summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.gitattributes9
-rw-r--r--.github/FUNDING.yml12
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.yml73
-rw-r--r--.github/ISSUE_TEMPLATE/config.yml10
-rw-r--r--.github/ISSUE_TEMPLATE/feature_request.yml72
-rw-r--r--.github/workflows/bisects.yml31
-rw-r--r--.github/workflows/ci_docs.yml116
-rw-r--r--.github/workflows/ci_gcc14.yml76
-rw-r--r--.github/workflows/ci_packages.yml74
-rw-r--r--.github/workflows/ci_publish.yml90
-rw-r--r--.github/workflows/stale.yml25
-rw-r--r--.gitignore69
-rw-r--r--.gitlab-ci.yml61
-rw-r--r--.travis.yml55
-rw-r--r--appveyor.yml64
-rw-r--r--azure-pipelines.yml168
-rwxr-xr-xbin/nim-gdb25
-rw-r--r--bin/nim-gdb.bat14
-rw-r--r--build_all.bat29
-rwxr-xr-xbuild_all.sh17
-rw-r--r--changelog.md170
-rw-r--r--changelogs/changelog_0_18_1.md663
-rw-r--r--changelogs/changelog_0_19_0.md253
-rw-r--r--changelogs/changelog_0_20_0.md294
-rw-r--r--changelogs/changelog_0_20_2.md55
-rw-r--r--changelogs/changelog_1_0_0.md138
-rw-r--r--changelogs/changelog_1_0_2.md56
-rw-r--r--changelogs/changelog_1_2_0.md535
-rw-r--r--changelogs/changelog_1_4_0.md874
-rw-r--r--changelogs/changelog_1_6_0.md957
-rw-r--r--changelogs/changelog_2_0_0.md330
-rw-r--r--changelogs/changelog_2_0_0_details.md560
-rw-r--r--changelogs/changelog_2_2_0.md248
-rw-r--r--changelogs/changelog_X_XX_X.md23
-rw-r--r--changelogs/readme.md61
-rw-r--r--ci/action.nim26
-rw-r--r--ci/build.bat14
-rw-r--r--ci/build.sh15
-rw-r--r--ci/build_autogen.bat26
-rw-r--r--ci/deps.bat4
-rw-r--r--ci/deps.sh16
-rw-r--r--ci/funs.sh147
-rw-r--r--ci/nsis_build.bat59
-rw-r--r--compiler.nimble9
-rw-r--r--compiler/aliasanalysis.nim128
-rw-r--r--compiler/aliases.nim67
-rw-r--r--compiler/ast.nim1889
-rw-r--r--compiler/astalgo.nim883
-rw-r--r--compiler/astmsgs.nim45
-rw-r--r--compiler/astyaml.nim154
-rw-r--r--compiler/bitsets.nim116
-rw-r--r--compiler/btrees.nim101
-rw-r--r--compiler/canonicalizer.nim421
-rw-r--r--compiler/cbuilder.nim174
-rw-r--r--compiler/ccgcalls.nim903
-rw-r--r--compiler/ccgexprs.nim3482
-rw-r--r--compiler/ccgliterals.nim95
-rw-r--r--compiler/ccgmerge.nim296
-rw-r--r--compiler/ccgmerge_unused.nim283
-rw-r--r--compiler/ccgreset.nim105
-rw-r--r--compiler/ccgstmts.nim1520
-rw-r--r--compiler/ccgthreadvars.nim41
-rw-r--r--compiler/ccgtrav.nim129
-rw-r--r--compiler/ccgtypes.nim1923
-rw-r--r--compiler/ccgutils.nim199
-rw-r--r--compiler/cgen.nim2443
-rw-r--r--compiler/cgendata.nim120
-rw-r--r--compiler/cgmeth.nim252
-rw-r--r--compiler/closureiters.nim940
-rw-r--r--compiler/cmdlinehelper.nim85
-rw-r--r--compiler/commands.nim1010
-rw-r--r--compiler/compiler.nimble28
-rw-r--r--compiler/concepts.nim343
-rw-r--r--compiler/condsyms.nim186
-rw-r--r--compiler/configuration.nim6
-rw-r--r--compiler/debugutils.nim72
-rw-r--r--compiler/depends.nim95
-rw-r--r--compiler/destroyer.nim465
-rw-r--r--compiler/dfa.nim620
-rw-r--r--compiler/docgen.nim2113
-rw-r--r--compiler/docgen2.nim56
-rw-r--r--compiler/enumtostr.nim110
-rw-r--r--compiler/errorhandling.nim85
-rw-r--r--compiler/evalffi.nim379
-rw-r--r--compiler/evaltempl.nim151
-rw-r--r--compiler/expanddefaults.nim131
-rw-r--r--compiler/extccomp.nim1069
-rw-r--r--compiler/filter_tmpl.nim37
-rw-r--r--compiler/filters.nim39
-rw-r--r--compiler/forloops.nim89
-rw-r--r--compiler/gorgeimpl.nim36
-rw-r--r--compiler/guards.nim673
-rw-r--r--compiler/hlo.nim39
-rw-r--r--compiler/ic/bitabs.nim178
-rw-r--r--compiler/ic/cbackend.nim180
-rw-r--r--compiler/ic/dce.nim169
-rw-r--r--compiler/ic/design.rst56
-rw-r--r--compiler/ic/ic.nim1343
-rw-r--r--compiler/ic/iclineinfos.nim84
-rw-r--r--compiler/ic/integrity.nim155
-rw-r--r--compiler/ic/navigator.nim183
-rw-r--r--compiler/ic/packed_ast.nim367
-rw-r--r--compiler/ic/replayer.nim171
-rw-r--r--compiler/ic/rodfiles.nim283
-rw-r--r--compiler/idents.nim62
-rw-r--r--compiler/idgen.nim59
-rw-r--r--compiler/importer.nim384
-rw-r--r--compiler/incremental.nim196
-rw-r--r--compiler/index.nim18
-rw-r--r--compiler/injectdestructors.nim1284
-rw-r--r--compiler/installer.ini55
-rw-r--r--compiler/int128.nim592
-rw-r--r--compiler/isolation_check.nim232
-rw-r--r--compiler/jsgen.nim2886
-rw-r--r--compiler/jstypes.nim122
-rw-r--r--compiler/lambdalifting.nim540
-rw-r--r--compiler/layouter.nim625
-rw-r--r--compiler/lexer.nim1283
-rw-r--r--compiler/liftdestructors.nim1324
-rw-r--r--compiler/liftlocals.nim25
-rw-r--r--compiler/lineinfos.nim324
-rw-r--r--compiler/linter.nim186
-rw-r--r--compiler/lists.nim28
-rw-r--r--compiler/llstream.nim90
-rw-r--r--compiler/lookups.nim865
-rw-r--r--compiler/lowerings.nim721
-rw-r--r--compiler/macrocacheimpl.nim59
-rw-r--r--compiler/magicsys.nim139
-rw-r--r--compiler/main.nim543
-rw-r--r--compiler/mangleutils.nim59
-rw-r--r--compiler/modulegraphs.nim763
-rw-r--r--compiler/modulepaths.nim162
-rw-r--r--compiler/modules.nim131
-rw-r--r--compiler/msgs.nim718
-rw-r--r--compiler/ndi.nim32
-rw-r--r--compiler/nilcheck.nim1387
-rw-r--r--compiler/nim.cfg50
-rw-r--r--compiler/nim.nim202
-rw-r--r--compiler/nimblecmd.nim138
-rw-r--r--compiler/nimconf.nim192
-rw-r--r--compiler/nimeval.nim105
-rw-r--r--compiler/nimfix/nimfix.nim111
-rw-r--r--compiler/nimfix/nimfix.nim.cfg17
-rw-r--r--compiler/nimfix/prettybase.nim41
-rw-r--r--compiler/nimlexbase.nim40
-rw-r--r--compiler/nimpaths.nim54
-rw-r--r--compiler/nimsets.nim106
-rw-r--r--compiler/nodejs.nim12
-rw-r--r--compiler/nodekinds.nim211
-rw-r--r--compiler/nversion.nim4
-rw-r--r--compiler/optimizer.nim290
-rw-r--r--compiler/options.nim964
-rw-r--r--compiler/packagehandling.nim25
-rw-r--r--compiler/packages.nim53
-rw-r--r--compiler/parampatterns.nim153
-rw-r--r--compiler/parser.nim1980
-rw-r--r--compiler/passaux.nim22
-rw-r--r--compiler/passes.nim311
-rw-r--r--compiler/pathutils.nim153
-rw-r--r--compiler/patterns.nim192
-rw-r--r--compiler/pipelines.nim312
-rw-r--r--compiler/pipelineutils.nim26
-rw-r--r--compiler/platform.nim91
-rw-r--r--compiler/plugins/active.nim2
-rw-r--r--compiler/plugins/customast.nim136
-rw-r--r--compiler/plugins/itersgen.nim14
-rw-r--r--compiler/plugins/locals.nim32
-rw-r--r--compiler/plugins/plugins.nimble (renamed from tests/modules/tnotuniquename/mnotuniquename.nim)0
-rw-r--r--compiler/pragmas.nim1082
-rw-r--r--compiler/prefixmatches.nim42
-rw-r--r--compiler/procfind.nim92
-rw-r--r--compiler/pushpoppragmas.nim54
-rw-r--r--compiler/readme.md7
-rw-r--r--compiler/readme.txt4
-rw-r--r--compiler/renderer.nim1224
-rw-r--r--compiler/renderverbatim.nim137
-rw-r--r--compiler/reorder.nim235
-rw-r--r--compiler/rod.nim29
-rw-r--r--compiler/rodimpl.nim885
-rw-r--r--compiler/rodutils.nim81
-rw-r--r--compiler/ropes.nim275
-rw-r--r--compiler/saturate.nim22
-rw-r--r--compiler/scriptconfig.nim159
-rw-r--r--compiler/sem.nim683
-rw-r--r--compiler/semasgn.nim337
-rw-r--r--compiler/semcall.nim891
-rw-r--r--compiler/semdata.nim472
-rw-r--r--compiler/semexprs.nim3254
-rw-r--r--compiler/semfields.nim97
-rw-r--r--compiler/semfold.nim923
-rw-r--r--compiler/semgnrc.nim591
-rw-r--r--compiler/seminst.nim399
-rw-r--r--compiler/semmacrosanity.nim65
-rw-r--r--compiler/semmagic.nim585
-rw-r--r--compiler/semobjconstr.nim510
-rw-r--r--compiler/semparallel.nim180
-rw-r--r--compiler/sempass2.nim1720
-rw-r--r--compiler/semstmts.nim2966
-rw-r--r--compiler/semstrictfuncs.nim55
-rw-r--r--compiler/semtempl.nim805
-rw-r--r--compiler/semtypes.nim2258
-rw-r--r--compiler/semtypinst.nim777
-rw-r--r--compiler/service.nim68
-rw-r--r--compiler/sighashes.nim370
-rw-r--r--compiler/sigmatch.nim2496
-rw-r--r--compiler/sinkparameter_inference.nim68
-rw-r--r--compiler/sizealignoffsetimpl.nim525
-rw-r--r--compiler/sourcemap.nim206
-rw-r--r--compiler/spawn.nim445
-rw-r--r--compiler/suggest.nim685
-rw-r--r--compiler/suggestsymdb.nim212
-rw-r--r--compiler/syntaxes.nim176
-rw-r--r--compiler/tccgen.nim61
-rw-r--r--compiler/transf.nim1086
-rw-r--r--compiler/trees.nim152
-rw-r--r--compiler/treetab.nim61
-rw-r--r--compiler/typeallowed.nim296
-rw-r--r--compiler/types.nim2050
-rw-r--r--compiler/typesrenderer.nim108
-rw-r--r--compiler/varpartitions.nim1019
-rw-r--r--compiler/vm.nim1799
-rw-r--r--compiler/vmconv.nim57
-rw-r--r--compiler/vmdef.nim168
-rw-r--r--compiler/vmdeps.nim209
-rw-r--r--compiler/vmgen.nim1913
-rw-r--r--compiler/vmhooks.nim85
-rw-r--r--compiler/vmmarshal.nim119
-rw-r--r--compiler/vmops.nim387
-rw-r--r--compiler/vmprofiler.nim45
-rw-r--r--compiler/vtables.nim167
-rw-r--r--compiler/wordrecg.nim267
-rw-r--r--compiler/writetracking.nim277
-rw-r--r--config/build_config.txt5
-rw-r--r--config/config.nims23
-rw-r--r--config/nim.cfg289
-rw-r--r--config/nimdoc.cfg1452
-rw-r--r--config/nimdoc.tex.cfg118
-rw-r--r--copying.txt2
-rw-r--r--doc/advopt.txt165
-rw-r--r--doc/apis.md85
-rw-r--r--doc/apis.txt81
-rw-r--r--doc/astspec.txt551
-rw-r--r--doc/backends.md406
-rw-r--r--doc/backends.txt474
-rw-r--r--doc/basicopt.txt29
-rw-r--r--doc/contributing.md798
-rw-r--r--doc/contributing.rst226
-rw-r--r--doc/destructors.md801
-rw-r--r--doc/docgen.md891
-rw-r--r--doc/docgen.rst378
-rw-r--r--doc/docgen_sample.nim6
-rw-r--r--doc/docs.md38
-rw-r--r--doc/docs.rst40
-rw-r--r--doc/docstyle.md164
-rw-r--r--doc/docstyle.rst140
-rw-r--r--doc/drnim.md205
-rw-r--r--doc/effects.txt5
-rw-r--r--doc/endb.rst203
-rw-r--r--doc/estp.md206
-rw-r--r--doc/estp.rst195
-rw-r--r--doc/exception_hierarchy_fragment.txt28
-rw-r--r--doc/filelist.txt14
-rw-r--r--doc/filters.md218
-rw-r--r--doc/filters.rst198
-rw-r--r--doc/gc.rst146
-rw-r--r--doc/grammar.txt174
-rw-r--r--doc/hcr.md245
-rw-r--r--doc/idetools.md617
-rw-r--r--doc/idetools.rst588
-rw-r--r--doc/intern.md679
-rw-r--r--doc/intern.txt669
-rw-r--r--doc/koch.md91
-rw-r--r--doc/koch.rst82
-rw-r--r--doc/lib.md682
-rw-r--r--doc/lib.rst568
-rw-r--r--doc/manual.md9033
-rw-r--r--doc/manual.rst8217
-rw-r--r--doc/manual/var_t_return.md24
-rw-r--r--doc/manual/var_t_return.rst20
-rw-r--r--doc/manual_experimental.md2669
-rw-r--r--doc/manual_experimental_strictnotnil.md255
-rw-r--r--doc/markdown_rst.md349
-rw-r--r--doc/mm.md95
-rw-r--r--doc/mytest.cfg2
-rw-r--r--doc/nep1.md335
-rw-r--r--doc/nep1.rst201
-rw-r--r--doc/nimc.md844
-rw-r--r--doc/nimc.rst543
-rw-r--r--doc/nimdoc.cls196
-rw-r--r--doc/nimdoc.css1171
-rw-r--r--doc/nimfix.rst56
-rw-r--r--doc/nimgrep.md128
-rw-r--r--doc/nimgrep.rst50
-rw-r--r--doc/nimgrep_cmdline.txt136
-rw-r--r--doc/niminst.md197
-rw-r--r--doc/niminst.rst195
-rw-r--r--doc/nims.md352
-rw-r--r--doc/nims.rst119
-rw-r--r--doc/nimsuggest.md176
-rw-r--r--doc/nimsuggest.rst167
-rw-r--r--doc/overview.md9
-rw-r--r--doc/overview.rst9
-rw-r--r--doc/packaging.md80
-rw-r--r--doc/pegdocs.txt100
-rw-r--r--doc/readme.txt4
-rw-r--r--doc/refc.md156
-rw-r--r--doc/regexprs.txt28
-rw-r--r--doc/rstcommon.rst51
-rw-r--r--doc/sets_fragment.txt103
-rw-r--r--doc/spawn.txt18
-rw-r--r--doc/subexes.txt7
-rw-r--r--doc/testament.md390
-rw-r--r--doc/tools.md45
-rw-r--r--doc/tools.txt29
-rw-r--r--doc/tut1.md1952
-rw-r--r--doc/tut1.rst1750
-rw-r--r--doc/tut2.md697
-rw-r--r--doc/tut2.rst1085
-rw-r--r--doc/tut3.md417
-rw-r--r--drnim/drnim.nim1272
-rw-r--r--drnim/nim.cfg18
-rw-r--r--drnim/tests/config.nims10
-rw-r--r--drnim/tests/tbasic_array_index.nim51
-rw-r--r--drnim/tests/tensures.nim74
-rw-r--r--drnim/tests/tphi.nim23
-rw-r--r--drnim/tests/tsetlen_invalidates.nim26
-rw-r--r--examples/allany.nim24
-rw-r--r--examples/c++iface/irrlichtex.nim114
-rw-r--r--examples/cgi/cgi_server.py11
-rw-r--r--examples/cgi/cgi_stacktrace.nim5
-rw-r--r--examples/cgi/example.nim7
-rw-r--r--examples/cgiex.nim12
-rw-r--r--examples/cross_calculator/.gitignore12
-rw-r--r--examples/cross_calculator/android/AndroidManifest.xml18
-rw-r--r--examples/cross_calculator/android/build.xml92
-rw-r--r--examples/cross_calculator/android/custom_rules.xml17
-rw-r--r--examples/cross_calculator/android/jni/Android.mk32
-rw-r--r--examples/cross_calculator/android/jni/backend-jni.c42
-rw-r--r--examples/cross_calculator/android/project.properties14
-rw-r--r--examples/cross_calculator/android/readme.txt24
-rw-r--r--examples/cross_calculator/android/res/layout/cross_calculator.xml72
-rw-r--r--examples/cross_calculator/android/res/values/strings.xml4
-rw-r--r--examples/cross_calculator/android/scripts/jnibuild.sh22
-rw-r--r--examples/cross_calculator/android/scripts/nimbuild.sh34
-rw-r--r--examples/cross_calculator/android/scripts/tags.sh13
-rw-r--r--examples/cross_calculator/android/src/com/github/nimrod/crosscalculator/CrossCalculator.java80
-rw-r--r--examples/cross_calculator/ios/cross-calculator.xcodeproj/project.pbxproj337
-rw-r--r--examples/cross_calculator/ios/readme.txt13
-rw-r--r--examples/cross_calculator/ios/resources/plist/cross-calculator-Info.plist30
-rw-r--r--examples/cross_calculator/ios/resources/ui/NRViewController.xib479
-rw-r--r--examples/cross_calculator/ios/scripts/tags.sh13
-rw-r--r--examples/cross_calculator/ios/scripts/xcode_prebuild.sh35
-rw-r--r--examples/cross_calculator/ios/src/AppDelegate.h7
-rw-r--r--examples/cross_calculator/ios/src/AppDelegate.m39
-rw-r--r--examples/cross_calculator/ios/src/NRViewController.h11
-rw-r--r--examples/cross_calculator/ios/src/NRViewController.m210
-rw-r--r--examples/cross_calculator/ios/src/cross-calculator-Prefix.pch10
-rw-r--r--examples/cross_calculator/ios/src/main.m13
-rw-r--r--examples/cross_calculator/lazarus/nimlaz.lpi140
-rw-r--r--examples/cross_calculator/lazarus/nimlaz.lpr21
-rw-r--r--examples/cross_calculator/lazarus/nimlaz.lrs5222
-rw-r--r--examples/cross_calculator/lazarus/nimlaz.rc6
-rw-r--r--examples/cross_calculator/lazarus/readme.txt8
-rw-r--r--examples/cross_calculator/lazarus/unit1.lfm46
-rw-r--r--examples/cross_calculator/lazarus/unit1.pas58
-rw-r--r--examples/cross_calculator/nim_backend/backend.nim5
-rw-r--r--examples/cross_calculator/nim_commandline/nim.cfg4
-rw-r--r--examples/cross_calculator/nim_commandline/nimcalculator.nim109
-rw-r--r--examples/cross_calculator/nim_commandline/readme.txt10
-rw-r--r--examples/cross_calculator/readme.txt13
-rw-r--r--examples/cross_todo/nim_backend/backend.nim195
-rw-r--r--examples/cross_todo/nim_backend/readme.txt14
-rw-r--r--examples/cross_todo/nim_backend/testbackend.nim74
-rw-r--r--examples/cross_todo/nim_commandline/nim.cfg4
-rw-r--r--examples/cross_todo/nim_commandline/nimtodo.nim297
-rw-r--r--examples/cross_todo/nim_commandline/readme.txt19
-rw-r--r--examples/cross_todo/readme.txt5
-rw-r--r--examples/debugging.nim17
-rw-r--r--examples/filterex.nim23
-rw-r--r--examples/fizzbuzz.nim14
-rw-r--r--examples/hallo.nim3
-rw-r--r--examples/htmlrefs.nim57
-rw-r--r--examples/htmltitle.nim36
-rw-r--r--examples/httpserver2.nim247
-rw-r--r--examples/keyval.nim8
-rw-r--r--examples/keyval2.nim7
-rw-r--r--examples/maximum.nim6
-rw-r--r--examples/myfile.txt11
-rw-r--r--examples/objciface/gnustepex.nim40
-rw-r--r--examples/parsecfgex.nim25
-rw-r--r--examples/readme.txt2
-rw-r--r--examples/ssl/extradata.nim26
-rw-r--r--examples/ssl/pskclient.nim16
-rw-r--r--examples/ssl/pskserver.nim20
-rw-r--r--examples/statcsv.nim60
-rw-r--r--examples/talk/dsl.nim33
-rw-r--r--examples/talk/formatoptimizer.nim55
-rw-r--r--examples/talk/hoisting.nim23
-rw-r--r--examples/talk/lazyeval.nim12
-rw-r--r--examples/talk/quasiquote.nim11
-rw-r--r--examples/talk/tags.nim9
-rw-r--r--examples/transff.nim8
-rw-r--r--examples/tunit.nim47
-rw-r--r--examples/unix_socket/client.nim6
-rw-r--r--examples/unix_socket/server.nim14
-rw-r--r--install.txt88
-rw-r--r--install_nimble.nims6
-rw-r--r--install_tools.nims6
-rw-r--r--koch.nim775
-rw-r--r--koch.nim.cfg8
-rw-r--r--lib/core/allocators.nim35
-rw-r--r--lib/core/hotcodereloading.nim41
-rw-r--r--lib/core/locks.nim54
-rw-r--r--lib/core/macrocache.nim231
-rw-r--r--lib/core/macros.nim1605
-rw-r--r--lib/core/refs.nim97
-rw-r--r--lib/core/rlocks.nim32
-rw-r--r--lib/core/seqs.nim139
-rw-r--r--lib/core/strs.nim110
-rw-r--r--lib/core/typeinfo.nim597
-rw-r--r--lib/core/typelayouts.nim19
-rw-r--r--lib/deprecated/core/unsigned.nim18
-rw-r--r--lib/deprecated/pure/actors.nim241
-rw-r--r--lib/deprecated/pure/actors.nim.cfg3
-rw-r--r--lib/deprecated/pure/asyncio.nim716
-rw-r--r--lib/deprecated/pure/ftpclient.nim675
-rw-r--r--lib/deprecated/pure/future.nim6
-rw-r--r--lib/deprecated/pure/mersenne.nim51
-rw-r--r--lib/deprecated/pure/ospaths.nim22
-rw-r--r--lib/deprecated/pure/oswalkdir.nim13
-rw-r--r--lib/deprecated/pure/parseurl.nim114
-rw-r--r--lib/deprecated/pure/rawsockets.nim14
-rw-r--r--lib/deprecated/pure/sockets.nim1759
-rw-r--r--lib/deprecated/pure/sums.nim80
-rw-r--r--lib/deps.txt14
-rw-r--r--lib/experimental/diff.nim339
-rw-r--r--lib/genode/alloc.nim20
-rw-r--r--lib/genode/constructibles.nim21
-rw-r--r--lib/genode/entrypoints.nim22
-rw-r--r--lib/genode/env.nim12
-rw-r--r--lib/genode/signals.nim77
-rw-r--r--lib/genode_cpp/signals.h39
-rw-r--r--lib/genode_cpp/syslocks.h25
-rw-r--r--lib/impure/db_mysql.nim419
-rw-r--r--lib/impure/db_odbc.nim546
-rw-r--r--lib/impure/db_postgres.nim538
-rw-r--r--lib/impure/db_sqlite.nim338
-rw-r--r--lib/impure/nre.nim618
-rw-r--r--lib/impure/nre/private/util.nim24
-rw-r--r--lib/impure/osinfo_posix.nim10
-rw-r--r--lib/impure/osinfo_win.nim10
-rw-r--r--lib/impure/rdstdin.nim100
-rw-r--r--lib/impure/re.nim557
-rw-r--r--lib/impure/ssl.nim100
-rw-r--r--lib/js/asyncjs.nim183
-rw-r--r--lib/js/dom.nim1628
-rw-r--r--lib/js/jsconsole.nim125
-rw-r--r--lib/js/jscore.nim172
-rw-r--r--lib/js/jsffi.nim469
-rw-r--r--lib/js/jsre.nim97
-rw-r--r--lib/nimbase.h332
-rw-r--r--lib/nimhcr.nim671
-rw-r--r--lib/nimhcr.nim.cfg5
-rw-r--r--lib/nimrtl.nim12
-rw-r--r--lib/nimrtl.nim.cfg1
-rw-r--r--lib/packages/docutils/dochelpers.nim298
-rw-r--r--lib/packages/docutils/docutils.nimble5
-rw-r--r--lib/packages/docutils/docutils.nimble.old7
-rw-r--r--lib/packages/docutils/highlite.nim370
-rw-r--r--lib/packages/docutils/rst.nim4004
-rw-r--r--lib/packages/docutils/rstast.nim195
-rw-r--r--lib/packages/docutils/rstgen.nim1063
-rw-r--r--lib/packages/docutils/rstidx.nim141
-rw-r--r--lib/packages/fsmonitor.nim229
-rw-r--r--lib/posix/epoll.nim64
-rw-r--r--lib/posix/inotify.nim130
-rw-r--r--lib/posix/kqueue.nim4
-rw-r--r--lib/posix/linux.nim53
-rw-r--r--lib/posix/posix.nim551
-rw-r--r--lib/posix/posix_freertos_consts.nim506
-rw-r--r--lib/posix/posix_haiku.nim603
-rw-r--r--lib/posix/posix_linux_amd64.nim101
-rw-r--r--lib/posix/posix_linux_amd64_consts.nim36
-rw-r--r--lib/posix/posix_macos_amd64.nim620
-rw-r--r--lib/posix/posix_nintendoswitch.nim506
-rw-r--r--lib/posix/posix_nintendoswitch_consts.nim587
-rw-r--r--lib/posix/posix_openbsd_amd64.nim565
-rw-r--r--lib/posix/posix_other.nim235
-rw-r--r--lib/posix/posix_other_consts.nim41
-rw-r--r--lib/posix/posix_utils.nim133
-rw-r--r--lib/posix/termios.nim32
-rw-r--r--lib/prelude.nim23
-rw-r--r--lib/pure/algorithm.nim901
-rw-r--r--lib/pure/async.nim11
-rw-r--r--lib/pure/asyncdispatch.nim1394
-rw-r--r--lib/pure/asyncfile.nim177
-rw-r--r--lib/pure/asyncftpclient.nim397
-rw-r--r--lib/pure/asyncfutures.nim321
-rw-r--r--lib/pure/asynchttpserver.nim339
-rw-r--r--lib/pure/asyncmacro.nim407
-rw-r--r--lib/pure/asyncnet.nim527
-rw-r--r--lib/pure/asyncstreams.nim118
-rw-r--r--lib/pure/base64.nim388
-rw-r--r--lib/pure/bitops.nim882
-rw-r--r--lib/pure/browsers.nim102
-rw-r--r--lib/pure/cgi.nim302
-rw-r--r--lib/pure/collections/LockFreeHash.nim609
-rw-r--r--lib/pure/collections/critbits.nim459
-rw-r--r--lib/pure/collections/deques.nim597
-rw-r--r--lib/pure/collections/hashcommon.nim76
-rw-r--r--lib/pure/collections/heapqueue.nim319
-rw-r--r--lib/pure/collections/intsets.nim437
-rw-r--r--lib/pure/collections/lists.nim899
-rw-r--r--lib/pure/collections/queues.nim259
-rw-r--r--lib/pure/collections/rtarrays.nim5
-rw-r--r--lib/pure/collections/sequtils.nim1586
-rw-r--r--lib/pure/collections/setimpl.nim156
-rw-r--r--lib/pure/collections/sets.nim1622
-rw-r--r--lib/pure/collections/sharedlist.nim33
-rw-r--r--lib/pure/collections/sharedstrings.nim152
-rw-r--r--lib/pure/collections/sharedtables.nim168
-rw-r--r--lib/pure/collections/tableimpl.nim226
-rw-r--r--lib/pure/collections/tables.nim3575
-rw-r--r--lib/pure/colors.nim450
-rw-r--r--lib/pure/complex.nim652
-rw-r--r--lib/pure/concurrency/atomics.nim433
-rw-r--r--lib/pure/concurrency/cpuinfo.nim157
-rw-r--r--lib/pure/concurrency/cpuload.nim15
-rw-r--r--lib/pure/concurrency/threadpool.nim288
-rw-r--r--lib/pure/cookies.nim61
-rw-r--r--lib/pure/coro.nim138
-rw-r--r--lib/pure/cstrutils.nim165
-rw-r--r--lib/pure/db_common.nim100
-rw-r--r--lib/pure/distros.nim147
-rw-r--r--lib/pure/dynlib.nim199
-rw-r--r--lib/pure/encodings.nim539
-rw-r--r--lib/pure/endians.nim94
-rw-r--r--lib/pure/events.nim100
-rw-r--r--lib/pure/fenv.nim102
-rw-r--r--lib/pure/future.nim4
-rw-r--r--lib/pure/hashes.nim807
-rw-r--r--lib/pure/htmlgen.nim799
-rw-r--r--lib/pure/htmlparser.nim3266
-rw-r--r--lib/pure/httpclient.nim1440
-rw-r--r--lib/pure/httpcore.nim270
-rw-r--r--lib/pure/httpserver.nim535
-rw-r--r--lib/pure/includes/asynccommon.nim211
-rw-r--r--lib/pure/includes/osenv.nim159
-rw-r--r--lib/pure/includes/oserr.nim88
-rw-r--r--lib/pure/includes/unicode_ranges.nim2007
-rw-r--r--lib/pure/ioselects/ioselectors_epoll.nim125
-rw-r--r--lib/pure/ioselects/ioselectors_kqueue.nim90
-rw-r--r--lib/pure/ioselects/ioselectors_poll.nim80
-rw-r--r--lib/pure/ioselects/ioselectors_select.nim68
-rw-r--r--lib/pure/json.nim1714
-rw-r--r--lib/pure/lenientops.nim36
-rw-r--r--lib/pure/lexbase.nim92
-rw-r--r--lib/pure/logging.nim783
-rw-r--r--lib/pure/marshal.nim251
-rw-r--r--lib/pure/matchers.nim68
-rw-r--r--lib/pure/math.nim1658
-rw-r--r--lib/pure/md5.nim222
-rw-r--r--lib/pure/memfiles.nim308
-rw-r--r--lib/pure/mersenne.nim46
-rw-r--r--lib/pure/mimetypes.nim1540
-rw-r--r--lib/pure/nativesockets.nim967
-rw-r--r--lib/pure/net.nim1530
-rw-r--r--lib/pure/nimprof.nim22
-rw-r--r--lib/pure/nimtracker.nim88
-rw-r--r--lib/pure/oids.nim110
-rw-r--r--lib/pure/options.nim551
-rw-r--r--lib/pure/os.nim1999
-rw-r--r--lib/pure/ospaths.nim619
-rw-r--r--lib/pure/osproc.nim1102
-rw-r--r--lib/pure/oswalkdir.nim35
-rw-r--r--lib/pure/parsecfg.nim426
-rw-r--r--lib/pure/parsecsv.nim348
-rw-r--r--lib/pure/parsejson.nim163
-rw-r--r--lib/pure/parseopt.nim609
-rw-r--r--lib/pure/parseopt2.nim164
-rw-r--r--lib/pure/parsesql.nim332
-rw-r--r--lib/pure/parseutils.nim1130
-rw-r--r--lib/pure/parsexml.nim406
-rw-r--r--lib/pure/pathnorm.nim121
-rw-r--r--lib/pure/pegs.nim1667
-rw-r--r--lib/pure/prelude.nim28
-rw-r--r--lib/pure/punycode.nim174
-rw-r--r--lib/pure/random.nim825
-rw-r--r--lib/pure/rationals.nim379
-rw-r--r--lib/pure/reservedmem.nim229
-rw-r--r--lib/pure/ropes.nim303
-rw-r--r--lib/pure/scgi.nim295
-rw-r--r--lib/pure/securehash.nim6
-rw-r--r--lib/pure/segfaults.nim24
-rw-r--r--lib/pure/selectors.nim214
-rw-r--r--lib/pure/smtp.nim245
-rw-r--r--lib/pure/smtp.nim.cfg1
-rw-r--r--lib/pure/ssl_certs.nim172
-rw-r--r--lib/pure/ssl_config.nim51
-rw-r--r--lib/pure/stats.nim230
-rw-r--r--lib/pure/streams.nim1501
-rw-r--r--lib/pure/streamwrapper.nim121
-rw-r--r--lib/pure/strformat.nim930
-rw-r--r--lib/pure/strmisc.nim98
-rw-r--r--lib/pure/strscans.nim272
-rw-r--r--lib/pure/strtabs.nim369
-rw-r--r--lib/pure/strutils.nim3419
-rw-r--r--lib/pure/subexes.nim408
-rw-r--r--lib/pure/sugar.nim469
-rw-r--r--lib/pure/terminal.nim730
-rw-r--r--lib/pure/times.nim3767
-rw-r--r--lib/pure/typetraits.nim397
-rw-r--r--lib/pure/unicode.nim2549
-rw-r--r--lib/pure/unidecode/gen.py34
-rw-r--r--lib/pure/unidecode/unidecode.dat841
-rw-r--r--lib/pure/unidecode/unidecode.nim68
-rw-r--r--lib/pure/unittest.nim476
-rw-r--r--lib/pure/uri.nim689
-rw-r--r--lib/pure/volatile.nim26
-rw-r--r--lib/pure/xmldom.nim1136
-rw-r--r--lib/pure/xmldomparser.nim168
-rw-r--r--lib/pure/xmlparser.nim32
-rw-r--r--lib/pure/xmltree.nim925
-rw-r--r--lib/std/appdirs.nim94
-rw-r--r--lib/std/assertions.nim122
-rw-r--r--lib/std/cmdline.nim313
-rw-r--r--lib/std/compilesettings.nim66
-rw-r--r--lib/std/decls.nim31
-rw-r--r--lib/std/dirs.nim135
-rw-r--r--lib/std/editdistance.nim266
-rw-r--r--lib/std/effecttraits.nim63
-rw-r--r--lib/std/enumerate.nim70
-rw-r--r--lib/std/enumutils.nim202
-rw-r--r--lib/std/envvars.nim221
-rw-r--r--lib/std/exitprocs.nim87
-rw-r--r--lib/std/files.nim46
-rw-r--r--lib/std/formatfloat.nim143
-rw-r--r--lib/std/genasts.nim89
-rw-r--r--lib/std/importutils.nim44
-rw-r--r--lib/std/isolation.nim49
-rw-r--r--lib/std/jsbigints.nim228
-rw-r--r--lib/std/jsfetch.nim202
-rw-r--r--lib/std/jsformdata.nim69
-rw-r--r--lib/std/jsheaders.nim83
-rw-r--r--lib/std/jsonutils.nim493
-rw-r--r--lib/std/logic.nim10
-rw-r--r--lib/std/monotimes.nim160
-rw-r--r--lib/std/objectdollar.nim13
-rw-r--r--lib/std/oserrors.nim117
-rw-r--r--lib/std/outparams.nim38
-rw-r--r--lib/std/packedsets.nim601
-rw-r--r--lib/std/paths.nim302
-rw-r--r--lib/std/private/asciitables.nim83
-rw-r--r--lib/std/private/bitops_utils.nim22
-rw-r--r--lib/std/private/decode_helpers.nim42
-rw-r--r--lib/std/private/digitsutils.nim116
-rw-r--r--lib/std/private/dragonbox.nim1325
-rw-r--r--lib/std/private/gitutils.nim74
-rw-r--r--lib/std/private/globs.nim70
-rw-r--r--lib/std/private/jsutils.nim96
-rw-r--r--lib/std/private/miscdollars.nim39
-rw-r--r--lib/std/private/ntpath.nim61
-rw-r--r--lib/std/private/osappdirs.nim176
-rw-r--r--lib/std/private/oscommon.nim186
-rw-r--r--lib/std/private/osdirs.nim570
-rw-r--r--lib/std/private/osfiles.nim416
-rw-r--r--lib/std/private/ospaths2.nim1030
-rw-r--r--lib/std/private/osseps.nim113
-rw-r--r--lib/std/private/ossymlinks.nim78
-rw-r--r--lib/std/private/schubfach.nim436
-rw-r--r--lib/std/private/since.nim33
-rw-r--r--lib/std/private/strimpl.nim113
-rw-r--r--lib/std/private/syslocks.nim234
-rw-r--r--lib/std/private/threadtypes.nim176
-rw-r--r--lib/std/private/underscored_calls.nim56
-rw-r--r--lib/std/private/win_getsysteminfo.nim15
-rw-r--r--lib/std/private/win_setenv.nim106
-rw-r--r--lib/std/setutils.nim77
-rw-r--r--lib/std/sha1.nim426
-rw-r--r--lib/std/socketstreams.nim182
-rw-r--r--lib/std/stackframes.nim30
-rw-r--r--lib/std/staticos.nim13
-rw-r--r--lib/std/strbasics.nim119
-rw-r--r--lib/std/symlinks.nim33
-rw-r--r--lib/std/syncio.nim942
-rw-r--r--lib/std/sysatomics.nim376
-rw-r--r--lib/std/sysrand.nim326
-rw-r--r--lib/std/tasks.nim312
-rw-r--r--lib/std/tempfiles.nim192
-rw-r--r--lib/std/time_t.nim23
-rw-r--r--lib/std/typedthreads.nim305
-rw-r--r--lib/std/varints.nim93
-rw-r--r--lib/std/vmutils.nim11
-rw-r--r--lib/std/widestrs.nim239
-rw-r--r--lib/std/with.nim48
-rw-r--r--lib/std/wordwrap.nim74
-rw-r--r--lib/std/wrapnils.nim193
-rw-r--r--lib/system.nim5907
-rw-r--r--lib/system/alloc.nim968
-rw-r--r--lib/system/ansi_c.nim233
-rw-r--r--lib/system/arc.nim267
-rw-r--r--lib/system/arithm.nim411
-rw-r--r--lib/system/arithmetics.nim405
-rw-r--r--lib/system/assign.nim215
-rw-r--r--lib/system/atomics.nim317
-rw-r--r--lib/system/basic_types.nim94
-rw-r--r--lib/system/bitmasks.nim39
-rw-r--r--lib/system/cellseqs_v1.nim46
-rw-r--r--lib/system/cellseqs_v2.nim53
-rw-r--r--lib/system/cellsets.nim145
-rw-r--r--lib/system/cgprocs.nim11
-rw-r--r--lib/system/channels.nim300
-rw-r--r--lib/system/channels_builtin.nim459
-rw-r--r--lib/system/chcks.nim171
-rw-r--r--lib/system/comparisons.nim337
-rw-r--r--lib/system/compilation.nim209
-rw-r--r--lib/system/coro_detection.nim20
-rw-r--r--lib/system/countbits_impl.nim93
-rw-r--r--lib/system/ctypes.nim84
-rw-r--r--lib/system/cyclebreaker.nim184
-rw-r--r--lib/system/debugger.nim305
-rw-r--r--lib/system/deepcopy.nim93
-rw-r--r--lib/system/dollars.nim147
-rw-r--r--lib/system/dyncalls.nim109
-rw-r--r--lib/system/embedded.nim29
-rw-r--r--lib/system/endb.nim558
-rw-r--r--lib/system/exceptions.nim122
-rw-r--r--lib/system/excpt.nim664
-rw-r--r--lib/system/fatal.nim58
-rw-r--r--lib/system/formatfloat.nim6
-rw-r--r--lib/system/gc.nim355
-rw-r--r--lib/system/gc2.nim777
-rw-r--r--lib/system/gc_common.nim251
-rw-r--r--lib/system/gc_hooks.nim53
-rw-r--r--lib/system/gc_interface.nim100
-rw-r--r--lib/system/gc_ms.nim195
-rw-r--r--lib/system/gc_regions.nim111
-rw-r--r--lib/system/hti.nim53
-rw-r--r--lib/system/inclrtl.nim31
-rw-r--r--lib/system/indexerrors.nim15
-rw-r--r--lib/system/indices.nim164
-rw-r--r--lib/system/integerops.nim132
-rw-r--r--lib/system/iterators.nim353
-rw-r--r--lib/system/iterators_1.nim180
-rw-r--r--lib/system/jssys.nim557
-rw-r--r--lib/system/memalloc.nim449
-rw-r--r--lib/system/memory.nim55
-rw-r--r--lib/system/memtracker.nim27
-rw-r--r--lib/system/mm/boehm.nim140
-rw-r--r--lib/system/mm/go.nim153
-rw-r--r--lib/system/mm/malloc.nim97
-rw-r--r--lib/system/mm/none.nim46
-rw-r--r--lib/system/mmdisp.nim540
-rw-r--r--lib/system/nimscript.nim246
-rw-r--r--lib/system/orc.nim543
-rw-r--r--lib/system/osalloc.nim71
-rw-r--r--lib/system/platforms.nim23
-rw-r--r--lib/system/profiler.nim15
-rw-r--r--lib/system/rawquits.nim27
-rw-r--r--lib/system/repr.nim120
-rw-r--r--lib/system/repr_impl.nim15
-rw-r--r--lib/system/repr_v2.nim194
-rw-r--r--lib/system/reprjs.nim106
-rw-r--r--lib/system/seqs_v2.nim227
-rw-r--r--lib/system/seqs_v2_reimpl.nim24
-rw-r--r--lib/system/setops.nim89
-rw-r--r--lib/system/sets.nim29
-rw-r--r--lib/system/stacktraces.nim83
-rw-r--r--lib/system/strmantle.nim263
-rw-r--r--lib/system/strs_v2.nim224
-rw-r--r--lib/system/sysio.nim434
-rw-r--r--lib/system/syslocks.nim228
-rw-r--r--lib/system/sysspawn.nim194
-rw-r--r--lib/system/sysstr.nim511
-rw-r--r--lib/system/threadids.nim103
-rw-r--r--lib/system/threadimpl.nim111
-rw-r--r--lib/system/threadlocalstorage.nim125
-rw-r--r--lib/system/threads.nim716
-rw-r--r--lib/system/timers.nim15
-rw-r--r--lib/system/widestrs.nim168
-rw-r--r--lib/system_overview.rst177
-rw-r--r--lib/windows/registry.nim31
-rw-r--r--lib/windows/winlean.nim570
-rw-r--r--lib/wrappers/iup.nim957
-rw-r--r--lib/wrappers/linenoise/linenoise.c20
-rw-r--r--lib/wrappers/linenoise/linenoise.h11
-rw-r--r--lib/wrappers/linenoise/linenoise.nim42
-rw-r--r--lib/wrappers/mysql.nim1112
-rw-r--r--lib/wrappers/odbcsql.nim847
-rw-r--r--lib/wrappers/openssl.nim691
-rw-r--r--lib/wrappers/pcre.nim54
-rw-r--r--lib/wrappers/postgres.nim364
-rw-r--r--lib/wrappers/sqlite3.nim370
-rw-r--r--lib/wrappers/tinyc.nim7
-rw-r--r--nim.nimble17
-rw-r--r--nimdoc/extlinks/project/doc/manual.md17
-rw-r--r--nimdoc/extlinks/project/expected/_._/util.html105
-rw-r--r--nimdoc/extlinks/project/expected/_._/util.idx2
-rw-r--r--nimdoc/extlinks/project/expected/doc/manual.html45
-rw-r--r--nimdoc/extlinks/project/expected/doc/manual.idx3
-rw-r--r--nimdoc/extlinks/project/expected/main.html142
-rw-r--r--nimdoc/extlinks/project/expected/main.idx4
-rw-r--r--nimdoc/extlinks/project/expected/sub/submodule.html131
-rw-r--r--nimdoc/extlinks/project/expected/sub/submodule.idx3
-rw-r--r--nimdoc/extlinks/project/expected/theindex.html59
-rw-r--r--nimdoc/extlinks/project/main.nim23
-rw-r--r--nimdoc/extlinks/project/sub/submodule.nim13
-rw-r--r--nimdoc/extlinks/util.nim2
-rw-r--r--nimdoc/rst2html/expected/rst_examples.html274
-rw-r--r--nimdoc/rst2html/source/rst_examples.rst514
-rw-r--r--nimdoc/rsttester.nim42
-rw-r--r--nimdoc/test_doctype/expected/test_doctype.html82
-rw-r--r--nimdoc/test_doctype/test_doctype.nim15
-rw-r--r--nimdoc/test_out_index_dot_html/expected/foo.idx2
-rw-r--r--nimdoc/test_out_index_dot_html/expected/index.html105
-rw-r--r--nimdoc/test_out_index_dot_html/expected/theindex.html43
-rw-r--r--nimdoc/test_out_index_dot_html/foo.nim3
-rw-r--r--nimdoc/tester.nim157
-rw-r--r--nimdoc/testproject/expected/nimdoc.out.css1036
-rw-r--r--nimdoc/testproject/expected/subdir/subdir_b/utils.html614
-rw-r--r--nimdoc/testproject/expected/subdir/subdir_b/utils.idx47
-rw-r--r--nimdoc/testproject/expected/testproject.html1260
-rw-r--r--nimdoc/testproject/expected/testproject.idx74
-rw-r--r--nimdoc/testproject/expected/theindex.html431
-rw-r--r--nimdoc/testproject/subdir/subdir_b/utils.nim213
-rw-r--r--nimdoc/testproject/subdir/subdir_b/utils_helpers.nim6
-rw-r--r--nimdoc/testproject/subdir/subdir_b/utils_overview.rst8
-rw-r--r--nimdoc/testproject/testproject.nim422
-rw-r--r--nimdoc/testproject/testproject.nimble (renamed from tinyc/tests/tests2/80_flexarray.expect)0
-rw-r--r--nimpretty/nimpretty.nim146
-rw-r--r--nimpretty/nimpretty.nim.cfg1
-rw-r--r--nimpretty/tester.nim59
-rw-r--r--nimpretty/tests/exhaustive.nim539
-rw-r--r--nimpretty/tests/expected/exhaustive.nim568
-rw-r--r--nimpretty/tests/expected/simple.nim29
-rw-r--r--nimpretty/tests/expected/simple2.nim22
-rw-r--r--nimpretty/tests/expected/simple3.nim15
-rw-r--r--nimpretty/tests/expected/simple4.nim2
-rw-r--r--nimpretty/tests/expected/tevil_spaces.nim58
-rw-r--r--nimpretty/tests/expected/wrong_ind.nim111
-rw-r--r--nimpretty/tests/simple.nim29
-rw-r--r--nimpretty/tests/simple2.nim22
-rw-r--r--nimpretty/tests/simple3.nim14
-rw-r--r--nimpretty/tests/simple4.nim2
-rw-r--r--nimpretty/tests/tevil_spaces.nim58
-rw-r--r--nimpretty/tests/wrong_ind.nim111
-rw-r--r--nimsuggest/config.nims2
-rw-r--r--nimsuggest/crashtester.nim4
-rw-r--r--nimsuggest/nimsuggest.nim992
-rw-r--r--nimsuggest/nimsuggest.nim.cfg2
-rw-r--r--nimsuggest/nimsuggest.nimble17
-rw-r--r--nimsuggest/procmonitor.nim34
-rw-r--r--nimsuggest/sexp.nim156
-rw-r--r--nimsuggest/tester.nim122
-rw-r--r--nimsuggest/tests/fixtures/mclass_macro.nim164
-rw-r--r--nimsuggest/tests/fixtures/mdep_v1.nim (renamed from nimsuggest/tests/dep_v1.nim)0
-rw-r--r--nimsuggest/tests/fixtures/mdep_v2.nim (renamed from nimsuggest/tests/dep_v2.nim)0
-rw-r--r--nimsuggest/tests/fixtures/mfakeassert.nim5
-rw-r--r--nimsuggest/tests/fixtures/minclude_import.nim15
-rw-r--r--nimsuggest/tests/fixtures/minclude_include.nim4
-rw-r--r--nimsuggest/tests/fixtures/minclude_types.nim6
-rw-r--r--nimsuggest/tests/fixtures/mstrutils.nim19
-rw-r--r--nimsuggest/tests/module_20265.nim6
-rw-r--r--nimsuggest/tests/t20265_1.nim8
-rw-r--r--nimsuggest/tests/t20265_2.nim8
-rw-r--r--nimsuggest/tests/t20440.nim7
-rw-r--r--nimsuggest/tests/t20440.nims1
-rw-r--r--nimsuggest/tests/t21185.nim18
-rw-r--r--nimsuggest/tests/t22448.nim11
-rw-r--r--nimsuggest/tests/taccent_highlight.nim7
-rw-r--r--nimsuggest/tests/tarrowcrash.nim20
-rw-r--r--nimsuggest/tests/tcallstrlit_highlight.nim11
-rw-r--r--nimsuggest/tests/tchk1.nim10
-rw-r--r--nimsuggest/tests/tchk2.nim35
-rw-r--r--nimsuggest/tests/tchk_compiles.nim8
-rw-r--r--nimsuggest/tests/tcon1.nim34
-rw-r--r--nimsuggest/tests/tcon_variable.nim12
-rw-r--r--nimsuggest/tests/tconcept1.nim12
-rw-r--r--nimsuggest/tests/tconcept2.nim15
-rw-r--r--nimsuggest/tests/tdef1.nim10
-rw-r--r--nimsuggest/tests/tdef2.nim13
-rw-r--r--nimsuggest/tests/tdef_forward.nim13
-rw-r--r--nimsuggest/tests/tdef_let.nim7
-rw-r--r--nimsuggest/tests/tdot1.nim2
-rw-r--r--nimsuggest/tests/tdot2.nim3
-rw-r--r--nimsuggest/tests/tdot3.nim4
-rw-r--r--nimsuggest/tests/tdot4.nim21
-rw-r--r--nimsuggest/tests/tenum_field.nim17
-rw-r--r--nimsuggest/tests/tfatal1.nim15
-rw-r--r--nimsuggest/tests/tgeneric_highlight.nim13
-rw-r--r--nimsuggest/tests/tgenerics.nim18
-rw-r--r--nimsuggest/tests/tic.nim20
-rw-r--r--nimsuggest/tests/timport_highlight.nim12
-rw-r--r--nimsuggest/tests/tinclude.nim28
-rw-r--r--nimsuggest/tests/tmacro_highlight.nim13
-rw-r--r--nimsuggest/tests/tobj_highlight.nim11
-rw-r--r--nimsuggest/tests/top_highlight.nim11
-rw-r--r--nimsuggest/tests/tqualified_highlight.nim14
-rw-r--r--nimsuggest/tests/tsetter_highlight.nim10
-rw-r--r--nimsuggest/tests/tsi_highlight.nim11
-rw-r--r--nimsuggest/tests/tstrutils.nim9
-rw-r--r--nimsuggest/tests/tsug_accquote.nim10
-rw-r--r--nimsuggest/tests/tsug_enum.nim18
-rw-r--r--nimsuggest/tests/tsug_pragmas.nim40
-rw-r--r--nimsuggest/tests/tsug_recursive.nim8
-rw-r--r--nimsuggest/tests/tsug_regression.nim26
-rw-r--r--nimsuggest/tests/tsug_template.nim12
-rw-r--r--nimsuggest/tests/tsug_typedecl.nim26
-rw-r--r--nimsuggest/tests/ttempl_inst.nim13
-rw-r--r--nimsuggest/tests/ttemplate_highlight.nim9
-rw-r--r--nimsuggest/tests/ttype_decl.nim8
-rw-r--r--nimsuggest/tests/ttype_highlight.nim27
-rw-r--r--nimsuggest/tests/tuse.nim22
-rw-r--r--nimsuggest/tests/tuse_enum.nim15
-rw-r--r--nimsuggest/tests/tuse_structure.nim15
-rw-r--r--nimsuggest/tests/tv3.nim27
-rw-r--r--nimsuggest/tests/tv3_con.nim13
-rw-r--r--nimsuggest/tests/tv3_definition.nim9
-rw-r--r--nimsuggest/tests/tv3_forward_definition.nim23
-rw-r--r--nimsuggest/tests/tv3_generics.nim18
-rw-r--r--nimsuggest/tests/tv3_globalSymbols.nim14
-rw-r--r--nimsuggest/tests/tv3_import.nim7
-rw-r--r--nimsuggest/tests/tv3_outline.nim45
-rw-r--r--nimsuggest/tests/tv3_typeDefinition.nim32
-rw-r--r--nimsuggest/tests/twithin_macro.nim178
-rw-r--r--nimsuggest/tests/twithin_macro_prefix.nim169
-rw-r--r--readme.md125
-rw-r--r--readme.txt18
-rw-r--r--security.md20
-rw-r--r--testament/azure.nim147
-rw-r--r--testament/backend.nim70
-rw-r--r--testament/caasdriver.nim (renamed from tests/testament/caasdriver.nim)10
-rw-r--r--testament/categories.nim774
-rw-r--r--testament/htmlgen.nim (renamed from tests/testament/htmlgen.nim)21
-rw-r--r--testament/important_packages.nim193
-rw-r--r--testament/lib/readme.md4
-rw-r--r--testament/lib/stdtest/netutils.nim13
-rw-r--r--testament/lib/stdtest/specialpaths.nim55
-rw-r--r--testament/lib/stdtest/testutils.nim126
-rw-r--r--testament/lib/stdtest/unittest_light.nim37
-rw-r--r--testament/specs.nim521
-rw-r--r--testament/testament.nim806
-rw-r--r--testament/testament.nim.cfg6
-rw-r--r--testament/testamenthtml.nimf (renamed from tests/testament/testamenthtml.templ)0
-rw-r--r--testament/tests/shouldfail/tccodecheck.nim8
-rw-r--r--testament/tests/shouldfail/tcolumn.nim8
-rw-r--r--testament/tests/shouldfail/terrormsg.nim8
-rw-r--r--testament/tests/shouldfail/texitcode1.nim3
-rw-r--r--testament/tests/shouldfail/tfile.nim6
-rw-r--r--testament/tests/shouldfail/tline.nim8
-rw-r--r--testament/tests/shouldfail/tmaxcodesize.nim5
-rw-r--r--testament/tests/shouldfail/tnimout.nim7
-rw-r--r--testament/tests/shouldfail/tnimoutfull.nim14
-rw-r--r--testament/tests/shouldfail/toutput.nim7
-rw-r--r--testament/tests/shouldfail/toutputsub.nim5
-rw-r--r--testament/tests/shouldfail/treject.nim7
-rw-r--r--testament/tests/shouldfail/tsortoutput.nim11
-rw-r--r--testament/tests/shouldfail/ttimeout.nim7
-rw-r--r--testament/tests/shouldfail/tvalgrind.nim17
-rw-r--r--tests/actiontable/tactiontable.nim26
-rw-r--r--tests/actiontable/tactiontable2.nim27
-rw-r--r--tests/alias/t19349.nim19
-rw-r--r--tests/align/globalalignas.nim3
-rw-r--r--tests/align/talign.nim69
-rw-r--r--tests/align/tillegalalign.nim7
-rw-r--r--tests/alloc/tmembug.nim54
-rw-r--r--tests/alloc/tmembug2.nim58
-rw-r--r--tests/ambsym/tambsym3.nim15
-rw-r--r--tests/arc/amodule.nim21
-rw-r--r--tests/arc/bmodule.nim4
-rw-r--r--tests/arc/cmodule.nim4
-rw-r--r--tests/arc/dmodule.nim23
-rw-r--r--tests/arc/nim.cfg1
-rw-r--r--tests/arc/t14383.nim217
-rw-r--r--tests/arc/t14472.nim43
-rw-r--r--tests/arc/t14864.nim5
-rw-r--r--tests/arc/t15909.nim16
-rw-r--r--tests/arc/t16033.nim10
-rw-r--r--tests/arc/t16458.nim6
-rw-r--r--tests/arc/t16558.nim9
-rw-r--r--tests/arc/t17025.nim56
-rw-r--r--tests/arc/t17173.nim10
-rw-r--r--tests/arc/t17812.nim41
-rw-r--r--tests/arc/t18645.nim18
-rw-r--r--tests/arc/t18971.nim10
-rw-r--r--tests/arc/t18977.nim26
-rw-r--r--tests/arc/t19231.nim18
-rw-r--r--tests/arc/t19364.nim30
-rw-r--r--tests/arc/t19401.nim32
-rw-r--r--tests/arc/t19402.nim32
-rw-r--r--tests/arc/t19435.nim29
-rw-r--r--tests/arc/t19457.nim16
-rw-r--r--tests/arc/t19862.nim15
-rw-r--r--tests/arc/t20456.nim7
-rw-r--r--tests/arc/t20588.nim25
-rw-r--r--tests/arc/t21184.nim77
-rw-r--r--tests/arc/t22218.nim25
-rw-r--r--tests/arc/t22237.nim55
-rw-r--r--tests/arc/t22478.nim46
-rw-r--r--tests/arc/t22787.nim37
-rw-r--r--tests/arc/t23247.nim52
-rw-r--r--tests/arc/t9650.nim87
-rw-r--r--tests/arc/taliased_reassign.nim41
-rw-r--r--tests/arc/tamemfiles.nim110
-rw-r--r--tests/arc/tamodule.nim9
-rw-r--r--tests/arc/tarc_macro.nim57
-rw-r--r--tests/arc/tarc_orc.nim186
-rw-r--r--tests/arc/tarcmisc.nim836
-rw-r--r--tests/arc/tasyncawait.nim68
-rw-r--r--tests/arc/tasyncleak.nim21
-rw-r--r--tests/arc/tasyncleak2.nim88
-rw-r--r--tests/arc/tasyncleak3.nim47
-rw-r--r--tests/arc/tasyncleak4.nim21
-rw-r--r--tests/arc/tasyncorc.nim26
-rw-r--r--tests/arc/tcaseobj.nim366
-rw-r--r--tests/arc/tcaseobjcopy.nim253
-rw-r--r--tests/arc/tclosureiter.nim36
-rw-r--r--tests/arc/tcomputedgoto.nim44
-rw-r--r--tests/arc/tcomputedgotocopy.nim44
-rw-r--r--tests/arc/tconst_to_sink.nim26
-rw-r--r--tests/arc/tcontrolflow.nim118
-rw-r--r--tests/arc/tcursor_field_obj_constr.nim44
-rw-r--r--tests/arc/tcursor_on_localvar.nim161
-rw-r--r--tests/arc/tcursorloop.nim45
-rw-r--r--tests/arc/tcustomtrace.nim240
-rw-r--r--tests/arc/tdeepcopy.nim67
-rw-r--r--tests/arc/tdestroy_in_loopcond.nim74
-rw-r--r--tests/arc/tdup.nim71
-rw-r--r--tests/arc/testfile.txt2
-rw-r--r--tests/arc/texceptions.nim16
-rw-r--r--tests/arc/texplicit_sink.nim29
-rw-r--r--tests/arc/tfuncobj.nim38
-rw-r--r--tests/arc/thamming_orc.nim155
-rw-r--r--tests/arc/thamming_thinout.nim183
-rw-r--r--tests/arc/thard_alignment.nim148
-rw-r--r--tests/arc/thavlak_orc_stress.nim414
-rw-r--r--tests/arc/theavy_recursion.nim43
-rw-r--r--tests/arc/timportedobj.nim14
-rw-r--r--tests/arc/titeration_doesnt_copy.nim67
-rw-r--r--tests/arc/tkeys_lent.nim17
-rw-r--r--tests/arc/tmalloc.nim16
-rw-r--r--tests/arc/tmarshal.nim140
-rw-r--r--tests/arc/tmove_regression.nim22
-rw-r--r--tests/arc/tmovebug.nim841
-rw-r--r--tests/arc/tmovebugcopy.nim526
-rw-r--r--tests/arc/tnewseq_legacy.nim13
-rw-r--r--tests/arc/top_no_cursor2.nim53
-rw-r--r--tests/arc/topenarray.nim86
-rw-r--r--tests/arc/topt_cursor.nim57
-rw-r--r--tests/arc/topt_cursor2.nim76
-rw-r--r--tests/arc/topt_no_cursor.nim376
-rw-r--r--tests/arc/topt_refcursors.nim54
-rw-r--r--tests/arc/topt_wasmoved_destroy_pairs.nim94
-rw-r--r--tests/arc/torc_basic_test.nim138
-rw-r--r--tests/arc/torc_selfcycles.nim33
-rw-r--r--tests/arc/torcbench.nim38
-rw-r--r--tests/arc/torcmisc.nim66
-rw-r--r--tests/arc/tref_cast_error.nim15
-rw-r--r--tests/arc/trepr.nim97
-rw-r--r--tests/arc/trtree.nim640
-rw-r--r--tests/arc/tshared_ptr_crash.nim67
-rw-r--r--tests/arc/tstrformat.nim22
-rw-r--r--tests/arc/tstringliteral.nim17
-rw-r--r--tests/arc/tthread.nim63
-rw-r--r--tests/arc/tunref_cycle.nim26
-rw-r--r--tests/arc/tweave.nim157
-rw-r--r--tests/arc/tweavecopy.nim154
-rw-r--r--tests/arc/twrong_sinkinference.nim18
-rw-r--r--tests/arithm/tand.nim20
-rw-r--r--tests/arithm/tnot.nim58
-rw-r--r--tests/arithm/tshl.nim34
-rw-r--r--tests/arithm/tshr.nim20
-rw-r--r--tests/arithm/tsubrange.nim13
-rw-r--r--tests/array/t15117.nim27
-rw-r--r--tests/array/t20248.nim14
-rw-r--r--tests/array/t7818.nim132
-rw-r--r--tests/array/t9932.nim11
-rw-r--r--tests/array/tarray.nim651
-rw-r--r--tests/array/tarray2.nim36
-rw-r--r--tests/array/tarray3.nim13
-rw-r--r--tests/array/tarraycons.nim5
-rw-r--r--tests/array/tarraycons2.nim23
-rw-r--r--tests/array/tarraycons_ptr_generic.nim51
-rw-r--r--tests/array/tarraycons_ptr_generic2.nim2
-rw-r--r--tests/array/tarraylen.nim18
-rw-r--r--tests/array/tarrindx.nim31
-rw-r--r--tests/array/tidx_lit_err1.nim6
-rw-r--r--tests/array/tidx_lit_err2.nim5
-rw-r--r--tests/array/tidx_lit_err3.nim5
-rw-r--r--tests/array/tinvalidarrayaccess.nim21
-rw-r--r--tests/array/tinvalidarrayaccess2.nim10
-rw-r--r--tests/array/tlargeindex.nim18
-rw-r--r--tests/array/troof1.nim43
-rw-r--r--tests/array/troof3.nim7
-rw-r--r--tests/array/troofregression.nim46
-rw-r--r--tests/array/troofregression2.nim102
-rw-r--r--tests/array/tunchecked.nim5
-rw-r--r--tests/assert/config.nims1
-rw-r--r--tests/assert/panicoverride.nim15
-rw-r--r--tests/assert/t21195.nim6
-rw-r--r--tests/assert/tassert.nim5
-rw-r--r--tests/assert/tassert2.nim111
-rw-r--r--tests/assert/tassert_c.nim40
-rw-r--r--tests/assert/tfailedassert.nim51
-rw-r--r--tests/assign/moverload_asgn2.nim4
-rw-r--r--tests/assign/tassign.nim241
-rw-r--r--tests/assign/tcopy.nim25
-rw-r--r--tests/assign/tgenericassign.nim24
-rw-r--r--tests/assign/tgenericassigntuples.nim16
-rw-r--r--tests/assign/tobjasgn.nim44
-rw-r--r--tests/assign/tobject_assign.nim49
-rw-r--r--tests/assign/toverload_asgn1.nim76
-rw-r--r--tests/assign/tvariantasgn.nim19
-rw-r--r--tests/ast_pattern_matching.nim584
-rw-r--r--tests/astspec/tastspec.nim1119
-rw-r--r--tests/async/nim.cfg1
-rw-r--r--tests/async/t11558.nim13
-rw-r--r--tests/async/t12221.nim40
-rw-r--r--tests/async/t13889.nim27
-rw-r--r--tests/async/t14820.nim25
-rw-r--r--tests/async/t15148.nim12
-rw-r--r--tests/async/t15804.nim15
-rw-r--r--tests/async/t17045.nim28
-rw-r--r--tests/async/t20111.nim19
-rw-r--r--tests/async/t21447.nim6
-rw-r--r--tests/async/t21893.nim13
-rw-r--r--tests/async/t22210.nim41
-rw-r--r--tests/async/t22210_2.nim73
-rw-r--r--tests/async/t3075.nim29
-rw-r--r--tests/async/t6100.nim15
-rw-r--r--tests/async/t6846.nim15
-rw-r--r--tests/async/t7192.nim14
-rw-r--r--tests/async/t7758.nim21
-rw-r--r--tests/async/t7985.nim19
-rw-r--r--tests/async/t8982.nim33
-rw-r--r--tests/async/t9201.nim14
-rw-r--r--tests/async/tacceptcloserace.nim4
-rw-r--r--tests/async/tasyncRecvLine.nim3
-rw-r--r--tests/async/tasync_gcsafe.nim2
-rw-r--r--tests/async/tasync_gcunsafe.nim4
-rw-r--r--tests/async/tasync_misc.nim83
-rw-r--r--tests/async/tasync_noasync.nim44
-rw-r--r--tests/async/tasync_nofuture.nim11
-rw-r--r--tests/async/tasync_traceback.nim100
-rw-r--r--tests/async/tasyncall.nim3
-rw-r--r--tests/async/tasyncawait.nim53
-rw-r--r--tests/async/tasyncclosestall.nim101
-rw-r--r--tests/async/tasyncconnect.nim5
-rw-r--r--tests/async/tasyncdial.nim6
-rw-r--r--tests/async/tasynceagain.nim67
-rw-r--r--tests/async/tasyncexceptions.nim7
-rw-r--r--tests/async/tasyncfile.nim8
-rw-r--r--tests/async/tasyncfilewrite.nim13
-rw-r--r--tests/async/tasyncintemplate.nim62
-rw-r--r--tests/async/tasyncnetudp.nim90
-rw-r--r--tests/async/tasyncrecursion.nim5
-rw-r--r--tests/async/tasyncsend4757.nim21
-rw-r--r--tests/async/tasyncssl.nim36
-rw-r--r--tests/async/tasynctry.nim34
-rw-r--r--tests/async/tawaitsemantics.nim4
-rw-r--r--tests/async/tbreak_must_exec_finally.nim25
-rw-r--r--tests/async/tcallbacks.nim5
-rw-r--r--tests/async/tdiscardableproc.nim9
-rw-r--r--tests/async/testmanyasyncevents.nim24
-rw-r--r--tests/async/tfuturestream.nim28
-rw-r--r--tests/async/tfuturevar.nim3
-rw-r--r--tests/async/tioselectors.nim57
-rw-r--r--tests/async/tjsandnativeasync.nim2
-rw-r--r--tests/async/tnewasyncudp.nim24
-rw-r--r--tests/async/tpendingcheck.nim5
-rw-r--r--tests/async/ttemplateinasync.nim11
-rw-r--r--tests/async/tupcoming_async.nim16
-rw-r--r--tests/async/twinasyncrw.nim56
-rw-r--r--tests/avr/nim.cfg12
-rw-r--r--tests/avr/panicoverride.nim13
-rw-r--r--tests/avr/thello.nim6
-rw-r--r--tests/benchmark.nim47
-rw-r--r--tests/benchmarks/fannkuch.nim69
-rw-r--r--tests/benchmarks/quicksort.nim54
-rw-r--r--tests/benchmarks/readme.md5
-rw-r--r--tests/benchmarks/ttls.nim30
-rw-r--r--tests/bind/tbind1.nim21
-rw-r--r--tests/bind/tbind2.nim17
-rw-r--r--tests/bind/tbind3.nim11
-rw-r--r--tests/bind/tbindoverload.nim12
-rw-r--r--tests/bind/tdatabind.nim95
-rw-r--r--tests/bind/tinvalidbindtypedesc.nim11
-rw-r--r--tests/bind/tmixin.nim27
-rw-r--r--tests/bind/tnicerrorforsymchoice.nim18
-rw-r--r--tests/borrow/tborrow.nim21
-rw-r--r--tests/borrow/tinvalidborrow.nim17
-rw-r--r--tests/c/tcompile.c3
-rw-r--r--tests/c/tcompile.nim10
-rw-r--r--tests/c/temit.nim (renamed from tests/misc/temit.nim)6
-rw-r--r--tests/c/treservedcidentsasfields.nim39
-rw-r--r--tests/caas/absurd_nesting.nim29
-rw-r--r--tests/caas/absurd_nesting.txt29
-rw-r--r--tests/caas/basic-recompile.txt10
-rw-r--r--tests/caas/compile-suggest.txt7
-rw-r--r--tests/caas/compile-then-def.txt11
-rw-r--r--tests/caas/completion_dot_syntax.txt9
-rw-r--r--tests/caas/completion_dot_syntax_dirty.nim25
-rw-r--r--tests/caas/completion_dot_syntax_main.nim24
-rw-r--r--tests/caas/def-def-compile.txt12
-rw-r--r--tests/caas/def-then-compile.txt8
-rw-r--r--tests/caas/forward_declarations.nim15
-rw-r--r--tests/caas/forward_declarations.txt9
-rw-r--r--tests/caas/forward_usages.txt17
-rw-r--r--tests/caas/idetools_api.nim84
-rw-r--r--tests/caas/idetools_api.txt59
-rw-r--r--tests/caas/imported.nim3
-rw-r--r--tests/caas/issue_416_template_shift.nim17
-rw-r--r--tests/caas/issue_416_template_shift.txt14
-rw-r--r--tests/caas/issue_452_export_shift.nim8
-rw-r--r--tests/caas/issue_452_export_shift.txt11
-rw-r--r--tests/caas/issue_477_dynamic_dispatch.nim19
-rw-r--r--tests/caas/issue_477_dynamic_dispatch.txt5
-rw-r--r--tests/caas/its_full_of_procs.nim29
-rw-r--r--tests/caas/its_full_of_procs.txt20
-rw-r--r--tests/caas/main.nim7
-rw-r--r--tests/caas/main_dirty.nim14
-rw-r--r--tests/caas/suggest-compile.txt13
-rw-r--r--tests/caas/suggest-invalid-source.txt26
-rw-r--r--tests/casestmt/t18964.nim12
-rw-r--r--tests/casestmt/t7699.nim15
-rw-r--r--tests/casestmt/tcase_arrayconstr.nim19
-rw-r--r--tests/casestmt/tcase_emptyset_when.nim24
-rw-r--r--tests/casestmt/tcase_issues.nim7
-rw-r--r--tests/casestmt/tcase_setconstr.nim15
-rw-r--r--tests/casestmt/tcaseexpr1.nim23
-rw-r--r--tests/casestmt/tcaseoverlaprange.nim2
-rw-r--r--tests/casestmt/tcaseoverlaprange2.nim2
-rw-r--r--tests/casestmt/tcasestm.nim108
-rw-r--r--tests/casestmt/tcasestmt.nim319
-rw-r--r--tests/casestmt/tcomputedgoto.nim15
-rw-r--r--tests/casestmt/tcstring.nim52
-rw-r--r--tests/casestmt/tduplicates.nim50
-rw-r--r--tests/casestmt/tincompletecaseobject.nim115
-rw-r--r--tests/casestmt/tincompletecaseobject2.nim26
-rw-r--r--tests/casestmt/tlinearscanend.nim4
-rw-r--r--tests/casestmt/trangeexhaustiveness.nim7
-rw-r--r--tests/cast/tcast.nim21
-rw-r--r--tests/ccgbugs/m1/defs.nim4
-rw-r--r--tests/ccgbugs/m19445.c3
-rw-r--r--tests/ccgbugs/m2/defs.nim4
-rw-r--r--tests/ccgbugs/mstatic_assert.nim6
-rw-r--r--tests/ccgbugs/pkg8616/rtarray.nim2
-rw-r--r--tests/ccgbugs/pkg8616/scheduler.nim10
-rw-r--r--tests/ccgbugs/t10128.nim18
-rw-r--r--tests/ccgbugs/t10964.nim7
-rw-r--r--tests/ccgbugs/t13062.nim33
-rw-r--r--tests/ccgbugs/t13902.nim12
-rw-r--r--tests/ccgbugs/t15428.nim22
-rw-r--r--tests/ccgbugs/t16027.nim13
-rw-r--r--tests/ccgbugs/t16374.nim38
-rw-r--r--tests/ccgbugs/t19445.nim13
-rw-r--r--tests/ccgbugs/t20139.nim10
-rw-r--r--tests/ccgbugs/t20141.nim27
-rw-r--r--tests/ccgbugs/t20787.nim4
-rw-r--r--tests/ccgbugs/t21116.nim10
-rw-r--r--tests/ccgbugs/t21972.nim33
-rw-r--r--tests/ccgbugs/t21995.nim9
-rw-r--r--tests/ccgbugs/t22462.nim20
-rw-r--r--tests/ccgbugs/t23796.nim25
-rw-r--r--tests/ccgbugs/t2procs.nim18
-rw-r--r--tests/ccgbugs/t5296.nim9
-rw-r--r--tests/ccgbugs/t5345.nim10
-rw-r--r--tests/ccgbugs/t6756.nim8
-rw-r--r--tests/ccgbugs/t8616.nim4
-rw-r--r--tests/ccgbugs/t8781.nim25
-rw-r--r--tests/ccgbugs/t8967.nim14
-rw-r--r--tests/ccgbugs/t9098.nim12
-rw-r--r--tests/ccgbugs/t9286.nim22
-rw-r--r--tests/ccgbugs/t9578.nim76
-rw-r--r--tests/ccgbugs/t9655.nim30
-rw-r--r--tests/ccgbugs/taddhigh.nim2
-rw-r--r--tests/ccgbugs/targ_lefttoright.nim71
-rw-r--r--tests/ccgbugs/tassign_nil_strings.nim13
-rw-r--r--tests/ccgbugs/tborrowmagic.nim8
-rw-r--r--tests/ccgbugs/tbug21505.nim39
-rw-r--r--tests/ccgbugs/tccgen1.nim2
-rw-r--r--tests/ccgbugs/tccgissues.nim14
-rw-r--r--tests/ccgbugs/tcgbug.nim129
-rw-r--r--tests/ccgbugs/tcodegenbug1.nim82
-rw-r--r--tests/ccgbugs/tcodegenbug_bool.nim11
-rw-r--r--tests/ccgbugs/tcodegendecllambda.nim14
-rw-r--r--tests/ccgbugs/tcompile_time_var_at_runtime.nim11
-rw-r--r--tests/ccgbugs/tconstobj.nim16
-rw-r--r--tests/ccgbugs/tctypes.nim43
-rw-r--r--tests/ccgbugs/tcvarargs.nim1
-rw-r--r--tests/ccgbugs/tdeepcopy_addr_rval.nim1
-rw-r--r--tests/ccgbugs/tderefblock.nim76
-rw-r--r--tests/ccgbugs/tforward_decl_only.nim2
-rw-r--r--tests/ccgbugs/tgeneric_closure.nim10
-rw-r--r--tests/ccgbugs/tgeneric_smallobj_asgn_opt.nim5
-rw-r--r--tests/ccgbugs/thtiobj.nim9
-rw-r--r--tests/ccgbugs/tissues.nim38
-rw-r--r--tests/ccgbugs/tlvalueconv.nim32
-rw-r--r--tests/ccgbugs/tmangle.nim16
-rw-r--r--tests/ccgbugs/tmangle_field.nim4
-rw-r--r--tests/ccgbugs/tmarkerproc_regression.nim10
-rw-r--r--tests/ccgbugs/tmissingbracket.nim11
-rw-r--r--tests/ccgbugs/tmissingderef.nim1
-rw-r--r--tests/ccgbugs/tmissingderef2.nim2
-rw-r--r--tests/ccgbugs/tmissinginit.nim7
-rw-r--r--tests/ccgbugs/tmissingvolatile.nim3
-rw-r--r--tests/ccgbugs/tnil_type.nim15
-rw-r--r--tests/ccgbugs/tnoalias.nim13
-rw-r--r--tests/ccgbugs/tnocodegen_for_compiletime.nim2
-rw-r--r--tests/ccgbugs/tobjconstr_bad_aliasing.nim56
-rw-r--r--tests/ccgbugs/topenarraycast.nim8
-rw-r--r--tests/ccgbugs/tprogmem.nim11
-rw-r--r--tests/ccgbugs/trecursive_closure.nim14
-rw-r--r--tests/ccgbugs/tret_arg_init.nim2
-rw-r--r--tests/ccgbugs/tsamename3.nim120
-rw-r--r--tests/ccgbugs/tsequence_outoforder.nim11
-rw-r--r--tests/ccgbugs/tsighash_typename_regression.nim22
-rw-r--r--tests/ccgbugs/tunsafeaddr.nim21
-rw-r--r--tests/ccgbugs/tuple_canon.nim12
-rw-r--r--tests/ccgbugs/twrong_setconstr.nim146
-rw-r--r--tests/ccgbugs/twrong_tupleconv.nim17
-rw-r--r--tests/ccgbugs/twrongrefcounting.nim11
-rw-r--r--tests/ccgbugs/xarray9578.nim7
-rw-r--r--tests/ccgbugs/xoa9578.nim4
-rw-r--r--tests/ccgbugs/xseq9578.nim7
-rw-r--r--tests/ccgbugs/xtuple9578.nim7
-rw-r--r--tests/ccgbugs/xua9578.nim7
-rw-r--r--tests/ccgbugs2/tcodegen.nim47
-rw-r--r--tests/ccgbugs2/tinefficient_const_table.nim (renamed from tests/ccgbugs/tinefficient_const_table.nim)4
-rw-r--r--tests/closure/t11042.nim55
-rw-r--r--tests/closure/t15594.nim10
-rw-r--r--tests/closure/t19095.nim35
-rw-r--r--tests/closure/t20152.nim20
-rw-r--r--tests/closure/t8550.nim13
-rw-r--r--tests/closure/t9334.nim19
-rw-r--r--tests/closure/tcapture.nim34
-rw-r--r--tests/closure/tclosure.nim523
-rw-r--r--tests/closure/tclosure0.nim87
-rw-r--r--tests/closure/tclosure2.nim101
-rw-r--r--tests/closure/tclosure3.nim21
-rw-r--r--tests/closure/tclosure4.nim13
-rw-r--r--tests/closure/tclosure_issues.nim82
-rw-r--r--tests/closure/tclosurebug2.nim194
-rw-r--r--tests/closure/tclosureinference3304.nim15
-rw-r--r--tests/closure/tcodegenerr1923.nim9
-rw-r--r--tests/closure/tdeeplynested.nim20
-rw-r--r--tests/closure/tdonotation.nim50
-rw-r--r--tests/closure/texplicit_dummy_closure.nim13
-rw-r--r--tests/closure/tfib50.nim22
-rw-r--r--tests/closure/tflatmap.nim24
-rw-r--r--tests/closure/tforum.nim44
-rw-r--r--tests/closure/tfutclosure2138.nim10
-rw-r--r--tests/closure/tinfer_closure_for_nestedproc.nim42
-rw-r--r--tests/closure/tinterf.nim24
-rw-r--r--tests/closure/tinvalidclosure.nim2
-rw-r--r--tests/closure/tinvalidclosure2.nim2
-rw-r--r--tests/closure/tinvalidclosure3.nim4
-rw-r--r--tests/closure/tinvalidclosure4.nim9
-rw-r--r--tests/closure/tinvalidclosure5.nim10
-rw-r--r--tests/closure/tissue1502def.nim6
-rw-r--r--tests/closure/tissue1642.nim3
-rw-r--r--tests/closure/tissue1846.nim16
-rw-r--r--tests/closure/tissue1911.nim7
-rw-r--r--tests/closure/tissue600.nim4
-rw-r--r--tests/closure/tjester.nim32
-rw-r--r--tests/closure/tmacrobust1512.nim40
-rw-r--r--tests/closure/tnamedparamanonproc.nim14
-rw-r--r--tests/closure/tnested.nim215
-rw-r--r--tests/closure/tnestedclosure.nim51
-rw-r--r--tests/closure/tnestedproc.nim12
-rw-r--r--tests/closure/tnoclosure.nim25
-rw-r--r--tests/closure/tstmtlist.nim9
-rw-r--r--tests/closure/ttimeinfo.nim15
-rw-r--r--tests/cnstseq/tcnstseq.nim17
-rw-r--r--tests/cnstseq/tcnstseq2.nim12
-rw-r--r--tests/cnstseq/tcnstseq3.nim7
-rw-r--r--tests/codegen/titaniummangle.nim193
-rw-r--r--tests/collections/tableadds.nim13
-rw-r--r--tests/collections/tactiontable.nim37
-rw-r--r--tests/collections/tapply.nim11
-rw-r--r--tests/collections/tcollections.nim105
-rw-r--r--tests/collections/tcollections_to_string.nim17
-rw-r--r--tests/collections/tcounttable.nim19
-rw-r--r--tests/collections/tdeques.nim17
-rw-r--r--tests/collections/thashes.nim90
-rw-r--r--tests/collections/thashsets.nim382
-rw-r--r--tests/collections/tindexby.nim22
-rw-r--r--tests/collections/tmapit.nim33
-rw-r--r--tests/collections/tseq.nim242
-rw-r--r--tests/collections/tsets.nim123
-rw-r--r--tests/collections/ttableconstr.nim16
-rw-r--r--tests/collections/ttables.nim722
-rw-r--r--tests/collections/ttables2.nim30
-rw-r--r--tests/collections/ttablesref.nim171
-rw-r--r--tests/collections/ttablesref2.nim20
-rw-r--r--tests/collections/ttablesthreads.nim276
-rw-r--r--tests/compileoption/texperimental.nim20
-rw-r--r--tests/compileoption/texperimental.nims19
-rw-r--r--tests/compiler/nim.cfg7
-rw-r--r--tests/compiler/samplelib.nim2
-rw-r--r--tests/compiler/t12684.nim7
-rw-r--r--tests/compiler/tasciitables.nim109
-rw-r--r--tests/compiler/tasm.nim15
-rw-r--r--tests/compiler/tbtrees.nim84
-rw-r--r--tests/compiler/tcmdlineclib.nim3
-rw-r--r--tests/compiler/tcmdlineclib.nims10
-rw-r--r--tests/compiler/tcmdlinecpuamd64.nim10
-rw-r--r--tests/compiler/tcmdlinecpuamd64.nim.cfg1
-rw-r--r--tests/compiler/tcmdlineoswin.nim14
-rw-r--r--tests/compiler/tcmdlineoswin.nim.cfg1
-rw-r--r--tests/compiler/tcppCompileToNamespace.nim11
-rw-r--r--tests/compiler/tdebugutils.nim38
-rw-r--r--tests/compiler/tgrammar.nim7
-rw-r--r--tests/compiler/tint128.nim184
-rw-r--r--tests/compiler/tnimblecmd.nim75
-rw-r--r--tests/compiler/tpathutils.nim18
-rw-r--r--tests/compiler/tprefixmatches.nim32
-rw-r--r--tests/compiler/tunittest_light.nim55
-rw-r--r--tests/compilerapi/exposed.nim2
-rw-r--r--tests/compilerapi/invalid.nim1
-rw-r--r--tests/compilerapi/myscript.nim5
-rw-r--r--tests/compilerapi/tcompilerapi.nim103
-rw-r--r--tests/compiles/mstaticlib.nim1
-rw-r--r--tests/compiles/t8630.nim13
-rw-r--r--tests/compiles/trecursive_generic_in_compiles.nim14
-rw-r--r--tests/compiles/tstaticlib.nim22
-rw-r--r--tests/concepts/libs/trie.nim26
-rw-r--r--tests/concepts/libs/trie_database.nim12
-rw-r--r--tests/concepts/matrixalgo.nim4
-rw-r--r--tests/concepts/t1128.nim21
-rw-r--r--tests/concepts/t18409.nim37
-rw-r--r--tests/concepts/t19730.nim20
-rw-r--r--tests/concepts/t20237.nim3
-rw-r--r--tests/concepts/t3330.nim50
-rw-r--r--tests/concepts/t3414.nim22
-rw-r--r--tests/concepts/t5642.nim25
-rw-r--r--tests/concepts/t5888.nim26
-rw-r--r--tests/concepts/t5968.nim20
-rw-r--r--tests/concepts/t5983.nim22
-rw-r--r--tests/concepts/t6462.nim23
-rw-r--r--tests/concepts/t6770.nim27
-rw-r--r--tests/concepts/t8012.nim15
-rw-r--r--tests/concepts/t8558.nim26
-rw-r--r--tests/concepts/t976.nim85
-rw-r--r--tests/concepts/tconcepts.nim451
-rw-r--r--tests/concepts/tconcepts_issues.nim556
-rw-r--r--tests/concepts/tconcepts_overload_precedence.nim2
-rw-r--r--tests/concepts/texplain.nim146
-rw-r--r--tests/concepts/tgraph.nim34
-rw-r--r--tests/concepts/tinfrecursion.nim13
-rw-r--r--tests/concepts/tmapconcept.nim4
-rw-r--r--tests/concepts/tmatrixconcept.nim8
-rw-r--r--tests/concepts/tmisc_issues.nim100
-rw-r--r--tests/concepts/tmonoid.nim12
-rw-r--r--tests/concepts/trandom_vars.nim42
-rw-r--r--tests/concepts/trandomvars.nim4
-rw-r--r--tests/concepts/trandomvars2.nim42
-rw-r--r--tests/concepts/treversable.nim31
-rw-r--r--tests/concepts/tspec.nim105
-rw-r--r--tests/concepts/tstackconcept.nim2
-rw-r--r--tests/concepts/ttrieconcept.nim7
-rw-r--r--tests/concepts/tusertypeclasses.nim36
-rw-r--r--tests/concepts/tusertypeclasses2.nim73
-rw-r--r--tests/concepts/tvectorspace.nim4
-rw-r--r--tests/concepts/twrapconcept.nim3
-rw-r--r--tests/config.nims49
-rw-r--r--tests/constraints/tconstraints.nim18
-rw-r--r--tests/constructors/a.nim2
-rw-r--r--tests/constructors/b.nim2
-rw-r--r--tests/constructors/t18990.nim3
-rw-r--r--tests/constructors/t5965_1.nim2
-rw-r--r--tests/constructors/t5965_2.nim2
-rw-r--r--tests/constructors/tconstr1.nim (renamed from tests/constr/tconstr1.nim)4
-rw-r--r--tests/constructors/tconstr2.nim (renamed from tests/constr/tconstr2.nim)6
-rw-r--r--tests/constructors/tinvalid_construction.nim327
-rw-r--r--tests/controlflow/tblock1.nim4
-rw-r--r--tests/controlflow/tbreak.nim44
-rw-r--r--tests/controlflow/tcontinue.nim28
-rw-r--r--tests/controlflow/tcontrolflow.nim116
-rw-r--r--tests/controlflow/tnestif.nim24
-rw-r--r--tests/controlflow/tstatret.nim9
-rw-r--r--tests/controlflow/tunamedbreak.nim15
-rw-r--r--tests/controlflow/tunreachable.nim79
-rw-r--r--tests/controlflow/tunreachable2.nim12
-rw-r--r--tests/converter/m18986.nim3
-rw-r--r--tests/converter/mdontleak.nim3
-rw-r--r--tests/converter/t18986.nim10
-rw-r--r--tests/converter/t21531.nim10
-rw-r--r--tests/converter/t7097.nim38
-rw-r--r--tests/converter/t7098.nim35
-rw-r--r--tests/converter/t9165.nim11
-rw-r--r--tests/converter/tconvcolors.nim4
-rw-r--r--tests/converter/tconvert.nim24
-rw-r--r--tests/converter/tconverter.nim11
-rw-r--r--tests/converter/tconverter_unique_ptr.nim149
-rw-r--r--tests/converter/tconverter_with_constraint.nim20
-rw-r--r--tests/converter/tconverter_with_varargs.nim2
-rw-r--r--tests/converter/tdontleak.nim10
-rw-r--r--tests/converter/texplicit_conversion.nim6
-rw-r--r--tests/converter/tgenericconverter.nim24
-rw-r--r--tests/converter/tgenericconverter2.nim56
-rw-r--r--tests/converter/tor_in_converter.nim2
-rw-r--r--tests/coroutines/texceptions.nim8
-rw-r--r--tests/coroutines/tgc.nim29
-rw-r--r--tests/coroutines/titerators.nim9
-rw-r--r--tests/coroutines/twait.nim34
-rw-r--r--tests/cpp/23962.h17
-rw-r--r--tests/cpp/amodule.nim10
-rw-r--r--tests/cpp/enum.hpp3
-rw-r--r--tests/cpp/fam.h4
-rw-r--r--tests/cpp/mexportc.nim9
-rw-r--r--tests/cpp/t10148.nim29
-rw-r--r--tests/cpp/t10241.nim19
-rw-r--r--tests/cpp/t12946.nim8
-rw-r--r--tests/cpp/t22679.nim50
-rw-r--r--tests/cpp/t22680.nim50
-rw-r--r--tests/cpp/t22712.nim15
-rw-r--r--tests/cpp/t23306.nim12
-rw-r--r--tests/cpp/t23434.nim17
-rw-r--r--tests/cpp/t23657.nim54
-rw-r--r--tests/cpp/t23962.nim32
-rw-r--r--tests/cpp/t4834.nim17
-rw-r--r--tests/cpp/t6986.nim19
-rw-r--r--tests/cpp/t8241.nim32
-rw-r--r--tests/cpp/t9013.nim9
-rw-r--r--tests/cpp/tasync_cpp.nim1
-rw-r--r--tests/cpp/tcasts.nim11
-rw-r--r--tests/cpp/tcodegendecl.nim17
-rw-r--r--tests/cpp/tconstructor.nim131
-rw-r--r--tests/cpp/tcovariancerules.nim45
-rw-r--r--tests/cpp/tcppraise.nim34
-rw-r--r--tests/cpp/tdont_init_instantiation.nim2
-rw-r--r--tests/cpp/tembarrassing_generic_bug.nim (renamed from tests/cpp/tembarrassing_generic_failure.nim)0
-rw-r--r--tests/cpp/temitlist.nim10
-rw-r--r--tests/cpp/tempty_generic_obj.nim15
-rw-r--r--tests/cpp/tenum_set.nim9
-rw-r--r--tests/cpp/tevalorder.nim18
-rw-r--r--tests/cpp/texportc.nim22
-rw-r--r--tests/cpp/tfam.nim7
-rw-r--r--tests/cpp/tinitializers.nim60
-rw-r--r--tests/cpp/tmember.nim75
-rw-r--r--tests/cpp/tmember_forward_declaration.nim27
-rw-r--r--tests/cpp/tnativesockets.nim1
-rw-r--r--tests/cpp/tnoinitfield.nim30
-rw-r--r--tests/cpp/torc.nim75
-rw-r--r--tests/cpp/tpassbypragmas.nim27
-rw-r--r--tests/cpp/tretvar.nim39
-rw-r--r--tests/cpp/tsigbreak.nim1
-rw-r--r--tests/cpp/ttemplatetype.nim2
-rw-r--r--tests/cpp/tterminate_handler.nim10
-rw-r--r--tests/cpp/ttypeinfo1.nim (renamed from tests/cpp/ttypeinfo.nim)0
-rw-r--r--tests/cpp/tvector_iterator.nim4
-rw-r--r--tests/cpp/tvirtual.nim126
-rw-r--r--tests/cpp/virtualptr.nim9
-rw-r--r--tests/defaultprocparam/tdefaultprocparam.nim4
-rw-r--r--tests/defer/t22309.nim11
-rw-r--r--tests/deprecated/tdeprecated.nim9
-rw-r--r--tests/destructor/const_smart_ptr.nim75
-rw-r--r--tests/destructor/helper.nim3
-rw-r--r--tests/destructor/nim.cfg1
-rw-r--r--tests/destructor/objFile.nim8
-rw-r--r--tests/destructor/smart_ptr.nim43
-rw-r--r--tests/destructor/t12037.nim34
-rw-r--r--tests/destructor/t16607.nim23
-rw-r--r--tests/destructor/t17198.nim32
-rw-r--r--tests/destructor/t23748.nim31
-rw-r--r--tests/destructor/t23837.nim51
-rw-r--r--tests/destructor/t5342.nim24
-rw-r--r--tests/destructor/t7346.nim14
-rw-r--r--tests/destructor/t9440.nim52
-rw-r--r--tests/destructor/tarc.nim184
-rw-r--r--tests/destructor/tarc2.nim31
-rw-r--r--tests/destructor/tarc3.nim101
-rw-r--r--tests/destructor/tarctypesections.nim70
-rw-r--r--tests/destructor/tarray_indexing.nim75
-rw-r--r--tests/destructor/tasync_prototype.nim59
-rw-r--r--tests/destructor/tasync_prototype_cyclic.nim55
-rw-r--r--tests/destructor/tatomicptrs.nim85
-rw-r--r--tests/destructor/tbintree2.nim101
-rw-r--r--tests/destructor/tcaseobj_transitions.nim49
-rw-r--r--tests/destructor/tcast.nim14
-rw-r--r--tests/destructor/tcomplexobjconstr.nim56
-rw-r--r--tests/destructor/tconst_smart_ptr.nim7
-rw-r--r--tests/destructor/tconsume_twice.nim15
-rw-r--r--tests/destructor/tcustomseqs.nim11
-rw-r--r--tests/destructor/tcustomstrings.nim24
-rw-r--r--tests/destructor/tcycle1.nim54
-rw-r--r--tests/destructor/tcycle2.nim36
-rw-r--r--tests/destructor/tcycle3.nim98
-rw-r--r--tests/destructor/tdangingref_simple.nim32
-rw-r--r--tests/destructor/tdestructor.nim101
-rw-r--r--tests/destructor/tdestructor3.nim151
-rw-r--r--tests/destructor/tdestructor_too_late.nim14
-rw-r--r--tests/destructor/tdistinctseq.nim8
-rw-r--r--tests/destructor/tdont_return_unowned_from_owned.nim56
-rw-r--r--tests/destructor/terror_module.nim20
-rw-r--r--tests/destructor/texceptions.nim29
-rw-r--r--tests/destructor/texplicit_move.nim30
-rw-r--r--tests/destructor/tfinalizer.nim31
-rw-r--r--tests/destructor/tgcdestructors.nim203
-rw-r--r--tests/destructor/tgcleak4.nim47
-rw-r--r--tests/destructor/tglobaldestructor.nim9
-rw-r--r--tests/destructor/tgotoexc_leak.nim19
-rwxr-xr-xtests/destructor/tgotoexceptions.nim117
-rw-r--r--tests/destructor/tgotoexceptions2.nim104
-rw-r--r--tests/destructor/tgotoexceptions3.nim7
-rw-r--r--tests/destructor/tgotoexceptions4.nim60
-rw-r--r--tests/destructor/tgotoexceptions5.nim45
-rw-r--r--tests/destructor/tgotoexceptions6.nim10
-rw-r--r--tests/destructor/tgotoexceptions7.nim49
-rw-r--r--tests/destructor/tgotoexceptions8.nim76
-rw-r--r--tests/destructor/tinvalid_rebind.nim15
-rw-r--r--tests/destructor/tmatrix.nim135
-rw-r--r--tests/destructor/tmisc_destructors.nim43
-rw-r--r--tests/destructor/tmove.nim18
-rw-r--r--tests/destructor/tmove_objconstr.nim151
-rw-r--r--tests/destructor/tnewruntime_misc.nim155
-rw-r--r--tests/destructor/tnewruntime_strutils.nim254
-rw-r--r--tests/destructor/tnonvardestructor.nim247
-rw-r--r--tests/destructor/tobjfield_analysis.nim51
-rw-r--r--tests/destructor/topt.nim61
-rw-r--r--tests/destructor/topttree.nim7
-rw-r--r--tests/destructor/towned_binary_tree.nim91
-rw-r--r--tests/destructor/tprevent_assign.nim33
-rw-r--r--tests/destructor/tprevent_assign2.nim56
-rw-r--r--tests/destructor/tprevent_assign3.nim54
-rw-r--r--tests/destructor/trecursive.nim60
-rw-r--r--tests/destructor/tselect.nim50
-rw-r--r--tests/destructor/tsetjmp_raise.nim11
-rw-r--r--tests/destructor/tsimpleclosure.nim62
-rw-r--r--tests/destructor/tsink.nim70
-rw-r--r--tests/destructor/ttuple.nim130
-rw-r--r--tests/destructor/turn_destroy_into_finalizer.nim26
-rw-r--r--tests/destructor/tuse_ownedref_after_move.nim57
-rw-r--r--tests/destructor/tuse_result_prevents_sinks.nim37
-rw-r--r--tests/destructor/tv2_cast.nim116
-rw-r--r--tests/destructor/tv2_raise.nim53
-rw-r--r--tests/destructor/twasmoved.nim14
-rw-r--r--tests/destructor/twasmoved_error.nim37
-rw-r--r--tests/destructor/twidgets.nim76
-rw-r--r--tests/destructor/twidgets_unown.nim72
-rw-r--r--tests/dir with space/more spaces/mspace.nim1
-rw-r--r--tests/dir with space/tspace.nim9
-rw-r--r--tests/discard/t23677.nim12
-rw-r--r--tests/discard/tdiscardable.nim148
-rw-r--r--tests/discard/tfinallyerrmsg.nim19
-rw-r--r--tests/discard/tillegaldiscard.nim9
-rw-r--r--tests/discard/tillegaldiscardtypes.nim14
-rw-r--r--tests/discard/tneedsdiscard.nim2
-rw-r--r--tests/discard/tneedsdiscard_in_for.nim22
-rw-r--r--tests/discard/tvoidcontext.nim2
-rw-r--r--tests/distinct/t7165.nim15
-rw-r--r--tests/distinct/tborrow.nim132
-rw-r--r--tests/distinct/tborrowdot.nim13
-rw-r--r--tests/distinct/tcomplexaddressableconv.nim21
-rw-r--r--tests/distinct/tcurrncy.nim38
-rw-r--r--tests/distinct/tdistinct.nim227
-rw-r--r--tests/distinct/tdistinct_consts.nim20
-rw-r--r--tests/distinct/tdistinct_issues.nim81
-rw-r--r--tests/distinct/tinvalidborrow.nim42
-rw-r--r--tests/distinct/tnil.nim33
-rw-r--r--tests/distinct/typeclassborrow.nim56
-rw-r--r--tests/dll/client.nim8
-rw-r--r--tests/dll/nimhcr_0.nim4
-rw-r--r--tests/dll/nimhcr_0_1.nim14
-rw-r--r--tests/dll/nimhcr_0_2.nim18
-rw-r--r--tests/dll/nimhcr_0_3.nim19
-rw-r--r--tests/dll/nimhcr_0_4.nim19
-rw-r--r--tests/dll/nimhcr_0_5.nim2
-rw-r--r--tests/dll/nimhcr_0_6.nim4
-rw-r--r--tests/dll/nimhcr_1.nim (renamed from tinyc/tests/tests2/81_types.expect)0
-rw-r--r--tests/dll/nimhcr_1_1.nim51
-rw-r--r--tests/dll/nimhcr_1_2.nim4
-rw-r--r--tests/dll/nimhcr_1_3.nim (renamed from tinyc/tests/tests2/82_attribs_position.expect)0
-rw-r--r--tests/dll/nimhcr_2.nim (renamed from tinyc/tests/tests2/92_enum_bitfield.expect)0
-rw-r--r--tests/dll/nimhcr_2_1.nim32
-rw-r--r--tests/dll/nimhcr_2_2.nim7
-rw-r--r--tests/dll/nimhcr_2_3.nim (renamed from web/links.rst)0
-rw-r--r--tests/dll/nimhcr_basic.nim8
-rw-r--r--tests/dll/nimhcr_integration.nim170
-rw-r--r--tests/dll/nimhcr_unit.nim149
-rw-r--r--tests/dll/nimhcr_unit.nim.cfg2
-rw-r--r--tests/dll/server.nim17
-rw-r--r--tests/dll/test_nimhcr_integration.bat10
-rwxr-xr-xtests/dll/test_nimhcr_integration.sh17
-rw-r--r--tests/dll/visibility.nim43
-rw-r--r--tests/dummy.txt1
-rw-r--r--tests/effects/tcast_as_pragma.nim18
-rw-r--r--tests/effects/tdiagnostic_messages.nim37
-rw-r--r--tests/effects/teffects1.nim39
-rw-r--r--tests/effects/teffects10.nim12
-rw-r--r--tests/effects/teffects11.nim21
-rw-r--r--tests/effects/teffects12.nim52
-rw-r--r--tests/effects/teffects13.nim19
-rw-r--r--tests/effects/teffects14.nim15
-rw-r--r--tests/effects/teffects15.nim18
-rw-r--r--tests/effects/teffects16.nim20
-rw-r--r--tests/effects/teffects17.nim17
-rw-r--r--tests/effects/teffects18.nim19
-rw-r--r--tests/effects/teffects19.nim23
-rw-r--r--tests/effects/teffects2.nim8
-rw-r--r--tests/effects/teffects3.nim9
-rw-r--r--tests/effects/teffects4.nim11
-rw-r--r--tests/effects/teffects6.nim33
-rw-r--r--tests/effects/teffects7.nim16
-rw-r--r--tests/effects/teffects8.nim13
-rw-r--r--tests/effects/teffects9.nim22
-rw-r--r--tests/effects/teffectsmisc.nim39
-rw-r--r--tests/effects/tfuncs_cannot_mutate.nim35
-rw-r--r--tests/effects/tfuncs_cannot_mutate2.nim24
-rw-r--r--tests/effects/tfuncs_cannot_mutate3.nim35
-rw-r--r--tests/effects/tfuncs_cannot_mutate_simple.nim19
-rw-r--r--tests/effects/tgcsafe.nim15
-rw-r--r--tests/effects/tgcsafe2.nim2
-rw-r--r--tests/effects/tgcsafe3.nim21
-rw-r--r--tests/effects/thooks.nim16
-rw-r--r--tests/effects/tlaxeffects.nim11
-rw-r--r--tests/effects/tnestedprocs.nim63
-rw-r--r--tests/effects/tnosideeffect.nim24
-rw-r--r--tests/effects/tsidee1.nim5
-rw-r--r--tests/effects/tsidee2.nim4
-rw-r--r--tests/effects/tsidee3.nim4
-rw-r--r--tests/effects/tsidee4.nim4
-rw-r--r--tests/effects/tstrict_caseobjects.nim47
-rw-r--r--tests/effects/tstrict_effects.nim27
-rw-r--r--tests/effects/tstrict_effects2.nim28
-rw-r--r--tests/effects/tstrict_effects3.nim57
-rw-r--r--tests/effects/tstrict_effects_sort.nim27
-rw-r--r--tests/effects/tstrict_funcs.nim46
-rw-r--r--tests/effects/tstrict_funcs_imports.nim176
-rw-r--r--tests/effects/tstrict_funcs_imports_js.nim20
-rw-r--r--tests/effects/tstrictfuncs_misc.nim65
-rw-r--r--tests/enum/m16462_1.nim3
-rw-r--r--tests/enum/m16462_2.nim3
-rw-r--r--tests/enum/mcrossmodule.nim6
-rw-r--r--tests/enum/t16462.nim5
-rw-r--r--tests/enum/t21863.nim28
-rw-r--r--tests/enum/tambiguousoverloads.nim26
-rw-r--r--tests/enum/tbasicenum.nim11
-rw-r--r--tests/enum/tcrossmodule.nim15
-rw-r--r--tests/enum/tenum.nim275
-rw-r--r--tests/enum/tenum2.nim16
-rw-r--r--tests/enum/tenum3.nim16
-rw-r--r--tests/enum/tenum_duplicate.nim10
-rw-r--r--tests/enum/tenum_invalid.nim8
-rw-r--r--tests/enum/tenum_no_rtti.nim12
-rw-r--r--tests/enum/tenum_self.nim11
-rw-r--r--tests/enum/tenumalias.nim7
-rw-r--r--tests/enum/tenumfieldpragma.nim22
-rw-r--r--tests/enum/tenumfieldpragmanoannot.nim10
-rw-r--r--tests/enum/tenumhole.nim17
-rw-r--r--tests/enum/tenumitems.nim4
-rw-r--r--tests/enum/tenummix.nim4
-rw-r--r--tests/enum/tenumoffset.nim20
-rw-r--r--tests/enum/tnamedenumfields.nim23
-rw-r--r--tests/enum/toptions.nim18
-rw-r--r--tests/enum/toverloadable_enums.nim120
-rw-r--r--tests/enum/toverloadedname.nim7
-rw-r--r--tests/enum/tpure_enums_conflict.nim27
-rw-r--r--tests/enum/tpure_enums_conflict_legacy.nim25
-rw-r--r--tests/enum/tredefinition.nim9
-rw-r--r--tests/enum/ttypenameconflict.nim13
-rw-r--r--tests/errmsgs/m8794.nim2
-rw-r--r--tests/errmsgs/mambparam1.nim1
-rw-r--r--tests/errmsgs/mambparam2.nim2
-rw-r--r--tests/errmsgs/mambparam3.nim1
-rw-r--r--tests/errmsgs/mb.nim (renamed from tests/clearmsg/mb.nim)0
-rw-r--r--tests/errmsgs/mc.nim (renamed from tests/clearmsg/mc.nim)0
-rw-r--r--tests/errmsgs/t10251.nim19
-rw-r--r--tests/errmsgs/t10376.nim32
-rw-r--r--tests/errmsgs/t10489_a.nim9
-rw-r--r--tests/errmsgs/t10489_b.nim9
-rw-r--r--tests/errmsgs/t10542.nim24
-rw-r--r--tests/errmsgs/t10594.nim7
-rw-r--r--tests/errmsgs/t10734.nim20
-rw-r--r--tests/errmsgs/t10735.nim68
-rw-r--r--tests/errmsgs/t1154.nim4
-rw-r--r--tests/errmsgs/t12844.nim13
-rw-r--r--tests/errmsgs/t14444.nim14
-rw-r--r--tests/errmsgs/t16654.nim12
-rw-r--r--tests/errmsgs/t17460.nim19
-rw-r--r--tests/errmsgs/t18327.nim5
-rw-r--r--tests/errmsgs/t18886.nim18
-rw-r--r--tests/errmsgs/t18983.nim7
-rw-r--r--tests/errmsgs/t19224.nim12
-rw-r--r--tests/errmsgs/t19882.nim10
-rw-r--r--tests/errmsgs/t19882_2.nim5
-rw-r--r--tests/errmsgs/t21257.nim20
-rw-r--r--tests/errmsgs/t22097.nim9
-rw-r--r--tests/errmsgs/t22284.nim25
-rw-r--r--tests/errmsgs/t22753.nim52
-rw-r--r--tests/errmsgs/t22852.nim9
-rw-r--r--tests/errmsgs/t22996.nim7
-rw-r--r--tests/errmsgs/t23060.nim5
-rw-r--r--tests/errmsgs/t23419.nim5
-rw-r--r--tests/errmsgs/t23435.nim12
-rw-r--r--tests/errmsgs/t23536.nim26
-rw-r--r--tests/errmsgs/t2614.nim21
-rw-r--r--tests/errmsgs/t5167_4.nim2
-rw-r--r--tests/errmsgs/t5167_5.nim28
-rw-r--r--tests/errmsgs/t5282.nim5
-rw-r--r--tests/errmsgs/t5870.nim17
-rw-r--r--tests/errmsgs/t6281.nim9
-rw-r--r--tests/errmsgs/t6483.nim31
-rw-r--r--tests/errmsgs/t6499.nim6
-rw-r--r--tests/errmsgs/t6608.nim15
-rw-r--r--tests/errmsgs/t8064.nim9
-rw-r--r--tests/errmsgs/t8339.nim8
-rw-r--r--tests/errmsgs/t8434.nim15
-rw-r--r--tests/errmsgs/t8610.nim5
-rw-r--r--tests/errmsgs/t8794.nim23
-rw-r--r--tests/errmsgs/t9768.nim31
-rw-r--r--tests/errmsgs/t9908_01.nim10
-rw-r--r--tests/errmsgs/t9908_02.nim10
-rw-r--r--tests/errmsgs/ta.nim (renamed from tests/clearmsg/ta.nim)0
-rw-r--r--tests/errmsgs/tambparam.nim16
-rw-r--r--tests/errmsgs/tambparam_legacy.nim14
-rw-r--r--tests/errmsgs/tassignunpack.nim3
-rw-r--r--tests/errmsgs/tcall_with_default_arg.nim18
-rw-r--r--tests/errmsgs/tcannot_capture_builtin.nim2
-rw-r--r--tests/errmsgs/tcase_stmt.nim29
-rw-r--r--tests/errmsgs/tconceptconstraint.nim3
-rw-r--r--tests/errmsgs/tconcisetypemismatch.nim23
-rw-r--r--tests/errmsgs/tconstinfo.nim7
-rw-r--r--tests/errmsgs/tconsttypemismatch.nim (renamed from tests/clearmsg/tconsttypemismatch.nim)3
-rw-r--r--tests/errmsgs/tdeclaredlocs.nim92
-rw-r--r--tests/errmsgs/tdetailed_position.nim2
-rw-r--r--tests/errmsgs/tdistinct_nil.nim23
-rw-r--r--tests/errmsgs/tdont_show_system.nim2
-rw-r--r--tests/errmsgs/temptysetparam.nim5
-rw-r--r--tests/errmsgs/tgcsafety.nim12
-rw-r--r--tests/errmsgs/tgeneral_excepts.nim10
-rw-r--r--tests/errmsgs/tgenericconstraint.nim3
-rw-r--r--tests/errmsgs/tgenericmismatchsegfault.nim13
-rw-r--r--tests/errmsgs/tgenericmismatchsegfault_legacy.nim10
-rw-r--r--tests/errmsgs/tinconsistentgensym.nim25
-rw-r--r--tests/errmsgs/tinteger_literals.nim14
-rw-r--r--tests/errmsgs/tinvalidinout.nim2
-rw-r--r--tests/errmsgs/tmacroerrorproc.nim (renamed from tests/clearmsg/tmacroerrorproc.nim)2
-rw-r--r--tests/errmsgs/tmake_tuple_visible.nim7
-rw-r--r--tests/errmsgs/tmetaobjectfields.nim75
-rw-r--r--tests/errmsgs/tmultiple_finally.nim12
-rw-r--r--tests/errmsgs/tnested_empty_seq.nim2
-rw-r--r--tests/errmsgs/tnested_generic_instantiation.nim24
-rw-r--r--tests/errmsgs/tnested_generic_instantiation2.nim27
-rw-r--r--tests/errmsgs/tnnodeadd.nim8
-rw-r--r--tests/errmsgs/tnnodeindex.nim8
-rw-r--r--tests/errmsgs/tnnodeindexkind.nim8
-rw-r--r--tests/errmsgs/tnon_concrete_cast.nim2
-rw-r--r--tests/errmsgs/tnoop.nim12
-rw-r--r--tests/errmsgs/tproc_mismatch.nim74
-rw-r--r--tests/errmsgs/tproper_stacktrace.nim37
-rw-r--r--tests/errmsgs/tproper_stacktrace2.nim3
-rw-r--r--tests/errmsgs/tproper_stacktrace3.nim3
-rw-r--r--tests/errmsgs/treportunused.nim65
-rw-r--r--tests/errmsgs/tshow_asgn.nim2
-rw-r--r--tests/errmsgs/tsigmatch.nim172
-rw-r--r--tests/errmsgs/tsigmatch2.nim47
-rw-r--r--tests/errmsgs/tsimpletypecheck.nim10
-rw-r--r--tests/errmsgs/tstaticexprnotype.nim5
-rw-r--r--tests/errmsgs/tstaticexprscope.nim11
-rw-r--r--tests/errmsgs/tstaticresult.nim10
-rw-r--r--tests/errmsgs/tsubscriptmismatch.nim11
-rw-r--r--tests/errmsgs/tsubscriptmismatch_legacy.nim10
-rw-r--r--tests/errmsgs/ttupleindexoutofbounds.nim2
-rw-r--r--tests/errmsgs/ttypeAllowed.nim28
-rw-r--r--tests/errmsgs/tuncheckedarrayvar.nim7
-rw-r--r--tests/errmsgs/tundeclared_field.nim50
-rw-r--r--tests/errmsgs/tundeclared_routine.nim44
-rw-r--r--tests/errmsgs/tundeclared_routine_compiles.nim11
-rw-r--r--tests/errmsgs/tunknown_named_parameter.nim27
-rw-r--r--tests/errmsgs/tunresolvedinnerproc.nim10
-rw-r--r--tests/errmsgs/tuntypedoverload.nim37
-rw-r--r--tests/errmsgs/twrong_at_operator.nim14
-rw-r--r--tests/errmsgs/twrong_explicit_typeargs.nim26
-rw-r--r--tests/errmsgs/twrong_explicit_typeargs_legacy.nim25
-rw-r--r--tests/errmsgs/twrongcolon.nim7
-rw-r--r--tests/exception/m21261.nim1
-rw-r--r--tests/exception/m22469.nim4
-rw-r--r--tests/exception/t13115.nim31
-rw-r--r--tests/exception/t18620.nim17
-rw-r--r--tests/exception/t20613.nim8
-rw-r--r--tests/exception/t21261.nim9
-rw-r--r--tests/exception/t22008.nim8
-rw-r--r--tests/exception/t22469.nim16
-rw-r--r--tests/exception/t9657.nim12
-rw-r--r--tests/exception/tcontinuexc.nim8
-rw-r--r--tests/exception/tcpp_imported_exc.nim30
-rw-r--r--tests/exception/tcpp_imported_exc2.nim10
-rw-r--r--tests/exception/tdefer1.nim10
-rw-r--r--tests/exception/tdont_overwrite_typename.nim4
-rw-r--r--tests/exception/testindexerroroutput.nims23
-rw-r--r--tests/exception/texcas.nim5
-rw-r--r--tests/exception/texception_inference.nim32
-rw-r--r--tests/exception/texceptionbreak.nim9
-rw-r--r--tests/exception/texceptions.nim68
-rw-r--r--tests/exception/texceptions2.nim130
-rw-r--r--tests/exception/texcpt1.nim15
-rw-r--r--tests/exception/texcsub.nim4
-rw-r--r--tests/exception/tfinally.nim6
-rw-r--r--tests/exception/tfinally2.nim12
-rw-r--r--tests/exception/tfinally3.nim7
-rw-r--r--tests/exception/tfinally4.nim37
-rw-r--r--tests/exception/tnestedreturn.nim1
-rw-r--r--tests/exception/tnestedreturn2.nim1
-rw-r--r--tests/exception/tonraise.nim34
-rw-r--r--tests/exception/treraise.nim4
-rw-r--r--tests/exception/tsetexceptions.nim11
-rw-r--r--tests/exception/tshow_real_exception_name.nim28
-rw-r--r--tests/exception/tunhandledexc.nim5
-rw-r--r--tests/exception/twrongexc.nim7
-rw-r--r--tests/exprs/t22604.nim36
-rw-r--r--tests/exprs/texprstmt.nim2
-rw-r--r--tests/exprs/tifexpr_typeinference.nim8
-rw-r--r--tests/exprs/tresultwarning.nim2
-rw-r--r--tests/exprs/tstmtexp.nim3
-rw-r--r--tests/exprs/tstmtexprs.nim16
-rw-r--r--tests/fields/tfieldindex.nim21
-rw-r--r--tests/fields/tfielditerator.nim46
-rw-r--r--tests/fields/tfielditerator2.nim70
-rw-r--r--tests/fields/tfields_in_template.nim15
-rw-r--r--tests/fields/tfields_with_break.nim33
-rw-r--r--tests/fields/timplicitfieldswithpartial.nim35
-rw-r--r--tests/flags/tgenscript.nim5
-rw-r--r--tests/float/tfloat1.nim5
-rw-r--r--tests/float/tfloat2.nim5
-rw-r--r--tests/float/tfloat3.nim15
-rw-r--r--tests/float/tfloat4.nim19
-rw-r--r--tests/float/tfloat5.nim7
-rw-r--r--tests/float/tfloat6.nim22
-rw-r--r--tests/float/tfloat7.nim7
-rw-r--r--tests/float/tfloatmod.nim130
-rw-r--r--tests/float/tfloatnan.nim45
-rw-r--r--tests/float/tfloatrange.nim50
-rw-r--r--tests/float/tfloats.nim163
-rw-r--r--tests/float/tissue5821.nim8
-rw-r--r--tests/fragmentation/tfragment_alloc.nim10
-rw-r--r--tests/fragmentation/tfragment_gc.nim2
-rw-r--r--tests/gc/closureleak.nim41
-rw-r--r--tests/gc/cyclecollector.nim6
-rw-r--r--tests/gc/cycleleak.nim2
-rw-r--r--tests/gc/gcbench.nim86
-rw-r--r--tests/gc/gcemscripten.nim2
-rw-r--r--tests/gc/gcleak.nim15
-rw-r--r--tests/gc/gcleak2.nim20
-rw-r--r--tests/gc/gcleak3.nim8
-rw-r--r--tests/gc/gcleak4.nim13
-rw-r--r--tests/gc/gcleak5.nim2
-rw-r--r--tests/gc/gctest.nim14
-rw-r--r--tests/gc/growobjcrash.nim11
-rw-r--r--tests/gc/panicoverride.nim14
-rw-r--r--tests/gc/stackrefleak.nim3
-rw-r--r--tests/gc/tdisable_orc.nim9
-rw-r--r--tests/gc/thavlak.nim346
-rw-r--r--tests/gc/tlists.nim10
-rw-r--r--tests/gc/trace_globals.nim33
-rw-r--r--tests/gc/tregionleak.nim23
-rw-r--r--tests/gc/tstandalone.nim14
-rw-r--r--tests/gc/weakrefs.nim9
-rw-r--r--tests/generics/m14509.nim16
-rw-r--r--tests/generics/m22373a.nim7
-rw-r--r--tests/generics/m22373b.nim18
-rw-r--r--tests/generics/m3770.nim11
-rw-r--r--tests/generics/mdotlookup.nim18
-rw-r--r--tests/generics/mfriends.nim (renamed from tests/friends/mfriends.nim)0
-rw-r--r--tests/generics/module_with_generics.nim2
-rw-r--r--tests/generics/mopensymimport1.nim34
-rw-r--r--tests/generics/mopensymimport2.nim16
-rw-r--r--tests/generics/mtypenodes.nim6
-rw-r--r--tests/generics/muninstantiatedgenericcalls.nim26
-rw-r--r--tests/generics/t1050.nim29
-rw-r--r--tests/generics/t1056.nim25
-rw-r--r--tests/generics/t12938.nim9
-rw-r--r--tests/generics/t13525.nim6
-rw-r--r--tests/generics/t14193.nim6
-rw-r--r--tests/generics/t14509.nim4
-rw-r--r--tests/generics/t1500.nim8
-rw-r--r--tests/generics/t17509.nim25
-rw-r--r--tests/generics/t1789.nim44
-rw-r--r--tests/generics/t18823.nim6
-rw-r--r--tests/generics/t18859.nim17
-rw-r--r--tests/generics/t19848.nim16
-rw-r--r--tests/generics/t20996.nim15
-rw-r--r--tests/generics/t21742.nim10
-rw-r--r--tests/generics/t21760.nim8
-rw-r--r--tests/generics/t21958.nim11
-rw-r--r--tests/generics/t22373.nim16
-rw-r--r--tests/generics/t22826.nim8
-rw-r--r--tests/generics/t23186.nim155
-rw-r--r--tests/generics/t23790.nim14
-rw-r--r--tests/generics/t23853.nim91
-rw-r--r--tests/generics/t23854.nim71
-rw-r--r--tests/generics/t23855.nim61
-rw-r--r--tests/generics/t2tables.nim4
-rw-r--r--tests/generics/t3770.nim13
-rw-r--r--tests/generics/t3977.nim14
-rw-r--r--tests/generics/t4668.nim166
-rw-r--r--tests/generics/t4884.nim11
-rw-r--r--tests/generics/t500.nim8
-rw-r--r--tests/generics/t5570.nim27
-rw-r--r--tests/generics/t5643.nim30
-rw-r--r--tests/generics/t5683.nim31
-rw-r--r--tests/generics/t5926.nim22
-rw-r--r--tests/generics/t6060.nim11
-rw-r--r--tests/generics/t6137.nim28
-rw-r--r--tests/generics/t6637.nim9
-rw-r--r--tests/generics/t7141.nim9
-rw-r--r--tests/generics/t7446.nim10
-rw-r--r--tests/generics/t7839.nim9
-rw-r--r--tests/generics/t8270.nim7
-rw-r--r--tests/generics/t88.nim33
-rw-r--r--tests/generics/tableref_is_nil.nim9
-rw-r--r--tests/generics/taliashijack.nim8
-rw-r--r--tests/generics/tarc_misc.nim21
-rw-r--r--tests/generics/tarray_with_somenumber.nim11
-rw-r--r--tests/generics/tbadcache.nim26
-rw-r--r--tests/generics/tbaddeprecated.nim55
-rw-r--r--tests/generics/tbind_bracket.nim20
-rw-r--r--tests/generics/tbintre2.nim31
-rw-r--r--tests/generics/tbintree.nim23
-rw-r--r--tests/generics/tbracketinstantiation.nim86
-rw-r--r--tests/generics/tcalltype.nim26
-rw-r--r--tests/generics/tcan.nim46
-rw-r--r--tests/generics/tcan_alias_generic.nim10
-rw-r--r--tests/generics/tcan_alias_specialised_generic.nim16
-rw-r--r--tests/generics/tcan_inherit_generic.nim16
-rw-r--r--tests/generics/tcan_specialise_generic.nim10
-rw-r--r--tests/generics/tclosed_sym.nim11
-rw-r--r--tests/generics/tconfusing_arrow.nim15
-rw-r--r--tests/generics/tconstraints.nim16
-rw-r--r--tests/generics/tdictdestruct.nim20
-rw-r--r--tests/generics/tdont_use_inner_scope.nim27
-rw-r--r--tests/generics/tdotlookup.nim10
-rw-r--r--tests/generics/texplicitgeneric1.nim4
-rw-r--r--tests/generics/texplicitgeneric2.nim8
-rw-r--r--tests/generics/tfakedependenttypes.nim61
-rw-r--r--tests/generics/tforward_generic.nim28
-rw-r--r--tests/generics/tforwardgenericconstrained.nim73
-rw-r--r--tests/generics/tfriends.nim (renamed from tests/friends/tfriends.nim)0
-rw-r--r--tests/generics/tgeneric0.nim203
-rw-r--r--tests/generics/tgeneric1.nim53
-rw-r--r--tests/generics/tgeneric2.nim15
-rw-r--r--tests/generics/tgeneric3.nim141
-rw-r--r--tests/generics/tgeneric4.nim10
-rw-r--r--tests/generics/tgeneric_closure.nim37
-rw-r--r--tests/generics/tgeneric_inheritance.nim19
-rw-r--r--tests/generics/tgeneric_recursionlimit.nim123
-rw-r--r--tests/generics/tgenericconst.nim39
-rw-r--r--tests/generics/tgenericdefaults.nim29
-rw-r--r--tests/generics/tgenericmatcher.nim50
-rw-r--r--tests/generics/tgenericmatcher2.nim18
-rw-r--r--tests/generics/tgenericprocvar.nim1
-rw-r--r--tests/generics/tgenericprop.nim12
-rw-r--r--tests/generics/tgenericrefs.nim38
-rw-r--r--tests/generics/tgenerics_issues.nim894
-rw-r--r--tests/generics/tgenerics_various.nim254
-rw-r--r--tests/generics/tgenericsdefaultvalues.nim14
-rw-r--r--tests/generics/tgenericshardcases.nim42
-rw-r--r--tests/generics/tgenerictmpl.nim21
-rw-r--r--tests/generics/tgenerictmpl2.nim2
-rw-r--r--tests/generics/tgenericvariant.nim17
-rw-r--r--tests/generics/tgenericwhen.nim58
-rw-r--r--tests/generics/tgensyminst.nim29
-rw-r--r--tests/generics/timpl_ast.nim80
-rw-r--r--tests/generics/timplicit_and_explicit.nim65
-rw-r--r--tests/generics/timports.nim63
-rw-r--r--tests/generics/tinferredgenericprocs.nim21
-rw-r--r--tests/generics/tinheritable_importcpp.nim10
-rw-r--r--tests/generics/tlamba_in_generic.nim13
-rw-r--r--tests/generics/tlateboundgenericparams.nim145
-rw-r--r--tests/generics/tlateboundstatic.nim2
-rw-r--r--tests/generics/tmacroinjectedsym.nim186
-rw-r--r--tests/generics/tmacroinjectedsymwarning.nim59
-rw-r--r--tests/generics/tmap_auto.nim13
-rw-r--r--tests/generics/tmetafield.nim20
-rw-r--r--tests/generics/tmodule_same_as_proc.nim9
-rw-r--r--tests/generics/tnestedissues.nim24
-rw-r--r--tests/generics/tnestedtemplate.nim9
-rw-r--r--tests/generics/tnullary_generics.nim26
-rw-r--r--tests/generics/tobjecttyperel.nim40
-rw-r--r--tests/generics/tobjecttyperel3.nim12
-rw-r--r--tests/generics/topensymimport.nim5
-rw-r--r--tests/generics/toverloading_typedesc.nim7
-rw-r--r--tests/generics/tparam_binding.nim5
-rw-r--r--tests/generics/tparser_generator.nim415
-rw-r--r--tests/generics/tpointerprocs.nim28
-rw-r--r--tests/generics/tprevent_double_bind.nim21
-rw-r--r--tests/generics/tproctypecache_falsepositive.nim17
-rw-r--r--tests/generics/tptrinheritance.nim20
-rw-r--r--tests/generics/trecursivegenerics.nim96
-rw-r--r--tests/generics/treentranttypes.nim21
-rw-r--r--tests/generics/treturn_inference.nim184
-rw-r--r--tests/generics/tsigtypeop.nim9
-rw-r--r--tests/generics/tspecial_numeric_inference.nim21
-rw-r--r--tests/generics/tspecialised_is_equivalent.nim14
-rw-r--r--tests/generics/tspecialized_procvar.nim17
-rw-r--r--tests/generics/tstatic_constrained.nim79
-rw-r--r--tests/generics/tstatictalias.nim20
-rw-r--r--tests/generics/tsubclassgenericerror.nim11
-rw-r--r--tests/generics/tsubtypeconstraint.nim13
-rw-r--r--tests/generics/ttable_alias.nim7
-rw-r--r--tests/generics/ttempl_in_generic.nim8
-rw-r--r--tests/generics/tthread_generic.nim6
-rw-r--r--tests/generics/ttypeclass_to_typeclass.nim12
-rw-r--r--tests/generics/tuninstantiated_failure.nim16
-rw-r--r--tests/generics/tuninstantiatedgenericcalls.nim517
-rw-r--r--tests/generics/tunique_type.nim2
-rw-r--r--tests/generics/tvarargs_vs_generic.nim26
-rw-r--r--tests/generics/twrong_explicit_typeargs.nim16
-rw-r--r--tests/generics/twrong_floatlit_type.nim2
-rw-r--r--tests/generics/twrong_generic_object.nim4
-rw-r--r--tests/global/a_module.nim6
-rw-r--r--tests/global/t15005.nim18
-rw-r--r--tests/global/t21896.nim9
-rw-r--r--tests/global/t3505.nim41
-rw-r--r--tests/global/t5958.nim9
-rw-r--r--tests/global/tglobal2.nim9
-rw-r--r--tests/global/tglobalforvar.nim4
-rw-r--r--tests/ic/config.nims1
-rw-r--r--tests/ic/mbaseobj.nim7
-rw-r--r--tests/ic/mcompiletime_counter.nim15
-rw-r--r--tests/ic/mdefconverter.nim2
-rw-r--r--tests/ic/mimports.nim9
-rw-r--r--tests/ic/mimportsb.nim4
-rw-r--r--tests/ic/tcompiletime_counter.nim24
-rw-r--r--tests/ic/tconverter.nim18
-rw-r--r--tests/ic/tgenericinst.nim11
-rw-r--r--tests/ic/tgenerics.nim38
-rw-r--r--tests/ic/thallo.nim28
-rw-r--r--tests/ic/timports.nim29
-rw-r--r--tests/ic/tmethods.nim28
-rw-r--r--tests/ic/tstdlib_import_changed.nim15
-rw-r--r--tests/implicit/timplicititems.nim4
-rw-r--r--tests/implicit/timplictderef.nim35
-rw-r--r--tests/import/buzz/m21496.nim1
-rw-r--r--tests/import/fizz/m21496.nim1
-rw-r--r--tests/import/t21496.nim9
-rw-r--r--tests/import/t22065.nim5
-rw-r--r--tests/import/t22208.nim6
-rw-r--r--tests/import/t23167.nim5
-rw-r--r--tests/importalls/m1.nim70
-rw-r--r--tests/importalls/m2.nim3
-rw-r--r--tests/importalls/m3.nim5
-rw-r--r--tests/importalls/m4.nim9
-rw-r--r--tests/importalls/mt0.nim9
-rw-r--r--tests/importalls/mt1.nim23
-rw-r--r--tests/importalls/mt2.nim104
-rw-r--r--tests/importalls/mt3.nim12
-rw-r--r--tests/importalls/mt4.nim4
-rw-r--r--tests/importalls/mt4b.nim3
-rw-r--r--tests/importalls/mt5.nim9
-rw-r--r--tests/importalls/mt6.nim13
-rw-r--r--tests/importalls/mt7.nim14
-rw-r--r--tests/importalls/mt8.nim23
-rw-r--r--tests/importalls/mt9.nim12
-rw-r--r--tests/importalls/tmain2.nim7
-rw-r--r--tests/init/t8314.nim27
-rw-r--r--tests/init/tcompiles.nim101
-rw-r--r--tests/init/tinitchecks_v2.nim83
-rw-r--r--tests/init/tlet.nim55
-rw-r--r--tests/init/tlet_uninit2.nim5
-rw-r--r--tests/init/tlet_uninit3.nim24
-rw-r--r--tests/init/tlet_uninit4.nim12
-rw-r--r--tests/init/toutparam_subtype.nim24
-rw-r--r--tests/init/toutparams.nim14
-rw-r--r--tests/init/tproveinit.nim18
-rw-r--r--tests/init/treturns.nim106
-rw-r--r--tests/init/tuninit1.nim8
-rw-r--r--tests/int/t1.nim61
-rw-r--r--tests/int/tarithm.nim187
-rw-r--r--tests/int/tashr.nim46
-rw-r--r--tests/int/tdiv.nim19
-rw-r--r--tests/int/tints.nim150
-rw-r--r--tests/int/tunsigned64mod.nim (renamed from tests/misc/tunsigned64mod.nim)8
-rw-r--r--tests/int/tunsignedcmp.nim (renamed from tests/misc/tunsignedcmp.nim)0
-rw-r--r--tests/int/tunsignedcomp.nim136
-rw-r--r--tests/int/tunsignedconv.nim115
-rw-r--r--tests/int/tunsignedinc.nim40
-rw-r--r--tests/int/tunsignedmisc.nim (renamed from tests/misc/tunsignedmisc.nim)6
-rw-r--r--tests/int/twrongexplicitvarconv.nim16
-rw-r--r--tests/int/twrongvarconv.nim9
-rw-r--r--tests/isolate/tisolate.nim41
-rw-r--r--tests/isolate/tisolate2.nim22
-rw-r--r--tests/isolate/tisolated_lock.nim67
-rw-r--r--tests/iter/t1550.nim24
-rw-r--r--tests/iter/t16076.nim45
-rw-r--r--tests/iter/t20891.nim28
-rw-r--r--tests/iter/t21306.nim118
-rw-r--r--tests/iter/t21737.nim22
-rw-r--r--tests/iter/t22148.nim15
-rw-r--r--tests/iter/t22548.nim21
-rw-r--r--tests/iter/t22619.nim83
-rw-r--r--tests/iter/t2771.nim25
-rw-r--r--tests/iter/t2closureiters.nim14
-rw-r--r--tests/iter/tanoniter1.nim1
-rw-r--r--tests/iter/tarrayiter.nim14
-rw-r--r--tests/iter/tclosureiters.nim97
-rw-r--r--tests/iter/tcomplex_openarray.nim33
-rw-r--r--tests/iter/tcountup.nim21
-rw-r--r--tests/iter/tgeniteratorinblock.nim54
-rw-r--r--tests/iter/timplicit_auto.nim2
-rw-r--r--tests/iter/titer.nim103
-rw-r--r--tests/iter/titer10.nim4
-rw-r--r--tests/iter/titer11.nim40
-rw-r--r--tests/iter/titer12.nim83
-rw-r--r--tests/iter/titer13.nim83
-rw-r--r--tests/iter/titer14.nim7
-rw-r--r--tests/iter/titer2.nim2
-rw-r--r--tests/iter/titer3.nim38
-rw-r--r--tests/iter/titer4.nim4
-rw-r--r--tests/iter/titer5.nim5
-rw-r--r--tests/iter/titer6.nim4
-rw-r--r--tests/iter/titer7.nim4
-rw-r--r--tests/iter/titer_issues.nim411
-rw-r--r--tests/iter/titer_no_tuple_unpack.nim16
-rw-r--r--tests/iter/titerable.nim2
-rw-r--r--tests/iter/titerautoerr1.nim8
-rw-r--r--tests/iter/titerautoerr2.nim8
-rw-r--r--tests/iter/titerautoerr3.nim9
-rw-r--r--tests/iter/titerconcat.nim (renamed from tests/iter/tconcat.nim)0
-rw-r--r--tests/iter/titertypedesc.nim17
-rw-r--r--tests/iter/titervaropenarray.nim5
-rw-r--r--tests/iter/tkeep_state_between_yield.nim36
-rw-r--r--tests/iter/tmoditer.nim61
-rw-r--r--tests/iter/tnested_closure_iter.nim16
-rw-r--r--tests/iter/tobj_iter.nim18
-rw-r--r--tests/iter/tpermutations.nim15
-rw-r--r--tests/iter/treciter.nim4
-rw-r--r--tests/iter/tshallowcopy_closures.nim9
-rw-r--r--tests/iter/twrap_walkdir.nim5
-rw-r--r--tests/iter/twrongiter.nim2
-rw-r--r--tests/iter/tyieldintry.nim180
-rw-r--r--tests/js/readme.md5
-rw-r--r--tests/js/t11166.nim20
-rw-r--r--tests/js/t11353.nim14
-rw-r--r--tests/js/t11354.nim20
-rw-r--r--tests/js/t11697.nim5
-rw-r--r--tests/js/t12223.nim20
-rw-r--r--tests/js/t12303.nim16
-rw-r--r--tests/js/t12672.nim12
-rw-r--r--tests/js/t14153.nim22
-rw-r--r--tests/js/t14570.nim11
-rw-r--r--tests/js/t17177.nim10
-rw-r--r--tests/js/t20233.nim7
-rw-r--r--tests/js/t20235.nim11
-rw-r--r--tests/js/t21209.nim6
-rw-r--r--tests/js/t21209.nims1
-rw-r--r--tests/js/t21247.nim15
-rw-r--r--tests/js/t21439.nim11
-rw-r--r--tests/js/t6612.nim24
-rw-r--r--tests/js/t7109.nim8
-rw-r--r--tests/js/t7127.nim2
-rw-r--r--tests/js/t7224.nim28
-rw-r--r--tests/js/t7249.nim21
-rw-r--r--tests/js/t7534.nim7
-rw-r--r--tests/js/t8231.nim3
-rw-r--r--tests/js/t8821.nim9
-rw-r--r--tests/js/t8914.nim12
-rw-r--r--tests/js/t9410.nim471
-rw-r--r--tests/js/taddr.nim79
-rw-r--r--tests/js/tarrayboundscheck.nim6
-rw-r--r--tests/js/tasync.nim33
-rw-r--r--tests/js/tasync_pragma.nim27
-rw-r--r--tests/js/tasyncjs.nim107
-rw-r--r--tests/js/tasyncjs_bad.nim22
-rw-r--r--tests/js/tasyncjs_pragma.nim29
-rw-r--r--tests/js/tbasicenum.nim10
-rw-r--r--tests/js/tbasics.nim62
-rw-r--r--tests/js/tbigint_backend.nim57
-rw-r--r--tests/js/tbyvar.nim15
-rw-r--r--tests/js/tclosures.nim54
-rw-r--r--tests/js/tcodegendeclproc.nim2
-rw-r--r--tests/js/tconsole.nim6
-rw-r--r--tests/js/tcopying.nim47
-rw-r--r--tests/js/tcsymbol.nim6
-rw-r--r--tests/js/tdanger.nim17
-rw-r--r--tests/js/tdiscard.nim3
-rw-r--r--tests/js/tdollar_float.nim62
-rw-r--r--tests/js/temptyseq.nim8
-rw-r--r--tests/js/test1.nim34
-rw-r--r--tests/js/test2.nim5
-rw-r--r--tests/js/testobjs.nim19
-rw-r--r--tests/js/tfieldchecks.nim48
-rw-r--r--tests/js/tglobal.nim30
-rw-r--r--tests/js/tindexdefect.nim9
-rw-r--r--tests/js/tjsffi.nim449
-rw-r--r--tests/js/tjsffi_old.nim340
-rw-r--r--tests/js/tjshello.nim2
-rw-r--r--tests/js/tjshello_stacktrace.nim9
-rw-r--r--tests/js/tjsnimscombined.nim1
-rw-r--r--tests/js/tjsnimscombined.nims1
-rw-r--r--tests/js/tlent.nim33
-rw-r--r--tests/js/tmangle.nim12
-rw-r--r--tests/js/tmodify_cstring.nim6
-rw-r--r--tests/js/tnativeexc.nim31
-rw-r--r--tests/js/tneginthash.nim21
-rw-r--r--tests/js/tnilstrs.nim25
-rw-r--r--tests/js/tos.nim21
-rw-r--r--tests/js/trepr.nim93
-rw-r--r--tests/js/treprinifexpr.nim18
-rw-r--r--tests/js/tseqops.nim21
-rw-r--r--tests/js/tsourcemap.nim96
-rw-r--r--tests/js/tstdlib_imports.nim80
-rw-r--r--tests/js/tstdlib_various.nim174
-rw-r--r--tests/js/tstreams.nim22
-rw-r--r--tests/js/tstringitems.nim12
-rw-r--r--tests/js/ttempgen.nim79
-rw-r--r--tests/js/tthismangle.nim23
-rw-r--r--tests/js/ttimes.nim43
-rw-r--r--tests/js/ttypedarray.nim26
-rw-r--r--tests/js/tunion.nim7
-rw-r--r--tests/js/tunittest_error.nim24
-rw-r--r--tests/js/tunittest_error2.nim16
-rw-r--r--tests/js/tunittests.nim11
-rw-r--r--tests/js/tvarargs.nim5
-rw-r--r--tests/js/twritestacktrace.nim12
-rw-r--r--tests/lent/t16898.nim31
-rw-r--r--tests/lent/t17621.nim15
-rw-r--r--tests/lent/tbasic_lent_check.nim47
-rw-r--r--tests/lent/tlent_from_var.nim107
-rw-r--r--tests/lent/tnot_allowed_lent.nim24
-rw-r--r--tests/lent/tnot_allowed_lent2.nim14
-rw-r--r--tests/lent/tvm.nim21
-rw-r--r--tests/let/t7936.nim27
-rw-r--r--tests/let/timportc.nim24
-rw-r--r--tests/let/timportc2.nim8
-rw-r--r--tests/let/tlet.nim3
-rw-r--r--tests/let/tlet2.nim3
-rw-r--r--tests/lexer/mlexerutils.nim9
-rw-r--r--tests/lexer/tcustom_numeric_literals.nim177
-rw-r--r--tests/lexer/tident.nim16
-rw-r--r--tests/lexer/tind1.nim2
-rw-r--r--tests/lexer/tintegerliterals.nim9
-rw-r--r--tests/lexer/tinvalidintegerliteral1.nim7
-rw-r--r--tests/lexer/tinvalidintegerliteral2.nim7
-rw-r--r--tests/lexer/tinvalidintegerliteral3.nim7
-rw-r--r--tests/lexer/tlexerspaces.nim2
-rw-r--r--tests/lexer/tmissingnl.nim5
-rw-r--r--tests/lexer/trawstr.nim (renamed from tests/misc/trawstr.nim)4
-rw-r--r--tests/lexer/tstrlits.nim11
-rw-r--r--tests/lexer/tunary_minus.nim83
-rw-r--r--tests/lexer/tunderscores.nim5
-rw-r--r--tests/lexer/tunicode_operators.nim16
-rw-r--r--tests/lookups/issue_23032/deep_scope.nim2
-rw-r--r--tests/lookups/issue_23172/m23172.nim6
-rw-r--r--tests/lookups/mambsym1.nim (renamed from tests/ambsym/mambsym1.nim)0
-rw-r--r--tests/lookups/mambsym2.nim (renamed from tests/ambsym/mambsym2.nim)0
-rw-r--r--tests/lookups/mambsym3.nim4
-rw-r--r--tests/lookups/mambsym4.nim4
-rw-r--r--tests/lookups/mambsys1.nim (renamed from tests/ambsym/mambsys1.nim)0
-rw-r--r--tests/lookups/mambsys2.nim (renamed from tests/ambsym/mambsys2.nim)0
-rw-r--r--tests/lookups/mambtype1.nim1
-rw-r--r--tests/lookups/mambtype2.nim4
-rw-r--r--tests/lookups/mbind3.nim (renamed from tests/bind/mbind3.nim)0
-rw-r--r--tests/lookups/mdisambsym1.nim2
-rw-r--r--tests/lookups/mdisambsym2.nim1
-rw-r--r--tests/lookups/mdisambsym3.nim1
-rw-r--r--tests/lookups/mmacroamb.nim10
-rw-r--r--tests/lookups/t23032.nim13
-rw-r--r--tests/lookups/t23172.nim9
-rw-r--r--tests/lookups/t23749.nim37
-rw-r--r--tests/lookups/tambiguousemit.nim12
-rw-r--r--tests/lookups/tambprocvar.nim19
-rw-r--r--tests/lookups/tambsym.nim (renamed from tests/ambsym/tambsym.nim)4
-rw-r--r--tests/lookups/tambsym2.nim (renamed from tests/ambsym/tambsym2.nim)3
-rw-r--r--tests/lookups/tambsym3.nim13
-rw-r--r--tests/lookups/tambsymmanual.nim25
-rw-r--r--tests/lookups/tambsys.nim (renamed from tests/ambsym/tambsys.nim)3
-rw-r--r--tests/lookups/tambtype.nim20
-rw-r--r--tests/lookups/tbind.nim78
-rw-r--r--tests/lookups/tbind_for_generics.nim17
-rw-r--r--tests/lookups/tdisambsym.nim8
-rw-r--r--tests/lookups/tenumlocalsym.nim22
-rw-r--r--tests/lookups/test.nim10
-rw-r--r--tests/lookups/tgensym.nim (renamed from tests/gensym/tgensym.nim)0
-rw-r--r--tests/lookups/tgensymgeneric.nim (renamed from tests/gensym/tgensymgeneric.nim)16
-rw-r--r--tests/lookups/tinvalidbindtypedesc.nim10
-rw-r--r--tests/lookups/tmacroamb.nim5
-rw-r--r--tests/lookups/tmoduleclash1.nim13
-rw-r--r--tests/lookups/tmoduleclash2.nim6
-rw-r--r--tests/lookups/tnicerrorforsymchoice.nim23
-rw-r--r--tests/lookups/told_bind_expr.nim15
-rw-r--r--tests/lookups/tprefer_proc.nim4
-rw-r--r--tests/lookups/tredef.nim2
-rw-r--r--tests/lookups/tundeclaredmixin.nim17
-rw-r--r--tests/m14634.nim48
-rw-r--r--tests/macros/m18235.nim42
-rw-r--r--tests/macros/macro_bug.nim11
-rw-r--r--tests/macros/mparsefile.nim4
-rw-r--r--tests/macros/t14227.nim23
-rw-r--r--tests/macros/t14329.nim4
-rw-r--r--tests/macros/t14511.nim54
-rw-r--r--tests/macros/t14847.nim20
-rw-r--r--tests/macros/t15691.nim22
-rw-r--r--tests/macros/t15751.nim11
-rw-r--r--tests/macros/t16758.nim38
-rw-r--r--tests/macros/t17836.nim15
-rw-r--r--tests/macros/t18203.nim15
-rw-r--r--tests/macros/t18235.nim18
-rw-r--r--tests/macros/t19766_20114.nim16
-rw-r--r--tests/macros/t20067.nim28
-rw-r--r--tests/macros/t20435.nim30
-rw-r--r--tests/macros/t21593.nim13
-rw-r--r--tests/macros/t23032_1.nim19
-rw-r--r--tests/macros/t23032_2.nim20
-rw-r--r--tests/macros/t23547.nim23
-rw-r--r--tests/macros/t23784.nim157
-rw-r--r--tests/macros/t7454.nim8
-rw-r--r--tests/macros/t7875.nim22
-rw-r--r--tests/macros/t8997.nim26
-rw-r--r--tests/macros/tastrepr.nim58
-rw-r--r--tests/macros/tbindsym.nim42
-rw-r--r--tests/macros/tbugs.nim106
-rw-r--r--tests/macros/tcasestmtmacro.nim33
-rw-r--r--tests/macros/tclosuremacro.nim38
-rw-r--r--tests/macros/tcollect.nim63
-rw-r--r--tests/macros/tcomplexecho.nim2
-rw-r--r--tests/macros/tcprag.nim32
-rw-r--r--tests/macros/tdebugstmt.nim29
-rw-r--r--tests/macros/tdump.nim13
-rw-r--r--tests/macros/tdumpast.nim168
-rw-r--r--tests/macros/tdumpastgen.nim2
-rw-r--r--tests/macros/tdumptree.nim9
-rw-r--r--tests/macros/tescape_var_into_quotedo_as_const.nim36
-rw-r--r--tests/macros/texpectIdent1.nim18
-rw-r--r--tests/macros/texpectIdent2.nim24
-rw-r--r--tests/macros/texprcolonexpr.nim19
-rw-r--r--tests/macros/tfail_parse.nim15
-rw-r--r--tests/macros/tgenericparams.nim13
-rw-r--r--tests/macros/tgensym.nim65
-rw-r--r--tests/macros/tgentemplates.nim35
-rw-r--r--tests/macros/tgetimpl.nim93
-rw-r--r--tests/macros/tgetraiseslist.nim29
-rw-r--r--tests/macros/tgettype.nim98
-rw-r--r--tests/macros/tgettype2.nim32
-rw-r--r--tests/macros/tgettypeinst.nim33
-rw-r--r--tests/macros/tgettypeinst7737.nim61
-rw-r--r--tests/macros/tidgen.nim19
-rw-r--r--tests/macros/tincremental.nim150
-rw-r--r--tests/macros/tinvalidtypesym.nim14
-rw-r--r--tests/macros/tisexported.nim10
-rw-r--r--tests/macros/tlexerex.nim15
-rw-r--r--tests/macros/tlocktypednode1.nim12
-rw-r--r--tests/macros/tlocktypednode2.nim12
-rw-r--r--tests/macros/tlocktypednode3.nim15
-rw-r--r--tests/macros/tmacro1.nim44
-rw-r--r--tests/macros/tmacro2.nim8
-rw-r--r--tests/macros/tmacro3.nim2
-rw-r--r--tests/macros/tmacro4.nim5
-rw-r--r--tests/macros/tmacro5.nim4
-rw-r--r--tests/macros/tmacro6.nim75
-rw-r--r--tests/macros/tmacro7.nim36
-rw-r--r--tests/macros/tmacro8.nim35
-rw-r--r--tests/macros/tmacro_in_template.nim10
-rw-r--r--tests/macros/tmacrogenerics.nim11
-rw-r--r--tests/macros/tmacrogensym.nim65
-rw-r--r--tests/macros/tmacrogetimpl.nim31
-rw-r--r--tests/macros/tmacros1.nim58
-rw-r--r--tests/macros/tmacros_issues.nim521
-rw-r--r--tests/macros/tmacros_various.nim391
-rw-r--r--tests/macros/tmacrostmt.nim133
-rw-r--r--tests/macros/tmacrotypes.nim159
-rw-r--r--tests/macros/tmemit.nim7
-rw-r--r--tests/macros/tmsginfo.nim24
-rw-r--r--tests/macros/tnewlit.nim45
-rw-r--r--tests/macros/tnewproc.nim51
-rw-r--r--tests/macros/tparsefile.nim11
-rw-r--r--tests/macros/tprintf.nim16
-rw-r--r--tests/macros/tprocgettype.nim28
-rw-r--r--tests/macros/tprochelpers.nim22
-rw-r--r--tests/macros/tquotedo.nim51
-rw-r--r--tests/macros/tquotewords.nim1
-rw-r--r--tests/macros/trecmacro.nim2
-rw-r--r--tests/macros/tsame_name_497.nim3
-rw-r--r--tests/macros/tsametype.nim1
-rw-r--r--tests/macros/tslice.nim38
-rw-r--r--tests/macros/tstaticparamsmacro.nim11
-rw-r--r--tests/macros/tstringinterp.nim2
-rw-r--r--tests/macros/tstructuredlogging.nim6
-rw-r--r--tests/macros/ttemplatesymbols.nim4
-rw-r--r--tests/macros/ttryparseexpr.nim6
-rw-r--r--tests/macros/ttypenodes.nim16
-rw-r--r--tests/macros/tvarargsuntyped.nim30
-rw-r--r--tests/macros/tvarnimnode.nim19
-rw-r--r--tests/macros/tvtable.nim2
-rw-r--r--tests/macros/typesafeprintf.nim7
-rw-r--r--tests/macros/typesapi.nim17
-rw-r--r--tests/magics/tlowhigh.nim24
-rw-r--r--tests/manyloc/argument_parser/argument_parser.nim35
-rw-r--r--tests/manyloc/argument_parser/ex_wget.nim2
-rw-r--r--tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim10
-rw-r--r--tests/manyloc/keineschweine/dependencies/enet/enet.nim79
-rw-r--r--tests/manyloc/keineschweine/dependencies/genpacket/genpacket.nim295
-rw-r--r--tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim22
-rw-r--r--tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim2
-rw-r--r--tests/manyloc/keineschweine/dependencies/genpacket/streams_enh.nim6
-rw-r--r--tests/manyloc/keineschweine/dependencies/nake/nake.nim13
-rw-r--r--tests/manyloc/keineschweine/enet_server/enet_client.nim4
-rw-r--r--tests/manyloc/keineschweine/enet_server/enet_server.nim10
-rw-r--r--tests/manyloc/keineschweine/enet_server/server_utils.nim4
-rw-r--r--tests/manyloc/keineschweine/keineschweine.nim16
-rw-r--r--tests/manyloc/keineschweine/keineschweine.nim.cfg1
-rw-r--r--tests/manyloc/keineschweine/lib/client_helpers.nim16
-rw-r--r--tests/manyloc/keineschweine/lib/estreams.nim10
-rw-r--r--tests/manyloc/keineschweine/lib/glut.nim2
-rw-r--r--tests/manyloc/keineschweine/lib/map_filter.nim8
-rw-r--r--tests/manyloc/keineschweine/lib/sg_assets.nim26
-rw-r--r--tests/manyloc/keineschweine/lib/sg_gui.nim50
-rw-r--r--tests/manyloc/keineschweine/lib/sg_packets.nim6
-rw-r--r--tests/manyloc/keineschweine/lib/vehicles.nim2
-rw-r--r--tests/manyloc/keineschweine/lib/zlib_helpers.nim17
-rw-r--r--tests/manyloc/keineschweine/server/old_dirserver.nim12
-rw-r--r--tests/manyloc/keineschweine/server/old_server_utils.nim4
-rw-r--r--tests/manyloc/keineschweine/server/old_sg_server.nim10
-rw-r--r--tests/manyloc/keineschweine/server/sg_lobby.nim7
-rw-r--r--tests/manyloc/nake/nake.nim17
-rw-r--r--tests/manyloc/nake/nakefile.nim26
-rw-r--r--tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim2
-rw-r--r--tests/manyloc/named_argument_bug/tri_engine/gfx/gl/primitive.nim8
-rw-r--r--tests/manyloc/named_argument_bug/tri_engine/math/vec.nim4
-rw-r--r--tests/manyloc/standalone/barebone.nim9
-rw-r--r--tests/manyloc/standalone/panicoverride.nim5
-rw-r--r--tests/manyloc/standalone2/panicoverride.nim14
-rw-r--r--tests/manyloc/standalone2/tavr.nim7
-rw-r--r--tests/manyloc/standalone2/tavr.nim.cfg5
-rw-r--r--tests/metatype/tautoproc.nim22
-rw-r--r--tests/metatype/tautotypetrait.nim14
-rw-r--r--tests/metatype/tbindtypedesc.nim19
-rw-r--r--tests/metatype/tcompilesregression.nim18
-rw-r--r--tests/metatype/tcompositetypeclasses.nim4
-rw-r--r--tests/metatype/tconstraints.nim15
-rw-r--r--tests/metatype/tcps.nim35
-rw-r--r--tests/metatype/tfieldaccessor.nim17
-rw-r--r--tests/metatype/tmatrix.nim48
-rw-r--r--tests/metatype/tmatrix1.nim4
-rw-r--r--tests/metatype/tmatrix3.nim2
-rw-r--r--tests/metatype/tmeta_typeclasses.nim (renamed from tests/metatype/ttypeclasses.nim)0
-rw-r--r--tests/metatype/tmetatype_issues.nim168
-rw-r--r--tests/metatype/tmetatype_various.nim73
-rw-r--r--tests/metatype/tmetatypematrix.nim46
-rw-r--r--tests/metatype/tmodulo.nim20
-rw-r--r--tests/metatype/tprocbothmeta.nim10
-rw-r--r--tests/metatype/tsemistatic.nim2
-rw-r--r--tests/metatype/tshacontext.nim44
-rw-r--r--tests/metatype/tstatic_generic_typeclass.nim30
-rw-r--r--tests/metatype/tstatic_ones.nim28
-rw-r--r--tests/metatype/tstatic_overloading.nim10
-rw-r--r--tests/metatype/tstaticparammacro.nim10
-rw-r--r--tests/metatype/tstaticparams.nim26
-rw-r--r--tests/metatype/tstaticvector.nim5
-rw-r--r--tests/metatype/ttensor.nim6
-rw-r--r--tests/metatype/ttypebar.nim14
-rw-r--r--tests/metatype/ttypedesc1.nim40
-rw-r--r--tests/metatype/ttypedesc2.nim57
-rw-r--r--tests/metatype/ttypedesc3.nim44
-rw-r--r--tests/metatype/ttypedescnotnimnode.nim21
-rw-r--r--tests/metatype/ttypeor.nim35
-rw-r--r--tests/metatype/ttypeselectors.nim9
-rw-r--r--tests/metatype/ttypetraits.nim422
-rw-r--r--tests/metatype/tunresolved_return_type.nim2
-rw-r--r--tests/metatype/twildtypedesc.nim10
-rw-r--r--tests/metatype/twrong_same_type.nim28
-rw-r--r--tests/metatype/tymatrix.nim2
-rw-r--r--tests/metatype/typeclassinference.nim22
-rw-r--r--tests/metatype/typedesc_as_value.nim2
-rw-r--r--tests/metatype/utypeclasses.nim4
-rw-r--r--tests/method/mmultim3.nim2
-rw-r--r--tests/method/t20515.nim20
-rw-r--r--tests/method/t22673.nim21
-rw-r--r--tests/method/tautonotgeneric.nim24
-rw-r--r--tests/method/tcompilegenerics.nim24
-rw-r--r--tests/method/temptybody.nim11
-rw-r--r--tests/method/tgeneric_methods.nim66
-rw-r--r--tests/method/tgeneric_methods2.nim15
-rw-r--r--tests/method/tmapper.nim2
-rw-r--r--tests/method/tmethod.nim2
-rw-r--r--tests/method/tmethod_issues.nim170
-rw-r--r--tests/method/tmethod_various.nim79
-rw-r--r--tests/method/tmethods1.nim22
-rw-r--r--tests/method/tmethods_old.nim12
-rw-r--r--tests/method/tmproto.nim25
-rw-r--r--tests/method/tmultim.nim97
-rw-r--r--tests/method/tmultim1.nim29
-rw-r--r--tests/method/tmultim2.nim39
-rw-r--r--tests/method/tmultim3.nim20
-rw-r--r--tests/method/tmultim4.nim47
-rw-r--r--tests/method/tmultim6.nim30
-rw-r--r--tests/method/tmultim7.nim48
-rw-r--r--tests/method/tmultim8.nim19
-rw-r--r--tests/method/tmultimjs.nim73
-rw-r--r--tests/method/tnildispatcher.nim2
-rw-r--r--tests/method/trecmeth.nim22
-rw-r--r--tests/method/treturn_var_t.nim25
-rw-r--r--tests/method/tsimmeth.nim14
-rw-r--r--tests/method/tsingle_methods.nim48
-rw-r--r--tests/method/tvtable.nim24
-rw-r--r--tests/misc/m15316.nim1
-rw-r--r--tests/misc/m15955.nim4
-rw-r--r--tests/misc/m15955_main.nim11
-rw-r--r--tests/misc/m20149.nim2
-rw-r--r--tests/misc/m20456.nims1
-rw-r--r--tests/misc/mbackend.nim30
-rw-r--r--tests/misc/mbetterrun.nim3
-rw-r--r--tests/misc/mfield_defect.nim30
-rw-r--r--tests/misc/mimportc.nim26
-rw-r--r--tests/misc/mjsondoc.nim14
-rw-r--r--tests/misc/modulea.nim2
-rw-r--r--tests/misc/msizeof5.nim131
-rw-r--r--tests/misc/msizeof5.nim.cfg5
-rw-r--r--tests/misc/mtlsemulation.h37
-rw-r--r--tests/misc/parsecomb.nim4
-rw-r--r--tests/misc/t11634.nim17
-rw-r--r--tests/misc/t12480.nim5
-rw-r--r--tests/misc/t12869.nim14
-rw-r--r--tests/misc/t14667.nim12
-rw-r--r--tests/misc/t15351.nim5
-rw-r--r--tests/misc/t15955.nim22
-rw-r--r--tests/misc/t16244.nim9
-rw-r--r--tests/misc/t16264.nim2
-rw-r--r--tests/misc/t16541.nim12
-rw-r--r--tests/misc/t17286.nim16
-rw-r--r--tests/misc/t18077.nim21
-rw-r--r--tests/misc/t18079.nim15
-rw-r--r--tests/misc/t19046.nim19
-rw-r--r--tests/misc/t20253.nim10
-rw-r--r--tests/misc/t20289.nim15
-rw-r--r--tests/misc/t20456_2.nim14
-rw-r--r--tests/misc/t20883.nim13
-rw-r--r--tests/misc/t21109.nim13
-rw-r--r--tests/misc/t21443.nim6
-rw-r--r--tests/misc/t23240.nim6
-rw-r--r--tests/misc/t3482.nim15
-rw-r--r--tests/misc/t3907.nim10
-rw-r--r--tests/misc/t5540.nim45
-rw-r--r--tests/misc/t6549.nim4
-rw-r--r--tests/misc/t8404.nim33
-rw-r--r--tests/misc/t8545.nim24
-rw-r--r--tests/misc/t9039.nim24
-rw-r--r--tests/misc/t9091.nim33
-rw-r--r--tests/misc/t9710.nim6
-rw-r--r--tests/misc/t99bott.nim8
-rw-r--r--tests/misc/tack.nim4
-rw-r--r--tests/misc/taddr.nim295
-rw-r--r--tests/misc/tapp_lib_staticlib.nim27
-rw-r--r--tests/misc/tbug511622.nim6
-rw-r--r--tests/misc/tcast.nim89
-rw-r--r--tests/misc/tcmdline.nim4
-rw-r--r--tests/misc/tconv.nim143
-rw-r--r--tests/misc/tcsharpusingstatement.nim76
-rw-r--r--tests/misc/tdangerisrelease.nim14
-rw-r--r--tests/misc/tdefine.nim77
-rw-r--r--tests/misc/tdllvar.nim6
-rw-r--r--tests/misc/temptyecho.nim2
-rw-r--r--tests/misc/tendian.nim3
-rw-r--r--tests/misc/tevents.nim48
-rw-r--r--tests/misc/tgenconstraints.nim32
-rw-r--r--tests/misc/tgetstartmilsecs.nim7
-rw-r--r--tests/misc/thallo.nim13
-rw-r--r--tests/misc/theaproots.nim6
-rw-r--r--tests/misc/thintoff.nim12
-rw-r--r--tests/misc/tidentconcatenations.nim32
-rw-r--r--tests/misc/tinc.nim12
-rw-r--r--tests/misc/tinit.nim3
-rw-r--r--tests/misc/tinout.nim4
-rw-r--r--tests/misc/tints.nim81
-rw-r--r--tests/misc/tinvalidarrayaccess.nim14
-rw-r--r--tests/misc/tinvalidnewseq.nim7
-rw-r--r--tests/misc/tissue710.nim2
-rw-r--r--tests/misc/tjoinable.nim3
-rw-r--r--tests/misc/tlastmod.nim17
-rw-r--r--tests/misc/tlocals.nim11
-rw-r--r--tests/misc/tloops.nim8
-rw-r--r--tests/misc/tmandelbrot.nim57
-rw-r--r--tests/misc/tmemoization.nim2
-rw-r--r--tests/misc/tmodulea.nim3
-rw-r--r--tests/misc/tnewuns.nim12
-rw-r--r--tests/misc/tnoforward.nim5
-rw-r--r--tests/misc/tnoinst.nim17
-rw-r--r--tests/misc/tnolen.nim3
-rw-r--r--tests/misc/tnoop.nim11
-rw-r--r--tests/misc/tnot.nim19
-rw-r--r--tests/misc/tparseopt.nim139
-rw-r--r--tests/misc/tpos.nim4
-rw-r--r--tests/misc/tprep.nim8
-rw-r--r--tests/misc/tquicksort.nim9
-rw-r--r--tests/misc/tradix.nim187
-rw-r--r--tests/misc/trangechecks.nim43
-rw-r--r--tests/misc/treadln.nim12
-rw-r--r--tests/misc/treadx.nim13
-rw-r--r--tests/misc/trfc405.nim112
-rw-r--r--tests/misc/trunner.nim444
-rw-r--r--tests/misc/trunner_special.nim37
-rw-r--r--tests/misc/tsamename.nim15
-rw-r--r--tests/misc/tsamename2.nim4
-rw-r--r--tests/misc/tsemfold.nim32
-rw-r--r--tests/misc/tshadow_magic_type.nim17
-rw-r--r--tests/misc/tsimplesort.nim7
-rw-r--r--tests/misc/tsimtych.nim12
-rw-r--r--tests/misc/tsizeof.nim746
-rw-r--r--tests/misc/tsizeof2.nim17
-rw-r--r--tests/misc/tsizeof3.nim58
-rw-r--r--tests/misc/tsizeof4.nim20
-rw-r--r--tests/misc/tslices.nim59
-rw-r--r--tests/misc/tsortdev.nim7
-rw-r--r--tests/misc/tstrace.nim20
-rw-r--r--tests/misc/tstrange.nim10
-rw-r--r--tests/misc/tstrdist.nim2
-rw-r--r--tests/misc/ttlsemulation.nim76
-rw-r--r--tests/misc/tunsignedcomp.nim136
-rw-r--r--tests/misc/tunsignedconv.nim41
-rw-r--r--tests/misc/tunsignedinc.nim28
-rw-r--r--tests/misc/tupcomingfeatures.nim35
-rw-r--r--tests/misc/tvarious.nim4
-rw-r--r--tests/misc/tvarious1.nim14
-rw-r--r--tests/misc/tvarnums.nim33
-rw-r--r--tests/misc/tvcc.nim9
-rw-r--r--tests/misc/åäö.nim5
-rw-r--r--tests/mmaptest.nim36
-rw-r--r--tests/modules/a/module_name_clashes.nim8
-rw-r--r--tests/modules/a/utils.nim2
-rw-r--r--tests/modules/b/module_name_clashes.nim3
-rw-r--r--tests/modules/b/utils.nim2
-rw-r--r--tests/modules/m9627/a.nim1
-rw-r--r--tests/modules/m9627/b.nim1
-rw-r--r--tests/modules/mforwarded_pure_enum.nim3
-rw-r--r--tests/modules/mforwarded_pure_enum2.nim4
-rw-r--r--tests/modules/mimport_in_config.nim2
-rw-r--r--tests/modules/mincludeprefix.nim1
-rw-r--r--tests/modules/mincludetemplate.nim1
-rw-r--r--tests/modules/mmodule_same_proc.nim6
-rw-r--r--tests/modules/mmodule_same_proc_client.nim2
-rw-r--r--tests/modules/mnotuniquename.nim1
-rw-r--r--tests/modules/morder_depa.nim5
-rw-r--r--tests/modules/morder_depb.nim1
-rw-r--r--tests/modules/seq.nim5
-rw-r--r--tests/modules/t8665.nim5
-rw-r--r--tests/modules/t9627.nim7
-rw-r--r--tests/modules/tambig_range.nim4
-rw-r--r--tests/modules/texplicit_system_import.nim6
-rw-r--r--tests/modules/texport2.nim9
-rw-r--r--tests/modules/tfowarded_pure_enum.nim7
-rw-r--r--tests/modules/timport_in_config.nim9
-rw-r--r--tests/modules/timport_in_config.nim.cfg2
-rw-r--r--tests/modules/timportas.nim21
-rw-r--r--tests/modules/timportexcept.nim3
-rw-r--r--tests/modules/tincludeas.nim6
-rw-r--r--tests/modules/tincludeprefix.nim3
-rw-r--r--tests/modules/tincludetemplate.nim5
-rw-r--r--tests/modules/tmismatchedvisibility.nim2
-rw-r--r--tests/modules/tmodule_name_clashes.nim17
-rw-r--r--tests/modules/tmodule_same_proc.nim9
-rw-r--r--tests/modules/tmodulesymtype.nim22
-rw-r--r--tests/modules/tnamspc.nim4
-rw-r--r--tests/modules/tnotuniquename.nim9
-rw-r--r--tests/modules/tnotuniquename2.nim3
-rw-r--r--tests/modules/tnotuniquename_dir/mnotuniquename.nim2
-rw-r--r--tests/modules/topaque.nim4
-rw-r--r--tests/modules/torder_dep.nim10
-rw-r--r--tests/modules/trecinca.nim4
-rw-r--r--tests/modules/trecincb.nim4
-rw-r--r--tests/modules/trecmod.nim2
-rw-r--r--tests/modules/treorder.nim9
-rw-r--r--tests/modules/tselfimport.nim3
-rw-r--r--tests/modules/tseq.nim8
-rw-r--r--tests/modules/tstrutils_insert_sep.nim13
-rw-r--r--tests/modules/tutils_ab.nim5
-rw-r--r--tests/msgs/mdeprecated3.nim10
-rw-r--r--tests/msgs/mspellsuggest.nim7
-rw-r--r--tests/msgs/mused2a.nim23
-rw-r--r--tests/msgs/mused2b.nim3
-rw-r--r--tests/msgs/mused2c.nim1
-rw-r--r--tests/msgs/mused3.nim76
-rw-r--r--tests/msgs/mused3a.nim41
-rw-r--r--tests/msgs/mused3b.nim12
-rw-r--r--tests/msgs/tdeprecated1.nim15
-rw-r--r--tests/msgs/tdeprecated2.nim42
-rw-r--r--tests/msgs/tdeprecated3.nim33
-rw-r--r--tests/msgs/tdeprecatedequalhook.nim11
-rw-r--r--tests/msgs/texpandmacro.nim18
-rw-r--r--tests/msgs/thints_off.nim4
-rw-r--r--tests/msgs/tspellsuggest.nim45
-rw-r--r--tests/msgs/tspellsuggest2.nim45
-rw-r--r--tests/msgs/tspellsuggest3.nim21
-rw-r--r--tests/msgs/tused2.nim46
-rw-r--r--tests/msgs/twarningaserror.nim35
-rw-r--r--tests/namedparams/tnamedparams2.nim8
-rw-r--r--tests/navigator/minclude.nim2
-rw-r--r--tests/navigator/tincludefile.nim29
-rw-r--r--tests/navigator/tnav1.nim33
-rw-r--r--tests/newconfig/bar/config.nims0
-rw-r--r--tests/newconfig/bar/mfoo.nim0
-rw-r--r--tests/newconfig/bar/mfoo.nim.cfg0
-rw-r--r--tests/newconfig/bar/mfoo.nims0
-rw-r--r--tests/newconfig/bar/nim.cfg0
-rw-r--r--tests/newconfig/foo2/mfoo2.customext2
-rw-r--r--tests/newconfig/mconfigcheck.nims9
-rw-r--r--tests/newconfig/tfoo.nim8
-rw-r--r--tests/newconfig/tfoo.nims88
-rw-r--r--tests/nimble/tnimblepathdollar.nim7
-rw-r--r--tests/nimble/tnimblepathdollar.nims5
-rw-r--r--tests/nimble/tnimblepathdollar_fault.nim13
-rw-r--r--tests/nimble/tnimblepathdollar_fault.nims5
-rw-r--r--tests/nimdoc/imp.nim1
-rw-r--r--tests/nimdoc/m13129.nim37
-rw-r--r--tests/nimdoc/readme.md3
-rw-r--r--tests/nimdoc/sub/imp.nim1
-rw-r--r--tests/nimdoc/sub/imp2.nim1
-rw-r--r--tests/nimdoc/sub/mmain.nim8
-rw-r--r--tests/nimdoc/t15916.nim16
-rw-r--r--tests/nimdoc/t17615.nim11
-rw-r--r--tests/nimdoc/trunnableexamples.nim213
-rw-r--r--tests/niminaction/Chapter1/various1.nim4
-rw-r--r--tests/niminaction/Chapter2/explicit_discard.nim4
-rw-r--r--tests/niminaction/Chapter2/no_def_eq.nim4
-rw-r--r--tests/niminaction/Chapter2/no_iterator.nim4
-rw-r--r--tests/niminaction/Chapter2/no_seq_type.nim4
-rw-r--r--tests/niminaction/Chapter2/resultaccept.nim2
-rw-r--r--tests/niminaction/Chapter2/resultreject.nim8
-rw-r--r--tests/niminaction/Chapter2/various2.nim14
-rw-r--r--tests/niminaction/Chapter3/ChatApp/src/client.nim4
-rw-r--r--tests/niminaction/Chapter3/ChatApp/src/protocol.nim2
-rw-r--r--tests/niminaction/Chapter3/ChatApp/src/server.nim8
-rw-r--r--tests/niminaction/Chapter3/various3.nim56
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/concurrency.nim10
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim10
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/naive.nim6
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim12
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/race_condition.nim8
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/sequential_counts.nim8
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim2
-rw-r--r--tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim7
-rw-r--r--tests/niminaction/Chapter7/Tweeter/src/database.nim8
-rw-r--r--tests/niminaction/Chapter7/Tweeter/src/tweeter.nim8
-rw-r--r--tests/niminaction/Chapter7/Tweeter/src/views/user.nim4
-rw-r--r--tests/niminaction/Chapter7/Tweeter/tests/database_test.nim7
-rw-r--r--tests/niminaction/Chapter8/canvas/canvas.nim4
-rw-r--r--tests/niminaction/Chapter8/sdl/sdl.nim10
-rw-r--r--tests/niminaction/Chapter8/sdl/sdl_test.nim28
-rw-r--r--tests/niminaction/Chapter8/sfml/sfml_test.nim3
-rw-r--r--tests/notnil/tmust_compile.nim24
-rw-r--r--tests/notnil/tnotnil.nim12
-rw-r--r--tests/notnil/tnotnil1.nim15
-rw-r--r--tests/notnil/tnotnil4.nim5
-rw-r--r--tests/notnil/tnotnil5.nim28
-rw-r--r--tests/notnil/tnotnil_in_objconstr.nim18
-rw-r--r--tests/notnil/tparse.nim18
-rw-r--r--tests/objects/m19342.c12
-rw-r--r--tests/objects/mobject_default_value.nim15
-rw-r--r--tests/objects/t12753.nim22
-rw-r--r--tests/objects/t17437.nim22
-rw-r--r--tests/objects/t19342.nim18
-rw-r--r--tests/objects/t19342_2.nim18
-rw-r--r--tests/objects/t20972.nim15
-rw-r--r--tests/objects/t22301.nim17
-rw-r--r--tests/objects/t3734.nim17
-rw-r--r--tests/objects/t4318.nim17
-rw-r--r--tests/objects/tdefaultfieldscheck.nim16
-rw-r--r--tests/objects/tdefaultrangetypescheck.nim11
-rw-r--r--tests/objects/tillegal_recursion.nim2
-rw-r--r--tests/objects/tillegal_recursion2.nim6
-rw-r--r--tests/objects/tillegal_recursion3.nim6
-rw-r--r--tests/objects/tinherentedvalues.nim53
-rw-r--r--tests/objects/tinherit_from_generic.nim13
-rw-r--r--tests/objects/tobj_asgn_dont_slice.nim3
-rw-r--r--tests/objects/tobjconstr.nim74
-rw-r--r--tests/objects/tobjconstr2.nim4
-rw-r--r--tests/objects/tobjcov.nim11
-rw-r--r--tests/objects/tobject.nim71
-rw-r--r--tests/objects/tobject2.nim21
-rw-r--r--tests/objects/tobject3.nim41
-rw-r--r--tests/objects/tobject_default_value.nim780
-rw-r--r--tests/objects/tobjects.nim52
-rw-r--r--tests/objects/tobjects_issues.nim117
-rw-r--r--tests/objects/tobjects_various.nim120
-rw-r--r--tests/objects/tobjloop.nim15
-rw-r--r--tests/objects/tobjpragma.nim9
-rw-r--r--tests/objects/tofopr.nim26
-rw-r--r--tests/objects/toop.nim21
-rw-r--r--tests/objects/toop1.nim3
-rw-r--r--tests/objects/trefobjsyntax.nim27
-rw-r--r--tests/objects/trefobjsyntax2.nim19
-rw-r--r--tests/objects/trefobjsyntax3.nim28
-rw-r--r--tests/objects/trequireinit.nim10
-rw-r--r--tests/objects/tunsafenew.nim10
-rw-r--r--tests/objects/tunsafenew2.nim15
-rw-r--r--tests/objects/twhen1.nim89
-rw-r--r--tests/objvariant/t14581.nim25
-rw-r--r--tests/objvariant/tadrdisc.nim4
-rw-r--r--tests/objvariant/tcheckedfield1.nim5
-rw-r--r--tests/objvariant/tconstobjvariant.nim18
-rw-r--r--tests/objvariant/tconstructionorder.nim20
-rw-r--r--tests/objvariant/temptycaseobj.nim4
-rw-r--r--tests/objvariant/tfloatrangeobj.nim14
-rw-r--r--tests/objvariant/tnon_zero_discrim_err.nim14
-rw-r--r--tests/objvariant/treassign.nim31
-rw-r--r--tests/objvariant/trt_discrim.nim198
-rw-r--r--tests/objvariant/trt_discrim_err0.nim17
-rw-r--r--tests/objvariant/trt_discrim_err1.nim17
-rw-r--r--tests/objvariant/trt_discrim_err2.nim14
-rw-r--r--tests/objvariant/trt_discrim_err3.nim17
-rw-r--r--tests/objvariant/tvariantstack.nim11
-rw-r--r--tests/objvariant/tyaoption.nim25
-rw-r--r--tests/openarray/t6163.nim17
-rw-r--r--tests/openarray/t8259.nim7
-rw-r--r--tests/openarray/topena1.nim7
-rw-r--r--tests/openarray/topenarray.nim88
-rw-r--r--tests/openarray/topenarrayrepr.nim8
-rw-r--r--tests/openarray/topenlen.nim5
-rw-r--r--tests/openarray/tptrarrayderef.nim48
-rw-r--r--tests/openarray/tuncheckedarray.nim19
-rw-r--r--tests/options/tnimbasepattern.nim26
-rw-r--r--tests/osproc/passenv.nim2
-rw-r--r--tests/osproc/ta_in.nim5
-rw-r--r--tests/osproc/ta_out.nim16
-rw-r--r--tests/osproc/tafalse.nim3
-rw-r--r--tests/osproc/tclose.nim8
-rw-r--r--tests/osproc/texecps.nim6
-rw-r--r--tests/osproc/texitcode.nim23
-rw-r--r--tests/osproc/texitsignal.nim5
-rw-r--r--tests/osproc/tnoexe.nim27
-rw-r--r--tests/osproc/treadlines.nim23
-rw-r--r--tests/osproc/tstdin.nim18
-rw-r--r--tests/osproc/tstdout.nim29
-rw-r--r--tests/osproc/twaitforexit.nim38
-rw-r--r--tests/osproc/tworkingdir.nim3
-rw-r--r--tests/overflow/tdistinct_range.nim6
-rw-r--r--tests/overflow/toverflow.nim82
-rw-r--r--tests/overflow/toverflow2.nim7
-rw-r--r--tests/overflow/toverflow_reorder.nim84
-rw-r--r--tests/overflow/tovfint.nim (renamed from tests/overflw/tovfint.nim)3
-rw-r--r--tests/overflow/trangechecks.nim48
-rw-r--r--tests/overflow/twronginference.nim10
-rw-r--r--tests/overflw/toverflw.nim84
-rw-r--r--tests/overflw/toverflw2.nim10
-rw-r--r--tests/overload/issue22142/tfail_implicit_ambiguous.nim10
-rw-r--r--tests/overload/issue22142/tfail_nested_pointers.nim12
-rw-r--r--tests/overload/issue22142/tfail_object_is_generic.nim16
-rw-r--r--tests/overload/issue22142/tfail_typeclass_var_invar.nim9
-rw-r--r--tests/overload/issue22142/tissue22142_shouldpass.nim68
-rw-r--r--tests/overload/m19737.nim10
-rw-r--r--tests/overload/mvaruintconv.nim145
-rw-r--r--tests/overload/t19737.nim15
-rw-r--r--tests/overload/t23249.nim17
-rw-r--r--tests/overload/t23755.nim62
-rw-r--r--tests/overload/t7416.nim9
-rw-r--r--tests/overload/t8829.nim18
-rw-r--r--tests/overload/tconverter_to_string.nim22
-rw-r--r--tests/overload/tgenericalias.nim13
-rw-r--r--tests/overload/timport.nim7
-rw-r--r--tests/overload/tissue4475.nim6
-rw-r--r--tests/overload/tnamedparamoverloading.nim14
-rw-r--r--tests/overload/tor_isnt_better.nim41
-rw-r--r--tests/overload/toverl.nim4
-rw-r--r--tests/overload/toverl2.nim33
-rw-r--r--tests/overload/toverl3.nim20
-rw-r--r--tests/overload/toverl4.nim32
-rw-r--r--tests/overload/toverload_issues.nim200
-rw-r--r--tests/overload/toverload_various.nim568
-rw-r--r--tests/overload/toverprc.nim45
-rw-r--r--tests/overload/toverwr.nim13
-rw-r--r--tests/overload/tparams_after_varargs.nim19
-rw-r--r--tests/overload/tprefer_specialized_generic.nim22
-rw-r--r--tests/overload/tprefer_tygenericinst.nim68
-rw-r--r--tests/overload/tproc_types_dont_like_subtypes.nim20
-rw-r--r--tests/overload/tselfderef.nim17
-rw-r--r--tests/overload/tstatic_with_converter.nim9
-rw-r--r--tests/overload/tstaticoverload.nim30
-rw-r--r--tests/overload/tstmtoverload.nim38
-rw-r--r--tests/overload/tsymtabchange_during_or.nim24
-rw-r--r--tests/overload/tuntypedarg.nim19
-rw-r--r--tests/overload/tvart_varargs.nim18
-rw-r--r--tests/overload/tvartypeclass.nim11
-rw-r--r--tests/overload/tvaruintconv.nim207
-rw-r--r--tests/package/stdlib/stdlib.nimble0
-rw-r--r--tests/package/stdlib/system.nim2
-rw-r--r--tests/package/tstdlib_name_not_special.nim3
-rw-r--r--tests/parallel/t10913.nim20
-rw-r--r--tests/parallel/t7535.nim11
-rw-r--r--tests/parallel/t9691.nim9
-rw-r--r--tests/parallel/tarray_of_channels.nim15
-rw-r--r--tests/parallel/tblocking_channel.nim6
-rw-r--r--tests/parallel/tconvexhull.nim22
-rw-r--r--tests/parallel/tdeepcopy.nim61
-rw-r--r--tests/parallel/tdeepcopy2.nim9
-rw-r--r--tests/parallel/tdisjoint_slice1.nim58
-rw-r--r--tests/parallel/tdisjoint_slice2.nim40
-rw-r--r--tests/parallel/tdont_be_stupid.nim15
-rw-r--r--tests/parallel/tflowvar.nim2
-rw-r--r--tests/parallel/tgc_unsafe.nim2
-rw-r--r--tests/parallel/tgc_unsafe2.nim11
-rw-r--r--tests/parallel/tguard1.nim6
-rw-r--r--tests/parallel/tinvalid_array_bounds.nim3
-rw-r--r--tests/parallel/tlet_spawn.nim16
-rw-r--r--tests/parallel/tmissing_deepcopy.nim12
-rw-r--r--tests/parallel/tnon_disjoint_slice1.nim3
-rw-r--r--tests/parallel/tparfind.nim1
-rw-r--r--tests/parallel/tpi.nim5
-rw-r--r--tests/parallel/tptr_to_ref.nim4
-rw-r--r--tests/parallel/tsendtwice.nim40
-rw-r--r--tests/parallel/tsimple_array_checks.nim40
-rw-r--r--tests/parallel/tsysspawn.nim37
-rw-r--r--tests/parallel/tsysspawnbadarg.nim2
-rw-r--r--tests/parallel/tuseafterdef.nim5
-rw-r--r--tests/parallel/twaitany.nim15
-rw-r--r--tests/parallel/twrong_refcounts.nim53
-rw-r--r--tests/parser/t12274.nim13
-rw-r--r--tests/parser/t15667.nim61
-rw-r--r--tests/parser/t19430.nim3
-rw-r--r--tests/parser/t19662.nim6
-rw-r--r--tests/parser/t20922.nim35
-rw-r--r--tests/parser/tbinarynotindented.nim3
-rw-r--r--tests/parser/tbinarynotsameline.nim10
-rw-r--r--tests/parser/tcommand_as_expr.nim9
-rw-r--r--tests/parser/tcommandequals.nim17
-rw-r--r--tests/parser/tcommandindent.nim16
-rw-r--r--tests/parser/tdo.nim19
-rw-r--r--tests/parser/tdoc_comments.nim4
-rw-r--r--tests/parser/tdomulttest.nim4
-rw-r--r--tests/parser/tdotlikeoperators.nim30
-rw-r--r--tests/parser/tdoublenotnil.nim3
-rw-r--r--tests/parser/tifexprs.nim12
-rw-r--r--tests/parser/tifextracolon.nim8
-rw-r--r--tests/parser/tinvcolonlocation1.nim2
-rw-r--r--tests/parser/tinvcolonlocation2.nim2
-rw-r--r--tests/parser/tinvcolonlocation3.nim2
-rw-r--r--tests/parser/tinvifstmt.nim12
-rw-r--r--tests/parser/tinvwhen.nim4
-rw-r--r--tests/parser/tletcolon.nim16
-rw-r--r--tests/parser/toprprec.nim11
-rw-r--r--tests/parser/tpostexprblocks.nim126
-rw-r--r--tests/parser/tprecedence.nim44
-rw-r--r--tests/parser/tprocexprasstmt.nim14
-rw-r--r--tests/parser/tstatementoperators.nim12
-rw-r--r--tests/parser/tstmtlists.nim180
-rw-r--r--tests/parser/tstrongspaces.nim83
-rw-r--r--tests/parser/ttry.nim27
-rw-r--r--tests/parser/ttupleunpack.nim71
-rw-r--r--tests/parser/ttypeclasses.nim43
-rw-r--r--tests/parser/ttypecommandcomma.nim9
-rw-r--r--tests/parser/ttypecommandindent1.nim9
-rw-r--r--tests/parser/ttypecommandindent2.nim11
-rw-r--r--tests/parser/ttypecommandindent3.nim11
-rw-r--r--tests/parser/ttypeexprobject.nim10
-rw-r--r--tests/parser/ttypeexprs.nim25
-rw-r--r--tests/parser/ttypemodifiers.nim526
-rw-r--r--tests/parser/ttypesectioncalls.nim328
-rw-r--r--tests/pragmas/cfunction.c4
-rw-r--r--tests/pragmas/custom_pragma.nim2
-rw-r--r--tests/pragmas/foobar.nim3
-rw-r--r--tests/pragmas/monoff1.nim1
-rw-r--r--tests/pragmas/mpushexperimental.nim30
-rw-r--r--tests/pragmas/mqualifiedmacro.nim10
-rw-r--r--tests/pragmas/t12558.nim15
-rw-r--r--tests/pragmas/t12640.nim26
-rw-r--r--tests/pragmas/t13306.nim10
-rw-r--r--tests/pragmas/t22713.nim12
-rw-r--r--tests/pragmas/t4384.nim3
-rw-r--r--tests/pragmas/t5149.nim12
-rw-r--r--tests/pragmas/t6448.nim17
-rw-r--r--tests/pragmas/t8741.nim29
-rw-r--r--tests/pragmas/tbitsize.nim10
-rw-r--r--tests/pragmas/tcompile_missing_file.nim5
-rw-r--r--tests/pragmas/tcompile_pragma.nim10
-rw-r--r--tests/pragmas/tcustom_pragma.nim454
-rw-r--r--tests/pragmas/tdeprecated.nim10
-rw-r--r--tests/pragmas/thintprocessing.nim18
-rw-r--r--tests/pragmas/tinvalid_user_pragma.nim9
-rw-r--r--tests/pragmas/tinvalidcustompragma.nim23
-rw-r--r--tests/pragmas/tlocks.nim1
-rw-r--r--tests/pragmas/tnoreturn.nim6
-rw-r--r--tests/pragmas/tonoff1.nim8
-rw-r--r--tests/pragmas/tonoff2.nim14
-rw-r--r--tests/pragmas/tpragmas_misc.nim75
-rw-r--r--tests/pragmas/tpragmas_reorder.nim19
-rw-r--r--tests/pragmas/tpush.nim129
-rw-r--r--tests/pragmas/tpushexperimental.nim13
-rw-r--r--tests/pragmas/tpushnotes.nim13
-rw-r--r--tests/pragmas/tqualifiedmacro.nim14
-rw-r--r--tests/pragmas/treorder.nim12
-rw-r--r--tests/pragmas/ttypedef_macro.nim66
-rw-r--r--tests/pragmas/tused.nim10
-rw-r--r--tests/pragmas/tuserpragma2.nim5
-rw-r--r--tests/pragmas/tuserpragmaargs.nim5
-rw-r--r--tests/pragmas/tvar_macro.nim128
-rw-r--r--tests/pragmas/twarning_off.nim15
-rw-r--r--tests/pragmas/warn_module.nim7
-rw-r--r--tests/proc/mdefaultprocparam.nim (renamed from tests/defaultprocparam/mdefaultprocparam.nim)0
-rw-r--r--tests/proc/t15949.nim16
-rw-r--r--tests/proc/t17157.nim6
-rw-r--r--tests/proc/t19795.nim19
-rw-r--r--tests/proc/t23874.nim26
-rw-r--r--tests/proc/tcolonisproc.nim (renamed from tests/misc/tcolonisproc.nim)6
-rw-r--r--tests/proc/tdefaultprocparam.nim90
-rw-r--r--tests/proc/texplicitgenericcount.nim24
-rw-r--r--tests/proc/texplicitgenericcountverbose.nim22
-rw-r--r--tests/proc/texplicitgenerics.nim55
-rw-r--r--tests/proc/tfunc_type.nim14
-rw-r--r--tests/proc/tgenericdefaultparam.nim98
-rw-r--r--tests/proc/tillegalreturntype.nim21
-rw-r--r--tests/proc/tinferlambdareturn.nim36
-rw-r--r--tests/proc/tlambdadonotation.nim78
-rw-r--r--tests/proc/tlambdapragma.nim7
-rw-r--r--tests/proc/tnamedparams.nim (renamed from tests/namedparams/tnamedparams.nim)5
-rw-r--r--tests/proc/tnamedparams2.nim15
-rw-r--r--tests/proc/tnamedparams3.nim (renamed from tests/namedparams/tnamedparams3.nim)0
-rw-r--r--tests/proc/tnestprc.nim16
-rw-r--r--tests/proc/tparamsindefault.nim120
-rw-r--r--tests/proc/tproc.nim31
-rw-r--r--tests/proc/tprocredef.nim3
-rw-r--r--tests/proc/tprocvar.nim39
-rw-r--r--tests/proc/tprocvarmismatch.nim18
-rw-r--r--tests/proc/tstaticsignature.nim268
-rw-r--r--tests/proc/tunderscoreparam.nim122
-rw-r--r--tests/proc/twrongdefaultvalue.nim25
-rw-r--r--tests/proc/typed.nim7
-rw-r--r--tests/proc/untyped.nim15
-rw-r--r--tests/procvar/tgenericprocvar.nim5
-rw-r--r--tests/procvar/tprocvar.nim18
-rw-r--r--tests/procvar/tprocvar2.nim32
-rw-r--r--tests/procvar/tprocvars.nim6
-rw-r--r--tests/range/t19678.nim17
-rw-r--r--tests/range/tbug499771.nim14
-rw-r--r--tests/range/tcolors.nim39
-rw-r--r--tests/range/tcompiletime_range_checks.nim52
-rw-r--r--tests/range/tenums.nim33
-rw-r--r--tests/range/texplicitvarconv.nim13
-rw-r--r--tests/range/tmatrix3.nim41
-rw-r--r--tests/range/tn8vsint16.nim18
-rw-r--r--tests/range/toutofrangevarconv.nim14
-rw-r--r--tests/range/trange.nim156
-rw-r--r--tests/range/tsubrange.nim3
-rw-r--r--tests/range/tsubrange2.nim4
-rw-r--r--tests/range/tsubrange3.nim3
-rw-r--r--tests/readme.md37
-rw-r--r--tests/realtimeGC/cmain.c67
-rw-r--r--tests/realtimeGC/main.nim.cfg6
-rw-r--r--tests/realtimeGC/nmain.nim46
-rw-r--r--tests/realtimeGC/readme.txt21
-rw-r--r--tests/realtimeGC/shared.nim4
-rw-r--r--tests/realtimeGC/shared.nim.cfg5
-rw-r--r--tests/realtimeGC/tmain.nim62
-rw-r--r--tests/refc/tsinkbug.nim26
-rw-r--r--tests/rodfiles/aconv.nim9
-rw-r--r--tests/rodfiles/amethods.nim13
-rw-r--r--tests/rodfiles/bconv.nim9
-rw-r--r--tests/rodfiles/bmethods.nim29
-rw-r--r--tests/rodfiles/bmethods2.nim29
-rw-r--r--tests/rodfiles/deada.nim8
-rw-r--r--tests/rodfiles/deada2.nim12
-rw-r--r--tests/rodfiles/deadb.nim7
-rw-r--r--tests/rodfiles/deadg.nim7
-rw-r--r--tests/rodfiles/gtkex1.nim14
-rw-r--r--tests/rodfiles/gtkex2.nim22
-rw-r--r--tests/rodfiles/hallo.nim6
-rw-r--r--tests/rodfiles/hallo2.nim17
-rw-r--r--tests/rodfiles/int2bool.nim7
-rw-r--r--tests/rodfiles/nim.cfg2
-rw-r--r--tests/rodfiles/tgeneric1.nim13
-rw-r--r--tests/rodfiles/tgeneric2.nim13
-rw-r--r--tests/sandwich/generic_library.nim6
-rw-r--r--tests/sandwich/helper_module.nim3
-rw-r--r--tests/sandwich/module_using_generic_library.nim12
-rw-r--r--tests/sandwich/tmain.nim9
-rw-r--r--tests/seq/tseq2.nim11
-rw-r--r--tests/seq/tseqcon.nim51
-rw-r--r--tests/seq/tseqcon2.nim9
-rw-r--r--tests/seq/tseqtuple.nim28
-rw-r--r--tests/seq/tsequtils.nim64
-rw-r--r--tests/seq/tshallowseq.nim17
-rw-r--r--tests/seq/ttoseq.nim18
-rw-r--r--tests/sets/m17385.nim11
-rw-r--r--tests/sets/t13764.nim6
-rw-r--r--tests/sets/t15435.nim29
-rw-r--r--tests/sets/t17385.nim4
-rw-r--r--tests/sets/t20997.nim18
-rw-r--r--tests/sets/t2669.nim6
-rw-r--r--tests/sets/t5792.nim17
-rw-r--r--tests/sets/thugeset.nim10
-rw-r--r--tests/sets/tnewsets.nim (renamed from tests/misc/tnewsets.nim)0
-rw-r--r--tests/sets/trangeincompatible.nim32
-rw-r--r--tests/sets/tsets.nim305
-rw-r--r--tests/sets/tsets2.nim71
-rw-r--r--tests/sets/tsets3.nim100
-rw-r--r--tests/sets/tsets_lt.nim12
-rw-r--r--tests/sets/tsets_various.nim286
-rw-r--r--tests/sets/twrongenumrange.nim50
-rw-r--r--tests/showoff/tdrdobbs_examples.nim6
-rw-r--r--tests/showoff/tformatopt.nim2
-rw-r--r--tests/showoff/tgenericmacrotypes.nim55
-rw-r--r--tests/specialops/tcallops.nim49
-rw-r--r--tests/specialops/tcallprecedence.nim44
-rw-r--r--tests/specialops/tdotops.nim12
-rw-r--r--tests/specialops/terrmsgs.nim76
-rw-r--r--tests/specialops/tnewseq.nim22
-rw-r--r--tests/specialops/tsetter.nim10
-rw-r--r--tests/statictypes/t20148.nim8
-rw-r--r--tests/statictypes/t5780.nim3
-rw-r--r--tests/statictypes/t9255.nim13
-rw-r--r--tests/statictypes/tgenericcomputedrange.nim125
-rw-r--r--tests/statictypes/tpassthruarith.nim6
-rw-r--r--tests/statictypes/tstatic.nim56
-rw-r--r--tests/statictypes/tstaticgenericparam.nim24
-rw-r--r--tests/statictypes/tstaticimportcpp.nim10
-rw-r--r--tests/statictypes/tstaticprocparams.nim16
-rw-r--r--tests/statictypes/tstatictypes.nim459
-rw-r--r--tests/stdlib/concurrency/atomicSample.nim9
-rw-r--r--tests/stdlib/concurrency/tatomic_import.nim11
-rw-r--r--tests/stdlib/concurrency/tatomics.nim637
-rw-r--r--tests/stdlib/concurrency/tatomics_size.nim21
-rw-r--r--tests/stdlib/config.nims5
-rw-r--r--tests/stdlib/genericstrformat.nim16
-rw-r--r--tests/stdlib/mgenast.nim57
-rw-r--r--tests/stdlib/mimportutils.nim38
-rw-r--r--tests/stdlib/mintsets.nim11
-rw-r--r--tests/stdlib/mstackframes.nim38
-rw-r--r--tests/stdlib/nre/captures.nim47
-rw-r--r--tests/stdlib/nre/escape.nim4
-rw-r--r--tests/stdlib/nre/find.nim12
-rw-r--r--tests/stdlib/nre/init.nim10
-rw-r--r--tests/stdlib/nre/match.nim14
-rw-r--r--tests/stdlib/nre/misc.nim10
-rw-r--r--tests/stdlib/nre/replace.nim14
-rw-r--r--tests/stdlib/nre/split.nim14
-rw-r--r--tests/stdlib/osproctest.nim8
-rw-r--r--tests/stdlib/somesql.sql2
-rw-r--r--tests/stdlib/t10231.nim16
-rw-r--r--tests/stdlib/t14139.nim10
-rw-r--r--tests/stdlib/t15663.nim9
-rw-r--r--tests/stdlib/t19304.nim7
-rw-r--r--tests/stdlib/t20023.nim10
-rw-r--r--tests/stdlib/t21251.nim6
-rw-r--r--tests/stdlib/t21406.nim7
-rw-r--r--tests/stdlib/t21564.nim32
-rw-r--r--tests/stdlib/t7686.nim10
-rw-r--r--tests/stdlib/t8925.nim16
-rw-r--r--tests/stdlib/t9754.nim6
-rw-r--r--tests/stdlib/talgorithm.nim275
-rw-r--r--tests/stdlib/tarithmetics.nim50
-rw-r--r--tests/stdlib/tasynchttpserver.nim121
-rw-r--r--tests/stdlib/tasynchttpserver_transferencoding.nim91
-rw-r--r--tests/stdlib/tbase64.nim63
-rw-r--r--tests/stdlib/tbitops.nim435
-rw-r--r--tests/stdlib/tbitops.nim.cfg2
-rw-r--r--tests/stdlib/tbitops2.nim168
-rw-r--r--tests/stdlib/tbitops2.nim.cfg2
-rw-r--r--tests/stdlib/tbitops_utils.nim19
-rw-r--r--tests/stdlib/tbug5382.nim11
-rw-r--r--tests/stdlib/tcasts.nim26
-rw-r--r--tests/stdlib/tcgi.nim31
-rw-r--r--tests/stdlib/tclosures.nim47
-rw-r--r--tests/stdlib/tcmdline.nim13
-rw-r--r--tests/stdlib/tcomplex.nim115
-rw-r--r--tests/stdlib/tcookies.nim22
-rw-r--r--tests/stdlib/tcount.nim29
-rw-r--r--tests/stdlib/tcputime.nim13
-rw-r--r--tests/stdlib/tcritbits.nim91
-rw-r--r--tests/stdlib/tcstring.nim93
-rw-r--r--tests/stdlib/tcstrutils.nim39
-rw-r--r--tests/stdlib/tdb.nim0
-rw-r--r--tests/stdlib/tdb.nims1
-rw-r--r--tests/stdlib/tdb_mysql.nim0
-rw-r--r--tests/stdlib/tdecls.nim50
-rw-r--r--tests/stdlib/tdecode_helpers.nim27
-rw-r--r--tests/stdlib/tdeques.nim243
-rw-r--r--tests/stdlib/tdiff.nim75
-rw-r--r--tests/stdlib/tdistros_detect.nim16
-rw-r--r--tests/stdlib/tdochelpers.nim221
-rw-r--r--tests/stdlib/techo.nim3
-rw-r--r--tests/stdlib/teditdistance.nim45
-rw-r--r--tests/stdlib/tencodings.nim107
-rw-r--r--tests/stdlib/tenumerate.nim24
-rw-r--r--tests/stdlib/tenumutils.nim49
-rw-r--r--tests/stdlib/tenvvars.nim162
-rw-r--r--tests/stdlib/testequivalence.nim14
-rw-r--r--tests/stdlib/texitprocs.nim22
-rw-r--r--tests/stdlib/tfdleak.nim152
-rw-r--r--tests/stdlib/tfdleak_multiple.nim31
-rw-r--r--tests/stdlib/tfenv.nim8
-rw-r--r--tests/stdlib/tfilesanddirs.nim36
-rw-r--r--tests/stdlib/tformat.nim12
-rw-r--r--tests/stdlib/tfrexp1.nim23
-rw-r--r--tests/stdlib/tgenast.nim274
-rw-r--r--tests/stdlib/tgetaddrinfo.nim38
-rw-r--r--tests/stdlib/tgetfileinfo.nim38
-rw-r--r--tests/stdlib/tgetprotobyname.nim19
-rw-r--r--tests/stdlib/tglobs.nim25
-rw-r--r--tests/stdlib/thashes.nim250
-rw-r--r--tests/stdlib/theapqueue.nim106
-rw-r--r--tests/stdlib/thighlite.nim45
-rw-r--r--tests/stdlib/thtmlparser.nim159
-rw-r--r--tests/stdlib/thtmlparser2813.nim45
-rw-r--r--tests/stdlib/thtmlparser2814.nim44
-rw-r--r--tests/stdlib/thttpclient.nim118
-rw-r--r--tests/stdlib/thttpclient_ssl.nim150
-rw-r--r--tests/stdlib/thttpclient_ssl_cert.pem29
-rw-r--r--tests/stdlib/thttpclient_ssl_key.pem52
-rw-r--r--tests/stdlib/thttpclient_standalone.nim59
-rw-r--r--tests/stdlib/thttpcore.nim117
-rw-r--r--tests/stdlib/timportutils.nim151
-rw-r--r--tests/stdlib/tintsets.nim6
-rw-r--r--tests/stdlib/tio.nim94
-rw-r--r--tests/stdlib/tisolation.nim135
-rw-r--r--tests/stdlib/tjsbigints.nim46
-rw-r--r--tests/stdlib/tjson.nim382
-rw-r--r--tests/stdlib/tjsonexternproc.nim8
-rw-r--r--tests/stdlib/tjsonmacro.nim286
-rw-r--r--tests/stdlib/tjsonmacro_reject.nim4
-rw-r--r--tests/stdlib/tjsonmacro_reject2.nim21
-rw-r--r--tests/stdlib/tjsontestsuite.nim4
-rw-r--r--tests/stdlib/tjsonutils.nim476
-rw-r--r--tests/stdlib/tlists.nim297
-rw-r--r--tests/stdlib/tlocks.nim7
-rw-r--r--tests/stdlib/tlwip.nim30
-rw-r--r--tests/stdlib/tmacros.nim349
-rw-r--r--tests/stdlib/tmarshal.nim246
-rw-r--r--tests/stdlib/tmarshalsegfault.nim54
-rw-r--r--tests/stdlib/tmath.nim545
-rw-r--r--tests/stdlib/tmath2.nim85
-rw-r--r--tests/stdlib/tmemfiles1.nim7
-rw-r--r--tests/stdlib/tmemfiles2.nim15
-rw-r--r--tests/stdlib/tmemlines.nim6
-rw-r--r--tests/stdlib/tmemlinesBuf.nim11
-rw-r--r--tests/stdlib/tmemmapstreams.nim6
-rw-r--r--tests/stdlib/tmemslices.nim8
-rw-r--r--tests/stdlib/tmersenne.nim13
-rw-r--r--tests/stdlib/tmget.nim27
-rw-r--r--tests/stdlib/tmimetypes.nim28
-rw-r--r--tests/stdlib/tmisc_issues.nim39
-rw-r--r--tests/stdlib/tmitems.nim32
-rw-r--r--tests/stdlib/tmonotimes.nim22
-rw-r--r--tests/stdlib/tnativesockets.nim34
-rw-r--r--tests/stdlib/tnet.nim77
-rw-r--r--tests/stdlib/tnet_ll.nim94
-rw-r--r--tests/stdlib/tnetbind.nim25
-rw-r--r--tests/stdlib/tnetconnect.nim30
-rw-r--r--tests/stdlib/tnetdial.nim9
-rw-r--r--tests/stdlib/tnilecho.nim2
-rw-r--r--tests/stdlib/tnre.nim20
-rw-r--r--tests/stdlib/tntpath.nim50
-rw-r--r--tests/stdlib/tobjectdollar.nim14
-rw-r--r--tests/stdlib/toids.nim15
-rw-r--r--tests/stdlib/topenssl.nim46
-rw-r--r--tests/stdlib/toptions.nim207
-rw-r--r--tests/stdlib/torderedtable.nim18
-rw-r--r--tests/stdlib/tos.nim916
-rw-r--r--tests/stdlib/tos_unc.nim11
-rw-r--r--tests/stdlib/tosenv.nim163
-rw-r--r--tests/stdlib/toserrors.nim9
-rw-r--r--tests/stdlib/tosproc.nim316
-rw-r--r--tests/stdlib/tosprocterminate.nim35
-rw-r--r--tests/stdlib/tpackedsets.nim265
-rw-r--r--tests/stdlib/tparscfg.nim69
-rw-r--r--tests/stdlib/tparsecfg.nim149
-rw-r--r--tests/stdlib/tparsecsv.nim36
-rw-r--r--tests/stdlib/tparseipv6.nim95
-rw-r--r--tests/stdlib/tparsesql.nim123
-rw-r--r--tests/stdlib/tparseuints.nim16
-rw-r--r--tests/stdlib/tparseutils.nim112
-rw-r--r--tests/stdlib/tparsopt.nim12
-rw-r--r--tests/stdlib/tpathnorm.nim36
-rw-r--r--tests/stdlib/tpaths.nim238
-rw-r--r--tests/stdlib/tpegs.nim350
-rw-r--r--tests/stdlib/tpermutations.nim19
-rw-r--r--tests/stdlib/tposix.nim74
-rw-r--r--tests/stdlib/tprelude.nim16
-rw-r--r--tests/stdlib/tquit.nim6
-rw-r--r--tests/stdlib/trandom.nim310
-rw-r--r--tests/stdlib/trat_float.nim (renamed from tests/rational/trat_float.nim)2
-rw-r--r--tests/stdlib/trat_init.nim (renamed from tests/rational/trat_init.nim)2
-rw-r--r--tests/stdlib/trationals.nim117
-rw-r--r--tests/stdlib/tre.nim122
-rw-r--r--tests/stdlib/treadln.nim23
-rw-r--r--tests/stdlib/tregex.nim6
-rw-r--r--tests/stdlib/tregistry.nim16
-rw-r--r--tests/stdlib/treguse.nim27
-rw-r--r--tests/stdlib/treloop.nim9
-rw-r--r--tests/stdlib/trepr.nim349
-rw-r--r--tests/stdlib/trepr2.nim32
-rw-r--r--tests/stdlib/trlocks.nim14
-rw-r--r--tests/stdlib/tropes.nim104
-rw-r--r--tests/stdlib/trst.nim1994
-rw-r--r--tests/stdlib/trstgen.nim1587
-rw-r--r--tests/stdlib/tsegfaults.nim29
-rw-r--r--tests/stdlib/tsequtils.nim547
-rw-r--r--tests/stdlib/tsetutils.nim49
-rw-r--r--tests/stdlib/tsharedlist.nim49
-rw-r--r--tests/stdlib/tsharedtable.nim90
-rw-r--r--tests/stdlib/tsince.nim32
-rw-r--r--tests/stdlib/tsinglylinkedring.nim29
-rw-r--r--tests/stdlib/tsocketstreams.nim65
-rw-r--r--tests/stdlib/tsortcall.nim66
-rw-r--r--tests/stdlib/tsplit.nim20
-rw-r--r--tests/stdlib/tsplit2.nim19
-rw-r--r--tests/stdlib/tsqlitebindatas.nim0
-rw-r--r--tests/stdlib/tsqlparser.nim3
-rw-r--r--tests/stdlib/tssl.nim138
-rw-r--r--tests/stdlib/tssl.nims5
-rw-r--r--tests/stdlib/tstackframes.nim34
-rw-r--r--tests/stdlib/tstaticos.nim8
-rw-r--r--tests/stdlib/tstats.nim61
-rw-r--r--tests/stdlib/tstdlib_issues.nim110
-rw-r--r--tests/stdlib/tstdlib_various.nim240
-rw-r--r--tests/stdlib/tstrbasics.nim103
-rw-r--r--tests/stdlib/tstreams.nim112
-rw-r--r--tests/stdlib/tstreams2.nim13
-rw-r--r--tests/stdlib/tstreams3.nim10
-rw-r--r--tests/stdlib/tstrformat.nim640
-rw-r--r--tests/stdlib/tstrformatlineinfo.nim8
-rw-r--r--tests/stdlib/tstrimpl.nim12
-rw-r--r--tests/stdlib/tstring.nim199
-rw-r--r--tests/stdlib/tstrmiscs.nim27
-rw-r--r--tests/stdlib/tstrscans.nim288
-rw-r--r--tests/stdlib/tstrset.nim16
-rw-r--r--tests/stdlib/tstrtabs.nim109
-rw-r--r--tests/stdlib/tstrtabs.nims5
-rw-r--r--tests/stdlib/tstrtabs2.nim32
-rw-r--r--tests/stdlib/tstrutil.nim306
-rw-r--r--tests/stdlib/tstrutils.nim913
-rw-r--r--tests/stdlib/tsugar.nim310
-rw-r--r--tests/stdlib/tsums.nim27
-rw-r--r--tests/stdlib/tsysrand.nim34
-rw-r--r--tests/stdlib/tsystem.nim200
-rw-r--r--tests/stdlib/ttables.nim316
-rw-r--r--tests/stdlib/ttasks.nim561
-rw-r--r--tests/stdlib/ttempfiles.nim51
-rw-r--r--tests/stdlib/tterminal.nim7
-rw-r--r--tests/stdlib/tterminal_12759.nim11
-rw-r--r--tests/stdlib/tterminal_15874.nim8
-rw-r--r--tests/stdlib/ttestutils.nim40
-rw-r--r--tests/stdlib/tthreadpool.nim14
-rw-r--r--tests/stdlib/ttimes.nim817
-rw-r--r--tests/stdlib/ttypeinfo.nim93
-rw-r--r--tests/stdlib/ttypeinfo.nims1
-rw-r--r--tests/stdlib/ttypetraits.nim94
-rw-r--r--tests/stdlib/tunicode.nim228
-rw-r--r--tests/stdlib/tunidecode.nim9
-rw-r--r--tests/stdlib/tunittest.nim126
-rw-r--r--tests/stdlib/tunittest_error.nim22
-rw-r--r--tests/stdlib/tunittestpass.nim20
-rw-r--r--tests/stdlib/tunittesttemplate.nim25
-rw-r--r--tests/stdlib/tunixsocket.nim35
-rw-r--r--tests/stdlib/turi.nim323
-rw-r--r--tests/stdlib/tuserlocks.nim21
-rw-r--r--tests/stdlib/tvarargs.nim18
-rw-r--r--tests/stdlib/tvarints.nim73
-rw-r--r--tests/stdlib/tvmutils.nim31
-rw-r--r--tests/stdlib/tvolatile.nim15
-rw-r--r--tests/stdlib/twalker.nim13
-rw-r--r--tests/stdlib/twchartoutf8.nim7
-rw-r--r--tests/stdlib/twith.nim44
-rw-r--r--tests/stdlib/twordwrap.nim48
-rw-r--r--tests/stdlib/twrapnils.nim224
-rw-r--r--tests/stdlib/twrongstattype.nim14
-rw-r--r--tests/stdlib/txmlgen.nim12
-rw-r--r--tests/stdlib/txmltree.nim131
-rw-r--r--tests/stdlib/tyield.nim258
-rw-r--r--tests/stdlib/unixsockettest.nim26
-rw-r--r--tests/stdlib/uselocks.nim5
-rw-r--r--tests/stmt/tforloop_tuple_multiple_underscore.nim10
-rw-r--r--tests/stmt/tforloop_tuple_underscore.nim16
-rw-r--r--tests/stmt/tforloop_underscore.nim6
-rw-r--r--tests/stmt/tgenericsunderscore.nim4
-rw-r--r--tests/stmt/tmiscunderscore.nim15
-rw-r--r--tests/strictnotnil/tnilcheck.nim381
-rw-r--r--tests/strictnotnil/tnilcheck_no_warnings.nim182
-rw-r--r--tests/stylecheck/fileinfo.nim2
-rw-r--r--tests/stylecheck/foreign_package/foreign_package.nim1
-rw-r--r--tests/stylecheck/foreign_package/foreign_package.nimble2
-rw-r--r--tests/stylecheck/t20397.nim4
-rw-r--r--tests/stylecheck/t20397_1.nim8
-rw-r--r--tests/stylecheck/t20397_2.nim7
-rw-r--r--tests/stylecheck/taccept.nim22
-rw-r--r--tests/stylecheck/tforeign_package.nim16
-rw-r--r--tests/stylecheck/thint.nim43
-rw-r--r--tests/stylecheck/treject.nim17
-rw-r--r--tests/stylecheck/tusages.nim22
-rw-r--r--tests/system/helpers/readall_echo.nim2
-rw-r--r--tests/system/t10307.nim24
-rw-r--r--tests/system/t20938.nim12
-rw-r--r--tests/system/t7894.nim23
-rw-r--r--tests/system/talloc.nim42
-rw-r--r--tests/system/talloc2.nim9
-rw-r--r--tests/system/tatomics1.nim9
-rw-r--r--tests/system/tcomparisons.nim51
-rw-r--r--tests/system/tconcat.nim (renamed from tests/concat/tconcat.nim)0
-rw-r--r--tests/system/tdeepcopy.nim7
-rw-r--r--tests/system/tdollars.nim199
-rw-r--r--tests/system/techo_unicode.nim41
-rw-r--r--tests/system/temptyecho.nim6
-rw-r--r--tests/system/tensuremove.nim131
-rw-r--r--tests/system/tensuremove1.nim16
-rw-r--r--tests/system/tensuremove2.nim15
-rw-r--r--tests/system/tensuremove3.nim28
-rw-r--r--tests/system/tenum_array_repr.nim2
-rw-r--r--tests/system/tfielditerator.nim126
-rw-r--r--tests/system/tfields.nim108
-rw-r--r--tests/system/tgcnone.nim7
-rw-r--r--tests/system/tgcregions.nim6
-rw-r--r--tests/system/tgogc.nim7
-rw-r--r--tests/system/timmutableinc.nim8
-rw-r--r--tests/system/tinvalidnot.nim19
-rw-r--r--tests/system/tio.nim25
-rw-r--r--tests/system/tlocals.nim76
-rw-r--r--tests/system/tlowhigh.nim32
-rw-r--r--tests/system/tmagics.nim62
-rw-r--r--tests/system/tmemory.nim16
-rw-r--r--tests/system/tnew.nim (renamed from tests/misc/tnew.nim)14
-rw-r--r--tests/system/tnewderef.nim (renamed from tests/misc/tnewderef.nim)0
-rw-r--r--tests/system/tnewstring_uninitialized.nim11
-rw-r--r--tests/system/tnilconcats.nim10
-rw-r--r--tests/system/tnim_stacktrace_override.nim18
-rw-r--r--tests/system/toString.nim110
-rw-r--r--tests/system/tostring.nim125
-rw-r--r--tests/system/tparams.nim20
-rw-r--r--tests/system/trealloc.nim14
-rw-r--r--tests/system/trefs.nim15
-rw-r--r--tests/system/tsigexitcode.nim23
-rw-r--r--tests/system/tslices.nim65
-rw-r--r--tests/system/tslimsystem.nim6
-rw-r--r--tests/system/tstatic.nim58
-rw-r--r--tests/system/tstatic_callable.nim12
-rw-r--r--tests/system/tstatic_callable_error.nim14
-rw-r--r--tests/system/tsystem_misc.nim159
-rw-r--r--tests/system/tuse_version16.nim49
-rw-r--r--tests/system/tvarargslen.nim60
-rw-r--r--tests/template/m1027a.nim1
-rw-r--r--tests/template/m1027b.nim1
-rw-r--r--tests/template/m19277_1.nim2
-rw-r--r--tests/template/m19277_2.nim2
-rw-r--r--tests/template/mdotcall.nim82
-rw-r--r--tests/template/mdotcall2.nim7
-rw-r--r--tests/template/mgensym_generic_cross_module.nim4
-rw-r--r--tests/template/mqualifiedtype1.nim2
-rw-r--r--tests/template/mqualifiedtype2.nim2
-rw-r--r--tests/template/mtempl5.nim14
-rw-r--r--tests/template/otests.nim4
-rw-r--r--tests/template/sunset.nimf68
-rw-r--r--tests/template/sunset.tmpl68
-rw-r--r--tests/template/t1027.nim22
-rw-r--r--tests/template/t11705.nim17
-rw-r--r--tests/template/t13426.nim87
-rw-r--r--tests/template/t17433.nim16
-rw-r--r--tests/template/t18113.nim14
-rw-r--r--tests/template/t19149.nim19
-rw-r--r--tests/template/t19277.nim19
-rw-r--r--tests/template/t19700.nim10
-rw-r--r--tests/template/t21231.nim10
-rw-r--r--tests/template/t21532.nim8
-rw-r--r--tests/template/t24112.nim19
-rw-r--r--tests/template/t2416.nim2
-rw-r--r--tests/template/t2do.nim23
-rw-r--r--tests/template/t6217.nim19
-rw-r--r--tests/template/t9534.nim21
-rw-r--r--tests/template/t_otemplates.nim4
-rw-r--r--tests/template/taliassyntax.nim74
-rw-r--r--tests/template/taliassyntaxerrors.nim28
-rw-r--r--tests/template/tcallsitelineinfo.nim17
-rw-r--r--tests/template/tcallsitelineinfo2.nim20
-rw-r--r--tests/template/tcan_access_hidden_field.nim9
-rw-r--r--tests/template/tconfusinglocal.nim4
-rw-r--r--tests/template/tdefault_nil.nim14
-rw-r--r--tests/template/tdefaultparam.nim56
-rw-r--r--tests/template/tdotcall.nim32
-rw-r--r--tests/template/template_issues.nim304
-rw-r--r--tests/template/template_pragmas.nim9
-rw-r--r--tests/template/template_various.nim406
-rw-r--r--tests/template/texponential_eval.nim8
-rw-r--r--tests/template/tgenericparam.nim93
-rw-r--r--tests/template/tgenerictemplates.nim37
-rw-r--r--tests/template/tgensym_generic_cross_module.nim14
-rw-r--r--tests/template/tgensym_instantiationinfo.nim24
-rw-r--r--tests/template/tgensym_label.nim18
-rw-r--r--tests/template/tgensymhijack.nim37
-rw-r--r--tests/template/tgensymregression.nim24
-rw-r--r--tests/template/tgetast_typeliar.nim23
-rw-r--r--tests/template/thygienictempl.nim6
-rw-r--r--tests/template/tidentconcatenations.nim31
-rw-r--r--tests/template/tinnerouterproc.nim20
-rw-r--r--tests/template/tissue909.nim16
-rw-r--r--tests/template/tissue993.nim20
-rw-r--r--tests/template/tit.nim11
-rw-r--r--tests/template/tlt.nim7
-rw-r--r--tests/template/tmethodcall.nim24
-rw-r--r--tests/template/tmodulealias.nim2
-rw-r--r--tests/template/tmore_regressions.nim44
-rw-r--r--tests/template/tnested.nim38
-rw-r--r--tests/template/tobjectdeclfield.nim21
-rw-r--r--tests/template/topensym.nim209
-rw-r--r--tests/template/topensymoverride.nim39
-rw-r--r--tests/template/topensymwarning.nim60
-rw-r--r--tests/template/tparams_gensymed.nim348
-rw-r--r--tests/template/tparamscope.nim10
-rw-r--r--tests/template/tpattern_with_converter.nim27
-rw-r--r--tests/template/tprefer_immediate.nim15
-rw-r--r--tests/template/tprocparshadow.nim11
-rw-r--r--tests/template/tqualifiedident.nim8
-rw-r--r--tests/template/tqualifiedtype.nim25
-rw-r--r--tests/template/tredefinition.nim13
-rw-r--r--tests/template/tredefinition_override.nim33
-rw-r--r--tests/template/tshadow.nim29
-rw-r--r--tests/template/tsighash_regression.nim5
-rw-r--r--tests/template/tstmt_semchecked_twice.nim30
-rw-r--r--tests/template/tsymchoicefield.nim11
-rw-r--r--tests/template/ttemp_in_varargs.nim9
-rw-r--r--tests/template/ttempl.nim27
-rw-r--r--tests/template/ttempl2.nim7
-rw-r--r--tests/template/ttempl3.nim31
-rw-r--r--tests/template/ttempl4.nim7
-rw-r--r--tests/template/ttempl5.nim28
-rw-r--r--tests/template/ttemplreturntype.nim4
-rw-r--r--tests/template/tunderscore1.nim11
-rw-r--r--tests/template/twhen_gensym.nim13
-rw-r--r--tests/template/twhenintemplates.nim11
-rw-r--r--tests/template/twrongmapit.nim12
-rw-r--r--tests/template/twrongopensymchoice.nim2
-rw-r--r--tests/template/typedescids.nim17
-rw-r--r--tests/template/utemplates.nim21
-rw-r--r--tests/test_nimscript.nims154
-rw-r--r--tests/testament/backend.nim75
-rw-r--r--tests/testament/categories.nim524
-rw-r--r--tests/testament/specs.nim202
-rw-r--r--tests/testament/t16576.nim7
-rw-r--r--tests/testament/tester.nim515
-rw-r--r--tests/testament/tester.nim.cfg1
-rw-r--r--tests/testament/tinlinemsg.nim8
-rw-r--r--tests/testament/tjoinable.nim9
-rw-r--r--tests/testament/treject.nim6
-rw-r--r--tests/testament/tshould_not_work.nim53
-rw-r--r--tests/testament/tspecialpaths.nim9
-rw-r--r--tests/testdata/mycert.pem105
-rw-r--r--tests/threads/t7172.nim34
-rw-r--r--tests/threads/t8535.nim30
-rw-r--r--tests/threads/tactors.nim13
-rw-r--r--tests/threads/tactors2.nim25
-rw-r--r--tests/threads/threadex.nim7
-rw-r--r--tests/threads/tjsthreads.nim6
-rw-r--r--tests/threads/tmanyjoin.nim3
-rw-r--r--tests/threads/tmembug.nim51
-rw-r--r--tests/threads/tonthreadcreation.nim2
-rw-r--r--tests/threads/tracy_allocator.nim1
-rw-r--r--tests/threads/trecursive_actor.nim20
-rw-r--r--tests/threads/treusetvar.nim7
-rw-r--r--tests/threads/tthreadanalysis.nim6
-rw-r--r--tests/threads/tthreadanalysis2.nim52
-rw-r--r--tests/threads/tthreadheapviolation1.nim4
-rw-r--r--tests/threads/tthreadvars.nim1
-rw-r--r--tests/threads/ttryrecv.nim8
-rw-r--r--tests/tools/compile/config.nims1
-rw-r--r--tests/tools/compile/readme.md1
-rw-r--r--tests/tools/compile/tdeps.nim5
-rw-r--r--tests/tools/compile/tdetect.nim5
-rw-r--r--tests/tools/compile/tkoch.nim5
-rw-r--r--tests/tools/compile/tniminst.nim5
-rw-r--r--tests/tools/config.nims3
-rw-r--r--tests/tools/dontmentionme.nim3
-rw-r--r--tests/tools/second.nim3
-rw-r--r--tests/tools/tlinter.nim40
-rw-r--r--tests/tools/tnimgrep.nim404
-rw-r--r--tests/tools/tnimscriptwithmacro.nims22
-rw-r--r--tests/tools/tunused_imports.nim41
-rw-r--r--tests/trmacros/targlist.nim9
-rw-r--r--tests/trmacros/tcse.nim13
-rw-r--r--tests/trmacros/tdisallowif.nim5
-rw-r--r--tests/trmacros/thoist.nim13
-rw-r--r--tests/trmacros/tmatrix.nim29
-rw-r--r--tests/trmacros/tnoalias.nim16
-rw-r--r--tests/trmacros/tnoalias2.nim19
-rw-r--r--tests/trmacros/tnoendlessrec.nim10
-rw-r--r--tests/trmacros/tnorewrite.nim20
-rw-r--r--tests/trmacros/tor.nim24
-rw-r--r--tests/trmacros/tpartial.nim11
-rw-r--r--tests/trmacros/tpatterns.nim23
-rw-r--r--tests/trmacros/trmacros_various.nim111
-rw-r--r--tests/trmacros/trmacros_various2.nim91
-rw-r--r--tests/trmacros/tstar.nim19
-rw-r--r--tests/trmacros/tstatic_t_bug.nim24
-rw-r--r--tests/trmacros/tstmtlist.nim15
-rw-r--r--tests/tuples/mnimsconstunpack.nim4
-rw-r--r--tests/tuples/t12892.nim8
-rw-r--r--tests/tuples/t18125_1.nim14
-rw-r--r--tests/tuples/t18125_2.nim20
-rw-r--r--tests/tuples/t7012.nim7
-rw-r--r--tests/tuples/t9177.nim15
-rw-r--r--tests/tuples/tanontuples.nim26
-rw-r--r--tests/tuples/tconver_tuple.nim23
-rw-r--r--tests/tuples/tdifferent_instantiations.nim9
-rw-r--r--tests/tuples/tfortupleunpack.nim52
-rw-r--r--tests/tuples/tgeneric_tuple.nim9
-rw-r--r--tests/tuples/tgeneric_tuple2.nim17
-rw-r--r--tests/tuples/tinferred_generic_const.nim14
-rw-r--r--tests/tuples/tnimsconstunpack.nim8
-rw-r--r--tests/tuples/ttuples_issues.nim133
-rw-r--r--tests/tuples/ttuples_various.nim211
-rw-r--r--tests/tuples/ttypedesc_in_tuple_a.nim5
-rw-r--r--tests/tuples/ttypedesc_in_tuple_b.nim5
-rw-r--r--tests/tuples/tuint_tuple.nim10
-rw-r--r--tests/tuples/tunpack_asgn.nim34
-rw-r--r--tests/tuples/tuple_subscript.nim40
-rw-r--r--tests/tuples/tuple_with_nil.nim48
-rw-r--r--tests/tuples/tuple_with_seq.nim46
-rw-r--r--tests/tuples/twrongtupleaccess.nim3
-rw-r--r--tests/typerel/t2plus.nim4
-rw-r--r--tests/typerel/t4799.nim1
-rw-r--r--tests/typerel/t4799_1.nim4
-rw-r--r--tests/typerel/t4799_2.nim4
-rw-r--r--tests/typerel/t4799_3.nim4
-rw-r--r--tests/typerel/t7600_1.nim5
-rw-r--r--tests/typerel/t7600_2.nim5
-rw-r--r--tests/typerel/t7734.nim19
-rw-r--r--tests/typerel/t8172.nim11
-rw-r--r--tests/typerel/t8905.nim7
-rw-r--r--tests/typerel/tcommontype.nim4
-rw-r--r--tests/typerel/temptynode.nim2
-rw-r--r--tests/typerel/texplicitcmp.nim13
-rw-r--r--tests/typerel/tno_int_in_bool_context.nim3
-rw-r--r--tests/typerel/tnoargopenarray.nim7
-rw-r--r--tests/typerel/tnocontains.nim2
-rw-r--r--tests/typerel/tproctypeclass.nim89
-rw-r--r--tests/typerel/tptrs.nim8
-rw-r--r--tests/typerel/trefs.nim5
-rw-r--r--tests/typerel/tregionptrs.nim16
-rw-r--r--tests/typerel/trettypeinference.nim2
-rw-r--r--tests/typerel/tsecondarrayproperty.nim5
-rw-r--r--tests/typerel/tsigmatch.nim6
-rw-r--r--tests/typerel/tstr_as_openarray.nim6
-rw-r--r--tests/typerel/ttuple1.nim8
-rw-r--r--tests/typerel/ttynilinstantiation.nim7
-rw-r--r--tests/typerel/ttypedesc_as_genericparam1.nim7
-rw-r--r--tests/typerel/ttypedesc_as_genericparam1_orc.nim5
-rw-r--r--tests/typerel/ttypedesc_as_genericparam2.nim3
-rw-r--r--tests/typerel/ttypelessemptyset.nim2
-rw-r--r--tests/typerel/ttypenoval.nim4
-rw-r--r--tests/typerel/ttypenovalue.nim3
-rw-r--r--tests/typerel/tuncheckedarray_eq.nim2
-rw-r--r--tests/typerel/tvoid.nim61
-rw-r--r--tests/typerel/typedescs.nim15
-rw-r--r--tests/typerel/typedescs2.nim17
-rw-r--r--tests/typerel/typredef.nim3
-rw-r--r--tests/types/t1252.nim12
-rw-r--r--tests/types/t14127_cast_number.nim30
-rw-r--r--tests/types/t15836.nim11
-rw-r--r--tests/types/t15836_2.nim21
-rw-r--r--tests/types/t21027.nim5
-rw-r--r--tests/types/t21260.nim13
-rw-r--r--tests/types/t5640.nim6
-rw-r--r--tests/types/t5648.nim17
-rw-r--r--tests/types/t6456.nim7
-rw-r--r--tests/types/t6969.nim6
-rw-r--r--tests/types/t7905.nim33
-rw-r--r--tests/types/taliasbugs.nim13
-rw-r--r--tests/types/tassignemptytuple.nim2
-rw-r--r--tests/types/tauto_canbe_void.nim10
-rw-r--r--tests/types/tcast1.nim63
-rw-r--r--tests/types/tcyclic.nim135
-rw-r--r--tests/types/temptyseqs.nim10
-rw-r--r--tests/types/texportgeneric.nim8
-rw-r--r--tests/types/tfinalobj.nim5
-rw-r--r--tests/types/tillegalrecursion3.nim12
-rw-r--r--tests/types/tillegalseqrecursion.nim6
-rw-r--r--tests/types/tillegaltuplerecursion.nim40
-rw-r--r--tests/types/tillegaltuplerecursiongeneric.nim7
-rw-r--r--tests/types/tillegaltuplerecursiongeneric2.nim9
-rw-r--r--tests/types/tillegaltyperecursion.nim53
-rw-r--r--tests/types/tillegaltyperecursion2.nim2
-rw-r--r--tests/types/tillegaltyperecursion3.nim10
-rw-r--r--tests/types/tillrec.nim3
-rw-r--r--tests/types/tinheritgenericparameter.nim39
-rw-r--r--tests/types/tinheritref.nim55
-rw-r--r--tests/types/tisop.nim48
-rw-r--r--tests/types/tisopr.nim88
-rw-r--r--tests/types/tissues_types.nim118
-rw-r--r--tests/types/titerable.nim143
-rw-r--r--tests/types/tlent_var.nim25
-rw-r--r--tests/types/tnontype.nim9
-rw-r--r--tests/types/told_pragma_syntax1.nim5
-rw-r--r--tests/types/told_pragma_syntax2.nim5
-rw-r--r--tests/types/tparameterizedparent0.nim2
-rw-r--r--tests/types/tparameterizedparent1.nim2
-rw-r--r--tests/types/tparameterizedparent3.nim2
-rw-r--r--tests/types/tparameterizedparent4.nim2
-rw-r--r--tests/types/ttopdowninference.nim333
-rw-r--r--tests/types/tyet_another_generic_regression.nim28
-rw-r--r--tests/untestable/gdb/gdb_pretty_printer_test.py64
-rw-r--r--tests/untestable/gdb/gdb_pretty_printer_test_program.nim89
-rwxr-xr-xtests/untestable/gdb/gdb_pretty_printer_test_run.sh13
-rw-r--r--tests/untestable/network/README.md8
-rw-r--r--tests/untestable/network/stdlib/tnet.nim16
-rw-r--r--tests/untestable/thttpclient_ssl_disabled.nim36
-rw-r--r--tests/untestable/thttpclient_ssl_env_var.nim74
-rw-r--r--tests/untestable/thttpclient_ssl_remotenetwork.nim230
-rw-r--r--tests/untestable/tpostgres.nim327
-rw-r--r--tests/untestable/tssl.nim2
-rw-r--r--tests/usingstmt/tthis.nim15
-rw-r--r--tests/usingstmt/tusingstatement.nim87
-rw-r--r--tests/usingstmt/tusingstmt.nim16
-rw-r--r--tests/valgrind/tbasic_valgrind.nim6
-rw-r--r--tests/valgrind/tleak_arc.nim14
-rw-r--r--tests/varres/tnewseq_on_result_vart.nim2
-rw-r--r--tests/varres/tprevent_forloopvar_mutations.nim16
-rw-r--r--tests/varres/tvarres0.nim111
-rw-r--r--tests/varres/tvarres1.nim3
-rw-r--r--tests/varres/tvarres2.nim3
-rw-r--r--tests/varres/tvarres_via_forwarding.nim2
-rw-r--r--tests/varres/tvartup.nim3
-rw-r--r--tests/varres/twrong_parameter.nim2
-rw-r--r--tests/varstmt/tvardecl.nim8
-rw-r--r--tests/views/t19986.nim42
-rw-r--r--tests/views/tcan_compile_nim.nim4
-rw-r--r--tests/views/tcannot_borrow.nim22
-rw-r--r--tests/views/tconst_views.nim37
-rw-r--r--tests/views/tdont_mutate.nim58
-rw-r--r--tests/views/tmust_borrow_from_first_parameter.nim9
-rw-r--r--tests/views/tsplit_into_openarray.nim37
-rw-r--r--tests/views/tsplit_into_seq.nim41
-rw-r--r--tests/views/tsplit_into_table.nim40
-rw-r--r--tests/views/tviews1.nim136
-rw-r--r--tests/views/tviews2.nim90
-rw-r--r--tests/vm/meta.nim22
-rw-r--r--tests/vm/mevalffi.nim71
-rw-r--r--tests/vm/mscriptcompiletime.nim7
-rw-r--r--tests/vm/t11637.nim52
-rw-r--r--tests/vm/t17039.nim10
-rw-r--r--tests/vm/t17121.nim9
-rw-r--r--tests/vm/t18103.nim35
-rw-r--r--tests/vm/t19075.nim19
-rw-r--r--tests/vm/t19199.nim6
-rw-r--r--tests/vm/t20746.nim13
-rw-r--r--tests/vm/t21704.nim69
-rw-r--r--tests/vm/t2574.nim14
-rw-r--r--tests/vm/t9622.nim30
-rw-r--r--tests/vm/tableinstatic.nim4
-rw-r--r--tests/vm/taddrof.nim110
-rw-r--r--tests/vm/tanonproc.nim4
-rw-r--r--tests/vm/tbitops.nim45
-rw-r--r--tests/vm/tcastint.nim223
-rw-r--r--tests/vm/tcgemptycallarg.nim3
-rw-r--r--tests/vm/tclosureiterator.nim9
-rw-r--r--tests/vm/tcnstseq.nim68
-rw-r--r--tests/vm/tcompilesetting.nim18
-rw-r--r--tests/vm/tcompiletimerange.nim28
-rw-r--r--tests/vm/tcompiletimesideeffects.nim42
-rw-r--r--tests/vm/tcompiletimetable.nim17
-rw-r--r--tests/vm/tcomponent.nim2
-rw-r--r--tests/vm/tconst.nim58
-rw-r--r--tests/vm/tconstarrayresem.nim29
-rw-r--r--tests/vm/tconsteval.nim31
-rw-r--r--tests/vm/tconstobj.nim67
-rw-r--r--tests/vm/tconstprocassignments.nim18
-rw-r--r--tests/vm/tconstresem.nim10
-rw-r--r--tests/vm/tconstscope1.nim5
-rw-r--r--tests/vm/tconstscope2.nim5
-rw-r--r--tests/vm/tconsttable.nim15
-rw-r--r--tests/vm/tconsttable2.nim2
-rw-r--r--tests/vm/tconvaddr.nim49
-rw-r--r--tests/vm/teval1.nim26
-rw-r--r--tests/vm/texception.nim14
-rw-r--r--tests/vm/texcl.nim2
-rw-r--r--tests/vm/textensionmap.nim2
-rw-r--r--tests/vm/tfarjump.nim14
-rw-r--r--tests/vm/tfibconst.nim43
-rw-r--r--tests/vm/tfile_rw.nim22
-rw-r--r--tests/vm/tforwardproc.nim2
-rw-r--r--tests/vm/tgenericcompiletimeproc.nim36
-rw-r--r--tests/vm/tgloballetfrommacro.nim13
-rw-r--r--tests/vm/tgorge.nim9
-rwxr-xr-x[-rw-r--r--]tests/vm/tgorge.sh0
-rwxr-xr-x[-rw-r--r--]tests/vm/tgorgeex.sh0
-rw-r--r--tests/vm/tinheritance.nim79
-rw-r--r--tests/vm/tissues.nim76
-rw-r--r--tests/vm/tmanyregs.nim16
-rw-r--r--tests/vm/tmaxloopiterations.nim15
-rw-r--r--tests/vm/tmisc_vm.nim459
-rw-r--r--tests/vm/tmitems.nim45
-rw-r--r--tests/vm/tmitems_vm.nim45
-rw-r--r--tests/vm/tnewseqofcap.nim14
-rw-r--r--tests/vm/tnilclosurecall.nim8
-rw-r--r--tests/vm/tnilclosurecallstacktrace.nim23
-rw-r--r--tests/vm/tnilref.nim7
-rw-r--r--tests/vm/tnimnode.nim34
-rw-r--r--tests/vm/tnocompiletimefunc.nim14
-rw-r--r--tests/vm/tnocompiletimefunclambda.nim6
-rw-r--r--tests/vm/tnoreturn.nim32
-rw-r--r--tests/vm/topenarrays.nim89
-rw-r--r--tests/vm/toverflowopcaddimmint.nim2
-rw-r--r--tests/vm/toverflowopcaddint.nim2
-rw-r--r--tests/vm/toverflowopcmulint.nim4
-rw-r--r--tests/vm/toverflowopcsubimmint.nim2
-rw-r--r--tests/vm/toverflowopcsubint.nim2
-rw-r--r--tests/vm/tquadplus.nim5
-rw-r--r--tests/vm/tref.nim61
-rw-r--r--tests/vm/treset.nim50
-rw-r--r--tests/vm/trgba.nim36
-rw-r--r--tests/vm/triangle_array.nim6
-rw-r--r--tests/vm/tscriptcompiletime.nims9
-rw-r--r--tests/vm/tseq_badinit.nim4
-rw-r--r--tests/vm/tsetlen.nim30
-rw-r--r--tests/vm/tsignaturehash.nim20
-rw-r--r--tests/vm/tsimpleglobals.nim2
-rw-r--r--tests/vm/tslow_tables.nim30
-rw-r--r--tests/vm/tslurp.nim10
-rw-r--r--tests/vm/tstaticprintseq.nim6
-rw-r--r--tests/vm/tstring_openarray.nim25
-rw-r--r--tests/vm/tstringnil.nim10
-rw-r--r--tests/vm/tswap.nim14
-rw-r--r--tests/vm/ttouintconv.nim17
-rw-r--r--tests/vm/ttypedesc.nim31
-rw-r--r--tests/vm/tunsupportedintfloatcast.nim3
-rw-r--r--tests/vm/tvarsection.nim11
-rw-r--r--tests/vm/tvmmisc.nim750
-rw-r--r--tests/vm/tvmops.nim46
-rw-r--r--tests/vm/tvmopsDanger.nim13
-rw-r--r--tests/vm/twrong_concat.nim8
-rw-r--r--tests/vm/twrongarray.nim2
-rw-r--r--tests/vm/tyaytypedesc.nim8
-rw-r--r--tests/vm/tzero_extend.nim20
-rw-r--r--tests/whenstmt/t12517.nim21
-rw-r--r--tests/whenstmt/t19426.nim16
-rw-r--r--tests/whenstmt/twhen.nim47
-rw-r--r--tests/whenstmt/twhen_macro.nim18
-rw-r--r--tests/xml/ttree_add.nim51
-rw-r--r--tests/xml/ttree_add1.nim53
-rw-r--r--tests/xml/ttree_delete.nim47
-rw-r--r--tests/xml/ttree_delete1.nim48
-rw-r--r--tests/xml/ttree_insert.nim53
-rw-r--r--tests/xml/ttree_insert1.nim51
-rw-r--r--tests/xml/ttree_replace.nim46
-rw-r--r--tests/xml/ttree_replace1.nim53
-rw-r--r--tinyc/arm-asm.c94
-rw-r--r--tinyc/arm-gen.c2151
-rw-r--r--tinyc/arm-link.c392
-rw-r--r--tinyc/arm64-gen.c1837
-rw-r--r--tinyc/arm64-link.c250
-rw-r--r--tinyc/c67-gen.c2540
-rw-r--r--tinyc/c67-link.c131
-rw-r--r--tinyc/coff.h446
-rw-r--r--tinyc/config.h22
-rw-r--r--tinyc/conftest.c87
-rw-r--r--tinyc/elf.h3237
-rw-r--r--tinyc/examples/ex1.c8
-rw-r--r--tinyc/examples/ex2.c98
-rw-r--r--tinyc/examples/ex3.c23
-rw-r--r--tinyc/examples/ex4.c26
-rw-r--r--tinyc/examples/ex5.c8
-rw-r--r--tinyc/i386-asm.c1714
-rw-r--r--tinyc/i386-asm.h480
-rw-r--r--tinyc/i386-gen.c1164
-rw-r--r--tinyc/i386-link.c244
-rw-r--r--tinyc/i386-tok.h253
-rw-r--r--tinyc/il-gen.c657
-rw-r--r--tinyc/il-opcodes.h251
-rw-r--r--tinyc/include/float.h57
-rw-r--r--tinyc/include/stdarg.h79
-rw-r--r--tinyc/include/stdbool.h11
-rw-r--r--tinyc/include/stddef.h54
-rw-r--r--tinyc/include/varargs.h12
-rw-r--r--tinyc/lib/alloca-arm.S17
-rw-r--r--tinyc/lib/alloca86-bt.S47
-rw-r--r--tinyc/lib/alloca86.S31
-rw-r--r--tinyc/lib/alloca86_64-bt.S56
-rw-r--r--tinyc/lib/alloca86_64.S34
-rw-r--r--tinyc/lib/armeabi.c501
-rw-r--r--tinyc/lib/armflush.c58
-rw-r--r--tinyc/lib/bcheck.c979
-rw-r--r--tinyc/lib/lib-arm64.c664
-rw-r--r--tinyc/lib/libtcc1.c622
-rw-r--r--tinyc/lib/va_list.c65
-rw-r--r--tinyc/libtcc.c1982
-rw-r--r--tinyc/libtcc.h100
-rw-r--r--tinyc/stab.def234
-rw-r--r--tinyc/stab.h17
-rw-r--r--tinyc/tcc-doc.texi1326
-rw-r--r--tinyc/tcc.c371
-rw-r--r--tinyc/tcc.h1657
-rw-r--r--tinyc/tccasm.c1303
-rw-r--r--tinyc/tcccoff.c948
-rw-r--r--tinyc/tccelf.c2994
-rw-r--r--tinyc/tccgen.c7369
-rw-r--r--tinyc/tcclib.h80
-rw-r--r--tinyc/tccpe.c2014
-rw-r--r--tinyc/tccpp.c3903
-rw-r--r--tinyc/tccrun.c840
-rw-r--r--tinyc/tcctok.h350
-rw-r--r--tinyc/tcctools.c546
-rw-r--r--tinyc/tests/42test.h13
-rw-r--r--tinyc/tests/abitest.c691
-rw-r--r--tinyc/tests/asmtest.S975
-rw-r--r--tinyc/tests/boundtest.c285
-rw-r--r--tinyc/tests/gcctestsuite.sh33
-rw-r--r--tinyc/tests/libtcc_test.c96
-rw-r--r--tinyc/tests/pp/01.c6
-rw-r--r--tinyc/tests/pp/01.expect1
-rw-r--r--tinyc/tests/pp/02.c28
-rw-r--r--tinyc/tests/pp/02.expect5
-rw-r--r--tinyc/tests/pp/03.c15
-rw-r--r--tinyc/tests/pp/03.expect5
-rw-r--r--tinyc/tests/pp/04.c4
-rw-r--r--tinyc/tests/pp/04.expect1
-rw-r--r--tinyc/tests/pp/05.c7
-rw-r--r--tinyc/tests/pp/05.expect3
-rw-r--r--tinyc/tests/pp/06.c5
-rw-r--r--tinyc/tests/pp/06.expect1
-rw-r--r--tinyc/tests/pp/07.c4
-rw-r--r--tinyc/tests/pp/07.expect2
-rw-r--r--tinyc/tests/pp/08.c4
-rw-r--r--tinyc/tests/pp/08.expect1
-rw-r--r--tinyc/tests/pp/09.c4
-rw-r--r--tinyc/tests/pp/09.expect1
-rw-r--r--tinyc/tests/pp/10.c10
-rw-r--r--tinyc/tests/pp/10.expect5
-rw-r--r--tinyc/tests/pp/11.c31
-rw-r--r--tinyc/tests/pp/11.expect15
-rw-r--r--tinyc/tests/pp/12.S8
-rw-r--r--tinyc/tests/pp/12.expect2
-rw-r--r--tinyc/tests/pp/13.S6
-rw-r--r--tinyc/tests/pp/13.expect2
-rw-r--r--tinyc/tests/pp/14.c13
-rw-r--r--tinyc/tests/pp/14.expect3
-rw-r--r--tinyc/tests/pp/15.c18
-rw-r--r--tinyc/tests/pp/15.expect5
-rw-r--r--tinyc/tests/pp/16.c3
-rw-r--r--tinyc/tests/pp/16.expect2
-rw-r--r--tinyc/tests/pp/17.c14
-rw-r--r--tinyc/tests/pp/17.expect6
-rw-r--r--tinyc/tests/pp/18.c15
-rw-r--r--tinyc/tests/pp/18.expect3
-rw-r--r--tinyc/tests/pp/19.c101
-rw-r--r--tinyc/tests/pp/19.expect14
-rw-r--r--tinyc/tests/pp/20.c13
-rw-r--r--tinyc/tests/pp/20.expect6
-rw-r--r--tinyc/tests/pp/21.c36
-rw-r--r--tinyc/tests/pp/21.expect8
-rw-r--r--tinyc/tests/pp/pp-counter.c27
-rw-r--r--tinyc/tests/pp/pp-counter.expect15
-rw-r--r--tinyc/tests/tcctest.c3813
-rw-r--r--tinyc/tests/tcctest.h9
-rw-r--r--tinyc/tests/testfp.c510
-rw-r--r--tinyc/tests/tests2/00_assignment.c18
-rw-r--r--tinyc/tests/tests2/00_assignment.expect3
-rw-r--r--tinyc/tests/tests2/01_comment.c14
-rw-r--r--tinyc/tests/tests2/01_comment.expect5
-rw-r--r--tinyc/tests/tests2/02_printf.c18
-rw-r--r--tinyc/tests/tests2/02_printf.expect15
-rw-r--r--tinyc/tests/tests2/03_struct.c31
-rw-r--r--tinyc/tests/tests2/03_struct.expect6
-rw-r--r--tinyc/tests/tests2/04_for.c15
-rw-r--r--tinyc/tests/tests2/04_for.expect10
-rw-r--r--tinyc/tests/tests2/05_array.c21
-rw-r--r--tinyc/tests/tests2/05_array.expect10
-rw-r--r--tinyc/tests/tests2/06_case.c29
-rw-r--r--tinyc/tests/tests2/06_case.expect8
-rw-r--r--tinyc/tests/tests2/07_function.c30
-rw-r--r--tinyc/tests/tests2/07_function.expect4
-rw-r--r--tinyc/tests/tests2/08_while.c24
-rw-r--r--tinyc/tests/tests2/08_while.expect11
-rw-r--r--tinyc/tests/tests2/09_do_while.c24
-rw-r--r--tinyc/tests/tests2/09_do_while.expect11
-rw-r--r--tinyc/tests/tests2/10_pointer.c40
-rw-r--r--tinyc/tests/tests2/10_pointer.expect8
-rw-r--r--tinyc/tests/tests2/11_precedence.c40
-rw-r--r--tinyc/tests/tests2/11_precedence.expect15
-rw-r--r--tinyc/tests/tests2/12_hashdefine.c14
-rw-r--r--tinyc/tests/tests2/12_hashdefine.expect2
-rw-r--r--tinyc/tests/tests2/13_integer_literals.c20
-rw-r--r--tinyc/tests/tests2/13_integer_literals.expect5
-rw-r--r--tinyc/tests/tests2/14_if.c21
-rw-r--r--tinyc/tests/tests2/14_if.expect2
-rw-r--r--tinyc/tests/tests2/15_recursion.c21
-rw-r--r--tinyc/tests/tests2/15_recursion.expect10
-rw-r--r--tinyc/tests/tests2/16_nesting.c21
-rw-r--r--tinyc/tests/tests2/16_nesting.expect18
-rw-r--r--tinyc/tests/tests2/17_enum.c72
-rw-r--r--tinyc/tests/tests2/17_enum.expect4
-rw-r--r--tinyc/tests/tests2/18_include.c12
-rw-r--r--tinyc/tests/tests2/18_include.expect3
-rw-r--r--tinyc/tests/tests2/18_include.h1
-rw-r--r--tinyc/tests/tests2/19_pointer_arithmetic.c28
-rw-r--r--tinyc/tests/tests2/19_pointer_arithmetic.expect3
-rw-r--r--tinyc/tests/tests2/20_pointer_comparison.c24
-rw-r--r--tinyc/tests/tests2/20_pointer_comparison.expect6
-rw-r--r--tinyc/tests/tests2/21_char_array.c33
-rw-r--r--tinyc/tests/tests2/21_char_array.expect7
-rw-r--r--tinyc/tests/tests2/22_floating_point.c50
-rw-r--r--tinyc/tests/tests2/22_floating_point.expect16
-rw-r--r--tinyc/tests/tests2/23_type_coercion.c54
-rw-r--r--tinyc/tests/tests2/23_type_coercion.expect12
-rw-r--r--tinyc/tests/tests2/24_math_library.c30
-rw-r--r--tinyc/tests/tests2/24_math_library.expect18
-rw-r--r--tinyc/tests/tests2/25_quicksort.c83
-rw-r--r--tinyc/tests/tests2/25_quicksort.expect2
-rw-r--r--tinyc/tests/tests2/26_character_constants.c17
-rw-r--r--tinyc/tests/tests2/26_character_constants.expect8
-rw-r--r--tinyc/tests/tests2/27_sizeof.c18
-rw-r--r--tinyc/tests/tests2/27_sizeof.expect4
-rw-r--r--tinyc/tests/tests2/28_strings.c45
-rw-r--r--tinyc/tests/tests2/28_strings.expect19
-rw-r--r--tinyc/tests/tests2/29_array_address.c13
-rw-r--r--tinyc/tests/tests2/29_array_address.expect1
-rw-r--r--tinyc/tests/tests2/30_hanoi.c122
-rw-r--r--tinyc/tests/tests2/30_hanoi.expect71
-rw-r--r--tinyc/tests/tests2/31_args.c14
-rw-r--r--tinyc/tests/tests2/31_args.expect6
-rw-r--r--tinyc/tests/tests2/32_led.c266
-rw-r--r--tinyc/tests/tests2/32_led.expect4
-rw-r--r--tinyc/tests/tests2/33_ternary_op.c15
-rw-r--r--tinyc/tests/tests2/33_ternary_op.expect10
-rw-r--r--tinyc/tests/tests2/34_array_assignment.c23
-rw-r--r--tinyc/tests/tests2/34_array_assignment.expect2
-rw-r--r--tinyc/tests/tests2/35_sizeof.c14
-rw-r--r--tinyc/tests/tests2/35_sizeof.expect2
-rw-r--r--tinyc/tests/tests2/36_array_initialisers.c21
-rw-r--r--tinyc/tests/tests2/36_array_initialisers.expect20
-rw-r--r--tinyc/tests/tests2/37_sprintf.c17
-rw-r--r--tinyc/tests/tests2/37_sprintf.expect20
-rw-r--r--tinyc/tests/tests2/38_multiple_array_index.c32
-rw-r--r--tinyc/tests/tests2/38_multiple_array_index.expect4
-rw-r--r--tinyc/tests/tests2/39_typedef.c65
-rw-r--r--tinyc/tests/tests2/39_typedef.expect3
-rw-r--r--tinyc/tests/tests2/40_stdio.c52
-rw-r--r--tinyc/tests/tests2/40_stdio.expect27
-rw-r--r--tinyc/tests/tests2/41_hashif.c85
-rw-r--r--tinyc/tests/tests2/41_hashif.expect6
-rw-r--r--tinyc/tests/tests2/42_function_pointer.c22
-rw-r--r--tinyc/tests/tests2/42_function_pointer.expect2
-rw-r--r--tinyc/tests/tests2/43_void_param.c15
-rw-r--r--tinyc/tests/tests2/43_void_param.expect1
-rw-r--r--tinyc/tests/tests2/44_scoped_declarations.c17
-rw-r--r--tinyc/tests/tests2/44_scoped_declarations.expect1
-rw-r--r--tinyc/tests/tests2/45_empty_for.c18
-rw-r--r--tinyc/tests/tests2/45_empty_for.expect10
-rw-r--r--tinyc/tests/tests2/46_grep.c568
-rw-r--r--tinyc/tests/tests2/46_grep.expect3
-rw-r--r--tinyc/tests/tests2/47_switch_return.c24
-rw-r--r--tinyc/tests/tests2/47_switch_return.expect4
-rw-r--r--tinyc/tests/tests2/48_nested_break.c26
-rw-r--r--tinyc/tests/tests2/48_nested_break.expect1
-rw-r--r--tinyc/tests/tests2/49_bracket_evaluation.c23
-rw-r--r--tinyc/tests/tests2/49_bracket_evaluation.expect1
-rw-r--r--tinyc/tests/tests2/50_logical_second_arg.c29
-rw-r--r--tinyc/tests/tests2/50_logical_second_arg.expect20
-rw-r--r--tinyc/tests/tests2/51_static.c30
-rw-r--r--tinyc/tests/tests2/51_static.expect8
-rw-r--r--tinyc/tests/tests2/52_unnamed_enum.c27
-rw-r--r--tinyc/tests/tests2/52_unnamed_enum.expect9
-rw-r--r--tinyc/tests/tests2/54_goto.c56
-rw-r--r--tinyc/tests/tests2/54_goto.expect8
-rw-r--r--tinyc/tests/tests2/55_lshift_type.c52
-rw-r--r--tinyc/tests/tests2/55_lshift_type.expect1
-rw-r--r--tinyc/tests/tests2/60_errors_and_warnings.c51
-rw-r--r--tinyc/tests/tests2/60_errors_and_warnings.expect28
-rw-r--r--tinyc/tests/tests2/64_macro_nesting.c12
-rw-r--r--tinyc/tests/tests2/64_macro_nesting.expect1
-rw-r--r--tinyc/tests/tests2/67_macro_concat.c14
-rw-r--r--tinyc/tests/tests2/67_macro_concat.expect2
-rw-r--r--tinyc/tests/tests2/70_floating_point_literals.c77
-rw-r--r--tinyc/tests/tests2/70_floating_point_literals.expect53
-rw-r--r--tinyc/tests/tests2/71_macro_empty_arg.c9
-rw-r--r--tinyc/tests/tests2/71_macro_empty_arg.expect1
-rw-r--r--tinyc/tests/tests2/72_long_long_constant.c19
-rw-r--r--tinyc/tests/tests2/72_long_long_constant.expect1
-rw-r--r--tinyc/tests/tests2/73_arm64.c527
-rw-r--r--tinyc/tests/tests2/73_arm64.expect174
-rw-r--r--tinyc/tests/tests2/75_array_in_struct_init.c33
-rw-r--r--tinyc/tests/tests2/75_array_in_struct_init.expect72
-rw-r--r--tinyc/tests/tests2/76_dollars_in_identifiers.c41
-rw-r--r--tinyc/tests/tests2/76_dollars_in_identifiers.expect14
-rw-r--r--tinyc/tests/tests2/77_push_pop_macro.c30
-rw-r--r--tinyc/tests/tests2/77_push_pop_macro.expect5
-rw-r--r--tinyc/tests/tests2/78_vla_label.c45
-rw-r--r--tinyc/tests/tests2/78_vla_label.expect6
-rw-r--r--tinyc/tests/tests2/79_vla_continue.c116
-rw-r--r--tinyc/tests/tests2/79_vla_continue.expect5
-rw-r--r--tinyc/tests/tests2/80_flexarray.c25
-rw-r--r--tinyc/tests/tests2/81_types.c43
-rw-r--r--tinyc/tests/tests2/82_attribs_position.c19
-rw-r--r--tinyc/tests/tests2/83_utf8_in_identifiers.c9
-rw-r--r--tinyc/tests/tests2/83_utf8_in_identifiers.expect2
-rw-r--r--tinyc/tests/tests2/84_hex-float.c12
-rw-r--r--tinyc/tests/tests2/84_hex-float.expect1
-rw-r--r--tinyc/tests/tests2/85_asm-outside-function.c9
-rw-r--r--tinyc/tests/tests2/85_asm-outside-function.expect1
-rw-r--r--tinyc/tests/tests2/86_memory-model.c38
-rw-r--r--tinyc/tests/tests2/86_memory-model.expect1
-rw-r--r--tinyc/tests/tests2/87_dead_code.c122
-rw-r--r--tinyc/tests/tests2/87_dead_code.expect18
-rw-r--r--tinyc/tests/tests2/88_codeopt.c68
-rw-r--r--tinyc/tests/tests2/88_codeopt.expect2
-rw-r--r--tinyc/tests/tests2/89_nocode_wanted.c112
-rw-r--r--tinyc/tests/tests2/89_nocode_wanted.expect14
-rw-r--r--tinyc/tests/tests2/90_struct-init.c282
-rw-r--r--tinyc/tests/tests2/90_struct-init.expect43
-rw-r--r--tinyc/tests/tests2/91_ptr_longlong_arith32.c15
-rw-r--r--tinyc/tests/tests2/91_ptr_longlong_arith32.expect1
-rw-r--r--tinyc/tests/tests2/92_enum_bitfield.c57
-rw-r--r--tinyc/tests/tests2/93_integer_promotion.c71
-rw-r--r--tinyc/tests/tests2/93_integer_promotion.expect46
-rw-r--r--tinyc/tests/tests2/94_generic.c64
-rw-r--r--tinyc/tests/tests2/94_generic.expect13
-rw-r--r--tinyc/tests/tests2/95_bitfields.c218
-rw-r--r--tinyc/tests/tests2/95_bitfields.expect149
-rw-r--r--tinyc/tests/tests2/95_bitfields_ms.c2
-rw-r--r--tinyc/tests/tests2/95_bitfields_ms.expect149
-rw-r--r--tinyc/tests/tests2/96_nodata_wanted.c84
-rw-r--r--tinyc/tests/tests2/96_nodata_wanted.expect23
-rw-r--r--tinyc/tests/tests2/97_utf8_string_literal.c12
-rw-r--r--tinyc/tests/tests2/97_utf8_string_literal.expect1
-rw-r--r--tinyc/tests/tests2/98_al_ax_extend.c41
-rw-r--r--tinyc/tests/tests2/98_al_ax_extend.expect9
-rw-r--r--tinyc/tests/tests2/99_fastcall.c276
-rw-r--r--tinyc/tests/tests2/99_fastcall.expect1
-rw-r--r--tinyc/tests/vla_test.c84
-rw-r--r--tinyc/texi2pod.pl427
-rw-r--r--tinyc/win32/build-tcc.bat189
-rw-r--r--tinyc/win32/examples/dll.c13
-rw-r--r--tinyc/win32/examples/fib.c24
-rw-r--r--tinyc/win32/examples/hello_dll.c20
-rw-r--r--tinyc/win32/examples/hello_win.c163
-rw-r--r--tinyc/win32/include/_mingw.h170
-rw-r--r--tinyc/win32/include/assert.h57
-rw-r--r--tinyc/win32/include/conio.h409
-rw-r--r--tinyc/win32/include/ctype.h281
-rw-r--r--tinyc/win32/include/dir.h31
-rw-r--r--tinyc/win32/include/direct.h68
-rw-r--r--tinyc/win32/include/dirent.h135
-rw-r--r--tinyc/win32/include/dos.h55
-rw-r--r--tinyc/win32/include/errno.h75
-rw-r--r--tinyc/win32/include/excpt.h123
-rw-r--r--tinyc/win32/include/fcntl.h52
-rw-r--r--tinyc/win32/include/fenv.h108
-rw-r--r--tinyc/win32/include/inttypes.h297
-rw-r--r--tinyc/win32/include/io.h418
-rw-r--r--tinyc/win32/include/limits.h111
-rw-r--r--tinyc/win32/include/locale.h91
-rw-r--r--tinyc/win32/include/malloc.h181
-rw-r--r--tinyc/win32/include/math.h737
-rw-r--r--tinyc/win32/include/mem.h13
-rw-r--r--tinyc/win32/include/memory.h40
-rw-r--r--tinyc/win32/include/process.h176
-rw-r--r--tinyc/win32/include/sec_api/conio_s.h42
-rw-r--r--tinyc/win32/include/sec_api/crtdbg_s.h19
-rw-r--r--tinyc/win32/include/sec_api/io_s.h33
-rw-r--r--tinyc/win32/include/sec_api/mbstring_s.h52
-rw-r--r--tinyc/win32/include/sec_api/search_s.h25
-rw-r--r--tinyc/win32/include/sec_api/stdio_s.h145
-rw-r--r--tinyc/win32/include/sec_api/stdlib_s.h67
-rw-r--r--tinyc/win32/include/sec_api/stralign_s.h30
-rw-r--r--tinyc/win32/include/sec_api/string_s.h41
-rw-r--r--tinyc/win32/include/sec_api/sys/timeb_s.h34
-rw-r--r--tinyc/win32/include/sec_api/tchar_s.h266
-rw-r--r--tinyc/win32/include/sec_api/time_s.h61
-rw-r--r--tinyc/win32/include/sec_api/wchar_s.h128
-rw-r--r--tinyc/win32/include/setjmp.h160
-rw-r--r--tinyc/win32/include/share.h28
-rw-r--r--tinyc/win32/include/signal.h63
-rw-r--r--tinyc/win32/include/stdint.h212
-rw-r--r--tinyc/win32/include/stdio.h429
-rw-r--r--tinyc/win32/include/stdlib.h580
-rw-r--r--tinyc/win32/include/string.h164
-rw-r--r--tinyc/win32/include/sys/fcntl.h13
-rw-r--r--tinyc/win32/include/sys/file.h14
-rw-r--r--tinyc/win32/include/sys/locking.h30
-rw-r--r--tinyc/win32/include/sys/stat.h290
-rw-r--r--tinyc/win32/include/sys/time.h69
-rw-r--r--tinyc/win32/include/sys/timeb.h133
-rw-r--r--tinyc/win32/include/sys/types.h118
-rw-r--r--tinyc/win32/include/sys/unistd.h14
-rw-r--r--tinyc/win32/include/sys/utime.h146
-rw-r--r--tinyc/win32/include/tcc/tcc_libm.h201
-rw-r--r--tinyc/win32/include/tchar.h1102
-rw-r--r--tinyc/win32/include/time.h287
-rw-r--r--tinyc/win32/include/vadefs.h11
-rw-r--r--tinyc/win32/include/values.h4
-rw-r--r--tinyc/win32/include/wchar.h873
-rw-r--r--tinyc/win32/include/wctype.h172
-rw-r--r--tinyc/win32/include/winapi/basetsd.h149
-rw-r--r--tinyc/win32/include/winapi/basetyps.h85
-rw-r--r--tinyc/win32/include/winapi/guiddef.h156
-rw-r--r--tinyc/win32/include/winapi/poppack.h8
-rw-r--r--tinyc/win32/include/winapi/pshpack1.h8
-rw-r--r--tinyc/win32/include/winapi/pshpack2.h8
-rw-r--r--tinyc/win32/include/winapi/pshpack4.h8
-rw-r--r--tinyc/win32/include/winapi/pshpack8.h8
-rw-r--r--tinyc/win32/include/winapi/winbase.h2951
-rw-r--r--tinyc/win32/include/winapi/wincon.h301
-rw-r--r--tinyc/win32/include/winapi/windef.h293
-rw-r--r--tinyc/win32/include/winapi/windows.h127
-rw-r--r--tinyc/win32/include/winapi/winerror.h3166
-rw-r--r--tinyc/win32/include/winapi/wingdi.h4080
-rw-r--r--tinyc/win32/include/winapi/winnt.h5835
-rw-r--r--tinyc/win32/include/winapi/winreg.h272
-rw-r--r--tinyc/win32/include/winapi/winuser.h5651
-rw-r--r--tinyc/win32/include/winapi/winver.h160
-rw-r--r--tinyc/win32/lib/chkstk.S191
-rw-r--r--tinyc/win32/lib/crt1.c79
-rw-r--r--tinyc/win32/lib/crt1w.c3
-rw-r--r--tinyc/win32/lib/dllcrt1.c13
-rw-r--r--tinyc/win32/lib/dllmain.c9
-rw-r--r--tinyc/win32/lib/gdi32.def337
-rw-r--r--tinyc/win32/lib/kernel32.def770
-rw-r--r--tinyc/win32/lib/msvcrt.def1399
-rw-r--r--tinyc/win32/lib/user32.def658
-rw-r--r--tinyc/win32/lib/wincrt1.c75
-rw-r--r--tinyc/win32/lib/wincrt1w.c3
-rw-r--r--tinyc/win32/tcc-win32.txt168
-rw-r--r--tinyc/x86_64-asm.h525
-rw-r--r--tinyc/x86_64-gen.c2229
-rw-r--r--tinyc/x86_64-link.c295
-rw-r--r--tools/UnicodeData.txt32840
-rw-r--r--tools/atlas/parse_requires.nim101
-rw-r--r--tools/ci_generate.nim141
-rw-r--r--tools/ci_testresults.nim24
-rw-r--r--tools/cmerge.nim6
-rw-r--r--tools/compiler.gdb39
-rw-r--r--tools/compiler.lldb40
-rw-r--r--tools/debug/customdebugtype.nim72
-rw-r--r--tools/debug/nim-gdb.py697
-rw-r--r--tools/debug/nimlldb.py1380
-rw-r--r--tools/deps.nim44
-rw-r--r--tools/detect/detect.nim57
-rw-r--r--tools/dochack/dochack.nim274
-rw-r--r--tools/dochack/fuzzysearch.nim141
-rw-r--r--tools/dochack/karax.nim343
-rw-r--r--tools/downloader.nim2
-rw-r--r--tools/finish.nim61
-rw-r--r--tools/grammar_nanny.nim54
-rw-r--r--tools/heapdump2dot.nim2
-rw-r--r--tools/heapdumprepl.nim10
-rw-r--r--tools/kochdocs.nim388
-rw-r--r--tools/nim.zsh-completion182
-rw-r--r--tools/nimblepkglist.nim (renamed from web/nimblepkglist.nim)15
-rw-r--r--tools/nimgrab.nim48
-rw-r--r--tools/nimgrep.nim1472
-rw-r--r--tools/nimgrep.nim.cfg5
-rw-r--r--tools/niminst/buildbat.nimf (renamed from tools/niminst/buildbat.tmpl)0
-rw-r--r--tools/niminst/buildsh.nimf287
-rw-r--r--tools/niminst/buildsh.tmpl218
-rw-r--r--tools/niminst/debcreation.nim8
-rw-r--r--tools/niminst/deinstall.nimf81
-rw-r--r--tools/niminst/deinstall.tmpl81
-rw-r--r--tools/niminst/inno.nimf (renamed from tools/niminst/inno.tmpl)0
-rw-r--r--tools/niminst/install.nimf138
-rw-r--r--tools/niminst/install.tmpl134
-rw-r--r--tools/niminst/makefile.nimf219
-rw-r--r--tools/niminst/makefile.tmpl177
-rw-r--r--tools/niminst/nim-file.icobin0 -> 3311 bytes
-rw-r--r--tools/niminst/niminst.nim202
-rw-r--r--tools/niminst/nsis.nimf (renamed from tools/niminst/nsis.tmpl)0
-rw-r--r--tools/niminst/setup.icobin0 -> 2551 bytes
-rw-r--r--tools/niminst/uninstall.icobin0 -> 2226 bytes
-rw-r--r--tools/nimrepl.nim172
-rw-r--r--tools/nimweb.nim549
-rw-r--r--tools/officialpackages.nim21
-rw-r--r--tools/ssl_config_parser.nim64
-rw-r--r--tools/ssl_config_parser.nims1
-rw-r--r--tools/trimcc.nim2
-rw-r--r--tools/unicode_parsedata.nim224
-rw-r--r--tools/urldownloader.nim431
-rw-r--r--tools/vccenv/vccenv.nim58
-rw-r--r--tools/vccenv/vccexe.nim66
-rw-r--r--tools/vccexe/vccenv.nim47
-rw-r--r--tools/vccexe/vccexe.nim237
-rw-r--r--tools/vccexe/vccvswhere.nim47
-rw-r--r--tools/vccexe/vcvarsall.nim102
-rw-r--r--tools/website.tmpl266
-rw-r--r--web/assets/bountysource/bountysource.pngbin27078 -> 0 bytes
-rw-r--r--web/assets/bountysource/meet_sponsors.pngbin77744 -> 0 bytes
-rw-r--r--web/assets/bountysource/meet_sponsors.xcfbin213398 -> 0 bytes
-rw-r--r--web/assets/bountysource/secondspectrum.pngbin53148 -> 0 bytes
-rw-r--r--web/assets/bountysource/xored.pngbin6258 -> 0 bytes
-rw-r--r--web/assets/bountysource/xored.svg1
-rw-r--r--web/assets/images/bg.pngbin149129 -> 0 bytes
-rw-r--r--web/assets/images/docs-articles.pngbin381 -> 0 bytes
-rw-r--r--web/assets/images/docs-examples.pngbin596 -> 0 bytes
-rw-r--r--web/assets/images/docs-internals.pngbin621 -> 0 bytes
-rw-r--r--web/assets/images/docs-libraries.pngbin335 -> 0 bytes
-rw-r--r--web/assets/images/docs-tools.pngbin636 -> 0 bytes
-rw-r--r--web/assets/images/docs-tutorials.pngbin560 -> 0 bytes
-rw-r--r--web/assets/images/favicon.icobin1150 -> 0 bytes
-rw-r--r--web/assets/images/foot.pngbin784 -> 0 bytes
-rw-r--r--web/assets/images/glow-arrow.pngbin8657 -> 0 bytes
-rw-r--r--web/assets/images/glow-line.pngbin2261 -> 0 bytes
-rw-r--r--web/assets/images/head-link.pngbin203 -> 0 bytes
-rw-r--r--web/assets/images/head-link_hover.pngbin799 -> 0 bytes
-rw-r--r--web/assets/images/head.pngbin171 -> 0 bytes
-rw-r--r--web/assets/images/link_aporia.pngbin1526 -> 0 bytes
-rw-r--r--web/assets/images/link_forum.pngbin1048 -> 0 bytes
-rw-r--r--web/assets/images/link_nimbuild.pngbin896 -> 0 bytes
-rw-r--r--web/assets/images/logo.pngbin116562 -> 0 bytes
-rw-r--r--web/assets/images/mascot.pngbin18022 -> 0 bytes
-rw-r--r--web/assets/images/more-links_editors.pngbin852 -> 0 bytes
-rw-r--r--web/assets/images/more-links_forum.pngbin858 -> 0 bytes
-rw-r--r--web/assets/images/more-links_github.pngbin713 -> 0 bytes
-rw-r--r--web/assets/images/more-links_nimbuild.pngbin724 -> 0 bytes
-rw-r--r--web/assets/images/nim-logo.svg500
-rw-r--r--web/assets/images/quote.pngbin1045 -> 0 bytes
-rw-r--r--web/assets/images/quotes.pngbin979 -> 0 bytes
-rw-r--r--web/assets/images/sidebar.pngbin109579 -> 0 bytes
-rw-r--r--web/assets/images/sidebar_h2.pngbin2044 -> 0 bytes
-rw-r--r--web/assets/images/sidebar_head.pngbin34993 -> 0 bytes
-rw-r--r--web/assets/images/site_foot.pngbin3109 -> 0 bytes
-rw-r--r--web/assets/images/site_neck.pngbin317 -> 0 bytes
-rw-r--r--web/assets/images/slideshow-nav.pngbin416 -> 0 bytes
-rw-r--r--web/assets/images/slideshow-nav_active.pngbin673 -> 0 bytes
-rw-r--r--web/assets/images/tabEnd.pngbin194 -> 0 bytes
-rw-r--r--web/assets/index.js44
-rw-r--r--web/assets/news/images/0.15.0/doc_search.gifbin4916578 -> 0 bytes
-rw-r--r--web/assets/news/images/0.15.0/doc_sort.gifbin9215210 -> 0 bytes
-rw-r--r--web/assets/news/images/0.16.0/nimble.pngbin316425 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/10_needs.pngbin39707 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/book.pngbin54231 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/book_opinion.pngbin39312 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/breakage.pngbin42032 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/dev_os.pngbin68341 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/difficulty_fixing_breakage.pngbin31324 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/do_you_use_nim.pngbin28789 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/domains.pngbin59065 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/editors.pngbin28665 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/ex_nim.pngbin45423 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/languages.pngbin84839 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/learning_resources.pngbin39711 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/nim_appeal.pngbin54043 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/nim_at_work.pngbin61209 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/nim_displeasing.pngbin52584 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/nim_domains.pngbin58405 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/nim_found.pngbin68570 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/nim_time.pngbin101792 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/nim_time_rust.pngbin35888 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/nim_versions.pngbin32490 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/nimble_opinion.pngbin41325 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/non_user.pngbin45695 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/planning_to_use_at_work.pngbin47859 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/project_size.pngbin29199 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/project_size_nim_rust.pngbin44546 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/project_size_work.pngbin39499 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/reliability.pngbin29235 -> 0 bytes
-rw-r--r--web/assets/news/images/survey/target_os.pngbin72162 -> 0 bytes
-rw-r--r--web/assets/niminaction/banner.jpgbin86775 -> 0 bytes
-rw-r--r--web/assets/niminaction/banner2.pngbin71587 -> 0 bytes
-rw-r--r--web/assets/style.css630
-rw-r--r--web/assets/zeo/banner.jpgbin233810 -> 0 bytes
-rw-r--r--web/community.rst150
-rw-r--r--web/documentation.rst70
-rw-r--r--web/download.rst115
-rw-r--r--web/inactive_sponsors.csv57
-rw-r--r--web/index.rst89
-rw-r--r--web/learn.rst70
-rw-r--r--web/news.rst144
-rw-r--r--web/news/e001_version_0_8_6.rst54
-rw-r--r--web/news/e002_version_0_8_8.rst82
-rw-r--r--web/news/e003_version_0_8_10.rst70
-rw-r--r--web/news/e004_version_0_8_12.rst122
-rw-r--r--web/news/e005_version_0_8_14.rst168
-rw-r--r--web/news/e006_version_0_9_0.rst182
-rw-r--r--web/news/e007_version_0_9_2.rst118
-rw-r--r--web/news/e008_new_website.rst13
-rw-r--r--web/news/e009_andreas_rumpfs_talk.rst11
-rw-r--r--web/news/e010_dr_dobbs_journal.rst9
-rw-r--r--web/news/e011_version_0_9_4.rst179
-rw-r--r--web/news/e012_version_0_9_6.rst65
-rw-r--r--web/news/e013_version_0_10_2.rst198
-rw-r--r--web/news/e014_version_0_11_0.rst396
-rw-r--r--web/news/e015_version_0_11_2.rst12
-rw-r--r--web/news/e016_nim_conf1.rst26
-rw-r--r--web/news/e017_version_0_12_0.rst403
-rw-r--r--web/news/e018_oscon_amsterdam.rst13
-rw-r--r--web/news/e019_version_0_13_0.rst182
-rw-r--r--web/news/e020_nim_in_action.rst32
-rw-r--r--web/news/e021_meet_sponsors.rst30
-rw-r--r--web/news/e022_version_0_14_0.rst476
-rw-r--r--web/news/e023_version_0_14_2.rst14
-rw-r--r--web/news/e024_survey.rst29
-rw-r--r--web/news/e025_bountysource_update.rst73
-rw-r--r--web/news/e026_survey_results.rst699
-rw-r--r--web/news/e027_version_0_15_0.rst517
-rw-r--r--web/news/e028_version_0_15_2.rst77
-rw-r--r--web/news/e029_version_0_16_0.rst222
-rw-r--r--web/news/e030_nim_in_action_in_production.rst53
-rw-r--r--web/news/e031_version_0_16_2.rst325
-rw-r--r--web/question.rst194
-rw-r--r--web/snippets/snippet1.nim4
-rw-r--r--web/sponsors.csv40
-rw-r--r--web/support.rst40
-rw-r--r--web/ticker.html26
-rw-r--r--web/website.ini82
4483 files changed, 334455 insertions, 244154 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..2c9ff103c
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,9 @@
+# Avoids changelog conflicts by assuming additions-only, which is by far the common case.
+# In the rare case where branch b1 rebases against branch b2 and both branches
+# modified the same changelog entry, you'll end up with that changelog entry
+# duplicated, which is easily identifiable and fixable.
+/changelog.md merge=union
+
+# bug https://github.com/dom96/choosenim/issues/256 for WSL CRLF
+*.sh text eol=lf
+/config/build_config.txt text eol=lf
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 000000000..dc771dd31
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,12 @@
+# These are supported funding model platforms
+
+github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: 'araq' # Replace with a single Patreon username
+open_collective: 'nim' # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: 'https://nim-lang.org/donate.html' # Replace with a single custom sponsorship URL
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 000000000..1e46e0544
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,73 @@
+name:        "Bug Report"
+description: "Create a new bug report. Have you found an unexpected behavior? Use this form."
+title:       "Think about the title, twice."
+labels:      ["unconfirmed"]
+body:
+
+- type: markdown
+  attributes:
+    value: |
+      Please provide a minimal code example that reproduces the bug if possible.
+      Reports with a reproducible example or detailed information will likely receive fixes faster.
+
+- type: textarea
+  id: description
+  attributes:
+    label: Description
+    description: |
+      Describe the problem. Code example can be given here.
+    placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
+  validations:
+    required: true
+    
+- type: textarea
+  id: nim-version
+  attributes:
+    label: Nim Version
+    description: |
+      Can be obtained from `nim -v` on the command line along with the OS/architecture.
+      For development versions, make sure to include the commit hash.
+  validations:
+    required: true
+
+- type: textarea
+  id: current-logs
+  attributes:
+    label: Current Output
+    description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
+    placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
+    render: text
+
+- type: textarea
+  id: expected-logs
+  attributes:
+    label: Expected Output
+    description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
+    placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
+    render: text
+
+- type: textarea
+  id: known-workarounds
+  attributes:
+    label: Known Workarounds
+    description: Provide any known workarounds.
+  validations:
+    required: false
+
+- type: textarea
+  id: extra-info
+  attributes:
+    label: Additional Information
+    description: Any additional relevant information.
+  validations:
+    required: false
+
+- type: markdown
+  attributes:
+    value: |
+      - Please check whether the problem still exists in the devel branch, which can be installed via [choosenim](https://github.com/nim-lang/choosenim/), [nightlies](https://github.com/nim-lang/nightlies/), or by [creating a temporary build of the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler-building-an-instrumented-compiler).
+      - Consider writing a PR targetting devel branch after filing this, see [contributing](https://nim-lang.github.io/Nim/contributing.html).
+      - If it's a pre-existing compiler bug, see [debugging the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler) which should give more context on a compiler crash.
+      - If it's a regression, you can help us by identifying which version introduced the bug by [bisecting](https://nim-lang.github.io/Nim/intern.html#bisecting-for-regressions) or at least trying known past releases (e.g. `choosenim 2.0.0`).
+        The Nim repo also supports bisecting in issue comments by adding `!nim c`, `!nim js` etc. before a code block, see [nimrun-action](https://github.com/juancarlospaco/nimrun-action).
+      - [Please, consider a Donation for the Nim project.](https://nim-lang.org/donate.html)
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 000000000..0187064b5
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,10 @@
+contact_links:
+  - name: Forum
+    url: https://forum.nim-lang.org
+    about: Please ask and answer questions here.
+  - name: Discord
+    url: https://discord.gg/nim
+    about: Please ask and answer questions here.
+  - name: Stack Overflow
+    url: https://stackoverflow.com/questions/tagged/nim-lang
+    about: Please ask and answer questions here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 000000000..5cb458626
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,72 @@
+name:        "Feature request"
+description: "Create a new Feature Request. Do you want to suggest a new feature? Use this form."
+title:       "Think about the title, twice."
+labels:      ["unconfirmed"]
+body:
+
+- type: markdown
+  attributes:
+    value: |
+      - Please provide a minimal code example that illustrates the basic idea behind your feature request.
+        Reports with full repro code and descriptive detailed information will likely receive feedback faster.
+      - There is 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 form,
+        but bear in mind that adding new features to the language is currently a low priority.
+
+- type: textarea
+  id: summary
+  attributes:
+    label: Summary
+    description: |
+      Use DETAILED DESCRIPTIVE information about the feature request.
+      Here, you go into more details about your ideas. This section can be a few paragraphs long.
+    placeholder: Short summary of your proposed feature.
+  validations:
+    required: true
+
+- type: textarea
+  id: description
+  attributes:
+    label: Description
+    description: Describe your solution, what problem does it fix?
+  validations:
+    required: true
+
+- type: textarea
+  id: alternatives
+  attributes:
+    label: Alternatives
+    description: Are there any alternatives you've considered?
+  validations:
+    required: false
+
+- type: textarea
+  id: Examples
+  attributes:
+    label: Examples
+    description: Provide examples for your feature request here.
+    placeholder: Example code here.
+
+- type: textarea
+  id: incompatibility
+  attributes:
+    label: Backwards Compatibility
+    description: If your Feature Request introduces backward-incompatible changes, describe them and propose how to deal with them.
+  validations:
+    required: false
+
+- type: textarea
+  id: links
+  attributes:
+    label: Links
+    description: Please copy and paste any relevant links to projects, issues, discussions, technical documentations, code samples, etc.
+  validations:
+    required: false
+
+- type: markdown
+  attributes:
+    value: |
+      - Thanks for your contributions!, your ideas will receive feedback from the community soon...
+      - **Remember to :star: Star the Nim project on GitHub!.**
+      - Consider writing a PR targetting devel branch after filing this, see [contributing](https://nim-lang.github.io/Nim/contributing.html).
+      - [Please, consider a Donation for the Nim project.](https://nim-lang.org/donate.html)
diff --git a/.github/workflows/bisects.yml b/.github/workflows/bisects.yml
new file mode 100644
index 000000000..4a6a92f01
--- /dev/null
+++ b/.github/workflows/bisects.yml
@@ -0,0 +1,31 @@
+# See https://github.com/juancarlospaco/nimrun-action/issues/3#issuecomment-1607344901
+name: issue comments bisects
+on:
+  issue_comment:
+    types: created
+
+jobs:
+  bisects:
+    if: |
+      github.event_name == 'issue_comment' && startsWith(github.event.comment.body, '!nim ') && github.event.issue.pull_request == null && github.event.comment.author_association != 'NONE'
+    strategy:
+      fail-fast: false
+      matrix:
+        platform: [ubuntu-latest, windows-latest, macos-latest]
+    name: ${{ matrix.platform }}-bisects
+    runs-on: ${{ matrix.platform }}
+    steps:
+      - uses: actions/checkout@v4
+
+      - uses: jiro4989/setup-nim-action@v1
+        with:
+          nim-version: 'devel'
+
+      - uses: juancarlospaco/nimrun-action@nim
+        if: |
+          runner.os == 'Linux'   && contains(github.event.comment.body, '-d:linux'  ) ||
+          runner.os == 'Windows' && contains(github.event.comment.body, '-d:windows') ||
+          runner.os == 'macOS'   && contains(github.event.comment.body, '-d:osx'    ) ||
+          runner.os == 'Linux' && !contains(github.event.comment.body, '-d:linux') && !contains(github.event.comment.body, '-d:windows') && !contains(github.event.comment.body, '-d:osx')
+        with:
+          github-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml
new file mode 100644
index 000000000..2faa37ce0
--- /dev/null
+++ b/.github/workflows/ci_docs.yml
@@ -0,0 +1,116 @@
+name: Nim Docs CI
+on:
+  push:
+    paths:
+      - 'compiler/**.nim'
+      - 'config/nimdoc.cfg'
+      - 'doc/**.rst'
+      - 'doc/**.md'
+      - 'doc/nimdoc.css'
+      - 'lib/**.nim'
+      - 'nimdoc/testproject/expected/testproject.html'
+      - 'tools/dochack/dochack.nim'
+      - 'tools/kochdocs.nim'
+      - '.github/workflows/ci_docs.yml'
+      - 'koch.nim'
+
+  pull_request:
+    # Run only on changes on these files.
+    paths:
+      - 'compiler/**.nim'
+      - 'config/nimdoc.cfg'
+      - 'doc/**.rst'
+      - 'doc/**.md'
+      - 'doc/nimdoc.css'
+      - 'lib/**.nim'
+      - 'nimdoc/testproject/expected/testproject.html'
+      - 'tools/dochack/dochack.nim'
+      - 'tools/kochdocs.nim'
+      - '.github/workflows/ci_docs.yml'
+      - 'koch.nim'
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  build:
+    strategy:
+      fail-fast: false
+      matrix:
+        target: [linux, windows, osx]
+        include:
+          - target: linux
+            os: ubuntu-20.04
+          - target: windows
+            os: windows-2019
+          - target: osx
+            os: macos-12
+
+    name: ${{ matrix.target }}
+    runs-on: ${{ matrix.os }}
+    timeout-minutes: 60 # refs bug #18178
+
+    steps:
+      - name: 'Checkout'
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 2
+
+      - name: 'Install build dependencies (macOS)'
+        if: runner.os == 'macOS'
+        run: brew install make
+
+      - name: 'Install build dependencies (Windows)'
+        if: runner.os == 'Windows'
+        shell: bash
+        run: |
+          set -e
+          . ci/funs.sh
+          nimInternalInstallDepsWindows
+          echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
+
+      - name: 'Add build binaries to PATH'
+        shell: bash
+        run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
+
+      - name: 'System information'
+        shell: bash
+        run: . ci/funs.sh && nimCiSystemInfo
+
+      - name: 'Build csourcesAny (posix)'
+        # this would work on windows and other CI use this on windows,
+        # but we ensure here that `ci/build_autogen.bat` keeps working on windows.
+        if: runner.os != 'Windows'
+        shell: bash
+        run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc
+          # was previously using caching via `actions/cache@v1` but this wasn't
+          # used in other CI pipelines and it's unclear the added complexity
+          # was worth the saving; can be revisited if needed.
+
+      - name: 'Build csourcesAny (windows)'
+        if: runner.os == 'Windows'
+        shell: cmd
+        run: ci/build_autogen.bat
+
+      - name: 'Build koch'
+        shell: bash
+        run: nim c koch
+
+      - name: 'Build the real compiler'
+        shell: bash
+        run: ./koch boot -d:release
+
+      - name: 'Build documentation'
+        shell: bash
+        run: ./koch doc --git.commit:devel
+
+      - name: 'Publish documentation to Github Pages'
+        if: |
+          github.event_name == 'push' && github.ref == 'refs/heads/devel' &&
+          matrix.target == 'linux'
+        uses: crazy-max/ghaction-github-pages@v4
+        with:
+          build_dir: doc/html
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/ci_gcc14.yml b/.github/workflows/ci_gcc14.yml
new file mode 100644
index 000000000..fb286887a
--- /dev/null
+++ b/.github/workflows/ci_gcc14.yml
@@ -0,0 +1,76 @@
+name: GCC 14
+on:
+  pull_request:
+  push:
+    branches:
+      - 'devel'
+
+
+jobs:
+  build:
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-24.04]
+        cpu: [amd64]
+    name: '${{ matrix.os }}'
+    runs-on: ${{ matrix.os }}
+    timeout-minutes: 60 # refs bug #18178
+    steps:
+      - name: 'Checkout'
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 2
+
+      - name: 'Install node.js 20.x'
+        uses: actions/setup-node@v4
+        with:
+          node-version: '20.x'
+
+      - name: 'Install dependencies (Linux amd64)'
+        if: runner.os == 'Linux' && matrix.cpu == 'amd64'
+        run: |
+          sudo apt update -qq
+          sudo apt remove needrestart
+          DEBIAN_FRONTEND='noninteractive' \
+            sudo apt install --no-install-recommends -yq \
+              libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
+              valgrind libc6-dbg libblas-dev xorg-dev
+      - name: 'Install dependencies (Linux amd64 gcc 14)'
+        if: runner.os == 'Linux' && matrix.cpu == 'amd64'
+        run: |
+          sudo add-apt-repository universe
+          sudo apt update -qq
+          sudo apt install -y gcc-14 g++-14 libpcre3 liblapack-dev
+          sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 60 --slave /usr/bin/g++ g++ /usr/bin/g++-14
+      - name: 'Install dependencies (macOS)'
+        if: runner.os == 'macOS'
+        run: brew install boehmgc make sfml gtk+3
+      - name: 'Install dependencies (Windows)'
+        if: runner.os == 'Windows'
+        shell: bash
+        run: |
+          set -e
+          . ci/funs.sh
+          nimInternalInstallDepsWindows
+          echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
+
+      - name: 'Add build binaries to PATH'
+        shell: bash
+        run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
+
+      - name: 'NIM_TESTAMENT_DISABLE_SSL'
+        shell: bash
+        run: echo "NIM_TESTAMENT_DISABLE_SSL=1" >> $GITHUB_ENV
+
+      - name: 'System information'
+        shell: bash
+        run: . ci/funs.sh && nimCiSystemInfo
+
+      - name: 'Build csourcesAny'
+        shell: bash
+        run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}'
+
+      - name: 'koch, Run CI'
+        shell: bash
+        run: . ci/funs.sh && nimInternalBuildKochAndRunCI
diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml
new file mode 100644
index 000000000..2ea5092e3
--- /dev/null
+++ b/.github/workflows/ci_packages.yml
@@ -0,0 +1,74 @@
+name: Packages CI
+on:
+  pull_request:
+  push:
+    branches:
+      - 'devel'
+      - 'version-2-0'
+      - 'version-1-6'
+      - 'version-1-2'
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  build:
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-20.04, macos-12]
+        cpu: [amd64]
+        batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
+    name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
+    runs-on: ${{ matrix.os }}
+    timeout-minutes: 60 # refs bug #18178
+    env:
+      NIM_TEST_PACKAGES: "1"
+      NIM_TESTAMENT_BATCH: ${{ matrix.batch }}
+    steps:
+      - name: 'Checkout'
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 2
+
+      - name: 'Install node.js 20.x'
+        uses: actions/setup-node@v4
+        with:
+          node-version: '20.x'
+
+      - name: 'Install dependencies (Linux amd64)'
+        if: runner.os == 'Linux' && matrix.cpu == 'amd64'
+        run: |
+          sudo apt-fast update -qq
+          DEBIAN_FRONTEND='noninteractive' \
+            sudo apt-fast install --no-install-recommends -yq \
+              libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
+              valgrind libc6-dbg libblas-dev xorg-dev
+      - name: 'Install dependencies (macOS)'
+        if: runner.os == 'macOS'
+        run: brew install boehmgc make sfml gtk+3
+      - name: 'Install dependencies (Windows)'
+        if: runner.os == 'Windows'
+        shell: bash
+        run: |
+          set -e
+          . ci/funs.sh
+          nimInternalInstallDepsWindows
+          echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
+
+      - name: 'Add build binaries to PATH'
+        shell: bash
+        run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
+
+      - name: 'System information'
+        shell: bash
+        run: . ci/funs.sh && nimCiSystemInfo
+
+      - name: 'Build csourcesAny'
+        shell: bash
+        run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}'
+
+      - name: 'koch, Run CI'
+        shell: bash
+        run: . ci/funs.sh && nimInternalBuildKochAndRunCI
diff --git a/.github/workflows/ci_publish.yml b/.github/workflows/ci_publish.yml
new file mode 100644
index 000000000..decfe953e
--- /dev/null
+++ b/.github/workflows/ci_publish.yml
@@ -0,0 +1,90 @@
+name: Tracking orc-booting compiler memory usage
+
+on:
+  pull_request_target:
+    types: [closed]
+
+
+jobs:
+  build:
+    if: github.event.pull_request.merged == true
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-20.04]
+        cpu: [amd64]
+    name: '${{ matrix.os }}'
+    runs-on: ${{ matrix.os }}
+    steps:
+      - name: 'Checkout'
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 2
+
+      - name: 'Install node.js 20.x'
+        uses: actions/setup-node@v4
+        with:
+          node-version: '20.x'
+
+      - name: 'Install dependencies (Linux amd64)'
+        if: runner.os == 'Linux' && matrix.cpu == 'amd64'
+        run: |
+          sudo apt-fast update -qq
+          DEBIAN_FRONTEND='noninteractive' \
+            sudo apt-fast install --no-install-recommends -yq \
+              libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
+              valgrind libc6-dbg libblas-dev xorg-dev
+      - name: 'Install dependencies (macOS)'
+        if: runner.os == 'macOS'
+        run: brew install boehmgc make sfml gtk+3
+      - name: 'Install dependencies (Windows)'
+        if: runner.os == 'Windows'
+        shell: bash
+        run: |
+          set -e
+          . ci/funs.sh
+          nimInternalInstallDepsWindows
+          echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
+
+      - name: 'Add build binaries to PATH'
+        shell: bash
+        run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
+
+      - name: 'System information'
+        shell: bash
+        run: . ci/funs.sh && nimCiSystemInfo
+
+      - name: 'Build csourcesAny'
+        shell: bash
+        run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}'
+
+      - name: 'Build koch'
+        shell: bash
+        run: nim c koch
+
+      - name: 'Build Nim'
+        shell: bash
+        run: ./koch boot -d:release -d:nimStrictMode --lib:lib
+
+      - name: 'Action'
+        shell: bash
+        run: nim c -r -d:release ci/action.nim
+
+      - name: 'Comment'
+        uses: actions/github-script@v7
+        with:
+          script: |
+            const fs = require('fs');
+
+            try {
+              const data = fs.readFileSync('ci/nimcache/results.txt', 'utf8');
+              github.rest.issues.createComment({
+              issue_number: context.issue.number,
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              body: data
+            })
+            } catch (err) {
+              console.error(err);
+            }
+
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 000000000..0c5a533e1
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,25 @@
+# https://github.com/actions/stale#usage
+name: Stale pull requests
+
+on:
+  schedule:
+    - cron: '0 0 * * *'  # Midnight.
+
+jobs:
+  stale:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/stale@v9
+        with:
+          days-before-pr-stale:    365
+          days-before-pr-close:    30
+          days-before-issue-stale: -1
+          days-before-issue-close: -1
+          exempt-pr-labels:    "ARC,bounty,Codegen,Crash,Generics,High Priority,Macros,Next release,Showstopper,Static[T]"
+          exempt-issue-labels: "Showstopper,Severe,bounty,Compiler Crash,Medium Priority"
+          stale-pr-message: >
+            This pull request is stale because it has been open for 1 year with no activity.
+            Contribute more commits on the pull request and rebase it on the latest devel,
+            or it will be closed in 30 days. Thank you for your contributions.
+          close-pr-message: >
+            This pull request has been marked as stale and closed due to inactivity after 395 days.
diff --git a/.gitignore b/.gitignore
index fc090130b..fad7909bd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,15 +3,17 @@
 !*.*
 
 # Cache
-nimcache/
-rnimcache/
-dnimcache/
+nimcache*/
+rnimcache*/
+dnimcache*/
 
 *.o
 !/icons/*.o
 *.obj
 *.ilk
+*.exp
 *.pdb
+*.lib
 *.dll
 *.exe
 *.so
@@ -19,6 +21,7 @@ dnimcache/
 *.zip
 *.iss
 *.log
+*.pdb
 
 mapping.txt
 tags
@@ -27,10 +30,10 @@ deinstall.sh
 
 doc/html/
 doc/*.html
-doc/*.pdf
+doc/pdf
 doc/*.idx
 /web/upload
-build/*
+/build/*
 bin/*
 
 # iOS specific wildcards.
@@ -40,24 +43,72 @@ bin/*
 *.perspectivev3
 *.swp
 .DS_Store
+.tags
 project.xcworkspace/
 xcuserdata/
 
 # Generated files.
 /compile.json
-/compiler/nimrod.dot
+/compiler/nim.dot
 /reject.json
 /run.json
-/testresults.html
+/tools/dochack/dochack.js
+*.json
+/pkgstemp/**/*
+# for `nim doc foo.nim`
+/*.html
+lib/**/*.html
+#/testresults.html #covered by /*.html
+
 /testresults.json
 testament.db
+/tests/**/*.json
+/tests/**/*.js
+
 /csources
-dist/
+/csources_v1
+/csources_v2
+
+/dist/
+# /lib/fusion # fusion is now unbundled; `git status` should reveal if it's there so users can act on it
 
 # Private directories and files (IDEs)
 .*/
 ~*
 
-# testament cruft
+# testament cruft; TODO: generate these in a gitignore'd dir (./build) in the first place.
 testresults/
 test.txt
+/test.ini
+
+tweeter.db
+tweeter_test.db
+
+/tests/megatest.nim
+/tests/ic/*_temp.nim
+/tests/navigator/*_temp.nim
+
+
+/outputExpected.txt
+/outputGotten.txt
+/t15148.txt
+/tests/vm/tfile_rw.txt
+
+/lib/pure/*.js
+
+!/.builds/
+
+!/.github
+
+# ignore debug dirs generated by dsymutil on OSX
+*.dSYM
+
+# for `nim c -r nimdoc/tester` etc; this can be in multiple places
+htmldocs
+
+## these are not needed anymore unless checkout old older versions
+nimdoc.out.css
+# except here:
+!/nimdoc/testproject/expected/*
+pkgs/
+/compiler/compiler/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index c37b4c8d4..000000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,61 +0,0 @@
-image: ubuntu:16.04
-
-stages:
-  - pre-build
-  - build
-  - deploy
-  - test
-
-.linux_set_path: &linux_set_path_def
-  before_script:
-    - export PATH=$(pwd)/bin${PATH:+:$PATH}
-  tags:
-    - linux
-
-.windows_set_path: &win_set_path_def
-  before_script:
-    - set PATH=%CD%\bin;%PATH%
-  tags:
-    - windows
-
-
-build-windows:
-  stage: build
-  script:
-    - ci\build.bat
-  artifacts:
-    paths:
-      - bin\nim.exe
-      - bin\nimd.exe
-      - compiler\nim.exe
-      - koch.exe
-    expire_in: 1 week
-  tags:
-    - windows
-
-deploy-windows:
-  stage: deploy
-  script:
-    - koch.exe winrelease
-  artifacts:
-    paths:
-      - build/*.exe
-      - build/*.zip
-    expire_in: 1 week
-  tags:
-    - windows
-    - fast
-
-
-
-test-windows:
-  stage: test
-  <<: *win_set_path_def
-  script:
-    - call ci\deps.bat
-    - nim c --taintMode:on tests\testament\tester
-    - tests\testament\tester.exe --pedantic all
-  tags:
-    - windows
-    - fast
-
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 5a091d0c7..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,55 +0,0 @@
-sudo: false
-language: c
-os:
-  - linux
-  - osx
-dist: trusty
-
-
-matrix:
-  allow_failures:
-    - os: osx
-
-addons:
-  apt:
-    packages:
-    - libcurl4-openssl-dev
-    - libsdl1.2-dev
-    - libgc-dev
-    - libsfml-dev
-
-before_install:
-  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi
-  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install boehmgc; fi
-  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install sfml; fi
-
-before_script:
-  - set -e
-  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then unset -f cd; fi
-  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then shell_session_update() { :; }; fi
-  - git clone --depth 1 https://github.com/nim-lang/csources.git
-  - cd csources
-  - sh build.sh
-  - cd ..
-  - export PATH=$(pwd)/bin${PATH:+:$PATH}
-script:
-  - nim c koch
-  - ./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
-  - nimble install niminst
-  - nim c --taintMode:on -d:nimCoroutines tests/testament/tester
-  - tests/testament/tester --pedantic all -d:nimCoroutines
-  - nim c -o:bin/nimpretty nimpretty/nimpretty.nim
-  - nim c -r nimpretty/tester.nim
-  - ./koch web
-  - ./koch csource
-  - ./koch nimsuggest
-#  - nim c -r nimsuggest/tester
-  - ( ! grep -F '.. code-block' -l -r --include '*.html' --exclude contributing.html --exclude docgen.html --exclude tut2.html )
-  - ( ! grep -F '..code-block' -l -r --include '*.html' --exclude contributing.html --exclude docgen.html --exclude tut2.html )
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index daa1d4e48..000000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,64 +0,0 @@
-version: '{build}'
-
-cache:
-- x86_64-4.9.2-release-win32-seh-rt_v4-rev4.7z
-- sqlite-dll-win64-x64-3160200.zip
-# - i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z
-
-matrix:
-  fast_finish: true
-
-environment:
-  matrix:
-    - MINGW_DIR: mingw64
-      MINGW_URL: https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.9.2/threads-win32/seh/x86_64-4.9.2-release-win32-seh-rt_v4-rev4.7z/download
-      MINGW_ARCHIVE: x86_64-4.9.2-release-win32-seh-rt_v4-rev4.7z
-      SQLITE_URL: http://www.sqlite.org/2017/sqlite-dll-win64-x64-3160200.zip
-      SQLITE_ARCHIVE: sqlite-dll-win64-x64-3160200.zip
-      platform: x64
-    # - MINGW_DIR: mingw32
-    #   MINGW_URL: https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/4.9.2/threads-win32/dwarf/i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z/download
-    #   MINGW_ARCHIVE: i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z
-    #   SQLITE_URL: http://www.sqlite.org/2017/sqlite-dll-win32-x86-3160200.zip
-    #   SQLITE_ARCHIVE: sqlite-dll-win32-x86-3160200.zip
-    #   platform: x86
-
-install:
-  - ps: Install-Product node 8 # node 8 or later is required to test js async stuff
-  - MKDIR %CD%\DIST
-  - MKDIR %CD%\DIST\PCRE
-  - nuget install pcre -Verbosity quiet -Version 8.33.0.1 -OutputDirectory %CD%\DIST\PCRE
-  - IF not exist "%SQLITE_ARCHIVE%" appveyor DownloadFile "%SQLITE_URL%" -FileName "%SQLITE_ARCHIVE%"
-  - 7z x -y "%SQLITE_ARCHIVE%" -o"%CD%\DIST"> nul
-  - IF not exist "%MINGW_ARCHIVE%" appveyor DownloadFile "%MINGW_URL%" -FileName "%MINGW_ARCHIVE%"
-  - 7z x -y "%MINGW_ARCHIVE%" -o"%CD%\DIST"> nul
-  - SET PATH=%CD%\DIST\%MINGW_DIR%\BIN;%CD%\BIN;%PATH%
-  - IF "%PLATFORM%" == "x64" ( copy C:\OpenSSL-Win64\libeay32.dll %CD%\BIN\libeay64.dll & copy C:\OpenSSL-Win64\libeay32.dll %CD%\BIN\libeay32.dll & copy C:\OpenSSL-Win64\libssl32.dll %CD%\BIN\libssl64.dll & copy C:\OpenSSL-Win64\libssl32.dll %CD%\BIN\libssl32.dll )
-    ELSE ( copy C:\OpenSSL-Win32\libeay32.dll %CD%\BIN\libeay32.dll & copy C:\OpenSSL-Win32\libssl32.dll %CD%\BIN\libssl32.dll )
-  - IF "%PLATFORM%" == "x64" ( copy %CD%\DIST\sqlite3.dll %CD%\BIN\sqlite3_64.dll ) ELSE ( copy %CD%\DIST\sqlite3.dll %CD%\BIN\sqlite3_32.dll )
-  - IF "%PLATFORM%" == "x64" ( copy %CD%\DIST\PCRE\pcre.redist.8.33.0.1\build\native\bin\v100\x64\Release\dynamic\utf8\pcre8.dll %CD%\bin\pcre64.dll ) ELSE ( copy %CD%\DIST\PCRE\pcre.redist.8.33.0.1\build\native\bin\v100\Win32\Release\dynamic\utf8\pcre8.dll %CD%\bin\pcre32.dll )
-  - git clone --depth 1 https://github.com/nim-lang/csources
-  - cd csources
-  - IF "%PLATFORM%" == "x64" ( build64.bat ) else ( build.bat )
-  - cd ..
-
-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
-  - nimble install sdl1
-  - nimble install jester@#head
-  - nimble install niminst
-  - nim c --taintMode:on -d:nimCoroutines tests/testament/tester
-
-test_script:
-  - tests\testament\tester --pedantic all -d:nimCoroutines
-#  - koch csource
-#  - koch zip
-
-deploy: off
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
new file mode 100644
index 000000000..9ef202948
--- /dev/null
+++ b/azure-pipelines.yml
@@ -0,0 +1,168 @@
+trigger:
+  branches:
+    include:
+    - 'devel'
+    - 'version-*'
+pr:
+  branches:
+    include:
+    - '*'
+
+variables:
+- name: skipci
+  value: false 
+
+jobs:
+- job: packages
+
+  timeoutInMinutes: 90 # default `60` led to lots of cancelled jobs; use 0 for unlimited (may be undesirable)
+
+  strategy:
+    matrix:
+      Linux_amd64:
+        vmImage: 'ubuntu-20.04'
+        CPU: amd64
+      # regularly breaks, refs bug #17325
+#       Linux_i386:
+#         # on 'ubuntu-16.04' (not supported anymore anyways) it errored with:
+#         # g++-multilib : Depends: gcc-multilib (>= 4:5.3.1-1ubuntu1) but it is not going to be installed
+#         vmImage: 'ubuntu-18.04'
+#         CPU: i386
+      OSX_amd64:
+        vmImage: 'macOS-12'
+        CPU: amd64
+      OSX_amd64_cpp:
+        vmImage: 'macOS-12'
+        CPU: amd64
+        NIM_COMPILE_TO_CPP: true
+      Windows_amd64_batch0_3:
+        vmImage: 'windows-2019'
+        CPU: amd64
+        # see also: `NIM_TEST_PACKAGES`
+        NIM_TESTAMENT_BATCH: "0_3"
+      Windows_amd64_batch1_3:
+        vmImage: 'windows-2019'
+        CPU: amd64
+        NIM_TESTAMENT_BATCH: "1_3"
+      Windows_amd64_batch2_3:
+        vmImage: 'windows-2019'
+        CPU: amd64
+        NIM_TESTAMENT_BATCH: "2_3"
+
+  pool:
+    vmImage: $(vmImage)
+
+  workspace:
+    clean: all
+
+  steps:
+    - bash: git config --global core.autocrlf false
+      displayName: 'Disable auto conversion to CRLF by git (Windows-only)'
+      condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
+
+    - checkout: self
+      fetchDepth: 2 # see D20210329T004830
+
+    - bash: |
+        set -e
+        . ci/funs.sh
+        if nimIsCiSkip; then
+          echo '##vso[task.setvariable variable=skipci]true'
+        fi
+      displayName: 'Check whether to skip CI'
+
+    - task: NodeTool@0
+      inputs:
+        versionSpec: '20.x'
+      displayName: 'Install node.js 20.x'
+      condition: and(succeeded(), eq(variables['skipci'], 'false'))
+
+    - bash: |
+        set -e
+        . ci/funs.sh
+        echo_run sudo apt-fast update -qq
+        DEBIAN_FRONTEND='noninteractive' \
+          echo_run sudo apt-fast install --no-install-recommends -yq \
+            libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg
+      displayName: 'Install dependencies (amd64 Linux)'
+      condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'amd64'))
+
+    - bash: |
+        set -e
+        . ci/funs.sh
+        echo_run sudo dpkg --add-architecture i386
+        # Downgrade llvm:
+        # - llvm has to be downgraded to have 32bit version installed for sfml.
+
+        cat << EOF | sudo tee /etc/apt/preferences.d/pin-to-rel
+        Package: libllvm6.0
+        Pin: origin "azure.archive.ubuntu.com"
+        Pin-Priority: 1001
+        EOF
+
+        # echo_run sudo apt-fast update -qq
+        echo_run sudo apt-fast update -qq || echo "failed, see bug #17343"
+        # `:i386` (e.g. in `libffi-dev:i386`) is needed otherwise you may get:
+        # `could not load: libffi.so` during dynamic loading.
+        DEBIAN_FRONTEND='noninteractive' \
+          echo_run sudo apt-fast install --no-install-recommends --allow-downgrades -yq \
+            g++-multilib gcc-multilib libcurl4-openssl-dev:i386 libgc-dev:i386 \
+            libsdl1.2-dev:i386 libsfml-dev:i386 libglib2.0-dev:i386 libffi-dev:i386
+
+        cat << EOF > bin/gcc
+        #!/bin/bash
+
+        exec $(which gcc) -m32 "\$@"
+        EOF
+
+        cat << EOF > bin/g++
+        #!/bin/bash
+
+        exec $(which g++) -m32 "\$@"
+        EOF
+
+        echo_run chmod 755 bin/gcc
+        echo_run chmod 755 bin/g++
+
+      displayName: 'Install dependencies (i386 Linux)'
+      condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'i386'))
+
+    - bash: brew install boehmgc make sfml
+      displayName: 'Install dependencies (OSX)'
+      condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Darwin'))
+
+    - bash: |
+        set -e
+        . ci/funs.sh
+        nimInternalInstallDepsWindows
+        echo_run echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/dist/mingw64/bin'
+
+      displayName: 'Install dependencies (Windows)'
+      condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Windows_NT'))
+
+    - bash: echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/bin'
+      condition: and(succeeded(), eq(variables['skipci'], 'false'))
+      displayName: 'Add build binaries to PATH'
+
+    - bash: . ci/funs.sh && nimCiSystemInfo
+      condition: and(succeeded(), eq(variables['skipci'], 'false'))
+      displayName: 'System information'
+
+    - bash: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu=$(CPU)
+      condition: and(succeeded(), eq(variables['skipci'], 'false'))
+      displayName: 'Build csourcesAny'
+
+    # this could be revived if performance justifies it (needs a few updates)
+    # - task: Cache@2
+    #   inputs:
+    #     key: 'csourcesAny | "$(Agent.OS)" | $(CPU) | $(csources_version)'
+    #     path: $(nim_csources)
+    #   condition: and(succeeded(), eq(variables['skipci'], 'false'))
+    #   displayName: 'Restore built csourcesAny'
+
+    - bash: . ci/funs.sh && nimInternalBuildKochAndRunCI
+      # would could also show on failure: echo '##vso[task.complete result=Failed]'
+      condition: and(succeeded(), eq(variables['skipci'], 'false'))
+      displayName: 'koch, Run CI'
+      env:
+        SYSTEM_ACCESSTOKEN: $(System.AccessToken)
diff --git a/bin/nim-gdb b/bin/nim-gdb
new file mode 100755
index 000000000..d4d60e432
--- /dev/null
+++ b/bin/nim-gdb
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+
+# Exit if anything fails
+set -e
+
+which nim > /dev/null || (echo "nim not in PATH"; exit 1)
+which gdb > /dev/null || (echo "gdb not in PATH"; exit 1)
+which readlink > /dev/null || (echo "readlink not in PATH."; exit 1)
+
+if [[ $(uname -s) == Darwin || $(uname -s) == *BSD ]]; then
+  NIM_SYSROOT=$(dirname $(dirname $(readlink -f $(which nim))))
+else
+  NIM_SYSROOT=$(dirname $(dirname $(readlink -e $(which nim))))
+fi
+
+# Find out where the pretty printer Python module is
+GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/debug/nim-gdb.py"
+
+# Run GDB with the additional arguments that load the pretty printers
+# Set the environment variable `NIM_GDB` to overwrite the call to a
+# different/specific command (defaults to `gdb`).
+NIM_GDB="${NIM_GDB:-gdb}"
+# exec replaces the new process of bash with gdb. It is always good to
+# have fewer processes.
+exec "${NIM_GDB}" -eval-command="source $GDB_PYTHON_MODULE_PATH" "$@"
diff --git a/bin/nim-gdb.bat b/bin/nim-gdb.bat
new file mode 100644
index 000000000..b5537dd9d
--- /dev/null
+++ b/bin/nim-gdb.bat
@@ -0,0 +1,14 @@
+@echo off
+for %%i in (nim.exe) do (set NIM_BIN=%%~dp$PATH:i)
+
+for %%i in ("%NIM_BIN%\..\") do (set NIM_ROOT=%%~fi)
+
+set @GDB_PYTHON_MODULE_PATH=%NIM_ROOT%\tools\debug\nim-gdb.py
+set @NIM_GDB=gdb.exe
+
+@echo source %@GDB_PYTHON_MODULE_PATH%> wingdbcommand.txt
+%@NIM_GDB% --command="wingdbcommand.txt" %*
+del wingdbcommand.txt /f /q
+
+EXIT /B %ERRORLEVEL%
+@echo on
diff --git a/build_all.bat b/build_all.bat
new file mode 100644
index 000000000..ef4a5f6a4
--- /dev/null
+++ b/build_all.bat
@@ -0,0 +1,29 @@
+@echo off
+rem DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
+rem Build development version of the compiler; can be rerun safely
+rem bare bones version of ci/funs.sh adapted for windows.
+
+rem Read in some common shared variables (shared with other tools),
+rem see https://stackoverflow.com/questions/3068929/how-to-read-file-contents-into-a-variable-in-a-batch-file
+for /f "delims== tokens=1,2" %%G in (config/build_config.txt) do set %%G=%%H
+SET nim_csources=bin\nim_csources_%nim_csourcesHash%.exe
+echo "building from csources: %nim_csources%"
+
+if not exist %nim_csourcesDir% (
+  git clone -q --depth 1 -b %nim_csourcesBranch% %nim_csourcesUrl% %nim_csourcesDir%
+)
+
+if not exist %nim_csources% (
+  cd %nim_csourcesDir%
+  git checkout %nim_csourcesHash%
+  echo "%PROCESSOR_ARCHITECTURE%"
+  if "%PROCESSOR_ARCHITECTURE%"=="AMD64" (
+    SET ARCH=64
+  )
+  CALL build.bat
+  cd ..
+  copy /y bin\nim.exe  %nim_csources%
+)
+ bin\nim.exe c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
+ koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
+ koch tools --skipUserCfg --skipParentCfg --hints:off
diff --git a/build_all.sh b/build_all.sh
new file mode 100755
index 000000000..83848f41a
--- /dev/null
+++ b/build_all.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+# DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
+
+# build development version of the compiler; can be rerun safely.
+# arguments can be passed, e.g.:
+# CC=gcc ucpu=amd64 uos=darwin
+
+set -u # error on undefined variables
+set -e # exit on first error
+
+. ci/funs.sh
+nimBuildCsourcesIfNeeded "$@"
+
+echo_run bin/nim c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
+echo_run ./koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
+echo_run ./koch tools --skipUserCfg --skipParentCfg --hints:off
+
diff --git a/changelog.md b/changelog.md
index 4a490dfdd..8acd2120a 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,176 +1,18 @@
-## v0.19.X - XX/XX/2018
+# v2.x.x - yyyy-mm-dd
 
-### Changes affecting backwards compatibility
 
-- The stdlib module ``future`` has been renamed to ``sugar``.
-- ``macros.callsite`` is now deprecated. Since the introduction of ``varargs``
-  parameters this became unnecessary.
-- Anonymous tuples with a single element can now be written as ``(1,)`` with a
-  trailing comma. The underlying AST is ``nnkTupleConst(newLit 1)`` for this
-  example. ``nnkTupleConstr`` is a new node kind your macros need to be able
-  to deal with!
-- Indexing into a ``cstring`` for the JS target is now mapped
-  to ``charCodeAt``.
-- Assignments that would "slice" an object into its supertype are now prevented
-  at runtime. Use ``ref object`` with inheritance rather than ``object`` with
-  inheritance to prevent this issue.
-- The ``not nil`` type annotation now has to be enabled explicitly
-  via ``{.experimental: "notnil"}`` as we are still not pleased with how this
-  feature works with Nim's containers.
-- The parser now warns about inconsistent spacing around binary operators as
-  these can easily be confused with unary operators. This warning will likely
-  become an error in the future.
+## Changes affecting backward compatibility
 
 
-#### Breaking changes in the standard library
+## Standard library additions and changes
 
-- ``re.split`` for empty regular expressions now yields every character in
-  the string which is what other programming languages chose to do.
-- The returned tuple of ``system.instantiationInfo`` now has a third field
-  containing the column of the instantiation.
 
-- ``cookies.setCookie` no longer assumes UTC for the expiration date.
-- ``strutils.formatEng`` does not distinguish between ``nil`` and ``""``
-  strings anymore for its ``unit`` parameter. Instead the space is controlled
-  by a new parameter ``useUnitSpace``.
+## Language changes
 
-- ``proc `-`*(a, b: Time): int64`` in the ``times`` module has changed return type
-  to ``times.Duration`` in order to support higher time resolutions.
-  The proc is no longer deprecated.
-- ``posix.Timeval.tv_sec`` has changed type to ``posix.Time``.
 
-- ``math.`mod` `` for floats now behaves the same as ``mod`` for integers
-  (previously it used floor division like Python). Use ``math.floorMod`` for the old behavior.
+## Compiler changes
 
-- For string inputs, ``unicode.isUpper`` and ``unicode.isLower`` now require a
-  second mandatory parameter ``skipNonAlpha``.
 
-- For string inputs, ``strutils.isUpperAscii`` and ``strutils.isLowerAscii`` now
-  require a second mandatory parameter ``skipNonAlpha``.
+## Tool changes
 
-- The procs ``parseHexInt`` and ``parseOctInt`` now fail on empty strings
-    and strings containing only valid prefixes, e.g. "0x" for hex integers.
 
-
-#### Breaking changes in the compiler
-
-- The undocumented ``#? braces`` parsing mode was removed.
-- The undocumented PHP backend was removed.
-
-### Library additions
-
-- ``re.split`` now also supports the ``maxsplit`` parameter for consistency
-  with ``strutils.split``.
-- Added ``system.toOpenArray`` in order to support zero-copy slicing
-  operations. This is currently not yet available for the JavaScript target.
-- Added ``getCurrentDir``, ``findExe``, ``cpDir`` and  ``mvDir`` procs to
-  ``nimscript``.
-- The ``times`` module now supports up to nanosecond time resolution when available.
-- Added the type ``times.Duration`` for representing fixed durations of time.
-- Added the proc ``times.convert`` for converting between different time units,
-  e.g days to seconds.
-- Added the proc ``algorithm.binarySearch[T, K]`` with the ```cmp``` parameter.
-- Added the proc ``algorithm.upperBound``.
-- Added inverse hyperbolic functions, ``math.arcsinh``, ``math.arccosh`` and ``math.arctanh`` procs.
-- Added cotangent, secant and cosecant procs ``math.cot``, ``math.sec`` and ``math.csc``; and their hyperbolic, inverse and inverse hyperbolic functions, ``math.coth``, ``math.sech``, ``math.csch``, ``math.arccot``, ``math.arcsec``, ``math.arccsc``, ``math.arccoth``, ``math.arcsech`` and ``math.arccsch`` procs.
-- Added the procs ``math.floorMod`` and ``math.floorDiv`` for floor based integer division.
-- Added the procs ``rationals.`div```, ``rationals.`mod```, ``rationals.floorDiv`` and ``rationals.floorMod`` for rationals.
-- Added the proc ``math.prod`` for product of elements in openArray.
-- Added the proc ``parseBinInt`` to parse a binary integer from a string, which returns the value.
-- ``parseOct`` and ``parseBin`` in parseutils now also support the ``maxLen`` argument similar to ``parseHexInt``
-- Added the proc ``flush`` for memory mapped files.
-- Added the ``MemMapFileStream``.
-
-### Library changes
-
-- ``macros.astGenRepr``, ``macros.lispRepr`` and ``macros.treeRepr``
-  now escapes the content of string literals consistently.
-- ``macros.NimSym`` and ``macros.NimIdent`` is now deprecated in favor
-  of the more general ``NimNode``.
-- ``macros.getImpl`` now includes the pragmas of types, instead of omitting them.
-- ``macros.hasCustomPragma`` and ``macros.getCustomPragmaVal`` now
-  also support ``ref`` and ``ptr`` types, pragmas on types and variant
-  fields.
-- ``system.SomeReal`` is now called ``SomeFloat`` for consistency and
-  correctness.
-- ``algorithm.smartBinarySearch`` and ``algorithm.binarySearch`` is
-  now joined in ``binarySearch``. ``smartbinarySearch`` is now
-  deprecated.
-- The `terminal` module now exports additional procs for generating ANSI color
-  codes as strings.
-- Added the parameter ``val`` for the ``CritBitTree[int].inc`` proc.
-- An exception raised from a ``test`` block of ``unittest`` now shows its type in
-  error message.
-- The ``compiler/nimeval`` API was rewritten to simplify the "compiler as an
-  API". Using the Nim compiler and its VM as a scripting engine has never been
-  easier. See ``tests/compilerapi/tcompilerapi.nim`` for an example of how to
-  use the Nim VM in a native Nim application.
-- Added the parameter ``val`` for the ``CritBitTree[T].incl`` proc.
-- The proc ``tgamma`` was renamed to ``gamma``. ``tgamma`` is deprecated.
-- The ``pegs`` module now exports getters for the fields of its ``Peg`` and ``NonTerminal``
-  object types. ``Peg``s with child nodes now have the standard ``items`` and ``pairs``
-  iterators.
-
-### Language additions
-
-- Dot calls combined with explicit generic instantiations can now be written
-  as ``x.y[:z]`` which is transformed into ``y[z](x)`` by the parser.
-- ``func`` is now an alias for ``proc {.noSideEffect.}``.
-- In order to make ``for`` loops and iterators more flexible to use Nim now
-  supports so called "for-loop macros". See
-  the `manual <manual.html#macros-for-loop-macros>`_ for more details.
-
-### Language changes
-
-- The `importcpp` pragma now allows importing the listed fields of generic
-  C++ types. Support for numeric parameters have also been added through
-  the use of `static[T]` types.
-  (#6415)
-
-- Native C++ exceptions can now be imported with `importcpp` pragma.
-  Imported exceptions can be raised and caught just like Nim exceptions.
-  More details in language manual.
-
-- ``nil`` for strings/seqs is finally gone. Instead the default value for
-  these is ``"" / @[]``.
-
-- Accessing the binary zero terminator in Nim's native strings
-  is now invalid. Internally a Nim string still has the trailing zero for
-  zero-copy interoperability with ``cstring``. Compile your code with the
-  new switch ``--laxStrings:on`` if you need a transition period.
-
-- The command syntax now supports keyword arguments after the first comma.
-
-- Thread-local variables can now be declared inside procs. This implies all
-  the effects of the `global` pragma.
-
-- Nim now supports `except` clause in the export statement.
-
-### Tool changes
-
-- ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed
-  ``doc``. The old ``jsondoc`` can still be invoked with ``jsondoc0``.
-
-### Compiler changes
-
-- The VM's instruction count limit was raised to 1 billion instructions in
-  order to support more complex computations at compile-time.
-
-- Support for hot code reloading has been implemented for the JavaScript
-  target. To use it, compile your code with `--hotCodeReloading:on` and use a
-  helper library such as LiveReload or BrowserSync.
-
-- A new compiler option `--cppCompileToNamespace` puts the generated C++ code
-  into the namespace "Nim" in order to avoid naming conflicts with existing
-  C++ code. This is done for all Nim code - internal and exported.
-
-- Added ``macros.getProjectPath`` and ``ospaths.putEnv`` procs to Nim's virtual
-  machine.
-
-- The ``deadCodeElim`` option is now always turned on and the switch has no
-  effect anymore, but is recognized for backwards compatibility.
-
-- ``experimental`` is now a pragma / command line switch that can enable specific
-  language extensions, it is not an all-or-nothing switch anymore.
-
-### Bugfixes
diff --git a/changelogs/changelog_0_18_1.md b/changelogs/changelog_0_18_1.md
new file mode 100644
index 000000000..d0b7fd8ee
--- /dev/null
+++ b/changelogs/changelog_0_18_1.md
@@ -0,0 +1,663 @@
+## v0.18.0 - 01/03/2018
+
+### Changes affecting backwards compatibility
+
+#### Breaking changes in the standard library
+
+- The ``[]`` proc for strings now raises an ``IndexError`` exception when
+  the specified slice is out of bounds. See issue
+  [#6223](https://github.com/nim-lang/Nim/issues/6223) for more details.
+  You can use ``substr(str, start, finish)`` to get the old behaviour back,
+  see [this commit](https://github.com/nim-lang/nimbot/commit/98cc031a27ea89947daa7f0bb536bcf86462941f) for an example.
+
+- ``strutils.split`` and ``strutils.rsplit`` with an empty string and a
+  separator now returns that empty string.
+  See issue [#4377](https://github.com/nim-lang/Nim/issues/4377).
+
+- Arrays of char cannot be converted to ``cstring`` anymore, pointers to
+  arrays of char can! This means ``$`` for arrays can finally exist
+  in ``system.nim`` and do the right thing. This means ``$myArrayOfChar`` changed
+  its behaviour! Compile with ``-d:nimNoArrayToString`` to see where to fix your
+  code.
+
+- `reExtended` is no longer default for the `re` constructor in the `re`
+  module.
+
+- The behavior of ``$`` has been changed for all standard library collections. The
+  collection-to-string implementations now perform proper quoting and escaping of
+  strings and chars.
+
+- `newAsyncSocket` taking an `AsyncFD` now runs `setBlocking(false)` on the
+  fd.
+
+- ``mod`` and bitwise ``and`` do not produce ``range`` subtypes anymore. This
+  turned out to be more harmful than helpful and the language is simpler
+  without this special typing rule.
+
+- ``formatFloat``/``formatBiggestFloat`` now support formatting floats with zero
+  precision digits. The previous ``precision = 0`` behavior (default formatting)
+  is now available via ``precision = -1``.
+
+- Moved from stdlib into Nimble packages:
+  - [``basic2d``](https://github.com/nim-lang/basic2d)
+    _deprecated: use ``glm``, ``arraymancer``, ``neo``, or another package instead_
+  - [``basic3d``](https://github.com/nim-lang/basic3d)
+    _deprecated: use ``glm``, ``arraymancer``, ``neo``, or another package instead_
+  - [``gentabs``](https://github.com/lcrees/gentabs)
+  - [``libuv``](https://github.com/lcrees/libuv)
+  - [``numeric``](https://github.com/lcrees/polynumeric)
+  - [``poly``](https://github.com/lcrees/polynumeric)
+  - [``pdcurses``](https://github.com/lcrees/pdcurses)
+  - [``romans``](https://github.com/lcrees/romans)
+  - [``libsvm``](https://github.com/nim-lang/libsvm_legacy)
+  - [``joyent_http_parser``](https://github.com/nim-lang/joyent_http_parser)
+
+- Proc [toCountTable](https://nim-lang.org/docs/tables.html#toCountTable,openArray[A])
+  now produces a `CountTable` with values correspoding to the number of occurrences
+  of the key in the input. It used to produce a table with all values set to `1`.
+
+  Counting occurrences in a sequence used to be:
+
+  ```nim
+  let mySeq = @[1, 2, 1, 3, 1, 4]
+  var myCounter = initCountTable[int]()
+
+  for item in mySeq:
+    myCounter.inc item
+  ```
+
+  Now, you can simply do:
+
+  ```nim
+  let
+    mySeq = @[1, 2, 1, 3, 1, 4]
+    myCounter = mySeq.toCountTable()
+  ```
+
+- If you use ``--dynlibOverride:ssl`` with OpenSSL 1.0.x, you now have to
+  define ``openssl10`` symbol (``-d:openssl10``). By default OpenSSL 1.1.x is
+  assumed.
+
+- ``newNativeSocket`` is now named ``createNativeSocket``.
+
+- ``newAsyncNativeSocket`` is now named ``createAsyncNativeSocket``
+  and it no longer raises an OS error but returns an ``osInvalidSocket`` when
+  creation fails.
+
+- The ``securehash`` module is now deprecated. Instead import ``std / sha1``.
+
+- The ``readPasswordFromStdin`` proc has been moved from the ``rdstdin``
+  to the ``terminal`` module, thus it does not depend on linenoise anymore.
+
+#### Breaking changes in the compiler
+
+- ``\n`` is now only the single line feed character like in most
+  other programming languages. The new platform specific newline escape sequence is
+  written as ``\p``. This change only affects the Windows platform.
+
+- The overloading rules changed slightly so that constrained generics are
+  preferred over unconstrained generics. (Bug #6526)
+
+- We changed how array accesses "from backwards" like ``a[^1]`` or ``a[0..^1]`` are
+  implemented. These are now implemented purely in ``system.nim`` without compiler
+  support. There is a new "heterogeneous" slice type ``system.HSlice`` that takes 2
+  generic parameters which can be ``BackwardsIndex`` indices. ``BackwardsIndex`` is
+  produced by ``system.^``.
+  This means if you overload ``[]`` or ``[]=`` you need to ensure they also work
+  with ``system.BackwardsIndex`` (if applicable for the accessors).
+
+- The parsing rules of ``if`` expressions were changed so that multiple
+  statements are allowed in the branches. We found few code examples that
+  now fail because of this change, but here is one:
+
+```nim
+t[ti] = if exp_negative: '-' else: '+'; inc(ti)
+```
+
+This now needs to be written as:
+
+```nim
+t[ti] = (if exp_negative: '-' else: '+'); inc(ti)
+```
+
+- The experimental overloading of the dot ``.`` operators now take
+  an ``untyped``` parameter as the field name, it used to be
+  a ``static[string]``. You can use ``when defined(nimNewDot)`` to make
+  your code work with both old and new Nim versions.
+  See [special-operators](https://nim-lang.org/docs/manual.html#special-operators)
+  for more information.
+
+- ``yield`` (or ``await`` which is mapped to ``yield``) never worked reliably
+  in an array, seq or object constructor and is now prevented at compile-time.
+
+### Library additions
+
+- **Added ``sequtils.mapLiterals`` for easier construction of array and tuple literals.**
+
+- Added ``system.runnableExamples`` to make examples in Nim's documentation easier
+  to write and test. The examples are tested as the last step of
+  ``nim doc``.
+
+- Implemented ``getIoHandler`` proc in the ``asyncdispatch`` module that allows
+  you to retrieve the underlying IO Completion Port or ``Selector[AsyncData]``
+  object in the specified dispatcher.
+
+- For string formatting / interpolation a new module
+  called [strformat](https://nim-lang.org/docs/strformat.html) has been added
+  to the stdlib.
+
+- The `ReadyKey` type in the selectors module now contains an ``errorCode``
+  field to help distinguish between ``Event.Error`` events.
+
+- Implemented an `accept` proc that works on a `SocketHandle` in
+  ``nativesockets``.
+
+- Added ``algorithm.rotateLeft``.
+
+- Added ``typetraits.$`` as an alias for ``typetraits.name``.
+
+- Added ``system.getStackTraceEntries`` that allows you to access the stack
+  trace in a structured manner without string parsing.
+
+- Added ``parseutils.parseSaturatedNatural``.
+
+- Added ``macros.unpackVarargs``.
+
+- Added support for asynchronous programming for the JavaScript backend using
+  the `asyncjs` module.
+
+- Added true color support for some terminals. Example:
+```nim
+import colors, terminal
+
+const Nim = "Efficient and expressive programming."
+
+var
+  fg = colYellow
+  bg = colBlue
+  int = 1.0
+
+enableTrueColors()
+
+for i in 1..15:
+  styledEcho bgColor, bg, fgColor, fg, Nim, resetStyle
+  int -= 0.01
+  fg = intensity(fg, int)
+
+setForegroundColor colRed
+setBackgroundColor colGreen
+styledEcho "Red on Green.", resetStyle
+```
+
+### Library changes
+
+- ``echo`` now works with strings that contain ``\0`` (the binary zero is not
+  shown) and ``nil`` strings are equal to empty strings.
+
+- JSON: Deprecated `getBVal`, `getFNum`, and `getNum` in favour of
+  `getBool`, `getFloat`, `getBiggestInt`. A new `getInt` procedure was also
+  added.
+
+- ``rationals.toRational`` now uses an algorithm based on continued fractions.
+  This means its results are more precise and it can't run into an infinite loop
+  anymore.
+
+- ``os.getEnv`` now takes an optional ``default`` parameter that tells ``getEnv``
+  what to return if the environment variable does not exist.
+
+- The ``random`` procs in ``random.nim`` have all been deprecated. Instead use
+  the new ``rand`` procs. The module now exports the state of the random
+  number generator as type ``Rand`` so multiple threads can easily use their
+  own random number generators that do not require locking. For more information
+  about this rename see issue [#6934](https://github.com/nim-lang/Nim/issues/6934)
+
+- ``writeStackTrace`` is now proclaimed to have no IO effect (even though it does)
+  so that it is more useful for debugging purposes.
+
+- ``db_mysql`` module: ``DbConn`` is now a ``distinct`` type that doesn't expose the
+  details of the underlying ``PMySQL`` type.
+
+- ``parseopt2`` is now deprecated, use ``parseopt`` instead.
+
+### Language additions
+
+- It is now possible to forward declare object types so that mutually
+  recursive types can be created across module boundaries. See
+  [package level objects](https://nim-lang.org/docs/manual.html#package-level-objects)
+  for more information.
+
+- Added support for casting between integers of same bitsize in VM (compile time and nimscript).
+  This allows to, among other things, reinterpret signed integers as unsigned.
+
+- Custom pragmas are now supported using pragma ``pragma``, please see language
+  manual for details.
+
+- Standard library modules can now also be imported via the ``std`` pseudo-directory.
+  This is useful in order to distinguish between standard library and nimble package
+  imports:
+
+  ```nim
+  import std / [strutils, os, osproc]
+  import someNimblePackage / [strutils, os]
+  ```
+
+### Language changes
+
+- The **unary** ``<`` is now deprecated, for ``.. <`` use ``..<`` for other usages
+  use the ``pred`` proc.
+
+- Bodies of ``for`` loops now get their own scope:
+
+```nim
+# now compiles:
+for i in 0..4:
+  let i = i + 1
+  echo i
+```
+
+- To make Nim even more robust the system iterators ``..`` and ``countup``
+  now only accept a single generic type ``T``. This means the following code
+  doesn't die with an "out of range" error anymore:
+
+```nim
+var b = 5.Natural
+var a = -5
+for i in a..b:
+  echo i
+```
+
+- ``atomic`` and ``generic`` are no longer keywords in Nim. ``generic`` used to be
+  an alias for ``concept``, ``atomic`` was not used for anything.
+
+- The memory manager now uses a variant of the TLSF algorithm that has much
+  better memory fragmentation behaviour. According
+  to [http://www.gii.upv.es/tlsf/](http://www.gii.upv.es/tlsf/) the maximum
+  fragmentation measured is lower than 25%. As a nice bonus ``alloc`` and
+  ``dealloc`` became O(1) operations.
+
+- The compiler is now more consistent in its treatment of ambiguous symbols:
+  Types that shadow procs and vice versa are marked as ambiguous (bug #6693).
+
+- codegenDecl pragma now works for the JavaScript backend. It returns an empty
+  string for function return type placeholders.
+
+- Extra semantic checks for procs with noreturn pragma: return type is not allowed,
+  statements after call to noreturn procs are no longer allowed.
+
+- Noreturn proc calls and raising exceptions branches are now skipped during common type
+  deduction in ``if`` and ``case`` expressions. The following code snippets now compile:
+  ```nim
+  import strutils
+  let str = "Y"
+  let a = case str:
+    of "Y": true
+    of "N": false
+    else: raise newException(ValueError, "Invalid boolean")
+  let b = case str:
+    of nil, "": raise newException(ValueError, "Invalid boolean")
+    elif str.startsWith("Y"): true
+    elif str.startsWith("N"): false
+    else: false
+  let c = if str == "Y": true
+    elif str == "N": false
+    else:
+      echo "invalid bool"
+      quit("this is the end")
+  ```
+
+- Pragmas now support call syntax, for example: ``{.exportc"myname".}`` and
+  ``{.exportc("myname").}``
+
+- The ``deprecated`` pragma now supports a user-definable warning message for procs.
+
+  ```nim
+  proc bar {.deprecated: "use foo instead".} =
+    return
+
+  bar()
+  ```
+
+### Tool changes
+
+- The ``nim doc`` command is now an alias for ``nim doc2``, the second version of
+  the documentation generator. The old version 1 can still be accessed
+  via the new ``nim doc0`` command.
+
+- Nim's ``rst2html`` command now supports the testing of code snippets via an RST
+  extension that we called ``:test:``::
+
+  ```rst
+  .. code-block:: nim
+      :test:
+    # shows how the 'if' statement works
+    if true: echo "yes"
+  ```
+
+### Compiler changes
+
+### Bugfixes
+
+- Fixed "ReraiseError when using try/except within finally block"
+  ([#5871](https://github.com/nim-lang/Nim/issues/5871))
+- Fixed "Range type inference leads to counter-intuitive behvaiour"
+  ([#5854](https://github.com/nim-lang/Nim/issues/5854))
+- Fixed "JSON % operator can fail in extern procs with dynamic types"
+  ([#6385](https://github.com/nim-lang/Nim/issues/6385))
+- Fixed ""intVal is not accessible" in VM"
+  ([#6083](https://github.com/nim-lang/Nim/issues/6083))
+- Fixed "Add excl for OrderedSet"
+  ([#2467](https://github.com/nim-lang/Nim/issues/2467))
+- Fixed "newSeqOfCap actually doesn't reserve memory"
+  ([#6403](https://github.com/nim-lang/Nim/issues/6403))
+- Fixed "[Regression] Nim segfaults"
+  ([#6435](https://github.com/nim-lang/Nim/issues/6435))
+- Fixed "Seq assignment is slower than expected"
+  ([#6433](https://github.com/nim-lang/Nim/issues/6433))
+- Fixed "json module issues with empty dicts and lists"
+  ([#6438](https://github.com/nim-lang/Nim/issues/6438))
+- Fixed "mingw installed via finish.exe fails to link if Nim located in path with whitespace"
+  ([#6452](https://github.com/nim-lang/Nim/issues/6452))
+- Fixed "unittest.check does not perform short-circuit evaluation"
+  ([#5784](https://github.com/nim-lang/Nim/issues/5784))
+- Fixed "Error while concatenating an array of chars."
+  ([#5861](https://github.com/nim-lang/Nim/issues/5861))
+- Fixed "range initialization: [ProveInit] hint: Cannot prove that"
+  ([#6474](https://github.com/nim-lang/Nim/issues/6474))
+- Fixed "scanf can call procs with side-effects multiple times"
+  ([#6487](https://github.com/nim-lang/Nim/issues/6487))
+- Fixed "gcsafe detection problem"
+  ([#5620](https://github.com/nim-lang/Nim/issues/5620))
+- Fixed "C++ codegen: `mitems` generates invalid code."
+  ([#4910](https://github.com/nim-lang/Nim/issues/4910))
+- Fixed "strange runtime behavior on macOS"
+  ([#6496](https://github.com/nim-lang/Nim/issues/6496))
+- Fixed "stdtmpl: invalid indentation after a line ending in question mark"
+  ([#5070](https://github.com/nim-lang/Nim/issues/5070))
+- Fixed "Windows: NAN troubles on c backend"
+  ([#6511](https://github.com/nim-lang/Nim/issues/6511))
+- Fixed "lib/nim/system/cellsets.nim(33, 31) Error: type mismatch while attempting to compile for 16bit CPUs"
+  ([#3558](https://github.com/nim-lang/Nim/issues/3558))
+- Fixed "Can't compile dynlib with ``-d:useNimRtl`` and ``--threads:on``"
+  ([#5143](https://github.com/nim-lang/Nim/issues/5143))
+- Fixed "var s = @[0,1,2,...] can generate thousand of single assignments in C code"
+  ([#5007](https://github.com/nim-lang/Nim/issues/5007))
+- Fixed "`echo` discards everything after a null character"
+  ([#1137](https://github.com/nim-lang/Nim/issues/1137))
+- Fixed "Turn off reExtended by default"
+  ([#5627](https://github.com/nim-lang/Nim/issues/5627))
+- Fixed "Bad Links in docs/backends.html"
+  ([#5914](https://github.com/nim-lang/Nim/issues/5914))
+- Fixed "Index out of bounds error in db_postgres when executing non parameter-substituted queries containing "?""
+  ([#6571](https://github.com/nim-lang/Nim/issues/6571))
+- Fixed "Please add pipe2 support to posix stdlib"
+  ([#6553](https://github.com/nim-lang/Nim/issues/6553))
+- Fixed "Return semantics vary depending on return style"
+  ([#6422](https://github.com/nim-lang/Nim/issues/6422))
+- Fixed "parsecsv.open reports SIGSEGV when calling 'open' on missing file"
+  ([#6148](https://github.com/nim-lang/Nim/issues/6148))
+- Fixed "VCC: Nim generates non-compilable code for system.nim"
+  ([#6606](https://github.com/nim-lang/Nim/issues/6606))
+- Fixed "Generic subtype matches worse than a generic"
+  ([#6526](https://github.com/nim-lang/Nim/issues/6526))
+- Fixed "formatFloat inconsistent scientific notation"
+  ([#6589](https://github.com/nim-lang/Nim/issues/6589))
+- Fixed "Generated c code calls function twice"
+  ([#6292](https://github.com/nim-lang/Nim/issues/6292))
+- Fixed "Range type inference leads to counter-intuitive behvaiour"
+  ([#5854](https://github.com/nim-lang/Nim/issues/5854))
+- Fixed "New backward indexing is too limited"
+  ([#6631](https://github.com/nim-lang/Nim/issues/6631))
+- Fixed "Table usage in a macro (SIGSEGV: Illegal storage access.)"
+  ([#1860](https://github.com/nim-lang/Nim/issues/1860))
+- Fixed "Incorrect deprecation error"
+  ([#6634](https://github.com/nim-lang/Nim/issues/6634))
+- Fixed "Wrong indices in arrays not starting with 0"
+  ([#6675](https://github.com/nim-lang/Nim/issues/6675))
+- Fixed "if expressions"
+  ([#6609](https://github.com/nim-lang/Nim/issues/6609))
+- Fixed "BackwardsIndex: converter + `[]` + unrelated type[^1]: lib/system.nim(3536, 3) Error"
+  ([#6692](https://github.com/nim-lang/Nim/issues/6692))
+- Fixed "BackwardsIndex: converter + `[]` + unrelated type[^1]: lib/system.nim(3536, 3) Error"
+  ([#6692](https://github.com/nim-lang/Nim/issues/6692))
+- Fixed "js backend 0.17.3: array bounds check for non zero based arrays is buggy"
+  ([#6532](https://github.com/nim-lang/Nim/issues/6532))
+- Fixed "HttpClient's new API doesn't work through a proxy for https URLs"
+  ([#6685](https://github.com/nim-lang/Nim/issues/6685))
+- Fixed "isServing isn't declared and isn't compiling"
+  ([#6707](https://github.com/nim-lang/Nim/issues/6707))
+- Fixed "[Regression] value out of range"
+  ([#6710](https://github.com/nim-lang/Nim/issues/6710))
+
+- Fixed "Error when using `multisync` macro"
+  ([#6708](https://github.com/nim-lang/Nim/issues/6708))
+
+- Fixed "formatFloat inconsistent scientific notation"
+  ([#6589](https://github.com/nim-lang/Nim/issues/6589))
+- Fixed "Using : (constructor arguments) for passing values to functions with default arguments causes a compiler crash."
+  ([#6765](https://github.com/nim-lang/Nim/issues/6765))
+- Fixed "In-place object initialization leads to vcc incompatible code"
+  ([#6757](https://github.com/nim-lang/Nim/issues/6757))
+- Fixed "Improve parseCookies doc"
+  ([#5721](https://github.com/nim-lang/Nim/issues/5721))
+- Fixed "Parser regression with nested do notation inside conditional"
+  ([#6166](https://github.com/nim-lang/Nim/issues/6166))
+- Fixed "Request for better error message"
+  ([#6776](https://github.com/nim-lang/Nim/issues/6776))
+- Fixed "Testament tester does not execute test with `exitcode` only"
+  ([#6775](https://github.com/nim-lang/Nim/issues/6775))
+- Fixed "JS integer division off by one"
+  ([#6753](https://github.com/nim-lang/Nim/issues/6753))
+- Fixed "Regression: cannot prove not nil"
+  ([#5781](https://github.com/nim-lang/Nim/issues/5781))
+- Fixed "SIGSEGV: Illegal storage access. (Attempt to read from nil?) in generic proc"
+  ([#6073](https://github.com/nim-lang/Nim/issues/6073))
+- Fixed "Request for better error message"
+  ([#6776](https://github.com/nim-lang/Nim/issues/6776))
+- Fixed "Nim #head: sorting via reference hangs compiler"
+  ([#6724](https://github.com/nim-lang/Nim/issues/6724))
+- Fixed "Cannot cast pointer to char in cpp"
+  ([#5979](https://github.com/nim-lang/Nim/issues/5979))
+- Fixed "asynchttpserver replies with several errors on single request"
+  ([#6386](https://github.com/nim-lang/Nim/issues/6386))
+- Fixed "object variants superclass trigger bad codegen"
+  ([#5521](https://github.com/nim-lang/Nim/issues/5521))
+- Fixed "JS integer division off by one"
+  ([#6753](https://github.com/nim-lang/Nim/issues/6753))
+- Fixed "js backend compiler crash with tables indexed by certain types"
+  ([#6568](https://github.com/nim-lang/Nim/issues/6568))
+- Fixed "Jsgen bug with is"
+  ([#6445](https://github.com/nim-lang/Nim/issues/6445))
+- Fixed "Subrange definition with ..<"
+  ([#6788](https://github.com/nim-lang/Nim/issues/6788))
+- Fixed "fields not initialized: array with enum index type as object field."
+  ([#6682](https://github.com/nim-lang/Nim/issues/6682))
+- Fixed "Can not delete data in table when table's data type is kind of "not nil""
+  ([#6555](https://github.com/nim-lang/Nim/issues/6555))
+- Fixed "tables.nim: Cannot prove that 'n' is initialized"
+  ([#6121](https://github.com/nim-lang/Nim/issues/6121))
+- Fixed "issues with 'not nil' applied to a closure proc"
+  ([#6489](https://github.com/nim-lang/Nim/issues/6489))
+- Fixed "`not nil` not working in some cases"
+  ([#4686](https://github.com/nim-lang/Nim/issues/4686))
+- Fixed "Cannot prove '@[v]' is not nil"
+  ([#3993](https://github.com/nim-lang/Nim/issues/3993))
+
+- Fixed "Feature: support TCP_NODELAY in net.sockets"
+  ([#6795](https://github.com/nim-lang/Nim/issues/6795))
+- Fixed "Code that makes the compiler throw an error message and then hangs"
+  ([#6820](https://github.com/nim-lang/Nim/issues/6820))
+- Fixed "Code that makes the compiler throw an error message and then hangs"
+  ([#6820](https://github.com/nim-lang/Nim/issues/6820))
+- Fixed "Inconsistent behavior with sequence and string slicing"
+  ([#6223](https://github.com/nim-lang/Nim/issues/6223))
+- Fixed "Wrong behavior of "split" (proc and iterator)"
+  ([#4377](https://github.com/nim-lang/Nim/issues/4377))
+- Fixed "[Documentation] Invalid module name: [foo, bar]"
+  ([#6831](https://github.com/nim-lang/Nim/issues/6831))
+- Fixed "The destructor is not called for temporary objects"
+  ([#4214](https://github.com/nim-lang/Nim/issues/4214))
+- Fixed "Destructors does not work with implicit items iterator in for loop"
+  ([#985](https://github.com/nim-lang/Nim/issues/985))
+- Fixed "Error in template when using the type of the parameter inside it"
+  ([#6756](https://github.com/nim-lang/Nim/issues/6756))
+- Fixed "should json.to() respect parent attributes?"
+  ([#5856](https://github.com/nim-lang/Nim/issues/5856))
+- Fixed "json 'to' macro can not marshalize into tuples"
+  ([#6095](https://github.com/nim-lang/Nim/issues/6095))
+- Fixed "json.to fails with seq[T]"
+  ([#6604](https://github.com/nim-lang/Nim/issues/6604))
+- Fixed "json.to() is not worth using compared to marshal.to[T]"
+  ([#5848](https://github.com/nim-lang/Nim/issues/5848))
+- Fixed "Memory not being released in time, running out of memory"
+  ([#6031](https://github.com/nim-lang/Nim/issues/6031))
+- Fixed "[Regression] Bad C codegen for generic code"
+  ([#6889](https://github.com/nim-lang/Nim/issues/6889))
+- Fixed "rollingFileLogger deletes file on every start."
+  ([#6264](https://github.com/nim-lang/Nim/issues/6264))
+- Fixed "Remove/deprecate securehash module."
+  ([#6033](https://github.com/nim-lang/Nim/issues/6033))
+- Fixed "[bug or not] object construction for seq[T] failed without space after colon"
+  ([#5999](https://github.com/nim-lang/Nim/issues/5999))
+- Fixed "issues with the random module"
+  ([#4726](https://github.com/nim-lang/Nim/issues/4726))
+- Fixed "Reassigning local var to seq of objects results in nil element in Object's seq field"
+  ([#668](https://github.com/nim-lang/Nim/issues/668))
+- Fixed "Compilation error with "newseq[string]""
+  ([#6726](https://github.com/nim-lang/Nim/issues/6726))
+- Fixed "await inside array/dict literal produces invalid code - Part 2"
+  ([#6626](https://github.com/nim-lang/Nim/issues/6626))
+- Fixed "terminal.eraseline() gives OverflowError on Windows"
+  ([#6931](https://github.com/nim-lang/Nim/issues/6931))
+- Fixed "[Regression] `sequtils.any` conflicts with `system.any`"
+  ([#6932](https://github.com/nim-lang/Nim/issues/6932))
+- Fixed "C++ codegen: `mitems` generates invalid code."
+  ([#4910](https://github.com/nim-lang/Nim/issues/4910))
+- Fixed "seq.mitems produces invalid cpp codegen"
+  ([#6892](https://github.com/nim-lang/Nim/issues/6892))
+- Fixed "Concepts regression"
+  ([#6108](https://github.com/nim-lang/Nim/issues/6108))
+- Fixed "Generic iterable concept with array crashes compiler"
+  ([#6277](https://github.com/nim-lang/Nim/issues/6277))
+- Fixed "C code generation "‘a’ is a pointer; did you mean to use ‘->’?""
+  ([#6462](https://github.com/nim-lang/Nim/issues/6462))
+- Fixed "`--NimblePath` fails if a `-` in path which is not followed by a number"
+  ([#6949](https://github.com/nim-lang/Nim/issues/6949))
+- Fixed ""not registered in the selector" in asyncfile.close() for something that clearly was registered"
+  ([#6906](https://github.com/nim-lang/Nim/issues/6906))
+- Fixed "strange frexp behavior"
+  ([#6353](https://github.com/nim-lang/Nim/issues/6353))
+
+- Fixed "noreturn branches of case statements shouldn't contribute to type"
+  ([#6885](https://github.com/nim-lang/Nim/issues/6885))
+- Fixed "Type inference for 'if' statements changed"
+  ([#6980](https://github.com/nim-lang/Nim/issues/6980))
+- Fixed "newest asyncdispatch recursion"
+  ([#6100](https://github.com/nim-lang/Nim/issues/6100))
+- Fixed "Ambiguous identifier between set type and proc"
+  ([#6965](https://github.com/nim-lang/Nim/issues/6965))
+
+- Fixed "Inconsistent behavior with sequence and string slicing"
+  ([#6223](https://github.com/nim-lang/Nim/issues/6223))
+
+- Fixed "Unsupported OpenSSL library imported dynamically"
+  ([#5000](https://github.com/nim-lang/Nim/issues/5000))
+- Fixed "`nim check` segfaults"
+  ([#6972](https://github.com/nim-lang/Nim/issues/6972))
+- Fixed "GC deadlock"
+  ([#6988](https://github.com/nim-lang/Nim/issues/6988))
+- Fixed "Create a seq without memory initialization"
+  ([#6401](https://github.com/nim-lang/Nim/issues/6401))
+- Fixed "Fix bug for getch on Windows while using the arrow keys"
+  ([#6966](https://github.com/nim-lang/Nim/issues/6966))
+- Fixed "runnableExamples doesn't work in templates"
+  ([#7018](https://github.com/nim-lang/Nim/issues/7018))
+- Fixed "runnableExamples doesn't work with untyped statement blocks"
+  ([#7019](https://github.com/nim-lang/Nim/issues/7019))
+
+- Fixed "Critical bug in parseBiggestFloat"
+  ([#7060](https://github.com/nim-lang/Nim/issues/7060))
+- Fixed "[RFC] strformat.% should be gone"
+  ([#7078](https://github.com/nim-lang/Nim/issues/7078))
+- Fixed "compiler crash on simple macro"
+  ([#7093](https://github.com/nim-lang/Nim/issues/7093))
+- Fixed "Make newlines sane again"
+  ([#7089](https://github.com/nim-lang/Nim/issues/7089))
+- Fixed "JS - Unicode enum string representation issue"
+  ([#6741](https://github.com/nim-lang/Nim/issues/6741))
+- Fixed "Strange behaviour of 0.17.3 (working ok in 0.17.2)"
+  ([#6989](https://github.com/nim-lang/Nim/issues/6989))
+- Fixed "Strange behaviour of 0.17.3 (working ok in 0.17.2)"
+  ([#6989](https://github.com/nim-lang/Nim/issues/6989))
+- Fixed "Compiler crash: try expression with infix as"
+  ([#7116](https://github.com/nim-lang/Nim/issues/7116))
+- Fixed "nimsuggest crash"
+  ([#7140](https://github.com/nim-lang/Nim/issues/7140))
+- Fixed "[RFC] Reintroduce readChar"
+  ([#7072](https://github.com/nim-lang/Nim/issues/7072))
+- Fixed "Copyright line needs updating"
+  ([#7129](https://github.com/nim-lang/Nim/issues/7129))
+- Fixed "-0.0 doesn't result in negative zero in VM"
+  ([#7079](https://github.com/nim-lang/Nim/issues/7079))
+- Fixed "Windows large filesize"
+  ([#7121](https://github.com/nim-lang/Nim/issues/7121))
+- Fixed "Securehash is not parsimonious with MD5 and other hash modules"
+  ([#6961](https://github.com/nim-lang/Nim/issues/6961))
+- Fixed "os.findExe() shouldn't look in current directory on posix, unless exe has a /"
+  ([#6939](https://github.com/nim-lang/Nim/issues/6939))
+- Fixed "`compiles(...)` with `fatal` pragma causes compiler to exit early"
+  ([#7080](https://github.com/nim-lang/Nim/issues/7080))
+- Fixed "NPE when compile macro that returns concrete value"
+  ([#5450](https://github.com/nim-lang/Nim/issues/5450))
+- Fixed "Using a variable of type `int | float` causes internal compiler error"
+  ([#6946](https://github.com/nim-lang/Nim/issues/6946))
+- Fixed "Unsigned integers could not be used as array indexes."
+  ([#7153](https://github.com/nim-lang/Nim/issues/7153))
+- Fixed "countdown with uint causes underflow"
+  ([#4220](https://github.com/nim-lang/Nim/issues/4220))
+- Fixed "Inconsistent method call syntax"
+  ([#7200](https://github.com/nim-lang/Nim/issues/7200))
+- Fixed "Impossible to create an empty const array"
+  ([#6853](https://github.com/nim-lang/Nim/issues/6853))
+- Fixed "Strange UINT handling"
+  ([#3985](https://github.com/nim-lang/Nim/issues/3985))
+- Fixed "Bad codegen when passing arg that is part of return value destination"
+  ([#6960](https://github.com/nim-lang/Nim/issues/6960))
+- Fixed "No info about gcsafety in error message when global var is accessed in async proc"
+  ([#6186](https://github.com/nim-lang/Nim/issues/6186))
+- Fixed "Resolving package vs. local import ambiguities"
+  ([#2819](https://github.com/nim-lang/Nim/issues/2819))
+- Fixed "Internal error with type() operator"
+  ([#3711](https://github.com/nim-lang/Nim/issues/3711))
+- Fixed "newAsyncSocket should raise an OS error plus other inconsistencies"
+  ([#4995](https://github.com/nim-lang/Nim/issues/4995))
+- Fixed "mapLiterals fails with negative values"
+  ([#7215](https://github.com/nim-lang/Nim/issues/7215))
+- Fixed "fmWrite doesn't truncate file with openAsync, unlike open()"
+  ([#5531](https://github.com/nim-lang/Nim/issues/5531))
+- Fixed "Move libsvm to an external nimble module"
+  ([#5786](https://github.com/nim-lang/Nim/issues/5786))
+- Fixed "Prevent acceptAddr gotcha with newSocket"
+  ([#7227](https://github.com/nim-lang/Nim/issues/7227))
+- Fixed "strtabs.getOrDefault is inconsistent with tables.getOrDefault"
+  ([#4265](https://github.com/nim-lang/Nim/issues/4265))
+
+- Fixed "Code falling through into exception handler when no exception thrown."
+  ([#7232](https://github.com/nim-lang/Nim/issues/7232))
+- Fixed "the new generic inference rules are broken"
+  ([#7247](https://github.com/nim-lang/Nim/issues/7247))
+- Fixed "Odd `..<` regression"
+  ([#6992](https://github.com/nim-lang/Nim/issues/6992))
+- Fixed "Different proc type inferred from default parameter"
+  ([#4659](https://github.com/nim-lang/Nim/issues/4659))
+- Fixed "Different proc type inferred from default parameter"
+  ([#4659](https://github.com/nim-lang/Nim/issues/4659))
+- Fixed "Testament sometimes ignores test failures"
+  ([#7236](https://github.com/nim-lang/Nim/issues/7236))
+- Fixed "New Allocator Fails On >=4GB Requests"
+  ([#7120](https://github.com/nim-lang/Nim/issues/7120))
+- Fixed "User pragmas hide effect specifications from sempass2"
+  ([#7216](https://github.com/nim-lang/Nim/issues/7216))
+- Fixed "C++: SIGABRT instead of IndexError for out-of-bounds"
+  ([#6512](https://github.com/nim-lang/Nim/issues/6512))
+- Fixed "An uncaught exception in cpp mode doesn't show the exception name/msg"
+  ([#6431](https://github.com/nim-lang/Nim/issues/6431))
diff --git a/changelogs/changelog_0_19_0.md b/changelogs/changelog_0_19_0.md
new file mode 100644
index 000000000..45cefe933
--- /dev/null
+++ b/changelogs/changelog_0_19_0.md
@@ -0,0 +1,253 @@
+## v0.19.X - XX/XX/2018
+
+### Changes affecting backwards compatibility
+
+- The stdlib module ``future`` has been renamed to ``sugar``.
+- ``macros.callsite`` is now deprecated. Since the introduction of ``varargs``
+  parameters this became unnecessary.
+- Anonymous tuples with a single element can now be written as ``(1,)`` with a
+  trailing comma. The underlying AST is ``nnkTupleConstr(newLit 1)`` for this
+  example. ``nnkTupleConstr`` is a new node kind your macros need to be able
+  to deal with!
+- Indexing into a ``cstring`` for the JS target is now mapped
+  to ``charCodeAt``.
+- Assignments that would "slice" an object into its supertype are now prevented
+  at runtime. Use ``ref object`` with inheritance rather than ``object`` with
+  inheritance to prevent this issue.
+- The ``not nil`` type annotation now has to be enabled explicitly
+  via ``{.experimental: "notnil".}`` as we are still not pleased with how this
+  feature works with Nim's containers.
+- The parser now warns about inconsistent spacing around binary operators as
+  these can easily be confused with unary operators. This warning will likely
+  become an error in the future.
+- The ``'c`` and ``'C'`` suffix for octal literals is now deprecated to
+  bring the language in line with the standard library (e.g. ``parseOct``).
+- The dot style for import paths (e.g ``import path.to.module`` instead of
+  ``import path/to/module``) has been deprecated.
+
+#### Breaking changes in the standard library
+
+- ``re.split`` for empty regular expressions now yields every character in
+  the string which is what other programming languages chose to do.
+- The returned tuple of ``system.instantiationInfo`` now has a third field
+  containing the column of the instantiation.
+
+- ``cookies.setCookie`` no longer assumes UTC for the expiration date.
+- ``strutils.formatEng`` does not distinguish between ``nil`` and ``""``
+  strings anymore for its ``unit`` parameter. Instead the space is controlled
+  by a new parameter ``useUnitSpace``.
+
+- The ``times.parse`` and ``times.format`` procs have been rewritten.
+  The proc signatures are the same so it should generally not break anything.
+  However, the new implementation is a bit stricter, which is a breaking change.
+  For example ``parse("2017-01-01 foo", "yyyy-MM-dd")`` will now raise an error.
+
+- ``proc `-`*(a, b: Time): int64`` in the ``times`` module has changed return type
+  to ``times.Duration`` in order to support higher time resolutions.
+  The proc is no longer deprecated.
+
+- The ``times.Timezone`` is now an immutable ref-type that must be initialized
+  with an explicit constructor (``newTimezone``).
+
+- ``posix.Timeval.tv_sec`` has changed type to ``posix.Time``.
+
+- ``math.`mod` `` for floats now behaves the same as ``mod`` for integers
+  (previously it used floor division like Python). Use ``math.floorMod`` for the old behavior.
+
+- For string inputs, ``unicode.isUpper`` and ``unicode.isLower`` now require a
+  second mandatory parameter ``skipNonAlpha``.
+
+- For string inputs, ``strutils.isUpperAscii`` and ``strutils.isLowerAscii`` now
+  require a second mandatory parameter ``skipNonAlpha``.
+
+- ``osLastError`` is now marked with ``sideEffect``
+- The procs ``parseHexInt`` and ``parseOctInt`` now fail on empty strings
+  and strings containing only valid prefixes, e.g. "0x" for hex integers.
+
+- ``terminal.setCursorPos`` and ``terminal.setCursorXPos`` now work correctly
+  with 0-based coordinates on POSIX (previously, you needed to use
+  1-based coordinates on POSIX for correct behaviour; the Windows behaviour
+  was always correct).
+
+- ``lineInfoObj`` now returns absolute path instead of project path.
+  It's used by ``lineInfo``, ``check``, ``expect``, ``require``, etc.
+
+- ``net.sendTo`` no longer returns an int and now raises an ``OSError``.
+- `threadpool`'s `await` and derivatives have been renamed to `blockUntil`
+  to avoid confusions with `await` from the `async` macro.
+
+
+#### Breaking changes in the compiler
+
+- The undocumented ``#? braces`` parsing mode was removed.
+- The undocumented PHP backend was removed.
+- The default location of ``nimcache`` for the native code targets was
+  changed. Read [the compiler user guide](https://nim-lang.org/docs/nimc.html#generated-c-code-directory)
+  for more information.
+
+### Library additions
+
+- ``re.split`` now also supports the ``maxsplit`` parameter for consistency
+  with ``strutils.split``.
+- Added ``system.toOpenArray`` in order to support zero-copy slicing
+  operations. This is currently not yet available for the JavaScript target.
+- Added ``getCurrentDir``, ``findExe``, ``cpDir`` and  ``mvDir`` procs to
+  ``nimscript``.
+- The ``times`` module now supports up to nanosecond time resolution when available.
+- Added the type ``times.Duration`` for representing fixed durations of time.
+- Added the proc ``times.convert`` for converting between different time units,
+  e.g days to seconds.
+- Added the proc ``algorithm.binarySearch[T, K]`` with the ```cmp``` parameter.
+- Added the proc ``algorithm.upperBound``.
+- Added inverse hyperbolic functions, ``math.arcsinh``, ``math.arccosh`` and ``math.arctanh`` procs.
+- Added cotangent, secant and cosecant procs ``math.cot``, ``math.sec`` and ``math.csc``; and their hyperbolic, inverse and inverse hyperbolic functions, ``math.coth``, ``math.sech``, ``math.csch``, ``math.arccot``, ``math.arcsec``, ``math.arccsc``, ``math.arccoth``, ``math.arcsech`` and ``math.arccsch`` procs.
+- Added the procs ``math.floorMod`` and ``math.floorDiv`` for floor based integer division.
+- Added the procs ``rationals.`div```, ``rationals.`mod```, ``rationals.floorDiv`` and ``rationals.floorMod`` for rationals.
+- Added the proc ``math.prod`` for product of elements in openArray.
+- Added the proc ``parseBinInt`` to parse a binary integer from a string, which returns the value.
+- ``parseOct`` and ``parseBin`` in parseutils now also support the ``maxLen`` argument similar to ``parseHexInt``.
+- Added the proc ``flush`` for memory mapped files.
+- Added the ``MemMapFileStream``.
+- Added a simple interpreting event parser template ``eventParser`` to the ``pegs`` module.
+- Added ``macros.copyLineInfo`` to copy lineInfo from other node.
+- Added ``system.ashr`` an arithmetic right shift for integers.
+
+### Library changes
+
+- ``macros.astGenRepr``, ``macros.lispRepr`` and ``macros.treeRepr``
+  now escapes the content of string literals consistently.
+- ``macros.NimSym`` and ``macros.NimIdent`` is now deprecated in favor
+  of the more general ``NimNode``.
+- ``macros.getImpl`` now includes the pragmas of types, instead of omitting them.
+- ``macros.hasCustomPragma`` and ``macros.getCustomPragmaVal`` now
+  also support ``ref`` and ``ptr`` types, pragmas on types and variant
+  fields.
+- ``system.SomeReal`` is now called ``SomeFloat`` for consistency and
+  correctness.
+- ``algorithm.smartBinarySearch`` and ``algorithm.binarySearch`` is
+  now joined in ``binarySearch``. ``smartbinarySearch`` is now
+  deprecated.
+- The `terminal` module now exports additional procs for generating ANSI color
+  codes as strings.
+- Added the parameter ``val`` for the ``CritBitTree[int].inc`` proc.
+- An exception raised from a ``test`` block of ``unittest`` now shows its type in
+  error message.
+- The ``compiler/nimeval`` API was rewritten to simplify the "compiler as an
+  API". Using the Nim compiler and its VM as a scripting engine has never been
+  easier. See ``tests/compilerapi/tcompilerapi.nim`` for an example of how to
+  use the Nim VM in a native Nim application.
+- Added the parameter ``val`` for the ``CritBitTree[T].incl`` proc.
+- The proc ``tgamma`` was renamed to ``gamma``. ``tgamma`` is deprecated.
+- The ``pegs`` module now exports getters for the fields of its ``Peg`` and ``NonTerminal``
+  object types. ``Peg``s with child nodes now have the standard ``items`` and ``pairs``
+  iterators.
+- The ``accept`` socket procedure defined in the ``net`` module can now accept
+  a nil socket.
+
+### Language additions
+
+- Dot calls combined with explicit generic instantiations can now be written
+  as ``x.y[:z]`` which is transformed into ``y[z](x)`` by the parser.
+- ``func`` is now an alias for ``proc {.noSideEffect.}``.
+- In order to make ``for`` loops and iterators more flexible to use Nim now
+  supports so called "for-loop macros". See
+  the [manual](manual.html#macros-for-loop-macros) for more details.
+  This feature enables a Python-like generic ``enumerate`` implementation.
+
+- Case statements can now be rewritten via macros. See the [manual](manual.html#macros-case-statement-macros) for more information.
+  This feature enables custom pattern matchers.
+
+
+- the `typedesc` special type has been renamed to just `type`.
+- `static` and `type` are now also modifiers similar to `ref` and `ptr`.
+  They denote the special types `static[T]` and `type[T]`.
+- Forcing compile-time evaluation with `static` now supports specifying
+  the desired target type (as a concrete type or as a type class)
+- The `type` operator now supports checking that the supplied expression
+  matches an expected type constraint.
+
+### Language changes
+
+- The `importcpp` pragma now allows importing the listed fields of generic
+  C++ types. Support for numeric parameters have also been added through
+  the use of `static[T]` types.
+  (#6415)
+
+- Native C++ exceptions can now be imported with `importcpp` pragma.
+  Imported exceptions can be raised and caught just like Nim exceptions.
+  More details in language manual.
+
+- ``nil`` for strings/seqs is finally gone. Instead the default value for
+  these is ``"" / @[]``. Use ``--nilseqs:on`` for a transition period.
+
+- Accessing the binary zero terminator in Nim's native strings
+  is now invalid. Internally a Nim string still has the trailing zero for
+  zero-copy interoperability with ``cstring``. Compile your code with the
+  new switch ``--laxStrings:on`` if you need a transition period.
+
+- The command syntax now supports keyword arguments after the first comma.
+
+- Thread-local variables can now be declared inside procs. This implies all
+  the effects of the ``global`` pragma.
+
+- Nim now supports the ``except`` clause in the export statement.
+
+- Range float types, example ``range[0.0 .. Inf]``. More details in language manual.
+- The ``{.this.}`` pragma has been deprecated. It never worked within generics and
+  we found the resulting code harder to read than the more explicit ``obj.field``
+  syntax.
+- "Memory regions" for pointer types have been deprecated, they were hardly used
+  anywhere. Note that this has **nothing** to do with the ``--gc:regions`` switch
+  of managing memory.
+
+- The exception hierarchy was slightly reworked, ``SystemError`` was renamed to
+  ``CatchableError`` and is the new base class for any exception that is guaranteed to
+  be catchable. This change should have minimal impact on most existing Nim code.
+
+
+### Tool changes
+
+- ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed
+  ``doc``. The old ``jsondoc`` can still be invoked with ``jsondoc0``.
+
+### Compiler changes
+
+- The VM's instruction count limit was raised to 3 million instructions in
+  order to support more complex computations at compile-time.
+
+- Support for hot code reloading has been implemented for the JavaScript
+  target. To use it, compile your code with `--hotCodeReloading:on` and use a
+  helper library such as LiveReload or BrowserSync.
+
+- A new compiler option `--cppCompileToNamespace` puts the generated C++ code
+  into the namespace "Nim" in order to avoid naming conflicts with existing
+  C++ code. This is done for all Nim code - internal and exported.
+
+- Added ``macros.getProjectPath`` and ``os.putEnv`` procs to Nim's virtual
+  machine.
+
+- The ``deadCodeElim`` option is now always turned on and the switch has no
+  effect anymore, but is recognized for backwards compatibility.
+
+- ``experimental`` is now a pragma / command line switch that can enable specific
+  language extensions, it is not an all-or-nothing switch anymore.
+
+- Nintendo Switch was added as a new platform target. See [the compiler user guide](https://nim-lang.org/docs/nimc.html)
+  for more info.
+
+- macros.bindSym now capable to accepts not only literal string or string constant expression.
+  bindSym enhancement make it also can accepts computed string or ident node inside macros /
+  compile time functions / static blocks. Only in templates / regular code it retains it's old behavior.
+  This new feature can be accessed via {.experimental: "dynamicBindSym".} pragma/switch.
+
+- On Posix systems the global system wide configuration is now put under ``/etc/nim/nim.cfg``,
+  it used to be ``/etc/nim.cfg``. Usually it does not exist, however.
+
+- On Posix systems the user configuration is now looked under ``$XDG_CONFIG_HOME/nim/nim.cfg``
+  (if ``XDG_CONFIG_HOME`` is not defined, then under ``~/.config/nim/nim.cfg``). It used to be
+  ``$XDG_CONFIG_DIR/nim.cfg`` (and ``~/.config/nim.cfg``).
+
+  Similarly, on Windows, the user configuration is now looked under ``%APPDATA%/nim/nim.cfg``.
+  This used to be ``%APPDATA%/nim.cfg``.
+
+### Bugfixes
diff --git a/changelogs/changelog_0_20_0.md b/changelogs/changelog_0_20_0.md
new file mode 100644
index 000000000..9ddfd36dc
--- /dev/null
+++ b/changelogs/changelog_0_20_0.md
@@ -0,0 +1,294 @@
+## v0.20.0 - 2019-06-06
+
+
+### Changes affecting backwards compatibility
+
+- `shr` is now sign preserving. Use `-d:nimOldShiftRight` to enable
+  the old behavior globally.
+
+- The ``isLower``, ``isUpper`` family of procs in strutils/unicode
+  operating on **strings** have been
+  deprecated since it was unclear what these do. Note that the much more
+  useful procs that operate on ``char`` or ``Rune`` are not affected.
+
+- `strutils.editDistance` has been deprecated,
+  use `editdistance.editDistance` or `editdistance.editDistanceAscii`
+  instead.
+
+- The OpenMP parallel iterator \``||`\` now supports any `#pragma omp directive`
+  and not just `#pragma omp parallel for`. See
+  [OpenMP documentation](https://www.openmp.org/wp-content/uploads/OpenMP-4.5-1115-CPP-web.pdf).
+
+  The default annotation is `parallel for`, if you used OpenMP without annotation
+  the change is transparent, if you used annotations you will have to prefix
+  your previous annotations with `parallel for`.
+
+  Furthermore, an overload with positive stepping is available.
+
+- The `unchecked` pragma was removed, instead use `system.UncheckedArray`.
+
+- 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.
+
+- Methods are now ordinary "single" methods, only the first parameter is
+  used to select the variant at runtime. For backwards compatibility
+  use the new `--multimethods:on` switch.
+
+- Generic methods are now deprecated; they never worked well.
+
+- Compile time checks for integer and float conversions are now stricter.
+  For example, `const x = uint32(-1)` now gives a compile time error instead
+  of being equivalent to `const x = 0xFFFFFFFF'u32`.
+
+- Using `typed` as the result type in templates/macros now means
+  "expression with a type". The old meaning of `typed` is preserved
+  as `void` or no result type at all.
+
+- A bug allowed `macro foo(): int = 123` to compile even though a
+  macro has to return a `NimNode`. This has been fixed.
+
+- With the exception of `uint` and `uint64`, conversion to unsigned types
+  are now range checked during runtime.
+
+- Macro arguments of type `typedesc` are now passed to the macro as
+  `NimNode` like every other type except `static`. Use `typed` for a
+  behavior that is identical in new and old
+  Nim. See the RFC [Pass typedesc as NimNode to macros](https://github.com/nim-lang/RFCs/issues/148)
+  for more details.
+
+
+#### Breaking changes in the standard library
+
+- `osproc.execProcess` now also takes a `workingDir` parameter.
+
+- `std/sha1.secureHash` now accepts `openArray[char]`, not `string`. (Former
+   successful matches should keep working, though former failures will not.)
+
+- `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.parseBiggestInt`, `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 an `OverflowError` and sometimes they
+  returned `0`.
+
+- The procs `parseutils.parseBin`, `parseutils.parseOct` and `parseutils.parseHex`
+  were not clearing their `var` parameter `number` and used to push its value to
+  the left when storing the parsed string into it. Now they always set the value
+  of the parameter to `0` before storing the result of the parsing, unless the
+  string to parse is not valid (then the value of `number` is not changed).
+
+- `streams.StreamObject` now restricts its fields to only raise `system.Defect`,
+  `system.IOError` and `system.OSError`.
+  This change only affects custom stream implementations.
+
+- nre's `RegexMatch.{captureBounds,captures}[]`  no longer return `Option` or
+  `nil`/`""`, respectively. Use the newly added `n in p.captures` method to
+  check if a group is captured, otherwise you'll receive an exception.
+
+- nre's `RegexMatch.{captureBounds,captures}.toTable` no longer accept a
+  default parameter. Instead uncaptured entries are left empty. Use
+  `Table.getOrDefault()` if you need defaults.
+
+- nre's `RegexMatch.captures.{items,toSeq}` now returns an `Option[string]`
+  instead of a `string`. With the removal of `nil` strings, this is the only
+  way to indicate a missing match. Inside your loops, instead
+  of `capture == ""` or `capture == nil`, use `capture.isSome` to check if a capture is
+  present, and `capture.get` to get its value.
+
+- nre's `replace()` no longer throws `ValueError` when the replacement string
+  has missing captures. It instead throws `KeyError` for named captures, and
+  `IndexError` for unnamed captures. This is consistent 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 equality:
+  `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)
+
+- procs `string.add(int)` and `string.add(float)` which implicitly convert
+  ints and floats to string have been deprecated.
+  Use `string.addInt(int)` and `string.addFloat(float)` instead.
+
+- ``case object`` branch transitions via ``system.reset`` are deprecated.
+  Compile your code with ``-d:nimOldCaseObjects`` for a transition period.
+
+- base64 module: The default parameter `newLine` for the `encode` procs
+  was changed from `"\13\10"` to the empty string `""`.
+
+
+#### Breaking changes in the compiler
+
+- The compiler now implements the "generic symbol prepass" for `when` statements
+  in generics, see bug #8603. This means that code like this does not compile
+  anymore:
+
+```nim
+proc enumToString*(enums: openArray[enum]): string =
+  # typo: 'e' instead 'enums'
+  when e.low.ord >= 0 and e.high.ord < 256:
+    result = newString(enums.len)
+  else:
+    result = newString(enums.len * 2)
+```
+
+- ``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
+
+- There is a new stdlib module `std/editdistance` as a replacement for the
+  deprecated `strutils.editDistance`.
+
+- There is a new stdlib module `std/wordwrap` as a replacement for the
+  deprecated `strutils.wordwrap`.
+
+- Added `split`, `splitWhitespace`, `size`, `alignLeft`, `align`,
+  `strip`, `repeat` procs and iterators to `unicode.nim`.
+
+- Added `or` for `NimNode` in `macros`.
+
+- Added `system.typeof` for more control over how `type` expressions
+  can be deduced.
+
+- Added `macros.isInstantiationOf` for checking if the proc symbol
+  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` (implemented as `getAppFilename` at CT),
+  can be used to retrieve the currently executing compiler.
+
+- Added `xmltree.toXmlAttributes`.
+
+- Added ``std/sums`` module for fast summation functions.
+
+- Added `Rusage`, `getrusage`, `wait4` to the posix interface.
+
+- Added the `posix_utils` module.
+
+- Added `system.default`.
+
+- Added `sequtils.items` for closure iterators, allows closure iterators
+  to be used by the `mapIt`, `filterIt`, `allIt`, `anyIt`, etc.
+
+
+### Library changes
+
+- The string output of `macros.lispRepr` proc has been tweaked
+  slightly. The `dumpLisp` macro in this module now outputs an
+  indented proper Lisp, devoid of commas.
+
+- Added `macros.signatureHash` that returns a stable identifier
+  derived from the signature of a symbol.
+
+- In `strutils` empty strings now no longer matched as substrings
+  anymore.
+
+- The `Complex` type is now a generic object and not a tuple anymore.
+
+- The `ospaths` module is now deprecated, use `os` instead. Note that
+  `os` is available in a NimScript environment but unsupported
+  operations produce a compile-time error.
+
+- The `parseopt` module now supports a new flag `allowWhitespaceAfterColon`
+  (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` was moved to `lib/deprecated`.
+
+- The switch ``-d:useWinAnsi`` is not supported anymore.
+
+- In `times` module, procs `format` and `parse` accept a new optional
+  `DateTimeLocale` argument for formatting/parsing dates in other languages.
+
+
+### Language additions
+
+- 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 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.
+- Tuple unpacking is now supported for constants and for loop variables.
+- Case object branches can be initialized with a runtime discriminator if
+  possible discriminator values are constrained within a case statement.
+
+### Language changes
+
+- The standard extension for SCF (source code filters) files was changed from
+  ``.tmpl`` to ``.nimf``,
+  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.
+
+- Hash sets and tables are initialized by default. The explicit `initHashSet`,
+  `initTable`, etc. are not needed anymore.
+
+
+### Tool changes
+
+- `jsondoc` now includes a `moduleDescription` field with the module
+  description. `jsondoc0` shows comments as its own objects as shown in the
+  documentation.
+- `nimpretty`: --backup now defaults to `off` instead of `on` and the flag was
+  undocumented; use `git` instead of relying on backup files.
+- `koch` now defaults to build the latest *stable* Nimble version unless you
+  explicitly ask for the latest master version via `--latest`.
+
+
+### Compiler changes
+
+- The deprecated `fmod` proc is now unavailable on the VM.
+- A new `--outdir` option was added.
+- The compiled JavaScript file for the project produced by executing `nim js`
+  will no longer be placed in the nimcache directory.
+- The `--hotCodeReloading` has been implemented for the native targets.
+  The compiler also provides a new more flexible API for handling the
+  hot code reloading events in the code.
+- The compiler now supports a ``--expandMacro:macroNameHere`` switch
+  for easy introspection into what a macro expands into.
+- The `-d:release` switch now does not disable runtime checks anymore.
+  For a release build that also disables runtime checks
+  use `-d:release -d:danger` or simply `-d:danger`.
+
+
+### Bugfixes
diff --git a/changelogs/changelog_0_20_2.md b/changelogs/changelog_0_20_2.md
new file mode 100644
index 000000000..4f09ae3c5
--- /dev/null
+++ b/changelogs/changelog_0_20_2.md
@@ -0,0 +1,55 @@
+# v0.20.2 - 2019-07-17
+
+
+## Changes affecting backwards compatibility
+
+- All `strutils.rfind` procs now take `start` and `last` like `strutils.find`
+  with the same data slice/index meaning. This is backwards compatible for
+  calls *not* changing the `rfind` `start` parameter from its default. (#11487)
+
+  In the unlikely case that you were using `rfind X, start=N`, or `rfind X, N`,
+  then you need to change that to `rfind X, last=N` or `rfind X, 0, N`. (This
+  should minimize gotchas porting code from other languages like Python or C++.)
+
+- On Windows stderr/stdout/stdin are not opened as binary files anymore. Use the switch
+  `-d:nimBinaryStdFiles` for a transition period.
+
+### Breaking changes in the standard library
+
+- Mac OS X / BSD: TSa_Family is now the ``uint8`` type, so type
+  conversions like ``x.sin_family = uint16 toInt(nativesockets.AF_INET)``
+  need to be changed into ``x.sin_family = TSa_Family toInt(nativesockets.AF_INET)``.
+
+
+### Breaking changes in the compiler
+
+
+## Library additions
+
+- `toOpenArray` is now available for the JS target.
+
+## Library changes
+
+- Fix async IO operations stalling even after socket is closed. (#11232)
+
+- More informative error message for `streams.openFileStream`. (#11438)
+
+
+## Language additions
+
+
+## Language changes
+
+
+### Tool changes
+
+
+### Compiler changes
+
+- Better error message for IndexError for empty containers. (#11476)
+
+- Fix regression in semfold for old right shift. (#11477)
+
+- Fix for passing tuples as static params to macros. (#11423)
+
+## Bugfixes
diff --git a/changelogs/changelog_1_0_0.md b/changelogs/changelog_1_0_0.md
new file mode 100644
index 000000000..5a9185ecb
--- /dev/null
+++ b/changelogs/changelog_1_0_0.md
@@ -0,0 +1,138 @@
+# v1.0 - 2019-09-23
+
+
+## Changes affecting backwards compatibility
+
+- The switch ``-d:nimBinaryStdFiles`` does not exist anymore. Instead
+  stdin/stdout/stderr are binary files again. This change only affects
+  Windows.
+
+- On Windows console applications the code-page is set at program startup
+  to UTF-8. Use the new switch `-d:nimDontSetUtf8CodePage` to disable this
+  feature.
+
+- The language definition and compiler are now stricter about ``gensym``'ed
+  symbols in hygienic templates. See the section in the
+  [manual](https://nim-lang.org/docs/manual.html#templates-hygiene-in-templates)
+  for further details. Use the compiler switch `--oldgensym:on` for a
+  transition period.
+
+
+### Breaking changes in the standard library
+
+- We removed `unicode.Rune16` without any deprecation period as the name
+  was wrong (see the [RFC](https://github.com/nim-lang/RFCs/issues/151) for details)
+  and we didn't find any usages of it in the wild. If you still need it, add this
+  piece of code to your project:
+```nim
+type
+  Rune16* = distinct int16
+```
+
+- `exportc` now uses C instead of C++ mangling with `nim cpp`, matching behavior
+  of `importc`, see #10578.
+  Use the new `exportcpp` to mangle as C++ when using `nim cpp`.
+
+
+### Breaking changes in the compiler
+
+- A bug allowing `int` to be implicitly converted to range types of smaller size
+  (e.g `range[0'i8..10'i8]`) has been fixed.
+
+
+
+## Library additions
+
+- `encodings.getCurrentEncoding` now distinguishes between the console's
+  encoding and the OS's encoding. This distinction is only meaningful on
+  Windows.
+- Added `system.getOsFileHandle` which is usually more useful
+  than `system.getFileHandle`. This distinction is only meaningful on
+  Windows.
+- Added a `json.parseJsonFragments` iterator that can be used to speedup
+  JSON processing substantially when there are JSON fragments separated
+  by whitespace.
+
+
+
+## Library changes
+
+- Added `os.delEnv` and `nimscript.delEnv`. (#11466)
+
+- Enabled Oid usage in hashtables. (#11472)
+
+- Added `unsafeColumnAt` procs, that return unsafe cstring from InstantRow. (#11647)
+
+- Make public `Sha1Digest` and `Sha1State` types and `newSha1State`,
+  `update` and `finalize` procedures from `sha1` module. (#11694)
+
+- Added the `std/monotimes` module which implements monotonic timestamps.
+
+- Consistent error handling of two `exec` overloads. (#10967)
+
+- Officially the following modules now have an unstable API:
+  - std/varints
+  - core/allocators
+  - core/hotcodereloading
+  - asyncstreams
+  - base64
+  - browsers
+  - collections/rtarrays
+  - collections/sharedlist
+  - collections/sharedtable
+  - concurrency/atomics
+  - concurrency/cpuload
+  - concurrency/threadpool
+  - coro
+  - endians
+  - httpcore
+  - parsesql
+  - pathnorm
+  - reservedmem
+  - typetraits
+
+  Every other stdlib module is API stable with respect to version 1.
+
+
+
+## Language additions
+
+- Inline iterators returning `lent T` types are now supported, similarly to
+  iterators returning `var T`:
+```nim
+iterator myitems[T](x: openarray[T]): lent T
+iterator mypairs[T](x: openarray[T]): tuple[idx: int, val: lent T]
+```
+
+- Added an `importjs` pragma that can now be used instead of `importcpp`
+  and `importc` to import symbols from JavaScript. `importjs` for routines always
+  takes a "pattern" for maximum flexibility.
+
+
+
+## Language changes
+
+- `uint64` is now finally a regular ordinal type. This means `high(uint64)` compiles
+  and yields the correct value.
+
+
+### Tool changes
+
+- The Nim compiler now does not recompile the Nim project via ``nim c -r`` if
+  no dependent Nim file changed. This feature can be overridden by
+  the ``--forceBuild`` command line option.
+- The Nim compiler now warns about unused module imports. You can use a
+  top level ``{.used.}`` pragma in the module that you want to be importable
+  without producing this warning.
+- The "testament" testing tool's name was changed
+  from `tester` to `testament` and is generally available as a tool to run Nim
+  tests automatically.
+
+
+### Compiler changes
+
+- VM can now cast integer type arbitrarily. (#11459)
+
+
+
+## Bugfixes
diff --git a/changelogs/changelog_1_0_2.md b/changelogs/changelog_1_0_2.md
new file mode 100644
index 000000000..aa01c503f
--- /dev/null
+++ b/changelogs/changelog_1_0_2.md
@@ -0,0 +1,56 @@
+# v1.0.2 - 2019-10-23
+
+
+## Bugfixes
+
+* fixes the --verbosity:2 regression
+* Fixed "Fail to compile a file twice under Windows (v1.0 bug)." [#12242](https://github.com/nim-lang/Nim/issues/12242)
+* fix nimpretty removing space before pragma
+* JS: gensym is stricter for 'this'
+* Fixed "VM Assertion Error with newruntime" [#12294](https://github.com/nim-lang/Nim/issues/12294)
+* Fixed "Assertion error when running `nim check` on compiler/nim.nim" [#12281](https://github.com/nim-lang/Nim/issues/12281)
+* Fixed "Compiler crash with empty array and generic instantiation with int as parameter" [#12264](https://github.com/nim-lang/Nim/issues/12264)
+* Fixed "Regression in JS backend codegen "Error: request to generate code for .compileTime proc"" [#12240](https://github.com/nim-lang/Nim/issues/12240)
+* Fix how `relativePath` handle case sensitiviy
+* Fixed "SIGSEGV in compiler when using generic types and seqs" [#12336](https://github.com/nim-lang/Nim/issues/12336)
+* Fixed "[1.0.0] weird interaction between `import os` and casting integer to char on macosx trigger bad codegen" [#12291](https://github.com/nim-lang/Nim/issues/12291)
+* VM: no special casing for big endian machines
+* Fixed "`internal error: environment misses` with a simple template inside one of Jester macros" [#12323](https://github.com/nim-lang/Nim/issues/12323)
+* nimsuggest: fix tcp socket leak
+* nimsuggest: fix tcp socket leak for epc backend
+* Fixed "`writeFile` and `write(f, str)` skip null bytes on Windows" [#12315](https://github.com/nim-lang/Nim/issues/12315)
+* Fixed "Crash in intsets symmetric_difference" [#12366](https://github.com/nim-lang/Nim/issues/12366)
+* Fixed "[regression] VM crash when dealing with var param of a proc result" [#12244](https://github.com/nim-lang/Nim/issues/12244)
+* fixes a koch regression that made 'koch boot --listcmd' not work anymore
+* Fixed "[regression] inconsistent signed int `mod` operator between runtime, compiletime, and semfold" [#12332](https://github.com/nim-lang/Nim/issues/12332)
+* Fixed "Boehm disables interior pointer checking" [#12286](https://github.com/nim-lang/Nim/issues/12286)
+* Fixes semCustomPragma when nkSym
+* Fixed yield in nkCheckedFieldExpr
+* Fixed "`randomize()` from `random` not working on JS" [#12418](https://github.com/nim-lang/Nim/issues/12418)
+* Fixed "Compiler crash with invalid object variant" [#12379](https://github.com/nim-lang/Nim/issues/12379)
+* fix type's case in random.nim
+* Fixed "Update docs with a better way to signal unimplemented methods" [#10804](https://github.com/nim-lang/Nim/issues/10804)
+* Fixed "Nim language manual, push pragma is not explained well" [#10824](https://github.com/nim-lang/Nim/issues/10824)
+* Fixed "[regression] Importing more than one module with same name from different packages produce bad codegen" [#12420](https://github.com/nim-lang/Nim/issues/12420)
+* Namespace unittest enums to avoid name conflicts
+* Fixed "VM checks unsigned integers for overflow." [#12310](https://github.com/nim-lang/Nim/issues/12310)
+* Fixed "line directive is not generated for first line of function definition" [#12426](https://github.com/nim-lang/Nim/issues/12426)
+
+
+
+## Documentation improvements
+
+* threadpool: fix link in docs (#12258)
+* Fix spellings (#12277)
+* fix #12278, don't expose internal PCRE documentation
+* Fixed "Documentation of quitprocs is wrong" [#12279(https://github.com/nim-lang/Nim/issues/12279)
+* Fix typo in docs
+* Fix reference to parseSpec proc in readme
+* [doc/tut1] removed discard discussion in comments
+* Documentation improvements around the db interface 
+* Easier build instructions for windows - just run `build_all.bat`.
+* fix a few dead links and a missing sentence in documentation
+* Macro docs additions
+* Updated the code example in the os module to use better grammar.
+* Mention "lambdas" and `=>` in the manual
+* Better documentation on Garbage Collector
diff --git a/changelogs/changelog_1_2_0.md b/changelogs/changelog_1_2_0.md
new file mode 100644
index 000000000..1f76df0b4
--- /dev/null
+++ b/changelogs/changelog_1_2_0.md
@@ -0,0 +1,535 @@
+# v1.2.0 - 2020-04-02
+
+
+## Standard library additions and changes
+- Added overloaded `strformat.fmt` macro that use specified characters as
+  delimiter instead of '{' and '}'.
+- Added new procs in `tables.nim`: `OrderedTable.pop`, `CountTable.del`,
+  `CountTable.pop`, `Table.pop`.
+- Added `strtabs.clear` overload that reuses the existing mode.
+- Added `browsers.osOpen` const alias for the operating system specific *"open"* command.
+- Added `sugar.dup` for turning in-place algorithms like `sort` and `shuffle`
+  into operations that work on a copy of the data and return the mutated copy,
+  like the existing `sorted` does.
+- Added `sugar.collect` that does comprehension for seq/set/table collections.
+- Added `sugar.capture` for capturing some local loop variables when creating a
+  closure. This is an enhanced version of `closureScope`.
+- Added `typetraits.tupleLen` to get number of elements of a tuple/type tuple,
+  and `typetraits.get` to get the ith element of a type tuple.
+- Added `typetraits.genericParams` to return a tuple of generic params from a
+  generic instantiation.
+- `options` now treats `proc` like other pointer types, meaning `nil` proc variables
+  are converted to `None`.
+- Added `os.normalizePathEnd` for additional path sanitization.
+- Added `times.fromUnixFloat,toUnixFloat`, sub-second resolution versions of
+  `fromUnix`,`toUnixFloat`.
+- Added `wrapnils` module for chains of field-access and indexing where the LHS
+  can be nil. This simplifies code by reducing need for if-else branches around
+  intermediate maybe nil values. E.g. `echo ?.n.typ.kind`.
+- Added `minIndex`, `maxIndex` and `unzip` to the `sequtils` module.
+- Added `os.isRelativeTo` to tell whether a path is relative to another.
+- Added `resetOutputFormatters` to `unittest`.
+- Added `expectIdent` to the `macros` module.
+- Added `os.isValidFilename` that returns `true` if `filename` argument is valid
+  for cross-platform use.
+- Added `times.isLeapDay`.
+- `base64` adds URL-Safe Base64, implements RFC-4648 Section-7.
+- Added a new module, `std / compilesettings` for querying the compiler about
+  diverse configuration settings.
+- Added `net.getPeerCertificates` and `asyncnet.getPeerCertificates` for
+  retrieving the verified certificate chain of the peer we are connected to
+  through an SSL-wrapped `Socket`/`AsyncSocket`.
+- Added `browsers.openDefaultBrowser` without URL, implements IETF RFC-6694 Section-3.
+- Added `jsconsole.trace`, `jsconsole.table`, `jsconsole.exception` for JavaScript target.
+- Added `distinctBase` overload for values: `assert 12.MyInt.distinctBase == 12`
+- Added new module `std/stackframes`, in particular `setFrameMsg`, which enables
+  custom runtime annotation of stackframes, see #13351 for examples.
+  Turn on/off via `--stackTraceMsgs:on/off`.
+- Added `sequtils.countIt`, allowing for counting items using a predicate.
+- Added a `with` macro for easy function chaining that's available everywhere,
+  there is no need to concern your APIs with returning the first argument
+  to enable "chaining", instead use the dedicated macro `with` that
+  was designed for it. For example:
+
+```nim
+import std/with
+
+type
+  Foo = object
+    col, pos: string
+
+proc setColor(f: var Foo; r, g, b: int) = f.col = $(r, g, b)
+proc setPosition(f: var Foo; x, y: float) = f.pos = $(x, y)
+
+var f: Foo
+with(f, setColor(2, 3, 4), setPosition(0.0, 1.0))
+echo f
+```
+
+- `macros.newLit` now works for ref object types.
+- `macro pragmas` can now be used in type sections.
+- 5 new pragmas were added to Nim in order to make the upcoming tooling more
+  convenient to use. Nim compiler checks these pragmas for syntax but otherwise
+  ignores them. The pragmas are `requires`, `ensures`, `assume`, `assert`, `invariant`.
+- `system.writeFile` has been overloaded to also support `openarray[byte]`.
+- `asyncdispatch.drain` now properly takes into account `selector.hasPendingOperations`
+  and only returns once all pending async operations are guaranteed to have completed.
+- `sequtils.zip` now returns a sequence of anonymous tuples i.e. those tuples
+  now do not have fields named "a" and "b".
+- `distinctBase` has been moved from `sugar` to `typetraits` and now it is
+  implemented as compiler type trait instead of macro. `distinctBase` in sugar
+  module is now deprecated.
+- `CountTable.mget` has been removed from `tables.nim`. It didn't work, and it
+  was an oversight to be included in v1.0.
+- `tables.merge(CountTable, CountTable): CountTable` has been removed.
+  It didn't work well together with the existing inplace version of the same proc
+  (`tables.merge(var CountTable, CountTable)`).
+  It was an oversight to be included in v1.0.
+- `asyncdispatch.drain` now consistently uses the passed timeout value for all
+  iterations of the event loop, and not just the first iteration.
+  This is more consistent with the other asyncdispatch APIs, and allows
+  `asyncdispatch.drain` to be more efficient.
+- `base64.encode` and `base64.decode` were made faster by about 50%.
+- `htmlgen` adds [MathML](https://wikipedia.org/wiki/MathML) support
+  (ISO 40314).
+- `macros.eqIdent` is now invariant to export markers and backtick quotes.
+- `htmlgen.html` allows `lang` in the `<html>` tag and common valid attributes.
+- `macros.basename` and `basename=` got support for `PragmaExpr`,
+  so that an expression like `MyEnum {.pure.}` is handled correctly.
+- `httpclient.maxredirects` changed from `int` to `Natural`, because negative values
+  serve no purpose whatsoever.
+- `httpclient.newHttpClient` and `httpclient.newAsyncHttpClient` added `headers`
+  argument to set initial HTTP Headers, instead of a hardcoded empty `newHttpHeader()`.
+- `parseutils.parseUntil` has now a different behaviour if the `until` parameter is
+  empty. This was required for intuitive behaviour of the strscans module
+  (see bug #13605).
+- `strutils.formatFloat` with `precision = 0` has the same behavior in all
+  backends, and it is compatible with Python's behavior,
+  e.g. `formatFloat(3.14159, precision = 0)` is now `3`, not `3.`.
+- `times.parse` now only uses input to compute its result, and not `now`:
+  `parse("2020", "YYYY", utc())` is now `2020-01-01T00:00:00Z` instead of
+  `2020-03-02T00:00:00Z` if run on 03-02; it also doesn't crash anymore when
+  used on 29th, 30th, 31st of each month.
+- `httpcore.==(string, HttpCode)` is now deprecated due to lack of practical
+  usage. The `$` operator can be used to obtain the string form of `HttpCode`
+  for comparison if desired.
+- `std/oswalkdir` was buggy, it's now deprecated and reuses `std/os` procs.
+- `os.walkDir` and `os.walkDirRec` now have new flag, `checkDir` (default: false).
+  If it is set to true, it will throw if input dir is invalid instead of a noop
+  (which is the default behaviour, as it was before this change),
+  `os.walkDirRec` only throws if top-level dir is invalid, but ignores errors for
+  subdirs, otherwise it would be impossible to resume iteration.
+- The `FD` variant of `selector.unregister` for `ioselector_epoll` and
+  `ioselector_select` now properly handle the `Event.User` select event type.
+- `joinPath` path normalization when `/` is the first argument works correctly:
+  `assert "/" / "/a" == "/a"`. Fixed the edge case: `assert "" / "" == ""`.
+- `xmltree` now adds indentation consistently to child nodes for any number
+  of children nodes.
+- `os.splitPath()` behavior synchronized with `os.splitFile()` to return "/"
+  as the dir component of `/root_sub_dir` instead of the empty string.
+- The deprecated `lc` macro has been removed from `sugar`. It is now replaced
+  with the more powerful `collect` macro.
+- `os.relativePath("foo", "foo")` is now `"."`, not `""`, as `""` means invalid
+  path and shouldn't be conflated with `"."`; use `-d:nimOldRelativePathBehavior`
+  to restore the old behavior.
+- `os.joinPath(a, b)` now honors trailing slashes in `b` (or `a` if `b` = "").
+- `base64.encode` no longer supports `lineLen` and `newLine`.
+  Use `base64.encodeMime` instead.
+
+
+### Breaking changes
+
+- `net.newContext` now performs SSL Certificate checking on Linux and OSX.
+  Define `nimDisableCertificateValidation` to disable it globally.
+
+
+
+## Language changes
+
+- An `align` pragma can now be used for variables and object fields, similar
+  to the `alignas` declaration modifier in C/C++.
+- The `=sink` type bound operator is now optional. The compiler can now use a
+  combination of `=destroy` and `copyMem` to move objects efficiently.
+- Unsigned integer operators have been fixed to allow promotion of the first operand.
+- Conversions to unsigned integers are unchecked at runtime, imitating earlier Nim
+  versions. The documentation was improved to acknowledge this special case.
+  See https://github.com/nim-lang/RFCs/issues/175 for more details.
+- There is a new syntax for lvalue references: `var b {.byaddr.} = expr` enabled by
+  `import std/decls`.
+- `var a {.foo.}: MyType = expr` now lowers to `foo(a, MyType, expr)` for
+  non-builtin pragmas, enabling things like lvalue references (see `decls.byaddr`).
+
+
+
+## Compiler changes
+
+- The generated JS code uses spaces, instead of mixing spaces and tabs.
+- The Nim compiler now supports the ``--asm`` command option for easier
+  inspection of the produced assembler code.
+- The Nim compiler now supports a new pragma called ``.localPassc`` to
+  pass specific compiler options to the C(++) backend for the C(++) file
+  that was produced from the current Nim module.
+- The compiler now inferes "sink parameters". To disable this for a specific routine,
+  annotate it with `.nosinks`. To disable it for a section of code, use
+  `{.push sinkInference: off.}`...`{.pop.}`.
+- The compiler now supports a new switch `--panics:on` that turns runtime
+  errors like `IndexError` or `OverflowError` into fatal errors that **cannot**
+  be caught via Nim's `try` statement. `--panics:on` can improve the
+  runtime efficiency and code size of your program significantly.
+- The compiler now warns about inheriting directly from `system.Exception` as
+  this is **very bad** style. You should inherit from `ValueError`, `IOError`,
+  `OSError` or from a different specific exception type that inherits from
+  `CatchableError` and cannot be confused with a `Defect`.
+- The error reporting for Nim's effect system has been improved.
+- Implicit conversions for `const` behave correctly now, meaning that code like
+  `const SOMECONST = 0.int; procThatTakesInt32(SOMECONST)` will be illegal now.
+  Simply write `const SOMECONST = 0` instead.
+- The `{.dynlib.}` pragma is now required for exporting symbols when making
+  shared objects on POSIX and macOS, which make it consistent with the behavior
+  on Windows.
+- The compiler is now more strict about type conversions concerning proc
+  types: Type conversions cannot be used to hide `.raise` effects or side
+  effects, instead a `cast` must be used. With the flag `--useVersion:1.0` the
+  old behaviour is emulated.
+- The Nim compiler now implements a faster way to detect overflows based
+  on GCC's `__builtin_sadd_overflow` family of functions. (Clang also
+  supports these). Some versions of GCC lack this feature and unfortunately
+  we cannot detect this case reliably. So if you get compilation errors like
+  "undefined reference to `__builtin_saddll_overflow`" compile your programs
+  with `-d:nimEmulateOverflowChecks`.
+
+
+
+
+## Bugfixes
+
+- Fixed "`nimgrep --nocolor` is ignored on posix; should be instead: `--nimgrep --color=[auto]|true|false`"
+  ([#7591](https://github.com/nim-lang/Nim/issues/7591))
+- Fixed "Runtime index on const array (of converted obj) causes C-compiler error"
+  ([#10514](https://github.com/nim-lang/Nim/issues/10514))
+- Fixed "windows x86 with vcc compile error with "asmNoStackFrame""
+  ([#12298](https://github.com/nim-lang/Nim/issues/12298))
+- Fixed "[TODO] regression: Error: Locks requires --threads:on option"
+  ([#12330](https://github.com/nim-lang/Nim/issues/12330))
+- Fixed "Add --cc option to --help or --fullhelp output"
+  ([#12010](https://github.com/nim-lang/Nim/issues/12010))
+- Fixed "questionable `csize` definition in `system.nim`"
+  ([#12187](https://github.com/nim-lang/Nim/issues/12187))
+- Fixed "os.getAppFilename() returns incorrect results on OpenBSD"
+  ([#12389](https://github.com/nim-lang/Nim/issues/12389))
+- Fixed "HashSet[uint64] slow insertion depending on values"
+  ([#11764](https://github.com/nim-lang/Nim/issues/11764))
+- Fixed "Differences between compiling 'classic call syntax' vs 'method call syntax' ."
+  ([#12453](https://github.com/nim-lang/Nim/issues/12453))
+- Fixed "c -d:nodejs --> SIGSEGV: Illegal storage access"
+  ([#12502](https://github.com/nim-lang/Nim/issues/12502))
+- Fixed "Closure iterator crashes on --newruntime due to "dangling references""
+  ([#12443](https://github.com/nim-lang/Nim/issues/12443))
+- Fixed "No `=destroy` for elements of closure environments other than for latest devel --gc:destructors"
+  ([#12577](https://github.com/nim-lang/Nim/issues/12577))
+- Fixed "strutils:formatBiggestFloat() gives different results in JS mode"
+  ([#8242](https://github.com/nim-lang/Nim/issues/8242))
+- Fixed "Regression (devel): the new `csize_t` definition isn't consistently used, nor tested thoroughly..."
+  ([#12597](https://github.com/nim-lang/Nim/issues/12597))
+- Fixed "tables.take() is defined only for `Table` and missed for other table containers"
+  ([#12519](https://github.com/nim-lang/Nim/issues/12519))
+- Fixed "`pthread_key_t` errors on OpenBSD"
+  ([#12135](https://github.com/nim-lang/Nim/issues/12135))
+- Fixed "newruntime: simple seq pop at ct results in compile error"
+  ([#12644](https://github.com/nim-lang/Nim/issues/12644))
+- Fixed "[Windows] finish.exe C:\Users\<USERNAME>\.nimble\bin is not in your PATH environment variable."
+  ([#12319](https://github.com/nim-lang/Nim/issues/12319))
+- Fixed "Error with strformat + asyncdispatch + const"
+  ([#12612](https://github.com/nim-lang/Nim/issues/12612))
+- Fixed "MultipartData needs $"
+  ([#11863](https://github.com/nim-lang/Nim/issues/11863))
+- Fixed "Nim stdlib style issues with --styleCheck:error"
+  ([#12687](https://github.com/nim-lang/Nim/issues/12687))
+- Fixed "new $nimbleDir path substitution yields unexpected search paths"
+  ([#12767](https://github.com/nim-lang/Nim/issues/12767))
+- Fixed "Regression: inlined procs now get multiple rounds of destructor injection"
+  ([#12766](https://github.com/nim-lang/Nim/issues/12766))
+- Fixed "newruntime: compiler generates defective code"
+  ([#12669](https://github.com/nim-lang/Nim/issues/12669))
+- Fixed "broken windows modules path handling because of 'os.relativePath' breaking changes"
+  ([#12734](https://github.com/nim-lang/Nim/issues/12734))
+- Fixed "for loop tuple syntax not rendered correctly"
+  ([#12740](https://github.com/nim-lang/Nim/issues/12740))
+- Fixed "Crash when trying to use `type.name[0]`"
+  ([#12804](https://github.com/nim-lang/Nim/issues/12804))
+- Fixed "Enums should be considered Trivial types in Atomics"
+  ([#12812](https://github.com/nim-lang/Nim/issues/12812))
+- Fixed "Produce static/const initializations for variables when possible"
+  ([#12216](https://github.com/nim-lang/Nim/issues/12216))
+- Fixed "Assigning descriminator field leads to internal assert with --gc:destructors"
+  ([#12821](https://github.com/nim-lang/Nim/issues/12821))
+- Fixed "nimsuggest `use` command does not return all instances of symbol"
+  ([#12832](https://github.com/nim-lang/Nim/issues/12832))
+- Fixed "@[] is a problem for --gc:destructors"
+  ([#12820](https://github.com/nim-lang/Nim/issues/12820))
+- Fixed "Codegen ICE in allPathsAsgnResult"
+  ([#12827](https://github.com/nim-lang/Nim/issues/12827))
+- Fixed "seq[Object with ref and destructor type] doesn't work in old runtime"
+  ([#12882](https://github.com/nim-lang/Nim/issues/12882))
+- Fixed "Destructor not invoked because it is instantiated too late, old runtime"
+  ([#12883](https://github.com/nim-lang/Nim/issues/12883))
+- Fixed "The collect macro does not handle if/case correctly"
+  ([#12874](https://github.com/nim-lang/Nim/issues/12874))
+- Fixed "allow typed/untyped params in magic procs (even if not in stdlib)"
+  ([#12911](https://github.com/nim-lang/Nim/issues/12911))
+- Fixed "ARC/newruntime memory corruption"
+  ([#12899](https://github.com/nim-lang/Nim/issues/12899))
+- Fixed "tasyncclosestall.nim still flaky test: Address already in use"
+  ([#12919](https://github.com/nim-lang/Nim/issues/12919))
+- Fixed "newruntime and computed goto: variables inside the loop are in generated code uninitialised"
+  ([#12785](https://github.com/nim-lang/Nim/issues/12785))
+- Fixed "osx: dsymutil needs to be called for debug builds to keep debug info"
+  ([#12735](https://github.com/nim-lang/Nim/issues/12735))
+- Fixed "codegen ICE with ref objects, gc:destructors"
+  ([#12826](https://github.com/nim-lang/Nim/issues/12826))
+- Fixed "mutable iterator cannot yield named tuples"
+  ([#12945](https://github.com/nim-lang/Nim/issues/12945))
+- Fixed "parsecfg stores "\r\n" line breaks just as "\n""
+  ([#12970](https://github.com/nim-lang/Nim/issues/12970))
+- Fixed "db_postgres.getValue issues warning when no rows found"
+  ([#12973](https://github.com/nim-lang/Nim/issues/12973))
+- Fixed "ARC: Unpacking tuple with seq causes segfault"
+  ([#12989](https://github.com/nim-lang/Nim/issues/12989))
+- Fixed "ARC/newruntime: strutils.join on seq with only empty strings causes segfault"
+  ([#12965](https://github.com/nim-lang/Nim/issues/12965))
+- Fixed "regression (1.0.4): `{.push exportc.}` wrongly affects generic instantiations, causing codegen errors"
+  ([#12985](https://github.com/nim-lang/Nim/issues/12985))
+- Fixed "cdt, crash with --gc:arc, works fine with default gc"
+  ([#12978](https://github.com/nim-lang/Nim/issues/12978))
+- Fixed "ARC: No indexError thrown on out-of-bound seq access, SIGSEGV instead"
+  ([#12961](https://github.com/nim-lang/Nim/issues/12961))
+- Fixed "ARC/async: Returning in a try-block results in wrong codegen"
+  ([#12956](https://github.com/nim-lang/Nim/issues/12956))
+- Fixed "asm keyword is generating wrong output C code when --cc:tcc"
+  ([#12988](https://github.com/nim-lang/Nim/issues/12988))
+- Fixed "Destructor not invoked"
+  ([#13026](https://github.com/nim-lang/Nim/issues/13026))
+- Fixed "ARC/newruntime: Adding inherited var ref object to seq with base type causes segfault"
+  ([#12964](https://github.com/nim-lang/Nim/issues/12964))
+- Fixed "Style check error with JS compiler target"
+  ([#13032](https://github.com/nim-lang/Nim/issues/13032))
+- Fixed "regression(1.0.4): undeclared identifier: 'readLines'; plus another regression and bug"
+  ([#13013](https://github.com/nim-lang/Nim/issues/13013))
+- Fixed "regression(1.04) `invalid pragma: since` with nim js"
+  ([#12996](https://github.com/nim-lang/Nim/issues/12996))
+- Fixed "Sink to MemMove optimization in injectdestructors"
+  ([#13002](https://github.com/nim-lang/Nim/issues/13002))
+- Fixed "--gc:arc: `catch` doesn't work with exception subclassing"
+  ([#13072](https://github.com/nim-lang/Nim/issues/13072))
+- Fixed "nim c --gc:arc --exceptions:{setjmp,goto} incorrectly handles raise; `nim cpp --gc:arc` is ok"
+  ([#13070](https://github.com/nim-lang/Nim/issues/13070))
+- Fixed "typetraits feature request - get subtype of a generic type"
+  ([#6454](https://github.com/nim-lang/Nim/issues/6454))
+- Fixed "CountTable inconsistencies between keys() and len() after setting value to 0"
+  ([#12813](https://github.com/nim-lang/Nim/issues/12813))
+- Fixed "{.align.} pragma is not applied if there is a generic field"
+  ([#13122](https://github.com/nim-lang/Nim/issues/13122))
+- Fixed "ARC, finalizer, allow rebinding the same function multiple times"
+  ([#13112](https://github.com/nim-lang/Nim/issues/13112))
+- Fixed "`nim doc` treats `export localSymbol` incorrectly"
+  ([#13100](https://github.com/nim-lang/Nim/issues/13100))
+- Fixed "--gc:arc SIGSEGV (double free?)"
+  ([#13119](https://github.com/nim-lang/Nim/issues/13119))
+- Fixed "codegen bug with arc"
+  ([#13105](https://github.com/nim-lang/Nim/issues/13105))
+- Fixed "symbols not defined in the grammar"
+  ([#10665](https://github.com/nim-lang/Nim/issues/10665))
+- Fixed "[JS] Move is not defined"
+  ([#9674](https://github.com/nim-lang/Nim/issues/9674))
+- Fixed "[TODO] pathutils.`/` can return invalid AbsoluteFile"
+  ([#13121](https://github.com/nim-lang/Nim/issues/13121))
+- Fixed "regression(1.04) `nim doc main.nim` generates broken html (no css)"
+  ([#12998](https://github.com/nim-lang/Nim/issues/12998))
+- Fixed "Wrong supportsCopyMem on string in type section"
+  ([#13095](https://github.com/nim-lang/Nim/issues/13095))
+- Fixed "Arc, finalizer, out of memory"
+  ([#13157](https://github.com/nim-lang/Nim/issues/13157))
+- Fixed "`--genscript` messes up nimcache and future nim invocations"
+  ([#13144](https://github.com/nim-lang/Nim/issues/13144))
+- Fixed "--gc:arc with --exceptions:goto for "nim c" generate invalid c code"
+  ([#13186](https://github.com/nim-lang/Nim/issues/13186))
+- Fixed "[regression] duplicate member `_i1` codegen bug"
+  ([#13195](https://github.com/nim-lang/Nim/issues/13195))
+- Fixed "RTree investigations with valgrind for --gc:arc"
+  ([#13110](https://github.com/nim-lang/Nim/issues/13110))
+- Fixed "relativePath("foo", ".") returns wrong path"
+  ([#13211](https://github.com/nim-lang/Nim/issues/13211))
+- Fixed "asyncftpclient - problem with welcome.msg"
+  ([#4684](https://github.com/nim-lang/Nim/issues/4684))
+- Fixed "Unclear error message, lowest priority"
+  ([#13256](https://github.com/nim-lang/Nim/issues/13256))
+- Fixed "Channel messages are corrupted"
+  ([#13219](https://github.com/nim-lang/Nim/issues/13219))
+- Fixed "Codegen bug with exportc and case objects"
+  ([#13281](https://github.com/nim-lang/Nim/issues/13281))
+- Fixed "[bugfix] fix #11590: c compiler warnings silently ignored, giving undefined behavior"
+  ([#11591](https://github.com/nim-lang/Nim/issues/11591))
+- Fixed "[CI] tnetdial flaky test"
+  ([#13132](https://github.com/nim-lang/Nim/issues/13132))
+- Fixed "Cross-Compiling with -d:mingw fails to locate compiler under OSX"
+  ([#10717](https://github.com/nim-lang/Nim/issues/10717))
+- Fixed "`nim doc --project` broken with imports below main project file or duplicate names"
+  ([#13150](https://github.com/nim-lang/Nim/issues/13150))
+- Fixed "regression: isNamedTuple(MyGenericTuple[int]) is false, should be true"
+  ([#13349](https://github.com/nim-lang/Nim/issues/13349))
+- Fixed "--gc:arc codegen bug copying objects bound to C structs with missing C struct fields"
+  ([#13269](https://github.com/nim-lang/Nim/issues/13269))
+- Fixed "write requires conversion to string"
+  ([#13182](https://github.com/nim-lang/Nim/issues/13182))
+- Fixed "Some remarks to stdlib documentation"
+  ([#13352](https://github.com/nim-lang/Nim/issues/13352))
+- Fixed "a `check` in unittest generated by template doesn't show actual value"
+  ([#6736](https://github.com/nim-lang/Nim/issues/6736))
+- Fixed "Implicit return with case expression fails with 'var' return."
+  ([#3339](https://github.com/nim-lang/Nim/issues/3339))
+- Fixed "Segfault with closure on arc"
+  ([#13314](https://github.com/nim-lang/Nim/issues/13314))
+- Fixed "[Macro] Crash on malformed case statement with multiple else"
+  ([#13255](https://github.com/nim-lang/Nim/issues/13255))
+- Fixed "regression: `echo 'discard' | nim c -r -` generates a file '-' ; `-` should be treated specially"
+  ([#13374](https://github.com/nim-lang/Nim/issues/13374))
+- Fixed "on OSX, debugging (w gdb or lldb) a nim program crashes at the 1st call to `execCmdEx`"
+  ([#9634](https://github.com/nim-lang/Nim/issues/9634))
+- Fixed "Internal error in getTypeDescAux"
+  ([#13378](https://github.com/nim-lang/Nim/issues/13378))
+- Fixed "gc:arc mode breaks tuple let"
+  ([#13368](https://github.com/nim-lang/Nim/issues/13368))
+- Fixed "Nim compiler hangs for certain C/C++ compiler errors"
+  ([#8648](https://github.com/nim-lang/Nim/issues/8648))
+- Fixed "htmlgen does not support `data-*` attributes"
+  ([#13444](https://github.com/nim-lang/Nim/issues/13444))
+- Fixed "[gc:arc] setLen will cause string not to be null-terminated."
+  ([#13457](https://github.com/nim-lang/Nim/issues/13457))
+- Fixed "joinPath("", "") is "/" ; should be """
+  ([#13455](https://github.com/nim-lang/Nim/issues/13455))
+- Fixed "[CI] flaky test on windows: tests/osproc/texitcode.nim"
+  ([#13449](https://github.com/nim-lang/Nim/issues/13449))
+- Fixed "Casting to float32 on NimVM is broken"
+  ([#13479](https://github.com/nim-lang/Nim/issues/13479))
+- Fixed "`--hints:off` doesn't work (doesn't override ~/.config/nim.cfg)"
+  ([#8312](https://github.com/nim-lang/Nim/issues/8312))
+- Fixed "joinPath("", "") is "/" ; should be """
+  ([#13455](https://github.com/nim-lang/Nim/issues/13455))
+- Fixed "tables.values is broken"
+  ([#13496](https://github.com/nim-lang/Nim/issues/13496))
+- Fixed "global user config can override project specific config"
+  ([#9405](https://github.com/nim-lang/Nim/issues/9405))
+- Fixed "Non deterministic macros and id consistency problem"
+  ([#12627](https://github.com/nim-lang/Nim/issues/12627))
+- Fixed "try expression doesn't work with return on expect branch"
+  ([#13490](https://github.com/nim-lang/Nim/issues/13490))
+- Fixed "CI will break every 4 years on feb 28: times doesn't handle leap years properly"
+  ([#13543](https://github.com/nim-lang/Nim/issues/13543))
+- Fixed "[minor] `nimgrep --word` doesn't work with operators (eg misses  `1 +% 2`)"
+  ([#13528](https://github.com/nim-lang/Nim/issues/13528))
+- Fixed "`as` is usable as infix operator but its existence and precedence are not documented"
+  ([#13409](https://github.com/nim-lang/Nim/issues/13409))
+- Fixed "JSON unmarshalling drops seq's items"
+  ([#13531](https://github.com/nim-lang/Nim/issues/13531))
+- Fixed "os.joinPath returns wrong path when head ends '\' or '/' and tail starts '..'."
+  ([#13579](https://github.com/nim-lang/Nim/issues/13579))
+- Fixed "Block-local types with the same name lead to bad codegen (sighashes regression)"
+  ([#5170](https://github.com/nim-lang/Nim/issues/5170))
+- Fixed "tuple codegen error"
+  ([#12704](https://github.com/nim-lang/Nim/issues/12704))
+- Fixed "newHttpHeaders does not accept repeated headers"
+  ([#13573](https://github.com/nim-lang/Nim/issues/13573))
+- Fixed "regression: --incremental:on fails on simplest example"
+  ([#13319](https://github.com/nim-lang/Nim/issues/13319))
+- Fixed "strscan can't get value of last element in format"
+  ([#13605](https://github.com/nim-lang/Nim/issues/13605))
+- Fixed "hashes_examples crashes with "Bus Error" (unaligned access) on sparc64"
+  ([#12508](https://github.com/nim-lang/Nim/issues/12508))
+- Fixed "gc:arc bug with re-used `seq[T]`"
+  ([#13596](https://github.com/nim-lang/Nim/issues/13596))
+- Fixed "`raise CatchableError` is broken with --gc:arc  when throwing inside a proc"
+  ([#13599](https://github.com/nim-lang/Nim/issues/13599))
+- Fixed "cpp --gc:arc --exceptions:goto fails to raise with discard"
+  ([#13436](https://github.com/nim-lang/Nim/issues/13436))
+- Fixed "terminal doesn't compile with -d:useWinAnsi"
+  ([#13607](https://github.com/nim-lang/Nim/issues/13607))
+- Fixed "Parsing "sink ptr T" - region needs to be an object type"
+  ([#12757](https://github.com/nim-lang/Nim/issues/12757))
+- Fixed "gc:arc + threads:on + closures compilation error"
+  ([#13519](https://github.com/nim-lang/Nim/issues/13519))
+- Fixed "[ARC] segmentation fault"
+  ([#13240](https://github.com/nim-lang/Nim/issues/13240))
+- Fixed "times.toDateTime buggy on 29th, 30th and 31th of each month"
+  ([#13558](https://github.com/nim-lang/Nim/issues/13558))
+- Fixed "Deque misbehaves on VM"
+  ([#13310](https://github.com/nim-lang/Nim/issues/13310))
+- Fixed "Nimscript listFiles should throw exception when path is not found"
+  ([#12676](https://github.com/nim-lang/Nim/issues/12676))
+- Fixed "koch boot fails if even an empty config.nims is present in ~/.config/nims/ [devel regression]"
+  ([#13633](https://github.com/nim-lang/Nim/issues/13633))
+- Fixed "nim doc generates lots of false positive LockLevel warnings"
+  ([#13218](https://github.com/nim-lang/Nim/issues/13218))
+- Fixed "Arrays are passed by copy to iterators, causing crashes, unnecessary allocations and slowdowns"
+  ([#12747](https://github.com/nim-lang/Nim/issues/12747))
+- Fixed "Range types always uses signed integer as a base type"
+  ([#13646](https://github.com/nim-lang/Nim/issues/13646))
+- Fixed "Generate c code cannot compile with recent devel version"
+  ([#13645](https://github.com/nim-lang/Nim/issues/13645))
+- Fixed "[regression] VM: Error: cannot convert -1 to uint64"
+  ([#13661](https://github.com/nim-lang/Nim/issues/13661))
+- Fixed "Spurious raiseException(Exception) detected"
+  ([#13654](https://github.com/nim-lang/Nim/issues/13654))
+- Fixed "gc:arc memory leak"
+  ([#13659](https://github.com/nim-lang/Nim/issues/13659))
+- Fixed "Error: cannot convert -1 to uint (inside tuples)"
+  ([#13671](https://github.com/nim-lang/Nim/issues/13671))
+- Fixed "strformat issue with --gc:arc"
+  ([#13622](https://github.com/nim-lang/Nim/issues/13622))
+- Fixed "astToStr doesn't work inside generics"
+  ([#13524](https://github.com/nim-lang/Nim/issues/13524))
+- Fixed "oswalkdir.walkDirRec wont return folders"
+  ([#11458](https://github.com/nim-lang/Nim/issues/11458))
+- Fixed "`echo 'echo 1' | nim c -r -`  silently gives wrong results (nimBetterRun not updated for stdin)"
+  ([#13412](https://github.com/nim-lang/Nim/issues/13412))
+- Fixed "gc:arc destroys the global variable accidentally."
+  ([#13691](https://github.com/nim-lang/Nim/issues/13691))
+- Fixed "[minor] sigmatch errors should be sorted, for reproducible errors"
+  ([#13538](https://github.com/nim-lang/Nim/issues/13538))
+- Fixed "Exception when converting csize to clong"
+  ([#13698](https://github.com/nim-lang/Nim/issues/13698))
+- Fixed "ARC: variables are no copied on the thread spawn causing crashes"
+  ([#13708](https://github.com/nim-lang/Nim/issues/13708))
+- Fixed "Illegal distinct seq causes compiler crash"
+  ([#13720](https://github.com/nim-lang/Nim/issues/13720))
+- Fixed "cyclic seq definition crashes the compiler"
+  ([#13715](https://github.com/nim-lang/Nim/issues/13715))
+- Fixed "Iterator with openArray parameter make the argument evaluated many times"
+  ([#13417](https://github.com/nim-lang/Nim/issues/13417))
+- Fixed "net/asyncnet: Unable to access peer's certificate chain"
+  ([#13299](https://github.com/nim-lang/Nim/issues/13299))
+- Fixed "Accidentally "SIGSEGV: Illegal storage access" error after arc optimizations (#13325)"
+  ([#13709](https://github.com/nim-lang/Nim/issues/13709))
+- Fixed "Base64 Regression"
+  ([#13722](https://github.com/nim-lang/Nim/issues/13722))
+- Fixed "A regression (?) with --gc:arc and repr"
+  ([#13731](https://github.com/nim-lang/Nim/issues/13731))
+- Fixed "Internal compiler error when using the new variable pragmas"
+  ([#13737](https://github.com/nim-lang/Nim/issues/13737))
+- Fixed "bool conversion produces vcc 2019 warning at cpp compilation stage"
+  ([#13744](https://github.com/nim-lang/Nim/issues/13744))
+- Fixed "Compiler "does not detect" a type recursion error in the wrong code, remaining frozen"
+  ([#13763](https://github.com/nim-lang/Nim/issues/13763))
+- Fixed "[minor] regression: `Foo[0.0] is Foo[-0.0]` is now false"
+  ([#13730](https://github.com/nim-lang/Nim/issues/13730))
+- Fixed "`nim doc` - only whitespace on first line causes segfault"
+  ([#13631](https://github.com/nim-lang/Nim/issues/13631))
+- Fixed "hashset regression"
+  ([#13794](https://github.com/nim-lang/Nim/issues/13794))
+- Fixed "`os.getApplFreebsd` could return incorrect paths in the case of a long path"
+  ([#13806](https://github.com/nim-lang/Nim/issues/13806))
+- Fixed "Destructors are not inherited"
+  ([#13810](https://github.com/nim-lang/Nim/issues/13810))
+- Fixed "io.readLines AssertionError on devel"
+  ([#13829](https://github.com/nim-lang/Nim/issues/13829))
+- Fixed "exceptions:goto accidentally reset the variable during exception handling"
+  ([#13782](https://github.com/nim-lang/Nim/issues/13782))
diff --git a/changelogs/changelog_1_4_0.md b/changelogs/changelog_1_4_0.md
new file mode 100644
index 000000000..77f66ffde
--- /dev/null
+++ b/changelogs/changelog_1_4_0.md
@@ -0,0 +1,874 @@
+# v1.4.0 - 2020-10-16
+
+
+
+## Standard library additions and changes
+
+- Added some enhancements to `std/jsonutils` module.
+  * Added a possibility to deserialize JSON arrays directly to `HashSet` and
+    `OrderedSet` types and respectively to serialize those types to JSON arrays
+    via `jsonutils.fromJson` and `jsonutils.toJson` procedures.
+  * Added a possibility to deserialize JSON `null` objects to Nim option objects
+    and respectively to serialize Nim option object to JSON object if `isSome`
+    or to JSON null object if `isNone` via `jsonutils.fromJson` and
+    `jsonutils.toJson` procedures.
+  * Added a `Joptions` parameter to `jsonutils.fromJson` currently
+    containing two boolean options `allowExtraKeys` and `allowMissingKeys`.
+    - If `allowExtraKeys` is `true` Nim's object to which the JSON is parsed is
+      not required to have a field for every JSON key.
+    - If `allowMissingKeys` is `true` Nim's object to which JSON is parsed is
+      allowed to have fields without corresponding JSON keys.
+- Added `bindParams`, `bindParam` to `db_sqlite` for binding parameters into a `SqlPrepared` statement.
+- Added `tryInsert`,`insert` procs to `db_*` libs which accept primary key column name.
+- Added `xmltree.newVerbatimText` support create `style`'s,`script`'s text.
+- `uri` module now implements RFC-2397.
+- Added [DOM Parser](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser)
+  to the `dom` module for the JavaScript target.
+- The default hash for `Ordinal` has changed to something more bit-scrambling.
+  `import hashes; proc hash(x: myInt): Hash = hashIdentity(x)` recovers the old
+  one in an instantiation context while `-d:nimIntHash1` recovers it globally.
+- `deques.peekFirst` and `deques.peekLast` now have `var Deque[T] -> var T` overloads.
+- File handles created from high-level abstractions in the stdlib will no longer
+  be inherited by child processes. In particular, these modules are affected:
+  `asyncdispatch`, `asyncnet`, `system`, `nativesockets`, `net` and `selectors`.
+
+  For `asyncdispatch`, `asyncnet`, `net` and `nativesockets`, an `inheritable`
+  flag has been added to all `proc`s that create sockets, allowing the user to
+  control whether the resulting socket is inheritable. This flag is provided to
+  ease the writing of multi-process servers, where sockets inheritance is
+  desired.
+
+  For a transition period, define `nimInheritHandles` to enable file handle
+  inheritance by default. This flag does **not** affect the `selectors` module
+  due to the differing semantics between operating systems.
+
+  `asyncdispatch.setInheritable`, `system.setInheritable` and
+  `nativesockets.setInheritable` are also introduced for setting file handle or
+  socket inheritance. Not all platforms have these `proc`s defined.
+
+- The file descriptors created for internal bookkeeping by `ioselector_kqueue`
+  and `ioselector_epoll` will no longer be leaked to child processes.
+
+- `strutils.formatFloat` with `precision = 0` has been restored to the version
+  1 behaviour that produces a trailing dot, e.g. `formatFloat(3.14159, precision = 0)`
+  is now `3.`, not `3`.
+- Added `commonPrefixLen` to `critbits`.
+
+- `relativePath(rel, abs)` and `relativePath(abs, rel)` used to silently give wrong results
+  (see #13222); instead they now use `getCurrentDir` to resolve those cases,
+  and this can now throw in edge cases where `getCurrentDir` throws.
+  `relativePath` also now works for js with `-d:nodejs`.
+
+- JavaScript and NimScript standard library changes: `streams.StringStream` is
+  now supported in JavaScript, with the limitation that any buffer `pointer`s
+  used must be castable to `ptr string`, any incompatible pointer type will not
+  work. The `lexbase` and `streams` modules used to fail to compile on
+  NimScript due to a bug, but this has been fixed.
+
+  The following modules now compile on both JS and NimScript: `parsecsv`,
+  `parsecfg`, `parsesql`, `xmlparser`, `htmlparser` and `ropes`. Additionally
+  supported for JS is `cstrutils.startsWith` and `cstrutils.endsWith`, for
+  NimScript: `json`, `parsejson`, `strtabs` and `unidecode`.
+
+- Added `streams.readStr` and `streams.peekStr` overloads to
+  accept an existing string to modify, which avoids memory
+  allocations, similar to `streams.readLine` (#13857).
+
+- Added high-level `asyncnet.sendTo` and `asyncnet.recvFrom` UDP functionality.
+
+- `dollars.$` now works for unsigned ints with `nim js`.
+
+- Improvements to the `bitops` module, including bitslices, non-mutating versions
+  of the original masking functions, `mask`/`masked`, and varargs support for
+  `bitand`, `bitor`, and `bitxor`.
+
+- `sugar.=>` and `sugar.->` changes: Previously `(x, y: int)` was transformed
+  into `(x: auto, y: int)`, it now becomes `(x: int, y: int)` for consistency
+  with regular proc definitions (although you cannot use semicolons).
+
+  Pragmas and using a name are now allowed on the lefthand side of `=>`. Here
+  is an example of these changes:
+  ```nim
+  import sugar
+
+  foo(x, y: int) {.noSideEffect.} => x + y
+
+  # is transformed into
+
+  proc foo(x: int, y: int): auto {.noSideEffect.} = x + y
+  ```
+
+- The fields of `times.DateTime` are now private, and are accessed with getters and deprecated setters.
+
+- The `times` module now handles the default value for `DateTime` more consistently.
+  Most procs raise an assertion error when given
+  an uninitialized `DateTime`, the exceptions are `==` and `$` (which returns `"Uninitialized DateTime"`).
+  The proc `times.isInitialized` has been added which can be used to check if
+  a `DateTime` has been initialized.
+
+- Fix a bug where calling `close` on io streams in `osproc.startProcess` was a noop and led to
+  hangs if a process had both reads from stdin and writes (e.g. to stdout).
+
+- The callback that is passed to `system.onThreadDestruction` must now be `.raises: []`.
+- The callback that is assigned to `system.onUnhandledException` must now be `.gcsafe`.
+
+- `osproc.execCmdEx` now takes an optional `input` for stdin, `workingDir` and `env`
+  parameters.
+
+- Added a `ssl_config` module containing lists of secure ciphers as recommended by
+  [Mozilla OpSec](https://wiki.mozilla.org/Security/Server_Side_TLS)
+
+- `net.newContext` now defaults to the list of ciphers targeting
+  ["Intermediate compatibility"](https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28recommended.29)
+  per Mozilla's recommendation instead of `ALL`. This change should protect
+  users from the use of weak and insecure ciphers while still provides
+  adequate compatibility with the majority of the Internet.
+
+- A new module `std/jsonutils` with hookable `jsonTo,toJson,fromJson` operations for json
+  serialization/deserialization of custom types was added.
+
+- A new proc `heapqueue.find[T](heap: HeapQueue[T], x: T): int` to get index of element ``x``
+  was added.
+- Added `rstgen.rstToLatex` a convenience proc for `renderRstToOut` and `initRstGenerator`.
+- Added `os.normalizeExe`.
+- `macros.newLit` now preserves named vs unnamed tuples.
+- Added `random.gauss`, that uses the ratio of uniforms method of sampling from a Gaussian distribution.
+- Added `typetraits.elementType` to get the element type of an iterable.
+- `typetraits.$` changes: `$(int,)` is now `"(int,)"` instead of `"(int)"`;
+  `$tuple[]` is now `"tuple[]"` instead of `"tuple"`;
+  `$((int, float), int)` is now `"((int, float), int)"` instead of `"(tuple of (int, float), int)"`
+- Added `macros.extractDocCommentsAndRunnables` helper.
+
+- `strformat.fmt` and `strformat.&` support `specifier =`. `fmt"{expr=}"` now
+  expands to `fmt"expr={expr}"`.
+- Deprecations: instead of `os.existsDir` use `dirExists`, instead of `os.existsFile` use `fileExists`.
+
+- Added the `jsre` module, [Regular Expressions for the JavaScript target.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions).
+- Made `maxLines` argument `Positive` in `logging.newRollingFileLogger`,
+  because negative values will result in a new file being created for each logged
+  line which doesn't make sense.
+- Changed `log` in `logging` to use proper log level for JavaScript,
+  e.g. `debug` uses `console.debug`, `info` uses `console.info`, `warn` uses `console.warn`, etc.
+- Tables, HashSets, SharedTables and deques don't require anymore that the passed
+  initial size must be a power of two - this is done internally.
+  Proc `rightSize` for Tables and HashSets is deprecated, as it is not needed anymore.
+  `CountTable.inc` takes `val: int` again not `val: Positive`; i.e. it can "count down" again.
+- Removed deprecated symbols from `macros` module, some of which were deprecated already in `0.15`.
+- Removed `sugar.distinctBase`, deprecated since `0.19`. Use `typetraits.distinctBase`.
+- `asyncdispatch.PDispatcher.handles` is exported so that an external low-level libraries can access it.
+
+- `std/with`, `sugar.dup` now support object field assignment expressions:
+  ```nim
+  import std/with
+
+  type Foo = object
+    x, y: int
+
+  var foo = Foo()
+  with foo:
+    x = 10
+    y = 20
+
+  echo foo
+  ```
+
+- Proc `math.round` is no longer deprecated. The advice to use `strformat` instead
+  cannot be applied to every use case. The limitations and the (lack of) reliability
+  of `round` are well documented.
+
+- Added `getprotobyname` to `winlean`. Added `getProtoByname` to `nativesockets` which returns a protocol code
+  from the database that matches the protocol `name`.
+
+- Added missing attributes and methods to `dom.Navigator` like `deviceMemory`, `onLine`, `vibrate()`, etc.
+
+- Added `strutils.indentation` and `strutils.dedent` which enable indented string literals:
+  ```nim
+  import strutils
+  echo dedent """
+    This
+      is
+        cool!
+    """
+  ```
+
+- Added `initUri(isIpv6: bool)` to `uri` module, now `uri` supports parsing ipv6 hostname.
+
+- Added `readLines(p: Process)` to `osproc`.
+
+- Added the below `toX` procs for collections. The usage is similar to procs such as
+  `sets.toHashSet` and `tables.toTable`. Previously, it was necessary to create the
+  respective empty collection and add items manually.
+    * `critbits.toCritBitTree`, which creates a `CritBitTree` from an `openArray` of
+       items or an `openArray` of pairs.
+    * `deques.toDeque`, which creates a `Deque` from an `openArray`.
+    * `heapqueue.toHeapQueue`, which creates a `HeapQueue` from an `openArray`.
+    * `intsets.toIntSet`, which creates an `IntSet` from an `openArray`.
+
+- Added `progressInterval` argument to `asyncftpclient.newAsyncFtpClient` to control the interval
+  at which progress callbacks are called.
+
+- Added `os.copyFileToDir`.
+
+## Language changes
+
+- The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts
+  `wasMoved` calls where needed.
+- The `=` hook is now called `=copy` for clarity. The old name `=` is still available so there
+  is no need to update your code. This change was backported to 1.2 too so you can use the
+  more readable `=copy` without loss of compatibility.
+
+- In the newruntime it is now allowed to assign to the discriminator field
+  without restrictions as long as the case object doesn't have a custom destructor.
+  The discriminator value doesn't have to be a constant either. If you have a
+  custom destructor for a case object and you do want to freely assign discriminator
+  fields, it is recommended to refactor the object into 2 objects like this:
+
+  ```nim
+  type
+    MyObj = object
+      case kind: bool
+      of true: y: ptr UncheckedArray[float]
+      of false: z: seq[int]
+
+  proc `=destroy`(x: MyObj) =
+    if x.kind and x.y != nil:
+      deallocShared(x.y)
+  ```
+  Refactor into:
+  ```nim
+  type
+    MySubObj = object
+      val: ptr UncheckedArray[float]
+    MyObj = object
+      case kind: bool
+      of true: y: MySubObj
+      of false: z: seq[int]
+
+  proc `=destroy`(x: MySubObj) =
+    if x.val != nil:
+      deallocShared(x.val)
+  ```
+- `getImpl` on enum type symbols now returns field syms instead of idents. This helps
+  with writing typed macros. The old behavior for backwards compatibility can be restored
+  with `--useVersion:1.0`.
+- The typed AST for proc headers will now have the arguments be syms instead of idents.
+  This helps with writing typed macros. The old behaviour for backwards compatibility can
+  be restored with `--useVersion:1.0`.
+- ``let`` statements can now be used without a value if declared with
+  ``importc``/``importcpp``/``importjs``/``importobjc``.
+- The keyword `from` is now usable as an operator.
+- Exceptions inheriting from `system.Defect` are no longer tracked with
+  the `.raises: []` exception tracking mechanism. This is more consistent with the
+  built-in operations. The following always used to compile (and still does):
+  ```nim
+  proc mydiv(a, b): int {.raises: [].} =
+    a div b # can raise an DivByZeroDefect
+  ```
+
+  Now also this compiles:
+  ```nim
+  proc mydiv(a, b): int {.raises: [].} =
+    if b == 0: raise newException(DivByZeroDefect, "division by zero")
+    else: result = a div b
+  ```
+
+  The reason for this is that `DivByZeroDefect` inherits from `Defect` and
+  with `--panics:on` `Defects` become unrecoverable errors.
+
+- Added the `thiscall` calling convention as specified by Microsoft, mostly for hooking purposes.
+- Deprecated the `{.unroll.}` pragma, because it was always ignored by the compiler anyway.
+- Removed the deprecated `strutils.isNilOrWhitespace`.
+- Removed the deprecated `sharedtables.initSharedTable`.
+- Removed the deprecated `asyncdispatch.newAsyncNativeSocket`.
+- Removed the deprecated `dom.releaseEvents` and `dom.captureEvents`.
+
+- Removed `sharedlist.initSharedList`, was deprecated and produces undefined behaviour.
+
+- There is a new experimental feature called "strictFuncs" which makes the definition of
+  `.noSideEffect` stricter. [See here](manual_experimental.html#stricts-funcs)
+  for more information.
+
+- "for-loop macros" (see [the manual](manual.html#macros-for-loop-macros)) are no longer
+  an experimental feature. In other words, you don't have to write pragma
+  `{.experimental: "forLoopMacros".}` if you want to use them.
+
+- Added the ``.noalias`` pragma. It is mapped to C's ``restrict`` keyword for the increased
+  performance this keyword can enable.
+
+- `items` no longer compiles with enums with holes as its behavior was error prone, see #14004.
+- `system.deepcopy` has to be enabled explicitly for `--gc:arc` and `--gc:orc` via
+  `--deepcopy:on`.
+
+- Added the `std/effecttraits` module for introspection of the inferred effects.
+  We hope this enables `async` macros that are precise about the possible exceptions that
+  can be raised.
+- The pragma blocks `{.gcsafe.}: ...` and `{.noSideEffect.}: ...` can now also be
+  written as `{.cast(gcsafe).}: ...` and `{.cast(noSideEffect).}: ...`. This is the new
+  preferred way of writing these, emphasizing their unsafe nature.
+
+
+## Compiler changes
+
+- Specific warnings can now be turned into errors via `--warningAsError[X]:on|off`.
+- The `define` and `undef` pragmas have been de-deprecated.
+- New command: `nim r main.nim [args...]` which compiles and runs main.nim, and implies `--usenimcache`
+  so that the output is saved to $nimcache/main$exeExt, using the same logic as `nim c -r` to
+  avoid recompilations when sources don't change.
+  Example:
+  ```bash
+  nim r compiler/nim.nim --help # only compiled the first time
+  echo 'import os; echo getCurrentCompilerExe()' | nim r - # this works too
+  nim r compiler/nim.nim --fullhelp # no recompilation
+  nim r --nimcache:/tmp main # binary saved to /tmp/main
+  ```
+- `--hint:processing` is now supported and means `--hint:processing:on`
+  (likewise with other hints and warnings), which is consistent with all other bool flags.
+  (since 1.3.3).
+- `nim doc -r main` and `nim rst2html -r main` now call `openDefaultBrowser`.
+- Added the new hint `--hint:msgOrigin` will show where a compiler msg (hint|warning|error)
+  was generated; this helps in particular when it's non obvious where it came from
+  either because multiple locations generate the same message, or because the
+  message involves runtime formatting.
+- Added the new flag `--backend:js|c|cpp|objc` (or -b:js etc), to change the backend; can be
+  used with any command (e.g. nim r, doc, check etc); safe to re-assign.
+- Added the new flag `--doccmd:cmd` to pass additional flags for runnableExamples,
+  e.g.: `--doccmd:-d:foo --threads`
+  use `--doccmd:skip` to skip runnableExamples and rst test snippets.
+- Added the new flag `--usenimcache` to output binary files to nimcache.
+- `runnableExamples "-b:cpp -r:off": code` is now supported, allowing to override
+  how an example is compiled and run, for example to change the backend.
+- `nim doc` now outputs under `$projectPath/htmldocs` when `--outdir` is unspecified
+  (with or without `--project`); passing `--project` now automatically generates
+  an index and enables search.
+  See [docgen](docgen.html#introduction-quick-start) for details.
+- Removed the `--oldNewlines` switch.
+- Removed the `--laxStrings` switch for mutating the internal zero terminator on strings.
+- Removed the `--oldast` switch.
+- Removed the `--oldgensym` switch.
+- `$getType(untyped)` is now "untyped" instead of "expr", `$getType(typed)` is
+  now "typed" instead of "stmt".
+- Sink inference is now disabled per default and has to enabled explicitly via
+  `--sinkInference:on`. *Note*: For the standard library sink inference remains
+  enabled. This change is most relevant for the `--gc:arc`, `--gc:orc` memory
+  management modes.
+
+
+## Tool changes
+
+- `nimsuggest` now returns both the forward declaration and the
+  implementation location upon a `def` query. Previously the behavior was
+  to return the forward declaration only.
+
+
+## Bugfixes
+
+- Fixed "repr() not available for uint{,8,16,32,64} under --gc:arc"
+  ([#13872](https://github.com/nim-lang/Nim/issues/13872))
+- Fixed "Critical: 1 completed Future, multiple await: Only 1 await will be awakened (the last one)"
+  ([#13889](https://github.com/nim-lang/Nim/issues/13889))
+- Fixed "crash on openarray interator with argument in stmtListExpr"
+  ([#13739](https://github.com/nim-lang/Nim/issues/13739))
+- Fixed "Some compilers on Windows don't work"
+  ([#13910](https://github.com/nim-lang/Nim/issues/13910))
+- Fixed "httpclient hangs if it recieves an HTTP 204 (No Content)"
+  ([#13894](https://github.com/nim-lang/Nim/issues/13894))
+- Fixed ""distinct uint64" type corruption on 32-bit, when using {.borrow.} operators"
+  ([#13902](https://github.com/nim-lang/Nim/issues/13902))
+- Fixed "Regression: impossible to use typed pragmas with proc types"
+  ([#13909](https://github.com/nim-lang/Nim/issues/13909))
+- Fixed "openssl wrapper corrupts stack on OpenSSL 1.1.1f + Android"
+  ([#13903](https://github.com/nim-lang/Nim/issues/13903))
+- Fixed "C compile error with --gc:arc on version 1.2.0 "unknown type name 'TGenericSeq'"
+  ([#13863](https://github.com/nim-lang/Nim/issues/13863))
+- Fixed "var return type for proc doesn't work at c++ backend"
+  ([#13848](https://github.com/nim-lang/Nim/issues/13848))
+- Fixed "TimeFormat() should raise an error but craches at compilation time"
+  ([#12864](https://github.com/nim-lang/Nim/issues/12864))
+- Fixed "gc:arc cannot fully support threadpool with FlowVar"
+  ([#13781](https://github.com/nim-lang/Nim/issues/13781))
+- Fixed "simple 'var openarray[char]' assignment crash when the openarray source is a local string and using gc:arc"
+  ([#14003](https://github.com/nim-lang/Nim/issues/14003))
+- Fixed "Cant use expressions with `when` in `type` sections."
+  ([#14007](https://github.com/nim-lang/Nim/issues/14007))
+- Fixed "`for a in MyEnum` gives incorrect results with enum with holes"
+  ([#14001](https://github.com/nim-lang/Nim/issues/14001))
+- Fixed "Trivial crash"
+  ([#12741](https://github.com/nim-lang/Nim/issues/12741))
+- Fixed "Enum with holes cannot be used as Table index"
+  ([#12834](https://github.com/nim-lang/Nim/issues/12834))
+- Fixed "spawn proc that uses typedesc crashes the compiler"
+  ([#14014](https://github.com/nim-lang/Nim/issues/14014))
+- Fixed "Docs Search `Results` box styling is not Dark Mode Friendly"
+  ([#13972](https://github.com/nim-lang/Nim/issues/13972))
+- Fixed "--gc:arc -d:useSysAssert undeclared identifier `cstderr` with newSeq"
+  ([#14038](https://github.com/nim-lang/Nim/issues/14038))
+- Fixed "issues in the manual"
+  ([#12486](https://github.com/nim-lang/Nim/issues/12486))
+- Fixed "Annoying warning: inherit from a more precise exception type like ValueError, IOError or OSError [InheritFromException]"
+  ([#14052](https://github.com/nim-lang/Nim/issues/14052))
+- Fixed "relativePath("foo", "/") and relativePath("/", "foo") is wrong"
+  ([#13222](https://github.com/nim-lang/Nim/issues/13222))
+- Fixed "[regression] `parseEnum` does not work anymore for enums with holes"
+  ([#14030](https://github.com/nim-lang/Nim/issues/14030))
+- Fixed "Exception types in the stdlib should inherit from `CatchableError` or `Defect`, not `Exception`"
+  ([#10288](https://github.com/nim-lang/Nim/issues/10288))
+- Fixed "Make debugSend and debugRecv procs public in smtp.nim"
+  ([#12189](https://github.com/nim-lang/Nim/issues/12189))
+- Fixed "xmltree need add raw text, when add style element"
+  ([#14064](https://github.com/nim-lang/Nim/issues/14064))
+- Fixed "raises requirement does not propagate to derived methods"
+  ([#8481](https://github.com/nim-lang/Nim/issues/8481))
+- Fixed "tests/stdlib/tgetaddrinfo.nim fails on NetBSD"
+  ([#14091](https://github.com/nim-lang/Nim/issues/14091))
+- Fixed "tests/niminaction/Chapter8/sdl/sdl_test.nim fails on NetBSD"
+  ([#14088](https://github.com/nim-lang/Nim/issues/14088))
+- Fixed "Incorrect escape sequence for example in jsffi library documentation"
+  ([#14110](https://github.com/nim-lang/Nim/issues/14110))
+- Fixed "HCR: Can not link exported const, in external library"
+  ([#13915](https://github.com/nim-lang/Nim/issues/13915))
+- Fixed "Cannot import std/unidecode"
+  ([#14112](https://github.com/nim-lang/Nim/issues/14112))
+- Fixed "macOS: dsymutil should not be called on static libraries"
+  ([#14132](https://github.com/nim-lang/Nim/issues/14132))
+- Fixed "nim jsondoc -o:doc.json filename.nim fails when sequences without a type are used"
+  ([#14066](https://github.com/nim-lang/Nim/issues/14066))
+- Fixed "algorithm.sortedByIt template corrupts tuple input under --gc:arc"
+  ([#14079](https://github.com/nim-lang/Nim/issues/14079))
+- Fixed "Invalid C code with lvalue conversion"
+  ([#14160](https://github.com/nim-lang/Nim/issues/14160))
+- Fixed "strformat: doc example fails"
+  ([#14054](https://github.com/nim-lang/Nim/issues/14054))
+- Fixed "Nim doc fail to run for nim 1.2.0 (nim 1.0.4 is ok)"
+  ([#13986](https://github.com/nim-lang/Nim/issues/13986))
+- Fixed "Exception when converting csize to clong"
+  ([#13698](https://github.com/nim-lang/Nim/issues/13698))
+- Fixed "[Documentation] overloading using named arguments works but is not documented"
+  ([#11932](https://github.com/nim-lang/Nim/issues/11932))
+- Fixed "import os + use of existsDir/dirExists/existsFile/fileExists/findExe in config.nims causes "ambiguous call' error"
+  ([#14142](https://github.com/nim-lang/Nim/issues/14142))
+- Fixed "import os + use of existsDir/dirExists/existsFile/fileExists/findExe in config.nims causes "ambiguous call' error"
+  ([#14142](https://github.com/nim-lang/Nim/issues/14142))
+- Fixed "runnableExamples doc gen crashes compiler with `except Exception as e` syntax"
+  ([#14177](https://github.com/nim-lang/Nim/issues/14177))
+- Fixed "[ARC] Segfault with cyclic references (?)"
+  ([#14159](https://github.com/nim-lang/Nim/issues/14159))
+- Fixed "Semcheck regression when accessing a static parameter in proc"
+  ([#14136](https://github.com/nim-lang/Nim/issues/14136))
+- Fixed "iterator walkDir doesn't work with -d:useWinAnsi"
+  ([#14201](https://github.com/nim-lang/Nim/issues/14201))
+- Fixed "cas is wrong for tcc"
+  ([#14151](https://github.com/nim-lang/Nim/issues/14151))
+- Fixed "proc execCmdEx doesn't work with -d:useWinAnsi"
+  ([#14203](https://github.com/nim-lang/Nim/issues/14203))
+- Fixed "Use -d:nimEmulateOverflowChecks by default?"
+  ([#14209](https://github.com/nim-lang/Nim/issues/14209))
+- Fixed "Old sequences with destructor objects bug"
+  ([#14217](https://github.com/nim-lang/Nim/issues/14217))
+- Fixed "[ARC] ICE when changing the discriminant of a return value"
+  ([#14244](https://github.com/nim-lang/Nim/issues/14244))
+- Fixed "[ARC] ICE with static objects"
+  ([#14236](https://github.com/nim-lang/Nim/issues/14236))
+- Fixed "[ARC] "internal error: environment misses: a" in a finalizer"
+  ([#14243](https://github.com/nim-lang/Nim/issues/14243))
+- Fixed "[ARC] compile failure using repr with object containing `ref seq[string]`"
+  ([#14270](https://github.com/nim-lang/Nim/issues/14270))
+- Fixed "[ARC] implicit move on last use happening on non-last use"
+  ([#14269](https://github.com/nim-lang/Nim/issues/14269))
+- Fixed "[ARC] Compiler crash with a recursive non-ref object variant"
+  ([#14294](https://github.com/nim-lang/Nim/issues/14294))
+- Fixed "htmlparser.parseHtml behaves differently using --gc:arc or --gc:orc"
+  ([#13946](https://github.com/nim-lang/Nim/issues/13946))
+- Fixed "Invalid return value of openProcess is NULL rather than INVALID_HANDLE_VALUE(-1) in windows"
+  ([#14289](https://github.com/nim-lang/Nim/issues/14289))
+- Fixed "ARC codegen bug with inline iterators"
+  ([#14219](https://github.com/nim-lang/Nim/issues/14219))
+- Fixed "Building koch on OpenBSD fails unless the Nim directory is in `$PATH`"
+  ([#13758](https://github.com/nim-lang/Nim/issues/13758))
+- Fixed "[gc:arc] case object assignment SIGSEGV: destroy not called for primitive type "
+  ([#14312](https://github.com/nim-lang/Nim/issues/14312))
+- Fixed "Crash when using thread and --gc:arc "
+  ([#13881](https://github.com/nim-lang/Nim/issues/13881))
+- Fixed "Getting "Warning: Cannot prove that 'result' is initialized" for an importcpp'd proc with `var T` return type"
+  ([#14314](https://github.com/nim-lang/Nim/issues/14314))
+- Fixed "`nim cpp -r --gc:arc` segfaults on caught AssertionError"
+  ([#13071](https://github.com/nim-lang/Nim/issues/13071))
+- Fixed "tests/async/tasyncawait.nim is recently very flaky"
+  ([#14320](https://github.com/nim-lang/Nim/issues/14320))
+- Fixed "Documentation nonexistent quitprocs module"
+  ([#14331](https://github.com/nim-lang/Nim/issues/14331))
+- Fixed "SIGSEV encountered when creating threads in a loop w/ --gc:arc"
+  ([#13935](https://github.com/nim-lang/Nim/issues/13935))
+- Fixed "nim-gdb is missing from all released packages"
+  ([#13104](https://github.com/nim-lang/Nim/issues/13104))
+- Fixed "sysAssert error with gc:arc on 3 line program"
+  ([#13862](https://github.com/nim-lang/Nim/issues/13862))
+- Fixed "compiler error with inline async proc and pragma"
+  ([#13998](https://github.com/nim-lang/Nim/issues/13998))
+- Fixed "[ARC] Compiler crash when adding to a seq[ref Object]"
+  ([#14333](https://github.com/nim-lang/Nim/issues/14333))
+- Fixed "nimvm: sysFatal: unhandled exception: 'sons' is not accessible using discriminant 'kind' of type 'TNode' [FieldError]"
+  ([#14340](https://github.com/nim-lang/Nim/issues/14340))
+- Fixed "[Regression] karax events are not firing "
+  ([#14350](https://github.com/nim-lang/Nim/issues/14350))
+- Fixed "odbcsql module has some wrong integer types"
+  ([#9771](https://github.com/nim-lang/Nim/issues/9771))
+- Fixed "db_sqlite needs sqlPrepared"
+  ([#13559](https://github.com/nim-lang/Nim/issues/13559))
+- Fixed "[Regression] `createThread` is not GC-safe"
+  ([#14370](https://github.com/nim-lang/Nim/issues/14370))
+- Fixed "Broken example on hot code reloading"
+  ([#14380](https://github.com/nim-lang/Nim/issues/14380))
+- Fixed "runnableExamples block with `except` on specified error fails with `nim doc`"
+  ([#12746](https://github.com/nim-lang/Nim/issues/12746))
+- Fixed "compiler as a library: findNimStdLibCompileTime fails to find system.nim"
+  ([#12293](https://github.com/nim-lang/Nim/issues/12293))
+- Fixed "5 bugs with importcpp exceptions"
+  ([#14369](https://github.com/nim-lang/Nim/issues/14369))
+- Fixed "Docs shouldn't collapse pragmas inside runnableExamples/code blocks"
+  ([#14174](https://github.com/nim-lang/Nim/issues/14174))
+- Fixed "Bad codegen/emit for hashes.hiXorLo in some contexts."
+  ([#14394](https://github.com/nim-lang/Nim/issues/14394))
+- Fixed "Boehm GC does not scan thread-local storage"
+  ([#14364](https://github.com/nim-lang/Nim/issues/14364))
+- Fixed "RVO not exception safe"
+  ([#14126](https://github.com/nim-lang/Nim/issues/14126))
+- Fixed "runnableExamples that are only compiled"
+  ([#10731](https://github.com/nim-lang/Nim/issues/10731))
+- Fixed "`foldr` raises IndexError when called on sequence"
+  ([#14404](https://github.com/nim-lang/Nim/issues/14404))
+- Fixed "moveFile does not overwrite destination file"
+  ([#14057](https://github.com/nim-lang/Nim/issues/14057))
+- Fixed "doc2 outputs in current work dir"
+  ([#6583](https://github.com/nim-lang/Nim/issues/6583))
+- Fixed "[docgen] proc doc comments silently omitted after 1st runnableExamples"
+  ([#9227](https://github.com/nim-lang/Nim/issues/9227))
+- Fixed "`nim doc --project` shows '@@/' instead of '../' for relative paths to submodules"
+  ([#14448](https://github.com/nim-lang/Nim/issues/14448))
+- Fixed "re, nre have wrong `start` semantics"
+  ([#14284](https://github.com/nim-lang/Nim/issues/14284))
+- Fixed "runnableExamples should preserve source code doc comments, strings, and (maybe) formatting"
+  ([#8871](https://github.com/nim-lang/Nim/issues/8871))
+- Fixed "`nim doc ..` fails when runnableExamples uses `$`  [devel] [regression]"
+  ([#14485](https://github.com/nim-lang/Nim/issues/14485))
+- Fixed "`items` is 20%~30% slower than iteration via an index"
+  ([#14421](https://github.com/nim-lang/Nim/issues/14421))
+- Fixed "ARC: unreliable setLen "
+  ([#14495](https://github.com/nim-lang/Nim/issues/14495))
+- Fixed "lent is unsafe: after #14447 you can modify variables with "items" loop for sequences"
+  ([#14498](https://github.com/nim-lang/Nim/issues/14498))
+- Fixed "`var op = fn()` wrongly gives warning `ObservableStores` with `object of RootObj` type"
+  ([#14514](https://github.com/nim-lang/Nim/issues/14514))
+- Fixed "Compiler assertion"
+  ([#14562](https://github.com/nim-lang/Nim/issues/14562))
+- Fixed "Can't get `ord` of a value of a Range type in the JS backend "
+  ([#14570](https://github.com/nim-lang/Nim/issues/14570))
+- Fixed "js: can't take addr of param (including implicitly via `lent`)"
+  ([#14576](https://github.com/nim-lang/Nim/issues/14576))
+- Fixed "{.noinit.} ignored in for loop -> bad codegen for non-movable types"
+  ([#14118](https://github.com/nim-lang/Nim/issues/14118))
+- Fixed "generic destructor gives: `Error: unresolved generic parameter`"
+  ([#14315](https://github.com/nim-lang/Nim/issues/14315))
+- Fixed "Memory leak with arc gc"
+  ([#14568](https://github.com/nim-lang/Nim/issues/14568))
+- Fixed "escape analysis broken with `lent`"
+  ([#14557](https://github.com/nim-lang/Nim/issues/14557))
+- Fixed "`wrapWords` seems to ignore linebreaks when wrapping, leaving breaks in the wrong place"
+  ([#14579](https://github.com/nim-lang/Nim/issues/14579))
+- Fixed "`lent` gives wrong results with -d:release"
+  ([#14578](https://github.com/nim-lang/Nim/issues/14578))
+- Fixed "Nested await expressions regression: `await a(await expandValue())` doesnt compile"
+  ([#14279](https://github.com/nim-lang/Nim/issues/14279))
+- Fixed "windows CI docs fails with strange errors"
+  ([#14545](https://github.com/nim-lang/Nim/issues/14545))
+- Fixed "[CI] tests/async/tioselectors.nim flaky test for freebsd + OSX CI"
+  ([#13166](https://github.com/nim-lang/Nim/issues/13166))
+- Fixed "seq.setLen sometimes doesn't zero memory"
+  ([#14655](https://github.com/nim-lang/Nim/issues/14655))
+- Fixed "`nim dump` is roughly 100x slower in 1.3 versus 1.2"
+  ([#14179](https://github.com/nim-lang/Nim/issues/14179))
+- Fixed "Regression: devel docgen cannot generate document for method"
+  ([#14691](https://github.com/nim-lang/Nim/issues/14691))
+- Fixed "recently flaky tests/async/t7758.nim"
+  ([#14685](https://github.com/nim-lang/Nim/issues/14685))
+- Fixed "Bind no longer working in generic procs."
+  ([#11811](https://github.com/nim-lang/Nim/issues/11811))
+- Fixed "The pegs module doesn't work with generics!"
+  ([#14718](https://github.com/nim-lang/Nim/issues/14718))
+- Fixed "Defer is not properly working for asynchronous procedures."
+  ([#13899](https://github.com/nim-lang/Nim/issues/13899))
+- Fixed "Add an ARC test with threads in a loop"
+  ([#14690](https://github.com/nim-lang/Nim/issues/14690))
+- Fixed "[goto exceptions] {.noReturn.} pragma is not detected in a case expression"
+  ([#14458](https://github.com/nim-lang/Nim/issues/14458))
+- Fixed "[exceptions:goto] C compiler error with dynlib pragma calling a proc"
+  ([#14240](https://github.com/nim-lang/Nim/issues/14240))
+- Fixed "Cannot borrow var float64 in infix assignment"
+  ([#14440](https://github.com/nim-lang/Nim/issues/14440))
+- Fixed "lib/pure/memfiles.nim: compilation error with --taintMode:on"
+  ([#14760](https://github.com/nim-lang/Nim/issues/14760))
+- Fixed "newWideCString allocates a multiple of the memory needed"
+  ([#14750](https://github.com/nim-lang/Nim/issues/14750))
+- Fixed "Nim source archive install: 'install.sh' fails with error: cp: cannot stat 'bin/nim-gdb': No such file or directory"
+  ([#14748](https://github.com/nim-lang/Nim/issues/14748))
+- Fixed "`nim cpp -r tests/exception/t9657` hangs"
+  ([#10343](https://github.com/nim-lang/Nim/issues/10343))
+- Fixed "Detect tool fails on FreeBSD"
+  ([#14715](https://github.com/nim-lang/Nim/issues/14715))
+- Fixed "compiler crash: `findUnresolvedStatic` "
+  ([#14802](https://github.com/nim-lang/Nim/issues/14802))
+- Fixed "seq namespace (?) regression"
+  ([#4796](https://github.com/nim-lang/Nim/issues/4796))
+- Fixed "Possible out of bounds string access in std/colors parseColor and isColor"
+  ([#14839](https://github.com/nim-lang/Nim/issues/14839))
+- Fixed "compile error on latest devel with orc and ssl"
+  ([#14647](https://github.com/nim-lang/Nim/issues/14647))
+- Fixed "[minor] `$` wrong for type tuple"
+  ([#13432](https://github.com/nim-lang/Nim/issues/13432))
+- Fixed "Documentation missing on devel asyncftpclient"
+  ([#14846](https://github.com/nim-lang/Nim/issues/14846))
+- Fixed "nimpretty is confused with a trailing comma in enum definition"
+  ([#14401](https://github.com/nim-lang/Nim/issues/14401))
+- Fixed "Output arguments get ignored when compiling with --app:staticlib"
+  ([#12745](https://github.com/nim-lang/Nim/issues/12745))
+- Fixed "[ARC] destructive move destroys the object too early"
+  ([#14396](https://github.com/nim-lang/Nim/issues/14396))
+- Fixed "highlite.getNextToken() crashes if the buffer string is "echo "\"""
+  ([#14830](https://github.com/nim-lang/Nim/issues/14830))
+- Fixed "Memory corruption with --gc:arc with a seq of objects with an empty body."
+  ([#14472](https://github.com/nim-lang/Nim/issues/14472))
+- Fixed "Stropped identifiers don't work as field names in tuple literals"
+  ([#14911](https://github.com/nim-lang/Nim/issues/14911))
+- Fixed "Please revert my commit"
+  ([#14930](https://github.com/nim-lang/Nim/issues/14930))
+- Fixed "[ARC] C compiler error with inline iterators and imports"
+  ([#14864](https://github.com/nim-lang/Nim/issues/14864))
+- Fixed "AsyncHttpClient segfaults with gc:orc, possibly memory corruption"
+  ([#14402](https://github.com/nim-lang/Nim/issues/14402))
+- Fixed "[ARC] Template with a block evaluating to a GC'd value results in a compiler crash"
+  ([#14899](https://github.com/nim-lang/Nim/issues/14899))
+- Fixed "[ARC] Weird issue with if expressions and templates"
+  ([#14900](https://github.com/nim-lang/Nim/issues/14900))
+- Fixed "xmlparser does not compile on devel"
+  ([#14805](https://github.com/nim-lang/Nim/issues/14805))
+- Fixed "returning lent T from a var T param gives codegen errors or SIGSEGV"
+  ([#14878](https://github.com/nim-lang/Nim/issues/14878))
+- Fixed "[ARC] Weird issue with if expressions and templates"
+  ([#14900](https://github.com/nim-lang/Nim/issues/14900))
+- Fixed "threads:on + gc:orc + unittest = C compiler errors"
+  ([#14865](https://github.com/nim-lang/Nim/issues/14865))
+- Fixed "mitems, mpairs doesn't work at compile time anymore"
+  ([#12129](https://github.com/nim-lang/Nim/issues/12129))
+- Fixed "strange result from executing code in const expression"
+  ([#10465](https://github.com/nim-lang/Nim/issues/10465))
+- Fixed "Same warning printed 3 times"
+  ([#11009](https://github.com/nim-lang/Nim/issues/11009))
+- Fixed "type alias for generic typeclass doesn't work"
+  ([#4668](https://github.com/nim-lang/Nim/issues/4668))
+- Fixed "exceptions:goto Bug devel codegen lvalue NIM_FALSE=NIM_FALSE"
+  ([#14925](https://github.com/nim-lang/Nim/issues/14925))
+- Fixed "the --useVersion:1.0 no longer works in devel"
+  ([#14912](https://github.com/nim-lang/Nim/issues/14912))
+- Fixed "template declaration of iterator doesn't compile"
+  ([#4722](https://github.com/nim-lang/Nim/issues/4722))
+- Fixed "Compiler crash on type inheritance with static generic parameter and equality check"
+  ([#12571](https://github.com/nim-lang/Nim/issues/12571))
+- Fixed "Nim crashes while handling a cast in async circumstances."
+  ([#13815](https://github.com/nim-lang/Nim/issues/13815))
+- Fixed "[ARC] Internal compiler error when calling an iterator from an inline proc "
+  ([#14383](https://github.com/nim-lang/Nim/issues/14383))
+- Fixed ""Cannot instantiate" error when template uses generic type"
+  ([#5926](https://github.com/nim-lang/Nim/issues/5926))
+- Fixed "Different raises behaviour for newTerminal between Linux and Windows"
+  ([#12759](https://github.com/nim-lang/Nim/issues/12759))
+- Fixed "Expand on a type (that defines a proc type) in error message "
+  ([#6608](https://github.com/nim-lang/Nim/issues/6608))
+- Fixed "unittest require quits program with an exit code of 0"
+  ([#14475](https://github.com/nim-lang/Nim/issues/14475))
+- Fixed "Range type: Generics vs concrete type, semcheck difference."
+  ([#8426](https://github.com/nim-lang/Nim/issues/8426))
+- Fixed "[Macro] Type mismatch when parameter name is the same as a field"
+  ([#13253](https://github.com/nim-lang/Nim/issues/13253))
+- Fixed "Generic instantiation failure when converting a sequence of circular generic types to strings"
+  ([#10396](https://github.com/nim-lang/Nim/issues/10396))
+- Fixed "initOptParser ignores argument after value option with empty value."
+  ([#13086](https://github.com/nim-lang/Nim/issues/13086))
+- Fixed "[ARC] proc with both explicit and implicit return results in a C compiler error"
+  ([#14985](https://github.com/nim-lang/Nim/issues/14985))
+- Fixed "Alias type forgets implicit generic params depending on order"
+  ([#14990](https://github.com/nim-lang/Nim/issues/14990))
+- Fixed "[ARC] sequtils.insert has different behaviour between ARC/refc"
+  ([#14994](https://github.com/nim-lang/Nim/issues/14994))
+- Fixed "The documentation for "hot code reloading" references a non-existent npm package"
+  ([#13621](https://github.com/nim-lang/Nim/issues/13621))
+- Fixed "existsDir deprecated but breaking `dir` undeclared"
+  ([#15006](https://github.com/nim-lang/Nim/issues/15006))
+- Fixed "uri.decodeUrl crashes on incorrectly formatted input"
+  ([#14082](https://github.com/nim-lang/Nim/issues/14082))
+- Fixed "testament incorrectly reports time for tests, leading to wrong conclusions"
+  ([#14822](https://github.com/nim-lang/Nim/issues/14822))
+- Fixed "Calling peekChar with Stream returned from osproc.outputStream generate runtime error"
+  ([#14906](https://github.com/nim-lang/Nim/issues/14906))
+- Fixed "localPassC pragma should come *after* other flags"
+  ([#14194](https://github.com/nim-lang/Nim/issues/14194))
+- Fixed ""Could not load" dynamic library at runtime because of hidden dependency"
+  ([#2408](https://github.com/nim-lang/Nim/issues/2408))
+- Fixed "--gc:arc generate invalid code for {.global.} (`«nimErr_» in NIM_UNLIKELY`)"
+  ([#14480](https://github.com/nim-lang/Nim/issues/14480))
+- Fixed "Using `^` from stdlib/math along with converters gives a match for types that aren't SomeNumber"
+  ([#15033](https://github.com/nim-lang/Nim/issues/15033))
+- Fixed "[ARC] Weird exception behaviour from doAssertRaises"
+  ([#15026](https://github.com/nim-lang/Nim/issues/15026))
+- Fixed "[ARC] Compiler crash declaring a finalizer proc directly in 'new'"
+  ([#15044](https://github.com/nim-lang/Nim/issues/15044))
+- Fixed "[ARC] C compiler error when creating a var of a const seq"
+  ([#15036](https://github.com/nim-lang/Nim/issues/15036))
+- Fixed "code with named arguments in proc of winim/com can not been compiled"
+  ([#15056](https://github.com/nim-lang/Nim/issues/15056))
+- Fixed "javascript backend produces javascript code with syntax error in object syntax"
+  ([#14534](https://github.com/nim-lang/Nim/issues/14534))
+- Fixed "--gc:arc should be ignored in JS mode."
+  ([#14684](https://github.com/nim-lang/Nim/issues/14684))
+- Fixed "arc: C compilation error with imported global code using a closure iterator"
+  ([#12990](https://github.com/nim-lang/Nim/issues/12990))
+- Fixed "[ARC] Crash when modifying a string with mitems iterator"
+  ([#15052](https://github.com/nim-lang/Nim/issues/15052))
+- Fixed "[ARC] SIGSEGV when calling a closure as a tuple field in a seq"
+  ([#15038](https://github.com/nim-lang/Nim/issues/15038))
+- Fixed "pass varargs[seq[T]] to iterator give empty seq "
+  ([#12576](https://github.com/nim-lang/Nim/issues/12576))
+- Fixed "Compiler crashes when using string as object variant selector with else branch"
+  ([#14189](https://github.com/nim-lang/Nim/issues/14189))
+- Fixed "JS compiler error related to implicit return and return var type"
+  ([#11354](https://github.com/nim-lang/Nim/issues/11354))
+- Fixed "`nkRecWhen` causes internalAssert in semConstructFields"
+  ([#14698](https://github.com/nim-lang/Nim/issues/14698))
+- Fixed "Memory leaks with async (closure iterators?) under ORC"
+  ([#15076](https://github.com/nim-lang/Nim/issues/15076))
+- Fixed "strutil.insertSep() fails on negative numbers"
+  ([#11352](https://github.com/nim-lang/Nim/issues/11352))
+- Fixed "Constructing a uint64 range on a 32-bit machine leads to incorrect codegen"
+  ([#14616](https://github.com/nim-lang/Nim/issues/14616))
+- Fixed "heapqueue pushpop() proc doesn't compile"
+  ([#14139](https://github.com/nim-lang/Nim/issues/14139))
+- Fixed "[ARC] SIGSEGV when trying to swap in a literal/const string"
+  ([#15112](https://github.com/nim-lang/Nim/issues/15112))
+- Fixed "Defer and --gc:arc"
+  ([#15071](https://github.com/nim-lang/Nim/issues/15071))
+- Fixed "internal error: compiler/semobjconstr.nim(324, 20) example"
+  ([#15111](https://github.com/nim-lang/Nim/issues/15111))
+- Fixed "[ARC] Sequence "disappears" with a table inside of a table with an object variant"
+  ([#15122](https://github.com/nim-lang/Nim/issues/15122))
+- Fixed "[ARC] SIGSEGV with tuple assignment caused by cursor inference"
+  ([#15130](https://github.com/nim-lang/Nim/issues/15130))
+- Fixed "Issue with --gc:arc at compile time"
+  ([#15129](https://github.com/nim-lang/Nim/issues/15129))
+- Fixed "Writing an empty string to an AsyncFile raises an IndexDefect"
+  ([#15148](https://github.com/nim-lang/Nim/issues/15148))
+- Fixed "Compiler is confused about call convention of function with nested closure"
+  ([#5688](https://github.com/nim-lang/Nim/issues/5688))
+- Fixed "Nil check on each field fails in generic function"
+  ([#15101](https://github.com/nim-lang/Nim/issues/15101))
+- Fixed "{.nimcall.} convention won't avoid the creation of closures"
+  ([#8473](https://github.com/nim-lang/Nim/issues/8473))
+- Fixed "smtp.nim(161, 40) Error: type mismatch: got <typeof(nil)> but expected 'SslContext = void'"
+  ([#15177](https://github.com/nim-lang/Nim/issues/15177))
+- Fixed "[strscans] scanf doesn't match a single character with $+ if it's the end of the string"
+  ([#15064](https://github.com/nim-lang/Nim/issues/15064))
+- Fixed "Crash and incorrect return values when using readPasswordFromStdin on Windows."
+  ([#15207](https://github.com/nim-lang/Nim/issues/15207))
+- Fixed "Possible capture error with fieldPairs and genericParams"
+  ([#15221](https://github.com/nim-lang/Nim/issues/15221))
+- Fixed "The StmtList processing of template parameters can lead to unexpected errors"
+  ([#5691](https://github.com/nim-lang/Nim/issues/5691))
+- Fixed "[ARC] C compiler error when passing a var openArray to a sink openArray"
+  ([#15035](https://github.com/nim-lang/Nim/issues/15035))
+- Fixed "Inconsistent unsigned -> signed RangeDefect usage across integer sizes"
+  ([#15210](https://github.com/nim-lang/Nim/issues/15210))
+- Fixed "toHex results in RangeDefect exception when used with large uint64"
+  ([#15257](https://github.com/nim-lang/Nim/issues/15257))
+- Fixed "Arc sink arg crash"
+  ([#15238](https://github.com/nim-lang/Nim/issues/15238))
+- Fixed "SQL escape in db_mysql is not enough"
+  ([#15219](https://github.com/nim-lang/Nim/issues/15219))
+- Fixed "Mixing 'return' with expressions is allowed in 1.2"
+  ([#15280](https://github.com/nim-lang/Nim/issues/15280))
+- Fixed "os.getFileInfo() causes ICE with --gc:arc on Windows"
+  ([#15286](https://github.com/nim-lang/Nim/issues/15286))
+- Fixed "[ARC] Sequence "disappears" with a table inside of a table with an object variant"
+  ([#15122](https://github.com/nim-lang/Nim/issues/15122))
+- Fixed "Documentation regression jsre module missing"
+  ([#15183](https://github.com/nim-lang/Nim/issues/15183))
+- Fixed "CountTable.smallest/largest() on empty table either asserts or gives bogus answer"
+  ([#15021](https://github.com/nim-lang/Nim/issues/15021))
+- Fixed "[Regression] Parser regression"
+  ([#15305](https://github.com/nim-lang/Nim/issues/15305))
+- Fixed "[ARC] SIGSEGV with tuple unpacking caused by cursor inference"
+  ([#15147](https://github.com/nim-lang/Nim/issues/15147))
+- Fixed "LwIP/FreeRTOS compile error - missing SIGPIPE and more "
+  ([#15302](https://github.com/nim-lang/Nim/issues/15302))
+- Fixed "Memory leaks with async (closure iterators?) under ORC"
+  ([#15076](https://github.com/nim-lang/Nim/issues/15076))
+- Fixed "Bug compiling with --gc:arg or --gc:orc"
+  ([#15325](https://github.com/nim-lang/Nim/issues/15325))
+- Fixed "memory corruption in tmarshall.nim"
+  ([#9754](https://github.com/nim-lang/Nim/issues/9754))
+- Fixed "typed macros break generic proc definitions"
+  ([#15326](https://github.com/nim-lang/Nim/issues/15326))
+- Fixed "nim doc2 ignores --docSeeSrcUrl parameter"
+  ([#6071](https://github.com/nim-lang/Nim/issues/6071))
+- Fixed "The decodeData Iterator from cgi module crash"
+  ([#15369](https://github.com/nim-lang/Nim/issues/15369))
+- Fixed "|| iterator generates invalid code when compiling with --debugger:native"
+  ([#9710](https://github.com/nim-lang/Nim/issues/9710))
+- Fixed "Wrong number of variables"
+  ([#15360](https://github.com/nim-lang/Nim/issues/15360))
+- Fixed "Coercions with distinct types should traverse pointer modifiers transparently."
+  ([#7165](https://github.com/nim-lang/Nim/issues/7165))
+- Fixed "Error with distinct generic TableRef"
+  ([#6060](https://github.com/nim-lang/Nim/issues/6060))
+- Fixed "Support images in nim docgen"
+  ([#6430](https://github.com/nim-lang/Nim/issues/6430))
+- Fixed "Regression. Double sem check for procs."
+  ([#15389](https://github.com/nim-lang/Nim/issues/15389))
+- Fixed "uri.nim url with literal ipv6 address is printed wrong, and cannot parsed again"
+  ([#15333](https://github.com/nim-lang/Nim/issues/15333))
+- Fixed "[ARC] Object variant gets corrupted with cursor inference"
+  ([#15361](https://github.com/nim-lang/Nim/issues/15361))
+- Fixed "`nim doc ..` compiler crash (regression 0.19.6 => 1.0)"
+  ([#14474](https://github.com/nim-lang/Nim/issues/14474))
+- Fixed "cannot borrow result; what it borrows from is potentially mutated"
+  ([#15403](https://github.com/nim-lang/Nim/issues/15403))
+- Fixed "memory corruption for seq.add(seq) with gc:arc and d:useMalloc "
+  ([#14983](https://github.com/nim-lang/Nim/issues/14983))
+- Fixed "DocGen HTML output appears improperly when encountering text immediately after/before inline monospace; in some cases won't compile"
+  ([#11537](https://github.com/nim-lang/Nim/issues/11537))
+- Fixed "Deepcopy in arc crashes"
+  ([#15405](https://github.com/nim-lang/Nim/issues/15405))
+- Fixed "pop pragma takes invalid input"
+  ([#15430](https://github.com/nim-lang/Nim/issues/15430))
+- Fixed "tests/stdlib/tgetprotobyname fails on NetBSD"
+  ([#15452](https://github.com/nim-lang/Nim/issues/15452))
+- Fixed "defer doesnt work with block, break and await"
+  ([#15243](https://github.com/nim-lang/Nim/issues/15243))
+- Fixed "tests/stdlib/tssl failing on NetBSD"
+  ([#15493](https://github.com/nim-lang/Nim/issues/15493))
+- Fixed "strictFuncs doesn't seem to catch simple ref mutation"
+  ([#15508](https://github.com/nim-lang/Nim/issues/15508))
+- Fixed "Sizeof of case object is incorrect. Showstopper"
+  ([#15516](https://github.com/nim-lang/Nim/issues/15516))
+- Fixed "[ARC] Internal error when trying to use a parallel for loop"
+  ([#15512](https://github.com/nim-lang/Nim/issues/15512))
+- Fixed "[ARC] Type-bound assign op is not being generated"
+  ([#15510](https://github.com/nim-lang/Nim/issues/15510))
+- Fixed "[ARC] Crash when adding openArray proc argument to a local seq"
+  ([#15511](https://github.com/nim-lang/Nim/issues/15511))
+- Fixed "VM: const case object gets some fields zeroed out at runtime"
+  ([#13081](https://github.com/nim-lang/Nim/issues/13081))
+- Fixed "regression(1.2.6 => devel): VM: const case object field access gives: 'sons' is not accessible"
+  ([#15532](https://github.com/nim-lang/Nim/issues/15532))
+- Fixed "Csources: huge size increase (x2.3) in 0.20"
+  ([#12027](https://github.com/nim-lang/Nim/issues/12027))
+- Fixed "Out of date error message for GC options"
+  ([#15547](https://github.com/nim-lang/Nim/issues/15547))
+- Fixed "dbQuote additional escape regression"
+  ([#15560](https://github.com/nim-lang/Nim/issues/15560))
diff --git a/changelogs/changelog_1_6_0.md b/changelogs/changelog_1_6_0.md
new file mode 100644
index 000000000..4a1047d20
--- /dev/null
+++ b/changelogs/changelog_1_6_0.md
@@ -0,0 +1,957 @@
+# v1.6.0 - 2021-10-14
+
+
+
+# Major new features
+
+With so many new features, pinpointing the most salient ones is a subjective exercise,
+but here are a select few:
+
+
+## `iterable[T]`
+
+The `iterable[T]` type class was added to match called iterators,
+which solves a number of long-standing issues related to iterators.
+Example:
+
+```nim
+iterator iota(n: int): int =
+  for i in 0..<n: yield i
+
+# previously, you'd need `untyped`, which caused other problems such as lack
+# of type inference, overloading issues, and MCS.
+template sumOld(a: untyped): untyped = # no type inference possible
+  var result: typeof(block:(for ai in a: ai))
+  for ai in a: result += ai
+  result
+
+assert sumOld(iota(3)) == 0 + 1 + 2
+
+# now, you can write:
+template sum[T](a: iterable[T]): T =
+  # `template sum(a: iterable): auto =` would also be possible
+  var result: T
+  for ai in a: result += ai
+  result
+
+assert sum(iota(3)) == 0 + 1 + 2 # or `iota(3).sum`
+```
+
+In particular iterable arguments can now be used with the method call syntax. For example:
+```nim
+import std/[sequtils, os]
+echo walkFiles("*").toSeq # now works
+```
+See PR [#17196](https://github.com/nim-lang/Nim/pull/17196) for additional details.
+
+
+## Strict effects
+
+The effect system was refined and there is a new `.effectsOf` annotation that does
+explicitly what was previously done implicitly. See the
+[manual](https://nim-lang.github.io/Nim/manual.html#effect-system-effectsof-annotation)
+for more details.
+To write code that is portable with older Nim versions, use this idiom:
+
+```nim
+when defined(nimHasEffectsOf):
+  {.experimental: "strictEffects".}
+else:
+  {.pragma: effectsOf.}
+
+proc mysort(s: seq; cmp: proc(a, b: T): int) {.effectsOf: cmp.}
+```
+
+To enable the new effect system, compile with `--experimental:strictEffects`.
+See also [#18777](https://github.com/nim-lang/Nim/pull/18777) and RFC
+[#408](https://github.com/nim-lang/RFCs/issues/408).
+
+
+## Private imports and private field access
+
+A new import syntax `import foo {.all.}` now allows importing all symbols
+(public or private) from `foo`.
+This can be useful for testing purposes or for more flexibility in project organization.
+
+Example:
+```nim
+from system {.all.} as system2 import nil
+echo system2.ThisIsSystem # ThisIsSystem is private in `system`
+import os {.all.} # weirdTarget is private in `os`
+echo weirdTarget # or `os.weirdTarget`
+```
+
+Added a new module `std/importutils`, and an API `privateAccess`, which allows access
+to private fields for an object type in the current scope.
+
+Example:
+```nim
+import times
+from std/importutils import privateAccess
+block:
+  let t = now()
+  # echo t.monthdayZero # Error: undeclared field: 'monthdayZero' for type times.DateTime
+  privateAccess(typeof(t)) # enables private access in this scope
+  echo t.monthdayZero # ok
+```
+
+See PR [#17706](https://github.com/nim-lang/Nim/pull/17706) for additional details.
+
+
+## `nim --eval:cmd`
+
+Added `nim --eval:cmd` to evaluate a command directly, e.g.: `nim --eval:"echo 1"`.
+It defaults to `e` (nimscript) but can also work with other commands, e.g.:
+```bash
+find . | nim r --eval:'import strutils; for a in stdin.lines: echo a.toUpper'
+```
+
+```bash
+# use as a calculator:
+nim --eval:'echo 3.1 / (1.2+7)'
+# explore a module's APIs, including private symbols:
+nim --eval:'import os {.all.}; echo weirdTarget'
+# use a custom backend:
+nim r -b:js --eval:"import std/jsbigints; echo 2'big ** 64'big"
+```
+
+See PR [#15687](https://github.com/nim-lang/Nim/pull/15687) for more details.
+
+
+## Round-trip float to string
+
+`system.addFloat` and `system.$` now can produce string representations of
+floating point numbers that are minimal in size and possess round-trip and correct
+rounding guarantees (via the
+[Dragonbox](https://raw.githubusercontent.com/jk-jeon/dragonbox/master/other_files/Dragonbox.pdf) algorithm).
+This currently has to be enabled via `-d:nimPreviewFloatRoundtrip`.
+It is expected that this behavior becomes the new default in upcoming versions,
+as with other `nimPreviewX` define flags.
+
+Example:
+```nim
+from math import round
+let a = round(9.779999999999999, 2)
+assert a == 9.78
+echo a # with `-d:nimPreviewFloatRoundtrip`: 9.78, like in python3 (instead of  9.779999999999999)
+```
+
+
+## New `std/jsbigints` module
+
+Provides arbitrary precision integers for the JS target. See PR
+[#16409](https://github.com/nim-lang/Nim/pull/16409).
+Example:
+```nim
+import std/jsbigints
+assert 2'big ** 65'big == 36893488147419103232'big
+echo 0xdeadbeef'big shl 4'big # 59774856944n
+```
+
+
+## New `std/sysrand` module
+Cryptographically secure pseudorandom number generator,
+allows generating random numbers from a secure source provided by the operating system.
+Example:
+```nim
+import std/sysrand
+assert urandom(1234) != urandom(1234) # unlikely to fail in practice
+```
+See PR [#16459](https://github.com/nim-lang/Nim/pull/16459).
+
+
+## New module: `std/tempfiles`
+
+Allows creating temporary files and directories, see PR
+[#17361](https://github.com/nim-lang/Nim/pull/17361) and followups.
+```nim
+import std/tempfiles
+let tmpPath = genTempPath("prefix", "suffix.log", "/tmp/")
+# tmpPath looks like: /tmp/prefixpmW1P2KLsuffix.log
+
+let dir = createTempDir("tmpprefix_", "_end")
+# created dir looks like: getTempDir() / "tmpprefix_YEl9VuVj_end"
+
+let (cfile, path) = createTempFile("tmpprefix_", "_end.tmp")
+# path looks like: getTempDir() / "tmpprefix_FDCIRZA0_end.tmp"
+cfile.write "foo"
+cfile.setFilePos 0
+assert readAll(cfile) == "foo"
+close cfile
+assert readFile(path) == "foo"
+```
+
+
+## User-defined literals
+
+Custom numeric literals (e.g. `-128'bignum`) are now supported.
+Additionally, the unary minus in `-1` is now part of the integer literal, i.e.
+it is now parsed as a single token.
+This implies that edge cases like `-128'i8` finally work correctly.
+Example:
+```nim
+func `'big`*(num: cstring): JsBigInt {.importjs: "BigInt(#)".}
+assert 0xffffffffffffffff'big == (1'big shl 64'big) - 1'big
+```
+
+
+## Dot-like operators
+
+With `-d:nimPreviewDotLikeOps`, dot-like operators (operators starting with `.`,
+but not with `..`) now have the same precedence as `.`, so that `a.?b.c` is now
+parsed as `(a.?b).c` instead of `a.?(b.c)`.
+A warning is generated when a dot-like operator is used without `-d:nimPreviewDotLikeOps`.
+
+An important use case is to enable dynamic fields without affecting the
+built-in `.` operator, e.g. for `std/jsffi`, `std/json`, `pkg/nimpy`. Example:
+```nim
+import std/json
+template `.?`(a: JsonNode, b: untyped{ident}): JsonNode =
+  a[astToStr(b)]
+let j = %*{"a1": {"a2": 10}}
+assert j.?a1.?a2.getInt == 10
+```
+
+
+## Block arguments now support optional parameters
+
+This solves a major pain point for routines accepting block parameters,
+see PR [#18631](https://github.com/nim-lang/Nim/pull/18631) for details:
+
+```nim
+template fn(a = 1, b = 2, body) = discard
+fn(1, 2): # already works
+  bar
+fn(a = 1): # now works
+  bar
+```
+
+Likewise with multiple block arguments via `do`:
+```nim
+template fn(a = 1, b = 2, body1, body2) = discard
+fn(a = 1): # now works
+  bar1
+do:
+  bar2
+```
+
+
+# Other new features
+
+## New and deprecated modules
+
+The following modules were added (they are discussed in the rest of the text):
+- `std/enumutils`
+- `std/genasts`
+- `std/importutils`
+- `std/jsbigints`
+- `std/jsfetch`
+- `std/jsformdata`
+- `std/jsheaders`
+- `std/packedsets`
+- `std/setutils`
+- `std/socketstreams`
+- `std/strbasics`
+- `std/sysrand`
+- `std/tasks`
+- `std/tempfiles`
+- `std/vmutils`
+
+- Deprecated `std/mersenne`.
+- Removed deprecated `std/iup` module from stdlib; it has already moved to
+  [nimble](https://github.com/nim-lang/iup).
+
+
+## New `std/jsfetch` module
+
+Provides a wrapper for JS Fetch API.
+Example:
+```nim
+# requires -d:nimExperimentalAsyncjsThen
+import std/[jsfetch, asyncjs, jsconsole, jsffi, sugar]
+proc fn {.async.} =
+  await fetch("https://api.github.com/users/torvalds".cstring)
+    .then((response: Response) => response.json())
+    .then((json: JsObject) => console.log(json))
+    .catch((err: Error) => console.log("Request Failed", err))
+discard fn()
+```
+
+
+## New `std/tasks` module
+
+Provides basic primitives for creating parallel programs.
+Example:
+```nim
+import std/tasks
+var num = 0
+proc hello(a: int) = num+=a
+
+let b = toTask hello(13) # arguments must be isolated, see `std/isolation`
+b.invoke()
+assert num == 13
+b.invoke() # can be called again
+assert num == 26
+```
+
+
+## New module: `std/genasts`
+
+Provides an API `genAst` that avoids the problems inherent with `quote do` and can
+be used as a replacement.
+Example showing how this could be used for writing a simplified version of `unittest.check`:
+```nim
+import std/[genasts, macros, strutils]
+macro check2(cond: bool): untyped =
+  assert cond.kind == nnkInfix, "$# not implemented" % $cond.kind
+  result = genAst(cond, s = repr(cond), lhs = cond[1], rhs = cond[2]):
+    # each local symbol we access must be explicitly captured
+    if not cond:
+      doAssert false, "'$#'' failed: lhs: '$#', rhs: '$#'" % [s, $lhs, $rhs]
+let a = 3
+check2 a*2 == a+3
+if false: check2 a*2 < a+1 # would error with: 'a * 2 < a + 1'' failed: lhs: '6', rhs: '4'
+```
+
+See PR [#17426](https://github.com/nim-lang/Nim/pull/17426) for details.
+
+
+## New module: `std/setutils`
+
+- Added `setutils.toSet` that can take any iterable and convert it to a built-in `set`,
+  if the iterable yields a built-in settable type.
+- Added `setutils.fullSet` which returns a full built-in `set` for a valid type.
+- Added `setutils.complement` which returns the complement of a built-in `set`.
+- Added `setutils.[]=`.
+
+
+## New module: `std/enumutils`
+
+- Added `genEnumCaseStmt` macro that generates
+  case statement to parse string to enum.
+- Added `items` for enums with holes.
+- Added `symbolName` to return the `enum` symbol name ignoring the human-readable name.
+- Added `symbolRank` to return the index in which an `enum` member is listed in an enum.
+
+
+## `system`
+
+- Added `system.prepareMutation` for better support of low
+  level `moveMem`, `copyMem` operations for `gc:orc`'s copy-on-write string
+  implementation.
+- `system.addEscapedChar` now renders `\r` as `\r` instead of `\c`, to be compatible
+  with most other languages.
+- Added `cmpMem` to `system`.
+- `doAssertRaises` now correctly handles foreign exceptions.
+- `addInt` now supports unsigned integers.
+
+Compatibility notes:
+- `system.delete` had surprising behavior when the index passed to it was out of
+  bounds (it would delete the last entry then). Compile with `-d:nimStrictDelete` so
+  that an index error is produced instead. Be aware however that your code might depend on
+  this quirky behavior so a review process is required on your part before you can
+  use `-d:nimStrictDelete`. To make this review easier, use the `-d:nimAuditDelete`
+  switch, which pretends that `system.delete` is deprecated so that it is easier
+  to see where it was used in your code.
+  `-d:nimStrictDelete` will become the default in upcoming versions.
+- `cuchar` is now deprecated as it aliased `char` where arguably it should have aliased `uint8`.
+  Please use `char` or `uint8` instead.
+- `repr` now doesn't insert trailing newlines; the previous behavior was very inconsistent,
+  see [#16034](https://github.com/nim-lang/Nim/pull/16034).
+  Use `-d:nimLegacyReprWithNewline` for the previous behavior.
+  `repr` now also renders ASTs correctly for user defined literals, setters, `do`, etc.
+- Deprecated `any`. See RFC [#281](https://github.com/nim-lang/RFCs/issues/281).
+- The unary slice `..b` was deprecated, use `0..b` instead.
+
+
+## `std/math`
+
+- Added `almostEqual` for comparing two float values using a machine epsilon.
+- Added `clamp` which allows using a `Slice` to clamp to a value.
+- Added `ceilDiv` for integer division that rounds up.
+- Added `isNaN`.
+- Added `copySign`.
+- Added `euclDiv` and `euclMod`.
+- Added `signbit`.
+- Added `frexp` overload procs. Deprecated `c_frexp`, use `frexp` instead.
+
+Compatibility notes:
+- `math.round` now rounds "away from zero" in the JS backend, which is consistent
+  with other backends. See [#9125](https://github.com/nim-lang/Nim/pull/9125).
+  Use `-d:nimLegacyJsRound` for the previous behavior.
+
+
+## Random number generators: `std/random`, `std/sysrand`, `std/oids`
+
+- Added `std/sysrand` module (see details above).
+- Added `randState` template that exposes the default random number generator.
+  Useful for library authors.
+- Added `initRand()` overload with no argument which uses the current time as a seed.
+- `initRand(seed)` now allows `seed == 0`.
+- Fixed overflow bugs.
+- Fix `initRand` to avoid random number sequences overlapping, refs
+  [#18744](https://github.com/nim-lang/Nim/pull/18744).
+- `std/oids` now uses `std/random`.
+
+Compatibility notes:
+- Deprecated `std/mersenne`.
+- `random.initRand(seed)` now produces non-skewed values for the first call to
+  `rand()` after initialization with a small (< 30000) seed.
+  Use `-d:nimLegacyRandomInitRand` to restore previous behavior for a transition
+  time, see PR [#17467](https://github.com/nim-lang/Nim/pull/17467).
+
+
+## `std/json`, `std/jsonutils`
+
+- With `-d:nimPreviewJsonutilsHoleyEnum`, `jsonutils` now can serialize/deserialize
+  holey enums as regular enums (via `ord`) instead of as strings.
+  It is expected that this behavior becomes the new default in upcoming versions.
+ `toJson` now serializes `JsonNode` as is via reference (without a deep copy)
+   instead of treating `JsonNode` as a regular ref object,
+  this can be customized via `jsonNodeMode`.
+- `std/json` and `std/jsonutils` now serialize `NaN`, `Inf`, `-Inf` as strings,
+  so that `%[NaN, -Inf]` is the string `["nan","-inf"]` instead of `[nan,-inf]`
+  which was invalid JSON.
+- `std/json` can now handle integer literals and floating point literals of
+  arbitrary length and precision.
+  Numbers that do not fit the underlying `BiggestInt` or `BiggestFloat` fields are
+  kept as string literals and one can use external BigNum libraries to handle these.
+  The `parseFloat` family of functions also has now optional `rawIntegers` and
+  `rawFloats` parameters that can be used to enforce that all integer or float
+  literals remain in the "raw" string form so that client code can easily treat
+  small and large numbers uniformly.
+- Added `BackwardsIndex` overload for `JsonNode`.
+- `json.%`,`json.to`, `jsonutils.fromJson`,`jsonutils.toJson` now work with `uint|uint64`
+  instead of raising (as in 1.4) or giving wrong results (as in 1.2).
+- `std/jsonutils` now handles `cstring` (including as Table key), and `set`.
+- Added `jsonutils.jsonTo` overload with `opt = Joptions()` param.
+- `jsonutils.toJson` now supports customization via `ToJsonOptions`.
+- `std/json`, `std/jsonutils` now support round-trip serialization when
+  `-d:nimPreviewFloatRoundtrip` is used.
+
+
+## `std/typetraits`, `std/compilesettings`
+
+- `distinctBase` now is identity instead of error for non distinct types.
+- `distinctBase` now allows controlling whether to be recursive or not.
+- Added `enumLen` to return the number of elements in an enum.
+- Added `HoleyEnum` for enums with holes, `OrdinalEnum` for enums without holes.
+- Added `hasClosure`.
+- Added `pointerBase` to return `T` for `ref T | ptr T`.
+- Added `compilesettings.SingleValueSetting.libPath`.
+
+
+## networking: `std/net`, `std/asyncnet`, `std/htmlgen`, `std/httpclient`, `std/asyncdispatch`, `std/asynchttpserver`, `std/httpcore`
+
+- Fixed buffer overflow bugs in `std/net`.
+- Exported `sslHandle` from `std/net` and `std/asyncnet`.
+- Added `hasDataBuffered` to `std/asyncnet`.
+- Various functions in `std/httpclient` now accept `url` of type `Uri`.
+  Moreover, the `request` function's `httpMethod` argument of type `string` was
+  deprecated in favor of `HttpMethod` `enum` type; see
+  [#15919](https://github.com/nim-lang/Nim/pull/15919).
+- Added `asyncdispatch.activeDescriptors` that returns the number of currently
+  active async event handles/file descriptors.
+- Added `getPort` to `std/asynchttpserver` to resolve OS-assigned `Port(0)`;
+  this is usually recommended instead of hardcoding ports which can lead to
+  "Address already in use" errors.
+- Fixed premature garbage collection in `std/asyncdispatch`, when a stacktrace
+  override is in place.
+- Added `httpcore.is1xx` and missing HTTP codes.
+- Added `htmlgen.portal` for [making "SPA style" pages using HTML only](https://web.dev/hands-on-portals).
+
+Compatibility notes:
+- On Windows, the SSL library now checks for valid certificates.
+  For this purpose it uses the `cacert.pem` file, which was extracted
+  from `https://curl.se/ca/cacert.pem`. Besides
+  the OpenSSL DLLs (e.g. `libssl-1_1-x64.dll`, `libcrypto-1_1-x64.dll`) you
+  now also need to ship `cacert.pem` with your `.exe` file.
+
+
+## `std/hashes`
+
+- `hashes.hash` can now support `object` and `ref` (can be overloaded in user code),
+  if `-d:nimPreviewHashRef` is used. It is expected that this behavior
+  becomes the new default in upcoming versions.
+- `hashes.hash(proc|ptr|ref|pointer)` now calls `hash(int)` and honors `-d:nimIntHash1`.
+  `hashes.hash(closure)` has also been improved.
+
+
+## OS: `std/os`, `std/io`, `std/socketstream`, `std/linenoise`, `std/tempfiles`
+
+- `os.FileInfo` (returned by `getFileInfo`) now contains `blockSize`,
+  determining preferred I/O block size for this file object.
+- Added `os.getCacheDir()` to return platform specific cache directory.
+- Improved `os.getTempDir()`, see PR [#16914](https://github.com/nim-lang/Nim/pull/16914).
+- Added `os.isAdmin` to tell whether the caller's process is a member of the
+  Administrators local group (on Windows) or a root (on POSIX).
+- Added optional `options` argument to `copyFile`, `copyFileToDir`, and
+  `copyFileWithPermissions`. By default, on non-Windows OSes, symlinks are
+  followed (copy files symlinks point to); on Windows, `options` argument is
+  ignored and symlinks are skipped.
+- On non-Windows OSes, `copyDir` and `copyDirWithPermissions` copy symlinks as
+  symlinks (instead of skipping them as it was before); on Windows symlinks are
+  skipped.
+- On non-Windows OSes, `moveFile` and `moveDir` move symlinks as symlinks
+  (instead of skipping them sometimes as it was before).
+- Added optional `followSymlinks` argument to `setFilePermissions`.
+- Added a simpler to use `io.readChars` overload.
+- Added `socketstream` module that wraps sockets in the stream interface.
+- Added experimental `linenoise.readLineStatus` to get line and status (e.g. ctrl-D or ctrl-C).
+
+
+## Environment variable handling
+
+- Empty environment variable values are now supported across OS's and backends.
+- Environment variable APIs now work in multithreaded scenarios, by delegating to
+  direct OS calls instead of trying to keep track of the environment.
+- `putEnv`, `delEnv` now work at compile time.
+- NodeJS backend now supports osenv: `getEnv`, `putEnv`, `envPairs`, `delEnv`, `existsEnv`.
+
+Compatibility notes:
+- `std/os`: `putEnv` now raises if the first argument contains a `=`.
+
+
+## POSIX
+
+- On POSIX systems, the default signal handlers used for Nim programs (it's
+  used for printing the stacktrace on fatal signals) will now re-raise the
+  signal for the OS default handlers to handle.
+  This lets the OS perform its default actions, which might include core
+  dumping (on select signals) and notifying the parent process about the cause
+  of termination.
+- On POSIX systems, we now ignore `SIGPIPE` signals, use `-d:nimLegacySigpipeHandler`
+  for previous behavior.
+- Added `posix_utils.osReleaseFile` to get system identification from `os-release`
+  file on Linux and the BSDs.
+  ([link](https://www.freedesktop.org/software/systemd/man/os-release.html))
+- Removed undefined behavior for `posix.open`.
+
+
+## `std/prelude`
+
+- `std/strformat` is now part of `include std/prelude`.
+- Added `std/sequtils` import to `std/prelude`.
+- `std/prelude` now works with the JS target.
+- `std/prelude` can now be used via `include std/prelude`, but `include prelude` still works.
+
+
+## String manipulation: `std/strformat`, `std/strbasics`
+
+- Added support for parenthesized expressions.
+- Added support for const strings instead of just string literals.
+- Added `std/strbasics` for high-performance string operations.
+- Added `strip`, `setSlice`, `add(a: var string, b: openArray[char])`.
+
+
+## `std/wrapnils`
+
+- `std/wrapnils` doesn't use `experimental:dotOperators` anymore, avoiding
+  issues like bug [#13063](https://github.com/nim-lang/Nim/issues/13063)
+  (which affected error messages) for modules importing `std/wrapnils`.
+- Added `??.` macro which returns an `Option`.
+- `std/wrapnils` can now be used to protect against `FieldDefect` errors in
+  case objects, generates optimal code (no overhead compared to manual
+  if-else branches), and preserves lvalue semantics which allows modifying
+  an expression.
+
+
+## Containers: `std/algorithm`, `std/lists`, `std/sequtils`, `std/options`, `std/packedsets`
+
+- Removed the optional `longestMatch` parameter of the `critbits._WithPrefix`
+  iterators (it never worked reliably).
+- Added `algorithm.merge`.
+- In `std/lists`: renamed `append` to `add` and retained `append` as an alias;
+  added `prepend` and `prependMoved` analogously to `add` and `addMoved`;
+  added `remove` for `SinglyLinkedList`s.
+- Added new operations for singly- and doubly linked lists: `lists.toSinglyLinkedList`
+  and `lists.toDoublyLinkedList` convert from `openArray`s; `lists.copy` implements
+  shallow copying; `lists.add` concatenates two lists - an O(1) variation that consumes
+  its argument, `addMoved`, is also supplied.
+  See PRs [#16362](https://github.com/nim-lang/Nim/pull/16362),
+  [#16536](https://github.com/nim-lang/Nim/pull/16536).
+- New module: `std/packedsets`.
+  Generalizes `std/intsets`, see PR [#15564](https://github.com/nim-lang/Nim/pull/15564).
+
+Compatibility notes:
+- Deprecated `sequtils.delete` and added an overload taking a `Slice` that raises
+  a defect if the slice is out of bounds, likewise with `strutils.delete`.
+- Deprecated `proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T]`
+  in `std/algorithm`.
+- `std/options` changed `$some(3)` to `"some(3)"` instead of `"Some(3)"`
+  and `$none(int)` to `"none(int)"` instead of `"None[int]"`.
+
+
+## `std/times`
+
+- Added `ZZZ` and `ZZZZ` patterns to `times.nim` `DateTime` parsing, to match time
+  zone offsets without colons, e.g. `UTC+7 -> +0700`.
+- Added `dateTime` and deprecated `initDateTime`.
+
+
+## `std/macros` and AST
+
+- New module `std/genasts`, see description above.
+- The required name of case statement macros for the experimental
+  `caseStmtMacros` feature has changed from `match` to `` `case` ``.
+- Tuple expressions are now parsed consistently as
+  `nnkTupleConstr` node. Will affect macros expecting nodes to be of `nnkPar`.
+- In `std/macros`, `treeRepr,lispRepr,astGenRepr` now represent SymChoice nodes
+  in a collapsed way.
+  Use `-d:nimLegacyMacrosCollapseSymChoice` to get the previous behavior.
+- Made custom op in `macros.quote` work for all statements.
+
+
+## `std/sugar`
+
+- Added `sugar.dumpToString` which improves on `sugar.dump`.
+- Added an overload for the `collect` macro that infers the container type based
+  on the syntax of the last expression. Works with std seqs, tables and sets.
+
+Compatibility notes:
+- Removed support for named procs in `sugar.=>`.
+
+
+## Parsing: `std/parsecfg`, `std/strscans`, `std/uri`
+
+- Added `sections` iterator in `parsecfg`.
+- `strscans.scanf` now supports parsing single characters.
+- Added `strscans.scanTuple` which uses `strscans.scanf` internally,
+  returning a tuple which can be unpacked for easier usage of `scanf`.
+- Added `decodeQuery` to `std/uri`.
+- `parseopt.initOptParser` has been made available and `parseopt` has been
+  added back to `std/prelude` for all backends. Previously `initOptParser` was
+  unavailable if the `std/os` module did not have `paramCount` or `paramStr`,
+  but the use of these in `initOptParser` were conditionally to the runtime
+  arguments passed to it, so `initOptParser` has been changed to raise
+  `ValueError` when the real command line is not available. `parseopt` was
+  previously excluded from `std/prelude` for JS, as it could not be imported.
+
+Compatibility notes:
+- Changed the behavior of `uri.decodeQuery` when there are unencoded `=`
+  characters in the decoded values. Prior versions would raise an error. This is
+  no longer the case to comply with the HTML spec and other languages'
+  implementations. Old behavior can be obtained with
+  `-d:nimLegacyParseQueryStrict`. `cgi.decodeData` which uses the same
+  underlying code is also updated the same way.
+
+
+## JS stdlib changes
+
+- Added `std/jsbigints` module, which provides arbitrary precision integers for the JS target.
+- Added `setCurrentException` for the JS backend.
+- `writeStackTrace` is available in the JS backend now.
+- Added `then`, `catch` to `std/asyncjs` for promise pipelining, for now hidden
+  behind `-d:nimExperimentalAsyncjsThen`.
+- Added `std/jsfetch` module [Fetch](https://developer.mozilla.org/docs/Web/API/Fetch_API)
+  wrapper for the JS target.
+- Added `std/jsheaders` module [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers)
+  wrapper for the JS target.
+- Added `std/jsformdata` module [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
+  wrapper for the JS target.
+- Added `jscore.debugger` to [call any available debugging functionality, such as breakpoints](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger).
+- Added `jsconsole.dir`, `jsconsole.dirxml`, `jsconsole.timeStamp`.
+- Added dollar `$` and `len` for `jsre.RegExp`.
+- Added `jsconsole.jsAssert` for the JS target.
+- Added `**` to `std/jsffi`.
+- Added `copyWithin` [for `seq` and `array` for JS targets](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin).
+- In `std/dom`, `Interval` is now a `ref object`, same as `Timeout`.
+  Definitions of `setTimeout`, `clearTimeout`, `setInterval`, `clearInterval` were updated.
+- Added `dom.scrollIntoView` proc with options.
+- Added `dom.setInterval`, `dom.clearInterval` overloads.
+- Merged `std/dom_extensions` into the `std/dom` module,
+  as it was a module with a single line, see RFC [#413](https://github.com/nim-lang/RFCs/issues/413).
+- `$` now gives more correct results on the JS backend.
+
+
+## JS compiler changes
+
+- `cstring` doesn't support the `[]=` operator anymore in the JS backend.
+- Array literals now use JS typed arrays when the corresponding JS typed array exists,
+  for example `[byte(1), 2, 3]` generates `new Uint8Array([1, 2, 3])`.
+
+
+## VM and nimscript backend
+
+- VM now supports `addr(mystring[ind])` (index + index assignment).
+- `nimscript` now handles `except Exception as e`.
+- `nil` dereference is not allowed at compile time. `cast[ptr int](nil)[]` is rejected at compile time.
+- `static[T]` now works better, refs [#17590](https://github.com/nim-lang/Nim/pull/17590),
+  [#15853](https://github.com/nim-lang/Nim/pull/15853).
+- `distinct T` conversions now work in VM.
+- `items(cstring)` now works in VM.
+- Fix `addr`, `len`, `high` in VM ([#16002](https://github.com/nim-lang/Nim/pull/16002),
+  [#16610](https://github.com/nim-lang/Nim/pull/16610)).
+- `std/cstrutils` now works in VM.
+
+
+## OS/platform-specific notes
+
+- Support for Apple silicon/M1.
+- Support for 32-bit RISC-V, refs [#16231](https://github.com/nim-lang/Nim/pull/16231).
+- Support for armv8l, refs [#18901](https://github.com/nim-lang/Nim/pull/18901).
+- Support for CROSSOS, refs [#18889](https://github.com/nim-lang/Nim/pull/18889).
+- The allocator for Nintendo Switch, which was nonfunctional because
+  of breaking changes in libnx, was removed, in favor of the new `-d:nimAllocPagesViaMalloc` option.
+- Allow reading parameters when compiling for Nintendo Switch.
+- `--nimcache` now correctly works in a cross-compilation setting.
+- Cross compilation targeting Windows was improved.
+- This now works from macOS/Linux: `nim r -d:mingw main`
+
+
+
+## Performance / memory optimizations
+
+- The comment field in PNode AST was moved to a side channel, reducing overall
+  memory usage during compilation by a factor 1.25x
+- `std/jsonutils` deserialization is now up to 20x faster.
+- `os.copyFile` is now 2.5x faster on macOS, by using `copyfile` from `copyfile.h`;
+  use `-d:nimLegacyCopyFile` for macOS < 10.5.
+- Float to string conversion is now 10x faster thanks to the Dragonbox algorithm,
+  with `-d:nimPreviewFloatRoundtrip`.
+- `newSeqWith` is 3x faster.
+- CI now supports batching (making Windows CI 2.3X faster).
+- Sets now uses the optimized `countSetBits` proc, see PR
+  [#17334](https://github.com/nim-lang/Nim/pull/17334).
+
+
+## Debugging
+
+- You can now enable/disable VM tracing in user code via `vmutils.vmTrace`.
+- `koch tools` now builds `bin/nim_dbg` which allows easy access to a debug version
+  of Nim without recompiling.
+- Added new module `compiler/debugutils` to help with debugging Nim compiler.
+- Renamed `-d:nimCompilerStackraceHints` to `-d:nimCompilerStacktraceHints` and
+  used it in more contexts; this flag which works in tandem with `--stackTraceMsgs`
+  to show user code context in compiler stacktraces.
+
+
+## Type system
+
+- `typeof(voidStmt)` now works and returns `void`.
+- `enum` values can now be overloaded. This needs to be enabled
+  via `{.experimental: "overloadableEnums".}`. We hope that this feature allows
+  for the development of more fluent (less ugly) APIs.
+  See RFC [#373](https://github.com/nim-lang/RFCs/issues/373) for more details.
+- A type conversion from one `enum` type to another now produces an `[EnumConv]` warning.
+  You should use `ord` (or `cast`, but the compiler won't help, if you misuse it) instead.
+  ```nim
+  type A = enum a1, a2
+  type B = enum b1, b2
+  echo a1.B # produces a warning
+  echo a1.ord.B # produces no warning
+  ```
+- A dangerous implicit conversion to `cstring` now triggers a `[CStringConv]` warning.
+  This warning will become an error in future versions! Use an explicit conversion
+  like `cstring(x)` in order to silence the warning.
+- There is a new warning for *any* type conversion to `enum` that can be enabled via
+  `.warning[AnyEnumConv]:on` or `--warning:AnyEnumConv:on`.
+- Reusing a type name in a different scope now works, refs [#17710](https://github.com/nim-lang/Nim/pull/17710).
+- Fixed implicit and explicit generics in procedures, refs [#18808](https://github.com/nim-lang/Nim/pull/18808).
+
+
+## New-style concepts
+
+Example:
+```nim
+type
+  Comparable = concept # no T, an atom
+    proc cmp(a, b: Self): int
+```
+The new design does not rely on `system.compiles` and may compile faster.
+See PR [#15251](https://github.com/nim-lang/Nim/pull/15251)
+and RFC [#168](https://github.com/nim-lang/RFCs/issues/168) for details.
+
+
+## Lexical / syntactic
+
+- Nim now supports a small subset of Unicode operators as operator symbols.
+  The supported symbols are: "∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔".
+  To enable this feature, use `--experimental:unicodeOperators`. Note that due
+  to parser limitations you **cannot** enable this feature via a
+  pragma `{.experimental: "unicodeOperators".}` reliably, you need to enable
+  it via the command line or in a configuration file.
+- `var a {.foo.} = expr` now works inside templates (except when `foo` is overloaded).
+
+
+## Compiler messages, error messages, hints, warnings
+
+- Significant improvement to error messages involving effect mismatches,
+  see PRs [#18384](https://github.com/nim-lang/Nim/pull/18384),
+  [#18418](https://github.com/nim-lang/Nim/pull/18418).
+- Added `--declaredLocs` to show symbol declaration location in error messages.
+- Added `--spellSuggest` to show spelling suggestions on typos.
+- Added `--processing:dots|filenames|off` which customizes `hintProcessing`;
+  `--processing:filenames` shows which include/import modules are being compiled as an import stack.
+- `FieldDefect` messages now shows discriminant value + lineinfo, in all backends (C, JS, VM)
+- Added `--hintAsError` with similar semantics as `--warningAsError`.
+- Added `--unitsep:on|off` to control whether to add ASCII unit separator `\31`
+  before a newline for every generated message (potentially multiline),
+  so tooling can tell when messages start and end.
+- Added `--filenames:abs|canonical|legacyRelProj` which replaces `--listFullPaths:on|off`
+- `--hint:all:on|off` is now supported to select or deselect all hints; it
+  differs from `--hints:on|off` which acts as a (reversible) gate.
+  Likewise with `--warning:all:on|off`.
+- The style checking of the compiler now supports a `--styleCheck:usages` switch.
+  This switch enforces that every symbol is written as it was declared, not enforcing
+  the official Nim style guide. To be enabled, this has to be combined either
+  with `--styleCheck:error` or `--styleCheck:hint`.
+- Type mismatch errors now show more context, use `-d:nimLegacyTypeMismatch` for
+  previous behavior.
+- `typedesc[Foo]` now renders as such instead of `type Foo` in compiler messages.
+- `runnableExamples` now show originating location in stacktraces on failure.
+- `SuccessX` message now shows more useful information.
+- New `DuplicateModuleImport` warning; improved `UnusedImport` and
+  `XDeclaredButNotUsed` accuracy.
+
+Compatibility notes:
+- `--hint:CC` now prints to stderr (like all other hints) instead of stdout.
+
+
+## Building and running Nim programs, configuration system
+
+- JSON build instructions are now generated in `$nimcache/outFileBasename.json`
+  instead of `$nimcache/projectName.json`. This allows avoiding recompiling a
+  given project compiled with different options if the output file differs.
+- `--usenimcache` (implied by `nim r main`) now generates an output file that includes
+  a hash of some of the compilation options, which allows caching generated binaries:
+  ```bash
+    nim r main # recompiles
+    nim r -d:foo main # recompiles
+    nim r main # uses cached binary
+    nim r main arg1 arg2 # likewise (runtime arguments are irrelevant)
+  ```
+- `nim r` now supports cross compilation from unix to windows when specifying
+  `-d:mingw` by using Wine, e.g.:
+  `nim r --eval:'import os; echo "a" / "b"'` prints `a\b`.
+- `nim` can compile version 1.4.0 as follows:
+  `nim c --lib:lib --stylecheck:off -d:nimVersion140 compiler/nim`.
+  `-d:nimVersion140` is not needed for bootstrapping, only for building 1.4.0 from devel.
+- `nim e` now accepts arbitrary file extensions for the nimscript file,
+  although `.nims` is still the preferred extension in general.
+- The configuration subsystem now allows for `-d:release` and `-d:danger` to work as expected.
+  The downside is that these defines now have custom logic that doesn't apply for
+  other defines.
+
+
+## Multithreading
+
+- TLS: macOS now uses native TLS (`--tlsEmulation:off`). TLS now works with
+  `importcpp` non-POD types; such types must use `.cppNonPod` and
+  `--tlsEmulation:off`should be used.
+- Added `unsafeIsolate` and `extract` to `std/isolation`.
+- Added `std/tasks`, see description above.
+
+
+## Memory management
+
+- `--gc:arc` now bootstraps (PR [#17342](https://github.com/nim-lang/Nim/pull/17342)).
+- Lots of improvements to `gc:arc`, `gc:orc`, see PR
+  [#15697](https://github.com/nim-lang/Nim/pull/15697),
+  [#16849](https://github.com/nim-lang/Nim/pull/16849),
+  [#17993](https://github.com/nim-lang/Nim/pull/17993).
+- `--gc:orc` is now 10% faster than previously for common workloads.
+  If you have trouble with its changed behavior, compile with `-d:nimOldOrc`.
+- The `--gc:orc` algorithm was refined so that custom container types can participate in the
+  cycle collection process. See the documentation of `=trace` for more details.
+- On embedded devices `malloc` can now be used instead of `mmap` via `-d:nimAllocPagesViaMalloc`.
+  This is only supported for `--gc:orc` or `--gc:arc`.
+
+Compatibility notes:
+- `--newruntime` and `--refchecks` are deprecated,
+  use `--gc:arc`, `--gc:orc`, or `--gc:none` as appropriate instead.
+
+
+## Docgen
+
+- docgen: RST files can now use single backticks instead of double backticks and
+  correctly render in both `nim rst2html` (as before) as well as common tools rendering
+  RST directly (e.g. GitHub).
+  This is done by adding the `default-role:: code` directive inside the RST file
+  (which is now handled by `nim rst2html`).
+- Source+Edit links now appear on top of every docgen'd page when
+  `nim doc --git.url:url ...` is given.
+- Latex doc generation is revised: output `.tex` files should be compiled
+  by `xelatex` (not by `pdflatex` as before). Now default Latex settings
+  provide support for Unicode and better avoid margin overflows.
+  The minimum required version is TeXLive 2018 (or an equivalent MikTeX version).
+- The RST parser now supports footnotes, citations, admonitions, and short style
+  references with symbols.
+- The RST parser now supports Markdown table syntax.
+  Known limitations:
+  - cell alignment is not supported, i.e. alignment annotations in a delimiter
+    row (`:---`, `:--:`, `---:`) are ignored
+  - every table row must start with `|`, e.g. `| cell 1 | cell 2 |`.
+- Implemented `doc2tex` compiler command which converts documentation in
+  `.nim` files to Latex.
+- docgen now supports syntax highlighting for inline code.
+- docgen now supports same-line doc comments:
+  ```nim
+  func fn*(a: int): int = 42  ## Doc comment
+  ```
+- docgen now renders `deprecated` and other pragmas.
+- `runnableExamples` now works with templates and nested templates.
+- `runnableExamples: "-r:off"` now works for examples that should compile but not run.
+- `runnableExamples` now renders code verbatim, and produces correct code in all cases.
+- docgen now shows correct, canonical import paths in docs.
+- docgen now shows all routines in sidebar, and the proc signature is now shown in sidebar.
+
+
+## Effects and checks
+
+- Significant improvement to error messages involving effect mismatches
+- There is a new `cast` section `{.cast(uncheckedAssign).}: body` that disables some
+  compiler checks regarding `case objects`. This allows serialization libraries
+  to avoid ugly, non-portable solutions. See RFC
+  [#407](https://github.com/nim-lang/RFCs/issues/407) for more details.
+
+Compatibility notes:
+- Fixed effect tracking for borrowed procs (see [#18882](https://github.com/nim-lang/Nim/pull/18882)).
+  One consequence is that, under some circumstances, Nim could previously permit a procedure with side effects to be written with `func` - you may need to change some occurrences of `func` to `proc`.
+  To illustrate, Nim versions before 1.6.0 compile the below without error
+  ```nim
+  proc print(s: string) =
+    echo s
+
+  type
+    MyString = distinct string
+
+  proc print(s: MyString) {.borrow.}
+
+  func foo(s: MyString) =
+    print(s)
+  ```
+  but Nim 1.6.0 produces the error
+  ```
+  Error: 'foo' can have side effects
+  ```
+  similar to how we expect that
+  ```nim
+  func print(s: string) =
+    echo s
+  ```
+  produces
+  ```
+  Error: 'print' can have side effects
+  ```
+
+
+## Tools
+
+- Major improvements to `nimgrep`, see PR [#15612
+](https://github.com/nim-lang/Nim/pull/15612).
+- `fusion` is now un-bundled from Nim, `./koch fusion` will
+  install it via Nimble at a fixed hash.
+- `testament`: added `nimoutFull: bool` spec to compare full output of compiler
+  instead of a subset; many bugfixes to testament.
+
+
+## Misc/cleanups
+
+- Deprecated `TaintedString` and `--taintmode`.
+- Deprecated `--nilseqs` which is now a noop.
+- Added `-d:nimStrictMode` in CI in several places to ensure code doesn't have
+  certain hints/warnings.
+- Removed `.travis.yml`, `appveyor.yml.disabled`, `.github/workflows/ci.yml.disabled`.
+- `[skip ci]` now works in azure and CI pipelines, see detail in PR
+  [#17561](https://github.com/nim-lang/Nim/pull/17561).
diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md
new file mode 100644
index 000000000..457cc62a6
--- /dev/null
+++ b/changelogs/changelog_2_0_0.md
@@ -0,0 +1,330 @@
+# v2.0.0 - 2023-08-01
+
+Version 2.0 is a big milestone with too many changes to list them all here.
+
+For a full list see [details](changelog_2_0_0_details.html).
+
+
+## New features
+
+### Better tuple unpacking
+
+Tuple unpacking for variables is now treated as syntax sugar that directly
+expands into multiple assignments. Along with this, tuple unpacking for
+variables can now be nested.
+
+```nim
+proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3)
+
+# Now nesting is supported!
+let (x, (_, y), _, z) = returnsNestedTuple()
+
+```
+
+### Improved type inference
+
+A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference) has been implemented for a variety of basic cases.
+
+For example, code like the following now compiles:
+
+```nim
+let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")]
+```
+
+### Forbidden Tags
+
+[Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) now supports the definition
+of forbidden tags by the `.forbids` pragma which can be used to disable certain effects in proc types.
+
+For example:
+
+```nim
+
+type IO = object ## input/output effect
+proc readLine(): string {.tags: [IO].} = discard
+proc echoLine(): void = discard
+
+proc no_IO_please() {.forbids: [IO].} =
+  # this is OK because it didn't define any tag:
+  echoLine()
+  # the compiler prevents this:
+  let y = readLine()
+
+```
+
+### New standard library modules
+
+The famous `os` module got an overhaul. Several of its features are available
+under a new interface that introduces a `Path` abstraction. A `Path` is
+a `distinct string`, which improves the type safety when dealing with paths, files
+and directories.
+
+Use:
+
+- `std/oserrors` for OS error reporting.
+- `std/envvars` for environment variables handling.
+- `std/paths` for path handling.
+- `std/dirs` for directory creation/deletion/traversal.
+- `std/files` for file existence checking, file deletions and moves.
+- `std/symlinks` for symlink handling.
+- `std/appdirs` for accessing configuration/home/temp directories.
+- `std/cmdline` for reading command line parameters.
+
+### Consistent underscore handling
+
+The underscore identifier (`_`) is now generally not added to scope when
+used as the name of a definition. While this was already the case for
+variables, it is now also the case for routine parameters, generic
+parameters, routine declarations, type declarations, etc. This means that the following code now does not compile:
+
+```nim
+proc foo(_: int): int = _ + 1
+echo foo(1)
+
+proc foo[_](t: typedesc[_]): seq[_] = @[default(_)]
+echo foo[int]()
+
+proc _() = echo "_"
+_()
+
+type _ = int
+let x: _ = 3
+```
+
+Whereas the following code now compiles:
+
+```nim
+proc foo(_, _: int): int = 123
+echo foo(1, 2)
+
+proc foo[_, _](): int = 123
+echo foo[int, bool]()
+
+proc foo[T, U](_: typedesc[T], _: typedesc[U]): (T, U) = (default(T), default(U))
+echo foo(int, bool)
+
+proc _() = echo "one"
+proc _() = echo "two"
+
+type _ = int
+type _ = float
+```
+
+### JavaScript codegen improvement
+
+The JavaScript backend now uses [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
+for 64-bit integer types (`int64` and `uint64`) by default. As this affects
+JS code generation, code using these types to interface with the JS backend
+may need to be updated. Note that `int` and `uint` are not affected.
+
+For compatibility with [platforms that do not support BigInt](https://caniuse.com/bigint)
+and in the case of potential bugs with the new implementation, the
+old behavior is currently still supported with the command line option
+`--jsbigint64:off`.
+
+
+## Docgen improvements
+
+`Markdown` is now the default markup language of doc comments (instead
+of the legacy `RstMarkdown` mode). In this release we begin to separate
+RST and Markdown features to better follow specification of each
+language, with the focus on Markdown development.
+See also [the docs](https://nim-lang.github.io/Nim/markdown_rst.html).
+
+* Added a `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to
+  select the markup language mode in the doc comments of the current `.nim`
+  file for processing by `nim doc`:
+
+    1. `Markdown` (default) is basically CommonMark (standard Markdown) +
+        some Pandoc Markdown features + some RST features that are missing
+        in our current implementation of CommonMark and Pandoc Markdown.
+    2. `RST` closely follows the RST spec with few additional Nim features.
+    3. `RstMarkdown` is a maximum mix of RST and Markdown features, which
+        is kept for the sake of compatibility and ease of migration.
+
+* Added separate `md2html` and `rst2html` commands for processing
+  standalone `.md` and `.rst` files respectively (and also `md2tex`/`rst2tex`).
+
+* Added Pandoc Markdown bracket syntax `[...]` for making anchor-less links.
+* Docgen now supports concise syntax for referencing Nim symbols:
+  instead of specifying HTML anchors directly one can use original
+  Nim symbol declarations (adding the aforementioned link brackets
+  `[...]` around them).
+  * To use this feature across modules, a new `importdoc` directive was added.
+    Using this feature for referencing also helps to ensure that links
+    (inside one module or the whole project) are not broken.
+* Added support for RST & Markdown quote blocks (blocks starting with `>`).
+* Added a popular Markdown definition lists extension.
+* Added Markdown indented code blocks (blocks indented by >= 4 spaces).
+* Added syntax for additional parameters to Markdown code blocks:
+
+       ```nim test="nim c $1"
+       ...
+       ```
+
+
+## C++ interop enhancements
+
+Nim 2.0 takes C++ interop to the next level. With the new [virtual](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma and the extended [constructor](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma.
+Now one can define constructors and virtual procs that maps to C++ constructors and virtual methods, allowing one to further customize
+the interoperability. There is also extended support for the [codeGenDecl](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-codegendecl-pragma) pragma, so that it works on types.
+
+It's a common pattern in C++ to use inheritance to extend a library. Some even use multiple inheritance as a mechanism to make interfaces.
+
+Consider the following example:
+
+```cpp
+
+struct Base {
+  int someValue;
+  Base(int inValue)  {
+    someValue = inValue;
+  };
+};
+
+class IPrinter {
+public:
+  virtual void print() = 0;
+};
+```
+
+```nim
+
+type
+  Base* {.importcpp, inheritable.} = object
+    someValue*: int32
+  IPrinter* {.importcpp.} = object
+
+const objTemplate = """
+  struct $1 : public $3, public IPrinter {
+    $2
+  };
+""";
+
+type NimChild {.codegenDecl: objTemplate .} = object of Base
+
+proc makeNimChild(val: int32): NimChild {.constructor: "NimClass('1 #1) : Base(#1)".} =
+  echo "It calls the base constructor passing " & $this.someValue
+  this.someValue = val * 2 # Notice how we can access `this` inside the constructor. It's of the type `ptr NimChild`.
+
+proc print*(self: NimChild) {.virtual.} =
+  echo "Some value is " & $self.someValue
+
+let child = makeNimChild(10)
+child.print()
+```
+
+It outputs:
+
+```
+It calls the base constructor passing 10
+Some value is 20
+```
+
+
+## ARC/ORC refinements
+
+With the 2.0 release, the ARC/ORC model got refined once again and is now finally complete:
+
+1. Programmers now have control over the "item was moved from" state as `=wasMoved` is overridable.
+2. There is a new `=dup` hook which is more efficient than the old combination of `=wasMoved(tmp); =copy(tmp, x)` operations.
+3. Destructors now take a parameter of the attached object type `T` directly and don't have to take a `var T` parameter.
+
+With these important optimizations we improved the runtime of the compiler and important benchmarks by 0%! Wait ... what?
+Yes, unfortunately it turns out that for a modern optimizer like in GCC or LLVM there is no difference.
+
+But! This refined model is more efficient once separate compilation enters the picture. In other words, as we think of
+providing a stable ABI it is important not to lose any efficiency in the calling conventions.
+
+
+## Tool changes
+
+- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs` before). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop.
+- nimgrep now offers the option `--inContext` (and `--notInContext`), which
+  allows to filter only matches with the context block containing a given pattern.
+- nimgrep: names of options containing "include/exclude" are deprecated,
+  e.g. instead of `--includeFile` and `--excludeFile` we have
+  `--filename` and `--notFilename` respectively.
+  Also, the semantics are now consistent for such positive/negative filters.
+- Nim now ships with an alternative package manager called Atlas. More on this in upcoming versions.
+
+
+## Porting guide
+
+### Block and Break
+
+Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead. In other words, turn:
+
+```nim
+
+block:
+  a()
+  if cond:
+    break
+  b()
+
+```
+
+Into:
+
+```nim
+
+block maybePerformB:
+  a()
+  if cond:
+    break maybePerformB
+  b()
+
+```
+
+### Strict funcs
+
+The definition of `"strictFuncs"` was changed.
+The old definition was roughly: "A store to a ref/ptr deref is forbidden unless it's coming from a `var T` parameter".
+The new definition is: "A store to a ref/ptr deref is forbidden."
+
+This new definition is much easier to understand, the price is some expressitivity. The following code used to be
+accepted:
+
+```nim
+
+{.experimental: "strictFuncs".}
+
+type Node = ref object
+  s: string
+
+func create(s: string): Node =
+  result = Node()
+  result.s = s # store to result[]
+
+```
+
+Now it has to be rewritten to:
+
+```nim
+
+{.experimental: "strictFuncs".}
+
+type Node = ref object
+  s: string
+
+func create(s: string): Node =
+  result = Node(s: s)
+
+```
+
+### Standard library
+
+Several standard library modules have been moved to nimble packages, use `nimble` or `atlas` to install them:
+
+- `std/punycode` => `punycode`
+- `std/asyncftpclient` => `asyncftpclient`
+- `std/smtp` => `smtp`
+- `std/db_common` => `db_connector/db_common`
+- `std/db_sqlite` => `db_connector/db_sqlite`
+- `std/db_mysql` => `db_connector/db_mysql`
+- `std/db_postgres` => `db_connector/db_postgres`
+- `std/db_odbc` => `db_connector/db_odbc`
+- `std/md5` => `checksums/md5`
+- `std/sha1` => `checksums/sha1`
+- `std/sums` => `sums`
diff --git a/changelogs/changelog_2_0_0_details.md b/changelogs/changelog_2_0_0_details.md
new file mode 100644
index 000000000..24dc4edad
--- /dev/null
+++ b/changelogs/changelog_2_0_0_details.md
@@ -0,0 +1,560 @@
+# v2.0.0 - 2023-08-01
+
+
+## Changes affecting backward compatibility
+
+- ORC is now the default memory management strategy. Use
+  `--mm:refc` for a transition period.
+
+- The `threads:on` option is now the default.
+
+- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response. It follows Apache's `HttpClient` (Java), `http` (go) and .NET `HttpWebResponse` (C#) behaviors. Previously it raised a `ValueError`.
+
+- `addr` is now available for all addressable locations,
+  `unsafeAddr` is now deprecated and an alias for `addr`.
+
+- Certain definitions from the default `system` module have been moved to
+  the following new modules:
+
+  - `std/syncio`
+  - `std/assertions`
+  - `std/formatfloat`
+  - `std/objectdollar`
+  - `std/widestrs`
+  - `std/typedthreads`
+  - `std/sysatomics`
+
+  In the future, these definitions will be removed from the `system` module,
+  and their respective modules will have to be imported to use them.
+  Currently, to make these imports required, the `-d:nimPreviewSlimSystem` option
+  may be used.
+
+- Enabling `-d:nimPreviewSlimSystem` also removes the following deprecated
+  symbols in the `system` module:
+  - Aliases with an `Error` suffix to exception types that have a `Defect` suffix
+    (see [exceptions](https://nim-lang.github.io/Nim/exceptions.html)):
+    `ArithmeticError`, `DivByZeroError`, `OverflowError`,
+    `AccessViolationError`, `AssertionError`, `OutOfMemError`, `IndexError`,
+    `FieldError`, `RangeError`, `StackOverflowError`, `ReraiseError`,
+    `ObjectAssignmentError`, `ObjectConversionError`, `FloatingPointError`,
+    `FloatOverflowError`, `FloatUnderflowError`, `FloatInexactError`,
+    `DeadThreadError`, `NilAccessError`
+  - `addQuitProc`, replaced by `exitprocs.addExitProc`
+  - Legacy unsigned conversion operations: `ze`, `ze64`, `toU8`, `toU16`, `toU32`
+  - `TaintedString`, formerly a distinct alias to `string`
+  - `PInt32`, `PInt64`, `PFloat32`, `PFloat64`, aliases to
+    `ptr int32`, `ptr int64`, `ptr float32`, `ptr float64`
+
+- Enabling `-d:nimPreviewSlimSystem` removes the import of `channels_builtin` in
+  in the `system` module, which is replaced by [threading/channels](https://github.com/nim-lang/threading/blob/master/threading/channels.nim). Use the command `nimble install threading` and import `threading/channels`.
+
+- Enabling `-d:nimPreviewCstringConversion` causes `ptr char`, `ptr array[N, char]` and `ptr UncheckedArray[N, char]` to not support conversion to `cstring` anymore.
+
+- Enabling `-d:nimPreviewProcConversion` causes `proc` to not support conversion to
+  `pointer` anymore. `cast` may be used instead.
+
+- The `gc:v2` option is removed.
+
+- The `mainmodule` and `m` options are removed.
+
+- Optional parameters in combination with `: body` syntax ([RFC #405](https://github.com/nim-lang/RFCs/issues/405))
+  are now opt-in via `experimental:flexibleOptionalParams`.
+
+- Automatic dereferencing (experimental feature) is removed.
+
+- The `Math.trunc` polyfill for targeting Internet Explorer was
+  previously included in most JavaScript output files.
+  Now, it is only included with `-d:nimJsMathTruncPolyfill`.
+  If you are targeting Internet Explorer, you may choose to enable this option
+  or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma).
+  Nim uses `Math.trunc` for the division and modulo operators for integers.
+
+- `shallowCopy` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and
+`sink` for optimization purposes.
+
+- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fulfill its promises.
+
+- The `{.this.}` pragma, deprecated since 0.19, has been removed.
+- `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead.
+  ```nim
+  type Foo = distinct ptr int
+
+  # Before:
+  var x: Foo = nil
+  # After:
+  var x: Foo = Foo(nil)
+  ```
+- Removed two type pragma syntaxes deprecated since 0.20, namely
+  `type Foo = object {.final.}`, and `type Foo {.final.} [T] = object`. Instead,
+  use `type Foo[T] {.final.} = object`.
+
+- `foo a = b` now means `foo(a = b)` rather than `foo(a) = b`. This is consistent
+  with the existing behavior of `foo a, b = c` meaning `foo(a, b = c)`.
+  This decision was made with the assumption that the old syntax was used rarely;
+  if your code used the old syntax, please be aware of this change.
+
+- [Overloadable enums](https://nim-lang.github.io/Nim/manual.html#overloadable-enum-value-names) and Unicode Operators
+  are no longer experimental.
+
+- `macros.getImpl` for `const` symbols now returns the full definition node
+  (as `nnkConstDef`) rather than the AST of the constant value.
+
+- Lock levels are deprecated, now a noop.
+
+- `strictEffects` are no longer experimental.
+  Use `legacy:laxEffects` to keep backward compatibility.
+
+- The `gorge`/`staticExec` calls will now return a descriptive message in the output
+  if the execution fails for whatever reason. To get back legacy behaviour, use `-d:nimLegacyGorgeErrors`.
+
+- Pointer to `cstring` conversions now trigger a `[PtrToCstringConv]` warning.
+  This warning will become an error in future versions! Use a `cast` operation
+  like `cast[cstring](x)` instead.
+
+- `logging` will default to flushing all log level messages. To get the legacy behaviour of only flushing Error and Fatal messages, use `-d:nimV1LogFlushBehavior`.
+
+- Redefining templates with the same signature was previously
+  allowed to support certain macro code. To do this explicitly, the
+  `{.redefine.}` pragma has been added. Note that this is only for templates.
+  Implicit redefinition of templates is now deprecated and will give an error in the future.
+
+- Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead.
+
+- Several Standard libraries have been moved to nimble packages, use `nimble` to install them:
+  - `std/punycode` => `punycode`
+  - `std/asyncftpclient` => `asyncftpclient`
+  - `std/smtp` => `smtp`
+  - `std/db_common` => `db_connector/db_common`
+  - `std/db_sqlite` => `db_connector/db_sqlite`
+  - `std/db_mysql` => `db_connector/db_mysql`
+  - `std/db_postgres` => `db_connector/db_postgres`
+  - `std/db_odbc` => `db_connector/db_odbc`
+  - `std/md5` => `checksums/md5`
+  - `std/sha1` => `checksums/sha1`
+  - `std/sums` => `std/sums`
+
+- Previously, calls like `foo(a, b): ...` or `foo(a, b) do: ...` where the final argument of
+  `foo` had type `proc ()` were assumed by the compiler to mean `foo(a, b, proc () = ...)`.
+  This behavior is now deprecated. Use `foo(a, b) do (): ...` or `foo(a, b, proc () = ...)` instead.
+
+- When `--warning[BareExcept]:on` is enabled, if an `except` specifies no exception or any exception not inheriting from `Defect` or `CatchableError`, a `warnBareExcept` warning will be triggered. For example, the following code will emit a warning:
+  ```nim
+  try:
+    discard
+  except: # Warning: The bare except clause is deprecated; use `except CatchableError:` instead [BareExcept]
+    discard
+  ```
+
+- The experimental `strictFuncs` feature now disallows a store to the heap via a `ref` or `ptr` indirection.
+
+- The underscore identifier (`_`) is now generally not added to scope when
+  used as the name of a definition. While this was already the case for
+  variables, it is now also the case for routine parameters, generic
+  parameters, routine declarations, type declarations, etc. This means that the following code now does not compile:
+
+  ```nim
+  proc foo(_: int): int = _ + 1
+  echo foo(1)
+
+  proc foo[_](t: typedesc[_]): seq[_] = @[default(_)]
+  echo foo[int]()
+
+  proc _() = echo "_"
+  _()
+
+  type _ = int
+  let x: _ = 3
+  ```
+
+  Whereas the following code now compiles:
+
+  ```nim
+  proc foo(_, _: int): int = 123
+  echo foo(1, 2)
+
+  proc foo[_, _](): int = 123
+  echo foo[int, bool]()
+
+  proc foo[T, U](_: typedesc[T], _: typedesc[U]): (T, U) = (default(T), default(U))
+  echo foo(int, bool)
+
+  proc _() = echo "one"
+  proc _() = echo "two"
+
+  type _ = int
+  type _ = float
+  ```
+
+- Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages.
+
+- The JavaScript backend now uses [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
+  for 64-bit integer types (`int64` and `uint64`) by default. As this affects
+  JS code generation, code using these types to interface with the JS backend
+  may need to be updated. Note that `int` and `uint` are not affected.
+
+  For compatibility with [platforms that do not support BigInt](https://caniuse.com/bigint)
+  and in the case of potential bugs with the new implementation, the
+  old behavior is currently still supported with the command line option
+  `--jsbigint64:off`.
+
+- The `proc` and `iterator` type classes now respectively only match
+  procs and iterators. Previously both type classes matched any of
+  procs or iterators.
+
+  ```nim
+  proc prc(): int =
+    123
+
+  iterator iter(): int {.closure.} =
+    yield 123
+
+  proc takesProc[T: proc](x: T) = discard
+  proc takesIter[T: iterator](x: T) = discard
+
+  # always compiled:
+  takesProc(prc)
+  takesIter(iter)
+  # no longer compiles:
+  takesProc(iter)
+  takesIter(prc)
+  ```
+
+- The `proc` and `iterator` type classes now accept a calling convention pragma
+  (i.e. `proc {.closure.}`) that must be shared by matching proc or iterator
+  types. Previously, pragmas were parsed but discarded if no parameter list
+  was given.
+
+  This is represented in the AST by an `nnkProcTy`/`nnkIteratorTy` node with
+  an `nnkEmpty` node in the place of the `nnkFormalParams` node, and the pragma
+  node in the same place as in a concrete `proc` or `iterator` type node. This
+  state of the AST may be unexpected to existing code, both due to the
+  replacement of the `nnkFormalParams` node as well as having child nodes
+  unlike other type class AST.
+
+- Signed integer literals in `set` literals now default to a range type of
+  `0..255` instead of `0..65535` (the maximum size of sets).
+
+- `case` statements with `else` branches put before `elif`/`of` branches in macros
+  are rejected with "invalid order of case branches".
+
+- Destructors now default to `.raises: []` (i.e. destructors must not raise
+  unlisted exceptions) and explicitly raising destructors are implementation
+  defined behavior.
+
+- The very old, undocumented `deprecated` pragma statement syntax for
+  deprecated aliases is now a no-op. The regular deprecated pragma syntax is
+  generally sufficient instead.
+
+  ```nim
+  # now does nothing:
+  {.deprecated: [OldName: NewName].}
+
+  # instead use:
+  type OldName* {.deprecated: "use NewName instead".} = NewName
+  const oldName* {.deprecated: "use newName instead".} = newName
+  ```
+
+  `defined(nimalias)` can be used to check for versions when this syntax was
+  available; however since code that used this syntax is usually very old,
+  these deprecated aliases are likely not used anymore and it may make sense
+  to simply remove these statements.
+
+- `getProgramResult` and `setProgramResult` in `std/exitprocs` are no longer
+  declared when they are not available on the backend. Previously it would call
+  `doAssert false` at runtime despite the condition being checkable at compile-time.
+
+- Custom destructors now supports non-var parameters, e.g. ``proc `=destroy`[T: object](x: T)`` is valid. ``proc `=destroy`[T: object](x: var T)`` is deprecated.
+
+- Relative imports will not resolve to searched paths anymore, e.g. `import ./tables` now reports an error properly.
+
+## Standard library additions and changes
+
+[//]: # "Changes:"
+- OpenSSL 3 is now supported.
+- `macros.parseExpr` and `macros.parseStmt` now accept an optional
+  `filename` argument for more informative errors.
+- The `colors` module is expanded with missing colors from the CSS color standard.
+  `colPaleVioletRed` and `colMediumPurple` have also been changed to match the CSS color standard.
+- Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)).
+- The `md5` module now works at compile time and in JavaScript.
+- Changed `mimedb` to use an `OrderedTable` instead of `OrderedTableRef`, to support `const` tables.
+- `strutils.find` now uses and defaults to `last = -1` for whole string searches,
+  making limiting it to just the first char (`last = 0`) valid.
+- `strutils.split` and `strutils.rsplit` now return the source string as a single element for an empty separator.
+- `random.rand` now works with `Ordinal`s.
+- Undeprecated `os.isvalidfilename`.
+- `std/oids` now uses `int64` to store time internally (before, it was int32).
+- `std/uri.Uri` dollar (`$`) improved, precalculates the `string` result length from the `Uri`.
+- `std/uri.Uri.isIpv6` is now exported.
+- `std/logging.ConsoleLogger` and `FileLogger` now have a `flushThreshold` attribute to set what log message levels are automatically flushed. For Nim v1 use `-d:nimFlushAllLogs` to automatically flush all message levels. Flushing all logs is the default behavior for Nim v2.
+
+
+- `std/jsfetch.newFetchOptions` now has default values for all parameters.
+- `std/jsformdata` now accepts the `Blob` data type.
+
+- `std/sharedlist` and `std/sharedtables` are now deprecated, see [RFC #433](https://github.com/nim-lang/RFCs/issues/433).
+
+- There is a new compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove the dependency on the Linux `getrandom` syscall.
+
+  This compile flag only affects Linux builds and is necessary if either compiling on a Linux kernel version < 3.17, or if code built will be executing on kernel < 3.17.
+
+  On Linux kernels < 3.17 (such as kernel 3.10 in RHEL7 and CentOS7), the `getrandom` syscall was not yet introduced. Without this, the `std/sysrand` module will not build properly, and if code is built on a kernel >= 3.17 without the flag, any usage of the `std/sysrand` module will fail to execute on a kernel < 3.17 (since it attempts to perform a syscall to `getrandom`, which isn't present in the current kernel). A compile flag has been added to force the `std/sysrand` module to use /dev/urandom (available since Linux kernel 1.3.30), rather than the `getrandom` syscall. This allows for use of a cryptographically secure PRNG, regardless of kernel support for the `getrandom` syscall.
+
+  When building for RHEL7/CentOS7 for example, the entire build process for nim from a source package would then be:
+  ```sh
+  $ yum install devtoolset-8 # Install GCC version 8 vs the standard 4.8.5 on RHEL7/CentOS7. Alternatively use -d:nimEmulateOverflowChecks. See issue #13692 for details
+  $ scl enable devtoolset-8 bash # Run bash shell with default toolchain of gcc 8
+  $ sh build.sh  # per unix install instructions
+  $ bin/nim c koch  # per unix install instructions
+  $ ./koch boot -d:release  # per unix install instructions
+  $ ./koch tools -d:nimNoGetRandom  # pass the nimNoGetRandom flag to compile std/sysrand without support for getrandom syscall
+  ```
+
+  This is necessary to pass when building Nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for the stdlib (`sysrand` in particular).
+
+[//]: # "Additions:"
+- Added ISO 8601 week date utilities in `times`:
+  - Added `IsoWeekRange`, a range type for weeks in a week-based year.
+  - Added `IsoYear`, a distinct type for a week-based year in contrast to a regular year.
+  - Added an `initDateTime` overload to create a `DateTime` from an ISO week date.
+  - Added `getIsoWeekAndYear` to get an ISO week number and week-based year from a datetime.
+  - Added `getIsoWeeksInYear` to return the number of weeks in a week-based year.
+- Added new modules which were previously part of `std/os`:
+  - Added `std/oserrors` for OS error reporting.
+  - Added `std/envvars` for environment variables handling.
+  - Added `std/cmdline` for reading command line parameters.
+  - Added `std/paths`, `std/dirs`, `std/files`, `std/symlinks` and `std/appdirs`.
+- Added `sep` parameter in `std/uri` to specify the query separator.
+- Added `UppercaseLetters`, `LowercaseLetters`, `PunctuationChars`, `PrintableChars` sets to `std/strutils`.
+- Added `complex.sgn` for obtaining the phase of complex numbers.
+- Added `insertAdjacentText`, `insertAdjacentElement`, `insertAdjacentHTML`,
+  `after`, `before`, `closest`, `append`, `hasAttributeNS`, `removeAttributeNS`,
+  `hasPointerCapture`, `releasePointerCapture`, `requestPointerLock`,
+  `replaceChildren`, `replaceWith`, `scrollIntoViewIfNeeded`, `setHTML`,
+  `toggleAttribute`, and `matches` to `std/dom`.
+- Added [`jsre.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices).
+- Added `capacity` for `string` and `seq` to return the current capacity, see [RFC #460](https://github.com/nim-lang/RFCs/issues/460).
+- Added `openArray[char]` overloads for `std/parseutils` and `std/unicode`, allowing for more code reuse.
+- Added a `safe` parameter to `base64.encodeMime`.
+- Added `parseutils.parseSize` - inverse to `strutils.formatSize` - to parse human readable sizes.
+- Added `minmax` to `sequtils`, as a more efficient `(min(_), max(_))` over sequences.
+- `std/jscore` for the JavaScript target:
+  + Added bindings to [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift)
+  and [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask).
+  + Added `toDateString`, `toISOString`, `toJSON`, `toTimeString`, `toUTCString` converters for `DateTime`.
+- Added `BackwardsIndex` overload for `CacheSeq`.
+- Added support for nested `with` blocks in `std/with`.
+- Added `ensureMove` to the system module. It ensures that the passed argument is moved, otherwise an error is given at the compile time.
+
+
+[//]: # "Deprecations:"
+- Deprecated `selfExe` for Nimscript.
+- Deprecated `std/base64.encode` for collections of arbitrary integer element type.
+  Now only `byte` and `char` are supported.
+
+[//]: # "Removals:"
+- Removed deprecated module `parseopt2`.
+- Removed deprecated module `sharedstrings`.
+- Removed deprecated module `dom_extensions`.
+- Removed deprecated module `LockFreeHash`.
+- Removed deprecated module `events`.
+- Removed deprecated `oids.oidToString`.
+- Removed define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`.
+- Removed deprecated `jsre.test` and `jsre.toString`.
+- Removed deprecated `math.c_frexp`.
+- Removed deprecated `` httpcore.`==` ``.
+- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that take wrong argument types.
+- Removed deprecated `osproc.poDemon`, symbol with typo.
+- Removed deprecated `tables.rightSize`.
+
+
+- Removed deprecated `posix.CLONE_STOPPED`.
+
+
+## Language changes
+
+- [Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) now supports the definition of forbidden tags by the `.forbids` pragma
+  which can be used to disable certain effects in proc types.
+- [Case statement macros](https://nim-lang.github.io/Nim/manual.html#macros-case-statement-macros) are no longer experimental,
+  meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them.
+- Full command syntax and block arguments i.e. `foo a, b: c` are now allowed
+  for the right-hand side of type definitions in type sections. Previously
+  they would error with "invalid indentation".
+
+- Compile-time define changes:
+  - `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`.
+    In the command line, this is defined as `-d:a.b.c`. Older versions can
+    use backticks as in ``defined(`a.b.c`)`` to access such defines.
+  - [Define pragmas for constants](https://nim-lang.github.io/Nim/manual.html#implementation-specific-pragmas-compileminustime-define-pragmas)
+    now support a string argument for qualified define names.
+
+    ```nim
+    # -d:package.FooBar=42
+    const FooBar {.intdefine: "package.FooBar".}: int = 5
+    echo FooBar # 42
+    ```
+
+    This was added to help disambiguate similar define names for different packages.
+    In older versions, this could only be achieved with something like the following:
+
+    ```nim
+    const FooBar = block:
+      const `package.FooBar` {.intdefine.}: int = 5
+      `package.FooBar`
+    ```
+  - A generic `define` pragma for constants has been added that interprets
+    the value of the define based on the type of the constant value.
+    See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#generic-nimdefine-pragma)
+    for a list of supported types.
+
+- [Macro pragmas](https://nim-lang.github.io/Nim/manual.html#userminusdefined-pragmas-macro-pragmas) changes:
+  - Templates now accept macro pragmas.
+  - Macro pragmas for var/let/const sections have been redesigned in a way that works
+    similarly to routine macro pragmas. The new behavior is documented in the
+    [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#extended-macro-pragmas).
+  - Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`,
+    allowing multiple type definitions to be injected in place of the original type definition.
+
+    ```nim
+    import macros
+
+    macro multiply(amount: static int, s: untyped): untyped =
+      let name = $s[0].basename
+      result = newNimNode(nnkTypeSection)
+      for i in 1 .. amount:
+        result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2]))
+
+    type
+      Foo = object
+      Bar {.multiply: 3.} = object
+        x, y, z: int
+      Baz = object
+    # becomes
+    type
+      Foo = object
+      Bar1 = object
+        x, y, z: int
+      Bar2 = object
+        x, y, z: int
+      Bar3 = object
+        x, y, z: int
+      Baz = object
+    ```
+
+- A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference)
+  has been implemented for a variety of basic cases. For example, code like the following now compiles:
+
+  ```nim
+  let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")]
+  ```
+
+- `cstring` is now accepted as a selector in `case` statements, removing the
+  need to convert to `string`. On the JS backend, this is translated directly
+  to a `switch` statement.
+
+- Nim now supports `out` parameters and ["strict definitions"](https://nim-lang.github.io/Nim/manual_experimental.html#strict-definitions-and-nimout-parameters).
+- Nim now offers a [strict mode](https://nim-lang.github.io/Nim/manual_experimental.html#strict-case-objects) for `case objects`.
+
+- IBM Z architecture and macOS m1 arm64 architecture are supported.
+
+- `=wasMoved` can now be overridden by users.
+
+- There is a new pragma called [quirky](https://nim-lang.github.io/Nim/manual_experimental.html#quirky-routines) that can be used to affect the code
+  generation of goto based exception handling. It can improve the produced code size but its effects can be subtle so use it with care.
+
+- Tuple unpacking for variables is now treated as syntax sugar that directly
+  expands into multiple assignments. Along with this, tuple unpacking for
+  variables can now be nested.
+
+  ```nim
+  proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3)
+
+  let (x, (_, y), _, z) = returnsNestedTuple()
+  # roughly becomes
+  let
+    tmpTup1 = returnsNestedTuple()
+    x = tmpTup1[0]
+    tmpTup2 = tmpTup1[1]
+    y = tmpTup2[1]
+    z = tmpTup1[3]
+  ```
+
+  As a result `nnkVarTuple` nodes in variable sections will no longer be
+  reflected in `typed` AST.
+
+- C++ interoperability:
+  - New [`virtual`](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma added.
+  - Improvements to [`constructor`](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma.
+
+## Compiler changes
+
+- The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the
+  reality better. (Nim moved away from all techniques based on "tracing".)
+
+- Defines the `gcRefc` symbol which allows writing specific code for the refc GC.
+
+- `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`,
+  without requiring `-d:nimVersion140` which is now a noop.
+
+- `--styleCheck`, `--hintAsError` and `--warningAsError` now only apply to the current package.
+
+- The switch `--nimMainPrefix:prefix` has been added to add a prefix to the names of `NimMain` and
+  related functions produced on the backend. This prevents conflicts with other Nim
+  static libraries.
+
+- When compiling for release, the flag `-fno-math-errno` is used for GCC.
+- Removed deprecated `LineTooLong` hint.
+- Line numbers and file names of source files work correctly inside templates for JavaScript targets.
+
+- Removed support for LCC (Local C), Pelles C, Digital Mars and Watcom compilers.
+
+
+## Docgen
+
+- `Markdown` is now the default markup language of doc comments (instead
+  of the legacy `RstMarkdown` mode). In this release we begin to separate
+  RST and Markdown features to better follow specification of each
+  language, with the focus on Markdown development.
+  See also [the docs](https://nim-lang.github.io/Nim/markdown_rst.html).
+
+  * Added a `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to
+    select the markup language mode in the doc comments of the current `.nim`
+    file for processing by `nim doc`:
+
+      1. `Markdown` (default) is basically CommonMark (standard Markdown) +
+         some Pandoc Markdown features + some RST features that are missing
+         in our current implementation of CommonMark and Pandoc Markdown.
+      2. `RST` closely follows the RST spec with few additional Nim features.
+      3. `RstMarkdown` is a maximum mix of RST and Markdown features, which
+          is kept for the sake of compatibility and ease of migration.
+
+  * Added separate `md2html` and `rst2html` commands for processing
+    standalone `.md` and `.rst` files respectively (and also `md2tex`/`rst2tex`).
+
+- Added Pandoc Markdown bracket syntax `[...]` for making anchor-less links.
+- Docgen now supports concise syntax for referencing Nim symbols:
+  instead of specifying HTML anchors directly one can use original
+  Nim symbol declarations (adding the aforementioned link brackets
+  `[...]` around them).
+    * To use this feature across modules, a new `importdoc` directive was added.
+      Using this feature for referencing also helps to ensure that links
+      (inside one module or the whole project) are not broken.
+- Added support for RST & Markdown quote blocks (blocks starting with `>`).
+- Added a popular Markdown definition lists extension.
+- Added Markdown indented code blocks (blocks indented by >= 4 spaces).
+- Added syntax for additional parameters to Markdown code blocks:
+
+       ```nim test="nim c $1"
+       ...
+       ```
+
+## Tool changes
+
+- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs` before). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop.
+- nimgrep added the option `--inContext` (and `--notInContext`), which
+  allows to filter only matches with the context block containing a given pattern.
+- nimgrep: names of options containing "include/exclude" are deprecated,
+  e.g. instead of `--includeFile` and `--excludeFile` we have
+  `--filename` and `--notFilename` respectively.
+  Also the semantics are now consistent for such positive/negative filters.
+- koch now supports the `--skipIntegrityCheck` option. The command `koch --skipIntegrityCheck boot -d:release` always builds the compiler twice.
diff --git a/changelogs/changelog_2_2_0.md b/changelogs/changelog_2_2_0.md
new file mode 100644
index 000000000..b50cbeb27
--- /dev/null
+++ b/changelogs/changelog_2_2_0.md
@@ -0,0 +1,248 @@
+# v2.2.0 - 2024-10-02
+
+
+## Changes affecting backward compatibility
+
+- `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` is out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backward compatibility.
+
+- The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec.
+
+- Methods now support implementations based on a VTable by using `--experimental:vtables`. Methods are then confined to the same module where their type has been defined.
+
+- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
+
+- A bug where tuple unpacking assignment with a longer tuple on the RHS than the LHS was allowed has been fixed, i.e. code like:
+  ```nim
+  var a, b: int
+  (a, b) = (1, 2, 3, 4)
+  ```
+  will no longer compile.
+
+- `internalNew` is removed from the `system` module, use `new` instead.
+
+- `bindMethod` in `std/jsffi` is deprecated, don't use it with closures.
+
+- JS backend now supports lambda lifting for closures. Use `--legacy:jsNoLambdaLifting` to emulate old behaviors.
+
+- JS backend now supports closure iterators.
+
+- `owner` in `std/macros` is deprecated.
+
+- Ambiguous type symbols in generic procs and templates now generate symchoice nodes.
+  Previously; in templates they would error immediately at the template definition,
+  and in generic procs a type symbol would arbitrarily be captured, losing the
+  information of the other symbols. This means that generic code can now give
+  errors for ambiguous type symbols, and macros operating on generic proc AST
+  may encounter symchoice nodes instead of the arbitrarily resolved type symbol nodes.
+
+- Partial generic instantiation of routines is no longer allowed. Previously
+  it compiled in niche situations due to bugs in the compiler.
+
+  ```nim
+  proc foo[T, U](x: T, y: U) = echo (x, y)
+  proc foo[T, U](x: var T, y: U) = echo "var ", (x, y)
+
+  proc bar[T]() =
+    foo[float](1, "abc")
+
+  bar[int]() # before: (1.0, "abc"), now: type mismatch, missing generic parameter
+  ```
+
+- `const` values now open a new scope for each constant, meaning symbols
+  declared in them can no longer be used outside or in the value of
+  other constants.
+
+  ```nim
+  const foo = (var a = 1; a)
+  const bar = a # error
+  let baz = a # error
+  ```
+
+- The following POSIX wrappers have had their types changed from signed to
+  unsigned types on OSX and FreeBSD/OpenBSD to correct codegen errors:
+  - `Gid` (was `int32`, is now `uint32`)
+  - `Uid` (was `int32`, is now `uint32`)
+  - `Dev` (was `int32`, is now `uint32` on FreeBSD)
+  - `Nlink` (was `int16`, is now `uint32` on OpenBSD and `uint16` on OSX/other BSD)
+  - `sin6_flowinfo` and `sin6_scope_id` fields of `Sockaddr_in6`
+    (were `int32`, are now `uint32`)
+  - `n_net` field of `Tnetent` (was `int32`, is now `uint32`)
+
+- The `Atomic[T]` type on C++ now uses C11 primitives by default instead of
+  `std::atomic`. To use `std::atomic` instead, `-d:nimUseCppAtomics` can be defined.
+
+
+
+
+
+
+## Standard library additions and changes
+
+[//]: # "Changes:"
+
+- Changed `std/osfiles.copyFile` to allow specifying `bufferSize` instead of a hard-coded one.
+- Changed `std/osfiles.copyFile` to use `POSIX_FADV_SEQUENTIAL` hints for kernel-level aggressive sequential read-aheads.
+- `std/htmlparser` has been moved to a nimble package, use `nimble` or `atlas` to install it.
+- Changed `std/os.copyDir` and `copyDirWithPermissions` to allow skipping special "file" objects like FIFOs, device files, etc on Unix by specifying a `skipSpecial` parameter.
+
+[//]: # "Additions:"
+
+- Added `newStringUninit` to the `system` module, which creates a new string of length `len` like `newString` but with uninitialized content.
+- Added `setLenUninit` to the `system` module, which doesn't initialize
+slots when enlarging a sequence.
+- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.
+- Added `rangeBase` to `std/typetraits` to obtain the base type of a range type or
+  convert a value with a range type to its base type.
+- Added Viewport API for the JavaScript targets in the `dom` module.
+- Added `toSinglyLinkedRing` and `toDoublyLinkedRing` to `std/lists` to convert from `openArray`s.
+- ORC: To be enabled via `nimOrcStats` there is a new API called `GC_orcStats` that can be used to query how many
+  objects the cyclic collector did free. If the number is zero that is a strong indicator that you can use `--mm:arc`
+  instead of `--mm:orc`.
+- A `$` template is provided for `Path` in `std/paths`.
+- `std/hashes.hash(x:string)` changed to produce a 64-bit string `Hash` (based
+on Google's Farm Hash) which is also often faster than the present one.  Define
+`nimStringHash2` to get the old values back.  `--jsbigint=off` mode always only
+produces the old values.  This may impact your automated tests if they depend
+on hash order in some obvious or indirect way.  Using `sorted` or `OrderedTable`
+is often an easy workaround.
+
+[//]: # "Deprecations:"
+
+- Deprecates `system.newSeqUninitialized`, which is replaced by `newSeqUninit`.
+
+[//]: # "Removals:"
+
+
+
+
+
+
+## Language changes
+
+- `noInit` can be used in types and fields to disable member initializers in the C++ backend.
+
+- C++ custom constructors initializers see https://nim-lang.org/docs/manual_experimental.html#constructor-initializer
+
+- `member` can be used to attach a procedure to a C++ type.
+
+- C++ `constructor` now reuses `result` instead creating `this`.
+
+- Tuple unpacking changes:
+  - Tuple unpacking assignment now supports using underscores to discard values.
+    ```nim
+    var a, c: int
+    (a, _, c) = (1, 2, 3)
+    ```
+  - Tuple unpacking variable declarations now support type annotations, but
+    only for the entire tuple.
+    ```nim
+    let (a, b): (int, int) = (1, 2)
+    let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc"))
+    ```
+
+- The experimental option `--experimental:openSym` has been added to allow
+  captured symbols in generic routine and template bodies respectively to be
+  replaced by symbols injected locally by templates/macros at instantiation
+  time. `bind` may be used to keep the captured symbols over the injected ones
+  regardless of enabling the option, but other methods like renaming the
+  captured symbols should be used instead so that the code is not affected by
+  context changes.
+
+  Since this change may affect runtime behavior, the experimental switch
+  `openSym` needs to be enabled; and a warning is given in the case where an
+  injected symbol would replace a captured symbol not bound by `bind` and
+  the experimental switch isn't enabled.
+
+  ```nim
+  const value = "captured"
+  template foo(x: int, body: untyped): untyped =
+    let value {.inject.} = "injected"
+    body
+
+  proc old[T](): string =
+    foo(123):
+      return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
+  echo old[int]() # "captured"
+
+  template oldTempl(): string =
+    block:
+      foo(123):
+        value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
+  echo oldTempl() # "captured"
+
+  {.experimental: "openSym".}
+
+  proc bar[T](): string =
+    foo(123):
+      return value
+  assert bar[int]() == "injected" # previously it would be "captured"
+
+  proc baz[T](): string =
+    bind value
+    foo(123):
+      return value
+  assert baz[int]() == "captured"
+
+  template barTempl(): string =
+    block:
+      foo(123):
+        value
+  assert barTempl() == "injected" # previously it would be "captured"
+
+  template bazTempl(): string =
+    bind value
+    block:
+      foo(123):
+        value
+  assert bazTempl() == "captured"
+  ```
+
+  This option also generates a new node kind `nnkOpenSym` which contains
+  exactly 1 `nnkSym` node. In the future this might be merged with a slightly
+  modified `nnkOpenSymChoice` node but macros that want to support the
+  experimental feature should still handle `nnkOpenSym`, as the node kind would
+  simply not be generated as opposed to being removed.
+
+  Another experimental switch `genericsOpenSym` exists that enables this behavior
+  at instantiation time, meaning templates etc can enable it specifically when
+  they are being called. However this does not generate `nnkOpenSym` nodes
+  (unless the other switch is enabled) and so doesn't reflect the regular
+  behavior of the switch.
+
+  ```nim
+  const value = "captured"
+  template foo(x: int, body: untyped): untyped =
+    let value {.inject.} = "injected"
+    {.push experimental: "genericsOpenSym".}
+    body
+    {.pop.}
+
+  proc bar[T](): string =
+    foo(123):
+      return value
+  echo bar[int]() # "injected"
+
+  template barTempl(): string =
+    block:
+      var res: string
+      foo(123):
+        res = value
+      res
+  assert barTempl() == "injected"
+  ```
+
+
+
+
+
+## Compiler changes
+
+- `--nimcache` using a relative path as the argument in a config file is now relative to the config file instead of the current directory.
+
+
+
+
+
+## Tool changes
+
+- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package.
diff --git a/changelogs/changelog_X_XX_X.md b/changelogs/changelog_X_XX_X.md
new file mode 100644
index 000000000..f8a09a535
--- /dev/null
+++ b/changelogs/changelog_X_XX_X.md
@@ -0,0 +1,23 @@
+# v2.xx.x - yyyy-mm-dd
+
+This is an example file.
+The changes should go to changelog.md!
+
+## Changes affecting backward compatibility
+
+- `foo` now behaves differently, use `-d:nimLegacyFoo` for previous behavior.
+
+## Standard library additions and changes
+
+- Added `example.exampleProc`.
+
+- Changed `example.foo` to take additional `bar` parameter.
+
+## Language changes
+
+
+## Compiler changes
+
+
+## Tool changes
+
diff --git a/changelogs/readme.md b/changelogs/readme.md
new file mode 100644
index 000000000..3ff1e1b7b
--- /dev/null
+++ b/changelogs/readme.md
@@ -0,0 +1,61 @@
+# Update changelog
+After each release, call:
+```
+git mv changelog.md changelogs/changelog_0_19_0.md # use correct version
+cp changelogs/changelog_X_XX_X.md changelog.md
+git add changelog.md
+```
+
+## Recent changelogs are saved here (with their git history)
+
+## Older changelogs are stored in https://github.com/nim-lang/website
+
+## source files:
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2009-12-21-version-086-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2010-03-14-version-088-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2010-10-20-version-0810-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2011-07-10-version-0812-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2012-02-09-version-0814-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2012-09-23-version-090-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2013-05-20-version-092-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2014-04-21-version-094-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2014-10-19-version-096-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2014-12-29-version-0102-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2015-04-30-version-0110-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2015-05-04-version-0112-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2015-10-27-version-0120-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-01-18-version-0130-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-06-07-version-0140-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-06-09-version-0142-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-09-30-version-0150-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-10-23-version-0152-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2017-01-08-version-0160-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2017-05-17-version-0170-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2017-09-07-version-0172-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2018-03-01-version-0180-released.md
+* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2018-09-26-version-0190-released.md
+
+## urls:
+* https://nim-lang.org/blog/2009/12/21/version-086-released.html
+* https://nim-lang.org/blog/2010/03/14/version-088-released.html
+* https://nim-lang.org/blog/2010/10/20/version-0810-released.html
+* https://nim-lang.org/blog/2011/07/10/version-0812-released.html
+* https://nim-lang.org/blog/2012/02/09/version-0814-released.html
+* https://nim-lang.org/blog/2012/09/23/version-090-released.html
+* https://nim-lang.org/blog/2013/05/20/version-092-released.html
+* https://nim-lang.org/blog/2014/04/21/version-094-released.html
+* https://nim-lang.org/blog/2014/10/19/version-096-released.html
+* https://nim-lang.org/blog/2014/12/29/version-0102-released.html
+* https://nim-lang.org/blog/2015/04/30/version-0110-released.html
+* https://nim-lang.org/blog/2015/05/04/version-0112-released.html
+* https://nim-lang.org/blog/2015/10/27/version-0120-released.html
+* https://nim-lang.org/blog/2016/01/18/version-0130-released.html
+* https://nim-lang.org/blog/2016/06/07/version-0140-released.html
+* https://nim-lang.org/blog/2016/06/09/version-0142-released.html
+* https://nim-lang.org/blog/2016/09/30/version-0150-released.html
+* https://nim-lang.org/blog/2016/10/23/version-0152-released.html
+* https://nim-lang.org/blog/2017/01/08/version-0160-released.html
+* https://nim-lang.org/blog/2017/05/17/version-0170-released.html
+* https://nim-lang.org/blog/2017/09/07/version-0172-released.html
+* https://nim-lang.org/blog/2018/03/01/version-0180-released.html
+* https://nim-lang.org/blog/2018/09/26/version-0190-released.html
diff --git a/ci/action.nim b/ci/action.nim
new file mode 100644
index 000000000..ad0f4df0b
--- /dev/null
+++ b/ci/action.nim
@@ -0,0 +1,26 @@
+import std/[strutils, os, osproc, parseutils, strformat]
+
+
+proc main() =
+  var msg = ""
+  const cmd = "./koch boot --mm:orc -d:release"
+
+  let (output, exitCode) = execCmdEx(cmd)
+
+  doAssert exitCode == 0, output
+
+  let start = rfind(output, "Hint: mm")
+  doAssert parseUntil(output, msg, "; proj", start) > 0, output
+
+  let (commitHash, _) = execCmdEx("""git log --format="%H" -n 1""")
+
+  let welcomeMessage = fmt"""Thanks for your hard work on this PR!
+The lines below are statistics of the Nim compiler built from {commitHash}
+
+{msg}
+"""
+  createDir "ci/nimcache"
+  writeFile "ci/nimcache/results.txt", welcomeMessage
+
+when isMainModule:
+  main()
diff --git a/ci/build.bat b/ci/build.bat
deleted file mode 100644
index cdce8d3d2..000000000
--- a/ci/build.bat
+++ /dev/null
@@ -1,14 +0,0 @@
-REM Some debug info
-echo "Running on %CI_RUNNER_ID% (%CI_RUNNER_DESCRIPTION%) with tags %CI_RUNNER_TAGS%."
-gcc -v
-
-git clone --depth 1 https://github.com/nim-lang/csources.git
-cd csources
-call build64.bat
-cd ..
-set PATH=%CD%\bin;%PATH%
-nim -v
-nim c koch
-koch.exe boot
-copy bin/nim bin/nimd
-koch.exe boot -d:release
diff --git a/ci/build.sh b/ci/build.sh
deleted file mode 100644
index 6321fffba..000000000
--- a/ci/build.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-sh ci/deps.sh
-
-# Build from C sources.
-git clone --depth 1 https://github.com/nim-lang/csources.git
-cd csources
-sh build.sh
-cd ..
-# Add Nim to the PATH
-export PATH=$(pwd)/bin${PATH:+:$PATH}
-# Bootstrap.
-nim -v
-nim c koch
-./koch boot
-cp bin/nim bin/nimd
-./koch boot -d:release
diff --git a/ci/build_autogen.bat b/ci/build_autogen.bat
new file mode 100644
index 000000000..0b6ca566e
--- /dev/null
+++ b/ci/build_autogen.bat
@@ -0,0 +1,26 @@
+@echo off
+rem DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
+rem Build development version of the compiler; can be rerun safely
+rem bare bones version of ci/funs.sh adapted for windows.
+
+rem Read in some common shared variables (shared with other tools),
+rem see https://stackoverflow.com/questions/3068929/how-to-read-file-contents-into-a-variable-in-a-batch-file
+for /f "delims== tokens=1,2" %%G in (config/build_config.txt) do set %%G=%%H
+SET nim_csources=bin\nim_csources_%nim_csourcesHash%.exe
+echo "building from csources: %nim_csources%"
+
+if not exist %nim_csourcesDir% (
+  git clone -q --depth 1 -b %nim_csourcesBranch% %nim_csourcesUrl% %nim_csourcesDir%
+)
+
+if not exist %nim_csources% (
+  cd %nim_csourcesDir%
+  git checkout %nim_csourcesHash%
+  echo "%PROCESSOR_ARCHITECTURE%"
+  if "%PROCESSOR_ARCHITECTURE%"=="AMD64" (
+    SET ARCH=64
+  )
+  CALL build.bat
+  cd ..
+  copy /y bin\nim.exe  %nim_csources%
+)
diff --git a/ci/deps.bat b/ci/deps.bat
deleted file mode 100644
index bda1fe14f..000000000
--- a/ci/deps.bat
+++ /dev/null
@@ -1,4 +0,0 @@
-nim e install_nimble.nims
-nim e tests/test_nimscript.nims
-nimble update
-nimble install -y zip opengl sdl1 jester@#head niminst
diff --git a/ci/deps.sh b/ci/deps.sh
deleted file mode 100644
index f0f831a2a..000000000
--- a/ci/deps.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-# Some debug info
-echo "Running on $CI_RUNNER_ID ($CI_RUNNER_DESCRIPTION) with tags $CI_RUNNER_TAGS."
-
-# Packages
-apt-get update -qq
-apt-get install -y -qq build-essential git libcurl4-openssl-dev libsdl1.2-dev libgc-dev nodejs
-
-gcc -v
-
-export PATH=$(pwd)/bin${PATH:+:$PATH}
-
-# Nimble deps
-nim e install_nimble.nims
-nim e tests/test_nimscript.nims
-nimble update
-nimble install zip opengl sdl1 jester@#head niminst
diff --git a/ci/funs.sh b/ci/funs.sh
new file mode 100644
index 000000000..026366198
--- /dev/null
+++ b/ci/funs.sh
@@ -0,0 +1,147 @@
+# Utilities used in CI pipelines and tooling to avoid duplication.
+# Avoid top-level statements.
+# Prefer nim scripts whenever possible.
+# functions starting with `_` are considered internal, less stable.
+
+echo_run () {
+  # echo's a command before running it, which helps understanding logs
+  echo ""
+  echo "cmd: $@" # in azure we could also use this: echo '##[section]"$@"'
+  "$@"
+}
+
+nimGetLastCommit() {
+  git log --no-merges -1 --pretty=format:"%s"
+}
+
+nimIsCiSkip(){
+  # D20210329T004830:here refs https://github.com/microsoft/azure-pipelines-agent/issues/2944
+  # `--no-merges` is needed to avoid merge commits which occur for PR's.
+  # $(Build.SourceVersionMessage) is not helpful
+  # nor is `github.event.head_commit.message` for github actions.
+  # Note: `[skip ci]` is now handled automatically for github actions, see https://github.blog/changelog/2021-02-08-github-actions-skip-pull-request-and-push-workflows-with-skip-ci/
+  commitMsg=$(nimGetLastCommit)
+  echo commitMsg: "$commitMsg"
+  if [[ $commitMsg == *"[skip ci]"* ]]; then
+    echo "skipci: true"
+    return 0
+  else
+    echo "skipci: false"
+    return 1
+  fi
+}
+
+nimInternalInstallDepsWindows(){
+  echo_run mkdir dist
+  echo_run curl -L https://nim-lang.org/download/mingw64.7z -o dist/mingw64.7z
+  echo_run curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip
+  echo_run 7z x dist/mingw64.7z -odist
+  echo_run 7z x dist/dlls.zip -obin
+}
+
+nimInternalBuildKochAndRunCI(){
+  echo_run nim c koch
+  if ! echo_run ./koch runCI; then
+    echo_run echo "runCI failed"
+    echo_run nim r tools/ci_testresults.nim
+    return 1
+  fi
+}
+
+nimDefineVars(){
+  . config/build_config.txt
+  nim_csources=bin/nim_csources_$nim_csourcesHash
+}
+
+_nimNumCpu(){
+  # linux: $(nproc)
+  # FreeBSD | macOS: $(sysctl -n hw.ncpu)
+  # OpenBSD: $(sysctl -n hw.ncpuonline)
+  # windows: $NUMBER_OF_PROCESSORS ?
+  if env | grep -q '^NIMCORES='; then
+    echo $NIMCORES
+  else
+    echo $(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || 1)
+  fi
+}
+
+_nimBuildCsourcesIfNeeded(){
+  # if some systems cannot use make or gmake, we could add support for calling `build.sh`
+  # but this is slower (not parallel jobs) and would require making build.sh
+  # understand the arguments passed to the makefile (e.g. `CC=gcc ucpu=amd64 uos=darwin`),
+  # instead of `--cpu amd64 --os darwin`.
+  unamestr=$(uname)
+  # uname values: https://en.wikipedia.org/wiki/Uname
+  if [ "$unamestr" = 'FreeBSD' ]; then
+    makeX=gmake
+  elif [ "$unamestr" = 'OpenBSD' ]; then
+    makeX=gmake
+  elif [ "$unamestr" = 'NetBSD' ]; then
+    makeX=gmake
+  elif [ "$unamestr" = 'CROSSOS' ]; then
+    makeX=gmake
+  elif [ "$unamestr" = 'SunOS' ]; then
+    makeX=gmake
+  else
+    makeX=make
+  fi
+  nCPU=$(_nimNumCpu)
+  echo_run which $makeX
+  # parallel jobs (5X faster on 16 cores: 10s instead of 50s)
+  echo_run $makeX -C $nim_csourcesDir -j $((nCPU + 2)) -l $nCPU "$@"
+  # keep $nim_csources in case needed to investigate bootstrap issues
+  # without having to rebuild
+  echo_run cp bin/nim $nim_csources
+}
+
+nimCiSystemInfo(){
+  nimDefineVars
+  echo_run eval echo '$'nim_csources
+  echo_run pwd
+  echo_run date
+  echo_run uname -a
+  echo_run git log --no-merges -1 --pretty=oneline
+  echo_run eval echo '$'PATH
+  echo_run gcc -v
+  echo_run node -v
+  echo_run make -v
+}
+
+nimCsourcesHash(){
+  nimDefineVars
+  echo $nim_csourcesHash
+}
+
+nimBuildCsourcesIfNeeded(){
+  # goal: allow cachine each tagged version independently
+  # to avoid rebuilding, so that tools like `git bisect`
+  # can grab a cached past version without rebuilding.
+  nimDefineVars
+  (
+    set -e
+    # avoid polluting caller scope with internal variable definitions.
+    if test -f "$nim_csources"; then
+      echo "$nim_csources exists."
+    else
+      if test -d "$nim_csourcesDir"; then
+        echo "$nim_csourcesDir exists."
+      else
+        # Note: using git tags would allow fetching just what's needed, unlike git hashes, e.g.
+        # via `git clone -q --depth 1 --branch $tag $nim_csourcesUrl`.
+        echo_run git clone -q --depth 1 -b $nim_csourcesBranch \
+            $nim_csourcesUrl "$nim_csourcesDir"
+        # old `git` versions don't support -C option, using `cd` explicitly:
+        echo_run cd "$nim_csourcesDir"
+        echo_run git checkout $nim_csourcesHash
+        echo_run cd "$OLDPWD"
+        # if needed we could also add: `git reset --hard $nim_csourcesHash`
+      fi
+      _nimBuildCsourcesIfNeeded "$@"
+    fi
+
+    echo_run rm -f bin/nim
+      # fixes bug #17913, but it's unclear why it's needed, maybe specific to MacOS Big Sur 11.3 on M1 arch?
+    echo_run cp $nim_csources bin/nim
+    echo_run $nim_csources -v
+  )
+}
diff --git a/ci/nsis_build.bat b/ci/nsis_build.bat
deleted file mode 100644
index 4e9d0d67c..000000000
--- a/ci/nsis_build.bat
+++ /dev/null
@@ -1,59 +0,0 @@
-REM - Run the full testsuite;  tests\testament\tester all
-
-REM - Uncomment the list of changes in news.txt
-REM - write a news ticker entry
-REM - Update the version
-
-REM - Generate the full docs;  koch web0
-REM - Generate the installers;
-REM - Update the version in system.nim
-REM - Test the installers
-REM - Tag the release
-REM - Merge devel into master
-REM - Update csources
-
-set NIMVER=%1
-
-Rem Build -docs file:
-koch web0
-cd web\upload
-7z a -tzip docs-%NIMVER%.zip *.html
-move /y docs-%NIMVER%.zip download
-cd ..\..
-
-Rem Build csources
-koch csources -d:release || exit /b
-
-rem Grab C sources and nimsuggest
-git clone --depth 1 https://github.com/nim-lang/csources.git
-
-set PATH=%CD%\bin;%PATH%
-
-ReM Build Win32 version:
-
-set PATH=C:\Users\araq\projects\mingw32\bin;%PATH%
-cd csources
-call build.bat
-cd ..
-ReM Rebuilding koch is necessary because it uses its pointer size to determine
-ReM which mingw link to put in the NSIS installer.
-nim c --out:koch_temp koch || exit /b
-koch_temp boot -d:release || exit /b
-koch_temp nsis -d:release || exit /b
-koch_temp zip -d:release || exit /b
-dir build
-move /y build\nim_%NIMVER%.exe build\nim-%NIMVER%_x32.exe || exit /b
-move /y build\nim-%NIMVER%.zip build\nim-%NIMVER%_x32.zip || exit /b
-
-
-ReM Build Win64 version:
-set PATH=C:\Users\araq\projects\mingw64\bin;%PATH%
-cd csources
-call build64.bat
-cd ..
-nim c --out:koch_temp koch || exit /b
-koch_temp boot -d:release || exit /b
-koch_temp nsis -d:release || exit /b
-koch_temp zip -d:release || exit /b
-move /y build\nim_%NIMVER%.exe build\nim-%NIMVER%_x64.exe || exit /b
-move /y build\nim-%NIMVER%.zip build\nim-%NIMVER%_x64.zip || exit /b
diff --git a/compiler.nimble b/compiler.nimble
deleted file mode 100644
index 5929df3ec..000000000
--- a/compiler.nimble
+++ /dev/null
@@ -1,9 +0,0 @@
-
-version = system.NimVersion
-author = "Andreas Rumpf"
-description = "Compiler package providing the compiler sources as a library."
-license = "MIT"
-
-installDirs = @["compiler"]
-
-requires "nim >= 0.14.0"
diff --git a/compiler/aliasanalysis.nim b/compiler/aliasanalysis.nim
new file mode 100644
index 000000000..e24c6d8e2
--- /dev/null
+++ b/compiler/aliasanalysis.nim
@@ -0,0 +1,128 @@
+
+import ast
+
+import std / assertions
+
+const
+  PathKinds0* = {nkDotExpr, nkCheckedFieldExpr,
+                 nkBracketExpr, nkDerefExpr, nkHiddenDeref,
+                 nkAddr, nkHiddenAddr,
+                 nkObjDownConv, nkObjUpConv}
+  PathKinds1* = {nkHiddenStdConv, nkHiddenSubConv}
+
+proc skipConvDfa*(n: PNode): PNode =
+  result = n
+  while true:
+    case result.kind
+    of nkObjDownConv, nkObjUpConv:
+      result = result[0]
+    of PathKinds1:
+      result = result[1]
+    else: break
+
+proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
+  var n = orig
+  while true:
+    case n.kind
+    of PathKinds0 - {nkHiddenDeref, nkDerefExpr}:
+      n = n[0]
+    of PathKinds1:
+      n = n[1]
+    of nkHiddenDeref, nkDerefExpr:
+      # We "own" sinkparam[].loc but not ourVar[].location as it is a nasty
+      # pointer indirection.
+      # bug #14159, we cannot reason about sinkParam[].location as it can
+      # still be shared for tyRef.
+      n = n[0]
+      return n.kind == nkSym and n.sym.owner == owner and
+         (n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned})
+    else: break
+  # XXX Allow closure deref operations here if we know
+  # the owner controlled the closure allocation?
+  result = n.kind == nkSym and n.sym.owner == owner and
+    {sfGlobal, sfThread, sfCursor} * n.sym.flags == {} and
+    (n.sym.kind != skParam or isSinkParam(n.sym)) # or n.sym.typ.kind == tyVar)
+  # Note: There is a different move analyzer possible that checks for
+  # consume(param.key); param.key = newValue  for all paths. Then code like
+  #
+  #   let splited = split(move self.root, x)
+  #   self.root = merge(splited.lower, splited.greater)
+  #
+  # could be written without the ``move self.root``. However, this would be
+  # wrong! Then the write barrier for the ``self.root`` assignment would
+  # free the old data and all is lost! Lesson: Don't be too smart, trust the
+  # lower level C++ optimizer to specialize this code.
+
+type AliasKind* = enum
+  yes, no, maybe
+
+proc aliases*(obj, field: PNode): AliasKind =
+  # obj -> field:
+  # x -> x: true
+  # x -> x.f: true
+  # x.f -> x: false
+  # x.f -> x.f: true
+  # x.f -> x.v: false
+  # x -> x[]: true
+  # x[] -> x: false
+  # x -> x[0]: true
+  # x[0] -> x: false
+  # x[0] -> x[0]: true
+  # x[0] -> x[1]: false
+  # x -> x[i]: true
+  # x[i] -> x: false
+  # x[i] -> x[i]: maybe; Further analysis could make this return true when i is a runtime-constant
+  # x[i] -> x[j]: maybe; also returns maybe if only one of i or j is a compiletime-constant
+  template collectImportantNodes(result, n) =
+    var result: seq[PNode] = @[]
+    var n = n
+    while true:
+      case n.kind
+      of PathKinds0 - {nkDotExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref}:
+        n = n[0]
+      of PathKinds1:
+        n = n[1]
+      of nkDotExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref:
+        result.add n
+        n = n[0]
+      of nkSym:
+        result.add n
+        break
+      else: return no
+
+  collectImportantNodes(objImportantNodes, obj)
+  collectImportantNodes(fieldImportantNodes, field)
+
+  # If field is less nested than obj, then it cannot be part of/aliased by obj
+  if fieldImportantNodes.len < objImportantNodes.len: return no
+
+  result = yes
+  for i in 1..objImportantNodes.len:
+    # We compare the nodes leading to the location of obj and field
+    # with each other.
+    # We continue until they diverge, in which case we return no, or
+    # until we reach the location of obj, in which case we do not need
+    # to look further, since field must be part of/aliased by obj now.
+    # If we encounter an element access using an index which is a runtime value,
+    # we simply return maybe instead of yes; should further nodes not diverge.
+    let currFieldPath = fieldImportantNodes[^i]
+    let currObjPath = objImportantNodes[^i]
+
+    if currFieldPath.kind != currObjPath.kind:
+      return no
+
+    case currFieldPath.kind
+    of nkSym:
+      if currFieldPath.sym != currObjPath.sym: return no
+    of nkDotExpr:
+      if currFieldPath[1].sym != currObjPath[1].sym: return no
+    of nkDerefExpr, nkHiddenDeref:
+      discard
+    of nkBracketExpr:
+      if currFieldPath[1].kind in nkLiterals and currObjPath[1].kind in nkLiterals:
+        if currFieldPath[1].intVal != currObjPath[1].intVal:
+          return no
+      else:
+        result = maybe
+    else: assert false # unreachable
+
diff --git a/compiler/aliases.nim b/compiler/aliases.nim
index f79210dd7..fa1167753 100644
--- a/compiler/aliases.nim
+++ b/compiler/aliases.nim
@@ -10,7 +10,12 @@
 ## Simple alias analysis for the HLO and the code generators.
 
 import
-  ast, astalgo, types, trees, intsets, msgs
+  ast, astalgo, types, trees
+
+import std/intsets
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 type
   TAnalysisResult* = enum
@@ -22,17 +27,17 @@ proc isPartOfAux(n: PNode, b: PType, marker: var IntSet): TAnalysisResult =
   result = arNo
   case n.kind
   of nkRecList:
-    for i in countup(0, sonsLen(n) - 1):
-      result = isPartOfAux(n.sons[i], b, marker)
+    for i in 0..<n.len:
+      result = isPartOfAux(n[i], b, marker)
       if result == arYes: return
   of nkRecCase:
-    assert(n.sons[0].kind == nkSym)
-    result = isPartOfAux(n.sons[0], b, marker)
+    assert(n[0].kind == nkSym)
+    result = isPartOfAux(n[0], b, marker)
     if result == arYes: return
-    for i in countup(1, sonsLen(n) - 1):
-      case n.sons[i].kind
+    for i in 1..<n.len:
+      case n[i].kind
       of nkOfBranch, nkElse:
-        result = isPartOfAux(lastSon(n.sons[i]), b, marker)
+        result = isPartOfAux(lastSon(n[i]), b, marker)
         if result == arYes: return
       else: discard "isPartOfAux(record case branch)"
   of nkSym:
@@ -46,14 +51,16 @@ proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
   if compareTypes(a, b, dcEqIgnoreDistinct): return arYes
   case a.kind
   of tyObject:
-    if a.sons[0] != nil:
-      result = isPartOfAux(a.sons[0].skipTypes(skipPtrs), b, marker)
+    if a.baseClass != nil:
+      result = isPartOfAux(a.baseClass.skipTypes(skipPtrs), b, marker)
     if result == arNo: result = isPartOfAux(a.n, b, marker)
   of tyGenericInst, tyDistinct, tyAlias, tySink:
-    result = isPartOfAux(lastSon(a), b, marker)
-  of tyArray, tySet, tyTuple:
-    for i in countup(0, sonsLen(a) - 1):
-      result = isPartOfAux(a.sons[i], b, marker)
+    result = isPartOfAux(skipModifier(a), b, marker)
+  of tySet, tyArray:
+    result = isPartOfAux(a.elementType, b, marker)
+  of tyTuple:
+    for aa in a.kids:
+      result = isPartOfAux(aa, b, marker)
       if result == arYes: return
   else: discard
 
@@ -74,24 +81,29 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
   ## cases:
   ##
   ## YES-cases:
+  ##  ```
   ##  x    <| x   # for general trees
   ##  x[]  <| x
   ##  x[i] <| x
   ##  x.f  <| x
+  ##  ```
   ##
   ## NO-cases:
+  ## ```
   ## x           !<| y    # depending on type and symbol kind
   ## x[constA]   !<| x[constB]
   ## x.f         !<| x.g
   ## x.f         !<| y.f  iff x !<= y
+  ## ```
   ##
   ## MAYBE-cases:
   ##
+  ##  ```
   ##  x[] ?<| y[]   iff compatible type
   ##
   ##
   ##  x[]  ?<| y  depending on type
-  ##
+  ##  ```
   if a.kind == b.kind:
     case a.kind
     of nkSym:
@@ -106,9 +118,11 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
         # use expensive type check:
         if isPartOf(a.sym.typ, b.sym.typ) != arNo:
           result = arMaybe
+        else:
+          result = arNo
     of nkBracketExpr:
       result = isPartOf(a[0], b[0])
-      if len(a) >= 2 and len(b) >= 2:
+      if a.len >= 2 and b.len >= 2:
         # array accesses:
         if result == arYes and isDeepConstExpr(a[1]) and isDeepConstExpr(b[1]):
           # we know it's the same array and we have 2 constant indexes;
@@ -141,7 +155,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
       result = isPartOf(a[1], b[1])
     of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
       result = isPartOf(a[0], b[0])
-    else: discard
+    else: result = arNo
     # Calls return a new location, so a default of ``arNo`` is fine.
   else:
     # go down recursively; this is quite demanding:
@@ -157,6 +171,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
 
     of DerefKinds:
       # a* !<| b[] iff
+      result = arNo
       if isPartOf(a.typ, b.typ) != arNo:
         result = isPartOf(a, b[0])
         if result == arNo: result = arMaybe
@@ -178,7 +193,9 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
         if isPartOf(a.typ, b.typ) != arNo:
           result = isPartOf(a[0], b)
           if result == arNo: result = arMaybe
-      else: discard
+        else:
+          result = arNo
+      else: result = arNo
     of nkObjConstr:
       result = arNo
       for i in 1..<b.len:
@@ -186,4 +203,16 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
         if res != arNo:
           result = res
           if res == arYes: break
-    else: discard
+    of nkCallKinds:
+      result = arNo
+      for i in 1..<b.len:
+        let res = isPartOf(a, b[i])
+        if res != arNo:
+          result = res
+          if res == arYes: break
+    of nkBracket:
+      if b.len > 0:
+        result = isPartOf(a, b[0])
+      else:
+        result = arNo
+    else: result = arNo
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 40c1b064d..a342e1ea7 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -10,224 +10,38 @@
 # abstract syntax tree + symbol table
 
 import
-  lineinfos, hashes, nversion, options, strutils, std / sha1, ropes, idents,
-  intsets, idgen
+  lineinfos, options, ropes, idents, int128, wordrecg
 
-type
-  TCallingConvention* = enum
-    ccDefault,                # proc has no explicit calling convention
-    ccStdCall,                # procedure is stdcall
-    ccCDecl,                  # cdecl
-    ccSafeCall,               # safecall
-    ccSysCall,                # system call
-    ccInline,                 # proc should be inlined
-    ccNoInline,               # proc should not be inlined
-    ccFastCall,               # fastcall (pass parameters in registers)
-    ccClosure,                # proc has a closure
-    ccNoConvention            # needed for generating proper C procs sometimes
+import std/[tables, hashes]
+from std/strutils import toLowerAscii
 
-const
-  CallingConvToStr*: array[TCallingConvention, string] = ["", "stdcall",
-    "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall",
-    "closure", "noconv"]
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+export int128
+
+import nodekinds
+export nodekinds
 
 type
-  TNodeKind* = enum # order is extremely important, because ranges are used
-                    # to check whether a node belongs to a certain class
-    nkNone,               # unknown node kind: indicates an error
-                          # Expressions:
-                          # Atoms:
-    nkEmpty,              # the node is empty
-    nkIdent,              # node is an identifier
-    nkSym,                # node is a symbol
-    nkType,               # node is used for its typ field
-
-    nkCharLit,            # a character literal ''
-    nkIntLit,             # an integer literal
-    nkInt8Lit,
-    nkInt16Lit,
-    nkInt32Lit,
-    nkInt64Lit,
-    nkUIntLit,            # an unsigned integer literal
-    nkUInt8Lit,
-    nkUInt16Lit,
-    nkUInt32Lit,
-    nkUInt64Lit,
-    nkFloatLit,           # a floating point literal
-    nkFloat32Lit,
-    nkFloat64Lit,
-    nkFloat128Lit,
-    nkStrLit,             # a string literal ""
-    nkRStrLit,            # a raw string literal r""
-    nkTripleStrLit,       # a triple string literal """
-    nkNilLit,             # the nil literal
-                          # end of atoms
-    nkComesFrom,          # "comes from" template/macro information for
-                          # better stack trace generation
-    nkDotCall,            # used to temporarily flag a nkCall node;
-                          # this is used
-                          # for transforming ``s.len`` to ``len(s)``
-
-    nkCommand,            # a call like ``p 2, 4`` without parenthesis
-    nkCall,               # a call like p(x, y) or an operation like +(a, b)
-    nkCallStrLit,         # a call with a string literal
-                          # x"abc" has two sons: nkIdent, nkRStrLit
-                          # x"""abc""" has two sons: nkIdent, nkTripleStrLit
-    nkInfix,              # a call like (a + b)
-    nkPrefix,             # a call like !a
-    nkPostfix,            # something like a! (also used for visibility)
-    nkHiddenCallConv,     # an implicit type conversion via a type converter
-
-    nkExprEqExpr,         # a named parameter with equals: ''expr = expr''
-    nkExprColonExpr,      # a named parameter with colon: ''expr: expr''
-    nkIdentDefs,          # a definition like `a, b: typeDesc = expr`
-                          # either typeDesc or expr may be nil; used in
-                          # formal parameters, var statements, etc.
-    nkVarTuple,           # a ``var (a, b) = expr`` construct
-    nkPar,                # syntactic (); may be a tuple constructor
-    nkObjConstr,          # object constructor: T(a: 1, b: 2)
-    nkCurly,              # syntactic {}
-    nkCurlyExpr,          # an expression like a{i}
-    nkBracket,            # syntactic []
-    nkBracketExpr,        # an expression like a[i..j, k]
-    nkPragmaExpr,         # an expression like a{.pragmas.}
-    nkRange,              # an expression like i..j
-    nkDotExpr,            # a.b
-    nkCheckedFieldExpr,   # a.b, but b is a field that needs to be checked
-    nkDerefExpr,          # a^
-    nkIfExpr,             # if as an expression
-    nkElifExpr,
-    nkElseExpr,
-    nkLambda,             # lambda expression
-    nkDo,                 # lambda block appering as trailing proc param
-    nkAccQuoted,          # `a` as a node
-
-    nkTableConstr,        # a table constructor {expr: expr}
-    nkBind,               # ``bind expr`` node
-    nkClosedSymChoice,    # symbol choice node; a list of nkSyms (closed)
-    nkOpenSymChoice,      # symbol choice node; a list of nkSyms (open)
-    nkHiddenStdConv,      # an implicit standard type conversion
-    nkHiddenSubConv,      # an implicit type conversion from a subtype
-                          # to a supertype
-    nkConv,               # a type conversion
-    nkCast,               # a type cast
-    nkStaticExpr,         # a static expr
-    nkAddr,               # a addr expression
-    nkHiddenAddr,         # implicit address operator
-    nkHiddenDeref,        # implicit ^ operator
-    nkObjDownConv,        # down conversion between object types
-    nkObjUpConv,          # up conversion between object types
-    nkChckRangeF,         # range check for floats
-    nkChckRange64,        # range check for 64 bit ints
-    nkChckRange,          # range check for ints
-    nkStringToCString,    # string to cstring
-    nkCStringToString,    # cstring to string
-                          # end of expressions
-
-    nkAsgn,               # a = b
-    nkFastAsgn,           # internal node for a fast ``a = b``
-                          # (no string copy)
-    nkGenericParams,      # generic parameters
-    nkFormalParams,       # formal parameters
-    nkOfInherit,          # inherited from symbol
-
-    nkImportAs,           # a 'as' b in an import statement
-    nkProcDef,            # a proc
-    nkMethodDef,          # a method
-    nkConverterDef,       # a converter
-    nkMacroDef,           # a macro
-    nkTemplateDef,        # a template
-    nkIteratorDef,        # an iterator
-
-    nkOfBranch,           # used inside case statements
-                          # for (cond, action)-pairs
-    nkElifBranch,         # used in if statements
-    nkExceptBranch,       # an except section
-    nkElse,               # an else part
-    nkAsmStmt,            # an assembler block
-    nkPragma,             # a pragma statement
-    nkPragmaBlock,        # a pragma with a block
-    nkIfStmt,             # an if statement
-    nkWhenStmt,           # a when expression or statement
-    nkForStmt,            # a for statement
-    nkParForStmt,         # a parallel for statement
-    nkWhileStmt,          # a while statement
-    nkCaseStmt,           # a case statement
-    nkTypeSection,        # a type section (consists of type definitions)
-    nkVarSection,         # a var section
-    nkLetSection,         # a let section
-    nkConstSection,       # a const section
-    nkConstDef,           # a const definition
-    nkTypeDef,            # a type definition
-    nkYieldStmt,          # the yield statement as a tree
-    nkDefer,              # the 'defer' statement
-    nkTryStmt,            # a try statement
-    nkFinally,            # a finally section
-    nkRaiseStmt,          # a raise statement
-    nkReturnStmt,         # a return statement
-    nkBreakStmt,          # a break statement
-    nkContinueStmt,       # a continue statement
-    nkBlockStmt,          # a block statement
-    nkStaticStmt,         # a static statement
-    nkDiscardStmt,        # a discard statement
-    nkStmtList,           # a list of statements
-    nkImportStmt,         # an import statement
-    nkImportExceptStmt,   # an import x except a statement
-    nkExportStmt,         # an export statement
-    nkExportExceptStmt,   # an 'export except' statement
-    nkFromStmt,           # a from * import statement
-    nkIncludeStmt,        # an include statement
-    nkBindStmt,           # a bind statement
-    nkMixinStmt,          # a mixin statement
-    nkUsingStmt,          # an using statement
-    nkCommentStmt,        # a comment statement
-    nkStmtListExpr,       # a statement list followed by an expr; this is used
-                          # to allow powerful multi-line templates
-    nkBlockExpr,          # a statement block ending in an expr; this is used
-                          # to allowe powerful multi-line templates that open a
-                          # temporary scope
-    nkStmtListType,       # a statement list ending in a type; for macros
-    nkBlockType,          # a statement block ending in a type; for macros
-                          # types as syntactic trees:
-
-    nkWith,               # distinct with `foo`
-    nkWithout,            # distinct without `foo`
-
-    nkTypeOfExpr,         # type(1+2)
-    nkObjectTy,           # object body
-    nkTupleTy,            # tuple body
-    nkTupleClassTy,       # tuple type class
-    nkTypeClassTy,        # user-defined type class
-    nkStaticTy,           # ``static[T]``
-    nkRecList,            # list of object parts
-    nkRecCase,            # case section of object
-    nkRecWhen,            # when section of object
-    nkRefTy,              # ``ref T``
-    nkPtrTy,              # ``ptr T``
-    nkVarTy,              # ``var T``
-    nkConstTy,            # ``const T``
-    nkMutableTy,          # ``mutable T``
-    nkDistinctTy,         # distinct type
-    nkProcTy,             # proc type
-    nkIteratorTy,         # iterator type
-    nkSharedTy,           # 'shared T'
-                          # we use 'nkPostFix' for the 'not nil' addition
-    nkEnumTy,             # enum body
-    nkEnumFieldDef,       # `ident = expr` in an enumeration
-    nkArgList,            # argument list
-    nkPattern,            # a special pattern; used for matching
-    nkReturnToken,        # token used for interpretation
-    nkClosure,            # (prc, env)-pair (internally used for code gen)
-    nkGotoState,          # used for the state machine (for iterators)
-    nkState,              # give a label to a code section (for iterators)
-    nkBreakState,         # special break statement for easier code generation
-    nkFuncDef,            # a func
-    nkTupleConstr         # a tuple constructor
+  TCallingConvention* = enum
+    ccNimCall = "nimcall"           # nimcall, also the default
+    ccStdCall = "stdcall"           # procedure is stdcall
+    ccCDecl = "cdecl"               # cdecl
+    ccSafeCall = "safecall"         # safecall
+    ccSysCall = "syscall"           # system call
+    ccInline = "inline"             # proc should be inlined
+    ccNoInline = "noinline"         # proc should not be inlined
+    ccFastCall = "fastcall"         # fastcall (pass parameters in registers)
+    ccThisCall = "thiscall"         # thiscall (parameters are pushed right-to-left)
+    ccClosure  = "closure"          # proc has a closure
+    ccNoConvention = "noconv"       # needed for generating proper C procs sometimes
+    ccMember = "member"             # proc is a (cpp) member
 
   TNodeKinds* = set[TNodeKind]
 
 type
-  TSymFlag* = enum    # already 33 flags!
+  TSymFlag* = enum    # 63 flags!
     sfUsed,           # read access of sym (for warnings) or simply used
     sfExported,       # symbol is exported from module
     sfFromGeneric,    # symbol is instantiation of a generic; this is needed
@@ -236,8 +50,11 @@ type
     sfGlobal,         # symbol is at global scope
 
     sfForward,        # symbol is forward declared
+    sfWasForwarded,   # symbol had a forward declaration
+                      # (implies it's too dangerous to patch its type signature)
     sfImportc,        # symbol is external; imported
     sfExportc,        # symbol is exported (under a specified name)
+    sfMangleCpp,      # mangle as cpp (combines with `sfExportc`)
     sfVolatile,       # variable is volatile
     sfRegister,       # variable should be placed in a register
     sfPure,           # object is "pure" that means it has no type-information
@@ -252,14 +69,20 @@ type
                       # *OR*: a proc is indirectly called (used as first class)
     sfCompilerProc,   # proc is a compiler proc, that is a C proc that is
                       # needed for the code generator
-    sfProcvar,        # proc can be passed to a proc var
+    sfEscapes         # param escapes
+                      # currently unimplemented
     sfDiscriminant,   # field is a discriminant in a record/object
+    sfRequiresInit,   # field must be initialized during construction
     sfDeprecated,     # symbol is deprecated
     sfExplain,        # provide more diagnostics when this symbol is used
     sfError,          # usage of symbol should trigger a compile-time error
     sfShadowed,       # a symbol that was shadowed in some inner scope
     sfThread,         # proc will run as a thread
                       # variable is a thread variable
+    sfCppNonPod,      # tells compiler to treat such types as non-pod's, so that
+                      # `thread_local` is used instead of `__thread` for
+                      # {.threadvar.} + `--threads`. Only makes sense for importcpp types.
+                      # This has a performance impact so isn't set by default.
     sfCompileTime,    # proc can be evaluated at compile time
     sfConstructor,    # proc is a C++ constructor
     sfDispatcher,     # copied method symbol is the dispatcher
@@ -270,29 +93,58 @@ type
     sfNamedParamCall, # symbol needs named parameter call syntax in target
                       # language; for interfacing with Objective C
     sfDiscardable,    # returned value may be discarded implicitly
-    sfOverriden,      # proc is overriden
+    sfOverridden,     # proc is overridden
+    sfCallsite        # A flag for template symbols to tell the
+                      # compiler it should use line information from
+                      # the calling side of the macro, not from the
+                      # implementation.
     sfGenSym          # symbol is 'gensym'ed; do not add to symbol table
+    sfNonReloadable   # symbol will be left as-is when hot code reloading is on -
+                      # meaning that it won't be renamed and/or changed in any way
+    sfGeneratedOp     # proc is a generated '='; do not inject destructors in it
+                      # variable is generated closure environment; requires early
+                      # destruction for --newruntime.
+    sfTemplateParam   # symbol is a template parameter
+    sfCursor          # variable/field is a cursor, see RFC 177 for details
+    sfInjectDestructors # whether the proc needs the 'injectdestructors' transformation
+    sfNeverRaises     # proc can never raise an exception, not even OverflowDefect
+                      # or out-of-memory
+    sfSystemRaisesDefect # proc in the system can raise defects
+    sfUsedInFinallyOrExcept  # symbol is used inside an 'except' or 'finally'
+    sfSingleUsedTemp  # For temporaries that we know will only be used once
+    sfNoalias         # 'noalias' annotation, means C's 'restrict'
+                      # for templates and macros, means cannot be called
+                      # as a lone symbol (cannot use alias syntax)
+    sfEffectsDelayed  # an 'effectsDelayed' parameter
+    sfGeneratedType   # A anonymous generic type that is generated by the compiler for
+                      # objects that do not have generic parameters in case one of the
+                      # object fields has one.
+                      #
+                      # This is disallowed but can cause the typechecking to go into
+                      # an infinite loop, this flag is used as a sentinel to stop it.
+    sfVirtual         # proc is a C++ virtual function
+    sfByCopy          # param is marked as pass bycopy
+    sfMember          # proc is a C++ member of a type
+    sfCodegenDecl     # type, proc, global or proc param is marked as codegenDecl
+    sfWasGenSym       # symbol was 'gensym'ed
+    sfForceLift       # variable has to be lifted into closure environment
+
+    sfDirty           # template is not hygienic (old styled template) module,
+                      # compiled from a dirty-buffer
+    sfCustomPragma    # symbol is custom pragma template
+    sfBase,           # a base method
+    sfGoto            # var is used for 'goto' code generation
+    sfAnon,           # symbol name that was generated by the compiler
+                      # the compiler will avoid printing such names
+                      # in user messages.
+    sfAllUntyped      # macro or template is immediately expanded in a generic context
+    sfTemplateRedefinition # symbol is a redefinition of an earlier template
 
   TSymFlags* = set[TSymFlag]
 
 const
   sfNoInit* = sfMainModule       # don't generate code to init the variable
 
-  sfImmediate* = sfDispatcher
-    # macro or template is immediately expanded
-    # without considering any possible overloads
-  sfAllUntyped* = sfVolatile # macro or template is immediately expanded \
-    # in a generic context
-
-  sfDirty* = sfPure
-    # template is not hygienic (old styled template)
-    # module, compiled from a dirty-buffer
-
-  sfAnon* = sfDiscardable
-    # symbol name that was generated by the compiler
-    # the compiler will avoid printing such names
-    # in user messages.
-
   sfNoForward* = sfRegister
     # forward declarations are not required (per module)
   sfReorder* = sfForward
@@ -300,13 +152,10 @@ const
 
   sfCompileToCpp* = sfInfixCall       # compile the module as C++ code
   sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
-  sfExperimental* = sfOverriden       # module uses the .experimental switch
-  sfGoto* = sfOverriden               # var is used for 'goto' code generation
+  sfExperimental* = sfOverridden       # module uses the .experimental switch
   sfWrittenTo* = sfBorrow             # param is assigned to
-  sfEscapes* = sfProcvar              # param escapes
-  sfBase* = sfDiscriminant
-  sfIsSelf* = sfOverriden             # param is 'self'
-  sfCustomPragma* = sfRegister        # symbol is custom pragma template
+                                      # currently unimplemented
+  sfCppMember* = { sfVirtual, sfMember, sfConstructor } # proc is a C++ member, meaning it will be attached to the type definition
 
 const
   # getting ready for the future expr/stmt merge
@@ -315,10 +164,14 @@ const
   nkEffectList* = nkArgList
   # hacks ahead: an nkEffectList is a node with 4 children:
   exceptionEffects* = 0 # exceptions at position 0
-  usesEffects* = 1      # read effects at position 1
-  writeEffects* = 2     # write effects at position 2
+  requiresEffects* = 1      # 'requires' annotation
+  ensuresEffects* = 2     # 'ensures' annotation
   tagEffects* = 3       # user defined tag ('gc', 'time' etc.)
-  effectListLen* = 4    # list of effects list
+  pragmasEffects* = 4    # not an effect, but a slot for pragmas in proc type
+  forbiddenEffects* = 5    # list of illegal effects
+  effectListLen* = 6    # list of effects list
+  nkLastBlockStmts* = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
+                        # these must be last statements in a block
 
 type
   TTypeKind* = enum  # order is important!
@@ -329,7 +182,7 @@ type
                      # (apparently something with bootstrapping)
                      # if you need to add a type, they can apparently be reused
     tyNone, tyBool, tyChar,
-    tyEmpty, tyAlias, tyNil, tyExpr, tyStmt, tyTypeDesc,
+    tyEmpty, tyAlias, tyNil, tyUntyped, tyTyped, tyTypeDesc,
     tyGenericInvocation, # ``T[a, b]`` for types to invoke
     tyGenericBody,       # ``T[a, b, body]`` last parameter is the body
     tyGenericInst,       # ``T[a, b, realInstance]`` instantiated generic type
@@ -352,14 +205,17 @@ type
     tySequence,
     tyProc,
     tyPointer, tyOpenArray,
-    tyString, tyCString, tyForward,
+    tyString, tyCstring, tyForward,
     tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers
     tyFloat, tyFloat32, tyFloat64, tyFloat128,
     tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64,
-    tyOptAsRef, tySink, tyLent,
+    tyOwned, tySink, tyLent,
     tyVarargs,
-    tyUnused,
-    tyProxy # used as errornous type (for idetools)
+    tyUncheckedArray
+      # An array with boundaries [0,+∞]
+
+    tyError # used as erroneous type (for idetools)
+      # as an erroneous node should match everything
 
     tyBuiltInTypeClass
       # Type such as the catch-all object, tuple, seq, etc
@@ -384,9 +240,9 @@ type
     tyInferred
       # In the initial state `base` stores a type class constraining
       # the types that can be inferred. After a candidate type is
-      # selected, it's stored in `lastSon`. Between `base` and `lastSon`
+      # selected, it's stored in `last`. Between `base` and `last`
       # there may be 0, 2 or more types that were also considered as
-      # possible candidates in the inference process (i.e. lastSon will
+      # possible candidates in the inference process (i.e. last will
       # be updated to store a type best conforming to all candidates)
 
     tyAnd, tyOr, tyNot
@@ -406,30 +262,33 @@ type
       # instantiation and prior to this it has the potential to
       # be any type.
 
-    tyOpt
-      # Builtin optional type
+    tyConcept
+      # new style concept.
 
     tyVoid
       # now different from tyEmpty, hurray!
+    tyIterable
 
 static:
   # remind us when TTypeKind stops to fit in a single 64-bit word
-  assert TTypeKind.high.ord <= 63
+  # assert TTypeKind.high.ord <= 63
+  discard
 
 const
   tyPureObject* = tyTuple
   GcTypeKinds* = {tyRef, tySequence, tyString}
-  tyError* = tyProxy # as an errornous node should match everything
-  tyUnknown* = tyFromExpr
-
-  tyUnknownTypes* = {tyError, tyFromExpr}
 
   tyTypeClasses* = {tyBuiltInTypeClass, tyCompositeTypeClass,
                     tyUserTypeClass, tyUserTypeClassInst,
                     tyAnd, tyOr, tyNot, tyAnything}
 
-  tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyExpr} + tyTypeClasses
+  tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyUntyped} + tyTypeClasses
   tyUserTypeClasses* = {tyUserTypeClass, tyUserTypeClassInst}
+  # consider renaming as `tyAbstractVarRange`
+  abstractVarRange* = {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal,
+                       tyTypeDesc, tyAlias, tyInferred, tySink, tyOwned}
+  abstractInst* = {tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias,
+                   tyInferred, tySink, tyOwned} # xxx what about tyStatic?
 
 type
   TTypeKinds* = set[TTypeKind]
@@ -451,18 +310,30 @@ type
     nfExplicitCall # x.y() was used instead of x.y
     nfExprCall  # this is an attempt to call a regular expression
     nfIsRef     # this node is a 'ref' node; used for the VM
+    nfIsPtr     # this node is a 'ptr' node; used for the VM
     nfPreventCg # this node should be ignored by the codegen
     nfBlockArg  # this a stmtlist appearing in a call (e.g. a do block)
     nfFromTemplate # a top-level node returned from a template
+    nfDefaultParam # an automatically inserter default parameter
+    nfDefaultRefsParam # a default param value references another parameter
+                       # the flag is applied to proc default values and to calls
+    nfExecuteOnReload  # A top-level statement that will be executed during reloads
+    nfLastRead  # this node is a last read
+    nfFirstWrite # this node is a first write
+    nfHasComment # node has a comment
+    nfSkipFieldChecking # node skips field visable checking
+    nfDisabledOpenSym # temporary: node should be nkOpenSym but cannot
+                      # because openSym experimental switch is disabled
+                      # gives warning instead
 
   TNodeFlags* = set[TNodeFlag]
-  TTypeFlag* = enum   # keep below 32 for efficiency reasons (now: beyond that)
+  TTypeFlag* = enum   # keep below 32 for efficiency reasons (now: 47)
     tfVarargs,        # procedure has C styled varargs
                       # tyArray type represeting a varargs list
     tfNoSideEffect,   # procedure type does not allow side effects
     tfFinal,          # is the object final?
     tfInheritable,    # is the object inheritable?
-    tfAcyclic,        # type is acyclic (for GC optimization)
+    tfHasOwned,       # type contains an 'owned' type and must be moved
     tfEnumHasHoles,   # enum cannot be mapped into a range
     tfShallow,        # type can be shallow copied on assignment
     tfThread,         # proc type is marked as ``thread``; alias for ``gcsafe``
@@ -486,9 +357,11 @@ type
     tfIterator,       # type is really an iterator, not a tyProc
     tfPartial,        # type is declared as 'partial'
     tfNotNil,         # type cannot be 'nil'
-
-    tfNeedsInit,      # type constains a "not nil" constraint somewhere or some
-                      # other type so that it requires initialization
+    tfRequiresInit,   # type contains a "not nil" constraint somewhere or
+                      # a `requiresInit` field, so the default zero init
+                      # is not appropriate
+    tfNeedsFullInit,  # object type marked with {.requiresInit.}
+                      # all fields must be initialized
     tfVarIsPtr,       # 'var' type is translated like 'ptr' even in C++ mode
     tfHasMeta,        # type contains "wildcard" sub-types such as generic params
                       # or other type classes
@@ -498,6 +371,7 @@ type
     tfGenericTypeParam
     tfImplicitTypeParam
     tfInferrableStatic
+    tfConceptMatchedTypeSym
     tfExplicit        # for typedescs, marks types explicitly prefixed with the
                       # `type` operator (e.g. type int)
     tfWildcard        # consider a proc like foo[T, I](x: Type[T, I])
@@ -509,9 +383,22 @@ type
     tfTriggersCompileTime # uses the NimNode type which make the proc
                           # implicitly '.compiletime'
     tfRefsAnonObj     # used for 'ref object' and 'ptr object'
-    tfCovariant       # covariant generic param mimicing a ptr type
-    tfWeakCovariant   # covariant generic param mimicing a seq/array type
+    tfCovariant       # covariant generic param mimicking a ptr type
+    tfWeakCovariant   # covariant generic param mimicking a seq/array type
     tfContravariant   # contravariant generic param
+    tfCheckedForDestructor # type was checked for having a destructor.
+                           # If it has one, t.destructor is not nil.
+    tfAcyclic # object type was annotated as .acyclic
+    tfIncompleteStruct # treat this type as if it had sizeof(pointer)
+    tfCompleteStruct
+      # (for importc types); type is fully specified, allowing to compute
+      # sizeof, alignof, offsetof at CT
+    tfExplicitCallConv
+    tfIsConstructor
+    tfEffectSystemWorkaround
+    tfIsOutParam
+    tfSendable
+    tfImplicitStatic
 
   TTypeFlags* = set[TTypeFlag]
 
@@ -547,91 +434,86 @@ type
                           # file (it is loaded on demand, which may
                           # mean: never)
     skPackage,            # symbol is a package (used for canonicalization)
-    skAlias               # an alias (needs to be resolved immediately)
   TSymKinds* = set[TSymKind]
 
 const
   routineKinds* = {skProc, skFunc, skMethod, skIterator,
                    skConverter, skMacro, skTemplate}
-  tfIncompleteStruct* = tfVarargs
-  tfUncheckedArray* = tfVarargs
+  ExportableSymKinds* = {skVar, skLet, skConst, skType, skEnumField, skStub} + routineKinds
+
   tfUnion* = tfNoSideEffect
   tfGcSafe* = tfThread
   tfObjHasKids* = tfEnumHasHoles
-  tfOldSchoolExprStmt* = tfVarargs # for now used to distinguish \
-    # 'varargs[expr]' from 'varargs[untyped]'. Eventually 'expr' will be
-    # deprecated and this mess can be cleaned up.
   tfReturnsNew* = tfInheritable
+  tfNonConstExpr* = tfExplicitCallConv
+    ## tyFromExpr where the expression shouldn't be evaluated as a static value
   skError* = skUnknown
 
-  # type flags that are essential for type equality:
-  eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr}
+var
+  eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect, tfIsOutParam}
+    ## type flags that are essential for type equality.
+    ## This is now a variable because for emulation of version:1.0 we
+    ## might exclude {tfGcSafe, tfNoSideEffect}.
 
 type
   TMagic* = enum # symbols that require compiler magic:
     mNone,
-    mDefined, mDefinedInScope, mCompiles, mArrGet, mArrPut, mAsgn,
-    mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mPlugin,
-    mEcho, mShallowCopy, mSlurp, mStaticExec,
+    mDefined, mDeclared, mDeclaredInScope, mCompiles, mArrGet, mArrPut, mAsgn,
+    mLow, mHigh, mSizeOf, mAlignOf, mOffsetOf, mTypeTrait,
+    mIs, mOf, mAddr, mType, mTypeOf,
+    mPlugin, mEcho, mShallowCopy, mSlurp, mStaticExec, mStatic,
     mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
-    mUnaryLt, mInc, mDec, mOrd,
+    mInc, mDec, mOrd,
     mNew, mNewFinalize, mNewSeq, mNewSeqOfCap,
     mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq,
-    mXLenStr, mXLenSeq,
     mIncl, mExcl, mCard, mChr,
     mGCref, mGCunref,
     mAddI, mSubI, mMulI, mDivI, mModI,
     mSucc, mPred,
     mAddF64, mSubF64, mMulF64, mDivF64,
-    mShrI, mShlI, mBitandI, mBitorI, mBitxorI,
+    mShrI, mShlI, mAshrI, mBitandI, mBitorI, mBitxorI,
     mMinI, mMaxI,
-    mMinF64, mMaxF64,
     mAddU, mSubU, mMulU, mDivU, mModU,
     mEqI, mLeI, mLtI,
     mEqF64, mLeF64, mLtF64,
     mLeU, mLtU,
-    mLeU64, mLtU64,
     mEqEnum, mLeEnum, mLtEnum,
     mEqCh, mLeCh, mLtCh,
     mEqB, mLeB, mLtB,
-    mEqRef, mEqUntracedRef, mLePtr, mLtPtr,
+    mEqRef, mLePtr, mLtPtr,
     mXor, mEqCString, mEqProc,
     mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot,
     mUnaryPlusI, mBitnotI,
-    mUnaryPlusF64, mUnaryMinusF64, mAbsF64,
-    mZe8ToI, mZe8ToI64,
-    mZe16ToI, mZe16ToI64,
-    mZe32ToI64, mZeIToI64,
-    mToU8, mToU16, mToU32,
-    mToFloat, mToBiggestFloat,
-    mToInt, mToBiggestInt,
-    mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr,
+    mUnaryPlusF64, mUnaryMinusF64,
+    mCharToStr, mBoolToStr,
+    mCStrToStr,
     mStrToStr, mEnumToStr,
     mAnd, mOr,
+    mImplies, mIff, mExists, mForall, mOld,
     mEqStr, mLeStr, mLtStr,
-    mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mSymDiffSet,
+    mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
     mConStrStr, mSlice,
     mDotDot, # this one is only necessary to give nice compile time warnings
     mFields, mFieldPairs, mOmpParFor,
     mAppendStrCh, mAppendStrStr, mAppendSeqElem,
-    mInRange, mInSet, mRepr, mExit,
+    mInSet, mRepr, mExit,
     mSetLengthStr, mSetLengthSeq,
     mIsPartOf, mAstToStr, mParallel,
-    mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
+    mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq,
     mNewString, mNewStringOfCap, mParseBiggestFloat,
-    mReset,
-    mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs,
+    mMove, mEnsureMove, mWasMoved, mDup, mDestroy, mTrace,
+    mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField,
+    mArray, mOpenArray, mRange, mSet, mSeq, mVarargs,
     mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
-    mOrdinal,
+    mOrdinal, mIterableType,
     mInt, mInt8, mInt16, mInt32, mInt64,
     mUInt, mUInt8, mUInt16, mUInt32, mUInt64,
     mFloat, mFloat32, mFloat64, mFloat128,
     mBool, mChar, mString, mCstring,
-    mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, mStmt, mTypeDesc,
-    mVoidType, mPNimrodNode, mShared, mGuarded, mLock, mSpawn, mDeepCopy,
+    mPointer, mNil, mExpr, mStmt, mTypeDesc,
+    mVoidType, mPNimrodNode, mSpawn, mDeepCopy,
     mIsMainModule, mCompileDate, mCompileTime, mProcCall,
     mCpuEndian, mHostOS, mHostCPU, mBuildOS, mBuildCPU, mAppType,
-    mNaN, mInf, mNegInf,
     mCompileOption, mCompileOptionArg,
     mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel,
     mNKind, mNSymKind,
@@ -640,57 +522,66 @@ type
     mNctPut, mNctLen, mNctGet, mNctHasNext, mNctNext,
 
     mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal,
-    mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo,
-    mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent,
-    mNBindSym, mLocals, mNCallSite,
+    mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetStrVal, mNLineInfo,
+    mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mNSigHash, mNSizeOf,
+    mNBindSym, mNCallSite,
     mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNGenSym,
     mNHint, mNWarning, mNError,
-    mInstantiationInfo, mGetTypeInfo,
-    mNimvm, mIntDefine, mStrDefine, mRunnableExamples,
-    mException, mBuiltinType
+    mInstantiationInfo, mGetTypeInfo, mGetTypeInfoV2,
+    mNimvm, mIntDefine, mStrDefine, mBoolDefine, mGenericDefine, mRunnableExamples,
+    mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf,
+    mSymIsInstantiationOf, mNodeId, mPrivateAccess, mZeroDefault
+
 
-# things that we can evaluate safely at compile time, even if not asked for it:
 const
-  ctfeWhitelist* = {mNone, mUnaryLt, mSucc,
+  # things that we can evaluate safely at compile time, even if not asked for it:
+  ctfeWhitelist* = {mNone, mSucc,
     mPred, mInc, mDec, mOrd, mLengthOpenArray,
-    mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq,
-    mArrGet, mArrPut, mAsgn,
+    mLengthStr, mLengthArray, mLengthSeq,
+    mArrGet, mArrPut, mAsgn, mDestroy,
     mIncl, mExcl, mCard, mChr,
     mAddI, mSubI, mMulI, mDivI, mModI,
     mAddF64, mSubF64, mMulF64, mDivF64,
     mShrI, mShlI, mBitandI, mBitorI, mBitxorI,
     mMinI, mMaxI,
-    mMinF64, mMaxF64,
     mAddU, mSubU, mMulU, mDivU, mModU,
     mEqI, mLeI, mLtI,
     mEqF64, mLeF64, mLtF64,
     mLeU, mLtU,
-    mLeU64, mLtU64,
     mEqEnum, mLeEnum, mLtEnum,
     mEqCh, mLeCh, mLtCh,
     mEqB, mLeB, mLtB,
-    mEqRef, mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor,
+    mEqRef, mEqProc, mLePtr, mLtPtr, mEqCString, mXor,
     mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI,
-    mUnaryPlusF64, mUnaryMinusF64, mAbsF64,
-    mZe8ToI, mZe8ToI64,
-    mZe16ToI, mZe16ToI64,
-    mZe32ToI64, mZeIToI64,
-    mToU8, mToU16, mToU32,
-    mToFloat, mToBiggestFloat,
-    mToInt, mToBiggestInt,
-    mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr,
+    mUnaryPlusF64, mUnaryMinusF64,
+    mCharToStr, mBoolToStr,
+    mCStrToStr,
     mStrToStr, mEnumToStr,
     mAnd, mOr,
     mEqStr, mLeStr, mLtStr,
-    mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mSymDiffSet,
+    mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
     mConStrStr, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
-    mInRange, mInSet, mRepr,
-    mCopyStr, mCopyStrLast}
-  # magics that require special semantic checking and
-  # thus cannot be overloaded (also documented in the spec!):
-  SpecialSemMagics* = {
-    mDefined, mDefinedInScope, mCompiles, mLow, mHigh, mSizeOf, mIs, mOf,
-    mShallowCopy, mExpandToAst, mParallel, mSpawn, mAstToStr}
+    mInSet, mRepr, mOpenArrayToSeq}
+
+  generatedMagics* = {mNone, mIsolate, mFinished, mOpenArrayToSeq}
+    ## magics that are generated as normal procs in the backend
+
+type
+  ItemId* = object
+    module*: int32
+    item*: int32
+
+proc `$`*(x: ItemId): string =
+  "(module: " & $x.module & ", item: " & $x.item & ")"
+
+proc `==`*(a, b: ItemId): bool {.inline.} =
+  a.item == b.item and a.module == b.module
+
+proc hash*(x: ItemId): Hash =
+  var h: Hash = hash(x.module)
+  h = h !& hash(x.item)
+  result = !$h
+
 
 type
   PNode* = ref TNode
@@ -716,12 +607,12 @@ type
       ident*: PIdent
     else:
       sons*: TNodeSeq
-    comment*: string
+    when defined(nimsuggest):
+      endInfo*: TLineInfo
 
-  TSymSeq* = seq[PSym]
   TStrTable* = object         # a table[PIdent] of PSym
     counter*: int
-    data*: TSymSeq
+    data*: seq[PSym]
 
   # -------------- backend information -------------------------------
   TLocKind* = enum
@@ -738,9 +629,6 @@ type
     locOther                  # location is something other
   TLocFlag* = enum
     lfIndirect,               # backend introduced a pointer
-    lfFullExternalName, # only used when 'conf.cmd == cmdPretty': Indicates
-      # that the symbol has been imported via 'importc: "fullname"' and
-      # no format string.
     lfNoDeepCopy,             # no need for a deep copy
     lfNoDecl,                 # do not declare it in C
     lfDynamicLib,             # link symbol to dynamic library
@@ -748,12 +636,14 @@ type
     lfHeader,                 # include header file for symbol
     lfImportCompilerProc,     # ``importc`` of a compilerproc
     lfSingleUse               # no location yet and will only be used once
+    lfEnforceDeref            # a copyMem is required to dereference if this a
+                              # ptr array due to C array limitations.
+                              # See #1181, #6422, #11171
+    lfPrepareForMutation      # string location is about to be mutated (V2)
   TStorageLoc* = enum
     OnUnknown,                # location is unknown (stack, heap or static)
     OnStatic,                 # in a static section
     OnStack,                  # location is on hardware stack
-    OnStackShadowDup,         # location is on the stack but also replicated
-                              # on the shadow stack
     OnHeap                    # location is on heap or global
                               # (reference counting needed)
   TLocFlags* = set[TLocFlag]
@@ -762,8 +652,7 @@ type
     storage*: TStorageLoc
     flags*: TLocFlags         # location's flags
     lode*: PNode              # Node where the location came from; can be faked
-    r*: Rope                  # rope value of location (code generators)
-    dup*: Rope                # duplicated location for precise stack scans
+    snippet*: Rope            # C code snippet of location (code generators)
 
   # ---------------- end of backend information ------------------------------
 
@@ -771,9 +660,10 @@ type
     libHeader, libDynamic
 
   TLib* = object              # also misused for headers!
+                              # keep in sync with PackedLib
     kind*: TLibKind
     generated*: bool          # needed for the backends:
-    isOverriden*: bool
+    isOverridden*: bool
     name*: Rope
     path*: PNode              # can be a string literal!
 
@@ -787,43 +677,35 @@ type
 
   PInstantiation* = ref TInstantiation
 
-  TScope* = object
+  TScope* {.acyclic.} = object
     depthLevel*: int
     symbols*: TStrTable
     parent*: PScope
+    allowPrivateAccess*: seq[PSym] #  # enable access to private fields
 
   PScope* = ref TScope
 
   PLib* = ref TLib
-  TSym* {.acyclic.} = object of TIdObj
+  TSym* {.acyclic.} = object # Keep in sync with PackedSym
+    itemId*: ItemId
     # proc and type instantiations are cached in the generic symbol
     case kind*: TSymKind
-    of skType, skGenericParam:
-      typeInstCache*: seq[PType]
     of routineKinds:
-      procInstCache*: seq[PInstantiation]
-      gcUnsafetyReason*: PSym  # for better error messages wrt gcsafe
-      #scope*: PScope          # the scope where the proc was defined
-    of skModule, skPackage:
-      # modules keep track of the generic symbols they use from other modules.
-      # this is because in incremental compilation, when a module is about to
-      # be replaced with a newer version, we must decrement the usage count
-      # of all previously used generics.
-      # For 'import as' we copy the module symbol but shallowCopy the 'tab'
-      # and set the 'usedGenerics' to ... XXX gah! Better set module.name
-      # instead? But this doesn't work either. --> We need an skModuleAlias?
-      # No need, just leave it as skModule but set the owner accordingly and
-      # check for the owner when touching 'usedGenerics'.
-      usedGenerics*: seq[PInstantiation]
-      tab*: TStrTable         # interface table for modules
+      #procInstCache*: seq[PInstantiation]
+      gcUnsafetyReason*: PSym  # for better error messages regarding gcsafe
+      transformedBody*: PNode  # cached body after transf pass
     of skLet, skVar, skField, skForVar:
       guard*: PSym
       bitsize*: int
+      alignment*: int # for alignment
     else: nil
     magic*: TMagic
     typ*: PType
     name*: PIdent
     info*: TLineInfo
+    when defined(nimsuggest):
+      endInfo*: TLineInfo
+      hasUserSpecifiedType*: bool  # used for determining whether to display inlay type hints
     owner*: PSym
     flags*: TSymFlags
     ast*: PNode               # syntax tree of proc, iterator, etc.:
@@ -838,41 +720,57 @@ type
     position*: int            # used for many different things:
                               # for enum fields its position;
                               # for fields its offset
-                              # for parameters its position
+                              # for parameters its position (starting with 0)
                               # for a conditional:
                               # 1 iff the symbol is defined, else 0
                               # (or not in symbol table)
                               # for modules, an unique index corresponding
                               # to the module's fileIdx
                               # for variables a slot index for the evaluator
-                              # for routines a superop-ID
-    offset*: int              # offset of record field
+    offset*: int32            # offset of record field
+    disamb*: int32            # disambiguation number; the basic idea is that
+                              # `<procname>__<module>_<disamb>` is unique
     loc*: TLoc
     annex*: PLib              # additional fields (seldom used, so we use a
-                              # reference to another object to safe space)
+                              # reference to another object to save space)
+    when hasFFI:
+      cname*: string          # resolved C declaration name in importc decl, e.g.:
+                              # proc fun() {.importc: "$1aux".} => cname = funaux
     constraint*: PNode        # additional constraints like 'lit|result'; also
-                              # misused for the codegenDecl pragma in the hope
+                              # misused for the codegenDecl and virtual pragmas in the hope
                               # it won't cause problems
                               # for skModule the string literal to output for
                               # deprecated modules.
+    instantiatedFrom*: PSym   # for instances, the generic symbol where it came from.
     when defined(nimsuggest):
       allUsages*: seq[TLineInfo]
 
   TTypeSeq* = seq[PType]
-  TLockLevel* = distinct int16
-  TType* {.acyclic.} = object of TIdObj # \
+
+  TTypeAttachedOp* = enum ## as usual, order is important here
+    attachedWasMoved,
+    attachedDestructor,
+    attachedAsgn,
+    attachedDup,
+    attachedSink,
+    attachedTrace,
+    attachedDeepCopy
+
+  TType* {.acyclic.} = object # \
                               # types are identical iff they have the
                               # same id; there may be multiple copies of a type
                               # in memory!
+                              # Keep in sync with PackedType
+    itemId*: ItemId
     kind*: TTypeKind          # kind of type
     callConv*: TCallingConvention # for procs
     flags*: TTypeFlags        # flags of the type
-    sons*: TTypeSeq           # base types, etc.
+    sons: TTypeSeq           # base types, etc.
     n*: PNode                 # node for types:
                               # for range types a nkRange node
                               # for record types a nkRecord node
                               # for enum types a list of symbols
-                              # for tyInt it can be the int literal
+                              # if kind == tyInt: it is an 'int literal(x)' type
                               # for procs and tyGenericBody, it's the
                               # formal param list
                               # for concepts, the concept body
@@ -880,44 +778,21 @@ type
     owner*: PSym              # the 'owner' of the type
     sym*: PSym                # types have the sym associated with them
                               # it is used for converting types to strings
-    destructor*: PSym         # destructor. warning: nil here may not necessary
-                              # mean that there is no destructor.
-                              # see instantiateDestructor in semdestruct.nim
-    deepCopy*: PSym           # overriden 'deepCopy' operation
-    assignment*: PSym         # overriden '=' operation
-    sink*: PSym               # overriden '=sink' operation
-    methods*: seq[(int,PSym)] # attached methods
     size*: BiggestInt         # the size of the type in bytes
                               # -1 means that the size is unkwown
     align*: int16             # the type's alignment requirements
-    lockLevel*: TLockLevel    # lock level as required for deadlock checking
+    paddingAtEnd*: int16      #
     loc*: TLoc
     typeInst*: PType          # for generic instantiations the tyGenericInst that led to this
                               # type.
+    uniqueId*: ItemId         # due to a design mistake, we need to keep the real ID here as it
+                              # is required by the --incremental:on mode.
 
   TPair* = object
     key*, val*: RootRef
 
   TPairSeq* = seq[TPair]
 
-  TIdPair* = object
-    key*: PIdObj
-    val*: RootRef
-
-  TIdPairSeq* = seq[TIdPair]
-  TIdTable* = object # the same as table[PIdent] of PObject
-    counter*: int
-    data*: TIdPairSeq
-
-  TIdNodePair* = object
-    key*: PIdObj
-    val*: PNode
-
-  TIdNodePairSeq* = seq[TIdNodePair]
-  TIdNodeTable* = object # the same as table[PIdObj] of PNode
-    counter*: int
-    data*: TIdNodePairSeq
-
   TNodePair* = object
     h*: Hash                 # because it is expensive to compute!
     key*: PNode
@@ -937,13 +812,47 @@ type
   TImplication* = enum
     impUnknown, impNo, impYes
 
+template nodeId(n: PNode): int = cast[int](n)
+
+type Gconfig = object
+  # we put comments in a side channel to avoid increasing `sizeof(TNode)`, which
+  # reduces memory usage given that `PNode` is the most allocated type by far.
+  comments: Table[int, string] # nodeId => comment
+  useIc*: bool
+
+var gconfig {.threadvar.}: Gconfig
+
+proc setUseIc*(useIc: bool) = gconfig.useIc = useIc
+
+proc comment*(n: PNode): string =
+  if nfHasComment in n.flags and not gconfig.useIc:
+    # IC doesn't track comments, see `packed_ast`, so this could fail
+    result = gconfig.comments[n.nodeId]
+  else:
+    result = ""
+
+proc `comment=`*(n: PNode, a: string) =
+  let id = n.nodeId
+  if a.len > 0:
+    # if needed, we could periodically cleanup gconfig.comments when its size increases,
+    # to ensure only live nodes (and with nfHasComment) have an entry in gconfig.comments;
+    # for compiling compiler, the waste is very small:
+    # num calls to newNodeImpl: 14984160 (num of PNode allocations)
+    # size of gconfig.comments: 33585
+    # num of nodes with comments that were deleted and hence wasted: 3081
+    n.flags.incl nfHasComment
+    gconfig.comments[id] = a
+  elif nfHasComment in n.flags:
+    n.flags.excl nfHasComment
+    gconfig.comments.del(id)
+
 # BUGFIX: a module is overloadable so that a proc can have the
 # same name as an imported module. This is necessary because of
 # the poor naming choices in the standard library.
 
 const
   OverloadableSyms* = {skProc, skFunc, skMethod, skIterator,
-    skConverter, skModule, skTemplate, skMacro}
+    skConverter, skModule, skTemplate, skMacro, skEnumField}
 
   GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody,
     tyGenericParam}
@@ -957,21 +866,22 @@ const
     tyBool, tyChar, tyEnum, tyArray, tyObject,
     tySet, tyTuple, tyRange, tyPtr, tyRef, tyVar, tyLent, tySequence, tyProc,
     tyPointer,
-    tyOpenArray, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
+    tyOpenArray, tyString, tyCstring, tyInt..tyInt64, tyFloat..tyFloat128,
     tyUInt..tyUInt64}
   IntegralTypes* = {tyBool, tyChar, tyEnum, tyInt..tyInt64,
-    tyFloat..tyFloat128, tyUInt..tyUInt64}
+    tyFloat..tyFloat128, tyUInt..tyUInt64} # weird name because it contains tyFloat
   ConstantDataTypes*: TTypeKinds = {tyArray, tySet,
                                     tyTuple, tySequence}
-  NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence,
-    tyProc, tyString, tyError}
-  ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType,
-    skIterator,
-    skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
+  NilableTypes*: TTypeKinds = {tyPointer, tyCstring, tyRef, tyPtr,
+    tyProc, tyError} # TODO
+  PtrLikeKinds*: TTypeKinds = {tyPointer, tyPtr} # for VM
   PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
                                       nfDotSetter, nfDotField,
-                                      nfIsRef, nfPreventCg, nfLL,
-                                      nfFromTemplate}
+                                      nfIsRef, nfIsPtr, nfPreventCg, nfLL,
+                                      nfFromTemplate, nfDefaultRefsParam,
+                                      nfExecuteOnReload, nfLastRead,
+                                      nfFirstWrite, nfSkipFieldChecking,
+                                      nfDisabledOpenSym}
   namePos* = 0
   patternPos* = 1    # empty except for term rewriting macros
   genericParamsPos* = 2
@@ -980,19 +890,21 @@ const
   miscPos* = 5  # used for undocumented and hacky stuff
   bodyPos* = 6       # position of body; use rodread.getBody() instead!
   resultPos* = 7
-  dispatcherPos* = 8 # caution: if method has no 'result' it can be position 7!
+  dispatcherPos* = 8
+
+  nfAllFieldsSet* = nfBase2
 
-  nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
-                  nkCommand, nkCallStrLit, nkHiddenCallConv}
   nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice,
-                   nkClosedSymChoice}
+                   nkClosedSymChoice, nkOpenSym}
 
   nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit}
   nkLiterals* = {nkCharLit..nkTripleStrLit}
   nkFloatLiterals* = {nkFloatLit..nkFloat128Lit}
   nkLambdaKinds* = {nkLambda, nkDo}
   declarativeDefs* = {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef}
+  routineDefs* = declarativeDefs + {nkMacroDef, nkTemplateDef}
   procDefs* = nkLambdaKinds + declarativeDefs
+  callableDefs* = nkLambdaKinds + routineDefs
 
   nkSymChoices* = {nkClosedSymChoice, nkOpenSymChoice}
   nkStrKinds* = {nkStrLit..nkTripleStrLit}
@@ -1001,9 +913,68 @@ const
   skProcKinds* = {skProc, skFunc, skTemplate, skMacro, skIterator,
                   skMethod, skConverter}
 
+  defaultSize = -1
+  defaultAlignment = -1
+  defaultOffset* = -1
+
+proc getPIdent*(a: PNode): PIdent {.inline.} =
+  ## Returns underlying `PIdent` for `{nkSym, nkIdent}`, or `nil`.
+  case a.kind
+  of nkSym: a.sym.name
+  of nkIdent: a.ident
+  of nkOpenSymChoice, nkClosedSymChoice: a.sons[0].sym.name
+  of nkOpenSym: getPIdent(a.sons[0])
+  else: nil
+
+const
+  moduleShift = when defined(cpu32): 20 else: 24
+
+template id*(a: PType | PSym): int =
+  let x = a
+  (x.itemId.module.int shl moduleShift) + x.itemId.item.int
+
+type
+  IdGenerator* = ref object # unfortunately, we really need the 'shared mutable' aspect here.
+    module*: int32
+    symId*: int32
+    typeId*: int32
+    sealed*: bool
+    disambTable*: CountTable[PIdent]
+
+const
+  PackageModuleId* = -3'i32
+
+proc idGeneratorFromModule*(m: PSym): IdGenerator =
+  assert m.kind == skModule
+  result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0, disambTable: initCountTable[PIdent]())
+
+proc idGeneratorForPackage*(nextIdWillBe: int32): IdGenerator =
+  result = IdGenerator(module: PackageModuleId, symId: nextIdWillBe - 1'i32, typeId: 0, disambTable: initCountTable[PIdent]())
+
+proc nextSymId(x: IdGenerator): ItemId {.inline.} =
+  assert(not x.sealed)
+  inc x.symId
+  result = ItemId(module: x.module, item: x.symId)
+
+proc nextTypeId*(x: IdGenerator): ItemId {.inline.} =
+  assert(not x.sealed)
+  inc x.typeId
+  result = ItemId(module: x.module, item: x.typeId)
+
+when false:
+  proc nextId*(x: IdGenerator): ItemId {.inline.} =
+    inc x.item
+    result = x[]
+
+when false:
+  proc storeBack*(dest: var IdGenerator; src: IdGenerator) {.inline.} =
+    assert dest.ItemId.module == src.ItemId.module
+    if dest.ItemId.item > src.ItemId.item:
+      echo dest.ItemId.item, " ", src.ItemId.item, " ", src.ItemId.module
+    assert dest.ItemId.item <= src.ItemId.item
+    dest = src
+
 var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things
-#var
-#  gMainPackageId*: int
 
 proc isCallExpr*(n: PNode): bool =
   result = n.kind in nkCallKinds
@@ -1011,44 +982,128 @@ proc isCallExpr*(n: PNode): bool =
 proc discardSons*(father: PNode)
 
 proc len*(n: PNode): int {.inline.} =
-  if isNil(n.sons): result = 0
-  else: result = len(n.sons)
+  result = n.sons.len
 
 proc safeLen*(n: PNode): int {.inline.} =
   ## works even for leaves.
-  if n.kind in {nkNone..nkNilLit} or isNil(n.sons): result = 0
-  else: result = len(n.sons)
+  if n.kind in {nkNone..nkNilLit}: result = 0
+  else: result = n.len
 
 proc safeArrLen*(n: PNode): int {.inline.} =
   ## works for array-like objects (strings passed as openArray in VM).
-  if n.kind in {nkStrLit..nkTripleStrLit}:result = len(n.strVal)
+  if n.kind in {nkStrLit..nkTripleStrLit}: result = n.strVal.len
   elif n.kind in {nkNone..nkFloat128Lit}: result = 0
-  else: result = len(n)
+  else: result = n.len
 
 proc add*(father, son: PNode) =
   assert son != nil
-  if isNil(father.sons): father.sons = @[]
-  add(father.sons, son)
+  father.sons.add(son)
+
+proc addAllowNil*(father, son: PNode) {.inline.} =
+  father.sons.add(son)
+
+template `[]`*(n: PNode, i: int): PNode = n.sons[i]
+template `[]=`*(n: PNode, i: int; x: PNode) = n.sons[i] = x
+
+template `[]`*(n: PNode, i: BackwardsIndex): PNode = n[n.len - i.int]
+template `[]=`*(n: PNode, i: BackwardsIndex; x: PNode) = n[n.len - i.int] = x
+
+proc add*(father, son: PType) =
+  assert son != nil
+  father.sons.add(son)
 
-type Indexable = PNode | PType
+proc addAllowNil*(father, son: PType) {.inline.} =
+  father.sons.add(son)
 
-template `[]`*(n: Indexable, i: int): Indexable = n.sons[i]
-template `[]=`*(n: Indexable, i: int; x: Indexable) = n.sons[i] = x
+template `[]`*(n: PType, i: int): PType = n.sons[i]
+template `[]=`*(n: PType, i: int; x: PType) = n.sons[i] = x
 
-template `[]`*(n: Indexable, i: BackwardsIndex): Indexable = n[n.len - i.int]
-template `[]=`*(n: Indexable, i: BackwardsIndex; x: Indexable) = n[n.len - i.int] = x
+template `[]`*(n: PType, i: BackwardsIndex): PType = n[n.len - i.int]
+template `[]=`*(n: PType, i: BackwardsIndex; x: PType) = n[n.len - i.int] = x
+
+proc getDeclPragma*(n: PNode): PNode =
+  ## return the `nkPragma` node for declaration `n`, or `nil` if no pragma was found.
+  ## Currently only supports routineDefs + {nkTypeDef}.
+  case n.kind
+  of routineDefs:
+    if n[pragmasPos].kind != nkEmpty: result = n[pragmasPos]
+    else: result = nil
+  of nkTypeDef:
+    #[
+    type F3*{.deprecated: "x3".} = int
+
+    TypeSection
+      TypeDef
+        PragmaExpr
+          Postfix
+            Ident "*"
+            Ident "F3"
+          Pragma
+            ExprColonExpr
+              Ident "deprecated"
+              StrLit "x3"
+        Empty
+        Ident "int"
+    ]#
+    if n[0].kind == nkPragmaExpr:
+      result = n[0][1]
+    else:
+      result = nil
+  else:
+    # support as needed for `nkIdentDefs` etc.
+    result = nil
+  if result != nil:
+    assert result.kind == nkPragma, $(result.kind, n.kind)
+
+proc extractPragma*(s: PSym): PNode =
+  ## gets the pragma node of routine/type/var/let/const symbol `s`
+  if s.kind in routineKinds: # bug #24167
+    if s.ast[pragmasPos] != nil and s.ast[pragmasPos].kind != nkEmpty:
+      result = s.ast[pragmasPos]
+    else:
+      result = nil
+  elif s.kind in {skType, skVar, skLet, skConst}:
+    if s.ast != nil and s.ast.len > 0:
+      if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1:
+        # s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma]
+        result = s.ast[0][1]
+      else:
+        result = nil
+    else:
+      result = nil
+  else:
+    result = nil
+  assert result == nil or result.kind == nkPragma
+
+proc skipPragmaExpr*(n: PNode): PNode =
+  ## if pragma expr, give the node the pragmas are applied to,
+  ## otherwise give node itself
+  if n.kind == nkPragmaExpr:
+    result = n[0]
+  else:
+    result = n
+
+proc setInfoRecursive*(n: PNode, info: TLineInfo) =
+  ## set line info recursively
+  if n != nil:
+    for i in 0..<n.safeLen: setInfoRecursive(n[i], info)
+    n.info = info
 
 when defined(useNodeIds):
-  const nodeIdToDebug* = -1 # 299750 # 300761 #300863 # 300879
+  const nodeIdToDebug* = -1 # 2322968
   var gNodeId: int
 
-proc newNode*(kind: TNodeKind): PNode =
-  new(result)
-  result.kind = kind
-  #result.info = UnknownLineInfo() inlined:
-  result.info.fileIndex = InvalidFileIdx
-  result.info.col = int16(-1)
-  result.info.line = uint16(0)
+template newNodeImpl(info2) =
+  result = PNode(kind: kind, info: info2)
+  when false:
+    # this would add overhead, so we skip it; it results in a small amount of leaked entries
+    # for old PNode that gets re-allocated at the same address as a PNode that
+    # has `nfHasComment` set (and an entry in that table). Only `nfHasComment`
+    # should be used to test whether a PNode has a comment; gconfig.comments
+    # can contain extra entries for deleted PNode's with comments.
+    gconfig.comments.del(cast[int](result))
+
+template setIdMaybe() =
   when defined(useNodeIds):
     result.id = gNodeId
     if result.id == nodeIdToDebug:
@@ -1056,32 +1111,107 @@ proc newNode*(kind: TNodeKind): PNode =
       writeStackTrace()
     inc gNodeId
 
+proc newNode*(kind: TNodeKind): PNode =
+  ## new node with unknown line info, no type, and no children
+  newNodeImpl(unknownLineInfo)
+  setIdMaybe()
+
+proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode =
+  ## new node with line info, no type, and no children
+  newNodeImpl(info)
+  setIdMaybe()
+
+proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode =
+  ## new node with line info, type, and children
+  newNodeImpl(info)
+  if children > 0:
+    newSeq(result.sons, children)
+  setIdMaybe()
+
+proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
+  ## new node with line info, type, and no children
+  result = newNode(kind)
+  result.info = info
+  result.typ = typ
+
+proc newNode*(kind: TNodeKind, info: TLineInfo): PNode =
+  ## new node with line info, no type, and no children
+  newNodeImpl(info)
+  setIdMaybe()
+
+proc newAtom*(ident: PIdent, info: TLineInfo): PNode =
+  result = newNode(nkIdent, info)
+  result.ident = ident
+
+proc newAtom*(kind: TNodeKind, intVal: BiggestInt, info: TLineInfo): PNode =
+  result = newNode(kind, info)
+  result.intVal = intVal
+
+proc newAtom*(kind: TNodeKind, floatVal: BiggestFloat, info: TLineInfo): PNode =
+  result = newNode(kind, info)
+  result.floatVal = floatVal
+
+proc newAtom*(kind: TNodeKind; strVal: sink string; info: TLineInfo): PNode =
+  result = newNode(kind, info)
+  result.strVal = strVal
+
+proc newTree*(kind: TNodeKind; info: TLineInfo; children: varargs[PNode]): PNode =
+  result = newNodeI(kind, info)
+  if children.len > 0:
+    result.info = children[0].info
+  result.sons = @children
+
 proc newTree*(kind: TNodeKind; children: varargs[PNode]): PNode =
   result = newNode(kind)
   if children.len > 0:
     result.info = children[0].info
   result.sons = @children
 
+proc newTreeI*(kind: TNodeKind; info: TLineInfo; children: varargs[PNode]): PNode =
+  result = newNodeI(kind, info)
+  if children.len > 0:
+    result.info = children[0].info
+  result.sons = @children
+
+proc newTreeIT*(kind: TNodeKind; info: TLineInfo; typ: PType; children: varargs[PNode]): PNode =
+  result = newNodeIT(kind, info, typ)
+  if children.len > 0:
+    result.info = children[0].info
+  result.sons = @children
+
 template previouslyInferred*(t: PType): PType =
-  if t.sons.len > 1: t.lastSon else: nil
+  if t.sons.len > 1: t.last else: nil
 
-proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
+when false:
+  import tables, strutils
+  var x: CountTable[string]
+
+  addQuitProc proc () {.noconv.} =
+    for k, v in pairs(x):
+      echo k
+      echo v
+
+proc newSym*(symKind: TSymKind, name: PIdent, idgen: IdGenerator; owner: PSym,
              info: TLineInfo; options: TOptions = {}): PSym =
   # generates a symbol and initializes the hash field too
-  new(result)
-  result.name = name
-  result.kind = symKind
-  result.flags = {}
-  result.info = info
-  result.options = options
-  result.owner = owner
-  result.offset = -1
-  result.id = getID()
-  when debugIds:
-    registerId(result)
-  #if result.id == 93289:
-  #  writeStacktrace()
-  #  MessageOut(name.s & " has id: " & toString(result.id))
+  assert not name.isNil
+  let id = nextSymId idgen
+  result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id,
+                options: options, owner: owner, offset: defaultOffset,
+                disamb: getOrDefault(idgen.disambTable, name).int32)
+  idgen.disambTable.inc name
+  when false:
+    if id.module == 48 and id.item == 39:
+      writeStackTrace()
+      echo "kind ", symKind, " ", name.s
+      if owner != nil: echo owner.name.s
+
+proc astdef*(s: PSym): PNode =
+  # get only the definition (initializer) portion of the ast
+  if s.ast != nil and s.ast.kind in {nkIdentDefs, nkConstDef}:
+    s.ast[2]
+  else:
+    s.ast
 
 proc isMetaType*(t: PType): bool =
   return t.kind in tyMetaTypes or
@@ -1125,24 +1255,16 @@ const                         # for all kind of hash tables:
 
 proc copyStrTable*(dest: var TStrTable, src: TStrTable) =
   dest.counter = src.counter
-  if isNil(src.data): return
-  setLen(dest.data, len(src.data))
-  for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
-
-proc copyIdTable*(dest: var TIdTable, src: TIdTable) =
-  dest.counter = src.counter
-  if isNil(src.data): return
-  newSeq(dest.data, len(src.data))
-  for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
+  setLen(dest.data, src.data.len)
+  for i in 0..high(src.data): dest.data[i] = src.data[i]
 
 proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) =
   dest.counter = src.counter
-  if isNil(src.data): return
-  setLen(dest.data, len(src.data))
-  for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
+  setLen(dest.data, src.data.len)
+  for i in 0..high(src.data): dest.data[i] = src.data[i]
 
 proc discardSons*(father: PNode) =
-  father.sons = nil
+  father.sons = @[]
 
 proc withInfo*(n: PNode, info: TLineInfo): PNode =
   n.info = info
@@ -1165,58 +1287,89 @@ proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
   result.typ = sym.typ
   result.info = info
 
-proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode =
-  new(result)
-  result.kind = kind
-  result.info = info
-  when defined(useNodeIds):
-    result.id = gNodeId
-    if result.id == nodeIdToDebug:
-      echo "KIND ", result.kind
-      writeStackTrace()
-    inc gNodeId
-
-proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode =
-  new(result)
-  result.kind = kind
-  result.info = info
-  if children > 0:
-    newSeq(result.sons, children)
-  when defined(useNodeIds):
-    result.id = gNodeId
-    if result.id == nodeIdToDebug:
-      echo "KIND ", result.kind
-      writeStackTrace()
-    inc gNodeId
-
-proc newNode*(kind: TNodeKind, info: TLineInfo, sons: TNodeSeq = @[],
-             typ: PType = nil): PNode =
-  new(result)
-  result.kind = kind
-  result.info = info
-  result.typ = typ
-  # XXX use shallowCopy here for ownership transfer:
-  result.sons = sons
-  when defined(useNodeIds):
-    result.id = gNodeId
-    if result.id == nodeIdToDebug:
-      echo "KIND ", result.kind
-      writeStackTrace()
-    inc gNodeId
-
-proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
-  result = newNode(kind)
-  result.info = info
-  result.typ = typ
+proc newOpenSym*(n: PNode): PNode {.inline.} =
+  result = newTreeI(nkOpenSym, n.info, n)
 
 proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
   result = newNode(kind)
   result.intVal = intVal
 
-proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode =
-  result = newIntNode(kind, intVal)
+proc newIntNode*(kind: TNodeKind, intVal: Int128): PNode =
+  result = newNode(kind)
+  result.intVal = castToInt64(intVal)
+
+proc lastSon*(n: PNode): PNode {.inline.} = n.sons[^1]
+template setLastSon*(n: PNode, s: PNode) = n.sons[^1] = s
+
+template firstSon*(n: PNode): PNode = n.sons[0]
+template secondSon*(n: PNode): PNode = n.sons[1]
+
+template hasSon*(n: PNode): bool = n.len > 0
+template has2Sons*(n: PNode): bool = n.len > 1
+
+proc replaceFirstSon*(n, newson: PNode) {.inline.} =
+  n.sons[0] = newson
+
+proc replaceSon*(n: PNode; i: int; newson: PNode) {.inline.} =
+  n.sons[i] = newson
+
+proc last*(n: PType): PType {.inline.} = n.sons[^1]
+
+proc elementType*(n: PType): PType {.inline.} = n.sons[^1]
+proc skipModifier*(n: PType): PType {.inline.} = n.sons[^1]
+
+proc indexType*(n: PType): PType {.inline.} = n.sons[0]
+proc baseClass*(n: PType): PType {.inline.} = n.sons[0]
+
+proc base*(t: PType): PType {.inline.} =
+  result = t.sons[0]
+
+proc returnType*(n: PType): PType {.inline.} = n.sons[0]
+proc setReturnType*(n, r: PType) {.inline.} = n.sons[0] = r
+proc setIndexType*(n, idx: PType) {.inline.} = n.sons[0] = idx
+
+proc firstParamType*(n: PType): PType {.inline.} = n.sons[1]
+proc firstGenericParam*(n: PType): PType {.inline.} = n.sons[1]
+
+proc typeBodyImpl*(n: PType): PType {.inline.} = n.sons[^1]
+
+proc genericHead*(n: PType): PType {.inline.} = n.sons[0]
+
+proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
+  ## Used throughout the compiler code to test whether a type tree contains or
+  ## doesn't contain a specific type/types - it is often the case that only the
+  ## last child nodes of a type tree need to be searched. This is a really hot
+  ## path within the compiler!
+  result = t
+  while result.kind in kinds: result = last(result)
+
+proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode =
+  let kind = skipTypes(typ, abstractVarRange).kind
+  case kind
+  of tyInt:     result = newNode(nkIntLit)
+  of tyInt8:    result = newNode(nkInt8Lit)
+  of tyInt16:   result = newNode(nkInt16Lit)
+  of tyInt32:   result = newNode(nkInt32Lit)
+  of tyInt64:   result = newNode(nkInt64Lit)
+  of tyChar:    result = newNode(nkCharLit)
+  of tyUInt:    result = newNode(nkUIntLit)
+  of tyUInt8:   result = newNode(nkUInt8Lit)
+  of tyUInt16:  result = newNode(nkUInt16Lit)
+  of tyUInt32:  result = newNode(nkUInt32Lit)
+  of tyUInt64:  result = newNode(nkUInt64Lit)
+  of tyBool, tyEnum:
+    # XXX: does this really need to be the kind nkIntLit?
+    result = newNode(nkIntLit)
+  of tyStatic: # that's a pre-existing bug, will fix in another PR
+    result = newNode(nkIntLit)
+  else: raiseAssert $kind
+  result.intVal = intVal
   result.typ = typ
 
+proc newIntTypeNode*(intVal: Int128, typ: PType): PNode =
+  # XXX: introduce range check
+  newIntTypeNode(castToInt64(intVal), typ)
+
 proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode =
   result = newNode(kind)
   result.floatVal = floatVal
@@ -1229,11 +1382,6 @@ proc newStrNode*(strVal: string; info: TLineInfo): PNode =
   result = newNodeI(nkStrLit, info)
   result.strVal = strVal
 
-proc addSon*(father, son: PNode) =
-  assert son != nil
-  if isNil(father.sons): father.sons = @[]
-  add(father.sons, son)
-
 proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode,
                  params,
                  name, pattern, genericParams,
@@ -1243,54 +1391,148 @@ proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode,
                   pragmas, exceptions, body]
 
 const
-  UnspecifiedLockLevel* = TLockLevel(-1'i16)
-  MaxLockLevel* = 1000'i16
-  UnknownLockLevel* = TLockLevel(1001'i16)
-
-proc `$`*(x: TLockLevel): string =
-  if x.ord == UnspecifiedLockLevel.ord: result = "<unspecified>"
-  elif x.ord == UnknownLockLevel.ord: result = "<unknown>"
-  else: result = $int16(x)
-
-proc newType*(kind: TTypeKind, owner: PSym): PType =
-  new(result)
-  result.kind = kind
-  result.owner = owner
-  result.size = - 1
-  result.align = 2            # default alignment
-  result.id = getID()
-  result.lockLevel = UnspecifiedLockLevel
-  when debugIds:
-    registerId(result)
+  AttachedOpToStr*: array[TTypeAttachedOp, string] = [
+    "=wasMoved", "=destroy", "=copy", "=dup", "=sink", "=trace", "=deepcopy"]
+
+proc `$`*(s: PSym): string =
+  if s != nil:
+    result = s.name.s & "@" & $s.id
+  else:
+    result = "<nil>"
+
+when false:
+  iterator items*(t: PType): PType =
+    for i in 0..<t.sons.len: yield t.sons[i]
+
+  iterator pairs*(n: PType): tuple[i: int, n: PType] =
+    for i in 0..<n.sons.len: yield (i, n.sons[i])
+
+when true:
+  proc len*(n: PType): int {.inline.} =
+    result = n.sons.len
+
+proc sameTupleLengths*(a, b: PType): bool {.inline.} =
+  result = a.sons.len == b.sons.len
+
+iterator tupleTypePairs*(a, b: PType): (int, PType, PType) =
+  for i in 0 ..< a.sons.len:
+    yield (i, a.sons[i], b.sons[i])
+
+iterator underspecifiedPairs*(a, b: PType; start = 0; without = 0): (PType, PType) =
+  # XXX Figure out with what typekinds this is called.
+  for i in start ..< min(a.sons.len, b.sons.len) + without:
+    yield (a.sons[i], b.sons[i])
+
+proc signatureLen*(t: PType): int {.inline.} =
+  result = t.sons.len
+
+proc paramsLen*(t: PType): int {.inline.} =
+  result = t.sons.len - 1
+
+proc genericParamsLen*(t: PType): int {.inline.} =
+  assert t.kind == tyGenericInst
+  result = t.sons.len - 2 # without 'head' and 'body'
+
+proc genericInvocationParamsLen*(t: PType): int {.inline.} =
+  assert t.kind == tyGenericInvocation
+  result = t.sons.len - 1 # without 'head'
+
+proc kidsLen*(t: PType): int {.inline.} =
+  result = t.sons.len
+
+proc genericParamHasConstraints*(t: PType): bool {.inline.} = t.sons.len > 0
+
+proc hasElementType*(t: PType): bool {.inline.} = t.sons.len > 0
+proc isEmptyTupleType*(t: PType): bool {.inline.} = t.sons.len == 0
+proc isSingletonTupleType*(t: PType): bool {.inline.} = t.sons.len == 1
+
+proc genericConstraint*(t: PType): PType {.inline.} = t.sons[0]
+
+iterator genericInstParams*(t: PType): (bool, PType) =
+  for i in 1..<t.sons.len-1:
+    yield (i!=1, t.sons[i])
+
+iterator genericInstParamPairs*(a, b: PType): (int, PType, PType) =
+  for i in 1..<min(a.sons.len, b.sons.len)-1:
+    yield (i-1, a.sons[i], b.sons[i])
+
+iterator genericInvocationParams*(t: PType): (bool, PType) =
+  for i in 1..<t.sons.len:
+    yield (i!=1, t.sons[i])
+
+iterator genericInvocationAndBodyElements*(a, b: PType): (PType, PType) =
+  for i in 1..<a.sons.len:
+    yield (a.sons[i], b.sons[i-1])
+
+iterator genericInvocationParamPairs*(a, b: PType): (bool, PType, PType) =
+  for i in 1..<a.sons.len:
+    if i >= b.sons.len:
+      yield (false, nil, nil)
+    else:
+      yield (true, a.sons[i], b.sons[i])
+
+iterator genericBodyParams*(t: PType): (int, PType) =
+  for i in 0..<t.sons.len-1:
+    yield (i, t.sons[i])
+
+iterator userTypeClassInstParams*(t: PType): (bool, PType) =
+  for i in 1..<t.sons.len-1:
+    yield (i!=1, t.sons[i])
+
+iterator ikids*(t: PType): (int, PType) =
+  for i in 0..<t.sons.len: yield (i, t.sons[i])
+
+const
+  FirstParamAt* = 1
+  FirstGenericParamAt* = 1
+
+iterator paramTypes*(t: PType): (int, PType) =
+  for i in FirstParamAt..<t.sons.len: yield (i, t.sons[i])
+
+iterator paramTypePairs*(a, b: PType): (PType, PType) =
+  for i in FirstParamAt..<a.sons.len: yield (a.sons[i], b.sons[i])
+
+template paramTypeToNodeIndex*(x: int): int = x
+
+iterator kids*(t: PType): PType =
+  for i in 0..<t.sons.len: yield t.sons[i]
+
+iterator signature*(t: PType): PType =
+  # yields return type + parameter types
+  for i in 0..<t.sons.len: yield t.sons[i]
+
+proc newType*(kind: TTypeKind; idgen: IdGenerator; owner: PSym; son: sink PType = nil): PType =
+  let id = nextTypeId idgen
+  result = PType(kind: kind, owner: owner, size: defaultSize,
+                 align: defaultAlignment, itemId: id,
+                 uniqueId: id, sons: @[])
+  if son != nil: result.sons.add son
   when false:
-    if result.id == 205734:
+    if result.itemId.module == 55 and result.itemId.item == 2:
       echo "KNID ", kind
       writeStackTrace()
 
+proc setSons*(dest: PType; sons: sink seq[PType]) {.inline.} = dest.sons = sons
+proc setSon*(dest: PType; son: sink PType) {.inline.} = dest.sons = @[son]
+proc setSonsLen*(dest: PType; len: int) {.inline.} = setLen(dest.sons, len)
+
 proc mergeLoc(a: var TLoc, b: TLoc) =
-  if a.k == low(a.k): a.k = b.k
-  if a.storage == low(a.storage): a.storage = b.storage
-  a.flags = a.flags + b.flags
+  if a.k == low(typeof(a.k)): a.k = b.k
+  if a.storage == low(typeof(a.storage)): a.storage = b.storage
+  a.flags.incl b.flags
   if a.lode == nil: a.lode = b.lode
-  if a.r == nil: a.r = b.r
+  if a.snippet == "": a.snippet = b.snippet
 
 proc newSons*(father: PNode, length: int) =
-  if isNil(father.sons):
-    newSeq(father.sons, length)
-  else:
-    setLen(father.sons, length)
+  setLen(father.sons, length)
 
 proc newSons*(father: PType, length: int) =
-  if isNil(father.sons):
-    newSeq(father.sons, length)
-  else:
-    setLen(father.sons, length)
+  setLen(father.sons, length)
 
-proc sonsLen*(n: PType): int = n.sons.len
-proc len*(n: PType): int = n.sons.len
-proc sonsLen*(n: PNode): int = n.sons.len
-proc lastSon*(n: PNode): PNode = n.sons[^1]
-proc lastSon*(n: PType): PType = n.sons[^1]
+proc truncateInferredTypeCandidates*(t: PType) {.inline.} =
+  assert t.kind == tyInferred
+  if t.sons.len > 1:
+    setLen(t.sons, 1)
 
 proc assignType*(dest, src: PType) =
   dest.kind = src.kind
@@ -1299,113 +1541,74 @@ proc assignType*(dest, src: PType) =
   dest.n = src.n
   dest.size = src.size
   dest.align = src.align
-  dest.destructor = src.destructor
-  dest.deepCopy = src.deepCopy
-  dest.sink = src.sink
-  dest.assignment = src.assignment
-  dest.lockLevel = src.lockLevel
   # this fixes 'type TLock = TSysLock':
   if src.sym != nil:
     if dest.sym != nil:
-      dest.sym.flags = dest.sym.flags + (src.sym.flags-{sfExported})
+      dest.sym.flags.incl src.sym.flags-{sfUsed, sfExported}
       if dest.sym.annex == nil: dest.sym.annex = src.sym.annex
       mergeLoc(dest.sym.loc, src.sym.loc)
     else:
       dest.sym = src.sym
-  newSons(dest, sonsLen(src))
-  for i in countup(0, sonsLen(src) - 1): dest.sons[i] = src.sons[i]
+  newSons(dest, src.sons.len)
+  for i in 0..<src.sons.len: dest[i] = src[i]
 
-proc copyType*(t: PType, owner: PSym, keepId: bool): PType =
-  result = newType(t.kind, owner)
+proc copyType*(t: PType, idgen: IdGenerator, owner: PSym): PType =
+  result = newType(t.kind, idgen, owner)
   assignType(result, t)
-  if keepId:
-    result.id = t.id
-  else:
-    when debugIds: registerId(result)
   result.sym = t.sym          # backend-info should not be copied
 
-proc exactReplica*(t: PType): PType = copyType(t, t.owner, true)
+proc exactReplica*(t: PType): PType =
+  result = PType(kind: t.kind, owner: t.owner, size: defaultSize,
+                 align: defaultAlignment, itemId: t.itemId,
+                 uniqueId: t.uniqueId)
+  assignType(result, t)
+  result.sym = t.sym          # backend-info should not be copied
 
-proc copySym*(s: PSym, keepId: bool = false): PSym =
-  result = newSym(s.kind, s.name, s.owner, s.info, s.options)
+proc copySym*(s: PSym; idgen: IdGenerator): PSym =
+  result = newSym(s.kind, s.name, idgen, s.owner, s.info, s.options)
   #result.ast = nil            # BUGFIX; was: s.ast which made problems
   result.typ = s.typ
-  if keepId:
-    result.id = s.id
-  else:
-    result.id = getID()
-    when debugIds: registerId(result)
   result.flags = s.flags
   result.magic = s.magic
-  if s.kind == skModule:
-    copyStrTable(result.tab, s.tab)
   result.options = s.options
   result.position = s.position
   result.loc = s.loc
   result.annex = s.annex      # BUGFIX
+  result.constraint = s.constraint
   if result.kind in {skVar, skLet, skField}:
     result.guard = s.guard
+    result.bitsize = s.bitsize
+    result.alignment = s.alignment
 
-proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo;
+proc createModuleAlias*(s: PSym, idgen: IdGenerator, newIdent: PIdent, info: TLineInfo;
                         options: TOptions): PSym =
-  result = newSym(s.kind, newIdent, s.owner, info, options)
+  result = newSym(s.kind, newIdent, idgen, s.owner, info, options)
   # keep ID!
   result.ast = s.ast
-  result.id = s.id
+  #result.id = s.id # XXX figure out what to do with the ID.
   result.flags = s.flags
-  system.shallowCopy(result.tab, s.tab)
   result.options = s.options
   result.position = s.position
   result.loc = s.loc
   result.annex = s.annex
-  # XXX once usedGenerics is used, ensure module aliases keep working!
-  assert s.usedGenerics == nil
-
-proc initStrTable*(x: var TStrTable) =
-  x.counter = 0
-  newSeq(x.data, StartSize)
-
-proc newStrTable*: TStrTable =
-  initStrTable(result)
-
-proc initIdTable*(x: var TIdTable) =
-  x.counter = 0
-  newSeq(x.data, StartSize)
-
-proc newIdTable*: TIdTable =
-  initIdTable(result)
 
-proc resetIdTable*(x: var TIdTable) =
-  x.counter = 0
-  # clear and set to old initial size:
-  setLen(x.data, 0)
-  setLen(x.data, StartSize)
+proc initStrTable*(): TStrTable =
+  result = TStrTable(counter: 0)
+  newSeq(result.data, StartSize)
 
-proc initObjectSet*(x: var TObjectSet) =
-  x.counter = 0
-  newSeq(x.data, StartSize)
+proc initObjectSet*(): TObjectSet =
+  result = TObjectSet(counter: 0)
+  newSeq(result.data, StartSize)
 
-proc initIdNodeTable*(x: var TIdNodeTable) =
-  x.counter = 0
-  newSeq(x.data, StartSize)
-
-proc initNodeTable*(x: var TNodeTable) =
-  x.counter = 0
-  newSeq(x.data, StartSize)
-
-proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
-  ## Used throughout the compiler code to test whether a type tree contains or
-  ## doesn't contain a specific type/types - it is often the case that only the
-  ## last child nodes of a type tree need to be searched. This is a really hot
-  ## path within the compiler!
-  result = t
-  while result.kind in kinds: result = lastSon(result)
+proc initNodeTable*(): TNodeTable =
+  result = TNodeTable(counter: 0)
+  newSeq(result.data, StartSize)
 
 proc skipTypes*(t: PType, kinds: TTypeKinds; maxIters: int): PType =
   result = t
   var i = maxIters
   while result.kind in kinds:
-    result = lastSon(result)
+    result = last(result)
     dec i
     if i == 0: return nil
 
@@ -1413,35 +1616,29 @@ proc skipTypesOrNil*(t: PType, kinds: TTypeKinds): PType =
   ## same as skipTypes but handles 'nil'
   result = t
   while result != nil and result.kind in kinds:
-    if result.len == 0: return nil
-    result = lastSon(result)
+    if result.sons.len == 0: return nil
+    result = last(result)
 
 proc isGCedMem*(t: PType): bool {.inline.} =
   result = t.kind in {tyString, tyRef, tySequence} or
            t.kind == tyProc and t.callConv == ccClosure
 
-proc propagateToOwner*(owner, elem: PType) =
-  const HaveTheirOwnEmpty = {tySequence, tyOpt, tySet, tyPtr, tyRef, tyProc}
-  owner.flags = owner.flags + (elem.flags * {tfHasMeta, tfTriggersCompileTime})
+proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
+  owner.flags.incl elem.flags * {tfHasMeta, tfTriggersCompileTime}
   if tfNotNil in elem.flags:
     if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}:
       owner.flags.incl tfNotNil
-    elif owner.kind notin HaveTheirOwnEmpty:
-      owner.flags.incl tfNeedsInit
-
-  if tfNeedsInit in elem.flags:
-    if owner.kind in HaveTheirOwnEmpty: discard
-    else: owner.flags.incl tfNeedsInit
 
   if elem.isMetaType:
     owner.flags.incl tfHasMeta
 
-  if tfHasAsgn in elem.flags:
+  let mask = elem.flags * {tfHasAsgn, tfHasOwned}
+  if mask != {} and propagateHasAsgn:
     let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink})
     if o2.kind in {tyTuple, tyObject, tyArray,
-                   tySequence, tyOpt, tySet, tyDistinct}:
-      o2.flags.incl tfHasAsgn
-      owner.flags.incl tfHasAsgn
+                   tySequence, tySet, tyDistinct}:
+      o2.flags.incl mask
+      owner.flags.incl mask
 
   if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
                        tyGenericInvocation, tyPtr}:
@@ -1451,24 +1648,17 @@ proc propagateToOwner*(owner, elem: PType) =
       # ensure this doesn't bite us in sempass2.
       owner.flags.incl tfHasGCedMem
 
-proc rawAddSon*(father, son: PType) =
-  if isNil(father.sons): father.sons = @[]
-  add(father.sons, son)
-  if not son.isNil: propagateToOwner(father, son)
-
-proc rawAddSonNoPropagationOfTypeFlags*(father, son: PType) =
-  if isNil(father.sons): father.sons = @[]
-  add(father.sons, son)
+proc rawAddSon*(father, son: PType; propagateHasAsgn = true) =
+  father.sons.add(son)
+  if not son.isNil: propagateToOwner(father, son, propagateHasAsgn)
 
 proc addSonNilAllowed*(father, son: PNode) =
-  if isNil(father.sons): father.sons = @[]
-  add(father.sons, son)
+  father.sons.add(son)
 
 proc delSon*(father: PNode, idx: int) =
-  if isNil(father.sons): return
-  var length = sonsLen(father)
-  for i in countup(idx, length - 2): father.sons[i] = father.sons[i + 1]
-  setLen(father.sons, length - 1)
+  if father.len == 0: return
+  for i in idx..<father.len - 1: father[i] = father[i + 1]
+  father.sons.setLen(father.len - 1)
 
 proc copyNode*(src: PNode): PNode =
   # does not copy its sons!
@@ -1489,93 +1679,155 @@ proc copyNode*(src: PNode): PNode =
   of nkIdent: result.ident = src.ident
   of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
   else: discard
+  when defined(nimsuggest):
+    result.endInfo = src.endInfo
 
-proc shallowCopy*(src: PNode): PNode =
-  # does not copy its sons, but provides space for them:
-  if src == nil: return nil
-  result = newNode(src.kind)
-  result.info = src.info
-  result.typ = src.typ
-  result.flags = src.flags * PersistentNodeFlags
-  result.comment = src.comment
+template transitionNodeKindCommon(k: TNodeKind) =
+  let obj {.inject.} = n[]
+  n[] = TNode(kind: k, typ: obj.typ, info: obj.info, flags: obj.flags)
+  # n.comment = obj.comment # shouldn't be needed, the address doesnt' change
   when defined(useNodeIds):
-    if result.id == nodeIdToDebug:
+    n.id = obj.id
+
+proc transitionSonsKind*(n: PNode, kind: range[nkComesFrom..nkTupleConstr]) =
+  transitionNodeKindCommon(kind)
+  n.sons = obj.sons
+
+proc transitionIntKind*(n: PNode, kind: range[nkCharLit..nkUInt64Lit]) =
+  transitionNodeKindCommon(kind)
+  n.intVal = obj.intVal
+
+proc transitionIntToFloatKind*(n: PNode, kind: range[nkFloatLit..nkFloat128Lit]) =
+  transitionNodeKindCommon(kind)
+  n.floatVal = BiggestFloat(obj.intVal)
+
+proc transitionNoneToSym*(n: PNode) =
+  transitionNodeKindCommon(nkSym)
+
+template transitionSymKindCommon*(k: TSymKind) =
+  let obj {.inject.} = s[]
+  s[] = TSym(kind: k, itemId: obj.itemId, magic: obj.magic, typ: obj.typ, name: obj.name,
+             info: obj.info, owner: obj.owner, flags: obj.flags, ast: obj.ast,
+             options: obj.options, position: obj.position, offset: obj.offset,
+             loc: obj.loc, annex: obj.annex, constraint: obj.constraint)
+  when hasFFI:
+    s.cname = obj.cname
+  when defined(nimsuggest):
+    s.allUsages = obj.allUsages
+
+proc transitionGenericParamToType*(s: PSym) =
+  transitionSymKindCommon(skType)
+
+proc transitionRoutineSymKind*(s: PSym, kind: range[skProc..skTemplate]) =
+  transitionSymKindCommon(kind)
+  s.gcUnsafetyReason = obj.gcUnsafetyReason
+  s.transformedBody = obj.transformedBody
+
+proc transitionToLet*(s: PSym) =
+  transitionSymKindCommon(skLet)
+  s.guard = obj.guard
+  s.bitsize = obj.bitsize
+  s.alignment = obj.alignment
+
+template copyNodeImpl(dst, src, processSonsStmt) =
+  if src == nil: return
+  dst = newNode(src.kind)
+  dst.info = src.info
+  when defined(nimsuggest):
+    result.endInfo = src.endInfo
+  dst.typ = src.typ
+  dst.flags = src.flags * PersistentNodeFlags
+  dst.comment = src.comment
+  when defined(useNodeIds):
+    if dst.id == nodeIdToDebug:
       echo "COMES FROM ", src.id
   case src.kind
-  of nkCharLit..nkUInt64Lit: result.intVal = src.intVal
-  of nkFloatLiterals: result.floatVal = src.floatVal
-  of nkSym: result.sym = src.sym
-  of nkIdent: result.ident = src.ident
-  of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
-  else: newSeq(result.sons, sonsLen(src))
+  of nkCharLit..nkUInt64Lit: dst.intVal = src.intVal
+  of nkFloatLiterals: dst.floatVal = src.floatVal
+  of nkSym: dst.sym = src.sym
+  of nkIdent: dst.ident = src.ident
+  of nkStrLit..nkTripleStrLit: dst.strVal = src.strVal
+  else: processSonsStmt
+
+proc shallowCopy*(src: PNode): PNode =
+  # does not copy its sons, but provides space for them:
+  copyNodeImpl(result, src):
+    newSeq(result.sons, src.len)
 
 proc copyTree*(src: PNode): PNode =
   # copy a whole syntax tree; performs deep copying
-  if src == nil:
-    return nil
-  result = newNode(src.kind)
-  result.info = src.info
-  result.typ = src.typ
-  result.flags = src.flags * PersistentNodeFlags
-  result.comment = src.comment
-  when defined(useNodeIds):
-    if result.id == nodeIdToDebug:
-      echo "COMES FROM ", src.id
-  case src.kind
-  of nkCharLit..nkUInt64Lit: result.intVal = src.intVal
-  of nkFloatLiterals: result.floatVal = src.floatVal
-  of nkSym: result.sym = src.sym
-  of nkIdent: result.ident = src.ident
-  of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
-  else:
-    newSeq(result.sons, sonsLen(src))
-    for i in countup(0, sonsLen(src) - 1):
-      result.sons[i] = copyTree(src.sons[i])
+  copyNodeImpl(result, src):
+    newSeq(result.sons, src.len)
+    for i in 0..<src.len:
+      result[i] = copyTree(src[i])
+
+proc copyTreeWithoutNode*(src, skippedNode: PNode): PNode =
+  copyNodeImpl(result, src):
+    result.sons = newSeqOfCap[PNode](src.len)
+    for n in src.sons:
+      if n != skippedNode:
+        result.sons.add copyTreeWithoutNode(n, skippedNode)
 
 proc hasSonWith*(n: PNode, kind: TNodeKind): bool =
-  for i in countup(0, sonsLen(n) - 1):
-    if n.sons[i].kind == kind:
+  for i in 0..<n.len:
+    if n[i].kind == kind:
       return true
   result = false
 
 proc hasNilSon*(n: PNode): bool =
-  for i in countup(0, safeLen(n) - 1):
-    if n.sons[i] == nil:
+  for i in 0..<n.safeLen:
+    if n[i] == nil:
       return true
-    elif hasNilSon(n.sons[i]):
+    elif hasNilSon(n[i]):
       return true
   result = false
 
 proc containsNode*(n: PNode, kinds: TNodeKinds): bool =
+  result = false
   if n == nil: return
   case n.kind
   of nkEmpty..nkNilLit: result = n.kind in kinds
   else:
-    for i in countup(0, sonsLen(n) - 1):
-      if n.kind in kinds or containsNode(n.sons[i], kinds): return true
+    for i in 0..<n.len:
+      if n.kind in kinds or containsNode(n[i], kinds): return true
 
 proc hasSubnodeWith*(n: PNode, kind: TNodeKind): bool =
   case n.kind
-  of nkEmpty..nkNilLit: result = n.kind == kind
+  of nkEmpty..nkNilLit, nkFormalParams: result = n.kind == kind
   else:
-    for i in countup(0, sonsLen(n) - 1):
-      if (n.sons[i].kind == kind) or hasSubnodeWith(n.sons[i], kind):
+    for i in 0..<n.len:
+      if (n[i].kind == kind) or hasSubnodeWith(n[i], kind):
         return true
     result = false
 
-proc getInt*(a: PNode): BiggestInt =
+proc getInt*(a: PNode): Int128 =
+  case a.kind
+  of nkCharLit, nkUIntLit..nkUInt64Lit:
+    result = toInt128(cast[uint64](a.intVal))
+  of nkInt8Lit..nkInt64Lit:
+    result = toInt128(a.intVal)
+  of nkIntLit:
+    # XXX: enable this assert
+    # assert a.typ.kind notin {tyChar, tyUint..tyUInt64}
+    result = toInt128(a.intVal)
+  else:
+    raiseRecoverableError("cannot extract number from invalid AST node")
+
+proc getInt64*(a: PNode): int64 {.deprecated: "use getInt".} =
   case a.kind
-  of nkCharLit..nkUInt64Lit: result = a.intVal
+  of nkCharLit, nkUIntLit..nkUInt64Lit, nkIntLit..nkInt64Lit:
+    result = a.intVal
   else:
-    #internalError(a.info, "getInt")
-    doAssert false, "getInt"
-    #result = 0
+    raiseRecoverableError("cannot extract number from invalid AST node")
 
 proc getFloat*(a: PNode): BiggestFloat =
   case a.kind
   of nkFloatLiterals: result = a.floatVal
+  of nkCharLit, nkUIntLit..nkUInt64Lit, nkIntLit..nkInt64Lit:
+    result = BiggestFloat a.intVal
   else:
-    doAssert false, "getFloat"
+    raiseRecoverableError("cannot extract number from invalid AST node")
+    #doAssert false, "getFloat"
     #internalError(a.info, "getFloat")
     #result = 0.0
 
@@ -1584,9 +1836,10 @@ proc getStr*(a: PNode): string =
   of nkStrLit..nkTripleStrLit: result = a.strVal
   of nkNilLit:
     # let's hope this fixes more problems than it creates:
-    result = nil
+    result = ""
   else:
-    doAssert false, "getStr"
+    raiseRecoverableError("cannot extract string from invalid AST node")
+    #doAssert false, "getStr"
     #internalError(a.info, "getStr")
     #result = ""
 
@@ -1595,28 +1848,51 @@ proc getStrOrChar*(a: PNode): string =
   of nkStrLit..nkTripleStrLit: result = a.strVal
   of nkCharLit..nkUInt64Lit: result = $chr(int(a.intVal))
   else:
-    doAssert false, "getStrOrChar"
+    raiseRecoverableError("cannot extract string from invalid AST node")
+    #doAssert false, "getStrOrChar"
     #internalError(a.info, "getStrOrChar")
     #result = ""
 
-proc isGenericRoutine*(s: PSym): bool =
-  case s.kind
-  of skProcKinds:
-    result = sfFromGeneric in s.flags or
-             (s.ast != nil and s.ast[genericParamsPos].kind != nkEmpty)
-  else: discard
+proc isGenericParams*(n: PNode): bool {.inline.} =
+  ## used to judge whether a node is generic params.
+  n != nil and n.kind == nkGenericParams
+
+proc isGenericRoutine*(n: PNode): bool  {.inline.} =
+  n != nil and n.kind in callableDefs and n[genericParamsPos].isGenericParams
+
+proc isGenericRoutineStrict*(s: PSym): bool {.inline.} =
+  ## determines if this symbol represents a generic routine
+  ## the unusual name is so it doesn't collide and eventually replaces
+  ## `isGenericRoutine`
+  s.kind in skProcKinds and s.ast.isGenericRoutine
+
+proc isGenericRoutine*(s: PSym): bool {.inline.} =
+  ## determines if this symbol represents a generic routine or an instance of
+  ## one. This should be renamed accordingly and `isGenericRoutineStrict`
+  ## should take this name instead.
+  ##
+  ## Warning/XXX: Unfortunately, it considers a proc kind symbol flagged with
+  ## sfFromGeneric as a generic routine. Instead this should likely not be the
+  ## case and the concepts should be teased apart:
+  ## - generic definition
+  ## - generic instance
+  ## - either generic definition or instance
+  s.kind in skProcKinds and (sfFromGeneric in s.flags or
+                             s.ast.isGenericRoutine)
 
 proc skipGenericOwner*(s: PSym): PSym =
   ## Generic instantiations are owned by their originating generic
   ## symbol. This proc skips such owners and goes straight to the owner
   ## of the generic itself (the module or the enclosing proc).
-  result = if s.kind in skProcKinds and sfFromGeneric in s.flags:
+  result = if s.kind == skModule:
+            s
+           elif s.kind in skProcKinds and sfFromGeneric in s.flags and s.owner.kind != skModule:
              s.owner.owner
            else:
              s.owner
 
 proc originatingModule*(s: PSym): PSym =
-  result = s.owner
+  result = s
   while result.kind != skModule: result = result.owner
 
 proc isRoutine*(s: PSym): bool {.inline.} =
@@ -1624,32 +1900,23 @@ proc isRoutine*(s: PSym): bool {.inline.} =
 
 proc isCompileTimeProc*(s: PSym): bool {.inline.} =
   result = s.kind == skMacro or
-           s.kind == skProc and sfCompileTime in s.flags
-
-proc requiredParams*(s: PSym): int =
-  # Returns the number of required params (without default values)
-  # XXX: Perhaps we can store this in the `offset` field of the
-  # symbol instead?
-  for i in 1 ..< s.typ.len:
-    if s.typ.n[i].sym.ast != nil:
-      return i - 1
-  return s.typ.len - 1
+           s.kind in {skProc, skFunc} and sfCompileTime in s.flags
 
 proc hasPattern*(s: PSym): bool {.inline.} =
-  result = isRoutine(s) and s.ast.sons[patternPos].kind != nkEmpty
+  result = isRoutine(s) and s.ast[patternPos].kind != nkEmpty
 
 iterator items*(n: PNode): PNode =
-  for i in 0..<n.safeLen: yield n.sons[i]
+  for i in 0..<n.safeLen: yield n[i]
 
 iterator pairs*(n: PNode): tuple[i: int, n: PNode] =
-  for i in 0..<n.safeLen: yield (i, n.sons[i])
+  for i in 0..<n.safeLen: yield (i, n[i])
 
 proc isAtom*(n: PNode): bool {.inline.} =
   result = n.kind >= nkNone and n.kind <= nkNilLit
 
 proc isEmptyType*(t: PType): bool {.inline.} =
-  ## 'void' and 'stmt' types are often equivalent to 'nil' these days:
-  result = t == nil or t.kind in {tyVoid, tyStmt}
+  ## 'void' and 'typed' types are often equivalent to 'nil' these days:
+  result = t == nil or t.kind in {tyVoid, tyTyped}
 
 proc makeStmtList*(n: PNode): PNode =
   if n.kind == nkStmtList:
@@ -1660,59 +1927,78 @@ proc makeStmtList*(n: PNode): PNode =
 
 proc skipStmtList*(n: PNode): PNode =
   if n.kind in {nkStmtList, nkStmtListExpr}:
-    for i in 0 .. n.len-2:
+    for i in 0..<n.len-1:
       if n[i].kind notin {nkEmpty, nkCommentStmt}: return n
     result = n.lastSon
   else:
     result = n
 
-proc toRef*(typ: PType): PType =
+proc toVar*(typ: PType; kind: TTypeKind; idgen: IdGenerator): PType =
+  ## If ``typ`` is not a tyVar then it is converted into a `var <typ>` and
+  ## returned. Otherwise ``typ`` is simply returned as-is.
+  result = typ
+  if typ.kind != kind:
+    result = newType(kind, idgen, typ.owner, typ)
+
+proc toRef*(typ: PType; idgen: IdGenerator): PType =
   ## If ``typ`` is a tyObject then it is converted into a `ref <typ>` and
   ## returned. Otherwise ``typ`` is simply returned as-is.
   result = typ
-  if typ.kind == tyObject:
-    result = newType(tyRef, typ.owner)
-    rawAddSon(result, typ)
+  if typ.skipTypes({tyAlias, tyGenericInst}).kind == tyObject:
+    result = newType(tyRef, idgen, typ.owner, typ)
 
 proc toObject*(typ: PType): PType =
   ## If ``typ`` is a tyRef then its immediate son is returned (which in many
   ## cases should be a ``tyObject``).
   ## Otherwise ``typ`` is simply returned as-is.
+  let t = typ.skipTypes({tyAlias, tyGenericInst})
+  if t.kind == tyRef: t.elementType
+  else: typ
+
+proc toObjectFromRefPtrGeneric*(typ: PType): PType =
+  #[
+  See also `toObject`.
+  Finds the underlying `object`, even in cases like these:
+  type
+    B[T] = object f0: int
+    A1[T] = ref B[T]
+    A2[T] = ref object f1: int
+    A3 = ref object f2: int
+    A4 = object f3: int
+  ]#
   result = typ
-  if result.kind == tyRef:
-    result = result.lastSon
-
-proc isException*(t: PType): bool =
-  # check if `y` is object type and it inherits from Exception
-  assert(t != nil)
-
-  if t.kind != tyObject:
-    return false
-
-  var base = t
-  while base != nil:
-    if base.sym != nil and base.sym.magic == mException:
-      return true
-    base = base.lastSon
-  return false
+  while true:
+    case result.kind
+    of tyGenericBody: result = result.last
+    of tyRef, tyPtr, tyGenericInst, tyGenericInvocation, tyAlias: result = result[0]
+      # automatic dereferencing is deep, refs #18298.
+    else: break
+  # result does not have to be object type
 
 proc isImportedException*(t: PType; conf: ConfigRef): bool =
-  assert(t != nil)
-  if optNoCppExceptions in conf.globalOptions:
+  assert t != nil
+
+  if conf.exc != excCpp:
     return false
 
   let base = t.skipTypes({tyAlias, tyPtr, tyDistinct, tyGenericInst})
-
-  if base.sym != nil and sfCompileToCpp in base.sym.flags:
-    result = true
+  result = base.sym != nil and {sfCompileToCpp, sfImportc} * base.sym.flags != {}
 
 proc isInfixAs*(n: PNode): bool =
-  return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "as"
+  return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.id == ord(wAs)
+
+proc skipColon*(n: PNode): PNode =
+  result = n
+  if n.kind == nkExprColonExpr:
+    result = n[1]
 
 proc findUnresolvedStatic*(n: PNode): PNode =
-  if n.kind == nkSym and n.typ.kind == tyStatic and n.typ.n == nil:
+  if n.kind == nkSym and n.typ != nil and n.typ.kind == tyStatic and n.typ.n == nil:
     return n
-
+  if n.typ != nil and n.typ.kind == tyTypeDesc:
+    let t = skipTypes(n.typ, {tyTypeDesc})
+    if t.kind == tyGenericParam and not t.genericParamHasConstraints:
+      return n
   for son in n:
     let n = son.findUnresolvedStatic
     if n != nil: return n
@@ -1723,14 +2009,127 @@ when false:
   proc containsNil*(n: PNode): bool =
     # only for debugging
     if n.isNil: return true
-    for i in 0 ..< n.safeLen:
+    for i in 0..<n.safeLen:
       if n[i].containsNil: return true
 
-template hasDestructor*(t: PType): bool = tfHasAsgn in t.flags
+
+template hasDestructor*(t: PType): bool = {tfHasAsgn, tfHasOwned} * t.flags != {}
+
 template incompleteType*(t: PType): bool =
   t.sym != nil and {sfForward, sfNoForward} * t.sym.flags == {sfForward}
 
 template typeCompleted*(s: PSym) =
   incl s.flags, sfNoForward
 
-template getBody*(s: PSym): PNode = s.ast[bodyPos]
+template detailedInfo*(sym: PSym): string =
+  sym.name.s
+
+proc isInlineIterator*(typ: PType): bool {.inline.} =
+  typ.kind == tyProc and tfIterator in typ.flags and typ.callConv != ccClosure
+
+proc isIterator*(typ: PType): bool {.inline.} =
+  typ.kind == tyProc and tfIterator in typ.flags
+
+proc isClosureIterator*(typ: PType): bool {.inline.} =
+  typ.kind == tyProc and tfIterator in typ.flags and typ.callConv == ccClosure
+
+proc isClosure*(typ: PType): bool {.inline.} =
+  typ.kind == tyProc and typ.callConv == ccClosure
+
+proc isNimcall*(s: PSym): bool {.inline.} =
+  s.typ.callConv == ccNimCall
+
+proc isExplicitCallConv*(s: PSym): bool {.inline.} =
+  tfExplicitCallConv in s.typ.flags
+
+proc isSinkParam*(s: PSym): bool {.inline.} =
+  s.kind == skParam and (s.typ.kind == tySink or tfHasOwned in s.typ.flags)
+
+proc isSinkType*(t: PType): bool {.inline.} =
+  t.kind == tySink or tfHasOwned in t.flags
+
+proc newProcType*(info: TLineInfo; idgen: IdGenerator; owner: PSym): PType =
+  result = newType(tyProc, idgen, owner)
+  result.n = newNodeI(nkFormalParams, info)
+  rawAddSon(result, nil) # return type
+  # result.n[0] used to be `nkType`, but now it's `nkEffectList` because
+  # the effects are now stored in there too ... this is a bit hacky, but as
+  # usual we desperately try to save memory:
+  result.n.add newNodeI(nkEffectList, info)
+
+proc addParam*(procType: PType; param: PSym) =
+  param.position = procType.sons.len-1
+  procType.n.add newSymNode(param)
+  rawAddSon(procType, param.typ)
+
+const magicsThatCanRaise = {
+  mNone, mSlurp, mStaticExec, mParseExprToAst, mParseStmtToAst, mEcho}
+
+proc canRaiseConservative*(fn: PNode): bool =
+  if fn.kind == nkSym and fn.sym.magic notin magicsThatCanRaise:
+    result = false
+  else:
+    result = true
+
+proc canRaise*(fn: PNode): bool =
+  if fn.kind == nkSym and (fn.sym.magic notin magicsThatCanRaise or
+      {sfImportc, sfInfixCall} * fn.sym.flags == {sfImportc} or
+      sfGeneratedOp in fn.sym.flags):
+    result = false
+  elif fn.kind == nkSym and fn.sym.magic == mEcho:
+    result = true
+  else:
+    # TODO check for n having sons? or just return false for now if not
+    if fn.typ != nil and fn.typ.n != nil and fn.typ.n[0].kind == nkSym:
+      result = false
+    else:
+      result = fn.typ != nil and fn.typ.n != nil and ((fn.typ.n[0].len < effectListLen) or
+        (fn.typ.n[0][exceptionEffects] != nil and
+        fn.typ.n[0][exceptionEffects].safeLen > 0))
+
+proc toHumanStrImpl[T](kind: T, num: static int): string =
+  result = $kind
+  result = result[num..^1]
+  result[0] = result[0].toLowerAscii
+
+proc toHumanStr*(kind: TSymKind): string =
+  ## strips leading `sk`
+  result = toHumanStrImpl(kind, 2)
+
+proc toHumanStr*(kind: TTypeKind): string =
+  ## strips leading `tk`
+  result = toHumanStrImpl(kind, 2)
+
+proc skipHiddenAddr*(n: PNode): PNode {.inline.} =
+  (if n.kind == nkHiddenAddr: n[0] else: n)
+
+proc isNewStyleConcept*(n: PNode): bool {.inline.} =
+  assert n.kind == nkTypeClassTy
+  result = n[0].kind == nkEmpty
+
+proc isOutParam*(t: PType): bool {.inline.} = tfIsOutParam in t.flags
+
+const
+  nodesToIgnoreSet* = {nkNone..pred(nkSym), succ(nkSym)..nkNilLit,
+    nkTypeSection, nkProcDef, nkConverterDef,
+    nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
+    nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
+    nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
+    nkTypeOfExpr, nkMixinStmt, nkBindStmt}
+
+proc isTrue*(n: PNode): bool =
+  n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
+    n.kind == nkIntLit and n.intVal != 0
+
+type
+  TypeMapping* = Table[ItemId, PType]
+  SymMapping* = Table[ItemId, PSym]
+
+template idTableGet*(tab: typed; key: PSym | PType): untyped = tab.getOrDefault(key.itemId)
+template idTablePut*(tab: typed; key, val: PSym | PType) = tab[key.itemId] = val
+
+template initSymMapping*(): Table[ItemId, PSym] = initTable[ItemId, PSym]()
+template initTypeMapping*(): Table[ItemId, PType] = initTable[ItemId, PType]()
+
+template resetIdTable*(tab: Table[ItemId, PSym]) = tab.clear()
+template resetIdTable*(tab: Table[ItemId, PType]) = tab.clear()
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index 0afe56bb7..7a9892f78 100644
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -12,27 +12,40 @@
 # the data structures here are used in various places of the compiler.
 
 import
-  ast, hashes, intsets, strutils, options, lineinfos, ropes, idents, rodutils,
+  ast, astyaml, options, lineinfos, idents, rodutils,
   msgs
 
+import std/[hashes, intsets]
+import std/strutils except addf
+
+export astyaml.treeToYaml, astyaml.typeToYaml, astyaml.symToYaml, astyaml.lineInfoToStr
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 proc hashNode*(p: RootRef): Hash
-proc treeToYaml*(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
-  # Convert a tree into its YAML representation; this is used by the
-  # YAML code generator and it is invaluable for debugging purposes.
-  # If maxRecDepht <> -1 then it won't print the whole graph.
-proc typeToYaml*(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope
-proc symToYaml*(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope
-proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): Rope
-
-when declared(echo):
-  # these are for debugging only: They are not really deprecated, but I want
-  # the warning so that release versions do not contain debugging statements:
-  proc debug*(conf: ConfigRef; n: PSym) {.deprecated.}
-  proc debug*(conf: ConfigRef; n: PType) {.deprecated.}
-  proc debug*(conf: ConfigRef; n: PNode) {.deprecated.}
-
-template mdbg*: bool {.dirty.} =
-  when compiles(c.module):
+
+# these are for debugging only: They are not really deprecated, but I want
+# the warning so that release versions do not contain debugging statements:
+proc debug*(n: PSym; conf: ConfigRef = nil) {.exportc: "debugSym", deprecated.}
+proc debug*(n: PType; conf: ConfigRef = nil) {.exportc: "debugType", deprecated.}
+proc debug*(n: PNode; conf: ConfigRef = nil) {.exportc: "debugNode", deprecated.}
+
+template debug*(x: PSym|PType|PNode) {.deprecated.} =
+  when compiles(c.config):
+    debug(c.config, x)
+  elif compiles(c.graph.config):
+    debug(c.graph.config, x)
+  else:
+    error()
+
+template debug*(x: auto) {.deprecated.} =
+  echo x
+
+template mdbg*: bool {.deprecated.} =
+  when compiles(c.graph):
+    c.module.fileIdx == c.graph.config.projectMainIdx
+  elif compiles(c.module):
     c.module.fileIdx == c.config.projectMainIdx
   elif compiles(c.c.module):
     c.c.module.fileIdx == c.c.config.projectMainIdx
@@ -52,19 +65,8 @@ template mdbg*: bool {.dirty.} =
   else:
     error()
 
-# --------------------------- ident tables ----------------------------------
-proc idTableGet*(t: TIdTable, key: PIdObj): RootRef
-proc idTableGet*(t: TIdTable, key: int): RootRef
-proc idTablePut*(t: var TIdTable, key: PIdObj, val: RootRef)
-proc idTableHasObjectAsKey*(t: TIdTable, key: PIdObj): bool
-  # checks if `t` contains the `key` (compared by the pointer value, not only
-  # `key`'s id)
-proc idNodeTableGet*(t: TIdNodeTable, key: PIdObj): PNode
-proc idNodeTablePut*(t: var TIdNodeTable, key: PIdObj, val: PNode)
-
 # ---------------------------------------------------------------------------
 
-proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym
 proc lookupInRecord*(n: PNode, field: PIdent): PSym
 proc mustRehash*(length, counter: int): bool
 proc nextTry*(h, maxHash: Hash): Hash {.inline.}
@@ -83,28 +85,28 @@ type
     data*: TIIPairSeq
 
 
-proc initIiTable*(x: var TIITable)
+proc initIITable*(x: var TIITable)
 proc iiTableGet*(t: TIITable, key: int): int
 proc iiTablePut*(t: var TIITable, key, val: int)
 
 # implementation
 
-proc skipConvAndClosure*(n: PNode): PNode =
+proc skipConvCastAndClosure*(n: PNode): PNode =
   result = n
   while true:
     case result.kind
     of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64,
        nkClosure:
-      result = result.sons[0]
-    of nkHiddenStdConv, nkHiddenSubConv, nkConv:
-      result = result.sons[1]
+      result = result[0]
+    of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkCast:
+      result = result[1]
     else: break
 
 proc sameValue*(a, b: PNode): bool =
   result = false
   case a.kind
   of nkCharLit..nkUInt64Lit:
-    if b.kind in {nkCharLit..nkUInt64Lit}: result = a.intVal == b.intVal
+    if b.kind in {nkCharLit..nkUInt64Lit}: result = getInt(a) == getInt(b)
   of nkFloatLit..nkFloat64Lit:
     if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal == b.floatVal
   of nkStrLit..nkTripleStrLit:
@@ -118,8 +120,8 @@ proc leValue*(a, b: PNode): bool =
   # a <= b?
   result = false
   case a.kind
-  of nkCharLit..nkUInt32Lit:
-    if b.kind in {nkCharLit..nkUInt32Lit}: result = a.intVal <= b.intVal
+  of nkCharLit..nkUInt64Lit:
+    if b.kind in {nkCharLit..nkUInt64Lit}: result = getInt(a) <= getInt(b)
   of nkFloatLit..nkFloat64Lit:
     if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal
   of nkStrLit..nkTripleStrLit:
@@ -139,17 +141,17 @@ proc lookupInRecord(n: PNode, field: PIdent): PSym =
   result = nil
   case n.kind
   of nkRecList:
-    for i in countup(0, sonsLen(n) - 1):
-      result = lookupInRecord(n.sons[i], field)
+    for i in 0..<n.len:
+      result = lookupInRecord(n[i], field)
       if result != nil: return
   of nkRecCase:
-    if (n.sons[0].kind != nkSym): return nil
-    result = lookupInRecord(n.sons[0], field)
+    if (n[0].kind != nkSym): return nil
+    result = lookupInRecord(n[0], field)
     if result != nil: return
-    for i in countup(1, sonsLen(n) - 1):
-      case n.sons[i].kind
+    for i in 1..<n.len:
+      case n[i].kind
       of nkOfBranch, nkElse:
-        result = lookupInRecord(lastSon(n.sons[i]), field)
+        result = lookupInRecord(lastSon(n[i]), field)
         if result != nil: return
       else: return nil
   of nkSym:
@@ -161,14 +163,56 @@ proc getModule*(s: PSym): PSym =
   assert((result.kind == skModule) or (result.owner != result))
   while result != nil and result.kind != skModule: result = result.owner
 
-proc getSymFromList(list: PNode, ident: PIdent, start: int = 0): PSym =
-  for i in countup(start, sonsLen(list) - 1):
-    if list.sons[i].kind == nkSym:
-      result = list.sons[i].sym
+proc fromSystem*(op: PSym): bool {.inline.} = sfSystemModule in getModule(op).flags
+proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym =
+  for i in start..<list.len:
+    if list[i].kind == nkSym:
+      result = list[i].sym
       if result.name.id == ident.id: return
     else: return nil
   result = nil
 
+proc sameIgnoreBacktickGensymInfo(a, b: string): bool =
+  result = false
+  if a[0] != b[0]: return false
+  var alen = a.len - 1
+  while alen > 0 and a[alen] != '`': dec(alen)
+  if alen <= 0: alen = a.len
+
+  var i = 1
+  var j = 1
+  while true:
+    while i < alen and a[i] == '_': inc i
+    while j < b.len and b[j] == '_': inc j
+    var aa = if i < alen: toLowerAscii(a[i]) else: '\0'
+    var bb = if j < b.len: toLowerAscii(b[j]) else: '\0'
+    if aa != bb: return false
+
+    # the characters are identical:
+    if i >= alen:
+      # both cursors at the end:
+      if j >= b.len: return true
+      # not yet at the end of 'b':
+      return false
+    elif j >= b.len:
+      return false
+    inc i
+    inc j
+
+proc getNamedParamFromList*(list: PNode, ident: PIdent): PSym =
+  ## Named parameters are special because a named parameter can be
+  ## gensym'ed and then they have '\`<number>' suffix that we need to
+  ## ignore, see compiler / evaltempl.nim, snippet:
+  ##   ```nim
+  ##   result.add newIdentNode(getIdent(c.ic, x.name.s & "\`gensym" & $x.id),
+  ##            if c.instLines: actual.info else: templ.info)
+  ##   ```
+  result = nil
+  for i in 1..<list.len:
+    let it = list[i].sym
+    if it.name.id == ident.id or
+        sameIgnoreBacktickGensymInfo(it.name.s, ident.s): return it
+
 proc hashNode(p: RootRef): Hash =
   result = hash(cast[pointer](p))
 
@@ -176,257 +220,271 @@ proc mustRehash(length, counter: int): bool =
   assert(length > counter)
   result = (length * 2 < counter * 3) or (length - counter < 4)
 
-proc rspaces(x: int): Rope =
-  # returns x spaces
-  result = rope(spaces(x))
-
-proc toYamlChar(c: char): string =
-  case c
-  of '\0'..'\x1F', '\x7F'..'\xFF': result = "\\u" & strutils.toHex(ord(c), 4)
-  of '\'', '\"', '\\': result = '\\' & c
-  else: result = $c
-
-proc makeYamlString*(s: string): Rope =
-  # We have to split long strings into many ropes. Otherwise
-  # this could trigger InternalError(111). See the ropes module for
-  # further information.
-  const MaxLineLength = 64
-  result = nil
-  var res = "\""
-  for i in countup(0, if s.isNil: -1 else: (len(s)-1)):
-    if (i + 1) mod MaxLineLength == 0:
-      add(res, '\"')
-      add(res, "\n")
-      add(result, rope(res))
-      res = "\""              # reset
-    add(res, toYamlChar(s[i]))
-  add(res, '\"')
-  add(result, rope(res))
-
-proc flagsToStr[T](flags: set[T]): Rope =
-  if flags == {}:
-    result = rope("[]")
-  else:
-    result = nil
-    for x in items(flags):
-      if result != nil: add(result, ", ")
-      add(result, makeYamlString($x))
-    result = "[" & result & "]"
-
-proc lineInfoToStr(conf: ConfigRef; info: TLineInfo): Rope =
-  result = "[$1, $2, $3]" % [makeYamlString(toFilename(conf, info)),
-                             rope(toLinenumber(info)),
-                             rope(toColumn(info))]
-
-proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet,
-                   indent, maxRecDepth: int): Rope
-proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet,
-                  indent, maxRecDepth: int): Rope
-proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet,
-                   indent, maxRecDepth: int): Rope
-
-proc ropeConstr(indent: int, c: openArray[Rope]): Rope =
-  # array of (name, value) pairs
-  var istr = rspaces(indent + 2)
-  result = rope("{")
+import std/tables
+
+const backrefStyle = "\e[90m"
+const enumStyle = "\e[34m"
+const numberStyle = "\e[33m"
+const stringStyle = "\e[32m"
+const resetStyle  = "\e[0m"
+
+type
+  DebugPrinter = object
+    conf: ConfigRef
+    visited: Table[pointer, int]
+    renderSymType: bool
+    indent: int
+    currentLine: int
+    firstItem: bool
+    useColor: bool
+    res: string
+
+proc indentMore(this: var DebugPrinter) =
+  this.indent += 2
+
+proc indentLess(this: var DebugPrinter) =
+  this.indent -= 2
+
+proc newlineAndIndent(this: var DebugPrinter) =
+  this.res.add "\n"
+  this.currentLine += 1
+  for i in 0..<this.indent:
+    this.res.add ' '
+
+proc openCurly(this: var DebugPrinter) =
+  this.res.add "{"
+  this.indentMore
+  this.firstItem = true
+
+proc closeCurly(this: var DebugPrinter) =
+  this.indentLess
+  this.newlineAndIndent
+  this.res.add "}"
+
+proc comma(this: var DebugPrinter) =
+  this.res.add ", "
+
+proc openBracket(this: var DebugPrinter) =
+  this.res.add "["
+  #this.indentMore
+
+proc closeBracket(this: var DebugPrinter) =
+  #this.indentLess
+  this.res.add "]"
+
+proc key(this: var DebugPrinter; key: string) =
+  if not this.firstItem:
+    this.res.add ","
+  this.firstItem = false
+
+  this.newlineAndIndent
+  this.res.add "\""
+  this.res.add key
+  this.res.add "\": "
+
+proc value(this: var DebugPrinter; value: string) =
+  if this.useColor:
+    this.res.add stringStyle
+  this.res.add "\""
+  this.res.add value
+  this.res.add "\""
+  if this.useColor:
+    this.res.add resetStyle
+
+proc value(this: var DebugPrinter; value: BiggestInt) =
+  if this.useColor:
+    this.res.add numberStyle
+  this.res.addInt value
+  if this.useColor:
+    this.res.add resetStyle
+
+proc value[T: enum](this: var DebugPrinter; value: T) =
+  if this.useColor:
+    this.res.add enumStyle
+  this.res.add "\""
+  this.res.add $value
+  this.res.add "\""
+  if this.useColor:
+    this.res.add resetStyle
+
+proc value[T: enum](this: var DebugPrinter; value: set[T]) =
+  this.openBracket
+  let high = card(value)-1
   var i = 0
-  while i <= high(c):
-    if i > 0: add(result, ",")
-    addf(result, "$N$1\"$2\": $3", [istr, c[i], c[i + 1]])
-    inc(i, 2)
-  addf(result, "$N$1}", [rspaces(indent)])
-
-proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet, indent: int,
-                  maxRecDepth: int): Rope =
-  if n == nil:
-    result = rope("null")
-  elif containsOrIncl(marker, n.id):
-    result = "\"$1 @$2\"" % [rope(n.name.s), rope(
-        strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]
-  else:
-    var ast = treeToYamlAux(conf, n.ast, marker, indent + 2, maxRecDepth - 1)
-    result = ropeConstr(indent, [rope("kind"),
-                                 makeYamlString($n.kind),
-                                 rope("name"), makeYamlString(n.name.s),
-                                 rope("typ"), typeToYamlAux(conf, n.typ, marker,
-                                   indent + 2, maxRecDepth - 1),
-                                 rope("info"), lineInfoToStr(conf, n.info),
-                                 rope("flags"), flagsToStr(n.flags),
-                                 rope("magic"), makeYamlString($n.magic),
-                                 rope("ast"), ast, rope("options"),
-                                 flagsToStr(n.options), rope("position"),
-                                 rope(n.position)])
-
-proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int,
-                   maxRecDepth: int): Rope =
-  if n == nil:
-    result = rope("null")
-  elif containsOrIncl(marker, n.id):
-    result = "\"$1 @$2\"" % [rope($n.kind), rope(
-        strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]
-  else:
-    if sonsLen(n) > 0:
-      result = rope("[")
-      for i in countup(0, sonsLen(n) - 1):
-        if i > 0: add(result, ",")
-        addf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(conf, n.sons[i],
-            marker, indent + 4, maxRecDepth - 1)])
-      addf(result, "$N$1]", [rspaces(indent + 2)])
-    else:
-      result = rope("null")
-    result = ropeConstr(indent, [rope("kind"),
-                                 makeYamlString($n.kind),
-                                 rope("sym"), symToYamlAux(conf, n.sym, marker,
-        indent + 2, maxRecDepth - 1), rope("n"), treeToYamlAux(conf, n.n, marker,
-        indent + 2, maxRecDepth - 1), rope("flags"), flagsToStr(n.flags),
-                                 rope("callconv"),
-                                 makeYamlString(CallingConvToStr[n.callConv]),
-                                 rope("size"), rope(n.size),
-                                 rope("align"), rope(n.align),
-                                 rope("sons"), result])
-
-proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent: int,
-                   maxRecDepth: int): Rope =
-  if n == nil:
-    result = rope("null")
-  else:
-    var istr = rspaces(indent + 2)
-    result = "{$N$1\"kind\": $2" % [istr, makeYamlString($n.kind)]
-    if maxRecDepth != 0:
-      addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)])
-      case n.kind
-      of nkCharLit..nkInt64Lit:
-        addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)])
-      of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
-        addf(result, ",$N$1\"floatVal\": $2",
-            [istr, rope(n.floatVal.toStrMaxPrecision)])
-      of nkStrLit..nkTripleStrLit:
-        if n.strVal.isNil:
-          addf(result, ",$N$1\"strVal\": null", [istr])
-        else:
-          addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
-      of nkSym:
-        addf(result, ",$N$1\"sym\": $2",
-             [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth)])
-      of nkIdent:
-        if n.ident != nil:
-          addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
-        else:
-          addf(result, ",$N$1\"ident\": null", [istr])
-      else:
-        if sonsLen(n) > 0:
-          addf(result, ",$N$1\"sons\": [", [istr])
-          for i in countup(0, sonsLen(n) - 1):
-            if i > 0: add(result, ",")
-            addf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(conf, n.sons[i],
-                marker, indent + 4, maxRecDepth - 1)])
-          addf(result, "$N$1]", [istr])
-      addf(result, ",$N$1\"typ\": $2",
-           [istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth)])
-    addf(result, "$N$1}", [rspaces(indent)])
-
-proc treeToYaml(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope =
-  var marker = initIntSet()
-  result = treeToYamlAux(conf, n, marker, indent, maxRecDepth)
-
-proc typeToYaml(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope =
-  var marker = initIntSet()
-  result = typeToYamlAux(conf, n, marker, indent, maxRecDepth)
-
-proc symToYaml(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope =
-  var marker = initIntSet()
-  result = symToYamlAux(conf, n, marker, indent, maxRecDepth)
-
-proc debugTree*(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int; renderType=false): Rope
-proc debugType(conf: ConfigRef; n: PType, maxRecDepth=100): Rope =
+  for v in value:
+    this.value v
+    if i != high:
+      this.comma
+    inc i
+  this.closeBracket
+
+template earlyExit(this: var DebugPrinter; n: PType | PNode | PSym) =
   if n == nil:
-    result = rope("null")
+    this.res.add "null"
+    return
+  let index = this.visited.getOrDefault(cast[pointer](n), -1)
+  if index < 0:
+    this.visited[cast[pointer](n)] = this.currentLine
   else:
-    result = rope($n.kind)
-    if n.sym != nil:
-      add(result, " ")
-      add(result, n.sym.name.s)
-    if n.kind in IntegralTypes and n.n != nil:
-      add(result, ", node: ")
-      add(result, debugTree(conf, n.n, 2, maxRecDepth-1, renderType=true))
-    if (n.kind != tyString) and (sonsLen(n) > 0) and maxRecDepth != 0:
-      add(result, "(")
-      for i in countup(0, sonsLen(n) - 1):
-        if i > 0: add(result, ", ")
-        if n.sons[i] == nil:
-          add(result, "null")
-        else:
-          add(result, debugType(conf, n.sons[i], maxRecDepth-1))
-      if n.kind == tyObject and n.n != nil:
-        add(result, ", node: ")
-        add(result, debugTree(conf, n.n, 2, maxRecDepth-1, renderType=true))
-      add(result, ")")
-
-proc debugTree(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int;
-               renderType=false): Rope =
-  if n == nil:
-    result = rope("null")
+    if this.useColor:
+      this.res.add backrefStyle
+    this.res.add "<defined "
+    this.res.addInt(this.currentLine - index)
+    this.res.add " lines upwards>"
+    if this.useColor:
+      this.res.add resetStyle
+    return
+
+proc value(this: var DebugPrinter; value: PType)
+proc value(this: var DebugPrinter; value: PNode)
+proc value(this: var DebugPrinter; value: PSym) =
+  earlyExit(this, value)
+
+  this.openCurly
+  this.key("kind")
+  this.value(value.kind)
+  this.key("name")
+  this.value(value.name.s)
+  this.key("id")
+  this.value(value.id)
+  if value.kind in {skField, skEnumField, skParam}:
+    this.key("position")
+    this.value(value.position)
+
+  if card(value.flags) > 0:
+    this.key("flags")
+    this.value(value.flags)
+
+  if this.renderSymType and value.typ != nil:
+    this.key "typ"
+    this.value(value.typ)
+
+  this.closeCurly
+
+proc value(this: var DebugPrinter; value: PType) =
+  earlyExit(this, value)
+
+  this.openCurly
+  this.key "kind"
+  this.value value.kind
+
+  this.key "id"
+  this.value value.id
+
+  if value.sym != nil:
+    this.key "sym"
+    this.value value.sym
+    #this.value value.sym.name.s
+
+  if card(value.flags) > 0:
+    this.key "flags"
+    this.value value.flags
+
+  if value.kind in IntegralTypes and value.n != nil:
+    this.key "n"
+    this.value value.n
+
+  this.key "sons"
+  this.openBracket
+  for i, a in value.ikids:
+    if i > 0: this.comma
+    this.value a
+  this.closeBracket
+
+  if value.n != nil:
+    this.key "n"
+    this.value value.n
+
+  this.closeCurly
+
+proc value(this: var DebugPrinter; value: PNode) =
+  earlyExit(this, value)
+
+  this.openCurly
+  this.key "kind"
+  this.value  value.kind
+  if value.comment.len > 0:
+    this.key "comment"
+    this.value  value.comment
+  when defined(useNodeIds):
+    this.key "id"
+    this.value value.id
+  if this.conf != nil:
+    this.key "info"
+    this.value $lineInfoToStr(this.conf, value.info)
+  if value.flags != {}:
+    this.key "flags"
+    this.value value.flags
+
+  if value.typ != nil:
+    this.key "typ"
+    this.value value.typ.kind
   else:
-    var istr = rspaces(indent + 2)
-    result = "{$N$1\"kind\": $2" %
-             [istr, makeYamlString($n.kind)]
-    when defined(useNodeIds):
-      addf(result, ",$N$1\"id\": $2", [istr, rope(n.id)])
-    addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)])
-    if maxRecDepth != 0:
-      addf(result, ",$N$1\"flags\": $2", [istr, rope($n.flags)])
-      case n.kind
-      of nkCharLit..nkUInt64Lit:
-        addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)])
-      of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
-        addf(result, ",$N$1\"floatVal\": $2",
-            [istr, rope(n.floatVal.toStrMaxPrecision)])
-      of nkStrLit..nkTripleStrLit:
-        if n.strVal.isNil:
-          addf(result, ",$N$1\"strVal\": null", [istr])
-        else:
-          addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
-      of nkSym:
-        addf(result, ",$N$1\"sym\": $2_$3",
-            [istr, rope(n.sym.name.s), rope(n.sym.id)])
-        #     [istr, symToYaml(n.sym, indent, maxRecDepth),
-        #     rope(n.sym.id)])
-        if renderType and n.sym.typ != nil:
-          addf(result, ",$N$1\"typ\": $2", [istr, debugType(conf, n.sym.typ, 2)])
-      of nkIdent:
-        if n.ident != nil:
-          addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
-        else:
-          addf(result, ",$N$1\"ident\": null", [istr])
-      else:
-        if sonsLen(n) > 0:
-          addf(result, ",$N$1\"sons\": [", [istr])
-          for i in countup(0, sonsLen(n) - 1):
-            if i > 0: add(result, ",")
-            addf(result, "$N$1$2", [rspaces(indent + 4), debugTree(conf, n.sons[i],
-                indent + 4, maxRecDepth - 1, renderType)])
-          addf(result, "$N$1]", [istr])
-    addf(result, "$N$1}", [rspaces(indent)])
-
-when declared(echo):
-  proc debug(conf: ConfigRef; n: PSym) =
-    if n == nil:
-      echo("null")
-    elif n.kind == skUnknown:
-      echo("skUnknown")
-    else:
-      #writeLine(stdout, $symToYaml(n, 0, 1))
-      echo("$1_$2: $3, $4, $5, $6" % [
-        n.name.s, $n.id, $flagsToStr(n.flags), $flagsToStr(n.loc.flags),
-        $lineInfoToStr(conf, n.info), $n.kind])
-
-  proc debug(conf: ConfigRef; n: PType) =
-    echo($debugType(conf, n))
-
-  proc debug(conf: ConfigRef; n: PNode) =
-    echo($debugTree(conf, n, 0, 100))
+    this.key "typ"
+    this.value "nil"
 
-proc nextTry(h, maxHash: Hash): Hash =
+  case value.kind
+  of nkCharLit..nkUInt64Lit:
+    this.key "intVal"
+    this.value value.intVal
+  of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
+    this.key "floatVal"
+    this.value value.floatVal.toStrMaxPrecision
+  of nkStrLit..nkTripleStrLit:
+    this.key "strVal"
+    this.value value.strVal
+  of nkSym:
+    this.key "sym"
+    this.value value.sym
+    #this.value value.sym.name.s
+  of nkIdent:
+    if value.ident != nil:
+      this.key "ident"
+      this.value value.ident.s
+  else:
+    if this.renderSymType and value.typ != nil:
+      this.key "typ"
+      this.value value.typ
+    if value.len > 0:
+      this.key "sons"
+      this.openBracket
+      for i in 0..<value.len:
+        this.value value[i]
+        if i != value.len - 1:
+          this.comma
+      this.closeBracket
+
+  this.closeCurly
+
+
+proc debug(n: PSym; conf: ConfigRef) =
+  var this = DebugPrinter(
+    visited: initTable[pointer, int](),
+    renderSymType: true,
+    useColor: not defined(windows)
+  )
+  this.value(n)
+  echo($this.res)
+
+proc debug(n: PType; conf: ConfigRef) =
+  var this = DebugPrinter(
+    visited: initTable[pointer, int](),
+    renderSymType: true,
+    useColor: not defined(windows)
+  )
+  this.value(n)
+  echo($this.res)
+
+proc debug(n: PNode; conf: ConfigRef) =
+  var this = DebugPrinter(
+    visited: initTable[pointer, int](),
+    renderSymType: false,
+    useColor: not defined(windows)
+  )
+  this.value(n)
+  echo($this.res)
+
+proc nextTry(h, maxHash: Hash): Hash {.inline.} =
   result = ((5 * h) + 1) and maxHash
   # For any initial h in range(maxHash), repeating that maxHash times
   # generates each int in range(maxHash) exactly once (see any text on
@@ -451,13 +509,13 @@ proc objectSetRawInsert(data: var TObjectSeq, obj: RootRef) =
 
 proc objectSetEnlarge(t: var TObjectSet) =
   var n: TObjectSeq
-  newSeq(n, len(t.data) * GrowthFactor)
-  for i in countup(0, high(t.data)):
+  newSeq(n, t.data.len * GrowthFactor)
+  for i in 0..high(t.data):
     if t.data[i] != nil: objectSetRawInsert(n, t.data[i])
   swap(t.data, n)
 
 proc objectSetIncl*(t: var TObjectSet, obj: RootRef) =
-  if mustRehash(len(t.data), t.counter): objectSetEnlarge(t)
+  if mustRehash(t.data.len, t.counter): objectSetEnlarge(t)
   objectSetRawInsert(t.data, obj)
   inc(t.counter)
 
@@ -470,7 +528,7 @@ proc objectSetContainsOrIncl*(t: var TObjectSet, obj: RootRef): bool =
     if it == obj:
       return true             # found it
     h = nextTry(h, high(t.data))
-  if mustRehash(len(t.data), t.counter):
+  if mustRehash(t.data.len, t.counter):
     objectSetEnlarge(t)
     objectSetRawInsert(t.data, obj)
   else:
@@ -487,35 +545,18 @@ proc strTableContains*(t: TStrTable, n: PSym): bool =
     h = nextTry(h, high(t.data))
   result = false
 
-proc strTableRawInsert(data: var TSymSeq, n: PSym) =
+proc strTableRawInsert(data: var seq[PSym], n: PSym) =
   var h: Hash = n.name.h and high(data)
-  if sfImmediate notin n.flags:
-    # fast path:
-    while data[h] != nil:
-      if data[h] == n:
-        # allowed for 'export' feature:
-        #InternalError(n.info, "StrTableRawInsert: " & n.name.s)
-        return
-      h = nextTry(h, high(data))
-    assert(data[h] == nil)
-    data[h] = n
-  else:
-    # slow path; we have to ensure immediate symbols are preferred for
-    # symbol lookups.
-    # consider the chain: foo (immediate), foo, bar, bar (immediate)
-    # then bar (immediate) gets replaced with foo (immediate) and the non
-    # immediate foo is picked! Thus we need to replace it with the first
-    # slot that has in fact the same identifier stored in it!
-    var favPos = -1
-    while data[h] != nil:
-      if data[h] == n: return
-      if favPos < 0 and data[h].name.id == n.name.id: favPos = h
-      h = nextTry(h, high(data))
-    assert(data[h] == nil)
-    data[h] = n
-    if favPos >= 0: swap data[h], data[favPos]
-
-proc symTabReplaceRaw(data: var TSymSeq, prevSym: PSym, newSym: PSym) =
+  while data[h] != nil:
+    if data[h] == n:
+      # allowed for 'export' feature:
+      #InternalError(n.info, "StrTableRawInsert: " & n.name.s)
+      return
+    h = nextTry(h, high(data))
+  assert(data[h] == nil)
+  data[h] = n
+
+proc symTabReplaceRaw(data: var seq[PSym], prevSym: PSym, newSym: PSym) =
   assert prevSym.name.h == newSym.name.h
   var h: Hash = prevSym.name.h and high(data)
   while data[h] != nil:
@@ -529,21 +570,22 @@ proc symTabReplace*(t: var TStrTable, prevSym: PSym, newSym: PSym) =
   symTabReplaceRaw(t.data, prevSym, newSym)
 
 proc strTableEnlarge(t: var TStrTable) =
-  var n: TSymSeq
-  newSeq(n, len(t.data) * GrowthFactor)
-  for i in countup(0, high(t.data)):
+  var n: seq[PSym]
+  newSeq(n, t.data.len * GrowthFactor)
+  for i in 0..high(t.data):
     if t.data[i] != nil: strTableRawInsert(n, t.data[i])
   swap(t.data, n)
 
 proc strTableAdd*(t: var TStrTable, n: PSym) =
-  if mustRehash(len(t.data), t.counter): strTableEnlarge(t)
+  if mustRehash(t.data.len, t.counter): strTableEnlarge(t)
   strTableRawInsert(t.data, n)
   inc(t.counter)
 
-proc strTableIncl*(t: var TStrTable, n: PSym; onConflictKeepOld=false): bool {.discardable.} =
-  # returns true if n is already in the string table:
-  # It is essential that `n` is written nevertheless!
-  # This way the newest redefinition is picked by the semantic analyses!
+proc strTableInclReportConflict*(t: var TStrTable, n: PSym;
+                                 onConflictKeepOld = false): PSym =
+  # if `t` has a conflicting symbol (same identifier as `n`), return it
+  # otherwise return `nil`. Incl `n` to `t` unless `onConflictKeepOld = true`
+  # and a conflict was found.
   assert n.name != nil
   var h: Hash = n.name.h and high(t.data)
   var replaceSlot = -1
@@ -555,21 +597,26 @@ proc strTableIncl*(t: var TStrTable, n: PSym; onConflictKeepOld=false): bool {.d
     # So it is possible the very same sym is added multiple
     # times to the symbol table which we allow here with the 'it == n' check.
     if it.name.id == n.name.id:
-      if it == n: return false
+      if it == n: return nil
       replaceSlot = h
     h = nextTry(h, high(t.data))
   if replaceSlot >= 0:
+    result = t.data[replaceSlot] # found it
     if not onConflictKeepOld:
       t.data[replaceSlot] = n # overwrite it with newer definition!
-    return true             # found it
-  elif mustRehash(len(t.data), t.counter):
+    return result # but return the old one
+  elif mustRehash(t.data.len, t.counter):
     strTableEnlarge(t)
     strTableRawInsert(t.data, n)
   else:
     assert(t.data[h] == nil)
     t.data[h] = n
   inc(t.counter)
-  result = false
+  result = nil
+
+proc strTableIncl*(t: var TStrTable, n: PSym;
+                   onConflictKeepOld = false): bool {.discardable.} =
+  result = strTableInclReportConflict(t, n, onConflictKeepOld) != nil
 
 proc strTableGet*(t: TStrTable, name: PIdent): PSym =
   var h: Hash = name.h and high(t.data)
@@ -586,16 +633,21 @@ type
     name*: PIdent
 
 proc nextIdentIter*(ti: var TIdentIter, tab: TStrTable): PSym =
+  # hot spots
   var h = ti.h and high(tab.data)
   var start = h
-  result = tab.data[h]
-  while result != nil:
-    if result.name.id == ti.name.id: break
+  var p {.cursor.} = tab.data[h]
+  while p != nil:
+    if p.name.id == ti.name.id: break
     h = nextTry(h, high(tab.data))
     if h == start:
-      result = nil
+      p = nil
       break
-    result = tab.data[h]
+    p = tab.data[h]
+  if p != nil:
+    result = p # increase the count
+  else:
+    result = nil
   ti.h = nextTry(h, high(tab.data))
 
 proc initIdentIter*(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym =
@@ -655,137 +707,16 @@ proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym =
     result = nextIter(ti, tab)
 
 iterator items*(tab: TStrTable): PSym =
-  var it: TTabIter
+  var it: TTabIter = default(TTabIter)
   var s = initTabIter(it, tab)
   while s != nil:
     yield s
     s = nextIter(it, tab)
 
-proc hasEmptySlot(data: TIdPairSeq): bool =
-  for h in countup(0, high(data)):
-    if data[h].key == nil:
-      return true
-  result = false
-
-proc idTableRawGet(t: TIdTable, key: int): int =
-  var h: Hash
-  h = key and high(t.data)    # start with real hash value
-  while t.data[h].key != nil:
-    if t.data[h].key.id == key:
-      return h
-    h = nextTry(h, high(t.data))
-  result = - 1
-
-proc idTableHasObjectAsKey(t: TIdTable, key: PIdObj): bool =
-  var index = idTableRawGet(t, key.id)
-  if index >= 0: result = t.data[index].key == key
-  else: result = false
-
-proc idTableGet(t: TIdTable, key: PIdObj): RootRef =
-  var index = idTableRawGet(t, key.id)
-  if index >= 0: result = t.data[index].val
-  else: result = nil
-
-proc idTableGet(t: TIdTable, key: int): RootRef =
-  var index = idTableRawGet(t, key)
-  if index >= 0: result = t.data[index].val
-  else: result = nil
-
-iterator pairs*(t: TIdTable): tuple[key: int, value: RootRef] =
-  for i in 0..high(t.data):
-    if t.data[i].key != nil:
-      yield (t.data[i].key.id, t.data[i].val)
-
-proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: RootRef) =
-  var h: Hash
-  h = key.id and high(data)
-  while data[h].key != nil:
-    assert(data[h].key.id != key.id)
-    h = nextTry(h, high(data))
-  assert(data[h].key == nil)
-  data[h].key = key
-  data[h].val = val
-
-proc idTablePut(t: var TIdTable, key: PIdObj, val: RootRef) =
-  var
-    index: int
-    n: TIdPairSeq
-  index = idTableRawGet(t, key.id)
-  if index >= 0:
-    assert(t.data[index].key != nil)
-    t.data[index].val = val
-  else:
-    if mustRehash(len(t.data), t.counter):
-      newSeq(n, len(t.data) * GrowthFactor)
-      for i in countup(0, high(t.data)):
-        if t.data[i].key != nil:
-          idTableRawInsert(n, t.data[i].key, t.data[i].val)
-      assert(hasEmptySlot(n))
-      swap(t.data, n)
-    idTableRawInsert(t.data, key, val)
-    inc(t.counter)
-
-iterator idTablePairs*(t: TIdTable): tuple[key: PIdObj, val: RootRef] =
-  for i in 0 .. high(t.data):
-    if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
-
-proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int =
-  var h: Hash
-  h = key.id and high(t.data) # start with real hash value
-  while t.data[h].key != nil:
-    if t.data[h].key.id == key.id:
-      return h
-    h = nextTry(h, high(t.data))
-  result = - 1
-
-proc idNodeTableGet(t: TIdNodeTable, key: PIdObj): PNode =
-  var index: int
-  index = idNodeTableRawGet(t, key)
-  if index >= 0: result = t.data[index].val
-  else: result = nil
-
-proc idNodeTableGetLazy*(t: TIdNodeTable, key: PIdObj): PNode =
-  if not isNil(t.data):
-    result = idNodeTableGet(t, key)
-
-proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) =
-  var h: Hash
-  h = key.id and high(data)
-  while data[h].key != nil:
-    assert(data[h].key.id != key.id)
-    h = nextTry(h, high(data))
-  assert(data[h].key == nil)
-  data[h].key = key
-  data[h].val = val
-
-proc idNodeTablePut(t: var TIdNodeTable, key: PIdObj, val: PNode) =
-  var index = idNodeTableRawGet(t, key)
-  if index >= 0:
-    assert(t.data[index].key != nil)
-    t.data[index].val = val
-  else:
-    if mustRehash(len(t.data), t.counter):
-      var n: TIdNodePairSeq
-      newSeq(n, len(t.data) * GrowthFactor)
-      for i in countup(0, high(t.data)):
-        if t.data[i].key != nil:
-          idNodeTableRawInsert(n, t.data[i].key, t.data[i].val)
-      swap(t.data, n)
-    idNodeTableRawInsert(t.data, key, val)
-    inc(t.counter)
-
-proc idNodeTablePutLazy*(t: var TIdNodeTable, key: PIdObj, val: PNode) =
-  if isNil(t.data): initIdNodeTable(t)
-  idNodeTablePut(t, key, val)
-
-iterator pairs*(t: TIdNodeTable): tuple[key: PIdObj, val: PNode] =
-  for i in 0 .. high(t.data):
-    if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
-
 proc initIITable(x: var TIITable) =
   x.counter = 0
   newSeq(x.data, StartSize)
-  for i in countup(0, StartSize - 1): x.data[i].key = InvalidKey
+  for i in 0..<StartSize: x.data[i].key = InvalidKey
 
 proc iiTableRawGet(t: TIITable, key: int): int =
   var h: Hash
@@ -816,13 +747,25 @@ proc iiTablePut(t: var TIITable, key, val: int) =
     assert(t.data[index].key != InvalidKey)
     t.data[index].val = val
   else:
-    if mustRehash(len(t.data), t.counter):
+    if mustRehash(t.data.len, t.counter):
       var n: TIIPairSeq
-      newSeq(n, len(t.data) * GrowthFactor)
-      for i in countup(0, high(n)): n[i].key = InvalidKey
-      for i in countup(0, high(t.data)):
+      newSeq(n, t.data.len * GrowthFactor)
+      for i in 0..high(n): n[i].key = InvalidKey
+      for i in 0..high(t.data):
         if t.data[i].key != InvalidKey:
           iiTableRawInsert(n, t.data[i].key, t.data[i].val)
       swap(t.data, n)
     iiTableRawInsert(t.data, key, val)
     inc(t.counter)
+
+proc listSymbolNames*(symbols: openArray[PSym]): string =
+  result = ""
+  for sym in symbols:
+    if result.len > 0:
+      result.add ", "
+    result.add sym.name.s
+
+proc isDiscriminantField*(n: PNode): bool =
+  if n.kind == nkCheckedFieldExpr: sfDiscriminant in n[0][1].sym.flags
+  elif n.kind == nkDotExpr: sfDiscriminant in n[1].sym.flags
+  else: false
diff --git a/compiler/astmsgs.nim b/compiler/astmsgs.nim
new file mode 100644
index 000000000..aeeff1fd0
--- /dev/null
+++ b/compiler/astmsgs.nim
@@ -0,0 +1,45 @@
+# this module avoids ast depending on msgs or vice versa
+import std/strutils
+import options, ast, msgs
+
+proc typSym*(t: PType): PSym =
+  result = t.sym
+  if result == nil and t.kind == tyGenericInst: # this might need to be refined
+    result = t.genericHead.sym
+
+proc addDeclaredLoc*(result: var string, conf: ConfigRef; sym: PSym) =
+  result.add " [$1 declared in $2]" % [sym.kind.toHumanStr, toFileLineCol(conf, sym.info)]
+
+proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; sym: PSym) =
+  if optDeclaredLocs in conf.globalOptions and sym != nil:
+    addDeclaredLoc(result, conf, sym)
+
+proc addDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
+  # xxx figure out how to resolve `tyGenericParam`, e.g. for
+  # proc fn[T](a: T, b: T) = discard
+  # fn(1.1, "a")
+  let typ = typ.skipTypes(abstractInst + {tyStatic, tySequence, tyArray, tySet, tyUserTypeClassInst, tyVar, tyRef, tyPtr} - {tyRange})
+  result.add " [$1" % typ.kind.toHumanStr
+  if typ.sym != nil:
+    result.add " declared in " & toFileLineCol(conf, typ.sym.info)
+  result.add "]"
+
+proc addTypeNodeDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
+  result.add " [$1" % typ.kind.toHumanStr
+  if typ.sym != nil:
+    result.add " declared in " & toFileLineCol(conf, typ.sym.info)
+  result.add "]"
+
+proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; typ: PType) =
+  if optDeclaredLocs in conf.globalOptions: addDeclaredLoc(result, conf, typ)
+
+template quoteExpr*(a: string): untyped =
+  ## can be used for quoting expressions in error msgs.
+  "'" & a & "'"
+
+proc genFieldDefect*(conf: ConfigRef, field: string, disc: PSym): string =
+  let obj = disc.owner.name.s # `types.typeToString` might be better, eg for generics
+  result = "field '$#' is not accessible for type '$#'" % [field, obj]
+  if optDeclaredLocs in conf.globalOptions:
+    result.add " [discriminant declared in $#]" % toFileLineCol(conf, disc.info)
+  result.add " using '$# = " % disc.name.s
diff --git a/compiler/astyaml.nim b/compiler/astyaml.nim
new file mode 100644
index 000000000..b0fa2bfb2
--- /dev/null
+++ b/compiler/astyaml.nim
@@ -0,0 +1,154 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# AST YAML printing
+
+import "."/[ast, lineinfos, msgs, options, rodutils]
+import std/[intsets, strutils]
+
+proc addYamlString*(res: var string; s: string) =
+  res.add "\""
+  for c in s:
+    case c
+    of '\0' .. '\x1F', '\x7F' .. '\xFF':
+      res.add("\\u" & strutils.toHex(ord(c), 4))
+    of '\"', '\\':
+      res.add '\\' & c
+    else:
+      res.add c
+
+  res.add('\"')
+
+proc makeYamlString(s: string): string =
+  result = ""
+  result.addYamlString(s)
+
+proc flagsToStr[T](flags: set[T]): string =
+  if flags == {}:
+    result = "[]"
+  else:
+    result = ""
+    for x in items(flags):
+      if result != "":
+        result.add(", ")
+      result.addYamlString($x)
+    result = "[" & result & "]"
+
+proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): string =
+  result = "["
+  result.addYamlString(toFilename(conf, info))
+  result.addf ", $1, $2]", [toLinenumber(info), toColumn(info)]
+
+proc treeToYamlAux(res: var string; conf: ConfigRef; n: PNode; marker: var IntSet; indent, maxRecDepth: int)
+proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet; indent, maxRecDepth: int)
+proc typeToYamlAux(res: var string; conf: ConfigRef; n: PType; marker: var IntSet; indent, maxRecDepth: int)
+
+proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet; indent: int; maxRecDepth: int) =
+  if n == nil:
+    res.add("null")
+  elif containsOrIncl(marker, n.id):
+    res.addYamlString(n.name.s)
+  else:
+    let istr = spaces(indent * 4)
+
+    res.addf("kind: $1", [makeYamlString($n.kind)])
+    res.addf("\n$1name: $2", [istr, makeYamlString(n.name.s)])
+    res.addf("\n$1typ: ", [istr])
+    res.typeToYamlAux(conf, n.typ, marker, indent + 1, maxRecDepth - 1)
+    if conf != nil:
+      # if we don't pass the config, we probably don't care about the line info
+      res.addf("\n$1info: $2", [istr, lineInfoToStr(conf, n.info)])
+    if card(n.flags) > 0:
+      res.addf("\n$1flags: $2", [istr, flagsToStr(n.flags)])
+    res.addf("\n$1magic: $2", [istr, makeYamlString($n.magic)])
+    res.addf("\n$1ast: ", [istr])
+    res.treeToYamlAux(conf, n.ast, marker, indent + 1, maxRecDepth - 1)
+    res.addf("\n$1options: $2", [istr, flagsToStr(n.options)])
+    res.addf("\n$1position: $2", [istr, $n.position])
+    res.addf("\n$1k: $2", [istr, makeYamlString($n.loc.k)])
+    res.addf("\n$1storage: $2", [istr, makeYamlString($n.loc.storage)])
+    if card(n.loc.flags) > 0:
+      res.addf("\n$1flags: $2", [istr, makeYamlString($n.loc.flags)])
+    res.addf("\n$1snippet: $2", [istr, n.loc.snippet])
+    res.addf("\n$1lode: $2", [istr])
+    res.treeToYamlAux(conf, n.loc.lode, marker, indent + 1, maxRecDepth - 1)
+
+proc typeToYamlAux(res: var string; conf: ConfigRef; n: PType; marker: var IntSet; indent: int; maxRecDepth: int) =
+  if n == nil:
+    res.add("null")
+  elif containsOrIncl(marker, n.id):
+    res.addf "\"$1 @$2\"" % [$n.kind, strutils.toHex(cast[uint](n), sizeof(n) * 2)]
+  else:
+    let istr = spaces(indent * 4)
+    res.addf("kind: $2", [istr, makeYamlString($n.kind)])
+    res.addf("\n$1sym: ")
+    res.symToYamlAux(conf, n.sym, marker, indent + 1, maxRecDepth - 1)
+    res.addf("\n$1n: ")
+    res.treeToYamlAux(conf, n.n, marker, indent + 1, maxRecDepth - 1)
+    if card(n.flags) > 0:
+      res.addf("\n$1flags: $2", [istr, flagsToStr(n.flags)])
+    res.addf("\n$1callconv: $2", [istr, makeYamlString($n.callConv)])
+    res.addf("\n$1size: $2", [istr, $(n.size)])
+    res.addf("\n$1align: $2", [istr, $(n.align)])
+    if n.hasElementType:
+      res.addf("\n$1sons:")
+      for a in n.kids:
+        res.addf("\n  - ")
+        res.typeToYamlAux(conf, a, marker, indent + 1, maxRecDepth - 1)
+
+proc treeToYamlAux(res: var string; conf: ConfigRef; n: PNode; marker: var IntSet; indent: int;
+                   maxRecDepth: int) =
+  if n == nil:
+    res.add("null")
+  else:
+    var istr = spaces(indent * 4)
+    res.addf("kind: $1" % [makeYamlString($n.kind)])
+
+    if maxRecDepth != 0:
+      if conf != nil:
+        res.addf("\n$1info: $2", [istr, lineInfoToStr(conf, n.info)])
+      case n.kind
+      of nkCharLit .. nkInt64Lit:
+        res.addf("\n$1intVal: $2", [istr, $(n.intVal)])
+      of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
+        res.addf("\n$1floatVal: $2", [istr, n.floatVal.toStrMaxPrecision])
+      of nkStrLit .. nkTripleStrLit:
+        res.addf("\n$1strVal: $2", [istr, makeYamlString(n.strVal)])
+      of nkSym:
+        res.addf("\n$1sym: ", [istr])
+        res.symToYamlAux(conf, n.sym, marker, indent + 1, maxRecDepth)
+      of nkIdent:
+        if n.ident != nil:
+          res.addf("\n$1ident: $2", [istr, makeYamlString(n.ident.s)])
+        else:
+          res.addf("\n$1ident: null", [istr])
+      else:
+        if n.len > 0:
+          res.addf("\n$1sons: ", [istr])
+          for i in 0 ..< n.len:
+            res.addf("\n$1  - ", [istr])
+            res.treeToYamlAux(conf, n[i], marker, indent + 1, maxRecDepth - 1)
+      if n.typ != nil:
+        res.addf("\n$1typ: ", [istr])
+        res.typeToYamlAux(conf, n.typ, marker, indent + 1, maxRecDepth)
+
+proc treeToYaml*(conf: ConfigRef; n: PNode; indent: int = 0; maxRecDepth: int = -1): string =
+  var marker = initIntSet()
+  result = newStringOfCap(1024)
+  result.treeToYamlAux(conf, n, marker, indent, maxRecDepth)
+
+proc typeToYaml*(conf: ConfigRef; n: PType; indent: int = 0; maxRecDepth: int = -1): string =
+  var marker = initIntSet()
+  result = newStringOfCap(1024)
+  result.typeToYamlAux(conf, n, marker, indent, maxRecDepth)
+
+proc symToYaml*(conf: ConfigRef; n: PSym; indent: int = 0; maxRecDepth: int = -1): string =
+  var marker = initIntSet()
+  result = newStringOfCap(1024)
+  result.symToYamlAux(conf, n, marker, indent, maxRecDepth)
diff --git a/compiler/bitsets.nim b/compiler/bitsets.nim
index e38732877..7d142b01d 100644
--- a/compiler/bitsets.nim
+++ b/compiler/bitsets.nim
@@ -10,88 +10,88 @@
 # this unit handles Nim sets; it implements bit sets
 # the code here should be reused in the Nim standard library
 
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 type
-  TBitSet* = seq[int8]        # we use byte here to avoid issues with
+  ElemType = byte
+  TBitSet* = seq[ElemType]    # we use byte here to avoid issues with
                               # cross-compiling; uint would be more efficient
                               # however
-
 const
-  ElemSize* = sizeof(int8) * 8
-
-proc bitSetInit*(b: var TBitSet, length: int)
-proc bitSetUnion*(x: var TBitSet, y: TBitSet)
-proc bitSetDiff*(x: var TBitSet, y: TBitSet)
-proc bitSetSymDiff*(x: var TBitSet, y: TBitSet)
-proc bitSetIntersect*(x: var TBitSet, y: TBitSet)
-proc bitSetIncl*(x: var TBitSet, elem: BiggestInt)
-proc bitSetExcl*(x: var TBitSet, elem: BiggestInt)
-proc bitSetIn*(x: TBitSet, e: BiggestInt): bool
-proc bitSetEquals*(x, y: TBitSet): bool
-proc bitSetContains*(x, y: TBitSet): bool
-proc bitSetCard*(x: TBitSet): BiggestInt
-# implementation
-
-proc bitSetIn(x: TBitSet, e: BiggestInt): bool =
-  result = (x[int(e div ElemSize)] and toU8(int(1 shl (e mod ElemSize)))) !=
-      toU8(0)
-
-proc bitSetIncl(x: var TBitSet, elem: BiggestInt) =
+  ElemSize* = 8
+  One = ElemType(1)
+  Zero = ElemType(0)
+
+template modElemSize(arg: untyped): untyped = arg and 7
+template divElemSize(arg: untyped): untyped = arg shr 3
+
+proc bitSetIn*(x: TBitSet, e: BiggestInt): bool =
+  result = (x[int(e.divElemSize)] and (One shl e.modElemSize)) != Zero
+
+proc bitSetIncl*(x: var TBitSet, elem: BiggestInt) =
   assert(elem >= 0)
-  x[int(elem div ElemSize)] = x[int(elem div ElemSize)] or
-      toU8(int(1 shl (elem mod ElemSize)))
+  x[int(elem.divElemSize)] = x[int(elem.divElemSize)] or
+      (One shl elem.modElemSize)
 
-proc bitSetExcl(x: var TBitSet, elem: BiggestInt) =
-  x[int(elem div ElemSize)] = x[int(elem div ElemSize)] and
-      not toU8(int(1 shl (elem mod ElemSize)))
+proc bitSetExcl*(x: var TBitSet, elem: BiggestInt) =
+  x[int(elem.divElemSize)] = x[int(elem.divElemSize)] and
+      not(One shl elem.modElemSize)
 
-proc bitSetInit(b: var TBitSet, length: int) =
+proc bitSetInit*(b: var TBitSet, length: int) =
   newSeq(b, length)
 
-proc bitSetUnion(x: var TBitSet, y: TBitSet) =
-  for i in countup(0, high(x)): x[i] = x[i] or y[i]
+proc bitSetUnion*(x: var TBitSet, y: TBitSet) =
+  for i in 0..high(x): x[i] = x[i] or y[i]
 
-proc bitSetDiff(x: var TBitSet, y: TBitSet) =
-  for i in countup(0, high(x)): x[i] = x[i] and not y[i]
+proc bitSetDiff*(x: var TBitSet, y: TBitSet) =
+  for i in 0..high(x): x[i] = x[i] and not y[i]
 
-proc bitSetSymDiff(x: var TBitSet, y: TBitSet) =
-  for i in countup(0, high(x)): x[i] = x[i] xor y[i]
+proc bitSetSymDiff*(x: var TBitSet, y: TBitSet) =
+  for i in 0..high(x): x[i] = x[i] xor y[i]
 
-proc bitSetIntersect(x: var TBitSet, y: TBitSet) =
-  for i in countup(0, high(x)): x[i] = x[i] and y[i]
+proc bitSetIntersect*(x: var TBitSet, y: TBitSet) =
+  for i in 0..high(x): x[i] = x[i] and y[i]
 
-proc bitSetEquals(x, y: TBitSet): bool =
-  for i in countup(0, high(x)):
+proc bitSetEquals*(x, y: TBitSet): bool =
+  for i in 0..high(x):
     if x[i] != y[i]:
       return false
   result = true
 
-proc bitSetContains(x, y: TBitSet): bool =
-  for i in countup(0, high(x)):
-    if (x[i] and not y[i]) != int8(0):
+proc bitSetContains*(x, y: TBitSet): bool =
+  for i in 0..high(x):
+    if (x[i] and not y[i]) != Zero:
       return false
   result = true
 
 # Number of set bits for all values of int8
-const populationCount: array[low(int8)..high(int8), int8] = block:
-    var arr: array[low(int8)..high(int8), int8]
+const populationCount: array[uint8, uint8] = block:
+    var arr: array[uint8, uint8]
 
-    proc countSetBits(x: int8): int8 =
+    proc countSetBits(x: uint8): uint8 =
       return
-        ( x and 0b00000001'i8) +
-        ((x and 0b00000010'i8) shr 1) +
-        ((x and 0b00000100'i8) shr 2) +
-        ((x and 0b00001000'i8) shr 3) +
-        ((x and 0b00010000'i8) shr 4) +
-        ((x and 0b00100000'i8) shr 5) +
-        ((x and 0b01000000'i8) shr 6) +
-        ((x and 0b10000000'i8) shr 7)
-        
-
-    for it in low(int8)..high(int8):
-      arr[it] = countSetBits(it)
+        ( x and 0b00000001'u8) +
+        ((x and 0b00000010'u8) shr 1) +
+        ((x and 0b00000100'u8) shr 2) +
+        ((x and 0b00001000'u8) shr 3) +
+        ((x and 0b00010000'u8) shr 4) +
+        ((x and 0b00100000'u8) shr 5) +
+        ((x and 0b01000000'u8) shr 6) +
+        ((x and 0b10000000'u8) shr 7)
+
+
+    for it in low(uint8)..high(uint8):
+      arr[it] = countSetBits(cast[uint8](it))
 
     arr
 
-proc bitSetCard(x: TBitSet): BiggestInt =
+proc bitSetCard*(x: TBitSet): BiggestInt =
+  result = 0
   for it in x:
-    result.inc populationCount[it]
+    result.inc int(populationCount[it])
+
+proc bitSetToWord*(s: TBitSet; size: int): BiggestUInt =
+  result = 0
+  for j in 0..<size:
+    if j < s.len: result = result or (BiggestUInt(s[j]) shl (j * 8))
diff --git a/compiler/btrees.nim b/compiler/btrees.nim
index 6cd6e51f4..3b737b1bc 100644
--- a/compiler/btrees.nim
+++ b/compiler/btrees.nim
@@ -10,13 +10,16 @@
 ## BTree implementation with few features, but good enough for the
 ## Nim compiler's needs.
 
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 const
   M = 512    # max children per B-tree node = M-1
              # (must be even and greater than 2)
   Mhalf = M div 2
 
 type
-  Node[Key, Val] = ref object
+  Node[Key, Val] {.acyclic.} = ref object
     entries: int
     keys: array[M, Key]
     case isInternal: bool
@@ -35,37 +38,41 @@ template less(a, b): bool = cmp(a, b) < 0
 template eq(a, b): bool = cmp(a, b) == 0
 
 proc getOrDefault*[Key, Val](b: BTree[Key, Val], key: Key): Val =
+  result = default(Val)
   var x = b.root
   while x.isInternal:
-    for j in 0 ..< x.entries:
+    for j in 0..<x.entries:
       if j+1 == x.entries or less(key, x.keys[j+1]):
         x = x.links[j]
         break
   assert(not x.isInternal)
-  for j in 0 ..< x.entries:
+  for j in 0..<x.entries:
     if eq(key, x.keys[j]): return x.vals[j]
 
 proc contains*[Key, Val](b: BTree[Key, Val], key: Key): bool =
   var x = b.root
   while x.isInternal:
-    for j in 0 ..< x.entries:
+    for j in 0..<x.entries:
       if j+1 == x.entries or less(key, x.keys[j+1]):
         x = x.links[j]
         break
   assert(not x.isInternal)
-  for j in 0 ..< x.entries:
+  for j in 0..<x.entries:
     if eq(key, x.keys[j]): return true
   return false
 
 proc copyHalf[Key, Val](h, result: Node[Key, Val]) =
-  for j in 0 ..< Mhalf:
+  for j in 0..<Mhalf:
     result.keys[j] = h.keys[Mhalf + j]
   if h.isInternal:
-    for j in 0 ..< Mhalf:
+    for j in 0..<Mhalf:
       result.links[j] = h.links[Mhalf + j]
   else:
-    for j in 0 ..< Mhalf:
-      shallowCopy(result.vals[j], h.vals[Mhalf + j])
+    for j in 0..<Mhalf:
+      when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
+        result.vals[j] = move h.vals[Mhalf + j]
+      else:
+        shallowCopy(result.vals[j], h.vals[Mhalf + j])
 
 proc split[Key, Val](h: Node[Key, Val]): Node[Key, Val] =
   ## split node in half
@@ -79,10 +86,16 @@ proc insert[Key, Val](h: Node[Key, Val], key: Key, val: Val): Node[Key, Val] =
   var j = 0
   if not h.isInternal:
     while j < h.entries:
+      if eq(key, h.keys[j]):
+        h.vals[j] = val
+        return
       if less(key, h.keys[j]): break
       inc j
     for i in countdown(h.entries, j+1):
-      shallowCopy(h.vals[i], h.vals[i-1])
+      when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
+        h.vals[i] = move h.vals[i-1]
+      else:
+        shallowCopy(h.vals[i], h.vals[i-1])
     h.vals[j] = val
   else:
     var newLink: Node[Key, Val] = nil
@@ -132,8 +145,7 @@ proc `$`[Key, Val](b: BTree[Key, Val]): string =
   result = ""
   toString(b.root, "", result)
 
-proc hasNext*[Key, Val](b: BTree[Key, Val]; index: int): bool =
-  result = index < b.entries
+proc hasNext*[Key, Val](b: BTree[Key, Val]; index: int): bool = index < b.entries
 
 proc countSubTree[Key, Val](it: Node[Key, Val]): int =
   if it.isInternal:
@@ -166,68 +178,3 @@ iterator pairs*[Key, Val](b: BTree[Key, Val]): (Key, Val) =
     yield (k, v)
 
 proc len*[Key, Val](b: BTree[Key, Val]): int {.inline.} = b.entries
-
-when isMainModule:
-
-  import random, tables
-
-  proc main =
-    var st = initBTree[string, string]()
-    st.add("www.cs.princeton.edu", "abc")
-    st.add("www.princeton.edu",    "128.112.128.15")
-    st.add("www.yale.edu",         "130.132.143.21")
-    st.add("www.simpsons.com",     "209.052.165.60")
-    st.add("www.apple.com",        "17.112.152.32")
-    st.add("www.amazon.com",       "207.171.182.16")
-    st.add("www.ebay.com",         "66.135.192.87")
-    st.add("www.cnn.com",          "64.236.16.20")
-    st.add("www.google.com",       "216.239.41.99")
-    st.add("www.nytimes.com",      "199.239.136.200")
-    st.add("www.microsoft.com",    "207.126.99.140")
-    st.add("www.dell.com",         "143.166.224.230")
-    st.add("www.slashdot.org",     "66.35.250.151")
-    st.add("www.espn.com",         "199.181.135.201")
-    st.add("www.weather.com",      "63.111.66.11")
-    st.add("www.yahoo.com",        "216.109.118.65")
-
-    assert st.getOrDefault("www.cs.princeton.edu") == "abc"
-    assert st.getOrDefault("www.harvardsucks.com") == nil
-
-    assert st.getOrDefault("www.simpsons.com") == "209.052.165.60"
-    assert st.getOrDefault("www.apple.com") == "17.112.152.32"
-    assert st.getOrDefault("www.ebay.com") == "66.135.192.87"
-    assert st.getOrDefault("www.dell.com") == "143.166.224.230"
-    assert(st.entries == 16)
-
-    for k, v in st:
-      echo k, ": ", v
-
-    when false:
-      var b2 = initBTree[string, string]()
-      const iters = 10_000
-      for i in 1..iters:
-        b2.add($i, $(iters - i))
-      for i in 1..iters:
-        let x = b2.getOrDefault($i)
-        if x != $(iters - i):
-          echo "got ", x, ", but expected ", iters - i
-      echo b2.entries
-
-    when true:
-      var b2 = initBTree[int, string]()
-      var t2 = initTable[int, string]()
-      const iters = 100_000
-      for i in 1..iters:
-        let x = rand(high(int))
-        if not t2.hasKey(x):
-          doAssert b2.getOrDefault(x).len == 0, " what, tree has this element " & $x
-          t2[x] = $x
-          b2.add(x, $x)
-
-      doAssert b2.entries == t2.len
-      echo "unique entries ", b2.entries
-      for k, v in t2:
-        doAssert $k == v
-        doAssert b2.getOrDefault(k) == $k
-
-  main()
diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer.nim
deleted file mode 100644
index d1669a06c..000000000
--- a/compiler/canonicalizer.nim
+++ /dev/null
@@ -1,421 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module implements the canonalization for the various caching mechanisms.
-
-import strutils, db_sqlite, md5
-
-var db: DbConn
-
-# We *hash* the relevant information into 128 bit hashes. This should be good
-# enough to prevent any collisions.
-
-type
-  TUid = distinct MD5Digest
-
-# For name mangling we encode these hashes via a variant of base64 (called
-# 'base64a') and prepend the *primary* identifier to ease the debugging pain.
-# So a signature like:
-#
-#   proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt)
-#
-# is mangled into:
-#   gABI_MTdmOWY5MTQ1MDcyNGQ3ZA
-#
-# This is a good compromise between correctness and brevity. ;-)
-
-const
-  cb64 = [
-    "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",
-    "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
-    "_A", "_B"]
-
-proc toBase64a(s: cstring, len: int): string =
-  ## encodes `s` into base64 representation. After `lineLen` characters, a
-  ## `newline` is added.
-  result = newStringOfCap(((len + 2) div 3) * 4)
-  var i = 0
-  while i < s.len - 2:
-    let a = ord(s[i])
-    let b = ord(s[i+1])
-    let c = ord(s[i+2])
-    result.add cb64[a shr 2]
-    result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
-    result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
-    result.add cb64[c and 0x3F]
-    inc(i, 3)
-  if i < s.len-1:
-    let a = ord(s[i])
-    let b = ord(s[i+1])
-    result.add cb64[a shr 2]
-    result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
-    result.add cb64[((b and 0x0F) shl 2)]
-  elif i < s.len:
-    let a = ord(s[i])
-    result.add cb64[a shr 2]
-    result.add cb64[(a and 3) shl 4]
-
-proc toBase64a(u: TUid): string = toBase64a(cast[cstring](u), sizeof(u))
-
-proc `&=`(c: var MD5Context, s: string) = md5Update(c, s, s.len)
-
-proc hashSym(c: var MD5Context, s: PSym) =
-  if sfAnon in s.flags or s.kind == skGenericParam:
-    c &= ":anon"
-  else:
-    var it = s.owner
-    while it != nil:
-      hashSym(c, it)
-      c &= "."
-      it = s.owner
-    c &= s.name.s
-
-proc hashTree(c: var MD5Context, n: PNode) =
-  if n == nil:
-    c &= "\255"
-    return
-  var k = n.kind
-  md5Update(c, cast[cstring](addr(k)), 1)
-  # we really must not hash line information. 'n.typ' is debatable but
-  # shouldn't be necessary for now and avoids potential infinite recursions.
-  case n.kind
-  of nkEmpty, nkNilLit, nkType: discard
-  of nkIdent:
-    c &= n.ident.s
-  of nkSym:
-    hashSym(c, n.sym)
-  of nkCharLit..nkUInt64Lit:
-    var v = n.intVal
-    md5Update(c, cast[cstring](addr(v)), sizeof(v))
-  of nkFloatLit..nkFloat64Lit:
-    var v = n.floatVal
-    md5Update(c, cast[cstring](addr(v)), sizeof(v))
-  of nkStrLit..nkTripleStrLit:
-    c &= n.strVal
-  else:
-    for i in 0..<n.len: hashTree(c, n.sons[i])
-
-proc hashType(c: var MD5Context, t: PType) =
-  # modelled after 'typeToString'
-  if t == nil:
-    c &= "\254"
-    return
-
-  var k = t.kind
-  md5Update(c, cast[cstring](addr(k)), 1)
-
-  if t.sym != nil and sfAnon notin t.sym.flags:
-    # t.n for literals, but not for e.g. objects!
-    if t.kind in {tyFloat, tyInt}: c.hashNode(t.n)
-    c.hashSym(t.sym)
-
-  case t.kind
-  of tyGenericBody, tyGenericInst, tyGenericInvocation:
-    for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvocation)):
-      c.hashType t.sons[i]
-  of tyUserTypeClass:
-    internalAssert t.sym != nil and t.sym.owner != nil
-    c &= t.sym.owner.name.s
-  of tyUserTypeClassInst:
-    let body = t.base
-    c.hashSym body.sym
-    for i in countup(1, sonsLen(t) - 2):
-      c.hashType t.sons[i]
-  of tyFromExpr:
-    c.hashTree(t.n)
-  of tyArray:
-    c.hashTree(t.sons[0].n)
-    c.hashType(t.sons[1])
-  of tyTuple:
-    if t.n != nil:
-      assert(sonsLen(t.n) == sonsLen(t))
-      for i in countup(0, sonsLen(t.n) - 1):
-        assert(t.n.sons[i].kind == nkSym)
-        c &= t.n.sons[i].sym.name.s
-        c &= ":"
-        c.hashType(t.sons[i])
-        c &= ","
-    else:
-      for i in countup(0, sonsLen(t) - 1): c.hashType t.sons[i]
-  of tyRange:
-    c.hashTree(t.n)
-    c.hashType(t.sons[0])
-  of tyProc:
-    c &= (if tfIterator in t.flags: "iterator " else: "proc ")
-    for i in 0..<t.len: c.hashType(t.sons[i])
-    md5Update(c, cast[cstring](addr(t.callConv)), 1)
-
-    if tfNoSideEffect in t.flags: c &= ".noSideEffect"
-    if tfThread in t.flags: c &= ".thread"
-  else:
-    for i in 0..<t.len: c.hashType(t.sons[i])
-  if tfNotNil in t.flags: c &= "not nil"
-
-proc canonConst(n: PNode): TUid =
-  var c: MD5Context
-  md5Init(c)
-  c.hashTree(n)
-  c.hashType(n.typ)
-  md5Final(c, MD5Digest(result))
-
-proc canonSym(s: PSym): TUid =
-  var c: MD5Context
-  md5Init(c)
-  c.hashSym(s)
-  md5Final(c, MD5Digest(result))
-
-proc pushType(w: PRodWriter, t: PType) =
-  # check so that the stack does not grow too large:
-  if iiTableGet(w.index.tab, t.id) == InvalidKey:
-    w.tstack.add(t)
-
-proc pushSym(w: PRodWriter, s: PSym) =
-  # check so that the stack does not grow too large:
-  if iiTableGet(w.index.tab, s.id) == InvalidKey:
-    w.sstack.add(s)
-
-proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
-                result: var string) =
-  if n == nil:
-    # nil nodes have to be stored too:
-    result.add("()")
-    return
-  result.add('(')
-  encodeVInt(ord(n.kind), result)
-  # we do not write comments for now
-  # Line information takes easily 20% or more of the filesize! Therefore we
-  # omit line information if it is the same as the father's line information:
-  if fInfo.fileIndex != n.info.fileIndex:
-    result.add('?')
-    encodeVInt(n.info.col, result)
-    result.add(',')
-    encodeVInt(n.info.line, result)
-    result.add(',')
-    encodeVInt(fileIdx(w, toFilename(n.info)), result)
-  elif fInfo.line != n.info.line:
-    result.add('?')
-    encodeVInt(n.info.col, result)
-    result.add(',')
-    encodeVInt(n.info.line, result)
-  elif fInfo.col != n.info.col:
-    result.add('?')
-    encodeVInt(n.info.col, result)
-  var f = n.flags * PersistentNodeFlags
-  if f != {}:
-    result.add('$')
-    encodeVInt(cast[int32](f), result)
-  if n.typ != nil:
-    result.add('^')
-    encodeVInt(n.typ.id, result)
-    pushType(w, n.typ)
-  case n.kind
-  of nkCharLit..nkInt64Lit:
-    if n.intVal != 0:
-      result.add('!')
-      encodeVBiggestInt(n.intVal, result)
-  of nkFloatLit..nkFloat64Lit:
-    if n.floatVal != 0.0:
-      result.add('!')
-      encodeStr($n.floatVal, result)
-  of nkStrLit..nkTripleStrLit:
-    if n.strVal != "":
-      result.add('!')
-      encodeStr(n.strVal, result)
-  of nkIdent:
-    result.add('!')
-    encodeStr(n.ident.s, result)
-  of nkSym:
-    result.add('!')
-    encodeVInt(n.sym.id, result)
-    pushSym(w, n.sym)
-  else:
-    for i in countup(0, sonsLen(n) - 1):
-      encodeNode(w, n.info, n.sons[i], result)
-  add(result, ')')
-
-proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
-  var oldLen = result.len
-  result.add('<')
-  if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
-  if loc.s != low(loc.s):
-    add(result, '*')
-    encodeVInt(ord(loc.s), result)
-  if loc.flags != {}:
-    add(result, '$')
-    encodeVInt(cast[int32](loc.flags), result)
-  if loc.t != nil:
-    add(result, '^')
-    encodeVInt(cast[int32](loc.t.id), result)
-    pushType(w, loc.t)
-  if loc.r != nil:
-    add(result, '!')
-    encodeStr($loc.r, result)
-  if loc.a != 0:
-    add(result, '?')
-    encodeVInt(loc.a, result)
-  if oldLen + 1 == result.len:
-    # no data was necessary, so remove the '<' again:
-    setLen(result, oldLen)
-  else:
-    add(result, '>')
-
-proc encodeType(w: PRodWriter, t: PType, result: var string) =
-  if t == nil:
-    # nil nodes have to be stored too:
-    result.add("[]")
-    return
-  # we need no surrounding [] here because the type is in a line of its own
-  if t.kind == tyForward: internalError("encodeType: tyForward")
-  # for the new rodfile viewer we use a preceding [ so that the data section
-  # can easily be disambiguated:
-  add(result, '[')
-  encodeVInt(ord(t.kind), result)
-  add(result, '+')
-  encodeVInt(t.id, result)
-  if t.n != nil:
-    encodeNode(w, unknownLineInfo(), t.n, result)
-  if t.flags != {}:
-    add(result, '$')
-    encodeVInt(cast[int32](t.flags), result)
-  if t.callConv != low(t.callConv):
-    add(result, '?')
-    encodeVInt(ord(t.callConv), result)
-  if t.owner != nil:
-    add(result, '*')
-    encodeVInt(t.owner.id, result)
-    pushSym(w, t.owner)
-  if t.sym != nil:
-    add(result, '&')
-    encodeVInt(t.sym.id, result)
-    pushSym(w, t.sym)
-  if t.size != - 1:
-    add(result, '/')
-    encodeVBiggestInt(t.size, result)
-  if t.align != 2:
-    add(result, '=')
-    encodeVInt(t.align, result)
-  encodeLoc(w, t.loc, result)
-  for i in countup(0, sonsLen(t) - 1):
-    if t.sons[i] == nil:
-      add(result, "^()")
-    else:
-      add(result, '^')
-      encodeVInt(t.sons[i].id, result)
-      pushType(w, t.sons[i])
-
-proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
-  add(result, '|')
-  encodeVInt(ord(lib.kind), result)
-  add(result, '|')
-  encodeStr($lib.name, result)
-  add(result, '|')
-  encodeNode(w, info, lib.path, result)
-
-proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
-  if s == nil:
-    # nil nodes have to be stored too:
-    result.add("{}")
-    return
-  # we need no surrounding {} here because the symbol is in a line of its own
-  encodeVInt(ord(s.kind), result)
-  result.add('+')
-  encodeVInt(s.id, result)
-  result.add('&')
-  encodeStr(s.name.s, result)
-  if s.typ != nil:
-    result.add('^')
-    encodeVInt(s.typ.id, result)
-    pushType(w, s.typ)
-  result.add('?')
-  if s.info.col != -1'i16: encodeVInt(s.info.col, result)
-  result.add(',')
-  if s.info.line != -1'i16: encodeVInt(s.info.line, result)
-  result.add(',')
-  encodeVInt(fileIdx(w, toFilename(s.info)), result)
-  if s.owner != nil:
-    result.add('*')
-    encodeVInt(s.owner.id, result)
-    pushSym(w, s.owner)
-  if s.flags != {}:
-    result.add('$')
-    encodeVInt(cast[int32](s.flags), result)
-  if s.magic != mNone:
-    result.add('@')
-    encodeVInt(ord(s.magic), result)
-  if s.options != w.options:
-    result.add('!')
-    encodeVInt(cast[int32](s.options), result)
-  if s.position != 0:
-    result.add('%')
-    encodeVInt(s.position, result)
-  if s.offset != - 1:
-    result.add('`')
-    encodeVInt(s.offset, result)
-  encodeLoc(w, s.loc, result)
-  if s.annex != nil: encodeLib(w, s.annex, s.info, result)
-  if s.constraint != nil:
-    add(result, '#')
-    encodeNode(w, unknownLineInfo(), s.constraint, result)
-  # lazy loading will soon reload the ast lazily, so the ast needs to be
-  # the last entry of a symbol:
-  if s.ast != nil:
-    # we used to attempt to save space here by only storing a dummy AST if
-    # it is not necessary, but Nim's heavy compile-time evaluation features
-    # make that unfeasible nowadays:
-    encodeNode(w, s.info, s.ast, result)
-
-
-proc createDb() =
-  db.exec(sql"""
-    create table if not exists Module(
-      id integer primary key,
-      name varchar(256) not null,
-      fullpath varchar(256) not null,
-      interfHash varchar(256) not null,
-      fullHash varchar(256) not null,
-
-      created timestamp not null default (DATETIME('now'))
-    );""")
-
-  db.exec(sql"""
-    create table if not exists Backend(
-      id integer primary key,
-      strongdeps varchar(max) not null,
-      weakdeps varchar(max) not null,
-      header varchar(max) not null,
-      code varchar(max) not null
-    )
-
-    create table if not exists Symbol(
-      id integer primary key,
-      module integer not null,
-      backend integer not null,
-      name varchar(max) not null,
-      data varchar(max) not null,
-      created timestamp not null default (DATETIME('now')),
-
-      foreign key (module) references Module(id),
-      foreign key (backend) references Backend(id)
-    );""")
-
-  db.exec(sql"""
-    create table if not exists Type(
-      id integer primary key,
-      module integer not null,
-      name varchar(max) not null,
-      data varchar(max) not null,
-      created timestamp not null default (DATETIME('now')),
-
-      foreign key (module) references module(id)
-    );""")
-
-
diff --git a/compiler/cbuilder.nim b/compiler/cbuilder.nim
new file mode 100644
index 000000000..bb1bdfe27
--- /dev/null
+++ b/compiler/cbuilder.nim
@@ -0,0 +1,174 @@
+type
+  Snippet = string
+  Builder = string
+
+template newBuilder(s: string): Builder =
+  s
+
+proc addField(obj: var Builder; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
+  obj.add('\t')
+  obj.add(typ)
+  obj.add(" ")
+  obj.add(name)
+  if isFlexArray:
+    obj.add("[SEQ_DECL_SIZE]")
+  if initializer.len != 0:
+    obj.add(initializer)
+  obj.add(";\n")
+
+proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
+  ## for fields based on an `skField` symbol
+  obj.add('\t')
+  if field.alignment > 0:
+    obj.add("NIM_ALIGN(")
+    obj.addInt(field.alignment)
+    obj.add(") ")
+  obj.add(typ)
+  if sfNoalias in field.flags:
+    obj.add(" NIM_NOALIAS")
+  obj.add(" ")
+  obj.add(name)
+  if isFlexArray:
+    obj.add("[SEQ_DECL_SIZE]")
+  if field.bitsize != 0:
+    obj.add(":")
+    obj.addInt(field.bitsize)
+  if initializer.len != 0:
+    obj.add(initializer)
+  obj.add(";\n")
+
+type
+  BaseClassKind = enum
+    bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
+  StructBuilderInfo = object
+    baseKind: BaseClassKind
+    preFieldsLen: int
+
+proc structOrUnion(t: PType): Snippet =
+  let t = t.skipTypes({tyAlias, tySink})
+  if tfUnion in t.flags: "union"
+  else: "struct"
+
+proc ptrType(t: Snippet): Snippet =
+  t & "*"
+
+proc startSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet): StructBuilderInfo =
+  result = StructBuilderInfo(baseKind: bcNone)
+  obj.add("struct ")
+  obj.add(name)
+  if baseType.len != 0:
+    if m.compileToCpp:
+      result.baseKind = bcCppInherit
+    else:
+      result.baseKind = bcSupField
+  if result.baseKind == bcCppInherit:
+    obj.add(" : public ")
+    obj.add(baseType)
+  obj.add(" ")
+  obj.add("{\n")
+  result.preFieldsLen = obj.len
+  if result.baseKind == bcSupField:
+    obj.addField(name = "Sup", typ = baseType)
+
+proc finishSimpleStruct(obj: var Builder; m: BModule; info: StructBuilderInfo) =
+  if info.baseKind == bcNone and info.preFieldsLen == obj.len:
+    # no fields were added, add dummy field
+    obj.addField(name = "dummy", typ = "char")
+  obj.add("};\n")
+
+template addSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet; body: typed) =
+  ## for independent structs, not directly based on a Nim type
+  let info = startSimpleStruct(obj, m, name, baseType)
+  body
+  finishSimpleStruct(obj, m, info)
+
+proc startStruct(obj: var Builder; m: BModule; t: PType; name: string; baseType: Snippet): StructBuilderInfo =
+  result = StructBuilderInfo(baseKind: bcNone)
+  if tfPacked in t.flags:
+    if hasAttribute in CC[m.config.cCompiler].props:
+      obj.add(structOrUnion(t))
+      obj.add(" __attribute__((__packed__))")
+    else:
+      obj.add("#pragma pack(push, 1)\n")
+      obj.add(structOrUnion(t))
+  else:
+    obj.add(structOrUnion(t))
+  obj.add(" ")
+  obj.add(name)
+  if t.kind == tyObject:
+    if t.baseClass == nil:
+      if lacksMTypeField(t):
+        result.baseKind = bcNone
+      elif optTinyRtti in m.config.globalOptions:
+        result.baseKind = bcNoneTinyRtti
+      else:
+        result.baseKind = bcNoneRtti
+    elif m.compileToCpp:
+      result.baseKind = bcCppInherit
+    else:
+      result.baseKind = bcSupField
+  elif baseType.len != 0:
+    if m.compileToCpp:
+      result.baseKind = bcCppInherit
+    else:
+      result.baseKind = bcSupField
+  if result.baseKind == bcCppInherit:
+    obj.add(" : public ")
+    obj.add(baseType)
+  obj.add(" ")
+  obj.add("{\n")
+  result.preFieldsLen = obj.len
+  case result.baseKind
+  of bcNone:
+    # rest of the options add a field or don't need it due to inheritance,
+    # we need to add the dummy field for uncheckedarray ahead of time
+    # so that it remains trailing
+    if t.itemId notin m.g.graph.memberProcsPerType and
+        t.n != nil and t.n.len == 1 and t.n[0].kind == nkSym and
+        t.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
+      # only consists of flexible array field, add *initial* dummy field
+      obj.addField(name = "dummy", typ = "char")
+  of bcCppInherit: discard
+  of bcNoneRtti:
+    obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType")))
+  of bcNoneTinyRtti:
+    obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
+  of bcSupField:
+    obj.addField(name = "Sup", typ = baseType)
+
+proc finishStruct(obj: var Builder; m: BModule; t: PType; info: StructBuilderInfo) =
+  if info.baseKind == bcNone and info.preFieldsLen == obj.len and
+      t.itemId notin m.g.graph.memberProcsPerType:
+    # no fields were added, add dummy field
+    obj.addField(name = "dummy", typ = "char")
+  obj.add("};\n")
+  if tfPacked in t.flags and hasAttribute notin CC[m.config.cCompiler].props:
+    obj.add("#pragma pack(pop)\n")
+
+template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: Snippet; body: typed) =
+  ## for structs built directly from a Nim type
+  let info = startStruct(obj, m, typ, name, baseType)
+  body
+  finishStruct(obj, m, typ, info)
+
+template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
+  ## adds a field with a `struct { ... }` type, building it according to `body`
+  obj.add('\t')
+  if tfPacked in parentTyp.flags:
+    if hasAttribute in CC[m.config.cCompiler].props:
+      obj.add("struct __attribute__((__packed__)) {\n")
+    else:
+      obj.add("#pragma pack(push, 1)\nstruct {")
+  else:
+    obj.add("struct {\n")
+  body
+  obj.add("} ")
+  obj.add(fieldName)
+  obj.add(";\n")
+  if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props:
+    result.add("#pragma pack(pop)\n")
+
+template addAnonUnion(obj: var Builder; body: typed) =
+  obj.add "union{\n"
+  body
+  obj.add("};\n")
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index 2f9cc822b..ac607e3ad 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -9,273 +9,573 @@
 #
 # included from cgen.nim
 
-proc leftAppearsOnRightSide(le, ri: PNode): bool =
+proc canRaiseDisp(p: BProc; n: PNode): bool =
+  # we assume things like sysFatal cannot raise themselves
+  if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}:
+    result = false
+  elif optPanics in p.config.globalOptions or
+      (n.kind == nkSym and sfSystemModule in getModule(n.sym).flags and
+       sfSystemRaisesDefect notin n.sym.flags):
+    # we know we can be strict:
+    result = canRaise(n)
+  else:
+    # we have to be *very* conservative:
+    result = canRaiseConservative(n)
+
+proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
+  proc locationEscapes(p: BProc; le: PNode; inTryStmt: bool): bool =
+    result = false
+    var n = le
+    while true:
+      # do NOT follow nkHiddenDeref here!
+      case n.kind
+      of nkSym:
+        # we don't own the location so it escapes:
+        if n.sym.owner != p.prc:
+          return true
+        elif inTryStmt and sfUsedInFinallyOrExcept in n.sym.flags:
+          # it is also an observable store if the location is used
+          # in 'except' or 'finally'
+          return true
+        return false
+      of nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv,
+          nkCheckedFieldExpr:
+        n = n[0]
+      of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+        n = n[1]
+      else:
+        # cannot analyse the location; assume the worst
+        return true
+
+  result = false
   if le != nil:
-    for i in 1 ..< ri.len:
+    for i in 1..<ri.len:
       let r = ri[i]
       if isPartOf(le, r) != arNo: return true
+    # we use the weaker 'canRaise' here in order to prevent too many
+    # annoying warnings, see #14514
+    if canRaise(ri[0]) and
+        locationEscapes(p, le, p.nestedTryStmts.len > 0):
+      message(p.config, le.info, warnObservableStores, $le)
+  # bug #19613 prevent dangerous aliasing too:
+  if dest != nil and dest != le:
+    for i in 1..<ri.len:
+      let r = ri[i]
+      if isPartOf(dest, r) != arNo: return true
 
 proc hasNoInit(call: PNode): bool {.inline.} =
-  result = call.sons[0].kind == nkSym and sfNoInit in call.sons[0].sym.flags
+  result = call[0].kind == nkSym and sfNoInit in call[0].sym.flags
+
+proc isHarmlessStore(p: BProc; canRaise: bool; d: TLoc): bool =
+  if d.k in {locTemp, locNone} or not canRaise:
+    result = true
+  elif d.k == locLocalVar and p.withinTryWithExcept == 0:
+    # we cannot observe a store to a local variable if the current proc
+    # has no error handler:
+    result = true
+  else:
+    result = false
+
+proc cleanupTemp(p: BProc; returnType: PType, tmp: TLoc): bool =
+  if returnType.kind in {tyVar, tyLent}:
+    # we don't need to worry about var/lent return types
+    result = false
+  elif hasDestructor(returnType) and getAttachedOp(p.module.g.graph, returnType, attachedDestructor) != nil:
+    let dtor = getAttachedOp(p.module.g.graph, returnType, attachedDestructor)
+    var op = initLocExpr(p, newSymNode(dtor))
+    var callee = rdLoc(op)
+    let destroy = if dtor.typ.firstParamType.kind == tyVar:
+        callee & "(&" & rdLoc(tmp) & ")"
+      else:
+        callee & "(" & rdLoc(tmp) & ")"
+    raiseExitCleanup(p, destroy)
+    result = true
+  else:
+    result = false
 
 proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
                callee, params: Rope) =
-  var pl = callee & ~"(" & params
+  let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
+  genLineDir(p, ri)
+  var pl = callee & "(" & params
   # getUniqueType() is too expensive here:
-  var typ = skipTypes(ri.sons[0].typ, abstractInst)
-  if typ.sons[0] != nil:
-    if isInvalidReturnType(p.config, typ.sons[0]):
-      if params != nil: pl.add(~", ")
+  var typ = skipTypes(ri[0].typ, abstractInst)
+  if typ.returnType != nil:
+    var flags: TAssignmentFlags = {}
+    if typ.returnType.kind in {tyOpenArray, tyVarargs}:
+      # perhaps generate no temp if the call doesn't have side effects
+      flags.incl needTempForOpenArray
+    if isInvalidReturnType(p.config, typ):
+      if params.len != 0: pl.add(", ")
       # beware of 'result = p(result)'. We may need to allocate a temporary:
-      if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
+      if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
         # Great, we can use 'd':
-        if d.k == locNone: getTemp(p, typ.sons[0], d, needsInit=true)
+        if d.k == locNone: d = getTemp(p, typ.returnType, needsInit=true)
         elif d.k notin {locTemp} and not hasNoInit(ri):
           # reset before pass as 'result' var:
           discard "resetLoc(p, d)"
-        add(pl, addrLoc(p.config, d))
-        add(pl, ~");$n")
+        pl.add(addrLoc(p.config, d))
+        pl.add(");\n")
         line(p, cpsStmts, pl)
       else:
-        var tmp: TLoc
-        getTemp(p, typ.sons[0], tmp, needsInit=true)
-        add(pl, addrLoc(p.config, tmp))
-        add(pl, ~");$n")
+        var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
+        pl.add(addrLoc(p.config, tmp))
+        pl.add(");\n")
         line(p, cpsStmts, pl)
         genAssignment(p, d, tmp, {}) # no need for deep copying
+      if canRaise: raiseExit(p)
     else:
-      add(pl, ~")")
-      if p.module.compileToCpp and lfSingleUse in d.flags:
-        # do not generate spurious temporaries for C++! For C we're better off
-        # with them to prevent undefined behaviour and because the codegen
-        # is free to emit expressions multiple times!
-        d.k = locCall
-        d.r = pl
-        excl d.flags, lfSingleUse
-      else:
-        if d.k == locNone: getTemp(p, typ.sons[0], d)
+      pl.add(")")
+      if p.module.compileToCpp:
+        if lfSingleUse in d.flags:
+          # do not generate spurious temporaries for C++! For C we're better off
+          # with them to prevent undefined behaviour and because the codegen
+          # is free to emit expressions multiple times!
+          d.k = locCall
+          d.snippet = pl
+          excl d.flags, lfSingleUse
+        else:
+          if d.k == locNone and p.splitDecls == 0:
+            d = getTempCpp(p, typ.returnType, pl)
+          else:
+            if d.k == locNone: d = getTemp(p, typ.returnType)
+            var list = initLoc(locCall, d.lode, OnUnknown)
+            list.snippet = pl
+            genAssignment(p, d, list, {needAssignCall}) # no need for deep copying
+            if canRaise: raiseExit(p)
+
+      elif isHarmlessStore(p, canRaise, d):
+        var useTemp = false
+        if d.k == locNone:
+          useTemp = true
+          d = getTemp(p, typ.returnType)
         assert(d.t != nil)        # generate an assignment to d:
-        var list: TLoc
-        initLoc(list, locCall, d.lode, OnUnknown)
-        list.r = pl
-        genAssignment(p, d, list, {}) # no need for deep copying
+        var list = initLoc(locCall, d.lode, OnUnknown)
+        list.snippet = pl
+        genAssignment(p, d, list, flags+{needAssignCall}) # no need for deep copying
+        if canRaise:
+          if not (useTemp and cleanupTemp(p, typ.returnType, d)):
+            raiseExit(p)
+      else:
+        var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
+        var list = initLoc(locCall, d.lode, OnUnknown)
+        list.snippet = pl
+        genAssignment(p, tmp, list, flags+{needAssignCall}) # no need for deep copying
+        if canRaise:
+          if not cleanupTemp(p, typ.returnType, tmp):
+            raiseExit(p)
+        genAssignment(p, d, tmp, {})
   else:
-    add(pl, ~");$n")
+    pl.add(");\n")
     line(p, cpsStmts, pl)
-
-proc isInCurrentFrame(p: BProc, n: PNode): bool =
-  # checks if `n` is an expression that refers to the current frame;
-  # this does not work reliably because of forwarding + inlining can break it
-  case n.kind
-  of nkSym:
-    if n.sym.kind in {skVar, skResult, skTemp, skLet} and p.prc != nil:
-      result = p.prc.id == n.sym.owner.id
-  of nkDotExpr, nkBracketExpr:
-    if skipTypes(n.sons[0].typ, abstractInst).kind notin {tyVar,tyLent,tyPtr,tyRef}:
-      result = isInCurrentFrame(p, n.sons[0])
-  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
-    result = isInCurrentFrame(p, n.sons[1])
-  of nkHiddenDeref, nkDerefExpr:
-    # what about: var x = addr(y); callAsOpenArray(x[])?
-    # *shrug* ``addr`` is unsafe anyway.
+    if canRaise: raiseExit(p)
+
+proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType)
+
+proc reifiedOpenArray(n: PNode): bool {.inline.} =
+  var x = n
+  while true:
+    case x.kind
+    of {nkAddr, nkHiddenAddr, nkHiddenDeref}:
+      x = x[0]
+    of nkHiddenStdConv:
+      x = x[1]
+    else:
+      break
+  if x.kind == nkSym and x.sym.kind == skParam:
     result = false
-  of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
-    result = isInCurrentFrame(p, n.sons[0])
-  else: discard
-
-proc genBoundsCheck(p: BProc; arr, a, b: TLoc)
-
-proc openArrayLoc(p: BProc, n: PNode): Rope =
-  var a: TLoc
-
-  let q = skipConv(n)
+  else:
+    result = true
+
+proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareForMutation = false): (Rope, Rope) =
+  var a = initLocExpr(p, q[1])
+  var b = initLocExpr(p, q[2])
+  var c = initLocExpr(p, q[3])
+  # bug #23321: In the function mapType, ptrs (tyPtr, tyVar, tyLent, tyRef)
+  # are mapped into ctPtrToArray, the dereference of which is skipped
+  # in the `genDeref`. We need to skip these ptrs here
+  let ty = skipTypes(a.t, abstractVar+{tyPtr, tyRef})
+  # but first produce the required index checks:
+  if optBoundsCheck in p.options:
+    genBoundsCheck(p, a, b, c, ty)
+  if prepareForMutation:
+    linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
+  let dest = getTypeDesc(p.module, destType)
+  let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)]
+  case ty.kind
+  of tyArray:
+    let first = toInt64(firstOrd(p.config, ty))
+    if first == 0:
+      result = ("($3*)(($1)+($2))" % [rdLoc(a), rdLoc(b), dest],
+                lengthExpr)
+    else:
+      var lit = newRopeAppender()
+      intLiteral(first, lit)
+      result = ("($4*)($1)+(($2)-($3))" %
+        [rdLoc(a), rdLoc(b), lit, dest],
+        lengthExpr)
+  of tyOpenArray, tyVarargs:
+    if reifiedOpenArray(q[1]):
+      result = ("($3*)($1.Field0)+($2)" % [rdLoc(a), rdLoc(b), dest],
+                lengthExpr)
+    else:
+      result = ("($3*)($1)+($2)" % [rdLoc(a), rdLoc(b), dest],
+                lengthExpr)
+  of tyUncheckedArray, tyCstring:
+    result = ("($3*)($1)+($2)" % [rdLoc(a), rdLoc(b), dest],
+              lengthExpr)
+  of tyString, tySequence:
+    let atyp = skipTypes(a.t, abstractInst)
+    if formalType.skipTypes(abstractInst).kind in {tyVar} and atyp.kind == tyString and
+        optSeqDestructors in p.config.globalOptions:
+      linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
+    if atyp.kind in {tyVar} and not compileToCpp(p.module):
+      result = ("(($5) ? (($4*)(*$1)$3+($2)) : NIM_NIL)" %
+                  [rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, "*" & rdLoc(a))],
+                lengthExpr)
+    else:
+      result = ("(($5) ? (($4*)$1$3+($2)) : NIM_NIL)" %
+                  [rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, rdLoc(a))],
+                lengthExpr)
+  else:
+    result = ("", "")
+    internalError(p.config, "openArrayLoc: " & typeToString(a.t))
+
+proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
+  var q = skipConv(n)
+  var skipped = false
+  while q.kind == nkStmtListExpr and q.len > 0:
+    skipped = true
+    q = q.lastSon
   if getMagic(q) == mSlice:
     # magic: pass slice to openArray:
-    var b, c: TLoc
-    initLocExpr(p, q[1], a)
-    initLocExpr(p, q[2], b)
-    initLocExpr(p, q[3], c)
-    # but first produce the required index checks:
-    if optBoundsCheck in p.options:
-      genBoundsCheck(p, a, b, c)
-    let ty = skipTypes(a.t, abstractVar+{tyPtr})
-    case ty.kind
-    of tyArray:
-      let first = firstOrd(p.config, ty)
-      if first == 0:
-        result = "($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)]
-      else:
-        result = "($1)+(($2)-($4)), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), intLiteral(first)]
-    of tyOpenArray, tyVarargs:
-      result = "($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)]
-    of tyString, tySequence:
-      if skipTypes(n.typ, abstractInst).kind == tyVar and
-          not compileToCpp(p.module):
-        result = "(*$1)->data+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)]
-      else:
-        result = "$1->data+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)]
-    else:
-      internalError(p.config, "openArrayLoc: " & typeToString(a.t))
+    if skipped:
+      q = skipConv(n)
+      while q.kind == nkStmtListExpr and q.len > 0:
+        for i in 0..<q.len-1:
+          genStmts(p, q[i])
+        q = q.lastSon
+    let (x, y) = genOpenArraySlice(p, q, formalType, n.typ.elementType)
+    result.add x & ", " & y
   else:
-    initLocExpr(p, n, a)
-    case skipTypes(a.t, abstractVar).kind
+    var a = initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n)
+    case skipTypes(a.t, abstractVar+{tyStatic}).kind
     of tyOpenArray, tyVarargs:
-      result = "$1, $1Len_0" % [rdLoc(a)]
+      if reifiedOpenArray(n):
+        if a.t.kind in {tyVar, tyLent}:
+          result.add "$1->Field0, $1->Field1" % [rdLoc(a)]
+        else:
+          result.add "$1.Field0, $1.Field1" % [rdLoc(a)]
+      else:
+        result.add "$1, $1Len_0" % [rdLoc(a)]
     of tyString, tySequence:
-      if skipTypes(n.typ, abstractInst).kind == tyVar and
-            not compileToCpp(p.module):
-        result = "(*$1)->data, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p)]
+      let ntyp = skipTypes(n.typ, abstractInst)
+      if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and
+          optSeqDestructors in p.config.globalOptions:
+        linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
+      if ntyp.kind in {tyVar} and not compileToCpp(p.module):
+        var t = TLoc(snippet: "(*$1)" % [a.rdLoc])
+        result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
+                     [a.rdLoc, lenExpr(p, t), dataField(p),
+                      dataFieldAccessor(p, "*" & a.rdLoc)]
       else:
-        result = "$1->data, ($1 ? $1->$2 : 0)" % [a.rdLoc, lenField(p)]
+        result.add "($4) ? ($1$3) : NIM_NIL, $2" %
+                     [a.rdLoc, lenExpr(p, a), dataField(p), dataFieldAccessor(p, a.rdLoc)]
     of tyArray:
-      result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
+      result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
     of tyPtr, tyRef:
-      case lastSon(a.t).kind
+      case elementType(a.t).kind
       of tyString, tySequence:
-        result = "(*$1)->data, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p)]
+        var t = TLoc(snippet: "(*$1)" % [a.rdLoc])
+        result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
+                     [a.rdLoc, lenExpr(p, t), dataField(p),
+                      dataFieldAccessor(p, "*" & a.rdLoc)]
       of tyArray:
-        result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))]
+        result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, elementType(a.t)))]
       else:
         internalError(p.config, "openArrayLoc: " & typeToString(a.t))
     else: internalError(p.config, "openArrayLoc: " & typeToString(a.t))
 
-proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} =
-  var a: TLoc
-  initLocExpr(p, n.sons[0], a)
-  result = "($1 ? $1->data : (NCSTRING)\"\")" % [a.rdLoc]
+proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc =
+  # Bug https://github.com/status-im/nimbus-eth2/issues/1549
+  # Aliasing is preferred over stack overflows.
+  # Also don't regress for non ARC-builds, too risky.
+  if needsTmp and a.lode.typ != nil and p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and
+      getSize(p.config, a.lode.typ) < 1024:
+    result = getTemp(p, a.lode.typ, needsInit=false)
+    genAssignment(p, result, a, {})
+  else:
+    result = a
+
+proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc =
+  result = getTemp(p, a.lode.typ, needsInit=false)
+  genAssignment(p, result, a, {})
+
+proc genArgStringToCString(p: BProc, n: PNode; result: var Rope; needsTmp: bool) {.inline.} =
+  var a = initLocExpr(p, n[0])
+  appcg(p.module, result, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc])
 
-proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope =
+proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; needsTmp = false) =
   var a: TLoc
   if n.kind == nkStringToCString:
-    result = genArgStringToCString(p, n)
+    genArgStringToCString(p, n, result, needsTmp)
   elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
-    var n = if n.kind != nkHiddenAddr: n else: n.sons[0]
-    result = openArrayLoc(p, n)
-  elif ccgIntroducedPtr(p.config, param):
-    initLocExpr(p, n, a)
-    result = addrLoc(p.config, a)
-  elif p.module.compileToCpp and param.typ.kind == tyVar and
+    var n = if n.kind != nkHiddenAddr: n else: n[0]
+    openArrayLoc(p, param.typ, n, result)
+  elif ccgIntroducedPtr(p.config, param, call[0].typ.returnType) and
+    (optByRef notin param.options or not p.module.compileToCpp):
+    a = initLocExpr(p, n)
+    if n.kind in {nkCharLit..nkNilLit}:
+      addAddrLoc(p.config, literalsNeedsTmp(p, a), result)
+    else:
+      addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result)
+  elif p.module.compileToCpp and param.typ.kind in {tyVar} and
       n.kind == nkHiddenAddr:
-    initLocExprSingleUse(p, n.sons[0], a)
+    # bug #23748: we need to introduce a temporary here. The expression type
+    # will be a reference in C++ and we cannot create a temporary reference
+    # variable. Thus, we create a temporary pointer variable instead.
+    let needsIndirect = mapType(p.config, n[0].typ, mapTypeChooser(n[0]) == skParam) != ctArray
+    if needsIndirect:
+      n.typ = n.typ.exactReplica
+      n.typ.flags.incl tfVarIsPtr
+    a = initLocExprSingleUse(p, n)
+    a = withTmpIfNeeded(p, a, needsTmp)
+    if needsIndirect: a.flags.incl lfIndirect
     # if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still
     # means '*T'. See posix.nim for lots of examples that do that in the wild.
-    let callee = call.sons[0]
+    let callee = call[0]
     if callee.kind == nkSym and
-        {sfImportC, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportC} and
-        {lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
-      result = addrLoc(p.config, a)
+        {sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and
+        {lfHeader, lfNoDecl} * callee.sym.loc.flags != {} and
+        needsIndirect:
+      addAddrLoc(p.config, a, result)
     else:
-      result = rdLoc(a)
+      addRdLoc(a, result)
   else:
-    initLocExprSingleUse(p, n, a)
-    result = rdLoc(a)
-
-proc genArgNoParam(p: BProc, n: PNode): Rope =
+    a = initLocExprSingleUse(p, n)
+    if param.typ.kind in {tyVar, tyPtr, tyRef, tySink}:
+      let typ = skipTypes(param.typ, abstractPtrs)
+      if not sameBackendTypePickyAliases(typ, n.typ.skipTypes(abstractPtrs)):
+        a.snippet = "(($1) ($2))" %
+          [getTypeDesc(p.module, param.typ), rdCharLoc(a)]
+    addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
+  #assert result != nil
+
+proc genArgNoParam(p: BProc, n: PNode; result: var Rope; needsTmp = false) =
   var a: TLoc
   if n.kind == nkStringToCString:
-    result = genArgStringToCString(p, n)
+    genArgStringToCString(p, n, result, needsTmp)
   else:
-    initLocExprSingleUse(p, n, a)
-    result = rdLoc(a)
-
-template genParamLoop(params) {.dirty.} =
-  if i < sonsLen(typ):
-    assert(typ.n.sons[i].kind == nkSym)
-    let paramType = typ.n.sons[i]
-    if not paramType.typ.isCompileTimeOnly:
-      if params != nil: add(params, ~", ")
-      add(params, genArg(p, ri.sons[i], paramType.sym, ri))
+    a = initLocExprSingleUse(p, n)
+    addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
+
+import aliasanalysis
+
+proc potentialAlias(n: PNode, potentialWrites: seq[PNode]): bool =
+  result = false
+  for p in potentialWrites:
+    if p.aliases(n) != no or n.aliases(p) != no:
+      return true
+
+proc skipTrivialIndirections(n: PNode): PNode =
+  result = n
+  while true:
+    case result.kind
+    of nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr, nkObjDownConv, nkObjUpConv:
+      result = result[0]
+    of nkHiddenStdConv, nkHiddenSubConv:
+      result = result[1]
+    else: break
+
+proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) =
+  case n.kind:
+  of nkLiterals, nkIdent, nkFormalParams: discard
+  of nkSym:
+    if mutate: result.add n
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
+    getPotentialWrites(n[0], true, result)
+    getPotentialWrites(n[1], mutate, result)
+  of nkAddr, nkHiddenAddr:
+    getPotentialWrites(n[0], true, result)
+  of nkBracketExpr, nkDotExpr, nkCheckedFieldExpr:
+    getPotentialWrites(n[0], mutate, result)
+  of nkCallKinds:
+    case n.getMagic:
+    of mIncl, mExcl, mInc, mDec, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
+        mAddr, mNew, mNewFinalize, mWasMoved, mDestroy:
+      getPotentialWrites(n[1], true, result)
+      for i in 2..<n.len:
+        getPotentialWrites(n[i], mutate, result)
+    of mSwap:
+      for i in 1..<n.len:
+        getPotentialWrites(n[i], true, result)
+    else:
+      for i in 1..<n.len:
+        getPotentialWrites(n[i], mutate, result)
   else:
-    if params != nil: add(params, ~", ")
-    add(params, genArgNoParam(p, ri.sons[i]))
+    for s in n:
+      getPotentialWrites(s, mutate, result)
+
+proc getPotentialReads(n: PNode; result: var seq[PNode]) =
+  case n.kind:
+  of nkLiterals, nkIdent, nkFormalParams: discard
+  of nkSym: result.add n
+  else:
+    for s in n:
+      getPotentialReads(s, result)
+
+proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
+  # We must generate temporaries in cases like #14396
+  # to keep the strict Left-To-Right evaluation
+  var needTmp = newSeq[bool](ri.len - 1)
+  var potentialWrites: seq[PNode] = @[]
+  for i in countdown(ri.len - 1, 1):
+    if ri[i].skipTrivialIndirections.kind == nkSym:
+      needTmp[i - 1] = potentialAlias(ri[i], potentialWrites)
+    else:
+      #if not ri[i].typ.isCompileTimeOnly:
+      var potentialReads: seq[PNode] = @[]
+      getPotentialReads(ri[i], potentialReads)
+      for n in potentialReads:
+        if not needTmp[i - 1]:
+          needTmp[i - 1] = potentialAlias(n, potentialWrites)
+      getPotentialWrites(ri[i], false, potentialWrites)
+    when false:
+      # this optimization is wrong, see bug #23748
+      if ri[i].kind in {nkHiddenAddr, nkAddr}:
+        # Optimization: don't use a temp, if we would only take the address anyway
+        needTmp[i - 1] = false
+
+  var oldLen = result.len
+  for i in 1..<ri.len:
+    if i < typ.n.len:
+      assert(typ.n[i].kind == nkSym)
+      let paramType = typ.n[i]
+      if not paramType.typ.isCompileTimeOnly:
+        if oldLen != result.len:
+          result.add(", ")
+          oldLen = result.len
+        genArg(p, ri[i], paramType.sym, ri, result, needTmp[i-1])
+    else:
+      if oldLen != result.len:
+        result.add(", ")
+        oldLen = result.len
+      genArgNoParam(p, ri[i], result, needTmp[i-1])
+
+proc addActualSuffixForHCR(res: var Rope, module: PSym, sym: PSym) =
+  if sym.flags * {sfImportc, sfNonReloadable} == {} and sym.loc.k == locProc and
+      (sym.typ.callConv == ccInline or sym.owner.id == module.id):
+    res = res & "_actual".rope
 
 proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
-  var op: TLoc
   # this is a hotspot in the compiler
-  initLocExpr(p, ri.sons[0], op)
-  var params: Rope
+  var op = initLocExpr(p, ri[0])
   # getUniqueType() is too expensive here:
-  var typ = skipTypes(ri.sons[0].typ, abstractInst)
+  var typ = skipTypes(ri[0].typ, abstractInstOwned)
   assert(typ.kind == tyProc)
-  assert(sonsLen(typ) == sonsLen(typ.n))
-  var length = sonsLen(ri)
-  for i in countup(1, length - 1):
-    genParamLoop(params)
-  fixupCall(p, le, ri, d, op.r, params)
 
-proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
+  var params = newRopeAppender()
+  genParams(p, ri, typ, params)
 
-  proc getRawProcType(p: BProc, t: PType): Rope =
-    result = getClosureType(p.module, t, clHalf)
+  var callee = rdLoc(op)
+  if p.hcrOn and ri[0].kind == nkSym:
+    callee.addActualSuffixForHCR(p.module.module, ri[0].sym)
+  fixupCall(p, le, ri, d, callee, params)
+
+proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
 
   proc addComma(r: Rope): Rope =
-    result = if r == nil: r else: r & ~", "
+    if r.len == 0: r else: r & ", "
 
   const PatProc = "$1.ClE_0? $1.ClP_0($3$1.ClE_0):(($4)($1.ClP_0))($2)"
   const PatIter = "$1.ClP_0($3$1.ClE_0)" # we know the env exists
-  var op: TLoc
-  initLocExpr(p, ri.sons[0], op)
-  var pl: Rope
 
-  var typ = skipTypes(ri.sons[0].typ, abstractInst)
+  var op = initLocExpr(p, ri[0])
+
+  # getUniqueType() is too expensive here:
+  var typ = skipTypes(ri[0].typ, abstractInstOwned)
   assert(typ.kind == tyProc)
-  var length = sonsLen(ri)
-  for i in countup(1, length - 1):
-    assert(sonsLen(typ) == sonsLen(typ.n))
-    genParamLoop(pl)
+
+  var pl = newRopeAppender()
+  genParams(p, ri, typ, pl)
 
   template genCallPattern {.dirty.} =
-    lineF(p, cpsStmts, callPattern & ";$n", [op.r, pl, pl.addComma, rawProc])
+    if tfIterator in typ.flags:
+      lineF(p, cpsStmts, PatIter & ";$n", [rdLoc(op), pl, pl.addComma, rawProc])
+    else:
+      lineF(p, cpsStmts, PatProc & ";$n", [rdLoc(op), pl, pl.addComma, rawProc])
 
-  let rawProc = getRawProcType(p, typ)
-  let callPattern = if tfIterator in typ.flags: PatIter else: PatProc
-  if typ.sons[0] != nil:
-    if isInvalidReturnType(p.config, typ.sons[0]):
-      if sonsLen(ri) > 1: add(pl, ~", ")
+  let rawProc = getClosureType(p.module, typ, clHalf)
+  let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
+  if typ.returnType != nil:
+    if isInvalidReturnType(p.config, typ):
+      if ri.len > 1: pl.add(", ")
       # beware of 'result = p(result)'. We may need to allocate a temporary:
-      if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
+      if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
         # Great, we can use 'd':
         if d.k == locNone:
-          getTemp(p, typ.sons[0], d, needsInit=true)
+          d = getTemp(p, typ.returnType, needsInit=true)
         elif d.k notin {locTemp} and not hasNoInit(ri):
           # reset before pass as 'result' var:
           discard "resetLoc(p, d)"
-        add(pl, addrLoc(p.config, d))
+        pl.add(addrLoc(p.config, d))
         genCallPattern()
+        if canRaise: raiseExit(p)
       else:
-        var tmp: TLoc
-        getTemp(p, typ.sons[0], tmp, needsInit=true)
-        add(pl, addrLoc(p.config, tmp))
+        var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
+        pl.add(addrLoc(p.config, tmp))
         genCallPattern()
+        if canRaise: raiseExit(p)
         genAssignment(p, d, tmp, {}) # no need for deep copying
-    else:
-      if d.k == locNone: getTemp(p, typ.sons[0], d)
+    elif isHarmlessStore(p, canRaise, d):
+      if d.k == locNone: d = getTemp(p, typ.returnType)
       assert(d.t != nil)        # generate an assignment to d:
-      var list: TLoc
-      initLoc(list, locCall, d.lode, OnUnknown)
-      list.r = callPattern % [op.r, pl, pl.addComma, rawProc]
+      var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
+      if tfIterator in typ.flags:
+        list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
+      else:
+        list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
       genAssignment(p, d, list, {}) # no need for deep copying
+      if canRaise: raiseExit(p)
+    else:
+      var tmp: TLoc = getTemp(p, typ.returnType)
+      assert(d.t != nil)        # generate an assignment to d:
+      var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
+      if tfIterator in typ.flags:
+        list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
+      else:
+        list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
+      genAssignment(p, tmp, list, {})
+      if canRaise: raiseExit(p)
+      genAssignment(p, d, tmp, {})
   else:
     genCallPattern()
+    if canRaise: raiseExit(p)
 
-proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
-  if i < sonsLen(typ):
+proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope;
+                 argsCounter: var int) =
+  if i < typ.n.len:
     # 'var T' is 'T&' in C++. This means we ignore the request of
     # any nkHiddenAddr when it's a 'var T'.
-    let paramType = typ.n.sons[i]
+    let paramType = typ.n[i]
     assert(paramType.kind == nkSym)
     if paramType.typ.isCompileTimeOnly:
-      result = nil
-    elif typ.sons[i].kind == tyVar and ri.sons[i].kind == nkHiddenAddr:
-      result = genArgNoParam(p, ri.sons[i][0])
+      discard
+    elif paramType.typ.kind in {tyVar} and ri[i].kind == nkHiddenAddr:
+      if argsCounter > 0: result.add ", "
+      genArgNoParam(p, ri[i][0], result)
+      inc argsCounter
     else:
-      result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym)
+      if argsCounter > 0: result.add ", "
+      genArgNoParam(p, ri[i], result) #, typ.n[i].sym)
+      inc argsCounter
   else:
     if tfVarargs notin typ.flags:
       localError(p.config, ri.info, "wrong argument count")
-      result = nil
     else:
-      result = genArgNoParam(p, ri.sons[i])
+      if argsCounter > 0: result.add ", "
+      genArgNoParam(p, ri[i], result)
+      inc argsCounter
 
 discard """
 Dot call syntax in C++
@@ -319,99 +619,99 @@ proc skipAddrDeref(node: PNode): PNode =
   var isAddr = false
   case n.kind
   of nkAddr, nkHiddenAddr:
-    n = n.sons[0]
+    n = n[0]
     isAddr = true
   of nkDerefExpr, nkHiddenDeref:
-    n = n.sons[0]
+    n = n[0]
   else: return n
-  if n.kind == nkObjDownConv: n = n.sons[0]
+  if n.kind == nkObjDownConv: n = n[0]
   if isAddr and n.kind in {nkDerefExpr, nkHiddenDeref}:
-    result = n.sons[0]
+    result = n[0]
   elif n.kind in {nkAddr, nkHiddenAddr}:
-    result = n.sons[0]
+    result = n[0]
   else:
     result = node
 
-proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
+proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope) =
   # for better or worse c2nim translates the 'this' argument to a 'var T'.
   # However manual wrappers may also use 'ptr T'. In any case we support both
   # for convenience.
-  internalAssert p.config, i < sonsLen(typ)
-  assert(typ.n.sons[i].kind == nkSym)
+  internalAssert p.config, i < typ.n.len
+  assert(typ.n[i].kind == nkSym)
   # if the parameter is lying (tyVar) and thus we required an additional deref,
   # skip the deref:
   var ri = ri[i]
   while ri.kind == nkObjDownConv: ri = ri[0]
-  let t = typ.sons[i].skipTypes({tyGenericInst, tyAlias, tySink})
-  if t.kind == tyVar:
+  let t = typ[i].skipTypes({tyGenericInst, tyAlias, tySink})
+  if t.kind in {tyVar}:
     let x = if ri.kind == nkHiddenAddr: ri[0] else: ri
     if x.typ.kind == tyPtr:
-      result = genArgNoParam(p, x)
+      genArgNoParam(p, x, result)
       result.add("->")
     elif x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].typ.kind == tyPtr:
-      result = genArgNoParam(p, x[0])
+      genArgNoParam(p, x[0], result)
       result.add("->")
     else:
-      result = genArgNoParam(p, x)
+      genArgNoParam(p, x, result)
       result.add(".")
   elif t.kind == tyPtr:
     if ri.kind in {nkAddr, nkHiddenAddr}:
-      result = genArgNoParam(p, ri[0])
+      genArgNoParam(p, ri[0], result)
       result.add(".")
     else:
-      result = genArgNoParam(p, ri)
+      genArgNoParam(p, ri, result)
       result.add("->")
   else:
     ri = skipAddrDeref(ri)
     if ri.kind in {nkAddr, nkHiddenAddr}: ri = ri[0]
-    result = genArgNoParam(p, ri) #, typ.n.sons[i].sym)
+    genArgNoParam(p, ri, result) #, typ.n[i].sym)
     result.add(".")
 
-proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope =
+proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Rope) =
   var i = 0
   var j = 1
   while i < pat.len:
     case pat[i]
     of '@':
-      if j < ri.len:
-        result.add genOtherArg(p, ri, j, typ)
-        for k in j+1 ..< ri.len:
-          result.add(~", ")
-          result.add genOtherArg(p, ri, k, typ)
+      var argsCounter = 0
+      for k in j..<ri.len:
+        genOtherArg(p, ri, k, typ, result, argsCounter)
       inc i
     of '#':
-      if pat[i+1] in {'+', '@'}:
+      if i+1 < pat.len and pat[i+1] in {'+', '@'}:
         let ri = ri[j]
         if ri.kind in nkCallKinds:
-          let typ = skipTypes(ri.sons[0].typ, abstractInst)
-          if pat[i+1] == '+': result.add genArgNoParam(p, ri.sons[0])
-          result.add(~"(")
+          let typ = skipTypes(ri[0].typ, abstractInst)
+          if pat[i+1] == '+': genArgNoParam(p, ri[0], result)
+          result.add("(")
           if 1 < ri.len:
-            result.add genOtherArg(p, ri, 1, typ)
-          for k in j+1 ..< ri.len:
-            result.add(~", ")
-            result.add genOtherArg(p, ri, k, typ)
-          result.add(~")")
+            var argsCounterB = 0
+            genOtherArg(p, ri, 1, typ, result, argsCounterB)
+          for k in j+1..<ri.len:
+            var argsCounterB = 0
+            genOtherArg(p, ri, k, typ, result, argsCounterB)
+          result.add(")")
         else:
           localError(p.config, ri.info, "call expression expected for C++ pattern")
         inc i
-      elif pat[i+1] == '.':
-        result.add genThisArg(p, ri, j, typ)
+      elif i+1 < pat.len and pat[i+1] == '.':
+        genThisArg(p, ri, j, typ, result)
         inc i
-      elif pat[i+1] == '[':
-        var arg = ri.sons[j].skipAddrDeref
+      elif i+1 < pat.len and pat[i+1] == '[':
+        var arg = ri[j].skipAddrDeref
         while arg.kind in {nkAddr, nkHiddenAddr, nkObjDownConv}: arg = arg[0]
-        result.add genArgNoParam(p, arg)
+        genArgNoParam(p, arg, result)
         #result.add debugTree(arg, 0, 10)
       else:
-        result.add genOtherArg(p, ri, j, typ)
+        var argsCounter = 0
+        genOtherArg(p, ri, j, typ, result, argsCounter)
       inc j
       inc i
     of '\'':
-      var idx, stars: int
+      var idx, stars: int = 0
       if scanCppGenericSlot(pat, i, idx, stars):
         var t = resolveStarsInCppType(typ, idx, stars)
-        if t == nil: result.add(~"void")
+        if t == nil: result.add("void")
         else: result.add(getTypeDesc(p.module, t))
     else:
       let start = i
@@ -419,142 +719,147 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope =
         if pat[i] notin {'@', '#', '\''}: inc(i)
         else: break
       if i - 1 >= start:
-        add(result, substr(pat, start, i - 1))
+        result.add(substr(pat, start, i - 1))
 
 proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
-  var op: TLoc
-  initLocExpr(p, ri.sons[0], op)
+  var op = initLocExpr(p, ri[0])
   # getUniqueType() is too expensive here:
-  var typ = skipTypes(ri.sons[0].typ, abstractInst)
+  var typ = skipTypes(ri[0].typ, abstractInst)
   assert(typ.kind == tyProc)
-  var length = sonsLen(ri)
-  assert(sonsLen(typ) == sonsLen(typ.n))
   # don't call '$' here for efficiency:
-  let pat = ri.sons[0].sym.loc.r.data
-  internalAssert p.config, pat != nil
+  let pat = $ri[0].sym.loc.snippet
+  internalAssert p.config, pat.len > 0
   if pat.contains({'#', '(', '@', '\''}):
-    var pl = genPatternCall(p, ri, pat, typ)
+    var pl = newRopeAppender()
+    genPatternCall(p, ri, pat, typ, pl)
     # simpler version of 'fixupCall' that works with the pl+params combination:
-    var typ = skipTypes(ri.sons[0].typ, abstractInst)
-    if typ.sons[0] != nil:
+    var typ = skipTypes(ri[0].typ, abstractInst)
+    if typ.returnType != nil:
       if p.module.compileToCpp and lfSingleUse in d.flags:
         # do not generate spurious temporaries for C++! For C we're better off
         # with them to prevent undefined behaviour and because the codegen
         # is free to emit expressions multiple times!
         d.k = locCall
-        d.r = pl
+        d.snippet = pl
         excl d.flags, lfSingleUse
       else:
-        if d.k == locNone: getTemp(p, typ.sons[0], d)
+        if d.k == locNone: d = getTemp(p, typ.returnType)
         assert(d.t != nil)        # generate an assignment to d:
-        var list: TLoc
-        initLoc(list, locCall, d.lode, OnUnknown)
-        list.r = pl
+        var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
+        list.snippet = pl
         genAssignment(p, d, list, {}) # no need for deep copying
     else:
-      add(pl, ~";$n")
+      pl.add(";\n")
       line(p, cpsStmts, pl)
   else:
-    var pl: Rope = nil
-    #var param = typ.n.sons[1].sym
+    var pl = newRopeAppender()
+    var argsCounter = 0
     if 1 < ri.len:
-      add(pl, genThisArg(p, ri, 1, typ))
-    add(pl, op.r)
-    var params: Rope
-    for i in countup(2, length - 1):
-      if params != nil: params.add(~", ")
-      assert(sonsLen(typ) == sonsLen(typ.n))
-      add(params, genOtherArg(p, ri, i, typ))
+      genThisArg(p, ri, 1, typ, pl)
+    pl.add(op.snippet)
+    var params = newRopeAppender()
+    for i in 2..<ri.len:
+      genOtherArg(p, ri, i, typ, params, argsCounter)
     fixupCall(p, le, ri, d, pl, params)
 
 proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
   # generates a crappy ObjC call
-  var op: TLoc
-  initLocExpr(p, ri.sons[0], op)
-  var pl = ~"["
+  var op = initLocExpr(p, ri[0])
+  var pl = "["
   # getUniqueType() is too expensive here:
-  var typ = skipTypes(ri.sons[0].typ, abstractInst)
+  var typ = skipTypes(ri[0].typ, abstractInst)
   assert(typ.kind == tyProc)
-  var length = sonsLen(ri)
-  assert(sonsLen(typ) == sonsLen(typ.n))
 
   # don't call '$' here for efficiency:
-  let pat = ri.sons[0].sym.loc.r.data
-  internalAssert p.config, pat != nil
+  let pat = $ri[0].sym.loc.snippet
+  internalAssert p.config, pat.len > 0
   var start = 3
   if ' ' in pat:
     start = 1
-    add(pl, op.r)
-    if length > 1:
-      add(pl, ~": ")
-      add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri))
+    pl.add(op.snippet)
+    if ri.len > 1:
+      pl.add(": ")
+      genArg(p, ri[1], typ.n[1].sym, ri, pl)
       start = 2
   else:
-    if length > 1:
-      add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri))
-      add(pl, ~" ")
-    add(pl, op.r)
-    if length > 2:
-      add(pl, ~": ")
-      add(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym, ri))
-  for i in countup(start, length-1):
-    assert(sonsLen(typ) == sonsLen(typ.n))
-    if i >= sonsLen(typ):
+    if ri.len > 1:
+      genArg(p, ri[1], typ.n[1].sym, ri, pl)
+      pl.add(" ")
+    pl.add(op.snippet)
+    if ri.len > 2:
+      pl.add(": ")
+      genArg(p, ri[2], typ.n[2].sym, ri, pl)
+  for i in start..<ri.len:
+    if i >= typ.n.len:
       internalError(p.config, ri.info, "varargs for objective C method?")
-    assert(typ.n.sons[i].kind == nkSym)
-    var param = typ.n.sons[i].sym
-    add(pl, ~" ")
-    add(pl, param.name.s)
-    add(pl, ~": ")
-    add(pl, genArg(p, ri.sons[i], param, ri))
-  if typ.sons[0] != nil:
-    if isInvalidReturnType(p.config, typ.sons[0]):
-      if sonsLen(ri) > 1: add(pl, ~" ")
+    assert(typ.n[i].kind == nkSym)
+    var param = typ.n[i].sym
+    pl.add(" ")
+    pl.add(param.name.s)
+    pl.add(": ")
+    genArg(p, ri[i], param, ri, pl)
+  if typ.returnType != nil:
+    if isInvalidReturnType(p.config, typ):
+      if ri.len > 1: pl.add(" ")
       # beware of 'result = p(result)'. We always allocate a temporary:
       if d.k in {locTemp, locNone}:
         # We already got a temp. Great, special case it:
-        if d.k == locNone: getTemp(p, typ.sons[0], d, needsInit=true)
-        add(pl, ~"Result: ")
-        add(pl, addrLoc(p.config, d))
-        add(pl, ~"];$n")
+        if d.k == locNone: d = getTemp(p, typ.returnType, needsInit=true)
+        pl.add("Result: ")
+        pl.add(addrLoc(p.config, d))
+        pl.add("];\n")
         line(p, cpsStmts, pl)
       else:
-        var tmp: TLoc
-        getTemp(p, typ.sons[0], tmp, needsInit=true)
-        add(pl, addrLoc(p.config, tmp))
-        add(pl, ~"];$n")
+        var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
+        pl.add(addrLoc(p.config, tmp))
+        pl.add("];\n")
         line(p, cpsStmts, pl)
         genAssignment(p, d, tmp, {}) # no need for deep copying
     else:
-      add(pl, ~"]")
-      if d.k == locNone: getTemp(p, typ.sons[0], d)
+      pl.add("]")
+      if d.k == locNone: d = getTemp(p, typ.returnType)
       assert(d.t != nil)        # generate an assignment to d:
-      var list: TLoc
-      initLoc(list, locCall, ri, OnUnknown)
-      list.r = pl
+      var list: TLoc = initLoc(locCall, ri, OnUnknown)
+      list.snippet = pl
       genAssignment(p, d, list, {}) # no need for deep copying
   else:
-    add(pl, ~"];$n")
+    pl.add("];\n")
     line(p, cpsStmts, pl)
 
-proc genCall(p: BProc, e: PNode, d: var TLoc) =
-  if e.sons[0].typ.skipTypes({tyGenericInst, tyAlias, tySink}).callConv == ccClosure:
-    genClosureCall(p, nil, e, d)
-  elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags:
-    genInfixCall(p, nil, e, d)
-  elif e.sons[0].kind == nkSym and sfNamedParamCall in e.sons[0].sym.flags:
-    genNamedParamCall(p, e, d)
-  else:
-    genPrefixCall(p, nil, e, d)
-  postStmtActions(p)
+proc notYetAlive(n: PNode): bool {.inline.} =
+  let r = getRoot(n)
+  result = r != nil and r.loc.lode == nil
+
+proc isInactiveDestructorCall(p: BProc, e: PNode): bool =
+  #[ Consider this example.
+
+    var :tmpD_3281815
+    try:
+      if true:
+        return
+      let args_3280013 =
+        wasMoved_3281816(:tmpD_3281815)
+        `=_3280036`(:tmpD_3281815, [1])
+        :tmpD_3281815
+    finally:
+      `=destroy_3280027`(args_3280013)
+
+  We want to return early but the 'finally' section is traversed before
+  the 'let args = ...' statement. We exploit this to generate better
+  code for 'return'. ]#
+  result = e.len == 2 and e[0].kind == nkSym and
+    e[0].sym.name.s == "=destroy" and notYetAlive(e[1].skipAddr)
 
 proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
-  if ri.sons[0].typ.skipTypes({tyGenericInst, tyAlias, tySink}).callConv == ccClosure:
+  if p.withinBlockLeaveActions > 0 and isInactiveDestructorCall(p, ri):
+    return
+  if ri[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure:
     genClosureCall(p, le, ri, d)
-  elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags:
+  elif ri[0].kind == nkSym and sfInfixCall in ri[0].sym.flags:
     genInfixCall(p, le, ri, d)
-  elif ri.sons[0].kind == nkSym and sfNamedParamCall in ri.sons[0].sym.flags:
+  elif ri[0].kind == nkSym and sfNamedParamCall in ri[0].sym.flags:
     genNamedParamCall(p, ri, d)
   else:
     genPrefixCall(p, le, ri, d)
-  postStmtActions(p)
+
+proc genCall(p: BProc, e: PNode, d: var TLoc) = genAsgnCall(p, nil, e, d)
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 82cc3a1fb..545d43ae8 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -9,125 +9,148 @@
 
 # included from cgen.nim
 
+when defined(nimCompilerStacktraceHints):
+  import std/stackframes
+
+proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode,
+                      result: var Rope; count: var int;
+                      isConst: bool, info: TLineInfo)
+
 # -------------------------- constant expressions ------------------------
 
-proc int64Literal(i: BiggestInt): Rope =
+proc rdSetElemLoc(conf: ConfigRef; a: TLoc, typ: PType; result: var Rope)
+
+proc int64Literal(i: BiggestInt; result: var Rope) =
   if i > low(int64):
-    result = "IL64($1)" % [rope(i)]
+    result.add "IL64($1)" % [rope(i)]
   else:
-    result = ~"(IL64(-9223372036854775807) - IL64(1))"
+    result.add "(IL64(-9223372036854775807) - IL64(1))"
 
-proc uint64Literal(i: uint64): Rope = rope($i & "ULL")
+proc uint64Literal(i: uint64; result: var Rope) = result.add rope($i & "ULL")
 
-proc intLiteral(i: BiggestInt): Rope =
+proc intLiteral(i: BiggestInt; result: var Rope) =
   if i > low(int32) and i <= high(int32):
-    result = rope(i)
+    result.add rope(i)
   elif i == low(int32):
     # Nim has the same bug for the same reasons :-)
-    result = ~"(-2147483647 -1)"
+    result.add "(-2147483647 -1)"
   elif i > low(int64):
-    result = "IL64($1)" % [rope(i)]
+    result.add "IL64($1)" % [rope(i)]
   else:
-    result = ~"(IL64(-9223372036854775807) - IL64(1))"
+    result.add "(IL64(-9223372036854775807) - IL64(1))"
 
-proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
-  if ty == nil: internalError(p.config, n.info, "genLiteral: ty is nil")
+proc intLiteral(i: Int128; result: var Rope) =
+  intLiteral(toInt64(i), result)
+
+proc genLiteral(p: BProc, n: PNode, ty: PType; result: var Rope) =
   case n.kind
   of nkCharLit..nkUInt64Lit:
-    case skipTypes(ty, abstractVarRange).kind
+    var k: TTypeKind
+    if ty != nil:
+      k = skipTypes(ty, abstractVarRange).kind
+    else:
+      case n.kind
+      of nkCharLit: k = tyChar
+      of nkUInt64Lit: k = tyUInt64
+      of nkInt64Lit: k = tyInt64
+      else: k = tyNil # don't go into the case variant that uses 'ty'
+    case k
     of tyChar, tyNil:
-      result = intLiteral(n.intVal)
+      intLiteral(n.intVal, result)
     of tyBool:
-      if n.intVal != 0: result = ~"NIM_TRUE"
-      else: result = ~"NIM_FALSE"
-    of tyInt64: result = int64Literal(n.intVal)
-    of tyUInt64: result = uint64Literal(uint64(n.intVal))
+      if n.intVal != 0: result.add "NIM_TRUE"
+      else: result.add "NIM_FALSE"
+    of tyInt64: int64Literal(n.intVal, result)
+    of tyUInt64: uint64Literal(uint64(n.intVal), result)
     else:
-      result = "(($1) $2)" % [getTypeDesc(p.module,
-          ty), intLiteral(n.intVal)]
+      result.add "(("
+      result.add getTypeDesc(p.module, ty)
+      result.add ")"
+      intLiteral(n.intVal, result)
+      result.add ")"
   of nkNilLit:
-    let t = skipTypes(ty, abstractVarRange)
-    if t.kind == tyProc and t.callConv == ccClosure:
+    let k = if ty == nil: tyPointer else: skipTypes(ty, abstractVarRange).kind
+    if k == tyProc and skipTypes(ty, abstractVarRange).callConv == ccClosure:
       let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
-      result = p.module.tmpBase & rope(id)
+      let tmpName = p.module.tmpBase & rope(id)
       if id == p.module.labels:
         # not found in cache:
         inc(p.module.labels)
-        addf(p.module.s[cfsData],
+        p.module.s[cfsStrData].addf(
              "static NIM_CONST $1 $2 = {NIM_NIL,NIM_NIL};$n",
-             [getTypeDesc(p.module, ty), result])
+             [getTypeDesc(p.module, ty), tmpName])
+      result.add tmpName
+    elif k in {tyPointer, tyNil, tyProc}:
+      result.add rope("NIM_NIL")
     else:
-      result = rope("NIM_NIL")
+      result.add "(($1) NIM_NIL)" % [getTypeDesc(p.module, ty)]
   of nkStrLit..nkTripleStrLit:
-    case skipTypes(ty, abstractVarRange).kind
+    let k = if ty == nil: tyString
+            else: skipTypes(ty, abstractVarRange + {tyStatic, tyUserTypeClass, tyUserTypeClassInst}).kind
+    case k
     of tyNil:
-      result = genNilStringLiteral(p.module, n.info)
+      genNilStringLiteral(p.module, n.info, result)
     of tyString:
-      # with the new semantics for 'nil' strings, we can map "" to nil and
+      # with the new semantics for not 'nil' strings, we can map "" to nil and
       # save tons of allocations:
-      #if n.strVal.len == 0: result = genNilStringLiteral(p.module, n.info)
-      #else:
-      result = genStringLiteral(p.module, n)
+      if n.strVal.len == 0 and optSeqDestructors notin p.config.globalOptions:
+        genNilStringLiteral(p.module, n.info, result)
+      else:
+        genStringLiteral(p.module, n, result)
     else:
-      if n.strVal.isNil: result = rope("NIM_NIL")
-      else: result = makeCString(n.strVal)
+      result.add makeCString(n.strVal)
   of nkFloatLit, nkFloat64Lit:
-    result = rope(n.floatVal.toStrMaxPrecision)
+    if ty.kind == tyFloat32:
+      result.add rope(n.floatVal.float32.toStrMaxPrecision)
+    else:
+      result.add rope(n.floatVal.toStrMaxPrecision)
   of nkFloat32Lit:
-    result = rope(n.floatVal.toStrMaxPrecision("f"))
+    result.add rope(n.floatVal.float32.toStrMaxPrecision)
   else:
     internalError(p.config, n.info, "genLiteral(" & $n.kind & ')')
-    result = nil
-
-proc genLiteral(p: BProc, n: PNode): Rope =
-  result = genLiteral(p, n, n.typ)
 
-proc bitSetToWord(s: TBitSet, size: int): BiggestInt =
-  result = 0
-  when true:
-    for j in countup(0, size - 1):
-      if j < len(s): result = result or `shl`(ze64(s[j]), j * 8)
-  else:
-    # not needed, too complex thinking:
-    if CPU[platform.hostCPU].endian == CPU[targetCPU].endian:
-      for j in countup(0, size - 1):
-        if j < len(s): result = result or `shl`(Ze64(s[j]), j * 8)
-    else:
-      for j in countup(0, size - 1):
-        if j < len(s): result = result or `shl`(Ze64(s[j]), (Size - 1 - j) * 8)
+proc genLiteral(p: BProc, n: PNode; result: var Rope) =
+  genLiteral(p, n, n.typ, result)
 
-proc genRawSetData(cs: TBitSet, size: int): Rope =
-  var frmt: FormatStr
+proc genRawSetData(cs: TBitSet, size: int; result: var Rope) =
   if size > 8:
-    result = "{$n" % []
-    for i in countup(0, size - 1):
+    var res = "{\n"
+    for i in 0..<size:
+      res.add "0x"
+      res.add "0123456789abcdef"[cs[i] div 16]
+      res.add "0123456789abcdef"[cs[i] mod 16]
       if i < size - 1:
-        # not last iteration?
-        if (i + 1) mod 8 == 0: frmt = "0x$1,$n"
-        else: frmt = "0x$1, "
+        # not last iteration
+        if i mod 8 == 7:
+          res.add ",\n"
+        else:
+          res.add ", "
       else:
-        frmt = "0x$1}$n"
-      addf(result, frmt, [rope(toHex(ze64(cs[i]), 2))])
+        res.add "}\n"
+
+    result.add rope(res)
   else:
-    result = intLiteral(bitSetToWord(cs, size))
-    #  result := rope('0x' + ToHex(bitSetToWord(cs, size), size * 2))
+    intLiteral(cast[BiggestInt](bitSetToWord(cs, size)), result)
 
-proc genSetNode(p: BProc, n: PNode): Rope =
-  var cs: TBitSet
+proc genSetNode(p: BProc, n: PNode; result: var Rope) =
   var size = int(getSize(p.config, n.typ))
-  toBitSet(p.config, n, cs)
+  let cs = toBitSet(p.config, n)
   if size > 8:
     let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
-    result = p.module.tmpBase & rope(id)
+    let tmpName = p.module.tmpBase & rope(id)
     if id == p.module.labels:
       # not found in cache:
       inc(p.module.labels)
-      addf(p.module.s[cfsData], "static NIM_CONST $1 $2 = $3;$n",
-           [getTypeDesc(p.module, n.typ), result, genRawSetData(cs, size)])
+      p.module.s[cfsStrData].addf("static NIM_CONST $1 $2 = ",
+           [getTypeDesc(p.module, n.typ), tmpName])
+      genRawSetData(cs, size, p.module.s[cfsStrData])
+      p.module.s[cfsStrData].addf(";$n", [])
+    result.add tmpName
   else:
-    result = genRawSetData(cs, size)
+    genRawSetData(cs, size, result)
 
 proc getStorageLoc(n: PNode): TStorageLoc =
+  ## deadcode
   case n.kind
   of nkSym:
     case n.sym.kind
@@ -141,16 +164,18 @@ proc getStorageLoc(n: PNode): TStorageLoc =
       else: result = OnUnknown
     else: result = OnUnknown
   of nkDerefExpr, nkHiddenDeref:
-    case n.sons[0].typ.kind
+    case n[0].typ.kind
     of tyVar, tyLent: result = OnUnknown
     of tyPtr: result = OnStack
     of tyRef: result = OnHeap
-    else: doAssert(false, "getStorageLoc")
+    else:
+      result = OnUnknown
+      doAssert(false, "getStorageLoc")
   of nkBracketExpr, nkDotExpr, nkObjDownConv, nkObjUpConv:
-    result = getStorageLoc(n.sons[0])
+    result = getStorageLoc(n[0])
   else: result = OnUnknown
 
-proc canMove(n: PNode): bool =
+proc canMove(p: BProc, n: PNode; dest: TLoc): bool =
   # for now we're conservative here:
   if n.kind == nkBracket:
     # This needs to be kept consistent with 'const' seq code
@@ -158,40 +183,26 @@ proc canMove(n: PNode): bool =
     if not isDeepConstExpr(n) or n.len == 0:
       if skipTypes(n.typ, abstractVarRange).kind == tySequence:
         return true
+  elif n.kind in nkStrKinds and n.strVal.len == 0:
+    # Empty strings are codegen'd as NIM_NIL so it's just a pointer copy
+    return true
   result = n.kind in nkCallKinds
+  #if not result and dest.k == locTemp:
+  #  return true
+
   #if result:
   #  echo n.info, " optimized ", n
   #  result = false
 
-proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
-  if dest.storage == OnStack or not usesNativeGC(p.config):
-    linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
+proc genRefAssign(p: BProc, dest, src: TLoc) =
+  if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config):
+    linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
   elif dest.storage == OnHeap:
-    # location is on heap
-    # now the writer barrier is inlined for performance:
-    #
-    #    if afSrcIsNotNil in flags:
-    #      UseMagic(p.module, 'nimGCref')
-    #      lineF(p, cpsStmts, 'nimGCref($1);$n', [rdLoc(src)])
-    #    elif afSrcIsNil notin flags:
-    #      UseMagic(p.module, 'nimGCref')
-    #      lineF(p, cpsStmts, 'if ($1) nimGCref($1);$n', [rdLoc(src)])
-    #    if afDestIsNotNil in flags:
-    #      UseMagic(p.module, 'nimGCunref')
-    #      lineF(p, cpsStmts, 'nimGCunref($1);$n', [rdLoc(dest)])
-    #    elif afDestIsNil notin flags:
-    #      UseMagic(p.module, 'nimGCunref')
-    #      lineF(p, cpsStmts, 'if ($1) nimGCunref($1);$n', [rdLoc(dest)])
-    #    lineF(p, cpsStmts, '$1 = $2;$n', [rdLoc(dest), rdLoc(src)])
-    if canFormAcycle(dest.t):
-      linefmt(p, cpsStmts, "#asgnRef((void**) $1, $2);$n",
-              addrLoc(p.config, dest), rdLoc(src))
-    else:
-      linefmt(p, cpsStmts, "#asgnRefNoCycle((void**) $1, $2);$n",
-              addrLoc(p.config, dest), rdLoc(src))
+    linefmt(p, cpsStmts, "#asgnRef((void**) $1, $2);$n",
+            [addrLoc(p.config, dest), rdLoc(src)])
   else:
     linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n",
-            addrLoc(p.config, dest), rdLoc(src))
+            [addrLoc(p.config, dest), rdLoc(src)])
 
 proc asgnComplexity(n: PNode): int =
   if n != nil:
@@ -201,16 +212,20 @@ proc asgnComplexity(n: PNode): int =
       # 'case objects' are too difficult to inline their assignment operation:
       result = 100
     of nkRecList:
+      result = 0
       for t in items(n):
         result += asgnComplexity(t)
-    else: discard
+    else: result = 0
+  else:
+    result = 0
 
 proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc =
-  assert field != nil
-  result.k = locField
-  result.storage = a.storage
-  result.lode = lodeTyp t
-  result.r = rdLoc(a) & "." & field
+  assert field != ""
+  result = TLoc(k: locField,
+    storage: a.storage,
+    lode: lodeTyp t,
+    snippet: rdLoc(a) & "." & field
+  )
 
 proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   let newflags =
@@ -221,8 +236,7 @@ proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     else:
       flags
   let t = skipTypes(dest.t, abstractInst).getUniqueType()
-  for i in 0 ..< t.len:
-    let t = t.sons[i]
+  for i, t in t.ikids:
     let field = "Field$1" % [i.rope]
     genAssignment(p, optAsgnLoc(dest, t, field),
                      optAsgnLoc(src, t, field), newflags)
@@ -240,9 +254,9 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags,
   case t.kind
   of nkSym:
     let field = t.sym
-    if field.loc.r == nil: fillObjectFields(p.module, typ)
-    genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.r),
-                     optAsgnLoc(src, field.typ, field.loc.r), newflags)
+    if field.loc.snippet == "": fillObjectFields(p.module, typ)
+    genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.snippet),
+                     optAsgnLoc(src, field.typ, field.loc.snippet), newflags)
   of nkRecList:
     for child in items(t): genOptAsgnObject(p, dest, src, newflags, child, typ)
   else: discard
@@ -254,163 +268,208 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   # tfShallow flag for the built-in string type too! So we check only
   # here for this flag, where it is reasonably safe to do so
   # (for objects, etc.):
-  if needToCopy notin flags or
+  if optSeqDestructors in p.config.globalOptions:
+    linefmt(p, cpsStmts,
+        "$1 = $2;$n",
+        [rdLoc(dest), rdLoc(src)])
+  elif needToCopy notin flags or
       tfShallow in skipTypes(dest.t, abstractVarRange).flags:
-    if dest.storage == OnStack or not usesNativeGC(p.config):
-      useStringh(p.module)
+    if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config):
       linefmt(p, cpsStmts,
-           "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
-           addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest))
+           "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
+           [addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest)])
     else:
       linefmt(p, cpsStmts, "#genericShallowAssign((void*)$1, (void*)$2, $3);$n",
-              addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfo(p.module, dest.t, dest.lode.info))
+              [addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfoV1(p.module, dest.t, dest.lode.info)])
   else:
     linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n",
-            addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfo(p.module, dest.t, dest.lode.info))
+            [addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfoV1(p.module, dest.t, dest.lode.info)])
+
+proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc; flags: TAssignmentFlags) =
+  assert d.k != locNone
+  #  getTemp(p, d.t, d)
+
+  case a.t.skipTypes(abstractVar).kind
+  of tyOpenArray, tyVarargs:
+    if reifiedOpenArray(a.lode):
+      if needTempForOpenArray in flags:
+        var tmp: TLoc = getTemp(p, a.t)
+        linefmt(p, cpsStmts, "$2 = $1; $n",
+                [a.rdLoc, tmp.rdLoc])
+        linefmt(p, cpsStmts, "$1.Field0 = $2.Field0; $1.Field1 = $2.Field1;$n",
+          [rdLoc(d), tmp.rdLoc])
+      else:
+        linefmt(p, cpsStmts, "$1.Field0 = $2.Field0; $1.Field1 = $2.Field1;$n",
+          [rdLoc(d), a.rdLoc])
+    else:
+      linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $2Len_0;$n",
+        [rdLoc(d), a.rdLoc])
+  of tySequence:
+    linefmt(p, cpsStmts, "$1.Field0 = ($5) ? ($2$3) : NIM_NIL; $1.Field1 = $4;$n",
+      [rdLoc(d), a.rdLoc, dataField(p), lenExpr(p, a), dataFieldAccessor(p, a.rdLoc)])
+  of tyArray:
+    linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $3;$n",
+      [rdLoc(d), rdLoc(a), rope(lengthOrd(p.config, a.t))])
+  of tyString:
+    let etyp = skipTypes(a.t, abstractInst)
+    if etyp.kind in {tyVar} and optSeqDestructors in p.config.globalOptions:
+      linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
+
+    linefmt(p, cpsStmts, "$1.Field0 = ($5) ? ($2$3) : NIM_NIL; $1.Field1 = $4;$n",
+      [rdLoc(d), a.rdLoc, dataField(p), lenExpr(p, a), dataFieldAccessor(p, a.rdLoc)])
+  else:
+    internalError(p.config, a.lode.info, "cannot handle " & $a.t.kind)
 
 proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   # This function replaces all other methods for generating
   # the assignment operation in C.
   if src.t != nil and src.t.kind == tyPtr:
     # little HACK to support the new 'var T' as return type:
-    linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
+    linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
     return
   let ty = skipTypes(dest.t, abstractRange + tyUserTypeClasses + {tyStatic})
   case ty.kind
   of tyRef:
-    genRefAssign(p, dest, src, flags)
+    genRefAssign(p, dest, src)
   of tySequence:
-    if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode):
-      genRefAssign(p, dest, src, flags)
+    if optSeqDestructors in p.config.globalOptions:
+      genGenericAsgn(p, dest, src, flags)
+    elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode, dest):
+      genRefAssign(p, dest, src)
     else:
       linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n",
-              addrLoc(p.config, dest), rdLoc(src),
-              genTypeInfo(p.module, dest.t, dest.lode.info))
+              [addrLoc(p.config, dest), rdLoc(src),
+              genTypeInfoV1(p.module, dest.t, dest.lode.info)])
   of tyString:
-    if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode):
-      genRefAssign(p, dest, src, flags)
+    if optSeqDestructors in p.config.globalOptions:
+      genGenericAsgn(p, dest, src, flags)
+    elif ({needToCopy, needToCopySinkParam} * flags == {} and src.storage != OnStatic) or canMove(p, src.lode, dest):
+      genRefAssign(p, dest, src)
     else:
-      if dest.storage == OnStack or not usesNativeGC(p.config):
-        linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc)
+      if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config):
+        linefmt(p, cpsStmts, "$1 = #copyString($2);$n", [dest.rdLoc, src.rdLoc])
       elif dest.storage == OnHeap:
         # we use a temporary to care for the dreaded self assignment:
-        var tmp: TLoc
-        getTemp(p, ty, tmp)
+        var tmp: TLoc = getTemp(p, ty)
         linefmt(p, cpsStmts, "$3 = $1; $1 = #copyStringRC1($2);$n",
-                dest.rdLoc, src.rdLoc, tmp.rdLoc)
-        linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", tmp.rdLoc)
+                [dest.rdLoc, src.rdLoc, tmp.rdLoc])
+        linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", [tmp.rdLoc])
       else:
         linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n",
-               addrLoc(p.config, dest), rdLoc(src))
+               [addrLoc(p.config, dest), rdLoc(src)])
   of tyProc:
-    if needsComplexAssignment(dest.t):
+    if containsGarbageCollectedRef(dest.t):
       # optimize closure assignment:
       let a = optAsgnLoc(dest, dest.t, "ClE_0".rope)
       let b = optAsgnLoc(src, dest.t, "ClE_0".rope)
-      genRefAssign(p, a, b, flags)
-      linefmt(p, cpsStmts, "$1.ClP_0 = $2.ClP_0;$n", rdLoc(dest), rdLoc(src))
+      genRefAssign(p, a, b)
+      linefmt(p, cpsStmts, "$1.ClP_0 = $2.ClP_0;$n", [rdLoc(dest), rdLoc(src)])
     else:
-      linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
+      linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
   of tyTuple:
-    if needsComplexAssignment(dest.t):
-      if dest.t.len <= 4: genOptAsgnTuple(p, dest, src, flags)
+    if containsGarbageCollectedRef(dest.t):
+      if dest.t.kidsLen <= 4: genOptAsgnTuple(p, dest, src, flags)
       else: genGenericAsgn(p, dest, src, flags)
     else:
-      linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
+      linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
   of tyObject:
     # XXX: check for subtyping?
     if ty.isImportedCppType:
-      linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
+      linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
     elif not isObjLackingTypeField(ty):
       genGenericAsgn(p, dest, src, flags)
-    elif needsComplexAssignment(ty):
-      if ty.sons[0].isNil and asgnComplexity(ty.n) <= 4:
+    elif containsGarbageCollectedRef(ty):
+      if ty[0].isNil and asgnComplexity(ty.n) <= 4 and
+            needAssignCall notin flags: # calls might contain side effects
         discard getTypeDesc(p.module, ty)
         internalAssert p.config, ty.n != nil
         genOptAsgnObject(p, dest, src, flags, ty.n, ty)
       else:
         genGenericAsgn(p, dest, src, flags)
     else:
-      linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
+      linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
   of tyArray:
-    if needsComplexAssignment(dest.t):
+    if containsGarbageCollectedRef(dest.t) and p.config.selectedGC notin {gcArc, gcAtomicArc, gcOrc, gcHooks}:
       genGenericAsgn(p, dest, src, flags)
     else:
-      useStringh(p.module)
       linefmt(p, cpsStmts,
-           "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
-           rdLoc(dest), rdLoc(src), getTypeDesc(p.module, dest.t))
+           "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
+           [rdLoc(dest), rdLoc(src), getTypeDesc(p.module, dest.t)])
   of tyOpenArray, tyVarargs:
     # open arrays are always on the stack - really? What if a sequence is
     # passed to an open array?
-    if needsComplexAssignment(dest.t):
+    if reifiedOpenArray(dest.lode):
+      genOpenArrayConv(p, dest, src, flags)
+    elif containsGarbageCollectedRef(dest.t):
       linefmt(p, cpsStmts,     # XXX: is this correct for arrays?
            "#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
-           addrLoc(p.config, dest), addrLoc(p.config, src),
-           genTypeInfo(p.module, dest.t, dest.lode.info))
+           [addrLoc(p.config, dest), addrLoc(p.config, src),
+           genTypeInfoV1(p.module, dest.t, dest.lode.info)])
     else:
-      useStringh(p.module)
       linefmt(p, cpsStmts,
-           # bug #4799, keep the memcpy for a while
-           #"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len_0);$n",
+           # bug #4799, keep the nimCopyMem for a while
+           #"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len_0);\n",
            "$1 = $2;$n",
-           rdLoc(dest), rdLoc(src))
+           [rdLoc(dest), rdLoc(src)])
   of tySet:
-    if mapType(p.config, ty) == ctArray:
-      useStringh(p.module)
-      linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n",
-              rdLoc(dest), rdLoc(src), rope(getSize(p.config, dest.t)))
+    if mapSetType(p.config, ty) == ctArray:
+      linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, $3);$n",
+              [rdLoc(dest), rdLoc(src), getSize(p.config, dest.t)])
     else:
-      linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
-  of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCString,
-     tyInt..tyUInt64, tyRange, tyVar, tyLent:
-    linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
+      linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
+  of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCstring,
+     tyInt..tyUInt64, tyRange, tyVar, tyLent, tyNil:
+    linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
   else: internalError(p.config, "genAssignment: " & $ty.kind)
 
   if optMemTracker in p.options and dest.storage in {OnHeap, OnUnknown}:
     #writeStackTrace()
     #echo p.currLineInfo, " requesting"
     linefmt(p, cpsStmts, "#memTrackerWrite((void*)$1, $2, $3, $4);$n",
-            addrLoc(p.config, dest), rope getSize(p.config, dest.t),
+            [addrLoc(p.config, dest), getSize(p.config, dest.t),
             makeCString(toFullPath(p.config, p.currLineInfo)),
-            rope p.currLineInfo.safeLineNm)
+            p.currLineInfo.safeLineNm])
 
 proc genDeepCopy(p: BProc; dest, src: TLoc) =
   template addrLocOrTemp(a: TLoc): Rope =
     if a.k == locExpr:
-      var tmp: TLoc
-      getTemp(p, a.t, tmp)
+      var tmp: TLoc = getTemp(p, a.t)
       genAssignment(p, tmp, a, {})
       addrLoc(p.config, tmp)
     else:
       addrLoc(p.config, a)
 
-  var ty = skipTypes(dest.t, abstractVarRange)
+  var ty = skipTypes(dest.t, abstractVarRange + {tyStatic})
   case ty.kind
   of tyPtr, tyRef, tyProc, tyTuple, tyObject, tyArray:
     # XXX optimize this
     linefmt(p, cpsStmts, "#genericDeepCopy((void*)$1, (void*)$2, $3);$n",
-            addrLoc(p.config, dest), addrLocOrTemp(src),
-            genTypeInfo(p.module, dest.t, dest.lode.info))
+            [addrLoc(p.config, dest), addrLocOrTemp(src),
+            genTypeInfoV1(p.module, dest.t, dest.lode.info)])
   of tySequence, tyString:
-    linefmt(p, cpsStmts, "#genericSeqDeepCopy($1, $2, $3);$n",
-            addrLoc(p.config, dest), rdLoc(src),
-            genTypeInfo(p.module, dest.t, dest.lode.info))
+    if optTinyRtti in p.config.globalOptions:
+      linefmt(p, cpsStmts, "#genericDeepCopy((void*)$1, (void*)$2, $3);$n",
+              [addrLoc(p.config, dest), addrLocOrTemp(src),
+              genTypeInfoV1(p.module, dest.t, dest.lode.info)])
+    else:
+      linefmt(p, cpsStmts, "#genericSeqDeepCopy($1, $2, $3);$n",
+              [addrLoc(p.config, dest), rdLoc(src),
+              genTypeInfoV1(p.module, dest.t, dest.lode.info)])
   of tyOpenArray, tyVarargs:
+    let source = addrLocOrTemp(src)
     linefmt(p, cpsStmts,
-         "#genericDeepCopyOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
-         addrLoc(p.config, dest), addrLocOrTemp(src),
-         genTypeInfo(p.module, dest.t, dest.lode.info))
+         "#genericDeepCopyOpenArray((void*)$1, (void*)$2, $2->Field1, $3);$n",
+         [addrLoc(p.config, dest), source,
+         genTypeInfoV1(p.module, dest.t, dest.lode.info)])
   of tySet:
-    if mapType(p.config, ty) == ctArray:
-      useStringh(p.module)
-      linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n",
-              rdLoc(dest), rdLoc(src), rope(getSize(p.config, dest.t)))
+    if mapSetType(p.config, ty) == ctArray:
+      linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, $3);$n",
+              [rdLoc(dest), rdLoc(src), getSize(p.config, dest.t)])
     else:
-      linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
-  of tyPointer, tyChar, tyBool, tyEnum, tyCString,
+      linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
+  of tyPointer, tyChar, tyBool, tyEnum, tyCstring,
      tyInt..tyUInt64, tyRange, tyVar, tyLent:
-    linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
+    linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
   else: internalError(p.config, "genDeepCopy: " & $ty.kind)
 
 proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) =
@@ -421,11 +480,10 @@ proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) =
     d = s # ``d`` is free, so fill it with ``s``
 
 proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) =
-  var a: TLoc
   if d.k != locNone:
+    var a: TLoc = initLoc(locData, n, OnStatic)
     # need to generate an assignment here
-    initLoc(a, locData, n, OnStatic)
-    a.r = r
+    a.snippet = r
     if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
     else: genAssignment(p, d, a, {needToCopy})
   else:
@@ -433,14 +491,13 @@ proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) =
     # the flags field!
     d.k = locData
     d.lode = n
-    d.r = r
+    d.snippet = r
 
 proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) =
-  var a: TLoc
   if d.k != locNone:
     # need to generate an assignment here
-    initLoc(a, locExpr, n, s)
-    a.r = r
+    var a: TLoc = initLoc(locExpr, n, s)
+    a.snippet = r
     if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
     else: genAssignment(p, d, a, {needToCopy})
   else:
@@ -448,245 +505,264 @@ proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) =
     # the flags field!
     d.k = locExpr
     d.lode = n
-    d.r = r
+    d.snippet = r
 
-proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
-  var a, b: TLoc
+proc binaryStmt(p: BProc, e: PNode, d: var TLoc, op: string) =
   if d.k != locNone: internalError(p.config, e.info, "binaryStmt")
-  initLocExpr(p, e.sons[1], a)
-  initLocExpr(p, e.sons[2], b)
-  lineCg(p, cpsStmts, frmt, rdLoc(a), rdLoc(b))
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
+  lineCg(p, cpsStmts, "$1 $2 $3;$n", [rdLoc(a), op, rdLoc(b)])
 
-proc unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
-  var a: TLoc
+proc binaryStmtAddr(p: BProc, e: PNode, d: var TLoc, cpname: string) =
+  if d.k != locNone: internalError(p.config, e.info, "binaryStmtAddr")
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
+  lineCg(p, cpsStmts, "#$1($2, $3);$n", [cpname, byRefLoc(p, a), rdLoc(b)])
+
+template unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
   if d.k != locNone: internalError(p.config, e.info, "unaryStmt")
-  initLocExpr(p, e.sons[1], a)
+  var a: TLoc = initLocExpr(p, e[1])
   lineCg(p, cpsStmts, frmt, [rdLoc(a)])
 
-proc binaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) =
-  var a, b: TLoc
-  assert(e.sons[1].typ != nil)
-  assert(e.sons[2].typ != nil)
-  initLocExpr(p, e.sons[1], a)
-  initLocExpr(p, e.sons[2], b)
+template binaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) =
+  assert(e[1].typ != nil)
+  assert(e[2].typ != nil)
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
   putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a), rdLoc(b)]))
 
-proc binaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
-  var a, b: TLoc
-  assert(e.sons[1].typ != nil)
-  assert(e.sons[2].typ != nil)
-  initLocExpr(p, e.sons[1], a)
-  initLocExpr(p, e.sons[2], b)
+template binaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
+  assert(e[1].typ != nil)
+  assert(e[2].typ != nil)
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
   putIntoDest(p, d, e, ropecg(p.module, frmt, [a.rdCharLoc, b.rdCharLoc]))
 
-proc unaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) =
-  var a: TLoc
-  initLocExpr(p, e.sons[1], a)
+template unaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) =
+  var a: TLoc = initLocExpr(p, e[1])
   putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a)]))
 
-proc unaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
-  var a: TLoc
-  initLocExpr(p, e.sons[1], a)
+template unaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
+  var a: TLoc = initLocExpr(p, e[1])
   putIntoDest(p, d, e, ropecg(p.module, frmt, [rdCharLoc(a)]))
 
-proc binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc;
-                            frmt: string): Rope =
+template binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc;
+                            cpname: string): Rope =
   var size = getSize(p.config, t)
   let storage = if size < p.config.target.intSize: rope("NI")
                 else: getTypeDesc(p.module, t)
-  result = getTempName(p.module)
-  linefmt(p, cpsLocals, "$1 $2;$n", storage, result)
-  lineCg(p, cpsStmts, frmt, result, rdCharLoc(a), rdCharLoc(b))
+  var result = getTempName(p.module)
+  linefmt(p, cpsLocals, "$1 $2;$n", [storage, result])
+  lineCg(p, cpsStmts, "if (#$2($3, $4, &$1)) { #raiseOverflow(); ",
+      [result, cpname, rdCharLoc(a), rdCharLoc(b)])
+  raiseInstr(p, p.s(cpsStmts))
+  linefmt p, cpsStmts, "};$n", []
+
   if size < p.config.target.intSize or t.kind in {tyRange, tyEnum}:
-    linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseOverflow();$n",
-            result, intLiteral(firstOrd(p.config, t)), intLiteral(lastOrd(p.config, t)))
+    var first = newRopeAppender()
+    intLiteral(firstOrd(p.config, t), first)
+    var last = newRopeAppender()
+    intLiteral(lastOrd(p.config, t), last)
+    linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3){ #raiseOverflow(); ",
+            [result, first, last])
+    raiseInstr(p, p.s(cpsStmts))
+    linefmt p, cpsStmts, "}$n", []
+
+  result
 
 proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   const
     prc: array[mAddI..mPred, string] = [
-      "$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n",
-      "$# = #mulInt($#, $#);$n", "$# = #divInt($#, $#);$n",
-      "$# = #modInt($#, $#);$n",
-      "$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n"]
+      "nimAddInt", "nimSubInt",
+      "nimMulInt", "nimDivInt", "nimModInt",
+      "nimAddInt", "nimSubInt"
+    ]
     prc64: array[mAddI..mPred, string] = [
-      "$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n",
-      "$# = #mulInt64($#, $#);$n", "$# = #divInt64($#, $#);$n",
-      "$# = #modInt64($#, $#);$n",
-      "$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n"]
-    opr: array[mAddI..mPred, string] = [
-      "($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)",
-      "($#)($# / $#)", "($#)($# % $#)",
-      "($#)($# + $#)", "($#)($# - $#)"]
-  var a, b: TLoc
-  assert(e.sons[1].typ != nil)
-  assert(e.sons[2].typ != nil)
-  initLocExpr(p, e.sons[1], a)
-  initLocExpr(p, e.sons[2], b)
+      "nimAddInt64", "nimSubInt64",
+      "nimMulInt64", "nimDivInt64", "nimModInt64",
+      "nimAddInt64", "nimSubInt64"
+    ]
+    opr: array[mAddI..mPred, string] = ["+", "-", "*", "/", "%", "+", "-"]
+  assert(e[1].typ != nil)
+  assert(e[2].typ != nil)
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
   # skipping 'range' is correct here as we'll generate a proper range check
   # later via 'chckRange'
   let t = e.typ.skipTypes(abstractRange)
-  if optOverflowCheck notin p.options:
-    let res = opr[m] % [getTypeDesc(p.module, e.typ), rdLoc(a), rdLoc(b)]
+  if optOverflowCheck notin p.options or (m in {mSucc, mPred} and t.kind in {tyUInt..tyUInt64}):
+    let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
     putIntoDest(p, d, e, res)
   else:
-    let res = binaryArithOverflowRaw(p, t, a, b,
-                                   if t.kind == tyInt64: prc64[m] else: prc[m])
-    putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res])
+    # we handle div by zero here so that we know that the compilerproc's
+    # result is only for overflows.
+    var needsOverflowCheck = true
+    if m in {mDivI, mModI}:
+      var canBeZero = true
+      if e[2].kind in {nkIntLit..nkUInt64Lit}:
+        canBeZero = e[2].intVal == 0
+      if e[2].kind in {nkIntLit..nkInt64Lit}:
+        needsOverflowCheck = e[2].intVal == -1
+      if canBeZero:
+        linefmt(p, cpsStmts, "if ($1 == 0){ #raiseDivByZero(); ", [rdLoc(b)])
+        raiseInstr(p, p.s(cpsStmts))
+        linefmt(p, cpsStmts, "}$n", [])
+    if needsOverflowCheck:
+      let res = binaryArithOverflowRaw(p, t, a, b,
+        if t.kind == tyInt64: prc64[m] else: prc[m])
+      putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res])
+    else:
+      let res = "($1)(($2) $3 ($4))" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
+      putIntoDest(p, d, e, res)
 
 proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
-  const
-    opr: array[mUnaryMinusI..mAbsI, string] = [
-      mUnaryMinusI: "((NI$2)-($1))",
-      mUnaryMinusI64: "-($1)",
-      mAbsI: "($1 > 0? ($1) : -($1))"]
-  var
-    a: TLoc
-    t: PType
-  assert(e.sons[1].typ != nil)
-  initLocExpr(p, e.sons[1], a)
+  var t: PType
+  assert(e[1].typ != nil)
+  var a: TLoc = initLocExpr(p, e[1])
   t = skipTypes(e.typ, abstractRange)
   if optOverflowCheck in p.options:
-    linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n",
-            rdLoc(a), intLiteral(firstOrd(p.config, t)))
-  putIntoDest(p, d, e, opr[m] % [rdLoc(a), rope(getSize(p.config, t) * 8)])
+    var first = newRopeAppender()
+    intLiteral(firstOrd(p.config, t), first)
+    linefmt(p, cpsStmts, "if ($1 == $2){ #raiseOverflow(); ",
+            [rdLoc(a), first])
+    raiseInstr(p, p.s(cpsStmts))
+    linefmt p, cpsStmts, "}$n", []
+
+  case m
+  of mUnaryMinusI:
+    putIntoDest(p, d, e, "((NI$2)-($1))" % [rdLoc(a), rope(getSize(p.config, t) * 8)])
+  of mUnaryMinusI64:
+    putIntoDest(p, d, e, "-($1)" % [rdLoc(a)])
+  of mAbsI:
+    putIntoDest(p, d, e, "($1 > 0? ($1) : -($1))" % [rdLoc(a)])
+  else:
+    assert(false, $m)
 
 proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
-  const
-    binArithTab: array[mAddF64..mXor, string] = [
-      "(($4)($1) + ($4)($2))", # AddF64
-      "(($4)($1) - ($4)($2))", # SubF64
-      "(($4)($1) * ($4)($2))", # MulF64
-      "(($4)($1) / ($4)($2))", # DivF64
-
-      "($4)((NU$5)($1) >> (NU$3)($2))", # ShrI
-      "($4)((NU$3)($1) << (NU$3)($2))", # ShlI
-      "($4)($1 & $2)",      # BitandI
-      "($4)($1 | $2)",      # BitorI
-      "($4)($1 ^ $2)",      # BitxorI
-      "(($1 <= $2) ? $1 : $2)", # MinI
-      "(($1 >= $2) ? $1 : $2)", # MaxI
-      "(($1 <= $2) ? $1 : $2)", # MinF64
-      "(($1 >= $2) ? $1 : $2)", # MaxF64
-      "($4)((NU$3)($1) + (NU$3)($2))", # AddU
-      "($4)((NU$3)($1) - (NU$3)($2))", # SubU
-      "($4)((NU$3)($1) * (NU$3)($2))", # MulU
-      "($4)((NU$3)($1) / (NU$3)($2))", # DivU
-      "($4)((NU$3)($1) % (NU$3)($2))", # ModU
-      "($1 == $2)",           # EqI
-      "($1 <= $2)",           # LeI
-      "($1 < $2)",            # LtI
-      "($1 == $2)",           # EqF64
-      "($1 <= $2)",           # LeF64
-      "($1 < $2)",            # LtF64
-      "((NU$3)($1) <= (NU$3)($2))", # LeU
-      "((NU$3)($1) < (NU$3)($2))", # LtU
-      "((NU64)($1) <= (NU64)($2))", # LeU64
-      "((NU64)($1) < (NU64)($2))", # LtU64
-      "($1 == $2)",           # EqEnum
-      "($1 <= $2)",           # LeEnum
-      "($1 < $2)",            # LtEnum
-      "((NU8)($1) == (NU8)($2))", # EqCh
-      "((NU8)($1) <= (NU8)($2))", # LeCh
-      "((NU8)($1) < (NU8)($2))", # LtCh
-      "($1 == $2)",           # EqB
-      "($1 <= $2)",           # LeB
-      "($1 < $2)",            # LtB
-      "($1 == $2)",           # EqRef
-      "($1 == $2)",           # EqPtr
-      "($1 <= $2)",           # LePtr
-      "($1 < $2)",            # LtPtr
-      "($1 != $2)"]           # Xor
   var
-    a, b: TLoc
-    s, k: BiggestInt
-  assert(e.sons[1].typ != nil)
-  assert(e.sons[2].typ != nil)
-  initLocExpr(p, e.sons[1], a)
-  initLocExpr(p, e.sons[2], b)
+    s, k: BiggestInt = 0
+  assert(e[1].typ != nil)
+  assert(e[2].typ != nil)
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
   # BUGFIX: cannot use result-type here, as it may be a boolean
   s = max(getSize(p.config, a.t), getSize(p.config, b.t)) * 8
   k = getSize(p.config, a.t) * 8
-  putIntoDest(p, d, e,
-              binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s),
-                                      getSimpleTypeDesc(p.module, e.typ), rope(k)])
+
+  template applyFormat(frmt: untyped) =
+    putIntoDest(p, d, e, frmt % [
+      rdLoc(a), rdLoc(b), rope(s),
+      getSimpleTypeDesc(p.module, e.typ), rope(k)]
+    )
+
+  case op
+  of mAddF64: applyFormat("(($4)($1) + ($4)($2))")
+  of mSubF64: applyFormat("(($4)($1) - ($4)($2))")
+  of mMulF64: applyFormat("(($4)($1) * ($4)($2))")
+  of mDivF64: applyFormat("(($4)($1) / ($4)($2))")
+  of mShrI: applyFormat("($4)((NU$5)($1) >> (NU$3)($2))")
+  of mShlI: applyFormat("($4)((NU$3)($1) << (NU$3)($2))")
+  of mAshrI: applyFormat("($4)((NI$3)($1) >> (NU$3)($2))")
+  of mBitandI: applyFormat("($4)($1 & $2)")
+  of mBitorI: applyFormat("($4)($1 | $2)")
+  of mBitxorI: applyFormat("($4)($1 ^ $2)")
+  of mMinI: applyFormat("(($1 <= $2) ? $1 : $2)")
+  of mMaxI: applyFormat("(($1 >= $2) ? $1 : $2)")
+  of mAddU: applyFormat("($4)((NU$3)($1) + (NU$3)($2))")
+  of mSubU: applyFormat("($4)((NU$3)($1) - (NU$3)($2))")
+  of mMulU: applyFormat("($4)((NU$3)($1) * (NU$3)($2))")
+  of mDivU: applyFormat("($4)((NU$3)($1) / (NU$3)($2))")
+  of mModU: applyFormat("($4)((NU$3)($1) % (NU$3)($2))")
+  of mEqI: applyFormat("($1 == $2)")
+  of mLeI: applyFormat("($1 <= $2)")
+  of mLtI: applyFormat("($1 < $2)")
+  of mEqF64: applyFormat("($1 == $2)")
+  of mLeF64: applyFormat("($1 <= $2)")
+  of mLtF64: applyFormat("($1 < $2)")
+  of mLeU: applyFormat("((NU$3)($1) <= (NU$3)($2))")
+  of mLtU: applyFormat("((NU$3)($1) < (NU$3)($2))")
+  of mEqEnum: applyFormat("($1 == $2)")
+  of mLeEnum: applyFormat("($1 <= $2)")
+  of mLtEnum: applyFormat("($1 < $2)")
+  of mEqCh: applyFormat("((NU8)($1) == (NU8)($2))")
+  of mLeCh: applyFormat("((NU8)($1) <= (NU8)($2))")
+  of mLtCh: applyFormat("((NU8)($1) < (NU8)($2))")
+  of mEqB: applyFormat("($1 == $2)")
+  of mLeB: applyFormat("($1 <= $2)")
+  of mLtB: applyFormat("($1 < $2)")
+  of mEqRef: applyFormat("($1 == $2)")
+  of mLePtr: applyFormat("($1 <= $2)")
+  of mLtPtr: applyFormat("($1 < $2)")
+  of mXor: applyFormat("($1 != $2)")
+  else:
+    assert(false, $op)
 
 proc genEqProc(p: BProc, e: PNode, d: var TLoc) =
-  var a, b: TLoc
-  assert(e.sons[1].typ != nil)
-  assert(e.sons[2].typ != nil)
-  initLocExpr(p, e.sons[1], a)
-  initLocExpr(p, e.sons[2], b)
-  if a.t.skipTypes(abstractInst).callConv == ccClosure:
+  assert(e[1].typ != nil)
+  assert(e[2].typ != nil)
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
+  if a.t.skipTypes(abstractInstOwned).callConv == ccClosure:
     putIntoDest(p, d, e,
       "($1.ClP_0 == $2.ClP_0 && $1.ClE_0 == $2.ClE_0)" % [rdLoc(a), rdLoc(b)])
   else:
     putIntoDest(p, d, e, "($1 == $2)" % [rdLoc(a), rdLoc(b)])
 
 proc genIsNil(p: BProc, e: PNode, d: var TLoc) =
-  let t = skipTypes(e.sons[1].typ, abstractRange)
+  let t = skipTypes(e[1].typ, abstractRange)
   if t.kind == tyProc and t.callConv == ccClosure:
     unaryExpr(p, e, d, "($1.ClP_0 == 0)")
   else:
     unaryExpr(p, e, d, "($1 == 0)")
 
 proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
-  const
-    unArithTab: array[mNot..mToBiggestInt, string] = ["!($1)", # Not
-      "$1",                   # UnaryPlusI
-      "($3)((NU$2) ~($1))",   # BitnotI
-      "$1",                   # UnaryPlusF64
-      "-($1)",                # UnaryMinusF64
-      "($1 < 0? -($1) : ($1))", # AbsF64; BUGFIX: fabs() makes problems
-                                # for Tiny C, so we don't use it
-      "(($3)(NU)(NU8)($1))",  # mZe8ToI
-      "(($3)(NU64)(NU8)($1))", # mZe8ToI64
-      "(($3)(NU)(NU16)($1))", # mZe16ToI
-      "(($3)(NU64)(NU16)($1))", # mZe16ToI64
-      "(($3)(NU64)(NU32)($1))", # mZe32ToI64
-      "(($3)(NU64)(NU)($1))", # mZeIToI64
-      "(($3)(NU8)(NU)($1))", # ToU8
-      "(($3)(NU16)(NU)($1))", # ToU16
-      "(($3)(NU32)(NU64)($1))", # ToU32
-      "((double) ($1))",      # ToFloat
-      "((double) ($1))",      # ToBiggestFloat
-      "float64ToInt32($1)",   # ToInt
-      "float64ToInt64($1)"]   # ToBiggestInt
   var
-    a: TLoc
     t: PType
-  assert(e.sons[1].typ != nil)
-  initLocExpr(p, e.sons[1], a)
+  assert(e[1].typ != nil)
+  var a = initLocExpr(p, e[1])
   t = skipTypes(e.typ, abstractRange)
-  putIntoDest(p, d, e,
-              unArithTab[op] % [rdLoc(a), rope(getSize(p.config, t) * 8),
+
+  template applyFormat(frmt: untyped) =
+    putIntoDest(p, d, e, frmt % [rdLoc(a), rope(getSize(p.config, t) * 8),
                 getSimpleTypeDesc(p.module, e.typ)])
+  case op
+  of mNot:
+    applyFormat("!($1)")
+  of mUnaryPlusI:
+    applyFormat("$1")
+  of mBitnotI:
+    applyFormat("($3)((NU$2) ~($1))")
+  of mUnaryPlusF64:
+    applyFormat("$1")
+  of mUnaryMinusF64:
+    applyFormat("-($1)")
+  else:
+    assert false, $op
 
 proc isCppRef(p: BProc; typ: PType): bool {.inline.} =
   result = p.module.compileToCpp and
-      skipTypes(typ, abstractInst).kind == tyVar and
-      tfVarIsPtr notin skipTypes(typ, abstractInst).flags
+      skipTypes(typ, abstractInstOwned).kind in {tyVar} and
+      tfVarIsPtr notin skipTypes(typ, abstractInstOwned).flags
 
-proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
-  let mt = mapType(p.config, e.sons[0].typ)
-  if mt in {ctArray, ctPtrToArray} and not enforceDeref:
+proc genDeref(p: BProc, e: PNode, d: var TLoc) =
+  let mt = mapType(p.config, e[0].typ, mapTypeChooser(e[0]) == skParam)
+  if mt in {ctArray, ctPtrToArray} and lfEnforceDeref notin d.flags:
     # XXX the amount of hacks for C's arrays is incredible, maybe we should
     # simply wrap them in a struct? --> Losing auto vectorization then?
-    #if e[0].kind != nkBracketExpr:
-    #  message(e.info, warnUser, "CAME HERE " & renderTree(e))
-    expr(p, e.sons[0], d)
-    if e.sons[0].typ.skipTypes(abstractInst).kind == tyRef:
+    expr(p, e[0], d)
+    if e[0].typ.skipTypes(abstractInstOwned).kind == tyRef:
       d.storage = OnHeap
   else:
     var a: TLoc
-    var typ = e.sons[0].typ
+    var typ = e[0].typ
     if typ.kind in {tyUserTypeClass, tyUserTypeClassInst} and typ.isResolvedUserTypeClass:
-      typ = typ.lastSon
-    typ = typ.skipTypes(abstractInst)
-    if typ.kind == tyVar and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e.sons[0].kind == nkHiddenAddr:
-      initLocExprSingleUse(p, e[0][0], d)
+      typ = typ.last
+    typ = typ.skipTypes(abstractInstOwned)
+    if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e[0].kind == nkHiddenAddr:
+      d = initLocExprSingleUse(p, e[0][0])
       return
     else:
-      initLocExprSingleUse(p, e.sons[0], a)
+      a = initLocExprSingleUse(p, e[0])
     if d.k == locNone:
       # dest = *a;  <-- We do not know that 'dest' is on the heap!
       # It is completely wrong to set 'd.storage' here, unless it's not yet
@@ -705,63 +781,73 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
       else:
         internalError(p.config, e.info, "genDeref " & $typ.kind)
     elif p.module.compileToCpp:
-      if typ.kind == tyVar and tfVarIsPtr notin typ.flags and
+      if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and
            e.kind == nkHiddenDeref:
         putIntoDest(p, d, e, rdLoc(a), a.storage)
         return
-    if enforceDeref and mt == ctPtrToArray:
+    if mt == ctPtrToArray and lfEnforceDeref in d.flags:
       # we lie about the type for better C interop: 'ptr array[3,T]' is
       # translated to 'ptr T', but for deref'ing this produces wrong code.
       # See tmissingderef. So we get rid of the deref instead. The codegen
       # ends up using 'memcpy' for the array assignment,
       # so the '&' and '*' cancel out:
-      putIntoDest(p, d, lodeTyp(a.t.sons[0]), rdLoc(a), a.storage)
+      putIntoDest(p, d, e, rdLoc(a), a.storage)
     else:
       putIntoDest(p, d, e, "(*$1)" % [rdLoc(a)], a.storage)
 
+proc cowBracket(p: BProc; n: PNode) =
+  if n.kind == nkBracketExpr and optSeqDestructors in p.config.globalOptions:
+    let strCandidate = n[0]
+    if strCandidate.typ.skipTypes(abstractInst).kind == tyString:
+      var a: TLoc = initLocExpr(p, strCandidate)
+      linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
+
+proc cow(p: BProc; n: PNode) {.inline.} =
+  if n.kind == nkHiddenAddr: cowBracket(p, n[0])
+
 proc genAddr(p: BProc, e: PNode, d: var TLoc) =
   # careful  'addr(myptrToArray)' needs to get the ampersand:
-  if e.sons[0].typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
-    var a: TLoc
-    initLocExpr(p, e.sons[0], a)
-    putIntoDest(p, d, e, "&" & a.r, a.storage)
+  if e[0].typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr}:
+    var a: TLoc = initLocExpr(p, e[0])
+    putIntoDest(p, d, e, "&" & a.snippet, a.storage)
     #Message(e.info, warnUser, "HERE NEW &")
-  elif mapType(p.config, e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ):
-    expr(p, e.sons[0], d)
+  elif mapType(p.config, e[0].typ, mapTypeChooser(e[0]) == skParam) == ctArray or isCppRef(p, e.typ):
+    expr(p, e[0], d)
+    # bug #19497
+    d.lode = e
   else:
-    var a: TLoc
-    initLocExpr(p, e.sons[0], a)
+    var a: TLoc = initLocExpr(p, e[0])
     putIntoDest(p, d, e, addrLoc(p.config, a), a.storage)
 
 template inheritLocation(d: var TLoc, a: TLoc) =
   if d.k == locNone: d.storage = a.storage
 
-proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc) =
-  initLocExpr(p, e.sons[0], a)
-  if e.sons[1].kind != nkSym: internalError(p.config, e.info, "genRecordFieldAux")
+proc genRecordFieldAux(p: BProc, e: PNode, d: var TLoc, a: var TLoc) =
+  a = initLocExpr(p, e[0])
+  if e[1].kind != nkSym: internalError(p.config, e.info, "genRecordFieldAux")
   d.inheritLocation(a)
   discard getTypeDesc(p.module, a.t) # fill the record's fields.loc
 
 proc genTupleElem(p: BProc, e: PNode, d: var TLoc) =
   var
-    a: TLoc
-    i: int
-  initLocExpr(p, e.sons[0], a)
-  let tupType = a.t.skipTypes(abstractInst)
+    i: int = 0
+  var a: TLoc = initLocExpr(p, e[0])
+  let tupType = a.t.skipTypes(abstractInst+{tyVar})
   assert tupType.kind == tyTuple
   d.inheritLocation(a)
   discard getTypeDesc(p.module, a.t) # fill the record's fields.loc
   var r = rdLoc(a)
-  case e.sons[1].kind
-  of nkIntLit..nkUInt64Lit: i = int(e.sons[1].intVal)
+  case e[1].kind
+  of nkIntLit..nkUInt64Lit: i = int(e[1].intVal)
   else: internalError(p.config, e.info, "genTupleElem")
-  addf(r, ".Field$1", [rope(i)])
+  r.addf(".Field$1", [rope(i)])
   putIntoDest(p, d, e, r, a.storage)
 
 proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope;
                       resTyp: ptr PType = nil): PSym =
+  result = nil
   var ty = ty
-  assert r != nil
+  assert r != ""
   while ty != nil:
     ty = ty.skipTypes(skipPtrs)
     assert(ty.kind in {tyTuple, tyObject})
@@ -769,177 +855,290 @@ proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope;
     if result != nil:
       if resTyp != nil: resTyp[] = ty
       break
-    if not p.module.compileToCpp: add(r, ".Sup")
-    ty = ty.sons[0]
+    if not p.module.compileToCpp: r.add(".Sup")
+    ty = ty[0]
   if result == nil: internalError(p.config, field.info, "genCheckedRecordField")
 
 proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
-  var a: TLoc
+  var a: TLoc = default(TLoc)
+  if p.module.compileToCpp and e.kind == nkDotExpr and e[1].kind == nkSym and e[1].typ.kind == tyPtr:
+    # special case for C++: we need to pull the type of the field as member and friends require the complete type.
+    let typ = e[1].typ.elementType
+    if typ.itemId in p.module.g.graph.memberProcsPerType:
+      discard getTypeDesc(p.module, typ)
+
   genRecordFieldAux(p, e, d, a)
   var r = rdLoc(a)
-  var f = e.sons[1].sym
-  let ty = skipTypes(a.t, abstractInst + tyUserTypeClasses)
+  var f = e[1].sym
+  let ty = skipTypes(a.t, abstractInstOwned + tyUserTypeClasses)
   if ty.kind == tyTuple:
     # we found a unique tuple type which lacks field information
     # so we use Field$i
-    addf(r, ".Field$1", [rope(f.position)])
+    r.add ".Field"
+    r.add rope(f.position)
     putIntoDest(p, d, e, r, a.storage)
   else:
-    var rtyp: PType
+    var rtyp: PType = nil
     let field = lookupFieldAgain(p, ty, f, r, addr rtyp)
-    if field.loc.r == nil and rtyp != nil: fillObjectFields(p.module, rtyp)
-    if field.loc.r == nil: internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty))
-    addf(r, ".$1", [field.loc.r])
+    if field.loc.snippet == "" and rtyp != nil: fillObjectFields(p.module, rtyp)
+    if field.loc.snippet == "": internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty))
+    r.add "."
+    r.add field.loc.snippet
     putIntoDest(p, d, e, r, a.storage)
+  r.freeze
 
 proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc)
 
 proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
   var test, u, v: TLoc
-  for i in countup(1, sonsLen(e) - 1):
-    var it = e.sons[i]
+  for i in 1..<e.len:
+    var it = e[i]
     assert(it.kind in nkCallKinds)
-    assert(it.sons[0].kind == nkSym)
-    let op = it.sons[0].sym
-    if op.magic == mNot: it = it.sons[1]
-    let disc = it.sons[2].skipConv
+    assert(it[0].kind == nkSym)
+    let op = it[0].sym
+    if op.magic == mNot: it = it[1]
+    let disc = it[2].skipConv
     assert(disc.kind == nkSym)
-    initLoc(test, locNone, it, OnStack)
-    initLocExpr(p, it.sons[1], u)
-    initLoc(v, locExpr, disc, OnUnknown)
-    v.r = obj
-    v.r.add(".")
-    v.r.add(disc.sym.loc.r)
+    test = initLoc(locNone, it, OnStack)
+    u = initLocExpr(p, it[1])
+    v = initLoc(locExpr, disc, OnUnknown)
+    v.snippet = newRopeAppender()
+    v.snippet.add obj
+    v.snippet.add(".")
+    v.snippet.add(disc.sym.loc.snippet)
     genInExprAux(p, it, u, v, test)
-    let id = nodeTableTestOrSet(p.module.dataCache,
-                               newStrNode(nkStrLit, field.name.s), p.module.labels)
-    let strLit = if id == p.module.labels: genStringLiteralDataOnly(p.module, field.name.s, e.info)
-                 else: p.module.tmpBase & rope(id)
-    if op.magic == mNot:
-      linefmt(p, cpsStmts,
-              "if ($1) #raiseFieldError($2);$n",
-              rdLoc(test), genStringLiteralFromData(p.module, strLit, e.info))
+    var msg = ""
+    if optDeclaredLocs in p.config.globalOptions:
+      # xxx this should be controlled by a separate flag, and
+      # used for other similar defects so that location information is shown
+      # even without the expensive `--stacktrace`; binary size could be optimized
+      # by encoding the file names separately from `file(line:col)`, essentially
+      # passing around `TLineInfo` + the set of files in the project.
+      msg.add toFileLineCol(p.config, e.info) & " "
+    msg.add genFieldDefect(p.config, field.name.s, disc.sym)
+    var strLit = newRopeAppender()
+    genStringLiteral(p.module, newStrNode(nkStrLit, msg), strLit)
+
+    ## discriminant check
+    template fun(code) = linefmt(p, cpsStmts, code, [rdLoc(test)])
+    if op.magic == mNot: fun("if ($1) ") else: fun("if (!($1)) ")
+
+    ## call raiseFieldError2 on failure
+    var discIndex = newRopeAppender()
+    rdSetElemLoc(p.config, v, u.t, discIndex)
+    if optTinyRtti in p.config.globalOptions:
+      let base = disc.typ.skipTypes(abstractInst+{tyRange})
+      case base.kind
+      of tyEnum:
+        const code = "{ #raiseFieldErrorStr($1, $2); "
+        let toStrProc = getToStringProc(p.module.g.graph, base)
+        # XXX need to modify this logic for IC.
+        # need to analyze nkFieldCheckedExpr and marks procs "used" like range checks in dce
+        var toStr: TLoc = default(TLoc)
+        expr(p, newSymNode(toStrProc), toStr)
+        let enumStr = "$1($2)" % [rdLoc(toStr), rdLoc(v)]
+        linefmt(p, cpsStmts, code, [strLit, enumStr])
+      else:
+        const code = "{ #raiseFieldError2($1, (NI)$2); "
+        linefmt(p, cpsStmts, code, [strLit, discIndex])
+
     else:
-      linefmt(p, cpsStmts,
-              "if (!($1)) #raiseFieldError($2);$n",
-              rdLoc(test), genStringLiteralFromData(p.module, strLit, e.info))
+      # complication needed for signed types
+      let first = p.config.firstOrd(disc.sym.typ)
+      var firstLit = newRopeAppender()
+      int64Literal(cast[int](first), firstLit)
+      let discName = genTypeInfo(p.config, p.module, disc.sym.typ, e.info)
+      const code = "{ #raiseFieldError2($1, #reprDiscriminant(((NI)$2) + (NI)$3, $4)); "
+      linefmt(p, cpsStmts, code, [strLit, discIndex, firstLit, discName])
+
+    raiseInstr(p, p.s(cpsStmts))
+    linefmt p, cpsStmts, "}$n", []
 
 proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
+  assert e[0].kind == nkDotExpr
   if optFieldCheck in p.options:
-    var a: TLoc
-    genRecordFieldAux(p, e.sons[0], d, a)
+    var a: TLoc = default(TLoc)
+    genRecordFieldAux(p, e[0], d, a)
     let ty = skipTypes(a.t, abstractInst + tyUserTypeClasses)
     var r = rdLoc(a)
-    let f = e.sons[0].sons[1].sym
+    let f = e[0][1].sym
     let field = lookupFieldAgain(p, ty, f, r)
-    if field.loc.r == nil: fillObjectFields(p.module, ty)
-    if field.loc.r == nil:
+    if field.loc.snippet == "": fillObjectFields(p.module, ty)
+    if field.loc.snippet == "":
       internalError(p.config, e.info, "genCheckedRecordField") # generate the checks:
     genFieldCheck(p, e, r, field)
-    add(r, ropecg(p.module, ".$1", field.loc.r))
-    putIntoDest(p, d, e.sons[0], r, a.storage)
+    r.add(".")
+    r.add field.loc.snippet
+    putIntoDest(p, d, e[0], r, a.storage)
+    r.freeze
   else:
-    genRecordField(p, e.sons[0], d)
+    genRecordField(p, e[0], d)
+
+proc genUncheckedArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
+  var a = initLocExpr(p, x)
+  var b = initLocExpr(p, y)
+  d.inheritLocation(a)
+  putIntoDest(p, d, n, ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]),
+              a.storage)
 
 proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
-  var a, b: TLoc
-  initLocExpr(p, x, a)
-  initLocExpr(p, y, b)
+  var a = initLocExpr(p, x)
+  var b = initLocExpr(p, y)
   var ty = skipTypes(a.t, abstractVarRange + abstractPtrs + tyUserTypeClasses)
-  var first = intLiteral(firstOrd(p.config, ty))
+  var first = newRopeAppender()
+  intLiteral(firstOrd(p.config, ty), first)
   # emit range check:
-  if optBoundsCheck in p.options and tfUncheckedArray notin ty.flags:
+  if optBoundsCheck in p.options and ty.kind != tyUncheckedArray:
     if not isConstExpr(y):
       # semantic pass has already checked for const index expressions
-      if firstOrd(p.config, ty) == 0:
+      if firstOrd(p.config, ty) == 0 and lastOrd(p.config, ty) >= 0:
         if (firstOrd(p.config, b.t) < firstOrd(p.config, ty)) or (lastOrd(p.config, b.t) > lastOrd(p.config, ty)):
-          linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)) #raiseIndexError();$n",
-                  rdCharLoc(b), intLiteral(lastOrd(p.config, ty)))
+          var last = newRopeAppender()
+          intLiteral(lastOrd(p.config, ty), last)
+          linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)){ #raiseIndexError2($1, $2); ",
+                  [rdCharLoc(b), last])
+          raiseInstr(p, p.s(cpsStmts))
+          linefmt p, cpsStmts, "}$n", []
       else:
-        linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseIndexError();$n",
-                rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty)))
+        var last = newRopeAppender()
+        intLiteral(lastOrd(p.config, ty), last)
+        linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3){ #raiseIndexError3($1, $2, $3); ",
+                [rdCharLoc(b), first, last])
+        raiseInstr(p, p.s(cpsStmts))
+        linefmt p, cpsStmts, "}$n", []
+
     else:
       let idx = getOrdValue(y)
       if idx < firstOrd(p.config, ty) or idx > lastOrd(p.config, ty):
-        localError(p.config, x.info, "index out of bounds")
+        localError(p.config, x.info, formatErrorIndexBound(idx, firstOrd(p.config, ty), lastOrd(p.config, ty)))
   d.inheritLocation(a)
   putIntoDest(p, d, n,
-              ropecg(p.module, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.storage)
+              ropecg(p.module, "$1[($2)- $3]", [rdLoc(a), rdCharLoc(b), first]), a.storage)
 
 proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) =
-  var a, b: TLoc
-  initLocExpr(p, x, a)
-  initLocExpr(p, y, b)
-  var ty = skipTypes(a.t, abstractVarRange)
+  var a = initLocExpr(p, x)
+  var b = initLocExpr(p, y)
   inheritLocation(d, a)
   putIntoDest(p, d, n,
-              ropecg(p.module, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage)
+              ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage)
 
-proc genBoundsCheck(p: BProc; arr, a, b: TLoc) =
-  let ty = skipTypes(arr.t, abstractVarRange)
+proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType) =
+  let ty = arrTyp
   case ty.kind
   of tyOpenArray, tyVarargs:
-    linefmt(p, cpsStmts,
-      "if ($2-$1 != -1 && " &
-      "((NU)($1) >= (NU)($3Len_0) || (NU)($2) >= (NU)($3Len_0))) #raiseIndexError();$n",
-      rdLoc(a), rdLoc(b), rdLoc(arr))
-  of tyArray:
-    let first = intLiteral(firstOrd(p.config, ty))
-    if tfUncheckedArray notin ty.flags:
+    if reifiedOpenArray(arr.lode):
       linefmt(p, cpsStmts,
         "if ($2-$1 != -1 && " &
-        "($2-$1 < -1 || $1 < $3 || $1 > $4 || $2 < $3 || $2 > $4)) #raiseIndexError();$n",
-        rdCharLoc(a), rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty)))
+        "($1 < 0 || $1 >= $3.Field1 || $2 < 0 || $2 >= $3.Field1)){ #raiseIndexError4($1, $2, $3.Field1); ",
+        [rdLoc(a), rdLoc(b), rdLoc(arr)])
+    else:
+      linefmt(p, cpsStmts,
+        "if ($2-$1 != -1 && ($1 < 0 || $1 >= $3Len_0 || $2 < 0 || $2 >= $3Len_0))" &
+        "{ #raiseIndexError4($1, $2, $3Len_0); ",
+        [rdLoc(a), rdLoc(b), rdLoc(arr)])
+    raiseInstr(p, p.s(cpsStmts))
+    linefmt p, cpsStmts, "}$n", []
+
+  of tyArray:
+    var first = newRopeAppender()
+    intLiteral(firstOrd(p.config, ty), first)
+    var last = newRopeAppender()
+    intLiteral(lastOrd(p.config, ty), last)
+    linefmt(p, cpsStmts,
+      "if ($2-$1 != -1 && " &
+      "($2-$1 < -1 || $1 < $3 || $1 > $4 || $2 < $3 || $2 > $4)){ #raiseIndexError(); ",
+      [rdCharLoc(a), rdCharLoc(b), first, last])
+
+    raiseInstr(p, p.s(cpsStmts))
+    linefmt p, cpsStmts, "}$n", []
+
   of tySequence, tyString:
     linefmt(p, cpsStmts,
       "if ($2-$1 != -1 && " &
-      "(!$3 || (NU)($1) >= (NU)($3->$4) || (NU)($2) >= (NU)($3->$4))) #raiseIndexError();$n",
-      rdLoc(a), rdLoc(b), rdLoc(arr), lenField(p))
+      "($1 < 0 || $1 >= $3 || $2 < 0 || $2 >= $3)){ #raiseIndexError4($1, $2, $3); ",
+      [rdLoc(a), rdLoc(b), lenExpr(p, arr)])
+    raiseInstr(p, p.s(cpsStmts))
+    linefmt p, cpsStmts, "}$n", []
+
   else: discard
 
 proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
-  var a, b: TLoc
-  initLocExpr(p, x, a)
-  initLocExpr(p, y, b) # emit range check:
-  if optBoundsCheck in p.options:
-    linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)) #raiseIndexError();$n",
-            rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``!
-  inheritLocation(d, a)
-  putIntoDest(p, d, n,
-              ropecg(p.module, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage)
+  var a = initLocExpr(p, x)
+  var b = initLocExpr(p, y)
+  if not reifiedOpenArray(x):
+    # emit range check:
+    if optBoundsCheck in p.options:
+      linefmt(p, cpsStmts, "if ($1 < 0 || $1 >= $2Len_0){ #raiseIndexError2($1,$2Len_0-1); ",
+              [rdCharLoc(b), rdLoc(a)]) # BUGFIX: ``>=`` and not ``>``!
+      raiseInstr(p, p.s(cpsStmts))
+      linefmt p, cpsStmts, "}$n", []
+
+    inheritLocation(d, a)
+    putIntoDest(p, d, n,
+                ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage)
+  else:
+    if optBoundsCheck in p.options:
+      linefmt(p, cpsStmts, "if ($1 < 0 || $1 >= $2.Field1){ #raiseIndexError2($1,$2.Field1-1); ",
+              [rdCharLoc(b), rdLoc(a)]) # BUGFIX: ``>=`` and not ``>``!
+      raiseInstr(p, p.s(cpsStmts))
+      linefmt p, cpsStmts, "}$n", []
+
+    inheritLocation(d, a)
+    putIntoDest(p, d, n,
+                ropecg(p.module, "$1.Field0[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage)
 
 proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
-  var a, b: TLoc
-  initLocExpr(p, x, a)
-  initLocExpr(p, y, b)
+  var a = initLocExpr(p, x)
+  var b = initLocExpr(p, y)
   var ty = skipTypes(a.t, abstractVarRange)
   if ty.kind in {tyRef, tyPtr}:
-    ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check:
+    ty = skipTypes(ty.elementType, abstractVarRange)
+  # emit range check:
   if optBoundsCheck in p.options:
-    if ty.kind == tyString and (not defined(nimNoZeroTerminator) or optLaxStrings in p.options):
-      linefmt(p, cpsStmts,
-              "if (!$2 || (NU)($1) > (NU)($2->$3)) #raiseIndexError();$n",
-              rdLoc(b), rdLoc(a), lenField(p))
-    else:
-      linefmt(p, cpsStmts,
-              "if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
-              rdLoc(b), rdLoc(a), lenField(p))
+    linefmt(p, cpsStmts,
+            "if ($1 < 0 || $1 >= $2){ #raiseIndexError2($1,$2-1); ",
+            [rdCharLoc(b), lenExpr(p, a)])
+    raiseInstr(p, p.s(cpsStmts))
+    linefmt p, cpsStmts, "}$n", []
+
   if d.k == locNone: d.storage = OnHeap
   if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
-    a.r = ropecg(p.module, "(*$1)", a.r)
+    a.snippet = ropecg(p.module, "(*$1)", [a.snippet])
+
+  if lfPrepareForMutation in d.flags and ty.kind == tyString and
+      optSeqDestructors in p.config.globalOptions:
+    linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
   putIntoDest(p, d, n,
-              ropecg(p.module, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.storage)
+              ropecg(p.module, "$1$3[$2]", [rdLoc(a), rdCharLoc(b), dataField(p)]), a.storage)
 
 proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) =
-  var ty = skipTypes(n.sons[0].typ, abstractVarRange + tyUserTypeClasses)
-  if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange)
+  var ty = skipTypes(n[0].typ, abstractVarRange + tyUserTypeClasses)
+  if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.elementType, abstractVarRange)
   case ty.kind
-  of tyArray: genArrayElem(p, n, n.sons[0], n.sons[1], d)
-  of tyOpenArray, tyVarargs: genOpenArrayElem(p, n, n.sons[0], n.sons[1], d)
-  of tySequence, tyString: genSeqElem(p, n, n.sons[0], n.sons[1], d)
-  of tyCString: genCStringElem(p, n, n.sons[0], n.sons[1], d)
+  of tyUncheckedArray: genUncheckedArrayElem(p, n, n[0], n[1], d)
+  of tyArray: genArrayElem(p, n, n[0], n[1], d)
+  of tyOpenArray, tyVarargs: genOpenArrayElem(p, n, n[0], n[1], d)
+  of tySequence, tyString: genSeqElem(p, n, n[0], n[1], d)
+  of tyCstring: genCStringElem(p, n, n[0], n[1], d)
   of tyTuple: genTupleElem(p, n, d)
   else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
+  discard getTypeDesc(p.module, n.typ)
+
+proc isSimpleExpr(n: PNode): bool =
+  # calls all the way down --> can stay expression based
+  case n.kind
+  of nkCallKinds, nkDotExpr, nkPar, nkTupleConstr,
+      nkObjConstr, nkBracket, nkCurly, nkHiddenDeref, nkDerefExpr, nkHiddenAddr,
+      nkHiddenStdConv, nkHiddenSubConv, nkConv, nkAddr:
+    for c in n:
+      if not isSimpleExpr(c): return false
+    result = true
+  of nkStmtListExpr:
+    for i in 0..<n.len-1:
+      if n[i].kind notin {nkCommentStmt, nkEmpty}: return false
+    result = isSimpleExpr(n.lastSon)
+  else:
+    result = n.isAtom
 
 proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   # how to generate code?
@@ -962,60 +1161,87 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   # tmp = a
   # end:
   # a = tmp
-  var
-    L: TLabel
-    tmp: TLoc
-  getTemp(p, e.typ, tmp)      # force it into a temp!
-  inc p.splitDecls
-  expr(p, e.sons[1], tmp)
-  L = getLabel(p)
-  if m == mOr:
-    lineF(p, cpsStmts, "if ($1) goto $2;$n", [rdLoc(tmp), L])
-  else:
-    lineF(p, cpsStmts, "if (!($1)) goto $2;$n", [rdLoc(tmp), L])
-  expr(p, e.sons[2], tmp)
-  fixLabel(p, L)
-  if d.k == locNone:
-    d = tmp
+  when false:
+    #if isSimpleExpr(e) and p.module.compileToCpp:
+    #getTemp(p, e.typ, tmpA)
+    #getTemp(p, e.typ, tmpB)
+    var tmpA = initLocExprSingleUse(p, e[1])
+    var tmpB = initLocExprSingleUse(p, e[2])
+    tmpB.k = locExpr
+    if m == mOr:
+      tmpB.snippet = "((" & rdLoc(tmpA) & ")||(" & rdLoc(tmpB) & "))"
+    else:
+      tmpB.snippet = "((" & rdLoc(tmpA) & ")&&(" & rdLoc(tmpB) & "))"
+    if d.k == locNone:
+      d = tmpB
+    else:
+      genAssignment(p, d, tmpB, {})
   else:
-    genAssignment(p, d, tmp, {}) # no need for deep copying
-  dec p.splitDecls
+    var
+      L: TLabel
+    var tmp: TLoc = getTemp(p, e.typ)      # force it into a temp!
+    inc p.splitDecls
+    expr(p, e[1], tmp)
+    L = getLabel(p)
+    if m == mOr:
+      lineF(p, cpsStmts, "if ($1) goto $2;$n", [rdLoc(tmp), L])
+    else:
+      lineF(p, cpsStmts, "if (!($1)) goto $2;$n", [rdLoc(tmp), L])
+    expr(p, e[2], tmp)
+    fixLabel(p, L)
+    if d.k == locNone:
+      d = tmp
+    else:
+      genAssignment(p, d, tmp, {}) # no need for deep copying
+    dec p.splitDecls
 
 proc genEcho(p: BProc, n: PNode) =
-  # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)``
+  # this unusual way of implementing it ensures that e.g. ``echo("hallo", 45)``
   # is threadsafe.
   internalAssert p.config, n.kind == nkBracket
   if p.config.target.targetOS == osGenode:
-    # bypass libc and print directly to the Genode LOG session
-    var args: Rope = nil
+    # echo directly to the Genode LOG session
+    var args: Rope = ""
     var a: TLoc
-    for it in n.sons:
+    for i, it in n.sons:
       if it.skipConv.kind == nkNilLit:
-        add(args, ", \"\"")
-      else:
-        initLocExpr(p, it, a)
-        addf(args, ", $1? ($1)->data:\"\"", [rdLoc(a)])
+        args.add(", \"\"")
+      elif n.len != 0:
+        a = initLocExpr(p, it)
+        if i > 0:
+          args.add(", ")
+        case detectStrVersion(p.module)
+        of 2:
+          args.add(ropecg(p.module, "Genode::Cstring($1.p->data, $1.len)", [a.rdLoc]))
+        else:
+          args.add(ropecg(p.module, "Genode::Cstring($1->data, $1->len)", [a.rdLoc]))
     p.module.includeHeader("<base/log.h>")
-    linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args)
+    p.module.includeHeader("<util/string.h>")
+    linefmt(p, cpsStmts, """Genode::log($1);$n""", [args])
   else:
     if n.len == 0:
-      linefmt(p, cpsStmts, "#echoBinSafe(NIM_NIL, $1);$n", n.len.rope)
+      linefmt(p, cpsStmts, "#echoBinSafe(NIM_NIL, $1);$n", [n.len])
     else:
-      var a: TLoc
-      initLocExpr(p, n, a)
-      linefmt(p, cpsStmts, "#echoBinSafe($1, $2);$n", a.rdLoc, n.len.rope)
+      var a: TLoc = initLocExpr(p, n)
+      linefmt(p, cpsStmts, "#echoBinSafe($1, $2);$n", [a.rdLoc, n.len])
     when false:
       p.module.includeHeader("<stdio.h>")
       linefmt(p, cpsStmts, "printf($1$2);$n",
-              makeCString(repeat("%s", n.len) & "\L"), args)
-      linefmt(p, cpsStmts, "fflush(stdout);$n")
+              makeCString(repeat("%s", n.len) & "\L"), [args])
+      linefmt(p, cpsStmts, "fflush(stdout);$n", [])
 
 proc gcUsage(conf: ConfigRef; n: PNode) =
   if conf.selectedGC == gcNone: message(conf, n.info, warnGcMem, n.renderTree)
 
+proc strLoc(p: BProc; d: TLoc): Rope =
+  if optSeqDestructors in p.config.globalOptions:
+    result = byRefLoc(p, d)
+  else:
+    result = rdLoc(d)
+
 proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
   #   <Nim code>
-  #   s = 'Hello ' & name & ', how do you feel?' & 'z'
+  #   s = "Hello " & name & ", how do you feel?" & 'z'
   #
   #   <generated C code>
   #  {
@@ -1030,25 +1256,26 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
   #    appendChar(tmp0, 'z');
   #    asgn(s, tmp0);
   #  }
-  var a, tmp: TLoc
-  getTemp(p, e.typ, tmp)
+  var a: TLoc
+  var tmp: TLoc = getTemp(p, e.typ)
   var L = 0
-  var appends: Rope = nil
-  var lens: Rope = nil
-  for i in countup(0, sonsLen(e) - 2):
+  var appends: Rope = ""
+  var lens: Rope = ""
+  for i in 0..<e.len - 1:
     # compute the length expression:
-    initLocExpr(p, e.sons[i + 1], a)
-    if skipTypes(e.sons[i + 1].typ, abstractVarRange).kind == tyChar:
+    a = initLocExpr(p, e[i + 1])
+    if skipTypes(e[i + 1].typ, abstractVarRange).kind == tyChar:
       inc(L)
-      add(appends, ropecg(p.module, "#appendChar($1, $2);$n", tmp.r, rdLoc(a)))
+      appends.add(ropecg(p.module, "#appendChar($1, $2);$n", [strLoc(p, tmp), rdLoc(a)]))
     else:
-      if e.sons[i + 1].kind in {nkStrLit..nkTripleStrLit}:
-        inc(L, len(e.sons[i + 1].strVal))
+      if e[i + 1].kind in {nkStrLit..nkTripleStrLit}:
+        inc(L, e[i + 1].strVal.len)
       else:
-        addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)])
-      add(appends, ropecg(p.module, "#appendString($1, $2);$n", tmp.r, rdLoc(a)))
-  linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", tmp.r, lens, rope(L))
-  add(p.s(cpsStmts), appends)
+        lens.add(lenExpr(p, a))
+        lens.add(" + ")
+      appends.add(ropecg(p.module, "#appendString($1, $2);$n", [strLoc(p, tmp), rdLoc(a)]))
+  linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", [tmp.snippet, lens, L])
+  p.s(cpsStmts).add appends
   if d.k == locNone:
     d = tmp
   else:
@@ -1057,7 +1284,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
 
 proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
   #  <Nim code>
-  #  s &= 'Hello ' & name & ', how do you feel?' & 'z'
+  #  s &= "Hello " & name & ", how do you feel?" & 'z'
   #  // BUG: what if s is on the left side too?
   #  <generated C code>
   #  {
@@ -1068,158 +1295,250 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
   #    appendChar(s, 'z');
   #  }
   var
-    a, dest: TLoc
-    appends, lens: Rope
+    a, call: TLoc
+    appends, lens: Rope = ""
   assert(d.k == locNone)
   var L = 0
-  initLocExpr(p, e.sons[1], dest)
-  for i in countup(0, sonsLen(e) - 3):
+  var dest = initLocExpr(p, e[1])
+  for i in 0..<e.len - 2:
     # compute the length expression:
-    initLocExpr(p, e.sons[i + 2], a)
-    if skipTypes(e.sons[i + 2].typ, abstractVarRange).kind == tyChar:
+    a = initLocExpr(p, e[i + 2])
+    if skipTypes(e[i + 2].typ, abstractVarRange).kind == tyChar:
       inc(L)
-      add(appends, ropecg(p.module, "#appendChar($1, $2);$n",
-                        rdLoc(dest), rdLoc(a)))
+      appends.add(ropecg(p.module, "#appendChar($1, $2);$n",
+                        [strLoc(p, dest), rdLoc(a)]))
     else:
-      if e.sons[i + 2].kind in {nkStrLit..nkTripleStrLit}:
-        inc(L, len(e.sons[i + 2].strVal))
+      if e[i + 2].kind in {nkStrLit..nkTripleStrLit}:
+        inc(L, e[i + 2].strVal.len)
       else:
-        addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)])
-      add(appends, ropecg(p.module, "#appendString($1, $2);$n",
-                        rdLoc(dest), rdLoc(a)))
-  linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n",
-          rdLoc(dest), lens, rope(L))
-  add(p.s(cpsStmts), appends)
-  gcUsage(p.config, e)
+        lens.add(lenExpr(p, a))
+        lens.add(" + ")
+      appends.add(ropecg(p.module, "#appendString($1, $2);$n",
+                        [strLoc(p, dest), rdLoc(a)]))
+  if optSeqDestructors in p.config.globalOptions:
+    linefmt(p, cpsStmts, "#prepareAdd($1, $2$3);$n",
+            [byRefLoc(p, dest), lens, L])
+  else:
+    call = initLoc(locCall, e, OnHeap)
+    call.snippet = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, L])
+    genAssignment(p, dest, call, {})
+    gcUsage(p.config, e)
+  p.s(cpsStmts).add appends
 
 proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
   # seq &= x  -->
   #    seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x));
   #    seq->data[seq->len-1] = x;
-  let seqAppendPattern = if not p.module.compileToCpp:
-                           "$1 = ($2) #incrSeqV3(&($1)->Sup, $3);$n"
-                         else:
-                           "$1 = ($2) #incrSeqV3($1, $3);$n"
-  var a, b, dest, tmpL: TLoc
-  initLocExpr(p, e.sons[1], a)
-  initLocExpr(p, e.sons[2], b)
-  let seqType = skipTypes(e.sons[1].typ, {tyVar})
-  lineCg(p, cpsStmts, seqAppendPattern, [
-      rdLoc(a),
-      getTypeDesc(p.module, e.sons[1].typ),
-      genTypeInfo(p.module, seqType, e.info)])
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
+  let seqType = skipTypes(e[1].typ, {tyVar})
+  var call = initLoc(locCall, e, OnHeap)
+  if not p.module.compileToCpp:
+    const seqAppendPattern = "($2) #incrSeqV3((TGenericSeq*)($1), $3)"
+    call.snippet = ropecg(p.module, seqAppendPattern, [rdLoc(a),
+      getTypeDesc(p.module, e[1].typ),
+      genTypeInfoV1(p.module, seqType, e.info)])
+  else:
+    const seqAppendPattern = "($2) #incrSeqV3($1, $3)"
+    call.snippet = ropecg(p.module, seqAppendPattern, [rdLoc(a),
+      getTypeDesc(p.module, e[1].typ),
+      genTypeInfoV1(p.module, seqType, e.info)])
+  # emit the write barrier if required, but we can always move here, so
+  # use 'genRefAssign' for the seq.
+  genRefAssign(p, a, call)
   #if bt != b.t:
   #  echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t)
-  initLoc(dest, locExpr, e.sons[2], OnHeap)
-  getIntTemp(p, tmpL)
-  lineCg(p, cpsStmts, "$1 = $2->$3++;$n", tmpL.r, rdLoc(a), lenField(p))
-  dest.r = ropecg(p.module, "$1->data[$2]", rdLoc(a), tmpL.r)
-  genAssignment(p, dest, b, {needToCopy, afDestIsNil})
+  var dest = initLoc(locExpr, e[2], OnHeap)
+  var tmpL = getIntTemp(p)
+  lineCg(p, cpsStmts, "$1 = $2->$3++;$n", [tmpL.snippet, rdLoc(a), lenField(p)])
+  dest.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(a), tmpL.snippet, dataField(p)])
+  genAssignment(p, dest, b, {needToCopy})
   gcUsage(p.config, e)
 
-proc genReset(p: BProc, n: PNode) =
-  var a: TLoc
-  initLocExpr(p, n.sons[1], a)
-  linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
-          addrLoc(p.config, a),
-          genTypeInfo(p.module, skipTypes(a.t, {tyVar}), n.info))
+proc genDefault(p: BProc; n: PNode; d: var TLoc) =
+  if d.k == locNone: d = getTemp(p, n.typ, needsInit=true)
+  else: resetLoc(p, d)
 
-proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
+proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) =
   var sizeExpr = sizeExpr
   let typ = a.t
-  var b: TLoc
-  initLoc(b, locExpr, a.lode, OnHeap)
-  let refType = typ.skipTypes(abstractInst)
+  var b: TLoc = initLoc(locExpr, a.lode, OnHeap)
+  let refType = typ.skipTypes(abstractInstOwned)
   assert refType.kind == tyRef
-  let bt = refType.lastSon
-  if sizeExpr.isNil:
-    sizeExpr = "sizeof($1)" %
-        [getTypeDesc(p.module, bt)]
-  let args = [getTypeDesc(p.module, typ),
-              genTypeInfo(p.module, typ, a.lode.info),
-              sizeExpr]
-  if a.storage == OnHeap and usesNativeGC(p.config):
-    # use newObjRC1 as an optimization
-    if canFormAcycle(a.t):
-      linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc)
-    else:
-      linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", a.rdLoc)
-    b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", args)
-    linefmt(p, cpsStmts, "$1 = $2;$n", a.rdLoc, b.rdLoc)
-  else:
-    b.r = ropecg(p.module, "($1) #newObj($2, $3)", args)
-    genAssignment(p, a, b, {})  # set the object type:
-  genObjectInit(p, cpsStmts, bt, a, false)
+  let bt = refType.elementType
+  if sizeExpr == "":
+    sizeExpr = "sizeof($1)" % [getTypeDesc(p.module, bt)]
+
+  if optTinyRtti in p.config.globalOptions:
+    if needsInit:
+      b.snippet = ropecg(p.module, "($1) #nimNewObj($2, NIM_ALIGNOF($3))",
+          [getTypeDesc(p.module, typ), sizeExpr, getTypeDesc(p.module, bt)])
+    else:
+      b.snippet = ropecg(p.module, "($1) #nimNewObjUninit($2, NIM_ALIGNOF($3))",
+          [getTypeDesc(p.module, typ), sizeExpr, getTypeDesc(p.module, bt)])
+    genAssignment(p, a, b, {})
+  else:
+    let ti = genTypeInfoV1(p.module, typ, a.lode.info)
+    let op = getAttachedOp(p.module.g.graph, bt, attachedDestructor)
+    if op != nil and not isTrivialProc(p.module.g.graph, op):
+      # the prototype of a destructor is ``=destroy(x: var T)`` and that of a
+      # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
+      # convention at least:
+      if op.typ == nil or op.typ.callConv != ccNimCall:
+        localError(p.module.config, a.lode.info,
+          "the destructor that is turned into a finalizer needs " &
+          "to have the 'nimcall' calling convention")
+      var f: TLoc = initLocExpr(p, newSymNode(op))
+      p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
+
+    if a.storage == OnHeap and usesWriteBarrier(p.config):
+      if canFormAcycle(p.module.g.graph, a.t):
+        linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", [a.rdLoc])
+      else:
+        linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", [a.rdLoc])
+      if p.config.selectedGC == gcGo:
+        # newObjRC1() would clash with unsureAsgnRef() - which is used by gcGo to
+        # implement the write barrier
+        b.snippet = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
+        linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n",
+                [addrLoc(p.config, a), b.rdLoc])
+      else:
+        # use newObjRC1 as an optimization
+        b.snippet = ropecg(p.module, "($1) #newObjRC1($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
+        linefmt(p, cpsStmts, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
+    else:
+      b.snippet = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
+      genAssignment(p, a, b, {})
+  # set the object type:
+  genObjectInit(p, cpsStmts, bt, a, constructRefObj)
 
 proc genNew(p: BProc, e: PNode) =
-  var a: TLoc
-  initLocExpr(p, e.sons[1], a)
+  var a: TLoc = initLocExpr(p, e[1])
   # 'genNew' also handles 'unsafeNew':
   if e.len == 3:
-    var se: TLoc
-    initLocExpr(p, e.sons[2], se)
-    rawGenNew(p, a, se.rdLoc)
+    var se: TLoc = initLocExpr(p, e[2])
+    rawGenNew(p, a, se.rdLoc, needsInit = true)
   else:
-    rawGenNew(p, a, nil)
+    rawGenNew(p, a, "", needsInit = true)
   gcUsage(p.config, e)
 
-proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) =
+proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) =
   let seqtype = skipTypes(dest.t, abstractVarRange)
-  let args = [getTypeDesc(p.module, seqtype),
-              genTypeInfo(p.module, seqtype, dest.lode.info), length]
-  var call: TLoc
-  initLoc(call, locExpr, dest.lode, OnHeap)
-  if dest.storage == OnHeap and usesNativeGC(p.config):
-    if canFormAcycle(dest.t):
-      linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc)
-    else:
-      linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", dest.rdLoc)
-    call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", args)
-    linefmt(p, cpsStmts, "$1 = $2;$n", dest.rdLoc, call.rdLoc)
-  else:
-    call.r = ropecg(p.module, "($1) #newSeq($2, $3)", args)
+  var call: TLoc = initLoc(locExpr, dest.lode, OnHeap)
+  if dest.storage == OnHeap and usesWriteBarrier(p.config):
+    if canFormAcycle(p.module.g.graph, dest.t):
+      linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", [dest.rdLoc])
+    else:
+      linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", [dest.rdLoc])
+    if not lenIsZero:
+      if p.config.selectedGC == gcGo:
+        # we need the write barrier
+        call.snippet = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
+              genTypeInfoV1(p.module, seqtype, dest.lode.info), length])
+        linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", [addrLoc(p.config, dest), call.rdLoc])
+      else:
+        call.snippet = ropecg(p.module, "($1) #newSeqRC1($2, $3)", [getTypeDesc(p.module, seqtype),
+              genTypeInfoV1(p.module, seqtype, dest.lode.info), length])
+        linefmt(p, cpsStmts, "$1 = $2;$n", [dest.rdLoc, call.rdLoc])
+  else:
+    if lenIsZero:
+      call.snippet = rope"NIM_NIL"
+    else:
+      call.snippet = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
+              genTypeInfoV1(p.module, seqtype, dest.lode.info), length])
     genAssignment(p, dest, call, {})
 
 proc genNewSeq(p: BProc, e: PNode) =
-  var a, b: TLoc
-  initLocExpr(p, e.sons[1], a)
-  initLocExpr(p, e.sons[2], b)
-  genNewSeqAux(p, a, b.rdLoc)
-  gcUsage(p.config, e)
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
+  if optSeqDestructors in p.config.globalOptions:
+    let seqtype = skipTypes(e[1].typ, abstractVarRange)
+    linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n",
+      [a.rdLoc, b.rdLoc,
+       getTypeDesc(p.module, seqtype.elementType),
+       getSeqPayloadType(p.module, seqtype)])
+  else:
+    let lenIsZero = e[2].kind == nkIntLit and e[2].intVal == 0
+    genNewSeqAux(p, a, b.rdLoc, lenIsZero)
+    gcUsage(p.config, e)
 
 proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
   let seqtype = skipTypes(e.typ, abstractVarRange)
-  var a: TLoc
-  initLocExpr(p, e.sons[1], a)
-  putIntoDest(p, d, e, ropecg(p.module,
-              "($1)#nimNewSeqOfCap($2, $3)", [
-              getTypeDesc(p.module, seqtype),
-              genTypeInfo(p.module, seqtype, e.info), a.rdLoc]))
-  gcUsage(p.config, e)
+  var a: TLoc = initLocExpr(p, e[1])
+  if optSeqDestructors in p.config.globalOptions:
+    if d.k == locNone: d = getTemp(p, e.typ, needsInit=false)
+    linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3));$n",
+      [d.rdLoc, a.rdLoc, getTypeDesc(p.module, seqtype.elementType),
+      getSeqPayloadType(p.module, seqtype),
+    ])
+  else:
+    if d.k == locNone: d = getTemp(p, e.typ, needsInit=false) # bug #22560
+    putIntoDest(p, d, e, ropecg(p.module,
+                "($1)#nimNewSeqOfCap($2, $3)", [
+                getTypeDesc(p.module, seqtype),
+                genTypeInfoV1(p.module, seqtype, e.info), a.rdLoc]))
+    gcUsage(p.config, e)
+
+proc rawConstExpr(p: BProc, n: PNode; d: var TLoc) =
+  let t = n.typ
+  discard getTypeDesc(p.module, t) # so that any fields are initialized
+  let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
+  fillLoc(d, locData, n, p.module.tmpBase & rope(id), OnStatic)
+  if id == p.module.labels:
+    # expression not found in the cache:
+    inc(p.module.labels)
+    var data = "static NIM_CONST $1 $2 = " % [getTypeDesc(p.module, t), d.snippet]
+    # bug #23627; when generating const object fields, it's likely that
+    # we need to generate type infos for the object, which may be an object with
+    # custom hooks. We need to generate potential consts in the hooks first.
+    genBracedInit(p, n, isConst = true, t, data)
+    data.addf(";$n", [])
+    p.module.s[cfsData].add data
 
-proc genConstExpr(p: BProc, n: PNode): Rope
 proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
   if d.k == locNone and n.len > ord(n.kind == nkObjConstr) and n.isDeepConstExpr:
-    let t = n.typ
-    discard getTypeDesc(p.module, t) # so that any fields are initialized
-    let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
-    fillLoc(d, locData, n, p.module.tmpBase & rope(id), OnStatic)
-    if id == p.module.labels:
-      # expression not found in the cache:
-      inc(p.module.labels)
-      addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
-           [getTypeDesc(p.module, t), d.r, genConstExpr(p, n)])
+    rawConstExpr(p, n, d)
     result = true
   else:
     result = false
 
+
+proc genFieldObjConstr(p: BProc; ty: PType; useTemp, isRef: bool; nField, val, check: PNode; d: var TLoc; r: Rope; info: TLineInfo) =
+  var tmp2 = TLoc(snippet: r)
+  let field = lookupFieldAgain(p, ty, nField.sym, tmp2.snippet)
+  if field.loc.snippet == "": fillObjectFields(p.module, ty)
+  if field.loc.snippet == "": internalError(p.config, info, "genFieldObjConstr")
+  if check != nil and optFieldCheck in p.options:
+    genFieldCheck(p, check, r, field)
+  tmp2.snippet.add(".")
+  tmp2.snippet.add(field.loc.snippet)
+  if useTemp:
+    tmp2.k = locTemp
+    tmp2.storage = if isRef: OnHeap else: OnStack
+  else:
+    tmp2.k = d.k
+    tmp2.storage = if isRef: OnHeap else: d.storage
+  tmp2.lode = val
+  if nField.typ.skipTypes(abstractVar).kind in {tyOpenArray, tyVarargs}:
+    var tmp3 = getTemp(p, val.typ)
+    expr(p, val, tmp3)
+    genOpenArrayConv(p, tmp2, tmp3, {})
+  else:
+    expr(p, val, tmp2)
+
 proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
-  #echo rendertree e, " ", e.isDeepConstExpr
   # inheritance in C++ does not allow struct initialization so
   # we skip this step here:
-  if not p.module.compileToCpp:
+  if not p.module.compileToCpp and optSeqDestructors notin p.config.globalOptions:
+    # disabled optimization: it is wrong for C++ and now also
+    # causes trouble for --gc:arc, see bug #13240
+    #[
+      var box: seq[Thing]
+      for i in 0..3:
+        box.add Thing(s1: "121") # pass by sink can mutate Thing.
+    ]#
     if handleConstExpr(p, e, d): return
-  var t = e.typ.skipTypes(abstractInst)
+  var t = e.typ.skipTypes(abstractInstOwned)
   let isRef = t.kind == tyRef
 
   # check if we need to construct the object in a temporary
@@ -1228,42 +1547,33 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
         (d.k notin {locTemp,locLocalVar,locGlobalVar,locParam,locField}) or
         (isPartOf(d.lode, e) != arNo)
 
-  var tmp: TLoc
+  var tmp: TLoc = default(TLoc)
   var r: Rope
+  let needsZeroMem = p.config.selectedGC notin {gcArc, gcAtomicArc, gcOrc} or nfAllFieldsSet notin e.flags
   if useTemp:
-    getTemp(p, t, tmp)
+    tmp = getTemp(p, t)
     r = rdLoc(tmp)
     if isRef:
-      rawGenNew(p, tmp, nil)
-      t = t.lastSon.skipTypes(abstractInst)
+      rawGenNew(p, tmp, "", needsInit = nfAllFieldsSet notin e.flags)
+      t = t.elementType.skipTypes(abstractInstOwned)
       r = "(*$1)" % [r]
       gcUsage(p.config, e)
-    else:
+    elif needsZeroMem:
       constructLoc(p, tmp)
+    else:
+      genObjectInit(p, cpsStmts, t, tmp, constructObj)
   else:
-    resetLoc(p, d)
+    if needsZeroMem: resetLoc(p, d)
+    else: genObjectInit(p, cpsStmts, d.t, d, if isRef: constructRefObj else: constructObj)
     r = rdLoc(d)
   discard getTypeDesc(p.module, t)
   let ty = getUniqueType(t)
-  for i in 1 ..< e.len:
-    let it = e.sons[i]
-    var tmp2: TLoc
-    tmp2.r = r
-    let field = lookupFieldAgain(p, ty, it.sons[0].sym, tmp2.r)
-    if field.loc.r == nil: fillObjectFields(p.module, ty)
-    if field.loc.r == nil: internalError(p.config, e.info, "genObjConstr")
-    if it.len == 3 and optFieldCheck in p.options:
-      genFieldCheck(p, it.sons[2], r, field)
-    add(tmp2.r, ".")
-    add(tmp2.r, field.loc.r)
-    if useTemp:
-      tmp2.k = locTemp
-      tmp2.storage = if isRef: OnHeap else: OnStack
-    else:
-      tmp2.k = d.k
-      tmp2.storage = if isRef: OnHeap else: d.storage
-    tmp2.lode = it.sons[1]
-    expr(p, it.sons[1], tmp2)
+  for i in 1..<e.len:
+    var check: PNode = nil
+    if e[i].len == 3 and optFieldCheck in p.options:
+      check = e[i][2]
+    genFieldObjConstr(p, ty, useTemp, isRef, e[i][0], e[i][1], check, d, r, e.info)
+
   if useTemp:
     if d.k == locNone:
       d = tmp
@@ -1271,23 +1581,36 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
       genAssignment(p, d, tmp, {})
 
 proc lhsDoesAlias(a, b: PNode): bool =
+  result = false
   for y in b:
     if isPartOf(a, y) != arNo: return true
 
 proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
-  var arr, tmp: TLoc
+  var arr: TLoc
+  var tmp: TLoc = default(TLoc)
   # bug #668
   let doesAlias = lhsDoesAlias(d.lode, n)
   let dest = if doesAlias: addr(tmp) else: addr(d)
   if doesAlias:
-    getTemp(p, n.typ, tmp)
+    tmp = getTemp(p, n.typ)
   elif d.k == locNone:
-    getTemp(p, n.typ, d)
-  # generate call to newSeq before adding the elements per hand:
-  genNewSeqAux(p, dest[], intLiteral(sonsLen(n)))
-  for i in countup(0, sonsLen(n) - 1):
-    initLoc(arr, locExpr, n[i], OnHeap)
-    arr.r = ropecg(p.module, "$1->data[$2]", rdLoc(dest[]), intLiteral(i))
+    d = getTemp(p, n.typ)
+
+  var lit = newRopeAppender()
+  intLiteral(n.len, lit)
+  if optSeqDestructors in p.config.globalOptions:
+    let seqtype = n.typ
+    linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n",
+      [rdLoc dest[], lit, getTypeDesc(p.module, seqtype.elementType),
+      getSeqPayloadType(p.module, seqtype)])
+  else:
+    # generate call to newSeq before adding the elements per hand:
+    genNewSeqAux(p, dest[], lit, n.len == 0)
+  for i in 0..<n.len:
+    arr = initLoc(locExpr, n[i], OnHeap)
+    var lit = newRopeAppender()
+    intLiteral(i, lit)
+    arr.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(dest[]), lit, dataField(p)])
     arr.storage = OnHeap            # we know that sequences are on the heap
     expr(p, n[i], arr)
   gcUsage(p.config, n)
@@ -1298,110 +1621,129 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
       genAssignment(p, d, tmp, {})
 
 proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
-  var elem, a, arr: TLoc
-  if n.sons[1].kind == nkBracket:
-    n.sons[1].typ = n.typ
-    genSeqConstr(p, n.sons[1], d)
+  var elem, arr: TLoc
+  if n[1].kind == nkBracket:
+    n[1].typ = n.typ
+    genSeqConstr(p, n[1], d)
     return
   if d.k == locNone:
-    getTemp(p, n.typ, d)
+    d = getTemp(p, n.typ)
+  var a = initLocExpr(p, n[1])
   # generate call to newSeq before adding the elements per hand:
-  let L = int(lengthOrd(p.config, n.sons[1].typ))
-  genNewSeqAux(p, d, intLiteral(L))
-  initLocExpr(p, n.sons[1], a)
+  let L = toInt(lengthOrd(p.config, n[1].typ))
+  if optSeqDestructors in p.config.globalOptions:
+    let seqtype = n.typ
+    linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n",
+      [rdLoc d, L, getTypeDesc(p.module, seqtype.elementType),
+      getSeqPayloadType(p.module, seqtype)])
+  else:
+    var lit = newRopeAppender()
+    intLiteral(L, lit)
+    genNewSeqAux(p, d, lit, L == 0)
   # bug #5007; do not produce excessive C source code:
   if L < 10:
-    for i in countup(0, L - 1):
-      initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
-      elem.r = ropecg(p.module, "$1->data[$2]", rdLoc(d), intLiteral(i))
+    for i in 0..<L:
+      elem = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
+      var lit = newRopeAppender()
+      intLiteral(i, lit)
+      elem.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(d), lit, dataField(p)])
       elem.storage = OnHeap # we know that sequences are on the heap
-      initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage)
-      arr.r = ropecg(p.module, "$1[$2]", rdLoc(a), intLiteral(i))
-      genAssignment(p, elem, arr, {afDestIsNil, needToCopy})
-  else:
-    var i: TLoc
-    getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), i)
-    let oldCode = p.s(cpsStmts)
-    linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",  i.r, L.rope)
-    initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
-    elem.r = ropecg(p.module, "$1->data[$2]", rdLoc(d), rdLoc(i))
+      arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n[1].typ, abstractInst)), a.storage)
+      arr.snippet = ropecg(p.module, "$1[$2]", [rdLoc(a), lit])
+      genAssignment(p, elem, arr, {needToCopy})
+  else:
+    var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt))
+    linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",  [i.snippet, L])
+    elem = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
+    elem.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(d), rdLoc(i), dataField(p)])
     elem.storage = OnHeap # we know that sequences are on the heap
-    initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage)
-    arr.r = ropecg(p.module, "$1[$2]", rdLoc(a), rdLoc(i))
-    genAssignment(p, elem, arr, {afDestIsNil, needToCopy})
+    arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n[1].typ, abstractInst)), a.storage)
+    arr.snippet = ropecg(p.module, "$1[$2]", [rdLoc(a), rdLoc(i)])
+    genAssignment(p, elem, arr, {needToCopy})
     lineF(p, cpsStmts, "}$n", [])
 
 
 proc genNewFinalize(p: BProc, e: PNode) =
   var
-    a, b, f: TLoc
+    b: TLoc
     refType, bt: PType
     ti: Rope
-  refType = skipTypes(e.sons[1].typ, abstractVarRange)
-  initLocExpr(p, e.sons[1], a)
-  initLocExpr(p, e.sons[2], f)
-  initLoc(b, locExpr, a.lode, OnHeap)
-  ti = genTypeInfo(p.module, refType, e.info)
-  addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
-  b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [
+  refType = skipTypes(e[1].typ, abstractVarRange)
+  var a = initLocExpr(p, e[1])
+  var f = initLocExpr(p, e[2])
+  b = initLoc(locExpr, a.lode, OnHeap)
+  ti = genTypeInfo(p.config, p.module, refType, e.info)
+  p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
+  b.snippet = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [
       getTypeDesc(p.module, refType),
-      ti, getTypeDesc(p.module, skipTypes(refType.lastSon, abstractRange))])
+      ti, getTypeDesc(p.module, skipTypes(refType.elementType, abstractRange))])
   genAssignment(p, a, b, {})  # set the object type:
-  bt = skipTypes(refType.lastSon, abstractRange)
-  genObjectInit(p, cpsStmts, bt, a, false)
+  bt = skipTypes(refType.elementType, abstractRange)
+  genObjectInit(p, cpsStmts, bt, a, constructRefObj)
   gcUsage(p.config, e)
 
-proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo): Rope =
-  # unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we
-  # have to call it here first:
-  let ti = genTypeInfo(p.module, dest, info)
-  if tfFinal in dest.flags or (objHasKidsValid in p.module.flags and
-                               tfObjHasKids notin dest.flags):
-    result = "$1.m_type == $2" % [a, ti]
-  else:
-    discard cgsym(p.module, "TNimType")
-    inc p.module.labels
-    let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope
-    addf(p.module.s[cfsVars], "static TNimType* $#[2];$n", [cache])
-    result = ropecg(p.module, "#isObjWithCache($#.m_type, $#, $#)", a, ti, cache)
-  when false:
-    # former version:
-    result = ropecg(p.module, "#isObj($1.m_type, $2)",
-                  a, genTypeInfo(p.module, dest, info))
+proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo; result: var Rope) =
+  if optTinyRtti in p.config.globalOptions:
+    let token = $genDisplayElem(MD5Digest(hashType(dest, p.config)))
+    appcg(p.module, result, "#isObjDisplayCheck($#.m_type, $#, $#)", [a, getObjDepth(dest), token])
+  else:
+    # unfortunately 'genTypeInfoV1' sets tfObjHasKids as a side effect, so we
+    # have to call it here first:
+    let ti = genTypeInfoV1(p.module, dest, info)
+    if tfFinal in dest.flags or (objHasKidsValid in p.module.flags and
+                                tfObjHasKids notin dest.flags):
+      result.add "$1.m_type == $2" % [a, ti]
+    else:
+      cgsym(p.module, "TNimType")
+      inc p.module.labels
+      let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope
+      p.module.s[cfsVars].addf("static TNimType* $#[2];$n", [cache])
+      appcg(p.module, result, "#isObjWithCache($#.m_type, $#, $#)", [a, ti, cache])
 
 proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
-  var a: TLoc
-  initLocExpr(p, x, a)
+  var a: TLoc = initLocExpr(p, x)
   var dest = skipTypes(typ, typedescPtrs)
   var r = rdLoc(a)
-  var nilCheck: Rope = nil
-  var t = skipTypes(a.t, abstractInst)
+  var nilCheck: Rope = ""
+  var t = skipTypes(a.t, abstractInstOwned)
   while t.kind in {tyVar, tyLent, tyPtr, tyRef}:
     if t.kind notin {tyVar, tyLent}: nilCheck = r
     if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp:
-      r = ropecg(p.module, "(*$1)", r)
-    t = skipTypes(t.lastSon, typedescInst)
+      r = ropecg(p.module, "(*$1)", [r])
+    t = skipTypes(t.elementType, typedescInst+{tyOwned})
   discard getTypeDesc(p.module, t)
   if not p.module.compileToCpp:
-    while t.kind == tyObject and t.sons[0] != nil:
-      add(r, ~".Sup")
-      t = skipTypes(t.sons[0], skipPtrs)
+    while t.kind == tyObject and t.baseClass != nil:
+      r.add(".Sup")
+      t = skipTypes(t.baseClass, skipPtrs)
   if isObjLackingTypeField(t):
     globalError(p.config, x.info,
       "no 'of' operator available for pure objects")
-  if nilCheck != nil:
-    r = ropecg(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r, x.info))
+
+  var ro = newRopeAppender()
+  genOfHelper(p, dest, r, x.info, ro)
+  var ofExpr = newRopeAppender()
+  ofExpr.add "("
+  if nilCheck != "":
+    ofExpr.add "("
+    ofExpr.add nilCheck
+    ofExpr.add ") && ("
+    ofExpr.add ro
+    ofExpr.add "))"
   else:
-    r = ropecg(p.module, "($1)", genOfHelper(p, dest, r, x.info))
-  putIntoDest(p, d, x, r, a.storage)
+    ofExpr.add ro
+    ofExpr.add ")"
+
+  putIntoDest(p, d, x, ofExpr, a.storage)
 
 proc genOf(p: BProc, n: PNode, d: var TLoc) =
-  genOf(p, n.sons[1], n.sons[2].typ, d)
+  genOf(p, n[1], n[2].typ, d)
 
 proc genRepr(p: BProc, e: PNode, d: var TLoc) =
-  var a: TLoc
-  initLocExpr(p, e.sons[1], a)
-  var t = skipTypes(e.sons[1].typ, abstractVarRange)
+  if optTinyRtti in p.config.globalOptions:
+    localError(p.config, e.info, "'repr' is not available for --newruntime")
+  var a: TLoc = initLocExpr(p, e[1])
+  var t = skipTypes(e[1].typ, abstractVarRange)
   case t.kind
   of tyInt..tyInt64, tyUInt..tyUInt64:
     putIntoDest(p, d, e,
@@ -1415,87 +1757,144 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
   of tyEnum, tyOrdinal:
     putIntoDest(p, d, e,
                 ropecg(p.module, "#reprEnum((NI)$1, $2)", [
-                rdLoc(a), genTypeInfo(p.module, t, e.info)]), a.storage)
+                rdLoc(a), genTypeInfoV1(p.module, t, e.info)]), a.storage)
   of tyString:
     putIntoDest(p, d, e, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.storage)
   of tySet:
     putIntoDest(p, d, e, ropecg(p.module, "#reprSet($1, $2)", [
-                addrLoc(p.config, a), genTypeInfo(p.module, t, e.info)]), a.storage)
+                addrLoc(p.config, a), genTypeInfoV1(p.module, t, e.info)]), a.storage)
   of tyOpenArray, tyVarargs:
-    var b: TLoc
-    case a.t.kind
+    var b: TLoc = default(TLoc)
+    case skipTypes(a.t, abstractVarRange).kind
     of tyOpenArray, tyVarargs:
       putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage)
     of tyString, tySequence:
       putIntoDest(p, b, e,
-                  "$1->data, ($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)], a.storage)
+                  "($4) ? ($1$3) : NIM_NIL, $2" %
+                    [rdLoc(a), lenExpr(p, a), dataField(p), dataFieldAccessor(p, a.rdLoc)],
+                  a.storage)
     of tyArray:
       putIntoDest(p, b, e,
                   "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))], a.storage)
-    else: internalError(p.config, e.sons[0].info, "genRepr()")
+    else: internalError(p.config, e[0].info, "genRepr()")
     putIntoDest(p, d, e,
         ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b),
-        genTypeInfo(p.module, elemType(t), e.info)]), a.storage)
-  of tyCString, tyArray, tyRef, tyPtr, tyPointer, tyNil, tySequence:
+        genTypeInfoV1(p.module, elemType(t), e.info)]), a.storage)
+  of tyCstring, tyArray, tyRef, tyPtr, tyPointer, tyNil, tySequence:
     putIntoDest(p, d, e,
                 ropecg(p.module, "#reprAny($1, $2)", [
-                rdLoc(a), genTypeInfo(p.module, t, e.info)]), a.storage)
+                rdLoc(a), genTypeInfoV1(p.module, t, e.info)]), a.storage)
   of tyEmpty, tyVoid:
     localError(p.config, e.info, "'repr' doesn't support 'void' type")
   else:
     putIntoDest(p, d, e, ropecg(p.module, "#reprAny($1, $2)",
-                              [addrLoc(p.config, a), genTypeInfo(p.module, t, e.info)]),
+                              [addrLoc(p.config, a), genTypeInfoV1(p.module, t, e.info)]),
                                a.storage)
   gcUsage(p.config, e)
 
-proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) =
-  let t = e.sons[1].typ
-  putIntoDest(p, d, e, genTypeInfo(p.module, t, e.info))
+proc rdMType(p: BProc; a: TLoc; nilCheck: var Rope; result: var Rope; enforceV1 = false) =
+  var derefs = rdLoc(a)
+  var t = skipTypes(a.t, abstractInst)
+  while t.kind in {tyVar, tyLent, tyPtr, tyRef}:
+    if t.kind notin {tyVar, tyLent}: nilCheck = derefs
+    if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp:
+      derefs = "(*$1)" % [derefs]
+    t = skipTypes(t.elementType, abstractInst)
+  result.add derefs
+  discard getTypeDesc(p.module, t)
+  if not p.module.compileToCpp:
+    while t.kind == tyObject and t.baseClass != nil:
+      result.add(".Sup")
+      t = skipTypes(t.baseClass, skipPtrs)
+  result.add ".m_type"
+  if optTinyRtti in p.config.globalOptions and enforceV1:
+    result.add "->typeInfoV1"
 
-proc genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) =
-  var a: TLoc
-  initLocExpr(p, n.sons[1], a)
-  a.r = ropecg(p.module, frmt, [rdLoc(a)])
-  if d.k == locNone: getTemp(p, n.typ, d)
+proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) =
+  cgsym(p.module, "TNimType")
+  let t = e[1].typ
+  # ordinary static type information
+  putIntoDest(p, d, e, genTypeInfoV1(p.module, t, e.info))
+
+proc genGetTypeInfoV2(p: BProc, e: PNode, d: var TLoc) =
+  let t = e[1].typ
+  if isFinal(t) or e[0].sym.name.s != "getDynamicTypeInfo":
+    # ordinary static type information
+    putIntoDest(p, d, e, genTypeInfoV2(p.module, t, e.info))
+  else:
+    var a: TLoc = initLocExpr(p, e[1])
+    var nilCheck = ""
+    # use the dynamic type stored at offset 0:
+    var rt = newRopeAppender()
+    rdMType(p, a, nilCheck, rt)
+    putIntoDest(p, d, e, rt)
+
+proc genAccessTypeField(p: BProc; e: PNode; d: var TLoc) =
+  var a: TLoc = initLocExpr(p, e[1])
+  var nilCheck = ""
+  # use the dynamic type stored at offset 0:
+  var rt = newRopeAppender()
+  rdMType(p, a, nilCheck, rt)
+  putIntoDest(p, d, e, rt)
+
+template genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) =
+  var a: TLoc = initLocExpr(p, n[1])
+  a.snippet = ropecg(p.module, frmt, [rdLoc(a)])
+  a.flags.excl lfIndirect # this flag should not be propagated here (not just for HCR)
+  if d.k == locNone: d = getTemp(p, n.typ)
   genAssignment(p, d, a, {})
   gcUsage(p.config, n)
 
 proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
-  var a = e.sons[1]
-  if a.kind == nkHiddenAddr: a = a.sons[0]
+  var a = e[1]
+  if a.kind == nkHiddenAddr: a = a[0]
   var typ = skipTypes(a.typ, abstractVar + tyUserTypeClasses)
   case typ.kind
   of tyOpenArray, tyVarargs:
-    if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)")
-    else: unaryExpr(p, e, d, "$1Len_0")
-  of tyCString:
-    useStringh(p.module)
-    if op == mHigh: unaryExpr(p, e, d, "($1 ? (strlen($1)-1) : -1)")
-    else: unaryExpr(p, e, d, "($1 ? strlen($1) : 0)")
-  of tyString:
-    if not p.module.compileToCpp:
-      if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->Sup.len-1) : -1)")
-      else: unaryExpr(p, e, d, "($1 ? $1->Sup.len : 0)")
-    else:
-      if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->len-1) : -1)")
-      else: unaryExpr(p, e, d, "($1 ? $1->len : 0)")
-  of tySequence:
-    var a, tmp: TLoc
-    initLocExpr(p, e[1], a)
-    getIntTemp(p, tmp)
-    var frmt: FormatStr
-    if not p.module.compileToCpp:
+    # Bug #9279, len(toOpenArray()) has to work:
+    if a.kind in nkCallKinds and a[0].kind == nkSym and a[0].sym.magic == mSlice:
+      # magic: pass slice to openArray:
+      var m = initLocExpr(p, a[1])
+      var b = initLocExpr(p, a[2])
+      var c = initLocExpr(p, a[3])
+      if optBoundsCheck in p.options:
+        genBoundsCheck(p, m, b, c, skipTypes(m.t, abstractVarRange))
       if op == mHigh:
-        frmt = "$1 = ($2 ? ($2->Sup.len-1) : -1);$n"
+        putIntoDest(p, d, e, ropecg(p.module, "(($2)-($1))", [rdLoc(b), rdLoc(c)]))
       else:
-        frmt = "$1 = ($2 ? $2->Sup.len : 0);$n"
+        putIntoDest(p, d, e, ropecg(p.module, "(($2)-($1)+1)", [rdLoc(b), rdLoc(c)]))
     else:
-      if op == mHigh:
-        frmt = "$1 = ($2 ? ($2->len-1) : -1);$n"
+      if not reifiedOpenArray(a):
+        if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)")
+        else: unaryExpr(p, e, d, "$1Len_0")
       else:
-        frmt = "$1 = ($2 ? $2->len : 0);$n"
-    lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a))
-    putIntoDest(p, d, e, tmp.r)
+        let isDeref = a.kind in {nkHiddenDeref, nkDerefExpr}
+        if op == mHigh:
+          if isDeref:
+            unaryExpr(p, e, d, "($1->Field1-1)")
+          else:
+            unaryExpr(p, e, d, "($1.Field1-1)")
+        else:
+          if isDeref:
+            unaryExpr(p, e, d, "$1->Field1")
+          else:
+            unaryExpr(p, e, d, "$1.Field1")
+  of tyCstring:
+    if op == mHigh: unaryExpr(p, e, d, "(#nimCStrLen($1)-1)")
+    else: unaryExpr(p, e, d, "#nimCStrLen($1)")
+  of tyString:
+    var a: TLoc = initLocExpr(p, e[1])
+    var x = lenExpr(p, a)
+    if op == mHigh: x = "($1-1)" % [x]
+    putIntoDest(p, d, e, x)
+  of tySequence:
+    # we go through a temporary here because people write bullshit code.
+    var tmp: TLoc = getIntTemp(p)
+    var a = initLocExpr(p, e[1])
+    var x = lenExpr(p, a)
+    if op == mHigh: x = "($1-1)" % [x]
+    lineCg(p, cpsStmts, "$1 = $2;$n", [tmp.snippet, x])
+    putIntoDest(p, d, e, tmp.snippet)
   of tyArray:
     # YYY: length(sideeffect) is optimized away incorrectly?
     if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(p.config, typ)))
@@ -1503,47 +1902,72 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   else: internalError(p.config, e.info, "genArrayLen()")
 
 proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
-  var a, b: TLoc
+  if optSeqDestructors in p.config.globalOptions:
+    e[1] = makeAddr(e[1], p.module.idgen)
+    genCall(p, e, d)
+    return
   assert(d.k == locNone)
-  var x = e.sons[1]
+  var x = e[1]
   if x.kind in {nkAddr, nkHiddenAddr}: x = x[0]
-  initLocExpr(p, x, a)
-  initLocExpr(p, e.sons[2], b)
-  let t = skipTypes(e.sons[1].typ, {tyVar})
-  let setLenPattern = if not p.module.compileToCpp:
-      "$1 = ($3) #setLengthSeqV2(&($1)->Sup, $4, $2);$n"
-    else:
-      "$1 = ($3) #setLengthSeqV2($1, $4, $2);$n"
+  var a = initLocExpr(p, x)
+  var b = initLocExpr(p, e[2])
+  let t = skipTypes(e[1].typ, {tyVar})
 
-  lineCg(p, cpsStmts, setLenPattern, [
+  var call = initLoc(locCall, e, OnHeap)
+  if not p.module.compileToCpp:
+    const setLenPattern = "($3) #setLengthSeqV2(($1)?&($1)->Sup:NIM_NIL, $4, $2)"
+    call.snippet = ropecg(p.module, setLenPattern, [
       rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
-      genTypeInfo(p.module, t.skipTypes(abstractInst), e.info)])
+      genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)])
+
+  else:
+    const setLenPattern = "($3) #setLengthSeqV2($1, $4, $2)"
+    call.snippet = ropecg(p.module, setLenPattern, [
+      rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
+      genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)])
+
+  genAssignment(p, a, call, {})
   gcUsage(p.config, e)
 
 proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) =
-  binaryStmt(p, e, d, "$1 = #setLengthStr($1, $2);$n")
-  gcUsage(p.config, e)
+  if optSeqDestructors in p.config.globalOptions:
+    binaryStmtAddr(p, e, d, "setLengthStrV2")
+  else:
+    if d.k != locNone: internalError(p.config, e.info, "genSetLengthStr")
+    var a = initLocExpr(p, e[1])
+    var b = initLocExpr(p, e[2])
+
+    var call = initLoc(locCall, e, OnHeap)
+    call.snippet = ropecg(p.module, "#setLengthStr($1, $2)", [
+        rdLoc(a), rdLoc(b)])
+    genAssignment(p, a, call, {})
+    gcUsage(p.config, e)
 
 proc genSwap(p: BProc, e: PNode, d: var TLoc) =
   # swap(a, b) -->
   # temp = a
   # a = b
   # b = temp
-  var a, b, tmp: TLoc
-  getTemp(p, skipTypes(e.sons[1].typ, abstractVar), tmp)
-  initLocExpr(p, e.sons[1], a) # eval a
-  initLocExpr(p, e.sons[2], b) # eval b
+  cowBracket(p, e[1])
+  cowBracket(p, e[2])
+  var tmp: TLoc = getTemp(p, skipTypes(e[1].typ, abstractVar))
+  var a = initLocExpr(p, e[1]) # eval a
+  var b = initLocExpr(p, e[2]) # eval b
   genAssignment(p, tmp, a, {})
   genAssignment(p, a, b, {})
   genAssignment(p, b, tmp, {})
 
-proc rdSetElemLoc(conf: ConfigRef; a: TLoc, setType: PType): Rope =
+proc rdSetElemLoc(conf: ConfigRef; a: TLoc, typ: PType; result: var Rope) =
   # read a location of an set element; it may need a subtraction operation
   # before the set operation
-  result = rdCharLoc(a)
+  result.add "("
+  result.add rdCharLoc(a)
+  let setType = typ.skipTypes(abstractPtrs)
   assert(setType.kind == tySet)
   if firstOrd(conf, setType) != 0:
-    result = "($1- $2)" % [result, rope(firstOrd(conf, setType))]
+    result.add " - "
+    result.add rope(firstOrd(conf, setType))
+  result.add ")"
 
 proc fewCmps(conf: ConfigRef; s: PNode): bool =
   # this function estimates whether it is better to emit code
@@ -1554,94 +1978,110 @@ proc fewCmps(conf: ConfigRef; s: PNode): bool =
   elif elemType(s.typ).kind in {tyInt, tyInt16..tyInt64}:
     result = true             # better not emit the set if int is basetype!
   else:
-    result = sonsLen(s) <= 8  # 8 seems to be a good value
+    result = s.len <= 8  # 8 seems to be a good value
 
-proc binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) =
-  putIntoDest(p, d, e, frmt % [rdLoc(a), rdSetElemLoc(p.config, b, a.t)])
+template binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) =
+  var elem = newRopeAppender()
+  rdSetElemLoc(p.config, b, a.t, elem)
+  putIntoDest(p, d, e, frmt % [rdLoc(a), elem])
 
 proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) =
-  case int(getSize(p.config, skipTypes(e.sons[1].typ, abstractVar)))
-  of 1: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&7U)))!=0)")
-  of 2: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&15U)))!=0)")
-  of 4: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&31U)))!=0)")
+  case int(getSize(p.config, skipTypes(e[1].typ, abstractVar)))
+  of 1: binaryExprIn(p, e, a, b, d, "(($1 &((NU8)1<<((NU)($2)&7U)))!=0)")
+  of 2: binaryExprIn(p, e, a, b, d, "(($1 &((NU16)1<<((NU)($2)&15U)))!=0)")
+  of 4: binaryExprIn(p, e, a, b, d, "(($1 &((NU32)1<<((NU)($2)&31U)))!=0)")
   of 8: binaryExprIn(p, e, a, b, d, "(($1 &((NU64)1<<((NU)($2)&63U)))!=0)")
   else: binaryExprIn(p, e, a, b, d, "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)")
 
-proc binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) =
-  var a, b: TLoc
+template binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) =
   assert(d.k == locNone)
-  initLocExpr(p, e.sons[1], a)
-  initLocExpr(p, e.sons[2], b)
-  lineF(p, cpsStmts, frmt, [rdLoc(a), rdSetElemLoc(p.config, b, a.t)])
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
+  var elem = newRopeAppender()
+  rdSetElemLoc(p.config, b, a.t, elem)
+  lineF(p, cpsStmts, frmt, [rdLoc(a), elem])
 
 proc genInOp(p: BProc, e: PNode, d: var TLoc) =
   var a, b, x, y: TLoc
-  if (e.sons[1].kind == nkCurly) and fewCmps(p.config, e.sons[1]):
+  if (e[1].kind == nkCurly) and fewCmps(p.config, e[1]):
     # a set constructor but not a constant set:
     # do not emit the set, but generate a bunch of comparisons; and if we do
     # so, we skip the unnecessary range check: This is a semantical extension
     # that code now relies on. :-/ XXX
-    let ea = if e.sons[2].kind in {nkChckRange, nkChckRange64}:
-               e.sons[2].sons[0]
+    let ea = if e[2].kind in {nkChckRange, nkChckRange64}:
+               e[2][0]
              else:
-               e.sons[2]
-    initLocExpr(p, ea, a)
-    initLoc(b, locExpr, e, OnUnknown)
-    b.r = rope("(")
-    var length = sonsLen(e.sons[1])
-    for i in countup(0, length - 1):
-      let it = e.sons[1].sons[i]
-      if it.kind == nkRange:
-        initLocExpr(p, it.sons[0], x)
-        initLocExpr(p, it.sons[1], y)
-        addf(b.r, "$1 >= $2 && $1 <= $3",
-             [rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)])
-      else:
-        initLocExpr(p, it, x)
-        addf(b.r, "$1 == $2", [rdCharLoc(a), rdCharLoc(x)])
-      if i < length - 1: add(b.r, " || ")
-    add(b.r, ")")
-    putIntoDest(p, d, e, b.r)
-  else:
-    assert(e.sons[1].typ != nil)
-    assert(e.sons[2].typ != nil)
-    initLocExpr(p, e.sons[1], a)
-    initLocExpr(p, e.sons[2], b)
+               e[2]
+    a = initLocExpr(p, ea)
+    b = initLoc(locExpr, e, OnUnknown)
+    if e[1].len > 0:
+      b.snippet = rope("(")
+      for i in 0..<e[1].len:
+        let it = e[1][i]
+        if it.kind == nkRange:
+          x = initLocExpr(p, it[0])
+          y = initLocExpr(p, it[1])
+          b.snippet.addf("$1 >= $2 && $1 <= $3",
+               [rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)])
+        else:
+          x = initLocExpr(p, it)
+          b.snippet.addf("$1 == $2", [rdCharLoc(a), rdCharLoc(x)])
+        if i < e[1].len - 1: b.snippet.add(" || ")
+      b.snippet.add(")")
+    else:
+      # handle the case of an empty set
+      b.snippet = rope("0")
+    putIntoDest(p, d, e, b.snippet)
+  else:
+    assert(e[1].typ != nil)
+    assert(e[2].typ != nil)
+    a = initLocExpr(p, e[1])
+    b = initLocExpr(p, e[2])
     genInExprAux(p, e, a, b, d)
 
 proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   const
-    lookupOpr: array[mLeSet..mSymDiffSet, string] = [
+    lookupOpr: array[mLeSet..mMinusSet, string] = [
+      "for ($1 = 0; $1 < $2; $1++) { $n" &
+      "  $3 = (($4[$1] & ~ $5[$1]) == 0);$n" &
+      "  if (!$3) break;}$n",
       "for ($1 = 0; $1 < $2; $1++) { $n" &
-        "  $3 = (($4[$1] & ~ $5[$1]) == 0);$n" &
-        "  if (!$3) break;}$n", "for ($1 = 0; $1 < $2; $1++) { $n" &
-        "  $3 = (($4[$1] & ~ $5[$1]) == 0);$n" & "  if (!$3) break;}$n" &
-        "if ($3) $3 = (memcmp($4, $5, $2) != 0);$n",
-      "&", "|", "& ~", "^"]
-  var a, b, i: TLoc
-  var setType = skipTypes(e.sons[1].typ, abstractVar)
+      "  $3 = (($4[$1] & ~ $5[$1]) == 0);$n" &
+      "  if (!$3) break;}$n" &
+      "if ($3) $3 = (#nimCmpMem($4, $5, $2) != 0);$n",
+      "&",
+      "|",
+      "& ~"]
+  var a, b: TLoc
+  var i: TLoc
+  var setType = skipTypes(e[1].typ, abstractVar)
   var size = int(getSize(p.config, setType))
   case size
   of 1, 2, 4, 8:
     case op
     of mIncl:
-      var ts = "NU" & $(size * 8)
-      binaryStmtInExcl(p, e, d,
-          "$1 |= ((" & ts & ")1)<<(($2)%(sizeof(" & ts & ")*8));$n")
+      case size
+      of 1: binaryStmtInExcl(p, e, d, "$1 |= ((NU8)1)<<(($2) & 7);$n")
+      of 2: binaryStmtInExcl(p, e, d, "$1 |= ((NU16)1)<<(($2) & 15);$n")
+      of 4: binaryStmtInExcl(p, e, d, "$1 |= ((NU32)1)<<(($2) & 31);$n")
+      of 8: binaryStmtInExcl(p, e, d, "$1 |= ((NU64)1)<<(($2) & 63);$n")
+      else: assert(false, $size)
     of mExcl:
-      var ts = "NU" & $(size * 8)
-      binaryStmtInExcl(p, e, d, "$1 &= ~(((" & ts & ")1) << (($2) % (sizeof(" &
-          ts & ")*8)));$n")
+      case size
+      of 1: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU8)1) << (($2) & 7));$n")
+      of 2: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU16)1) << (($2) & 15));$n")
+      of 4: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU32)1) << (($2) & 31));$n")
+      of 8: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU64)1) << (($2) & 63));$n")
+      else: assert(false, $size)
     of mCard:
       if size <= 4: unaryExprChar(p, e, d, "#countBits32($1)")
       else: unaryExprChar(p, e, d, "#countBits64($1)")
-    of mLtSet: binaryExprChar(p, e, d, "(($1 & ~ $2 ==0)&&($1 != $2))")
+    of mLtSet: binaryExprChar(p, e, d, "((($1 & ~ $2)==0)&&($1 != $2))")
     of mLeSet: binaryExprChar(p, e, d, "(($1 & ~ $2)==0)")
     of mEqSet: binaryExpr(p, e, d, "($1 == $2)")
     of mMulSet: binaryExpr(p, e, d, "($1 & $2)")
     of mPlusSet: binaryExpr(p, e, d, "($1 | $2)")
     of mMinusSet: binaryExpr(p, e, d, "($1 & ~ $2)")
-    of mSymDiffSet: binaryExpr(p, e, d, "($1 ^ $2)")
     of mInSet:
       genInOp(p, e, d)
     else: internalError(p.config, e.info, "genSetOp()")
@@ -1649,23 +2089,32 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     case op
     of mIncl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] |=(1U<<($2&7U));$n")
     of mExcl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] &= ~(1U<<($2&7U));$n")
-    of mCard: unaryExprChar(p, e, d, "#cardSet($1, " & $size & ')')
+    of mCard:
+      var a: TLoc = initLocExpr(p, e[1])
+      putIntoDest(p, d, e, ropecg(p.module, "#cardSet($1, $2)", [rdCharLoc(a), size]))
     of mLtSet, mLeSet:
-      getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), i) # our counter
-      initLocExpr(p, e.sons[1], a)
-      initLocExpr(p, e.sons[2], b)
-      if d.k == locNone: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyBool), d)
-      lineF(p, cpsStmts, lookupOpr[op],
-           [rdLoc(i), rope(size), rdLoc(d), rdLoc(a), rdLoc(b)])
+      i = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter
+      a = initLocExpr(p, e[1])
+      b = initLocExpr(p, e[2])
+      if d.k == locNone: d = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyBool))
+      if op == mLtSet:
+        linefmt(p, cpsStmts, lookupOpr[mLtSet],
+           [rdLoc(i), size, rdLoc(d), rdLoc(a), rdLoc(b)])
+      else:
+        linefmt(p, cpsStmts, lookupOpr[mLeSet],
+           [rdLoc(i), size, rdLoc(d), rdLoc(a), rdLoc(b)])
     of mEqSet:
-      useStringh(p.module)
-      binaryExprChar(p, e, d, "(memcmp($1, $2, " & $(size) & ")==0)")
-    of mMulSet, mPlusSet, mMinusSet, mSymDiffSet:
+      assert(e[1].typ != nil)
+      assert(e[2].typ != nil)
+      var a = initLocExpr(p, e[1])
+      var b = initLocExpr(p, e[2])
+      putIntoDest(p, d, e, ropecg(p.module, "(#nimCmpMem($1, $2, $3)==0)", [a.rdCharLoc, b.rdCharLoc, size]))
+    of mMulSet, mPlusSet, mMinusSet:
       # we inline the simple for loop for better code generation:
-      getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), i) # our counter
-      initLocExpr(p, e.sons[1], a)
-      initLocExpr(p, e.sons[2], b)
-      if d.k == locNone: getTemp(p, a.t, d)
+      i = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter
+      a = initLocExpr(p, e[1])
+      b = initLocExpr(p, e[2])
+      if d.k == locNone: d = getTemp(p, setType)
       lineF(p, cpsStmts,
            "for ($1 = 0; $1 < $2; $1++) $n" &
            "  $3[$1] = $4[$1] $6 $5[$1];$n", [
@@ -1679,25 +2128,34 @@ proc genOrd(p: BProc, e: PNode, d: var TLoc) =
 
 proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
   const
-    ValueTypes = {tyTuple, tyObject, tyArray, tyOpenArray, tyVarargs}
+    ValueTypes = {tyTuple, tyObject, tyArray, tyOpenArray, tyVarargs, tyUncheckedArray}
   # we use whatever C gives us. Except if we have a value-type, we need to go
   # through its address:
-  var a: TLoc
-  initLocExpr(p, e.sons[1], a)
-  let etyp = skipTypes(e.typ, abstractRange)
+  var a: TLoc = initLocExpr(p, e[1])
+  let etyp = skipTypes(e.typ, abstractRange+{tyOwned})
+  let srcTyp = skipTypes(e[1].typ, abstractRange)
   if etyp.kind in ValueTypes and lfIndirect notin a.flags:
     putIntoDest(p, d, e, "(*($1*) ($2))" %
         [getTypeDesc(p.module, e.typ), addrLoc(p.config, a)], a.storage)
-  elif etyp.kind == tyProc and etyp.callConv == ccClosure:
+  elif etyp.kind == tyProc and etyp.callConv == ccClosure and srcTyp.callConv != ccClosure:
     putIntoDest(p, d, e, "(($1) ($2))" %
         [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage)
   else:
-    let srcTyp = skipTypes(e.sons[1].typ, abstractRange)
     # C++ does not like direct casts from pointer to shorter integral types
     if srcTyp.kind in {tyPtr, tyPointer} and etyp.kind in IntegralTypes:
       putIntoDest(p, d, e, "(($1) (ptrdiff_t) ($2))" %
           [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
+    elif optSeqDestructors in p.config.globalOptions and etyp.kind in {tySequence, tyString}:
+      putIntoDest(p, d, e, "(*($1*) (&$2))" %
+          [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
+    elif etyp.kind == tyBool and srcTyp.kind in IntegralTypes:
+      putIntoDest(p, d, e, "(($1) != 0)" % [rdCharLoc(a)], a.storage)
     else:
+      if etyp.kind == tyPtr:
+        # generates the definition of structs for casts like cast[ptr object](addr x)[]
+        let internalType = etyp.skipTypes({tyPtr})
+        if internalType.kind == tyObject:
+          discard getTypeDesc(p.module, internalType)
       putIntoDest(p, d, e, "(($1) ($2))" %
           [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
 
@@ -1705,100 +2163,272 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) =
   const ValueTypes = {tyFloat..tyFloat128, tyTuple, tyObject, tyArray}
   let
     destt = skipTypes(e.typ, abstractRange)
-    srct = skipTypes(e.sons[1].typ, abstractRange)
+    srct = skipTypes(e[1].typ, abstractRange)
   if destt.kind in ValueTypes or srct.kind in ValueTypes:
     # 'cast' and some float type involved? --> use a union.
     inc(p.labels)
     var lbl = p.labels.rope
-    var tmp: TLoc
-    tmp.r = "LOC$1.source" % [lbl]
-    linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n",
-      getTypeDesc(p.module, e.sons[1].typ), getTypeDesc(p.module, e.typ), lbl)
+    var tmp: TLoc = default(TLoc)
+    tmp.snippet = "LOC$1.source" % [lbl]
+    let destsize = getSize(p.config, destt)
+    let srcsize = getSize(p.config, srct)
+
+    if destsize > srcsize:
+      linefmt(p, cpsLocals, "union { $1 dest; $2 source; } LOC$3;$n #nimZeroMem(&LOC$3, sizeof(LOC$3));$n",
+        [getTypeDesc(p.module, e.typ), getTypeDesc(p.module, e[1].typ), lbl])
+    else:
+      linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n",
+        [getTypeDesc(p.module, e[1].typ), getTypeDesc(p.module, e.typ), lbl])
     tmp.k = locExpr
     tmp.lode = lodeTyp srct
     tmp.storage = OnStack
     tmp.flags = {}
-    expr(p, e.sons[1], tmp)
+    expr(p, e[1], tmp)
     putIntoDest(p, d, e, "LOC$#.dest" % [lbl], tmp.storage)
   else:
     # I prefer the shorter cast version for pointer types -> generate less
     # C code; plus it's the right thing to do for closures:
     genSomeCast(p, e, d)
 
-proc genRangeChck(p: BProc, n: PNode, d: var TLoc, magic: string) =
-  var a: TLoc
+proc genRangeChck(p: BProc, n: PNode, d: var TLoc) =
+  var a: TLoc = initLocExpr(p, n[0])
   var dest = skipTypes(n.typ, abstractVar)
-  # range checks for unsigned turned out to be buggy and annoying:
-  if optRangeCheck notin p.options or dest.skipTypes({tyRange}).kind in
-                                             {tyUInt..tyUInt64}:
-    initLocExpr(p, n.sons[0], a)
-    putIntoDest(p, d, n, "(($1) ($2))" %
-        [getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage)
+  if optRangeCheck notin p.options or (dest.kind in {tyUInt..tyUInt64} and
+      checkUnsignedConversions notin p.config.legacyFeatures):
+    discard "no need to generate a check because it was disabled"
   else:
-    initLocExpr(p, n.sons[0], a)
-    putIntoDest(p, d, lodeTyp dest, ropecg(p.module, "(($1)#$5($2, $3, $4))", [
-        getTypeDesc(p.module, dest), rdCharLoc(a),
-        genLiteral(p, n.sons[1], dest), genLiteral(p, n.sons[2], dest),
-        rope(magic)]), a.storage)
+    let n0t = n[0].typ
+
+    # emit range check:
+    if n0t.kind in {tyUInt, tyUInt64}:
+      var first = newRopeAppender()
+      genLiteral(p, n[1], dest, first)
+      var last = newRopeAppender()
+      genLiteral(p, n[2], dest, last)
+      linefmt(p, cpsStmts, "if ($1 > ($5)($3)){ #raiseRangeErrorNoArgs(); ",
+        [rdCharLoc(a), first, last,
+        raiser, getTypeDesc(p.module, n0t)])
+      raiseInstr(p, p.s(cpsStmts))
+      linefmt p, cpsStmts, "}$n", []
+
+    else:
+      let raiser =
+        case skipTypes(n.typ, abstractVarRange).kind
+        of tyUInt..tyUInt64, tyChar: "raiseRangeErrorU"
+        of tyFloat..tyFloat128: "raiseRangeErrorF"
+        else: "raiseRangeErrorI"
+      cgsym(p.module, raiser)
+
+      let boundaryCast =
+        if n0t.skipTypes(abstractVarRange).kind in {tyUInt, tyUInt32, tyUInt64}:
+          "(NI64)"
+        else:
+          ""
+      var first = newRopeAppender()
+      genLiteral(p, n[1], dest, first)
+      var last = newRopeAppender()
+      genLiteral(p, n[2], dest, last)
+      linefmt(p, cpsStmts, "if ($5($1) < $2 || $5($1) > $3){ $4($1, $2, $3); ",
+        [rdCharLoc(a), first, last,
+        raiser, boundaryCast])
+      raiseInstr(p, p.s(cpsStmts))
+      linefmt p, cpsStmts, "}$n", []
+
+  if sameBackendTypeIgnoreRange(dest, n[0].typ):
+    # don't cast so an address can be taken for `var` conversions
+    putIntoDest(p, d, n, "($1)" % [rdCharLoc(a)], a.storage)
+  else:
+    putIntoDest(p, d, n, "(($1) ($2))" %
+      [getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage)
 
 proc genConv(p: BProc, e: PNode, d: var TLoc) =
   let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink})
-  if sameBackendType(destType, e.sons[1].typ):
-    expr(p, e.sons[1], d)
+  if sameBackendTypeIgnoreRange(destType, e[1].typ):
+    expr(p, e[1], d)
   else:
     genSomeCast(p, e, d)
 
 proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
-  var a: TLoc
-  initLocExpr(p, n.sons[0], a)
-  putIntoDest(p, d, n, "($1 ? $1->data : (NCSTRING)\"\")" % [rdLoc(a)],
+  var a: TLoc = initLocExpr(p, n[0])
+  putIntoDest(p, d, n,
+              ropecg(p.module, "#nimToCStringConv($1)", [rdLoc(a)]),
+#                "($1 ? $1->data : (NCSTRING)\"\")" % [a.rdLoc],
               a.storage)
 
 proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
-  var a: TLoc
-  initLocExpr(p, n.sons[0], a)
-  putIntoDest(p, d, n,
-              ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]),
-              a.storage)
+  var a: TLoc = initLocExpr(p, n[0])
+  if p.module.compileToCpp:
+    # fixes for const qualifier; bug #12703; bug #19588
+    putIntoDest(p, d, n,
+            ropecg(p.module, "#cstrToNimstr((NCSTRING) $1)", [rdLoc(a)]),
+            a.storage)
+  else:
+    putIntoDest(p, d, n,
+                ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]),
+                a.storage)
   gcUsage(p.config, n)
 
 proc genStrEquals(p: BProc, e: PNode, d: var TLoc) =
   var x: TLoc
-  var a = e.sons[1]
-  var b = e.sons[2]
+  var a = e[1]
+  var b = e[2]
   if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "":
-    initLocExpr(p, e.sons[2], x)
+    x = initLocExpr(p, e[2])
     putIntoDest(p, d, e,
-      ropecg(p.module, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p)))
+      ropecg(p.module, "($1 == 0)", [lenExpr(p, x)]))
   elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "":
-    initLocExpr(p, e.sons[1], x)
+    x = initLocExpr(p, e[1])
     putIntoDest(p, d, e,
-      ropecg(p.module, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p)))
+      ropecg(p.module, "($1 == 0)", [lenExpr(p, x)]))
   else:
     binaryExpr(p, e, d, "#eqStrings($1, $2)")
 
 proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   if {optNaNCheck, optInfCheck} * p.options != {}:
     const opr: array[mAddF64..mDivF64, string] = ["+", "-", "*", "/"]
-    var a, b: TLoc
-    assert(e.sons[1].typ != nil)
-    assert(e.sons[2].typ != nil)
-    initLocExpr(p, e.sons[1], a)
-    initLocExpr(p, e.sons[2], b)
+    assert(e[1].typ != nil)
+    assert(e[2].typ != nil)
+    var a = initLocExpr(p, e[1])
+    var b = initLocExpr(p, e[2])
     putIntoDest(p, d, e, ropecg(p.module, "(($4)($2) $1 ($4)($3))",
-                              rope(opr[m]), rdLoc(a), rdLoc(b),
-                              getSimpleTypeDesc(p.module, e[1].typ)))
+                              [opr[m], rdLoc(a), rdLoc(b),
+                              getSimpleTypeDesc(p.module, e[1].typ)]))
     if optNaNCheck in p.options:
-      linefmt(p, cpsStmts, "#nanCheck($1);$n", rdLoc(d))
+      linefmt(p, cpsStmts, "if ($1 != $1){ #raiseFloatInvalidOp(); ", [rdLoc(d)])
+      raiseInstr(p, p.s(cpsStmts))
+      linefmt p, cpsStmts, "}$n", []
+
     if optInfCheck in p.options:
-      linefmt(p, cpsStmts, "#infCheck($1);$n", rdLoc(d))
+      linefmt(p, cpsStmts, "if ($1 != 0.0 && $1*0.5 == $1) { #raiseFloatOverflow($1); ", [rdLoc(d)])
+      raiseInstr(p, p.s(cpsStmts))
+      linefmt p, cpsStmts, "}$n", []
+
   else:
     binaryArith(p, e, d, m)
 
+proc genWasMoved(p: BProc; n: PNode) =
+  var a: TLoc
+  let n1 = n[1].skipAddr
+  if p.withinBlockLeaveActions > 0 and notYetAlive(n1):
+    discard
+  else:
+    a = initLocExpr(p, n1, {lfEnforceDeref})
+    resetLoc(p, a)
+    #linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
+    #  [addrLoc(p.config, a), getTypeDesc(p.module, a.t)])
+
+proc genMove(p: BProc; n: PNode; d: var TLoc) =
+  var a: TLoc = initLocExpr(p, n[1].skipAddr)
+  if n.len == 4:
+    # generated by liftdestructors:
+    var src: TLoc = initLocExpr(p, n[2])
+    linefmt(p, cpsStmts, "if ($1.p != $2.p) {", [rdLoc(a), rdLoc(src)])
+    genStmts(p, n[3])
+    linefmt(p, cpsStmts, "}$n$1.len = $2.len; $1.p = $2.p;$n", [rdLoc(a), rdLoc(src)])
+  else:
+    if d.k == locNone: d = getTemp(p, n.typ)
+    if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
+      genAssignment(p, d, a, {})
+      var op = getAttachedOp(p.module.g.graph, n.typ, attachedWasMoved)
+      if op == nil:
+        resetLoc(p, a)
+      else:
+        var b = initLocExpr(p, newSymNode(op))
+        case skipTypes(a.t, abstractVar+{tyStatic}).kind
+        of tyOpenArray, tyVarargs: # todo fixme generated `wasMoved` hooks for
+                                   # openarrays, but it probably shouldn't?
+          var s: string
+          if reifiedOpenArray(a.lode):
+            if a.t.kind in {tyVar, tyLent}:
+              s = "$1->Field0, $1->Field1" % [rdLoc(a)]
+            else:
+              s = "$1.Field0, $1.Field1" % [rdLoc(a)]
+          else:
+            s = "$1, $1Len_0" % [rdLoc(a)]
+          linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), s])
+        else:
+          if p.module.compileToCpp:
+            linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), rdLoc(a)])
+          else:
+            linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), byRefLoc(p, a)])
+    else:
+      if n[1].kind == nkSym and isSinkParam(n[1].sym):
+        var tmp = getTemp(p, n[1].typ.skipTypes({tySink}))
+        genAssignment(p, tmp, a, {needToCopySinkParam})
+        genAssignment(p, d, tmp, {})
+        resetLoc(p, tmp)
+      else:
+        genAssignment(p, d, a, {})
+      resetLoc(p, a)
+
+proc genDestroy(p: BProc; n: PNode) =
+  if optSeqDestructors in p.config.globalOptions:
+    let arg = n[1].skipAddr
+    let t = arg.typ.skipTypes(abstractInst)
+    case t.kind
+    of tyString:
+      var a: TLoc = initLocExpr(p, arg)
+      if optThreads in p.config.globalOptions:
+        linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" &
+          " #deallocShared($1.p);$n" &
+          "}$n", [rdLoc(a)])
+      else:
+        linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" &
+          " #dealloc($1.p);$n" &
+          "}$n", [rdLoc(a)])
+    of tySequence:
+      var a: TLoc = initLocExpr(p, arg)
+      linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" &
+        " #alignedDealloc($1.p, NIM_ALIGNOF($2));$n" &
+        "}$n",
+        [rdLoc(a), getTypeDesc(p.module, t.elementType)])
+    else: discard "nothing to do"
+  else:
+    let t = n[1].typ.skipTypes(abstractVar)
+    let op = getAttachedOp(p.module.g.graph, t, attachedDestructor)
+    if op != nil and getBody(p.module.g.graph, op).len != 0:
+      internalError(p.config, n.info, "destructor turned out to be not trivial")
+    discard "ignore calls to the default destructor"
+
+proc genDispose(p: BProc; n: PNode) =
+  when false:
+    let elemType = n[1].typ.skipTypes(abstractVar).elementType
+
+    var a: TLoc = initLocExpr(p, n[1].skipAddr)
+
+    if isFinal(elemType):
+      if elemType.destructor != nil:
+        var destroyCall = newNodeI(nkCall, n.info)
+        genStmts(p, destroyCall)
+      lineFmt(p, cpsStmts, "#nimRawDispose($1, NIM_ALIGNOF($2))", [rdLoc(a), getTypeDesc(p.module, elemType)])
+    else:
+      # ``nimRawDisposeVirtual`` calls the ``finalizer`` which is the same as the
+      # destructor, but it uses the runtime type. Afterwards the memory is freed:
+      lineCg(p, cpsStmts, ["#nimDestroyAndDispose($#)", rdLoc(a)])
+
+proc genSlice(p: BProc; e: PNode; d: var TLoc) =
+  let (x, y) = genOpenArraySlice(p, e, e.typ, e.typ.elementType,
+    prepareForMutation = e[1].kind == nkHiddenDeref and
+                         e[1].typ.skipTypes(abstractInst).kind == tyString and
+                         p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc})
+  if d.k == locNone: d = getTemp(p, e.typ)
+  linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $3;$n", [rdLoc(d), x, y])
+  when false:
+    localError(p.config, e.info, "invalid context for 'toOpenArray'; " &
+      "'toOpenArray' is only valid within a call expression")
+
+proc genEnumToStr(p: BProc, e: PNode, d: var TLoc) =
+  let t = e[1].typ.skipTypes(abstractInst+{tyRange})
+  let toStrProc = getToStringProc(p.module.g.graph, t)
+  # XXX need to modify this logic for IC.
+  var n = copyTree(e)
+  n[0] = newSymNode(toStrProc)
+  expr(p, n, d)
+
 proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   case op
   of mOr, mAnd: genAndOr(p, e, d, op)
-  of mNot..mToBiggestInt: unaryArith(p, e, d, op)
+  of mNot..mUnaryMinusF64: unaryArith(p, e, d, op)
   of mUnaryMinusI..mAbsI: unaryArithOverflow(p, e, d, op)
   of mAddF64..mDivF64: binaryFloatArith(p, e, d, op)
   of mShrI..mXor: binaryArith(p, e, d, op)
@@ -1806,108 +2436,181 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mAddI..mPred: binaryArithOverflow(p, e, d, op)
   of mRepr: genRepr(p, e, d)
   of mGetTypeInfo: genGetTypeInfo(p, e, d)
+  of mGetTypeInfoV2: genGetTypeInfoV2(p, e, d)
   of mSwap: genSwap(p, e, d)
-  of mUnaryLt:
-    if optOverflowCheck notin p.options: unaryExpr(p, e, d, "($1 - 1)")
-    else: unaryExpr(p, e, d, "#subInt($1, 1)")
   of mInc, mDec:
-    const opr: array[mInc..mDec, string] = ["$1 += $2;$n", "$1 -= $2;$n"]
-    const fun64: array[mInc..mDec, string] = ["$# = #addInt64($#, $#);$n",
-                                               "$# = #subInt64($#, $#);$n"]
-    const fun: array[mInc..mDec, string] = ["$# = #addInt($#, $#);$n",
-                                             "$# = #subInt($#, $#);$n"]
-    let underlying = skipTypes(e.sons[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent, tyRange})
+    const opr: array[mInc..mDec, string] = ["+=", "-="]
+    const fun64: array[mInc..mDec, string] = ["nimAddInt64", "nimSubInt64"]
+    const fun: array[mInc..mDec, string] = ["nimAddInt","nimSubInt"]
+    let underlying = skipTypes(e[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent, tyRange, tyDistinct})
     if optOverflowCheck notin p.options or underlying.kind in {tyUInt..tyUInt64}:
       binaryStmt(p, e, d, opr[op])
     else:
-      var a, b: TLoc
-      assert(e.sons[1].typ != nil)
-      assert(e.sons[2].typ != nil)
-      initLocExpr(p, e.sons[1], a)
-      initLocExpr(p, e.sons[2], b)
+      assert(e[1].typ != nil)
+      assert(e[2].typ != nil)
+      var a = initLocExpr(p, e[1])
+      var b = initLocExpr(p, e[2])
 
-      let ranged = skipTypes(e.sons[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent})
+      let ranged = skipTypes(e[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent, tyDistinct})
       let res = binaryArithOverflowRaw(p, ranged, a, b,
         if underlying.kind == tyInt64: fun64[op] else: fun[op])
-      putIntoDest(p, a, e.sons[1], "($#)($#)" % [
+
+      putIntoDest(p, a, e[1], "($#)($#)" % [
         getTypeDesc(p.module, ranged), res])
 
   of mConStrStr: genStrConcat(p, e, d)
-  of mAppendStrCh: binaryStmt(p, e, d, "$1 = #addChar($1, $2);$n")
+  of mAppendStrCh:
+    if optSeqDestructors in p.config.globalOptions:
+      binaryStmtAddr(p, e, d, "nimAddCharV1")
+    else:
+      var call = initLoc(locCall, e, OnHeap)
+      var dest = initLocExpr(p, e[1])
+      var b = initLocExpr(p, e[2])
+      call.snippet = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)])
+      genAssignment(p, dest, call, {})
   of mAppendStrStr: genStrAppend(p, e, d)
-  of mAppendSeqElem: genSeqElemAppend(p, e, d)
+  of mAppendSeqElem:
+    if optSeqDestructors in p.config.globalOptions:
+      e[1] = makeAddr(e[1], p.module.idgen)
+      genCall(p, e, d)
+    else:
+      genSeqElemAppend(p, e, d)
   of mEqStr: genStrEquals(p, e, d)
   of mLeStr: binaryExpr(p, e, d, "(#cmpStrings($1, $2) <= 0)")
   of mLtStr: binaryExpr(p, e, d, "(#cmpStrings($1, $2) < 0)")
   of mIsNil: genIsNil(p, e, d)
-  of mIntToStr: genDollar(p, e, d, "#nimIntToStr($1)")
-  of mInt64ToStr: genDollar(p, e, d, "#nimInt64ToStr($1)")
   of mBoolToStr: genDollar(p, e, d, "#nimBoolToStr($1)")
   of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)")
-  of mFloatToStr: genDollar(p, e, d, "#nimFloatToStr($1)")
-  of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)")
-  of mStrToStr: expr(p, e.sons[1], d)
-  of mEnumToStr: genRepr(p, e, d)
+  of mCStrToStr:
+    if p.module.compileToCpp:
+      # fixes for const qualifier; bug #12703; bug #19588
+      genDollar(p, e, d, "#cstrToNimstr((NCSTRING) $1)")
+    else:
+      genDollar(p, e, d, "#cstrToNimstr($1)")
+  of mStrToStr, mUnown: expr(p, e[1], d)
+  of generatedMagics: genCall(p, e, d)
+  of mEnumToStr:
+    if optTinyRtti in p.config.globalOptions:
+      genEnumToStr(p, e, d)
+    else:
+      genRepr(p, e, d)
   of mOf: genOf(p, e, d)
   of mNew: genNew(p, e)
-  of mNewFinalize: genNewFinalize(p, e)
-  of mNewSeq: genNewSeq(p, e)
+  of mNewFinalize:
+    if optTinyRtti in p.config.globalOptions:
+      var a: TLoc = initLocExpr(p, e[1])
+      rawGenNew(p, a, "", needsInit = true)
+      gcUsage(p.config, e)
+    else:
+      genNewFinalize(p, e)
+  of mNewSeq:
+    if optSeqDestructors in p.config.globalOptions:
+      e[1] = makeAddr(e[1], p.module.idgen)
+      genCall(p, e, d)
+    else:
+      genNewSeq(p, e)
   of mNewSeqOfCap: genNewSeqOfCap(p, e, d)
   of mSizeOf:
-    let t = e.sons[1].typ.skipTypes({tyTypeDesc})
-    putIntoDest(p, d, e, "((NI)sizeof($1))" % [getTypeDesc(p.module, t)])
+    let t = e[1].typ.skipTypes({tyTypeDesc})
+    putIntoDest(p, d, e, "((NI)sizeof($1))" % [getTypeDesc(p.module, t, dkVar)])
+  of mAlignOf:
+    let t = e[1].typ.skipTypes({tyTypeDesc})
+    putIntoDest(p, d, e, "((NI)NIM_ALIGNOF($1))" % [getTypeDesc(p.module, t, dkVar)])
+  of mOffsetOf:
+    var dotExpr: PNode
+    if e[1].kind == nkDotExpr:
+      dotExpr = e[1]
+    elif e[1].kind == nkCheckedFieldExpr:
+      dotExpr = e[1][0]
+    else:
+      dotExpr = nil
+      internalError(p.config, e.info, "unknown ast")
+    let t = dotExpr[0].typ.skipTypes({tyTypeDesc})
+    let tname = getTypeDesc(p.module, t, dkVar)
+    let member =
+      if t.kind == tyTuple:
+        "Field" & rope(dotExpr[1].sym.position)
+      else: dotExpr[1].sym.loc.snippet
+    putIntoDest(p,d,e, "((NI)offsetof($1, $2))" % [tname, member])
   of mChr: genSomeCast(p, e, d)
   of mOrd: genOrd(p, e, d)
   of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray:
     genArrayLen(p, e, d, op)
-  of mXLenStr:
-    if not p.module.compileToCpp:
-      unaryExpr(p, e, d, "($1->Sup.len)")
-    else:
-      unaryExpr(p, e, d, "$1->len")
-  of mXLenSeq:
-    # see 'taddhigh.nim' for why we need to use a temporary here:
-    var a, tmp: TLoc
-    initLocExpr(p, e[1], a)
-    getIntTemp(p, tmp)
-    var frmt: FormatStr
-    if not p.module.compileToCpp:
-      frmt = "$1 = $2->Sup.len;$n"
-    else:
-      frmt = "$1 = $2->len;$n"
-    lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a))
-    putIntoDest(p, d, e, tmp.r)
-  of mGCref: unaryStmt(p, e, d, "#nimGCref($1);$n")
-  of mGCunref: unaryStmt(p, e, d, "#nimGCunref($1);$n")
+  of mGCref:
+    # only a magic for the old GCs
+    unaryStmt(p, e, d, "if ($1) { #nimGCref($1); }$n")
+  of mGCunref:
+    # only a magic for the old GCs
+    unaryStmt(p, e, d, "if ($1) { #nimGCunref($1); }$n")
   of mSetLengthStr: genSetLengthStr(p, e, d)
   of mSetLengthSeq: genSetLengthSeq(p, e, d)
   of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet,
      mInSet:
     genSetOp(p, e, d, op)
-  of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit,
-      mParseBiggestFloat:
-    var opr = e.sons[0].sym
+  of mNewString, mNewStringOfCap, mExit, mParseBiggestFloat:
+    var opr = e[0].sym
+    # Why would anyone want to set nodecl to one of these hardcoded magics?
+    # - not sure, and it wouldn't work if the symbol behind the magic isn't
+    #   somehow forward-declared from some other usage, but it is *possible*
     if lfNoDecl notin opr.loc.flags:
-      discard cgsym(p.module, $opr.loc.r)
+      let prc = magicsys.getCompilerProc(p.module.g.graph, $opr.loc.snippet)
+      assert prc != nil, $opr.loc.snippet
+      # HACK:
+      # Explicitly add this proc as declared here so the cgsym call doesn't
+      # add a forward declaration - without this we could end up with the same
+      # 2 forward declarations. That happens because the magic symbol and the original
+      # one that shall be used have different ids (even though a call to one is
+      # actually a call to the other) so checking into m.declaredProtos with the 2 different ids doesn't work.
+      # Why would 2 identical forward declarations be a problem?
+      # - in the case of hot code-reloading we generate function pointers instead
+      #   of forward declarations and in C++ it is an error to redefine a global
+      let wasDeclared = containsOrIncl(p.module.declaredProtos, prc.id)
+      # Make the function behind the magic get actually generated - this will
+      # not lead to a forward declaration! The genCall will lead to one.
+      cgsym(p.module, $opr.loc.snippet)
+      # make sure we have pointer-initialising code for hot code reloading
+      if not wasDeclared and p.hcrOn:
+        p.module.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n",
+             [mangleDynLibProc(prc), getTypeDesc(p.module, prc.loc.t), getModuleDllPath(p.module, prc)])
     genCall(p, e, d)
-  of mReset: genReset(p, e)
+  of mDefault, mZeroDefault: genDefault(p, e, d)
   of mEcho: genEcho(p, e[1].skipConv)
   of mArrToSeq: genArrToSeq(p, e, d)
   of mNLen..mNError, mSlurp..mQuoteAst:
-    localError(p.config, e.info, strutils.`%`(errXMustBeCompileTime, e.sons[0].sym.name.s))
+    localError(p.config, e.info, strutils.`%`(errXMustBeCompileTime, e[0].sym.name.s))
   of mSpawn:
-    let n = lowerings.wrapProcForSpawn(p.module.g.graph, p.module.module, e, e.typ, nil, nil)
-    expr(p, n, d)
+    when defined(leanCompiler):
+      p.config.quitOrRaise "compiler built without support for the 'spawn' statement"
+    else:
+      let n = spawn.wrapProcForSpawn(p.module.g.graph, p.module.idgen, p.module.module, e, e.typ, nil, nil)
+      expr(p, n, d)
   of mParallel:
-    let n = semparallel.liftParallel(p.module.g.graph, p.module.module, e)
-    expr(p, n, d)
+    when defined(leanCompiler):
+      p.config.quitOrRaise "compiler built without support for the 'parallel' statement"
+    else:
+      let n = semparallel.liftParallel(p.module.g.graph, p.module.idgen, p.module.module, e)
+      expr(p, n, d)
   of mDeepCopy:
-    var a, b: TLoc
+    if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and optEnableDeepCopy notin p.config.globalOptions:
+      localError(p.config, e.info,
+        "for --mm:arc|atomicArc|orc 'deepcopy' support has to be enabled with --deepcopy:on")
+
     let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1]
-    initLocExpr(p, x, a)
-    initLocExpr(p, e.sons[2], b)
+    var a = initLocExpr(p, x)
+    var b = initLocExpr(p, e[2])
     genDeepCopy(p, a, b)
   of mDotDot, mEqCString: genCall(p, e, d)
+  of mWasMoved: genWasMoved(p, e)
+  of mMove: genMove(p, e, d)
+  of mDestroy: genDestroy(p, e)
+  of mAccessEnv: unaryExpr(p, e, d, "$1.ClE_0")
+  of mAccessTypeField: genAccessTypeField(p, e, d)
+  of mSlice: genSlice(p, e, d)
+  of mTrace: discard "no code to generate"
+  of mEnsureMove:
+    expr(p, e[1], d)
+  of mDup:
+    expr(p, e[1], d)
   else:
     when defined(debugMagics):
       echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind
@@ -1916,105 +2619,139 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
 proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
   # example: { a..b, c, d, e, f..g }
   # we have to emit an expression of the form:
-  # memset(tmp, 0, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c);
+  # nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c);
   # incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g);
   var
-    a, b, idx: TLoc
+    a, b: TLoc
+  var idx: TLoc
   if nfAllConst in e.flags:
-    putIntoDest(p, d, e, genSetNode(p, e))
+    var elem = newRopeAppender()
+    genSetNode(p, e, elem)
+    putIntoDest(p, d, e, elem)
   else:
-    if d.k == locNone: getTemp(p, e.typ, d)
+    if d.k == locNone: d = getTemp(p, e.typ)
     if getSize(p.config, e.typ) > 8:
       # big set:
-      useStringh(p.module)
-      lineF(p, cpsStmts, "memset($1, 0, sizeof($2));$n",
+      linefmt(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n",
           [rdLoc(d), getTypeDesc(p.module, e.typ)])
       for it in e.sons:
         if it.kind == nkRange:
-          getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), idx) # our counter
-          initLocExpr(p, it.sons[0], a)
-          initLocExpr(p, it.sons[1], b)
+          idx = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter
+          a = initLocExpr(p, it[0])
+          b = initLocExpr(p, it[1])
+          var aa = newRopeAppender()
+          rdSetElemLoc(p.config, a, e.typ, aa)
+          var bb = newRopeAppender()
+          rdSetElemLoc(p.config, b, e.typ, bb)
           lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" &
               "$2[(NU)($1)>>3] |=(1U<<((NU)($1)&7U));$n", [rdLoc(idx), rdLoc(d),
-              rdSetElemLoc(p.config, a, e.typ), rdSetElemLoc(p.config, b, e.typ)])
+              aa, bb])
         else:
-          initLocExpr(p, it, a)
+          a = initLocExpr(p, it)
+          var aa = newRopeAppender()
+          rdSetElemLoc(p.config, a, e.typ, aa)
           lineF(p, cpsStmts, "$1[(NU)($2)>>3] |=(1U<<((NU)($2)&7U));$n",
-               [rdLoc(d), rdSetElemLoc(p.config, a, e.typ)])
+               [rdLoc(d), aa])
     else:
       # small set
       var ts = "NU" & $(getSize(p.config, e.typ) * 8)
       lineF(p, cpsStmts, "$1 = 0;$n", [rdLoc(d)])
       for it in e.sons:
         if it.kind == nkRange:
-          getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), idx) # our counter
-          initLocExpr(p, it.sons[0], a)
-          initLocExpr(p, it.sons[1], b)
+          idx = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter
+          a = initLocExpr(p, it[0])
+          b = initLocExpr(p, it[1])
+          var aa = newRopeAppender()
+          rdSetElemLoc(p.config, a, e.typ, aa)
+          var bb = newRopeAppender()
+          rdSetElemLoc(p.config, b, e.typ, bb)
+
           lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" &
-              "$2 |=((" & ts & ")(1)<<(($1)%(sizeof(" & ts & ")*8)));$n", [
-              rdLoc(idx), rdLoc(d), rdSetElemLoc(p.config, a, e.typ),
-              rdSetElemLoc(p.config, b, e.typ)])
+              "$2 |=(($5)(1)<<(($1)%(sizeof($5)*8)));$n", [
+              rdLoc(idx), rdLoc(d), aa, bb, rope(ts)])
         else:
-          initLocExpr(p, it, a)
+          a = initLocExpr(p, it)
+          var aa = newRopeAppender()
+          rdSetElemLoc(p.config, a, e.typ, aa)
           lineF(p, cpsStmts,
-               "$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n",
-               [rdLoc(d), rdSetElemLoc(p.config, a, e.typ)])
+               "$1 |=(($3)(1)<<(($2)%(sizeof($3)*8)));$n",
+               [rdLoc(d), aa, rope(ts)])
 
 proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
   var rec: TLoc
   if not handleConstExpr(p, n, d):
     let t = n.typ
     discard getTypeDesc(p.module, t) # so that any fields are initialized
-    if d.k == locNone: getTemp(p, t, d)
-    for i in countup(0, sonsLen(n) - 1):
-      var it = n.sons[i]
-      if it.kind == nkExprColonExpr: it = it.sons[1]
-      initLoc(rec, locExpr, it, d.storage)
-      rec.r = "$1.Field$2" % [rdLoc(d), rope(i)]
+
+    var tmp: TLoc = default(TLoc)
+    # bug #16331
+    let doesAlias = lhsDoesAlias(d.lode, n)
+    let dest = if doesAlias: addr(tmp) else: addr(d)
+    if doesAlias:
+      tmp = getTemp(p, n.typ)
+    elif d.k == locNone:
+      d = getTemp(p, n.typ)
+
+    for i in 0..<n.len:
+      var it = n[i]
+      if it.kind == nkExprColonExpr: it = it[1]
+      rec = initLoc(locExpr, it, dest[].storage)
+      rec.snippet = "$1.Field$2" % [rdLoc(dest[]), rope(i)]
+      rec.flags.incl(lfEnforceDeref)
       expr(p, it, rec)
 
+    if doesAlias:
+      if d.k == locNone:
+        d = tmp
+      else:
+        genAssignment(p, d, tmp, {})
+
 proc isConstClosure(n: PNode): bool {.inline.} =
-  result = n.sons[0].kind == nkSym and isRoutine(n.sons[0].sym) and
-      n.sons[1].kind == nkNilLit
+  result = n[0].kind == nkSym and isRoutine(n[0].sym) and
+      n[1].kind == nkNilLit
 
 proc genClosure(p: BProc, n: PNode, d: var TLoc) =
-  assert n.kind == nkClosure
+  assert n.kind in {nkPar, nkTupleConstr, nkClosure}
 
   if isConstClosure(n):
     inc(p.module.labels)
     var tmp = "CNSTCLOSURE" & rope(p.module.labels)
-    addf(p.module.s[cfsData], "static NIM_CONST $1 $2 = $3;$n",
-        [getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)])
+    var data = "static NIM_CONST $1 $2 = " % [getTypeDesc(p.module, n.typ), tmp]
+    genBracedInit(p, n, isConst = true, n.typ, data)
+    data.addf(";$n", [])
+    p.module.s[cfsData].add data
     putIntoDest(p, d, n, tmp, OnStatic)
   else:
-    var tmp, a, b: TLoc
-    initLocExpr(p, n.sons[0], a)
-    initLocExpr(p, n.sons[1], b)
-    if n.sons[0].skipConv.kind == nkClosure:
+    var tmp: TLoc
+    var a = initLocExpr(p, n[0])
+    var b = initLocExpr(p, n[1])
+    if n[0].skipConv.kind == nkClosure:
       internalError(p.config, n.info, "closure to closure created")
     # tasyncawait.nim breaks with this optimization:
     when false:
       if d.k != locNone:
         linefmt(p, cpsStmts, "$1.ClP_0 = $2; $1.ClE_0 = $3;$n",
-                d.rdLoc, a.rdLoc, b.rdLoc)
+                [d.rdLoc, a.rdLoc, b.rdLoc])
     else:
-      getTemp(p, n.typ, tmp)
+      tmp = getTemp(p, n.typ)
       linefmt(p, cpsStmts, "$1.ClP_0 = $2; $1.ClE_0 = $3;$n",
-              tmp.rdLoc, a.rdLoc, b.rdLoc)
+              [tmp.rdLoc, a.rdLoc, b.rdLoc])
       putLocIntoDest(p, d, tmp)
 
 proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) =
   var arr: TLoc
   if not handleConstExpr(p, n, d):
-    if d.k == locNone: getTemp(p, n.typ, d)
-    for i in countup(0, sonsLen(n) - 1):
-      initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), d.storage)
-      arr.r = "$1[$2]" % [rdLoc(d), intLiteral(i)]
-      expr(p, n.sons[i], arr)
+    if d.k == locNone: d = getTemp(p, n.typ)
+    for i in 0..<n.len:
+      arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), d.storage)
+      var lit = newRopeAppender()
+      intLiteral(i, lit)
+      arr.snippet = "$1[$2]" % [rdLoc(d), lit]
+      expr(p, n[i], arr)
 
 proc genComplexConst(p: BProc, sym: PSym, d: var TLoc) =
   requestConstImpl(p, sym)
-  assert((sym.loc.r != nil) and (sym.loc.t != nil))
+  assert((sym.loc.snippet != "") and (sym.loc.t != nil))
   putLocIntoDest(p, d, sym.loc)
 
 template genStmtListExprImpl(exprOrStmt) {.dirty.} =
@@ -2022,11 +2759,11 @@ template genStmtListExprImpl(exprOrStmt) {.dirty.} =
   let hasNimFrame = p.prc != nil and
       sfSystemModule notin p.module.module.flags and
       optStackTrace in p.prc.options
-  var frameName: Rope = nil
-  for i in 0 .. n.len - 2:
+  var frameName: Rope = ""
+  for i in 0..<n.len - 1:
     let it = n[i]
     if it.kind == nkComesFrom:
-      if hasNimFrame and frameName == nil:
+      if hasNimFrame and frameName == "":
         inc p.labels
         frameName = "FR" & rope(p.labels) & "_"
         let theMacro = it[0].sym
@@ -2036,83 +2773,82 @@ template genStmtListExprImpl(exprOrStmt) {.dirty.} =
     else:
       genStmts(p, it)
   if n.len > 0: exprOrStmt
-  if frameName != nil:
-    add p.s(cpsStmts), deinitFrameNoDebug(p, frameName)
+  if frameName != "":
+    p.s(cpsStmts).add deinitFrameNoDebug(p, frameName)
 
 proc genStmtListExpr(p: BProc, n: PNode, d: var TLoc) =
   genStmtListExprImpl:
-    expr(p, n[n.len - 1], d)
+    expr(p, n[^1], d)
 
 proc genStmtList(p: BProc, n: PNode) =
   genStmtListExprImpl:
-    genStmts(p, n[n.len - 1])
+    genStmts(p, n[^1])
+
+from parampatterns import isLValue
 
 proc upConv(p: BProc, n: PNode, d: var TLoc) =
-  var a: TLoc
-  initLocExpr(p, n.sons[0], a)
+  var a: TLoc = initLocExpr(p, n[0])
   let dest = skipTypes(n.typ, abstractPtrs)
   if optObjCheck in p.options and not isObjLackingTypeField(dest):
-    var r = rdLoc(a)
-    var nilCheck: Rope = nil
-    var t = skipTypes(a.t, abstractInst)
-    while t.kind in {tyVar, tyLent, tyPtr, tyRef}:
-      if t.kind notin {tyVar, tyLent}: nilCheck = r
-      if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp:
-        r = "(*$1)" % [r]
-      t = skipTypes(t.lastSon, abstractInst)
-    discard getTypeDesc(p.module, t)
-    if not p.module.compileToCpp:
-      while t.kind == tyObject and t.sons[0] != nil:
-        add(r, ".Sup")
-        t = skipTypes(t.sons[0], skipPtrs)
-    if nilCheck != nil:
-      linefmt(p, cpsStmts, "if ($1) #chckObj($2.m_type, $3);$n",
-              nilCheck, r, genTypeInfo(p.module, dest, n.info))
-    else:
-      linefmt(p, cpsStmts, "#chckObj($1.m_type, $2);$n",
-              r, genTypeInfo(p.module, dest, n.info))
-  if n.sons[0].typ.kind != tyObject:
-    putIntoDest(p, d, n,
+    var nilCheck = ""
+    var r = newRopeAppender()
+    rdMType(p, a, nilCheck, r)
+    if optTinyRtti in p.config.globalOptions:
+      let checkFor = $getObjDepth(dest)
+      let token = $genDisplayElem(MD5Digest(hashType(dest, p.config)))
+      if nilCheck != "":
+        linefmt(p, cpsStmts, "if ($1 && !#isObjDisplayCheck($2, $3, $4)){ #raiseObjectConversionError(); ",
+                [nilCheck, r, checkFor, token])
+      else:
+        linefmt(p, cpsStmts, "if (!#isObjDisplayCheck($1, $2, $3)){ #raiseObjectConversionError(); ",
+                [r, checkFor, token])
+    else:
+      let checkFor = genTypeInfoV1(p.module, dest, n.info)
+      if nilCheck != "":
+        linefmt(p, cpsStmts, "if ($1 && !#isObj($2, $3)){ #raiseObjectConversionError(); ",
+                [nilCheck, r, checkFor])
+      else:
+        linefmt(p, cpsStmts, "if (!#isObj($1, $2)){ #raiseObjectConversionError(); ",
+                [r, checkFor])
+    raiseInstr(p, p.s(cpsStmts))
+    linefmt p, cpsStmts, "}$n", []
+
+  if n[0].typ.kind != tyObject:
+    if n.isLValue:
+      putIntoDest(p, d, n,
+                "(*(($1*) (&($2))))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
+    else:
+      putIntoDest(p, d, n,
                 "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
   else:
     putIntoDest(p, d, n, "(*($1*) ($2))" %
                         [getTypeDesc(p.module, dest), addrLoc(p.config, a)], a.storage)
 
 proc downConv(p: BProc, n: PNode, d: var TLoc) =
-  if p.module.compileToCpp:
-    discard getTypeDesc(p.module, skipTypes(n[0].typ, abstractPtrs))
-    expr(p, n.sons[0], d)     # downcast does C++ for us
-  else:
-    var dest = skipTypes(n.typ, abstractPtrs)
+  var arg = n[0]
+  while arg.kind == nkObjDownConv: arg = arg[0]
 
-    var arg = n.sons[0]
-    while arg.kind == nkObjDownConv: arg = arg.sons[0]
-
-    var src = skipTypes(arg.typ, abstractPtrs)
-    discard getTypeDesc(p.module, src)
-    var a: TLoc
-    initLocExpr(p, arg, a)
-    var r = rdLoc(a)
-    let isRef = skipTypes(arg.typ, abstractInst).kind in {tyRef, tyPtr, tyVar, tyLent}
-    if isRef:
-      add(r, "->Sup")
-    else:
-      add(r, ".Sup")
-    for i in countup(2, abs(inheritanceDiff(dest, src))): add(r, ".Sup")
-    if isRef:
-      # it can happen that we end up generating '&&x->Sup' here, so we pack
-      # the '&x->Sup' into a temporary and then those address is taken
-      # (see bug #837). However sometimes using a temporary is not correct:
-      # init(TFigure(my)) # where it is passed to a 'var TFigure'. We test
-      # this by ensuring the destination is also a pointer:
-      if d.k == locNone and skipTypes(n.typ, abstractInst).kind in {tyRef, tyPtr, tyVar, tyLent}:
-        getTemp(p, n.typ, d)
-        linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r)
-      else:
-        r = "&" & r
-        putIntoDest(p, d, n, r, a.storage)
-    else:
-      putIntoDest(p, d, n, r, a.storage)
+  let dest = skipTypes(n.typ, abstractPtrs)
+  let src = skipTypes(arg.typ, abstractPtrs)
+  discard getTypeDesc(p.module, src)
+  let isRef = skipTypes(arg.typ, abstractInstOwned).kind in {tyRef, tyPtr, tyVar, tyLent}
+  if isRef and d.k == locNone and n.typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr} and n.isLValue:
+    # it can happen that we end up generating '&&x->Sup' here, so we pack
+    # the '&x->Sup' into a temporary and then those address is taken
+    # (see bug #837). However sometimes using a temporary is not correct:
+    # init(TFigure(my)) # where it is passed to a 'var TFigure'. We test
+    # this by ensuring the destination is also a pointer:
+    var a: TLoc = initLocExpr(p, arg)
+    putIntoDest(p, d, n,
+              "(*(($1*) (&($2))))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
+  elif p.module.compileToCpp:
+    # C++ implicitly downcasts for us
+    expr(p, arg, d)
+  else:
+    var a: TLoc = initLocExpr(p, arg)
+    var r = rdLoc(a) & (if isRef: "->Sup" else: ".Sup")
+    for i in 2..abs(inheritanceDiff(dest, src)): r.add(".Sup")
+    putIntoDest(p, d, n, if isRef: "&" & r else: r, a.storage)
 
 proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
   let t = n.typ
@@ -2123,8 +2859,10 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
   if id == p.module.labels:
     # expression not found in the cache:
     inc(p.module.labels)
-    addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
-         [getTypeDesc(p.module, t), tmp, genConstExpr(p, n)])
+    p.module.s[cfsData].addf("static NIM_CONST $1 $2 = ",
+         [getTypeDesc(p.module, t, dkConst), tmp])
+    genBracedInit(p, n, isConst = true, t, p.module.s[cfsData])
+    p.module.s[cfsData].addf(";$n", [])
 
   if d.k == locNone:
     fillLoc(d, locData, n, tmp, OnStatic)
@@ -2135,14 +2873,74 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
     if t.kind notin {tySequence, tyString}:
       d.storage = OnStatic
 
+proc genConstSetup(p: BProc; sym: PSym): bool =
+  let m = p.module
+  useHeader(m, sym)
+  if sym.loc.k == locNone:
+    fillBackendName(p.module, sym)
+    fillLoc(sym.loc, locData, sym.astdef, OnStatic)
+  if m.hcrOn: incl(sym.loc.flags, lfIndirect)
+  result = lfNoDecl notin sym.loc.flags
+
+proc genConstHeader(m, q: BModule; p: BProc, sym: PSym) =
+  if sym.loc.snippet == "":
+    if not genConstSetup(p, sym): return
+  assert(sym.loc.snippet != "", $sym.name.s & $sym.itemId)
+  if m.hcrOn:
+    m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.snippet]);
+    m.initProc.procSec(cpsLocals).addf(
+      "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.snippet,
+      getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(q, sym)])
+  else:
+    let headerDecl = "extern NIM_CONST $1 $2;$n" %
+        [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.snippet]
+    m.s[cfsData].add(headerDecl)
+    if sfExportc in sym.flags and p.module.g.generatedHeader != nil:
+      p.module.g.generatedHeader.s[cfsData].add(headerDecl)
+
+proc genConstDefinition(q: BModule; p: BProc; sym: PSym) =
+  # add a suffix for hcr - will later init the global pointer with this data
+  let actualConstName = if q.hcrOn: sym.loc.snippet & "_const" else: sym.loc.snippet
+  var data = newRopeAppender()
+  data.addf("N_LIB_PRIVATE NIM_CONST $1 $2 = ",
+           [getTypeDesc(q, sym.typ), actualConstName])
+  genBracedInit(q.initProc, sym.astdef, isConst = true, sym.typ, data)
+  data.addf(";$n", [])
+  q.s[cfsData].add data
+  if q.hcrOn:
+    # generate the global pointer with the real name
+    q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, dkVar), sym.loc.snippet])
+    # register it (but ignore the boolean result of hcrRegisterGlobal)
+    q.initProc.procSec(cpsLocals).addf(
+      "\thcrRegisterGlobal($1, \"$2\", sizeof($3), NULL, (void**)&$2);$n",
+      [getModuleDllPath(q, sym), sym.loc.snippet, rdLoc(sym.loc)])
+    # always copy over the contents of the actual constant with the _const
+    # suffix ==> this means that the constant is reloadable & updatable!
+    q.initProc.procSec(cpsLocals).add(ropecg(q,
+      "\t#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n",
+      [sym.loc.snippet, actualConstName, rdLoc(sym.loc)]))
+
+proc genConstStmt(p: BProc, n: PNode) =
+  # This code is only used in the new DCE implementation.
+  assert useAliveDataFromDce in p.module.flags
+  let m = p.module
+  for it in n:
+    if it[0].kind == nkSym:
+      let sym = it[0].sym
+      if not isSimpleConst(sym.typ) and sym.itemId.item in m.alive and genConstSetup(p, sym):
+        genConstDefinition(m, p, sym)
+
 proc expr(p: BProc, n: PNode, d: var TLoc) =
+  when defined(nimCompilerStacktraceHints):
+    setFrameMsg p.config$n.info & " " & $n.kind
   p.currLineInfo = n.info
+
   case n.kind
   of nkSym:
     var sym = n.sym
     case sym.kind
     of skMethod:
-      if {sfDispatcher, sfForward} * sym.flags != {}:
+      if useAliveDataFromDce in p.module.flags or {sfDispatcher, sfForward} * sym.flags != {}:
         # we cannot produce code for the dispatcher yet:
         fillProcLoc(p.module, n)
         genProcPrototype(p.module, sym)
@@ -2155,13 +2953,23 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
       if sfCompileTime in sym.flags:
         localError(p.config, n.info, "request to generate code for .compileTime proc: " &
            sym.name.s)
-      genProc(p.module, sym)
-      if sym.loc.r == nil or sym.loc.lode == nil:
+      if useAliveDataFromDce in p.module.flags and sym.typ.callConv != ccInline:
+        fillProcLoc(p.module, n)
+        genProcPrototype(p.module, sym)
+      else:
+        genProc(p.module, sym)
+      if sym.loc.snippet == "" or sym.loc.lode == nil:
         internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
       putLocIntoDest(p, d, sym.loc)
     of skConst:
       if isSimpleConst(sym.typ):
-        putIntoDest(p, d, n, genLiteral(p, sym.ast, sym.typ), OnStatic)
+        var lit = newRopeAppender()
+        genLiteral(p, sym.astdef, sym.typ, lit)
+        putIntoDest(p, d, n, lit, OnStatic)
+      elif useAliveDataFromDce in p.module.flags:
+        genConstHeader(p.module, p.module, p, sym)
+        assert((sym.loc.snippet != "") and (sym.loc.t != nil))
+        putLocIntoDest(p, d, sym.loc)
       else:
         genComplexConst(p, sym, d)
     of skEnumField:
@@ -2172,26 +2980,35 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
     of skVar, skForVar, skResult, skLet:
       if {sfGlobal, sfThread} * sym.flags != {}:
         genVarPrototype(p.module, n)
-      if sym.loc.r == nil or sym.loc.t == nil:
+        if sfCompileTime in sym.flags:
+          genSingleVar(p, sym, n, astdef(sym))
+
+      if sym.loc.snippet == "" or sym.loc.t == nil:
         #echo "FAILED FOR PRCO ", p.prc.name.s
         #echo renderTree(p.prc.ast, {renderIds})
         internalError p.config, n.info, "expr: var not init " & sym.name.s & "_" & $sym.id
       if sfThread in sym.flags:
         accessThreadLocalVar(p, sym)
         if emulatedThreadVars(p.config):
-          putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.r)
+          putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.snippet)
         else:
           putLocIntoDest(p, d, sym.loc)
       else:
         putLocIntoDest(p, d, sym.loc)
     of skTemp:
-      if sym.loc.r == nil or sym.loc.t == nil:
+      when false:
+        # this is more harmful than helpful.
+        if sym.loc.snippet == "":
+          # we now support undeclared 'skTemp' variables for easier
+          # transformations in other parts of the compiler:
+          assignLocalVar(p, n)
+      if sym.loc.snippet == "" or sym.loc.t == nil:
         #echo "FAILED FOR PRCO ", p.prc.name.s
         #echo renderTree(p.prc.ast, {renderIds})
         internalError(p.config, n.info, "expr: temp not init " & sym.name.s & "_" & $sym.id)
       putLocIntoDest(p, d, sym.loc)
     of skParam:
-      if sym.loc.r == nil or sym.loc.t == nil:
+      if sym.loc.snippet == "" or sym.loc.t == nil:
         # echo "FAILED FOR PRCO ", p.prc.name.s
         # debug p.prc.typ.n
         # echo renderTree(p.prc.ast, {renderIds})
@@ -2200,19 +3017,24 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
     else: internalError(p.config, n.info, "expr(" & $sym.kind & "); unknown symbol")
   of nkNilLit:
     if not isEmptyType(n.typ):
-      putIntoDest(p, d, n, genLiteral(p, n))
+      var lit = newRopeAppender()
+      genLiteral(p, n, lit)
+      putIntoDest(p, d, n, lit)
   of nkStrLit..nkTripleStrLit:
-    putDataIntoDest(p, d, n, genLiteral(p, n))
-  of nkIntLit..nkUInt64Lit,
-     nkFloatLit..nkFloat128Lit, nkCharLit:
-    putIntoDest(p, d, n, genLiteral(p, n))
+    var lit = newRopeAppender()
+    genLiteral(p, n, lit)
+    putDataIntoDest(p, d, n, lit)
+  of nkIntLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkCharLit:
+    var lit = newRopeAppender()
+    genLiteral(p, n, lit)
+    putIntoDest(p, d, n, lit)
   of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand,
      nkCallStrLit:
-    genLineDir(p, n)
-    let op = n.sons[0]
+    genLineDir(p, n) # may be redundant, it is generated in fixupCall as well
+    let op = n[0]
     if n.typ.isNil:
       # discard the value:
-      var a: TLoc
+      var a: TLoc = default(TLoc)
       if op.kind == nkSym and op.sym.magic != mNone:
         genMagicExpr(p, n, a, op.sym.magic)
       else:
@@ -2225,7 +3047,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
         genCall(p, n, d)
   of nkCurly:
     if isDeepConstExpr(n) and n.len != 0:
-      putIntoDest(p, d, n, genSetNode(p, n))
+      var lit = newRopeAppender()
+      genSetNode(p, n, lit)
+      putIntoDest(p, d, n, lit)
     else:
       genSetConstr(p, n, d)
   of nkBracket:
@@ -2236,14 +3060,25 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
     else:
       genArrayConstr(p, n, d)
   of nkPar, nkTupleConstr:
-    if isDeepConstExpr(n) and n.len != 0:
+    if n.typ != nil and n.typ.kind == tyProc and n.len == 2:
+      genClosure(p, n, d)
+    elif isDeepConstExpr(n) and n.len != 0:
       exprComplexConst(p, n, d)
     else:
       genTupleConstr(p, n, d)
   of nkObjConstr: genObjConstr(p, n, d)
   of nkCast: genCast(p, n, d)
   of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, d)
-  of nkHiddenAddr, nkAddr: genAddr(p, n, d)
+  of nkHiddenAddr:
+    if n[0].kind == nkDerefExpr:
+      # addr ( deref ( x )) --> x
+      var x = n[0][0]
+      if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
+        x.typ = n.typ
+      expr(p, x, d)
+      return
+    genAddr(p, n, d)
+  of nkAddr: genAddr(p, n, d)
   of nkBracketExpr: genBracketExpr(p, n, d)
   of nkDerefExpr, nkHiddenDeref: genDeref(p, n, d)
   of nkDotExpr: genRecordField(p, n, d)
@@ -2254,18 +3089,16 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
   of nkIfExpr, nkIfStmt: genIf(p, n, d)
   of nkWhen:
     # This should be a "when nimvm" node.
-    expr(p, n.sons[1].sons[0], d)
+    expr(p, n[1][0], d)
   of nkObjDownConv: downConv(p, n, d)
   of nkObjUpConv: upConv(p, n, d)
-  of nkChckRangeF: genRangeChck(p, n, d, "chckRangeF")
-  of nkChckRange64: genRangeChck(p, n, d, "chckRange64")
-  of nkChckRange: genRangeChck(p, n, d, "chckRange")
+  of nkChckRangeF, nkChckRange64, nkChckRange: genRangeChck(p, n, d)
   of nkStringToCString: convStrToCStr(p, n, d)
   of nkCStringToString: convCStrToStr(p, n, d)
   of nkLambdaKinds:
-    var sym = n.sons[namePos].sym
+    var sym = n[namePos].sym
     genProc(p.module, sym)
-    if sym.loc.r == nil or sym.loc.lode == nil:
+    if sym.loc.snippet == "" or sym.loc.lode == nil:
       internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
     putLocIntoDest(p, d, sym.loc)
   of nkClosure: genClosure(p, n, d)
@@ -2273,15 +3106,20 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
   of nkEmpty: discard
   of nkWhileStmt: genWhileStmt(p, n)
   of nkVarSection, nkLetSection: genVarStmt(p, n)
-  of nkConstSection: discard  # consts generated lazily on use
+  of nkConstSection:
+    if useAliveDataFromDce in p.module.flags:
+      genConstStmt(p, n)
+    # else: consts generated lazily on use
   of nkForStmt: internalError(p.config, n.info, "for statement not eliminated")
   of nkCaseStmt: genCase(p, n, d)
   of nkReturnStmt: genReturnStmt(p, n)
   of nkBreakStmt: genBreakStmt(p, n)
   of nkAsgn:
+    cow(p, n[1])
     if nfPreventCg notin n.flags:
       genAsgn(p, n, fastAsgn=false)
-  of nkFastAsgn:
+  of nkFastAsgn, nkSinkAsgn:
+    cow(p, n[1])
     if nfPreventCg notin n.flags:
       # transf is overly aggressive with 'nkFastAsgn', so we work around here.
       # See tests/run/tcnstseq3 for an example that would fail otherwise.
@@ -2290,22 +3128,17 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
     let ex = n[0]
     if ex.kind != nkEmpty:
       genLineDir(p, n)
-      var a: TLoc
-      if ex.kind in nkCallKinds and (ex[0].kind != nkSym or
-                                     ex[0].sym.magic == mNone):
-        # bug #6037: do not assign to a temp in C++ mode:
-        incl a.flags, lfSingleUse
-        genCall(p, ex, a)
-        if lfSingleUse notin a.flags:
-          line(p, cpsStmts, a.r & ";\L")
-      else:
-        initLocExpr(p, ex, a)
+      var a: TLoc = initLocExprSingleUse(p, ex)
+      line(p, cpsStmts, "(void)(" & a.snippet & ");\L")
   of nkAsmStmt: genAsmStmt(p, n)
-  of nkTryStmt:
-    if p.module.compileToCpp and optNoCppExceptions notin p.config.globalOptions:
+  of nkTryStmt, nkHiddenTryStmt:
+    case p.config.exc
+    of excGoto:
+      genTryGoto(p, n, d)
+    of excCpp:
       genTryCpp(p, n, d)
     else:
-      genTry(p, n, d)
+      genTrySetjmp(p, n, d)
   of nkRaiseStmt: genRaiseStmt(p, n)
   of nkTypeSection:
     # we have to emit the type information for object types here to support
@@ -2316,174 +3149,327 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
      nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt:
     discard
   of nkPragma: genPragma(p, n)
-  of nkPragmaBlock: expr(p, n.lastSon, d)
+  of nkPragmaBlock:
+    var inUncheckedAssignSection = 0
+    let pragmaList = n[0]
+    for pi in pragmaList:
+      if whichPragma(pi) == wCast:
+        case whichPragma(pi[1])
+        of wUncheckedAssign:
+          inUncheckedAssignSection = 1
+        else:
+          discard
+
+    inc p.inUncheckedAssignSection, inUncheckedAssignSection
+    expr(p, n.lastSon, d)
+    dec p.inUncheckedAssignSection, inUncheckedAssignSection
+
   of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef:
-    if n.sons[genericParamsPos].kind == nkEmpty:
-      var prc = n.sons[namePos].sym
-      # due to a bug/limitation in the lambda lifting, unused inner procs
-      # are not transformed correctly. We work around this issue (#411) here
-      # by ensuring it's no inner proc (owner is a module):
-      if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags:
+    if n[genericParamsPos].kind == nkEmpty:
+      var prc = n[namePos].sym
+      if useAliveDataFromDce in p.module.flags:
+        if p.module.alive.contains(prc.itemId.item) and
+            prc.magic in generatedMagics:
+          genProc(p.module, prc)
+      elif prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags:
         if ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or
             (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
             (prc.kind == skMethod):
-          # we have not only the header:
-          if prc.getBody.kind != nkEmpty or lfDynamicLib in prc.loc.flags:
-            genProc(p.module, prc)
+          # due to a bug/limitation in the lambda lifting, unused inner procs
+          # are not transformed correctly. We work around this issue (#411) here
+          # by ensuring it's no inner proc (owner is a module).
+          # Generate proc even if empty body, bugfix #11651.
+          genProc(p.module, prc)
   of nkParForStmt: genParForStmt(p, n)
   of nkState: genState(p, n)
-  of nkGotoState: genGotoState(p, n)
+  of nkGotoState:
+    # simply never set it back to 0 here from here on...
+    inc p.splitDecls
+    genGotoState(p, n)
   of nkBreakState: genBreakState(p, n, d)
+  of nkMixinStmt, nkBindStmt: discard
   else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind")
 
-proc genNamedConstExpr(p: BProc, n: PNode): Rope =
-  if n.kind == nkExprColonExpr: result = genConstExpr(p, n.sons[1])
-  else: result = genConstExpr(p, n)
-
-proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope =
-  var t = skipTypes(typ, abstractRange-{tyTypeDesc})
+proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) =
+  var t = skipTypes(typ, abstractRange+{tyOwned}-{tyTypeDesc})
   case t.kind
-  of tyBool: result = rope"NIM_FALSE"
-  of tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64: result = rope"0"
-  of tyFloat..tyFloat128: result = rope"0.0"
-  of tyCString, tyString, tyVar, tyLent, tyPointer, tyPtr, tySequence, tyExpr,
-     tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil:
-    result = rope"NIM_NIL"
+  of tyBool: result.add rope"NIM_FALSE"
+  of tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64: result.add rope"0"
+  of tyFloat..tyFloat128: result.add rope"0.0"
+  of tyCstring, tyVar, tyLent, tyPointer, tyPtr, tyUntyped,
+     tyTyped, tyTypeDesc, tyStatic, tyRef, tyNil:
+    result.add rope"NIM_NIL"
+  of tyString, tySequence:
+    if optSeqDestructors in p.config.globalOptions:
+      result.add "{0, NIM_NIL}"
+    else:
+      result.add "NIM_NIL"
   of tyProc:
     if t.callConv != ccClosure:
-      result = rope"NIM_NIL"
+      result.add "NIM_NIL"
     else:
-      result = rope"{NIM_NIL, NIM_NIL}"
+      result.add "{NIM_NIL, NIM_NIL}"
   of tyObject:
-    if not isObjLackingTypeField(t) and not p.module.compileToCpp:
-      result = "{{$1}}" % [genTypeInfo(p.module, t, info)]
-    else:
-      result = rope"{}"
+    var count = 0
+    result.add "{"
+    getNullValueAuxT(p, t, t, t.n, nil, result, count, true, info)
+    result.add "}"
   of tyTuple:
-    result = rope"{"
-    for i in 0 ..< typ.len:
+    result.add "{"
+    if p.vccAndC and t.isEmptyTupleType:
+      result.add "0"
+    for i, a in t.ikids:
       if i > 0: result.add ", "
-      result.add getDefaultValue(p, typ.sons[i], info)
+      getDefaultValue(p, a, info, result)
     result.add "}"
-  of tyArray: result = rope"{}"
+  of tyArray:
+    result.add "{"
+    for i in 0..<toInt(lengthOrd(p.config, t.indexType)):
+      if i > 0: result.add ", "
+      getDefaultValue(p, t.elementType, info, result)
+    result.add "}"
+    #result = rope"{}"
+  of tyOpenArray, tyVarargs:
+    result.add "{NIM_NIL, 0}"
   of tySet:
-    if mapType(p.config, t) == ctArray: result = rope"{}"
-    else: result = rope"0"
+    if mapSetType(p.config, t) == ctArray: result.add "{}"
+    else: result.add "0"
   else:
     globalError(p.config, info, "cannot create null element for: " & $t.kind)
 
-proc getNullValueAux(p: BProc; t: PType; obj, cons: PNode, result: var Rope; count: var int) =
+proc isEmptyCaseObjectBranch(n: PNode): bool =
+  for it in n:
+    if it.kind == nkSym and not isEmptyType(it.sym.typ): return false
+  return true
+
+proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
+                     result: var Rope; count: var int;
+                     isConst: bool, info: TLineInfo) =
   case obj.kind
   of nkRecList:
     for it in obj.sons:
-      getNullValueAux(p, t, it, cons, result, count)
+      getNullValueAux(p, t, it, constOrNil, result, count, isConst, info)
   of nkRecCase:
-    getNullValueAux(p, t, obj.sons[0], cons, result, count)
-    for i in countup(1, sonsLen(obj) - 1):
-      getNullValueAux(p, t, lastSon(obj.sons[i]), cons, result, count)
+    getNullValueAux(p, t, obj[0], constOrNil, result, count, isConst, info)
+    var res = ""
+    if count > 0: res.add ", "
+    var branch = Zero
+    if constOrNil != nil:
+      ## find kind value, default is zero if not specified
+      for i in 1..<constOrNil.len:
+        if constOrNil[i].kind == nkExprColonExpr:
+          if constOrNil[i][0].sym.name.id == obj[0].sym.name.id:
+            branch = getOrdValue(constOrNil[i][1])
+            break
+        elif i == obj[0].sym.position:
+          branch = getOrdValue(constOrNil[i])
+          break
+
+    let selectedBranch = caseObjDefaultBranch(obj, branch)
+    res.add "{"
+    var countB = 0
+    let b = lastSon(obj[selectedBranch])
+    # designated initilization is the only way to init non first element of unions
+    # branches are allowed to have no members (b.len == 0), in this case they don't need initializer
+    if b.kind == nkRecList and not isEmptyCaseObjectBranch(b):
+      res.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {"
+      getNullValueAux(p, t,  b, constOrNil, res, countB, isConst, info)
+      res.add "}"
+    elif b.kind == nkSym:
+      res.add "." & mangleRecFieldName(p.module, b.sym) & " = "
+      getNullValueAux(p, t,  b, constOrNil, res, countB, isConst, info)
+    else:
+      return
+    result.add res
+    result.add "}"
+
   of nkSym:
     if count > 0: result.add ", "
     inc count
     let field = obj.sym
-    for i in 1..<cons.len:
-      if cons[i].kind == nkExprColonExpr:
-        if cons[i][0].sym.name.id == field.name.id:
-          result.add genConstExpr(p, cons[i][1])
+    if constOrNil != nil:
+      for i in 1..<constOrNil.len:
+        if constOrNil[i].kind == nkExprColonExpr:
+          assert constOrNil[i][0].kind == nkSym, "illformed object constr; the field is not a sym"
+          if constOrNil[i][0].sym.name.id == field.name.id:
+            genBracedInit(p, constOrNil[i][1], isConst, field.typ, result)
+            return
+        elif i == field.position:
+          genBracedInit(p, constOrNil[i], isConst, field.typ, result)
           return
-      elif i == field.position:
-        result.add genConstExpr(p, cons[i])
-        return
     # not found, produce default value:
-    result.add getDefaultValue(p, field.typ, cons.info)
+    getDefaultValue(p, field.typ, info, result)
   else:
-    localError(p.config, cons.info, "cannot create null element for: " & $obj)
+    localError(p.config, info, "cannot create null element for: " & $obj)
 
-proc getNullValueAuxT(p: BProc; orig, t: PType; obj, cons: PNode, result: var Rope; count: var int) =
-  var base = t.sons[0]
+proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode,
+                      result: var Rope; count: var int;
+                      isConst: bool, info: TLineInfo) =
+  var base = t.baseClass
   let oldRes = result
-  if not p.module.compileToCpp: result.add "{"
   let oldcount = count
   if base != nil:
+    result.add "{"
     base = skipTypes(base, skipPtrs)
-    getNullValueAuxT(p, orig, base, base.n, cons, result, count)
-  elif not isObjLackingTypeField(t) and not p.module.compileToCpp:
-    addf(result, "$1", [genTypeInfo(p.module, orig, obj.info)])
+    getNullValueAuxT(p, orig, base, base.n, constOrNil, result, count, isConst, info)
+    result.add "}"
+  elif not isObjLackingTypeField(t):
+    if optTinyRtti in p.config.globalOptions:
+      result.add genTypeInfoV2(p.module, orig, obj.info)
+    else:
+      result.add genTypeInfoV1(p.module, orig, obj.info)
     inc count
-  getNullValueAux(p, t, obj, cons, result, count)
+  getNullValueAux(p, t, obj, constOrNil, result, count, isConst, info)
   # do not emit '{}' as that is not valid C:
-  if oldcount == count: result = oldres
-  elif not p.module.compileToCpp: result.add "}"
+  if oldcount == count: result = oldRes
 
-proc genConstObjConstr(p: BProc; n: PNode): Rope =
-  result = nil
-  let t = n.typ.skipTypes(abstractInst)
+proc genConstObjConstr(p: BProc; n: PNode; isConst: bool; result: var Rope) =
+  let t = n.typ.skipTypes(abstractInstOwned)
   var count = 0
   #if not isObjLackingTypeField(t) and not p.module.compileToCpp:
-  #  addf(result, "{$1}", [genTypeInfo(p.module, t)])
+  #  result.addf("{$1}", [genTypeInfo(p.module, t)])
   #  inc count
-  getNullValueAuxT(p, t, t, t.n, n, result, count)
-  if p.module.compileToCpp:
-    result = "{$1}$n" % [result]
-
-proc genConstSimpleList(p: BProc, n: PNode): Rope =
-  var length = sonsLen(n)
-  result = rope("{")
-  let t = n.typ.skipTypes(abstractInst)
-  for i in countup(0, length - 2):
-    addf(result, "$1,$n", [genNamedConstExpr(p, n.sons[i])])
-  if length > 0:
-    add(result, genNamedConstExpr(p, n.sons[length - 1]))
-  addf(result, "}$n", [])
-
-proc genConstSeq(p: BProc, n: PNode, t: PType): Rope =
+  result.add "{"
+  if t.kind == tyObject:
+    getNullValueAuxT(p, t, t, t.n, n, result, count, isConst, n.info)
+  result.add("}\n")
+
+proc genConstSimpleList(p: BProc, n: PNode; isConst: bool; result: var Rope) =
+  result.add "{"
+  if p.vccAndC and n.len == 0 and n.typ.kind == tyArray:
+    getDefaultValue(p, n.typ.elementType, n.info, result)
+  for i in 0..<n.len:
+    let it = n[i]
+    if i > 0: result.add ",\n"
+    if it.kind == nkExprColonExpr: genBracedInit(p, it[1], isConst, it[0].typ, result)
+    else: genBracedInit(p, it, isConst, it.typ, result)
+  result.add("}\n")
+
+proc genConstTuple(p: BProc, n: PNode; isConst: bool; tup: PType; result: var Rope) =
+  result.add "{"
+  if p.vccAndC and n.len == 0:
+    result.add "0"
+  for i in 0..<n.len:
+    let it = n[i]
+    if i > 0: result.add ",\n"
+    if it.kind == nkExprColonExpr: genBracedInit(p, it[1], isConst, tup[i], result)
+    else: genBracedInit(p, it, isConst, tup[i], result)
+  result.add("}\n")
+
+proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool; result: var Rope) =
   var data = "{{$1, $1 | NIM_STRLIT_FLAG}" % [n.len.rope]
+  let base = t.skipTypes(abstractInst)[0]
   if n.len > 0:
     # array part needs extra curlies:
     data.add(", {")
-    for i in countup(0, n.len - 1):
+    for i in 0..<n.len:
       if i > 0: data.addf(",$n", [])
-      data.add genConstExpr(p, n.sons[i])
+      genBracedInit(p, n[i], isConst, base, data)
     data.add("}")
   data.add("}")
 
-  result = getTempName(p.module)
-  let base = t.skipTypes(abstractInst).sons[0]
+  let tmpName = getTempName(p.module)
 
-  appcg(p.module, cfsData,
-        "NIM_CONST struct {$n" &
+  appcg(p.module, cfsStrData,
+        "static $5 struct {$n" &
         "  #TGenericSeq Sup;$n" &
         "  $1 data[$2];$n" &
         "} $3 = $4;$n", [
-        getTypeDesc(p.module, base), n.len.rope, result, data])
+        getTypeDesc(p.module, base), n.len, tmpName, data,
+        if isConst: "NIM_CONST" else: ""])
+
+  result.add "(($1)&$2)" % [getTypeDesc(p.module, t), tmpName]
+
+proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool; result: var Rope) =
+  let base = t.skipTypes(abstractInst)[0]
+  var data = rope""
+  if n.len > 0:
+    data.add(", {")
+    for i in 0..<n.len:
+      if i > 0: data.addf(",$n", [])
+      genBracedInit(p, n[i], isConst, base, data)
+    data.add("}")
 
-  result = "(($1)&$2)" % [getTypeDesc(p.module, t), result]
+  let payload = getTempName(p.module)
+  appcg(p.module, cfsStrData,
+    "static $5 struct {$n" &
+    "  NI cap; $1 data[$2];$n" &
+    "} $3 = {$2 | NIM_STRLIT_FLAG$4};$n", [
+    getTypeDesc(p.module, base), n.len, payload, data,
+    if isConst: "const" else: ""])
+  result.add "{$1, ($2*)&$3}" % [rope(n.len), getSeqPayloadType(p.module, t), payload]
 
-proc genConstExpr(p: BProc, n: PNode): Rope =
+proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Rope) =
   case n.kind
   of nkHiddenStdConv, nkHiddenSubConv:
-    result = genConstExpr(p, n.sons[1])
-  of nkCurly:
-    var cs: TBitSet
-    toBitSet(p.config, n, cs)
-    result = genRawSetData(cs, int(getSize(p.config, n.typ)))
-  of nkBracket, nkPar, nkTupleConstr, nkClosure:
-    var t = skipTypes(n.typ, abstractInst)
-    if t.kind == tySequence:
-      result = genConstSeq(p, n, n.typ)
-    elif t.kind == tyProc and t.callConv == ccClosure and not n.sons.isNil and
-         n.sons[0].kind == nkNilLit and n.sons[1].kind == nkNilLit:
-      # this hack fixes issue that nkNilLit is expanded to {NIM_NIL,NIM_NIL}
-      # this behaviour is needed since closure_var = nil must be
-      # expanded to {NIM_NIL,NIM_NIL}
-      # in VM closures are initialized with nkPar(nkNilLit, nkNilLit)
-      # leading to duplicate code like this:
-      # "{NIM_NIL,NIM_NIL}, {NIM_NIL,NIM_NIL}"
-      result = ~"{NIM_NIL,NIM_NIL}"
-    else:
-      result = genConstSimpleList(p, n)
-  of nkObjConstr:
-    result = genConstObjConstr(p, n)
-  else:
-    var d: TLoc
-    initLocExpr(p, n, d)
-    result = rdLoc(d)
+    genBracedInit(p, n[1], isConst, n.typ, result)
+  else:
+    var ty = tyNone
+    var typ: PType = nil
+    if optionalType == nil:
+      if n.kind in nkStrKinds:
+        ty = tyString
+      else:
+        internalError(p.config, n.info, "node has no type")
+    else:
+      typ = skipTypes(optionalType, abstractInstOwned + {tyStatic})
+      ty = typ.kind
+    case ty
+    of tySet:
+      let cs = toBitSet(p.config, n)
+      genRawSetData(cs, int(getSize(p.config, n.typ)), result)
+    of tySequence:
+      if optSeqDestructors in p.config.globalOptions:
+        genConstSeqV2(p, n, typ, isConst, result)
+      else:
+        genConstSeq(p, n, typ, isConst, result)
+    of tyProc:
+      if typ.callConv == ccClosure and n.safeLen > 1 and n[1].kind == nkNilLit:
+        # n.kind could be: nkClosure, nkTupleConstr and maybe others; `n.safeLen`
+        # guards against the case of `nkSym`, refs bug #14340.
+        # Conversion: nimcall -> closure.
+        # this hack fixes issue that nkNilLit is expanded to {NIM_NIL,NIM_NIL}
+        # this behaviour is needed since closure_var = nil must be
+        # expanded to {NIM_NIL,NIM_NIL}
+        # in VM closures are initialized with nkPar(nkNilLit, nkNilLit)
+        # leading to duplicate code like this:
+        # "{NIM_NIL,NIM_NIL}, {NIM_NIL,NIM_NIL}"
+        if n[0].kind == nkNilLit:
+          result.add "{NIM_NIL,NIM_NIL}"
+        else:
+          var d: TLoc = initLocExpr(p, n[0])
+          result.add "{(($1) $2),NIM_NIL}" % [getClosureType(p.module, typ, clHalfWithEnv), rdLoc(d)]
+      else:
+        var d: TLoc = initLocExpr(p, n)
+        result.add rdLoc(d)
+    of tyArray, tyVarargs:
+      genConstSimpleList(p, n, isConst, result)
+    of tyTuple:
+      genConstTuple(p, n, isConst, typ, result)
+    of tyOpenArray:
+      if n.kind != nkBracket:
+        internalError(p.config, n.info, "const openArray expression is not an array construction")
+
+      var data = newRopeAppender()
+      genConstSimpleList(p, n, isConst, data)
+
+      let payload = getTempName(p.module)
+      let ctype = getTypeDesc(p.module, typ.elementType)
+      let arrLen = n.len
+      appcg(p.module, cfsStrData,
+        "static $5 $1 $3[$2] = $4;$n", [
+        ctype, arrLen, payload, data,
+        if isConst: "const" else: ""])
+      result.add "{($1*)&$2, $3}" % [ctype, payload, rope arrLen]
+
+    of tyObject:
+      genConstObjConstr(p, n, isConst, result)
+    of tyString, tyCstring:
+      if optSeqDestructors in p.config.globalOptions and n.kind != nkNilLit and ty == tyString:
+        genStringLiteralV2Const(p.module, n, isConst, result)
+      else:
+        var d: TLoc = initLocExpr(p, n)
+        result.add rdLoc(d)
+    else:
+      var d: TLoc = initLocExpr(p, n)
+      result.add rdLoc(d)
diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim
index cfe71375e..cbef6771f 100644
--- a/compiler/ccgliterals.nim
+++ b/compiler/ccgliterals.nim
@@ -7,6 +7,8 @@
 #    distribution, for details about the copyright.
 #
 
+# included from cgen.nim
+
 ## This include file contains the logic to produce constant string
 ## and seq literals. The code here is responsible that
 ## ``const x = ["a", "b"]`` works without hidden runtime creation code.
@@ -19,7 +21,7 @@ template detectVersion(field, corename) =
     if core == nil or core.kind != skConst:
       m.g.field = 1
     else:
-      m.g.field = int ast.getInt(core.ast)
+      m.g.field = toInt(ast.getInt(core.astdef))
   result = m.g.field
 
 proc detectStrVersion(m: BModule): int =
@@ -30,62 +32,87 @@ proc detectSeqVersion(m: BModule): int =
 
 # ----- Version 1: GC'ed strings and seqs --------------------------------
 
-proc genStringLiteralDataOnlyV1(m: BModule, s: string): Rope =
-  discard cgsym(m, "TGenericSeq")
-  result = getTempName(m)
-  addf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n",
-       [result, makeCString(s), rope(len(s))])
+proc genStringLiteralDataOnlyV1(m: BModule, s: string; result: var Rope) =
+  cgsym(m, "TGenericSeq")
+  let tmp = getTempName(m)
+  result.add tmp
+  m.s[cfsStrData].addf("STRING_LITERAL($1, $2, $3);$n",
+       [tmp, makeCString(s), rope(s.len)])
 
-proc genStringLiteralV1(m: BModule; n: PNode): Rope =
+proc genStringLiteralV1(m: BModule; n: PNode; result: var Rope) =
   if s.isNil:
-    result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", [])
+    appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
   else:
     let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
     if id == m.labels:
       # string literal not found in the cache:
-      result = ropecg(m, "((#NimStringDesc*) &$1)",
-                      [genStringLiteralDataOnlyV1(m, n.strVal)])
+      appcg(m, result, "((#NimStringDesc*) &", [])
+      genStringLiteralDataOnlyV1(m, n.strVal, result)
+      result.add ")"
     else:
-      result = ropecg(m, "((#NimStringDesc*) &$1$2)",
-                      [m.tmpBase, rope(id)])
+      appcg(m, result, "((#NimStringDesc*) &$1$2)",
+                      [m.tmpBase, id])
 
 # ------ Version 2: destructor based strings and seqs -----------------------
 
-proc genStringLiteralDataOnlyV2(m: BModule, s: string): Rope =
-  result = getTempName(m)
-  addf(m.s[cfsData], " static const NIM_CHAR $1[$2] = $3;$n",
-       [result, rope(len(s)+1), makeCString(s)])
+proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope; isConst: bool) =
+  m.s[cfsStrData].addf("static $4 struct {$n" &
+       "  NI cap; NIM_CHAR data[$2+1];$n" &
+       "} $1 = { $2 | NIM_STRLIT_FLAG, $3 };$n",
+       [result, rope(s.len), makeCString(s),
+       rope(if isConst: "const" else: "")])
 
-proc genStringLiteralV2(m: BModule; n: PNode): Rope =
+proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Rope) =
   let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
   if id == m.labels:
+    let pureLit = getTempName(m)
+    genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
+    let tmp = getTempName(m)
+    result.add tmp
+    cgsym(m, "NimStrPayload")
+    cgsym(m, "NimStringV2")
     # string literal not found in the cache:
-    let pureLit = genStringLiteralDataOnlyV2(m, n.strVal)
-    result = getTempName(m)
-    addf(m.s[cfsData], "static const #NimStringV2 $1 = {$2, $2, $3};$n",
-        [result, rope(len(n.strVal)+1), pureLit])
+    m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
+          [tmp, rope(n.strVal.len), pureLit, rope(if isConst: "const" else: "")])
   else:
-    result = m.tmpBase & rope(id)
+    let tmp = getTempName(m)
+    result.add tmp
+    m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
+          [tmp, rope(n.strVal.len), m.tmpBase & rope(id),
+          rope(if isConst: "const" else: "")])
+
+proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Rope) =
+  let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
+  var pureLit: Rope
+  if id == m.labels:
+    pureLit = getTempName(m)
+    cgsym(m, "NimStrPayload")
+    cgsym(m, "NimStringV2")
+    # string literal not found in the cache:
+    genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
+  else:
+    pureLit = m.tmpBase & rope(id)
+  result.addf "{$1, (NimStrPayload*)&$2}", [rope(n.strVal.len), pureLit]
 
 # ------ Version selector ---------------------------------------------------
 
-proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo): Rope =
+proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
+                              isConst: bool; result: var Rope) =
   case detectStrVersion(m)
-  of 0, 1: result = genStringLiteralDataOnlyV1(m, s)
-  of 2: result = genStringLiteralDataOnlyV2(m, s)
+  of 0, 1: genStringLiteralDataOnlyV1(m, s, result)
+  of 2:
+    let tmp = getTempName(m)
+    genStringLiteralDataOnlyV2(m, s, tmp, isConst)
+    result.add tmp
   else:
     localError(m.config, info, "cannot determine how to produce code for string literal")
 
-proc genStringLiteralFromData(m: BModule; data: Rope; info: TLineInfo): Rope =
-  result = ropecg(m, "((#NimStringDesc*) &$1)",
-                [data])
-
-proc genNilStringLiteral(m: BModule; info: TLineInfo): Rope =
-  result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", [])
+proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Rope) =
+  appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
 
-proc genStringLiteral(m: BModule; n: PNode): Rope =
+proc genStringLiteral(m: BModule; n: PNode; result: var Rope) =
   case detectStrVersion(m)
-  of 0, 1: result = genStringLiteralV1(m, n)
-  of 2: result = genStringLiteralV2(m, n)
+  of 0, 1: genStringLiteralV1(m, n, result)
+  of 2: genStringLiteralV2(m, n, isConst = true, result)
   else:
     localError(m.config, n.info, "cannot determine how to produce code for string literal")
diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim
deleted file mode 100644
index 664f89b73..000000000
--- a/compiler/ccgmerge.nim
+++ /dev/null
@@ -1,296 +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 implements the merge operation of 2 different C files. This
-## is needed for incremental compilation.
-
-import
-  ast, astalgo, ropes, options, strutils, nimlexbase, msgs, cgendata, rodutils,
-  intsets, platform, llstream, tables, sighashes
-
-# Careful! Section marks need to contain a tabulator so that they cannot
-# be part of C string literals.
-
-const
-  CFileSectionNames: array[TCFileSection, string] = [
-    cfsMergeInfo: "",
-    cfsHeaders: "NIM_merge_HEADERS",
-    cfsForwardTypes: "NIM_merge_FORWARD_TYPES",
-    cfsTypes: "NIM_merge_TYPES",
-    cfsSeqTypes: "NIM_merge_SEQ_TYPES",
-    cfsFieldInfo: "NIM_merge_FIELD_INFO",
-    cfsTypeInfo: "NIM_merge_TYPE_INFO",
-    cfsProcHeaders: "NIM_merge_PROC_HEADERS",
-    cfsVars: "NIM_merge_VARS",
-    cfsData: "NIM_merge_DATA",
-    cfsProcs: "NIM_merge_PROCS",
-    cfsInitProc: "NIM_merge_INIT_PROC",
-    cfsTypeInit1: "NIM_merge_TYPE_INIT1",
-    cfsTypeInit2: "NIM_merge_TYPE_INIT2",
-    cfsTypeInit3: "NIM_merge_TYPE_INIT3",
-    cfsDebugInit: "NIM_merge_DEBUG_INIT",
-    cfsDynLibInit: "NIM_merge_DYNLIB_INIT",
-    cfsDynLibDeinit: "NIM_merge_DYNLIB_DEINIT",
-  ]
-  CProcSectionNames: array[TCProcSection, string] = [
-    cpsLocals: "NIM_merge_PROC_LOCALS",
-    cpsInit: "NIM_merge_PROC_INIT",
-    cpsStmts: "NIM_merge_PROC_BODY"
-  ]
-  NimMergeEndMark = "/*\tNIM_merge_END:*/"
-
-proc genSectionStart*(fs: TCFileSection; conf: ConfigRef): Rope =
-  if compilationCachePresent(conf):
-    result = nil
-    add(result, "\n/*\t")
-    add(result, CFileSectionNames[fs])
-    add(result, ":*/\n")
-
-proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope =
-  if compilationCachePresent(conf):
-    result = rope(NimMergeEndMark & "\n")
-
-proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope =
-  if compilationCachePresent(conf):
-    result = rope(nil)
-    add(result, "\n/*\t")
-    add(result, CProcSectionNames[ps])
-    add(result, ":*/\n")
-
-proc genSectionEnd*(ps: TCProcSection; conf: ConfigRef): Rope =
-  if compilationCachePresent(conf):
-    result = rope(NimMergeEndMark & "\n")
-
-proc writeTypeCache(a: TypeCache, s: var string) =
-  var i = 0
-  for id, value in pairs(a):
-    if i == 10:
-      i = 0
-      s.add('\L')
-    else:
-      s.add(' ')
-    encodeStr($id, s)
-    s.add(':')
-    encodeStr($value, s)
-    inc i
-  s.add('}')
-
-proc writeIntSet(a: IntSet, s: var string) =
-  var i = 0
-  for x in items(a):
-    if i == 10:
-      i = 0
-      s.add('\L')
-    else:
-      s.add(' ')
-    encodeVInt(x, s)
-    inc i
-  s.add('}')
-
-proc genMergeInfo*(m: BModule): Rope =
-  if not compilationCachePresent(m.config): return nil
-  var s = "/*\tNIM_merge_INFO:\n"
-  s.add("typeCache:{")
-  writeTypeCache(m.typeCache, s)
-  s.add("declared:{")
-  writeIntSet(m.declaredThings, s)
-  when false:
-    s.add("typeInfo:{")
-    writeIntSet(m.typeInfoMarker, s)
-  s.add("labels:")
-  encodeVInt(m.labels, s)
-  s.add(" flags:")
-  encodeVInt(cast[int](m.flags), s)
-  s.add("\n*/")
-  result = s.rope
-
-template `^`(pos: int): untyped = L.buf[pos]
-
-proc skipWhite(L: var TBaseLexer) =
-  var pos = L.bufpos
-  while true:
-    case ^pos
-    of CR: pos = nimlexbase.handleCR(L, pos)
-    of LF: pos = nimlexbase.handleLF(L, pos)
-    of ' ': inc pos
-    else: break
-  L.bufpos = pos
-
-proc skipUntilCmd(L: var TBaseLexer) =
-  var pos = L.bufpos
-  while true:
-    case ^pos
-    of CR: pos = nimlexbase.handleCR(L, pos)
-    of LF: pos = nimlexbase.handleLF(L, pos)
-    of '\0': break
-    of '/':
-      if ^(pos+1) == '*' and ^(pos+2) == '\t':
-        inc pos, 3
-        break
-      inc pos
-    else: inc pos
-  L.bufpos = pos
-
-proc atEndMark(buf: cstring, pos: int): bool =
-  var s = 0
-  while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
-  result = s == NimMergeEndMark.len
-
-proc readVerbatimSection(L: var TBaseLexer): Rope =
-  var pos = L.bufpos
-  var buf = L.buf
-  var r = newStringOfCap(30_000)
-  while true:
-    case buf[pos]
-    of CR:
-      pos = nimlexbase.handleCR(L, pos)
-      buf = L.buf
-      r.add('\L')
-    of LF:
-      pos = nimlexbase.handleLF(L, pos)
-      buf = L.buf
-      r.add('\L')
-    of '\0':
-      doAssert(false, "ccgmerge: expected: " & NimMergeEndMark)
-      break
-    else:
-      if atEndMark(buf, pos):
-        inc pos, NimMergeEndMark.len
-        break
-      r.add(buf[pos])
-      inc pos
-  L.bufpos = pos
-  result = r.rope
-
-proc readKey(L: var TBaseLexer, result: var string) =
-  var pos = L.bufpos
-  var buf = L.buf
-  setLen(result, 0)
-  while buf[pos] in IdentChars:
-    result.add(buf[pos])
-    inc pos
-  if buf[pos] != ':': doAssert(false, "ccgmerge: ':' expected")
-  L.bufpos = pos + 1 # skip ':'
-
-proc newFakeType(id: int): PType =
-  new(result)
-  result.id = id
-
-proc readTypeCache(L: var TBaseLexer, result: var TypeCache) =
-  if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
-  inc L.bufpos
-  while ^L.bufpos != '}':
-    skipWhite(L)
-    var key = decodeStr(L.buf, L.bufpos)
-    if ^L.bufpos != ':': doAssert(false, "ccgmerge: ':' expected")
-    inc L.bufpos
-    var value = decodeStr(L.buf, L.bufpos)
-    # XXX implement me
-    when false:
-      idTablePut(result, newFakeType(key), value.rope)
-  inc L.bufpos
-
-proc readIntSet(L: var TBaseLexer, result: var IntSet) =
-  if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
-  inc L.bufpos
-  while ^L.bufpos != '}':
-    skipWhite(L)
-    var key = decodeVInt(L.buf, L.bufpos)
-    result.incl(key)
-  inc L.bufpos
-
-proc processMergeInfo(L: var TBaseLexer, m: BModule) =
-  var k = newStringOfCap("typeCache".len)
-  while true:
-    skipWhite(L)
-    if ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
-      inc(L.bufpos, 2)
-      break
-    readKey(L, k)
-    case k
-    of "typeCache": readTypeCache(L, m.typeCache)
-    of "declared":  readIntSet(L, m.declaredThings)
-    of "typeInfo":
-      when false: readIntSet(L, m.typeInfoMarker)
-    of "labels":    m.labels = decodeVInt(L.buf, L.bufpos)
-    of "flags":
-      m.flags = cast[set[CodegenFlag]](decodeVInt(L.buf, L.bufpos) != 0)
-    else: doAssert(false, "ccgmerge: unknown key: " & k)
-
-when not defined(nimhygiene):
-  {.pragma: inject.}
-
-template withCFile(cfilename: string, body: untyped) =
-  var s = llStreamOpen(cfilename, fmRead)
-  if s == nil: return
-  var L {.inject.}: TBaseLexer
-  openBaseLexer(L, s)
-  var k {.inject.} = newStringOfCap("NIM_merge_FORWARD_TYPES".len)
-  while true:
-    skipUntilCmd(L)
-    if ^L.bufpos == '\0': break
-    body
-  closeBaseLexer(L)
-
-proc readMergeInfo*(cfilename: string, m: BModule) =
-  ## reads the merge meta information into `m`.
-  withCFile(cfilename):
-    readKey(L, k)
-    if k == "NIM_merge_INFO":
-      processMergeInfo(L, m)
-      break
-
-type
-  TMergeSections = object
-    f: TCFileSections
-    p: TCProcSections
-
-proc readMergeSections(cfilename: string, m: var TMergeSections) =
-  ## reads the merge sections into `m`.
-  withCFile(cfilename):
-    readKey(L, k)
-    if k == "NIM_merge_INFO":
-      discard
-    elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
-      inc(L.bufpos, 2)
-      # read back into section
-      skipWhite(L)
-      var verbatim = readVerbatimSection(L)
-      skipWhite(L)
-      var sectionA = CFileSectionNames.find(k)
-      if sectionA > 0 and sectionA <= high(TCFileSection).int:
-        m.f[TCFileSection(sectionA)] = verbatim
-      else:
-        var sectionB = CProcSectionNames.find(k)
-        if sectionB >= 0 and sectionB <= high(TCProcSection).int:
-          m.p[TCProcSection(sectionB)] = verbatim
-        else:
-          doAssert(false, "ccgmerge: unknown section: " & k)
-    else:
-      doAssert(false, "ccgmerge: '*/' expected")
-
-proc mergeRequired*(m: BModule): bool =
-  for i in cfsHeaders..cfsProcs:
-    if m.s[i] != nil:
-      #echo "not empty: ", i, " ", m.s[i]
-      return true
-  for i in low(TCProcSection)..high(TCProcSection):
-    if m.initProc.s(i) != nil:
-      #echo "not empty: ", i, " ", m.initProc.s[i]
-      return true
-
-proc mergeFiles*(cfilename: string, m: BModule) =
-  ## merges the C file with the old version on hard disc.
-  var old: TMergeSections
-  readMergeSections(cfilename, old)
-  # do the merge; old section before new section:
-  for i in low(TCFileSection)..high(TCFileSection):
-    m.s[i] = old.f[i] & m.s[i]
-  for i in low(TCProcSection)..high(TCProcSection):
-    m.initProc.s(i) = old.p[i] & m.initProc.s(i)
diff --git a/compiler/ccgmerge_unused.nim b/compiler/ccgmerge_unused.nim
new file mode 100644
index 000000000..a1413034f
--- /dev/null
+++ b/compiler/ccgmerge_unused.nim
@@ -0,0 +1,283 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements the merge operation of 2 different C files. This
+## is needed for incremental compilation.
+
+import
+  ast, ropes, options, strutils, nimlexbase, cgendata, rodutils,
+  intsets, llstream, tables, modulegraphs, pathutils
+
+# Careful! Section marks need to contain a tabulator so that they cannot
+# be part of C string literals.
+
+const
+  CFileSectionNames: array[TCFileSection, string] = [
+    cfsHeaders: "NIM_merge_HEADERS",
+    cfsFrameDefines: "NIM_merge_FRAME_DEFINES",
+    cfsForwardTypes: "NIM_merge_FORWARD_TYPES",
+    cfsTypes: "NIM_merge_TYPES",
+    cfsSeqTypes: "NIM_merge_SEQ_TYPES",
+    cfsTypeInfo: "NIM_merge_TYPE_INFO",
+    cfsProcHeaders: "NIM_merge_PROC_HEADERS",
+    cfsData: "NIM_merge_DATA",
+    cfsVars: "NIM_merge_VARS",
+    cfsProcs: "NIM_merge_PROCS",
+    cfsInitProc: "NIM_merge_INIT_PROC",
+    cfsDatInitProc: "NIM_merge_DATINIT_PROC",
+    cfsTypeInit1: "NIM_merge_TYPE_INIT1",
+    cfsTypeInit3: "NIM_merge_TYPE_INIT3",
+    cfsDynLibInit: "NIM_merge_DYNLIB_INIT"
+  ]
+  CProcSectionNames: array[TCProcSection, string] = [
+    cpsLocals: "NIM_merge_PROC_LOCALS",
+    cpsInit: "NIM_merge_PROC_INIT",
+    cpsStmts: "NIM_merge_PROC_BODY"
+  ]
+  NimMergeEndMark = "/*\tNIM_merge_END:*/"
+
+proc genSectionStart*(fs: TCFileSection; conf: ConfigRef): Rope =
+  # useful for debugging and only adds at most a few lines in each file
+  result.add("\n/* section: ")
+  result.add(CFileSectionNames[fs])
+  result.add(" */\n")
+  if compilationCachePresent(conf):
+    result = nil
+    result.add("\n/*\t")
+    result.add(CFileSectionNames[fs])
+    result.add(":*/\n")
+
+proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope =
+  if compilationCachePresent(conf):
+    result = rope(NimMergeEndMark & "\n")
+
+proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope =
+  if compilationCachePresent(conf):
+    result = rope("")
+    result.add("\n/*\t")
+    result.add(CProcSectionNames[ps])
+    result.add(":*/\n")
+
+proc genSectionEnd*(ps: TCProcSection; conf: ConfigRef): Rope =
+  if compilationCachePresent(conf):
+    result = rope(NimMergeEndMark & "\n")
+
+proc writeTypeCache(a: TypeCache, s: var string) =
+  var i = 0
+  for id, value in pairs(a):
+    if i == 10:
+      i = 0
+      s.add('\L')
+    else:
+      s.add(' ')
+    encodeStr($id, s)
+    s.add(':')
+    encodeStr($value, s)
+    inc i
+  s.add('}')
+
+proc writeIntSet(a: IntSet, s: var string) =
+  var i = 0
+  for x in items(a):
+    if i == 10:
+      i = 0
+      s.add('\L')
+    else:
+      s.add(' ')
+    encodeVInt(x, s)
+    inc i
+  s.add('}')
+
+proc genMergeInfo*(m: BModule): Rope =
+  if not compilationCachePresent(m.config): return nil
+  var s = "/*\tNIM_merge_INFO:\n"
+  s.add("typeCache:{")
+  writeTypeCache(m.typeCache, s)
+  s.add("declared:{")
+  writeIntSet(m.declaredThings, s)
+  when false:
+    s.add("typeInfo:{")
+    writeIntSet(m.typeInfoMarker, s)
+  s.add("labels:")
+  encodeVInt(m.labels, s)
+  s.add(" flags:")
+  encodeVInt(cast[int](m.flags), s)
+  s.add("\n*/")
+  result = s.rope
+
+template `^`(pos: int): untyped = L.buf[pos]
+
+proc skipWhite(L: var TBaseLexer) =
+  var pos = L.bufpos
+  while true:
+    case ^pos
+    of CR: pos = nimlexbase.handleCR(L, pos)
+    of LF: pos = nimlexbase.handleLF(L, pos)
+    of ' ': inc pos
+    else: break
+  L.bufpos = pos
+
+proc skipUntilCmd(L: var TBaseLexer) =
+  var pos = L.bufpos
+  while true:
+    case ^pos
+    of CR: pos = nimlexbase.handleCR(L, pos)
+    of LF: pos = nimlexbase.handleLF(L, pos)
+    of '\0': break
+    of '/':
+      if ^(pos+1) == '*' and ^(pos+2) == '\t':
+        inc pos, 3
+        break
+      inc pos
+    else: inc pos
+  L.bufpos = pos
+
+proc atEndMark(buf: cstring, pos: int): bool =
+  var s = 0
+  while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
+  result = s == NimMergeEndMark.len
+
+proc readVerbatimSection(L: var TBaseLexer): Rope =
+  var pos = L.bufpos
+  var r = newStringOfCap(30_000)
+  while true:
+    case L.buf[pos]
+    of CR:
+      pos = nimlexbase.handleCR(L, pos)
+      r.add('\L')
+    of LF:
+      pos = nimlexbase.handleLF(L, pos)
+      r.add('\L')
+    of '\0':
+      doAssert(false, "ccgmerge: expected: " & NimMergeEndMark)
+      break
+    else:
+      if atEndMark(L.buf, pos):
+        inc pos, NimMergeEndMark.len
+        break
+      r.add(L.buf[pos])
+      inc pos
+  L.bufpos = pos
+  result = r.rope
+
+proc readKey(L: var TBaseLexer, result: var string) =
+  var pos = L.bufpos
+  setLen(result, 0)
+  while L.buf[pos] in IdentChars:
+    result.add(L.buf[pos])
+    inc pos
+  if L.buf[pos] != ':': doAssert(false, "ccgmerge: ':' expected")
+  L.bufpos = pos + 1 # skip ':'
+
+proc readTypeCache(L: var TBaseLexer, result: var TypeCache) =
+  if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
+  inc L.bufpos
+  while ^L.bufpos != '}':
+    skipWhite(L)
+    var key = decodeStr(L.buf, L.bufpos)
+    if ^L.bufpos != ':': doAssert(false, "ccgmerge: ':' expected")
+    inc L.bufpos
+    discard decodeStr(L.buf, L.bufpos)
+  inc L.bufpos
+
+proc readIntSet(L: var TBaseLexer, result: var IntSet) =
+  if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
+  inc L.bufpos
+  while ^L.bufpos != '}':
+    skipWhite(L)
+    var key = decodeVInt(L.buf, L.bufpos)
+    result.incl(key)
+  inc L.bufpos
+
+proc processMergeInfo(L: var TBaseLexer, m: BModule) =
+  var k = newStringOfCap("typeCache".len)
+  while true:
+    skipWhite(L)
+    if ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
+      inc(L.bufpos, 2)
+      break
+    readKey(L, k)
+    case k
+    of "typeCache": readTypeCache(L, m.typeCache)
+    of "declared":  readIntSet(L, m.declaredThings)
+    of "typeInfo":
+      when false: readIntSet(L, m.typeInfoMarker)
+    of "labels":    m.labels = decodeVInt(L.buf, L.bufpos)
+    of "flags":
+      m.flags = cast[set[CodegenFlag]](decodeVInt(L.buf, L.bufpos) != 0)
+    else: doAssert(false, "ccgmerge: unknown key: " & k)
+
+template withCFile(cfilename: AbsoluteFile, body: untyped) =
+  var s = llStreamOpen(cfilename, fmRead)
+  if s == nil: return
+  var L {.inject.}: TBaseLexer
+  openBaseLexer(L, s)
+  var k {.inject.} = newStringOfCap("NIM_merge_FORWARD_TYPES".len)
+  while true:
+    skipUntilCmd(L)
+    if ^L.bufpos == '\0': break
+    body
+  closeBaseLexer(L)
+
+proc readMergeInfo*(cfilename: AbsoluteFile, m: BModule) =
+  ## reads the merge meta information into `m`.
+  withCFile(cfilename):
+    readKey(L, k)
+    if k == "NIM_merge_INFO":
+      processMergeInfo(L, m)
+      break
+
+type
+  TMergeSections = object
+    f: TCFileSections
+    p: TCProcSections
+
+proc readMergeSections(cfilename: AbsoluteFile, m: var TMergeSections) =
+  ## reads the merge sections into `m`.
+  withCFile(cfilename):
+    readKey(L, k)
+    if k == "NIM_merge_INFO":
+      discard
+    elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
+      inc(L.bufpos, 2)
+      # read back into section
+      skipWhite(L)
+      var verbatim = readVerbatimSection(L)
+      skipWhite(L)
+      var sectionA = CFileSectionNames.find(k)
+      if sectionA > 0 and sectionA <= high(TCFileSection).int:
+        m.f[TCFileSection(sectionA)] = verbatim
+      else:
+        var sectionB = CProcSectionNames.find(k)
+        if sectionB >= 0 and sectionB <= high(TCProcSection).int:
+          m.p[TCProcSection(sectionB)] = verbatim
+        else:
+          doAssert(false, "ccgmerge: unknown section: " & k)
+    else:
+      doAssert(false, "ccgmerge: '*/' expected")
+
+proc mergeRequired*(m: BModule): bool =
+  for i in cfsHeaders..cfsProcs:
+    if m.s[i] != nil:
+      #echo "not empty: ", i, " ", m.s[i]
+      return true
+  for i in TCProcSection:
+    if m.initProc.s(i) != nil:
+      #echo "not empty: ", i, " ", m.initProc.s[i]
+      return true
+
+proc mergeFiles*(cfilename: AbsoluteFile, m: BModule) =
+  ## merges the C file with the old version on hard disc.
+  var old: TMergeSections
+  readMergeSections(cfilename, old)
+  # do the merge; old section before new section:
+  for i in TCFileSection:
+    m.s[i] = old.f[i] & m.s[i]
+  for i in TCProcSection:
+    m.initProc.s(i) = old.p[i] & m.initProc.s(i)
diff --git a/compiler/ccgreset.nim b/compiler/ccgreset.nim
new file mode 100644
index 000000000..6caeb8084
--- /dev/null
+++ b/compiler/ccgreset.nim
@@ -0,0 +1,105 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# included from cgen.nim
+
+## Code specialization instead of the old, incredibly slow 'genericReset'
+## implementation.
+
+proc specializeResetT(p: BProc, accessor: Rope, typ: PType)
+
+proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
+                     typ: PType) =
+  if n == nil: return
+  case n.kind
+  of nkRecList:
+    for i in 0..<n.len:
+      specializeResetN(p, accessor, n[i], typ)
+  of nkRecCase:
+    if (n[0].kind != nkSym): internalError(p.config, n.info, "specializeResetN")
+    let disc = n[0].sym
+    if disc.loc.snippet == "": fillObjectFields(p.module, typ)
+    if disc.loc.t == nil:
+      internalError(p.config, n.info, "specializeResetN()")
+    lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.snippet])
+    for i in 1..<n.len:
+      let branch = n[i]
+      assert branch.kind in {nkOfBranch, nkElse}
+      if branch.kind == nkOfBranch:
+        genCaseRange(p, branch)
+      else:
+        lineF(p, cpsStmts, "default:$n", [])
+      specializeResetN(p, accessor, lastSon(branch), typ)
+      lineF(p, cpsStmts, "break;$n", [])
+    lineF(p, cpsStmts, "} $n", [])
+    specializeResetT(p, "$1.$2" % [accessor, disc.loc.snippet], disc.loc.t)
+  of nkSym:
+    let field = n.sym
+    if field.typ.kind == tyVoid: return
+    if field.loc.snippet == "": fillObjectFields(p.module, typ)
+    if field.loc.t == nil:
+      internalError(p.config, n.info, "specializeResetN()")
+    specializeResetT(p, "$1.$2" % [accessor, field.loc.snippet], field.loc.t)
+  else: internalError(p.config, n.info, "specializeResetN()")
+
+proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
+  if typ == nil: return
+
+  case typ.kind
+  of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred,
+     tySink, tyOwned:
+    specializeResetT(p, accessor, skipModifier(typ))
+  of tyArray:
+    let arraySize = lengthOrd(p.config, typ.indexType)
+    var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt))
+    linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
+            [i.snippet, arraySize])
+    specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType)
+    lineF(p, cpsStmts, "}$n", [])
+  of tyObject:
+    var x = typ.baseClass
+    if x != nil: x = x.skipTypes(skipPtrs)
+    specializeResetT(p, accessor.parentObj(p.module), x)
+    if typ.n != nil: specializeResetN(p, accessor, typ.n, typ)
+  of tyTuple:
+    let typ = getUniqueType(typ)
+    for i, a in typ.ikids:
+      specializeResetT(p, ropecg(p.module, "$1.Field$2", [accessor, i]), a)
+
+  of tyString, tyRef, tySequence:
+    lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1, NIM_NIL);$n", [accessor])
+
+  of tyProc:
+    if typ.callConv == ccClosure:
+      lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1.ClE_0, NIM_NIL);$n", [accessor])
+      lineCg(p, cpsStmts, "$1.ClP_0 = NIM_NIL;$n", [accessor])
+    else:
+      lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
+  of tyChar, tyBool, tyEnum, tyRange, tyInt..tyUInt64:
+    lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
+  of tyCstring, tyPointer, tyPtr, tyVar, tyLent:
+    lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
+  of tySet:
+    case mapSetType(p.config, typ)
+    of ctArray:
+      lineCg(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n",
+          [accessor, getTypeDesc(p.module, typ)])
+    of ctInt8, ctInt16, ctInt32, ctInt64:
+      lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
+    else:
+      raiseAssert "unexpected set type kind"
+  of tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
+      tyGenericParam, tyOrdinal, tyOpenArray, tyForward, tyVarargs,
+      tyUncheckedArray, tyError, tyBuiltInTypeClass, tyUserTypeClass,
+      tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot,
+      tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable:
+    discard
+
+proc specializeReset(p: BProc, a: TLoc) =
+  specializeResetT(p, rdLoc(a), a.t)
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index f9654bb1f..883108f2c 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -8,79 +8,134 @@
 #
 
 # included from cgen.nim
-
 const
   RangeExpandLimit = 256      # do not generate ranges
                               # over 'RangeExpandLimit' elements
   stringCaseThreshold = 8
     # above X strings a hash-switch for strings is generated
 
-proc registerGcRoot(p: BProc, v: PSym) =
-  if p.config.selectedGC in {gcMarkAndSweep, gcGenerational, gcV2, gcRefc} and
+proc registerTraverseProc(p: BProc, v: PSym) =
+  var traverseProc = ""
+  if p.config.selectedGC in {gcMarkAndSweep, gcHooks, gcRefc} and
+      optOwnedRefs notin p.config.globalOptions and
       containsGarbageCollectedRef(v.loc.t):
     # we register a specialized marked proc here; this has the advantage
     # that it works out of the box for thread local storage then :-)
-    let prc = genTraverseProcForGlobal(p.module, v, v.info)
+    traverseProc = genTraverseProcForGlobal(p.module, v, v.info)
+
+  if traverseProc.len != 0 and not p.hcrOn:
     if sfThread in v.flags:
-      appcg(p.module, p.module.initProc.procSec(cpsInit),
-        "#nimRegisterThreadLocalMarker($1);$n", [prc])
+      appcg(p.module, p.module.preInitProc.procSec(cpsInit),
+        "$n\t#nimRegisterThreadLocalMarker($1);$n$n", [traverseProc])
     else:
-      appcg(p.module, p.module.initProc.procSec(cpsInit),
-        "#nimRegisterGlobalMarker($1);$n", [prc])
+      appcg(p.module, p.module.preInitProc.procSec(cpsInit),
+        "$n\t#nimRegisterGlobalMarker($1);$n$n", [traverseProc])
 
 proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} =
-  if n.kind == nkEmpty: return false
-  if isInvalidReturnType(conf, n.typ):
-    # var v = f()
-    # is transformed into: var v;  f(addr v)
-    # where 'f' **does not** initialize the result!
-    return false
-  result = true
+  if n.kind == nkEmpty:
+    result = false
+  elif n.kind in nkCallKinds and n[0] != nil and n[0].typ != nil and n[0].typ.skipTypes(abstractInst).kind == tyProc:
+    if n[0].kind == nkSym and sfConstructor in n[0].sym.flags:
+      result = true
+    elif isInvalidReturnType(conf, n[0].typ, true):
+      # var v = f()
+      # is transformed into: var v;  f(addr v)
+      # where 'f' **does not** initialize the result!
+      result = false
+    else:
+      result = true
+  elif isInvalidReturnType(conf, n.typ, false):
+    result = false
+  else:
+    result = true
 
 proc inExceptBlockLen(p: BProc): int =
+  result = 0
   for x in p.nestedTryStmts:
     if x.inExcept: result.inc
 
+proc startBlockInternal(p: BProc): int {.discardable.} =
+  inc(p.labels)
+  result = p.blocks.len
+
+  p.blocks.add initBlock()
+
+  p.blocks[result].id = p.labels
+  p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16
+  p.blocks[result].nestedExceptStmts = p.inExceptBlockLen.int16
+
+template startBlock(p: BProc, start: FormatStr = "{$n",
+                args: varargs[Rope]): int =
+  lineCg(p, cpsStmts, start, args)
+  startBlockInternal(p)
+
+proc endBlock(p: BProc)
+
 proc genVarTuple(p: BProc, n: PNode) =
-  var tup, field: TLoc
   if n.kind != nkVarTuple: internalError(p.config, n.info, "genVarTuple")
-  var L = sonsLen(n)
 
   # if we have a something that's been captured, use the lowering instead:
-  for i in countup(0, L-3):
+  for i in 0..<n.len-2:
     if n[i].kind != nkSym:
-      genStmts(p, lowerTupleUnpacking(p.module.g.graph, n, p.prc))
+      genStmts(p, lowerTupleUnpacking(p.module.g.graph, n, p.module.idgen, p.prc))
       return
 
+  # check only the first son
+  var forHcr = treatGlobalDifferentlyForHCR(p.module, n[0].sym)
+  let hcrCond = if forHcr: getTempName(p.module) else: ""
+  var hcrGlobals: seq[tuple[loc: TLoc, tp: Rope]] = @[]
+  # determine if the tuple is constructed at top-level scope or inside of a block (if/while/block)
+  let isGlobalInBlock = forHcr and p.blocks.len > 2
+  # do not close and reopen blocks if this is a 'global' but inside of a block (if/while/block)
+  forHcr = forHcr and not isGlobalInBlock
+
+  if forHcr:
+    # check with the boolean if the initializing code for the tuple should be ran
+    lineCg(p, cpsStmts, "if ($1)$n", [hcrCond])
+    startBlock(p)
+
   genLineDir(p, n)
-  initLocExpr(p, n.sons[L-1], tup)
+  var tup = initLocExpr(p, n[^1])
   var t = tup.t.skipTypes(abstractInst)
-  for i in countup(0, L-3):
-    let vn = n.sons[i]
+  for i in 0..<n.len-2:
+    let vn = n[i]
     let v = vn.sym
     if sfCompileTime in v.flags: continue
     if sfGlobal in v.flags:
-      assignGlobalVar(p, vn)
-      genObjectInit(p, cpsInit, v.typ, v.loc, true)
-      registerGcRoot(p, v)
+      assignGlobalVar(p, vn, "")
+      genObjectInit(p, cpsInit, v.typ, v.loc, constructObj)
+      registerTraverseProc(p, v)
     else:
       assignLocalVar(p, vn)
-      initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[L-1]))
-    initLoc(field, locExpr, vn, tup.storage)
+      initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[^1]))
+    var field = initLoc(locExpr, vn, tup.storage)
     if t.kind == tyTuple:
-      field.r = "$1.Field$2" % [rdLoc(tup), rope(i)]
+      field.snippet = "$1.Field$2" % [rdLoc(tup), rope(i)]
     else:
-      if t.n.sons[i].kind != nkSym: internalError(p.config, n.info, "genVarTuple")
-      field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n.sons[i].sym, t)]
+      if t.n[i].kind != nkSym: internalError(p.config, n.info, "genVarTuple")
+      field.snippet = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n[i].sym)]
     putLocIntoDest(p, v.loc, field)
+    if forHcr or isGlobalInBlock:
+      hcrGlobals.add((loc: v.loc, tp: "NULL"))
+
+  if forHcr:
+    # end the block where the tuple gets initialized
+    endBlock(p)
+  if forHcr or isGlobalInBlock:
+    # insert the registration of the globals for the different parts of the tuple at the
+    # start of the current scope (after they have been iterated) and init a boolean to
+    # check if any of them is newly introduced and the initializing code has to be ran
+    lineCg(p, cpsLocals, "NIM_BOOL $1 = NIM_FALSE;$n", [hcrCond])
+    for curr in hcrGlobals:
+      lineCg(p, cpsLocals, "$1 |= hcrRegisterGlobal($4, \"$2\", sizeof($3), $5, (void**)&$2);$N",
+              [hcrCond, curr.loc.snippet, rdLoc(curr.loc), getModuleDllPath(p.module, n[0].sym), curr.tp])
 
-proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false)
 
 proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} =
-  if ri.kind in nkCallKinds and (ri.sons[0].kind != nkSym or
-                                 ri.sons[0].sym.magic == mNone):
+  if ri.kind in nkCallKinds and (ri[0].kind != nkSym or
+                                 ri[0].sym.magic == mNone):
     genAsgnCall(p, le, ri, a)
-  elif ri.kind in {nkDerefExpr, nkHiddenDeref}:
+  else:
     # this is a hacky way to fix #1181 (tmissingderef)::
     #
     #  var arr1 = cast[ptr array[4, int8]](addr foo)[]
@@ -88,26 +143,15 @@ proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} =
     # However, fixing this properly really requires modelling 'array' as
     # a 'struct' in C to preserve dereferencing semantics completely. Not
     # worth the effort until version 1.0 is out.
-    genDeref(p, ri, a, enforceDeref=true)
-  else:
+    a.flags.incl(lfEnforceDeref)
     expr(p, ri, a)
 
-proc startBlock(p: BProc, start: FormatStr = "{$n",
-                args: varargs[Rope]): int {.discardable.} =
-  lineCg(p, cpsStmts, start, args)
-  inc(p.labels)
-  result = len(p.blocks)
-  setLen(p.blocks, result + 1)
-  p.blocks[result].id = p.labels
-  p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16
-  p.blocks[result].nestedExceptStmts = p.inExceptBlockLen.int16
-
-proc assignLabel(b: var TBlock): Rope {.inline.} =
+proc assignLabel(b: var TBlock; result: var Rope) {.inline.} =
   b.label = "LA" & b.id.rope
-  result = b.label
+  result.add b.label
 
-proc blockBody(b: var TBlock): Rope =
-  result = b.sections[cpsLocals]
+proc blockBody(b: var TBlock; result: var Rope) =
+  result.add b.sections[cpsLocals]
   if b.frameLen > 0:
     result.addf("FR_.len+=$1;$n", [b.frameLen.rope])
   result.add(b.sections[cpsInit])
@@ -116,7 +160,7 @@ proc blockBody(b: var TBlock): Rope =
 proc endBlock(p: BProc, blockEnd: Rope) =
   let topBlock = p.blocks.len-1
   # the block is merged into the parent block
-  add(p.blocks[topBlock-1].sections[cpsStmts], p.blocks[topBlock].blockBody)
+  p.blocks[topBlock].blockBody(p.blocks[topBlock-1].sections[cpsStmts])
   setLen(p.blocks, topBlock)
   # this is done after the block is popped so $n is
   # properly indented when pretty printing is enabled
@@ -124,13 +168,14 @@ proc endBlock(p: BProc, blockEnd: Rope) =
 
 proc endBlock(p: BProc) =
   let topBlock = p.blocks.len - 1
-  var blockEnd = if p.blocks[topBlock].label != nil:
-      ropecg(p.module, "} $1: ;$n", p.blocks[topBlock].label)
-    else:
-      ~"}$n"
   let frameLen = p.blocks[topBlock].frameLen
+  var blockEnd: Rope = ""
   if frameLen > 0:
     blockEnd.addf("FR_.len-=$1;$n", [frameLen.rope])
+  if p.blocks[topBlock].label.len != 0:
+    blockEnd.addf("} $1: ;$n", [p.blocks[topBlock].label])
+  else:
+    blockEnd.addf("}$n", [])
   endBlock(p, blockEnd)
 
 proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} =
@@ -152,23 +197,24 @@ proc genState(p: BProc, n: PNode) =
   internalAssert p.config, n.len == 1
   let n0 = n[0]
   if n0.kind == nkIntLit:
-    let idx = n.sons[0].intVal
-    linefmt(p, cpsStmts, "STATE$1: ;$n", idx.rope)
+    let idx = n[0].intVal
+    linefmt(p, cpsStmts, "STATE$1: ;$n", [idx])
   elif n0.kind == nkStrLit:
-    linefmt(p, cpsStmts, "$1: ;$n", n0.strVal.rope)
+    linefmt(p, cpsStmts, "$1: ;$n", [n0.strVal])
 
 proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
   # Called by return and break stmts.
-  # Deals with issues faced when jumping out of try/except/finally stmts,
+  # Deals with issues faced when jumping out of try/except/finally stmts.
 
-  var stack = newSeq[tuple[n: PNode, inExcept: bool]](0)
+  var stack = newSeq[tuple[fin: PNode, inExcept: bool, label: Natural]](0)
 
-  for i in countup(1, howManyTrys):
+  inc p.withinBlockLeaveActions
+  for i in 1..howManyTrys:
     let tryStmt = p.nestedTryStmts.pop
-    if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
+    if p.config.exc == excSetjmp:
       # Pop safe points generated by try
       if not tryStmt.inExcept:
-        linefmt(p, cpsStmts, "#popSafePoint();$n")
+        linefmt(p, cpsStmts, "#popSafePoint();$n", [])
 
     # Pop this try-stmt of the list of nested trys
     # so we don't infinite recurse on it in the next step.
@@ -176,19 +222,21 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
 
     # Find finally-stmt for this try-stmt
     # and generate a copy of its sons
-    var finallyStmt = lastSon(tryStmt.n)
-    if finallyStmt.kind == nkFinally:
-      genStmts(p, finallyStmt.sons[0])
+    var finallyStmt = tryStmt.fin
+    if finallyStmt != nil:
+      genStmts(p, finallyStmt[0])
+
+  dec p.withinBlockLeaveActions
 
   # push old elements again:
   for i in countdown(howManyTrys-1, 0):
     p.nestedTryStmts.add(stack[i])
 
-  if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
-    # Pop exceptions that was handled by the
-    # except-blocks we are in
+  # Pop exceptions that was handled by the
+  # except-blocks we are in
+  if noSafePoints notin p.flags:
     for i in countdown(howManyExcepts-1, 0):
-      linefmt(p, cpsStmts, "#popCurrentException();$n")
+      linefmt(p, cpsStmts, "#popCurrentException();$n", [])
 
 proc genGotoState(p: BProc, n: PNode) =
   # we resist the temptation to translate it into duff's device as it later
@@ -196,35 +244,34 @@ proc genGotoState(p: BProc, n: PNode) =
   # switch (x.state) {
   #   case 0: goto STATE0;
   # ...
-  var a: TLoc
-  initLocExpr(p, n.sons[0], a)
+  var a: TLoc = initLocExpr(p, n[0])
   lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)])
-  p.beforeRetNeeded = true
+  p.flags.incl beforeRetNeeded
   lineF(p, cpsStmts, "case -1:$n", [])
   blockLeaveActions(p,
     howManyTrys    = p.nestedTryStmts.len,
     howManyExcepts = p.inExceptBlockLen)
   lineF(p, cpsStmts, " goto BeforeRet_;$n", [])
-  var statesCounter = lastOrd(p.config, n.sons[0].typ)
+  var statesCounter = lastOrd(p.config, n[0].typ)
   if n.len >= 2 and n[1].kind == nkIntLit:
-    statesCounter = n[1].intVal
+    statesCounter = getInt(n[1])
   let prefix = if n.len == 3 and n[2].kind == nkStrLit: n[2].strVal.rope
                else: rope"STATE"
-  for i in 0i64 .. statesCounter:
+  for i in 0i64..toInt64(statesCounter):
     lineF(p, cpsStmts, "case $2: goto $1$2;$n", [prefix, rope(i)])
   lineF(p, cpsStmts, "}$n", [])
 
 proc genBreakState(p: BProc, n: PNode, d: var TLoc) =
   var a: TLoc
-  initLoc(d, locExpr, n, OnUnknown)
+  d = initLoc(locExpr, n, OnUnknown)
 
-  if n.sons[0].kind == nkClosure:
-    initLocExpr(p, n.sons[0].sons[1], a)
-    d.r = "(((NI*) $1)[1] < 0)" % [rdLoc(a)]
+  if n[0].kind == nkClosure:
+    a = initLocExpr(p, n[0][1])
+    d.snippet = "(((NI*) $1)[1] < 0)" % [rdLoc(a)]
   else:
-    initLocExpr(p, n.sons[0], a)
+    a = initLocExpr(p, n[0])
     # the environment is guaranteed to contain the 'state' field at offset 1:
-    d.r = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)]
+    d.snippet = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)]
 
 proc genGotoVar(p: BProc; value: PNode) =
   if value.kind notin {nkCharLit..nkUInt64Lit}:
@@ -232,85 +279,176 @@ proc genGotoVar(p: BProc; value: PNode) =
   else:
     lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
 
-proc genSingleVar(p: BProc, a: PNode) =
-  let vn = a.sons[0]
-  let v = vn.sym
-  if sfCompileTime in v.flags: return
+proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Rope)
+
+proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) =
+  if lfDynamicLib in v.loc.flags or sfThread in v.flags or p.hcrOn:
+    discard "nothing to do"
+  elif sfGlobal in v.flags and value != nil and isDeepConstExpr(value, p.module.compileToCpp) and
+      p.withinLoop == 0 and not containsGarbageCollectedRef(v.typ):
+    #echo "New code produced for ", v.name.s, " ", p.config $ value.info
+    genBracedInit(p, value, isConst = false, v.typ, result)
+
+proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): string =
+  result = ""
+  var argsCounter = 0
+  let typ = skipTypes(call[0].typ, abstractInst)
+  assert(typ.kind == tyProc)
+  for i in 1..<call.len:
+    #if it's a type we can just generate here another initializer as we are in an initializer context
+    if call[i].kind == nkCall and call[i][0].kind == nkSym and call[i][0].sym.kind == skType:
+      if argsCounter > 0: result.add ","
+      result.add genCppInitializer(p.module, p, call[i][0].sym.typ, didGenTemp)
+    else:
+      #We need to test for temp in globals, see: #23657
+      let param =
+        if typ[i].kind in {tyVar} and call[i].kind == nkHiddenAddr:
+          call[i][0]
+        else:
+          call[i]
+      if param.kind != nkBracketExpr or param.typ.kind in
+        {tyRef, tyPtr, tyUncheckedArray, tyArray, tyOpenArray,
+          tyVarargs, tySequence, tyString, tyCstring, tyTuple}:
+        let tempLoc = initLocExprSingleUse(p, param)
+        didGenTemp = didGenTemp or tempLoc.k == locTemp
+      genOtherArg(p, call, i, typ, result, argsCounter)
+
+proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope, didGenTemp: var bool) =
+  let params = genCppParamsForCtor(p, call, didGenTemp)
+  if params.len == 0:
+    decl = runtimeFormat("$#;\n", [decl])
+  else:
+    decl = runtimeFormat("$#($#);\n", [decl, params])
+
+proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
   if sfGoto in v.flags:
     # translate 'var state {.goto.} = X' into 'goto LX':
-    genGotoVar(p, a.sons[2])
+    genGotoVar(p, value)
     return
+  let imm = isAssignedImmediately(p.config, value)
+  let isCppCtorCall = p.module.compileToCpp and imm and
+    value.kind in nkCallKinds and value[0].kind == nkSym and
+    v.typ.kind != tyPtr and sfConstructor in value[0].sym.flags
   var targetProc = p
+  var valueAsRope = ""
+  potentialValueInit(p, v, value, valueAsRope)
   if sfGlobal in v.flags:
     if v.flags * {sfImportc, sfExportc} == {sfImportc} and
-        a.sons[2].kind == nkEmpty and
+        value.kind == nkEmpty and
         v.loc.flags * {lfHeader, lfNoDecl} != {}:
       return
     if sfPure in v.flags:
       # v.owner.kind != skModule:
       targetProc = p.module.preInitProc
-    assignGlobalVar(targetProc, vn)
+    if isCppCtorCall and not containsHiddenPointer(v.typ):
+      var didGenTemp = false
+      callGlobalVarCppCtor(targetProc, v, vn, value, didGenTemp)
+      if didGenTemp:
+        message(p.config, vn.info, warnGlobalVarConstructorTemporary, vn.sym.name.s)
+        #We fail to call the constructor in the global scope so we do the call inside the main proc
+        assignGlobalVar(targetProc, vn, valueAsRope)
+        var loc = initLocExprSingleUse(targetProc, value)
+        genAssignment(targetProc, v.loc, loc, {})
+    else:
+      assignGlobalVar(targetProc, vn, valueAsRope)
+
     # XXX: be careful here.
     # Global variables should not be zeromem-ed within loops
     # (see bug #20).
     # That's why we are doing the construction inside the preInitProc.
     # genObjectInit relies on the C runtime's guarantees that
     # global variables will be initialized to zero.
-    genObjectInit(p.module.preInitProc, cpsInit, v.typ, v.loc, true)
+    if valueAsRope.len == 0:
+      var loc = v.loc
+      # When the native TLS is unavailable, a global thread-local variable needs
+      # one more layer of indirection in order to access the TLS block.
+      # Only do this for complex types that may need a call to `objectInit`
+      if sfThread in v.flags and emulatedThreadVars(p.config) and
+        isComplexValueType(v.typ):
+        loc = initLocExprSingleUse(p.module.preInitProc, vn)
+      genObjectInit(p.module.preInitProc, cpsInit, v.typ, loc, constructObj)
     # Alternative construction using default constructor (which may zeromem):
     # if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc)
     if sfExportc in v.flags and p.module.g.generatedHeader != nil:
       genVarPrototype(p.module.g.generatedHeader, vn)
-    registerGcRoot(p, v)
+    registerTraverseProc(p, v)
   else:
-    let value = a.sons[2]
-    let imm = isAssignedImmediately(p.config, value)
     if imm and p.module.compileToCpp and p.splitDecls == 0 and
-        not containsHiddenPointer(v.typ):
+        not containsHiddenPointer(v.typ) and
+        nimErrorFlagAccessed notin p.flags:
       # C++ really doesn't like things like 'Foo f; f = x' as that invokes a
       # parameterless constructor followed by an assignment operator. So we
-      # generate better code here:
-      genLineDir(p, a)
-      let decl = localVarDecl(p, vn)
+      # generate better code here: 'Foo f = x;'
+      genLineDir(p, vn)
+      var decl = localVarDecl(p, vn)
       var tmp: TLoc
-      if value.kind in nkCallKinds and value[0].kind == nkSym and
-           sfConstructor in value[0].sym.flags:
-        var params: Rope
-        let typ = skipTypes(value.sons[0].typ, abstractInst)
-        assert(typ.kind == tyProc)
-        for i in 1..<value.len:
-          if params != nil: params.add(~", ")
-          assert(sonsLen(typ) == sonsLen(typ.n))
-          add(params, genOtherArg(p, value, i, typ))
-        if params == nil:
-          lineF(p, cpsStmts, "$#;$n", [decl])
-        else:
-          lineF(p, cpsStmts, "$#($#);$n", [decl, params])
+      if isCppCtorCall:
+        var didGenTemp = false
+        genCppVarForCtor(p, value, decl, didGenTemp)
+        line(p, cpsStmts, decl)
       else:
-        initLocExprSingleUse(p, value, tmp)
-        lineF(p, cpsStmts, "$# = $#;$n", [decl, tmp.rdLoc])
+        tmp = initLocExprSingleUse(p, value)
+        if value.kind == nkEmpty:
+          lineF(p, cpsStmts, "$#;\n", [decl])
+        else:
+          lineF(p, cpsStmts, "$# = $#;\n", [decl, tmp.rdLoc])
       return
     assignLocalVar(p, vn)
     initLocalVar(p, v, imm)
 
-  if a.sons[2].kind != nkEmpty:
-    genLineDir(targetProc, a)
-    loadInto(targetProc, a.sons[0], a.sons[2], v.loc)
+  let traverseProc = "NULL"
+  # If the var is in a block (control flow like if/while or a block) in global scope just
+  # register the so called "global" so it can be used later on. There is no need to close
+  # and reopen of if (nim_hcr_do_init_) blocks because we are in one already anyway.
+  var forHcr = treatGlobalDifferentlyForHCR(p.module, v)
+  if forHcr and targetProc.blocks.len > 3 and v.owner.kind == skModule:
+    # put it in the locals section - mainly because of loops which
+    # use the var in a call to resetLoc() in the statements section
+    lineCg(targetProc, cpsLocals, "hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1);$n",
+           [v.loc.snippet, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
+    # nothing special left to do later on - let's avoid closing and reopening blocks
+    forHcr = false
+
+  # we close and reopen the global if (nim_hcr_do_init_) blocks in the main Init function
+  # for the module so we can have globals and top-level code be interleaved and still
+  # be able to re-run it but without the top level code - just the init of globals
+  if forHcr:
+    lineCg(targetProc, cpsStmts, "if (hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1))$N",
+           [v.loc.snippet, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
+    startBlock(targetProc)
+  if value.kind != nkEmpty and valueAsRope.len == 0:
+    genLineDir(targetProc, vn)
+    if not isCppCtorCall:
+      loadInto(targetProc, vn, value, v.loc)
+  if forHcr:
+    endBlock(targetProc)
+
+proc genSingleVar(p: BProc, a: PNode) =
+  let v = a[0].sym
+  if sfCompileTime in v.flags:
+    # fix issue #12640
+    # {.global, compileTime.} pragma in proc
+    if sfGlobal in v.flags and p.prc != nil and p.prc.kind == skProc:
+      discard
+    else:
+      return
+  genSingleVar(p, v, a[0], a[2])
 
 proc genClosureVar(p: BProc, a: PNode) =
-  var immediateAsgn = a.sons[2].kind != nkEmpty
+  var immediateAsgn = a[2].kind != nkEmpty
+  var v: TLoc = initLocExpr(p, a[0])
+  genLineDir(p, a)
   if immediateAsgn:
-    var v: TLoc
-    initLocExpr(p, a.sons[0], v)
-    genLineDir(p, a)
-    loadInto(p, a.sons[0], a.sons[2], v)
+    loadInto(p, a[0], a[2], v)
+  elif sfNoInit notin a[0][1].sym.flags:
+    constructLoc(p, v)
 
 proc genVarStmt(p: BProc, n: PNode) =
   for it in n.sons:
     if it.kind == nkCommentStmt: continue
     if it.kind == nkIdentDefs:
       # can be a lifted var nowadays ...
-      if it.sons[0].kind == nkSym:
+      if it[0].kind == nkSym:
         genSingleVar(p, it)
       else:
         genClosureVar(p, it)
@@ -333,83 +471,99 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
     a: TLoc
     lelse: TLabel
   if not isEmptyType(n.typ) and d.k == locNone:
-    getTemp(p, n.typ, d)
+    d = getTemp(p, n.typ)
   genLineDir(p, n)
   let lend = getLabel(p)
   for it in n.sons:
     # bug #4230: avoid false sharing between branches:
     if d.k == locTemp and isEmptyType(n.typ): d.k = locNone
     if it.len == 2:
-      when newScopeForIf: startBlock(p)
-      initLocExprSingleUse(p, it.sons[0], a)
+      startBlock(p)
+      a = initLocExprSingleUse(p, it[0])
       lelse = getLabel(p)
       inc(p.labels)
       lineF(p, cpsStmts, "if (!$1) goto $2;$n",
             [rdLoc(a), lelse])
-      when not newScopeForIf: startBlock(p)
       if p.module.compileToCpp:
         # avoid "jump to label crosses initialization" error:
-        add(p.s(cpsStmts), "{")
-        expr(p, it.sons[1], d)
-        add(p.s(cpsStmts), "}")
+        p.s(cpsStmts).add "{"
+        expr(p, it[1], d)
+        p.s(cpsStmts).add "}"
       else:
-        expr(p, it.sons[1], d)
+        expr(p, it[1], d)
       endBlock(p)
-      if sonsLen(n) > 1:
+      if n.len > 1:
         lineF(p, cpsStmts, "goto $1;$n", [lend])
       fixLabel(p, lelse)
     elif it.len == 1:
       startBlock(p)
-      expr(p, it.sons[0], d)
+      expr(p, it[0], d)
       endBlock(p)
     else: internalError(p.config, n.info, "genIf()")
-  if sonsLen(n) > 1: fixLabel(p, lend)
+  if n.len > 1: fixLabel(p, lend)
 
 proc genReturnStmt(p: BProc, t: PNode) =
   if nfPreventCg in t.flags: return
-  p.beforeRetNeeded = true
+  p.flags.incl beforeRetNeeded
   genLineDir(p, t)
-  if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0])
+  if (t[0].kind != nkEmpty): genStmts(p, t[0])
   blockLeaveActions(p,
     howManyTrys    = p.nestedTryStmts.len,
     howManyExcepts = p.inExceptBlockLen)
-  if (p.finallySafePoints.len > 0):
+  if (p.finallySafePoints.len > 0) and noSafePoints notin p.flags:
     # If we're in a finally block, and we came here by exception
     # consume it before we return.
-    var safePoint = p.finallySafePoints[p.finallySafePoints.len-1]
-    linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint)
+    var safePoint = p.finallySafePoints[^1]
+    linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", [safePoint])
   lineF(p, cpsStmts, "goto BeforeRet_;$n", [])
 
 proc genGotoForCase(p: BProc; caseStmt: PNode) =
-  for i in 1 ..< caseStmt.len:
+  for i in 1..<caseStmt.len:
     startBlock(p)
-    let it = caseStmt.sons[i]
-    for j in 0 .. it.len-2:
-      if it.sons[j].kind == nkRange:
+    let it = caseStmt[i]
+    for j in 0..<it.len-1:
+      if it[j].kind == nkRange:
         localError(p.config, it.info, "range notation not available for computed goto")
         return
-      let val = getOrdValue(it.sons[j])
+      let val = getOrdValue(it[j])
       lineF(p, cpsStmts, "NIMSTATE_$#:$n", [val.rope])
     genStmts(p, it.lastSon)
     endBlock(p)
 
+
+iterator fieldValuePairs(n: PNode): tuple[memberSym, valueSym: PNode] =
+  assert(n.kind in {nkLetSection, nkVarSection})
+  for identDefs in n:
+    if identDefs.kind == nkIdentDefs:
+      let valueSym = identDefs[^1]
+      for i in 0..<identDefs.len-2:
+        let memberSym = identDefs[i]
+        yield((memberSym: memberSym, valueSym: valueSym))
+
 proc genComputedGoto(p: BProc; n: PNode) =
   # first pass: Generate array of computed labels:
+
+  # flatten the loop body because otherwise let and var sections
+  # wrapped inside stmt lists by inject destructors won't be recognised
+  let n = n.flattenStmts()
   var casePos = -1
-  var arraySize: int
-  for i in 0 ..< n.len:
-    let it = n.sons[i]
+  var arraySize: int = 0
+  for i in 0..<n.len:
+    let it = n[i]
     if it.kind == nkCaseStmt:
       if lastSon(it).kind != nkOfBranch:
         localError(p.config, it.info,
             "case statement must be exhaustive for computed goto"); return
       casePos = i
-      let aSize = lengthOrd(p.config, it.sons[0].typ)
+      if enumHasHoles(it[0].typ):
+        localError(p.config, it.info,
+            "case statement cannot work on enums with holes for computed goto"); return
+      let aSize = lengthOrd(p.config, it[0].typ)
       if aSize > 10_000:
         localError(p.config, it.info,
             "case statement has too many cases for computed goto"); return
-      arraySize = aSize.int
-      if firstOrd(p.config, it.sons[0].typ) != 0:
+      arraySize = toInt(aSize)
+      if firstOrd(p.config, it[0].typ) != 0:
         localError(p.config, it.info,
             "case statement has to start at 0 for computed goto"); return
   if casePos < 0:
@@ -419,132 +573,162 @@ proc genComputedGoto(p: BProc; n: PNode) =
   let tmp = "TMP$1_" % [id.rope]
   var gotoArray = "static void* $#[$#] = {" % [tmp, arraySize.rope]
   for i in 1..arraySize-1:
-    gotoArray.addf("&&TMP$#_, ", [(id+i).rope])
-  gotoArray.addf("&&TMP$#_};$n", [(id+arraySize).rope])
+    gotoArray.addf("&&TMP$#_, ", [rope(id+i)])
+  gotoArray.addf("&&TMP$#_};$n", [rope(id+arraySize)])
   line(p, cpsLocals, gotoArray)
 
-  let topBlock = p.blocks.len-1
-  let oldBody = p.blocks[topBlock].sections[cpsStmts]
-  p.blocks[topBlock].sections[cpsStmts] = nil
-
-  for j in casePos+1 ..< n.len: genStmts(p, n.sons[j])
-  let tailB = p.blocks[topBlock].sections[cpsStmts]
-
-  p.blocks[topBlock].sections[cpsStmts] = nil
-  for j in 0 .. casePos-1: genStmts(p, n.sons[j])
-  let tailA = p.blocks[topBlock].sections[cpsStmts]
+  for j in 0..<casePos:
+    genStmts(p, n[j])
 
-  p.blocks[topBlock].sections[cpsStmts] = oldBody & tailA
-
-  let caseStmt = n.sons[casePos]
-  var a: TLoc
-  initLocExpr(p, caseStmt.sons[0], a)
+  let caseStmt = n[casePos]
+  var a: TLoc = initLocExpr(p, caseStmt[0])
   # first goto:
   lineF(p, cpsStmts, "goto *$#[$#];$n", [tmp, a.rdLoc])
 
-  for i in 1 ..< caseStmt.len:
+  for i in 1..<caseStmt.len:
     startBlock(p)
-    let it = caseStmt.sons[i]
-    for j in 0 .. it.len-2:
-      if it.sons[j].kind == nkRange:
+    let it = caseStmt[i]
+    for j in 0..<it.len-1:
+      if it[j].kind == nkRange:
         localError(p.config, it.info, "range notation not available for computed goto")
         return
-      let val = getOrdValue(it.sons[j])
-      lineF(p, cpsStmts, "TMP$#_:$n", [intLiteral(val+id+1)])
+
+      let val = getOrdValue(it[j])
+      var lit = newRopeAppender()
+      intLiteral(toInt64(val)+id+1, lit)
+      lineF(p, cpsStmts, "TMP$#_:$n", [lit])
+
     genStmts(p, it.lastSon)
-    #for j in casePos+1 ..< n.len: genStmts(p, n.sons[j]) # tailB
-    #for j in 0 .. casePos-1: genStmts(p, n.sons[j])  # tailA
-    add(p.s(cpsStmts), tailB)
-    add(p.s(cpsStmts), tailA)
 
-    var a: TLoc
-    initLocExpr(p, caseStmt.sons[0], a)
+    for j in casePos+1..<n.len:
+      genStmts(p, n[j])
+
+    for j in 0..<casePos:
+      # prevent new local declarations
+      # compile declarations as assignments
+      let it = n[j]
+      if it.kind in {nkLetSection, nkVarSection}:
+        let asgn = copyNode(it)
+        asgn.transitionSonsKind(nkAsgn)
+        asgn.sons.setLen 2
+        for sym, value in it.fieldValuePairs:
+          if value.kind != nkEmpty:
+            asgn[0] = sym
+            asgn[1] = value
+            genStmts(p, asgn)
+      else:
+        genStmts(p, it)
+
+    var a: TLoc = initLocExpr(p, caseStmt[0])
     lineF(p, cpsStmts, "goto *$#[$#];$n", [tmp, a.rdLoc])
     endBlock(p)
 
+  for j in casePos+1..<n.len:
+    genStmts(p, n[j])
+
+
 proc genWhileStmt(p: BProc, t: PNode) =
   # we don't generate labels here as for example GCC would produce
   # significantly worse code
   var
     a: TLoc
-  assert(sonsLen(t) == 2)
+  assert(t.len == 2)
   inc(p.withinLoop)
   genLineDir(p, t)
 
   preserveBreakIdx:
-    p.breakIdx = startBlock(p, "while (1) {$n")
-    p.blocks[p.breakIdx].isLoop = true
-    initLocExpr(p, t.sons[0], a)
-    if (t.sons[0].kind != nkIntLit) or (t.sons[0].intVal == 0):
-      let label = assignLabel(p.blocks[p.breakIdx])
-      lineF(p, cpsStmts, "if (!$1) goto $2;$n", [rdLoc(a), label])
-    var loopBody = t.sons[1]
+    var loopBody = t[1]
     if loopBody.stmtsContainPragma(wComputedGoto) and
-        hasComputedGoto in CC[p.config.cCompiler].props:
-      # for closure support weird loop bodies are generated:
-      if loopBody.len == 2 and loopBody.sons[0].kind == nkEmpty:
-        loopBody = loopBody.sons[1]
+       hasComputedGoto in CC[p.config.cCompiler].props:
+         # for closure support weird loop bodies are generated:
+      if loopBody.len == 2 and loopBody[0].kind == nkEmpty:
+        loopBody = loopBody[1]
       genComputedGoto(p, loopBody)
     else:
+      p.breakIdx = startBlock(p, "while (1) {$n")
+      p.blocks[p.breakIdx].isLoop = true
+      a = initLocExpr(p, t[0])
+      if (t[0].kind != nkIntLit) or (t[0].intVal == 0):
+        lineF(p, cpsStmts, "if (!$1) goto ", [rdLoc(a)])
+        assignLabel(p.blocks[p.breakIdx], p.s(cpsStmts))
+        appcg(p, cpsStmts, ";$n", [])
       genStmts(p, loopBody)
 
-    if optProfiler in p.options:
-      # invoke at loop body exit:
-      linefmt(p, cpsStmts, "#nimProfile();$n")
-    endBlock(p)
+      if optProfiler in p.options:
+        # invoke at loop body exit:
+        linefmt(p, cpsStmts, "#nimProfile();$n", [])
+      endBlock(p)
 
   dec(p.withinLoop)
 
 proc genBlock(p: BProc, n: PNode, d: var TLoc) =
-  # bug #4505: allocate the temp in the outer scope
-  # so that it can escape the generated {}:
-  if not isEmptyType(n.typ) and d.k == locNone:
-    getTemp(p, n.typ, d)
+  if not isEmptyType(n.typ):
+    # bug #4505: allocate the temp in the outer scope
+    # so that it can escape the generated {}:
+    if d.k == locNone:
+      d = getTemp(p, n.typ)
+    d.flags.incl(lfEnforceDeref)
   preserveBreakIdx:
     p.breakIdx = startBlock(p)
-    if n.sons[0].kind != nkEmpty:
+    if n[0].kind != nkEmpty:
       # named block?
-      assert(n.sons[0].kind == nkSym)
-      var sym = n.sons[0].sym
+      assert(n[0].kind == nkSym)
+      var sym = n[0].sym
       sym.loc.k = locOther
       sym.position = p.breakIdx+1
-    expr(p, n.sons[1], d)
+    expr(p, n[1], d)
     endBlock(p)
 
 proc genParForStmt(p: BProc, t: PNode) =
-  assert(sonsLen(t) == 3)
+  assert(t.len == 3)
   inc(p.withinLoop)
   genLineDir(p, t)
 
   preserveBreakIdx:
-    let forLoopVar = t.sons[0].sym
-    var rangeA, rangeB: TLoc
-    assignLocalVar(p, t.sons[0])
+    let forLoopVar = t[0].sym
+    assignLocalVar(p, t[0])
     #initLoc(forLoopVar.loc, locLocalVar, forLoopVar.typ, onStack)
     #discard mangleName(forLoopVar)
-    let call = t.sons[1]
-    initLocExpr(p, call.sons[1], rangeA)
-    initLocExpr(p, call.sons[2], rangeB)
-
-    lineF(p, cpsStmts, "#pragma omp parallel for $4$n" &
-                        "for ($1 = $2; $1 <= $3; ++$1)",
-                        [forLoopVar.loc.rdLoc,
-                        rangeA.rdLoc, rangeB.rdLoc,
-                        call.sons[3].getStr.rope])
+    let call = t[1]
+    assert(call.len == 4 or call.len == 5)
+    var rangeA = initLocExpr(p, call[1])
+    var rangeB = initLocExpr(p, call[2])
+
+    # $n at the beginning because of #9710
+    if call.len == 4: # procName(a, b, annotation)
+      if call[0].sym.name.s == "||":  # `||`(a, b, annotation)
+        lineF(p, cpsStmts, "$n#pragma omp $4$n" &
+                            "for ($1 = $2; $1 <= $3; ++$1)",
+                            [forLoopVar.loc.rdLoc,
+                            rangeA.rdLoc, rangeB.rdLoc,
+                            call[3].getStr.rope])
+      else:
+        lineF(p, cpsStmts, "$n#pragma $4$n" &
+                    "for ($1 = $2; $1 <= $3; ++$1)",
+                    [forLoopVar.loc.rdLoc,
+                    rangeA.rdLoc, rangeB.rdLoc,
+                    call[3].getStr.rope])
+    else: # `||`(a, b, step, annotation)
+      var step: TLoc = initLocExpr(p, call[3])
+      lineF(p, cpsStmts, "$n#pragma omp $5$n" &
+                    "for ($1 = $2; $1 <= $3; $1 += $4)",
+                    [forLoopVar.loc.rdLoc,
+                    rangeA.rdLoc, rangeB.rdLoc, step.rdLoc,
+                    call[4].getStr.rope])
 
     p.breakIdx = startBlock(p)
     p.blocks[p.breakIdx].isLoop = true
-    genStmts(p, t.sons[2])
+    genStmts(p, t[2])
     endBlock(p)
 
   dec(p.withinLoop)
 
 proc genBreakStmt(p: BProc, t: PNode) =
   var idx = p.breakIdx
-  if t.sons[0].kind != nkEmpty:
+  if t[0].kind != nkEmpty:
     # named break?
-    assert(t.sons[0].kind == nkSym)
-    var sym = t.sons[0].sym
+    assert(t[0].kind == nkSym)
+    var sym = t[0].sym
     doAssert(sym.loc.k == locOther)
     idx = sym.position-1
   else:
@@ -552,54 +736,101 @@ proc genBreakStmt(p: BProc, t: PNode) =
     while idx >= 0 and not p.blocks[idx].isLoop: dec idx
     if idx < 0 or not p.blocks[idx].isLoop:
       internalError(p.config, t.info, "no loop to break")
-  let label = assignLabel(p.blocks[idx])
+  p.blocks[idx].label = "LA" & p.blocks[idx].id.rope
   blockLeaveActions(p,
     p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts,
     p.inExceptBlockLen - p.blocks[idx].nestedExceptStmts)
   genLineDir(p, t)
-  lineF(p, cpsStmts, "goto $1;$n", [label])
+  lineF(p, cpsStmts, "goto $1;$n", [p.blocks[idx].label])
+
+proc raiseExit(p: BProc) =
+  assert p.config.exc == excGoto
+  if nimErrorFlagDisabled notin p.flags:
+    p.flags.incl nimErrorFlagAccessed
+    if p.nestedTryStmts.len == 0:
+      p.flags.incl beforeRetNeeded
+      # easy case, simply goto 'ret':
+      lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto BeforeRet_;$n", [])
+    else:
+      lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto LA$1_;$n",
+        [p.nestedTryStmts[^1].label])
+
+proc raiseExitCleanup(p: BProc, destroy: string) =
+  assert p.config.exc == excGoto
+  if nimErrorFlagDisabled notin p.flags:
+    p.flags.incl nimErrorFlagAccessed
+    if p.nestedTryStmts.len == 0:
+      p.flags.incl beforeRetNeeded
+      # easy case, simply goto 'ret':
+      lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) {$1; goto BeforeRet_;}$n", [destroy])
+    else:
+      lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) {$2; goto LA$1_;}$n",
+        [p.nestedTryStmts[^1].label, destroy])
 
-proc genRaiseStmt(p: BProc, t: PNode) =
-  if p.module.compileToCpp:
-    discard cgsym(p.module, "popCurrentExceptionEx")
-  if p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept:
+proc finallyActions(p: BProc) =
+  if p.config.exc != excGoto and p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept:
     # if the current try stmt have a finally block,
     # we must execute it before reraising
-    var finallyBlock = p.nestedTryStmts[^1].n[^1]
-    if finallyBlock.kind == nkFinally:
+    let finallyBlock = p.nestedTryStmts[^1].fin
+    if finallyBlock != nil:
       genSimpleBlock(p, finallyBlock[0])
+
+proc raiseInstr(p: BProc; result: var Rope) =
+  if p.config.exc == excGoto:
+    let L = p.nestedTryStmts.len
+    if L == 0:
+      p.flags.incl beforeRetNeeded
+      # easy case, simply goto 'ret':
+      result.add ropecg(p.module, "goto BeforeRet_;$n", [])
+    else:
+      # raise inside an 'except' must go to the finally block,
+      # raise outside an 'except' block must go to the 'except' list.
+      result.add ropecg(p.module, "goto LA$1_;$n",
+        [p.nestedTryStmts[L-1].label])
+      # + ord(p.nestedTryStmts[L-1].inExcept)])
+
+proc genRaiseStmt(p: BProc, t: PNode) =
   if t[0].kind != nkEmpty:
-    var a: TLoc
-    initLocExprSingleUse(p, t[0], a)
+    var a: TLoc = initLocExprSingleUse(p, t[0])
+    finallyActions(p)
     var e = rdLoc(a)
+    discard getTypeDesc(p.module, t[0].typ)
     var typ = skipTypes(t[0].typ, abstractPtrs)
+    case p.config.exc
+    of excCpp:
+      blockLeaveActions(p, howManyTrys = 0, howManyExcepts = p.inExceptBlockLen)
+    of excGoto:
+      blockLeaveActions(p, howManyTrys = 0,
+        howManyExcepts = (if p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept: 1 else: 0))
+    else:
+      discard
     genLineDir(p, t)
     if isImportedException(typ, p.config):
       lineF(p, cpsStmts, "throw $1;$n", [e])
     else:
-      lineCg(p, cpsStmts, "#raiseException((#Exception*)$1, $2);$n",
-          [e, makeCString(typ.sym.name.s)])
+      lineCg(p, cpsStmts, "#raiseExceptionEx((#Exception*)$1, $2, $3, $4, $5);$n",
+          [e, makeCString(typ.sym.name.s),
+          makeCString(if p.prc != nil: p.prc.name.s else: p.module.module.name.s),
+          quotedFilename(p.config, t.info), toLinenumber(t.info)])
+      if optOwnedRefs in p.config.globalOptions:
+        lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [e])
   else:
+    finallyActions(p)
     genLineDir(p, t)
-    # reraise the last exception:
-    if p.module.compileToCpp and optNoCppExceptions notin p.config.globalOptions:
-      line(p, cpsStmts, ~"throw;$n")
-    else:
-      linefmt(p, cpsStmts, "#reraiseException();$n")
+    linefmt(p, cpsStmts, "#reraiseException();$n", [])
+  raiseInstr(p, p.s(cpsStmts))
 
-proc genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
+template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
                           rangeFormat, eqFormat: FormatStr, labl: TLabel) =
-  var
-    x, y: TLoc
-  var length = sonsLen(b)
-  for i in countup(0, length - 2):
-    if b.sons[i].kind == nkRange:
-      initLocExpr(p, b.sons[i].sons[0], x)
-      initLocExpr(p, b.sons[i].sons[1], y)
+  var x, y: TLoc
+  for i in 0..<b.len - 1:
+    if b[i].kind == nkRange:
+      x = initLocExpr(p, b[i][0])
+      y = initLocExpr(p, b[i][1])
       lineCg(p, cpsStmts, rangeFormat,
            [rdCharLoc(e), rdCharLoc(x), rdCharLoc(y), labl])
     else:
-      initLocExpr(p, b.sons[i], x)
+      x = initLocExpr(p, b[i])
       lineCg(p, cpsStmts, eqFormat, [rdCharLoc(e), rdCharLoc(x), labl])
 
 proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc,
@@ -609,23 +840,23 @@ proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc,
     # bug #4230: avoid false sharing between branches:
     if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
     lineF(p, cpsStmts, "LA$1_: ;$n", [rope(labId + i)])
-    if t.sons[i].kind == nkOfBranch:
-      var length = sonsLen(t.sons[i])
-      exprBlock(p, t.sons[i].sons[length - 1], d)
+    if t[i].kind == nkOfBranch:
+      exprBlock(p, t[i][^1], d)
       lineF(p, cpsStmts, "goto $1;$n", [lend])
     else:
-      exprBlock(p, t.sons[i].sons[0], d)
+      exprBlock(p, t[i][0], d)
   result = lend
 
-proc genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc,
+template genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc,
                        rangeFormat, eqFormat: FormatStr,
                        until: int, a: TLoc): TLabel =
   # generate a C-if statement for a Nim case statement
+  var res: TLabel
   var labId = p.labels
   for i in 1..until:
     inc(p.labels)
-    if t.sons[i].kind == nkOfBranch: # else statement
-      genCaseGenericBranch(p, t.sons[i], a, rangeFormat, eqFormat,
+    if t[i].kind == nkOfBranch: # else statement
+      genCaseGenericBranch(p, t[i], a, rangeFormat, eqFormat,
                            "LA" & rope(p.labels) & "_")
     else:
       lineF(p, cpsStmts, "goto LA$1_;$n", [rope(p.labels)])
@@ -633,75 +864,94 @@ proc genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc,
     inc(p.labels)
     var gotoTarget = p.labels
     lineF(p, cpsStmts, "goto LA$1_;$n", [rope(gotoTarget)])
-    result = genCaseSecondPass(p, t, d, labId, until)
+    res = genCaseSecondPass(p, t, d, labId, until)
     lineF(p, cpsStmts, "LA$1_: ;$n", [rope(gotoTarget)])
   else:
-    result = genCaseSecondPass(p, t, d, labId, until)
+    res = genCaseSecondPass(p, t, d, labId, until)
+  res
 
-proc genCaseGeneric(p: BProc, t: PNode, d: var TLoc,
+template genCaseGeneric(p: BProc, t: PNode, d: var TLoc,
                     rangeFormat, eqFormat: FormatStr) =
-  var a: TLoc
-  initLocExpr(p, t.sons[0], a)
-  var lend = genIfForCaseUntil(p, t, d, rangeFormat, eqFormat, sonsLen(t)-1, a)
+  var a: TLoc = initLocExpr(p, t[0])
+  var lend = genIfForCaseUntil(p, t, d, rangeFormat, eqFormat, t.len-1, a)
   fixLabel(p, lend)
 
 proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
+                         stringKind: TTypeKind,
                          branches: var openArray[Rope]) =
   var x: TLoc
-  var length = sonsLen(b)
-  for i in countup(0, length - 2):
-    assert(b.sons[i].kind != nkRange)
-    initLocExpr(p, b.sons[i], x)
-    assert(b.sons[i].kind in {nkStrLit..nkTripleStrLit})
-    var j = int(hashString(p.config, b.sons[i].strVal) and high(branches))
-    appcg(p.module, branches[j], "if (#eqStrings($1, $2)) goto $3;$n",
+  for i in 0..<b.len - 1:
+    assert(b[i].kind != nkRange)
+    x = initLocExpr(p, b[i])
+    var j: int = 0
+    case b[i].kind
+    of nkStrLit..nkTripleStrLit:
+      j = int(hashString(p.config, b[i].strVal) and high(branches))
+    of nkNilLit: j = 0
+    else:
+      assert false, "invalid string case branch node kind"
+    if stringKind == tyCstring:
+      appcg(p.module, branches[j], "if (#eqCstrings($1, $2)) goto $3;$n",
+         [rdLoc(e), rdLoc(x), labl])
+    else:
+      appcg(p.module, branches[j], "if (#eqStrings($1, $2)) goto $3;$n",
          [rdLoc(e), rdLoc(x), labl])
 
-proc genStringCase(p: BProc, t: PNode, d: var TLoc) =
+proc genStringCase(p: BProc, t: PNode, stringKind: TTypeKind, d: var TLoc) =
   # count how many constant strings there are in the case:
   var strings = 0
-  for i in countup(1, sonsLen(t) - 1):
-    if t.sons[i].kind == nkOfBranch: inc(strings, sonsLen(t.sons[i]) - 1)
+  for i in 1..<t.len:
+    if t[i].kind == nkOfBranch: inc(strings, t[i].len - 1)
   if strings > stringCaseThreshold:
     var bitMask = math.nextPowerOfTwo(strings) - 1
     var branches: seq[Rope]
     newSeq(branches, bitMask + 1)
-    var a: TLoc
-    initLocExpr(p, t.sons[0], a) # fist pass: gnerate ifs+goto:
+    var a: TLoc = initLocExpr(p, t[0]) # first pass: generate ifs+goto:
     var labId = p.labels
-    for i in countup(1, sonsLen(t) - 1):
+    for i in 1..<t.len:
       inc(p.labels)
-      if t.sons[i].kind == nkOfBranch:
-        genCaseStringBranch(p, t.sons[i], a, "LA" & rope(p.labels) & "_",
-                            branches)
+      if t[i].kind == nkOfBranch:
+        genCaseStringBranch(p, t[i], a, "LA" & rope(p.labels) & "_",
+                            stringKind, branches)
       else:
         # else statement: nothing to do yet
         # but we reserved a label, which we use later
         discard
-    linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n",
-            rdLoc(a), rope(bitMask))
-    for j in countup(0, high(branches)):
-      if branches[j] != nil:
+    if stringKind == tyCstring:
+      linefmt(p, cpsStmts, "switch (#hashCstring($1) & $2) {$n",
+              [rdLoc(a), bitMask])
+    else:
+      linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n",
+              [rdLoc(a), bitMask])
+    for j in 0..high(branches):
+      if branches[j] != "":
+        var lit = newRopeAppender()
+        intLiteral(j, lit)
         lineF(p, cpsStmts, "case $1: $n$2break;$n",
-             [intLiteral(j), branches[j]])
+             [lit, branches[j]])
     lineF(p, cpsStmts, "}$n", []) # else statement:
-    if t.sons[sonsLen(t)-1].kind != nkOfBranch:
+    if t[^1].kind != nkOfBranch:
       lineF(p, cpsStmts, "goto LA$1_;$n", [rope(p.labels)])
     # third pass: generate statements
-    var lend = genCaseSecondPass(p, t, d, labId, sonsLen(t)-1)
+    var lend = genCaseSecondPass(p, t, d, labId, t.len-1)
     fixLabel(p, lend)
   else:
-    genCaseGeneric(p, t, d, "", "if (#eqStrings($1, $2)) goto $3;$n")
+    if stringKind == tyCstring:
+      genCaseGeneric(p, t, d, "", "if (#eqCstrings($1, $2)) goto $3;$n")
+    else:
+      genCaseGeneric(p, t, d, "", "if (#eqStrings($1, $2)) goto $3;$n")
 
 proc branchHasTooBigRange(b: PNode): bool =
-  for i in countup(0, sonsLen(b)-2):
+  result = false
+  for it in b:
     # last son is block
-    if (b.sons[i].kind == nkRange) and
-        b.sons[i].sons[1].intVal - b.sons[i].sons[0].intVal > RangeExpandLimit:
+    if (it.kind == nkRange) and
+        it[1].intVal - it[0].intVal > RangeExpandLimit:
       return true
 
 proc ifSwitchSplitPoint(p: BProc, n: PNode): int =
-  for i in 1..n.len-1:
+  result = 0
+  for i in 1..<n.len:
     var branch = n[i]
     var stmtBlock = lastSon(branch)
     if stmtBlock.stmtsContainPragma(wLinearScanEnd):
@@ -711,38 +961,42 @@ proc ifSwitchSplitPoint(p: BProc, n: PNode): int =
         result = i
 
 proc genCaseRange(p: BProc, branch: PNode) =
-  var length = branch.len
-  for j in 0 .. length-2:
+  for j in 0..<branch.len-1:
     if branch[j].kind == nkRange:
       if hasSwitchRange in CC[p.config.cCompiler].props:
-        lineF(p, cpsStmts, "case $1 ... $2:$n", [
-            genLiteral(p, branch[j][0]),
-            genLiteral(p, branch[j][1])])
+        var litA = newRopeAppender()
+        var litB = newRopeAppender()
+        genLiteral(p, branch[j][0], litA)
+        genLiteral(p, branch[j][1], litB)
+        lineF(p, cpsStmts, "case $1 ... $2:$n", [litA, litB])
       else:
         var v = copyNode(branch[j][0])
         while v.intVal <= branch[j][1].intVal:
-          lineF(p, cpsStmts, "case $1:$n", [genLiteral(p, v)])
+          var litA = newRopeAppender()
+          genLiteral(p, v, litA)
+          lineF(p, cpsStmts, "case $1:$n", [litA])
           inc(v.intVal)
     else:
-      lineF(p, cpsStmts, "case $1:$n", [genLiteral(p, branch[j])])
+      var litA = newRopeAppender()
+      genLiteral(p, branch[j], litA)
+      lineF(p, cpsStmts, "case $1:$n", [litA])
 
 proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
   # analyse 'case' statement:
   var splitPoint = ifSwitchSplitPoint(p, n)
 
   # generate if part (might be empty):
-  var a: TLoc
-  initLocExpr(p, n.sons[0], a)
+  var a: TLoc = initLocExpr(p, n[0])
   var lend = if splitPoint > 0: genIfForCaseUntil(p, n, d,
                     rangeFormat = "if ($1 >= $2 && $1 <= $3) goto $4;$n",
                     eqFormat = "if ($1 == $2) goto $3;$n",
-                    splitPoint, a) else: nil
+                    splitPoint, a) else: ""
 
   # generate switch part (might be empty):
   if splitPoint+1 < n.len:
     lineF(p, cpsStmts, "switch ($1) {$n", [rdCharLoc(a)])
     var hasDefault = false
-    for i in splitPoint+1 ..< n.len:
+    for i in splitPoint+1..<n.len:
       # bug #4230: avoid false sharing between branches:
       if d.k == locTemp and isEmptyType(n.typ): d.k = locNone
       var branch = n[i]
@@ -754,36 +1008,212 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
         hasDefault = true
       exprBlock(p, branch.lastSon, d)
       lineF(p, cpsStmts, "break;$n", [])
-    if (hasAssume in CC[p.config.cCompiler].props) and not hasDefault:
-      lineF(p, cpsStmts, "default: __assume(0);$n", [])
+    if not hasDefault:
+      if hasBuiltinUnreachable in CC[p.config.cCompiler].props:
+        lineF(p, cpsStmts, "default: __builtin_unreachable();$n", [])
+      elif hasAssume in CC[p.config.cCompiler].props:
+        lineF(p, cpsStmts, "default: __assume(0);$n", [])
     lineF(p, cpsStmts, "}$n", [])
-  if lend != nil: fixLabel(p, lend)
+  if lend != "": fixLabel(p, lend)
 
 proc genCase(p: BProc, t: PNode, d: var TLoc) =
   genLineDir(p, t)
   if not isEmptyType(t.typ) and d.k == locNone:
-    getTemp(p, t.typ, d)
-  case skipTypes(t.sons[0].typ, abstractVarRange).kind
+    d = getTemp(p, t.typ)
+  case skipTypes(t[0].typ, abstractVarRange).kind
   of tyString:
-    genStringCase(p, t, d)
+    genStringCase(p, t, tyString, d)
+  of tyCstring:
+    genStringCase(p, t, tyCstring, d)
   of tyFloat..tyFloat128:
     genCaseGeneric(p, t, d, "if ($1 >= $2 && $1 <= $3) goto $4;$n",
                             "if ($1 == $2) goto $3;$n")
   else:
-    if t.sons[0].kind == nkSym and sfGoto in t.sons[0].sym.flags:
+    if t[0].kind == nkSym and sfGoto in t[0].sym.flags:
       genGotoForCase(p, t)
     else:
       genOrdinalCase(p, t, d)
 
 proc genRestoreFrameAfterException(p: BProc) =
   if optStackTrace in p.module.config.options:
-    if not p.hasCurFramePointer:
-      p.hasCurFramePointer = true
+    if hasCurFramePointer notin p.flags:
+      p.flags.incl hasCurFramePointer
       p.procSec(cpsLocals).add(ropecg(p.module, "\tTFrame* _nimCurFrame;$n", []))
       p.procSec(cpsInit).add(ropecg(p.module, "\t_nimCurFrame = #getFrame();$n", []))
-    linefmt(p, cpsStmts, "#setFrame(_nimCurFrame);$n")
+    linefmt(p, cpsStmts, "#setFrame(_nimCurFrame);$n", [])
 
 proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
+  #[ code to generate:
+
+    std::exception_ptr error;
+    try {
+      body;
+    } catch (Exception e) {
+      error = std::current_exception();
+      if (ofExpr(e, TypeHere)) {
+
+        error = nullptr; // handled
+      } else if (...) {
+
+      } else {
+        throw;
+      }
+    } catch(...) {
+      // C++ exception occured, not under Nim's control.
+    }
+    {
+      /* finally: */
+      printf('fin!\n');
+      if (error) std::rethrow_exception(error); // re-raise the exception
+    }
+  ]#
+  p.module.includeHeader("<exception>")
+
+  if not isEmptyType(t.typ) and d.k == locNone:
+    d = getTemp(p, t.typ)
+  genLineDir(p, t)
+
+  inc(p.labels, 2)
+  let etmp = p.labels
+  #init on locals, fixes #23306
+  lineCg(p, cpsLocals, "std::exception_ptr T$1_;$n", [etmp])
+
+  let fin = if t[^1].kind == nkFinally: t[^1] else: nil
+  p.nestedTryStmts.add((fin, false, 0.Natural))
+
+  if t.kind == nkHiddenTryStmt:
+    lineCg(p, cpsStmts, "try {$n", [])
+    expr(p, t[0], d)
+    lineCg(p, cpsStmts, "}$n", [])
+  else:
+    startBlock(p, "try {$n")
+    expr(p, t[0], d)
+    endBlock(p)
+
+  # First pass: handle Nim based exceptions:
+  lineCg(p, cpsStmts, "catch (#Exception* T$1_) {$n", [etmp+1])
+  genRestoreFrameAfterException(p)
+  # an unhandled exception happened!
+  lineCg(p, cpsStmts, "T$1_ = std::current_exception();$n", [etmp])
+  p.nestedTryStmts[^1].inExcept = true
+  var hasImportedCppExceptions = false
+  var i = 1
+  var hasIf = false
+  var hasElse = false
+  while (i < t.len) and (t[i].kind == nkExceptBranch):
+    # bug #4230: avoid false sharing between branches:
+    if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
+    if t[i].len == 1:
+      hasImportedCppExceptions = true
+      # general except section:
+      hasElse = true
+      if hasIf: lineF(p, cpsStmts, "else ", [])
+      startBlock(p)
+      # we handled the error:
+      expr(p, t[i][0], d)
+      linefmt(p, cpsStmts, "#popCurrentException();$n", [])
+      endBlock(p)
+    else:
+      var orExpr = newRopeAppender()
+      var exvar = PNode(nil)
+      for j in 0..<t[i].len - 1:
+        var typeNode = t[i][j]
+        if t[i][j].isInfixAs():
+          typeNode = t[i][j][1]
+          exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
+        assert(typeNode.kind == nkType)
+        if isImportedException(typeNode.typ, p.config):
+          hasImportedCppExceptions = true
+        else:
+          if orExpr.len != 0: orExpr.add("||")
+          let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
+          if optTinyRtti in p.config.globalOptions:
+            let checkFor = $getObjDepth(typeNode.typ)
+            appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(typeNode.typ, p.config)))])
+          else:
+            let checkFor = genTypeInfoV1(p.module, typeNode.typ, typeNode.info)
+            appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
+
+      if orExpr.len != 0:
+        if hasIf:
+          startBlock(p, "else if ($1) {$n", [orExpr])
+        else:
+          startBlock(p, "if ($1) {$n", [orExpr])
+          hasIf = true
+        if exvar != nil:
+          fillLocalName(p, exvar.sym)
+          fillLoc(exvar.sym.loc, locTemp, exvar, OnStack)
+          linefmt(p, cpsStmts, "$1 $2 = T$3_;$n", [getTypeDesc(p.module, exvar.sym.typ),
+            rdLoc(exvar.sym.loc), rope(etmp+1)])
+        # we handled the error:
+        linefmt(p, cpsStmts, "T$1_ = nullptr;$n", [etmp])
+        expr(p, t[i][^1], d)
+        linefmt(p, cpsStmts, "#popCurrentException();$n", [])
+        endBlock(p)
+    inc(i)
+  if hasIf and not hasElse:
+    linefmt(p, cpsStmts, "else throw;$n", [etmp])
+  linefmt(p, cpsStmts, "}$n", [])
+
+  # Second pass: handle C++ based exceptions:
+  template genExceptBranchBody(body: PNode) {.dirty.} =
+    genRestoreFrameAfterException(p)
+    #linefmt(p, cpsStmts, "T$1_ = std::current_exception();$n", [etmp])
+    expr(p, body, d)
+
+  var catchAllPresent = false
+  incl p.flags, noSafePoints # mark as not needing 'popCurrentException'
+  if hasImportedCppExceptions:
+    for i in 1..<t.len:
+      if t[i].kind != nkExceptBranch: break
+
+      # bug #4230: avoid false sharing between branches:
+      if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
+
+      if t[i].len == 1:
+        # general except section:
+        startBlock(p, "catch (...) {$n", [])
+        genExceptBranchBody(t[i][0])
+        endBlock(p)
+        catchAllPresent = true
+      else:
+        for j in 0..<t[i].len-1:
+          var typeNode = t[i][j]
+          if t[i][j].isInfixAs():
+            typeNode = t[i][j][1]
+            if isImportedException(typeNode.typ, p.config):
+              let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
+              fillLocalName(p, exvar.sym)
+              fillLoc(exvar.sym.loc, locTemp, exvar, OnStack)
+              startBlock(p, "catch ($1& $2) {$n", getTypeDesc(p.module, typeNode.typ), rdLoc(exvar.sym.loc))
+              genExceptBranchBody(t[i][^1])  # exception handler body will duplicated for every type
+              endBlock(p)
+          elif isImportedException(typeNode.typ, p.config):
+            startBlock(p, "catch ($1&) {$n", getTypeDesc(p.module, t[i][j].typ))
+            genExceptBranchBody(t[i][^1])  # exception handler body will duplicated for every type
+            endBlock(p)
+
+  excl p.flags, noSafePoints
+  discard pop(p.nestedTryStmts)
+  # general finally block:
+  if t.len > 0 and t[^1].kind == nkFinally:
+    if not catchAllPresent:
+      startBlock(p, "catch (...) {$n", [])
+      genRestoreFrameAfterException(p)
+      linefmt(p, cpsStmts, "T$1_ = std::current_exception();$n", [etmp])
+      endBlock(p)
+
+    startBlock(p)
+    genStmts(p, t[^1][0])
+    linefmt(p, cpsStmts, "if (T$1_) std::rethrow_exception(T$1_);$n", [etmp])
+    endBlock(p)
+
+proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) =
+  # There are two versions we generate, depending on whether we
+  # catch C++ exceptions, imported via .importcpp or not. The
+  # code can be easier if there are no imported C++ exceptions
+  # to deal with.
+
   # code to generate:
   #
   #   try
@@ -805,10 +1235,11 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
     expr(p, body, d)
 
   if not isEmptyType(t.typ) and d.k == locNone:
-    getTemp(p, t.typ, d)
+    d = getTemp(p, t.typ)
   genLineDir(p, t)
-  discard cgsym(p.module, "popCurrentExceptionEx")
-  add(p.nestedTryStmts, (t, false))
+  cgsym(p.module, "popCurrentExceptionEx")
+  let fin = if t[^1].kind == nkFinally: t[^1] else: nil
+  p.nestedTryStmts.add((fin, false, 0.Natural))
   startBlock(p, "try {$n")
   expr(p, t[0], d)
   endBlock(p)
@@ -829,10 +1260,11 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
       genExceptBranchBody(t[i][0])
       endBlock(p)
     else:
-      for j in 0..t[i].len-2:
+      for j in 0..<t[i].len-1:
         if t[i][j].isInfixAs():
           let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
-          fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), OnUnknown)
+          fillLocalName(p, exvar.sym)
+          fillLoc(exvar.sym.loc, locTemp, exvar, OnUnknown)
           startBlock(p, "catch ($1& $2) {$n", getTypeDesc(p.module, t[i][j][1].typ), rdLoc(exvar.sym.loc))
         else:
           startBlock(p, "catch ($1&) {$n", getTypeDesc(p.module, t[i][j].typ))
@@ -841,17 +1273,124 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
 
   discard pop(p.nestedTryStmts)
 
-  if not catchAllPresent and t[^1].kind == nkFinally:
-    # finally requires catch all presence
-    startBlock(p, "catch (...) {$n")
+  if t[^1].kind == nkFinally:
+    # c++ does not have finally, therefore code needs to be generated twice
+    if not catchAllPresent:
+      # finally requires catch all presence
+      startBlock(p, "catch (...) {$n")
+      genStmts(p, t[^1][0])
+      line(p, cpsStmts, "throw;\n")
+      endBlock(p)
+
     genSimpleBlock(p, t[^1][0])
-    line(p, cpsStmts, ~"throw;$n")
+
+proc bodyCanRaise(p: BProc; n: PNode): bool =
+  case n.kind
+  of nkCallKinds:
+    result = canRaiseDisp(p, n[0])
+    if not result:
+      # also check the arguments:
+      for i in 1 ..< n.len:
+        if bodyCanRaise(p, n[i]): return true
+  of nkRaiseStmt:
+    result = true
+  of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
+      nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
+    result = false
+  else:
+    for i in 0 ..< safeLen(n):
+      if bodyCanRaise(p, n[i]): return true
+    result = false
+
+proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
+  let fin = if t[^1].kind == nkFinally: t[^1] else: nil
+  inc p.labels
+  let lab = p.labels
+  let hasExcept = t[1].kind == nkExceptBranch
+  if hasExcept: inc p.withinTryWithExcept
+  p.nestedTryStmts.add((fin, false, Natural lab))
+
+  p.flags.incl nimErrorFlagAccessed
+
+  if not isEmptyType(t.typ) and d.k == locNone:
+    d = getTemp(p, t.typ)
+
+  expr(p, t[0], d)
+
+  if 1 < t.len and t[1].kind == nkExceptBranch:
+    startBlock(p, "if (NIM_UNLIKELY(*nimErr_)) {$n")
+  else:
+    startBlock(p)
+  linefmt(p, cpsStmts, "LA$1_:;$n", [lab])
+
+  p.nestedTryStmts[^1].inExcept = true
+  var i = 1
+  while (i < t.len) and (t[i].kind == nkExceptBranch):
+
+    inc p.labels
+    let nextExcept = p.labels
+    p.nestedTryStmts[^1].label = nextExcept
+
+    # bug #4230: avoid false sharing between branches:
+    if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
+    if t[i].len == 1:
+      # general except section:
+      if i > 1: lineF(p, cpsStmts, "else", [])
+      startBlock(p)
+      # we handled the exception, remember this:
+      linefmt(p, cpsStmts, "*nimErr_ = NIM_FALSE;$n", [])
+      expr(p, t[i][0], d)
+    else:
+      var orExpr = newRopeAppender()
+      for j in 0..<t[i].len - 1:
+        assert(t[i][j].kind == nkType)
+        if orExpr.len != 0: orExpr.add("||")
+        let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
+        if optTinyRtti in p.config.globalOptions:
+          let checkFor = $getObjDepth(t[i][j].typ)
+          appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)",
+            [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))])
+        else:
+          let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
+          appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
+
+      if i > 1: line(p, cpsStmts, "else ")
+      startBlock(p, "if ($1) {$n", [orExpr])
+      # we handled the exception, remember this:
+      linefmt(p, cpsStmts, "*nimErr_ = NIM_FALSE;$n", [])
+      expr(p, t[i][^1], d)
+
+    linefmt(p, cpsStmts, "#popCurrentException();$n", [])
+    linefmt(p, cpsStmts, "LA$1_:;$n", [nextExcept])
     endBlock(p)
 
-  if t[^1].kind == nkFinally:
-    genSimpleBlock(p, t[^1][0])
+    inc(i)
+  discard pop(p.nestedTryStmts)
+  endBlock(p)
 
-proc genTry(p: BProc, t: PNode, d: var TLoc) =
+  if i < t.len and t[i].kind == nkFinally:
+    startBlock(p)
+    if not bodyCanRaise(p, t[i][0]):
+      # this is an important optimization; most destroy blocks are detected not to raise an
+      # exception and so we help the C optimizer by not mutating nimErr_ pointlessly:
+      genStmts(p, t[i][0])
+    else:
+      # pretend we did handle the error for the safe execution of the 'finally' section:
+      p.procSec(cpsLocals).add(ropecg(p.module, "NIM_BOOL oldNimErrFin$1_;$n", [lab]))
+      linefmt(p, cpsStmts, "oldNimErrFin$1_ = *nimErr_; *nimErr_ = NIM_FALSE;$n", [lab])
+      genStmts(p, t[i][0])
+      # this is correct for all these cases:
+      # 1. finally is run during ordinary control flow
+      # 2. finally is run after 'except' block handling: these however set the
+      #    error back to nil.
+      # 3. finally is run for exception handling code without any 'except'
+      #    handler present or only handlers that did not match.
+      linefmt(p, cpsStmts, "*nimErr_ = oldNimErrFin$1_;$n", [lab])
+    endBlock(p)
+  raiseExit(p)
+  if hasExcept: inc p.withinTryWithExcept
+
+proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
   # code to generate:
   #
   # XXX: There should be a standard dispatch algorithm
@@ -881,98 +1420,137 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
   #    propagateCurrentException();
   #
   if not isEmptyType(t.typ) and d.k == locNone:
-    getTemp(p, t.typ, d)
-  p.module.includeHeader("<setjmp.h>")
+    d = getTemp(p, t.typ)
+  let quirkyExceptions = p.config.exc == excQuirky or
+      (t.kind == nkHiddenTryStmt and sfSystemModule in p.module.module.flags)
+  if not quirkyExceptions:
+    p.module.includeHeader("<setjmp.h>")
+  else:
+    p.flags.incl noSafePoints
   genLineDir(p, t)
-  var safePoint = getTempName(p.module)
-  discard cgsym(p.module, "Exception")
-  linefmt(p, cpsLocals, "#TSafePoint $1;$n", safePoint)
-  linefmt(p, cpsStmts, "#pushSafePoint(&$1);$n", safePoint)
-  if isDefined(p.config, "nimStdSetjmp"):
-    linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", safePoint)
-  elif isDefined(p.config, "nimSigSetjmp"):
-    linefmt(p, cpsStmts, "$1.status = sigsetjmp($1.context, 0);$n", safePoint)
-  elif isDefined(p.config, "nimRawSetjmp"):
-    linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", safePoint)
+  cgsym(p.module, "Exception")
+  var safePoint: Rope = ""
+  if not quirkyExceptions:
+    safePoint = getTempName(p.module)
+    linefmt(p, cpsLocals, "#TSafePoint $1;$n", [safePoint])
+    linefmt(p, cpsStmts, "#pushSafePoint(&$1);$n", [safePoint])
+    if isDefined(p.config, "nimStdSetjmp"):
+      linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
+    elif isDefined(p.config, "nimSigSetjmp"):
+      linefmt(p, cpsStmts, "$1.status = sigsetjmp($1.context, 0);$n", [safePoint])
+    elif isDefined(p.config, "nimBuiltinSetjmp"):
+      linefmt(p, cpsStmts, "$1.status = __builtin_setjmp($1.context);$n", [safePoint])
+    elif isDefined(p.config, "nimRawSetjmp"):
+      if isDefined(p.config, "mswindows"):
+        if isDefined(p.config, "vcc") or isDefined(p.config, "clangcl"):
+          # For the vcc compiler, use `setjmp()` with one argument.
+          # See https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setjmp?view=msvc-170
+          linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
+        else:
+          # The Windows `_setjmp()` takes two arguments, with the second being an
+          # undocumented buffer used by the SEH mechanism for stack unwinding.
+          # Mingw-w64 has been trying to get it right for years, but it's still
+          # prone to stack corruption during unwinding, so we disable that by setting
+          # it to NULL.
+          # More details: https://github.com/status-im/nimbus-eth2/issues/3121
+          linefmt(p, cpsStmts, "$1.status = _setjmp($1.context, 0);$n", [safePoint])
+      else:
+        linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", [safePoint])
+    else:
+      linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
+    lineCg(p, cpsStmts, "if ($1.status == 0) {$n", [safePoint])
+  let fin = if t[^1].kind == nkFinally: t[^1] else: nil
+  p.nestedTryStmts.add((fin, quirkyExceptions, 0.Natural))
+  expr(p, t[0], d)
+  if not quirkyExceptions:
+    linefmt(p, cpsStmts, "#popSafePoint();$n", [])
+    lineCg(p, cpsStmts, "}$n", [])
+    startBlock(p, "else {$n")
+    linefmt(p, cpsStmts, "#popSafePoint();$n", [])
+    genRestoreFrameAfterException(p)
+  elif 1 < t.len and t[1].kind == nkExceptBranch:
+    startBlock(p, "if (#nimBorrowCurrentException()) {$n")
   else:
-    linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", safePoint)
-  startBlock(p, "if ($1.status == 0) {$n", [safePoint])
-  var length = sonsLen(t)
-  add(p.nestedTryStmts, (t, false))
-  expr(p, t.sons[0], d)
-  linefmt(p, cpsStmts, "#popSafePoint();$n")
-  endBlock(p)
-  startBlock(p, "else {$n")
-  linefmt(p, cpsStmts, "#popSafePoint();$n")
-  genRestoreFrameAfterException(p)
+    startBlock(p)
   p.nestedTryStmts[^1].inExcept = true
   var i = 1
-  while (i < length) and (t.sons[i].kind == nkExceptBranch):
+  while (i < t.len) and (t[i].kind == nkExceptBranch):
     # bug #4230: avoid false sharing between branches:
     if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
-    var blen = sonsLen(t.sons[i])
-    if blen == 1:
+    if t[i].len == 1:
       # general except section:
       if i > 1: lineF(p, cpsStmts, "else", [])
       startBlock(p)
-      linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint)
-      expr(p, t.sons[i].sons[0], d)
-      linefmt(p, cpsStmts, "#popCurrentException();$n")
+      if not quirkyExceptions:
+        linefmt(p, cpsStmts, "$1.status = 0;$n", [safePoint])
+      expr(p, t[i][0], d)
+      linefmt(p, cpsStmts, "#popCurrentException();$n", [])
       endBlock(p)
     else:
-      var orExpr: Rope = nil
-      for j in countup(0, blen - 2):
-        assert(t.sons[i].sons[j].kind == nkType)
-        if orExpr != nil: add(orExpr, "||")
-        let isObjFormat = if not p.module.compileToCpp:
-          "#isObj(#getCurrentException()->Sup.m_type, $1)"
-          else: "#isObj(#getCurrentException()->m_type, $1)"
-        appcg(p.module, orExpr, isObjFormat,
-              [genTypeInfo(p.module, t[i][j].typ, t[i][j].info)])
+      var orExpr = newRopeAppender()
+      for j in 0..<t[i].len - 1:
+        assert(t[i][j].kind == nkType)
+        if orExpr.len != 0: orExpr.add("||")
+        let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
+        if optTinyRtti in p.config.globalOptions:
+          let checkFor = $getObjDepth(t[i][j].typ)
+          appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)",
+              [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))])
+        else:
+          let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
+          appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
+
       if i > 1: line(p, cpsStmts, "else ")
       startBlock(p, "if ($1) {$n", [orExpr])
-      linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint)
-      expr(p, t.sons[i].sons[blen-1], d)
-      linefmt(p, cpsStmts, "#popCurrentException();$n")
+      if not quirkyExceptions:
+        linefmt(p, cpsStmts, "$1.status = 0;$n", [safePoint])
+      expr(p, t[i][^1], d)
+      linefmt(p, cpsStmts, "#popCurrentException();$n", [])
       endBlock(p)
     inc(i)
   discard pop(p.nestedTryStmts)
   endBlock(p) # end of else block
-  if i < length and t.sons[i].kind == nkFinally:
+  if i < t.len and t[i].kind == nkFinally:
     p.finallySafePoints.add(safePoint)
-    genSimpleBlock(p, t.sons[i].sons[0])
+    startBlock(p)
+    genStmts(p, t[i][0])
+    # pretend we handled the exception in a 'finally' so that we don't
+    # re-raise the unhandled one but instead keep the old one (it was
+    # not popped either):
+    if not quirkyExceptions and getCompilerProc(p.module.g.graph, "nimLeaveFinally") != nil:
+      linefmt(p, cpsStmts, "if ($1.status != 0) #nimLeaveFinally();$n", [safePoint])
+    endBlock(p)
     discard pop(p.finallySafePoints)
-  linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint)
+  if not quirkyExceptions:
+    linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", [safePoint])
 
-proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
+proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) =
   var res = ""
-  for it in t.sons:
+  let offset =
+    if isAsmStmt: 1 # first son is pragmas
+    else: 0
+
+  for i in offset..<t.len:
+    let it = t[i]
     case it.kind
     of nkStrLit..nkTripleStrLit:
       res.add(it.strVal)
     of nkSym:
       var sym = it.sym
       if sym.kind in {skProc, skFunc, skIterator, skMethod}:
-        var a: TLoc
-        initLocExpr(p, it, a)
+        var a: TLoc = initLocExpr(p, it)
         res.add($rdLoc(a))
       elif sym.kind == skType:
         res.add($getTypeDesc(p.module, sym.typ))
       else:
         discard getTypeDesc(p.module, skipTypes(sym.typ, abstractPtrs))
-        var r = sym.loc.r
-        if r == nil:
-          # if no name has already been given,
-          # it doesn't matter much:
-          r = mangleName(p.module, sym)
-          sym.loc.r = r       # but be consequent!
-        res.add($r)
+        fillBackendName(p.module, sym)
+        res.add($sym.loc.snippet)
     of nkTypeOfExpr:
       res.add($getTypeDesc(p.module, it.typ))
     else:
       discard getTypeDesc(p.module, skipTypes(it.typ, abstractPtrs))
-      var a: TLoc
-      initLocExpr(p, it, a)
+      var a: TLoc = initLocExpr(p, it)
       res.add($a.rdLoc)
 
   if isAsmStmt and hasGnuAsm in CC[p.config.cCompiler].props:
@@ -983,166 +1561,150 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
         if x[j] in {'"', ':'}:
           # don't modify the line if already in quotes or
           # some clobber register list:
-          add(result, x); add(result, "\L")
+          result.add(x); result.add("\L")
         else:
           # ignore empty lines
-          add(result, "\"")
-          add(result, x)
-          add(result, "\\n\"\n")
+          result.add("\"")
+          result.add(x.replace("\"", "\\\""))
+          result.add("\\n\"\n")
   else:
     res.add("\L")
-    result = res.rope
+    result.add res.rope
 
 proc genAsmStmt(p: BProc, t: PNode) =
   assert(t.kind == nkAsmStmt)
   genLineDir(p, t)
-  var s = genAsmOrEmitStmt(p, t, isAsmStmt=true)
+  var s = newRopeAppender()
+
+  var asmSyntax = ""
+  if (let p = t[0]; p.kind == nkPragma):
+    for i in p:
+      if whichPragma(i) == wAsmSyntax:
+        asmSyntax = i[1].strVal
+
+  if asmSyntax != "" and
+     not (
+      asmSyntax == "gcc" and hasGnuAsm in CC[p.config.cCompiler].props or
+      asmSyntax == "vcc" and hasGnuAsm notin CC[p.config.cCompiler].props):
+    localError(
+      p.config, t.info,
+      "Your compiler does not support the specified inline assembler")
+
+  genAsmOrEmitStmt(p, t, isAsmStmt=true, s)
   # see bug #2362, "top level asm statements" seem to be a mis-feature
   # but even if we don't do this, the example in #2362 cannot possibly
   # work:
   if p.prc == nil:
     # top level asm statement?
-    addf(p.module.s[cfsProcHeaders], CC[p.config.cCompiler].asmStmtFrmt, [s])
+    p.module.s[cfsProcHeaders].add runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s])
   else:
-    lineF(p, cpsStmts, CC[p.config.cCompiler].asmStmtFrmt, [s])
+    addIndent p, p.s(cpsStmts)
+    p.s(cpsStmts).add runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s])
 
 proc determineSection(n: PNode): TCFileSection =
   result = cfsProcHeaders
-  if n.len >= 1 and n.sons[0].kind in {nkStrLit..nkTripleStrLit}:
-    let sec = n.sons[0].strVal
-    if sec.startsWith("/*TYPESECTION*/"): result = cfsTypes
+  if n.len >= 1 and n[0].kind in {nkStrLit..nkTripleStrLit}:
+    let sec = n[0].strVal
+    if sec.startsWith("/*TYPESECTION*/"): result = cfsForwardTypes # TODO WORKAROUND
     elif sec.startsWith("/*VARSECTION*/"): result = cfsVars
     elif sec.startsWith("/*INCLUDESECTION*/"): result = cfsHeaders
 
 proc genEmit(p: BProc, t: PNode) =
-  var s = genAsmOrEmitStmt(p, t.sons[1])
+  var s = newRopeAppender()
+  genAsmOrEmitStmt(p, t[1], false, s)
   if p.prc == nil:
     # top level emit pragma?
     let section = determineSection(t[1])
     genCLineDir(p.module.s[section], t.info, p.config)
-    add(p.module.s[section], s)
+    p.module.s[section].add(s)
   else:
     genLineDir(p, t)
     line(p, cpsStmts, s)
 
-proc genBreakPoint(p: BProc, t: PNode) =
-  var name: string
-  if optEndb in p.options:
-    if t.kind == nkExprColonExpr:
-      assert(t.sons[1].kind in {nkStrLit..nkTripleStrLit})
-      name = normalize(t.sons[1].strVal)
-    else:
-      inc(p.module.g.breakPointId)
-      name = "bp" & $p.module.g.breakPointId
-    genLineDir(p, t)          # BUGFIX
-    appcg(p.module, p.module.g.breakpoints,
-         "#dbgRegisterBreakpoint($1, (NCSTRING)$2, (NCSTRING)$3);$n", [
-        rope(toLinenumber(t.info)), makeCString(toFilename(p.config, t.info)),
-        makeCString(name)])
-
-proc genWatchpoint(p: BProc, n: PNode) =
-  if optEndb notin p.options: return
-  var a: TLoc
-  initLocExpr(p, n.sons[1], a)
-  let typ = skipTypes(n.sons[1].typ, abstractVarRange)
-  lineCg(p, cpsStmts, "#dbgRegisterWatchpoint($1, (NCSTRING)$2, $3);$n",
-        [addrLoc(p.config, a), makeCString(renderTree(n.sons[1])),
-        genTypeInfo(p.module, typ, n.info)])
-
 proc genPragma(p: BProc, n: PNode) =
-  for it in n.sons:
+  for i in 0..<n.len:
+    let it = n[i]
     case whichPragma(it)
     of wEmit: genEmit(p, it)
-    of wBreakpoint: genBreakPoint(p, it)
-    of wWatchPoint: genWatchpoint(p, it)
-    of wInjectStmt:
-      var p = newProc(nil, p.module)
-      p.options = p.options - {optLineTrace, optStackTrace}
-      genStmts(p, it.sons[1])
-      p.module.injectStmt = p.s(cpsStmts)
+    of wPush:
+      processPushBackendOption(p.config, p.optionsStack, p.options, n, i+1)
+    of wPop:
+      processPopBackendOption(p.config, p.optionsStack, p.options)
     else: discard
 
-proc fieldDiscriminantCheckNeeded(p: BProc, asgn: PNode): bool =
-  if optFieldCheck in p.options:
-    var le = asgn.sons[0]
-    if le.kind == nkCheckedFieldExpr:
-      var field = le.sons[0].sons[1].sym
-      result = sfDiscriminant in field.flags
-    elif le.kind == nkDotExpr:
-      var field = le.sons[1].sym
-      result = sfDiscriminant in field.flags
 
 proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType,
                           field: PSym) =
   var t = skipTypes(objtype, abstractVar)
   assert t.kind == tyObject
-  discard genTypeInfo(p.module, t, a.lode.info)
-  var L = lengthOrd(p.config, field.typ)
+  discard genTypeInfoV1(p.module, t, a.lode.info)
   if not containsOrIncl(p.module.declaredThings, field.id):
     appcg(p.module, cfsVars, "extern $1",
-          discriminatorTableDecl(p.module, t, field))
+          [discriminatorTableDecl(p.module, t, field)])
+  var lit = newRopeAppender()
+  intLiteral(toInt64(lengthOrd(p.config, field.typ))+1, lit)
   lineCg(p, cpsStmts,
         "#FieldDiscriminantCheck((NI)(NU)($1), (NI)(NU)($2), $3, $4);$n",
         [rdLoc(a), rdLoc(tmp), discriminatorTableName(p.module, t, field),
-         intLiteral(L+1)])
+         lit])
+  if p.config.exc == excGoto:
+    raiseExit(p)
+
+when false:
+  proc genCaseObjDiscMapping(p: BProc, e: PNode, t: PType, field: PSym; d: var TLoc) =
+    const ObjDiscMappingProcSlot = -5
+    var theProc: PSym = nil
+    for idx, p in items(t.methods):
+      if idx == ObjDiscMappingProcSlot:
+        theProc = p
+        break
+    if theProc == nil:
+      theProc = genCaseObjDiscMapping(t, field, e.info, p.module.g.graph, p.module.idgen)
+      t.methods.add((ObjDiscMappingProcSlot, theProc))
+    var call = newNodeIT(nkCall, e.info, getSysType(p.module.g.graph, e.info, tyUInt8))
+    call.add newSymNode(theProc)
+    call.add e
+    expr(p, call, d)
 
 proc asgnFieldDiscriminant(p: BProc, e: PNode) =
-  var a, tmp: TLoc
-  var dotExpr = e.sons[0]
-  if dotExpr.kind == nkCheckedFieldExpr: dotExpr = dotExpr.sons[0]
-  initLocExpr(p, e.sons[0], a)
-  getTemp(p, a.t, tmp)
-  expr(p, e.sons[1], tmp)
-  genDiscriminantCheck(p, a, tmp, dotExpr.sons[0].typ, dotExpr.sons[1].sym)
+  var dotExpr = e[0]
+  if dotExpr.kind == nkCheckedFieldExpr: dotExpr = dotExpr[0]
+  var a = initLocExpr(p, e[0])
+  var tmp: TLoc = getTemp(p, a.t)
+  expr(p, e[1], tmp)
+  if p.inUncheckedAssignSection == 0:
+    let field = dotExpr[1].sym
+    genDiscriminantCheck(p, a, tmp, dotExpr[0].typ, field)
+    message(p.config, e.info, warnCaseTransition)
   genAssignment(p, a, tmp, {})
 
-proc patchAsgnStmtListExpr(father, orig, n: PNode) =
-  case n.kind
-  of nkDerefExpr, nkHiddenDeref:
-    let asgn = copyNode(orig)
-    asgn.add orig[0]
-    asgn.add n
-    father.add asgn
-  of nkStmtList, nkStmtListExpr:
-    for x in n:
-      patchAsgnStmtListExpr(father, orig, x)
-  else:
-    father.add n
-
 proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
-  if e.sons[0].kind == nkSym and sfGoto in e.sons[0].sym.flags:
+  if e[0].kind == nkSym and sfGoto in e[0].sym.flags:
+    genLineDir(p, e)
+    genGotoVar(p, e[1])
+  elif optFieldCheck in p.options and isDiscriminantField(e[0]):
     genLineDir(p, e)
-    genGotoVar(p, e.sons[1])
-  elif not fieldDiscriminantCheckNeeded(p, e):
-    # this fixes bug #6422 but we really need to change the representation of
-    # arrays in the backend...
+    asgnFieldDiscriminant(p, e)
+  else:
     let le = e[0]
     let ri = e[1]
-    var needsRepair = false
-    var it = ri
-    while it.kind in {nkStmtList, nkStmtListExpr}:
-      it = it.lastSon
-      needsRepair = true
-    if it.kind in {nkDerefExpr, nkHiddenDeref} and needsRepair:
-      var patchedTree = newNodeI(nkStmtList, e.info)
-      patchAsgnStmtListExpr(patchedTree, e, ri)
-      genStmts(p, patchedTree)
-      return
-
-    var a: TLoc
-    if le.kind in {nkDerefExpr, nkHiddenDeref}:
-      genDeref(p, le, a, enforceDeref=true)
-    else:
-      initLocExpr(p, le, a)
+    var a: TLoc = initLoc(locNone, le, OnUnknown)
+    discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs), dkVar)
+    a.flags.incl(lfEnforceDeref)
+    a.flags.incl(lfPrepareForMutation)
+    genLineDir(p, le) # it can be a nkBracketExpr, which may raise
+    expr(p, le, a)
+    a.flags.excl(lfPrepareForMutation)
     if fastAsgn: incl(a.flags, lfNoDeepCopy)
     assert(a.t != nil)
     genLineDir(p, ri)
-    loadInto(p, e.sons[0], ri, a)
-  else:
-    genLineDir(p, e)
-    asgnFieldDiscriminant(p, e)
+    loadInto(p, le, ri, a)
 
 proc genStmts(p: BProc, t: PNode) =
-  var a: TLoc
+  var a: TLoc = default(TLoc)
+
+  let isPush = p.config.hasHint(hintExtendedContext)
+  if isPush: pushInfoContext(p.config, t.info)
   expr(p, t, a)
-  internalAssert p.config, a.k in {locNone, locTemp, locLocalVar}
+  if isPush: popInfoContext(p.config)
+  internalAssert p.config, a.k in {locNone, locTemp, locLocalVar, locExpr}
diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim
index 3e8a87041..1f551f022 100644
--- a/compiler/ccgthreadvars.nim
+++ b/compiler/ccgthreadvars.nim
@@ -7,8 +7,8 @@
 #    distribution, for details about the copyright.
 #
 
-## Thread var support for crappy architectures that lack native support for
-## thread local storage. (**Thank you Mac OS X!**)
+## Thread var support for architectures that lack native support for
+## thread local storage.
 
 # included from cgen.nim
 
@@ -16,12 +16,12 @@ proc emulatedThreadVars(conf: ConfigRef): bool =
   result = {optThreads, optTlsEmulation} <= conf.globalOptions
 
 proc accessThreadLocalVar(p: BProc, s: PSym) =
-  if emulatedThreadVars(p.config) and not p.threadVarAccessed:
-    p.threadVarAccessed = true
+  if emulatedThreadVars(p.config) and threadVarAccessed notin p.flags:
+    p.flags.incl threadVarAccessed
     incl p.module.flags, usesThreadVars
-    addf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV_;$n", [])
-    add(p.procSec(cpsInit),
-      ropecg(p.module, "\tNimTV_ = (NimThreadVars*) #GetThreadLocalVars();$n"))
+    p.procSec(cpsLocals).addf("\tNimThreadVars* NimTV_;$n", [])
+    p.procSec(cpsInit).add(
+      ropecg(p.module, "\tNimTV_ = (NimThreadVars*) #GetThreadLocalVars();$n", []))
 
 proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
   if emulatedThreadVars(m.config):
@@ -30,23 +30,30 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
     # allocator for it :-(
     if not containsOrIncl(m.g.nimtvDeclared, s.id):
       m.g.nimtvDeps.add(s.loc.t)
-      addf(m.g.nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
+      m.g.nimtv.addf("$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.snippet])
   else:
-    if isExtern: add(m.s[cfsVars], "extern ")
-    if optThreads in m.config.globalOptions: add(m.s[cfsVars], "NIM_THREADVAR ")
-    add(m.s[cfsVars], getTypeDesc(m, s.loc.t))
-    addf(m.s[cfsVars], " $1;$n", [s.loc.r])
+    if isExtern: m.s[cfsVars].add("extern ")
+    elif lfExportLib in s.loc.flags: m.s[cfsVars].add("N_LIB_EXPORT_VAR ")
+    else: m.s[cfsVars].add("N_LIB_PRIVATE ")
+    if optThreads in m.config.globalOptions:
+      let sym = s.typ.sym
+      if sym != nil and sfCppNonPod in sym.flags:
+        m.s[cfsVars].add("NIM_THREAD_LOCAL ")
+      else: m.s[cfsVars].add("NIM_THREADVAR ")
+    m.s[cfsVars].add(getTypeDesc(m, s.loc.t))
+    m.s[cfsVars].addf(" $1;$n", [s.loc.snippet])
 
 proc generateThreadLocalStorage(m: BModule) =
-  if m.g.nimtv != nil and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
+  if m.g.nimtv != "" and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
     for t in items(m.g.nimtvDeps): discard getTypeDesc(m, t)
-    addf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [m.g.nimtv])
+    finishTypeDescriptions(m)
+    m.s[cfsSeqTypes].addf("typedef struct {$1} NimThreadVars;$n", [m.g.nimtv])
 
 proc generateThreadVarsSize(m: BModule) =
-  if m.g.nimtv != nil:
-    let externc = if m.config.cmd == cmdCompileToCpp or
+  if m.g.nimtv != "":
+    let externc = if m.config.backend == backendCpp or
                        sfCompileToCpp in m.module.flags: "extern \"C\" "
                   else: ""
-    addf(m.s[cfsProcs],
+    m.s[cfsProcs].addf(
       "$#NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}$n",
       [externc.rope])
diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim
index 4514ce7dc..ed4c79d9a 100644
--- a/compiler/ccgtrav.nim
+++ b/compiler/ccgtrav.nim
@@ -7,8 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-## Generates traversal procs for the C backend. Traversal procs are only an
-## optimization; the GC works without them too.
+## Generates traversal procs for the C backend.
 
 # included from cgen.nim
 
@@ -17,27 +16,30 @@ type
     p: BProc
     visitorFrmt: string
 
+const
+  visitorFrmt = "#nimGCvisit((void*)$1, $2);$n"
+
 proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType)
 proc genCaseRange(p: BProc, branch: PNode)
-proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false)
+proc getTemp(p: BProc, t: PType, needsInit=false): TLoc
 
 proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
                      typ: PType) =
   if n == nil: return
   case n.kind
   of nkRecList:
-    for i in countup(0, sonsLen(n) - 1):
-      genTraverseProc(c, accessor, n.sons[i], typ)
+    for i in 0..<n.len:
+      genTraverseProc(c, accessor, n[i], typ)
   of nkRecCase:
-    if (n.sons[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
+    if (n[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
     var p = c.p
-    let disc = n.sons[0].sym
-    if disc.loc.r == nil: fillObjectFields(c.p.module, typ)
+    let disc = n[0].sym
+    if disc.loc.snippet == "": fillObjectFields(c.p.module, typ)
     if disc.loc.t == nil:
       internalError(c.p.config, n.info, "genTraverseProc()")
-    lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
-    for i in countup(1, sonsLen(n) - 1):
-      let branch = n.sons[i]
+    lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.snippet])
+    for i in 1..<n.len:
+      let branch = n[i]
       assert branch.kind in {nkOfBranch, nkElse}
       if branch.kind == nkOfBranch:
         genCaseRange(c.p, branch)
@@ -49,10 +51,10 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
   of nkSym:
     let field = n.sym
     if field.typ.kind == tyVoid: return
-    if field.loc.r == nil: fillObjectFields(c.p.module, typ)
+    if field.loc.snippet == "": fillObjectFields(c.p.module, typ)
     if field.loc.t == nil:
       internalError(c.p.config, n.info, "genTraverseProc()")
-    genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
+    genTraverseProc(c, "$1.$2" % [accessor, field.loc.snippet], field.loc.t)
   else: internalError(c.p.config, n.info, "genTraverseProc()")
 
 proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
@@ -61,56 +63,69 @@ proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
   else:
     result = accessor
 
+proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType)
 proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
   if typ == nil: return
 
   var p = c.p
   case typ.kind
   of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred,
-     tySink:
-    genTraverseProc(c, accessor, lastSon(typ))
+     tySink, tyOwned:
+    genTraverseProc(c, accessor, skipModifier(typ))
   of tyArray:
-    let arraySize = lengthOrd(c.p.config, typ.sons[0])
-    var i: TLoc
-    getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo(), tyInt), i)
-    let oldCode = p.s(cpsStmts)
+    let arraySize = lengthOrd(c.p.config, typ.indexType)
+    var i: TLoc = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
+    var oldCode = p.s(cpsStmts)
+    freeze oldCode
     linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
-            i.r, arraySize.rope)
+            [i.snippet, arraySize])
     let oldLen = p.s(cpsStmts).len
-    genTraverseProc(c, ropecg(c.p.module, "$1[$2]", accessor, i.r), typ.sons[1])
+    genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType)
     if p.s(cpsStmts).len == oldLen:
       # do not emit dummy long loops for faster debug builds:
       p.s(cpsStmts) = oldCode
     else:
       lineF(p, cpsStmts, "}$n", [])
   of tyObject:
-    for i in countup(0, sonsLen(typ) - 1):
-      var x = typ.sons[i]
-      if x != nil: x = x.skipTypes(skipPtrs)
-      genTraverseProc(c, accessor.parentObj(c.p.module), x)
+    var x = typ.baseClass
+    if x != nil: x = x.skipTypes(skipPtrs)
+    genTraverseProc(c, accessor.parentObj(c.p.module), x)
     if typ.n != nil: genTraverseProc(c, accessor, typ.n, typ)
   of tyTuple:
     let typ = getUniqueType(typ)
-    for i in countup(0, sonsLen(typ) - 1):
-      genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", accessor, i.rope), typ.sons[i])
-  of tyRef, tyString, tySequence:
-    lineCg(p, cpsStmts, c.visitorFrmt, accessor)
+    for i, a in typ.ikids:
+      genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", [accessor, i]), a)
+  of tyRef:
+    lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
+  of tySequence:
+    if optSeqDestructors notin c.p.module.config.globalOptions:
+      lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
+    elif containsGarbageCollectedRef(typ.elementType):
+      # destructor based seqs are themselves not traced but their data is, if
+      # they contain a GC'ed type:
+      lineCg(p, cpsStmts, "#nimGCvisitSeq((void*)$1, $2);$n", [accessor, c.visitorFrmt])
+      #genTraverseProcSeq(c, accessor, typ)
+  of tyString:
+    if tfHasAsgn notin typ.flags:
+      lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
   of tyProc:
     if typ.callConv == ccClosure:
-      lineCg(p, cpsStmts, c.visitorFrmt, ropecg(c.p.module, "$1.ClE_0", accessor))
+      lineCg(p, cpsStmts, visitorFrmt, [ropecg(c.p.module, "$1.ClE_0", [accessor]), c.visitorFrmt])
   else:
     discard
 
 proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
   var p = c.p
   assert typ.kind == tySequence
-  var i: TLoc
-  getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo(), tyInt), i)
-  let oldCode = p.s(cpsStmts)
-  lineF(p, cpsStmts, "for ($1 = 0; $1 < $2->$3; $1++) {$n",
-      [i.r, accessor, rope(if c.p.module.compileToCpp: "len" else: "Sup.len")])
+  var i = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
+  var oldCode = p.s(cpsStmts)
+  freeze oldCode
+  var a = TLoc(snippet: accessor)
+
+  lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
+      [i.snippet, lenExpr(c.p, a)])
   let oldLen = p.s(cpsStmts).len
-  genTraverseProc(c, "$1->data[$2]" % [accessor, i.r], typ.sons[0])
+  genTraverseProc(c, "$1$3[$2]" % [accessor, i.snippet, dataField(c.p)], typ.elementType)
   if p.s(cpsStmts).len == oldLen:
     # do not emit dummy long loops for faster debug builds:
     p.s(cpsStmts) = oldCode
@@ -118,50 +133,58 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
     lineF(p, cpsStmts, "}$n", [])
 
 proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
-  var c: TTraversalClosure
   var p = newProc(nil, m)
   result = "Marker_" & getTypeName(m, origTyp, sig)
-  var typ = origTyp.skipTypes(abstractInst)
+  let
+    hcrOn = m.hcrOn
+    typ = origTyp.skipTypes(abstractInstOwned)
+    markerName = if hcrOn: result & "_actual" else: result
+    header = "static N_NIMCALL(void, $1)(void* p, NI op)" % [markerName]
+    t = getTypeDesc(m, typ)
 
-  let header = "static N_NIMCALL(void, $1)(void* p, NI op)" % [result]
-
-  let t = getTypeDesc(m, typ)
   lineF(p, cpsLocals, "$1 a;$n", [t])
   lineF(p, cpsInit, "a = ($1)p;$n", [t])
 
-  c.p = p
-  c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n"
+  var c = TTraversalClosure(p: p,
+    visitorFrmt: "op" # "#nimGCvisit((void*)$1, op);$n"
+    )
 
   assert typ.kind != tyTypeDesc
   if typ.kind == tySequence:
     genTraverseProcSeq(c, "a".rope, typ)
   else:
-    if skipTypes(typ.sons[0], typedescInst).kind == tyArray:
+    if skipTypes(typ.elementType, typedescInst+{tyOwned}).kind == tyArray:
       # C's arrays are broken beyond repair:
-      genTraverseProc(c, "a".rope, typ.sons[0])
+      genTraverseProc(c, "a".rope, typ.elementType)
     else:
-      genTraverseProc(c, "(*a)".rope, typ.sons[0])
+      genTraverseProc(c, "(*a)".rope, typ.elementType)
 
-  let generatedProc = "$1 {$n$2$3$4}$n" %
+  let generatedProc = "$1 {$n$2$3$4}\n" %
         [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
 
-  m.s[cfsProcHeaders].addf("$1;$n", [header])
+  m.s[cfsProcHeaders].addf("$1;\n", [header])
   m.s[cfsProcs].add(generatedProc)
 
+  if hcrOn:
+    m.s[cfsProcHeaders].addf("N_NIMCALL_PTR(void, $1)(void*, NI);\n", [result])
+    m.s[cfsDynLibInit].addf("\t$1 = (N_NIMCALL_PTR(void, )(void*, NI)) hcrRegisterProc($3, \"$1\", (void*)$2);\n",
+         [result, markerName, getModuleDllPath(m)])
+
 proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
-  discard genTypeInfo(m, s.loc.t, info)
+  discard genTypeInfoV1(m, s.loc.t, info)
 
-  var c: TTraversalClosure
   var p = newProc(nil, m)
-  var sLoc = s.loc.r
+  var sLoc = rdLoc(s.loc)
   result = getTempName(m)
 
   if sfThread in s.flags and emulatedThreadVars(m.config):
     accessThreadLocalVar(p, s)
     sLoc = "NimTV_->" & sLoc
 
-  c.visitorFrmt = "#nimGCvisit((void*)$1, 0);$n"
-  c.p = p
+  var c = TTraversalClosure(p: p,
+    visitorFrmt: "0" # "#nimGCvisit((void*)$1, 0);$n"
+  )
+
   let header = "static N_NIMCALL(void, $1)(void)" % [result]
   genTraverseProc(c, sLoc, s.loc.t)
 
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 82508e37e..2c2556336 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -11,10 +11,29 @@
 
 # ------------------------- Name Mangling --------------------------------
 
-import sighashes
-from lowerings import createObj
+import sighashes, modulegraphs, std/strscans
+import ../dist/checksums/src/checksums/md5
+import std/sequtils
 
-proc genProcHeader(m: BModule, prc: PSym): Rope
+type
+  TypeDescKind = enum
+    dkParam #skParam
+    dkRefParam #param passed by ref when {.byref.} is used. Cpp only. C goes straight to dkParam and is handled as a regular pointer
+    dkRefGenericParam #param passed by ref when {.byref.} is used that is also a generic. Cpp only. C goes straight to dkParam and is handled as a regular pointer
+    dkVar #skVar
+    dkField #skField
+    dkResult #skResult
+    dkConst #skConst
+    dkOther #skType, skTemp, skLet and skForVar so far
+
+proc descKindFromSymKind(kind: TSymKind): TypeDescKind =
+  case kind
+  of skParam: dkParam
+  of skVar: dkVar
+  of skField: dkField
+  of skResult: dkResult
+  of skConst: dkConst
+  else: dkOther
 
 proc isKeyword(w: PIdent): bool =
   # Nim and C++ share some keywords
@@ -36,51 +55,72 @@ proc mangleField(m: BModule; name: PIdent): string =
   if isKeyword(name):
     result.add "_0"
 
-when false:
-  proc hashOwner(s: PSym): SigHash =
-    var m = s
-    while m.kind != skModule: m = m.owner
-    let p = m.owner
-    assert p.kind == skPackage
-    result = gDebugInfo.register(p.name.s, m.name.s)
-
-proc mangleName(m: BModule; s: PSym): Rope =
-  result = s.loc.r
-  if result == nil:
-    result = s.name.s.mangle.rope
-    add(result, idOrSig(s, m.module.name.s, m.sigConflicts))
-    s.loc.r = result
+proc mangleProc(m: BModule; s: PSym; makeUnique: bool): string =
+  result = "_Z"  # Common prefix in Itanium ABI
+  result.add encodeSym(m, s, makeUnique)
+  if s.typ.len > 1: #we dont care about the return param
+    for i in 1..<s.typ.len:
+      if s.typ[i].isNil: continue
+      result.add encodeType(m, s.typ[i])
+
+  if result in m.g.mangledPrcs:
+    result = mangleProc(m, s, true)
+  else:
+    m.g.mangledPrcs.incl(result)
+
+proc fillBackendName(m: BModule; s: PSym) =
+  if s.loc.snippet == "":
+    var result: Rope
+    if not m.compileToCpp and s.kind in routineKinds and optCDebug in m.g.config.globalOptions and
+      m.g.config.symbolFiles == disabledSf:
+      result = mangleProc(m, s, false).rope
+    else:
+      result = s.name.s.mangle.rope
+      result.add mangleProcNameExt(m.g.graph, s)
+    if m.hcrOn:
+      result.add '_'
+      result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts, m.config))
+    s.loc.snippet = result
     writeMangledName(m.ndi, s, m.config)
 
-proc mangleParamName(m: BModule; s: PSym): Rope =
-  ## we cannot use 'sigConflicts' here since we have a BModule, not a BProc.
-  ## Fortunately C's scoping rules are sane enough so that that doesn't
-  ## cause any trouble.
-  result = s.loc.r
-  if result == nil:
+proc fillParamName(m: BModule; s: PSym) =
+  if s.loc.snippet == "":
     var res = s.name.s.mangle
-    if isKeyword(s.name) or m.g.config.cppDefines.contains(res):
-      res.add "_0"
-    result = res.rope
-    s.loc.r = result
+    res.add mangleParamExt(s)
+    #res.add idOrSig(s, res, m.sigConflicts, m.config)
+    # Take into account if HCR is on because of the following scenario:
+    #   if a module gets imported and it has some more importc symbols in it,
+    # some param names might receive the "_0" suffix to distinguish from what
+    # is newly available. That might lead to changes in the C code in nimcache
+    # that contain only a parameter name change, but that is enough to mandate
+    # recompilation of that source file and thus a new shared object will be
+    # relinked. That may lead to a module getting reloaded which wasn't intended
+    # and that may be fatal when parts of the current active callstack when
+    # performCodeReload() was called are from the module being reloaded
+    # unintentionally - example (3 modules which import one another):
+    #   main => proxy => reloadable
+    # we call performCodeReload() in proxy to reload only changes in reloadable
+    # but there is a new import which introduces an importc symbol `socket`
+    # and a function called in main or proxy uses `socket` as a parameter name.
+    # That would lead to either needing to reload `proxy` or to overwrite the
+    # executable file for the main module, which is running (or both!) -> error.
+    s.loc.snippet = res.rope
     writeMangledName(m.ndi, s, m.config)
 
-proc mangleLocalName(p: BProc; s: PSym): Rope =
+proc fillLocalName(p: BProc; s: PSym) =
   assert s.kind in skLocalVars+{skTemp}
   #assert sfGlobal notin s.flags
-  result = s.loc.r
-  if result == nil:
+  if s.loc.snippet == "":
     var key = s.name.s.mangle
-    shallow(key)
     let counter = p.sigConflicts.getOrDefault(key)
-    result = key.rope
+    var result = key.rope
     if s.kind == skTemp:
       # speed up conflict search for temps (these are quite common):
       if counter != 0: result.add "_" & rope(counter+1)
     elif counter != 0 or isKeyword(s.name) or p.module.g.config.cppDefines.contains(key):
       result.add "_" & rope(counter+1)
     p.sigConflicts.inc(key)
-    s.loc.r = result
+    s.loc.snippet = result
     if s.kind != skTemp: writeMangledName(p.module.ndi, s, p.config)
 
 proc scopeMangledParam(p: BProc; param: PSym) =
@@ -89,40 +129,42 @@ proc scopeMangledParam(p: BProc; param: PSym) =
   ## generate unique identifiers reliably (consider that ``var a = a`` is
   ## even an idiom in Nim).
   var key = param.name.s.mangle
-  shallow(key)
   p.sigConflicts.inc(key)
 
 const
   irrelevantForBackend = {tyGenericBody, tyGenericInst, tyGenericInvocation,
-                          tyDistinct, tyRange, tyStatic, tyAlias, tySink, tyInferred}
+                          tyDistinct, tyRange, tyStatic, tyAlias, tySink,
+                          tyInferred, tyOwned}
 
-proc typeName(typ: PType): Rope =
+proc typeName(typ: PType; result: var Rope) =
   let typ = typ.skipTypes(irrelevantForBackend)
-  result =
-    if typ.sym != nil and typ.kind in {tyObject, tyEnum}:
-      rope($typ.kind & '_' & typ.sym.name.s.mangle)
-    else:
-      rope($typ.kind)
+  result.add $typ.kind
+  if typ.sym != nil and typ.kind in {tyObject, tyEnum}:
+    result.add "_"
+    result.add typ.sym.name.s.mangle
 
 proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
   var t = typ
   while true:
     if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}:
-      return t.sym.loc.r
+      return t.sym.loc.snippet
 
     if t.kind in irrelevantForBackend:
-      t = t.lastSon
+      t = t.skipModifier
     else:
       break
-  let typ = if typ.kind in {tyAlias, tySink}: typ.lastSon else: typ
-  if typ.loc.r == nil:
-    typ.loc.r = typ.typeName & $sig
+  let typ = if typ.kind in {tyAlias, tySink, tyOwned}: typ.elementType else: typ
+  if typ.loc.snippet == "":
+    typ.typeName(typ.loc.snippet)
+    typ.loc.snippet.add $sig
   else:
     when defined(debugSigHashes):
       # check consistency:
-      assert($typ.loc.r == $(typ.typeName & $sig))
-  result = typ.loc.r
-  if result == nil: internalError(m.config, "getTypeName: " & $typ.kind)
+      var tn = newRopeAppender()
+      typ.typeName(tn)
+      assert($typ.loc.snippet == $(tn & $sig))
+  result = typ.loc.snippet
+  if result == "": internalError(m.config, "getTypeName: " & $typ.kind)
 
 proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
   case int(getSize(conf, typ))
@@ -132,21 +174,25 @@ proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
   of 8: result = ctInt64
   else: result = ctArray
 
-proc mapType(conf: ConfigRef; typ: PType): TCTypeKind =
+proc mapType(conf: ConfigRef; typ: PType; isParam: bool): TCTypeKind =
   ## Maps a Nim type to a C type
   case typ.kind
-  of tyNone, tyStmt: result = ctVoid
+  of tyNone, tyTyped: result = ctVoid
   of tyBool: result = ctBool
   of tyChar: result = ctChar
+  of tyNil: result = ctPtr
   of tySet: result = mapSetType(conf, typ)
-  of tyOpenArray, tyArray, tyVarargs: result = ctArray
+  of tyOpenArray, tyVarargs:
+    if isParam: result = ctArray
+    else: result = ctStruct
+  of tyArray, tyUncheckedArray: result = ctArray
   of tyObject, tyTuple: result = ctStruct
   of tyUserTypeClasses:
     doAssert typ.isResolvedUserTypeClass
-    return mapType(conf, typ.lastSon)
+    result = mapType(conf, typ.skipModifier, isParam)
   of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal,
-     tyTypeDesc, tyAlias, tySink, tyInferred:
-    result = mapType(conf, lastSon(typ))
+     tyTypeDesc, tyAlias, tySink, tyInferred, tyOwned:
+    result = mapType(conf, skipModifier(typ), isParam)
   of tyEnum:
     if firstOrd(conf, typ) < 0:
       result = ctInt32
@@ -157,32 +203,36 @@ proc mapType(conf: ConfigRef; typ: PType): TCTypeKind =
       of 4: result = ctInt32
       of 8: result = ctInt64
       else: result = ctInt32
-  of tyRange: result = mapType(conf, typ.sons[0])
-  of tyPtr, tyVar, tyLent, tyRef, tyOptAsRef:
-    var base = skipTypes(typ.lastSon, typedescInst)
+  of tyRange: result = mapType(conf, typ.elementType, isParam)
+  of tyPtr, tyVar, tyLent, tyRef:
+    var base = skipTypes(typ.elementType, typedescInst)
     case base.kind
-    of tyOpenArray, tyArray, tyVarargs: result = ctPtrToArray
-    #of tySet:
-    #  if mapSetType(base) == ctArray: result = ctPtrToArray
-    #  else: result = ctPtr
-    # XXX for some reason this breaks the pegs module
+    of tyOpenArray, tyArray, tyVarargs, tyUncheckedArray: result = ctPtrToArray
+    of tySet:
+      if mapSetType(conf, base) == ctArray: result = ctPtrToArray
+      else: result = ctPtr
     else: result = ctPtr
   of tyPointer: result = ctPtr
   of tySequence: result = ctNimSeq
   of tyProc: result = if typ.callConv != ccClosure: ctProc else: ctStruct
   of tyString: result = ctNimStr
-  of tyCString: result = ctCString
+  of tyCstring: result = ctCString
   of tyInt..tyUInt64:
     result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt))
   of tyStatic:
-    if typ.n != nil: result = mapType(conf, lastSon typ)
-    else: doAssert(false, "mapType")
-  else: doAssert(false, "mapType")
+    if typ.n != nil: result = mapType(conf, typ.skipModifier, isParam)
+    else:
+      result = ctVoid
+      doAssert(false, "mapType: " & $typ.kind)
+  else:
+    result = ctVoid
+    doAssert(false, "mapType: " & $typ.kind)
+
 
 proc mapReturnType(conf: ConfigRef; typ: PType): TCTypeKind =
   #if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr
   #else:
-  result = mapType(conf, typ)
+  result = mapType(conf, typ, false)
 
 proc isImportedType(t: PType): bool =
   result = t.sym != nil and sfImportc in t.sym.flags
@@ -192,30 +242,47 @@ proc isImportedCppType(t: PType): bool =
   result = (t.sym != nil and sfInfixCall in t.sym.flags) or
            (x.sym != nil and sfInfixCall in x.sym.flags)
 
-proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope
-proc needsComplexAssignment(typ: PType): bool =
-  result = containsGarbageCollectedRef(typ)
+proc isOrHasImportedCppType(typ: PType): bool =
+  searchTypeFor(typ.skipTypes({tyRef}), isImportedCppType)
+
+proc hasNoInit(t: PType): bool =
+  result = t.sym != nil and sfNoInit in t.sym.flags
+
+proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDescKind): Rope
 
 proc isObjLackingTypeField(typ: PType): bool {.inline.} =
   result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and
-      (typ.sons[0] == nil) or isPureObject(typ))
+      (typ.baseClass == nil) or isPureObject(typ))
 
-proc isInvalidReturnType(conf: ConfigRef; rettype: PType): bool =
+proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool =
   # Arrays and sets cannot be returned by a C procedure, because C is
   # such a poor programming language.
   # We exclude records with refs too. This enhances efficiency and
   # is necessary for proper code generation of assignments.
-  if rettype == nil: result = true
+  var rettype = typ
+  var isAllowedCall = true
+  if isProc:
+    rettype = rettype[0]
+    isAllowedCall = typ.callConv in {ccClosure, ccInline, ccNimCall}
+  if rettype == nil or (isAllowedCall and
+                    getSize(conf, rettype) > conf.target.floatSize*3):
+    result = true
   else:
-    case mapType(conf, rettype)
+    case mapType(conf, rettype, false)
     of ctArray:
       result = not (skipTypes(rettype, typedescInst).kind in
           {tyVar, tyLent, tyRef, tyPtr})
     of ctStruct:
       let t = skipTypes(rettype, typedescInst)
-      if rettype.isImportedCppType or t.isImportedCppType: return false
-      result = needsComplexAssignment(t) or
-          (t.kind == tyObject and not isObjLackingTypeField(t))
+      if rettype.isImportedCppType or t.isImportedCppType or
+          (typ.callConv == ccCDecl and conf.selectedGC in {gcArc, gcAtomicArc, gcOrc}):
+        # prevents nrvo for cdecl procs; # bug #23401
+        result = false
+      else:
+        result = containsGarbageCollectedRef(t) or
+            (t.kind == tyObject and not isObjLackingTypeField(t)) or
+            (getSize(conf, rettype) == szUnknownSize and (t.sym == nil or sfImportc notin t.sym.flags))
+
     else: result = false
 
 const
@@ -223,7 +290,9 @@ const
     "N_STDCALL", "N_CDECL", "N_SAFECALL",
     "N_SYSCALL", # this is probably not correct for all platforms,
                  # but one can #define it to what one wants
-    "N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_CLOSURE", "N_NOCONV"]
+    "N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_THISCALL", "N_CLOSURE", "N_NOCONV",
+    "N_NOCONV" #ccMember is N_NOCONV
+    ]
 
 proc cacheGetType(tab: TypeCache; sig: SigHash): Rope =
   # returns nil if we need to declare this type
@@ -231,46 +300,32 @@ proc cacheGetType(tab: TypeCache; sig: SigHash): Rope =
   # linear search is not necessary anymore:
   result = tab.getOrDefault(sig)
 
-proc addAbiCheck(m: BModule, t: PType, name: Rope) =
-  if isDefined(m.config, "checkabi"):
-    addf(m.s[cfsTypeInfo], "NIM_CHECK_SIZE($1, $2);$n", [name, rope(getSize(m.config, t))])
+proc addAbiCheck(m: BModule; t: PType, name: Rope) =
+  if isDefined(m.config, "checkAbi") and (let size = getSize(m.config, t); size != szUnknownSize):
+    var msg = "backend & Nim disagree on size for: "
+    msg.addTypeHeader(m.config, t)
+    var msg2 = ""
+    msg2.addQuoted msg # not a hostspot so extra allocation doesn't matter
+    m.s[cfsTypeInfo].addf("NIM_STATIC_ASSERT(sizeof($1) == $2, $3);$n", [name, rope(size), msg2.rope])
+    # see `testCodegenABICheck` for example error message it generates
 
-proc ccgIntroducedPtr(conf: ConfigRef; s: PSym): bool =
-  var pt = skipTypes(s.typ, typedescInst)
-  assert skResult != s.kind
-  if tfByRef in pt.flags: return true
-  elif tfByCopy in pt.flags: return false
-  case pt.kind
-  of tyObject:
-    if s.typ.sym != nil and sfForward in s.typ.sym.flags:
-      # forwarded objects are *always* passed by pointers for consistency!
-      result = true
-    elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3):
-      result = true           # requested anyway
-    elif (tfFinal in pt.flags) and (pt.sons[0] == nil):
-      result = false          # no need, because no subtyping possible
-    else:
-      result = true           # ordinary objects are always passed by reference,
-                              # otherwise casting doesn't work
-  of tyTuple:
-    result = (getSize(conf, pt) > conf.target.floatSize*3) or (optByRef in s.options)
-  else: result = false
 
-proc fillResult(conf: ConfigRef; param: PNode) =
-  fillLoc(param.sym.loc, locParam, param, ~"Result",
+proc fillResult(conf: ConfigRef; param: PNode, proctype: PType) =
+  fillLoc(param.sym.loc, locParam, param, "Result",
           OnStack)
   let t = param.sym.typ
-  if mapReturnType(conf, t) != ctArray and isInvalidReturnType(conf, t):
+  if mapReturnType(conf, t) != ctArray and isInvalidReturnType(conf, proctype):
     incl(param.sym.loc.flags, lfIndirect)
     param.sym.loc.storage = OnUnknown
 
 proc typeNameOrLiteral(m: BModule; t: PType, literal: string): Rope =
   if t.sym != nil and sfImportc in t.sym.flags and t.sym.magic == mNone:
-    result = t.sym.loc.r
+    useHeader(m, t.sym)
+    result = t.sym.loc.snippet
   else:
     result = rope(literal)
 
-proc getSimpleTypeDesc(m: BModule, typ: PType): Rope =
+proc getSimpleTypeDesc(m: BModule; typ: PType): Rope =
   const
     NumericalTypeToStr: array[tyInt..tyUInt64, string] = [
       "NI", "NI8", "NI16", "NI32", "NI64",
@@ -282,67 +337,73 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope =
   of tyString:
     case detectStrVersion(m)
     of 2:
-      discard cgsym(m, "string")
+      cgsym(m, "NimStrPayload")
+      cgsym(m, "NimStringV2")
       result = typeNameOrLiteral(m, typ, "NimStringV2")
     else:
-      discard cgsym(m, "NimStringDesc")
+      cgsym(m, "NimStringDesc")
       result = typeNameOrLiteral(m, typ, "NimStringDesc*")
-  of tyCString: result = typeNameOrLiteral(m, typ, "NCSTRING")
+  of tyCstring: result = typeNameOrLiteral(m, typ, "NCSTRING")
   of tyBool: result = typeNameOrLiteral(m, typ, "NIM_BOOL")
   of tyChar: result = typeNameOrLiteral(m, typ, "NIM_CHAR")
-  of tyNil: result = typeNameOrLiteral(m, typ, "0")
+  of tyNil: result = typeNameOrLiteral(m, typ, "void*")
   of tyInt..tyUInt64:
     result = typeNameOrLiteral(m, typ, NumericalTypeToStr[typ.kind])
-  of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ.sons[0])
+  of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ.skipModifier)
   of tyStatic:
-    if typ.n != nil: result = getSimpleTypeDesc(m, lastSon typ)
-    else: internalError(m.config, "tyStatic for getSimpleTypeDesc")
-  of tyGenericInst, tyAlias, tySink:
-    result = getSimpleTypeDesc(m, lastSon typ)
-  else: result = nil
-
-  if result != nil and typ.isImportedType():
-    let sig = hashType typ
-    if cacheGetType(m.typeCache, sig) == nil:
+    if typ.n != nil: result = getSimpleTypeDesc(m, skipModifier typ)
+    else:
+      result = ""
+      internalError(m.config, "tyStatic for getSimpleTypeDesc")
+  of tyGenericInst, tyAlias, tySink, tyOwned:
+    result = getSimpleTypeDesc(m, skipModifier typ)
+  else: result = ""
+
+  if result != "" and typ.isImportedType():
+    let sig = hashType(typ, m.config)
+    if cacheGetType(m.typeCache, sig) == "":
       m.typeCache[sig] = result
-      addAbiCheck(m, typ, result)
 
-proc pushType(m: BModule, typ: PType) =
-  add(m.typeStack, typ)
+proc pushType(m: BModule; typ: PType) =
+  for i in 0..high(m.typeStack):
+    # pointer equality is good enough here:
+    if m.typeStack[i] == typ: return
+  m.typeStack.add(typ)
 
-proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope =
+proc getTypePre(m: BModule; typ: PType; sig: SigHash): Rope =
   if typ == nil: result = rope("void")
   else:
     result = getSimpleTypeDesc(m, typ)
-    if result == nil: result = cacheGetType(m.typeCache, sig)
+    if result == "": result = cacheGetType(m.typeCache, sig)
 
-proc structOrUnion(t: PType): Rope =
-  let t = t.skipTypes({tyAlias, tySink})
-  (if tfUnion in t.flags: rope("union") else: rope("struct"))
+proc addForwardStructFormat(m: BModule; structOrUnion: Rope, typename: Rope) =
+  if m.compileToCpp:
+    m.s[cfsForwardTypes].addf "$1 $2;$n", [structOrUnion, typename]
+  else:
+    m.s[cfsForwardTypes].addf "typedef $1 $2 $2;$n", [structOrUnion, typename]
 
-proc getForwardStructFormat(m: BModule): string =
-  if m.compileToCpp: result = "$1 $2;$n"
-  else: result = "typedef $1 $2 $2;$n"
+proc seqStar(m: BModule): string =
+  if optSeqDestructors in m.config.globalOptions: result = ""
+  else: result = "*"
 
-proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope =
+proc getTypeForward(m: BModule; typ: PType; sig: SigHash): Rope =
   result = cacheGetType(m.forwTypeCache, sig)
-  if result != nil: return
+  if result != "": return
   result = getTypePre(m, typ, sig)
-  if result != nil: return
-  let concrete = typ.skipTypes(abstractInst + {tyOpt})
+  if result != "": return
+  let concrete = typ.skipTypes(abstractInst)
   case concrete.kind
   of tySequence, tyTuple, tyObject:
     result = getTypeName(m, typ, sig)
     m.forwTypeCache[sig] = result
     if not isImportedType(concrete):
-      addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
-          [structOrUnion(typ), result])
+      addForwardStructFormat(m, structOrUnion(typ), result)
     else:
       pushType(m, concrete)
     doAssert m.forwTypeCache[sig] == result
   else: internalError(m.config, "getTypeForward(" & $typ.kind & ')')
 
-proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
+proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TypeDescKind): Rope =
   ## like getTypeDescAux but creates only a *weak* dependency. In other words
   ## we know we only need a pointer to it so we only generate a struct forward
   ## declaration:
@@ -350,15 +411,53 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
   case etB.kind
   of tyObject, tyTuple:
     if isImportedCppType(etB) and t.kind == tyGenericInst:
-      result = getTypeDescAux(m, t, check)
+      result = getTypeDescAux(m, t, check, kind)
     else:
-      result = getTypeForward(m, t, hashType(t))
+      result = getTypeForward(m, t, hashType(t, m.config))
       pushType(m, t)
   of tySequence:
-    result = getTypeForward(m, t, hashType(t)) & "*"
-    pushType(m, t)
+    let sig = hashType(t, m.config)
+    if optSeqDestructors in m.config.globalOptions:
+      if skipTypes(etB[0], typedescInst).kind == tyEmpty:
+        internalError(m.config, "cannot map the empty seq type to a C type")
+
+      result = cacheGetType(m.forwTypeCache, sig)
+      if result == "":
+        result = getTypeName(m, t, sig)
+        if not isImportedType(t):
+          m.forwTypeCache[sig] = result
+          addForwardStructFormat(m, rope"struct", result)
+          let payload = result & "_Content"
+          addForwardStructFormat(m, rope"struct", payload)
+
+      if cacheGetType(m.typeCache, sig) == "":
+        m.typeCache[sig] = result
+        #echo "adding ", sig, " ", typeToString(t), " ", m.module.name.s
+        appcg(m, m.s[cfsTypes],
+          "struct $1 {\n" &
+          "  NI len; $1_Content* p;\n" &
+          "};\n", [result])
+        pushType(m, t)
+    else:
+      result = getTypeForward(m, t, sig) & seqStar(m)
+      pushType(m, t)
   else:
-    result = getTypeDescAux(m, t, check)
+    result = getTypeDescAux(m, t, check, kind)
+
+proc getSeqPayloadType(m: BModule; t: PType): Rope =
+  var check = initIntSet()
+  result = getTypeDescWeak(m, t, check, dkParam) & "_Content"
+  #result = getTypeForward(m, t, hashType(t)) & "_Content"
+
+proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) =
+  let sig = hashType(t, m.config)
+  let result = cacheGetType(m.typeCache, sig)
+  if result == "":
+    discard getTypeDescAux(m, t, check, dkVar)
+  else:
+    appcg(m, m.s[cfsTypes], """
+struct $2_Content { NI cap; $1 data[SEQ_DECL_SIZE]; };
+""", [getTypeDescAux(m, t.skipTypes(abstractInst)[0], check, dkVar), result])
 
 proc paramStorageLoc(param: PSym): TStorageLoc =
   if param.typ.skipTypes({tyVar, tyLent, tyTypeDesc}).kind notin {
@@ -367,210 +466,344 @@ proc paramStorageLoc(param: PSym): TStorageLoc =
   else:
     result = OnUnknown
 
-proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
+macro unrollChars(x: static openArray[char], name, body: untyped) =
+  result = newStmtList()
+  for a in x:
+    result.add(newBlockStmt(newStmtList(
+      newConstStmt(name, newLit(a)),
+      copy body
+    )))
+
+proc multiFormat*(frmt: var string, chars: static openArray[char], args: openArray[seq[string]]) =
+  var res: string
+  unrollChars(chars, c):
+    res = ""
+    let arg = args[find(chars, c)]
+    var i = 0
+    var num = 0
+    while i < frmt.len:
+      if frmt[i] == c:
+        inc(i)
+        case frmt[i]
+        of c:
+          res.add(c)
+          inc(i)
+        of '0'..'9':
+          var j = 0
+          while true:
+            j = j * 10 + ord(frmt[i]) - ord('0')
+            inc(i)
+            if i >= frmt.len or frmt[i] notin {'0'..'9'}: break
+          num = j
+          if j > high(arg) + 1:
+            raiseAssert "invalid format string: " & frmt
+          else:
+            res.add(arg[j-1])
+        else:
+          raiseAssert "invalid format string: " & frmt
+      var start = i
+      while i < frmt.len:
+        if frmt[i] != c: inc(i)
+        else: break
+      if i - 1 >= start:
+        res.add(substr(frmt, start, i - 1))
+    frmt = res
+
+template cgDeclFrmt*(s: PSym): string =
+  s.constraint.strVal
+
+proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params: var string,
+                   check: var IntSet, declareEnvironment=true;
+                   weakDep=false;) =
+  let t = prc.typ
+  let isCtor = sfConstructor in prc.flags
+  if isCtor or (name[0] == '~' and sfMember in prc.flags):
+    # destructors can't have void
+    rettype = ""
+  elif t.returnType == nil or isInvalidReturnType(m.config, t):
+    rettype = "void"
+  else:
+    if rettype == "":
+      rettype = getTypeDescAux(m, t.returnType, check, dkResult)
+    else:
+      rettype = runtimeFormat(rettype.replace("'0", "$1"), [getTypeDescAux(m, t.returnType, check, dkResult)])
+  var types, names, args: seq[string] = @[]
+  if not isCtor:
+    var this = t.n[1].sym
+    fillParamName(m, this)
+    fillLoc(this.loc, locParam, t.n[1],
+            this.paramStorageLoc)
+    if this.typ.kind == tyPtr:
+      this.loc.snippet = "this"
+    else:
+      this.loc.snippet = "(*this)"
+    names.add this.loc.snippet
+    types.add getTypeDescWeak(m, this.typ, check, dkParam)
+
+  let firstParam = if isCtor: 1 else: 2
+  for i in firstParam..<t.n.len:
+    if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genMemberProcParams")
+    var param = t.n[i].sym
+    var descKind = dkParam
+    if optByRef in param.options:
+      if param.typ.kind == tyGenericInst:
+        descKind = dkRefGenericParam
+      else:
+        descKind = dkRefParam
+    var typ, name: string
+    fillParamName(m, param)
+    fillLoc(param.loc, locParam, t.n[i],
+            param.paramStorageLoc)
+    if ccgIntroducedPtr(m.config, param, t.returnType) and descKind == dkParam:
+      typ = getTypeDescWeak(m, param.typ, check, descKind) & "*"
+      incl(param.loc.flags, lfIndirect)
+      param.loc.storage = OnUnknown
+    elif weakDep:
+      typ = getTypeDescWeak(m, param.typ, check, descKind)
+    else:
+      typ = getTypeDescAux(m, param.typ, check, descKind)
+    if sfNoalias in param.flags:
+      typ.add("NIM_NOALIAS ")
+
+    name = param.loc.snippet
+    types.add typ
+    names.add name
+    if sfCodegenDecl notin param.flags:
+      args.add types[^1] & " " & names[^1]
+    else:
+      args.add runtimeFormat(param.cgDeclFrmt, [types[^1], names[^1]])
+
+  multiFormat(params, @['\'', '#'], [types, names])
+  multiFormat(superCall, @['\'', '#'], [types, names])
+  multiFormat(name, @['\'', '#'], [types, names]) #so we can ~'1 on members
+  if params == "()":
+    if types.len == 0:
+      params = "(void)"
+    else:
+      params = "(" & args.join(", ") & ")"
+  if tfVarargs in t.flags:
+    if params != "(":
+      params[^1] = ','
+    else:
+      params.delete(params.len()-1..params.len()-1)
+    params.add("...)")
+
+proc genProcParams(m: BModule; t: PType, rettype, params: var Rope,
                    check: var IntSet, declareEnvironment=true;
-                   weakDep=false) =
-  params = nil
-  if t.sons[0] == nil or isInvalidReturnType(m.config, t.sons[0]):
-    rettype = ~"void"
+                   weakDep=false;) =
+  params = "("
+  if t.returnType == nil or isInvalidReturnType(m.config, t):
+    rettype = "void"
   else:
-    rettype = getTypeDescAux(m, t.sons[0], check)
-  for i in countup(1, sonsLen(t.n) - 1):
-    if t.n.sons[i].kind != nkSym: internalError(m.config, t.n.info, "genProcParams")
-    var param = t.n.sons[i].sym
+    rettype = getTypeDescAux(m, t.returnType, check, dkResult)
+  for i in 1..<t.n.len:
+    if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genProcParams")
+    var param = t.n[i].sym
+    var descKind = dkParam
+    if m.config.backend == backendCpp and optByRef in param.options:
+      if param.typ.kind == tyGenericInst:
+        descKind = dkRefGenericParam
+      else:
+        descKind = dkRefParam
     if isCompileTimeOnly(param.typ): continue
-    if params != nil: add(params, ~", ")
-    fillLoc(param.loc, locParam, t.n.sons[i], mangleParamName(m, param),
+    if params != "(": params.add(", ")
+    fillParamName(m, param)
+    fillLoc(param.loc, locParam, t.n[i],
             param.paramStorageLoc)
-    if ccgIntroducedPtr(m.config, param):
-      add(params, getTypeDescWeak(m, param.typ, check))
-      add(params, ~"*")
+    var typ: Rope
+    if ccgIntroducedPtr(m.config, param, t.returnType) and descKind == dkParam:
+      typ = (getTypeDescWeak(m, param.typ, check, descKind))
+      typ.add("*")
       incl(param.loc.flags, lfIndirect)
       param.loc.storage = OnUnknown
     elif weakDep:
-      add(params, getTypeDescWeak(m, param.typ, check))
+      typ = (getTypeDescWeak(m, param.typ, check, descKind))
+    else:
+      typ = (getTypeDescAux(m, param.typ, check, descKind))
+    typ.add(" ")
+    if sfNoalias in param.flags:
+      typ.add("NIM_NOALIAS ")
+    if sfCodegenDecl notin param.flags:
+      params.add(typ)
+      params.add(param.loc.snippet)
     else:
-      add(params, getTypeDescAux(m, param.typ, check))
-    add(params, ~" ")
-    add(params, param.loc.r)
+      params.add runtimeFormat(param.cgDeclFrmt, [typ, param.loc.snippet])
     # declare the len field for open arrays:
-    var arr = param.typ
-    if arr.kind in {tyVar, tyLent}: arr = arr.lastSon
+    var arr = param.typ.skipTypes({tyGenericInst})
+    if arr.kind in {tyVar, tyLent, tySink}: arr = arr.elementType
     var j = 0
     while arr.kind in {tyOpenArray, tyVarargs}:
       # this fixes the 'sort' bug:
       if param.typ.kind in {tyVar, tyLent}: param.loc.storage = OnUnknown
       # need to pass hidden parameter:
-      addf(params, ", NI $1Len_$2", [param.loc.r, j.rope])
+      params.addf(", NI $1Len_$2", [param.loc.snippet, j.rope])
       inc(j)
-      arr = arr.sons[0]
-  if t.sons[0] != nil and isInvalidReturnType(m.config, t.sons[0]):
-    var arr = t.sons[0]
-    if params != nil: add(params, ", ")
-    if mapReturnType(m.config, t.sons[0]) != ctArray:
-      add(params, getTypeDescWeak(m, arr, check))
-      add(params, "*")
+      arr = arr[0].skipTypes({tySink})
+  if t.returnType != nil and isInvalidReturnType(m.config, t):
+    var arr = t.returnType
+    if params != "(": params.add(", ")
+    if mapReturnType(m.config, arr) != ctArray:
+      if isHeaderFile in m.flags:
+        # still generates types for `--header`
+        params.add(getTypeDescAux(m, arr, check, dkResult))
+        params.add("*")
+      else:
+        params.add(getTypeDescWeak(m, arr, check, dkResult))
+        params.add("*")
     else:
-      add(params, getTypeDescAux(m, arr, check))
-    addf(params, " Result", [])
+      params.add(getTypeDescAux(m, arr, check, dkResult))
+    params.addf(" Result", [])
   if t.callConv == ccClosure and declareEnvironment:
-    if params != nil: add(params, ", ")
-    add(params, "void* ClE_0")
+    if params != "(": params.add(", ")
+    params.add("void* ClE_0")
   if tfVarargs in t.flags:
-    if params != nil: add(params, ", ")
-    add(params, "...")
-  if params == nil: add(params, "void)")
-  else: add(params, ")")
-  params = "(" & params
-
-proc mangleRecFieldName(m: BModule; field: PSym, rectype: PType): Rope =
-  if (rectype.sym != nil) and
-      ({sfImportc, sfExportc} * rectype.sym.flags != {}):
-    result = field.loc.r
+    if params != "(": params.add(", ")
+    params.add("...")
+  if params == "(": params.add("void)")
+  else: params.add(")")
+
+proc mangleRecFieldName(m: BModule; field: PSym): Rope =
+  if {sfImportc, sfExportc} * field.flags != {}:
+    result = field.loc.snippet
   else:
     result = rope(mangleField(m, field.name))
-  if result == nil: internalError(m.config, field.info, "mangleRecFieldName")
-
-proc genRecordFieldsAux(m: BModule, n: PNode,
-                        accessExpr: Rope, rectype: PType,
-                        check: var IntSet): Rope =
-  result = nil
+  if result == "": internalError(m.config, field.info, "mangleRecFieldName")
+
+proc hasCppCtor(m: BModule; typ: PType): bool =
+  result = false
+  if m.compileToCpp and typ != nil and typ.itemId in m.g.graph.memberProcsPerType:
+    for prc in m.g.graph.memberProcsPerType[typ.itemId]:
+      if sfConstructor in prc.flags:
+        return true
+
+proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): string
+
+proc genCppInitializer(m: BModule, prc: BProc; typ: PType; didGenTemp: var bool): string =
+  #To avoid creating a BProc per test when called inside a struct nil BProc is allowed
+  result = "{}"
+  if typ.itemId in m.g.graph.initializersPerType:
+    let call = m.g.graph.initializersPerType[typ.itemId]
+    if call != nil:
+      var p = prc
+      if p == nil:
+        p = BProc(module: m)
+      result = "{" & genCppParamsForCtor(p, call, didGenTemp) & "}"
+      if prc == nil:
+        assert p.blocks.len == 0, "BProc belongs to a struct doesnt have blocks"
+
+proc genRecordFieldsAux(m: BModule; n: PNode,
+                        rectype: PType,
+                        check: var IntSet; result: var Builder; unionPrefix = "") =
   case n.kind
   of nkRecList:
-    for i in countup(0, sonsLen(n) - 1):
-      add(result, genRecordFieldsAux(m, n.sons[i], accessExpr, rectype, check))
+    for i in 0..<n.len:
+      genRecordFieldsAux(m, n[i], rectype, check, result, unionPrefix)
   of nkRecCase:
-    if n.sons[0].kind != nkSym: internalError(m.config, n.info, "genRecordFieldsAux")
-    add(result, genRecordFieldsAux(m, n.sons[0], accessExpr, rectype, check))
-    let uname = rope(mangle(n.sons[0].sym.name.s) & 'U')
-    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
+    if n[0].kind != nkSym: internalError(m.config, n.info, "genRecordFieldsAux")
+    genRecordFieldsAux(m, n[0], rectype, check, result, unionPrefix)
+    # prefix mangled name with "_U" to avoid clashes with other field names,
+    # since identifiers are not allowed to start with '_'
+    var unionBody: Rope = ""
+    for i in 1..<n.len:
+      case n[i].kind
       of nkOfBranch, nkElse:
-        let k = lastSon(n.sons[i])
+        let k = lastSon(n[i])
         if k.kind != nkSym:
-          let sname = "S" & rope(i)
-          let a = genRecordFieldsAux(m, k, "$1.$2" % [ae, sname], rectype,
-                                     check)
-          if a != nil:
-            if tfPacked notin rectype.flags:
-              add(unionBody, "struct {")
-            else:
-              if hasAttribute in CC[m.config.cCompiler].props:
-                add(unionBody, "struct __attribute__((__packed__)){" )
-              else:
-                addf(unionBody, "#pragma pack(push, 1)$nstruct{", [])
-            add(unionBody, a)
-            addf(unionBody, "} $1;$n", [sname])
-            if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props:
-              addf(unionBody, "#pragma pack(pop)$n", [])
+          let structName = "_" & mangleRecFieldName(m, n[0].sym) & "_" & $i
+          var a = newBuilder("")
+          genRecordFieldsAux(m, k, rectype, check, a, unionPrefix & $structName & ".")
+          if a.len != 0:
+            unionBody.addFieldWithStructType(m, rectype, structName):
+              unionBody.add(a)
         else:
-          add(unionBody, genRecordFieldsAux(m, k, ae, rectype, check))
+          genRecordFieldsAux(m, k, rectype, check, unionBody, unionPrefix)
       else: internalError(m.config, "genRecordFieldsAux(record case branch)")
-    if unionBody != nil:
-      addf(result, "union{$n$1} $2;$n", [unionBody, uname])
+    if unionBody.len != 0:
+      result.addAnonUnion:
+        result.add(unionBody)
   of nkSym:
     let field = n.sym
     if field.typ.kind == tyVoid: return
     #assert(field.ast == nil)
-    let sname = mangleRecFieldName(m, field, rectype)
-    let ae = if accessExpr != nil: "$1.$2" % [accessExpr, sname]
-             else: sname
-    fillLoc(field.loc, locField, n, ae, OnUnknown)
+    let sname = mangleRecFieldName(m, field)
+    fillLoc(field.loc, locField, n, unionPrefix & 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:
     if not isImportedCppType(rectype):
       let fieldType = field.loc.lode.typ.skipTypes(abstractInst)
-      if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags:
-        addf(result, "$1 $2[SEQ_DECL_SIZE];$n",
-            [getTypeDescAux(m, fieldType.elemType, check), sname])
-      elif fieldType.kind in {tySequence, tyOpt}:
+      var typ: Rope = ""
+      var isFlexArray = false
+      var initializer = ""
+      if fieldType.kind == tyUncheckedArray:
+        typ = getTypeDescAux(m, fieldType.elemType, check, dkField)
+        isFlexArray = true
+      elif fieldType.kind == tySequence:
         # we need to use a weak dependency here for trecursive_table.
-        addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname])
-      elif field.bitsize != 0:
-        addf(result, "$1 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check), sname, rope($field.bitsize)])
+        typ = getTypeDescWeak(m, field.loc.t, check, dkField)
       else:
+        typ = getTypeDescAux(m, field.loc.t, check, dkField)
         # don't use fieldType here because we need the
         # tyGenericInst for C++ template support
-        addf(result, "$1 $2;$n", [getTypeDescAux(m, field.loc.t, check), sname])
+        let noInit = sfNoInit in field.flags or (field.typ.sym != nil and sfNoInit in field.typ.sym.flags)
+        if not noInit and (fieldType.isOrHasImportedCppType() or hasCppCtor(m, field.owner.typ)):
+          var didGenTemp = false
+          initializer = genCppInitializer(m, nil, fieldType, didGenTemp)
+      result.addField(field, sname, typ, isFlexArray, initializer)
   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)
+proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl:bool = false)
+
+proc addRecordFields(result: var Builder; m: BModule; typ: PType, check: var IntSet) =
+  genRecordFieldsAux(m, typ.n, typ, check, result)
+  if typ.itemId in m.g.graph.memberProcsPerType:
+    let procs = m.g.graph.memberProcsPerType[typ.itemId]
+    var isDefaultCtorGen, isCtorGen: bool = false
+    for prc in procs:
+      var header: Rope = ""
+      if sfConstructor in prc.flags:
+        isCtorGen = true
+        if prc.typ.n.len == 1:
+          isDefaultCtorGen = true
+      if lfNoDecl in prc.loc.flags: continue
+      genMemberProcHeader(m, prc, header, false, true)
+      result.addf "$1;$n", [header]
+    if isCtorGen and not isDefaultCtorGen:
+      var ch: IntSet = default(IntSet)
+      result.addf "$1() = default;$n", [getTypeDescAux(m, typ, ch, dkOther)]
 
 proc fillObjectFields*(m: BModule; typ: PType) =
   # sometimes generic objects are not consistently merged. We patch over
   # this fact here.
   var check = initIntSet()
-  discard getRecordFields(m, typ, check)
+  var ignored = newBuilder("")
+  addRecordFields(ignored, m, typ, check)
+
+proc mangleDynLibProc(sym: PSym): Rope
 
-proc getRecordDesc(m: BModule, typ: PType, name: Rope,
+proc getRecordDesc(m: BModule; typ: PType, name: Rope,
                    check: var IntSet): Rope =
   # declare the record:
-  var hasField = false
-
-  if tfPacked in typ.flags:
-    if hasAttribute in CC[m.config.cCompiler].props:
-      result = structOrUnion(typ) & " __attribute__((__packed__))"
-    else:
-      result = "#pragma pack(push, 1)\L" & structOrUnion(typ)
-  else:
-    result = structOrUnion(typ)
-
-  result.add " "
-  result.add name
-
-  if typ.kind == tyObject:
-
-    if typ.sons[0] == nil:
-      if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags:
-        appcg(m, result, " {$n", [])
-      else:
-        appcg(m, result, " {$n#TNimType* m_type;$n", [])
-        hasField = true
-    elif m.compileToCpp:
-      appcg(m, result, " : public $1 {$n",
-                      [getTypeDescAux(m, typ.sons[0].skipTypes(skipPtrs), check)])
-      if typ.isException:
-        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->raise_id) popCurrentExceptionEx(this->raise_id);}$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
-      hasField = true
-    else:
-      appcg(m, result, " {$n  $1 Sup;$n",
-                      [getTypeDescAux(m, typ.sons[0].skipTypes(skipPtrs), check)])
-      hasField = true
-  else:
-    addf(result, " {$n", [name])
-
-  let desc = getRecordFields(m, typ, check)
-  if desc == nil and not hasField:
-    addf(result, "char dummy;$n", [])
+  var baseType: string = ""
+  if typ.baseClass != nil:
+    baseType = getTypeDescAux(m, typ.baseClass.skipTypes(skipPtrs), check, dkField)
+  if typ.sym == nil or sfCodegenDecl notin typ.sym.flags:
+    result = newBuilder("")
+    result.addStruct(m, typ, name, baseType):
+      result.addRecordFields(m, typ, check)
   else:
-    add(result, desc)
-  add(result, "};\L")
-  if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props:
-    result.add "#pragma pack(pop)\L"
+    var desc = newBuilder("")
+    desc.addRecordFields(m, typ, check)
+    result = runtimeFormat(typ.sym.cgDeclFrmt, [name, desc, baseType])
 
-proc getTupleDesc(m: BModule, typ: PType, name: Rope,
+proc getTupleDesc(m: BModule; typ: PType, name: Rope,
                   check: var IntSet): Rope =
-  result = "$1 $2 {$n" % [structOrUnion(typ), name]
-  var desc: Rope = nil
-  for i in countup(0, sonsLen(typ) - 1):
-    addf(desc, "$1 Field$2;$n",
-         [getTypeDescAux(m, typ.sons[i], check), rope(i)])
-  if desc == nil: add(result, "char dummy;\L")
-  else: add(result, desc)
-  add(result, "};\L")
+  result = newBuilder("")
+  result.addStruct(m, typ, name, ""):
+    for i, a in typ.ikids:
+      result.addField(
+        name = "Field" & $i,
+        typ = getTypeDescAux(m, a, check, dkField))
 
 proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool =
   # A helper proc for handling cppimport patterns, involving numeric
@@ -592,18 +825,31 @@ proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool =
 proc resolveStarsInCppType(typ: PType, idx, stars: int): PType =
   # Make sure the index refers to one of the generic params of the type.
   # XXX: we should catch this earlier and report it as a semantic error.
-  if idx >= typ.len:
-    doAssert false, "invalid apostrophe type parameter index"
+  if idx >= typ.kidsLen:
+    raiseAssert "invalid apostrophe type parameter index"
 
-  result = typ.sons[idx]
+  result = typ[idx]
   for i in 1..stars:
-    if result != nil and result.len > 0:
-      result = if result.kind == tyGenericInst: result.sons[1]
+    if result != nil and result.kidsLen > 0:
+      result = if result.kind == tyGenericInst: result[FirstGenericParamAt]
                else: result.elemType
 
-proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
+proc getOpenArrayDesc(m: BModule; t: PType, check: var IntSet; kind: TypeDescKind): Rope =
+  let sig = hashType(t, m.config)
+  if kind == dkParam:
+    result = getTypeDescWeak(m, t.elementType, check, kind) & "*"
+  else:
+    result = cacheGetType(m.typeCache, sig)
+    if result == "":
+      result = getTypeName(m, t, sig)
+      m.typeCache[sig] = result
+      let elemType = getTypeDescWeak(m, t.elementType, check, kind)
+      m.s[cfsTypes].addf("typedef struct {$n$2* Field0;$nNI Field1;$n} $1;$n",
+                         [result, elemType])
+
+proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDescKind): Rope =
   # returns only the type's name
-  var t = origTyp.skipTypes(irrelevantForBackend)
+  var t = origTyp.skipTypes(irrelevantForBackend-{tyOwned})
   if containsOrIncl(check, t.id):
     if not (isImportedCppType(origTyp) or isImportedCppType(t)):
       internalError(m.config, "cannot generate C type for: " & typeToString(origTyp))
@@ -612,154 +858,178 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
     # C type generation into an analysis and a code generation phase somehow.
   if t.sym != nil: useHeader(m, t.sym)
   if t != origTyp and origTyp.sym != nil: useHeader(m, origTyp.sym)
-  let sig = hashType(origTyp)
+  let sig = hashType(origTyp, m.config)
+
   result = getTypePre(m, t, sig)
-  if result != nil:
+  defer: # defer is the simplest in this case
+    if isImportedType(t) and not m.typeABICache.containsOrIncl(sig):
+      addAbiCheck(m, t, result)
+
+  if result != "" and t.kind != tyOpenArray:
     excl(check, t.id)
+    if kind == dkRefParam or kind == dkRefGenericParam and origTyp.kind == tyGenericInst:
+      result.add("&")
     return
   case t.kind
-  of tyRef, tyOptAsRef, tyPtr, tyVar, tyLent:
-    var star = if t.kind == tyVar and tfVarIsPtr notin origTyp.flags and
+  of tyRef, tyPtr, tyVar, tyLent:
+    var star = if t.kind in {tyVar} and tfVarIsPtr notin origTyp.flags and
                     compileToCpp(m): "&" else: "*"
-    var et = origTyp.skipTypes(abstractInst).lastSon
+    var et = origTyp.skipTypes(abstractInst).elementType
     var etB = et.skipTypes(abstractInst)
-    if etB.kind in {tyArray, tyOpenArray, tyVarargs}:
-      # this is correct! sets have no proper base type, so we treat
-      # ``var set[char]`` in `getParamTypeDesc`
-      et = elemType(etB)
+    if mapType(m.config, t, kind == dkParam) == ctPtrToArray and (etB.kind != tyOpenArray or kind == dkParam):
+      if etB.kind == tySet:
+        et = getSysType(m.g.graph, unknownLineInfo, tyUInt8)
+      else:
+        et = elemType(etB)
       etB = et.skipTypes(abstractInst)
       star[0] = '*'
     case etB.kind
     of tyObject, tyTuple:
       if isImportedCppType(etB) and et.kind == tyGenericInst:
-        result = getTypeDescAux(m, et, check) & star
+        result = getTypeDescAux(m, et, check, kind) & star
       else:
         # no restriction! We have a forward declaration for structs
-        let name = getTypeForward(m, et, hashType et)
+        let name = getTypeForward(m, et, hashType(et, m.config))
         result = name & star
         m.typeCache[sig] = result
     of tySequence:
-      # no restriction! We have a forward declaration for structs
-      let name = getTypeForward(m, et, hashType et)
-      result = name & "*" & star
-      m.typeCache[sig] = result
-      pushType(m, et)
+      if optSeqDestructors in m.config.globalOptions:
+        result = getTypeDescWeak(m, et, check, kind) & star
+        m.typeCache[sig] = result
+      else:
+        # no restriction! We have a forward declaration for structs
+        let name = getTypeForward(m, et, hashType(et, m.config))
+        result = name & seqStar(m) & star
+        m.typeCache[sig] = result
+        pushType(m, et)
     else:
       # else we have a strong dependency  :-(
-      result = getTypeDescAux(m, et, check) & star
+      result = getTypeDescAux(m, et, check, kind) & star
       m.typeCache[sig] = result
   of tyOpenArray, tyVarargs:
-    result = getTypeDescWeak(m, t.sons[0], check) & "*"
-    m.typeCache[sig] = result
+    result = getOpenArrayDesc(m, t, check, kind)
   of tyEnum:
     result = cacheGetType(m.typeCache, sig)
-    if result == nil:
+    if result == "":
       result = getTypeName(m, origTyp, sig)
       if not (isImportedCppType(t) or
           (sfImportc in t.sym.flags and t.sym.magic == mNone)):
         m.typeCache[sig] = result
         var size: int
         if firstOrd(m.config, t) < 0:
-          addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result])
+          m.s[cfsTypes].addf("typedef NI32 $1;$n", [result])
           size = 4
         else:
           size = int(getSize(m.config, t))
           case size
-          of 1: addf(m.s[cfsTypes], "typedef NU8 $1;$n", [result])
-          of 2: addf(m.s[cfsTypes], "typedef NU16 $1;$n", [result])
-          of 4: addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result])
-          of 8: addf(m.s[cfsTypes], "typedef NI64 $1;$n", [result])
+          of 1: m.s[cfsTypes].addf("typedef NU8 $1;$n", [result])
+          of 2: m.s[cfsTypes].addf("typedef NU16 $1;$n", [result])
+          of 4: m.s[cfsTypes].addf("typedef NI32 $1;$n", [result])
+          of 8: m.s[cfsTypes].addf("typedef NI64 $1;$n", [result])
           else: internalError(m.config, t.sym.info, "getTypeDescAux: enum")
         when false:
           let owner = hashOwner(t.sym)
           if not gDebugInfo.hasEnum(t.sym.name.s, t.sym.info.line, owner):
             var vals: seq[(string, int)] = @[]
-            for i in countup(0, t.n.len - 1):
-              assert(t.n.sons[i].kind == nkSym)
-              let field = t.n.sons[i].sym
+            for i in 0..<t.n.len:
+              assert(t.n[i].kind == nkSym)
+              let field = t.n[i].sym
               vals.add((field.name.s, field.position.int))
             gDebugInfo.registerEnum(EnumDesc(size: size, owner: owner, id: t.sym.id,
               name: t.sym.name.s, values: vals))
   of tyProc:
     result = getTypeName(m, origTyp, sig)
     m.typeCache[sig] = result
-    var rettype, desc: Rope
+    var rettype, desc: Rope = ""
     genProcParams(m, t, rettype, desc, check, true, true)
     if not isImportedType(t):
       if t.callConv != ccClosure: # procedure vars may need a closure!
-        addf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n",
+        m.s[cfsTypes].addf("typedef $1_PTR($2, $3) $4;$n",
              [rope(CallingConvToStr[t.callConv]), rettype, result, desc])
       else:
-        addf(m.s[cfsTypes], "typedef struct {$n" &
+        m.s[cfsTypes].addf("typedef struct {$n" &
             "N_NIMCALL_PTR($2, ClP_0) $3;$n" &
             "void* ClE_0;$n} $1;$n",
              [result, rettype, desc])
   of tySequence:
-    # we cannot use getTypeForward here because then t would be associated
-    # with the name of the struct, not with the pointer to the struct:
-    result = cacheGetType(m.forwTypeCache, sig)
-    if result == nil:
-      result = getTypeName(m, origTyp, sig)
+    if optSeqDestructors in m.config.globalOptions:
+      result = getTypeDescWeak(m, t, check, kind)
+    else:
+      # we cannot use getTypeForward here because then t would be associated
+      # with the name of the struct, not with the pointer to the struct:
+      result = cacheGetType(m.forwTypeCache, sig)
+      if result == "":
+        result = getTypeName(m, origTyp, sig)
+        if not isImportedType(t):
+          addForwardStructFormat(m, structOrUnion(t), result)
+        m.forwTypeCache[sig] = result
+      assert(cacheGetType(m.typeCache, sig) == "")
+      m.typeCache[sig] = result & seqStar(m)
       if not isImportedType(t):
-        addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
-            [structOrUnion(t), result])
-      m.forwTypeCache[sig] = result
-    assert(cacheGetType(m.typeCache, sig) == nil)
-    m.typeCache[sig] = result & "*"
+        if skipTypes(t.elementType, typedescInst).kind != tyEmpty:
+          var struct = newBuilder("")
+          let baseType = cgsymValue(m, "TGenericSeq")
+          struct.addSimpleStruct(m, name = result, baseType = baseType):
+            struct.addField(
+              name = "data",
+              typ = getTypeDescAux(m, t.elementType, check, kind),
+              isFlexArray = true)
+          m.s[cfsSeqTypes].add struct
+        else:
+          result = rope("TGenericSeq")
+      result.add(seqStar(m))
+  of tyUncheckedArray:
+    result = getTypeName(m, origTyp, sig)
+    m.typeCache[sig] = result
     if not isImportedType(t):
-      if skipTypes(t.sons[0], typedescInst).kind != tyEmpty:
-        const
-          cppSeq = "struct $2 : #TGenericSeq {$n"
-          cSeq = "struct $2 {$n" &
-                 "  #TGenericSeq Sup;$n"
-        appcg(m, m.s[cfsSeqTypes],
-            (if m.compileToCpp: cppSeq else: cSeq) &
-            "  $1 data[SEQ_DECL_SIZE];$n" &
-            "};$n", [getTypeDescAux(m, t.sons[0], check), result])
-      else:
-        result = rope("TGenericSeq")
-    add(result, "*")
+      let foo = getTypeDescAux(m, t.elementType, check, kind)
+      m.s[cfsTypes].addf("typedef $1 $2[1];$n", [foo, result])
   of tyArray:
-    var n: BiggestInt = lengthOrd(m.config, t)
+    var n: BiggestInt = toInt64(lengthOrd(m.config, t))
     if n <= 0: n = 1   # make an array of at least one element
     result = getTypeName(m, origTyp, sig)
     m.typeCache[sig] = result
     if not isImportedType(t):
-      let foo = getTypeDescAux(m, t.sons[1], check)
-      addf(m.s[cfsTypes], "typedef $1 $2[$3];$n",
-           [foo, result, rope(n)])
-    else: addAbiCheck(m, t, result)
+      let e = getTypeDescAux(m, t.elementType, check, kind)
+      m.s[cfsTypes].addf("typedef $1 $2[$3];$n",
+           [e, result, rope(n)])
   of tyObject, tyTuple:
-    if isImportedCppType(t) and origTyp.kind == tyGenericInst:
-      let cppName = getTypeName(m, t, sig)
+    let tt = origTyp.skipTypes({tyDistinct})
+    if isImportedCppType(t) and tt.kind == tyGenericInst:
+      let cppNameAsRope = getTypeName(m, t, sig)
+      let cppName = $cppNameAsRope
       var i = 0
       var chunkStart = 0
-      while i < cppName.data.len:
-        if cppName.data[i] == '\'':
+
+      template addResultType(ty: untyped) =
+        if ty == nil or ty.kind == tyVoid:
+          result.add("void")
+        elif ty.kind == tyStatic:
+          internalAssert m.config, ty.n != nil
+          result.add ty.n.renderTree
+        else:
+          result.add getTypeDescAux(m, ty, check, kind)
+
+      while i < cppName.len:
+        if cppName[i] == '\'':
           var chunkEnd = i-1
-          var idx, stars: int
-          if scanCppGenericSlot(cppName.data, i, idx, stars):
-            result.add cppName.data.substr(chunkStart, chunkEnd)
+          var idx, stars: int = 0
+          if scanCppGenericSlot(cppName, i, idx, stars):
+            result.add cppName.substr(chunkStart, chunkEnd)
             chunkStart = i
 
-            let typeInSlot = resolveStarsInCppType(origTyp, idx + 1, stars)
-            if typeInSlot == nil or typeInSlot.kind == tyVoid:
-              result.add(~"void")
-            elif typeInSlot.kind == tyStatic:
-              internalAssert m.config, typeInSlot.n != nil
-              result.add typeInSlot.n.renderTree
-            else:
-              result.add getTypeDescAux(m, typeInSlot, check)
+            let typeInSlot = resolveStarsInCppType(tt, idx + 1, stars)
+            addResultType(typeInSlot)
         else:
           inc i
 
       if chunkStart != 0:
-        result.add cppName.data.substr(chunkStart)
+        result.add cppName.substr(chunkStart)
       else:
-        result = cppName & "<"
-        for i in 1 .. origTyp.len-2:
-          if i > 1: result.add(" COMMA ")
-          result.add(getTypeDescAux(m, origTyp.sons[i], check))
+        result = cppNameAsRope & "<"
+        for needsComma, a in tt.genericInstParams:
+          if needsComma: result.add(" COMMA ")
+          addResultType(a)
         result.add("> ")
       # always call for sideeffects:
       assert t.kind != tyTuple
@@ -767,122 +1037,206 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
       # The resulting type will include commas and these won't play well
       # with the C macros for defining procs such as N_NIMCALL. We must
       # create a typedef for the type and use it in the proc signature:
-      let typedefName = ~"TY" & $sig
-      addf(m.s[cfsTypes], "typedef $1 $2;$n", [result, typedefName])
+      let typedefName = "TY" & $sig
+      m.s[cfsTypes].addf("typedef $1 $2;$n", [result, typedefName])
       m.typeCache[sig] = typedefName
       result = typedefName
     else:
-      when false:
-        if t.sym != nil and t.sym.name.s == "KeyValuePair":
-          if t == origTyp:
-            echo "wtf: came here"
-            writeStackTrace()
-            quit 1
       result = cacheGetType(m.forwTypeCache, sig)
-      if result == nil:
-        when false:
-          if t.sym != nil and t.sym.name.s == "KeyValuePair":
-            # or {sfImportc, sfExportc} * t.sym.flags == {}:
-            if t.loc.r != nil:
-              echo t.kind, " ", hashType t
-              echo origTyp.kind, " ", sig
-            assert t.loc.r == nil
+      if result == "":
         result = getTypeName(m, origTyp, sig)
         m.forwTypeCache[sig] = result
         if not isImportedType(t):
-          addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
-             [structOrUnion(t), result])
+          addForwardStructFormat(m, structOrUnion(t), result)
         assert m.forwTypeCache[sig] == result
       m.typeCache[sig] = result # always call for sideeffects:
       if not incompleteType(t):
         let recdesc = if t.kind != tyTuple: getRecordDesc(m, t, result, check)
                       else: getTupleDesc(m, t, result, check)
         if not isImportedType(t):
-          add(m.s[cfsTypes], recdesc)
-        elif tfIncompleteStruct notin t.flags: addAbiCheck(m, t, result)
+          m.s[cfsTypes].add(recdesc)
+        elif tfIncompleteStruct notin t.flags:
+          discard # addAbiCheck(m, t, result) # already handled elsewhere
   of tySet:
-    result = $t.kind & '_' & getTypeName(m, t.lastSon, hashType t.lastSon)
+    # Don't use the imported name as it may be scoped: 'Foo::SomeKind'
+    result = rope("tySet_")
+    t.elementType.typeName(result)
+    result.add $t.elementType.hashType(m.config)
     m.typeCache[sig] = result
     if not isImportedType(t):
       let s = int(getSize(m.config, t))
       case s
-      of 1, 2, 4, 8: addf(m.s[cfsTypes], "typedef NU$2 $1;$n", [result, rope(s*8)])
-      else: addf(m.s[cfsTypes], "typedef NU8 $1[$2];$n",
+      of 1, 2, 4, 8: m.s[cfsTypes].addf("typedef NU$2 $1;$n", [result, rope(s*8)])
+      else: m.s[cfsTypes].addf("typedef NU8 $1[$2];$n",
              [result, rope(getSize(m.config, t))])
-  of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink,
+  of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink, tyOwned,
      tyUserTypeClass, tyUserTypeClassInst, tyInferred:
-    result = getTypeDescAux(m, lastSon(t), check)
+    result = getTypeDescAux(m, skipModifier(t), check, kind)
   else:
     internalError(m.config, "getTypeDescAux(" & $t.kind & ')')
-    result = nil
+    result = ""
   # fixes bug #145:
   excl(check, t.id)
 
-proc getTypeDesc(m: BModule, typ: PType): Rope =
+
+proc getTypeDesc(m: BModule; typ: PType; kind = dkParam): Rope =
   var check = initIntSet()
-  result = getTypeDescAux(m, typ, check)
+  result = getTypeDescAux(m, typ, check, kind)
 
 type
-  TClosureTypeKind = enum
-    clHalf, clHalfWithEnv, clFull
+  TClosureTypeKind = enum ## In C closures are mapped to 3 different things.
+    clHalf,           ## fn(args) type without the trailing 'void* env' parameter
+    clHalfWithEnv,    ## fn(args, void* env) type with trailing 'void* env' parameter
+    clFull            ## struct {fn(args, void* env), env}
 
-proc getClosureType(m: BModule, t: PType, kind: TClosureTypeKind): Rope =
+proc getClosureType(m: BModule; t: PType, kind: TClosureTypeKind): Rope =
   assert t.kind == tyProc
   var check = initIntSet()
   result = getTempName(m)
-  var rettype, desc: Rope
+  var rettype, desc: Rope = ""
   genProcParams(m, t, rettype, desc, check, declareEnvironment=kind != clHalf)
   if not isImportedType(t):
     if t.callConv != ccClosure or kind != clFull:
-      addf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n",
+      m.s[cfsTypes].addf("typedef $1_PTR($2, $3) $4;$n",
            [rope(CallingConvToStr[t.callConv]), rettype, result, desc])
     else:
-      addf(m.s[cfsTypes], "typedef struct {$n" &
+      m.s[cfsTypes].addf("typedef struct {$n" &
           "N_NIMCALL_PTR($2, ClP_0) $3;$n" &
           "void* ClE_0;$n} $1;$n",
            [result, rettype, desc])
 
 proc finishTypeDescriptions(m: BModule) =
   var i = 0
-  while i < len(m.typeStack):
-    discard getTypeDesc(m, m.typeStack[i])
+  var check = initIntSet()
+  while i < m.typeStack.len:
+    let t = m.typeStack[i]
+    if optSeqDestructors in m.config.globalOptions and t.skipTypes(abstractInst).kind == tySequence:
+      seqV2ContentType(m, t, check)
+    else:
+      discard getTypeDescAux(m, t, check, dkParam)
     inc(i)
+  m.typeStack.setLen 0
+
+proc isReloadable(m: BModule; prc: PSym): bool =
+  return m.hcrOn and sfNonReloadable notin prc.flags
+
+proc isNonReloadable(m: BModule; prc: PSym): bool =
+  return m.hcrOn and sfNonReloadable in prc.flags
+
+proc parseVFunctionDecl(val: string; name, params, retType, superCall: var string; isFnConst, isOverride, isMemberVirtual, isStatic: var bool; isCtor: bool, isFunctor=false) =
+  var afterParams: string = ""
+  if scanf(val, "$*($*)$s$*", name, params, afterParams):
+    if name.strip() == "operator" and params == "": #isFunctor?
+      parseVFunctionDecl(afterParams, name, params, retType, superCall, isFnConst, isOverride, isMemberVirtual, isStatic, isCtor, true)
+      return
+    if name.find("static ") > -1:
+      isStatic = true
+      name = name.replace("static ", "")
+    isFnConst = afterParams.find("const") > -1
+    isOverride = afterParams.find("override") > -1
+    isMemberVirtual = name.find("virtual ") > -1
+    if isMemberVirtual:
+      name = name.replace("virtual ", "")
+    if isFunctor:
+      name = "operator ()"
+    if isCtor:
+      discard scanf(afterParams, ":$s$*", superCall)
+    else:
+      discard scanf(afterParams, "->$s$* ", retType)
+
+  params = "(" & params & ")"
+
+proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl: bool = false) =
+  assert sfCppMember * prc.flags != {}
+  let isCtor = sfConstructor in prc.flags
+  var check = initIntSet()
+  fillBackendName(m, prc)
+  fillLoc(prc.loc, locProc, prc.ast[namePos], OnUnknown)
+  var memberOp = "#." #only virtual
+  var typ: PType
+  if isCtor:
+    typ = prc.typ.returnType
+  else:
+    typ = prc.typ.firstParamType
+  if typ.kind == tyPtr:
+    typ = typ.elementType
+    memberOp = "#->"
+  var typDesc = getTypeDescWeak(m, typ, check, dkParam)
+  let asPtrStr = rope(if asPtr: "_PTR" else: "")
+  var name, params, rettype, superCall: string = ""
+  var isFnConst, isOverride, isMemberVirtual, isStatic: bool = false
+  parseVFunctionDecl(prc.constraint.strVal, name, params, rettype, superCall, isFnConst, isOverride, isMemberVirtual, isStatic, isCtor)
+  genMemberProcParams(m, prc, superCall, rettype, name, params, check, true, false)
+  let isVirtual = sfVirtual in prc.flags or isMemberVirtual
+  var fnConst, override: string = ""
+  if isCtor:
+    name = typDesc
+  if isFnConst:
+    fnConst = " const"
+  if isFwdDecl:
+    if isStatic:
+      result.add "static "
+    if isVirtual:
+      rettype = "virtual " & rettype
+      if isOverride:
+        override = " override"
+    superCall = ""
+  else:
+    if not isCtor:
+      prc.loc.snippet = "$1$2(@)" % [memberOp, name]
+    elif superCall != "":
+      superCall = " : " & superCall
+
+    name = "$1::$2" % [typDesc, name]
 
-template cgDeclFrmt*(s: PSym): string = s.constraint.strVal
+  result.add "N_LIB_PRIVATE "
+  result.addf("$1$2($3, $4)$5$6$7$8",
+        [rope(CallingConvToStr[prc.typ.callConv]), asPtrStr, rettype, name,
+        params, fnConst, override, superCall])
 
-proc genProcHeader(m: BModule, prc: PSym): Rope =
-  var
-    rettype, params: Rope
-  genCLineDir(result, prc.info, m.config)
+proc genProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false) =
   # using static is needed for inline procs
-  if lfExportLib in prc.loc.flags:
-    if isHeaderFile in m.flags:
-      result.add "N_LIB_IMPORT "
-    else:
-      result.add "N_LIB_EXPORT "
-  elif prc.typ.callConv == ccInline:
-    result.add "static "
-  elif {sfImportc, sfExportc} * prc.flags == {}:
-    result.add "N_LIB_PRIVATE "
   var check = initIntSet()
-  fillLoc(prc.loc, locProc, prc.ast[namePos], mangleName(m, prc), OnUnknown)
-  genProcParams(m, prc.typ, rettype, params, check)
+  fillBackendName(m, prc)
+  fillLoc(prc.loc, locProc, prc.ast[namePos], OnUnknown)
+  var rettype, params: Rope = ""
+  genProcParams(m, prc.typ, rettype, params, check, true, false)
+  # handle the 2 options for hotcodereloading codegen - function pointer
+  # (instead of forward declaration) or header for function body with "_actual" postfix
+  let asPtrStr = rope(if asPtr: "_PTR" else: "")
+  var name = prc.loc.snippet
+  if not asPtr and isReloadable(m, prc):
+    name.add("_actual")
   # careful here! don't access ``prc.ast`` as that could reload large parts of
   # the object graph!
-  if prc.constraint.isNil:
-    addf(result, "$1($2, $3)$4",
-         [rope(CallingConvToStr[prc.typ.callConv]), rettype, prc.loc.r,
+  if sfCodegenDecl notin prc.flags:
+    if lfExportLib in prc.loc.flags:
+      if isHeaderFile in m.flags:
+        result.add "N_LIB_IMPORT "
+      else:
+        result.add "N_LIB_EXPORT "
+    elif prc.typ.callConv == ccInline or asPtr or isNonReloadable(m, prc):
+      result.add "static "
+    elif sfImportc notin prc.flags:
+      result.add "N_LIB_PRIVATE "
+    result.addf("$1$2($3, $4)$5",
+         [rope(CallingConvToStr[prc.typ.callConv]), asPtrStr, rettype, name,
          params])
   else:
-    result = prc.cgDeclFrmt % [rettype, prc.loc.r, params]
+    let asPtrStr = if asPtr: (rope("(*") & name & ")") else: name
+    result.add runtimeFormat(prc.cgDeclFrmt, [rettype, asPtrStr, params])
+
 
 # ------------------ type info generation -------------------------------------
 
-proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope
+proc genTypeInfoV1(m: BModule; t: PType; info: TLineInfo): Rope
 proc getNimNode(m: BModule): Rope =
   result = "$1[$2]" % [m.typeNodesName, rope(m.typeNodes)]
   inc(m.typeNodes)
 
+proc tiNameForHcr(m: BModule; name: Rope): Rope =
+  return if m.hcrOn: "(*".rope & name & ")" else: name
+
 proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
                         name, base: Rope; info: TLineInfo) =
   var nimtypeKind: int
@@ -892,229 +1246,245 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
   else:
     nimtypeKind = ord(typ.kind)
 
+  let nameHcr = tiNameForHcr(m, name)
+
   var size: Rope
-  if tfIncompleteStruct in typ.flags: size = rope"void*"
-  else: size = getTypeDesc(m, origType)
-  addf(m.s[cfsTypeInit3],
-       "$1.size = sizeof($2);$n" & "$1.kind = $3;$n" & "$1.base = $4;$n",
-       [name, size, rope(nimtypeKind), base])
+  if tfIncompleteStruct in typ.flags:
+    size = rope"void*"
+  else:
+    size = getTypeDesc(m, origType, dkVar)
+  m.s[cfsTypeInit3].addf(
+    "$1.size = sizeof($2);$n$1.align = NIM_ALIGNOF($2);$n$1.kind = $3;$n$1.base = $4;$n",
+    [nameHcr, size, rope(nimtypeKind), base]
+  )
   # compute type flags for GC optimization
   var flags = 0
   if not containsGarbageCollectedRef(typ): flags = flags or 1
-  if not canFormAcycle(typ): flags = flags or 2
-  #else MessageOut("can contain a cycle: " & typeToString(typ))
+  if not canFormAcycle(m.g.graph, typ): flags = flags or 2
+  #else echo("can contain a cycle: " & typeToString(typ))
   if flags != 0:
-    addf(m.s[cfsTypeInit3], "$1.flags = $2;$n", [name, rope(flags)])
-  discard cgsym(m, "TNimType")
+    m.s[cfsTypeInit3].addf("$1.flags = $2;$n", [nameHcr, rope(flags)])
+  cgsym(m, "TNimType")
   if isDefined(m.config, "nimTypeNames"):
     var typename = typeToString(if origType.typeInst != nil: origType.typeInst
                                 else: origType, preferName)
     if typename == "ref object" and origType.skipTypes(skipPtrs).sym != nil:
       typename = "anon ref object from " & m.config$origType.skipTypes(skipPtrs).sym.info
-    addf(m.s[cfsTypeInit3], "$1.name = $2;$n",
-        [name, makeCstring typename])
-    discard cgsym(m, "nimTypeRoot")
-    addf(m.s[cfsTypeInit3], "$1.nextType = nimTypeRoot; nimTypeRoot=&$1;$n",
-         [name])
-  addf(m.s[cfsVars], "TNimType $1;$n", [name])
-
-proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope;
+    m.s[cfsTypeInit3].addf("$1.name = $2;$n",
+        [nameHcr, makeCString typename])
+    cgsym(m, "nimTypeRoot")
+    m.s[cfsTypeInit3].addf("$1.nextType = nimTypeRoot; nimTypeRoot=&$1;$n",
+         [nameHcr])
+
+  if m.hcrOn:
+    m.s[cfsStrData].addf("static TNimType* $1;$n", [name])
+    m.hcrCreateTypeInfosProc.addf("\thcrRegisterGlobal($2, \"$1\", sizeof(TNimType), NULL, (void**)&$1);$n",
+         [name, getModuleDllPath(m, m.module)])
+  else:
+    m.s[cfsStrData].addf("N_LIB_PRIVATE TNimType $1;$n", [name])
+
+proc genTypeInfoAux(m: BModule; typ, origType: PType, name: Rope;
                     info: TLineInfo) =
   var base: Rope
-  if sonsLen(typ) > 0 and typ.lastSon != nil:
-    var x = typ.lastSon
+  if typ.hasElementType and typ.last != nil:
+    var x = typ.last
     if typ.kind == tyObject: x = x.skipTypes(skipPtrs)
     if typ.kind == tyPtr and x.kind == tyObject and incompleteType(x):
       base = rope("0")
     else:
-      base = genTypeInfo(m, x, info)
+      base = genTypeInfoV1(m, x, info)
   else:
     base = rope("0")
   genTypeInfoAuxBase(m, typ, origType, name, base, info)
 
-proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): 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[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)]
+  result = "NimDT_$1_$2" % [rope($hashType(objtype, m.config)), rope(d.name.s.mangle)]
 
-proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): Rope =
-  discard cgsym(m, "TNimNode")
+proc rope(arg: Int128): Rope = rope($arg)
+
+proc discriminatorTableDecl(m: BModule; objtype: PType, d: PSym): Rope =
+  cgsym(m, "TNimNode")
   var tmp = discriminatorTableName(m, objtype, d)
   result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(m.config, d.typ)+1)]
 
-proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
+proc genTNimNodeArray(m: BModule; name: Rope, size: Rope) =
+  if m.hcrOn:
+    m.s[cfsData].addf("static TNimNode** $1;$n", [name])
+    m.hcrCreateTypeInfosProc.addf("\thcrRegisterGlobal($3, \"$1\", sizeof(TNimNode*) * $2, NULL, (void**)&$1);$n",
+         [name, size, getModuleDllPath(m, m.module)])
+  else:
+    m.s[cfsTypeInit1].addf("static TNimNode* $1[$2];$n", [name, size])
+
+proc genObjectFields(m: BModule; typ, origType: PType, n: PNode, expr: Rope;
                      info: TLineInfo) =
   case n.kind
   of nkRecList:
-    var L = sonsLen(n)
-    if L == 1:
-      genObjectFields(m, typ, origType, n.sons[0], expr, info)
-    elif L > 0:
-      var tmp = getTempName(m)
-      addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [tmp, rope(L)])
-      for i in countup(0, L-1):
+    if n.len == 1:
+      genObjectFields(m, typ, origType, n[0], expr, info)
+    elif n.len > 0:
+      var tmp = getTempName(m) & "_" & $n.len
+      genTNimNodeArray(m, tmp, rope(n.len))
+      for i in 0..<n.len:
         var tmp2 = getNimNode(m)
-        addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, rope(i), tmp2])
-        genObjectFields(m, typ, origType, n.sons[i], tmp2, info)
-      addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
-           [expr, rope(L), tmp])
+        m.s[cfsTypeInit3].addf("$1[$2] = &$3;$n", [tmp, rope(i), tmp2])
+        genObjectFields(m, typ, origType, n[i], tmp2, info)
+      m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
+           [expr, rope(n.len), tmp])
     else:
-      addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2;$n", [expr, rope(L)])
+      m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 2;$n", [expr, rope(n.len)])
   of nkRecCase:
-    assert(n.sons[0].kind == nkSym)
-    var field = n.sons[0].sym
+    assert(n[0].kind == nkSym)
+    var field = n[0].sym
     var tmp = discriminatorTableName(m, typ, field)
     var L = lengthOrd(m.config, field.typ)
     assert L > 0
-    if field.loc.r == nil: fillObjectFields(m, typ)
+    if field.loc.snippet == "": fillObjectFields(m, typ)
     if field.loc.t == nil:
       internalError(m.config, n.info, "genObjectFields")
-    addf(m.s[cfsTypeInit3], "$1.kind = 3;$n" &
+    m.s[cfsTypeInit3].addf("$1.kind = 3;$n" &
         "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
         "$1.name = $5;$n" & "$1.sons = &$6[0];$n" &
-        "$1.len = $7;$n", [expr, getTypeDesc(m, origType), field.loc.r,
-                           genTypeInfo(m, field.typ, info),
+        "$1.len = $7;$n", [expr, getTypeDesc(m, origType, dkVar), field.loc.snippet,
+                           genTypeInfoV1(m, field.typ, info),
                            makeCString(field.name.s),
                            tmp, rope(L)])
-    addf(m.s[cfsData], "TNimNode* $1[$2];$n", [tmp, rope(L+1)])
-    for i in countup(1, sonsLen(n)-1):
-      var b = n.sons[i]           # branch
+    m.s[cfsData].addf("TNimNode* $1[$2];$n", [tmp, rope(L+1)])
+    for i in 1..<n.len:
+      var b = n[i]           # branch
       var tmp2 = getNimNode(m)
       genObjectFields(m, typ, origType, lastSon(b), tmp2, info)
       case b.kind
       of nkOfBranch:
-        if sonsLen(b) < 2:
+        if b.len < 2:
           internalError(m.config, b.info, "genObjectFields; nkOfBranch broken")
-        for j in countup(0, sonsLen(b) - 2):
-          if b.sons[j].kind == nkRange:
-            var x = int(getOrdValue(b.sons[j].sons[0]))
-            var y = int(getOrdValue(b.sons[j].sons[1]))
+        for j in 0..<b.len - 1:
+          if b[j].kind == nkRange:
+            var x = toInt(getOrdValue(b[j][0]))
+            var y = toInt(getOrdValue(b[j][1]))
             while x <= y:
-              addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, rope(x), tmp2])
+              m.s[cfsTypeInit3].addf("$1[$2] = &$3;$n", [tmp, rope(x), tmp2])
               inc(x)
           else:
-            addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n",
-                 [tmp, rope(getOrdValue(b.sons[j])), tmp2])
+            m.s[cfsTypeInit3].addf("$1[$2] = &$3;$n",
+                 [tmp, rope(getOrdValue(b[j])), tmp2])
       of nkElse:
-        addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n",
+        m.s[cfsTypeInit3].addf("$1[$2] = &$3;$n",
              [tmp, rope(L), tmp2])
       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.snippet == "": fillObjectFields(m, typ)
       if field.loc.t == nil:
         internalError(m.config, n.info, "genObjectFields")
-      addf(m.s[cfsTypeInit3], "$1.kind = 1;$n" &
+      m.s[cfsTypeInit3].addf("$1.kind = 1;$n" &
           "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
-          "$1.name = $5;$n", [expr, getTypeDesc(m, origType),
-          field.loc.r, genTypeInfo(m, field.typ, info), makeCString(field.name.s)])
+          "$1.name = $5;$n", [expr, getTypeDesc(m, origType, dkVar),
+          field.loc.snippet, genTypeInfoV1(m, field.typ, info), makeCString(field.name.s)])
   else: internalError(m.config, n.info, "genObjectFields")
 
-proc genObjectInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) =
-  if typ.kind == tyObject:
-    if incompleteType(typ):
-      localError(m.config, info, "request for RTTI generation for incomplete object: " &
-                        typeToString(typ))
-    genTypeInfoAux(m, typ, origType, name, info)
-  else:
-    genTypeInfoAuxBase(m, typ, origType, name, rope("0"), info)
+proc genObjectInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) =
+  assert typ.kind == tyObject
+  if incompleteType(typ):
+    localError(m.config, info, "request for RTTI generation for incomplete object: " &
+                      typeToString(typ))
+  genTypeInfoAux(m, typ, origType, name, info)
   var tmp = getNimNode(m)
-  if not isImportedType(typ):
+  if (not isImportedType(typ)) or tfCompleteStruct in typ.flags:
     genObjectFields(m, typ, origType, typ.n, tmp, info)
-  addf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, tmp])
-  var t = typ.sons[0]
+  m.s[cfsTypeInit3].addf("$1.node = &$2;$n", [tiNameForHcr(m, name), tmp])
+  var t = typ.baseClass
   while t != nil:
     t = t.skipTypes(skipPtrs)
     t.flags.incl tfObjHasKids
-    t = t.sons[0]
+    t = t.baseClass
 
-proc genTupleInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) =
+proc genTupleInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) =
   genTypeInfoAuxBase(m, typ, typ, name, rope("0"), info)
   var expr = getNimNode(m)
-  var length = sonsLen(typ)
-  if length > 0:
-    var tmp = getTempName(m)
-    addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [tmp, rope(length)])
-    for i in countup(0, length - 1):
-      var a = typ.sons[i]
+  if not typ.isEmptyTupleType:
+    var tmp = getTempName(m) & "_" & $typ.kidsLen
+    genTNimNodeArray(m, tmp, rope(typ.kidsLen))
+    for i, a in typ.ikids:
       var tmp2 = getNimNode(m)
-      addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, rope(i), tmp2])
-      addf(m.s[cfsTypeInit3], "$1.kind = 1;$n" &
+      m.s[cfsTypeInit3].addf("$1[$2] = &$3;$n", [tmp, rope(i), tmp2])
+      m.s[cfsTypeInit3].addf("$1.kind = 1;$n" &
           "$1.offset = offsetof($2, Field$3);$n" &
           "$1.typ = $4;$n" &
           "$1.name = \"Field$3\";$n",
-           [tmp2, getTypeDesc(m, origType), rope(i), genTypeInfo(m, a, info)])
-    addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
-         [expr, rope(length), tmp])
+           [tmp2, getTypeDesc(m, origType, dkVar), rope(i), genTypeInfoV1(m, a, info)])
+    m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
+         [expr, rope(typ.kidsLen), tmp])
   else:
-    addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2;$n",
-         [expr, rope(length)])
-  addf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, expr])
+    m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 2;$n",
+         [expr, rope(typ.kidsLen)])
+  m.s[cfsTypeInit3].addf("$1.node = &$2;$n", [tiNameForHcr(m, name), expr])
 
-proc genEnumInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
+proc genEnumInfo(m: BModule; typ: PType, name: Rope; info: TLineInfo) =
   # Type information for enumerations is quite heavy, so we do some
   # optimizations here: The ``typ`` field is never set, as it is redundant
   # anyway. We generate a cstring array and a loop over it. Exceptional
   # positions will be reset after the loop.
   genTypeInfoAux(m, typ, typ, name, info)
-  var nodePtrs = getTempName(m)
-  var length = sonsLen(typ.n)
-  addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n",
-       [nodePtrs, rope(length)])
-  var enumNames, specialCases: Rope
+  var nodePtrs = getTempName(m) & "_" & $typ.n.len
+  genTNimNodeArray(m, nodePtrs, rope(typ.n.len))
+  var enumNames, specialCases: Rope = ""
   var firstNimNode = m.typeNodes
   var hasHoles = false
-  for i in countup(0, length - 1):
-    assert(typ.n.sons[i].kind == nkSym)
-    var field = typ.n.sons[i].sym
+  for i in 0..<typ.n.len:
+    assert(typ.n[i].kind == nkSym)
+    var field = typ.n[i].sym
     var elemNode = getNimNode(m)
     if field.ast == nil:
       # no explicit string literal for the enum field, so use field.name:
-      add(enumNames, makeCString(field.name.s))
+      enumNames.add(makeCString(field.name.s))
     else:
-      add(enumNames, makeCString(field.ast.strVal))
-    if i < length - 1: add(enumNames, ", \L")
+      enumNames.add(makeCString(field.ast.strVal))
+    if i < typ.n.len - 1: enumNames.add(", \L")
     if field.position != i or tfEnumHasHoles in typ.flags:
-      addf(specialCases, "$1.offset = $2;$n", [elemNode, rope(field.position)])
+      specialCases.addf("$1.offset = $2;$n", [elemNode, rope(field.position)])
       hasHoles = true
   var enumArray = getTempName(m)
   var counter = getTempName(m)
-  addf(m.s[cfsTypeInit1], "NI $1;$n", [counter])
-  addf(m.s[cfsTypeInit1], "static char* NIM_CONST $1[$2] = {$n$3};$n",
-       [enumArray, rope(length), enumNames])
-  addf(m.s[cfsTypeInit3], "for ($1 = 0; $1 < $2; $1++) {$n" &
+  m.s[cfsTypeInit1].addf("NI $1;$n", [counter])
+  m.s[cfsTypeInit1].addf("static char* NIM_CONST $1[$2] = {$n$3};$n",
+       [enumArray, rope(typ.n.len), enumNames])
+  m.s[cfsTypeInit3].addf("for ($1 = 0; $1 < $2; $1++) {$n" &
       "$3[$1+$4].kind = 1;$n" & "$3[$1+$4].offset = $1;$n" &
       "$3[$1+$4].name = $5[$1];$n" & "$6[$1] = &$3[$1+$4];$n" & "}$n", [counter,
-      rope(length), m.typeNodesName, rope(firstNimNode), enumArray, nodePtrs])
-  add(m.s[cfsTypeInit3], specialCases)
-  addf(m.s[cfsTypeInit3],
+      rope(typ.n.len), m.typeNodesName, rope(firstNimNode), enumArray, nodePtrs])
+  m.s[cfsTypeInit3].add(specialCases)
+  m.s[cfsTypeInit3].addf(
        "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n$4.node = &$1;$n",
-       [getNimNode(m), rope(length), nodePtrs, name])
+       [getNimNode(m), rope(typ.n.len), nodePtrs, tiNameForHcr(m, name)])
   if hasHoles:
     # 1 << 2 is {ntfEnumHole}
-    addf(m.s[cfsTypeInit3], "$1.flags = 1<<2;$n", [name])
+    m.s[cfsTypeInit3].addf("$1.flags = 1<<2;$n", [tiNameForHcr(m, name)])
 
-proc genSetInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
-  assert(typ.sons[0] != nil)
+proc genSetInfo(m: BModule; typ: PType, name: Rope; info: TLineInfo) =
+  assert(typ.elementType != nil)
   genTypeInfoAux(m, typ, typ, name, info)
   var tmp = getNimNode(m)
-  addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 0;$n" & "$3.node = &$1;$n",
-       [tmp, rope(firstOrd(m.config, typ)), name])
+  m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 0;$n$3.node = &$1;$n",
+       [tmp, rope(firstOrd(m.config, typ)), tiNameForHcr(m, name)])
 
-proc genArrayInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
-  genTypeInfoAuxBase(m, typ, typ, name, genTypeInfo(m, typ.sons[1], info), info)
+proc genArrayInfo(m: BModule; typ: PType, name: Rope; info: TLineInfo) =
+  genTypeInfoAuxBase(m, typ, typ, name, genTypeInfoV1(m, typ.elementType, info), info)
 
 proc fakeClosureType(m: BModule; owner: PSym): PType =
   # we generate the same RTTI as for a tuple[pointer, ref tuple[]]
-  result = newType(tyTuple, owner)
-  result.rawAddSon(newType(tyPointer, owner))
-  var r = newType(tyRef, owner)
-  let obj = createObj(m.g.graph, owner, owner.info, final=false)
+  result = newType(tyTuple, m.idgen, owner)
+  result.rawAddSon(newType(tyPointer, m.idgen, owner))
+  var r = newType(tyRef, m.idgen, owner)
+  let obj = createObj(m.g.graph, m.idgen, owner, owner.info, final=false)
   r.rawAddSon(obj)
   result.rawAddSon(r)
 
@@ -1122,79 +1492,440 @@ include ccgtrav
 
 proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
   genProc(m, s)
-  addf(m.s[cfsTypeInit3], "$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n",
-     [result, s.loc.r])
+  m.s[cfsTypeInit3].addf("$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n",
+     [result, s.loc.snippet])
+
+proc declareNimType(m: BModule; name: string; str: Rope, module: int) =
+  let nr = rope(name)
+  if m.hcrOn:
+    m.s[cfsStrData].addf("static $2* $1;$n", [str, nr])
+    m.s[cfsTypeInit1].addf("\t$1 = ($3*)hcrGetGlobal($2, \"$1\");$n",
+          [str, getModuleDllPath(m, module), nr])
+  else:
+    m.s[cfsStrData].addf("extern $2 $1;$n", [str, nr])
+
+proc genTypeInfo2Name(m: BModule; t: PType): Rope =
+  var it = t
+  it = it.skipTypes(skipPtrs)
+  if it.sym != nil and tfFromGeneric notin it.flags:
+    var m = it.sym.owner
+    while m != nil and m.kind != skModule: m = m.owner
+    if m == nil or sfSystemModule in m.flags:
+      # produce short names for system types:
+      result = it.sym.name.s
+    else:
+      var p = m.owner
+      result = ""
+      if p != nil and p.kind == skPackage:
+        result.add p.name.s & "."
+      result.add m.name.s & "."
+      result.add it.sym.name.s
+  else:
+    result = $hashType(it, m.config)
+  result = makeCString(result)
+
+proc isTrivialProc(g: ModuleGraph; s: PSym): bool {.inline.} = getBody(g, s).len == 0
+
+proc generateRttiDestructor(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
+              info: TLineInfo; idgen: IdGenerator; theProc: PSym): PSym =
+  # the wrapper is roughly like:
+  # proc rttiDestroy(x: pointer) =
+  #   `=destroy`(cast[ptr T](x)[])
+  let procname = getIdent(g.cache, "rttiDestroy")
+  result = newSym(skProc, procname, idgen, owner, info)
+  let dest = newSym(skParam, getIdent(g.cache, "dest"), idgen, result, info)
+
+  dest.typ = getSysType(g, info, tyPointer)
+
+  result.typ = newProcType(info, idgen, owner)
+  result.typ.addParam dest
+
+  var n = newNodeI(nkProcDef, info, bodyPos+1)
+  for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info)
+  n[namePos] = newSymNode(result)
+  n[paramsPos] = result.typ.n
+  let body = newNodeI(nkStmtList, info)
+  let castType = makePtrType(typ, idgen)
+  if theProc.typ.firstParamType.kind != tyVar:
+    body.add newTreeI(nkCall, info, newSymNode(theProc), newDeref(newTreeIT(
+      nkCast, info, castType, newNodeIT(nkType, info, castType),
+      newSymNode(dest)
+    ))
+    )
+  else:
+    let addrOf = newNodeIT(nkHiddenAddr, info, theProc.typ.firstParamType)
+    addrOf.add newDeref(newTreeIT(
+      nkCast, info, castType, newNodeIT(nkType, info, castType),
+      newSymNode(dest)
+    ))
+    body.add newTreeI(nkCall, info, newSymNode(theProc),
+      addrOf
+    )
+  n[bodyPos] = body
+  result.ast = n
+
+  incl result.flags, sfFromGeneric
+  incl result.flags, sfGeneratedOp
+
+proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp; result: var Rope) =
+  let theProc = getAttachedOp(m.g.graph, t, op)
+  if theProc != nil and not isTrivialProc(m.g.graph, theProc):
+    # the prototype of a destructor is ``=destroy(x: var T)`` and that of a
+    # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
+    # convention at least:
+    if theProc.typ == nil or theProc.typ.callConv != ccNimCall:
+      localError(m.config, info,
+        theProc.name.s & " needs to have the 'nimcall' calling convention")
+
+    if op == attachedDestructor:
+      let wrapper = generateRttiDestructor(m.g.graph, t, theProc.owner, attachedDestructor,
+                theProc.info, m.idgen, theProc)
+      genProc(m, wrapper)
+      result.add wrapper.loc.snippet
+    else:
+      genProc(m, theProc)
+      result.add theProc.loc.snippet
+
+    when false:
+      if not canFormAcycle(m.g.graph, t) and op == attachedTrace:
+        echo "ayclic but has this =trace ", t, " ", theProc.ast
+  else:
+    when false:
+      if op == attachedTrace and m.config.selectedGC == gcOrc and
+          containsGarbageCollectedRef(t):
+        # unfortunately this check is wrong for an object type that only contains
+        # .cursor fields like 'Node' inside 'cycleleak'.
+        internalError(m.config, info, "no attached trace proc found")
+    result.add rope("NIM_NIL")
+
+proc getObjDepth(t: PType): int16 =
+  var x = t
+  result = -1
+  while x != nil:
+    x = skipTypes(x, skipPtrs)
+    x = x[0]
+    inc(result)
+
+proc genDisplayElem(d: MD5Digest): uint32 =
+  result = 0
+  for i in 0..3:
+    result += uint32(d[i])
+    result = result shl 8
+
+proc genDisplay(m: BModule; t: PType, depth: int): Rope =
+  result = Rope"{"
+  var x = t
+  var seqs = newSeq[string](depth+1)
+  var i = 0
+  while x != nil:
+    x = skipTypes(x, skipPtrs)
+    seqs[i] = $genDisplayElem(MD5Digest(hashType(x, m.config)))
+    x = x[0]
+    inc i
+
+  for i in countdown(depth, 1):
+    result.add seqs[i] & ", "
+  result.add seqs[0]
+  result.add "}"
+
+proc genVTable(seqs: seq[PSym]): string =
+  result = "{"
+  for i in 0..<seqs.len:
+    if i > 0: result.add ", "
+    result.add "(void *) " & seqs[i].loc.snippet
+  result.add "}"
+
+proc genTypeInfoV2OldImpl(m: BModule; t, origType: PType, name: Rope; info: TLineInfo) =
+  cgsym(m, "TNimTypeV2")
+  m.s[cfsStrData].addf("N_LIB_PRIVATE TNimTypeV2 $1;$n", [name])
+
+  var flags = 0
+  if not canFormAcycle(m.g.graph, t): flags = flags or 1
+
+  var typeEntry = newRopeAppender()
+  addf(typeEntry, "$1.destructor = (void*)", [name])
+  genHook(m, t, info, attachedDestructor, typeEntry)
 
-proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
+  addf(typeEntry, "; $1.traceImpl = (void*)", [name])
+  genHook(m, t, info, attachedTrace, typeEntry)
+
+  let objDepth = if t.kind == tyObject: getObjDepth(t) else: -1
+
+  if t.kind in {tyObject, tyDistinct} and incompleteType(t):
+    localError(m.config, info, "request for RTTI generation for incomplete object: " &
+              typeToString(t))
+
+  if isDefined(m.config, "nimTypeNames"):
+    var typeName: Rope
+    if t.kind in {tyObject, tyDistinct}:
+      typeName = genTypeInfo2Name(m, t)
+    else:
+      typeName = rope("NIM_NIL")
+    addf(typeEntry, "; $1.name = $2", [name, typeName])
+  addf(typeEntry, "; $1.size = sizeof($2); $1.align = (NI16) NIM_ALIGNOF($2); $1.depth = $3; $1.flags = $4;",
+    [name, getTypeDesc(m, t), rope(objDepth), rope(flags)])
+
+  if objDepth >= 0:
+    let objDisplay = genDisplay(m, t, objDepth)
+    let objDisplayStore = getTempName(m)
+    m.s[cfsVars].addf("static $1 $2[$3] = $4;$n", [getTypeDesc(m, getSysType(m.g.graph, unknownLineInfo, tyUInt32), dkVar), objDisplayStore, rope(objDepth+1), objDisplay])
+    addf(typeEntry, "$1.display = $2;$n", [name, rope(objDisplayStore)])
+
+  let dispatchMethods = toSeq(getMethodsPerType(m.g.graph, t))
+  if dispatchMethods.len > 0:
+    let vTablePointerName = getTempName(m)
+    m.s[cfsVars].addf("static void* $1[$2] = $3;$n", [vTablePointerName, rope(dispatchMethods.len), genVTable(dispatchMethods)])
+    for i in dispatchMethods:
+      genProcPrototype(m, i)
+    addf(typeEntry, "$1.vTable = $2;$n", [name, vTablePointerName])
+
+  m.s[cfsTypeInit3].add typeEntry
+
+  if t.kind == tyObject and t.baseClass != nil and optEnableDeepCopy in m.config.globalOptions:
+    discard genTypeInfoV1(m, t, info)
+
+proc genTypeInfoV2Impl(m: BModule; t, origType: PType, name: Rope; info: TLineInfo) =
+  cgsym(m, "TNimTypeV2")
+  m.s[cfsStrData].addf("N_LIB_PRIVATE TNimTypeV2 $1;$n", [name])
+
+  var flags = 0
+  if not canFormAcycle(m.g.graph, t): flags = flags or 1
+
+  var typeEntry = newRopeAppender()
+  addf(typeEntry, "N_LIB_PRIVATE TNimTypeV2 $1 = {", [name])
+  add(typeEntry, ".destructor = (void*)")
+  genHook(m, t, info, attachedDestructor, typeEntry)
+
+  let objDepth = if t.kind == tyObject: getObjDepth(t) else: -1
+
+  if t.kind in {tyObject, tyDistinct} and incompleteType(t):
+    localError(m.config, info, "request for RTTI generation for incomplete object: " &
+              typeToString(t))
+
+  addf(typeEntry, ", .size = sizeof($1), .align = (NI16) NIM_ALIGNOF($1), .depth = $2",
+    [getTypeDesc(m, t), rope(objDepth)])
+
+  if objDepth >= 0:
+    let objDisplay = genDisplay(m, t, objDepth)
+    let objDisplayStore = getTempName(m)
+    m.s[cfsVars].addf("static NIM_CONST $1 $2[$3] = $4;$n", [getTypeDesc(m, getSysType(m.g.graph, unknownLineInfo, tyUInt32), dkVar), objDisplayStore, rope(objDepth+1), objDisplay])
+    addf(typeEntry, ", .display = $1", [rope(objDisplayStore)])
+  if isDefined(m.config, "nimTypeNames"):
+    var typeName: Rope
+    if t.kind in {tyObject, tyDistinct}:
+      typeName = genTypeInfo2Name(m, t)
+    else:
+      typeName = rope("NIM_NIL")
+    addf(typeEntry, ", .name = $1", [typeName])
+  add(typeEntry, ", .traceImpl = (void*)")
+  genHook(m, t, info, attachedTrace, typeEntry)
+
+  let dispatchMethods = toSeq(getMethodsPerType(m.g.graph, t))
+  if dispatchMethods.len > 0:
+    addf(typeEntry, ", .flags = $1", [rope(flags)])
+    for i in dispatchMethods:
+      genProcPrototype(m, i)
+    addf(typeEntry, ", .vTable = $1};$n", [genVTable(dispatchMethods)])
+    m.s[cfsVars].add typeEntry
+  else:
+    addf(typeEntry, ", .flags = $1};$n", [rope(flags)])
+    m.s[cfsVars].add typeEntry
+
+  if t.kind == tyObject and t.baseClass != nil and optEnableDeepCopy in m.config.globalOptions:
+    discard genTypeInfoV1(m, t, info)
+
+proc genTypeInfoV2(m: BModule; t: PType; info: TLineInfo): Rope =
+  let origType = t
+  # distinct types can have their own destructors
+  var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses - {tyDistinct})
+
+  let prefixTI = if m.hcrOn: "(" else: "(&"
+
+  let sig = hashType(origType, m.config)
+  result = m.typeInfoMarkerV2.getOrDefault(sig)
+  if result != "":
+    return prefixTI.rope & result & ")".rope
+
+  let marker = m.g.typeInfoMarkerV2.getOrDefault(sig)
+  if marker.str != "":
+    cgsym(m, "TNimTypeV2")
+    declareNimType(m, "TNimTypeV2", marker.str, marker.owner)
+    # also store in local type section:
+    m.typeInfoMarkerV2[sig] = marker.str
+    return prefixTI.rope & marker.str & ")".rope
+
+  result = "NTIv2$1_" % [rope($sig)]
+  m.typeInfoMarkerV2[sig] = result
+
+  let owner = t.skipTypes(typedescPtrs).itemId.module
+  if owner != m.module.position and moduleOpenForCodegen(m.g.graph, FileIndex owner):
+    # make sure the type info is created in the owner module
+    discard genTypeInfoV2(m.g.modules[owner], origType, info)
+    # reference the type info as extern here
+    cgsym(m, "TNimTypeV2")
+    declareNimType(m, "TNimTypeV2", result, owner)
+    return prefixTI.rope & result & ")".rope
+
+  m.g.typeInfoMarkerV2[sig] = (str: result, owner: owner)
+  if m.compileToCpp or m.hcrOn:
+    genTypeInfoV2OldImpl(m, t, origType, result, info)
+  else:
+    genTypeInfoV2Impl(m, t, origType, result, info)
+  result = prefixTI.rope & result & ")".rope
+
+proc openArrayToTuple(m: BModule; t: PType): PType =
+  result = newType(tyTuple, m.idgen, t.owner)
+  let p = newType(tyPtr, m.idgen, t.owner)
+  let a = newType(tyUncheckedArray, m.idgen, t.owner)
+  a.add t.elementType
+  p.add a
+  result.add p
+  result.add getSysType(m.g.graph, t.owner.info, tyInt)
+
+proc typeToC(t: PType): string =
+  ## Just for more readable names, the result doesn't have
+  ## to be unique.
+  let s = typeToString(t)
+  result = newStringOfCap(s.len)
+  for c in s:
+    case c
+    of 'a'..'z':
+      result.add c
+    of 'A'..'Z':
+      result.add toLowerAscii(c)
+    of ' ':
+      discard
+    of ',':
+      result.add '_'
+    of '.':
+      result.add 'O'
+    of '[', '(', '{':
+      result.add 'L'
+    of ']', ')', '}':
+      result.add 'T'
+    else:
+      # We mangle upper letters and digits too so that there cannot
+      # be clashes with our special meanings
+      result.addInt ord(c)
+
+proc genTypeInfoV1(m: BModule; t: PType; info: TLineInfo): Rope =
   let origType = t
   var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses)
 
-  let sig = hashType(origType)
+  let prefixTI = if m.hcrOn: "(" else: "(&"
+
+  let sig = hashType(origType, m.config)
   result = m.typeInfoMarker.getOrDefault(sig)
-  if result != nil:
-    return "(&".rope & result & ")".rope
-
-  result = m.g.typeInfoMarker.getOrDefault(sig)
-  if result != nil:
-    discard cgsym(m, "TNimType")
-    discard cgsym(m, "TNimNode")
-    addf(m.s[cfsVars], "extern TNimType $1;$n", [result])
+  if result != "":
+    return prefixTI.rope & result & ")".rope
+
+  let marker = m.g.typeInfoMarker.getOrDefault(sig)
+  if marker.str != "":
+    cgsym(m, "TNimType")
+    cgsym(m, "TNimNode")
+    declareNimType(m, "TNimType", marker.str, marker.owner)
     # also store in local type section:
-    m.typeInfoMarker[sig] = result
-    return "(&".rope & result & ")".rope
+    m.typeInfoMarker[sig] = marker.str
+    return prefixTI.rope & marker.str & ")".rope
 
-  result = "NTI$1_" % [rope($sig)]
+  result = "NTI$1$2_" % [rope(typeToC(t)), rope($sig)]
   m.typeInfoMarker[sig] = result
 
-  let owner = t.skipTypes(typedescPtrs).owner.getModule
-  if owner != m.module:
+  let old = m.g.graph.emittedTypeInfo.getOrDefault($result)
+  if old != FileIndex(0):
+    cgsym(m, "TNimType")
+    cgsym(m, "TNimNode")
+    declareNimType(m, "TNimType", result, old.int)
+    return prefixTI.rope & result & ")".rope
+
+  var owner = t.skipTypes(typedescPtrs).itemId.module
+  if owner != m.module.position and moduleOpenForCodegen(m.g.graph, FileIndex owner):
     # make sure the type info is created in the owner module
-    discard genTypeInfo(m.g.modules[owner.position], origType, info)
+    discard genTypeInfoV1(m.g.modules[owner], origType, info)
     # reference the type info as extern here
-    discard cgsym(m, "TNimType")
-    discard cgsym(m, "TNimNode")
-    addf(m.s[cfsVars], "extern TNimType $1;$n", [result])
-    return "(&".rope & result & ")".rope
+    cgsym(m, "TNimType")
+    cgsym(m, "TNimNode")
+    declareNimType(m, "TNimType", result, owner)
+    return prefixTI.rope & result & ")".rope
+  else:
+    owner = m.module.position.int32
+
+  m.g.typeInfoMarker[sig] = (str: result, owner: owner)
+  rememberEmittedTypeInfo(m.g.graph, FileIndex(owner), $result)
 
-  m.g.typeInfoMarker[sig] = result
   case t.kind
   of tyEmpty, tyVoid: result = rope"0"
-  of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar, tyLent:
+  of tyPointer, tyBool, tyChar, tyCstring, tyString, tyInt..tyUInt64, tyVar, tyLent:
     genTypeInfoAuxBase(m, t, t, result, rope"0", info)
   of tyStatic:
-    if t.n != nil: result = genTypeInfo(m, lastSon t, info)
-    else: internalError(m.config, "genTypeInfo(" & $t.kind & ')')
+    if t.n != nil: result = genTypeInfoV1(m, skipModifier t, info)
+    else: internalError(m.config, "genTypeInfoV1(" & $t.kind & ')')
   of tyUserTypeClasses:
     internalAssert m.config, t.isResolvedUserTypeClass
-    return genTypeInfo(m, t.lastSon, info)
+    return genTypeInfoV1(m, t.skipModifier, info)
   of tyProc:
     if t.callConv != ccClosure:
       genTypeInfoAuxBase(m, t, t, result, rope"0", info)
     else:
       let x = fakeClosureType(m, t.owner)
       genTupleInfo(m, x, x, result, info)
-  of tySequence, tyRef, tyOptAsRef:
+  of tySequence:
     genTypeInfoAux(m, t, t, result, info)
-    if m.config.selectedGC >= gcMarkAndSweep:
+    if m.config.selectedGC in {gcMarkAndSweep, gcRefc, gcGo}:
       let markerProc = genTraverseProc(m, origType, sig)
-      addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [result, markerProc])
-  of tyPtr, tyRange: genTypeInfoAux(m, t, t, result, info)
+      m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc])
+  of tyRef:
+    genTypeInfoAux(m, t, t, result, info)
+    if m.config.selectedGC in {gcMarkAndSweep, gcRefc, gcGo}:
+      let markerProc = genTraverseProc(m, origType, sig)
+      m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc])
+  of tyPtr, tyRange, tyUncheckedArray: genTypeInfoAux(m, t, t, result, info)
   of tyArray: genArrayInfo(m, t, result, info)
   of tySet: genSetInfo(m, t, result, info)
   of tyEnum: genEnumInfo(m, t, result, info)
-  of tyObject: genObjectInfo(m, t, origType, result, info)
+  of tyObject:
+    genObjectInfo(m, t, origType, result, info)
   of tyTuple:
     # if t.n != nil: genObjectInfo(m, t, result)
     # else:
     # BUGFIX: use consistently RTTI without proper field names; otherwise
     # results are not deterministic!
     genTupleInfo(m, t, origType, result, info)
-  else: internalError(m.config, "genTypeInfo(" & $t.kind & ')')
-  if t.deepCopy != nil:
-    genDeepCopyProc(m, t.deepCopy, result)
-  elif origType.deepCopy != nil:
-    genDeepCopyProc(m, origType.deepCopy, result)
-  result = "(&".rope & result & ")".rope
+  of tyOpenArray:
+    let x = openArrayToTuple(m, t)
+    genTupleInfo(m, x, origType, result, info)
+  else: internalError(m.config, "genTypeInfoV1(" & $t.kind & ')')
+
+  var op = getAttachedOp(m.g.graph, t, attachedDeepCopy)
+  if op == nil:
+    op = getAttachedOp(m.g.graph, origType, attachedDeepCopy)
+  if op != nil:
+    genDeepCopyProc(m, op, result)
+
+  if optTinyRtti in m.config.globalOptions and t.kind == tyObject and sfImportc notin t.sym.flags:
+    let v2info = genTypeInfoV2(m, origType, info)
+    addf(m.s[cfsTypeInit3], "$1->typeInfoV1 = (void*)&$2; $2.typeInfoV2 = (void*)$1;$n", [
+      v2info, result])
+
+  result = prefixTI.rope & result & ")".rope
+
+proc genTypeInfo*(config: ConfigRef, m: BModule; t: PType; info: TLineInfo): Rope =
+  if optTinyRtti in config.globalOptions:
+    result = genTypeInfoV2(m, t, info)
+  else:
+    result = genTypeInfoV1(m, t, info)
 
 proc genTypeSection(m: BModule, n: PNode) =
-  discard
+  var intSet = initIntSet()
+  for i in 0..<n.len:
+    if len(n[i]) == 0: continue
+    if n[i][0].kind != nkPragmaExpr: continue
+    for p in 0..<n[i][0].len:
+      if (n[i][0][p].kind notin {nkSym, nkPostfix}): continue
+      var s = n[i][0][p]
+      if s.kind == nkPostfix:
+        s = n[i][0][p][1]
+      if {sfExportc, sfCompilerProc} * s.sym.flags == {sfExportc}:
+        discard getTypeDescAux(m, s.typ, intSet, descKindFromSymKind(s.sym.kind))
+        if m.g.generatedHeader != nil:
+          discard getTypeDescAux(m.g.generatedHeader, s.typ, intSet, descKindFromSymKind(s.sym.kind))
diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim
index 75cd3d35d..c0e574186 100644
--- a/compiler/ccgutils.nim
+++ b/compiler/ccgutils.nim
@@ -10,47 +10,55 @@
 # This module declares some helpers for the C code generator.
 
 import
-  ast, astalgo, ropes, hashes, strutils, types, msgs, wordrecg,
-  platform, trees, options
+  ast, types, msgs, wordrecg,
+  platform, trees, options, cgendata, mangleutils
+
+import std/[hashes, strutils, formatfloat]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
   case n.kind
   of nkStmtList:
-    for i in 0 ..< n.len:
+    result = nil
+    for i in 0..<n.len:
       result = getPragmaStmt(n[i], w)
       if result != nil: break
   of nkPragma:
-    for i in 0 ..< n.len:
+    result = nil
+    for i in 0..<n.len:
       if whichPragma(n[i]) == w: return n[i]
-  else: discard
+  else:
+    result = nil
 
 proc stmtsContainPragma*(n: PNode, w: TSpecialWord): bool =
   result = getPragmaStmt(n, w) != nil
 
 proc hashString*(conf: ConfigRef; s: string): BiggestInt =
-  # has to be the same algorithm as system.hashString!
+  # has to be the same algorithm as strmantle.hashString!
   if CPU[conf.target.targetCPU].bit == 64:
     # we have to use the same bitwidth
     # as the target CPU
-    var b = 0'i64
-    for i in countup(0, len(s) - 1):
-      b = b +% ord(s[i])
-      b = b +% `shl`(b, 10)
-      b = b xor `shr`(b, 6)
-    b = b +% `shl`(b, 3)
-    b = b xor `shr`(b, 11)
-    b = b +% `shl`(b, 15)
-    result = b
+    var b = 0'u64
+    for i in 0..<s.len:
+      b = b + uint(s[i])
+      b = b + (b shl 10)
+      b = b xor (b shr 6)
+    b = b + (b shl 3)
+    b = b xor (b shr 11)
+    b = b + (b shl 15)
+    result = cast[Hash](b)
   else:
-    var a = 0'i32
-    for i in countup(0, len(s) - 1):
-      a = a +% ord(s[i]).int32
-      a = a +% `shl`(a, 10'i32)
-      a = a xor `shr`(a, 6'i32)
-    a = a +% `shl`(a, 3'i32)
-    a = a xor `shr`(a, 11'i32)
-    a = a +% `shl`(a, 15'i32)
-    result = a
+    var a = 0'u32
+    for i in 0..<s.len:
+      a = a + uint32(s[i])
+      a = a + (a shl 10)
+      a = a xor (a shr 6)
+    a = a + (a shl 3)
+    a = a xor (a shr 11)
+    a = a + (a shl 15)
+    result = cast[Hash](uint(a))
 
 template getUniqueType*(key: PType): PType = key
 
@@ -60,48 +68,103 @@ proc makeSingleLineCString*(s: string): string =
     c.toCChar(result)
   result.add('\"')
 
-proc mangle*(name: string): string =
-  result = newStringOfCap(name.len)
-  var start = 0
-  if name[0] in Digits:
-    result.add("X" & name[0])
-    start = 1
-  var requiresUnderscore = false
-  template special(x) =
-    result.add x
-    requiresUnderscore = true
-  for i in start..(name.len-1):
-    let c = name[i]
-    case c
-    of 'a'..'z', '0'..'9', 'A'..'Z':
-      add(result, c)
-    of '_':
-      # we generate names like 'foo_9' for scope disambiguations and so
-      # disallow this here:
-      if i > 0 and i < name.len-1 and name[i+1] in Digits:
-        discard
-      else:
-        add(result, c)
-    of '$': special "dollar"
-    of '%': special "percent"
-    of '&': special "amp"
-    of '^': special "roof"
-    of '!': special "emark"
-    of '?': special "qmark"
-    of '*': special "star"
-    of '+': special "plus"
-    of '-': special "minus"
-    of '/': special "slash"
-    of '=': special "eq"
-    of '<': special "lt"
-    of '>': special "gt"
-    of '~': special "tilde"
-    of ':': special "colon"
-    of '.': special "dot"
-    of '@': special "at"
-    of '|': special "bar"
+proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
+  case int(getSize(conf, typ))
+  of 1: result = ctInt8
+  of 2: result = ctInt16
+  of 4: result = ctInt32
+  of 8: result = ctInt64
+  else: result = ctArray
+
+proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
+  var pt = skipTypes(s.typ, typedescInst)
+  assert skResult != s.kind
+
+  #note precedence: params override types
+  if optByRef in s.options: return true
+  elif sfByCopy in s.flags: return false
+  elif tfByRef in pt.flags: return true
+  elif tfByCopy in pt.flags: return false
+  case pt.kind
+  of tyObject:
+    if s.typ.sym != nil and sfForward in s.typ.sym.flags:
+      # forwarded objects are *always* passed by pointers for consistency!
+      result = true
+    elif s.typ.kind == tySink and conf.selectedGC notin {gcArc, gcAtomicArc, gcOrc, gcHooks}:
+      # bug #23354:
+      result = false
+    elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3):
+      result = true           # requested anyway
+    elif (tfFinal in pt.flags) and (pt[0] == nil):
+      result = false          # no need, because no subtyping possible
     else:
-      add(result, "X" & toHex(ord(c), 2))
-      requiresUnderscore = true
-  if requiresUnderscore:
-    result.add "_"
+      result = true           # ordinary objects are always passed by reference,
+                              # otherwise casting doesn't work
+  of tyTuple:
+    result = (getSize(conf, pt) > conf.target.floatSize*3) or (optByRef in s.options)
+  else:
+    result = false
+  # first parameter and return type is 'lent T'? --> use pass by pointer
+  if s.position == 0 and retType != nil and retType.kind == tyLent:
+    result = not (pt.kind in {tyVar, tyArray, tyOpenArray, tyVarargs, tyRef, tyPtr, tyPointer} or
+      pt.kind == tySet and mapSetType(conf, pt) == ctArray)
+
+proc encodeName*(name: string): string =
+  result = mangle(name)
+  result = $result.len & result
+
+proc makeUnique(m: BModule; s: PSym, name: string = ""): string =
+  result = if name == "": s.name.s else: name
+  result.add "__"
+  result.add m.g.graph.ifaces[s.itemId.module].uniqueName
+  result.add "_u"
+  result.add $s.itemId.item
+
+proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false): string =
+  #Module::Type
+  var name = s.name.s
+  if makeUnique:
+    name = makeUnique(m, s, name)
+  "N" & encodeName(s.skipGenericOwner.name.s) & encodeName(name) & "E"
+
+proc encodeType*(m: BModule; t: PType): string =
+  result = ""
+  var kindName = ($t.kind)[2..^1]
+  kindName[0] = toLower($kindName[0])[0]
+  case t.kind
+  of tyObject, tyEnum, tyDistinct, tyUserTypeClass, tyGenericParam:
+    result = encodeSym(m, t.sym)
+  of tyGenericInst, tyUserTypeClassInst, tyGenericBody:
+    result = encodeName(t[0].sym.name.s)
+    result.add "I"
+    for i in 1..<t.len - 1:
+      result.add encodeType(m, t[i])
+    result.add "E"
+  of tySequence, tyOpenArray, tyArray, tyVarargs, tyTuple, tyProc, tySet, tyTypeDesc,
+    tyPtr, tyRef, tyVar, tyLent, tySink, tyStatic, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
+    result =
+      case t.kind:
+      of tySequence: encodeName("seq")
+      else: encodeName(kindName)
+    result.add "I"
+    for i in 0..<t.len:
+      let s = t[i]
+      if s.isNil: continue
+      result.add encodeType(m, s)
+    result.add "E"
+  of tyRange:
+    var val = "range_"
+    if t.n[0].typ.kind in {tyFloat..tyFloat128}:
+      val.addFloat t.n[0].floatVal
+      val.add "_"
+      val.addFloat t.n[1].floatVal
+    else:
+      val.add $t.n[0].intVal & "_" & $t.n[1].intVal
+    result = encodeName(val)
+  of tyString..tyUInt64, tyPointer, tyBool, tyChar, tyVoid, tyAnything, tyNil, tyEmpty:
+    result = encodeName(kindName)
+  of tyAlias, tyInferred, tyOwned:
+    result = encodeType(m, t.elementType)
+  else:
+    assert false, "encodeType " & $t.kind
+
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index cd344f096..091f5c842 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -10,18 +10,34 @@
 ## This module implements the C code generator.
 
 import
-  ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets,
-  nversion, nimsets, msgs, std / sha1, bitsets, idents, types,
-  ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth,
-  condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases,
-  lowerings, semparallel, tables, sets, ndi, lineinfos
+  ast, astalgo, trees, platform, magicsys, extccomp, options,
+  nversion, nimsets, msgs, bitsets, idents, types,
+  ccgutils, ropes, wordrecg, treetab, cgmeth,
+  rodutils, renderer, cgendata, aliases,
+  lowerings, ndi, lineinfos, pathutils, transf,
+  injectdestructors, astmsgs, modulepaths, pushpoppragmas,
+  mangleutils
 
-import strutils except `%` # collides with ropes.`%`
+from expanddefaults import caseObjDefaultBranch
 
-from modulegraphs import ModuleGraph
-from lineinfos import
-  warnGcMem, errXMustBeCompileTime, hintDependency, errGenerated, errCannotOpenFile
-import dynlib
+import pipelineutils
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+when not defined(leanCompiler):
+  import spawn, semparallel
+
+import std/strutils except `%`, addf # collides with ropes.`%`
+
+from ic / ic import ModuleBackendFlag
+import std/[dynlib, math, tables, sets, os, intsets, hashes]
+
+const
+  # we use some ASCII control characters to insert directives that will be converted to real code in a postprocessing pass
+  postprocessDirStart = '\1'
+  postprocessDirSep = '\31'
+  postprocessDirEnd = '\23'
 
 when not declared(dynlib.libCandidates):
   proc libCandidates(s: string, dest: var seq[string]) =
@@ -34,35 +50,44 @@ when not declared(dynlib.libCandidates):
       for middle in split(substr(s, le + 1, ri - 1), '|'):
         libCandidates(prefix & middle & suffix, dest)
     else:
-      add(dest, s)
+      dest.add(s)
 
 when options.hasTinyCBackend:
   import tccgen
 
-# implementation
+proc hcrOn(m: BModule): bool = m.config.hcrOn
+proc hcrOn(p: BProc): bool = p.module.config.hcrOn
 
 proc addForwardedProc(m: BModule, prc: PSym) =
-  m.forwardedProcs.add(prc)
-  inc(m.g.forwardedProcsCounter)
+  m.g.forwardedProcs.add(prc)
 
 proc findPendingModule(m: BModule, s: PSym): BModule =
-  var ms = getModule(s)
-  result = m.g.modules[ms.position]
+  # TODO fixme
+  if m.config.symbolFiles == v2Sf:
+    let ms = s.itemId.module  #getModule(s)
+    result = m.g.modules[ms]
+  else:
+    var ms = getModule(s)
+    result = m.g.modules[ms.position]
+
+proc initLoc(k: TLocKind, lode: PNode, s: TStorageLoc, flags: TLocFlags = {}): TLoc =
+  result = TLoc(k: k, storage: s, lode: lode,
+                snippet: "", flags: flags)
 
-proc initLoc(result: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) =
-  result.k = k
-  result.storage = s
-  result.lode = lode
-  result.r = nil
-  result.flags = {}
+proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) {.inline.} =
+  # fills the loc if it is not already initialized
+  if a.k == locNone:
+    a.k = k
+    a.lode = lode
+    a.storage = s
+    if a.snippet == "": a.snippet = r
 
-proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) =
+proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) {.inline.} =
   # fills the loc if it is not already initialized
   if a.k == locNone:
     a.k = k
     a.lode = lode
     a.storage = s
-    if a.r == nil: a.r = r
 
 proc t(a: TLoc): PType {.inline.} =
   if a.lode.kind == nkSym:
@@ -80,121 +105,195 @@ proc isSimpleConst(typ: PType): bool =
       {tyTuple, tyObject, tyArray, tySet, tySequence} and not
       (t.kind == tyProc and t.callConv == ccClosure)
 
-proc useStringh(m: BModule) =
-  if includesStringh notin m.flags:
-    incl m.flags, includesStringh
-    m.includeHeader("<string.h>")
-
 proc useHeader(m: BModule, sym: PSym) =
   if lfHeader in sym.loc.flags:
     assert(sym.annex != nil)
     let str = getStr(sym.annex.path)
     m.includeHeader(str)
 
-proc cgsym(m: BModule, name: string): Rope
+proc cgsym(m: BModule, name: string)
+proc cgsymValue(m: BModule, name: string): Rope
+
+proc getCFile(m: BModule): AbsoluteFile
+
+proc getModuleDllPath(m: BModule): Rope =
+  let (dir, name, ext) = splitFile(getCFile(m))
+  let filename = strutils.`%`(platform.OS[m.g.config.target.targetOS].dllFrmt, [name & ext])
+  result = makeCString(dir.string & "/" & filename)
+
+proc getModuleDllPath(m: BModule, module: int): Rope =
+  result = getModuleDllPath(m.g.modules[module])
+
+proc getModuleDllPath(m: BModule, s: PSym): Rope =
+  result = getModuleDllPath(m.g.modules[s.itemId.module])
+
+import std/macros
+
+proc cgFormatValue(result: var string; value: string) =
+  result.add value
+
+proc cgFormatValue(result: var string; value: BiggestInt) =
+  result.addInt value
 
-proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope =
-  assert m != nil
+proc cgFormatValue(result: var string; value: Int128) =
+  result.addInt128 value
+
+# TODO: please document
+macro ropecg(m: BModule, frmt: static[FormatStr], args: untyped): Rope =
+  args.expectKind nnkBracket
+  # echo "ropecg ", newLit(frmt).repr, ", ", args.repr
   var i = 0
-  var length = len(frmt)
-  result = nil
+  result = nnkStmtListExpr.newTree()
+
+  result.add quote do:
+    assert `m` != nil
+
+  let resVar = genSym(nskVar, "res")
+  # during `koch boot` the median of all generates strings from this
+  # macro is around 40 bytes in length.
+  result.add newVarStmt(resVar, newCall(bindSym"newStringOfCap", newLit(80)))
+  let formatValue = bindSym"cgFormatValue"
+
   var num = 0
-  while i < length:
+  var strLit = ""
+
+  template flushStrLit() =
+    if strLit != "":
+      result.add newCall(ident "add", resVar, newLit(strLit))
+      strLit.setLen 0
+
+  while i < frmt.len:
     if frmt[i] == '$':
       inc(i)                  # skip '$'
       case frmt[i]
       of '$':
-        add(result, "$")
+        strLit.add '$'
         inc(i)
       of '#':
+        flushStrLit()
         inc(i)
-        add(result, args[num])
+        result.add newCall(formatValue, resVar, args[num])
+        inc(num)
+      of '^':
+        flushStrLit()
+        inc(i)
+        result.add newCall(formatValue, resVar, args[^1])
         inc(num)
       of '0'..'9':
         var j = 0
         while true:
           j = (j * 10) + ord(frmt[i]) - ord('0')
           inc(i)
-          if i >= length or not (frmt[i] in {'0'..'9'}): break
+          if i >= frmt.len or not (frmt[i] in {'0'..'9'}): break
         num = j
-        if j > high(args) + 1:
-          internalError(m.config, "ropes: invalid format string $" & $j)
-        add(result, args[j-1])
+        if j > args.len:
+          error("ropes: invalid format string " & newLit(frmt).repr & " args.len: " & $args.len)
+
+        flushStrLit()
+        result.add newCall(formatValue, resVar, args[j-1])
       of 'n':
-        if optLineDir notin m.config.options: add(result, "\L")
+        flushStrLit()
+        result.add quote do:
+          if optLineDir notin `m`.config.options:
+            `resVar`.add("\L")
         inc(i)
       of 'N':
-        add(result, "\L")
+        strLit.add "\L"
         inc(i)
-      else: internalError(m.config, "ropes: invalid format string $" & frmt[i])
+      else:
+        error("ropes: invalid format string $" & frmt[i])
     elif frmt[i] == '#' and frmt[i+1] in IdentStartChars:
       inc(i)
       var j = i
       while frmt[j] in IdentChars: inc(j)
-      var ident = substr(frmt, i, j-1)
+      var ident = newLit(substr(frmt, i, j-1))
       i = j
-      add(result, cgsym(m, ident))
+      flushStrLit()
+      result.add newCall(formatValue, resVar, newCall(ident"cgsymValue", m, ident))
     elif frmt[i] == '#' and frmt[i+1] == '$':
       inc(i, 2)
       var j = 0
       while frmt[i] in Digits:
         j = (j * 10) + ord(frmt[i]) - ord('0')
         inc(i)
-      add(result, cgsym(m, $args[j-1]))
-    var start = i
-    while i < length:
-      if frmt[i] != '$' and frmt[i] != '#': inc(i)
-      else: break
-    if i - 1 >= start:
-      add(result, substr(frmt, start, i - 1))
+      let ident = args[j-1]
+      flushStrLit()
+      result.add newCall(formatValue, resVar, newCall(ident"cgsymValue", m, ident))
+    elif frmt[i] == '#' and frmt[i+1] == '#':
+      inc(i, 2)
+      strLit.add("#")
+    else:
+      strLit.add(frmt[i])
+      inc(i)
 
-proc indentLine(p: BProc, r: Rope): Rope =
-  result = r
-  for i in countup(0, p.blocks.len-1):
-    prepend(result, "\t".rope)
+  flushStrLit()
+  result.add newCall(ident"rope", resVar)
 
-proc appcg(m: BModule, c: var Rope, frmt: FormatStr,
-           args: varargs[Rope]) =
-  add(c, ropecg(m, frmt, args))
+proc addIndent(p: BProc; result: var Rope) =
+  var i = result.len
+  let newLen = i + p.blocks.len
+  result.setLen newLen
+  while i < newLen:
+    result[i] = '\t'
+    inc i
 
-proc appcg(m: BModule, s: TCFileSection, frmt: FormatStr,
-           args: varargs[Rope]) =
-  add(m.s[s], ropecg(m, frmt, args))
+template appcg(m: BModule, c: var Rope, frmt: FormatStr,
+           args: untyped) =
+  c.add(ropecg(m, frmt, args))
 
-proc appcg(p: BProc, s: TCProcSection, frmt: FormatStr,
-           args: varargs[Rope]) =
-  add(p.s(s), ropecg(p.module, frmt, args))
+template appcg(m: BModule, sec: TCFileSection, frmt: FormatStr,
+           args: untyped) =
+  m.s[sec].add(ropecg(m, frmt, args))
 
-proc line(p: BProc, s: TCProcSection, r: Rope) =
-  add(p.s(s), indentLine(p, r))
+template appcg(p: BProc, sec: TCProcSection, frmt: FormatStr,
+           args: untyped) =
+  p.s(sec).add(ropecg(p.module, frmt, args))
 
-proc line(p: BProc, s: TCProcSection, r: string) =
-  add(p.s(s), indentLine(p, r.rope))
+template line(p: BProc, sec: TCProcSection, r: string) =
+  addIndent p, p.s(sec)
+  p.s(sec).add(r)
 
-proc lineF(p: BProc, s: TCProcSection, frmt: FormatStr,
-              args: openarray[Rope]) =
-  add(p.s(s), indentLine(p, frmt % args))
+template lineF(p: BProc, sec: TCProcSection, frmt: FormatStr,
+              args: untyped) =
+  addIndent p, p.s(sec)
+  p.s(sec).add(frmt % args)
 
-proc lineCg(p: BProc, s: TCProcSection, frmt: FormatStr,
-               args: varargs[Rope]) =
-  add(p.s(s), indentLine(p, ropecg(p.module, frmt, args)))
+template lineCg(p: BProc, sec: TCProcSection, frmt: FormatStr,
+               args: untyped) =
+  addIndent p, p.s(sec)
+  p.s(sec).add(ropecg(p.module, frmt, args))
 
-proc linefmt(p: BProc, s: TCProcSection, frmt: FormatStr,
-             args: varargs[Rope]) =
-  add(p.s(s), indentLine(p, ropecg(p.module, frmt, args)))
+template linefmt(p: BProc, sec: TCProcSection, frmt: FormatStr,
+             args: untyped) =
+  addIndent p, p.s(sec)
+  p.s(sec).add(ropecg(p.module, frmt, args))
 
 proc safeLineNm(info: TLineInfo): int =
   result = toLinenumber(info)
   if result < 0: result = 0 # negative numbers are not allowed in #line
 
-proc genCLineDir(r: var Rope, filename: string, line: int; conf: ConfigRef) =
+proc genPostprocessDir(field1, field2, field3: string): string =
+  result = postprocessDirStart & field1 & postprocessDirSep & field2 & postprocessDirSep & field3 & postprocessDirEnd
+
+proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; conf: ConfigRef) =
   assert line >= 0
-  if optLineDir in conf.options:
-    addf(r, "$N#line $2 $1$N",
-        [rope(makeSingleLineCString(filename)), rope(line)])
+  if optLineDir in conf.options and line > 0:
+    if fileIdx == InvalidFileIdx:
+      r.add(rope("\n#line " & $line & " \"generated_not_to_break_here\"\n"))
+    else:
+      r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n"))
+
+proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; p: BProc; info: TLineInfo; lastFileIndex: FileIndex) =
+  assert line >= 0
+  if optLineDir in p.config.options and line > 0:
+    if fileIdx == InvalidFileIdx:
+      r.add(rope("\n#line " & $line & " \"generated_not_to_break_here\"\n"))
+    else:
+      r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n"))
 
 proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) =
-  genCLineDir(r, toFullPath(conf, info), info.safeLineNm, conf)
+  if optLineDir in conf.options:
+    genCLineDir(r, info.fileIndex, info.safeLineNm, conf)
 
 proc freshLineInfo(p: BProc; info: TLineInfo): bool =
   if p.lastLineInfo.line != info.line or
@@ -202,53 +301,107 @@ proc freshLineInfo(p: BProc; info: TLineInfo): bool =
     p.lastLineInfo.line = info.line
     p.lastLineInfo.fileIndex = info.fileIndex
     result = true
+  else:
+    result = false
+
+proc genCLineDir(r: var Rope, p: BProc, info: TLineInfo; conf: ConfigRef) =
+  if optLineDir in conf.options:
+    let lastFileIndex = p.lastLineInfo.fileIndex
+    if freshLineInfo(p, info):
+      genCLineDir(r, info.fileIndex, info.safeLineNm, p, info, lastFileIndex)
 
 proc genLineDir(p: BProc, t: PNode) =
+  if p == p.module.preInitProc: return
   let line = t.info.safeLineNm
 
   if optEmbedOrigSrc in p.config.globalOptions:
-    add(p.s(cpsStmts), ~"//" & sourceLine(p.config, t.info) & "\L")
-  genCLineDir(p.s(cpsStmts), toFullPath(p.config, t.info), line, p.config)
-  if ({optStackTrace, optEndb} * p.options == {optStackTrace, optEndb}) and
-      (p.prc == nil or sfPure notin p.prc.flags):
-    if freshLineInfo(p, t.info):
-      linefmt(p, cpsStmts, "#endb($1, $2);$N",
-              line.rope, makeCString(toFilename(p.config, t.info)))
-  elif ({optLineTrace, optStackTrace} * p.options ==
-      {optLineTrace, optStackTrace}) and
-      (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIDX:
-    if freshLineInfo(p, t.info):
-      linefmt(p, cpsStmts, "nimln_($1, $2);$n",
-              line.rope, quotedFilename(p.config, t.info))
-
-proc postStmtActions(p: BProc) {.inline.} =
-  add(p.s(cpsStmts), p.module.injectStmt)
+    p.s(cpsStmts).add("//" & sourceLine(p.config, t.info) & "\L")
+  let lastFileIndex = p.lastLineInfo.fileIndex
+  let freshLine = freshLineInfo(p, t.info)
+  if freshLine:
+    genCLineDir(p.s(cpsStmts), t.info.fileIndex, line, p, t.info, lastFileIndex)
+  if ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and
+      (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx:
+      if freshLine:
+        line(p, cpsStmts, genPostprocessDir("nimln", $line, $t.info.fileIndex.int32))
 
 proc accessThreadLocalVar(p: BProc, s: PSym)
 proc emulatedThreadVars(conf: ConfigRef): bool {.inline.}
 proc genProc(m: BModule, prc: PSym)
+proc raiseInstr(p: BProc; result: var Rope)
 
 template compileToCpp(m: BModule): untyped =
-  m.config.cmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags
+  m.config.backend == backendCpp or sfCompileToCpp in m.module.flags
 
 proc getTempName(m: BModule): Rope =
   result = m.tmpBase & rope(m.labels)
   inc m.labels
 
+proc rdLoc(a: TLoc): Rope =
+  # 'read' location (deref if indirect)
+  if lfIndirect in a.flags:
+    result = "(*" & a.snippet & ")"
+  else:
+    result = a.snippet
+
+proc addRdLoc(a: TLoc; result: var Rope) =
+  if lfIndirect in a.flags:
+    result.add "(*" & a.snippet & ")"
+  else:
+    result.add a.snippet
+
+proc lenField(p: BProc): Rope {.inline.} =
+  result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
+
+proc lenExpr(p: BProc; a: TLoc): Rope =
+  if optSeqDestructors in p.config.globalOptions:
+    result = rdLoc(a) & ".len"
+  else:
+    result = "($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)]
+
+proc dataFieldAccessor(p: BProc, sym: Rope): Rope =
+  if optSeqDestructors in p.config.globalOptions:
+    result = "(" & sym & ").p"
+  else:
+    result = sym
+
+proc dataField(p: BProc): Rope =
+  if optSeqDestructors in p.config.globalOptions:
+    result = rope".p->data"
+  else:
+    result = rope"->data"
+
+proc genProcPrototype(m: BModule, sym: PSym)
+
+include cbuilder
 include ccgliterals
 include ccgtypes
 
 # ------------------------------ Manager of temporaries ------------------
 
-proc rdLoc(a: TLoc): Rope =
-  # 'read' location (deref if indirect)
-  result = a.r
-  if lfIndirect in a.flags: result = "(*$1)" % [result]
+template mapTypeChooser(n: PNode): TSymKind =
+  (if n.kind == nkSym: n.sym.kind else: skVar)
+
+template mapTypeChooser(a: TLoc): TSymKind = mapTypeChooser(a.lode)
+
+proc addAddrLoc(conf: ConfigRef; a: TLoc; result: var Rope) =
+  if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray:
+    result.add "(&" & a.snippet & ")"
+  else:
+    result.add a.snippet
 
 proc addrLoc(conf: ConfigRef; a: TLoc): Rope =
-  result = a.r
-  if lfIndirect notin a.flags and mapType(conf, a.t) != ctArray:
-    result = "(&" & result & ")"
+  if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray:
+    result = "(&" & a.snippet & ")"
+  else:
+    result = a.snippet
+
+proc byRefLoc(p: BProc; a: TLoc): Rope =
+  if lfIndirect notin a.flags and mapType(p.config, a.t, mapTypeChooser(a) == skParam) != ctArray and not
+      p.module.compileToCpp:
+    result = "(&" & a.snippet & ")"
+  else:
+    result = a.snippet
 
 proc rdCharLoc(a: TLoc): Rope =
   # read a location that may need a char-cast:
@@ -256,87 +409,146 @@ proc rdCharLoc(a: TLoc): Rope =
   if skipTypes(a.t, abstractRange).kind == tyChar:
     result = "((NU8)($1))" % [result]
 
-proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
-                   takeAddr: bool) =
-  if p.module.compileToCpp and t.isException and not isDefined(p.config, "noCppExceptions"):
-    # init vtable in Exception object for polymorphic exceptions
-    includeHeader(p.module, "<new>")
-    linefmt(p, section, "new ($1) $2;$n", rdLoc(a), getTypeDesc(p.module, t))
+type
+  TAssignmentFlag = enum
+    needToCopy
+    needToCopySinkParam
+    needTempForOpenArray
+    needAssignCall
+  TAssignmentFlags = set[TAssignmentFlag]
+
+proc genObjConstr(p: BProc, e: PNode, d: var TLoc)
+proc rawConstExpr(p: BProc, n: PNode; d: var TLoc)
+proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags)
+
+type
+  ObjConstrMode = enum
+    constructObj,
+    constructRefObj
 
+proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: var TLoc,
+                   mode: ObjConstrMode) =
+  #if optNimV2 in p.config.globalOptions: return
   case analyseObjectWithTypeField(t)
   of frNone:
     discard
   of frHeader:
     var r = rdLoc(a)
-    if not takeAddr: r = "(*$1)" % [r]
+    if mode == constructRefObj: r = "(*$1)" % [r]
     var s = skipTypes(t, abstractInst)
     if not p.module.compileToCpp:
-      while (s.kind == tyObject) and (s.sons[0] != nil):
-        add(r, ".Sup")
-        s = skipTypes(s.sons[0], skipPtrs)
-    linefmt(p, section, "$1.m_type = $2;$n", r, genTypeInfo(p.module, t, a.lode.info))
+      while s.kind == tyObject and s[0] != nil:
+        r.add(".Sup")
+        s = skipTypes(s[0], skipPtrs)
+    if optTinyRtti in p.config.globalOptions:
+      linefmt(p, section, "$1.m_type = $2;$n", [r, genTypeInfoV2(p.module, t, a.lode.info)])
+    else:
+      linefmt(p, section, "$1.m_type = $2;$n", [r, genTypeInfoV1(p.module, t, a.lode.info)])
   of frEmbedded:
-    # worst case for performance:
-    var r = if takeAddr: addrLoc(p.config, a) else: rdLoc(a)
-    linefmt(p, section, "#objectInit($1, $2);$n", r, genTypeInfo(p.module, t, a.lode.info))
+    if optTinyRtti in p.config.globalOptions:
+      var tmp: TLoc = default(TLoc)
+      if mode == constructRefObj:
+        let objType = t.skipTypes(abstractInst+{tyRef})
+        rawConstExpr(p, newNodeIT(nkType, a.lode.info, objType), tmp)
+        linefmt(p, cpsStmts,
+            "#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n",
+            [rdLoc(a), rdLoc(tmp), getTypeDesc(p.module, objType, descKindFromSymKind mapTypeChooser(a))])
+      else:
+        rawConstExpr(p, newNodeIT(nkType, a.lode.info, t), tmp)
+        genAssignment(p, a, tmp, {})
+    else:
+      # worst case for performance:
+      var r = if mode == constructObj: addrLoc(p.config, a) else: rdLoc(a)
+      linefmt(p, section, "#objectInit($1, $2);$n", [r, genTypeInfoV1(p.module, t, a.lode.info)])
 
-type
-  TAssignmentFlag = enum
-    needToCopy, afDestIsNil, afDestIsNotNil, afSrcIsNil, afSrcIsNotNil
-  TAssignmentFlags = set[TAssignmentFlag]
+  if isException(t):
+    var r = rdLoc(a)
+    if mode == constructRefObj: r = "(*$1)" % [r]
+    var s = skipTypes(t, abstractInst)
+    if not p.module.compileToCpp:
+      while s.kind == tyObject and s[0] != nil and s.sym.magic != mException:
+        r.add(".Sup")
+        s = skipTypes(s[0], skipPtrs)
+    linefmt(p, section, "$1.name = $2;$n", [r, makeCString(t.skipTypes(abstractInst).sym.name.s)])
 
-proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags)
+proc genRefAssign(p: BProc, dest, src: TLoc)
 
 proc isComplexValueType(t: PType): bool {.inline.} =
-  let t = t.skipTypes(abstractInst)
-  result = t.kind in {tyArray, tySet, tyTuple, tyObject} or
+  let t = t.skipTypes(abstractInst + tyUserTypeClasses)
+  result = t.kind in {tyArray, tySet, tyTuple, tyObject, tyOpenArray} or
     (t.kind == tyProc and t.callConv == ccClosure)
 
+include ccgreset
+
 proc resetLoc(p: BProc, loc: var TLoc) =
-  let containsGcRef = containsGarbageCollectedRef(loc.t)
+  let containsGcRef = optSeqDestructors notin p.config.globalOptions and containsGarbageCollectedRef(loc.t)
   let typ = skipTypes(loc.t, abstractVarRange)
-  if isImportedCppType(typ): return
-  if not isComplexValueType(typ):
+  if isImportedCppType(typ): 
+    var didGenTemp = false
+    linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(loc), genCppInitializer(p.module, p, typ, didGenTemp)])
+    return
+  if optSeqDestructors in p.config.globalOptions and typ.kind in {tyString, tySequence}:
+    assert loc.snippet != ""
+
+    let atyp = skipTypes(loc.t, abstractInst)
+    if atyp.kind in {tyVar, tyLent}:
+      linefmt(p, cpsStmts, "$1->len = 0; $1->p = NIM_NIL;$n", [rdLoc(loc)])
+    else:
+      linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
+  elif not isComplexValueType(typ):
     if containsGcRef:
-      var nilLoc: TLoc
-      initLoc(nilLoc, locTemp, loc.lode, OnStack)
-      nilLoc.r = rope("NIM_NIL")
-      genRefAssign(p, loc, nilLoc, {afSrcIsNil})
+      var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack)
+      nilLoc.snippet = rope("NIM_NIL")
+      genRefAssign(p, loc, nilLoc)
     else:
-      linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc))
+      linefmt(p, cpsStmts, "$1 = 0;$n", [rdLoc(loc)])
   else:
-    if optNilCheck in p.options:
-      linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(p.config, loc))
-    if loc.storage != OnStack:
-      linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
-              addrLoc(p.config, loc), genTypeInfo(p.module, loc.t, loc.lode.info))
+    if loc.storage != OnStack and containsGcRef:
+      specializeReset(p, loc)
+      when false:
+        linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
+                [addrLoc(p.config, loc), genTypeInfoV1(p.module, loc.t, loc.lode.info)])
       # XXX: generated reset procs should not touch the m_type
       # field, so disabling this should be safe:
-      genObjectInit(p, cpsStmts, loc.t, loc, true)
+      genObjectInit(p, cpsStmts, loc.t, loc, constructObj)
     else:
-      useStringh(p.module)
       # array passed as argument decayed into pointer, bug #7332
       # so we use getTypeDesc here rather than rdLoc(loc)
-      linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n",
-              addrLoc(p.config, loc), getTypeDesc(p.module, loc.t))
+      let tyDesc = getTypeDesc(p.module, loc.t, descKindFromSymKind mapTypeChooser(loc))
+      if p.module.compileToCpp and isOrHasImportedCppType(typ):
+        if lfIndirect in loc.flags:
+          #C++ cant be just zeroed. We need to call the ctors
+          var tmp = getTemp(p, loc.t)
+          linefmt(p, cpsStmts,"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
+                  [addrLoc(p.config, loc), addrLoc(p.config, tmp), tyDesc])
+      else:
+        linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
+                [addrLoc(p.config, loc), tyDesc])
+
       # XXX: We can be extra clever here and call memset only
       # on the bytes following the m_type field?
-      genObjectInit(p, cpsStmts, loc.t, loc, true)
+      genObjectInit(p, cpsStmts, loc.t, loc, constructObj)
 
-proc constructLoc(p: BProc, loc: TLoc, isTemp = false) =
+proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) =
   let typ = loc.t
-  if not isComplexValueType(typ):
-    linefmt(p, cpsStmts, "$1 = ($2)0;$n", rdLoc(loc),
-      getTypeDesc(p.module, typ))
+  if optSeqDestructors in p.config.globalOptions and skipTypes(typ, abstractInst + {tyStatic}).kind in {tyString, tySequence}:
+    linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
+  elif not isComplexValueType(typ):
+    if containsGarbageCollectedRef(loc.t):
+      var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack)
+      nilLoc.snippet = rope("NIM_NIL")
+      genRefAssign(p, loc, nilLoc)
+    else:
+      linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc),
+        getTypeDesc(p.module, typ, descKindFromSymKind mapTypeChooser(loc))])
   else:
-    if not isTemp or containsGarbageCollectedRef(loc.t):
-      # don't use memset for temporary values for performance if we can
+    if (not isTemp or containsGarbageCollectedRef(loc.t)) and not hasNoInit(loc.t):
+      # don't use nimZeroMem for temporary values for performance if we can
       # avoid it:
-      if not isImportedCppType(typ):
-        useStringh(p.module)
-        linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n",
-                addrLoc(p.config, loc), getTypeDesc(p.module, typ))
-    genObjectInit(p, cpsStmts, loc.t, loc, true)
+      if not isOrHasImportedCppType(typ):
+        linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
+                [addrLoc(p.config, loc), getTypeDesc(p.module, typ, descKindFromSymKind mapTypeChooser(loc))])
+    genObjectInit(p, cpsStmts, loc.t, loc, constructObj)
 
 proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
   if sfNoInit notin v.flags:
@@ -350,178 +562,253 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
     if not immediateAsgn:
       constructLoc(p, v.loc)
 
-proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
+proc getTemp(p: BProc, t: PType, needsInit=false): TLoc =
   inc(p.labels)
-  result.r = "T" & rope(p.labels) & "_"
-  linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r)
-  result.k = locTemp
-  result.lode = lodeTyp t
-  result.storage = OnStack
-  result.flags = {}
+  result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
+                storage: OnStack, flags: {})
+  if p.module.compileToCpp and isOrHasImportedCppType(t):
+    var didGenTemp = false
+    linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.snippet,
+      genCppInitializer(p.module, p, t, didGenTemp)])
+  else:
+    linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.snippet])
   constructLoc(p, result, not needsInit)
+  when false:
+    # XXX Introduce a compiler switch in order to detect these easily.
+    if getSize(p.config, t) > 1024 * 1024:
+      if p.prc != nil:
+        echo "ENORMOUS TEMPORARY! ", p.config $ p.prc.info
+      else:
+        echo "ENORMOUS TEMPORARY! ", p.config $ p.lastLineInfo
+      writeStackTrace()
+
+proc getTempCpp(p: BProc, t: PType, value: Rope): TLoc =
+  inc(p.labels)
+  result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
+                storage: OnStack, flags: {})
+  linefmt(p, cpsStmts, "auto $1 = $2;$n", [result.snippet, value])
 
-proc getIntTemp(p: BProc, result: var TLoc) =
+proc getIntTemp(p: BProc): TLoc =
   inc(p.labels)
-  result.r = "T" & rope(p.labels) & "_"
-  linefmt(p, cpsLocals, "NI $1;$n", result.r)
-  result.k = locTemp
-  result.storage = OnStack
-  result.lode = lodeTyp getSysType(p.module.g.graph, unknownLineInfo(), tyInt)
-  result.flags = {}
-
-proc initGCFrame(p: BProc): Rope =
-  if p.gcFrameId > 0: result = "struct {$1} GCFRAME_;$n" % [p.gcFrameType]
-
-proc deinitGCFrame(p: BProc): Rope =
-  if p.gcFrameId > 0:
-    result = ropecg(p.module,
-                    "if (((NU)&GCFRAME_) < 4096) #nimGCFrame(&GCFRAME_);$n")
-
-proc localDebugInfo(p: BProc, s: PSym) =
-  if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return
-  # XXX work around a bug: No type information for open arrays possible:
-  if skipTypes(s.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: return
-  var a = "&" & s.loc.r
-  if s.kind == skParam and ccgIntroducedPtr(p.config, s): a = s.loc.r
-  lineF(p, cpsInit,
-       "FR_.s[$1].address = (void*)$3; FR_.s[$1].typ = $4; FR_.s[$1].name = $2;$n",
-       [p.maxFrameLen.rope, makeCString(normalize(s.name.s)), a,
-        genTypeInfo(p.module, s.loc.t, s.info)])
-  inc(p.maxFrameLen)
-  inc p.blocks[p.blocks.len-1].frameLen
+  result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp,
+                storage: OnStack, lode: lodeTyp getSysType(p.module.g.graph, unknownLineInfo, tyInt),
+                flags: {})
+  linefmt(p, cpsLocals, "NI $1;$n", [result.snippet])
 
 proc localVarDecl(p: BProc; n: PNode): Rope =
+  result = ""
   let s = n.sym
   if s.loc.k == locNone:
-    fillLoc(s.loc, locLocalVar, n, mangleLocalName(p, s), OnStack)
+    fillLocalName(p, s)
+    fillLoc(s.loc, locLocalVar, n, OnStack)
     if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
-  result = getTypeDesc(p.module, s.typ)
-  if s.constraint.isNil:
-    if sfRegister in s.flags: add(result, " register")
+  if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
+    result.addf("NIM_ALIGN($1) ", [rope(s.alignment)])
+
+  genCLineDir(result, p, n.info, p.config)
+
+  result.add getTypeDesc(p.module, s.typ, dkVar)
+  if sfCodegenDecl notin s.flags:
+    if sfRegister in s.flags: result.add(" register")
     #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds:
-    #  add(decl, " GC_GUARD")
-    if sfVolatile in s.flags: add(result, " volatile")
-    add(result, " ")
-    add(result, s.loc.r)
+    #  decl.add(" GC_GUARD")
+    if sfVolatile in s.flags: result.add(" volatile")
+    if sfNoalias in s.flags: result.add(" NIM_NOALIAS")
+    result.add(" ")
+    result.add(s.loc.snippet)
   else:
-    result = s.cgDeclFrmt % [result, s.loc.r]
+    result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.snippet])
 
 proc assignLocalVar(p: BProc, n: PNode) =
   #assert(s.loc.k == locNone) # not yet assigned
   # this need not be fulfilled for inline procs; they are regenerated
   # for each module that uses them!
-  let nl = if optLineDir in p.config.options: "" else: "\L"
-  let decl = localVarDecl(p, n) & ";" & nl
+  let nl = if optLineDir in p.config.options: "" else: "\n"
+  var decl = localVarDecl(p, n)
+  if p.module.compileToCpp and isOrHasImportedCppType(n.typ):
+    var didGenTemp = false
+    decl.add genCppInitializer(p.module, p, n.typ, didGenTemp)
+  decl.add ";" & nl
   line(p, cpsLocals, decl)
-  localDebugInfo(p, n.sym)
 
 include ccgthreadvars
 
 proc varInDynamicLib(m: BModule, sym: PSym)
-proc mangleDynLibProc(sym: PSym): Rope
 
-proc assignGlobalVar(p: BProc, n: PNode) =
+proc treatGlobalDifferentlyForHCR(m: BModule, s: PSym): bool =
+  return m.hcrOn and {sfThread, sfGlobal} * s.flags == {sfGlobal} and
+      ({lfNoDecl, lfHeader} * s.loc.flags == {})
+      # and s.owner.kind == skModule # owner isn't always a module (global pragma on local var)
+      # and s.loc.k == locGlobalVar  # loc isn't always initialized when this proc is used
+
+proc genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) =
+  let s = n.sym
+  if sfCodegenDecl notin s.flags:
+    if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
+      decl.addf "NIM_ALIGN($1) ", [rope(s.alignment)]
+    if p.hcrOn: decl.add("static ")
+    elif sfImportc in s.flags: decl.add("extern ")
+    elif lfExportLib in s.loc.flags: decl.add("N_LIB_EXPORT_VAR ")
+    else: decl.add("N_LIB_PRIVATE ")
+    if s.kind == skLet and value != "": decl.add("NIM_CONST ")
+    decl.add(td)
+    if p.hcrOn: decl.add("*")
+    if sfRegister in s.flags: decl.add(" register")
+    if sfVolatile in s.flags: decl.add(" volatile")
+    if sfNoalias in s.flags: decl.add(" NIM_NOALIAS")
+  else:
+    if value != "":
+      decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.snippet, value])
+    else:
+      decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.snippet])
+
+proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope; didGenTemp: var bool)
+
+proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
   let s = n.sym
   if s.loc.k == locNone:
-    fillLoc(s.loc, locGlobalVar, n, mangleName(p.module, s), OnHeap)
+    fillBackendName(p.module, s)
+    fillLoc(s.loc, locGlobalVar, n, OnHeap)
+    if treatGlobalDifferentlyForHCR(p.module, s): incl(s.loc.flags, lfIndirect)
 
   if lfDynamicLib in s.loc.flags:
     var q = findPendingModule(p.module, s)
     if q != nil and not containsOrIncl(q.declaredThings, s.id):
       varInDynamicLib(q, s)
     else:
-      s.loc.r = mangleDynLibProc(s)
+      s.loc.snippet = mangleDynLibProc(s)
+    if value != "":
+      internalError(p.config, n.info, ".dynlib variables cannot have a value")
     return
   useHeader(p.module, s)
   if lfNoDecl in s.loc.flags: return
-  if sfThread in s.flags:
-    declareThreadVar(p.module, s, sfImportc in s.flags)
-  else:
-    var decl: Rope = nil
-    var td = getTypeDesc(p.module, s.loc.t)
-    if s.constraint.isNil:
-      if sfImportc in s.flags: add(decl, "extern ")
-      add(decl, td)
-      if sfRegister in s.flags: add(decl, " register")
-      if sfVolatile in s.flags: add(decl, " volatile")
-      addf(decl, " $1;$n", [s.loc.r])
+  if not containsOrIncl(p.module.declaredThings, s.id):
+    if sfThread in s.flags:
+      declareThreadVar(p.module, s, sfImportc in s.flags)
+      if value != "":
+        internalError(p.config, n.info, ".threadvar variables cannot have a value")
     else:
-      decl = (s.cgDeclFrmt & ";$n") % [td, s.loc.r]
-    add(p.module.s[cfsVars], decl)
-  if p.withinLoop > 0:
+      var decl: Rope = ""
+      let td = getTypeDesc(p.module, s.loc.t, dkVar)
+      genGlobalVarDecl(p, n, td, value, decl)
+      if s.constraint.isNil:
+        if value != "":
+          if p.module.compileToCpp and value.startsWith "{{}":
+            # TODO: taking this branch, re"\{\{\}(,\s\{\})*\}" might be emitted, resulting in
+            # either warnings (GCC 12.2+) or errors (Clang 15, MSVC 19.3+) of C++11+ compilers **when
+            # explicit constructors are around** due to overload resolution rules in place [^0][^1][^2]
+            # *Workaround* here: have C++'s static initialization mechanism do the default init work,
+            # for us lacking a deeper knowledge of an imported object's constructors' ex-/implicitness
+            # (so far) *and yet* trying to achieve default initialization.
+            # Still, generating {}s in genConstObjConstr() just to omit them here is faaaar from ideal;
+            # need to figure out a better way, possibly by keeping around more data about the
+            # imported objects' contructors?
+            #
+            # [^0]: https://en.cppreference.com/w/cpp/language/aggregate_initialization
+            # [^1]: https://cplusplus.github.io/CWG/issues/1518.html
+            # [^2]: https://eel.is/c++draft/over.match.ctor
+            decl.addf(" $1;$n", [s.loc.snippet])
+          else:
+            decl.addf(" $1 = $2;$n", [s.loc.snippet, value])
+        else:
+          decl.addf(" $1;$n", [s.loc.snippet])
+
+      p.module.s[cfsVars].add(decl)
+  if p.withinLoop > 0 and value == "":
     # fixes tests/run/tzeroarray:
     resetLoc(p, s.loc)
-  if p.module.module.options * {optStackTrace, optEndb} ==
-                               {optStackTrace, optEndb}:
-    appcg(p.module, p.module.s[cfsDebugInit],
-          "#dbgRegisterGlobal($1, &$2, $3);$n",
-         [makeCString(normalize(s.owner.name.s & '.' & s.name.s)),
-          s.loc.r, genTypeInfo(p.module, s.typ, n.info)])
-
-proc assignParam(p: BProc, s: PSym) =
-  assert(s.loc.r != nil)
+
+proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode; didGenTemp: var bool) =
+  let s = vn.sym
+  fillBackendName(p.module, s)
+  fillLoc(s.loc, locGlobalVar, vn, OnHeap)
+  var decl: Rope = ""
+  let td = getTypeDesc(p.module, vn.sym.typ, dkVar)
+  genGlobalVarDecl(p, vn, td, "", decl)
+  decl.add " " & $s.loc.snippet
+  genCppVarForCtor(p, value, decl, didGenTemp)
+  if didGenTemp:  return # generated in the caller
+  p.module.s[cfsVars].add decl
+
+proc assignParam(p: BProc, s: PSym, retType: PType) =
+  assert(s.loc.snippet != "")
   scopeMangledParam(p, s)
-  localDebugInfo(p, s)
 
 proc fillProcLoc(m: BModule; n: PNode) =
   let sym = n.sym
   if sym.loc.k == locNone:
-    fillLoc(sym.loc, locProc, n, mangleName(m, sym), OnStack)
+    fillBackendName(m, sym)
+    fillLoc(sym.loc, locProc, n, OnStack)
 
 proc getLabel(p: BProc): TLabel =
   inc(p.labels)
   result = "LA" & rope(p.labels) & "_"
 
 proc fixLabel(p: BProc, labl: TLabel) =
-  lineF(p, cpsStmts, "$1: ;$n", [labl])
+  p.s(cpsStmts).add("$1: ;$n" % [labl])
 
 proc genVarPrototype(m: BModule, n: PNode)
 proc requestConstImpl(p: BProc, sym: PSym)
 proc genStmts(p: BProc, t: PNode)
 proc expr(p: BProc, n: PNode, d: var TLoc)
-proc genProcPrototype(m: BModule, sym: PSym)
-proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc)
-proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags)
-proc intLiteral(i: BiggestInt): Rope
-proc genLiteral(p: BProc, n: PNode): Rope
-proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope
 
-proc initLocExpr(p: BProc, e: PNode, result: var TLoc) =
-  initLoc(result, locNone, e, OnUnknown)
+proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc)
+proc intLiteral(i: BiggestInt; result: var Rope)
+proc genLiteral(p: BProc, n: PNode; result: var Rope)
+proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope; argsCounter: var int)
+proc raiseExit(p: BProc)
+proc raiseExitCleanup(p: BProc, destroy: string)
+
+proc initLocExpr(p: BProc, e: PNode, flags: TLocFlags = {}): TLoc =
+  result = initLoc(locNone, e, OnUnknown, flags)
   expr(p, e, result)
 
-proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) =
-  initLoc(result, locNone, e, OnUnknown)
-  result.flags.incl lfSingleUse
+proc initLocExprSingleUse(p: BProc, e: PNode): TLoc =
+  result = initLoc(locNone, e, OnUnknown)
+  if e.kind in nkCallKinds and (e[0].kind != nkSym or e[0].sym.magic == mNone):
+    # We cannot check for tfNoSideEffect here because of mutable parameters.
+    discard "bug #8202; enforce evaluation order for nested calls for C++ too"
+    # We may need to consider that 'f(g())' cannot be rewritten to 'tmp = g(); f(tmp)'
+    # if 'tmp' lacks a move/assignment operator.
+    if e[0].kind == nkSym and sfCompileToCpp in e[0].sym.flags:
+      result.flags.incl lfSingleUse
+  else:
+    result.flags.incl lfSingleUse
   expr(p, e, result)
 
-proc lenField(p: BProc): Rope =
-  result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
-
 include ccgcalls, "ccgstmts.nim"
 
 proc initFrame(p: BProc, procname, filename: Rope): Rope =
-  discard cgsym(p.module, "nimFrame")
-  if p.maxFrameLen > 0:
-    discard cgsym(p.module, "VarSlot")
-    result = ropecg(p.module, "\tnimfrs_($1, $2, $3, $4);$n",
-                  procname, filename, p.maxFrameLen.rope,
-                  p.blocks[0].frameLen.rope)
-  else:
-    result = ropecg(p.module, "\tnimfr_($1, $2);$n", procname, filename)
+  const frameDefines = """
+$1define nimfr_(proc, file) \
+  TFrame FR_; \
+  FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; #nimFrame(&FR_);
+
+$1define nimln_(n) \
+  FR_.line = n;
+
+$1define nimlf_(n, file) \
+  FR_.line = n; FR_.filename = file;
+
+"""
+  if p.module.s[cfsFrameDefines].len == 0:
+    appcg(p.module, p.module.s[cfsFrameDefines], frameDefines, ["#"])
+
+  cgsym(p.module, "nimFrame")
+  result = ropecg(p.module, "\tnimfr_($1, $2);$n", [procname, filename])
 
 proc initFrameNoDebug(p: BProc; frame, procname, filename: Rope; line: int): Rope =
-  discard cgsym(p.module, "nimFrame")
-  addf(p.blocks[0].sections[cpsLocals], "TFrame $1;$n", [frame])
+  cgsym(p.module, "nimFrame")
+  p.blocks[0].sections[cpsLocals].addf("TFrame $1;$n", [frame])
   result = ropecg(p.module, "\t$1.procname = $2; $1.filename = $3; " &
                       " $1.line = $4; $1.len = -1; nimFrame(&$1);$n",
-                      frame, procname, filename, rope(line))
+                      [frame, procname, filename, line])
 
 proc deinitFrameNoDebug(p: BProc; frame: Rope): Rope =
-  result = ropecg(p.module, "\t#popFrameOfAddr(&$1);$n", frame)
+  result = ropecg(p.module, "\t#popFrameOfAddr(&$1);$n", [frame])
 
 proc deinitFrame(p: BProc): Rope =
-  result = ropecg(p.module, "\t#popFrame();$n")
+  result = ropecg(p.module, "\t#popFrame();$n", [])
 
 include ccgexprs
 
@@ -538,141 +825,166 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
   if not lib.generated:
     lib.generated = true
     var tmp = getTempName(m)
-    assert(lib.name == nil)
+    assert(lib.name == "")
     lib.name = tmp # BUGFIX: cgsym has awful side-effects
-    addf(m.s[cfsVars], "static void* $1;$n", [tmp])
+    m.s[cfsVars].addf("static void* $1;$n", [tmp])
     if lib.path.kind in {nkStrLit..nkTripleStrLit}:
       var s: TStringSeq = @[]
       libCandidates(lib.path.strVal, s)
       rawMessage(m.config, hintDependency, lib.path.strVal)
-      var loadlib: Rope = nil
-      for i in countup(0, high(s)):
+      var loadlib: Rope = ""
+      for i in 0..high(s):
         inc(m.labels)
-        if i > 0: add(loadlib, "||")
+        if i > 0: loadlib.add("||")
         let n = newStrNode(nkStrLit, s[i])
         n.info = lib.path.info
-        appcg(m, loadlib, "($1 = #nimLoadLibrary($2))$n",
-              [tmp, genStringLiteral(m, n)])
+        appcg(m, loadlib, "($1 = #nimLoadLibrary(", [tmp])
+        genStringLiteral(m, n, loadlib)
+        loadlib.addf "))$n", []
       appcg(m, m.s[cfsDynLibInit],
-            "if (!($1)) #nimLoadLibraryError($2);$n",
-            [loadlib, genStringLiteral(m, lib.path)])
+            "if (!($1)) #nimLoadLibraryError(",
+            [loadlib])
+      genStringLiteral(m, lib.path, m.s[cfsDynLibInit])
+      m.s[cfsDynLibInit].addf ");$n", []
+
     else:
       var p = newProc(nil, m)
-      p.options = p.options - {optStackTrace, optEndb}
-      var dest: TLoc
-      initLocExpr(p, lib.path, dest)
-      add(m.s[cfsVars], p.s(cpsLocals))
-      add(m.s[cfsDynLibInit], p.s(cpsInit))
-      add(m.s[cfsDynLibInit], p.s(cpsStmts))
+      p.options.excl optStackTrace
+      p.flags.incl nimErrorFlagDisabled
+      var dest: TLoc = initLoc(locTemp, lib.path, OnStack)
+      dest.snippet = getTempName(m)
+      appcg(m, m.s[cfsDynLibInit],"$1 $2;$n",
+           [getTypeDesc(m, lib.path.typ, dkVar), rdLoc(dest)])
+      expr(p, lib.path, dest)
+
+      m.s[cfsVars].add(p.s(cpsLocals))
+      m.s[cfsDynLibInit].add(p.s(cpsInit))
+      m.s[cfsDynLibInit].add(p.s(cpsStmts))
       appcg(m, m.s[cfsDynLibInit],
            "if (!($1 = #nimLoadLibrary($2))) #nimLoadLibraryError($2);$n",
            [tmp, rdLoc(dest)])
 
-  if lib.name == nil: internalError(m.config, "loadDynamicLib")
+  if lib.name == "": internalError(m.config, "loadDynamicLib")
 
 proc mangleDynLibProc(sym: PSym): Rope =
+  # we have to build this as a single rope in order not to trip the
+  # optimization in genInfixCall, see test tests/cpp/t8241.nim
   if sfCompilerProc in sym.flags:
-    # NOTE: sym.loc.r is the external name!
+    # NOTE: sym.loc.snippet is the external name!
     result = rope(sym.name.s)
   else:
-    result = "Dl_$1_" % [rope(sym.id)]
+    result = rope(strutils.`%`("Dl_$1_", $sym.id))
 
 proc symInDynamicLib(m: BModule, sym: PSym) =
   var lib = sym.annex
   let isCall = isGetProcAddr(lib)
-  var extname = sym.loc.r
+  var extname = sym.loc.snippet
   if not isCall: loadDynamicLib(m, lib)
   var tmp = mangleDynLibProc(sym)
-  sym.loc.r = tmp             # from now on we only need the internal name
+  sym.loc.snippet = tmp             # from now on we only need the internal name
   sym.typ.sym = nil           # generate a new name
   inc(m.labels, 2)
   if isCall:
     let n = lib.path
-    var a: TLoc
-    initLocExpr(m.initProc, n[0], a)
+    var a: TLoc = initLocExpr(m.initProc, n[0])
     var params = rdLoc(a) & "("
-    for i in 1 .. n.len-2:
-      initLocExpr(m.initProc, n[i], a)
+    for i in 1..<n.len-1:
+      a = initLocExpr(m.initProc, n[i])
       params.add(rdLoc(a))
       params.add(", ")
     let load = "\t$1 = ($2) ($3$4));$n" %
-        [tmp, getTypeDesc(m, sym.typ), params, makeCString($extname)]
+        [tmp, getTypeDesc(m, sym.typ, dkVar), params, makeCString($extname)]
     var last = lastSon(n)
-    if last.kind == nkHiddenStdConv: last = last.sons[1]
+    if last.kind == nkHiddenStdConv: last = last[1]
     internalAssert(m.config, last.kind == nkStrLit)
     let idx = last.strVal
     if idx.len == 0:
-      add(m.initProc.s(cpsStmts), load)
+      m.initProc.s(cpsStmts).add(load)
     elif idx.len == 1 and idx[0] in {'0'..'9'}:
-      add(m.extensionLoaders[idx[0]], load)
+      m.extensionLoaders[idx[0]].add(load)
     else:
       internalError(m.config, sym.info, "wrong index: " & idx)
   else:
     appcg(m, m.s[cfsDynLibInit],
         "\t$1 = ($2) #nimGetProcAddr($3, $4);$n",
-        [tmp, getTypeDesc(m, sym.typ), lib.name, makeCString($extname)])
-  addf(m.s[cfsVars], "$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t)])
+        [tmp, getTypeDesc(m, sym.typ, dkVar), lib.name, makeCString($extname)])
+  m.s[cfsVars].addf("$2 $1;$n", [sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar)])
 
 proc varInDynamicLib(m: BModule, sym: PSym) =
   var lib = sym.annex
-  var extname = sym.loc.r
+  var extname = sym.loc.snippet
   loadDynamicLib(m, lib)
   incl(sym.loc.flags, lfIndirect)
   var tmp = mangleDynLibProc(sym)
-  sym.loc.r = tmp             # from now on we only need the internal name
+  sym.loc.snippet = tmp             # from now on we only need the internal name
   inc(m.labels, 2)
   appcg(m, m.s[cfsDynLibInit],
       "$1 = ($2*) #nimGetProcAddr($3, $4);$n",
-      [tmp, getTypeDesc(m, sym.typ), lib.name, makeCString($extname)])
-  addf(m.s[cfsVars], "$2* $1;$n",
-      [sym.loc.r, getTypeDesc(m, sym.loc.t)])
+      [tmp, getTypeDesc(m, sym.typ, dkVar), lib.name, makeCString($extname)])
+  m.s[cfsVars].addf("$2* $1;$n",
+      [sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar)])
 
 proc symInDynamicLibPartial(m: BModule, sym: PSym) =
-  sym.loc.r = mangleDynLibProc(sym)
+  sym.loc.snippet = mangleDynLibProc(sym)
   sym.typ.sym = nil           # generate a new name
 
-proc cgsym(m: BModule, name: string): Rope =
+proc cgsymImpl(m: BModule; sym: PSym) {.inline.} =
+  case sym.kind
+  of skProc, skFunc, skMethod, skConverter, skIterator: genProc(m, sym)
+  of skVar, skResult, skLet: genVarPrototype(m, newSymNode sym)
+  of skType: discard getTypeDesc(m, sym.typ)
+  else: internalError(m.config, "cgsym: " & $sym.kind)
+
+proc cgsym(m: BModule, name: string) =
+  let sym = magicsys.getCompilerProc(m.g.graph, name)
+  if sym != nil:
+    cgsymImpl m, sym
+  else:
+    rawMessage(m.config, errGenerated, "system module needs: " & name)
+
+proc cgsymValue(m: BModule, name: string): Rope =
   let sym = magicsys.getCompilerProc(m.g.graph, name)
   if sym != nil:
-    case sym.kind
-    of skProc, skFunc, skMethod, skConverter, skIterator: genProc(m, sym)
-    of skVar, skResult, skLet: genVarPrototype(m, newSymNode sym)
-    of skType: discard getTypeDesc(m, sym.typ)
-    else: internalError(m.config, "cgsym: " & name & ": " & $sym.kind)
+    cgsymImpl m, sym
   else:
-    # we used to exclude the system module from this check, but for DLL
-    # generation support this sloppyness leads to hard to detect bugs, so
-    # we're picky here for the system module too:
     rawMessage(m.config, errGenerated, "system module needs: " & name)
-  result = sym.loc.r
+  result = sym.loc.snippet
+  if m.hcrOn and sym != nil and sym.kind in {skProc..skIterator}:
+    result.addActualSuffixForHCR(m.module, sym)
 
 proc generateHeaders(m: BModule) =
-  add(m.s[cfsHeaders], "\L#include \"nimbase.h\"\L")
+  var nimbase = m.config.nimbasePattern
+  if nimbase == "": nimbase = "nimbase.h"
+  m.s[cfsHeaders].addf("\L#include \"$1\"\L", [nimbase])
 
   for it in m.headerFiles:
     if it[0] == '#':
-      add(m.s[cfsHeaders], rope(it.replace('`', '"') & "\L"))
-    elif it[0] notin {'\"', '<'}:
-      addf(m.s[cfsHeaders], "#include \"$1\"$N", [rope(it)])
+      m.s[cfsHeaders].add(rope(it.replace('`', '"') & "\L"))
+    elif it[0] notin {'"', '<'}:
+      m.s[cfsHeaders].addf("#include \"$1\"$N", [rope(it)])
     else:
-      addf(m.s[cfsHeaders], "#include $1$N", [rope(it)])
-  add(m.s[cfsHeaders], "#undef LANGUAGE_C\L")
-  add(m.s[cfsHeaders], "#undef MIPSEB\L")
-  add(m.s[cfsHeaders], "#undef MIPSEL\L")
-  add(m.s[cfsHeaders], "#undef PPC\L")
-  add(m.s[cfsHeaders], "#undef R3000\L")
-  add(m.s[cfsHeaders], "#undef R4000\L")
-  add(m.s[cfsHeaders], "#undef i386\L")
-  add(m.s[cfsHeaders], "#undef linux\L")
-  add(m.s[cfsHeaders], "#undef mips\L")
-  add(m.s[cfsHeaders], "#undef near\L")
-  add(m.s[cfsHeaders], "#undef powerpc\L")
-  add(m.s[cfsHeaders], "#undef unix\L")
-
-proc openNamespaceNim(): Rope =
-  result.add("namespace Nim {\L")
-
-proc closeNamespaceNim(): Rope =
+      m.s[cfsHeaders].addf("#include $1$N", [rope(it)])
+  m.s[cfsHeaders].add("""#undef LANGUAGE_C
+#undef MIPSEB
+#undef MIPSEL
+#undef PPC
+#undef R3000
+#undef R4000
+#undef i386
+#undef linux
+#undef mips
+#undef near
+#undef far
+#undef powerpc
+#undef unix
+""")
+
+proc openNamespaceNim(namespace: string; result: var Rope) =
+  result.add("namespace ")
+  result.add(namespace)
+  result.add(" {\L")
+
+proc closeNamespaceNim(result: var Rope) =
   result.add("}\L")
 
 proc closureSetup(p: BProc, prc: PSym) =
@@ -685,25 +997,41 @@ proc closureSetup(p: BProc, prc: PSym) =
   #echo "created environment: ", env.id, " for ", prc.name.s
   assignLocalVar(p, ls)
   # generate cast assignment:
-  linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n",
-          rdLoc(env.loc), getTypeDesc(p.module, env.typ))
+  if p.config.selectedGC == gcGo:
+    linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, ($2) ClE_0);$n",
+            [addrLoc(p.config, env.loc), getTypeDesc(p.module, env.typ)])
+  else:
+    linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n",
+            [rdLoc(env.loc), getTypeDesc(p.module, env.typ)])
+
+const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt, nkTemplateDef,
+                  nkMacroDef, nkMixinStmt, nkBindStmt, nkFormalParams} +
+                  declarativeDefs
 
 proc containsResult(n: PNode): bool =
-  if n.kind == nkSym and n.sym.kind == skResult:
-    result = true
+  result = false
+  case n.kind
+  of succ(nkEmpty)..pred(nkSym), succ(nkSym)..nkNilLit, harmless:
+    discard
+  of nkReturnStmt:
+    for i in 0..<n.len:
+      if containsResult(n[i]): return true
+    result = n.len > 0 and n[0].kind == nkEmpty
+  of nkSym:
+    if n.sym.kind == skResult:
+      result = true
   else:
-    for i in 0..<n.safeLen:
+    for i in 0..<n.len:
       if containsResult(n[i]): return true
 
 proc easyResultAsgn(n: PNode): PNode =
-  const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt} +
-                    declarativeDefs
+  result = nil
   case n.kind
   of nkStmtList, nkStmtListExpr:
     var i = 0
     while i < n.len and n[i].kind in harmless: inc i
     if i < n.len: result = easyResultAsgn(n[i])
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     if n[0].kind == nkSym and n[0].sym.kind == skResult and not containsResult(n[1]):
       incl n.flags, nfPreventCg
       return n[1]
@@ -713,143 +1041,354 @@ proc easyResultAsgn(n: PNode): PNode =
       if result != nil: incl n.flags, nfPreventCg
   else: discard
 
-proc genProcAux(m: BModule, prc: PSym) =
+type
+  InitResultEnum = enum Unknown, InitSkippable, InitRequired
+
+proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
+  # Exceptions coming from calls don't have not be considered here:
+  #
+  # proc bar(): string = raise newException(...)
+  #
+  # proc foo(): string =
+  #   # optimized out: 'reset(result)'
+  #   result = bar()
+  #
+  # try:
+  #   a = foo()
+  # except:
+  #   echo "a was not written to"
+  #
+  template allPathsInBranch(it) =
+    let a = allPathsAsgnResult(p, it)
+    case a
+    of InitRequired: return InitRequired
+    of InitSkippable: discard
+    of Unknown:
+      # sticky, but can be overwritten by InitRequired:
+      result = Unknown
+
+  result = Unknown
+  case n.kind
+  of nkStmtList, nkStmtListExpr:
+    for it in n:
+      result = allPathsAsgnResult(p, it)
+      if result != Unknown: return result
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
+    if n[0].kind == nkSym and n[0].sym.kind == skResult:
+      if not containsResult(n[1]):
+        if allPathsAsgnResult(p, n[1]) == InitRequired:
+          result = InitRequired
+        else:
+          result = InitSkippable
+      else: result = InitRequired
+    elif containsResult(n):
+      result = InitRequired
+    else:
+      result = allPathsAsgnResult(p, n[1])
+  of nkReturnStmt:
+    if n.len > 0:
+      if n[0].kind == nkEmpty and result != InitSkippable:
+        # This is a bare `return` statement, if `result` was not initialized
+        # anywhere else (or if we're not sure about this) let's require it to be
+        # initialized. This avoids cases like #9286 where this heuristic lead to
+        # wrong code being generated.
+        result = InitRequired
+      else: result = allPathsAsgnResult(p, n[0])
+  of nkIfStmt, nkIfExpr:
+    var exhaustive = false
+    result = InitSkippable
+    for it in n:
+      # Every condition must not use 'result':
+      if it.len == 2 and containsResult(it[0]):
+        return InitRequired
+      if it.len == 1: exhaustive = true
+      allPathsInBranch(it.lastSon)
+    # if the 'if' statement is not exhaustive and yet it touched 'result'
+    # in some way, say Unknown.
+    if not exhaustive: result = Unknown
+  of nkCaseStmt:
+    if containsResult(n[0]): return InitRequired
+    result = InitSkippable
+    var exhaustive = skipTypes(n[0].typ,
+        abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString, tyCstring}
+    for i in 1..<n.len:
+      let it = n[i]
+      allPathsInBranch(it.lastSon)
+      if it.kind == nkElse: exhaustive = true
+    if not exhaustive: result = Unknown
+  of nkWhileStmt:
+    # some dubious code can assign the result in the 'while'
+    # condition and that would be fine. Everything else isn't:
+    result = allPathsAsgnResult(p, n[0])
+    if result == Unknown:
+      result = allPathsAsgnResult(p, n[1])
+      # we cannot assume that the 'while' loop is really executed at least once:
+      if result == InitSkippable: result = Unknown
+  of harmless:
+    result = Unknown
+  of nkGotoState, nkBreakState:
+    # give up for now.
+    result = InitRequired
+  of nkSym:
+    # some path reads from 'result' before it was written to!
+    if n.sym.kind == skResult: result = InitRequired
+  of nkTryStmt, nkHiddenTryStmt:
+    # We need to watch out for the following problem:
+    # try:
+    #   result = stuffThatRaises()
+    # except:
+    #   discard "result was not set"
+    #
+    # So ... even if the assignment to 'result' is the very first
+    # assignment this is not good enough! The only pattern we allow for
+    # is 'finally: result = x'
+    result = InitSkippable
+    allPathsInBranch(n[0])
+    for i in 1..<n.len:
+      if n[i].kind == nkFinally:
+        result = allPathsAsgnResult(p, n[i].lastSon)
+      else:
+        allPathsInBranch(n[i].lastSon)
+  of nkCallKinds:
+    if canRaiseDisp(p, n[0]):
+      result = InitRequired
+    else:
+      for i in 0..<n.safeLen:
+        allPathsInBranch(n[i])
+  of nkRaiseStmt:
+    result = InitRequired
+  of nkChckRangeF, nkChckRange64, nkChckRange:
+    # TODO: more checks might need to be covered like overflow, indexDefect etc.
+    # bug #22852
+    result = InitRequired
+  else:
+    for i in 0..<n.safeLen:
+      allPathsInBranch(n[i])
+
+proc getProcTypeCast(m: BModule, prc: PSym): Rope =
+  result = getTypeDesc(m, prc.loc.t)
+  if prc.typ.callConv == ccClosure:
+    var rettype, params: Rope = ""
+    var check = initIntSet()
+    genProcParams(m, prc.typ, rettype, params, check)
+    result = "$1(*)$2" % [rettype, params]
+
+proc genProcBody(p: BProc; procBody: PNode) =
+  genStmts(p, procBody) # modifies p.locals, p.init, etc.
+  if {nimErrorFlagAccessed, nimErrorFlagDeclared, nimErrorFlagDisabled} * p.flags == {nimErrorFlagAccessed}:
+    p.flags.incl nimErrorFlagDeclared
+    p.blocks[0].sections[cpsLocals].add(ropecg(p.module, "NIM_BOOL* nimErr_;$n", []))
+    p.blocks[0].sections[cpsInit].add(ropecg(p.module, "nimErr_ = #nimErrorFlag();$n", []))
+
+proc isNoReturn(m: BModule; s: PSym): bool {.inline.} =
+  sfNoReturn in s.flags and m.config.exc != excGoto
+
+proc genProcAux*(m: BModule, prc: PSym) =
   var p = newProc(prc, m)
-  var header = genProcHeader(m, prc)
-  var returnStmt: Rope = nil
+  var header = newRopeAppender()
+  let isCppMember = m.config.backend == backendCpp and sfCppMember * prc.flags != {}
+  if isCppMember:
+    genMemberProcHeader(m, prc, header)
+  else:
+    genProcHeader(m, prc, header)
+  var returnStmt: Rope = ""
   assert(prc.ast != nil)
-  if sfPure notin prc.flags and prc.typ.sons[0] != nil:
+
+  var procBody = transformBody(m.g.graph, m.idgen, prc, {})
+  if sfInjectDestructors in prc.flags:
+    procBody = injectDestructorCalls(m.g.graph, m.idgen, prc, procBody)
+
+  let tmpInfo = prc.info
+  discard freshLineInfo(p, prc.info)
+
+  if sfPure notin prc.flags and prc.typ.returnType != nil:
     if resultPos >= prc.ast.len:
       internalError(m.config, prc.info, "proc has no result symbol")
-    let resNode = prc.ast.sons[resultPos]
+    let resNode = prc.ast[resultPos]
     let res = resNode.sym # get result symbol
-    if not isInvalidReturnType(m.config, prc.typ.sons[0]):
+    if not isInvalidReturnType(m.config, prc.typ) and sfConstructor notin prc.flags:
       if sfNoInit in prc.flags: incl(res.flags, sfNoInit)
-      if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(prc.getBody); val != nil):
+      if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(procBody); val != nil):
         var decl = localVarDecl(p, resNode)
-        var a: TLoc
-        initLocExprSingleUse(p, val, a)
-        linefmt(p, cpsStmts, "$1 = $2;$n", decl, rdLoc(a))
+        var a: TLoc = initLocExprSingleUse(p, val)
+        linefmt(p, cpsStmts, "$1 = $2;$n", [decl, rdLoc(a)])
       else:
         # declare the result symbol:
         assignLocalVar(p, resNode)
-        assert(res.loc.r != nil)
-        initLocalVar(p, res, immediateAsgn=false)
-      returnStmt = ropecg(p.module, "\treturn $1;$n", rdLoc(res.loc))
+        assert(res.loc.snippet != "")
+        if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and
+            allPathsAsgnResult(p, procBody) == InitSkippable:
+          # In an ideal world the codegen could rely on injectdestructors doing its job properly
+          # and then the analysis step would not be required.
+          discard "result init optimized out"
+        else:
+          initLocalVar(p, res, immediateAsgn=false)
+      returnStmt = ropecg(p.module, "\treturn $1;$n", [rdLoc(res.loc)])
+    elif sfConstructor in prc.flags:
+      resNode.sym.loc.flags.incl lfIndirect
+      fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap)
+      prc.loc.snippet = getTypeDesc(m, resNode.sym.loc.t, dkVar)
     else:
-      fillResult(p.config, resNode)
-      assignParam(p, res)
-      if sfNoInit notin prc.flags: resetLoc(p, res.loc)
+      fillResult(p.config, resNode, prc.typ)
+      assignParam(p, res, prc.typ.returnType)
+      # We simplify 'unsureAsgn(result, nil); unsureAsgn(result, x)'
+      # to 'unsureAsgn(result, x)'
+      # Sketch why this is correct: If 'result' points to a stack location
+      # the 'unsureAsgn' is a nop. If it points to a global variable the
+      # global is either 'nil' or points to valid memory and so the RC operation
+      # succeeds without touching not-initialized memory.
+      if sfNoInit in prc.flags: discard
+      elif allPathsAsgnResult(p, procBody) == InitSkippable: discard
+      else:
+        resetLoc(p, res.loc)
       if skipTypes(res.typ, abstractInst).kind == tyArray:
         #incl(res.loc.flags, lfIndirect)
         res.loc.storage = OnUnknown
 
-  for i in countup(1, sonsLen(prc.typ.n) - 1):
-    let param = prc.typ.n.sons[i].sym
+  for i in 1..<prc.typ.n.len:
+    let param = prc.typ.n[i].sym
     if param.typ.isCompileTimeOnly: continue
-    assignParam(p, param)
+    assignParam(p, param, prc.typ.returnType)
   closureSetup(p, prc)
-  genStmts(p, prc.getBody) # modifies p.locals, p.init, etc.
-  var generatedProc: Rope
-  if sfNoReturn in prc.flags:
-    if hasDeclspec in extccomp.CC[p.config.cCompiler].props:
+  genProcBody(p, procBody)
+
+  prc.info = tmpInfo
+
+  var generatedProc: Rope = ""
+  generatedProc.genCLineDir prc.info, m.config
+  if isNoReturn(p.module, prc):
+    if hasDeclspec in extccomp.CC[p.config.cCompiler].props and not isCppMember:
       header = "__declspec(noreturn) " & header
   if sfPure in prc.flags:
-    if hasDeclspec in extccomp.CC[p.config.cCompiler].props:
+    if hasDeclspec in extccomp.CC[p.config.cCompiler].props and not isCppMember:
       header = "__declspec(naked) " & header
-    generatedProc = ropecg(p.module, "$N$1 {$n$2$3$4}$N$N",
-                         header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts))
+    generatedProc.add ropecg(p.module, "$1 {$n$2$3$4}$N$N",
+                         [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)])
   else:
-    generatedProc = ropecg(p.module, "$N$1 {$N", header)
-    add(generatedProc, initGCFrame(p))
+    if m.hcrOn and isReloadable(m, prc):
+      # Add forward declaration for "_actual"-suffixed functions defined in the same module (or inline).
+      # This fixes the use of methods and also the case when 2 functions within the same module
+      # call each other using directly the "_actual" versions (an optimization) - see issue #11608
+      m.s[cfsProcHeaders].addf("$1;\n", [header])
+    generatedProc.add ropecg(p.module, "$1 {$n", [header])
     if optStackTrace in prc.options:
-      add(generatedProc, p.s(cpsLocals))
+      generatedProc.add(p.s(cpsLocals))
       var procname = makeCString(prc.name.s)
-      add(generatedProc, initFrame(p, procname, quotedFilename(p.config, prc.info)))
+      generatedProc.add(initFrame(p, procname, quotedFilename(p.config, prc.info)))
     else:
-      add(generatedProc, p.s(cpsLocals))
+      generatedProc.add(p.s(cpsLocals))
     if optProfiler in prc.options:
       # invoke at proc entry for recursion:
       appcg(p, cpsInit, "\t#nimProfile();$n", [])
-    if p.beforeRetNeeded: add(generatedProc, "{")
-    add(generatedProc, p.s(cpsInit))
-    add(generatedProc, p.s(cpsStmts))
-    if p.beforeRetNeeded: add(generatedProc, ~"\t}BeforeRet_: ;$n")
-    add(generatedProc, deinitGCFrame(p))
-    if optStackTrace in prc.options: add(generatedProc, deinitFrame(p))
-    add(generatedProc, returnStmt)
-    add(generatedProc, ~"}$N")
-  add(m.s[cfsProcs], generatedProc)
+    # this pair of {} is required for C++ (C++ is weird with its
+    # control flow integrity checks):
+    if beforeRetNeeded in p.flags: generatedProc.add("{")
+    generatedProc.add(p.s(cpsInit))
+    generatedProc.add(p.s(cpsStmts))
+    if beforeRetNeeded in p.flags: generatedProc.add("\t}BeforeRet_: ;\n")
+    if optStackTrace in prc.options: generatedProc.add(deinitFrame(p))
+    generatedProc.add(returnStmt)
+    generatedProc.add("}\n")
+  m.s[cfsProcs].add(generatedProc)
+  if isReloadable(m, prc):
+    m.s[cfsDynLibInit].addf("\t$1 = ($3) hcrRegisterProc($4, \"$1\", (void*)$2);$n",
+         [prc.loc.snippet, prc.loc.snippet & "_actual", getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
 
 proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} =
   result = (sfCompileToCpp in m.module.flags and
            sfCompileToCpp notin sym.getModule().flags and
-           m.config.cmd != cmdCompileToCpp) or (
-           sym.flags * {sfImportc, sfInfixCall, sfCompilerProc} == {sfImportc} and
+           m.config.backend != backendCpp) or (
+           sym.flags * {sfInfixCall, sfCompilerProc, sfMangleCpp} == {} and
+           sym.flags * {sfImportc, sfExportc} != {} and
            sym.magic == mNone and
-           m.config.cmd == cmdCompileToCpp)
+           m.config.backend == backendCpp)
 
 proc genProcPrototype(m: BModule, sym: PSym) =
   useHeader(m, sym)
-  if lfNoDecl in sym.loc.flags: return
+  if lfNoDecl in sym.loc.flags or sfCppMember * sym.flags != {}: return
   if lfDynamicLib in sym.loc.flags:
-    if getModule(sym).id != m.module.id and
+    if sym.itemId.module != m.module.position and
         not containsOrIncl(m.declaredThings, sym.id):
-      add(m.s[cfsVars], ropecg(m, "extern $1 $2;$n",
-                        getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)))
+      m.s[cfsVars].add(ropecg(m, "$1 $2 $3;$n",
+                        [(if isReloadable(m, sym): "static" else: "extern"),
+                        getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)]))
+      if isReloadable(m, sym):
+        m.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n",
+             [mangleDynLibProc(sym), getTypeDesc(m, sym.loc.t), getModuleDllPath(m, sym)])
   elif not containsOrIncl(m.declaredProtos, sym.id):
-    var header = genProcHeader(m, sym)
-    if sfNoReturn in sym.flags and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
-      header = "__declspec(noreturn) " & header
-    if sym.typ.callConv != ccInline and requiresExternC(m, sym):
-      header = "extern \"C\" " & header
-    if sfPure in sym.flags and hasAttribute in CC[m.config.cCompiler].props:
-      header.add(" __attribute__((naked))")
-    if sfNoReturn in sym.flags and hasAttribute in CC[m.config.cCompiler].props:
-      header.add(" __attribute__((noreturn))")
-    add(m.s[cfsProcHeaders], ropecg(m, "$1;$n", header))
-
+    let asPtr = isReloadable(m, sym)
+    var header = newRopeAppender()
+    genProcHeader(m, sym, header, asPtr)
+    if not asPtr:
+      if isNoReturn(m, sym) and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
+        header = "__declspec(noreturn) " & header
+      if sym.typ.callConv != ccInline and requiresExternC(m, sym):
+        header = "extern \"C\" " & header
+      if sfPure in sym.flags and hasAttribute in CC[m.config.cCompiler].props:
+        header.add(" __attribute__((naked))")
+      if isNoReturn(m, sym) and hasAttribute in CC[m.config.cCompiler].props:
+        header.add(" __attribute__((noreturn))")
+    m.s[cfsProcHeaders].add(ropecg(m, "$1;$N", [header]))
+
+# TODO: figure out how to rename this - it DOES generate a forward declaration
 proc genProcNoForward(m: BModule, prc: PSym) =
   if lfImportCompilerProc in prc.loc.flags:
     fillProcLoc(m, prc.ast[namePos])
     useHeader(m, prc)
     # dependency to a compilerproc:
-    discard cgsym(m, prc.name.s)
+    cgsym(m, prc.name.s)
     return
   if lfNoDecl in prc.loc.flags:
     fillProcLoc(m, prc.ast[namePos])
-    useHeader(m, prc)
     genProcPrototype(m, prc)
+  elif lfDynamicLib in prc.loc.flags:
+    var q = findPendingModule(m, prc)
+    fillProcLoc(q, prc.ast[namePos])
+    genProcPrototype(m, prc)
+    if q != nil and not containsOrIncl(q.declaredThings, prc.id):
+      symInDynamicLib(q, prc)
+      # register the procedure even though it is in a different dynamic library and will not be
+      # reloadable (and has no _actual suffix) - other modules will need to be able to get it through
+      # the hcr dynlib (also put it in the DynLibInit section - right after it gets loaded)
+      if isReloadable(q, prc):
+        q.s[cfsDynLibInit].addf("\t$1 = ($2) hcrRegisterProc($3, \"$1\", (void*)$1);$n",
+            [prc.loc.snippet, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)])
+    else:
+      symInDynamicLibPartial(m, prc)
   elif prc.typ.callConv == ccInline:
     # We add inline procs to the calling module to enable C based inlining.
     # This also means that a check with ``q.declaredThings`` is wrong, we need
     # a check for ``m.declaredThings``.
     if not containsOrIncl(m.declaredThings, prc.id):
       #if prc.loc.k == locNone:
-      fillProcLoc(m, prc.ast[namePos])
+      # mangle the inline proc based on the module where it is defined -
+      # not on the first module that uses it
+      let m2 = if m.config.symbolFiles != disabledSf: m
+               else: findPendingModule(m, prc)
+      fillProcLoc(m2, prc.ast[namePos])
       #elif {sfExportc, sfImportc} * prc.flags == {}:
       #  # reset name to restore consistency in case of hashing collisions:
       #  echo "resetting ", prc.id, " by ", m.module.name.s
-      #  prc.loc.r = nil
-      #  prc.loc.r = mangleName(m, prc)
-      useHeader(m, prc)
+      #  prc.loc.snippet = nil
+      #  prc.loc.snippet = mangleName(m, prc)
       genProcPrototype(m, prc)
       genProcAux(m, prc)
-  elif lfDynamicLib in prc.loc.flags:
-    var q = findPendingModule(m, prc)
-    fillProcLoc(q, prc.ast[namePos])
-    useHeader(m, prc)
-    genProcPrototype(m, prc)
-    if q != nil and not containsOrIncl(q.declaredThings, prc.id):
-      symInDynamicLib(q, prc)
-    else:
-      symInDynamicLibPartial(m, prc)
   elif sfImportc notin prc.flags:
     var q = findPendingModule(m, prc)
     fillProcLoc(q, prc.ast[namePos])
-    useHeader(m, prc)
+    # generate a getProc call to initialize the pointer for this
+    # externally-to-the-current-module defined proc, also important
+    # to do the declaredProtos check before the call to genProcPrototype
+    if isReloadable(m, prc) and prc.id notin m.declaredProtos and
+      q != nil and q.module.id != m.module.id:
+      m.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n",
+           [prc.loc.snippet, getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
     genProcPrototype(m, prc)
     if q != nil and not containsOrIncl(q.declaredThings, prc.id):
+      # make sure there is a "prototype" in the external module
+      # which will actually become a function pointer
+      if isReloadable(m, prc):
+        genProcPrototype(q, prc)
       genProcAux(q, prc)
   else:
     fillProcLoc(m, prc.ast[namePos])
@@ -857,25 +1396,16 @@ proc genProcNoForward(m: BModule, prc: PSym) =
     if sfInfixCall notin prc.flags: genProcPrototype(m, prc)
 
 proc requestConstImpl(p: BProc, sym: PSym) =
-  var m = p.module
-  useHeader(m, sym)
-  if sym.loc.k == locNone:
-    fillLoc(sym.loc, locData, sym.ast, mangleName(p.module, sym), OnStatic)
-  if lfNoDecl in sym.loc.flags: return
-  # declare implementation:
-  var q = findPendingModule(m, sym)
-  if q != nil and not containsOrIncl(q.declaredThings, sym.id):
-    assert q.initProc.module == q
-    addf(q.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
-        [getTypeDesc(q, sym.typ), sym.loc.r, genConstExpr(q.initProc, sym.ast)])
-  # declare header:
-  if q != m and not containsOrIncl(m.declaredThings, sym.id):
-    assert(sym.loc.r != nil)
-    let headerDecl = "extern NIM_CONST $1 $2;$n" %
-        [getTypeDesc(m, sym.loc.t), sym.loc.r]
-    add(m.s[cfsData], headerDecl)
-    if sfExportc in sym.flags and p.module.g.generatedHeader != nil:
-      add(p.module.g.generatedHeader.s[cfsData], headerDecl)
+  if genConstSetup(p, sym):
+    let m = p.module
+    # declare implementation:
+    var q = findPendingModule(m, sym)
+    if q != nil and not containsOrIncl(q.declaredThings, sym.id):
+      assert q.initProc.module == q
+      genConstDefinition(q, p, sym)
+    # declare header:
+    if q != m and not containsOrIncl(m.declaredThings, sym.id):
+      genConstHeader(m, q, p, sym)
 
 proc isActivated(prc: PSym): bool = prc.typ != nil
 
@@ -897,103 +1427,176 @@ proc genVarPrototype(m: BModule, n: PNode) =
   #assert(sfGlobal in sym.flags)
   let sym = n.sym
   useHeader(m, sym)
-  fillLoc(sym.loc, locGlobalVar, n, mangleName(m, sym), OnHeap)
-  if (lfNoDecl in sym.loc.flags) or containsOrIncl(m.declaredThings, sym.id):
+  fillBackendName(m, sym)
+  fillLoc(sym.loc, locGlobalVar, n, OnHeap)
+  if treatGlobalDifferentlyForHCR(m, sym): incl(sym.loc.flags, lfIndirect)
+
+  if (lfNoDecl in sym.loc.flags) or contains(m.declaredThings, sym.id):
     return
   if sym.owner.id != m.module.id:
     # else we already have the symbol generated!
-    assert(sym.loc.r != nil)
+    assert(sym.loc.snippet != "")
+    incl(m.declaredThings, sym.id)
     if sfThread in sym.flags:
       declareThreadVar(m, sym, true)
     else:
-      add(m.s[cfsVars], "extern ")
-      add(m.s[cfsVars], getTypeDesc(m, sym.loc.t))
-      if lfDynamicLib in sym.loc.flags: add(m.s[cfsVars], "*")
-      if sfRegister in sym.flags: add(m.s[cfsVars], " register")
-      if sfVolatile in sym.flags: add(m.s[cfsVars], " volatile")
-      addf(m.s[cfsVars], " $1;$n", [sym.loc.r])
-
-proc addIntTypes(result: var Rope; conf: ConfigRef) {.inline.} =
-  addf(result, "#define NIM_NEW_MANGLING_RULES\L" &
-               "#define NIM_INTBITS $1\L", [
+      if sym.kind in {skLet, skVar, skField, skForVar} and sym.alignment > 0:
+        m.s[cfsVars].addf "NIM_ALIGN($1) ", [rope(sym.alignment)]
+      m.s[cfsVars].add(if m.hcrOn: "static " else: "extern ")
+      m.s[cfsVars].add(getTypeDesc(m, sym.loc.t, dkVar))
+      if m.hcrOn: m.s[cfsVars].add("*")
+      if lfDynamicLib in sym.loc.flags: m.s[cfsVars].add("*")
+      if sfRegister in sym.flags: m.s[cfsVars].add(" register")
+      if sfVolatile in sym.flags: m.s[cfsVars].add(" volatile")
+      if sfNoalias in sym.flags: m.s[cfsVars].add(" NIM_NOALIAS")
+      m.s[cfsVars].addf(" $1;$n", [sym.loc.snippet])
+      if m.hcrOn: m.initProc.procSec(cpsLocals).addf(
+        "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.snippet,
+        getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(m, sym)])
+
+proc addNimDefines(result: var Rope; conf: ConfigRef) {.inline.} =
+  result.addf("#define NIM_INTBITS $1\L", [
     platform.CPU[conf.target.targetCPU].intSize.rope])
-  if optUseNimNamespace in conf.globalOptions: result.add("#define USE_NIM_NAMESPACE\L")
+  if conf.cppCustomNamespace.len > 0:
+    result.add("#define USE_NIM_NAMESPACE ")
+    result.add(conf.cppCustomNamespace)
+    result.add("\L")
+  if conf.isDefined("nimEmulateOverflowChecks"):
+    result.add("#define NIM_EmulateOverflowChecks\L")
+
+proc headerTop(): Rope =
+  result = "/* Generated by Nim Compiler v$1 */$N" % [rope(VersionAsString)]
 
 proc getCopyright(conf: ConfigRef; cfile: Cfile): Rope =
-  if optCompileOnly in conf.globalOptions:
-    result = ("/* Generated by Nim Compiler v$1 */$N" &
-        "/*   (c) " & copyrightYear & " Andreas Rumpf */$N" &
-        "/* The generated code is subject to the original license. */$N") %
-        [rope(VersionAsString)]
-  else:
-    result = ("/* Generated by Nim Compiler v$1 */$N" &
-        "/*   (c) " & copyrightYear & " Andreas Rumpf */$N" &
-        "/* The generated code is subject to the original license. */$N" &
-        "/* Compiled for: $2, $3, $4 */$N" &
-        "/* Command for C compiler:$n   $5 */$N") %
-        [rope(VersionAsString),
-        rope(platform.OS[conf.target.targetOS].name),
+  result = headerTop()
+  if optCompileOnly notin conf.globalOptions:
+    result.add ("/* Compiled for: $1, $2, $3 */$N" &
+        "/* Command for C compiler:$n   $4 */$N") %
+        [rope(platform.OS[conf.target.targetOS].name),
         rope(platform.CPU[conf.target.targetCPU].name),
         rope(extccomp.CC[conf.cCompiler].name),
         rope(getCompileCFileCmd(conf, cfile))]
 
 proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
   result = getCopyright(conf, cfile)
-  addIntTypes(result, conf)
+  if conf.hcrOn: result.add("#define NIM_HOT_CODE_RELOADING\L")
+  addNimDefines(result, conf)
+
+proc getSomeNameForModule(conf: ConfigRef, filename: AbsoluteFile): Rope =
+  ## Returns a mangled module name.
+  result = mangleModuleName(conf, filename).mangle
+
+proc getSomeNameForModule(m: BModule): Rope =
+  ## Returns a mangled module name.
+  assert m.module.kind == skModule
+  assert m.module.owner.kind == skPackage
+  result = mangleModuleName(m.g.config, m.filename).mangle
+
+proc getSomeInitName(m: BModule, suffix: string): Rope =
+  if not m.hcrOn:
+    result = getSomeNameForModule(m)
+  else:
+    result = ""
+  result.add suffix
 
-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.makeCString])
+proc getInitName(m: BModule): Rope =
+  if sfMainModule in m.module.flags:
+    # generate constant name for main module, for "easy" debugging.
+    result = rope(m.config.nimMainPrefix) & rope"NimMainModule"
+  else:
+    result = getSomeInitName(m, "Init000")
+
+proc getDatInitName(m: BModule): Rope = getSomeInitName(m, "DatInit000")
+proc getHcrInitName(m: BModule): Rope = getSomeInitName(m, "HcrInit000")
+
+proc hcrGetProcLoadCode(m: BModule, sym, prefix, handle, getProcFunc: string): Rope
 
 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 propagate
+  ## into other modules, only simple rope manipulations are allowed
+  var preMainCode: Rope = ""
+  if m.hcrOn:
+    proc loadLib(handle: string, name: string): Rope =
+      result = ""
+      let prc = magicsys.getCompilerProc(m.g.graph, name)
+      assert prc != nil
+      let n = newStrNode(nkStrLit, prc.annex.path.strVal)
+      n.info = prc.annex.path.info
+      var strLit = newRopeAppender()
+      genStringLiteral(m, n, strLit)
+      appcg(m, result, "\tif (!($1 = #nimLoadLibrary($2)))$N" &
+                       "\t\t#nimLoadLibraryError($2);$N",
+                       [handle, strLit])
+
+    preMainCode.add(loadLib("hcr_handle", "hcrGetProc"))
+    if m.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
+      preMainCode.add("\t$1PreMain();\L" % [rope m.config.nimMainPrefix])
+    else:
+      preMainCode.add("\tvoid* rtl_handle;\L")
+      preMainCode.add(loadLib("rtl_handle", "nimGC_setStackBottom"))
+      preMainCode.add(hcrGetProcLoadCode(m, "nimGC_setStackBottom", "nimrtl_", "rtl_handle", "nimGetProcAddr"))
+      preMainCode.add("\tinner = $1PreMain;\L" % [rope m.config.nimMainPrefix])
+      preMainCode.add("\tinitStackBottomWith_actual((void *)&inner);\L")
+      preMainCode.add("\t(*inner)();\L")
+  else:
+    preMainCode.add("\t$1PreMain();\L" % [rope m.config.nimMainPrefix])
+
+  var posixCmdLine: Rope = ""
+  if optNoMain notin m.config.globalOptions:
+    posixCmdLine.add "N_LIB_PRIVATE int cmdCount;\L"
+    posixCmdLine.add "N_LIB_PRIVATE char** cmdLine;\L"
+    posixCmdLine.add "N_LIB_PRIVATE char** gEnv;\L"
+
   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" &
+
+    PreMainBody = "$N" &
+      "N_LIB_PRIVATE void $3PreMainInner(void) {$N" &
       "$2" &
-      "$3" &
       "}$N$N" &
-      "void PreMain(void) {$N" &
+      "$4" &
+      "N_LIB_PRIVATE void $3PreMain(void) {$N" &
+      "##if $5$N" & # 1 for volatile call, 0 for non-volatile
       "\tvoid (*volatile inner)(void);$N" &
-      "\tsystemDatInit000();$N" &
-      "\tinner = PreMainInner;$N" &
-      "$4$5" &
+      "\tinner = $3PreMainInner;$N" &
+      "$1" &
       "\t(*inner)();$N" &
+      "##else$N" &
+      "$1" &
+      "\t$3PreMainInner();$N" &
+      "##endif$N" &
       "}$N$N"
 
     MainProcs =
-      "\tNimMain();$N"
+      "\t$^NimMain();$N"
 
     MainProcsWithResult =
-      MainProcs & "\treturn nim_program_result;$N"
+      MainProcs & ("\treturn $1nim_program_result;$N")
 
-    NimMainInner = "N_CDECL(void, NimMainInner)(void) {$N" &
+    NimMainInner = "N_LIB_PRIVATE N_CDECL(void, $5NimMainInner)(void) {$N" &
         "$1" &
       "}$N$N"
 
     NimMainProc =
-      "N_CDECL(void, NimMain)(void) {$N" &
-        "\tvoid (*volatile inner)(void);$N" &
-        "\tPreMain();$N" &
-        "\tinner = NimMainInner;$N" &
-        "$2" &
-        "\t(*inner)();$N" &
+      "N_CDECL(void, $5NimMain)(void) {$N" &
+      "##if $6$N" & # 1 for volatile call, 0 for non-volatile
+      "\tvoid (*volatile inner)(void);$N" &
+      "$4" &
+      "\tinner = $5NimMainInner;$N" &
+      "$2" &
+      "\t(*inner)();$N" &
+      "##else$N" &
+      "$4" &
+      "$2" &
+      "\t$5NimMainInner();$N" &
+      "##endif$N" &
       "}$N$N"
 
     NimMainBody = NimMainInner & NimMainProc
 
-    PosixNimMain =
-      "int cmdCount;$N" &
-      "char** cmdLine;$N" &
-      "char** gEnv;$N" &
-      NimMainBody
-
     PosixCMain =
       "int main(int argc, char** args, char** env) {$N" &
         "\tcmdLine = args;$N" &
@@ -1020,19 +1623,19 @@ proc genMainProc(m: BModule) =
     WinCDllMain =
       "BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $N" &
       "                    LPVOID lpvReserved) {$N" &
-      "\tif(fwdreason == DLL_PROCESS_ATTACH) {$N" & MainProcs & "}$N" &
+      "\tif (fwdreason == DLL_PROCESS_ATTACH) {$N" & MainProcs & "\t}$N" &
       "\treturn 1;$N}$N$N"
 
     PosixNimDllMain = WinNimDllMain
 
     PosixCDllMain =
-      "void NIM_POSIX_INIT NimMainInit(void) {$N" &
+      "N_LIB_PRIVATE void NIM_POSIX_INIT NimMainInit(void) {$N" &
         MainProcs &
       "}$N$N"
 
     GenodeNimMain =
       "extern Genode::Env *nim_runtime_env;$N" &
-      "extern void nim_component_construct(Genode::Env*);$N$N" &
+      "extern \"C\" void nim_component_construct(Genode::Env*);$N$N" &
       NimMainBody
 
     ComponentConstruct =
@@ -1047,180 +1650,413 @@ proc genMainProc(m: BModule) =
       "\t});$N" &
       "}$N$N"
 
-  var nimMain, otherMain: FormatStr
   if m.config.target.targetOS == osWindows and
       m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}:
-    if optGenGuiApp in m.config.globalOptions:
-      nimMain = WinNimMain
-      otherMain = WinCMain
-    else:
-      nimMain = WinNimDllMain
-      otherMain = WinCDllMain
     m.includeHeader("<windows.h>")
   elif m.config.target.targetOS == osGenode:
-    nimMain = GenodeNimMain
-    otherMain = ComponentConstruct
     m.includeHeader("<libc/component.h>")
-  elif optGenDynLib in m.config.globalOptions:
-    nimMain = PosixNimDllMain
-    otherMain = PosixCDllMain
-  elif m.config.target.targetOS == osStandalone:
-    nimMain = PosixNimMain
-    otherMain = StandaloneCMain
-  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)
 
   let initStackBottomCall =
-    if m.config.target.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope
-    else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N")
+    if m.config.target.targetOS == osStandalone or m.config.selectedGC in {gcNone, gcArc, gcAtomicArc, gcOrc}: "".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])
-
-  appcg(m, m.s[cfsProcs], nimMain,
-        [m.g.mainModInit, initStackBottomCall, rope(m.labels)])
-  if optNoMain notin m.config.globalOptions:
-    if optUseNimNamespace in m.config.globalOptions:
-      m.s[cfsProcs].add closeNamespaceNim() & "using namespace Nim;\L"
-
-    appcg(m, m.s[cfsProcs], otherMain, [])
-    if optUseNimNamespace in m.config.globalOptions: m.s[cfsProcs].add openNamespaceNim()
-
-proc getSomeInitName(m: PSym, suffix: string): Rope =
-  assert m.kind == skModule
-  assert m.owner.kind == skPackage
-  if {sfSystemModule, sfMainModule} * m.flags == {}:
-    result = m.owner.name.s.mangle.rope
-    result.add "_"
-  result.add m.name.s.mangle
-  result.add suffix
 
-proc getInitName(m: PSym): Rope =
-  if sfMainModule in m.flags:
-    # generate constant name for main module, for "easy" debugging.
-    result = rope"NimMainModule"
+  let isVolatile = if m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}: "1" else: "0"
+  appcg(m, m.s[cfsProcs], PreMainBody, [m.g.mainDatInit, m.g.otherModsInit, m.config.nimMainPrefix, posixCmdLine, isVolatile])
+
+  if m.config.target.targetOS == osWindows and
+      m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}:
+    if optGenGuiApp in m.config.globalOptions:
+      const nimMain = WinNimMain
+      appcg(m, m.s[cfsProcs], nimMain,
+        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile])
+    else:
+      const nimMain = WinNimDllMain
+      appcg(m, m.s[cfsProcs], nimMain,
+        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile])
+  elif m.config.target.targetOS == osGenode:
+    const nimMain = GenodeNimMain
+    appcg(m, m.s[cfsProcs], nimMain,
+        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile])
+  elif optGenDynLib in m.config.globalOptions:
+    const nimMain = PosixNimDllMain
+    appcg(m, m.s[cfsProcs], nimMain,
+        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile])
   else:
-    result = getSomeInitName(m, "Init000")
+    const nimMain = NimMainBody
+    appcg(m, m.s[cfsProcs], nimMain,
+        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile])
+
+  if optNoMain notin m.config.globalOptions:
+    if m.config.cppCustomNamespace.len > 0:
+      closeNamespaceNim(m.s[cfsProcs])
+      m.s[cfsProcs].add "using namespace " & m.config.cppCustomNamespace & ";\L"
+
+    if m.config.target.targetOS == osWindows and
+        m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}:
+      if optGenGuiApp in m.config.globalOptions:
+        const otherMain = WinCMain
+        appcg(m, m.s[cfsProcs], otherMain, [if m.hcrOn: "*" else: "", m.config.nimMainPrefix])
+      else:
+        const otherMain = WinCDllMain
+        appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix])
+    elif m.config.target.targetOS == osGenode:
+      const otherMain = ComponentConstruct
+      appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix])
+    elif optGenDynLib in m.config.globalOptions:
+      const otherMain = PosixCDllMain
+      appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix])
+    elif m.config.target.targetOS == osStandalone:
+      const otherMain = StandaloneCMain
+      appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix])
+    else:
+      const otherMain = PosixCMain
+      appcg(m, m.s[cfsProcs], otherMain, [if m.hcrOn: "*" else: "", m.config.nimMainPrefix])
+
+    if m.config.cppCustomNamespace.len > 0:
+      openNamespaceNim(m.config.cppCustomNamespace, m.s[cfsProcs])
+
+proc registerInitProcs*(g: BModuleList; m: PSym; flags: set[ModuleBackendFlag]) =
+  ## Called from the IC backend.
+  if HasDatInitProc in flags:
+    let datInit = getSomeNameForModule(g.config, g.config.toFullPath(m.info.fileIndex).AbsoluteFile) & "DatInit000"
+    g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit])
+    g.mainDatInit.addf("\t$1();$N", [datInit])
+  if HasModuleInitProc in flags:
+    let init = getSomeNameForModule(g.config, g.config.toFullPath(m.info.fileIndex).AbsoluteFile) & "Init000"
+    g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init])
+    let initCall = "\t$1();$N" % [init]
+    if sfMainModule in m.flags:
+      g.mainModInit.add(initCall)
+    elif sfSystemModule in m.flags:
+      g.mainDatInit.add(initCall) # systemInit must called right after systemDatInit if any
+    else:
+      g.otherModsInit.add(initCall)
 
-proc getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit000")
+proc whichInitProcs*(m: BModule): set[ModuleBackendFlag] =
+  # called from IC.
+  result = {}
+  if m.hcrOn or m.preInitProc.s(cpsInit).len > 0 or m.preInitProc.s(cpsStmts).len > 0:
+    result.incl HasModuleInitProc
+  for i in cfsTypeInit1..cfsDynLibInit:
+    if m.s[i].len != 0:
+      result.incl HasDatInitProc
+      break
 
-proc registerModuleToMain(g: BModuleList; m: PSym) =
-  var
+proc registerModuleToMain(g: BModuleList; m: BModule) =
+  let
     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:
-    addf(g.mainDatInit, "\t$1();$N", [datInit])
+
+  if m.hcrOn:
+    var hcrModuleMeta = "$nN_LIB_PRIVATE const char* hcr_module_list[] = {$n" % []
+    let systemModulePath = getModuleDllPath(m, g.modules[g.graph.config.m.systemFileIdx.int].module)
+    let mainModulePath = getModuleDllPath(m, m.module)
+    if sfMainModule in m.module.flags:
+      hcrModuleMeta.addf("\t$1,$n", [systemModulePath])
+    g.graph.importDeps.withValue(FileIndex(m.module.position), deps):
+      for curr in deps[]:
+        hcrModuleMeta.addf("\t$1,$n", [getModuleDllPath(m, g.modules[curr.int].module)])
+    hcrModuleMeta.addf("\t\"\"};$n", [])
+    hcrModuleMeta.addf("$nN_LIB_EXPORT N_NIMCALL(void**, HcrGetImportedModules)() { return (void**)hcr_module_list; }$n", [])
+    hcrModuleMeta.addf("$nN_LIB_EXPORT N_NIMCALL(char*, HcrGetSigHash)() { return \"$1\"; }$n$n",
+                          [($sigHash(m.module, m.config)).rope])
+    if sfMainModule in m.module.flags:
+      g.mainModProcs.add(hcrModuleMeta)
+      g.mainModProcs.addf("static void* hcr_handle;$N", [])
+      g.mainModProcs.addf("N_LIB_EXPORT N_NIMCALL(void, $1)(void);$N", [init])
+      g.mainModProcs.addf("N_LIB_EXPORT N_NIMCALL(void, $1)(void);$N", [datInit])
+      g.mainModProcs.addf("N_LIB_EXPORT N_NIMCALL(void, $1)(void*, N_NIMCALL_PTR(void*, getProcAddr)(void*, char*));$N", [m.getHcrInitName])
+      g.mainModProcs.addf("N_LIB_EXPORT N_NIMCALL(void, HcrCreateTypeInfos)(void);$N", [])
+      g.mainModInit.addf("\t$1();$N", [init])
+      g.otherModsInit.addf("\thcrInit((void**)hcr_module_list, $1, $2, $3, hcr_handle, nimGetProcAddr);$n",
+                            [mainModulePath, systemModulePath, datInit])
+      g.mainDatInit.addf("\t$1(hcr_handle, nimGetProcAddr);$N", [m.getHcrInitName])
+      g.mainDatInit.addf("\thcrAddModule($1);\n", [mainModulePath])
+      g.mainDatInit.addf("\tHcrCreateTypeInfos();$N", [])
+      # nasty nasty hack to get the command line functionality working with HCR
+      # register the 2 variables on behalf of the os module which might not even
+      # be loaded (in which case it will get collected but that is not a problem)
+      # EDIT: indeed, this hack, in combination with another un-necessary one
+      # (`makeCString` was doing line wrap of string litterals) was root cause for
+      # bug #16265.
+      let osModulePath = ($systemModulePath).replace("stdlib_system", "stdlib_os").rope
+      g.mainDatInit.addf("\thcrAddModule($1);\n", [osModulePath])
+      g.mainDatInit.add("\tint* cmd_count;\n")
+      g.mainDatInit.add("\tchar*** cmd_line;\n")
+      g.mainDatInit.addf("\thcrRegisterGlobal($1, \"cmdCount\", sizeof(cmd_count), NULL, (void**)&cmd_count);$N", [osModulePath])
+      g.mainDatInit.addf("\thcrRegisterGlobal($1, \"cmdLine\", sizeof(cmd_line), NULL, (void**)&cmd_line);$N", [osModulePath])
+      g.mainDatInit.add("\t*cmd_count = cmdCount;\n")
+      g.mainDatInit.add("\t*cmd_line = cmdLine;\n")
+    else:
+      m.s[cfsInitProc].add(hcrModuleMeta)
+    return
+
+  if m.s[cfsDatInitProc].len > 0:
+    g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit])
+    g.mainDatInit.addf("\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:
+      g.mainDatInit.add(ropecg(m, "\t#initThreadVarsEmulation();$N", []))
+    if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}:
+      g.mainDatInit.add(ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", []))
+
+  if m.s[cfsInitProc].len > 0:
+    g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init])
     let initCall = "\t$1();$N" % [init]
-    if sfMainModule in m.flags:
-      add(g.mainModInit, initCall)
+    if sfMainModule in m.module.flags:
+      g.mainModInit.add(initCall)
+    elif sfSystemModule in m.module.flags:
+      g.mainDatInit.add(initCall) # systemInit must called right after systemDatInit if any
     else:
-      add(g.otherModsInit, initCall)
+      g.otherModsInit.add(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 propagate
+  ## into other modules, only simple rope manipulations are allowed
+
+  var moduleDatInitRequired = m.hcrOn
+
+  var prc = "$1 N_NIMCALL(void, $2)(void) {$N" %
+    [rope(if m.hcrOn: "N_LIB_EXPORT" else: "N_LIB_PRIVATE"), getDatInitName(m)]
+
+  # we don't want to break into such init code - could happen if a line
+  # directive from a function written by the user spills after itself
+  genCLineDir(prc, InvalidFileIdx, 999999, m.config)
+
+  for i in cfsTypeInit1..cfsDynLibInit:
+    if m.s[i].len != 0:
+      moduleDatInitRequired = true
+      prc.add(m.s[i])
+
+  prc.addf("}$N$N", [])
+
+  if moduleDatInitRequired:
+    m.s[cfsDatInitProc].add(prc)
+    #rememberFlag(m.g.graph, m.module, HasDatInitProc)
+
+# Very similar to the contents of symInDynamicLib - basically only the
+# things needed for the hot code reloading runtime procs to be loaded
+proc hcrGetProcLoadCode(m: BModule, sym, prefix, handle, getProcFunc: string): Rope =
+  let prc = magicsys.getCompilerProc(m.g.graph, sym)
+  assert prc != nil
+  fillProcLoc(m, prc.ast[namePos])
+
+  var extname = prefix & sym
+  var tmp = mangleDynLibProc(prc)
+  prc.loc.snippet = tmp
+  prc.typ.sym = nil
+
+  if not containsOrIncl(m.declaredThings, prc.id):
+    m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.snippet, getTypeDesc(m, prc.loc.t, dkVar)])
+
+  result = "\t$1 = ($2) $3($4, $5);$n" %
+      [tmp, getTypeDesc(m, prc.typ, dkVar), getProcFunc.rope, handle.rope, makeCString(prefix & sym)]
 
 proc genInitCode(m: BModule) =
-  var initname = getInitName(m.module)
-  var prc = "N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N" % [initname]
+  ## 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 propagate
+  ## into other modules, only simple rope manipulations are allowed
+  var moduleInitRequired = m.hcrOn
+  let initname = getInitName(m)
+  var prc = "$1 N_NIMCALL(void, $2)(void) {$N" %
+    [rope(if m.hcrOn: "N_LIB_EXPORT" else: "N_LIB_PRIVATE"), initname]
+  # we don't want to break into such init code - could happen if a line
+  # directive from a function written by the user spills after itself
+  genCLineDir(prc, InvalidFileIdx, 999999, m.config)
   if m.typeNodes > 0:
-    appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n",
-          [m.typeNodesName, rope(m.typeNodes)])
+    if m.hcrOn:
+      appcg(m, m.s[cfsTypeInit1], "\t#TNimNode* $1;$N", [m.typeNodesName])
+      appcg(m, m.s[cfsTypeInit1], "\thcrRegisterGlobal($3, \"$1_$2\", sizeof(TNimNode) * $2, NULL, (void**)&$1);$N",
+            [m.typeNodesName, m.typeNodes, getModuleDllPath(m, m.module)])
+    else:
+      appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n",
+            [m.typeNodesName, m.typeNodes])
   if m.nimTypes > 0:
     appcg(m, m.s[cfsTypeInit1], "static #TNimType $1[$2];$n",
-          [m.nimTypesName, rope(m.nimTypes)])
-
-  add(prc, initGCFrame(m.initProc))
-
-  add(prc, genSectionStart(cpsLocals, m.config))
-  add(prc, m.preInitProc.s(cpsLocals))
-  add(prc, m.initProc.s(cpsLocals))
-  add(prc, m.postInitProc.s(cpsLocals))
-  add(prc, genSectionEnd(cpsLocals, m.config))
-
-  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, m.postInitProc.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, m.postInitProc.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)])
+          [m.nimTypesName, m.nimTypes])
+
+  if m.hcrOn:
+    prc.addf("\tint* nim_hcr_dummy_ = 0;$n" &
+              "\tNIM_BOOL nim_hcr_do_init_ = " &
+                  "hcrRegisterGlobal($1, \"module_initialized_\", 1, NULL, (void**)&nim_hcr_dummy_);$n",
+      [getModuleDllPath(m, m.module)])
+
+  template writeSection(thing: untyped, section: TCProcSection, addHcrGuards = false) =
+    if m.thing.s(section).len > 0:
+      moduleInitRequired = true
+      if addHcrGuards: prc.add("\tif (nim_hcr_do_init_) {\n\n")
+      prc.add(m.thing.s(section))
+      if addHcrGuards: prc.add("\n\t} // nim_hcr_do_init_\n")
+
+  if m.preInitProc.s(cpsInit).len > 0 or m.preInitProc.s(cpsStmts).len > 0:
+    # Give this small function its own scope
+    prc.addf("{$N", [])
+    # Keep a bogus frame in case the code needs one
+    prc.add("\tTFrame FR_; FR_.len = 0;\n")
+
+    writeSection(preInitProc, cpsLocals)
+    writeSection(preInitProc, cpsInit, m.hcrOn)
+    writeSection(preInitProc, cpsStmts)
+    prc.addf("}/* preInitProc end */$N", [])
+    when false:
+      m.initProc.blocks[0].sections[cpsLocals].add m.preInitProc.s(cpsLocals)
+      m.initProc.blocks[0].sections[cpsInit].prepend m.preInitProc.s(cpsInit)
+      m.initProc.blocks[0].sections[cpsStmts].prepend m.preInitProc.s(cpsStmts)
+
+  # add new scope for following code, because old vcc compiler need variable
+  # be defined at the top of the block
+  prc.addf("{$N", [])
+  writeSection(initProc, cpsLocals)
+
+  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)
+        prc.add(initFrame(m.initProc, procname, quotedFilename(m.config, m.module.info)))
+      else:
+        prc.add("\tTFrame FR_; FR_.len = 0;\n")
 
-  for i in cfsTypeInit1..cfsDynLibInit:
-    add(prc, genSectionStart(i, m.config))
-    add(prc, m.s[i])
-    add(prc, genSectionEnd(i, m.config))
+    writeSection(initProc, cpsInit, m.hcrOn)
+    writeSection(initProc, cpsStmts)
+
+    if beforeRetNeeded in m.initProc.flags:
+      prc.add("\tBeforeRet_: ;\n")
+
+    if m.config.exc == excGoto:
+      if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil:
+        m.appcg(prc, "\t#nimTestErrorFlag();$n", [])
+
+    if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
+      prc.add(deinitFrame(m.initProc))
+
+  prc.addf("}$N", [])
+
+  prc.addf("}$N$N", [])
 
-  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)
+
+  if m.hcrOn:
+    var procsToLoad = @["hcrRegisterProc", "hcrGetProc", "hcrRegisterGlobal", "hcrGetGlobal"]
+
+    m.s[cfsInitProc].addf("N_LIB_EXPORT N_NIMCALL(void, $1)(void* handle, N_NIMCALL_PTR(void*, getProcAddr)(void*, char*)) {$N", [getHcrInitName(m)])
+    if sfMainModule in m.module.flags:
+      # additional procs to load
+      procsToLoad.add("hcrInit")
+      procsToLoad.add("hcrAddModule")
+    # load procs
+    for curr in procsToLoad:
+      m.s[cfsInitProc].add(hcrGetProcLoadCode(m, curr, "", "handle", "getProcAddr"))
+    m.s[cfsInitProc].addf("}$N$N", [])
 
   for i, el in pairs(m.extensionLoaders):
-    if el != nil:
+    if el != "":
       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
+      prc.add(ex)
+
+  if moduleInitRequired or sfMainModule in m.module.flags:
+    m.s[cfsInitProc].add(prc)
+    #rememberFlag(m.g.graph, m.module, HasModuleInitProc)
+
+  genDatInitCode(m)
+
+  if m.hcrOn:
+    m.s[cfsInitProc].addf("N_LIB_EXPORT N_NIMCALL(void, HcrCreateTypeInfos)(void) {$N", [])
+    m.s[cfsInitProc].add(m.hcrCreateTypeInfosProc)
+    m.s[cfsInitProc].addf("}$N$N", [])
+
+  registerModuleToMain(m.g, m)
+
+proc postprocessCode(conf: ConfigRef, r: var Rope) =
+  # find the first directive
+  var f = r.find(postprocessDirStart)
+  if f == -1:
+    return
+
+  var
+    nimlnDirLastF = ""
+
+  var res: Rope = r.substr(0, f - 1)
+  while f != -1:
+    var
+      e = r.find(postprocessDirEnd, f + 1)
+      dir = r.substr(f + 1, e - 1).split(postprocessDirSep)
+    case dir[0]
+    of "nimln":
+      if dir[2] == nimlnDirLastF:
+        res.add("nimln_(" & dir[1] & ");")
+      else:
+        res.add("nimlf_(" & dir[1] & ", " & quotedFilename(conf, dir[2].parseInt.FileIndex) & ");")
+        nimlnDirLastF = dir[2]
+    else:
+      raiseAssert "unexpected postprocess directive"
+
+    # find the next directive
+    f = r.find(postprocessDirStart, e + 1)
+    # copy the code until the next directive
+    if f != -1:
+      res.add(r.substr(e + 1, f - 1))
+    else:
+      res.add(r.substr(e + 1))
+
+  r = res
 
 proc genModule(m: BModule, cfile: Cfile): Rope =
+  var moduleIsEmpty = true
+
   result = getFileHeader(m.config, cfile)
-  result.add(genMergeInfo(m))
 
   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 optUseNimNamespace in m.config.globalOptions and i == cfsHeaders:
-      result.add openNamespaceNim()
-  add(result, m.s[cfsInitProc])
-  if optUseNimNamespace in m.config.globalOptions: result.add closeNamespaceNim()
-
-proc newPreInitProc(m: BModule): BProc =
-  result = newProc(nil, m)
-  # little hack so that unique temporaries are generated:
-  result.labels = 100_000
-
-proc newPostInitProc(m: BModule): BProc =
-  result = newProc(nil, m)
-  # little hack so that unique temporaries are generated:
-  result.labels = 200_000
+  result.add(m.s[cfsHeaders])
+  if m.config.cppCustomNamespace.len > 0:
+    openNamespaceNim(m.config.cppCustomNamespace, result)
+  if m.s[cfsFrameDefines].len > 0:
+    result.add(m.s[cfsFrameDefines])
+
+  for i in cfsForwardTypes..cfsProcs:
+    if m.s[i].len > 0:
+      moduleIsEmpty = false
+      result.add(m.s[i])
+
+  if m.s[cfsInitProc].len > 0:
+    moduleIsEmpty = false
+    result.add(m.s[cfsInitProc])
+  if m.s[cfsDatInitProc].len > 0 or m.hcrOn:
+    moduleIsEmpty = false
+    result.add(m.s[cfsDatInitProc])
+
+  if m.config.cppCustomNamespace.len > 0:
+    closeNamespaceNim(result)
+
+  if optLineDir in m.config.options:
+    var srcFileDefs = ""
+    for fi in 0..m.config.m.fileInfos.high:
+      srcFileDefs.add("#define FX_" & $fi & " " & makeSingleLineCString(toFullPath(m.config, fi.FileIndex)) & "\n")
+    result = srcFileDefs & result
+
+  if moduleIsEmpty:
+    result = ""
+
+  postprocessCode(m.config, result)
 
 proc initProcOptions(m: BModule): TOptions =
   let opts = m.config.options
   if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts
 
-proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
+proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule =
   new(result)
   result.g = g
   result.tmpBase = rope("TM" & $hashOwner(module) & "_")
@@ -1235,76 +2071,28 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
   result.typeInfoMarker = initTable[SigHash, Rope]()
   result.sigConflicts = initCountTable[SigHash]()
   result.initProc = newProc(nil, result)
+  for i in low(result.s)..high(result.s): result.s[i] = newRopeAppender()
   result.initProc.options = initProcOptions(result)
-  result.preInitProc = newPreInitProc(result)
-  result.postInitProc = newPostInitProc(result)
-  initNodeTable(result.dataCache)
+  result.preInitProc = newProc(nil, result)
+  result.preInitProc.flags.incl nimErrorFlagDisabled
+  result.preInitProc.labels = 100_000 # little hack so that unique temporaries are generated
+  result.dataCache = initNodeTable()
   result.typeStack = @[]
-  result.forwardedProcs = @[]
   result.typeNodesName = getTempName(result)
   result.nimTypesName = getTempName(result)
   # no line tracing for the init sections of the system module so that we
-  # don't generate a TFrame which can confuse the stack botton initialization:
+  # don't generate a TFrame which can confuse the stack bottom initialization:
   if sfSystemModule in module.flags:
     incl result.flags, preventStackTrace
     excl(result.preInitProc.options, optStackTrace)
-    excl(result.postInitProc.options, optStackTrace)
-  let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCFilePath(g.config, filename), "ndi")
-                else: ""
+  let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCfilePath(g.config, filename), "ndi")
+                else: AbsoluteFile""
   open(result.ndi, ndiName, g.config)
 
-proc nullify[T](arr: var T) =
-  for i in low(arr)..high(arr):
-    arr[i] = Rope(nil)
-
-proc resetModule*(m: BModule) =
-  # between two compilations in CAAS mode, we can throw
-  # away all the data that was written to disk
-  m.headerFiles = @[]
-  m.declaredProtos = initIntSet()
-  m.forwTypeCache = initTable[SigHash, Rope]()
-  m.initProc = newProc(nil, m)
-  m.initProc.options = initProcOptions(m)
-  m.preInitProc = newPreInitProc(m)
-  m.postInitProc = newPostInitProc(m)
-  initNodeTable(m.dataCache)
-  m.typeStack = @[]
-  m.forwardedProcs = @[]
-  m.typeNodesName = getTempName(m)
-  m.nimTypesName = getTempName(m)
-  if sfSystemModule in m.module.flags:
-    incl m.flags, preventStackTrace
-  else:
-    excl m.flags, preventStackTrace
-  nullify m.s
-  m.typeNodes = 0
-  m.nimTypes = 0
-  nullify m.extensionLoaders
-
-  # indicate that this is now cached module
-  # the cache will be invalidated by nullifying gModules
-  #m.fromCache = true
-  m.g = nil
-
-  # we keep only the "merge info" information for the module
-  # and the properties that can't change:
-  # m.filename
-  # m.cfilename
-  # m.isHeaderFile
-  # m.module ?
-  # m.typeCache
-  # m.declaredThings
-  # m.typeInfoMarker
-  # m.labels
-  # m.FrameDeclared
-
-proc resetCgenModules*(g: BModuleList) =
-  for m in cgenModules(g): resetModule(m)
-
 proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
-  result = rawNewModule(g, module, toFullPath(conf, module.position.FileIndex))
+  result = rawNewModule(g, module, AbsoluteFile toFullPath(conf, module.position.FileIndex))
 
-proc newModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
+proc newModule*(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
   # we should create only one cgen module for each module sym
   result = rawNewModule(g, module, conf)
   if module.position >= g.modules.len:
@@ -1317,49 +2105,45 @@ template injectG() {.dirty.} =
     graph.backend = newModuleList(graph)
   let g = BModuleList(graph.backend)
 
-proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
+proc setupCgen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
   injectG()
   result = newModule(g, module, graph.config)
+  result.idgen = idgen
   if optGenIndex in graph.config.globalOptions and g.generatedHeader == nil:
-    let f = if graph.config.headerFile.len > 0: graph.config.headerFile
+    let f = if graph.config.headerFile.len > 0: AbsoluteFile graph.config.headerFile
             else: graph.config.projectFull
     g.generatedHeader = rawNewModule(g, module,
-      changeFileExt(completeCFilePath(graph.config, f), hExt))
+      changeFileExt(completeCfilePath(graph.config, f), hExt))
     incl g.generatedHeader.flags, isHeaderFile
 
 proc writeHeader(m: BModule) =
-  var result = ("/* Generated by Nim Compiler v$1 */$N" &
-        "/*   (c) 2017 Andreas Rumpf */$N" &
-        "/* The generated code is subject to the original license. */$N") %
-        [rope(VersionAsString)]
-
+  var result = headerTop()
   var guard = "__$1__" % [m.filename.splitFile.name.rope]
   result.addf("#ifndef $1$n#define $1$n", [guard])
-  addIntTypes(result, m.config)
+  addNimDefines(result, m.config)
   generateHeaders(m)
 
   generateThreadLocalStorage(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 optUseNimNamespace in m.config.globalOptions and i == cfsHeaders: result.add openNamespaceNim()
-  add(result, m.s[cfsInitProc])
+  for i in cfsHeaders..cfsProcs:
+    result.add(m.s[i])
+    if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders:
+      openNamespaceNim(m.config.cppCustomNamespace, result)
+  result.add(m.s[cfsInitProc])
 
   if optGenDynLib in m.config.globalOptions:
     result.add("N_LIB_IMPORT ")
-  result.addf("N_CDECL(void, NimMain)(void);$n", [])
-  if optUseNimNamespace in m.config.globalOptions: result.add closeNamespaceNim()
+  result.addf("N_CDECL(void, $1NimMain)(void);$n", [rope m.config.nimMainPrefix])
+  if m.config.cppCustomNamespace.len > 0: closeNamespaceNim(result)
   result.addf("#endif /* $1 */$n", [guard])
   if not writeRope(result, m.filename):
-    rawMessage(m.config, errCannotOpenFile, m.filename)
+    rawMessage(m.config, errCannotOpenFile, m.filename.string)
 
-proc getCFile(m: BModule): string =
+proc getCFile(m: BModule): AbsoluteFile =
   let ext =
-      if m.compileToCpp: ".cpp"
-      elif m.config.cmd == cmdCompileToOC or sfCompileToObjC in m.module.flags: ".m"
-      else: ".c"
-  result = changeFileExt(completeCFilePath(m.config, withPackageName(m.config, m.cfilename)), ext)
+      if m.compileToCpp: ".nim.cpp"
+      elif m.config.backend == backendObjc or sfCompileToObjc in m.module.flags: ".nim.m"
+      else: ".nim.c"
+  result = changeFileExt(completeCfilePath(m.config, mangleModuleName(m.config, m.cfilename).AbsoluteFile), ext)
 
 when false:
   proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext =
@@ -1368,48 +2152,63 @@ when false:
     readMergeInfo(getCFile(m), m)
     result = m
 
-proc myProcess(b: PPassContext, n: PNode): PNode =
-  result = n
-  if b == nil: return
-  var m = BModule(b)
-  if passes.skipCodegen(m.config, n): return
+proc addHcrInitGuards(p: BProc, n: PNode, inInitGuard: var bool) =
+  if n.kind == nkStmtList:
+    for child in n:
+      addHcrInitGuards(p, child, inInitGuard)
+  else:
+    let stmtShouldExecute = n.kind in {nkVarSection, nkLetSection} or
+                            nfExecuteOnReload in n.flags
+    if inInitGuard:
+      if stmtShouldExecute:
+        endBlock(p)
+        inInitGuard = false
+    else:
+      if not stmtShouldExecute:
+        line(p, cpsStmts, "if (nim_hcr_do_init_)\n")
+        startBlock(p)
+        inInitGuard = true
+
+    genStmts(p, n)
+
+proc genTopLevelStmt*(m: BModule; n: PNode) =
+  ## Also called from `ic/cbackend.nim`.
+  if pipelineutils.skipCodegen(m.config, n): return
   m.initProc.options = initProcOptions(m)
   #softRnl = if optLineDir in m.config.options: noRnl else: rnl
   # XXX replicate this logic!
-  genStmts(m.initProc, n)
+  var transformedN = transformStmt(m.g.graph, m.idgen, m.module, n)
+  if sfInjectDestructors in m.module.flags:
+    transformedN = injectDestructorCalls(m.g.graph, m.idgen, m.module, transformedN)
 
-proc finishModule(m: BModule) =
-  var i = 0
-  while i <= high(m.forwardedProcs):
-    # Note: ``genProc`` may add to ``m.forwardedProcs``, so we cannot use
-    # a ``for`` loop here
-    var prc = m.forwardedProcs[i]
-    if sfForward in prc.flags:
-      internalError(m.config, prc.info, "still forwarded: " & prc.name.s)
-    genProcNoForward(m, prc)
-    inc(i)
-  assert(m.g.forwardedProcsCounter >= i)
-  dec(m.g.forwardedProcsCounter, i)
-  setLen(m.forwardedProcs, 0)
+  if m.hcrOn:
+    addHcrInitGuards(m.initProc, transformedN, m.inHcrInitGuard)
+  else:
+    genProcBody(m.initProc, transformedN)
 
 proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
-  result = true
   if optForceFullMake notin m.config.globalOptions:
-    if not equalsFile(code, cfile.cname):
-      if isDefined(m.config, "nimdiff"):
+    if not moduleHasChanged(m.g.graph, m.module):
+      result = false
+    elif not equalsFile(code, cfile.cname):
+      when false:
+        #m.config.symbolFiles == readOnlySf: #isDefined(m.config, "nimdiff"):
         if fileExists(cfile.cname):
-          copyFile(cfile.cname, cfile.cname & ".backup")
-          echo "diff ", cfile.cname, ".backup ", cfile.cname
+          copyFile(cfile.cname.string, cfile.cname.string & ".backup")
+          echo "diff ", cfile.cname.string, ".backup ", cfile.cname.string
         else:
-          echo "new file ", cfile.cname
+          echo "new file ", cfile.cname.string
       if not writeRope(code, cfile.cname):
-        rawMessage(m.config, errCannotOpenFile, cfile.cname)
-      return
-    if existsFile(cfile.obj) and os.fileNewer(cfile.obj, cfile.cname):
+        rawMessage(m.config, errCannotOpenFile, cfile.cname.string)
+      result = true
+    elif fileExists(cfile.obj) and os.fileNewer(cfile.obj.string, cfile.cname.string):
       result = false
+    else:
+      result = true
   else:
     if not writeRope(code, cfile.cname):
-      rawMessage(m.config, errCannotOpenFile, cfile.cname)
+      rawMessage(m.config, errCannotOpenFile, cfile.cname.string)
+    result = true
 
 # We need 2 different logics here: pending modules (including
 # 'nim__dat') may require file merging for the combination of dead code
@@ -1418,94 +2217,144 @@ proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
 # it would generate multiple 'main' procs, for instance.
 
 proc writeModule(m: BModule, pending: bool) =
-  # generate code for the init statements of the module:
+  template onExit() = close(m.ndi, m.config)
   let cfile = getCFile(m)
-
-  if true or optForceFullMake in m.config.globalOptions:
+  if moduleHasChanged(m.g.graph, m.module):
     genInitCode(m)
     finishTypeDescriptions(m)
     if sfMainModule in m.module.flags:
       # generate main file:
-      add(m.s[cfsProcHeaders], m.g.mainModProcs)
+      genMainProc(m)
+      m.s[cfsProcHeaders].add(m.g.mainModProcs)
       generateThreadVarsSize(m)
 
-    var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
-    var code = genModule(m, cf)
+  var cf = Cfile(nimname: m.module.name.s, cname: cfile,
+                  obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {})
+  var code = genModule(m, cf)
+  if code != "" or m.config.symbolFiles != disabledSf:
     when hasTinyCBackend:
-      if conf.cmd == cmdRun:
-        tccgen.compileCCode($code)
+      if m.config.cmd == cmdTcc:
+        tccgen.compileCCode($code, m.config)
+        onExit()
         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)
-    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
-    # that ``system.o`` is missing, so we need to call the C compiler for it:
-    var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
-    if not existsFile(cf.obj): cf.flags = {CfileFlag.Cached}
-    addFileToCompile(m.config, cf)
-  close(m.ndi)
+  onExit()
 
 proc updateCachedModule(m: BModule) =
   let cfile = getCFile(m)
-  var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
-
-  if mergeRequired(m) and sfMainModule notin m.module.flags:
-    mergeFiles(cfile, m)
-    genInitCode(m)
-    finishTypeDescriptions(m)
-
-    var code = genModule(m, cf)
-    if not writeRope(code, cfile):
-      rawMessage(m.config, errCannotOpenFile, cfile)
-  else:
-    cf.flags = {CfileFlag.Cached}
+  var cf = Cfile(nimname: m.module.name.s, cname: cfile,
+                 obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {})
+  if sfMainModule notin m.module.flags:
+    genMainProc(m)
+  cf.flags = {CfileFlag.Cached}
   addFileToCompile(m.config, cf)
 
-proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
-  result = n
-  if b == nil: return
-  var m = BModule(b)
-  if passes.skipCodegen(m.config, n): return
-  # if the module is cached, we don't regenerate the main proc
-  # nor the dispatchers? But if the dispatchers changed?
-  # XXX emit the dispatchers into its own .c file?
-  if n != nil:
-    m.initProc.options = initProcOptions(m)
-    genStmts(m.initProc, n)
-  # cached modules need to registered too:
-  registerModuleToMain(m.g, m.module)
-
+proc generateLibraryDestroyGlobals(graph: ModuleGraph; m: BModule; body: PNode; isDynlib: bool): PSym =
+  let procname = getIdent(graph.cache, "NimDestroyGlobals")
+  result = newSym(skProc, procname, m.idgen, m.module.owner, m.module.info)
+  result.typ = newProcType(m.module.info, m.idgen, m.module.owner)
+  result.typ.callConv = ccCDecl
+  incl result.flags, sfExportc
+  result.loc.snippet = "NimDestroyGlobals"
+  if isDynlib:
+    incl(result.loc.flags, lfExportLib)
+
+  let theProc = newNodeI(nkProcDef, m.module.info, bodyPos+1)
+  for i in 0..<theProc.len: theProc[i] = newNodeI(nkEmpty, m.module.info)
+  theProc[namePos] = newSymNode(result)
+  theProc[bodyPos] = body
+  result.ast = theProc
+
+proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
+  ## Also called from IC.
   if sfMainModule in m.module.flags:
-    if m.g.forwardedProcsCounter == 0:
-      incl m.flags, objHasKidsValid
-    let disp = generateMethodDispatchers(graph)
-    for x in disp: genProcAux(m, x.sym)
-    genMainProc(m)
+    # phase ordering problem here: We need to announce this
+    # dependency to 'nimTestErrorFlag' before system.c has been written to disk.
+    if m.config.exc == excGoto and getCompilerProc(graph, "nimTestErrorFlag") != nil:
+      cgsym(m, "nimTestErrorFlag")
+
+    if {optGenStaticLib, optGenDynLib, optNoMain} * m.config.globalOptions == {}:
+      for i in countdown(high(graph.globalDestructors), 0):
+        n.add graph.globalDestructors[i]
+    else:
+      var body = newNodeI(nkStmtList, m.module.info)
+      for i in countdown(high(graph.globalDestructors), 0):
+        body.add graph.globalDestructors[i]
+      body.flags.incl nfTransf # should not be further transformed
+      let dtor = generateLibraryDestroyGlobals(graph, m, body, optGenDynLib in m.config.globalOptions)
+      genProcAux(m, dtor)
+  if pipelineutils.skipCodegen(m.config, n): return
+  if moduleHasChanged(graph, m.module):
+    # if the module is cached, we don't regenerate the main proc
+    # nor the dispatchers? But if the dispatchers changed?
+    # XXX emit the dispatchers into its own .c file?
+    if n != nil:
+      m.initProc.options = initProcOptions(m)
+      genProcBody(m.initProc, n)
+
+    if m.hcrOn:
+      # make sure this is pulled in (meaning hcrGetGlobal() is called for it during init)
+      let sym = magicsys.getCompilerProc(m.g.graph, "programResult")
+      # ignore when not available, could be a module imported early in `system`
+      if sym != nil:
+        cgsymImpl m, sym
+      if m.inHcrInitGuard:
+        endBlock(m.initProc)
+
+    if sfMainModule in m.module.flags:
+      if m.hcrOn:
+        # pull ("define" since they are inline when HCR is on) these functions in the main file
+        # so it can load the HCR runtime and later pass the library handle to the HCR runtime which
+        # will in turn pass it to the other modules it initializes so they can initialize the
+        # register/get procs so they don't have to have the definitions of these functions as well
+        cgsym(m, "nimLoadLibrary")
+        cgsym(m, "nimLoadLibraryError")
+        cgsym(m, "nimGetProcAddr")
+        cgsym(m, "procAddrError")
+        cgsym(m, "rawWrite")
+
+      # raise dependencies on behalf of genMainProc
+      if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}:
+        cgsym(m, "initStackBottomWith")
+      if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
+        cgsym(m, "initThreadVarsEmulation")
+
+      if m.g.forwardedProcs.len == 0:
+        incl m.flags, objHasKidsValid
+      if optMultiMethods in m.g.config.globalOptions or
+          m.g.config.selectedGC notin {gcArc, gcOrc, gcAtomicArc} or
+          vtables notin m.g.config.features:
+        generateIfMethodDispatchers(graph, m.idgen)
+
+
+  let mm = m
+  m.g.modulesClosed.add mm
+
+proc genForwardedProcs(g: BModuleList) =
+  # Forward declared proc:s lack bodies when first encountered, so they're given
+  # a second pass here
+  # Note: ``genProcNoForward`` may add to ``forwardedProcs``
+  while g.forwardedProcs.len > 0:
+    let
+      prc = g.forwardedProcs.pop()
+      m = g.modules[prc.itemId.module]
+    if sfForward in prc.flags:
+      internalError(m.config, prc.info, "still forwarded: " & prc.name.s)
+
+    genProcNoForward(m, prc)
 
 proc cgenWriteModules*(backend: RootRef, config: ConfigRef) =
   let g = BModuleList(backend)
+  g.config = config
+
   # we need to process the transitive closure because recursive module
   # deps are allowed (and the system module is processed in the wrong
   # order anyway)
-  g.config = config
-  if g.generatedHeader != nil: finishModule(g.generatedHeader)
-  while g.forwardedProcsCounter > 0:
-    for m in cgenModules(g):
-      finishModule(m)
+  genForwardedProcs(g)
+
   for m in cgenModules(g):
     m.writeModule(pending=true)
   writeMapping(config, g.mapping)
   if g.generatedHeader != nil: writeHeader(g.generatedHeader)
-
-const cgenPass* = makePass(myOpen, myProcess, myClose)
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index a526a0f00..5368e9dc7 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -10,35 +10,32 @@
 ## This module contains the data structures for the C code generation phase.
 
 import
-  ast, astalgo, ropes, passes, options, intsets, platform, sighashes,
-  tables, ndi, lineinfos
+  ast, ropes, options,
+  ndi, lineinfos, pathutils, modulegraphs
 
-from modulegraphs import ModuleGraph
+import std/[intsets, tables, sets]
 
 type
   TLabel* = Rope              # for the C generator a label is just a rope
   TCFileSection* = enum       # the sections a generated C file consists of
-    cfsMergeInfo,             # section containing merge information
     cfsHeaders,               # section for C include file headers
+    cfsFrameDefines           # section for nim frame macros
     cfsForwardTypes,          # section for C forward typedefs
     cfsTypes,                 # section for C typedefs
     cfsSeqTypes,              # section for sequence types only
                               # this is needed for strange type generation
                               # reasons
-    cfsFieldInfo,             # section for field information
-    cfsTypeInfo,              # section for type information
+    cfsTypeInfo,              # section for type information (ag ABI checks)
     cfsProcHeaders,           # section for C procs prototypes
-    cfsVars,                  # section for C variable declarations
+    cfsStrData,               # section for constant string literals
     cfsData,                  # section for C constant data
+    cfsVars,                  # section for C variable declarations
     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
-    cfsDebugInit,             # section for init of debug information
     cfsDynLibInit,            # section for init of dynamic library binding
-    cfsDynLibDeinit           # section for deinitialization of dynamic
-                              # libraries
   TCTypeKind* = enum          # describes the type kind of a C type
     ctVoid, ctChar, ctBool,
     ctInt, ctInt8, ctInt16, ctInt32, ctInt64,
@@ -58,21 +55,27 @@ type
     id*: int                  # the ID of the label; positive means that it
     label*: Rope              # generated text for the label
                               # nil if label is not used
-    sections*: TCProcSections # the code beloging
+    sections*: TCProcSections # the code belonging
     isLoop*: bool             # whether block is a loop
     nestedTryStmts*: int16    # how many try statements is it nested into
     nestedExceptStmts*: int16 # how many except statements is it nested into
     frameLen*: int16
 
+  TCProcFlag* = enum
+    beforeRetNeeded,
+    threadVarAccessed,
+    hasCurFramePointer,
+    noSafePoints,
+    nimErrorFlagAccessed,
+    nimErrorFlagDeclared,
+    nimErrorFlagDisabled
+
   TCProc = object             # represents C proc that is currently generated
     prc*: PSym                # the Nim proc that this C proc belongs to
-    beforeRetNeeded*: bool    # true iff 'BeforeRet' label for proc is needed
-    threadVarAccessed*: bool  # true if the proc already accessed some threadvar
-    hasCurFramePointer*: bool # true if _nimCurFrame var needed to recover after
-                              # exception is generated
+    flags*: set[TCProcFlag]
     lastLineInfo*: TLineInfo  # to avoid generating excessive 'nimln' statements
     currLineInfo*: TLineInfo  # AST codegen will make this superfluous
-    nestedTryStmts*: seq[tuple[n: PNode, inExcept: bool]]
+    nestedTryStmts*: seq[tuple[fin: PNode, inExcept: bool, label: Natural]]
                               # in how many nested try statements we are
                               # (the vars must be volatile then)
                               # bool is true when are in the except part of a try block
@@ -85,20 +88,22 @@ type
     options*: TOptions        # options that should be used for code
                               # generation; this is the same as prc.options
                               # unless prc == nil
-    maxFrameLen*: int         # max length of frame descriptor
+    optionsStack*: seq[(TOptions, TNoteKinds)]
     module*: BModule          # used to prevent excessive parameter passing
     withinLoop*: int          # > 0 if we are within a loop
     splitDecls*: int          # > 0 if we are in some context for C++ that
                               # requires 'T x = T()' to become 'T x; x = T()'
                               # (yes, C++ is weird like that)
-    gcFrameId*: Natural       # for the GC stack marking
-    gcFrameType*: Rope        # the struct {} we put the GC markers into
+    withinTryWithExcept*: int # required for goto based exception handling
+    withinBlockLeaveActions*: int # complex to explain
     sigConflicts*: CountTable[string]
+    inUncheckedAssignSection*: int
 
   TTypeSeq* = seq[PType]
   TypeCache* = Table[SigHash, Rope]
+  TypeCacheWithOwner* = Table[SigHash, tuple[str: Rope, owner: int32]]
 
-  Codegenflag* = enum
+  CodegenFlag* = enum
     preventStackTrace,  # true if stack traces need to be prevented
     usesThreadVars,     # true if the module uses a thread var
     frameDeclared,      # hack for ROD support so that we don't declare
@@ -106,17 +111,18 @@ type
     isHeaderFile,       # C source file is the header file
     includesStringh,    # C source file already includes ``<string.h>``
     objHasKidsValid     # whether we can rely on tfObjHasKids
-
+    useAliveDataFromDce # use the `alive: IntSet` field instead of
+                        # computing alive data on our own.
 
   BModuleList* = ref object of RootObj
     mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
     mapping*: Rope             # the generated mapping file (if requested)
     modules*: seq[BModule]     # list of all compiled modules
-    forwardedProcsCounter*: int
+    modulesClosed*: 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
-    breakpoints*: Rope # later the breakpoints are inserted into the main proc
-    typeInfoMarker*: TypeCache
+    typeInfoMarker*: TypeCacheWithOwner
+    typeInfoMarkerV2*: TypeCacheWithOwner
     config*: ConfigRef
     graph*: ModuleGraph
     strVersion*, seqVersion*: int # version of the string/seq implementation to use
@@ -131,39 +137,46 @@ type
                             # unconditionally...
                             # nimtvDeps is VERY hard to cache because it's
                             # not a list of IDs nor can it be made to be one.
+    mangledPrcs*: HashSet[string]
 
-  TCGen = object of TPassContext # represents a C source file
+  TCGen = object of PPassContext # represents a C source file
     s*: TCFileSections        # sections of the C file
-    flags*: set[Codegenflag]
+    flags*: set[CodegenFlag]
     module*: PSym
-    filename*: string
-    cfilename*: string        # filename of the module (including path,
+    filename*: AbsoluteFile
+    cfilename*: AbsoluteFile  # filename of the module (including path,
                               # without extension)
     tmpBase*: Rope            # base for temp identifier generation
     typeCache*: TypeCache     # cache the generated types
+    typeABICache*: HashSet[SigHash] # cache for ABI checks; reusing typeCache
+                              # would be ideal but for some reason enums
+                              # don't seem to get cached so it'd generate
+                              # 1 ABI check per occurrence in code
     forwTypeCache*: TypeCache # cache for forward declarations of types
     declaredThings*: IntSet   # things we have declared in this .c file
     declaredProtos*: IntSet   # prototypes we have declared in this .c file
+    alive*: IntSet            # symbol IDs of alive data as computed by `dce.nim`
     headerFiles*: seq[string] # needed headers to include
     typeInfoMarker*: TypeCache # needed for generating type information
+    typeInfoMarkerV2*: TypeCache
     initProc*: BProc          # code for init procedure
-    postInitProc*: BProc      # code to be executed after the init proc
     preInitProc*: BProc       # code executed before the init proc
+    hcrCreateTypeInfosProc*: Rope # type info globals are in here when HCR=on
+    inHcrInitGuard*: bool     # We are currently within a HCR reloading guard.
     typeStack*: TTypeSeq      # used for type generation
     dataCache*: TNodeTable
-    forwardedProcs*: TSymSeq  # keep forwarded procs here
     typeNodes*, nimTypes*: int # used for type info generation
     typeNodesName*, nimTypesName*: Rope # used for type info generation
     labels*: Natural          # for generating unique module-scope names
     extensionLoaders*: array['0'..'9', Rope] # special procs for the
-                                              # OpenGL wrapper
-    injectStmt*: Rope
+                                             # OpenGL wrapper
     sigConflicts*: CountTable[SigHash]
     g*: BModuleList
     ndi*: NdiFile
 
 template config*(m: BModule): ConfigRef = m.g.config
 template config*(p: BProc): ConfigRef = p.module.g.config
+template vccAndC*(p: BProc): bool = p.module.config.cCompiler == ccVcc and p.module.config.backend == backendC
 
 proc includeHeader*(this: BModule; header: string) =
   if not this.headerFiles.contains header:
@@ -171,30 +184,35 @@ proc includeHeader*(this: BModule; header: string) =
 
 proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} =
   # section in the current block
-  result = p.blocks[p.blocks.len-1].sections[s]
+  result = p.blocks[^1].sections[s]
 
 proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} =
   # top level proc sections
   result = p.blocks[0].sections[s]
 
+proc initBlock*(): TBlock =
+  result = TBlock()
+  for i in low(result.sections)..high(result.sections):
+    result.sections[i] = newRopeAppender()
+
 proc newProc*(prc: PSym, module: BModule): BProc =
-  new(result)
-  result.prc = prc
-  result.module = module
-  if prc != nil: result.options = prc.options
-  else: result.options = module.config.options
-  newSeq(result.blocks, 1)
-  result.nestedTryStmts = @[]
-  result.finallySafePoints = @[]
-  result.sigConflicts = initCountTable[string]()
+  result = BProc(
+    prc: prc,
+    module: module,
+    optionsStack: if module.initProc != nil: module.initProc.optionsStack
+                  else: @[],
+    options: if prc != nil: prc.options
+             else: module.config.options,
+    blocks: @[initBlock()],
+    sigConflicts: initCountTable[string]())
+  if optQuirky in result.options:
+    result.flags = {nimErrorFlagDisabled}
 
 proc newModuleList*(g: ModuleGraph): BModuleList =
-  BModuleList(modules: @[], typeInfoMarker: initTable[SigHash, Rope](), config: g.config,
-    graph: g, nimtvDeps: @[], nimtvDeclared: initIntSet())
+  BModuleList(typeInfoMarker: initTable[SigHash, tuple[str: Rope, owner: int32]](),
+    config: g.config, graph: g, nimtvDeclared: initIntSet())
 
 iterator cgenModules*(g: BModuleList): BModule =
-  for i in 0..high(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 g.modules[i] != nil: yield g.modules[i]
+  for m in g.modulesClosed:
+    # iterate modules in the order they were closed
+    yield m
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index 5b58e6498..ca97d0494 100644
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -7,11 +7,18 @@
 #    distribution, for details about the copyright.
 #
 
-## This module implements code generation for multi methods.
+## This module implements code generation for methods.
 
 import
-  intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
-  sempass2, strutils, modulegraphs, lineinfos
+  options, ast, msgs, idents, renderer, types, magicsys,
+  sempass2, modulegraphs, lineinfos, astalgo
+
+import std/intsets
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+import std/[tables]
 
 proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
   var dest = skipTypes(d, abstractPtrs)
@@ -23,11 +30,11 @@ proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
       result = n
     elif diff < 0:
       result = newNodeIT(nkObjUpConv, n.info, d)
-      addSon(result, n)
+      result.add n
       if downcast: internalError(conf, n.info, "cgmeth.genConv: no upcast allowed")
     elif diff > 0:
       result = newNodeIT(nkObjDownConv, n.info, d)
-      addSon(result, n)
+      result.add n
       if not downcast:
         internalError(conf, n.info, "cgmeth.genConv: no downcast allowed")
     else:
@@ -37,53 +44,58 @@ proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
 
 proc getDispatcher*(s: PSym): PSym =
   ## can return nil if is has no dispatcher.
-  let dispn = lastSon(s.ast)
-  if dispn.kind == nkSym:
-    let disp = dispn.sym
-    if sfDispatcher in disp.flags: result = disp
+  if dispatcherPos < s.ast.len:
+    result = s.ast[dispatcherPos].sym
+    doAssert sfDispatcher in result.flags
+  else:
+    result = nil
 
 proc methodCall*(n: PNode; conf: ConfigRef): PNode =
   result = n
   # replace ordinary method by dispatcher method:
-  let disp = getDispatcher(result.sons[0].sym)
+  let disp = getDispatcher(result[0].sym)
   if disp != nil:
-    result.sons[0].sym = disp
+    result[0].typ = disp.typ
+    result[0].sym = disp
     # change the arguments to up/downcasts to fit the dispatcher's parameters:
-    for i in countup(1, sonsLen(result)-1):
-      result.sons[i] = genConv(result.sons[i], disp.typ.sons[i], true, conf)
+    for i in 1..<result.len:
+      result[i] = genConv(result[i], disp.typ[i], true, conf)
   else:
-    localError(conf, n.info, "'" & $result.sons[0] & "' lacks a dispatcher")
+    localError(conf, n.info, "'" & $result[0] & "' lacks a dispatcher")
 
 type
   MethodResult = enum No, Invalid, Yes
 
-proc sameMethodBucket(a, b: PSym): MethodResult =
+proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
+  result = No
   if a.name.id != b.name.id: return
-  if sonsLen(a.typ) != sonsLen(b.typ):
+  if a.typ.signatureLen != b.typ.signatureLen:
     return
 
-  for i in countup(1, sonsLen(a.typ) - 1):
-    var aa = a.typ.sons[i]
-    var bb = b.typ.sons[i]
+  var i = 0
+  for x, y in paramTypePairs(a.typ, b.typ):
+    inc i
+    var aa = x
+    var bb = y
     while true:
       aa = skipTypes(aa, {tyGenericInst, tyAlias})
       bb = skipTypes(bb, {tyGenericInst, tyAlias})
-      if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef, tyLent}:
-        aa = aa.lastSon
-        bb = bb.lastSon
+      if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef, tyLent, tySink}:
+        aa = aa.elementType
+        bb = bb.elementType
       else:
         break
-    if sameType(aa, bb):
+    if sameType(x, y):
       if aa.kind == tyObject and result != Invalid:
         result = Yes
-    elif aa.kind == tyObject and bb.kind == tyObject:
+    elif aa.kind == tyObject and bb.kind == tyObject and (i == 1 or multiMethods):
       let diff = inheritanceDiff(bb, aa)
       if diff < 0:
         if result != Invalid:
           result = Yes
         else:
           return No
-      elif diff != high(int):
+      elif diff != high(int) and sfFromGeneric notin (a.flags+b.flags):
         result = Invalid
       else:
         return No
@@ -91,40 +103,45 @@ proc sameMethodBucket(a, b: PSym): MethodResult =
       return No
   if result == Yes:
     # check for return type:
-    if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]):
-      if b.typ.sons[0] != nil and b.typ.sons[0].kind == tyExpr:
+    # ignore flags of return types; # bug #22673
+    if not sameTypeOrNil(a.typ.returnType, b.typ.returnType, {IgnoreFlags}):
+      if b.typ.returnType != nil and b.typ.returnType.kind == tyUntyped:
         # infer 'auto' from the base to make it consistent:
-        b.typ.sons[0] = a.typ.sons[0]
+        b.typ.setReturnType a.typ.returnType
       else:
         return No
 
 proc attachDispatcher(s: PSym, dispatcher: PNode) =
-  var L = s.ast.len-1
-  var x = s.ast.sons[L]
-  if x.kind == nkSym and sfDispatcher in x.sym.flags:
+  if dispatcherPos < s.ast.len:
     # we've added a dispatcher already, so overwrite it
-    s.ast.sons[L] = dispatcher
+    s.ast[dispatcherPos] = dispatcher
   else:
-    s.ast.add(dispatcher)
+    setLen(s.ast.sons, dispatcherPos+1)
+    if s.ast[resultPos] == nil:
+      s.ast[resultPos] = newNodeI(nkEmpty, s.info)
+    s.ast[dispatcherPos] = dispatcher
 
-proc createDispatcher(s: PSym): PSym =
-  var disp = copySym(s)
+proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
+  var disp = copySym(s, idgen)
   incl(disp.flags, sfDispatcher)
   excl(disp.flags, sfExported)
-  disp.typ = copyType(disp.typ, disp.typ.owner, false)
+  let old = disp.typ
+  disp.typ = copyType(disp.typ, idgen, disp.typ.owner)
+  copyTypeProps(g, idgen.module, disp.typ, old)
+
   # we can't inline the dispatcher itself (for now):
-  if disp.typ.callConv == ccInline: disp.typ.callConv = ccDefault
+  if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
   disp.ast = copyTree(s.ast)
-  disp.ast.sons[bodyPos] = newNodeI(nkEmpty, s.info)
-  disp.loc.r = nil
-  if s.typ.sons[0] != nil:
-    if disp.ast.sonsLen > resultPos:
-      disp.ast.sons[resultPos].sym = copySym(s.ast.sons[resultPos].sym)
+  disp.ast[bodyPos] = newNodeI(nkEmpty, s.info)
+  disp.loc.snippet = ""
+  if s.typ.returnType != nil:
+    if disp.ast.len > resultPos:
+      disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, idgen)
     else:
       # We've encountered a method prototype without a filled-in
       # resultPos slot. We put a placeholder in there that will
       # be updated in fixupDispatcher().
-      disp.ast.addSon(newNodeI(nkEmpty, s.info))
+      disp.ast.add newNodeI(nkEmpty, s.info)
   attachDispatcher(s, newSymNode(disp))
   # attach to itself to prevent bugs:
   attachDispatcher(disp, newSymNode(disp))
@@ -136,36 +153,26 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
   # from later definitions, particularly the resultPos slot. Also,
   # the lock level of the dispatcher needs to be updated/checked
   # against that of the method.
-  if disp.ast.sonsLen > resultPos and meth.ast.sonsLen > resultPos and
-     disp.ast.sons[resultPos].kind == nkEmpty:
-    disp.ast.sons[resultPos] = copyTree(meth.ast.sons[resultPos])
+  if disp.ast.len > resultPos and meth.ast.len > resultPos and
+     disp.ast[resultPos].kind == nkEmpty:
+    disp.ast[resultPos] = copyTree(meth.ast[resultPos])
 
-  # The following code works only with lock levels, so we disable
-  # it when they're not available.
-  when declared(TLockLevel):
-    proc `<`(a, b: TLockLevel): bool {.borrow.}
-    proc `==`(a, b: TLockLevel): bool {.borrow.}
-    if disp.typ.lockLevel == UnspecifiedLockLevel:
-      disp.typ.lockLevel = meth.typ.lockLevel
-    elif meth.typ.lockLevel != UnspecifiedLockLevel and
-         meth.typ.lockLevel != disp.typ.lockLevel:
-      message(conf, meth.info, warnLockLevel,
-        "method has lock level $1, but another method has $2" %
-        [$meth.typ.lockLevel, $disp.typ.lockLevel])
-      # XXX The following code silences a duplicate warning in
-      # checkMethodeffects() in sempass2.nim for now.
-      if disp.typ.lockLevel < meth.typ.lockLevel:
-        disp.typ.lockLevel = meth.typ.lockLevel
+proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
+  var witness: PSym = nil
+  if s.typ.firstParamType.owner.getModule != s.getModule and vtables in g.config.features and not
+      g.config.isDefined("nimInternalNonVtablesTesting"):
+    localError(g.config, s.info, errGenerated, "method `" & s.name.s &
+          "` can be defined only in the same module with its type (" & s.typ.firstParamType.typeToString() & ")")
+  if sfImportc in s.flags:
+    localError(g.config, s.info, errGenerated, "method `" & s.name.s &
+          "` is not allowed to have 'importc' pragmas")
 
-proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) =
-  let L = len(g.methods)
-  var witness: PSym
-  for i in countup(0, L - 1):
+  for i in 0..<g.methods.len:
     let disp = g.methods[i].dispatcher
-    case sameMethodBucket(disp, s)
+    case sameMethodBucket(disp, s, multimethods = optMultiMethods in g.config.globalOptions)
     of Yes:
-      add(g.methods[i].methods, s)
-      attachDispatcher(s, lastSon(disp.ast))
+      g.methods[i].methods.add(s)
+      attachDispatcher(s, disp.ast[dispatcherPos])
       fixupDispatcher(s, disp, g.config)
       #echo "fixup ", disp.name.s, " ", disp.id
       when useEffectSystem: checkMethodEffects(g, disp, s)
@@ -178,44 +185,49 @@ proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) =
     of Invalid:
       if witness.isNil: witness = g.methods[i].methods[0]
   # create a new dispatcher:
-  add(g.methods, (methods: @[s], dispatcher: createDispatcher(s)))
+  # stores the id and the position
+  if s.typ.firstParamType.skipTypes(skipPtrs).itemId notin g.bucketTable:
+    g.bucketTable[s.typ.firstParamType.skipTypes(skipPtrs).itemId] = 1
+  else:
+    g.bucketTable.inc(s.typ.firstParamType.skipTypes(skipPtrs).itemId)
+  g.methods.add((methods: @[s], dispatcher: createDispatcher(s, g, idgen)))
   #echo "adding ", s.info
-  #if fromCache:
-  #  internalError(s.info, "no method dispatcher found")
   if witness != nil:
     localError(g.config, s.info, "invalid declaration order; cannot attach '" & s.name.s &
                        "' to method defined here: " & g.config$witness.info)
   elif sfBase notin s.flags:
     message(g.config, s.info, warnUseBase)
 
-proc relevantCol(methods: TSymSeq, col: int): bool =
+proc relevantCol*(methods: seq[PSym], col: int): bool =
   # returns true iff the position is relevant
-  var t = methods[0].typ.sons[col].skipTypes(skipPtrs)
+  result = false
+  var t = methods[0].typ[col].skipTypes(skipPtrs)
   if t.kind == tyObject:
-    for i in countup(1, high(methods)):
-      let t2 = skipTypes(methods[i].typ.sons[col], skipPtrs)
+    for i in 1..high(methods):
+      let t2 = skipTypes(methods[i].typ[col], skipPtrs)
       if not sameType(t2, t):
         return true
 
 proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
-  for col in countup(1, sonsLen(a.typ) - 1):
+  result = 0
+  for col in FirstParamAt..<a.typ.signatureLen:
     if contains(relevantCols, col):
-      var aa = skipTypes(a.typ.sons[col], skipPtrs)
-      var bb = skipTypes(b.typ.sons[col], skipPtrs)
+      var aa = skipTypes(a.typ[col], skipPtrs)
+      var bb = skipTypes(b.typ[col], skipPtrs)
       var d = inheritanceDiff(aa, bb)
       if (d != high(int)) and d != 0:
         return d
 
-proc sortBucket(a: var TSymSeq, relevantCols: IntSet) =
+proc sortBucket*(a: var seq[PSym], relevantCols: IntSet) =
   # we use shellsort here; fast and simple
-  var n = len(a)
+  var n = a.len
   var h = 1
   while true:
     h = 3 * h + 1
     if h > n: break
   while true:
     h = h div 3
-    for i in countup(h, n - 1):
+    for i in h..<n:
       var v = a[i]
       var j = i
       while cmpSignatures(a[j - h], v, relevantCols) >= 0:
@@ -225,70 +237,72 @@ proc sortBucket(a: var TSymSeq, relevantCols: IntSet) =
       a[j] = v
     if h == 1: break
 
-proc genDispatcher(g: ModuleGraph; methods: TSymSeq, relevantCols: IntSet): PSym =
-  var base = lastSon(methods[0].ast).sym
+proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet; idgen: IdGenerator): PSym =
+  var base = methods[0].ast[dispatcherPos].sym
   result = base
-  var paramLen = sonsLen(base.typ)
+  var paramLen = base.typ.signatureLen
   var nilchecks = newNodeI(nkStmtList, base.info)
   var disp = newNodeI(nkIfStmt, base.info)
-  var ands = getSysSym(g, unknownLineInfo(), "and")
-  var iss = getSysSym(g, unknownLineInfo(), "of")
-  let boolType = getSysType(g, unknownLineInfo(), tyBool)
-  for col in countup(1, paramLen - 1):
+  var ands = getSysMagic(g, unknownLineInfo, "and", mAnd)
+  var iss = getSysMagic(g, unknownLineInfo, "of", mOf)
+  let boolType = getSysType(g, unknownLineInfo, tyBool)
+  for col in FirstParamAt..<paramLen:
     if contains(relevantCols, col):
-      let param = base.typ.n.sons[col].sym
+      let param = base.typ.n[col].sym
       if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
-        addSon(nilchecks, newTree(nkCall,
-            newSymNode(getCompilerProc(g, "chckNilDisp")), newSymNode(param)))
-  for meth in countup(0, high(methods)):
+        nilchecks.add newTree(nkCall,
+            newSymNode(getCompilerProc(g, "chckNilDisp")), newSymNode(param))
+  for meth in 0..high(methods):
     var curr = methods[meth]      # generate condition:
     var cond: PNode = nil
-    for col in countup(1, paramLen - 1):
+    for col in FirstParamAt..<paramLen:
       if contains(relevantCols, col):
         var isn = newNodeIT(nkCall, base.info, boolType)
-        addSon(isn, newSymNode(iss))
-        let param = base.typ.n.sons[col].sym
-        addSon(isn, newSymNode(param))
-        addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
+        isn.add newSymNode(iss)
+        let param = base.typ.n[col].sym
+        isn.add newSymNode(param)
+        isn.add newNodeIT(nkType, base.info, curr.typ[col])
         if cond != nil:
           var a = newNodeIT(nkCall, base.info, boolType)
-          addSon(a, newSymNode(ands))
-          addSon(a, cond)
-          addSon(a, isn)
+          a.add newSymNode(ands)
+          a.add cond
+          a.add isn
           cond = a
         else:
           cond = isn
-    let retTyp = base.typ.sons[0]
+    let retTyp = base.typ.returnType
     let call = newNodeIT(nkCall, base.info, retTyp)
-    addSon(call, newSymNode(curr))
-    for col in countup(1, paramLen - 1):
-      addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym),
-                           curr.typ.sons[col], false, g.config))
+    call.add newSymNode(curr)
+    for col in 1..<paramLen:
+      call.add genConv(newSymNode(base.typ.n[col].sym),
+                           curr.typ[col], false, g.config)
     var ret: PNode
     if retTyp != nil:
       var a = newNodeI(nkFastAsgn, base.info)
-      addSon(a, newSymNode(base.ast.sons[resultPos].sym))
-      addSon(a, call)
+      a.add newSymNode(base.ast[resultPos].sym)
+      a.add call
       ret = newNodeI(nkReturnStmt, base.info)
-      addSon(ret, a)
+      ret.add a
     else:
       ret = call
     if cond != nil:
       var a = newNodeI(nkElifBranch, base.info)
-      addSon(a, cond)
-      addSon(a, ret)
-      addSon(disp, a)
+      a.add cond
+      a.add ret
+      disp.add a
     else:
       disp = ret
   nilchecks.add disp
-  result.ast.sons[bodyPos] = nilchecks
+  nilchecks.flags.incl nfTransf # should not be further transformed
+  result.ast[bodyPos] = nilchecks
 
-proc generateMethodDispatchers*(g: ModuleGraph): PNode =
-  result = newNode(nkStmtList)
-  for bucket in countup(0, len(g.methods) - 1):
+proc generateIfMethodDispatchers*(g: ModuleGraph, idgen: IdGenerator) =
+  for bucket in 0..<g.methods.len:
     var relevantCols = initIntSet()
-    for col in countup(1, sonsLen(g.methods[bucket].methods[0].typ) - 1):
+    for col in FirstParamAt..<g.methods[bucket].methods[0].typ.signatureLen:
       if relevantCol(g.methods[bucket].methods, col): incl(relevantCols, col)
+      if optMultiMethods notin g.config.globalOptions:
+        # if multi-methods are not enabled, we are interested only in the first field
+        break
     sortBucket(g.methods[bucket].methods, relevantCols)
-    addSon(result,
-           newSymNode(genDispatcher(g, g.methods[bucket].methods, relevantCols)))
+    g.addDispatchers genIfDispatcher(g, g.methods[bucket].methods, relevantCols, idgen)
diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim
index 6fa856b2f..8bdd04ca7 100644
--- a/compiler/closureiters.nim
+++ b/compiler/closureiters.nim
@@ -18,7 +18,8 @@
 #    dec a
 #
 # Should be transformed to:
-#  STATE0:
+#  case :state
+#  of 0:
 #    if a > 0:
 #      echo "hi"
 #      :state = 1 # Next state
@@ -26,19 +27,14 @@
 #    else:
 #      :state = 2 # Next state
 #      break :stateLoop # Proceed to the next state
-#  STATE1:
+#  of 1:
 #    dec a
 #    :state = 0 # Next state
 #    break :stateLoop # Proceed to the next state
-#  STATE2:
+#  of 2:
 #    :state = -1 # End of execution
-
-# The transformation should play well with lambdalifting, however depending
-# on situation, it can be called either before or after lambdalifting
-# transformation. As such we behave slightly differently, when accessing
-# iterator state, or using temp variables. If lambdalifting did not happen,
-# we just create local variables, so that they will be lifted further on.
-# Otherwise, we utilize existing env, created by lambdalifting.
+#  else:
+#    return
 
 # Lambdalifting treats :state variable specially, it should always end up
 # as the first field in env. Currently C codegen depends on this behavior.
@@ -90,9 +86,6 @@
 #  :unrollFinally = true
 #  goto nearestFinally (or -1 if not exists)
 #
-# Every finally block calls closureIterEndFinally() upon its successful
-# completion.
-#
 # Example:
 #
 # try:
@@ -107,12 +100,13 @@
 # Is transformed to (yields are left in place for example simplicity,
 #    in reality the code is subdivided even more, as described above):
 #
-# STATE0: # Try
+# case :state
+# of 0: # Try
 #   yield 0
 #   raise ...
 #   :state = 2 # What would happen should we not raise
 #   break :stateLoop
-# STATE1: # Except
+# of 1: # Except
 #   yield 1
 #   :tmpResult = 3           # Return
 #   :unrollFinally = true # Return
@@ -120,30 +114,41 @@
 #   break :stateLoop
 #   :state = 2 # What would happen should we not return
 #   break :stateLoop
-# STATE2: # Finally
+# of 2: # Finally
 #   yield 2
 #   if :unrollFinally: # This node is created by `newEndFinallyNode`
 #     if :curExc.isNil:
-#       return :tmpResult
+#       if nearestFinally == 0:
+#         return :tmpResult
+#       else:
+#         :state = nearestFinally # bubble up
 #     else:
+#       closureIterSetupExc(nil)
 #       raise
 #   state = -1 # Goto next state. In this case we just exit
 #   break :stateLoop
+# else:
+#   return
 
 import
-  intsets, strutils, options, ast, astalgo, trees, treetab, msgs, idents,
-  renderer, types, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos
+  ast, msgs, idents,
+  renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos,
+  options
+
+import std/tables
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 type
   Ctx = object
     g: ModuleGraph
     fn: PSym
-    stateVarSym: PSym # :state variable. nil if env already introduced by lambdalifting
     tmpResultSym: PSym # Used when we return, but finally has to interfere
     unrollFinallySym: PSym # Indicates that we're unrolling finally states (either exception happened or premature return)
     curExcSym: PSym # Current exception
 
-    states: seq[PNode] # The resulting states. Every state is an nkState node.
+    states: seq[tuple[label: int, body: PNode]] # The resulting states.
     blockLevel: int # Temp used to transform break and continue stmts
     stateLoopLabel: PSym # Label to break on, when jumping between states.
     exitStateIdx: int # index of the last state
@@ -154,15 +159,24 @@ type
     curExcHandlingState: int # Negative for except, positive for finally
     nearestFinally: int # Index of the nearest finally block. For try/except it
                     # is their finally. For finally it is parent finally. Otherwise -1
+    idgen: IdGenerator
+    varStates: Table[ItemId, int] # Used to detect if local variable belongs to multiple states
+    stateVarSym: PSym # :state variable. nil if env already introduced by lambdalifting
+      # remove if -d:nimOptIters is default, treating it as always nil
+    nimOptItersEnabled: bool # tracks if -d:nimOptIters is enabled
+      # should be default when issues are fixed, see #24094
 
 const
-  nkSkip = { nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
-            nkCommentStmt } + procDefs
+  nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
+            nkCommentStmt, nkMixinStmt, nkBindStmt} + procDefs
+  emptyStateLabel = -1
+  localNotSeen = -1
+  localRequiresLifting = -2
 
 proc newStateAccess(ctx: var Ctx): PNode =
   if ctx.stateVarSym.isNil:
     result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)),
-        getStateField(ctx.g, ctx.fn), ctx.fn.info)
+      getStateField(ctx.g, ctx.fn), ctx.fn.info)
   else:
     result = newSymNode(ctx.stateVarSym)
 
@@ -174,12 +188,13 @@ proc newStateAssgn(ctx: var Ctx, toValue: PNode): PNode =
 proc newStateAssgn(ctx: var Ctx, stateNo: int = -2): PNode =
   # Creates state assignment:
   #   :state = stateNo
-  ctx.newStateAssgn(newIntTypeNode(nkIntLit, stateNo, ctx.g.getSysType(TLineInfo(), tyInt)))
+  ctx.newStateAssgn(newIntTypeNode(stateNo, ctx.g.getSysType(TLineInfo(), tyInt)))
 
 proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym =
-  result = newSym(skVar, getIdent(ctx.g.cache, name), ctx.fn, ctx.fn.info)
+  result = newSym(skVar, getIdent(ctx.g.cache, name), ctx.idgen, ctx.fn, ctx.fn.info)
   result.typ = typ
-  assert(not typ.isNil)
+  result.flags.incl sfNoInit
+  assert(not typ.isNil, "Env var needs a type")
 
   if not ctx.stateVarSym.isNil:
     # We haven't gone through labmda lifting yet, so just create a local var,
@@ -190,7 +205,7 @@ proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym =
   else:
     let envParam = getEnvParam(ctx.fn)
     # let obj = envParam.typ.lastSon
-    result = addUniqueField(envParam.typ.lastSon, result, ctx.g.cache)
+    result = addUniqueField(envParam.typ.elementType, result, ctx.g.cache, ctx.idgen)
 
 proc newEnvVarAccess(ctx: Ctx, s: PSym): PNode =
   if ctx.stateVarSym.isNil:
@@ -198,9 +213,12 @@ proc newEnvVarAccess(ctx: Ctx, s: PSym): PNode =
   else:
     result = newSymNode(s)
 
+proc newTempVarAccess(ctx: Ctx, s: PSym): PNode =
+  result = newSymNode(s, ctx.fn.info)
+
 proc newTmpResultAccess(ctx: var Ctx): PNode =
   if ctx.tmpResultSym.isNil:
-    ctx.tmpResultSym = ctx.newEnvVar(":tmpResult", ctx.fn.typ[0])
+    ctx.tmpResultSym = ctx.newEnvVar(":tmpResult", ctx.fn.typ.returnType)
   ctx.newEnvVarAccess(ctx.tmpResultSym)
 
 proc newUnrollFinallyAccess(ctx: var Ctx, info: TLineInfo): PNode =
@@ -210,7 +228,7 @@ proc newUnrollFinallyAccess(ctx: var Ctx, info: TLineInfo): PNode =
 
 proc newCurExcAccess(ctx: var Ctx): PNode =
   if ctx.curExcSym.isNil:
-    ctx.curExcSym = ctx.newEnvVar(":curExc", ctx.g.callCodegenProc("getCurrentException", ctx.g.emptyNode).typ)
+    ctx.curExcSym = ctx.newEnvVar(":curExc", ctx.g.callCodegenProc("getCurrentException").typ)
   ctx.newEnvVarAccess(ctx.curExcSym)
 
 proc newState(ctx: var Ctx, n, gotoOut: PNode): int =
@@ -220,10 +238,7 @@ proc newState(ctx: var Ctx, n, gotoOut: PNode): int =
 
   result = ctx.states.len
   let resLit = ctx.g.newIntLit(n.info, result)
-  let s = newNodeI(nkState, n.info)
-  s.add(resLit)
-  s.add(n)
-  ctx.states.add(s)
+  ctx.states.add((result, n))
   ctx.exceptionTable.add(ctx.curExcHandlingState)
 
   if not gotoOut.isNil:
@@ -239,12 +254,29 @@ proc toStmtList(n: PNode): PNode =
 proc addGotoOut(n: PNode, gotoOut: PNode): PNode =
   # Make sure `n` is a stmtlist, and ends with `gotoOut`
   result = toStmtList(n)
-  if result.len != 0 and result.sons[^1].kind != nkGotoState:
+  if result.len == 0 or result[^1].kind != nkGotoState:
     result.add(gotoOut)
 
-proc newTempVar(ctx: var Ctx, typ: PType): PSym =
-  result = ctx.newEnvVar(":tmpSlLower" & $ctx.tempVarId, typ)
+proc newTempVarDef(ctx: Ctx, s: PSym, initialValue: PNode): PNode =
+  var v = initialValue
+  if v == nil:
+    v = ctx.g.emptyNode
+  newTree(nkVarSection, newTree(nkIdentDefs, newSymNode(s), ctx.g.emptyNode, v))
+
+proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode
+
+proc newTempVar(ctx: var Ctx, typ: PType, parent: PNode, initialValue: PNode = nil): PSym =
+  if ctx.nimOptItersEnabled:
+    result = newSym(skVar, getIdent(ctx.g.cache, ":tmpSlLower" & $ctx.tempVarId), ctx.idgen, ctx.fn, ctx.fn.info)
+  else:
+    result = ctx.newEnvVar(":tmpSlLower" & $ctx.tempVarId, typ)
   inc ctx.tempVarId
+  result.typ = typ
+  assert(not typ.isNil, "Temp var needs a type")
+  if ctx.nimOptItersEnabled:
+    parent.add(ctx.newTempVarDef(result, initialValue))
+  elif initialValue != nil:
+    parent.add(ctx.newEnvVarAsgn(result, initialValue))
 
 proc hasYields(n: PNode): bool =
   # TODO: This is very inefficient. It traverses the node, looking for nkYieldStmt.
@@ -252,10 +284,11 @@ proc hasYields(n: PNode): bool =
   of nkYieldStmt:
     result = true
   of nkSkip:
-    discard
+    result = false
   else:
-    for c in n:
-      if c.hasYields:
+    result = false
+    for i in ord(n.kind == nkCast)..<n.len:
+      if n[i].hasYields:
         result = true
         break
 
@@ -275,7 +308,7 @@ proc transformBreaksAndContinuesInWhile(ctx: var Ctx, n: PNode, before, after: P
     if ctx.blockLevel == 0:
       result = after
   else:
-    for i in 0 ..< n.len:
+    for i in 0..<n.len:
       n[i] = ctx.transformBreaksAndContinuesInWhile(n[i], before, after)
 
 proc transformBreaksInBlock(ctx: var Ctx, n: PNode, label, after: PNode): PNode =
@@ -295,7 +328,7 @@ proc transformBreaksInBlock(ctx: var Ctx, n: PNode, label, after: PNode): PNode
       if label.kind == nkSym and n[0].sym == label.sym:
         result = after
   else:
-    for i in 0 ..< n.len:
+    for i in 0..<n.len:
       n[i] = ctx.transformBreaksInBlock(n[i], label, after)
 
 proc newNullifyCurExc(ctx: var Ctx, info: TLineInfo): PNode =
@@ -319,12 +352,12 @@ proc collectExceptState(ctx: var Ctx, n: PNode): PNode {.inline.} =
       var ifBranch: PNode
 
       if c.len > 1:
-        var cond: PNode
-        for i in 0 .. c.len - 2:
+        var cond: PNode = nil
+        for i in 0..<c.len - 1:
           assert(c[i].kind == nkType)
           let nextCond = newTree(nkCall,
             newSymNode(g.getSysMagic(c.info, "of", mOf)),
-            g.callCodegenProc("getCurrentException", ctx.g.emptyNode),
+            g.callCodegenProc("getCurrentException"),
             c[i])
           nextCond.typ = ctx.g.getSysType(c.info, tyBool)
           nextCond.info = c.info
@@ -359,12 +392,12 @@ proc addElseToExcept(ctx: var Ctx, n: PNode) =
     block: # :unrollFinally = true
       branchBody.add(newTree(nkAsgn,
         ctx.newUnrollFinallyAccess(n.info),
-        newIntTypeNode(nkIntLit, 1, ctx.g.getSysType(n.info, tyBool))))
+        newIntTypeNode(1, ctx.g.getSysType(n.info, tyBool))))
 
     block: # :curExc = getCurrentException()
       branchBody.add(newTree(nkAsgn,
         ctx.newCurExcAccess(),
-        ctx.g.callCodegenProc("getCurrentException", ctx.g.emptyNode)))
+        ctx.g.callCodegenProc("getCurrentException")))
 
     block: # goto nearestFinally
       branchBody.add(newTree(nkGotoState, ctx.g.newIntLit(n.info, ctx.nearestFinally)))
@@ -382,42 +415,62 @@ proc getFinallyNode(ctx: var Ctx, n: PNode): PNode =
 proc hasYieldsInExpressions(n: PNode): bool =
   case n.kind
   of nkSkip:
-    discard
+    result = false
   of nkStmtListExpr:
     if isEmptyType(n.typ):
+      result = false
       for c in n:
         if c.hasYieldsInExpressions:
           return true
     else:
       result = n.hasYields
+  of nkCast:
+    result = false
+    for i in 1..<n.len:
+      if n[i].hasYieldsInExpressions:
+        return true
   else:
+    result = false
     for c in n:
       if c.hasYieldsInExpressions:
         return true
 
 proc exprToStmtList(n: PNode): tuple[s, res: PNode] =
   assert(n.kind == nkStmtListExpr)
-  result.s = newNodeI(nkStmtList, n.info)
+  result = (newNodeI(nkStmtList, n.info), nil)
   result.s.sons = @[]
 
   var n = n
   while n.kind == nkStmtListExpr:
     result.s.sons.add(n.sons)
-    result.s.sons.setLen(result.s.sons.len - 1) # delete last son
+    result.s.sons.setLen(result.s.len - 1) # delete last son
     n = n[^1]
 
   result.res = n
 
+proc newTempVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode =
+  if isEmptyType(v.typ):
+    result = v
+  else:
+    result = newTree(nkFastAsgn, ctx.newTempVarAccess(s), v)
+    result.info = v.info
 
 proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode =
-  result = newTree(nkFastAsgn, ctx.newEnvVarAccess(s), v)
-  result.info = v.info
+  # unused with -d:nimOptIters
+  if isEmptyType(v.typ):
+    result = v
+  else:
+    result = newTree(nkFastAsgn, ctx.newEnvVarAccess(s), v)
+    result.info = v.info
 
 proc addExprAssgn(ctx: Ctx, output, input: PNode, sym: PSym) =
+  var input = input
   if input.kind == nkStmtListExpr:
     let (st, res) = exprToStmtList(input)
     output.add(st)
-    output.add(ctx.newEnvVarAsgn(sym, res))
+    input = res
+  if ctx.nimOptItersEnabled:
+    output.add(ctx.newTempVarAsgn(sym, input))
   else:
     output.add(ctx.newEnvVarAsgn(sym, input))
 
@@ -429,6 +482,16 @@ proc newNotCall(g: ModuleGraph; e: PNode): PNode =
   result = newTree(nkCall, newSymNode(g.getSysMagic(e.info, "not", mNot), e.info), e)
   result.typ = g.getSysType(e.info, tyBool)
 
+proc boolLit(g: ModuleGraph; info: TLineInfo; value: bool): PNode =
+  result = newIntLit(g, info, ord value)
+  result.typ = getSysType(g, info, tyBool)
+
+proc captureVar(c: var Ctx, s: PSym) =
+  if c.varStates.getOrDefault(s.itemId) != localRequiresLifting:
+    c.varStates[s.itemId] = localRequiresLifting # Mark this variable for lifting
+    let e = getEnvParam(c.fn)
+    discard addField(e.typ.elementType, s, c.g.cache, c.idgen)
+
 proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
   result = n
   case n.kind
@@ -437,7 +500,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
 
   of nkYieldStmt:
     var ns = false
-    for i in 0 ..< n.len:
+    for i in 0..<n.len:
       n[i] = ctx.lowerStmtListExprs(n[i], ns)
 
     if ns:
@@ -451,7 +514,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
 
   of nkPar, nkObjConstr, nkTupleConstr, nkBracket:
     var ns = false
-    for i in 0 ..< n.len:
+    for i in 0..<n.len:
       n[i] = ctx.lowerStmtListExprs(n[i], ns)
 
     if ns:
@@ -461,27 +524,33 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
       if n.typ.isNil: internalError(ctx.g.config, "lowerStmtListExprs: constr typ.isNil")
       result.typ = n.typ
 
-      for i in 0 ..< n.len:
-        if n[i].kind == nkStmtListExpr:
+      for i in 0..<n.len:
+        case n[i].kind
+        of nkExprColonExpr:
+          if n[i][1].kind == nkStmtListExpr:
+            let (st, ex) = exprToStmtList(n[i][1])
+            result.add(st)
+            n[i][1] = ex
+        of nkStmtListExpr:
           let (st, ex) = exprToStmtList(n[i])
           result.add(st)
           n[i] = ex
+        else: discard
       result.add(n)
 
   of nkIfStmt, nkIfExpr:
     var ns = false
-    for i in 0 ..< n.len:
+    for i in 0..<n.len:
       n[i] = ctx.lowerStmtListExprs(n[i], ns)
 
     if ns:
       needsSplit = true
-      var tmp: PSym
-      var s: PNode
+      var tmp: PSym = nil
       let isExpr = not isEmptyType(n.typ)
       if isExpr:
-        tmp = ctx.newTempVar(n.typ)
         result = newNodeI(nkStmtListExpr, n.info)
         result.typ = n.typ
+        tmp = ctx.newTempVar(n.typ, result)
       else:
         result = newNodeI(nkStmtList, n.info)
 
@@ -532,11 +601,15 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
         else:
           internalError(ctx.g.config, "lowerStmtListExpr(nkIf): " & $branch.kind)
 
-      if isExpr: result.add(ctx.newEnvVarAccess(tmp))
+      if isExpr:
+        if ctx.nimOptItersEnabled:
+          result.add(ctx.newTempVarAccess(tmp))
+        else:
+          result.add(ctx.newEnvVarAccess(tmp))
 
-  of nkTryStmt:
+  of nkTryStmt, nkHiddenTryStmt:
     var ns = false
-    for i in 0 ..< n.len:
+    for i in 0..<n.len:
       n[i] = ctx.lowerStmtListExprs(n[i], ns)
 
     if ns:
@@ -546,10 +619,10 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
       if isExpr:
         result = newNodeI(nkStmtListExpr, n.info)
         result.typ = n.typ
-        let tmp = ctx.newTempVar(n.typ)
+        let tmp = ctx.newTempVar(n.typ, result)
 
         n[0] = ctx.convertExprBodyToAsgn(n[0], tmp)
-        for i in 1 ..< n.len:
+        for i in 1..<n.len:
           let branch = n[i]
           case branch.kind
           of nkExceptBranch:
@@ -562,11 +635,14 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
           else:
             internalError(ctx.g.config, "lowerStmtListExpr(nkTryStmt): " & $branch.kind)
         result.add(n)
-        result.add(ctx.newEnvVarAccess(tmp))
+        if ctx.nimOptItersEnabled:
+          result.add(ctx.newTempVarAccess(tmp))
+        else:
+          result.add(ctx.newEnvVarAccess(tmp))
 
   of nkCaseStmt:
     var ns = false
-    for i in 0 ..< n.len:
+    for i in 0..<n.len:
       n[i] = ctx.lowerStmtListExprs(n[i], ns)
 
     if ns:
@@ -575,30 +651,39 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
       let isExpr = not isEmptyType(n.typ)
 
       if isExpr:
-        let tmp = ctx.newTempVar(n.typ)
         result = newNodeI(nkStmtListExpr, n.info)
         result.typ = n.typ
+        let tmp = ctx.newTempVar(n.typ, result)
 
         if n[0].kind == nkStmtListExpr:
           let (st, ex) = exprToStmtList(n[0])
           result.add(st)
           n[0] = ex
 
-        for i in 1 ..< n.len:
+        for i in 1..<n.len:
           let branch = n[i]
           case branch.kind
           of nkOfBranch:
-            branch[1] = ctx.convertExprBodyToAsgn(branch[1], tmp)
+            branch[^1] = ctx.convertExprBodyToAsgn(branch[^1], tmp)
           of nkElse:
             branch[0] = ctx.convertExprBodyToAsgn(branch[0], tmp)
           else:
             internalError(ctx.g.config, "lowerStmtListExpr(nkCaseStmt): " & $branch.kind)
         result.add(n)
-        result.add(ctx.newEnvVarAccess(tmp))
+        if ctx.nimOptItersEnabled:
+          result.add(ctx.newTempVarAccess(tmp))
+        else:
+          result.add(ctx.newEnvVarAccess(tmp))
+      elif n[0].kind == nkStmtListExpr:
+        result = newNodeI(nkStmtList, n.info)
+        let (st, ex) = exprToStmtList(n[0])
+        result.add(st)
+        n[0] = ex
+        result.add(n)
 
-  of nkCallKinds:
+  of nkCallKinds, nkChckRange, nkChckRangeF, nkChckRange64:
     var ns = false
-    for i in 0 ..< n.len:
+    for i in 0..<n.len:
       n[i] = ctx.lowerStmtListExprs(n[i], ns)
 
     if ns:
@@ -618,10 +703,14 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
           result.add(st)
           cond = ex
 
-        let tmp = ctx.newTempVar(cond.typ)
-        result.add(ctx.newEnvVarAsgn(tmp, cond))
+        let tmp = ctx.newTempVar(cond.typ, result, cond)
+        # result.add(ctx.newTempVarAsgn(tmp, cond))
 
-        var check = ctx.newEnvVarAccess(tmp)
+        var check: PNode
+        if ctx.nimOptItersEnabled:
+          check = ctx.newTempVarAccess(tmp)
+        else:
+          check = ctx.newEnvVarAccess(tmp)
         if n[0].sym.magic == mOr:
           check = ctx.g.newNotCall(check)
 
@@ -631,23 +720,32 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
           let (st, ex) = exprToStmtList(cond)
           ifBody.add(st)
           cond = ex
-        ifBody.add(ctx.newEnvVarAsgn(tmp, cond))
+        if ctx.nimOptItersEnabled:
+          ifBody.add(ctx.newTempVarAsgn(tmp, cond))
+        else:
+          ifBody.add(ctx.newEnvVarAsgn(tmp, cond))
 
         let ifBranch = newTree(nkElifBranch, check, ifBody)
         let ifNode = newTree(nkIfStmt, ifBranch)
         result.add(ifNode)
-        result.add(ctx.newEnvVarAccess(tmp))
+        if ctx.nimOptItersEnabled:
+          result.add(ctx.newTempVarAccess(tmp))
+        else:
+          result.add(ctx.newEnvVarAccess(tmp))
       else:
-        for i in 0 ..< n.len:
+        for i in 0..<n.len:
           if n[i].kind == nkStmtListExpr:
             let (st, ex) = exprToStmtList(n[i])
             result.add(st)
             n[i] = ex
 
           if n[i].kind in nkCallKinds: # XXX: This should better be some sort of side effect tracking
-            let tmp = ctx.newTempVar(n[i].typ)
-            result.add(ctx.newEnvVarAsgn(tmp, n[i]))
-            n[i] = ctx.newEnvVarAccess(tmp)
+            let tmp = ctx.newTempVar(n[i].typ, result, n[i])
+            # result.add(ctx.newTempVarAsgn(tmp, n[i]))
+            if ctx.nimOptItersEnabled:
+              n[i] = ctx.newTempVarAccess(tmp)
+            else:
+              n[i] = ctx.newEnvVarAccess(tmp)
 
         result.add(n)
 
@@ -663,11 +761,17 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
         let (st, ex) = exprToStmtList(c[^1])
         result.add(st)
         c[^1] = ex
+      for i in 0 .. c.len - 3:
+        if c[i].kind == nkSym:
+          let s = c[i].sym
+          if sfForceLift in s.flags:
+            ctx.captureVar(s)
+
       result.add(varSect)
 
   of nkDiscardStmt, nkReturnStmt, nkRaiseStmt:
     var ns = false
-    for i in 0 ..< n.len:
+    for i in 0..<n.len:
       n[i] = ctx.lowerStmtListExprs(n[i], ns)
 
     if ns:
@@ -678,23 +782,24 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
       n[0] = ex
       result.add(n)
 
-  of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv:
+  of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv, nkObjDownConv,
+      nkDerefExpr, nkHiddenDeref:
     var ns = false
-    for i in 0 ..< n.len:
+    for i in ord(n.kind == nkCast)..<n.len:
       n[i] = ctx.lowerStmtListExprs(n[i], ns)
 
     if ns:
       needsSplit = true
       result = newNodeI(nkStmtListExpr, n.info)
       result.typ = n.typ
-      let (st, ex) = exprToStmtList(n[1])
+      let (st, ex) = exprToStmtList(n[^1])
       result.add(st)
-      n[1] = ex
+      n[^1] = ex
       result.add(n)
 
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     var ns = false
-    for i in 0 ..< n.len:
+    for i in 0..<n.len:
       n[i] = ctx.lowerStmtListExprs(n[i], ns)
 
     if ns:
@@ -712,9 +817,26 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
 
       result.add(n)
 
-  of nkWhileStmt:
-    var ns = false
+  of nkBracketExpr:
+    var lhsNeedsSplit = false
+    var rhsNeedsSplit = false
+    n[0] = ctx.lowerStmtListExprs(n[0], lhsNeedsSplit)
+    n[1] = ctx.lowerStmtListExprs(n[1], rhsNeedsSplit)
+    if lhsNeedsSplit or rhsNeedsSplit:
+      needsSplit = true
+      result = newNodeI(nkStmtListExpr, n.info)
+      if lhsNeedsSplit:
+        let (st, ex) = exprToStmtList(n[0])
+        result.add(st)
+        n[0] = ex
 
+      if rhsNeedsSplit:
+        let (st, ex) = exprToStmtList(n[1])
+        result.add(st)
+        n[1] = ex
+      result.add(n)
+
+  of nkWhileStmt:
     var condNeedsSplit = false
     n[0] = ctx.lowerStmtListExprs(n[0], condNeedsSplit)
     var bodyNeedsSplit = false
@@ -730,10 +852,10 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
         let check = newTree(nkIfStmt, branch)
         let newBody = newTree(nkStmtList, st, check, n[1])
 
-        n[0] = newSymNode(ctx.g.getSysSym(n[0].info, "true"))
+        n[0] = ctx.g.boolLit(n[0].info, true)
         n[1] = newBody
 
-  of nkDotExpr:
+  of nkDotExpr, nkCheckedFieldExpr:
     var ns = false
     n[0] = ctx.lowerStmtListExprs(n[0], ns)
     if ns:
@@ -753,21 +875,24 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
       result = newNodeI(nkStmtListExpr, n.info)
       result.typ = n.typ
       let (st, ex) = exprToStmtList(n[1])
-      n.kind = nkBlockStmt
+      n.transitionSonsKind(nkBlockStmt)
       n.typ = nil
       n[1] = st
       result.add(n)
       result.add(ex)
 
   else:
-    for i in 0 ..< n.len:
+    for i in 0..<n.len:
       n[i] = ctx.lowerStmtListExprs(n[i], needsSplit)
 
 proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
   # Generate the following code:
   #   if :unrollFinally:
   #       if :curExc.isNil:
-  #         return :tmpResult
+  #         if nearestFinally == 0:
+  #           return :tmpResult
+  #         else:
+  #           :state = nearestFinally # bubble up
   #       else:
   #         raise
   let curExc = ctx.newCurExcAccess()
@@ -776,17 +901,27 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
   let cmp = newTree(nkCall, newSymNode(ctx.g.getSysMagic(info, "==", mEqRef), info), curExc, nilnode)
   cmp.typ = ctx.g.getSysType(info, tyBool)
 
-  let asgn = newTree(nkFastAsgn,
-    newSymNode(getClosureIterResult(ctx.g, ctx.fn), info),
-    ctx.newTmpResultAccess())
+  let retStmt =
+    if ctx.nearestFinally == 0:
+      # last finally, we can return
+      let retValue = if ctx.fn.typ.returnType.isNil:
+                   ctx.g.emptyNode
+                 else:
+                   newTree(nkFastAsgn,
+                           newSymNode(getClosureIterResult(ctx.g, ctx.fn, ctx.idgen), info),
+                           ctx.newTmpResultAccess())
+      newTree(nkReturnStmt, retValue)
+    else:
+      # bubble up to next finally
+      newTree(nkGotoState, ctx.g.newIntLit(info, ctx.nearestFinally))
 
-  let retStmt = newTree(nkReturnStmt, asgn)
   let branch = newTree(nkElifBranch, cmp, retStmt)
 
-  # The C++ backend requires `getCurrentException` here.
-  let raiseStmt = newTree(nkRaiseStmt, ctx.g.callCodegenProc("getCurrentException", ctx.g.emptyNode))
+  let nullifyExc = newTree(nkCall, newSymNode(ctx.g.getCompilerProc("closureIterSetupExc")), nilnode)
+  nullifyExc.info = info
+  let raiseStmt = newTree(nkRaiseStmt, curExc)
   raiseStmt.info = info
-  let elseBranch = newTree(nkElse, raiseStmt)
+  let elseBranch = newTree(nkElse, newTree(nkStmtList, nullifyExc, raiseStmt))
 
   let ifBody = newTree(nkIfStmt, branch, elseBranch)
   let elifBranch = newTree(nkElifBranch, ctx.newUnrollFinallyAccess(info), ifBody)
@@ -799,20 +934,23 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode =
   case n.kind
   of nkReturnStmt:
     # We're somewhere in try, transform to finally unrolling
-    assert(ctx.nearestFinally != 0)
+    if ctx.nearestFinally == 0:
+      # return is within the finally
+      return
 
     result = newNodeI(nkStmtList, n.info)
 
     block: # :unrollFinally = true
       let asgn = newNodeI(nkAsgn, n.info)
       asgn.add(ctx.newUnrollFinallyAccess(n.info))
-      asgn.add(newIntTypeNode(nkIntLit, 1, ctx.g.getSysType(n.info, tyBool)))
+      asgn.add(newIntTypeNode(1, ctx.g.getSysType(n.info, tyBool)))
       result.add(asgn)
 
     if n[0].kind != nkEmpty:
       let asgnTmpResult = newNodeI(nkAsgn, n.info)
       asgnTmpResult.add(ctx.newTmpResultAccess())
-      asgnTmpResult.add(n[0])
+      let x = if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}: n[0][1] else: n[0]
+      asgnTmpResult.add(x)
       result.add(asgnTmpResult)
 
     result.add(ctx.newNullifyCurExc(n.info))
@@ -822,175 +960,174 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode =
 
   of nkSkip:
     discard
+  of nkTryStmt:
+    if n.hasYields:
+      # the inner try will handle these transformations
+      discard
+    else:
+      for i in 0..<n.len:
+        n[i] = ctx.transformReturnsInTry(n[i])
   else:
-    for i in 0 ..< n.len:
+    for i in 0..<n.len:
       n[i] = ctx.transformReturnsInTry(n[i])
 
 proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode =
   result = n
-  case n.kind:
-    of nkSkip:
-      discard
+  case n.kind
+  of nkSkip: discard
 
-    of nkStmtList, nkStmtListExpr:
-      assert(isEmptyType(n.typ), "nkStmtListExpr not lowered")
+  of nkStmtList, nkStmtListExpr:
+    result = addGotoOut(result, gotoOut)
+    for i in 0..<n.len:
+      if n[i].hasYields:
+        # Create a new split
+        let go = newNodeI(nkGotoState, n[i].info)
+        n[i] = ctx.transformClosureIteratorBody(n[i], go)
+
+        let s = newNodeI(nkStmtList, n[i + 1].info)
+        for j in i + 1..<n.len:
+          s.add(n[j])
+
+        n.sons.setLen(i + 1)
+        discard ctx.newState(s, go)
+        if ctx.transformClosureIteratorBody(s, gotoOut) != s:
+          internalError(ctx.g.config, "transformClosureIteratorBody != s")
+        break
 
-      result = addGotoOut(result, gotoOut)
-      for i in 0 ..< n.len:
-        if n[i].hasYieldsInExpressions:
-          # Lower nkStmtListExpr nodes inside `n[i]` first
-          var ns = false
-          n[i] = ctx.lowerStmtListExprs(n[i], ns)
+  of nkYieldStmt:
+    result = newNodeI(nkStmtList, n.info)
+    result.add(n)
+    result.add(gotoOut)
 
-        if n[i].hasYields:
-          # Create a new split
-          let go = newNodeI(nkGotoState, n[i].info)
-          n[i] = ctx.transformClosureIteratorBody(n[i], go)
+  of nkElse, nkElseExpr:
+    result[0] = addGotoOut(result[0], gotoOut)
+    result[0] = ctx.transformClosureIteratorBody(result[0], gotoOut)
 
-          let s = newNodeI(nkStmtList, n[i + 1].info)
-          for j in i + 1 ..< n.len:
-            s.add(n[j])
+  of nkElifBranch, nkElifExpr, nkOfBranch:
+    result[^1] = addGotoOut(result[^1], gotoOut)
+    result[^1] = ctx.transformClosureIteratorBody(result[^1], gotoOut)
 
-          n.sons.setLen(i + 1)
-          discard ctx.newState(s, go)
-          if ctx.transformClosureIteratorBody(s, gotoOut) != s:
-            internalError(ctx.g.config, "transformClosureIteratorBody != s")
-          break
+  of nkIfStmt, nkCaseStmt:
+    for i in 0..<n.len:
+      n[i] = ctx.transformClosureIteratorBody(n[i], gotoOut)
+    if n[^1].kind != nkElse:
+      # We don't have an else branch, but every possible branch has to end with
+      # gotoOut, so add else here.
+      let elseBranch = newTree(nkElse, gotoOut)
+      n.add(elseBranch)
 
-    of nkYieldStmt:
-      result = newNodeI(nkStmtList, n.info)
-      result.add(n)
-      result.add(gotoOut)
-
-    of nkElse, nkElseExpr:
-      result[0] = addGotoOut(result[0], gotoOut)
-      result[0] = ctx.transformClosureIteratorBody(result[0], gotoOut)
-
-    of nkElifBranch, nkElifExpr, nkOfBranch:
-      result[^1] = addGotoOut(result[^1], gotoOut)
-      result[^1] = ctx.transformClosureIteratorBody(result[^1], gotoOut)
-
-    of nkIfStmt, nkCaseStmt:
-      for i in 0 ..< n.len:
-        n[i] = ctx.transformClosureIteratorBody(n[i], gotoOut)
-      if n[^1].kind != nkElse:
-        # We don't have an else branch, but every possible branch has to end with
-        # gotoOut, so add else here.
-        let elseBranch = newTree(nkElse, gotoOut)
-        n.add(elseBranch)
-
-    of nkWhileStmt:
-      # while e:
-      #   s
-      # ->
-      # BEGIN_STATE:
-      #   if e:
-      #     s
-      #     goto BEGIN_STATE
-      #   else:
-      #     goto OUT
-
-      result = newNodeI(nkGotoState, n.info)
-
-      let s = newNodeI(nkStmtList, n.info)
-      discard ctx.newState(s, result)
-      let ifNode = newNodeI(nkIfStmt, n.info)
-      let elifBranch = newNodeI(nkElifBranch, n.info)
-      elifBranch.add(n[0])
-
-      var body = addGotoOut(n[1], result)
-
-      body = ctx.transformBreaksAndContinuesInWhile(body, result, gotoOut)
-      body = ctx.transformClosureIteratorBody(body, result)
-
-      elifBranch.add(body)
-      ifNode.add(elifBranch)
+  of nkWhileStmt:
+    # while e:
+    #   s
+    # ->
+    # BEGIN_STATE:
+    #   if e:
+    #     s
+    #     goto BEGIN_STATE
+    #   else:
+    #     goto OUT
 
-      let elseBranch = newTree(nkElse, gotoOut)
-      ifNode.add(elseBranch)
-      s.add(ifNode)
-
-    of nkBlockStmt:
-      result[1] = addGotoOut(result[1], gotoOut)
-      result[1] = ctx.transformBreaksInBlock(result[1], result[0], gotoOut)
-      result[1] = ctx.transformClosureIteratorBody(result[1], gotoOut)
-
-    of nkTryStmt:
-      # See explanation above about how this works
-      ctx.hasExceptions = true
-
-      result = newNodeI(nkGotoState, n.info)
-      var tryBody = toStmtList(n[0])
-      var exceptBody = ctx.collectExceptState(n)
-      var finallyBody = newTree(nkStmtList, getFinallyNode(ctx, n))
-      finallyBody = ctx.transformReturnsInTry(finallyBody)
-      finallyBody.add(ctx.newEndFinallyNode(finallyBody.info))
-
-      # The following index calculation is based on the knowledge how state
-      # indexes are assigned
-      let tryIdx = ctx.states.len
-      var exceptIdx, finallyIdx: int
-      if exceptBody.kind != nkEmpty:
-        exceptIdx = -(tryIdx + 1)
-        finallyIdx = tryIdx + 2
-      else:
-        exceptIdx = tryIdx + 1
-        finallyIdx = tryIdx + 1
+    result = newNodeI(nkGotoState, n.info)
 
-      let outToFinally = newNodeI(nkGotoState, finallyBody.info)
+    let s = newNodeI(nkStmtList, n.info)
+    discard ctx.newState(s, result)
+    let ifNode = newNodeI(nkIfStmt, n.info)
+    let elifBranch = newNodeI(nkElifBranch, n.info)
+    elifBranch.add(n[0])
 
-      block: # Create initial states.
-        let oldExcHandlingState = ctx.curExcHandlingState
-        ctx.curExcHandlingState = exceptIdx
-        let realTryIdx = ctx.newState(tryBody, result)
-        assert(realTryIdx == tryIdx)
+    var body = addGotoOut(n[1], result)
 
-        if exceptBody.kind != nkEmpty:
-          ctx.curExcHandlingState = finallyIdx
-          let realExceptIdx = ctx.newState(exceptBody, nil)
-          assert(realExceptIdx == -exceptIdx)
+    body = ctx.transformBreaksAndContinuesInWhile(body, result, gotoOut)
+    body = ctx.transformClosureIteratorBody(body, result)
 
-        ctx.curExcHandlingState = oldExcHandlingState
-        let realFinallyIdx = ctx.newState(finallyBody, outToFinally)
-        assert(realFinallyIdx == finallyIdx)
+    elifBranch.add(body)
+    ifNode.add(elifBranch)
 
-      block: # Subdivide the states
-        let oldNearestFinally = ctx.nearestFinally
-        ctx.nearestFinally = finallyIdx
+    let elseBranch = newTree(nkElse, gotoOut)
+    ifNode.add(elseBranch)
+    s.add(ifNode)
 
-        let oldExcHandlingState = ctx.curExcHandlingState
+  of nkBlockStmt:
+    result[1] = addGotoOut(result[1], gotoOut)
+    result[1] = ctx.transformBreaksInBlock(result[1], result[0], gotoOut)
+    result[1] = ctx.transformClosureIteratorBody(result[1], gotoOut)
+
+  of nkTryStmt, nkHiddenTryStmt:
+    # See explanation above about how this works
+    ctx.hasExceptions = true
+
+    result = newNodeI(nkGotoState, n.info)
+    var tryBody = toStmtList(n[0])
+    var exceptBody = ctx.collectExceptState(n)
+    var finallyBody = newTree(nkStmtList, getFinallyNode(ctx, n))
+    finallyBody = ctx.transformReturnsInTry(finallyBody)
+    finallyBody.add(ctx.newEndFinallyNode(finallyBody.info))
+
+    # The following index calculation is based on the knowledge how state
+    # indexes are assigned
+    let tryIdx = ctx.states.len
+    var exceptIdx, finallyIdx: int
+    if exceptBody.kind != nkEmpty:
+      exceptIdx = -(tryIdx + 1)
+      finallyIdx = tryIdx + 2
+    else:
+      exceptIdx = tryIdx + 1
+      finallyIdx = tryIdx + 1
 
-        ctx.curExcHandlingState = exceptIdx
+    let outToFinally = newNodeI(nkGotoState, finallyBody.info)
 
-        if ctx.transformReturnsInTry(tryBody) != tryBody:
-          internalError(ctx.g.config, "transformReturnsInTry != tryBody")
-        if ctx.transformClosureIteratorBody(tryBody, outToFinally) != tryBody:
-          internalError(ctx.g.config, "transformClosureIteratorBody != tryBody")
+    block: # Create initial states.
+      let oldExcHandlingState = ctx.curExcHandlingState
+      ctx.curExcHandlingState = exceptIdx
+      let realTryIdx = ctx.newState(tryBody, result)
+      assert(realTryIdx == tryIdx)
 
+      if exceptBody.kind != nkEmpty:
         ctx.curExcHandlingState = finallyIdx
-        ctx.addElseToExcept(exceptBody)
-        if ctx.transformReturnsInTry(exceptBody) != exceptBody:
-          internalError(ctx.g.config, "transformReturnsInTry != exceptBody")
-        if ctx.transformClosureIteratorBody(exceptBody, outToFinally) != exceptBody:
-          internalError(ctx.g.config, "transformClosureIteratorBody != exceptBody")
+        let realExceptIdx = ctx.newState(exceptBody, nil)
+        assert(realExceptIdx == -exceptIdx)
 
-        ctx.curExcHandlingState = oldExcHandlingState
-        ctx.nearestFinally = oldNearestFinally
-        if ctx.transformClosureIteratorBody(finallyBody, gotoOut) != finallyBody:
-          internalError(ctx.g.config, "transformClosureIteratorBody != finallyBody")
+      ctx.curExcHandlingState = oldExcHandlingState
+      let realFinallyIdx = ctx.newState(finallyBody, outToFinally)
+      assert(realFinallyIdx == finallyIdx)
 
-    of nkGotoState, nkForStmt:
-      internalError(ctx.g.config, "closure iter " & $n.kind)
+    block: # Subdivide the states
+      let oldNearestFinally = ctx.nearestFinally
+      ctx.nearestFinally = finallyIdx
 
-    else:
-      for i in 0 ..< n.len:
-        n[i] = ctx.transformClosureIteratorBody(n[i], gotoOut)
+      let oldExcHandlingState = ctx.curExcHandlingState
+
+      ctx.curExcHandlingState = exceptIdx
+
+      if ctx.transformReturnsInTry(tryBody) != tryBody:
+        internalError(ctx.g.config, "transformReturnsInTry != tryBody")
+      if ctx.transformClosureIteratorBody(tryBody, outToFinally) != tryBody:
+        internalError(ctx.g.config, "transformClosureIteratorBody != tryBody")
+
+      ctx.curExcHandlingState = finallyIdx
+      ctx.addElseToExcept(exceptBody)
+      if ctx.transformReturnsInTry(exceptBody) != exceptBody:
+        internalError(ctx.g.config, "transformReturnsInTry != exceptBody")
+      if ctx.transformClosureIteratorBody(exceptBody, outToFinally) != exceptBody:
+        internalError(ctx.g.config, "transformClosureIteratorBody != exceptBody")
+
+      ctx.curExcHandlingState = oldExcHandlingState
+      ctx.nearestFinally = oldNearestFinally
+      if ctx.transformClosureIteratorBody(finallyBody, gotoOut) != finallyBody:
+        internalError(ctx.g.config, "transformClosureIteratorBody != finallyBody")
+
+  of nkGotoState, nkForStmt:
+    internalError(ctx.g.config, "closure iter " & $n.kind)
+
+  else:
+    for i in 0..<n.len:
+      n[i] = ctx.transformClosureIteratorBody(n[i], gotoOut)
 
 proc stateFromGotoState(n: PNode): int =
   assert(n.kind == nkGotoState)
   result = n[0].intVal.int
 
-proc tranformStateAssignments(ctx: var Ctx, n: PNode): PNode =
+proc transformStateAssignments(ctx: var Ctx, n: PNode): PNode =
   # This transforms 3 patterns:
   ########################## 1
   # yield e
@@ -1020,19 +1157,19 @@ proc tranformStateAssignments(ctx: var Ctx, n: PNode): PNode =
       result.add(ctx.newStateAssgn(stateFromGotoState(n[1])))
 
       var retStmt = newNodeI(nkReturnStmt, n.info)
-      if n[0].sons[0].kind != nkEmpty:
-        var a = newNodeI(nkAsgn, n[0].sons[0].info)
-        var retVal = n[0].sons[0] #liftCapturedVars(n.sons[0], owner, d, c)
-        addSon(a, newSymNode(getClosureIterResult(ctx.g, ctx.fn)))
-        addSon(a, retVal)
+      if n[0][0].kind != nkEmpty:
+        var a = newNodeI(nkAsgn, n[0][0].info)
+        var retVal = n[0][0] #liftCapturedVars(n[0], owner, d, c)
+        a.add newSymNode(getClosureIterResult(ctx.g, ctx.fn, ctx.idgen))
+        a.add retVal
         retStmt.add(a)
       else:
         retStmt.add(ctx.g.emptyNode)
 
       result.add(retStmt)
     else:
-      for i in 0 ..< n.len:
-        n[i] = ctx.tranformStateAssignments(n[i])
+      for i in 0..<n.len:
+        n[i] = ctx.transformStateAssignments(n[i])
 
   of nkSkip:
     discard
@@ -1051,8 +1188,8 @@ proc tranformStateAssignments(ctx: var Ctx, n: PNode): PNode =
     result.add(breakState)
 
   else:
-    for i in 0 ..< n.len:
-      n[i] = ctx.tranformStateAssignments(n[i])
+    for i in 0..<n.len:
+      n[i] = ctx.transformStateAssignments(n[i])
 
 proc skipStmtList(ctx: Ctx; n: PNode): PNode =
   result = n
@@ -1069,10 +1206,10 @@ proc skipEmptyStates(ctx: Ctx, stateIdx: int): int =
     let label = stateIdx
     if label == ctx.exitStateIdx: break
     var newLabel = label
-    if label == -1:
+    if label == emptyStateLabel:
       newLabel = ctx.exitStateIdx
     else:
-      let fs = skipStmtList(ctx, ctx.states[label][1])
+      let fs = skipStmtList(ctx, ctx.states[label].body)
       if fs.kind == nkGotoState:
         newLabel = fs[0].intVal.int
     if label == newLabel: break
@@ -1081,9 +1218,9 @@ proc skipEmptyStates(ctx: Ctx, stateIdx: int): int =
     if maxJumps == 0:
       assert(false, "Internal error")
 
-  result = ctx.states[stateIdx][0].intVal.int
+  result = ctx.states[stateIdx].label
 
-proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode =
+proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode=
   result = n
   case n.kind
   of nkSkip:
@@ -1092,14 +1229,14 @@ proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode =
     result = copyTree(n)
     result[0].intVal = ctx.skipEmptyStates(result[0].intVal.int)
   else:
-    for i in 0 ..< n.len:
+    for i in 0..<n.len:
       n[i] = ctx.skipThroughEmptyStates(n[i])
 
-proc newArrayType(g: ModuleGraph; n: int, t: PType, owner: PSym): PType =
-  result = newType(tyArray, owner)
+proc newArrayType(g: ModuleGraph; n: int, t: PType; idgen: IdGenerator; owner: PSym): PType =
+  result = newType(tyArray, idgen, owner)
 
-  let rng = newType(tyRange, owner)
-  rng.n = newTree(nkRange, g.newIntLit(owner.info, 0), g.newIntLit(owner.info, n))
+  let rng = newType(tyRange, idgen, owner)
+  rng.n = newTree(nkRange, g.newIntLit(owner.info, 0), g.newIntLit(owner.info, n - 1))
   rng.rawAddSon(t)
 
   result.rawAddSon(rng)
@@ -1107,7 +1244,7 @@ proc newArrayType(g: ModuleGraph; n: int, t: PType, owner: PSym): PType =
 
 proc createExceptionTable(ctx: var Ctx): PNode {.inline.} =
   result = newNodeI(nkBracket, ctx.fn.info)
-  result.typ = ctx.g.newArrayType(ctx.exceptionTable.len, ctx.g.getSysType(ctx.fn.info, tyInt16), ctx.fn)
+  result.typ = ctx.g.newArrayType(ctx.exceptionTable.len, ctx.g.getSysType(ctx.fn.info, tyInt16), ctx.idgen, ctx.fn)
 
   for i in ctx.exceptionTable:
     let elem = newIntNode(nkIntLit, i)
@@ -1130,7 +1267,6 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
 
   # :state = exceptionTable[:state]
   block:
-
     # exceptionTable[:state]
     let getNextState = newTree(nkBracketExpr,
       ctx.createExceptionTable(),
@@ -1145,7 +1281,7 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
     let cond = newTree(nkCall,
       ctx.g.getSysMagic(info, "==", mEqI).newSymNode(),
       ctx.newStateAccess(),
-      newIntTypeNode(nkIntLit, 0, intTyp))
+      newIntTypeNode(0, intTyp))
     cond.typ = boolTyp
 
     let raiseStmt = newTree(nkRaiseStmt, ctx.g.emptyNode)
@@ -1157,7 +1293,7 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
   block:
     let cond = newTree(nkCall,
       ctx.g.getSysMagic(info, "<", mLtI).newSymNode,
-      newIntTypeNode(nkIntLit, 0, intTyp),
+      newIntTypeNode(0, intTyp),
       ctx.newStateAccess())
     cond.typ = boolTyp
 
@@ -1169,7 +1305,7 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
     let cond = newTree(nkCall,
       ctx.g.getSysMagic(info, "<", mLtI).newSymNode,
       ctx.newStateAccess(),
-      newIntTypeNode(nkIntLit, 0, intTyp))
+      newIntTypeNode(0, intTyp))
     cond.typ = boolTyp
 
     let negateState = newTree(nkCall,
@@ -1185,7 +1321,7 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
   block:
     result.add(newTree(nkAsgn,
       ctx.newCurExcAccess(),
-      ctx.g.callCodegenProc("getCurrentException", ctx.g.emptyNode)))
+      ctx.g.callCodegenProc("getCurrentException")))
 
 proc wrapIntoTryExcept(ctx: var Ctx, n: PNode): PNode {.inline.} =
   let setupExc = newTree(nkCall,
@@ -1200,28 +1336,25 @@ proc wrapIntoTryExcept(ctx: var Ctx, n: PNode): PNode {.inline.} =
 proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode =
   # while true:
   #   block :stateLoop:
-  #     gotoState :state
+  #     local vars decl (if needed)
   #     body # Might get wrapped in try-except
   let loopBody = newNodeI(nkStmtList, n.info)
-  result = newTree(nkWhileStmt, newSymNode(ctx.g.getSysSym(n.info, "true")), loopBody)
+  result = newTree(nkWhileStmt, ctx.g.boolLit(n.info, true), loopBody)
   result.info = n.info
 
+  let localVars = newNodeI(nkStmtList, n.info)
   if not ctx.stateVarSym.isNil:
     let varSect = newNodeI(nkVarSection, n.info)
     addVar(varSect, newSymNode(ctx.stateVarSym))
-    loopBody.add(varSect)
+    localVars.add(varSect)
 
     if not ctx.tempVars.isNil:
-      loopBody.add(ctx.tempVars)
+      localVars.add(ctx.tempVars)
 
   let blockStmt = newNodeI(nkBlockStmt, n.info)
   blockStmt.add(newSymNode(ctx.stateLoopLabel))
 
-  let gs = newNodeI(nkGotoState, n.info)
-  gs.add(ctx.newStateAccess())
-  gs.add(ctx.g.newIntLit(n.info, ctx.states.len - 1))
-
-  var blockBody = newTree(nkStmtList, gs, n)
+  var blockBody = newTree(nkStmtList, localVars, n)
   if ctx.hasExceptions:
     blockBody = ctx.wrapIntoTryExcept(blockBody)
 
@@ -1234,73 +1367,252 @@ proc deleteEmptyStates(ctx: var Ctx) =
 
   # Apply new state indexes and mark unused states with -1
   var iValid = 0
-  for i, s in ctx.states:
-    let body = skipStmtList(ctx, s[1])
+  for i, s in ctx.states.mpairs:
+    let body = skipStmtList(ctx, s.body)
     if body.kind == nkGotoState and i != ctx.states.len - 1 and i != 0:
       # This is an empty state. Mark with -1.
-      s[0].intVal = -1
+      s.label = emptyStateLabel
     else:
-      s[0].intVal = iValid
+      s.label = iValid
       inc iValid
 
   for i, s in ctx.states:
-    let body = skipStmtList(ctx, s[1])
+    let body = skipStmtList(ctx, s.body)
     if body.kind != nkGotoState or i == 0:
-      discard ctx.skipThroughEmptyStates(s)
+      discard ctx.skipThroughEmptyStates(s.body)
       let excHandlState = ctx.exceptionTable[i]
       if excHandlState < 0:
         ctx.exceptionTable[i] = -ctx.skipEmptyStates(-excHandlState)
       elif excHandlState != 0:
         ctx.exceptionTable[i] = ctx.skipEmptyStates(excHandlState)
 
-  var i = 0
+  var i = 1 # ignore the entry and the exit
   while i < ctx.states.len - 1:
-    let fs = skipStmtList(ctx, ctx.states[i][1])
-    if fs.kind == nkGotoState and i != 0:
+    if ctx.states[i].label == emptyStateLabel:
       ctx.states.delete(i)
       ctx.exceptionTable.delete(i)
     else:
       inc i
 
-proc transformClosureIterator*(g: ModuleGraph; fn: PSym, n: PNode): PNode =
-  var ctx: Ctx
-  ctx.g = g
-  ctx.fn = fn
+type
+  PreprocessContext = object
+    finallys: seq[PNode]
+    config: ConfigRef
+    blocks: seq[(PNode, int)]
+    idgen: IdGenerator
+  FreshVarsContext = object
+    tab: Table[int, PSym]
+    config: ConfigRef
+    info: TLineInfo
+    idgen: IdGenerator
+
+proc freshVars(n: PNode; c: var FreshVarsContext): PNode =
+  case n.kind
+  of nkSym:
+    let x = c.tab.getOrDefault(n.sym.id)
+    if x == nil:
+      result = n
+    else:
+      result = newSymNode(x, n.info)
+  of nkSkip - {nkSym}:
+    result = n
+  of nkLetSection, nkVarSection:
+    result = copyNode(n)
+    for it in n:
+      if it.kind in {nkIdentDefs, nkVarTuple}:
+        let idefs = copyNode(it)
+        for v in 0..it.len-3:
+          if it[v].kind == nkSym:
+            let x = copySym(it[v].sym, c.idgen)
+            c.tab[it[v].sym.id] = x
+            idefs.add newSymNode(x)
+          else:
+            idefs.add it[v]
 
-  if getEnvParam(fn).isNil:
-    # Lambda lifting was not done yet. Use temporary :state sym, which
-    # be handled specially by lambda lifting. Local temp vars (if needed)
-    # should folllow the same logic.
-    ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), fn, fn.info)
-    ctx.stateVarSym.typ = g.createClosureIterStateType(fn)
+        for rest in it.len-2 ..< it.len: idefs.add it[rest]
+        result.add idefs
+      else:
+        result.add it
+  of nkRaiseStmt:
+    result = nil
+    localError(c.config, c.info, "unsupported control flow: 'finally: ... raise' duplicated because of 'break'")
+  else:
+    result = n
+    for i in 0..<n.safeLen:
+      result[i] = freshVars(n[i], c)
+
+proc preprocess(c: var PreprocessContext; n: PNode): PNode =
+  # in order to fix bug #15243 without risking regressions, we preprocess
+  # the AST so that 'break' statements inside a 'try finally' also have the
+  # finally section. We need to duplicate local variables here and also
+  # detect: 'finally: raises X' which is currently not supported. We produce
+  # an error for this case for now. All this will be done properly with Yuriy's
+  # patch.
 
-  ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), fn, fn.info)
-  let n = n.toStmtList
+  result = n
+  case n.kind
+  of nkTryStmt:
+    let f = n.lastSon
+    var didAddSomething = false
+    if f.kind == nkFinally:
+      c.finallys.add f.lastSon
+      didAddSomething = true
+
+    for i in 0 ..< n.len:
+      result[i] = preprocess(c, n[i])
+
+    if didAddSomething:
+      discard c.finallys.pop()
+
+  of nkWhileStmt, nkBlockStmt:
+    if not n.hasYields: return n
+    c.blocks.add((n, c.finallys.len))
+    for i in 0 ..< n.len:
+      result[i] = preprocess(c, n[i])
+    discard c.blocks.pop()
+
+  of nkBreakStmt:
+    if c.blocks.len == 0:
+      discard
+    else:
+      var fin = -1
+      if n[0].kind == nkEmpty:
+        fin = c.blocks[^1][1]
+      elif n[0].kind == nkSym:
+        for i in countdown(c.blocks.high, 0):
+          if c.blocks[i][0].kind == nkBlockStmt and c.blocks[i][0][0].kind == nkSym and
+              c.blocks[i][0][0].sym == n[0].sym:
+            fin = c.blocks[i][1]
+            break
+
+      if fin >= 0:
+        result = newNodeI(nkStmtList, n.info)
+        for i in countdown(c.finallys.high, fin):
+          var vars = FreshVarsContext(tab: initTable[int, PSym](), config: c.config, info: n.info, idgen: c.idgen)
+          result.add freshVars(copyTree(c.finallys[i]), vars)
+          c.idgen = vars.idgen
+        result.add n
+  of nkSkip: discard
+  else:
+    for i in 0 ..< n.len:
+      result[i] = preprocess(c, n[i])
+
+proc detectCapturedVars(c: var Ctx, n: PNode, stateIdx: int) =
+  case n.kind
+  of nkSym:
+    let s = n.sym
+    if s.kind in {skResult, skVar, skLet, skForVar, skTemp} and sfGlobal notin s.flags and s.owner == c.fn:
+      let vs = c.varStates.getOrDefault(s.itemId, localNotSeen)
+      if vs == localNotSeen: # First seing this variable
+        c.varStates[s.itemId] = stateIdx
+      elif vs == localRequiresLifting:
+        discard # Sym already marked
+      elif vs != stateIdx:
+        c.captureVar(s)
+  of nkReturnStmt:
+    if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}:
+      # we have a `result = result` expression produced by the closure
+      # transform, let's not touch the LHS in order to make the lifting pass
+      # correct when `result` is lifted
+      detectCapturedVars(c, n[0][1], stateIdx)
+    else:
+      detectCapturedVars(c, n[0], stateIdx)
+  else:
+    for i in 0 ..< n.safeLen:
+      detectCapturedVars(c, n[i], stateIdx)
+
+proc detectCapturedVars(c: var Ctx) =
+  for i, s in c.states:
+    detectCapturedVars(c, s.body, i)
+
+proc liftLocals(c: var Ctx, n: PNode): PNode =
+  result = n
+  case n.kind
+  of nkSym:
+    let s = n.sym
+    if c.varStates.getOrDefault(s.itemId) == localRequiresLifting:
+      # lift
+      let e = getEnvParam(c.fn)
+      let field = getFieldFromObj(e.typ.elementType, s)
+      assert(field != nil)
+      result = rawIndirectAccess(newSymNode(e), field, n.info)
+    # elif c.varStates.getOrDefault(s.itemId, localNotSeen) != localNotSeen:
+    #   echo "Not lifting ", s.name.s
+
+  of nkReturnStmt:
+    if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}:
+      # we have a `result = result` expression produced by the closure
+      # transform, let's not touch the LHS in order to make the lifting pass
+      # correct when `result` is lifted
+      n[0][1] = liftLocals(c, n[0][1])
+    else:
+      n[0] = liftLocals(c, n[0])
+  else:
+    for i in 0 ..< n.safeLen:
+      n[i] = liftLocals(c, n[i])
+
+proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n: PNode): PNode =
+  var ctx = Ctx(g: g, fn: fn, idgen: idgen,
+    # should be default when issues are fixed, see #24094:
+    nimOptItersEnabled: isDefined(g.config, "nimOptIters"))
+
+  if getEnvParam(fn).isNil:
+    if ctx.nimOptItersEnabled:
+      # The transformation should always happen after at least partial lambdalifting
+      # is performed, so that the closure iter environment is always created upfront.
+      doAssert(false, "Env param not created before iter transformation")
+    else:
+      # Lambda lifting was not done yet. Use temporary :state sym, which will
+      # be handled specially by lambda lifting. Local temp vars (if needed)
+      # should follow the same logic.
+      ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), idgen, fn, fn.info)
+      ctx.stateVarSym.typ = g.createClosureIterStateType(fn, idgen)
+
+  ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), idgen, fn, fn.info)
+  var pc = PreprocessContext(finallys: @[], config: g.config, idgen: idgen)
+  var n = preprocess(pc, n.toStmtList)
+  #echo "transformed into ", n
+  #var n = n.toStmtList
 
   discard ctx.newState(n, nil)
   let gotoOut = newTree(nkGotoState, g.newIntLit(n.info, -1))
 
+  var ns = false
+  n = ctx.lowerStmtListExprs(n, ns)
+
+  if n.hasYieldsInExpressions():
+    internalError(ctx.g.config, "yield in expr not lowered")
+
   # Splitting transformation
   discard ctx.transformClosureIteratorBody(n, gotoOut)
 
   # Optimize empty states away
   ctx.deleteEmptyStates()
 
-  # Make new body by concating the list of states
-  result = newNodeI(nkStmtList, n.info)
+  let caseDispatcher = newTreeI(nkCaseStmt, n.info,
+      ctx.newStateAccess())
+
+  if ctx.nimOptItersEnabled:
+    # Lamdalifting will not touch our locals, it is our responsibility to lift those that
+    # need it.
+    detectCapturedVars(ctx)
+
   for s in ctx.states:
-    assert(s.len == 2)
-    let body = s[1]
-    s.sons.del(1)
-    result.add(s)
-    result.add(body)
+    let body = ctx.transformStateAssignments(s.body)
+    caseDispatcher.add newTreeI(nkOfBranch, body.info, g.newIntLit(body.info, s.label), body)
+
+  caseDispatcher.add newTreeI(nkElse, n.info, newTreeI(nkReturnStmt, n.info, g.emptyNode))
+
+  result = wrapIntoStateLoop(ctx, caseDispatcher)
+  if ctx.nimOptItersEnabled:
+    result = liftLocals(ctx, result)
 
-  result = ctx.tranformStateAssignments(result)
-  result = ctx.wrapIntoStateLoop(result)
+  when false:
+    echo "TRANSFORM TO STATES: "
+    echo renderTree(result)
 
-  # echo "TRANSFORM TO STATES: "
-  # echo renderTree(result)
+    echo "exception table:"
+    for i, e in ctx.exceptionTable:
+      echo i, " -> ", e
 
-  # echo "exception table:"
-  # for i, e in ctx.exceptionTable:
-  #   echo i, " -> ", e
+    echo "ENV: ", renderTree(getEnvParam(fn).typ.elementType.n)
diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim
new file mode 100644
index 000000000..e51248639
--- /dev/null
+++ b/compiler/cmdlinehelper.nim
@@ -0,0 +1,85 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2018 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Helpers for binaries that use compiler passes, e.g.: nim, nimsuggest
+
+import
+  options, idents, nimconf, extccomp, commands, msgs,
+  lineinfos, modulegraphs, condsyms, pathutils
+
+import std/[os, parseopt]
+
+proc prependCurDir*(f: AbsoluteFile): AbsoluteFile =
+  when defined(unix):
+    if os.isAbsolute(f.string): result = f
+    else: result = AbsoluteFile("./" & f.string)
+  else:
+    result = f
+
+proc addCmdPrefix*(result: var string, kind: CmdLineKind) =
+  # consider moving this to std/parseopt
+  case kind
+  of cmdLongOption: result.add "--"
+  of cmdShortOption: result.add "-"
+  of cmdArgument, cmdEnd: discard
+
+type
+  NimProg* = ref object
+    suggestMode*: bool
+    supportsStdinFile*: bool
+    processCmdLine*: proc(pass: TCmdLinePass, cmd: string; config: ConfigRef)
+
+proc initDefinesProg*(self: NimProg, conf: ConfigRef, name: string) =
+  condsyms.initDefines(conf.symbols)
+  defineSymbol conf.symbols, name
+
+proc processCmdLineAndProjectPath*(self: NimProg, conf: ConfigRef) =
+  self.processCmdLine(passCmd1, "", conf)
+  if conf.projectIsCmd and conf.projectName in ["-", ""]:
+    handleCmdInput(conf)
+  elif self.supportsStdinFile and conf.projectName == "-":
+    handleStdinInput(conf)
+  elif conf.projectName != "":
+    setFromProjectName(conf, conf.projectName)
+  else:
+    conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile getCurrentDir())
+
+proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: ConfigRef;
+                                   graph: ModuleGraph): bool =
+  if self.suggestMode:
+    conf.setCmd cmdIdeTools
+  if conf.cmd == cmdNimscript:
+    incl(conf.globalOptions, optWasNimscript)
+  loadConfigs(DefaultConfig, cache, conf, graph.idgen) # load all config files
+  # restores `conf.notes` after loading config files
+  # because it has overwrites the notes when compiling the system module which
+  # is a foreign module compared to the project
+  if conf.cmd in cmdBackends:
+    conf.notes = conf.mainPackageNotes
+
+  if not self.suggestMode:
+    let scriptFile = conf.projectFull.changeFileExt("nims")
+    # 'nim foo.nims' means to just run the NimScript file and do nothing more:
+    if fileExists(scriptFile) and scriptFile == conf.projectFull:
+      if conf.cmd == cmdNone: conf.setCmd cmdNimscript
+      if conf.cmd == cmdNimscript: return false
+  # now process command line arguments again, because some options in the
+  # command line can overwrite the config file's settings
+  if conf.backend != backendJs: # bug #19059
+    extccomp.initVars(conf)
+  self.processCmdLine(passCmd2, "", conf)
+  if conf.cmd == cmdNone:
+    rawMessage(conf, errGenerated, "command missing")
+
+  graph.suggestMode = self.suggestMode
+  return true
+
+proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: ConfigRef; graph: ModuleGraph): bool =
+  ## Alias for loadConfigsAndProcessCmdLine, here for backwards compatibility
+  loadConfigsAndProcessCmdLine(self, cache, conf, graph)
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 330504a76..cbf915ca6 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -9,31 +9,36 @@
 
 # This module handles the parsing of command line arguments.
 
-
 # We do this here before the 'import' statement so 'defined' does not get
-# confused with 'TGCMode.gcGenerational' etc.
+# confused with 'TGCMode.gcMarkAndSweep' etc.
 template bootSwitch(name, expr, userString) =
   # Helper to build boot constants, for debugging you can 'echo' the else part.
   const name = if expr: " " & userString else: ""
 
 bootSwitch(usedRelease, defined(release), "-d:release")
-bootSwitch(usedGnuReadline, defined(useLinenoise), "-d:useLinenoise")
+bootSwitch(usedDanger, defined(danger), "-d:danger")
+# `useLinenoise` deprecated in favor of `nimUseLinenoise`, kept for backward compatibility
+bootSwitch(useLinenoise, defined(nimUseLinenoise) or defined(useLinenoise), "-d:nimUseLinenoise")
 bootSwitch(usedBoehm, defined(boehmgc), "--gc:boehm")
 bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
-bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational")
 bootSwitch(usedGoGC, defined(gogc), "--gc:go")
 bootSwitch(usedNoGC, defined(nogc), "--gc:none")
 
+import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs, enumutils]
 import
-  os, msgs, options, nversion, condsyms, strutils, extccomp, platform,
-  wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils, lineinfos
+  msgs, options, nversion, condsyms, extccomp, platform,
+  wordrecg, nimblecmd, lineinfos, pathutils
+
+import std/pathnorm
+
+from ast import setUseIc, eqTypeFlags, tfGcSafe, tfNoSideEffect
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 # but some have deps to imported modules. Yay.
 bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc")
-bootSwitch(usedNativeStacktrace,
-  defined(nativeStackTrace) and nativeStackTraceSupported,
-  "-d:nativeStackTrace")
-bootSwitch(usedFFI, hasFFI, "-d:useFFI")
+bootSwitch(usedFFI, hasFFI, "-d:nimHasLibFFI")
 
 type
   TCmdLinePass* = enum
@@ -46,15 +51,15 @@ const
       "Compiled at $4\n" &
       "Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n"
 
+proc genFeatureDesc[T: enum](t: typedesc[T]): string {.compileTime.} =
+  result = ""
+  for f in T:
+    if result.len > 0: result.add "|"
+    result.add $f
+
 const
-  Usage = slurp"../doc/basicopt.txt".replace("//", "")
-  FeatureDesc = block:
-    var x = ""
-    for f in low(Feature)..high(Feature):
-      if x.len > 0: x.add "|"
-      x.add $f
-    x
-  AdvancedUsage = slurp"../doc/advopt.txt".replace("//", "") % FeatureDesc
+  Usage = slurp"../doc/basicopt.txt".replace(" //", "   ")
+  AdvancedUsage = slurp"../doc/advopt.txt".replace(" //", "   ") % [genFeatureDesc(Feature), genFeatureDesc(LegacyFeature)]
 
 proc getCommandLineDesc(conf: ConfigRef): string =
   result = (HelpMessage % [VersionAsString, platform.OS[conf.target.hostOS].name,
@@ -91,29 +96,29 @@ proc writeVersionInfo(conf: ConfigRef; pass: TCmdLinePass) =
                                  CPU[conf.target.hostCPU].name, CompileDate]),
                {msgStdout})
 
-    const gitHash = gorge("git log -n 1 --format=%H").strip
+    const gitHash {.strdefine.} = gorge("git log -n 1 --format=%H").strip
+      # xxx move this logic to std/private/gitutils
     when gitHash.len == 40:
       msgWriteln(conf, "git hash: " & gitHash, {msgStdout})
 
-    msgWriteln(conf, "active boot switches:" & usedRelease &
-      usedTinyC & usedGnuReadline & usedNativeStacktrace &
-      usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedGoGC & usedNoGC,
+    msgWriteln(conf, "active boot switches:" & usedRelease & usedDanger &
+      usedTinyC & useLinenoise &
+      usedFFI & usedBoehm & usedMarkAndSweep & usedGoGC & usedNoGC,
                {msgStdout})
     msgQuit(0)
 
-proc writeCommandLineUsage*(conf: ConfigRef; helpWritten: var bool) =
-  if not helpWritten:
-    msgWriteln(conf, getCommandLineDesc(conf), {msgStdout})
-    helpWritten = true
+proc writeCommandLineUsage*(conf: ConfigRef) =
+  msgWriteln(conf, getCommandLineDesc(conf), {msgStdout})
 
 proc addPrefix(switch: string): string =
-  if len(switch) == 1: result = "-" & switch
+  if switch.len <= 1: result = "-" & switch
   else: result = "--" & switch
 
 const
   errInvalidCmdLineOption = "invalid command line option: '$1'"
   errOnOrOffExpectedButXFound = "'on' or 'off' expected, but '$1' found"
   errOnOffOrListExpectedButXFound = "'on', 'off' or 'list' expected, but '$1' found"
+  errOffHintsError = "'off', 'hint', 'error' or 'usages' expected, but '$1' found"
 
 proc invalidCmdLineOption(conf: ConfigRef; pass: TCmdLinePass, switch: string, info: TLineInfo) =
   if switch == " ": localError(conf, info, errInvalidCmdLineOption % "-")
@@ -123,38 +128,50 @@ proc splitSwitch(conf: ConfigRef; switch: string, cmd, arg: var string, pass: TC
                  info: TLineInfo) =
   cmd = ""
   var i = 0
-  if i < len(switch) and switch[i] == '-': inc(i)
-  if i < len(switch) and switch[i] == '-': inc(i)
-  while i < len(switch):
+  if i < switch.len and switch[i] == '-': inc(i)
+  if i < switch.len and switch[i] == '-': inc(i)
+  while i < switch.len:
     case switch[i]
-    of 'a'..'z', 'A'..'Z', '0'..'9', '_', '.': add(cmd, switch[i])
+    of 'a'..'z', 'A'..'Z', '0'..'9', '_', '.': cmd.add(switch[i])
     else: break
     inc(i)
-  if i >= len(switch): arg = ""
-  elif switch[i] in {':', '=', '['}: arg = substr(switch, i + 1)
+  if i >= switch.len: arg = ""
+  # cmd:arg => (cmd,arg)
+  elif switch[i] in {':', '='}: arg = substr(switch, i + 1)
+  # cmd[sub]:rest => (cmd,[sub]:rest)
+  elif switch[i] == '[': arg = substr(switch, i)
   else: invalidCmdLineOption(conf, pass, switch, info)
 
+template switchOn(arg: string): bool =
+  # xxx use `switchOn` wherever appropriate
+  case arg.normalize
+  of "", "on": true
+  of "off": false
+  else:
+    localError(conf, info, errOnOrOffExpectedButXFound % arg)
+    false
+
 proc processOnOffSwitch(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass,
                         info: TLineInfo) =
   case arg.normalize
-  of "on": conf.options = conf.options + op
-  of "off": conf.options = conf.options - op
+  of "", "on": conf.options.incl op
+  of "off": conf.options.excl op
   else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
 
 proc processOnOffSwitchOrList(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass,
                               info: TLineInfo): bool =
   result = false
   case arg.normalize
-  of "on": conf.options = conf.options + op
-  of "off": conf.options = conf.options - op
+  of "on": conf.options.incl op
+  of "off": conf.options.excl op
   of "list": result = true
   else: localError(conf, info, errOnOffOrListExpectedButXFound % arg)
 
 proc processOnOffSwitchG(conf: ConfigRef; op: TGlobalOptions, arg: string, pass: TCmdLinePass,
                          info: TLineInfo) =
   case arg.normalize
-  of "on": conf.globalOptions = conf.globalOptions + op
-  of "off": conf.globalOptions = conf.globalOptions - op
+  of "", "on": conf.globalOptions.incl op
+  of "off": conf.globalOptions.excl op
   else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
 
 proc expectArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
@@ -167,78 +184,135 @@ proc expectNoArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info:
 
 proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
                          info: TLineInfo; orig: string; conf: ConfigRef) =
-  var id = ""  # arg = "X]:on|off"
+  var id = ""  # arg = key or [key] or key:val or [key]:val;  with val=on|off
   var i = 0
-  var n = hintMin
-  while i < len(arg) and (arg[i] != ']'):
-    add(id, arg[i])
+  var notes: set[TMsgKind] = {}
+  var isBracket = false
+  if i < arg.len and arg[i] == '[':
+    isBracket = true
     inc(i)
-  if i < len(arg) and (arg[i] == ']'): inc(i)
-  else: invalidCmdLineOption(conf, pass, orig, info)
-  if i < len(arg) and (arg[i] in {':', '='}): inc(i)
+  while i < arg.len and (arg[i] notin {':', '=', ']'}):
+    id.add(arg[i])
+    inc(i)
+  if isBracket:
+    if i < arg.len and arg[i] == ']': inc(i)
+    else: invalidCmdLineOption(conf, pass, orig, info)
+
+  if i == arg.len: discard
+  elif i < arg.len and (arg[i] in {':', '='}): inc(i)
   else: invalidCmdLineOption(conf, pass, orig, info)
-  if state == wHint:
-    let x = findStr(lineinfos.HintsToStr, id)
-    if x >= 0: n = TNoteKind(x + ord(hintMin))
-    else: localError(conf, info, "unknown hint: " & id)
+
+  let isSomeHint = state in {wHint, wHintAsError}
+  template findNote(noteMin, noteMax, name) =
+    # unfortunately, hintUser and warningUser clash, otherwise implementation would simplify a bit
+    let x = findStr(noteMin, noteMax, id, errUnknown)
+    if x != errUnknown: notes = {TNoteKind(x)}
+    else:
+      if isSomeHint:
+        message(conf, info, hintUnknownHint, id)
+      else:
+        localError(conf, info, "unknown $#: $#" % [name, id])
+  case id.normalize
+  of "all": # other note groups would be easy to support via additional cases
+    notes = if isSomeHint: {hintMin..hintMax} else: {warnMin..warnMax}
+  elif isSomeHint: findNote(hintMin, hintMax, "hint")
+  else: findNote(warnMin, warnMax, "warning")
+  var val = substr(arg, i).normalize
+  if val == "": val = "on"
+  if val notin ["on", "off"]:
+    # xxx in future work we should also allow users to have control over `foreignPackageNotes`
+    # so that they can enable `hints|warnings|warningAsErrors` for all the code they depend on.
+    localError(conf, info, errOnOrOffExpectedButXFound % arg)
   else:
-    let x = findStr(lineinfos.WarningsToStr, id)
-    if x >= 0: n = TNoteKind(x + ord(warnMin))
-    else: localError(conf, info, "unknown warning: " & id)
-  case substr(arg, i).normalize
-  of "on":
-    incl(conf.notes, n)
-    incl(conf.mainPackageNotes, n)
-    incl(conf.enableNotes, n)
-  of "off":
-    excl(conf.notes, n)
-    excl(conf.mainPackageNotes, n)
-    incl(conf.disableNotes, n)
-    excl(conf.foreignPackageNotes, n)
-  else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
+    let isOn = val == "on"
+    if isOn and id.normalize == "all":
+      localError(conf, info, "only 'all:off' is supported")
+    for n in notes:
+      if n notin conf.cmdlineNotes or pass == passCmd1:
+        if pass == passCmd1: incl(conf.cmdlineNotes, n)
+        incl(conf.modifiedyNotes, n)
+        if state in {wWarningAsError, wHintAsError}:
+          conf.warningAsErrors[n] = isOn # xxx rename warningAsErrors to noteAsErrors
+        else:
+          conf.notes[n] = isOn
+          conf.mainPackageNotes[n] = isOn
+        if not isOn: excl(conf.foreignPackageNotes, n)
 
 proc processCompile(conf: ConfigRef; filename: string) =
   var found = findFile(conf, filename)
-  if found == "": found = filename
+  if found.isEmpty: found = AbsoluteFile filename
   extccomp.addExternalFileToCompile(conf, found)
 
 const
-  errNoneBoehmRefcExpectedButXFound = "'none', 'boehm' or 'refc' expected, but '$1' found"
+  errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'atomicArc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found"
   errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
-  errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found"
+  errGuiConsoleOrLibExpectedButXFound = "'gui', 'console', 'lib' or 'staticlib' expected, but '$1' found"
+  errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
+  errInvalidFeatureButXFound = Feature.toSeq.map(proc(val:Feature): string = "'$1'" % $val).join(", ") & " expected, but '$1' found"
+
+template warningOptionNoop(switch: string) =
+  warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch)
+
+template deprecatedAlias(oldName, newName: string) =
+  warningDeprecated(conf, info, "'$#' is a deprecated alias for '$#'" % [oldName, newName])
 
 proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo): bool =
   case switch.normalize
-  of "gc":
+  of "gc", "mm":
     case arg.normalize
-    of "boehm":        result = conf.selectedGC == gcBoehm
-    of "refc":         result = conf.selectedGC == gcRefc
-    of "v2":           result = conf.selectedGC == gcV2
+    of "boehm": result = conf.selectedGC == gcBoehm
+    of "refc": result = conf.selectedGC == gcRefc
     of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
-    of "generational": result = conf.selectedGC == gcGenerational
-    of "go":           result = conf.selectedGC == gcGo
-    of "none":         result = conf.selectedGC == gcNone
+    of "destructors", "arc": result = conf.selectedGC == gcArc
+    of "orc": result = conf.selectedGC == gcOrc
+    of "hooks": result = conf.selectedGC == gcHooks
+    of "go": result = conf.selectedGC == gcGo
+    of "none": result = conf.selectedGC == gcNone
     of "stack", "regions": result = conf.selectedGC == gcRegions
-    else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
+    of "atomicarc": result = conf.selectedGC == gcAtomicArc
+    else:
+      result = false
+      localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
   of "opt":
     case arg.normalize
     of "speed": result = contains(conf.options, optOptimizeSpeed)
     of "size": result = contains(conf.options, optOptimizeSize)
     of "none": result = conf.options * {optOptimizeSpeed, optOptimizeSize} == {}
-    else: localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg)
+    else:
+      result = false
+      localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg)
   of "verbosity": result = $conf.verbosity == arg
   of "app":
     case arg.normalize
-    of "gui":       result = contains(conf.globalOptions, optGenGuiApp)
-    of "console":   result = not contains(conf.globalOptions, optGenGuiApp)
-    of "lib":       result = contains(conf.globalOptions, optGenDynLib) and
+    of "gui": result = contains(conf.globalOptions, optGenGuiApp)
+    of "console": result = not contains(conf.globalOptions, optGenGuiApp)
+    of "lib": result = contains(conf.globalOptions, optGenDynLib) and
                       not contains(conf.globalOptions, optGenGuiApp)
     of "staticlib": result = contains(conf.globalOptions, optGenStaticLib) and
                       not contains(conf.globalOptions, optGenGuiApp)
-    else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg)
+    else:
+      result = false
+      localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg)
   of "dynliboverride":
     result = isDynlibOverride(conf, arg)
-  else: invalidCmdLineOption(conf, passCmd1, switch, info)
+  of "exceptions":
+    case arg.normalize
+    of "cpp": result = conf.exc == excCpp
+    of "setjmp": result = conf.exc == excSetjmp
+    of "quirky": result = conf.exc == excQuirky
+    of "goto": result = conf.exc == excGoto
+    else:
+      result = false
+      localError(conf, info, errInvalidExceptionSystem % arg)
+  of "experimental":
+    try:
+      result = conf.features.contains parseEnum[Feature](arg)
+    except ValueError:
+      result = false
+      localError(conf, info, errInvalidFeatureButXFound % arg)
+  else:
+    result = false
+    invalidCmdLineOption(conf, passCmd1, switch, info)
 
 proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool =
   case switch.normalize
@@ -251,8 +325,9 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
   of "hints": result = contains(conf.options, optHints)
   of "threadanalysis": result = contains(conf.globalOptions, optThreadAnalysis)
   of "stacktrace": result = contains(conf.options, optStackTrace)
+  of "stacktracemsgs": result = contains(conf.options, optStackTraceMsgs)
   of "linetrace": result = contains(conf.options, optLineTrace)
-  of "debugger": result = contains(conf.options, optEndb)
+  of "debugger": result = contains(conf.globalOptions, optCDebug)
   of "profiler": result = contains(conf.options, optProfiler)
   of "memtracker": result = contains(conf.options, optMemTracker)
   of "checks", "x": result = conf.options * ChecksOptions == ChecksOptions
@@ -260,219 +335,434 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
     result = conf.options * {optNaNCheck, optInfCheck} == {optNaNCheck, optInfCheck}
   of "infchecks": result = contains(conf.options, optInfCheck)
   of "nanchecks": result = contains(conf.options, optNaNCheck)
-  of "nilchecks": result = contains(conf.options, optNilCheck)
   of "objchecks": result = contains(conf.options, optObjCheck)
   of "fieldchecks": result = contains(conf.options, optFieldCheck)
   of "rangechecks": result = contains(conf.options, optRangeCheck)
   of "boundchecks": result = contains(conf.options, optBoundsCheck)
+  of "refchecks":
+    warningDeprecated(conf, info, "refchecks is deprecated!")
+    result = contains(conf.options, optRefCheck)
   of "overflowchecks": result = contains(conf.options, optOverflowCheck)
-  of "movechecks": result = contains(conf.options, optMoveCheck)
+  of "staticboundchecks": result = contains(conf.options, optStaticBoundsCheck)
+  of "stylechecks": result = contains(conf.options, optStyleCheck)
   of "linedir": result = contains(conf.options, optLineDir)
   of "assertions", "a": result = contains(conf.options, optAssert)
   of "run", "r": result = contains(conf.globalOptions, optRun)
   of "symbolfiles": result = conf.symbolFiles != disabledSf
   of "genscript": result = contains(conf.globalOptions, optGenScript)
+  of "gencdeps": result = contains(conf.globalOptions, optGenCDeps)
   of "threads": result = contains(conf.globalOptions, optThreads)
-  of "taintmode": result = contains(conf.globalOptions, optTaintMode)
   of "tlsemulation": result = contains(conf.globalOptions, optTlsEmulation)
   of "implicitstatic": result = contains(conf.options, optImplicitStatic)
-  of "patterns": result = contains(conf.options, optPatterns)
+  of "patterns", "trmacros":
+    if switch.normalize == "patterns": deprecatedAlias(switch, "trmacros")
+    result = contains(conf.options, optTrMacros)
   of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
-  else: invalidCmdLineOption(conf, passCmd1, switch, info)
+  of "nilseqs", "nilchecks", "taintmode":
+    warningOptionNoop(switch)
+    result = false
+  of "panics": result = contains(conf.globalOptions, optPanics)
+  of "jsbigint64": result = contains(conf.globalOptions, optJsBigInt64)
+  else:
+    result = false
+    invalidCmdLineOption(conf, passCmd1, switch, info)
 
 proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
-                 notRelativeToProj = false): string =
+                 notRelativeToProj = false): AbsoluteDir =
   let p = if os.isAbsolute(path) or '$' in path:
             path
           elif notRelativeToProj:
             getCurrentDir() / path
           else:
-            conf.projectPath / path
+            conf.projectPath.string / path
   try:
-    result = pathSubs(conf, p, toFullPath(conf, info).splitFile().dir)
+    result = AbsoluteDir pathSubs(conf, p, toFullPath(conf, info).splitFile().dir)
   except ValueError:
     localError(conf, info, "invalid path: " & p)
-    result = p
+    result = AbsoluteDir p
 
-proc processCfgPath(conf: ConfigRef; path: string, info: TLineInfo): string =
-  let path = if path[0] == '"': strutils.unescape(path) else: path
+proc processCfgPath(conf: ConfigRef; path: string, info: TLineInfo): AbsoluteDir =
+  let path = if path.len > 0 and path[0] == '"': strutils.unescape(path)
+             else: path
   let basedir = toFullPath(conf, info).splitFile().dir
   let p = if os.isAbsolute(path) or '$' in path:
             path
           else:
             basedir / path
   try:
-    result = pathSubs(conf, p, basedir)
+    result = AbsoluteDir pathSubs(conf, p, basedir)
   except ValueError:
     localError(conf, info, "invalid path: " & p)
-    result = p
+    result = AbsoluteDir p
 
 const
   errInvalidNumber = "$1 is not a valid number"
 
+proc makeAbsolute(s: string): AbsoluteFile =
+  if isAbsolute(s):
+    AbsoluteFile pathnorm.normalizePath(s)
+  else:
+    AbsoluteFile pathnorm.normalizePath(os.getCurrentDir() / s)
+
+proc setTrackingInfo(conf: ConfigRef; dirty, file, line, column: string,
+                     info: TLineInfo) =
+  ## set tracking info, common code for track, trackDirty, & ideTrack
+  var ln: int = 0
+  var col: int = 0
+  if parseUtils.parseInt(line, ln) <= 0:
+    localError(conf, info, errInvalidNumber % line)
+  if parseUtils.parseInt(column, col) <= 0:
+    localError(conf, info, errInvalidNumber % column)
+
+  let a = makeAbsolute(file)
+  if dirty == "":
+    conf.m.trackPos = newLineInfo(conf, a, ln, col)
+  else:
+    let dirtyOriginalIdx = fileInfoIdx(conf, a)
+    if dirtyOriginalIdx.int32 >= 0:
+      msgs.setDirtyFile(conf, dirtyOriginalIdx, makeAbsolute(dirty))
+    conf.m.trackPos = newLineInfo(dirtyOriginalIdx, ln, col)
+
 proc trackDirty(conf: ConfigRef; arg: string, info: TLineInfo) =
   var a = arg.split(',')
   if a.len != 4: localError(conf, info,
                             "DIRTY_BUFFER,ORIGINAL_FILE,LINE,COLUMN expected")
-  var line, column: int
-  if parseUtils.parseInt(a[2], line) <= 0:
-    localError(conf, info, errInvalidNumber % a[1])
-  if parseUtils.parseInt(a[3], column) <= 0:
-    localError(conf, info, errInvalidNumber % a[2])
-
-  let dirtyOriginalIdx = fileInfoIdx(conf, a[1])
-  if dirtyOriginalIdx.int32 >= 0:
-    msgs.setDirtyFile(conf, dirtyOriginalIdx, a[0])
-
-  conf.m.trackPos = newLineInfo(dirtyOriginalIdx, line, column)
+  setTrackingInfo(conf, a[0], a[1], a[2], a[3], info)
 
 proc track(conf: ConfigRef; arg: string, info: TLineInfo) =
   var a = arg.split(',')
   if a.len != 3: localError(conf, info, "FILE,LINE,COLUMN expected")
-  var line, column: int
-  if parseUtils.parseInt(a[1], line) <= 0:
-    localError(conf, info, errInvalidNumber % a[1])
-  if parseUtils.parseInt(a[2], column) <= 0:
-    localError(conf, info, errInvalidNumber % a[2])
-  conf.m.trackPos = newLineInfo(conf, a[0], line, column)
+  setTrackingInfo(conf, "", a[0], a[1], a[2], info)
+
+proc trackIde(conf: ConfigRef; cmd: IdeCmd, arg: string, info: TLineInfo) =
+  ## set the tracking info related to an ide cmd, supports optional dirty file
+  var a = arg.split(',')
+  case a.len
+  of 4:
+    setTrackingInfo(conf, a[0], a[1], a[2], a[3], info)
+  of 3:
+    setTrackingInfo(conf, "", a[0], a[1], a[2], info)
+  else:
+    localError(conf, info, "[DIRTY_BUFFER,]ORIGINAL_FILE,LINE,COLUMN expected")
+  conf.ideCmd = cmd
 
 proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
   if pass in {passCmd2, passPP}:
     expectArg(conf, switch, arg, pass, info)
     options.inclDynlibOverride(conf, arg)
 
+template handleStdinOrCmdInput =
+  conf.projectFull = conf.projectName.AbsoluteFile
+  conf.projectPath = AbsoluteDir getCurrentDir()
+  if conf.outDir.isEmpty:
+    conf.outDir = getNimcacheDir(conf)
+
+proc handleStdinInput*(conf: ConfigRef) =
+  conf.projectName = "stdinfile"
+  conf.projectIsStdin = true
+  handleStdinOrCmdInput()
+
+proc handleCmdInput*(conf: ConfigRef) =
+  conf.projectName = "cmdfile"
+  handleStdinOrCmdInput()
+
+proc parseCommand*(command: string): Command =
+  case command.normalize
+  of "c", "cc", "compile", "compiletoc": cmdCompileToC
+  of "cpp", "compiletocpp": cmdCompileToCpp
+  of "objc", "compiletooc": cmdCompileToOC
+  of "js", "compiletojs": cmdCompileToJS
+  of "r": cmdCrun
+  of "m": cmdM
+  of "run": cmdTcc
+  of "check": cmdCheck
+  of "e": cmdNimscript
+  of "doc0": cmdDoc0
+  of "doc2", "doc": cmdDoc
+  of "doc2tex": cmdDoc2tex
+  of "rst2html": cmdRst2html
+  of "md2tex": cmdMd2tex
+  of "md2html": cmdMd2html
+  of "rst2tex": cmdRst2tex
+  of "jsondoc0": cmdJsondoc0
+  of "jsondoc2", "jsondoc": cmdJsondoc
+  of "ctags": cmdCtags
+  of "buildindex": cmdBuildindex
+  of "gendepend": cmdGendepend
+  of "dump": cmdDump
+  of "parse": cmdParse
+  of "rod": cmdRod
+  of "secret": cmdInteractive
+  of "nop", "help": cmdNop
+  of "jsonscript": cmdJsonscript
+  else: cmdUnknown
+
+proc setCmd*(conf: ConfigRef, cmd: Command) =
+  ## sets cmd, backend so subsequent flags can query it (e.g. so --gc:arc can be ignored for backendJs)
+  # Note that `--backend` can override the backend, so the logic here must remain reversible.
+  conf.cmd = cmd
+  case cmd
+  of cmdCompileToC, cmdCrun, cmdTcc: conf.backend = backendC
+  of cmdCompileToCpp: conf.backend = backendCpp
+  of cmdCompileToOC: conf.backend = backendObjc
+  of cmdCompileToJS: conf.backend = backendJs
+  else: discard
+
+proc setCommandEarly*(conf: ConfigRef, command: string) =
+  conf.command = command
+  setCmd(conf, command.parseCommand)
+  # command early customizations
+  # must be handled here to honor subsequent `--hint:x:on|off`
+  case conf.cmd
+  of cmdRst2html, cmdRst2tex, cmdMd2html, cmdMd2tex:
+      # xxx see whether to add others: cmdGendepend, etc.
+    conf.foreignPackageNotes = {hintSuccessX}
+  else:
+    conf.foreignPackageNotes = foreignPackageNotesDefault
+
+proc specialDefine(conf: ConfigRef, key: string; pass: TCmdLinePass) =
+  # Keep this syncronized with the default config/nim.cfg!
+  if cmpIgnoreStyle(key, "nimQuirky") == 0:
+    conf.exc = excQuirky
+  elif cmpIgnoreStyle(key, "release") == 0 or cmpIgnoreStyle(key, "danger") == 0:
+    if pass in {passCmd1, passPP}:
+      conf.options.excl {optStackTrace, optLineTrace, optLineDir, optOptimizeSize}
+      conf.globalOptions.excl {optExcessiveStackTrace, optCDebug}
+      conf.options.incl optOptimizeSpeed
+  if cmpIgnoreStyle(key, "danger") == 0 or cmpIgnoreStyle(key, "quick") == 0:
+    if pass in {passCmd1, passPP}:
+      conf.options.excl {optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck,
+        optOverflowCheck, optAssert, optStackTrace, optLineTrace, optLineDir}
+      conf.globalOptions.excl {optCDebug}
+
+proc initOrcDefines*(conf: ConfigRef) =
+  conf.selectedGC = gcOrc
+  defineSymbol(conf.symbols, "gcorc")
+  defineSymbol(conf.symbols, "gcdestructors")
+  incl conf.globalOptions, optSeqDestructors
+  incl conf.globalOptions, optTinyRtti
+  defineSymbol(conf.symbols, "nimSeqsV2")
+  defineSymbol(conf.symbols, "nimV2")
+  if conf.exc == excNone and conf.backend != backendCpp:
+    conf.exc = excGoto
+
+proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef) =
+  defineSymbol(conf.symbols, "gcdestructors")
+  incl conf.globalOptions, optSeqDestructors
+  incl conf.globalOptions, optTinyRtti
+  if pass in {passCmd2, passPP}:
+    defineSymbol(conf.symbols, "nimSeqsV2")
+    defineSymbol(conf.symbols, "nimV2")
+  if conf.exc == excNone and conf.backend != backendCpp:
+    conf.exc = excGoto
+
+proc unregisterArcOrc*(conf: ConfigRef) =
+  undefSymbol(conf.symbols, "gcdestructors")
+  undefSymbol(conf.symbols, "gcarc")
+  undefSymbol(conf.symbols, "gcorc")
+  undefSymbol(conf.symbols, "gcatomicarc")
+  undefSymbol(conf.symbols, "nimSeqsV2")
+  undefSymbol(conf.symbols, "nimV2")
+  excl conf.globalOptions, optSeqDestructors
+  excl conf.globalOptions, optTinyRtti
+
+proc processMemoryManagementOption(switch, arg: string, pass: TCmdLinePass,
+                info: TLineInfo; conf: ConfigRef) =
+  if conf.backend == backendJs: return # for: bug #16033
+  expectArg(conf, switch, arg, pass, info)
+  if pass in {passCmd2, passPP}:
+    case arg.normalize
+    of "boehm":
+      unregisterArcOrc(conf)
+      conf.selectedGC = gcBoehm
+      defineSymbol(conf.symbols, "boehmgc")
+      incl conf.globalOptions, optTlsEmulation # Boehm GC doesn't scan the real TLS
+    of "refc":
+      unregisterArcOrc(conf)
+      defineSymbol(conf.symbols, "gcrefc")
+      conf.selectedGC = gcRefc
+    of "markandsweep":
+      unregisterArcOrc(conf)
+      conf.selectedGC = gcMarkAndSweep
+      defineSymbol(conf.symbols, "gcmarkandsweep")
+    of "destructors", "arc":
+      conf.selectedGC = gcArc
+      defineSymbol(conf.symbols, "gcarc")
+      registerArcOrc(pass, conf)
+    of "orc":
+      conf.selectedGC = gcOrc
+      defineSymbol(conf.symbols, "gcorc")
+      registerArcOrc(pass, conf)
+    of "atomicarc":
+      conf.selectedGC = gcAtomicArc
+      defineSymbol(conf.symbols, "gcatomicarc")
+      registerArcOrc(pass, conf)
+    of "hooks":
+      conf.selectedGC = gcHooks
+      defineSymbol(conf.symbols, "gchooks")
+      incl conf.globalOptions, optSeqDestructors
+      processOnOffSwitchG(conf, {optSeqDestructors}, arg, pass, info)
+      if pass in {passCmd2, passPP}:
+        defineSymbol(conf.symbols, "nimSeqsV2")
+    of "go":
+      unregisterArcOrc(conf)
+      conf.selectedGC = gcGo
+      defineSymbol(conf.symbols, "gogc")
+    of "none":
+      unregisterArcOrc(conf)
+      conf.selectedGC = gcNone
+      defineSymbol(conf.symbols, "nogc")
+    of "stack", "regions":
+      unregisterArcOrc(conf)
+      conf.selectedGC = gcRegions
+      defineSymbol(conf.symbols, "gcregions")
+    else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
+
+proc pathRelativeToConfig(arg: string, pass: TCmdLinePass, conf: ConfigRef): string =
+  if pass == passPP and not isAbsolute(arg):
+    assert isAbsolute(conf.currentConfigDir), "something is wrong with currentConfigDir"
+    result = conf.currentConfigDir / arg
+  else:
+    result = arg
+
 proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
                     conf: ConfigRef) =
-  var
-    key, val: string
+  var key = ""
+  var val = ""
   case switch.normalize
+  of "eval":
+    expectArg(conf, switch, arg, pass, info)
+    conf.projectIsCmd = true
+    conf.cmdInput = arg # can be empty (a nim file with empty content is valid too)
+    if conf.cmd == cmdNone:
+      conf.command = "e"
+      conf.setCmd cmdNimscript # better than `cmdCrun` as a default
+      conf.implicitCmd = true
   of "path", "p":
     expectArg(conf, switch, arg, pass, info)
-    addPath(conf, if pass == passPP: processCfgPath(conf, arg, info) else: processPath(conf, arg, info), info)
-  of "nimblepath", "babelpath":
-    # keep the old name for compat
+    for path in nimbleSubs(conf, arg):
+      addPath(conf, if pass == passPP: processCfgPath(conf, path, info)
+                    else: processPath(conf, path, info), info)
+  of "nimblepath":
     if pass in {passCmd2, passPP} and optNoNimblePath notin conf.globalOptions:
       expectArg(conf, switch, arg, pass, info)
       var path = processPath(conf, arg, info, notRelativeToProj=true)
-      let nimbleDir = getEnv("NIMBLE_DIR")
-      if nimbleDir.len > 0 and pass == passPP: path = nimbleDir / "pkgs"
+      let nimbleDir = AbsoluteDir getEnv("NIMBLE_DIR")
+      if not nimbleDir.isEmpty and pass == passPP:
+        path = nimbleDir / RelativeDir"pkgs2"
+        nimblePath(conf, path, info)
+        path = nimbleDir / RelativeDir"pkgs"
       nimblePath(conf, path, info)
-  of "nonimblepath", "nobabelpath":
+  of "nonimblepath":
     expectNoArg(conf, switch, arg, pass, info)
     disableNimblePath(conf)
+  of "clearnimblepath":
+    expectNoArg(conf, switch, arg, pass, info)
+    clearNimblePath(conf)
   of "excludepath":
     expectArg(conf, switch, arg, pass, info)
     let path = processPath(conf, arg, info)
-
-    conf.searchPaths.keepItIf(cmpPaths(it, path) != 0)
-    conf.lazyPaths.keepItIf(cmpPaths(it, path) != 0)
-
-    if (len(path) > 0) and (path[len(path) - 1] == DirSep):
-      let strippedPath = path[0 .. (len(path) - 2)]
-      conf.searchPaths.keepItIf(cmpPaths(it, strippedPath) != 0)
-      conf.lazyPaths.keepItIf(cmpPaths(it, strippedPath) != 0)
+    conf.searchPaths.keepItIf(it != path)
+    conf.lazyPaths.keepItIf(it != path)
   of "nimcache":
     expectArg(conf, switch, arg, pass, info)
-    conf.nimcacheDir = processPath(conf, arg, info, true)
+    var arg = arg
+    # refs bug #18674, otherwise `--os:windows` messes up with `--nimcache` set
+    # in config nims files, e.g. via: `import os; switch("nimcache", "/tmp/somedir")`
+    if conf.target.targetOS == osWindows and DirSep == '/': arg = arg.replace('\\', '/')
+    conf.nimcacheDir = processPath(conf, pathRelativeToConfig(arg, pass, conf), info, notRelativeToProj=true)
   of "out", "o":
     expectArg(conf, switch, arg, pass, info)
-    conf.outFile = arg
+    let f = splitFile(processPath(conf, arg, info, notRelativeToProj=true).string)
+    conf.outFile = RelativeFile f.name & f.ext
+    conf.outDir = toAbsoluteDir f.dir
+  of "outdir":
+    expectArg(conf, switch, arg, pass, info)
+    conf.outDir = processPath(conf, arg, info, notRelativeToProj=true)
+  of "usenimcache":
+    processOnOffSwitchG(conf, {optUseNimcache}, arg, pass, info)
   of "docseesrcurl":
     expectArg(conf, switch, arg, pass, info)
     conf.docSeeSrcUrl = arg
-  of "mainmodule", "m":
-    discard "allow for backwards compatibility, but don't do anything"
+  of "docroot":
+    conf.docRoot = if arg.len == 0: docRootDefault else: arg
+  of "backend", "b":
+    let backend = parseEnum(arg.normalize, TBackend.default)
+    if backend == TBackend.default: localError(conf, info, "invalid backend: '$1'" % arg)
+    if backend == backendJs: # bug #21209
+      conf.globalOptions.excl {optThreadAnalysis, optThreads}
+      if optRun in conf.globalOptions:
+        # for now, -r uses nodejs, so define nodejs
+        defineSymbol(conf.symbols, "nodejs")
+    conf.backend = backend
+  of "doccmd": conf.docCmd = arg
   of "define", "d":
     expectArg(conf, switch, arg, pass, info)
     if {':', '='} in arg:
       splitSwitch(conf, arg, key, val, pass, info)
+      specialDefine(conf, key, pass)
       defineSymbol(conf.symbols, key, val)
     else:
+      specialDefine(conf, arg, pass)
       defineSymbol(conf.symbols, arg)
   of "undef", "u":
     expectArg(conf, switch, arg, pass, info)
     undefSymbol(conf.symbols, arg)
-  of "symbol":
-    expectArg(conf, switch, arg, pass, info)
-    # deprecated, do nothing
   of "compile":
     expectArg(conf, switch, arg, pass, info)
     if pass in {passCmd2, passPP}: processCompile(conf, arg)
   of "link":
     expectArg(conf, switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: addExternalFileToLink(conf, arg)
+    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, optGenIndex}, arg, pass, info)
   of "gc":
-    expectArg(conf, switch, arg, pass, info)
-    case arg.normalize
-    of "boehm":
-      conf.selectedGC = gcBoehm
-      defineSymbol(conf.symbols, "boehmgc")
-    of "refc":
-      conf.selectedGC = gcRefc
-    of "v2":
-      conf.selectedGC = gcV2
-    of "markandsweep":
-      conf.selectedGC = gcMarkAndSweep
-      defineSymbol(conf.symbols, "gcmarkandsweep")
-    of "generational":
-      conf.selectedGC = gcGenerational
-      defineSymbol(conf.symbols, "gcgenerational")
-    of "go":
-      conf.selectedGC = gcGo
-      defineSymbol(conf.symbols, "gogc")
-    of "none":
-      conf.selectedGC = gcNone
-      defineSymbol(conf.symbols, "nogc")
-    of "stack", "regions":
-      conf.selectedGC= gcRegions
-      defineSymbol(conf.symbols, "gcregions")
-    else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
+    warningDeprecated(conf, info, "`gc:option` is deprecated; use `mm:option` instead")
+    processMemoryManagementOption(switch, arg, pass, info, conf)
+  of "mm":
+    processMemoryManagementOption(switch, arg, pass, info, conf)
   of "warnings", "w":
     if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf)
   of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf)
   of "hint": processSpecificNote(arg, wHint, pass, info, switch, conf)
+  of "warningaserror": processSpecificNote(arg, wWarningAsError, pass, info, switch, conf)
+  of "hintaserror": processSpecificNote(arg, wHintAsError, pass, info, switch, conf)
   of "hints":
     if processOnOffSwitchOrList(conf, {optHints}, arg, pass, info): listHints(conf)
-  of "threadanalysis": processOnOffSwitchG(conf, {optThreadAnalysis}, arg, pass, info)
+  of "threadanalysis":
+    if conf.backend == backendJs: discard
+    else: processOnOffSwitchG(conf, {optThreadAnalysis}, arg, pass, info)
   of "stacktrace": processOnOffSwitch(conf, {optStackTrace}, arg, pass, info)
+  of "stacktracemsgs": processOnOffSwitch(conf, {optStackTraceMsgs}, arg, pass, info)
   of "excessivestacktrace": processOnOffSwitchG(conf, {optExcessiveStackTrace}, arg, pass, info)
   of "linetrace": processOnOffSwitch(conf, {optLineTrace}, arg, pass, info)
   of "debugger":
     case arg.normalize
-    of "on", "endb":
-      conf.options.incl optEndb
-      defineSymbol(conf.symbols, "endb")
+    of "on", "native", "gdb":
+      conf.globalOptions.incl optCDebug
+      conf.options.incl optLineDir
+      #defineSymbol(conf.symbols, "nimTypeNames") # type names are used in gdb pretty printing
     of "off":
-      conf.options.excl optEndb
-      undefSymbol(conf.symbols, "endb")
-    of "native", "gdb":
-      incl(conf.globalOptions, optCDebug)
-      conf.options = conf.options + {optLineDir} - {optEndb}
-      defineSymbol(conf.symbols, "nimTypeNames") # type names are used in gdb pretty printing
-      undefSymbol(conf.symbols, "endb")
+      conf.globalOptions.excl optCDebug
     else:
-      localError(conf, info, "expected endb|gdb but found " & arg)
+      localError(conf, info, "expected native|gdb|on|off but found " & arg)
+  of "g": # alias for --debugger:native
+    conf.globalOptions.incl optCDebug
+    conf.options.incl optLineDir
+    #defineSymbol(conf.symbols, "nimTypeNames") # type names are used in gdb pretty printing
   of "profiler":
     processOnOffSwitch(conf, {optProfiler}, arg, pass, info)
     if optProfiler in conf.options: defineSymbol(conf.symbols, "profiler")
@@ -482,44 +772,47 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     if optMemTracker in conf.options: defineSymbol(conf.symbols, "memtracker")
     else: undefSymbol(conf.symbols, "memtracker")
   of "hotcodereloading":
-    processOnOffSwitch(conf, {optHotCodeReloading}, arg, pass, info)
-    if optHotCodeReloading in conf.options: defineSymbol(conf.symbols, "hotcodereloading")
-    else: undefSymbol(conf.symbols, "hotcodereloading")
-  of "oldnewlines":
-    case arg.normalize
-    of "on":
-      conf.oldNewlines = true
-      defineSymbol(conf.symbols, "nimOldNewlines")
-    of "off":
-      conf.oldNewlines = false
-      undefSymbol(conf.symbols, "nimOldNewlines")
+    processOnOffSwitchG(conf, {optHotCodeReloading}, arg, pass, info)
+    if conf.hcrOn:
+      defineSymbol(conf.symbols, "hotcodereloading")
+      defineSymbol(conf.symbols, "useNimRtl")
+      # hardcoded linking with dynamic runtime for MSVC for smaller binaries
+      # should do the same for all compilers (wherever applicable)
+      if isVSCompatible(conf):
+        extccomp.addCompileOptionCmd(conf, "/MD")
     else:
-      localError(conf, info, errOnOrOffExpectedButXFound % arg)
-  of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info)
+      undefSymbol(conf.symbols, "hotcodereloading")
+      undefSymbol(conf.symbols, "useNimRtl")
   of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info)
   of "floatchecks":
     processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info)
   of "infchecks": processOnOffSwitch(conf, {optInfCheck}, arg, pass, info)
   of "nanchecks": processOnOffSwitch(conf, {optNaNCheck}, arg, pass, info)
-  of "nilchecks": processOnOffSwitch(conf, {optNilCheck}, arg, pass, info)
   of "objchecks": processOnOffSwitch(conf, {optObjCheck}, arg, pass, info)
   of "fieldchecks": processOnOffSwitch(conf, {optFieldCheck}, arg, pass, info)
   of "rangechecks": processOnOffSwitch(conf, {optRangeCheck}, arg, pass, info)
   of "boundchecks": processOnOffSwitch(conf, {optBoundsCheck}, arg, pass, info)
+  of "refchecks":
+    warningDeprecated(conf, info, "refchecks is deprecated!")
+    processOnOffSwitch(conf, {optRefCheck}, arg, pass, info)
   of "overflowchecks": processOnOffSwitch(conf, {optOverflowCheck}, arg, pass, info)
-  of "movechecks": processOnOffSwitch(conf, {optMoveCheck}, arg, pass, info)
+  of "staticboundchecks": processOnOffSwitch(conf, {optStaticBoundsCheck}, arg, pass, info)
+  of "stylechecks": processOnOffSwitch(conf, {optStyleCheck}, arg, pass, info)
   of "linedir": processOnOffSwitch(conf, {optLineDir}, arg, pass, info)
   of "assertions", "a": processOnOffSwitch(conf, {optAssert}, arg, pass, info)
-  of "deadcodeelim": discard # deprecated, dead code elim always on
   of "threads":
-    processOnOffSwitchG(conf, {optThreads}, arg, pass, info)
-    #if optThreads in conf.globalOptions: incl(conf.notes, warnGcUnsafe)
-  of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
-  of "taintmode": processOnOffSwitchG(conf, {optTaintMode}, arg, pass, info)
+    if conf.backend == backendJs or conf.cmd == cmdNimscript: discard
+    else: processOnOffSwitchG(conf, {optThreads}, arg, pass, info)
+    #if optThreads in conf.globalOptions: conf.setNote(warnGcUnsafe)
+  of "tlsemulation":
+    processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
+    if optTlsEmulation in conf.globalOptions:
+      conf.legacyFeatures.incl emitGenerics
   of "implicitstatic":
     processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info)
-  of "patterns":
-    processOnOffSwitch(conf, {optPatterns}, arg, pass, info)
+  of "patterns", "trmacros":
+    if switch.normalize == "patterns": deprecatedAlias(switch, "trmacros")
+    processOnOffSwitch(conf, {optTrMacros}, arg, pass, info)
   of "opt":
     expectArg(conf, switch, arg, pass, info)
     case arg.normalize
@@ -551,6 +844,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
       defineSymbol(conf.symbols, "dll")
     of "staticlib":
       incl(conf.globalOptions, optGenStaticLib)
+      incl(conf.globalOptions, optNoMain)
       excl(conf.globalOptions, optGenGuiApp)
       defineSymbol(conf.symbols, "library")
       defineSymbol(conf.symbols, "staticlib")
@@ -569,51 +863,87 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     if pass in {passCmd2, passPP}: conf.cLibs.add processPath(conf, arg, info)
   of "clib":
     expectArg(conf, switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: conf.cLinkedLibs.add processPath(conf, arg, info)
+    if pass in {passCmd2, passPP}:
+      conf.cLinkedLibs.add arg
   of "header":
     if conf != nil: conf.headerFile = arg
     incl(conf.globalOptions, optGenIndex)
+  of "nimbasepattern":
+    if conf != nil: conf.nimbasePattern = arg
   of "index":
-    processOnOffSwitchG(conf, {optGenIndex}, arg, pass, info)
+    case arg.normalize
+    of "", "on": conf.globalOptions.incl {optGenIndex}
+    of "only": conf.globalOptions.incl {optGenIndexOnly, optGenIndex}
+    of "off": conf.globalOptions.excl {optGenIndex, optGenIndexOnly}
+    else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
+  of "noimportdoc":
+    processOnOffSwitchG(conf, {optNoImportdoc}, 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 "asm":
+    processOnOffSwitchG(conf, {optProduceAsm}, 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}:
-      let theOS = platform.nameToOS(arg)
-      if theOS == osNone: localError(conf, info, "unknown OS: '$1'" % arg)
-      elif theOS != conf.target.hostOS:
-        setTarget(conf.target, theOS, conf.target.targetCPU)
+    let theOS = platform.nameToOS(arg)
+    if theOS == osNone:
+      let osList = platform.listOSnames().join(", ")
+      localError(conf, info, "unknown OS: '$1'. Available options are: $2" % [arg, $osList])
+    else:
+      setTarget(conf.target, theOS, conf.target.targetCPU)
   of "cpu":
     expectArg(conf, switch, arg, pass, info)
-    if pass in {passCmd1, passPP}:
-      let cpu = platform.nameToCPU(arg)
-      if cpu == cpuNone: localError(conf, info, "unknown CPU: '$1'" % arg)
-      elif cpu != conf.target.hostCPU:
-        setTarget(conf.target, conf.target.targetOS, cpu)
+    let cpu = platform.nameToCPU(arg)
+    if cpu == cpuNone:
+      let cpuList = platform.listCPUnames().join(", ")
+      localError(conf, info, "unknown CPU: '$1'. Available options are: $2" % [ arg, cpuList])
+    else:
+      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)
+    if conf.backend == backendJs:
+      # for now, -r uses nodejs, so define nodejs
+      defineSymbol(conf.symbols, "nodejs")
+  of "maxloopiterationsvm":
+    expectArg(conf, switch, arg, pass, info)
+    var value: int = 10_000_000
+    discard parseSaturatedNatural(arg, value)
+    if not value > 0: localError(conf, info, "maxLoopIterationsVM must be a positive integer greater than zero")
+    conf.maxLoopIterationsVM = value
+  of "errormax":
+    expectArg(conf, switch, arg, pass, info)
+    # Note: `nim check` (etc) can overwrite this.
+    # `0` is meaningless, give it a useful meaning as in clang's -ferror-limit
+    # If user doesn't set this flag and the code doesn't either, it'd
+    # have the same effect as errorMax = 1
+    var value: int = 0
+    discard parseSaturatedNatural(arg, value)
+    conf.errorMax = if value == 0: high(int) else: value
   of "verbosity":
     expectArg(conf, switch, arg, pass, info)
-    conf.verbosity = parseInt(arg)
-    conf.notes = NotesVerbosity[conf.verbosity]
-    incl(conf.notes, conf.enableNotes)
-    excl(conf.notes, conf.disableNotes)
+    let verbosity = parseInt(arg)
+    if verbosity notin 0..3:
+      localError(conf, info, "invalid verbosity level: '$1'" % arg)
+    conf.verbosity = verbosity
+    var verb = NotesVerbosity[conf.verbosity]
+    ## We override the default `verb` by explicitly modified (set/unset) notes.
+    conf.notes = (conf.modifiedyNotes * conf.notes + verb) -
+      (conf.modifiedyNotes * verb - conf.notes)
     conf.mainPackageNotes = conf.notes
   of "parallelbuild":
     expectArg(conf, switch, arg, pass, info)
-    conf.numberOfProcessors = parseInt(arg)
+    var value: int = 0
+    discard parseSaturatedNatural(arg, value)
+    conf.numberOfProcessors = value
   of "version", "v":
     expectNoArg(conf, switch, arg, pass, info)
     writeVersionInfo(conf, pass)
@@ -626,30 +956,33 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
   of "help", "h":
     expectNoArg(conf, switch, arg, pass, info)
     helpOnError(conf, pass)
-  of "symbolfiles", "incremental":
-    case arg.normalize
-    of "on": conf.symbolFiles = v2Sf
-    of "off": conf.symbolFiles = disabledSf
-    of "writeonly": conf.symbolFiles = writeOnlySf
-    of "readonly": conf.symbolFiles = readOnlySf
-    of "v2": conf.symbolFiles = v2Sf
-    else: localError(conf, info, "invalid option for --symbolFiles: " & arg)
+  of "symbolfiles", "incremental", "ic":
+    if switch.normalize == "symbolfiles": deprecatedAlias(switch, "incremental")
+      # xxx maybe also ic, since not in help?
+    if pass in {passCmd2, passPP}:
+      case arg.normalize
+      of "on": conf.symbolFiles = v2Sf
+      of "off": conf.symbolFiles = disabledSf
+      of "writeonly": conf.symbolFiles = writeOnlySf
+      of "readonly": conf.symbolFiles = readOnlySf
+      of "v2": conf.symbolFiles = v2Sf
+      of "stress": conf.symbolFiles = stressTest
+      else: localError(conf, info, "invalid option for --incremental: " & arg)
+    setUseIc(conf.symbolFiles != disabledSf)
   of "skipcfg":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl(conf.globalOptions, optSkipConfigFile)
+    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)
+    if switch.normalize == "gendeps": deprecatedAlias(switch, "genscript")
+    processOnOffSwitchG(conf, {optGenScript}, arg, pass, info)
+    processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
+  of "gencdeps":
+    processOnOffSwitchG(conf, {optGenCDeps}, arg, pass, info)
   of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info)
   of "lib":
     expectArg(conf, switch, arg, pass, info)
@@ -659,8 +992,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     splitSwitch(conf, arg, key, val, pass, info)
     os.putEnv(key, val)
   of "cc":
-    expectArg(conf, switch, arg, pass, info)
-    setCC(conf, arg, info)
+    if conf.backend != backendJs: # bug #19330
+      expectArg(conf, switch, arg, pass, info)
+      setCC(conf, arg, info)
   of "track":
     expectArg(conf, switch, arg, pass, info)
     track(conf, arg, info)
@@ -671,31 +1005,50 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     expectNoArg(conf, switch, arg, pass, info)
     conf.ideCmd = ideSug
   of "def":
-    expectNoArg(conf, switch, arg, pass, info)
-    conf.ideCmd = ideDef
-  of "eval":
     expectArg(conf, switch, arg, pass, info)
-    conf.evalExpr = arg
+    trackIde(conf, ideDef, arg, info)
   of "context":
     expectNoArg(conf, switch, arg, pass, info)
     conf.ideCmd = ideCon
   of "usages":
-    expectNoArg(conf, switch, arg, pass, info)
-    conf.ideCmd = ideUse
+    expectArg(conf, switch, arg, pass, info)
+    trackIde(conf, ideUse, arg, info)
+  of "defusages":
+    expectArg(conf, switch, arg, pass, info)
+    trackIde(conf, ideDus, arg, info)
   of "stdout":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl(conf.globalOptions, optStdout)
+    processOnOffSwitchG(conf, {optStdout}, arg, pass, info)
+  of "filenames":
+    case arg.normalize
+    of "abs": conf.filenameOption = foAbs
+    of "canonical": conf.filenameOption = foCanonical
+    of "legacyrelproj": conf.filenameOption = foLegacyRelProj
+    else: localError(conf, info, "expected: abs|canonical|legacyRelProj, got: $1" % arg)
+  of "processing":
+    incl(conf.notes, hintProcessing)
+    incl(conf.mainPackageNotes, hintProcessing)
+    case arg.normalize
+    of "dots": conf.hintProcessingDots = true
+    of "filenames": conf.hintProcessingDots = false
+    of "off":
+      excl(conf.notes, hintProcessing)
+      excl(conf.mainPackageNotes, hintProcessing)
+    else: localError(conf, info, "expected: dots|filenames|off, got: $1" % arg)
+  of "unitsep":
+    conf.unitSep = if switchOn(arg): "\31" else: ""
   of "listfullpaths":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl conf.globalOptions, optListFullPaths
+    # xxx in future work, use `warningDeprecated`
+    conf.filenameOption = if switchOn(arg): foAbs else: foCanonical
+  of "spellsuggest":
+    if arg.len == 0: conf.spellSuggestMax = spellSuggestSecretSauce
+    elif arg == "auto": conf.spellSuggestMax = spellSuggestSecretSauce
+    else: conf.spellSuggestMax = parseInt(arg)
+  of "declaredlocs":
+    processOnOffSwitchG(conf, {optDeclaredLocs}, arg, pass, info)
   of "dynliboverride":
     dynlibOverride(conf, switch, arg, pass, info)
   of "dynliboverrideall":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl conf.globalOptions, optDynlibOverrideAll
-  of "cs":
-    # only supported for compatibility. Does nothing.
-    expectArg(conf, switch, arg, pass, info)
+    processOnOffSwitchG(conf, {optDynlibOverrideAll}, arg, pass, info)
   of "experimental":
     if arg.len == 0:
       conf.features.incl oldExperimentalFeatures
@@ -704,64 +1057,143 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
         conf.features.incl parseEnum[Feature](arg)
       except ValueError:
         localError(conf, info, "unknown experimental feature")
+  of "legacy":
+    try:
+      conf.legacyFeatures.incl parseEnum[LegacyFeature](arg)
+    except ValueError:
+      localError(conf, info, "unknown obsolete feature")
   of "nocppexceptions":
     expectNoArg(conf, switch, arg, pass, info)
-    incl(conf.globalOptions, optNoCppExceptions)
+    conf.exc = low(ExceptionSystem)
     defineSymbol(conf.symbols, "noCppExceptions")
+  of "shownonexports":
+    expectNoArg(conf, switch, arg, pass, info)
+    showNonExportedFields(conf)
+  of "exceptions":
+    case arg.normalize
+    of "cpp": conf.exc = excCpp
+    of "setjmp": conf.exc = excSetjmp
+    of "quirky": conf.exc = excQuirky
+    of "goto": conf.exc = excGoto
+    else: localError(conf, info, errInvalidExceptionSystem % arg)
   of "cppdefine":
     expectArg(conf, switch, arg, pass, info)
     if conf != nil:
       conf.cppDefine(arg)
   of "newruntime":
+    warningDeprecated(conf, info, "newruntime is deprecated, use arc/orc instead!")
     expectNoArg(conf, switch, arg, pass, info)
-    doAssert(conf != nil)
-    incl(conf.features, destructor)
-    defineSymbol(conf.symbols, "nimNewRuntime")
-  of "nep1":
-    processOnOffSwitchG(conf, {optCheckNep1}, arg, pass, info)
+    if pass in {passCmd2, passPP}:
+      doAssert(conf != nil)
+      incl(conf.features, destructor)
+      incl(conf.globalOptions, optTinyRtti)
+      incl(conf.globalOptions, optOwnedRefs)
+      incl(conf.globalOptions, optSeqDestructors)
+      defineSymbol(conf.symbols, "nimV2")
+      conf.selectedGC = gcHooks
+      defineSymbol(conf.symbols, "gchooks")
+      defineSymbol(conf.symbols, "nimSeqsV2")
+      defineSymbol(conf.symbols, "nimOwnedEnabled")
+  of "seqsv2":
+    processOnOffSwitchG(conf, {optSeqDestructors}, arg, pass, info)
+    if pass in {passCmd2, passPP}:
+      defineSymbol(conf.symbols, "nimSeqsV2")
+  of "stylecheck":
+    case arg.normalize
+    of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError}
+    of "hint": conf.globalOptions = conf.globalOptions + {optStyleHint} - {optStyleError}
+    of "error": conf.globalOptions = conf.globalOptions + {optStyleError}
+    of "usages": conf.globalOptions.incl optStyleUsages
+    else: localError(conf, info, errOffHintsError % arg)
+  of "showallmismatches":
+    processOnOffSwitchG(conf, {optShowAllMismatches}, arg, pass, info)
   of "cppcompiletonamespace":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl conf.globalOptions, optUseNimNamespace
-    defineSymbol(conf.symbols, "cppCompileToNamespace")
+    if arg.len > 0:
+      conf.cppCustomNamespace = arg
+    else:
+      conf.cppCustomNamespace = "Nim"
+    defineSymbol(conf.symbols, "cppCompileToNamespace", conf.cppCustomNamespace)
+  of "docinternal":
+    processOnOffSwitchG(conf, {optDocInternal}, arg, pass, info)
+  of "multimethods":
+    processOnOffSwitchG(conf, {optMultiMethods}, arg, pass, info)
+  of "expandmacro":
+    expectArg(conf, switch, arg, pass, info)
+    conf.macrosToExpand[arg] = "T"
+  of "expandarc":
+    expectArg(conf, switch, arg, pass, info)
+    conf.arcToExpand[arg] = "T"
+  of "benchmarkvm":
+    processOnOffSwitchG(conf, {optBenchmarkVM}, arg, pass, info)
+  of "profilevm":
+    processOnOffSwitchG(conf, {optProfileVM}, arg, pass, info)
+  of "sinkinference":
+    processOnOffSwitch(conf, {optSinkInference}, arg, pass, info)
+  of "cursorinference":
+    # undocumented, for debugging purposes only:
+    processOnOffSwitch(conf, {optCursorInference}, arg, pass, info)
+  of "panics":
+    processOnOffSwitchG(conf, {optPanics}, arg, pass, info)
+    if optPanics in conf.globalOptions:
+      defineSymbol(conf.symbols, "nimPanics")
+  of "jsbigint64":
+    processOnOffSwitchG(conf, {optJsBigInt64}, arg, pass, info)
+  of "sourcemap": # xxx document in --fullhelp
+    conf.globalOptions.incl optSourcemap
+    conf.options.incl optLineDir
+  of "deepcopy":
+    processOnOffSwitchG(conf, {optEnableDeepCopy}, arg, pass, info)
+  of "": # comes from "-" in for example: `nim c -r -` (gets stripped from -)
+    handleStdinInput(conf)
+  of "nilseqs", "nilchecks", "symbol", "taintmode", "cs", "deadcodeelim": warningOptionNoop(switch)
+  of "nimmainprefix": conf.nimMainPrefix = arg
   else:
     if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg)
     else: invalidCmdLineOption(conf, pass, switch, info)
 
-template gCmdLineInfo*(): untyped = newLineInfo(config, "command line", 1, 1)
-
 proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) =
-  var cmd, arg: string
+  var cmd = ""
+  var arg = ""
   splitSwitch(config, switch, cmd, arg, pass, gCmdLineInfo)
   processSwitch(cmd, arg, pass, gCmdLineInfo, config)
 
-
 proc processSwitch*(pass: TCmdLinePass; p: OptParser; config: ConfigRef) =
   # hint[X]:off is parsed as (p.key = "hint[X]", p.val = "off")
-  # we fix this here
+  # we transform it to (key = hint, val = [X]:off)
   var bracketLe = strutils.find(p.key, '[')
   if bracketLe >= 0:
     var key = substr(p.key, 0, bracketLe - 1)
-    var val = substr(p.key, bracketLe + 1) & ':' & p.val
+    var val = substr(p.key, bracketLe) & ':' & p.val
     processSwitch(key, val, pass, gCmdLineInfo, config)
   else:
     processSwitch(p.key, p.val, pass, gCmdLineInfo, config)
 
 proc processArgument*(pass: TCmdLinePass; p: OptParser;
                       argsCount: var int; config: ConfigRef): bool =
+  if argsCount == 0 and config.implicitCmd:
+    argsCount.inc
   if argsCount == 0:
     # nim filename.nims  is the same as "nim e filename.nims":
-    if p.key.endswith(".nims"):
-      config.command = "e"
+    if p.key.endsWith(".nims"):
+      config.setCmd cmdNimscript
+      incl(config.globalOptions, optWasNimscript)
       config.projectName = unixToNativePath(p.key)
       config.arguments = cmdLineRest(p)
       result = true
     elif pass != passCmd2:
-      config.command = p.key
+      setCommandEarly(config, p.key)
+      result = false
+    else: result = false
   else:
     if pass == passCmd1: config.commandArgs.add p.key
     if argsCount == 1:
+      if p.key.endsWith(".nims"):
+        incl(config.globalOptions, optWasNimscript)
       # support UNIX style filenames everywhere for portable build scripts:
-      config.projectName = unixToNativePath(p.key)
+      if config.projectName.len == 0:
+        config.projectName = unixToNativePath(p.key)
       config.arguments = cmdLineRest(p)
       result = true
+    else:
+      result = false
   inc argsCount
diff --git a/compiler/compiler.nimble b/compiler/compiler.nimble
new file mode 100644
index 000000000..ef3f343e1
--- /dev/null
+++ b/compiler/compiler.nimble
@@ -0,0 +1,28 @@
+include "../lib/system/compilation.nim"
+version = $NimMajor & "." & $NimMinor & "." & $NimPatch
+author = "Andreas Rumpf"
+description = "Compiler package providing the compiler sources as a library."
+license = "MIT"
+skipDirs = @["."]
+installDirs = @["compiler"]
+
+import os
+
+var compilerDir = ""
+
+before install:
+  rmDir("compiler")
+
+  let
+    files = listFiles(".")
+    dirs = listDirs(".")
+
+  mkDir("compiler")
+
+  for f in files:
+    cpFile(f, "compiler" / f)
+
+  for d in dirs:
+    cpDir(d, "compiler" / d)
+
+requires "nim"
diff --git a/compiler/concepts.nim b/compiler/concepts.nim
new file mode 100644
index 000000000..d48bacdc5
--- /dev/null
+++ b/compiler/concepts.nim
@@ -0,0 +1,343 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## New styled concepts for Nim. See https://github.com/nim-lang/RFCs/issues/168
+## for details. Note this is a first implementation and only the "Concept matching"
+## section has been implemented.
+
+import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types
+
+import std/intsets
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+const
+  logBindings = false
+
+## Code dealing with Concept declarations
+## --------------------------------------
+
+proc declareSelf(c: PContext; info: TLineInfo) =
+  ## Adds the magical 'Self' symbols to the current scope.
+  let ow = getCurrOwner(c)
+  let s = newSym(skType, getIdent(c.cache, "Self"), c.idgen, ow, info)
+  s.typ = newType(tyTypeDesc, c.idgen, ow)
+  s.typ.flags.incl {tfUnresolved, tfPacked}
+  s.typ.add newType(tyEmpty, c.idgen, ow)
+  addDecl(c, s, info)
+
+proc semConceptDecl(c: PContext; n: PNode): PNode =
+  ## Recursive helper for semantic checking for the concept declaration.
+  ## Currently we only support (possibly empty) lists of statements
+  ## containing 'proc' declarations and the like.
+  case n.kind
+  of nkStmtList, nkStmtListExpr:
+    result = shallowCopy(n)
+    for i in 0..<n.len:
+      result[i] = semConceptDecl(c, n[i])
+  of nkProcDef..nkIteratorDef, nkFuncDef:
+    result = c.semExpr(c, n, {efWantStmt})
+  of nkTypeClassTy:
+    result = shallowCopy(n)
+    for i in 0..<n.len-1:
+      result[i] = n[i]
+    result[^1] = semConceptDecl(c, n[^1])
+  of nkCommentStmt:
+    result = n
+  else:
+    localError(c.config, n.info, "unexpected construct in the new-styled concept: " & renderTree(n))
+    result = n
+
+proc semConceptDeclaration*(c: PContext; n: PNode): PNode =
+  ## Semantic checking for the concept declaration. Runs
+  ## when we process the concept itself, not its matching process.
+  assert n.kind == nkTypeClassTy
+  inc c.inConceptDecl
+  openScope(c)
+  declareSelf(c, n.info)
+  result = semConceptDecl(c, n)
+  rawCloseScope(c)
+  dec c.inConceptDecl
+
+## Concept matching
+## ----------------
+
+type
+  MatchCon = object ## Context we pass around during concept matching.
+    inferred: seq[(PType, PType)] ## we need a seq here so that we can easily undo inferences \
+      ## that turned out to be wrong.
+    marker: IntSet ## Some protection against wild runaway recursions.
+    potentialImplementation: PType ## the concrete type that might match the concept we try to match.
+    magic: TMagic  ## mArrGet and mArrPut is wrong in system.nim and
+                   ## cannot be fixed that easily.
+                   ## Thus we special case it here.
+
+proc existingBinding(m: MatchCon; key: PType): PType =
+  ## checks if we bound the type variable 'key' already to some
+  ## concrete type.
+  for i in 0..<m.inferred.len:
+    if m.inferred[i][0] == key: return m.inferred[i][1]
+  return nil
+
+proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
+
+proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
+  ## The heart of the concept matching process. 'f' is the formal parameter of some
+  ## routine inside the concept that we're looking for. 'a' is the formal parameter
+  ## of a routine that might match.
+  const
+    ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyGenericInst, tyAlias, tyInferred}
+  case f.kind
+  of tyAlias:
+    result = matchType(c, f.skipModifier, a, m)
+  of tyTypeDesc:
+    if isSelf(f):
+      #let oldLen = m.inferred.len
+      result = matchType(c, a, m.potentialImplementation, m)
+      #echo "self is? ", result, " ", a.kind, " ", a, " ", m.potentialImplementation, " ", m.potentialImplementation.kind
+      #m.inferred.setLen oldLen
+      #echo "A for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
+    else:
+      if a.kind == tyTypeDesc and f.hasElementType == a.hasElementType:
+        if f.hasElementType:
+          result = matchType(c, f.elementType, a.elementType, m)
+        else:
+          result = true # both lack it
+      else:
+        result = false
+
+  of tyGenericInvocation:
+    result = false
+    if a.kind == tyGenericInst and a.genericHead.kind == tyGenericBody:
+      if sameType(f.genericHead, a.genericHead) and f.kidsLen == a.kidsLen-1:
+        for i in FirstGenericParamAt ..< f.kidsLen:
+          if not matchType(c, f[i], a[i], m): return false
+        return true
+  of tyGenericParam:
+    let ak = a.skipTypes({tyVar, tySink, tyLent, tyOwned})
+    if ak.kind in {tyTypeDesc, tyStatic} and not isSelf(ak):
+      result = false
+    else:
+      let old = existingBinding(m, f)
+      if old == nil:
+        if f.hasElementType and f.elementType.kind != tyNone:
+          # also check the generic's constraints:
+          let oldLen = m.inferred.len
+          result = matchType(c, f.elementType, a, m)
+          m.inferred.setLen oldLen
+          if result:
+            when logBindings: echo "A adding ", f, " ", ak
+            m.inferred.add((f, ak))
+        elif m.magic == mArrGet and ak.kind in {tyArray, tyOpenArray, tySequence, tyVarargs, tyCstring, tyString}:
+          when logBindings: echo "B adding ", f, " ", lastSon ak
+          m.inferred.add((f, last ak))
+          result = true
+        else:
+          when logBindings: echo "C adding ", f, " ", ak
+          m.inferred.add((f, ak))
+          #echo "binding ", typeToString(ak), " to ", typeToString(f)
+          result = true
+      elif not m.marker.containsOrIncl(old.id):
+        result = matchType(c, old, ak, m)
+        if m.magic == mArrPut and ak.kind == tyGenericParam:
+          result = true
+      else:
+        result = false
+    #echo "B for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
+
+  of tyVar, tySink, tyLent, tyOwned:
+    # modifiers in the concept must be there in the actual implementation
+    # too but not vice versa.
+    if a.kind == f.kind:
+      result = matchType(c, f.elementType, a.elementType, m)
+    elif m.magic == mArrPut:
+      result = matchType(c, f.elementType, a, m)
+    else:
+      result = false
+  of tyEnum, tyObject, tyDistinct:
+    result = sameType(f, a)
+  of tyEmpty, tyString, tyCstring, tyPointer, tyNil, tyUntyped, tyTyped, tyVoid:
+    result = a.skipTypes(ignorableForArgType).kind == f.kind
+  of tyBool, tyChar, tyInt..tyUInt64:
+    let ak = a.skipTypes(ignorableForArgType)
+    result = ak.kind == f.kind or ak.kind == tyOrdinal or
+       (ak.kind == tyGenericParam and ak.hasElementType and ak.elementType.kind == tyOrdinal)
+  of tyConcept:
+    let oldLen = m.inferred.len
+    let oldPotentialImplementation = m.potentialImplementation
+    m.potentialImplementation = a
+    result = conceptMatchNode(c, f.n.lastSon, m)
+    m.potentialImplementation = oldPotentialImplementation
+    if not result:
+      m.inferred.setLen oldLen
+  of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr,
+     tyGenericInst:
+    # ^ XXX Rewrite this logic, it's more complex than it needs to be.
+    result = false
+    let ak = a.skipTypes(ignorableForArgType - {f.kind})
+    if ak.kind == f.kind and f.kidsLen == ak.kidsLen:
+      for i in 0..<ak.kidsLen:
+        if not matchType(c, f[i], ak[i], m): return false
+      return true
+  of tyOr:
+    let oldLen = m.inferred.len
+    if a.kind == tyOr:
+      # say the concept requires 'int|float|string' if the potentialImplementation
+      # says 'int|string' that is good enough.
+      var covered = 0
+      for ff in f.kids:
+        for aa in a.kids:
+          let oldLenB = m.inferred.len
+          let r = matchType(c, ff, aa, m)
+          if r:
+            inc covered
+            break
+          m.inferred.setLen oldLenB
+
+      result = covered >= a.kidsLen
+      if not result:
+        m.inferred.setLen oldLen
+    else:
+      result = false
+      for ff in f.kids:
+        result = matchType(c, ff, a, m)
+        if result: break # and remember the binding!
+        m.inferred.setLen oldLen
+  of tyNot:
+    if a.kind == tyNot:
+      result = matchType(c, f.elementType, a.elementType, m)
+    else:
+      let oldLen = m.inferred.len
+      result = not matchType(c, f.elementType, a, m)
+      m.inferred.setLen oldLen
+  of tyAnything:
+    result = true
+  of tyOrdinal:
+    result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
+  else:
+    result = false
+
+proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool =
+  ## Like 'matchType' but with extra logic dealing with proc return types
+  ## which can be nil or the 'void' type.
+  if f.isEmptyType:
+    result = a.isEmptyType
+  elif a == nil:
+    result = false
+  else:
+    result = matchType(c, f, a, m)
+
+proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
+  ## Checks if 'candidate' matches 'n' from the concept body. 'n' is a nkProcDef
+  ## or similar.
+
+  # watch out: only add bindings after a completely successful match.
+  let oldLen = m.inferred.len
+
+  let can = candidate.typ.n
+  let con = n[0].sym.typ.n
+
+  if can.len < con.len:
+    # too few arguments, cannot be a match:
+    return false
+
+  let common = min(can.len, con.len)
+  for i in 1 ..< common:
+    if not matchType(c, con[i].typ, can[i].typ, m):
+      m.inferred.setLen oldLen
+      return false
+
+  if not matchReturnType(c, n[0].sym.typ.returnType, candidate.typ.returnType, m):
+    m.inferred.setLen oldLen
+    return false
+
+  # all other parameters have to be optional parameters:
+  for i in common ..< can.len:
+    assert can[i].kind == nkSym
+    if can[i].sym.ast == nil:
+      # has too many arguments one of which is not optional:
+      m.inferred.setLen oldLen
+      return false
+
+  return true
+
+proc matchSyms(c: PContext, n: PNode; kinds: set[TSymKind]; m: var MatchCon): bool =
+  ## Walk the current scope, extract candidates which the same name as 'n[namePos]',
+  ## 'n' is the nkProcDef or similar from the concept that we try to match.
+  let candidates = searchInScopesAllCandidatesFilterBy(c, n[namePos].sym.name, kinds)
+  for candidate in candidates:
+    #echo "considering ", typeToString(candidate.typ), " ", candidate.magic
+    m.magic = candidate.magic
+    if matchSym(c, candidate, n, m): return true
+  result = false
+
+proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
+  ## Traverse the concept's AST ('n') and see if every declaration inside 'n'
+  ## can be matched with the current scope.
+  case n.kind
+  of nkStmtList, nkStmtListExpr:
+    for i in 0..<n.len:
+      if not conceptMatchNode(c, n[i], m):
+        return false
+    return true
+  of nkProcDef, nkFuncDef:
+    # procs match any of: proc, template, macro, func, method, converter.
+    # The others are more specific.
+    # XXX: Enforce .noSideEffect for 'nkFuncDef'? But then what are the use cases...
+    const filter = {skProc, skTemplate, skMacro, skFunc, skMethod, skConverter}
+    result = matchSyms(c, n, filter, m)
+  of nkTemplateDef:
+    result = matchSyms(c, n, {skTemplate}, m)
+  of nkMacroDef:
+    result = matchSyms(c, n, {skMacro}, m)
+  of nkConverterDef:
+    result = matchSyms(c, n, {skConverter}, m)
+  of nkMethodDef:
+    result = matchSyms(c, n, {skMethod}, m)
+  of nkIteratorDef:
+    result = matchSyms(c, n, {skIterator}, m)
+  of nkCommentStmt:
+    result = true
+  else:
+    # error was reported earlier.
+    result = false
+
+proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TypeMapping; invocation: PType): bool =
+  ## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but
+  ## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fulfill the
+  ## concept's requirements. If so, we return true and fill the 'bindings' with pairs of
+  ## (typeVar, instance) pairs. ('typeVar' is usually simply written as a generic 'T'.)
+  ## 'invocation' can be nil for atomic concepts. For non-atomic concepts, it contains the
+  ## `C[S, T]` parent type that we look for. We need this because we need to store bindings
+  ## for 'S' and 'T' inside 'bindings' on a successful match. It is very important that
+  ## we do not add any bindings at all on an unsuccessful match!
+  var m = MatchCon(inferred: @[], potentialImplementation: arg)
+  result = conceptMatchNode(c, concpt.n.lastSon, m)
+  if result:
+    for (a, b) in m.inferred:
+      if b.kind == tyGenericParam:
+        var dest = b
+        while true:
+          dest = existingBinding(m, dest)
+          if dest == nil or dest.kind != tyGenericParam: break
+        if dest != nil:
+          bindings.idTablePut(a, dest)
+          when logBindings: echo "A bind ", a, " ", dest
+      else:
+        bindings.idTablePut(a, b)
+        when logBindings: echo "B bind ", a, " ", b
+    # we have a match, so bind 'arg' itself to 'concpt':
+    bindings.idTablePut(concpt, arg)
+    # invocation != nil means we have a non-atomic concept:
+    if invocation != nil and arg.kind == tyGenericInst and invocation.kidsLen == arg.kidsLen-1:
+      # bind even more generic parameters
+      assert invocation.kind == tyGenericInvocation
+      for i in FirstGenericParamAt ..< invocation.kidsLen:
+        bindings.idTablePut(invocation[i], arg[i])
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index 56587a4fa..5043fc5d4 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -10,64 +10,162 @@
 # This module handles the conditional symbols.
 
 import
-  strtabs, platform, strutils, idents
+  std/strtabs
 
-const
-  catNone = "false"
+from options import Feature
+from lineinfos import hintMin, hintMax, warnMin, warnMax
 
 proc defineSymbol*(symbols: StringTableRef; symbol: string, value: string = "true") =
   symbols[symbol] = value
 
 proc undefSymbol*(symbols: StringTableRef; symbol: string) =
-  symbols[symbol] = catNone
+  symbols.del(symbol)
 
 #proc lookupSymbol*(symbols: StringTableRef; symbol: string): string =
 #  result = if isDefined(symbol): gSymbols[symbol] else: nil
 
 iterator definedSymbolNames*(symbols: StringTableRef): string =
-  for key, val in pairs(symbols):
-    if val != catNone: yield key
+  for key in keys(symbols):
+    yield key
 
 proc countDefinedSymbols*(symbols: StringTableRef): int =
-  result = 0
-  for key, val in pairs(symbols):
-    if val != catNone: inc(result)
+  symbols.len
 
 proc initDefines*(symbols: StringTableRef) =
   # for bootstrapping purposes and old code:
   template defineSymbol(s) = symbols.defineSymbol(s)
-  defineSymbol("nimhygiene")
-  defineSymbol("niminheritable")
-  defineSymbol("nimmixin")
-  defineSymbol("nimeffects")
-  defineSymbol("nimbabel")
-  defineSymbol("nimcomputedgoto")
-  defineSymbol("nimunion")
-  defineSymbol("nimnewshared")
-  defineSymbol("nimrequiresnimframe")
-  defineSymbol("nimparsebiggestfloatmagic")
-  defineSymbol("nimalias")
-  defineSymbol("nimlocks")
-  defineSymbol("nimnode")
-  defineSymbol("nimnomagic64")
-  defineSymbol("nimvarargstyped")
-  defineSymbol("nimtypedescfixed")
-  defineSymbol("nimKnowsNimvm")
-  defineSymbol("nimArrIdx")
-  defineSymbol("nimImmediateDeprecated")
-  defineSymbol("nimNewShiftOps")
-  defineSymbol("nimDistros")
-  defineSymbol("nimHasCppDefine")
-  defineSymbol("nimGenericInOutFlags")
-  when false: defineSymbol("nimHasOpt")
-  defineSymbol("nimNoArrayToCstringConversion")
-  defineSymbol("nimNewRoof")
-  defineSymbol("nimHasRunnableExamples")
-  defineSymbol("nimNewDot")
-  defineSymbol("nimHasNilChecks")
-  defineSymbol("nimSymKind")
-  defineSymbol("nimVmEqIdent")
-  defineSymbol("nimNoNil")
-  defineSymbol("nimNoZeroTerminator")
-  defineSymbol("nimNotNil")
-  defineSymbol("nimVmExportFixed")
+  defineSymbol("nimhygiene") # deadcode
+  defineSymbol("niminheritable") # deadcode
+  defineSymbol("nimmixin") # deadcode
+  defineSymbol("nimeffects") # deadcode
+  defineSymbol("nimbabel") # deadcode
+  defineSymbol("nimcomputedgoto") # deadcode
+  defineSymbol("nimunion") # deadcode
+  defineSymbol("nimnewshared") # deadcode
+  defineSymbol("nimNewTypedesc") # deadcode
+  defineSymbol("nimrequiresnimframe") # deadcode
+  defineSymbol("nimparsebiggestfloatmagic") # deadcode
+  defineSymbol("nimlocks") # deadcode
+  defineSymbol("nimnode") # deadcode
+  defineSymbol("nimvarargstyped") # deadcode
+  defineSymbol("nimtypedescfixed") # deadcode
+  defineSymbol("nimKnowsNimvm") # deadcode
+  defineSymbol("nimArrIdx") # deadcode
+  defineSymbol("nimHasalignOf") # deadcode
+  defineSymbol("nimDistros") # deadcode
+  defineSymbol("nimHasCppDefine") # deadcode
+  defineSymbol("nimGenericInOutFlags") # deadcode
+  when false: defineSymbol("nimHasOpt") # deadcode
+  defineSymbol("nimNoArrayToCstringConversion") # deadcode
+  defineSymbol("nimHasRunnableExamples") # deadcode
+  defineSymbol("nimNewDot") # deadcode
+  defineSymbol("nimHasNilChecks") # deadcode
+  defineSymbol("nimSymKind") # deadcode
+  defineSymbol("nimVmEqIdent") # deadcode
+  defineSymbol("nimNoNil") # deadcode
+  defineSymbol("nimNoZeroTerminator") # deadcode
+  defineSymbol("nimNotNil") # deadcode
+  defineSymbol("nimVmExportFixed") # deadcode
+  defineSymbol("nimHasSymOwnerInMacro") # deadcode
+  defineSymbol("nimNewRuntime") # deadcode
+  defineSymbol("nimIncrSeqV3") # deadcode
+  defineSymbol("nimAshr") # deadcode
+  defineSymbol("nimNoNilSeqs") # deadcode
+  defineSymbol("nimNoNilSeqs2") # deadcode
+  defineSymbol("nimHasUserErrors") # deadcode
+  defineSymbol("nimUncheckedArrayTyp") # deadcode
+  defineSymbol("nimHasTypeof") # deadcode
+  defineSymbol("nimErrorProcCanHaveBody") # deadcode
+  defineSymbol("nimHasInstantiationOfInMacro") # deadcode
+  defineSymbol("nimHasHotCodeReloading") # deadcode
+  defineSymbol("nimHasNilSeqs") # deadcode
+  defineSymbol("nimHasSignatureHashInMacro") # deadcode
+  defineSymbol("nimHasDefault") # deadcode
+  defineSymbol("nimMacrosSizealignof") # deadcode
+  defineSymbol("nimNoZeroExtendMagic") # deadcode
+  defineSymbol("nimMacrosGetNodeId") # deadcode
+  defineSymbol("nimFixedForwardGeneric") # deadcode
+  defineSymbol("nimToOpenArrayCString") # deadcode
+  defineSymbol("nimHasUsed") # deadcode
+  defineSymbol("nimnomagic64") # deadcode
+  defineSymbol("nimNewShiftOps") # deadcode
+  defineSymbol("nimHasCursor") # deadcode
+  defineSymbol("nimAlignPragma") # deadcode
+  defineSymbol("nimHasExceptionsQuery") # deadcode
+  defineSymbol("nimHasIsNamedTuple") # deadcode
+  defineSymbol("nimHashOrdinalFixed") # deadcode
+  defineSymbol("nimHasSinkInference") # deadcode
+  defineSymbol("nimNewIntegerOps") # deadcode
+  defineSymbol("nimHasInvariant") # deadcode
+
+
+
+  for f in Feature:
+    defineSymbol("nimHas" & $f)
+
+  for s in warnMin..warnMax:
+    defineSymbol("nimHasWarning" & $s)
+  for s in hintMin..hintMax:
+    defineSymbol("nimHasHint" & $s)
+
+  defineSymbol("nimFixedOwned")
+  defineSymbol("nimHasStyleChecks")
+
+  when defined(nimHasLibFFI):
+    # Renaming as we can't conflate input vs output define flags; e.g. this
+    # will report the right thing regardless of whether user adds
+    # `-d:nimHasLibFFI` in his user config.
+    defineSymbol("nimHasLibFFIEnabled") # deadcode
+
+  defineSymbol("nimHasStacktraceMsgs") # deadcode
+  defineSymbol("nimDoesntTrackDefects")
+  defineSymbol("nimHasLentIterators") # deadcode
+  defineSymbol("nimHasDeclaredMagic") # deadcode
+  defineSymbol("nimHasStacktracesModule") # deadcode
+  defineSymbol("nimHasEffectTraitsModule")
+  defineSymbol("nimHasCastPragmaBlocks")
+  defineSymbol("nimHasDeclaredLocs")
+  defineSymbol("nimHasJsBigIntBackend")
+  defineSymbol("nimHasWarningAsError")
+  defineSymbol("nimHasHintAsError")
+  defineSymbol("nimHasSpellSuggest")
+  defineSymbol("nimHasCustomLiterals")
+  defineSymbol("nimHasUnifiedTuple")
+  defineSymbol("nimHasIterable")
+  defineSymbol("nimHasTypeofVoid") # deadcode
+  defineSymbol("nimHasDragonBox") # deadcode
+  defineSymbol("nimHasHintAll")
+  defineSymbol("nimHasTrace")
+  defineSymbol("nimHasEffectsOf")
+
+  defineSymbol("nimHasEnforceNoRaises")
+  defineSymbol("nimHasTopDownInference")
+  defineSymbol("nimHasTemplateRedefinitionPragma")
+  defineSymbol("nimHasCstringCase")
+  defineSymbol("nimHasCallsitePragma")
+
+  defineSymbol("nimHasWarnCastSizes") # deadcode
+  defineSymbol("nimHasOutParams")
+  defineSymbol("nimHasSystemRaisesDefect")
+  defineSymbol("nimHasWarnUnnamedBreak")
+  defineSymbol("nimHasGenericDefine")
+  defineSymbol("nimHasDefineAliases")
+  defineSymbol("nimHasWarnBareExcept")
+  defineSymbol("nimHasDup")
+  defineSymbol("nimHasChecksums")
+  defineSymbol("nimHasSendable")
+  defineSymbol("nimAllowNonVarDestructor")
+  defineSymbol("nimHasQuirky")
+  defineSymbol("nimHasEnsureMove")
+  defineSymbol("nimHasNoReturnError")
+
+  defineSymbol("nimUseStrictDefs")
+  defineSymbol("nimHasNolineTooLong")
+
+  defineSymbol("nimHasCastExtendedVm")
+  defineSymbol("nimHasWarnStdPrefix")
+
+  defineSymbol("nimHasVtables")
+  defineSymbol("nimHasGenericsOpenSym2")
+  defineSymbol("nimHasGenericsOpenSym3")
+  defineSymbol("nimHasJsNoLambdaLifting")
diff --git a/compiler/configuration.nim b/compiler/configuration.nim
deleted file mode 100644
index 22e0b834e..000000000
--- a/compiler/configuration.nim
+++ /dev/null
@@ -1,6 +0,0 @@
-## Use the module 'lineinfos' instead!
-
-{.deprecated.}
-
-import lineinfos
-export lineinfos
diff --git a/compiler/debugutils.nim b/compiler/debugutils.nim
new file mode 100644
index 000000000..adbb0517f
--- /dev/null
+++ b/compiler/debugutils.nim
@@ -0,0 +1,72 @@
+##[
+Utilities to help with debugging nim compiler.
+
+Experimental API, subject to change.
+]##
+
+#[
+## example
+useful debugging flags:
+--stacktrace -d:debug -d:nimDebugUtils
+ nim c -o:bin/nim_temp --stacktrace -d:debug -d:nimDebugUtils compiler/nim
+
+## future work
+* expose and improve astalgo.debug, replacing it by std/prettyprints,
+  refs https://github.com/nim-lang/RFCs/issues/385
+]#
+
+import options
+import std/wrapnils
+export wrapnils
+  # allows using things like: `?.n.sym.typ.len`
+
+import std/stackframes
+export stackframes
+  # allows using things like: `setFrameMsg c.config$n.info & " " & $n.kind`
+  # which doesn't log, but augments stacktrace with side channel information
+
+var conf0: ConfigRef
+
+proc onNewConfigRef*(conf: ConfigRef) {.inline.} =
+  ## Caches `conf`, which can be retrieved with `getConfigRef`.
+  ## This avoids having to forward `conf` all the way down the call chain to
+  ## procs that need it during a debugging session.
+  conf0 = conf
+
+proc getConfigRef*(): ConfigRef =
+  ## nil, if -d:nimDebugUtils wasn't specified
+  result = conf0
+
+proc isCompilerDebug*(): bool =
+  ##[
+  Provides a simple way for user code to enable/disable logging in the compiler
+  in a granular way. This can then be used in the compiler as follows:
+  ```nim
+  if isCompilerDebug():
+    echo ?.n.sym.typ.len
+  ```
+  ]##
+  runnableExamples:
+    proc main =
+      echo 2
+      {.define(nimCompilerDebug).}
+      echo 3.5 # code section in which `isCompilerDebug` will be true
+      {.undef(nimCompilerDebug).}
+      echo 'x'
+  conf0.isDefined("nimCompilerDebug")
+
+proc enteringDebugSection*() {.exportc, dynlib.} =
+  ## Provides a way for native debuggers to enable breakpoints, watchpoints, etc
+  ## when code of interest is being compiled.
+  ## 
+  ## Set your debugger to break on entering `nimCompilerIsEnteringDebugSection`
+  ## and then execute a desired command.
+  discard
+
+proc exitingDebugSection*() {.exportc, dynlib.} =
+  ## Provides a way for native debuggers to disable breakpoints, watchpoints, etc
+  ## when code of interest is no longer being compiled.
+  ## 
+  ## Set your debugger to break on entering `exitingDebugSection`
+  ## and then execute a desired command.
+  discard
diff --git a/compiler/depends.nim b/compiler/depends.nim
index d0a1139ef..638f1eb51 100644
--- a/compiler/depends.nim
+++ b/compiler/depends.nim
@@ -9,13 +9,20 @@
 
 # This module implements a dependency file generator.
 
-import
-  os, options, ast, astalgo, msgs, ropes, idents, passes, modulepaths
+import options, ast, ropes, pathutils, msgs, lineinfos
+
+import modulegraphs
+
+import std/[os, parseutils]
+import std/strutils except addf
+import std/private/globs
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
-from modulegraphs import ModuleGraph
 
 type
-  TGen = object of TPassContext
+  TGen = object of PPassContext
     module: PSym
     config: ConfigRef
     graph: ModuleGraph
@@ -25,41 +32,79 @@ type
     dotGraph: Rope
 
 proc addDependencyAux(b: Backend; importing, imported: string) =
-  addf(b.dotGraph, "$1 -> \"$2\";$n", [rope(importing), rope(imported)])
+  b.dotGraph.addf("\"$1\" -> \"$2\";$n", [rope(importing), rope(imported)])
   # s1 -> s2_4[label="[0-9]"];
 
-proc addDotDependency(c: PPassContext, n: PNode): PNode =
+proc toNimblePath(s: string, isStdlib: bool): string =
+  const stdPrefix = "std/"
+  const pkgPrefix = "pkg/"
+  if isStdlib:
+    let sub = "lib/"
+    var start = s.find(sub)
+    if start < 0:
+      raiseAssert "unreachable"
+    else:
+      start += sub.len
+      let base = s[start..^1]
+
+      if base.startsWith("system") or base.startsWith("std"):
+        result = base
+      else:
+        for dir in stdlibDirs:
+          if base.startsWith(dir):
+            return stdPrefix & base.splitFile.name
+
+        result = stdPrefix & base
+  else:
+    var sub = getEnv("NIMBLE_DIR")
+    if sub.len == 0:
+      sub = ".nimble/pkgs/"
+    else:
+      sub.add "/pkgs/"
+    var start = s.find(sub)
+    if start < 0:
+      sub[^1] = '2'
+      sub.add '/'
+      start = s.find(sub) # /pkgs2
+      if start < 0:
+        return s
+
+    start += sub.len
+    start += skipUntil(s, '/', start)
+    start += 1
+    result = pkgPrefix & s[start..^1]
+
+proc addDependency(c: PPassContext, g: PGen, b: Backend, n: PNode) =
+  doAssert n.kind == nkSym, $n.kind
+
+  let path = splitFile(toProjPath(g.config, n.sym.position.FileIndex))
+  let modulePath = splitFile(toProjPath(g.config, g.module.position.FileIndex))
+  let parent = nativeToUnixPath(modulePath.dir / modulePath.name).toNimblePath(belongsToStdlib(g.graph, g.module))
+  let child = nativeToUnixPath(path.dir / path.name).toNimblePath(belongsToStdlib(g.graph, n.sym))
+  addDependencyAux(b, parent, child)
+
+proc addDotDependency*(c: PPassContext, n: PNode): PNode =
   result = n
   let g = PGen(c)
   let b = Backend(g.graph.backend)
   case n.kind
   of nkImportStmt:
-    for i in countup(0, sonsLen(n) - 1):
-      var imported = getModuleName(g.config, n.sons[i])
-      addDependencyAux(b, g.module.name.s, imported)
+    for i in 0..<n.len:
+      addDependency(c, g, b, n[i])
   of nkFromStmt, nkImportExceptStmt:
-    var imported = getModuleName(g.config, n.sons[0])
-    addDependencyAux(b, g.module.name.s, imported)
+    addDependency(c, g, b, n[0])
   of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr:
-    for i in countup(0, sonsLen(n) - 1): discard addDotDependency(c, n.sons[i])
+    for i in 0..<n.len: discard addDotDependency(c, n[i])
   else:
     discard
 
-proc generateDot*(graph: ModuleGraph; project: string) =
+proc generateDot*(graph: ModuleGraph; project: AbsoluteFile) =
   let b = Backend(graph.backend)
   discard writeRope("digraph $1 {$n$2}$n" % [
-      rope(changeFileExt(extractFilename(project), "")), b.dotGraph],
+      rope(project.splitFile.name), b.dotGraph],
             changeFileExt(project, "dot"))
 
-proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
-  var g: PGen
-  new(g)
-  g.module = module
-  g.config = graph.config
-  g.graph = graph
+proc setupDependPass*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
+  result = PGen(module: module, config: graph.config, graph: graph)
   if graph.backend == nil:
-    graph.backend = Backend(dotGraph: nil)
-  result = g
-
-const gendependPass* = makePass(open = myOpen, process = addDotDependency)
-
+    graph.backend = Backend(dotGraph: "")
diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim
deleted file mode 100644
index 0395728c2..000000000
--- a/compiler/destroyer.nim
+++ /dev/null
@@ -1,465 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2017 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Injects destructor calls into Nim code as well as
-## an optimizer that optimizes copies to moves. This is implemented as an
-## AST to AST transformation so that every backend benefits from it.
-
-## Rules for destructor injections:
-##
-## foo(bar(X(), Y()))
-## X and Y get destroyed after bar completes:
-##
-## foo( (tmpX = X(); tmpY = Y(); tmpBar = bar(tmpX, tmpY);
-##       destroy(tmpX); destroy(tmpY);
-##       tmpBar))
-## destroy(tmpBar)
-##
-## var x = f()
-## body
-##
-## is the same as:
-##
-##  var x;
-##  try:
-##    move(x, f())
-##  finally:
-##    destroy(x)
-##
-## But this really just an optimization that tries to avoid to
-## introduce too many temporaries, the 'destroy' is caused by
-## the 'f()' call. No! That is not true for 'result = f()'!
-##
-## x = y where y is read only once
-## is the same as:  move(x, y)
-##
-## Actually the more general rule is: The *last* read of ``y``
-## can become a move if ``y`` is the result of a construction.
-##
-## We also need to keep in mind here that the number of reads is
-## control flow dependent:
-## let x = foo()
-## while true:
-##   y = x  # only one read, but the 2nd iteration will fail!
-## This also affects recursions! Only usages that do not cross
-## a loop boundary (scope) and are not used in function calls
-## are safe.
-##
-##
-## x = f() is the same as:  move(x, f())
-##
-## x = y
-## is the same as:  copy(x, y)
-##
-## Reassignment works under this scheme:
-## var x = f()
-## x = y
-##
-## is the same as:
-##
-##  var x;
-##  try:
-##    move(x, f())
-##    copy(x, y)
-##  finally:
-##    destroy(x)
-##
-##  result = f()  must not destroy 'result'!
-##
-## The produced temporaries clutter up the code and might lead to
-## inefficiencies. A better strategy is to collect all the temporaries
-## in a single object that we put into a single try-finally that
-## surrounds the proc body. This means the code stays quite efficient
-## when compiled to C. In fact, we do the same for variables, so
-## destructors are called when the proc returns, not at scope exit!
-## This makes certains idioms easier to support. (Taking the slice
-## of a temporary object.)
-##
-## foo(bar(X(), Y()))
-## X and Y get destroyed after bar completes:
-##
-## var tmp: object
-## foo( (move tmp.x, X(); move tmp.y, Y(); tmp.bar = bar(tmpX, tmpY);
-##       tmp.bar))
-## destroy(tmp.bar)
-## destroy(tmp.x); destroy(tmp.y)
-##
-
-##[
-From https://github.com/nim-lang/Nim/wiki/Destructors
-
-Rule      Pattern                 Transformed into
-----      -------                 ----------------
-1.1	      var x: T; stmts	        var x: T; try stmts
-                                  finally: `=destroy`(x)
-1.2       var x: sink T; stmts    var x: sink T; stmts; ensureEmpty(x)
-2         x = f()                 `=sink`(x, f())
-3         x = lastReadOf z        `=sink`(x, z)
-4.1       y = sinkParam           `=sink`(y, sinkParam)
-4.2       x = y                   `=`(x, y) # a copy
-5.1       f_sink(g())             f_sink(g())
-5.2       f_sink(y)               f_sink(copy y); # copy unless we can see it's the last read
-5.3       f_sink(move y)          f_sink(y); reset(y) # explicit moves empties 'y'
-5.4       f_noSink(g())           var tmp = bitwiseCopy(g()); f(tmp); `=destroy`(tmp)
-
-Remarks: Rule 1.2 is not yet implemented because ``sink`` is currently
-  not allowed as a local variable.
-
-``move`` builtin needs to be implemented.
-]##
-
-import
-  intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
-  strutils, options, dfa, lowerings, tables, modulegraphs,
-  lineinfos
-
-const
-  InterestingSyms = {skVar, skResult, skLet}
-
-type
-  Con = object
-    owner: PSym
-    g: ControlFlowGraph
-    jumpTargets: IntSet
-    tmpObj: PType
-    tmp: PSym
-    destroys, topLevelVars: PNode
-    toDropBit: Table[int, PSym]
-    graph: ModuleGraph
-    emptyNode: PNode
-
-proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode =
-  # XXX why are temps fields in an object here?
-  let f = newSym(skField, getIdent(c.graph.cache, ":d" & $c.tmpObj.n.len), c.owner, info)
-  f.typ = typ
-  rawAddField c.tmpObj, f
-  result = rawDirectAccess(c.tmp, f)
-
-proc isHarmlessVar*(s: PSym; c: Con): bool =
-  # 's' is harmless if it used only once and its
-  # definition/usage are not split by any labels:
-  #
-  # let s = foo()
-  # while true:
-  #   a[i] = s
-  #
-  # produces:
-  #
-  # def s
-  # L1:
-  #   use s
-  # goto L1
-  #
-  # let s = foo()
-  # if cond:
-  #   a[i] = s
-  # else:
-  #   a[j] = s
-  #
-  # produces:
-  #
-  # def s
-  # fork L2
-  # use s
-  # goto L3
-  # L2:
-  # use s
-  # L3
-  #
-  # So this analysis is for now overly conservative, but correct.
-  var defsite = -1
-  var usages = 0
-  for i in 0..<c.g.len:
-    case c.g[i].kind
-    of def:
-      if c.g[i].sym == s:
-        if defsite < 0: defsite = i
-        else: return false
-    of use:
-      if c.g[i].sym == s:
-        if defsite < 0: return false
-        for j in defsite .. i:
-          # not within the same basic block?
-          if j in c.jumpTargets: return false
-        # if we want to die after the first 'use':
-        if usages > 1: return false
-        inc usages
-    of useWithinCall:
-      if c.g[i].sym == s: return false
-    of goto, fork:
-      discard "we do not perform an abstract interpretation yet"
-
-template interestingSym(s: PSym): bool =
-  s.owner == c.owner and s.kind in InterestingSyms and hasDestructor(s.typ)
-
-proc patchHead(n: PNode) =
-  if n.kind in nkCallKinds and n[0].kind == nkSym and n.len > 1:
-    let s = n[0].sym
-    if s.name.s[0] == '=' and s.name.s in ["=sink", "=", "=destroy"]:
-      if sfFromGeneric in s.flags:
-        excl(s.flags, sfFromGeneric)
-        patchHead(s.getBody)
-      if n[1].typ.isNil:
-        # XXX toptree crashes without this workaround. Figure out why.
-        return
-      let t = n[1].typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink, tyInferred})
-      template patch(op, field) =
-        if s.name.s == op and field != nil and field != s:
-          n.sons[0].sym = field
-      patch "=sink", t.sink
-      patch "=", t.assignment
-      patch "=destroy", t.destructor
-  for x in n:
-    patchHead(x)
-
-proc patchHead(s: PSym) =
-  if sfFromGeneric in s.flags:
-    patchHead(s.ast[bodyPos])
-
-template genOp(opr, opname) =
-  let op = opr
-  if op == nil:
-    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
-  result = newTree(nkCall, newSymNode(op), newTree(nkHiddenAddr, dest))
-
-proc genSink(c: Con; t: PType; dest: PNode): PNode =
-  let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
-  genOp(if t.sink != nil: t.sink else: t.assignment, "=sink")
-
-proc genCopy(c: Con; t: PType; dest: PNode): PNode =
-  let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
-  genOp(t.assignment, "=")
-
-proc genDestroy(c: Con; t: PType; dest: PNode): PNode =
-  let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
-  genOp(t.destructor, "=destroy")
-
-proc addTopVar(c: var Con; v: PNode) =
-  c.topLevelVars.add newTree(nkIdentDefs, v, c.emptyNode, c.emptyNode)
-
-proc dropBit(c: var Con; s: PSym): PSym =
-  result = c.toDropBit.getOrDefault(s.id)
-  assert result != nil
-
-proc registerDropBit(c: var Con; s: PSym) =
-  let result = newSym(skTemp, getIdent(c.graph.cache, s.name.s & "_AliveBit"), c.owner, s.info)
-  result.typ = getSysType(c.graph, s.info, tyBool)
-  let trueVal = newIntTypeNode(nkIntLit, 1, result.typ)
-  c.topLevelVars.add newTree(nkIdentDefs, newSymNode result, c.emptyNode, trueVal)
-  c.toDropBit[s.id] = result
-  # generate:
-  #  if not sinkParam_AliveBit: `=destroy`(sinkParam)
-  c.destroys.add newTree(nkIfStmt,
-    newTree(nkElifBranch, newSymNode result, genDestroy(c, s.typ, newSymNode s)))
-
-proc p(n: PNode; c: var Con): PNode
-
-template recurse(n, dest) =
-  for i in 0..<n.len:
-    dest.add p(n[i], c)
-
-proc isSinkParam(s: PSym): bool {.inline.} =
-  result = s.kind == skParam and s.typ.kind == tySink
-
-const constrExprs = nkCallKinds+{nkObjConstr}
-
-proc destructiveMoveSink(n: PNode; c: var Con): PNode =
-  # generate:  (chckMove(sinkParam_AliveBit); sinkParam_AliveBit = false; sinkParam)
-  result = newNodeIT(nkStmtListExpr, n.info, n.typ)
-  let bit = newSymNode dropBit(c, n.sym)
-  if optMoveCheck in c.owner.options:
-    result.add callCodegenProc(c.graph, "chckMove", bit)
-  result.add newTree(nkAsgn, bit,
-    newIntTypeNode(nkIntLit, 0, getSysType(c.graph, n.info, tyBool)))
-  result.add n
-
-proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
-  if ri.kind in constrExprs:
-    result = genSink(c, ri.typ, dest)
-    # watch out and no not transform 'ri' twice if it's a call:
-    let ri2 = copyNode(ri)
-    recurse(ri, ri2)
-    result.add ri2
-  elif ri.kind == nkSym and isHarmlessVar(ri.sym, c):
-    result = genSink(c, ri.typ, dest)
-    result.add p(ri, c)
-  elif ri.kind == nkSym and isSinkParam(ri.sym):
-    result = genSink(c, ri.typ, dest)
-    result.add destructiveMoveSink(ri, c)
-  else:
-    result = genCopy(c, ri.typ, dest)
-    result.add p(ri, c)
-
-proc passCopyToSink(n: PNode; c: var Con): PNode =
-  result = newNodeIT(nkStmtListExpr, n.info, n.typ)
-  let tmp = getTemp(c, n.typ, n.info)
-  if hasDestructor(n.typ):
-    var m = genCopy(c, n.typ, tmp)
-    m.add p(n, c)
-    result.add m
-    message(c.graph.config, n.info, hintPerformance,
-      "passing '$1' to a sink parameter introduces an implicit copy; " &
-      "use 'move($1)' to prevent it" % $n)
-  else:
-    result.add newTree(nkAsgn, tmp, p(n, c))
-  result.add tmp
-
-proc genReset(n: PNode; c: var Con): PNode =
-  result = newNodeI(nkCall, n.info)
-  result.add(newSymNode(createMagic(c.graph, "reset", mReset)))
-  # The mReset builtin does not take the address:
-  result.add n
-
-proc destructiveMoveVar(n: PNode; c: var Con): PNode =
-  # generate: (let tmp = v; reset(v); tmp)
-  result = newNodeIT(nkStmtListExpr, n.info, n.typ)
-
-  var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), c.owner, n.info)
-  var v = newNodeI(nkLetSection, n.info)
-  let tempAsNode = newSymNode(temp)
-
-  var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
-  vpart.sons[0] = tempAsNode
-  vpart.sons[1] = c.emptyNode
-  vpart.sons[2] = n
-  add(v, vpart)
-
-  result.add v
-  result.add genReset(n, c)
-  result.add tempAsNode
-
-proc p(n: PNode; c: var Con): PNode =
-  case n.kind
-  of nkVarSection, nkLetSection:
-    discard "transform; var x = y to  var x; x op y  where op is a move or copy"
-    result = newNodeI(nkStmtList, n.info)
-
-    for i in 0..<n.len:
-      let it = n[i]
-      let L = it.len-1
-      let ri = it[L]
-      if it.kind == nkVarTuple and hasDestructor(ri.typ):
-        let x = lowerTupleUnpacking(c.graph, it, c.owner)
-        result.add p(x, c)
-      elif it.kind == nkIdentDefs and hasDestructor(it[0].typ):
-        for j in 0..L-2:
-          let v = it[j]
-          doAssert v.kind == nkSym
-          # move the variable declaration to the top of the frame:
-          c.addTopVar v
-          # make sure it's destroyed at the end of the proc:
-          c.destroys.add genDestroy(c, v.typ, v)
-          if ri.kind != nkEmpty:
-            let r = moveOrCopy(v, ri, c)
-            result.add r
-      else:
-        # keep it, but transform 'ri':
-        var varSection = copyNode(n)
-        var itCopy = copyNode(it)
-        for j in 0..L-1:
-          itCopy.add it[j]
-        itCopy.add p(ri, c)
-        varSection.add itCopy
-        result.add varSection
-  of nkCallKinds:
-    let parameters = n[0].typ
-    let L = if parameters != nil: parameters.len else: 0
-    for i in 1 ..< n.len:
-      let arg = n[i]
-      if i < L and parameters[i].kind == tySink:
-        if arg.kind in nkCallKinds:
-          # recurse but skip the call expression in order to prevent
-          # destructor injections: Rule 5.1 is different from rule 5.4!
-          let a = copyNode(arg)
-          recurse(arg, a)
-          n.sons[i] = a
-        elif arg.kind in {nkObjConstr, nkCharLit..nkFloat128Lit}:
-          discard "object construction to sink parameter: nothing to do"
-        elif arg.kind == nkSym and isHarmlessVar(arg.sym, c):
-          # if x is a variable and it its last read we eliminate its
-          # destructor invokation, but don't. We need to reset its memory
-          # to disable its destructor which we have not elided:
-          n.sons[i] = destructiveMoveVar(arg, c)
-        elif arg.kind == nkSym and isSinkParam(arg.sym):
-          # mark the sink parameter as used:
-          n.sons[i] = destructiveMoveSink(arg, c)
-        else:
-          # an object that is not temporary but passed to a 'sink' parameter
-          # results in a copy.
-          n.sons[i] = passCopyToSink(arg, c)
-      else:
-        n.sons[i] = p(arg, c)
-
-    if n.typ != nil and hasDestructor(n.typ):
-      discard "produce temp creation"
-      result = newNodeIT(nkStmtListExpr, n.info, n.typ)
-      let tmp = getTemp(c, n.typ, n.info)
-      var sinkExpr = genSink(c, n.typ, tmp)
-      sinkExpr.add n
-      result.add sinkExpr
-      result.add tmp
-      c.destroys.add genDestroy(c, n.typ, tmp)
-    else:
-      result = n
-  of nkAsgn, nkFastAsgn:
-    if hasDestructor(n[0].typ):
-      result = moveOrCopy(n[0], n[1], c)
-    else:
-      result = copyNode(n)
-      recurse(n, result)
-  of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef,
-      nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
-    result = n
-  else:
-    result = copyNode(n)
-    recurse(n, result)
-
-proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
-  when defined(nimDebugDestroys):
-    echo "injecting into ", n
-  var c: Con
-  c.owner = owner
-  c.tmp = newSym(skTemp, getIdent(g.cache, ":d"), owner, n.info)
-  c.tmpObj = createObj(g, owner, n.info)
-  c.tmp.typ = c.tmpObj
-  c.destroys = newNodeI(nkStmtList, n.info)
-  c.topLevelVars = newNodeI(nkVarSection, n.info)
-  c.toDropBit = initTable[int, PSym]()
-  c.graph = g
-  c.emptyNode = newNodeI(nkEmpty, n.info)
-  let cfg = constructCfg(owner, n)
-  shallowCopy(c.g, cfg)
-  c.jumpTargets = initIntSet()
-  for i in 0..<c.g.len:
-    if c.g[i].kind in {goto, fork}:
-      c.jumpTargets.incl(i+c.g[i].dest)
-  if owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}:
-    let params = owner.typ.n
-    for i in 1 ..< params.len:
-      let param = params[i].sym
-      if param.typ.kind == tySink: registerDropBit(c, param)
-  let body = p(n, c)
-  if c.tmp.typ.n.len > 0:
-    c.addTopVar(newSymNode c.tmp)
-  result = newNodeI(nkStmtList, n.info)
-  if c.topLevelVars.len > 0:
-    result.add c.topLevelVars
-  if c.destroys.len > 0:
-    result.add newTryFinally(body, c.destroys)
-  else:
-    result.add body
-
-  when defined(nimDebugDestroys):
-    if owner.name.s == "main" or true:
-      echo "------------------------------------"
-      echo owner.name.s, " transformed to: "
-      echo result
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index 013242f62..5534d07e7 100644
--- a/compiler/dfa.nim
+++ b/compiler/dfa.nim
@@ -7,268 +7,385 @@
 #    distribution, for details about the copyright.
 #
 
-## Data flow analysis for Nim. For now the task is to prove that every
-## usage of a local variable 'v' is covered by an initialization to 'v'
-## first.
+## Data flow analysis for Nim.
 ## We transform the AST into a linear list of instructions first to
-## make this easier to handle: There are only 2 different branching
+## make this easier to handle: There are only 3 different branching
 ## instructions: 'goto X' is an unconditional goto, 'fork X'
 ## is a conditional goto (either the next instruction or 'X' can be
-## taken). Exhaustive case statements are translated
+## taken), 'loop X' is the only jump that jumps back.
+##
+## Exhaustive case statements are translated
 ## so that the last branch is transformed into an 'else' branch.
 ## ``return`` and ``break`` are all covered by 'goto'.
-## The case to detect is ``use v`` that is not dominated by
-## a ``def v``.
+##
 ## The data structures and algorithms used here are inspired by
 ## "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen.
 ## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf
 
-import ast, astalgo, types, intsets, tables, msgs, options, lineinfos
+import ast, lineinfos, renderer, aliasanalysis
+import std/private/asciitables
+import std/intsets
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 type
   InstrKind* = enum
-    goto, fork, def, use,
-    useWithinCall # this strange special case is used to get more
-                  # move optimizations out of regular code
-                  # XXX This is still overly pessimistic in
-                  # call(let x = foo; bar(x))
+    goto, loop, fork, def, use
   Instr* = object
-    n*: PNode
     case kind*: InstrKind
-    of def, use, useWithinCall: sym*: PSym
-    of goto, fork: dest*: int
+    of goto, fork, loop: dest*: int
+    of def, use:
+      n*: PNode # contains the def/use location.
 
   ControlFlowGraph* = seq[Instr]
 
   TPosition = distinct int
-  TBlock = object
-    label: PSym
-    fixups: seq[TPosition]
 
-  ValueKind = enum
-    undef, value, valueOrUndef
+  TBlock = object
+    case isTryBlock: bool
+    of false:
+      label: PSym
+      breakFixups: seq[(TPosition, seq[PNode])] # Contains the gotos for the breaks along with their pending finales
+    of true:
+      finale: PNode
+      raiseFixups: seq[TPosition] # Contains the gotos for the raises
 
   Con = object
     code: ControlFlowGraph
-    inCall: int
+    inTryStmt, interestingInstructions: int
     blocks: seq[TBlock]
+    owner: PSym
+    root: PSym
 
-proc debugInfo(info: TLineInfo): string =
-  result = $info.line #info.toFilename & ":" & $info.line
-
-proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) =
+proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
   # for debugging purposes
   # first iteration: compute all necessary labels:
+  result = ""
   var jumpTargets = initIntSet()
   let last = if last < 0: c.len-1 else: min(last, c.len-1)
   for i in start..last:
-    if c[i].kind in {goto, fork}:
+    if c[i].kind in {goto, fork, loop}:
       jumpTargets.incl(i+c[i].dest)
   var i = start
   while i <= last:
     if i in jumpTargets: result.add("L" & $i & ":\n")
     result.add "\t"
-    result.add $c[i].kind
+    result.add ($i & " " & $c[i].kind)
     result.add "\t"
     case c[i].kind
-    of def, use, useWithinCall:
-      result.add c[i].sym.name.s
-    of goto, fork:
+    of def, use:
+      result.add renderTree(c[i].n)
+      result.add("\t#")
+      result.add($c[i].n.info.line)
+      result.add("\n")
+    of goto, fork, loop:
       result.add "L"
-      result.add c[i].dest+i
-    result.add("\t#")
-    result.add(debugInfo(c[i].n.info))
-    result.add("\n")
+      result.addInt c[i].dest+i
     inc i
   if i in jumpTargets: result.add("L" & $i & ": End\n")
 
-
-proc echoCfg*(c: ControlFlowGraph; start=0; last = -1) {.deprecated.} =
+proc echoCfg*(c: ControlFlowGraph; start = 0; last = -1) {.deprecated.} =
   ## echos the ControlFlowGraph for debugging purposes.
-  var buf = ""
-  codeListing(c, buf, start, last)
-  when declared(echo):
-    echo buf
+  echo codeListing(c, start, last).alignTable
 
-proc forkI(c: var Con; n: PNode): TPosition =
+proc forkI(c: var Con): TPosition =
   result = TPosition(c.code.len)
-  c.code.add Instr(n: n, kind: fork, dest: 0)
+  c.code.add Instr(kind: fork, dest: 0)
 
-proc gotoI(c: var Con; n: PNode): TPosition =
+proc gotoI(c: var Con): TPosition =
   result = TPosition(c.code.len)
-  c.code.add Instr(n: n, kind: goto, dest: 0)
+  c.code.add Instr(kind: goto, dest: 0)
 
-proc genLabel(c: Con): TPosition =
-  result = TPosition(c.code.len)
+proc genLabel(c: Con): TPosition = TPosition(c.code.len)
+
+template checkedDistance(dist): int =
+  doAssert low(int) div 2 + 1 < dist and dist < high(int) div 2
+  dist
 
-proc jmpBack(c: var Con, n: PNode, p = TPosition(0)) =
-  let dist = p.int - c.code.len
-  doAssert(-0x7fff < dist and dist < 0x7fff)
-  c.code.add Instr(n: n, kind: goto, dest: dist)
+proc jmpBack(c: var Con, p = TPosition(0)) =
+  c.code.add Instr(kind: loop, dest: checkedDistance(p.int - c.code.len))
 
 proc patch(c: var Con, p: TPosition) =
   # patch with current index
-  let p = p.int
-  let diff = c.code.len - p
-  doAssert(-0x7fff < diff and diff < 0x7fff)
-  c.code[p].dest = diff
+  c.code[p.int].dest = checkedDistance(c.code.len - p.int)
+
+proc gen(c: var Con; n: PNode)
 
 proc popBlock(c: var Con; oldLen: int) =
-  for f in c.blocks[oldLen].fixups:
-    c.patch(f)
+  var exits: seq[TPosition] = @[]
+  exits.add c.gotoI()
+  for f in c.blocks[oldLen].breakFixups:
+    c.patch(f[0])
+    for finale in f[1]:
+      c.gen(finale)
+    exits.add c.gotoI()
+  for e in exits:
+    c.patch e
   c.blocks.setLen(oldLen)
 
-template withBlock(labl: PSym; body: untyped) {.dirty.} =
-  var oldLen {.gensym.} = c.blocks.len
-  c.blocks.add TBlock(label: labl, fixups: @[])
+template withBlock(labl: PSym; body: untyped) =
+  let oldLen = c.blocks.len
+  c.blocks.add TBlock(isTryBlock: false, label: labl)
   body
   popBlock(c, oldLen)
 
-proc isTrue(n: PNode): bool =
-  n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
-    n.kind == nkIntLit and n.intVal != 0
-
-proc gen(c: var Con; n: PNode) # {.noSideEffect.}
+template forkT(body) =
+  let lab1 = c.forkI()
+  body
+  c.patch(lab1)
 
 proc genWhile(c: var Con; n: PNode) =
-  # L1:
+  # lab1:
   #   cond, tmp
-  #   fork tmp, L2
+  #   fork tmp, lab2
   #   body
-  #   jmp L1
-  # L2:
-  let L1 = c.genLabel
+  #   jmp lab1
+  # lab2:
+  let lab1 = c.genLabel
   withBlock(nil):
-    if isTrue(n.sons[0]):
-      c.gen(n.sons[1])
-      c.jmpBack(n, L1)
+    if isTrue(n[0]):
+      c.gen(n[1])
+      c.jmpBack(lab1)
     else:
-      c.gen(n.sons[0])
-      let L2 = c.forkI(n)
-      c.gen(n.sons[1])
-      c.jmpBack(n, L1)
-      c.patch(L2)
-
-proc genBlock(c: var Con; n: PNode) =
-  withBlock(n.sons[0].sym):
-    c.gen(n.sons[1])
-
-proc genBreak(c: var Con; n: PNode) =
-  let L1 = c.gotoI(n)
-  if n.sons[0].kind == nkSym:
-    #echo cast[int](n.sons[0].sym)
-    for i in countdown(c.blocks.len-1, 0):
-      if c.blocks[i].label == n.sons[0].sym:
-        c.blocks[i].fixups.add L1
-        return
-    #globalError(n.info, "VM problem: cannot find 'break' target")
-  else:
-    c.blocks[c.blocks.high].fixups.add L1
+      c.gen(n[0])
+      forkT:
+        c.gen(n[1])
+        c.jmpBack(lab1)
 
 proc genIf(c: var Con, n: PNode) =
+  #[
+
+  if cond:
+    A
+  elif condB:
+    B
+  elif condC:
+    C
+  else:
+    D
+
+  cond
+  fork lab1
+  A
+  goto Lend
+  lab1:
+    condB
+    fork lab2
+    B
+    goto Lend2
+  lab2:
+    condC
+    fork L3
+    C
+    goto Lend3
+  L3:
+    D
+  ]#
   var endings: seq[TPosition] = @[]
-  for i in countup(0, len(n) - 1):
-    var it = n.sons[i]
-    c.gen(it.sons[0])
+  let oldInteresting = c.interestingInstructions
+  let oldLen = c.code.len
+
+  for i in 0..<n.len:
+    let it = n[i]
+    c.gen(it[0])
     if it.len == 2:
-      let elsePos = c.forkI(it.sons[1])
-      c.gen(it.sons[1])
-      if i < sonsLen(n)-1:
-        endings.add(c.gotoI(it.sons[1]))
-      c.patch(elsePos)
-  for endPos in endings: c.patch(endPos)
+      forkT:
+        c.gen(it.lastSon)
+        endings.add c.gotoI()
+
+  if oldInteresting == c.interestingInstructions:
+    setLen c.code, oldLen
+  else:
+    for i in countdown(endings.high, 0):
+      c.patch(endings[i])
 
 proc genAndOr(c: var Con; n: PNode) =
   #   asgn dest, a
-  #   fork L1
+  #   fork lab1
   #   asgn dest, b
-  # L1:
-  c.gen(n.sons[1])
-  let L1 = c.forkI(n)
-  c.gen(n.sons[2])
-  c.patch(L1)
+  # lab1:
+  c.gen(n[1])
+  forkT:
+    c.gen(n[2])
 
 proc genCase(c: var Con; n: PNode) =
-  #  if (!expr1) goto L1;
+  #  if (!expr1) goto lab1;
   #    thenPart
   #    goto LEnd
-  #  L1:
-  #  if (!expr2) goto L2;
+  #  lab1:
+  #  if (!expr2) goto lab2;
   #    thenPart2
   #    goto LEnd
-  #  L2:
+  #  lab2:
   #    elsePart
   #  Lend:
+  let isExhaustive = skipTypes(n[0].typ,
+    abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString, tyCstring}
+
   var endings: seq[TPosition] = @[]
-  c.gen(n.sons[0])
-  for i in 1 ..< n.len:
-    let it = n.sons[i]
-    if it.len == 1:
-      c.gen(it.sons[0])
-    else:
-      let elsePos = c.forkI(it.lastSon)
+  c.gen(n[0])
+  let oldInteresting = c.interestingInstructions
+  let oldLen = c.code.len
+  for i in 1..<n.len:
+    let it = n[i]
+    if it.len == 1 or (i == n.len-1 and isExhaustive):
+      # treat the last branch as 'else' if this is an exhaustive case statement.
       c.gen(it.lastSon)
-      if i < sonsLen(n)-1:
-        endings.add(c.gotoI(it.lastSon))
-      c.patch(elsePos)
-  for endPos in endings: c.patch(endPos)
+    else:
+      forkT:
+        c.gen(it.lastSon)
+        endings.add c.gotoI()
+
+  if oldInteresting == c.interestingInstructions:
+    setLen c.code, oldLen
+  else:
+    for i in countdown(endings.high, 0):
+      c.patch(endings[i])
+
+proc genBlock(c: var Con; n: PNode) =
+  withBlock(n[0].sym):
+    c.gen(n[1])
+
+proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =
+  let lab1 = c.gotoI()
+  if c.blocks[i].isTryBlock:
+    c.blocks[i].raiseFixups.add lab1
+  else:
+    var trailingFinales: seq[PNode] = @[]
+    if c.inTryStmt > 0:
+      # Ok, we are in a try, lets see which (if any) try's we break out from:
+      for b in countdown(c.blocks.high, i):
+        if c.blocks[b].isTryBlock:
+          trailingFinales.add c.blocks[b].finale
+
+    c.blocks[i].breakFixups.add (lab1, trailingFinales)
+
+proc genBreak(c: var Con; n: PNode) =
+  inc c.interestingInstructions
+  if n[0].kind == nkSym:
+    for i in countdown(c.blocks.high, 0):
+      if not c.blocks[i].isTryBlock and c.blocks[i].label == n[0].sym:
+        genBreakOrRaiseAux(c, i, n)
+        return
+    #globalError(n.info, "VM problem: cannot find 'break' target")
+  else:
+    for i in countdown(c.blocks.high, 0):
+      if not c.blocks[i].isTryBlock:
+        genBreakOrRaiseAux(c, i, n)
+        return
 
 proc genTry(c: var Con; n: PNode) =
   var endings: seq[TPosition] = @[]
-  let elsePos = c.forkI(n)
-  c.gen(n.sons[0])
-  c.patch(elsePos)
-  for i in 1 ..< n.len:
-    let it = n.sons[i]
+
+  let oldLen = c.blocks.len
+  c.blocks.add TBlock(isTryBlock: true, finale: if n[^1].kind == nkFinally: n[^1] else: newNode(nkEmpty))
+
+  inc c.inTryStmt
+  c.gen(n[0])
+  dec c.inTryStmt
+
+  for f in c.blocks[oldLen].raiseFixups:
+    c.patch(f)
+
+  c.blocks.setLen oldLen
+
+  for i in 1..<n.len:
+    let it = n[i]
     if it.kind != nkFinally:
-      var blen = len(it)
-      let endExcept = c.forkI(it)
-      c.gen(it.lastSon)
-      if i < sonsLen(n)-1:
-        endings.add(c.gotoI(it))
-      c.patch(endExcept)
-  for endPos in endings: c.patch(endPos)
+      forkT:
+        c.gen(it.lastSon)
+        endings.add c.gotoI()
+  for i in countdown(endings.high, 0):
+    c.patch(endings[i])
+
   let fin = lastSon(n)
   if fin.kind == nkFinally:
-    c.gen(fin.sons[0])
+    c.gen(fin[0])
+
+template genNoReturn(c: var Con) =
+  # leave the graph
+  c.code.add Instr(kind: goto, dest: high(int) - c.code.len)
 
 proc genRaise(c: var Con; n: PNode) =
-  gen(c, n.sons[0])
-  c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len)
+  inc c.interestingInstructions
+  gen(c, n[0])
+  if c.inTryStmt > 0:
+    for i in countdown(c.blocks.high, 0):
+      if c.blocks[i].isTryBlock:
+        genBreakOrRaiseAux(c, i, n)
+        return
+    assert false # Unreachable
+  else:
+    genNoReturn(c)
+
+proc genImplicitReturn(c: var Con) =
+  if c.owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter} and resultPos < c.owner.ast.len:
+    gen(c, c.owner.ast[resultPos])
 
 proc genReturn(c: var Con; n: PNode) =
-  if n.sons[0].kind != nkEmpty: gen(c, n.sons[0])
-  c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len)
+  inc c.interestingInstructions
+  if n[0].kind != nkEmpty:
+    gen(c, n[0])
+  else:
+    genImplicitReturn(c)
+  genBreakOrRaiseAux(c, 0, n)
 
 const
-  InterestingSyms = {skVar, skResult, skLet}
-
-proc genUse(c: var Con; n: PNode) =
-  var n = n
-  while n.kind in {nkDotExpr, nkCheckedFieldExpr,
-                   nkBracketExpr, nkDerefExpr, nkHiddenDeref,
-                   nkAddr, nkHiddenAddr}:
-    n = n[0]
-  if n.kind == nkSym and n.sym.kind in InterestingSyms:
-    if c.inCall > 0:
-      c.code.add Instr(n: n, kind: useWithinCall, sym: n.sym)
-    else:
-      c.code.add Instr(n: n, kind: use, sym: n.sym)
+  InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp}
+
+proc skipTrivials(c: var Con, n: PNode): PNode =
+  result = n
+  while true:
+    case result.kind
+    of PathKinds0 - {nkBracketExpr}:
+      result = result[0]
+    of nkBracketExpr:
+      gen(c, result[1])
+      result = result[0]
+    of PathKinds1:
+      result = result[1]
+    else: break
+
+proc genUse(c: var Con; orig: PNode) =
+  let n = c.skipTrivials(orig)
+
+  if n.kind == nkSym:
+    if n.sym.kind in InterestingSyms and n.sym == c.root:
+      c.code.add Instr(kind: use, n: orig)
+      inc c.interestingInstructions
+  else:
+    gen(c, n)
+
+proc genDef(c: var Con; orig: PNode) =
+  let n = c.skipTrivials(orig)
 
-proc genDef(c: var Con; n: PNode) =
   if n.kind == nkSym and n.sym.kind in InterestingSyms:
-    c.code.add Instr(n: n, kind: def, sym: n.sym)
+    if n.sym == c.root:
+      c.code.add Instr(kind: def, n: orig)
+      inc c.interestingInstructions
 
 proc genCall(c: var Con; n: PNode) =
   gen(c, n[0])
   var t = n[0].typ
   if t != nil: t = t.skipTypes(abstractInst)
-  inc c.inCall
   for i in 1..<n.len:
     gen(c, n[i])
-    if t != nil and i < t.len and t.sons[i].kind == tyVar:
+    if t != nil and i < t.signatureLen and isOutParam(t[i]):
+      # Pass by 'out' is a 'must def'. Good enough for a move optimizer.
       genDef(c, n[i])
-  dec c.inCall
+  # every call can potentially raise:
+  if c.inTryStmt > 0 and canRaiseConservative(n[0]):
+    inc c.interestingInstructions
+    # we generate the instruction sequence:
+    # fork lab1
+    # goto exceptionHandler (except or finally)
+    # lab1:
+    forkT:
+      for i in countdown(c.blocks.high, 0):
+        if c.blocks[i].isTryBlock:
+          genBreakOrRaiseAux(c, i, n)
+          break
 
 proc genMagic(c: var Con; n: PNode; m: TMagic) =
   case m
@@ -276,170 +393,99 @@ proc genMagic(c: var Con; n: PNode; m: TMagic) =
   of mNew, mNewFinalize:
     genDef(c, n[1])
     for i in 2..<n.len: gen(c, n[i])
-  of mExit:
-    genCall(c, n)
-    c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len)
   else:
     genCall(c, n)
 
 proc genVarSection(c: var Con; n: PNode) =
   for a in n:
-    if a.kind == nkCommentStmt: continue
-    if a.kind == nkVarTuple:
+    if a.kind == nkCommentStmt:
+      discard
+    elif a.kind == nkVarTuple:
       gen(c, a.lastSon)
-      for i in 0 .. a.len-3: genDef(c, a[i])
+      for i in 0..<a.len-2: genDef(c, a[i])
     else:
       gen(c, a.lastSon)
       if a.lastSon.kind != nkEmpty:
-        genDef(c, a.sons[0])
+        genDef(c, a[0])
 
 proc gen(c: var Con; n: PNode) =
   case n.kind
   of nkSym: genUse(c, n)
   of nkCallKinds:
-    if n.sons[0].kind == nkSym:
-      let s = n.sons[0].sym
+    if n[0].kind == nkSym:
+      let s = n[0].sym
       if s.magic != mNone:
         genMagic(c, n, s.magic)
       else:
         genCall(c, n)
+      if sfNoReturn in n[0].sym.flags:
+        genNoReturn(c)
     else:
       genCall(c, n)
   of nkCharLit..nkNilLit: discard
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     gen(c, n[1])
+
+    if n[0].kind in PathKinds0:
+      let a = c.skipTrivials(n[0])
+      if a.kind in nkCallKinds:
+        gen(c, a)
+
+    # watch out: 'obj[i].f2 = value' sets 'f2' but
+    # "uses" 'i'. But we are only talking about builtin array indexing so
+    # it doesn't matter and 'x = 34' is NOT a usage of 'x'.
     genDef(c, n[0])
-  of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr,
-     nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr:
-    gen(c, n[0])
+  of PathKinds0 - {nkObjDownConv, nkObjUpConv}:
+    genUse(c, n)
   of nkIfStmt, nkIfExpr: genIf(c, n)
   of nkWhenStmt:
     # This is "when nimvm" node. Chose the first branch.
-    gen(c, n.sons[0].sons[1])
+    gen(c, n[0][1])
   of nkCaseStmt: genCase(c, n)
   of nkWhileStmt: genWhile(c, n)
   of nkBlockExpr, nkBlockStmt: genBlock(c, n)
   of nkReturnStmt: genReturn(c, n)
   of nkRaiseStmt: genRaise(c, n)
   of nkBreakStmt: genBreak(c, n)
-  of nkTryStmt: genTry(c, n)
+  of nkTryStmt, nkHiddenTryStmt: genTry(c, n)
   of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange,
-     nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr:
+     nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr, nkYieldStmt:
     for x in n: gen(c, x)
   of nkPragmaBlock: gen(c, n.lastSon)
-  of nkDiscardStmt: gen(c, n.sons[0])
-  of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkExprColonExpr, nkExprEqExpr,
-     nkCast:
-    gen(c, n.sons[1])
-  of nkObjDownConv, nkStringToCString, nkCStringToString: gen(c, n.sons[0])
+  of nkDiscardStmt, nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
+    gen(c, n[0])
+  of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast, PathKinds1:
+    gen(c, n[1])
   of nkVarSection, nkLetSection: genVarSection(c, n)
+  of nkDefer: raiseAssert "dfa construction pass requires the elimination of 'defer'"
   else: discard
 
-proc dfa(code: seq[Instr]; conf: ConfigRef) =
-  var u = newSeq[IntSet](code.len) # usages
-  var d = newSeq[IntSet](code.len) # defs
-  var c = newSeq[IntSet](code.len) # consumed
-  var backrefs = initTable[int, int]()
-  for i in 0..<code.len:
-    u[i] = initIntSet()
-    d[i] = initIntSet()
-    c[i] = initIntSet()
-    case code[i].kind
-    of use, useWithinCall: u[i].incl(code[i].sym.id)
-    of def: d[i].incl(code[i].sym.id)
-    of fork, goto:
-      let d = i+code[i].dest
-      backrefs.add(d, i)
-
-  var w = @[0]
-  var maxIters = 50
-  var someChange = true
-  var takenGotos = initIntSet()
-  var consuming = -1
-  while w.len > 0 and maxIters > 0: # and someChange:
-    dec maxIters
-    var pc = w.pop() # w[^1]
-    var prevPc = -1
-    # this simulates a single linear control flow execution:
-    while pc < code.len:
-      if prevPc >= 0:
-        someChange = false
-        # merge step and test for changes (we compute the fixpoints here):
-        # 'u' needs to be the union of prevPc, pc
-        # 'd' needs to be the intersection of 'pc'
-        for id in u[prevPc]:
-          if not u[pc].containsOrIncl(id):
-            someChange = true
-        # in (a; b) if ``a`` sets ``v`` so does ``b``. The intersection
-        # is only interesting on merge points:
-        for id in d[prevPc]:
-          if not d[pc].containsOrIncl(id):
-            someChange = true
-        # if this is a merge point, we take the intersection of the 'd' sets:
-        if backrefs.hasKey(pc):
-          var intersect = initIntSet()
-          assign(intersect, d[pc])
-          var first = true
-          for prevPc in backrefs.allValues(pc):
-            for def in d[pc]:
-              if def notin d[prevPc]:
-                excl(intersect, def)
-                someChange = true
-                when defined(debugDfa):
-                  echo "Excluding ", pc, " prev ", prevPc
-          assign d[pc], intersect
-      if consuming >= 0:
-        if not c[pc].containsOrIncl(consuming):
-          someChange = true
-        consuming = -1
-
-      # our interpretation ![I!]:
-      prevPc = pc
-      case code[pc].kind
-      of goto:
-        # we must leave endless loops eventually:
-        if not takenGotos.containsOrIncl(pc) or someChange:
-          pc = pc + code[pc].dest
-        else:
-          inc pc
-      of fork:
-        # we follow the next instruction but push the dest onto our "work" stack:
-        #if someChange:
-        w.add pc + code[pc].dest
-        inc pc
-      of use, useWithinCall:
-        #if not d[prevPc].missingOrExcl():
-        # someChange = true
-        consuming = code[pc].sym.id
-        inc pc
-      of def:
-        if not d[pc].containsOrIncl(code[pc].sym.id):
-          someChange = true
-        inc pc
-
-  when defined(useDfa) and defined(debugDfa):
-    for i in 0..<code.len:
-      echo "PC ", i, ": defs: ", d[i], "; uses ", u[i], "; consumes ", c[i]
-
-  # now check the condition we're interested in:
-  for i in 0..<code.len:
-    case code[i].kind
-    of use, useWithinCall:
-      let s = code[i].sym
-      if s.id notin d[i]:
-        localError(conf, code[i].n.info, "usage of uninitialized variable: " & s.name.s)
-      if s.id in c[i]:
-        localError(conf, code[i].n.info, "usage of an already consumed variable: " & s.name.s)
-
-    else: discard
-
-proc dataflowAnalysis*(s: PSym; body: PNode; conf: ConfigRef) =
-  var c = Con(code: @[], blocks: @[])
-  gen(c, body)
-  when defined(useDfa) and defined(debugDfa): echoCfg(c.code)
-  dfa(c.code, conf)
-
-proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph =
+when false:
+  proc optimizeJumps(c: var ControlFlowGraph) =
+    for i in 0..<c.len:
+      case c[i].kind
+      of goto, fork:
+        var pc = i + c[i].dest
+        if pc < c.len and c[pc].kind == goto:
+          while pc < c.len and c[pc].kind == goto:
+            let newPc = pc + c[pc].dest
+            if newPc > pc:
+              pc = newPc
+            else:
+              break
+          c[i].dest = pc - i
+      of loop, def, use: discard
+
+proc constructCfg*(s: PSym; body: PNode; root: PSym): ControlFlowGraph =
   ## constructs a control flow graph for ``body``.
-  var c = Con(code: @[], blocks: @[])
-  shallowCopy(result, c.code)
+  var c = Con(code: @[], blocks: @[], owner: s, root: root)
+  withBlock(s):
+    gen(c, body)
+    if root.kind == skResult:
+      genImplicitReturn(c)
+  when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
+    result = c.code # will move
+  else:
+    shallowCopy(result, c.code)
+  when false:
+    optimizeJumps result
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index d463dc3c0..8e5f5e4e7 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -7,95 +7,350 @@
 #    distribution, for details about the copyright.
 #
 
-# This is the documentation generator. It is currently pretty simple: No
-# semantic checking is done for the code. Cross-references are generated
-# by knowing how the anchors are going to be named.
+## This is the Nim documentation generator. Cross-references are generated
+## by knowing how the anchors are going to be named.
+##
+## .. importdoc:: ../docgen.md
+##
+## For corresponding users' documentation see [Nim DocGen Tools Guide].
 
 import
-  ast, strutils, strtabs, options, msgs, os, ropes, idents,
-  wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast,
-  packages/docutils/rst, packages/docutils/rstgen, times,
-  packages/docutils/highlite, sempass2, json, xmltree, cgi,
-  typesrenderer, astalgo, modulepaths, lineinfos
+  ast, options, msgs, idents,
+  wordrecg, syntaxes, renderer, lexer,
+  packages/docutils/[rst, rstidx, rstgen, dochelpers],
+  trees, types,
+  typesrenderer, astalgo, lineinfos,
+  pathutils, nimpaths, renderverbatim, packages
+import packages/docutils/rstast except FileIndex, TLineInfo
+
+import std/[os, strutils, strtabs, algorithm, json, osproc, tables, intsets, xmltree, sequtils]
+from std/uri import encodeUrl
+from nodejs import findNodeJs
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
+
+
+const
+  exportSection = skField
+  docCmdSkip = "skip"
+  DocColOffset = "## ".len  # assuming that a space was added after ##
 
 type
-  TSections = array[TSymKind, Rope]
+  ItemFragment = object  ## A fragment from each item will be eventually
+                         ## constructed by converting `rst` fields to strings.
+    case isRst: bool
+    of true:
+      rst: PRstNode
+    of false:            ## contains ready markup e.g. from runnableExamples
+      str: string
+  ItemPre = seq[ItemFragment]  ## A pre-processed item.
+  Item = object        ## Any item in documentation, e.g. symbol
+                       ## entry. Configuration variable ``doc.item``
+                       ## is used for its HTML rendering.
+    descRst: ItemPre     ## Description of the item (may contain
+                         ## runnableExamples).
+    substitutions: seq[string]    ## Variable names in `doc.item`...
+    sortName: string    ## The string used for sorting in output
+    info: rstast.TLineInfo  ## place where symbol was defined (for messages)
+    anchor: string  ## e.g. HTML anchor
+    name: string  ## short name of the symbol, not unique
+                  ## (includes backticks ` if present)
+    detailedName: string  ## longer name like `proc search(x: int): int`
+  ModSection = object  ## Section like Procs, Types, etc.
+    secItems: Table[string, seq[Item]]
+                         ## Map basic name -> pre-processed items.
+    finalMarkup: string  ## The items, after RST pass 2 and rendering.
+  ModSections = array[TSymKind, ModSection]
+  TocItem = object  ## HTML TOC item
+    content: string
+    sortName: string
+  TocSectionsFinal = array[TSymKind, string]
+  ExampleGroup = ref object
+    ## a group of runnableExamples with same rdoccmd
+    rdoccmd: string ## from 1st arg in `runnableExamples(rdoccmd): body`
+    docCmd: string ## from user config, e.g. --doccmd:-d:foo
+    code: string ## contains imports; each import contains `body`
+    index: int ## group index
+  JsonItem = object  # pre-processed item: `rst` should be finalized
+    json: JsonNode
+    rst: PRstNode
+    rstField: string
   TDocumentor = object of rstgen.RstGenerator
-    modDesc: Rope           # module description
-    toc, section: TSections
+    modDescPre: ItemPre   # module description, not finalized
+    modDescFinal: string  # module description, after RST pass 2 and rendering
+    module: PSym
+    modDeprecationMsg: string
+    section: ModSections     # entries of ``.nim`` file (for `proc`s, etc)
+    tocSimple: array[TSymKind, seq[TocItem]]
+      # TOC entries for non-overloadable symbols (e.g. types, constants)...
+    tocTable:  array[TSymKind, Table[string, seq[TocItem]]]
+      # ...otherwise (e.g. procs)
+    toc2: TocSectionsFinal  # TOC `content`, which is probably wrapped
+                            # in `doc.section.toc2`
+    toc: TocSectionsFinal  # final TOC (wrapped in `doc.section.toc`)
     indexValFilename: string
     analytics: string  # Google Analytics javascript, "" if doesn't exist
     seenSymbols: StringTableRef # avoids duplicate symbol generation for HTML.
-    jArray: JsonNode
+    jEntriesPre: seq[JsonItem] # pre-processed RST + JSON content
+    jEntriesFinal: JsonNode    # final JSON after RST pass 2 and rendering
     types: TStrTable
-    isPureRst: bool
+    sharedState: PRstSharedState
+    standaloneDoc: bool        # is markup (.rst/.md) document?
     conf*: ConfigRef
     cache*: IdentCache
+    exampleCounter: int
+    emitted: IntSet # we need to track which symbols have been emitted
+                    # already. See bug #3655
+    thisDir*: AbsoluteDir
+    exampleGroups: OrderedTable[string, ExampleGroup]
+    wroteSupportFiles*: bool
+    nimToRstFid: Table[lineinfos.FileIndex, rstast.FileIndex]
+      ## map Nim FileIndex -> RST one, it's needed because we keep them separate
 
   PDoc* = ref TDocumentor ## Alias to type less.
 
+proc add(dest: var ItemPre, rst: PRstNode) = dest.add ItemFragment(isRst: true, rst: rst)
+proc add(dest: var ItemPre, str: string) = dest.add ItemFragment(isRst: false, str: str)
+
+proc addRstFileIndex(d: PDoc, fileIndex: lineinfos.FileIndex): rstast.FileIndex =
+  let invalid = rstast.FileIndex(-1)
+  result = d.nimToRstFid.getOrDefault(fileIndex, default = invalid)
+  if result == invalid:
+    let fname = toFullPath(d.conf, fileIndex)
+    result = addFilename(d.sharedState, fname)
+    d.nimToRstFid[fileIndex] = result
+
+proc addRstFileIndex(d: PDoc, info: lineinfos.TLineInfo): rstast.FileIndex =
+  addRstFileIndex(d, info.fileIndex)
+
+proc cmpDecimalsIgnoreCase(a, b: string): int =
+  ## For sorting with correct handling of cases like 'uint8' and 'uint16'.
+  ## Also handles leading zeros well (however note that leading zeros are
+  ## significant when lengths of numbers mismatch, e.g. 'bar08' > 'bar8' !).
+  runnableExamples:
+    doAssert cmpDecimalsIgnoreCase("uint8", "uint16") < 0
+    doAssert cmpDecimalsIgnoreCase("val00032", "val16suffix") > 0
+    doAssert cmpDecimalsIgnoreCase("val16suffix", "val16") > 0
+    doAssert cmpDecimalsIgnoreCase("val_08_32", "val_08_8") > 0
+    doAssert cmpDecimalsIgnoreCase("val_07_32", "val_08_8") < 0
+    doAssert cmpDecimalsIgnoreCase("ab8", "ab08") < 0
+    doAssert cmpDecimalsIgnoreCase("ab8de", "ab08c") < 0 # sanity check
+  let aLen = a.len
+  let bLen = b.len
+  var
+    iA = 0
+    iB = 0
+  while iA < aLen and iB < bLen:
+    if isDigit(a[iA]) and isDigit(b[iB]):
+      var
+        limitA = iA  # index after the last (least significant) digit
+        limitB = iB
+      while limitA < aLen and isDigit(a[limitA]): inc limitA
+      while limitB < bLen and isDigit(b[limitB]): inc limitB
+      var pos = max(limitA-iA, limitB-iA)
+      while pos > 0:
+        if limitA-pos < iA:  # digit in `a` is 0 effectively
+          result = ord('0') - ord(b[limitB-pos])
+        elif limitB-pos < iB:  # digit in `b` is 0 effectively
+          result = ord(a[limitA-pos]) - ord('0')
+        else:
+          result = ord(a[limitA-pos]) - ord(b[limitB-pos])
+        if result != 0: return
+        dec pos
+      result = (limitA - iA) - (limitB - iB)  # consider 'bar08' > 'bar8'
+      if result != 0: return
+      iA = limitA
+      iB = limitB
+    else:
+      result = ord(toLowerAscii(a[iA])) - ord(toLowerAscii(b[iB]))
+      if result != 0: return
+      inc iA
+      inc iB
+  result = (aLen - iA) - (bLen - iB)
+
+proc prettyString(a: object): string =
+  # xxx pending std/prettyprint refs https://github.com/nim-lang/RFCs/issues/203#issuecomment-602534906
+  result = ""
+  for k, v in fieldPairs(a):
+    result.add k & ": " & $v & "\n"
+
+proc presentationPath*(conf: ConfigRef, file: AbsoluteFile): RelativeFile =
+  ## returns a relative file that will be appended to outDir
+  let file2 = $file
+  template bail() =
+    result = relativeTo(file, conf.projectPath)
+  proc nimbleDir(): AbsoluteDir =
+    getNimbleFile(conf, file2).parentDir.AbsoluteDir
+  case conf.docRoot:
+  of docRootDefault:
+    result = getRelativePathFromConfigPath(conf, file)
+    let dir = nimbleDir()
+    if not dir.isEmpty:
+      let result2 = relativeTo(file, dir)
+      if not result2.isEmpty and (result.isEmpty or result2.string.len < result.string.len):
+        result = result2
+    if result.isEmpty: bail()
+  of "@pkg":
+    let dir = nimbleDir()
+    if dir.isEmpty: bail()
+    else: result = relativeTo(file, dir)
+  of "@path":
+    result = getRelativePathFromConfigPath(conf, file)
+    if result.isEmpty: bail()
+  elif conf.docRoot.len > 0:
+    # we're (currently) requiring `isAbsolute` to avoid confusion when passing
+    # a relative path (would it be relative with regard to $PWD or to projectfile)
+    conf.globalAssert conf.docRoot.isAbsolute, arg=conf.docRoot
+    conf.globalAssert conf.docRoot.dirExists, arg=conf.docRoot
+    # needed because `canonicalizePath` called on `file`
+    result = file.relativeTo conf.docRoot.expandFilename.AbsoluteDir
+  else:
+    bail()
+  if isAbsolute(result.string):
+    result = file.string.splitPath()[1].RelativeFile
+  result = result.string.replace("..", dotdotMangle).RelativeFile
+  doAssert not result.isEmpty
+  doAssert not isAbsolute(result.string)
+
 proc whichType(d: PDoc; n: PNode): PSym =
   if n.kind == nkSym:
     if d.types.strTableContains(n.sym):
       result = n.sym
+    else:
+      result = nil
   else:
-    for i in 0..<safeLen(n):
+    result = nil
+    for i in 0..<n.safeLen:
       let x = whichType(d, n[i])
       if x != nil: return x
 
 proc attachToType(d: PDoc; p: PSym): PSym =
-  let params = p.ast.sons[paramsPos]
-  # first check the first parameter, then the return type,
-  # then the other parameter:
+  result = nil
+  let params = p.ast[paramsPos]
   template check(i) =
     result = whichType(d, params[i])
     if result != nil: return result
 
+  # first check the first parameter, then the return type,
+  # then the other parameter:
   if params.len > 1: check(1)
   if params.len > 0: check(0)
   for i in 2..<params.len: check(i)
 
-template declareClosures =
+template declareClosures(currentFilename: AbsoluteFile, destFile: string) =
   proc compilerMsgHandler(filename: string, line, col: int,
-                          msgKind: rst.MsgKind, arg: string) {.procvar.} =
+                          msgKind: rst.MsgKind, arg: string) {.gcsafe.} =
     # translate msg kind:
     var k: TMsgKind
     case msgKind
     of meCannotOpenFile: k = errCannotOpenFile
     of meExpected: k = errXExpected
-    of meGridTableNotImplemented: k = errGridTableNotImplemented
-    of meNewSectionExpected: k = errNewSectionExpected
-    of meGeneralParseError: k = errGeneralParseError
-    of meInvalidDirective: k = errInvalidDirectiveX
-    of mwRedefinitionOfLabel: k = warnRedefinitionOfLabel
-    of mwUnknownSubstitution: k = warnUnknownSubstitutionX
-    of mwUnsupportedLanguage: k = warnLanguageXNotSupported
-    of mwUnsupportedField: k = warnFieldXNotSupported
-    globalError(conf, newLineInfo(conf, filename, line, col), k, arg)
-
-  proc docgenFindFile(s: string): string {.procvar.} =
-    result = options.findFile(conf, s)
+    of meMissingClosing: k = errRstMissingClosing
+    of meGridTableNotImplemented: k = errRstGridTableNotImplemented
+    of meMarkdownIllformedTable: k = errRstMarkdownIllformedTable
+    of meIllformedTable: k = errRstIllformedTable
+    of meNewSectionExpected: k = errRstNewSectionExpected
+    of meGeneralParseError: k = errRstGeneralParseError
+    of meInvalidDirective: k = errRstInvalidDirectiveX
+    of meInvalidField: k = errRstInvalidField
+    of meFootnoteMismatch: k = errRstFootnoteMismatch
+    of meSandboxedDirective: k = errRstSandboxedDirective
+    of mwRedefinitionOfLabel: k = warnRstRedefinitionOfLabel
+    of mwUnknownSubstitution: k = warnRstUnknownSubstitutionX
+    of mwAmbiguousLink: k = warnRstAmbiguousLink
+    of mwBrokenLink: k = warnRstBrokenLink
+    of mwUnsupportedLanguage: k = warnRstLanguageXNotSupported
+    of mwUnsupportedField: k = warnRstFieldXNotSupported
+    of mwUnusedImportdoc: k = warnRstUnusedImportdoc
+    of mwRstStyle: k = warnRstStyle
+    {.gcsafe.}:
+      let errorsAsWarnings = (roPreferMarkdown in d.sharedState.options) and
+          not d.standaloneDoc  # not tolerate errors in .rst/.md files
+      if whichMsgClass(msgKind) == mcError and errorsAsWarnings:
+        liMessage(conf, newLineInfo(conf, AbsoluteFile filename, line, col),
+                  k, arg, doNothing, instLoc(), ignoreError=true)
+        # when our Markdown parser fails, we currently can only terminate the
+        # parsing (and then we will return monospaced text instead of markup):
+        raiseRecoverableError("")
+      else:
+        globalError(conf, newLineInfo(conf, AbsoluteFile filename, line, col), k, arg)
+
+  proc docgenFindFile(s: string): string {.gcsafe.} =
+    result = options.findFile(conf, s).string
     if result.len == 0:
       result = getCurrentDir() / s
-      if not existsFile(result): result = ""
-
-proc parseRst(text, filename: string,
-              line, column: int, hasToc: var bool,
-              rstOptions: RstParseOptions;
-              conf: ConfigRef): PRstNode =
-  declareClosures()
-  result = rstParse(text, filename, line, column, hasToc, rstOptions,
-                    docgenFindFile, compilerMsgHandler)
-
-proc newDocumentor*(filename: string; cache: IdentCache; conf: ConfigRef): PDoc =
-  declareClosures()
+      if not fileExists(result): result = ""
+
+  proc docgenFindRefFile(targetRelPath: string):
+         tuple[targetPath: string, linkRelPath: string] {.gcsafe.} =
+    let fromDir = splitFile(destFile).dir  # dir where we reference from
+    let basedir = os.splitFile(currentFilename.string).dir
+    let outDirPath: RelativeFile =
+        presentationPath(conf, AbsoluteFile(basedir / targetRelPath))
+          # use presentationPath because `..` path can be be mangled to `_._`
+    result = (string(conf.outDir / outDirPath), "")
+    if not fileExists(result.targetPath):
+      # this can happen if targetRelPath goes to parent directory `OUTDIR/..`.
+      # Trying it, this may cause ambiguities, but allows us to insert
+      # "packages" into each other, which is actually used in Nim repo itself.
+      let destPath = fromDir / targetRelPath
+      if destPath != result.targetPath and fileExists(destPath):
+        result.targetPath = destPath
+
+    result.linkRelPath = relativePath(result.targetPath.splitFile.dir,
+                                      fromDir).replace('\\', '/')
+
+
+proc parseRst(text: string,
+              line, column: int,
+              conf: ConfigRef, sharedState: PRstSharedState): PRstNode =
+  result = rstParsePass1(text, line, column, sharedState)
+
+proc getOutFile2(conf: ConfigRef; filename: RelativeFile,
+                 ext: string, guessTarget: bool): AbsoluteFile =
+  if optWholeProject in conf.globalOptions or guessTarget:
+    let d = conf.outDir
+    createDir(d)
+    result = d / changeFileExt(filename, ext)
+  elif not conf.outFile.isEmpty:
+    result = absOutFile(conf)
+  else:
+    result = getOutFile(conf, filename, ext)
+
+proc isLatexCmd(conf: ConfigRef): bool =
+  conf.cmd in {cmdRst2tex, cmdMd2tex, cmdDoc2tex}
+
+proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
+                    outExt: string = HtmlExt, module: PSym = nil,
+                    standaloneDoc = false, preferMarkdown = true,
+                    hasToc = true): PDoc =
+  let destFile = getOutFile2(conf, presentationPath(conf, filename), outExt, false).string
   new(result)
+  let d = result  # pass `d` to `declareClosures`:
+  declareClosures(currentFilename = filename, destFile = destFile)
+  result.module = module
   result.conf = conf
   result.cache = cache
-  initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex),
-                   conf.configVars, filename, {roSupportRawDirective},
+  result.outDir = conf.outDir.string
+  result.standaloneDoc = standaloneDoc
+  var options= {roSupportRawDirective, roSupportMarkdown, roSandboxDisabled}
+  if preferMarkdown:
+    options.incl roPreferMarkdown
+  if not standaloneDoc: options.incl roNimFile
+  # (options can be changed dynamically in `setDoctype` by `{.doctype.}`)
+  result.hasToc = hasToc
+  result.sharedState = newRstSharedState(
+      options, filename.string,
+      docgenFindFile, docgenFindRefFile, compilerMsgHandler, hasToc)
+  initRstGenerator(result[], (if conf.isLatexCmd: outLatex else: outHtml),
+                   conf.configVars, filename.string,
                    docgenFindFile, compilerMsgHandler)
 
+  if conf.configVars.hasKey("doc.googleAnalytics") and
+      conf.configVars.hasKey("doc.plausibleAnalytics"):
+    raiseAssert "Either use googleAnalytics or plausibleAnalytics"
+
   if conf.configVars.hasKey("doc.googleAnalytics"):
     result.analytics = """
 <script>
@@ -109,103 +364,108 @@ proc newDocumentor*(filename: string; cache: IdentCache; conf: ConfigRef): PDoc
 
 </script>
     """ % [conf.configVars.getOrDefault"doc.googleAnalytics"]
+  elif conf.configVars.hasKey("doc.plausibleAnalytics"):
+    result.analytics = """
+    <script defer data-domain="$1" src="https://plausible.io/js/plausible.js"></script>
+    """ % [conf.configVars.getOrDefault"doc.plausibleAnalytics"]
   else:
     result.analytics = ""
 
   result.seenSymbols = newStringTable(modeCaseInsensitive)
   result.id = 100
-  result.jArray = newJArray()
-  initStrTable result.types
-  result.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string; status: int; content: string) =
-    localError(conf, newLineInfo(conf, d.filename, -1, -1), warnUser, "only 'rst2html' supports the ':test:' attribute")
-
-proc dispA(conf: ConfigRef; dest: var Rope, xml, tex: string, args: openArray[Rope]) =
-  if conf.cmd != cmdRst2tex: addf(dest, xml, args)
-  else: addf(dest, tex, args)
+  result.jEntriesFinal = newJArray()
+  result.types = initStrTable()
+  result.onTestSnippet =
+    proc (gen: var RstGenerator; filename, cmd: string; status: int; content: string) {.gcsafe.} =
+      if conf.docCmd == docCmdSkip: return
+      inc(gen.id)
+      var d = (ptr TDocumentor)(addr gen)
+      var outp: AbsoluteFile
+      if filename.len == 0:
+        let nameOnly = splitFile(d.filename).name
+        # "snippets" needed, refs bug #17183
+        outp = getNimcacheDir(conf) / "snippets".RelativeDir / RelativeDir(nameOnly) /
+               RelativeFile(nameOnly & "_snippet_" & $d.id & ".nim")
+      elif isAbsolute(filename):
+        outp = AbsoluteFile(filename)
+      else:
+        # Nim's convention: every path is relative to the file it was written in:
+        let nameOnly = splitFile(d.filename).name
+        outp = AbsoluteDir(nameOnly) / RelativeFile(filename)
+      # Make sure the destination directory exists
+      createDir(outp.splitFile.dir)
+      # Include the current file if we're parsing a nim file
+      let importStmt = if d.standaloneDoc: "" else: "import \"$1\"\n" % [d.filename.replace("\\", "/")]
+      writeFile(outp, importStmt & content)
+
+      proc interpSnippetCmd(cmd: string): string =
+        # backward compatibility hacks; interpolation commands should explicitly use `$`
+        if cmd.startsWith "nim ": result = "$nim " & cmd[4..^1]
+        else: result = cmd
+        # factor with D20210224T221756
+        result = result.replace("$1", "$options") % [
+          "nim", os.getAppFilename().quoteShell,
+          "libpath", quoteShell(d.conf.libpath),
+          "docCmd", d.conf.docCmd,
+          "backend", $d.conf.backend,
+          "options", outp.quoteShell,
+            # xxx `quoteShell` seems buggy if user passes options = "-d:foo somefile.nim"
+        ]
+      let cmd = cmd.interpSnippetCmd
+      rawMessage(conf, hintExecuting, cmd)
+      let (output, gotten) = execCmdEx(cmd)
+      if gotten != status:
+        rawMessage(conf, errGenerated, "snippet failed: cmd: '$1' status: $2 expected: $3 output: $4" % [cmd, $gotten, $status, output])
+  result.emitted = initIntSet()
+  result.destFile = destFile
+  result.thisDir = result.destFile.AbsoluteFile.splitFile.dir
+
+template dispA(conf: ConfigRef; dest: var string, xml, tex: string,
+               args: openArray[string]) =
+  if not conf.isLatexCmd: dest.addf(xml, args)
+  else: dest.addf(tex, args)
 
 proc getVarIdx(varnames: openArray[string], id: string): int =
-  for i in countup(0, high(varnames)):
+  for i in 0..high(varnames):
     if cmpIgnoreStyle(varnames[i], id) == 0:
       return i
   result = -1
 
-proc ropeFormatNamedVars(conf: ConfigRef; frmt: FormatStr,
-                         varnames: openArray[string],
-                         varvalues: openArray[Rope]): Rope =
-  var i = 0
-  var L = len(frmt)
-  result = nil
-  var num = 0
-  while i < L:
-    if frmt[i] == '$':
-      inc(i)                  # skip '$'
-      case frmt[i]
-      of '#':
-        add(result, varvalues[num])
-        inc(num)
-        inc(i)
-      of '$':
-        add(result, "$")
-        inc(i)
-      of '0'..'9':
-        var j = 0
-        while true:
-          j = (j * 10) + ord(frmt[i]) - ord('0')
-          inc(i)
-          if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break
-        if j > high(varvalues) + 1:
-          rawMessage(conf, errGenerated, "Invalid format string; too many $s: " & frmt)
-        num = j
-        add(result, varvalues[j - 1])
-      of 'A'..'Z', 'a'..'z', '\x80'..'\xFF':
-        var id = ""
-        while true:
-          add(id, frmt[i])
-          inc(i)
-          if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break
-        var idx = getVarIdx(varnames, id)
-        if idx >= 0: add(result, varvalues[idx])
-        else: rawMessage(conf, errGenerated, "unknown substition variable: " & id)
-      of '{':
-        var id = ""
-        inc(i)
-        while i < frmt.len and frmt[i] != '}':
-          add(id, frmt[i])
-          inc(i)
-        if i >= frmt.len:
-          rawMessage(conf, errGenerated, "expected closing '}'")
-        else:
-          inc(i)                # skip }
-        # search for the variable:
-        let idx = getVarIdx(varnames, id)
-        if idx >= 0: add(result, varvalues[idx])
-        else: rawMessage(conf, errGenerated, "unknown substition variable: " & id)
-      else:
-        add(result, "$")
-    var start = i
-    while i < L:
-      if frmt[i] != '$': inc(i)
-      else: break
-    if i - 1 >= start: add(result, substr(frmt, start, i - 1))
-
-proc genComment(d: PDoc, n: PNode): string =
-  result = ""
-  var dummyHasToc: bool
-  if n.comment != nil:
-    renderRstToOut(d[], parseRst(n.comment, toFilename(d.conf, n.info),
-                               toLinenumber(n.info), toColumn(n.info),
-                               dummyHasToc, d.options, d.conf), result)
+proc genComment(d: PDoc, n: PNode): PRstNode =
+  if n.comment.len > 0:
+    d.sharedState.currFileIdx = addRstFileIndex(d, n.info)
+    try:
+      result = parseRst(n.comment,
+                        toLinenumber(n.info),
+                        toColumn(n.info) + DocColOffset,
+                        d.conf, d.sharedState)
+    except ERecoverableError:
+      result = newRstNode(rnLiteralBlock, @[newRstLeaf(n.comment)])
+  else:
+    result = nil
 
-proc genRecComment(d: PDoc, n: PNode): Rope =
+proc genRecCommentAux(d: PDoc, n: PNode): PRstNode =
   if n == nil: return nil
-  result = genComment(d, n).rope
+  result = genComment(d, n)
   if result == nil:
-    if n.kind notin {nkEmpty..nkNilLit, nkEnumTy, nkTupleTy}:
-      for i in countup(0, len(n)-1):
-        result = genRecComment(d, n.sons[i])
+    if n.kind in {nkStmtList, nkStmtListExpr, nkTypeDef, nkConstDef, nkTypeClassTy,
+                  nkObjectTy, nkRefTy, nkPtrTy, nkAsgn, nkFastAsgn, nkSinkAsgn, nkHiddenStdConv}:
+      # notin {nkEmpty..nkNilLit, nkEnumTy, nkTupleTy}:
+      for i in 0..<n.len:
+        result = genRecCommentAux(d, n[i])
         if result != nil: return
   else:
-    n.comment = nil
+    n.comment = ""
+
+proc genRecComment(d: PDoc, n: PNode): PRstNode =
+  if n == nil: return nil
+  result = genComment(d, n)
+  if result == nil:
+    if n.kind in {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef,
+                  nkMacroDef, nkTemplateDef, nkConverterDef}:
+      result = genRecCommentAux(d, n[bodyPos])
+    else:
+      result = genRecCommentAux(d, n)
 
 proc getPlainDocstring(n: PNode): string =
   ## Gets the plain text docstring of a node non destructively.
@@ -213,59 +473,95 @@ proc getPlainDocstring(n: PNode): string =
   ## You need to call this before genRecComment, whose side effects are removal
   ## of comments from the tree. The proc will recursively scan and return all
   ## the concatenated ``##`` comments of the node.
-  result = ""
-  if n == nil: return
-  if n.comment != nil and startsWith(n.comment, "##"):
+  if n == nil: result = ""
+  elif startsWith(n.comment, "##"):
     result = n.comment
-  if result.len < 1:
-    for i in countup(0, safeLen(n)-1):
-      result = getPlainDocstring(n.sons[i])
+  else:
+    result = ""
+    for i in 0..<n.safeLen:
+      result = getPlainDocstring(n[i])
       if result.len > 0: return
 
-proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRenderFlags = {}) =
-  var r: TSrcGen
+proc externalDep(d: PDoc; module: PSym): string =
+  if optWholeProject in d.conf.globalOptions or d.conf.docRoot.len > 0:
+    let full = AbsoluteFile toFullPath(d.conf, FileIndex module.position)
+    let tmp = getOutFile2(d.conf, presentationPath(d.conf, full), HtmlExt, sfMainModule notin module.flags)
+    result = relativeTo(tmp, d.thisDir, '/').string
+  else:
+    result = extractFilename toFullPath(d.conf, FileIndex module.position)
+
+proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var string;
+                           renderFlags: TRenderFlags = {};
+                           procLink: string) =
+  var r: TSrcGen = initTokRender(n, renderFlags)
   var literal = ""
-  initTokRender(r, n, renderFlags)
   var kind = tkEof
+  var tokenPos = 0
+  var procTokenPos = 0
+  template escLit(): untyped = esc(d.target, literal)
   while true:
     getNextTok(r, kind, literal)
+    inc tokenPos
     case kind
     of tkEof:
       break
     of tkComment:
       dispA(d.conf, result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}",
-            [rope(esc(d.target, literal))])
+            [escLit])
     of tokKeywordLow..tokKeywordHigh:
+      if kind in {tkProc, tkMethod, tkIterator, tkMacro, tkTemplate, tkFunc, tkConverter}:
+        procTokenPos = tokenPos
       dispA(d.conf, result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}",
-            [rope(literal)])
+            [literal])
     of tkOpr:
       dispA(d.conf, result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}",
-            [rope(esc(d.target, literal))])
-    of tkStrLit..tkTripleStrLit:
+            [escLit])
+    of tkStrLit..tkTripleStrLit, tkCustomLit:
       dispA(d.conf, result, "<span class=\"StringLit\">$1</span>",
-            "\\spanStringLit{$1}", [rope(esc(d.target, literal))])
+            "\\spanStringLit{$1}", [escLit])
     of tkCharLit:
       dispA(d.conf, result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}",
-            [rope(esc(d.target, literal))])
+            [escLit])
     of tkIntLit..tkUInt64Lit:
       dispA(d.conf, result, "<span class=\"DecNumber\">$1</span>",
-            "\\spanDecNumber{$1}", [rope(esc(d.target, literal))])
+            "\\spanDecNumber{$1}", [escLit])
     of tkFloatLit..tkFloat128Lit:
       dispA(d.conf, result, "<span class=\"FloatNumber\">$1</span>",
-            "\\spanFloatNumber{$1}", [rope(esc(d.target, literal))])
+            "\\spanFloatNumber{$1}", [escLit])
     of tkSymbol:
-      dispA(d.conf, result, "<span class=\"Identifier\">$1</span>",
-            "\\spanIdentifier{$1}", [rope(esc(d.target, literal))])
+      let s = getTokSym(r)
+      # -2 because of the whitespace in between:
+      if procTokenPos == tokenPos-2 and procLink != "":
+        dispA(d.conf, result, "<a href=\"#$2\"><span class=\"Identifier\">$1</span></a>",
+              "\\spanIdentifier{$1}", [escLit, procLink])
+      elif s != nil and s.kind in {skType, skVar, skLet, skConst} and
+           sfExported in s.flags and s.owner != nil and
+           belongsToProjectPackage(d.conf, s.owner) and d.target == outHtml:
+        let external = externalDep(d, s.owner)
+        result.addf "<a href=\"$1#$2\"><span class=\"Identifier\">$3</span></a>",
+          [changeFileExt(external, "html"), literal,
+           escLit]
+      else:
+        dispA(d.conf, result, "<span class=\"Identifier\">$1</span>",
+              "\\spanIdentifier{$1}", [escLit])
     of tkSpaces, tkInvalid:
-      add(result, literal)
-    of tkCurlyDotLe:
-      dispA(d.conf, result, """<span class="Other pragmabegin">$1</span><div class="pragma">""",
-                    "\\spanOther{$1}",
-                  [rope(esc(d.target, literal))])
-    of tkCurlyDotRi:
-      dispA(d.conf, result, "</div><span class=\"Other pragmaend\">$1</span>",
-                    "\\spanOther{$1}",
-                  [rope(esc(d.target, literal))])
+      result.add(literal)
+    of tkHideableStart:
+      template fun(s) = dispA(d.conf, result, s, "\\spanOther{$1}", [escLit])
+      if renderRunnableExamples in renderFlags: fun "$1"
+      else:
+        # 1st span is required for the JS to work properly
+        fun """
+<span>
+<span class="Other pragmadots">...</span>
+</span>
+<span class="pragmawrap">""".replace("\n", "")  # Must remove newlines because wrapped in a <pre>
+    of tkHideableEnd:
+      template fun(s) = dispA(d.conf, result, s, "\\spanOther{$1}", [escLit])
+      if renderRunnableExamples in renderFlags: fun "$1"
+      else: fun "</span>"
+    of tkCurlyDotLe: dispA(d.conf, result, "$1", "\\spanOther{$1}", [escLit])
+    of tkCurlyDotRi: dispA(d.conf, result, "$1", "\\spanOther{$1}", [escLit])
     of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
        tkBracketDotLe, tkBracketDotRi, tkParDotLe,
        tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
@@ -273,109 +569,301 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe
        tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr,
        tkBracketLeColon:
       dispA(d.conf, result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
-            [rope(esc(d.target, literal))])
+            [escLit])
+
+proc exampleOutputDir(d: PDoc): AbsoluteDir = d.conf.getNimcacheDir / RelativeDir"runnableExamples"
+
+proc runAllExamples(d: PDoc) =
+  # This used to be: `let backend = if isDefined(d.conf, "js"): "js"` (etc), however
+  # using `-d:js` (etc) cannot work properly, e.g. would fail with `importjs`
+  # since semantics are affected by `config.backend`, not by isDefined(d.conf, "js")
+  let outputDir = d.exampleOutputDir
+  for _, group in d.exampleGroups:
+    if group.docCmd == docCmdSkip: continue
+    let outp = outputDir / RelativeFile("$1_group$2_examples.nim" % [d.filename.splitFile.name, $group.index])
+    group.code = "# autogenerated by docgen\n# source: $1\n# rdoccmd: $2\n$3" % [d.filename, group.rdoccmd, group.code]
+    writeFile(outp, group.code)
+    # most useful semantics is that `docCmd` comes after `rdoccmd`, so that we can (temporarily) override
+    # via command line
+    # D20210224T221756:here
+    var pathArgs = "--path:$path" % [ "path", quoteShell(d.conf.projectPath) ]
+    for p in d.conf.searchPaths:
+      pathArgs = "$args --path:$path" % [ "args", pathArgs, "path", quoteShell(p) ]
+    let cmd = "$nim $backend -r --lib:$libpath --warning:UnusedImport:off $pathArgs --nimcache:$nimcache $rdoccmd $docCmd $file" % [
+      "nim", quoteShell(os.getAppFilename()),
+      "backend", $d.conf.backend,
+      "pathArgs", pathArgs,
+      "libpath", quoteShell(d.conf.libpath),
+      "nimcache", quoteShell(outputDir),
+      "file", quoteShell(outp),
+      "rdoccmd", group.rdoccmd,
+      "docCmd", group.docCmd,
+    ]
+    if d.conf.backend == backendJs and findNodeJs() == "":
+      discard "ignore JS runnableExample"
+    elif os.execShellCmd(cmd) != 0:
+      d.conf.quitOrRaise "[runnableExamples] failed: generated file: '$1' group: '$2' cmd: $3" % [outp.string, group[].prettyString, cmd]
+    else:
+      # keep generated source file `outp` to allow inspection.
+      rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp.string])
+      # removeFile(outp.changeFileExt(ExeExt)) # it's in nimcache, no need to remove
+
+proc quoted(a: string): string =
+  result = ""
+  result.addQuoted(a)
+
+proc toInstantiationInfo(conf: ConfigRef, info: TLineInfo): (string, int, int) =
+  # xxx expose in compiler/lineinfos.nim
+  (conf.toMsgFilename(info), info.line.int, info.col.int + ColOffset)
+
+proc prepareExample(d: PDoc; n: PNode, topLevel: bool): tuple[rdoccmd: string, code: string] =
+  ## returns `rdoccmd` and source code for this runnableExamples
+  var rdoccmd = ""
+  if n.len < 2 or n.len > 3: globalError(d.conf, n.info, "runnableExamples invalid")
+  if n.len == 3:
+    let n1 = n[1]
+    # xxx this should be evaluated during sempass
+    if n1.kind notin nkStrKinds: globalError(d.conf, n1.info, "string litteral expected")
+    rdoccmd = n1.strVal
+
+  let useRenderModule = false
+  let loc = d.conf.toFileLineCol(n.info)
+  let code = extractRunnableExamplesSource(d.conf, n)
+
+  if d.conf.errorCounter > 0:
+    return (rdoccmd, code)
+
+  let comment = "autogenerated by docgen\nloc: $1\nrdoccmd: $2" % [loc, rdoccmd]
+  let outputDir = d.exampleOutputDir
+  createDir(outputDir)
+  inc d.exampleCounter
+  let outp = outputDir / RelativeFile("$#_examples_$#.nim" % [d.filename.extractFilename.changeFileExt"", $d.exampleCounter])
+
+  if useRenderModule:
+    var docComment = newTree(nkCommentStmt)
+    docComment.comment = comment
+    var runnableExamples = newTree(nkStmtList,
+        docComment,
+        newTree(nkImportStmt, newStrNode(nkStrLit, "std/assertions")),
+        newTree(nkImportStmt, newStrNode(nkStrLit, d.filename)))
+    runnableExamples.info = n.info
+    for a in n.lastSon: runnableExamples.add a
+
+    # buggy, refs bug #17292
+    # still worth fixing as it can affect other code relying on `renderModule`,
+    # so we keep this code path here for now, which could still be useful in some
+    # other situations.
+    renderModule(runnableExamples, outp.string, conf = d.conf)
+
+  else:
+    var code2 = code
+    if code.len > 0 and "codeReordering" notin code:
+      let codeIndent = extractRunnableExamplesSource(d.conf, n, indent = 2)
+      # hacky but simplest solution, until we devise a way to make `{.line.}`
+      # work without introducing a scope
+      code2 = """
+{.line: $#.}:
+$#
+""" % [$toInstantiationInfo(d.conf, n.info), codeIndent]
+    code2 = """
+#[
+$#
+]#
+import std/assertions
+import $#
+$#
+""" % [comment, d.filename.quoted, code2]
+    writeFile(outp.string, code2)
+
+  if rdoccmd notin d.exampleGroups:
+    d.exampleGroups[rdoccmd] = ExampleGroup(rdoccmd: rdoccmd, docCmd: d.conf.docCmd, index: d.exampleGroups.len)
+  d.exampleGroups[rdoccmd].code.add "import $1\n" % outp.string.quoted
+
+  var codeShown: string
+  if topLevel: # refs https://github.com/nim-lang/RFCs/issues/352
+    let title = canonicalImport(d.conf, AbsoluteFile d.filename)
+    codeShown = "import $#\n$#" % [title, code]
+  else:
+    codeShown = code
+  result = (rdoccmd, codeShown)
+  when false:
+    proc extractImports(n: PNode; result: PNode) =
+      if n.kind in {nkImportStmt, nkImportExceptStmt, nkFromStmt}:
+        result.add copyTree(n)
+        n.kind = nkEmpty
+        return
+      for i in 0..<n.safeLen: extractImports(n[i], result)
+    let imports = newTree(nkStmtList)
+    var savedLastSon = copyTree n.lastSon
+    extractImports(savedLastSon, imports)
+    for imp in imports: runnableExamples.add imp
+    runnableExamples.add newTree(nkBlockStmt, newNode(nkEmpty), copyTree savedLastSon)
+
+type RunnableState = enum
+  rsStart
+  rsComment
+  rsRunnable
+  rsDone
+
+proc getAllRunnableExamplesImpl(d: PDoc; n: PNode, dest: var ItemPre,
+                                state: RunnableState, topLevel: bool):
+                               RunnableState =
+  ##[
+  Simple state machine to tell whether we render runnableExamples and doc comments.
+  This is to ensure that we can interleave runnableExamples and doc comments freely;
+  the logic is easy to change but currently a doc comment following another doc comment
+  will not render, to avoid rendering in following case:
+
+  proc fn* =
+    runnableExamples: discard
+    ## d1
+    runnableExamples: discard
+    ## d2
+
+    ## internal explanation  # <- this one should be out; it's part of rest of function body and would likey not make sense in doc comment
+    discard # some code
+  ]##
 
-proc getAllRunnableExamples(d: PDoc; n: PNode; dest: var Rope) =
   case n.kind
+  of nkCommentStmt:
+    if state in {rsStart, rsRunnable}:
+      dest.add genRecComment(d, n)
+      return rsComment
   of nkCallKinds:
-    if n[0].kind == nkSym and n[0].sym.magic == mRunnableExamples and
+    if isRunnableExamples(n[0]) and
         n.len >= 2 and n.lastSon.kind == nkStmtList:
-      dispA(d.conf, dest, "\n<strong class=\"examples_text\">$1</strong>\n",
-          "\n\\textbf{$1}\n", [rope"Examples:"])
-      inc d.listingCounter
-      let id = $d.listingCounter
-      dest.add(d.config.getOrDefault"doc.listing_start" % [id, "langNim"])
-      # this is a rather hacky way to get rid of the initial indentation
-      # that the renderer currently produces:
-      var i = 0
-      var body = n.lastSon
-      if body.len == 1 and body.kind == nkStmtList and
-          body.lastSon.kind == nkStmtList:
-        body = body.lastSon
-      for b in body:
-        if i > 0: dest.add "\n"
-        inc i
-        nodeToHighlightedHtml(d, b, dest, {})
-      dest.add(d.config.getOrDefault"doc.listing_end" % id)
-  else: discard
-  for i in 0 ..< n.safeLen:
-    getAllRunnableExamples(d, n[i], dest)
-
-when false:
-  proc findDocComment(n: PNode): PNode =
-    if n == nil: return nil
-    if not isNil(n.comment) and startsWith(n.comment, "##"): return n
-    for i in countup(0, safeLen(n)-1):
-      result = findDocComment(n.sons[i])
-      if result != nil: return
-
-  proc extractDocComment*(s: PSym, d: PDoc): string =
-    let n = findDocComment(s.ast)
-    result = ""
-    if not n.isNil:
-      if not d.isNil:
-        var dummyHasToc: bool
-        renderRstToOut(d[], parseRst(n.comment, toFilename(d.conf, n.info),
-                                     toLinenumber(n.info), toColumn(n.info),
-                                     dummyHasToc, d.options + {roSkipPounds}),
-                       result)
+      if state in {rsStart, rsComment, rsRunnable}:
+        let (rdoccmd, code) = prepareExample(d, n, topLevel)
+        var msg = "Example:"
+        if rdoccmd.len > 0: msg.add " cmd: " & rdoccmd
+        var s: string = ""
+        dispA(d.conf, s, "\n<p><strong class=\"examples_text\">$1</strong></p>\n",
+            "\n\n\\textbf{$1}\n", [msg])
+        dest.add s
+        inc d.listingCounter
+        let id = $d.listingCounter
+        dest.add(d.config.getOrDefault"doc.listing_start" % [id, "langNim", ""])
+        var dest2 = ""
+        renderNimCode(dest2, code, d.target)
+        dest.add dest2
+        dest.add(d.config.getOrDefault"doc.listing_end" % id)
+        return rsRunnable
       else:
-        result = n.comment.substr(2).replace("\n##", "\n").strip
+        localError(d.conf, n.info, errUser, "runnableExamples must appear before the first non-comment statement")
+  else: discard
+  return rsDone
+    # change this to `rsStart` if you want to keep generating doc comments
+    # and runnableExamples that occur after some code in routine
+
+proc getRoutineBody(n: PNode): PNode =
+  ##[
+  nim transforms these quite differently:
+
+  proc someType*(): int =
+    ## foo
+    result = 3
+=>
+  result =
+    ## foo
+    3;
+
+  proc someType*(): int =
+    ## foo
+    3
+=>
+  ## foo
+  result = 3;
+
+  so we normalize the results to get to the statement list containing the
+  (0 or more) doc comments and runnableExamples.
+  ]##
+  result = n[bodyPos]
+
+  # This won't be transformed: result.id = 10. Namely result[0].kind != nkSym.
+  if result.kind == nkAsgn and result[0].kind == nkSym and
+                               n.len > bodyPos+1 and n[bodyPos+1].kind == nkSym:
+    doAssert result.len == 2
+    result = result[1]
+
+proc getAllRunnableExamples(d: PDoc, n: PNode, dest: var ItemPre) =
+  var n = n
+  var state = rsStart
+  template fn(n2, topLevel) =
+    state = getAllRunnableExamplesImpl(d, n2, dest, state, topLevel)
+  dest.add genComment(d, n)
+  case n.kind
+  of routineDefs:
+    n = n.getRoutineBody
+    case n.kind
+    of nkCommentStmt, nkCallKinds: fn(n, topLevel = false)
+    else:
+      for i in 0..<n.safeLen:
+        fn(n[i], topLevel = false)
+        if state == rsDone: discard # check all sons
+  else: fn(n, topLevel = true)
 
-proc isVisible(n: PNode): bool =
+proc isVisible(d: PDoc; n: PNode): bool =
   result = false
   if n.kind == nkPostfix:
-    if n.len == 2 and n.sons[0].kind == nkIdent:
-      var v = n.sons[0].ident
+    if n.len == 2 and n[0].kind == nkIdent:
+      var v = n[0].ident
       result = v.id == ord(wStar) or v.id == ord(wMinus)
   elif n.kind == nkSym:
     # we cannot generate code for forwarded symbols here as we have no
     # exception tracking information here. Instead we copy over the comment
     # from the proc header.
-    result = {sfExported, sfFromGeneric, sfForward}*n.sym.flags == {sfExported}
+    if optDocInternal in d.conf.globalOptions:
+      result = {sfFromGeneric, sfForward}*n.sym.flags == {}
+    else:
+      result = {sfExported, sfFromGeneric, sfForward}*n.sym.flags == {sfExported}
+    if result and containsOrIncl(d.emitted, n.sym.id):
+      result = false
   elif n.kind == nkPragmaExpr:
-    result = isVisible(n.sons[0])
+    result = isVisible(d, n[0])
 
-proc getName(d: PDoc, n: PNode, splitAfter = -1): string =
+proc getName(n: PNode): string =
   case n.kind
-  of nkPostfix: result = getName(d, n.sons[1], splitAfter)
-  of nkPragmaExpr: result = getName(d, n.sons[0], splitAfter)
-  of nkSym: result = esc(d.target, n.sym.renderDefinitionName, splitAfter)
-  of nkIdent: result = esc(d.target, n.ident.s, splitAfter)
+  of nkPostfix: result = getName(n[1])
+  of nkPragmaExpr: result = getName(n[0])
+  of nkSym: result = n.sym.renderDefinitionName
+  of nkIdent: result = n.ident.s
   of nkAccQuoted:
-    result = esc(d.target, "`")
-    for i in 0..<n.len: result.add(getName(d, n[i], splitAfter))
-    result.add esc(d.target, "`")
-  of nkOpenSymChoice, nkClosedSymChoice:
-    result = getName(d, n[0], splitAfter)
+    result = "`"
+    for i in 0..<n.len: result.add(getName(n[i]))
+    result = "`"
+  of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
+    result = getName(n[0])
   else:
     result = ""
 
+proc getNameEsc(d: PDoc, n: PNode): string =
+  esc(d.target, getName(n))
+
 proc getNameIdent(cache: IdentCache; n: PNode): PIdent =
   case n.kind
-  of nkPostfix: result = getNameIdent(cache, n.sons[1])
-  of nkPragmaExpr: result = getNameIdent(cache, n.sons[0])
+  of nkPostfix: result = getNameIdent(cache, n[1])
+  of nkPragmaExpr: result = getNameIdent(cache, n[0])
   of nkSym: result = n.sym.name
   of nkIdent: result = n.ident
   of nkAccQuoted:
     var r = ""
     for i in 0..<n.len: r.add(getNameIdent(cache, n[i]).s)
     result = getIdent(cache, r)
-  of nkOpenSymChoice, nkClosedSymChoice:
+  of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
     result = getNameIdent(cache, n[0])
   else:
     result = nil
 
 proc getRstName(n: PNode): PRstNode =
   case n.kind
-  of nkPostfix: result = getRstName(n.sons[1])
-  of nkPragmaExpr: result = getRstName(n.sons[0])
-  of nkSym: result = newRstNode(rnLeaf, n.sym.renderDefinitionName)
-  of nkIdent: result = newRstNode(rnLeaf, n.ident.s)
+  of nkPostfix: result = getRstName(n[1])
+  of nkPragmaExpr: result = getRstName(n[0])
+  of nkSym: result = newRstLeaf(n.sym.renderDefinitionName)
+  of nkIdent: result = newRstLeaf(n.ident.s)
   of nkAccQuoted:
-    result = getRstName(n.sons[0])
-    for i in 1 ..< n.len: result.text.add(getRstName(n[i]).text)
-  of nkOpenSymChoice, nkClosedSymChoice:
+    result = getRstName(n[0])
+    for i in 1..<n.len: result.text.add(getRstName(n[i]).text)
+  of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
     result = getRstName(n[0])
   else:
     result = nil
@@ -389,10 +877,8 @@ proc newUniquePlainSymbol(d: PDoc, original: string): string =
     result = original
     d.seenSymbols[original] = ""
     return
-
   # Iterate over possible numeric variants of the original name.
   var count = 2
-
   while true:
     result = original & "_" & $count
     if not d.seenSymbols.hasKey(result):
@@ -400,7 +886,6 @@ proc newUniquePlainSymbol(d: PDoc, original: string): string =
       break
     count += 1
 
-
 proc complexName(k: TSymKind, n: PNode, baseName: string): string =
   ## Builds a complex unique href name for the node.
   ##
@@ -412,36 +897,28 @@ proc complexName(k: TSymKind, n: PNode, baseName: string): string =
   ## types will be added with a preceding dash. Return types won't be added.
   ##
   ## If you modify the output of this proc, please update the anchor generation
-  ## section of ``doc/docgen.txt``.
+  ## section of ``doc/docgen.rst``.
   result = baseName
-  case k:
-  of skProc, skFunc: result.add(defaultParamSeparator)
-  of skMacro: result.add(".m" & defaultParamSeparator)
-  of skMethod: result.add(".e" & defaultParamSeparator)
-  of skIterator: result.add(".i" & defaultParamSeparator)
-  of skTemplate: result.add(".t" & defaultParamSeparator)
-  of skConverter: result.add(".c" & defaultParamSeparator)
+  case k
+  of skProc, skFunc: discard
+  of skMacro: result.add(".m")
+  of skMethod: result.add(".e")
+  of skIterator: result.add(".i")
+  of skTemplate: result.add(".t")
+  of skConverter: result.add(".c")
   else: discard
-
-  if len(n) > paramsPos and n[paramsPos].kind == nkFormalParams:
-    result.add(renderParamTypes(n[paramsPos]))
-
-
-proc isCallable(n: PNode): bool =
-  ## Returns true if `n` contains a callable node.
-  case n.kind
-  of nkProcDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef,
-    nkConverterDef, nkFuncDef: result = true
-  else:
-    result = false
-
+  if n.safeLen > paramsPos and n[paramsPos].kind == nkFormalParams:
+    let params = renderParamTypes(n[paramsPos])
+    if params.len > 0:
+      result.add(defaultParamSeparator)
+      result.add(params)
 
 proc docstringSummary(rstText: string): string =
   ## Returns just the first line or a brief chunk of text from a rst string.
   ##
   ## Most docstrings will contain a one liner summary, so stripping at the
   ## first newline is usually fine. If after that the content is still too big,
-  ## it is stripped at the first comma, colon or dot, usual english sentence
+  ## it is stripped at the first comma, colon or dot, usual English sentence
   ## separators.
   ##
   ## No guarantees are made on the size of the output, but it should be small.
@@ -452,7 +929,7 @@ proc docstringSummary(rstText: string): string =
   result = rstText.substr(2).strip
   var pos = result.find('\L')
   if pos > 0:
-    result.delete(pos, result.len - 1)
+    result.setLen(pos - 1)
     result.add("…")
   if pos < maxDocstringChars:
     return
@@ -460,114 +937,323 @@ proc docstringSummary(rstText: string): string =
   pos = result.find({'.', ',', ':'})
   let last = result.len - 1
   if pos > 0 and pos < last:
-    result.delete(pos, last)
+    result.setLen(pos - 1)
     result.add("…")
 
+proc genDeprecationMsg(d: PDoc, n: PNode): string =
+  ## Given a nkPragma wDeprecated node output a well-formatted section
+  if n == nil: return
+
+  case n.safeLen:
+  of 0: # Deprecated w/o any message
+    result = getConfigVar(d.conf, "doc.deprecationmsg") % [
+       "label" , "Deprecated", "message", ""]
+  of 2: # Deprecated w/ a message
+    if n[1].kind in {nkStrLit..nkTripleStrLit}:
+      result = getConfigVar(d.conf, "doc.deprecationmsg") % [
+          "label", "Deprecated:", "message", xmltree.escape(n[1].strVal)]
+    else:
+      result = ""
+  else:
+    raiseAssert "unreachable"
+
+type DocFlags = enum
+  kDefault
+  kForceExport
+
+proc genSeeSrc(d: PDoc, path: string, line: int): string =
+  result = ""
+  let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc")
+  if docItemSeeSrc.len > 0:
+    let path = relativeTo(AbsoluteFile path, AbsoluteDir getCurrentDir(), '/')
+    when false:
+      let cwd = canonicalizePath(d.conf, getCurrentDir())
+      var path = path
+      if path.startsWith(cwd):
+        path = path[cwd.len+1..^1].replace('\\', '/')
+    let gitUrl = getConfigVar(d.conf, "git.url")
+    if gitUrl.len > 0:
+      let defaultBranch =
+        if NimPatch mod 2 == 1: "devel"
+        else: "version-$1-$2" % [$NimMajor, $NimMinor]
+      let commit = getConfigVar(d.conf, "git.commit", defaultBranch)
+      let develBranch = getConfigVar(d.conf, "git.devel", "devel")
+      dispA(d.conf, result, "$1", "", [docItemSeeSrc % [
+          "path", path.string, "line", $line, "url", gitUrl,
+          "commit", commit, "devel", develBranch]])
+
+proc symbolPriority(k: TSymKind): int =
+  result = case k
+    of skMacro: -3
+    of skTemplate: -2
+    of skIterator: -1
+    else: 0  # including skProc which have higher priority
+    # documentation itself has even higher priority 1
+
+proc getTypeKind(n: PNode): string =
+  case n[2].kind
+  of nkEnumTy: "enum"
+  of nkObjectTy: "object"
+  of nkTupleTy: "tuple"
+  else: ""
+
+proc toLangSymbol(k: TSymKind, n: PNode, baseName: string): LangSymbol =
+  ## Converts symbol info (names/types/parameters) in `n` into format
+  ## `LangSymbol` convenient for ``rst.nim``/``dochelpers.nim``.
+  result = LangSymbol(name: baseName.nimIdentNormalize,
+      symKind: k.toHumanStr
+  )
+  if k in routineKinds:
+    var
+      paramTypes: seq[string] = @[]
+    renderParamTypes(paramTypes, n[paramsPos], toNormalize=true)
+    let paramNames = renderParamNames(n[paramsPos], toNormalize=true)
+    # In some rare cases (system.typeof) parameter type is not set for default:
+    doAssert paramTypes.len <= paramNames.len
+    for i in 0 ..< paramNames.len:
+      if i < paramTypes.len:
+        result.parameters.add (paramNames[i], paramTypes[i])
+      else:
+        result.parameters.add (paramNames[i], "")
+    result.parametersProvided = true
+
+    result.outType = renderOutType(n[paramsPos], toNormalize=true)
 
-proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
-  if not isVisible(nameNode): return
+  if k in {skProc, skFunc, skType, skIterator}:
+    # Obtain `result.generics`
+    # Use `n[miscPos]` since n[genericParamsPos] does not contain constraints
+    var genNode: PNode = nil
+    if k == skType:
+      genNode = n[1]  # FIXME: what is index 1?
+    else:
+      if n[miscPos].kind != nkEmpty:
+        genNode = n[miscPos][1]   # FIXME: what is index 1?
+    if genNode != nil:
+      var literal = ""
+      var r: TSrcGen = initTokRender(genNode, {renderNoBody, renderNoComments,
+        renderNoPragmas, renderNoProcDefs, renderExpandUsing, renderNoPostfix})
+      var kind = tkEof
+      while true:
+        getNextTok(r, kind, literal)
+        if kind == tkEof:
+          break
+        if kind != tkSpaces:
+          result.generics.add(literal.nimIdentNormalize)
+
+  if k == skType: result.symTypeKind = getTypeKind(n)
+
+proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonExports: bool = false) =
+  if (docFlags != kForceExport) and not isVisible(d, nameNode): return
   let
-    name = getName(d, nameNode)
-    nameRope = name.rope
+    name = getName(nameNode)
+    nameEsc = esc(d.target, name)
   var plainDocstring = getPlainDocstring(n) # call here before genRecComment!
-  var result: Rope = nil
+  var result = ""
   var literal, plainName = ""
   var kind = tkEof
-  var comm = genRecComment(d, n)  # call this here for the side-effect!
-  getAllRunnableExamples(d, n, comm)
-  var r: TSrcGen
+  var comm: ItemPre = default(ItemPre)
+  if n.kind in routineDefs:
+    getAllRunnableExamples(d, n, comm)
+  else:
+    comm.add genRecComment(d, n)
+
   # Obtain the plain rendered string for hyperlink titles.
-  initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments,
-    renderNoPragmas, renderNoProcDefs})
+  var r: TSrcGen = initTokRender(n, {renderNoBody, renderNoComments, renderDocComments,
+    renderNoPragmas, renderNoProcDefs, renderExpandUsing, renderNoPostfix})
   while true:
     getNextTok(r, kind, literal)
     if kind == tkEof:
       break
     plainName.add(literal)
 
-  # Render the HTML hyperlink.
-  nodeToHighlightedHtml(d, n, result, {renderNoBody, renderNoComments, renderDocComments})
+  var pragmaNode = getDeclPragma(n)
+  if pragmaNode != nil: pragmaNode = findPragma(pragmaNode, wDeprecated)
 
   inc(d.id)
   let
-    plainNameRope = rope(xmltree.escape(plainName.strip))
+    plainNameEsc = esc(d.target, plainName.strip)
+    typeDescr =
+      if k == skType and getTypeKind(n) != "": getTypeKind(n)
+      else: k.toHumanStr
+    detailedName = typeDescr & " " & (
+        if k in routineKinds: plainName else: name)
+    uniqueName = if k in routineKinds: plainNameEsc else: nameEsc
+    sortName = if k in routineKinds: plainName.strip else: name
     cleanPlainSymbol = renderPlainSymbolName(nameNode)
     complexSymbol = complexName(k, n, cleanPlainSymbol)
-    plainSymbolRope = rope(cleanPlainSymbol)
-    plainSymbolEncRope = rope(encodeUrl(cleanPlainSymbol))
-    itemIDRope = rope(d.id)
+    plainSymbolEnc = encodeUrl(cleanPlainSymbol, usePlus = false)
     symbolOrId = d.newUniquePlainSymbol(complexSymbol)
-    symbolOrIdRope = symbolOrId.rope
-    symbolOrIdEncRope = encodeUrl(symbolOrId).rope
-
-  var seeSrcRope: Rope = nil
-  let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc")
-  if docItemSeeSrc.len > 0:
-    let cwd = canonicalizePath(d.conf, getCurrentDir())
-    var path = toFullPath(d.conf, n.info)
-    if path.startsWith(cwd):
-      path = path[cwd.len+1 .. ^1].replace('\\', '/')
-    let gitUrl = getConfigVar(d.conf, "git.url")
-    if gitUrl.len > 0:
-      var commit = getConfigVar(d.conf, "git.commit")
-      if commit.len == 0: commit = "master"
-      dispA(d.conf, seeSrcRope, "$1", "", [ropeFormatNamedVars(d.conf, docItemSeeSrc,
-          ["path", "line", "url", "commit"], [rope path,
-          rope($n.info.line), rope gitUrl,
-          rope commit])])
-
-  add(d.section[k], ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.item"),
-    ["name", "header", "desc", "itemID", "header_plain", "itemSym",
-      "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "seeSrc"],
-    [nameRope, result, comm, itemIDRope, plainNameRope, plainSymbolRope,
-      symbolOrIdRope, plainSymbolEncRope, symbolOrIdEncRope, seeSrcRope]))
-
-  var attype: Rope
-  if k in routineKinds and nameNode.kind == nkSym:
+    symbolOrIdEnc = encodeUrl(symbolOrId, usePlus = false)
+    deprecationMsg = genDeprecationMsg(d, pragmaNode)
+    rstLangSymbol = toLangSymbol(k, n, cleanPlainSymbol)
+    symNameNode =
+      if nameNode.kind == nkPostfix: nameNode[1]
+      else: nameNode
+
+  # we generate anchors automatically for subsequent use in doc comments
+  let lineinfo = rstast.TLineInfo(
+      line: nameNode.info.line, col: nameNode.info.col,
+      fileIndex: addRstFileIndex(d, nameNode.info))
+  addAnchorNim(d.sharedState, external = false, refn = symbolOrId,
+               tooltip = detailedName, langSym = rstLangSymbol,
+               priority = symbolPriority(k), info = lineinfo,
+               module = addRstFileIndex(d, FileIndex d.module.position))
+
+  var renderFlags = {renderNoBody, renderNoComments, renderDocComments,
+    renderSyms, renderExpandUsing, renderNoPostfix}
+  if nonExports:
+    renderFlags.incl renderNonExportedFields
+  nodeToHighlightedHtml(d, n, result, renderFlags, symbolOrIdEnc)
+
+  let seeSrc = genSeeSrc(d, toFullPath(d.conf, n.info), n.info.line.int)
+
+  d.section[k].secItems.mgetOrPut(cleanPlainSymbol, newSeq[Item]()).add Item(
+    descRst: comm,
+    sortName: sortName,
+    info: lineinfo,
+    anchor: symbolOrId,
+    detailedName: detailedName,
+    name: name,
+    substitutions: @[
+     "uniqueName", uniqueName,
+     "header", result, "itemID", $d.id,
+     "header_plain", plainNameEsc, "itemSym", cleanPlainSymbol,
+     "itemSymEnc", plainSymbolEnc,
+     "itemSymOrIDEnc", symbolOrIdEnc, "seeSrc", seeSrc,
+     "deprecationMsg", deprecationMsg])
+
+  let external = d.destFile.AbsoluteFile.relativeTo(d.conf.outDir, '/').changeFileExt(HtmlExt).string
+
+  var attype = ""
+  if k in routineKinds and symNameNode.kind == nkSym:
     let att = attachToType(d, nameNode.sym)
     if att != nil:
-      attype = rope esc(d.target, att.name.s)
-  add(d.toc[k], ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.item.toc"),
-    ["name", "header", "desc", "itemID", "header_plain", "itemSym",
-      "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "attype"],
-    [rope(getName(d, nameNode, d.splitAfter)), result, comm,
-      itemIDRope, plainNameRope, plainSymbolRope, symbolOrIdRope,
-      plainSymbolEncRope, symbolOrIdEncRope, attype]))
-
-  # Ironically for types the complexSymbol is *cleaner* than the plainName
-  # because it doesn't include object fields or documentation comments. So we
-  # use the plain one for callable elements, and the complex for the rest.
-  var linkTitle = changeFileExt(extractFilename(d.filename), "") & " : "
-  if n.isCallable: linkTitle.add(xmltree.escape(plainName.strip))
-  else: linkTitle.add(xmltree.escape(complexSymbol.strip))
-
-  setIndexTerm(d[], symbolOrId, name, linkTitle,
-    xmltree.escape(plainDocstring.docstringSummary))
-  if k == skType and nameNode.kind == nkSym:
-    d.types.strTableAdd nameNode.sym
-
-proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
-  if not isVisible(nameNode): return
+      attype = esc(d.target, att.name.s)
+  elif k == skType and symNameNode.kind == nkSym and
+      symNameNode.sym.typ.kind in {tyEnum, tyBool}:
+    let etyp = symNameNode.sym.typ
+    for e in etyp.n:
+      if e.sym.kind != skEnumField: continue
+      let plain = renderPlainSymbolName(e)
+      let symbolOrId = d.newUniquePlainSymbol(plain)
+      setIndexTerm(d[], ieNim, htmlFile = external, id = symbolOrId,
+                   term = plain, linkTitle = symNameNode.sym.name.s & '.' & plain,
+                   linkDesc = xmltree.escape(getPlainDocstring(e).docstringSummary),
+                   line = n.info.line.int)
+
+  d.tocSimple[k].add TocItem(
+    sortName: sortName,
+    content: getConfigVar(d.conf, "doc.item.toc") % [
+      "name", name, "header_plain", plainNameEsc,
+      "itemSymOrIDEnc", symbolOrIdEnc])
+
+  d.tocTable[k].mgetOrPut(cleanPlainSymbol, newSeq[TocItem]()).add TocItem(
+    sortName: sortName,
+    content: getConfigVar(d.conf, "doc.item.tocTable") % [
+      "name", name, "header_plain", plainNameEsc,
+      "itemSymOrID", symbolOrId.replace(",", ",<wbr>"),
+      "itemSymOrIDEnc", symbolOrIdEnc])
+
+  setIndexTerm(d[], ieNim, htmlFile = external, id = symbolOrId, term = name,
+               linkTitle = detailedName,
+               linkDesc = xmltree.escape(plainDocstring.docstringSummary),
+               line = n.info.line.int)
+  if k == skType and symNameNode.kind == nkSym:
+    d.types.strTableAdd symNameNode.sym
+
+proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false): JsonItem =
+  if not isVisible(d, nameNode): return
   var
-    name = getName(d, nameNode)
-    comm = $genRecComment(d, n)
+    name = getNameEsc(d, nameNode)
+    comm = genRecComment(d, n)
     r: TSrcGen
+    renderFlags = {renderNoBody, renderNoComments, renderDocComments,
+      renderExpandUsing, renderNoPostfix}
+  if nonExports:
+    renderFlags.incl renderNonExportedFields
+  r = initTokRender(n, renderFlags)
+  result = JsonItem(json: %{ "name": %name, "type": %($k), "line": %n.info.line.int,
+                   "col": %n.info.col}
+  )
+  if comm != nil:
+    result.rst = comm
+    result.rstField = "description"
+  if r.buf.len > 0:
+    result.json["code"] = %r.buf
+  if k in routineKinds:
+    result.json["signature"] = newJObject()
+    if n[paramsPos][0].kind != nkEmpty:
+      result.json["signature"]["return"] = %($n[paramsPos][0])
+    if n[paramsPos].len > 1:
+      result.json["signature"]["arguments"] = newJArray()
+    for paramIdx in 1 ..< n[paramsPos].len:
+      for identIdx in 0 ..< n[paramsPos][paramIdx].len - 2:
+        let
+          paramName = $n[paramsPos][paramIdx][identIdx]
+          paramType = $n[paramsPos][paramIdx][^2]
+        if n[paramsPos][paramIdx][^1].kind != nkEmpty:
+          let paramDefault = $n[paramsPos][paramIdx][^1]
+          result.json["signature"]["arguments"].add %{"name": %paramName, "type": %paramType, "default": %paramDefault}
+        else:
+          result.json["signature"]["arguments"].add %{"name": %paramName, "type": %paramType}
+    if n[pragmasPos].kind != nkEmpty:
+      result.json["signature"]["pragmas"] = newJArray()
+      for pragma in n[pragmasPos]:
+        result.json["signature"]["pragmas"].add %($pragma)
+    if n[genericParamsPos].kind != nkEmpty:
+      result.json["signature"]["genericParams"] = newJArray()
+      for genericParam in n[genericParamsPos]:
+        var param = %{"name": %($genericParam)}
+        if genericParam.sym.typ.len > 0:
+          param["types"] = newJArray()
+          param["types"] = %($genericParam.sym.typ.elementType)
+        result.json["signature"]["genericParams"].add param
+  if optGenIndex in d.conf.globalOptions:
+    genItem(d, n, nameNode, k, kForceExport)
 
-  initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
-
-  result = %{ "name": %name, "type": %($k), "line": %n.info.line.int,
-                 "col": %n.info.col}
-  if comm != nil and comm != "":
-    result["description"] = %comm
-  if r.buf != nil:
-    result["code"] = %r.buf
+proc setDoctype(d: PDoc, n: PNode) =
+  ## Processes `{.doctype.}` pragma changing Markdown/RST parsing options.
+  if n == nil:
+    return
+  if n.len != 2:
+    localError(d.conf, n.info, errUser,
+      "doctype pragma takes exactly 1 argument"
+    )
+    return
+  var dt = ""
+  case n[1].kind
+  of nkStrLit:
+    dt = toLowerAscii(n[1].strVal)
+  of nkIdent:
+    dt = toLowerAscii(n[1].ident.s)
+  else:
+    localError(d.conf, n.info, errUser,
+      "unknown argument type $1 provided to doctype" % [$n[1].kind]
+    )
+    return
+  case dt
+  of "markdown":
+    d.sharedState.options.incl roSupportMarkdown
+    d.sharedState.options.incl roPreferMarkdown
+  of "rstmarkdown":
+    d.sharedState.options.incl roSupportMarkdown
+    d.sharedState.options.excl roPreferMarkdown
+  of "rst":
+    d.sharedState.options.excl roSupportMarkdown
+    d.sharedState.options.excl roPreferMarkdown
+  else:
+    localError(d.conf, n.info, errUser,
+      (
+        "unknown doctype value \"$1\", should be from " &
+        "\"RST\", \"Markdown\", \"RstMarkdown\""
+      ) % [dt]
+    )
 
 proc checkForFalse(n: PNode): bool =
   result = n.kind == nkIdent and cmpIgnoreStyle(n.ident.s, "false") == 0
 
 proc traceDeps(d: PDoc, it: PNode) =
   const k = skModule
-
   if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
     let sep = it[0]
     let dir = it[1]
@@ -576,328 +1262,693 @@ proc traceDeps(d: PDoc, it: PNode) =
     a.add dir
     a.add sep # dummy entry, replaced in the loop
     for x in it[2]:
-      a.sons[2] = x
+      a[2] = x
       traceDeps(d, a)
+  elif it.kind == nkSym and belongsToProjectPackage(d.conf, it.sym):
+    let external = externalDep(d, it.sym)
+    if d.section[k].finalMarkup != "": d.section[k].finalMarkup.add(", ")
+    dispA(d.conf, d.section[k].finalMarkup,
+          "<a class=\"reference external\" href=\"$2\">$1</a>",
+          "$1", [esc(d.target, external.prettyLink),
+                 changeFileExt(external, "html")])
+
+proc exportSym(d: PDoc; s: PSym) =
+  const k = exportSection
+  if s.kind == skModule and belongsToProjectPackage(d.conf, s):
+    let external = externalDep(d, s)
+    if d.section[k].finalMarkup != "": d.section[k].finalMarkup.add(", ")
+    dispA(d.conf, d.section[k].finalMarkup,
+          "<a class=\"reference external\" href=\"$2\">$1</a>",
+          "$1", [esc(d.target, external.prettyLink),
+                 changeFileExt(external, "html")])
+  elif s.kind != skModule and s.owner != nil:
+    let module = originatingModule(s)
+    if belongsToProjectPackage(d.conf, module):
+      let
+        complexSymbol = complexName(s.kind, s.ast, s.name.s)
+        symbolOrId = d.newUniquePlainSymbol(complexSymbol)
+        external = externalDep(d, module)
+      if d.section[k].finalMarkup != "": d.section[k].finalMarkup.add(", ")
+      # XXX proper anchor generation here
+      dispA(d.conf, d.section[k].finalMarkup,
+            "<a href=\"$2#$3\"><span class=\"Identifier\">$1</span></a>",
+            "$1", [esc(d.target, s.name.s),
+                   changeFileExt(external, "html"),
+                   symbolOrId])
+
+proc documentNewEffect(cache: IdentCache; n: PNode): PNode =
+  let s = n[namePos].sym
+  if tfReturnsNew in s.typ.flags:
+    result = newIdentNode(getIdent(cache, "new"), n.info)
   else:
-    if d.section[k] != nil: add(d.section[k], ", ")
-    dispA(d.conf, d.section[k],
-          "<a class=\"reference external\" href=\"$1.html\">$1</a>",
-          "$1", [rope(getModuleName(d.conf, it))])
+    result = nil
 
-proc generateDoc*(d: PDoc, n: PNode) =
+proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, idx: int): PNode =
+  let spec = effectSpec(x, effectType)
+  if isNil(spec):
+    let s = n[namePos].sym
+
+    let actual = s.typ.n[0]
+    if actual.len != effectListLen: return
+    let real = actual[idx]
+    if real == nil: return
+    let realLen = real.len
+    # warning: hack ahead:
+    var effects = newNodeI(nkBracket, n.info, realLen)
+    for i in 0..<realLen:
+      var t = typeToString(real[i].typ)
+      if t.startsWith("ref "): t = substr(t, 4)
+      effects[i] = newIdentNode(getIdent(cache, t), n.info)
+      # set the type so that the following analysis doesn't screw up:
+      effects[i].typ = real[i].typ
+
+    result = newTreeI(nkExprColonExpr, n.info,
+      newIdentNode(getIdent(cache, $effectType), n.info), effects)
+  else:
+    result = nil
+
+proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode =
+  let s = n[namePos].sym
+  let params = s.typ.n
+
+  var effects = newNodeI(nkBracket, n.info)
+  for i in 1..<params.len:
+    if params[i].kind == nkSym and flag in params[i].sym.flags:
+      effects.add params[i]
+
+  if effects.len > 0:
+    result = newTreeI(nkExprColonExpr, n.info,
+      newIdentNode(getIdent(cache, pragmaName), n.info), effects)
+  else:
+    result = nil
+
+proc documentRaises*(cache: IdentCache; n: PNode) =
+  if n[namePos].kind != nkSym: return
+  let pragmas = n[pragmasPos]
+  let p1 = documentEffect(cache, n, pragmas, wRaises, exceptionEffects)
+  let p2 = documentEffect(cache, n, pragmas, wTags, tagEffects)
+  let p3 = documentWriteEffect(cache, n, sfWrittenTo, "writes")
+  let p4 = documentNewEffect(cache, n)
+  let p5 = documentWriteEffect(cache, n, sfEscapes, "escapes")
+  let p6 = documentEffect(cache, n, pragmas, wForbids, forbiddenEffects)
+
+  if p1 != nil or p2 != nil or p3 != nil or p4 != nil or p5 != nil or p6 != nil:
+    if pragmas.kind == nkEmpty:
+      n[pragmasPos] = newNodeI(nkPragma, n.info)
+    if p1 != nil: n[pragmasPos].add p1
+    if p2 != nil: n[pragmasPos].add p2
+    if p3 != nil: n[pragmasPos].add p3
+    if p4 != nil: n[pragmasPos].add p4
+    if p5 != nil: n[pragmasPos].add p5
+    if p6 != nil: n[pragmasPos].add p6
+
+proc generateDoc*(d: PDoc, n, orig: PNode, config: ConfigRef, docFlags: DocFlags = kDefault) =
+  ## Goes through nim nodes recursively and collects doc comments.
+  ## Main function for `doc`:option: command,
+  ## which is implemented in ``docgen2.nim``.
+  template genItemAux(skind) =
+    genItem(d, n, n[namePos], skind, docFlags)
+  let showNonExports = optShowNonExportedFields in config.globalOptions
   case n.kind
-  of nkCommentStmt: add(d.modDesc, genComment(d, n))
-  of nkProcDef:
-    when useEffectSystem: documentRaises(d.cache, n)
-    genItem(d, n, n.sons[namePos], skProc)
-  of nkFuncDef:
+  of nkPragma:
+    let pragmaNode = findPragma(n, wDeprecated)
+    d.modDeprecationMsg.add(genDeprecationMsg(d, pragmaNode))
+    let doctypeNode = findPragma(n, wDoctype)
+    setDoctype(d, doctypeNode)
+  of nkCommentStmt: d.modDescPre.add(genComment(d, n))
+  of nkProcDef, nkFuncDef:
     when useEffectSystem: documentRaises(d.cache, n)
-    genItem(d, n, n.sons[namePos], skFunc)
+    genItemAux(skProc)
   of nkMethodDef:
     when useEffectSystem: documentRaises(d.cache, n)
-    genItem(d, n, n.sons[namePos], skMethod)
+    genItemAux(skMethod)
   of nkIteratorDef:
     when useEffectSystem: documentRaises(d.cache, n)
-    genItem(d, n, n.sons[namePos], skIterator)
-  of nkMacroDef: genItem(d, n, n.sons[namePos], skMacro)
-  of nkTemplateDef: genItem(d, n, n.sons[namePos], skTemplate)
+    genItemAux(skIterator)
+  of nkMacroDef: genItemAux(skMacro)
+  of nkTemplateDef: genItemAux(skTemplate)
   of nkConverterDef:
     when useEffectSystem: documentRaises(d.cache, n)
-    genItem(d, n, n.sons[namePos], skConverter)
+    genItemAux(skConverter)
   of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
-    for i in countup(0, sonsLen(n) - 1):
-      if n.sons[i].kind != nkCommentStmt:
+    for i in 0..<n.len:
+      if n[i].kind != nkCommentStmt:
         # order is always 'type var let const':
-        genItem(d, n.sons[i], n.sons[i].sons[0],
-                succ(skType, ord(n.kind)-ord(nkTypeSection)))
+        genItem(d, n[i], n[i][0],
+                succ(skType, ord(n.kind)-ord(nkTypeSection)), docFlags, showNonExports)
   of nkStmtList:
-    for i in countup(0, sonsLen(n) - 1): generateDoc(d, n.sons[i])
+    for i in 0..<n.len: generateDoc(d, n[i], orig, config)
   of nkWhenStmt:
     # generate documentation for the first branch only:
-    if not checkForFalse(n.sons[0].sons[0]):
-      generateDoc(d, lastSon(n.sons[0]))
+    if not checkForFalse(n[0][0]):
+      generateDoc(d, lastSon(n[0]), orig, config)
   of nkImportStmt:
-    for i in 0 .. sonsLen(n)-1: traceDeps(d, n.sons[i])
-  of nkFromStmt, nkImportExceptStmt: traceDeps(d, n.sons[0])
+    for it in n: traceDeps(d, it)
+  of nkExportStmt:
+    for it in n:
+      # bug #23051; don't generate documentation for exported symbols again
+      if it.kind == nkSym and sfExported notin it.sym.flags:
+        if d.module != nil and d.module == it.sym.owner:
+          generateDoc(d, it.sym.ast, orig, config, kForceExport)
+        elif it.sym.ast != nil:
+          exportSym(d, it.sym)
+  of nkExportExceptStmt: discard "transformed into nkExportStmt by semExportExcept"
+  of nkFromStmt, nkImportExceptStmt: traceDeps(d, n[0])
+  of nkCallKinds:
+    var comm: ItemPre = default(ItemPre)
+    getAllRunnableExamples(d, n, comm)
+    if comm.len != 0: d.modDescPre.add(comm)
   else: discard
 
-proc add(d: PDoc; j: JsonNode) =
-  if j != nil: d.jArray.add j
+proc overloadGroupName(s: string, k: TSymKind): string =
+  ## Turns a name like `f` into anchor `f-procs-all`
+  s & "-" & k.toHumanStr & "s-all"
+
+proc setIndexTitle(d: PDoc, useMetaTitle: bool) =
+  let titleKind = if d.standaloneDoc: ieMarkupTitle else: ieNimTitle
+  let external = AbsoluteFile(d.destFile)
+    .relativeTo(d.conf.outDir, '/')
+    .changeFileExt(HtmlExt)
+    .string
+  var term, linkTitle: string
+  if useMetaTitle and d.meta[metaTitle].len != 0:
+    term = d.meta[metaTitleRaw]
+    linkTitle = d.meta[metaTitleRaw]
+  else:
+    let filename = extractFilename(d.filename)
+    term =
+      if d.standaloneDoc: filename  # keep .rst/.md extension
+      else: changeFileExt(filename, "")  # rm .nim extension
+    linkTitle =
+      if d.standaloneDoc: term  # keep .rst/.md extension
+      else: canonicalImport(d.conf, AbsoluteFile d.filename)
+  if not d.standaloneDoc:
+    linkTitle = "module " & linkTitle
+  setIndexTerm(d[], titleKind, htmlFile = external, id = "",
+               term = term, linkTitle = linkTitle)
+
+proc finishGenerateDoc*(d: var PDoc) =
+  ## Perform 2nd RST pass for resolution of links/footnotes/headings...
+  # copy file map `filenames` to ``rstgen.nim`` for its warnings
+  d.filenames = d.sharedState.filenames
+
+  # Main title/subtitle are allowed only in the first RST fragment of document
+  var firstRst = PRstNode(nil)
+  for fragment in d.modDescPre:
+    if fragment.isRst:
+      firstRst = fragment.rst
+      break
+  d.hasToc = d.hasToc or d.sharedState.hasToc
+  # in --index:only mode we do NOT want to load other .idx, only write ours:
+  let importdoc = optGenIndexOnly notin d.conf.globalOptions and
+                  optNoImportdoc notin d.conf.globalOptions
+  preparePass2(d.sharedState, firstRst, importdoc)
+
+  if optGenIndexOnly in d.conf.globalOptions:
+    # Top-level doc.comments may contain titles and :idx: statements:
+    for fragment in d.modDescPre:
+      if fragment.isRst:
+        traverseForIndex(d[], fragment.rst)
+    setIndexTitle(d, useMetaTitle = d.standaloneDoc)
+    # Symbol-associated doc.comments may contain :idx: statements:
+    for k in TSymKind:
+      for _, overloadChoices in d.section[k].secItems:
+        for item in overloadChoices:
+          for fragment in item.descRst:
+            if fragment.isRst:
+              traverseForIndex(d[], fragment.rst)
+
+  # add anchors to overload groups before RST resolution
+  for k in TSymKind:
+    if k in routineKinds:
+      for plainName, overloadChoices in d.section[k].secItems:
+        if overloadChoices.len > 1:
+          let refn = overloadGroupName(plainName, k)
+          let tooltip = "$1 ($2 overloads)" % [
+                      k.toHumanStr & " " & plainName, $overloadChoices.len]
+          let name = nimIdentBackticksNormalize(plainName)
+          # save overload group to ``.idx``
+          let external = d.destFile.AbsoluteFile.relativeTo(d.conf.outDir, '/').
+                         changeFileExt(HtmlExt).string
+          setIndexTerm(d[], ieNimGroup, htmlFile = external, id = refn,
+                       term = name, linkTitle = k.toHumanStr,
+                       linkDesc = "", line = overloadChoices[0].info.line.int)
+          if optGenIndexOnly in d.conf.globalOptions: continue
+          addAnchorNim(d.sharedState, external=false, refn, tooltip,
+                       LangSymbol(symKind: k.toHumanStr,
+                                  name: name,
+                                  isGroup: true),
+                       priority = symbolPriority(k),
+                       # select index `0` just to have any meaningful warning:
+                       info = overloadChoices[0].info,
+                       module = addRstFileIndex(d, FileIndex d.module.position))
+
+  if optGenIndexOnly in d.conf.globalOptions:
+    return
+
+  # Finalize fragments of ``.nim`` or ``.rst`` file
+  proc renderItemPre(d: PDoc, fragments: ItemPre, result: var string) =
+    for f in fragments:
+      case f.isRst:
+      of true:
+        var resolved = resolveSubs(d.sharedState, f.rst)
+        renderRstToOut(d[], resolved, result)
+      of false: result &= f.str
+  proc cmp(x, y: Item): int = cmpDecimalsIgnoreCase(x.sortName, y.sortName)
+  for k in TSymKind:
+    # add symbols to section for each `k`, while optionally wrapping
+    # overloadable items with the same basic name by ``doc.item2``
+    let overloadableNames = toSeq(keys(d.section[k].secItems))
+    for plainName in overloadableNames.sorted(cmpDecimalsIgnoreCase):
+      var overloadChoices = d.section[k].secItems[plainName]
+      overloadChoices.sort(cmp)
+      var nameContent = ""
+      for item in overloadChoices:
+        var itemDesc: string = ""
+        renderItemPre(d, item.descRst, itemDesc)
+        nameContent.add(
+          getConfigVar(d.conf, "doc.item") % (
+              item.substitutions & @[
+                "desc", itemDesc,
+                "name", item.name,
+                "itemSymOrID", item.anchor]))
+      if k in routineKinds:
+        let plainNameEsc1 = esc(d.target, plainName.strip)
+        let plainNameEsc2 = esc(d.target, plainName.strip, escMode=emUrl)
+        d.section[k].finalMarkup.add(
+          getConfigVar(d.conf, "doc.item2") % (
+            @["header_plain", plainNameEsc1,
+              "overloadGroupName", overloadGroupName(plainNameEsc2, k),
+              "content", nameContent]))
+      else:
+        d.section[k].finalMarkup.add(nameContent)
+    d.section[k].secItems.clear
+  renderItemPre(d, d.modDescPre, d.modDescFinal)
+  d.modDescPre.setLen 0
+
+  # Finalize fragments of ``.json`` file
+  for i, entry in d.jEntriesPre:
+    if entry.rst != nil:
+      let resolved = resolveSubs(d.sharedState, entry.rst)
+      var str: string = ""
+      renderRstToOut(d[], resolved, str)
+      entry.json[entry.rstField] = %str
+      d.jEntriesPre[i].rst = nil
+
+    d.jEntriesFinal.add entry.json # generates docs
 
-proc generateJson*(d: PDoc, n: PNode) =
+  setIndexTitle(d, useMetaTitle = d.standaloneDoc)
+  completePass2(d.sharedState)
+
+proc add(d: PDoc; j: JsonItem) =
+  if j.json != nil or j.rst != nil: d.jEntriesPre.add j
+
+proc generateJson*(d: PDoc, n: PNode, config: ConfigRef, includeComments: bool = true) =
   case n.kind
+  of nkPragma:
+    let doctypeNode = findPragma(n, wDoctype)
+    setDoctype(d, doctypeNode)
   of nkCommentStmt:
-    if n.comment != nil and startsWith(n.comment, "##"):
-      let stripped = n.comment.substr(2).strip
-      d.add %{ "comment": %stripped, "line": %n.info.line.int,
-               "col": %n.info.col }
-  of nkProcDef:
-    when useEffectSystem: documentRaises(d.cache, n)
-    d.add genJsonItem(d, n, n.sons[namePos], skProc)
-  of nkFuncDef:
+    if includeComments:
+      d.add JsonItem(rst: genComment(d, n), rstField: "comment",
+                     json: %Table[string, string]())
+    else:
+      d.modDescPre.add(genComment(d, n))
+  of nkProcDef, nkFuncDef:
     when useEffectSystem: documentRaises(d.cache, n)
-    d.add genJsonItem(d, n, n.sons[namePos], skFunc)
+    d.add genJsonItem(d, n, n[namePos], skProc)
   of nkMethodDef:
     when useEffectSystem: documentRaises(d.cache, n)
-    d.add genJsonItem(d, n, n.sons[namePos], skMethod)
+    d.add genJsonItem(d, n, n[namePos], skMethod)
   of nkIteratorDef:
     when useEffectSystem: documentRaises(d.cache, n)
-    d.add genJsonItem(d, n, n.sons[namePos], skIterator)
+    d.add genJsonItem(d, n, n[namePos], skIterator)
   of nkMacroDef:
-    d.add genJsonItem(d, n, n.sons[namePos], skMacro)
+    d.add genJsonItem(d, n, n[namePos], skMacro)
   of nkTemplateDef:
-    d.add genJsonItem(d, n, n.sons[namePos], skTemplate)
+    d.add genJsonItem(d, n, n[namePos], skTemplate)
   of nkConverterDef:
     when useEffectSystem: documentRaises(d.cache, n)
-    d.add genJsonItem(d, n, n.sons[namePos], skConverter)
+    d.add genJsonItem(d, n, n[namePos], skConverter)
   of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
-    for i in countup(0, sonsLen(n) - 1):
-      if n.sons[i].kind != nkCommentStmt:
+    for i in 0..<n.len:
+      if n[i].kind != nkCommentStmt:
         # order is always 'type var let const':
-        d.add genJsonItem(d, n.sons[i], n.sons[i].sons[0],
-                succ(skType, ord(n.kind)-ord(nkTypeSection)))
+        d.add genJsonItem(d, n[i], n[i][0],
+                succ(skType, ord(n.kind)-ord(nkTypeSection)), optShowNonExportedFields in config.globalOptions)
   of nkStmtList:
-    for i in countup(0, sonsLen(n) - 1):
-      generateJson(d, n.sons[i])
+    for i in 0..<n.len:
+      generateJson(d, n[i], config, includeComments)
   of nkWhenStmt:
     # generate documentation for the first branch only:
-    if not checkForFalse(n.sons[0].sons[0]):
-      generateJson(d, lastSon(n.sons[0]))
+    if not checkForFalse(n[0][0]):
+      generateJson(d, lastSon(n[0]), config, includeComments)
   else: discard
 
 proc genTagsItem(d: PDoc, n, nameNode: PNode, k: TSymKind): string =
-  result = getName(d, nameNode) & "\n"
+  result = getNameEsc(d, nameNode) & "\n"
 
-proc generateTags*(d: PDoc, n: PNode, r: var Rope) =
+proc generateTags*(d: PDoc, n: PNode, r: var string) =
   case n.kind
   of nkCommentStmt:
-    if n.comment != nil and startsWith(n.comment, "##"):
+    if startsWith(n.comment, "##"):
       let stripped = n.comment.substr(2).strip
       r.add stripped
   of nkProcDef:
     when useEffectSystem: documentRaises(d.cache, n)
-    r.add genTagsItem(d, n, n.sons[namePos], skProc)
+    r.add genTagsItem(d, n, n[namePos], skProc)
   of nkFuncDef:
     when useEffectSystem: documentRaises(d.cache, n)
-    r.add genTagsItem(d, n, n.sons[namePos], skFunc)
+    r.add genTagsItem(d, n, n[namePos], skFunc)
   of nkMethodDef:
     when useEffectSystem: documentRaises(d.cache, n)
-    r.add genTagsItem(d, n, n.sons[namePos], skMethod)
+    r.add genTagsItem(d, n, n[namePos], skMethod)
   of nkIteratorDef:
     when useEffectSystem: documentRaises(d.cache, n)
-    r.add genTagsItem(d, n, n.sons[namePos], skIterator)
+    r.add genTagsItem(d, n, n[namePos], skIterator)
   of nkMacroDef:
-    r.add genTagsItem(d, n, n.sons[namePos], skMacro)
+    r.add genTagsItem(d, n, n[namePos], skMacro)
   of nkTemplateDef:
-    r.add genTagsItem(d, n, n.sons[namePos], skTemplate)
+    r.add genTagsItem(d, n, n[namePos], skTemplate)
   of nkConverterDef:
     when useEffectSystem: documentRaises(d.cache, n)
-    r.add genTagsItem(d, n, n.sons[namePos], skConverter)
+    r.add genTagsItem(d, n, n[namePos], skConverter)
   of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
-    for i in countup(0, sonsLen(n) - 1):
-      if n.sons[i].kind != nkCommentStmt:
+    for i in 0..<n.len:
+      if n[i].kind != nkCommentStmt:
         # order is always 'type var let const':
-        r.add genTagsItem(d, n.sons[i], n.sons[i].sons[0],
+        r.add genTagsItem(d, n[i], n[i][0],
                 succ(skType, ord(n.kind)-ord(nkTypeSection)))
   of nkStmtList:
-    for i in countup(0, sonsLen(n) - 1):
-      generateTags(d, n.sons[i], r)
+    for i in 0..<n.len:
+      generateTags(d, n[i], r)
   of nkWhenStmt:
     # generate documentation for the first branch only:
-    if not checkForFalse(n.sons[0].sons[0]):
-      generateTags(d, lastSon(n.sons[0]), r)
+    if not checkForFalse(n[0][0]):
+      generateTags(d, lastSon(n[0]), r)
   else: discard
 
-proc genSection(d: PDoc, kind: TSymKind) =
-  const sectionNames: array[skModule..skTemplate, string] = [
+proc genSection(d: PDoc, kind: TSymKind, groupedToc = false) =
+  const sectionNames: array[skModule..skField, string] = [
     "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Funcs",
-    "Methods", "Iterators", "Converters", "Macros", "Templates"
+    "Methods", "Iterators", "Converters", "Macros", "Templates", "Exports"
+  ]
+  if d.section[kind].finalMarkup == "": return
+  var title = sectionNames[kind]
+  d.section[kind].finalMarkup = getConfigVar(d.conf, "doc.section") % [
+      "sectionid", $ord(kind), "sectionTitle", title,
+      "sectionTitleID", $(ord(kind) + 50), "content", d.section[kind].finalMarkup]
+
+  proc cmp(x, y: TocItem): int = cmpDecimalsIgnoreCase(x.sortName, y.sortName)
+  if groupedToc:
+    let overloadableNames = toSeq(keys(d.tocTable[kind]))
+    for plainName in overloadableNames.sorted(cmpDecimalsIgnoreCase):
+      var overloadChoices = d.tocTable[kind][plainName]
+      overloadChoices.sort(cmp)
+      var content: string = ""
+      for item in overloadChoices:
+        content.add item.content
+      d.toc2[kind].add getConfigVar(d.conf, "doc.section.toc2") % [
+          "sectionid", $ord(kind), "sectionTitle", title,
+          "sectionTitleID", $(ord(kind) + 50),
+          "content", content, "plainName", plainName]
+  else:
+    for item in d.tocSimple[kind].sorted(cmp):
+      d.toc2[kind].add item.content
+
+  let sectionValues = @[
+     "sectionID", $ord(kind), "sectionTitleID", $(ord(kind) + 50),
+     "sectionTitle", title
   ]
-  if d.section[kind] == nil: return
-  var title = sectionNames[kind].rope
-  d.section[kind] = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.section"), [
-      "sectionid", "sectionTitle", "sectionTitleID", "content"], [
-      ord(kind).rope, title, rope(ord(kind) + 50), d.section[kind]])
-  d.toc[kind] = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.section.toc"), [
-      "sectionid", "sectionTitle", "sectionTitleID", "content"], [
-      ord(kind).rope, title, rope(ord(kind) + 50), d.toc[kind]])
-
-proc genOutFile(d: PDoc): Rope =
+
+  # Check if the toc has any children
+  if d.toc2[kind] != "":
+    # Use the dropdown version instead and store the children in the dropdown
+    d.toc[kind] = getConfigVar(d.conf, "doc.section.toc") % (sectionValues & @[
+       "content", d.toc2[kind]
+    ])
+  else:
+    # Just have the link
+    d.toc[kind] =  getConfigVar(d.conf, "doc.section.toc_item") % sectionValues
+
+proc relLink(outDir: AbsoluteDir, destFile: AbsoluteFile, linkto: RelativeFile): string =
+  $relativeTo(outDir / linkto, destFile.splitFile().dir, '/')
+
+proc genOutFile(d: PDoc, groupedToc = false): string =
   var
-    code, content: Rope
+    code, content: string = ""
     title = ""
   var j = 0
-  var tmp = ""
-  renderTocEntries(d[], j, 1, tmp)
-  var toc = tmp.rope
-  for i in countup(low(TSymKind), high(TSymKind)):
-    genSection(d, i)
-    add(toc, d.toc[i])
-  if toc != nil:
-    toc = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.toc"), ["content"], [toc])
-  for i in countup(low(TSymKind), high(TSymKind)): add(code, d.section[i])
+  var toc = ""
+  renderTocEntries(d[], j, 1, toc)
+  for i in TSymKind:
+    var shouldSort = i in routineKinds and groupedToc
+    genSection(d, i, shouldSort)
+    toc.add(d.toc[i])
+  if toc != "" or d.target == outLatex:
+    # for Latex $doc.toc will automatically generate TOC if `d.hasToc` is set
+    toc = getConfigVar(d.conf, "doc.toc") % ["content", toc]
+  for i in TSymKind: code.add(d.section[i].finalMarkup)
 
   # Extract the title. Non API modules generate an entry in the index table.
   if d.meta[metaTitle].len != 0:
     title = d.meta[metaTitle]
-    setIndexTerm(d[], "", title)
   else:
-    # Modules get an automatic title for the HTML, but no entry in the index.
-    title = "Module " & extractFilename(changeFileExt(d.filename, ""))
-
-  let bodyname = if d.hasToc and not d.isPureRst: "doc.body_toc_group"
+    title = canonicalImport(d.conf, AbsoluteFile d.filename)
+  title = esc(d.target, title)
+  var subtitle = ""
+  if d.meta[metaSubtitle] != "":
+    dispA(d.conf, subtitle, "<h2 class=\"subtitle\">$1</h2>",
+        "\\\\\\vspace{0.5em}\\large $1", [esc(d.target, d.meta[metaSubtitle])])
+
+  var groupsection = getConfigVar(d.conf, "doc.body_toc_groupsection")
+  let bodyname = if d.hasToc and not d.standaloneDoc and not d.conf.isLatexCmd:
+                   groupsection.setLen 0
+                   "doc.body_toc_group"
                  elif d.hasToc: "doc.body_toc"
                  else: "doc.body_no_toc"
-  content = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, bodyname), ["title",
-      "tableofcontents", "moduledesc", "date", "time", "content"],
-      [title.rope, toc, d.modDesc, rope(getDateStr()),
-      rope(getClockStr()), code])
+  let seeSrc = genSeeSrc(d, d.filename, 1)
+  content = getConfigVar(d.conf, bodyname) % [
+      "title", title, "subtitle", subtitle,
+      "tableofcontents", toc, "moduledesc", d.modDescFinal, "date", getDateStr(),
+      "time", getClockStr(), "content", code,
+      "deprecationMsg", d.modDeprecationMsg,
+      "theindexhref", relLink(d.conf.outDir, d.destFile.AbsoluteFile,
+                              theindexFname.RelativeFile),
+      "body_toc_groupsection", groupsection, "seeSrc", seeSrc]
   if optCompileOnly notin d.conf.globalOptions:
     # XXX what is this hack doing here? 'optCompileOnly' means raw output!?
-    code = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.file"), ["title",
-        "tableofcontents", "moduledesc", "date", "time",
-        "content", "author", "version", "analytics"],
-        [title.rope, toc, d.modDesc, rope(getDateStr()),
-                     rope(getClockStr()), content, d.meta[metaAuthor].rope,
-                     d.meta[metaVersion].rope, d.analytics.rope])
+    code = getConfigVar(d.conf, "doc.file") % [
+        "nimdoccss", relLink(d.conf.outDir, d.destFile.AbsoluteFile,
+                             nimdocOutCss.RelativeFile),
+        "dochackjs", relLink(d.conf.outDir, d.destFile.AbsoluteFile,
+                             docHackJsFname.RelativeFile),
+        "title", title, "subtitle", subtitle, "tableofcontents", toc,
+        "moduledesc", d.modDescFinal, "date", getDateStr(), "time", getClockStr(),
+        "content", content, "author", d.meta[metaAuthor],
+        "version", esc(d.target, d.meta[metaVersion]), "analytics", d.analytics,
+        "deprecationMsg", d.modDeprecationMsg, "nimVersion", $NimMajor & "." & $NimMinor & "." & $NimPatch]
   else:
     code = content
   result = code
 
+proc indexFile(d: PDoc): AbsoluteFile =
+  let dir = d.conf.outDir
+  result = dir / changeFileExt(presentationPath(d.conf,
+                                                AbsoluteFile d.filename),
+                               IndexExt)
+  let (finalDir, _, _) = result.string.splitFile
+  createDir(finalDir)
+
 proc generateIndex*(d: PDoc) =
   if optGenIndex in d.conf.globalOptions:
-    writeIndexFile(d[], splitFile(d.conf.outFile).dir /
-                        splitFile(d.filename).name & IndexExt)
-
-proc getOutFile2(conf: ConfigRef; filename, ext, dir: string): string =
-  if optWholeProject in conf.globalOptions:
-    let d = if conf.outFile != "": conf.outFile else: dir
-    createDir(d)
-    result = d / changeFileExt(filename, ext)
-  else:
-    result = getOutFile(conf, filename, ext)
-
-proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) =
-  var content = genOutFile(d)
-  var success = true
-  var filename: string
+    let dest = indexFile(d)
+    writeIndexFile(d[], dest.string)
+
+proc updateOutfile(d: PDoc, outfile: AbsoluteFile) =
+  if d.module == nil or sfMainModule in d.module.flags: # nil for e.g. for commandRst2Html
+    if d.conf.outFile.isEmpty:
+      d.conf.outFile = outfile.relativeTo(d.conf.outDir)
+      if isAbsolute(d.conf.outFile.string):
+        d.conf.outFile = splitPath(d.conf.outFile.string)[1].RelativeFile
+
+proc writeOutput*(d: PDoc, useWarning = false, groupedToc = false) =
+  if optGenIndexOnly in d.conf.globalOptions:
+    d.conf.outFile = indexFile(d).relativeTo(d.conf.outDir)  # just for display
+    return
+  runAllExamples(d)
+  var content = genOutFile(d, groupedToc)
   if optStdout in d.conf.globalOptions:
-    writeRope(stdout, content)
-    filename = "<stdout>"
+    write(stdout, content)
   else:
-    filename = getOutFile2(d.conf, filename, outExt, "htmldocs")
-    success = writeRope(content, filename)
-  if not success:
-    rawMessage(d.conf, if useWarning: warnCannotOpenFile else: errCannotOpenFile, filename)
-
-proc writeOutputJson*(d: PDoc, filename, outExt: string,
-                      useWarning = false) =
+    template outfile: untyped = d.destFile.AbsoluteFile
+    #let outfile = getOutFile2(d.conf, shortenDir(d.conf, filename), outExt)
+    let dir = outfile.splitFile.dir
+    createDir(dir)
+    updateOutfile(d, outfile)
+    try:
+      writeFile(outfile, content)
+    except IOError:
+      rawMessage(d.conf, if useWarning: warnCannotOpenFile else: errCannotOpenFile,
+        outfile.string)
+    if not d.wroteSupportFiles: # nimdoc.css + dochack.js
+      let nimr = $d.conf.getPrefixDir()
+      case d.target
+      of outHtml:
+        copyFile(docCss.interp(nimr = nimr), $d.conf.outDir / nimdocOutCss)
+      of outLatex:
+        copyFile(docCls.interp(nimr = nimr), $d.conf.outDir / nimdocOutCls)
+      if optGenIndex in d.conf.globalOptions:
+        let docHackJs2 = getDocHacksJs(nimr, nim = getAppFilename())
+        copyFile(docHackJs2, $d.conf.outDir / docHackJs2.lastPathPart)
+      d.wroteSupportFiles = true
+
+proc writeOutputJson*(d: PDoc, useWarning = false) =
+  runAllExamples(d)
+  var modDesc: string = ""
+  for desc in d.modDescFinal:
+    modDesc &= desc
   let content = %*{"orig": d.filename,
     "nimble": getPackageName(d.conf, d.filename),
-    "entries": d.jArray}
+    "moduleDescription": modDesc,
+    "entries": d.jEntriesFinal}
   if optStdout in d.conf.globalOptions:
-    write(stdout, $content)
+    writeLine(stdout, $content)
   else:
-    var f: File
-    if open(f, getOutFile2(d.conf, splitFile(filename).name,
-            outExt, "jsondocs"), fmWrite):
+    let dir = d.destFile.splitFile.dir
+    createDir(dir)
+    var f: File = default(File)
+    if open(f, d.destFile, fmWrite):
       write(f, $content)
       close(f)
+      updateOutfile(d, d.destFile.AbsoluteFile)
     else:
-      discard "fixme: error report"
+      localError(d.conf, newLineInfo(d.conf, AbsoluteFile d.filename, -1, -1),
+                 warnUser, "unable to open file \"" & d.destFile &
+                 "\" for writing")
+
+proc handleDocOutputOptions*(conf: ConfigRef) =
+  if optWholeProject in conf.globalOptions:
+    # Backward compatibility with previous versions
+    # xxx this is buggy when user provides `nim doc --project -o:sub/bar.html main`,
+    # it'd write to `sub/bar.html/main.html`
+    conf.outDir = AbsoluteDir(conf.outDir / conf.outFile)
 
 proc commandDoc*(cache: IdentCache, conf: ConfigRef) =
+  ## implementation of deprecated ``doc0`` command (without semantic checking)
+  handleDocOutputOptions conf
   var ast = parseFile(conf.projectMainIdx, cache, conf)
   if ast == nil: return
-  var d = newDocumentor(conf.projectFull, cache, conf)
-  d.hasToc = true
-  generateDoc(d, ast)
-  writeOutput(d, conf.projectFull, HtmlExt)
+  var d = newDocumentor(conf.projectFull, cache, conf, hasToc = true)
+  generateDoc(d, ast, ast, conf)
+  finishGenerateDoc(d)
+  writeOutput(d)
   generateIndex(d)
 
-proc commandRstAux(cache: IdentCache, conf: ConfigRef; filename, outExt: string) =
+proc commandRstAux(cache: IdentCache, conf: ConfigRef;
+                   filename: AbsoluteFile, outExt: string,
+                   preferMarkdown: bool) =
   var filen = addFileExt(filename, "txt")
-  var d = newDocumentor(filen, cache, conf)
-  d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string;
-                          status: int; content: string) =
-    var outp: string
-    if filename.len == 0:
-      inc(d.id)
-      let nameOnly = splitFile(d.filename).name
-      let subdir = getNimcacheDir(conf) / nameOnly
-      createDir(subdir)
-      outp = subdir / (nameOnly & "_snippet_" & $d.id & ".nim")
-    elif isAbsolute(filename):
-      outp = filename
-    else:
-      # Nim's convention: every path is relative to the file it was written in:
-      outp = splitFile(d.filename).dir / filename
-    writeFile(outp, content)
-    let cmd = cmd % quoteShell(outp)
-    rawMessage(conf, hintExecuting, cmd)
-    if execShellCmd(cmd) != status:
-      rawMessage(conf, errGenerated, "executing of external program failed: " & cmd)
-
-  d.isPureRst = true
-  var rst = parseRst(readFile(filen), filen, 0, 1, d.hasToc,
-                     {roSupportRawDirective}, conf)
-  var modDesc = newStringOfCap(30_000)
-  #d.modDesc = newMutableRope(30_000)
-  renderRstToOut(d[], rst, modDesc)
-  #freezeMutableRope(d.modDesc)
-  d.modDesc = rope(modDesc)
-  writeOutput(d, filename, outExt)
-  generateIndex(d)
-
-proc commandRst2Html*(cache: IdentCache, conf: ConfigRef) =
-  commandRstAux(cache, conf, conf.projectFull, HtmlExt)
-
-proc commandRst2TeX*(cache: IdentCache, conf: ConfigRef) =
-  commandRstAux(cache, conf, conf.projectFull, TexExt)
+  var d = newDocumentor(filen, cache, conf, outExt, standaloneDoc = true,
+                        preferMarkdown = preferMarkdown, hasToc = false)
+  try:
+    let rst = parseRst(readFile(filen.string),
+                      line=LineRstInit, column=ColRstInit,
+                      conf, d.sharedState)
+    d.modDescPre = @[ItemFragment(isRst: true, rst: rst)]
+    finishGenerateDoc(d)
+    writeOutput(d)
+    generateIndex(d)
+  except ERecoverableError:
+    discard "already reported the error"
+
+proc commandRst2Html*(cache: IdentCache, conf: ConfigRef,
+                      preferMarkdown=false) =
+  commandRstAux(cache, conf, conf.projectFull, HtmlExt, preferMarkdown)
+
+proc commandRst2TeX*(cache: IdentCache, conf: ConfigRef,
+                     preferMarkdown=false) =
+  commandRstAux(cache, conf, conf.projectFull, TexExt, preferMarkdown)
 
 proc commandJson*(cache: IdentCache, conf: ConfigRef) =
+  ## implementation of a deprecated jsondoc0 command
   var ast = parseFile(conf.projectMainIdx, cache, conf)
   if ast == nil: return
-  var d = newDocumentor(conf.projectFull, cache, conf)
-  d.hasToc = true
-  generateJson(d, ast)
-  let json = d.jArray
-  let content = rope(pretty(json))
+  var d = newDocumentor(conf.projectFull, cache, conf, hasToc = true)
+  d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string;
+                          status: int; content: string) {.gcsafe.} =
+    localError(conf, newLineInfo(conf, AbsoluteFile d.filename, -1, -1),
+               warnUser, "the ':test:' attribute is not supported by this backend")
+  generateJson(d, ast, conf)
+  finishGenerateDoc(d)
+  let json = d.jEntriesFinal
+  let content = pretty(json)
 
   if optStdout in d.conf.globalOptions:
-    writeRope(stdout, content)
+    write(stdout, content)
   else:
     #echo getOutFile(gProjectFull, JsonExt)
-    let filename = getOutFile(conf, conf.projectFull, JsonExt)
-    if not writeRope(content, filename):
-      rawMessage(conf, errCannotOpenFile, filename)
+    let filename = getOutFile(conf, RelativeFile conf.projectName, JsonExt)
+    try:
+      writeFile(filename, content)
+    except IOError:
+      rawMessage(conf, errCannotOpenFile, filename.string)
 
 proc commandTags*(cache: IdentCache, conf: ConfigRef) =
   var ast = parseFile(conf.projectMainIdx, cache, conf)
   if ast == nil: return
-  var d = newDocumentor(conf.projectFull, cache, conf)
-  d.hasToc = true
+  var d = newDocumentor(conf.projectFull, cache, conf, hasToc = true)
+  d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string;
+                          status: int; content: string) {.gcsafe.} =
+    localError(conf, newLineInfo(conf, AbsoluteFile d.filename, -1, -1),
+               warnUser, "the ':test:' attribute is not supported by this backend")
   var
-    content: Rope
+    content = ""
   generateTags(d, ast, content)
 
   if optStdout in d.conf.globalOptions:
-    writeRope(stdout, content)
+    write(stdout, content)
   else:
     #echo getOutFile(gProjectFull, TagsExt)
-    let filename = getOutFile(conf, conf.projectFull, TagsExt)
-    if not writeRope(content, filename):
-      rawMessage(conf, errCannotOpenFile, filename)
-
-proc commandBuildIndex*(cache: IdentCache, conf: ConfigRef) =
-  var content = mergeIndexes(conf.projectFull).rope
-
-  let code = ropeFormatNamedVars(conf, getConfigVar(conf, "doc.file"), ["title",
-      "tableofcontents", "moduledesc", "date", "time",
-      "content", "author", "version", "analytics"],
-      ["Index".rope, nil, nil, rope(getDateStr()),
-                   rope(getClockStr()), content, nil, nil, nil])
+    let filename = getOutFile(conf, RelativeFile conf.projectName, TagsExt)
+    try:
+      writeFile(filename, content)
+    except IOError:
+      rawMessage(conf, errCannotOpenFile, filename.string)
+
+proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"") =
+  if optGenIndexOnly in conf.globalOptions:
+    return
+  var content = mergeIndexes(dir)
+
+  var outFile = outFile
+  if outFile.isEmpty: outFile = theindexFname.RelativeFile.changeFileExt("")
+  let filename = getOutFile(conf, outFile, HtmlExt)
+
+  let code = getConfigVar(conf, "doc.file") % [
+      "nimdoccss", relLink(conf.outDir, filename, nimdocOutCss.RelativeFile),
+      "dochackjs", relLink(conf.outDir, filename, docHackJsFname.RelativeFile),
+      "title", "Index",
+      "subtitle", "", "tableofcontents", "", "moduledesc", "",
+      "date", getDateStr(), "time", getClockStr(),
+      "content", content, "author", "", "version", "", "analytics", "", "nimVersion", $NimMajor & "." & $NimMinor & "." & $NimPatch]
   # no analytics because context is not available
-  let filename = getOutFile(conf, "theindex", HtmlExt)
-  if not writeRope(code, filename):
-    rawMessage(conf, errCannotOpenFile, filename)
+
+  try:
+    writeFile(filename, code)
+  except IOError:
+    rawMessage(conf, errCannotOpenFile, filename.string)
+
+proc commandBuildIndexJson*(conf: ConfigRef, dir: string, outFile = RelativeFile"") =
+  var (modules, symbols, docs) = readIndexDir(dir)
+  let documents = toSeq(keys(Table[IndexEntry, seq[IndexEntry]](docs)))
+  let body = %*({"documents": documents, "modules": modules, "symbols": symbols})
+
+  var outFile = outFile
+  if outFile.isEmpty: outFile = theindexFname.RelativeFile.changeFileExt("")
+  let filename = getOutFile(conf, outFile, JsonExt)
+
+  try:
+    writeFile(filename, $body)
+  except IOError:
+    rawMessage(conf, errCannotOpenFile, filename.string)
diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim
index 068c47bb3..7fb11a3bd 100644
--- a/compiler/docgen2.nim
+++ b/compiler/docgen2.nim
@@ -11,58 +11,70 @@
 # semantic checking.
 
 import
-  os, options, ast, astalgo, msgs, ropes, idents, passes, docgen, lineinfos
+  options, ast, msgs, docgen, lineinfos, pathutils, packages
 
-from modulegraphs import ModuleGraph
+from modulegraphs import ModuleGraph, PPassContext
 
 type
-  TGen = object of TPassContext
+  TGen = object of PPassContext
     doc: PDoc
     module: PSym
+    config: ConfigRef
   PGen = ref TGen
 
+proc shouldProcess(g: PGen): bool =
+  (optWholeProject in g.doc.conf.globalOptions and g.doc.conf.belongsToProjectPackage(g.module)) or
+      sfMainModule in g.module.flags or g.config.projectMainIdx == g.module.info.fileIndex
+
 template closeImpl(body: untyped) {.dirty.} =
   var g = PGen(p)
   let useWarning = sfMainModule notin g.module.flags
-  #echo g.module.name.s, " ", g.module.owner.id, " ", gMainPackageId
-  if (g.module.owner.id == g.doc.conf.mainPackageId and optWholeProject in g.doc.conf.globalOptions) or
-      sfMainModule in g.module.flags:
+  let groupedToc = true
+  if shouldProcess(g):
+    finishGenerateDoc(g.doc)
     body
     try:
       generateIndex(g.doc)
     except IOError:
       discard
 
-proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
+proc closeDoc*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
+  result = nil
   closeImpl:
-    writeOutput(g.doc, toFilename(graph.config, FileIndex g.module.position), HtmlExt, useWarning)
+    writeOutput(g.doc, useWarning, groupedToc)
 
-proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
+proc closeJson*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
+  result = nil
   closeImpl:
-    writeOutputJson(g.doc, toFilename(graph.config, FileIndex g.module.position), ".json", useWarning)
+    writeOutputJson(g.doc, useWarning)
 
-proc processNode(c: PPassContext, n: PNode): PNode =
+proc processNode*(c: PPassContext, n: PNode): PNode =
   result = n
   var g = PGen(c)
-  generateDoc(g.doc, n)
+  if shouldProcess(g):
+    generateDoc(g.doc, n, n, g.config)
 
-proc processNodeJson(c: PPassContext, n: PNode): PNode =
+proc processNodeJson*(c: PPassContext, n: PNode): PNode =
   result = n
   var g = PGen(c)
-  generateJson(g.doc, n)
+  if shouldProcess(g):
+    generateJson(g.doc, n, g.config, false)
 
-proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
+template myOpenImpl(ext: untyped) {.dirty.} =
   var g: PGen
   new(g)
   g.module = module
-  var d = newDocumentor(toFilename(graph.config, FileIndex module.position), graph.cache, graph.config)
-  d.hasToc = true
+  g.config = graph.config
+  var d = newDocumentor(AbsoluteFile toFullPath(graph.config, FileIndex module.position),
+      graph.cache, graph.config, ext, module, hasToc = true)
   g.doc = d
   result = g
 
-const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close)
-const docgen2JsonPass* = makePass(open = myOpen, process = processNodeJson,
-                                  close = closeJson)
+proc openHtml*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
+  myOpenImpl(HtmlExt)
+
+proc openTex*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
+  myOpenImpl(TexExt)
 
-proc finishDoc2Pass*(project: string) =
-  discard
+proc openJson*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
+  myOpenImpl(JsonExt)
diff --git a/compiler/enumtostr.nim b/compiler/enumtostr.nim
new file mode 100644
index 000000000..dc516d2e5
--- /dev/null
+++ b/compiler/enumtostr.nim
@@ -0,0 +1,110 @@
+
+import ast, idents, lineinfos, modulegraphs, magicsys
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
+proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
+  result = newSym(skProc, getIdent(g.cache, "$"), idgen, t.owner, info)
+
+  let dest = newSym(skParam, getIdent(g.cache, "e"), idgen, result, info)
+  dest.typ = t
+
+  let res = newSym(skResult, getIdent(g.cache, "result"), idgen, result, info)
+  res.typ = getSysType(g, info, tyString)
+
+  result.typ = newType(tyProc, idgen, t.owner)
+  result.typ.n = newNodeI(nkFormalParams, info)
+  rawAddSon(result.typ, res.typ)
+  result.typ.n.add newNodeI(nkEffectList, info)
+
+  result.typ.addParam dest
+
+  var body = newNodeI(nkStmtList, info)
+  var caseStmt = newNodeI(nkCaseStmt, info)
+  caseStmt.add(newSymNode dest)
+
+  # copy the branches over, but replace the fields with the for loop body:
+  for i in 0..<t.n.len:
+    assert(t.n[i].kind == nkSym)
+    var field = t.n[i].sym
+    let val = if field.ast == nil: field.name.s else: field.ast.strVal
+    caseStmt.add newTree(nkOfBranch, newIntTypeNode(field.position, t),
+      newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res), newStrNode(val, info))))
+    #newIntTypeNode(nkIntLit, field.position, t)
+
+  body.add(caseStmt)
+
+  var n = newNodeI(nkProcDef, info, bodyPos+2)
+  for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info)
+  n[namePos] = newSymNode(result)
+  n[paramsPos] = result.typ.n
+  n[bodyPos] = body
+  n[resultPos] = newSymNode(res)
+  result.ast = n
+  incl result.flags, sfFromGeneric
+  incl result.flags, sfNeverRaises
+
+proc searchObjCaseImpl(obj: PNode; field: PSym): PNode =
+  case obj.kind
+  of nkSym:
+    result = nil
+  of nkElse, nkOfBranch:
+    result = searchObjCaseImpl(obj.lastSon, field)
+  else:
+    if obj.kind == nkRecCase and obj[0].kind == nkSym and obj[0].sym == field:
+      result = obj
+    else:
+      result = nil
+      for x in obj:
+        result = searchObjCaseImpl(x, field)
+        if result != nil: break
+
+proc searchObjCase(t: PType; field: PSym): PNode =
+  result = searchObjCaseImpl(t.n, field)
+  if result == nil and t.baseClass != nil:
+    result = searchObjCase(t.baseClass.skipTypes({tyAlias, tyGenericInst, tyRef, tyPtr}), field)
+  doAssert result != nil
+
+proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
+  result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), idgen, t.owner, info)
+
+  let dest = newSym(skParam, getIdent(g.cache, "e"), idgen, result, info)
+  dest.typ = field.typ
+
+  let res = newSym(skResult, getIdent(g.cache, "result"), idgen, result, info)
+  res.typ = getSysType(g, info, tyUInt8)
+
+  result.typ = newType(tyProc, idgen, t.owner)
+  result.typ.n = newNodeI(nkFormalParams, info)
+  rawAddSon(result.typ, res.typ)
+  result.typ.n.add newNodeI(nkEffectList, info)
+
+  result.typ.addParam dest
+
+  var body = newNodeI(nkStmtList, info)
+  var caseStmt = newNodeI(nkCaseStmt, info)
+  caseStmt.add(newSymNode dest)
+
+  let subObj = searchObjCase(t, field)
+  for i in 1..<subObj.len:
+    let ofBranch = subObj[i]
+    var newBranch = newNodeI(ofBranch.kind, ofBranch.info)
+    for j in 0..<ofBranch.len-1:
+      newBranch.add ofBranch[j]
+
+    newBranch.add newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res), newIntNode(nkInt8Lit, i)))
+    caseStmt.add newBranch
+
+  body.add(caseStmt)
+
+  var n = newNodeI(nkProcDef, info, bodyPos+2)
+  for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info)
+  n[namePos] = newSymNode(result)
+  n[paramsPos] = result.typ.n
+  n[bodyPos] = body
+  n[resultPos] = newSymNode(res)
+  result.ast = n
+  incl result.flags, sfFromGeneric
+  incl result.flags, sfNeverRaises
diff --git a/compiler/errorhandling.nim b/compiler/errorhandling.nim
new file mode 100644
index 000000000..2cde9e3fb
--- /dev/null
+++ b/compiler/errorhandling.nim
@@ -0,0 +1,85 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2021 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module contains support code for new-styled error
+## handling via an `nkError` node kind.
+
+import ast, renderer, options, types
+import std/strutils
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+type
+  ErrorKind* = enum ## expand as you need.
+    RawTypeMismatchError
+    ExpressionCannotBeCalled
+    CustomError
+    WrongNumberOfArguments
+    AmbiguousCall
+
+proc errorSubNode*(n: PNode): PNode =
+  case n.kind
+  of nkEmpty..nkNilLit:
+    result = nil
+  of nkError:
+    result = n
+  else:
+    result = nil
+    for i in 0..<n.len:
+      result = errorSubNode(n[i])
+      if result != nil: break
+
+proc newError*(wrongNode: PNode; k: ErrorKind; args: varargs[PNode]): PNode =
+  assert wrongNode.kind != nkError
+  let innerError = errorSubNode(wrongNode)
+  if innerError != nil:
+    return innerError
+  var idgen = idGeneratorForPackage(-1'i32)
+  result = newNodeIT(nkError, wrongNode.info, newType(tyError, idgen, nil))
+  result.add wrongNode
+  result.add newIntNode(nkIntLit, ord(k))
+  for a in args: result.add a
+
+proc newError*(wrongNode: PNode; msg: string): PNode =
+  assert wrongNode.kind != nkError
+  let innerError = errorSubNode(wrongNode)
+  if innerError != nil:
+    return innerError
+  var idgen = idGeneratorForPackage(-1'i32)
+  result = newNodeIT(nkError, wrongNode.info, newType(tyError, idgen, nil))
+  result.add wrongNode
+  result.add newIntNode(nkIntLit, ord(CustomError))
+  result.add newStrNode(msg, wrongNode.info)
+
+proc errorToString*(config: ConfigRef; n: PNode): string =
+  assert n.kind == nkError
+  assert n.len > 1
+  let wrongNode = n[0]
+  case ErrorKind(n[1].intVal)
+  of RawTypeMismatchError:
+    result = "type mismatch"
+  of ExpressionCannotBeCalled:
+    result = "expression '$1' cannot be called" % wrongNode[0].renderTree
+  of CustomError:
+    result = n[2].strVal
+  of WrongNumberOfArguments:
+    result = "wrong number of arguments"
+  of AmbiguousCall:
+    let a = n[2].sym
+    let b = n[3].sym
+    var args = "("
+    for i in 1..<wrongNode.len:
+      if i > 1: args.add(", ")
+      args.add(typeToString(wrongNode[i].typ))
+    args.add(")")
+    result = "ambiguous call; both $1 and $2 match for: $3" % [
+      getProcHeader(config, a),
+      getProcHeader(config, b),
+      args]
diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim
index e863c8995..9871c81af 100644
--- a/compiler/evalffi.nim
+++ b/compiler/evalffi.nim
@@ -9,76 +9,85 @@
 
 ## This file implements the FFI part of the evaluator for Nim code.
 
-import ast, astalgo, ropes, types, options, tables, dynlib, libffi, msgs, os
+import ast, types, options, msgs, lineinfos
+from std/os import getAppFilename
+import libffi/libffi
+
+import std/[tables, dynlib]
 
 when defined(windows):
   const libcDll = "msvcrt.dll"
-else:
+elif defined(linux):
   const libcDll = "libc.so(.6|.5|)"
+elif defined(openbsd):
+  const libcDll = "/usr/lib/libc.so(.95.1|)"
+elif defined(bsd):
+  const libcDll = "/lib/libc.so.7"
+elif defined(osx):
+  const libcDll = "/usr/lib/libSystem.dylib"
+else:
+  {.error: "`libcDll` not implemented on this platform".}
 
 type
-  TDllCache = tables.TTable[string, TLibHandle]
+  TDllCache = tables.Table[string, LibHandle]
 var
-  gDllCache = initTable[string, TLibHandle]()
+  gDllCache = initTable[string, LibHandle]()
 
 when defined(windows):
-  var gExeHandle = loadLib(os.getAppFilename())
+  var gExeHandle = loadLib(getAppFilename())
 else:
   var gExeHandle = loadLib()
 
-proc getDll(cache: var TDllCache; dll: string; info: TLineInfo): pointer =
-  result = cache[dll]
+proc getDll(conf: ConfigRef, cache: var TDllCache; dll: string; info: TLineInfo): pointer =
+  result = nil
+  if dll in cache:
+    return cache[dll]
+  var libs: seq[string] = @[]
+  libCandidates(dll, libs)
+  for c in libs:
+    result = loadLib(c)
+    if not result.isNil: break
   if result.isNil:
-    var libs: seq[string] = @[]
-    libCandidates(dll, libs)
-    for c in libs:
-      result = loadLib(c)
-      if not result.isNil: break
-    if result.isNil:
-      globalError(info, "cannot load: " & dll)
-    cache[dll] = result
+    globalError(conf, info, "cannot load: " & dll)
+  cache[dll] = result
 
 const
   nkPtrLit = nkIntLit # hopefully we can get rid of this hack soon
 
-var myerrno {.importc: "errno", header: "<errno.h>".}: cint ## error variable
-
-proc importcSymbol*(sym: PSym): PNode =
-  let name = ropeToStr(sym.loc.r)
-
+proc importcSymbol*(conf: ConfigRef, sym: PSym): PNode =
+  let name = sym.cname # $sym.loc.r would point to internal name
   # the AST does not support untyped pointers directly, so we use an nkIntLit
   # that contains the address instead:
   result = newNodeIT(nkPtrLit, sym.info, sym.typ)
-  case name
-  of "stdin":  result.intVal = cast[ByteAddress](system.stdin)
-  of "stdout": result.intVal = cast[ByteAddress](system.stdout)
-  of "stderr": result.intVal = cast[ByteAddress](system.stderr)
-  of "vmErrnoWrapper": result.intVal = cast[ByteAddress](myerrno)
-  else:
+  when true:
+    var libPathMsg = ""
     let lib = sym.annex
     if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}:
-      globalError(sym.info, "dynlib needs to be a string lit for the REPL")
-    var theAddr: pointer
-    if lib.isNil and not gExehandle.isNil:
+      globalError(conf, sym.info, "dynlib needs to be a string lit")
+    var theAddr: pointer = nil
+    if (lib.isNil or lib.kind == libHeader) and not gExeHandle.isNil:
+      libPathMsg = "current exe: " & getAppFilename() & " nor libc: " & libcDll
       # first try this exe itself:
-      theAddr = gExehandle.symAddr(name)
+      theAddr = gExeHandle.symAddr(name.cstring)
       # then try libc:
       if theAddr.isNil:
-        let dllhandle = gDllCache.getDll(libcDll, sym.info)
-        theAddr = dllhandle.symAddr(name)
+        let dllhandle = getDll(conf, gDllCache, libcDll, sym.info)
+        theAddr = dllhandle.symAddr(name.cstring)
     elif not lib.isNil:
-      let dllhandle = gDllCache.getDll(if lib.kind == libHeader: libcDll
-                                       else: lib.path.strVal, sym.info)
-      theAddr = dllhandle.symAddr(name)
-    if theAddr.isNil: globalError(sym.info, "cannot import: " & sym.name.s)
-    result.intVal = cast[ByteAddress](theAddr)
-
-proc mapType(t: ast.PType): ptr libffi.TType =
+      let dll = if lib.kind == libHeader: libcDll else: lib.path.strVal
+      libPathMsg = dll
+      let dllhandle = getDll(conf, gDllCache, dll, sym.info)
+      theAddr = dllhandle.symAddr(name.cstring)
+    if theAddr.isNil: globalError(conf, sym.info,
+      "cannot import symbol: " & name & " from " & libPathMsg)
+    result.intVal = cast[int](theAddr)
+
+proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type =
   if t == nil: return addr libffi.type_void
 
   case t.kind
   of tyBool, tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64, tySet:
-    case t.getSize
+    case getSize(conf, t)
     of 1: result = addr libffi.type_uint8
     of 2: result = addr libffi.type_sint16
     of 4: result = addr libffi.type_sint32
@@ -86,93 +95,98 @@ proc mapType(t: ast.PType): ptr libffi.TType =
     else: result = nil
   of tyFloat, tyFloat64: result = addr libffi.type_double
   of tyFloat32: result = addr libffi.type_float
-  of tyVar, tyLent, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr,
-     tyStmt, tyTypeDesc, tyProc, tyArray, tyStatic, tyNil:
+  of tyVar, tyLent, tyPointer, tyPtr, tyRef, tyCstring, tySequence, tyString, tyUntyped,
+     tyTyped, tyTypeDesc, tyProc, tyArray, tyStatic, tyNil:
     result = addr libffi.type_pointer
   of tyDistinct, tyAlias, tySink:
-    result = mapType(t.sons[0])
+    result = mapType(conf, t.skipModifier)
   else:
     result = nil
   # too risky:
   #of tyFloat128: result = addr libffi.type_longdouble
 
-proc mapCallConv(cc: TCallingConvention, info: TLineInfo): TABI =
+proc mapCallConv(conf: ConfigRef, cc: TCallingConvention, info: TLineInfo): TABI =
   case cc
-  of ccDefault: result = DEFAULT_ABI
-  of ccStdCall: result = when defined(windows): STDCALL else: DEFAULT_ABI
+  of ccNimCall: result = DEFAULT_ABI
+  of ccStdCall: result = when defined(windows) and defined(x86): STDCALL else: DEFAULT_ABI
   of ccCDecl: result = DEFAULT_ABI
   else:
-    globalError(info, "cannot map calling convention to FFI")
+    result = default(TABI)
+    globalError(conf, info, "cannot map calling convention to FFI")
 
-template rd(T, p: untyped): untyped = (cast[ptr T](p))[]
-template wr(T, p, v: untyped): untyped = (cast[ptr T](p))[] = v
+template rd(typ, p: untyped): untyped = (cast[ptr typ](p))[]
+template wr(typ, p, v: untyped): untyped = (cast[ptr typ](p))[] = v
 template `+!`(x, y: untyped): untyped =
-  cast[pointer](cast[ByteAddress](x) + y)
+  cast[pointer](cast[int](x) + y)
 
-proc packSize(v: PNode, typ: PType): int =
+proc packSize(conf: ConfigRef, v: PNode, typ: PType): int =
   ## computes the size of the blob
   case typ.kind
   of tyPtr, tyRef, tyVar, tyLent:
     if v.kind in {nkNilLit, nkPtrLit}:
       result = sizeof(pointer)
     else:
-      result = sizeof(pointer) + packSize(v.sons[0], typ.lastSon)
+      result = sizeof(pointer) + packSize(conf, v[0], typ.elementType)
   of tyDistinct, tyGenericInst, tyAlias, tySink:
-    result = packSize(v, typ.sons[0])
+    result = packSize(conf, v, typ.skipModifier)
   of tyArray:
     # consider: ptr array[0..1000_000, int] which is common for interfacing;
     # we use the real length here instead
     if v.kind in {nkNilLit, nkPtrLit}:
       result = sizeof(pointer)
     elif v.len != 0:
-      result = v.len * packSize(v.sons[0], typ.sons[1])
+      result = v.len * packSize(conf, v[0], typ.elementType)
+    else:
+      result = 0
   else:
-    result = typ.getSize.int
+    result = getSize(conf, typ).int
 
-proc pack(v: PNode, typ: PType, res: pointer)
+proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer)
 
-proc getField(n: PNode; position: int): PSym =
+proc getField(conf: ConfigRef, n: PNode; position: int): PSym =
   case n.kind
   of nkRecList:
-    for i in countup(0, sonsLen(n) - 1):
-      result = getField(n.sons[i], position)
+    result = nil
+    for i in 0..<n.len:
+      result = getField(conf, n[i], position)
       if result != nil: return
   of nkRecCase:
-    result = getField(n.sons[0], position)
+    result = getField(conf, n[0], position)
     if result != nil: return
-    for i in countup(1, sonsLen(n) - 1):
-      case n.sons[i].kind
+    for i in 1..<n.len:
+      case n[i].kind
       of nkOfBranch, nkElse:
-        result = getField(lastSon(n.sons[i]), position)
+        result = getField(conf, lastSon(n[i]), position)
         if result != nil: return
-      else: internalError(n.info, "getField(record case branch)")
+      else: internalError(conf, n.info, "getField(record case branch)")
   of nkSym:
     if n.sym.position == position: result = n.sym
-  else: discard
+    else: result = nil
+  else: result = nil
 
-proc packObject(x: PNode, typ: PType, res: pointer) =
-  internalAssert x.kind in {nkObjConstr, nkPar, nkTupleConstr}
+proc packObject(conf: ConfigRef, x: PNode, typ: PType, res: pointer) =
+  internalAssert conf, x.kind in {nkObjConstr, nkPar, nkTupleConstr}
   # compute the field's offsets:
-  discard typ.getSize
-  for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1):
-    var it = x.sons[i]
+  discard getSize(conf, typ)
+  for i in ord(x.kind == nkObjConstr)..<x.len:
+    var it = x[i]
     if it.kind == nkExprColonExpr:
-      internalAssert it.sons[0].kind == nkSym
-      let field = it.sons[0].sym
-      pack(it.sons[1], field.typ, res +! field.offset)
+      internalAssert conf, it[0].kind == nkSym
+      let field = it[0].sym
+      pack(conf, it[1], field.typ, res +! field.offset)
     elif typ.n != nil:
-      let field = getField(typ.n, i)
-      pack(it, field.typ, res +! field.offset)
+      let field = getField(conf, typ.n, i)
+      pack(conf, it, field.typ, res +! field.offset)
     else:
       # XXX: todo
-      globalError(x.info, "cannot pack unnamed tuple")
+      globalError(conf, x.info, "cannot pack unnamed tuple")
 
 const maxPackDepth = 20
 var packRecCheck = 0
 
-proc pack(v: PNode, typ: PType, res: pointer) =
-  template awr(T, v: untyped): untyped =
-    wr(T, res, v)
+proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer) =
+  template awr(typ, v: untyped): untyped =
+    wr(typ, res, v)
 
   case typ.kind
   of tyBool: awr(bool, v.intVal != 0)
@@ -188,18 +202,18 @@ proc pack(v: PNode, typ: PType, res: pointer) =
   of tyUInt32: awr(uint32, v.intVal.uint32)
   of tyUInt64: awr(uint64, v.intVal.uint64)
   of tyEnum, tySet:
-    case v.typ.getSize
+    case getSize(conf, v.typ)
     of 1: awr(uint8, v.intVal.uint8)
     of 2: awr(uint16, v.intVal.uint16)
     of 4: awr(int32, v.intVal.int32)
     of 8: awr(int64, v.intVal.int64)
     else:
-      globalError(v.info, "cannot map value to FFI (tyEnum, tySet)")
+      globalError(conf, v.info, "cannot map value to FFI (tyEnum, tySet)")
   of tyFloat: awr(float, v.floatVal)
   of tyFloat32: awr(float32, v.floatVal)
   of tyFloat64: awr(float64, v.floatVal)
 
-  of tyPointer, tyProc,  tyCString, tyString:
+  of tyPointer, tyProc,  tyCstring, tyString:
     if v.kind == nkNilLit:
       # nothing to do since the memory is 0 initialized anyway
       discard
@@ -208,7 +222,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
     elif v.kind in {nkStrLit..nkTripleStrLit}:
       awr(cstring, cstring(v.strVal))
     else:
-      globalError(v.info, "cannot map pointer/proc value to FFI")
+      globalError(conf, v.info, "cannot map pointer/proc value to FFI")
   of tyPtr, tyRef, tyVar, tyLent:
     if v.kind == nkNilLit:
       # nothing to do since the memory is 0 initialized anyway
@@ -218,44 +232,44 @@ proc pack(v: PNode, typ: PType, res: pointer) =
     else:
       if packRecCheck > maxPackDepth:
         packRecCheck = 0
-        globalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
+        globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ))
       inc packRecCheck
-      pack(v.sons[0], typ.lastSon, res +! sizeof(pointer))
+      pack(conf, v[0], typ.elementType, res +! sizeof(pointer))
       dec packRecCheck
       awr(pointer, res +! sizeof(pointer))
   of tyArray:
-    let baseSize = typ.sons[1].getSize
-    for i in 0 ..< v.len:
-      pack(v.sons[i], typ.sons[1], res +! i * baseSize)
+    let baseSize = getSize(conf, typ.elementType)
+    for i in 0..<v.len:
+      pack(conf, v[i], typ.elementType, res +! i * baseSize)
   of tyObject, tyTuple:
-    packObject(v, typ, res)
+    packObject(conf, v, typ, res)
   of tyNil:
     discard
   of tyDistinct, tyGenericInst, tyAlias, tySink:
-    pack(v, typ.sons[0], res)
+    pack(conf, v, typ.skipModifier, res)
   else:
-    globalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
+    globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ))
 
-proc unpack(x: pointer, typ: PType, n: PNode): PNode
+proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode
 
-proc unpackObjectAdd(x: pointer, n, result: PNode) =
+proc unpackObjectAdd(conf: ConfigRef, x: pointer, n, result: PNode) =
   case n.kind
   of nkRecList:
-    for i in countup(0, sonsLen(n) - 1):
-      unpackObjectAdd(x, n.sons[i], result)
+    for i in 0..<n.len:
+      unpackObjectAdd(conf, x, n[i], result)
   of nkRecCase:
-    globalError(result.info, "case objects cannot be unpacked")
+    globalError(conf, result.info, "case objects cannot be unpacked")
   of nkSym:
     var pair = newNodeI(nkExprColonExpr, result.info, 2)
-    pair.sons[0] = n
-    pair.sons[1] = unpack(x +! n.sym.offset, n.sym.typ, nil)
+    pair[0] = n
+    pair[1] = unpack(conf, x +! n.sym.offset, n.sym.typ, nil)
     #echo "offset: ", n.sym.name.s, " ", n.sym.offset
     result.add pair
   else: discard
 
-proc unpackObject(x: pointer, typ: PType, n: PNode): PNode =
+proc unpackObject(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
   # compute the field's offsets:
-  discard typ.getSize
+  discard getSize(conf, typ)
 
   # iterate over any actual field of 'n' ... if n is nil we need to create
   # the nkPar node:
@@ -263,36 +277,36 @@ proc unpackObject(x: pointer, typ: PType, n: PNode): PNode =
     result = newNode(nkTupleConstr)
     result.typ = typ
     if typ.n.isNil:
-      internalError("cannot unpack unnamed tuple")
-    unpackObjectAdd(x, typ.n, result)
+      internalError(conf, "cannot unpack unnamed tuple")
+    unpackObjectAdd(conf, x, typ.n, result)
   else:
     result = n
     if result.kind notin {nkObjConstr, nkPar, nkTupleConstr}:
-      globalError(n.info, "cannot map value from FFI")
+      globalError(conf, n.info, "cannot map value from FFI")
     if typ.n.isNil:
-      globalError(n.info, "cannot unpack unnamed tuple")
-    for i in countup(ord(n.kind == nkObjConstr), sonsLen(n) - 1):
-      var it = n.sons[i]
+      globalError(conf, n.info, "cannot unpack unnamed tuple")
+    for i in ord(n.kind == nkObjConstr)..<n.len:
+      var it = n[i]
       if it.kind == nkExprColonExpr:
-        internalAssert it.sons[0].kind == nkSym
-        let field = it.sons[0].sym
-        it.sons[1] = unpack(x +! field.offset, field.typ, it.sons[1])
+        internalAssert conf, it[0].kind == nkSym
+        let field = it[0].sym
+        it[1] = unpack(conf, x +! field.offset, field.typ, it[1])
       else:
-        let field = getField(typ.n, i)
-        n.sons[i] = unpack(x +! field.offset, field.typ, it)
+        let field = getField(conf, typ.n, i)
+        n[i] = unpack(conf, x +! field.offset, field.typ, it)
 
-proc unpackArray(x: pointer, typ: PType, n: PNode): PNode =
+proc unpackArray(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
   if n.isNil:
     result = newNode(nkBracket)
     result.typ = typ
-    newSeq(result.sons, lengthOrd(typ).int)
+    newSeq(result.sons, lengthOrd(conf, typ).toInt)
   else:
     result = n
     if result.kind != nkBracket:
-      globalError(n.info, "cannot map value from FFI")
-  let baseSize = typ.sons[1].getSize
-  for i in 0 ..< result.len:
-    result.sons[i] = unpack(x +! i * baseSize, typ.sons[1], result.sons[i])
+      globalError(conf, n.info, "cannot map value from FFI")
+  let baseSize = getSize(conf, typ.elementType)
+  for i in 0..<result.len:
+    result[i] = unpack(conf, x +! i * baseSize, typ.elementType, result[i])
 
 proc canonNodeKind(k: TNodeKind): TNodeKind =
   case k
@@ -301,7 +315,7 @@ proc canonNodeKind(k: TNodeKind): TNodeKind =
   of nkStrLit..nkTripleStrLit: result = nkStrLit
   else: result = k
 
-proc unpack(x: pointer, typ: PType, n: PNode): PNode =
+proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
   template aw(k, v, field: untyped): untyped =
     if n.isNil:
       result = newNode(k)
@@ -313,7 +327,7 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
         #echo "expected ", k, " but got ", result.kind
         #debug result
         return newNodeI(nkExceptBranch, n.info)
-        #globalError(n.info, "cannot map value from FFI")
+        #globalError(conf, n.info, "cannot map value from FFI")
     result.field = v
 
   template setNil() =
@@ -323,7 +337,7 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
     else:
       reset n[]
       result = n
-      result.kind = nkNilLit
+      result[] = TNode(kind: nkNilLit)
       result.typ = typ
 
   template awi(kind, v: untyped): untyped = aw(kind, v, intVal)
@@ -344,13 +358,14 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
   of tyUInt32: awi(nkUInt32Lit, rd(uint32, x).BiggestInt)
   of tyUInt64: awi(nkUInt64Lit, rd(uint64, x).BiggestInt)
   of tyEnum:
-    case typ.getSize
+    case getSize(conf, typ)
     of 1: awi(nkIntLit, rd(uint8, x).BiggestInt)
     of 2: awi(nkIntLit, rd(uint16, x).BiggestInt)
     of 4: awi(nkIntLit, rd(int32, x).BiggestInt)
     of 8: awi(nkIntLit, rd(int64, x).BiggestInt)
     else:
-      globalError(n.info, "cannot map value from FFI (tyEnum, tySet)")
+      result = nil
+      globalError(conf, n.info, "cannot map value from FFI (tyEnum, tySet)")
   of tyFloat: awf(nkFloatLit, rd(float, x))
   of tyFloat32: awf(nkFloat32Lit, rd(float32, x))
   of tyFloat64: awf(nkFloat64Lit, rd(float64, x))
@@ -363,24 +378,25 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
       # in their unboxed representation so nothing it to be unpacked:
       result = n
     else:
-      awi(nkPtrLit, cast[ByteAddress](p))
+      awi(nkPtrLit, cast[int](p))
   of tyPtr, tyRef, tyVar, tyLent:
     let p = rd(pointer, x)
     if p.isNil:
       setNil()
     elif n == nil or n.kind == nkPtrLit:
-      awi(nkPtrLit, cast[ByteAddress](p))
+      awi(nkPtrLit, cast[int](p))
     elif n != nil and n.len == 1:
-      internalAssert n.kind == nkRefTy
-      n.sons[0] = unpack(p, typ.lastSon, n.sons[0])
+      internalAssert(conf, n.kind == nkRefTy)
+      n[0] = unpack(conf, p, typ.elementType, n[0])
       result = n
     else:
-      globalError(n.info, "cannot map value from FFI " & typeToString(typ))
+      result = nil
+      globalError(conf, n.info, "cannot map value from FFI " & typeToString(typ))
   of tyObject, tyTuple:
-    result = unpackObject(x, typ, n)
+    result = unpackObject(conf, x, typ, n)
   of tyArray:
-    result = unpackArray(x, typ, n)
-  of tyCString, tyString:
+    result = unpackArray(conf, x, typ, n)
+  of tyCstring, tyString:
     let p = rd(cstring, x)
     if p.isNil:
       setNil()
@@ -389,14 +405,15 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
   of tyNil:
     setNil()
   of tyDistinct, tyGenericInst, tyAlias, tySink:
-    result = unpack(x, typ.lastSon, n)
+    result = unpack(conf, x, typ.skipModifier, n)
   else:
     # XXX what to do with 'array' here?
-    globalError(n.info, "cannot map value from FFI " & typeToString(typ))
+    result = nil
+    globalError(conf, n.info, "cannot map value from FFI " & typeToString(typ))
 
-proc fficast*(x: PNode, destTyp: PType): PNode =
+proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
   if x.kind == nkPtrLit and x.typ.kind in {tyPtr, tyRef, tyVar, tyLent, tyPointer,
-                                           tyProc, tyCString, tyString,
+                                           tyProc, tyCstring, tyString,
                                            tySequence}:
     result = newNodeIT(x.kind, x.info, destTyp)
     result.intVal = x.intVal
@@ -404,93 +421,93 @@ proc fficast*(x: PNode, destTyp: PType): PNode =
     result = newNodeIT(x.kind, x.info, destTyp)
   else:
     # we play safe here and allocate the max possible size:
-    let size = max(packSize(x, x.typ), packSize(x, destTyp))
+    let size = max(packSize(conf, x, x.typ), packSize(conf, x, destTyp))
     var a = alloc0(size)
-    pack(x, x.typ, a)
+    pack(conf, x, x.typ, a)
     # cast through a pointer needs a new inner object:
     let y = if x.kind == nkRefTy: newNodeI(nkRefTy, x.info, 1)
             else: x.copyTree
     y.typ = x.typ
-    result = unpack(a, destTyp, y)
+    result = unpack(conf, a, destTyp, y)
     dealloc a
 
-proc callForeignFunction*(call: PNode): PNode =
-  internalAssert call.sons[0].kind == nkPtrLit
+proc callForeignFunction*(conf: ConfigRef, call: PNode): PNode =
+  internalAssert conf, call[0].kind == nkPtrLit
 
-  var cif: TCif
-  var sig: TParamList
+  var cif: TCif = default(TCif)
+  var sig: ParamList = default(ParamList)
   # use the arguments' types for varargs support:
-  for i in 1..call.len-1:
-    sig[i-1] = mapType(call.sons[i].typ)
+  for i in 1..<call.len:
+    sig[i-1] = mapType(conf, call[i].typ)
     if sig[i-1].isNil:
-      globalError(call.info, "cannot map FFI type")
-
-  let typ = call.sons[0].typ
-  if prep_cif(cif, mapCallConv(typ.callConv, call.info), cuint(call.len-1),
-              mapType(typ.sons[0]), sig) != OK:
-    globalError(call.info, "error in FFI call")
-
-  var args: TArgList
-  let fn = cast[pointer](call.sons[0].intVal)
-  for i in 1 .. call.len-1:
-    var t = call.sons[i].typ
-    args[i-1] = alloc0(packSize(call.sons[i], t))
-    pack(call.sons[i], t, args[i-1])
-  let retVal = if isEmptyType(typ.sons[0]): pointer(nil)
-               else: alloc(typ.sons[0].getSize.int)
+      globalError(conf, call.info, "cannot map FFI type")
+
+  let typ = call[0].typ
+  if prep_cif(cif, mapCallConv(conf, typ.callConv, call.info), cuint(call.len-1),
+              mapType(conf, typ.returnType), sig) != OK:
+    globalError(conf, call.info, "error in FFI call")
+
+  var args: ArgList = default(ArgList)
+  let fn = cast[pointer](call[0].intVal)
+  for i in 1..<call.len:
+    var t = call[i].typ
+    args[i-1] = alloc0(packSize(conf, call[i], t))
+    pack(conf, call[i], t, args[i-1])
+  let retVal = if isEmptyType(typ.returnType): pointer(nil)
+               else: alloc(getSize(conf, typ.returnType).int)
 
   libffi.call(cif, fn, retVal, args)
 
   if retVal.isNil:
     result = newNode(nkEmpty)
   else:
-    result = unpack(retVal, typ.sons[0], nil)
+    result = unpack(conf, retVal, typ.returnType, nil)
     result.info = call.info
 
   if retVal != nil: dealloc retVal
-  for i in 1 .. call.len-1:
-    call.sons[i] = unpack(args[i-1], typ.sons[i], call[i])
+  for i in 1..<call.len:
+    call[i] = unpack(conf, args[i-1], typ[i], call[i])
     dealloc args[i-1]
 
-proc callForeignFunction*(fn: PNode, fntyp: PType,
+proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
                           args: var TNodeSeq, start, len: int,
                           info: TLineInfo): PNode =
-  internalAssert fn.kind == nkPtrLit
+  internalAssert conf, fn.kind == nkPtrLit
 
-  var cif: TCif
-  var sig: TParamList
+  var cif: TCif = default(TCif)
+  var sig: ParamList = default(ParamList)
   for i in 0..len-1:
     var aTyp = args[i+start].typ
     if aTyp.isNil:
-      internalAssert i+1 < fntyp.len
-      aTyp = fntyp.sons[i+1]
+      internalAssert conf, i+1 < fntyp.len
+      aTyp = fntyp[i+1]
       args[i+start].typ = aTyp
-    sig[i] = mapType(aTyp)
-    if sig[i].isNil: globalError(info, "cannot map FFI type")
+    sig[i] = mapType(conf, aTyp)
+    if sig[i].isNil: globalError(conf, info, "cannot map FFI type")
 
-  if prep_cif(cif, mapCallConv(fntyp.callConv, info), cuint(len),
-              mapType(fntyp.sons[0]), sig) != OK:
-    globalError(info, "error in FFI call")
+  if prep_cif(cif, mapCallConv(conf, fntyp.callConv, info), cuint(len),
+              mapType(conf, fntyp[0]), sig) != OK:
+    globalError(conf, info, "error in FFI call")
 
-  var cargs: TArgList
+  var cargs: ArgList = default(ArgList)
   let fn = cast[pointer](fn.intVal)
-  for i in 0 .. len-1:
+  for i in 0..len-1:
     let t = args[i+start].typ
-    cargs[i] = alloc0(packSize(args[i+start], t))
-    pack(args[i+start], t, cargs[i])
-  let retVal = if isEmptyType(fntyp.sons[0]): pointer(nil)
-               else: alloc(fntyp.sons[0].getSize.int)
+    cargs[i] = alloc0(packSize(conf, args[i+start], t))
+    pack(conf, args[i+start], t, cargs[i])
+  let retVal = if isEmptyType(fntyp[0]): pointer(nil)
+               else: alloc(getSize(conf, fntyp[0]).int)
 
   libffi.call(cif, fn, retVal, cargs)
 
   if retVal.isNil:
     result = newNode(nkEmpty)
   else:
-    result = unpack(retVal, fntyp.sons[0], nil)
+    result = unpack(conf, retVal, fntyp[0], nil)
     result.info = info
 
   if retVal != nil: dealloc retVal
-  for i in 0 .. len-1:
+  for i in 0..len-1:
     let t = args[i+start].typ
-    args[i+start] = unpack(cargs[i], t, args[i+start])
+    args[i+start] = unpack(conf, cargs[i], t, args[i+start])
     dealloc cargs[i]
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index d6c630e79..77c136d63 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -9,56 +9,103 @@
 
 ## Template evaluation engine. Now hygienic.
 
-import
-  strutils, options, ast, astalgo, msgs, os, idents, wordrecg, renderer,
-  lineinfos
+import options, ast, astalgo, msgs, renderer, lineinfos, idents, trees
+import std/strutils
 
 type
   TemplCtx = object
     owner, genSymOwner: PSym
     instLines: bool   # use the instantiation lines numbers
-    mapping: TIdTable # every gensym'ed symbol needs to be mapped to some
-                      # new symbol
+    isDeclarative: bool
+    mapping: SymMapping # every gensym'ed symbol needs to be mapped to some
+                        # new symbol
     config: ConfigRef
+    ic: IdentCache
+    instID: int
+    idgen: IdGenerator
 
 proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
   result = copyNode(a)
-  if ctx.instLines: result.info = b.info
+  if ctx.instLines: setInfoRecursive(result, b.info)
 
 proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
   template handleParam(param) =
     let x = param
     if x.kind == nkArgList:
       for y in items(x): result.add(y)
+    elif nfDefaultRefsParam in x.flags:
+      # value of default param needs to be evaluated like template body
+      # if it contains other template params
+      var res: PNode
+      if isAtom(x):
+        res = newNodeI(nkPar, x.info)
+        evalTemplateAux(x, actual, c, res)
+        if res.len == 1: res = res[0]
+      else:
+        res = copyNode(x)
+        for i in 0..<x.safeLen:
+          evalTemplateAux(x[i], actual, c, res)
+      result.add res
     else:
       result.add copyTree(x)
 
   case templ.kind
   of nkSym:
     var s = templ.sym
-    if s.owner.id == c.owner.id:
-      if s.kind == skParam and sfGenSym notin s.flags:
-        handleParam actual.sons[s.position]
-      elif s.kind == skGenericParam or
-           s.kind == skType and s.typ != nil and s.typ.kind == tyGenericParam:
-        handleParam actual.sons[s.owner.typ.len + s.position - 1]
+    if (s.owner == nil and s.kind == skParam) or s.owner == c.owner:
+      if s.kind == skParam and {sfGenSym, sfTemplateParam} * s.flags == {sfTemplateParam}:
+        handleParam actual[s.position]
+      elif (s.owner != nil) and (s.kind == skGenericParam or
+           s.kind == skType and s.typ != nil and s.typ.kind == tyGenericParam):
+        handleParam actual[s.owner.typ.signatureLen + s.position - 1]
       else:
         internalAssert c.config, sfGenSym in s.flags or s.kind == skType
-        var x = PSym(idTableGet(c.mapping, s))
+        var x = idTableGet(c.mapping, s)
         if x == nil:
-          x = copySym(s, false)
-          x.owner = c.genSymOwner
+          x = copySym(s, c.idgen)
+          # sem'check needs to set the owner properly later, see bug #9476
+          x.owner = nil # c.genSymOwner
+          #if x.kind == skParam and x.owner.kind == skModule:
+          #  internalAssert c.config, false
           idTablePut(c.mapping, s, x)
-        result.add newSymNode(x, if c.instLines: actual.info else: templ.info)
+        if sfGenSym in s.flags:
+          # TODO: getIdent(c.ic, "`" & x.name.s & "`gensym" & $c.instID)
+          result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $c.instID),
+            if c.instLines: actual.info else: templ.info)
+        else:
+          result.add newSymNode(x, if c.instLines: actual.info else: templ.info)
     else:
       result.add copyNode(c, templ, actual)
   of nkNone..nkIdent, nkType..nkNilLit: # atom
     result.add copyNode(c, templ, actual)
+  of nkCommentStmt:
+    # for the documentation generator we don't keep documentation comments
+    # in the AST that would confuse it (bug #9432), but only if we are not in a
+    # "declarative" context (bug #9235).
+    if c.isDeclarative:
+      var res = copyNode(c, templ, actual)
+      for i in 0..<templ.len:
+        evalTemplateAux(templ[i], actual, c, res)
+      result.add res
+    else:
+      result.add newNodeI(nkEmpty, templ.info)
   else:
-    var res = copyNode(c, templ, actual)
-    for i in countup(0, sonsLen(templ) - 1):
-      evalTemplateAux(templ.sons[i], actual, c, res)
-    result.add res
+    var isDeclarative = false
+    if templ.kind in {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef,
+                      nkMacroDef, nkTemplateDef, nkConverterDef, nkTypeSection,
+                      nkVarSection, nkLetSection, nkConstSection} and
+        not c.isDeclarative:
+      c.isDeclarative = true
+      isDeclarative = true
+    if (not c.isDeclarative) and templ.kind in nkCallKinds and isRunnableExamples(templ[0]):
+      # fixes bug #16993, bug #18054
+      discard
+    else:
+      var res = copyNode(c, templ, actual)
+      for i in 0..<templ.len:
+        evalTemplateAux(templ[i], actual, c, res)
+      result.add res
+    if isDeclarative: c.isDeclarative = false
 
 const
   errWrongNumberOfArguments = "wrong number of arguments"
@@ -81,9 +128,9 @@ proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode
     # their bodies. We could try to fix this, but it may be
     # wiser to just deprecate immediate templates and macros
     # now that we have working untyped parameters.
-    genericParams = if sfImmediate in s.flags or fromHlo: 0
+    genericParams = if fromHlo: 0
                     else: s.ast[genericParamsPos].len
-    expectedRegularParams = s.typ.len-1
+    expectedRegularParams = s.typ.paramsLen
     givenRegularParams = totalParams - genericParams
   if givenRegularParams < 0: givenRegularParams = 0
 
@@ -95,22 +142,22 @@ proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode
                 n.renderTree)
 
   result = newNodeI(nkArgList, n.info)
-  for i in 1 .. givenRegularParams:
-    result.addSon n[i]
+  for i in 1..givenRegularParams:
+    result.add n[i]
 
   # handle parameters with default values, which were
   # not supplied by the user
-  for i in givenRegularParams+1 .. expectedRegularParams:
-    let default = s.typ.n.sons[i].sym.ast
+  for i in givenRegularParams+1..expectedRegularParams:
+    let default = s.typ.n[i].sym.ast
     if default.isNil or default.kind == nkEmpty:
       localError(conf, n.info, errWrongNumberOfArguments)
-      addSon(result, newNodeI(nkEmpty, n.info))
+      result.add newNodeI(nkEmpty, n.info)
     else:
-      addSon(result, default.copyTree)
+      result.add default.copyTree
 
-  # add any generic paramaters
-  for i in 1 .. genericParams:
-    result.addSon n.sons[givenRegularParams + i]
+  # add any generic parameters
+  for i in 1..genericParams:
+    result.add n[givenRegularParams + i]
 
 # to prevent endless recursion in template instantiation
 const evalTemplateLimit* = 1000
@@ -128,7 +175,7 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
       if x.kind in nkCallKinds:
         for i in 1..<x.len:
           if x[i].kind in nkCallKinds:
-            x.sons[i].info = info
+            x[i].info = info
   else:
     result = newNodeI(nkStmtListExpr, info)
     var d = newNodeI(nkComesFrom, info)
@@ -138,7 +185,10 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
     result.typ = res.typ
 
 proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
-                   conf: ConfigRef; fromHlo=false): PNode =
+                   conf: ConfigRef;
+                   ic: IdentCache; instID: ref int;
+                   idgen: IdGenerator;
+                   fromHlo=false): PNode =
   inc(conf.evalTemplateCounter)
   if conf.evalTemplateCounter > evalTemplateLimit:
     globalError(conf, n.info, errTemplateInstantiationTooNested)
@@ -146,28 +196,35 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
 
   # replace each param by the corresponding node:
   var args = evalTemplateArgs(n, tmpl, conf, fromHlo)
-  var ctx: TemplCtx
-  ctx.owner = tmpl
-  ctx.genSymOwner = genSymOwner
-  ctx.config = conf
-  initIdTable(ctx.mapping)
-
-  let body = tmpl.getBody
+  var ctx = TemplCtx(owner: tmpl,
+    genSymOwner: genSymOwner,
+    config: conf,
+    ic: ic,
+    mapping: initSymMapping(),
+    instID: instID[],
+    idgen: idgen
+  )
+
+  let body = tmpl.ast[bodyPos]
+  #echo "instantion of ", renderTree(body, {renderIds})
   if isAtom(body):
     result = newNodeI(nkPar, body.info)
     evalTemplateAux(body, args, ctx, result)
-    if result.len == 1: result = result.sons[0]
+    if result.len == 1: result = result[0]
     else:
       localError(conf, result.info, "illformed AST: " &
                   renderTree(result, {renderNoComments}))
   else:
     result = copyNode(body)
-    #ctx.instLines = body.kind notin {nkStmtList, nkStmtListExpr,
-    #                                 nkBlockStmt, nkBlockExpr}
-    #if ctx.instLines: result.info = n.info
-    for i in countup(0, safeLen(body) - 1):
-      evalTemplateAux(body.sons[i], args, ctx, result)
+    ctx.instLines = sfCallsite in tmpl.flags
+    if ctx.instLines:
+      setInfoRecursive(result, n.info)
+    for i in 0..<body.safeLen:
+      evalTemplateAux(body[i], args, ctx, result)
   result.flags.incl nfFromTemplate
   result = wrapInComesFrom(n.info, tmpl, result)
+  #if ctx.debugActive:
+  #  echo "instantion of ", renderTree(result, {renderIds})
   dec(conf.evalTemplateCounter)
-
+  # The instID must be unique for every template instantiation, so we increment it here
+  inc instID[]
diff --git a/compiler/expanddefaults.nim b/compiler/expanddefaults.nim
new file mode 100644
index 000000000..c520d8849
--- /dev/null
+++ b/compiler/expanddefaults.nim
@@ -0,0 +1,131 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2023 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+import lineinfos, ast, types
+
+proc caseObjDefaultBranch*(obj: PNode; branch: Int128): int =
+  result = 0
+  for i in 1 ..< obj.len:
+    for j in 0 .. obj[i].len - 2:
+      if obj[i][j].kind == nkRange:
+        let x = getOrdValue(obj[i][j][0])
+        let y = getOrdValue(obj[i][j][1])
+        if branch >= x and branch <= y:
+          return i
+      elif getOrdValue(obj[i][j]) == branch:
+        return i
+    if obj[i].len == 1:
+      # else branch
+      return i
+  return 1
+
+template newZero(t: PType; info: TLineInfo; k = nkIntLit): PNode = newNodeIT(k, info, t)
+
+proc expandDefault*(t: PType; info: TLineInfo): PNode
+
+proc expandField(s: PSym; info: TLineInfo): PNode =
+  result = newNodeIT(nkExprColonExpr, info, s.typ)
+  result.add newSymNode(s)
+  result.add expandDefault(s.typ, info)
+
+proc expandDefaultN(n: PNode; info: TLineInfo; res: PNode) =
+  case n.kind
+  of nkRecList:
+    for i in 0..<n.len:
+      expandDefaultN(n[i], info, res)
+  of nkRecCase:
+    res.add expandField(n[0].sym, info)
+    var branch = Zero
+    let constOrNil = n[0].sym.astdef
+    if constOrNil != nil:
+      branch = getOrdValue(constOrNil)
+
+    let selectedBranch = caseObjDefaultBranch(n, branch)
+    let b = lastSon(n[selectedBranch])
+    expandDefaultN b, info, res
+  of nkSym:
+    res.add expandField(n.sym, info)
+  else:
+    discard
+
+proc expandDefaultObj(t: PType; info: TLineInfo; res: PNode) =
+  if t.baseClass != nil:
+    expandDefaultObj(t.baseClass, info, res)
+  expandDefaultN(t.n, info, res)
+
+proc expandDefault(t: PType; info: TLineInfo): PNode =
+  case t.kind
+  of tyInt:     result = newZero(t, info, nkIntLit)
+  of tyInt8:    result = newZero(t, info, nkInt8Lit)
+  of tyInt16:   result = newZero(t, info, nkInt16Lit)
+  of tyInt32:   result = newZero(t, info, nkInt32Lit)
+  of tyInt64:   result = newZero(t, info, nkInt64Lit)
+  of tyUInt:    result = newZero(t, info, nkUIntLit)
+  of tyUInt8:   result = newZero(t, info, nkUInt8Lit)
+  of tyUInt16:  result = newZero(t, info, nkUInt16Lit)
+  of tyUInt32:  result = newZero(t, info, nkUInt32Lit)
+  of tyUInt64:  result = newZero(t, info, nkUInt64Lit)
+  of tyFloat:   result = newZero(t, info, nkFloatLit)
+  of tyFloat32: result = newZero(t, info, nkFloat32Lit)
+  of tyFloat64: result = newZero(t, info, nkFloat64Lit)
+  of tyFloat128: result = newZero(t, info, nkFloat64Lit)
+  of tyChar:    result = newZero(t, info, nkCharLit)
+  of tyBool:    result = newZero(t, info, nkIntLit)
+  of tyEnum:
+    # Could use low(T) here to finally fix old language quirks
+    result = newZero(t, info, nkIntLit)
+  of tyRange:
+    # Could use low(T) here to finally fix old language quirks
+    result = expandDefault(skipModifier t, info)
+  of tyVoid: result = newZero(t, info, nkEmpty)
+  of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned:
+    result = expandDefault(t.skipModifier, info)
+  of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic:
+    if t.hasElementType:
+      result = expandDefault(t.skipModifier, info)
+    else:
+      result = newZero(t, info, nkEmpty)
+  of tyFromExpr:
+    if t.n != nil and t.n.typ != nil:
+      result = expandDefault(t.n.typ, info)
+    else:
+      result = newZero(t, info, nkEmpty)
+  of tyArray:
+    result = newZero(t, info, nkBracket)
+    let n = toInt64(lengthOrd(nil, t))
+    for i in 0..<n:
+      result.add expandDefault(t.elementType, info)
+  of tyPtr, tyRef, tyProc, tyPointer, tyCstring:
+    result = newZero(t, info, nkNilLit)
+  of tyVar, tyLent:
+    let e = t.elementType
+    if e.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
+      # skip the modifier, `var openArray` is a (ptr, len) pair too:
+      result = expandDefault(e, info)
+    else:
+      result = newZero(e, info, nkNilLit)
+  of tySet:
+    result = newZero(t, info, nkCurly)
+  of tyObject:
+    result = newNodeIT(nkObjConstr, info, t)
+    result.add newNodeIT(nkType, info, t)
+    expandDefaultObj(t, info, result)
+  of tyTuple:
+    result = newZero(t, info, nkTupleConstr)
+    for it in t.kids:
+      result.add expandDefault(it, info)
+  of tyVarargs, tyOpenArray, tySequence, tyUncheckedArray:
+    result = newZero(t, info, nkBracket)
+  of tyString:
+    result = newZero(t, info, nkStrLit)
+  of tyNone, tyEmpty, tyUntyped, tyTyped, tyTypeDesc,
+     tyNil, tyGenericInvocation, tyError, tyBuiltInTypeClass,
+     tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass,
+     tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward:
+    result = newZero(t, info, nkEmpty) # bug indicator
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 615b8c1e1..ce25da773 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -12,9 +12,16 @@
 # from a lineinfos file, to provide generalized procedures to compile
 # nim files.
 
-import
-  ropes, os, strutils, osproc, platform, condsyms, options, msgs,
-  lineinfos, std / sha1, streams
+import ropes, platform, condsyms, options, msgs, lineinfos, pathutils, modulepaths
+
+import std/[os, osproc, streams, sequtils, times, strtabs, json, jsonutils, sugar, parseutils]
+
+import std / strutils except addf
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions]
+
+import ../dist/checksums/src/checksums/sha1
 
 type
   TInfoCCProp* = enum         # properties of the C compiler:
@@ -26,6 +33,7 @@ type
     hasGnuAsm,                # CC's asm uses the absurd GNU assembler syntax
     hasDeclspec,              # CC has __declspec(X)
     hasAttribute,             # CC has __attribute__((X))
+    hasBuiltinUnreachable     # CC has __builtin_unreachable
   TInfoCCProps* = set[TInfoCCProp]
   TInfoCC* = tuple[
     name: string,        # the short name of the compiler
@@ -48,6 +56,8 @@ type
                          # used on some platforms
     asmStmtFrmt: string, # format of ASM statement
     structStmtFmt: string, # Format for struct statement
+    produceAsm: string,  # Format how to produce assembler listings
+    cppXsupport: string, # what to do to enable C++X support
     props: TInfoCCProps] # properties of the C compiler
 
 
@@ -58,13 +68,16 @@ type
 template compiler(name, settings: untyped): untyped =
   proc name: TInfoCC {.compileTime.} = settings
 
+const
+  gnuAsmListing = "-Wa,-acdl=$asmfile -g -fverbose-asm -masm=intel"
+
 # GNU C and C++ Compiler
 compiler gcc:
   result = (
     name: "gcc",
     objExt: "o",
-    optSpeed: " -O3 -ffast-math ",
-    optSize: " -Os -ffast-math ",
+    optSpeed: " -O3 -fno-ident",
+    optSize: " -Os -fno-ident",
     compilerExe: "gcc",
     cppCompiler: "g++",
     compileTmpl: "-c $options $include -o $objfile $file",
@@ -78,10 +91,39 @@ compiler gcc:
     linkLibCmd: " -l$1",
     debug: "",
     pic: "-fPIC",
+    asmStmtFrmt: "__asm__($1);$n",
+    structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
+    produceAsm: gnuAsmListing,
+    cppXsupport: "-std=gnu++17 -funsigned-char",
+    props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
+            hasAttribute, hasBuiltinUnreachable})
+
+# GNU C and C++ Compiler
+compiler nintendoSwitchGCC:
+  result = (
+    name: "switch_gcc",
+    objExt: "o",
+    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",
+    buildGui: " -mwindows",
+    buildDll: " -shared",
+    buildLib: "aarch64-none-elf-gcc-ar rcs $libfile $objfiles",
+    linkerExe: "aarch64-none-elf-gcc",
+    linkTmpl: "$buildgui $builddll -Wl,-Map,$mapfile -o $exefile $objfiles $options",
+    includeCmd: " -I",
+    linkDirCmd: " -L",
+    linkLibCmd: " -l$1",
+    debug: "",
+    pic: "-fPIE",
     asmStmtFrmt: "asm($1);$n",
     structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
+    produceAsm: gnuAsmListing,
+    cppXsupport: "-std=gnu++17 -funsigned-char",
     props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
-            hasAttribute})
+            hasAttribute, hasBuiltinUnreachable})
 
 # LLVM Frontend for GCC/G++
 compiler llvmGcc:
@@ -90,8 +132,8 @@ compiler llvmGcc:
   result.name = "llvm_gcc"
   result.compilerExe = "llvm-gcc"
   result.cppCompiler = "llvm-g++"
-  when defined(macosx):
-    # OS X has no 'llvm-ar' tool:
+  when defined(macosx) or defined(openbsd):
+    # `llvm-ar` not available
     result.buildLib = "ar rcs $libfile $objfiles"
   else:
     result.buildLib = "llvm-ar rcs $libfile $objfiles"
@@ -109,16 +151,16 @@ compiler vcc:
   result = (
     name: "vcc",
     objExt: "obj",
-    optSpeed: " /Ogityb2 /G7 /arch:SSE2 ",
-    optSize: " /O1 /G7 ",
+    optSpeed: " /Ogityb2 ",
+    optSize: " /O1 ",
     compilerExe: "cl",
     cppCompiler: "cl",
-    compileTmpl: "/c $options $include /Fo$objfile $file",
-    buildGui: " /link /SUBSYSTEM:WINDOWS ",
+    compileTmpl: "/c$vccplatform $options $include /nologo /Fo$objfile $file",
+    buildGui: " /SUBSYSTEM:WINDOWS user32.lib ",
     buildDll: " /LD",
-    buildLib: "lib /OUT:$libfile $objfiles",
+    buildLib: "vccexe --command:lib$vccplatform /nologo /OUT:$libfile $objfiles",
     linkerExe: "cl",
-    linkTmpl: "$options $builddll /Fe$exefile $objfiles $buildgui",
+    linkTmpl: "$builddll$vccplatform /Fe$exefile $objfiles $buildgui /nologo $options",
     includeCmd: " /I",
     linkDirCmd: " /LIBPATH:",
     linkLibCmd: " $1.lib",
@@ -126,8 +168,34 @@ compiler vcc:
     pic: "",
     asmStmtFrmt: "__asm{$n$1$n}$n",
     structStmtFmt: "$3$n$1 $2",
+    produceAsm: "/Fa$asmfile",
+    cppXsupport: "",
     props: {hasCpp, hasAssume, hasDeclspec})
 
+# Nvidia CUDA NVCC Compiler
+compiler nvcc:
+  result = gcc()
+  result.name = "nvcc"
+  result.compilerExe = "nvcc"
+  result.cppCompiler = "nvcc"
+  result.compileTmpl = "-c -x cu -Xcompiler=\"$options\" $include -o $objfile $file"
+  result.linkTmpl = "$buildgui $builddll -o $exefile $objfiles -Xcompiler=\"$options\""
+
+# AMD HIPCC Compiler (rocm/cuda)
+compiler hipcc:
+  result = clang()
+  result.name = "hipcc"
+  result.compilerExe = "hipcc"
+  result.cppCompiler = "hipcc"
+
+compiler clangcl:
+  result = vcc()
+  result.name = "clang_cl"
+  result.compilerExe = "clang-cl"
+  result.cppCompiler = "clang-cl"
+  result.linkerExe = "clang-cl"
+  result.linkTmpl = "-fuse-ld=lld " & result.linkTmpl
+
 # Intel C/C++ Compiler
 compiler icl:
   result = vcc()
@@ -142,30 +210,6 @@ compiler icc:
   result.compilerExe = "icc"
   result.linkerExe = "icc"
 
-# Local C Compiler
-compiler lcc:
-  result = (
-    name: "lcc",
-    objExt: "obj",
-    optSpeed: " -O -p6 ",
-    optSize: " -O -p6 ",
-    compilerExe: "lcc",
-    cppCompiler: "",
-    compileTmpl: "$options $include -Fo$objfile $file",
-    buildGui: " -subsystem windows",
-    buildDll: " -dll",
-    buildLib: "", # XXX: not supported yet
-    linkerExe: "lcclnk",
-    linkTmpl: "$options $buildgui $builddll -O $exefile $objfiles",
-    includeCmd: " -I",
-    linkDirCmd: "", # XXX: not supported yet
-    linkLibCmd: "", # XXX: not supported yet
-    debug: " -g5 ",
-    pic: "",
-    asmStmtFrmt: "_asm{$n$1$n}$n",
-    structStmtFmt: "$1 $2",
-    props: {})
-
 # Borland C Compiler
 compiler bcc:
   result = (
@@ -188,58 +232,11 @@ compiler bcc:
     pic: "",
     asmStmtFrmt: "__asm{$n$1$n}$n",
     structStmtFmt: "$1 $2",
+    produceAsm: "",
+    cppXsupport: "",
     props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard,
             hasAttribute})
 
-
-# Digital Mars C Compiler
-compiler dmc:
-  result = (
-    name: "dmc",
-    objExt: "obj",
-    optSpeed: " -ff -o -6 ",
-    optSize: " -ff -o -6 ",
-    compilerExe: "dmc",
-    cppCompiler: "",
-    compileTmpl: "-c $options $include -o$objfile $file",
-    buildGui: " -L/exet:nt/su:windows",
-    buildDll: " -WD",
-    buildLib: "", # XXX: not supported yet
-    linkerExe: "dmc",
-    linkTmpl: "$options $buildgui $builddll -o$exefile $objfiles",
-    includeCmd: " -I",
-    linkDirCmd: "", # XXX: not supported yet
-    linkLibCmd: "", # XXX: not supported yet
-    debug: " -g ",
-    pic: "",
-    asmStmtFrmt: "__asm{$n$1$n}$n",
-    structStmtFmt: "$3$n$1 $2",
-    props: {hasCpp})
-
-# Watcom C Compiler
-compiler wcc:
-  result = (
-    name: "wcc",
-    objExt: "obj",
-    optSpeed: " -ox -on -6 -d0 -fp6 -zW ",
-    optSize: "",
-    compilerExe: "wcl386",
-    cppCompiler: "",
-    compileTmpl: "-c $options $include -fo=$objfile $file",
-    buildGui: " -bw",
-    buildDll: " -bd",
-    buildLib: "", # XXX: not supported yet
-    linkerExe: "wcl386",
-    linkTmpl: "$options $buildgui $builddll -fe=$exefile $objfiles ",
-    includeCmd: " -i=",
-    linkDirCmd: "", # XXX: not supported yet
-    linkLibCmd: "", # XXX: not supported yet
-    debug: " -d2 ",
-    pic: "",
-    asmStmtFrmt: "__asm{$n$1$n}$n",
-    structStmtFmt: "$1 $2",
-    props: {hasCpp})
-
 # Tiny C Compiler
 compiler tcc:
   result = (
@@ -260,49 +257,26 @@ compiler tcc:
     linkLibCmd: "", # XXX: not supported yet
     debug: " -g ",
     pic: "",
-    asmStmtFrmt: "__asm{$n$1$n}$n",
-    structStmtFmt: "$1 $2",
-    props: {hasSwitchRange, hasComputedGoto})
-
-# Pelles C Compiler
-compiler pcc:
-  # Pelles C
-  result = (
-    name: "pcc",
-    objExt: "obj",
-    optSpeed: " -Ox ",
-    optSize: " -Os ",
-    compilerExe: "cc",
-    cppCompiler: "",
-    compileTmpl: "-c $options $include -Fo$objfile $file",
-    buildGui: " -SUBSYSTEM:WINDOWS",
-    buildDll: " -DLL",
-    buildLib: "", # XXX: not supported yet
-    linkerExe: "cc",
-    linkTmpl: "$options $buildgui $builddll -OUT:$exefile $objfiles",
-    includeCmd: " -I",
-    linkDirCmd: "", # XXX: not supported yet
-    linkLibCmd: "", # XXX: not supported yet
-    debug: " -Zi ",
-    pic: "",
-    asmStmtFrmt: "__asm{$n$1$n}$n",
+    asmStmtFrmt: "asm($1);$n",
     structStmtFmt: "$1 $2",
-    props: {})
+    produceAsm: gnuAsmListing,
+    cppXsupport: "",
+    props: {hasSwitchRange, hasComputedGoto, hasGnuAsm})
 
 # Your C Compiler
-compiler ucc:
+compiler envcc:
   result = (
-    name: "ucc",
+    name: "env",
     objExt: "o",
     optSpeed: " -O3 ",
     optSize: " -O1 ",
-    compilerExe: "cc",
+    compilerExe: "",
     cppCompiler: "",
-    compileTmpl: "-c $options $include -o $objfile $file",
+    compileTmpl: "-c $ccenvflags $options $include -o $objfile $file",
     buildGui: "",
     buildDll: " -shared ",
     buildLib: "", # XXX: not supported yet
-    linkerExe: "cc",
+    linkerExe: "",
     linkTmpl: "-o $exefile $buildgui $builddll $objfiles $options",
     includeCmd: " -I",
     linkDirCmd: "", # XXX: not supported yet
@@ -311,49 +285,63 @@ compiler ucc:
     pic: "",
     asmStmtFrmt: "__asm{$n$1$n}$n",
     structStmtFmt: "$1 $2",
-    props: {})
+    produceAsm: "",
+    cppXsupport: "",
+    props: {hasGnuAsm})
 
 const
   CC*: array[succ(low(TSystemCC))..high(TSystemCC), TInfoCC] = [
     gcc(),
+    nintendoSwitchGCC(),
     llvmGcc(),
     clang(),
-    lcc(),
     bcc(),
-    dmc(),
-    wcc(),
     vcc(),
     tcc(),
-    pcc(),
-    ucc(),
+    envcc(),
     icl(),
-    icc()]
+    icc(),
+    clangcl(),
+    hipcc(),
+    nvcc()]
 
   hExt* = ".h"
 
-proc libNameTmpl(conf: ConfigRef): string {.inline.} =
-  result = if conf.target.targetOS == osWindows: "$1.lib" else: "lib$1.a"
+template writePrettyCmdsStderr(cmd) =
+  if cmd.len > 0:
+    flushDot(conf)
+    stderr.writeLine(cmd)
 
 proc nameToCC*(name: string): TSystemCC =
   ## Returns the kind of compiler referred to by `name`, or ccNone
   ## if the name doesn't refer to any known compiler.
-  for i in countup(succ(ccNone), high(TSystemCC)):
+  for i in succ(ccNone)..high(TSystemCC):
     if cmpIgnoreStyle(name, CC[i].name) == 0:
       return i
   result = ccNone
 
+proc listCCnames(): string =
+  result = ""
+  for i in succ(ccNone)..high(TSystemCC):
+    if i > succ(ccNone): result.add ", "
+    result.add CC[i].name
+
+proc isVSCompatible*(conf: ConfigRef): bool =
+  return conf.cCompiler == ccVcc or
+          conf.cCompiler == ccClangCl or
+          (conf.cCompiler == ccIcl and conf.target.hostOS in osDos..osWindows)
+
 proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
   # use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given
   # for niminst support
-  let fullSuffix =
-    if conf.cmd == cmdCompileToCpp:
-      ".cpp" & suffix
-    elif conf.cmd == cmdCompileToOC:
-      ".objc" & suffix
-    elif conf.cmd == cmdCompileToJS:
-      ".js" & suffix
-    else:
-      suffix
+  var fullSuffix = suffix
+  case conf.backend
+  of backendCpp, backendJs, backendObjc: fullSuffix = "." & $conf.backend & suffix
+  of backendC: discard
+  of backendInvalid:
+    # during parsing of cfg files; we don't know the backend yet, no point in
+    # guessing wrong thing
+    return ""
 
   if (conf.target.hostOS != conf.target.targetOS or conf.target.hostCPU != conf.target.targetCPU) and
       optCompileOnly notin conf.globalOptions:
@@ -361,8 +349,10 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
                      platform.OS[conf.target.targetOS].name & '.' &
                      CC[c].name & fullSuffix
     result = getConfigVar(conf, fullCCname)
-    if result.len == 0:
-      # not overriden for this cross compilation setting?
+    if existsConfigVar(conf, fullCCname):
+      result = getConfigVar(conf, fullCCname)
+    else:
+      # not overridden for this cross compilation setting?
       result = getConfigVar(conf, CC[c].name & fullSuffix)
   else:
     result = getConfigVar(conf, CC[c].name & fullSuffix)
@@ -370,16 +360,16 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
 proc setCC*(conf: ConfigRef; ccname: string; info: TLineInfo) =
   conf.cCompiler = nameToCC(ccname)
   if conf.cCompiler == ccNone:
-    localError(conf, info, "unknown C compiler: '$1'" % ccname)
+    localError(conf, info, "unknown C compiler: '$1'. Available options are: $2" % [ccname, listCCnames()])
   conf.compileOptions = getConfigVar(conf, conf.cCompiler, ".options.always")
   conf.linkOptions = ""
-  conf.ccompilerpath = getConfigVar(conf, conf.cCompiler, ".path")
-  for i in countup(low(CC), high(CC)): undefSymbol(conf.symbols, CC[i].name)
+  conf.cCompilerPath = getConfigVar(conf, conf.cCompiler, ".path")
+  for c in CC: undefSymbol(conf.symbols, c.name)
   defineSymbol(conf.symbols, CC[conf.cCompiler].name)
 
 proc addOpt(dest: var string, src: string) =
-  if len(dest) == 0 or dest[len(dest)-1] != ' ': add(dest, " ")
-  add(dest, src)
+  if dest.len == 0 or dest[^1] != ' ': dest.add(" ")
+  dest.add(src)
 
 proc addLinkOption*(conf: ConfigRef; option: string) =
   addOpt(conf.linkOptions, option)
@@ -396,26 +386,32 @@ proc addCompileOptionCmd*(conf: ConfigRef; option: string) =
 
 proc initVars*(conf: ConfigRef) =
   # we need to define the symbol here, because ``CC`` may have never been set!
-  for i in countup(low(CC), high(CC)): undefSymbol(conf.symbols, CC[i].name)
+  for c in CC: undefSymbol(conf.symbols, c.name)
   defineSymbol(conf.symbols, CC[conf.cCompiler].name)
   addCompileOption(conf, getConfigVar(conf, conf.cCompiler, ".options.always"))
   #addLinkOption(getConfigVar(cCompiler, ".options.linker"))
-  if len(conf.ccompilerpath) == 0:
-    conf.ccompilerpath = getConfigVar(conf, conf.cCompiler, ".path")
+  if conf.cCompilerPath.len == 0:
+    conf.cCompilerPath = getConfigVar(conf, conf.cCompiler, ".path")
 
-proc completeCFilePath*(conf: ConfigRef; cfile: string, createSubDir: bool = true): string =
+proc completeCfilePath*(conf: ConfigRef; cfile: AbsoluteFile,
+                        createSubDir: bool = true): AbsoluteFile =
+  ## Generate the absolute file path to the generated modules.
   result = completeGeneratedFilePath(conf, cfile, createSubDir)
 
-proc toObjFile*(conf: ConfigRef; filename: string): string =
+proc toObjFile*(conf: ConfigRef; filename: AbsoluteFile): AbsoluteFile =
   # Object file for compilation
-  #if filename.endsWith(".cpp"):
-  #  result = changeFileExt(filename, "cpp." & CC[cCompiler].objExt)
-  #else:
-  result = changeFileExt(filename, CC[conf.cCompiler].objExt)
+  result = AbsoluteFile(filename.string & "." & CC[conf.cCompiler].objExt)
 
 proc addFileToCompile*(conf: ConfigRef; cf: Cfile) =
   conf.toCompile.add(cf)
 
+proc addLocalCompileOption*(conf: ConfigRef; option: string; nimfile: AbsoluteFile) =
+  let key = completeCfilePath(conf, mangleModuleName(conf, nimfile).AbsoluteFile).string
+  var value = conf.cfileSpecificOptions.getOrDefault(key)
+  if strutils.find(value, option, 0) < 0:
+    addOpt(value, option)
+    conf.cfileSpecificOptions[key] = value
+
 proc resetCompilationLists*(conf: ConfigRef) =
   conf.toCompile.setLen 0
   ## XXX: we must associate these with their originating module
@@ -424,11 +420,11 @@ proc resetCompilationLists*(conf: ConfigRef) =
   # Maybe we can do that in checkDep on the other hand?
   conf.externalToLink.setLen 0
 
-proc addExternalFileToLink*(conf: ConfigRef; filename: string) =
-  conf.externalToLink.insert(filename, 0)
+proc addExternalFileToLink*(conf: ConfigRef; filename: AbsoluteFile) =
+  conf.externalToLink.insert(filename.string, 0)
 
 proc execWithEcho(conf: ConfigRef; cmd: string, msg = hintExecuting): int =
-  rawMessage(conf, msg, cmd)
+  rawMessage(conf, msg, if msg == hintLinking and not(optListCmd in conf.globalOptions or conf.verbosity > 1): "" else: cmd)
   result = execCmd(cmd)
 
 proc execExternalProgram*(conf: ConfigRef; cmd: string, msg = hintExecuting) =
@@ -436,14 +432,12 @@ proc execExternalProgram*(conf: ConfigRef; cmd: string, msg = hintExecuting) =
     rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
       cmd)
 
-proc generateScript(conf: ConfigRef; projectFile: string, script: Rope) =
-  let (dir, name, ext) = splitFile(projectFile)
-  let filename = getNimcacheDir(conf) / addFileExt("compile_" & name,
-                                     platform.OS[conf.target.targetOS].scriptExt)
-  if writeRope(script, filename):
-    copyFile(conf.libpath / "nimbase.h", getNimcacheDir(conf) / "nimbase.h")
-  else:
-    rawMessage(conf, errGenerated, "could not write to file: " & filename)
+proc generateScript(conf: ConfigRef; script: Rope) =
+  let (_, name, _) = splitFile(conf.outFile.string)
+  let filename = getNimcacheDir(conf) / RelativeFile(addFileExt("compile_" & name,
+                                     platform.OS[conf.target.targetOS].scriptExt))
+  if not writeRope(script, filename):
+    rawMessage(conf, errGenerated, "could not write to file: " & filename.string)
 
 proc getOptSpeed(conf: ConfigRef; c: TSystemCC): string =
   result = getConfigVar(conf, c, ".options.speed")
@@ -465,32 +459,56 @@ proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} =
   # really: Cross compilation from Linux to Linux for example is entirely
   # reasonable.
   # `optGenMapping` is included here for niminst.
-  result = conf.globalOptions * {optGenScript, optGenMapping} != {}
+  # We use absolute paths for vcc / cl, see issue #19883.
+  let options =
+    if conf.cCompiler == ccVcc:
+      {optGenMapping}
+    else:
+      {optGenScript, optGenMapping}
+  result = conf.globalOptions * options != {}
 
-proc cFileSpecificOptions(conf: ConfigRef; cfilename: string): string =
+proc cFileSpecificOptions(conf: ConfigRef; nimname, fullNimFile: string): string =
   result = conf.compileOptions
+
   for option in conf.compileOptionsCmd:
     if strutils.find(result, option, 0) < 0:
       addOpt(result, option)
 
-  let trunk = splitFile(cfilename).name
   if optCDebug in conf.globalOptions:
-    let key = trunk & ".debug"
+    let key = nimname & ".debug"
     if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
     else: addOpt(result, getDebug(conf, conf.cCompiler))
   if optOptimizeSpeed in conf.options:
-    let key = trunk & ".speed"
+    let key = nimname & ".speed"
     if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
     else: addOpt(result, getOptSpeed(conf, conf.cCompiler))
   elif optOptimizeSize in conf.options:
-    let key = trunk & ".size"
+    let key = nimname & ".size"
     if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
     else: addOpt(result, getOptSize(conf, conf.cCompiler))
-  let key = trunk & ".always"
+  let key = nimname & ".always"
   if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
 
+  addOpt(result, conf.cfileSpecificOptions.getOrDefault(fullNimFile))
+
 proc getCompileOptions(conf: ConfigRef): string =
-  result = cFileSpecificOptions(conf, "__dummy__")
+  result = cFileSpecificOptions(conf, "__dummy__", "__dummy__")
+
+proc vccplatform(conf: ConfigRef): string =
+  # VCC specific but preferable over the config hacks people
+  # had to do before, see #11306
+  if conf.cCompiler == ccVcc:
+    let exe = getConfigVar(conf, conf.cCompiler, ".exe")
+    if "vccexe.exe" == extractFilename(exe):
+      result = case conf.target.targetCPU
+        of cpuI386: " --platform:x86"
+        of cpuArm: " --platform:arm"
+        of cpuAmd64: " --platform:amd64"
+        else: ""
+    else:
+      result = ""
+  else:
+    result = ""
 
 proc getLinkOptions(conf: ConfigRef): string =
   result = conf.linkOptions & " " & conf.linkOptionsCmd & " "
@@ -503,178 +521,275 @@ proc needsExeExt(conf: ConfigRef): bool {.inline.} =
   result = (optGenScript in conf.globalOptions and conf.target.targetOS == osWindows) or
            (conf.target.hostOS == osWindows)
 
-proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: string): string =
-  result = if conf.cmd == cmdCompileToCpp and not cfile.endsWith(".c"):
-             CC[compiler].cppCompiler
-           else:
-             CC[compiler].compilerExe
+proc useCpp(conf: ConfigRef; cfile: AbsoluteFile): bool =
+  # List of possible file extensions taken from gcc
+  for ext in [".C", ".cc", ".cpp", ".CPP", ".c++", ".cp", ".cxx"]:
+    if cfile.string.endsWith(ext): return true
+  false
+
+proc envFlags(conf: ConfigRef): string =
+  result = if conf.backend == backendCpp:
+            getEnv("CXXFLAGS")
+          else:
+            getEnv("CFLAGS")
+
+proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; isCpp: bool): string =
+  if compiler == ccEnv:
+    result = if isCpp:
+              getEnv("CXX")
+            else:
+              getEnv("CC")
+  else:
+    result = if isCpp:
+              CC[compiler].cppCompiler
+            else:
+              CC[compiler].compilerExe
   if result.len == 0:
     rawMessage(conf, errGenerated,
       "Compiler '$1' doesn't support the requested target" %
       CC[compiler].name)
 
+proc ccHasSaneOverflow*(conf: ConfigRef): bool =
+  if conf.cCompiler == ccGcc:
+    result = false # assume an old or crappy GCC
+    var exe = getConfigVar(conf, conf.cCompiler, ".exe")
+    if exe.len == 0: exe = CC[conf.cCompiler].compilerExe
+    # NOTE: should we need the full version, use -dumpfullversion
+    let (s, exitCode) = try: execCmdEx(exe & " -dumpversion") except IOError, OSError, ValueError: ("", 1)
+    if exitCode == 0:
+      var major: int = 0
+      discard parseInt(s, major)
+      result = major >= 5
+  else:
+    result = conf.cCompiler == ccCLang
+
 proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string =
   result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe
-           elif optMixedMode in conf.globalOptions and conf.cmd != cmdCompileToCpp: CC[compiler].cppCompiler
-           else: getCompilerExe(conf, compiler, "")
+           else: getCompilerExe(conf, compiler, optMixedMode in conf.globalOptions or conf.backend == backendCpp)
+
+proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile,
+                         isMainFile = false; produceOutput = false): string =
+  let
+    c = conf.cCompiler
+    isCpp = useCpp(conf, cfile.cname)
+  # We produce files like module.nim.cpp, so the absolute Nim filename is not
+  # cfile.name but `cfile.cname.changeFileExt("")`:
+  var options = cFileSpecificOptions(conf, cfile.nimname, cfile.cname.changeFileExt("").string)
+  if isCpp:
+    # needs to be prepended so that --passc:-std=c++17 can override default.
+    # we could avoid allocation by making cFileSpecificOptions inplace
+    options = CC[c].cppXsupport & ' ' & options
+    # If any C++ file was compiled, we need to use C++ driver for linking as well
+    incl conf.globalOptions, optMixedMode
 
-proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string =
-  var c = conf.cCompiler
-  var options = cFileSpecificOptions(conf, cfile.cname)
   var exe = getConfigVar(conf, c, ".exe")
-  if exe.len == 0: exe = getCompilerExe(conf, c, cfile.cname)
+  if exe.len == 0: exe = getCompilerExe(conf, c, isCpp)
 
   if needsExeExt(conf): exe = addFileExt(exe, "exe")
-  if optGenDynLib in conf.globalOptions and
+  if (optGenDynLib in conf.globalOptions or (conf.hcrOn and not isMainFile)) and
       ospNeedsPIC in platform.OS[conf.target.targetOS].props:
-    add(options, ' ' & CC[c].pic)
+    options.add(' ' & CC[c].pic)
 
-  var includeCmd, compilePattern: string
-  if not noAbsolutePaths(conf):
-    # compute include paths:
-    includeCmd = CC[c].includeCmd & quoteShell(conf.libpath)
+  if cfile.customArgs != "":
+    options.add ' '
+    options.add cfile.customArgs
 
+  var compilePattern: string
+  # compute include paths:
+  var includeCmd = CC[c].includeCmd & quoteShell(conf.libpath)
+  if not noAbsolutePaths(conf):
     for includeDir in items(conf.cIncludes):
       includeCmd.add(join([CC[c].includeCmd, includeDir.quoteShell]))
 
-    compilePattern = joinPath(conf.ccompilerpath, exe)
+    compilePattern = joinPath(conf.cCompilerPath, exe)
   else:
-    includeCmd = ""
-    compilePattern = getCompilerExe(conf, c, cfile.cname)
+    compilePattern = exe
 
-  var cf = if noAbsolutePaths(conf): extractFilename(cfile.cname)
+  includeCmd.add(join([CC[c].includeCmd, quoteShell(conf.projectPath.string)]))
+
+  let cf = if noAbsolutePaths(conf): AbsoluteFile extractFilename(cfile.cname.string)
            else: cfile.cname
 
-  var objfile =
-    if cfile.obj.len == 0:
-      if not cfile.flags.contains(CfileFlag.External) or noAbsolutePaths(conf):
-        toObjFile(conf, cf)
+  let objfile =
+    if cfile.obj.isEmpty:
+      if CfileFlag.External notin cfile.flags or noAbsolutePaths(conf):
+        toObjFile(conf, cf).string
       else:
-        completeCFilePath(conf, toObjFile(conf, cf))
+        completeCfilePath(conf, toObjFile(conf, cf)).string
     elif noAbsolutePaths(conf):
-      extractFilename(cfile.obj)
+      extractFilename(cfile.obj.string)
     else:
-      cfile.obj
+      cfile.obj.string
+
+  # D files are required by nintendo switch libs for
+  # compilation. They are basically a list of all includes.
+  let dfile = objfile.changeFileExt(".d").quoteShell
 
-  objfile = quoteShell(objfile)
-  cf = quoteShell(cf)
+  let cfsh = quoteShell(cf)
   result = quoteShell(compilePattern % [
-    "file", cf, "objfile", objfile, "options", options,
-    "include", includeCmd, "nim", getPrefixDir(conf),
-    "nim", getPrefixDir(conf), "lib", conf.libpath])
-  add(result, ' ')
-  addf(result, CC[c].compileTmpl, [
-    "file", cf, "objfile", objfile,
+    "dfile", dfile,
+    "file", cfsh, "objfile", quoteShell(objfile), "options", options,
+    "include", includeCmd, "nim", getPrefixDir(conf).string,
+    "lib", conf.libpath.string,
+    "ccenvflags", envFlags(conf)])
+
+  if optProduceAsm in conf.globalOptions:
+    if CC[conf.cCompiler].produceAsm.len > 0:
+      let asmfile = objfile.changeFileExt(".asm").quoteShell
+      addOpt(result, CC[conf.cCompiler].produceAsm % ["asmfile", asmfile])
+      if produceOutput:
+        rawMessage(conf, hintUserRaw, "Produced assembler here: " & asmfile)
+    else:
+      if produceOutput:
+        rawMessage(conf, hintUserRaw, "Couldn't produce assembler listing " &
+          "for the selected C compiler: " & CC[conf.cCompiler].name)
+
+  result.add(' ')
+  strutils.addf(result, CC[c].compileTmpl, [
+    "dfile", dfile,
+    "file", cfsh, "objfile", quoteShell(objfile),
     "options", options, "include", includeCmd,
     "nim", quoteShell(getPrefixDir(conf)),
-    "nim", quoteShell(getPrefixDir(conf)),
-    "lib", quoteShell(conf.libpath)])
+    "lib", quoteShell(conf.libpath),
+    "vccplatform", vccplatform(conf),
+    "ccenvflags", envFlags(conf)])
 
 proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash =
   result = secureHash(
-    $secureHashFile(cfile.cname) &
+    $secureHashFile(cfile.cname.string) &
     platform.OS[conf.target.targetOS].name &
     platform.CPU[conf.target.targetCPU].name &
     extccomp.CC[conf.cCompiler].name &
     getCompileCFileCmd(conf, cfile))
 
 proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
-  if conf.cmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
-    return false
+  if conf.backend == backendJs: return false # pre-existing behavior, but not sure it's good
 
-  var hashFile = toGeneratedFile(conf, conf.withPackageName(cfile.cname), "sha1")
-  var currentHash = footprint(conf, cfile)
-  var f: File
-  if open(f, hashFile, fmRead):
+  let hashFile = toGeneratedFile(conf, conf.mangleModuleName(cfile.cname).AbsoluteFile, "sha1")
+  let currentHash = footprint(conf, cfile)
+  var f: File = default(File)
+  if open(f, hashFile.string, fmRead):
     let oldHash = parseSecureHash(f.readLine())
     close(f)
     result = oldHash != currentHash
   else:
     result = true
   if result:
-    if open(f, hashFile, fmWrite):
+    if open(f, hashFile.string, fmWrite):
       f.writeLine($currentHash)
       close(f)
 
 proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) =
-  if optForceFullMake notin conf.globalOptions and not externalFileChanged(conf, c):
+  # we want to generate the hash file unconditionally
+  let extFileChanged = externalFileChanged(conf, c)
+  if optForceFullMake notin conf.globalOptions and fileExists(c.obj) and
+      not extFileChanged:
     c.flags.incl CfileFlag.Cached
+  else:
+    # make sure Nim keeps recompiling the external file on reruns
+    # if compilation is not successful
+    discard tryRemoveFile(c.obj.string)
   conf.toCompile.add(c)
 
-proc addExternalFileToCompile*(conf: ConfigRef; filename: string) =
-  var c = Cfile(cname: filename,
-    obj: toObjFile(conf, completeCFilePath(conf, changeFileExt(filename, ""), false)),
+proc addExternalFileToCompile*(conf: ConfigRef; filename: AbsoluteFile) =
+  var c = Cfile(nimname: splitFile(filename).name, cname: filename,
+    obj: toObjFile(conf, completeCfilePath(conf, filename, false)),
     flags: {CfileFlag.External})
   addExternalFileToCompile(conf, c)
 
-proc compileCFile(conf: ConfigRef; list: CFileList, script: var Rope, cmds: var TStringSeq,
-                  prettyCmds: var TStringSeq) =
-  for it in list:
-    # call the C compiler for the .c file:
-    if it.flags.contains(CfileFlag.Cached): continue
-    var compileCmd = getCompileCFileCmd(conf, it)
-    if optCompileOnly notin conf.globalOptions:
-      add(cmds, compileCmd)
-      let (_, name, _) = splitFile(it.cname)
-      add(prettyCmds, "CC: " & name)
-    if optGenScript in conf.globalOptions:
-      add(script, compileCmd)
-      add(script, "\n")
-
-proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string =
+proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
+                objfiles: string, isDllBuild: bool, removeStaticFile: bool): string =
   if optGenStaticLib in conf.globalOptions:
-    var libname: string
-    if conf.outFile.len > 0:
-      libname = conf.outFile.expandTilde
-      if not libname.isAbsolute():
-        libname = getCurrentDir() / libname
-    else:
-      libname = (libNameTmpl(conf) % splitFile(conf.projectName).name)
-    result = CC[conf.cCompiler].buildLib % ["libfile", libname,
-                                       "objfiles", objfiles]
+    if removeStaticFile:
+      removeFile output # fixes: bug #16947
+    result = CC[conf.cCompiler].buildLib % ["libfile", quoteShell(output),
+                                            "objfiles", objfiles,
+                                            "vccplatform", vccplatform(conf)]
   else:
     var linkerExe = getConfigVar(conf, conf.cCompiler, ".linkerexe")
-    if len(linkerExe) == 0: linkerExe = getLinkerExe(conf, conf.cCompiler)
+    if linkerExe.len == 0: linkerExe = getLinkerExe(conf, conf.cCompiler)
     # bug #6452: We must not use ``quoteShell`` here for ``linkerExe``
     if needsExeExt(conf): linkerExe = addFileExt(linkerExe, "exe")
     if noAbsolutePaths(conf): result = linkerExe
-    else: result = joinPath(conf.cCompilerpath, linkerExe)
-    let buildgui = if optGenGuiApp in conf.globalOptions: CC[conf.cCompiler].buildGui
-                   else: ""
-    var exefile, builddll: string
-    if optGenDynLib in conf.globalOptions:
-      exefile = platform.OS[conf.target.targetOS].dllFrmt % splitFile(projectfile).name
-      builddll = CC[conf.cCompiler].buildDll
-    else:
-      exefile = splitFile(projectfile).name & platform.OS[conf.target.targetOS].exeExt
-      builddll = ""
-    if conf.outFile.len > 0:
-      exefile = conf.outFile.expandTilde
-      if not exefile.isAbsolute():
-        exefile = getCurrentDir() / exefile
-    if not noAbsolutePaths(conf):
-      if not exefile.isAbsolute():
-        exefile = joinPath(splitFile(projectfile).dir, exefile)
+    else: result = joinPath(conf.cCompilerPath, linkerExe)
+    let buildgui = if optGenGuiApp in conf.globalOptions and conf.target.targetOS == osWindows:
+                     CC[conf.cCompiler].buildGui
+                   else:
+                     ""
+    let builddll = if isDllBuild: CC[conf.cCompiler].buildDll else: ""
+    let exefile = quoteShell(output)
+
     when false:
       if optCDebug in conf.globalOptions:
         writeDebugInfo(exefile.changeFileExt("ndb"))
-    exefile = quoteShell(exefile)
+
+    # Map files are required by Nintendo Switch compilation. They are a list
+    # of all function calls in the library and where they come from.
+    let mapfile = quoteShell(getNimcacheDir(conf) / RelativeFile(splitFile(output).name & ".map"))
+
     let linkOptions = getLinkOptions(conf) & " " &
                       getConfigVar(conf, conf.cCompiler, ".options.linker")
     var linkTmpl = getConfigVar(conf, conf.cCompiler, ".linkTmpl")
     if linkTmpl.len == 0:
       linkTmpl = CC[conf.cCompiler].linkTmpl
     result = quoteShell(result % ["builddll", builddll,
+        "mapfile", mapfile,
         "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
-        "exefile", exefile, "nim", getPrefixDir(conf), "lib", conf.libpath])
+        "exefile", exefile, "nim", getPrefixDir(conf).string, "lib", conf.libpath.string])
     result.add ' '
-    addf(result, linkTmpl, ["builddll", builddll,
+    strutils.addf(result, linkTmpl, ["builddll", builddll,
+        "mapfile", mapfile,
         "buildgui", buildgui, "options", linkOptions,
         "objfiles", objfiles, "exefile", exefile,
         "nim", quoteShell(getPrefixDir(conf)),
-        "lib", quoteShell(conf.libpath)])
-
-template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body: untyped): typed =
+        "lib", quoteShell(conf.libpath),
+        "vccplatform", vccplatform(conf)])
+    # On windows the debug information for binaries is emitted in a separate .pdb
+    # file and the binaries (.dll and .exe) contain a full path to that .pdb file.
+    # This is a problem for hot code reloading because even when we copy the .dll
+    # and load the copy so the build process may overwrite the original .dll on
+    # the disk (windows locks the files of running binaries) the copy still points
+    # to the original .pdb (and a simple copy of the .pdb won't help). This is a
+    # problem when a debugger is attached to the program we are hot-reloading.
+    # This problem is nonexistent on Unix since there by default debug symbols
+    # are embedded in the binaries so loading a copy of a .so will be fine. There
+    # is the '/Z7' flag for the MSVC compiler to embed the debug info of source
+    # files into their respective .obj files but the linker still produces a .pdb
+    # when a final .dll or .exe is linked so the debug info isn't embedded.
+    # There is also the issue that even when a .dll is unloaded the debugger
+    # still keeps the .pdb for that .dll locked. This is a major problem and
+    # because of this we cannot just alternate between 2 names for a .pdb file
+    # when rebuilding a .dll - instead we need to accumulate differently named
+    # .pdb files in the nimcache folder - this is the easiest and most reliable
+    # way of being able to debug and rebuild the program at the same time. This
+    # is accomplished using the /PDB:<filename> flag (there also exists the
+    # /PDBALTPATH:<filename> flag). The only downside is that the .pdb files are
+    # at least 300kb big (when linking statically to the runtime - or else 5mb+)
+    # and will quickly accumulate. There is a hacky solution: we could try to
+    # delete all .pdb files with a pattern and swallow exceptions.
+    #
+    # links about .pdb files and hot code reloading:
+    # https://ourmachinery.com/post/dll-hot-reloading-in-theory-and-practice/
+    # https://ourmachinery.com/post/little-machines-working-together-part-2/
+    # https://github.com/fungos/cr
+    # https://fungos.github.io/blog/2017/11/20/cr.h-a-simple-c-hot-reload-header-only-library/
+    # on forcing the debugger to unlock a locked .pdb of an unloaded library:
+    # https://blog.molecular-matters.com/2017/05/09/deleting-pdb-files-locked-by-visual-studio/
+    # and a bit about the .pdb format in case that is ever needed:
+    # https://github.com/crosire/blink
+    # http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles
+    if conf.hcrOn and isVSCompatible(conf):
+      let t = now()
+      let pdb = output.string & "." & format(t, "MMMM-yyyy-HH-mm-") & $t.nanosecond & ".pdb"
+      result.add " /link /PDB:" & pdb
+  if optCDebug in conf.globalOptions and conf.cCompiler == ccVcc:
+    result.add " /Zi /FS /Od"
+
+template getLinkCmd(conf: ConfigRef; output: AbsoluteFile, objfiles: string,
+                    removeStaticFile = false): string =
+  getLinkCmd(conf, output, objfiles, optGenDynLib in conf.globalOptions, removeStaticFile)
+
+template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body: untyped) =
   try:
     body
   except OSError:
@@ -686,22 +801,27 @@ template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body
         (ose.msg & " " & $ose.errorCode))
     raise
 
+proc getExtraCmds(conf: ConfigRef; output: AbsoluteFile): seq[string] =
+  result = @[]
+  when defined(macosx):
+    if optCDebug in conf.globalOptions and optGenStaticLib notin conf.globalOptions:
+      # if needed, add an option to skip or override location
+      result.add "dsymutil " & $(output).quoteShell
+
 proc execLinkCmd(conf: ConfigRef; linkCmd: string) =
   tryExceptOSErrorMessage(conf, "invocation of external linker program failed."):
-    execExternalProgram(conf, linkCmd,
-      if optListCmd in conf.globalOptions or conf.verbosity > 1: hintExecuting else: hintLinking)
+    execExternalProgram(conf, linkCmd, hintLinking)
 
 proc execCmdsInParallel(conf: ConfigRef; cmds: seq[string]; prettyCb: proc (idx: int)) =
   let runCb = proc (idx: int, p: Process) =
     let exitCode = p.peekExitCode
     if exitCode != 0:
       rawMessage(conf, errGenerated, "execution of an external compiler program '" &
-        cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" &
-        p.outputStream.readAll.strip)
+        cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n")
   if conf.numberOfProcessors == 0: conf.numberOfProcessors = countProcessors()
   var res = 0
   if conf.numberOfProcessors <= 1:
-    for i in countup(0, high(cmds)):
+    for i in 0..high(cmds):
       tryExceptOSErrorMessage(conf, "invocation of external compiler program failed."):
         res = execWithEcho(conf, cmds[i])
       if res != 0:
@@ -709,34 +829,107 @@ proc execCmdsInParallel(conf: ConfigRef; cmds: seq[string]; prettyCb: proc (idx:
           cmds[i])
   else:
     tryExceptOSErrorMessage(conf, "invocation of external compiler program failed."):
-      if optListCmd in conf.globalOptions or conf.verbosity > 1:
-        res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath},
-                            conf.numberOfProcessors, afterRunEvent=runCb)
-      elif conf.verbosity == 1:
-        res = execProcesses(cmds, {poStdErrToStdOut, poUsePath},
+      res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
                             conf.numberOfProcessors, prettyCb, afterRunEvent=runCb)
-      else:
-        res = execProcesses(cmds, {poStdErrToStdOut, poUsePath},
-                            conf.numberOfProcessors, afterRunEvent=runCb)
   if res != 0:
     if conf.numberOfProcessors <= 1:
       rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
         cmds.join())
 
-proc callCCompiler*(conf: ConfigRef; projectfile: string) =
+proc linkViaResponseFile(conf: ConfigRef; cmd: string) =
+  # Extracting the linker.exe here is a bit hacky but the best solution
+  # given ``buildLib``'s design.
+  var i = 0
+  var last = 0
+  if cmd.len > 0 and cmd[0] == '"':
+    inc i
+    while i < cmd.len and cmd[i] != '"': inc i
+    last = i
+    inc i
+  else:
+    while i < cmd.len and cmd[i] != ' ': inc i
+    last = i
+  while i < cmd.len and cmd[i] == ' ': inc i
+  let linkerArgs = conf.projectName & "_" & "linkerArgs.txt"
+  let args = cmd.substr(i)
+  # GCC's response files don't support backslashes. Junk.
+  if conf.cCompiler == ccGcc or conf.cCompiler == ccCLang:
+    writeFile(linkerArgs, args.replace('\\', '/'))
+  else:
+    writeFile(linkerArgs, args)
+  try:
+    when defined(macosx):
+      execLinkCmd(conf, "xargs " & cmd.substr(0, last) & " < " & linkerArgs)
+    else:
+      execLinkCmd(conf, cmd.substr(0, last) & " @" & linkerArgs)
+  finally:
+    removeFile(linkerArgs)
+
+proc linkViaShellScript(conf: ConfigRef; cmd: string) =
+  let linkerScript = conf.projectName & "_" & "linkerScript.sh"
+  writeFile(linkerScript, cmd)
+  let shell = getEnv("SHELL")
+  try:
+    execLinkCmd(conf, shell & " " & linkerScript)
+  finally:
+    removeFile(linkerScript)
+
+proc getObjFilePath(conf: ConfigRef, f: Cfile): string =
+  if noAbsolutePaths(conf): f.obj.extractFilename
+  else: f.obj.string
+
+proc hcrLinkTargetName(conf: ConfigRef, objFile: string, isMain = false): AbsoluteFile =
+  let basename = splitFile(objFile).name
+  let targetName = if isMain: basename & ".exe"
+                   else: platform.OS[conf.target.targetOS].dllFrmt % basename
+  result = conf.getNimcacheDir / RelativeFile(targetName)
+
+proc displayProgressCC(conf: ConfigRef, path, compileCmd: string): string =
+  result = ""
+  if conf.hasHint(hintCC):
+    if optListCmd in conf.globalOptions or conf.verbosity > 1:
+      result = MsgKindToStr[hintCC] % (demangleModuleName(path.splitFile.name) & ": " & compileCmd)
+    else:
+      result = MsgKindToStr[hintCC] % demangleModuleName(path.splitFile.name)
+
+proc preventLinkCmdMaxCmdLen(conf: ConfigRef, linkCmd: string) =
+  # Prevent linkcmd from exceeding the maximum command line length.
+  # Windows's command line limit is about 8K (8191 characters) so C compilers on
+  # Windows support a feature where the command line can be passed via ``@linkcmd``
+  # to them.
+  const MaxCmdLen = when defined(windows): 8_000 elif defined(macosx): 260_000 else: 32_000
+  if linkCmd.len > MaxCmdLen:
+    when defined(macosx):
+      linkViaShellScript(conf, linkCmd)
+    else:
+      linkViaResponseFile(conf, linkCmd)
+  else:
+    execLinkCmd(conf, linkCmd)
+
+proc callCCompiler*(conf: ConfigRef) =
   var
-    linkCmd: string
+    linkCmd: string = ""
+    extraCmds: seq[string]
   if conf.globalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
     return # speed up that call if only compiling and no script shall be
            # generated
   #var c = cCompiler
-  var script: Rope = nil
-  var cmds: TStringSeq = @[]
-  var prettyCmds: TStringSeq = @[]
-  let prettyCb = proc (idx: int) =
-    when declared(echo):
-      echo prettyCmds[idx]
-  compileCFile(conf, conf.toCompile, script, cmds, prettyCmds)
+  var script: Rope = ""
+  var cmds: TStringSeq = default(TStringSeq)
+  var prettyCmds: TStringSeq = default(TStringSeq)
+  let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
+
+  for idx, it in conf.toCompile:
+    # call the C compiler for the .c file:
+    if CfileFlag.Cached in it.flags: continue
+    let compileCmd = getCompileCFileCmd(conf, it, idx == conf.toCompile.len - 1, produceOutput=true)
+    if optCompileOnly notin conf.globalOptions:
+      cmds.add(compileCmd)
+      prettyCmds.add displayProgressCC(conf, $it.cname, compileCmd)
+    if optGenScript in conf.globalOptions:
+      script.add(compileCmd)
+      script.add("\n")
+
   if optCompileOnly notin conf.globalOptions:
     execCmdsInParallel(conf, cmds, prettyCb)
   if optNoLinking notin conf.globalOptions:
@@ -744,140 +937,186 @@ proc callCCompiler*(conf: ConfigRef; projectfile: string) =
     var objfiles = ""
     for it in conf.externalToLink:
       let objFile = if noAbsolutePaths(conf): it.extractFilename else: it
-      add(objfiles, ' ')
-      add(objfiles, quoteShell(
+      objfiles.add(' ')
+      objfiles.add(quoteShell(
           addFileExt(objFile, CC[conf.cCompiler].objExt)))
-    for x in conf.toCompile:
-      let objFile = if noAbsolutePaths(conf): x.obj.extractFilename else: x.obj
-      add(objfiles, ' ')
-      add(objfiles, quoteShell(objFile))
 
-    linkCmd = getLinkCmd(conf, projectfile, objfiles)
-    if optCompileOnly notin conf.globalOptions:
-      execLinkCmd(conf, linkCmd)
+    if conf.hcrOn: # lets assume that optCompileOnly isn't on
+      cmds = @[]
+      let mainFileIdx = conf.toCompile.len - 1
+      for idx, x in conf.toCompile:
+        # don't relink each of the many binaries (one for each source file) if the nim code is
+        # cached because that would take too much time for small changes - the only downside to
+        # this is that if an external-to-link file changes the final target wouldn't be relinked
+        if CfileFlag.Cached in x.flags: continue
+        # we pass each object file as if it is the project file - a .dll will be created for each such
+        # object file in the nimcache directory, and only in the case of the main project file will
+        # there be probably an executable (if the project is such) which will be copied out of the nimcache
+        let objFile = conf.getObjFilePath(x)
+        let buildDll = idx != mainFileIdx
+        let linkTarget = conf.hcrLinkTargetName(objFile, not buildDll)
+        cmds.add(getLinkCmd(conf, linkTarget, objfiles & " " & quoteShell(objFile), buildDll, removeStaticFile = true))
+        # try to remove all .pdb files for the current binary so they don't accumulate endlessly in the nimcache
+        # for more info check the comment inside of getLinkCmd() where the /PDB:<filename> MSVC flag is used
+        if isVSCompatible(conf):
+          for pdb in walkFiles(objFile & ".*.pdb"):
+            discard tryRemoveFile(pdb)
+      # execute link commands in parallel - output will be a bit different
+      # if it fails than that from execLinkCmd() but that doesn't matter
+      prettyCmds = map(prettyCmds, proc (curr: string): string = return curr.replace("CC", "Link"))
+      execCmdsInParallel(conf, cmds, prettyCb)
+      # only if not cached - copy the resulting main file from the nimcache folder to its originally intended destination
+      if CfileFlag.Cached notin conf.toCompile[mainFileIdx].flags:
+        let mainObjFile = getObjFilePath(conf, conf.toCompile[mainFileIdx])
+        let src = conf.hcrLinkTargetName(mainObjFile, true)
+        let dst = conf.prepareToWriteOutput
+        copyFileWithPermissions(src.string, dst.string)
+    else:
+      for x in conf.toCompile:
+        let objFile = if noAbsolutePaths(conf): x.obj.extractFilename else: x.obj.string
+        objfiles.add(' ')
+        objfiles.add(quoteShell(objFile))
+      let mainOutput = if optGenScript notin conf.globalOptions: conf.prepareToWriteOutput
+                       else: AbsoluteFile(conf.outFile)
+
+      linkCmd = getLinkCmd(conf, mainOutput, objfiles, removeStaticFile = true)
+      extraCmds = getExtraCmds(conf, mainOutput)
+      if optCompileOnly notin conf.globalOptions:
+        preventLinkCmdMaxCmdLen(conf, linkCmd)
+        for cmd in extraCmds:
+          execExternalProgram(conf, cmd, hintExecuting)
   else:
     linkCmd = ""
   if optGenScript in conf.globalOptions:
-    add(script, linkCmd)
-    add(script, "\n")
-    generateScript(conf, projectfile, script)
-
-#from json import escapeJson
-import json
-
-proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
-  template lit(x: untyped) = f.write x
-  template str(x: untyped) =
-    when compiles(escapeJson(x, buf)):
-      buf.setLen 0
-      escapeJson(x, buf)
-      f.write buf
-    else:
-      f.write escapeJson(x)
-
-  proc cfiles(conf: ConfigRef; f: File; buf: var string; clist: CfileList, isExternal: bool) =
-    var pastStart = false
-    for it in clist:
-      if CfileFlag.Cached in it.flags: continue
-      let compileCmd = getCompileCFileCmd(conf, it)
-      if pastStart: lit "],\L"
-      lit "["
-      str it.cname
-      lit ", "
-      str compileCmd
-      pastStart = true
-    lit "]\L"
-
-  proc linkfiles(conf: ConfigRef; f: File; buf, objfiles: var string; clist: CfileList;
-                 llist: seq[string]) =
-    var pastStart = false
-    for it in llist:
-      let objfile = if noAbsolutePaths(conf): it.extractFilename
-                    else: it
-      let objstr = addFileExt(objfile, CC[conf.cCompiler].objExt)
-      add(objfiles, ' ')
-      add(objfiles, objstr)
-      if pastStart: lit ",\L"
-      str objstr
-      pastStart = true
-
-    for it in clist:
-      let objstr = quoteShell(it.obj)
-      add(objfiles, ' ')
-      add(objfiles, objstr)
-      if pastStart: lit ",\L"
-      str objstr
-      pastStart = true
-    lit "\L"
-
-  var buf = newStringOfCap(50)
-
-  let file = projectfile.splitFile.name
-  let jsonFile = toGeneratedFile(conf, file, "json")
-
-  var f: File
-  if open(f, jsonFile, fmWrite):
-    lit "{\"compile\":[\L"
-    cfiles(conf, f, buf, conf.toCompile, false)
-    lit "],\L\"link\":[\L"
-    var objfiles = ""
-    # XXX add every file here that is to link
-    linkfiles(conf, f, buf, objfiles, conf.toCompile, conf.externalToLink)
-
-    lit "],\L\"linkcmd\": "
-    str getLinkCmd(conf, projectfile, objfiles)
-    lit "\L}\L"
-    close(f)
-
-proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
-  let file = projectfile.splitFile.name
-  let jsonFile = toGeneratedFile(conf, file, "json")
-  try:
-    let data = json.parseFile(jsonFile)
-    let toCompile = data["compile"]
-    doAssert toCompile.kind == JArray
-    var cmds: TStringSeq = @[]
-    var prettyCmds: TStringSeq = @[]
-    for c in toCompile:
-      doAssert c.kind == JArray
-      doAssert c.len >= 2
-
-      add(cmds, c[1].getStr)
-      let (_, name, _) = splitFile(c[0].getStr)
-      add(prettyCmds, "CC: " & name)
-
-    let prettyCb = proc (idx: int) =
-      when declared(echo):
-        echo prettyCmds[idx]
-    execCmdsInParallel(conf, cmds, prettyCb)
-
-    let linkCmd = data["linkcmd"]
-    doAssert linkCmd.kind == JString
-    execLinkCmd(conf, linkCmd.getStr)
-  except:
-    when declared(echo):
-      echo getCurrentException().getStackTrace()
-    quit "error evaluating JSON file: " & jsonFile
-
-proc genMappingFiles(conf: ConfigRef; list: CFileList): Rope =
+    script.add(linkCmd)
+    script.add("\n")
+    generateScript(conf, script)
+
+template hashNimExe(): string = $secureHashFile(os.getAppFilename())
+
+proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile =
+  # `outFile` is better than `projectName`, as it allows having different json
+  # files for a given source file compiled with different options; it also
+  # works out of the box with `hashMainCompilationParams`.
+  result = getNimcacheDir(conf) / conf.outFile.changeFileExt("json")
+
+const cacheVersion = "D20240927T193831" # update when `BuildCache` spec changes
+type BuildCache = object
+  cacheVersion: string
+  outputFile: string
+  outputLastModificationTime: string
+  compile: seq[(string, string)]
+  link: seq[string]
+  linkcmd: string
+  extraCmds: seq[string]
+  configFiles: seq[string] # the hash shouldn't be needed
+  stdinInput: bool
+  projectIsCmd: bool
+  cmdInput: string
+  currentDir: string
+  cmdline: string
+  depfiles: seq[(string, string)]
+  nimexe: string
+
+proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) =
+  var linkFiles = collect(for it in conf.externalToLink:
+    var it = it
+    if conf.noAbsolutePaths: it = it.extractFilename
+    it.addFileExt(CC[conf.cCompiler].objExt))
+  for it in conf.toCompile: linkFiles.add it.obj.string
+  var bcache = BuildCache(
+    cacheVersion: cacheVersion,
+    outputFile: conf.absOutFile.string,
+    compile: collect(for i, it in conf.toCompile:
+      if CfileFlag.Cached notin it.flags: (it.cname.string, getCompileCFileCmd(conf, it))),
+    link: linkFiles,
+    linkcmd: getLinkCmd(conf, conf.absOutFile, linkFiles.quoteShellCommand),
+    extraCmds: getExtraCmds(conf, conf.absOutFile),
+    stdinInput: conf.projectIsStdin,
+    projectIsCmd: conf.projectIsCmd,
+    cmdInput: conf.cmdInput,
+    configFiles: conf.configFiles.mapIt(it.string),
+    currentDir: getCurrentDir())
+  if optRun in conf.globalOptions or isDefined(conf, "nimBetterRun"):
+    bcache.cmdline = conf.commandLine
+    for it in conf.m.fileInfos:
+      let path = it.fullPath.string
+      if isAbsolute(path): # TODO: else?
+        if path in deps:
+          bcache.depfiles.add (path, deps[path])
+        else: # backup for configs etc.
+          bcache.depfiles.add (path, $secureHashFile(path))
+
+    bcache.nimexe = hashNimExe()
+    if fileExists(bcache.outputFile):
+      bcache.outputLastModificationTime = $getLastModificationTime(bcache.outputFile)
+  conf.jsonBuildFile = conf.jsonBuildInstructionsFile
+  conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty)
+
+proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile): bool =
+  result = false
+  if not fileExists(jsonFile) or not fileExists(conf.absOutFile): return true
+  var bcache: BuildCache = default(BuildCache)
+  try: bcache.fromJson(jsonFile.string.parseFile)
+  except IOError, OSError, ValueError:
+    stderr.write "Warning: JSON processing failed for: $#\n" % jsonFile.string
+    return true
+  if bcache.currentDir != getCurrentDir() or # fixes bug #16271
+     bcache.configFiles != conf.configFiles.mapIt(it.string) or
+     bcache.cacheVersion != cacheVersion or bcache.outputFile != conf.absOutFile.string or
+     bcache.cmdline != conf.commandLine or bcache.nimexe != hashNimExe() or
+     bcache.projectIsCmd != conf.projectIsCmd or conf.cmdInput != bcache.cmdInput: return true
+  if bcache.stdinInput or conf.projectIsStdin: return true
+    # xxx optimize by returning false if stdin input was the same
+  for (file, hash) in bcache.depfiles:
+    if $secureHashFile(file) != hash: return true
+  if bcache.outputLastModificationTime != $getLastModificationTime(bcache.outputFile):
+    return true
+
+proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
+  var bcache: BuildCache = default(BuildCache)
+  try: bcache.fromJson(jsonFile.string.parseFile)
+  except ValueError, KeyError, JsonKindError:
+    let e = getCurrentException()
+    conf.quitOrRaise "\ncaught exception:\n$#\nstacktrace:\n$#error evaluating JSON file: $#" %
+      [e.msg, e.getStackTrace(), jsonFile.string]
+  let output = bcache.outputFile
+  createDir output.parentDir
+  let outputCurrent = $conf.absOutFile
+  if output != outputCurrent or bcache.cacheVersion != cacheVersion:
+    globalError(conf, gCmdLineInfo,
+      "jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " %
+      [outputCurrent, output, jsonFile.string])
+  var cmds: TStringSeq = default(TStringSeq)
+  var prettyCmds: TStringSeq = default(TStringSeq)
+  let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
+  for (name, cmd) in bcache.compile:
+    cmds.add cmd
+    prettyCmds.add displayProgressCC(conf, name, cmd)
+  execCmdsInParallel(conf, cmds, prettyCb)
+  preventLinkCmdMaxCmdLen(conf, bcache.linkcmd)
+  for cmd in bcache.extraCmds: execExternalProgram(conf, cmd, hintExecuting)
+
+proc genMappingFiles(conf: ConfigRef; list: CfileList): Rope =
+  result = ""
   for it in list:
-    addf(result, "--file:r\"$1\"$N", [rope(it.cname)])
+    result.addf("--file:r\"$1\"$N", [rope(it.cname.string)])
 
 proc writeMapping*(conf: ConfigRef; symbolMapping: Rope) =
   if optGenMapping notin conf.globalOptions: return
   var code = rope("[C_Files]\n")
-  add(code, genMappingFiles(conf, conf.toCompile))
-  add(code, "\n[C_Compiler]\nFlags=")
-  add(code, strutils.escape(getCompileOptions(conf)))
+  code.add(genMappingFiles(conf, conf.toCompile))
+  code.add("\n[C_Compiler]\nFlags=")
+  code.add(strutils.escape(getCompileOptions(conf)))
 
-  add(code, "\n[Linker]\nFlags=")
-  add(code, strutils.escape(getLinkOptions(conf) & " " &
+  code.add("\n[Linker]\nFlags=")
+  code.add(strutils.escape(getLinkOptions(conf) & " " &
                             getConfigVar(conf, conf.cCompiler, ".options.linker")))
 
-  add(code, "\n[Environment]\nlibpath=")
-  add(code, strutils.escape(conf.libpath))
+  code.add("\n[Environment]\nlibpath=")
+  code.add(strutils.escape(conf.libpath.string))
 
-  addf(code, "\n[Symbols]$n$1", [symbolMapping])
-  let filename = joinPath(conf.projectPath, "mapping.txt")
+  code.addf("\n[Symbols]$n$1", [symbolMapping])
+  let filename = conf.projectPath / RelativeFile"mapping.txt"
   if not writeRope(code, filename):
-    rawMessage(conf, errGenerated, "could not write to file: " & filename)
+    rawMessage(conf, errGenerated, "could not write to file: " & filename.string)
diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim
index 09455ced7..921a94b31 100644
--- a/compiler/filter_tmpl.nim
+++ b/compiler/filter_tmpl.nim
@@ -10,8 +10,10 @@
 # This module implements Nim's standard template filter.
 
 import
-  llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
-  renderer, filters, lineinfos
+  llstream, ast, msgs, options,
+  filters, lineinfos, pathutils
+
+import std/strutils
 
 type
   TParseState = enum
@@ -22,7 +24,7 @@ type
     info: TLineInfo
     indent, emitPar: int
     x: string                # the current input line
-    outp: PLLStream          # the ouput will be parsed by pnimsyn
+    outp: PLLStream          # the output will be parsed by parser
     subsChar, nimDirective: char
     emit, conc, toStr: string
     curly, bracket, par: int
@@ -75,7 +77,7 @@ proc parseLine(p: var TTmplParser) =
     let d = j
     var keyw = ""
     while j < len and p.x[j] in PatternChars:
-      add(keyw, p.x[j])
+      keyw.add(p.x[j])
       inc(j)
 
     scanPar(p, j)
@@ -199,23 +201,22 @@ proc parseLine(p: var TTmplParser) =
           inc(j)
     llStreamWrite(p.outp, "\\n\"")
 
-proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode; conf: ConfigRef): PLLStream =
-  var p: TTmplParser
-  p.config = conf
-  p.info = newLineInfo(conf, filename, 0, 0)
-  p.outp = llStreamOpen("")
-  p.inp = stdin
-  p.subsChar = charArg(conf, call, "subschar", 1, '$')
-  p.nimDirective = charArg(conf, call, "metachar", 2, '#')
-  p.emit = strArg(conf, call, "emit", 3, "result.add")
-  p.conc = strArg(conf, call, "conc", 4, " & ")
-  p.toStr = strArg(conf, call, "tostring", 5, "$")
-  p.x = newStringOfCap(120)
+proc filterTmpl*(conf: ConfigRef, stdin: PLLStream, filename: AbsoluteFile,
+                 call: PNode): PLLStream =
+  var p = TTmplParser(config: conf, info: newLineInfo(conf, filename, 0, 0),
+                outp: llStreamOpen(""), inp: stdin,
+                subsChar: charArg(conf, call, "subschar", 1, '$'),
+                nimDirective: charArg(conf, call, "metachar", 2, '#'),
+                emit: strArg(conf, call, "emit", 3, "result.add"),
+                conc: strArg(conf, call, "conc", 4, " & "),
+                toStr: strArg(conf, call, "tostring", 5, "$"),
+                x: newStringOfCap(120)
+                )
   # do not process the first line which contains the directive:
   if llStreamReadLine(p.inp, p.x):
-    p.info.line = p.info.line + 1'u16
+    inc p.info.line
   while llStreamReadLine(p.inp, p.x):
-    p.info.line = p.info.line + 1'u16
+    inc p.info.line
     parseLine(p)
   newLine(p)
   result = p.outp
diff --git a/compiler/filters.nim b/compiler/filters.nim
index 3ebbad678..3cd56e3be 100644
--- a/compiler/filters.nim
+++ b/compiler/filters.nim
@@ -10,8 +10,10 @@
 # This module implements Nim's simple filters and helpers for filters.
 
 import
-  llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
-  renderer
+  llstream, idents, ast, msgs, options,
+  renderer, pathutils
+
+import std/strutils
 
 proc invalidPragma(conf: ConfigRef; n: PNode) =
   localError(conf, n.info,
@@ -20,34 +22,41 @@ proc invalidPragma(conf: ConfigRef; n: PNode) =
 proc getArg(conf: ConfigRef; n: PNode, name: string, pos: int): PNode =
   result = nil
   if n.kind in {nkEmpty..nkNilLit}: return
-  for i in countup(1, sonsLen(n) - 1):
-    if n.sons[i].kind == nkExprEqExpr:
-      if n.sons[i].sons[0].kind != nkIdent: invalidPragma(conf, n)
-      if cmpIgnoreStyle(n.sons[i].sons[0].ident.s, name) == 0:
-        return n.sons[i].sons[1]
+  for i in 1..<n.len:
+    if n[i].kind == nkExprEqExpr:
+      if n[i][0].kind != nkIdent: invalidPragma(conf, n)
+      if cmpIgnoreStyle(n[i][0].ident.s, name) == 0:
+        return n[i][1]
     elif i == pos:
-      return n.sons[i]
+      return n[i]
 
 proc charArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: char): char =
+
   var x = getArg(conf, n, name, pos)
   if x == nil: result = default
   elif x.kind == nkCharLit: result = chr(int(x.intVal))
-  else: invalidPragma(conf, n)
+  else:
+    result = default(char)
+    invalidPragma(conf, n)
 
 proc strArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: string): string =
   var x = getArg(conf, n, name, pos)
   if x == nil: result = default
   elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal
-  else: invalidPragma(conf, n)
+  else:
+    result = ""
+    invalidPragma(conf, n)
 
 proc boolArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: bool): bool =
   var x = getArg(conf, n, name, pos)
   if x == nil: result = default
   elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "true") == 0: result = true
   elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "false") == 0: result = false
-  else: invalidPragma(conf, n)
+  else:
+    result = false
+    invalidPragma(conf, n)
 
-proc filterStrip*(conf: ConfigRef; stdin: PLLStream, filename: string, call: PNode): PLLStream =
+proc filterStrip*(conf: ConfigRef; stdin: PLLStream, filename: AbsoluteFile, call: PNode): PLLStream =
   var pattern = strArg(conf, call, "startswith", 1, "")
   var leading = boolArg(conf, call, "leading", 2, true)
   var trailing = boolArg(conf, call, "trailing", 3, true)
@@ -55,15 +64,15 @@ proc filterStrip*(conf: ConfigRef; stdin: PLLStream, filename: string, call: PNo
   var line = newStringOfCap(80)
   while llStreamReadLine(stdin, line):
     var stripped = strip(line, leading, trailing)
-    if len(pattern) == 0 or startsWith(stripped, pattern):
+    if pattern.len == 0 or startsWith(stripped, pattern):
       llStreamWriteln(result, stripped)
     else:
       llStreamWriteln(result, line)
   llStreamClose(stdin)
 
-proc filterReplace*(conf: ConfigRef; stdin: PLLStream, filename: string, call: PNode): PLLStream =
+proc filterReplace*(conf: ConfigRef; stdin: PLLStream, filename: AbsoluteFile, call: PNode): PLLStream =
   var sub = strArg(conf, call, "sub", 1, "")
-  if len(sub) == 0: invalidPragma(conf, call)
+  if sub.len == 0: invalidPragma(conf, call)
   var by = strArg(conf, call, "by", 2, "")
   result = llStreamOpen("")
   var line = newStringOfCap(80)
diff --git a/compiler/forloops.nim b/compiler/forloops.nim
deleted file mode 100644
index 2cd1db7f7..000000000
--- a/compiler/forloops.nim
+++ /dev/null
@@ -1,89 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module implements for loop detection for better C code generation.
-
-import ast, astalgo
-
-const
-  someCmp = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
-    mEqUntracedRef, mLeI, mLeF64, mLeU, mLeU64, mLeEnum,
-    mLeCh, mLeB, mLePtr, mLtI, mLtF64, mLtU, mLtU64, mLtEnum,
-    mLtCh, mLtB, mLtPtr}
-
-proc isCounter(s: PSym): bool {.inline.} =
-  s.kind in {skResult, skVar, skLet, skTemp} and
-  {sfGlobal, sfAddrTaken} * s.flags == {}
-
-proc isCall(n: PNode): bool {.inline.} =
-  n.kind in nkCallKinds and n[0].kind == nkSym
-
-proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags
-
-proc getCounter(lastStmt: PNode): PSym =
-  if lastStmt.isCall:
-    let op = lastStmt.sym
-    if op.magic in {mDec, mInc} or
-        ((op.name.s == "+=" or op.name.s == "-=") and op.fromSystem):
-      if op[1].kind == nkSym and isCounter(op[1].sym):
-        result = op[1].sym
-
-proc counterInTree(n, loop: PNode; counter: PSym): bool =
-  # prune the search tree: within the loop the counter may be used:
-  if n == loop: return
-  case n.kind
-  of nkSym:
-    if n.sym == counter: return true
-  of nkVarSection, nkLetSection:
-    # definitions are fine!
-    for it in n:
-      if counterInTree(it.lastSon): return true
-  else:
-    for i in 0 ..< safeLen(n):
-      if counterInTree(n[i], loop, counter): return true
-
-proc copyExcept(n: PNode, x, dest: PNode) =
-  if x == n: return
-  if n.kind in {nkStmtList, nkStmtListExpr}:
-    for i in 0 ..< n.len: copyExcept(n[i], x, dest)
-  else:
-    dest.add n
-
-type
-  ForLoop* = object
-    counter*: PSym
-    init*, cond*, increment*, body*: PNode
-
-proc extractForLoop*(loop, fullTree: PNode): ForLoop =
-  ## returns 'counter == nil' if the while loop 'n' is not a for loop:
-  assert loop.kind == nkWhileStmt
-  let cond == loop[0]
-
-  if not cond.isCall: return
-  if cond[0].sym.magic notin someCmp: return
-
-  var lastStmt = loop[1]
-  while lastStmt.kind in {nkStmtList, nkStmtListExpr}:
-    lastStmt = lastStmt.lastSon
-
-  let counter = getCounter(lastStmt)
-  if counter.isNil or counter.ast.isNil: return
-
-  template `=~`(a, b): expr = a.kind == nkSym and a.sym == b
-
-  if cond[1] =~ counter or cond[2] =~ counter:
-    # ok, now check 'counter' is not used *after* the loop
-    if counterInTree(fullTree, loop, counter): return
-    # ok, success, fill in the fields:
-    result.counter = counter
-    result.init = counter.ast
-    result.cond = cond
-    result.increment = lastStmt
-    result.body = newNodeI(nkStmtList, loop[1].info)
-    copyExcept(loop[1], lastStmt, result.body)
diff --git a/compiler/gorgeimpl.nim b/compiler/gorgeimpl.nim
index 44ad46136..da911c84c 100644
--- a/compiler/gorgeimpl.nim
+++ b/compiler/gorgeimpl.nim
@@ -9,8 +9,14 @@
 
 ## Module that implements ``gorge`` for the compiler.
 
-import msgs, std / sha1, os, osproc, streams, strutils, options,
-  lineinfos
+import msgs, options, lineinfos, pathutils
+
+import std/[os, osproc, streams]
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
+
+import ../dist/checksums/src/checksums/sha1
 
 proc readOutput(p: Process): (string, int) =
   result[0] = ""
@@ -24,35 +30,45 @@ proc readOutput(p: Process): (string, int) =
 
 proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (string, int) =
   let workingDir = parentDir(toFullPath(conf, info))
-  if cache.len > 0:# and optForceFullMake notin gGlobalOptions:
+  result = ("", 0)
+  if cache.len > 0:
     let h = secureHash(cmd & "\t" & input & "\t" & cache)
-    let filename = options.toGeneratedFile(conf, "gorge_" & $h, "txt")
-    var f: File
-    if open(f, filename):
+    let filename = toGeneratedFile(conf, AbsoluteFile("gorge_" & $h), "txt").string
+    var f: File = default(File)
+    if optForceFullMake notin conf.globalOptions and open(f, filename):
       result = (f.readAll, 0)
       f.close
       return
     var readSuccessful = false
     try:
       var p = startProcess(cmd, workingDir,
-                           options={poEvalCommand, poStderrToStdout})
+                           options={poEvalCommand, poStdErrToStdOut})
       if input.len != 0:
         p.inputStream.write(input)
         p.inputStream.close()
       result = p.readOutput
+      p.close()
       readSuccessful = true
       # only cache successful runs:
       if result[1] == 0:
         writeFile(filename, result[0])
     except IOError, OSError:
-      if not readSuccessful: result = ("", -1)
+      if not readSuccessful:
+        when defined(nimLegacyGorgeErrors):
+          result = ("", -1)
+        else:
+          result = ("Error running startProcess: " & getCurrentExceptionMsg(), -1)
   else:
     try:
       var p = startProcess(cmd, workingDir,
-                           options={poEvalCommand, poStderrToStdout})
+                           options={poEvalCommand, poStdErrToStdOut})
       if input.len != 0:
         p.inputStream.write(input)
         p.inputStream.close()
       result = p.readOutput
+      p.close()
     except IOError, OSError:
-      result = ("", -1)
+      when defined(nimLegacyGorgeErrors):
+        result = ("", -1)
+      else:
+        result = ("Error running startProcess: " & getCurrentExceptionMsg(), -1)
diff --git a/compiler/guards.nim b/compiler/guards.nim
index 99bb51fce..bbb239867 100644
--- a/compiler/guards.nim
+++ b/compiler/guards.nim
@@ -10,22 +10,24 @@
 ## This module implements the 'implies' relation for guards.
 
 import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents,
-  saturate, modulegraphs, options, lineinfos
+  saturate, modulegraphs, options, lineinfos, int128
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 const
   someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
-    mEqUntracedRef, mEqStr, mEqSet, mEqCString}
+    mEqStr, mEqSet, mEqCString}
 
   # set excluded here as the semantics are vastly different:
-  someLe = {mLeI, mLeF64, mLeU, mLeU64, mLeEnum,
+  someLe = {mLeI, mLeF64, mLeU, mLeEnum,
             mLeCh, mLeB, mLePtr, mLeStr}
-  someLt = {mLtI, mLtF64, mLtU, mLtU64, mLtEnum,
+  someLt = {mLtI, mLtF64, mLtU, mLtEnum,
             mLtCh, mLtB, mLtPtr, mLtStr}
 
-  someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq,
-             mXLenStr, mXLenSeq}
+  someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq}
 
-  someIn = {mInRange, mInSet}
+  someIn = {mInSet}
 
   someHigh = {mHigh}
   # we don't list unsigned here because wrap around semantics suck for
@@ -35,8 +37,8 @@ const
   someMul = {mMulI, mMulF64}
   someDiv = {mDivI, mDivF64}
   someMod = {mModI}
-  someMax = {mMaxI, mMaxF64}
-  someMin = {mMinI, mMinF64}
+  someMax = {mMaxI}
+  someMin = {mMinI}
   someBinaryOp = someAdd+someSub+someMul+someMax+someMin
 
 proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit}
@@ -47,8 +49,12 @@ proc isLet(n: PNode): bool =
     if n.sym.kind in {skLet, skTemp, skForVar}:
       result = true
     elif n.sym.kind == skParam and skipTypes(n.sym.typ,
-                                             abstractInst).kind != tyVar:
+                                             abstractInst).kind notin {tyVar}:
       result = true
+    else:
+      result = false
+  else:
+    result = false
 
 proc isVar(n: PNode): bool =
   n.kind == nkSym and n.sym.kind in {skResult, skVar} and
@@ -65,16 +71,19 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
   while true:
     case n.kind
     of nkDotExpr, nkCheckedFieldExpr, nkObjUpConv, nkObjDownConv:
-      n = n.sons[0]
-    of nkDerefExpr, nkHiddenDeref:
-      n = n.sons[0]
+      n = n[0]
+    of nkDerefExpr:
+      n = n[0]
       inc derefs
+    of nkHiddenDeref:
+      n = n[0]
+      if not isApprox: inc derefs
     of nkBracketExpr:
-      if isConstExpr(n.sons[1]) or isLet(n.sons[1]):
-        n = n.sons[0]
+      if isConstExpr(n[1]) or isLet(n[1]) or isConstExpr(n[1].skipConv):
+        n = n[0]
       else: return
     of nkHiddenStdConv, nkHiddenSubConv, nkConv:
-      n = n.sons[1]
+      n = n[1]
     else:
       break
   result = n.isLet and derefs <= ord(isApprox)
@@ -83,56 +92,36 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
 
 proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
 
-type
-  Operators* = object
-    opNot, opContains, opLe, opLt, opAnd, opOr, opIsNil, opEq: PSym
-    opAdd, opSub, opMul, opDiv, opLen: PSym
-
-proc initOperators*(g: ModuleGraph): Operators =
-  result.opLe = createMagic(g, "<=", mLeI)
-  result.opLt = createMagic(g, "<", mLtI)
-  result.opAnd = createMagic(g, "and", mAnd)
-  result.opOr = createMagic(g, "or", mOr)
-  result.opIsNil = createMagic(g, "isnil", mIsNil)
-  result.opEq = createMagic(g, "==", mEqI)
-  result.opAdd = createMagic(g, "+", mAddI)
-  result.opSub = createMagic(g, "-", mSubI)
-  result.opMul = createMagic(g, "*", mMulI)
-  result.opDiv = createMagic(g, "div", mDivI)
-  result.opLen = createMagic(g, "len", mLengthSeq)
-  result.opNot = createMagic(g, "not", mNot)
-  result.opContains = createMagic(g, "contains", mInSet)
-
 proc swapArgs(fact: PNode, newOp: PSym): PNode =
   result = newNodeI(nkCall, fact.info, 3)
-  result.sons[0] = newSymNode(newOp)
-  result.sons[1] = fact.sons[2]
-  result.sons[2] = fact.sons[1]
+  result[0] = newSymNode(newOp)
+  result[1] = fact[2]
+  result[2] = fact[1]
 
 proc neg(n: PNode; o: Operators): PNode =
   if n == nil: return nil
   case n.getMagic
   of mNot:
-    result = n.sons[1]
+    result = n[1]
   of someLt:
     # not (a < b)  ==  a >= b  ==  b <= a
     result = swapArgs(n, o.opLe)
   of someLe:
     result = swapArgs(n, o.opLt)
   of mInSet:
-    if n.sons[1].kind != nkCurly: return nil
-    let t = n.sons[2].typ.skipTypes(abstractInst)
+    if n[1].kind != nkCurly: return nil
+    let t = n[2].typ.skipTypes(abstractInst)
     result = newNodeI(nkCall, n.info, 3)
-    result.sons[0] = n.sons[0]
-    result.sons[2] = n.sons[2]
+    result[0] = n[0]
+    result[2] = n[2]
     if t.kind == tyEnum:
-      var s = newNodeIT(nkCurly, n.info, n.sons[1].typ)
+      var s = newNodeIT(nkCurly, n.info, n[1].typ)
       for e in t.n:
         let eAsNode = newIntNode(nkIntLit, e.sym.position)
-        if not inSet(n.sons[1], eAsNode): s.add eAsNode
-      result.sons[1] = s
+        if not inSet(n[1], eAsNode): s.add eAsNode
+      result[1] = s
     #elif t.kind notin {tyString, tySequence} and lengthOrd(t) < 1000:
-    #  result.sons[1] = complement(n.sons[1])
+    #  result[1] = complement(n[1])
     else:
       # not ({2, 3, 4}.contains(x))   x != 2 and x != 3 and x != 4
       # XXX todo
@@ -140,33 +129,35 @@ proc neg(n: PNode; o: Operators): PNode =
   of mOr:
     # not (a or b) --> not a and not b
     let
-      a = n.sons[1].neg(o)
-      b = n.sons[2].neg(o)
+      a = n[1].neg(o)
+      b = n[2].neg(o)
     if a != nil and b != nil:
       result = newNodeI(nkCall, n.info, 3)
-      result.sons[0] = newSymNode(o.opAnd)
-      result.sons[1] = a
-      result.sons[2] = b
+      result[0] = newSymNode(o.opAnd)
+      result[1] = a
+      result[2] = b
     elif a != nil:
       result = a
     elif b != nil:
       result = b
+    else:
+      result = nil
   else:
     # leave  not (a == 4)  as it is
     result = newNodeI(nkCall, n.info, 2)
-    result.sons[0] = newSymNode(o.opNot)
-    result.sons[1] = n
+    result[0] = newSymNode(o.opNot)
+    result[1] = n
 
-proc buildCall(op: PSym; a: PNode): PNode =
+proc buildCall*(op: PSym; a: PNode): PNode =
   result = newNodeI(nkCall, a.info, 2)
-  result.sons[0] = newSymNode(op)
-  result.sons[1] = a
+  result[0] = newSymNode(op)
+  result[1] = a
 
-proc buildCall(op: PSym; a, b: PNode): PNode =
+proc buildCall*(op: PSym; a, b: PNode): PNode =
   result = newNodeI(nkInfix, a.info, 3)
-  result.sons[0] = newSymNode(op)
-  result.sons[1] = a
-  result.sons[2] = b
+  result[0] = newSymNode(op)
+  result[1] = a
+  result[2] = b
 
 proc `|+|`(a, b: PNode): PNode =
   result = copyNode(a)
@@ -218,7 +209,7 @@ proc highBound*(conf: ConfigRef; x: PNode; o: Operators): PNode =
              nkIntLit.newIntNode(lastOrd(conf, typ))
            elif typ.kind == tySequence and x.kind == nkSym and
                x.sym.kind == skConst:
-             nkIntLit.newIntNode(x.sym.ast.len-1)
+             nkIntLit.newIntNode(x.sym.astdef.len-1)
            else:
              o.opAdd.buildCall(o.opLen.buildCall(x), minusOne())
   result.info = x.info
@@ -250,35 +241,35 @@ proc pred(n: PNode): PNode =
   else:
     result = n
 
+proc buildLe*(o: Operators; a, b: PNode): PNode =
+  result = o.opLe.buildCall(a, b)
+
 proc canon*(n: PNode; o: Operators): PNode =
-  # XXX for now only the new code in 'semparallel' uses this
   if n.safeLen >= 1:
     result = shallowCopy(n)
-    for i in 0 ..< n.len:
-      result.sons[i] = canon(n.sons[i], o)
+    for i in 0..<n.len:
+      result[i] = canon(n[i], o)
   elif n.kind == nkSym and n.sym.kind == skLet and
-      n.sym.ast.getMagic in (someEq + someAdd + someMul + someMin +
-      someMax + someHigh + {mUnaryLt} + someSub + someLen + someDiv):
-    result = n.sym.ast.copyTree
+      n.sym.astdef.getMagic in (someEq + someAdd + someMul + someMin +
+      someMax + someHigh + someSub + someLen + someDiv):
+    result = n.sym.astdef.copyTree
   else:
     result = n
   case result.getMagic
   of someEq, someAdd, someMul, someMin, someMax:
     # these are symmetric; put value as last:
-    if result.sons[1].isValue and not result.sons[2].isValue:
-      result = swapArgs(result, result.sons[0].sym)
+    if result[1].isValue and not result[2].isValue:
+      result = swapArgs(result, result[0].sym)
       # (4 + foo) + 2 --> (foo + 4) + 2
   of someHigh:
     # high == len+(-1)
     result = o.opAdd.buildCall(o.opLen.buildCall(result[1]), minusOne())
-  of mUnaryLt:
-    result = buildCall(o.opAdd, result[1], minusOne())
   of someSub:
     # x - 4  -->  x + (-4)
     result = negate(result[1], result[2], result, o)
   of someLen:
-    result.sons[0] = o.opLen.newSymNode
-  of someLt:
+    result[0] = o.opLen.newSymNode
+  of someLt - {mLtF64}:
     # x < y  same as x <= y-1:
     let y = n[2].canon(o)
     let p = pred(y)
@@ -318,16 +309,16 @@ proc canon*(n: PNode; o: Operators): PNode =
         if plus != nil and not isLetLocation(x, true):
           result = buildCall(result[0].sym, plus, y[1])
       else: discard
-    elif x.isValue and y.getMagic in someAdd and y[2].isValue:
+    elif x.isValue and y.getMagic in someAdd and y[2].kind == x.kind:
       # 0 <= a.len + 3
       # -3 <= a.len
-      result.sons[1] = x |-| y[2]
-      result.sons[2] = y[1]
-    elif x.isValue and y.getMagic in someSub and y[2].isValue:
+      result[1] = x |-| y[2]
+      result[2] = y[1]
+    elif x.isValue and y.getMagic in someSub and y[2].kind == x.kind:
       # 0 <= a.len - 3
       # 3 <= a.len
-      result.sons[1] = x |+| y[2]
-      result.sons[2] = y[1]
+      result[1] = x |+| y[2]
+      result[2] = y[1]
   else: discard
 
 proc buildAdd*(a: PNode; b: BiggestInt; o: Operators): PNode =
@@ -336,43 +327,57 @@ proc buildAdd*(a: PNode; b: BiggestInt; o: Operators): PNode =
 proc usefulFact(n: PNode; o: Operators): PNode =
   case n.getMagic
   of someEq:
-    if skipConv(n.sons[2]).kind == nkNilLit and (
-        isLetLocation(n.sons[1], false) or isVar(n.sons[1])):
-      result = o.opIsNil.buildCall(n.sons[1])
+    if skipConv(n[2]).kind == nkNilLit and (
+        isLetLocation(n[1], false) or isVar(n[1])):
+      result = o.opIsNil.buildCall(n[1])
     else:
-      if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true):
+      if isLetLocation(n[1], true) or isLetLocation(n[2], true):
         # XXX algebraic simplifications!  'i-1 < a.len' --> 'i < a.len+1'
         result = n
+      elif n[1].getMagic in someLen or n[2].getMagic in someLen:
+        result = n
+      else:
+        result = nil
   of someLe+someLt:
-    if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true):
+    if isLetLocation(n[1], true) or isLetLocation(n[2], true):
       # XXX algebraic simplifications!  'i-1 < a.len' --> 'i < a.len+1'
       result = n
     elif n[1].getMagic in someLen or n[2].getMagic in someLen:
       # XXX Rethink this whole idea of 'usefulFact' for semparallel
       result = n
+    else:
+      result = nil
   of mIsNil:
-    if isLetLocation(n.sons[1], false) or isVar(n.sons[1]):
+    if isLetLocation(n[1], false) or isVar(n[1]):
       result = n
+    else:
+      result = nil
   of someIn:
-    if isLetLocation(n.sons[1], true):
+    if isLetLocation(n[1], true):
       result = n
+    else:
+      result = nil
   of mAnd:
     let
-      a = usefulFact(n.sons[1], o)
-      b = usefulFact(n.sons[2], o)
+      a = usefulFact(n[1], o)
+      b = usefulFact(n[2], o)
     if a != nil and b != nil:
       result = newNodeI(nkCall, n.info, 3)
-      result.sons[0] = newSymNode(o.opAnd)
-      result.sons[1] = a
-      result.sons[2] = b
+      result[0] = newSymNode(o.opAnd)
+      result[1] = a
+      result[2] = b
     elif a != nil:
       result = a
     elif b != nil:
       result = b
+    else:
+      result = nil
   of mNot:
-    let a = usefulFact(n.sons[1], o)
+    let a = usefulFact(n[1], o)
     if a != nil:
       result = a.neg(o)
+    else:
+      result = nil
   of mOr:
     # 'or' sucks! (p.isNil or q.isNil) --> hard to do anything
     # with that knowledge...
@@ -381,36 +386,52 @@ proc usefulFact(n: PNode; o: Operators): PNode =
     #  (x == 3) or (y == 2)  ---> not ( not (x==3) and not (y == 2))
     #  not (x != 3 and y != 2)
     let
-      a = usefulFact(n.sons[1], o).neg(o)
-      b = usefulFact(n.sons[2], o).neg(o)
+      a = usefulFact(n[1], o).neg(o)
+      b = usefulFact(n[2], o).neg(o)
     if a != nil and b != nil:
       result = newNodeI(nkCall, n.info, 3)
-      result.sons[0] = newSymNode(o.opAnd)
-      result.sons[1] = a
-      result.sons[2] = b
+      result[0] = newSymNode(o.opAnd)
+      result[1] = a
+      result[2] = b
       result = result.neg(o)
+    else:
+      result = nil
   elif n.kind == nkSym and n.sym.kind == skLet:
     # consider:
     #   let a = 2 < x
     #   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)
+    else:
+      result = nil
   elif n.kind == nkStmtListExpr:
     result = usefulFact(n.lastSon, o)
+  else:
+    result = nil
 
 type
   TModel* = object
     s*: seq[PNode] # the "knowledge base"
-    o*: Operators
+    g*: ModuleGraph
+    beSmart*: bool
 
 proc addFact*(m: var TModel, nn: PNode) =
-  let n = usefulFact(nn, m.o)
-  if n != nil: m.s.add n
+  let n = usefulFact(nn, m.g.operators)
+  if n != nil:
+    if not m.beSmart:
+      m.s.add n
+    else:
+      let c = canon(n, m.g.operators)
+      if c.getMagic == mAnd:
+        addFact(m, c[1])
+        addFact(m, c[2])
+      else:
+        m.s.add c
 
 proc addFactNeg*(m: var TModel, n: PNode) =
-  let n = n.neg(m.o)
+  let n = n.neg(m.g.operators)
   if n != nil: addFact(m, n)
 
 proc sameOpr(a, b: PSym): bool =
@@ -436,24 +457,31 @@ proc sameTree*(a, b: PNode): bool =
       if not result and a.sym.magic != mNone:
         result = a.sym.magic == b.sym.magic or sameOpr(a.sym, b.sym)
     of nkIdent: result = a.ident.id == b.ident.id
-    of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
+    of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal
     of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
     of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
     of nkType: result = a.typ == b.typ
     of nkEmpty, nkNilLit: result = true
     else:
-      if sonsLen(a) == sonsLen(b):
-        for i in countup(0, sonsLen(a) - 1):
-          if not sameTree(a.sons[i], b.sons[i]): return
+      if a.len == b.len:
+        for i in 0..<a.len:
+          if not sameTree(a[i], b[i]): return
         result = true
 
 proc hasSubTree(n, x: PNode): bool =
   if n.sameTree(x): result = true
   else:
-    for i in 0..safeLen(n)-1:
-      if hasSubTree(n.sons[i], x): return true
+    case n.kind
+    of nkEmpty..nkNilLit:
+      result = n.sameTree(x)
+    of nkFormalParams:
+      result = false
+    else:
+      result = false
+      for i in 0..<n.len:
+        if hasSubTree(n[i], x): return true
 
-proc invalidateFacts*(m: var TModel, n: PNode) =
+proc invalidateFacts*(s: var seq[PNode], n: PNode) =
   # We are able to guard local vars (as opposed to 'let' variables)!
   # 'while p != nil: f(p); p = p.next'
   # This is actually quite easy to do:
@@ -471,32 +499,47 @@ proc invalidateFacts*(m: var TModel, n: PNode) =
   # The same mechanism could be used for more complex data stored on the heap;
   # procs that 'write: []' cannot invalidate 'n.kind' for instance. In fact, we
   # could CSE these expressions then and help C's optimizer.
-  for i in 0..high(m.s):
-    if m.s[i] != nil and m.s[i].hasSubTree(n): m.s[i] = nil
+  for i in 0..high(s):
+    if s[i] != nil and s[i].hasSubTree(n): s[i] = nil
+
+proc invalidateFacts*(m: var TModel, n: PNode) =
+  invalidateFacts(m.s, n)
 
 proc valuesUnequal(a, b: PNode): bool =
   if a.isValue and b.isValue:
     result = not sameValue(a, b)
+  else:
+    result = false
 
 proc impliesEq(fact, eq: PNode): TImplication =
-  let (loc, val) = if isLocation(eq.sons[1]): (1, 2) else: (2, 1)
+  let (loc, val) = if isLocation(eq[1]): (1, 2) else: (2, 1)
 
-  case fact.sons[0].sym.magic
+  case fact[0].sym.magic
   of someEq:
-    if sameTree(fact.sons[1], eq.sons[loc]):
+    if sameTree(fact[1], eq[loc]):
       # this is not correct; consider:  a == b;  a == 1 --> unknown!
-      if sameTree(fact.sons[2], eq.sons[val]): result = impYes
-      elif valuesUnequal(fact.sons[2], eq.sons[val]): result = impNo
-    elif sameTree(fact.sons[2], eq.sons[loc]):
-      if sameTree(fact.sons[1], eq.sons[val]): result = impYes
-      elif valuesUnequal(fact.sons[1], eq.sons[val]): result = impNo
+      if sameTree(fact[2], eq[val]): result = impYes
+      elif valuesUnequal(fact[2], eq[val]): result = impNo
+      else:
+        result = impUnknown
+    elif sameTree(fact[2], eq[loc]):
+      if sameTree(fact[1], eq[val]): result = impYes
+      elif valuesUnequal(fact[1], eq[val]): result = impNo
+      else:
+        result = impUnknown
+    else:
+      result = impUnknown
   of mInSet:
     # remember: mInSet is 'contains' so the set comes first!
-    if sameTree(fact.sons[2], eq.sons[loc]) and isValue(eq.sons[val]):
-      if inSet(fact.sons[1], eq.sons[val]): result = impYes
+    if sameTree(fact[2], eq[loc]) and isValue(eq[val]):
+      if inSet(fact[1], eq[val]): result = impYes
       else: result = impNo
-  of mNot, mOr, mAnd: assert(false, "impliesEq")
-  else: discard
+    else:
+      result = impUnknown
+  of mNot, mOr, mAnd:
+    result = impUnknown
+    assert(false, "impliesEq")
+  else: result = impUnknown
 
 proc leImpliesIn(x, c, aSet: PNode): TImplication =
   if c.kind in {nkCharLit..nkUInt64Lit}:
@@ -506,13 +549,19 @@ proc leImpliesIn(x, c, aSet: PNode): TImplication =
     var value = newIntNode(c.kind, firstOrd(nil, x.typ))
     # don't iterate too often:
     if c.intVal - value.intVal < 1000:
-      var i, pos, neg: int
+      var i, pos, neg: int = 0
       while value.intVal <= c.intVal:
         if inSet(aSet, value): inc pos
         else: inc neg
         inc i; inc value.intVal
       if pos == i: result = impYes
       elif neg == i: result = impNo
+      else:
+        result = impUnknown
+    else:
+      result = impUnknown
+  else:
+    result = impUnknown
 
 proc geImpliesIn(x, c, aSet: PNode): TImplication =
   if c.kind in {nkCharLit..nkUInt64Lit}:
@@ -522,44 +571,60 @@ proc geImpliesIn(x, c, aSet: PNode): TImplication =
     var value = newIntNode(c.kind, c.intVal)
     let max = lastOrd(nil, x.typ)
     # don't iterate too often:
-    if max - value.intVal < 1000:
-      var i, pos, neg: int
+    if max - getInt(value) < toInt128(1000):
+      var i, pos, neg: int = 0
       while value.intVal <= max:
         if inSet(aSet, value): inc pos
         else: inc neg
         inc i; inc value.intVal
       if pos == i: result = impYes
       elif neg == i: result = impNo
+      else: result = impUnknown
+    else:
+      result = impUnknown
+  else:
+    result = impUnknown
 
 proc compareSets(a, b: PNode): TImplication =
   if equalSets(nil, a, b): result = impYes
   elif intersectSets(nil, a, b).len == 0: result = impNo
+  else: result = impUnknown
 
 proc impliesIn(fact, loc, aSet: PNode): TImplication =
-  case fact.sons[0].sym.magic
+  case fact[0].sym.magic
   of someEq:
-    if sameTree(fact.sons[1], loc):
-      if inSet(aSet, fact.sons[2]): result = impYes
+    if sameTree(fact[1], loc):
+      if inSet(aSet, fact[2]): result = impYes
       else: result = impNo
-    elif sameTree(fact.sons[2], loc):
-      if inSet(aSet, fact.sons[1]): result = impYes
+    elif sameTree(fact[2], loc):
+      if inSet(aSet, fact[1]): result = impYes
       else: result = impNo
+    else:
+      result = impUnknown
   of mInSet:
-    if sameTree(fact.sons[2], loc):
-      result = compareSets(fact.sons[1], aSet)
+    if sameTree(fact[2], loc):
+      result = compareSets(fact[1], aSet)
+    else:
+      result = impUnknown
   of someLe:
-    if sameTree(fact.sons[1], loc):
-      result = leImpliesIn(fact.sons[1], fact.sons[2], aSet)
-    elif sameTree(fact.sons[2], loc):
-      result = geImpliesIn(fact.sons[2], fact.sons[1], aSet)
+    if sameTree(fact[1], loc):
+      result = leImpliesIn(fact[1], fact[2], aSet)
+    elif sameTree(fact[2], loc):
+      result = geImpliesIn(fact[2], fact[1], aSet)
+    else:
+      result = impUnknown
   of someLt:
-    if sameTree(fact.sons[1], loc):
-      result = leImpliesIn(fact.sons[1], fact.sons[2].pred, aSet)
-    elif sameTree(fact.sons[2], loc):
+    if sameTree(fact[1], loc):
+      result = leImpliesIn(fact[1], fact[2].pred, aSet)
+    elif sameTree(fact[2], loc):
       # 4 < x  -->  3 <= x
-      result = geImpliesIn(fact.sons[2], fact.sons[1].pred, aSet)
-  of mNot, mOr, mAnd: assert(false, "impliesIn")
-  else: discard
+      result = geImpliesIn(fact[2], fact[1].pred, aSet)
+    else:
+      result = impUnknown
+  of mNot, mOr, mAnd:
+    result = impUnknown
+    assert(false, "impliesIn")
+  else: result = impUnknown
 
 proc valueIsNil(n: PNode): TImplication =
   if n.kind == nkNilLit: impYes
@@ -567,93 +632,151 @@ proc valueIsNil(n: PNode): TImplication =
   else: impUnknown
 
 proc impliesIsNil(fact, eq: PNode): TImplication =
-  case fact.sons[0].sym.magic
+  case fact[0].sym.magic
   of mIsNil:
-    if sameTree(fact.sons[1], eq.sons[1]):
+    if sameTree(fact[1], eq[1]):
       result = impYes
+    else:
+      result = impUnknown
   of someEq:
-    if sameTree(fact.sons[1], eq.sons[1]):
-      result = valueIsNil(fact.sons[2].skipConv)
-    elif sameTree(fact.sons[2], eq.sons[1]):
-      result = valueIsNil(fact.sons[1].skipConv)
-  of mNot, mOr, mAnd: assert(false, "impliesIsNil")
-  else: discard
+    if sameTree(fact[1], eq[1]):
+      result = valueIsNil(fact[2].skipConv)
+    elif sameTree(fact[2], eq[1]):
+      result = valueIsNil(fact[1].skipConv)
+    else:
+      result = impUnknown
+  of mNot, mOr, mAnd:
+    result = impUnknown
+    assert(false, "impliesIsNil")
+  else: result = impUnknown
 
 proc impliesGe(fact, x, c: PNode): TImplication =
   assert isLocation(x)
-  case fact.sons[0].sym.magic
+  case fact[0].sym.magic
   of someEq:
-    if sameTree(fact.sons[1], x):
-      if isValue(fact.sons[2]) and isValue(c):
+    if sameTree(fact[1], x):
+      if isValue(fact[2]) and isValue(c):
         # fact:  x = 4;  question x >= 56? --> true iff 4 >= 56
-        if leValue(c, fact.sons[2]): result = impYes
+        if leValue(c, fact[2]): result = impYes
         else: result = impNo
-    elif sameTree(fact.sons[2], x):
-      if isValue(fact.sons[1]) and isValue(c):
-        if leValue(c, fact.sons[1]): result = impYes
+      else:
+        result = impUnknown
+    elif sameTree(fact[2], x):
+      if isValue(fact[1]) and isValue(c):
+        if leValue(c, fact[1]): result = impYes
         else: result = impNo
+      else:
+        result = impUnknown
+    else:
+      result = impUnknown
   of someLt:
-    if sameTree(fact.sons[1], x):
-      if isValue(fact.sons[2]) and isValue(c):
+    if sameTree(fact[1], x):
+      if isValue(fact[2]) and isValue(c):
         # fact:  x < 4;  question N <= x? --> false iff N <= 4
-        if leValue(fact.sons[2], c): result = impNo
+        if leValue(fact[2], c): result = impNo
+        else: result = impUnknown
         # fact:  x < 4;  question 2 <= x? --> we don't know
-    elif sameTree(fact.sons[2], x):
+      else:
+        result = impUnknown
+    elif sameTree(fact[2], x):
       # fact: 3 < x; question: N-1 < x ?  --> true iff N-1 <= 3
-      if isValue(fact.sons[1]) and isValue(c):
-        if leValue(c.pred, fact.sons[1]): result = impYes
+      if isValue(fact[1]) and isValue(c):
+        if leValue(c.pred, fact[1]): result = impYes
+        else: result = impUnknown
+      else:
+        result = impUnknown
+    else:
+      result = impUnknown
   of someLe:
-    if sameTree(fact.sons[1], x):
-      if isValue(fact.sons[2]) and isValue(c):
+    if sameTree(fact[1], x):
+      if isValue(fact[2]) and isValue(c):
         # fact:  x <= 4;  question x >= 56? --> false iff 4 <= 56
-        if leValue(fact.sons[2], c): result = impNo
+        if leValue(fact[2], c): result = impNo
         # fact:  x <= 4;  question x >= 2? --> we don't know
-    elif sameTree(fact.sons[2], x):
+        else:
+          result = impUnknown
+      else:
+        result = impUnknown
+    elif sameTree(fact[2], x):
       # fact: 3 <= x; question: x >= 2 ?  --> true iff 2 <= 3
-      if isValue(fact.sons[1]) and isValue(c):
-        if leValue(c, fact.sons[1]): result = impYes
-  of mNot, mOr, mAnd: assert(false, "impliesGe")
-  else: discard
+      if isValue(fact[1]) and isValue(c):
+        if leValue(c, fact[1]): result = impYes
+        else: result = impUnknown
+      else:
+        result = impUnknown
+    else:
+      result = impUnknown
+  of mNot, mOr, mAnd:
+    result = impUnknown
+    assert(false, "impliesGe")
+  else: result = impUnknown
 
 proc impliesLe(fact, x, c: PNode): TImplication =
   if not isLocation(x):
+    if c.isValue:
+      if leValue(x, x): return impYes
+      else: return impNo
     return impliesGe(fact, c, x)
-  case fact.sons[0].sym.magic
+  case fact[0].sym.magic
   of someEq:
-    if sameTree(fact.sons[1], x):
-      if isValue(fact.sons[2]) and isValue(c):
+    if sameTree(fact[1], x):
+      if isValue(fact[2]) and isValue(c):
         # fact:  x = 4;  question x <= 56? --> true iff 4 <= 56
-        if leValue(fact.sons[2], c): result = impYes
+        if leValue(fact[2], c): result = impYes
         else: result = impNo
-    elif sameTree(fact.sons[2], x):
-      if isValue(fact.sons[1]) and isValue(c):
-        if leValue(fact.sons[1], c): result = impYes
+      else:
+        result = impUnknown
+    elif sameTree(fact[2], x):
+      if isValue(fact[1]) and isValue(c):
+        if leValue(fact[1], c): result = impYes
         else: result = impNo
+      else:
+        result = impUnknown
+    else:
+      result = impUnknown
   of someLt:
-    if sameTree(fact.sons[1], x):
-      if isValue(fact.sons[2]) and isValue(c):
+    if sameTree(fact[1], x):
+      if isValue(fact[2]) and isValue(c):
         # fact:  x < 4;  question x <= N? --> true iff N-1 <= 4
-        if leValue(fact.sons[2], c.pred): result = impYes
+        if leValue(fact[2], c.pred): result = impYes
+        else:
+          result = impUnknown
         # fact:  x < 4;  question x <= 2? --> we don't know
-    elif sameTree(fact.sons[2], x):
+      else:
+        result = impUnknown
+    elif sameTree(fact[2], x):
       # fact: 3 < x; question: x <= 1 ?  --> false iff 1 <= 3
-      if isValue(fact.sons[1]) and isValue(c):
-        if leValue(c, fact.sons[1]): result = impNo
-
+      if isValue(fact[1]) and isValue(c):
+        if leValue(c, fact[1]): result = impNo
+        else: result = impUnknown
+      else:
+        result = impUnknown
+    else:
+      result = impUnknown
   of someLe:
-    if sameTree(fact.sons[1], x):
-      if isValue(fact.sons[2]) and isValue(c):
+    if sameTree(fact[1], x):
+      if isValue(fact[2]) and isValue(c):
         # fact:  x <= 4;  question x <= 56? --> true iff 4 <= 56
-        if leValue(fact.sons[2], c): result = impYes
+        if leValue(fact[2], c): result = impYes
+        else: result = impUnknown
         # fact:  x <= 4;  question x <= 2? --> we don't know
+      else:
+        result = impUnknown
 
-    elif sameTree(fact.sons[2], x):
+    elif sameTree(fact[2], x):
       # fact: 3 <= x; question: x <= 2 ?  --> false iff 2 < 3
-      if isValue(fact.sons[1]) and isValue(c):
-        if leValue(c, fact.sons[1].pred): result = impNo
+      if isValue(fact[1]) and isValue(c):
+        if leValue(c, fact[1].pred): result = impNo
+        else:result = impUnknown
+      else:
+        result = impUnknown
+    else:
+      result = impUnknown
 
-  of mNot, mOr, mAnd: assert(false, "impliesLe")
-  else: discard
+  of mNot, mOr, mAnd:
+    result = impUnknown
+    assert(false, "impliesLe")
+  else: result = impUnknown
 
 proc impliesLt(fact, x, c: PNode): TImplication =
   # x < 3  same as x <= 2:
@@ -665,6 +788,8 @@ proc impliesLt(fact, x, c: PNode): TImplication =
     let q = x.pred
     if q != x:
       result = impliesLe(fact, q, c)
+    else:
+      result = impUnknown
 
 proc `~`(x: TImplication): TImplication =
   case x
@@ -686,36 +811,37 @@ proc factImplies(fact, prop: PNode): TImplication =
 
     #  (not a) -> b  compute as  not (a -> b) ???
     #  == not a or not b == not (a and b)
-    let arg = fact.sons[1]
+    let arg = fact[1]
     case arg.getMagic
     of mIsNil, mEqRef:
       return ~factImplies(arg, prop)
     of mAnd:
       # not (a and b)  means  not a or not b:
       # a or b --> both need to imply 'prop'
-      let a = factImplies(arg.sons[1], prop)
-      let b = factImplies(arg.sons[2], prop)
+      let a = factImplies(arg[1], prop)
+      let b = factImplies(arg[2], prop)
       if a == b: return ~a
       return impUnknown
     else:
       return impUnknown
   of mAnd:
-    result = factImplies(fact.sons[1], prop)
+    result = factImplies(fact[1], prop)
     if result != impUnknown: return result
-    return factImplies(fact.sons[2], prop)
+    return factImplies(fact[2], prop)
   else: discard
 
-  case prop.sons[0].sym.magic
-  of mNot: result = ~fact.factImplies(prop.sons[1])
+  case prop[0].sym.magic
+  of mNot: result = ~fact.factImplies(prop[1])
   of mIsNil: result = impliesIsNil(fact, prop)
   of someEq: result = impliesEq(fact, prop)
-  of someLe: result = impliesLe(fact, prop.sons[1], prop.sons[2])
-  of someLt: result = impliesLt(fact, prop.sons[1], prop.sons[2])
-  of mInSet: result = impliesIn(fact, prop.sons[2], prop.sons[1])
+  of someLe: result = impliesLe(fact, prop[1], prop[2])
+  of someLt: result = impliesLt(fact, prop[1], prop[2])
+  of mInSet: result = impliesIn(fact, prop[2], prop[1])
   else: result = impUnknown
 
 proc doesImply*(facts: TModel, prop: PNode): TImplication =
   assert prop.kind in nkCallKinds
+  result = impUnknown
   for f in facts.s:
     # facts can be invalidated, in which case they are 'nil':
     if not f.isNil:
@@ -723,7 +849,7 @@ proc doesImply*(facts: TModel, prop: PNode): TImplication =
       if result != impUnknown: return
 
 proc impliesNotNil*(m: TModel, arg: PNode): TImplication =
-  result = doesImply(m, m.o.opIsNil.buildCall(arg).neg(m.o))
+  result = doesImply(m, m.g.operators.opIsNil.buildCall(arg).neg(m.g.operators))
 
 proc simpleSlice*(a, b: PNode): BiggestInt =
   # returns 'c' if a..b matches (i+c)..(i+c), -1 otherwise. (i)..(i) is matched
@@ -744,7 +870,7 @@ template isSub(x): untyped = x.getMagic in someSub
 template isVal(x): untyped = x.kind in {nkCharLit..nkUInt64Lit}
 template isIntVal(x, y): untyped = x.intVal == y
 
-import macros
+import std/macros
 
 macro `=~`(x: PNode, pat: untyped): bool =
   proc m(x, pat, conds: NimNode) =
@@ -777,12 +903,7 @@ macro `=~`(x: PNode, pat: untyped): bool =
 
   var conds = newTree(nnkBracket)
   m(x, pat, conds)
-  when compiles(nestList(ident"and", conds)):
-    result = nestList(ident"and", conds)
-  #elif declared(macros.toNimIdent):
-  #  result = nestList(toNimIdent"and", conds)
-  else:
-    result = nestList(!"and", conds)
+  result = nestList(ident"and", conds)
 
 proc isMinusOne(n: PNode): bool =
   n.kind in {nkCharLit..nkUInt64Lit} and n.intVal == -1
@@ -802,7 +923,7 @@ proc ple(m: TModel; a, b: PNode): TImplication =
     if lastOrd(nil, a.typ) <= b.intVal: return impYes
   # 3 <= x   iff  low(x) <= 3
   if a.isValue and b.typ != nil and b.typ.isOrdinalType:
-    if firstOrd(nil, b.typ) <= a.intVal: return impYes
+    if a.intVal <= firstOrd(nil, b.typ): return impYes
 
   # x <= x
   if sameTree(a, b): return impYes
@@ -813,7 +934,11 @@ proc ple(m: TModel; a, b: PNode): TImplication =
 
   #   x <= y+c  if 0 <= c and x <= y
   #   x <= y+(-c)  if c <= 0  and y >= x
-  if b.getMagic in someAdd and zero() <=? b[2] and a <=? b[1]: return impYes
+  if b.getMagic in someAdd:
+    if zero() <=? b[2] and a <=? b[1]: return impYes
+    # x <= y-c  if x+c <= y
+    if b[2] <=? zero() and (canon(m.g.operators.opSub.buildCall(a, b[2]), m.g.operators) <=? b[1]):
+      return impYes
 
   #   x+c <= y  if c <= 0 and x <= y
   if a.getMagic in someAdd and a[2] <=? zero() and a[1] <=? b: return impYes
@@ -826,20 +951,20 @@ proc ple(m: TModel; a, b: PNode): TImplication =
   if a.getMagic in someMul and a[2].isValue and a[1].getMagic in someDiv and
       a[1][2].isValue:
     # simplify   (x div 4) * 2 <= y   to  x div (c div d)  <= y
-    if ple(m, buildCall(m.o.opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes:
+    if ple(m, buildCall(m.g.operators.opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes:
       return impYes
 
   # x*3 + x == x*4. It follows that:
   # x*3 + y <= x*4  if  y <= x  and 3 <= 4
   if a =~ x*dc + y and b =~ x2*ec:
     if sameTree(x, x2):
-      let ec1 = m.o.opAdd.buildCall(ec, minusOne())
+      let ec1 = m.g.operators.opAdd.buildCall(ec, minusOne())
       if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? x:
         return impYes
   elif a =~ x*dc and b =~ x2*ec + y:
     #echo "BUG cam ehrer e ", a, " <=? ", b
     if sameTree(x, x2):
-      let ec1 = m.o.opAdd.buildCall(ec, minusOne())
+      let ec1 = m.g.operators.opAdd.buildCall(ec, minusOne())
       if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? zero():
         return impYes
 
@@ -881,8 +1006,8 @@ proc replaceSubTree(n, x, by: PNode): PNode =
     result = by
   elif hasSubTree(n, x):
     result = shallowCopy(n)
-    for i in 0 .. safeLen(n)-1:
-      result.sons[i] = replaceSubTree(n.sons[i], x, by)
+    for i in 0..n.safeLen-1:
+      result[i] = replaceSubTree(n[i], x, by)
   else:
     result = n
 
@@ -892,6 +1017,7 @@ proc applyReplacements(n: PNode; rep: TReplacements): PNode =
 
 proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
   # now check for inferrable facts: a <= b and b <= c  implies a <= c
+  result = impUnknown
   for i in 0..m.s.high:
     let fact = m.s[i]
     if fact != nil and fact.getMagic in someLe:
@@ -902,10 +1028,13 @@ proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
       # --> true  if  (len-100) <= (len-1)
       let x = fact[1]
       let y = fact[2]
-      if sameTree(x, a) and y.getMagic in someAdd and b.getMagic in someAdd and
-         sameTree(y[1], b[1]):
-        if ple(m, b[2], y[2]) == impYes:
-          return impYes
+      # x <= y.
+      # Question: x <= b? True iff y <= b.
+      if sameTree(x, a):
+        if ple(m, y, b) == impYes: return impYes
+        if y.getMagic in someAdd and b.getMagic in someAdd and sameTree(y[1], b[1]):
+          if ple(m, b[2], y[2]) == impYes:
+            return impYes
 
       # x <= y implies a <= b  if  a <= x and y <= b
       if ple(m, a, x) == impYes:
@@ -934,17 +1063,17 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
       let b = fact[2]
       if a.kind == nkSym: replacements.add((a,b))
       else: replacements.add((b,a))
-  var m: TModel
+  var m = TModel()
   var a = aa
   var b = bb
   if replacements.len > 0:
     m.s = @[]
-    m.o = model.o
+    m.g = model.g
     # make the other facts consistent:
     for fact in model.s:
       if fact != nil and fact.getMagic notin someEq:
         # XXX 'canon' should not be necessary here, but it is
-        m.s.add applyReplacements(fact, replacements).canon(m.o)
+        m.s.add applyReplacements(fact, replacements).canon(m.g.operators)
     a = applyReplacements(aa, replacements)
     b = applyReplacements(bb, replacements)
   else:
@@ -953,54 +1082,60 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
   result = pleViaModelRec(m, a, b)
 
 proc proveLe*(m: TModel; a, b: PNode): TImplication =
-  let x = canon(m.o.opLe.buildCall(a, b), m.o)
+  let x = canon(m.g.operators.opLe.buildCall(a, b), m.g.operators)
   #echo "ROOT ", renderTree(x[1]), " <=? ", renderTree(x[2])
   result = ple(m, x[1], x[2])
   if result == impUnknown:
     # try an alternative:  a <= b  iff  not (b < a)  iff  not (b+1 <= a):
-    let y = canon(m.o.opLe.buildCall(m.o.opAdd.buildCall(b, one()), a), m.o)
+    let y = canon(m.g.operators.opLe.buildCall(m.g.operators.opAdd.buildCall(b, one()), a), m.g.operators)
     result = ~ple(m, y[1], y[2])
 
 proc addFactLe*(m: var TModel; a, b: PNode) =
-  m.s.add canon(m.o.opLe.buildCall(a, b), m.o)
+  m.s.add canon(m.g.operators.opLe.buildCall(a, b), m.g.operators)
+
+proc addFactLt*(m: var TModel; a, b: PNode) =
+  let bb = m.g.operators.opAdd.buildCall(b, minusOne())
+  addFactLe(m, a, bb)
 
 proc settype(n: PNode): PType =
-  result = newType(tySet, n.typ.owner)
-  addSonSkipIntLit(result, n.typ)
+  var idgen = idGeneratorForPackage(-1'i32)
+  result = newType(tySet, idgen, n.typ.owner)
+  addSonSkipIntLit(result, n.typ, idgen)
 
 proc buildOf(it, loc: PNode; o: Operators): PNode =
   var s = newNodeI(nkCurly, it.info, it.len-1)
   s.typ = settype(loc)
-  for i in 0..it.len-2: s.sons[i] = it.sons[i]
+  for i in 0..<it.len-1: s[i] = it[i]
   result = newNodeI(nkCall, it.info, 3)
-  result.sons[0] = newSymNode(o.opContains)
-  result.sons[1] = s
-  result.sons[2] = loc
+  result[0] = newSymNode(o.opContains)
+  result[1] = s
+  result[2] = loc
 
 proc buildElse(n: PNode; o: Operators): PNode =
-  var s = newNodeIT(nkCurly, n.info, settype(n.sons[0]))
-  for i in 1..n.len-2:
-    let branch = n.sons[i]
-    assert branch.kind == nkOfBranch
-    for j in 0..branch.len-2:
-      s.add(branch.sons[j])
+  var s = newNodeIT(nkCurly, n.info, settype(n[0]))
+  for i in 1..<n.len-1:
+    let branch = n[i]
+    assert branch.kind != nkElse
+    if branch.kind == nkOfBranch:
+      for j in 0..<branch.len-1:
+        s.add(branch[j])
   result = newNodeI(nkCall, n.info, 3)
-  result.sons[0] = newSymNode(o.opContains)
-  result.sons[1] = s
-  result.sons[2] = n.sons[0]
+  result[0] = newSymNode(o.opContains)
+  result[1] = s
+  result[2] = n[0]
 
 proc addDiscriminantFact*(m: var TModel, n: PNode) =
   var fact = newNodeI(nkCall, n.info, 3)
-  fact.sons[0] = newSymNode(m.o.opEq)
-  fact.sons[1] = n.sons[0]
-  fact.sons[2] = n.sons[1]
+  fact[0] = newSymNode(m.g.operators.opEq)
+  fact[1] = n[0]
+  fact[2] = n[1]
   m.s.add fact
 
 proc addAsgnFact*(m: var TModel, key, value: PNode) =
   var fact = newNodeI(nkCall, key.info, 3)
-  fact.sons[0] = newSymNode(m.o.opEq)
-  fact.sons[1] = key
-  fact.sons[2] = value
+  fact[0] = newSymNode(m.g.operators.opEq)
+  fact[1] = key
+  fact[2] = value
   m.s.add fact
 
 proc sameSubexprs*(m: TModel; a, b: PNode): bool =
@@ -1014,34 +1149,38 @@ proc sameSubexprs*(m: TModel; a, b: PNode): bool =
   # However, nil checking requires exactly the same mechanism! But for now
   # we simply use sameTree and live with the unsoundness of the analysis.
   var check = newNodeI(nkCall, a.info, 3)
-  check.sons[0] = newSymNode(m.o.opEq)
-  check.sons[1] = a
-  check.sons[2] = b
+  check[0] = newSymNode(m.g.operators.opEq)
+  check[1] = a
+  check[2] = b
   result = m.doesImply(check) == impYes
 
 proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) =
-  let branch = n.sons[i]
+  let branch = n[i]
   if branch.kind == nkOfBranch:
-    m.s.add buildOf(branch, n.sons[0], m.o)
+    m.s.add buildOf(branch, n[0], m.g.operators)
   else:
-    m.s.add n.buildElse(m.o).neg(m.o)
+    m.s.add n.buildElse(m.g.operators).neg(m.g.operators)
 
 proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
-  if check.sons[1].kind == nkCurly:
+  if check[1].kind == nkCurly:
     result = copyTree(check)
     if access.kind == nkDotExpr:
       var a = copyTree(access)
-      a.sons[1] = check.sons[2]
-      result.sons[2] = a
+      a[1] = check[2]
+      result[2] = a
       # 'access.kind != nkDotExpr' can happen for object constructors
       # which we don't check yet
   else:
     # it is some 'not'
     assert check.getMagic == mNot
-    result = buildProperFieldCheck(access, check.sons[1], o).neg(o)
+    result = buildProperFieldCheck(access, check[1], o).neg(o)
 
-proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef) =
-  for i in 1..n.len-1:
-    let check = buildProperFieldCheck(n.sons[0], n.sons[i], m.o)
+proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef; produceError: bool) =
+  for i in 1..<n.len:
+    let check = buildProperFieldCheck(n[0], n[i], m.g.operators)
     if check != nil and m.doesImply(check) != impYes:
-      message(conf, n.info, warnProveField, renderTree(n.sons[0])); break
+      if produceError:
+        localError(conf, n.info, "field access outside of valid case branch: " & renderTree(n[0]))
+      else:
+        message(conf, n.info, warnProveField, renderTree(n[0]))
+      break
diff --git a/compiler/hlo.nim b/compiler/hlo.nim
index bbbcb4e56..9fdec38c0 100644
--- a/compiler/hlo.nim
+++ b/compiler/hlo.nim
@@ -8,18 +8,21 @@
 #
 
 # This include implements the high level optimization pass.
+# included from sem.nim
 
 proc hlo(c: PContext, n: PNode): PNode
 
 proc evalPattern(c: PContext, n, orig: PNode): PNode =
-  internalAssert c.config, n.kind == nkCall and n.sons[0].kind == nkSym
+  internalAssert c.config, n.kind == nkCall and n[0].kind == nkSym
   # we need to ensure that the resulting AST is semchecked. However, it's
-  # aweful to semcheck before macro invocation, so we don't and treat
+  # awful to semcheck before macro invocation, so we don't and treat
   # templates and macros as immediate in this context.
-  var rule: string
-  if optHints in c.config.options and hintPattern in c.config.notes:
-    rule = renderTree(n, {renderNoComments})
-  let s = n.sons[0].sym
+  var rule: string =
+    if c.config.hasHint(hintPattern):
+      renderTree(n, {renderNoComments})
+    else:
+      ""
+  let s = n[0].sym
   case s.kind
   of skMacro:
     result = semMacroExpr(c, n, orig, s)
@@ -27,7 +30,7 @@ proc evalPattern(c: PContext, n, orig: PNode): PNode =
     result = semTemplateExpr(c, n, s, {efFromHlo})
   else:
     result = semDirectOp(c, n, {})
-  if optHints in c.config.options and hintPattern in c.config.notes:
+  if c.config.hasHint(hintPattern):
     message(c.config, orig.info, hintPattern, rule & " --> '" &
       renderTree(result, {renderNoComments}) & "'")
 
@@ -50,7 +53,7 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
         c.patterns[i] = nil
         if x.kind == nkStmtList:
           assert x.len == 3
-          x.sons[1] = evalPattern(c, x.sons[1], result)
+          x[1] = evalPattern(c, x[1], result)
           result = flattenStmts(x)
         else:
           result = evalPattern(c, x, result)
@@ -67,18 +70,18 @@ proc hlo(c: PContext, n: PNode): PNode =
     # already processed (special cases in semstmts.nim)
     result = n
   else:
-    if n.kind in {nkFastAsgn, nkAsgn, nkIdentDefs, nkVarTuple} and
-        n.sons[0].kind == nkSym and
-        {sfGlobal, sfPure} * n.sons[0].sym.flags == {sfGlobal, sfPure}:
+    if n.kind in {nkFastAsgn, nkAsgn, nkSinkAsgn, nkIdentDefs, nkVarTuple} and
+        n[0].kind == nkSym and
+        {sfGlobal, sfPure} <= n[0].sym.flags:
       # do not optimize 'var g {.global} = re(...)' again!
       return n
     result = applyPatterns(c, n)
     if result == n:
       # no optimization applied, try subtrees:
-      for i in 0 ..< safeLen(result):
-        let a = result.sons[i]
+      for i in 0..<result.safeLen:
+        let a = result[i]
         let h = hlo(c, a)
-        if h != a: result.sons[i] = h
+        if h != a: result[i] = h
     else:
       # perform type checking, so that the replacement still fits:
       if isEmptyType(n.typ) and isEmptyType(result.typ):
@@ -86,18 +89,18 @@ proc hlo(c: PContext, n: PNode): PNode =
       else:
         result = fitNode(c, n.typ, result, n.info)
       # optimization has been applied so check again:
-      result = commonOptimizations(c.graph, c.module, result)
+      result = commonOptimizations(c.graph, c.idgen, c.module, result)
       result = hlo(c, result)
-      result = commonOptimizations(c.graph, c.module, result)
+      result = commonOptimizations(c.graph, c.idgen, c.module, result)
 
 proc hloBody(c: PContext, n: PNode): PNode =
   # fast exit:
-  if c.patterns.len == 0 or optPatterns notin c.config.options: return n
+  if c.patterns.len == 0 or optTrMacros notin c.config.options: return n
   c.hloLoopDetector = 0
   result = hlo(c, n)
 
 proc hloStmt(c: PContext, n: PNode): PNode =
   # fast exit:
-  if c.patterns.len == 0 or optPatterns notin c.config.options: return n
+  if c.patterns.len == 0 or optTrMacros notin c.config.options: return n
   c.hloLoopDetector = 0
   result = hlo(c, n)
diff --git a/compiler/ic/bitabs.nim b/compiler/ic/bitabs.nim
new file mode 100644
index 000000000..0c9994c83
--- /dev/null
+++ b/compiler/ic/bitabs.nim
@@ -0,0 +1,178 @@
+## A BiTable is a table that can be seen as an optimized pair
+## of `(Table[LitId, Val], Table[Val, LitId])`.
+
+import std/hashes
+import rodfiles
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+type
+  LitId* = distinct uint32
+
+  BiTable*[T] = object
+    vals: seq[T] # indexed by LitId
+    keys: seq[LitId]  # indexed by hash(val)
+
+proc initBiTable*[T](): BiTable[T] = BiTable[T](vals: @[], keys: @[])
+
+proc nextTry(h, maxHash: Hash): Hash {.inline.} =
+  result = (h + 1) and maxHash
+
+template maxHash(t): untyped = high(t.keys)
+template isFilled(x: LitId): bool = x.uint32 > 0'u32
+
+proc `$`*(x: LitId): string {.borrow.}
+proc `<`*(x, y: LitId): bool {.borrow.}
+proc `<=`*(x, y: LitId): bool {.borrow.}
+proc `==`*(x, y: LitId): bool {.borrow.}
+proc hash*(x: LitId): Hash {.borrow.}
+
+
+proc len*[T](t: BiTable[T]): int = t.vals.len
+
+proc mustRehash(length, counter: int): bool {.inline.} =
+  assert(length > counter)
+  result = (length * 2 < counter * 3) or (length - counter < 4)
+
+const
+  idStart = 1
+
+template idToIdx(x: LitId): int = x.int - idStart
+
+proc hasLitId*[T](t: BiTable[T]; x: LitId): bool =
+  let idx = idToIdx(x)
+  result = idx >= 0 and idx < t.vals.len
+
+proc enlarge[T](t: var BiTable[T]) =
+  var n: seq[LitId]
+  newSeq(n, len(t.keys) * 2)
+  swap(t.keys, n)
+  for i in 0..high(n):
+    let eh = n[i]
+    if isFilled(eh):
+      var j = hash(t.vals[idToIdx eh]) and maxHash(t)
+      while isFilled(t.keys[j]):
+        j = nextTry(j, maxHash(t))
+      t.keys[j] = move n[i]
+
+proc getKeyId*[T](t: BiTable[T]; v: T): LitId =
+  let origH = hash(v)
+  var h = origH and maxHash(t)
+  if t.keys.len != 0:
+    while true:
+      let litId = t.keys[h]
+      if not isFilled(litId): break
+      if t.vals[idToIdx t.keys[h]] == v: return litId
+      h = nextTry(h, maxHash(t))
+  return LitId(0)
+
+proc getOrIncl*[T](t: var BiTable[T]; v: T): LitId =
+  let origH = hash(v)
+  var h = origH and maxHash(t)
+  if t.keys.len != 0:
+    while true:
+      let litId = t.keys[h]
+      if not isFilled(litId): break
+      if t.vals[idToIdx t.keys[h]] == v: return litId
+      h = nextTry(h, maxHash(t))
+    # not found, we need to insert it:
+    if mustRehash(t.keys.len, t.vals.len):
+      enlarge(t)
+      # recompute where to insert:
+      h = origH and maxHash(t)
+      while true:
+        let litId = t.keys[h]
+        if not isFilled(litId): break
+        h = nextTry(h, maxHash(t))
+  else:
+    setLen(t.keys, 16)
+    h = origH and maxHash(t)
+
+  result = LitId(t.vals.len + idStart)
+  t.keys[h] = result
+  t.vals.add v
+
+
+proc `[]`*[T](t: var BiTable[T]; litId: LitId): var T {.inline.} =
+  let idx = idToIdx litId
+  assert idx < t.vals.len
+  result = t.vals[idx]
+
+proc `[]`*[T](t: BiTable[T]; litId: LitId): lent T {.inline.} =
+  let idx = idToIdx litId
+  assert idx < t.vals.len
+  result = t.vals[idx]
+
+proc hash*[T](t: BiTable[T]): Hash =
+  ## as the keys are hashes of the values, we simply use them instead
+  var h: Hash = 0
+  for i, n in pairs t.keys:
+    h = h !& hash((i, n))
+  result = !$h
+
+proc store*[T](f: var RodFile; t: BiTable[T]) =
+  storeSeq(f, t.vals)
+  storeSeq(f, t.keys)
+
+proc load*[T](f: var RodFile; t: var BiTable[T]) =
+  loadSeq(f, t.vals)
+  loadSeq(f, t.keys)
+
+proc sizeOnDisc*(t: BiTable[string]): int =
+  result = 4
+  for x in t.vals:
+    result += x.len + 4
+  result += t.keys.len * sizeof(LitId)
+
+when isMainModule:
+
+  var t: BiTable[string]
+
+  echo getOrIncl(t, "hello")
+
+  echo getOrIncl(t, "hello")
+  echo getOrIncl(t, "hello3")
+  echo getOrIncl(t, "hello4")
+  echo getOrIncl(t, "helloasfasdfdsa")
+  echo getOrIncl(t, "hello")
+  echo getKeyId(t, "hello")
+  echo getKeyId(t, "none")
+
+  for i in 0 ..< 100_000:
+    discard t.getOrIncl($i & "___" & $i)
+
+  for i in 0 ..< 100_000:
+    assert t.getOrIncl($i & "___" & $i).idToIdx == i + 4
+  echo "begin"
+  echo t.vals.len
+
+  echo t.vals[0]
+  echo t.vals[1004]
+
+  echo "middle"
+
+  var tf: BiTable[float]
+
+  discard tf.getOrIncl(0.4)
+  discard tf.getOrIncl(16.4)
+  discard tf.getOrIncl(32.4)
+  echo getKeyId(tf, 32.4)
+
+  var f2 = open("testblah.bin", fmWrite)
+  echo store(f2, tf)
+  f2.close
+
+  var f1 = open("testblah.bin", fmRead)
+
+  var t2: BiTable[float]
+
+  echo f1.load(t2)
+  echo t2.vals.len
+
+  echo getKeyId(t2, 32.4)
+
+  echo "end"
+
+
+  f1.close
diff --git a/compiler/ic/cbackend.nim b/compiler/ic/cbackend.nim
new file mode 100644
index 000000000..83f1b4cc7
--- /dev/null
+++ b/compiler/ic/cbackend.nim
@@ -0,0 +1,180 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2021 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## New entry point into our C/C++ code generator. Ideally
+## somebody would rewrite the old backend (which is 8000 lines of crufty Nim code)
+## to work on packed trees directly and produce the C code as an AST which can
+## then be rendered to text in a very simple manner. Unfortunately nobody wrote
+## this code. So instead we wrap the existing cgen.nim and its friends so that
+## we call directly into the existing code generation logic but avoiding the
+## naive, outdated `passes` design. Thus you will see some
+## `useAliveDataFromDce in flags` checks in the old code -- the old code is
+## also doing cross-module dependency tracking and DCE that we don't need
+## anymore. DCE is now done as prepass over the entire packed module graph.
+
+import std/[packedsets, algorithm, tables]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen,
+  pathutils, extccomp, msgs, modulepaths]
+
+import packed_ast, ic, dce, rodfiles
+
+proc unpackTree(g: ModuleGraph; thisModule: int;
+                tree: PackedTree; n: NodePos): PNode =
+  var decoder = initPackedDecoder(g.config, g.cache)
+  result = loadNodes(decoder, g.packed, thisModule, tree, n)
+
+proc setupBackendModule(g: ModuleGraph; m: var LoadedModule) =
+  if g.backend == nil:
+    g.backend = cgendata.newModuleList(g)
+  assert g.backend != nil
+  var bmod = cgen.newModule(BModuleList(g.backend), m.module, g.config)
+  bmod.idgen = idgenFromLoadedModule(m)
+
+proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var AliveSyms) =
+  var bmod = BModuleList(g.backend).modules[m.module.position]
+  assert bmod != nil
+  bmod.flags.incl useAliveDataFromDce
+  bmod.alive = move alive[m.module.position]
+
+  for p in allNodes(m.fromDisk.topLevel):
+    let n = unpackTree(g, m.module.position, m.fromDisk.topLevel, p)
+    cgen.genTopLevelStmt(bmod, n)
+
+  finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
+  for disp in getDispatchers(g):
+    genProcAux(bmod, disp)
+  m.fromDisk.backendFlags = cgen.whichInitProcs(bmod)
+
+proc replayTypeInfo(g: ModuleGraph; m: var LoadedModule; origin: FileIndex) =
+  for x in mitems(m.fromDisk.emittedTypeInfo):
+    #echo "found type ", x, " for file ", int(origin)
+    g.emittedTypeInfo[x] = origin
+
+proc addFileToLink(config: ConfigRef; m: PSym) =
+  let filename = AbsoluteFile toFullPath(config, m.position.FileIndex)
+  let ext =
+      if config.backend == backendCpp: ".nim.cpp"
+      elif config.backend == backendObjc: ".nim.m"
+      else: ".nim.c"
+  let cfile = changeFileExt(completeCfilePath(config,
+                            mangleModuleName(config, filename).AbsoluteFile), ext)
+  let objFile = completeCfilePath(config, toObjFile(config, cfile))
+  if fileExists(objFile):
+    var cf = Cfile(nimname: m.name.s, cname: cfile,
+                   obj: objFile,
+                   flags: {CfileFlag.Cached})
+    addFileToCompile(config, cf)
+
+when defined(debugDce):
+  import os, std/packedsets
+
+proc storeAliveSymsImpl(asymFile: AbsoluteFile; s: seq[int32]) =
+  var f = rodfiles.create(asymFile.string)
+  f.storeHeader()
+  f.storeSection aliveSymsSection
+  f.storeSeq(s)
+  close f
+
+template prepare {.dirty.} =
+  let asymFile = toRodFile(config, AbsoluteFile toFullPath(config, position.FileIndex), ".alivesyms")
+  var s = newSeqOfCap[int32](alive[position].len)
+  for a in items(alive[position]): s.add int32(a)
+  sort(s)
+
+proc storeAliveSyms(config: ConfigRef; position: int; alive: AliveSyms) =
+  prepare()
+  storeAliveSymsImpl(asymFile, s)
+
+proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool =
+  prepare()
+  var f2 = rodfiles.open(asymFile.string)
+  f2.loadHeader()
+  f2.loadSection aliveSymsSection
+  var oldData: seq[int32] = @[]
+  f2.loadSeq(oldData)
+  f2.close
+  if f2.err == ok and oldData == s:
+    result = false
+  else:
+    when defined(debugDce):
+      let oldAsSet = toPackedSet[int32](oldData)
+      let newAsSet = toPackedSet[int32](s)
+      echo "set of live symbols changed ", asymFile.changeFileExt("rod"), " ", position, " ", f2.err
+      echo "in old but not in new ", oldAsSet.difference(newAsSet), " number of entries in old ", oldAsSet.len
+      echo "in new but not in old ", newAsSet.difference(oldAsSet), " number of entries in new ", newAsSet.len
+      #if execShellCmd(getAppFilename() & " rod " & quoteShell(asymFile.changeFileExt("rod"))) != 0:
+      #  echo "command failed"
+    result = true
+    storeAliveSymsImpl(asymFile, s)
+
+proc genPackedModule(g: ModuleGraph, i: int; alive: var AliveSyms) =
+  # case statement here to enforce exhaustive checks.
+  case g.packed[i].status
+  of undefined:
+    discard "nothing to do"
+  of loading, stored:
+    assert false
+  of storing, outdated:
+    storeAliveSyms(g.config, g.packed[i].module.position, alive)
+    generateCodeForModule(g, g.packed[i], alive)
+    closeRodFile(g, g.packed[i].module)
+  of loaded:
+    if g.packed[i].loadedButAliveSetChanged:
+      generateCodeForModule(g, g.packed[i], alive)
+    else:
+      addFileToLink(g.config, g.packed[i].module)
+      replayTypeInfo(g, g.packed[i], FileIndex(i))
+
+      if g.backend == nil:
+        g.backend = cgendata.newModuleList(g)
+      registerInitProcs(BModuleList(g.backend), g.packed[i].module, g.packed[i].fromDisk.backendFlags)
+
+proc generateCode*(g: ModuleGraph) =
+  ## The single entry point, generate C(++) code for the entire
+  ## Nim program aka `ModuleGraph`.
+  resetForBackend(g)
+  var alive = computeAliveSyms(g.packed, g.config)
+
+  when false:
+    for i in 0..<len(g.packed):
+      echo i, " is of status ", g.packed[i].status, " ", toFullPath(g.config, FileIndex(i))
+
+  # First pass: Setup all the backend modules for all the modules that have
+  # changed:
+  for i in 0..<len(g.packed):
+    # case statement here to enforce exhaustive checks.
+    case g.packed[i].status
+    of undefined:
+      discard "nothing to do"
+    of loading, stored:
+      assert false
+    of storing, outdated:
+      setupBackendModule(g, g.packed[i])
+    of loaded:
+      # Even though this module didn't change, DCE might trigger a change.
+      # Consider this case: Module A uses symbol S from B and B does not use
+      # S itself. A is then edited not to use S either. Thus we have to
+      # recompile B in order to remove S from the final result.
+      if aliveSymsChanged(g.config, g.packed[i].module.position, alive):
+        g.packed[i].loadedButAliveSetChanged = true
+        setupBackendModule(g, g.packed[i])
+
+  # Second pass: Code generation.
+  let mainModuleIdx = g.config.projectMainIdx2.int
+  # We need to generate the main module last, because only then
+  # all init procs have been registered:
+  for i in 0..<len(g.packed):
+    if i != mainModuleIdx:
+      genPackedModule(g, i, alive)
+  if mainModuleIdx >= 0:
+    genPackedModule(g, mainModuleIdx, alive)
diff --git a/compiler/ic/dce.nim b/compiler/ic/dce.nim
new file mode 100644
index 000000000..6eb36431e
--- /dev/null
+++ b/compiler/ic/dce.nim
@@ -0,0 +1,169 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2021 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Dead code elimination (=DCE) for IC.
+
+import std/[intsets, tables]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+import ".." / [ast, options, lineinfos, types]
+
+import packed_ast, ic, bitabs
+
+type
+  AliveSyms* = seq[IntSet]
+  AliveContext* = object ## Purpose is to fill the 'alive' field.
+    stack: seq[(int, TOptions, NodePos)] ## A stack for marking symbols as alive.
+    decoder: PackedDecoder ## We need a PackedDecoder for module ID address translations.
+    thisModule: int  ## The module we're currently analysing for DCE.
+    alive: AliveSyms ## The final result of our computation.
+    options: TOptions
+    compilerProcs: Table[string, (int, int32)]
+
+proc isExportedToC(c: var AliveContext; g: PackedModuleGraph; symId: int32): bool =
+  ## "Exported to C" procs are special (these are marked with '.exportc') because these
+  ## must not be optimized away!
+  let symPtr = unsafeAddr g[c.thisModule].fromDisk.syms[symId]
+  let flags = symPtr.flags
+  # due to a bug/limitation in the lambda lifting, unused inner procs
+  # are not transformed correctly; issue (#411). However, the whole purpose here
+  # is to eliminate unused procs. So there is no special logic required for this case.
+  if sfCompileTime notin flags:
+    if ({sfExportc, sfCompilerProc} * flags != {}) or
+        (symPtr.kind == skMethod):
+      result = true
+    else:
+      result = false
+      # XXX: This used to be a condition to:
+      #  (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
+    if sfCompilerProc in flags:
+      c.compilerProcs[g[c.thisModule].fromDisk.strings[symPtr.name]] = (c.thisModule, symId)
+  else:
+    result = false
+
+template isNotGeneric(n: NodePos): bool = ithSon(tree, n, genericParamsPos).kind == nkEmpty
+
+proc followLater(c: var AliveContext; g: PackedModuleGraph; module: int; item: int32) =
+  ## Marks a symbol 'item' as used and later in 'followNow' the symbol's body will
+  ## be analysed.
+  if not c.alive[module].containsOrIncl(item):
+    var body = g[module].fromDisk.syms[item].ast
+    if body != emptyNodeId:
+      let opt = g[module].fromDisk.syms[item].options
+      if g[module].fromDisk.syms[item].kind in routineKinds:
+        body = NodeId ithSon(g[module].fromDisk.bodies, NodePos body, bodyPos)
+      c.stack.add((module, opt, NodePos(body)))
+
+    when false:
+      let nid = g[module].fromDisk.syms[item].name
+      if nid != LitId(0):
+        let name = g[module].fromDisk.strings[nid]
+        if name in ["nimFrame", "callDepthLimitReached"]:
+          echo "I was called! ", name, " body exists: ", body != emptyNodeId, " ", module, " ", item
+
+proc requestCompilerProc(c: var AliveContext; g: PackedModuleGraph; name: string) =
+  let (module, item) = c.compilerProcs[name]
+  followLater(c, g, module, item)
+
+proc loadTypeKind(t: PackedItemId; c: AliveContext; g: PackedModuleGraph; toSkip: set[TTypeKind]): TTypeKind =
+  template kind(t: ItemId): TTypeKind = g[t.module].fromDisk.types[t.item].kind
+
+  var t2 = translateId(t, g, c.thisModule, c.decoder.config)
+  result = t2.kind
+  while result in toSkip:
+    t2 = translateId(g[t2.module].fromDisk.types[t2.item].types[^1], g, t2.module, c.decoder.config)
+    result = t2.kind
+
+proc rangeCheckAnalysis(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: NodePos) =
+  ## Replicates the logic of `ccgexprs.genRangeChck`.
+  ## XXX Refactor so that the duplicated logic is avoided. However, for now it's not clear
+  ## the approach has enough merit.
+  var dest = loadTypeKind(n.typ, c, g, abstractVar)
+  if optRangeCheck notin c.options or dest in {tyUInt..tyUInt64}:
+    discard "no need to generate a check because it was disabled"
+  else:
+    let n0t = loadTypeKind(n.firstSon.typ, c, g, {})
+    if n0t in {tyUInt, tyUInt64}:
+      c.requestCompilerProc(g, "raiseRangeErrorNoArgs")
+    else:
+      let raiser =
+        case loadTypeKind(n.typ, c, g, abstractVarRange)
+        of tyUInt..tyUInt64, tyChar: "raiseRangeErrorU"
+        of tyFloat..tyFloat128: "raiseRangeErrorF"
+        else: "raiseRangeErrorI"
+      c.requestCompilerProc(g, raiser)
+
+proc aliveCode(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: NodePos) =
+  ## Marks the symbols we encounter when we traverse the AST at `tree[n]` as alive, unless
+  ## it is purely in a declarative context (type section etc.).
+  case n.kind
+  of nkNone..pred(nkSym), succ(nkSym)..nkNilLit:
+    discard "ignore non-sym atoms"
+  of nkSym:
+    # This symbol is alive and everything its body references.
+    followLater(c, g, c.thisModule, tree[n].soperand)
+  of nkModuleRef:
+    let (n1, n2) = sons2(tree, n)
+    assert n1.kind == nkNone
+    assert n2.kind == nkNone
+    let m = n1.litId
+    let item = tree[n2].soperand
+    let otherModule = toFileIndexCached(c.decoder, g, c.thisModule, m).int
+    followLater(c, g, otherModule, item)
+  of nkMacroDef, nkTemplateDef, nkTypeSection, nkTypeOfExpr,
+     nkCommentStmt, nkIncludeStmt,
+     nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
+     nkFromStmt, nkStaticStmt:
+    discard
+  of nkVarSection, nkLetSection, nkConstSection:
+    # XXX ignore the defining local variable name?
+    for son in sonsReadonly(tree, n):
+      aliveCode(c, g, tree, son)
+  of nkChckRangeF, nkChckRange64, nkChckRange:
+    rangeCheckAnalysis(c, g, tree, n)
+  of nkProcDef, nkConverterDef, nkMethodDef, nkFuncDef, nkIteratorDef:
+    if n.firstSon.kind == nkSym and isNotGeneric(n):
+      let item = tree[n.firstSon].soperand
+      if isExportedToC(c, g, item):
+        # This symbol is alive and everything its body references.
+        followLater(c, g, c.thisModule, item)
+  else:
+    for son in sonsReadonly(tree, n):
+      aliveCode(c, g, tree, son)
+
+proc followNow(c: var AliveContext; g: PackedModuleGraph) =
+  ## Mark all entries in the stack. Marking can add more entries
+  ## to the stack but eventually we have looked at every alive symbol.
+  while c.stack.len > 0:
+    let (modId, opt, ast) = c.stack.pop()
+    c.thisModule = modId
+    c.options = opt
+    aliveCode(c, g, g[modId].fromDisk.bodies, ast)
+
+proc computeAliveSyms*(g: PackedModuleGraph; conf: ConfigRef): AliveSyms =
+  ## Entry point for our DCE algorithm.
+  var c = AliveContext(stack: @[], decoder: PackedDecoder(config: conf),
+                       thisModule: -1, alive: newSeq[IntSet](g.len),
+                       options: conf.options)
+  for i in countdown(len(g)-1, 0):
+    if g[i].status != undefined:
+      c.thisModule = i
+      for p in allNodes(g[i].fromDisk.topLevel):
+        aliveCode(c, g, g[i].fromDisk.topLevel, p)
+
+  followNow(c, g)
+  result = move(c.alive)
+
+proc isAlive*(a: AliveSyms; module: int, item: int32): bool =
+  ## Backends use this to query if a symbol is `alive` which means
+  ## we need to produce (C/C++/etc) code for it.
+  result = a[module].contains(item)
+
diff --git a/compiler/ic/design.rst b/compiler/ic/design.rst
new file mode 100644
index 000000000..b096e3103
--- /dev/null
+++ b/compiler/ic/design.rst
@@ -0,0 +1,56 @@
+====================================
+  Incremental Recompilations
+====================================
+
+We split the Nim compiler into a frontend and a backend.
+The frontend produces a set of `.rod` files. Every `.nim` module
+produces its own `.rod` file.
+
+- The IR must be a faithful representation of the AST in memory.
+- The backend can do its own caching but doesn't have to. In the
+  current implementation the backend also caches its results.
+
+Advantage of the "set of files" vs the previous global database:
+- By construction, we either read from the `.rod` file or from the
+  `.nim` file, there can be no inconsistency. There can also be no
+  partial updates.
+- No dependency to external packages (SQLite). SQLite simply is too
+  slow and the old way of serialization was too slow too. We use a
+  format designed for Nim and expect to base further tools on this
+  file format.
+
+References to external modules must be (moduleId, symId) pairs.
+The symbol IDs are module specific. This way no global ID increment
+mechanism needs to be implemented that we could get wrong. ModuleIds
+are rod-file specific too.
+
+
+
+Global state
+------------
+
+There is no global state.
+
+Rod File Format
+---------------
+
+It's a simple binary file format. `rodfiles.nim` contains some details.
+
+
+Backend
+-------
+
+Nim programmers have to come to enjoy whole-program dead code elimination,
+by default. Since this is a "whole program" optimization, it does break
+modularity. However, thanks to the packed AST representation we can perform
+this global analysis without having to unpack anything. This is basically
+a mark&sweep GC algorithm:
+
+- Start with the top level statements. Every symbol that is referenced
+  from a top level statement is not "dead" and needs to be compiled by
+  the backend.
+- Every symbol referenced from a referenced symbol also has to be
+  compiled.
+
+Caching logic: Only if the set of alive symbols is different from the
+last run, the module has to be regenerated.
diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim
new file mode 100644
index 000000000..8e81633ef
--- /dev/null
+++ b/compiler/ic/ic.nim
@@ -0,0 +1,1343 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+import std/[hashes, tables, intsets, monotimes]
+import packed_ast, bitabs, rodfiles
+import ".." / [ast, idents, lineinfos, msgs, ropes, options,
+  pathutils, condsyms, packages, modulepaths]
+#import ".." / [renderer, astalgo]
+from std/os import removeFile, isAbsolute
+
+import ../../dist/checksums/src/checksums/sha1
+
+import iclineinfos
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions, formatfloat]
+
+type
+  PackedConfig* = object
+    backend: TBackend
+    selectedGC: TGCMode
+    cCompiler: TSystemCC
+    options: TOptions
+    globalOptions: TGlobalOptions
+
+  ModuleBackendFlag* = enum
+    HasDatInitProc
+    HasModuleInitProc
+
+  PackedModule* = object ## the parts of a PackedEncoder that are part of the .rod file
+    definedSymbols: string
+    moduleFlags: TSymFlags
+    includes*: seq[(LitId, string)] # first entry is the module filename itself
+    imports: seq[LitId] # the modules this module depends on
+    toReplay*: PackedTree # pragmas and VM specific state to replay.
+    topLevel*: PackedTree  # top level statements
+    bodies*: PackedTree # other trees. Referenced from typ.n and sym.ast by their position.
+    #producedGenerics*: Table[GenericKey, SymId]
+    exports*: seq[(LitId, int32)]
+    hidden: seq[(LitId, int32)]
+    reexports: seq[(LitId, PackedItemId)]
+    compilerProcs*: seq[(LitId, int32)]
+    converters*, methods*, trmacros*, pureEnums*: seq[int32]
+
+    typeInstCache*: seq[(PackedItemId, PackedItemId)]
+    procInstCache*: seq[PackedInstantiation]
+    attachedOps*: seq[(PackedItemId, TTypeAttachedOp, PackedItemId)]
+    methodsPerGenericType*: seq[(PackedItemId, int, PackedItemId)]
+    enumToStringProcs*: seq[(PackedItemId, PackedItemId)]
+    methodsPerType*: seq[(PackedItemId, PackedItemId)]
+    dispatchers*: seq[PackedItemId]
+
+    emittedTypeInfo*: seq[string]
+    backendFlags*: set[ModuleBackendFlag]
+
+    syms*: OrderedTable[int32, PackedSym]
+    types*: OrderedTable[int32, PackedType]
+    strings*: BiTable[string] # we could share these between modules.
+    numbers*: BiTable[BiggestInt] # we also store floats in here so
+                                  # that we can assure that every bit is kept
+    man*: LineInfoManager
+
+    cfg: PackedConfig
+
+  PackedEncoder* = object
+    #m*: PackedModule
+    thisModule*: int32
+    lastFile*: FileIndex # remember the last lookup entry.
+    lastLit*: LitId
+    filenames*: Table[FileIndex, LitId]
+    pendingTypes*: seq[PType]
+    pendingSyms*: seq[PSym]
+    typeMarker*: IntSet #Table[ItemId, TypeId]  # ItemId.item -> TypeId
+    symMarker*: IntSet #Table[ItemId, SymId]    # ItemId.item -> SymId
+    config*: ConfigRef
+
+proc toString*(tree: PackedTree; pos: NodePos; m: PackedModule; nesting: int;
+               result: var string) =
+  if result.len > 0 and result[^1] notin {' ', '\n'}:
+    result.add ' '
+
+  result.add $tree[pos].kind
+  case tree[pos].kind
+  of nkEmpty, nkNilLit, nkType: discard
+  of nkIdent, nkStrLit..nkTripleStrLit:
+    result.add " "
+    result.add m.strings[LitId tree[pos].uoperand]
+  of nkSym:
+    result.add " "
+    result.add m.strings[m.syms[tree[pos].soperand].name]
+  of directIntLit:
+    result.add " "
+    result.addInt tree[pos].soperand
+  of externSIntLit:
+    result.add " "
+    result.addInt m.numbers[LitId tree[pos].uoperand]
+  of externUIntLit:
+    result.add " "
+    result.addInt cast[uint64](m.numbers[LitId tree[pos].uoperand])
+  of nkFloatLit..nkFloat128Lit:
+    result.add " "
+    result.addFloat cast[BiggestFloat](m.numbers[LitId tree[pos].uoperand])
+  else:
+    result.add "(\n"
+    for i in 1..(nesting+1)*2: result.add ' '
+    for child in sonsReadonly(tree, pos):
+      toString(tree, child, m, nesting + 1, result)
+    result.add "\n"
+    for i in 1..nesting*2: result.add ' '
+    result.add ")"
+    #for i in 1..nesting*2: result.add ' '
+
+proc toString*(tree: PackedTree; n: NodePos; m: PackedModule): string =
+  result = ""
+  toString(tree, n, m, 0, result)
+
+proc debug*(tree: PackedTree; m: PackedModule) =
+  stdout.write toString(tree, NodePos 0, m)
+
+proc isActive*(e: PackedEncoder): bool = e.config != nil
+proc disable(e: var PackedEncoder) = e.config = nil
+
+template primConfigFields(fn: untyped) {.dirty.} =
+  fn backend
+  fn selectedGC
+  fn cCompiler
+  fn options
+  fn globalOptions
+
+proc definedSymbolsAsString(config: ConfigRef): string =
+  result = newStringOfCap(200)
+  result.add "config"
+  for d in definedSymbolNames(config.symbols):
+    result.add ' '
+    result.add d
+
+proc rememberConfig(c: var PackedEncoder; m: var PackedModule; config: ConfigRef; pc: PackedConfig) =
+  m.definedSymbols = definedSymbolsAsString(config)
+  #template rem(x) =
+  #  c.m.cfg.x = config.x
+  #primConfigFields rem
+  m.cfg = pc
+
+const
+  debugConfigDiff = defined(debugConfigDiff)
+
+when debugConfigDiff:
+  import hashes, tables, intsets, sha1, strutils, sets
+
+proc configIdentical(m: PackedModule; config: ConfigRef): bool =
+  result = m.definedSymbols == definedSymbolsAsString(config)
+  when debugConfigDiff:
+    if not result:
+      var wordsA = m.definedSymbols.split(Whitespace).toHashSet()
+      var wordsB = definedSymbolsAsString(config).split(Whitespace).toHashSet()
+      for c in wordsA - wordsB:
+        echo "in A but not in B ", c
+      for c in wordsB - wordsA:
+        echo "in B but not in A ", c
+  template eq(x) =
+    result = result and m.cfg.x == config.x
+    when debugConfigDiff:
+      if m.cfg.x != config.x:
+        echo "B ", m.cfg.x, " ", config.x
+  primConfigFields eq
+
+proc rememberStartupConfig*(dest: var PackedConfig, config: ConfigRef) =
+  template rem(x) =
+    dest.x = config.x
+  primConfigFields rem
+  dest.globalOptions.excl optForceFullMake
+
+proc hashFileCached(conf: ConfigRef; fileIdx: FileIndex): string =
+  result = msgs.getHash(conf, fileIdx)
+  if result.len == 0:
+    let fullpath = msgs.toFullPath(conf, fileIdx)
+    result = $secureHashFile(fullpath)
+    msgs.setHash(conf, fileIdx, result)
+
+proc toLitId(x: FileIndex; c: var PackedEncoder; m: var PackedModule): LitId =
+  ## store a file index as a literal
+  if x == c.lastFile:
+    result = c.lastLit
+  else:
+    result = c.filenames.getOrDefault(x)
+    if result == LitId(0):
+      let p = msgs.toFullPath(c.config, x)
+      result = getOrIncl(m.strings, p)
+      c.filenames[x] = result
+    c.lastFile = x
+    c.lastLit = result
+  assert result != LitId(0)
+
+proc toFileIndex*(x: LitId; m: PackedModule; config: ConfigRef): FileIndex =
+  result = msgs.fileInfoIdx(config, AbsoluteFile m.strings[x])
+
+proc includesIdentical(m: var PackedModule; config: ConfigRef): bool =
+  for it in mitems(m.includes):
+    if hashFileCached(config, toFileIndex(it[0], m, config)) != it[1]:
+      return false
+  result = true
+
+proc initEncoder*(c: var PackedEncoder; m: var PackedModule; moduleSym: PSym; config: ConfigRef; pc: PackedConfig) =
+  ## setup a context for serializing to packed ast
+  c.thisModule = moduleSym.itemId.module
+  c.config = config
+  m.moduleFlags = moduleSym.flags
+  m.bodies = newTreeFrom(m.topLevel)
+  m.toReplay = newTreeFrom(m.topLevel)
+
+  c.lastFile = FileIndex(-10)
+
+  let thisNimFile = FileIndex c.thisModule
+  var h = msgs.getHash(config, thisNimFile)
+  if h.len == 0:
+    let fullpath = msgs.toFullPath(config, thisNimFile)
+    if isAbsolute(fullpath):
+      # For NimScript compiler API support the main Nim file might be from a stream.
+      h = $secureHashFile(fullpath)
+      msgs.setHash(config, thisNimFile, h)
+  m.includes.add((toLitId(thisNimFile, c, m), h)) # the module itself
+
+  rememberConfig(c, m, config, pc)
+
+proc addIncludeFileDep*(c: var PackedEncoder; m: var PackedModule; f: FileIndex) =
+  m.includes.add((toLitId(f, c, m), hashFileCached(c.config, f)))
+
+proc addImportFileDep*(c: var PackedEncoder; m: var PackedModule; f: FileIndex) =
+  m.imports.add toLitId(f, c, m)
+
+proc addHidden*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
+  assert s.kind != skUnknown
+  let nameId = getOrIncl(m.strings, s.name.s)
+  m.hidden.add((nameId, s.itemId.item))
+  assert s.itemId.module == c.thisModule
+
+proc addExported*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
+  assert s.kind != skUnknown
+  assert s.itemId.module == c.thisModule
+  let nameId = getOrIncl(m.strings, s.name.s)
+  m.exports.add((nameId, s.itemId.item))
+
+proc addConverter*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
+  assert c.thisModule == s.itemId.module
+  m.converters.add(s.itemId.item)
+
+proc addTrmacro*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
+  m.trmacros.add(s.itemId.item)
+
+proc addPureEnum*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
+  assert s.kind == skType
+  m.pureEnums.add(s.itemId.item)
+
+proc addMethod*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
+  m.methods.add s.itemId.item
+
+proc addReexport*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
+  assert s.kind != skUnknown
+  if s.kind == skModule: return
+  let nameId = getOrIncl(m.strings, s.name.s)
+  m.reexports.add((nameId, PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m),
+                                        item: s.itemId.item)))
+
+proc addCompilerProc*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
+  let nameId = getOrIncl(m.strings, s.name.s)
+  m.compilerProcs.add((nameId, s.itemId.item))
+
+proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule)
+proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
+proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemId
+
+proc flush(c: var PackedEncoder; m: var PackedModule) =
+  ## serialize any pending types or symbols from the context
+  while true:
+    if c.pendingTypes.len > 0:
+      discard storeType(c.pendingTypes.pop, c, m)
+    elif c.pendingSyms.len > 0:
+      discard storeSym(c.pendingSyms.pop, c, m)
+    else:
+      break
+
+proc toLitId(x: string; m: var PackedModule): LitId =
+  ## store a string as a literal
+  result = getOrIncl(m.strings, x)
+
+proc toLitId(x: BiggestInt; m: var PackedModule): LitId =
+  ## store an integer as a literal
+  result = getOrIncl(m.numbers, x)
+
+proc toPackedInfo(x: TLineInfo; c: var PackedEncoder; m: var PackedModule): PackedLineInfo =
+  pack(m.man, toLitId(x.fileIndex, c, m), x.line.int32, x.col.int32)
+  #PackedLineInfo(line: x.line, col: x.col, file: toLitId(x.fileIndex, c, m))
+
+proc safeItemId(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId {.inline.} =
+  ## given a symbol, produce an ItemId with the correct properties
+  ## for local or remote symbols, packing the symbol as necessary
+  if s == nil or s.kind == skPackage:
+    result = nilItemId
+  #elif s.itemId.module == c.thisModule:
+  #  result = PackedItemId(module: LitId(0), item: s.itemId.item)
+  else:
+    assert int(s.itemId.module) >= 0
+    result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m),
+                          item: s.itemId.item)
+
+proc addMissing(c: var PackedEncoder; p: PSym) =
+  ## consider queuing a symbol for later addition to the packed tree
+  if p != nil and p.itemId.module == c.thisModule:
+    if p.itemId.item notin c.symMarker:
+      if not (sfForward in p.flags and p.kind in routineKinds):
+        c.pendingSyms.add p
+
+proc addMissing(c: var PackedEncoder; p: PType) =
+  ## consider queuing a type for later addition to the packed tree
+  if p != nil and p.uniqueId.module == c.thisModule:
+    if p.uniqueId.item notin c.typeMarker:
+      c.pendingTypes.add p
+
+template storeNode(dest, src, field) =
+  var nodeId: NodeId
+  if src.field != nil:
+    nodeId = getNodeId(m.bodies)
+    toPackedNode(src.field, m.bodies, c, m)
+  else:
+    nodeId = emptyNodeId
+  dest.field = nodeId
+
+proc storeTypeLater(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemId =
+  # We store multiple different trees in m.bodies. For this to work out, we
+  # cannot immediately store types/syms. We enqueue them instead to ensure
+  # we only write one tree into m.bodies after the other.
+  if t.isNil: return nilItemId
+
+  assert t.uniqueId.module >= 0
+  assert t.uniqueId.item > 0
+  result = PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c, m), item: t.uniqueId.item)
+  if t.uniqueId.module == c.thisModule:
+    # the type belongs to this module, so serialize it here, eventually.
+    addMissing(c, t)
+
+proc storeSymLater(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId =
+  if s.isNil: return nilItemId
+  assert s.itemId.module >= 0
+  assert s.itemId.item >= 0
+  result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), item: s.itemId.item)
+  if s.itemId.module == c.thisModule:
+    # the sym belongs to this module, so serialize it here, eventually.
+    addMissing(c, s)
+
+proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemId =
+  ## serialize a ptype
+  if t.isNil: return nilItemId
+
+  assert t.uniqueId.module >= 0
+  assert t.uniqueId.item > 0
+  result = PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c, m), item: t.uniqueId.item)
+
+  if t.uniqueId.module == c.thisModule and not c.typeMarker.containsOrIncl(t.uniqueId.item):
+    #if t.uniqueId.item >= m.types.len:
+    #  setLen m.types, t.uniqueId.item+1
+
+    var p = PackedType(id: t.uniqueId.item, kind: t.kind, flags: t.flags, callConv: t.callConv,
+      size: t.size, align: t.align, nonUniqueId: t.itemId.item,
+      paddingAtEnd: t.paddingAtEnd)
+    storeNode(p, t, n)
+    p.typeInst = t.typeInst.storeType(c, m)
+    for kid in kids t:
+      p.types.add kid.storeType(c, m)
+    c.addMissing t.sym
+    p.sym = t.sym.safeItemId(c, m)
+    c.addMissing t.owner
+    p.owner = t.owner.safeItemId(c, m)
+
+    # fill the reserved slot, nothing else:
+    m.types[t.uniqueId.item] = p
+
+proc toPackedLib(l: PLib; c: var PackedEncoder; m: var PackedModule): PackedLib =
+  ## the plib hangs off the psym via the .annex field
+  if l.isNil: return
+  result = PackedLib(kind: l.kind, generated: l.generated,
+    isOverridden: l.isOverridden, name: toLitId($l.name, m)
+  )
+  storeNode(result, l, path)
+
+proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId =
+  ## serialize a psym
+  if s.isNil: return nilItemId
+
+  assert s.itemId.module >= 0
+  result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), item: s.itemId.item)
+
+  if s.itemId.module == c.thisModule and not c.symMarker.containsOrIncl(s.itemId.item):
+    #if s.itemId.item >= m.syms.len:
+    #  setLen m.syms, s.itemId.item+1
+
+    assert sfForward notin s.flags
+
+    var p = PackedSym(id: s.itemId.item, kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c, m), magic: s.magic,
+      position: s.position, offset: s.offset, disamb: s.disamb, options: s.options,
+      name: s.name.s.toLitId(m))
+
+    storeNode(p, s, ast)
+    storeNode(p, s, constraint)
+
+    if s.kind in {skLet, skVar, skField, skForVar}:
+      c.addMissing s.guard
+      p.guard = s.guard.safeItemId(c, m)
+      p.bitsize = s.bitsize
+      p.alignment = s.alignment
+
+    p.externalName = toLitId(s.loc.snippet, m)
+    p.locFlags = s.loc.flags
+    c.addMissing s.typ
+    p.typ = s.typ.storeType(c, m)
+    c.addMissing s.owner
+    p.owner = s.owner.safeItemId(c, m)
+    p.annex = toPackedLib(s.annex, c, m)
+    when hasFFI:
+      p.cname = toLitId(s.cname, m)
+    p.instantiatedFrom = s.instantiatedFrom.safeItemId(c, m)
+
+    # fill the reserved slot, nothing else:
+    m.syms[s.itemId.item] = p
+
+proc addModuleRef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule) =
+  ## add a remote symbol reference to the tree
+  let info = n.info.toPackedInfo(c, m)
+  if n.typ != n.sym.typ:
+    ir.addNode(kind = nkModuleRef, operand = 3.int32, # spans 3 nodes in total
+               info = info, flags = n.flags,
+               typeId = storeTypeLater(n.typ, c, m))
+  else:
+    ir.addNode(kind = nkModuleRef, operand = 3.int32, # spans 3 nodes in total
+              info = info, flags = n.flags)
+  ir.addNode(kind = nkNone, info = info,
+             operand = toLitId(n.sym.itemId.module.FileIndex, c, m).int32)
+  ir.addNode(kind = nkNone, info = info,
+             operand = n.sym.itemId.item)
+
+proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule) =
+  ## serialize a node into the tree
+  if n == nil:
+    ir.addNode(kind = nkNilRodNode, operand = 1, info = NoLineInfo)
+    return
+  let info = toPackedInfo(n.info, c, m)
+  case n.kind
+  of nkNone, nkEmpty, nkNilLit, nkType:
+    ir.addNode(kind = n.kind, flags = n.flags, operand = 0,
+               typeId = storeTypeLater(n.typ, c, m), info = info)
+  of nkIdent:
+    ir.addNode(kind = n.kind, flags = n.flags,
+                operand = int32 getOrIncl(m.strings, n.ident.s),
+                typeId = storeTypeLater(n.typ, c, m), info = info)
+  of nkSym:
+    if n.sym.itemId.module == c.thisModule:
+      # it is a symbol that belongs to the module we're currently
+      # packing:
+      let id = n.sym.storeSymLater(c, m).item
+      if n.typ != n.sym.typ:
+        ir.addNode(kind = nkSym, flags = n.flags, operand = id,
+                   info = info,
+                   typeId = storeTypeLater(n.typ, c, m))
+      else:
+        ir.addNode(kind = nkSym, flags = n.flags, operand = id,
+                   info = info)
+    else:
+      # store it as an external module reference:
+      addModuleRef(n, ir, c, m)
+  of externIntLit:
+    ir.addNode(kind = n.kind, flags = n.flags,
+               operand = int32 getOrIncl(m.numbers, n.intVal),
+               typeId = storeTypeLater(n.typ, c, m), info = info)
+  of nkStrLit..nkTripleStrLit:
+    ir.addNode(kind = n.kind, flags = n.flags,
+               operand = int32 getOrIncl(m.strings, n.strVal),
+               typeId = storeTypeLater(n.typ, c, m), info = info)
+  of nkFloatLit..nkFloat128Lit:
+    ir.addNode(kind = n.kind, flags = n.flags,
+               operand = int32 getOrIncl(m.numbers, cast[BiggestInt](n.floatVal)),
+               typeId = storeTypeLater(n.typ, c, m), info = info)
+  else:
+    let patchPos = ir.prepare(n.kind, n.flags,
+                              storeTypeLater(n.typ, c, m), info)
+    for i in 0..<n.len:
+      toPackedNode(n[i], ir, c, m)
+    ir.patch patchPos
+
+proc storeTypeInst*(c: var PackedEncoder; m: var PackedModule; s: PSym; inst: PType) =
+  m.typeInstCache.add (storeSymLater(s, c, m), storeTypeLater(inst, c, m))
+
+proc addPragmaComputation*(c: var PackedEncoder; m: var PackedModule; n: PNode) =
+  toPackedNode(n, m.toReplay, c, m)
+
+proc toPackedProcDef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule) =
+  let info = toPackedInfo(n.info, c, m)
+  let patchPos = ir.prepare(n.kind, n.flags,
+                            storeTypeLater(n.typ, c, m), info)
+  for i in 0..<n.len:
+    if i != bodyPos:
+      toPackedNode(n[i], ir, c, m)
+    else:
+      # do not serialize the body of the proc, it's unnecessary since
+      # n[0].sym.ast has the sem'checked variant of it which is what
+      # everybody should use instead.
+      ir.addNode(kind = nkEmpty, flags = {}, operand = 0,
+                 typeId = nilItemId, info = info)
+  ir.patch patchPos
+
+proc toPackedNodeIgnoreProcDefs(n: PNode, encoder: var PackedEncoder; m: var PackedModule) =
+  case n.kind
+  of routineDefs:
+    toPackedProcDef(n, m.topLevel, encoder, m)
+    when false:
+      # we serialize n[namePos].sym instead
+      if n[namePos].kind == nkSym:
+        let s = n[namePos].sym
+        discard storeSym(s, encoder, m)
+        if s.flags * {sfExportc, sfCompilerProc, sfCompileTime} == {sfExportc}:
+          m.exportCProcs.add(s.itemId.item)
+      else:
+        toPackedNode(n, m.topLevel, encoder, m)
+  of nkStmtList, nkStmtListExpr:
+    for it in n:
+      toPackedNodeIgnoreProcDefs(it, encoder, m)
+  of nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
+     nkFromStmt, nkIncludeStmt:
+    discard "nothing to do"
+  else:
+    toPackedNode(n, m.topLevel, encoder, m)
+
+proc toPackedNodeTopLevel*(n: PNode, encoder: var PackedEncoder; m: var PackedModule) =
+  toPackedNodeIgnoreProcDefs(n, encoder, m)
+  flush encoder, m
+
+proc toPackedGeneratedProcDef*(s: PSym, encoder: var PackedEncoder; m: var PackedModule) =
+  ## Generic procs and generated `=hook`'s need explicit top-level entries so
+  ## that the code generator can work without having to special case these. These
+  ## entries will also be useful for other tools and are the cleanest design
+  ## I can come up with.
+  assert s.kind in routineKinds
+  toPackedProcDef(s.ast, m.topLevel, encoder, m)
+  #flush encoder, m
+
+proc storeAttachedProcDef*(t: PType; op: TTypeAttachedOp; s: PSym,
+                           encoder: var PackedEncoder; m: var PackedModule) =
+  assert s.kind in routineKinds
+  assert isActive(encoder)
+  let tid = storeTypeLater(t, encoder, m)
+  let sid = storeSymLater(s, encoder, m)
+  m.attachedOps.add (tid, op, sid)
+  toPackedGeneratedProcDef(s, encoder, m)
+
+proc storeInstantiation*(c: var PackedEncoder; m: var PackedModule; s: PSym; i: PInstantiation) =
+  var t = newSeq[PackedItemId](i.concreteTypes.len)
+  for j in 0..high(i.concreteTypes):
+    t[j] = storeTypeLater(i.concreteTypes[j], c, m)
+  m.procInstCache.add PackedInstantiation(key: storeSymLater(s, c, m),
+                                          sym: storeSymLater(i.sym, c, m),
+                                          concreteTypes: t)
+  toPackedGeneratedProcDef(i.sym, c, m)
+
+proc storeExpansion*(c: var PackedEncoder; m: var PackedModule; info: TLineInfo; s: PSym) =
+  toPackedNode(newSymNode(s, info), m.bodies, c, m)
+
+proc loadError(err: RodFileError; filename: AbsoluteFile; config: ConfigRef;) =
+  case err
+  of cannotOpen:
+    rawMessage(config, warnCannotOpenFile, filename.string)
+  of includeFileChanged:
+    rawMessage(config, warnFileChanged, filename.string)
+  else:
+    rawMessage(config, warnCannotOpenFile, filename.string & " reason: " & $err)
+    #echo "Error: ", $err, " loading file: ", filename.string
+
+proc toRodFile*(conf: ConfigRef; f: AbsoluteFile; ext = RodExt): AbsoluteFile =
+  result = changeFileExt(completeGeneratedFilePath(conf,
+    mangleModuleName(conf, f).AbsoluteFile), ext)
+
+const
+  BenchIC* = false
+
+when BenchIC:
+  var gloadBodies: MonoTime
+
+  template bench(x, body) =
+    let start = getMonoTime()
+    body
+    x = x + (getMonoTime() - start)
+
+else:
+  template bench(x, body) = body
+
+proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef;
+                  ignoreConfig = false): RodFileError =
+  var f = rodfiles.open(filename.string)
+  f.loadHeader()
+  f.loadSection configSection
+
+  f.loadPrim m.definedSymbols
+  f.loadPrim m.moduleFlags
+  f.loadPrim m.cfg
+
+  if f.err == ok and not configIdentical(m, config) and not ignoreConfig:
+    f.err = configMismatch
+
+  template loadSeqSection(section, data) {.dirty.} =
+    f.loadSection section
+    f.loadSeq data
+
+  template loadTableSection(section, data) {.dirty.} =
+    f.loadSection section
+    f.loadOrderedTable data
+
+  template loadTabSection(section, data) {.dirty.} =
+    f.loadSection section
+    f.load data
+
+  loadTabSection stringsSection, m.strings
+
+  loadSeqSection checkSumsSection, m.includes
+  if config.cmd != cmdM and not includesIdentical(m, config):
+    f.err = includeFileChanged
+
+  loadSeqSection depsSection, m.imports
+
+  bench gloadBodies:
+
+    loadTabSection numbersSection, m.numbers
+
+    loadSeqSection exportsSection, m.exports
+    loadSeqSection hiddenSection, m.hidden
+    loadSeqSection reexportsSection, m.reexports
+
+    loadSeqSection compilerProcsSection, m.compilerProcs
+
+    loadSeqSection trmacrosSection, m.trmacros
+
+    loadSeqSection convertersSection, m.converters
+    loadSeqSection methodsSection, m.methods
+    loadSeqSection pureEnumsSection, m.pureEnums
+
+    loadTabSection toReplaySection, m.toReplay
+    loadTabSection topLevelSection, m.topLevel
+
+    loadTabSection bodiesSection, m.bodies
+    loadTableSection symsSection, m.syms
+    loadTableSection typesSection, m.types
+
+    loadSeqSection typeInstCacheSection, m.typeInstCache
+    loadSeqSection procInstCacheSection, m.procInstCache
+    loadSeqSection attachedOpsSection, m.attachedOps
+    loadSeqSection methodsPerGenericTypeSection, m.methodsPerGenericType
+    loadSeqSection enumToStringProcsSection, m.enumToStringProcs
+    loadSeqSection methodsPerTypeSection, m.methodsPerType
+    loadSeqSection dispatchersSection, m.dispatchers
+    loadSeqSection typeInfoSection, m.emittedTypeInfo
+
+    f.loadSection backendFlagsSection
+    f.loadPrim m.backendFlags
+
+    f.loadSection sideChannelSection
+  f.load m.man
+
+  close(f)
+  result = f.err
+
+# -------------------------------------------------------------------------
+
+proc storeError(err: RodFileError; filename: AbsoluteFile) =
+  echo "Error: ", $err, "; couldn't write to ", filename.string
+  removeFile(filename.string)
+
+proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var PackedModule) =
+  flush encoder, m
+  #rememberConfig(encoder, encoder.config)
+
+  var f = rodfiles.create(filename.string)
+  f.storeHeader()
+  f.storeSection configSection
+  f.storePrim m.definedSymbols
+  f.storePrim m.moduleFlags
+  f.storePrim m.cfg
+
+  template storeSeqSection(section, data) {.dirty.} =
+    f.storeSection section
+    f.storeSeq data
+
+  template storeTabSection(section, data) {.dirty.} =
+    f.storeSection section
+    f.store data
+
+  template storeTableSection(section, data) {.dirty.} =
+    f.storeSection section
+    f.storeOrderedTable data
+
+  storeTabSection stringsSection, m.strings
+
+  storeSeqSection checkSumsSection, m.includes
+
+  storeSeqSection depsSection, m.imports
+
+  storeTabSection numbersSection, m.numbers
+
+  storeSeqSection exportsSection, m.exports
+  storeSeqSection hiddenSection, m.hidden
+  storeSeqSection reexportsSection, m.reexports
+
+  storeSeqSection compilerProcsSection, m.compilerProcs
+
+  storeSeqSection trmacrosSection, m.trmacros
+  storeSeqSection convertersSection, m.converters
+  storeSeqSection methodsSection, m.methods
+  storeSeqSection pureEnumsSection, m.pureEnums
+
+  storeTabSection toReplaySection, m.toReplay
+  storeTabSection topLevelSection, m.topLevel
+
+  storeTabSection bodiesSection, m.bodies
+  storeTableSection symsSection, m.syms
+
+  storeTableSection typesSection, m.types
+
+  storeSeqSection typeInstCacheSection, m.typeInstCache
+  storeSeqSection procInstCacheSection, m.procInstCache
+  storeSeqSection attachedOpsSection, m.attachedOps
+  storeSeqSection methodsPerGenericTypeSection, m.methodsPerGenericType
+  storeSeqSection enumToStringProcsSection, m.enumToStringProcs
+  storeSeqSection methodsPerTypeSection, m.methodsPerType
+  storeSeqSection dispatchersSection, m.dispatchers
+  storeSeqSection typeInfoSection, m.emittedTypeInfo
+
+  f.storeSection backendFlagsSection
+  f.storePrim m.backendFlags
+
+  f.storeSection sideChannelSection
+  f.store m.man
+
+  close(f)
+  encoder.disable()
+  if f.err != ok:
+    storeError(f.err, filename)
+
+  when false:
+    # basic loader testing:
+    var m2: PackedModule
+    discard loadRodFile(filename, m2, encoder.config)
+    echo "loaded ", filename.string
+
+# ----------------------------------------------------------------------------
+
+type
+  PackedDecoder* = object
+    lastModule: int
+    lastLit: LitId
+    lastFile: FileIndex # remember the last lookup entry.
+    config*: ConfigRef
+    cache*: IdentCache
+
+type
+  ModuleStatus* = enum
+    undefined,
+    storing,  # state is strictly for stress-testing purposes
+    loading,
+    loaded,
+    outdated,
+    stored    # store is complete, no further additions possible
+
+  LoadedModule* = object
+    status*: ModuleStatus
+    symsInit, typesInit, loadedButAliveSetChanged*: bool
+    fromDisk*: PackedModule
+    syms: OrderedTable[int32, PSym] # indexed by itemId
+    types: OrderedTable[int32, PType]
+    module*: PSym # the one true module symbol.
+    iface, ifaceHidden: Table[PIdent, seq[PackedItemId]]
+      # PackedItemId so that it works with reexported symbols too
+      # ifaceHidden includes private symbols
+
+type
+  PackedModuleGraph* = object
+    pm*: seq[LoadedModule] # indexed by FileIndex
+    when BenchIC:
+      depAnalysis: MonoTime
+      loadBody: MonoTime
+      loadSym, loadType, loadBodies: MonoTime
+
+when BenchIC:
+  proc echoTimes*(m: PackedModuleGraph) =
+    echo "analysis: ", m.depAnalysis, " loadBody: ", m.loadBody, " loadSym: ",
+      m.loadSym, " loadType: ", m.loadType, " all bodies: ", gloadBodies
+
+template `[]`*(m: PackedModuleGraph; i: int): LoadedModule = m.pm[i]
+template len*(m: PackedModuleGraph): int = m.pm.len
+
+proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t: PackedItemId): PType
+proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s: PackedItemId): PSym
+
+proc toFileIndexCached*(c: var PackedDecoder; g: PackedModuleGraph; thisModule: int; f: LitId): FileIndex =
+  if f == LitId(0):
+    result = InvalidFileIdx
+  elif c.lastLit == f and c.lastModule == thisModule:
+    result = c.lastFile
+  else:
+    result = toFileIndex(f, g[thisModule].fromDisk, c.config)
+    c.lastModule = thisModule
+    c.lastLit = f
+    c.lastFile = result
+
+proc translateLineInfo(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
+                       x: PackedLineInfo): TLineInfo =
+  assert g[thisModule].status in {loaded, storing, stored}
+  let (fileId, line, col) = unpack(g[thisModule].fromDisk.man, x)
+  result = TLineInfo(line: line.uint16, col: col.int16,
+            fileIndex: toFileIndexCached(c, g, thisModule, fileId))
+
+proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
+                tree: PackedTree; n: NodePos): PNode =
+  let k = n.kind
+  if k == nkNilRodNode:
+    return nil
+  when false:
+    echo "loading node ", c.config $ translateLineInfo(c, g, thisModule, n.info)
+  result = newNodeIT(k, translateLineInfo(c, g, thisModule, n.info),
+    loadType(c, g, thisModule, n.typ))
+  result.flags = n.flags
+
+  case k
+  of nkNone, nkEmpty, nkNilLit, nkType:
+    discard
+  of nkIdent:
+    result.ident = getIdent(c.cache, g[thisModule].fromDisk.strings[n.litId])
+  of nkSym:
+    result.sym = loadSym(c, g, thisModule, PackedItemId(module: LitId(0), item: tree[n].soperand))
+    if result.typ == nil:
+      result.typ = result.sym.typ
+  of externIntLit:
+    result.intVal = g[thisModule].fromDisk.numbers[n.litId]
+  of nkStrLit..nkTripleStrLit:
+    result.strVal = g[thisModule].fromDisk.strings[n.litId]
+  of nkFloatLit..nkFloat128Lit:
+    result.floatVal = cast[BiggestFloat](g[thisModule].fromDisk.numbers[n.litId])
+  of nkModuleRef:
+    let (n1, n2) = sons2(tree, n)
+    assert n1.kind == nkNone
+    assert n2.kind == nkNone
+    transitionNoneToSym(result)
+    result.sym = loadSym(c, g, thisModule, PackedItemId(module: n1.litId, item: tree[n2].soperand))
+    if result.typ == nil:
+      result.typ = result.sym.typ
+  else:
+    for n0 in sonsReadonly(tree, n):
+      result.addAllowNil loadNodes(c, g, thisModule, tree, n0)
+
+proc initPackedDecoder*(config: ConfigRef; cache: IdentCache): PackedDecoder =
+  result = PackedDecoder(
+    lastModule: int32(-1),
+    lastLit: LitId(0),
+    lastFile: FileIndex(-1),
+    config: config,
+    cache: cache)
+
+proc loadProcHeader(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
+                    tree: PackedTree; n: NodePos): PNode =
+  # do not load the body of the proc. This will be done later in
+  # getProcBody, if required.
+  let k = n.kind
+  result = newNodeIT(k, translateLineInfo(c, g, thisModule, n.info),
+    loadType(c, g, thisModule, n.typ))
+  result.flags = n.flags
+  assert k in {nkProcDef, nkMethodDef, nkIteratorDef, nkFuncDef, nkConverterDef, nkLambda}
+  var i = 0
+  for n0 in sonsReadonly(tree, n):
+    if i != bodyPos:
+      result.add loadNodes(c, g, thisModule, tree, n0)
+    else:
+      result.addAllowNil nil
+    inc i
+
+proc loadProcBody(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
+                  tree: PackedTree; n: NodePos): PNode =
+  result = nil
+  var i = 0
+  for n0 in sonsReadonly(tree, n):
+    if i == bodyPos:
+      result = loadNodes(c, g, thisModule, tree, n0)
+    inc i
+
+proc moduleIndex*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
+                  s: PackedItemId): int32 {.inline.} =
+  result = if s.module == LitId(0): thisModule.int32
+           else: toFileIndexCached(c, g, thisModule, s.module).int32
+
+proc symHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
+                         s: PackedSym; si, item: int32): PSym =
+  result = PSym(itemId: ItemId(module: si, item: item),
+    kind: s.kind, magic: s.magic, flags: s.flags,
+    info: translateLineInfo(c, g, si, s.info),
+    options: s.options,
+    position: if s.kind in {skForVar, skVar, skLet, skTemp}: 0 else: s.position,
+    offset: if s.kind in routineKinds: defaultOffset else: s.offset,
+    disamb: s.disamb,
+    name: getIdent(c.cache, g[si].fromDisk.strings[s.name])
+  )
+
+template loadAstBody(p, field) =
+  if p.field != emptyNodeId:
+    result.field = loadNodes(c, g, si, g[si].fromDisk.bodies, NodePos p.field)
+
+template loadAstBodyLazy(p, field) =
+  if p.field != emptyNodeId:
+    result.field = loadProcHeader(c, g, si, g[si].fromDisk.bodies, NodePos p.field)
+
+proc loadLib(c: var PackedDecoder; g: var PackedModuleGraph;
+             si, item: int32; l: PackedLib): PLib =
+  # XXX: hack; assume a zero LitId means the PackedLib is all zero (empty)
+  if l.name.int == 0:
+    result = nil
+  else:
+    result = PLib(generated: l.generated, isOverridden: l.isOverridden,
+                  kind: l.kind, name: rope g[si].fromDisk.strings[l.name])
+    loadAstBody(l, path)
+
+proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
+                       s: PackedSym; si, item: int32; result: PSym) =
+  result.typ = loadType(c, g, si, s.typ)
+  loadAstBody(s, constraint)
+  if result.kind in {skProc, skFunc, skIterator, skConverter, skMethod}:
+    loadAstBodyLazy(s, ast)
+  else:
+    loadAstBody(s, ast)
+  result.annex = loadLib(c, g, si, item, s.annex)
+  when hasFFI:
+    result.cname = g[si].fromDisk.strings[s.cname]
+
+  if s.kind in {skLet, skVar, skField, skForVar}:
+    result.guard = loadSym(c, g, si, s.guard)
+    result.bitsize = s.bitsize
+    result.alignment = s.alignment
+  result.owner = loadSym(c, g, si, s.owner)
+  let externalName = g[si].fromDisk.strings[s.externalName]
+  if externalName != "":
+    result.loc.snippet = externalName
+  result.loc.flags = s.locFlags
+  result.instantiatedFrom = loadSym(c, g, si, s.instantiatedFrom)
+
+proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
+                    fileIdx: FileIndex; cachedModules: var seq[FileIndex]): bool
+proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
+                       fileIdx: FileIndex; m: var LoadedModule)
+
+proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s: PackedItemId): PSym =
+  if s == nilItemId:
+    result = nil
+  else:
+    let si = moduleIndex(c, g, thisModule, s)
+    if si >= g.len:
+      g.pm.setLen(si+1)
+
+    if g[si].status == undefined and c.config.cmd == cmdM:
+      var cachedModules: seq[FileIndex] = @[]
+      discard needsRecompile(g, c.config, c.cache, FileIndex(si), cachedModules)
+      for m in cachedModules:
+        loadToReplayNodes(g, c.config, c.cache, m, g[int m])
+
+    assert g[si].status in {loaded, storing, stored}
+    #if not g[si].symsInit:
+    #  g[si].symsInit = true
+    #  setLen g[si].syms, g[si].fromDisk.syms.len
+
+    if g[si].syms.getOrDefault(s.item) == nil:
+      if g[si].fromDisk.syms[s.item].kind != skModule:
+        result = symHeaderFromPacked(c, g, g[si].fromDisk.syms[s.item], si, s.item)
+        # store it here early on, so that recursions work properly:
+        g[si].syms[s.item] = result
+        symBodyFromPacked(c, g, g[si].fromDisk.syms[s.item], si, s.item, result)
+      else:
+        result = g[si].module
+        assert result != nil
+        g[si].syms[s.item] = result
+
+    else:
+      result = g[si].syms[s.item]
+
+proc typeHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
+                          t: PackedType; si, item: int32): PType =
+  result = PType(itemId: ItemId(module: si, item: t.nonUniqueId), kind: t.kind,
+                flags: t.flags, size: t.size, align: t.align,
+                paddingAtEnd: t.paddingAtEnd,
+                uniqueId: ItemId(module: si, item: item),
+                callConv: t.callConv)
+
+proc typeBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
+                        t: PackedType; si, item: int32; result: PType) =
+  result.sym = loadSym(c, g, si, t.sym)
+  result.owner = loadSym(c, g, si, t.owner)
+  when false:
+    for op, item in pairs t.attachedOps:
+      result.attachedOps[op] = loadSym(c, g, si, item)
+  result.typeInst = loadType(c, g, si, t.typeInst)
+  var sons = newSeq[PType]()
+  for son in items t.types:
+    sons.add loadType(c, g, si, son)
+  result.setSons(sons)
+  loadAstBody(t, n)
+  when false:
+    for gen, id in items t.methods:
+      result.methods.add((gen, loadSym(c, g, si, id)))
+
+proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t: PackedItemId): PType =
+  if t == nilItemId:
+    result = nil
+  else:
+    let si = moduleIndex(c, g, thisModule, t)
+    assert g[si].status in {loaded, storing, stored}
+    assert t.item > 0
+
+    #if not g[si].typesInit:
+    #  g[si].typesInit = true
+    #  setLen g[si].types, g[si].fromDisk.types.len
+
+    if g[si].types.getOrDefault(t.item) == nil:
+      result = typeHeaderFromPacked(c, g, g[si].fromDisk.types[t.item], si, t.item)
+      # store it here early on, so that recursions work properly:
+      g[si].types[t.item] = result
+      typeBodyFromPacked(c, g, g[si].fromDisk.types[t.item], si, t.item, result)
+      #assert result.itemId.item == t.item, $(result.itemId.item, t.item)
+      assert result.itemId.item > 0, $(result.itemId.item, t.item)
+    else:
+      result = g[si].types[t.item]
+      assert result.itemId.item > 0, "2"
+
+proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
+                       fileIdx: FileIndex; m: var LoadedModule) =
+  m.iface = initTable[PIdent, seq[PackedItemId]]()
+  m.ifaceHidden = initTable[PIdent, seq[PackedItemId]]()
+  template impl(iface, e) =
+    let nameLit = e[0]
+    let e2 =
+      when e[1] is PackedItemId: e[1]
+      else: PackedItemId(module: LitId(0), item: e[1])
+    iface.mgetOrPut(cache.getIdent(m.fromDisk.strings[nameLit]), @[]).add(e2)
+
+  for e in m.fromDisk.exports:
+    m.iface.impl(e)
+    m.ifaceHidden.impl(e)
+  for e in m.fromDisk.reexports:
+    m.iface.impl(e)
+    m.ifaceHidden.impl(e)
+  for e in m.fromDisk.hidden:
+    m.ifaceHidden.impl(e)
+
+  let filename = AbsoluteFile toFullPath(conf, fileIdx)
+  # We cannot call ``newSym`` here, because we have to circumvent the ID
+  # mechanism, which we do in order to assign each module a persistent ID.
+  m.module = PSym(kind: skModule, itemId: ItemId(module: int32(fileIdx), item: 0'i32),
+                  name: getIdent(cache, splitFile(filename).name),
+                  info: newLineInfo(fileIdx, 1, 1),
+                  position: int(fileIdx))
+  m.module.owner = getPackage(conf, cache, fileIdx)
+  m.module.flags = m.fromDisk.moduleFlags
+
+proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
+                       fileIdx: FileIndex; m: var LoadedModule) =
+  m.module.ast = newNode(nkStmtList)
+  if m.fromDisk.toReplay.len > 0:
+    var decoder = PackedDecoder(
+      lastModule: int32(-1),
+      lastLit: LitId(0),
+      lastFile: FileIndex(-1),
+      config: conf,
+      cache: cache)
+    for p in allNodes(m.fromDisk.toReplay):
+      m.module.ast.add loadNodes(decoder, g, int(fileIdx), m.fromDisk.toReplay, p)
+
+proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
+                    fileIdx: FileIndex; cachedModules: var seq[FileIndex]): bool =
+  # Does the file belong to the fileIdx need to be recompiled?
+  let m = int(fileIdx)
+  if m >= g.len:
+    g.pm.setLen(m+1)
+
+  case g[m].status
+  of undefined:
+    g[m].status = loading
+    let fullpath = msgs.toFullPath(conf, fileIdx)
+    let rod = toRodFile(conf, AbsoluteFile fullpath)
+    let err = loadRodFile(rod, g[m].fromDisk, conf, ignoreConfig = conf.cmd == cmdM)
+    if err == ok:
+      if conf.cmd == cmdM:
+        setupLookupTables(g, conf, cache, fileIdx, g[m])
+        cachedModules.add fileIdx
+        g[m].status = loaded
+        result = false
+      else:
+        result = optForceFullMake in conf.globalOptions
+        # check its dependencies:
+        let imp = g[m].fromDisk.imports
+        for dep in imp:
+          let fid = toFileIndex(dep, g[m].fromDisk, conf)
+          # Warning: we need to traverse the full graph, so
+          # do **not use break here**!
+          if needsRecompile(g, conf, cache, fid, cachedModules):
+            result = true
+
+        if not result:
+          setupLookupTables(g, conf, cache, fileIdx, g[m])
+          cachedModules.add fileIdx
+          g[m].status = loaded
+        else:
+          g.pm[m] = LoadedModule(status: outdated, module: g[m].module)
+    else:
+      loadError(err, rod, conf)
+      g[m].status = outdated
+      result = true
+    when false: loadError(err, rod, conf)
+  of loading, loaded:
+    # For loading: Assume no recompile is required.
+    result = false
+  of outdated, storing, stored:
+    result = true
+
+proc moduleFromRodFile*(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
+                        fileIdx: FileIndex; cachedModules: var seq[FileIndex]): PSym =
+  ## Returns 'nil' if the module needs to be recompiled.
+  bench g.depAnalysis:
+    if needsRecompile(g, conf, cache, fileIdx, cachedModules):
+      result = nil
+    else:
+      result = g[int fileIdx].module
+      assert result != nil
+      assert result.position == int(fileIdx)
+  for m in cachedModules:
+    loadToReplayNodes(g, conf, cache, m, g[int m])
+
+template setupDecoder() {.dirty.} =
+  var decoder = PackedDecoder(
+    lastModule: int32(-1),
+    lastLit: LitId(0),
+    lastFile: FileIndex(-1),
+    config: config,
+    cache: cache)
+
+proc loadProcBody*(config: ConfigRef, cache: IdentCache;
+                   g: var PackedModuleGraph; s: PSym): PNode =
+  bench g.loadBody:
+    let mId = s.itemId.module
+    var decoder = PackedDecoder(
+      lastModule: int32(-1),
+      lastLit: LitId(0),
+      lastFile: FileIndex(-1),
+      config: config,
+      cache: cache)
+    let pos = g[mId].fromDisk.syms[s.itemId.item].ast
+    assert pos != emptyNodeId
+    result = loadProcBody(decoder, g, mId, g[mId].fromDisk.bodies, NodePos pos)
+
+proc loadTypeFromId*(config: ConfigRef, cache: IdentCache;
+                     g: var PackedModuleGraph; module: int; id: PackedItemId): PType =
+  bench g.loadType:
+    result = g[module].types.getOrDefault(id.item)
+    if result == nil:
+      var decoder = PackedDecoder(
+        lastModule: int32(-1),
+        lastLit: LitId(0),
+        lastFile: FileIndex(-1),
+        config: config,
+        cache: cache)
+      result = loadType(decoder, g, module, id)
+
+proc loadSymFromId*(config: ConfigRef, cache: IdentCache;
+                    g: var PackedModuleGraph; module: int; id: PackedItemId): PSym =
+  bench g.loadSym:
+    result = g[module].syms.getOrDefault(id.item)
+    if result == nil:
+      var decoder = PackedDecoder(
+        lastModule: int32(-1),
+        lastLit: LitId(0),
+        lastFile: FileIndex(-1),
+        config: config,
+        cache: cache)
+      result = loadSym(decoder, g, module, id)
+
+proc translateId*(id: PackedItemId; g: PackedModuleGraph; thisModule: int; config: ConfigRef): ItemId =
+  if id.module == LitId(0):
+    ItemId(module: thisModule.int32, item: id.item)
+  else:
+    ItemId(module: toFileIndex(id.module, g[thisModule].fromDisk, config).int32, item: id.item)
+
+proc simulateLoadedModule*(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
+                           moduleSym: PSym; m: PackedModule) =
+  # For now only used for heavy debugging. In the future we could use this to reduce the
+  # compiler's memory consumption.
+  let idx = moduleSym.position
+  assert g[idx].status in {storing}
+  g[idx].status = loaded
+  assert g[idx].module == moduleSym
+  setupLookupTables(g, conf, cache, FileIndex(idx), g[idx])
+  loadToReplayNodes(g, conf, cache, FileIndex(idx), g[idx])
+
+# ---------------- symbol table handling ----------------
+
+type
+  RodIter* = object
+    decoder: PackedDecoder
+    values: seq[PackedItemId]
+    i, module: int
+
+template interfSelect(a: LoadedModule, importHidden: bool): auto =
+  var ret = a.iface.addr
+  if importHidden: ret = a.ifaceHidden.addr
+  ret[]
+
+proc initRodIter*(it: var RodIter; config: ConfigRef, cache: IdentCache;
+                  g: var PackedModuleGraph; module: FileIndex;
+                  name: PIdent, importHidden: bool): PSym =
+  it.decoder = PackedDecoder(
+    lastModule: int32(-1),
+    lastLit: LitId(0),
+    lastFile: FileIndex(-1),
+    config: config,
+    cache: cache)
+  it.values = g[int module].interfSelect(importHidden).getOrDefault(name)
+  it.i = 0
+  it.module = int(module)
+  if it.i < it.values.len:
+    result = loadSym(it.decoder, g, int(module), it.values[it.i])
+    inc it.i
+  else:
+    result = nil
+
+proc initRodIterAllSyms*(it: var RodIter; config: ConfigRef, cache: IdentCache;
+                         g: var PackedModuleGraph; module: FileIndex; importHidden: bool): PSym =
+  it.decoder = PackedDecoder(
+    lastModule: int32(-1),
+    lastLit: LitId(0),
+    lastFile: FileIndex(-1),
+    config: config,
+    cache: cache)
+  it.values = @[]
+  it.module = int(module)
+  for v in g[int module].interfSelect(importHidden).values:
+    it.values.add v
+  it.i = 0
+  if it.i < it.values.len:
+    result = loadSym(it.decoder, g, int(module), it.values[it.i])
+    inc it.i
+  else:
+    result = nil
+
+proc nextRodIter*(it: var RodIter; g: var PackedModuleGraph): PSym =
+  if it.i < it.values.len:
+    result = loadSym(it.decoder, g, it.module, it.values[it.i])
+    inc it.i
+  else:
+    result = nil
+
+iterator interfaceSymbols*(config: ConfigRef, cache: IdentCache;
+                           g: var PackedModuleGraph; module: FileIndex;
+                           name: PIdent, importHidden: bool): PSym =
+  setupDecoder()
+  let values = g[int module].interfSelect(importHidden).getOrDefault(name)
+  for pid in values:
+    let s = loadSym(decoder, g, int(module), pid)
+    assert s != nil
+    yield s
+
+proc interfaceSymbol*(config: ConfigRef, cache: IdentCache;
+                      g: var PackedModuleGraph; module: FileIndex;
+                      name: PIdent, importHidden: bool): PSym =
+  setupDecoder()
+  let values = g[int module].interfSelect(importHidden).getOrDefault(name)
+  result = loadSym(decoder, g, int(module), values[0])
+
+proc idgenFromLoadedModule*(m: LoadedModule): IdGenerator =
+  IdGenerator(module: m.module.itemId.module, symId: int32 m.fromDisk.syms.len,
+              typeId: int32 m.fromDisk.types.len)
+
+proc searchForCompilerproc*(m: LoadedModule; name: string): int32 =
+  # slow, linear search, but the results are cached:
+  for it in items(m.fromDisk.compilerProcs):
+    if m.fromDisk.strings[it[0]] == name:
+      return it[1]
+  return -1
+
+# ------------------------- .rod file viewer ---------------------------------
+
+proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) =
+  var m: PackedModule = PackedModule()
+  let err = loadRodFile(rodfile, m, config, ignoreConfig=true)
+  if err != ok:
+    config.quitOrRaise "Error: could not load: " & $rodfile.string & " reason: " & $err
+
+  when false:
+    echo "exports:"
+    for ex in m.exports:
+      echo "  ", m.strings[ex[0]], " local ID: ", ex[1]
+      assert ex[0] == m.syms[ex[1]].name
+      # ex[1] int32
+
+    echo "reexports:"
+    for ex in m.reexports:
+      echo "  ", m.strings[ex[0]]
+    #  reexports*: seq[(LitId, PackedItemId)]
+
+    echo "hidden: " & $m.hidden.len
+    for ex in m.hidden:
+      echo "  ", m.strings[ex[0]], " local ID: ", ex[1]
+
+  when false:
+    echo "all symbols"
+    for i in 0..high(m.syms):
+      if m.syms[i].name != LitId(0):
+        echo "  ", m.strings[m.syms[i].name], " local ID: ", i, " kind ", m.syms[i].kind
+      else:
+        echo "  <anon symbol?> local ID: ", i, " kind ", m.syms[i].kind
+
+  echo "symbols: ", m.syms.len, " types: ", m.types.len,
+    " top level nodes: ", m.topLevel.len, " other nodes: ", m.bodies.len,
+    " strings: ", m.strings.len, " numbers: ", m.numbers.len
+
+  echo "SIZES:"
+  echo "symbols: ", m.syms.len * sizeof(PackedSym), " types: ", m.types.len * sizeof(PackedType),
+    " top level nodes: ", m.topLevel.len * sizeof(PackedNode),
+    " other nodes: ", m.bodies.len * sizeof(PackedNode),
+    " strings: ", sizeOnDisc(m.strings)
+  when false:
+    var tt = 0
+    var fc = 0
+    for x in m.topLevel:
+      if x.kind == nkSym or x.typeId == nilItemId: inc tt
+      if x.flags == {}: inc fc
+    for x in m.bodies:
+      if x.kind == nkSym or x.typeId == nilItemId: inc tt
+      if x.flags == {}: inc fc
+    let total = float(m.topLevel.len + m.bodies.len)
+    echo "nodes with nil type: ", tt, " in % ", tt.float / total
+    echo "nodes with empty flags: ", fc.float / total
diff --git a/compiler/ic/iclineinfos.nim b/compiler/ic/iclineinfos.nim
new file mode 100644
index 000000000..74a7d971b
--- /dev/null
+++ b/compiler/ic/iclineinfos.nim
@@ -0,0 +1,84 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2024 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# For the line information we use 32 bits. They are used as follows:
+# Bit 0 (AsideBit): If we have inline line information or not. If not, the
+# remaining 31 bits are used as an index into a seq[(LitId, int, int)].
+#
+# We use 10 bits for the "file ID", this means a program can consist of as much
+# as 1024 different files. (If it uses more files than that, the overflow bit
+# would be set.)
+# This means we have 21 bits left to encode the (line, col) pair. We use 7 bits for the column
+# so 128 is the limit and 14 bits for the line number.
+# The packed representation supports files with up to 16384 lines.
+# Keep in mind that whenever any limit is reached the AsideBit is set and the real line
+# information is kept in a side channel.
+
+import std / assertions
+
+const
+  AsideBit = 1
+  FileBits = 10
+  LineBits = 14
+  ColBits = 7
+  FileMax = (1 shl FileBits) - 1
+  LineMax = (1 shl LineBits) - 1
+  ColMax = (1 shl ColBits) - 1
+
+static:
+  assert AsideBit + FileBits + LineBits + ColBits == 32
+
+import .. / ic / [bitabs, rodfiles] # for LitId
+
+type
+  PackedLineInfo* = distinct uint32
+
+  LineInfoManager* = object
+    aside: seq[(LitId, int32, int32)]
+
+const
+  NoLineInfo* = PackedLineInfo(0'u32)
+
+proc pack*(m: var LineInfoManager; file: LitId; line, col: int32): PackedLineInfo =
+  if file.uint32 <= FileMax.uint32 and line <= LineMax and col <= ColMax:
+    let col = if col < 0'i32: 0'u32 else: col.uint32
+    let line = if line < 0'i32: 0'u32 else: line.uint32
+    # use inline representation:
+    result = PackedLineInfo((file.uint32 shl 1'u32) or (line shl uint32(AsideBit + FileBits)) or
+      (col shl uint32(AsideBit + FileBits + LineBits)))
+  else:
+    result = PackedLineInfo((m.aside.len shl 1) or AsideBit)
+    m.aside.add (file, line, col)
+
+proc unpack*(m: LineInfoManager; i: PackedLineInfo): (LitId, int32, int32) =
+  let i = i.uint32
+  if (i and 1'u32) == 0'u32:
+    # inline representation:
+    result = (LitId((i shr 1'u32) and FileMax.uint32),
+      int32((i shr uint32(AsideBit + FileBits)) and LineMax.uint32),
+      int32((i shr uint32(AsideBit + FileBits + LineBits)) and ColMax.uint32))
+  else:
+    result = m.aside[int(i shr 1'u32)]
+
+proc getFileId*(m: LineInfoManager; i: PackedLineInfo): LitId =
+  result = unpack(m, i)[0]
+
+proc store*(r: var RodFile; m: LineInfoManager) = storeSeq(r, m.aside)
+proc load*(r: var RodFile; m: var LineInfoManager) = loadSeq(r, m.aside)
+
+when isMainModule:
+  var m = LineInfoManager(aside: @[])
+  for i in 0'i32..<16388'i32:
+    for col in 0'i32..<100'i32:
+      let packed = pack(m, LitId(1023), i, col)
+      let u = unpack(m, packed)
+      assert u[0] == LitId(1023)
+      assert u[1] == i
+      assert u[2] == col
+  echo m.aside.len
diff --git a/compiler/ic/integrity.nim b/compiler/ic/integrity.nim
new file mode 100644
index 000000000..3e8ea2503
--- /dev/null
+++ b/compiler/ic/integrity.nim
@@ -0,0 +1,155 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2021 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Integrity checking for a set of .rod files.
+## The set must cover a complete Nim project.
+
+import std/[sets, tables]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+import ".." / [ast, modulegraphs]
+import packed_ast, bitabs, ic
+
+type
+  CheckedContext = object
+    g: ModuleGraph
+    thisModule: int32
+    checkedSyms: HashSet[ItemId]
+    checkedTypes: HashSet[ItemId]
+
+proc checkType(c: var CheckedContext; typeId: PackedItemId)
+proc checkForeignSym(c: var CheckedContext; symId: PackedItemId)
+proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos)
+
+proc checkTypeObj(c: var CheckedContext; typ: PackedType) =
+  for child in typ.types:
+    checkType(c, child)
+  if typ.n != emptyNodeId:
+    checkNode(c, c.g.packed[c.thisModule].fromDisk.bodies, NodePos typ.n)
+  if typ.sym != nilItemId:
+    checkForeignSym(c, typ.sym)
+  if typ.owner != nilItemId:
+    checkForeignSym(c, typ.owner)
+  checkType(c, typ.typeInst)
+
+proc checkType(c: var CheckedContext; typeId: PackedItemId) =
+  if typeId == nilItemId: return
+  let itemId = translateId(typeId, c.g.packed, c.thisModule, c.g.config)
+  if not c.checkedTypes.containsOrIncl(itemId):
+    let oldThisModule = c.thisModule
+    c.thisModule = itemId.module
+    checkTypeObj c, c.g.packed[itemId.module].fromDisk.types[itemId.item]
+    c.thisModule = oldThisModule
+
+proc checkSym(c: var CheckedContext; s: PackedSym) =
+  if s.name != LitId(0):
+    assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId s.name
+  checkType c, s.typ
+  if s.ast != emptyNodeId:
+    checkNode(c, c.g.packed[c.thisModule].fromDisk.bodies, NodePos s.ast)
+  if s.owner != nilItemId:
+    checkForeignSym(c, s.owner)
+
+proc checkLocalSym(c: var CheckedContext; item: int32) =
+  let itemId = ItemId(module: c.thisModule, item: item)
+  if not c.checkedSyms.containsOrIncl(itemId):
+    checkSym c, c.g.packed[c.thisModule].fromDisk.syms[item]
+
+proc checkForeignSym(c: var CheckedContext; symId: PackedItemId) =
+  let itemId = translateId(symId, c.g.packed, c.thisModule, c.g.config)
+  if not c.checkedSyms.containsOrIncl(itemId):
+    let oldThisModule = c.thisModule
+    c.thisModule = itemId.module
+    checkSym c, c.g.packed[itemId.module].fromDisk.syms[itemId.item]
+    c.thisModule = oldThisModule
+
+proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos) =
+  let t = findType(tree, n)
+  if t != nilItemId:
+    checkType(c, t)
+  case n.kind
+  of nkEmpty, nkNilLit, nkType, nkNilRodNode:
+    discard
+  of nkIdent:
+    assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId
+  of nkSym:
+    checkLocalSym(c, tree[n].soperand)
+  of directIntLit:
+    discard
+  of externIntLit, nkFloatLit..nkFloat128Lit:
+    assert c.g.packed[c.thisModule].fromDisk.numbers.hasLitId n.litId
+  of nkStrLit..nkTripleStrLit:
+    assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId
+  of nkModuleRef:
+    let (n1, n2) = sons2(tree, n)
+    assert n1.kind == nkNone
+    assert n2.kind == nkNone
+    checkForeignSym(c, PackedItemId(module: n1.litId, item: tree[n2].soperand))
+  else:
+    for n0 in sonsReadonly(tree, n):
+      checkNode(c, tree, n0)
+
+proc checkTree(c: var CheckedContext; t: PackedTree) =
+  for p in allNodes(t): checkNode(c, t, p)
+
+proc checkLocalSymIds(c: var CheckedContext; m: PackedModule; symIds: seq[int32]) =
+  for symId in symIds:
+    assert symId >= 0 and symId < m.syms.len, $symId & " " & $m.syms.len
+
+proc checkModule(c: var CheckedContext; m: PackedModule) =
+  # We check that:
+  # - Every symbol references existing types and symbols.
+  # - Every tree node references existing types and symbols.
+  for _, v in pairs(m.syms):
+    checkLocalSym c, v.id
+
+  checkTree c, m.toReplay
+  checkTree c, m.topLevel
+
+  for e in m.exports:
+    #assert e[1] >= 0 and e[1] < m.syms.len
+    assert e[0] == m.syms[e[1]].name
+
+  for e in m.compilerProcs:
+    #assert e[1] >= 0 and e[1] < m.syms.len
+    assert e[0] == m.syms[e[1]].name
+
+  checkLocalSymIds c, m, m.converters
+  checkLocalSymIds c, m, m.methods
+  checkLocalSymIds c, m, m.trmacros
+  checkLocalSymIds c, m, m.pureEnums
+  #[
+    To do: Check all these fields:
+
+    reexports*: seq[(LitId, PackedItemId)]
+    macroUsages*: seq[(PackedItemId, PackedLineInfo)]
+
+    typeInstCache*: seq[(PackedItemId, PackedItemId)]
+    procInstCache*: seq[PackedInstantiation]
+    attachedOps*: seq[(TTypeAttachedOp, PackedItemId, PackedItemId)]
+    methodsPerGenericType*: seq[(PackedItemId, int, PackedItemId)]
+    enumToStringProcs*: seq[(PackedItemId, PackedItemId)]
+    methodsPerType*: seq[(PackedItemId, PackedItemId)]
+    dispatchers*: seq[PackedItemId]
+  ]#
+
+proc checkIntegrity*(g: ModuleGraph) =
+  var c = CheckedContext(g: g)
+  for i in 0..<len(g.packed):
+    # case statement here to enforce exhaustive checks.
+    case g.packed[i].status
+    of undefined:
+      discard "nothing to do"
+    of loading:
+      assert false, "cannot check integrity: Module still loading"
+    of stored, storing, outdated, loaded:
+      c.thisModule = int32 i
+      checkModule(c, g.packed[i].fromDisk)
diff --git a/compiler/ic/navigator.nim b/compiler/ic/navigator.nim
new file mode 100644
index 000000000..39037b94f
--- /dev/null
+++ b/compiler/ic/navigator.nim
@@ -0,0 +1,183 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2021 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Supports the "nim check --ic:on --defusages:FILE,LINE,COL"
+## IDE-like features. It uses the set of .rod files to accomplish
+## its task. The set must cover a complete Nim project.
+
+import std/[sets, tables]
+
+from std/os import nil
+from std/private/miscdollars import toLocation
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+import ".." / [ast, modulegraphs, msgs, options]
+import iclineinfos
+import packed_ast, bitabs, ic
+
+type
+  UnpackedLineInfo = object
+    file: LitId
+    line, col: int
+  NavContext = object
+    g: ModuleGraph
+    thisModule: int32
+    trackPos: UnpackedLineInfo
+    alreadyEmitted: HashSet[string]
+    outputSep: char # for easier testing, use short filenames and spaces instead of tabs.
+
+proc isTracked(man: LineInfoManager; current: PackedLineInfo, trackPos: UnpackedLineInfo, tokenLen: int): bool =
+  let (currentFile, currentLine, currentCol) = man.unpack(current)
+  if currentFile == trackPos.file and currentLine == trackPos.line:
+    let col = trackPos.col
+    if col >= currentCol and col < currentCol+tokenLen:
+      result = true
+    else:
+      result = false
+  else:
+    result = false
+
+proc searchLocalSym(c: var NavContext; s: PackedSym; info: PackedLineInfo): bool =
+  result = s.name != LitId(0) and
+    isTracked(c.g.packed[c.thisModule].fromDisk.man, info, c.trackPos, c.g.packed[c.thisModule].fromDisk.strings[s.name].len)
+
+proc searchForeignSym(c: var NavContext; s: ItemId; info: PackedLineInfo): bool =
+  let name = c.g.packed[s.module].fromDisk.syms[s.item].name
+  result = name != LitId(0) and
+    isTracked(c.g.packed[c.thisModule].fromDisk.man, info, c.trackPos, c.g.packed[s.module].fromDisk.strings[name].len)
+
+const
+  EmptyItemId = ItemId(module: -1'i32, item: -1'i32)
+
+proc search(c: var NavContext; tree: PackedTree): ItemId =
+  # We use the linear representation here directly:
+  for i in 0..<len(tree):
+    let i = NodePos(i)
+    case tree[i].kind
+    of nkSym:
+      let item = tree[i].soperand
+      if searchLocalSym(c, c.g.packed[c.thisModule].fromDisk.syms[item], tree[i].info):
+        return ItemId(module: c.thisModule, item: item)
+    of nkModuleRef:
+      let (currentFile, currentLine, currentCol) = c.g.packed[c.thisModule].fromDisk.man.unpack(tree[i].info)
+      if currentLine == c.trackPos.line and currentFile == c.trackPos.file:
+        let (n1, n2) = sons2(tree, i)
+        assert n1.kind == nkInt32Lit
+        assert n2.kind == nkInt32Lit
+        let pId = PackedItemId(module: n1.litId, item: tree[n2].soperand)
+        let itemId = translateId(pId, c.g.packed, c.thisModule, c.g.config)
+        if searchForeignSym(c, itemId, tree[i].info):
+          return itemId
+    else: discard
+  return EmptyItemId
+
+proc isDecl(tree: PackedTree; n: NodePos): bool =
+  # XXX This is not correct yet.
+  const declarativeNodes = procDefs + {nkMacroDef, nkTemplateDef,
+    nkLetSection, nkVarSection, nkUsingStmt, nkConstSection, nkTypeSection,
+    nkIdentDefs, nkEnumTy, nkVarTuple}
+  result = n.int >= 0 and tree[n].kind in declarativeNodes
+
+proc usage(c: var NavContext; info: PackedLineInfo; isDecl: bool) =
+  let (fileId, line, col) = unpack(c.g.packed[c.thisModule].fromDisk.man, info)
+  var m = ""
+  var file = c.g.packed[c.thisModule].fromDisk.strings[fileId]
+  if c.outputSep == ' ':
+    file = os.extractFilename file
+  toLocation(m, file, line, col + ColOffset)
+  if not c.alreadyEmitted.containsOrIncl(m):
+    msgWriteln c.g.config, (if isDecl: "def" else: "usage") & c.outputSep & m
+
+proc list(c: var NavContext; tree: PackedTree; sym: ItemId) =
+  for i in 0..<len(tree):
+    let i = NodePos(i)
+    case tree[i].kind
+    of nkSym:
+      let item = tree[i].soperand
+      if sym.item == item and sym.module == c.thisModule:
+        usage(c, tree[i].info, isDecl(tree, parent(i)))
+    of nkModuleRef:
+      let (n1, n2) = sons2(tree, i)
+      assert n1.kind == nkNone
+      assert n2.kind == nkNone
+      let pId = PackedItemId(module: n1.litId, item: tree[n2].soperand)
+      let itemId = translateId(pId, c.g.packed, c.thisModule, c.g.config)
+      if itemId.item == sym.item and sym.module == itemId.module:
+        usage(c, tree[i].info, isDecl(tree, parent(i)))
+    else: discard
+
+proc searchForIncludeFile(g: ModuleGraph; fullPath: string): int =
+  for i in 0..<len(g.packed):
+    for k in 1..high(g.packed[i].fromDisk.includes):
+      # we start from 1 because the first "include" file is
+      # the module's filename.
+      if os.cmpPaths(g.packed[i].fromDisk.strings[g.packed[i].fromDisk.includes[k][0]], fullPath) == 0:
+        return i
+  return -1
+
+proc nav(g: ModuleGraph) =
+  # translate the track position to a packed position:
+  let unpacked = g.config.m.trackPos
+  var mid = unpacked.fileIndex.int
+
+  let fullPath = toFullPath(g.config, unpacked.fileIndex)
+
+  if g.packed[mid].status == undefined:
+    # check if 'mid' is an include file of some other module:
+    mid = searchForIncludeFile(g, fullPath)
+
+  if mid < 0:
+    localError(g.config, unpacked, "unknown file name: " & fullPath)
+    return
+
+  let fileId = g.packed[mid].fromDisk.strings.getKeyId(fullPath)
+
+  if fileId == LitId(0):
+    internalError(g.config, unpacked, "cannot find a valid file ID")
+    return
+
+  var c = NavContext(
+    g: g,
+    thisModule: int32 mid,
+    trackPos: UnpackedLineInfo(line: unpacked.line.int, col: unpacked.col.int, file: fileId),
+    outputSep: if isDefined(g.config, "nimIcNavigatorTests"): ' ' else: '\t'
+  )
+  var symId = search(c, g.packed[mid].fromDisk.topLevel)
+  if symId == EmptyItemId:
+    symId = search(c, g.packed[mid].fromDisk.bodies)
+
+  if symId == EmptyItemId:
+    localError(g.config, unpacked, "no symbol at this position")
+    return
+
+  for i in 0..<len(g.packed):
+    # case statement here to enforce exhaustive checks.
+    case g.packed[i].status
+    of undefined:
+      discard "nothing to do"
+    of loading:
+      assert false, "cannot check integrity: Module still loading"
+    of stored, storing, outdated, loaded:
+      c.thisModule = int32 i
+      list(c, g.packed[i].fromDisk.topLevel, symId)
+      list(c, g.packed[i].fromDisk.bodies, symId)
+
+proc navDefinition*(g: ModuleGraph) = nav(g)
+proc navUsages*(g: ModuleGraph) = nav(g)
+proc navDefusages*(g: ModuleGraph) = nav(g)
+
+proc writeRodFiles*(g: ModuleGraph) =
+  for i in 0..<len(g.packed):
+    case g.packed[i].status
+    of undefined, loading, stored, loaded:
+      discard "nothing to do"
+    of storing, outdated:
+      closeRodFile(g, g.packed[i].module)
diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim
new file mode 100644
index 000000000..a39bb7adf
--- /dev/null
+++ b/compiler/ic/packed_ast.nim
@@ -0,0 +1,367 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Packed AST representation, mostly based on a seq of nodes.
+## For IC support. Far future: Rewrite the compiler passes to
+## use this representation directly in all the transformations,
+## it is superior.
+
+import std/[hashes, tables, strtabs]
+import bitabs, rodfiles
+import ".." / [ast, options]
+
+import iclineinfos
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+type
+  SymId* = distinct int32
+  ModuleId* = distinct int32
+  NodePos* = distinct int
+
+  NodeId* = distinct int32
+
+  PackedItemId* = object
+    module*: LitId       # 0 if it's this module
+    item*: int32         # same as the in-memory representation
+
+const
+  nilItemId* = PackedItemId(module: LitId(0), item: 0.int32)
+
+const
+  emptyNodeId* = NodeId(-1)
+
+type
+  PackedLib* = object
+    kind*: TLibKind
+    generated*: bool
+    isOverridden*: bool
+    name*: LitId
+    path*: NodeId
+
+  PackedSym* = object
+    id*: int32
+    kind*: TSymKind
+    name*: LitId
+    typ*: PackedItemId
+    flags*: TSymFlags
+    magic*: TMagic
+    info*: PackedLineInfo
+    ast*: NodeId
+    owner*: PackedItemId
+    guard*: PackedItemId
+    bitsize*: int
+    alignment*: int # for alignment
+    options*: TOptions
+    position*: int
+    offset*: int32
+    disamb*: int32
+    externalName*: LitId # instead of TLoc
+    locFlags*: TLocFlags
+    annex*: PackedLib
+    when hasFFI:
+      cname*: LitId
+    constraint*: NodeId
+    instantiatedFrom*: PackedItemId
+
+  PackedType* = object
+    id*: int32
+    kind*: TTypeKind
+    callConv*: TCallingConvention
+    #nodekind*: TNodeKind
+    flags*: TTypeFlags
+    types*: seq[PackedItemId]
+    n*: NodeId
+    #nodeflags*: TNodeFlags
+    sym*: PackedItemId
+    owner*: PackedItemId
+    size*: BiggestInt
+    align*: int16
+    paddingAtEnd*: int16
+    # not serialized: loc*: TLoc because it is backend-specific
+    typeInst*: PackedItemId
+    nonUniqueId*: int32
+
+  PackedNode* = object     # 8 bytes
+    x: uint32
+    info*: PackedLineInfo
+
+  PackedTree* = object ## usually represents a full Nim module
+    nodes: seq[PackedNode]
+    withFlags: seq[(int32, TNodeFlags)]
+    withTypes: seq[(int32, PackedItemId)]
+
+  PackedInstantiation* = object
+    key*, sym*: PackedItemId
+    concreteTypes*: seq[PackedItemId]
+
+const
+  NodeKindBits = 8'u32
+  NodeKindMask = (1'u32 shl NodeKindBits) - 1'u32
+
+template kind*(n: PackedNode): TNodeKind = TNodeKind(n.x and NodeKindMask)
+template uoperand*(n: PackedNode): uint32 = (n.x shr NodeKindBits)
+template soperand*(n: PackedNode): int32 = int32(uoperand(n))
+
+template toX(k: TNodeKind; operand: uint32): uint32 =
+  uint32(k) or (operand shl NodeKindBits)
+
+template toX(k: TNodeKind; operand: LitId): uint32 =
+  uint32(k) or (operand.uint32 shl NodeKindBits)
+
+template typeId*(n: PackedNode): PackedItemId = n.typ
+
+proc `==`*(a, b: SymId): bool {.borrow.}
+proc hash*(a: SymId): Hash {.borrow.}
+
+proc `==`*(a, b: NodePos): bool {.borrow.}
+#proc `==`*(a, b: PackedItemId): bool {.borrow.}
+proc `==`*(a, b: NodeId): bool {.borrow.}
+
+proc newTreeFrom*(old: PackedTree): PackedTree =
+  result = PackedTree(nodes: @[])
+  when false: result.sh = old.sh
+
+proc addIdent*(tree: var PackedTree; s: LitId; info: PackedLineInfo) =
+  tree.nodes.add PackedNode(x: toX(nkIdent, uint32(s)), info: info)
+
+proc addSym*(tree: var PackedTree; s: int32; info: PackedLineInfo) =
+  tree.nodes.add PackedNode(x: toX(nkSym, cast[uint32](s)), info: info)
+
+proc addSymDef*(tree: var PackedTree; s: SymId; info: PackedLineInfo) =
+  tree.nodes.add PackedNode(x: toX(nkSym, cast[uint32](s)), info: info)
+
+proc isAtom*(tree: PackedTree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= nkNilLit
+
+type
+  PatchPos = distinct int
+
+proc addNode*(t: var PackedTree; kind: TNodeKind; operand: int32;
+              typeId: PackedItemId = nilItemId; info: PackedLineInfo;
+              flags: TNodeFlags = {}) =
+  t.nodes.add PackedNode(x: toX(kind, cast[uint32](operand)), info: info)
+  if flags != {}:
+    t.withFlags.add (t.nodes.len.int32 - 1, flags)
+  if typeId != nilItemId:
+    t.withTypes.add (t.nodes.len.int32 - 1, typeId)
+
+proc prepare*(tree: var PackedTree; kind: TNodeKind; flags: TNodeFlags; typeId: PackedItemId; info: PackedLineInfo): PatchPos =
+  result = PatchPos tree.nodes.len
+  tree.addNode(kind = kind, flags = flags, operand = 0, info = info, typeId = typeId)
+
+proc prepare*(dest: var PackedTree; source: PackedTree; sourcePos: NodePos): PatchPos =
+  result = PatchPos dest.nodes.len
+  dest.nodes.add source.nodes[sourcePos.int]
+
+proc patch*(tree: var PackedTree; pos: PatchPos) =
+  let pos = pos.int
+  let k = tree.nodes[pos].kind
+  assert k > nkNilLit
+  let distance = int32(tree.nodes.len - pos)
+  assert distance > 0
+  tree.nodes[pos].x = toX(k, cast[uint32](distance))
+
+proc len*(tree: PackedTree): int {.inline.} = tree.nodes.len
+
+proc `[]`*(tree: PackedTree; i: NodePos): lent PackedNode {.inline.} =
+  tree.nodes[i.int]
+
+template rawSpan(n: PackedNode): int = int(uoperand(n))
+
+proc nextChild(tree: PackedTree; pos: var int) {.inline.} =
+  if tree.nodes[pos].kind > nkNilLit:
+    assert tree.nodes[pos].uoperand > 0
+    inc pos, tree.nodes[pos].rawSpan
+  else:
+    inc pos
+
+iterator sonsReadonly*(tree: PackedTree; n: NodePos): NodePos =
+  var pos = n.int
+  assert tree.nodes[pos].kind > nkNilLit
+  let last = pos + tree.nodes[pos].rawSpan
+  inc pos
+  while pos < last:
+    yield NodePos pos
+    nextChild tree, pos
+
+iterator sons*(dest: var PackedTree; tree: PackedTree; n: NodePos): NodePos =
+  let patchPos = prepare(dest, tree, n)
+  for x in sonsReadonly(tree, n): yield x
+  patch dest, patchPos
+
+iterator isons*(dest: var PackedTree; tree: PackedTree;
+                n: NodePos): (int, NodePos) =
+  var i = 0
+  for ch0 in sons(dest, tree, n):
+    yield (i, ch0)
+    inc i
+
+iterator sonsFrom1*(tree: PackedTree; n: NodePos): NodePos =
+  var pos = n.int
+  assert tree.nodes[pos].kind > nkNilLit
+  let last = pos + tree.nodes[pos].rawSpan
+  inc pos
+  if pos < last:
+    nextChild tree, pos
+  while pos < last:
+    yield NodePos pos
+    nextChild tree, pos
+
+iterator sonsWithoutLast2*(tree: PackedTree; n: NodePos): NodePos =
+  var count = 0
+  for child in sonsReadonly(tree, n):
+    inc count
+  var pos = n.int
+  assert tree.nodes[pos].kind > nkNilLit
+  let last = pos + tree.nodes[pos].rawSpan
+  inc pos
+  while pos < last and count > 2:
+    yield NodePos pos
+    dec count
+    nextChild tree, pos
+
+proc parentImpl(tree: PackedTree; n: NodePos): NodePos =
+  # finding the parent of a node is rather easy:
+  var pos = n.int - 1
+  while pos >= 0 and (isAtom(tree, pos) or (pos + tree.nodes[pos].rawSpan - 1 < n.int)):
+    dec pos
+  #assert pos >= 0, "node has no parent"
+  result = NodePos(pos)
+
+template parent*(n: NodePos): NodePos = parentImpl(tree, n)
+
+proc hasXsons*(tree: PackedTree; n: NodePos; x: int): bool =
+  var count = 0
+  if tree.nodes[n.int].kind > nkNilLit:
+    for child in sonsReadonly(tree, n): inc count
+  result = count == x
+
+proc hasAtLeastXsons*(tree: PackedTree; n: NodePos; x: int): bool =
+  if tree.nodes[n.int].kind > nkNilLit:
+    var count = 0
+    for child in sonsReadonly(tree, n):
+      inc count
+      if count >= x: return true
+  return false
+
+proc firstSon*(tree: PackedTree; n: NodePos): NodePos {.inline.} =
+  NodePos(n.int+1)
+proc kind*(tree: PackedTree; n: NodePos): TNodeKind {.inline.} =
+  tree.nodes[n.int].kind
+proc litId*(tree: PackedTree; n: NodePos): LitId {.inline.} =
+  LitId tree.nodes[n.int].uoperand
+proc info*(tree: PackedTree; n: NodePos): PackedLineInfo {.inline.} =
+  tree.nodes[n.int].info
+
+proc findType*(tree: PackedTree; n: NodePos): PackedItemId =
+  for x in tree.withTypes:
+    if x[0] == int32(n): return x[1]
+    if x[0] > int32(n): return nilItemId
+  return nilItemId
+
+proc findFlags*(tree: PackedTree; n: NodePos): TNodeFlags =
+  for x in tree.withFlags:
+    if x[0] == int32(n): return x[1]
+    if x[0] > int32(n): return {}
+  return {}
+
+template typ*(n: NodePos): PackedItemId =
+  tree.findType(n)
+template flags*(n: NodePos): TNodeFlags =
+  tree.findFlags(n)
+
+template uoperand*(n: NodePos): uint32 =
+  tree.nodes[n.int].uoperand
+
+proc span*(tree: PackedTree; pos: int): int {.inline.} =
+  if isAtom(tree, pos): 1 else: tree.nodes[pos].rawSpan
+
+proc sons2*(tree: PackedTree; n: NodePos): (NodePos, NodePos) =
+  assert(not isAtom(tree, n.int))
+  let a = n.int+1
+  let b = a + span(tree, a)
+  result = (NodePos a, NodePos b)
+
+proc sons3*(tree: PackedTree; n: NodePos): (NodePos, NodePos, NodePos) =
+  assert(not isAtom(tree, n.int))
+  let a = n.int+1
+  let b = a + span(tree, a)
+  let c = b + span(tree, b)
+  result = (NodePos a, NodePos b, NodePos c)
+
+proc ithSon*(tree: PackedTree; n: NodePos; i: int): NodePos =
+  result = default(NodePos)
+  if tree.nodes[n.int].kind > nkNilLit:
+    var count = 0
+    for child in sonsReadonly(tree, n):
+      if count == i: return child
+      inc count
+  assert false, "node has no i-th child"
+
+when false:
+  proc `@`*(tree: PackedTree; lit: LitId): lent string {.inline.} =
+    tree.sh.strings[lit]
+
+template kind*(n: NodePos): TNodeKind = tree.nodes[n.int].kind
+template info*(n: NodePos): PackedLineInfo = tree.nodes[n.int].info
+template litId*(n: NodePos): LitId = LitId tree.nodes[n.int].uoperand
+
+template symId*(n: NodePos): SymId = SymId tree.nodes[n.int].soperand
+
+proc firstSon*(n: NodePos): NodePos {.inline.} = NodePos(n.int+1)
+
+const
+  externIntLit* = {nkCharLit,
+    nkIntLit,
+    nkInt8Lit,
+    nkInt16Lit,
+    nkInt32Lit,
+    nkInt64Lit,
+    nkUIntLit,
+    nkUInt8Lit,
+    nkUInt16Lit,
+    nkUInt32Lit,
+    nkUInt64Lit}
+
+  externSIntLit* = {nkIntLit, nkInt8Lit, nkInt16Lit, nkInt32Lit, nkInt64Lit}
+  externUIntLit* = {nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit}
+  directIntLit* = nkNone
+
+template copyInto*(dest, n, body) =
+  let patchPos = prepare(dest, tree, n)
+  body
+  patch dest, patchPos
+
+template copyIntoKind*(dest, kind, info, body) =
+  let patchPos = prepare(dest, kind, info)
+  body
+  patch dest, patchPos
+
+proc getNodeId*(tree: PackedTree): NodeId {.inline.} = NodeId tree.nodes.len
+
+iterator allNodes*(tree: PackedTree): NodePos =
+  var p = 0
+  while p < tree.len:
+    yield NodePos(p)
+    let s = span(tree, p)
+    inc p, s
+
+proc toPackedItemId*(item: int32): PackedItemId {.inline.} =
+  PackedItemId(module: LitId(0), item: item)
+
+proc load*(f: var RodFile; t: var PackedTree) =
+  loadSeq f, t.nodes
+  loadSeq f, t.withFlags
+  loadSeq f, t.withTypes
+
+proc store*(f: var RodFile; t: PackedTree) =
+  storeSeq f, t.nodes
+  storeSeq f, t.withFlags
+  storeSeq f, t.withTypes
diff --git a/compiler/ic/replayer.nim b/compiler/ic/replayer.nim
new file mode 100644
index 000000000..b244ec885
--- /dev/null
+++ b/compiler/ic/replayer.nim
@@ -0,0 +1,171 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Module that contains code to replay global VM state changes and pragma
+## state like ``{.compile: "foo.c".}``. For IC (= Incremental compilation)
+## support.
+
+import ".." / [ast, modulegraphs, trees, extccomp, btrees,
+  msgs, lineinfos, pathutils, options, cgmeth]
+
+import std/tables
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+import packed_ast, ic, bitabs
+
+proc replayStateChanges*(module: PSym; g: ModuleGraph) =
+  let list = module.ast
+  assert list != nil
+  assert list.kind == nkStmtList
+  for n in list:
+    assert n.kind == nkReplayAction
+    # Fortunately only a tiny subset of the available pragmas need to
+    # be replayed here. This is always a subset of ``pragmas.stmtPragmas``.
+    if n.len >= 2:
+      internalAssert g.config, n[0].kind == nkStrLit and n[1].kind == nkStrLit
+      case n[0].strVal
+      of "hint": message(g.config, n.info, hintUser, n[1].strVal)
+      of "warning": message(g.config, n.info, warnUser, n[1].strVal)
+      of "error": localError(g.config, n.info, errUser, n[1].strVal)
+      of "compile":
+        internalAssert g.config, n.len == 4 and n[2].kind == nkStrLit
+        let cname = AbsoluteFile n[1].strVal
+        var cf = Cfile(nimname: splitFile(cname).name, cname: cname,
+                       obj: AbsoluteFile n[2].strVal,
+                       flags: {CfileFlag.External},
+                       customArgs: n[3].strVal)
+        extccomp.addExternalFileToCompile(g.config, cf)
+      of "link":
+        extccomp.addExternalFileToLink(g.config, AbsoluteFile n[1].strVal)
+      of "passl":
+        extccomp.addLinkOption(g.config, n[1].strVal)
+      of "passc":
+        extccomp.addCompileOption(g.config, n[1].strVal)
+      of "localpassc":
+        extccomp.addLocalCompileOption(g.config, n[1].strVal, toFullPathConsiderDirty(g.config, module.info.fileIndex))
+      of "cppdefine":
+        options.cppDefine(g.config, n[1].strVal)
+      of "inc":
+        let destKey = n[1].strVal
+        let by = n[2].intVal
+        let v = getOrDefault(g.cacheCounters, destKey)
+        g.cacheCounters[destKey] = v+by
+      of "put":
+        let destKey = n[1].strVal
+        let key = n[2].strVal
+        let val = n[3]
+        if not contains(g.cacheTables, destKey):
+          g.cacheTables[destKey] = initBTree[string, PNode]()
+        if not contains(g.cacheTables[destKey], key):
+          g.cacheTables[destKey].add(key, val)
+        else:
+          internalError(g.config, n.info, "key already exists: " & key)
+      of "incl":
+        let destKey = n[1].strVal
+        let val = n[2]
+        if not contains(g.cacheSeqs, destKey):
+          g.cacheSeqs[destKey] = newTree(nkStmtList, val)
+        else:
+          block search:
+            for existing in g.cacheSeqs[destKey]:
+              if exprStructuralEquivalent(existing, val, strictSymEquality=true):
+                break search
+            g.cacheSeqs[destKey].add val
+      of "add":
+        let destKey = n[1].strVal
+        let val = n[2]
+        if not contains(g.cacheSeqs, destKey):
+          g.cacheSeqs[destKey] = newTree(nkStmtList, val)
+        else:
+          g.cacheSeqs[destKey].add val
+      else:
+        internalAssert g.config, false
+
+proc replayBackendProcs*(g: ModuleGraph; module: int) =
+  for it in mitems(g.packed[module].fromDisk.attachedOps):
+    let key = translateId(it[0], g.packed, module, g.config)
+    let op = it[1]
+    let tmp = translateId(it[2], g.packed, module, g.config)
+    let symId = FullId(module: tmp.module, packed: it[2])
+    g.attachedOps[op][key] = LazySym(id: symId, sym: nil)
+
+  for it in mitems(g.packed[module].fromDisk.enumToStringProcs):
+    let key = translateId(it[0], g.packed, module, g.config)
+    let tmp = translateId(it[1], g.packed, module, g.config)
+    let symId = FullId(module: tmp.module, packed: it[1])
+    g.enumToStringProcs[key] = LazySym(id: symId, sym: nil)
+
+  for it in mitems(g.packed[module].fromDisk.methodsPerType):
+    let key = translateId(it[0], g.packed, module, g.config)
+    let tmp = translateId(it[1], g.packed, module, g.config)
+    let symId = FullId(module: tmp.module, packed: it[1])
+    g.methodsPerType.mgetOrPut(key, @[]).add LazySym(id: symId, sym: nil)
+
+  for it in mitems(g.packed[module].fromDisk.dispatchers):
+    let tmp = translateId(it, g.packed, module, g.config)
+    let symId = FullId(module: tmp.module, packed: it)
+    g.dispatchers.add LazySym(id: symId, sym: nil)
+
+proc replayGenericCacheInformation*(g: ModuleGraph; module: int) =
+  ## We remember the generic instantiations a module performed
+  ## in order to to avoid the code bloat that generic code tends
+  ## to imply. This is cheaper than deduplication of identical
+  ## generic instantiations. However, deduplication is more
+  ## powerful and general and I hope to implement it soon too
+  ## (famous last words).
+  assert g.packed[module].status == loaded
+  for it in g.packed[module].fromDisk.typeInstCache:
+    let key = translateId(it[0], g.packed, module, g.config)
+    g.typeInstCache.mgetOrPut(key, @[]).add LazyType(id: FullId(module: module, packed: it[1]), typ: nil)
+
+  for it in mitems(g.packed[module].fromDisk.procInstCache):
+    let key = translateId(it.key, g.packed, module, g.config)
+    let sym = translateId(it.sym, g.packed, module, g.config)
+    var concreteTypes = newSeq[FullId](it.concreteTypes.len)
+    for i in 0..high(it.concreteTypes):
+      let tmp = translateId(it.concreteTypes[i], g.packed, module, g.config)
+      concreteTypes[i] = FullId(module: tmp.module, packed: it.concreteTypes[i])
+
+    g.procInstCache.mgetOrPut(key, @[]).add LazyInstantiation(
+      module: module, sym: FullId(module: sym.module, packed: it.sym),
+      concreteTypes: concreteTypes, inst: nil)
+
+  for it in mitems(g.packed[module].fromDisk.methodsPerGenericType):
+    let key = translateId(it[0], g.packed, module, g.config)
+    let col = it[1]
+    let tmp = translateId(it[2], g.packed, module, g.config)
+    let symId = FullId(module: tmp.module, packed: it[2])
+    g.methodsPerGenericType.mgetOrPut(key, @[]).add (col, LazySym(id: symId, sym: nil))
+
+  replayBackendProcs(g, module)
+
+  for it in mitems(g.packed[module].fromDisk.methods):
+    let sym = loadSymFromId(g.config, g.cache, g.packed, module,
+                            PackedItemId(module: LitId(0), item: it))
+    methodDef(g, g.idgen, sym)
+
+  when false:
+    # not used anymore:
+    for it in mitems(g.packed[module].fromDisk.compilerProcs):
+      let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it[1]))
+      g.lazyCompilerprocs[g.packed[module].fromDisk.sh.strings[it[0]]] = symId
+
+  for it in mitems(g.packed[module].fromDisk.converters):
+    let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it))
+    g.ifaces[module].converters.add LazySym(id: symId, sym: nil)
+
+  for it in mitems(g.packed[module].fromDisk.trmacros):
+    let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it))
+    g.ifaces[module].patterns.add LazySym(id: symId, sym: nil)
+
+  for it in mitems(g.packed[module].fromDisk.pureEnums):
+    let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it))
+    g.ifaces[module].pureEnums.add LazySym(id: symId, sym: nil)
diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim
new file mode 100644
index 000000000..ac995dd2e
--- /dev/null
+++ b/compiler/ic/rodfiles.nim
@@ -0,0 +1,283 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Low level binary format used by the compiler to store and load various AST
+## and related data.
+##
+## NB: this is incredibly low level and if you're interested in how the
+##     compiler works and less a storage format, you're probably looking for
+##     the `ic` or `packed_ast` modules to understand the logical format.
+
+from std/typetraits import supportsCopyMem
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions]
+
+import std / tables
+
+## Overview
+## ========
+## `RodFile` represents a Rod File (versioned binary format), and the
+## associated data for common interactions such as IO and error tracking
+## (`RodFileError`). The file format broken up into sections (`RodSection`)
+## and preceded by a header (see: `cookie`). The precise layout, section
+## ordering and data following the section are determined by the user. See
+## `ic.loadRodFile`.
+##
+## A basic but "wrong" example of the lifecycle:
+## ---------------------------------------------
+## 1. `create` or `open`        - create a new one or open an existing
+## 2. `storeHeader`             - header info
+## 3. `storePrim` or `storeSeq` - save your stuff
+## 4. `close`                   - and we're done
+##
+## Now read the bits below to understand what's missing.
+##
+## ### Issues with the Example
+## Missing Sections:
+## This is a low level API, so headers and sections need to be stored and
+## loaded by the user, see `storeHeader` & `loadHeader` and `storeSection` &
+## `loadSection`, respectively.
+##
+## No Error Handling:
+## The API is centered around IO and prone to error, each operation checks or
+## sets the `RodFile.err` field. A user of this API needs to handle these
+## appropriately.
+##
+## API Notes
+## =========
+##
+## Valid inputs for Rod files
+## --------------------------
+## ASTs, hopes, dreams, and anything as long as it and any children it may have
+## support `copyMem`. This means anything that is not a pointer and that does not contain a pointer. At a glance these are:
+## * string
+## * objects & tuples (fields are recursed)
+## * sequences AKA `seq[T]`
+##
+## Note on error handling style
+## ----------------------------
+## A flag based approach is used where operations no-op in case of a
+## preexisting error and set the flag if they encounter one.
+##
+## Misc
+## ----
+## * 'Prim' is short for 'primitive', as in a non-sequence type
+
+type
+  RodSection* = enum
+    versionSection
+    configSection
+    stringsSection
+    checkSumsSection
+    depsSection
+    numbersSection
+    exportsSection
+    hiddenSection
+    reexportsSection
+    compilerProcsSection
+    trmacrosSection
+    convertersSection
+    methodsSection
+    pureEnumsSection
+    toReplaySection
+    topLevelSection
+    bodiesSection
+    symsSection
+    typesSection
+    typeInstCacheSection
+    procInstCacheSection
+    attachedOpsSection
+    methodsPerGenericTypeSection
+    enumToStringProcsSection
+    methodsPerTypeSection
+    dispatchersSection
+    typeInfoSection  # required by the backend
+    backendFlagsSection
+    aliveSymsSection # beware, this is stored in a `.alivesyms` file.
+    sideChannelSection
+    namespaceSection
+    symnamesSection
+
+  RodFileError* = enum
+    ok, tooBig, cannotOpen, ioFailure, wrongHeader, wrongSection, configMismatch,
+    includeFileChanged
+
+  RodFile* = object
+    f*: File
+    currentSection*: RodSection # for error checking
+    err*: RodFileError # little experiment to see if this works
+                       # better than exceptions.
+
+const
+  RodVersion = 2
+  defaultCookie = [byte(0), byte('R'), byte('O'), byte('D'),
+            byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(RodVersion)]
+
+proc setError(f: var RodFile; err: RodFileError) {.inline.} =
+  f.err = err
+  #raise newException(IOError, "IO error")
+
+proc storePrim*(f: var RodFile; s: string) =
+  ## Stores a string.
+  ## The len is prefixed to allow for later retreival.
+  if f.err != ok: return
+  if s.len >= high(int32):
+    setError f, tooBig
+    return
+  var lenPrefix = int32(s.len)
+  if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
+    setError f, ioFailure
+  else:
+    if s.len != 0:
+      if writeBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len:
+        setError f, ioFailure
+
+proc storePrim*[T](f: var RodFile; x: T) =
+  ## Stores a non-sequence/string `T`.
+  ## If `T` doesn't support `copyMem` and is an object or tuple then the fields
+  ## are written -- the user from context will need to know which `T` to load.
+  if f.err != ok: return
+  when supportsCopyMem(T):
+    if writeBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x):
+      setError f, ioFailure
+  elif T is tuple:
+    for y in fields(x):
+      storePrim(f, y)
+  elif T is object:
+    for y in fields(x):
+      when y is seq:
+        storeSeq(f, y)
+      else:
+        storePrim(f, y)
+  else:
+    {.error: "unsupported type for 'storePrim'".}
+
+proc storeSeq*[T](f: var RodFile; s: seq[T]) =
+  ## Stores a sequence of `T`s, with the len as a prefix for later retrieval.
+  if f.err != ok: return
+  if s.len >= high(int32):
+    setError f, tooBig
+    return
+  var lenPrefix = int32(s.len)
+  if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
+    setError f, ioFailure
+  else:
+    for i in 0..<s.len:
+      storePrim(f, s[i])
+
+proc storeOrderedTable*[K, T](f: var RodFile; s: OrderedTable[K, T]) =
+  if f.err != ok: return
+  if s.len >= high(int32):
+    setError f, tooBig
+    return
+  var lenPrefix = int32(s.len)
+  if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
+    setError f, ioFailure
+  else:
+    for _, v in s:
+      storePrim(f, v)
+
+proc loadPrim*(f: var RodFile; s: var string) =
+  ## Read a string, the length was stored as a prefix
+  if f.err != ok: return
+  var lenPrefix = int32(0)
+  if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
+    setError f, ioFailure
+  else:
+    s = newString(lenPrefix)
+    if lenPrefix > 0:
+      if readBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len:
+        setError f, ioFailure
+
+proc loadPrim*[T](f: var RodFile; x: var T) =
+  ## Load a non-sequence/string `T`.
+  if f.err != ok: return
+  when supportsCopyMem(T):
+    if readBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x):
+      setError f, ioFailure
+  elif T is tuple:
+    for y in fields(x):
+      loadPrim(f, y)
+  elif T is object:
+    for y in fields(x):
+      when y is seq:
+        loadSeq(f, y)
+      else:
+        loadPrim(f, y)
+  else:
+    {.error: "unsupported type for 'loadPrim'".}
+
+proc loadSeq*[T](f: var RodFile; s: var seq[T]) =
+  ## `T` must be compatible with `copyMem`, see `loadPrim`
+  if f.err != ok: return
+  var lenPrefix = int32(0)
+  if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
+    setError f, ioFailure
+  else:
+    s = newSeq[T](lenPrefix)
+    for i in 0..<lenPrefix:
+      loadPrim(f, s[i])
+
+proc loadOrderedTable*[K, T](f: var RodFile; s: var OrderedTable[K, T]) =
+  ## `T` must be compatible with `copyMem`, see `loadPrim`
+  if f.err != ok: return
+  var lenPrefix = int32(0)
+  if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
+    setError f, ioFailure
+  else:
+    s = initOrderedTable[K, T](lenPrefix)
+    for i in 0..<lenPrefix:
+      var x = default T
+      loadPrim(f, x)
+      s[x.id] = x
+
+proc storeHeader*(f: var RodFile; cookie = defaultCookie) =
+  ## stores the header which is described by `cookie`.
+  if f.err != ok: return
+  if f.f.writeBytes(cookie, 0, cookie.len) != cookie.len:
+    setError f, ioFailure
+
+proc loadHeader*(f: var RodFile; cookie = defaultCookie) =
+  ## Loads the header which is described by `cookie`.
+  if f.err != ok: return
+  var thisCookie: array[cookie.len, byte] = default(array[cookie.len, byte])
+  if f.f.readBytes(thisCookie, 0, thisCookie.len) != thisCookie.len:
+    setError f, ioFailure
+  elif thisCookie != cookie:
+    setError f, wrongHeader
+
+proc storeSection*(f: var RodFile; s: RodSection) =
+  ## update `currentSection` and writes the bytes value of s.
+  if f.err != ok: return
+  assert f.currentSection < s
+  f.currentSection = s
+  storePrim(f, s)
+
+proc loadSection*(f: var RodFile; expected: RodSection) =
+  ## read the bytes value of s, sets and error if the section is incorrect.
+  if f.err != ok: return
+  var s: RodSection = default(RodSection)
+  loadPrim(f, s)
+  if expected != s and f.err == ok:
+    setError f, wrongSection
+
+proc create*(filename: string): RodFile =
+  ## create the file and open it for writing
+  result = default(RodFile)
+  if not open(result.f, filename, fmWrite):
+    setError result, cannotOpen
+
+proc close*(f: var RodFile) = close(f.f)
+
+proc open*(filename: string): RodFile =
+  ## open the file for reading
+  result = default(RodFile)
+  if not open(result.f, filename, fmRead):
+    setError result, cannotOpen
diff --git a/compiler/idents.nim b/compiler/idents.nim
index 0a2f2d5cf..34177e76d 100644
--- a/compiler/idents.nim
+++ b/compiler/idents.nim
@@ -11,16 +11,16 @@
 # An identifier is a shared immutable string that can be compared by its
 # id. This module is essential for the compiler's performance.
 
-import
-  hashes, strutils, wordrecg
+import wordrecg
+import std/hashes
 
-type
-  TIdObj* = object of RootObj
-    id*: int # unique id; use this for comparisons and not the pointers
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
-  PIdObj* = ref TIdObj
+type
   PIdent* = ref TIdent
-  TIdent*{.acyclic.} = object of TIdObj
+  TIdent*{.acyclic.} = object
+    id*: int # unique id; use this for comparisons and not the pointers
     s*: string
     next*: PIdent             # for hash-table chaining
     h*: Hash                 # hash value of s
@@ -30,14 +30,7 @@ type
     wordCounter: int
     idAnon*, idDelegator*, emptyIdent*: PIdent
 
-when false:
-  var
-    legacy: IdentCache
-
-proc resetIdentCache*() =
-  when false:
-    for i in low(legacy.buckets)..high(legacy.buckets):
-      legacy.buckets[i] = nil
+proc resetIdentCache*() = discard
 
 proc cmpIgnoreStyle*(a, b: cstring, blen: int): int =
   if a[0] != b[0]: return 1
@@ -73,11 +66,9 @@ proc cmpExact(a, b: cstring, blen: int): int =
   if result == 0:
     if a[i] != '\0': result = 1
 
-{.this: self.}
-
-proc getIdent*(self: IdentCache; identifier: cstring, length: int, h: Hash): PIdent =
-  var idx = h and high(buckets)
-  result = buckets[idx]
+proc getIdent*(ic: IdentCache; identifier: cstring, length: int, h: Hash): PIdent =
+  var idx = h and high(ic.buckets)
+  result = ic.buckets[idx]
   var last: PIdent = nil
   var id = 0
   while result != nil:
@@ -85,8 +76,8 @@ proc getIdent*(self: IdentCache; identifier: cstring, length: int, h: Hash): PId
       if last != nil:
         # make access to last looked up identifier faster:
         last.next = result.next
-        result.next = buckets[idx]
-        buckets[idx] = result
+        result.next = ic.buckets[idx]
+        ic.buckets[idx] = result
       return
     elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0:
       assert((id == 0) or (id == result.id))
@@ -96,21 +87,21 @@ proc getIdent*(self: IdentCache; identifier: cstring, length: int, h: Hash): PId
   new(result)
   result.h = h
   result.s = newString(length)
-  for i in countup(0, length - 1): result.s[i] = identifier[i]
-  result.next = buckets[idx]
-  buckets[idx] = result
+  for i in 0..<length: result.s[i] = identifier[i]
+  result.next = ic.buckets[idx]
+  ic.buckets[idx] = result
   if id == 0:
-    inc(wordCounter)
-    result.id = -wordCounter
+    inc(ic.wordCounter)
+    result.id = -ic.wordCounter
   else:
     result.id = id
 
-proc getIdent*(self: IdentCache; identifier: string): PIdent =
-  result = getIdent(cstring(identifier), len(identifier),
+proc getIdent*(ic: IdentCache; identifier: string): PIdent =
+  result = getIdent(ic, cstring(identifier), identifier.len,
                     hashIgnoreStyle(identifier))
 
-proc getIdent*(self: IdentCache; identifier: string, h: Hash): PIdent =
-  result = getIdent(cstring(identifier), len(identifier), h)
+proc getIdent*(ic: IdentCache; identifier: string, h: Hash): PIdent =
+  result = getIdent(ic, cstring(identifier), identifier.len, h)
 
 proc newIdentCache*(): IdentCache =
   result = IdentCache()
@@ -119,9 +110,14 @@ proc newIdentCache*(): IdentCache =
   result.idDelegator = result.getIdent":delegator"
   result.emptyIdent = result.getIdent("")
   # initialize the keywords:
-  for s in countup(succ(low(specialWords)), high(specialWords)):
-    result.getIdent(specialWords[s], hashIgnoreStyle(specialWords[s])).id = ord(s)
+  for s in succ(low(TSpecialWord))..high(TSpecialWord):
+    result.getIdent($s, hashIgnoreStyle($s)).id = ord(s)
 
 proc whichKeyword*(id: PIdent): TSpecialWord =
   if id.id < 0: result = wInvalid
   else: result = TSpecialWord(id.id)
+
+proc hash*(x: PIdent): Hash {.inline.} = x.h
+proc `==`*(a, b: PIdent): bool {.inline.} =
+  if a.isNil or b.isNil: result = system.`==`(a, b)
+  else: result = a.id == b.id
diff --git a/compiler/idgen.nim b/compiler/idgen.nim
deleted file mode 100644
index 7d103ffd7..000000000
--- a/compiler/idgen.nim
+++ /dev/null
@@ -1,59 +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 contains a simple persistent id generator.
-
-import idents, strutils, os, options
-
-var gFrontEndId*: int
-
-const
-  debugIds* = false
-
-when debugIds:
-  import intsets
-
-  var usedIds = initIntSet()
-
-proc registerID*(id: PIdObj) =
-  when debugIds:
-    if id.id == -1 or containsOrIncl(usedIds, id.id):
-      internalError("ID already used: " & $id.id)
-
-proc getID*(): int {.inline.} =
-  result = gFrontEndId
-  inc(gFrontEndId)
-
-proc setId*(id: int) {.inline.} =
-  gFrontEndId = max(gFrontEndId, id + 1)
-
-proc idSynchronizationPoint*(idRange: int) =
-  gFrontEndId = (gFrontEndId div idRange + 1) * idRange + 1
-
-proc toGid(conf: ConfigRef; f: string): string =
-  # we used to use ``f.addFileExt("gid")`` (aka ``$project.gid``), but this
-  # will cause strange bugs if multiple projects are in the same folder, so
-  # we simply use a project independent name:
-  result = options.completeGeneratedFilePath(conf, "nim.gid")
-
-proc saveMaxIds*(conf: ConfigRef; project: string) =
-  var f = open(toGid(conf, project), fmWrite)
-  f.writeLine($gFrontEndId)
-  f.close()
-
-proc loadMaxIds*(conf: ConfigRef; project: string) =
-  var f: File
-  if open(f, toGid(conf, project), fmRead):
-    var line = newStringOfCap(20)
-    if f.readLine(line):
-      var frontEndId = parseInt(line)
-      if f.readLine(line):
-        # var backEndId = parseInt(line)
-        gFrontEndId = max(gFrontEndId, frontEndId)
-    f.close()
diff --git a/compiler/importer.nim b/compiler/importer.nim
index c013b93ab..ffb7e0305 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -7,106 +7,214 @@
 #    distribution, for details about the copyright.
 #
 
-# This module implements the symbol importing mechanism.
+## This module implements the symbol importing mechanism.
 
 import
-  intsets, strutils, os, ast, astalgo, msgs, options, idents, lookups,
-  semdata, passes, renderer, modulepaths, sigmatch, lineinfos
+  ast, astalgo, msgs, options, idents, lookups,
+  semdata, modulepaths, sigmatch, lineinfos,
+  modulegraphs, wordrecg
+from std/strutils import `%`, startsWith
+from std/sequtils import addUnique
+import std/[sets, tables, intsets]
 
-proc evalImport*(c: PContext, n: PNode): PNode
-proc evalFrom*(c: PContext, n: PNode): PNode
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 proc readExceptSet*(c: PContext, n: PNode): IntSet =
   assert n.kind in {nkImportExceptStmt, nkExportExceptStmt}
   result = initIntSet()
-  for i in 1 ..< n.len:
+  for i in 1..<n.len:
     let ident = lookups.considerQuotedIdent(c, n[i])
     result.incl(ident.id)
 
-proc importPureEnumField*(c: PContext; s: PSym) =
-  var check = strTableGet(c.importTable.symbols, s.name)
-  if check == nil:
+proc declarePureEnumField*(c: PContext; s: PSym) =
+  # XXX Remove the outer 'if' statement and see what breaks.
+  var amb = false
+  if someSymFromImportTable(c, s.name, amb) == nil:
     strTableAdd(c.pureEnumFields, s)
+    when false:
+      let checkB = strTableGet(c.pureEnumFields, s.name)
+      if checkB == nil:
+        strTableAdd(c.pureEnumFields, s)
+    when false:
+      # mark as ambiguous:
+      incl(c.ambiguousSymbols, checkB.id)
+      incl(c.ambiguousSymbols, s.id)
 
-proc rawImportSymbol(c: PContext, s: PSym) =
+proc importPureEnumField(c: PContext; s: PSym) =
+  var amb = false
+  if someSymFromImportTable(c, s.name, amb) == nil:
+    strTableAdd(c.pureEnumFields, s)
+    when false:
+      let checkB = strTableGet(c.pureEnumFields, s.name)
+      if checkB == nil:
+        strTableAdd(c.pureEnumFields, s)
+    when false:
+      # mark as ambiguous:
+      incl(c.ambiguousSymbols, checkB.id)
+      incl(c.ambiguousSymbols, s.id)
+
+proc importPureEnumFields(c: PContext; s: PSym; etyp: PType) =
+  assert sfPure in s.flags
+  for j in 0..<etyp.n.len:
+    var e = etyp.n[j].sym
+    if e.kind != skEnumField:
+      internalError(c.config, s.info, "rawImportSymbol")
+      # BUGFIX: because of aliases for enums the symbol may already
+      # have been put into the symbol table
+      # BUGFIX: but only iff they are the same symbols!
+    for check in importedItems(c, e.name):
+      if check.id == e.id:
+        e = nil
+        break
+    if e != nil:
+      importPureEnumField(c, e)
+
+proc rawImportSymbol(c: PContext, s, origin: PSym; importSet: var IntSet) =
   # This does not handle stubs, because otherwise loading on demand would be
   # pointless in practice. So importing stubs is fine here!
   # check if we have already a symbol of the same name:
-  var check = strTableGet(c.importTable.symbols, s.name)
-  if check != nil and check.id != s.id:
-    if s.kind notin OverloadableSyms or check.kind notin OverloadableSyms:
-      # s and check need to be qualified:
-      incl(c.ambiguousSymbols, s.id)
-      incl(c.ambiguousSymbols, check.id)
+  when false:
+    var check = someSymFromImportTable(c, s.name)
+    if check != nil and check.id != s.id:
+      if s.kind notin OverloadableSyms or check.kind notin OverloadableSyms:
+        # s and check need to be qualified:
+        incl(c.ambiguousSymbols, s.id)
+        incl(c.ambiguousSymbols, check.id)
   # thanks to 'export' feature, it could be we import the same symbol from
-  # multiple sources, so we need to call 'StrTableAdd' here:
-  strTableAdd(c.importTable.symbols, s)
+  # multiple sources, so we need to call 'strTableAdd' here:
+  when false:
+    # now lazy. Speeds up the compiler and is a prerequisite for IC.
+    strTableAdd(c.importTable.symbols, s)
+  else:
+    importSet.incl s.id
   if s.kind == skType:
     var etyp = s.typ
     if etyp.kind in {tyBool, tyEnum}:
-      for j in countup(0, sonsLen(etyp.n) - 1):
-        var e = etyp.n.sons[j].sym
+      for j in 0..<etyp.n.len:
+        var e = etyp.n[j].sym
         if e.kind != skEnumField:
           internalError(c.config, s.info, "rawImportSymbol")
           # BUGFIX: because of aliases for enums the symbol may already
           # have been put into the symbol table
           # BUGFIX: but only iff they are the same symbols!
-        var it: TIdentIter
-        check = initIdentIter(it, c.importTable.symbols, e.name)
-        while check != nil:
+        for check in importedItems(c, e.name):
           if check.id == e.id:
             e = nil
             break
-          check = nextIdentIter(it, c.importTable.symbols)
         if e != nil:
           if sfPure notin s.flags:
-            rawImportSymbol(c, e)
+            rawImportSymbol(c, e, origin, importSet)
           else:
             importPureEnumField(c, e)
   else:
-    # rodgen assures that converters and patterns are no stubs
-    if s.kind == skConverter: addConverter(c, s)
-    if hasPattern(s): addPattern(c, s)
+    if s.kind == skConverter: addConverter(c, LazySym(sym: s))
+    if hasPattern(s): addPattern(c, LazySym(sym: s))
+  if s.owner != origin:
+    c.exportIndirections.incl((origin.id, s.id))
+
+proc splitPragmas(c: PContext, n: PNode): (PNode, seq[TSpecialWord]) =
+  template bail = globalError(c.config, n.info, "invalid pragma")
+  result = (nil, @[])
+  if n.kind == nkPragmaExpr:
+    if n.len == 2 and n[1].kind == nkPragma:
+      result[0] = n[0]
+      for ni in n[1]:
+        if ni.kind == nkIdent: result[1].add whichKeyword(ni.ident)
+        else: bail()
+    else: bail()
+  else:
+    result[0] = n
+    if result[0].safeLen > 0:
+      (result[0][^1], result[1]) = splitPragmas(c, result[0][^1])
+
+proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) =
+  let (n, kws) = splitPragmas(c, n)
+  if kws.len > 0:
+    globalError(c.config, n.info, "unexpected pragma")
 
-proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
   let ident = lookups.considerQuotedIdent(c, n)
-  let s = strTableGet(fromMod.tab, ident)
+  let s = someSym(c.graph, fromMod, ident)
   if s == nil:
     errorUndeclaredIdentifier(c, n.info, ident.s)
   else:
     when false:
       if s.kind == skStub: loadStub(s)
-    if s.kind notin ExportableSymKinds:
-      internalError(c.config, n.info, "importSymbol: 2")
+    let multiImport = s.kind notin ExportableSymKinds or s.kind in skProcKinds
     # for an enumeration we have to add all identifiers
-    case s.kind
-    of skProcKinds:
+    if multiImport:
       # for a overloadable syms add all overloaded routines
-      var it: TIdentIter
-      var e = initIdentIter(it, fromMod.tab, s.name)
+      var it: ModuleIter = default(ModuleIter)
+      var e = initModuleIter(it, c.graph, fromMod, s.name)
       while e != nil:
         if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
-        rawImportSymbol(c, e)
-        e = nextIdentIter(it, fromMod.tab)
-    else: rawImportSymbol(c, s)
+        if s.kind in ExportableSymKinds:
+          rawImportSymbol(c, e, fromMod, importSet)
+        e = nextModuleIter(it, c.graph)
+    else:
+      rawImportSymbol(c, s, fromMod, importSet)
+    suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
+
+proc addImport(c: PContext; im: sink ImportedModule) =
+  for i in 0..high(c.imports):
+    if c.imports[i].m == im.m:
+      # we have already imported the module: Check which import
+      # is more "powerful":
+      case c.imports[i].mode
+      of importAll: discard "already imported all symbols"
+      of importSet:
+        case im.mode
+        of importAll, importExcept:
+          # XXX: slightly wrong semantics for 'importExcept'...
+          # But we should probably change the spec and disallow this case.
+          c.imports[i] = im
+        of importSet:
+          # merge the import sets:
+          c.imports[i].imported.incl im.imported
+      of importExcept:
+        case im.mode
+        of importAll:
+          c.imports[i] = im
+        of importSet:
+          discard
+        of importExcept:
+          var cut = initIntSet()
+          # only exclude what is consistent between the two sets:
+          for j in im.exceptSet:
+            if j in c.imports[i].exceptSet:
+              cut.incl j
+          c.imports[i].exceptSet = cut
+      return
+  c.imports.add im
+
+template addUnnamedIt(c: PContext, fromMod: PSym; filter: untyped) {.dirty.} =
+  for it in mitems c.graph.ifaces[fromMod.position].converters:
+    if filter:
+      loadPackedSym(c.graph, it)
+      if sfExported in it.sym.flags:
+        addConverter(c, it)
+  for it in mitems c.graph.ifaces[fromMod.position].patterns:
+    if filter:
+      loadPackedSym(c.graph, it)
+      if sfExported in it.sym.flags:
+        addPattern(c, it)
+  for it in mitems c.graph.ifaces[fromMod.position].pureEnums:
+    if filter:
+      loadPackedSym(c.graph, it)
+      importPureEnumFields(c, it.sym, it.sym.typ)
 
 proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
-  var i: TTabIter
-  var s = initTabIter(i, fromMod.tab)
-  while s != nil:
-    if s.kind != skModule:
-      if s.kind != skEnumField:
-        if s.kind notin ExportableSymKinds:
-          internalError(c.config, s.info, "importAllSymbols: " & $s.kind)
-        if exceptSet.isNil or s.name.id notin exceptSet:
-          rawImportSymbol(c, s)
-    s = nextIter(i, fromMod.tab)
+  c.addImport ImportedModule(m: fromMod, mode: importExcept, exceptSet: exceptSet)
+  addUnnamedIt(c, fromMod, it.sym.name.id notin exceptSet)
 
 proc importAllSymbols*(c: PContext, fromMod: PSym) =
-  var exceptSet: IntSet
-  importAllSymbolsExcept(c, fromMod, exceptSet)
+  c.addImport ImportedModule(m: fromMod, mode: importAll)
+  addUnnamedIt(c, fromMod, true)
+  when false:
+    var exceptSet: IntSet
+    importAllSymbolsExcept(c, fromMod, exceptSet)
 
-proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) =
+proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; importSet: var IntSet) =
   if n.isNil: return
   case n.kind
   of nkExportStmt:
@@ -116,98 +224,170 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) =
       if s.kind == skModule:
         importAllSymbolsExcept(c, s, exceptSet)
       elif exceptSet.isNil or s.name.id notin exceptSet:
-        rawImportSymbol(c, s)
+        rawImportSymbol(c, s, fromMod, importSet)
   of nkExportExceptStmt:
     localError(c.config, n.info, "'export except' not implemented")
   else:
-    for i in 0..safeLen(n)-1:
-      importForwarded(c, n.sons[i], exceptSet)
+    for i in 0..n.safeLen-1:
+      importForwarded(c, n[i], exceptSet, fromMod, importSet)
 
-proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym =
+proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym =
   result = realModule
+  template createModuleAliasImpl(ident): untyped =
+    createModuleAlias(realModule, c.idgen, ident, n.info, c.config.options)
   if n.kind != nkImportAs: discard
-  elif n.len != 2 or n.sons[1].kind != nkIdent:
+  elif n.len != 2 or n[1].kind != nkIdent:
     localError(c.config, n.info, "module alias must be an identifier")
-  elif n.sons[1].ident.id != realModule.name.id:
+  elif n[1].ident.id != realModule.name.id:
     # some misguided guy will write 'import abc.foo as foo' ...
-    result = createModuleAlias(realModule, n.sons[1].ident, realModule.info,
-                               c.config.options)
+    result = createModuleAliasImpl(n[1].ident)
+  if result == realModule:
+    # avoids modifying `realModule`, see D20201209T194412 for `import {.all.}`
+    result = createModuleAliasImpl(realModule.name)
+  if importHidden:
+    result.options.incl optImportHidden
+  let moduleIdent = if n.kind == nkInfix: n[^1] else: n
+  c.unusedImports.add((result, moduleIdent.info))
+  c.importModuleMap[result.id] = realModule.id
+  c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id
+
+proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] =
+  result = (nil, false)
+  var ret = default(typeof(result))
+  proc processPragma(n2: PNode): PNode =
+    let (result2, kws) = splitPragmas(c, n2)
+    result = result2
+    for ai in kws:
+      case ai
+      of wImportHidden: ret.importHidden = true
+      else: globalError(c.config, n.info, "invalid pragma, expected: " & ${wImportHidden})
+
+  if n.kind == nkInfix and considerQuotedIdent(c, n[0]).s == "as":
+    ret.node = newNodeI(nkImportAs, n.info)
+    ret.node.add n[1].processPragma
+    ret.node.add n[2]
+  else:
+    ret.node = n.processPragma
+  return ret
 
-proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
-  var f = checkModuleName(c.config, n)
-  if f != InvalidFileIDX:
+proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
+  let transf = transformImportAs(c, n)
+  n = transf.node
+  let f = checkModuleName(c.config, n)
+  if f != InvalidFileIdx:
+    addImportFileDep(c, f)
     let L = c.graph.importStack.len
     let recursion = c.graph.importStack.find(f)
     c.graph.importStack.add f
     #echo "adding ", toFullPath(f), " at ", L+1
     if recursion >= 0:
       var err = ""
-      for i in countup(recursion, L-1):
+      for i in recursion..<L:
         if i > recursion: err.add "\n"
         err.add toFullPath(c.config, c.graph.importStack[i]) & " imports " &
                 toFullPath(c.config, c.graph.importStack[i+1])
       c.recursiveDep = err
-    result = importModuleAs(c, n, c.graph.importModuleCallback(c.graph, c.module, f))
+
+    var realModule: PSym
+    discard pushOptionEntry(c)
+    realModule = c.graph.importModuleCallback(c.graph, c.module, f)
+    result = importModuleAs(c, n, realModule, transf.importHidden)
+    popOptionEntry(c)
+
     #echo "set back to ", L
     c.graph.importStack.setLen(L)
     # we cannot perform this check reliably because of
-    # test: modules/import_in_config)
-    when true:
-      if result.info.fileIndex == c.module.info.fileIndex and
-          result.info.fileIndex == n.info.fileIndex:
-        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)
+    # test: modules/import_in_config) # xxx is that still true?
+    if realModule == c.module:
+      localError(c.config, n.info, "module '$1' cannot import itself" % realModule.name.s)
+    if sfDeprecated in realModule.flags:
+      var prefix = ""
+      if realModule.constraint != nil: prefix = realModule.constraint.strVal & "; "
+      message(c.config, n.info, warnDeprecated, prefix & realModule.name.s & " is deprecated")
+    let moduleName = getModuleName(c.config, n)
+    if belongsToStdlib(c.graph, result) and not startsWith(moduleName, stdPrefix) and
+        not startsWith(moduleName, "system/") and not startsWith(moduleName, "packages/"):
+      message(c.config, n.info, warnStdPrefix, realModule.name.s)
+
+    proc suggestMod(n: PNode; s: PSym) =
+      if n.kind == nkImportAs:
+        suggestMod(n[0], realModule)
+      elif n.kind == nkInfix:
+        suggestMod(n[2], s)
       else:
-        message(c.config, n.info, warnDeprecated, result.name.s)
-    suggestSym(c.config, n.info, result, c.graph.usageSym, false)
-    importStmtResult.add newStrNode(toFullPath(c.config, f), n.info)
+        suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
+    suggestMod(n, result)
+    importStmtResult.add newSymNode(result, n.info)
+    #newStrNode(toFullPath(c.config, f), n.info)
+  else:
+    result = nil
+
+proc afterImport(c: PContext, m: PSym) =
+  if isCachedModule(c.graph, m): return
+  # fixes bug #17510, for re-exported symbols
+  let realModuleId = c.importModuleMap[m.id]
+  for s in allSyms(c.graph, m):
+    if s.owner.id != realModuleId:
+      c.exportIndirections.incl((m.id, s.id))
 
 proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
+  var it = it
   let m = myImportModule(c, it, importStmtResult)
   if m != nil:
-    var emptySet: IntSet
     # ``addDecl`` needs to be done before ``importAllSymbols``!
     addDecl(c, m, it.info) # add symbol to symbol table of module
-    importAllSymbolsExcept(c, m, emptySet)
-    #importForwarded(c, m.ast, emptySet)
+    importAllSymbols(c, m)
+    #importForwarded(c, m.ast, emptySet, m)
+    afterImport(c, m)
 
-proc evalImport(c: PContext, n: PNode): PNode =
-  #result = n
+proc evalImport*(c: PContext, n: PNode): PNode =
   result = newNodeI(nkImportStmt, n.info)
-  for i in countup(0, sonsLen(n) - 1):
-    let it = n.sons[i]
-    if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
-      let sep = it[0]
-      let dir = it[1]
-      let a = newNodeI(nkInfix, it.info)
-      a.add sep
-      a.add dir
-      a.add sep # dummy entry, replaced in the loop
-      for x in it[2]:
-        a.sons[2] = x
-        impMod(c, a, result)
+  for i in 0..<n.len:
+    let it = n[i]
+    if it.kind in {nkInfix, nkPrefix} and it[^1].kind == nkBracket:
+      let lastPos = it.len - 1
+      var imp = copyNode(it)
+      newSons(imp, it.len)
+      for i in 0 ..< lastPos: imp[i] = it[i]
+      imp[lastPos] = imp[0] # dummy entry, replaced in the loop
+      for x in it[lastPos]:
+        # transform `a/b/[c as d]` to `/a/b/c as d`
+        if x.kind == nkInfix and x[0].ident.s == "as":
+          var impAs = copyNode(x)
+          newSons(impAs, 3)
+          impAs[0] = x[0]
+          imp[lastPos] = x[1]
+          impAs[1] = imp
+          impAs[2] = x[2]
+          impMod(c, impAs, result)
+        else:
+          imp[lastPos] = x
+          impMod(c, imp, result)
     else:
       impMod(c, it, result)
 
-proc evalFrom(c: PContext, n: PNode): PNode =
+proc evalFrom*(c: PContext, n: PNode): PNode =
   result = newNodeI(nkImportStmt, n.info)
   checkMinSonsLen(n, 2, c.config)
-  var m = myImportModule(c, n.sons[0], result)
+  var m = myImportModule(c, n[0], result)
   if m != nil:
-    n.sons[0] = newSymNode(m)
+    n[0] = newSymNode(m)
     addDecl(c, m, n.info)               # add symbol to symbol table of module
-    for i in countup(1, sonsLen(n) - 1):
-      if n.sons[i].kind != nkNilLit:
-        importSymbol(c, n.sons[i], m)
+
+    var im = ImportedModule(m: m, mode: importSet, imported: initIntSet())
+    for i in 1..<n.len:
+      if n[i].kind != nkNilLit:
+        importSymbol(c, n[i], m, im.imported)
+    c.addImport im
+    afterImport(c, m)
 
 proc evalImportExcept*(c: PContext, n: PNode): PNode =
   result = newNodeI(nkImportStmt, n.info)
   checkMinSonsLen(n, 2, c.config)
-  var m = myImportModule(c, n.sons[0], result)
+  var m = myImportModule(c, n[0], result)
   if m != nil:
-    n.sons[0] = newSymNode(m)
+    n[0] = newSymNode(m)
     addDecl(c, m, n.info)               # add symbol to symbol table of module
     importAllSymbolsExcept(c, m, readExceptSet(c, n))
-    #importForwarded(c, m.ast, exceptSet)
+    #importForwarded(c, m.ast, exceptSet, m)
+    afterImport(c, m)
diff --git a/compiler/incremental.nim b/compiler/incremental.nim
deleted file mode 100644
index 2008d35de..000000000
--- a/compiler/incremental.nim
+++ /dev/null
@@ -1,196 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2018 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Basic type definitions the module graph needs in order to support
-## incremental compilations.
-
-const nimIncremental* = defined(nimIncremental)
-
-import options, lineinfos
-
-when nimIncremental:
-  import ast, msgs, intsets, btrees, db_sqlite, std / sha1
-  from strutils import parseInt
-
-  type
-    Writer* = object
-      sstack*: seq[PSym]          # a stack of symbols to process
-      tstack*: seq[PType]         # a stack of types to process
-      tmarks*, smarks*: IntSet
-      forwardedSyms*: seq[PSym]
-
-    Reader* = object
-      syms*: BTree[int, PSym]
-      types*: BTree[int, PType]
-
-    IncrementalCtx* = object
-      db*: DbConn
-      w*: Writer
-      r*: Reader
-      configChanged*: bool
-
-  proc init*(incr: var IncrementalCtx) =
-    incr.w.sstack = @[]
-    incr.w.tstack = @[]
-    incr.w.tmarks = initIntSet()
-    incr.w.smarks = initIntSet()
-    incr.w.forwardedSyms = @[]
-    incr.r.syms = initBTree[int, PSym]()
-    incr.r.types = initBTree[int, PType]()
-
-
-  proc hashFileCached*(conf: ConfigRef; fileIdx: FileIndex; fullpath: string): string =
-    result = msgs.getHash(conf, fileIdx)
-    if result.len == 0:
-      result = $secureHashFile(fullpath)
-      msgs.setHash(conf, fileIdx, result)
-
-  proc toDbFileId*(incr: var IncrementalCtx; conf: ConfigRef; fileIdx: FileIndex): int =
-    if fileIdx == FileIndex(-1): return -1
-    let fullpath = toFullPath(conf, fileIdx)
-    let row = incr.db.getRow(sql"select id, fullhash from filenames where fullpath = ?",
-      fullpath)
-    let id = row[0]
-    let fullhash = hashFileCached(conf, fileIdx, fullpath)
-    if id.len == 0:
-      result = int incr.db.insertID(sql"insert into filenames(fullpath, fullhash) values (?, ?)",
-        fullpath, fullhash)
-    else:
-      if row[1] != fullhash:
-        incr.db.exec(sql"update filenames set fullhash = ? where fullpath = ?", fullhash, fullpath)
-      result = parseInt(id)
-
-  proc fromDbFileId*(incr: var IncrementalCtx; conf: ConfigRef; dbId: int): FileIndex =
-    if dbId == -1: return FileIndex(-1)
-    let fullpath = incr.db.getValue(sql"select fullpath from filenames where id = ?", dbId)
-    doAssert fullpath.len > 0, "cannot find file name for DB ID " & $dbId
-    result = fileInfoIdx(conf, fullpath)
-
-
-  proc addModuleDep*(incr: var IncrementalCtx; conf: ConfigRef;
-                     module, fileIdx: FileIndex;
-                     isIncludeFile: bool) =
-    if conf.symbolFiles != v2Sf: return
-
-    let a = toDbFileId(incr, conf, module)
-    let b = toDbFileId(incr, conf, fileIdx)
-
-    incr.db.exec(sql"insert into deps(module, dependency, isIncludeFile) values (?, ?, ?)",
-      a, b, ord(isIncludeFile))
-
-  # --------------- Database model ---------------------------------------------
-
-  proc createDb*(db: DbConn) =
-    db.exec(sql"""
-      create table if not exists controlblock(
-        idgen integer not null
-      );
-    """)
-
-    db.exec(sql"""
-      create table if not exists config(
-        config varchar(8000) not null
-      );
-    """)
-
-    db.exec(sql"""
-      create table if not exists filenames(
-        id integer primary key,
-        fullpath varchar(8000) not null,
-        fullHash varchar(256) not null
-      );
-    """)
-    db.exec sql"create index if not exists FilenameIx on filenames(fullpath);"
-
-    db.exec(sql"""
-      create table if not exists modules(
-        id integer primary key,
-        nimid integer not null,
-        fullpath varchar(8000) not null,
-        interfHash varchar(256) not null,
-        fullHash varchar(256) not null,
-
-        created timestamp not null default (DATETIME('now'))
-      );""")
-    db.exec(sql"""create unique index if not exists SymNameIx on modules(fullpath);""")
-
-    db.exec(sql"""
-      create table if not exists deps(
-        id integer primary key,
-        module integer not null,
-        dependency integer not null,
-        isIncludeFile integer not null,
-        foreign key (module) references filenames(id),
-        foreign key (dependency) references filenames(id)
-      );""")
-    db.exec(sql"""create index if not exists DepsIx on deps(module);""")
-
-    db.exec(sql"""
-      create table if not exists types(
-        id integer primary key,
-        nimid integer not null,
-        module integer not null,
-        data blob not null,
-        foreign key (module) references module(id)
-      );
-    """)
-    db.exec sql"create index TypeByModuleIdx on types(module);"
-    db.exec sql"create index TypeByNimIdIdx on types(nimid);"
-
-    db.exec(sql"""
-      create table if not exists syms(
-        id integer primary key,
-        nimid integer not null,
-        module integer not null,
-        name varchar(256) not null,
-        data blob not null,
-        exported int not null,
-        foreign key (module) references module(id)
-      );
-    """)
-    db.exec sql"create index if not exists SymNameIx on syms(name);"
-    db.exec sql"create index SymByNameAndModuleIdx on syms(name, module);"
-    db.exec sql"create index SymByModuleIdx on syms(module);"
-    db.exec sql"create index SymByNimIdIdx on syms(nimid);"
-
-
-    db.exec(sql"""
-      create table if not exists toplevelstmts(
-        id integer primary key,
-        position integer not null,
-        module integer not null,
-        data blob not null,
-        foreign key (module) references module(id)
-      );
-    """)
-    db.exec sql"create index TopLevelStmtByModuleIdx on toplevelstmts(module);"
-    db.exec sql"create index TopLevelStmtByPositionIdx on toplevelstmts(position);"
-
-    db.exec(sql"""
-      create table if not exists statics(
-        id integer primary key,
-        module integer not null,
-        data blob not null,
-        foreign key (module) references module(id)
-      );
-    """)
-    db.exec sql"create index StaticsByModuleIdx on toplevelstmts(module);"
-    db.exec sql"insert into controlblock(idgen) values (0)"
-
-
-else:
-  type
-    IncrementalCtx* = object
-
-  template init*(incr: IncrementalCtx) = discard
-
-  template addModuleDep*(incr: var IncrementalCtx; conf: ConfigRef;
-                     module, fileIdx: FileIndex;
-                     isIncludeFile: bool) =
-    discard
diff --git a/compiler/index.nim b/compiler/index.nim
new file mode 100644
index 000000000..2c2a34fb5
--- /dev/null
+++ b/compiler/index.nim
@@ -0,0 +1,18 @@
+##[
+This module only exists to generate docs for the compiler.
+
+## links
+* [main docs](../lib.html)
+* [compiler user guide](../nimc.html)
+* [Internals of the Nim Compiler](../intern.html)
+]##
+
+#[
+note: this is named `index` so that navigating to https://nim-lang.github.io/Nim/compiler/
+will work.
+
+xxx this should also import other modules, not transitively imported by `compiler/nim.nim`,
+eg `evalffi`, otherwise these aren't shown. A glob could be used at CT.
+]#
+
+import nim
diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim
new file mode 100644
index 000000000..3dcc364a3
--- /dev/null
+++ b/compiler/injectdestructors.nim
@@ -0,0 +1,1284 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2017 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Injects destructor calls into Nim code as well as
+## an optimizer that optimizes copies to moves. This is implemented as an
+## AST to AST transformation so that every backend benefits from it.
+
+## See doc/destructors.rst for a spec of the implemented rewrite rules
+
+import
+  ast, astalgo, msgs, renderer, magicsys, types, idents,
+  options, lowerings, modulegraphs,
+  lineinfos, parampatterns, sighashes, liftdestructors, optimizer,
+  varpartitions, aliasanalysis, dfa, wordrecg
+
+import std/[strtabs, tables, strutils, intsets]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+from trees import exprStructuralEquivalent, getRoot, whichPragma
+
+type
+  Con = object
+    owner: PSym
+    when true:
+      g: ControlFlowGraph
+    graph: ModuleGraph
+    inLoop, inSpawn, inLoopCond: int
+    uninit: IntSet # set of uninit'ed vars
+    idgen: IdGenerator
+    body: PNode
+    otherUsage: TLineInfo
+    inUncheckedAssignSection: int
+    inEnsureMove: int
+
+  Scope = object # we do scope-based memory management.
+    # a scope is comparable to an nkStmtListExpr like
+    # (try: statements; dest = y(); finally: destructors(); dest)
+    vars: seq[PSym]
+    wasMoved: seq[PNode]
+    final: seq[PNode] # finally section
+    locals: seq[PSym]
+    body: PNode
+    needsTry: bool
+    parent: ptr Scope
+
+  ProcessMode = enum
+    normal
+    consumed
+    sinkArg
+
+const toDebug {.strdefine.} = ""
+when toDebug.len > 0:
+  var shouldDebug = false
+
+template dbg(body) =
+  when toDebug.len > 0:
+    if shouldDebug:
+      body
+
+proc hasDestructor(c: Con; t: PType): bool {.inline.} =
+  result = ast.hasDestructor(t)
+  when toDebug.len > 0:
+    # for more effective debugging
+    if not result and c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
+      assert(not containsGarbageCollectedRef(t))
+
+proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode =
+  let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.idgen, c.owner, info)
+  sym.typ = typ
+  s.vars.add(sym)
+  result = newSymNode(sym)
+
+proc nestedScope(parent: var Scope; body: PNode): Scope =
+  Scope(vars: @[], locals: @[], wasMoved: @[], final: @[], body: body, needsTry: false, parent: addr(parent))
+
+proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}; inReturn = false): PNode
+
+type
+  MoveOrCopyFlag = enum
+    IsDecl, IsExplicitSink, IsReturn
+
+proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; flags: set[MoveOrCopyFlag] = {}): PNode
+
+when false:
+  var
+    perfCounters: array[InstrKind, int]
+
+  proc showCounters*() =
+    for i in low(InstrKind)..high(InstrKind):
+      echo "INSTR ", i, " ", perfCounters[i]
+
+proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
+  let root = parampatterns.exprRoot(n, allowCalls=false)
+  if root == nil: return false
+
+  var s = addr(scope)
+  while s != nil:
+    if s.locals.contains(root): break
+    s = s.parent
+
+  c.g = constructCfg(c.owner, if s != nil: s.body else: c.body, root)
+  dbg:
+    echo "\n### ", c.owner.name.s, ":\nCFG:"
+    echoCfg(c.g)
+    #echo c.body
+
+  var j = 0
+  while j < c.g.len:
+    if c.g[j].kind == use and c.g[j].n == n: break
+    inc j
+  c.otherUsage = unknownLineInfo
+  if j < c.g.len:
+    var pcs = @[j+1]
+    var marked = initIntSet()
+    result = true
+    while pcs.len > 0:
+      var pc = pcs.pop()
+      if not marked.contains(pc):
+        let oldPc = pc
+        while pc < c.g.len:
+          dbg:
+            echo "EXEC ", c.g[pc].kind, " ", pc, " ", n
+          when false:
+            inc perfCounters[c.g[pc].kind]
+          case c.g[pc].kind
+          of loop:
+            let back = pc + c.g[pc].dest
+            if not marked.containsOrIncl(back):
+              pc = back
+            else:
+              break
+          of goto:
+            pc = pc + c.g[pc].dest
+          of fork:
+            if not marked.contains(pc+1):
+              pcs.add pc + 1
+            pc = pc + c.g[pc].dest
+          of use:
+            if c.g[pc].n.aliases(n) != no or n.aliases(c.g[pc].n) != no:
+              c.otherUsage = c.g[pc].n.info
+              return false
+            inc pc
+          of def:
+            if c.g[pc].n.aliases(n) == yes:
+              # the path leads to a redefinition of 's' --> sink 's'.
+              break
+            elif n.aliases(c.g[pc].n) != no:
+              # only partially writes to 's' --> can't sink 's', so this def reads 's'
+              # or maybe writes to 's' --> can't sink 's'
+              c.otherUsage = c.g[pc].n.info
+              return false
+            inc pc
+        marked.incl oldPc
+  else:
+    result = false
+
+proc isLastRead(n: PNode; c: var Con; s: var Scope): bool =
+  # bug #23354; an object type could have a non-trival assignements when it is passed to a sink parameter
+  if not hasDestructor(c, n.typ) and (n.typ.kind != tyObject or isTrival(getAttachedOp(c.graph, n.typ, attachedAsgn))): return true
+
+  let m = skipConvDfa(n)
+  result = (m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or
+      isLastReadImpl(n, c, s)
+
+proc isFirstWrite(n: PNode; c: var Con): bool =
+  let m = skipConvDfa(n)
+  result = nfFirstWrite in m.flags
+
+proc isCursor(n: PNode): bool =
+  case n.kind
+  of nkSym:
+    sfCursor in n.sym.flags
+  of nkDotExpr:
+    isCursor(n[1])
+  of nkCheckedFieldExpr:
+    isCursor(n[0])
+  else:
+    false
+
+template isUnpackedTuple(n: PNode): bool =
+  ## we move out all elements of unpacked tuples,
+  ## hence unpacked tuples themselves don't need to be destroyed
+  ## except it's already a cursor
+  (n.kind == nkSym and n.sym.kind == skTemp and
+   n.sym.typ.kind == tyTuple and sfCursor notin n.sym.flags)
+
+proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string; inferredFromCopy = false) =
+  var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">"
+  if inferredFromCopy:
+    m.add ", which is inferred from unavailable '=copy'"
+
+  if (opname == "=" or opname == "=copy" or opname == "=dup") and ri != nil:
+    m.add "; requires a copy because it's not the last read of '"
+    m.add renderTree(ri)
+    m.add '\''
+    if c.otherUsage != unknownLineInfo:
+       # ri.comment.startsWith('\n'):
+      m.add "; another read is done here: "
+      m.add c.graph.config $ c.otherUsage
+      #m.add c.graph.config $ c.g[parseInt(ri.comment[1..^1])].n.info
+    elif ri.kind == nkSym and ri.sym.kind == skParam and not isSinkType(ri.sym.typ):
+      m.add "; try to make "
+      m.add renderTree(ri)
+      m.add " a 'sink' parameter"
+  m.add "; routine: "
+  m.add c.owner.name.s
+  #m.add "\n\n"
+  #m.add renderTree(c.body, {renderIds})
+  localError(c.graph.config, ri.info, errGenerated, m)
+
+proc makePtrType(c: var Con, baseType: PType): PType =
+  result = newType(tyPtr, c.idgen, c.owner)
+  addSonSkipIntLit(result, baseType, c.idgen)
+
+proc genOp(c: var Con; op: PSym; dest: PNode): PNode =
+  var addrExp: PNode
+  if op.typ != nil and op.typ.signatureLen > 1 and op.typ.firstParamType.kind != tyVar:
+    addrExp = dest
+  else:
+    addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
+    addrExp.add(dest)
+  result = newTree(nkCall, newSymNode(op), addrExp)
+
+proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
+  var op = getAttachedOp(c.graph, t, kind)
+  if op == nil or op.ast.isGenericRoutine:
+    # give up and find the canonical type instead:
+    let h = sighashes.hashType(t, c.graph.config, {CoType, CoConsiderOwned, CoDistinct})
+    let canon = c.graph.canonTypes.getOrDefault(h)
+    if canon != nil:
+      op = getAttachedOp(c.graph, canon, kind)
+  if op == nil:
+    #echo dest.typ.id
+    globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] &
+      "' operator not found for type " & typeToString(t))
+  elif op.ast.isGenericRoutine:
+    globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] &
+      "' operator is generic")
+  dbg:
+    if kind == attachedDestructor:
+      echo "destructor is ", op.id, " ", op.ast
+  if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
+  c.genOp(op, dest)
+
+proc genDestroy(c: var Con; dest: PNode): PNode =
+  let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
+  result = c.genOp(t, attachedDestructor, dest, nil)
+
+proc canBeMoved(c: Con; t: PType): bool {.inline.} =
+  let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
+  if optOwnedRefs in c.graph.config.globalOptions:
+    result = t.kind != tyRef and getAttachedOp(c.graph, t, attachedSink) != nil
+  else:
+    result = getAttachedOp(c.graph, t, attachedSink) != nil
+
+proc isNoInit(dest: PNode): bool {.inline.} =
+  result = dest.kind == nkSym and sfNoInit in dest.sym.flags
+
+proc deepAliases(dest, ri: PNode): bool =
+  case ri.kind
+  of nkCallKinds, nkStmtListExpr, nkBracket, nkTupleConstr, nkObjConstr,
+     nkCast, nkConv, nkObjUpConv, nkObjDownConv:
+    for r in ri:
+      if deepAliases(dest, r): return true
+    return false
+  else:
+    return aliases(dest, ri) != no
+
+proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
+  if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or IsDecl in flags or
+      (isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or
+      isNoInit(dest) or IsReturn in flags:
+    # optimize sink call into a bitwise memcopy
+    result = newTree(nkFastAsgn, dest, ri)
+  else:
+    let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
+    if getAttachedOp(c.graph, t, attachedSink) != nil:
+      result = c.genOp(t, attachedSink, dest, ri)
+      result.add ri
+    else:
+      # the default is to use combination of `=destroy(dest)` and
+      # and copyMem(dest, source). This is efficient.
+      if deepAliases(dest, ri):
+        # consider: x = x + y, it is wrong to destroy the destination first!
+        # tmp to support self assignments
+        let tmp = c.getTemp(s, dest.typ, dest.info)
+        result = newTree(nkStmtList, newTree(nkFastAsgn, tmp, dest), newTree(nkFastAsgn, dest, ri),
+                         c.genDestroy(tmp))
+      else:
+        result = newTree(nkStmtList, c.genDestroy(dest), newTree(nkFastAsgn, dest, ri))
+
+proc isCriticalLink(dest: PNode): bool {.inline.} =
+  #[
+  Lins's idea that only "critical" links can introduce a cycle. This is
+  critical for the performance guarantees that we strive for: If you
+  traverse a data structure, no tracing will be performed at all.
+  ORC is about this promise: The GC only touches the memory that the
+  mutator touches too.
+
+  These constructs cannot possibly create cycles::
+
+    local = ...
+
+    new(x)
+    dest = ObjectConstructor(field: noalias(dest))
+
+  But since 'ObjectConstructor' is already moved into 'dest' all we really have
+  to look for is assignments to local variables.
+  ]#
+  result = dest.kind != nkSym
+
+proc finishCopy(c: var Con; result, dest: PNode; flags: set[MoveOrCopyFlag]; isFromSink: bool) =
+  if c.graph.config.selectedGC == gcOrc and IsExplicitSink notin flags:
+    # add cyclic flag, but not to sink calls, which IsExplicitSink generates
+    let t = dest.typ.skipTypes(tyUserTypeClasses + {tyGenericInst, tyAlias, tySink, tyDistinct})
+    if cyclicType(c.graph, t):
+      result.add boolLit(c.graph, result.info, isFromSink or isCriticalLink(dest))
+
+proc genMarkCyclic(c: var Con; result, dest: PNode) =
+  if c.graph.config.selectedGC == gcOrc:
+    let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
+    if cyclicType(c.graph, t):
+      if t.kind == tyRef:
+        result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, dest)
+      else:
+        let xenv = genBuiltin(c.graph, c.idgen, mAccessEnv, "accessEnv", dest)
+        xenv.typ = getSysType(c.graph, dest.info, tyPointer)
+        result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, xenv)
+
+proc genCopyNoCheck(c: var Con; dest, ri: PNode; a: TTypeAttachedOp): PNode =
+  let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
+  result = c.genOp(t, a, dest, ri)
+  assert ri.typ != nil
+
+proc genCopy(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag]): PNode =
+  if c.inEnsureMove > 0:
+    localError(c.graph.config, ri.info, errFailedMove, "cannot move '" & $ri &
+                                                      "', which introduces an implicit copy")
+  let t = dest.typ
+  if tfHasOwned in t.flags and ri.kind != nkNilLit:
+    # try to improve the error message here:
+    if IsExplicitSink in flags:
+      c.checkForErrorPragma(t, ri, "=sink")
+    else:
+      c.checkForErrorPragma(t, ri, "=copy")
+  let a = if IsExplicitSink in flags: attachedSink else: attachedAsgn
+  result = c.genCopyNoCheck(dest, ri, a)
+  assert ri.typ != nil
+
+proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
+  # discriminator is ordinal value that doesn't need sink destroy
+  # but fields within active case branch might need destruction
+
+  # tmp to support self assignments
+  let tmp = c.getTemp(s, n[1].typ, n.info)
+
+  result = newTree(nkStmtList)
+  result.add newTree(nkFastAsgn, tmp, p(n[1], c, s, consumed))
+  result.add p(n[0], c, s, normal)
+
+  let le = p(n[0], c, s, normal)
+  let leDotExpr = if le.kind == nkCheckedFieldExpr: le[0] else: le
+  let objType = leDotExpr[0].typ
+
+  if hasDestructor(c, objType):
+    if getAttachedOp(c.graph, objType, attachedDestructor) != nil and
+        sfOverridden in getAttachedOp(c.graph, objType, attachedDestructor).flags:
+      localError(c.graph.config, n.info, errGenerated, """Assignment to discriminant for objects with user defined destructor is not supported, object must have default destructor.
+It is best to factor out piece of object that needs custom destructor into separate object or not use discriminator assignment""")
+      result.add newTree(nkFastAsgn, le, tmp)
+      return
+
+    # generate: if le != tmp: `=destroy`(le)
+    if c.inUncheckedAssignSection != 0:
+      let branchDestructor = produceDestructorForDiscriminator(c.graph, objType, leDotExpr[1].sym, n.info, c.idgen)
+      let cond = newNodeIT(nkInfix, n.info, getSysType(c.graph, unknownLineInfo, tyBool))
+      cond.add newSymNode(getMagicEqSymForType(c.graph, le.typ, n.info))
+      cond.add le
+      cond.add tmp
+      let notExpr = newNodeIT(nkPrefix, n.info, getSysType(c.graph, unknownLineInfo, tyBool))
+      notExpr.add newSymNode(createMagic(c.graph, c.idgen, "not", mNot))
+      notExpr.add cond
+      result.add newTree(nkIfStmt, newTree(nkElifBranch, notExpr, c.genOp(branchDestructor, le)))
+  result.add newTree(nkFastAsgn, le, tmp)
+
+proc genWasMoved(c: var Con, n: PNode): PNode =
+  let typ = n.typ.skipTypes({tyGenericInst, tyAlias, tySink})
+  let op = getAttachedOp(c.graph, n.typ, attachedWasMoved)
+  if op != nil:
+    if sfError in op.flags:
+      c.checkForErrorPragma(n.typ, n, "=wasMoved")
+    result = genOp(c, op, n)
+  else:
+    result = newNodeI(nkCall, n.info)
+    result.add(newSymNode(createMagic(c.graph, c.idgen, "`=wasMoved`", mWasMoved)))
+    result.add copyTree(n) #mWasMoved does not take the address
+    #if n.kind != nkSym:
+    #  message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")")
+
+proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode =
+  result = newNodeI(nkCall, info)
+  result.add(newSymNode(createMagic(c.graph, c.idgen, "default", mDefault)))
+  result.typ = t
+
+proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
+  # generate: (let tmp = v; reset(v); tmp)
+  if (not hasDestructor(c, n.typ)) and c.inEnsureMove == 0:
+    assert n.kind != nkSym or not hasDestructor(c, n.sym.typ) or
+          (n.typ.kind == tyPtr and n.sym.typ.kind == tyRef)
+      # bug #23505; transformed by `transf`: addr (deref ref) -> ptr
+      # we know it's really a pointer; so here we assign it directly
+    result = copyTree(n)
+  else:
+    result = newNodeIT(nkStmtListExpr, n.info, n.typ)
+
+    var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), c.idgen, c.owner, n.info)
+    temp.typ = n.typ
+    var v = newNodeI(nkLetSection, n.info)
+    let tempAsNode = newSymNode(temp)
+
+    var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
+    vpart[0] = tempAsNode
+    vpart[1] = newNodeI(nkEmpty, tempAsNode.info)
+    vpart[2] = n
+    v.add(vpart)
+
+    result.add v
+    let nn = skipConv(n)
+    if hasDestructor(c, n.typ):
+      c.genMarkCyclic(result, nn)
+    let wasMovedCall = c.genWasMoved(nn)
+    result.add wasMovedCall
+    result.add tempAsNode
+
+proc isCapturedVar(n: PNode): bool =
+  let root = getRoot(n)
+  if root != nil: result = root.name.s[0] == ':'
+  else: result = false
+
+proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
+  result = newNodeIT(nkStmtListExpr, n.info, n.typ)
+  let nTyp = n.typ.skipTypes(tyUserTypeClasses)
+  let tmp = c.getTemp(s, nTyp, n.info)
+  if hasDestructor(c, nTyp):
+    let typ = nTyp.skipTypes({tyGenericInst, tyAlias, tySink})
+    let op = getAttachedOp(c.graph, typ, attachedDup)
+    if op != nil and tfHasOwned notin typ.flags:
+      if sfError in op.flags:
+        c.checkForErrorPragma(nTyp, n, "=dup")
+      else:
+        let copyOp = getAttachedOp(c.graph, typ, attachedAsgn)
+        if copyOp != nil and sfError in copyOp.flags and
+           sfOverridden notin op.flags:
+          c.checkForErrorPragma(nTyp, n, "=dup", inferredFromCopy = true)
+
+      let src = p(n, c, s, normal)
+      var newCall = newTreeIT(nkCall, src.info, src.typ,
+            newSymNode(op),
+            src)
+      c.finishCopy(newCall, n, {}, isFromSink = true)
+      result.add newTreeI(nkFastAsgn,
+          src.info, tmp,
+          newCall
+      )
+    else:
+      result.add c.genWasMoved(tmp)
+      var m = c.genCopy(tmp, n, {})
+      m.add p(n, c, s, normal)
+      c.finishCopy(m, n, {}, isFromSink = true)
+      result.add m
+    if isLValue(n) and not isCapturedVar(n) and nTyp.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
+      message(c.graph.config, n.info, hintPerformance,
+        ("passing '$1' to a sink parameter introduces an implicit copy; " &
+        "if possible, rearrange your program's control flow to prevent it") % $n)
+    if c.inEnsureMove > 0:
+      localError(c.graph.config, n.info, errFailedMove,
+        ("cannot move '$1', passing '$1' to a sink parameter introduces an implicit copy") % $n)
+  else:
+    if c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
+      assert(not containsManagedMemory(nTyp))
+    if nTyp.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
+      localError(c.graph.config, n.info, "cannot create an implicit openArray copy to be passed to a sink parameter")
+    result.add newTree(nkAsgn, tmp, p(n, c, s, normal))
+  # Since we know somebody will take over the produced copy, there is
+  # no need to destroy it.
+  result.add tmp
+
+proc isDangerousSeq(t: PType): bool {.inline.} =
+  let t = t.skipTypes(abstractInst)
+  result = t.kind == tySequence and tfHasOwned notin t.elementType.flags
+
+proc containsConstSeq(n: PNode): bool =
+  if n.kind == nkBracket and n.len > 0 and n.typ != nil and isDangerousSeq(n.typ):
+    return true
+  result = false
+  case n.kind
+  of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast:
+    result = containsConstSeq(n[1])
+  of nkObjConstr, nkClosure:
+    for i in 1..<n.len:
+      if containsConstSeq(n[i]): return true
+  of nkCurly, nkBracket, nkPar, nkTupleConstr:
+    for son in n:
+      if containsConstSeq(son): return true
+  else: discard
+
+proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode =
+  # it can happen that we need to destroy expression contructors
+  # like [], (), closures explicitly in order to not leak them.
+  if arg.typ != nil and hasDestructor(c, arg.typ):
+    # produce temp creation for (fn, env). But we need to move 'env'?
+    # This was already done in the sink parameter handling logic.
+    result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
+    let tmp = c.getTemp(s, arg.typ, arg.info)
+    result.add c.genSink(s, tmp, arg, {IsDecl})
+    result.add tmp
+    s.final.add c.genDestroy(tmp)
+  else:
+    result = arg
+
+proc cycleCheck(n: PNode; c: var Con) =
+  if c.graph.config.selectedGC notin {gcArc, gcAtomicArc}: return
+  var value = n[1]
+  if value.kind == nkClosure:
+    value = value[1]
+  if value.kind == nkNilLit: return
+  let destTyp = n[0].typ.skipTypes(abstractInst)
+  if destTyp.kind != tyRef and not (destTyp.kind == tyProc and destTyp.callConv == ccClosure):
+    return
+
+  var x = n[0]
+  var field: PNode = nil
+  while true:
+    if x.kind == nkDotExpr:
+      field = x[1]
+      if field.kind == nkSym and sfCursor in field.sym.flags: return
+      x = x[0]
+    elif x.kind in {nkBracketExpr, nkCheckedFieldExpr, nkDerefExpr, nkHiddenDeref}:
+      x = x[0]
+    else:
+      break
+    if exprStructuralEquivalent(x, value, strictSymEquality = true):
+      let msg =
+        if field != nil:
+          "'$#' creates an uncollectable ref cycle; annotate '$#' with .cursor" % [$n, $field]
+        else:
+          "'$#' creates an uncollectable ref cycle" % [$n]
+      message(c.graph.config, n.info, warnCycleCreated, msg)
+      break
+
+proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; res: PNode) =
+  # move the variable declaration to the top of the frame:
+  s.vars.add v.sym
+  if isUnpackedTuple(v):
+    if c.inLoop > 0:
+      # unpacked tuple needs reset at every loop iteration
+      res.add newTree(nkFastAsgn, v, genDefaultCall(v.typ, c, v.info))
+  elif sfThread notin v.sym.flags and sfCursor notin v.sym.flags:
+    # do not destroy thread vars for now at all for consistency.
+    if {sfGlobal, sfPure} <= v.sym.flags or sfGlobal in v.sym.flags and s.parent == nil:
+      c.graph.globalDestructors.add c.genDestroy(v)
+    else:
+      s.final.add c.genDestroy(v)
+
+proc processScope(c: var Con; s: var Scope; ret: PNode): PNode =
+  result = newNodeI(nkStmtList, ret.info)
+  if s.vars.len > 0:
+    let varSection = newNodeI(nkVarSection, ret.info)
+    for tmp in s.vars:
+      varSection.add newTree(nkIdentDefs, newSymNode(tmp), newNodeI(nkEmpty, ret.info),
+                                                           newNodeI(nkEmpty, ret.info))
+    result.add varSection
+  if s.wasMoved.len > 0 or s.final.len > 0:
+    let finSection = newNodeI(nkStmtList, ret.info)
+    for m in s.wasMoved: finSection.add m
+    for i in countdown(s.final.high, 0): finSection.add s.final[i]
+    if s.needsTry:
+      result.add newTryFinally(ret, finSection)
+    else:
+      result.add ret
+      result.add finSection
+  else:
+    result.add ret
+
+  if s.parent != nil: s.parent[].needsTry = s.parent[].needsTry or s.needsTry
+
+template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: untyped, tmpFlags: TSymFlags): PNode =
+  assert not ret.typ.isEmptyType
+  var result = newNodeIT(nkStmtListExpr, ret.info, ret.typ)
+  # There is a possibility to do this check: s.wasMoved.len > 0 or s.final.len > 0
+  # later and use it to eliminate the temporary when theres no need for it, but its
+  # tricky because you would have to intercept moveOrCopy at a certain point
+  let tmp = c.getTemp(s.parent[], ret.typ, ret.info)
+  tmp.sym.flags = tmpFlags
+  let cpy = if hasDestructor(c, ret.typ) and
+                ret.typ.kind notin {tyOpenArray, tyVarargs}:
+                # bug #23247 we don't own the data, so it's harmful to destroy it
+              s.parent[].final.add c.genDestroy(tmp)
+              moveOrCopy(tmp, ret, c, s, {IsDecl})
+            else:
+              newTree(nkFastAsgn, tmp, p(ret, c, s, normal))
+
+  if s.vars.len > 0:
+    let varSection = newNodeI(nkVarSection, ret.info)
+    for tmp in s.vars:
+      varSection.add newTree(nkIdentDefs, newSymNode(tmp), newNodeI(nkEmpty, ret.info),
+                                                           newNodeI(nkEmpty, ret.info))
+    result.add varSection
+  let finSection = newNodeI(nkStmtList, ret.info)
+  for m in s.wasMoved: finSection.add m
+  for i in countdown(s.final.high, 0): finSection.add s.final[i]
+  if s.needsTry:
+    result.add newTryFinally(newTree(nkStmtListExpr, cpy, processCall(tmp, s.parent[])), finSection)
+  else:
+    result.add cpy
+    result.add finSection
+    result.add processCall(tmp, s.parent[])
+
+  if s.parent != nil: s.parent[].needsTry = s.parent[].needsTry or s.needsTry
+
+  result
+
+template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
+                           tmpFlags = {sfSingleUsedTemp}) =
+  template maybeVoid(child, s): untyped =
+    if isEmptyType(child.typ): p(child, c, s, normal)
+    else: processCall(child, s)
+
+  case n.kind
+  of nkStmtList, nkStmtListExpr:
+    # a statement list does not open a new scope
+    if n.len == 0: return n
+    result = copyNode(n)
+    for i in 0..<n.len-1:
+      result.add p(n[i], c, s, normal)
+    result.add maybeVoid(n[^1], s)
+
+  of nkCaseStmt:
+    result = copyNode(n)
+    result.add p(n[0], c, s, normal)
+    for i in 1..<n.len:
+      let it = n[i]
+      assert it.kind in {nkOfBranch, nkElse}
+
+      var branch = shallowCopy(it)
+      for j in 0 ..< it.len-1:
+        branch[j] = copyTree(it[j])
+      var ofScope = nestedScope(s, it.lastSon)
+      branch[^1] = if n.typ.isEmptyType or it[^1].typ.isEmptyType or willProduceStmt:
+                     processScope(c, ofScope, maybeVoid(it[^1], ofScope))
+                   else:
+                     processScopeExpr(c, ofScope, it[^1], processCall, tmpFlags)
+      result.add branch
+
+  of nkWhileStmt:
+    inc c.inLoop
+    inc c.inLoopCond
+    result = copyNode(n)
+    result.add p(n[0], c, s, normal)
+    dec c.inLoopCond
+    var bodyScope = nestedScope(s, n[1])
+    let bodyResult = p(n[1], c, bodyScope, normal)
+    result.add processScope(c, bodyScope, bodyResult)
+    dec c.inLoop
+
+  of nkParForStmt:
+    inc c.inLoop
+    result = shallowCopy(n)
+    let last = n.len-1
+    for i in 0..<last-1:
+      result[i] = n[i]
+    result[last-1] = p(n[last-1], c, s, normal)
+    var bodyScope = nestedScope(s, n[1])
+    let bodyResult = p(n[last], c, bodyScope, normal)
+    result[last] = processScope(c, bodyScope, bodyResult)
+    dec c.inLoop
+
+  of nkBlockStmt, nkBlockExpr:
+    result = copyNode(n)
+    result.add n[0]
+    var bodyScope = nestedScope(s, n[1])
+    result.add if n[1].typ.isEmptyType or willProduceStmt:
+                 processScope(c, bodyScope, processCall(n[1], bodyScope))
+               else:
+                 processScopeExpr(c, bodyScope, n[1], processCall, tmpFlags)
+
+  of nkIfStmt, nkIfExpr:
+    result = copyNode(n)
+    for i in 0..<n.len:
+      let it = n[i]
+      var branch = shallowCopy(it)
+      var branchScope = nestedScope(s, it.lastSon)
+      if it.kind in {nkElifBranch, nkElifExpr}:
+        #Condition needs to be destroyed outside of the condition/branch scope
+        branch[0] = p(it[0], c, s, normal)
+
+      branch[^1] = if n.typ.isEmptyType or it[^1].typ.isEmptyType or willProduceStmt:
+                     processScope(c, branchScope, maybeVoid(it[^1], branchScope))
+                   else:
+                     processScopeExpr(c, branchScope, it[^1], processCall, tmpFlags)
+      result.add branch
+
+  of nkTryStmt:
+    result = copyNode(n)
+    var tryScope = nestedScope(s, n[0])
+    result.add if n[0].typ.isEmptyType or willProduceStmt:
+                 processScope(c, tryScope, maybeVoid(n[0], tryScope))
+               else:
+                 processScopeExpr(c, tryScope, n[0], maybeVoid, tmpFlags)
+
+    for i in 1..<n.len:
+      let it = n[i]
+      var branch = copyTree(it)
+      var branchScope = nestedScope(s, it[^1])
+      branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt or it.kind == nkFinally:
+                     processScope(c, branchScope, if it.kind == nkFinally: p(it[^1], c, branchScope, normal)
+                                                  else: maybeVoid(it[^1], branchScope))
+                   else:
+                     processScopeExpr(c, branchScope, it[^1], processCall, tmpFlags)
+      result.add branch
+
+  of nkWhen: # This should be a "when nimvm" node.
+    result = copyTree(n)
+    result[1][0] = processCall(n[1][0], s)
+
+  of nkPragmaBlock:
+    var inUncheckedAssignSection = 0
+    let pragmaList = n[0]
+    for pi in pragmaList:
+      if whichPragma(pi) == wCast:
+        case whichPragma(pi[1])
+        of wUncheckedAssign:
+          inUncheckedAssignSection = 1
+        else:
+          discard
+    result = shallowCopy(n)
+    inc c.inUncheckedAssignSection, inUncheckedAssignSection
+    for i in 0 ..< n.len-1:
+      result[i] = p(n[i], c, s, normal)
+    result[^1] = maybeVoid(n[^1], s)
+    dec c.inUncheckedAssignSection, inUncheckedAssignSection
+
+  else:
+    result = nil
+    assert(false)
+
+proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
+  if optOwnedRefs in c.graph.config.globalOptions and n[0].kind != nkEmpty:
+    if n[0].kind in nkCallKinds:
+      let call = p(n[0], c, s, normal)
+      result = copyNode(n)
+      result.add call
+    else:
+      let tmp = c.getTemp(s, n[0].typ, n.info)
+      var m = c.genCopyNoCheck(tmp, n[0], attachedAsgn)
+      m.add p(n[0], c, s, normal)
+      c.finishCopy(m, n[0], {}, isFromSink = false)
+      result = newTree(nkStmtList, c.genWasMoved(tmp), m)
+      var toDisarm = n[0]
+      if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon
+      if toDisarm.kind == nkSym and toDisarm.sym.owner == c.owner:
+        result.add c.genWasMoved(toDisarm)
+      result.add newTree(nkRaiseStmt, tmp)
+  else:
+    result = copyNode(n)
+    if n[0].kind != nkEmpty:
+      result.add p(n[0], c, s, sinkArg)
+    else:
+      result.add copyNode(n[0])
+  s.needsTry = true
+
+template isCustomDestructor(c: Con, t: PType): bool =
+  hasDestructor(c, t) and
+          getAttachedOp(c.graph, t, attachedDestructor) != nil and
+          sfOverridden in getAttachedOp(c.graph, t, attachedDestructor).flags
+
+proc hasCustomDestructor(c: Con, t: PType): bool =
+  result = isCustomDestructor(c, t)
+  var obj = t
+  while obj.baseClass != nil:
+    obj = skipTypes(obj.baseClass, abstractPtrs)
+    result = result or isCustomDestructor(c, obj)
+
+proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}; inReturn = false): PNode =
+  if n.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt,
+                nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt, nkParForStmt, nkTryStmt, nkPragmaBlock}:
+    template process(child, s): untyped = p(child, c, s, mode)
+    handleNestedTempl(n, process, tmpFlags = tmpFlags)
+  elif mode == sinkArg:
+    if n.containsConstSeq:
+      # const sequences are not mutable and so we need to pass a copy to the
+      # sink parameter (bug #11524). Note that the string implementation is
+      # different and can deal with 'const string sunk into var'.
+      result = passCopyToSink(n, c, s)
+    elif n.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkClosure, nkNilLit} +
+         nkCallKinds + nkLiterals:
+      if n.kind in nkCallKinds and n[0].kind == nkSym:
+        if n[0].sym.magic == mEnsureMove:
+          inc c.inEnsureMove
+          result = p(n[1], c, s, sinkArg)
+          dec c.inEnsureMove
+        else:
+          result = p(n, c, s, consumed)
+      else:
+        result = p(n, c, s, consumed)
+    elif ((n.kind == nkSym and isSinkParam(n.sym)) or isAnalysableFieldAccess(n, c.owner)) and
+        isLastRead(n, c, s) and not (n.kind == nkSym and isCursor(n)):
+      # Sinked params can be consumed only once. We need to reset the memory
+      # to disable the destructor which we have not elided
+      result = destructiveMoveVar(n, c, s)
+    elif n.kind in {nkHiddenSubConv, nkHiddenStdConv, nkConv}:
+      result = copyTree(n)
+      if n.typ.skipTypes(abstractInst-{tyOwned}).kind != tyOwned and
+          n[1].typ.skipTypes(abstractInst-{tyOwned}).kind == tyOwned:
+        # allow conversions from owned to unowned via this little hack:
+        let nTyp = n[1].typ
+        n[1].typ = n.typ
+        result[1] = p(n[1], c, s, sinkArg)
+        result[1].typ = nTyp
+      else:
+        result[1] = p(n[1], c, s, sinkArg)
+    elif n.kind in {nkObjDownConv, nkObjUpConv}:
+      result = copyTree(n)
+      result[0] = p(n[0], c, s, sinkArg)
+    elif n.kind == nkCast and n.typ.skipTypes(abstractInst).kind in {tyString, tySequence}:
+      result = copyTree(n)
+      result[1] = p(n[1], c, s, sinkArg)
+    elif n.typ == nil:
+      # 'raise X' can be part of a 'case' expression. Deal with it here:
+      result = p(n, c, s, normal)
+    else:
+      # copy objects that are not temporary but passed to a 'sink' parameter
+      result = passCopyToSink(n, c, s)
+  else:
+    case n.kind
+    of nkBracket, nkTupleConstr, nkClosure, nkCurly:
+      # Let C(x) be the construction, 'x' the vector of arguments.
+      # C(x) either owns 'x' or it doesn't.
+      # If C(x) owns its data, we must consume C(x).
+      # If it doesn't own the data, it's harmful to destroy it (double frees etc).
+      # We have the freedom to choose whether it owns it or not so we are smart about it
+      # and we say, "if passed to a sink we demand C(x) to own its data"
+      # otherwise we say "C(x) is just some temporary storage, it doesn't own anything,
+      # don't destroy it"
+      # but if C(x) is a ref it MUST own its data since we must destroy it
+      # so then we have no choice but to use 'sinkArg'.
+      let m = if mode == normal: normal
+              else: sinkArg
+
+      result = copyTree(n)
+      for i in ord(n.kind == nkClosure)..<n.len:
+        if n[i].kind == nkExprColonExpr:
+          result[i][1] = p(n[i][1], c, s, m)
+        elif n[i].kind == nkRange:
+          result[i][0] = p(n[i][0], c, s, m)
+          result[i][1] = p(n[i][1], c, s, m)
+        else:
+          result[i] = p(n[i], c, s, m)
+    of nkObjConstr:
+      # see also the remark about `nkTupleConstr`.
+      let t = n.typ.skipTypes(abstractInst)
+      let isRefConstr = t.kind == tyRef
+      let m = if isRefConstr: sinkArg
+              elif mode == normal: normal
+              else: sinkArg
+
+      result = copyTree(n)
+      for i in 1..<n.len:
+        if n[i].kind == nkExprColonExpr:
+          let field = lookupFieldAgain(t, n[i][0].sym)
+          if field != nil and (sfCursor in field.flags or field.typ.kind in {tyOpenArray, tyVarargs}):
+            # don't sink fields with openarray types
+            result[i][1] = p(n[i][1], c, s, normal)
+          else:
+            result[i][1] = p(n[i][1], c, s, m)
+        else:
+          result[i] = p(n[i], c, s, m)
+      if mode == normal and (isRefConstr or hasCustomDestructor(c, t)):
+        result = ensureDestruction(result, n, c, s)
+    of nkCallKinds:
+      if n[0].kind == nkSym and n[0].sym.magic == mEnsureMove:
+        inc c.inEnsureMove
+        result = p(n[1], c, s, sinkArg)
+        dec c.inEnsureMove
+        return
+
+      let inSpawn = c.inSpawn
+      if n[0].kind == nkSym and n[0].sym.magic == mSpawn:
+        c.inSpawn.inc
+      elif c.inSpawn > 0:
+        c.inSpawn.dec
+
+      # bug #23907; skips tyGenericInst for generic callbacks
+      let parameters = if n[0].typ != nil: n[0].typ.skipTypes(abstractInst) else: n[0].typ
+      let L = if parameters != nil: parameters.signatureLen else: 0
+
+      when false:
+        var isDangerous = false
+        if n[0].kind == nkSym and n[0].sym.magic in {mOr, mAnd}:
+          inc c.inDangerousBranch
+          isDangerous = true
+
+      result = shallowCopy(n)
+      for i in 1..<n.len:
+        if i < L and isCompileTimeOnly(parameters[i]):
+          result[i] = n[i]
+        elif i < L and (isSinkTypeForParam(parameters[i]) or inSpawn > 0):
+          result[i] = p(n[i], c, s, sinkArg)
+        else:
+          result[i] = p(n[i], c, s, normal)
+
+      when false:
+        if isDangerous:
+          dec c.inDangerousBranch
+
+      if n[0].kind == nkSym and n[0].sym.magic in {mNew, mNewFinalize}:
+        result[0] = copyTree(n[0])
+        if c.graph.config.selectedGC in {gcHooks, gcArc, gcAtomicArc, gcOrc}:
+          let destroyOld = c.genDestroy(result[1])
+          result = newTree(nkStmtList, destroyOld, result)
+      else:
+        result[0] = p(n[0], c, s, normal)
+      if canRaise(n[0]): s.needsTry = true
+      if mode == normal:
+        if result.typ != nil and result.typ.kind notin {tyOpenArray, tyVarargs}:
+          # Returns of openarray types shouldn't be destroyed
+          # bug #19435; # bug #23247
+          result = ensureDestruction(result, n, c, s)
+    of nkDiscardStmt: # Small optimization
+      result = shallowCopy(n)
+      if n[0].kind != nkEmpty:
+        result[0] = p(n[0], c, s, normal)
+      else:
+        result[0] = copyNode(n[0])
+    of nkVarSection, nkLetSection:
+      # transform; var x = y to  var x; x op y  where op is a move or copy
+      result = newNodeI(nkStmtList, n.info)
+      for it in n:
+        var ri = it[^1]
+        if it.kind == nkVarTuple and hasDestructor(c, ri.typ):
+          for i in 0..<it.len-2:
+            if it[i].kind == nkSym: s.locals.add it[i].sym
+          let x = lowerTupleUnpacking(c.graph, it, c.idgen, c.owner)
+          result.add p(x, c, s, consumed)
+        elif it.kind == nkIdentDefs and hasDestructor(c, skipPragmaExpr(it[0]).typ):
+          for j in 0..<it.len-2:
+            let v = skipPragmaExpr(it[j])
+            if v.kind == nkSym:
+              if sfCompileTime in v.sym.flags: continue
+              s.locals.add v.sym
+              pVarTopLevel(v, c, s, result)
+            if ri.kind != nkEmpty:
+              result.add moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
+            elif ri.kind == nkEmpty and c.inLoop > 0:
+              let skipInit = v.kind == nkDotExpr and # Closure var
+                             sfNoInit in v[1].sym.flags
+              if not skipInit:
+                result.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, if v.kind == nkSym: {IsDecl} else: {})
+        else: # keep the var but transform 'ri':
+          var v = copyNode(n)
+          var itCopy = copyNode(it)
+          for j in 0..<it.len-1:
+            itCopy.add it[j]
+          var flags = {sfSingleUsedTemp}
+          if it.kind == nkIdentDefs and it.len == 3 and it[0].kind == nkSym and
+                                        sfGlobal in it[0].sym.flags:
+            flags.incl sfGlobal
+          itCopy.add p(it[^1], c, s, normal, tmpFlags = flags)
+          v.add itCopy
+          result.add v
+    of nkAsgn, nkFastAsgn, nkSinkAsgn:
+      if hasDestructor(c, n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda}:
+        if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}:
+          cycleCheck(n, c)
+        assert n[1].kind notin {nkAsgn, nkFastAsgn, nkSinkAsgn}
+        var flags = if n.kind == nkSinkAsgn: {IsExplicitSink} else: {}
+        if inReturn:
+          flags.incl(IsReturn)
+        result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s, flags)
+      elif isDiscriminantField(n[0]):
+        result = c.genDiscriminantAsgn(s, n)
+      else:
+        result = copyNode(n)
+        result.add p(n[0], c, s, mode)
+        result.add p(n[1], c, s, consumed)
+    of nkRaiseStmt:
+      result = pRaiseStmt(n, c, s)
+    of nkWhileStmt:
+      internalError(c.graph.config, n.info, "nkWhileStmt should have been handled earlier")
+      result = n
+    of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
+       nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
+       nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
+       nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
+       nkTypeOfExpr, nkMixinStmt, nkBindStmt:
+      result = n
+
+    of nkStringToCString, nkCStringToString, nkChckRangeF, nkChckRange64, nkChckRange:
+      result = shallowCopy(n)
+      for i in 0 ..< n.len:
+        result[i] = p(n[i], c, s, normal)
+      if n.typ != nil and hasDestructor(c, n.typ):
+        if mode == normal:
+          result = ensureDestruction(result, n, c, s)
+
+    of nkHiddenSubConv, nkHiddenStdConv, nkConv:
+      # we have an "ownership invariance" for all constructors C(x).
+      # See the comment for nkBracket construction. If the caller wants
+      # to own 'C(x)', it really wants to own 'x' too. If it doesn't,
+      # we need to destroy 'x' but the function call handling ensures that
+      # already.
+      result = copyTree(n)
+      if n.typ.skipTypes(abstractInst-{tyOwned}).kind != tyOwned and
+          n[1].typ.skipTypes(abstractInst-{tyOwned}).kind == tyOwned:
+        # allow conversions from owned to unowned via this little hack:
+        let nTyp = n[1].typ
+        n[1].typ = n.typ
+        result[1] = p(n[1], c, s, mode)
+        result[1].typ = nTyp
+      else:
+        result[1] = p(n[1], c, s, mode)
+
+    of nkObjDownConv, nkObjUpConv:
+      result = copyTree(n)
+      result[0] = p(n[0], c, s, mode)
+
+    of nkDotExpr:
+      result = shallowCopy(n)
+      result[0] = p(n[0], c, s, normal)
+      for i in 1 ..< n.len:
+        result[i] = n[i]
+      if mode == sinkArg and hasDestructor(c, n.typ):
+        if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c, s):
+          s.wasMoved.add c.genWasMoved(n)
+        else:
+          result = passCopyToSink(result, c, s)
+
+    of nkBracketExpr, nkAddr, nkHiddenAddr, nkDerefExpr, nkHiddenDeref:
+      result = shallowCopy(n)
+      for i in 0 ..< n.len:
+        result[i] = p(n[i], c, s, normal)
+      if mode == sinkArg and hasDestructor(c, n.typ):
+        if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c, s):
+          # consider 'a[(g; destroy(g); 3)]', we want to say 'wasMoved(a[3])'
+          # without the junk, hence 'c.genWasMoved(n)'
+          # and not 'c.genWasMoved(result)':
+          s.wasMoved.add c.genWasMoved(n)
+        else:
+          result = passCopyToSink(result, c, s)
+
+    of nkDefer, nkRange:
+      result = shallowCopy(n)
+      for i in 0 ..< n.len:
+        result[i] = p(n[i], c, s, normal)
+
+    of nkBreakStmt:
+      s.needsTry = true
+      result = n
+    of nkReturnStmt:
+      result = shallowCopy(n)
+      for i in 0..<n.len:
+        result[i] = p(n[i], c, s, mode, inReturn=true)
+      s.needsTry = true
+    of nkCast:
+      result = shallowCopy(n)
+      result[0] = n[0]
+      result[1] = p(n[1], c, s, mode)
+    of nkCheckedFieldExpr:
+      result = shallowCopy(n)
+      result[0] = p(n[0], c, s, mode)
+      for i in 1..<n.len:
+        result[i] = n[i]
+    of nkGotoState, nkState, nkAsmStmt:
+      result = n
+    else:
+      result = nil
+      internalError(c.graph.config, n.info, "cannot inject destructors to node kind: " & $n.kind)
+
+proc sameLocation*(a, b: PNode): bool =
+  proc sameConstant(a, b: PNode): bool =
+    a.kind in nkLiterals and b.kind in nkLiterals and a.intVal == b.intVal
+
+  const nkEndPoint = {nkSym, nkDotExpr, nkCheckedFieldExpr, nkBracketExpr}
+  if a.kind in nkEndPoint and b.kind in nkEndPoint:
+    if a.kind == b.kind:
+      case a.kind
+      of nkSym: a.sym == b.sym
+      of nkDotExpr, nkCheckedFieldExpr: sameLocation(a[0], b[0]) and sameLocation(a[1], b[1])
+      of nkBracketExpr: sameLocation(a[0], b[0]) and sameConstant(a[1], b[1])
+      else: false
+    else: false
+  else:
+    case a.kind
+    of nkSym, nkDotExpr, nkCheckedFieldExpr, nkBracketExpr:
+      # Reached an endpoint, flip to recurse the other side.
+      sameLocation(b, a)
+    of nkAddr, nkHiddenAddr, nkDerefExpr, nkHiddenDeref:
+      # We don't need to check addr/deref levels or differentiate between the two,
+      # since pointers don't have hooks :) (e.g: var p: ptr pointer; p[] = addr p)
+      sameLocation(a[0], b)
+    of nkObjDownConv, nkObjUpConv: sameLocation(a[0], b)
+    of nkHiddenStdConv, nkHiddenSubConv: sameLocation(a[1], b)
+    else: false
+
+proc genFieldAccessSideEffects(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
+  # with side effects
+  var temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), c.idgen, c.owner, ri[1].info)
+  temp.typ = ri[1].typ
+  var v = newNodeI(nkLetSection, ri[1].info)
+  let tempAsNode = newSymNode(temp)
+
+  var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
+  vpart[0] = tempAsNode
+  vpart[1] = newNodeI(nkEmpty, tempAsNode.info)
+  vpart[2] = ri[1]
+  v.add(vpart)
+
+  var newAccess = copyNode(ri)
+  newAccess.add ri[0]
+  newAccess.add tempAsNode
+
+  var snk = c.genSink(s, dest, newAccess, flags)
+  result = newTree(nkStmtList, v, snk, c.genWasMoved(newAccess))
+
+proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopyFlag] = {}): PNode =
+  var ri = ri
+  var isEnsureMove = 0
+  if ri.kind in nkCallKinds and ri[0].kind == nkSym and ri[0].sym.magic == mEnsureMove:
+    ri = ri[1]
+    isEnsureMove = 1
+  if sameLocation(dest, ri):
+    # rule (self-assignment-removal):
+    result = newNodeI(nkEmpty, dest.info)
+  elif isCursor(dest) or dest.typ.kind in {tyOpenArray, tyVarargs}:
+    # hoisted openArray parameters might end up here
+    # openArray types don't have a lifted assignment operation (it's empty)
+    # bug #22132
+    case ri.kind:
+    of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
+      template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags)
+      # We know the result will be a stmt so we use that fact to optimize
+      handleNestedTempl(ri, process, willProduceStmt = true)
+    else:
+      result = newTree(nkFastAsgn, dest, p(ri, c, s, normal))
+  else:
+    let ri2 = if ri.kind == nkWhen: ri[1][0] else: ri
+    case ri2.kind
+    of nkCallKinds:
+      result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
+    of nkBracketExpr:
+      if isUnpackedTuple(ri[0]):
+        # unpacking of tuple: take over the elements
+        result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
+      elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s):
+        if aliases(dest, ri) == no:
+          # Rule 3: `=sink`(x, z); wasMoved(z)
+          if isAtom(ri[1]):
+            var snk = c.genSink(s, dest, ri, flags)
+            result = newTree(nkStmtList, snk, c.genWasMoved(ri))
+          else:
+            result = genFieldAccessSideEffects(c, s, dest, ri, flags)
+        else:
+          result = c.genSink(s, dest, destructiveMoveVar(ri, c, s), flags)
+      else:
+        inc c.inEnsureMove, isEnsureMove
+        result = c.genCopy(dest, ri, flags)
+        dec c.inEnsureMove, isEnsureMove
+        result.add p(ri, c, s, consumed)
+        c.finishCopy(result, dest, flags, isFromSink = false)
+    of nkBracket:
+      # array constructor
+      if ri.len > 0 and isDangerousSeq(ri.typ):
+        inc c.inEnsureMove, isEnsureMove
+        result = c.genCopy(dest, ri, flags)
+        dec c.inEnsureMove, isEnsureMove
+        result.add p(ri, c, s, consumed)
+        c.finishCopy(result, dest, flags, isFromSink = false)
+      else:
+        result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
+    of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
+      result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
+    of nkSym:
+      if isSinkParam(ri.sym) and isLastRead(ri, c, s):
+        # Rule 3: `=sink`(x, z); wasMoved(z)
+        let snk = c.genSink(s, dest, ri, flags)
+        result = newTree(nkStmtList, snk, c.genWasMoved(ri))
+      elif ri.sym.kind != skParam and
+          isAnalysableFieldAccess(ri, c.owner) and
+          isLastRead(ri, c, s) and canBeMoved(c, dest.typ):
+        # Rule 3: `=sink`(x, z); wasMoved(z)
+        let snk = c.genSink(s, dest, ri, flags)
+        result = newTree(nkStmtList, snk, c.genWasMoved(ri))
+      else:
+        inc c.inEnsureMove, isEnsureMove
+        result = c.genCopy(dest, ri, flags)
+        dec c.inEnsureMove, isEnsureMove
+        result.add p(ri, c, s, consumed)
+        c.finishCopy(result, dest, flags, isFromSink = false)
+    of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast:
+      result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
+    of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
+      template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags)
+      # We know the result will be a stmt so we use that fact to optimize
+      handleNestedTempl(ri, process, willProduceStmt = true)
+    of nkRaiseStmt:
+      result = pRaiseStmt(ri, c, s)
+    else:
+      if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and
+          canBeMoved(c, dest.typ):
+        # Rule 3: `=sink`(x, z); wasMoved(z)
+        let snk = c.genSink(s, dest, ri, flags)
+        result = newTree(nkStmtList, snk, c.genWasMoved(ri))
+      else:
+        inc c.inEnsureMove, isEnsureMove
+        result = c.genCopy(dest, ri, flags)
+        dec c.inEnsureMove, isEnsureMove
+        result.add p(ri, c, s, consumed)
+        c.finishCopy(result, dest, flags, isFromSink = false)
+
+when false:
+  proc computeUninit(c: var Con) =
+    if not c.uninitComputed:
+      c.uninitComputed = true
+      c.uninit = initIntSet()
+      var init = initIntSet()
+      discard initialized(c.g, pc = 0, init, c.uninit, int.high)
+
+  proc injectDefaultCalls(n: PNode, c: var Con) =
+    case n.kind
+    of nkVarSection, nkLetSection:
+      for it in n:
+        if it.kind == nkIdentDefs and it[^1].kind == nkEmpty:
+          computeUninit(c)
+          for j in 0..<it.len-2:
+            let v = skipPragmaExpr(it[j])
+            doAssert v.kind == nkSym
+            if c.uninit.contains(v.sym.id):
+              it[^1] = genDefaultCall(v.sym.typ, c, v.info)
+              break
+    of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef,
+        nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
+      discard
+    else:
+      for i in 0..<n.safeLen:
+        injectDefaultCalls(n[i], c)
+
+proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): PNode =
+  when toDebug.len > 0:
+    shouldDebug = toDebug == owner.name.s or toDebug == "always"
+  if sfGeneratedOp in owner.flags or (owner.kind == skIterator and isInlineIterator(owner.typ)):
+    return n
+  var c = Con(owner: owner, graph: g, idgen: idgen, body: n, otherUsage: unknownLineInfo)
+
+  if optCursorInference in g.config.options:
+    computeCursors(owner, n, g)
+
+  var scope = Scope(body: n)
+  let body = p(n, c, scope, normal)
+
+  if owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}:
+    let params = owner.typ.n
+    for i in 1..<params.len:
+      let t = params[i].sym.typ
+      if isSinkTypeForParam(t) and hasDestructor(c, t.skipTypes({tySink})):
+        scope.final.add c.genDestroy(params[i])
+  #if optNimV2 in c.graph.config.globalOptions:
+  #  injectDefaultCalls(n, c)
+  result = optimize processScope(c, scope, body)
+  dbg:
+    echo ">---------transformed-to--------->"
+    echo renderTree(result, {renderIds})
+
+  if g.config.arcToExpand.hasKey(owner.name.s):
+    echo "--expandArc: ", owner.name.s
+    echo renderTree(result, {renderIr, renderNoComments})
+    echo "-- end of expandArc ------------------------"
diff --git a/compiler/installer.ini b/compiler/installer.ini
index 2847e4e62..54a35dbee 100644
--- a/compiler/installer.ini
+++ b/compiler/installer.ini
@@ -6,15 +6,17 @@ Name: "Nim"
 Version: "$version"
 Platforms: """
   windows: i386;amd64
-  linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv64
-  macosx: i386;amd64;powerpc64
+  linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64;loongarch64
+  macosx: i386;amd64;powerpc64;arm64
   solaris: i386;amd64;sparc;sparc64
-  freebsd: i386;amd64
-  netbsd: i386;amd64
-  openbsd: i386;amd64
+  freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el
+  netbsd: i386;amd64;arm64
+  openbsd: i386;amd64;arm;arm64
   dragonfly: i386;amd64
+  crossos: amd64
   haiku: i386;amd64
   android: i386;arm;arm64
+  nintendoswitch: arm64
 """
 
 Authors: "Andreas Rumpf"
@@ -34,9 +36,8 @@ App: Console
 License: "copying.txt"
 
 [Config]
-Files: "config/nim.cfg"
-Files: "config/nimdoc.cfg"
-Files: "config/nimdoc.tex.cfg"
+Files: "config/*.cfg"
+Files: "config/config.nims"
 
 [Documentation]
 ; Files: "doc/*.html"
@@ -48,11 +49,8 @@ Start: "doc/html/overview.html"
 
 
 [Other]
-Files: "readme.txt;copying.txt;install.txt"
-Files: "makefile"
+Files: "copying.txt"
 Files: "koch.nim"
-Files: "install_nimble.nims"
-Files: "install_tools.nims"
 
 Files: "icons/nim.ico"
 Files: "icons/nim.rc"
@@ -67,16 +65,13 @@ Files: "compiler"
 Files: "doc"
 Files: "doc/html"
 Files: "tools"
+Files: "tools/debug/nim-gdb.py"
+Files: "nimpretty"
+Files: "testament"
 Files: "nimsuggest"
 Files: "nimsuggest/tests/*.nim"
-Files: "web/website.ini"
-Files: "web/ticker.html"
-Files: "web/*.nim"
-Files: "web/*.rst"
-Files: "web/*.csv"
-Files: "web/news/*.rst"
-Files: "bin/nimblepkg/*.nim"
-Files: "bin/nimblepkg/*.cfg"
+Files: "changelogs/*.md"
+Files: "ci/funs.sh"
 
 [Lib]
 Files: "lib"
@@ -84,18 +79,21 @@ Files: "lib"
 [Other]
 Files: "examples"
 Files: "dist/nimble"
-Files: "dist/nimsuggest"
+Files: "dist/checksums"
 
 Files: "tests"
 
 [Windows]
 Files: "bin/nim.exe"
-Files: "bin/c2nim.exe"
 Files: "bin/nimgrep.exe"
 Files: "bin/nimsuggest.exe"
 Files: "bin/nimble.exe"
 Files: "bin/vccexe.exe"
 Files: "bin/nimgrab.exe"
+Files: "bin/nimpretty.exe"
+Files: "bin/testament.exe"
+Files: "bin/nim-gdb.bat"
+Files: "bin/atlas.exe"
 
 Files: "koch.exe"
 Files: "finish.exe"
@@ -113,9 +111,10 @@ Download: r"Aporia Text Editor|dist|aporia.zip|97997|https://nim-lang.org/downlo
 ; for now only NSIS supports optional downloads
 
 [WinBin]
-Files: "$NIMINSTDEPS/makelink.exe"
-Files: "$NIMINSTDEPS/7zG.exe"
-Files: "$NIMINSTDEPS/*.dll"
+Files: "bin/makelink.exe"
+Files: "bin/7zG.exe"
+Files: "bin/*.dll"
+Files: "bin/cacert.pem"
 
 [UnixBin]
 Files: "bin/nim"
@@ -124,6 +123,8 @@ Files: "bin/nim"
 [Unix]
 InstallScript: "yes"
 UninstallScript: "yes"
+Files: "bin/nim-gdb"
+Files: "build_all.sh"
 
 
 [InnoSetup]
@@ -145,5 +146,5 @@ shortDesc: "The Nim Compiler"
 licenses: "bin/nim,MIT;lib/*,MIT;"
 
 [nimble]
-pkgName: "compiler"
-pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt"
+pkgName: "nim"
+pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt;doc/nimdoc.css;doc/nimdoc.cls"
diff --git a/compiler/int128.nim b/compiler/int128.nim
new file mode 100644
index 000000000..74e581cd5
--- /dev/null
+++ b/compiler/int128.nim
@@ -0,0 +1,592 @@
+## This module is for compiler internal use only. For reliable error
+## messages and range checks, the compiler needs a data type that can
+## hold all from `low(BiggestInt)` to `high(BiggestUInt)`, This
+## type is for that purpose.
+
+from std/math import trunc
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+type
+  Int128* = object
+    udata: array[4, uint32]
+
+template sdata(arg: Int128, idx: int): int32 =
+  # udata and sdata was supposed to be in a union, but unions are
+  # handled incorrectly in the VM.
+  cast[ptr int32](arg.udata[idx].unsafeAddr)[]
+
+# encoding least significant int first (like LittleEndian)
+
+const
+  Zero* = Int128(udata: [0'u32, 0, 0, 0])
+  One* = Int128(udata: [1'u32, 0, 0, 0])
+  Ten* = Int128(udata: [10'u32, 0, 0, 0])
+  Min = Int128(udata: [0'u32, 0, 0, 0x80000000'u32])
+  Max = Int128(udata: [high(uint32), high(uint32), high(uint32), uint32(high(int32))])
+  NegOne* = Int128(udata: [0xffffffff'u32, 0xffffffff'u32, 0xffffffff'u32, 0xffffffff'u32])
+
+template low*(t: typedesc[Int128]): Int128 = Min
+template high*(t: typedesc[Int128]): Int128 = Max
+
+proc `$`*(a: Int128): string
+
+proc toInt128*[T: SomeInteger | bool](arg: T): Int128 =
+  {.noSideEffect.}:
+    result = Zero
+    when T is bool: result.sdata(0) = int32(arg)
+    elif T is SomeUnsignedInt:
+      when sizeof(arg) <= 4:
+        result.udata[0] = uint32(arg)
+      else:
+        result.udata[0] = uint32(arg and T(0xffffffff))
+        result.udata[1] = uint32(arg shr 32)
+    elif sizeof(arg) <= 4:
+      result.sdata(0) = int32(arg)
+      if arg < 0: # sign extend
+        result.sdata(1) = -1
+        result.sdata(2) = -1
+        result.sdata(3) = -1
+    else:
+      let tmp = int64(arg)
+      result.udata[0] = uint32(tmp and 0xffffffff)
+      result.sdata(1) = int32(tmp shr 32)
+      if arg < 0: # sign extend
+        result.sdata(2) = -1
+        result.sdata(3) = -1
+
+template isNegative(arg: Int128): bool =
+  arg.sdata(3) < 0
+
+proc bitconcat(a, b: uint32): uint64 =
+  (uint64(a) shl 32) or uint64(b)
+
+proc toInt64*(arg: Int128): int64 =
+  if isNegative(arg):
+    assert(arg.sdata(3) == -1, "out of range")
+    assert(arg.sdata(2) == -1, "out of range")
+  else:
+    assert(arg.sdata(3) == 0, "out of range")
+    assert(arg.sdata(2) == 0, "out of range")
+
+  cast[int64](bitconcat(arg.udata[1], arg.udata[0]))
+
+proc toInt64Checked*(arg: Int128; onError: int64): int64 =
+  if isNegative(arg):
+    if arg.sdata(3) != -1 or arg.sdata(2) != -1:
+      return onError
+  else:
+    if arg.sdata(3) != 0 or arg.sdata(2) != 0:
+      return onError
+  return cast[int64](bitconcat(arg.udata[1], arg.udata[0]))
+
+proc toInt32*(arg: Int128): int32 =
+  if isNegative(arg):
+    assert(arg.sdata(3) == -1, "out of range")
+    assert(arg.sdata(2) == -1, "out of range")
+    assert(arg.sdata(1) == -1, "out of range")
+  else:
+    assert(arg.sdata(3) == 0, "out of range")
+    assert(arg.sdata(2) == 0, "out of range")
+    assert(arg.sdata(1) == 0, "out of range")
+
+  arg.sdata(0)
+
+proc toInt16*(arg: Int128): int16 =
+  if isNegative(arg):
+    assert(arg.sdata(3) == -1, "out of range")
+    assert(arg.sdata(2) == -1, "out of range")
+    assert(arg.sdata(1) == -1, "out of range")
+  else:
+    assert(arg.sdata(3) == 0, "out of range")
+    assert(arg.sdata(2) == 0, "out of range")
+    assert(arg.sdata(1) == 0, "out of range")
+
+  int16(arg.sdata(0))
+
+proc toInt8*(arg: Int128): int8 =
+  if isNegative(arg):
+    assert(arg.sdata(3) == -1, "out of range")
+    assert(arg.sdata(2) == -1, "out of range")
+    assert(arg.sdata(1) == -1, "out of range")
+  else:
+    assert(arg.sdata(3) == 0, "out of range")
+    assert(arg.sdata(2) == 0, "out of range")
+    assert(arg.sdata(1) == 0, "out of range")
+
+  int8(arg.sdata(0))
+
+proc toInt*(arg: Int128): int =
+  when sizeof(int) == 4:
+    cast[int](toInt32(arg))
+  else:
+    cast[int](toInt64(arg))
+
+proc toUInt64*(arg: Int128): uint64 =
+  assert(arg.udata[3] == 0)
+  assert(arg.udata[2] == 0)
+  bitconcat(arg.udata[1], arg.udata[0])
+
+proc toUInt32*(arg: Int128): uint32 =
+  assert(arg.udata[3] == 0)
+  assert(arg.udata[2] == 0)
+  assert(arg.udata[1] == 0)
+  arg.udata[0]
+
+proc toUInt16*(arg: Int128): uint16 =
+  assert(arg.udata[3] == 0)
+  assert(arg.udata[2] == 0)
+  assert(arg.udata[1] == 0)
+  uint16(arg.udata[0])
+
+proc toUInt8*(arg: Int128): uint8 =
+  assert(arg.udata[3] == 0)
+  assert(arg.udata[2] == 0)
+  assert(arg.udata[1] == 0)
+  uint8(arg.udata[0])
+
+proc toUInt*(arg: Int128): uint =
+  when sizeof(int) == 4:
+    cast[uint](toInt32(arg))
+  else:
+    cast[uint](toInt64(arg))
+
+proc castToInt64*(arg: Int128): int64 =
+  ## Conversion to int64 without range check.
+  cast[int64](bitconcat(arg.udata[1], arg.udata[0]))
+
+proc castToUInt64*(arg: Int128): uint64 =
+  ## Conversion to uint64 without range check.
+  cast[uint64](bitconcat(arg.udata[1], arg.udata[0]))
+
+proc addToHex(result: var string; arg: uint32) =
+  for i in 0..<8:
+    let idx = (arg shr ((7-i) * 4)) and 0xf
+    result.add "0123456789abcdef"[idx]
+
+proc addToHex*(result: var string; arg: Int128) =
+  var i = 3
+  while i >= 0:
+    result.addToHex(arg.udata[i])
+    i -= 1
+
+proc toHex*(arg: Int128): string =
+  result = ""
+  result.addToHex(arg)
+
+proc inc*(a: var Int128, y: uint32 = 1) =
+  a.udata[0] += y
+  if unlikely(a.udata[0] < y):
+    a.udata[1].inc
+    if unlikely(a.udata[1] == 0):
+      a.udata[2].inc
+      if unlikely(a.udata[2] == 0):
+        a.udata[3].inc
+        doAssert(a.sdata(3) != low(int32), "overflow")
+
+proc cmp*(a, b: Int128): int =
+  let tmp1 = cmp(a.sdata(3), b.sdata(3))
+  if tmp1 != 0: return tmp1
+  let tmp2 = cmp(a.udata[2], b.udata[2])
+  if tmp2 != 0: return tmp2
+  let tmp3 = cmp(a.udata[1], b.udata[1])
+  if tmp3 != 0: return tmp3
+  let tmp4 = cmp(a.udata[0], b.udata[0])
+  return tmp4
+
+proc `<`*(a, b: Int128): bool =
+  cmp(a, b) < 0
+
+proc `<=`*(a, b: Int128): bool =
+  cmp(a, b) <= 0
+
+proc `==`*(a, b: Int128): bool =
+  if a.udata[0] != b.udata[0]: return false
+  if a.udata[1] != b.udata[1]: return false
+  if a.udata[2] != b.udata[2]: return false
+  if a.udata[3] != b.udata[3]: return false
+  return true
+
+proc bitnot*(a: Int128): Int128 =
+  result = Zero
+  result.udata[0] = not a.udata[0]
+  result.udata[1] = not a.udata[1]
+  result.udata[2] = not a.udata[2]
+  result.udata[3] = not a.udata[3]
+
+proc bitand*(a, b: Int128): Int128 =
+  result = Zero
+  result.udata[0] = a.udata[0] and b.udata[0]
+  result.udata[1] = a.udata[1] and b.udata[1]
+  result.udata[2] = a.udata[2] and b.udata[2]
+  result.udata[3] = a.udata[3] and b.udata[3]
+
+proc bitor*(a, b: Int128): Int128 =
+  result = Zero
+  result.udata[0] = a.udata[0] or b.udata[0]
+  result.udata[1] = a.udata[1] or b.udata[1]
+  result.udata[2] = a.udata[2] or b.udata[2]
+  result.udata[3] = a.udata[3] or b.udata[3]
+
+proc bitxor*(a, b: Int128): Int128 =
+  result = Zero
+  result.udata[0] = a.udata[0] xor b.udata[0]
+  result.udata[1] = a.udata[1] xor b.udata[1]
+  result.udata[2] = a.udata[2] xor b.udata[2]
+  result.udata[3] = a.udata[3] xor b.udata[3]
+
+proc `shr`*(a: Int128, b: int): Int128 =
+  result = Zero
+  let b = b and 127
+  if b < 32:
+    result.sdata(3) = a.sdata(3) shr b
+    result.udata[2] = cast[uint32](bitconcat(a.udata[3], a.udata[2]) shr b)
+    result.udata[1] = cast[uint32](bitconcat(a.udata[2], a.udata[1]) shr b)
+    result.udata[0] = cast[uint32](bitconcat(a.udata[1], a.udata[0]) shr b)
+  elif b < 64:
+    if isNegative(a):
+      result.sdata(3) = -1
+    result.sdata(2) = a.sdata(3) shr (b and 31)
+    result.udata[1] = cast[uint32](bitconcat(a.udata[3], a.udata[2]) shr (b and 31))
+    result.udata[0] = cast[uint32](bitconcat(a.udata[2], a.udata[1]) shr (b and 31))
+  elif b < 96:
+    if isNegative(a):
+      result.sdata(3) = -1
+      result.sdata(2) = -1
+    result.sdata(1) = a.sdata(3) shr (b and 31)
+    result.udata[0] = cast[uint32](bitconcat(a.udata[3], a.udata[2]) shr (b and 31))
+  else: # b < 128
+    if isNegative(a):
+      result.sdata(3) = -1
+      result.sdata(2) = -1
+      result.sdata(1) = -1
+    result.sdata(0) = a.sdata(3) shr (b and 31)
+
+proc `shl`*(a: Int128, b: int): Int128 =
+  result = Zero
+  let b = b and 127
+  if b < 32:
+    result.udata[0] = a.udata[0] shl b
+    result.udata[1] = cast[uint32]((bitconcat(a.udata[1], a.udata[0]) shl b) shr 32)
+    result.udata[2] = cast[uint32]((bitconcat(a.udata[2], a.udata[1]) shl b) shr 32)
+    result.udata[3] = cast[uint32]((bitconcat(a.udata[3], a.udata[2]) shl b) shr 32)
+  elif b < 64:
+    result.udata[0] = 0
+    result.udata[1] = a.udata[0] shl (b and 31)
+    result.udata[2] = cast[uint32]((bitconcat(a.udata[1], a.udata[0]) shl (b and 31)) shr 32)
+    result.udata[3] = cast[uint32]((bitconcat(a.udata[2], a.udata[1]) shl (b and 31)) shr 32)
+  elif b < 96:
+    result.udata[0] = 0
+    result.udata[1] = 0
+    result.udata[2] = a.udata[0] shl (b and 31)
+    result.udata[3] = cast[uint32]((bitconcat(a.udata[1], a.udata[0]) shl (b and 31)) shr 32)
+  else:
+    result.udata[0] = 0
+    result.udata[1] = 0
+    result.udata[2] = 0
+    result.udata[3] = a.udata[0] shl (b and 31)
+
+proc `+`*(a, b: Int128): Int128 =
+  result = Zero
+  let tmp0 = uint64(a.udata[0]) + uint64(b.udata[0])
+  result.udata[0] = cast[uint32](tmp0)
+  let tmp1 = uint64(a.udata[1]) + uint64(b.udata[1]) + (tmp0 shr 32)
+  result.udata[1] = cast[uint32](tmp1)
+  let tmp2 = uint64(a.udata[2]) + uint64(b.udata[2]) + (tmp1 shr 32)
+  result.udata[2] = cast[uint32](tmp2)
+  let tmp3 = uint64(a.udata[3]) + uint64(b.udata[3]) + (tmp2 shr 32)
+  result.udata[3] = cast[uint32](tmp3)
+
+proc `+=`*(a: var Int128, b: Int128) =
+  a = a + b
+
+proc `-`*(a: Int128): Int128 =
+  result = bitnot(a)
+  result.inc
+
+proc `-`*(a, b: Int128): Int128 =
+  a + (-b)
+
+proc `-=`*(a: var Int128, b: Int128) =
+  a = a - b
+
+proc abs*(a: Int128): Int128 =
+  if isNegative(a):
+    -a
+  else:
+    a
+
+proc abs(a: int32): int =
+  if a < 0: -a else: a
+
+proc `*`(a: Int128, b: uint32): Int128 =
+  result = Zero
+  let tmp0 = uint64(a.udata[0]) * uint64(b)
+  let tmp1 = uint64(a.udata[1]) * uint64(b)
+  let tmp2 = uint64(a.udata[2]) * uint64(b)
+  let tmp3 = uint64(a.udata[3]) * uint64(b)
+
+  if unlikely(tmp3 > uint64(high(int32))):
+    assert(false, "overflow")
+
+  result.udata[0] = cast[uint32](tmp0)
+  result.udata[1] = cast[uint32](tmp1) + cast[uint32](tmp0 shr 32)
+  result.udata[2] = cast[uint32](tmp2) + cast[uint32](tmp1 shr 32)
+  result.udata[3] = cast[uint32](tmp3) + cast[uint32](tmp2 shr 32)
+
+proc `*`*(a: Int128, b: int32): Int128 =
+  result = a * cast[uint32](abs(b))
+  if b < 0:
+    result = -result
+
+proc `*=`(a: var Int128, b: int32) =
+  a = a * b
+
+proc makeInt128(high, low: uint64): Int128 =
+  result = Zero
+  result.udata[0] = cast[uint32](low)
+  result.udata[1] = cast[uint32](low shr 32)
+  result.udata[2] = cast[uint32](high)
+  result.udata[3] = cast[uint32](high shr 32)
+
+proc high64(a: Int128): uint64 =
+  bitconcat(a.udata[3], a.udata[2])
+
+proc low64(a: Int128): uint64 =
+  bitconcat(a.udata[1], a.udata[0])
+
+proc `*`*(lhs, rhs: Int128): Int128 =
+  let a32 = uint64(lhs.udata[1])
+  let a00 = uint64(lhs.udata[0])
+  let b32 = uint64(rhs.udata[1])
+  let b00 = uint64(rhs.udata[0])
+  result = makeInt128(high64(lhs) * low64(rhs) + low64(lhs) * high64(rhs) + a32 * b32, a00 * b00)
+  result += toInt128(a32 * b00) shl 32
+  result += toInt128(a00 * b32) shl 32
+
+proc `*=`*(a: var Int128, b: Int128) =
+  a = a * b
+
+import std/bitops
+
+proc fastLog2*(a: Int128): int =
+  result = 0
+  if a.udata[3] != 0:
+    return 96 + fastLog2(a.udata[3])
+  if a.udata[2] != 0:
+    return 64 + fastLog2(a.udata[2])
+  if a.udata[1] != 0:
+    return 32 + fastLog2(a.udata[1])
+  if a.udata[0] != 0:
+    return fastLog2(a.udata[0])
+
+proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] =
+  assert(divisor != Zero)
+  result = (Zero, Zero)
+
+  let isNegativeA = isNegative(dividend)
+  let isNegativeB = isNegative(divisor)
+
+  var dividend = abs(dividend)
+  let divisor = abs(divisor)
+
+  if divisor > dividend:
+    result.quotient = Zero
+    if isNegativeA:
+      result.remainder = -dividend
+    else:
+      result.remainder = dividend
+    return
+
+  if divisor == dividend:
+    if isNegativeA xor isNegativeB:
+      result.quotient = NegOne
+    else:
+      result.quotient = One
+    result.remainder = Zero
+    return
+
+  var denominator = divisor
+  var quotient = Zero
+
+  # Left aligns the MSB of the denominator and the dividend.
+  let shift = fastLog2(dividend) - fastLog2(denominator)
+  denominator = denominator shl shift
+
+  # Uses shift-subtract algorithm to divide dividend by denominator. The
+  # remainder will be left in dividend.
+  for i in 0..shift:
+    quotient = quotient shl 1
+    if dividend >= denominator:
+      dividend -= denominator
+      quotient = bitor(quotient, One)
+
+    denominator = denominator shr 1
+
+  if isNegativeA xor isNegativeB:
+    result.quotient = -quotient
+  else:
+    result.quotient = quotient
+  if isNegativeA:
+    result.remainder = -dividend
+  else:
+    result.remainder = dividend
+
+proc `div`*(a, b: Int128): Int128 =
+  let (a, _) = divMod(a, b)
+  return a
+
+proc `mod`*(a, b: Int128): Int128 =
+  let (_, b) = divMod(a, b)
+  return b
+
+proc addInt128*(result: var string; value: Int128) =
+  let initialSize = result.len
+  if value == Zero:
+    result.add '0'
+  elif value == low(Int128):
+    result.add "-170141183460469231731687303715884105728"
+  else:
+    let isNegative = isNegative(value)
+    var value = abs(value)
+    while value > Zero:
+      let (quot, rem) = divMod(value, Ten)
+      result.add "0123456789"[rem.toInt64]
+      value = quot
+    if isNegative:
+      result.add '-'
+
+    var i = initialSize
+    var j = high(result)
+    while i < j:
+      swap(result[i], result[j])
+      i += 1
+      j -= 1
+
+proc `$`*(a: Int128): string =
+  # "-170141183460469231731687303715884105728".len == 41
+  result = newStringOfCap(41)
+  result.addInt128(a)
+
+proc parseDecimalInt128*(arg: string, pos: int = 0): Int128 =
+  assert(pos < arg.len)
+  assert(arg[pos] in {'-', '0'..'9'})
+
+  var isNegative = false
+  var pos = pos
+  if arg[pos] == '-':
+    isNegative = true
+    pos += 1
+
+  result = Zero
+  while pos < arg.len and arg[pos] in '0'..'9':
+    result = result * Ten
+    result.inc(uint32(arg[pos]) - uint32('0'))
+    pos += 1
+
+  if isNegative:
+    result = -result
+
+# fluff
+
+proc `<`*(a: Int128, b: BiggestInt): bool =
+  cmp(a, toInt128(b)) < 0
+
+proc `<`*(a: BiggestInt, b: Int128): bool =
+  cmp(toInt128(a), b) < 0
+
+proc `<=`*(a: Int128, b: BiggestInt): bool =
+  cmp(a, toInt128(b)) <= 0
+
+proc `<=`*(a: BiggestInt, b: Int128): bool =
+  cmp(toInt128(a), b) <= 0
+
+proc `==`*(a: Int128, b: BiggestInt): bool =
+  a == toInt128(b)
+
+proc `==`*(a: BiggestInt, b: Int128): bool =
+  toInt128(a) == b
+
+proc `-`*(a: BiggestInt, b: Int128): Int128 =
+  toInt128(a) - b
+
+proc `-`*(a: Int128, b: BiggestInt): Int128 =
+  a - toInt128(b)
+
+proc `+`*(a: BiggestInt, b: Int128): Int128 =
+  toInt128(a) + b
+
+proc `+`*(a: Int128, b: BiggestInt): Int128 =
+  a + toInt128(b)
+
+proc toFloat64*(arg: Int128): float64 =
+  let isNegative = isNegative(arg)
+  let arg = abs(arg)
+
+  let a = float64(bitconcat(arg.udata[1], arg.udata[0]))
+  let b = float64(bitconcat(arg.udata[3], arg.udata[2]))
+
+  result = a + 18446744073709551616'f64 * b # a + 2^64 * b
+  if isNegative:
+    result = -result
+
+proc ldexp(x: float64, exp: cint): float64 {.importc: "ldexp", header: "<math.h>".}
+
+template bitor(a, b, c: Int128): Int128 = bitor(bitor(a, b), c)
+
+proc toInt128*(arg: float64): Int128 =
+  let isNegative = arg < 0
+  let v0 = ldexp(abs(arg), -100)
+  let w0 = uint64(trunc(v0))
+  let v1 = ldexp(v0 - float64(w0), 50)
+  let w1 = uint64(trunc(v1))
+  let v2 = ldexp(v1 - float64(w1), 50)
+  let w2 = uint64(trunc(v2))
+
+  let res = bitor(toInt128(w0) shl 100, toInt128(w1) shl 50, toInt128(w2))
+  if isNegative:
+    return -res
+  else:
+    return res
+
+proc maskUInt64*(arg: Int128): Int128 {.noinit, inline.} =
+  result = Zero
+  result.udata[0] = arg.udata[0]
+  result.udata[1] = arg.udata[1]
+  result.udata[2] = 0
+  result.udata[3] = 0
+
+proc maskUInt32*(arg: Int128): Int128 {.noinit, inline.} =
+  result = Zero
+  result.udata[0] = arg.udata[0]
+  result.udata[1] = 0
+  result.udata[2] = 0
+  result.udata[3] = 0
+
+proc maskUInt16*(arg: Int128): Int128 {.noinit, inline.} =
+  result = Zero
+  result.udata[0] = arg.udata[0] and 0xffff
+  result.udata[1] = 0
+  result.udata[2] = 0
+  result.udata[3] = 0
+
+proc maskUInt8*(arg: Int128): Int128 {.noinit, inline.} =
+  result = Zero
+  result.udata[0] = arg.udata[0] and 0xff
+  result.udata[1] = 0
+  result.udata[2] = 0
+  result.udata[3] = 0
+
+proc maskBytes*(arg: Int128, numbytes: int): Int128 {.noinit.} =
+  case numbytes
+  of 1:
+    return maskUInt8(arg)
+  of 2:
+    return maskUInt16(arg)
+  of 4:
+    return maskUInt32(arg)
+  of 8:
+    return maskUInt64(arg)
+  else:
+    raiseAssert "masking only implemented for 1, 2, 4 and 8 bytes"
diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim
new file mode 100644
index 000000000..17fbde29e
--- /dev/null
+++ b/compiler/isolation_check.nim
@@ -0,0 +1,232 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Implementation of the check that `recover` needs, see
+## https://github.com/nim-lang/RFCs/issues/244 for more details.
+
+import
+  ast, types, renderer
+
+import std/intsets
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+proc canAlias(arg, ret: PType; marker: var IntSet): bool
+
+proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool =
+  case n.kind
+  of nkRecList:
+    result = false
+    for i in 0..<n.len:
+      result = canAliasN(arg, n[i], marker)
+      if result: return
+  of nkRecCase:
+    assert(n[0].kind == nkSym)
+    result = canAliasN(arg, n[0], marker)
+    if result: return
+    for i in 1..<n.len:
+      case n[i].kind
+      of nkOfBranch, nkElse:
+        result = canAliasN(arg, lastSon(n[i]), marker)
+        if result: return
+      else: discard
+  of nkSym:
+    result = canAlias(arg, n.sym.typ, marker)
+  else: result = false
+
+proc canAlias(arg, ret: PType; marker: var IntSet): bool =
+  if containsOrIncl(marker, ret.id):
+    return false
+
+  if ret.kind in {tyPtr, tyPointer}:
+    # unsafe so we don't care:
+    return false
+  if compareTypes(arg, ret, dcEqIgnoreDistinct):
+    return true
+  case ret.kind
+  of tyObject:
+    if isFinal(ret):
+      result = canAliasN(arg, ret.n, marker)
+      if not result and ret.baseClass != nil:
+        result = canAlias(arg, ret.baseClass, marker)
+    else:
+      result = true
+  of tyTuple:
+    result = false
+    for r in ret.kids:
+      result = canAlias(arg, r, marker)
+      if result: break
+  of tyArray, tySequence, tyDistinct, tyGenericInst,
+     tyAlias, tyInferred, tySink, tyLent, tyOwned, tyRef:
+    result = canAlias(arg, ret.skipModifier, marker)
+  of tyProc:
+    result = ret.callConv == ccClosure
+  else:
+    result = false
+
+proc isValueOnlyType(t: PType): bool =
+  # t doesn't contain pointers and references
+  proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr, tyVar, tyLent}
+  result = not types.searchTypeFor(t, wrap)
+
+type
+  SearchResult = enum
+    NotFound, Abort, Found
+
+proc containsDangerousRefAux(t: PType; marker: var IntSet): SearchResult
+
+proc containsDangerousRefAux(n: PNode; marker: var IntSet): SearchResult =
+  result = NotFound
+  case n.kind
+  of nkRecList:
+    for i in 0..<n.len:
+      result = containsDangerousRefAux(n[i], marker)
+      if result == Found: return result
+  of nkRecCase:
+    assert(n[0].kind == nkSym)
+    result = containsDangerousRefAux(n[0], marker)
+    if result == Found: return result
+    for i in 1..<n.len:
+      case n[i].kind
+      of nkOfBranch, nkElse:
+        result = containsDangerousRefAux(lastSon(n[i]), marker)
+        if result == Found: return result
+      else: discard
+  of nkSym:
+    result = containsDangerousRefAux(n.sym.typ, marker)
+  else: discard
+
+proc containsDangerousRefAux(t: PType; marker: var IntSet): SearchResult =
+  result = NotFound
+  if t == nil: return result
+  if containsOrIncl(marker, t.id): return result
+
+  if t.kind == tyRef or (t.kind == tyProc and t.callConv == ccClosure):
+    result = Found
+  elif tfSendable in t.flags:
+    result = Abort
+  else:
+    # continue the type traversal:
+    result = NotFound
+
+  if result != NotFound: return result
+  case t.kind
+  of tyObject:
+    if t.baseClass != nil:
+      result = containsDangerousRefAux(t.baseClass.skipTypes(skipPtrs), marker)
+    if result == NotFound: result = containsDangerousRefAux(t.n, marker)
+  of tyGenericInst, tyDistinct, tyAlias, tySink:
+    result = containsDangerousRefAux(skipModifier(t), marker)
+  of tyArray, tySet, tySequence:
+    result = containsDangerousRefAux(t.elementType, marker)
+  of tyTuple:
+    for a in t.kids:
+      result = containsDangerousRefAux(a, marker)
+      if result == Found: return result
+  else:
+    discard
+
+proc containsDangerousRef(t: PType): bool =
+  # a `ref` type is "dangerous" if it occurs not within a type that is like `Isolated[T]`.
+  # For example:
+  # `ref int` # dangerous
+  # `Isolated[ref int]` # not dangerous
+  var marker = initIntSet()
+  result = containsDangerousRefAux(t, marker) == Found
+
+proc canAlias*(arg, ret: PType): bool =
+  if isValueOnlyType(arg):
+    # can alias only with addr(arg.x) and we don't care if it is not safe
+    result = false
+  else:
+    var marker = initIntSet()
+    result = canAlias(arg, ret, marker)
+
+const
+  SomeVar = {skForVar, skParam, skVar, skLet, skConst, skResult, skTemp}
+
+proc containsVariable(n: PNode): bool =
+  case n.kind
+  of nodesToIgnoreSet:
+    result = false
+  of nkSym:
+    result = n.sym.kind in SomeVar
+  else:
+    for ch in n:
+      if containsVariable(ch): return true
+    result = false
+
+proc checkIsolate*(n: PNode): bool =
+  if types.containsTyRef(n.typ):
+    # XXX Maybe require that 'n.typ' is acyclic. This is not much
+    # worse than the already exisiting inheritance and closure restrictions.
+    case n.kind
+    of nkCharLit..nkNilLit:
+      result = true
+    of nkCallKinds:
+      # XXX: as long as we don't update the analysis while examining arguments
+      #      we can do an early check of the return type, otherwise this is a
+      #      bug and needs to be moved below
+      if tfNoSideEffect notin n[0].typ.flags:
+        return false
+      for i in 1..<n.len:
+        if checkIsolate(n[i]):
+          discard "fine, it is isolated already"
+        else:
+          let argType = n[i].typ
+          if argType != nil and not isCompileTimeOnly(argType) and containsDangerousRef(argType):
+            if argType.canAlias(n.typ) or containsVariable(n[i]):
+              # bug #19013: Alias information is not enough, we need to check for potential
+              # "overlaps". I claim the problem can only happen by reading again from a location
+              # that materialized which is only possible if a variable that contains a `ref`
+              # is involved.
+              return false
+      result = true
+    of nkIfStmt, nkIfExpr:
+      result = false
+      for it in n:
+        result = checkIsolate(it.lastSon)
+        if not result: break
+    of nkCaseStmt:
+      result = false
+      for i in 1..<n.len:
+        result = checkIsolate(n[i].lastSon)
+        if not result: break
+    of nkObjConstr:
+      result = true
+      for i in 1..<n.len:
+        result = checkIsolate(n[i].lastSon)
+        if not result: break
+    of nkBracket, nkTupleConstr, nkPar:
+      result = false
+      for it in n:
+        result = checkIsolate(it)
+        if not result: break
+    of nkHiddenStdConv, nkHiddenSubConv, nkCast, nkConv:
+      result = checkIsolate(n[1])
+    of nkObjUpConv, nkObjDownConv, nkDotExpr:
+      result = checkIsolate(n[0])
+    of nkStmtList, nkStmtListExpr:
+      if n.len > 0:
+        result = checkIsolate(n[^1])
+      else:
+        result = false
+    of nkSym:
+      result = true
+      if n.sym.kind in SomeVar:
+        let argType = n.typ
+        if argType != nil and not isCompileTimeOnly(argType) and containsDangerousRef(argType):
+          result = false
+    else:
+      # unanalysable expression:
+      result = false
+  else:
+    # no ref, no cry:
+    result = true
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index ef54841ae..713944def 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -14,34 +14,45 @@ The JS code generator contains only 2 tricks:
 
 Trick 1
 -------
-Some locations (for example 'var int') require "fat pointers" (``etyBaseIndex``)
+Some locations (for example 'var int') require "fat pointers" (`etyBaseIndex`)
 which are pairs (array, index). The derefence operation is then 'array[index]'.
-Check ``mapType`` for the details.
+Check `mapType` for the details.
 
 Trick 2
 -------
 It is preferable to generate '||' and '&&' if possible since that is more
 idiomatic and hence should be friendlier for the JS JIT implementation. However
-code like ``foo and (let bar = baz())`` cannot be translated this way. Instead
-the expressions need to be transformed into statements. ``isSimpleExpr``
+code like `foo and (let bar = baz())` cannot be translated this way. Instead
+the expressions need to be transformed into statements. `isSimpleExpr`
 implements the required case distinction.
 """
 
 
 import
-  ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options,
-  nversion, nimsets, msgs, std / sha1, bitsets, idents, types, os, tables,
-  times, ropes, math, passes, ccgutils, wordrecg, renderer,
-  intsets, cgmeth, lowerings, sighashes, lineinfos, rodutils
+  ast, trees, magicsys, options,
+  nversion, msgs, idents, types,
+  ropes, wordrecg, renderer,
+  cgmeth, lowerings, sighashes, modulegraphs, lineinfos,
+  transf, injectdestructors, sourcemap, astmsgs, pushpoppragmas,
+  mangleutils
 
-from modulegraphs import ModuleGraph
+import pipelineutils
+
+import std/[json, sets, math, tables, intsets]
+import std/strutils except addf
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
+
+import std/formatfloat
 
 type
-  TJSGen = object of TPassContext
+  TJSGen = object of PPassContext
     module: PSym
     graph: ModuleGraph
     config: ConfigRef
     sigConflicts: CountTable[SigHash]
+    initProc: PProc
 
   BModule = ref TJSGen
   TJSTypeKind = enum       # necessary JS "types"
@@ -66,6 +77,12 @@ type
     res: Rope               # result part; index if this is an
                              # (address, index)-tuple
     address: Rope           # address of an (address, index)-tuple
+    tmpLoc: Rope            # tmp var which stores the (address, index)
+                            # pair to prevent multiple evals.
+                            # the tmp is initialized upon evaling the
+                            # address.
+                            # might be nil.
+                            # (see `maybeMakeTemp`)
 
   TBlock = object
     id: int                  # the ID of the label; positive means that it
@@ -77,8 +94,8 @@ type
     forwarded: seq[PSym]
     generatedSyms: IntSet
     typeInfoGenerated: IntSet
-    classes: seq[(PType, Rope)]
     unique: int    # for temp identifier generation
+    inSystem: bool
 
   PProc = ref TProc
   TProc = object
@@ -86,35 +103,39 @@ type
     prc: PSym
     globals, locals, body: Rope
     options: TOptions
+    optionsStack: seq[(TOptions, TNoteKinds)]
     module: BModule
     g: PGlobals
     beforeRetNeeded: bool
     unique: int    # for temp identifier generation
     blocks: seq[TBlock]
     extraIndent: int
+    previousFileName: string  # For frameInfo inside templates.
+    # legacy: generatedParamCopies and up fields are used for jsNoLambdaLifting
+    generatedParamCopies: IntSet
     up: PProc     # up the call chain; required for closure support
-    declaredGlobals: IntSet
 
 template config*(p: PProc): ConfigRef = p.module.config
 
 proc indentLine(p: PProc, r: Rope): Rope =
-  result = r
   var p = p
-  while true:
-    for i in countup(0, p.blocks.len - 1 + p.extraIndent):
-      prepend(result, "\t".rope)
-    if p.up == nil or p.up.prc != p.prc.owner:
-      break
-    p = p.up
+  if jsNoLambdaLifting in p.config.legacyFeatures:
+    var ind = 0
+    while true:
+      inc ind, p.blocks.len + p.extraIndent
+      if p.up == nil or p.up.prc != p.prc.owner:
+        break
+      p = p.up
+    result = repeat(' ', ind*2) & r
+  else:
+    let ind = p.blocks.len + p.extraIndent
+    result = repeat(' ', ind*2) & r
 
 template line(p: PProc, added: string) =
-  add(p.body, indentLine(p, rope(added)))
-
-template line(p: PProc, added: Rope) =
-  add(p.body, indentLine(p, added))
+  p.body.add(indentLine(p, rope(added)))
 
 template lineF(p: PProc, frmt: FormatStr, args: varargs[Rope]) =
-  add(p.body, indentLine(p, ropes.`%`(frmt, args)))
+  p.body.add(indentLine(p, ropes.`%`(frmt, args)))
 
 template nested(p, body) =
   inc p.extraIndent
@@ -122,40 +143,42 @@ template nested(p, body) =
   dec p.extraIndent
 
 proc newGlobals(): PGlobals =
-  new(result)
-  result.forwarded = @[]
-  result.generatedSyms = initIntSet()
-  result.typeInfoGenerated = initIntSet()
-  result.classes = @[]
-
-proc initCompRes(r: var TCompRes) =
-  r.address = nil
-  r.res = nil
-  r.typ = etyNone
-  r.kind = resNone
+  result = PGlobals(forwarded: @[],
+        generatedSyms: initIntSet(),
+        typeInfoGenerated: initIntSet()
+        )
+
+proc initCompRes(): TCompRes =
+  result = TCompRes(address: "", res: "",
+    tmpLoc: "", typ: etyNone, kind: resNone
+  )
 
 proc rdLoc(a: TCompRes): Rope {.inline.} =
-  result = a.res
-  when false:
-    if a.typ != etyBaseIndex:
-      result = a.res
-    else:
-      result = "$1[$2]" % [a.address, a.res]
+  if a.typ != etyBaseIndex:
+    result = a.res
+  else:
+    result = "$1[$2]" % [a.address, a.res]
 
 proc newProc(globals: PGlobals, module: BModule, procDef: PNode,
              options: TOptions): PProc =
   result = PProc(
     blocks: @[],
+    optionsStack: if module.initProc != nil: module.initProc.optionsStack
+                  else: @[],
     options: options,
     module: module,
     procDef: procDef,
     g: globals,
     extraIndent: int(procDef != nil))
-  if procDef != nil: result.prc = procDef.sons[namePos].sym
+  if procDef != nil: result.prc = procDef[namePos].sym
 
-proc declareGlobal(p: PProc; id: int; r: Rope) =
-  if p.prc != nil and not p.declaredGlobals.containsOrIncl(id):
-    p.locals.addf("global $1;$n", [r])
+proc initProcOptions(module: BModule): TOptions =
+  result = module.config.options
+  if PGlobals(module.graph.backend).inSystem:
+    result.excl(optStackTrace)
+
+proc newInitProc(globals: PGlobals, module: BModule): PProc =
+  result = newProc(globals, module, nil, initProcOptions(module))
 
 const
   MappedToObject = {tyObject, tyArray, tyTuple, tyOpenArray,
@@ -164,38 +187,40 @@ const
 proc mapType(typ: PType): TJSTypeKind =
   let t = skipTypes(typ, abstractInst)
   case t.kind
-  of tyVar, tyRef, tyPtr, tyLent:
-    if skipTypes(t.lastSon, abstractInst).kind in MappedToObject:
+  of tyVar, tyRef, tyPtr:
+    if skipTypes(t.elementType, abstractInst).kind in MappedToObject:
       result = etyObject
     else:
       result = etyBaseIndex
   of tyPointer:
     # treat a tyPointer like a typed pointer to an array of bytes
     result = etyBaseIndex
-  of tyRange, tyDistinct, tyOrdinal, tyProxy:
-    result = mapType(t.sons[0])
+  of tyRange, tyDistinct, tyOrdinal, tyError, tyLent:
+    # tyLent is no-op as JS has pass-by-reference semantics
+    result = mapType(skipModifier t)
   of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt
   of tyBool: result = etyBool
   of tyFloat..tyFloat128: result = etyFloat
   of tySet: result = etyObject # map a set to a table
-  of tyString, tySequence, tyOpt: result = etySeq
-  of tyObject, tyArray, tyTuple, tyOpenArray, tyVarargs:
+  of tyString, tySequence: result = etySeq
+  of tyObject, tyArray, tyTuple, tyOpenArray, tyVarargs, tyUncheckedArray:
     result = etyObject
   of tyNil: result = etyNull
   of tyGenericParam, tyGenericBody, tyGenericInvocation,
      tyNone, tyFromExpr, tyForward, tyEmpty,
-     tyExpr, tyStmt, tyTypeDesc, tyBuiltInTypeClass, tyCompositeTypeClass,
+     tyUntyped, tyTyped, tyTypeDesc, tyBuiltInTypeClass, tyCompositeTypeClass,
      tyAnd, tyOr, tyNot, tyAnything, tyVoid:
     result = etyNone
   of tyGenericInst, tyInferred, tyAlias, tyUserTypeClass, tyUserTypeClassInst,
-     tySink:
-    result = mapType(typ.lastSon)
+     tySink, tyOwned:
+    result = mapType(typ.skipModifier)
   of tyStatic:
-    if t.n != nil: result = mapType(lastSon t)
+    if t.n != nil: result = mapType(skipModifier t)
     else: result = etyNone
   of tyProc: result = etyProc
-  of tyCString: result = etyString
-  of tyUnused, tyOptAsRef: doAssert(false, "mapType")
+  of tyCstring: result = etyString
+  of tyConcept, tyIterable:
+    raiseAssert "unreachable"
 
 proc mapType(p: PProc; typ: PType): TJSTypeKind =
   result = mapType(typ)
@@ -221,8 +246,8 @@ proc mangleName(m: BModule, s: PSym): Rope =
     for chr in name:
       if chr notin {'A'..'Z','a'..'z','_','$','0'..'9'}:
         return false
-  result = s.loc.r
-  if result == nil:
+  result = s.loc.snippet
+  if result == "":
     if s.kind == skField and s.name.s.validJsName:
       result = rope(s.name.s)
     elif s.kind == skTemp:
@@ -233,25 +258,26 @@ proc mangleName(m: BModule, s: PSym): Rope =
       while i < s.name.s.len:
         let c = s.name.s[i]
         case c
-        of 'A'..'Z':
-          if i > 0 and s.name.s[i-1] in {'a'..'z'}:
-            x.add '_'
-          x.add(chr(c.ord - 'A'.ord + 'a'.ord))
-        of 'a'..'z', '_', '0'..'9':
+        of 'A'..'Z', 'a'..'z', '_', '0'..'9':
           x.add c
         else:
           x.add("HEX" & toHex(ord(c), 2))
         inc i
       result = rope(x)
-    if s.name.s != "this" and s.kind != skField:
-      if optHotCodeReloading in m.config.options:
+    # From ES5 on reserved words can be used as object field names
+    if s.kind != skField:
+      if m.config.hcrOn:
         # When hot reloading is enabled, we must ensure that the names
         # of functions and types will be preserved across rebuilds:
-        add(result, idOrSig(s, m.module.name.s, m.sigConflicts))
+        result.add(idOrSig(s, m.module.name.s, m.sigConflicts, m.config))
+      elif s.kind == skParam:
+        result.add mangleParamExt(s)
+      elif s.kind in routineKinds:
+        result.add mangleProcNameExt(m.graph, s)
       else:
-        add(result, "_")
-        add(result, rope(s.id))
-    s.loc.r = result
+        result.add("_")
+        result.add(rope(s.id))
+    s.loc.snippet = result
 
 proc escapeJSString(s: string): string =
   result = newStringOfCap(s.len + s.len shr 2)
@@ -267,17 +293,30 @@ proc escapeJSString(s: string): string =
     of '\v': result.add("\\v")
     of '\\': result.add("\\\\")
     of '\"': result.add("\\\"")
-    else: add(result, c)
+    else: result.add(c)
   result.add("\"")
 
 proc makeJSString(s: string, escapeNonAscii = true): Rope =
-  if s.isNil:
-    result = "null".rope
-  elif escapeNonAscii:
+  if escapeNonAscii:
     result = strutils.escape(s).rope
   else:
     result = escapeJSString(s).rope
 
+proc makeJsNimStrLit(s: string): Rope =
+  var x = newStringOfCap(4*s.len+1)
+  x.add "["
+  var i = 0
+  if i < s.len:
+    x.addInt int64(s[i])
+    inc i
+  while i < s.len:
+    x.add ","
+    x.addInt int64(s[i])
+    inc i
+  x.add "]"
+  result = rope(x)
+
+
 include jstypes
 
 proc gen(p: PProc, n: PNode, r: var TCompRes)
@@ -292,7 +331,7 @@ proc useMagic(p: PProc, name: string) =
     internalAssert p.config, s.kind in {skProc, skFunc, skMethod, skConverter}
     if not p.g.generatedSyms.containsOrIncl(s.id):
       let code = genProc(p, s)
-      add(p.g.constants, code)
+      p.g.constants.add(code)
   else:
     if p.prc != nil:
       globalError(p.config, p.prc.info, "system module needs: " & name)
@@ -301,23 +340,30 @@ proc useMagic(p: PProc, name: string) =
 
 proc isSimpleExpr(p: PProc; n: PNode): bool =
   # calls all the way down --> can stay expression based
-  if n.kind in nkCallKinds+{nkBracketExpr, nkDotExpr, nkPar, nkTupleConstr} or
-      (n.kind in {nkObjConstr, nkBracket, nkCurly}):
+  case n.kind
+  of nkCallKinds, nkBracketExpr, nkDotExpr, nkPar, nkTupleConstr,
+    nkObjConstr, nkBracket, nkCurly,
+    nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr,
+    nkConv, nkHiddenStdConv, nkHiddenSubConv:
     for c in n:
       if not p.isSimpleExpr(c): return false
     result = true
-  elif n.isAtom:
-    result = true
+  of nkStmtListExpr:
+    for i in 0..<n.len-1:
+      if n[i].kind notin {nkCommentStmt, nkEmpty}: return false
+    result = isSimpleExpr(p, n.lastSon)
+  else:
+    result = n.isAtom
 
 proc getTemp(p: PProc, defineInLocals: bool = true): Rope =
   inc(p.unique)
-  result = "Tmp$1" % [rope(p.unique)]
+  result = "Temporary$1" % [rope(p.unique)]
   if defineInLocals:
-    add(p.locals, p.indentLine("var $1;$n" % [result]))
+    p.locals.add(p.indentLine("var $1;$n" % [result]))
 
 proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) =
   assert r.kind == resNone
-  var x, y: TCompRes
+  var x, y: TCompRes = default(TCompRes)
   if p.isSimpleExpr(a) and p.isSimpleExpr(b):
     gen(p, a, x)
     gen(p, b, y)
@@ -344,7 +390,7 @@ proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) =
 
 proc genOr(p: PProc, a, b: PNode, r: var TCompRes) =
   assert r.kind == resNone
-  var x, y: TCompRes
+  var x, y: TCompRes = default(TCompRes)
   if p.isSimpleExpr(a) and p.isSimpleExpr(b):
     gen(p, a, x)
     gen(p, b, y)
@@ -361,169 +407,458 @@ proc genOr(p: PProc, a, b: PNode, r: var TCompRes) =
     line(p, "}")
 
 type
-  TMagicFrmt = array[0..3, string]
+  TMagicFrmt = array[0..1, string]
   TMagicOps = array[mAddI..mStrToStr, TMagicFrmt]
 
-const # magic checked op; magic unchecked op; checked op; unchecked op
-  jsOps: TMagicOps = [
-    ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # AddI
-    ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # SubI
-    ["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI
-    ["divInt", "", "divInt($1, $2)", "Math.trunc($1 / $2)"], # DivI
-    ["modInt", "", "modInt($1, $2)", "Math.trunc($1 % $2)"], # ModI
-    ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ
-    ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred
-    ["", "", "($1 + $2)", "($1 + $2)"], # AddF64
-    ["", "", "($1 - $2)", "($1 - $2)"], # SubF64
-    ["", "", "($1 * $2)", "($1 * $2)"], # MulF64
-    ["", "", "($1 / $2)", "($1 / $2)"], # DivF64
-    ["", "", "", ""], # ShrI
-    ["", "", "($1 << $2)", "($1 << $2)"], # ShlI
-    ["", "", "($1 & $2)", "($1 & $2)"], # BitandI
-    ["", "", "($1 | $2)", "($1 | $2)"], # BitorI
-    ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI
-    ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI
-    ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI
-    ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
-    ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
-    ["", "", "", ""], # addU
-    ["", "", "", ""], # subU
-    ["", "", "", ""], # mulU
-    ["", "", "", ""], # divU
-    ["", "", "($1 % $2)", "($1 % $2)"], # modU
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqI
-    ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
-    ["", "", "($1 < $2)", "($1 < $2)"], # LtI
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqF64
-    ["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
-    ["", "", "($1 < $2)", "($1 < $2)"], # LtF64
-    ["", "", "($1 <= $2)", "($1 <= $2)"], # leU
-    ["", "", "($1 < $2)", "($1 < $2)"], # ltU
-    ["", "", "($1 <= $2)", "($1 <= $2)"], # leU64
-    ["", "", "($1 < $2)", "($1 < $2)"], # ltU64
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqEnum
-    ["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum
-    ["", "", "($1 < $2)", "($1 < $2)"], # LtEnum
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqCh
-    ["", "", "($1 <= $2)", "($1 <= $2)"], # LeCh
-    ["", "", "($1 < $2)", "($1 < $2)"], # LtCh
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqB
-    ["", "", "($1 <= $2)", "($1 <= $2)"], # LeB
-    ["", "", "($1 < $2)", "($1 < $2)"], # LtB
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqRef
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqUntracedRef
-    ["", "", "($1 <= $2)", "($1 <= $2)"], # LePtr
-    ["", "", "($1 < $2)", "($1 < $2)"], # LtPtr
-    ["", "", "($1 != $2)", "($1 != $2)"], # Xor
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqCString
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqProc
-    ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
-    ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
-    ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI
-    ["", "", "!($1)", "!($1)"], # Not
-    ["", "", "+($1)", "+($1)"], # UnaryPlusI
-    ["", "", "~($1)", "~($1)"], # BitnotI
-    ["", "", "+($1)", "+($1)"], # UnaryPlusF64
-    ["", "", "-($1)", "-($1)"], # UnaryMinusF64
-    ["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64
-    ["Ze8ToI", "Ze8ToI", "Ze8ToI($1)", "Ze8ToI($1)"], # mZe8ToI
-    ["Ze8ToI64", "Ze8ToI64", "Ze8ToI64($1)", "Ze8ToI64($1)"], # mZe8ToI64
-    ["Ze16ToI", "Ze16ToI", "Ze16ToI($1)", "Ze16ToI($1)"], # mZe16ToI
-    ["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64
-    ["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64
-    ["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64
-    ["toU8", "toU8", "toU8($1)", "toU8($1)"], # toU8
-    ["toU16", "toU16", "toU16($1)", "toU16($1)"], # toU16
-    ["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32
-    ["", "", "$1", "$1"],     # ToFloat
-    ["", "", "$1", "$1"],     # ToBiggestFloat
-    ["", "", "Math.trunc($1)", "Math.trunc($1)"], # ToInt
-    ["", "", "Math.trunc($1)", "Math.trunc($1)"], # ToBiggestInt
-    ["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"],
-    ["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"],
-    ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
-    ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
-    ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
-    ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"],
-    ["", "", "$1", "$1"]]
-
-proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
-  var x, y: TCompRes
+const # magic checked op; magic unchecked op;
+  jsMagics: TMagicOps = [
+    mAddI: ["addInt", ""],
+    mSubI: ["subInt", ""],
+    mMulI: ["mulInt", ""],
+    mDivI: ["divInt", ""],
+    mModI: ["modInt", ""],
+    mSucc: ["addInt", ""],
+    mPred: ["subInt", ""],
+    mAddF64: ["", ""],
+    mSubF64: ["", ""],
+    mMulF64: ["", ""],
+    mDivF64: ["", ""],
+    mShrI: ["", ""],
+    mShlI: ["", ""],
+    mAshrI: ["", ""],
+    mBitandI: ["", ""],
+    mBitorI: ["", ""],
+    mBitxorI: ["", ""],
+    mMinI: ["nimMin", "nimMin"],
+    mMaxI: ["nimMax", "nimMax"],
+    mAddU: ["", ""],
+    mSubU: ["", ""],
+    mMulU: ["", ""],
+    mDivU: ["", ""],
+    mModU: ["", ""],
+    mEqI: ["", ""],
+    mLeI: ["", ""],
+    mLtI: ["", ""],
+    mEqF64: ["", ""],
+    mLeF64: ["", ""],
+    mLtF64: ["", ""],
+    mLeU: ["", ""],
+    mLtU: ["", ""],
+    mEqEnum: ["", ""],
+    mLeEnum: ["", ""],
+    mLtEnum: ["", ""],
+    mEqCh: ["", ""],
+    mLeCh: ["", ""],
+    mLtCh: ["", ""],
+    mEqB: ["", ""],
+    mLeB: ["", ""],
+    mLtB: ["", ""],
+    mEqRef: ["", ""],
+    mLePtr: ["", ""],
+    mLtPtr: ["", ""],
+    mXor: ["", ""],
+    mEqCString: ["", ""],
+    mEqProc: ["", ""],
+    mUnaryMinusI: ["negInt", ""],
+    mUnaryMinusI64: ["negInt64", ""],
+    mAbsI: ["absInt", ""],
+    mNot: ["", ""],
+    mUnaryPlusI: ["", ""],
+    mBitnotI: ["", ""],
+    mUnaryPlusF64: ["", ""],
+    mUnaryMinusF64: ["", ""],
+    mCharToStr: ["nimCharToStr", "nimCharToStr"],
+    mBoolToStr: ["nimBoolToStr", "nimBoolToStr"],
+    mCStrToStr: ["cstrToNimstr", "cstrToNimstr"],
+    mStrToStr: ["", ""]]
+
+proc needsTemp(p: PProc; n: PNode): bool =
+  # check if n contains a call to determine
+  # if a temp should be made to prevent multiple evals
+  result = false
+  if n.kind in nkCallKinds + {nkTupleConstr, nkObjConstr, nkBracket, nkCurly}:
+    return true
+  for c in n:
+    if needsTemp(p, c):
+      return true
+
+proc maybeMakeTemp(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rope] =
+  var
+    a = x.rdLoc
+    b = a
+  if needsTemp(p, n):
+    # if we have tmp just use it
+    if x.tmpLoc != "" and (mapType(n.typ) == etyBaseIndex or n.kind in {nkHiddenDeref, nkDerefExpr}):
+      b = "$1[0][$1[1]]" % [x.tmpLoc]
+      (a: a, tmp: b)
+    else:
+      let tmp = p.getTemp
+      b = tmp
+      a = "($1 = $2, $1)" % [tmp, a]
+      (a: a, tmp: b)
+  else:
+    (a: a, tmp: b)
+
+proc maybeMakeTempAssignable(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rope] =
+  var
+    a = x.rdLoc
+    b = a
+  if needsTemp(p, n):
+    # if we have tmp just use it
+    if x.tmpLoc != "" and (mapType(n.typ) == etyBaseIndex or n.kind in {nkHiddenDeref, nkDerefExpr}):
+      b = "$1[0][$1[1]]" % [x.tmpLoc]
+      result = (a: a, tmp: b)
+    elif x.tmpLoc != "" and n.kind == nkBracketExpr:
+      # genArrayAddr
+      var
+        address, index: TCompRes = default(TCompRes)
+        first: Int128 = Zero
+      gen(p, n[0], address)
+      gen(p, n[1], index)
+      let (m1, tmp1) = maybeMakeTemp(p, n[0], address)
+      let typ = skipTypes(n[0].typ, abstractPtrs)
+      if typ.kind == tyArray:
+        first = firstOrd(p.config, typ.indexType)
+      if optBoundsCheck in p.options:
+        useMagic(p, "chckIndx")
+        if first == 0: # save a couple chars
+          index.res = "chckIndx($1, 0, ($2).length - 1)" % [index.res, tmp1]
+        else:
+          index.res = "chckIndx($1, $2, ($3).length + ($2) - 1) - ($2)" % [
+            index.res, rope(first), tmp1]
+      elif first != 0:
+        index.res = "($1) - ($2)" % [index.res, rope(first)]
+      else:
+        discard # index.res = index.res
+      let (n1, tmp2) = maybeMakeTemp(p, n[1], index)
+      result = (a: "$1[$2]" % [m1, n1], tmp: "$1[$2]" % [tmp1, tmp2])
+    # could also put here: nkDotExpr -> genFieldAccess, nkCheckedFieldExpr -> genCheckedFieldOp
+    # but the uses of maybeMakeTempAssignable don't need them
+    else:
+      result = (a: a, tmp: b)
+  else:
+    result = (a: a, tmp: b)
+
+template binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string,
+                    reassign = false) =
+  # $1 and $2 in the `frmt` string bind to lhs and rhs of the expr,
+  # if $3 or $4 are present they will be substituted with temps for
+  # lhs and rhs respectively
+  var x, y: TCompRes = default(TCompRes)
   useMagic(p, magic)
-  gen(p, n.sons[1], x)
-  gen(p, n.sons[2], y)
-  r.res = frmt % [x.rdLoc, y.rdLoc]
+  gen(p, n[1], x)
+  gen(p, n[2], y)
+
+  var
+    a, tmp = x.rdLoc
+    b, tmp2 = y.rdLoc
+  when reassign:
+    (a, tmp) = maybeMakeTempAssignable(p, n[1], x)
+  else:
+    when "$3" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x)
+    when "$4" in frmt: (b, tmp2) = maybeMakeTemp(p, n[2], y)
+
+  r.res = frmt % [a, b, tmp, tmp2]
   r.kind = resExpr
 
-proc unsignedTrimmerJS(size: BiggestInt): Rope =
+proc unsignedTrimmer(size: BiggestInt): string =
   case size
-  of 1: rope"& 0xff"
-  of 2: rope"& 0xffff"
-  of 4: rope">>> 0"
-  else: rope""
-
+  of 1: "& 0xff"
+  of 2: "& 0xffff"
+  of 4: ">>> 0"
+  else: ""
 
-template unsignedTrimmer(size: BiggestInt): Rope =
-  size.unsignedTrimmerJS
+proc signedTrimmer(size: BiggestInt): string =
+  # sign extension is done by shifting to the left and then back to the right
+  "<< $1 >> $1" % [$(32 - size * 8)]
 
 proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string,
-                    reassign = false) =
-  var x, y: TCompRes
-  gen(p, n.sons[1], x)
-  gen(p, n.sons[2], y)
-  let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size)
-  if reassign:
-    r.res = "$1 = (($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer]
+                    reassign: static[bool] = false) =
+  var x, y: TCompRes = default(TCompRes)
+  gen(p, n[1], x)
+  gen(p, n[2], y)
+  let size = n[1].typ.skipTypes(abstractRange).size
+  when reassign:
+    let (a, tmp) = maybeMakeTempAssignable(p, n[1], x)
+    if size == 8 and optJsBigInt64 in p.config.globalOptions:
+      r.res = "$1 = BigInt.asUintN(64, ($4 $2 $3))" % [a, rope op, y.rdLoc, tmp]
+    else:
+      let trimmer = unsignedTrimmer(size)
+      r.res = "$1 = (($5 $2 $3) $4)" % [a, rope op, y.rdLoc, trimmer, tmp]
   else:
-    r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer]
+    if size == 8 and optJsBigInt64 in p.config.globalOptions:
+      r.res = "BigInt.asUintN(64, ($1 $2 $3))" % [x.rdLoc, rope op, y.rdLoc]
+    else:
+      let trimmer = unsignedTrimmer(size)
+      r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer]
+  r.kind = resExpr
 
-proc ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
+template ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
   var x, y, z: TCompRes
   useMagic(p, magic)
-  gen(p, n.sons[1], x)
-  gen(p, n.sons[2], y)
-  gen(p, n.sons[3], z)
+  gen(p, n[1], x)
+  gen(p, n[2], y)
+  gen(p, n[3], z)
   r.res = frmt % [x.rdLoc, y.rdLoc, z.rdLoc]
   r.kind = resExpr
 
-proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
+template unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
+  # $1 binds to n[1], if $2 is present it will be substituted to a tmp of $1
   useMagic(p, magic)
-  gen(p, n.sons[1], r)
-  r.res = frmt % [r.rdLoc]
+  gen(p, n[1], r)
+  var a, tmp = r.rdLoc
+  if "$2" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], r)
+  r.res = frmt % [a, tmp]
+  r.kind = resExpr
+
+proc genBreakState(p: PProc, n: PNode, r: var TCompRes) =
+  var a: TCompRes = default(TCompRes)
+  # mangle `:state` properly somehow
+  if n.kind == nkClosure:
+    gen(p, n[1], a)
+    r.res = "(($1).HEX3Astate < 0)" % [rdLoc(a)]
+  else:
+    gen(p, n, a)
+    r.res = "((($1.ClE_0).HEX3Astate) < 0)" % [rdLoc(a)]
   r.kind = resExpr
 
 proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
   var
-    x, y: TCompRes
+    x, y: TCompRes = default(TCompRes)
+    xLoc, yLoc: Rope = ""
   let i = ord(optOverflowCheck notin p.options)
-  useMagic(p, jsOps[op][i])
-  if sonsLen(n) > 2:
-    gen(p, n.sons[1], x)
-    gen(p, n.sons[2], y)
-    r.res = jsOps[op][i + 2] % [x.rdLoc, y.rdLoc]
+  useMagic(p, jsMagics[op][i])
+  if n.len > 2:
+    gen(p, n[1], x)
+    gen(p, n[2], y)
+    xLoc = x.rdLoc
+    yLoc = y.rdLoc
   else:
-    gen(p, n.sons[1], r)
-    r.res = jsOps[op][i + 2] % [r.rdLoc]
+    gen(p, n[1], r)
+    xLoc = r.rdLoc
+
+  template applyFormat(frmt) =
+    r.res = frmt % [xLoc, yLoc]
+  template applyFormat(frmtA, frmtB) =
+    if i == 0: applyFormat(frmtA) else: applyFormat(frmtB)
+
+  template bitwiseExpr(op: string) =
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    if typ.kind in {tyUInt, tyUInt32}:
+      r.res = "(($1 $2 $3) >>> 0)" % [xLoc, op, yLoc]
+    else:
+      r.res = "($1 $2 $3)" % [xLoc, op, yLoc]
+
+  case op
+  of mAddI:
+    if i == 0:
+      if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
+        useMagic(p, "addInt64")
+        applyFormat("addInt64($1, $2)")
+      else:
+        applyFormat("addInt($1, $2)")
+    else:
+      applyFormat("($1 + $2)")
+  of mSubI:
+    if i == 0:
+      if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
+        useMagic(p, "subInt64")
+        applyFormat("subInt64($1, $2)")
+      else:
+        applyFormat("subInt($1, $2)")
+    else:
+      applyFormat("($1 - $2)")
+  of mMulI:
+    if i == 0:
+      if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
+        useMagic(p, "mulInt64")
+        applyFormat("mulInt64($1, $2)")
+      else:
+        applyFormat("mulInt($1, $2)")
+    else:
+      applyFormat("($1 * $2)")
+  of mDivI:
+    if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
+      useMagic(p, "divInt64")
+      applyFormat("divInt64($1, $2)", "$1 / $2")
+    else:
+      applyFormat("divInt($1, $2)", "Math.trunc($1 / $2)")
+  of mModI:
+    if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
+      useMagic(p, "modInt64")
+      applyFormat("modInt64($1, $2)", "$1 % $2")
+    else:
+      applyFormat("modInt($1, $2)", "Math.trunc($1 % $2)")
+  of mSucc:
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    case typ.kind
+    of tyUInt..tyUInt32:
+      binaryUintExpr(p, n, r, "+")
+    of tyUInt64:
+      if optJsBigInt64 in p.config.globalOptions:
+        applyFormat("BigInt.asUintN(64, $1 + BigInt($2))")
+      else: binaryUintExpr(p, n, r, "+")
+    elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
+      if optOverflowCheck notin p.options:
+        applyFormat("BigInt.asIntN(64, $1 + BigInt($2))")
+      else: binaryExpr(p, n, r, "addInt64", "addInt64($1, BigInt($2))")
+    else:
+      if optOverflowCheck notin p.options: applyFormat("$1 + $2")
+      else: binaryExpr(p, n, r, "addInt", "addInt($1, $2)")
+  of mPred:
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    case typ.kind
+    of tyUInt..tyUInt32:
+      binaryUintExpr(p, n, r, "-")
+    of tyUInt64:
+      if optJsBigInt64 in p.config.globalOptions:
+        applyFormat("BigInt.asUintN(64, $1 - BigInt($2))")
+      else: binaryUintExpr(p, n, r, "-")
+    elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
+      if optOverflowCheck notin p.options:
+        applyFormat("BigInt.asIntN(64, $1 - BigInt($2))")
+      else: binaryExpr(p, n, r, "subInt64", "subInt64($1, BigInt($2))")
+    else:
+      if optOverflowCheck notin p.options: applyFormat("$1 - $2")
+      else: binaryExpr(p, n, r, "subInt", "subInt($1, $2)")
+  of mAddF64: applyFormat("($1 + $2)", "($1 + $2)")
+  of mSubF64: applyFormat("($1 - $2)", "($1 - $2)")
+  of mMulF64: applyFormat("($1 * $2)", "($1 * $2)")
+  of mDivF64: applyFormat("($1 / $2)", "($1 / $2)")
+  of mShrI:
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
+      applyFormat("BigInt.asIntN(64, BigInt.asUintN(64, $1) >> BigInt($2))")
+    elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
+      applyFormat("($1 >> BigInt($2))")
+    else:
+      if typ.kind in {tyInt..tyInt32}:
+        let trimmerU = unsignedTrimmer(typ.size)
+        let trimmerS = signedTrimmer(typ.size)
+        r.res = "((($1 $2) >>> $3) $4)" % [xLoc, trimmerU, yLoc, trimmerS]
+      else:
+        applyFormat("($1 >>> $2)")
+  of mShlI:
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    if typ.size == 8:
+      if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
+        applyFormat("BigInt.asIntN(64, $1 << BigInt($2))")
+      elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
+        applyFormat("BigInt.asUintN(64, $1 << BigInt($2))")
+      else:
+        applyFormat("($1 * Math.pow(2, $2))")
+    else:
+      if typ.kind in {tyUInt..tyUInt32}:
+        let trimmer = unsignedTrimmer(typ.size)
+        r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer]
+      else:
+        let trimmer = signedTrimmer(typ.size)
+        r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer]
+  of mAshrI:
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    if typ.size == 8:
+      if optJsBigInt64 in p.config.globalOptions:
+        applyFormat("($1 >> BigInt($2))")
+      else:
+        applyFormat("Math.floor($1 / Math.pow(2, $2))")
+    else:
+      if typ.kind in {tyUInt..tyUInt32}:
+        applyFormat("($1 >>> $2)")
+      else:
+        applyFormat("($1 >> $2)")
+  of mBitandI: bitwiseExpr("&")
+  of mBitorI: bitwiseExpr("|")
+  of mBitxorI: bitwiseExpr("^")
+  of mMinI: applyFormat("nimMin($1, $2)", "nimMin($1, $2)")
+  of mMaxI: applyFormat("nimMax($1, $2)", "nimMax($1, $2)")
+  of mAddU: applyFormat("", "")
+  of mSubU: applyFormat("", "")
+  of mMulU: applyFormat("", "")
+  of mDivU: applyFormat("", "")
+  of mModU: applyFormat("($1 % $2)", "($1 % $2)")
+  of mEqI: applyFormat("($1 == $2)", "($1 == $2)")
+  of mLeI: applyFormat("($1 <= $2)", "($1 <= $2)")
+  of mLtI: applyFormat("($1 < $2)", "($1 < $2)")
+  of mEqF64: applyFormat("($1 == $2)", "($1 == $2)")
+  of mLeF64: applyFormat("($1 <= $2)", "($1 <= $2)")
+  of mLtF64: applyFormat("($1 < $2)", "($1 < $2)")
+  of mLeU: applyFormat("($1 <= $2)", "($1 <= $2)")
+  of mLtU: applyFormat("($1 < $2)", "($1 < $2)")
+  of mEqEnum: applyFormat("($1 == $2)", "($1 == $2)")
+  of mLeEnum: applyFormat("($1 <= $2)", "($1 <= $2)")
+  of mLtEnum: applyFormat("($1 < $2)", "($1 < $2)")
+  of mEqCh: applyFormat("($1 == $2)", "($1 == $2)")
+  of mLeCh: applyFormat("($1 <= $2)", "($1 <= $2)")
+  of mLtCh: applyFormat("($1 < $2)", "($1 < $2)")
+  of mEqB: applyFormat("($1 == $2)", "($1 == $2)")
+  of mLeB: applyFormat("($1 <= $2)", "($1 <= $2)")
+  of mLtB: applyFormat("($1 < $2)", "($1 < $2)")
+  of mEqRef: applyFormat("($1 == $2)", "($1 == $2)")
+  of mLePtr: applyFormat("($1 <= $2)", "($1 <= $2)")
+  of mLtPtr: applyFormat("($1 < $2)", "($1 < $2)")
+  of mXor: applyFormat("($1 != $2)", "($1 != $2)")
+  of mEqCString: applyFormat("($1 == $2)", "($1 == $2)")
+  of mEqProc: applyFormat("($1 == $2)", "($1 == $2)")
+  of mUnaryMinusI: applyFormat("negInt($1)", "-($1)")
+  of mUnaryMinusI64: applyFormat("negInt64($1)", "-($1)")
+  of mAbsI:
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
+      useMagic(p, "absInt64")
+      applyFormat("absInt64($1)", "absInt64($1)")
+    else:
+      applyFormat("absInt($1)", "Math.abs($1)")
+  of mNot: applyFormat("!($1)", "!($1)")
+  of mUnaryPlusI: applyFormat("+($1)", "+($1)")
+  of mBitnotI:
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    if typ.kind in {tyUInt..tyUInt64}:
+      if typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
+        applyFormat("BigInt.asUintN(64, ~($1))")
+      else:
+        let trimmer = unsignedTrimmer(typ.size)
+        r.res = "(~($1) $2)" % [xLoc, trimmer]
+    else:
+      applyFormat("~($1)")
+  of mUnaryPlusF64: applyFormat("+($1)", "+($1)")
+  of mUnaryMinusF64: applyFormat("-($1)", "-($1)")
+  of mCharToStr: applyFormat("nimCharToStr($1)", "nimCharToStr($1)")
+  of mBoolToStr: applyFormat("nimBoolToStr($1)", "nimBoolToStr($1)")
+  of mCStrToStr: applyFormat("cstrToNimstr($1)", "cstrToNimstr($1)")
+  of mStrToStr, mUnown, mIsolate, mFinished: applyFormat("$1", "$1")
+  else:
+    assert false, $op
 
 proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
   case op
   of mAddU: binaryUintExpr(p, n, r, "+")
   of mSubU: binaryUintExpr(p, n, r, "-")
   of mMulU: binaryUintExpr(p, n, r, "*")
-  of mDivU: binaryUintExpr(p, n, r, "/")
+  of mDivU:
+    binaryUintExpr(p, n, r, "/")
+    if optJsBigInt64 notin p.config.globalOptions and
+        n[1].typ.skipTypes(abstractRange).size == 8:
+      # bigint / already truncates
+      r.res = "Math.trunc($1)" % [r.res]
   of mDivI:
     arithAux(p, n, r, op)
   of mModI:
     arithAux(p, n, r, op)
-  of mShrI:
-    var x, y: TCompRes
-    gen(p, n.sons[1], x)
-    gen(p, n.sons[2], y)
-    let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size)
-    r.res = "(($1 $2) >>> $3)" % [x.rdLoc, trimmer, y.rdLoc]
-  of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr,
-      mCStrToStr, mStrToStr, mEnumToStr:
+  of mCharToStr, mBoolToStr, mCStrToStr, mStrToStr, mEnumToStr:
     arithAux(p, n, r, op)
+  of mEqRef:
+    if mapType(n[1].typ) != etyBaseIndex:
+      arithAux(p, n, r, op)
+    else:
+      var x, y: TCompRes = default(TCompRes)
+      gen(p, n[1], x)
+      gen(p, n[2], y)
+      r.res = "($# == $# && $# == $#)" % [x.address, y.address, x.res, y.res]
+  of mEqProc:
+    if skipTypes(n[1].typ, abstractInst).callConv == ccClosure:
+      binaryExpr(p, n, r, "cmpClosures", "cmpClosures($1, $2)")
+    else:
+      arithAux(p, n, r, op)
   else:
     arithAux(p, n, r, op)
   r.kind = resExpr
@@ -532,36 +867,42 @@ proc hasFrameInfo(p: PProc): bool =
   ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and
       ((p.prc == nil) or not (sfPure in p.prc.flags))
 
+proc lineDir(config: ConfigRef, info: TLineInfo, line: int): Rope =
+  "/* line $2:$3 \"$1\" */$n" % [
+    rope(toFullPath(config, info)), rope(line), rope(info.toColumn)
+  ]
+
 proc genLineDir(p: PProc, n: PNode) =
   let line = toLinenumber(n.info)
-  if optLineDir in p.options:
-    lineF(p, "// line $2 \"$1\"$n",
-         [rope(toFilename(p.config, n.info)), rope(line)])
-  if {optStackTrace, optEndb} * p.options == {optStackTrace, optEndb} and
-      ((p.prc == nil) or sfPure notin p.prc.flags):
-    useMagic(p, "endb")
-    lineF(p, "endb($1);$n", [rope(line)])
-  elif hasFrameInfo(p):
+  if line < 0:
+    return
+  if optEmbedOrigSrc in p.config.globalOptions:
+    lineF(p, "//$1$n", [sourceLine(p.config, n.info)])
+  if optLineDir in p.options or optLineDir in p.config.options:
+    lineF(p, "$1", [lineDir(p.config, n.info, line)])
+  if hasFrameInfo(p):
     lineF(p, "F.line = $1;$n", [rope(line)])
+    let currentFileName = toFilename(p.config, n.info)
+    if p.previousFileName != currentFileName:
+      lineF(p, "F.filename = $1;$n", [makeJSString(currentFileName)])
+      p.previousFileName = currentFileName
 
 proc genWhileStmt(p: PProc, n: PNode) =
-  var
-    cond: TCompRes
+  var cond: TCompRes = default(TCompRes)
   internalAssert p.config, isEmptyType(n.typ)
   genLineDir(p, n)
   inc(p.unique)
-  var length = len(p.blocks)
-  setLen(p.blocks, length + 1)
-  p.blocks[length].id = -p.unique
-  p.blocks[length].isLoop = true
+  setLen(p.blocks, p.blocks.len + 1)
+  p.blocks[^1].id = -p.unique
+  p.blocks[^1].isLoop = true
   let labl = p.unique.rope
-  lineF(p, "L$1: while (true) {$n", [labl])
-  p.nested: gen(p, n.sons[0], cond)
-  lineF(p, "if (!$1) break L$2;$n",
+  lineF(p, "Label$1: while (true) {$n", [labl])
+  p.nested: gen(p, n[0], cond)
+  lineF(p, "if (!$1) break Label$2;$n",
        [cond.res, labl])
-  p.nested: genStmt(p, n.sons[1])
+  p.nested: genStmt(p, n[1])
   lineF(p, "}$n", [labl])
-  setLen(p.blocks, length)
+  setLen(p.blocks, p.blocks.len - 1)
 
 proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) =
   if src.kind != resNone:
@@ -570,7 +911,7 @@ proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) =
     else:
       lineF(p, "$1;$n", [src.rdLoc])
     src.kind = resNone
-    src.res = nil
+    src.res = ""
 
 proc genTry(p: PProc, n: PNode, r: var TCompRes) =
   # code to generate:
@@ -580,8 +921,8 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
   #  try {
   #    stmts;
   #    --excHandler;
-  #  } catch (EXC) {
-  #    var prevJSError = lastJSError; lastJSError = EXC;
+  #  } catch (EXCEPTION) {
+  #    var prevJSError = lastJSError; lastJSError = EXCEPTION;
   #    framePtr = tmpFramePtr;
   #    --excHandler;
   #    if (e.typ && e.typ == NTI433 || e.typ == NTI2321) {
@@ -602,45 +943,68 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
     r.res = getTemp(p)
   inc(p.unique)
   var i = 1
-  var length = sonsLen(n)
-  var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch
+  var catchBranchesExist = n.len > 1 and n[i].kind == nkExceptBranch
   if catchBranchesExist:
-    add(p.body, "++excHandler;\L")
+    p.body.add("++excHandler;\L")
   var tmpFramePtr = rope"F"
-  if optStackTrace notin p.options:
-    tmpFramePtr = p.getTemp(true)
-    line(p, tmpFramePtr & " = framePtr;\L")
   lineF(p, "try {$n", [])
-  var a: TCompRes
-  gen(p, n.sons[0], a)
+  var a: TCompRes = default(TCompRes)
+  gen(p, n[0], a)
   moveInto(p, a, r)
   var generalCatchBranchExists = false
-  let dollar = rope("")
   if catchBranchesExist:
-    addf(p.body, "--excHandler;$n} catch (EXC) {$n var prevJSError = lastJSError;$n" &
-        " lastJSError = EXC;$n --excHandler;$n", [])
-    line(p, "framePtr = $1;$n" % [tmpFramePtr])
-  while i < length and n.sons[i].kind == nkExceptBranch:
-    let blen = sonsLen(n.sons[i])
-    if blen == 1:
+    p.body.addf("--excHandler;$n} catch (EXCEPTION) {$n var prevJSError = lastJSError;$n" &
+        " lastJSError = EXCEPTION;$n --excHandler;$n", [])
+    if hasFrameInfo(p):
+      line(p, "framePtr = $1;$n" % [tmpFramePtr])
+  while i < n.len and n[i].kind == nkExceptBranch:
+    if n[i].len == 1:
       # general except section:
       generalCatchBranchExists = true
       if i > 1: lineF(p, "else {$n", [])
-      gen(p, n.sons[i].sons[0], a)
+      gen(p, n[i][0], a)
       moveInto(p, a, r)
       if i > 1: lineF(p, "}$n", [])
     else:
-      var orExpr: Rope = nil
+      var orExpr: Rope = ""
+      var excAlias: PNode = nil
+
       useMagic(p, "isObj")
-      for j in countup(0, blen - 2):
-        if n.sons[i].sons[j].kind != nkType:
+      for j in 0..<n[i].len - 1:
+        var throwObj: PNode
+        let it = n[i][j]
+
+        if it.isInfixAs():
+          throwObj = it[1]
+          excAlias = it[2]
+          # If this is a ``except exc as sym`` branch there must be no following
+          # nodes
+          doAssert orExpr == ""
+        elif it.kind == nkType:
+          throwObj = it
+        else:
+          throwObj = nil
           internalError(p.config, n.info, "genTryStmt")
-        if orExpr != nil: add(orExpr, "||")
-        addf(orExpr, "isObj($2lastJSError.m_type, $1)",
-             [genTypeInfo(p, n.sons[i].sons[j].typ), dollar])
+
+        if orExpr != "": orExpr.add("||")
+        # Generate the correct type checking code depending on whether this is a
+        # NIM-native or a JS-native exception
+        # if isJsObject(throwObj.typ):
+        if isImportedException(throwObj.typ, p.config):
+          orExpr.addf("lastJSError instanceof $1",
+            [throwObj.typ.sym.loc.snippet])
+        else:
+          orExpr.addf("isObj(lastJSError.m_type, $1)",
+               [genTypeInfo(p, throwObj.typ)])
+
       if i > 1: line(p, "else ")
-      lineF(p, "if ($1lastJSError && ($2)) {$n", [dollar, orExpr])
-      gen(p, n.sons[i].sons[blen - 1], a)
+      lineF(p, "if (lastJSError && ($1)) {$n", [orExpr])
+      # If some branch requires a local alias introduce it here. This is needed
+      # since JS cannot do ``catch x as y``.
+      if excAlias != nil:
+        excAlias.sym.loc.snippet = mangleName(p.module, excAlias.sym)
+        lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.snippet)
+      gen(p, n[i][^1], a)
       moveInto(p, a, r)
       lineF(p, "}$n", [])
     inc(i)
@@ -650,113 +1014,163 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
       line(p, "else {\L")
       line(p, "\treraiseException();\L")
       line(p, "}\L")
-    addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar])
+    lineF(p, "lastJSError = prevJSError;$n")
   line(p, "} finally {\L")
-  line(p, "framePtr = $1;$n" % [tmpFramePtr])
-  if i < length and n.sons[i].kind == nkFinally:
-    genStmt(p, n.sons[i].sons[0])
+  if hasFrameInfo(p):
+    line(p, "framePtr = $1;$n" % [tmpFramePtr])
+  if i < n.len and n[i].kind == nkFinally:
+    genStmt(p, n[i][0])
   line(p, "}\L")
 
 proc genRaiseStmt(p: PProc, n: PNode) =
-  genLineDir(p, n)
-  if n.sons[0].kind != nkEmpty:
-    var a: TCompRes
-    gen(p, n.sons[0], a)
-    let typ = skipTypes(n.sons[0].typ, abstractPtrs)
+  if n[0].kind != nkEmpty:
+    var a: TCompRes = default(TCompRes)
+    gen(p, n[0], a)
+    let typ = skipTypes(n[0].typ, abstractPtrs)
+    genLineDir(p, n)
     useMagic(p, "raiseException")
     lineF(p, "raiseException($1, $2);$n",
              [a.rdLoc, makeJSString(typ.sym.name.s)])
   else:
+    genLineDir(p, n)
     useMagic(p, "reraiseException")
     line(p, "reraiseException();\L")
 
 proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
   var
-    cond, stmt: TCompRes
+    a, b, cond, stmt: TCompRes = default(TCompRes)
   genLineDir(p, n)
-  gen(p, n.sons[0], cond)
-  let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString
-  if stringSwitch:
+  gen(p, n[0], cond)
+  let typeKind = skipTypes(n[0].typ, abstractVar+{tyRange}).kind
+  var transferRange = false
+  let anyString = typeKind in {tyString, tyCstring}
+  case typeKind
+  of tyString:
     useMagic(p, "toJSStr")
     lineF(p, "switch (toJSStr($1)) {$n", [cond.rdLoc])
+  of tyFloat..tyFloat128, tyInt..tyInt64, tyUInt..tyUInt64:
+    transferRange = true
   else:
     lineF(p, "switch ($1) {$n", [cond.rdLoc])
   if not isEmptyType(n.typ):
     r.kind = resVal
     r.res = getTemp(p)
-  for i in countup(1, sonsLen(n) - 1):
-    let it = n.sons[i]
+  for i in 1..<n.len:
+    let it = n[i]
+    let itLen = it.len
     case it.kind
     of nkOfBranch:
-      for j in countup(0, sonsLen(it) - 2):
-        let e = it.sons[j]
+      if transferRange:
+        if i == 1:
+          lineF(p, "if (", [])
+        else:
+          lineF(p, "else if (", [])
+      for j in 0..<itLen - 1:
+        let e = it[j]
         if e.kind == nkRange:
-          var v = copyNode(e.sons[0])
-          while v.intVal <= e.sons[1].intVal:
-            gen(p, v, cond)
-            lineF(p, "case $1:$n", [cond.rdLoc])
-            inc(v.intVal)
+          if transferRange:
+            gen(p, e[0], a)
+            gen(p, e[1], b)
+            if j != itLen - 2:
+              lineF(p, "$1 >= $2 && $1 <= $3 || $n", [cond.rdLoc, a.rdLoc, b.rdLoc])
+            else:
+              lineF(p, "$1 >= $2 && $1 <= $3", [cond.rdLoc, a.rdLoc, b.rdLoc])
+          else:
+            var v = copyNode(e[0])
+            while v.intVal <= e[1].intVal:
+              gen(p, v, cond)
+              lineF(p, "case $1:$n", [cond.rdLoc])
+              inc(v.intVal)
         else:
-          if stringSwitch:
+          if anyString:
             case e.kind
             of nkStrLit..nkTripleStrLit: lineF(p, "case $1:$n",
                 [makeJSString(e.strVal, false)])
+            of nkNilLit: lineF(p, "case null:$n", [])
             else: internalError(p.config, e.info, "jsgen.genCaseStmt: 2")
           else:
-            gen(p, e, cond)
-            lineF(p, "case $1:$n", [cond.rdLoc])
+            if transferRange:
+              gen(p, e, a)
+              if j != itLen - 2:
+                lineF(p, "$1 == $2 || $n", [cond.rdLoc, a.rdLoc])
+              else:
+                lineF(p, "$1 == $2", [cond.rdLoc, a.rdLoc])
+            else:
+              gen(p, e, a)
+              lineF(p, "case $1:$n", [a.rdLoc])
+      if transferRange:
+        lineF(p, "){", [])
       p.nested:
         gen(p, lastSon(it), stmt)
         moveInto(p, stmt, r)
-        lineF(p, "break;$n", [])
+        if transferRange:
+          lineF(p, "}$n", [])
+        else:
+          lineF(p, "break;$n", [])
     of nkElse:
-      lineF(p, "default: $n", [])
+      if transferRange:
+        if n.len == 2: # a dangling else for a case statement
+          transferRange = false
+          lineF(p, "switch ($1) {$n", [cond.rdLoc])
+          lineF(p, "default: $n", [])
+        else:
+          lineF(p, "else{$n", [])
+      else:
+        lineF(p, "default: $n", [])
       p.nested:
-        gen(p, it.sons[0], stmt)
+        gen(p, it[0], stmt)
         moveInto(p, stmt, r)
-        lineF(p, "break;$n", [])
+        if transferRange:
+          lineF(p, "}$n", [])
+        else:
+          lineF(p, "break;$n", [])
     else: internalError(p.config, it.info, "jsgen.genCaseStmt")
-  lineF(p, "}$n", [])
+  if not transferRange:
+    lineF(p, "}$n", [])
 
 proc genBlock(p: PProc, n: PNode, r: var TCompRes) =
   inc(p.unique)
-  let idx = len(p.blocks)
-  if n.sons[0].kind != nkEmpty:
+  let idx = p.blocks.len
+  if n[0].kind != nkEmpty:
     # named block?
-    if (n.sons[0].kind != nkSym): internalError(p.config, n.info, "genBlock")
-    var sym = n.sons[0].sym
+    if (n[0].kind != nkSym): internalError(p.config, n.info, "genBlock")
+    var sym = n[0].sym
     sym.loc.k = locOther
     sym.position = idx+1
   let labl = p.unique
-  lineF(p, "L$1: do {$n", [labl.rope])
+  lineF(p, "Label$1: {$n", [labl.rope])
   setLen(p.blocks, idx + 1)
   p.blocks[idx].id = - p.unique # negative because it isn't used yet
-  gen(p, n.sons[1], r)
+  gen(p, n[1], r)
   setLen(p.blocks, idx)
-  lineF(p, "} while(false);$n", [labl.rope])
+  lineF(p, "};$n", [labl.rope])
 
 proc genBreakStmt(p: PProc, n: PNode) =
   var idx: int
   genLineDir(p, n)
-  if n.sons[0].kind != nkEmpty:
+  if n[0].kind != nkEmpty:
     # named break?
-    assert(n.sons[0].kind == nkSym)
-    let sym = n.sons[0].sym
+    assert(n[0].kind == nkSym)
+    let sym = n[0].sym
     assert(sym.loc.k == locOther)
     idx = sym.position-1
   else:
     # an unnamed 'break' can only break a loop after 'transf' pass:
-    idx = len(p.blocks) - 1
+    idx = p.blocks.len - 1
     while idx >= 0 and not p.blocks[idx].isLoop: dec idx
     if idx < 0 or not p.blocks[idx].isLoop:
       internalError(p.config, n.info, "no loop to break")
   p.blocks[idx].id = abs(p.blocks[idx].id) # label is used
-  lineF(p, "break L$1;$n", [rope(p.blocks[idx].id)])
+  lineF(p, "break Label$1;$n", [rope(p.blocks[idx].id)])
 
-proc genAsmOrEmitStmt(p: PProc, n: PNode) =
+proc genAsmOrEmitStmt(p: PProc, n: PNode; isAsmStmt = false) =
   genLineDir(p, n)
-  p.body.add p.indentLine(nil)
-  for i in countup(0, sonsLen(n) - 1):
+  p.body.add p.indentLine("")
+  let offset =
+    if isAsmStmt: 1 # first son is pragmas
+    else: 0
+
+  for i in offset..<n.len:
     let it = n[i]
     case it.kind
     of nkStrLit..nkTripleStrLit:
@@ -764,59 +1178,81 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) =
     of nkSym:
       let v = it.sym
       # for backwards compatibility we don't deref syms here :-(
-      if v.kind in {skVar, skLet, skTemp, skConst, skResult, skParam, skForVar}:
-        p.body.add mangleName(p.module, v)
+      if false:
+        discard
       else:
-        var r: TCompRes
+        var r = default(TCompRes)
         gen(p, it, r)
+
+        if it.typ.kind == tyPointer:
+          # A fat pointer is disguised as an array
+          r.res = r.address
+          r.address = ""
+          r.typ = etyNone
+        elif r.typ == etyBaseIndex:
+          # Deference first
+          r.res = "$1[$2]" % [r.address, r.res]
+          r.address = ""
+          r.typ = etyNone
+
         p.body.add(r.rdLoc)
     else:
-      var r: TCompRes
+      var r: TCompRes = default(TCompRes)
       gen(p, it, r)
       p.body.add(r.rdLoc)
   p.body.add "\L"
 
 proc genIf(p: PProc, n: PNode, r: var TCompRes) =
-  var cond, stmt: TCompRes
+  var cond, stmt: TCompRes = default(TCompRes)
   var toClose = 0
   if not isEmptyType(n.typ):
     r.kind = resVal
     r.res = getTemp(p)
-  for i in countup(0, sonsLen(n) - 1):
-    let it = n.sons[i]
-    if sonsLen(it) != 1:
+  for i in 0..<n.len:
+    let it = n[i]
+    if it.len != 1:
       if i > 0:
         lineF(p, "else {$n", [])
         inc(toClose)
-      p.nested: gen(p, it.sons[0], cond)
+      p.nested: gen(p, it[0], cond)
       lineF(p, "if ($1) {$n", [cond.rdLoc])
-      gen(p, it.sons[1], stmt)
+      gen(p, it[1], stmt)
     else:
       # else part:
       lineF(p, "else {$n", [])
-      p.nested: gen(p, it.sons[0], stmt)
+      p.nested: gen(p, it[0], stmt)
     moveInto(p, stmt, r)
     lineF(p, "}$n", [])
   line(p, repeat('}', toClose) & "\L")
 
-proc generateHeader(p: PProc, typ: PType): Rope =
-  result = nil
-  for i in countup(1, sonsLen(typ.n) - 1):
-    assert(typ.n.sons[i].kind == nkSym)
-    var param = typ.n.sons[i].sym
+proc generateHeader(p: PProc, prc: PSym): Rope =
+  result = ""
+  let typ = prc.typ
+  if jsNoLambdaLifting notin p.config.legacyFeatures:
+    if typ.callConv == ccClosure:
+      # we treat Env as the `this` parameter of the function
+      # to keep it simple
+      let env = prc.ast[paramsPos].lastSon
+      assert env.kind == nkSym, "env is missing"
+      env.sym.loc.snippet = "this"
+
+  for i in 1..<typ.n.len:
+    assert(typ.n[i].kind == nkSym)
+    var param = typ.n[i].sym
     if isCompileTimeOnly(param.typ): continue
-    if result != nil: add(result, ", ")
+    if result != "": result.add(", ")
     var name = mangleName(p.module, param)
-    add(result, name)
+    result.add(name)
     if mapType(param.typ) == etyBaseIndex:
-      add(result, ", ")
-      add(result, name)
-      add(result, "_Idx")
+      result.add(", ")
+      result.add(name)
+      result.add("_Idx")
 
 proc countJsParams(typ: PType): int =
-  for i in countup(1, sonsLen(typ.n) - 1):
-    assert(typ.n.sons[i].kind == nkSym)
-    var param = typ.n.sons[i].sym
+  result = 0
+  for i in 1..<typ.n.len:
+    assert(typ.n[i].kind == nkSym)
+    var param = typ.n[i].sym
     if isCompileTimeOnly(param.typ): continue
     if mapType(param.typ) == etyBaseIndex:
       inc result, 2
@@ -825,67 +1261,87 @@ proc countJsParams(typ: PType): int =
 
 const
   nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
-    nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkTupleConstr, nkObjConstr, nkStringToCString,
+    nkFloatLit..nkFloat64Lit, nkPar, nkStringToCString,
+    nkObjConstr, nkTupleConstr, nkBracket,
     nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix,
     nkCommand, nkHiddenCallConv, nkCallStrLit}
 
 proc needsNoCopy(p: PProc; y: PNode): bool =
-  result = (y.kind in nodeKindsNeedNoCopy) or
-      (skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyLent, tyVar})
+  return y.kind in nodeKindsNeedNoCopy or
+        ((mapType(y.typ) != etyBaseIndex or
+          (jsNoLambdaLifting in p.config.legacyFeatures and y.kind == nkSym and y.sym.kind == skParam)) and
+          (skipTypes(y.typ, abstractInst).kind in
+            {tyRef, tyPtr, tyLent, tyVar, tyCstring, tyProc, tyOwned, tyOpenArray} + IntegralTypes))
 
 proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
-  var a, b: TCompRes
+  var a, b: TCompRes = default(TCompRes)
   var xtyp = mapType(p, x.typ)
 
-  if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject:
-    gen(p, x.sons[0], a)
-    let tmp = p.getTemp(false)
-    lineF(p, "var $1 = $2;$n", [tmp, a.rdLoc])
-    a.res = "$1[0][$1[1]]" % [tmp]
-  else:
-    gen(p, x, a)
+  # disable `[]=` for cstring
+  if x.kind == nkBracketExpr and x.len >= 2 and x[0].typ.skipTypes(abstractInst).kind == tyCstring:
+    localError(p.config, x.info, "cstring doesn't support `[]=` operator")
 
+  gen(p, x, a)
+  genLineDir(p, y)
   gen(p, y, b)
 
   # we don't care if it's an etyBaseIndex (global) of a string, it's
   # still a string that needs to be copied properly:
-  if x.typ.skipTypes(abstractInst).kind in {tySequence, tyOpt, tyString}:
+  if x.typ.skipTypes(abstractInst).kind in {tySequence, tyString}:
     xtyp = etySeq
   case xtyp
   of etySeq:
-    if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
+    if x.typ.kind in {tyVar, tyLent} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
       lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
     else:
       useMagic(p, "nimCopy")
       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 in {tyVar, tyLent, tyOpenArray, tyVarargs} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
       lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
     else:
       useMagic(p, "nimCopy")
-      lineF(p, "nimCopy($1, $2, $3);$n",
-               [a.res, b.res, genTypeInfo(p, y.typ)])
+      # supports proc getF(): var T
+      if x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].kind in nkCallKinds:
+          lineF(p, "nimCopy($1, $2, $3);$n",
+                [a.res, b.res, genTypeInfo(p, x.typ)])
+      else:
+        lineF(p, "$1 = nimCopy($1, $2, $3);$n",
+              [a.res, b.res, genTypeInfo(p, x.typ)])
   of etyBaseIndex:
     if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
       if y.kind == nkCall:
         let tmp = p.getTemp(false)
         lineF(p, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc])
       elif b.typ == etyBaseIndex:
-        lineF(p, "$# = $#;$n", [a.res, b.rdLoc])
+        lineF(p, "$# = [$#, $#];$n", [a.res, b.address, b.res])
+      elif b.typ == etyNone:
+        internalAssert p.config, b.address == ""
+        lineF(p, "$# = [$#, 0];$n", [a.address, b.res])
+      elif x.typ.kind == tyVar and y.typ.kind == tyPtr:
+        lineF(p, "$# = [$#, $#];$n", [a.res, b.address, b.res])
+        lineF(p, "$1 = $2;$n", [a.address, b.res])
+        lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
+      elif a.typ == etyBaseIndex:
+        # array indexing may not map to var type
+        if b.address != "":
+          lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res])
+        else:
+          lineF(p, "$1 = $2;$n", [a.address, b.res])
       else:
-        internalError(p.config, x.info, "genAsgn")
-    else:
+        internalError(p.config, x.info, $("genAsgn", b.typ, a.typ))
+    elif b.address != "":
       lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res])
+    else:
+      lineF(p, "$1 = $2;$n", [a.address, b.res])
   else:
-    lineF(p, "$1 = $2;$n", [a.res, b.res])
+    lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
 
 proc genAsgn(p: PProc, n: PNode) =
-  genLineDir(p, n)
-  genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=false)
+  genAsgnAux(p, n[0], n[1], noCopyNeeded=false)
 
 proc genFastAsgn(p: PProc, n: PNode) =
-  genLineDir(p, n)
   # 'shallowCopy' always produced 'noCopyNeeded = true' here but this is wrong
   # for code like
   #  while j >= pos:
@@ -893,184 +1349,270 @@ proc genFastAsgn(p: PProc, n: PNode) =
   # See bug #5933. So we try to be more compatible with the C backend semantics
   # here for 'shallowCopy'. This is an educated guess and might require further
   # changes later:
-  let noCopy = n[0].typ.skipTypes(abstractInst).kind in {tySequence, tyOpt, tyString}
-  genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=noCopy)
+  let noCopy = n[0].typ.skipTypes(abstractInst).kind in {tySequence, tyString}
+  genAsgnAux(p, n[0], n[1], noCopyNeeded=noCopy)
 
 proc genSwap(p: PProc, n: PNode) =
-  var a, b: TCompRes
-  gen(p, n.sons[1], a)
-  gen(p, n.sons[2], b)
-  var tmp = p.getTemp(false)
-  if mapType(p, skipTypes(n.sons[1].typ, abstractVar)) == etyBaseIndex:
-    let tmp2 = p.getTemp(false)
-    if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
-      internalError(p.config, n.info, "genSwap")
-    lineF(p, "var $1 = $2; $2 = $3; $3 = $1;$n",
-             [tmp, a.address, b.address])
-    tmp = tmp2
-  lineF(p, "var $1 = $2; $2 = $3; $3 = $1;",
-           [tmp, a.res, b.res])
+  let stmtList = lowerSwap(p.module.graph, n, p.module.idgen, if p.prc != nil: p.prc else: p.module.module)
+  assert stmtList.kind == nkStmtList
+  for i in 0..<stmtList.len:
+    genStmt(p, stmtList[i])
 
 proc getFieldPosition(p: PProc; f: PNode): int =
   case f.kind
   of nkIntLit..nkUInt64Lit: result = int(f.intVal)
   of nkSym: result = f.sym.position
-  else: internalError(p.config, f.info, "genFieldPosition")
+  else:
+    result = 0
+    internalError(p.config, f.info, "genFieldPosition")
 
 proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
-  var a: TCompRes
+  var a: TCompRes = default(TCompRes)
   r.typ = etyBaseIndex
-  let b = if n.kind == nkHiddenAddr: n.sons[0] else: n
-  gen(p, b.sons[0], a)
-  if skipTypes(b.sons[0].typ, abstractVarRange).kind == tyTuple:
-    r.res = makeJSString("Field" & $getFieldPosition(p, b.sons[1]))
+  let b = if n.kind == nkHiddenAddr: n[0] else: n
+  gen(p, b[0], a)
+  if skipTypes(b[0].typ, abstractVarRange).kind == tyTuple:
+    r.res = makeJSString("Field" & $getFieldPosition(p, b[1]))
   else:
-    if b.sons[1].kind != nkSym: internalError(p.config, b.sons[1].info, "genFieldAddr")
-    var f = b.sons[1].sym
-    if f.loc.r == nil: f.loc.r = mangleName(p.module, f)
-    r.res = makeJSString($f.loc.r)
+    if b[1].kind != nkSym: internalError(p.config, b[1].info, "genFieldAddr")
+    var f = b[1].sym
+    if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
+    r.res = makeJSString($f.loc.snippet)
   internalAssert p.config, a.typ != etyBaseIndex
   r.address = a.res
   r.kind = resExpr
 
 proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
-  r.typ = etyNone
-  gen(p, n.sons[0], r)
-  let otyp = skipTypes(n.sons[0].typ, abstractVarRange)
+  gen(p, n[0], r)
+  r.typ = mapType(n.typ)
+  let otyp = skipTypes(n[0].typ, abstractVarRange)
+
+  template mkTemp(i: int) =
+    if r.typ == etyBaseIndex:
+      if needsTemp(p, n[i]):
+        let tmp = p.getTemp
+        r.address = "($1 = $2, $1)[0]" % [tmp, r.res]
+        r.res = "$1[1]" % [tmp]
+        r.tmpLoc = tmp
+      else:
+        r.address = "$1[0]" % [r.res]
+        r.res = "$1[1]" % [r.res]
   if otyp.kind == tyTuple:
     r.res = ("$1.Field$2") %
-        [r.res, getFieldPosition(p, n.sons[1]).rope]
+        [r.res, getFieldPosition(p, n[1]).rope]
+    mkTemp(0)
   else:
-    if n.sons[1].kind != nkSym: internalError(p.config, n.sons[1].info, "genFieldAccess")
-    var f = n.sons[1].sym
-    if f.loc.r == nil: f.loc.r = mangleName(p.module, f)
-    r.res = "$1.$2" % [r.res, f.loc.r]
+    if n[1].kind != nkSym: internalError(p.config, n[1].info, "genFieldAccess")
+    var f = n[1].sym
+    if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
+    r.res = "$1.$2" % [r.res, f.loc.snippet]
+    mkTemp(1)
   r.kind = resExpr
 
 proc genAddr(p: PProc, n: PNode, r: var TCompRes)
 
-proc genCheckedFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
-  let m = if n.kind == nkHiddenAddr: n.sons[0] else: n
-  internalAssert p.config, m.kind == nkCheckedFieldExpr
-  genAddr(p, m, r) # XXX
-
-proc genCheckedFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
-  genFieldAccess(p, n.sons[0], r) # XXX
+proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) =
+  internalAssert p.config, n.kind == nkCheckedFieldExpr
+  # nkDotExpr to access the requested field
+  let accessExpr = n[0]
+  # nkCall to check if the discriminant is valid
+  var checkExpr = n[1]
+
+  let negCheck = checkExpr[0].sym.magic == mNot
+  if negCheck:
+    checkExpr = checkExpr[^1]
+
+  # Field symbol
+  var field = accessExpr[1].sym
+  internalAssert p.config, field.kind == skField
+  if field.loc.snippet == "": field.loc.snippet = mangleName(p.module, field)
+  # Discriminant symbol
+  let disc = checkExpr[2].sym
+  internalAssert p.config, disc.kind == skField
+  if disc.loc.snippet == "": disc.loc.snippet = mangleName(p.module, disc)
+
+  var setx: TCompRes = default(TCompRes)
+  gen(p, checkExpr[1], setx)
+
+  var obj: TCompRes = default(TCompRes)
+  gen(p, accessExpr[0], obj)
+  # Avoid evaluating the LHS twice (one to read the discriminant and one to read
+  # the field)
+  let tmp = p.getTemp()
+  lineF(p, "var $1 = $2;$n", tmp, obj.res)
+
+  useMagic(p, "raiseFieldError2")
+  useMagic(p, "makeNimstrLit")
+  useMagic(p, "reprDiscriminant") # no need to offset by firstOrd unlike for cgen
+  let msg = genFieldDefect(p.config, field.name.s, disc)
+  lineF(p, "if ($1[$2.$3]$4undefined) { raiseFieldError2(makeNimstrLit($5), reprDiscriminant($2.$3, $6)); }$n",
+    setx.res, tmp, disc.loc.snippet, if negCheck: "!==" else: "===",
+    makeJSString(msg), genTypeInfo(p, disc.typ))
+
+  if addrTyp != nil and mapType(p, addrTyp) == etyBaseIndex:
+    r.typ = etyBaseIndex
+    r.res = makeJSString($field.loc.snippet)
+    r.address = tmp
+  else:
+    r.typ = etyNone
+    r.res = "$1.$2" % [tmp, field.loc.snippet]
+  r.kind = resExpr
 
 proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
   var
-    a, b: TCompRes
-    first: BiggestInt
+    a, b: TCompRes = default(TCompRes)
+    first: Int128 = Zero
   r.typ = etyBaseIndex
-  let m = if n.kind == nkHiddenAddr: n.sons[0] else: n
-  gen(p, m.sons[0], a)
-  gen(p, m.sons[1], b)
-  internalAssert p.config, a.typ != etyBaseIndex and b.typ != etyBaseIndex
-  r.address = a.res
-  var typ = skipTypes(m.sons[0].typ, abstractPtrs)
-  if typ.kind == tyArray: first = firstOrd(p.config, typ.sons[0])
-  else: first = 0
+  let m = if n.kind == nkHiddenAddr: n[0] else: n
+  gen(p, m[0], a)
+  gen(p, m[1], b)
+  #internalAssert p.config, a.typ != etyBaseIndex and b.typ != etyBaseIndex
+  let (x, tmp) = maybeMakeTemp(p, m[0], a)
+  r.address = x
+  var typ = skipTypes(m[0].typ, abstractPtrs)
+  if typ.kind == tyArray:
+    first = firstOrd(p.config, typ.indexType)
   if optBoundsCheck in p.options:
     useMagic(p, "chckIndx")
-    r.res = "chckIndx($1, $2, $3.length+$2-1)-$2" % [b.res, rope(first), a.res]
+    if first == 0: # save a couple chars
+      r.res = "chckIndx($1, 0, ($2).length - 1)" % [b.res, tmp]
+    else:
+      r.res = "chckIndx($1, $2, ($3).length + ($2) - 1) - ($2)" % [
+        b.res, rope(first), tmp]
   elif first != 0:
-    r.res = "($1)-$2" % [b.res, rope(first)]
+    r.res = "($1) - ($2)" % [b.res, rope(first)]
   else:
     r.res = b.res
   r.kind = resExpr
 
 proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) =
-  var ty = skipTypes(n.sons[0].typ, abstractVarRange)
-  if ty.kind in {tyRef, tyPtr, tyLent}: ty = skipTypes(ty.lastSon, abstractVarRange)
+  var ty = skipTypes(n[0].typ, abstractVarRange+tyUserTypeClasses)
+  if ty.kind in {tyRef, tyPtr, tyLent, tyOwned}: ty = skipTypes(ty.elementType, abstractVarRange)
   case ty.kind
-  of tyArray, tyOpenArray, tySequence, tyString, tyCString, tyVarargs:
+  of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs:
     genArrayAddr(p, n, r)
   of tyTuple:
     genFieldAddr(p, n, r)
   else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
-  r.typ = etyNone
-  if r.res == nil: internalError(p.config, n.info, "genArrayAccess")
-  if ty.kind == tyCString:
+  r.typ = mapType(n.typ)
+  if r.res == "": internalError(p.config, n.info, "genArrayAccess")
+  if ty.kind == tyCstring:
     r.res = "$1.charCodeAt($2)" % [r.address, r.res]
+  elif r.typ == etyBaseIndex:
+    if needsTemp(p, n[0]):
+      let tmp = p.getTemp
+      r.address = "($1 = $2, $1)[0]" % [tmp, r.rdLoc]
+      r.res = "$1[1]" % [tmp]
+      r.tmpLoc = tmp
+    else:
+      let x = r.rdLoc
+      r.address = "$1[0]" % [x]
+      r.res = "$1[1]" % [x]
   else:
     r.res = "$1[$2]" % [r.address, r.res]
-  r.address = nil
   r.kind = resExpr
 
 template isIndirect(x: PSym): bool =
   let v = x
   ({sfAddrTaken, sfGlobal} * v.flags != {} and
     #(mapType(v.typ) != etyObject) and
-    {sfImportc, sfVolatile, sfExportc} * v.flags == {} and
+    {sfImportc, sfExportc} * v.flags == {} and
     v.kind notin {skProc, skFunc, skConverter, skMethod, skIterator,
                   skConst, skTemp, skLet})
 
+proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) =
+  let s = n.sym
+  if s.loc.snippet == "": internalError(p.config, n.info, "genAddr: 3")
+  case s.kind
+  of skParam:
+    r.res = s.loc.snippet
+    r.address = ""
+    r.typ = etyNone
+  of skVar, skLet, skResult:
+    r.kind = resExpr
+    let jsType = mapType(p):
+      if typ.isNil:
+        n.typ
+      else:
+        typ
+    if jsType == etyObject:
+      # make addr() a no-op:
+      r.typ = etyNone
+      if isIndirect(s):
+        r.res = s.loc.snippet & "[0]"
+      else:
+        r.res = s.loc.snippet
+      r.address = ""
+    elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex:
+      # for ease of code generation, we do not distinguish between
+      # sfAddrTaken and sfGlobal.
+      r.typ = etyBaseIndex
+      r.address = s.loc.snippet
+      r.res = rope("0")
+    else:
+      # 'var openArray' for instance produces an 'addr' but this is harmless:
+      gen(p, n, r)
+      #internalError(p.config, n.info, "genAddr: 4 " & renderTree(n))
+  else: internalError(p.config, n.info, $("genAddr: 2", s.kind))
+
 proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
-  case n.sons[0].kind
-  of nkSym:
-    let s = n.sons[0].sym
-    if s.loc.r == nil: internalError(p.config, n.info, "genAddr: 3")
-    case s.kind
-    of skVar, skLet, skResult:
-      r.kind = resExpr
-      let jsType = mapType(p, n.typ)
-      if jsType == etyObject:
-        # make addr() a no-op:
-        r.typ = etyNone
-        if isIndirect(s):
-          r.res = s.loc.r & "[0]"
-        else:
-          r.res = s.loc.r
-        r.address = nil
-      elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex:
-        # for ease of code generation, we do not distinguish between
-        # sfAddrTaken and sfGlobal.
-        r.typ = etyBaseIndex
-        r.address = s.loc.r
-        r.res = rope("0")
+  if n.kind == nkSym:
+    genSymAddr(p, n, nil, r)
+  else:
+    case n[0].kind
+    of nkSym:
+      genSymAddr(p, n[0], n.typ, r)
+    of nkCheckedFieldExpr:
+      genCheckedFieldOp(p, n[0], n.typ, r)
+    of nkDotExpr:
+      if mapType(p, n.typ) == etyBaseIndex:
+        genFieldAddr(p, n[0], r)
+      else:
+        genFieldAccess(p, n[0], r)
+    of nkBracketExpr:
+      var ty = skipTypes(n[0].typ, abstractVarRange)
+      if ty.kind in MappedToObject:
+        gen(p, n[0], r)
       else:
+        let kindOfIndexedExpr = skipTypes(n[0][0].typ, abstractVarRange+tyUserTypeClasses).kind
+        case kindOfIndexedExpr
+        of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs:
+          genArrayAddr(p, n[0], r)
+        of tyTuple:
+          genFieldAddr(p, n[0], r)
+        of tyGenericBody:
+          genAddr(p, n[^1], r)
+        else: internalError(p.config, n[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')')
+    of nkObjDownConv:
+      gen(p, n[0], r)
+    of nkHiddenDeref:
+      gen(p, n[0], r)
+    of nkDerefExpr:
+      var x = n[0]
+      if n.kind == nkHiddenAddr:
+        x = n[0][0]
+        if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
+          x.typ = n.typ
+      gen(p, x, r)
+    of nkHiddenAddr:
+      gen(p, n[0], r)
+    of nkConv:
+      genAddr(p, n[0], r)
+    of nkStmtListExpr:
+      if n.len == 1: gen(p, n[0], r)
+      else: internalError(p.config, n[0].info, "genAddr for complex nkStmtListExpr")
+    of nkCallKinds:
+      if n[0].typ.kind == tyOpenArray:
         # 'var openArray' for instance produces an 'addr' but this is harmless:
-        gen(p, n.sons[0], r)
-        #internalError(p.config, n.info, "genAddr: 4 " & renderTree(n))
-    else: internalError(p.config, n.info, "genAddr: 2")
-  of nkCheckedFieldExpr:
-    genCheckedFieldAddr(p, n, r)
-  of nkDotExpr:
-    if mapType(p, n.typ) == etyBaseIndex:
-      genFieldAddr(p, n.sons[0], r)
-    else:
-      genFieldAccess(p, n.sons[0], r)
-  of nkBracketExpr:
-    var ty = skipTypes(n.sons[0].typ, abstractVarRange)
-    if ty.kind in MappedToObject:
-      gen(p, n.sons[0], r)
-    else:
-      let kindOfIndexedExpr = skipTypes(n.sons[0].sons[0].typ, abstractVarRange).kind
-      case kindOfIndexedExpr
-      of tyArray, tyOpenArray, tySequence, tyString, tyCString, tyVarargs:
-        genArrayAddr(p, n.sons[0], r)
-      of tyTuple:
-        genFieldAddr(p, n.sons[0], r)
-      else: internalError(p.config, n.sons[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')')
-  of nkObjDownConv:
-    gen(p, n.sons[0], r)
-  of nkHiddenDeref:
-    gen(p, n.sons[0].sons[0], r)
-  else: internalError(p.config, n.sons[0].info, "genAddr: " & $n.sons[0].kind)
-
-proc thisParam(p: PProc; typ: PType): PType =
-  discard
+        # namely toOpenArray(a, 1, 3)
+        gen(p, n[0], r)
+      else:
+        internalError(p.config, n[0].info, "genAddr: " & $n[0].kind)
+    else:
+      internalError(p.config, n[0].info, "genAddr: " & $n[0].kind)
 
 proc attachProc(p: PProc; content: Rope; s: PSym) =
-  let otyp = thisParam(p, s.typ)
-  if otyp != nil:
-    for i, cls in p.g.classes:
-      if sameType(cls[0], otyp):
-        add(p.g.classes[i][1], content)
-        return
-    p.g.classes.add((otyp, content))
-  else:
-    add(p.g.code, content)
+  p.g.code.add(content)
 
 proc attachProc(p: PProc; s: PSym) =
   let newp = genProc(p, s)
@@ -1078,44 +1620,75 @@ proc attachProc(p: PProc; s: PSym) =
 
 proc genProcForSymIfNeeded(p: PProc, s: PSym) =
   if not p.g.generatedSyms.containsOrIncl(s.id):
-    let newp = genProc(p, s)
-    var owner = p
-    while owner != nil and owner.prc != s.owner:
-      owner = owner.up
-    if owner != nil: add(owner.locals, newp)
-    else: attachProc(p, newp, s)
+    if jsNoLambdaLifting in p.config.legacyFeatures:
+      let newp = genProc(p, s)
+      var owner = p
+      while owner != nil and owner.prc != s.owner:
+        owner = owner.up
+      if owner != nil: owner.locals.add(newp)
+      else: attachProc(p, newp, s)
+    else:
+      attachProc(p, 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.snippet, genTypeInfo(p, s.typ)]
+        owner.locals.add(owner.indentLine(copy))
+      return
+    owner = owner.up
+
+proc genVarInit(p: PProc, v: PSym, n: PNode)
 
 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:
+    if s.loc.snippet == "":
       internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
+    if sfCompileTime in s.flags:
+      genVarInit(p, s, if s.astdef != nil: s.astdef else: newNodeI(nkEmpty, s.info))
+    if jsNoLambdaLifting in p.config.legacyFeatures and s.kind == skParam:
+      genCopyForParamIfNeeded(p, n)
     let k = mapType(p, s.typ)
     if k == etyBaseIndex:
       r.typ = etyBaseIndex
       if {sfAddrTaken, sfGlobal} * s.flags != {}:
-        r.address = "$1[0]" % [s.loc.r]
-        r.res = "$1[1]" % [s.loc.r]
+        if isIndirect(s):
+          r.address = "$1[0][0]" % [s.loc.snippet]
+          r.res = "$1[0][1]" % [s.loc.snippet]
+        else:
+          r.address = "$1[0]" % [s.loc.snippet]
+          r.res = "$1[1]" % [s.loc.snippet]
       else:
-        r.address = s.loc.r
-        r.res = s.loc.r & "_Idx"
+        r.address = s.loc.snippet
+        r.res = s.loc.snippet & "_Idx"
     elif isIndirect(s):
-      r.res = "$1[0]" % [s.loc.r]
+      r.res = "$1[0]" % [s.loc.snippet]
     else:
-      r.res = s.loc.r
+      r.res = s.loc.snippet
   of skConst:
     genConstant(p, s)
-    if s.loc.r == nil:
+    if s.loc.snippet == "":
       internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
-    r.res = s.loc.r
-  of skProc, skFunc, skConverter, skMethod:
+    r.res = s.loc.snippet
+  of skProc, skFunc, skConverter, skMethod, skIterator:
+    if sfCompileTime in s.flags:
+      localError(p.config, n.info, "request to generate code for .compileTime proc: " &
+          s.name.s)
     discard mangleName(p.module, s)
-    r.res = s.loc.r
-    if lfNoDecl in s.loc.flags or s.magic != mNone or
+    r.res = s.loc.snippet
+    if lfNoDecl in s.loc.flags or s.magic notin generatedMagics or
        {sfImportc, sfInfixCall} * s.flags != {}:
       discard
-    elif s.kind == skMethod and s.getBody.kind == nkEmpty:
+    elif s.kind == skMethod and getBody(p.module.graph, s).kind == nkEmpty:
       # we cannot produce code for the dispatcher yet:
       discard
     elif sfForward in s.flags:
@@ -1123,84 +1696,92 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
     else:
       genProcForSymIfNeeded(p, s)
   else:
-    if s.loc.r == nil:
+    if s.loc.snippet == "":
       internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
-    r.res = s.loc.r
+    if mapType(p, s.typ) == etyBaseIndex:
+      r.address = s.loc.snippet
+      r.res = s.loc.snippet & "_Idx"
+    else:
+      r.res = s.loc.snippet
   r.kind = resVal
 
 proc genDeref(p: PProc, n: PNode, r: var TCompRes) =
-  let it = n.sons[0]
+  let it = n[0]
   let t = mapType(p, it.typ)
-  if t == etyObject:
+  if t == etyObject or it.typ.kind == tyLent:
     gen(p, it, r)
   else:
-    var a: TCompRes
+    var a: TCompRes = default(TCompRes)
     gen(p, it, a)
-    r.kind = resExpr
-    if a.typ == etyBaseIndex:
-      r.res = "$1[$2]" % [a.address, a.res]
-    elif it.kind == nkCall:
+    r.kind = a.kind
+    r.typ = mapType(p, n.typ)
+    if r.typ == etyBaseIndex:
       let tmp = p.getTemp
-      r.res = "($1 = $2, $1[0])[$1[1]]" % [tmp, a.res]
-    elif t == etyBaseIndex:
-      r.res = "$1[0]" % [a.res]
+      r.address = "($1 = $2, $1)[0]" % [tmp, a.rdLoc]
+      r.res = "$1[1]" % [tmp]
+      r.tmpLoc = tmp
+    elif a.typ == etyBaseIndex:
+      if a.tmpLoc != "":
+        r.tmpLoc = a.tmpLoc
+      r.res = a.rdLoc
     else:
       internalError(p.config, n.info, "genDeref")
 
 proc genArgNoParam(p: PProc, n: PNode, r: var TCompRes) =
-  var a: TCompRes
+  var a: TCompRes = default(TCompRes)
   gen(p, n, a)
   if a.typ == etyBaseIndex:
-    add(r.res, a.address)
-    add(r.res, ", ")
-    add(r.res, a.res)
+    r.res.add(a.address)
+    r.res.add(", ")
+    r.res.add(a.res)
   else:
-    add(r.res, a.res)
+    r.res.add(a.res)
 
 proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes; emitted: ptr int = nil) =
-  var a: TCompRes
+  var a: TCompRes = default(TCompRes)
   gen(p, n, a)
   if skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs} and
       a.typ == etyBaseIndex:
-    add(r.res, "$1[$2]" % [a.address, a.res])
+    r.res.add("$1[$2]" % [a.address, a.res])
   elif a.typ == etyBaseIndex:
-    add(r.res, a.address)
-    add(r.res, ", ")
-    add(r.res, a.res)
+    r.res.add(a.address)
+    r.res.add(", ")
+    r.res.add(a.res)
     if emitted != nil: inc emitted[]
-  elif n.typ.kind in {tyVar, tyLent} and n.kind in nkCallKinds and mapType(param.typ) == etyBaseIndex:
+  elif n.typ.kind in {tyVar, tyPtr, tyRef, tyLent, tyOwned} and
+      n.kind in nkCallKinds and mapType(param.typ) == etyBaseIndex:
     # this fixes bug #5608:
     let tmp = getTemp(p)
-    add(r.res, "($1 = $2, $1[0]), $1[1]" % [tmp, a.rdLoc])
+    r.res.add("($1 = $2, $1[0]), $1[1]" % [tmp, a.rdLoc])
     if emitted != nil: inc emitted[]
   else:
-    add(r.res, a.res)
+    r.res.add(a.res)
 
 proc genArgs(p: PProc, n: PNode, r: var TCompRes; start=1) =
-  add(r.res, "(")
+  r.res.add("(")
   var hasArgs = false
 
-  var typ = skipTypes(n.sons[0].typ, abstractInst)
+  var typ = skipTypes(n[0].typ, abstractInst)
   assert(typ.kind == tyProc)
-  assert(sonsLen(typ) == sonsLen(typ.n))
+  assert(typ.len == typ.n.len)
   var emitted = start-1
 
-  for i in countup(start, sonsLen(n) - 1):
-    let it = n.sons[i]
+  for i in start..<n.len:
+    let it = n[i]
     var paramType: PNode = nil
-    if i < sonsLen(typ):
-      assert(typ.n.sons[i].kind == nkSym)
-      paramType = typ.n.sons[i]
+    if i < typ.len:
+      assert(typ.n[i].kind == nkSym)
+      paramType = typ.n[i]
       if paramType.typ.isCompileTimeOnly: continue
 
-    if hasArgs: add(r.res, ", ")
+    if hasArgs: r.res.add(", ")
     if paramType.isNil:
       genArgNoParam(p, it, r)
     else:
       genArg(p, it, paramType.sym, r, addr emitted)
     inc emitted
     hasArgs = true
-  add(r.res, ")")
+  r.res.add(")")
   when false:
     # XXX look into this:
     let jsp = countJsParams(typ)
@@ -1216,9 +1797,9 @@ proc genOtherArg(p: PProc; n: PNode; i: int; typ: PType;
         " but got only: " & $(n.len-1))
   let it = n[i]
   var paramType: PNode = nil
-  if i < sonsLen(typ):
-    assert(typ.n.sons[i].kind == nkSym)
-    paramType = typ.n.sons[i]
+  if i < typ.len:
+    assert(typ.n[i].kind == nkSym)
+    paramType = typ.n[i]
     if paramType.typ.isCompileTimeOnly: return
   if paramType.isNil:
     genArgNoParam(p, it, r)
@@ -1235,8 +1816,8 @@ proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType;
     case pat[i]
     of '@':
       var generated = 0
-      for k in j ..< n.len:
-        if generated > 0: add(r.res, ", ")
+      for k in j..<n.len:
+        if generated > 0: r.res.add(", ")
         genOtherArg(p, n, k, typ, generated, r)
       inc i
     of '#':
@@ -1246,11 +1827,11 @@ proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType;
       inc i
     of '\31':
       # unit separator
-      add(r.res, "#")
+      r.res.add("#")
       inc i
     of '\29':
       # group separator
-      add(r.res, "@")
+      r.res.add("@")
       inc i
     else:
       let start = i
@@ -1258,53 +1839,58 @@ proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType;
         if pat[i] notin {'@', '#', '\31', '\29'}: inc(i)
         else: break
       if i - 1 >= start:
-        add(r.res, substr(pat, start, i - 1))
+        r.res.add(substr(pat, start, i - 1))
 
 proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
   # don't call '$' here for efficiency:
   let f = n[0].sym
-  if f.loc.r == nil: f.loc.r = mangleName(p.module, f)
+  if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
   if sfInfixCall in f.flags:
-    let pat = n.sons[0].sym.loc.r.data
-    internalAssert p.config, pat != nil
+    let pat = $n[0].sym.loc.snippet
+    internalAssert p.config, pat.len > 0
     if pat.contains({'#', '(', '@'}):
-      var typ = skipTypes(n.sons[0].typ, abstractInst)
+      var typ = skipTypes(n[0].typ, abstractInst)
       assert(typ.kind == tyProc)
       genPatternCall(p, n, pat, typ, r)
       return
   if n.len != 1:
-    gen(p, n.sons[1], r)
+    gen(p, n[1], r)
     if r.typ == etyBaseIndex:
-      if r.address == nil:
+      if r.address == "":
         globalError(p.config, n.info, "cannot invoke with infix syntax")
       r.res = "$1[$2]" % [r.address, r.res]
-      r.address = nil
+      r.address = ""
       r.typ = etyNone
-    add(r.res, ".")
-  var op: TCompRes
-  gen(p, n.sons[0], op)
-  add(r.res, op.res)
+    r.res.add(".")
+  var op: TCompRes = default(TCompRes)
+  gen(p, n[0], op)
+  r.res.add(op.res)
   genArgs(p, n, r, 2)
 
 proc genCall(p: PProc, n: PNode, r: var TCompRes) =
-  if n.sons[0].kind == nkSym and thisParam(p, n.sons[0].typ) != nil:
-    genInfixCall(p, n, r)
-    return
-  gen(p, n.sons[0], r)
+  gen(p, n[0], r)
   genArgs(p, n, r)
+  if n.typ != nil:
+    let t = mapType(n.typ)
+    if t == etyBaseIndex:
+      let tmp = p.getTemp
+      r.address = "($1 = $2, $1)[0]" % [tmp, r.rdLoc]
+      r.res = "$1[1]" % [tmp]
+      r.tmpLoc = tmp
+      r.typ = t
 
 proc genEcho(p: PProc, n: PNode, r: var TCompRes) =
   let n = n[1].skipConv
   internalAssert p.config, n.kind == nkBracket
   useMagic(p, "toJSStr") # Used in rawEcho
   useMagic(p, "rawEcho")
-  add(r.res, "rawEcho(")
-  for i in countup(0, sonsLen(n) - 1):
-    let it = n.sons[i]
+  r.res.add("rawEcho(")
+  for i in 0..<n.len:
+    let it = n[i]
     if it.typ.isCompileTimeOnly: continue
-    if i > 0: add(r.res, ", ")
+    if i > 0: r.res.add(", ")
     genArgNoParam(p, it, r)
-  add(r.res, ")")
+  r.res.add(")")
   r.kind = resExpr
 
 proc putToSeq(s: string, indirect: bool): Rope =
@@ -1315,13 +1901,15 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope
 proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output: var Rope) =
   case rec.kind
   of nkRecList:
-    for i in countup(0, sonsLen(rec) - 1):
-      createRecordVarAux(p, rec.sons[i], excludedFieldIDs, output)
+    for i in 0..<rec.len:
+      createRecordVarAux(p, rec[i], excludedFieldIDs, output)
   of nkRecCase:
-    createRecordVarAux(p, rec.sons[0], excludedFieldIDs, output)
-    for i in countup(1, sonsLen(rec) - 1):
-      createRecordVarAux(p, lastSon(rec.sons[i]), excludedFieldIDs, output)
+    createRecordVarAux(p, rec[0], excludedFieldIDs, output)
+    for i in 1..<rec.len:
+      createRecordVarAux(p, lastSon(rec[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)])
@@ -1332,43 +1920,71 @@ proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: v
   var t = typ
   if objHasTypeField(t):
     if output.len > 0: output.add(", ")
-    addf(output, "m_type: $1", [genTypeInfo(p, t)])
+    output.addf("m_type: $1", [genTypeInfo(p, t)])
   while t != nil:
     t = t.skipTypes(skipPtrs)
     createRecordVarAux(p, t.n, excludedFieldIDs, output)
-    t = t.sons[0]
+    t = t.baseClass
 
-proc arrayTypeForElemType(typ: PType): string =
-  # XXX This should also support tyEnum and tyBool
+proc arrayTypeForElemType(conf: ConfigRef; typ: PType): string =
+  let typ = typ.skipTypes(abstractRange)
   case typ.kind
   of tyInt, tyInt32: "Int32Array"
   of tyInt16: "Int16Array"
   of tyInt8: "Int8Array"
-  of tyUint, tyUint32: "Uint32Array"
-  of tyUint16: "Uint16Array"
-  of tyUint8: "Uint8Array"
+  of tyInt64:
+    if optJsBigInt64 in conf.globalOptions:
+      "BigInt64Array"
+    else:
+      ""
+  of tyUInt, tyUInt32: "Uint32Array"
+  of tyUInt16: "Uint16Array"
+  of tyUInt8, tyChar, tyBool: "Uint8Array"
+  of tyUInt64:
+    if optJsBigInt64 in conf.globalOptions:
+      "BigUint64Array"
+    else:
+      ""
   of tyFloat32: "Float32Array"
   of tyFloat64, tyFloat: "Float64Array"
-  else: nil
+  of tyEnum:
+    case typ.size
+    of 1: "Uint8Array"
+    of 2: "Uint16Array"
+    of 4: "Uint32Array"
+    else: ""
+  else: ""
 
 proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
   var t = skipTypes(typ, abstractInst)
   case t.kind
-  of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar:
+  of tyInt8..tyInt32, tyUInt8..tyUInt32, tyEnum, tyChar:
     result = putToSeq("0", indirect)
+  of tyInt, tyUInt:
+    if $t.sym.loc.snippet == "bigint":
+      result = putToSeq("0n", indirect)
+    else:
+      result = putToSeq("0", indirect)
+  of tyInt64, tyUInt64:
+    if optJsBigInt64 in p.config.globalOptions:
+      result = putToSeq("0n", indirect)
+    else:
+      result = putToSeq("0", indirect)
   of tyFloat..tyFloat128:
     result = putToSeq("0.0", indirect)
-  of tyRange, tyGenericInst, tyAlias, tySink:
-    result = createVar(p, lastSon(typ), indirect)
+  of tyRange, tyGenericInst, tyAlias, tySink, tyOwned, tyLent:
+    result = createVar(p, skipModifier(typ), indirect)
   of tySet:
     result = putToSeq("{}", indirect)
   of tyBool:
     result = putToSeq("false", indirect)
+  of tyNil:
+    result = putToSeq("null", indirect)
   of tyArray:
-    let length = int(lengthOrd(p.config, t))
+    let length = toInt(lengthOrd(p.config, t))
     let e = elemType(t)
-    let jsTyp = arrayTypeForElemType(e)
-    if not jsTyp.isNil:
+    let jsTyp = arrayTypeForElemType(p.config, e)
+    if jsTyp.len > 0:
       result = "new $1($2)" % [rope(jsTyp), rope(length)]
     elif length > 32:
       useMagic(p, "arrayConstr")
@@ -1380,72 +1996,84 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
       result = rope("[")
       var i = 0
       while i < length:
-        if i > 0: add(result, ", ")
-        add(result, createVar(p, e, false))
+        if i > 0: result.add(", ")
+        result.add(createVar(p, e, false))
         inc(i)
-      add(result, "]")
+      result.add("]")
     if indirect: result = "[$1]" % [result]
   of tyTuple:
     result = rope("{")
-    for i in 0..<t.sonsLen:
-      if i > 0: add(result, ", ")
-      addf(result, "Field$1: $2", [i.rope,
-            createVar(p, t.sons[i], false)])
-    add(result, "}")
+    for i in 0..<t.len:
+      if i > 0: result.add(", ")
+      result.addf("Field$1: $2", [i.rope,
+            createVar(p, t[i], false)])
+    result.add("}")
     if indirect: result = "[$1]" % [result]
   of tyObject:
-    var initList: Rope
+    var initList: Rope = ""
     createObjInitList(p, t, initIntSet(), initList)
-    result = ("{$1}") % [initList]
+    result = ("({$1})") % [initList]
     if indirect: result = "[$1]" % [result]
-  of tyVar, tyPtr, tyLent, tyRef:
+  of tyVar, tyPtr, tyRef, tyPointer:
     if mapType(p, t) == etyBaseIndex:
       result = putToSeq("[null, 0]", indirect)
     else:
       result = putToSeq("null", indirect)
-  of tySequence, tyOpt, tyString, tyCString, tyPointer, tyProc:
+  of tySequence, tyString:
+    result = putToSeq("[]", indirect)
+  of tyCstring, tyProc, tyOpenArray:
     result = putToSeq("null", indirect)
   of tyStatic:
     if t.n != nil:
-      result = createVar(p, lastSon t, indirect)
+      result = createVar(p, skipModifier t, indirect)
     else:
       internalError(p.config, "createVar: " & $t.kind)
-      result = nil
+      result = ""
   else:
     internalError(p.config, "createVar: " & $t.kind)
-    result = nil
+    result = ""
 
-template returnType: untyped =
-  ~""
+template returnType: untyped = ""
 
 proc genVarInit(p: PProc, v: PSym, n: PNode) =
   var
-    a: TCompRes
+    a: TCompRes = default(TCompRes)
     s: Rope
     varCode: string
     varName = mangleName(p.module, v)
-    useReloadingGuard = sfGlobal in v.flags and optHotCodeReloading in p.config.options
+    useReloadingGuard = sfGlobal in v.flags and p.config.hcrOn
+    useGlobalPragmas = sfGlobal in v.flags and ({sfPure, sfThread} * v.flags != {})
 
   if v.constraint.isNil:
     if useReloadingGuard:
       lineF(p, "var $1;$n", varName)
       lineF(p, "if ($1 === undefined) {$n", varName)
       varCode = $varName
+      inc p.extraIndent
+    elif useGlobalPragmas:
+      lineF(p, "if (globalThis.$1 === undefined) {$n", varName)
+      varCode = "globalThis." & $varName
+      inc p.extraIndent
     else:
       varCode = "var $2"
   else:
+    # Is this really a thought through feature?  this basically unused
+    # feature makes it impossible for almost all format strings in
+    # this function to be checked at compile time.
     varCode = v.constraint.strVal
 
   if n.kind == nkEmpty:
-    lineF(p, varCode & " = $3;$n",
-               [returnType, varName, createVar(p, v.typ, isIndirect(v))])
-    if v.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and mapType(p, v.typ) == etyBaseIndex:
+    if not isIndirect(v) and
+      v.typ.kind in {tyVar, tyPtr, tyLent, tyRef, tyOwned} and mapType(p, v.typ) == etyBaseIndex:
+      lineF(p, "var $1 = null;$n", [varName])
       lineF(p, "var $1_Idx = 0;$n", [varName])
+    else:
+      line(p, runtimeFormat(varCode & " = $3;$n", [returnType, varName, createVar(p, v.typ, isIndirect(v))]))
   else:
     gen(p, n, a)
     case mapType(p, v.typ)
     of etyObject, etySeq:
-      if needsNoCopy(p, n):
+      if v.typ.kind in {tyOpenArray, tyVarargs} or needsNoCopy(p, n):
         s = a.res
       else:
         useMagic(p, "nimCopy")
@@ -1454,141 +2082,151 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
       let targetBaseIndex = {sfAddrTaken, sfGlobal} * v.flags == {}
       if a.typ == etyBaseIndex:
         if targetBaseIndex:
-          lineF(p, varCode & " = $3, $2_Idx = $4;$n",
-                   [returnType, v.loc.r, a.address, a.res])
+          line(p, runtimeFormat(varCode & " = $3, $2_Idx = $4;$n",
+                   [returnType, v.loc.snippet, a.address, a.res]))
         else:
-          lineF(p, varCode & " = [$3, $4];$n",
-                   [returnType, v.loc.r, a.address, a.res])
+          if isIndirect(v):
+            line(p, runtimeFormat(varCode & " = [[$3, $4]];$n",
+                     [returnType, v.loc.snippet, a.address, a.res]))
+          else:
+            line(p, runtimeFormat(varCode & " = [$3, $4];$n",
+                     [returnType, v.loc.snippet, a.address, a.res]))
       else:
         if targetBaseIndex:
           let tmp = p.getTemp
           lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n",
-                   [tmp, a.res, v.loc.r])
+                   [tmp, a.res, v.loc.snippet])
         else:
-          lineF(p, varCode & " = $3;$n", [returnType, v.loc.r, a.res])
+          line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.snippet, a.res]))
       return
     else:
       s = a.res
     if isIndirect(v):
-      lineF(p, varCode & " = [$3];$n", [returnType, v.loc.r, s])
+      line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.snippet, s]))
     else:
-      lineF(p, varCode & " = $3;$n", [returnType, v.loc.r, s])
+      line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.snippet, s]))
 
-  if useReloadingGuard:
+  if useReloadingGuard or useGlobalPragmas:
+    dec p.extraIndent
     lineF(p, "}$n")
 
+proc genClosureVar(p: PProc, n: PNode) =
+  # assert n[2].kind != nkEmpty
+  # TODO: fixme transform `var env.x` into `var env.x = default()` after
+  # the order of transf and lambdalifting is fixed
+  if n[2].kind != nkEmpty:
+    genAsgnAux(p, n[0], n[2], false)
+  else:
+    var a: TCompRes = default(TCompRes)
+    gen(p, n[0], a)
+    line(p, runtimeFormat("$1 = $2;$n", [rdLoc(a), createVar(p, n[0].typ, false)]))
+
 proc genVarStmt(p: PProc, n: PNode) =
-  for i in countup(0, sonsLen(n) - 1):
-    var a = n.sons[i]
+  for i in 0..<n.len:
+    var a = n[i]
     if a.kind != nkCommentStmt:
       if a.kind == nkVarTuple:
-        let unpacked = lowerTupleUnpacking(p.module.graph, a, p.prc)
+        let unpacked = lowerTupleUnpacking(p.module.graph, a, p.module.idgen, p.prc)
         genStmt(p, unpacked)
       else:
         assert(a.kind == nkIdentDefs)
-        assert(a.sons[0].kind == nkSym)
-        var v = a.sons[0].sym
-        if lfNoDecl notin v.loc.flags and sfImportc notin v.flags:
-          genLineDir(p, a)
-          genVarInit(p, v, a.sons[2])
+        if a[0].kind == nkSym:
+          var v = a[0].sym
+          if lfNoDecl notin v.loc.flags and sfImportc notin v.flags:
+            genLineDir(p, a)
+            if sfCompileTime notin v.flags:
+              genVarInit(p, v, a[2])
+            else:
+              # lazy emit, done when it's actually used.
+              if v.ast == nil: v.ast = a[2]
+        else: # closure
+          genClosureVar(p, a)
 
 proc genConstant(p: PProc, c: PSym) =
   if lfNoDecl notin c.loc.flags and not p.g.generatedSyms.containsOrIncl(c.id):
-    let oldBody = p.body
-    p.body = nil
-    #genLineDir(p, c.ast)
-    genVarInit(p, c, c.ast)
-    add(p.g.constants, p.body)
+    let oldBody = move p.body
+    #genLineDir(p, c.astdef)
+    genVarInit(p, c, c.astdef)
+    p.g.constants.add(p.body)
     p.body = oldBody
 
 proc genNew(p: PProc, n: PNode) =
-  var a: TCompRes
-  gen(p, n.sons[1], a)
-  var t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
-  lineF(p, "$1 = $2;$n", [a.res, createVar(p, t, false)])
+  var a: TCompRes = default(TCompRes)
+  gen(p, n[1], a)
+  var t = skipTypes(n[1].typ, abstractVar)[0]
+  if mapType(t) == etyObject:
+    lineF(p, "$1 = $2;$n", [a.rdLoc, createVar(p, t, false)])
+  elif a.typ == etyBaseIndex:
+    lineF(p, "$1 = [$3]; $2 = 0;$n", [a.address, a.res, createVar(p, t, false)])
+  else:
+    lineF(p, "$1 = [[$2], 0];$n", [a.rdLoc, createVar(p, t, false)])
 
 proc genNewSeq(p: PProc, n: PNode) =
-  var x, y: TCompRes
-  gen(p, n.sons[1], x)
-  gen(p, n.sons[2], y)
-  let t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
-  lineF(p, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}", [
+  var x, y: TCompRes = default(TCompRes)
+  gen(p, n[1], x)
+  gen(p, n[2], y)
+  let t = skipTypes(n[1].typ, abstractVar)[0]
+  lineF(p, "$1 = new Array($2); for (var i = 0 ; i < $2 ; ++i) { $1[i] = $3; }", [
     x.rdLoc, y.rdLoc, createVar(p, t, false)])
 
 proc genOrd(p: PProc, n: PNode, r: var TCompRes) =
-  case skipTypes(n.sons[1].typ, abstractVar).kind
-  of tyEnum, tyInt..tyUInt64, tyChar: gen(p, n.sons[1], r)
-  of tyBool: unaryExpr(p, n, r, "", "($1 ? 1:0)")
+  case skipTypes(n[1].typ, abstractVar + abstractRange).kind
+  of tyEnum, tyInt..tyInt32, tyUInt..tyUInt32, tyChar: gen(p, n[1], r)
+  of tyInt64, tyUInt64:
+    if optJsBigInt64 in p.config.globalOptions:
+      unaryExpr(p, n, r, "", "Number($1)")
+    else: gen(p, n[1], r)
+  of tyBool: unaryExpr(p, n, r, "", "($1 ? 1 : 0)")
   else: internalError(p.config, n.info, "genOrd")
 
 proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) =
-  var a: TCompRes
+  var a: TCompRes = default(TCompRes)
 
-  gen(p, n.sons[1], a)
+  gen(p, n[1], a)
   r.kind = resExpr
-  if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyChar:
+  if skipTypes(n[1].typ, abstractVarRange).kind == tyChar:
     r.res.add("[$1].concat(" % [a.res])
   else:
-    r.res.add("($1.slice(0,-1)).concat(" % [a.res])
+    r.res.add("($1).concat(" % [a.res])
 
-  for i in countup(2, sonsLen(n) - 2):
-    gen(p, n.sons[i], a)
-    if skipTypes(n.sons[i].typ, abstractVarRange).kind == tyChar:
+  for i in 2..<n.len - 1:
+    gen(p, n[i], a)
+    if skipTypes(n[i].typ, abstractVarRange).kind == tyChar:
       r.res.add("[$1]," % [a.res])
     else:
-      r.res.add("$1.slice(0,-1)," % [a.res])
+      r.res.add("$1," % [a.res])
 
-  gen(p, n.sons[sonsLen(n) - 1], a)
-  if skipTypes(n.sons[sonsLen(n) - 1].typ, abstractVarRange).kind == tyChar:
-    r.res.add("[$1, 0])" % [a.res])
+  gen(p, n[^1], a)
+  if skipTypes(n[^1].typ, abstractVarRange).kind == tyChar:
+    r.res.add("[$1])" % [a.res])
   else:
     r.res.add("$1)" % [a.res])
 
-proc genToArray(p: PProc; n: PNode; r: var TCompRes) =
-  # we map mArray to PHP's array constructor, a mild hack:
-  var a, b: TCompRes
-  r.kind = resExpr
-  r.res = rope("array(")
-  let x = skipConv(n[1])
-  if x.kind == nkBracket:
-    for i in countup(0, x.len - 1):
-      let it = x[i]
-      if it.kind in {nkPar, nkTupleConstr} and it.len == 2:
-        if i > 0: r.res.add(", ")
-        gen(p, it[0], a)
-        gen(p, it[1], b)
-        r.res.add("$# => $#" % [a.rdLoc, b.rdLoc])
-      else:
-        localError(p.config, it.info, "'toArray' needs tuple constructors")
-  else:
-    localError(p.config, x.info, "'toArray' needs an array literal")
-  r.res.add(")")
-
-proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = nil) =
+proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = "") =
   useMagic(p, magic)
-  add(r.res, magic & "(")
-  var a: TCompRes
+  r.res.add(magic & "(")
+  var a: TCompRes = default(TCompRes)
 
-  gen(p, n.sons[1], a)
+  gen(p, n[1], a)
   if magic == "reprAny":
     # the pointer argument in reprAny is expandend to
     # (pointedto, pointer), so we need to fill it
-    if a.address.isNil:
-      add(r.res, a.res)
-      add(r.res, ", null")
+    if a.address.len == 0:
+      r.res.add(a.res)
+      r.res.add(", null")
     else:
-      add(r.res, "$1, $2" % [a.address, a.res])
+      r.res.add("$1, $2" % [a.address, a.res])
   else:
-    add(r.res, a.res)
+    r.res.add(a.res)
 
-  if not typ.isNil:
-    add(r.res, ", ")
-    add(r.res, typ)
-  add(r.res, ")")
+  if typ != "":
+    r.res.add(", ")
+    r.res.add(typ)
+  r.res.add(")")
 
 proc genRepr(p: PProc, n: PNode, r: var TCompRes) =
-  let t = skipTypes(n.sons[1].typ, abstractVarRange)
-  case t.kind:
+  let t = skipTypes(n[1].typ, abstractVarRange)
+  case t.kind
   of tyInt..tyInt64, tyUInt..tyUInt64:
     genReprAux(p, n, r, "reprInt")
   of tyChar:
@@ -1611,11 +2249,13 @@ proc genRepr(p: PProc, n: PNode, r: var TCompRes) =
     genReprAux(p, n, r, "reprJSONStringify")
   else:
     genReprAux(p, n, r, "reprAny", genTypeInfo(p, t))
+  r.kind = resExpr
 
 proc genOf(p: PProc, n: PNode, r: var TCompRes) =
-  var x: TCompRes
-  let t = skipTypes(n.sons[2].typ, abstractVarRange+{tyRef, tyPtr, tyLent, tyTypeDesc})
-  gen(p, n.sons[1], x)
+  var x: TCompRes = default(TCompRes)
+  let t = skipTypes(n[2].typ,
+                    abstractVarRange+{tyRef, tyPtr, tyLent, tyTypeDesc, tyOwned})
+  gen(p, n[1], x)
   if tfFinal in t.flags:
     r.res = "($1.m_type == $2)" % [x.res, genTypeInfo(p, t)]
   else:
@@ -1623,50 +2263,97 @@ proc genOf(p: PProc, n: PNode, r: var TCompRes) =
     r.res = "isObj($1.m_type, $2)" % [x.res, genTypeInfo(p, t)]
   r.kind = resExpr
 
-proc genReset(p: PProc, n: PNode) =
-  var x: TCompRes
-  useMagic(p, "genericReset")
-  gen(p, n.sons[1], x)
-  addf(p.body, "$1 = genericReset($1, $2);$n", [x.res,
-                genTypeInfo(p, n.sons[1].typ)])
+proc genDefault(p: PProc, n: PNode; r: var TCompRes) =
+  r.res = createVar(p, n.typ, indirect = false)
+  r.kind = resExpr
+
+proc genWasMoved(p: PProc, n: PNode) =
+  # TODO: it should be done by nir
+  var x: TCompRes = default(TCompRes)
+  gen(p, n[1], x)
+  if x.typ == etyBaseIndex:
+    lineF(p, "$1 = null, $2 = 0;$n", [x.address, x.res])
+  else:
+    var y: TCompRes = default(TCompRes)
+    genDefault(p, n[1], y)
+    let (a, _) = maybeMakeTempAssignable(p, n[1], x)
+    lineF(p, "$1 = $2;$n", [a, y.rdLoc])
+
+proc genMove(p: PProc; n: PNode; r: var TCompRes) =
+  var a: TCompRes = default(TCompRes)
+  r.kind = resVal
+  r.res = p.getTemp()
+  gen(p, n[1], a)
+  lineF(p, "$1 = $2;$n", [r.rdLoc, a.rdLoc])
+  genWasMoved(p, n)
+  #lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc])
+
+proc genDup(p: PProc; n: PNode; r: var TCompRes) =
+  var a: TCompRes = default(TCompRes)
+  r.kind = resVal
+  r.res = p.getTemp()
+  gen(p, n[1], a)
+  lineF(p, "$1 = $2;$n", [r.rdLoc, a.rdLoc])
+
+proc genJSArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
+  var a: TCompRes = default(TCompRes)
+  r.res = rope("[")
+  r.kind = resExpr
+  for i in 0 ..< n.len:
+    if i > 0: r.res.add(", ")
+    gen(p, n[i], a)
+    if a.typ == etyBaseIndex:
+      r.res.addf("[$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)]
+      r.res.add(a.res)
+  r.res.add("]")
 
 proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   var
     a: TCompRes
     line, filen: Rope
-  var op = n.sons[0].sym.magic
+  var op = n[0].sym.magic
   case op
-  of mOr: genOr(p, n.sons[1], n.sons[2], r)
-  of mAnd: genAnd(p, n.sons[1], n.sons[2], r)
+  of mOr: genOr(p, n[1], n[2], r)
+  of mAnd: genAnd(p, n[1], n[2], r)
   of mAddI..mStrToStr: arith(p, n, r, op)
   of mRepr: genRepr(p, n, r)
   of mSwap: genSwap(p, n)
-  of mUnaryLt:
-    # XXX: range checking?
-    if not (optOverflowCheck in p.options): unaryExpr(p, n, r, "", "$1 - 1")
-    else: unaryExpr(p, n, r, "subInt", "subInt($1, 1)")
   of mAppendStrCh:
     binaryExpr(p, n, r, "addChar",
-        "if ($1 != null) { addChar($1, $2); } else { $1 = [$2, 0]; }")
+        "addChar($1, $2);")
   of mAppendStrStr:
-    if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString:
-        binaryExpr(p, n, r, "", "if ($1 != null) { $1 += $2; } else { $1 = $2; }")
+    var lhs, rhs: TCompRes = default(TCompRes)
+    gen(p, n[1], lhs)
+    gen(p, n[2], rhs)
+
+    if skipTypes(n[1].typ, abstractVarRange).kind == tyCstring:
+      let (b, tmp) = maybeMakeTemp(p, n[2], rhs)
+      r.res = "if (null != $1) { if (null == $2) $2 = $3; else $2 += $3; }" %
+        [b, lhs.rdLoc, tmp]
     else:
-      binaryExpr(p, n, r, "",
-        "if ($1 != null) { $1 = ($1.slice(0, -1)).concat($2); } else { $1 = $2;}")
-    # XXX: make a copy of $2, because of Javascript's sucking semantics
+      let (a, tmp) = maybeMakeTemp(p, n[1], lhs)
+      r.res = "$1.push.apply($3, $2);" % [a, rhs.rdLoc, tmp]
+    r.kind = resExpr
   of mAppendSeqElem:
-    var x, y: TCompRes
-    gen(p, n.sons[1], x)
-    gen(p, n.sons[2], y)
-    if needsNoCopy(p, n[2]):
-      r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, y.rdLoc]
+    var x, y: TCompRes = default(TCompRes)
+    gen(p, n[1], x)
+    gen(p, n[2], y)
+    if mapType(n[2].typ) == etyBaseIndex:
+      let c = "[$1, $2]" % [y.address, y.res]
+      r.res = "$1.push($2);" % [x.rdLoc, c]
+    elif needsNoCopy(p, n[2]):
+      r.res = "$1.push($2);" % [x.rdLoc, y.rdLoc]
     else:
       useMagic(p, "nimCopy")
       let c = getTemp(p, defineInLocals=false)
       lineF(p, "var $1 = nimCopy(null, $2, $3);$n",
             [c, y.rdLoc, genTypeInfo(p, n[2].typ)])
-      r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, c]
+      r.res = "$1.push($2);" % [x.rdLoc, c]
     r.kind = resExpr
   of mConStrStr:
     genConStrStr(p, n, r)
@@ -1676,48 +2363,93 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
     binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)")
   of mLtStr:
     binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)")
-  of mIsNil: unaryExpr(p, n, r, "", "($1 === null)")
+  of mIsNil:
+    # we want to accept undefined, so we ==
+    if mapType(n[1].typ) != etyBaseIndex:
+      unaryExpr(p, n, r, "", "($1 == null)")
+    else:
+      var x: TCompRes = default(TCompRes)
+      gen(p, n[1], x)
+      r.res = "($# == null && $# === 0)" % [x.address, x.res]
   of mEnumToStr: genRepr(p, n, r)
   of mNew, mNewFinalize: genNew(p, n)
-  of mSizeOf: r.res = rope(getSize(p.config, n.sons[1].typ))
-  of mChr, mArrToSeq: gen(p, n.sons[1], r)      # nothing to do
+  of mChr: gen(p, n[1], r)
+  of mArrToSeq:
+    # only array literals doesn't need copy
+    if n[1].kind == nkBracket:
+      genJSArrayConstr(p, n[1], r)
+    else:
+      var x: TCompRes = default(TCompRes)
+      gen(p, n[1], x)
+      useMagic(p, "nimCopy")
+      r.res = "nimCopy(null, $1, $2)" % [x.rdLoc, genTypeInfo(p, n.typ)]
+  of mOpenArrayToSeq:
+    genCall(p, n, r)
+  of mDestroy, mTrace: discard "ignore calls to the default destructor"
   of mOrd: genOrd(p, n, r)
-  of mLengthStr:
-    if n.sons[1].typ.skipTypes(abstractInst).kind == tyCString:
-      unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)")
-    else:
-      unaryExpr(p, n, r, "", "($1 != null ? $1.length-1 : 0)")
-  of mXLenStr: unaryExpr(p, n, r, "", "$1.length-1")
-  of mLengthSeq, mLengthOpenArray, mLengthArray:
-    unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)")
-  of mXLenSeq:
-    unaryExpr(p, n, r, "", "$1.length")
+  of mLengthStr, mLengthSeq, mLengthOpenArray, mLengthArray:
+    var x: TCompRes = default(TCompRes)
+    gen(p, n[1], x)
+    if skipTypes(n[1].typ, abstractInst).kind == tyCstring:
+      let (a, tmp) = maybeMakeTemp(p, n[1], x)
+      r.res = "(($1) == null ? 0 : ($2).length)" % [a, tmp]
+    else:
+      r.res = "($1).length" % [x.rdLoc]
+    r.kind = resExpr
   of mHigh:
-    if skipTypes(n.sons[1].typ, abstractVar).kind == tyString:
-      unaryExpr(p, n, r, "", "($1 != null ? ($1.length-2) : -1)")
+    var x: TCompRes = default(TCompRes)
+    gen(p, n[1], x)
+    if skipTypes(n[1].typ, abstractInst).kind == tyCstring:
+      let (a, tmp) = maybeMakeTemp(p, n[1], x)
+      r.res = "(($1) == null ? -1 : ($2).length - 1)" % [a, tmp]
     else:
-      unaryExpr(p, n, r, "", "($1 != null ? ($1.length-1) : -1)")
+      r.res = "($1).length - 1" % [x.rdLoc]
+    r.kind = resExpr
   of mInc:
-    if n[1].typ.skipTypes(abstractRange).kind in tyUInt .. tyUInt64:
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    case typ.kind
+    of tyUInt..tyUInt32:
       binaryUintExpr(p, n, r, "+", true)
+    of tyUInt64:
+      if optJsBigInt64 in p.config.globalOptions:
+        binaryExpr(p, n, r, "", "$1 = BigInt.asUintN(64, $3 + BigInt($2))", true)
+      else: binaryUintExpr(p, n, r, "+", true)
+    elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
+      if optOverflowCheck notin p.options:
+        binaryExpr(p, n, r, "", "$1 = BigInt.asIntN(64, $3 + BigInt($2))", true)
+      else: binaryExpr(p, n, r, "addInt64", "$1 = addInt64($3, BigInt($2))", true)
     else:
       if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2")
-      else: binaryExpr(p, n, r, "addInt", "$1 = addInt($1, $2)")
+      else: binaryExpr(p, n, r, "addInt", "$1 = addInt($3, $2)", true)
   of ast.mDec:
-    if n[1].typ.skipTypes(abstractRange).kind in tyUInt .. tyUInt64:
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    case typ.kind
+    of tyUInt..tyUInt32:
       binaryUintExpr(p, n, r, "-", true)
+    of tyUInt64:
+      if optJsBigInt64 in p.config.globalOptions:
+        binaryExpr(p, n, r, "", "$1 = BigInt.asUintN(64, $3 - BigInt($2))", true)
+      else: binaryUintExpr(p, n, r, "-", true)
+    elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
+      if optOverflowCheck notin p.options:
+        binaryExpr(p, n, r, "", "$1 = BigInt.asIntN(64, $3 - BigInt($2))", true)
+      else: binaryExpr(p, n, r, "subInt64", "$1 = subInt64($3, BigInt($2))", true)
     else:
       if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2")
-      else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)")
+      else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)", true)
   of mSetLengthStr:
-    binaryExpr(p, n, r, "", "$1.length = $2+1; $1[$1.length-1] = 0")
+    binaryExpr(p, n, r, "mnewString",
+      """if ($1.length < $2) { for (var i = $3.length; i < $4; ++i) $3.push(0); }
+         else {$3.length = $4; }""")
   of mSetLengthSeq:
-    var x, y: TCompRes
-    gen(p, n.sons[1], x)
-    gen(p, n.sons[2], y)
-    let t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
-    r.res = """if ($1.length < $2) { for (var i=$1.length;i<$2;++i) $1.push($3); }
-               else { $1.length = $2; }""" % [x.rdLoc, y.rdLoc, createVar(p, t, false)]
+    var x, y: TCompRes = default(TCompRes)
+    gen(p, n[1], x)
+    gen(p, n[2], y)
+    let t = skipTypes(n[1].typ, abstractVar)[0]
+    let (a, tmp) = maybeMakeTemp(p, n[1], x)
+    let (b, tmp2) = maybeMakeTemp(p, n[2], y)
+    r.res = """if ($1.length < $2) { for (var i = $4.length ; i < $5 ; ++i) $4.push($3); }
+               else { $4.length = $5; }""" % [a, b, createVar(p, t, false), tmp, tmp2]
     r.kind = resExpr
   of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)")
   of mLtSet: binaryExpr(p, n, r, "SetLt", "SetLt($1, $2)")
@@ -1733,128 +2465,209 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   of mNewSeq: genNewSeq(p, n)
   of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]")
   of mOf: genOf(p, n, r)
-  of mReset: genReset(p, n)
+  of mDefault, mZeroDefault: genDefault(p, n, r)
+  of mWasMoved: genWasMoved(p, n)
   of mEcho: genEcho(p, n, r)
   of mNLen..mNError, mSlurp, mStaticExec:
-    localError(p.config, n.info, errXMustBeCompileTime % n.sons[0].sym.name.s)
-  of mCopyStr:
-    binaryExpr(p, n, r, "", "($1.slice($2))")
-  of mCopyStrLast:
-    ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))")
+    localError(p.config, n.info, errXMustBeCompileTime % n[0].sym.name.s)
   of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)")
   of mNewStringOfCap:
     unaryExpr(p, n, r, "mnewString", "mnewString(0)")
   of mDotDot:
-    genProcForSymIfNeeded(p, n.sons[0].sym)
+    genProcForSymIfNeeded(p, n[0].sym)
     genCall(p, n, r)
   of mParseBiggestFloat:
     useMagic(p, "nimParseBiggestFloat")
     genCall(p, n, r)
-  of mArray:
-    genCall(p, n, r)
+  of mSlice:
+    # arr.slice([begin[, end]]): 'end' is exclusive
+    var x, y, z: TCompRes = default(TCompRes)
+    gen(p, n[1], x)
+    gen(p, n[2], y)
+    gen(p, n[3], z)
+    r.res = "($1.slice($2, $3 + 1))" % [x.rdLoc, y.rdLoc, z.rdLoc]
+    r.kind = resExpr
+  of mMove:
+    genMove(p, n, r)
+  of mDup:
+    genDup(p, n, r)
+  of mEnsureMove:
+    gen(p, n[1], r)
   else:
     genCall(p, n, r)
     #else internalError(p.config, e.info, 'genMagic: ' + magicToStr[op]);
 
 proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) =
   var
-    a, b: TCompRes
+    a, b: TCompRes = default(TCompRes)
   useMagic(p, "setConstr")
   r.res = rope("setConstr(")
   r.kind = resExpr
-  for i in countup(0, sonsLen(n) - 1):
-    if i > 0: add(r.res, ", ")
-    var it = n.sons[i]
+  for i in 0..<n.len:
+    if i > 0: r.res.add(", ")
+    var it = n[i]
     if it.kind == nkRange:
-      gen(p, it.sons[0], a)
-      gen(p, it.sons[1], b)
-      addf(r.res, "[$1, $2]", [a.res, b.res])
+      gen(p, it[0], a)
+      gen(p, it[1], b)
+
+      if it[0].typ.kind == tyBool:
+        r.res.addf("$1, $2", [a.res, b.res])
+      else:
+        r.res.addf("[$1, $2]", [a.res, b.res])
     else:
       gen(p, it, a)
-      add(r.res, a.res)
-  add(r.res, ")")
+      r.res.add(a.res)
+  r.res.add(")")
   # emit better code for constant sets:
   if isDeepConstExpr(n):
     inc(p.g.unique)
     let tmp = rope("ConstSet") & rope(p.g.unique)
-    addf(p.g.constants, "var $1 = $2;$n", [tmp, r.res])
+    p.g.constants.addf("var $1 = $2;$n", [tmp, r.res])
     r.res = tmp
 
 proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
-  var a: TCompRes
-  r.res = rope("[")
-  r.kind = resExpr
-  for i in countup(0, sonsLen(n) - 1):
-    if i > 0: add(r.res, ", ")
-    gen(p, n.sons[i], a)
-    add(r.res, a.res)
-  add(r.res, "]")
+  ## Constructs array or sequence.
+  ## Nim array of uint8..uint32, int8..int32 maps to JS typed arrays.
+  ## Nim sequence maps to JS array.
+  var t = skipTypes(n.typ, abstractInst)
+  let e = elemType(t)
+  let jsTyp = arrayTypeForElemType(p.config, e)
+  if skipTypes(n.typ, abstractVarRange).kind != tySequence and jsTyp.len > 0:
+    # generate typed array
+    # for example Nim generates `new Uint8Array([1, 2, 3])` for `[byte(1), 2, 3]`
+    # TODO use `set` or loop to initialize typed array which improves performances in some situations
+    var a: TCompRes = default(TCompRes)
+    r.res = "new $1([" % [rope(jsTyp)]
+    r.kind = resExpr
+    for i in 0 ..< n.len:
+      if i > 0: r.res.add(", ")
+      gen(p, n[i], a)
+      r.res.add(a.res)
+    r.res.add("])")
+  else:
+    genJSArrayConstr(p, n, r)
 
 proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
-  var a: TCompRes
+  var a: TCompRes = default(TCompRes)
   r.res = rope("{")
   r.kind = resExpr
-  for i in countup(0, sonsLen(n) - 1):
-    if i > 0: add(r.res, ", ")
-    var it = n.sons[i]
-    if it.kind == nkExprColonExpr: it = it.sons[1]
+  for i in 0..<n.len:
+    if i > 0: r.res.add(", ")
+    var it = n[i]
+    if it.kind == nkExprColonExpr: it = it[1]
     gen(p, it, a)
-    addf(r.res, "Field$#: $#", [i.rope, a.res])
+    let typ = it.typ.skipTypes(abstractInst)
+    if a.typ == etyBaseIndex:
+      r.res.addf("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)]
+      r.res.addf("Field$#: $#", [i.rope, a.res])
   r.res.add("}")
 
 proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
-  var a: TCompRes
+  var a: TCompRes = default(TCompRes)
   r.kind = resExpr
-  var initList : Rope
+  var initList : Rope = ""
   var fieldIDs = initIntSet()
-  for i in countup(1, sonsLen(n) - 1):
-    if i > 1: add(initList, ", ")
-    var it = n.sons[i]
+  let nTyp = n.typ.skipTypes(abstractInst)
+  for i in 1..<n.len:
+    if i > 1: initList.add(", ")
+    var it = n[i]
     internalAssert p.config, it.kind == nkExprColonExpr
-    let val = it.sons[1]
+    let val = it[1]
     gen(p, val, a)
-    var f = it.sons[0].sym
-    if f.loc.r == nil: f.loc.r = mangleName(p.module, f)
-    fieldIDs.incl(f.id)
+    var f = it[0].sym
+    if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
+    fieldIDs.incl(lookupFieldAgain(n.typ.skipTypes({tyDistinct}), f).id)
 
     let typ = val.typ.skipTypes(abstractInst)
-    if (typ.kind in IntegralTypes+{tyCstring, tyRef, tyPtr} and
-          mapType(p, typ) != etyBaseIndex) or needsNoCopy(p, it.sons[1]):
-      discard
+    if a.typ == etyBaseIndex:
+      initList.addf("$#: [$#, $#]", [f.loc.snippet, a.address, a.res])
     else:
-      useMagic(p, "nimCopy")
-      a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
-    addf(initList, "$#: $#", [f.loc.r, a.res])
+      if not needsNoCopy(p, val):
+        useMagic(p, "nimCopy")
+        a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
+      initList.addf("$#: $#", [f.loc.snippet, a.res])
   let t = skipTypes(n.typ, abstractInst + skipPtrs)
   createObjInitList(p, t, fieldIDs, initList)
   r.res = ("{$1}") % [initList]
 
 proc genConv(p: PProc, n: PNode, r: var TCompRes) =
   var dest = skipTypes(n.typ, abstractVarRange)
-  var src = skipTypes(n.sons[1].typ, abstractVarRange)
-  gen(p, n.sons[1], r)
+  var src = skipTypes(n[1].typ, abstractVarRange)
+  gen(p, n[1], r)
   if dest.kind == src.kind:
     # no-op conversion
     return
-  case dest.kind:
-  of tyBool:
+  let toInt = (dest.kind in tyInt..tyInt32)
+  let fromInt = (src.kind in tyInt..tyInt32)
+  let toUint = (dest.kind in tyUInt..tyUInt32)
+  let fromUint = (src.kind in tyUInt..tyUInt32)
+  if toUint and (fromInt or fromUint):
+    let trimmer = unsignedTrimmer(dest.size)
+    r.res = "($1 $2)" % [r.res, trimmer]
+  elif dest.kind == tyBool:
     r.res = "(!!($1))" % [r.res]
     r.kind = resExpr
-  of tyInt:
-    r.res = "(($1)|0)" % [r.res]
+  elif toInt:
+    if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
+      r.res = "Number($1)" % [r.res]
+    else:
+      r.res = "(($1) | 0)" % [r.res]
+  elif dest.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
+    if fromInt or fromUint or src.kind in {tyBool, tyChar, tyEnum}:
+      r.res = "BigInt($1)" % [r.res]
+    elif src.kind in {tyFloat..tyFloat64}:
+      r.res = "BigInt(Math.trunc($1))" % [r.res]
+    elif src.kind == tyUInt64:
+      r.res = "BigInt.asIntN(64, $1)" % [r.res]
+  elif dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
+    if fromUint or src.kind in {tyBool, tyChar, tyEnum}:
+      r.res = "BigInt($1)" % [r.res]
+    elif fromInt: # could be negative
+      r.res = "BigInt.asUintN(64, BigInt($1))" % [r.res]
+    elif src.kind in {tyFloat..tyFloat64}:
+      r.res = "BigInt.asUintN(64, BigInt(Math.trunc($1)))" % [r.res]
+    elif src.kind == tyInt64:
+      r.res = "BigInt.asUintN(64, $1)" % [r.res]
+  elif toUint or dest.kind in tyFloat..tyFloat64:
+    if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
+      r.res = "Number($1)" % [r.res]
   else:
     # TODO: What types must we handle here?
     discard
 
 proc upConv(p: PProc, n: PNode, r: var TCompRes) =
-  gen(p, n.sons[0], r)        # XXX
+  gen(p, n[0], r)        # XXX
 
 proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) =
-  var a, b: TCompRes
-  gen(p, n.sons[0], r)
-  if optRangeCheck in p.options:
-    gen(p, n.sons[1], a)
-    gen(p, n.sons[2], b)
+  var a, b: TCompRes = default(TCompRes)
+  gen(p, n[0], r)
+  let src = skipTypes(n[0].typ, abstractVarRange)
+  let dest = skipTypes(n.typ, abstractVarRange)
+  if optRangeCheck notin p.options:
+    if optJsBigInt64 in p.config.globalOptions and
+          dest.kind in {tyUInt..tyUInt32, tyInt..tyInt32} and
+          src.kind in {tyInt64, tyUInt64}:
+      # conversions to Number are kept
+      r.res = "Number($1)" % [r.res]
+    else:
+      discard
+  elif dest.kind in {tyUInt..tyUInt64} and checkUnsignedConversions notin p.config.legacyFeatures:
+    if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
+      r.res = "BigInt.asUintN($1, $2)" % [$(dest.size * 8), r.res]
+    else:
+      r.res = "BigInt.asUintN($1, BigInt($2))" % [$(dest.size * 8), r.res]
+    if not (dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions):
+      r.res = "Number($1)" % [r.res]
+  else:
+    if src.kind in {tyInt64, tyUInt64} and dest.kind notin {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
+      # we do a range check anyway, so it's ok if the number gets rounded
+      r.res = "Number($1)" % [r.res]
+    gen(p, n[1], a)
+    gen(p, n[2], b)
     useMagic(p, "chckRange")
     r.res = "chckRange($1, $2, $3)" % [r.res, a.res, b.res]
     r.kind = resExpr
@@ -1862,11 +2675,11 @@ proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) =
 proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) =
   # we do an optimization here as this is likely to slow down
   # much of the code otherwise:
-  if n.sons[0].kind == nkCStringToString:
-    gen(p, n.sons[0].sons[0], r)
+  if n[0].kind == nkCStringToString:
+    gen(p, n[0][0], r)
   else:
-    gen(p, n.sons[0], r)
-    if r.res == nil: internalError(p.config, n.info, "convStrToCStr")
+    gen(p, n[0], r)
+    if r.res == "": internalError(p.config, n.info, "convStrToCStr")
     useMagic(p, "toJSStr")
     r.res = "toJSStr($1)" % [r.res]
     r.kind = resExpr
@@ -1874,11 +2687,11 @@ proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) =
 proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) =
   # we do an optimization here as this is likely to slow down
   # much of the code otherwise:
-  if n.sons[0].kind == nkStringToCString:
-    gen(p, n.sons[0].sons[0], r)
+  if n[0].kind == nkStringToCString:
+    gen(p, n[0][0], r)
   else:
-    gen(p, n.sons[0], r)
-    if r.res == nil: internalError(p.config, n.info, "convCStrToStr")
+    gen(p, n[0], r)
+    if r.res == "": internalError(p.config, n.info, "convCStrToStr")
     useMagic(p, "cstrToNimstr")
     r.res = "cstrToNimstr($1)" % [r.res]
     r.kind = resExpr
@@ -1886,15 +2699,15 @@ proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) =
 proc genReturnStmt(p: PProc, n: PNode) =
   if p.procDef == nil: internalError(p.config, n.info, "genReturnStmt")
   p.beforeRetNeeded = true
-  if n.sons[0].kind != nkEmpty:
-    genStmt(p, n.sons[0])
+  if n[0].kind != nkEmpty:
+    genStmt(p, n[0])
   else:
     genLineDir(p, n)
   lineF(p, "break BeforeRet;$n", [])
 
 proc frameCreate(p: PProc; procname, filename: Rope): Rope =
-  let frameFmt =
-    "var F={procname:$1,prev:framePtr,filename:$2,line:0};$n"
+  const frameFmt =
+    "var F = {procname: $1, prev: framePtr, filename: $2, line: 0};$n"
 
   result = p.indentLine(frameFmt % [procname, filename])
   result.add p.indentLine(ropes.`%`("framePtr = F;$n", []))
@@ -1906,110 +2719,131 @@ proc genProcBody(p: PProc, prc: PSym): Rope =
   if hasFrameInfo(p):
     result = frameCreate(p,
               makeJSString(prc.owner.name.s & '.' & prc.name.s),
-              makeJSString(toFilename(p.config, prc.info)))
+              makeJSString(toFilenameOption(p.config, prc.info.fileIndex, foStacktrace)))
   else:
-    result = nil
+    result = ""
   if p.beforeRetNeeded:
-    result.add p.indentLine(~"BeforeRet: do {$n")
+    result.add p.indentLine("BeforeRet: {\n")
     result.add p.body
-    result.add p.indentLine(~"} while (false);$n")
+    result.add p.indentLine("};\n")
   else:
-    add(result, p.body)
+    result.add(p.body)
   if prc.typ.callConv == ccSysCall:
     result = ("try {$n$1} catch (e) {$n" &
       " alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}") % [result]
   if hasFrameInfo(p):
-    add(result, frameDestroy(p))
+    result.add(frameDestroy(p))
 
-proc optionaLine(p: Rope): Rope =
-  if p == nil:
-    return nil
+proc optionalLine(p: Rope): Rope =
+  if p == "":
+    return ""
   else:
     return p & "\L"
 
 proc genProc(oldProc: PProc, prc: PSym): Rope =
+  ## Generate a JS procedure ('function').
+  result = ""
   var
     resultSym: PSym
-    a: TCompRes
+    a: TCompRes = default(TCompRes)
   #if gVerbosity >= 3:
   #  echo "BEGIN generating code for: " & prc.name.s
   var p = newProc(oldProc.g, oldProc.module, prc.ast, prc.options)
   p.up = oldProc
-  var returnStmt: Rope = nil
-  var resultAsgn: Rope = nil
+  var returnStmt: Rope = ""
+  var resultAsgn: Rope = ""
   var name = mangleName(p.module, prc)
-  let header = generateHeader(p, prc.typ)
-  if prc.typ.sons[0] != nil and sfPure notin prc.flags:
-    resultSym = prc.ast.sons[resultPos].sym
+  let header = generateHeader(p, prc)
+  if prc.typ.returnType != nil and sfPure notin prc.flags:
+    resultSym = prc.ast[resultPos].sym
     let mname = mangleName(p.module, resultSym)
-    let resVar = createVar(p, resultSym.typ, isIndirect(resultSym))
-    resultAsgn = p.indentLine(("var $# = $#;$n") % [mname, resVar])
-    if resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and
-        mapType(p, resultSym.typ) == etyBaseIndex:
+    # otherwise uses "fat pointers"
+    let useRawPointer = not isIndirect(resultSym) and
+      resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef, tyOwned} and
+        mapType(p, resultSym.typ) == etyBaseIndex
+    if useRawPointer:
+      resultAsgn = p.indentLine(("var $# = null;$n") % [mname])
       resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname])
-    gen(p, prc.ast.sons[resultPos], a)
+    else:
+      let resVar = createVar(p, resultSym.typ, isIndirect(resultSym))
+      resultAsgn = p.indentLine(("var $# = $#;$n") % [mname, resVar])
+    gen(p, prc.ast[resultPos], a)
     if mapType(p, resultSym.typ) == etyBaseIndex:
       returnStmt = "return [$#, $#];$n" % [a.address, a.res]
     else:
       returnStmt = "return $#;$n" % [a.res]
 
-  p.nested: genStmt(p, prc.getBody)
+  var transformedBody = transformBody(p.module.graph, p.module.idgen, prc, {})
+  if sfInjectDestructors in prc.flags:
+    transformedBody = injectDestructorCalls(p.module.graph, p.module.idgen, prc, transformedBody)
+
+  p.nested: genStmt(p, transformedBody)
+
+
+  if optLineDir in p.config.options:
+    result = lineDir(p.config, prc.info, toLinenumber(prc.info))
 
   var def: Rope
   if not prc.constraint.isNil:
-    def = (prc.constraint.strVal & " {$n$#$#$#$#$#") %
+    def = runtimeFormat(prc.constraint.strVal & " {$n$#$#$#$#$#",
             [ returnType,
               name,
               header,
-              optionaLine(p.globals),
-              optionaLine(p.locals),
-              optionaLine(resultAsgn),
-              optionaLine(genProcBody(p, prc)),
-              optionaLine(p.indentLine(returnStmt))]
+              optionalLine(p.globals),
+              optionalLine(p.locals),
+              optionalLine(resultAsgn),
+              optionalLine(genProcBody(p, prc)),
+              optionalLine(p.indentLine(returnStmt))])
   else:
-    result = ~"\L"
+    # if optLineDir in p.config.options:
+      # result.add("\L")
 
-    if optHotCodeReloading in p.config.options:
+    if p.config.hcrOn:
       # Here, we introduce thunks that create the equivalent of a jump table
       # for all global functions, because references to them may be stored
       # in JavaScript variables. The added indirection ensures that such
       # references will end up calling the reloaded code.
       var thunkName = name
       name = name & "IMLP"
-      result.add("function $#() { return $#.apply(this, arguments); }$n" %
+      result.add("\Lfunction $#() { return $#.apply(this, arguments); }$n" %
                  [thunkName, name])
 
-    def = "function $#($#) {$n$#$#$#$#$#" %
+    def = "\Lfunction $#($#) {$n$#$#$#$#$#" %
             [ name,
               header,
-              optionaLine(p.globals),
-              optionaLine(p.locals),
-              optionaLine(resultAsgn),
-              optionaLine(genProcBody(p, prc)),
-              optionaLine(p.indentLine(returnStmt))]
+              optionalLine(p.globals),
+              optionalLine(p.locals),
+              optionalLine(resultAsgn),
+              optionalLine(genProcBody(p, prc)),
+              optionalLine(p.indentLine(returnStmt))]
 
   dec p.extraIndent
   result.add p.indentLine(def)
-  result.add p.indentLine(~"}$n")
+  result.add p.indentLine("}\n")
 
   #if gVerbosity >= 3:
   #  echo "END   generated code for: " & prc.name.s
 
 proc genStmt(p: PProc, n: PNode) =
-  var r: TCompRes
+  var r: TCompRes = default(TCompRes)
   gen(p, n, r)
-  if r.res != nil: lineF(p, "$#;$n", [r.res])
+  if r.res != "": lineF(p, "$#;$n", [r.res])
 
 proc genPragma(p: PProc, n: PNode) =
-  for it in n.sons:
+  for i in 0..<n.len:
+    let it = n[i]
     case whichPragma(it)
-    of wEmit: genAsmOrEmitStmt(p, it.sons[1])
+    of wEmit: genAsmOrEmitStmt(p, it[1])
+    of wPush:
+      processPushBackendOption(p.config, p.optionsStack, p.options, n, i+1)
+    of wPop:
+      processPopBackendOption(p.config, p.optionsStack, p.options)
     else: discard
 
 proc genCast(p: PProc, n: PNode, r: var TCompRes) =
   var dest = skipTypes(n.typ, abstractVarRange)
-  var src = skipTypes(n.sons[1].typ, abstractVarRange)
-  gen(p, n.sons[1], r)
+  var src = skipTypes(n[1].typ, abstractVarRange)
+  gen(p, n[1], r)
   if dest.kind == src.kind:
     # no-op conversion
     return
@@ -2018,39 +2852,72 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) =
   let fromInt = (src.kind in tyInt..tyInt32)
   let fromUint = (src.kind in tyUInt..tyUInt32)
 
-  if toUint and (fromInt or fromUint):
-    let trimmer = unsignedTrimmer(dest.size)
-    r.res = "($1 $2)" % [r.res, trimmer]
+  if toUint:
+    if fromInt or fromUint:
+      r.res = "Number(BigInt.asUintN($1, BigInt($2)))" % [$(dest.size * 8), r.res]
+    elif src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
+      r.res = "Number(BigInt.asUintN($1, $2))" % [$(dest.size * 8), r.res]
   elif toInt:
-    if fromInt:
-      let trimmer = unsignedTrimmer(dest.size)
-      r.res = "($1 $2)" % [r.res, trimmer]
-    elif fromUint:
-      if src.size == 4 and dest.size == 4:
-        # XXX prevent multi evaluations
-        r.res = "($1|0)" % [r.res]
-      else:
-        let trimmer = unsignedTrimmer(dest.size)
-        let minuend = case dest.size
-          of 1: "0xfe"
-          of 2: "0xfffe"
-          of 4: "0xfffffffe"
-          else: ""
-        r.res = "($1 - ($2 $3))" % [rope minuend, r.res, trimmer]
+    if fromInt or fromUint:
+      r.res = "Number(BigInt.asIntN($1, BigInt($2)))" % [$(dest.size * 8), r.res]
+    elif src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
+      r.res = "Number(BigInt.asIntN($1, $2))" % [$(dest.size * 8), r.res]
+  elif dest.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
+    if fromInt or fromUint or src.kind in {tyBool, tyChar, tyEnum}:
+      r.res = "BigInt($1)" % [r.res]
+    elif src.kind in {tyFloat..tyFloat64}:
+      r.res = "BigInt(Math.trunc($1))" % [r.res]
+    elif src.kind == tyUInt64:
+      r.res = "BigInt.asIntN(64, $1)" % [r.res]
+  elif dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
+    if fromUint or src.kind in {tyBool, tyChar, tyEnum}:
+      r.res = "BigInt($1)" % [r.res]
+    elif fromInt: # could be negative
+      r.res = "BigInt.asUintN(64, BigInt($1))" % [r.res]
+    elif src.kind in {tyFloat..tyFloat64}:
+      r.res = "BigInt.asUintN(64, BigInt(Math.trunc($1)))" % [r.res]
+    elif src.kind == tyInt64:
+      r.res = "BigInt.asUintN(64, $1)" % [r.res]
+  elif dest.kind in tyFloat..tyFloat64:
+    if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
+      r.res = "Number($1)" % [r.res]
+  elif (src.kind == tyPtr and mapType(p, src) == etyObject) and dest.kind == tyPointer:
+    r.address = r.res
+    r.res = "null"
+    r.typ = etyBaseIndex
+  elif (dest.kind == tyPtr and mapType(p, dest) == etyObject) and src.kind == tyPointer:
+    r.res = r.address
+    r.typ = etyObject
 
 proc gen(p: PProc, n: PNode, r: var TCompRes) =
   r.typ = etyNone
   if r.kind != resCallee: r.kind = resNone
-  #r.address = nil
-  r.res = nil
+  #r.address = ""
+  r.res = ""
+
   case n.kind
   of nkSym:
     genSym(p, n, r)
   of nkCharLit..nkUInt64Lit:
-    if n.typ.kind == tyBool:
+    case n.typ.skipTypes(abstractVarRange).kind
+    of tyBool:
       r.res = if n.intVal == 0: rope"false" else: rope"true"
+    of tyUInt64:
+      r.res = rope($cast[BiggestUInt](n.intVal))
+      if optJsBigInt64 in p.config.globalOptions:
+        r.res.add('n')
+    of tyInt64:
+      let wrap = n.intVal < 0 # wrap negative integers with parens
+      if wrap: r.res.add '('
+      r.res.addInt n.intVal
+      if optJsBigInt64 in p.config.globalOptions:
+        r.res.add('n')
+      if wrap: r.res.add ')'
     else:
-      r.res = rope(n.intVal)
+      let wrap = n.intVal < 0 # wrap negative integers with parens
+      if wrap: r.res.add '('
+      r.res.addInt n.intVal
+      if wrap: r.res.add ')'
     r.kind = resExpr
   of nkNilLit:
     if isEmptyType(n.typ):
@@ -2065,16 +2932,22 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
       r.kind = resExpr
   of nkStrLit..nkTripleStrLit:
     if skipTypes(n.typ, abstractVarRange).kind == tyString:
-      useMagic(p, "makeNimstrLit")
-      r.res = "makeNimstrLit($1)" % [makeJSString(n.strVal)]
+      if n.strVal.len <= 64:
+        r.res = makeJsNimStrLit(n.strVal)
+      else:
+        useMagic(p, "makeNimstrLit")
+        r.res = "makeNimstrLit($1)" % [makeJSString(n.strVal)]
     else:
       r.res = makeJSString(n.strVal, false)
     r.kind = resExpr
   of nkFloatLit..nkFloat64Lit:
     let f = n.floatVal
     case classify(f)
-    of fcNaN:
-      r.res = rope"NaN"
+    of fcNan:
+      if signbit(f):
+        r.res = rope"-NaN"
+      else:
+        r.res = rope"NaN"
     of fcNegZero:
       r.res = rope"-0.0"
     of fcZero:
@@ -2083,30 +2956,53 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
       r.res = rope"Infinity"
     of fcNegInf:
       r.res = rope"-Infinity"
-    else: r.res = rope(f.toStrMaxPrecision)
+    else:
+      if n.typ.skipTypes(abstractVarRange).kind == tyFloat32:
+        r.res.addFloatRoundtrip(f.float32)
+      else:
+        r.res.addFloatRoundtrip(f)
     r.kind = resExpr
   of nkCallKinds:
-    if isEmptyType(n.typ): genLineDir(p, n)
-    if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone):
+    if isEmptyType(n.typ):
+      genLineDir(p, n)
+    if (n[0].kind == nkSym) and (n[0].sym.magic != mNone):
       genMagic(p, n, r)
-    elif n.sons[0].kind == nkSym and sfInfixCall in n.sons[0].sym.flags and
+    elif n[0].kind == nkSym and sfInfixCall in n[0].sym.flags and
         n.len >= 1:
       genInfixCall(p, n, r)
     else:
       genCall(p, n, r)
-  of nkClosure: gen(p, n[0], r)
+  of nkClosure:
+    if jsNoLambdaLifting in p.config.legacyFeatures:
+      gen(p, n[0], r)
+    else:
+      let tmp = getTemp(p)
+      var a: TCompRes = default(TCompRes)
+      var b: TCompRes = default(TCompRes)
+      gen(p, n[0], a)
+      gen(p, n[1], b)
+      lineF(p, "$1 = $2.bind($3); $1.ClP_0 = $2; $1.ClE_0 = $3;$n", [tmp, a.rdLoc, b.rdLoc])
+      r.res = tmp
+      r.kind = resVal
   of nkCurly: genSetConstr(p, n, r)
   of nkBracket: genArrayConstr(p, n, r)
   of nkPar, nkTupleConstr: genTupleConstr(p, n, r)
   of nkObjConstr: genObjConstr(p, n, r)
   of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, r)
   of nkAddr, nkHiddenAddr:
-    genAddr(p, n, r)
-  of nkDerefExpr, nkHiddenDeref: genDeref(p, n, r)
+    if n.typ.kind in {tyLent}:
+      gen(p, n[0], r)
+    else:
+      genAddr(p, n, r)
+  of nkDerefExpr, nkHiddenDeref:
+    if n.typ.kind in {tyLent}:
+      gen(p, n[0], r)
+    else:
+      genDeref(p, n, r)
   of nkBracketExpr: genArrayAccess(p, n, r)
   of nkDotExpr: genFieldAccess(p, n, r)
-  of nkCheckedFieldExpr: genCheckedFieldAccess(p, n, r)
-  of nkObjDownConv: gen(p, n.sons[0], r)
+  of nkCheckedFieldExpr: genCheckedFieldOp(p, n, nil, r)
+  of nkObjDownConv: gen(p, n[0], r)
   of nkObjUpConv: upConv(p, n, r)
   of nkCast: genCast(p, n, r)
   of nkChckRangeF: genRangeChck(p, n, r, "chckRangeF")
@@ -2116,26 +3012,26 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
   of nkCStringToString: convCStrToStr(p, n, r)
   of nkEmpty: discard
   of nkLambdaKinds:
-    let s = n.sons[namePos].sym
+    let s = n[namePos].sym
     discard mangleName(p.module, s)
-    r.res = s.loc.r
-    if lfNoDecl in s.loc.flags or s.magic != mNone: discard
+    r.res = s.loc.snippet
+    if lfNoDecl in s.loc.flags or s.magic notin generatedMagics: discard
     elif not p.g.generatedSyms.containsOrIncl(s.id):
-      add(p.locals, genProc(p, s))
+      p.locals.add(genProc(p, s))
   of nkType: r.res = genTypeInfo(p, n.typ)
   of nkStmtList, nkStmtListExpr:
     # this shows the distinction is nice for backends and should be kept
     # in the frontend
     let isExpr = not isEmptyType(n.typ)
-    for i in countup(0, sonsLen(n) - 1 - isExpr.ord):
-      genStmt(p, n.sons[i])
+    for i in 0..<n.len - isExpr.ord:
+      genStmt(p, n[i])
     if isExpr:
       gen(p, lastSon(n), r)
   of nkBlockStmt, nkBlockExpr: genBlock(p, n, r)
   of nkIfStmt, nkIfExpr: genIf(p, n, r)
   of nkWhen:
     # This is "when nimvm" node
-    gen(p, n.sons[1].sons[0], r)
+    gen(p, n[1][0], r)
   of nkWhileStmt: genWhileStmt(p, n)
   of nkVarSection, nkLetSection: genVarStmt(p, n)
   of nkConstSection: discard
@@ -2145,140 +3041,172 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
   of nkReturnStmt: genReturnStmt(p, n)
   of nkBreakStmt: genBreakStmt(p, n)
   of nkAsgn: genAsgn(p, n)
-  of nkFastAsgn: genFastAsgn(p, n)
+  of nkFastAsgn, nkSinkAsgn: genFastAsgn(p, n)
   of nkDiscardStmt:
-    if n.sons[0].kind != nkEmpty:
+    if n[0].kind != nkEmpty:
       genLineDir(p, n)
-      gen(p, n.sons[0], r)
-  of nkAsmStmt: genAsmOrEmitStmt(p, n)
-  of nkTryStmt: genTry(p, n, r)
+      gen(p, n[0], r)
+      r.res = "(" & r.res & ")"
+  of nkAsmStmt:
+    warningDeprecated(p.config, n.info, "'asm' for the JS target is deprecated, use the 'emit' pragma")
+    genAsmOrEmitStmt(p, n, true)
+  of nkTryStmt, nkHiddenTryStmt: genTry(p, n, r)
   of nkRaiseStmt: genRaiseStmt(p, n)
-  of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt,
+  of nkTypeSection, nkCommentStmt, nkIncludeStmt,
      nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
-     nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt: discard
+     nkFromStmt, nkTemplateDef, nkMacroDef, nkIteratorDef, nkStaticStmt,
+     nkMixinStmt, nkBindStmt: discard
   of nkPragma: genPragma(p, n)
   of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef:
-    var s = n.sons[namePos].sym
+    var s = n[namePos].sym
     if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}:
-      genSym(p, n.sons[namePos], r)
-      r.res = nil
+      genSym(p, n[namePos], r)
+      r.res = ""
   of nkGotoState, nkState:
-    internalError(p.config, n.info, "first class iterators not implemented")
+    globalError(p.config, n.info, "not implemented")
+  of nkBreakState:
+    genBreakState(p, n[0], r)
   of nkPragmaBlock: gen(p, n.lastSon, r)
   of nkComesFrom:
     discard "XXX to implement for better stack traces"
   else: internalError(p.config, n.info, "gen: unknown node type: " & $n.kind)
 
 proc newModule(g: ModuleGraph; module: PSym): BModule =
-  new(result)
-  result.module = module
-  result.sigConflicts = initCountTable[SigHash]()
+  ## Create a new JS backend module node.
   if g.backend == nil:
     g.backend = newGlobals()
-  result.graph = g
-  result.config = g.config
+  result = BModule(module: module, sigConflicts: initCountTable[SigHash](),
+                   graph: g, config: g.config
+  )
+  if sfSystemModule in module.flags:
+    PGlobals(g.backend).inSystem = true
 
 proc genHeader(): Rope =
-  result = (
-    "/* Generated by the Nim Compiler v$1 */$n" &
-    "/*   (c) " & copyrightYear & " Andreas Rumpf */$n$n" &
-    "var framePtr = null;$n" &
-    "var excHandler = 0;$n" &
-    "var lastJSError = null;$n" &
-    "if (typeof Int8Array === 'undefined') Int8Array = Array;$n" &
-    "if (typeof Int16Array === 'undefined') Int16Array = Array;$n" &
-    "if (typeof Int32Array === 'undefined') Int32Array = Array;$n" &
-    "if (typeof Uint8Array === 'undefined') Uint8Array = Array;$n" &
-    "if (typeof Uint16Array === 'undefined') Uint16Array = Array;$n" &
-    "if (typeof Uint32Array === 'undefined') Uint32Array = Array;$n" &
-    "if (typeof Float32Array === 'undefined') Float32Array = Array;$n" &
-    "if (typeof Float64Array === 'undefined') Float64Array = Array;$n") %
-    [rope(VersionAsString)]
+  ## Generate the JS header.
+  result = rope("""/* Generated by the Nim Compiler v$1 */
+    var framePtr = null;
+    var excHandler = 0;
+    var lastJSError = null;
+  """.unindent.format(VersionAsString))
+
+proc addHcrInitGuards(p: PProc, n: PNode,
+                      moduleLoadedVar: Rope, inInitGuard: var bool) =
+  if n.kind == nkStmtList:
+    for child in n:
+      addHcrInitGuards(p, child, moduleLoadedVar, inInitGuard)
+  else:
+    let stmtShouldExecute = n.kind in {
+      nkProcDef, nkFuncDef, nkMethodDef,nkConverterDef,
+      nkVarSection, nkLetSection} or nfExecuteOnReload in n.flags
+
+    if inInitGuard:
+      if stmtShouldExecute:
+        dec p.extraIndent
+        line(p, "}\L")
+        inInitGuard = false
+    else:
+      if not stmtShouldExecute:
+        lineF(p, "if ($1 == undefined) {$n", [moduleLoadedVar])
+        inc p.extraIndent
+        inInitGuard = true
+
+    genStmt(p, n)
 
 proc genModule(p: PProc, n: PNode) =
+  ## Generate the JS module code.
+  ## Called for each top level node in a Nim module.
   if optStackTrace in p.options:
-    add(p.body, frameCreate(p,
+    p.body.add(frameCreate(p,
         makeJSString("module " & p.module.module.name.s),
-        makeJSString(toFilename(p.config, p.module.module.info))))
-  genStmt(p, n)
+        makeJSString(toFilenameOption(p.config, p.module.module.info.fileIndex, foStacktrace))))
+  var transformedN = transformStmt(p.module.graph, p.module.idgen, p.module.module, n)
+  if sfInjectDestructors in p.module.module.flags:
+    transformedN = injectDestructorCalls(p.module.graph, p.module.idgen, p.module.module, transformedN)
+  if p.config.hcrOn and n.kind == nkStmtList:
+    let moduleSym = p.module.module
+    var moduleLoadedVar = rope(moduleSym.name.s) & "_loaded" &
+                          idOrSig(moduleSym, moduleSym.name.s, p.module.sigConflicts, p.config)
+    lineF(p, "var $1;$n", [moduleLoadedVar])
+    var inGuardedBlock = false
+
+    addHcrInitGuards(p, transformedN, moduleLoadedVar, inGuardedBlock)
+
+    if inGuardedBlock:
+      dec p.extraIndent
+      line(p, "}\L")
+
+    lineF(p, "$1 = true;$n", [moduleLoadedVar])
+  else:
+    genStmt(p, transformedN)
+
   if optStackTrace in p.options:
-    add(p.body, frameDestroy(p))
+    p.body.add(frameDestroy(p))
 
-proc myProcess(b: PPassContext, n: PNode): PNode =
+proc processJSCodeGen*(b: PPassContext, n: PNode): PNode =
+  ## Generate JS code for a node.
   result = n
   let m = BModule(b)
-  if passes.skipCodegen(m.config, n): return n
+  if pipelineutils.skipCodegen(m.config, n): return n
   if m.module == nil: internalError(m.config, n.info, "myProcess")
   let globals = PGlobals(m.graph.backend)
-  var p = newProc(globals, m, nil, m.module.options)
+  var p = newInitProc(globals, m)
+  m.initProc = p
   p.unique = globals.unique
   genModule(p, n)
-  add(p.g.code, p.locals)
-  add(p.g.code, p.body)
+  p.g.code.add(p.locals)
+  p.g.code.add(p.body)
 
 proc wholeCode(graph: ModuleGraph; m: BModule): Rope =
+  ## Combine source code from all nodes.
   let globals = PGlobals(graph.backend)
   for prc in globals.forwarded:
     if not globals.generatedSyms.containsOrIncl(prc.id):
-      var p = newProc(globals, m, nil, m.module.options)
+      var p = newInitProc(globals, m)
       attachProc(p, prc)
 
-  var disp = generateMethodDispatchers(graph)
-  for i in 0..sonsLen(disp)-1:
-    let prc = disp.sons[i].sym
+  generateIfMethodDispatchers(graph, m.idgen)
+  for prc in getDispatchers(graph):
     if not globals.generatedSyms.containsOrIncl(prc.id):
-      var p = newProc(globals, m, nil, m.module.options)
+      var p = newInitProc(globals, m)
       attachProc(p, prc)
 
   result = globals.typeInfo & globals.constants & globals.code
 
-proc getClassName(t: PType): Rope =
-  var s = t.sym
-  if s.isNil or sfAnon in s.flags:
-    s = skipTypes(t, abstractPtrs).sym
-  if s.isNil or sfAnon in s.flags:
-    doAssert(false, "cannot retrieve class name")
-  if s.loc.r != nil: result = s.loc.r
-  else: result = rope(s.name.s)
-
-proc genClass(conf: ConfigRef; obj: PType; content: Rope; ext: string) =
-  let cls = getClassName(obj)
-  let t = skipTypes(obj, abstractPtrs)
-  let extends = if t.kind == tyObject and t.sons[0] != nil:
-      " extends " & getClassName(t.sons[0])
-    else: nil
-  let result = ("<?php$n" &
-            "/* Generated by the Nim Compiler v$# */$n" &
-            "/*   (c) " & copyrightYear & " Andreas Rumpf */$n$n" &
-            "require_once \"nimsystem.php\";$n" &
-            "class $#$# {$n$#$n}$n") %
-           [rope(VersionAsString), cls, extends, content]
-
-  let outfile = changeFileExt(completeCFilePath(conf, $cls), ext)
-  discard writeRopeIfNotEqual(result, outfile)
-
-proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
-  result = myProcess(b, n)
+proc finalJSCodeGen*(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
+  ## Finalize JS code generation of a Nim module.
+  ## Param `n` may contain nodes returned from the last module close call.
   var m = BModule(b)
-  if passes.skipCodegen(m.config, n): return n
   if sfMainModule in m.module.flags:
-    let globals = PGlobals(graph.backend)
-    let ext = "js"
-    let f = if globals.classes.len == 0: toFilename(m.config, FileIndex m.module.position)
-            else: "nimsystem"
-    let code = wholeCode(graph, m)
-    let outfile =
-      if m.config.outFile.len > 0:
-        if m.config.outFile.isAbsolute: m.config.outFile
-        else: getCurrentDir() / m.config.outFile
-      else:
-        changeFileExt(completeCFilePath(m.config, f), ext)
-    discard writeRopeIfNotEqual(genHeader() & code, outfile)
-    for obj, content in items(globals.classes):
-      genClass(m.config, obj, content, ext)
-
-proc myOpen(graph: ModuleGraph; s: PSym): PPassContext =
+    # Add global destructors to the module.
+    # This must come before the last call to `myProcess`.
+    for i in countdown(high(graph.globalDestructors), 0):
+      n.add graph.globalDestructors[i]
+  # Process any nodes left over from the last call to `myClose`.
+  result = processJSCodeGen(b, n)
+  # Some codegen is different (such as no stacktraces; see `initProcOptions`)
+  # when `std/system` is being processed.
+  if sfSystemModule in m.module.flags:
+    PGlobals(graph.backend).inSystem = false
+  # Check if codegen should continue before any files are generated.
+  # It may bail early is if too many errors have been raised.
+  if pipelineutils.skipCodegen(m.config, n): return n
+  # Nim modules are compiled into a single JS file.
+  # If this is the main module, then this is the final call to `myClose`.
+  if sfMainModule in m.module.flags:
+    var code = genHeader() & wholeCode(graph, m)
+    let outFile = m.config.prepareToWriteOutput()
+    # Generate an optional source map.
+    if optSourcemap in m.config.globalOptions:
+      var map: SourceMap
+      map = genSourceMap($code, outFile.string)
+      code &= "\n//# sourceMappingURL=$#.map" % [outFile.string]
+      writeFile(outFile.string & ".map", $(%map))
+    # Check if the generated JS code matches the output file, or else
+    # write it to the file.
+    if not equalsFile(code, outFile):
+      if not writeRope(code, outFile):
+        rawMessage(m.config, errCannotOpenFile, outFile.string)
+
+proc setupJSgen*(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext =
   result = newModule(graph, s)
-
-const JSgenPass* = makePass(myOpen, myProcess, myClose)
-
+  result.idgen = idgen
diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim
index d86b09a03..d980f9989 100644
--- a/compiler/jstypes.nim
+++ b/compiler/jstypes.nim
@@ -7,28 +7,30 @@
 #    distribution, for details about the copyright.
 #
 
+# included from jsgen.nim
+
 ## Type info generation for the JS backend.
 
+proc rope(arg: Int128): Rope = rope($arg)
+
 proc genTypeInfo(p: PProc, typ: PType): Rope
 proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
   var
     s, u: Rope
-    length: int
     field: PSym
     b: PNode
-  result = nil
+  result = ""
   case n.kind
   of nkRecList:
-    length = sonsLen(n)
-    if length == 1:
-      result = genObjectFields(p, typ, n.sons[0])
+    if n.len == 1:
+      result = genObjectFields(p, typ, n[0])
     else:
-      s = nil
-      for i in countup(0, length - 1):
-        if i > 0: add(s, ", \L")
-        add(s, genObjectFields(p, typ, n.sons[i]))
+      s = ""
+      for i in 0..<n.len:
+        if i > 0: s.add(", \L")
+        s.add(genObjectFields(p, typ, n[i]))
       result = ("{kind: 2, len: $1, offset: 0, " &
-          "typ: null, name: null, sons: [$2]}") % [rope(length), s]
+          "typ: null, name: null, sons: [$2]}") % [rope(n.len), s]
   of nkSym:
     field = n.sym
     s = genTypeInfo(p, field.typ)
@@ -37,29 +39,28 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
                    [mangleName(p.module, field), s,
                     makeJSString(field.name.s)]
   of nkRecCase:
-    length = sonsLen(n)
-    if (n.sons[0].kind != nkSym): internalError(p.config, n.info, "genObjectFields")
-    field = n.sons[0].sym
+    if (n[0].kind != nkSym): internalError(p.config, n.info, "genObjectFields")
+    field = n[0].sym
     s = genTypeInfo(p, field.typ)
-    for i in countup(1, length - 1):
-      b = n.sons[i]           # branch
-      u = nil
+    for i in 1..<n.len:
+      b = n[i]           # branch
+      u = ""
       case b.kind
       of nkOfBranch:
-        if sonsLen(b) < 2:
+        if b.len < 2:
           internalError(p.config, b.info, "genObjectFields; nkOfBranch broken")
-        for j in countup(0, sonsLen(b) - 2):
-          if u != nil: add(u, ", ")
-          if b.sons[j].kind == nkRange:
-            addf(u, "[$1, $2]", [rope(getOrdValue(b.sons[j].sons[0])),
-                                 rope(getOrdValue(b.sons[j].sons[1]))])
+        for j in 0..<b.len - 1:
+          if u != "": u.add(", ")
+          if b[j].kind == nkRange:
+            u.addf("[$1, $2]", [rope(getOrdValue(b[j][0])),
+                                 rope(getOrdValue(b[j][1]))])
           else:
-            add(u, rope(getOrdValue(b.sons[j])))
+            u.add(rope(getOrdValue(b[j])))
       of nkElse:
         u = rope(lengthOrd(p.config, field.typ))
       else: internalError(p.config, n.info, "genObjectFields(nkRecCase)")
-      if result != nil: add(result, ", \L")
-      addf(result, "[setConstr($1), $2]",
+      if result != "": result.add(", \L")
+      result.addf("[setConstr($1), $2]",
            [u, genObjectFields(p, typ, lastSon(b))])
     result = ("{kind: 3, offset: \"$1\", len: $3, " &
         "typ: $2, name: $4, sons: [$5]}") % [
@@ -68,27 +69,27 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
   else: internalError(p.config, n.info, "genObjectFields")
 
 proc objHasTypeField(t: PType): bool {.inline.} =
-  tfInheritable in t.flags or t.sons[0] != nil
+  tfInheritable in t.flags or t.baseClass != nil
 
 proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
   let kind = if objHasTypeField(typ): tyObject else: tyTuple
   var s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " &
            "finalizer: null};$n") % [name, rope(ord(kind))]
   prepend(p.g.typeInfo, s)
-  addf(p.g.typeInfo, "var NNI$1 = $2;$n",
+  p.g.typeInfo.addf("var NNI$1 = $2;$n",
        [rope(typ.id), genObjectFields(p, typ, typ.n)])
-  addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)])
-  if (typ.kind == tyObject) and (typ.sons[0] != nil):
-    addf(p.g.typeInfo, "$1.base = $2;$n",
-         [name, genTypeInfo(p, typ.sons[0].skipTypes(skipPtrs))])
+  p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)])
+  if (typ.kind == tyObject) and (typ.baseClass != nil):
+    p.g.typeInfo.addf("$1.base = $2;$n",
+         [name, genTypeInfo(p, typ.baseClass.skipTypes(skipPtrs))])
 
 proc genTupleFields(p: PProc, typ: PType): Rope =
-  var s: Rope = nil
-  for i in 0 ..< typ.len:
-    if i > 0: add(s, ", \L")
+  var s: Rope = ""
+  for i in 0..<typ.len:
+    if i > 0: s.add(", \L")
     s.addf("{kind: 1, offset: \"Field$1\", len: 0, " &
            "typ: $2, name: \"Field$1\", sons: null}",
-           [i.rope, genTypeInfo(p, typ.sons[i])])
+           [i.rope, genTypeInfo(p, typ[i])])
   result = ("{kind: 2, len: $1, offset: 0, " &
             "typ: null, name: null, sons: [$2]}") % [rope(typ.len), s]
 
@@ -96,61 +97,60 @@ proc genTupleInfo(p: PProc, typ: PType, name: Rope) =
   var s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " &
            "finalizer: null};$n") % [name, rope(ord(typ.kind))]
   prepend(p.g.typeInfo, s)
-  addf(p.g.typeInfo, "var NNI$1 = $2;$n",
+  p.g.typeInfo.addf("var NNI$1 = $2;$n",
        [rope(typ.id), genTupleFields(p, typ)])
-  addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)])
+  p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)])
 
 proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
-  let length = sonsLen(typ.n)
-  var s: Rope = nil
-  for i in countup(0, length - 1):
-    if (typ.n.sons[i].kind != nkSym): internalError(p.config, typ.n.info, "genEnumInfo")
-    let field = typ.n.sons[i].sym
-    if i > 0: add(s, ", \L")
+  var s: Rope = ""
+  for i in 0..<typ.n.len:
+    if (typ.n[i].kind != nkSym): internalError(p.config, typ.n.info, "genEnumInfo")
+    let field = typ.n[i].sym
+    if i > 0: s.add(", \L")
     let extName = if field.ast == nil: field.name.s else: field.ast.strVal
-    addf(s, "\"$1\": {kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}",
+    s.addf("\"$1\": {kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}",
          [rope(field.position), name, makeJSString(extName)])
   var n = ("var NNI$1 = {kind: 2, offset: 0, typ: null, " &
-      "name: null, len: $2, sons: {$3}};$n") % [rope(typ.id), rope(length), s]
+      "name: null, len: $2, sons: {$3}};$n") % [rope(typ.id), rope(typ.n.len), s]
   s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " &
        "finalizer: null};$n") % [name, rope(ord(typ.kind))]
   prepend(p.g.typeInfo, s)
-  add(p.g.typeInfo, n)
-  addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)])
-  if typ.sons[0] != nil:
-    addf(p.g.typeInfo, "$1.base = $2;$n",
-         [name, genTypeInfo(p, typ.sons[0])])
+  p.g.typeInfo.add(n)
+  p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)])
+  if typ.baseClass != nil:
+    p.g.typeInfo.addf("$1.base = $2;$n",
+         [name, genTypeInfo(p, typ.baseClass)])
 
 proc genTypeInfo(p: PProc, typ: PType): Rope =
-  let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink})
+  let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned})
   result = "NTI$1" % [rope(t.id)]
   if containsOrIncl(p.g.typeInfoGenerated, t.id): return
   case t.kind
   of tyDistinct:
-    result = genTypeInfo(p, t.sons[0])
-  of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64:
+    result = genTypeInfo(p, t.skipModifier)
+  of tyPointer, tyProc, tyBool, tyChar, tyCstring, tyString, tyInt..tyUInt64:
     var s =
       "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
       [result, rope(ord(t.kind))]
     prepend(p.g.typeInfo, s)
-  of tyVar, tyLent, tyRef, tyPtr, tySequence, tyRange, tySet:
+  of tyVar, tyLent, tyRef, tyPtr, tySequence, tyRange, tySet, tyOpenArray:
     var s =
-      "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
+      "var $1 = {size: 0, kind: $2, base: null, node: null, finalizer: null};$n" %
               [result, rope(ord(t.kind))]
     prepend(p.g.typeInfo, s)
-    addf(p.g.typeInfo, "$1.base = $2;$n",
-         [result, genTypeInfo(p, t.lastSon)])
+    p.g.typeInfo.addf("$1.base = $2;$n",
+         [result, genTypeInfo(p, t.elementType)])
   of tyArray:
     var s =
-      "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
+      "var $1 = {size: 0, kind: $2, base: null, node: null, finalizer: null};$n" %
               [result, rope(ord(t.kind))]
     prepend(p.g.typeInfo, s)
-    addf(p.g.typeInfo, "$1.base = $2;$n",
-         [result, genTypeInfo(p, t.sons[1])])
+    p.g.typeInfo.addf("$1.base = $2;$n",
+         [result, genTypeInfo(p, t.elementType)])
   of tyEnum: genEnumInfo(p, t, result)
   of tyObject: genObjectInfo(p, t, result)
   of tyTuple: genTupleInfo(p, t, result)
   of tyStatic:
-    if t.n != nil: result = genTypeInfo(p, lastSon t)
+    if t.n != nil: result = genTypeInfo(p, skipModifier t)
     else: internalError(p.config, "genTypeInfo(" & $t.kind & ')')
   else: internalError(p.config, "genTypeInfo(" & $t.kind & ')')
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 0a4801150..54cdfc5bc 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -10,8 +10,14 @@
 # This file implements lambda lifting for the transformator.
 
 import
-  intsets, strutils, options, ast, astalgo, trees, treetab, msgs,
-  idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos
+  options, ast, astalgo, msgs,
+  idents, renderer, types, magicsys, lowerings, modulegraphs, lineinfos,
+  transf, liftdestructors, typeallowed
+
+import std/[strutils, tables, intsets]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 discard """
   The basic approach is that captured vars need to be put on the heap and
@@ -125,78 +131,78 @@ proc newCall(a: PSym, b: PNode): PNode =
   result.add newSymNode(a)
   result.add b
 
-proc createClosureIterStateType*(g: ModuleGraph; iter: PSym): PType =
+proc createClosureIterStateType*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PType =
   var n = newNodeI(nkRange, iter.info)
-  addSon(n, newIntNode(nkIntLit, -1))
-  addSon(n, newIntNode(nkIntLit, 0))
-  result = newType(tyRange, iter)
+  n.add newIntNode(nkIntLit, -1)
+  n.add newIntNode(nkIntLit, 0)
+  result = newType(tyRange, idgen, iter)
   result.n = n
   var intType = nilOrSysInt(g)
-  if intType.isNil: intType = newType(tyInt, iter)
+  if intType.isNil: intType = newType(tyInt, idgen, iter)
   rawAddSon(result, intType)
 
-proc createStateField(g: ModuleGraph; iter: PSym): PSym =
-  result = newSym(skField, getIdent(g.cache, ":state"), iter, iter.info)
-  result.typ = createClosureIterStateType(g, iter)
+proc createStateField(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
+  result = newSym(skField, getIdent(g.cache, ":state"), idgen, iter, iter.info)
+  result.typ = createClosureIterStateType(g, iter, idgen)
+
+template isIterator*(owner: PSym): bool =
+  owner.kind == skIterator and owner.typ.callConv == ccClosure
 
-proc createEnvObj(g: ModuleGraph; owner: PSym; info: TLineInfo): PType =
-  # YYY meh, just add the state field for every closure for now, it's too
-  # hard to figure out if it comes from a closure iterator:
-  result = createObj(g, owner, info, final=false)
-  rawAddField(result, createStateField(g, owner))
+proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType =
+  result = createObj(g, idgen, owner, info, final=false)
+  if owner.isIterator or not isDefined(g.config, "nimOptIters"):
+    rawAddField(result, createStateField(g, owner, idgen))
 
-proc getClosureIterResult*(g: ModuleGraph; iter: PSym): PSym =
+proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
   if resultPos < iter.ast.len:
-    result = iter.ast.sons[resultPos].sym
+    result = iter.ast[resultPos].sym
   else:
     # XXX a bit hacky:
-    result = newSym(skResult, getIdent(g.cache, ":result"), iter, iter.info, {})
-    result.typ = iter.typ.sons[0]
+    result = newSym(skResult, getIdent(g.cache, ":result"), idgen, iter, iter.info, {})
+    result.typ = iter.typ.returnType
     incl(result.flags, sfUsed)
     iter.ast.add newSymNode(result)
 
 proc addHiddenParam(routine: PSym, param: PSym) =
   assert param.kind == skParam
-  var params = routine.ast.sons[paramsPos]
+  var params = routine.ast[paramsPos]
   # -1 is correct here as param.position is 0 based but we have at position 0
   # some nkEffect node:
   param.position = routine.typ.n.len-1
-  addSon(params, newSymNode(param))
+  params.add newSymNode(param)
   #incl(routine.typ.flags, tfCapturesEnv)
   assert sfFromGeneric in param.flags
   #echo "produced environment: ", param.id, " for ", routine.id
 
-proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
-  let params = routine.ast.sons[paramsPos]
+proc getEnvParam*(routine: PSym): PSym =
+  let params = routine.ast[paramsPos]
   let hidden = lastSon(params)
   if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName:
     result = hidden.sym
     assert sfFromGeneric in result.flags
   else:
+    result = nil
+
+proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
+  result = getEnvParam(routine)
+  if result.isNil:
     # writeStackTrace()
     localError(g.config, routine.info, "internal error: could not find env param for " & routine.name.s)
     result = routine
 
-proc getEnvParam*(routine: PSym): PSym =
-  let params = routine.ast.sons[paramsPos]
-  let hidden = lastSon(params)
-  if hidden.kind == nkSym and hidden.sym.name.s == paramName:
-    result = hidden.sym
-    assert sfFromGeneric in result.flags
-
 proc interestingVar(s: PSym): bool {.inline.} =
   result = s.kind in {skVar, skLet, skTemp, skForVar, skParam, skResult} and
     sfGlobal notin s.flags and
     s.typ.kind notin {tyStatic, tyTypeDesc}
 
 proc illegalCapture(s: PSym): bool {.inline.} =
-  result = skipTypes(s.typ, abstractInst).kind in
-                   {tyVar, tyOpenArray, tyVarargs, tyLent} or
-      s.kind == skResult
+  result = classifyViewType(s.typ) != noView or s.kind == skResult
 
 proc isInnerProc(s: PSym): bool =
   if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone:
     result = s.skipGenericOwner.kind in routineKinds
+  else:
+    result = false
 
 proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
   # Bugfix: unfortunately we cannot use 'nkFastAsgn' here as that would
@@ -205,10 +211,10 @@ proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
   # which is however the only case when we generate an assignment in the first
   # place.
   result = newNodeI(nkAsgn, info, 2)
-  result.sons[0] = le
-  result.sons[1] = ri
+  result[0] = le
+  result[1] = ri
 
-proc makeClosure*(g: ModuleGraph; prc: PSym; env: PNode; info: TLineInfo): PNode =
+proc makeClosure*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; env: PNode; info: TLineInfo): PNode =
   result = newNodeIT(nkClosure, info, prc.typ)
   result.add(newSymNode(prc))
   if env == nil:
@@ -217,37 +223,52 @@ proc makeClosure*(g: ModuleGraph; prc: PSym; env: PNode; info: TLineInfo): PNode
     if env.skipConv.kind == nkClosure:
       localError(g.config, info, "internal error: taking closure of closure")
     result.add(env)
+  #if isClosureIterator(result.typ):
+  createTypeBoundOps(g, nil, result.typ, info, idgen)
+  if tfHasAsgn in result.typ.flags or optSeqDestructors in g.config.globalOptions:
+    prc.flags.incl sfInjectDestructors
 
 proc interestingIterVar(s: PSym): bool {.inline.} =
+  # unused with -d:nimOptIters
   # XXX optimization: Only lift the variable if it lives across
   # yield/return boundaries! This can potentially speed up
   # closure iterators quite a bit.
-  result = s.kind in {skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
+  result = s.kind in {skResult, skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
 
-template isIterator*(owner: PSym): bool =
-  owner.kind == skIterator and owner.typ.callConv == ccClosure
-
-proc liftingHarmful(conf: ConfigRef; owner: PSym): bool {.inline.} =
+template liftingHarmful(conf: ConfigRef; owner: PSym): bool =
   ## lambda lifting can be harmful for JS-like code generators.
   let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro
-  result = conf.cmd == cmdCompileToJS and not isCompileTime
-
-proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
+  jsNoLambdaLifting in conf.legacyFeatures and conf.backend == backendJs and not isCompileTime
+
+proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen: IdGenerator; owner: PSym) =
+  if owner.kind != skMacro:
+    createTypeBoundOps(g, nil, refType.elementType, info, idgen)
+    createTypeBoundOps(g, nil, refType, info, idgen)
+    if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions:
+      owner.flags.incl sfInjectDestructors
+
+proc genCreateEnv(env: PNode): PNode =
+  var c = newNodeIT(nkObjConstr, env.info, env.typ)
+  c.add newNodeIT(nkType, env.info, env.typ)
+  let e = copyTree(env)
+  e.flags.incl nfFirstWrite
+  result = newAsgnStmt(e, c)
+
+proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
   # transforms  (iter)  to  (let env = newClosure[iter](); (iter, env))
   if liftingHarmful(g.config, owner): return n
   let iter = n.sym
   assert iter.isIterator
 
-  result = newNodeIT(nkStmtListExpr, n.info, n.typ)
-
+  result = newNodeIT(nkStmtListExpr, n.info, iter.typ)
   let hp = getHiddenParam(g, iter)
   var env: PNode
   if owner.isIterator:
     let it = getHiddenParam(g, owner)
-    addUniqueField(it.typ.sons[0], hp, g.cache)
+    addUniqueField(it.typ.skipTypes({tyOwned})[0], hp, g.cache, idgen)
     env = indirectAccess(newSymNode(it), hp, hp.info)
   else:
-    let e = newSym(skLet, iter.name, owner, n.info)
+    let e = newSym(skLet, iter.name, idgen, owner, n.info)
     e.typ = hp.typ
     e.flags = hp.flags
     env = newSymNode(e)
@@ -255,49 +276,53 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
     addVar(v, env)
     result.add(v)
   # add 'new' statement:
-  result.add newCall(getSysSym(g, n.info, "internalNew"), env)
-  result.add makeClosure(g, iter, env, n.info)
+  #result.add newCall(getSysSym(g, n.info, "internalNew"), env)
+  result.add genCreateEnv(env)
+  createTypeBoundOpsLL(g, env.typ, n.info, idgen, owner)
+  result.add makeClosure(g, idgen, iter, env, n.info)
 
-proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode =
+proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: PSym): PNode =
+  # unused with -d:nimOptIters
   let envParam = getHiddenParam(g, owner)
-  let obj = envParam.typ.lastSon
-  addField(obj, s, g.cache)
+  let obj = envParam.typ.skipTypes({tyOwned, tyRef, tyPtr})
+  let field = addField(obj, s, g.cache, idgen)
 
   var access = newSymNode(envParam)
   assert obj.kind == tyObject
-  let field = getFieldFromObj(obj, s)
-  if field != nil:
-    result = rawIndirectAccess(access, field, s.info)
-  else:
-    localError(g.config, s.info, "internal error: cannot generate fresh variable")
-    result = access
+  result = rawIndirectAccess(access, field, s.info)
 
 # ------------------ new stuff -------------------------------------------
 
 proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
   let s = n.sym
+  let isEnv = s.name.id == getIdent(g.cache, ":env").id
   if illegalCapture(s):
-    localError(g.config, n.info, "illegal capture '$1' of type <$2> which is declared here: $3" %
-      [s.name.s, typeToString(s.typ), g.config$s.info])
-  elif owner.typ.callConv notin {ccClosure, ccDefault}:
+    localError(g.config, n.info,
+      ("'$1' is of type <$2> which cannot be captured as it would violate memory" &
+       " safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." &
+       " Consider using a <ref T> which can be captured.") %
+      [s.name.s, typeToString(s.typ.skipTypes({tyVar})), g.config$s.info])
+  elif not (owner.typ.isClosure or owner.isNimcall and not owner.isExplicitCallConv or isEnv):
     localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
-      [s.name.s, owner.name.s, CallingConvToStr[owner.typ.callConv]])
+      [s.name.s, owner.name.s, $owner.typ.callConv])
   incl(owner.typ.flags, tfCapturesEnv)
-  owner.typ.callConv = ccClosure
+  if not isEnv:
+    owner.typ.callConv = ccClosure
 
 type
   DetectionPass = object
     processed, capturedVars: IntSet
     ownerToType: Table[int, PType]
     somethingToDo: bool
+    inTypeOf: bool
     graph: ModuleGraph
+    idgen: IdGenerator
 
-proc initDetectionPass(g: ModuleGraph; fn: PSym): DetectionPass =
-  result.processed = initIntSet()
-  result.capturedVars = initIntSet()
-  result.ownerToType = initTable[int, PType]()
-  result.processed.incl(fn.id)
-  result.graph = g
+proc initDetectionPass(g: ModuleGraph; fn: PSym; idgen: IdGenerator): DetectionPass =
+  result = DetectionPass(processed: toIntSet([fn.id]),
+    capturedVars: initIntSet(), ownerToType: initTable[int, PType](),
+    graph: g, idgen: idgen
+  )
 
 discard """
 proc outer =
@@ -313,26 +338,60 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
                         info: TLineInfo): PType =
   result = c.ownerToType.getOrDefault(owner.id)
   if result.isNil:
-    result = newType(tyRef, owner)
-    let obj = createEnvObj(c.graph, owner, info)
-    rawAddSon(result, obj)
+    let env = getEnvParam(owner)
+    if env.isNil or not owner.isIterator or not isDefined(c.graph.config, "nimOptIters"):
+      result = newType(tyRef, c.idgen, owner)
+      let obj = createEnvObj(c.graph, c.idgen, owner, info)
+      rawAddSon(result, obj)
+    else:
+      result = env.typ
     c.ownerToType[owner.id] = result
 
+proc asOwnedRef(c: var DetectionPass; t: PType): PType =
+  if optOwnedRefs in c.graph.config.globalOptions:
+    assert t.kind == tyRef
+    result = newType(tyOwned, c.idgen, t.owner)
+    result.flags.incl tfHasOwned
+    result.rawAddSon t
+  else:
+    result = t
+
+proc getEnvTypeForOwnerUp(c: var DetectionPass; owner: PSym;
+                          info: TLineInfo): PType =
+  var r = c.getEnvTypeForOwner(owner, info)
+  result = newType(tyPtr, c.idgen, owner)
+  rawAddSon(result, r.skipTypes({tyOwned, tyRef, tyPtr}))
+
 proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
   let refObj = c.getEnvTypeForOwner(dest, info) # getHiddenParam(dest).typ
-  let obj = refObj.lastSon
-  let fieldType = c.getEnvTypeForOwner(dep, info) #getHiddenParam(dep).typ
+  let obj = refObj.skipTypes({tyOwned, tyRef, tyPtr})
+  # The assumption here is that gcDestructors means we cannot deal
+  # with cycles properly, so it's better to produce a weak ref (=ptr) here.
+  # This seems to be generally correct but since it's a bit risky it's disabled
+  # for now.
+  # XXX This is wrong for the 'hamming' test, so remove this logic again.
+  let fieldType = if isDefined(c.graph.config, "nimCycleBreaker"):
+                    c.getEnvTypeForOwnerUp(dep, info) #getHiddenParam(dep).typ
+                  else:
+                    c.getEnvTypeForOwner(dep, info)
   if refObj == fieldType:
     localError(c.graph.config, dep.info, "internal error: invalid up reference computed")
 
   let upIdent = getIdent(c.graph.cache, upName)
   let upField = lookupInRecord(obj.n, upIdent)
   if upField != nil:
-    if upField.typ != fieldType:
+    if upField.typ.skipTypes({tyOwned, tyRef, tyPtr}) != fieldType.skipTypes({tyOwned, tyRef, tyPtr}):
       localError(c.graph.config, dep.info, "internal error: up references do not agree")
+
+    when false:
+      if c.graph.config.selectedGC == gcDestructors and sfCursor notin upField.flags:
+        localError(c.graph.config, dep.info, "internal error: up reference is not a .cursor")
   else:
-    let result = newSym(skField, upIdent, obj.owner, obj.owner.info)
+    let result = newSym(skField, upIdent, c.idgen, obj.owner, obj.owner.info)
     result.typ = fieldType
+    when false:
+      if c.graph.config.selectedGC == gcDestructors:
+        result.flags.incl sfCursor
     rawAddField(obj, result)
 
 discard """
@@ -361,12 +420,15 @@ Consider:
 
 """
 
+proc isTypeOf(n: PNode): bool =
+  n.kind == nkSym and n.sym.magic in {mTypeOf, mType}
+
 proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
   var cp = getEnvParam(fn)
   let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner
   let t = c.getEnvTypeForOwner(owner, info)
   if cp == nil:
-    cp = newSym(skParam, getIdent(c.graph.cache, paramName), fn, fn.info)
+    cp = newSym(skParam, getIdent(c.graph.cache, paramName), c.idgen, fn, fn.info)
     incl(cp.flags, sfFromGeneric)
     cp.typ = t
     addHiddenParam(fn, cp)
@@ -389,26 +451,28 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
     if innerProc:
       if s.isIterator: c.somethingToDo = true
       if not c.processed.containsOrIncl(s.id):
-        detectCapturedVars(s.getBody, s, c)
+        let body = transformBody(c.graph, c.idgen, s, {useCache})
+        detectCapturedVars(body, s, c)
     let ow = s.skipGenericOwner
+    let innerClosure = innerProc and s.typ.callConv == ccClosure and not s.isIterator
+    let interested = interestingVar(s)
     if ow == owner:
       if owner.isIterator:
         c.somethingToDo = true
         addClosureParam(c, owner, n.info)
-        if interestingIterVar(s):
-          if not c.capturedVars.containsOrIncl(s.id):
-            let obj = getHiddenParam(c.graph, owner).typ.lastSon
-            #let obj = c.getEnvTypeForOwner(s.owner).lastSon
+        if not isDefined(c.graph.config, "nimOptIters") and interestingIterVar(s):
+          if not c.capturedVars.contains(s.id):
+            if not c.inTypeOf: c.capturedVars.incl(s.id)
+            let obj = getHiddenParam(c.graph, owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
+            #let obj = c.getEnvTypeForOwner(s.owner).skipTypes({tyOwned, tyRef, tyPtr})
 
             if s.name.id == getIdent(c.graph.cache, ":state").id:
-              obj.n[0].sym.id = -s.id
+              obj.n[0].sym.flags.incl sfNoInit
+              obj.n[0].sym.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
             else:
-              addField(obj, s, c.graph.cache)
-      # but always return because the rest of the proc is only relevant when
-      # ow != owner:
-      return
+              discard addField(obj, s, c.graph.cache, c.idgen)
     # direct or indirect dependency:
-    if (innerProc and s.typ.callConv == ccClosure) or interestingVar(s):
+    elif innerClosure or interested:
       discard """
         proc outer() =
           var x: int
@@ -425,10 +489,12 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
       addClosureParam(c, owner, n.info)
       #echo "capturing ", n.info
       # variable 's' is actually captured:
-      if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id):
-        let obj = c.getEnvTypeForOwner(ow, n.info).lastSon
-        #getHiddenParam(owner).typ.lastSon
-        addField(obj, s, c.graph.cache)
+      if interestingVar(s):
+        if not c.capturedVars.contains(s.id):
+          if not c.inTypeOf: c.capturedVars.incl(s.id)
+          let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr})
+          #getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
+          discard addField(obj, s, c.graph.cache, c.idgen)
       # create required upFields:
       var w = owner.skipGenericOwner
       if isInnerProc(w) or owner.isIterator:
@@ -451,27 +517,34 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
           createUpField(c, w, up, n.info)
           w = up
   of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit,
-     nkTemplateDef, nkTypeSection:
-    discard
-  of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef:
+     nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef,
+     nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt,
+     nkTypeOfExpr, nkMixinStmt, nkBindStmt:
     discard
-  of nkLambdaKinds, nkIteratorDef, nkFuncDef:
+  of nkLambdaKinds, nkIteratorDef:
     if n.typ != nil:
       detectCapturedVars(n[namePos], owner, c)
+  of nkReturnStmt:
+    detectCapturedVars(n[0], owner, c)
+  of nkIdentDefs:
+    detectCapturedVars(n[^1], owner, c)
   else:
+    if n.isCallExpr and n[0].isTypeOf:
+      c.inTypeOf = true
     for i in 0..<n.len:
       detectCapturedVars(n[i], owner, c)
+    c.inTypeOf = false
 
 type
   LiftingPass = object
     processed: IntSet
     envVars: Table[int, PNode]
     inContainer: int
+    unownedEnvVars: Table[int, PNode] # only required for --newruntime
 
 proc initLiftingPass(fn: PSym): LiftingPass =
-  result.processed = initIntSet()
-  result.processed.incl(fn.id)
-  result.envVars = initTable[int, PNode]()
+  result = LiftingPass(processed: toIntSet([fn.id]),
+                envVars: initTable[int, PNode]())
 
 proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   let s = n.sym
@@ -479,8 +552,8 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   let envParam = getHiddenParam(g, owner)
   if not envParam.isNil:
     var access = newSymNode(envParam)
+    var obj = access.typ.elementType
     while true:
-      let obj = access.typ.sons[0]
       assert obj.kind == tyObject
       let field = getFieldFromObj(obj, s)
       if field != nil:
@@ -488,59 +561,79 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
       let upField = lookupInRecord(obj.n, getIdent(g.cache, upName))
       if upField == nil: break
       access = rawIndirectAccess(access, upField, n.info)
+      obj = access.typ.baseClass
   localError(g.config, n.info, "internal error: environment misses: " & s.name.s)
   result = n
 
-proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType): PNode =
-  var v = newSym(skVar, getIdent(cache, envName), owner, owner.info)
-  incl(v.flags, sfShadowed)
+proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType; info: TLineInfo; idgen: IdGenerator): PNode =
+  var v = newSym(skVar, getIdent(cache, envName), idgen, owner, info)
+  v.flags = {sfShadowed, sfGeneratedOp}
   v.typ = typ
   result = newSymNode(v)
   when false:
     if owner.kind == skIterator and owner.typ.callConv == ccClosure:
       let it = getHiddenParam(owner)
-      addUniqueField(it.typ.sons[0], v)
+      addUniqueField(it.typ.elementType, v)
       result = indirectAccess(newSymNode(it), v, v.info)
     else:
       result = newSymNode(v)
 
-proc setupEnvVar(owner: PSym; d: DetectionPass;
-                 c: var LiftingPass): PNode =
+proc setupEnvVar(owner: PSym; d: var DetectionPass;
+                 c: var LiftingPass; info: TLineInfo): PNode =
   if owner.isIterator:
     return getHiddenParam(d.graph, owner).newSymNode
-  result = c.envvars.getOrDefault(owner.id)
+  result = c.envVars.getOrDefault(owner.id)
   if result.isNil:
     let envVarType = d.ownerToType.getOrDefault(owner.id)
     if envVarType.isNil:
       localError d.graph.config, owner.info, "internal error: could not determine closure type"
-    result = newEnvVar(d.graph.cache, owner, envVarType)
+    result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType), info, d.idgen)
     c.envVars[owner.id] = result
+    if optOwnedRefs in d.graph.config.globalOptions:
+      var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), d.idgen, owner, info)
+      v.flags = {sfShadowed, sfGeneratedOp}
+      v.typ = envVarType
+      c.unownedEnvVars[owner.id] = newSymNode(v)
 
 proc getUpViaParam(g: ModuleGraph; owner: PSym): PNode =
   let p = getHiddenParam(g, owner)
   result = p.newSymNode
   if owner.isIterator:
-    let upField = lookupInRecord(p.typ.lastSon.n, getIdent(g.cache, upName))
+    let upField = lookupInRecord(p.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(g.cache, upName))
     if upField == nil:
       localError(g.config, owner.info, "could not find up reference for closure iter")
     else:
       result = rawIndirectAccess(result, upField, p.info)
 
 proc rawClosureCreation(owner: PSym;
-                        d: DetectionPass; c: var LiftingPass): PNode =
+                        d: var DetectionPass; c: var LiftingPass;
+                        info: TLineInfo): PNode =
   result = newNodeI(nkStmtList, owner.info)
 
   var env: PNode
   if owner.isIterator:
     env = getHiddenParam(d.graph, owner).newSymNode
   else:
-    env = setupEnvVar(owner, d, c)
+    env = setupEnvVar(owner, d, c, info)
     if env.kind == nkSym:
       var v = newNodeI(nkVarSection, env.info)
       addVar(v, env)
       result.add(v)
+      if optOwnedRefs in d.graph.config.globalOptions:
+        let unowned = c.unownedEnvVars[owner.id]
+        assert unowned != nil
+        addVar(v, unowned)
+
     # add 'new' statement:
-    result.add(newCall(getSysSym(d.graph, env.info, "internalNew"), env))
+    result.add genCreateEnv(env)
+    if optOwnedRefs in d.graph.config.globalOptions:
+      let unowned = c.unownedEnvVars[owner.id]
+      assert unowned != nil
+      let env2 = copyTree(env)
+      env2.typ = unowned.typ
+      result.add newAsgnStmt(unowned, env2, env.info)
+      createTypeBoundOpsLL(d.graph, unowned.typ, env.info, d.idgen, owner)
+
     # add assignment statements for captured parameters:
     for i in 1..<owner.typ.n.len:
       let local = owner.typ.n[i].sym
@@ -548,11 +641,15 @@ proc rawClosureCreation(owner: PSym;
         let fieldAccess = indirectAccess(env, local, env.info)
         # add ``env.param = param``
         result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info))
+        if owner.kind != skMacro:
+          createTypeBoundOps(d.graph, nil, fieldAccess.typ, env.info, d.idgen)
+        if tfHasAsgn in fieldAccess.typ.flags or optSeqDestructors in d.graph.config.globalOptions:
+          owner.flags.incl sfInjectDestructors
 
-  let upField = lookupInRecord(env.typ.lastSon.n, getIdent(d.graph.cache, upName))
+  let upField = lookupInRecord(env.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName))
   if upField != nil:
     let up = getUpViaParam(d.graph, owner)
-    if up != nil and upField.typ == up.typ:
+    if up != nil and upField.typ.skipTypes({tyOwned, tyRef, tyPtr}) == up.typ.skipTypes({tyOwned, tyRef, tyPtr}):
       result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info),
                  up, env.info))
     #elif oldenv != nil and oldenv.typ == upField.typ:
@@ -560,40 +657,55 @@ proc rawClosureCreation(owner: PSym;
     #             oldenv, env.info))
     else:
       localError(d.graph.config, env.info, "internal error: cannot create up reference")
+  # we are not in the sem'check phase anymore! so pass 'nil' for the PContext
+  # and hope for the best:
+  createTypeBoundOpsLL(d.graph, env.typ, owner.info, d.idgen, owner)
+
+proc finishClosureCreation(owner: PSym; d: var DetectionPass; c: LiftingPass;
+                           info: TLineInfo; res: PNode) =
+  if optOwnedRefs in d.graph.config.globalOptions:
+    let unowned = c.unownedEnvVars[owner.id]
+    assert unowned != nil
+    let nilLit = newNodeIT(nkNilLit, info, unowned.typ)
+    res.add newAsgnStmt(unowned, nilLit, info)
+    createTypeBoundOpsLL(d.graph, unowned.typ, info, d.idgen, owner)
 
 proc closureCreationForIter(iter: PNode;
-                            d: DetectionPass; c: var LiftingPass): PNode =
+                            d: var DetectionPass; c: var LiftingPass): PNode =
   result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ)
   let owner = iter.sym.skipGenericOwner
-  var v = newSym(skVar, getIdent(d.graph.cache, envName), owner, iter.info)
+  var v = newSym(skVar, getIdent(d.graph.cache, envName), d.idgen, owner, iter.info)
   incl(v.flags, sfShadowed)
-  v.typ = getHiddenParam(d.graph, iter.sym).typ
+  v.typ = asOwnedRef(d, getHiddenParam(d.graph, iter.sym).typ)
   var vnode: PNode
   if owner.isIterator:
     let it = getHiddenParam(d.graph, owner)
-    addUniqueField(it.typ.sons[0], v, d.graph.cache)
+    addUniqueField(it.typ.skipTypes({tyOwned, tyRef, tyPtr}), v, d.graph.cache, d.idgen)
     vnode = indirectAccess(newSymNode(it), v, v.info)
   else:
     vnode = v.newSymNode
     var vs = newNodeI(nkVarSection, iter.info)
     addVar(vs, vnode)
     result.add(vs)
-  result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode))
+  result.add genCreateEnv(vnode)
+  createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, owner)
 
-  let upField = lookupInRecord(v.typ.lastSon.n, getIdent(d.graph.cache, upName))
+  let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName))
   if upField != nil:
-    let u = setupEnvVar(owner, d, c)
-    if u.typ == upField.typ:
+    let u = setupEnvVar(owner, d, c, iter.info)
+    if u.typ.skipTypes({tyOwned, tyRef, tyPtr}) == upField.typ.skipTypes({tyOwned, tyRef, tyPtr}):
       result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info),
                  u, iter.info))
     else:
       localError(d.graph.config, iter.info, "internal error: cannot create up reference for iter")
-  result.add makeClosure(d.graph, iter.sym, vnode, iter.info)
+  result.add makeClosure(d.graph, d.idgen, iter.sym, vnode, iter.info)
 
-proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
+proc accessViaEnvVar(n: PNode; owner: PSym; d: var DetectionPass;
                      c: var LiftingPass): PNode =
-  let access = setupEnvVar(owner, d, c)
-  let obj = access.typ.sons[0]
+  var access = setupEnvVar(owner, d, c, n.info)
+  if optOwnedRefs in d.graph.config.globalOptions:
+    access = c.unownedEnvVars[owner.id]
+  let obj = access.typ.skipTypes({tyOwned, tyRef, tyPtr})
   let field = getFieldFromObj(obj, n.sym)
   if field != nil:
     result = rawIndirectAccess(access, field, n.info)
@@ -602,39 +714,40 @@ proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
     result = n
 
 proc getStateField*(g: ModuleGraph; owner: PSym): PSym =
-  getHiddenParam(g, owner).typ.sons[0].n.sons[0].sym
+  getHiddenParam(g, owner).typ.skipTypes({tyOwned, tyRef, tyPtr}).n[0].sym
 
-proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
+proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
                       c: var LiftingPass): PNode
 
-proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
+proc symToClosure(n: PNode; owner: PSym; d: var DetectionPass;
                   c: var LiftingPass): PNode =
   let s = n.sym
   if s == owner:
     # recursive calls go through (lambda, hiddenParam):
     let available = getHiddenParam(d.graph, owner)
-    result = makeClosure(d.graph, s, available.newSymNode, n.info)
+    result = makeClosure(d.graph, d.idgen, s, available.newSymNode, n.info)
   elif s.isIterator:
     result = closureCreationForIter(n, d, c)
   elif s.skipGenericOwner == owner:
     # direct dependency, so use the outer's env variable:
-    result = makeClosure(d.graph, s, setupEnvVar(owner, d, c), n.info)
+    result = makeClosure(d.graph, d.idgen, s, setupEnvVar(owner, d, c, n.info), n.info)
   else:
+    result = nil
     let available = getHiddenParam(d.graph, owner)
     let wanted = getHiddenParam(d.graph, s).typ
     # ugh: call through some other inner proc;
     var access = newSymNode(available)
     while true:
       if access.typ == wanted:
-        return makeClosure(d.graph, s, access, n.info)
-      let obj = access.typ.sons[0]
+        return makeClosure(d.graph, d.idgen, s, access, n.info)
+      let obj = access.typ.skipTypes({tyOwned, tyRef, tyPtr})
       let upField = lookupInRecord(obj.n, getIdent(d.graph.cache, upName))
       if upField == nil:
         localError(d.graph.config, n.info, "internal error: no environment found")
         return n
       access = rawIndirectAccess(access, upField, n.info)
 
-proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
+proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
                       c: var LiftingPass): PNode =
   result = n
   case n.kind
@@ -646,36 +759,39 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
         #  echo renderTree(s.getBody, {renderIds})
         let oldInContainer = c.inContainer
         c.inContainer = 0
-        var body = liftCapturedVars(s.getBody, s, d, c)
-        if c.envvars.getOrDefault(s.id).isNil:
-          s.ast.sons[bodyPos] = body
+        var body = transformBody(d.graph, d.idgen, s, {})
+        body = liftCapturedVars(body, s, d, c)
+        if c.envVars.getOrDefault(s.id).isNil:
+          s.transformedBody = body
         else:
-          s.ast.sons[bodyPos] = newTree(nkStmtList, rawClosureCreation(s, d, c), body)
+          s.transformedBody = newTree(nkStmtList, rawClosureCreation(s, d, c, n.info), body)
+          finishClosureCreation(s, d, c, n.info, s.transformedBody)
         c.inContainer = oldInContainer
+
       if s.typ.callConv == ccClosure:
         result = symToClosure(n, owner, d, c)
+
     elif s.id in d.capturedVars:
       if s.owner != owner:
         result = accessViaEnvParam(d.graph, n, owner)
-      elif owner.isIterator and interestingIterVar(s):
+      elif owner.isIterator and not isDefined(d.graph.config, "nimOptIters") and interestingIterVar(s):
         result = accessViaEnvParam(d.graph, n, owner)
       else:
         result = accessViaEnvVar(n, owner, d, c)
   of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom,
-     nkTemplateDef, nkTypeSection:
-    discard
-  of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef:
+     nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef, nkConverterDef,
+     nkMacroDef, nkFuncDef, nkMixinStmt, nkBindStmt:
     discard
   of nkClosure:
     if n[1].kind == nkNilLit:
-      n.sons[0] = liftCapturedVars(n[0], owner, d, c)
-      let x = n.sons[0].skipConv
+      n[0] = liftCapturedVars(n[0], owner, d, c)
+      let x = n[0].skipConv
       if x.kind == nkClosure:
         #localError(n.info, "internal error: closure to closure created")
         # now we know better, so patch it:
-        n.sons[0] = x.sons[0]
-        n.sons[1] = x.sons[1]
-  of nkLambdaKinds, nkIteratorDef, nkFuncDef:
+        n[0] = x[0]
+        n[1] = x[1]
+  of nkLambdaKinds, nkIteratorDef:
     if n.typ != nil and n[namePos].kind == nkSym:
       let oldInContainer = c.inContainer
       c.inContainer = 0
@@ -685,71 +801,103 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
       c.inContainer = oldInContainer
   of nkHiddenStdConv:
     if n.len == 2:
-      n.sons[1] = liftCapturedVars(n[1], owner, d, c)
+      n[1] = liftCapturedVars(n[1], owner, d, c)
       if n[1].kind == nkClosure: result = n[1]
+  of nkReturnStmt:
+    if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}:
+      # we have a `result = result` expression produced by the closure
+      # transform, let's not touch the LHS in order to make the lifting pass
+      # correct when `result` is lifted
+      n[0][1] = liftCapturedVars(n[0][1], owner, d, c)
+    else:
+      n[0] = liftCapturedVars(n[0], owner, d, c)
+  of nkTypeOfExpr:
+    result = n
   else:
+    if n.isCallExpr and n[0].isTypeOf:
+      return
     if owner.isIterator:
       if nfLL in n.flags:
         # special case 'when nimVm' due to bug #3636:
-        n.sons[1] = liftCapturedVars(n[1], owner, d, c)
+        n[1] = liftCapturedVars(n[1], owner, d, c)
         return
 
     let inContainer = n.kind in {nkObjConstr, nkBracket}
     if inContainer: inc c.inContainer
     for i in 0..<n.len:
-      n.sons[i] = liftCapturedVars(n[i], owner, d, c)
+      n[i] = liftCapturedVars(n[i], owner, d, c)
     if inContainer: dec c.inContainer
 
 # ------------------ old stuff -------------------------------------------
 
 proc semCaptureSym*(s, owner: PSym) =
+  discard """
+    proc outer() =
+      var x: int
+      proc inner() =
+        proc innerInner() =
+          echo x
+        innerInner()
+      inner()
+    # inner() takes a closure too!
+  """
+  proc propagateClosure(start, last: PSym) =
+    var o = start
+    while o != nil and o.kind != skModule:
+      if o == last: break
+      o.typ.callConv = ccClosure
+      o = o.skipGenericOwner
+
   if interestingVar(s) and s.kind != skResult:
     if owner.typ != nil and not isGenericRoutine(owner):
       # XXX: is this really safe?
       # if we capture a var from another generic routine,
       # it won't be consider captured.
       var o = owner.skipGenericOwner
-      while o.kind != skModule and o != nil:
+      while o != nil and o.kind != skModule:
         if s.owner == o:
-          if owner.typ.callConv in {ccClosure, ccDefault} or owner.kind == skIterator:
+          if owner.typ.callConv == ccClosure or owner.kind == skIterator or
+             owner.typ.callConv == ccNimCall and tfExplicitCallConv notin owner.typ.flags:
             owner.typ.callConv = ccClosure
+            propagateClosure(owner.skipGenericOwner, s.owner)
           else:
             discard "do not produce an error here, but later"
-          #echo "computing .closure for ", owner.name.s, " ", owner.info, " because of ", s.name.s
+          #echo "computing .closure for ", owner.name.s, " because of ", s.name.s
         o = o.skipGenericOwner
     # since the analysis is not entirely correct, we don't set 'tfCapturesEnv'
     # here
 
-proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType): PNode =
-  var d = initDetectionPass(g, fn)
+proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType;
+                     idgen: IdGenerator): PNode =
+  var d = initDetectionPass(g, fn, idgen)
   var c = initLiftingPass(fn)
   # pretend 'fn' is a closure iterator for the analysis:
   let oldKind = fn.kind
   let oldCC = fn.typ.callConv
-  fn.kind = skIterator
+  fn.transitionRoutineSymKind(skIterator)
   fn.typ.callConv = ccClosure
   d.ownerToType[fn.id] = ptrType
   detectCapturedVars(body, fn, d)
   result = liftCapturedVars(body, fn, d, c)
-  fn.kind = oldKind
+  fn.transitionRoutineSymKind(oldKind)
   fn.typ.callConv = oldCC
 
-proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PNode =
-  # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs
-  # the transformation even when compiling to JS ...
-
-  # However we can do lifting for the stuff which is *only* compiletime.
+proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool;
+                  idgen: IdGenerator; flags: TransformFlags): PNode =
   let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro
 
-  if body.kind == nkEmpty or (
-      g.config.cmd == cmdCompileToJS and not isCompileTime) or
-      fn.skipGenericOwner.kind != skModule:
+  if body.kind == nkEmpty or (jsNoLambdaLifting in g.config.legacyFeatures and
+      g.config.backend == backendJs and not isCompileTime) or
+      (fn.skipGenericOwner.kind != skModule and force notin flags):
 
     # ignore forward declaration:
     result = body
     tooEarly = true
+    if fn.isIterator and isDefined(g.config, "nimOptIters"):
+      var d = initDetectionPass(g, fn, idgen)
+      addClosureParam(d, fn, body.info)
   else:
-    var d = initDetectionPass(g, fn)
+    var d = initDetectionPass(g, fn, idgen)
     detectCapturedVars(body, fn, d)
     if not d.somethingToDo and fn.isIterator:
       addClosureParam(d, fn, body.info)
@@ -757,8 +905,10 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PN
     if d.somethingToDo:
       var c = initLiftingPass(fn)
       result = liftCapturedVars(body, fn, d, c)
-      if c.envvars.getOrDefault(fn.id) != nil:
-        result = newTree(nkStmtList, rawClosureCreation(fn, d, c), result)
+      # echo renderTree(result, {renderIds})
+      if c.envVars.getOrDefault(fn.id) != nil:
+        result = newTree(nkStmtList, rawClosureCreation(fn, d, c, body.info), result)
+        finishClosureCreation(fn, d, c, body.info, result)
     else:
       result = body
     #if fn.name.s == "get2":
@@ -771,7 +921,7 @@ proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode =
 
 # ------------------- iterator transformation --------------------------------
 
-proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
+proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym): PNode =
   # problem ahead: the iterator could be invoked indirectly, but then
   # we don't know what environment to create here:
   #
@@ -801,23 +951,22 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
         ...
     """
   if liftingHarmful(g.config, owner): return body
-  var L = body.len
-  if not (body.kind == nkForStmt and body[L-2].kind in nkCallKinds):
+  if not (body.kind == nkForStmt and body[^2].kind in nkCallKinds):
     localError(g.config, body.info, "ignored invalid for loop")
     return body
-  var call = body[L-2]
+  var call = body[^2]
 
   result = newNodeI(nkStmtList, body.info)
 
   # static binding?
-  var env: PSym
+  var env: PSym = nil
   let op = call[0]
   if op.kind == nkSym and op.sym.isIterator:
     # createClosure()
     let iter = op.sym
 
     let hp = getHiddenParam(g, iter)
-    env = newSym(skLet, iter.name, owner, body.info)
+    env = newSym(skLet, iter.name, idgen, owner, body.info)
     env.typ = hp.typ
     env.flags = hp.flags
 
@@ -825,38 +974,43 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
     addVar(v, newSymNode(env))
     result.add(v)
     # add 'new' statement:
-    result.add(newCall(getSysSym(g, env.info, "internalNew"), env.newSymNode))
+    result.add genCreateEnv(env.newSymNode)
+    createTypeBoundOpsLL(g, env.typ, body.info, idgen, owner)
+
   elif op.kind == nkStmtListExpr:
     let closure = op.lastSon
     if closure.kind == nkClosure:
-      call.sons[0] = closure
-      for i in 0 .. op.len-2:
+      call[0] = closure
+      for i in 0..<op.len-1:
         result.add op[i]
 
   var loopBody = newNodeI(nkStmtList, body.info, 3)
   var whileLoop = newNodeI(nkWhileStmt, body.info, 2)
-  whileLoop.sons[0] = newIntTypeNode(nkIntLit, 1, getSysType(g, body.info, tyBool))
-  whileLoop.sons[1] = loopBody
+  whileLoop[0] = newIntTypeNode(1, getSysType(g, body.info, tyBool))
+  whileLoop[1] = loopBody
   result.add whileLoop
 
   # setup loopBody:
   # gather vars in a tuple:
   var v2 = newNodeI(nkLetSection, body.info)
-  var vpart = newNodeI(if L == 3: nkIdentDefs else: nkVarTuple, body.info)
-  for i in 0 .. L-3:
-    if body[i].kind == nkSym:
-      body[i].sym.kind = skLet
-    addSon(vpart, body[i])
+  var vpart = newNodeI(if body.len == 3: nkIdentDefs else: nkVarTuple, body.info)
+  if body.len == 3 and body[0].kind == nkVarTuple:
+    vpart = body[0] # fixes for (i,j) in walk() # bug #15924
+  else:
+    for i in 0..<body.len-2:
+      if body[i].kind == nkSym:
+        body[i].sym.transitionToLet()
+      vpart.add body[i]
 
-  addSon(vpart, newNodeI(nkEmpty, body.info)) # no explicit type
+    vpart.add newNodeI(nkEmpty, body.info) # no explicit type
   if not env.isNil:
-    call.sons[0] = makeClosure(g, call.sons[0].sym, env.newSymNode, body.info)
-  addSon(vpart, call)
-  addSon(v2, vpart)
+    call[0] = makeClosure(g, idgen, call[0].sym, env.newSymNode, body.info)
+  vpart.add call
+  v2.add vpart
 
-  loopBody.sons[0] = v2
+  loopBody[0] = v2
   var bs = newNodeI(nkBreakState, body.info)
-  bs.addSon(call.sons[0])
+  bs.add call[0]
 
   let ibs = newNodeI(nkIfStmt, body.info)
   let elifBranch = newNodeI(nkElifBranch, body.info)
@@ -868,5 +1022,5 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
   elifBranch.add(br)
   ibs.add(elifBranch)
 
-  loopBody.sons[1] = ibs
-  loopBody.sons[2] = body[L-1]
+  loopBody[1] = ibs
+  loopBody[2] = body[^1]
diff --git a/compiler/layouter.nim b/compiler/layouter.nim
index 62844db4b..0121b1185 100644
--- a/compiler/layouter.nim
+++ b/compiler/layouter.nim
@@ -9,51 +9,98 @@
 
 ## Layouter for nimpretty.
 
-import idents, lexer, lineinfos, llstream, options, msgs, strutils
-from os import changeFileExt
+import idents, lexer, ast, lineinfos, llstream, options, msgs, strutils, pathutils
 
 const
-  MaxLineLen = 80
-  LineCommentColumn = 30
+  MinLineLen = 15
 
 type
   SplitKind = enum
     splitComma, splitParLe, splitAnd, splitOr, splitIn, splitBinary
 
+  SemicolonKind = enum
+    detectSemicolonKind, useSemicolon, dontTouch
+
+  LayoutToken* = enum
+    ltSpaces,
+    ltCrucialNewline, ## a semantically crucial newline (indentation!)
+    ltSplittingNewline, ## newline used for splitting up long
+                        ## expressions (like after a comma or a binary operator)
+    ltTab,
+    ltOptionalNewline, ## optional newline introduced by nimpretty
+    ltComment, ltLit, ltKeyword, ltExportMarker, ltIdent,
+    ltOther, ltOpr, ltSomeParLe, ltSomeParRi,
+    ltBeginSection, ltEndSection
+
   Emitter* = object
     config: ConfigRef
     fid: FileIndex
-    lastTok: TTokType
-    inquote: bool
-    col, lastLineNumber, lineSpan, indentLevel, indWidth: int
-    nested: int
+    lastTok: TokType
+    inquote, lastTokWasTerse: bool
+    semicolons: SemicolonKind
+    col, lastLineNumber, lineSpan, indentLevel, indWidth*, inSection: int
+    keepIndents*: int
     doIndentMore*: int
-    content: string
+    kinds*: seq[LayoutToken]
+    tokens*: seq[string]
     indentStack: seq[int]
     fixedUntil: int # marks where we must not go in the content
     altSplitPos: array[SplitKind, int] # alternative split positions
+    maxLineLen*: int
 
 proc openEmitter*(em: var Emitter, cache: IdentCache;
                   config: ConfigRef, fileIdx: FileIndex) =
-  let fullPath = config.toFullPath(fileIdx)
-  em.indWidth = getIndentWidth(fileIdx, llStreamOpen(fullPath, fmRead),
-                               cache, config)
-  if em.indWidth == 0: em.indWidth = 2
+  let fullPath = AbsoluteFile config.toFullPath(fileIdx)
+  if em.indWidth == 0:
+    em.indWidth = getIndentWidth(fileIdx, llStreamOpen(fullPath, fmRead),
+                                cache, config)
+    if em.indWidth == 0: em.indWidth = 2
   em.config = config
   em.fid = fileIdx
   em.lastTok = tkInvalid
   em.inquote = false
   em.col = 0
-  em.content = newStringOfCap(16_000)
   em.indentStack = newSeqOfCap[int](30)
   em.indentStack.add 0
+  em.lastLineNumber = 1
+
+proc computeMax(em: Emitter; pos: int): int =
+  var p = pos
+  var extraSpace = 0
+  result = 0
+  while p < em.tokens.len and em.kinds[p] != ltEndSection:
+    var lhs = 0
+    var lineLen = 0
+    var foundTab = false
+    while p < em.tokens.len and em.kinds[p] != ltEndSection:
+      if em.kinds[p] in {ltCrucialNewline, ltSplittingNewline}:
+        if foundTab and lineLen <= em.maxLineLen:
+          result = max(result, lhs + extraSpace)
+        inc p
+        break
+      if em.kinds[p] == ltTab:
+        extraSpace = if em.kinds[p-1] == ltSpaces: 0 else: 1
+        foundTab = true
+      else:
+        if not foundTab:
+          inc lhs, em.tokens[p].len
+        inc lineLen, em.tokens[p].len
+      inc p
+
+proc computeRhs(em: Emitter; pos: int): int =
+  var p = pos
+  result = 0
+  while p < em.tokens.len and em.kinds[p] notin {ltCrucialNewline, ltSplittingNewline}:
+    inc result, em.tokens[p].len
+    inc p
+
+proc isLongEnough(lineLen, startPos, endPos: int): bool =
+  result = lineLen > MinLineLen and endPos > startPos + 4
 
-proc closeEmitter*(em: var Emitter) =
-  var f = llStreamOpen(em.config.outFile, fmWrite)
-  if f == nil:
-    rawMessage(em.config, errGenerated, "cannot open file: " & em.config.outFile)
-  f.llStreamWrite em.content
-  llStreamClose(f)
+proc findNewline(em: Emitter; p, lineLen: var int) =
+  while p < em.tokens.len and em.kinds[p] notin {ltCrucialNewline, ltSplittingNewline}:
+    inc lineLen, em.tokens[p].len
+    inc p
 
 proc countNewlines(s: string): int =
   result = 0
@@ -67,78 +114,356 @@ proc calcCol(em: var Emitter; s: string) =
     dec i
     inc em.col
 
-template wr(x) =
-  em.content.add x
+proc optionalIsGood(em: var Emitter; pos, currentLen: int): bool =
+  let ourIndent = em.tokens[pos].len
+  var p = pos+1
+  var lineLen = 0
+  em.findNewline(p, lineLen)
+  if p == pos+1: # optionalNewline followed by another newline
+    result = false
+  elif em.kinds[p-1] == ltComment and currentLen+lineLen < em.maxLineLen+MinLineLen:
+    result = false
+  elif p+1 < em.tokens.len and em.kinds[p+1] == ltSpaces and
+      em.kinds[p-1] == ltOptionalNewline:
+    if em.tokens[p+1].len == ourIndent:
+      # concatenate lines with the same indententation
+      var nlPos = p
+      var lineLenTotal = lineLen
+      inc p
+      em.findNewline(p, lineLenTotal)
+      if isLongEnough(lineLenTotal, nlPos, p):
+        em.kinds[nlPos] = ltOptionalNewline
+        if em.kinds[nlPos+1] == ltSpaces:
+          # inhibit extra spaces when concatenating two lines
+          em.tokens[nlPos+1] = if em.tokens[nlPos-2] == ",": " " else: ""
+      result = true
+    elif em.tokens[p+1].len < ourIndent:
+      result = isLongEnough(lineLen, pos, p)
+  elif em.kinds[pos+1] in {ltOther, ltSomeParLe, ltSomeParRi}: # note: pos+1, not p+1
+    result = false
+  else:
+    result = isLongEnough(lineLen, pos, p)
+
+proc lenOfNextTokens(em: Emitter; pos: int): int =
+  result = 0
+  for i in 1..<em.tokens.len-pos:
+    if em.kinds[pos+i] in {ltCrucialNewline, ltSplittingNewline, ltOptionalNewline}: break
+    inc result, em.tokens[pos+i].len
+
+proc guidingInd(em: Emitter; pos: int): int =
+  var i = pos - 1
+  while i >= 0 and em.kinds[i] != ltSomeParLe:
+    dec i
+  while i+1 <= em.kinds.high and em.kinds[i] != ltSomeParRi:
+    if em.kinds[i] == ltSplittingNewline and em.kinds[i+1] == ltSpaces:
+      return em.tokens[i+1].len
+    inc i
+  result = -1
+
+proc renderTokens*(em: var Emitter): string =
+  ## Render Emitter tokens to a string of code
+  template defaultCase() =
+    content.add em.tokens[i]
+    inc lineLen, em.tokens[i].len
+  var content = newStringOfCap(16_000)
+  var maxLhs = 0
+  var lineLen = 0
+  var lineBegin = 0
+  var openPars = 0
+  var i = 0
+  while i <= em.tokens.high:
+    when defined(debug):
+      echo (token: em.tokens[i], kind: em.kinds[i])
+    case em.kinds[i]
+    of ltBeginSection:
+      maxLhs = computeMax(em, lineBegin)
+    of ltEndSection:
+      maxLhs = 0
+      lineBegin = i+1
+    of ltTab:
+      if i >= 2 and em.kinds[i-2] in {ltCrucialNewline, ltSplittingNewline} and
+          em.kinds[i-1] in {ltCrucialNewline, ltSplittingNewline, ltSpaces}:
+        # a previous section has ended
+        maxLhs = 0
+
+      if maxLhs == 0:
+        if em.kinds[i-1] != ltSpaces:
+          content.add em.tokens[i]
+          inc lineLen, em.tokens[i].len
+      else:
+        # pick the shorter indentation token:
+        var spaces = maxLhs - lineLen
+        if spaces < em.tokens[i].len or computeRhs(em, i+1)+maxLhs <= em.maxLineLen+MinLineLen:
+          if spaces <= 0 and content[^1] notin {' ', '\L'}: spaces = 1
+          for j in 1..spaces: content.add ' '
+          inc lineLen, spaces
+        else:
+          content.add em.tokens[i]
+          inc lineLen, em.tokens[i].len
+    of ltCrucialNewline, ltSplittingNewline:
+      content.add em.tokens[i]
+      lineLen = 0
+      lineBegin = i+1
+    of ltOptionalNewline:
+      let totalLineLen = lineLen + lenOfNextTokens(em, i)
+      if totalLineLen > em.maxLineLen and optionalIsGood(em, i, lineLen):
+        if i-1 >= 0 and em.kinds[i-1] == ltSpaces:
+          let spaces = em.tokens[i-1].len
+          content.setLen(content.len - spaces)
+        content.add "\L"
+        let guide = if openPars > 0: guidingInd(em, i) else: -1
+        if guide >= 0:
+          content.add repeat(' ', guide)
+          lineLen = guide
+        else:
+          content.add em.tokens[i]
+          lineLen = em.tokens[i].len
+        lineBegin = i+1
+        if i+1 < em.kinds.len and em.kinds[i+1] == ltSpaces:
+          # inhibit extra spaces at the start of a new line
+          inc i
+    of ltLit:
+      let lineSpan = countNewlines(em.tokens[i])
+      if lineSpan > 0:
+        em.calcCol(em.tokens[i])
+        lineLen = em.col
+      else:
+        inc lineLen, em.tokens[i].len
+      content.add em.tokens[i]
+    of ltSomeParLe:
+      inc openPars
+      defaultCase()
+    of ltSomeParRi:
+      doAssert openPars > 0
+      dec openPars
+      defaultCase()
+    else:
+      defaultCase()
+    inc i
+
+  return content
+
+type
+  FinalCheck = proc (content: string; origAst: PNode): bool {.nimcall.}
+
+proc writeOut*(em: Emitter; content: string; origAst: PNode; check: FinalCheck) =
+  ## Write to disk
+  let outFile = em.config.absOutFile
+  if fileExists(outFile) and readFile(outFile.string) == content:
+    discard "do nothing, see #9499"
+    return
+
+  if check(content, origAst):
+    var f = llStreamOpen(outFile, fmWrite)
+    if f == nil:
+      rawMessage(em.config, errGenerated, "cannot open file: " & outFile.string)
+      return
+    f.llStreamWrite content
+    llStreamClose(f)
+
+proc closeEmitter*(em: var Emitter; origAst: PNode; check: FinalCheck) =
+  ## Renders emitter tokens and write to a file
+  let content = renderTokens(em)
+  em.writeOut(content, origAst, check)
+
+proc wr(em: var Emitter; x: string; lt: LayoutToken) =
+  em.tokens.add x
+  em.kinds.add lt
   inc em.col, x.len
+  assert em.tokens.len == em.kinds.len
+
+proc wrNewline(em: var Emitter; kind = ltCrucialNewline) =
+  em.tokens.add "\L"
+  em.kinds.add kind
+  em.col = 0
+
+proc newlineWasSplitting*(em: var Emitter) =
+  if em.kinds.len >= 3 and em.kinds[^3] == ltCrucialNewline:
+    em.kinds[^3] = ltSplittingNewline
+
+#[
+Splitting newlines can occur:
+- after commas, semicolon, '[', '('.
+- after binary operators, '='.
+- after ':' type
+
+We only need parser support for the "after type" case.
+]#
+
+proc wrSpaces(em: var Emitter; spaces: int) =
+  if spaces > 0:
+    wr(em, strutils.repeat(' ', spaces), ltSpaces)
+
+proc wrSpace(em: var Emitter) =
+  wr(em, " ", ltSpaces)
+
+proc wrTab(em: var Emitter) =
+  wr(em, " ", ltTab)
+
+proc beginSection*(em: var Emitter) =
+  let pos = max(0, em.tokens.len-2)
+  em.tokens.insert "", pos
+  em.kinds.insert ltBeginSection, pos
+  inc em.inSection
+
+#wr(em, "", ltBeginSection)
+proc endSection*(em: var Emitter) =
+  em.tokens.insert "", em.tokens.len-2
+  em.kinds.insert ltEndSection, em.kinds.len-2
+  dec em.inSection
+
+#wr(em, "", ltEndSection)
+
+proc removeSpaces(em: var Emitter) =
+  while em.kinds.len > 0 and em.kinds[^1] == ltSpaces:
+    let tokenLen = em.tokens[^1].len
+    setLen(em.tokens, em.tokens.len-1)
+    setLen(em.kinds, em.kinds.len-1)
+    dec em.col, tokenLen
 
-template goodCol(col): bool = col in 40..MaxLineLen
 
 const
   openPars = {tkParLe, tkParDotLe,
-              tkBracketLe, tkBracketLeColon, tkCurlyDotLe,
-              tkCurlyLe}
-  splitters = openPars + {tkComma, tkSemicolon}
+              tkBracketLe, tkBracketDotLe, tkBracketLeColon,
+              tkCurlyDotLe, tkCurlyLe}
+  closedPars = {tkParRi, tkParDotRi,
+                tkBracketRi, tkBracketDotRi,
+                tkCurlyDotRi, tkCurlyRi}
+
+  splitters = openPars + {tkComma, tkSemiColon} # do not add 'tkColon' here!
   oprSet = {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs,
-            tkIsnot, tkNot, tkOf, tkAs, tkDotDot, tkAnd, tkOr, tkXor}
+            tkIsnot, tkNot, tkOf, tkAs, tkFrom, tkDotDot, tkAnd, tkOr, tkXor}
 
-template rememberSplit(kind) =
-  if goodCol(em.col):
-    em.altSplitPos[kind] = em.content.len
+template goodCol(col): bool = col >= em.maxLineLen div 2
 
 template moreIndent(em): int =
-  (if em.doIndentMore > 0: em.indWidth*2 else: em.indWidth)
-
-proc softLinebreak(em: var Emitter, lit: string) =
-  # XXX Use an algorithm that is outlined here:
-  # https://llvm.org/devmtg/2013-04/jasper-slides.pdf
-  # +2 because we blindly assume a comma or ' &' might follow
-  if not em.inquote and em.col+lit.len+2 >= MaxLineLen:
-    if em.lastTok in splitters:
-      while em.content.len > 0 and em.content[em.content.high] == ' ':
-        setLen(em.content, em.content.len-1)
-      wr("\L")
-      em.col = 0
-      for i in 1..em.indentLevel+moreIndent(em): wr(" ")
+  if em.doIndentMore > 0: em.indWidth*2 else: em.indWidth
+
+template rememberSplit(kind) =
+  if goodCol(em.col) and not em.inquote:
+    let spaces = em.indentLevel+moreIndent(em)
+    if spaces < em.col and spaces > 0:
+      wr(em, strutils.repeat(' ', spaces), ltOptionalNewline)
+    #em.altSplitPos[kind] = em.tokens.len
+
+proc emitMultilineComment(em: var Emitter, lit: string, col: int; dontIndent: bool) =
+  # re-align every line in the multi-line comment:
+  var i = 0
+  var lastIndent = if em.keepIndents > 0: em.indentLevel else: em.indentStack[^1]
+  var b = 0
+  var dontIndent = dontIndent
+  var hasEmptyLine = false
+  for commentLine in splitLines(lit):
+    if i == 0 and (commentLine.endsWith("\\") or commentLine.endsWith("[")):
+      dontIndent = true
+      wr em, commentLine, ltComment
+    elif dontIndent:
+      if i > 0: wrNewline em
+      wr em, commentLine, ltComment
     else:
-      # search backwards for a good split position:
-      for a in em.altSplitPos:
-        if a > em.fixedUntil:
-          var spaces = 0
-          while a+spaces < em.content.len and em.content[a+spaces] == ' ':
-            inc spaces
-          if spaces > 0: delete(em.content, a, a+spaces-1)
-          let ws = "\L" & repeat(' ',em.indentLevel+moreIndent(em))
-          em.col = em.content.len - a
-          em.content.insert(ws, a)
-          break
-
-proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
-
-  template endsInWhite(em): bool =
-    em.content.len == 0 or em.content[em.content.high] in {' ', '\L'}
-  template endsInAlpha(em): bool =
-    em.content.len > 0 and em.content[em.content.high] in SymChars+{'_'}
-
-  proc emitComment(em: var Emitter; tok: TToken) =
-    let lit = strip fileSection(em.config, em.fid, tok.commentOffsetA, tok.commentOffsetB)
-    em.lineSpan = countNewlines(lit)
-    if em.lineSpan > 0: calcCol(em, lit)
+      let stripped = commentLine.strip()
+      if i == 0:
+        if em.kinds.len > 0 and em.kinds[^1] != ltTab:
+          wr(em, "", ltTab)
+      elif stripped.len == 0:
+        wrNewline em
+        hasEmptyLine = true
+      else:
+        var a = 0
+        while a < commentLine.len and commentLine[a] == ' ': inc a
+
+        if a > lastIndent:
+          b += em.indWidth
+          lastIndent = a
+        elif a < lastIndent:
+          b -= em.indWidth
+          lastIndent = a
+        wrNewline em
+        if not hasEmptyLine or col + b < 15:
+          if col + b > 0:
+            wr(em, repeat(' ', col+b), ltTab)
+          else:
+            wr(em, "", ltTab)
+        else:
+          wr(em, repeat(' ', a), ltSpaces)
+      wr em, stripped, ltComment
+    inc i
+
+proc lastChar(s: string): char =
+  result = if s.len > 0: s[s.high] else: '\0'
+
+proc endsInWhite(em: Emitter): bool =
+  var i = em.tokens.len-1
+  while i >= 0 and em.kinds[i] in {ltBeginSection, ltEndSection}: dec(i)
+  result = if i >= 0: em.kinds[i] in {ltSpaces, ltCrucialNewline, ltSplittingNewline, ltTab} else: true
+
+proc endsInNewline(em: Emitter): bool =
+  var i = em.tokens.len-1
+  while i >= 0 and em.kinds[i] in {ltBeginSection, ltEndSection, ltSpaces}: dec(i)
+  result = if i >= 0: em.kinds[i] in {ltCrucialNewline, ltSplittingNewline, ltTab} else: true
+
+proc endsInAlpha(em: Emitter): bool =
+  var i = em.tokens.len-1
+  while i >= 0 and em.kinds[i] in {ltBeginSection, ltEndSection}: dec(i)
+  result = if i >= 0: em.tokens[i].lastChar in SymChars+{'_'} else: false
+
+proc emitComment(em: var Emitter; tok: Token; dontIndent: bool) =
+  var col = em.col
+  let lit = strip fileSection(em.config, em.fid, tok.commentOffsetA, tok.commentOffsetB)
+  em.lineSpan = countNewlines(lit)
+  if em.lineSpan > 0: calcCol(em, lit)
+  if em.lineSpan == 0:
+    if not endsInNewline(em):
+      wrTab em
+    wr em, lit, ltComment
+  else:
     if not endsInWhite(em):
-      wr(" ")
-      if em.lineSpan == 0 and max(em.col, LineCommentColumn) + lit.len <= MaxLineLen:
-        for i in 1 .. LineCommentColumn - em.col: wr(" ")
-    wr lit
+      wrTab em
+      inc col
+    emitMultilineComment(em, lit, col, dontIndent)
+
+proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
+  template wasExportMarker(em): bool =
+    em.kinds.len > 0 and em.kinds[^1] == ltExportMarker
+
+  if tok.tokType == tkComment and tok.literal.startsWith("#!nimpretty"):
+    case tok.literal
+    of "#!nimpretty off":
+      inc em.keepIndents
+      wrNewline em
+      em.lastLineNumber = tok.line + 1
+    of "#!nimpretty on":
+      dec em.keepIndents
+      em.lastLineNumber = tok.line
+    wrNewline em
+    wr em, tok.literal, ltComment
+    em.col = 0
+    em.lineSpan = 0
+    return
 
   var preventComment = false
-  if tok.tokType == tkComment and tok.line == em.lastLineNumber and tok.indent >= 0:
+  if tok.tokType == tkComment and tok.line == em.lastLineNumber:
     # we have an inline comment so handle it before the indentation token:
-    emitComment(em, tok)
+    emitComment(em, tok, dontIndent = (em.inSection == 0))
     preventComment = true
-    em.fixedUntil = em.content.high
+    em.fixedUntil = em.tokens.high
 
   elif tok.indent >= 0:
-    if em.lastTok in (splitters + oprSet):
+    var newlineKind = ltCrucialNewline
+    if em.keepIndents > 0:
       em.indentLevel = tok.indent
+    elif (em.lastTok in (splitters + oprSet) and
+        tok.tokType notin (closedPars - {tkBracketDotRi})):
+      if tok.tokType in openPars and tok.indent > em.indentStack[^1]:
+        while em.indentStack[^1] < tok.indent:
+          em.indentStack.add(em.indentStack[^1] + em.indWidth)
+      while em.indentStack[^1] > tok.indent:
+        discard em.indentStack.pop()
+
+      # aka: we are in an expression context:
+      let alignment = max(tok.indent - em.indentStack[^1], 0)
+      em.indentLevel = alignment + em.indentStack.high * em.indWidth
+      newlineKind = ltSplittingNewline
     else:
       if tok.indent > em.indentStack[^1]:
         em.indentStack.add tok.indent
@@ -157,112 +482,128 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
        is not touched.
     ]#
     # remove trailing whitespace:
-    while em.content.len > 0 and em.content[em.content.high] == ' ':
-      setLen(em.content, em.content.len-1)
-    wr("\L")
-    for i in 2..tok.line - em.lastLineNumber: wr("\L")
-    em.col = 0
-    for i in 1..em.indentLevel:
-      wr(" ")
-    em.fixedUntil = em.content.high
+    removeSpaces em
+    wrNewline em, newlineKind
+    for i in 2..tok.line - em.lastLineNumber: wrNewline(em)
+    wrSpaces em, em.indentLevel
+    em.fixedUntil = em.tokens.high
 
+  var lastTokWasTerse = false
   case tok.tokType
   of tokKeywordLow..tokKeywordHigh:
     if endsInAlpha(em):
-      wr(" ")
+      wrSpace em
     elif not em.inquote and not endsInWhite(em) and
-        em.lastTok notin openPars:
+        em.lastTok notin (openPars+{tkOpr, tkDotDot}) and not em.lastTokWasTerse:
       #and tok.tokType in oprSet
-      wr(" ")
+      wrSpace em
 
     if not em.inquote:
-      wr(TokTypeToStr[tok.tokType])
-
-      case tok.tokType
-      of tkAnd: rememberSplit(splitAnd)
-      of tkOr: rememberSplit(splitOr)
-      of tkIn, tkNotin:
+      wr(em, $tok.tokType, ltKeyword)
+      if tok.tokType in {tkAnd, tkOr, tkIn, tkNotin}:
         rememberSplit(splitIn)
-        wr(" ")
-      else: discard
+        wrSpace em
     else:
       # keywords in backticks are not normalized:
-      wr(tok.ident.s)
+      wr(em, tok.ident.s, ltIdent)
 
   of tkColon:
-    wr(TokTypeToStr[tok.tokType])
-    wr(" ")
-  of tkSemicolon, tkComma:
-    wr(TokTypeToStr[tok.tokType])
+    wr(em, $tok.tokType, ltOther)
+    wrSpace em
+  of tkSemiColon, tkComma:
+    wr(em, $tok.tokType, ltOther)
     rememberSplit(splitComma)
-    wr(" ")
-  of tkParDotLe, tkParLe, tkBracketDotLe, tkBracketLe,
-     tkCurlyLe, tkCurlyDotLe, tkBracketLeColon:
-    if tok.strongSpaceA > 0 and not em.endsInWhite:
-      wr(" ")
-    wr(TokTypeToStr[tok.tokType])
-    rememberSplit(splitParLe)
-  of tkParRi,
-     tkBracketRi, tkCurlyRi,
-     tkBracketDotRi,
-     tkCurlyDotRi,
-     tkParDotRi,
-     tkColonColon, tkDot:
-    wr(TokTypeToStr[tok.tokType])
+    wrSpace em
+  of openPars:
+    if tsLeading in tok.spacing and not em.endsInWhite and
+        (not em.wasExportMarker or tok.tokType == tkCurlyDotLe):
+      wrSpace em
+    wr(em, $tok.tokType, ltSomeParLe)
+    if tok.tokType != tkCurlyDotLe:
+      rememberSplit(splitParLe)
+  of closedPars:
+    wr(em, $tok.tokType, ltSomeParRi)
+  of tkColonColon:
+    wr(em, $tok.tokType, ltOther)
+  of tkDot:
+    lastTokWasTerse = true
+    wr(em, $tok.tokType, ltOther)
   of tkEquals:
-    if not em.inquote and not em.endsInWhite: wr(" ")
-    wr(TokTypeToStr[tok.tokType])
-    if not em.inquote: wr(" ")
+    if not em.inquote and not em.endsInWhite: wrSpace(em)
+    wr(em, $tok.tokType, ltOther)
+    if not em.inquote: wrSpace(em)
   of tkOpr, tkDotDot:
-    if tok.strongSpaceA == 0 and tok.strongSpaceB == 0:
+    if em.inquote or (tok.spacing == {} and
+        tok.ident.s notin ["<", ">", "<=", ">=", "==", "!="]):
+      # bug #9504: remember to not spacify a keyword:
+      lastTokWasTerse = true
       # if not surrounded by whitespace, don't produce any whitespace either:
-      wr(tok.ident.s)
+      wr(em, tok.ident.s, ltOpr)
     else:
-      if not em.endsInWhite: wr(" ")
-      wr(tok.ident.s)
+      if not em.endsInWhite: wrSpace(em)
+      wr(em, tok.ident.s, ltOpr)
       template isUnary(tok): bool =
-        tok.strongSpaceB == 0 and tok.strongSpaceA > 0
+        tok.spacing == {tsLeading}
 
       if not isUnary(tok):
-        wr(" ")
         rememberSplit(splitBinary)
+        wrSpace(em)
   of tkAccent:
-    if not em.inquote and endsInAlpha(em): wr(" ")
-    wr(TokTypeToStr[tok.tokType])
+    if not em.inquote and endsInAlpha(em): wrSpace(em)
+    wr(em, $tok.tokType, ltOther)
     em.inquote = not em.inquote
   of tkComment:
     if not preventComment:
-      emitComment(em, tok)
+      emitComment(em, tok, dontIndent = false)
   of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, tkGTripleStrLit, tkCharLit:
-    let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB)
-    softLinebreak(em, lit)
-    if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wr(" ")
-    em.lineSpan = countNewlines(lit)
-    if em.lineSpan > 0: calcCol(em, lit)
-    wr lit
+    if not em.inquote:
+      let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB)
+      if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wrSpace(em)
+      em.lineSpan = countNewlines(lit)
+      if em.lineSpan > 0: calcCol(em, lit)
+      wr em, lit, ltLit
+    else:
+      if endsInAlpha(em): wrSpace(em)
+      wr em, tok.literal, ltLit
   of tkEof: discard
   else:
     let lit = if tok.ident != nil: tok.ident.s else: tok.literal
-    softLinebreak(em, lit)
-    if endsInAlpha(em): wr(" ")
-    wr lit
+    if endsInAlpha(em): wrSpace(em)
+    wr em, lit, ltIdent
 
   em.lastTok = tok.tokType
+  em.lastTokWasTerse = lastTokWasTerse
   em.lastLineNumber = tok.line + em.lineSpan
   em.lineSpan = 0
 
+proc endsWith(em: Emitter; k: varargs[string]): bool =
+  if em.tokens.len < k.len: return false
+  for i in 0..high(k):
+    if em.tokens[em.tokens.len - k.len + i] != k[i]: return false
+  return true
+
+proc rfind(em: Emitter, t: string): int =
+  for i in 1..5:
+    if em.tokens[^i] == t:
+      return i
+
 proc starWasExportMarker*(em: var Emitter) =
-  if em.content.endsWith(" * "):
-    setLen(em.content, em.content.len-3)
-    em.content.add("*")
+  if em.endsWith(" ", "*", " "):
+    setLen(em.tokens, em.tokens.len-3)
+    setLen(em.kinds, em.kinds.len-3)
+    em.tokens.add("*")
+    em.kinds.add ltExportMarker
     dec em.col, 2
 
 proc commaWasSemicolon*(em: var Emitter) =
-  if em.content.endsWith(", "):
-    setLen(em.content, em.content.len-2)
-    em.content.add("; ")
+  if em.semicolons == detectSemicolonKind:
+    em.semicolons = if em.rfind(";") > 0: useSemicolon else: dontTouch
+  if em.semicolons == useSemicolon:
+    let commaPos = em.rfind(",")
+    if commaPos > 0:
+      em.tokens[^commaPos] = ";"
 
 proc curlyRiWasPragma*(em: var Emitter) =
-  if em.content.endsWith("}"):
-    setLen(em.content, em.content.len-1)
-    em.content.add(".}")
+  if em.endsWith("}"):
+    em.tokens[^1] = ".}"
+    inc em.col
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index c5afa6e97..ad5dd560c 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -7,64 +7,78 @@
 #    distribution, for details about the copyright.
 #
 
-# This scanner is handwritten for efficiency. I used an elegant buffering
+# This lexer is handwritten for efficiency. I used an elegant buffering
 # scheme which I have not seen anywhere else:
 # We guarantee that a whole line is in the buffer. Thus only when scanning
-# the \n or \r character we have to check wether we need to read in the next
+# the \n or \r character we have to check whether we need to read in the next
 # chunk. (\n or \r already need special handling for incrementing the line
-# counter; choosing both \n and \r allows the scanner to properly read Unix,
+# counter; choosing both \n and \r allows the lexer to properly read Unix,
 # DOS or Macintosh text files, even when it is not the native format.
 
 import
-  hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
-  wordrecg, lineinfos
+  options, msgs, platform, idents, nimlexbase, llstream,
+  wordrecg, lineinfos, pathutils
+
+import std/[hashes, parseutils, strutils]
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, formatfloat]
 
 const
-  MaxLineLength* = 80         # lines longer than this lead to a warning
   numChars*: set[char] = {'0'..'9', 'a'..'z', 'A'..'Z'}
   SymChars*: set[char] = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'}
   SymStartChars*: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'}
   OpChars*: set[char] = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.',
     '|', '=', '%', '&', '$', '@', '~', ':'}
+  UnaryMinusWhitelist = {' ', '\t', '\n', '\r', ',', ';', '(', '[', '{'}
 
 # don't forget to update the 'highlite' module if these charsets should change
 
 type
-  TTokType* = enum
-    tkInvalid, tkEof,         # order is important here!
-    tkSymbol, # keywords:
-    tkAddr, tkAnd, tkAs, tkAsm,
-    tkBind, tkBlock, tkBreak, tkCase, tkCast,
-    tkConcept, tkConst, tkContinue, tkConverter,
-    tkDefer, tkDiscard, tkDistinct, tkDiv, tkDo,
-    tkElif, tkElse, tkEnd, tkEnum, tkExcept, tkExport,
-    tkFinally, tkFor, tkFrom, tkFunc,
-    tkIf, tkImport, tkIn, tkInclude, tkInterface,
-    tkIs, tkIsnot, tkIterator,
-    tkLet,
-    tkMacro, tkMethod, tkMixin, tkMod, tkNil, tkNot, tkNotin,
-    tkObject, tkOf, tkOr, tkOut,
-    tkProc, tkPtr, tkRaise, tkRef, tkReturn,
-    tkShl, tkShr, tkStatic,
-    tkTemplate,
-    tkTry, tkTuple, tkType, tkUsing,
-    tkVar, tkWhen, tkWhile, tkXor,
-    tkYield, # end of keywords
-    tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit,
-    tkUIntLit, tkUInt8Lit, tkUInt16Lit, tkUInt32Lit, tkUInt64Lit,
-    tkFloatLit, tkFloat32Lit, tkFloat64Lit, tkFloat128Lit,
-    tkStrLit, tkRStrLit, tkTripleStrLit,
-    tkGStrLit, tkGTripleStrLit, tkCharLit, tkParLe, tkParRi, tkBracketLe,
-    tkBracketRi, tkCurlyLe, tkCurlyRi,
-    tkBracketDotLe, tkBracketDotRi, # [. and  .]
-    tkCurlyDotLe, tkCurlyDotRi, # {.  and  .}
-    tkParDotLe, tkParDotRi,   # (. and .)
-    tkComma, tkSemiColon,
-    tkColon, tkColonColon, tkEquals, tkDot, tkDotDot, tkBracketLeColon,
-    tkOpr, tkComment, tkAccent,
-    tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr
-
-  TTokTypes* = set[TTokType]
+  TokType* = enum
+    tkInvalid = "tkInvalid", tkEof = "[EOF]", # order is important here!
+    tkSymbol = "tkSymbol", # keywords:
+    tkAddr = "addr", tkAnd = "and", tkAs = "as", tkAsm = "asm",
+    tkBind = "bind", tkBlock = "block", tkBreak = "break", tkCase = "case", tkCast = "cast",
+    tkConcept = "concept", tkConst = "const", tkContinue = "continue", tkConverter = "converter",
+    tkDefer = "defer", tkDiscard = "discard", tkDistinct = "distinct", tkDiv = "div", tkDo = "do",
+    tkElif = "elif", tkElse = "else", tkEnd = "end", tkEnum = "enum", tkExcept = "except", tkExport = "export",
+    tkFinally = "finally", tkFor = "for", tkFrom = "from", tkFunc = "func",
+    tkIf = "if", tkImport = "import", tkIn = "in", tkInclude = "include", tkInterface = "interface",
+    tkIs = "is", tkIsnot = "isnot", tkIterator = "iterator",
+    tkLet = "let",
+    tkMacro = "macro", tkMethod = "method", tkMixin = "mixin", tkMod = "mod", tkNil = "nil", tkNot = "not", tkNotin = "notin",
+    tkObject = "object", tkOf = "of", tkOr = "or", tkOut = "out",
+    tkProc = "proc", tkPtr = "ptr", tkRaise = "raise", tkRef = "ref", tkReturn = "return",
+    tkShl = "shl", tkShr = "shr", tkStatic = "static",
+    tkTemplate = "template",
+    tkTry = "try", tkTuple = "tuple", tkType = "type", tkUsing = "using",
+    tkVar = "var", tkWhen = "when", tkWhile = "while", tkXor = "xor",
+    tkYield = "yield", # end of keywords
+
+    tkIntLit = "tkIntLit", tkInt8Lit = "tkInt8Lit", tkInt16Lit = "tkInt16Lit",
+    tkInt32Lit = "tkInt32Lit", tkInt64Lit = "tkInt64Lit",
+    tkUIntLit = "tkUIntLit", tkUInt8Lit = "tkUInt8Lit", tkUInt16Lit = "tkUInt16Lit",
+    tkUInt32Lit = "tkUInt32Lit", tkUInt64Lit = "tkUInt64Lit",
+    tkFloatLit = "tkFloatLit", tkFloat32Lit = "tkFloat32Lit",
+    tkFloat64Lit = "tkFloat64Lit", tkFloat128Lit = "tkFloat128Lit",
+    tkStrLit = "tkStrLit", tkRStrLit = "tkRStrLit", tkTripleStrLit = "tkTripleStrLit",
+    tkGStrLit = "tkGStrLit", tkGTripleStrLit = "tkGTripleStrLit", tkCharLit = "tkCharLit",
+    tkCustomLit = "tkCustomLit",
+
+    tkParLe = "(", tkParRi = ")", tkBracketLe = "[",
+    tkBracketRi = "]", tkCurlyLe = "{", tkCurlyRi = "}",
+    tkBracketDotLe = "[.", tkBracketDotRi = ".]",
+    tkCurlyDotLe = "{.", tkCurlyDotRi = ".}",
+    tkParDotLe = "(.", tkParDotRi = ".)",
+    tkComma = ",", tkSemiColon = ";",
+    tkColon = ":", tkColonColon = "::", tkEquals = "=",
+    tkDot = ".", tkDotDot = "..", tkBracketLeColon = "[:",
+    tkOpr, tkComment, tkAccent = "`",
+    # these are fake tokens used by renderer.nim
+    tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr, tkHideableStart, tkHideableEnd
+
+  TokTypes* = set[TokType]
 
 const
   weakTokens = {tkComma, tkSemiColon, tkColon,
@@ -73,84 +87,50 @@ const
     # tokens that should not be considered for previousToken
   tokKeywordLow* = succ(tkSymbol)
   tokKeywordHigh* = pred(tkIntLit)
-  TokTypeToStr*: array[TTokType, string] = ["tkInvalid", "[EOF]",
-    "tkSymbol",
-    "addr", "and", "as", "asm",
-    "bind", "block", "break", "case", "cast",
-    "concept", "const", "continue", "converter",
-    "defer", "discard", "distinct", "div", "do",
-    "elif", "else", "end", "enum", "except", "export",
-    "finally", "for", "from", "func", "if",
-    "import", "in", "include", "interface", "is", "isnot", "iterator",
-    "let",
-    "macro", "method", "mixin", "mod",
-    "nil", "not", "notin", "object", "of", "or",
-    "out", "proc", "ptr", "raise", "ref", "return",
-    "shl", "shr", "static",
-    "template",
-    "try", "tuple", "type", "using",
-    "var", "when", "while", "xor",
-    "yield",
-    "tkIntLit", "tkInt8Lit", "tkInt16Lit", "tkInt32Lit", "tkInt64Lit",
-    "tkUIntLit", "tkUInt8Lit", "tkUInt16Lit", "tkUInt32Lit", "tkUInt64Lit",
-    "tkFloatLit", "tkFloat32Lit", "tkFloat64Lit", "tkFloat128Lit",
-    "tkStrLit", "tkRStrLit",
-    "tkTripleStrLit", "tkGStrLit", "tkGTripleStrLit", "tkCharLit", "(",
-    ")", "[", "]", "{", "}", "[.", ".]", "{.", ".}", "(.", ".)",
-    ",", ";",
-    ":", "::", "=", ".", "..", "[:",
-    "tkOpr", "tkComment", "`",
-    "tkSpaces", "tkInfixOpr",
-    "tkPrefixOpr", "tkPostfixOpr"]
 
 type
-  TNumericalBase* = enum
+  NumericalBase* = enum
     base10,                   # base10 is listed as the first element,
                               # so that it is the correct default value
     base2, base8, base16
 
-  CursorPosition* {.pure.} = enum ## XXX remove this again
-    None, InToken, BeforeToken, AfterToken
-
-  TToken* = object            # a Nim token
-    tokType*: TTokType        # the type of the token
-    indent*: int              # the indentation; != -1 if the token has been
-                              # preceded with indentation
-    ident*: PIdent            # the parsed identifier
-    iNumber*: BiggestInt      # the parsed integer literal
-    fNumber*: BiggestFloat    # the parsed floating point literal
-    base*: TNumericalBase     # the numerical base; only valid for int
-                              # or float literals
-    strongSpaceA*: int8       # leading spaces of an operator
-    strongSpaceB*: int8       # trailing spaces of an operator
-    literal*: string          # the parsed (string) literal; and
-                              # documentation comments are here too
+  TokenSpacing* = enum
+    tsLeading, tsTrailing, tsEof
+
+  Token* = object                # a Nim token
+    tokType*: TokType            # the type of the token
+    base*: NumericalBase         # the numerical base; only valid for int
+                                 # or float literals
+    spacing*: set[TokenSpacing]  # spaces around token
+    indent*: int                 # the indentation; != -1 if the token has been
+                                 # preceded with indentation
+    ident*: PIdent               # the parsed identifier
+    iNumber*: BiggestInt         # the parsed integer literal
+    fNumber*: BiggestFloat       # the parsed floating point literal
+    literal*: string             # the parsed (string) literal; and
+                                 # documentation comments are here too
     line*, col*: int
     when defined(nimpretty):
-      offsetA*, offsetB*: int   # used for pretty printing so that literals
-                                # like 0b01 or  r"\L" are unaffected
+      offsetA*, offsetB*: int # used for pretty printing so that literals
+                              # like 0b01 or  r"\L" are unaffected
       commentOffsetA*, commentOffsetB*: int
 
-  TErrorHandler* = proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string)
-  TLexer* = object of TBaseLexer
+  ErrorHandler* = proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string)
+  Lexer* = object of TBaseLexer
     fileIdx*: FileIndex
-    indentAhead*: int         # if > 0 an indendation has already been read
+    indentAhead*: int         # if > 0 an indentation has already been read
                               # this is needed because scanning comments
                               # needs so much look-ahead
     currLineIndent*: int
-    strongSpaces*, allowTabs*: bool
-    cursor*: CursorPosition
-    errorHandler*: TErrorHandler
+    errorHandler*: ErrorHandler
     cache*: IdentCache
     when defined(nimsuggest):
       previousToken: TLineInfo
+      tokenEnd*: TLineInfo
+      previousTokenEnd*: TLineInfo
     config*: ConfigRef
 
-when defined(nimpretty):
-  var
-    gIndentationWidth*: int
-
-proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} =
+proc getLineInfo*(L: Lexer, tok: Token): TLineInfo {.inline.} =
   result = newLineInfo(L.fileIdx, tok.line, tok.col)
   when defined(nimpretty):
     result.offsetA = tok.offsetA
@@ -158,8 +138,8 @@ proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} =
     result.commentOffsetA = tok.commentOffsetA
     result.commentOffsetB = tok.commentOffsetB
 
-proc isKeyword*(kind: TTokType): bool =
-  result = (kind >= tokKeywordLow) and (kind <= tokKeywordHigh)
+proc isKeyword*(kind: TokType): bool =
+  (kind >= tokKeywordLow) and (kind <= tokKeywordHigh)
 
 template ones(n): untyped = ((1 shl n)-1) # for utf-8 conversion
 
@@ -169,62 +149,37 @@ proc isNimIdentifier*(s: string): bool =
     var i = 1
     while i < sLen:
       if s[i] == '_': inc(i)
-      if i < sLen and s[i] notin SymChars: return
+      if i < sLen and s[i] notin SymChars: return false
       inc(i)
     result = true
+  else:
+    result = false
 
-proc tokToStr*(tok: TToken): string =
+proc `$`*(tok: Token): string =
   case tok.tokType
-  of tkIntLit..tkInt64Lit: result = $tok.iNumber
-  of tkFloatLit..tkFloat64Lit: result = $tok.fNumber
-  of tkInvalid, tkStrLit..tkCharLit, tkComment: result = tok.literal
-  of tkParLe..tkColon, tkEof, tkAccent:
-    result = TokTypeToStr[tok.tokType]
+  of tkIntLit..tkInt64Lit: $tok.iNumber
+  of tkFloatLit..tkFloat64Lit: $tok.fNumber
+  of tkInvalid, tkStrLit..tkCharLit, tkComment: tok.literal
+  of tkParLe..tkColon, tkEof, tkAccent: $tok.tokType
   else:
     if tok.ident != nil:
-      result = tok.ident.s
+      tok.ident.s
     else:
-      result = ""
-
-proc prettyTok*(tok: TToken): string =
-  if isKeyword(tok.tokType): result = "keyword " & tok.ident.s
-  else: result = tokToStr(tok)
-
-proc printTok*(conf: ConfigRef; tok: TToken) =
-  msgWriteln(conf, $tok.line & ":" & $tok.col & "\t" &
-      TokTypeToStr[tok.tokType] & " " & tokToStr(tok))
-
-proc initToken*(L: var TToken) =
-  L.tokType = tkInvalid
-  L.iNumber = 0
-  L.indent = 0
-  L.strongSpaceA = 0
-  L.literal = ""
-  L.fNumber = 0.0
-  L.base = base10
-  L.ident = nil
-  when defined(nimpretty):
-    L.commentOffsetA = 0
-    L.commentOffsetB = 0
-
-proc fillToken(L: var TToken) =
-  L.tokType = tkInvalid
-  L.iNumber = 0
-  L.indent = 0
-  L.strongSpaceA = 0
-  setLen(L.literal, 0)
-  L.fNumber = 0.0
-  L.base = base10
-  L.ident = nil
-  when defined(nimpretty):
-    L.commentOffsetA = 0
-    L.commentOffsetB = 0
+      ""
+
+proc prettyTok*(tok: Token): string =
+  if isKeyword(tok.tokType): "keyword " & tok.ident.s
+  else: $tok
 
-proc openLexer*(lex: var TLexer, fileIdx: FileIndex, inputstream: PLLStream;
+proc printTok*(conf: ConfigRef; tok: Token) =
+  # xxx factor with toLocation
+  msgWriteln(conf, $tok.line & ":" & $tok.col & "\t" & $tok.tokType & " " & $tok)
+
+proc openLexer*(lex: var Lexer, fileIdx: FileIndex, inputstream: PLLStream;
                  cache: IdentCache; config: ConfigRef) =
   openBaseLexer(lex, inputstream)
-  lex.fileIdx = fileidx
-  lex.indentAhead = - 1
+  lex.fileIdx = fileIdx
+  lex.indentAhead = -1
   lex.currLineIndent = 0
   inc(lex.lineNumber, inputstream.lineOffset)
   lex.cache = cache
@@ -232,36 +187,36 @@ proc openLexer*(lex: var TLexer, fileIdx: FileIndex, inputstream: PLLStream;
     lex.previousToken.fileIndex = fileIdx
   lex.config = config
 
-proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream;
+proc openLexer*(lex: var Lexer, filename: AbsoluteFile, inputstream: PLLStream;
                 cache: IdentCache; config: ConfigRef) =
   openLexer(lex, fileInfoIdx(config, filename), inputstream, cache, config)
 
-proc closeLexer*(lex: var TLexer) =
+proc closeLexer*(lex: var Lexer) =
   if lex.config != nil:
     inc(lex.config.linesCompiled, lex.lineNumber)
   closeBaseLexer(lex)
 
-proc getLineInfo(L: TLexer): TLineInfo =
+proc getLineInfo(L: Lexer): TLineInfo =
   result = newLineInfo(L.fileIdx, L.lineNumber, getColNumber(L, L.bufpos))
 
-proc dispMessage(L: TLexer; info: TLineInfo; msg: TMsgKind; arg: string) =
+proc dispMessage(L: Lexer; info: TLineInfo; msg: TMsgKind; arg: string) =
   if L.errorHandler.isNil:
     msgs.message(L.config, info, msg, arg)
   else:
     L.errorHandler(L.config, info, msg, arg)
 
-proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") =
+proc lexMessage*(L: Lexer, msg: TMsgKind, arg = "") =
   L.dispMessage(getLineInfo(L), msg, arg)
 
-proc lexMessageTok*(L: TLexer, msg: TMsgKind, tok: TToken, arg = "") =
+proc lexMessageTok*(L: Lexer, msg: TMsgKind, tok: Token, arg = "") =
   var info = newLineInfo(L.fileIdx, tok.line, tok.col)
   L.dispMessage(info, msg, arg)
 
-proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
+proc lexMessagePos(L: var Lexer, msg: TMsgKind, pos: int, arg = "") =
   var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart)
   L.dispMessage(info, msg, arg)
 
-proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool =
+proc matchTwoChars(L: Lexer, first: char, second: set[char]): bool =
   result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second)
 
 template tokenBegin(tok, pos) {.dirty.} =
@@ -275,7 +230,6 @@ template tokenEnd(tok, pos) {.dirty.} =
     let colB = getColNumber(L, pos)+1
     if L.fileIdx == L.config.m.trackPos.fileIndex and L.config.m.trackPos.col in colA..colB and
         L.lineNumber == L.config.m.trackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
-      L.cursor = CursorPosition.InToken
       L.config.m.trackPos.col = colA.int16
     colA = 0
   when defined(nimpretty):
@@ -300,312 +254,317 @@ template tokenEndPrevious(tok, pos) =
     let colB = getColNumber(L, pos)
     if L.fileIdx == L.config.m.trackPos.fileIndex and L.config.m.trackPos.col in colA..colB and
         L.lineNumber == L.config.m.trackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
-      L.cursor = CursorPosition.BeforeToken
       L.config.m.trackPos = L.previousToken
       L.config.m.trackPosAttached = true
     colA = 0
   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)
+template eatChar(L: var Lexer, t: var Token, replacementChar: char) =
+  t.literal.add(replacementChar)
   inc(L.bufpos)
 
-template eatChar(L: var TLexer, t: var TToken) =
-  add(t.literal, L.buf[L.bufpos])
+template eatChar(L: var Lexer, t: var Token) =
+  t.literal.add(L.buf[L.bufpos])
   inc(L.bufpos)
 
-proc getNumber(L: var TLexer, result: var TToken) =
-  proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) =
+proc getNumber(L: var Lexer, result: var Token) =
+  proc matchUnderscoreChars(L: var Lexer, tok: var Token, chars: set[char]): Natural =
     var pos = L.bufpos              # use registers for pos, buf
-    var buf = L.buf
+    result = 0
     while true:
-      if buf[pos] in chars:
-        add(tok.literal, buf[pos])
+      if L.buf[pos] in chars:
+        tok.literal.add(L.buf[pos])
         inc(pos)
+        inc(result)
       else:
         break
-      if buf[pos] == '_':
-        if buf[pos+1] notin chars:
+      if L.buf[pos] == '_':
+        if L.buf[pos+1] notin chars:
           lexMessage(L, errGenerated,
-            "only single underscores may occur in a token: '__' is invalid")
+            "only single underscores may occur in a token and token may not " &
+            "end with an underscore: e.g. '1__1' and '1_' are invalid")
           break
-        add(tok.literal, '_')
+        tok.literal.add('_')
         inc(pos)
     L.bufpos = pos
 
-  proc matchChars(L: var TLexer, tok: var TToken, chars: set[char]) =
+  proc matchChars(L: var Lexer, tok: var Token, chars: set[char]) =
     var pos = L.bufpos              # use registers for pos, buf
-    var buf = L.buf
-    while buf[pos] in chars:
-      add(tok.literal, buf[pos])
+    while L.buf[pos] in chars:
+      tok.literal.add(L.buf[pos])
       inc(pos)
     L.bufpos = pos
 
-  proc lexMessageLitNum(L: var TLexer, msg: string, startpos: int) =
+  proc lexMessageLitNum(L: var Lexer, msg: string, startpos: int, msgKind = errGenerated) =
     # Used to get slightly human friendlier err messages.
-    # Note: the erroneous 'O' char in the character set is intentional
-    const literalishChars = {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x', 'o', 'O',
-      'c', 'C', 'b', 'B', '_', '.', '\'', 'd', 'i', 'u'}
+    const literalishChars = {'A'..'Z', 'a'..'z', '0'..'9', '_', '.', '\''}
     var msgPos = L.bufpos
-    var t: TToken
-    t.literal = ""
+    var t = Token(literal: "")
     L.bufpos = startpos # Use L.bufpos as pos because of matchChars
     matchChars(L, t, literalishChars)
     # We must verify +/- specifically so that we're not past the literal
     if  L.buf[L.bufpos] in {'+', '-'} and
         L.buf[L.bufpos - 1] in {'e', 'E'}:
-      add(t.literal, L.buf[L.bufpos])
+      t.literal.add(L.buf[L.bufpos])
       inc(L.bufpos)
       matchChars(L, t, literalishChars)
-    if L.buf[L.bufpos] in {'\'', 'f', 'F', 'd', 'D', 'i', 'I', 'u', 'U'}:
+    if L.buf[L.bufpos] in literalishChars:
+      t.literal.add(L.buf[L.bufpos])
       inc(L.bufpos)
-      add(t.literal, L.buf[L.bufpos])
       matchChars(L, t, {'0'..'9'})
     L.bufpos = msgPos
-    lexMessage(L, errGenerated, msg % t.literal)
+    lexMessage(L, msgKind, msg % t.literal)
 
   var
-    startpos, endpos: int
     xi: BiggestInt
     isBase10 = true
+    numDigits = 0
   const
-    baseCodeChars = {'X', 'x', 'o', 'c', 'C', 'b', 'B'}
+    # 'c', 'C' is deprecated
+    baseCodeChars = {'X', 'x', 'o', 'b', 'B', 'c', 'C'}
     literalishChars = baseCodeChars + {'A'..'F', 'a'..'f', '0'..'9', '_', '\''}
     floatTypes = {tkFloatLit, tkFloat32Lit, tkFloat64Lit, tkFloat128Lit}
   result.tokType = tkIntLit   # int literal until we know better
   result.literal = ""
   result.base = base10
-  startpos = L.bufpos
-  tokenBegin(result, startPos)
+  tokenBegin(result, L.bufpos)
+
+  var isPositive = true
+  if L.buf[L.bufpos] == '-':
+    eatChar(L, result)
+    isPositive = false
+
+  let startpos = L.bufpos
+
+  template setNumber(field, value) =
+    field = (if isPositive: value else: -value)
 
   # First stage: find out base, make verifications, build token literal string
-  if L.buf[L.bufpos] == '0' and L.buf[L.bufpos + 1] in baseCodeChars + {'O'}:
+  # {'c', 'C'} is added for deprecation reasons to provide a clear error message
+  if L.buf[L.bufpos] == '0' and L.buf[L.bufpos + 1] in baseCodeChars + {'c', 'C', 'O'}:
     isBase10 = false
     eatChar(L, result, '0')
     case L.buf[L.bufpos]
+    of 'c', 'C':
+      lexMessageLitNum(L,
+                       "$1 will soon be invalid for oct literals; Use '0o' " &
+                       "for octals. 'c', 'C' prefix",
+                       startpos,
+                       warnDeprecated)
+      eatChar(L, result, 'c')
+      numDigits = matchUnderscoreChars(L, result, {'0'..'7'})
     of 'O':
-      lexMessageLitNum(L, "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.", startpos)
+      lexMessageLitNum(L, "$1 is an invalid int literal; For octal literals " &
+                          "use the '0o' prefix.", startpos)
     of 'x', 'X':
       eatChar(L, result, 'x')
-      matchUnderscoreChars(L, result, {'0'..'9', 'a'..'f', 'A'..'F'})
-    of 'o', 'c', 'C':
-      eatChar(L, result, 'c')
-      matchUnderscoreChars(L, result, {'0'..'7'})
+      numDigits = matchUnderscoreChars(L, result, {'0'..'9', 'a'..'f', 'A'..'F'})
+    of 'o':
+      eatChar(L, result, 'o')
+      numDigits = matchUnderscoreChars(L, result, {'0'..'7'})
     of 'b', 'B':
       eatChar(L, result, 'b')
-      matchUnderscoreChars(L, result, {'0'..'1'})
+      numDigits = matchUnderscoreChars(L, result, {'0'..'1'})
     else:
       internalError(L.config, getLineInfo(L), "getNumber")
+    if numDigits == 0:
+      lexMessageLitNum(L, "invalid number: '$1'", startpos)
   else:
-    matchUnderscoreChars(L, result, {'0'..'9'})
+    discard matchUnderscoreChars(L, result, {'0'..'9'})
     if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}):
       result.tokType = tkFloatLit
       eatChar(L, result, '.')
-      matchUnderscoreChars(L, result, {'0'..'9'})
+      discard matchUnderscoreChars(L, result, {'0'..'9'})
     if L.buf[L.bufpos] in {'e', 'E'}:
       result.tokType = tkFloatLit
-      eatChar(L, result, 'e')
+      eatChar(L, result)
       if L.buf[L.bufpos] in {'+', '-'}:
         eatChar(L, result)
-      matchUnderscoreChars(L, result, {'0'..'9'})
-  endpos = L.bufpos
+      discard matchUnderscoreChars(L, result, {'0'..'9'})
+  let endpos = L.bufpos
 
   # Second stage, find out if there's a datatype suffix and handle it
   var postPos = endpos
+
   if L.buf[postPos] in {'\'', 'f', 'F', 'd', 'D', 'i', 'I', 'u', 'U'}:
+    let errPos = postPos
+    var customLitPossible = false
     if L.buf[postPos] == '\'':
       inc(postPos)
+      customLitPossible = true
 
-    case L.buf[postPos]
-    of 'f', 'F':
-      inc(postPos)
-      if (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'):
-        result.tokType = tkFloat32Lit
-        inc(postPos, 2)
-      elif (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'):
-        result.tokType = tkFloat64Lit
-        inc(postPos, 2)
-      elif (L.buf[postPos] == '1') and
-           (L.buf[postPos + 1] == '2') and
-           (L.buf[postPos + 2] == '8'):
-        result.tokType = tkFloat128Lit
-        inc(postPos, 3)
-      else:   # "f" alone defaults to float32
-        result.tokType = tkFloat32Lit
-    of 'd', 'D':  # ad hoc convenience shortcut for f64
-      inc(postPos)
-      result.tokType = tkFloat64Lit
-    of 'i', 'I':
-      inc(postPos)
-      if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'):
-        result.tokType = tkInt64Lit
-        inc(postPos, 2)
-      elif (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'):
-        result.tokType = tkInt32Lit
-        inc(postPos, 2)
-      elif (L.buf[postPos] == '1') and (L.buf[postPos + 1] == '6'):
-        result.tokType = tkInt16Lit
-        inc(postPos, 2)
-      elif (L.buf[postPos] == '8'):
-        result.tokType = tkInt8Lit
-        inc(postPos)
-      else:
-        lexMessageLitNum(L, "invalid number: '$1'", startpos)
-    of 'u', 'U':
-      inc(postPos)
-      if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'):
-        result.tokType = tkUInt64Lit
-        inc(postPos, 2)
-      elif (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'):
-        result.tokType = tkUInt32Lit
-        inc(postPos, 2)
-      elif (L.buf[postPos] == '1') and (L.buf[postPos + 1] == '6'):
-        result.tokType = tkUInt16Lit
-        inc(postPos, 2)
-      elif (L.buf[postPos] == '8'):
-        result.tokType = tkUInt8Lit
-        inc(postPos)
+    if L.buf[postPos] in SymChars:
+      var suffix = newStringOfCap(10)
+      while true:
+        suffix.add L.buf[postPos]
+        inc postPos
+        if L.buf[postPos] notin SymChars+{'_'}: break
+      let suffixAsLower = suffix.toLowerAscii
+      case suffixAsLower
+      of "f", "f32": result.tokType = tkFloat32Lit
+      of "d", "f64": result.tokType = tkFloat64Lit
+      of "f128": result.tokType = tkFloat128Lit
+      of "i8": result.tokType = tkInt8Lit
+      of "i16": result.tokType = tkInt16Lit
+      of "i32": result.tokType = tkInt32Lit
+      of "i64": result.tokType = tkInt64Lit
+      of "u": result.tokType = tkUIntLit
+      of "u8": result.tokType = tkUInt8Lit
+      of "u16": result.tokType = tkUInt16Lit
+      of "u32": result.tokType = tkUInt32Lit
+      of "u64": result.tokType = tkUInt64Lit
+      elif customLitPossible:
+        # remember the position of the `'` so that the parser doesn't
+        # have to reparse the custom literal:
+        result.iNumber = len(result.literal)
+        result.literal.add '\''
+        result.literal.add suffix
+        result.tokType = tkCustomLit
       else:
-        result.tokType = tkUIntLit
+        lexMessageLitNum(L, "invalid number suffix: '$1'", errPos)
     else:
-      lexMessageLitNum(L, "invalid number: '$1'", startpos)
+      lexMessageLitNum(L, "invalid number suffix: '$1'", errPos)
 
   # Is there still a literalish char awaiting? Then it's an error!
   if  L.buf[postPos] in literalishChars or
      (L.buf[postPos] == '.' and L.buf[postPos + 1] in {'0'..'9'}):
     lexMessageLitNum(L, "invalid number: '$1'", startpos)
 
-  # Third stage, extract actual number
-  L.bufpos = startpos            # restore position
-  var pos: int = startpos
-  try:
-    if (L.buf[pos] == '0') and (L.buf[pos + 1] in baseCodeChars):
-      inc(pos, 2)
-      xi = 0                  # it is a base prefix
-
-      case L.buf[pos - 1]
-      of 'b', 'B':
-        result.base = base2
-        while pos < endpos:
-          if L.buf[pos] != '_':
-            xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0'))
-          inc(pos)
-      of 'o', 'c', 'C':
-        result.base = base8
-        while pos < endpos:
-          if L.buf[pos] != '_':
-            xi = `shl`(xi, 3) or (ord(L.buf[pos]) - ord('0'))
-          inc(pos)
-      of 'x', 'X':
-        result.base = base16
-        while pos < endpos:
-          case L.buf[pos]
-          of '_':
-            inc(pos)
-          of '0'..'9':
-            xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0'))
+  if result.tokType != tkCustomLit:
+    # Third stage, extract actual number
+    L.bufpos = startpos            # restore position
+    var pos = startpos
+    try:
+      if (L.buf[pos] == '0') and (L.buf[pos + 1] in baseCodeChars):
+        inc(pos, 2)
+        xi = 0                  # it is a base prefix
+
+        case L.buf[pos - 1]
+        of 'b', 'B':
+          result.base = base2
+          while pos < endpos:
+            if L.buf[pos] != '_':
+              xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0'))
             inc(pos)
-          of 'a'..'f':
-            xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('a') + 10)
+        # 'c', 'C' is deprecated (a warning is issued elsewhere)
+        of 'o', 'c', 'C':
+          result.base = base8
+          while pos < endpos:
+            if L.buf[pos] != '_':
+              xi = `shl`(xi, 3) or (ord(L.buf[pos]) - ord('0'))
             inc(pos)
-          of 'A'..'F':
-            xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10)
-            inc(pos)
-          else:
-            break
+        of 'x', 'X':
+          result.base = base16
+          while pos < endpos:
+            case L.buf[pos]
+            of '_':
+              inc(pos)
+            of '0'..'9':
+              xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0'))
+              inc(pos)
+            of 'a'..'f':
+              xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('a') + 10)
+              inc(pos)
+            of 'A'..'F':
+              xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10)
+              inc(pos)
+            else:
+              break
+        else:
+          internalError(L.config, getLineInfo(L), "getNumber")
+
+        case result.tokType
+        of tkIntLit, tkInt64Lit: setNumber result.iNumber, xi
+        of tkInt8Lit: setNumber result.iNumber, ashr(xi shl 56, 56)
+        of tkInt16Lit: setNumber result.iNumber, ashr(xi shl 48, 48)
+        of tkInt32Lit: setNumber result.iNumber, ashr(xi shl 32, 32)
+        of tkUIntLit, tkUInt64Lit: setNumber result.iNumber, xi
+        of tkUInt8Lit: setNumber result.iNumber, xi and 0xff
+        of tkUInt16Lit: setNumber result.iNumber, xi and 0xffff
+        of tkUInt32Lit: setNumber result.iNumber, xi and 0xffffffff
+        of tkFloat32Lit:
+          setNumber result.fNumber, (cast[ptr float32](addr(xi)))[]
+          # note: this code is endian neutral!
+          # XXX: Test this on big endian machine!
+        of tkFloat64Lit, tkFloatLit:
+          setNumber result.fNumber, (cast[ptr float64](addr(xi)))[]
+        else: internalError(L.config, getLineInfo(L), "getNumber")
+
+        # Bounds checks. Non decimal literals are allowed to overflow the range of
+        # the datatype as long as their pattern don't overflow _bitwise_, hence
+        # below checks of signed sizes against uint*.high is deliberate:
+        # (0x80'u8 = 128, 0x80'i8 = -128, etc == OK)
+        if result.tokType notin floatTypes:
+          let outOfRange =
+            case result.tokType
+            of tkUInt8Lit, tkUInt16Lit, tkUInt32Lit: result.iNumber != xi
+            of tkInt8Lit:  (xi > BiggestInt(uint8.high))
+            of tkInt16Lit: (xi > BiggestInt(uint16.high))
+            of tkInt32Lit: (xi > BiggestInt(uint32.high))
+            else: false
+
+          if outOfRange:
+            #echo "out of range num: ", result.iNumber, " vs ", xi
+            lexMessageLitNum(L, "number out of range: '$1'", startpos)
+
       else:
-        internalError(L.config, getLineInfo(L), "getNumber")
-
-      case result.tokType
-      of tkIntLit, tkInt64Lit: result.iNumber = xi
-      of tkInt8Lit: result.iNumber = BiggestInt(int8(toU8(int(xi))))
-      of tkInt16Lit: result.iNumber = BiggestInt(int16(toU16(int(xi))))
-      of tkInt32Lit: result.iNumber = BiggestInt(int32(toU32(int64(xi))))
-      of tkUIntLit, tkUInt64Lit: result.iNumber = xi
-      of tkUInt8Lit: result.iNumber = BiggestInt(uint8(toU8(int(xi))))
-      of tkUInt16Lit: result.iNumber = BiggestInt(uint16(toU16(int(xi))))
-      of tkUInt32Lit: result.iNumber = BiggestInt(uint32(toU32(int64(xi))))
-      of tkFloat32Lit:
-        result.fNumber = (cast[PFloat32](addr(xi)))[]
-        # note: this code is endian neutral!
-        # XXX: Test this on big endian machine!
-      of tkFloat64Lit, tkFloatLit:
-        result.fNumber = (cast[PFloat64](addr(xi)))[]
-      else: internalError(L.config, getLineInfo(L), "getNumber")
-
-      # Bounds checks. Non decimal literals are allowed to overflow the range of
-      # the datatype as long as their pattern don't overflow _bitwise_, hence
-      # below checks of signed sizes against uint*.high is deliberate:
-      # (0x80'u8 = 128, 0x80'i8 = -128, etc == OK)
-      if result.tokType notin floatTypes:
-        let outOfRange = case result.tokType:
-        of tkUInt8Lit, tkUInt16Lit, tkUInt32Lit: result.iNumber != xi
-        of tkInt8Lit: (xi > BiggestInt(uint8.high))
-        of tkInt16Lit: (xi > BiggestInt(uint16.high))
-        of tkInt32Lit: (xi > BiggestInt(uint32.high))
-        else: false
+        case result.tokType
+        of floatTypes:
+          result.fNumber = parseFloat(result.literal)
+        of tkUInt64Lit, tkUIntLit:
+          var iNumber: uint64 = uint64(0)
+          var len: int = 0
+          try:
+            len = parseBiggestUInt(result.literal, iNumber)
+          except ValueError:
+            raise newException(OverflowDefect, "number out of range: " & result.literal)
+          if len != result.literal.len:
+            raise newException(ValueError, "invalid integer: " & result.literal)
+          result.iNumber = cast[int64](iNumber)
+        else:
+          var iNumber: int64 = int64(0)
+          var len: int = 0
+          try:
+            len = parseBiggestInt(result.literal, iNumber)
+          except ValueError:
+            raise newException(OverflowDefect, "number out of range: " & result.literal)
+          if len != result.literal.len:
+            raise newException(ValueError, "invalid integer: " & result.literal)
+          result.iNumber = iNumber
+
+        # Explicit bounds checks.
+        let outOfRange =
+          case result.tokType
+          of tkInt8Lit: result.iNumber > int8.high or result.iNumber < int8.low
+          of tkUInt8Lit: result.iNumber > BiggestInt(uint8.high) or result.iNumber < 0
+          of tkInt16Lit: result.iNumber > int16.high or result.iNumber < int16.low
+          of tkUInt16Lit: result.iNumber > BiggestInt(uint16.high) or result.iNumber < 0
+          of tkInt32Lit: result.iNumber > int32.high or result.iNumber < int32.low
+          of tkUInt32Lit: result.iNumber > BiggestInt(uint32.high) or result.iNumber < 0
+          else: false
 
         if outOfRange:
-          #echo "out of range num: ", result.iNumber, " vs ", xi
           lexMessageLitNum(L, "number out of range: '$1'", startpos)
 
-    else:
-      case result.tokType
-      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
-      else:
-        result.iNumber = parseBiggestInt(result.literal)
+      # Promote int literal to int64? Not always necessary, but more consistent
+      if result.tokType == tkIntLit:
+        if result.iNumber > high(int32) or result.iNumber < low(int32):
+          result.tokType = tkInt64Lit
 
-      # Explicit bounds checks
-      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))
-        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)):
-        result.tokType = tkInt64Lit
-
-  except ValueError:
-    lexMessageLitNum(L, "invalid number: '$1'", startpos)
-  except OverflowError, RangeError:
-    lexMessageLitNum(L, "number out of range: '$1'", startpos)
+    except ValueError:
+      lexMessageLitNum(L, "invalid number: '$1'", startpos)
+    except OverflowDefect, RangeDefect:
+      lexMessageLitNum(L, "number out of range: '$1'", startpos)
   tokenEnd(result, postPos-1)
   L.bufpos = postPos
 
-proc handleHexChar(L: var TLexer, xi: var int) =
+proc handleHexChar(L: var Lexer, xi: var int; position: range[0..4]) =
+  template invalid() =
+    lexMessage(L, errGenerated,
+      "expected a hex digit, but found: " & L.buf[L.bufpos] &
+        "; maybe prepend with 0")
+
   case L.buf[L.bufpos]
   of '0'..'9':
     xi = (xi shl 4) or (ord(L.buf[L.bufpos]) - ord('0'))
@@ -616,104 +575,142 @@ proc handleHexChar(L: var TLexer, xi: var int) =
   of 'A'..'F':
     xi = (xi shl 4) or (ord(L.buf[L.bufpos]) - ord('A') + 10)
     inc(L.bufpos)
-  else: discard
+  of '"', '\'':
+    if position <= 1: invalid()
+    # do not progress the bufpos here.
+    if position == 0: inc(L.bufpos)
+  else:
+    invalid()
+    # Need to progress for `nim check`
+    inc(L.bufpos)
 
-proc handleDecChars(L: var TLexer, xi: var int) =
+proc handleDecChars(L: var Lexer, xi: var int) =
   while L.buf[L.bufpos] in {'0'..'9'}:
     xi = (xi * 10) + (ord(L.buf[L.bufpos]) - ord('0'))
     inc(L.bufpos)
 
-proc getEscapedChar(L: var TLexer, tok: var TToken) =
+proc addUnicodeCodePoint(s: var string, i: int) =
+  let i = cast[uint](i)
+  # inlined toUTF-8 to avoid unicode and strutils dependencies.
+  let pos = s.len
+  if i <= 127:
+    s.setLen(pos+1)
+    s[pos+0] = chr(i)
+  elif i <= 0x07FF:
+    s.setLen(pos+2)
+    s[pos+0] = chr((i shr 6) or 0b110_00000)
+    s[pos+1] = chr((i and ones(6)) or 0b10_0000_00)
+  elif i <= 0xFFFF:
+    s.setLen(pos+3)
+    s[pos+0] = chr(i shr 12 or 0b1110_0000)
+    s[pos+1] = chr(i shr 6 and ones(6) or 0b10_0000_00)
+    s[pos+2] = chr(i and ones(6) or 0b10_0000_00)
+  elif i <= 0x001FFFFF:
+    s.setLen(pos+4)
+    s[pos+0] = chr(i shr 18 or 0b1111_0000)
+    s[pos+1] = chr(i shr 12 and ones(6) or 0b10_0000_00)
+    s[pos+2] = chr(i shr 6 and ones(6) or 0b10_0000_00)
+    s[pos+3] = chr(i and ones(6) or 0b10_0000_00)
+  elif i <= 0x03FFFFFF:
+    s.setLen(pos+5)
+    s[pos+0] = chr(i shr 24 or 0b111110_00)
+    s[pos+1] = chr(i shr 18 and ones(6) or 0b10_0000_00)
+    s[pos+2] = chr(i shr 12 and ones(6) or 0b10_0000_00)
+    s[pos+3] = chr(i shr 6 and ones(6) or 0b10_0000_00)
+    s[pos+4] = chr(i and ones(6) or 0b10_0000_00)
+  elif i <= 0x7FFFFFFF:
+    s.setLen(pos+6)
+    s[pos+0] = chr(i shr 30 or 0b1111110_0)
+    s[pos+1] = chr(i shr 24 and ones(6) or 0b10_0000_00)
+    s[pos+2] = chr(i shr 18 and ones(6) or 0b10_0000_00)
+    s[pos+3] = chr(i shr 12 and ones(6) or 0b10_0000_00)
+    s[pos+4] = chr(i shr 6 and ones(6) or 0b10_0000_00)
+    s[pos+5] = chr(i and ones(6) or 0b10_0000_00)
+
+proc getEscapedChar(L: var Lexer, tok: var Token) =
   inc(L.bufpos)               # skip '\'
   case L.buf[L.bufpos]
   of 'n', 'N':
-    if L.config.oldNewlines:
-      if tok.tokType == tkCharLit:
-        lexMessage(L, errGenerated, "\\n not allowed in character literal")
-      add(tok.literal, L.config.target.tnl)
-    else:
-      add(tok.literal, '\L')
+    tok.literal.add('\L')
     inc(L.bufpos)
   of 'p', 'P':
     if tok.tokType == tkCharLit:
       lexMessage(L, errGenerated, "\\p not allowed in character literal")
-    add(tok.literal, L.config.target.tnl)
+    tok.literal.add(L.config.target.tnl)
     inc(L.bufpos)
   of 'r', 'R', 'c', 'C':
-    add(tok.literal, CR)
+    tok.literal.add(CR)
     inc(L.bufpos)
   of 'l', 'L':
-    add(tok.literal, LF)
+    tok.literal.add(LF)
     inc(L.bufpos)
   of 'f', 'F':
-    add(tok.literal, FF)
+    tok.literal.add(FF)
     inc(L.bufpos)
   of 'e', 'E':
-    add(tok.literal, ESC)
+    tok.literal.add(ESC)
     inc(L.bufpos)
   of 'a', 'A':
-    add(tok.literal, BEL)
+    tok.literal.add(BEL)
     inc(L.bufpos)
   of 'b', 'B':
-    add(tok.literal, BACKSPACE)
+    tok.literal.add(BACKSPACE)
     inc(L.bufpos)
   of 'v', 'V':
-    add(tok.literal, VT)
+    tok.literal.add(VT)
     inc(L.bufpos)
   of 't', 'T':
-    add(tok.literal, '\t')
+    tok.literal.add('\t')
     inc(L.bufpos)
   of '\'', '\"':
-    add(tok.literal, L.buf[L.bufpos])
+    tok.literal.add(L.buf[L.bufpos])
     inc(L.bufpos)
   of '\\':
-    add(tok.literal, '\\')
+    tok.literal.add('\\')
     inc(L.bufpos)
-  of 'x', 'X', 'u', 'U':
-    var tp = L.buf[L.bufpos]
+  of 'x', 'X':
     inc(L.bufpos)
     var xi = 0
-    handleHexChar(L, xi)
-    handleHexChar(L, xi)
-    if tp in {'u', 'U'}:
-      handleHexChar(L, xi)
-      handleHexChar(L, xi)
-      # inlined toUTF-8 to avoid unicode and strutils dependencies.
-      if xi <=% 127:
-        add(tok.literal, xi.char )
-      elif xi <=% 0x07FF:
-        add(tok.literal, ((xi shr 6) or 0b110_00000).char )
-        add(tok.literal, ((xi and ones(6)) or 0b10_0000_00).char )
-      elif xi <=% 0xFFFF:
-        add(tok.literal, (xi shr 12 or 0b1110_0000).char )
-        add(tok.literal, (xi shr 6 and ones(6) or 0b10_0000_00).char )
-        add(tok.literal, (xi and ones(6) or 0b10_0000_00).char )
-      else: # value is 0xFFFF
-        add(tok.literal, "\xef\xbf\xbf" )
+    handleHexChar(L, xi, 1)
+    handleHexChar(L, xi, 2)
+    tok.literal.add(chr(xi))
+  of 'u', 'U':
+    if tok.tokType == tkCharLit:
+      lexMessage(L, errGenerated, "\\u not allowed in character literal")
+    inc(L.bufpos)
+    var xi = 0
+    if L.buf[L.bufpos] == '{':
+      inc(L.bufpos)
+      var start = L.bufpos
+      while L.buf[L.bufpos] != '}':
+        handleHexChar(L, xi, 0)
+      if start == L.bufpos:
+        lexMessage(L, errGenerated,
+          "Unicode codepoint cannot be empty")
+      inc(L.bufpos)
+      if xi > 0x10FFFF:
+        let hex = ($L.buf)[start..L.bufpos-2]
+        lexMessage(L, errGenerated,
+          "Unicode codepoint must be lower than 0x10FFFF, but was: " & hex)
     else:
-      add(tok.literal, chr(xi))
+      handleHexChar(L, xi, 1)
+      handleHexChar(L, xi, 2)
+      handleHexChar(L, xi, 3)
+      handleHexChar(L, xi, 4)
+    addUnicodeCodePoint(tok.literal, xi)
   of '0'..'9':
     if matchTwoChars(L, '0', {'0'..'9'}):
       lexMessage(L, warnOctalEscape)
     var xi = 0
     handleDecChars(L, xi)
-    if (xi <= 255): add(tok.literal, chr(xi))
+    if (xi <= 255): tok.literal.add(chr(xi))
     else: lexMessage(L, errGenerated, "invalid character constant")
   else: lexMessage(L, errGenerated, "invalid character constant")
 
-proc newString(s: cstring, len: int): string =
-  ## XXX, how come there is no support for this?
-  result = newString(len)
-  for i in 0 ..< len:
-    result[i] = s[i]
-
-proc handleCRLF(L: var TLexer, pos: int): int =
+proc handleCRLF(L: var Lexer, pos: int): int =
   template registerLine =
     let col = L.getColNumber(pos)
 
-    if col > MaxLineLength:
-      lexMessagePos(L, hintLineTooLong, pos)
-
   case L.buf[pos]
   of CR:
     registerLine()
@@ -723,37 +720,40 @@ proc handleCRLF(L: var TLexer, pos: int): int =
     result = nimlexbase.handleLF(L, pos)
   else: result = pos
 
-proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
+type
+  StringMode = enum
+    normal,
+    raw,
+    generalized
+
+proc getString(L: var Lexer, tok: var Token, mode: StringMode) =
   var pos = L.bufpos
-  var buf = L.buf                 # put `buf` in a register
   var line = L.lineNumber         # save linenumber for better error message
-  tokenBegin(tok, pos)
+  tokenBegin(tok, pos - ord(mode == raw))
   inc pos # skip "
-  if buf[pos] == '\"' and buf[pos+1] == '\"':
+  if L.buf[pos] == '\"' and L.buf[pos+1] == '\"':
     tok.tokType = tkTripleStrLit # long string literal:
     inc(pos, 2)               # skip ""
     # skip leading newline:
-    if buf[pos] in {' ', '\t'}:
+    if L.buf[pos] in {' ', '\t'}:
       var newpos = pos+1
-      while buf[newpos] in {' ', '\t'}: inc newpos
-      if buf[newpos] in {CR, LF}: pos = newpos
+      while L.buf[newpos] in {' ', '\t'}: inc newpos
+      if L.buf[newpos] in {CR, LF}: pos = newpos
     pos = handleCRLF(L, pos)
-    buf = L.buf
     while true:
-      case buf[pos]
+      case L.buf[pos]
       of '\"':
-        if buf[pos+1] == '\"' and buf[pos+2] == '\"' and
-            buf[pos+3] != '\"':
+        if L.buf[pos+1] == '\"' and L.buf[pos+2] == '\"' and
+            L.buf[pos+3] != '\"':
           tokenEndIgnore(tok, pos+2)
           L.bufpos = pos + 3 # skip the three """
           break
-        add(tok.literal, '\"')
+        tok.literal.add('\"')
         inc(pos)
       of CR, LF:
         tokenEndIgnore(tok, pos)
         pos = handleCRLF(L, pos)
-        buf = L.buf
-        add(tok.literal, "\n")
+        tok.literal.add("\n")
       of nimlexbase.EndOfFile:
         tokenEndIgnore(tok, pos)
         var line2 = L.lineNumber
@@ -763,18 +763,18 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
         L.bufpos = pos
         break
       else:
-        add(tok.literal, buf[pos])
+        tok.literal.add(L.buf[pos])
         inc(pos)
   else:
     # ordinary string literal
-    if rawMode: tok.tokType = tkRStrLit
+    if mode != normal: tok.tokType = tkRStrLit
     else: tok.tokType = tkStrLit
     while true:
-      var c = buf[pos]
+      let c = L.buf[pos]
       if c == '\"':
-        if rawMode and buf[pos+1] == '\"':
+        if mode != normal and L.buf[pos+1] == '\"':
           inc(pos, 2)
-          add(tok.literal, '"')
+          tok.literal.add('"')
         else:
           tokenEndIgnore(tok, pos)
           inc(pos) # skip '"'
@@ -783,145 +783,243 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
         tokenEndIgnore(tok, pos)
         lexMessage(L, errGenerated, "closing \" expected")
         break
-      elif (c == '\\') and not rawMode:
+      elif (c == '\\') and mode == normal:
         L.bufpos = pos
         getEscapedChar(L, tok)
         pos = L.bufpos
       else:
-        add(tok.literal, c)
+        tok.literal.add(c)
         inc(pos)
     L.bufpos = pos
 
-proc getCharacter(L: var TLexer, tok: var TToken) =
+proc getCharacter(L: var Lexer; tok: var Token) =
   tokenBegin(tok, L.bufpos)
+  let startPos = L.bufpos
   inc(L.bufpos)               # skip '
-  var c = L.buf[L.bufpos]
+  let c = L.buf[L.bufpos]
   case c
-  of '\0'..pred(' '), '\'': lexMessage(L, errGenerated, "invalid character literal")
+  of '\0'..pred(' '), '\'':
+    lexMessage(L, errGenerated, "invalid character literal")
+    tok.literal = $c
   of '\\': getEscapedChar(L, tok)
   else:
     tok.literal = $c
     inc(L.bufpos)
-  if L.buf[L.bufpos] != '\'':
-    lexMessage(L, errGenerated, "missing closing ' for character literal")
-  tokenEndIgnore(tok, L.bufpos)
-  inc(L.bufpos)               # skip '
+  if L.buf[L.bufpos] == '\'':
+    tokenEndIgnore(tok, L.bufpos)
+    inc(L.bufpos)               # skip '
+  else:
+    if startPos > 0 and L.buf[startPos-1] == '`':
+      tok.literal = "'"
+      L.bufpos = startPos+1
+    else:
+      lexMessage(L, errGenerated, "missing closing ' for character literal")
+    tokenEndIgnore(tok, L.bufpos)
+
+const
+  UnicodeOperatorStartChars = {'\226', '\194', '\195'}
+    # the allowed unicode characters ("∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔")
+    # all start with one of these.
+
+type
+  UnicodeOprPred = enum
+    Mul, Add
+
+proc unicodeOprLen(buf: cstring; pos: int): (int8, UnicodeOprPred) =
+  template m(len): untyped = (int8(len), Mul)
+  template a(len): untyped = (int8(len), Add)
+  result = 0.m
+  case buf[pos]
+  of '\226':
+    if buf[pos+1] == '\136':
+      if buf[pos+2] == '\152': result = 3.m # ∘
+      elif buf[pos+2] == '\153': result = 3.m # ∙
+      elif buf[pos+2] == '\167': result = 3.m # ∧
+      elif buf[pos+2] == '\168': result = 3.a # ∨
+      elif buf[pos+2] == '\169': result = 3.m # ∩
+      elif buf[pos+2] == '\170': result = 3.a # ∪
+    elif buf[pos+1] == '\138':
+      if buf[pos+2] == '\147': result = 3.m # ⊓
+      elif buf[pos+2] == '\148': result = 3.a # ⊔
+      elif buf[pos+2] == '\149': result = 3.a # ⊕
+      elif buf[pos+2] == '\150': result = 3.a # ⊖
+      elif buf[pos+2] == '\151': result = 3.m # ⊗
+      elif buf[pos+2] == '\152': result = 3.m # ⊘
+      elif buf[pos+2] == '\153': result = 3.m # ⊙
+      elif buf[pos+2] == '\155': result = 3.m # ⊛
+      elif buf[pos+2] == '\158': result = 3.a # ⊞
+      elif buf[pos+2] == '\159': result = 3.a # ⊟
+      elif buf[pos+2] == '\160': result = 3.m # ⊠
+      elif buf[pos+2] == '\161': result = 3.m # ⊡
+    elif buf[pos+1] == '\152' and buf[pos+2] == '\133': result = 3.m # ★
+  of '\194':
+    if buf[pos+1] == '\177': result = 2.a # ±
+  of '\195':
+    if buf[pos+1] == '\151': result = 2.m # ×
+  else:
+    discard
 
-proc getSymbol(L: var TLexer, tok: var TToken) =
+proc getSymbol(L: var Lexer, tok: var Token) =
   var h: Hash = 0
   var pos = L.bufpos
-  var buf = L.buf
   tokenBegin(tok, pos)
+  var suspicious = false
   while true:
-    var c = buf[pos]
+    var c = L.buf[pos]
     case c
-    of 'a'..'z', '0'..'9', '\x80'..'\xFF':
+    of 'a'..'z', '0'..'9':
       h = h !& ord(c)
       inc(pos)
     of 'A'..'Z':
       c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
       h = h !& ord(c)
       inc(pos)
+      suspicious = true
     of '_':
-      if buf[pos+1] notin SymChars:
+      if L.buf[pos+1] notin SymChars:
         lexMessage(L, errGenerated, "invalid token: trailing underscore")
         break
       inc(pos)
+      suspicious = true
+    of '\x80'..'\xFF':
+      if c in UnicodeOperatorStartChars and unicodeOprLen(L.buf, pos)[0] != 0:
+        break
+      else:
+        h = h !& ord(c)
+        inc(pos)
     else: break
   tokenEnd(tok, pos-1)
   h = !$h
-  tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
-  L.bufpos = pos
+  tok.ident = L.cache.getIdent(cast[cstring](addr(L.buf[L.bufpos])), pos - L.bufpos, h)
   if (tok.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or
       (tok.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)):
     tok.tokType = tkSymbol
   else:
-    tok.tokType = TTokType(tok.ident.id + ord(tkSymbol))
+    tok.tokType = TokType(tok.ident.id + ord(tkSymbol))
+    if suspicious and {optStyleHint, optStyleError} * L.config.globalOptions != {}:
+      lintReport(L.config, getLineInfo(L), tok.ident.s.normalize, tok.ident.s)
+  L.bufpos = pos
+
 
-proc endOperator(L: var TLexer, tok: var TToken, pos: int,
+proc endOperator(L: var Lexer, tok: var Token, pos: int,
                  hash: Hash) {.inline.} =
   var h = !$hash
-  tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
+  tok.ident = L.cache.getIdent(cast[cstring](addr(L.buf[L.bufpos])), pos - L.bufpos, h)
   if (tok.ident.id < oprLow) or (tok.ident.id > oprHigh): tok.tokType = tkOpr
-  else: tok.tokType = TTokType(tok.ident.id - oprLow + ord(tkColon))
+  else: tok.tokType = TokType(tok.ident.id - oprLow + ord(tkColon))
   L.bufpos = pos
 
-proc getOperator(L: var TLexer, tok: var TToken) =
+proc getOperator(L: var Lexer, tok: var Token) =
   var pos = L.bufpos
-  var buf = L.buf
   tokenBegin(tok, pos)
   var h: Hash = 0
   while true:
-    var c = buf[pos]
-    if c notin OpChars: break
-    h = h !& ord(c)
-    inc(pos)
+    let c = L.buf[pos]
+    if c in OpChars:
+      h = h !& ord(c)
+      inc(pos)
+    elif c in UnicodeOperatorStartChars:
+      let oprLen = unicodeOprLen(L.buf, pos)[0]
+      if oprLen == 0: break
+      for i in 0..<oprLen:
+        h = h !& ord(L.buf[pos])
+        inc pos
+    else:
+      break
   endOperator(L, tok, pos, h)
   tokenEnd(tok, pos-1)
   # advance pos but don't store it in L.bufpos so the next token (which might
   # be an operator too) gets the preceding spaces:
-  tok.strongSpaceB = 0
-  while buf[pos] == ' ':
+  tok.spacing = tok.spacing - {tsTrailing, tsEof}
+  var trailing = false
+  while L.buf[pos] == ' ':
     inc pos
-    inc tok.strongSpaceB
-  if buf[pos] in {CR, LF, nimlexbase.EndOfFile}:
-    tok.strongSpaceB = -1
-
-proc newlineFollows*(L: TLexer): bool =
-  var pos = L.bufpos
-  var buf = L.buf
-  while true:
-    case buf[pos]
-    of ' ', '\t':
-      inc(pos)
-    of CR, LF:
-      result = true
-      break
-    of '#':
-      inc(pos)
-      if buf[pos] == '#': inc(pos)
-      if buf[pos] != '[': return true
-    else:
-      break
-
-proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
+    trailing = true
+  if L.buf[pos] in {CR, LF, nimlexbase.EndOfFile}:
+    tok.spacing.incl(tsEof)
+  elif trailing:
+    tok.spacing.incl(tsTrailing)
+
+proc getPrecedence*(tok: Token): int =
+  ## Calculates the precedence of the given token.
+  const
+    MulPred = 9
+    PlusPred = 8
+  case tok.tokType
+  of tkOpr:
+    let relevantChar = tok.ident.s[0]
+
+    # arrow like?
+    if tok.ident.s.len > 1 and tok.ident.s[^1] == '>' and
+      tok.ident.s[^2] in {'-', '~', '='}: return 0
+
+    template considerAsgn(value: untyped) =
+      result = if tok.ident.s[^1] == '=': 1 else: value
+
+    case relevantChar
+    of '$', '^': considerAsgn(10)
+    of '*', '%', '/', '\\': considerAsgn(MulPred)
+    of '~': result = 8
+    of '+', '-', '|': considerAsgn(PlusPred)
+    of '&': considerAsgn(7)
+    of '=', '<', '>', '!': result = 5
+    of '.': considerAsgn(6)
+    of '?': result = 2
+    of UnicodeOperatorStartChars:
+      if tok.ident.s[^1] == '=':
+        result = 1
+      else:
+        let (len, pred) = unicodeOprLen(cstring(tok.ident.s), 0)
+        if len != 0:
+          result = if pred == Mul: MulPred else: PlusPred
+        else:
+          result = 2
+    else: considerAsgn(2)
+  of tkDiv, tkMod, tkShl, tkShr: result = 9
+  of tkDotDot: result = 6
+  of tkIn, tkNotin, tkIs, tkIsnot, tkOf, tkAs, tkFrom: result = 5
+  of tkAnd: result = 4
+  of tkOr, tkXor, tkPtr, tkRef: result = 3
+  else: return -10
+
+proc skipMultiLineComment(L: var Lexer; tok: var Token; start: int;
                           isDoc: bool) =
   var pos = start
-  var buf = L.buf
   var toStrip = 0
   tokenBegin(tok, pos)
   # detect the amount of indentation:
   if isDoc:
     toStrip = getColNumber(L, pos)
-    while buf[pos] == ' ': inc pos
-    if buf[pos] in {CR, LF}:
+    while L.buf[pos] == ' ':
+      inc pos
+      inc toStrip
+    while L.buf[pos] in {CR, LF}:  # skip blank lines
       pos = handleCRLF(L, pos)
-      buf = L.buf
       toStrip = 0
-      while buf[pos] == ' ':
+      while L.buf[pos] == ' ':
         inc pos
         inc toStrip
   var nesting = 0
   while true:
-    case buf[pos]
+    case L.buf[pos]
     of '#':
       if isDoc:
-        if buf[pos+1] == '#' and buf[pos+2] == '[':
+        if L.buf[pos+1] == '#' and L.buf[pos+2] == '[':
           inc nesting
         tok.literal.add '#'
-      elif buf[pos+1] == '[':
+      elif L.buf[pos+1] == '[':
         inc nesting
       inc pos
     of ']':
       if isDoc:
-        if buf[pos+1] == '#' and buf[pos+2] == '#':
+        if L.buf[pos+1] == '#' and L.buf[pos+2] == '#':
           if nesting == 0:
             tokenEndIgnore(tok, pos+2)
             inc(pos, 3)
             break
           dec nesting
         tok.literal.add ']'
-      elif buf[pos+1] == '#':
+      elif L.buf[pos+1] == '#':
         if nesting == 0:
           tokenEndIgnore(tok, pos+1)
           inc(pos, 2)
@@ -931,14 +1029,12 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
     of CR, LF:
       tokenEndIgnore(tok, pos)
       pos = handleCRLF(L, pos)
-      buf = L.buf
       # strip leading whitespace:
       when defined(nimpretty): tok.literal.add "\L"
       if isDoc:
         when not defined(nimpretty): tok.literal.add "\n"
-        inc tok.iNumber
         var c = toStrip
-        while buf[pos] == ' ' and c > 0:
+        while L.buf[pos] == ' ' and c > 0:
           inc pos
           dec c
     of nimlexbase.EndOfFile:
@@ -946,57 +1042,57 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
       lexMessagePos(L, errGenerated, pos, "end of multiline comment expected")
       break
     else:
-      if isDoc or defined(nimpretty): tok.literal.add buf[pos]
+      if isDoc or defined(nimpretty): tok.literal.add L.buf[pos]
       inc(pos)
   L.bufpos = pos
   when defined(nimpretty):
     tok.commentOffsetB = L.offsetBase + pos - 1
 
-proc scanComment(L: var TLexer, tok: var TToken) =
+proc scanComment(L: var Lexer, tok: var Token) =
   var pos = L.bufpos
-  var buf = L.buf
   tok.tokType = tkComment
-  # iNumber contains the number of '\n' in the token
-  tok.iNumber = 0
-  assert buf[pos+1] == '#'
+  assert L.buf[pos+1] == '#'
   when defined(nimpretty):
-    tok.commentOffsetA = L.offsetBase + pos - 1
+    tok.commentOffsetA = L.offsetBase + pos
 
-  if buf[pos+2] == '[':
+  if L.buf[pos+2] == '[':
     skipMultiLineComment(L, tok, pos+3, true)
     return
   tokenBegin(tok, pos)
   inc(pos, 2)
 
   var toStrip = 0
-  while buf[pos] == ' ':
-    inc pos
-    inc toStrip
+  var stripInit = false
 
   while true:
-    var lastBackslash = -1
-    while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}:
-      if buf[pos] == '\\': lastBackslash = pos+1
-      add(tok.literal, buf[pos])
+    if not stripInit:  # find baseline indentation inside comment
+      while L.buf[pos] == ' ':
+        inc pos
+        inc toStrip
+      if L.buf[pos] in {CR, LF}:  # don't set toStrip in blank comment lines
+        toStrip = 0
+      else:  # found first non-whitespace character
+        stripInit = true
+    while L.buf[pos] notin {CR, LF, nimlexbase.EndOfFile}:
+      tok.literal.add(L.buf[pos])
       inc(pos)
     tokenEndIgnore(tok, pos)
     pos = handleCRLF(L, pos)
-    buf = L.buf
     var indent = 0
-    while buf[pos] == ' ':
+    while L.buf[pos] == ' ':
       inc(pos)
       inc(indent)
 
-    if buf[pos] == '#' and buf[pos+1] == '#':
+    if L.buf[pos] == '#' and L.buf[pos+1] == '#':
       tok.literal.add "\n"
       inc(pos, 2)
-      var c = toStrip
-      while buf[pos] == ' ' and c > 0:
-        inc pos
-        dec c
-      inc tok.iNumber
+      if stripInit:
+        var c = toStrip
+        while L.buf[pos] == ' ' and c > 0:
+          inc pos
+          dec c
     else:
-      if buf[pos] > ' ':
+      if L.buf[pos] > ' ':
         L.indentAhead = indent
       tokenEndIgnore(tok, pos)
       break
@@ -1004,58 +1100,65 @@ proc scanComment(L: var TLexer, tok: var TToken) =
   when defined(nimpretty):
     tok.commentOffsetB = L.offsetBase + pos - 1
 
-proc skip(L: var TLexer, tok: var TToken) =
+proc skip(L: var Lexer, tok: var Token) =
   var pos = L.bufpos
-  var buf = L.buf
   tokenBegin(tok, pos)
-  tok.strongSpaceA = 0
+  tok.spacing.excl(tsLeading)
   when defined(nimpretty):
     var hasComment = false
+    var commentIndent = L.currLineIndent
     tok.commentOffsetA = L.offsetBase + pos
     tok.commentOffsetB = tok.commentOffsetA
+    tok.line = -1
   while true:
-    case buf[pos]
+    case L.buf[pos]
     of ' ':
       inc(pos)
-      inc(tok.strongSpaceA)
+      tok.spacing.incl(tsLeading)
     of '\t':
-      if not L.allowTabs: lexMessagePos(L, errGenerated, pos, "tabulators are not allowed")
+      lexMessagePos(L, errGenerated, pos, "tabs are not allowed, use spaces instead")
       inc(pos)
     of CR, LF:
       tokenEndPrevious(tok, pos)
-      when defined(nimpretty):
-        # we are not yet in a comment, so update the comment token's line information:
-        if not hasComment: inc tok.line
       pos = handleCRLF(L, pos)
-      buf = L.buf
       var indent = 0
       while true:
-        if buf[pos] == ' ':
+        if L.buf[pos] == ' ':
           inc(pos)
           inc(indent)
-        elif buf[pos] == '#' and buf[pos+1] == '[':
-          when defined(nimpretty): hasComment = true
+        elif L.buf[pos] == '#' and L.buf[pos+1] == '[':
+          when defined(nimpretty):
+            hasComment = true
+            if tok.line < 0:
+              tok.line = L.lineNumber
+              commentIndent = indent
           skipMultiLineComment(L, tok, pos+2, false)
           pos = L.bufpos
-          buf = L.buf
         else:
           break
-      tok.strongSpaceA = 0
-      if buf[pos] > ' ' and (buf[pos] != '#' or buf[pos+1] == '#'):
+      tok.spacing.excl(tsLeading)
+      when defined(nimpretty):
+        if L.buf[pos] == '#' and tok.line < 0: commentIndent = indent
+      if L.buf[pos] > ' ' and (L.buf[pos] != '#' or L.buf[pos+1] == '#'):
         tok.indent = indent
         L.currLineIndent = indent
         break
     of '#':
       # do not skip documentation comment:
-      if buf[pos+1] == '#': break
-      when defined(nimpretty): hasComment = true
-      if buf[pos+1] == '[':
+      if L.buf[pos+1] == '#': break
+      when defined(nimpretty):
+        hasComment = true
+        if tok.line < 0:
+          tok.line = L.lineNumber
+
+      if L.buf[pos+1] == '[':
         skipMultiLineComment(L, tok, pos+2, false)
         pos = L.bufpos
-        buf = L.buf
       else:
         tokenBegin(tok, pos)
-        while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos)
+        while L.buf[pos] notin {CR, LF, nimlexbase.EndOfFile}:
+          when defined(nimpretty): tok.literal.add L.buf[pos]
+          inc(pos)
         tokenEndIgnore(tok, pos+1)
         when defined(nimpretty):
           tok.commentOffsetB = L.offsetBase + pos + 1
@@ -1067,20 +1170,21 @@ proc skip(L: var TLexer, tok: var TToken) =
     if hasComment:
       tok.commentOffsetB = L.offsetBase + pos - 1
       tok.tokType = tkComment
-    if gIndentationWidth <= 0:
-      gIndentationWidth = tok.indent
+      tok.indent = commentIndent
 
-proc rawGetTok*(L: var TLexer, tok: var TToken) =
+proc rawGetTok*(L: var Lexer, tok: var Token) =
   template atTokenEnd() {.dirty.} =
     when defined(nimsuggest):
+      L.previousTokenEnd.line = L.tokenEnd.line
+      L.previousTokenEnd.col = L.tokenEnd.col
+      L.tokenEnd.line = tok.line.uint16
+      L.tokenEnd.col = getColNumber(L, L.bufpos).int16
       # we attach the cursor to the last *strong* token
       if tok.tokType notin weakTokens:
         L.previousToken.line = tok.line.uint16
         L.previousToken.col = tok.col.int16
 
-  when defined(nimsuggest):
-    L.cursor = CursorPosition.None
-  fillToken(tok)
+  reset(tok)
   if L.indentAhead >= 0:
     tok.indent = L.indentAhead
     L.currLineIndent = L.indentAhead
@@ -1092,13 +1196,18 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
     if tok.tokType == tkComment:
       L.indentAhead = L.currLineIndent
       return
-  var c = L.buf[L.bufpos]
+  let c = L.buf[L.bufpos]
   tok.line = L.lineNumber
   tok.col = getColNumber(L, L.bufpos)
-  if c in SymStartChars - {'r', 'R'}:
+  if c in SymStartChars - {'r', 'R'} - UnicodeOperatorStartChars:
     getSymbol(L, tok)
   else:
     case c
+    of UnicodeOperatorStartChars:
+      if unicodeOprLen(L.buf, L.bufpos)[0] != 0:
+        getOperator(L, tok)
+      else:
+        getSymbol(L, tok)
     of '#':
       scanComment(L, tok)
     of '*':
@@ -1115,7 +1224,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
     of 'r', 'R':
       if L.buf[L.bufpos + 1] == '\"':
         inc(L.bufpos)
-        getString(L, tok, true)
+        getString(L, tok, raw)
       else:
         getSymbol(L, tok)
     of '(':
@@ -1150,7 +1259,6 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
         if L.fileIdx == L.config.m.trackPos.fileIndex and tok.col+1 == L.config.m.trackPos.col and
             tok.line == L.config.m.trackPos.line.int and L.config.ideCmd == ideSug:
           tok.tokType = tkDot
-          L.cursor = CursorPosition.InToken
           L.config.m.trackPos.col = tok.col.int16
           inc(L.bufpos)
           atTokenEnd()
@@ -1192,10 +1300,10 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
         tok.tokType = tkInvalid
         lexMessage(L, errGenerated, "invalid token: " & c & " (\\" & $(ord(c)) & ')')
     of '\"':
-      # check for extended raw string literal:
-      var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars
-      getString(L, tok, rawMode)
-      if rawMode:
+      # check for generalized raw string literal:
+      let mode = if L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars: generalized else: normal
+      getString(L, tok, mode)
+      if mode == generalized:
         # tkRStrLit -> tkGStrLit
         # tkTripleStrLit -> tkGTripleStrLit
         inc(tok.tokType, 2)
@@ -1207,7 +1315,28 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
       getNumber(L, tok)
       let c = L.buf[L.bufpos]
       if c in SymChars+{'_'}:
-        lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier")
+        if c in UnicodeOperatorStartChars and
+            unicodeOprLen(L.buf, L.bufpos)[0] != 0:
+          discard
+        else:
+          lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier")
+    of '-':
+      if L.buf[L.bufpos+1] in {'0'..'9'} and
+          (L.bufpos-1 == 0 or L.buf[L.bufpos-1] in UnaryMinusWhitelist):
+        # x)-23 # binary minus
+        # ,-23  # unary minus
+        # \n-78 # unary minus? Yes.
+        # =-3   # parsed as `=-` anyway
+        getNumber(L, tok)
+        let c = L.buf[L.bufpos]
+        if c in SymChars+{'_'}:
+          if c in UnicodeOperatorStartChars and
+              unicodeOprLen(L.buf, L.bufpos)[0] != 0:
+            discard
+          else:
+            lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier")
+      else:
+        getOperator(L, tok)
     else:
       if c in OpChars:
         getOperator(L, tok)
@@ -1223,12 +1352,26 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
 
 proc getIndentWidth*(fileIdx: FileIndex, inputstream: PLLStream;
                      cache: IdentCache; config: ConfigRef): int =
-  var lex: TLexer
-  var tok: TToken
-  initToken(tok)
+  result = 0
+  var lex: Lexer = default(Lexer)
+  var tok: Token = default(Token)
   openLexer(lex, fileIdx, inputstream, cache, config)
-  while true:
+  var prevToken = tkEof
+  while tok.tokType != tkEof:
     rawGetTok(lex, tok)
-    result = tok.indent
-    if result > 0 or tok.tokType == tkEof: break
+    if tok.indent > 0 and prevToken in {tkColon, tkEquals, tkType, tkConst, tkLet, tkVar, tkUsing}:
+      result = tok.indent
+      if result > 0: break
+    prevToken = tok.tokType
   closeLexer(lex)
+
+proc getPrecedence*(ident: PIdent): int =
+  ## assumes ident is binary operator already
+  let
+    tokType =
+      if ident.id in ord(tokKeywordLow) - ord(tkSymbol)..ord(tokKeywordHigh) - ord(tkSymbol):
+        TokType(ident.id + ord(tkSymbol))
+      else: tkOpr
+    tok = Token(ident: ident, tokType: tokType)
+
+  getPrecedence(tok)
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim
new file mode 100644
index 000000000..9ff5c0a9d
--- /dev/null
+++ b/compiler/liftdestructors.nim
@@ -0,0 +1,1324 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements lifting for type-bound operations
+## (`=sink`, `=copy`, `=destroy`, `=deepCopy`, `=wasMoved`, `=dup`).
+
+import modulegraphs, lineinfos, idents, ast, renderer, semdata,
+  sighashes, lowerings, options, types, msgs, magicsys, ccgutils
+
+import std/tables
+from trees import isCaseObj
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+type
+  TLiftCtx = object
+    g: ModuleGraph
+    info: TLineInfo # for construction
+    kind: TTypeAttachedOp
+    fn: PSym
+    asgnForType: PType
+    recurse: bool
+    addMemReset: bool    # add wasMoved() call after destructor call
+    canRaise: bool
+    filterDiscriminator: PSym  # we generating destructor for case branch
+    c: PContext # c can be nil, then we are called from lambdalifting!
+    idgen: IdGenerator
+
+template destructor*(t: PType): PSym = getAttachedOp(c.g, t, attachedDestructor)
+template assignment*(t: PType): PSym = getAttachedOp(c.g, t, attachedAsgn)
+template dup*(t: PType): PSym = getAttachedOp(c.g, t, attachedDup)
+template asink*(t: PType): PSym = getAttachedOp(c.g, t, attachedSink)
+
+proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
+proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
+              info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym
+
+proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
+                         idgen: IdGenerator)
+
+proc at(a, i: PNode, elemType: PType): PNode =
+  result = newNodeI(nkBracketExpr, a.info, 2)
+  result[0] = a
+  result[1] = i
+  result.typ = elemType
+
+proc destructorOverridden(g: ModuleGraph; t: PType): bool =
+  let op = getAttachedOp(g, t, attachedDestructor)
+  op != nil and sfOverridden in op.flags
+
+proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  for i, a in t.ikids:
+    let lit = lowerings.newIntLit(c.g, x.info, i)
+    let b = if c.kind == attachedTrace: y else: y.at(lit, a)
+    fillBody(c, a, body, x.at(lit, a), b)
+
+proc dotField(x: PNode, f: PSym): PNode =
+  result = newNodeI(nkDotExpr, x.info, 2)
+  if x.typ.skipTypes(abstractInst).kind == tyVar:
+    result[0] = x.newDeref
+  else:
+    result[0] = x
+  result[1] = newSymNode(f, x.info)
+  result.typ = f.typ
+
+proc newAsgnStmt(le, ri: PNode): PNode =
+  result = newNodeI(nkAsgn, le.info, 2)
+  result[0] = le
+  result[1] = ri
+
+proc genBuiltin*(g: ModuleGraph; idgen: IdGenerator; magic: TMagic; name: string; i: PNode): PNode =
+  result = newNodeI(nkCall, i.info)
+  result.add createMagic(g, idgen, name, magic).newSymNode
+  result.add i
+
+proc genBuiltin(c: var TLiftCtx; magic: TMagic; name: string; i: PNode): PNode =
+  result = genBuiltin(c.g, c.idgen, magic, name, i)
+
+proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink, attachedDup}:
+    body.add newAsgnStmt(x, y)
+  elif c.kind == attachedDestructor and c.addMemReset:
+    let call = genBuiltin(c, mDefault, "default", x)
+    call.typ = t
+    body.add newAsgnStmt(x, call)
+  elif c.kind == attachedWasMoved:
+    body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
+
+proc genAddr(c: var TLiftCtx; x: PNode): PNode =
+  if x.kind == nkHiddenDeref:
+    checkSonsLen(x, 1, c.g.config)
+    result = x[0]
+  else:
+    result = newNodeIT(nkHiddenAddr, x.info, makeVarType(x.typ.owner, x.typ, c.idgen))
+    result.add x
+
+proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
+  result = newNodeI(nkWhileStmt, c.info, 2)
+  let cmp = genBuiltin(c, mLtI, "<", i)
+  cmp.add genLen(c.g, dest)
+  cmp.typ = getSysType(c.g, c.info, tyBool)
+  result[0] = cmp
+  result[1] = newNodeI(nkStmtList, c.info)
+
+proc genIf(c: var TLiftCtx; cond, action: PNode): PNode =
+  result = newTree(nkIfStmt, newTree(nkElifBranch, cond, action))
+
+proc genContainerOf(c: var TLiftCtx; objType: PType, field, x: PSym): PNode =
+  # generate: cast[ptr ObjType](cast[int](addr(x)) - offsetOf(objType.field))
+  let intType = getSysType(c.g, unknownLineInfo, tyInt)
+
+  let addrOf = newNodeIT(nkAddr, c.info, makePtrType(x.owner, x.typ, c.idgen))
+  addrOf.add newDeref(newSymNode(x))
+  let castExpr1 = newNodeIT(nkCast, c.info, intType)
+  castExpr1.add newNodeIT(nkType, c.info, intType)
+  castExpr1.add addrOf
+
+  let dotExpr = newNodeIT(nkDotExpr, c.info, x.typ)
+  dotExpr.add newNodeIT(nkType, c.info, objType)
+  dotExpr.add newSymNode(field)
+
+  let offsetOf = genBuiltin(c, mOffsetOf, "offsetof", dotExpr)
+  offsetOf.typ = intType
+
+  let minusExpr = genBuiltin(c, mSubI, "-", castExpr1)
+  minusExpr.typ = intType
+  minusExpr.add offsetOf
+
+  let objPtr = makePtrType(objType.owner, objType, c.idgen)
+  result = newNodeIT(nkCast, c.info, objPtr)
+  result.add newNodeIT(nkType, c.info, objPtr)
+  result.add minusExpr
+
+proc destructorCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
+  var destroy = newNodeIT(nkCall, x.info, op.typ.returnType)
+  destroy.add(newSymNode(op))
+  if op.typ.firstParamType.kind != tyVar:
+    destroy.add x
+  else:
+    destroy.add genAddr(c, x)
+  if sfNeverRaises notin op.flags:
+    c.canRaise = true
+  if c.addMemReset:
+    result = newTree(nkStmtList, destroy, genBuiltin(c, mWasMoved,  "`=wasMoved`", x))
+  else:
+    result = destroy
+
+proc genWasMovedCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
+  result = newNodeIT(nkCall, x.info, op.typ.returnType)
+  result.add(newSymNode(op))
+  result.add genAddr(c, x)
+
+proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool, enforceWasMoved = false) =
+  case n.kind
+  of nkSym:
+    if c.filterDiscriminator != nil: return
+    let f = n.sym
+    let b = if c.kind == attachedTrace: y else: y.dotField(f)
+    if (sfCursor in f.flags and c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcHooks}) or
+        enforceDefaultOp:
+      defaultOp(c, f.typ, body, x.dotField(f), b)
+    else:
+      if enforceWasMoved:
+        body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x.dotField(f))
+      fillBody(c, f.typ, body, x.dotField(f), b)
+  of nkNilLit: discard
+  of nkRecCase:
+    # XXX This is only correct for 'attachedSink'!
+    var localEnforceDefaultOp = enforceDefaultOp
+    if c.kind == attachedSink:
+      # the value needs to be destroyed before we assign the selector
+      # or the value is lost
+      let prevKind = c.kind
+      let prevAddMemReset = c.addMemReset
+      c.kind = attachedDestructor
+      c.addMemReset = true
+      fillBodyObj(c, n, body, x, y, enforceDefaultOp = false)
+      c.kind = prevKind
+      c.addMemReset = prevAddMemReset
+      localEnforceDefaultOp = true
+
+    if c.kind != attachedDestructor:
+      # copy the selector before case stmt, but destroy after case stmt
+      fillBodyObj(c, n[0], body, x, y, enforceDefaultOp = false)
+
+    let oldfilterDiscriminator = c.filterDiscriminator
+    if c.filterDiscriminator == n[0].sym:
+      c.filterDiscriminator = nil # we have found the case part, proceed as normal
+
+    # we need to generate a case statement:
+    var caseStmt = newNodeI(nkCaseStmt, c.info)
+    # XXX generate 'if' that checks same branches
+    # generate selector:
+    var access = dotField(x, n[0].sym)
+    caseStmt.add(access)
+    var emptyBranches = 0
+    # copy the branches over, but replace the fields with the for loop body:
+    for i in 1..<n.len:
+      var branch = copyTree(n[i])
+      branch[^1] = newNodeI(nkStmtList, c.info)
+
+      fillBodyObj(c, n[i].lastSon, branch[^1], x, y,
+                  enforceDefaultOp = localEnforceDefaultOp, enforceWasMoved = c.kind == attachedAsgn)
+      if branch[^1].len == 0: inc emptyBranches
+      caseStmt.add(branch)
+    if emptyBranches != n.len-1:
+      body.add(caseStmt)
+
+    if c.kind == attachedDestructor:
+      # destructor for selector is done after case stmt
+      fillBodyObj(c, n[0], body, x, y, enforceDefaultOp = false)
+    c.filterDiscriminator = oldfilterDiscriminator
+  of nkRecList:
+    for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp, enforceWasMoved)
+  else:
+    illFormedAstLocal(n, c.g.config)
+
+proc fillBodyObjTImpl(c: var TLiftCtx; t: PType, body, x, y: PNode) =
+  if t.baseClass != nil:
+    let dest = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
+    dest.add newNodeI(nkEmpty, c.info)
+    dest.add x
+    var src = y
+    if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}:
+      src = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
+      src.add newNodeI(nkEmpty, c.info)
+      src.add y
+
+    fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, dest, src)
+  fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
+
+proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
+  var hasCase = isCaseObj(t.n)
+  var obj = t
+  while obj.baseClass != nil:
+    obj = skipTypes(obj.baseClass, abstractPtrs)
+    hasCase = hasCase or isCaseObj(obj.n)
+
+  if hasCase and c.kind in {attachedAsgn, attachedDeepCopy}:
+    # assignment for case objects is complex, we do:
+    # =destroy(dest)
+    # wasMoved(dest)
+    # for every field:
+    #   `=` dest.field, src.field
+    # ^ this is what we used to do, but for 'result = result.sons[0]' it
+    # destroys 'result' too early.
+    # So this is what we really need to do:
+    # let blob {.cursor.} = dest # remembers the old dest.kind
+    # wasMoved(dest)
+    # dest.kind = src.kind
+    # for every field (dependent on dest.kind):
+    #   `=` dest.field, src.field
+    # =destroy(blob)
+    var dummy = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
+    dummy.typ = y.typ
+    if ccgIntroducedPtr(c.g.config, dummy, y.typ):
+      # Because of potential aliasing when the src param is passed by ref, we need to check for equality here,
+      # because the wasMoved(dest) call would zero out src, if dest aliases src.
+      var cond = newTree(nkCall, newSymNode(c.g.getSysMagic(c.info, "==", mEqRef)),
+        newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x), newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y))
+      cond.typ = getSysType(c.g, x.info, tyBool)
+      body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
+    var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
+    temp.typ = x.typ
+    incl(temp.flags, sfFromGeneric)
+    var v = newNodeI(nkVarSection, c.info)
+    let blob = newSymNode(temp)
+    v.addVar(blob, x)
+    body.add v
+    #body.add newAsgnStmt(blob, x)
+
+    var wasMovedCall = newNodeI(nkCall, c.info)
+    wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "`=wasMoved`", mWasMoved)))
+    wasMovedCall.add x # mWasMoved does not take the address
+    body.add wasMovedCall
+
+    fillBodyObjTImpl(c, t, body, x, y)
+    when false:
+      # does not work yet due to phase-ordering problems:
+      assert t.destructor != nil
+      body.add destructorCall(c.g, t.destructor, blob)
+    let prevKind = c.kind
+    c.kind = attachedDestructor
+    fillBodyObjTImpl(c, t, body, blob, y)
+    c.kind = prevKind
+
+  else:
+    fillBodyObjTImpl(c, t, body, x, y)
+
+proc boolLit*(g: ModuleGraph; info: TLineInfo; value: bool): PNode =
+  result = newIntLit(g, info, ord value)
+  result.typ = getSysType(g, info, tyBool)
+
+proc getCycleParam(c: TLiftCtx): PNode =
+  assert c.kind in {attachedAsgn, attachedDup}
+  if c.fn.typ.len == 3 + ord(c.kind == attachedAsgn):
+    result = c.fn.typ.n.lastSon
+    assert result.kind == nkSym
+    assert result.sym.name.s == "cyclic"
+  else:
+    result = boolLit(c.g, c.info, true)
+
+proc newHookCall(c: var TLiftCtx; 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)
+  if sfNeverRaises notin op.flags:
+    c.canRaise = true
+  if op.typ.firstParamType.kind == tyVar:
+    result.add genAddr(c, x)
+  else:
+    result.add x
+  if y != nil:
+    result.add y
+  if op.typ.signatureLen == 4:
+    assert y != nil
+    if c.fn.typ.signatureLen == 4:
+      result.add getCycleParam(c)
+    else:
+      # assume the worst: A cycle is created:
+      result.add boolLit(c.g, y.info, true)
+
+proc newOpCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
+  result = newNodeIT(nkCall, x.info, op.typ.returnType)
+  result.add(newSymNode(op))
+  result.add x
+  if sfNeverRaises notin op.flags:
+    c.canRaise = true
+
+  if c.kind == attachedDup and op.typ.len == 3:
+    assert x != nil
+    if c.fn.typ.len == 3:
+      result.add getCycleParam(c)
+    else:
+      # assume the worst: A cycle is created:
+      result.add boolLit(c.g, x.info, true)
+
+proc newDeepCopyCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode =
+  result = newAsgnStmt(x, newOpCall(c, op, y))
+
+proc newDupCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode =
+  result = newAsgnStmt(x, newOpCall(c, op, y))
+
+proc usesBuiltinArc(t: PType): bool =
+  proc wrap(t: PType): bool {.nimcall.} = ast.isGCedMem(t)
+  result = types.searchTypeFor(t, wrap)
+
+proc useNoGc(c: TLiftCtx; t: PType): bool {.inline.} =
+  result = optSeqDestructors in c.g.config.globalOptions and
+    ({tfHasGCedMem, tfHasOwned} * t.flags != {} or usesBuiltinArc(t))
+
+proc requiresDestructor(c: TLiftCtx; t: PType): bool {.inline.} =
+  result = optSeqDestructors in c.g.config.globalOptions and
+    containsGarbageCollectedRef(t)
+
+proc instantiateGeneric(c: var TLiftCtx; op: PSym; t, typeInst: PType): PSym =
+  if c.c != nil and typeInst != nil:
+    result = c.c.instTypeBoundOp(c.c, op, typeInst, c.info, attachedAsgn, 1)
+  else:
+    localError(c.g.config, c.info,
+      "cannot generate destructor for generic type: " & typeToString(t))
+    result = nil
+
+proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
+                        field: var PSym): bool =
+  if optSeqDestructors in c.g.config.globalOptions:
+    var op = field
+    let destructorOverridden = destructorOverridden(c.g, t)
+    if op != nil and op != c.fn and
+        (sfOverridden in op.flags or destructorOverridden):
+      if sfError in op.flags:
+        incl c.fn.flags, sfError
+      #else:
+      #  markUsed(c.g.config, c.info, op, c.g.usageSym)
+      onUse(c.info, op)
+      body.add newHookCall(c, op, x, y)
+      result = true
+    elif op == nil and destructorOverridden:
+      op = produceSym(c.g, c.c, t, c.kind, c.info, c.idgen)
+      body.add newHookCall(c, op, x, y)
+      result = true
+    else:
+      result = false
+  elif tfHasAsgn in t.flags:
+    var op: PSym
+    if sameType(t, c.asgnForType):
+      # generate recursive call:
+      if c.recurse:
+        op = c.fn
+      else:
+        c.recurse = true
+        return false
+    else:
+      op = field
+      if op == nil:
+        op = produceSym(c.g, c.c, t, c.kind, c.info, c.idgen)
+    if sfError in op.flags:
+      incl c.fn.flags, sfError
+    #else:
+    #  markUsed(c.g.config, c.info, op, c.g.usageSym)
+    onUse(c.info, op)
+    # We also now do generic instantiations in the destructor lifting pass:
+    if op.ast.isGenericRoutine:
+      op = instantiateGeneric(c, op, t, t.typeInst)
+      field = op
+      #echo "trying to use ", op.ast
+      #echo "for ", op.name.s, " "
+      #debug(t)
+      #return false
+    assert op.ast[genericParamsPos].kind == nkEmpty
+    body.add newHookCall(c, op, x, y)
+    result = true
+  else:
+    result = false
+
+proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) =
+  let t = orig.skipTypes(abstractInst - {tyDistinct})
+  var op = t.destructor
+
+  if op != nil and sfOverridden in op.flags:
+    if op.ast.isGenericRoutine:
+      # patch generic destructor:
+      op = instantiateGeneric(c, op, t, t.typeInst)
+      setAttachedOp(c.g, c.idgen.module, t, attachedDestructor, op)
+
+  if op == nil and (useNoGc(c, t) or requiresDestructor(c, t)):
+    op = produceSym(c.g, c.c, t, attachedDestructor, c.info, c.idgen)
+    doAssert op != nil
+    doAssert op == t.destructor
+
+  if op != nil:
+    #markUsed(c.g.config, c.info, op, c.g.usageSym)
+    onUse(c.info, op)
+    body.add destructorCall(c, op, x)
+  elif useNoGc(c, t):
+    internalError(c.g.config, c.info,
+      "type-bound operator could not be resolved")
+
+proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
+  case c.kind
+  of attachedDestructor:
+    var op = t.destructor
+    if op != nil and sfOverridden in op.flags:
+
+      if op.ast.isGenericRoutine:
+        # patch generic destructor:
+        op = instantiateGeneric(c, op, t, t.typeInst)
+        setAttachedOp(c.g, c.idgen.module, t, attachedDestructor, op)
+
+      #markUsed(c.g.config, c.info, op, c.g.usageSym)
+      onUse(c.info, op)
+      body.add destructorCall(c, op, x)
+      result = true
+    else:
+      result = false
+    #result = addDestructorCall(c, t, body, x)
+  of attachedAsgn, attachedSink, attachedTrace:
+    var op = getAttachedOp(c.g, t, c.kind)
+    if op != nil and sfOverridden in op.flags:
+      if op.ast.isGenericRoutine:
+        # patch generic =trace:
+        op = instantiateGeneric(c, op, t, t.typeInst)
+        setAttachedOp(c.g, c.idgen.module, t, c.kind, op)
+
+    result = considerAsgnOrSink(c, t, body, x, y, op)
+    if op != nil:
+      setAttachedOp(c.g, c.idgen.module, t, c.kind, op)
+
+  of attachedDeepCopy:
+    let op = getAttachedOp(c.g, t, attachedDeepCopy)
+    if op != nil:
+      #markUsed(c.g.config, c.info, op, c.g.usageSym)
+      onUse(c.info, op)
+      body.add newDeepCopyCall(c, op, x, y)
+      result = true
+    else:
+      result = false
+
+  of attachedWasMoved:
+    var op = getAttachedOp(c.g, t, attachedWasMoved)
+    if op != nil and sfOverridden in op.flags:
+
+      if op.ast.isGenericRoutine:
+        # patch generic destructor:
+        op = instantiateGeneric(c, op, t, t.typeInst)
+        setAttachedOp(c.g, c.idgen.module, t, attachedWasMoved, op)
+
+      #markUsed(c.g.config, c.info, op, c.g.usageSym)
+      onUse(c.info, op)
+      body.add genWasMovedCall(c, op, x)
+      result = true
+    else:
+      result = false
+
+  of attachedDup:
+    var op = getAttachedOp(c.g, t, attachedDup)
+    if op != nil and sfOverridden in op.flags:
+
+      if op.ast.isGenericRoutine:
+        # patch generic destructor:
+        op = instantiateGeneric(c, op, t, t.typeInst)
+        setAttachedOp(c.g, c.idgen.module, t, attachedDup, op)
+
+      #markUsed(c.g.config, c.info, op, c.g.usageSym)
+      onUse(c.info, op)
+      body.add newDupCall(c, op, x, y)
+      result = true
+    else:
+      result = false
+
+proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
+  var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
+  temp.typ = getSysType(c.g, body.info, tyInt)
+  incl(temp.flags, sfFromGeneric)
+
+  var v = newNodeI(nkVarSection, c.info)
+  result = newSymNode(temp)
+  v.addVar(result, lowerings.newIntLit(c.g, body.info, first))
+  body.add v
+
+proc declareTempOf(c: var TLiftCtx; body: PNode; value: PNode): PNode =
+  var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
+  temp.typ = value.typ
+  incl(temp.flags, sfFromGeneric)
+
+  var v = newNodeI(nkVarSection, c.info)
+  result = newSymNode(temp)
+  v.addVar(result, value)
+  body.add v
+
+proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
+  let incCall = genBuiltin(c, mInc, "inc", i)
+  incCall.add lowerings.newIntLit(c.g, c.info, 1)
+  body.add incCall
+
+proc newSeqCall(c: var TLiftCtx; x, y: PNode): PNode =
+  # don't call genAddr(c, x) here:
+  result = genBuiltin(c, mNewSeq, "newSeq", x)
+  let lenCall = genBuiltin(c, mLengthSeq, "len", y)
+  lenCall.typ = getSysType(c.g, x.info, tyInt)
+  result.add lenCall
+
+proc setLenStrCall(c: var TLiftCtx; x, y: PNode): PNode =
+  let lenCall = genBuiltin(c, mLengthStr, "len", y)
+  lenCall.typ = getSysType(c.g, x.info, tyInt)
+  result = genBuiltin(c, mSetLengthStr, "setLen", x) # genAddr(g, x))
+  result.add lenCall
+
+proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode): PNode =
+  let lenCall = genBuiltin(c, mLengthSeq, "len", y)
+  lenCall.typ = getSysType(c.g, x.info, tyInt)
+  var op = getSysMagic(c.g, x.info, "setLen", mSetLengthSeq)
+  op = instantiateGeneric(c, op, t, t)
+  result = newTree(nkCall, newSymNode(op, x.info), x, lenCall)
+
+proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  let counterIdx = body.len
+  let i = declareCounter(c, body, toInt64(firstOrd(c.g.config, t)))
+  let whileLoop = genWhileLoop(c, i, x)
+  let elemType = t.elementType
+  let b = if c.kind == attachedTrace: y else: y.at(i, elemType)
+  fillBody(c, elemType, whileLoop[1], x.at(i, elemType), b)
+  if whileLoop[1].len > 0:
+    addIncStmt(c, whileLoop[1], i)
+    body.add whileLoop
+  else:
+    body.sons.setLen counterIdx
+
+proc checkSelfAssignment(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  var cond = callCodegenProc(c.g, "sameSeqPayload", c.info,
+      newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x),
+      newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y)
+      )
+  cond.typ = getSysType(c.g, c.info, tyBool)
+  body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
+
+proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  case c.kind
+  of attachedDup:
+    body.add setLenSeqCall(c, t, x, y)
+    forallElements(c, t, body, x, y)
+  of attachedAsgn, attachedDeepCopy:
+    # we generate:
+    # if x.p == y.p:
+    #   return
+    # setLen(dest, y.len)
+    # var i = 0
+    # while i < y.len: dest[i] = y[i]; inc(i)
+    # This is usually more efficient than a destroy/create pair.
+    checkSelfAssignment(c, t, body, x, y)
+    body.add setLenSeqCall(c, t, x, y)
+    forallElements(c, t, body, x, y)
+  of attachedSink:
+    let moveCall = genBuiltin(c, mMove, "move", x)
+    moveCall.add y
+    doAssert t.destructor != nil
+    moveCall.add destructorCall(c, t.destructor, x)
+    body.add moveCall
+  of attachedDestructor:
+    # destroy all elements:
+    forallElements(c, t, body, x, y)
+    body.add genBuiltin(c, mDestroy, "destroy", x)
+  of attachedTrace:
+    if canFormAcycle(c.g, t.elemType):
+      # follow all elements:
+      forallElements(c, t, body, x, y)
+  of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
+
+proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  createTypeBoundOps(c.g, c.c, t, body.info, c.idgen)
+  # recursions are tricky, so we might need to forward the generated
+  # operation here:
+  var t = t
+  if t.assignment == nil or t.destructor == nil or t.dup == nil:
+    let h = sighashes.hashType(t,c.g.config, {CoType, CoConsiderOwned, CoDistinct})
+    let canon = c.g.canonTypes.getOrDefault(h)
+    if canon != nil: t = canon
+
+  case c.kind
+  of attachedAsgn, attachedDeepCopy:
+    # XXX: replace these with assertions.
+    if t.assignment == nil:
+      return # protect from recursion
+    body.add newHookCall(c, t.assignment, x, y)
+  of attachedSink:
+    # we always inline the move for better performance:
+    let moveCall = genBuiltin(c, mMove, "move", x)
+    moveCall.add y
+    doAssert t.destructor != nil
+    moveCall.add destructorCall(c, t.destructor, x)
+    body.add moveCall
+    # alternatively we could do this:
+    when false:
+      doAssert t.asink != nil
+      body.add newHookCall(c, t.asink, x, y)
+  of attachedDestructor:
+    doAssert t.destructor != nil
+    body.add destructorCall(c, t.destructor, x)
+  of attachedTrace:
+    if t.kind != tyString and canFormAcycle(c.g, t.elemType):
+      let op = getAttachedOp(c.g, t, c.kind)
+      if op == nil:
+        return # protect from recursion
+      body.add newHookCall(c, op, x, y)
+  of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
+  of attachedDup:
+    # XXX: replace these with assertions.
+    let op = getAttachedOp(c.g, t, c.kind)
+    if op == nil:
+      return # protect from recursion
+    body.add newDupCall(c, op, x, y)
+
+proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  case c.kind
+  of attachedAsgn, attachedDeepCopy, attachedDup:
+    body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c, x), y)
+  of attachedSink:
+    let moveCall = genBuiltin(c, mMove, "move", x)
+    moveCall.add y
+    doAssert t.destructor != nil
+    moveCall.add destructorCall(c, t.destructor, x)
+    body.add moveCall
+  of attachedDestructor:
+    body.add genBuiltin(c, mDestroy, "destroy", x)
+  of attachedTrace:
+    discard "strings are atomic and have no inner elements that are to trace"
+  of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
+
+proc cyclicType*(g: ModuleGraph, t: PType): bool =
+  case t.kind
+  of tyRef: result = types.canFormAcycle(g, t.elementType)
+  of tyProc: result = t.callConv == ccClosure
+  else: result = false
+
+proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  #[ bug #15753 is really subtle. Usually the classical write barrier for reference
+  counting looks like this::
+
+    incRef source  # increment first; this takes care of self-assignments1
+    decRef dest
+    dest[] = source
+
+  However, 'decRef dest' might trigger a cycle collection and then the collector
+  traverses the graph. It is crucial that when it follows the pointers the assignment
+  'dest[] = source' already happened so that we don't do trial deletion on a wrong
+  graph -- this causes premature freeing of objects! The correct barrier looks like
+  this::
+
+    let tmp = dest
+    incRef source
+    dest[] = source
+    decRef tmp
+
+  ]#
+  var actions = newNodeI(nkStmtList, c.info)
+  let elemType = t.elementType
+
+  createTypeBoundOps(c.g, c.c, elemType, c.info, c.idgen)
+  let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(c.g, elemType)
+
+  let isInheritableAcyclicRef = c.g.config.selectedGC == gcOrc and
+                      (not isPureObject(elemType)) and
+                      tfAcyclic in skipTypes(elemType, abstractInst+{tyOwned}-{tyTypeDesc}).flags
+  # dynamic Acyclic refs need to use dyn decRef
+
+  let tmp =
+    if isCyclic and c.kind in {attachedAsgn, attachedSink, attachedDup}:
+      declareTempOf(c, body, x)
+    else:
+      x
+
+  if isFinal(elemType):
+    addDestructorCall(c, elemType, actions, genDeref(tmp, nkDerefExpr))
+    var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
+    alignOf.typ = getSysType(c.g, c.info, tyInt)
+    actions.add callCodegenProc(c.g, "nimRawDispose", c.info, tmp, alignOf)
+  else:
+    addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(tmp, nkDerefExpr))
+    actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, tmp)
+
+  var cond: PNode
+  if isCyclic:
+    if isFinal(elemType):
+      let typInfo = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
+      typInfo.typ = getSysType(c.g, c.info, tyPointer)
+      cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, tmp, typInfo)
+    else:
+      cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, tmp)
+  elif isInheritableAcyclicRef:
+    cond = callCodegenProc(c.g, "nimDecRefIsLastDyn", c.info, x)
+  else:
+    cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x)
+  cond.typ = getSysType(c.g, x.info, tyBool)
+
+  case c.kind
+  of attachedSink:
+    if isCyclic:
+      body.add newAsgnStmt(x, y)
+      body.add genIf(c, cond, actions)
+    else:
+      body.add genIf(c, cond, actions)
+      body.add newAsgnStmt(x, y)
+  of attachedAsgn:
+    if isCyclic:
+      body.add genIf(c, y, callCodegenProc(c.g,
+          "nimIncRefCyclic", c.info, y, getCycleParam(c)))
+      body.add newAsgnStmt(x, y)
+      body.add genIf(c, cond, actions)
+    else:
+      body.add genIf(c, y, callCodegenProc(c.g, "nimIncRef", c.info, y))
+      body.add genIf(c, cond, actions)
+      body.add newAsgnStmt(x, y)
+  of attachedDestructor:
+    body.add genIf(c, cond, actions)
+  of attachedDeepCopy: assert(false, "cannot happen")
+  of attachedTrace:
+    if isCyclic:
+      if isFinal(elemType):
+        let typInfo = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
+        typInfo.typ = getSysType(c.g, c.info, tyPointer)
+        body.add callCodegenProc(c.g, "nimTraceRef", c.info, genAddrOf(x, c.idgen), typInfo, y)
+      else:
+        # If the ref is polymorphic we have to account for this
+        body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x, c.idgen), y)
+      #echo "can follow ", elemType, " static ", isFinal(elemType)
+  of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
+  of attachedDup:
+    if isCyclic:
+      body.add newAsgnStmt(x, y)
+      body.add genIf(c, y, callCodegenProc(c.g,
+          "nimIncRefCyclic", c.info, y, getCycleParam(c)))
+    else:
+      body.add newAsgnStmt(x, y)
+      body.add genIf(c, y, callCodegenProc(c.g,
+          "nimIncRef", c.info, y))
+
+proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  ## Closures are really like refs except they always use a virtual destructor
+  ## and we need to do the refcounting only on the ref field which we call 'xenv':
+  let xenv = genBuiltin(c, mAccessEnv, "accessEnv", x)
+  xenv.typ = getSysType(c.g, c.info, tyPointer)
+
+  let isCyclic = c.g.config.selectedGC == gcOrc
+  let tmp =
+    if isCyclic and c.kind in {attachedAsgn, attachedSink, attachedDup}:
+      declareTempOf(c, body, xenv)
+    else:
+      xenv
+
+  var actions = newNodeI(nkStmtList, c.info)
+  actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, tmp)
+
+  let decRefProc =
+    if isCyclic: "nimDecRefIsLastCyclicDyn"
+    else: "nimDecRefIsLast"
+  let cond = callCodegenProc(c.g, decRefProc, c.info, tmp)
+  cond.typ = getSysType(c.g, x.info, tyBool)
+
+  case c.kind
+  of attachedSink:
+    if isCyclic:
+      body.add newAsgnStmt(x, y)
+      body.add genIf(c, cond, actions)
+    else:
+      body.add genIf(c, cond, actions)
+      body.add newAsgnStmt(x, y)
+  of attachedAsgn:
+    let yenv = genBuiltin(c, mAccessEnv, "accessEnv", y)
+    yenv.typ = getSysType(c.g, c.info, tyPointer)
+    if isCyclic:
+      body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRefCyclic", c.info, yenv, getCycleParam(c)))
+      body.add newAsgnStmt(x, y)
+      body.add genIf(c, cond, actions)
+    else:
+      body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRef", c.info, yenv))
+
+      body.add genIf(c, cond, actions)
+      body.add newAsgnStmt(x, y)
+  of attachedDup:
+    let yenv = genBuiltin(c, mAccessEnv, "accessEnv", y)
+    yenv.typ = getSysType(c.g, c.info, tyPointer)
+    if isCyclic:
+      body.add newAsgnStmt(x, y)
+      body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRefCyclic", c.info, yenv, getCycleParam(c)))
+    else:
+      body.add newAsgnStmt(x, y)
+      body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRef", c.info, yenv))
+  of attachedDestructor:
+    body.add genIf(c, cond, actions)
+  of attachedDeepCopy: assert(false, "cannot happen")
+  of attachedTrace:
+    body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv, c.idgen), y)
+  of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
+
+proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  case c.kind
+  of attachedSink:
+    # we 'nil' y out afterwards so we *need* to take over its reference
+    # count value:
+    body.add genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
+    body.add newAsgnStmt(x, y)
+  of attachedAsgn:
+    body.add genIf(c, y, callCodegenProc(c.g, "nimIncRef", c.info, y))
+    body.add genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
+    body.add newAsgnStmt(x, y)
+  of attachedDup:
+    body.add newAsgnStmt(x, y)
+    body.add genIf(c, y, callCodegenProc(c.g, "nimIncRef", c.info, y))
+  of attachedDestructor:
+    # it's better to prepend the destruction of weak refs in order to
+    # prevent wrong "dangling refs exist" problems:
+    var actions = newNodeI(nkStmtList, c.info)
+    actions.add callCodegenProc(c.g, "nimDecWeakRef", c.info, x)
+    let des = genIf(c, x, actions)
+    if body.len == 0:
+      body.add des
+    else:
+      body.sons.insert(des, 0)
+  of attachedDeepCopy: assert(false, "cannot happen")
+  of attachedTrace: discard
+  of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
+
+proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  var actions = newNodeI(nkStmtList, c.info)
+
+  let elemType = t.skipModifier
+  #fillBody(c, elemType, actions, genDeref(x), genDeref(y))
+  #var disposeCall = genBuiltin(c, mDispose, "dispose", x)
+
+  if isFinal(elemType):
+    addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr))
+    var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
+    alignOf.typ = getSysType(c.g, c.info, tyInt)
+    actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x, alignOf)
+  else:
+    addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr))
+    actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, x)
+
+  case c.kind
+  of attachedSink, attachedAsgn:
+    body.add genIf(c, x, actions)
+    body.add newAsgnStmt(x, y)
+  of attachedDup:
+    body.add newAsgnStmt(x, y)
+  of attachedDestructor:
+    body.add genIf(c, x, actions)
+  of attachedDeepCopy: assert(false, "cannot happen")
+  of attachedTrace: discard
+  of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
+
+proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  if c.kind == attachedDeepCopy:
+    # a big problem is that we don't know the environment's type here, so we
+    # have to go through some indirection; we delegate this to the codegen:
+    let call = newNodeI(nkCall, c.info, 2)
+    call.typ = t
+    call[0] = newSymNode(createMagic(c.g, c.idgen, "deepCopy", mDeepCopy))
+    call[1] = y
+    body.add newAsgnStmt(x, call)
+  elif (optOwnedRefs in c.g.config.globalOptions and
+      optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
+    let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
+    xx.typ = getSysType(c.g, c.info, tyPointer)
+    case c.kind
+    of attachedSink:
+      # we 'nil' y out afterwards so we *need* to take over its reference
+      # count value:
+      body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
+      body.add newAsgnStmt(x, y)
+    of attachedAsgn:
+      let yy = genBuiltin(c, mAccessEnv, "accessEnv", y)
+      yy.typ = getSysType(c.g, c.info, tyPointer)
+      body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy))
+      body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
+      body.add newAsgnStmt(x, y)
+    of attachedDup:
+      let yy = genBuiltin(c, mAccessEnv, "accessEnv", y)
+      yy.typ = getSysType(c.g, c.info, tyPointer)
+      body.add newAsgnStmt(x, y)
+      body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy))
+    of attachedDestructor:
+      let des = genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
+      if body.len == 0:
+        body.add des
+      else:
+        body.sons.insert(des, 0)
+    of attachedDeepCopy: assert(false, "cannot happen")
+    of attachedTrace: discard
+    of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
+
+proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
+  xx.typ = getSysType(c.g, c.info, tyPointer)
+  var actions = newNodeI(nkStmtList, c.info)
+  #discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(xx))
+  actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, xx)
+  case c.kind
+  of attachedSink, attachedAsgn:
+    body.add genIf(c, xx, actions)
+    body.add newAsgnStmt(x, y)
+  of attachedDup:
+    body.add newAsgnStmt(x, y)
+  of attachedDestructor:
+    body.add genIf(c, xx, actions)
+  of attachedDeepCopy: assert(false, "cannot happen")
+  of attachedTrace: discard
+  of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
+
+proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  case t.kind
+  of tyNone, tyEmpty, tyVoid: discard
+  of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCstring,
+      tyPtr, tyUncheckedArray, tyVar, tyLent:
+    defaultOp(c, t, body, x, y)
+  of tyRef:
+    if c.g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
+      atomicRefOp(c, t, body, x, y)
+    elif (optOwnedRefs in c.g.config.globalOptions and
+        optRefCheck in c.g.config.options):
+      weakrefOp(c, t, body, x, y)
+    else:
+      defaultOp(c, t, body, x, y)
+  of tyProc:
+    if t.callConv == ccClosure:
+      if c.g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
+        atomicClosureOp(c, t, body, x, y)
+      else:
+        closureOp(c, t, body, x, y)
+    else:
+      defaultOp(c, t, body, x, y)
+  of tyOwned:
+    let base = t.skipTypes(abstractInstOwned)
+    if optOwnedRefs in c.g.config.globalOptions:
+      case base.kind
+      of tyRef:
+        ownedRefOp(c, base, body, x, y)
+        return
+      of tyProc:
+        if base.callConv == ccClosure:
+          ownedClosureOp(c, base, body, x, y)
+          return
+      else: discard
+    defaultOp(c, base, body, x, y)
+  of tyArray:
+    if tfHasAsgn in t.flags or useNoGc(c, t):
+      forallElements(c, t, body, x, y)
+    else:
+      defaultOp(c, t, body, x, y)
+  of tySequence:
+    if useNoGc(c, t):
+      useSeqOrStrOp(c, t, body, x, y)
+    elif optSeqDestructors in c.g.config.globalOptions:
+      # note that tfHasAsgn is propagated so we need the check on
+      # 'selectedGC' here to determine if we have the new runtime.
+      discard considerUserDefinedOp(c, t, body, x, y)
+    elif tfHasAsgn in t.flags:
+      if c.kind in {attachedAsgn, attachedSink, attachedDeepCopy}:
+        body.add newSeqCall(c, x, y)
+      forallElements(c, t, body, x, y)
+    else:
+      defaultOp(c, t, body, x, y)
+  of tyString:
+    if useNoGc(c, t):
+      useSeqOrStrOp(c, t, body, x, y)
+    elif tfHasAsgn in t.flags:
+      discard considerUserDefinedOp(c, t, body, x, y)
+    else:
+      defaultOp(c, t, body, x, y)
+  of tyObject:
+    if not considerUserDefinedOp(c, t, body, x, y):
+      if t.sym != nil and sfImportc in t.sym.flags:
+        case c.kind
+        of {attachedAsgn, attachedSink, attachedDup}:
+          body.add newAsgnStmt(x, y)
+        of attachedWasMoved:
+          body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
+        else:
+          fillBodyObjT(c, t, body, x, y)
+      else:
+        if c.kind == attachedDup:
+          var op2 = getAttachedOp(c.g, t, attachedAsgn)
+          if op2 != nil and sfOverridden in op2.flags:
+            #markUsed(c.g.config, c.info, op, c.g.usageSym)
+            onUse(c.info, op2)
+            body.add newHookCall(c, t.assignment, x, y)
+          else:
+            fillBodyObjT(c, t, body, x, y)
+        else:
+          fillBodyObjT(c, t, body, x, y)
+  of tyDistinct:
+    if not considerUserDefinedOp(c, t, body, x, y):
+      fillBody(c, t.elementType, body, x, y)
+  of tyTuple:
+    fillBodyTup(c, t, body, x, y)
+  of tyVarargs, tyOpenArray:
+    if c.kind == attachedDestructor and (tfHasAsgn in t.flags or useNoGc(c, t)):
+      forallElements(c, t, body, x, y)
+    else:
+      discard "cannot copy openArray"
+
+  of tyFromExpr, tyError, tyBuiltInTypeClass, tyUserTypeClass,
+     tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
+     tyGenericParam, tyGenericBody, tyNil, tyUntyped, tyTyped,
+     tyTypeDesc, tyGenericInvocation, tyForward, tyStatic:
+    #internalError(c.g.config, c.info, "assignment requested for type: " & typeToString(t))
+    discard
+  of tyOrdinal, tyRange, tyInferred,
+     tyGenericInst, tyAlias, tySink:
+    fillBody(c, skipModifier(t), body, x, y)
+  of tyConcept, tyIterable: raiseAssert "unreachable"
+
+proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
+                            kind: TTypeAttachedOp; info: TLineInfo;
+                            idgen: IdGenerator): PSym =
+  assert typ.kind == tyDistinct
+  let baseType = typ.elementType
+  if getAttachedOp(g, baseType, kind) == nil:
+    # TODO: fixme `isDistinct` is a fix for #23552; remove it after
+    # `-d:nimPreviewNonVarDestructor` becomes the default
+    discard produceSym(g, c, baseType, kind, info, idgen, isDistinct = true)
+  result = getAttachedOp(g, baseType, kind)
+  setAttachedOp(g, idgen.module, typ, kind, result)
+
+proc symDupPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
+              info: TLineInfo; idgen: IdGenerator): PSym =
+  let procname = getIdent(g.cache, AttachedOpToStr[kind])
+  result = newSym(skProc, procname, idgen, owner, info)
+  let res = newSym(skResult, getIdent(g.cache, "result"), idgen, result, info)
+  let src = newSym(skParam, getIdent(g.cache, "src"),
+                   idgen, result, info)
+  res.typ = typ
+  src.typ = typ
+
+  result.typ = newType(tyProc, idgen, owner)
+  result.typ.n = newNodeI(nkFormalParams, info)
+  rawAddSon(result.typ, res.typ)
+  result.typ.n.add newNodeI(nkEffectList, info)
+
+  result.typ.addParam src
+
+  if g.config.selectedGC == gcOrc and
+    cyclicType(g, typ.skipTypes(abstractInst)):
+    let cycleParam = newSym(skParam, getIdent(g.cache, "cyclic"),
+                            idgen, result, info)
+    cycleParam.typ = getSysType(g, info, tyBool)
+    result.typ.addParam cycleParam
+
+  var n = newNodeI(nkProcDef, info, bodyPos+2)
+  for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info)
+  n[namePos] = newSymNode(result)
+  n[paramsPos] = result.typ.n
+  n[bodyPos] = newNodeI(nkStmtList, info)
+  n[resultPos] = newSymNode(res)
+  result.ast = n
+  incl result.flags, sfFromGeneric
+  incl result.flags, sfGeneratedOp
+
+proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
+              info: TLineInfo; idgen: IdGenerator; isDiscriminant = false; isDistinct = false): PSym =
+  if kind == attachedDup:
+    return symDupPrototype(g, typ, owner, kind, info, idgen)
+
+  let procname = getIdent(g.cache, AttachedOpToStr[kind])
+  result = newSym(skProc, procname, idgen, owner, info)
+  let dest = newSym(skParam, getIdent(g.cache, "dest"), idgen, result, info)
+  let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"),
+                   idgen, result, info)
+
+  if kind == attachedDestructor and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
+     ((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or (typ.kind in {tyRef, tyString, tySequence} and not isDistinct)):
+    dest.typ = typ
+  else:
+    dest.typ = makeVarType(typ.owner, typ, idgen)
+
+  if kind == attachedTrace:
+    src.typ = getSysType(g, info, tyPointer)
+  else:
+    src.typ = typ
+
+  result.typ = newProcType(info, idgen, owner)
+  result.typ.addParam dest
+  if kind notin {attachedDestructor, attachedWasMoved}:
+    result.typ.addParam src
+
+  if kind == attachedAsgn and g.config.selectedGC == gcOrc and
+      cyclicType(g, typ.skipTypes(abstractInst)):
+    let cycleParam = newSym(skParam, getIdent(g.cache, "cyclic"),
+                            idgen, result, info)
+    cycleParam.typ = getSysType(g, info, tyBool)
+    result.typ.addParam cycleParam
+
+  var n = newNodeI(nkProcDef, info, bodyPos+1)
+  for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info)
+  n[namePos] = newSymNode(result)
+  n[paramsPos] = result.typ.n
+  n[bodyPos] = newNodeI(nkStmtList, info)
+  result.ast = n
+  incl result.flags, sfFromGeneric
+  incl result.flags, sfGeneratedOp
+  if kind == attachedWasMoved:
+    incl result.flags, sfNoSideEffect
+    incl result.typ.flags, tfNoSideEffect
+
+proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  let xx = genBuiltin(c, mAccessTypeField, "accessTypeField", x)
+  let yy = genBuiltin(c, mAccessTypeField, "accessTypeField", y)
+  xx.typ = getSysType(c.g, c.info, tyPointer)
+  yy.typ = xx.typ
+  body.add newAsgnStmt(xx, yy)
+
+proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
+              info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym =
+  if typ.kind == tyDistinct:
+    return produceSymDistinctType(g, c, typ, kind, info, idgen)
+
+  result = getAttachedOp(g, typ, kind)
+  if result == nil:
+    result = symPrototype(g, typ, typ.owner, kind, info, idgen, isDistinct = isDistinct)
+
+  var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType: typ, idgen: idgen,
+                   fn: result)
+
+  let dest = if kind == attachedDup: result.ast[resultPos].sym else: result.typ.n[1].sym
+  let d = if result.typ.firstParamType.kind == tyVar: newDeref(newSymNode(dest)) else: newSymNode(dest)
+  let src = case kind
+            of {attachedDestructor, attachedWasMoved}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
+            of attachedDup: newSymNode(result.typ.n[1].sym)
+            else: newSymNode(result.typ.n[2].sym)
+
+  # register this operation already:
+  setAttachedOpPartial(g, idgen.module, typ, kind, result)
+
+  if kind == attachedSink and destructorOverridden(g, typ):
+    ## compiler can use a combination of `=destroy` and memCopy for sink op
+    dest.flags.incl sfCursor
+    let op = getAttachedOp(g, typ, attachedDestructor)
+    result.ast[bodyPos].add newOpCall(a, op, if op.typ.firstParamType.kind == tyVar: d[0] else: d)
+    result.ast[bodyPos].add newAsgnStmt(d, src)
+  else:
+    var tk: TTypeKind
+    if g.config.selectedGC in {gcArc, gcOrc, gcHooks, gcAtomicArc}:
+      tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind
+    else:
+      tk = tyNone # no special casing for strings and seqs
+    case tk
+    of tySequence:
+      fillSeqOp(a, typ, result.ast[bodyPos], d, src)
+    of tyString:
+      fillStrOp(a, typ, result.ast[bodyPos], d, src)
+    else:
+      fillBody(a, typ, result.ast[bodyPos], d, src)
+      if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy, attachedDup} and not lacksMTypeField(typ):
+        # bug #19205: Do not forget to also copy the hidden type field:
+        genTypeFieldCopy(a, typ, result.ast[bodyPos], d, src)
+
+  if not a.canRaise:
+    incl result.flags, sfNeverRaises
+    result.ast[pragmasPos] = newNodeI(nkPragma, info)
+    result.ast[pragmasPos].add newTree(nkExprColonExpr,
+        newIdentNode(g.cache.getIdent("raises"),  info), newNodeI(nkBracket, info))
+
+  completePartialOp(g, idgen.module, typ, kind, result)
+
+
+proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym,
+                                        info: TLineInfo; idgen: IdGenerator): PSym =
+  assert(typ.skipTypes({tyAlias, tyGenericInst}).kind == tyObject)
+  # discrimantor assignments needs pointers to destroy fields; alas, we cannot use non-var destructor here
+  result = symPrototype(g, field.typ, typ.owner, attachedDestructor, info, idgen, isDiscriminant = true)
+  var a = TLiftCtx(info: info, g: g, kind: attachedDestructor, asgnForType: typ, idgen: idgen,
+                   fn: result)
+  a.asgnForType = typ
+  a.filterDiscriminator = field
+  a.addMemReset = true
+  let discrimantDest = result.typ.n[1].sym
+
+  let dst = newSym(skVar, getIdent(g.cache, "dest"), idgen, result, info)
+  dst.typ = makePtrType(typ.owner, typ, idgen)
+  let dstSym = newSymNode(dst)
+  let d = newDeref(dstSym)
+  let v = newNodeI(nkVarSection, info)
+  v.addVar(dstSym, genContainerOf(a, typ, field, discrimantDest))
+  result.ast[bodyPos].add v
+  let placeHolder = newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
+  fillBody(a, typ, result.ast[bodyPos], d, placeHolder)
+  if not a.canRaise: incl result.flags, sfNeverRaises
+
+
+template liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
+  discard "now a nop"
+
+proc patchBody(g: ModuleGraph; c: PContext; n: PNode; info: TLineInfo; idgen: IdGenerator) =
+  if n.kind in nkCallKinds:
+    if n[0].kind == nkSym and n[0].sym.magic == mDestroy:
+      let t = n[1].typ.skipTypes(abstractVar)
+      if getAttachedOp(g, t, attachedDestructor) == nil:
+        discard produceSym(g, c, t, attachedDestructor, info, idgen)
+
+      let op = getAttachedOp(g, t, attachedDestructor)
+      if op != nil:
+        if op.ast.isGenericRoutine:
+          internalError(g.config, info, "resolved destructor is generic")
+        if op.magic == mDestroy and t.kind != tyString:
+          internalError(g.config, info, "patching mDestroy with mDestroy?")
+        n[0] = newSymNode(op)
+  for x in n: patchBody(g, c, x, info, idgen)
+
+proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: IdGenerator;
+          info: TLineInfo) =
+  let op = getAttachedOp(g, t, kind)
+  if op != nil and op.ast != nil and op.ast.isGenericRoutine:
+    if t.typeInst != nil:
+      var a = TLiftCtx(info: info, g: g, kind: kind, c: c, idgen: idgen)
+      let opInst = instantiateGeneric(a, op, t, t.typeInst)
+      if opInst.ast != nil:
+        patchBody(g, c, opInst.ast, info, a.idgen)
+      setAttachedOp(g, idgen.module, t, kind, opInst)
+    else:
+      localError(g.config, info, "unresolved generic parameter")
+
+proc isTrival*(s: PSym): bool {.inline.} =
+  s == nil or (s.ast != nil and s.ast[bodyPos].len == 0)
+
+proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
+                        idgen: IdGenerator) =
+  ## In the semantic pass this is called in strategic places
+  ## to ensure we lift assignment, destructors and moves properly.
+  ## The later 'injectdestructors' pass depends on it.
+  if orig == nil or {tfCheckedForDestructor, tfHasMeta} * orig.flags != {}: return
+  incl orig.flags, tfCheckedForDestructor
+
+  let skipped = orig.skipTypes({tyGenericInst, tyAlias, tySink})
+  if isEmptyContainer(skipped) or skipped.kind == tyStatic: return
+
+  let h = sighashes.hashType(skipped, g.config, {CoType, CoConsiderOwned, CoDistinct})
+  var canon = g.canonTypes.getOrDefault(h)
+  if canon == nil:
+    g.canonTypes[h] = skipped
+    canon = skipped
+
+  # multiple cases are to distinguish here:
+  # 1. we don't know yet if 'typ' has a nontrival destructor.
+  # 2. we have a nop destructor. --> mDestroy
+  # 3. we have a lifted destructor.
+  # 4. We have a custom destructor.
+  # 5. We have a (custom) generic destructor.
+
+  # we do not generate '=trace' procs if we
+  # have the cycle detection disabled, saves code size.
+  let lastAttached = if g.config.selectedGC == gcOrc: attachedTrace
+                     else: attachedSink
+
+  # bug #15122: We need to produce all prototypes before entering the
+  # mind boggling recursion. Hacks like these imply we should rewrite
+  # this module.
+  var generics: array[attachedWasMoved..attachedTrace, bool] = default(array[attachedWasMoved..attachedTrace, bool])
+  for k in attachedWasMoved..lastAttached:
+    generics[k] = getAttachedOp(g, canon, k) != nil
+    if not generics[k]:
+      setAttachedOp(g, idgen.module, canon, k,
+          symPrototype(g, canon, canon.owner, k, info, idgen))
+
+  # we generate the destructor first so that other operators can depend on it:
+  for k in attachedWasMoved..lastAttached:
+    if not generics[k]:
+      discard produceSym(g, c, canon, k, info, idgen)
+    else:
+      inst(g, c, canon, k, idgen, info)
+    if canon != orig:
+      setAttachedOp(g, idgen.module, orig, k, getAttachedOp(g, canon, k))
+
+  if not isTrival(getAttachedOp(g, orig, attachedDestructor)):
+    #or not isTrival(orig.assignment) or
+    # not isTrival(orig.sink):
+    orig.flags.incl tfHasAsgn
+    # ^ XXX Breaks IC!
diff --git a/compiler/liftlocals.nim b/compiler/liftlocals.nim
index ae789cd88..aaa0707e0 100644
--- a/compiler/liftlocals.nim
+++ b/compiler/liftlocals.nim
@@ -10,9 +10,11 @@
 ## This module implements the '.liftLocals' pragma.
 
 import
-  intsets, strutils, options, ast, astalgo, msgs,
+  options, ast, msgs,
   idents, renderer, types, lowerings, lineinfos
 
+import std/strutils
+
 from pragmas import getPragmaVal
 from wordrecg import wLiftLocals
 
@@ -21,19 +23,20 @@ type
     partialParam: PSym
     objType: PType
     cache: IdentCache
+    idgen: IdGenerator
 
 proc interestingVar(s: PSym): bool {.inline.} =
   result = s.kind in {skVar, skLet, skTemp, skForVar, skResult} and
     sfGlobal notin s.flags
 
 proc lookupOrAdd(c: var Ctx; s: PSym; info: TLineInfo): PNode =
-  let field = addUniqueField(c.objType, s, c.cache)
+  let field = addUniqueField(c.objType, s, c.cache, c.idgen)
   var deref = newNodeI(nkHiddenDeref, info)
   deref.typ = c.objType
-  add(deref, newSymNode(c.partialParam, info))
+  deref.add(newSymNode(c.partialParam, info))
   result = newNodeI(nkDotExpr, info)
-  add(result, deref)
-  add(result, newSymNode(field))
+  result.add(deref)
+  result.add(newSymNode(field))
   result.typ = field.typ
 
 proc liftLocals(n: PNode; i: int; c: var Ctx) =
@@ -42,18 +45,20 @@ proc liftLocals(n: PNode; i: int; c: var Ctx) =
   of nkSym:
     if interestingVar(it.sym):
       n[i] = lookupOrAdd(c, it.sym, it.info)
-  of procDefs, nkTypeSection: discard
+  of procDefs, nkTypeSection, nkMixinStmt, nkBindStmt: discard
   else:
-    for i in 0 ..< it.safeLen:
+    for i in 0..<it.safeLen:
       liftLocals(it, i, c)
 
 proc lookupParam(params, dest: PNode): PSym =
+  result = nil
   if dest.kind != nkIdent: return nil
-  for i in 1 ..< params.len:
+  for i in 1..<params.len:
     if params[i].kind == nkSym and params[i].sym.name.id == dest.ident.id:
       return params[i].sym
 
-proc liftLocalsIfRequested*(prc: PSym; n: PNode; cache: IdentCache; conf: ConfigRef): PNode =
+proc liftLocalsIfRequested*(prc: PSym; n: PNode; cache: IdentCache; conf: ConfigRef;
+                            idgen: IdGenerator): PNode =
   let liftDest = getPragmaVal(prc.ast, wLiftLocals)
   if liftDest == nil: return n
   let partialParam = lookupParam(prc.typ.n, liftDest)
@@ -65,7 +70,7 @@ proc liftLocalsIfRequested*(prc: PSym; n: PNode; cache: IdentCache; conf: Config
   if objType.kind != tyObject or tfPartial notin objType.flags:
     localError(conf, liftDest.info, "parameter '$1' is not a pointer to a partial object" % $liftDest)
     return n
-  var c = Ctx(partialParam: partialParam, objType: objType, cache: cache)
+  var c = Ctx(partialParam: partialParam, objType: objType, cache: cache, idgen: idgen)
   let w = newTree(nkStmtList, n)
   liftLocals(w, 0, c)
   result = w[0]
diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim
index cad1fe6aa..94a483299 100644
--- a/compiler/lineinfos.nim
+++ b/compiler/lineinfos.nim
@@ -7,187 +7,273 @@
 #    distribution, for details about the copyright.
 #
 
-## This module contains the ``TMsgKind`` enum as well as the
-## ``TLineInfo`` object.
+## This module contains the `TMsgKind` enum as well as the
+## `TLineInfo` object.
 
-import ropes, tables
+import ropes, pathutils
+import std/[hashes, tables]
 
 const
-  explanationsBaseUrl* = "https://nim-lang.org/docs/manual"
+  explanationsBaseUrl* = "https://nim-lang.github.io/Nim"
+    # was: "https://nim-lang.org/docs" but we're now usually showing devel docs
+    # instead of latest release docs.
+
+proc createDocLink*(urlSuffix: string): string =
+  # os.`/` is not appropriate for urls.
+  result = explanationsBaseUrl
+  if urlSuffix.len > 0 and urlSuffix[0] == '/':
+    result.add urlSuffix
+  else:
+    result.add "/" & urlSuffix
 
 type
   TMsgKind* = enum
-    errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile,
+    # fatal errors
+    errUnknown, errFatal, errInternal,
+    # non-fatal errors
+    errIllFormedAstX, errCannotOpenFile,
     errXExpected,
-    errGridTableNotImplemented,
-    errGeneralParseError,
-    errNewSectionExpected,
-    errInvalidDirectiveX,
+    errRstMissingClosing,
+    errRstGridTableNotImplemented,
+    errRstMarkdownIllformedTable,
+    errRstIllformedTable,
+    errRstNewSectionExpected,
+    errRstGeneralParseError,
+    errRstInvalidDirectiveX,
+    errRstInvalidField,
+    errRstFootnoteMismatch,
+    errRstSandboxedDirective,
+    errProveInit, # deadcode
     errGenerated,
+    errFailedMove,
     errUser,
-    warnCannotOpenFile,
-    warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
-    warnDeprecated, warnConfigDeprecated,
-    warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
-    warnUnknownSubstitutionX, warnLanguageXNotSupported,
-    warnFieldXNotSupported, warnCommentXIgnored,
-    warnTypelessParam,
-    warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
-    warnEachIdentIsTuple, warnShadowIdent,
-    warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
-    warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
-    warnInconsistentSpacing, warnUser,
-    hintSuccess, hintSuccessX,
-    hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
-    hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
-    hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
-    hintConditionAlwaysTrue, hintName, hintPattern,
-    hintExecuting, hintLinking, hintDependency,
-    hintSource, hintPerformance, hintStackTrace, hintGCStats,
-    hintGlobalVar,
-    hintUser, hintUserRaw
+    # warnings
+    warnCannotOpenFile = "CannotOpenFile", warnOctalEscape = "OctalEscape",
+    warnXIsNeverRead = "XIsNeverRead", warnXmightNotBeenInit = "XmightNotBeenInit",
+    warnDeprecated = "Deprecated", warnConfigDeprecated = "ConfigDeprecated",
+    warnDotLikeOps = "DotLikeOps",
+    warnSmallLshouldNotBeUsed = "SmallLshouldNotBeUsed", warnUnknownMagic = "UnknownMagic",
+    warnRstRedefinitionOfLabel = "RedefinitionOfLabel",
+    warnRstUnknownSubstitutionX = "UnknownSubstitutionX",
+    warnRstAmbiguousLink = "AmbiguousLink",
+    warnRstBrokenLink = "BrokenLink",
+    warnRstLanguageXNotSupported = "LanguageXNotSupported",
+    warnRstFieldXNotSupported = "FieldXNotSupported",
+    warnRstUnusedImportdoc = "UnusedImportdoc",
+    warnRstStyle = "warnRstStyle",
+    warnCommentXIgnored = "CommentXIgnored",
+    warnTypelessParam = "TypelessParam",
+    warnUseBase = "UseBase", warnWriteToForeignHeap = "WriteToForeignHeap",
+    warnUnsafeCode = "UnsafeCode", warnUnusedImportX = "UnusedImport",
+    warnInheritFromException = "InheritFromException", warnEachIdentIsTuple = "EachIdentIsTuple",
+    warnUnsafeSetLen = "UnsafeSetLen", warnUnsafeDefault = "UnsafeDefault",
+    warnProveInit = "ProveInit", warnProveField = "ProveField", warnProveIndex = "ProveIndex",
+    warnUnreachableElse = "UnreachableElse", warnUnreachableCode = "UnreachableCode",
+    warnStaticIndexCheck = "IndexCheck", warnGcUnsafe = "GcUnsafe", warnGcUnsafe2 = "GcUnsafe2",
+    warnUninit = "Uninit", warnGcMem = "GcMem", warnDestructor = "Destructor",
+    warnLockLevel = "LockLevel", # deadcode
+    warnResultShadowed = "ResultShadowed",
+    warnInconsistentSpacing = "Spacing",  warnCaseTransition = "CaseTransition",
+    warnCycleCreated = "CycleCreated", warnObservableStores = "ObservableStores",
+    warnStrictNotNil = "StrictNotNil",
+    warnResultUsed = "ResultUsed",
+    warnCannotOpen = "CannotOpen",
+    warnFileChanged = "FileChanged",
+    warnSuspiciousEnumConv = "EnumConv",
+    warnAnyEnumConv = "AnyEnumConv",
+    warnHoleEnumConv = "HoleEnumConv",
+    warnCstringConv = "CStringConv",
+    warnPtrToCstringConv = "PtrToCstringConv",
+    warnEffect = "Effect",
+    warnCastSizes = "CastSizes", # deadcode
+    warnAboveMaxSizeSet = "AboveMaxSizeSet",
+    warnImplicitTemplateRedefinition = "ImplicitTemplateRedefinition",
+    warnUnnamedBreak = "UnnamedBreak",
+    warnStmtListLambda = "StmtListLambda",
+    warnBareExcept = "BareExcept",
+    warnImplicitDefaultValue = "ImplicitDefaultValue",
+    warnIgnoredSymbolInjection = "IgnoredSymbolInjection",
+    warnStdPrefix = "StdPrefix"
+    warnUser = "User",
+    warnGlobalVarConstructorTemporary = "GlobalVarConstructorTemporary",
+    # hints
+    hintSuccess = "Success", hintSuccessX = "SuccessX",
+    hintCC = "CC",
+    hintXDeclaredButNotUsed = "XDeclaredButNotUsed", hintDuplicateModuleImport = "DuplicateModuleImport",
+    hintXCannotRaiseY = "XCannotRaiseY", hintConvToBaseNotNeeded = "ConvToBaseNotNeeded",
+    hintConvFromXtoItselfNotNeeded = "ConvFromXtoItselfNotNeeded", hintExprAlwaysX = "ExprAlwaysX",
+    hintQuitCalled = "QuitCalled", hintProcessing = "Processing", hintProcessingStmt = "ProcessingStmt", hintCodeBegin = "CodeBegin",
+    hintCodeEnd = "CodeEnd", hintConf = "Conf", hintPath = "Path",
+    hintConditionAlwaysTrue = "CondTrue", hintConditionAlwaysFalse = "CondFalse", hintName = "Name",
+    hintPattern = "Pattern", hintExecuting = "Exec", hintLinking = "Link", hintDependency = "Dependency",
+    hintSource = "Source", hintPerformance = "Performance", hintStackTrace = "StackTrace",
+    hintGCStats = "GCStats", hintGlobalVar = "GlobalVar", hintExpandMacro = "ExpandMacro",
+    hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
+    hintMsgOrigin = "MsgOrigin", # since 1.3.5
+    hintDeclaredLoc = "DeclaredLoc", # since 1.5.1
+    hintUnknownHint = "UnknownHint"
 
 const
   MsgKindToStr*: array[TMsgKind, string] = [
     errUnknown: "unknown error",
+    errFatal: "fatal error: $1",
     errInternal: "internal error: $1",
     errIllFormedAstX: "illformed AST: $1",
     errCannotOpenFile: "cannot open '$1'",
     errXExpected: "'$1' expected",
-    errGridTableNotImplemented: "grid table is not implemented",
-    errGeneralParseError: "general parse error",
-    errNewSectionExpected: "new section expected",
-    errInvalidDirectiveX: "invalid directive: '$1'",
+    errRstMissingClosing: "$1",
+    errRstGridTableNotImplemented: "grid table is not implemented",
+    errRstMarkdownIllformedTable: "illformed delimiter row of a markdown table",
+    errRstIllformedTable: "Illformed table: $1",
+    errRstNewSectionExpected: "new section expected $1",
+    errRstGeneralParseError: "general parse error",
+    errRstInvalidDirectiveX: "invalid directive: '$1'",
+    errRstInvalidField: "invalid field: $1",
+    errRstFootnoteMismatch: "number of footnotes and their references don't match: $1",
+    errRstSandboxedDirective: "disabled directive: '$1'",
+    errProveInit: "Cannot prove that '$1' is initialized.",  # deadcode
     errGenerated: "$1",
+    errFailedMove: "$1",
     errUser: "$1",
     warnCannotOpenFile: "cannot open '$1'",
     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",
+    warnDotLikeOps: "$1",
     warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)",
     warnUnknownMagic: "unknown magic '$1' might crash the compiler",
-    warnRedefinitionOfLabel: "redefinition of label '$1'",
-    warnUnknownSubstitutionX: "unknown substitution '$1'",
-    warnLanguageXNotSupported: "language '$1' not supported",
-    warnFieldXNotSupported: "field '$1' not supported",
+    warnRstRedefinitionOfLabel: "redefinition of label '$1'",
+    warnRstUnknownSubstitutionX: "unknown substitution '$1'",
+    warnRstAmbiguousLink: "ambiguous doc link $1",
+    warnRstBrokenLink: "broken link '$1'",
+    warnRstLanguageXNotSupported: "language '$1' not supported",
+    warnRstFieldXNotSupported: "field '$1' not supported",
+    warnRstUnusedImportdoc: "importdoc for '$1' is not used",
+    warnRstStyle: "RST style: $1",
     warnCommentXIgnored: "comment '$1' ignored",
-    warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
+    warnTypelessParam: "", # deadcode
     warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
     warnWriteToForeignHeap: "write to foreign heap",
     warnUnsafeCode: "unsafe code: '$1'",
+    warnUnusedImportX: "imported and not used: '$1'",
+    warnInheritFromException: "inherit from a more precise exception type like ValueError, " &
+      "IOError or OSError. If these don't suit, inherit from CatchableError or Defect.",
     warnEachIdentIsTuple: "each identifier is a tuple",
-    warnShadowIdent: "shadowed identifier: '$1'",
+    warnUnsafeSetLen: "setLen can potentially expand the sequence, " &
+                      "but the element type '$1' doesn't have a valid default value",
+    warnUnsafeDefault: "The '$1' type doesn't have a valid default value",
     warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.",
     warnProveField: "cannot prove that field '$1' is accessible",
     warnProveIndex: "cannot prove index '$1' is valid",
+    warnUnreachableElse: "unreachable else, all cases are already covered",
+    warnUnreachableCode: "unreachable code after 'return' statement or '{.noReturn.}' proc",
+    warnStaticIndexCheck: "$1",
     warnGcUnsafe: "not GC-safe: '$1'",
     warnGcUnsafe2: "$1",
-    warnUninit: "'$1' might not have been initialized",
+    warnUninit: "use explicit initialization of '$1' for clarity",
     warnGcMem: "'$1' uses GC'ed memory",
     warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.",
-    warnLockLevel: "$1",
+    warnLockLevel: "$1", # deadcode
     warnResultShadowed: "Special variable 'result' is shadowed.",
     warnInconsistentSpacing: "Number of spaces around '$#' is not consistent",
+    warnCaseTransition: "Potential object case transition, instantiate new object instead",
+    warnCycleCreated: "$1",
+    warnObservableStores: "observable stores to '$1'",
+    warnStrictNotNil: "$1",
+    warnResultUsed: "used 'result' variable",
+    warnCannotOpen: "cannot open: $1",
+    warnFileChanged: "file changed: $1",
+    warnSuspiciousEnumConv: "$1",
+    warnAnyEnumConv: "$1",
+    warnHoleEnumConv: "$1",
+    warnCstringConv: "$1",
+    warnPtrToCstringConv: "unsafe conversion to 'cstring' from '$1'; Use a `cast` operation like `cast[cstring](x)`; this will become a compile time error in the future",
+    warnEffect: "$1",
+    warnCastSizes: "$1", # deadcode
+    warnAboveMaxSizeSet: "$1",
+    warnImplicitTemplateRedefinition: "template '$1' is implicitly redefined; this is deprecated, add an explicit .redefine pragma",
+    warnUnnamedBreak: "Using an unnamed break in a block is deprecated; Use a named block with a named break instead",
+    warnStmtListLambda: "statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead",
+    warnBareExcept: "$1",
+    warnImplicitDefaultValue: "$1",
+    warnIgnoredSymbolInjection: "$1",
+    warnStdPrefix: "$1 needs the 'std' prefix",
     warnUser: "$1",
-    hintSuccess: "operation successful",
-    hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
-    hintLineTooLong: "line too long",
+    warnGlobalVarConstructorTemporary: "global variable '$1' initialization requires a temporary variable",
+    hintSuccess: "operation successful: $#",
+    # keep in sync with `testament.isSuccess`
+    hintSuccessX: "$build\n$loc lines; ${sec}s; $mem; proj: $project; out: $output",
+    hintCC: "CC: $1",
     hintXDeclaredButNotUsed: "'$1' is declared but not used",
+    hintDuplicateModuleImport: "$1",
+    hintXCannotRaiseY: "$1",
     hintConvToBaseNotNeeded: "conversion to base object is not needed",
     hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless",
     hintExprAlwaysX: "expression evaluates always to '$1'",
     hintQuitCalled: "quit() called",
     hintProcessing: "$1",
+    hintProcessingStmt: "$1",
     hintCodeBegin: "generated code listing:",
     hintCodeEnd: "end of listing",
     hintConf: "used config file '$1'",
     hintPath: "added path: '$1'",
     hintConditionAlwaysTrue: "condition is always true: '$1'",
-    hintName: "name should be: '$1'",
+    hintConditionAlwaysFalse: "condition is always false: '$1'",
+    hintName: "$1",
     hintPattern: "$1",
     hintExecuting: "$1",
-    hintLinking: "",
+    hintLinking: "$1",
     hintDependency: "$1",
     hintSource: "$1",
     hintPerformance: "$1",
     hintStackTrace: "$1",
     hintGCStats: "$1",
     hintGlobalVar: "global variable declared here",
+    hintExpandMacro: "expanded macro: $1",
     hintUser: "$1",
-    hintUserRaw: "$1"]
+    hintUserRaw: "$1",
+    hintExtendedContext: "$1",
+    hintMsgOrigin: "$1",
+    hintDeclaredLoc: "$1",
+    hintUnknownHint: "unknown hint: $1"
+  ]
 
 const
-  WarningsToStr* = ["CannotOpenFile", "OctalEscape",
-    "XIsNeverRead", "XmightNotBeenInit",
-    "Deprecated", "ConfigDeprecated",
-    "SmallLshouldNotBeUsed", "UnknownMagic",
-    "RedefinitionOfLabel", "UnknownSubstitutionX",
-    "LanguageXNotSupported", "FieldXNotSupported",
-    "CommentXIgnored",
-    "TypelessParam", "UseBase", "WriteToForeignHeap",
-    "UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
-    "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
-    "GcMem", "Destructor", "LockLevel", "ResultShadowed",
-    "Spacing", "User"]
-
-  HintsToStr* = ["Success", "SuccessX", "LineTooLong",
-    "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
-    "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
-    "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency",
-    "Source", "Performance", "StackTrace", "GCStats", "GlobalVar",
-    "User", "UserRaw"]
-
-const
-  fatalMin* = errUnknown
-  fatalMax* = errInternal
+  fatalMsgs* = {errUnknown..errInternal}
   errMin* = errUnknown
   errMax* = errUser
   warnMin* = warnCannotOpenFile
   warnMax* = pred(hintSuccess)
   hintMin* = hintSuccess
   hintMax* = high(TMsgKind)
-
-static:
-  doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1
-  doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1
+  rstWarnings* = {warnRstRedefinitionOfLabel..warnRstStyle}
 
 type
   TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
   TNoteKinds* = set[TNoteKind]
 
-const
-  NotesVerbosity*: array[0..3, TNoteKinds] = [
-    {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
-                                         warnProveField, warnProveIndex,
-                                         warnGcUnsafe,
-                                         hintSuccessX, hintPath, hintConf,
-                                         hintProcessing, hintPattern,
-                                         hintDependency,
-                                         hintExecuting, hintLinking,
-                                         hintCodeBegin, hintCodeEnd,
-                                         hintSource, hintStackTrace,
-                                         hintGlobalVar, hintGCStats},
-    {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
-                                         warnProveField, warnProveIndex,
-                                         warnGcUnsafe,
-                                         hintPath,
-                                         hintDependency,
-                                         hintCodeBegin, hintCodeEnd,
-                                         hintSource, hintStackTrace,
-                                         hintGlobalVar, hintGCStats},
-    {low(TNoteKind)..high(TNoteKind)} - {hintStackTrace, warnUninit},
-    {low(TNoteKind)..high(TNoteKind)}]
+proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
+  result = default(array[0..3, TNoteKinds])
+  result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv, warnBareExcept, warnStdPrefix}
+  result[2] = result[3] - {hintStackTrace, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt}
+  result[1] = result[2] - {warnProveField, warnProveIndex,
+    warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
+    hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin, hintPerformance}
+  result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf,
+    hintProcessing, hintPattern, hintExecuting, hintLinking, hintCC}
 
 const
+  NotesVerbosity* = computeNotesVerbosity()
   errXMustBeCompileTime* = "'$1' can only be used in compile-time context"
   errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected"
+  errFloatToString* = "cannot convert '$1' to '$2'"
 
 type
   TFileInfo* = object
-    fullPath*: string          # This is a canonical full filesystem path
-    projPath*: string          # This is relative to the project's root
+    fullPath*: AbsoluteFile    # This is a canonical full filesystem path
+    projPath*: RelativeFile    # This is relative to the project's root
     shortName*: string         # short name of the module
     quotedName*: Rope          # cached quoted short name for codegen
                                # purposes
@@ -198,11 +284,11 @@ type
                                #   used for better error messages and
                                #   embedding the original source in the
                                #   generated code
-    dirtyfile*: string         # the file that is actually read into memory
+    dirtyFile*: AbsoluteFile   # the file that is actually read into memory
                                # and parsed; usually "" but is used
                                # for 'nimsuggest'
     hash*: string              # the checksum of the file
-    dirty*: bool               # for 'nimfix' / 'nimpretty' like tooling
+    dirty*: bool               # for 'nimpretty' like tooling
     when defined(nimpretty):
       fullContent*: string
   FileIndex* = distinct int32
@@ -226,24 +312,28 @@ type
   TErrorOutputs* = set[TErrorOutput]
 
   ERecoverableError* = object of ValueError
-  ESuggestDone* = object of Exception
+  ESuggestDone* = object of ValueError
 
 proc `==`*(a, b: FileIndex): bool {.borrow.}
 
-const
-  InvalidFileIDX* = FileIndex(-1)
+proc hash*(i: TLineInfo): Hash =
+  hash (i.line.int, i.col.int, i.fileIndex.int)
 
-proc unknownLineInfo*(): TLineInfo =
-  result.line = uint16(0)
-  result.col = int16(-1)
-  result.fileIndex = InvalidFileIDX
+proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} =
+  raise newException(ERecoverableError, msg)
+
+const
+  InvalidFileIdx* = FileIndex(-1)
+  unknownLineInfo* = TLineInfo(line: 0, col: -1, fileIndex: InvalidFileIdx)
 
 type
   Severity* {.pure.} = enum ## VS Code only supports these three
     Hint, Warning, Error
 
-const trackPosInvalidFileIdx* = FileIndex(-2) # special marker so that no suggestions
-                                   # are produced within comments and string literals
+const
+  trackPosInvalidFileIdx* = FileIndex(-2) # special marker so that no suggestions
+                                          # are produced within comments and string literals
+  commandLineIdx* = FileIndex(-3)
 
 type
   MsgConfig* = object ## does not need to be stored in the incremental cache
@@ -252,7 +342,7 @@ type
                             ## some close token.
 
     errorOutputs*: TErrorOutputs
-    msgContext*: seq[TLineInfo]
+    msgContext*: seq[tuple[info: TLineInfo, detail: string]]
     lastError*: TLineInfo
     filenameToIndexTbl*: Table[string, FileIndex]
     fileInfos*: seq[TFileInfo]
@@ -260,8 +350,8 @@ type
 
 
 proc initMsgConfig*(): MsgConfig =
-  result.msgContext = @[]
-  result.lastError = unknownLineInfo()
-  result.filenameToIndexTbl = initTable[string, FileIndex]()
-  result.fileInfos = @[]
-  result.errorOutputs = {eStdOut, eStdErr}
+  result = MsgConfig(msgContext: @[], lastError: unknownLineInfo,
+                     filenameToIndexTbl: initTable[string, FileIndex](),
+                     fileInfos: @[], errorOutputs: {eStdOut, eStdErr}
+  )
+  result.filenameToIndexTbl["???"] = FileIndex(-1)
diff --git a/compiler/linter.nim b/compiler/linter.nim
index 7c9cdec83..a80c377e9 100644
--- a/compiler/linter.nim
+++ b/compiler/linter.nim
@@ -7,53 +7,24 @@
 #    distribution, for details about the copyright.
 #
 
-## This module implements the code "prettifier". This is part of the toolchain
-## to convert Nim code into a consistent style.
+## This module implements the style checker.
 
-import
-  strutils, os, intsets, strtabs
+import std/strutils
+from std/sugar import dup
 
-import options, ast, astalgo, msgs, semdata, ropes, idents,
-  lineinfos
+import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs, semdata, packages
+export packages
 
 const
   Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'}
 
 proc identLen*(line: string, start: int): int =
+  result = 0
   while start+result < line.len and line[start+result] in Letters:
     inc result
 
-when false:
-  import prettybase
-
-type
-  StyleCheck* {.pure.} = enum None, Warn, Auto
-
-var
-  gOverWrite* = true
-  gStyleCheck*: StyleCheck
-  gCheckExtern*, gOnlyMainfile*: bool
-
-proc overwriteFiles*(conf: ConfigRef) =
-  let doStrip = options.getConfigVar(conf, "pretty.strip").normalize == "on"
-  for i in 0 .. high(conf.m.fileInfos):
-    if conf.m.fileInfos[i].dirty and
-        (not gOnlyMainfile or FileIndex(i) == conf.projectMainIdx):
-      let newFile = if gOverWrite: conf.m.fileInfos[i].fullpath
-                    else: conf.m.fileInfos[i].fullpath.changeFileExt(".pretty.nim")
-      try:
-        var f = open(newFile, fmWrite)
-        for line in conf.m.fileInfos[i].lines:
-          if doStrip:
-            f.write line.strip(leading = false, trailing = true)
-          else:
-            f.write line
-          f.write(conf.m.fileInfos[i], "\L")
-        f.close
-      except IOError:
-        rawMessage(conf, errGenerated, "cannot open file: " & newFile)
-
 proc `=~`(s: string, a: openArray[string]): bool =
+  result = false
   for x in a:
     if s.startsWith(x): return true
 
@@ -70,10 +41,10 @@ proc beautifyName(s: string, k: TSymKind): string =
     # Types should start with a capital unless builtins like 'int' etc.:
     if s =~ ["int", "uint", "cint", "cuint", "clong", "cstring", "string",
              "char", "byte", "bool", "openArray", "seq", "array", "void",
-             "pointer", "float", "csize", "cdouble", "cchar", "cschar",
-             "cshort", "cu", "nil", "expr", "stmt", "typedesc", "auto", "any",
-             "range", "openarray", "varargs", "set", "cfloat"
-             ]:
+             "pointer", "float", "csize", "csize_t", "cdouble", "cchar", "cschar",
+             "cshort", "cu", "nil", "typedesc", "auto", "any",
+             "range", "openarray", "varargs", "set", "cfloat", "ref", "ptr",
+             "untyped", "typed", "static", "sink", "lent", "type", "owned", "iterable"]:
       result.add s[i]
     else:
       result.add toUpperAscii(s[i])
@@ -88,7 +59,9 @@ proc beautifyName(s: string, k: TSymKind): string =
   inc i
   while i < s.len:
     if s[i] == '_':
-      if i > 0 and s[i-1] in {'A'..'Z'}:
+      if i+1 >= s.len:
+        discard "trailing underscores should be stripped off"
+      elif i > 0 and s[i-1] in {'A'..'Z'}:
         # don't skip '_' as it's essential for e.g. 'GC_disable'
         result.add('_')
         inc i
@@ -102,75 +75,84 @@ proc beautifyName(s: string, k: TSymKind): string =
       result.add s[i]
     inc i
 
-proc differ*(line: string, a, b: int, x: string): bool =
-  let y = line[a..b]
-  result = cmpIgnoreStyle(y, x) == 0 and y != x
+proc differ*(line: string, a, b: int, x: string): string =
+  proc substrEq(s: string, pos, last: int, substr: string): bool =
+    result = true
+    for i in 0..<substr.len:
+      if pos+i > last or s[pos+i] != substr[i]: return false
+
+  result = ""
+  if not substrEq(line, a, b, x):
+    let y = line[a..b]
+    if cmpIgnoreStyle(y, x) == 0:
+      result = y
 
-proc replaceInFile(conf: ConfigRef; info: TLineInfo; newName: string) =
-  let line = conf.m.fileInfos[info.fileIndex.int].lines[info.line.int-1]
+proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
+  let beau = beautifyName(s.name.s, k)
+  if s.name.s != beau:
+    lintReport(conf, info, beau, s.name.s)
+
+template styleCheckDef*(ctx: PContext; info: TLineInfo; sym: PSym; k: TSymKind) =
+  ## Check symbol definitions adhere to NEP1 style rules.
+  if optStyleCheck in ctx.config.options and # ignore if styleChecks are off
+     {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # check only if hint/error is enabled
+     hintName in ctx.config.notes and # ignore if name checks are not requested
+     ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
+     optStyleUsages notin ctx.config.globalOptions and # ignore if requested to only check name usage
+     sym.kind != skResult and # ignore `result`
+     sym.kind != skTemp and # ignore temporary variables created by the compiler
+     sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols???
+     k notin {skType, skGenericParam} and # ignore types and generic params
+     (sym.typ == nil or sym.typ.kind != tyTypeDesc) and # ignore `typedesc`
+     {sfImportc, sfExportc} * sym.flags == {} and # ignore FFI
+     sfAnon notin sym.flags: # ignore if created by compiler
+    nep1CheckDefImpl(ctx.config, info, sym, k)
+
+template styleCheckDef*(ctx: PContext; info: TLineInfo; s: PSym) =
+  ## Check symbol definitions adhere to NEP1 style rules.
+  styleCheckDef(ctx, info, s, s.kind)
+
+template styleCheckDef*(ctx: PContext; s: PSym) =
+  ## Check symbol definitions adhere to NEP1 style rules.
+  styleCheckDef(ctx, s.info, s, s.kind)
+
+proc differs(conf: ConfigRef; info: TLineInfo; newName: string): string =
+  let line = sourceLine(conf, info)
   var first = min(info.col.int, line.len)
   if first < 0: return
   #inc first, skipIgnoreCase(line, "proc ", first)
   while first > 0 and line[first-1] in Letters: dec first
   if first < 0: return
-  if line[first] == '`': inc first
+  if first+1 < line.len and line[first] == '`': inc first
 
   let last = first+identLen(line, first)-1
-  if differ(line, first, last, newName):
-    # last-first+1 != newName.len or
-    var x = line.substr(0, first-1) & newName & line.substr(last+1)
-    system.shallowCopy(conf.m.fileInfos[info.fileIndex.int].lines[info.line.int-1], x)
-    conf.m.fileInfos[info.fileIndex.int].dirty = true
-
-proc checkStyle(conf: ConfigRef; cache: IdentCache; info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
-  let beau = beautifyName(s, k)
-  if s != beau:
-    if gStyleCheck == StyleCheck.Auto:
-      sym.name = getIdent(cache, beau)
-      replaceInFile(conf, info, beau)
-    else:
-      message(conf, info, hintName, beau)
-
-proc styleCheckDefImpl(conf: ConfigRef; cache: IdentCache; info: TLineInfo; s: PSym; k: TSymKind) =
-  # operators stay as they are:
-  if k in {skResult, skTemp} or s.name.s[0] notin Letters: return
-  if k in {skType, skGenericParam} and sfAnon in s.flags: return
-  if {sfImportc, sfExportc} * s.flags == {} or gCheckExtern:
-    checkStyle(conf, cache, info, s.name.s, k, s)
-
-proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
-  # operators stay as they are:
-  if k in {skResult, skTemp} or s.name.s[0] notin Letters: return
-  if k in {skType, skGenericParam} and sfAnon in s.flags: return
-  let beau = beautifyName(s.name.s, k)
-  if s.name.s != beau:
-    message(conf, info, hintName, beau)
-
-template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
-  if optCheckNep1 in conf.globalOptions:
-    nep1CheckDefImpl(conf, info, s, k)
-  when defined(nimfix):
-    if gStyleCheck != StyleCheck.None: styleCheckDefImpl(conf, cache, info, s, k)
-
-template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym) =
-  styleCheckDef(conf, info, s, s.kind)
-template styleCheckDef*(conf: ConfigRef; s: PSym) =
-  styleCheckDef(conf, s.info, s, s.kind)
+  result = differ(line, first, last, newName)
 
 proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) =
-  if info.fileIndex.int < 0: return
-  # we simply convert it to what it looks like in the definition
-  # for consistency
-
-  # operators stay as they are:
-  if s.kind in {skResult, skTemp} or s.name.s[0] notin Letters:
-    return
-  if s.kind in {skType, skGenericParam} and sfAnon in s.flags: return
   let newName = s.name.s
-
-  replaceInFile(conf, info, newName)
-  #if newName == "File": writeStackTrace()
-
-template styleCheckUse*(info: TLineInfo; s: PSym) =
-  when defined(nimfix):
-    if gStyleCheck != StyleCheck.None: styleCheckUseImpl(conf, info, s)
+  let badName = differs(conf, info, newName)
+  if badName.len > 0:
+    lintReport(conf, info, newName, badName, "".dup(addDeclaredLoc(conf, s)))
+
+template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) =
+  ## Check symbol uses match their definition's style.
+  if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
+     hintName in ctx.config.notes and # ignore if name checks are not requested
+     ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
+     sym.kind != skTemp and # ignore temporary variables created by the compiler
+     sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols???
+     sfAnon notin sym.flags: # ignore temporary variables created by the compiler
+    styleCheckUseImpl(ctx.config, info, sym)
+
+proc checkPragmaUseImpl(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) =
+  let wanted = $w
+  if pragmaName != wanted:
+    lintReport(conf, info, wanted, pragmaName)
+
+template checkPragmaUse*(ctx: PContext; info: TLineInfo; w: TSpecialWord; pragmaName: string, sym: PSym) =
+  ## Check builtin pragma uses match their definition's style.
+  ## Note: This only applies to builtin pragmas, not user pragmas.
+  if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
+     hintName in ctx.config.notes and # ignore if name checks are not requested
+     (sym != nil and ctx.config.belongsToProjectPackage(sym)): # ignore foreign packages
+    checkPragmaUseImpl(ctx.config, info, w, pragmaName)
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/llstream.nim b/compiler/llstream.nim
index 42bbb7600..cc8148483 100644
--- a/compiler/llstream.nim
+++ b/compiler/llstream.nim
@@ -10,13 +10,20 @@
 ## Low-level streams for high performance.
 
 import
-  strutils
+  pathutils
 
-# support '-d:useGnuReadline' for backwards compatibility:
-when not defined(windows) and (defined(useGnuReadline) or defined(useLinenoise)):
-  import rdstdin
+when defined(nimPreviewSlimSystem):
+  import std/syncio
+
+# support `useGnuReadline`, `useLinenoise` for backwards compatibility
+const hasRstdin = (defined(nimUseLinenoise) or defined(useLinenoise) or defined(useGnuReadline)) and
+  not defined(windows)
+
+when hasRstdin: import std/rdstdin
 
 type
+  TLLRepl* = proc (s: PLLStream, buf: pointer, bufLen: int): int
+  OnPrompt* = proc() {.closure.}
   TLLStreamKind* = enum       # enum of different stream implementations
     llsNone,                  # null stream: reading and writing has no effect
     llsString,                # stream encapsulates a string
@@ -28,33 +35,27 @@ type
     s*: string
     rd*, wr*: int             # for string streams
     lineOffset*: int          # for fake stdin line numbers
+    repl*: TLLRepl            # gives stdin control to clients
+    onPrompt*: OnPrompt
 
   PLLStream* = ref TLLStream
 
-proc llStreamOpen*(data: string): PLLStream =
-  new(result)
-  result.s = data
-  result.kind = llsString
+proc llStreamOpen*(data: sink string): PLLStream =
+  PLLStream(kind: llsString, s: data)
 
 proc llStreamOpen*(f: File): PLLStream =
-  new(result)
-  result.f = f
-  result.kind = llsFile
+  PLLStream(kind: llsFile, f: f)
 
-proc llStreamOpen*(filename: string, mode: FileMode): PLLStream =
-  new(result)
-  result.kind = llsFile
-  if not open(result.f, filename, mode): result = nil
+proc llStreamOpen*(filename: AbsoluteFile, mode: FileMode): PLLStream =
+  result = PLLStream(kind: llsFile)
+  if not open(result.f, filename.string, mode): result = nil
 
 proc llStreamOpen*(): PLLStream =
-  new(result)
-  result.kind = llsNone
+  PLLStream(kind: llsNone)
 
-proc llStreamOpenStdIn*(): PLLStream =
-  new(result)
-  result.kind = llsStdIn
-  result.s = ""
-  result.lineOffset = -1
+proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int
+proc llStreamOpenStdIn*(r: TLLRepl = llReadFromStdin, onPrompt: OnPrompt = nil): PLLStream =
+  PLLStream(kind: llsStdIn, s: "", lineOffset: -1, repl: r, onPrompt: onPrompt)
 
 proc llStreamClose*(s: PLLStream) =
   case s.kind
@@ -67,6 +68,7 @@ when not declared(readLineFromStdin):
   # fallback implementation:
   proc readLineFromStdin(prompt: string, line: var string): bool =
     stdout.write(prompt)
+    stdout.flushFile()
     result = readLine(stdin, line)
     if not result:
       stdout.write("\n")
@@ -77,6 +79,8 @@ proc endsWith*(x: string, s: set[char]): bool =
   while i >= 0 and x[i] == ' ': dec(i)
   if i >= 0 and x[i] in s:
     result = true
+  else:
+    result = false
 
 const
   LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^',
@@ -87,13 +91,14 @@ proc endsWithOpr*(x: string): bool =
   result = x.endsWith(LineContinuationOprs)
 
 proc continueLine(line: string, inTripleString: bool): bool {.inline.} =
-  result = inTripleString or
-      line[0] == ' ' or
-      line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs)
+  result = inTripleString or line.len > 0 and (
+        line[0] == ' ' or
+        line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs))
 
 proc countTriples(s: string): int =
+  result = 0
   var i = 0
-  while i < s.len:
+  while i+2 < s.len:
     if s[i] == '"' and s[i+1] == '"' and s[i+2] == '"':
       inc result
       inc i, 2
@@ -105,12 +110,12 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
   var line = newStringOfCap(120)
   var triples = 0
   while readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
-    add(s.s, line)
-    add(s.s, "\n")
+    s.s.add(line)
+    s.s.add("\n")
     inc triples, countTriples(line)
     if not continueLine(line, (triples and 1) == 1): break
   inc(s.lineOffset)
-  result = min(bufLen, len(s.s) - s.rd)
+  result = min(bufLen, s.s.len - s.rd)
   if result > 0:
     copyMem(buf, addr(s.s[s.rd]), result)
     inc(s.rd, result)
@@ -120,14 +125,15 @@ proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int =
   of llsNone:
     result = 0
   of llsString:
-    result = min(bufLen, len(s.s) - s.rd)
+    result = min(bufLen, s.s.len - s.rd)
     if result > 0:
       copyMem(buf, addr(s.s[0 + s.rd]), result)
       inc(s.rd, result)
   of llsFile:
     result = readBuffer(s.f, buf, bufLen)
   of llsStdIn:
-    result = llReadFromStdin(s, buf, bufLen)
+    if s.onPrompt!=nil: s.onPrompt()
+    result = s.repl(s, buf, bufLen)
 
 proc llStreamReadLine*(s: PLLStream, line: var string): bool =
   setLen(line, 0)
@@ -135,19 +141,19 @@ proc llStreamReadLine*(s: PLLStream, line: var string): bool =
   of llsNone:
     result = true
   of llsString:
-    while s.rd < len(s.s):
+    while s.rd < s.s.len:
       case s.s[s.rd]
-      of '\x0D':
+      of '\r':
         inc(s.rd)
-        if s.s[s.rd] == '\x0A': inc(s.rd)
+        if s.s[s.rd] == '\n': inc(s.rd)
         break
-      of '\x0A':
+      of '\n':
         inc(s.rd)
         break
       else:
-        add(line, s.s[s.rd])
+        line.add(s.s[s.rd])
         inc(s.rd)
-    result = line.len > 0 or s.rd < len(s.s)
+    result = line.len > 0 or s.rd < s.s.len
   of llsFile:
     result = readLine(s.f, line)
   of llsStdIn:
@@ -158,8 +164,8 @@ proc llStreamWrite*(s: PLLStream, data: string) =
   of llsNone, llsStdIn:
     discard
   of llsString:
-    add(s.s, data)
-    inc(s.wr, len(data))
+    s.s.add(data)
+    inc(s.wr, data.len)
   of llsFile:
     write(s.f, data)
 
@@ -173,7 +179,7 @@ proc llStreamWrite*(s: PLLStream, data: char) =
   of llsNone, llsStdIn:
     discard
   of llsString:
-    add(s.s, data)
+    s.s.add(data)
     inc(s.wr)
   of llsFile:
     c = data
@@ -185,7 +191,7 @@ proc llStreamWrite*(s: PLLStream, buf: pointer, buflen: int) =
     discard
   of llsString:
     if buflen > 0:
-      setLen(s.s, len(s.s) + buflen)
+      setLen(s.s, s.s.len + buflen)
       copyMem(addr(s.s[0 + s.wr]), buf, buflen)
       inc(s.wr, buflen)
   of llsFile:
@@ -200,7 +206,7 @@ proc llStreamReadAll*(s: PLLStream): string =
   of llsString:
     if s.rd == 0: result = s.s
     else: result = substr(s.s, s.rd)
-    s.rd = len(s.s)
+    s.rd = s.s.len
   of llsFile:
     result = newString(bufSize)
     var bytes = readBuffer(s.f, addr(result[0]), bufSize)
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index 87694988a..d8fcf73e0 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -8,10 +8,16 @@
 #
 
 # This module implements lookup helpers.
+import std/[algorithm, strutils, tables]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 import
-  intsets, ast, astalgo, idents, semdata, types, msgs, options,
-  renderer, wordrecg, idgen, nimfix.prettybase, lineinfos, strutils
+  ast, astalgo, idents, semdata, types, msgs, options,
+  renderer, lineinfos, modulegraphs, astmsgs, wordrecg
+
+import std/[intsets, sets]
 
 proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope)
 
@@ -36,34 +42,41 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
   of nkAccQuoted:
     case n.len
     of 0: handleError(n, origin)
-    of 1: result = considerQuotedIdent(c, n.sons[0], origin)
+    of 1: result = considerQuotedIdent(c, n[0], origin)
     else:
       var id = ""
       for i in 0..<n.len:
-        let x = n.sons[i]
+        let x = n[i]
         case x.kind
         of nkIdent: id.add(x.ident.s)
         of nkSym: id.add(x.sym.name.s)
+        of nkSymChoices, nkOpenSym:
+          if x[0].kind == nkSym:
+            id.add(x[0].sym.name.s)
+          else:
+            handleError(n, origin)
         of nkLiterals - nkFloatLiterals: id.add(x.renderTree)
         else: handleError(n, origin)
       result = getIdent(c.cache, id)
   of nkOpenSymChoice, nkClosedSymChoice:
     if n[0].kind == nkSym:
-      result = n.sons[0].sym.name
+      result = n[0].sym.name
     else:
       handleError(n, origin)
+  of nkOpenSym:
+    result = considerQuotedIdent(c, n[0], origin)
   else:
     handleError(n, origin)
 
 template addSym*(scope: PScope, s: PSym) =
   strTableAdd(scope.symbols, s)
 
-proc addUniqueSym*(scope: PScope, s: PSym): bool =
-  result = not strTableIncl(scope.symbols, s)
+proc addUniqueSym*(scope: PScope, s: PSym): PSym =
+  result = strTableInclReportConflict(scope.symbols, s)
 
 proc openScope*(c: PContext): PScope {.discardable.} =
   result = PScope(parent: c.currentScope,
-                  symbols: newStrTable(),
+                  symbols: initStrTable(),
                   depthLevel: c.scopeDepth + 1)
   c.currentScope = result
 
@@ -74,67 +87,242 @@ proc closeScope*(c: PContext) =
   ensureNoMissingOrUnusedSymbols(c, c.currentScope)
   rawCloseScope(c)
 
-iterator walkScopes*(scope: PScope): PScope =
+iterator allScopes*(scope: PScope): PScope =
   var current = scope
   while current != nil:
     yield current
     current = current.parent
 
-proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym =
-  if s == nil or s.kind != skAlias:
-    result = s
-  else:
-    result = s.owner
-    if conf.cmd == cmdPretty:
-      prettybase.replaceDeprecated(conf, n.info, s, result)
-    else:
-      message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " &
-              s.name.s)
+iterator localScopesFrom*(c: PContext; scope: PScope): PScope =
+  for s in allScopes(scope):
+    if s == c.topLevelScope: break
+    yield s
 
-proc localSearchInScope*(c: PContext, s: PIdent): PSym =
-  result = strTableGet(c.currentScope.symbols, s)
+proc isShadowScope*(s: PScope): bool {.inline.} =
+  s.parent != nil and s.parent.depthLevel == s.depthLevel
 
-proc searchInScopes*(c: PContext, s: PIdent): PSym =
-  for scope in walkScopes(c.currentScope):
+proc localSearchInScope*(c: PContext, s: PIdent): PSym =
+  var scope = c.currentScope
+  result = strTableGet(scope.symbols, s)
+  while result == nil and scope.isShadowScope:
+    # We are in a shadow scope, check in the parent too
+    scope = scope.parent
     result = strTableGet(scope.symbols, s)
-    if result != nil: return
+
+proc initIdentIter(ti: var ModuleIter; marked: var IntSet; im: ImportedModule; name: PIdent;
+                   g: ModuleGraph): PSym =
+  result = initModuleIter(ti, g, im.m, name)
+  while result != nil:
+    let b =
+      case im.mode
+      of importAll: true
+      of importSet: result.id in im.imported
+      of importExcept: name.id notin im.exceptSet
+    if b and not containsOrIncl(marked, result.id):
+      return result
+    result = nextModuleIter(ti, g)
+
+proc nextIdentIter(ti: var ModuleIter; marked: var IntSet; im: ImportedModule;
+                   g: ModuleGraph): PSym =
+  while true:
+    result = nextModuleIter(ti, g)
+    if result == nil: return nil
+    case im.mode
+    of importAll:
+      if not containsOrIncl(marked, result.id):
+        return result
+    of importSet:
+      if result.id in im.imported and not containsOrIncl(marked, result.id):
+        return result
+    of importExcept:
+      if result.name.id notin im.exceptSet and not containsOrIncl(marked, result.id):
+        return result
+
+iterator symbols(im: ImportedModule; marked: var IntSet; name: PIdent; g: ModuleGraph): PSym =
+  var ti: ModuleIter = default(ModuleIter)
+  var candidate = initIdentIter(ti, marked, im, name, g)
+  while candidate != nil:
+    yield candidate
+    candidate = nextIdentIter(ti, marked, im, g)
+
+iterator importedItems*(c: PContext; name: PIdent): PSym =
+  var marked = initIntSet()
+  for im in c.imports.mitems:
+    for s in symbols(im, marked, name, c.graph):
+      yield s
+
+proc allPureEnumFields(c: PContext; name: PIdent): seq[PSym] =
+  var ti: TIdentIter = default(TIdentIter)
+  result = @[]
+  var res = initIdentIter(ti, c.pureEnumFields, name)
+  while res != nil:
+    result.add res
+    res = nextIdentIter(ti, c.pureEnumFields)
+
+iterator allSyms*(c: PContext): (PSym, int, bool) =
+  # really iterate over all symbols in all the scopes. This is expensive
+  # and only used by suggest.nim.
+  var isLocal = true
+
+  var scopeN = 0
+  for scope in allScopes(c.currentScope):
+    if scope == c.topLevelScope: isLocal = false
+    dec scopeN
+    for item in scope.symbols:
+      yield (item, scopeN, isLocal)
+
+  dec scopeN
+  isLocal = false
+  for im in c.imports.mitems:
+    for s in modulegraphs.allSyms(c.graph, im.m):
+      assert s != nil
+      yield (s, scopeN, isLocal)
+
+iterator uniqueSyms*(c: PContext): (PSym, int, bool) =
+  ## Like [allSyms] except only returns unique symbols (Uniqueness determined by line + name)
+  # Track seen symbols so we don't duplicate them.
+  # The int is for the symbols name, and line info is
+  # to be able to tell apart symbols with same name but on different lines
+  var seen = initHashSet[(TLineInfo, int)]()
+  for res in allSyms(c):
+    if not seen.containsOrIncl((res[0].info, res[0].name.id)):
+      yield res
+
+
+proc someSymFromImportTable*(c: PContext; name: PIdent; ambiguous: var bool): PSym =
+  var marked = initIntSet()
+  var symSet = OverloadableSyms
   result = nil
+  block outer:
+    for im in c.imports.mitems:
+      for s in symbols(im, marked, name, c.graph):
+        if result == nil:
+          result = s
+        elif s.kind notin symSet or result.kind notin symSet:
+          ambiguous = true
+          break outer
+
+proc searchInScopes*(c: PContext, s: PIdent; ambiguous: var bool): PSym =
+  for scope in allScopes(c.currentScope):
+    result = strTableGet(scope.symbols, s)
+    if result != nil: return result
+  result = someSymFromImportTable(c, s, ambiguous)
 
-when declared(echo):
-  proc debugScopes*(c: PContext; limit=0) {.deprecated.} =
-    var i = 0
-    for scope in walkScopes(c.currentScope):
-      echo "scope ", i
-      for h in 0 .. high(scope.symbols.data):
-        if scope.symbols.data[h] != nil:
-          echo scope.symbols.data[h].name.s
-      if i == limit: break
-      inc i
-
-proc searchInScopes*(c: PContext, s: PIdent, filter: TSymKinds): PSym =
-  for scope in walkScopes(c.currentScope):
-    var ti: TIdentIter
+proc debugScopes*(c: PContext; limit=0, max = int.high) {.deprecated.} =
+  var i = 0
+  var count = 0
+  for scope in allScopes(c.currentScope):
+    echo "scope ", i
+    for h in 0..high(scope.symbols.data):
+      if scope.symbols.data[h] != nil:
+        if count >= max: return
+        echo count, ": ", scope.symbols.data[h].name.s
+        count.inc
+    if i == limit: return
+    inc i
+
+proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
+  result = @[]
+  for scope in allScopes(c.currentScope):
+    var ti: TIdentIter = default(TIdentIter)
     var candidate = initIdentIter(ti, scope.symbols, s)
     while candidate != nil:
-      if candidate.kind in filter: return candidate
+      if candidate.kind in filter:
+        result.add candidate
       candidate = nextIdentIter(ti, scope.symbols)
-  result = nil
 
-proc errorSym*(c: PContext, n: PNode): PSym =
+  if result.len == 0:
+    var marked = initIntSet()
+    for im in c.imports.mitems:
+      for s in symbols(im, marked, s, c.graph):
+        if s.kind in filter:
+          result.add s
+
+proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
+  result = @[]
+  block outer:
+    for scope in allScopes(c.currentScope):
+      var ti: TIdentIter = default(TIdentIter)
+      var candidate = initIdentIter(ti, scope.symbols, s)
+      while candidate != nil:
+        if candidate.kind in filter:
+          result.add candidate
+          # Break here, because further symbols encountered would be shadowed
+          break outer
+        candidate = nextIdentIter(ti, scope.symbols)
+
+  if result.len == 0:
+    var marked = initIntSet()
+    for im in c.imports.mitems:
+      for s in symbols(im, marked, s, c.graph):
+        if s.kind in filter:
+          result.add s
+
+proc cmpScopes*(ctx: PContext, s: PSym): int =
+  # Do not return a negative number
+  if s.originatingModule == ctx.module:
+    result = 2
+    var owner = s
+    while true:
+      owner = owner.skipGenericOwner
+      if owner.kind == skModule: break
+      inc result
+  else:
+    result = 1
+
+proc isAmbiguous*(c: PContext, s: PIdent, filter: TSymKinds, sym: var PSym): bool =
+  result = false
+  block outer:
+    for scope in allScopes(c.currentScope):
+      var ti: TIdentIter = default(TIdentIter)
+      var candidate = initIdentIter(ti, scope.symbols, s)
+      var scopeHasCandidate = false
+      while candidate != nil:
+        if candidate.kind in filter:
+          if scopeHasCandidate:
+            # 2 candidates in same scope, ambiguous
+            return true
+          else:
+            scopeHasCandidate = true
+            sym = candidate
+        candidate = nextIdentIter(ti, scope.symbols)
+      if scopeHasCandidate:
+        # scope had a candidate but wasn't ambiguous
+        return false
+
+  var importsHaveCandidate = false
+  var marked = initIntSet()
+  for im in c.imports.mitems:
+    for s in symbols(im, marked, s, c.graph):
+      if s.kind in filter:
+        if importsHaveCandidate:
+          # 2 candidates among imports, ambiguous
+          return true
+        else:
+          importsHaveCandidate = true
+          sym = s
+  if importsHaveCandidate:
+    # imports had a candidate but wasn't ambiguous
+    return false
+
+proc errorSym*(c: PContext, ident: PIdent, info: TLineInfo): PSym =
   ## creates an error symbol to avoid cascading errors (for IDE support)
+  result = newSym(skError, ident, c.idgen, getCurrOwner(c), info, {})
+  result.typ = errorType(c)
+  incl(result.flags, sfDiscardable)
+  # pretend it's from the top level scope to prevent cascading errors:
+  if c.config.cmd != cmdInteractive and c.compilesContextId == 0:
+    c.moduleScope.addSym(result)
+
+proc errorSym*(c: PContext, n: PNode): PSym =
   var m = n
   # ensure that 'considerQuotedIdent' can't fail:
-  if m.kind == nkDotExpr: m = m.sons[1]
+  if m.kind == nkDotExpr: m = m[1]
   let ident = if m.kind in {nkIdent, nkSym, nkAccQuoted}:
       considerQuotedIdent(c, m)
     else:
       getIdent(c.cache, "err:" & renderTree(m))
-  result = newSym(skError, ident, getCurrOwner(c), n.info, {})
-  result.typ = errorType(c)
-  incl(result.flags, sfDiscardable)
-  # pretend it's imported from some unknown module to prevent cascading errors:
-  if c.config.cmd != cmdInteractive and c.compilesContextId == 0:
-    c.importTable.addSym(result)
+  result = errorSym(c, ident, n.info)
 
 type
   TOverloadIterMode* = enum
@@ -142,152 +330,309 @@ type
     oimSymChoiceLocalLookup
   TOverloadIter* = object
     it*: TIdentIter
+    mit*: ModuleIter
     m*: PSym
     mode*: TOverloadIterMode
     symChoiceIndex*: int
-    scope*: PScope
-    inSymChoice: IntSet
+    currentScope: PScope
+    importIdx: int
+    marked: IntSet
 
-proc getSymRepr*(conf: ConfigRef; s: PSym): string =
+proc getSymRepr*(conf: ConfigRef; s: PSym, getDeclarationPath = true): string =
   case s.kind
-  of skProc, skFunc, skMethod, skConverter, skIterator:
-    result = getProcHeader(conf, s)
+  of routineKinds, skType:
+    result = getProcHeader(conf, s, getDeclarationPath = getDeclarationPath)
   else:
-    result = s.name.s
+    result = "'$1'" % s.name.s
+    if getDeclarationPath:
+      result.addDeclaredLoc(conf, s)
 
 proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
   # check if all symbols have been used and defined:
-  var it: TTabIter
+  var it: TTabIter = default(TTabIter)
   var s = initTabIter(it, scope.symbols)
   var missingImpls = 0
+  var unusedSyms: seq[tuple[sym: PSym, key: string]] = @[]
   while s != nil:
-    if sfForward in s.flags and s.kind != skType:
+    if sfForward in s.flags and s.kind notin {skType, skModule}:
       # too many 'implementation of X' errors are annoying
       # and slow 'suggest' down:
       if missingImpls == 0:
         localError(c.config, s.info, "implementation of '$1' expected" %
-            getSymRepr(c.config, s))
+            getSymRepr(c.config, s, getDeclarationPath=false))
       inc missingImpls
-    elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options:
-      if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}:
+    elif {sfUsed, sfExported} * s.flags == {}:
+      if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam, skEnumField}:
         # XXX: implicit type params are currently skTypes
         # 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))
+          unusedSyms.add (s, toFileLineCol(c.config, s.info))
     s = nextIter(it, scope.symbols)
+  for (s, _) in sortedByIt(unusedSyms, it.key):
+    message(c.config, s.info, hintXDeclaredButNotUsed, s.name.s)
 
-proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string) =
+proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string;
+                        conflictsWith: TLineInfo, note = errGenerated) =
+  ## Emit a redefinition error if in non-interactive mode
   if c.config.cmd != cmdInteractive:
-    localError(c.config, info, "redefinition of '$1'" % s)
+    localError(c.config, info, note,
+      "redefinition of '$1'; previous declaration here: $2" %
+      [s, c.config $ conflictsWith])
+
+# xxx pending bootstrap >= 1.4, replace all those overloads with a single one:
+# proc addDecl*(c: PContext, sym: PSym, info = sym.info, scope = c.currentScope) {.inline.} =
+proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) =
+  if sym.name.id == ord(wUnderscore): return
+  let conflict = scope.addUniqueSym(sym)
+  if conflict != nil:
+    if sym.kind == skModule and conflict.kind == skModule:
+      # e.g.: import foo; import foo
+      # xxx we could refine this by issuing a different hint for the case
+      # where a duplicate import happens inside an include.
+      if c.importModuleMap[sym.id] == c.importModuleMap[conflict.id]:
+        #only hints if the conflict is the actual module not just a shared name
+        localError(c.config, info, hintDuplicateModuleImport,
+          "duplicate import of '$1'; previous import here: $2" %
+          [sym.name.s, c.config $ conflict.info])
+    else:
+      wrongRedefinition(c, info, sym.name.s, conflict.info, errGenerated)
 
-proc addDecl*(c: PContext, sym: PSym, info: TLineInfo) =
-  if not c.currentScope.addUniqueSym(sym):
-    wrongRedefinition(c, info, sym.name.s)
+proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) {.inline.} =
+  addDeclAt(c, scope, sym, sym.info)
 
-proc addDecl*(c: PContext, sym: PSym) =
-  if not c.currentScope.addUniqueSym(sym):
-    wrongRedefinition(c, sym.info, sym.name.s)
+proc addDecl*(c: PContext, sym: PSym, info: TLineInfo) {.inline.} =
+  addDeclAt(c, c.currentScope, sym, info)
+
+proc addDecl*(c: PContext, sym: PSym) {.inline.} =
+  addDeclAt(c, c.currentScope, sym)
 
 proc addPrelimDecl*(c: PContext, sym: PSym) =
   discard c.currentScope.addUniqueSym(sym)
 
-proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) =
-  if not scope.addUniqueSym(sym):
-    wrongRedefinition(c, sym.info, sym.name.s)
+from ic / ic import addHidden
 
 proc addInterfaceDeclAux(c: PContext, sym: PSym) =
+  ## adds symbol to the module for either private or public access.
   if sfExported in sym.flags:
     # add to interface:
-    if c.module != nil: strTableAdd(c.module.tab, sym)
+    if c.module != nil: exportSym(c, sym)
     else: internalError(c.config, sym.info, "addInterfaceDeclAux")
+  elif sym.kind in ExportableSymKinds and c.module != nil and isTopLevelInsideDeclaration(c, sym):
+    strTableAdd(semtabAll(c.graph, c.module), sym)
+    if c.config.symbolFiles != disabledSf:
+      addHidden(c.encoder, c.packedRepr, sym)
 
 proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) =
+  ## adds a symbol on the scope and the interface if appropriate
   addDeclAt(c, scope, sym)
-  addInterfaceDeclAux(c, sym)
+  if not scope.isShadowScope:
+    # adding into a non-shadow scope, we need to handle exports, etc
+    addInterfaceDeclAux(c, sym)
+
+proc addInterfaceDecl*(c: PContext, sym: PSym) {.inline.} =
+  ## adds a decl and the interface if appropriate
+  addInterfaceDeclAt(c, c.currentScope, sym)
 
 proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) =
+  ## adds an symbol to the given scope, will check for and raise errors if it's
+  ## a redefinition as opposed to an overload.
   if fn.kind notin OverloadableSyms:
     internalError(c.config, fn.info, "addOverloadableSymAt")
     return
-  let check = strTableGet(scope.symbols, fn.name)
-  if check != nil and check.kind notin OverloadableSyms:
-    wrongRedefinition(c, fn.info, fn.name.s)
-  else:
-    scope.addSym(fn)
-
-proc addInterfaceDecl*(c: PContext, sym: PSym) =
-  # it adds the symbol to the interface if appropriate
-  addDecl(c, sym)
-  addInterfaceDeclAux(c, sym)
+  if fn.name.id != ord(wUnderscore):
+    let check = strTableGet(scope.symbols, fn.name)
+    if check != nil and check.kind notin OverloadableSyms:
+      wrongRedefinition(c, fn.info, fn.name.s, check.info)
+    else:
+      scope.addSym(fn)
 
 proc addInterfaceOverloadableSymAt*(c: PContext, scope: PScope, sym: PSym) =
-  # it adds the symbol to the interface if appropriate
+  ## adds an overloadable symbol on the scope and the interface if appropriate
   addOverloadableSymAt(c, scope, sym)
-  addInterfaceDeclAux(c, sym)
-
-when defined(nimfix):
-  # when we cannot find the identifier, retry with a changed identifer:
-  proc altSpelling(x: PIdent): PIdent =
-    case x.s[0]
-    of 'A'..'Z': result = getIdent(toLowerAscii(x.s[0]) & x.s.substr(1))
-    of 'a'..'z': result = getIdent(toLowerAscii(x.s[0]) & x.s.substr(1))
-    else: result = x
-
-  template fixSpelling(n: PNode; ident: PIdent; op: untyped) =
-    let alt = ident.altSpelling
-    result = op(c, alt).skipAlias(n)
-    if result != nil:
-      prettybase.replaceDeprecated(n.info, ident, alt)
-      return result
-else:
-  template fixSpelling(n: PNode; ident: PIdent; op: untyped) = discard
+  if not scope.isShadowScope:
+    # adding into a non-shadow scope, we need to handle exports, etc
+    addInterfaceDeclAux(c, sym)
+
+proc openShadowScope*(c: PContext) =
+  ## opens a shadow scope, just like any other scope except the depth is the
+  ## same as the parent -- see `isShadowScope`.
+  c.currentScope = PScope(parent: c.currentScope,
+                          symbols: initStrTable(),
+                          depthLevel: c.scopeDepth)
+
+proc closeShadowScope*(c: PContext) =
+  ## closes the shadow scope, but doesn't merge any of the symbols
+  ## Does not check for unused symbols or missing forward decls since a macro
+  ## or template consumes this AST
+  rawCloseScope(c)
+
+proc mergeShadowScope*(c: PContext) =
+  ## close the existing scope and merge in all defined symbols, this will also
+  ## trigger any export related code if this is into a non-shadow scope.
+  ##
+  ## Merges:
+  ## shadow -> shadow: add symbols to the parent but check for redefinitions etc
+  ## shadow -> non-shadow: the above, but also handle exports and all that
+  let shadowScope = c.currentScope
+  c.rawCloseScope
+  for sym in shadowScope.symbols:
+    if sym.kind in OverloadableSyms:
+      c.addInterfaceOverloadableSymAt(c.currentScope, sym)
+    else:
+      c.addInterfaceDecl(sym)
+
+
+import std/[editdistance, heapqueue]
+
+type SpellCandidate = object
+  dist: int
+  depth: int
+  msg: string
+  sym: PSym
+
+template toOrderTup(a: SpellCandidate): (int, int, string) =
+  # `dist` is first, to favor nearby matches
+  # `depth` is next, to favor nearby enclosing scopes among ties
+  # `sym.name.s` is last, to make the list ordered and deterministic among ties
+  (a.dist, a.depth, a.msg)
+
+proc `<`(a, b: SpellCandidate): bool =
+  a.toOrderTup < b.toOrderTup
+
+proc mustFixSpelling(c: PContext): bool {.inline.} =
+  result = c.config.spellSuggestMax != 0 and c.compilesContextId == 0
+    # don't slowdown inside compiles()
+
+proc fixSpelling(c: PContext, ident: PIdent, result: var string) =
+  ## when we cannot find the identifier, suggest nearby spellings
+  var list = initHeapQueue[SpellCandidate]()
+  let name0 = ident.s.nimIdentNormalize
+
+  for (sym, depth, isLocal) in allSyms(c):
+    let depth = -depth - 1
+    let dist = editDistance(name0, sym.name.s.nimIdentNormalize)
+    var msg: string = ""
+    msg.add "\n ($1, $2): '$3'" % [$dist, $depth, sym.name.s]
+    list.push SpellCandidate(dist: dist, depth: depth, msg: msg, sym: sym)
+
+  if list.len == 0: return
+  let e0 = list[0]
+  var
+    count = 0
+    last: PIdent = nil
+  while true:
+    # pending https://github.com/timotheecour/Nim/issues/373 use more efficient `itemsSorted`.
+    if list.len == 0: break
+    let e = list.pop()
+    if c.config.spellSuggestMax == spellSuggestSecretSauce:
+      const
+        minLengthForSuggestion = 4
+        maxCount = 3 # avoids ton of matches; three counts for equal distances
+      if e.dist > e0.dist or count >= maxCount or name0.len < minLengthForSuggestion: break
+    elif count >= c.config.spellSuggestMax: break
+    if count == 0:
+      result.add "\ncandidates (edit distance, scope distance); see '--spellSuggest': "
+    if e.sym.name != last:
+      result.add e.msg
+      count.inc
+      last = e.sym.name
+
+proc errorUseQualifier(c: PContext; info: TLineInfo; s: PSym; amb: var bool): PSym =
+  var err = "ambiguous identifier: '" & s.name.s & "'"
+  var i = 0
+  var ignoredModules = 0
+  result = nil
+  for candidate in importedItems(c, s.name):
+    if i == 0: err.add " -- use one of the following:\n"
+    else: err.add "\n"
+    err.add "  " & candidate.owner.name.s & "." & candidate.name.s
+    err.add ": " & typeToString(candidate.typ)
+    if candidate.kind == skModule:
+      inc ignoredModules
+    else:
+      result = candidate
+    inc i
+  if ignoredModules != i-1:
+    localError(c.config, info, errGenerated, err)
+    result = nil
+  else:
+    amb = false
 
 proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
-  var err = "Error: ambiguous identifier: '" & s.name.s & "'"
-  var ti: TIdentIter
-  var candidate = initIdentIter(ti, c.importTable.symbols, s.name)
+  var amb: bool = false
+  discard errorUseQualifier(c, info, s, amb)
+
+proc ambiguousIdentifierMsg*(candidates: seq[PSym], prefix = "use one of", indent = 0): string =
+  result = ""
+  for i in 0 ..< indent:
+    result.add(' ')
+  result.add "ambiguous identifier: '" & candidates[0].name.s & "'"
   var i = 0
-  while candidate != nil:
-    if i == 0: err.add " --use "
-    else: err.add " or "
-    err.add candidate.owner.name.s & "." & candidate.name.s
-    candidate = nextIdentIter(ti, c.importTable.symbols)
+  for candidate in candidates:
+    if i == 0: result.add " -- $1 the following:\n" % prefix
+    else: result.add "\n"
+    for i in 0 ..< indent:
+      result.add(' ')
+    result.add "  " & candidate.owner.name.s & "." & candidate.name.s
+    result.add ": " & typeToString(candidate.typ)
     inc i
-  localError(c.config, info, errGenerated, err)
 
-proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string) =
-  var err = "undeclared identifier: '" & name & "'"
-  if c.recursiveDep.len > 0:
-    err.add "\nThis might be caused by a recursive module dependency: "
-    err.add c.recursiveDep
-    # prevent excessive errors for 'nim check'
-    c.recursiveDep = nil
+proc errorUseQualifier*(c: PContext; info: TLineInfo; candidates: seq[PSym]) =
+  localError(c.config, info, errGenerated, ambiguousIdentifierMsg(candidates))
+
+proc ambiguousIdentifierMsg*(choices: PNode, indent = 0): string =
+  var candidates = newSeq[PSym](choices.len)
+  let prefix = if choices[0].typ.kind != tyProc: "use one of" else: "you need a helper proc to disambiguate"
+  for i, n in choices:
+    candidates[i] = n.sym
+  result = ambiguousIdentifierMsg(candidates, prefix, indent)
+
+proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) =
+  localError(c.config, info, errGenerated, ambiguousIdentifierMsg(choices))
+
+proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extra = "") =
+  var err: string
+  if name == "_":
+    err = "the special identifier '_' is ignored in declarations and cannot be used"
+  else:
+    err = "undeclared identifier: '" & name & "'"
+    if "`gensym" in name:
+      err.add "; if declared in a template, this identifier may be inconsistently marked inject or gensym"
+    if extra.len != 0:
+      err.add extra
+    if c.recursiveDep.len > 0:
+      err.add "\nThis might be caused by a recursive module dependency:\n"
+      err.add c.recursiveDep
+      # prevent excessive errors for 'nim check'
+      c.recursiveDep = ""
   localError(c.config, info, errGenerated, err)
 
+proc errorUndeclaredIdentifierHint*(c: PContext; ident: PIdent; info: TLineInfo): PSym =
+  var extra = ""
+  if c.mustFixSpelling: fixSpelling(c, ident, extra)
+  errorUndeclaredIdentifier(c, info, ident.s, extra)
+  result = errorSym(c, ident, info)
+
 proc lookUp*(c: PContext, n: PNode): PSym =
   # Looks up a symbol. Generates an error in case of nil.
+  var amb = false
   case n.kind
   of nkIdent:
-    result = searchInScopes(c, n.ident).skipAlias(n, c.config)
-    if result == nil:
-      fixSpelling(n, n.ident, searchInScopes)
-      errorUndeclaredIdentifier(c, n.info, n.ident.s)
-      result = errorSym(c, n)
+    result = searchInScopes(c, n.ident, amb)
+    if result == nil: result = errorUndeclaredIdentifierHint(c, n.ident, n.info)
   of nkSym:
     result = n.sym
   of nkAccQuoted:
     var ident = considerQuotedIdent(c, n)
-    result = searchInScopes(c, ident).skipAlias(n, c.config)
-    if result == nil:
-      fixSpelling(n, ident, searchInScopes)
-      errorUndeclaredIdentifier(c, n.info, ident.s)
-      result = errorSym(c, n)
+    result = searchInScopes(c, ident, amb)
+    if result == nil: result = errorUndeclaredIdentifierHint(c, ident, n.info)
   else:
     internalError(c.config, n.info, "lookUp")
-    return
-  if contains(c.ambiguousSymbols, result.id):
-    errorUseQualifier(c, n.info, result)
+    return nil
+  if amb:
+    #contains(c.ambiguousSymbols, result.id):
+    result = errorUseQualifier(c, n.info, result, amb)
   when false:
     if result.kind == skStub: loadStub(result)
 
@@ -295,163 +640,277 @@ type
   TLookupFlag* = enum
     checkAmbiguity, checkUndeclared, checkModule, checkPureEnumFields
 
+const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
+
+proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind],
+                       includePureEnum = false): seq[PSym] =
+  result = searchInScopesFilterBy(c, ident, filter)
+  if skEnumField in filter and (result.len == 0 or includePureEnum):
+    result.add allPureEnumFields(c, ident)
+
 proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
-  const allExceptModule = {low(TSymKind)..high(TSymKind)}-{skModule,skPackage}
   case n.kind
   of nkIdent, nkAccQuoted:
+    var amb = false
     var ident = considerQuotedIdent(c, n)
     if checkModule in flags:
-      result = searchInScopes(c, ident).skipAlias(n, c.config)
+      result = searchInScopes(c, ident, amb)
+      if result == nil:
+        let candidates = allPureEnumFields(c, ident)
+        if candidates.len > 0:
+          result = candidates[0]
+          amb = candidates.len > 1
+          if amb and checkAmbiguity in flags:
+            errorUseQualifier(c, n.info, candidates)
     else:
-      result = searchInScopes(c, ident, allExceptModule).skipAlias(n, c.config)
-    if result == nil and checkPureEnumFields in flags:
-      result = strTableGet(c.pureEnumFields, ident)
+      let candidates = lookUpCandidates(c, ident, allExceptModule)
+      if candidates.len > 0:
+        result = candidates[0]
+        amb = candidates.len > 1
+        if amb and checkAmbiguity in flags:
+          errorUseQualifier(c, n.info, candidates)
+      else:
+        result = nil
     if result == nil and checkUndeclared in flags:
-      fixSpelling(n, ident, searchInScopes)
-      errorUndeclaredIdentifier(c, n.info, ident.s)
-      result = errorSym(c, n)
-    elif checkAmbiguity in flags and result != nil and
-        contains(c.ambiguousSymbols, result.id):
-      errorUseQualifier(c, n.info, result)
+      result = errorUndeclaredIdentifierHint(c, ident, n.info)
+    elif checkAmbiguity in flags and result != nil and amb:
+      result = errorUseQualifier(c, n.info, result, amb)
+    c.isAmbiguous = amb
   of nkSym:
     result = n.sym
-    if checkAmbiguity in flags and contains(c.ambiguousSymbols, result.id):
-      errorUseQualifier(c, n.info, n.sym)
+  of nkOpenSym:
+    result = qualifiedLookUp(c, n[0], flags)
   of nkDotExpr:
     result = nil
-    var m = qualifiedLookUp(c, n.sons[0], (flags*{checkUndeclared})+{checkModule})
+    var m = qualifiedLookUp(c, n[0], (flags * {checkUndeclared}) + {checkModule})
     if m != nil and m.kind == skModule:
       var ident: PIdent = nil
-      if n.sons[1].kind == nkIdent:
-        ident = n.sons[1].ident
-      elif n.sons[1].kind == nkAccQuoted:
-        ident = considerQuotedIdent(c, n.sons[1])
+      if n[1].kind == nkAccQuoted:
+        ident = considerQuotedIdent(c, n[1])
+      else:
+        # this includes sym and symchoice nodes, but since we are looking in
+        # a module, it shouldn't matter what was captured
+        ident = n[1].getPIdent
       if ident != nil:
         if m == c.module:
-          result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n, c.config)
+          var ti: TIdentIter = default(TIdentIter)
+          result = initIdentIter(ti, c.topLevelScope.symbols, ident)
+          if result != nil and nextIdentIter(ti, c.topLevelScope.symbols) != nil:
+            # another symbol exists with same name
+            c.isAmbiguous = true
         else:
-          result = strTableGet(m.tab, ident).skipAlias(n, c.config)
+          var amb: bool = false
+          if c.importModuleLookup.getOrDefault(m.name.id).len > 1:
+            result = errorUseQualifier(c, n.info, m, amb)
+          else:
+            result = someSymAmb(c.graph, m, ident, amb)
+            if amb: c.isAmbiguous = true
         if result == nil and checkUndeclared in flags:
-          fixSpelling(n.sons[1], ident, searchInScopes)
-          errorUndeclaredIdentifier(c, n.sons[1].info, ident.s)
-          result = errorSym(c, n.sons[1])
-      elif n.sons[1].kind == nkSym:
-        result = n.sons[1].sym
+          result = errorUndeclaredIdentifierHint(c, ident, n[1].info)
+      elif n[1].kind == nkSym:
+        result = n[1].sym
+        if result.owner != nil and result.owner != m and checkUndeclared in flags:
+          # dotExpr in templates can end up here
+          result = errorUndeclaredIdentifierHint(c, result.name, n[1].info)
       elif checkUndeclared in flags and
-           n.sons[1].kind notin {nkOpenSymChoice, nkClosedSymChoice}:
-        localError(c.config, n.sons[1].info, "identifier expected, but got: " &
-                   renderTree(n.sons[1]))
-        result = errorSym(c, n.sons[1])
+           n[1].kind notin {nkOpenSymChoice, nkClosedSymChoice}:
+        localError(c.config, n[1].info, "identifier expected, but got: " &
+                   renderTree(n[1]))
+        result = errorSym(c, n[1])
   else:
     result = nil
   when false:
     if result != nil and result.kind == skStub: loadStub(result)
 
 proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
+  if n.kind == nkOpenSym:
+    # maybe the logic in semexprs should be mirrored here instead
+    # for now it only seems this is called for `pickSym` in `getTypeIdent` 
+    return initOverloadIter(o, c, n[0])
+  o.importIdx = -1
+  o.marked = initIntSet()
   case n.kind
   of nkIdent, nkAccQuoted:
+    result = nil
     var ident = considerQuotedIdent(c, n)
-    o.scope = c.currentScope
+    var scope = c.currentScope
     o.mode = oimNoQualifier
     while true:
-      result = initIdentIter(o.it, o.scope.symbols, ident).skipAlias(n, c.config)
+      result = initIdentIter(o.it, scope.symbols, ident)
       if result != nil:
+        o.currentScope = scope
         break
       else:
-        o.scope = o.scope.parent
-        if o.scope == nil: break
+        scope = scope.parent
+        if scope == nil:
+          for i in 0..c.imports.high:
+            result = initIdentIter(o.mit, o.marked, c.imports[i], ident, c.graph)
+            if result != nil:
+              o.currentScope = nil
+              o.importIdx = i
+              return result
+          return nil
+
   of nkSym:
     result = n.sym
     o.mode = oimDone
   of nkDotExpr:
+    result = nil
     o.mode = oimOtherModule
-    o.m = qualifiedLookUp(c, n.sons[0], {checkUndeclared, checkModule})
+    o.m = qualifiedLookUp(c, n[0], {checkUndeclared, checkModule})
     if o.m != nil and o.m.kind == skModule:
       var ident: PIdent = nil
-      if n.sons[1].kind == nkIdent:
-        ident = n.sons[1].ident
-      elif n.sons[1].kind == nkAccQuoted:
-        ident = considerQuotedIdent(c, n.sons[1], n)
+      if n[1].kind == nkIdent:
+        ident = n[1].ident
+      elif n[1].kind == nkAccQuoted:
+        ident = considerQuotedIdent(c, n[1], n)
       if ident != nil:
         if o.m == c.module:
           # a module may access its private members:
           result = initIdentIter(o.it, c.topLevelScope.symbols,
-                                 ident).skipAlias(n, c.config)
+                                 ident)
           o.mode = oimSelfModule
         else:
-          result = initIdentIter(o.it, o.m.tab, ident).skipAlias(n, c.config)
+          result = initModuleIter(o.mit, c.graph, o.m, ident)
       else:
-        noidentError(c.config, n.sons[1], n)
-        result = errorSym(c, n.sons[1])
+        noidentError(c.config, n[1], n)
+        result = errorSym(c, n[1])
   of nkClosedSymChoice, nkOpenSymChoice:
     o.mode = oimSymChoice
     if n[0].kind == nkSym:
-      result = n.sons[0].sym
+      result = n[0].sym
     else:
       o.mode = oimDone
       return nil
     o.symChoiceIndex = 1
-    o.inSymChoice = initIntSet()
-    incl(o.inSymChoice, result.id)
-  else: discard
+    o.marked = initIntSet()
+    incl(o.marked, result.id)
+  else: result = nil
   when false:
     if result != nil and result.kind == skStub: loadStub(result)
 
 proc lastOverloadScope*(o: TOverloadIter): int =
   case o.mode
-  of oimNoQualifier: result = if o.scope.isNil: -1 else: o.scope.depthLevel
+  of oimNoQualifier:
+    result = if o.importIdx >= 0: 0
+             elif o.currentScope.isNil: -1
+             else: o.currentScope.depthLevel
   of oimSelfModule:  result = 1
   of oimOtherModule: result = 0
   else: result = -1
 
+proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym =
+  result = nil
+  assert o.currentScope == nil
+  var idx = o.importIdx+1
+  o.importIdx = c.imports.len # assume the other imported modules lack this symbol too
+  while idx < c.imports.len:
+    result = initIdentIter(o.mit, o.marked, c.imports[idx], o.it.name, c.graph)
+    if result != nil:
+      # oh, we were wrong, some other module had the symbol, so remember that:
+      o.importIdx = idx
+      break
+    inc idx
+
+proc symChoiceExtension(o: var TOverloadIter; c: PContext; n: PNode): PSym =
+  result = nil
+  assert o.currentScope == nil
+  while o.importIdx < c.imports.len:
+    result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph)
+    #while result != nil and result.id in o.marked:
+    #  result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx])
+    if result != nil:
+      #assert result.id notin o.marked
+      return result
+    inc o.importIdx
+
 proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
   case o.mode
   of oimDone:
     result = nil
   of oimNoQualifier:
-    if o.scope != nil:
-      result = nextIdentIter(o.it, o.scope.symbols).skipAlias(n, c.config)
+    if o.currentScope != nil:
+      assert o.importIdx < 0
+      result = nextIdentIter(o.it, o.currentScope.symbols)
       while result == nil:
-        o.scope = o.scope.parent
-        if o.scope == nil: break
-        result = initIdentIter(o.it, o.scope.symbols, o.it.name).skipAlias(n, c.config)
-        # BUGFIX: o.it.name <-> n.ident
+        o.currentScope = o.currentScope.parent
+        if o.currentScope != nil:
+          result = initIdentIter(o.it, o.currentScope.symbols, o.it.name)
+          # BUGFIX: o.it.name <-> n.ident
+        else:
+          o.importIdx = 0
+          if c.imports.len > 0:
+            result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph)
+            if result == nil:
+              result = nextOverloadIterImports(o, c, n)
+          break
+    elif o.importIdx < c.imports.len:
+      result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph)
+      if result == nil:
+        result = nextOverloadIterImports(o, c, n)
     else:
       result = nil
   of oimSelfModule:
-    result = nextIdentIter(o.it, c.topLevelScope.symbols).skipAlias(n, c.config)
+    result = nextIdentIter(o.it, c.topLevelScope.symbols)
   of oimOtherModule:
-    result = nextIdentIter(o.it, o.m.tab).skipAlias(n, c.config)
+    result = nextModuleIter(o.mit, c.graph)
   of oimSymChoice:
-    if o.symChoiceIndex < sonsLen(n):
-      result = n.sons[o.symChoiceIndex].sym
-      incl(o.inSymChoice, result.id)
+    if o.symChoiceIndex < n.len:
+      result = n[o.symChoiceIndex].sym
+      incl(o.marked, result.id)
       inc o.symChoiceIndex
     elif n.kind == nkOpenSymChoice:
       # try 'local' symbols too for Koenig's lookup:
       o.mode = oimSymChoiceLocalLookup
-      o.scope = c.currentScope
-      result = firstIdentExcluding(o.it, o.scope.symbols,
-                                   n.sons[0].sym.name, o.inSymChoice).skipAlias(n, c.config)
+      o.currentScope = c.currentScope
+      result = firstIdentExcluding(o.it, o.currentScope.symbols,
+                                   n[0].sym.name, o.marked)
       while result == nil:
-        o.scope = o.scope.parent
-        if o.scope == nil: break
-        result = firstIdentExcluding(o.it, o.scope.symbols,
-                                     n.sons[0].sym.name, o.inSymChoice).skipAlias(n, c.config)
+        o.currentScope = o.currentScope.parent
+        if o.currentScope != nil:
+          result = firstIdentExcluding(o.it, o.currentScope.symbols,
+                                      n[0].sym.name, o.marked)
+        else:
+          o.importIdx = 0
+          result = symChoiceExtension(o, c, n)
+          break
+      if result != nil:
+        incl o.marked, result.id
+    else:
+      result = nil
   of oimSymChoiceLocalLookup:
-    result = nextIdentExcluding(o.it, o.scope.symbols, o.inSymChoice).skipAlias(n, c.config)
-    while result == nil:
-      o.scope = o.scope.parent
-      if o.scope == nil: break
-      result = firstIdentExcluding(o.it, o.scope.symbols,
-                                   n.sons[0].sym.name, o.inSymChoice).skipAlias(n, c.config)
+    if o.currentScope != nil:
+      result = nextIdentExcluding(o.it, o.currentScope.symbols, o.marked)
+      while result == nil:
+        o.currentScope = o.currentScope.parent
+        if o.currentScope != nil:
+          result = firstIdentExcluding(o.it, o.currentScope.symbols,
+                                      n[0].sym.name, o.marked)
+        else:
+          o.importIdx = 0
+          result = symChoiceExtension(o, c, n)
+          break
+      if result != nil:
+        incl o.marked, result.id
+
+    elif o.importIdx < c.imports.len:
+      result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph)
+      #assert result.id notin o.marked
+      #while result != nil and result.id in o.marked:
+      #  result = nextIdentIter(o.it, c.imports[o.importIdx])
+      if result == nil:
+        inc o.importIdx
+        result = symChoiceExtension(o, c, n)
+    else:
+      result = nil
 
   when false:
     if result != nil and result.kind == skStub: loadStub(result)
 
 proc pickSym*(c: PContext, n: PNode; kinds: set[TSymKind];
               flags: TSymFlags = {}): PSym =
-  var o: TOverloadIter
+  result = nil
+  var o: TOverloadIter = default(TOverloadIter)
   var a = initOverloadIter(o, c, n)
   while a != nil:
     if a.kind in kinds and flags <= a.flags:
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index 24a4f186e..2c9c4cb32 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -14,224 +14,271 @@ const
 
 import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs,
   lineinfos
-from trees import getMagic
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 proc newDeref*(n: PNode): PNode {.inline.} =
-  result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
-  addSon(result, n)
+  result = newNodeIT(nkHiddenDeref, n.info, n.typ.elementType)
+  result.add n
 
 proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode =
-  result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
-                     abstractInst).sons[i])
-  addSon(result, copyTree(tup))
-  var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
-  lit.intVal = i
-  addSon(result, lit)
+  if tup.kind == nkHiddenAddr:
+    result = newNodeIT(nkHiddenAddr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar, tyLent}))
+    result.add newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar, tyLent})[i])
+    result[0].add tup[0]
+    var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
+    lit.intVal = i
+    result[0].add lit
+  else:
+    result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
+                       abstractInst)[i])
+    result.add copyTree(tup)
+    var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
+    lit.intVal = i
+    result.add lit
 
 proc addVar*(father, v: PNode) =
   var vpart = newNodeI(nkIdentDefs, v.info, 3)
-  vpart.sons[0] = v
-  vpart.sons[1] = newNodeI(nkEmpty, v.info)
-  vpart.sons[2] = vpart[1]
-  addSon(father, vpart)
+  vpart[0] = v
+  vpart[1] = newNodeI(nkEmpty, v.info)
+  vpart[2] = vpart[1]
+  father.add vpart
 
-proc newAsgnStmt(le, ri: PNode): PNode =
+proc addVar*(father, v, value: PNode) =
+  var vpart = newNodeI(nkIdentDefs, v.info, 3)
+  vpart[0] = v
+  vpart[1] = newNodeI(nkEmpty, v.info)
+  vpart[2] = value
+  father.add vpart
+
+proc newAsgnStmt*(le, ri: PNode): PNode =
   result = newNodeI(nkAsgn, le.info, 2)
-  result.sons[0] = le
-  result.sons[1] = ri
+  result[0] = le
+  result[1] = ri
+
+proc newFastAsgnStmt*(le, ri: PNode): PNode =
+  result = newNodeI(nkFastAsgn, le.info, 2)
+  result[0] = le
+  result[1] = ri
 
-proc newFastAsgnStmt(le, ri: PNode): PNode =
+proc newFastMoveStmt*(g: ModuleGraph, le, ri: PNode): PNode =
   result = newNodeI(nkFastAsgn, le.info, 2)
-  result.sons[0] = le
-  result.sons[1] = ri
+  result[0] = le
+  result[1] = newNodeIT(nkCall, ri.info, ri.typ)
+  result[1].add newSymNode(getSysMagic(g, ri.info, "move", mMove))
+  result[1].add ri
 
-proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
+proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
   assert n.kind == nkVarTuple
   let value = n.lastSon
   result = newNodeI(nkStmtList, n.info)
 
-  var temp = newSym(skTemp, getIdent(g.cache, genPrefix), owner, value.info, g.config.options)
+  var tempAsNode: PNode
+  let avoidTemp = value.kind == nkSym
+  if avoidTemp:
+    tempAsNode = value
+  else:
+    var temp = newSym(skTemp, getIdent(g.cache, genPrefix), idgen,
+                  owner, value.info, g.config.options)
+    temp.typ = skipTypes(value.typ, abstractInst)
+    incl(temp.flags, sfFromGeneric)
+    tempAsNode = newSymNode(temp)
+
+  var v = newNodeI(nkVarSection, value.info)
+  if not avoidTemp:
+    v.addVar(tempAsNode, value)
+  result.add(v)
+
+  for i in 0..<n.len-2:
+    let val = newTupleAccess(g, tempAsNode, i)
+    if n[i].kind == nkSym: v.addVar(n[i], val)
+    else: result.add newAsgnStmt(n[i], val)
+
+proc evalOnce*(g: ModuleGraph; value: PNode; idgen: IdGenerator; owner: PSym): PNode =
+  ## Turns (value) into (let tmp = value; tmp) so that 'value' can be re-used
+  ## freely, multiple times. This is frequently required and such a builtin would also be
+  ## handy to have in macros.nim. The value that can be reused is 'result.lastSon'!
+  result = newNodeIT(nkStmtListExpr, value.info, value.typ)
+  var temp = newSym(skTemp, getIdent(g.cache, genPrefix), idgen,
+                    owner, value.info, g.config.options)
   temp.typ = skipTypes(value.typ, abstractInst)
   incl(temp.flags, sfFromGeneric)
 
-  var v = newNodeI(nkVarSection, value.info)
+  var v = newNodeI(nkLetSection, value.info)
   let tempAsNode = newSymNode(temp)
   v.addVar(tempAsNode)
   result.add(v)
-
   result.add newAsgnStmt(tempAsNode, value)
-  for i in 0 .. n.len-3:
-    if n.sons[i].kind == nkSym: v.addVar(n.sons[i])
-    result.add newAsgnStmt(n.sons[i], newTupleAccess(g, tempAsNode, i))
+  result.add tempAsNode
 
 proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
   result = newNodeI(nkBracketExpr, tup.info)
-  addSon(result, copyTree(tup))
+  result.add copyTree(tup)
   var lit = newNodeI(nkIntLit, tup.info)
   lit.intVal = i
-  addSon(result, lit)
+  result.add lit
 
 proc newTryFinally*(body, final: PNode): PNode =
-  result = newTree(nkTryStmt, body, newTree(nkFinally, final))
+  result = newTree(nkHiddenTryStmt, body, newTree(nkFinally, final))
 
-proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
-  let value = n.lastSon
-  result = newNodeI(nkStmtList, n.info)
-
-  var temp = newSym(skLet, getIdent(g.cache, "_"), owner, value.info, owner.options)
-  var v = newNodeI(nkLetSection, value.info)
-  let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
-
-  var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
-  vpart.sons[0] = tempAsNode
-  vpart.sons[1] = newNodeI(nkEmpty, value.info)
-  vpart.sons[2] = value
-  addSon(v, vpart)
-  result.add(v)
-
-  let lhs = n.sons[0]
-  for i in 0 .. lhs.len-1:
-    result.add newAsgnStmt(lhs.sons[i], newTupleAccessRaw(tempAsNode, i))
-
-proc lowerSwap*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
+proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
   result = newNodeI(nkStmtList, n.info)
   # note: cannot use 'skTemp' here cause we really need the copy for the VM :-(
-  var temp = newSym(skVar, getIdent(g.cache, genPrefix), owner, n.info, owner.options)
-  temp.typ = n.sons[1].typ
+  var temp = newSym(skVar, getIdent(g.cache, genPrefix), idgen, owner, n.info, owner.options)
+  temp.typ = n[1].typ
   incl(temp.flags, sfFromGeneric)
+  incl(temp.flags, sfGenSym)
 
   var v = newNodeI(nkVarSection, n.info)
   let tempAsNode = newSymNode(temp)
 
   var vpart = newNodeI(nkIdentDefs, v.info, 3)
-  vpart.sons[0] = tempAsNode
-  vpart.sons[1] = newNodeI(nkEmpty, v.info)
-  vpart.sons[2] = n[1]
-  addSon(v, vpart)
+  vpart[0] = tempAsNode
+  vpart[1] = newNodeI(nkEmpty, v.info)
+  vpart[2] = n[1]
+  v.add vpart
 
   result.add(v)
   result.add newFastAsgnStmt(n[1], n[2])
   result.add newFastAsgnStmt(n[2], tempAsNode)
 
-proc createObj*(g: ModuleGraph; owner: PSym, info: TLineInfo; final=true): PType =
-  result = newType(tyObject, owner)
+proc createObj*(g: ModuleGraph; idgen: IdGenerator; owner: PSym, info: TLineInfo; final=true): PType =
+  result = newType(tyObject, idgen, owner)
   if final:
     rawAddSon(result, nil)
     incl result.flags, tfFinal
   else:
     rawAddSon(result, getCompilerProc(g, "RootObj").typ)
   result.n = newNodeI(nkRecList, info)
-  let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info)),
-                  owner, info, owner.options)
+  let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info) & "_" & $owner.name.s),
+                  idgen, owner, info, owner.options)
   incl s.flags, sfAnon
   s.typ = result
   result.sym = s
 
+template fieldCheck {.dirty.} =
+  when false:
+    if tfCheckedForDestructor in obj.flags:
+      echo "missed field ", field.name.s
+      writeStackTrace()
+
 proc rawAddField*(obj: PType; field: PSym) =
   assert field.kind == skField
-  field.position = sonsLen(obj.n)
-  addSon(obj.n, newSymNode(field))
+  field.position = obj.n.len
+  obj.n.add newSymNode(field)
+  propagateToOwner(obj, field.typ)
+  fieldCheck()
 
 proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode =
   # returns a[].field as a node
   assert field.kind == skField
   var deref = newNodeI(nkHiddenDeref, info)
-  deref.typ = a.typ.skipTypes(abstractInst).sons[0]
-  addSon(deref, a)
+  deref.typ = a.typ.skipTypes(abstractInst)[0]
+  deref.add a
   result = newNodeI(nkDotExpr, info)
-  addSon(result, deref)
-  addSon(result, newSymNode(field))
+  result.add deref
+  result.add newSymNode(field)
   result.typ = field.typ
 
 proc rawDirectAccess*(obj, field: PSym): PNode =
   # returns a.field as a node
   assert field.kind == skField
   result = newNodeI(nkDotExpr, field.info)
-  addSon(result, newSymNode obj)
-  addSon(result, newSymNode field)
+  result.add newSymNode(obj)
+  result.add newSymNode(field)
   result.typ = field.typ
 
-proc lookupInRecord(n: PNode, id: int): PSym =
+proc lookupInRecord(n: PNode, id: ItemId): PSym =
   result = nil
   case n.kind
   of nkRecList:
-    for i in countup(0, sonsLen(n) - 1):
-      result = lookupInRecord(n.sons[i], id)
+    for i in 0..<n.len:
+      result = lookupInRecord(n[i], id)
       if result != nil: return
   of nkRecCase:
-    if n.sons[0].kind != nkSym: return
-    result = lookupInRecord(n.sons[0], id)
+    if n[0].kind != nkSym: return
+    result = lookupInRecord(n[0], id)
     if result != nil: return
-    for i in countup(1, sonsLen(n) - 1):
-      case n.sons[i].kind
+    for i in 1..<n.len:
+      case n[i].kind
       of nkOfBranch, nkElse:
-        result = lookupInRecord(lastSon(n.sons[i]), id)
+        result = lookupInRecord(lastSon(n[i]), id)
         if result != nil: return
       else: discard
   of nkSym:
-    if n.sym.id == -abs(id): result = n.sym
+    if n.sym.itemId.module == id.module and n.sym.itemId.item == -abs(id.item): result = n.sym
   else: discard
 
-proc addField*(obj: PType; s: PSym; cache: IdentCache) =
+proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym =
   # because of 'gensym' support, we have to mangle the name with its ID.
   # This is hacky but the clean solution is much more complex than it looks.
-  var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), s.owner, s.info,
-                     s.options)
-  field.id = -s.id
-  let t = skipIntLit(s.typ)
+  var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len),
+                     idgen, s.owner, s.info, s.options)
+  field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
+  let t = skipIntLit(s.typ, idgen)
   field.typ = t
-  assert t.kind != tyStmt
-  field.position = sonsLen(obj.n)
-  addSon(obj.n, newSymNode(field))
-
-proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache): PSym {.discardable.} =
-  result = lookupInRecord(obj.n, s.id)
+  if s.kind in {skLet, skVar, skField, skForVar}:
+    #field.bitsize = s.bitsize
+    field.alignment = s.alignment
+  assert t.kind != tyTyped
+  propagateToOwner(obj, t)
+  field.position = obj.n.len
+  # sfNoInit flag for skField is used in closureiterator codegen
+  field.flags = s.flags * {sfCursor, sfNoInit}
+  obj.n.add newSymNode(field)
+  fieldCheck()
+  result = field
+
+proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym {.discardable.} =
+  result = lookupInRecord(obj.n, s.itemId)
   if result == nil:
-    var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), s.owner, s.info,
-                       s.options)
-    field.id = -s.id
-    let t = skipIntLit(s.typ)
+    var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), idgen,
+                       s.owner, s.info, s.options)
+    field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
+    let t = skipIntLit(s.typ, idgen)
     field.typ = t
-    assert t.kind != tyStmt
-    field.position = sonsLen(obj.n)
-    addSon(obj.n, newSymNode(field))
+    assert t.kind != tyTyped
+    propagateToOwner(obj, t)
+    field.position = obj.n.len
+    obj.n.add newSymNode(field)
     result = field
 
-proc newDotExpr(obj, b: PSym): PNode =
+proc newDotExpr*(obj, b: PSym): PNode =
   result = newNodeI(nkDotExpr, obj.info)
-  let field = lookupInRecord(obj.typ.n, b.id)
+  let field = lookupInRecord(obj.typ.n, b.itemId)
   assert field != nil, b.name.s
-  addSon(result, newSymNode(obj))
-  addSon(result, newSymNode(field))
+  result.add newSymNode(obj)
+  result.add newSymNode(field)
   result.typ = field.typ
 
-proc indirectAccess*(a: PNode, b: int, info: TLineInfo): PNode =
+proc indirectAccess*(a: PNode, b: ItemId, info: TLineInfo): PNode =
   # returns a[].b as a node
   var deref = newNodeI(nkHiddenDeref, info)
-  deref.typ = a.typ.skipTypes(abstractInst).sons[0]
+  deref.typ = a.typ.skipTypes(abstractInst).elementType
   var t = deref.typ.skipTypes(abstractInst)
   var field: PSym
   while true:
     assert t.kind == tyObject
     field = lookupInRecord(t.n, b)
     if field != nil: break
-    t = t.sons[0]
+    t = t.baseClass
     if t == nil: break
     t = t.skipTypes(skipPtrs)
   #if field == nil:
   #  echo "FIELD ", b
   #  debug deref.typ
   assert field != nil
-  addSon(deref, a)
+  deref.add a
   result = newNodeI(nkDotExpr, info)
-  addSon(result, deref)
-  addSon(result, newSymNode(field))
+  result.add deref
+  result.add newSymNode(field)
   result.typ = field.typ
 
-proc indirectAccess(a: PNode, b: string, info: TLineInfo; cache: IdentCache): PNode =
+proc indirectAccess*(a: PNode, b: string, info: TLineInfo; cache: IdentCache): PNode =
   # returns a[].b as a node
   var deref = newNodeI(nkHiddenDeref, info)
-  deref.typ = a.typ.skipTypes(abstractInst).sons[0]
+  deref.typ = a.typ.skipTypes(abstractInst).elementType
   var t = deref.typ.skipTypes(abstractInst)
   var field: PSym
   let bb = getIdent(cache, b)
@@ -239,17 +286,17 @@ proc indirectAccess(a: PNode, b: string, info: TLineInfo; cache: IdentCache): PN
     assert t.kind == tyObject
     field = getSymFromList(t.n, bb)
     if field != nil: break
-    t = t.sons[0]
+    t = t.baseClass
     if t == nil: break
     t = t.skipTypes(skipPtrs)
   #if field == nil:
   #  echo "FIELD ", b
   #  debug deref.typ
   assert field != nil
-  addSon(deref, a)
+  deref.add a
   result = newNodeI(nkDotExpr, info)
-  addSon(result, deref)
-  addSon(result, newSymNode(field))
+  result.add deref
+  result.add newSymNode(field)
   result.typ = field.typ
 
 proc getFieldFromObj*(t: PType; v: PSym): PSym =
@@ -257,258 +304,47 @@ proc getFieldFromObj*(t: PType; v: PSym): PSym =
   var t = t
   while true:
     assert t.kind == tyObject
-    result = lookupInRecord(t.n, v.id)
+    result = lookupInRecord(t.n, v.itemId)
     if result != nil: break
-    t = t.sons[0]
+    t = t.baseClass
     if t == nil: break
     t = t.skipTypes(skipPtrs)
 
 proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode =
   # returns a[].b as a node
-  result = indirectAccess(a, b.id, info)
+  result = indirectAccess(a, b.itemId, info)
 
 proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
   result = indirectAccess(newSymNode(a), b, info)
 
-proc genAddrOf*(n: PNode): PNode =
+proc genAddrOf*(n: PNode; idgen: IdGenerator; typeKind = tyPtr): PNode =
   result = newNodeI(nkAddr, n.info, 1)
-  result.sons[0] = n
-  result.typ = newType(tyPtr, n.typ.owner)
+  result[0] = n
+  result.typ = newType(typeKind, idgen, n.typ.owner)
   result.typ.rawAddSon(n.typ)
 
-proc genDeref*(n: PNode): PNode =
-  result = newNodeIT(nkHiddenDeref, n.info,
-                     n.typ.skipTypes(abstractInst).sons[0])
+proc genDeref*(n: PNode; k = nkHiddenDeref): PNode =
+  result = newNodeIT(k, n.info,
+                     n.typ.skipTypes(abstractInst).elementType)
   result.add n
 
-proc callCodegenProc*(g: ModuleGraph; name: string, arg1: PNode;
-                      arg2, arg3, optionalArgs: PNode = nil): PNode =
-  result = newNodeI(nkCall, arg1.info)
+proc callCodegenProc*(g: ModuleGraph; name: string;
+                      info: TLineInfo = unknownLineInfo;
+                      arg1: PNode = nil, arg2: PNode = nil,
+                      arg3: PNode = nil, optionalArgs: PNode = nil): PNode =
+  result = newNodeI(nkCall, info)
   let sym = magicsys.getCompilerProc(g, name)
   if sym == nil:
-    localError(g.config, arg1.info, "system module needs: " & name)
+    localError(g.config, info, "system module needs: " & name)
   else:
     result.add newSymNode(sym)
-    result.add arg1
+    if arg1 != nil: result.add arg1
     if arg2 != nil: result.add arg2
     if arg3 != nil: result.add arg3
     if optionalArgs != nil:
-      for i in 1..optionalArgs.len-3:
+      for i in 1..<optionalArgs.len-2:
         result.add optionalArgs[i]
-    result.typ = sym.typ.sons[0]
-
-proc callProc(a: PNode): PNode =
-  result = newNodeI(nkCall, a.info)
-  result.add a
-  result.typ = a.typ.sons[0]
-
-# we have 4 cases to consider:
-# - a void proc --> nothing to do
-# - a proc returning GC'ed memory --> requires a flowVar
-# - a proc returning non GC'ed memory --> pass as hidden 'var' parameter
-# - not in a parallel environment --> requires a flowVar for memory safety
-type
-  TSpawnResult* = enum
-    srVoid, srFlowVar, srByVar
-  TFlowVarKind = enum
-    fvInvalid # invalid type T for 'FlowVar[T]'
-    fvGC      # FlowVar of a GC'ed type
-    fvBlob    # FlowVar of a blob type
-
-proc spawnResult*(t: PType; inParallel: bool): TSpawnResult =
-  if t.isEmptyType: srVoid
-  elif inParallel and not containsGarbageCollectedRef(t): srByVar
-  else: srFlowVar
-
-proc flowVarKind(t: PType): TFlowVarKind =
-  if t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC
-  elif containsGarbageCollectedRef(t): fvInvalid
-  else: fvBlob
-
-proc typeNeedsNoDeepCopy(t: PType): bool =
-  var t = t.skipTypes(abstractInst)
-  # for the tconvexhull example (and others) we're a bit lax here and pretend
-  # seqs and strings are *by value* only and 'shallow' doesn't exist!
-  if t.kind == tyString: return true
-  # note that seq[T] is fine, but 'var seq[T]' is not, so we need to skip 'var'
-  # for the stricter check and likewise we can skip 'seq' for a less
-  # strict check:
-  if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon
-  result = not containsGarbageCollectedRef(t)
-
-proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType;
-                 v: PNode; useShallowCopy=false): PSym =
-  result = newSym(skTemp, getIdent(g.cache, genPrefix), owner, varSection.info,
-                  owner.options)
-  result.typ = typ
-  incl(result.flags, sfFromGeneric)
-
-  var vpart = newNodeI(nkIdentDefs, varSection.info, 3)
-  vpart.sons[0] = newSymNode(result)
-  vpart.sons[1] = newNodeI(nkEmpty, varSection.info)
-  vpart.sons[2] = if varInit.isNil: v else: vpart[1]
-  varSection.add vpart
-  if varInit != nil:
-    if useShallowCopy and typeNeedsNoDeepCopy(typ):
-      varInit.add newFastAsgnStmt(newSymNode(result), v)
-    else:
-      let deepCopyCall = newNodeI(nkCall, varInit.info, 3)
-      deepCopyCall.sons[0] = newSymNode(getSysMagic(g, varSection.info, "deepCopy", mDeepCopy))
-      deepCopyCall.sons[1] = newSymNode(result)
-      deepCopyCall.sons[2] = v
-      varInit.add deepCopyCall
-
-discard """
-We generate roughly this:
-
-proc f_wrapper(thread, args) =
-  barrierEnter(args.barrier)  # for parallel statement
-  var a = args.a # thread transfer; deepCopy or shallowCopy or no copy
-                 # depending on whether we're in a 'parallel' statement
-  var b = args.b
-  var fv = args.fv
-
-  fv.owner = thread # optional
-  nimArgsPassingDone() # signal parent that the work is done
-  #
-  args.fv.blob = f(a, b, ...)
-  nimFlowVarSignal(args.fv)
-
-  # - or -
-  f(a, b, ...)
-  barrierLeave(args.barrier)  # for parallel statement
-
-stmtList:
-  var scratchObj
-  scratchObj.a = a
-  scratchObj.b = b
-
-  nimSpawn(f_wrapper, addr scratchObj)
-  scratchObj.fv # optional
-
-"""
-
-proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
-                       varSection, varInit, call, barrier, fv: PNode;
-                       spawnKind: TSpawnResult): PSym =
-  var body = newNodeI(nkStmtList, f.info)
-  var threadLocalBarrier: PSym
-  if barrier != nil:
-    var varSection2 = newNodeI(nkVarSection, barrier.info)
-    threadLocalBarrier = addLocalVar(g, varSection2, nil, argsParam.owner,
-                                     barrier.typ, barrier)
-    body.add varSection2
-    body.add callCodegenProc(g, "barrierEnter", threadLocalBarrier.newSymNode)
-  var threadLocalProm: PSym
-  if spawnKind == srByVar:
-    threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)
-  elif fv != nil:
-    internalAssert g.config, fv.typ.kind == tyGenericInst
-    threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)
-  body.add varSection
-  body.add varInit
-  if fv != nil and spawnKind != srByVar:
-    # generate:
-    #   fv.owner = threadParam
-    body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
-      "owner", fv.info, g.cache), threadParam.newSymNode)
-
-  body.add callCodegenProc(g, "nimArgsPassingDone", threadParam.newSymNode)
-  if spawnKind == srByVar:
-    body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call)
-  elif fv != nil:
-    let fk = fv.typ.sons[1].flowVarKind
-    if fk == fvInvalid:
-      localError(g.config, f.info, "cannot create a flowVar of type: " &
-        typeToString(fv.typ.sons[1]))
-    body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
-      if fk == fvGC: "data" else: "blob", fv.info, g.cache), call)
-    if fk == fvGC:
-      let incRefCall = newNodeI(nkCall, fv.info, 2)
-      incRefCall.sons[0] = newSymNode(getSysMagic(g, fv.info, "GCref", mGCref))
-      incRefCall.sons[1] = indirectAccess(threadLocalProm.newSymNode,
-                                          "data", fv.info, g.cache)
-      body.add incRefCall
-    if barrier == nil:
-      # by now 'fv' is shared and thus might have beeen overwritten! we need
-      # to use the thread-local view instead:
-      body.add callCodegenProc(g, "nimFlowVarSignal", threadLocalProm.newSymNode)
-  else:
-    body.add call
-  if barrier != nil:
-    body.add callCodegenProc(g, "barrierLeave", threadLocalBarrier.newSymNode)
-
-  var params = newNodeI(nkFormalParams, f.info)
-  params.add newNodeI(nkEmpty, f.info)
-  params.add threadParam.newSymNode
-  params.add argsParam.newSymNode
-
-  var t = newType(tyProc, threadParam.owner)
-  t.rawAddSon nil
-  t.rawAddSon threadParam.typ
-  t.rawAddSon argsParam.typ
-  t.n = newNodeI(nkFormalParams, f.info)
-  t.n.add newNodeI(nkEffectList, f.info)
-  t.n.add threadParam.newSymNode
-  t.n.add argsParam.newSymNode
-
-  let name = (if f.kind == nkSym: f.sym.name.s else: genPrefix) & "Wrapper"
-  result = newSym(skProc, getIdent(g.cache, name), argsParam.owner, f.info,
-                  argsParam.options)
-  let emptyNode = newNodeI(nkEmpty, f.info)
-  result.ast = newProcNode(nkProcDef, f.info, body = body,
-      params = params, name = newSymNode(result), pattern = emptyNode,
-      genericParams = emptyNode, pragmas = emptyNode,
-      exceptions = emptyNode)
-  result.typ = t
-
-proc createCastExpr(argsParam: PSym; objType: PType): PNode =
-  result = newNodeI(nkCast, argsParam.info)
-  result.add newNodeI(nkEmpty, argsParam.info)
-  result.add newSymNode(argsParam)
-  result.typ = newType(tyPtr, objType.owner)
-  result.typ.rawAddSon(objType)
-
-proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym,
-                             castExpr, call,
-                             varSection, varInit, result: PNode) =
-  let formals = n[0].typ.n
-  let tmpName = getIdent(g.cache, genPrefix)
-  for i in 1 ..< n.len:
-    # we pick n's type here, which hopefully is 'tyArray' and not
-    # 'tyOpenArray':
-    var argType = n[i].typ.skipTypes(abstractInst)
-    if i < formals.len and formals[i].typ.kind in {tyVar, tyLent}:
-      localError(g.config, n[i].info, "'spawn'ed function cannot have a 'var' parameter")
-    #elif containsTyRef(argType):
-    #  localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")
-
-    let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
-    var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)
-    field.typ = argType
-    objType.addField(field, g.cache)
-    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
-
-    let temp = addLocalVar(g, varSection, varInit, objType.owner, argType,
-                           indirectAccess(castExpr, field, n.info))
-    call.add(newSymNode(temp))
-
-proc getRoot*(n: PNode): PSym =
-  ## ``getRoot`` takes a *path* ``n``. A path is an lvalue expression
-  ## like ``obj.x[i].y``. The *root* of a path is the symbol that can be
-  ## determined as the owner; ``obj`` in the example.
-  case n.kind
-  of nkSym:
-    if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar}:
-      result = n.sym
-  of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr,
-      nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
-    result = getRoot(n.sons[0])
-  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
-    result = getRoot(n.sons[1])
-  of nkCallKinds:
-    if getMagic(n) == mSlice: result = getRoot(n.sons[1])
-  else: discard
+    result.typ = sym.typ.returnType
 
 proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode =
   result = nkIntLit.newIntNode(value)
@@ -516,202 +352,19 @@ proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode =
 
 proc genHigh*(g: ModuleGraph; n: PNode): PNode =
   if skipTypes(n.typ, abstractVar).kind == tyArray:
-    result = newIntLit(g, n.info, lastOrd(g.config, skipTypes(n.typ, abstractVar)))
+    result = newIntLit(g, n.info, toInt64(lastOrd(g.config, skipTypes(n.typ, abstractVar))))
   else:
     result = newNodeI(nkCall, n.info, 2)
     result.typ = getSysType(g, n.info, tyInt)
-    result.sons[0] = newSymNode(getSysMagic(g, n.info, "high", mHigh))
-    result.sons[1] = n
-
-proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym;
-                             castExpr, call,
-                             varSection, varInit, result: PNode) =
-  let formals = n[0].typ.n
-  let tmpName = getIdent(g.cache, genPrefix)
-  # we need to copy the foreign scratch object fields into local variables
-  # for correctness: These are called 'threadLocal' here.
-  for i in 1 ..< n.len:
-    let n = n[i]
-    let argType = skipTypes(if i < formals.len: formals[i].typ else: n.typ,
-                            abstractInst)
-    #if containsTyRef(argType):
-    #  localError(n.info, "'spawn'ed function cannot refer to 'ref'/closure")
-
-    let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
-    var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)
-
-    if argType.kind in {tyVarargs, tyOpenArray}:
-      # important special case: we always create a zero-copy slice:
-      let slice = newNodeI(nkCall, n.info, 4)
-      slice.typ = n.typ
-      slice.sons[0] = newSymNode(createMagic(g, "slice", mSlice))
-      slice.sons[0].typ = getSysType(g, n.info, tyInt) # fake type
-      var fieldB = newSym(skField, tmpName, objType.owner, n.info, g.config.options)
-      fieldB.typ = getSysType(g, n.info, tyInt)
-      objType.addField(fieldB, g.cache)
-
-      if getMagic(n) == mSlice:
-        let a = genAddrOf(n[1])
-        field.typ = a.typ
-        objType.addField(field, g.cache)
-        result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
-
-        var fieldA = newSym(skField, tmpName, objType.owner, n.info, g.config.options)
-        fieldA.typ = getSysType(g, n.info, tyInt)
-        objType.addField(fieldA, g.cache)
-        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2])
-        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3])
-
-        let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldA.typ,
-                                      indirectAccess(castExpr, fieldA, n.info),
-                                      useShallowCopy=true)
-        slice.sons[2] = threadLocal.newSymNode
-      else:
-        let a = genAddrOf(n)
-        field.typ = a.typ
-        objType.addField(field, g.cache)
-        result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
-        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(g, n))
-
-        slice.sons[2] = newIntLit(g, n.info, 0)
-      # the array itself does not need to go through a thread local variable:
-      slice.sons[1] = genDeref(indirectAccess(castExpr, field, n.info))
-
-      let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldB.typ,
-                                    indirectAccess(castExpr, fieldB, n.info),
-                                    useShallowCopy=true)
-      slice.sons[3] = threadLocal.newSymNode
-      call.add slice
-    elif (let size = computeSize(g.config, argType); size < 0 or size > 16) and
-        n.getRoot != nil:
-      # it is more efficient to pass a pointer instead:
-      let a = genAddrOf(n)
-      field.typ = a.typ
-      objType.addField(field, g.cache)
-      result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
-      let threadLocal = addLocalVar(g, varSection,nil, objType.owner, field.typ,
-                                    indirectAccess(castExpr, field, n.info),
-                                    useShallowCopy=true)
-      call.add(genDeref(threadLocal.newSymNode))
-    else:
-      # boring case
-      field.typ = argType
-      objType.addField(field, g.cache)
-      result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n)
-      let threadLocal = addLocalVar(g, varSection, varInit,
-                                    objType.owner, field.typ,
-                                    indirectAccess(castExpr, field, n.info),
-                                    useShallowCopy=true)
-      call.add(threadLocal.newSymNode)
-
-proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: PType;
-                       barrier, dest: PNode = nil): PNode =
-  # if 'barrier' != nil, then it is in a 'parallel' section and we
-  # generate quite different code
-  let n = spawnExpr[^2]
-  let spawnKind = spawnResult(retType, barrier!=nil)
-  case spawnKind
-  of srVoid:
-    internalAssert g.config, dest == nil
-    result = newNodeI(nkStmtList, n.info)
-  of srFlowVar:
-    internalAssert g.config, dest == nil
-    result = newNodeIT(nkStmtListExpr, n.info, retType)
-  of srByVar:
-    if dest == nil: localError(g.config, n.info, "'spawn' must not be discarded")
-    result = newNodeI(nkStmtList, n.info)
-
-  if n.kind notin nkCallKinds:
-    localError(g.config, n.info, "'spawn' takes a call expression")
-    return
-  if optThreadAnalysis in g.config.globalOptions:
-    if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
-      localError(g.config, n.info, "'spawn' takes a GC safe call expression")
-  var
-    threadParam = newSym(skParam, getIdent(g.cache, "thread"), owner, n.info, g.config.options)
-    argsParam = newSym(skParam, getIdent(g.cache, "args"), owner, n.info, g.config.options)
-  block:
-    let ptrType = getSysType(g, n.info, tyPointer)
-    threadParam.typ = ptrType
-    argsParam.typ = ptrType
-    argsParam.position = 1
-
-  var objType = createObj(g, owner, n.info)
-  incl(objType.flags, tfFinal)
-  let castExpr = createCastExpr(argsParam, objType)
-
-  var scratchObj = newSym(skVar, getIdent(g.cache, "scratch"), owner, n.info, g.config.options)
-  block:
-    scratchObj.typ = objType
-    incl(scratchObj.flags, sfFromGeneric)
-    var varSectionB = newNodeI(nkVarSection, n.info)
-    varSectionB.addVar(scratchObj.newSymNode)
-    result.add varSectionB
-
-  var call = newNodeIT(nkCall, n.info, n.typ)
-  var fn = n.sons[0]
-  # templates and macros are in fact valid here due to the nature of
-  # the transformation:
-  if fn.kind == nkClosure:
-    localError(g.config, n.info, "closure in spawn environment is not allowed")
-  if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro,
-                                               skFunc, skMethod, skConverter}):
-    # for indirect calls we pass the function pointer in the scratchObj
-    var argType = n[0].typ.skipTypes(abstractInst)
-    var field = newSym(skField, getIdent(g.cache, "fn"), owner, n.info, g.config.options)
-    field.typ = argType
-    objType.addField(field, g.cache)
-    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
-    fn = indirectAccess(castExpr, field, n.info)
-  elif fn.kind == nkSym and fn.sym.kind == skIterator:
-    localError(g.config, n.info, "iterator in spawn environment is not allowed")
-  elif fn.typ.callConv == ccClosure:
-    localError(g.config, n.info, "closure in spawn environment is not allowed")
-
-  call.add(fn)
-  var varSection = newNodeI(nkVarSection, n.info)
-  var varInit = newNodeI(nkStmtList, n.info)
-  if barrier.isNil:
-    setupArgsForConcurrency(g, n, objType, scratchObj, castExpr, call,
-                            varSection, varInit, result)
+    result[0] = newSymNode(getSysMagic(g, n.info, "high", mHigh))
+    result[1] = n
+
+proc genLen*(g: ModuleGraph; n: PNode): PNode =
+  if skipTypes(n.typ, abstractVar).kind == tyArray:
+    result = newIntLit(g, n.info, toInt64(lastOrd(g.config, skipTypes(n.typ, abstractVar)) + 1))
   else:
-    setupArgsForParallelism(g, n, objType, scratchObj, castExpr, call,
-                            varSection, varInit, result)
-
-  var barrierAsExpr: PNode = nil
-  if barrier != nil:
-    let typ = newType(tyPtr, owner)
-    typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ)
-    var field = newSym(skField, getIdent(g.cache, "barrier"), owner, n.info, g.config.options)
-    field.typ = typ
-    objType.addField(field, g.cache)
-    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier)
-    barrierAsExpr = indirectAccess(castExpr, field, n.info)
-
-  var fvField, fvAsExpr: PNode = nil
-  if spawnKind == srFlowVar:
-    var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)
-    field.typ = retType
-    objType.addField(field, g.cache)
-    fvField = newDotExpr(scratchObj, field)
-    fvAsExpr = indirectAccess(castExpr, field, n.info)
-    # create flowVar:
-    result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))
-    if barrier == nil:
-      result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField)
-
-  elif spawnKind == srByVar:
-    var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)
-    field.typ = newType(tyPtr, objType.owner)
-    field.typ.rawAddSon(retType)
-    objType.addField(field, g.cache)
-    fvAsExpr = indirectAccess(castExpr, field, n.info)
-    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest))
-
-  let wrapper = createWrapperProc(g, fn, threadParam, argsParam,
-                                  varSection, varInit, call,
-                                  barrierAsExpr, fvAsExpr, spawnKind)
-  result.add callCodegenProc(g, "nimSpawn" & $spawnExpr.len, wrapper.newSymNode,
-                             genAddrOf(scratchObj.newSymNode), nil, spawnExpr)
-
-  if spawnKind == srFlowVar: result.add fvField
+    result = newNodeI(nkCall, n.info, 2)
+    result.typ = getSysType(g, n.info, tyInt)
+    result[0] = newSymNode(getSysMagic(g, n.info, "len", mLengthSeq))
+    result[1] = n
+
diff --git a/compiler/macrocacheimpl.nim b/compiler/macrocacheimpl.nim
index d23040763..c869c2289 100644
--- a/compiler/macrocacheimpl.nim
+++ b/compiler/macrocacheimpl.nim
@@ -9,71 +9,36 @@
 
 ## This module implements helpers for the macro cache.
 
-import lineinfos, ast, modulegraphs, vmdef, magicsys
+import lineinfos, ast, vmdef
+
+proc append(c: PCtx; n: PNode) =
+  c.vmstateDiff.add((c.module, n))
 
 proc recordInc*(c: PCtx; info: TLineInfo; key: string; by: BiggestInt) =
-  var recorded = newNodeI(nkCommentStmt, info)
+  var recorded = newNodeI(nkReplayAction, info)
   recorded.add newStrNode("inc", info)
   recorded.add newStrNode(key, info)
   recorded.add newIntNode(nkIntLit, by)
-  c.graph.recordStmt(c.graph, c.module, recorded)
+  c.append(recorded)
 
 proc recordPut*(c: PCtx; info: TLineInfo; key: string; k: string; val: PNode) =
-  var recorded = newNodeI(nkCommentStmt, info)
+  var recorded = newNodeI(nkReplayAction, info)
   recorded.add newStrNode("put", info)
   recorded.add newStrNode(key, info)
   recorded.add newStrNode(k, info)
   recorded.add copyTree(val)
-  c.graph.recordStmt(c.graph, c.module, recorded)
+  c.append(recorded)
 
 proc recordAdd*(c: PCtx; info: TLineInfo; key: string; val: PNode) =
-  var recorded = newNodeI(nkCommentStmt, info)
+  var recorded = newNodeI(nkReplayAction, info)
   recorded.add newStrNode("add", info)
   recorded.add newStrNode(key, info)
   recorded.add copyTree(val)
-  c.graph.recordStmt(c.graph, c.module, recorded)
+  c.append(recorded)
 
 proc recordIncl*(c: PCtx; info: TLineInfo; key: string; val: PNode) =
-  var recorded = newNodeI(nkCommentStmt, info)
+  var recorded = newNodeI(nkReplayAction, info)
   recorded.add newStrNode("incl", info)
   recorded.add newStrNode(key, info)
   recorded.add copyTree(val)
-  c.graph.recordStmt(c.graph, c.module, recorded)
-
-when false:
-  proc genCall3(g: ModuleGraph; m: TMagic; s: string; a, b, c: PNode): PNode =
-    newTree(nkStaticStmt, newTree(nkCall, createMagic(g, s, m).newSymNode, a, b, c))
-
-  proc genCall2(g: ModuleGraph; m: TMagic; s: string; a, b: PNode): PNode =
-    newTree(nkStaticStmt, newTree(nkCall, createMagic(g, s, m).newSymNode, a, b))
-
-  template nodeFrom(s: string): PNode =
-    var res = newStrNode(s, info)
-    res.typ = getSysType(g, info, tyString)
-    res
-
-  template nodeFrom(i: BiggestInt): PNode =
-    var res = newIntNode(i, info)
-    res.typ = getSysType(g, info, tyInt)
-    res
-
-  template nodeFrom(n: PNode): PNode = copyTree(n)
-
-  template record(call) =
-    g.recordStmt(g, c.module, call)
-
-  proc recordInc*(c: PCtx; info: TLineInfo; key: string; by: BiggestInt) =
-    let g = c.graph
-    record genCall2(mNccInc, "inc", nodeFrom key, nodeFrom by)
-
-  proc recordPut*(c: PCtx; info: TLineInfo; key: string; k: string; val: PNode) =
-    let g = c.graph
-    record genCall3(mNctPut, "[]=", nodeFrom key, nodeFrom k, nodeFrom val)
-
-  proc recordAdd*(c: PCtx; info: TLineInfo; key: string; val: PNode) =
-    let g = c.graph
-    record genCall2(mNcsAdd, "add", nodeFrom key, nodeFrom val)
-
-  proc recordIncl*(c: PCtx; info: TLineInfo; key: string; val: PNode) =
-    let g = c.graph
-    record genCall2(mNcsIncl, "incl", nodeFrom key, nodeFrom val)
+  c.append(recorded)
diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim
index d40b9d732..1ec6b9a69 100644
--- a/compiler/magicsys.nim
+++ b/compiler/magicsys.nim
@@ -10,43 +10,37 @@
 # Built-in types and compilerprocs are registered here.
 
 import
-  ast, astalgo, hashes, msgs, platform, nversion, times, idents,
+  ast, astalgo, msgs, platform, idents,
   modulegraphs, lineinfos
 
 export createMagic
 
 proc nilOrSysInt*(g: ModuleGraph): PType = g.sysTypes[tyInt]
 
-proc registerSysType*(g: ModuleGraph; t: PType) =
-  if g.sysTypes[t.kind] == nil: g.sysTypes[t.kind] = t
-
 proc newSysType(g: ModuleGraph; kind: TTypeKind, size: int): PType =
-  result = newType(kind, g.systemModule)
+  result = newType(kind, g.idgen, g.systemModule)
   result.size = size
   result.align = size.int16
 
 proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym =
-  result = strTableGet(g.systemModule.tab, getIdent(g.cache, name))
+  result = systemModuleSym(g, getIdent(g.cache, name))
   if result == nil:
     localError(g.config, info, "system module needs: " & name)
-    result = newSym(skError, getIdent(g.cache, name), g.systemModule, g.systemModule.info, {})
-    result.typ = newType(tyError, g.systemModule)
-  if result.kind == skAlias: result = result.owner
+    result = newSym(skError, getIdent(g.cache, name), g.idgen, g.systemModule, g.systemModule.info, {})
+    result.typ = newType(tyError, g.idgen, g.systemModule)
 
 proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym =
-  var ti: TIdentIter
+  result = nil
   let id = getIdent(g.cache, name)
-  var r = initIdentIter(ti, g.systemModule.tab, id)
-  while r != nil:
+  for r in systemModuleSyms(g, id):
     if r.magic == m:
       # prefer the tyInt variant:
-      if r.typ.sons[0] != nil and r.typ.sons[0].kind == tyInt: return r
+      if r.typ.returnType != nil and r.typ.returnType.kind == tyInt: return r
       result = r
-    r = nextIdentIter(ti, g.systemModule.tab)
   if result != nil: return result
   localError(g.config, info, "system module needs: " & name)
-  result = newSym(skError, id, g.systemModule, g.systemModule.info, {})
-  result.typ = newType(tyError, g.systemModule)
+  result = newSym(skError, id, g.idgen, g.systemModule, g.systemModule.info, {})
+  result.typ = newType(tyError, g.idgen, g.systemModule)
 
 proc sysTypeFromName*(g: ModuleGraph; info: TLineInfo; name: string): PType =
   result = getSysSym(g, info, name).typ
@@ -56,6 +50,7 @@ proc getSysType*(g: ModuleGraph; info: TLineInfo; kind: TTypeKind): PType =
   result = g.sysTypes[kind]
   if result == nil:
     case kind
+    of tyVoid: result = sysTypeFromName("void")
     of tyInt: result = sysTypeFromName("int")
     of tyInt8: result = sysTypeFromName("int8")
     of tyInt16: result = sysTypeFromName("int16")
@@ -68,95 +63,58 @@ proc getSysType*(g: ModuleGraph; info: TLineInfo; kind: TTypeKind): PType =
     of tyUInt64: result = sysTypeFromName("uint64")
     of tyFloat: result = sysTypeFromName("float")
     of tyFloat32: result = sysTypeFromName("float32")
-    of tyFloat64: return sysTypeFromName("float64")
+    of tyFloat64: result = sysTypeFromName("float64")
     of tyFloat128: result = sysTypeFromName("float128")
     of tyBool: result = sysTypeFromName("bool")
     of tyChar: result = sysTypeFromName("char")
     of tyString: result = sysTypeFromName("string")
-    of tyCString: result = sysTypeFromName("cstring")
+    of tyCstring: result = sysTypeFromName("cstring")
     of tyPointer: result = sysTypeFromName("pointer")
     of tyNil: result = newSysType(g, tyNil, g.config.target.ptrSize)
     else: internalError(g.config, "request for typekind: " & $kind)
     g.sysTypes[kind] = result
   if result.kind != kind:
-    internalError(g.config, "wanted: " & $kind & " got: " & $result.kind)
+    if kind == tyFloat64 and result.kind == tyFloat: discard # because of aliasing
+    else:
+      internalError(g.config, "wanted: " & $kind & " got: " & $result.kind)
   if result == nil: internalError(g.config, "type not found: " & $kind)
 
 proc resetSysTypes*(g: ModuleGraph) =
   g.systemModule = nil
-  initStrTable(g.compilerprocs)
-  initStrTable(g.exposed)
+  g.compilerprocs = initStrTable()
+  g.exposed = initStrTable()
   for i in low(g.sysTypes)..high(g.sysTypes):
     g.sysTypes[i] = nil
 
-  for i in low(g.intTypeCache)..high(g.intTypeCache):
-    g.intTypeCache[i] = nil
-
-proc getIntLitType*(g: ModuleGraph; literal: PNode): PType =
-  # we cache some common integer literal types for performance:
-  let value = literal.intVal
-  if value >= low(g.intTypeCache) and value <= high(g.intTypeCache):
-    result = g.intTypeCache[value.int]
-    if result == nil:
-      let ti = getSysType(g, literal.info, tyInt)
-      result = copyType(ti, ti.owner, false)
-      result.n = literal
-      g.intTypeCache[value.int] = result
-  else:
-    let ti = getSysType(g, literal.info, tyInt)
-    result = copyType(ti, ti.owner, false)
-    result.n = literal
-
 proc getFloatLitType*(g: ModuleGraph; literal: PNode): PType =
   # for now we do not cache these:
   result = newSysType(g, tyFloat, size=8)
   result.n = literal
 
-proc skipIntLit*(t: PType): PType {.inline.} =
+proc skipIntLit*(t: PType; id: IdGenerator): PType {.inline.} =
   if t.n != nil and t.kind in {tyInt, tyFloat}:
-    result = copyType(t, t.owner, false)
+    result = copyType(t, id, t.owner)
     result.n = nil
   else:
     result = t
 
-proc addSonSkipIntLit*(father, son: PType) =
-  if isNil(father.sons): father.sons = @[]
-  let s = son.skipIntLit
-  add(father.sons, s)
+proc addSonSkipIntLit*(father, son: PType; id: IdGenerator) =
+  let s = son.skipIntLit(id)
+  father.add(s)
   propagateToOwner(father, s)
 
-proc setIntLitType*(g: ModuleGraph; result: PNode) =
-  let i = result.intVal
-  case g.config.target.intSize
-  of 8: result.typ = getIntLitType(g, result)
-  of 4:
-    if i >= low(int32) and i <= high(int32):
-      result.typ = getIntLitType(g, result)
-    else:
-      result.typ = getSysType(g, result.info, tyInt64)
-  of 2:
-    if i >= low(int16) and i <= high(int16):
-      result.typ = getIntLitType(g, result)
-    elif i >= low(int32) and i <= high(int32):
-      result.typ = getSysType(g, result.info, tyInt32)
-    else:
-      result.typ = getSysType(g, result.info, tyInt64)
-  of 1:
-    # 8 bit CPUs are insane ...
-    if i >= low(int8) and i <= high(int8):
-      result.typ = getIntLitType(g, result)
-    elif i >= low(int16) and i <= high(int16):
-      result.typ = getSysType(g, result.info, tyInt16)
-    elif i >= low(int32) and i <= high(int32):
-      result.typ = getSysType(g, result.info, tyInt32)
-    else:
-      result.typ = getSysType(g, result.info, tyInt64)
+proc makeVarType*(owner: PSym; baseType: PType; idgen: IdGenerator; kind = tyVar): PType =
+  if baseType.kind == kind:
+    result = baseType
   else:
-    internalError(g.config, result.info, "invalid int size")
+    result = newType(kind, idgen, owner)
+    addSonSkipIntLit(result, baseType, idgen)
 
 proc getCompilerProc*(g: ModuleGraph; name: string): PSym =
   let ident = getIdent(g.cache, name)
   result = strTableGet(g.compilerprocs, ident)
+  if result == nil:
+    result = loadCompilerProc(g, name)
 
 proc registerCompilerProc*(g: ModuleGraph; s: PSym) =
   strTableAdd(g.compilerprocs, s)
@@ -173,4 +131,39 @@ proc registerNimScriptSymbol*(g: ModuleGraph; s: PSym) =
 proc getNimScriptSymbol*(g: ModuleGraph; name: string): PSym =
   strTableGet(g.exposed, getIdent(g.cache, name))
 
-proc resetNimScriptSymbols*(g: ModuleGraph) = initStrTable(g.exposed)
+proc resetNimScriptSymbols*(g: ModuleGraph) = g.exposed = initStrTable()
+
+proc getMagicEqSymForType*(g: ModuleGraph; t: PType; info: TLineInfo): PSym =
+  case t.kind
+  of tyInt,  tyInt8, tyInt16, tyInt32, tyInt64,
+     tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64:
+    result = getSysMagic(g, info, "==", mEqI)
+  of tyEnum:
+    result = getSysMagic(g, info, "==", mEqEnum)
+  of tyBool:
+    result = getSysMagic(g, info, "==", mEqB)
+  of tyRef, tyPtr, tyPointer:
+    result = getSysMagic(g, info, "==", mEqRef)
+  of tyString:
+    result = getSysMagic(g, info, "==", mEqStr)
+  of tyChar:
+    result = getSysMagic(g, info, "==", mEqCh)
+  of tySet:
+    result = getSysMagic(g, info, "==", mEqSet)
+  of tyProc:
+    result = getSysMagic(g, info, "==", mEqProc)
+  else:
+    result = nil
+    globalError(g.config, info,
+      "can't find magic equals operator for type kind " & $t.kind)
+
+proc makePtrType*(baseType: PType; idgen: IdGenerator): PType =
+  result = newType(tyPtr, idgen, baseType.owner)
+  addSonSkipIntLit(result, baseType, idgen)
+
+proc makeAddr*(n: PNode; idgen: IdGenerator): PNode =
+  if n.kind == nkHiddenAddr:
+    result = n
+  else:
+    result = newTree(nkHiddenAddr, n)
+    result.typ = makePtrType(n.typ, idgen)
diff --git a/compiler/main.nim b/compiler/main.nim
index cd05ded62..4c52317cf 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -13,126 +13,189 @@ when not defined(nimcore):
   {.error: "nimcore MUST be defined for Nim's core tooling".}
 
 import
-  llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs,
-  os, condsyms, times,
-  wordrecg, sem, semdata, idents, passes, docgen, extccomp,
-  cgen, jsgen, json, nversion,
-  platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen,
-  docgen2, parser, modules, ccgutils, sigmatch, ropes,
-  modulegraphs, tables, rod, lineinfos
-
-from magicsys import resetSysTypes
-
-proc codegenPass(g: ModuleGraph) =
-  registerPass g, cgenPass
-
-proc semanticPasses(g: ModuleGraph) =
-  registerPass g, verbosePass
-  registerPass g, semPass
-
-proc writeDepsFile(g: ModuleGraph; project: string) =
-  let f = open(changeFileExt(project, "deps"), fmWrite)
-  for m in g.modules:
-    if m != nil:
-      f.writeLine(toFullPath(g.config, m.position.FileIndex))
+  std/[strutils, os, times, tables, with, json],
+  llstream, ast, lexer, syntaxes, options, msgs,
+  condsyms,
+  idents, extccomp,
+  cgen, nversion,
+  platform, nimconf, depends,
+  modules,
+  modulegraphs, lineinfos, pathutils, vmprofiler
+
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions]
+
+import ic / [cbackend, integrity, navigator, ic]
+
+import ../dist/checksums/src/checksums/sha1
+
+import pipelines
+
+when not defined(leanCompiler):
+  import docgen
+
+proc writeDepsFile(g: ModuleGraph) =
+  let fname = g.config.nimcacheDir / RelativeFile(g.config.projectName & ".deps")
+  let f = open(fname.string, fmWrite)
+  for m in g.ifaces:
+    if m.module != nil:
+      f.writeLine(toFullPath(g.config, m.module.position.FileIndex))
   for k in g.inclToMod.keys:
     if g.getModule(k).isNil:  # don't repeat includes which are also modules
       f.writeLine(toFullPath(g.config, k))
   f.close()
 
+proc writeCMakeDepsFile(conf: ConfigRef) =
+  ## write a list of C files for build systems like CMake.
+  ## only updated when the C file list changes.
+  let fname = getNimcacheDir(conf) / conf.outFile.changeFileExt("cdeps")
+  # generate output files list
+  var cfiles: seq[string] = @[]
+  for it in conf.toCompile: cfiles.add(it.cname.string)
+  let fileset = cfiles.toCountTable()
+  # read old cfiles list
+  var fl: File = default(File)
+  var prevset = initCountTable[string]()
+  if open(fl, fname.string, fmRead):
+    for line in fl.lines: prevset.inc(line)
+    fl.close()
+  # write cfiles out
+  if fileset != prevset:
+    fl = open(fname.string, fmWrite)
+    for line in cfiles: fl.writeLine(line)
+    fl.close()
+
 proc commandGenDepend(graph: ModuleGraph) =
-  semanticPasses(graph)
-  registerPass(graph, gendependPass)
-  compileProject(graph)
+  setPipeLinePass(graph, GenDependPass)
+  compilePipelineProject(graph)
   let project = graph.config.projectFull
-  writeDepsFile(graph, project)
+  writeDepsFile(graph)
   generateDot(graph, project)
-  execExternalProgram(graph.config, "dot -Tpng -o" & changeFileExt(project, "png") &
-      ' ' & changeFileExt(project, "dot"))
+
+  # dot in graphivz tool kit is required
+  let graphvizDotPath = findExe("dot")
+  if graphvizDotPath.len == 0:
+    quit("gendepend: Graphviz's tool dot is required," &
+    "see https://graphviz.org/download for downloading")
+
+  execExternalProgram(graph.config, "dot -Tpng -o" &
+      changeFileExt(project, "png").string &
+      ' ' & changeFileExt(project, "dot").string)
 
 proc commandCheck(graph: ModuleGraph) =
-  graph.config.errorMax = high(int)  # do not stop after first error
-  defineSymbol(graph.config.symbols, "nimcheck")
-  semanticPasses(graph)  # use an empty backend for semantic checking only
-  compileProject(graph)
-
-proc commandDoc2(graph: ModuleGraph; json: bool) =
-  graph.config.errorMax = high(int)  # do not stop after first error
-  semanticPasses(graph)
-  if json: registerPass(graph, docgen2JsonPass)
-  else: registerPass(graph, docgen2Pass)
-  compileProject(graph)
-  finishDoc2Pass(graph.config.projectName)
+  let conf = graph.config
+  conf.setErrorMaxHighMaybe
+  defineSymbol(conf.symbols, "nimcheck")
+  if optWasNimscript in conf.globalOptions:
+    defineSymbol(conf.symbols, "nimscript")
+    defineSymbol(conf.symbols, "nimconfig")
+  elif conf.backend == backendJs:
+    setTarget(conf.target, osJS, cpuJS)
+  setPipeLinePass(graph, SemPass)
+  compilePipelineProject(graph)
+
+  if conf.symbolFiles != disabledSf:
+    case conf.ideCmd
+    of ideDef: navDefinition(graph)
+    of ideUse: navUsages(graph)
+    of ideDus: navDefusages(graph)
+    else: discard
+    writeRodFiles(graph)
+
+when not defined(leanCompiler):
+  proc commandDoc2(graph: ModuleGraph; ext: string) =
+    handleDocOutputOptions graph.config
+    graph.config.setErrorMaxHighMaybe
+    case ext:
+    of TexExt:
+      setPipeLinePass(graph, Docgen2TexPass)
+    of JsonExt:
+      setPipeLinePass(graph, Docgen2JsonPass)
+    of HtmlExt:
+      setPipeLinePass(graph, Docgen2Pass)
+    else: raiseAssert $ext
+    compilePipelineProject(graph)
 
 proc commandCompileToC(graph: ModuleGraph) =
   let conf = graph.config
   extccomp.initVars(conf)
-  semanticPasses(graph)
-  registerPass(graph, cgenPass)
-
-  compileProject(graph)
-  cgenWriteModules(graph.backend, conf)
-  if conf.cmd != cmdRun:
-    let proj = changeFileExt(conf.projectFull, "")
-    extccomp.callCCompiler(conf, proj)
-    extccomp.writeJsonBuildInstructions(conf, proj)
+  if conf.symbolFiles == disabledSf:
+    if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"):
+      if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile):
+        # nothing changed
+        graph.config.notes = graph.config.mainPackageNotes
+        return
+
+  if not extccomp.ccHasSaneOverflow(conf):
+    conf.symbols.defineSymbol("nimEmulateOverflowChecks")
+
+  if conf.symbolFiles == disabledSf:
+    setPipeLinePass(graph, CgenPass)
+  else:
+    setPipeLinePass(graph, SemPass)
+  compilePipelineProject(graph)
+  if graph.config.errorCounter > 0:
+    return # issue #9933
+  if conf.symbolFiles == disabledSf:
+    cgenWriteModules(graph.backend, conf)
+  else:
+    if isDefined(conf, "nimIcIntegrityChecks"):
+      checkIntegrity(graph)
+    generateCode(graph)
+    # graph.backend can be nil under IC when nothing changed at all:
+    if graph.backend != nil:
+      cgenWriteModules(graph.backend, conf)
+  if conf.cmd != cmdTcc and graph.backend != nil:
+    extccomp.callCCompiler(conf)
+    # for now we do not support writing out a .json file with the build instructions when HCR is on
+    if not conf.hcrOn:
+      extccomp.writeJsonBuildInstructions(conf, graph.cachedFiles)
     if optGenScript in graph.config.globalOptions:
-      writeDepsFile(graph, toGeneratedFile(conf, proj, ""))
+      writeDepsFile(graph)
+    if optGenCDeps in graph.config.globalOptions:
+      writeCMakeDepsFile(conf)
 
 proc commandJsonScript(graph: ModuleGraph) =
-  let proj = changeFileExt(graph.config.projectFull, "")
-  extccomp.runJsonBuildInstructions(graph.config, proj)
+  extccomp.runJsonBuildInstructions(graph.config, graph.config.jsonBuildInstructionsFile)
 
 proc commandCompileToJS(graph: ModuleGraph) =
-  #incl(gGlobalOptions, optSafeCode)
-  setTarget(graph.config.target, osJS, cpuJS)
-  #initDefines()
-  defineSymbol(graph.config.symbols, "ecmascript") # For backward compatibility
-  defineSymbol(graph.config.symbols, "js")
-  semanticPasses(graph)
-  registerPass(graph, JSgenPass)
-  compileProject(graph)
-
-proc interactivePasses(graph: ModuleGraph) =
+  let conf = graph.config
+  when defined(leanCompiler):
+    globalError(conf, unknownLineInfo, "compiler wasn't built with JS code generator")
+  else:
+    conf.exc = excCpp
+    setTarget(conf.target, osJS, cpuJS)
+    defineSymbol(conf.symbols, "ecmascript") # For backward compatibility
+    setPipeLinePass(graph, JSgenPass)
+    compilePipelineProject(graph)
+    if optGenScript in conf.globalOptions:
+      writeDepsFile(graph)
+
+proc commandInteractive(graph: ModuleGraph) =
+  graph.config.setErrorMaxHighMaybe
   initDefines(graph.config.symbols)
   defineSymbol(graph.config.symbols, "nimscript")
+  # note: seems redundant with -d:nimHasLibFFI
   when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
-  registerPass(graph, verbosePass)
-  registerPass(graph, semPass)
-  registerPass(graph, evalPass)
-
-proc commandInteractive(graph: ModuleGraph) =
-  graph.config.errorMax = high(int)  # do not stop after first error
-  interactivePasses(graph)
-  compileSystemModule(graph)
+  setPipeLinePass(graph, InterpreterPass)
+  compilePipelineSystemModule(graph)
   if graph.config.commandArgs.len > 0:
-    discard graph.compileModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
+    discard graph.compilePipelineModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
   else:
     var m = graph.makeStdinModule()
     incl(m.flags, sfMainModule)
-    processModule(graph, m, llStreamOpenStdIn())
-
-const evalPasses = [verbosePass, semPass, evalPass]
-
-proc evalNim(graph: ModuleGraph; nodes: PNode, module: PSym) =
-  carryPasses(graph, nodes, module, evalPasses)
-
-proc commandEval(graph: ModuleGraph; exp: string) =
-  if graph.systemModule == nil:
-    interactivePasses(graph)
-    compileSystemModule(graph)
-  let echoExp = "echo \"eval\\t\", " & "repr(" & exp & ")"
-  evalNim(graph, echoExp.parseString(graph.cache, graph.config),
-    makeStdinModule(graph))
+    var idgen = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0)
+    let s = llStreamOpenStdIn(onPrompt = proc() = flushDot(graph.config))
+    discard processPipelineModule(graph, m, idgen, s)
 
 proc commandScan(cache: IdentCache, config: ConfigRef) =
-  var f = addFileExt(mainCommandArg(config), NimExt)
+  var f = addFileExt(AbsoluteFile mainCommandArg(config), NimExt)
   var stream = llStreamOpen(f, fmRead)
   if stream != nil:
     var
-      L: TLexer
-      tok: TToken
-    initToken(tok)
+      L: Lexer = default(Lexer)
+      tok: Token = default(Token)
     openLexer(L, f, stream, cache, config)
     while true:
       rawGetTok(L, tok)
@@ -140,90 +203,160 @@ proc commandScan(cache: IdentCache, config: ConfigRef) =
       if tok.tokType == tkEof: break
     closeLexer(L)
   else:
-    rawMessage(config, errGenerated, "cannot open file: " & f)
+    rawMessage(config, errGenerated, "cannot open file: " & f.string)
+
+proc commandView(graph: ModuleGraph) =
+  let f = toAbsolute(mainCommandArg(graph.config), AbsoluteDir getCurrentDir()).addFileExt(RodExt)
+  rodViewer(f, graph.config, graph.cache)
 
 const
   PrintRopeCacheStats = false
 
+proc hashMainCompilationParams*(conf: ConfigRef): string =
+  ## doesn't have to be complete; worst case is a cache hit and recompilation.
+  var state = newSha1State()
+  with state:
+    update os.getAppFilename() # nim compiler
+    update conf.commandLine # excludes `arguments`, as it should
+    update $conf.projectFull # so that running `nim r main` from 2 directories caches differently
+  result = $SecureHash(state.finalize())
+
+proc setOutFile*(conf: ConfigRef) =
+  proc libNameTmpl(conf: ConfigRef): string {.inline.} =
+    result = if conf.target.targetOS == osWindows: "$1.lib" else: "lib$1.a"
+
+  if conf.outFile.isEmpty:
+    var base = conf.projectName
+    if optUseNimcache in conf.globalOptions:
+      base.add "_" & hashMainCompilationParams(conf)
+    let targetName =
+      if conf.backend == backendJs: base & ".js"
+      elif optGenDynLib in conf.globalOptions:
+        platform.OS[conf.target.targetOS].dllFrmt % base
+      elif optGenStaticLib in conf.globalOptions: libNameTmpl(conf) % base
+      else: base & platform.OS[conf.target.targetOS].exeExt
+    conf.outFile = RelativeFile targetName
+
 proc mainCommand*(graph: ModuleGraph) =
   let conf = graph.config
   let cache = graph.cache
 
-  setupModuleCache(graph)
-  # In "nim serve" scenario, each command must reset the registered passes
-  clearPasses(graph)
   conf.lastCmdTime = epochTime()
   conf.searchPaths.add(conf.libpath)
-  setId(100)
-  case conf.command.normalize
-  of "c", "cc", "compile", "compiletoc":
-    # compile means compileToC currently
-    conf.cmd = cmdCompileToC
-    commandCompileToC(graph)
-  of "cpp", "compiletocpp":
-    conf.cmd = cmdCompileToCpp
-    defineSymbol(graph.config.symbols, "cpp")
-    commandCompileToC(graph)
-  of "objc", "compiletooc":
-    conf.cmd = cmdCompileToOC
-    defineSymbol(graph.config.symbols, "objc")
-    commandCompileToC(graph)
-  of "run":
-    conf.cmd = cmdRun
+
+  proc customizeForBackend(backend: TBackend) =
+    ## Sets backend specific options but don't compile to backend yet in
+    ## case command doesn't require it. This must be called by all commands.
+    if conf.backend == backendInvalid:
+      # only set if wasn't already set, to allow override via `nim c -b:cpp`
+      conf.backend = backend
+
+    defineSymbol(graph.config.symbols, $conf.backend)
+    case conf.backend
+    of backendC:
+      if conf.exc == excNone: conf.exc = excSetjmp
+    of backendCpp:
+      if conf.exc == excNone: conf.exc = excCpp
+    of backendObjc: discard
+    of backendJs:
+      if conf.hcrOn:
+        # XXX: At the moment, system.nim cannot be compiled in JS mode
+        # with "-d:useNimRtl". The HCR option has been processed earlier
+        # and it has added this define implictly, so we must undo that here.
+        # A better solution might be to fix system.nim
+        undefSymbol(conf.symbols, "useNimRtl")
+    of backendInvalid: raiseAssert "unreachable"
+
+  proc compileToBackend() =
+    customizeForBackend(conf.backend)
+    setOutFile(conf)
+    case conf.backend
+    of backendC: commandCompileToC(graph)
+    of backendCpp: commandCompileToC(graph)
+    of backendObjc: commandCompileToC(graph)
+    of backendJs: commandCompileToJS(graph)
+    of backendInvalid: raiseAssert "unreachable"
+
+  template docLikeCmd(body) =
+    when defined(leanCompiler):
+      conf.quitOrRaise "compiler wasn't built with documentation generator"
+    else:
+      wantMainModule(conf)
+      let docConf = if conf.cmd == cmdDoc2tex: DocTexConfig else: DocConfig
+      loadConfigs(docConf, cache, conf, graph.idgen)
+      defineSymbol(conf.symbols, "nimdoc")
+      body
+
+  ## command prepass
+  if conf.cmd == cmdCrun: conf.globalOptions.incl {optRun, optUseNimcache}
+  if conf.cmd notin cmdBackends + {cmdTcc}: customizeForBackend(backendC)
+  if conf.outDir.isEmpty:
+    # doc like commands can generate a lot of files (especially with --project)
+    # so by default should not end up in $PWD nor in $projectPath.
+    var ret = if optUseNimcache in conf.globalOptions: getNimcacheDir(conf)
+              else: conf.projectPath
+    if not ret.string.isAbsolute: # `AbsoluteDir` is not a real guarantee
+      rawMessage(conf, errCannotOpenFile, ret.string & "/")
+    if conf.cmd in cmdDocLike + {cmdRst2html, cmdRst2tex, cmdMd2html, cmdMd2tex}:
+      ret = ret / htmldocsDir
+    conf.outDir = ret
+
+  ## process all commands
+  case conf.cmd
+  of cmdBackends:
+    compileToBackend()
+    when BenchIC:
+      echoTimes graph.packed
+  of cmdTcc:
     when hasTinyCBackend:
-      extccomp.setCC("tcc")
-      commandCompileToC(graph)
+      extccomp.setCC(conf, "tcc", unknownLineInfo)
+      if conf.backend != backendC:
+        rawMessage(conf, errGenerated, "'run' requires c backend, got: '$1'" % $conf.backend)
+      compileToBackend()
     else:
       rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc")
-  of "js", "compiletojs":
-    conf.cmd = cmdCompileToJS
-    commandCompileToJS(graph)
-  of "doc0":
-    wantMainModule(conf)
-    conf.cmd = cmdDoc
-    loadConfigs(DocConfig, cache, conf)
-    commandDoc(cache, conf)
-  of "doc2", "doc":
-    conf.cmd = cmdDoc
-    loadConfigs(DocConfig, cache, conf)
-    defineSymbol(conf.symbols, "nimdoc")
-    commandDoc2(graph, false)
-  of "rst2html":
-    conf.cmd = cmdRst2html
-    loadConfigs(DocConfig, cache, conf)
-    commandRst2Html(cache, conf)
-  of "rst2tex":
-    conf.cmd = cmdRst2tex
-    loadConfigs(DocTexConfig, cache, conf)
-    commandRst2TeX(cache, conf)
-  of "jsondoc0":
-    wantMainModule(conf)
-    conf.cmd = cmdDoc
-    loadConfigs(DocConfig, cache, conf)
-    wantMainModule(conf)
-    defineSymbol(conf.symbols, "nimdoc")
-    commandJson(cache, conf)
-  of "jsondoc2", "jsondoc":
-    conf.cmd = cmdDoc
-    loadConfigs(DocConfig, cache, conf)
-    wantMainModule(conf)
-    defineSymbol(conf.symbols, "nimdoc")
-    commandDoc2(graph, true)
-  of "ctags":
-    wantMainModule(conf)
-    conf.cmd = cmdDoc
-    loadConfigs(DocConfig, cache, conf)
-    defineSymbol(conf.symbols, "nimdoc")
-    commandTags(cache, conf)
-  of "buildindex":
-    conf.cmd = cmdDoc
-    loadConfigs(DocConfig, cache, conf)
-    commandBuildIndex(cache, conf)
-  of "gendepend":
-    conf.cmd = cmdGenDepend
-    commandGenDepend(graph)
-  of "dump":
-    conf.cmd = cmdDump
+  of cmdDoc0: docLikeCmd commandDoc(cache, conf)
+  of cmdDoc:
+    docLikeCmd():
+      conf.setNoteDefaults(warnRstRedefinitionOfLabel, false) # issue #13218
+        # because currently generates lots of false positives due to conflation
+        # of labels links in doc comments, e.g. for random.rand:
+        #  ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer
+        #  ## * `rand proc<#rand,Rand,range[]>`_ that returns a float
+      commandDoc2(graph, HtmlExt)
+      if optGenIndex in conf.globalOptions and optWholeProject in conf.globalOptions:
+        commandBuildIndex(conf, $conf.outDir)
+  of cmdRst2html, cmdMd2html:
+    # XXX: why are warnings disabled by default for rst2html and rst2tex?
+    for warn in rstWarnings:
+      conf.setNoteDefaults(warn, true)
+    conf.setNoteDefaults(warnRstRedefinitionOfLabel, false) # similar to issue #13218
+    when defined(leanCompiler):
+      conf.quitOrRaise "compiler wasn't built with documentation generator"
+    else:
+      loadConfigs(DocConfig, cache, conf, graph.idgen)
+      commandRst2Html(cache, conf, preferMarkdown = (conf.cmd == cmdMd2html))
+  of cmdRst2tex, cmdMd2tex, cmdDoc2tex:
+    for warn in rstWarnings:
+      conf.setNoteDefaults(warn, true)
+    when defined(leanCompiler):
+      conf.quitOrRaise "compiler wasn't built with documentation generator"
+    else:
+      if conf.cmd in {cmdRst2tex, cmdMd2tex}:
+        loadConfigs(DocTexConfig, cache, conf, graph.idgen)
+        commandRst2TeX(cache, conf, preferMarkdown = (conf.cmd == cmdMd2tex))
+      else:
+        docLikeCmd commandDoc2(graph, TexExt)
+  of cmdJsondoc0: docLikeCmd commandJson(cache, conf)
+  of cmdJsondoc:
+    docLikeCmd():
+      commandDoc2(graph, JsonExt)
+      if optGenIndex in conf.globalOptions and optWholeProject in conf.globalOptions:
+        commandBuildIndexJson(conf, $conf.outDir)
+  of cmdCtags: docLikeCmd commandTags(cache, conf)
+  of cmdBuildindex: docLikeCmd commandBuildIndex(conf, $conf.projectFull, conf.outFile)
+  of cmdGendepend: commandGenDepend(graph)
+  of cmdDump:
     if getConfigVar(conf, "dump.format") == "json":
       wantMainModule(conf)
 
@@ -231,60 +364,72 @@ proc mainCommand*(graph: ModuleGraph) =
       for s in definedSymbolNames(conf.symbols): definedSymbols.elems.add(%s)
 
       var libpaths = newJArray()
-      for dir in conf.searchPaths: libpaths.elems.add(%dir)
+      var lazyPaths = newJArray()
+      for dir in conf.searchPaths: libpaths.elems.add(%dir.string)
+      for dir in conf.lazyPaths: lazyPaths.elems.add(%dir.string)
+
+      var hints = newJObject() # consider factoring with `listHints`
+      for a in hintMin..hintMax:
+        hints[$a] = %(a in conf.notes)
+      var warnings = newJObject()
+      for a in warnMin..warnMax:
+        warnings[$a] = %(a in conf.notes)
 
-      var dumpdata = % [
+      var dumpdata = %[
         (key: "version", val: %VersionAsString),
-        (key: "project_path", val: %conf.projectFull),
+        (key: "nimExe", val: %(getAppFilename())),
+        (key: "prefixdir", val: %conf.getPrefixDir().string),
+        (key: "libpath", val: %conf.libpath.string),
+        (key: "project_path", val: %conf.projectFull.string),
         (key: "defined_symbols", val: definedSymbols),
-        (key: "lib_paths", val: libpaths)
+        (key: "lib_paths", val: %libpaths),
+        (key: "lazyPaths", val: %lazyPaths),
+        (key: "outdir", val: %conf.outDir.string),
+        (key: "out", val: %conf.outFile.string),
+        (key: "nimcache", val: %getNimcacheDir(conf).string),
+        (key: "hints", val: hints),
+        (key: "warnings", val: warnings),
       ]
 
-      msgWriteln(conf, $dumpdata, {msgStdout, msgSkipHook})
+      msgWriteln(conf, $dumpdata, {msgStdout, msgSkipHook, msgNoUnitSep})
+        # `msgNoUnitSep` to avoid generating invalid json, refs bug #17853
     else:
       msgWriteln(conf, "-- list of currently defined symbols --",
-                 {msgStdout, msgSkipHook})
-      for s in definedSymbolNames(conf.symbols): msgWriteln(conf, s, {msgStdout, msgSkipHook})
+                 {msgStdout, msgSkipHook, msgNoUnitSep})
+      for s in definedSymbolNames(conf.symbols): msgWriteln(conf, s, {msgStdout, msgSkipHook, msgNoUnitSep})
       msgWriteln(conf, "-- end of list --", {msgStdout, msgSkipHook})
 
-      for it in conf.searchPaths: msgWriteln(conf, it)
-  of "check":
-    conf.cmd = cmdCheck
+      for it in conf.searchPaths: msgWriteln(conf, it.string)
+  of cmdCheck:
+    commandCheck(graph)
+  of cmdM:
+    graph.config.symbolFiles = v2Sf
+    setUseIc(graph.config.symbolFiles != disabledSf)
     commandCheck(graph)
-  of "parse":
-    conf.cmd = cmdParse
+  of cmdParse:
     wantMainModule(conf)
     discard parseFile(conf.projectMainIdx, cache, conf)
-  of "scan":
-    conf.cmd = cmdScan
+  of cmdRod:
     wantMainModule(conf)
-    commandScan(cache, conf)
-    msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!")
-  of "secret":
-    conf.cmd = cmdInteractive
-    commandInteractive(graph)
-  of "e":
-    commandEval(graph, mainCommandArg(conf))
-  of "nop", "help":
-    # prevent the "success" message:
-    conf.cmd = cmdDump
-  of "jsonscript":
-    conf.cmd = cmdJsonScript
+    commandView(graph)
+    #msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!")
+  of cmdInteractive: commandInteractive(graph)
+  of cmdNimscript:
+    if conf.projectIsCmd or conf.projectIsStdin: discard
+    elif not fileExists(conf.projectFull):
+      rawMessage(conf, errGenerated, "NimScript file does not exist: " & conf.projectFull.string)
+    # main NimScript logic handled in `loadConfigs`.
+  of cmdNop: discard
+  of cmdJsonscript:
+    setOutFile(graph.config)
     commandJsonScript(graph)
-  else:
+  of cmdUnknown, cmdNone, cmdIdeTools:
     rawMessage(conf, errGenerated, "invalid command: " & conf.command)
 
-  if conf.errorCounter == 0 and
-     conf.cmd notin {cmdInterpret, cmdRun, cmdDump}:
-    when declared(system.getMaxMem):
-      let usedMem = formatSize(getMaxMem()) & " peakmem"
-    else:
-      let usedMem = formatSize(getTotalMem())
-    rawMessage(conf, hintSuccessX, [$conf.linesCompiled,
-               formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3),
-               usedMem,
-               if isDefined(conf, "release"): "Release Build"
-               else: "Debug Build"])
+  if conf.errorCounter == 0 and conf.cmd notin {cmdTcc, cmdDump, cmdNop}:
+    if optProfileVM in conf.globalOptions:
+      echo conf.dump(conf.vmProfileData)
+    genSuccessX(conf)
 
   when PrintRopeCacheStats:
     echo "rope cache stats: "
@@ -293,5 +438,3 @@ proc mainCommand*(graph: ModuleGraph) =
     echo "  int tries: ", gCacheIntTries
     echo "  efficiency: ", formatFloat(1-(gCacheMisses.float/gCacheTries.float),
                                        ffDecimal, 3)
-
-  resetAttributes(conf)
diff --git a/compiler/mangleutils.nim b/compiler/mangleutils.nim
new file mode 100644
index 000000000..2ae954518
--- /dev/null
+++ b/compiler/mangleutils.nim
@@ -0,0 +1,59 @@
+import std/strutils
+import ast, modulegraphs
+
+proc mangle*(name: string): string =
+  result = newStringOfCap(name.len)
+  var start = 0
+  if name[0] in Digits:
+    result.add("X" & name[0])
+    start = 1
+  var requiresUnderscore = false
+  template special(x) =
+    result.add x
+    requiresUnderscore = true
+  for i in start..<name.len:
+    let c = name[i]
+    case c
+    of 'a'..'z', '0'..'9', 'A'..'Z':
+      result.add(c)
+    of '_':
+      # we generate names like 'foo_9' for scope disambiguations and so
+      # disallow this here:
+      if i > 0 and i < name.len-1 and name[i+1] in Digits:
+        discard
+      else:
+        result.add(c)
+    of '$': special "dollar"
+    of '%': special "percent"
+    of '&': special "amp"
+    of '^': special "roof"
+    of '!': special "emark"
+    of '?': special "qmark"
+    of '*': special "star"
+    of '+': special "plus"
+    of '-': special "minus"
+    of '/': special "slash"
+    of '\\': special "backslash"
+    of '=': special "eq"
+    of '<': special "lt"
+    of '>': special "gt"
+    of '~': special "tilde"
+    of ':': special "colon"
+    of '.': special "dot"
+    of '@': special "at"
+    of '|': special "bar"
+    else:
+      result.add("X" & toHex(ord(c), 2))
+      requiresUnderscore = true
+  if requiresUnderscore:
+    result.add "_"
+
+proc mangleParamExt*(s: PSym): string =
+  result = "_p"
+  result.addInt s.position
+
+proc mangleProcNameExt*(graph: ModuleGraph, s: PSym): string =
+  result = "__"
+  result.add graph.ifaces[s.itemId.module].uniqueName
+  result.add "_u"
+  result.addInt s.itemId.item # s.disamb #
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index 334cd1ae6..77762d23a 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -9,32 +9,85 @@
 
 ## This module implements the module graph data structure. The module graph
 ## represents a complete Nim project. Single modules can either be kept in RAM
-## or stored in a Sqlite database.
-##
-## The caching of modules is critical for 'nimsuggest' and is tricky to get
-## right. If module E is being edited, we need autocompletion (and type
-## checking) for E but we don't want to recompile depending
-## modules right away for faster turnaround times. Instead we mark the module's
-## dependencies as 'dirty'. Let D be a dependency of E. If D is dirty, we
-## need to recompile it and all of its dependencies that are marked as 'dirty'.
-## 'nimsuggest sug' actually is invoked for the file being edited so we know
-## its content changed and there is no need to compute any checksums.
-## Instead of a recursive algorithm, we use an iterative algorithm:
-##
-## - If a module gets recompiled, its dependencies need to be updated.
-## - Its dependent module stays the same.
-##
-
-import ast, intsets, tables, options, lineinfos, hashes, idents,
-  incremental, btrees
+## or stored in a rod-file.
+
+import std/[intsets, tables, hashes, strtabs, algorithm, os, strutils, parseutils]
+import ../dist/checksums/src/checksums/md5
+import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages, suggestsymdb
+import ic / [packed_ast, ic]
+
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 type
-  ModuleGraph* = ref object
-    modules*: seq[PSym]  ## indexed by int32 fileIdx
+  SigHash* = distinct MD5Digest
+
+  LazySym* = object
+    id*: FullId
+    sym*: PSym
+
+  Iface* = object       ## data we don't want to store directly in the
+                        ## ast.PSym type for s.kind == skModule
+    module*: PSym       ## module this "Iface" belongs to
+    converters*: seq[LazySym]
+    patterns*: seq[LazySym]
+    pureEnums*: seq[LazySym]
+    interf: TStrTable
+    interfHidden: TStrTable
+    uniqueName*: Rope
+
+  Operators* = object
+    opNot*, opContains*, opLe*, opLt*, opAnd*, opOr*, opIsNil*, opEq*: PSym
+    opAdd*, opSub*, opMul*, opDiv*, opLen*: PSym
+
+  FullId* = object
+    module*: int
+    packed*: PackedItemId
+
+  LazyType* = object
+    id*: FullId
+    typ*: PType
+
+  LazyInstantiation* = object
+    module*: int
+    sym*: FullId
+    concreteTypes*: seq[FullId]
+    inst*: PInstantiation
+
+  PipelinePass* = enum
+    NonePass
+    SemPass
+    JSgenPass
+    CgenPass
+    EvalPass
+    InterpreterPass
+    GenDependPass
+    Docgen2TexPass
+    Docgen2JsonPass
+    Docgen2Pass
+
+  ModuleGraph* {.acyclic.} = ref object
+    ifaces*: seq[Iface]  ## indexed by int32 fileIdx
+    packed*: PackedModuleGraph
+    encoders*: seq[PackedEncoder]
+
+    typeInstCache*: Table[ItemId, seq[LazyType]] # A symbol's ItemId.
+    procInstCache*: Table[ItemId, seq[LazyInstantiation]] # A symbol's ItemId.
+    attachedOps*: array[TTypeAttachedOp, Table[ItemId, LazySym]] # Type ID, destructors, etc.
+    methodsPerGenericType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods
+    memberProcsPerType*: Table[ItemId, seq[PSym]] # Type ID, attached member procs (only c++, virtual,member and ctor so far).
+    initializersPerType*: Table[ItemId, PNode] # Type ID, AST call to the default ctor (c++ only)
+    enumToStringProcs*: Table[ItemId, LazySym]
+    emittedTypeInfo*: Table[string, FileIndex]
+
+    startupPackedConfig*: PackedConfig
     packageSyms*: TStrTable
     deps*: IntSet # the dependency graph or potentially its transitive closure.
+    importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies
     suggestMode*: bool # whether we are in nimsuggest mode or not.
     invalidTransitiveClosure: bool
+    interactive*: bool
     inclToMod*: Table[FileIndex, FileIndex] # mapping of include file to the
                                             # first module that included it
     importStack*: seq[FileIndex]  # The current import stack. Used for detecting recursive
@@ -44,98 +97,571 @@ type
     cache*: IdentCache
     vm*: RootRef # unfortunately the 'vm' state is shared project-wise, this will
                  # be clarified in later compiler implementations.
+    repl*: RootRef # REPL state is shared project-wise.
     doStopCompile*: proc(): bool {.closure.}
     usageSym*: PSym # for nimsuggest
     owners*: seq[PSym]
-    methods*: seq[tuple[methods: TSymSeq, dispatcher: PSym]] # needs serialization!
+    suggestSymbols*: SuggestSymbolDatabase
+    suggestErrors*: Table[FileIndex, seq[Suggest]]
+    methods*: seq[tuple[methods: seq[PSym], dispatcher: PSym]] # needs serialization!
+    bucketTable*: CountTable[ItemId]
+    objectTree*: Table[ItemId, seq[tuple[depth: int, value: PType]]]
+    methodsPerType*: Table[ItemId, seq[LazySym]]
+    dispatchers*: seq[LazySym]
+
     systemModule*: PSym
     sysTypes*: array[TTypeKind, PType]
     compilerprocs*: TStrTable
     exposed*: TStrTable
-    intTypeCache*: array[-5..64, PType]
-    opContains*, opNot*: PSym
+    packageTypes*: TStrTable
     emptyNode*: PNode
-    incr*: IncrementalCtx
+    canonTypes*: Table[SigHash, PType]
+    symBodyHashes*: Table[int, SigHash] # symId to digest mapping
     importModuleCallback*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex): PSym {.nimcall.}
     includeFileCallback*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex): PNode {.nimcall.}
-    recordStmt*: proc (graph: ModuleGraph; m: PSym; n: PNode) {.nimcall.}
-    cacheSeqs*: Table[string, PNode] # state that is shared to suppor the 'macrocache' API
-    cacheCounters*: Table[string, BiggestInt]
-    cacheTables*: Table[string, BTree[string, PNode]]
+    cacheSeqs*: Table[string, PNode] # state that is shared to support the 'macrocache' API; IC: implemented
+    cacheCounters*: Table[string, BiggestInt] # IC: implemented
+    cacheTables*: Table[string, BTree[string, PNode]] # IC: implemented
+    passes*: seq[TPass]
+    pipelinePass*: PipelinePass
+    onDefinition*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
+    onDefinitionResolveForward*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
+    onUsage*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
+    globalDestructors*: seq[PNode]
+    strongSemCheck*: proc (graph: ModuleGraph; owner: PSym; body: PNode) {.nimcall.}
+    compatibleProps*: proc (graph: ModuleGraph; formal, actual: PType): bool {.nimcall.}
+    idgen*: IdGenerator
+    operators*: Operators
+
+    cachedFiles*: StringTableRef
+
+  TPassContext* = object of RootObj # the pass's context
+    idgen*: IdGenerator
+  PPassContext* = ref TPassContext
+
+  TPassOpen* = proc (graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nimcall.}
+  TPassClose* = proc (graph: ModuleGraph; p: PPassContext, n: PNode): PNode {.nimcall.}
+  TPassProcess* = proc (p: PPassContext, topLevelStmt: PNode): PNode {.nimcall.}
+
+  TPass* = tuple[open: TPassOpen,
+                 process: TPassProcess,
+                 close: TPassClose,
+                 isFrontend: bool]
+
+proc resetForBackend*(g: ModuleGraph) =
+  g.compilerprocs = initStrTable()
+  g.typeInstCache.clear()
+  g.procInstCache.clear()
+  for a in mitems(g.attachedOps):
+    a.clear()
+  g.methodsPerGenericType.clear()
+  g.enumToStringProcs.clear()
+  g.dispatchers.setLen(0)
+  g.methodsPerType.clear()
+
+const
+  cb64 = [
+    "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",
+    "0", "1", "2", "3", "4", "5", "6", "7", "8", "9a",
+    "9b", "9c"]
+
+proc toBase64a(s: cstring, len: int): string =
+  ## encodes `s` into base64 representation.
+  result = newStringOfCap(((len + 2) div 3) * 4)
+  result.add "__"
+  var i = 0
+  while i < len - 2:
+    let a = ord(s[i])
+    let b = ord(s[i+1])
+    let c = ord(s[i+2])
+    result.add cb64[a shr 2]
+    result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
+    result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
+    result.add cb64[c and 0x3F]
+    inc(i, 3)
+  if i < len-1:
+    let a = ord(s[i])
+    let b = ord(s[i+1])
+    result.add cb64[a shr 2]
+    result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
+    result.add cb64[((b and 0x0F) shl 2)]
+  elif i < len:
+    let a = ord(s[i])
+    result.add cb64[a shr 2]
+    result.add cb64[(a and 3) shl 4]
+
+template interfSelect(iface: Iface, importHidden: bool): TStrTable =
+  var ret = iface.interf.addr # without intermediate ptr, it creates a copy and compiler becomes 15x slower!
+  if importHidden: ret = iface.interfHidden.addr
+  ret[]
+
+template semtab(g: ModuleGraph, m: PSym): TStrTable =
+  g.ifaces[m.position].interf
+
+template semtabAll*(g: ModuleGraph, m: PSym): TStrTable =
+  g.ifaces[m.position].interfHidden
+
+proc initStrTables*(g: ModuleGraph, m: PSym) =
+  semtab(g, m) = initStrTable()
+  semtabAll(g, m) = initStrTable()
+
+proc strTableAdds*(g: ModuleGraph, m: PSym, s: PSym) =
+  strTableAdd(semtab(g, m), s)
+  strTableAdd(semtabAll(g, m), s)
+
+proc isCachedModule(g: ModuleGraph; module: int): bool {.inline.} =
+  result = module < g.packed.len and g.packed[module].status == loaded
+
+proc isCachedModule*(g: ModuleGraph; m: PSym): bool {.inline.} =
+  isCachedModule(g, m.position)
+
+proc simulateCachedModule(g: ModuleGraph; moduleSym: PSym; m: PackedModule) =
+  when false:
+    echo "simulating ", moduleSym.name.s, " ", moduleSym.position
+  simulateLoadedModule(g.packed, g.config, g.cache, moduleSym, m)
+
+proc initEncoder*(g: ModuleGraph; module: PSym) =
+  let id = module.position
+  if id >= g.encoders.len:
+    setLen g.encoders, id+1
+  ic.initEncoder(g.encoders[id],
+    g.packed[id].fromDisk, module, g.config, g.startupPackedConfig)
+
+type
+  ModuleIter* = object
+    fromRod: bool
+    modIndex: int
+    ti: TIdentIter
+    rodIt: RodIter
+    importHidden: bool
+
+proc initModuleIter*(mi: var ModuleIter; g: ModuleGraph; m: PSym; name: PIdent): PSym =
+  assert m.kind == skModule
+  mi.modIndex = m.position
+  mi.fromRod = isCachedModule(g, mi.modIndex)
+  mi.importHidden = optImportHidden in m.options
+  if mi.fromRod:
+    result = initRodIter(mi.rodIt, g.config, g.cache, g.packed, FileIndex mi.modIndex, name, mi.importHidden)
+  else:
+    result = initIdentIter(mi.ti, g.ifaces[mi.modIndex].interfSelect(mi.importHidden), name)
+
+proc nextModuleIter*(mi: var ModuleIter; g: ModuleGraph): PSym =
+  if mi.fromRod:
+    result = nextRodIter(mi.rodIt, g.packed)
+  else:
+    result = nextIdentIter(mi.ti, g.ifaces[mi.modIndex].interfSelect(mi.importHidden))
+
+iterator allSyms*(g: ModuleGraph; m: PSym): PSym =
+  let importHidden = optImportHidden in m.options
+  if isCachedModule(g, m):
+    var rodIt: RodIter = default(RodIter)
+    var r = initRodIterAllSyms(rodIt, g.config, g.cache, g.packed, FileIndex m.position, importHidden)
+    while r != nil:
+      yield r
+      r = nextRodIter(rodIt, g.packed)
+  else:
+    for s in g.ifaces[m.position].interfSelect(importHidden).data:
+      if s != nil:
+        yield s
+
+proc someSym*(g: ModuleGraph; m: PSym; name: PIdent): PSym =
+  let importHidden = optImportHidden in m.options
+  if isCachedModule(g, m):
+    result = interfaceSymbol(g.config, g.cache, g.packed, FileIndex(m.position), name, importHidden)
+  else:
+    result = strTableGet(g.ifaces[m.position].interfSelect(importHidden), name)
+
+proc someSymAmb*(g: ModuleGraph; m: PSym; name: PIdent; amb: var bool): PSym =
+  let importHidden = optImportHidden in m.options
+  if isCachedModule(g, m):
+    result = nil
+    for s in interfaceSymbols(g.config, g.cache, g.packed, FileIndex(m.position), name, importHidden):
+      if result == nil:
+        # set result to the first symbol
+        result = s
+      else:
+        # another symbol found
+        amb = true
+        break
+  else:
+    var ti: TIdentIter = default(TIdentIter)
+    result = initIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden), name)
+    if result != nil and nextIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden)) != nil:
+      # another symbol exists with same name
+      amb = true
+
+proc systemModuleSym*(g: ModuleGraph; name: PIdent): PSym =
+  result = someSym(g, g.systemModule, name)
+
+iterator systemModuleSyms*(g: ModuleGraph; name: PIdent): PSym =
+  var mi: ModuleIter = default(ModuleIter)
+  var r = initModuleIter(mi, g, g.systemModule, name)
+  while r != nil:
+    yield r
+    r = nextModuleIter(mi, g)
+
+proc resolveType(g: ModuleGraph; t: var LazyType): PType =
+  result = t.typ
+  if result == nil and isCachedModule(g, t.id.module):
+    result = loadTypeFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed)
+    t.typ = result
+  assert result != nil
+
+proc resolveSym(g: ModuleGraph; t: var LazySym): PSym =
+  result = t.sym
+  if result == nil and isCachedModule(g, t.id.module):
+    result = loadSymFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed)
+    t.sym = result
+  assert result != nil
+
+proc resolveInst(g: ModuleGraph; t: var LazyInstantiation): PInstantiation =
+  result = t.inst
+  if result == nil and isCachedModule(g, t.module):
+    result = PInstantiation(sym: loadSymFromId(g.config, g.cache, g.packed, t.sym.module, t.sym.packed))
+    result.concreteTypes = newSeq[PType](t.concreteTypes.len)
+    for i in 0..high(result.concreteTypes):
+      result.concreteTypes[i] = loadTypeFromId(g.config, g.cache, g.packed,
+          t.concreteTypes[i].module, t.concreteTypes[i].packed)
+    t.inst = result
+  assert result != nil
+
+proc resolveAttachedOp*(g: ModuleGraph; t: var LazySym): PSym =
+  result = t.sym
+  if result == nil:
+    result = loadSymFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed)
+    t.sym = result
+  assert result != nil
+
+iterator typeInstCacheItems*(g: ModuleGraph; s: PSym): PType =
+  if g.typeInstCache.contains(s.itemId):
+    let x = addr(g.typeInstCache[s.itemId])
+    for t in mitems(x[]):
+      yield resolveType(g, t)
+
+iterator procInstCacheItems*(g: ModuleGraph; s: PSym): PInstantiation =
+  if g.procInstCache.contains(s.itemId):
+    let x = addr(g.procInstCache[s.itemId])
+    for t in mitems(x[]):
+      yield resolveInst(g, t)
+
+
+proc getAttachedOp*(g: ModuleGraph; t: PType; op: TTypeAttachedOp): PSym =
+  ## returns the requested attached operation for type `t`. Can return nil
+  ## if no such operation exists.
+  if g.attachedOps[op].contains(t.itemId):
+    result = resolveAttachedOp(g, g.attachedOps[op][t.itemId])
+  else:
+    result = nil
+
+proc setAttachedOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
+  ## we also need to record this to the packed module.
+  g.attachedOps[op][t.itemId] = LazySym(sym: value)
+
+proc setAttachedOpPartial*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
+  ## we also need to record this to the packed module.
+  g.attachedOps[op][t.itemId] = LazySym(sym: value)
+
+proc completePartialOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
+  if g.config.symbolFiles != disabledSf:
+    assert module < g.encoders.len
+    assert isActive(g.encoders[module])
+    toPackedGeneratedProcDef(value, g.encoders[module], g.packed[module].fromDisk)
+    #storeAttachedProcDef(t, op, value, g.encoders[module], g.packed[module].fromDisk)
+
+iterator getDispatchers*(g: ModuleGraph): PSym =
+  for i in g.dispatchers.mitems:
+    yield resolveSym(g, i)
+
+proc addDispatchers*(g: ModuleGraph, value: PSym) =
+  # TODO: add it for packed modules
+  g.dispatchers.add LazySym(sym: value)
+
+iterator resolveLazySymSeq(g: ModuleGraph, list: var seq[LazySym]): PSym =
+  for it in list.mitems:
+    yield resolveSym(g, it)
+
+proc setMethodsPerType*(g: ModuleGraph; id: ItemId, methods: seq[LazySym]) =
+  # TODO: add it for packed modules
+  g.methodsPerType[id] = methods
+
+iterator getMethodsPerType*(g: ModuleGraph; t: PType): PSym =
+  if g.methodsPerType.contains(t.itemId):
+    for it in mitems g.methodsPerType[t.itemId]:
+      yield resolveSym(g, it)
+
+proc getToStringProc*(g: ModuleGraph; t: PType): PSym =
+  result = resolveSym(g, g.enumToStringProcs[t.itemId])
+  assert result != nil
+
+proc setToStringProc*(g: ModuleGraph; t: PType; value: PSym) =
+  g.enumToStringProcs[t.itemId] = LazySym(sym: value)
+
+iterator methodsForGeneric*(g: ModuleGraph; t: PType): (int, PSym) =
+  if g.methodsPerGenericType.contains(t.itemId):
+    for it in mitems g.methodsPerGenericType[t.itemId]:
+      yield (it[0], resolveSym(g, it[1]))
+
+proc addMethodToGeneric*(g: ModuleGraph; module: int; t: PType; col: int; m: PSym) =
+  g.methodsPerGenericType.mgetOrPut(t.itemId, @[]).add (col, LazySym(sym: m))
+
+proc hasDisabledAsgn*(g: ModuleGraph; t: PType): bool =
+  let op = getAttachedOp(g, t, attachedAsgn)
+  result = op != nil and sfError in op.flags
+
+proc copyTypeProps*(g: ModuleGraph; module: int; dest, src: PType) =
+  for k in low(TTypeAttachedOp)..high(TTypeAttachedOp):
+    let op = getAttachedOp(g, src, k)
+    if op != nil:
+      setAttachedOp(g, module, dest, k, op)
+
+proc loadCompilerProc*(g: ModuleGraph; name: string): PSym =
+  result = nil
+  if g.config.symbolFiles == disabledSf: return nil
+
+  # slow, linear search, but the results are cached:
+  for module in 0..<len(g.packed):
+    #if isCachedModule(g, module):
+    let x = searchForCompilerproc(g.packed[module], name)
+    if x >= 0:
+      result = loadSymFromId(g.config, g.cache, g.packed, module, toPackedItemId(x))
+      if result != nil:
+        strTableAdd(g.compilerprocs, result)
+      return result
+
+proc loadPackedSym*(g: ModuleGraph; s: var LazySym) =
+  if s.sym == nil:
+    s.sym = loadSymFromId(g.config, g.cache, g.packed, s.id.module, s.id.packed)
+
+proc `$`*(u: SigHash): string =
+  toBase64a(cast[cstring](unsafeAddr u), sizeof(u))
+
+proc `==`*(a, b: SigHash): bool =
+  result = equalMem(unsafeAddr a, unsafeAddr b, sizeof(a))
+
+proc hash*(u: SigHash): Hash =
+  result = 0
+  for x in 0..3:
+    result = (result shl 8) or u.MD5Digest[x].int
 
 proc hash*(x: FileIndex): Hash {.borrow.}
 
-{.this: g.}
+template getPContext(): untyped =
+  when c is PContext: c
+  else: c.c
+
+when defined(nimsuggest):
+  template onUse*(info: TLineInfo; s: PSym) = discard
+  template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
+else:
+  template onUse*(info: TLineInfo; s: PSym) = discard
+  template onDef*(info: TLineInfo; s: PSym) = discard
+  template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
 
 proc stopCompile*(g: ModuleGraph): bool {.inline.} =
-  result = doStopCompile != nil and doStopCompile()
+  result = g.doStopCompile != nil and g.doStopCompile()
 
-proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym =
-  result = newSym(skProc, getIdent(g.cache, name), nil, unknownLineInfo(), {})
+proc createMagic*(g: ModuleGraph; idgen: IdGenerator; name: string, m: TMagic): PSym =
+  result = newSym(skProc, getIdent(g.cache, name), idgen, nil, unknownLineInfo, {})
   result.magic = m
+  result.flags = {sfNeverRaises}
 
-proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
-  result = ModuleGraph()
-  initStrTable(result.packageSyms)
+proc createMagic(g: ModuleGraph; name: string, m: TMagic): PSym =
+  result = createMagic(g, g.idgen, name, m)
+
+proc uniqueModuleName*(conf: ConfigRef; m: PSym): string =
+  ## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'}
+  ## so that it is useful as a C identifier snippet.
+  let fid = FileIndex(m.position)
+  let path = AbsoluteFile toFullPath(conf, fid)
+  var isLib = false
+  var rel = ""
+  if path.string.startsWith(conf.libpath.string):
+    isLib = true
+    rel = relativeTo(path, conf.libpath).string
+  else:
+    rel = relativeTo(path, conf.projectPath).string
+
+  if not isLib and not belongsToProjectPackage(conf, m):
+    # special handlings for nimble packages
+    when DirSep == '\\':
+      let rel2 = replace(rel, '\\', '/')
+    else:
+      let rel2 = rel
+    const pkgs2 = "pkgs2/"
+    var start = rel2.find(pkgs2)
+    if start >= 0:
+      start += pkgs2.len
+      start += skipUntil(rel2, {'/'}, start)
+      if start+1 < rel2.len:
+        rel = "pkg/" & rel2[start+1..<rel.len] # strips paths
+
+  let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len
+  result = newStringOfCap(trunc)
+  for i in 0..<trunc:
+    let c = rel[i]
+    case c
+    of 'a'..'z', '0'..'9':
+      result.add c
+    of {os.DirSep, os.AltSep}:
+      result.add 'Z' # because it looks a bit like '/'
+    of '.':
+      result.add 'O' # a circle
+    else:
+      # We mangle upper letters too so that there cannot
+      # be clashes with our special meanings of 'Z' and 'O'
+      result.addInt ord(c)
+
+proc registerModule*(g: ModuleGraph; m: PSym) =
+  assert m != nil
+  assert m.kind == skModule
+
+  if m.position >= g.ifaces.len:
+    setLen(g.ifaces, m.position + 1)
+
+  if m.position >= g.packed.len:
+    setLen(g.packed.pm, m.position + 1)
+
+  g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[],
+                               uniqueName: rope(uniqueModuleName(g.config, m)))
+  initStrTables(g, m)
+
+proc registerModuleById*(g: ModuleGraph; m: FileIndex) =
+  registerModule(g, g.packed[int m].module)
+
+proc initOperators*(g: ModuleGraph): Operators =
+  # These are safe for IC.
+  # Public because it's used by DrNim.
+  result = Operators(
+    opLe: createMagic(g, "<=", mLeI),
+    opLt: createMagic(g, "<", mLtI),
+    opAnd: createMagic(g, "and", mAnd),
+    opOr: createMagic(g, "or", mOr),
+    opIsNil: createMagic(g, "isnil", mIsNil),
+    opEq: createMagic(g, "==", mEqI),
+    opAdd: createMagic(g, "+", mAddI),
+    opSub: createMagic(g, "-", mSubI),
+    opMul: createMagic(g, "*", mMulI),
+    opDiv: createMagic(g, "div", mDivI),
+    opLen: createMagic(g, "len", mLengthSeq),
+    opNot: createMagic(g, "not", mNot),
+    opContains: createMagic(g, "contains", mInSet)
+  )
+
+proc initModuleGraphFields(result: ModuleGraph) =
+  # A module ID of -1 means that the symbol is not attached to a module at all,
+  # but to the module graph:
+  result.idgen = IdGenerator(module: -1'i32, symId: 0'i32, typeId: 0'i32)
+  result.packageSyms = initStrTable()
   result.deps = initIntSet()
-  result.modules = @[]
+  result.importDeps = initTable[FileIndex, seq[FileIndex]]()
+  result.ifaces = @[]
   result.importStack = @[]
   result.inclToMod = initTable[FileIndex, FileIndex]()
-  result.config = config
-  result.cache = cache
   result.owners = @[]
+  result.suggestSymbols = initTable[FileIndex, SuggestFileSymbolDatabase]()
+  result.suggestErrors = initTable[FileIndex, seq[Suggest]]()
   result.methods = @[]
-  initStrTable(result.compilerprocs)
-  initStrTable(result.exposed)
-  result.opNot = createMagic(result, "not", mNot)
-  result.opContains = createMagic(result, "contains", mInSet)
+  result.compilerprocs = initStrTable()
+  result.exposed = initStrTable()
+  result.packageTypes = initStrTable()
   result.emptyNode = newNode(nkEmpty)
-  init(result.incr)
-  result.recordStmt = proc (graph: ModuleGraph; m: PSym; n: PNode) {.nimcall.} =
-    discard
   result.cacheSeqs = initTable[string, PNode]()
   result.cacheCounters = initTable[string, BiggestInt]()
   result.cacheTables = initTable[string, BTree[string, PNode]]()
+  result.canonTypes = initTable[SigHash, PType]()
+  result.symBodyHashes = initTable[int, SigHash]()
+  result.operators = initOperators(result)
+  result.emittedTypeInfo = initTable[string, FileIndex]()
+  result.cachedFiles = newStringTable()
+
+proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
+  result = ModuleGraph()
+  result.config = config
+  result.cache = cache
+  initModuleGraphFields(result)
 
 proc resetAllModules*(g: ModuleGraph) =
-  initStrTable(packageSyms)
-  deps = initIntSet()
-  modules = @[]
-  importStack = @[]
-  inclToMod = initTable[FileIndex, FileIndex]()
-  usageSym = nil
-  owners = @[]
-  methods = @[]
-  initStrTable(compilerprocs)
-  initStrTable(exposed)
+  g.packageSyms = initStrTable()
+  g.deps = initIntSet()
+  g.ifaces = @[]
+  g.importStack = @[]
+  g.inclToMod = initTable[FileIndex, FileIndex]()
+  g.usageSym = nil
+  g.owners = @[]
+  g.methods = @[]
+  g.compilerprocs = initStrTable()
+  g.exposed = initStrTable()
+  initModuleGraphFields(g)
 
 proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
-  if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len:
-    result = modules[fileIdx.int32]
+  result = nil
+  if fileIdx.int32 >= 0:
+    if isCachedModule(g, fileIdx.int32):
+      result = g.packed[fileIdx.int32].module
+    elif fileIdx.int32 < g.ifaces.len:
+      result = g.ifaces[fileIdx.int32].module
+
+proc moduleOpenForCodegen*(g: ModuleGraph; m: FileIndex): bool {.inline.} =
+  if g.config.symbolFiles == disabledSf:
+    result = true
+  else:
+    result = g.packed[m.int32].status notin {undefined, stored, loaded}
+
+proc rememberEmittedTypeInfo*(g: ModuleGraph; m: FileIndex; ti: string) =
+  #assert(not isCachedModule(g, m.int32))
+  if g.config.symbolFiles != disabledSf:
+    #assert g.encoders[m.int32].isActive
+    assert g.packed[m.int32].status != stored
+    g.packed[m.int32].fromDisk.emittedTypeInfo.add ti
+    #echo "added typeinfo ", m.int32, " ", ti, " suspicious ", not g.encoders[m.int32].isActive
+
+proc rememberFlag*(g: ModuleGraph; m: PSym; flag: ModuleBackendFlag) =
+  if g.config.symbolFiles != disabledSf:
+    #assert g.encoders[m.int32].isActive
+    assert g.packed[m.position].status != stored
+    g.packed[m.position].fromDisk.backendFlags.incl flag
+
+proc closeRodFile*(g: ModuleGraph; m: PSym) =
+  if g.config.symbolFiles in {readOnlySf, v2Sf}:
+    # For stress testing we seek to reload the symbols from memory. This
+    # way much of the logic is tested but the test is reproducible as it does
+    # not depend on the hard disk contents!
+    let mint = m.position
+    saveRodFile(toRodFile(g.config, AbsoluteFile toFullPath(g.config, FileIndex(mint))),
+                g.encoders[mint], g.packed[mint].fromDisk)
+    g.packed[mint].status = stored
+
+  elif g.config.symbolFiles == stressTest:
+    # debug code, but maybe a good idea for production? Could reduce the compiler's
+    # memory consumption considerably at the cost of more loads from disk.
+    let mint = m.position
+    simulateCachedModule(g, m, g.packed[mint].fromDisk)
+    g.packed[mint].status = loaded
 
 proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b
 
 proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) =
   assert m.position == m.info.fileIndex.int32
-  addModuleDep(g.incr, g.config, m.info.fileIndex, dep, isIncludeFile = false)
-  if suggestMode:
-    deps.incl m.position.dependsOn(dep.int)
-    # we compute the transitive closure later when quering the graph lazily.
+  if g.suggestMode:
+    g.deps.incl m.position.dependsOn(dep.int)
+    # we compute the transitive closure later when querying the graph lazily.
     # this improves efficiency quite a lot:
     #invalidTransitiveClosure = true
 
 proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) =
-  addModuleDep(g.incr, g.config, module, includeFile, isIncludeFile = true)
-  discard hasKeyOrPut(inclToMod, includeFile, module)
+  discard hasKeyOrPut(g.inclToMod, includeFile, module)
 
 proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex =
   ## returns 'fileIdx' if the file belonging to this index is
   ## directly used as a module or else the module that first
   ## references this include file.
-  if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len and modules[fileIdx.int32] != nil:
+  if fileIdx.int32 >= 0 and fileIdx.int32 < g.ifaces.len and g.ifaces[fileIdx.int32].module != nil:
     result = fileIdx
   else:
-    result = inclToMod.getOrDefault(fileIdx)
+    result = g.inclToMod.getOrDefault(fileIdx)
 
 proc transitiveClosure(g: var IntSet; n: int) =
   # warshall's algorithm
@@ -147,22 +673,107 @@ proc transitiveClosure(g: var IntSet; n: int) =
             g.incl i.dependsOn(j)
 
 proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) =
-  let m = getModule fileIdx
-  if m != nil: incl m.flags, sfDirty
+  let m = g.getModule fileIdx
+  if m != nil:
+    g.suggestSymbols.del(fileIdx)
+    g.suggestErrors.del(fileIdx)
+    incl m.flags, sfDirty
+
+proc unmarkAllDirty*(g: ModuleGraph) =
+  for i in 0i32..<g.ifaces.len.int32:
+    let m = g.ifaces[i].module
+    if m != nil:
+      m.flags.excl sfDirty
+
+proc isDirty*(g: ModuleGraph; m: PSym): bool =
+  result = g.suggestMode and sfDirty in m.flags
 
 proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
   # we need to mark its dependent modules D as dirty right away because after
   # nimsuggest is done with this module, the module's dirty flag will be
   # cleared but D still needs to be remembered as 'dirty'.
-  if invalidTransitiveClosure:
-    invalidTransitiveClosure = false
-    transitiveClosure(deps, modules.len)
+  if g.invalidTransitiveClosure:
+    g.invalidTransitiveClosure = false
+    transitiveClosure(g.deps, g.ifaces.len)
 
   # every module that *depends* on this file is also dirty:
-  for i in 0i32..<modules.len.int32:
-    let m = modules[i]
-    if m != nil and deps.contains(i.dependsOn(fileIdx.int)):
-      incl m.flags, sfDirty
+  for i in 0i32..<g.ifaces.len.int32:
+    if g.deps.contains(i.dependsOn(fileIdx.int)):
+      g.markDirty(FileIndex(i))
 
-proc isDirty*(g: ModuleGraph; m: PSym): bool =
-  result = suggestMode and sfDirty in m.flags
+proc needsCompilation*(g: ModuleGraph): bool =
+  # every module that *depends* on this file is also dirty:
+  result = false
+  for i in 0i32..<g.ifaces.len.int32:
+    let m = g.ifaces[i].module
+    if m != nil:
+      if sfDirty in m.flags:
+        return true
+
+proc needsCompilation*(g: ModuleGraph, fileIdx: FileIndex): bool =
+  result = false
+  let module = g.getModule(fileIdx)
+  if module != nil and g.isDirty(module):
+    return true
+
+  for i in 0i32..<g.ifaces.len.int32:
+    let m = g.ifaces[i].module
+    if m != nil and g.isDirty(m) and g.deps.contains(fileIdx.int32.dependsOn(i)):
+      return true
+
+proc getBody*(g: ModuleGraph; s: PSym): PNode {.inline.} =
+  result = s.ast[bodyPos]
+  if result == nil and g.config.symbolFiles in {readOnlySf, v2Sf, stressTest}:
+    result = loadProcBody(g.config, g.cache, g.packed, s)
+    s.ast[bodyPos] = result
+  assert result != nil
+
+proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex;
+                        cachedModules: var seq[FileIndex]): PSym =
+  ## Returns 'nil' if the module needs to be recompiled.
+  if g.config.symbolFiles in {readOnlySf, v2Sf, stressTest}:
+    result = moduleFromRodFile(g.packed, g.config, g.cache, fileIdx, cachedModules)
+  else:
+    result = nil
+
+proc configComplete*(g: ModuleGraph) =
+  rememberStartupConfig(g.startupPackedConfig, g.config)
+
+proc onProcessing*(graph: ModuleGraph, fileIdx: FileIndex, moduleStatus: string, fromModule: PSym, ) =
+  let conf = graph.config
+  let isNimscript = conf.isDefined("nimscript")
+  if (not isNimscript) or hintProcessing in conf.cmdlineNotes:
+    let path = toFilenameOption(conf, fileIdx, conf.filenameOption)
+    let indent = ">".repeat(graph.importStack.len)
+    let fromModule2 = if fromModule != nil: $fromModule.name.s else: "(toplevel)"
+    let mode = if isNimscript: "(nims) " else: ""
+    rawMessage(conf, hintProcessing, "$#$# $#: $#: $#" % [mode, indent, fromModule2, moduleStatus, path])
+
+proc getPackage*(graph: ModuleGraph; fileIdx: FileIndex): PSym =
+  ## Returns a package symbol for yet to be defined module for fileIdx.
+  ## The package symbol is added to the graph if it doesn't exist.
+  let pkgSym = getPackage(graph.config, graph.cache, fileIdx)
+  # check if the package is already in the graph
+  result = graph.packageSyms.strTableGet(pkgSym.name)
+  if result == nil:
+     # the package isn't in the graph, so create and add it
+    result = pkgSym
+    graph.packageSyms.strTableAdd(pkgSym)
+
+func belongsToStdlib*(graph: ModuleGraph, sym: PSym): bool =
+  ## Check if symbol belongs to the 'stdlib' package.
+  sym.getPackageSymbol.getPackageId == graph.systemModule.getPackageId
+
+proc fileSymbols*(graph: ModuleGraph, fileIdx: FileIndex): SuggestFileSymbolDatabase =
+  result = graph.suggestSymbols.getOrDefault(fileIdx, newSuggestFileSymbolDatabase(fileIdx, optIdeExceptionInlayHints in graph.config.globalOptions))
+  doAssert(result.fileIndex == fileIdx)
+
+iterator suggestSymbolsIter*(g: ModuleGraph): SymInfoPair =
+  for xs in g.suggestSymbols.values:
+    for i in xs.lineInfo.low..xs.lineInfo.high:
+      yield xs.getSymInfoPair(i)
+
+iterator suggestErrorsIter*(g: ModuleGraph): Suggest =
+  for xs in g.suggestErrors.values:
+    for x in xs:
+      yield x
diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim
index 87c7f3541..c9e6060e5 100644
--- a/compiler/modulepaths.nim
+++ b/compiler/modulepaths.nim
@@ -7,111 +7,10 @@
 #    distribution, for details about the copyright.
 #
 
-import ast, renderer, strutils, msgs, options, idents, os, lineinfos
+import ast, renderer, msgs, options, idents, lineinfos,
+  pathutils
 
-import nimblecmd
-
-when false:
-  const
-    considerParentDirs = not defined(noParentProjects)
-    considerNimbleDirs = not defined(noNimbleDirs)
-
-  proc findInNimbleDir(pkg, subdir, dir: string): string =
-    var best = ""
-    var bestv = ""
-    for k, p in os.walkDir(dir, relative=true):
-      if k == pcDir and p.len > pkg.len+1 and
-          p[pkg.len] == '-' and p.startsWith(pkg):
-        let (_, a) = getPathVersion(p)
-        if bestv.len == 0 or bestv < a:
-          bestv = a
-          best = dir / p
-
-    if best.len > 0:
-      var f: File
-      if open(f, best / changeFileExt(pkg, ".nimble-link")):
-        # the second line contains what we're interested in, see:
-        # https://github.com/nim-lang/nimble#nimble-link
-        var override = ""
-        discard readLine(f, override)
-        discard readLine(f, override)
-        close(f)
-        if not override.isAbsolute():
-          best = best / override
-        else:
-          best = override
-    let f = if subdir.len == 0: pkg else: subdir
-    let res = addFileExt(best / f, "nim")
-    if best.len > 0 and fileExists(res):
-      result = res
-
-const stdlibDirs = [
-  "pure", "core", "arch",
-  "pure/collections",
-  "pure/concurrency", "impure",
-  "wrappers", "wrappers/linenoise",
-  "windows", "posix", "js"]
-
-when false:
-  proc resolveDollar(project, source, pkg, subdir: string; info: TLineInfo): string =
-    template attempt(a) =
-      let x = addFileExt(a, "nim")
-      if fileExists(x): return x
-
-    case pkg
-    of "stdlib":
-      if subdir.len == 0:
-        return options.libpath
-      else:
-        for candidate in stdlibDirs:
-          attempt(options.libpath / candidate / subdir)
-    of "root":
-      let root = project.splitFile.dir
-      if subdir.len == 0:
-        return root
-      else:
-        attempt(root / subdir)
-    else:
-      when considerParentDirs:
-        var p = parentDir(source.splitFile.dir)
-        # support 'import $karax':
-        let f = if subdir.len == 0: pkg else: subdir
-
-        while p.len > 0:
-          let dir = p / pkg
-          if dirExists(dir):
-            attempt(dir / f)
-            # 2nd attempt: try to use 'karax/karax'
-            attempt(dir / pkg / f)
-            # 3rd attempt: try to use 'karax/src/karax'
-            attempt(dir / "src" / f)
-            attempt(dir / "src" / pkg / f)
-          p = parentDir(p)
-
-      when considerNimbleDirs:
-        if not options.gNoNimblePath:
-          var nimbleDir = getEnv("NIMBLE_DIR")
-          if nimbleDir.len == 0: nimbleDir = getHomeDir() / ".nimble"
-          result = findInNimbleDir(pkg, subdir, nimbleDir / "pkgs")
-          if result.len > 0: return result
-          when not defined(windows):
-            result = findInNimbleDir(pkg, subdir, "/opt/nimble/pkgs")
-            if result.len > 0: return result
-
-  proc scriptableImport(pkg, sub: string; info: TLineInfo): string =
-    result = resolveDollar(gProjectFull, info.toFullPath(), pkg, sub, info)
-    if result.isNil: result = ""
-
-  proc lookupPackage(pkg, subdir: PNode): string =
-    let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: ""
-    case pkg.kind
-    of nkStrLit, nkRStrLit, nkTripleStrLit:
-      result = scriptableImport(pkg.strVal, sub, pkg.info)
-    of nkIdent:
-      result = scriptableImport(pkg.ident.s, sub, pkg.info)
-    else:
-      localError(pkg.info, "package name must be an identifier or string literal")
-      result = ""
+import std/[strutils, os]
 
 proc getModuleName*(conf: ConfigRef; n: PNode): string =
   # This returns a short relative module name without the nim extension
@@ -131,13 +30,6 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
   of nkInfix:
     let n0 = n[0]
     let n1 = n[1]
-    if n0.kind == nkIdent and n0.ident.s == "as":
-      # XXX hack ahead:
-      n.kind = nkImportAs
-      n.sons[0] = n.sons[1]
-      n.sons[1] = n.sons[2]
-      n.sons.setLen(2)
-      return getModuleName(conf, n.sons[0])
     when false:
       if n1.kind == nkPrefix and n1[0].kind == nkIdent and n1[0].ident.s == "$":
         if n0.kind == nkIdent and n0.ident.s == "/":
@@ -146,30 +38,31 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
           localError(n.info, "only '/' supported with $package notation")
           result = ""
     else:
-      let modname = getModuleName(conf, n[2])
-      if $n1 == "std":
-        template attempt(a) =
-          let x = addFileExt(a, "nim")
-          if fileExists(x): return x
-        for candidate in stdlibDirs:
-          attempt(conf.libpath / candidate / modname)
-
-      # hacky way to implement 'x / y /../ z':
-      result = getModuleName(conf, n1)
-      result.add renderTree(n0, {renderNoComments})
-      result.add modname
+      if n0.kind in nkIdentKinds:
+        let ident = n0.getPIdent
+        if ident != nil and ident.s[0] == '/':
+          let modname = getModuleName(conf, n[2])
+          # hacky way to implement 'x / y /../ z':
+          result = getModuleName(conf, n1)
+          result.add renderTree(n0, {renderNoComments}).replace(" ")
+          result.add modname
+        else:
+          result = ""
+      else:
+        result = ""
   of nkPrefix:
     when false:
-      if n.sons[0].kind == nkIdent and n.sons[0].ident.s == "$":
+      if n[0].kind == nkIdent and n[0].ident.s == "$":
         result = lookupPackage(n[1], nil)
       else:
         discard
     # 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 is deprecated")
     result = renderTree(n, {renderNoComments}).replace(".", "/")
   of nkImportAs:
-    result = getModuleName(conf, n.sons[0])
+    result = getModuleName(conf, n[0])
   else:
     localError(conf, n.info, "invalid module name: '$1'" % n.renderTree)
     result = ""
@@ -178,10 +71,25 @@ proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex =
   # This returns the full canonical path for a given module import
   let modulename = getModuleName(conf, n)
   let fullPath = findModule(conf, modulename, toFullPath(conf, n.info))
-  if fullPath.len == 0:
+  if fullPath.isEmpty:
     if doLocalError:
       let m = if modulename.len > 0: modulename else: $n
       localError(conf, n.info, "cannot open file: " & m)
-    result = InvalidFileIDX
+    result = InvalidFileIdx
   else:
     result = fileInfoIdx(conf, fullPath)
+
+proc mangleModuleName*(conf: ConfigRef; path: AbsoluteFile): string =
+  ## Mangle a relative module path to avoid path and symbol collisions.
+  ##
+  ## Used by backends that need to generate intermediary files from Nim modules.
+  ## This is needed because the compiler uses a flat cache file hierarchy.
+  ##
+  ## Example:
+  ## `foo-#head/../bar` becomes `@foo-@hhead@s..@sbar`
+  "@m" & relativeTo(path, conf.projectPath).string.multiReplace(
+    {$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"})
+
+proc demangleModuleName*(path: string): string =
+  ## Demangle a relative module path.
+  result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@c": ":"})
diff --git a/compiler/modules.nim b/compiler/modules.nim
index b3a1e90d6..6e2af8bcc 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -10,121 +10,54 @@
 ## Implements the module handling, including the caching of modules.
 
 import
-  ast, astalgo, magicsys, std / sha1, msgs, cgendata, sigmatch, options,
-  idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs, rod,
-  lineinfos
+  ast, magicsys, msgs, options,
+  idents, lexer, syntaxes, modulegraphs,
+  lineinfos, pathutils
+
+import ../dist/checksums/src/checksums/sha1
+import std/strtabs
 
 proc resetSystemArtifacts*(g: ModuleGraph) =
   magicsys.resetSysTypes(g)
 
-proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
-  # We cannot call ``newSym`` here, because we have to circumvent the ID
-  # mechanism, which we do in order to assign each module a persistent ID.
-  new(result)
-  result.id = -1             # for better error checking
-  result.kind = skModule
-  let filename = toFullPath(graph.config, fileIdx)
-  result.name = getIdent(graph.cache, splitFile(filename).name)
-  if not isNimIdentifier(result.name.s):
-    rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s)
-
-  result.info = newLineInfo(fileIdx, 1, 1)
-  let
-    pck = getPackageName(graph.config, filename)
-    pck2 = if pck.len > 0: pck else: "unknown"
-    pack = getIdent(graph.cache, pck2)
-  var packSym = graph.packageSyms.strTableGet(pack)
-  if packSym == nil:
-    packSym = newSym(skPackage, getIdent(graph.cache, pck2), nil, result.info)
-    initStrTable(packSym.tab)
-    graph.packageSyms.strTableAdd(packSym)
+template getModuleIdent(graph: ModuleGraph, filename: AbsoluteFile): PIdent =
+  getIdent(graph.cache, splitFile(filename).name)
 
+proc partialInitModule*(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) =
+  let packSym = getPackage(graph, fileIdx)
   result.owner = packSym
   result.position = int fileIdx
 
-  if int(fileIdx) >= graph.modules.len:
-    setLen(graph.modules, int(fileIdx) + 1)
-  #growCache graph.modules, int fileIdx
-  graph.modules[result.position] = result
-
-  incl(result.flags, sfUsed)
-  initStrTable(result.tab)
-  strTableAdd(result.tab, result) # a module knows itself
-  let existing = strTableGet(packSym.tab, result.name)
-  if existing != nil and existing.info.fileIndex != result.info.fileIndex:
-    localError(graph.config, result.info,
-      "module names need to be unique per Nimble package; module clashes with " &
-        toFullPath(graph.config, existing.info.fileIndex))
-  # strTableIncl() for error corrections:
-  discard strTableIncl(packSym.tab, result)
-
-proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): PSym =
-  result = graph.getModule(fileIdx)
-  if result == nil:
-    result = newModule(graph, fileIdx)
-    result.flags = result.flags + flags
-    if sfMainModule in result.flags:
-      graph.config.mainPackageId = result.owner.id
-
-    result.id = getModuleId(graph, fileIdx, toFullPath(graph.config, fileIdx))
-    discard processModule(graph, result,
-      if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil)
-  elif graph.isDirty(result):
-    result.flags.excl sfDirty
-    # reset module fields:
-    initStrTable(result.tab)
-    result.ast = nil
-    discard processModule(graph, result,
-      if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil)
-    graph.markClientsDirty(fileIdx)
-
-proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym {.procvar.} =
-  # this is called by the semantic checking phase
-  assert graph.config != nil
-  result = compileModule(graph, fileIdx, {})
-  graph.addDep(s, fileIdx)
-  #if sfSystemModule in result.flags:
-  #  localError(result.info, errAttemptToRedefine, result.name.s)
-  # restore the notes for outer module:
-  graph.config.notes =
-    if s.owner.id == graph.config.mainPackageId: graph.config.mainPackageNotes
-    else: graph.config.foreignPackageNotes
+proc newModule*(graph: ModuleGraph; fileIdx: FileIndex): PSym =
+  let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
+  # We cannot call ``newSym`` here, because we have to circumvent the ID
+  # mechanism, which we do in order to assign each module a persistent ID.
+  result = PSym(kind: skModule, itemId: ItemId(module: int32(fileIdx), item: 0'i32),
+                name: getModuleIdent(graph, filename),
+                info: newLineInfo(fileIdx, 1, 1))
+  if not isNimIdentifier(result.name.s):
+    rawMessage(graph.config, errGenerated, "invalid module name: '" & result.name.s &
+              "'; a module name must be a valid Nim identifier.")
+  partialInitModule(result, graph, fileIdx, filename)
+  graph.registerModule(result)
 
-proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode {.procvar.} =
+proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode =
   result = syntaxes.parseFile(fileIdx, graph.cache, graph.config)
   graph.addDep(s, fileIdx)
   graph.addIncludeDep(s.position.FileIndex, fileIdx)
-
-proc connectCallbacks*(graph: ModuleGraph) =
-  graph.includeFileCallback = includeModule
-  graph.importModuleCallback = importModule
-
-proc compileSystemModule*(graph: ModuleGraph) =
-  if graph.systemModule == nil:
-    connectCallbacks(graph)
-    graph.config.m.systemFileIdx = fileInfoIdx(graph.config, graph.config.libpath / "system.nim")
-    discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule})
+  let path = toFullPath(graph.config, fileIdx)
+  graph.cachedFiles[path] = $secureHashFile(path)
 
 proc wantMainModule*(conf: ConfigRef) =
-  if conf.projectFull.len == 0:
-    fatal(conf, newLineInfo(conf, "command line", 1, 1), errGenerated, "command expects a filename")
+  if conf.projectFull.isEmpty:
+    fatal(conf, gCmdLineInfo, "command expects a filename")
   conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt))
 
-proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIDX) =
-  connectCallbacks(graph)
-  let conf = graph.config
-  wantMainModule(conf)
-  let systemFileIdx = fileInfoIdx(conf, conf.libpath / "system.nim")
-  let projectFile = if projectFileIdx == InvalidFileIDX: conf.projectMainIdx else: projectFileIdx
-  graph.importStack.add projectFile
-  if projectFile == systemFileIdx:
-    discard graph.compileModule(projectFile, {sfMainModule, sfSystemModule})
-  else:
-    graph.compileSystemModule()
-    discard graph.compileModule(projectFile, {sfMainModule})
+proc makeModule*(graph: ModuleGraph; filename: AbsoluteFile): PSym =
+  result = graph.newModule(fileInfoIdx(graph.config, filename))
+  registerModule(graph, result)
 
 proc makeModule*(graph: ModuleGraph; filename: string): PSym =
-  result = graph.newModule(fileInfoIdx(graph.config, filename))
-  result.id = getID()
+  result = makeModule(graph, AbsoluteFile filename)
 
-proc makeStdinModule*(graph: ModuleGraph): PSym = graph.makeModule"stdin"
+proc makeStdinModule*(graph: ModuleGraph): PSym = graph.makeModule(AbsoluteFile"stdin")
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 62948e81e..c49ca8c9b 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -8,10 +8,36 @@
 #
 
 import
-  options, strutils, os, tables, ropes, platform, terminal, macros,
-  lineinfos
+  std/[strutils, os, tables, terminal, macros, times],
+  std/private/miscdollars,
+  options, lineinfos, pathutils
 
-proc toCChar*(c: char; result: var string) =
+import ropes except `%`
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions]
+
+
+type InstantiationInfo* = typeof(instantiationInfo())
+template instLoc*(): InstantiationInfo = instantiationInfo(-2, fullPaths = true)
+
+template toStdOrrKind(stdOrr): untyped =
+  if stdOrr == stdout: stdOrrStdout else: stdOrrStderr
+
+proc toLowerAscii(a: var string) {.inline.} =
+  for c in mitems(a):
+    if isUpperAscii(c): c = char(uint8(c) xor 0b0010_0000'u8)
+
+proc flushDot*(conf: ConfigRef) =
+  ## safe to call multiple times
+  # xxx one edge case not yet handled is when `printf` is called at CT with `compiletimeFFI`.
+  let stdOrr = if optStdout in conf.globalOptions: stdout else: stderr
+  let stdOrrKind = toStdOrrKind(stdOrr)
+  if stdOrrKind in conf.lastMsgWasDot:
+    conf.lastMsgWasDot.excl stdOrrKind
+    write(stdOrr, "\n")
+
+proc toCChar*(c: char; result: var string) {.inline.} =
   case c
   of '\0'..'\x1F', '\x7F'..'\xFF':
     result.add '\\'
@@ -23,32 +49,28 @@ proc toCChar*(c: char; result: var string) =
     result.add c
 
 proc makeCString*(s: string): Rope =
-  const MaxLineLength = 64
-  result = nil
-  var res = newStringOfCap(int(s.len.toFloat * 1.1) + 1)
-  add(res, "\"")
-  for i in countup(0, len(s) - 1):
-    if (i + 1) mod MaxLineLength == 0:
-      add(res, "\"\L\"")
-    toCChar(s[i], res)
-  add(res, '\"')
-  add(result, rope(res))
-
-
-proc newFileInfo(fullPath, projPath: string): TFileInfo =
-  result.fullPath = fullPath
-  #shallow(result.fullPath)
-  result.projPath = projPath
-  #shallow(result.projPath)
-  let fileName = projPath.extractFilename
-  result.shortName = fileName.changeFileExt("")
-  result.quotedName = fileName.makeCString
-  result.quotedFullName = fullPath.makeCString
-  result.lines = @[]
+  result = newStringOfCap(int(s.len.toFloat * 1.1) + 1)
+  result.add("\"")
+  for i in 0..<s.len:
+    # line wrapping of string litterals in cgen'd code was a bad idea, e.g. causes: bug #16265
+    # It also makes reading c sources or grepping harder, for zero benefit.
+    # const MaxLineLength = 64
+    # if (i + 1) mod MaxLineLength == 0:
+    #   res.add("\"\L\"")
+    toCChar(s[i], result)
+  result.add('\"')
+
+proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile): TFileInfo =
+  result = TFileInfo(fullPath: fullPath, projPath: projPath,
+                    shortName: fullPath.extractFilename,
+                    quotedFullName: fullPath.string.makeCString,
+                    lines: @[]
+  )
+  result.quotedName = result.shortName.makeCString
   when defined(nimpretty):
-    if result.fullPath.len > 0:
+    if not result.fullPath.isEmpty:
       try:
-        result.fullContent = readFile(result.fullPath)
+        result.fullContent = readFile(result.fullPath.string)
       except IOError:
         #rawMessage(errCannotOpenFile, result.fullPath)
         # XXX fixme
@@ -58,55 +80,76 @@ when defined(nimpretty):
   proc fileSection*(conf: ConfigRef; fid: FileIndex; a, b: int): string =
     substr(conf.m.fileInfos[fid.int].fullContent, a, b)
 
-proc fileInfoKnown*(conf: ConfigRef; filename: string): bool =
+proc canonicalCase(path: var string) {.inline.} =
+  ## the idea is to only use this for checking whether a path is already in
+  ## the table but otherwise keep the original case
+  when FileSystemCaseSensitive: discard
+  else: toLowerAscii(path)
+
+proc fileInfoKnown*(conf: ConfigRef; filename: AbsoluteFile): bool =
   var
-    canon: string
+    canon: AbsoluteFile
   try:
     canon = canonicalizePath(conf, filename)
-  except:
+  except OSError:
     canon = filename
-  result = conf.m.filenameToIndexTbl.hasKey(canon)
+  canon.string.canonicalCase
+  result = conf.m.filenameToIndexTbl.hasKey(canon.string)
 
-proc fileInfoIdx*(conf: ConfigRef; filename: string; isKnownFile: var bool): FileIndex =
+proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile; isKnownFile: var bool): FileIndex =
   var
-    canon: string
+    canon: AbsoluteFile
     pseudoPath = false
 
   try:
     canon = canonicalizePath(conf, filename)
-    shallow(canon)
-  except:
+  except OSError:
     canon = filename
     # The compiler uses "filenames" such as `command line` or `stdin`
     # This flag indicates that we are working with such a path here
     pseudoPath = true
 
-  if conf.m.filenameToIndexTbl.hasKey(canon):
-    result = conf.m.filenameToIndexTbl[canon]
+  var canon2 = canon.string
+  canon2.canonicalCase
+
+  if conf.m.filenameToIndexTbl.hasKey(canon2):
+    isKnownFile = true
+    result = conf.m.filenameToIndexTbl[canon2]
   else:
     isKnownFile = false
     result = conf.m.fileInfos.len.FileIndex
-    conf.m.fileInfos.add(newFileInfo(canon, if pseudoPath: filename
-                                            else: shortenDir(conf, canon)))
-    conf.m.filenameToIndexTbl[canon] = result
+    conf.m.fileInfos.add(newFileInfo(canon, if pseudoPath: RelativeFile filename
+                                            else: relativeTo(canon, conf.projectPath)))
+    conf.m.filenameToIndexTbl[canon2] = result
 
-proc fileInfoIdx*(conf: ConfigRef; filename: string): FileIndex =
-  var dummy: bool
+proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile): FileIndex =
+  var dummy: bool = false
   result = fileInfoIdx(conf, filename, dummy)
 
+proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile; isKnownFile: var bool): FileIndex =
+  fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), isKnownFile)
+
+proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile): FileIndex =
+  var dummy: bool = false
+  fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), dummy)
+
 proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
-  result.fileIndex = fileInfoIdx
-  result.line = uint16(line)
-  result.col = int16(col)
+  result = TLineInfo(fileIndex: fileInfoIdx)
+  if line < int high(uint16):
+    result.line = uint16(line)
+  else:
+    result.line = high(uint16)
+  if col < int high(int16):
+    result.col = int16(col)
+  else:
+    result.col = -1
 
-proc newLineInfo*(conf: ConfigRef; filename: string, line, col: int): TLineInfo {.inline.} =
+proc newLineInfo*(conf: ConfigRef; filename: AbsoluteFile, line, col: int): TLineInfo {.inline.} =
   result = newLineInfo(fileInfoIdx(conf, filename), line, col)
 
-proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} =
-  raise newException(ERecoverableError, msg)
-
+const gCmdLineInfo* = newLineInfo(commandLineIdx, 1, 1)
 
-proc concat(strings: openarray[string]): string =
+proc concat(strings: openArray[string]): string =
   var totalLen = 0
   for s in strings: totalLen += s.len
   result = newStringOfCap totalLen
@@ -129,7 +172,6 @@ proc suggestQuit*() =
 # this format is understood by many text editors: it is the same that
 # Borland and Freepascal use
 const
-  PosFormat    = "$1($2, $3) "
   KindFormat   = " [$1]"
   KindColor    = fgCyan
   ErrorTitle   = "Error: "
@@ -138,45 +180,67 @@ 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
+  commandLineDesc* = "command line"
 
 proc getInfoContextLen*(conf: ConfigRef): int = return conf.m.msgContext.len
 proc setInfoContextLen*(conf: ConfigRef; L: int) = setLen(conf.m.msgContext, L)
 
-proc pushInfoContext*(conf: ConfigRef; info: TLineInfo) =
-  conf.m.msgContext.add(info)
+proc pushInfoContext*(conf: ConfigRef; info: TLineInfo; detail: string = "") =
+  conf.m.msgContext.add((info, detail))
 
 proc popInfoContext*(conf: ConfigRef) =
-  setLen(conf.m.msgContext, len(conf.m.msgContext) - 1)
+  setLen(conf.m.msgContext, conf.m.msgContext.len - 1)
 
 proc getInfoContext*(conf: ConfigRef; index: int): TLineInfo =
-  let L = conf.m.msgContext.len
-  let i = if index < 0: L + index else: index
-  if i >=% L: result = unknownLineInfo()
-  else: result = conf.m.msgContext[i]
+  let i = if index < 0: conf.m.msgContext.len + index else: index
+  if i >=% conf.m.msgContext.len: result = unknownLineInfo
+  else: result = conf.m.msgContext[i].info
 
 template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string =
-  (if fileIdx.int32 < 0: "???" else: conf.m.fileInfos[fileIdx.int32].projPath)
+  if fileIdx.int32 < 0 or conf == nil:
+    (if fileIdx == commandLineIdx: commandLineDesc else: "???")
+  else:
+    conf.m.fileInfos[fileIdx.int32].shortName
+
+proc toProjPath*(conf: ConfigRef; fileIdx: FileIndex): string =
+  if fileIdx.int32 < 0 or conf == nil:
+    (if fileIdx == commandLineIdx: commandLineDesc else: "???")
+  else: conf.m.fileInfos[fileIdx.int32].projPath.string
 
 proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string =
-  if fileIdx.int32 < 0: result = "???"
-  else: result = conf.m.fileInfos[fileIdx.int32].fullPath
+  if fileIdx.int32 < 0 or conf == nil:
+    result = (if fileIdx == commandLineIdx: commandLineDesc else: "???")
+  else:
+    result = conf.m.fileInfos[fileIdx.int32].fullPath.string
 
-proc setDirtyFile*(conf: ConfigRef; fileIdx: FileIndex; filename: string) =
+proc setDirtyFile*(conf: ConfigRef; fileIdx: FileIndex; filename: AbsoluteFile) =
   assert fileIdx.int32 >= 0
   conf.m.fileInfos[fileIdx.int32].dirtyFile = filename
+  setLen conf.m.fileInfos[fileIdx.int32].lines, 0
 
 proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) =
   assert fileIdx.int32 >= 0
-  shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash)
+  when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
+    conf.m.fileInfos[fileIdx.int32].hash = hash
+  else:
+    shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash)
+
 
 proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string =
   assert fileIdx.int32 >= 0
-  shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash)
+  when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
+    result = conf.m.fileInfos[fileIdx.int32].hash
+  else:
+    shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash)
 
-proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): string =
+proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): AbsoluteFile =
   if fileIdx.int32 < 0:
-    result = "???"
-  elif not conf.m.fileInfos[fileIdx.int32].dirtyFile.isNil:
+    result = AbsoluteFile(if fileIdx == commandLineIdx: commandLineDesc else: "???")
+  elif not conf.m.fileInfos[fileIdx.int32].dirtyFile.isEmpty:
     result = conf.m.fileInfos[fileIdx.int32].dirtyFile
   else:
     result = conf.m.fileInfos[fileIdx.int32].fullPath
@@ -184,16 +248,42 @@ proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): string =
 template toFilename*(conf: ConfigRef; info: TLineInfo): string =
   toFilename(conf, info.fileIndex)
 
+template toProjPath*(conf: ConfigRef; info: TLineInfo): string =
+  toProjPath(conf, info.fileIndex)
+
 template toFullPath*(conf: ConfigRef; info: TLineInfo): string =
   toFullPath(conf, info.fileIndex)
 
-proc toMsgFilename*(conf: ConfigRef; info: TLineInfo): string =
-  if info.fileIndex.int32 < 0:
-    result = "???"
-  elif optListFullPaths in conf.globalOptions:
-    result = conf.m.fileInfos[info.fileIndex.int32].fullPath
-  else:
-    result = conf.m.fileInfos[info.fileIndex.int32].projPath
+template toFullPathConsiderDirty*(conf: ConfigRef; info: TLineInfo): string =
+  string toFullPathConsiderDirty(conf, info.fileIndex)
+
+proc toFilenameOption*(conf: ConfigRef, fileIdx: FileIndex, opt: FilenameOption): string =
+  case opt
+  of foAbs: result = toFullPath(conf, fileIdx)
+  of foRelProject: result = toProjPath(conf, fileIdx)
+  of foCanonical:
+    let absPath = toFullPath(conf, fileIdx)
+    result = canonicalImportAux(conf, absPath.AbsoluteFile)
+  of foName: result = toProjPath(conf, fileIdx).lastPathPart
+  of foLegacyRelProj:
+    let
+      absPath = toFullPath(conf, fileIdx)
+      relPath = toProjPath(conf, fileIdx)
+    result = if (relPath.len > absPath.len) or (relPath.count("..") > 2):
+               absPath
+             else:
+               relPath
+  of foStacktrace:
+    if optExcessiveStackTrace in conf.globalOptions:
+      result = toFilenameOption(conf, fileIdx, foAbs)
+    else:
+      result = toFilenameOption(conf, fileIdx, foName)
+
+proc toMsgFilename*(conf: ConfigRef; fileIdx: FileIndex): string =
+  toFilenameOption(conf, fileIdx, conf.filenameOption)
+
+template toMsgFilename*(conf: ConfigRef; info: TLineInfo): string =
+  toMsgFilename(conf, info.fileIndex)
 
 proc toLinenumber*(info: TLineInfo): int {.inline.} =
   result = int info.line
@@ -201,11 +291,13 @@ proc toLinenumber*(info: TLineInfo): int {.inline.} =
 proc toColumn*(info: TLineInfo): int {.inline.} =
   result = info.col
 
-proc toFileLine*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
-  result = toFilename(conf, info) & ":" & $info.line
+proc toFileLineCol(info: InstantiationInfo): string {.inline.} =
+  result = ""
+  result.toLocation(info.filename, info.line, info.column + ColOffset)
 
 proc toFileLineCol*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
-  result = toFilename(conf, info) & "(" & $info.line & ", " & $info.col & ")"
+  result = ""
+  result.toLocation(toMsgFilename(conf, info), info.line.int, info.col.int + ColOffset)
 
 proc `$`*(conf: ConfigRef; info: TLineInfo): string = toFileLineCol(conf, info)
 
@@ -219,6 +311,7 @@ type
   MsgFlag* = enum  ## flags altering msgWriteln behavior
     msgStdout,     ## force writing to stdout, even stderr is default
     msgSkipHook    ## skip message hook even if it is present
+    msgNoUnitSep  ## the message is a complete "paragraph".
   MsgFlags* = set[MsgFlag]
 
 proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) =
@@ -230,16 +323,20 @@ proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) =
   ## This is used for 'nim dump' etc. where we don't have nimsuggest
   ## support.
   #if conf.cmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
-
+  let sep = if msgNoUnitSep notin flags: conf.unitSep else: ""
   if not isNil(conf.writelnHook) and msgSkipHook notin flags:
-    conf.writelnHook(s)
+    conf.writelnHook(s & sep)
   elif optStdout in conf.globalOptions or msgStdout in flags:
     if eStdOut in conf.m.errorOutputs:
-      writeLine(stdout, s)
+      flushDot(conf)
+      write stdout, s
+      writeLine(stdout, sep)
       flushFile(stdout)
   else:
     if eStdErr in conf.m.errorOutputs:
-      writeLine(stderr, s)
+      flushDot(conf)
+      write stderr, s
+      writeLine(stderr, sep)
       # On Windows stderr is fully-buffered when piped, regardless of C std.
       when defined(windows):
         flushFile(stderr)
@@ -267,66 +364,85 @@ macro callStyledWriteLineStderr(args: varargs[typed]): untyped =
   result.add(bindSym"stderr")
   for arg in children(args[0][1]):
     result.add(arg)
+  when false:
+    # not needed because styledWriteLine already ends with resetAttributes
+    result = newStmtList(result, newCall(bindSym"resetAttributes", bindSym"stderr"))
 
 template callWritelnHook(args: varargs[string, `$`]) =
   conf.writelnHook concat(args)
 
-template styledMsgWriteln*(args: varargs[typed]) =
+proc msgWrite(conf: ConfigRef; s: string) =
+  if conf.m.errorOutputs != {}:
+    let stdOrr =
+      if optStdout in conf.globalOptions:
+        stdout
+      else:
+        stderr
+    write(stdOrr, s)
+    flushFile(stdOrr)
+    conf.lastMsgWasDot.incl stdOrr.toStdOrrKind() # subsequent writes need `flushDot`
+
+template styledMsgWriteln(args: varargs[typed]) =
   if not isNil(conf.writelnHook):
     callIgnoringStyle(callWritelnHook, nil, args)
   elif optStdout in conf.globalOptions:
     if eStdOut in conf.m.errorOutputs:
+      flushDot(conf)
       callIgnoringStyle(writeLine, stdout, args)
       flushFile(stdout)
-  else:
-    if eStdErr in conf.m.errorOutputs:
-      if optUseColors in conf.globalOptions:
-        callStyledWriteLineStderr(args)
-      else:
-        callIgnoringStyle(writeLine, stderr, args)
-      # On Windows stderr is fully-buffered when piped, regardless of C std.
-      when defined(windows):
-        flushFile(stderr)
-
-proc coordToStr(coord: int): string =
-  if coord == -1: result = "???"
-  else: result = $coord
+  elif eStdErr in conf.m.errorOutputs:
+    flushDot(conf)
+    if optUseColors in conf.globalOptions:
+      callStyledWriteLineStderr(args)
+    else:
+      callIgnoringStyle(writeLine, stderr, args)
+    # On Windows stderr is fully-buffered when piped, regardless of C std.
+    when defined(windows):
+      flushFile(stderr)
 
-proc msgKindToString*(kind: TMsgKind): string =
+proc msgKindToString*(kind: TMsgKind): string = MsgKindToStr[kind]
   # later versions may provide translated error messages
-  result = MsgKindToStr[kind]
 
-proc getMessageStr(msg: TMsgKind, arg: string): string =
-  result = msgKindToString(msg) % [arg]
+proc getMessageStr(msg: TMsgKind, arg: string): string = msgKindToString(msg) % [arg]
 
-type
-  TErrorHandling = enum doNothing, doAbort, doRaise
+type TErrorHandling* = enum doNothing, doAbort, doRaise
 
-proc log*(s: string) {.procvar.} =
-  var f: File
+proc log*(s: string) =
+  var f: File = default(File)
   if open(f, getHomeDir() / "nimsuggest.log", fmAppend):
     f.writeLine(s)
     close(f)
 
-proc quit(conf: ConfigRef; msg: TMsgKind) =
-  if defined(debug) or msg == errInternal or hintStackTrace in conf.notes:
-    if stackTraceAvailable() and isNil(conf.writelnHook):
-      writeStackTrace()
-    else:
-      styledMsgWriteln(fgRed, "No stack traceback available\n" &
-          "To create a stacktrace, rerun compilation with ./koch temp " &
-          conf.command & " <file>")
+proc quit(conf: ConfigRef; msg: TMsgKind) {.gcsafe.} =
+  if conf.isDefined("nimDebug"): quitOrRaise(conf, $msg)
+  elif defined(debug) or msg == errInternal or conf.hasHint(hintStackTrace):
+    {.gcsafe.}:
+      if stackTraceAvailable() and isNil(conf.writelnHook):
+        writeStackTrace()
+      else:
+        styledMsgWriteln(fgRed, """
+No stack traceback available
+To create a stacktrace, rerun compilation with './koch temp $1 <file>', see $2 for details""" %
+          [conf.command, "intern.html#debugging-the-compiler".createDocLink], conf.unitSep)
   quit 1
 
-proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string) =
-  if msg >= fatalMin and msg <= fatalMax:
+proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string, ignoreMsg: bool) =
+  if msg in fatalMsgs:
     if conf.cmd == cmdIdeTools: log(s)
-    quit(conf, msg)
-  if msg >= errMin and msg <= errMax:
+    if conf.cmd != cmdIdeTools or msg != errFatal:
+      quit(conf, msg)
+  if msg >= errMin and msg <= errMax or
+      (msg in warnMin..hintMax and msg in conf.warningAsErrors and not ignoreMsg):
     inc(conf.errorCounter)
     conf.exitcode = 1'i8
     if conf.errorCounter >= conf.errorMax:
-      quit(conf, msg)
+      # only really quit when we're not in the new 'nim check --def' mode:
+      if conf.ideCmd == ideNone:
+        when defined(nimsuggest):
+          #we need to inform the user that something went wrong when initializing NimSuggest
+          raiseRecoverableError(s)
+        else:
+          quit(conf, msg)
     elif eh == doAbort and conf.cmd != cmdIdeTools:
       quit(conf, msg)
     elif eh == doRaise:
@@ -340,220 +456,268 @@ proc exactEquals*(a, b: TLineInfo): bool =
 
 proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) =
   const instantiationFrom = "template/generic instantiation from here"
+  const instantiationOfFrom = "template/generic instantiation of `$1` from here"
   var info = lastinfo
-  for i in 0 ..< len(conf.m.msgContext):
-    if conf.m.msgContext[i] != lastinfo and conf.m.msgContext[i] != info:
+  for i in 0..<conf.m.msgContext.len:
+    let context = conf.m.msgContext[i]
+    if context.info != lastinfo and context.info != info:
       if conf.structuredErrorHook != nil:
-        conf.structuredErrorHook(conf, conf.m.msgContext[i], instantiationFrom,
-                                  Severity.Error)
+        conf.structuredErrorHook(conf, context.info, instantiationFrom,
+                                 Severity.Hint)
       else:
-        styledMsgWriteln(styleBright,
-                         PosFormat % [toMsgFilename(conf, conf.m.msgContext[i]),
-                                      coordToStr(conf.m.msgContext[i].line.int),
-                                      coordToStr(conf.m.msgContext[i].col+1)],
-                         resetStyle,
-                         instantiationFrom)
-    info = conf.m.msgContext[i]
+        let message =
+          if context.detail == "":
+            instantiationFrom
+          else:
+            instantiationOfFrom.format(context.detail)
+        styledMsgWriteln(styleBright, conf.toFileLineCol(context.info), " ", resetStyle, message)
+    info = context.info
 
 proc ignoreMsgBecauseOfIdeTools(conf: ConfigRef; msg: TMsgKind): bool =
   msg >= errGenerated and conf.cmd == cmdIdeTools and optIdeDebug notin conf.globalOptions
 
-proc rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) =
-  var
-    title: string
-    color: ForegroundColor
-    kind: string
-    sev: Severity
-  case msg
-  of errMin..errMax:
-    sev = Severity.Error
-    writeContext(conf, unknownLineInfo())
-    title = ErrorTitle
-    color = ErrorColor
-  of warnMin..warnMax:
-    sev = Severity.Warning
-    if optWarns notin conf.options: return
-    if msg notin conf.notes: return
-    writeContext(conf, unknownLineInfo())
-    title = WarningTitle
-    color = WarningColor
-    kind = WarningsToStr[ord(msg) - ord(warnMin)]
-    inc(conf.warnCounter)
-  of hintMin..hintMax:
-    sev = Severity.Hint
-    if optHints notin conf.options: return
-    if msg notin conf.notes: return
-    title = HintTitle
-    color = HintColor
-    if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)]
-    inc(conf.hintCounter)
-  let s = msgKindToString(msg) % args
-
-  if conf.structuredErrorHook != nil:
-    conf.structuredErrorHook(conf, unknownLineInfo(),
-      s & (if kind != nil: KindFormat % kind else: ""), sev)
-
-  if not ignoreMsgBecauseOfIdeTools(conf, msg):
-    if kind != nil:
-      styledMsgWriteln(color, title, resetStyle, s,
-                       KindColor, `%`(KindFormat, kind))
-    else:
-      styledMsgWriteln(color, title, resetStyle, s)
-  handleError(conf, msg, doAbort, s)
-
-proc rawMessage*(conf: ConfigRef; msg: TMsgKind, arg: string) =
-  rawMessage(conf, msg, [arg])
-
-proc resetAttributes*(conf: ConfigRef) =
-  if {optUseColors, optStdout} * conf.globalOptions == {optUseColors}:
-    terminal.resetAttributes(stderr)
-
 proc addSourceLine(conf: ConfigRef; fileIdx: FileIndex, line: string) =
   conf.m.fileInfos[fileIdx.int32].lines.add line
 
-proc sourceLine*(conf: ConfigRef; i: TLineInfo): string =
-  if i.fileIndex.int32 < 0: return ""
-
-  if not optPreserveOrigSource(conf) and conf.m.fileInfos[i.fileIndex.int32].lines.len == 0:
+proc numLines*(conf: ConfigRef, fileIdx: FileIndex): int =
+  ## xxx there's an off by 1 error that should be fixed; if a file ends with "foo" or "foo\n"
+  ## it will return same number of lines (ie, a trailing empty line is discounted)
+  result = conf.m.fileInfos[fileIdx.int32].lines.len
+  if result == 0:
     try:
-      for line in lines(toFullPath(conf, i)):
-        addSourceLine conf, i.fileIndex, line.string
+      for line in lines(toFullPathConsiderDirty(conf, fileIdx).string):
+        addSourceLine conf, fileIdx, line
     except IOError:
       discard
-  assert i.fileIndex.int32 < conf.m.fileInfos.len
+    result = conf.m.fileInfos[fileIdx.int32].lines.len
+
+proc sourceLine*(conf: ConfigRef; i: TLineInfo): string =
+  ## 1-based index (matches editor line numbers); 1st line is for i.line = 1
+  ## last valid line is `numLines` inclusive
+  if i.fileIndex.int32 < 0: return ""
+  let num = numLines(conf, i.fileIndex)
   # can happen if the error points to EOF:
-  if i.line.int > conf.m.fileInfos[i.fileIndex.int32].lines.len: return ""
+  if i.line.int > num: return ""
 
   result = conf.m.fileInfos[i.fileIndex.int32].lines[i.line.int-1]
 
-proc writeSurroundingSrc(conf: ConfigRef; info: TLineInfo) =
-  const indent = "  "
-  msgWriteln(conf, indent & $sourceLine(conf, info))
-  msgWriteln(conf, indent & spaces(info.col) & '^')
+proc getSurroundingSrc(conf: ConfigRef; info: TLineInfo): string =
+  if conf.hasHint(hintSource) and info != unknownLineInfo:
+    const indent = "  "
+    result = "\n" & indent & $sourceLine(conf, info)
+    if info.col >= 0:
+      result.add "\n" & indent & spaces(info.col) & '^'
+  else:
+    result = ""
 
 proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): string =
   let title = case msg
               of warnMin..warnMax: WarningTitle
               of hintMin..hintMax: HintTitle
               else: ErrorTitle
-  result = PosFormat % [toMsgFilename(conf, info), coordToStr(info.line.int),
-                        coordToStr(info.col+1)] &
-           title &
-           getMessageStr(msg, arg)
+  conf.toFileLineCol(info) & " " & title & getMessageStr(msg, arg)
 
-proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
-               eh: TErrorHandling) =
+proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
+               eh: TErrorHandling, info2: InstantiationInfo, isRaw = false,
+               ignoreError = false) {.gcsafe, noinline.} =
   var
     title: string
     color: ForegroundColor
-    kind:  string
     ignoreMsg = false
     sev: Severity
+  let errorOutputsOld = conf.m.errorOutputs
+  if msg in fatalMsgs:
+    # don't gag, refs bug #7080, bug #18278; this can happen with `{.fatal.}`
+    # or inside a `tryConstExpr`.
+    conf.m.errorOutputs = {eStdOut, eStdErr}
+
+  let kind = if msg in warnMin..hintMax and msg != hintUserRaw: $msg else: "" # xxx not sure why hintUserRaw is special
   case msg
   of errMin..errMax:
     sev = Severity.Error
     writeContext(conf, info)
     title = ErrorTitle
     color = ErrorColor
-    # we try to filter error messages so that not two error message
-    # in the same file and line are produced:
-    #ignoreMsg = lastError == info and eh != doAbort
-    conf.m.lastError = info
+    when false:
+      # we try to filter error messages so that not two error message
+      # in the same file and line are produced:
+      # xxx `lastError` is only used in this disabled code; but could be useful to revive
+      ignoreMsg = conf.m.lastError == info and info != unknownLineInfo and eh != doAbort
+    if info != unknownLineInfo: conf.m.lastError = info
   of warnMin..warnMax:
     sev = Severity.Warning
-    ignoreMsg = optWarns notin conf.options or msg notin conf.notes
+    ignoreMsg = not conf.hasWarn(msg)
+    if not ignoreMsg and msg in conf.warningAsErrors:
+      title = ErrorTitle
+      color = ErrorColor
+    else:
+      title = WarningTitle
+      color = WarningColor
     if not ignoreMsg: writeContext(conf, info)
-    title = WarningTitle
-    color = WarningColor
-    kind = WarningsToStr[ord(msg) - ord(warnMin)]
     inc(conf.warnCounter)
   of hintMin..hintMax:
     sev = Severity.Hint
-    ignoreMsg = optHints notin conf.options or msg notin conf.notes
-    title = HintTitle
-    color = HintColor
-    if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)]
+    ignoreMsg = not conf.hasHint(msg)
+    if not ignoreMsg and msg in conf.warningAsErrors:
+      title = ErrorTitle
+      color = ErrorColor
+    else:
+      title = HintTitle
+      color = HintColor
     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)]
-  let s = getMessageStr(msg, arg)
 
+  let s = if isRaw: arg else: getMessageStr(msg, arg)
   if not ignoreMsg:
+    let loc = if info != unknownLineInfo: conf.toFileLineCol(info) & " " else: ""
+    # we could also show `conf.cmdInput` here for `projectIsCmd`
+    var kindmsg = if kind.len > 0: KindFormat % kind else: ""
     if conf.structuredErrorHook != nil:
-      conf.structuredErrorHook(conf, info, s & (if kind != nil: KindFormat % kind else: ""), sev)
+      conf.structuredErrorHook(conf, info, s & kindmsg, sev)
     if not ignoreMsgBecauseOfIdeTools(conf, msg):
-      if kind != nil:
-        styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s,
-                         KindColor, `%`(KindFormat, kind))
+      if msg == hintProcessing and conf.hintProcessingDots:
+        msgWrite(conf, ".")
       else:
-        styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s)
-      if hintSource in conf.notes:
-        conf.writeSurroundingSrc(info)
-  handleError(conf, msg, eh, s)
-
-proc fatal*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
-  # this fixes bug #7080 so that it is at least obvious 'fatal'
-  # was executed.
-  conf.m.errorOutputs = {eStdOut, eStdErr}
-  liMessage(conf, info, msg, arg, doAbort)
-
-proc globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
-  liMessage(conf, info, msg, arg, doRaise)
-
-proc globalError*(conf: ConfigRef; info: TLineInfo, arg: string) =
-  liMessage(conf, info, errGenerated, arg, doRaise)
-
-proc localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
-  liMessage(conf, info, msg, arg, doNothing)
-
-proc localError*(conf: ConfigRef; info: TLineInfo, arg: string) =
-  liMessage(conf, info, errGenerated, arg, doNothing)
-
-proc localError*(conf: ConfigRef; info: TLineInfo, format: string, params: openarray[string]) =
-  localError(conf, info, format % params)
-
-proc message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
-  liMessage(conf, info, msg, arg, doNothing)
-
-proc internalError*(conf: ConfigRef; info: TLineInfo, errMsg: string) =
-  if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return
+        styledMsgWriteln(styleBright, loc, resetStyle, color, title, resetStyle, s, KindColor, kindmsg,
+                         resetStyle, conf.getSurroundingSrc(info), conf.unitSep)
+        if hintMsgOrigin in conf.mainPackageNotes:
+          # xxx needs a bit of refactoring to honor `conf.filenameOption`
+          styledMsgWriteln(styleBright, toFileLineCol(info2), resetStyle,
+            " compiler msg initiated here", KindColor,
+            KindFormat % $hintMsgOrigin,
+            resetStyle, conf.unitSep)
+  if not ignoreError:
+    handleError(conf, msg, eh, s, ignoreMsg)
+  if msg in fatalMsgs:
+    # most likely would have died here but just in case, we restore state
+    conf.m.errorOutputs = errorOutputsOld
+
+template rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) =
+  let arg = msgKindToString(msg) % args
+  liMessage(conf, unknownLineInfo, msg, arg, eh = doAbort, instLoc(), isRaw = true)
+
+template rawMessage*(conf: ConfigRef; msg: TMsgKind, arg: string) =
+  liMessage(conf, unknownLineInfo, msg, arg, eh = doAbort, instLoc())
+
+template fatal*(conf: ConfigRef; info: TLineInfo, arg = "", msg = errFatal) =
+  liMessage(conf, info, msg, arg, doAbort, instLoc())
+
+template globalAssert*(conf: ConfigRef; cond: untyped, info: TLineInfo = unknownLineInfo, arg = "") =
+  ## avoids boilerplate
+  if not cond:
+    var arg2 = "'$1' failed" % [astToStr(cond)]
+    if arg.len > 0: arg2.add "; " & astToStr(arg) & ": " & arg
+    liMessage(conf, info, errGenerated, arg2, doRaise, instLoc())
+
+template globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
+  ## `local` means compilation keeps going until errorMax is reached (via `doNothing`),
+  ## `global` means it stops.
+  liMessage(conf, info, msg, arg, doRaise, instLoc())
+
+template globalError*(conf: ConfigRef; info: TLineInfo, arg: string) =
+  liMessage(conf, info, errGenerated, arg, doRaise, instLoc())
+
+template localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
+  liMessage(conf, info, msg, arg, doNothing, instLoc())
+
+template localError*(conf: ConfigRef; info: TLineInfo, arg: string) =
+  liMessage(conf, info, errGenerated, arg, doNothing, instLoc())
+
+template message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
+  liMessage(conf, info, msg, arg, doNothing, instLoc())
+
+proc warningDeprecated*(conf: ConfigRef, info: TLineInfo = gCmdLineInfo, msg = "") {.inline.} =
+  message(conf, info, warnDeprecated, msg)
+
+proc internalErrorImpl(conf: ConfigRef; info: TLineInfo, errMsg: string, info2: InstantiationInfo) =
+  if conf.cmd in {cmdIdeTools, cmdCheck} and conf.structuredErrorHook.isNil: return
   writeContext(conf, info)
-  liMessage(conf, info, errInternal, errMsg, doAbort)
+  liMessage(conf, info, errInternal, errMsg, doAbort, info2)
 
-proc internalError*(conf: ConfigRef; errMsg: string) =
-  if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return
-  writeContext(conf, unknownLineInfo())
-  rawMessage(conf, errInternal, errMsg)
+template internalError*(conf: ConfigRef; info: TLineInfo, errMsg: string) =
+  internalErrorImpl(conf, info, errMsg, instLoc())
 
-template assertNotNil*(conf: ConfigRef; e): untyped =
-  if e == nil: internalError(conf, $instantiationInfo())
-  e
+template internalError*(conf: ConfigRef; errMsg: string) =
+  internalErrorImpl(conf, unknownLineInfo, errMsg, instLoc())
 
 template internalAssert*(conf: ConfigRef, e: bool) =
-  if not e: internalError(conf, $instantiationInfo())
+  # xxx merge with `globalAssert`
+  if not e:
+    const info2 = instLoc()
+    let arg = info2.toFileLineCol
+    internalErrorImpl(conf, unknownLineInfo, arg, info2)
+
+template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, extraMsg = "") =
+  let m = "'$1' should be: '$2'$3" % [got, beau, extraMsg]
+  let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName
+  liMessage(conf, info, msg, m, doNothing, instLoc())
+
+proc quotedFilename*(conf: ConfigRef; fi: FileIndex): Rope =
+  if fi.int32 < 0:
+    result = makeCString "???"
+  elif optExcessiveStackTrace in conf.globalOptions:
+    result = conf.m.fileInfos[fi.int32].quotedFullName
+  else:
+    result = conf.m.fileInfos[fi.int32].quotedName
 
 proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
-  assert i.fileIndex.int32 >= 0
-  if optExcessiveStackTrace in conf.globalOptions:
-    result = conf.m.fileInfos[i.fileIndex.int32].quotedFullName
+  quotedFilename(conf, i.fileIndex)
+
+template listMsg(title, r) =
+  msgWriteln(conf, title, {msgNoUnitSep})
+  for a in r: msgWriteln(conf, "  [$1] $2" % [if a in conf.notes: "x" else: " ", $a], {msgNoUnitSep})
+
+proc listWarnings*(conf: ConfigRef) = listMsg("Warnings:", warnMin..warnMax)
+proc listHints*(conf: ConfigRef) = listMsg("Hints:", hintMin..hintMax)
+
+proc genSuccessX*(conf: ConfigRef) =
+  let mem =
+    when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem"
+    else: formatSize(getTotalMem()) & " totmem"
+  let loc = $conf.linesCompiled
+  var build = ""
+  var flags = ""
+  const debugModeHints = "none (DEBUG BUILD, `-d:release` generates faster code)"
+  if conf.cmd in cmdBackends:
+    if conf.backend != backendJs:
+      build.add "mm: $#; " % $conf.selectedGC
+      if optThreads in conf.globalOptions: build.add "threads: on; "
+      build.add "opt: "
+      if optOptimizeSpeed in conf.options: build.add "speed"
+      elif optOptimizeSize in conf.options: build.add "size"
+      else: build.add debugModeHints
+        # pending https://github.com/timotheecour/Nim/issues/752, point to optimization.html
+      if isDefined(conf, "danger"): flags.add " -d:danger"
+      elif isDefined(conf, "release"): flags.add " -d:release"
+    else:
+      build.add "opt: "
+      if isDefined(conf, "danger"):
+        build.add "speed"
+        flags.add " -d:danger"
+      elif isDefined(conf, "release"):
+        build.add "speed"
+        flags.add " -d:release"
+      else: build.add debugModeHints
+    if flags.len > 0: build.add "; options:" & flags
+  let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3)
+  let project = if conf.filenameOption == foAbs: $conf.projectFull else: $conf.projectName
+    # xxx honor conf.filenameOption more accurately
+  var output: string
+  if optCompileOnly in conf.globalOptions and conf.cmd != cmdJsonscript:
+    output = $conf.jsonBuildFile
+  elif conf.outFile.isEmpty and conf.cmd notin {cmdJsonscript} + cmdDocLike + cmdBackends:
+    # for some cmd we expect a valid absOutFile
+    output = "unknownOutput"
+  elif optStdout in conf.globalOptions:
+    output = "stdout"
   else:
-    result = conf.m.fileInfos[i.fileIndex.int32].quotedName
-
-proc listWarnings*(conf: ConfigRef) =
-  msgWriteln(conf, "Warnings:")
-  for warn in warnMin..warnMax:
-    msgWriteln(conf, "  [$1] $2" % [
-      if warn in conf.notes: "x" else: " ",
-      lineinfos.WarningsToStr[ord(warn) - ord(warnMin)]
-    ])
-
-proc listHints*(conf: ConfigRef) =
-  msgWriteln(conf, "Hints:")
-  for hint in hintMin..hintMax:
-    msgWriteln(conf, "  [$1] $2" % [
-      if hint in conf.notes: "x" else: " ",
-      lineinfos.HintsToStr[ord(hint) - ord(hintMin)]
+    output = $conf.absOutFile
+  if conf.filenameOption != foAbs: output = output.AbsoluteFile.extractFilename
+    # xxx honor filenameOption more accurately
+  rawMessage(conf, hintSuccessX, [
+    "build", build,
+    "loc", loc,
+    "sec", sec,
+    "mem", mem,
+    "project", project,
+    "output", output,
     ])
diff --git a/compiler/ndi.nim b/compiler/ndi.nim
index 9708c388d..cc18ab39f 100644
--- a/compiler/ndi.nim
+++ b/compiler/ndi.nim
@@ -10,31 +10,43 @@
 ## This module implements the generation of ``.ndi`` files for better debugging
 ## support of Nim code. "ndi" stands for "Nim debug info".
 
-import ast, msgs, ropes, options
+import ast, msgs, ropes, options, pathutils
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions]
 
 type
   NdiFile* = object
     enabled: bool
     f: File
     buf: string
+    filename: AbsoluteFile
+    syms: seq[PSym]
 
 proc doWrite(f: var NdiFile; s: PSym; conf: ConfigRef) =
   f.buf.setLen 0
-  f.buf.add s.info.line.int
+  f.buf.addInt s.info.line.int
   f.buf.add "\t"
-  f.buf.add s.info.col.int
+  f.buf.addInt s.info.col.int
   f.f.write(s.name.s, "\t")
-  f.f.writeRope(s.loc.r)
+  f.f.writeRope(s.loc.snippet)
   f.f.writeLine("\t", toFullPath(conf, s.info), "\t", f.buf)
 
 template writeMangledName*(f: NdiFile; s: PSym; conf: ConfigRef) =
-  if f.enabled: doWrite(f, s, conf)
+  if f.enabled: f.syms.add s
 
-proc open*(f: var NdiFile; filename: string; conf: ConfigRef) =
-  f.enabled = filename.len > 0
+proc open*(f: var NdiFile; filename: AbsoluteFile; conf: ConfigRef) =
+  f.enabled = not filename.isEmpty
   if f.enabled:
-    f.f = open(filename, fmWrite, 8000)
+    f.filename = filename
     f.buf = newStringOfCap(20)
 
-proc close*(f: var NdiFile) =
-  if f.enabled: close(f.f)
+proc close*(f: var NdiFile, conf: ConfigRef) =
+  if f.enabled:
+    f.f = open(f.filename.string, fmWrite, 8000)
+    doAssert f.f != nil, f.filename.string
+    for s in f.syms:
+      doWrite(f, s, conf)
+    close(f.f)
+    f.syms.reset
+    f.filename.reset
diff --git a/compiler/nilcheck.nim b/compiler/nilcheck.nim
new file mode 100644
index 000000000..7e0efc34b
--- /dev/null
+++ b/compiler/nilcheck.nim
@@ -0,0 +1,1387 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2017 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+import ast, renderer, msgs, options, lineinfos, idents, treetab
+import std/[intsets, tables, sequtils, strutils, sets, strformat, hashes]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+# IMPORTANT: notes not up to date, i'll update this comment again
+#
+# notes:
+#
+# Env: int => nilability
+# a = b
+#   nilability a <- nilability b
+# deref a
+#   if Nil error is nil
+#   if MaybeNil error might be nil, hint add if isNil
+#   if Safe fine
+# fun(arg: A)
+#   nilability arg <- for ref MaybeNil, for not nil or others Safe
+# map is env?
+# a or b
+#   each one forks a different env
+#   result = union(envL, envR)
+# a and b
+#   b forks a's env
+# if a: code
+#   result = union(previousEnv after not a, env after code)
+# if a: b else: c
+#   result = union(env after b, env after c)
+# result = b
+#   nilability result <- nilability b, if return type is not nil and result not safe, error
+# return b
+#   as result = b
+# try: a except: b finally: c
+#   in b and c env is union of all possible try first n lines, after union of a and b and c
+#   keep in mind canRaise and finally
+# case a: of b: c
+#   similar to if
+# call(arg)
+#   if it returns ref, assume it's MaybeNil: hint that one can add not nil to the return type
+# call(var arg) # zahary comment
+#   if arg is ref, assume it's MaybeNil after call
+# loop
+#   union of env for 0, 1, 2 iterations as Herb Sutter's paper
+#   why 2?
+# return
+#   if something: stop (break return etc)
+#   is equivalent to if something: .. else: remain
+# new(ref)
+#   ref becomes Safe
+# objConstr(a: b)
+#   returns safe
+# each check returns its nilability and map
+
+type
+  SeqOfDistinct[T, U] = distinct seq[U]
+
+# TODO use distinct base type instead of int?
+func `[]`[T, U](a: SeqOfDistinct[T, U], index: T): U =
+  (seq[U])(a)[index.int]
+
+proc `[]=`[T, U](a: var SeqOfDistinct[T, U], index: T, value: U) =
+  ((seq[U])(a))[index.int] = value
+
+func `[]`[T, U](a: var SeqOfDistinct[T, U], index: T): var U =
+  (seq[U])(a)[index.int]
+
+func len[T, U](a: SeqOfDistinct[T, U]): T =
+  (seq[U])(a).len.T
+
+func low[T, U](a: SeqOfDistinct[T, U]): T =
+  (seq[U])(a).low.T
+
+func high[T, U](a: SeqOfDistinct[T, U]): T =
+  (seq[U])(a).high.T
+
+proc setLen[T, U](a: var SeqOfDistinct[T, U], length: T) =
+  ((seq[U])(a)).setLen(length.Natural)
+
+
+proc newSeqOfDistinct[T, U](length: T = 0.T): SeqOfDistinct[T, U] =
+  (SeqOfDistinct[T, U])(newSeq[U](length.int))
+
+func newSeqOfDistinct[T, U](length: int = 0): SeqOfDistinct[T, U] =
+  # newSeqOfDistinct(length.T)
+  # ? newSeqOfDistinct[T, U](length.T)
+  (SeqOfDistinct[T, U])(newSeq[U](length))
+
+iterator items[T, U](a: SeqOfDistinct[T, U]): U =
+  for element in (seq[U])(a):
+    yield element
+
+iterator pairs[T, U](a: SeqOfDistinct[T, U]): (T, U) =
+  for i, element in (seq[U])(a):
+    yield (i.T, element)
+
+func `$`[T, U](a: SeqOfDistinct[T, U]): string =
+  $((seq[U])(a))
+
+proc add*[T, U](a: var SeqOfDistinct[T, U], value: U) =
+  ((seq[U])(a)).add(value)
+
+type
+  ## a hashed representation of a node: should be equal for structurally equal nodes
+  Symbol = distinct int
+
+  ## the index of an expression in the pre-indexed sequence of those
+  ExprIndex = distinct int16
+
+  ## the set index
+  SetIndex = distinct int
+
+  ## transition kind:
+  ##   what was the reason for changing the nilability of an expression
+  ##   useful for error messages and showing why an expression is being detected as nil / maybe nil
+  TransitionKind = enum TArg, TAssign, TType, TNil, TVarArg, TResult, TSafe, TPotentialAlias, TDependant
+
+  ## keep history for each transition
+  History = object
+    info: TLineInfo ## the location
+    nilability: Nilability ## the nilability
+    kind: TransitionKind ## what kind of transition was that
+    node: PNode ## the node of the expression
+
+  ## the context for the checker: an instance for each procedure
+  NilCheckerContext = ref object
+    # abstractTime: AbstractTime
+    # partitions: Partitions
+    # symbolGraphs: Table[Symbol, ]
+    symbolIndices: Table[Symbol, ExprIndex] ## index for each symbol
+    expressions: SeqOfDistinct[ExprIndex, PNode] ## a sequence of pre-indexed expressions
+    dependants: SeqOfDistinct[ExprIndex, IntSet] ## expr indices for expressions which are compound and based on others
+    warningLocations: HashSet[TLineInfo] ## warning locations to check we don't warn twice for stuff like warnings in for loops
+    idgen: IdGenerator ## id generator
+    config: ConfigRef ## the config of the compiler
+
+  ## a map that is containing the current nilability for usually a branch
+  ## and is pointing optionally to a parent map: they make a stack of maps
+  NilMap = ref object
+    expressions:  SeqOfDistinct[ExprIndex, Nilability] ## the expressions with the same order as in NilCheckerContext
+    history:  SeqOfDistinct[ExprIndex, seq[History]] ## history for each of them
+    # what about gc and refs?
+    setIndices: SeqOfDistinct[ExprIndex, SetIndex] ## set indices for each expression
+    sets:     SeqOfDistinct[SetIndex, IntSet] ## disjoint sets with the aliased expressions
+    parent:   NilMap ## the parent map
+
+  ## Nilability : if a value is nilable.
+  ## we have maybe nil and nil, so we can differentiate between
+  ## cases where we know for sure a value is nil and not
+  ## otherwise we can have Safe, MaybeNil
+  ## Parent: is because we just use a sequence with the same length
+  ## instead of a table, and we need to check if something was initialized
+  ## at all: if Parent is set, then you need to check the parent nilability
+  ## if the parent is nil, then for now we return MaybeNil
+  ## unreachable is the result of add(Safe, Nil) and others
+  ## it is a result of no states left, so it's usually e.g. in unreachable else branches?
+  Nilability* = enum Parent, Safe, MaybeNil, Nil, Unreachable
+
+  ## check
+  Check = object
+    nilability: Nilability
+    map: NilMap
+    elements: seq[(PNode, Nilability)]
+
+
+# useful to have known resultId so we can set it in the beginning and on return
+const resultId: Symbol = (-1).Symbol
+const resultExprIndex: ExprIndex = 0.ExprIndex
+const noSymbol = (-2).Symbol
+
+func `<`*(a: ExprIndex, b: ExprIndex): bool =
+  a.int16 < b.int16
+
+func `<=`*(a: ExprIndex, b: ExprIndex): bool =
+  a.int16 <= b.int16
+
+func `>`*(a: ExprIndex, b: ExprIndex): bool =
+  a.int16 > b.int16
+
+func `>=`*(a: ExprIndex, b: ExprIndex): bool =
+  a.int16 >= b.int16
+
+func `==`*(a: ExprIndex, b: ExprIndex): bool =
+  a.int16 == b.int16
+
+func `$`*(a: ExprIndex): string =
+  $(a.int16)
+
+func `+`*(a: ExprIndex, b: ExprIndex): ExprIndex =
+  (a.int16 + b.int16).ExprIndex
+
+# TODO overflowing / < 0?
+func `-`*(a: ExprIndex, b: ExprIndex): ExprIndex =
+  (a.int16 - b.int16).ExprIndex
+
+func `$`*(a: SetIndex): string =
+  $(a.int)
+
+func `==`*(a: SetIndex, b: SetIndex): bool =
+  a.int == b.int
+
+func `+`*(a: SetIndex, b: SetIndex): SetIndex =
+  (a.int + b.int).SetIndex
+
+# TODO over / under limit?
+func `-`*(a: SetIndex, b: SetIndex): SetIndex =
+  (a.int - b.int).SetIndex
+
+proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check
+proc checkCondition(n: PNode, ctx: NilCheckerContext, map: NilMap, reverse: bool, base: bool): NilMap
+
+# the NilMap structure
+
+proc newNilMap(parent: NilMap = nil, count: int = -1): NilMap =
+  var expressionsCount = 0
+  if count != -1:
+    expressionsCount = count
+  elif not parent.isNil:
+    expressionsCount = parent.expressions.len.int
+  result = NilMap(
+    expressions: newSeqOfDistinct[ExprIndex, Nilability](expressionsCount),
+    history: newSeqOfDistinct[ExprIndex, seq[History]](expressionsCount),
+    setIndices: newSeqOfDistinct[ExprIndex, SetIndex](expressionsCount),
+    parent: parent)
+  if parent.isNil:
+    for i, expr in result.expressions:
+      result.setIndices[i] = i.SetIndex
+      var newSet = initIntSet()
+      newSet.incl(i.int)
+      result.sets.add(newSet)
+  else:
+    for i, exprs in parent.sets:
+      result.sets.add(exprs)
+    for i, index in parent.setIndices:
+      result.setIndices[i] = index
+    # result.sets = parent.sets
+  # if not parent.isNil:
+  #   # optimize []?
+  #   result.expressions = parent.expressions
+  #   result.history = parent.history
+  #   result.sets = parent.sets
+  # result.base = if parent.isNil: result else: parent.base
+
+proc `[]`(map: NilMap, index: ExprIndex): Nilability =
+  if index < 0.ExprIndex or index >= map.expressions.len:
+    return MaybeNil
+  var now = map
+  while not now.isNil:
+    if now.expressions[index] != Parent:
+      return now.expressions[index]
+    now = now.parent
+  return MaybeNil
+
+proc history(map: NilMap, index: ExprIndex): seq[History] =
+  if index < map.expressions.len:
+    map.history[index]
+  else:
+    @[]
+
+
+# helpers for debugging
+
+# import macros
+
+# echo-s only when nilDebugInfo is defined
+# macro aecho*(a: varargs[untyped]): untyped =
+#   var e = nnkCall.newTree(ident"echo")
+#   for b in a:
+#     e.add(b)
+#   result = quote:
+#     when defined(nilDebugInfo):
+#       `e`
+
+# end of helpers for debugging
+
+
+proc symbol(n: PNode): Symbol
+func `$`(map: NilMap): string
+proc reverseDirect(map: NilMap): NilMap
+proc checkBranch(n: PNode, ctx: NilCheckerContext, map: NilMap): Check
+proc hasUnstructuredControlFlowJump(n: PNode): bool
+
+proc symbol(n: PNode): Symbol =
+  ## returns a Symbol for each expression
+  ## the goal is to get an unique Symbol
+  ## but we have to ensure hashTree does it as we expect
+  case n.kind:
+  of nkIdent:
+    # TODO ensure no idents get passed to symbol
+    result = noSymbol
+  of nkSym:
+    if n.sym.kind == skResult: # credit to disruptek for showing me that
+      result = resultId
+    else:
+      result = n.sym.id.Symbol
+  of nkHiddenAddr, nkAddr:
+    result = symbol(n[0])
+  else:
+    result = hashTree(n).Symbol
+  # echo "symbol ", n, " ", n.kind, " ", result.int
+
+func `$`(map: NilMap): string =
+  result = ""
+  var now = map
+  var stack: seq[NilMap] = @[]
+  while not now.isNil:
+    stack.add(now)
+    now = now.parent
+  result.add("### start\n")
+  for i in 0 .. stack.len - 1:
+    now = stack[i]
+    result.add("  ###\n")
+    for index, value in now.expressions:
+      result.add(&"    {index} {value}\n")
+  result.add "### end\n"
+
+proc namedMapDebugInfo(ctx: NilCheckerContext, map: NilMap): string =
+  result = ""
+  var now = map
+  var stack: seq[NilMap] = @[]
+  while not now.isNil:
+    stack.add(now)
+    now = now.parent
+  result.add("### start\n")
+  for i in 0 .. stack.len - 1:
+    now = stack[i]
+    result.add("  ###\n")
+    for index, value in now.expressions:
+      let name = ctx.expressions[index]
+      result.add(&"    {name} {index} {value}\n")
+  result.add("### end\n")
+
+proc namedSetsDebugInfo(ctx: NilCheckerContext, map: NilMap): string =
+  result = "### sets "
+  for index, setIndex in map.setIndices:
+    var aliasSet = map.sets[setIndex]
+    result.add("{")
+    let expressions = aliasSet.mapIt($ctx.expressions[it.ExprIndex])
+    result.add(join(expressions, ", "))
+    result.add("} ")
+  result.add("\n")
+
+proc namedMapAndSetsDebugInfo(ctx: NilCheckerContext, map: NilMap): string =
+  result = namedMapDebugInfo(ctx, map) & namedSetsDebugInfo(ctx, map)
+
+
+
+const noExprIndex = (-1).ExprIndex
+const noSetIndex = (-1).SetIndex
+
+proc `==`(a: Symbol, b: Symbol): bool =
+  a.int == b.int
+
+func `$`(a: Symbol): string =
+  $(a.int)
+
+template isConstBracket(n: PNode): bool =
+  n.kind == nkBracketExpr and n[1].kind in nkLiterals
+
+proc index(ctx: NilCheckerContext, n: PNode): ExprIndex =
+  # echo "n ", n, " ", n.kind
+  let a = symbol(n)
+  if ctx.symbolIndices.hasKey(a):
+    return ctx.symbolIndices[a]
+  else:
+    #for a, e in ctx.expressions:
+    #  echo a, " ", e
+    #echo n.kind
+    # internalError(ctx.config, n.info, "expected " & $a & " " & $n & " to have a index")
+    return noExprIndex
+    #
+  #ctx.symbolIndices[symbol(n)]
+
+
+proc aliasSet(ctx: NilCheckerContext, map: NilMap, n: PNode): IntSet =
+  result = map.sets[map.setIndices[ctx.index(n)]]
+
+proc aliasSet(ctx: NilCheckerContext, map: NilMap, index: ExprIndex): IntSet =
+  result = map.sets[map.setIndices[index]]
+
+
+
+proc store(map: NilMap, ctx: NilCheckerContext, index: ExprIndex, value: Nilability, kind: TransitionKind, info: TLineInfo, node: PNode = nil) =
+  if index == noExprIndex:
+    return
+  map.expressions[index] = value
+  map.history[index].add(History(info: info, kind: kind, node: node, nilability: value))
+  #echo node, " ", index, " ", value
+  #echo ctx.namedMapAndSetsDebugInfo(map)
+  #for a, b in map.sets:
+  #  echo a, " ", b
+  # echo map
+
+  var exprAliases = aliasSet(ctx, map, index)
+  for a in exprAliases:
+    if a.ExprIndex != index:
+      #echo "alias ", a, " ", index
+      map.expressions[a.ExprIndex] = value
+      if value == Safe:
+        map.history[a.ExprIndex] = @[]
+      else:
+        map.history[a.ExprIndex].add(History(info: info, kind: TPotentialAlias, node: node, nilability: value))
+
+proc moveOut(ctx: NilCheckerContext, map: NilMap, target: PNode) =
+  #echo "move out ", target
+  var targetIndex = ctx.index(target)
+  var targetSetIndex = map.setIndices[targetIndex]
+  if targetSetIndex != noSetIndex:
+    var targetSet = map.sets[targetSetIndex]
+    if targetSet.len > 1:
+      var other: ExprIndex = default(ExprIndex)
+
+      for element in targetSet:
+        if element.ExprIndex != targetIndex:
+          other = element.ExprIndex
+          break
+          # map.sets[element].excl(targetIndex)
+      map.sets[map.setIndices[other]].excl(targetIndex.int)
+      var newSet = initIntSet()
+      newSet.incl(targetIndex.int)
+      map.sets.add(newSet)
+      map.setIndices[targetIndex] = map.sets.len - 1.SetIndex
+
+proc moveOutDependants(ctx: NilCheckerContext, map: NilMap, node: PNode) =
+  let index = ctx.index(node)
+  for dependant in ctx.dependants[index]:
+    moveOut(ctx, map, ctx.expressions[dependant.ExprIndex])
+
+proc storeDependants(ctx: NilCheckerContext, map: NilMap, node: PNode, value: Nilability) =
+  let index = ctx.index(node)
+  for dependant in ctx.dependants[index]:
+    map.store(ctx, dependant.ExprIndex, value, TDependant, node.info, node)
+
+proc move(ctx: NilCheckerContext, map: NilMap, target: PNode, assigned: PNode) =
+  #echo "move ", target, " ", assigned
+  var targetIndex = ctx.index(target)
+  var assignedIndex: ExprIndex
+  var targetSetIndex = map.setIndices[targetIndex]
+  var assignedSetIndex: SetIndex
+  if assigned.kind == nkSym:
+    assignedIndex = ctx.index(assigned)
+    assignedSetIndex = map.setIndices[assignedIndex]
+  else:
+    assignedIndex = noExprIndex
+    assignedSetIndex = noSetIndex
+  if assignedIndex == noExprIndex:
+    moveOut(ctx, map, target)
+  elif targetSetIndex != assignedSetIndex:
+    map.sets[targetSetIndex].excl(targetIndex.int)
+    map.sets[assignedSetIndex].incl(targetIndex.int)
+    map.setIndices[targetIndex] = assignedSetIndex
+
+# proc hasKey(map: NilMap, ): bool =
+#   var now = map
+#   result = false
+#   while not now.isNil:
+#     if now.locals.hasKey(graphIndex):
+#       return true
+#     now = now.previous
+
+iterator pairs(map: NilMap): (ExprIndex, Nilability) =
+  for index, value in map.expressions:
+    yield (index, map[index])
+
+proc copyMap(map: NilMap): NilMap =
+  if map.isNil:
+    return nil
+  result = newNilMap(map.parent) # no need for copy? if we change only this
+  result.expressions = map.expressions
+  result.history = map.history
+  result.sets = map.sets
+  result.setIndices = map.setIndices
+
+using
+  n: PNode
+  conf: ConfigRef
+  ctx: NilCheckerContext
+  map: NilMap
+
+proc typeNilability(typ: PType): Nilability
+
+# maybe: if canRaise, return MaybeNil ?
+# no, because the target might be safe already
+# with or without an exception
+proc checkCall(n, ctx, map): Check =
+  # checks each call
+  # special case for new(T) -> result is always Safe
+  # for the others it depends on the return type of the call
+  # check args and handle possible mutations
+
+  var isNew = false
+  result = Check(map: map)
+  for i, child in n:
+    discard check(child, ctx, map)
+
+    if i > 0:
+      # var args make a new map with MaybeNil for our node
+      # as it might have been mutated
+      # TODO similar for normal refs and fields: find dependent exprs: brackets
+
+      if child.kind == nkHiddenAddr and not child.typ.isNil and child.typ.kind == tyVar and child.typ.elementType.kind == tyRef:
+        if not isNew:
+          result.map = newNilMap(map)
+          isNew = true
+        # result.map[$child] = MaybeNil
+        var arg = child
+        while arg.kind == nkHiddenAddr:
+          arg = arg[0]
+        let a = ctx.index(arg)
+        if a != noExprIndex:
+          moveOut(ctx, result.map, arg)
+          moveOutDependants(ctx, result.map, arg)
+          result.map.store(ctx, a, MaybeNil, TVarArg, n.info, arg)
+          storeDependants(ctx, result.map, arg, MaybeNil)
+      elif not child.typ.isNil and child.typ.kind == tyRef:
+        if child.kind in {nkSym, nkDotExpr} or isConstBracket(child):
+          let a = ctx.index(child)
+          if ctx.dependants[a].len > 0:
+            if not isNew:
+              result.map = newNilMap(map)
+              isNew = true
+            moveOutDependants(ctx, result.map, child)
+            storeDependants(ctx, result.map, child, MaybeNil)
+
+  if n[0].kind == nkSym and n[0].sym.magic == mNew:
+    # new hidden deref?
+    var value = if n[1].kind == nkHiddenDeref: n[1][0] else: n[1]
+    let b = ctx.index(value)
+    result.map.store(ctx, b, Safe, TAssign, value.info, value)
+    result.nilability = Safe
+  else:
+    # echo "n ", n, " ", n.typ.isNil
+    if not n.typ.isNil:
+      result.nilability = typeNilability(n.typ)
+    else:
+      result.nilability = Safe
+  # echo result.map
+
+template event(b: History): string =
+  case b.kind:
+  of TArg: "param with nilable type"
+  of TNil: "it returns true for isNil"
+  of TAssign: "assigns a value which might be nil"
+  of TVarArg: "passes it as a var arg which might change to nil"
+  of TResult: "it is nil by default"
+  of TType: "it has ref type"
+  of TSafe: "it is safe here as it returns false for isNil"
+  of TPotentialAlias: "it might be changed directly or through an alias"
+  of TDependant: "it might be changed because its base might be changed"
+
+proc derefWarning(n, ctx, map; kind: Nilability) =
+  ## a warning for potentially unsafe dereference
+  if n.info in ctx.warningLocations:
+    return
+  ctx.warningLocations.incl(n.info)
+  var a: seq[History] = @[]
+  if n.kind == nkSym:
+    a = history(map, ctx.index(n))
+  var res = ""
+  var issue = case kind:
+      of Nil: "it is nil"
+      of MaybeNil: "it might be nil"
+      of Unreachable: "it is unreachable"
+      else: ""
+  res.add("can't deref " & $n & ", " & issue)
+  if a.len > 0:
+    res.add("\n")
+  for b in a:
+    res.add("  " & event(b) & " on line " & $b.info.line & ":" & $b.info.col)
+  message(ctx.config, n.info, warnStrictNotNil, res)
+
+proc handleNilability(check: Check; n, ctx, map) =
+  ## handle the check:
+  ##   register a warning(error?) for Nil/MaybeNil
+  case check.nilability:
+  of Nil:
+    derefWarning(n, ctx, map, Nil)
+  of MaybeNil:
+    derefWarning(n, ctx, map, MaybeNil)
+  of Unreachable:
+    derefWarning(n, ctx, map, Unreachable)
+  else:
+    when defined(nilDebugInfo):
+      message(ctx.config, n.info, hintUser, "can deref " & $n)
+
+proc checkDeref(n, ctx, map): Check =
+  ## check dereference: deref n should be ok only if n is Safe
+  result = check(n[0], ctx, map)
+
+  handleNilability(result, n[0], ctx, map)
+
+
+proc checkRefExpr(n, ctx; check: Check): Check =
+  ## check ref expressions: TODO not sure when this happens
+  result = check
+  if n.typ.kind != tyRef:
+    result.nilability = typeNilability(n.typ)
+  elif tfNotNil notin n.typ.flags:
+    # echo "ref key ", n, " ", n.kind
+    if n.kind in {nkSym, nkDotExpr} or isConstBracket(n):
+      let key = ctx.index(n)
+      result.nilability = result.map[key]
+    elif n.kind == nkBracketExpr:
+      # sometimes false positive
+      result.nilability = MaybeNil
+    else:
+      # sometimes maybe false positive
+      result.nilability = MaybeNil
+
+proc checkDotExpr(n, ctx, map): Check =
+  ## check dot expressions: make sure we can dereference the base
+  result = check(n[0], ctx, map)
+  result = checkRefExpr(n, ctx, result)
+
+proc checkBracketExpr(n, ctx, map): Check =
+  ## check bracket expressions: make sure we can dereference the base
+  result = check(n[0], ctx, map)
+  # if might be deref: [] == *(a + index) for cstring
+  handleNilability(result, n[0], ctx, map)
+  result = check(n[1], ctx, result.map)
+  result = checkRefExpr(n, ctx, result)
+  # echo n, " ", result.nilability
+
+
+template union(l: Nilability, r: Nilability): Nilability =
+  ## unify two states
+  if l == r:
+    l
+  else:
+    MaybeNil
+
+template add(l: Nilability, r: Nilability): Nilability =
+  if l == r: # Safe Safe -> Safe etc
+    l
+  elif l == Parent: # Parent Safe -> Safe etc
+    r
+  elif r == Parent:  # Safe Parent -> Safe etc
+    l
+  elif l == Unreachable or r == Unreachable: # Safe Unreachable -> Unreachable etc
+    Unreachable
+  elif l == MaybeNil: # Safe MaybeNil -> Safe etc
+    r
+  elif r == MaybeNil: # MaybeNil Nil -> Nil etc
+    l
+  else: # Safe Nil -> Unreachable etc
+    Unreachable
+
+proc findCommonParent(l: NilMap, r: NilMap): NilMap =
+  result = l.parent
+  while not result.isNil:
+    var rparent = r.parent
+    while not rparent.isNil:
+      if result == rparent:
+        return result
+      rparent = rparent.parent
+    result = result.parent
+
+proc union(ctx: NilCheckerContext, l: NilMap, r: NilMap): NilMap =
+  ## unify two maps from different branches
+  ## combine their locals
+  ## what if they are from different parts of the same tree
+  ## e.g.
+  ## a -> b -> c
+  ##   -> b1
+  ## common then?
+  ##
+  if l.isNil:
+    return r
+  elif r.isNil:
+    return l
+
+  let common = findCommonParent(l, r)
+  result = newNilMap(common, ctx.expressions.len.int)
+
+  for index, value in l:
+    let h = history(r, index)
+    let info = if h.len > 0: h[^1].info else: TLineInfo(line: 0) # assert h.len > 0
+    # echo "history", name, value, r[name], h[^1].info.line
+    result.store(ctx, index, union(value, r[index]), TAssign, info)
+
+proc add(ctx: NilCheckerContext, l: NilMap, r: NilMap): NilMap =
+  #echo "add "
+  #echo namedMapDebugInfo(ctx, l)
+  #echo " : "
+  #echo namedMapDebugInfo(ctx, r)
+  if l.isNil:
+    return r
+  elif r.isNil:
+    return l
+
+  let common = findCommonParent(l, r)
+  result = newNilMap(common, ctx.expressions.len.int)
+
+  for index, value in l:
+    let h = history(r, index)
+    let info = if h.len > 0: h[^1].info else: TLineInfo(line: 0)
+    # TODO: refactor and also think: is TAssign a good one
+    result.store(ctx, index, add(value, r[index]), TAssign, info)
+
+  #echo "result"
+  #echo namedMapDebugInfo(ctx, result)
+  #echo ""
+  #echo ""
+
+
+proc checkAsgn(target: PNode, assigned: PNode; ctx, map): Check =
+  ## check assignment
+  ##   update map based on `assigned`
+  if assigned.kind != nkEmpty:
+    result = check(assigned, ctx, map)
+  else:
+    result = Check(nilability: typeNilability(target.typ), map: map)
+
+  # we need to visit and check those, but we don't use the result for now
+  # is it possible to somehow have another event happen here?
+  discard check(target, ctx, map)
+
+  if result.map.isNil:
+    result.map = map
+  if target.kind in {nkSym, nkDotExpr} or isConstBracket(target):
+    let t = ctx.index(target)
+    move(ctx, map, target, assigned)
+    case assigned.kind:
+    of nkNilLit:
+      result.map.store(ctx, t, Nil, TAssign, target.info, target)
+    else:
+      result.map.store(ctx, t, result.nilability, TAssign, target.info, target)
+      moveOutDependants(ctx, map, target)
+      storeDependants(ctx, map, target, MaybeNil)
+      if assigned.kind in {nkObjConstr, nkTupleConstr}:
+        for (element, value) in result.elements:
+          var elementNode = nkDotExpr.newTree(nkHiddenDeref.newTree(target), element)
+          if symbol(elementNode) in ctx.symbolIndices:
+            var elementIndex = ctx.index(elementNode)
+            result.map.store(ctx, elementIndex, value, TAssign, target.info, elementNode)
+
+
+proc checkReturn(n, ctx, map): Check =
+  ## check return
+  # return n same as result = n; return ?
+  result = check(n[0], ctx, map)
+  result.map.store(ctx, resultExprIndex, result.nilability, TAssign, n.info)
+
+
+proc checkIf(n, ctx, map): Check =
+  ## check branches based on condition
+  result = default(Check)
+  var mapIf: NilMap = map
+
+  # first visit the condition
+
+  # the structure is not If(Elif(Elif, Else), Else)
+  # it is
+  # If(Elif, Elif, Else)
+
+  var mapCondition = checkCondition(n.sons[0].sons[0], ctx, mapIf, false, true)
+
+  # the state of the conditions: negating conditions before the current one
+  var layerHistory = newNilMap(mapIf)
+  # the state after branch effects
+  var afterLayer: NilMap = nil
+  # the result nilability for expressions
+  var nilability = Safe
+
+  for branch in n.sons:
+    var branchConditionLayer = newNilMap(layerHistory)
+    var branchLayer: NilMap
+    var code: PNode
+    if branch.kind in {nkIfStmt, nkElifBranch}:
+      var mapCondition = checkCondition(branch[0], ctx, branchConditionLayer, false, true)
+      let reverseMapCondition = reverseDirect(mapCondition)
+      layerHistory = ctx.add(layerHistory, reverseMapCondition)
+      branchLayer = mapCondition
+      code = branch[1]
+    else:
+      branchLayer = layerHistory
+      code = branch
+
+    let branchCheck = checkBranch(code, ctx, branchLayer)
+    # handles nil afterLayer -> returns branchCheck.map
+    afterLayer = ctx.union(afterLayer, branchCheck.map)
+    nilability = if n.kind == nkIfStmt: Safe else: union(nilability, branchCheck.nilability)
+  if n.sons.len > 1:
+    result.map = afterLayer
+    result.nilability = nilability
+  else:
+    if not hasUnstructuredControlFlowJump(n[0][1]):
+      # here it matters what happend inside, because
+      # we might continue in the parent branch after entering this one
+      # either we enter the branch, so we get mapIf and effect of branch -> afterLayer
+      # or we dont , so we get mapIf and (not condition) effect -> layerHistory
+      result.map = ctx.union(layerHistory, afterLayer)
+      result.nilability = Safe # no expr?
+    else:
+      # similar to else: because otherwise we are jumping out of
+      # the branch, so no union with the mapIf (we dont continue if the condition was true)
+      # here it also doesn't matter for the parent branch what happened in the branch, e.g. assigning to nil
+      # as if we continue there, we haven't entered the branch probably
+      # so we don't do an union with afterLayer
+      # layerHistory has the effect of mapIf and (not condition)
+      result.map = layerHistory
+      result.nilability = Safe
+
+proc checkFor(n, ctx, map): Check =
+  ## check for loops
+  ##   try to repeat the unification of the code twice
+  ##   to detect what can change after a several iterations
+  ##   approach based on discussions with Zahary/Araq
+  ##   similar approach used for other loops
+  var m = map.copyMap()
+  var map0 = map.copyMap()
+  #echo namedMapDebugInfo(ctx, map)
+  m = check(n.sons[2], ctx, map).map.copyMap()
+  if n[0].kind == nkSym:
+    m.store(ctx, ctx.index(n[0]), typeNilability(n[0].typ), TAssign, n[0].info)
+  # echo namedMapDebugInfo(ctx, map)
+  var check2 = check(n.sons[2], ctx, m)
+  var map2 = check2.map
+
+  result = Check(map: ctx.union(map0, m))
+  result.map = ctx.union(result.map, map2)
+  result.nilability = Safe
+
+# check:
+# while code:
+#   code2
+
+# if code:
+#   code2
+# if code:
+#   code2
+
+# if code:
+#   code2
+
+# check(code), check(code2 in code's map)
+
+proc checkWhile(n, ctx, map): Check =
+  ## check while loops
+  ##   try to repeat the unification of the code twice
+  var m = checkCondition(n[0], ctx, map, false, false)
+  var map0 = map.copyMap()
+  m = check(n.sons[1], ctx, m).map
+  var map1 = m.copyMap()
+  var check2 = check(n.sons[1], ctx, m)
+  var map2 = check2.map
+
+  result = Check(map: ctx.union(map0, map1))
+  result.map = ctx.union(result.map, map2)
+  result.nilability = Safe
+
+proc checkInfix(n, ctx, map): Check =
+  ## check infix operators in condition
+  ##   a and b : map is based on a; next b
+  ##   a or b : map is an union of a and b's
+  ##   a == b : use checkCondition
+  ##   else: no change, just check args
+  result = default(Check)
+  if n[0].kind == nkSym:
+    var mapL: NilMap = nil
+    var mapR: NilMap = nil
+    if n[0].sym.magic notin {mAnd, mEqRef}:
+      mapL = checkCondition(n[1], ctx, map, false, false)
+      mapR = checkCondition(n[2], ctx, map, false, false)
+    case n[0].sym.magic:
+    of mOr:
+      result.map = ctx.union(mapL, mapR)
+    of mAnd:
+      result.map = checkCondition(n[1], ctx, map, false, false)
+      result.map = checkCondition(n[2], ctx, result.map, false, false)
+    of mEqRef:
+      if n[2].kind == nkIntLit:
+        if $n[2] == "true":
+          result.map = checkCondition(n[1], ctx, map, false, false)
+        elif $n[2] == "false":
+          result.map = checkCondition(n[1], ctx, map, true, false)
+      elif n[1].kind == nkIntLit:
+        if $n[1] == "true":
+          result.map = checkCondition(n[2], ctx, map, false, false)
+        elif $n[1] == "false":
+          result.map = checkCondition(n[2], ctx, map, true, false)
+
+      if result.map.isNil:
+        result.map = map
+    else:
+      result.map = map
+  else:
+    result.map = map
+  result.nilability = Safe
+
+proc checkIsNil(n, ctx, map; isElse: bool = false): Check =
+  ## check isNil calls
+  ## update the map depending on if it is not isNil or isNil
+  result = Check(map: newNilMap(map))
+  let value = n[1]
+  result.map.store(ctx, ctx.index(n[1]), if not isElse: Nil else: Safe, TArg, n.info, n)
+
+proc infix(ctx: NilCheckerContext, l: PNode, r: PNode, magic: TMagic): PNode =
+  var name = case magic:
+    of mEqRef: "=="
+    of mAnd: "and"
+    of mOr: "or"
+    else: ""
+
+  var cache = newIdentCache()
+  var op = newSym(skVar, cache.getIdent(name), ctx.idgen, nil, r.info)
+
+  op.magic = magic
+  result = nkInfix.newTree(
+    newSymNode(op, r.info),
+    l,
+    r)
+  result.typ = newType(tyBool, ctx.idgen, nil)
+
+proc prefixNot(ctx: NilCheckerContext, node: PNode): PNode =
+  var cache = newIdentCache()
+  var op = newSym(skVar, cache.getIdent("not"), ctx.idgen, nil, node.info)
+
+  op.magic = mNot
+  result = nkPrefix.newTree(
+    newSymNode(op, node.info),
+    node)
+  result.typ = newType(tyBool, ctx.idgen, nil)
+
+proc infixEq(ctx: NilCheckerContext, l: PNode, r: PNode): PNode =
+  infix(ctx, l, r, mEqRef)
+
+proc infixOr(ctx: NilCheckerContext, l: PNode, r: PNode): PNode =
+  infix(ctx, l, r, mOr)
+
+proc checkCase(n, ctx, map): Check =
+  # case a:
+  #   of b: c
+  #   of b2: c2
+  # is like
+  # if a == b:
+  #   c
+  # elif a == b2:
+  #   c2
+  # also a == true is a , a == false is not a
+  let base = n[0]
+  result = Check(map: map.copyMap())
+  result.nilability = Safe
+  var a: PNode = nil
+  for child in n:
+    case child.kind:
+    of nkOfBranch:
+      if child.len < 2:
+        # echo "case with of with < 2 ", n
+        continue # TODO why does this happen
+      let branchBase = child[0] # TODO a, b or a, b..c etc
+      let code = child[^1]
+      let test = infixEq(ctx, base, branchBase)
+      if a.isNil:
+        a = test
+      else:
+        a = infixOr(ctx, a, test)
+      let conditionMap = checkCondition(test, ctx, map.copyMap(), false, false)
+      let newCheck = checkBranch(code, ctx, conditionMap)
+      result.map = ctx.union(result.map, newCheck.map)
+      result.nilability = union(result.nilability, newCheck.nilability)
+    of nkElifBranch:
+      discard "TODO: maybe adapt to be similar to checkIf"
+    of nkElse:
+      let mapElse = checkCondition(prefixNot(ctx, a), ctx, map.copyMap(), false, false)
+      let newCheck = checkBranch(child[0], ctx, mapElse)
+      result.map = ctx.union(result.map, newCheck.map)
+      result.nilability = union(result.nilability, newCheck.nilability)
+    else:
+      discard
+
+# notes
+# try:
+#   a
+#   b
+# except:
+#   c
+# finally:
+#   d
+#
+# if a doesnt raise, this is not an exit point:
+#   so find what raises and update the map with that
+# (a, b); c; d
+# if nothing raises, except shouldn't happen
+# .. might be a false positive tho, if canRaise is not conservative?
+# so don't visit it
+#
+# nested nodes can raise as well: I hope nim returns canRaise for
+# their parents
+#
+# a lot of stuff can raise
+proc checkTry(n, ctx, map): Check =
+  var newMap = map.copyMap()
+  var currentMap = map
+  # we don't analyze except if nothing canRaise in try
+  var canRaise = false
+  var hasFinally = false
+  # var tryNodes: seq[PNode]
+  # if n[0].kind == nkStmtList:
+  #   tryNodes = toSeq(n[0])
+  # else:
+  #   tryNodes = @[n[0]]
+  # for i, child in tryNodes:
+  #   let (childNilability, childMap) = check(child, conf, currentMap)
+  #   echo childMap
+  #   currentMap = childMap
+  #   # TODO what about nested
+  #   if child.canRaise:
+  #     newMap = union(newMap, childMap)
+  #     canRaise = true
+  #   else:
+  #     newMap = childMap
+  let tryCheck = check(n[0], ctx, currentMap)
+  newMap = ctx.union(currentMap, tryCheck.map)
+  canRaise = n[0].canRaise
+
+  var afterTryMap = newMap
+  for a, branch in n:
+    if a > 0:
+      case branch.kind:
+      of nkFinally:
+        newMap = ctx.union(afterTryMap, newMap)
+        let childCheck = check(branch[0], ctx, newMap)
+        newMap = ctx.union(newMap, childCheck.map)
+        hasFinally = true
+      of nkExceptBranch:
+        if canRaise:
+          let childCheck = check(branch[^1], ctx, newMap)
+          newMap = ctx.union(newMap, childCheck.map)
+      else:
+        discard
+  if not hasFinally:
+    # we might have not hit the except branches
+    newMap = ctx.union(afterTryMap, newMap)
+  result = Check(nilability: Safe, map: newMap)
+
+proc hasUnstructuredControlFlowJump(n: PNode): bool =
+  ## if the node contains a direct stop
+  ## as a continue/break/raise/return: then it means
+  ## we should reverse some of the map in the code after the condition
+  ## similar to else
+  # echo "n ", n, " ", n.kind
+  case n.kind:
+  of nkStmtList:
+    for child in n:
+      if hasUnstructuredControlFlowJump(child):
+        return true
+  of nkReturnStmt, nkBreakStmt, nkContinueStmt, nkRaiseStmt:
+    return true
+  of nkIfStmt, nkIfExpr, nkElifExpr, nkElse:
+    return false
+  else:
+    discard
+  return false
+
+proc reverse(value: Nilability): Nilability =
+  case value:
+  of Nil: Safe
+  of MaybeNil: MaybeNil
+  of Safe: Nil
+  of Parent: Parent
+  of Unreachable: Unreachable
+
+proc reverse(kind: TransitionKind): TransitionKind =
+  case kind:
+  of TNil: TSafe
+  of TSafe: TNil
+  of TPotentialAlias: TPotentialAlias
+  else:
+    kind
+    # raise newException(ValueError, "expected TNil or TSafe")
+
+proc reverseDirect(map: NilMap): NilMap =
+  # we create a new layer
+  # reverse the values only in this layer:
+  # because conditions should've stored their changes there
+  # b: Safe (not b.isNil)
+  # b: Parent Parent
+  # b: Nil (b.isNil)
+
+  # layer block
+  # [ Parent ] [ Parent ]
+  #   if -> if state
+  #   layer -> reverse
+  #   older older0 new
+  #   older new
+  #  [ b Nil ] [ Parent ]
+  #  elif
+  #  [ b Nil, c Nil] [ Parent ]
+  #
+
+  # if b.isNil:
+  #   # [ b Safe]
+  #   c = A() # Safe
+  # elif not b.isNil:
+  #   # [ b Safe ] + [b Nil] MaybeNil Unreachable
+  #   # Unreachable defer can't deref b, it is unreachable
+  #   discard
+  # else:
+  #   b
+
+
+#  if
+
+
+
+  # if: we just pass the map with a new layer for its block
+  # elif: we just pass the original map but with a new layer is the reverse of the previous popped layer (?)
+  # elif:
+  # else: we just pass the original map but with a new layer which is initialized as the reverse of the
+  #   top layer of else
+  # else:
+  #
+  # [ b MaybeNil ] [b Parent] [b Parent] [b Safe] [b Nil] []
+  # Safe
+  # c == 1
+  # b Parent
+  # c == 2
+  # b Parent
+  # not b.isNil
+  # b Safe
+  # c == 3
+  # b Nil
+  # (else)
+  # b Nil
+
+  result = map.copyMap()
+  for index, value in result.expressions:
+    result.expressions[index] = reverse(value)
+    if result.history[index].len > 0:
+      result.history[index][^1].kind = reverse(result.history[index][^1].kind)
+      result.history[index][^1].nilability = result.expressions[index]
+
+proc checkCondition(n, ctx, map; reverse: bool, base: bool): NilMap =
+  ## check conditions : used for if, some infix operators
+  ##   isNil(a)
+  ##   it returns a new map: you need to reverse all the direct elements for else
+
+  # echo "condition ", n, " ", n.kind
+  if n.kind == nkCall:
+    result = newNilMap(map)
+    for element in n:
+      if element.kind == nkHiddenDeref and n[0].kind == nkSym and n[0].sym.magic == mIsNil:
+        result = check(element[0], ctx, result).map
+      else:
+        result = check(element, ctx, result).map
+
+    if n[0].kind == nkSym and n[0].sym.magic == mIsNil:
+      # isNil(arg)
+      var arg = n[1]
+      while arg.kind == nkHiddenDeref:
+        arg = arg[0]
+      if arg.kind in {nkSym, nkDotExpr} or isConstBracket(arg):
+        let a = ctx.index(arg)
+        result.store(ctx, a, if not reverse: Nil else: Safe, if not reverse: TNil else: TSafe, n.info, arg)
+      else:
+        discard
+    else:
+      discard
+  elif n.kind == nkPrefix and n[0].kind == nkSym and n[0].sym.magic == mNot:
+    result = checkCondition(n[1], ctx, map, not reverse, false)
+  elif n.kind == nkInfix:
+    result = newNilMap(map)
+    result = checkInfix(n, ctx, result).map
+  else:
+    result = check(n, ctx, map).map
+    result = newNilMap(map)
+  assert not result.isNil
+  assert not result.parent.isNil
+
+proc checkResult(n, ctx, map) =
+  let resultNilability = map[resultExprIndex]
+  case resultNilability:
+  of Nil:
+    message(ctx.config, n.info, warnStrictNotNil, "return value is nil")
+  of MaybeNil:
+    message(ctx.config, n.info, warnStrictNotNil, "return value might be nil")
+  of Unreachable:
+    message(ctx.config, n.info, warnStrictNotNil, "return value is unreachable")
+  of Safe, Parent:
+    discard
+
+proc checkBranch(n: PNode, ctx: NilCheckerContext, map: NilMap): Check =
+  result = check(n, ctx, map)
+
+
+# Faith!
+
+proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check =
+  assert not map.isNil
+
+  # echo "check n ", n, " ", n.kind
+  # echo "map ", namedMapDebugInfo(ctx, map)
+  case n.kind:
+  of nkSym:
+    result = Check(nilability: map[ctx.index(n)], map: map)
+  of nkCallKinds:
+    if n.sons[0].kind == nkSym:
+      let callSym = n.sons[0].sym
+      case callSym.magic:
+      of mAnd, mOr:
+        result = checkInfix(n, ctx, map)
+      of mIsNil:
+        result = checkIsNil(n, ctx, map)
+      else:
+        result = checkCall(n, ctx, map)
+    else:
+      result = checkCall(n, ctx, map)
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkExprColonExpr, nkExprEqExpr,
+     nkCast:
+    result = check(n.sons[1], ctx, map)
+  of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange,
+     nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr, nkElse:
+    result = Check(map: map)
+    if n.kind in {nkObjConstr, nkTupleConstr}:
+      # TODO deeper nested elements?
+      # A(field: B()) #
+      # field: Safe ->
+      var elements: seq[(PNode, Nilability)] = @[]
+      for i, child in n:
+        result = check(child, ctx, result.map)
+        if i > 0:
+          if child.kind == nkExprColonExpr:
+            elements.add((child[0], result.nilability))
+      result.elements = elements
+      result.nilability = Safe
+    else:
+      for child in n:
+        result = check(child, ctx, result.map)
+
+  of nkDotExpr:
+    result = checkDotExpr(n, ctx, map)
+  of nkDerefExpr, nkHiddenDeref:
+    result = checkDeref(n, ctx, map)
+  of nkAddr, nkHiddenAddr:
+    result = check(n.sons[0], ctx, map)
+  of nkIfStmt, nkIfExpr:
+    result = checkIf(n, ctx, map)
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
+    result = checkAsgn(n[0], n[1], ctx, map)
+  of nkVarSection, nkLetSection:
+    result = Check(map: map)
+    for child in n:
+      result = checkAsgn(child[0].skipPragmaExpr, child[2], ctx, result.map)
+  of nkForStmt:
+    result = checkFor(n, ctx, map)
+  of nkCaseStmt:
+    result = checkCase(n, ctx, map)
+  of nkReturnStmt:
+    result = checkReturn(n, ctx, map)
+  of nkBracketExpr:
+    result = checkBracketExpr(n, ctx, map)
+  of nkTryStmt:
+    result = checkTry(n, ctx, map)
+  of nkWhileStmt:
+    result = checkWhile(n, ctx, map)
+  of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
+      nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
+      nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
+      nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
+      nkTypeOfExpr, nkMixinStmt, nkBindStmt:
+
+    discard "don't follow this : same as varpartitions"
+    result = Check(nilability: Nil, map: map)
+  else:
+
+    var elementMap = map.copyMap()
+    var elementCheck = Check(map: elementMap)
+    for element in n:
+      elementCheck = check(element, ctx, elementCheck.map)
+
+    result = Check(nilability: Nil, map: elementCheck.map)
+
+
+
+
+proc typeNilability(typ: PType): Nilability =
+  assert not typ.isNil
+  # echo "typeNilability ", $typ.flags, " ", $typ.kind
+  result = if tfNotNil in typ.flags:
+    Safe
+  elif typ.kind in {tyRef, tyCstring, tyPtr, tyPointer}:
+    #
+    # tyVar ? tyVarargs ? tySink ? tyLent ?
+    # TODO spec? tests?
+    MaybeNil
+  else:
+    Safe
+  # echo "  result ", result
+
+proc preVisitNode(ctx: NilCheckerContext, node: PNode, conf: ConfigRef) =
+  # echo "visit node ", node
+  if node.kind in {nkSym, nkDotExpr} or isConstBracket(node):
+    let nodeSymbol = symbol(node)
+    if not ctx.symbolIndices.hasKey(nodeSymbol):
+      ctx.symbolIndices[nodeSymbol] = ctx.expressions.len
+      ctx.expressions.add(node)
+    if node.kind in {nkDotExpr, nkBracketExpr}:
+      if node.kind == nkDotExpr and (not node.typ.isNil and node.typ.kind == tyRef and tfNotNil notin node.typ.flags) or
+         node.kind == nkBracketExpr:
+        let index = ctx.symbolIndices[nodeSymbol]
+        var baseIndex = noExprIndex
+        # deref usually?
+        # ok, we hit another case
+        var base = if node[0].kind notin {nkSym, nkIdent}: node[0][0] else: node[0]
+        if base.kind != nkIdent:
+          let baseSymbol = symbol(base)
+          if not ctx.symbolIndices.hasKey(baseSymbol):
+            baseIndex = ctx.expressions.len # next visit should add it
+          else:
+            baseIndex = ctx.symbolIndices[baseSymbol]
+          if ctx.dependants.len <= baseIndex:
+            ctx.dependants.setLen(baseIndex + 1.ExprIndex)
+          ctx.dependants[baseIndex].incl(index.int)
+  case node.kind:
+  of nkSym, nkEmpty, nkNilLit, nkType, nkIdent, nkCharLit .. nkUInt64Lit, nkFloatLit .. nkFloat64Lit, nkStrLit .. nkTripleStrLit:
+    discard
+  of nkDotExpr:
+    # visit only the base
+    ctx.preVisitNode(node[0], conf)
+  else:
+    for element in node:
+      ctx.preVisitNode(element, conf)
+
+proc preVisit(ctx: NilCheckerContext, s: PSym, body: PNode, conf: ConfigRef) =
+  ctx.symbolIndices = {resultId: resultExprIndex}.toTable()
+  var cache = newIdentCache()
+  ctx.expressions = SeqOfDistinct[ExprIndex, PNode](@[newIdentNode(cache.getIdent("result"), s.ast.info)])
+  var emptySet: IntSet = initIntSet() # set[ExprIndex]
+  ctx.dependants = SeqOfDistinct[ExprIndex, IntSet](@[emptySet])
+  for i, arg in s.typ.n.sons:
+    if i > 0:
+      if arg.kind != nkSym:
+        continue
+      let argSymbol = symbol(arg)
+      if not ctx.symbolIndices.hasKey(argSymbol):
+        ctx.symbolIndices[argSymbol] = ctx.expressions.len
+        ctx.expressions.add(arg)
+  ctx.preVisitNode(body, conf)
+  if ctx.dependants.len < ctx.expressions.len:
+    ctx.dependants.setLen(ctx.expressions.len)
+  # echo ctx.symbolIndices
+  # echo ctx.expressions
+  # echo ctx.dependants
+
+proc checkNil*(s: PSym; body: PNode; conf: ConfigRef, idgen: IdGenerator) =
+  let line = s.ast.info.line
+  let fileIndex = s.ast.info.fileIndex.int
+  var filename = conf.m.fileInfos[fileIndex].fullPath.string
+
+  var context = NilCheckerContext(config: conf, idgen: idgen)
+  context.preVisit(s, body, conf)
+  var map = newNilMap(nil, context.symbolIndices.len)
+
+  for i, child in s.typ.n.sons:
+    if i > 0:
+      if child.kind != nkSym:
+        continue
+      map.store(context, context.index(child), typeNilability(child.typ), TArg, child.info, child)
+
+  map.store(context, resultExprIndex, if not s.typ.returnType.isNil and s.typ.returnType.kind == tyRef: Nil else: Safe, TResult, s.ast.info)
+
+  # echo "checking ", s.name.s, " ", filename
+
+  let res = check(body, context, map)
+  var canCheck = resultExprIndex in res.map.history.low .. res.map.history.high
+  if res.nilability == Safe and canCheck and res.map.history[resultExprIndex].len <= 1:
+    res.map.store(context, resultExprIndex, Safe, TAssign, s.ast.info)
+  else:
+    if res.nilability == Safe:
+      res.map.store(context, resultExprIndex, Safe, TAssign, s.ast.info)
+
+  # TODO check for nilability result
+  # (ANotNil, BNotNil) :
+  # do we check on asgn nilability at all?
+
+  if not s.typ.returnType.isNil and s.typ.returnType.kind == tyRef and tfNotNil in s.typ.returnType.flags:
+    checkResult(s.ast, context, res.map)
diff --git a/compiler/nim.cfg b/compiler/nim.cfg
index 1bd3fbfd6..ce5a22ad2 100644
--- a/compiler/nim.cfg
+++ b/compiler/nim.cfg
@@ -1,12 +1,17 @@
 # Special configuration file for the Nim project
 
 hint[XDeclaredButNotUsed]:off
-path:"llvm"
-path:"$projectPath/.."
 
 define:booting
 define:nimcore
-#define:nimIncremental
+define:nimPreviewFloatRoundtrip
+define:nimPreviewSlimSystem
+define:nimPreviewCstringConversion
+define:nimPreviewProcConversion
+define:nimPreviewRangeDefault
+define:nimPreviewNonVarDestructor
+threads:off
+
 #import:"$projectpath/testability"
 
 @if windows:
@@ -15,5 +20,44 @@ define:nimcore
 
 define:useStdoutAsStdmsg
 
+@if nimHasStyleChecks:
+  styleCheck:error
+@end
+
+
 #define:useNodeIds
 #gc:markAndSweep
+
+@if nimHasWarningObservableStores:
+  warning[ObservableStores]:off
+@end
+
+
+@if nimHasWarningAsError:
+  warningAsError[GcUnsafe2]:on
+@end
+
+@if nimHasWarnUnnamedBreak:
+  warningAserror[UnnamedBreak]:on
+@end
+
+@if nimHasWarnBareExcept:
+  warning[BareExcept]:on
+  warningAserror[BareExcept]:on
+@end
+
+
+@if nimUseStrictDefs:
+  experimental:strictDefs
+  warningAsError[Uninit]:on
+  warningAsError[ProveInit]:on
+@end
+
+@if nimHasWarnStdPrefix:
+  warning[StdPrefix]:on
+  warningAsError[StdPrefix]:on
+@end
+
+@if nimHasVtables:
+  experimental:vtables
+@end
diff --git a/compiler/nim.nim b/compiler/nim.nim
index 90049bdfb..005f11a58 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -7,21 +7,29 @@
 #    distribution, for details about the copyright.
 #
 
-when defined(gcc) and defined(windows):
-  when defined(x86):
-    {.link: "icons/nim.res".}
-  else:
-    {.link: "icons/nim_icon.o".}
+import std/[os, strutils, parseopt]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
-when defined(amd64) and defined(windows) and defined(vcc):
-  {.link: "icons/nim-amd64-windows-vcc.res".}
-when defined(i386) and defined(windows) and defined(vcc):
-  {.link: "icons/nim-i386-windows-vcc.res".}
+when defined(windows):
+  when defined(gcc):
+    when defined(x86):
+      {.link: "../icons/nim.res".}
+    else:
+      {.link: "../icons/nim_icon.o".}
+
+  when defined(amd64) and defined(vcc):
+    {.link: "../icons/nim-amd64-windows-vcc.res".}
+  when defined(i386) and defined(vcc):
+    {.link: "../icons/nim-i386-windows-vcc.res".}
 
 import
-  commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes,
-  extccomp, strutils, os, osproc, platform, main, parseopt,
-  nodejs, scriptconfig, idents, modulegraphs, lineinfos
+  commands, options, msgs, extccomp, main, idents, lineinfos, cmdlinehelper,
+  pathutils, modulegraphs
+
+from std/browsers import openDefaultBrowser
+from nodejs import findNodeJs
 
 when hasTinyCBackend:
   import tccgen
@@ -30,101 +38,129 @@ when defined(profiler) or defined(memProfiler):
   {.hint: "Profiling support is turned on!".}
   import nimprof
 
-proc prependCurDir(f: string): string =
-  when defined(unix):
-    if os.isAbsolute(f): result = f
-    else: result = "./" & f
-  else:
-    result = f
+proc nimbleLockExists(config: ConfigRef): bool =
+  const nimbleLock = "nimble.lock"
+  let pd = if not config.projectPath.isEmpty: config.projectPath else: AbsoluteDir(getCurrentDir())
+  if optSkipParentConfigFiles notin config.globalOptions:
+    for dir in parentDirs(pd.string, fromRoot=true, inclusive=false):
+      if fileExists(dir / nimbleLock):
+        return true
+  return fileExists(pd.string / nimbleLock)
 
 proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
   var p = parseopt.initOptParser(cmd)
   var argsCount = 0
+
+  config.commandLine.setLen 0
+    # bugfix: otherwise, config.commandLine ends up duplicated
+
   while true:
     parseopt.next(p)
     case p.kind
     of cmdEnd: break
-    of cmdLongoption, cmdShortOption:
-      if p.key == " ":
+    of cmdLongOption, cmdShortOption:
+      config.commandLine.add " "
+      config.commandLine.addCmdPrefix p.kind
+      config.commandLine.add p.key.quoteShell # quoteShell to be future proof
+      if p.val.len > 0:
+        config.commandLine.add ':'
+        config.commandLine.add p.val.quoteShell
+
+      if p.key == "": # `-` was passed to indicate main project is stdin
         p.key = "-"
         if processArgument(pass, p, argsCount, config): break
       else:
         processSwitch(pass, p, config)
     of cmdArgument:
+      config.commandLine.add " "
+      config.commandLine.add p.key.quoteShell
       if processArgument(pass, p, argsCount, config): break
   if pass == passCmd2:
-    if optRun notin config.globalOptions and config.arguments.len > 0 and config.command.normalize != "run":
+    if {optRun, optWasNimscript} * config.globalOptions == {} and
+        config.arguments.len > 0 and config.cmd notin {cmdTcc, cmdNimscript, cmdCrun}:
       rawMessage(config, errGenerated, errArgsNeedRunOption)
 
+  if config.nimbleLockExists:
+    # disable nimble path if nimble.lock is present.
+    # see https://github.com/nim-lang/nimble/issues/1004
+    disableNimblePath(config)
+
+proc getNimRunExe(conf: ConfigRef): string =
+  # xxx consider defining `conf.getConfigVar("nimrun.exe")` to allow users to
+  # customize the binary to run the command with, e.g. for custom `nodejs` or `wine`.
+  if conf.isDefined("mingw"):
+    if conf.isDefined("i386"): result = "wine"
+    elif conf.isDefined("amd64"): result = "wine64"
+    else: result = ""
+  else:
+    result = ""
+
 proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
-  condsyms.initDefines(conf.symbols)
+  let self = NimProg(
+    supportsStdinFile: true,
+    processCmdLine: processCmdLine
+  )
+  self.initDefinesProg(conf, "nim_compiler")
   if paramCount() == 0:
-    writeCommandLineUsage(conf, conf.helpWritten)
-  else:
-    # Process command line arguments:
-    processCmdLine(passCmd1, "", conf)
-    if conf.projectName == "-":
-      conf.projectName = "stdinfile"
-      conf.projectFull = "stdinfile"
-      conf.projectPath = canonicalizePath(conf, getCurrentDir())
-      conf.projectIsStdin = true
-    elif conf.projectName != "":
-      try:
-        conf.projectFull = canonicalizePath(conf, conf.projectName)
-      except OSError:
-        conf.projectFull = conf.projectName
-      let p = splitFile(conf.projectFull)
-      let dir = if p.dir.len > 0: p.dir else: getCurrentDir()
-      conf.projectPath = canonicalizePath(conf, dir)
-      conf.projectName = p.name
+    writeCommandLineUsage(conf)
+    return
+
+  self.processCmdLineAndProjectPath(conf)
+
+  var graph = newModuleGraph(cache, conf)
+  if not self.loadConfigsAndProcessCmdLine(cache, conf, graph):
+    return
+
+  if conf.cmd == cmdCheck and optWasNimscript notin conf.globalOptions and
+       conf.backend == backendInvalid:
+    conf.backend = backendC
+
+  if conf.selectedGC == gcUnselected:
+    if conf.backend in {backendC, backendCpp, backendObjc} or
+        (conf.cmd in cmdDocLike and conf.backend != backendJs) or
+        conf.cmd == cmdGendepend:
+      initOrcDefines(conf)
+
+  mainCommand(graph)
+  if conf.hasHint(hintGCStats): echo(GC_getStatistics())
+  #echo(GC_getStatistics())
+  if conf.errorCounter != 0: return
+  when hasTinyCBackend:
+    if conf.cmd == cmdTcc:
+      tccgen.run(conf, conf.arguments)
+  if optRun in conf.globalOptions:
+    let output = conf.absOutFile
+    case conf.cmd
+    of cmdBackends, cmdTcc:
+      let nimRunExe = getNimRunExe(conf)
+      var cmdPrefix = ""
+      if nimRunExe.len > 0: cmdPrefix.add nimRunExe.quoteShell
+      case conf.backend
+      of backendC, backendCpp, backendObjc: discard
+      of backendJs:
+        # D20210217T215950:here this flag is needed for node < v15.0.0, otherwise
+        # tasyncjs_fail` would fail, refs https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode
+        if cmdPrefix.len == 0: cmdPrefix = findNodeJs().quoteShell
+        cmdPrefix.add " --unhandled-rejections=strict"
+      else: raiseAssert $conf.backend
+      if cmdPrefix.len > 0: cmdPrefix.add " "
+        # without the `cmdPrefix.len > 0` check, on windows you'd get a cryptic:
+        # `The parameter is incorrect`
+      let cmd = cmdPrefix & output.quoteShell & ' ' & conf.arguments
+      execExternalProgram(conf, cmd.strip(leading=false,trailing=true))
+    of cmdDocLike, cmdRst2html, cmdRst2tex, cmdMd2html, cmdMd2tex: # bugfix(cmdRst2tex was missing)
+      if conf.arguments.len > 0:
+        # reserved for future use
+        rawMessage(conf, errGenerated, "'$1 cannot handle arguments" % [$conf.cmd])
+      openDefaultBrowser($output)
     else:
-      conf.projectPath = canonicalizePath(conf, getCurrentDir())
-    loadConfigs(DefaultConfig, cache, conf) # load all config files
-    let scriptFile = conf.projectFull.changeFileExt("nims")
-    if fileExists(scriptFile):
-      runNimScript(cache, scriptFile, freshDefines=false, conf)
-      # 'nim foo.nims' means to just run the NimScript file and do nothing more:
-      if scriptFile == conf.projectFull: return
-    elif fileExists(conf.projectPath / "config.nims"):
-      # directory wide NimScript file
-      runNimScript(cache, conf.projectPath / "config.nims", freshDefines=false, conf)
-    # now process command line arguments again, because some options in the
-    # command line can overwite the config file's settings
-    extccomp.initVars(conf)
-    processCmdLine(passCmd2, "", conf)
-    if conf.command == "":
-      rawMessage(conf, errGenerated, "command missing")
-    mainCommand(newModuleGraph(cache, conf))
-    if optHints in conf.options and hintGCStats in conf.notes: echo(GC_getStatistics())
-    #echo(GC_getStatistics())
-    if conf.errorCounter == 0:
-      when hasTinyCBackend:
-        if conf.cmd == cmdRun:
-          tccgen.run(conf.arguments)
-      if optRun in conf.globalOptions:
-        if conf.cmd == cmdCompileToJS:
-          var ex: string
-          if conf.outFile.len > 0:
-            ex = conf.outFile.prependCurDir.quoteShell
-          else:
-            ex = quoteShell(
-              completeCFilePath(conf, changeFileExt(conf.projectFull, "js").prependCurDir))
-          execExternalProgram(conf, findNodeJs() & " " & ex & ' ' & conf.arguments)
-        else:
-          var binPath: string
-          if conf.outFile.len > 0:
-            # If the user specified an outFile path, use that directly.
-            binPath = conf.outFile.prependCurDir
-          else:
-            # Figure out ourselves a valid binary name.
-            binPath = changeFileExt(conf.projectFull, ExeExt).prependCurDir
-          var ex = quoteShell(binPath)
-          execExternalProgram(conf, ex & ' ' & conf.arguments)
+      # support as needed
+      rawMessage(conf, errGenerated, "'$1 cannot handle --run" % [$conf.cmd])
 
 when declared(GC_setMaxPause):
   GC_setMaxPause 2_000
 
-when compileOption("gc", "v2") or compileOption("gc", "refc"):
+when compileOption("gc", "refc"):
   # the new correct mark&sweet collector is too slow :-/
   GC_disableMarkAndSweep()
 
diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim
index 9a23535bf..a5324ea76 100644
--- a/compiler/nimblecmd.nim
+++ b/compiler/nimblecmd.nim
@@ -9,15 +9,22 @@
 
 ## Implements some helper procs for Nimble (Nim's package manager) support.
 
-import parseutils, strutils, strtabs, os, options, msgs, sequtils,
-  lineinfos
+import options, msgs, lineinfos, pathutils
 
-proc addPath*(conf: ConfigRef; path: string, info: TLineInfo) =
+import std/[parseutils, strutils, os, tables, sequtils]
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions]
+
+import ../dist/checksums/src/checksums/sha1
+
+proc addPath*(conf: ConfigRef; path: AbsoluteDir, info: TLineInfo) =
   if not conf.searchPaths.contains(path):
     conf.searchPaths.insert(path, 0)
 
 type
   Version* = distinct string
+  PackageInfo = Table[string, tuple[version, checksum: string]]
 
 proc `$`*(ver: Version): string {.borrow.}
 
@@ -31,11 +38,16 @@ proc isSpecial(ver: Version): bool =
 
 proc isValidVersion(v: string): bool =
   if v.len > 0:
-    if v[0] in {'#'} + Digits: return true
+    if v[0] in {'#'} + Digits:
+      result = true
+    else:
+      result = false
+  else:
+    result = false
 
 proc `<`*(ver: Version, ver2: Version): bool =
   ## This is synced from Nimble's version module.
-
+  result = false
   # Handling for special versions such as "#head" or "#branch".
   if ver.isSpecial or ver2.isSpecial:
     if ver2.isSpecial and ($ver2).normalize == "#head":
@@ -48,7 +60,7 @@ proc `<`*(ver: Version, ver2: Version): bool =
   # Handling for normal versions such as "0.1.0" or "1.0".
   var sVer = string(ver).split('.')
   var sVer2 = string(ver2).split('.')
-  for i in 0..max(sVer.len, sVer2.len)-1:
+  for i in 0..<max(sVer.len, sVer2.len):
     var sVerI = 0
     if i < sVer.len:
       discard parseInt(sVer[i], sVerI)
@@ -62,42 +74,66 @@ proc `<`*(ver: Version, ver2: Version): bool =
     else:
       return false
 
-proc getPathVersion*(p: string): tuple[name, version: string] =
-  ## Splits path ``p`` in the format ``/home/user/.nimble/pkgs/package-0.1``
-  ## into ``(/home/user/.nimble/pkgs/package, 0.1)``
-  result.name = ""
-  result.version = ""
+proc getPathVersionChecksum*(p: string): tuple[name, version, checksum: string] =
+  ## Splits path ``p`` in the format
+  ## ``/home/user/.nimble/pkgs/package-0.1-febadeaea2345e777f0f6f8433f7f0a52edd5d1b`` into
+  ## ``("/home/user/.nimble/pkgs/package", "0.1", "febadeaea2345e777f0f6f8433f7f0a52edd5d1b")``
 
-  const specialSeparator = "-#"
-  var sepIdx = p.find(specialSeparator)
-  if sepIdx == -1:
-    sepIdx = p.rfind('-')
+  result = ("", "", "")
 
-  if sepIdx == -1:
-    result.name = p
-    return
+  const checksumSeparator = '-'
+  const versionSeparator = '-'
+  const specialVersionSepartator = "-#"
+  const separatorNotFound = -1
 
-  for i in sepIdx..<p.len:
-    if p[i] in {DirSep, AltSep}:
-      result.name = p
-      return
+  var checksumSeparatorIndex = p.rfind(checksumSeparator)
+  if checksumSeparatorIndex != separatorNotFound:
+    result.checksum = p.substr(checksumSeparatorIndex + 1)
+    if not result.checksum.isValidSha1Hash():
+      result.checksum = ""
+      checksumSeparatorIndex = p.len()
+  else:
+    checksumSeparatorIndex = p.len()
 
-  result.name = p[0 .. sepIdx - 1]
-  result.version = p.substr(sepIdx + 1)
+  var versionSeparatorIndex = p.rfind(
+    specialVersionSepartator, 0, checksumSeparatorIndex - 1)
+  if versionSeparatorIndex != separatorNotFound:
+    result.version = p.substr(
+      versionSeparatorIndex + 1, checksumSeparatorIndex - 1)
+  else:
+    versionSeparatorIndex = p.rfind(
+      versionSeparator, 0, checksumSeparatorIndex - 1)
+    if versionSeparatorIndex != separatorNotFound:
+      result.version = p.substr(
+        versionSeparatorIndex + 1, checksumSeparatorIndex - 1)
+    else:
+      versionSeparatorIndex = checksumSeparatorIndex
 
-proc addPackage(conf: ConfigRef; packages: StringTableRef, p: string; info: TLineInfo) =
-  let (name, ver) = getPathVersion(p)
+  result.name = p[0..<versionSeparatorIndex]
+
+proc addPackage*(conf: ConfigRef; packages: var PackageInfo, p: string;
+                 info: TLineInfo) =
+  let (name, ver, checksum) = getPathVersionChecksum(p)
   if isValidVersion(ver):
     let version = newVersion(ver)
-    if packages.getOrDefault(name).newVersion < version or
+    if packages.getOrDefault(name).version.newVersion < version or
       (not packages.hasKey(name)):
-      packages[name] = $version
+      if checksum.isValidSha1Hash():
+        packages[name] = ($version, checksum)
+      else:
+        packages[name] = ($version, "")
   else:
     localError(conf, info, "invalid package name: " & p)
 
-iterator chosen(packages: StringTableRef): string =
+iterator chosen(packages: PackageInfo): string =
   for key, val in pairs(packages):
-    let res = if val.len == 0: key else: key & '-' & val
+    var res = key
+    if val.version.len != 0:
+      res &= '-'
+      res &= val.version
+    if val.checksum.len != 0:
+      res &= '-'
+      res &= val.checksum
     yield res
 
 proc addNimblePath(conf: ConfigRef; p: string, info: TLineInfo) =
@@ -112,12 +148,12 @@ proc addNimblePath(conf: ConfigRef; p: string, info: TLineInfo) =
     if not path.isAbsolute():
       path = p / path
 
-  if not contains(conf.searchPaths, path):
+  if not contains(conf.searchPaths, AbsoluteDir path):
     message(conf, info, hintPath, path)
-    conf.lazyPaths.insert(path, 0)
+    conf.lazyPaths.insert(AbsoluteDir path, 0)
 
 proc addPathRec(conf: ConfigRef; dir: string, info: TLineInfo) =
-  var packages = newStringTable(modeStyleInsensitive)
+  var packages: PackageInfo = initTable[string, tuple[version, checksum: string]]()
   var pos = dir.len-1
   if dir[pos] in {DirSep, AltSep}: inc(pos)
   for k,p in os.walkDir(dir):
@@ -126,32 +162,10 @@ proc addPathRec(conf: ConfigRef; dir: string, info: TLineInfo) =
   for p in packages.chosen:
     addNimblePath(conf, p, info)
 
-proc nimblePath*(conf: ConfigRef; path: string, info: TLineInfo) =
-  addPathRec(conf, path, info)
-  addNimblePath(conf, path, info)
-
-when isMainModule:
-  proc v(s: string): Version = s.newVersion
-  # #head is special in the sense that it's assumed to always be newest.
-  doAssert v"1.0" < v"#head"
-  doAssert v"1.0" < v"1.1"
-  doAssert v"1.0.1" < v"1.1"
-  doAssert v"1" < v"1.1"
-  doAssert v"#aaaqwe" < v"1.1" # We cannot assume that a branch is newer.
-  doAssert v"#a111" < v"#head"
-
-  var rr = newStringTable()
-  addPackage rr, "irc-#a111"
-  addPackage rr, "irc-#head"
-  addPackage rr, "irc-0.1.0"
-  addPackage rr, "irc"
-  addPackage rr, "another"
-  addPackage rr, "another-0.1"
-
-  addPackage rr, "ab-0.1.3"
-  addPackage rr, "ab-0.1"
-  addPackage rr, "justone"
-
-  doAssert toSeq(rr.chosen) ==
-    @["irc-#head", "another-0.1", "ab-0.1.3", "justone"]
-
+proc nimblePath*(conf: ConfigRef; path: AbsoluteDir, info: TLineInfo) =
+  addPathRec(conf, path.string, info)
+  addNimblePath(conf, path.string, info)
+  let i = conf.nimblePaths.find(path)
+  if i != -1:
+    conf.nimblePaths.delete(i)
+  conf.nimblePaths.insert(path, 0)
diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim
index d3b4645dc..5417cd1e9 100644
--- a/compiler/nimconf.nim
+++ b/compiler/nimconf.nim
@@ -10,46 +10,51 @@
 # This module handles the reading of the config file.
 
 import
-  llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer,
-  options, idents, wordrecg, strtabs, lineinfos
+  llstream, commands, msgs, lexer, ast,
+  options, idents, wordrecg, lineinfos, pathutils, scriptconfig
+
+import std/[os, strutils, strtabs]
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
 
 # ---------------- configuration file parser -----------------------------
-# we use Nim's scanner here to save space and work
+# we use Nim's lexer here to save space and work
 
-proc ppGetTok(L: var TLexer, tok: var TToken) =
+proc ppGetTok(L: var Lexer, tok: var Token) =
   # simple filter
   rawGetTok(L, tok)
   while tok.tokType in {tkComment}: rawGetTok(L, tok)
 
-proc parseExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool
-proc parseAtom(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
+proc parseExpr(L: var Lexer, tok: var Token; config: ConfigRef): bool
+proc parseAtom(L: var Lexer, tok: var Token; config: ConfigRef): bool =
   if tok.tokType == tkParLe:
     ppGetTok(L, tok)
     result = parseExpr(L, tok, config)
     if tok.tokType == tkParRi: ppGetTok(L, tok)
     else: lexMessage(L, errGenerated, "expected closing ')'")
-  elif tok.ident.id == ord(wNot):
+  elif tok.tokType == tkNot:
     ppGetTok(L, tok)
     result = not parseAtom(L, tok, config)
   else:
     result = isDefined(config, tok.ident.s)
     ppGetTok(L, tok)
 
-proc parseAndExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
+proc parseAndExpr(L: var Lexer, tok: var Token; config: ConfigRef): bool =
   result = parseAtom(L, tok, config)
-  while tok.ident.id == ord(wAnd):
+  while tok.tokType == tkAnd:
     ppGetTok(L, tok)          # skip "and"
     var b = parseAtom(L, tok, config)
     result = result and b
 
-proc parseExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
+proc parseExpr(L: var Lexer, tok: var Token; config: ConfigRef): bool =
   result = parseAndExpr(L, tok, config)
-  while tok.ident.id == ord(wOr):
+  while tok.tokType == tkOr:
     ppGetTok(L, tok)          # skip "or"
     var b = parseAndExpr(L, tok, config)
     result = result or b
 
-proc evalppIf(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
+proc evalppIf(L: var Lexer, tok: var Token; config: ConfigRef): bool =
   ppGetTok(L, tok)            # skip 'if' or 'elif'
   result = parseExpr(L, tok, config)
   if tok.tokType == tkColon: ppGetTok(L, tok)
@@ -57,7 +62,7 @@ proc evalppIf(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
 
 #var condStack: seq[bool] = @[]
 
-proc doEnd(L: var TLexer, tok: var TToken; condStack: var seq[bool]) =
+proc doEnd(L: var Lexer, tok: var Token; condStack: var seq[bool]) =
   if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if")
   ppGetTok(L, tok)            # skip 'end'
   setLen(condStack, high(condStack))
@@ -66,21 +71,21 @@ type
   TJumpDest = enum
     jdEndif, jdElseEndif
 
-proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef;
+proc jumpToDirective(L: var Lexer, tok: var Token, dest: TJumpDest; config: ConfigRef;
                      condStack: var seq[bool])
-proc doElse(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) =
+proc doElse(L: var Lexer, tok: var Token; config: ConfigRef; condStack: var seq[bool]) =
   if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if")
   ppGetTok(L, tok)
   if tok.tokType == tkColon: ppGetTok(L, tok)
   if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif, config, condStack)
 
-proc doElif(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) =
+proc doElif(L: var Lexer, tok: var Token; config: ConfigRef; condStack: var seq[bool]) =
   if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if")
   var res = evalppIf(L, tok, config)
   if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif, config, condStack)
   else: condStack[high(condStack)] = true
 
-proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef;
+proc jumpToDirective(L: var Lexer, tok: var Token, dest: TJumpDest; config: ConfigRef;
                      condStack: var seq[bool]) =
   var nestedIfs = 0
   while true:
@@ -110,11 +115,11 @@ proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: Co
     else:
       ppGetTok(L, tok)
 
-proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) =
+proc parseDirective(L: var Lexer, tok: var Token; config: ConfigRef; condStack: var seq[bool]) =
   ppGetTok(L, tok)            # skip @
   case whichKeyword(tok.ident)
   of wIf:
-    setLen(condStack, len(condStack) + 1)
+    setLen(condStack, condStack.len + 1)
     let res = evalppIf(L, tok, config)
     condStack[high(condStack)] = res
     if not res: jumpToDirective(L, tok, jdElseEndif, config, condStack)
@@ -123,137 +128,165 @@ proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef; condStack
   of wEnd: doEnd(L, tok, condStack)
   of wWrite:
     ppGetTok(L, tok)
-    msgs.msgWriteln(config, strtabs.`%`(tokToStr(tok), config.configVars,
+    msgs.msgWriteln(config, strtabs.`%`($tok, config.configVars,
                                 {useEnvironment, useKey}))
     ppGetTok(L, tok)
   else:
     case tok.ident.s.normalize
     of "putenv":
       ppGetTok(L, tok)
-      var key = tokToStr(tok)
+      var key = $tok
       ppGetTok(L, tok)
-      os.putEnv(key, tokToStr(tok))
+      os.putEnv(key, $tok)
       ppGetTok(L, tok)
     of "prependenv":
       ppGetTok(L, tok)
-      var key = tokToStr(tok)
+      var key = $tok
       ppGetTok(L, tok)
-      os.putEnv(key, tokToStr(tok) & os.getEnv(key))
+      os.putEnv(key, $tok & os.getEnv(key))
       ppGetTok(L, tok)
     of "appendenv":
       ppGetTok(L, tok)
-      var key = tokToStr(tok)
+      var key = $tok
       ppGetTok(L, tok)
-      os.putEnv(key, os.getEnv(key) & tokToStr(tok))
+      os.putEnv(key, os.getEnv(key) & $tok)
       ppGetTok(L, tok)
     else:
-      lexMessage(L, errGenerated, "invalid directive: '$1'" % tokToStr(tok))
+      lexMessage(L, errGenerated, "invalid directive: '$1'" % $tok)
 
-proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) =
+proc confTok(L: var Lexer, tok: var Token; config: ConfigRef; condStack: var seq[bool]) =
   ppGetTok(L, tok)
   while tok.ident != nil and tok.ident.s == "@":
     parseDirective(L, tok, config, condStack)    # else: give the token to the parser
 
-proc checkSymbol(L: TLexer, tok: TToken) =
+proc checkSymbol(L: Lexer, tok: Token) =
   if tok.tokType notin {tkSymbol..tkInt64Lit, tkStrLit..tkTripleStrLit}:
-    lexMessage(L, errGenerated, "expected identifier, but got: " & tokToStr(tok))
+    lexMessage(L, errGenerated, "expected identifier, but got: " & $tok)
 
-proc parseAssignment(L: var TLexer, tok: var TToken;
-                     config: ConfigRef; condStack: var seq[bool]) =
-  if tok.ident.s == "-" or tok.ident.s == "--":
-    confTok(L, tok, config, condStack)           # skip unnecessary prefix
+proc parseAssignment(L: var Lexer, tok: var Token;
+                     config: ConfigRef; filename: AbsoluteFile; condStack: var seq[bool]) =
+  if tok.ident != nil:
+    if tok.ident.s == "-" or tok.ident.s == "--":
+      confTok(L, tok, config, condStack)           # skip unnecessary prefix
   var info = getLineInfo(L, tok) # save for later in case of an error
   checkSymbol(L, tok)
-  var s = tokToStr(tok)
+  var s = $tok
   confTok(L, tok, config, condStack)             # skip symbol
   var val = ""
   while tok.tokType == tkDot:
-    add(s, '.')
+    s.add('.')
     confTok(L, tok, config, condStack)
     checkSymbol(L, tok)
-    add(s, tokToStr(tok))
+    s.add($tok)
     confTok(L, tok, config, condStack)
   if tok.tokType == tkBracketLe:
     # BUGFIX: val, not s!
-    # BUGFIX: do not copy '['!
     confTok(L, tok, config, condStack)
     checkSymbol(L, tok)
-    add(val, tokToStr(tok))
+    val.add('[')
+    val.add($tok)
     confTok(L, tok, config, condStack)
     if tok.tokType == tkBracketRi: confTok(L, tok, config, condStack)
     else: lexMessage(L, errGenerated, "expected closing ']'")
-    add(val, ']')
+    val.add(']')
   let percent = tok.ident != nil and tok.ident.s == "%="
   if tok.tokType in {tkColon, tkEquals} or percent:
-    if len(val) > 0: add(val, ':')
+    if val.len > 0: val.add(':')
     confTok(L, tok, config, condStack)           # skip ':' or '=' or '%'
     checkSymbol(L, tok)
-    add(val, tokToStr(tok))
+    val.add($tok)
     confTok(L, tok, config, condStack)           # skip symbol
+    if tok.tokType in {tkColon, tkEquals}:
+      val.add($tok) # add the :
+      confTok(L, tok, config, condStack)           # skip symbol
+      checkSymbol(L, tok)
+      val.add($tok) # add the token after it
+      confTok(L, tok, config, condStack)           # skip symbol
     while tok.ident != nil and tok.ident.s == "&":
       confTok(L, tok, config, condStack)
       checkSymbol(L, tok)
-      add(val, tokToStr(tok))
+      val.add($tok)
       confTok(L, tok, config, condStack)
+  config.currentConfigDir = parentDir(filename.string)
   if percent:
     processSwitch(s, strtabs.`%`(val, config.configVars,
                                 {useEnvironment, useEmpty}), passPP, info, config)
   else:
     processSwitch(s, val, passPP, info, config)
 
-proc readConfigFile(
-    filename: string; cache: IdentCache; config: ConfigRef): bool =
+proc readConfigFile*(filename: AbsoluteFile; cache: IdentCache;
+                    config: ConfigRef): bool =
   var
-    L: TLexer
-    tok: TToken
+    L: Lexer = default(Lexer)
+    tok: Token
     stream: PLLStream
   stream = llStreamOpen(filename, fmRead)
   if stream != nil:
-    initToken(tok)
     openLexer(L, filename, stream, cache, config)
-    tok.tokType = tkEof       # to avoid a pointless warning
+    tok = Token(tokType: tkEof)       # to avoid a pointless warning
     var condStack: seq[bool] = @[]
     confTok(L, tok, config, condStack)           # read in the first token
-    while tok.tokType != tkEof: parseAssignment(L, tok, config, condStack)
-    if len(condStack) > 0: lexMessage(L, errGenerated, "expected @end")
+    while tok.tokType != tkEof: parseAssignment(L, tok, config, filename, condStack)
+    if condStack.len > 0: lexMessage(L, errGenerated, "expected @end")
     closeLexer(L)
     return true
+  else:
+    result = false
 
-proc getUserConfigPath(filename: string): string =
-  result = joinPath(getConfigDir(), filename)
+proc getUserConfigPath*(filename: RelativeFile): AbsoluteFile =
+  result = getConfigDir().AbsoluteDir / RelativeDir"nim" / filename
 
-proc getSystemConfigPath(conf: ConfigRef; filename: string): string =
+proc getSystemConfigPath*(conf: ConfigRef; filename: RelativeFile): AbsoluteFile =
   # try standard configuration file (installation did not distribute files
   # the UNIX way)
   let p = getPrefixDir(conf)
-  result = joinPath([p, "config", filename])
+  result = p / RelativeDir"config" / filename
   when defined(unix):
-    if not existsFile(result): result = joinPath([p, "etc", filename])
-    if not existsFile(result): result = "/etc/" & filename
+    if not fileExists(result): result = p / RelativeDir"etc/nim" / filename
+    if not fileExists(result): result = AbsoluteDir"/etc/nim" / filename
 
-proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) =
+proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: IdGenerator) =
   setDefaultLibpath(conf)
-
-  var configFiles = newSeq[string]()
-
-  template readConfigFile(path: string) =
+  template readConfigFile(path) =
     let configPath = path
     if readConfigFile(configPath, cache, conf):
-      add(configFiles, configPath)
+      conf.configFiles.add(configPath)
+
+  template runNimScriptIfExists(path: AbsoluteFile, isMain = false) =
+    let p = path # eval once
+    var s: PLLStream = nil
+    if isMain and optWasNimscript in conf.globalOptions:
+      if conf.projectIsStdin: s = stdin.llStreamOpen
+      elif conf.projectIsCmd: s = llStreamOpen(conf.cmdInput)
+    if s == nil and fileExists(p): s = llStreamOpen(p, fmRead)
+    if s != nil:
+      conf.configFiles.add(p)
+      runNimScript(cache, p, idgen, freshDefines = false, conf, s)
 
-  if optSkipConfigFile notin conf.globalOptions:
+  if optSkipSystemConfigFile notin conf.globalOptions:
     readConfigFile(getSystemConfigPath(conf, cfg))
 
+    if cfg == DefaultConfig:
+      runNimScriptIfExists(getSystemConfigPath(conf, DefaultConfigNims))
+
   if optSkipUserConfigFile notin conf.globalOptions:
     readConfigFile(getUserConfigPath(cfg))
 
-  let pd = if conf.projectPath.len > 0: conf.projectPath else: getCurrentDir()
+    if cfg == DefaultConfig:
+      runNimScriptIfExists(getUserConfigPath(DefaultConfigNims))
+
+  let pd = if not conf.projectPath.isEmpty: conf.projectPath else: AbsoluteDir(getCurrentDir())
   if optSkipParentConfigFiles notin conf.globalOptions:
-    for dir in parentDirs(pd, fromRoot=true, inclusive=false):
-      readConfigFile(dir / cfg)
+    for dir in parentDirs(pd.string, fromRoot=true, inclusive=false):
+      readConfigFile(AbsoluteDir(dir) / cfg)
+
+      if cfg == DefaultConfig:
+        runNimScriptIfExists(AbsoluteDir(dir) / DefaultConfigNims)
 
   if optSkipProjConfigFile notin conf.globalOptions:
     readConfigFile(pd / cfg)
+    if cfg == DefaultConfig:
+      runNimScriptIfExists(pd / DefaultConfigNims)
 
     if conf.projectName.len != 0:
       # new project wide config file:
@@ -262,5 +295,26 @@ proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) =
         projectConfig = changeFileExt(conf.projectFull, "nim.cfg")
       readConfigFile(projectConfig)
 
-  for filename in configFiles:
-    rawMessage(conf, hintConf, filename)
+
+  let scriptFile = conf.projectFull.changeFileExt("nims")
+  let scriptIsProj = scriptFile == conf.projectFull
+  template showHintConf =
+    for filename in conf.configFiles:
+      # delayed to here so that `hintConf` is honored
+      rawMessage(conf, hintConf, filename.string)
+  if conf.cmd == cmdNimscript:
+    showHintConf()
+    conf.configFiles.setLen 0
+  if conf.cmd notin {cmdIdeTools, cmdCheck, cmdDump}:
+    if conf.cmd == cmdNimscript:
+      runNimScriptIfExists(conf.projectFull, isMain = true)
+    else:
+      runNimScriptIfExists(scriptFile, isMain = true)
+  else:
+    if not scriptIsProj:
+      runNimScriptIfExists(scriptFile, isMain = true)
+    else:
+      # 'nimsuggest foo.nims' means to just auto-complete the NimScript file
+      # `nim check foo.nims' means to check the syntax of the NimScript file
+      discard
+  showHintConf()
diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim
index f20b5642c..0833cfeb3 100644
--- a/compiler/nimeval.nim
+++ b/compiler/nimeval.nim
@@ -9,24 +9,29 @@
 
 ## exposes the Nim VM to clients.
 import
-  ast, astalgo, modules, passes, condsyms,
-  options, sem, semdata, llstream, vm, vmdef,
-  modulegraphs, idents, os
+  ast, modules, condsyms,
+  options, llstream, lineinfos, vm,
+  vmdef, modulegraphs, idents, pathutils,
+  scriptconfig, std/[compilesettings, tables, os]
+
+import pipelines
+
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
 
 type
   Interpreter* = ref object ## Use Nim as an interpreter with this object
     mainModule: PSym
     graph: ModuleGraph
     scriptName: string
+    idgen: IdGenerator
 
 iterator exportedSymbols*(i: Interpreter): PSym =
   assert i != nil
   assert i.mainModule != nil, "no main module selected"
-  var it: TTabIter
-  var s = initTabIter(it, i.mainModule.tab)
-  while s != nil:
+  for s in modulegraphs.allSyms(i.graph, i.mainModule):
     yield s
-    s = nextIter(it, i.mainModule.tab)
 
 proc selectUniqueSymbol*(i: Interpreter; name: string;
                          symKinds: set[TSymKind] = {skLet, skVar}): PSym =
@@ -35,17 +40,17 @@ proc selectUniqueSymbol*(i: Interpreter; name: string;
   assert i != nil
   assert i.mainModule != nil, "no main module selected"
   let n = getIdent(i.graph.cache, name)
-  var it: TIdentIter
-  var s = initIdentIter(it, i.mainModule.tab, n)
+  var it: ModuleIter = default(ModuleIter)
+  var s = initModuleIter(it, i.graph, i.mainModule, n)
   result = nil
   while s != nil:
     if s.kind in symKinds:
       if result == nil: result = s
       else: return nil # ambiguous
-    s = nextIdentIter(it, i.mainModule.tab)
+    s = nextModuleIter(it, i.graph)
 
 proc selectRoutine*(i: Interpreter; name: string): PSym =
-  ## Selects a declared rountine (proc/func/etc) from the main module.
+  ## Selects a declared routine (proc/func/etc) from the main module.
   ## The routine needs to have the export marker ``*``. The only matching
   ## routine is returned and ``nil`` if it is overloaded.
   result = selectUniqueSymbol(i, name, {skTemplate, skMacro, skFunc,
@@ -58,6 +63,10 @@ proc callRoutine*(i: Interpreter; routine: PSym; args: openArray[PNode]): PNode
 proc getGlobalValue*(i: Interpreter; letOrVar: PSym): PNode =
   result = vm.getGlobalValue(PCtx i.graph.vm, letOrVar)
 
+proc setGlobalValue*(i: Interpreter; letOrVar: PSym, val: PNode) =
+  ## Sets a global value to a given PNode, does not do any type checking.
+  vm.setGlobalValue(PCtx i.graph.vm, letOrVar, val)
+
 proc implementRoutine*(i: Interpreter; pkg, module, name: string;
                        impl: proc (a: VmArgs) {.closure, gcsafe.}) =
   assert i != nil
@@ -68,18 +77,24 @@ proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) =
   ## This can also be used to *reload* the script.
   assert i != nil
   assert i.mainModule != nil, "no main module selected"
-  initStrTable(i.mainModule.tab)
+  initStrTables(i.graph, i.mainModule)
+  i.graph.cacheSeqs.clear()
+  i.graph.cacheCounters.clear()
+  i.graph.cacheTables.clear()
   i.mainModule.ast = nil
 
   let s = if scriptStream != nil: scriptStream
           else: llStreamOpen(findFile(i.graph.config, i.scriptName), fmRead)
-  processModule(i.graph, i.mainModule, s)
+  discard processPipelineModule(i.graph, i.mainModule, i.idgen, s)
 
 proc findNimStdLib*(): string =
   ## Tries to find a path to a valid "system.nim" file.
   ## Returns "" on failure.
   try:
     let nimexe = os.findExe("nim")
+      # this can't work with choosenim shims, refs https://github.com/dom96/choosenim/issues/189
+      # it'd need `nim dump --dump.format:json . | jq -r .libpath`
+      # which we should simplify as `nim dump --key:libpath`
     if nimexe.len == 0: return ""
     result = nimexe.splitPath()[0] /../ "lib"
     if not fileExists(result / "system.nim"):
@@ -89,32 +104,74 @@ proc findNimStdLib*(): string =
   except OSError, ValueError:
     return ""
 
+proc findNimStdLibCompileTime*(): string =
+  ## Same as `findNimStdLib` but uses source files used at compile time,
+  ## and asserts on error.
+  result = querySetting(libPath)
+  doAssert fileExists(result / "system.nim"), "result:" & result
+
 proc createInterpreter*(scriptName: string;
                         searchPaths: openArray[string];
-                        flags: TSandboxFlags = {}): Interpreter =
+                        flags: TSandboxFlags = {},
+                        defines = @[("nimscript", "true")],
+                        registerOps = true): Interpreter =
   var conf = newConfigRef()
   var cache = newIdentCache()
   var graph = newModuleGraph(cache, conf)
-  connectCallbacks(graph)
+  connectPipelineCallbacks(graph)
   initDefines(conf.symbols)
-  defineSymbol(conf.symbols, "nimscript")
-  defineSymbol(conf.symbols, "nimconfig")
-  registerPass(graph, semPass)
-  registerPass(graph, evalPass)
+  for define in defines:
+    defineSymbol(conf.symbols, define[0], define[1])
 
   for p in searchPaths:
-    conf.searchPaths.add(p)
-    if conf.libpath.len == 0: conf.libpath = p
+    conf.searchPaths.add(AbsoluteDir p)
+    if conf.libpath.isEmpty: conf.libpath = AbsoluteDir p
 
   var m = graph.makeModule(scriptName)
   incl(m.flags, sfMainModule)
-  var vm = newCtx(m, cache, graph)
+  var idgen = idGeneratorFromModule(m)
+  var vm = newCtx(m, cache, graph, idgen)
   vm.mode = emRepl
   vm.features = flags
+  if registerOps:
+    vm.registerAdditionalOps() # Required to register parts of stdlib modules
   graph.vm = vm
-  graph.compileSystemModule()
-  result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName)
+  setPipeLinePass(graph, EvalPass)
+  graph.compilePipelineSystemModule()
+  result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName, idgen: idgen)
 
 proc destroyInterpreter*(i: Interpreter) =
   ## destructor.
   discard "currently nothing to do."
+
+proc registerErrorHook*(i: Interpreter, hook:
+                        proc (config: ConfigRef; info: TLineInfo; msg: string;
+                              severity: Severity) {.gcsafe.}) =
+  i.graph.config.structuredErrorHook = hook
+
+proc runRepl*(r: TLLRepl;
+              searchPaths: openArray[string];
+              supportNimscript: bool) =
+  ## deadcode but please don't remove... might be revived
+  var conf = newConfigRef()
+  var cache = newIdentCache()
+  var graph = newModuleGraph(cache, conf)
+
+  for p in searchPaths:
+    conf.searchPaths.add(AbsoluteDir p)
+    if conf.libpath.isEmpty: conf.libpath = AbsoluteDir p
+
+  conf.cmd = cmdInteractive # see also `setCmd`
+  conf.setErrorMaxHighMaybe
+  initDefines(conf.symbols)
+  defineSymbol(conf.symbols, "nimscript")
+  if supportNimscript: defineSymbol(conf.symbols, "nimconfig")
+  when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
+  var m = graph.makeStdinModule()
+  incl(m.flags, sfMainModule)
+  var idgen = idGeneratorFromModule(m)
+
+  if supportNimscript: graph.vm = setupVM(m, cache, "stdin", graph, idgen)
+  setPipeLinePass(graph, InterpreterPass)
+  graph.compilePipelineSystemModule()
+  discard processPipelineModule(graph, m, idgen, llStreamOpenStdIn(r))
diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim
deleted file mode 100644
index 58b019cd3..000000000
--- a/compiler/nimfix/nimfix.nim
+++ /dev/null
@@ -1,111 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Nimfix is a tool that helps to convert old-style Nimrod code to Nim code.
-
-import strutils, os, parseopt
-import compiler/[options, commands, modules, sem,
-  passes, passaux, linter,
-  msgs, nimconf,
-  extccomp, condsyms,
-  modulegraphs, idents]
-
-const Usage = """
-Nimfix - Tool to patch Nim code
-Usage:
-  nimfix [options] projectfile.nim
-
-Options:
-  --overwriteFiles:on|off          overwrite the original nim files.
-                                   DEFAULT is ON!
-  --wholeProject                   overwrite every processed file.
-  --checkExtern:on|off             style check also extern names
-  --styleCheck:on|off|auto         performs style checking for identifiers
-                                   and suggests an alternative spelling;
-                                   'auto' corrects the spelling.
-  --bestEffort                     try to fix the code even when there
-                                   are errors.
-
-In addition, all command line options of Nim are supported.
-"""
-
-proc mainCommand =
-  registerPass verbosePass
-  registerPass semPass
-  conf.cmd = cmdPretty
-  searchPaths.add options.libpath
-  if gProjectFull.len != 0:
-    # current path is always looked first for modules
-    searchPaths.insert(gProjectPath, 0)
-
-  compileProject(newModuleGraph(), newIdentCache())
-  pretty.overwriteFiles()
-
-proc processCmdLine*(pass: TCmdLinePass, cmd: string, config: ConfigRef) =
-  var p = parseopt.initOptParser(cmd)
-  var argsCount = 0
-  gOnlyMainfile = true
-  while true:
-    parseopt.next(p)
-    case p.kind
-    of cmdEnd: break
-    of cmdLongoption, cmdShortOption:
-      case p.key.normalize
-      of "overwritefiles":
-        case p.val.normalize
-        of "on": gOverWrite = true
-        of "off": gOverWrite = false
-        else: localError(gCmdLineInfo, errOnOrOffExpected)
-      of "checkextern":
-        case p.val.normalize
-        of "on": gCheckExtern = true
-        of "off": gCheckExtern = false
-        else: localError(gCmdLineInfo, errOnOrOffExpected)
-      of "stylecheck":
-        case p.val.normalize
-        of "off": gStyleCheck = StyleCheck.None
-        of "on": gStyleCheck = StyleCheck.Warn
-        of "auto": gStyleCheck = StyleCheck.Auto
-        else: localError(gCmdLineInfo, errOnOrOffExpected)
-      of "wholeproject": gOnlyMainfile = false
-      of "besteffort": msgs.gErrorMax = high(int) # don't stop after first error
-      else:
-        processSwitch(pass, p, config)
-    of cmdArgument:
-      options.gProjectName = unixToNativePath(p.key)
-      # if processArgument(pass, p, argsCount): break
-
-proc handleCmdLine(config: ConfigRef) =
-  if paramCount() == 0:
-    stdout.writeLine(Usage)
-  else:
-    processCmdLine(passCmd1, "", config)
-    if gProjectName != "":
-      try:
-        gProjectFull = canonicalizePath(gProjectName)
-      except OSError:
-        gProjectFull = gProjectName
-      var p = splitFile(gProjectFull)
-      gProjectPath = p.dir
-      gProjectName = p.name
-    else:
-      gProjectPath = getCurrentDir()
-    loadConfigs(DefaultConfig, config) # load all config files
-    # now process command line arguments again, because some options in the
-    # command line can overwite the config file's settings
-    extccomp.initVars()
-    processCmdLine(passCmd2, "", config)
-    mainCommand()
-
-when compileOption("gc", "v2") or compileOption("gc", "refc"):
-  GC_disableMarkAndSweep()
-
-condsyms.initDefines()
-defineSymbol "nimfix"
-handleCmdline newConfigRef()
diff --git a/compiler/nimfix/nimfix.nim.cfg b/compiler/nimfix/nimfix.nim.cfg
deleted file mode 100644
index 0d9dbfa4b..000000000
--- a/compiler/nimfix/nimfix.nim.cfg
+++ /dev/null
@@ -1,17 +0,0 @@
-# Special configuration file for the Nim project
-# gc:markAndSweep
-
-hint[XDeclaredButNotUsed]:off
-path:"$projectPath/.."
-
-path:"$lib/packages/docutils"
-path:"$nim"
-
-define:useStdoutAsStdmsg
-symbol:nimfix
-define:nimfix
-
-cs:partial
-#define:useNodeIds
-define:booting
-define:noDocgen
diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim
deleted file mode 100644
index c3e16e5ba..000000000
--- a/compiler/nimfix/prettybase.nim
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-import strutils except Letters
-import lexbase, streams
-import ".." / [ast, msgs, lineinfos, idents, options, linter]
-from os import splitFile
-
-proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PIdent) =
-  let line = sourceLine(conf, info)
-  var first = min(info.col.int, line.len)
-  if first < 0: return
-  #inc first, skipIgnoreCase(line, "proc ", first)
-  while first > 0 and line[first-1] in Letters: dec first
-  if first < 0: return
-  if line[first] == '`': inc first
-
-  let last = first+identLen(line, first)-1
-  if cmpIgnoreStyle(line[first..last], oldSym.s) == 0:
-    var x = line.substr(0, first-1) & newSym.s & line.substr(last+1)
-    system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x)
-    conf.m.fileInfos[info.fileIndex.int32].dirty = true
-    #if newSym.s == "File": writeStackTrace()
-
-proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PSym) =
-  replaceDeprecated(conf, info, oldSym.name, newSym.name)
-
-proc replaceComment*(conf: ConfigRef; info: TLineInfo) =
-  let line = sourceLine(conf, info)
-  var first = info.col.int
-  if line[first] != '#': inc first
-
-  var x = line.substr(0, first-1) & "discard " & line.substr(first+1).escape
-  system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x)
-  conf.m.fileInfos[info.fileIndex.int32].dirty = true
diff --git a/compiler/nimlexbase.nim b/compiler/nimlexbase.nim
index 2e7416645..6708b57f8 100644
--- a/compiler/nimlexbase.nim
+++ b/compiler/nimlexbase.nim
@@ -12,8 +12,12 @@
 # handling that exists! Only at line endings checks are necessary
 # if the buffer needs refilling.
 
-import
-  llstream, strutils
+import llstream
+
+import std/strutils
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 const
   Lrz* = ' '
@@ -40,7 +44,8 @@ type
   TBaseLexer* = object of RootObj
     bufpos*: int
     buf*: cstring
-    bufLen*: int              # length of buffer in characters
+    bufStorage: string
+    bufLen: int
     stream*: PLLStream        # we read from this stream
     lineNumber*: int          # the current line number
                               # private data:
@@ -60,16 +65,12 @@ proc handleCR*(L: var TBaseLexer, pos: int): int
   # position to continue the scanning from. `pos` must be the position
   # of the CR.
 proc handleLF*(L: var TBaseLexer, pos: int): int
-  # Call this if you scanned over LF in the buffer; it returns the the
+  # Call this if you scanned over LF in the buffer; it returns the
   # position to continue the scanning from. `pos` must be the position
   # of the LF.
 # implementation
 
-const
-  chrSize = sizeof(char)
-
 proc closeBaseLexer(L: var TBaseLexer) =
-  dealloc(L.buf)
   llStreamClose(L.stream)
 
 proc fillBuffer(L: var TBaseLexer) =
@@ -84,10 +85,9 @@ proc fillBuffer(L: var TBaseLexer) =
   toCopy = L.bufLen - L.sentinel - 1
   assert(toCopy >= 0)
   if toCopy > 0:
-    moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize)
+    moveMem(addr L.buf[0], addr L.buf[L.sentinel + 1], toCopy)
     # "moveMem" handles overlapping regions
-  charsRead = llStreamRead(L.stream, addr(L.buf[toCopy]),
-                           (L.sentinel + 1) * chrSize) div chrSize
+  charsRead = llStreamRead(L.stream, addr L.buf[toCopy], L.sentinel + 1)
   s = toCopy + charsRead
   if charsRead < L.sentinel + 1:
     L.buf[s] = EndOfFile      # set end marker
@@ -107,10 +107,11 @@ proc fillBuffer(L: var TBaseLexer) =
         # double the buffer's size and try again:
         oldBufLen = L.bufLen
         L.bufLen = L.bufLen * 2
-        L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize))
+        L.bufStorage.setLen(L.bufLen)
+        L.buf = L.bufStorage.cstring
         assert(L.bufLen - oldBufLen == oldBufLen)
         charsRead = llStreamRead(L.stream, addr(L.buf[oldBufLen]),
-                                 oldBufLen * chrSize) div chrSize
+                                 oldBufLen)
         if charsRead < oldBufLen:
           L.buf[oldBufLen + charsRead] = EndOfFile
           L.sentinel = oldBufLen + charsRead
@@ -149,8 +150,9 @@ proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) =
   assert(bufLen > 0)
   L.bufpos = 0
   L.offsetBase = 0
+  L.bufStorage = newString(bufLen)
+  L.buf = L.bufStorage.cstring
   L.bufLen = bufLen
-  L.buf = cast[cstring](alloc(bufLen * chrSize))
   L.sentinel = bufLen - 1
   L.lineStart = 0
   L.lineNumber = 1            # lines start at 1
@@ -164,9 +166,9 @@ proc getColNumber(L: TBaseLexer, pos: int): int =
 proc getCurrentLine(L: TBaseLexer, marker: bool = true): string =
   result = ""
   var i = L.lineStart
-  while not (L.buf[i] in {CR, LF, EndOfFile}):
-    add(result, L.buf[i])
-    inc(i)
-  result.add("\n")
+  while L.buf[i] notin {CR, LF, EndOfFile}:
+    result.add L.buf[i]
+    inc i
+  result.add "\n"
   if marker:
-    result.add(spaces(getColNumber(L, L.bufpos)) & '^' & "\n")
+    result.add spaces(getColNumber(L, L.bufpos)) & '^' & "\n"
diff --git a/compiler/nimpaths.nim b/compiler/nimpaths.nim
new file mode 100644
index 000000000..0a66c3c1f
--- /dev/null
+++ b/compiler/nimpaths.nim
@@ -0,0 +1,54 @@
+##[
+Represents absolute paths, but using a symbolic variables (eg $nimr) which can be
+resolved at runtime; this avoids hardcoding at compile time absolute paths so
+that the project root can be relocated.
+
+xxx factor pending https://github.com/timotheecour/Nim/issues/616, see also
+$nim/testament/lib/stdtest/specialpaths.nim
+specialpaths is simpler because it doesn't need variables to be relocatable at
+runtime (eg for use in testament)
+
+interpolation variables:
+: $nimr: such that `$nimr/lib/system.nim` exists (avoids confusion with $nim binary)
+         in compiler, it's obtainable via getPrefixDir(); for other tools (eg koch),
+        this could be getCurrentDir() or getAppFilename().parentDir.parentDir,
+        depending on use case
+
+Unstable API
+]##
+
+import std/[os, strutils]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
+const
+  docCss* = "$nimr/doc/nimdoc.css"
+  docCls* = "$nimr/doc/nimdoc.cls"
+  docHackNim* = "$nimr/tools/dochack/dochack.nim"
+  docHackJs* = docHackNim.changeFileExt("js")
+  docHackJsFname* = docHackJs.lastPathPart
+  theindexFname* = "theindex.html"
+  nimdocOutCss* = "nimdoc.out.css"
+  nimdocOutCls* = "nimdoc.cls"
+    # `out` to make it easier to use with gitignore in user's repos
+  htmldocsDirname* = "htmldocs"
+  dotdotMangle* = "_._"  ## refs #13223
+    # if this changes, make sure it's consistent with `esc` and `escapeLink`
+    # lots of other obvious options won't work, see #14454; `_` could work too
+
+proc interp*(path: string, nimr: string): string =
+  result = path % ["nimr", nimr]
+  doAssert '$' notin result, $(path, nimr, result) # avoids un-interpolated variables in output
+
+proc getDocHacksJs*(nimr: string, nim = getCurrentCompilerExe(), forceRebuild = false): string =
+  ## return absolute path to dochack.js, rebuilding if it doesn't exist or if
+  ## `forceRebuild`.
+  let docHackJs2 = docHackJs.interp(nimr = nimr)
+  if forceRebuild or not docHackJs2.fileExists:
+    let cmd =  "$nim js -d:release $file" % ["nim", nim.quoteShell, "file", docHackNim.interp(nimr = nimr).quoteShell]
+    echo "getDocHacksJs: cmd: " & cmd
+    doAssert execShellCmd(cmd) == 0, $(cmd)
+  doAssert docHackJs2.fileExists
+  result = docHackJs2
diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim
index b00353e20..7edf55278 100644
--- a/compiler/nimsets.nim
+++ b/compiler/nimsets.nim
@@ -10,21 +10,23 @@
 # this unit handles Nim sets; it implements symbolic sets
 
 import
-  ast, astalgo, trees, nversion, lineinfos, platform, bitsets, types, renderer,
-  options
+  ast, astalgo, lineinfos, bitsets, types, options
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 proc inSet*(s: PNode, elem: PNode): bool =
   assert s.kind == nkCurly
   if s.kind != nkCurly:
     #internalError(s.info, "inSet")
     return false
-  for i in countup(0, sonsLen(s) - 1):
-    if s.sons[i].kind == nkRange:
-      if leValue(s.sons[i].sons[0], elem) and
-          leValue(elem, s.sons[i].sons[1]):
+  for i in 0..<s.len:
+    if s[i].kind == nkRange:
+      if leValue(s[i][0], elem) and
+          leValue(elem, s[i][1]):
         return true
     else:
-      if sameValue(s.sons[i], elem):
+      if sameValue(s[i], elem):
         return true
   result = false
 
@@ -32,13 +34,13 @@ proc overlap*(a, b: PNode): bool =
   if a.kind == nkRange:
     if b.kind == nkRange:
       # X..Y and C..D overlap iff (X <= D and C <= Y)
-      result = leValue(a.sons[0], b.sons[1]) and
-               leValue(b.sons[0], a.sons[1])
+      result = leValue(a[0], b[1]) and
+               leValue(b[0], a[1])
     else:
-      result = leValue(a.sons[0], b) and leValue(b, a.sons[1])
+      result = leValue(a[0], b) and leValue(b, a[1])
   else:
     if b.kind == nkRange:
-      result = leValue(b.sons[0], a) and leValue(a, b.sons[1])
+      result = leValue(b[0], a) and leValue(a, b[1])
     else:
       result = sameValue(a, b)
 
@@ -48,68 +50,69 @@ proc someInSet*(s: PNode, a, b: PNode): bool =
   if s.kind != nkCurly:
     #internalError(s.info, "SomeInSet")
     return false
-  for i in countup(0, sonsLen(s) - 1):
-    if s.sons[i].kind == nkRange:
-      if leValue(s.sons[i].sons[0], b) and leValue(b, s.sons[i].sons[1]) or
-          leValue(s.sons[i].sons[0], a) and leValue(a, s.sons[i].sons[1]):
+  for i in 0..<s.len:
+    if s[i].kind == nkRange:
+      if leValue(s[i][0], b) and leValue(b, s[i][1]) or
+          leValue(s[i][0], a) and leValue(a, s[i][1]):
         return true
     else:
       # a <= elem <= b
-      if leValue(a, s.sons[i]) and leValue(s.sons[i], b):
+      if leValue(a, s[i]) and leValue(s[i], b):
         return true
   result = false
 
-proc toBitSet*(conf: ConfigRef; s: PNode, b: var TBitSet) =
-  var first, j: BiggestInt
-  first = firstOrd(conf, s.typ.sons[0])
-  bitSetInit(b, int(getSize(conf, s.typ)))
-  for i in countup(0, sonsLen(s) - 1):
-    if s.sons[i].kind == nkRange:
-      j = getOrdValue(s.sons[i].sons[0])
-      while j <= getOrdValue(s.sons[i].sons[1]):
-        bitSetIncl(b, j - first)
+proc toBitSet*(conf: ConfigRef; s: PNode): TBitSet =
+  result = @[]
+  var first: Int128 = Zero
+  var j: Int128 = Zero
+  first = firstOrd(conf, s.typ.elementType)
+  bitSetInit(result, int(getSize(conf, s.typ)))
+  for i in 0..<s.len:
+    if s[i].kind == nkRange:
+      j = getOrdValue(s[i][0], first)
+      while j <= getOrdValue(s[i][1], first):
+        bitSetIncl(result, toInt64(j - first))
         inc(j)
     else:
-      bitSetIncl(b, getOrdValue(s.sons[i]) - first)
+      bitSetIncl(result, toInt64(getOrdValue(s[i]) - first))
 
 proc toTreeSet*(conf: ConfigRef; s: TBitSet, settype: PType, info: TLineInfo): PNode =
   var
     a, b, e, first: BiggestInt # a, b are interval borders
     elemType: PType
     n: PNode
-  elemType = settype.sons[0]
-  first = firstOrd(conf, elemType)
+  elemType = settype[0]
+  first = firstOrd(conf, elemType).toInt64
   result = newNodeI(nkCurly, info)
   result.typ = settype
   result.info = info
   e = 0
-  while e < len(s) * ElemSize:
+  while e < s.len * ElemSize:
     if bitSetIn(s, e):
       a = e
       b = e
       while true:
         inc(b)
-        if (b >= len(s) * ElemSize) or not bitSetIn(s, b): break
+        if (b >= s.len * ElemSize) or not bitSetIn(s, b): break
       dec(b)
-      let aa = newIntTypeNode(nkIntLit, a + first, elemType)
+      let aa = newIntTypeNode(a + first, elemType)
       aa.info = info
       if a == b:
-        addSon(result, aa)
+        result.add aa
       else:
         n = newNodeI(nkRange, info)
         n.typ = elemType
-        addSon(n, aa)
-        let bb = newIntTypeNode(nkIntLit, b + first, elemType)
+        n.add aa
+        let bb = newIntTypeNode(b + first, elemType)
         bb.info = info
-        addSon(n, bb)
-        addSon(result, n)
+        n.add bb
+        result.add n
       e = b
     inc(e)
 
 template nodeSetOp(a, b: PNode, op: untyped) {.dirty.} =
-  var x, y: TBitSet
-  toBitSet(conf, a, x)
-  toBitSet(conf, b, y)
+  var x = toBitSet(conf, a)
+  let y = toBitSet(conf, b)
   op(x, y)
   result = toTreeSet(conf, x, a.typ, a.info)
 
@@ -119,39 +122,34 @@ proc intersectSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSe
 proc symdiffSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff)
 
 proc containsSets*(conf: ConfigRef; a, b: PNode): bool =
-  var x, y: TBitSet
-  toBitSet(conf, a, x)
-  toBitSet(conf, b, y)
+  let x = toBitSet(conf, a)
+  let y = toBitSet(conf, b)
   result = bitSetContains(x, y)
 
 proc equalSets*(conf: ConfigRef; a, b: PNode): bool =
-  var x, y: TBitSet
-  toBitSet(conf, a, x)
-  toBitSet(conf, b, y)
+  let x = toBitSet(conf, a)
+  let y = toBitSet(conf, b)
   result = bitSetEquals(x, y)
 
 proc complement*(conf: ConfigRef; a: PNode): PNode =
-  var x: TBitSet
-  toBitSet(conf, a, x)
-  for i in countup(0, high(x)): x[i] = not x[i]
+  var x = toBitSet(conf, a)
+  for i in 0..high(x): x[i] = not x[i]
   result = toTreeSet(conf, x, a.typ, a.info)
 
 proc deduplicate*(conf: ConfigRef; a: PNode): PNode =
-  var x: TBitSet
-  toBitSet(conf, a, x)
+  let x = toBitSet(conf, a)
   result = toTreeSet(conf, x, a.typ, a.info)
 
 proc cardSet*(conf: ConfigRef; a: PNode): BiggestInt =
-  var x: TBitSet
-  toBitSet(conf, a, x)
+  let x = toBitSet(conf, a)
   result = bitSetCard(x)
 
 proc setHasRange*(s: PNode): bool =
   assert s.kind == nkCurly
   if s.kind != nkCurly:
     return false
-  for i in countup(0, sonsLen(s) - 1):
-    if s.sons[i].kind == nkRange:
+  for i in 0..<s.len:
+    if s[i].kind == nkRange:
       return true
   result = false
 
diff --git a/compiler/nodejs.nim b/compiler/nodejs.nim
index 7f9f28aaf..9753e1c99 100644
--- a/compiler/nodejs.nim
+++ b/compiler/nodejs.nim
@@ -1,8 +1,10 @@
-import os
+import std/os
 
-proc findNodeJs*(): string =
+proc findNodeJs*(): string {.inline.} =
+  ## Find NodeJS executable and return it as a string.
   result = findExe("nodejs")
-  if result == "":
+  if result.len == 0:
     result = findExe("node")
-  if result == "":
-    result = findExe("iojs")
+  if result.len == 0:
+    echo "Please install NodeJS first, see https://nodejs.org/en/download"
+    raise newException(IOError, "NodeJS not found in PATH")
diff --git a/compiler/nodekinds.nim b/compiler/nodekinds.nim
new file mode 100644
index 000000000..ccdbbd26d
--- /dev/null
+++ b/compiler/nodekinds.nim
@@ -0,0 +1,211 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## NodeKind enum.
+
+type
+  TNodeKind* = enum # order is extremely important, because ranges are used
+                    # to check whether a node belongs to a certain class
+    nkNone,               # unknown node kind: indicates an error
+                          # Expressions:
+                          # Atoms:
+    nkEmpty,              # the node is empty
+    nkIdent,              # node is an identifier
+    nkSym,                # node is a symbol
+    nkType,               # node is used for its typ field
+
+    nkCharLit,            # a character literal ''
+    nkIntLit,             # an integer literal
+    nkInt8Lit,
+    nkInt16Lit,
+    nkInt32Lit,
+    nkInt64Lit,
+    nkUIntLit,            # an unsigned integer literal
+    nkUInt8Lit,
+    nkUInt16Lit,
+    nkUInt32Lit,
+    nkUInt64Lit,
+    nkFloatLit,           # a floating point literal
+    nkFloat32Lit,
+    nkFloat64Lit,
+    nkFloat128Lit,
+    nkStrLit,             # a string literal ""
+    nkRStrLit,            # a raw string literal r""
+    nkTripleStrLit,       # a triple string literal """
+    nkNilLit,             # the nil literal
+                          # end of atoms
+    nkComesFrom,          # "comes from" template/macro information for
+                          # better stack trace generation
+    nkDotCall,            # used to temporarily flag a nkCall node;
+                          # this is used
+                          # for transforming ``s.len`` to ``len(s)``
+
+    nkCommand,            # a call like ``p 2, 4`` without parenthesis
+    nkCall,               # a call like p(x, y) or an operation like +(a, b)
+    nkCallStrLit,         # a call with a string literal
+                          # x"abc" has two sons: nkIdent, nkRStrLit
+                          # x"""abc""" has two sons: nkIdent, nkTripleStrLit
+    nkInfix,              # a call like (a + b)
+    nkPrefix,             # a call like !a
+    nkPostfix,            # something like a! (also used for visibility)
+    nkHiddenCallConv,     # an implicit type conversion via a type converter
+
+    nkExprEqExpr,         # a named parameter with equals: ''expr = expr''
+    nkExprColonExpr,      # a named parameter with colon: ''expr: expr''
+    nkIdentDefs,          # a definition like `a, b: typeDesc = expr`
+                          # either typeDesc or expr may be nil; used in
+                          # formal parameters, var statements, etc.
+    nkVarTuple,           # a ``var (a, b) = expr`` construct
+    nkPar,                # syntactic (); may be a tuple constructor
+    nkObjConstr,          # object constructor: T(a: 1, b: 2)
+    nkCurly,              # syntactic {}
+    nkCurlyExpr,          # an expression like a{i}
+    nkBracket,            # syntactic []
+    nkBracketExpr,        # an expression like a[i..j, k]
+    nkPragmaExpr,         # an expression like a{.pragmas.}
+    nkRange,              # an expression like i..j
+    nkDotExpr,            # a.b
+    nkCheckedFieldExpr,   # a.b, but b is a field that needs to be checked
+    nkDerefExpr,          # a^
+    nkIfExpr,             # if as an expression
+    nkElifExpr,
+    nkElseExpr,
+    nkLambda,             # lambda expression
+    nkDo,                 # lambda block appering as trailing proc param
+    nkAccQuoted,          # `a` as a node
+
+    nkTableConstr,        # a table constructor {expr: expr}
+    nkBind,               # ``bind expr`` node
+    nkClosedSymChoice,    # symbol choice node; a list of nkSyms (closed)
+    nkOpenSymChoice,      # symbol choice node; a list of nkSyms (open)
+    nkHiddenStdConv,      # an implicit standard type conversion
+    nkHiddenSubConv,      # an implicit type conversion from a subtype
+                          # to a supertype
+    nkConv,               # a type conversion
+    nkCast,               # a type cast
+    nkStaticExpr,         # a static expr
+    nkAddr,               # a addr expression
+    nkHiddenAddr,         # implicit address operator
+    nkHiddenDeref,        # implicit ^ operator
+    nkObjDownConv,        # down conversion between object types
+    nkObjUpConv,          # up conversion between object types
+    nkChckRangeF,         # range check for floats
+    nkChckRange64,        # range check for 64 bit ints
+    nkChckRange,          # range check for ints
+    nkStringToCString,    # string to cstring
+    nkCStringToString,    # cstring to string
+                          # end of expressions
+
+    nkAsgn,               # a = b
+    nkFastAsgn,           # internal node for a fast ``a = b``
+                          # (no string copy)
+    nkGenericParams,      # generic parameters
+    nkFormalParams,       # formal parameters
+    nkOfInherit,          # inherited from symbol
+
+    nkImportAs,           # a 'as' b in an import statement
+    nkProcDef,            # a proc
+    nkMethodDef,          # a method
+    nkConverterDef,       # a converter
+    nkMacroDef,           # a macro
+    nkTemplateDef,        # a template
+    nkIteratorDef,        # an iterator
+
+    nkOfBranch,           # used inside case statements
+                          # for (cond, action)-pairs
+    nkElifBranch,         # used in if statements
+    nkExceptBranch,       # an except section
+    nkElse,               # an else part
+    nkAsmStmt,            # an assembler block
+    nkPragma,             # a pragma statement
+    nkPragmaBlock,        # a pragma with a block
+    nkIfStmt,             # an if statement
+    nkWhenStmt,           # a when expression or statement
+    nkForStmt,            # a for statement
+    nkParForStmt,         # a parallel for statement
+    nkWhileStmt,          # a while statement
+    nkCaseStmt,           # a case statement
+    nkTypeSection,        # a type section (consists of type definitions)
+    nkVarSection,         # a var section
+    nkLetSection,         # a let section
+    nkConstSection,       # a const section
+    nkConstDef,           # a const definition
+    nkTypeDef,            # a type definition
+    nkYieldStmt,          # the yield statement as a tree
+    nkDefer,              # the 'defer' statement
+    nkTryStmt,            # a try statement
+    nkFinally,            # a finally section
+    nkRaiseStmt,          # a raise statement
+    nkReturnStmt,         # a return statement
+    nkBreakStmt,          # a break statement
+    nkContinueStmt,       # a continue statement
+    nkBlockStmt,          # a block statement
+    nkStaticStmt,         # a static statement
+    nkDiscardStmt,        # a discard statement
+    nkStmtList,           # a list of statements
+    nkImportStmt,         # an import statement
+    nkImportExceptStmt,   # an import x except a statement
+    nkExportStmt,         # an export statement
+    nkExportExceptStmt,   # an 'export except' statement
+    nkFromStmt,           # a from * import statement
+    nkIncludeStmt,        # an include statement
+    nkBindStmt,           # a bind statement
+    nkMixinStmt,          # a mixin statement
+    nkUsingStmt,          # an using statement
+    nkCommentStmt,        # a comment statement
+    nkStmtListExpr,       # a statement list followed by an expr; this is used
+                          # to allow powerful multi-line templates
+    nkBlockExpr,          # a statement block ending in an expr; this is used
+                          # to allow powerful multi-line templates that open a
+                          # temporary scope
+    nkStmtListType,       # a statement list ending in a type; for macros
+    nkBlockType,          # a statement block ending in a type; for macros
+                          # types as syntactic trees:
+
+    nkWith,               # distinct with `foo`
+    nkWithout,            # distinct without `foo`
+
+    nkTypeOfExpr,         # type(1+2)
+    nkObjectTy,           # object body
+    nkTupleTy,            # tuple body
+    nkTupleClassTy,       # tuple type class
+    nkTypeClassTy,        # user-defined type class
+    nkStaticTy,           # ``static[T]``
+    nkRecList,            # list of object parts
+    nkRecCase,            # case section of object
+    nkRecWhen,            # when section of object
+    nkRefTy,              # ``ref T``
+    nkPtrTy,              # ``ptr T``
+    nkVarTy,              # ``var T``
+    nkConstTy,            # ``const T``
+    nkOutTy,              # ``out T``
+    nkDistinctTy,         # distinct type
+    nkProcTy,             # proc type
+    nkIteratorTy,         # iterator type
+    nkSinkAsgn,           # '=sink(x, y)'
+    nkEnumTy,             # enum body
+    nkEnumFieldDef,       # `ident = expr` in an enumeration
+    nkArgList,            # argument list
+    nkPattern,            # a special pattern; used for matching
+    nkHiddenTryStmt,      # a hidden try statement
+    nkClosure,            # (prc, env)-pair (internally used for code gen)
+    nkGotoState,          # used for the state machine (for iterators)
+    nkState,              # give a label to a code section (for iterators)
+    nkBreakState,         # special break statement for easier code generation
+    nkFuncDef,            # a func
+    nkTupleConstr         # a tuple constructor
+    nkError               # erroneous AST node
+    nkModuleRef           # for .rod file support: A (moduleId, itemId) pair
+    nkReplayAction        # for .rod file support: A replay action
+    nkNilRodNode          # for .rod file support: a 'nil' PNode
+    nkOpenSym             # container for captured sym that can be overriden by local symbols
+
+const
+  nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
+                  nkCommand, nkCallStrLit, nkHiddenCallConv}
diff --git a/compiler/nversion.nim b/compiler/nversion.nim
index 4b8cf7100..811008989 100644
--- a/compiler/nversion.nim
+++ b/compiler/nversion.nim
@@ -12,9 +12,11 @@
 
 const
   MaxSetElements* = 1 shl 16  # (2^16) to support unicode character sets?
+  DefaultSetElements* = 1 shl 8
+    ## assumed set element count when using int literals
   VersionAsString* = system.NimVersion
   RodFileVersion* = "1223"       # modify this if the rod-format changes!
 
-  NimCompilerApiVersion* = 2 ## Check for the existance of this before accessing it
+  NimCompilerApiVersion* = 3 ## Check for the existence of this before accessing it
                              ## as older versions of the compiler API do not
                              ## declare this.
diff --git a/compiler/optimizer.nim b/compiler/optimizer.nim
new file mode 100644
index 000000000..34e8ec80f
--- /dev/null
+++ b/compiler/optimizer.nim
@@ -0,0 +1,290 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Optimizer:
+## - elide 'wasMoved(x); destroy(x)' pairs
+## - recognize "all paths lead to 'wasMoved(x)'"
+
+import
+  ast, renderer, idents
+
+from trees import exprStructuralEquivalent
+
+import std/[strutils, intsets]
+
+const
+  nfMarkForDeletion = nfNone # faster than a lookup table
+
+type
+  BasicBlock = object
+    wasMovedLocs: seq[PNode]
+    kind: TNodeKind
+    hasReturn, hasBreak: bool
+    label: PSym # can be nil
+    parent: ptr BasicBlock
+
+  Con = object
+    somethingTodo: bool
+    inFinally: int
+
+proc nestedBlock(parent: var BasicBlock; kind: TNodeKind): BasicBlock =
+  BasicBlock(wasMovedLocs: @[], kind: kind, hasReturn: false, hasBreak: false,
+    label: nil, parent: addr(parent))
+
+proc breakStmt(b: var BasicBlock; n: PNode) =
+  var it = addr(b)
+  while it != nil:
+    it.wasMovedLocs.setLen 0
+    it.hasBreak = true
+
+    if n.kind == nkSym:
+      if it.label == n.sym: break
+    else:
+      # unnamed break leaves the block is nkWhileStmt or the like:
+      if it.kind in {nkWhileStmt, nkBlockStmt, nkBlockExpr}: break
+
+    it = it.parent
+
+proc returnStmt(b: var BasicBlock) =
+  b.hasReturn = true
+  var it = addr(b)
+  while it != nil:
+    it.wasMovedLocs.setLen 0
+    it = it.parent
+
+proc mergeBasicBlockInfo(parent: var BasicBlock; this: BasicBlock) {.inline.} =
+  if this.hasReturn:
+    parent.wasMovedLocs.setLen 0
+    parent.hasReturn = true
+
+proc wasMovedTarget(matches: var IntSet; branch: seq[PNode]; moveTarget: PNode): bool =
+  result = false
+  for i in 0..<branch.len:
+    if exprStructuralEquivalent(branch[i][1].skipHiddenAddr, moveTarget,
+                                strictSymEquality = true):
+      result = true
+      matches.incl i
+
+proc intersect(summary: var seq[PNode]; branch: seq[PNode]) =
+  # keep all 'wasMoved(x)' calls in summary that are also in 'branch':
+  var i = 0
+  var matches = initIntSet()
+  while i < summary.len:
+    if wasMovedTarget(matches, branch, summary[i][1].skipHiddenAddr):
+      inc i
+    else:
+      summary.del i
+  for m in matches:
+    summary.add branch[m]
+
+
+proc invalidateWasMoved(c: var BasicBlock; x: PNode) =
+  var i = 0
+  while i < c.wasMovedLocs.len:
+    if exprStructuralEquivalent(c.wasMovedLocs[i][1].skipHiddenAddr, x,
+                                strictSymEquality = true):
+      c.wasMovedLocs.del i
+    else:
+      inc i
+
+proc wasMovedDestroyPair(c: var Con; b: var BasicBlock; d: PNode) =
+  var i = 0
+  while i < b.wasMovedLocs.len:
+    if exprStructuralEquivalent(b.wasMovedLocs[i][1].skipHiddenAddr, d[1].skipHiddenAddr,
+                                strictSymEquality = true):
+      b.wasMovedLocs[i].flags.incl nfMarkForDeletion
+      c.somethingTodo = true
+      d.flags.incl nfMarkForDeletion
+      b.wasMovedLocs.del i
+    else:
+      inc i
+
+proc analyse(c: var Con; b: var BasicBlock; n: PNode) =
+  case n.kind
+  of nkCallKinds:
+    var special = false
+    var reverse = false
+    if n[0].kind == nkSym:
+      let s = n[0].sym
+      let name = s.name.s.normalize
+      if name == "=wasmoved":
+        b.wasMovedLocs.add n
+        special = true
+      elif name == "=destroy":
+        if c.inFinally > 0 and (b.hasReturn or b.hasBreak):
+          discard "cannot optimize away the destructor"
+        else:
+          c.wasMovedDestroyPair b, n
+        special = true
+      elif name == "=sink":
+        reverse = true
+
+    if not special:
+      if not reverse:
+        for i in 0 ..< n.len:
+          analyse(c, b, n[i])
+      else:
+        #[ Test destructor/tmatrix.test3:
+        Prevent this from being elided. We should probably
+        find a better solution...
+
+            `=sink`(b, -
+              let blitTmp = b;
+              wasMoved(b);
+              blitTmp + a)
+            `=destroy`(b)
+
+        ]#
+        for i in countdown(n.len-1, 0):
+          analyse(c, b, n[i])
+      if canRaise(n[0]): returnStmt(b)
+
+  of nkSym:
+    # any usage of the location before destruction implies we
+    # cannot elide the 'wasMoved(x)':
+    b.invalidateWasMoved n
+
+  of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
+      nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
+      nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
+      nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
+      nkTypeOfExpr, nkMixinStmt, nkBindStmt:
+    discard "do not follow the construct"
+
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
+    # reverse order, see remark for `=sink`:
+    analyse(c, b, n[1])
+    analyse(c, b, n[0])
+
+  of nkIfStmt, nkIfExpr:
+    let isExhaustive = n[^1].kind in {nkElse, nkElseExpr}
+    var wasMovedSet: seq[PNode] = @[]
+
+    for i in 0 ..< n.len:
+      var branch = nestedBlock(b, n[i].kind)
+
+      analyse(c, branch, n[i])
+      mergeBasicBlockInfo(b, branch)
+      if isExhaustive:
+        if i == 0:
+          wasMovedSet = move(branch.wasMovedLocs)
+        else:
+          wasMovedSet.intersect(branch.wasMovedLocs)
+    for i in 0..<wasMovedSet.len:
+      b.wasMovedLocs.add wasMovedSet[i]
+
+  of nkCaseStmt:
+    let isExhaustive = skipTypes(n[0].typ,
+      abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString, tyCstring} or
+      n[^1].kind == nkElse
+
+    analyse(c, b, n[0])
+
+    var wasMovedSet: seq[PNode] = @[]
+
+    for i in 1 ..< n.len:
+      var branch = nestedBlock(b, n[i].kind)
+
+      analyse(c, branch, n[i])
+      mergeBasicBlockInfo(b, branch)
+      if isExhaustive:
+        if i == 1:
+          wasMovedSet = move(branch.wasMovedLocs)
+        else:
+          wasMovedSet.intersect(branch.wasMovedLocs)
+    for i in 0..<wasMovedSet.len:
+      b.wasMovedLocs.add wasMovedSet[i]
+
+  of nkTryStmt:
+    for i in 0 ..< n.len:
+      var tryBody = nestedBlock(b, nkTryStmt)
+
+      analyse(c, tryBody, n[i])
+      mergeBasicBlockInfo(b, tryBody)
+
+  of nkWhileStmt:
+    analyse(c, b, n[0])
+    var loopBody = nestedBlock(b, nkWhileStmt)
+    analyse(c, loopBody, n[1])
+    mergeBasicBlockInfo(b, loopBody)
+
+  of nkBlockStmt, nkBlockExpr:
+    var blockBody = nestedBlock(b, n.kind)
+    if n[0].kind == nkSym:
+      blockBody.label = n[0].sym
+    analyse(c, blockBody, n[1])
+    mergeBasicBlockInfo(b, blockBody)
+
+  of nkBreakStmt:
+    breakStmt(b, n[0])
+
+  of nkReturnStmt, nkRaiseStmt:
+    for child in n: analyse(c, b, child)
+    returnStmt(b)
+
+  of nkFinally:
+    inc c.inFinally
+    for child in n: analyse(c, b, child)
+    dec c.inFinally
+
+  else:
+    for child in n: analyse(c, b, child)
+
+proc opt(c: Con; n, parent: PNode; parentPos: int) =
+  template recurse() =
+    let x = shallowCopy(n)
+    for i in 0 ..< n.len:
+      opt(c, n[i], x, i)
+    parent[parentPos] = x
+
+  case n.kind
+  of nkCallKinds:
+    if nfMarkForDeletion in n.flags:
+      parent[parentPos] = newNodeI(nkEmpty, n.info)
+    else:
+      recurse()
+
+  of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
+      nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
+      nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
+      nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr,
+      nkMixinStmt, nkBindStmt:
+    parent[parentPos] = n
+
+  else:
+    recurse()
+
+
+proc optimize*(n: PNode): PNode =
+  # optimize away simple 'wasMoved(x); destroy(x)' pairs.
+  #[ Unfortunately this optimization is only really safe when no exceptions
+     are possible, see for example:
+
+  proc main(inp: string; cond: bool) =
+    if cond:
+      try:
+        var s = ["hi", inp & "more"]
+        for i in 0..4:
+          use s
+        consume(s)
+        wasMoved(s)
+      finally:
+        destroy(s)
+
+    Now assume 'use' raises, then we shouldn't do the 'wasMoved(s)'
+  ]#
+  var c: Con = Con()
+  var b: BasicBlock = default(BasicBlock)
+  analyse(c, b, n)
+  if c.somethingTodo:
+    result = shallowCopy(n)
+    for i in 0 ..< n.safeLen:
+      opt(c, n[i], result, i)
+  else:
+    result = n
diff --git a/compiler/options.nim b/compiler/options.nim
index 1d6ddb09f..b77bdd2a3 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -8,132 +8,269 @@
 #
 
 import
-  os, strutils, strtabs, osproc, sets, lineinfos, platform,
-  prefixmatches
+  lineinfos, platform,
+  prefixmatches, pathutils, nimpaths
+
+import std/[tables, os, strutils, strtabs, sets]
+from std/terminal import isatty
+from std/times import utc, fromUnix, local, getTime, format, DateTime
+from std/private/globs import nativeToUnixPath
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions]
 
-from terminal import isatty
 
 const
   hasTinyCBackend* = defined(tinyc)
   useEffectSystem* = true
   useWriteTracking* = false
-  hasFFI* = defined(useFFI)
-  newScopeForIf* = true
-  useCaas* = not defined(noCaas)
-  copyrightYear* = "2018"
+  hasFFI* = defined(nimHasLibFFI)
+  copyrightYear* = "2024"
+
+  nimEnableCovariance* = defined(nimEnableCovariance)
 
 type                          # please make sure we have under 32 options
                               # (improves code efficiency a lot!)
   TOption* = enum             # **keep binary compatible**
     optNone, optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck,
-    optOverflowCheck, optNilCheck,
-    optNaNCheck, optInfCheck, optMoveCheck,
+    optOverflowCheck, optRefCheck,
+    optNaNCheck, optInfCheck, optStaticBoundsCheck, optStyleCheck,
     optAssert, optLineDir, optWarns, optHints,
-    optOptimizeSpeed, optOptimizeSize, optStackTrace, # stack tracing support
+    optOptimizeSpeed, optOptimizeSize,
+    optStackTrace, # stack tracing support
+    optStackTraceMsgs, # enable custom runtime msgs via `setFrameMsg`
     optLineTrace,             # line tracing support (includes stack tracing)
-    optEndb,                  # embedded debugger
     optByRef,                 # use pass by ref for objects
                               # (for interfacing with C)
     optProfiler,              # profiler turned on
     optImplicitStatic,        # optimization: implicit at compile time
                               # evaluation
-    optPatterns,              # en/disable pattern matching
+    optTrMacros,              # en/disable pattern matching
     optMemTracker,
-    optHotCodeReloading,
-    optLaxStrings
+    optSinkInference          # 'sink T' inference
+    optCursorInference
+    optImportHidden
+    optQuirky
 
   TOptions* = set[TOption]
-  TGlobalOption* = enum       # **keep binary compatible**
+  TGlobalOption* = enum
     gloptNone, optForceFullMake,
-    optDeadCodeElimUnused,    # deprecated, always on
+    optWasNimscript,          # redundant with `cmdNimscript`, could be removed
     optListCmd, optCompileOnly, optNoLinking,
     optCDebug,                # turn on debugging information
     optGenDynLib,             # generate a dynamic library
     optGenStaticLib,          # generate a static library
     optGenGuiApp,             # generate a GUI application
     optGenScript,             # generate a script file to compile the *.c files
+    optGenCDeps,              # generate a list of *.c files to be read by CMake
     optGenMapping,            # generate a mapping file
     optRun,                   # run the compiled project
-    optCheckNep1,             # check that the names adhere to NEP-1
-    optSkipConfigFile,        # skip the general config file
-    optSkipProjConfigFile,    # skip the project's config file
-    optSkipUserConfigFile,    # skip the users's config file
-    optSkipParentConfigFiles, # skip parent dir's config files
+    optUseNimcache,           # save artifacts (including binary) in $nimcache
+    optStyleHint,             # check that the names adhere to NEP-1
+    optStyleError,            # enforce that the names adhere to NEP-1
+    optStyleUsages,           # only enforce consistent **usages** of the symbol
+    optSkipSystemConfigFile,  # skip the system's cfg/nims config file
+    optSkipProjConfigFile,    # skip the project's cfg/nims config file
+    optSkipUserConfigFile,    # skip the users's cfg/nims config file
+    optSkipParentConfigFiles, # skip parent dir's cfg/nims config files
     optNoMain,                # do not generate a "main" proc
     optUseColors,             # use colors for hints, warnings, and errors
     optThreads,               # support for multi-threading
     optStdout,                # output to stdout
     optThreadAnalysis,        # thread analysis pass
-    optTaintMode,             # taint mode turned on
     optTlsEmulation,          # thread var emulation turned on
     optGenIndex               # generate index file for documentation;
+    optGenIndexOnly           # generate only index file for documentation
+    optNoImportdoc            # disable loading external documentation files
     optEmbedOrigSrc           # embed the original source in the generated code
                               # also: generate header file
     optIdeDebug               # idetools: debug mode
     optIdeTerse               # idetools: use terse descriptions
-    optNoCppExceptions        # use C exception handling even with CPP
+    optIdeExceptionInlayHints
     optExcessiveStackTrace    # fully qualified module filenames
-    optWholeProject           # for 'doc2': output any dependency
+    optShowAllMismatches      # show all overloading resolution candidates
+    optWholeProject           # for 'doc': output any dependency
+    optDocInternal            # generate documentation for non-exported symbols
     optMixedMode              # true if some module triggered C++ codegen
-    optListFullPaths
+    optDeclaredLocs           # show declaration locations in messages
     optNoNimblePath
+    optHotCodeReloading
     optDynlibOverrideAll
-    optUseNimNamespace
+    optSeqDestructors         # active if the implementation uses the new
+                              # string/seq implementation based on destructors
+    optTinyRtti               # active if we use the new "tiny RTTI"
+                              # implementation
+    optOwnedRefs              # active if the Nim compiler knows about 'owned'.
+    optMultiMethods
+    optBenchmarkVM            # Enables cpuTime() in the VM
+    optProduceAsm             # produce assembler code
+    optPanics                 # turn panics (sysFatal) into a process termination
+    optSourcemap
+    optProfileVM              # enable VM profiler
+    optEnableDeepCopy         # ORC specific: enable 'deepcopy' for all types.
+    optShowNonExportedFields  # for documentation: show fields that are not exported
+    optJsBigInt64             # use bigints for 64-bit integers in JS
 
   TGlobalOptions* = set[TGlobalOption]
 
 const
-  harmlessOptions* = {optForceFullMake, optNoLinking, optRun,
-                      optUseColors, optStdout}
+  harmlessOptions* = {optForceFullMake, optNoLinking, optRun, optUseColors, optStdout}
+  genSubDir* = RelativeDir"nimcache"
+  NimExt* = "nim"
+  RodExt* = "rod"
+  HtmlExt* = "html"
+  JsonExt* = "json"
+  TagsExt* = "tags"
+  TexExt* = "tex"
+  IniExt* = "ini"
+  DefaultConfig* = RelativeFile"nim.cfg"
+  DefaultConfigNims* = RelativeFile"config.nims"
+  DocConfig* = RelativeFile"nimdoc.cfg"
+  DocTexConfig* = RelativeFile"nimdoc.tex.cfg"
+  htmldocsDir* = htmldocsDirname.RelativeDir
+  docRootDefault* = "@default" # using `@` instead of `$` to avoid shell quoting complications
+  oKeepVariableNames* = true
+  spellSuggestSecretSauce* = -1
+
+type
+  TBackend* = enum
+    backendInvalid = "" # for parseEnum
+    backendC = "c"
+    backendCpp = "cpp"
+    backendJs = "js"
+    backendObjc = "objc"
+    # backendNimscript = "nimscript" # this could actually work
+    # backendLlvm = "llvm" # probably not well supported; was cmdCompileToLLVM
+
+  Command* = enum  ## Nim's commands
+    cmdNone # not yet processed command
+    cmdUnknown # command unmapped
+    cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS,
+    cmdCrun # compile and run in nimache
+    cmdTcc # run the project via TCC backend
+    cmdCheck # semantic checking for whole project
+    cmdM     # only compile a single
+    cmdParse # parse a single file (for debugging)
+    cmdRod # .rod to some text representation (for debugging)
+    cmdIdeTools # ide tools (e.g. nimsuggest)
+    cmdNimscript # evaluate nimscript
+    cmdDoc0
+    cmdDoc      # convert .nim doc comments to HTML
+    cmdDoc2tex  # convert .nim doc comments to LaTeX
+    cmdRst2html # convert a reStructuredText file to HTML
+    cmdRst2tex # convert a reStructuredText file to TeX
+    cmdMd2html # convert a Markdown file to HTML
+    cmdMd2tex # convert a Markdown file to TeX
+    cmdJsondoc0
+    cmdJsondoc
+    cmdCtags
+    cmdBuildindex
+    cmdGendepend
+    cmdDump
+    cmdInteractive # start interactive session
+    cmdNop
+    cmdJsonscript # compile a .json build file
+    # old unused: cmdInterpret, cmdDef: def feature (find definition for IDEs)
+
+const
+  cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
+                  cmdCompileToJS, cmdCrun}
+  cmdDocLike* = {cmdDoc0, cmdDoc, cmdDoc2tex, cmdJsondoc0, cmdJsondoc,
+                 cmdCtags, cmdBuildindex}
 
 type
-  TCommands* = enum           # Nim's commands
-                              # **keep binary compatible**
-    cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
-    cmdCompileToJS,
-    cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc,
-    cmdGenDepend, cmdDump,
-    cmdCheck,                 # semantic checking for whole project
-    cmdParse,                 # parse a single file (for debugging)
-    cmdScan,                  # scan a single file (for debugging)
-    cmdIdeTools,              # ide tools
-    cmdDef,                   # def feature (find definition for IDEs)
-    cmdRst2html,              # convert a reStructuredText file to HTML
-    cmdRst2tex,               # convert a reStructuredText file to TeX
-    cmdInteractive,           # start interactive session
-    cmdRun,                   # run the project via TCC backend
-    cmdJsonScript             # compile a .json build file
   TStringSeq* = seq[string]
   TGCMode* = enum             # the selected GC
-    gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcRefc,
-    gcV2, gcGenerational
+    gcUnselected = "unselected"
+    gcNone = "none"
+    gcBoehm = "boehm"
+    gcRegions = "regions"
+    gcArc = "arc"
+    gcOrc = "orc"
+    gcAtomicArc = "atomicArc"
+    gcMarkAndSweep = "markAndSweep"
+    gcHooks = "hooks"
+    gcRefc = "refc"
+    gcGo = "go"
+    # gcRefc and the GCs that follow it use a write barrier,
+    # as far as usesWriteBarrier() is concerned
 
   IdeCmd* = enum
-    ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
-    ideHighlight, ideOutline, ideKnown, ideMsg
+    ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideChkFile, ideMod,
+    ideHighlight, ideOutline, ideKnown, ideMsg, ideProject, ideGlobalSymbols,
+    ideRecompile, ideChanged, ideType, ideDeclaration, ideExpand, ideInlayHints
 
-  Feature* = enum  ## experimental features
-    implicitDeref,
+  Feature* = enum  ## experimental features; DO NOT RENAME THESE!
     dotOperators,
     callOperator,
     parallel,
     destructor,
-    notnil
+    notnil,
+    dynamicBindSym,
+    forLoopMacros, # not experimental anymore; remains here for backwards compatibility
+    caseStmtMacros, # ditto
+    codeReordering,
+    compiletimeFFI,
+      ## This requires building nim with `-d:nimHasLibFFI`
+      ## which itself requires `koch installdeps libffi`, see #10150
+      ## Note: this feature can't be localized with {.push.}
+    vmopsDanger,
+    strictFuncs,
+    views,
+    strictNotNil,
+    overloadableEnums, # deadcode
+    strictEffects,
+    unicodeOperators, # deadcode
+    flexibleOptionalParams,
+    strictDefs,
+    strictCaseObjects,
+    inferGenericTypes,
+    openSym, # remove nfDisabledOpenSym when this is default
+    # alternative to above:
+    genericsOpenSym
+    vtables
+
+  LegacyFeature* = enum
+    allowSemcheckedAstModification,
+      ## Allows to modify a NimNode where the type has already been
+      ## flagged with nfSem. If you actually do this, it will cause
+      ## bugs.
+    checkUnsignedConversions
+      ## Historically and especially in version 1.0.0 of the language
+      ## conversions to unsigned numbers were checked. In 1.0.4 they
+      ## are not anymore.
+    laxEffects
+      ## Lax effects system prior to Nim 2.0.
+    verboseTypeMismatch
+    emitGenerics
+      ## generics are emitted in the module that contains them.
+      ## Useful for libraries that rely on local passC
+    jsNoLambdaLifting
+      ## Old transformation for closures in JS backend
 
   SymbolFilesOption* = enum
-    disabledSf, writeOnlySf, readOnlySf, v2Sf
+    disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest
 
   TSystemCC* = enum
-    ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
-    ccTcc, ccPcc, ccUcc, ccIcl, ccIcc
+    ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccBcc, ccVcc,
+    ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl, ccHipcc, ccNvcc
+
+  ExceptionSystem* = enum
+    excNone,   # no exception system selected yet
+    excSetjmp, # setjmp based exception handling
+    excCpp,    # use C++'s native exception handling
+    excGoto,   # exception handling based on goto (should become the new default for C)
+    excQuirky  # quirky exception handling
 
   CfileFlag* {.pure.} = enum
     Cached,    ## no need to recompile this time
     External   ## file was introduced via .compile pragma
 
   Cfile* = object
-    cname*, obj*: string
-    flags*: set[CFileFlag]
+    nimname*: string
+    cname*, obj*: AbsoluteFile
+    flags*: set[CfileFlag]
+    customArgs*: string
   CfileList* = seq[Cfile]
 
   Suggest* = ref object
@@ -154,73 +291,133 @@ type
     scope*, localUsages*, globalUsages*: int # more usages is better
     tokenLen*: int
     version*: int
+    endLine*: uint16
+    endCol*: int
+    inlayHintInfo*: SuggestInlayHint
+
   Suggestions* = seq[Suggest]
 
-  ConfigRef* = ref object ## every global configuration
+  SuggestInlayHintKind* = enum
+    sihkType = "Type",
+    sihkParameter = "Parameter"
+    sihkException = "Exception"
+
+  SuggestInlayHint* = ref object
+    kind*: SuggestInlayHintKind
+    line*: int                   # Starts at 1
+    column*: int                 # Starts at 0
+    label*: string
+    paddingLeft*: bool
+    paddingRight*: bool
+    allowInsert*: bool
+    tooltip*: string
+
+  ProfileInfo* = object
+    time*: float
+    count*: int
+
+  ProfileData* = ref object
+    data*: TableRef[TLineInfo, ProfileInfo]
+
+  StdOrrKind* = enum
+    stdOrrStdout
+    stdOrrStderr
+
+  FilenameOption* = enum
+    foAbs # absolute path, e.g.: /pathto/bar/foo.nim
+    foRelProject # relative to project path, e.g.: ../foo.nim
+    foCanonical # canonical module name
+    foLegacyRelProj # legacy, shortest of (foAbs, foRelProject)
+    foName # lastPathPart, e.g.: foo.nim
+    foStacktrace # if optExcessiveStackTrace: foAbs else: foName
+
+  ConfigRef* {.acyclic.} = ref object ## every global configuration
                           ## fields marked with '*' are subject to
                           ## the incremental compilation mechanisms
                           ## (+) means "part of the dependency"
+    backend*: TBackend # set via `nim x` or `nim --backend:x`
     target*: Target       # (+)
-    linesCompiled*: int  # all lines that have been compiled
+    linesCompiled*: int   # all lines that have been compiled
     options*: TOptions    # (+)
     globalOptions*: TGlobalOptions # (+)
+    macrosToExpand*: StringTableRef
+    arcToExpand*: StringTableRef
     m*: MsgConfig
+    filenameOption*: FilenameOption # how to render paths in compiler messages
+    unitSep*: string
     evalTemplateCounter*: int
     evalMacroCounter*: int
     exitcode*: int8
-    cmd*: TCommands  # the command
+    cmd*: Command  # raw command parsed as enum
+    cmdInput*: string  # input command
+    projectIsCmd*: bool # whether we're compiling from a command input
+    implicitCmd*: bool # whether some flag triggered an implicit `command`
     selectedGC*: TGCMode       # the selected GC (+)
+    exc*: ExceptionSystem
+    hintProcessingDots*: bool # true for dots, false for filenames
     verbosity*: int            # how verbose the compiler is
     numberOfProcessors*: int   # number of processors
-    evalExpr*: string          # expression for idetools --eval
     lastCmdTime*: float        # when caas is enabled, we measure each command
     symbolFiles*: SymbolFilesOption
+    spellSuggestMax*: int # max number of spelling suggestions for typos
 
     cppDefines*: HashSet[string] # (*)
     headerFile*: string
+    nimbasePattern*: string # pattern to find nimbase.h
     features*: set[Feature]
+    legacyFeatures*: set[LegacyFeature]
     arguments*: string ## the arguments to be passed to the program that
                        ## should be run
-    helpWritten*: bool
     ideCmd*: IdeCmd
-    oldNewlines*: bool
-    cCompiler*: TSystemCC
-    enableNotes*: TNoteKinds
-    disableNotes*: TNoteKinds
+    cCompiler*: TSystemCC # the used compiler
+    modifiedyNotes*: TNoteKinds # notes that have been set/unset from either cmdline/configs
+    cmdlineNotes*: TNoteKinds # notes that have been set/unset from cmdline
     foreignPackageNotes*: TNoteKinds
-    notes*: TNoteKinds
+    notes*: TNoteKinds # notes after resolving all logic(defaults, verbosity)/cmdline/configs
+    warningAsErrors*: TNoteKinds
     mainPackageNotes*: TNoteKinds
     mainPackageId*: int
     errorCounter*: int
     hintCounter*: int
     warnCounter*: int
     errorMax*: int
+    maxLoopIterationsVM*: int ## VM: max iterations of all loops
+    isVmTrace*: bool
     configVars*: StringTableRef
     symbols*: StringTableRef ## We need to use a StringTableRef here as defined
                              ## symbols are always guaranteed to be style
                              ## insensitive. Otherwise hell would break lose.
     packageCache*: StringTableRef
-    searchPaths*: seq[string]
-    lazyPaths*: seq[string]
-    outFile*, prefixDir*, libpath*, nimcacheDir*: string
-    dllOverrides, moduleOverrides*: StringTableRef
+    nimblePaths*: seq[AbsoluteDir]
+    searchPaths*: seq[AbsoluteDir]
+    lazyPaths*: seq[AbsoluteDir]
+    outFile*: RelativeFile
+    outDir*: AbsoluteDir
+    jsonBuildFile*: AbsoluteFile
+    prefixDir*, libpath*, nimcacheDir*: AbsoluteDir
+    dllOverrides*, moduleOverrides*, cfileSpecificOptions*: StringTableRef
     projectName*: string # holds a name like 'nim'
-    projectPath*: string # holds a path like /home/alice/projects/nim/compiler/
-    projectFull*: string # projectPath/projectName
+    projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/
+    projectFull*: AbsoluteFile # projectPath/projectName
     projectIsStdin*: bool # whether we're compiling from stdin
+    lastMsgWasDot*: set[StdOrrKind] # the last compiler message was a single '.'
     projectMainIdx*: FileIndex # the canonical path id of the main module
+    projectMainIdx2*: FileIndex # consider merging with projectMainIdx
     command*: string # the main command (e.g. cc, check, scan, etc)
     commandArgs*: seq[string] # any arguments after the main command
-    keepComments*: bool # whether the parser needs to keep comments
+    commandLine*: string
+    extraCmds*: seq[string] # for writeJsonBuildInstructions
     implicitImports*: seq[string] # modules that are to be implicitly imported
     implicitIncludes*: seq[string] # modules that are to be implicitly included
     docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \
     # The string uses the formatting variables `path` and `line`.
+    docRoot*: string ## see nim --fullhelp for --docRoot
+    docCmd*: string ## see nim --fullhelp for --docCmd
 
-     # the used compiler
-    cIncludes*: seq[string]  # directories to search for included files
-    cLibs*: seq[string]      # directories to search for lib files
-    cLinkedLibs*: seq[string]  # libraries to link
+    configFiles*: seq[AbsoluteFile]     # config files (cfg,nims)
+    cIncludes*: seq[AbsoluteDir]  # directories to search for included files
+    cLibs*: seq[AbsoluteDir]      # directories to search for lib files
+    cLinkedLibs*: seq[string]     # libraries to link
 
     externalToLink*: seq[string]  # files to link in addition to the file
                                   # we compiled (*)
@@ -228,34 +425,101 @@ type
     compileOptionsCmd*: seq[string]
     linkOptions*: string          # (*)
     compileOptions*: string       # (*)
-    ccompilerpath*: string
+    cCompilerPath*: string
     toCompile*: CfileList         # (*)
     suggestionResultHook*: proc (result: Suggest) {.closure.}
     suggestVersion*: int
     suggestMaxResults*: int
     lastLineInfo*: TLineInfo
-    writelnHook*: proc (output: string) {.closure.}
+    writelnHook*: proc (output: string) {.closure, gcsafe.}
     structuredErrorHook*: proc (config: ConfigRef; info: TLineInfo; msg: string;
-                                severity: Severity) {.closure.}
+                                severity: Severity) {.closure, gcsafe.}
+    cppCustomNamespace*: string
+    nimMainPrefix*: string
+    vmProfileData*: ProfileData
+
+    expandProgress*: bool
+    expandLevels*: int
+    expandNodeResult*: string
+    expandPosition*: TLineInfo
+
+    currentConfigDir*: string # used for passPP only; absolute dir
+    clientProcessId*: int
+
+
+
+proc assignIfDefault*[T](result: var T, val: T, def = default(T)) =
+  ## if `result` was already assigned to a value (that wasn't `def`), this is a noop.
+  if result == def: result = val
+
+template setErrorMaxHighMaybe*(conf: ConfigRef) =
+  ## do not stop after first error (but honor --errorMax if provided)
+  assignIfDefault(conf.errorMax, high(int))
+
+proc setNoteDefaults*(conf: ConfigRef, note: TNoteKind, enabled = true) =
+  template fun(op) =
+    conf.notes.op note
+    conf.mainPackageNotes.op note
+    conf.foreignPackageNotes.op note
+  if enabled: fun(incl) else: fun(excl)
 
-template depConfigFields*(fn) {.dirty.} =
-  fn(target)
-  fn(options)
-  fn(globalOptions)
-  fn(selectedGC)
+proc setNote*(conf: ConfigRef, note: TNoteKind, enabled = true) =
+  # see also `prepareConfigNotes` which sets notes
+  if note notin conf.cmdlineNotes:
+    if enabled: incl(conf.notes, note) else: excl(conf.notes, note)
 
-const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel}
+proc hasHint*(conf: ConfigRef, note: TNoteKind): bool =
+  # ternary states instead of binary states would simplify logic
+  if optHints notin conf.options: false
+  elif note in {hintConf, hintProcessing}:
+    # could add here other special notes like hintSource
+    # these notes apply globally.
+    note in conf.mainPackageNotes
+  else: note in conf.notes
+
+proc hasWarn*(conf: ConfigRef, note: TNoteKind): bool {.inline.} =
+  optWarns in conf.options and note in conf.notes
+
+proc hcrOn*(conf: ConfigRef): bool = return optHotCodeReloading in conf.globalOptions
+
+when false:
+  template depConfigFields*(fn) {.dirty.} = # deadcode
+    fn(target)
+    fn(options)
+    fn(globalOptions)
+    fn(selectedGC)
+
+const oldExperimentalFeatures* = {dotOperators, callOperator, parallel}
 
 const
-  ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
+  ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck,
     optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck,
-    optMoveCheck}
+    optStyleCheck}
 
   DefaultOptions* = {optObjCheck, optFieldCheck, optRangeCheck,
-    optBoundsCheck, optOverflowCheck, optAssert, optWarns,
-    optHints, optStackTrace, optLineTrace,
-    optPatterns, optNilCheck, optMoveCheck}
-  DefaultGlobalOptions* = {optThreadAnalysis}
+    optBoundsCheck, optOverflowCheck, optAssert, optWarns, optRefCheck,
+    optHints, optStackTrace, optLineTrace, # consider adding `optStackTraceMsgs`
+    optTrMacros, optStyleCheck, optCursorInference}
+  DefaultGlobalOptions* = {optThreadAnalysis, optExcessiveStackTrace,
+    optJsBigInt64}
+
+proc getSrcTimestamp(): DateTime =
+  try:
+    result = utc(fromUnix(parseInt(getEnv("SOURCE_DATE_EPOCH",
+                                          "not a number"))))
+  except ValueError:
+    # Environment variable malformed.
+    # https://reproducible-builds.org/specs/source-date-epoch/: "If the
+    # value is malformed, the build process SHOULD exit with a non-zero
+    # error code", which this doesn't do. This uses local time, because
+    # that maintains compatibility with existing usage.
+    result = utc getTime()
+
+proc getDateStr*(): string =
+  result = format(getSrcTimestamp(), "yyyy-MM-dd")
+
+proc getClockStr*(): string =
+  result = format(getSrcTimestamp(), "HH:mm:ss")
 
 template newPackageCache*(): untyped =
   newStringTable(when FileSystemCaseSensitive:
@@ -263,42 +527,66 @@ template newPackageCache*(): untyped =
                  else:
                    modeCaseSensitive)
 
+proc newProfileData(): ProfileData =
+  ProfileData(data: newTable[TLineInfo, ProfileInfo]())
+
+const foreignPackageNotesDefault* = {
+  hintProcessing, warnUnknownMagic, hintQuitCalled, hintExecuting, hintUser, warnUser}
+
+proc isDefined*(conf: ConfigRef; symbol: string): bool
+
+when defined(nimDebugUtils):
+  # this allows inserting debugging utilties in all modules that import `options`
+  # with a single switch, which is useful when debugging compiler.
+  import debugutils
+  export debugutils
+
+proc initConfigRefCommon(conf: ConfigRef) =
+  conf.selectedGC = gcUnselected
+  conf.verbosity = 1
+  conf.hintProcessingDots = true
+  conf.options = DefaultOptions
+  conf.globalOptions = DefaultGlobalOptions
+  conf.filenameOption = foAbs
+  conf.foreignPackageNotes = foreignPackageNotesDefault
+  conf.notes = NotesVerbosity[1]
+  conf.mainPackageNotes = NotesVerbosity[1]
+
 proc newConfigRef*(): ConfigRef =
   result = ConfigRef(
-    selectedGC: gcRefc,
     cCompiler: ccGcc,
-    verbosity: 1,
-    options: DefaultOptions,
-    globalOptions: DefaultGlobalOptions,
+    macrosToExpand: newStringTable(modeStyleInsensitive),
+    arcToExpand: newStringTable(modeStyleInsensitive),
     m: initMsgConfig(),
-    evalExpr: "",
-    cppDefines: initSet[string](),
-    headerFile: "", features: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic,
-    hintQuitCalled, hintExecuting},
-    notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1],
+    cppDefines: initHashSet[string](),
+    headerFile: "", features: {}, legacyFeatures: {},
     configVars: newStringTable(modeStyleInsensitive),
     symbols: newStringTable(modeStyleInsensitive),
     packageCache: newPackageCache(),
     searchPaths: @[],
     lazyPaths: @[],
-    outFile: "", prefixDir: "", libpath: "", nimcacheDir: "",
+    outFile: RelativeFile"",
+    outDir: AbsoluteDir"",
+    prefixDir: AbsoluteDir"",
+    libpath: AbsoluteDir"", nimcacheDir: AbsoluteDir"",
     dllOverrides: newStringTable(modeCaseInsensitive),
     moduleOverrides: newStringTable(modeStyleInsensitive),
+    cfileSpecificOptions: newStringTable(modeCaseSensitive),
     projectName: "", # holds a name like 'nim'
-    projectPath: "", # holds a path like /home/alice/projects/nim/compiler/
-    projectFull: "", # projectPath/projectName
+    projectPath: AbsoluteDir"", # holds a path like /home/alice/projects/nim/compiler/
+    projectFull: AbsoluteFile"", # projectPath/projectName
     projectIsStdin: false, # whether we're compiling from stdin
     projectMainIdx: FileIndex(0'i32), # the canonical path id of the main module
     command: "", # the main command (e.g. cc, check, scan, etc)
     commandArgs: @[], # any arguments after the main command
-    keepComments: true, # whether the parser needs to keep comments
+    commandLine: "",
     implicitImports: @[], # modules that are to be implicitly imported
     implicitIncludes: @[], # modules that are to be implicitly included
     docSeeSrcUrl: "",
     cIncludes: @[],   # directories to search for included files
     cLibs: @[],       # directories to search for lib files
     cLinkedLibs: @[],  # libraries to link
-
+    backend: backendInvalid,
     externalToLink: @[],
     linkOptionsCmd: "",
     compileOptionsCmd: @[],
@@ -307,30 +595,34 @@ proc newConfigRef*(): ConfigRef =
     ccompilerpath: "",
     toCompile: @[],
     arguments: "",
-    suggestMaxResults: 10_000
+    suggestMaxResults: 10_000,
+    maxLoopIterationsVM: 10_000_000,
+    vmProfileData: newProfileData(),
+    spellSuggestMax: spellSuggestSecretSauce,
+    currentConfigDir: ""
   )
+  initConfigRefCommon(result)
   setTargetFromSystem(result.target)
   # enable colors by default on terminals
   if terminal.isatty(stderr):
     incl(result.globalOptions, optUseColors)
+  when defined(nimDebugUtils):
+    onNewConfigRef(result)
 
 proc newPartialConfigRef*(): ConfigRef =
   ## create a new ConfigRef that is only good enough for error reporting.
-  result = ConfigRef(
-    selectedGC: gcRefc,
-    verbosity: 1,
-    options: DefaultOptions,
-    globalOptions: DefaultGlobalOptions,
-    foreignPackageNotes: {hintProcessing, warnUnknownMagic,
-    hintQuitCalled, hintExecuting},
-    notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1])
+  when defined(nimDebugUtils):
+    result = getConfigRef()
+  else:
+    result = ConfigRef()
+    initConfigRefCommon(result)
 
 proc cppDefine*(c: ConfigRef; define: string) =
   c.cppDefines.incl define
 
 proc isDefined*(conf: ConfigRef; symbol: string): bool =
   if conf.symbols.hasKey(symbol):
-    result = conf.symbols[symbol] != "false"
+    result = true
   elif cmpIgnoreStyle(symbol, CPU[conf.target.targetCPU].name) == 0:
     result = true
   elif cmpIgnoreStyle(symbol, platform.OS[conf.target.targetOS].name) == 0:
@@ -344,20 +636,33 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool =
       result = conf.target.targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
                             osQnx, osAtari, osAix,
                             osHaiku, osVxWorks, osSolaris, osNetbsd,
-                            osFreebsd, osOpenbsd, osDragonfly, osMacosx,
-                            osAndroid}
+                            osFreebsd, osOpenbsd, osDragonfly, osMacosx, osIos,
+                            osAndroid, osNintendoSwitch, osFreeRTOS, osCrossos, osZephyr, osNuttX}
     of "linux":
       result = conf.target.targetOS in {osLinux, osAndroid}
     of "bsd":
-      result = conf.target.targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly}
+      result = conf.target.targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osCrossos}
+    of "freebsd":
+      result = conf.target.targetOS in {osFreebsd, osCrossos}
     of "emulatedthreadvars":
       result = platform.OS[conf.target.targetOS].props.contains(ospLacksThreadVars)
     of "msdos": result = conf.target.targetOS == osDos
     of "mswindows", "win32": result = conf.target.targetOS == osWindows
-    of "macintosh": result = conf.target.targetOS in {osMacos, osMacosx}
+    of "macintosh":
+      result = conf.target.targetOS in {osMacos, osMacosx, osIos}
+    of "osx", "macosx":
+      result = conf.target.targetOS in {osMacosx, osIos}
     of "sunos": result = conf.target.targetOS == osSolaris
-    of "littleendian": result = CPU[conf.target.targetCPU].endian == platform.littleEndian
-    of "bigendian": result = CPU[conf.target.targetCPU].endian == platform.bigEndian
+    of "nintendoswitch":
+      result = conf.target.targetOS == osNintendoSwitch
+    of "freertos", "lwip":
+      result = conf.target.targetOS == osFreeRTOS
+    of "zephyr":
+      result = conf.target.targetOS == osZephyr
+    of "nuttx":
+      result = conf.target.targetOS == osNuttX
+    of "littleendian": result = CPU[conf.target.targetCPU].endian == littleEndian
+    of "bigendian": result = CPU[conf.target.targetCPU].endian == bigEndian
     of "cpu8": result = CPU[conf.target.targetCPU].bit == 8
     of "cpu16": result = CPU[conf.target.targetCPU].bit == 16
     of "cpu32": result = CPU[conf.target.targetCPU].bit == 32
@@ -365,35 +670,25 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool =
     of "nimrawsetjmp":
       result = conf.target.targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd,
                             osDragonfly, osMacosx}
-    else: discard
+    else: result = false
 
-proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in {cmdDoc, cmdIdeTools}
-proc usesNativeGC*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
+template quitOrRaise*(conf: ConfigRef, msg = "") =
+  # xxx in future work, consider whether to also intercept `msgQuit` calls
+  if conf.isDefined("nimDebug"):
+    raiseAssert msg
+  else:
+    quit(msg) # quits with QuitFailure
+
+proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in cmdDocLike + {cmdIdeTools}
+proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
 
 template compilationCachePresent*(conf: ConfigRef): untyped =
-  conf.symbolFiles in {v2Sf, writeOnlySf}
+  false
+#  conf.symbolFiles in {v2Sf, writeOnlySf}
 
 template optPreserveOrigSource*(conf: ConfigRef): untyped =
   optEmbedOrigSrc in conf.globalOptions
 
-const
-  genSubDir* = "nimcache"
-  NimExt* = "nim"
-  RodExt* = "rod"
-  HtmlExt* = "html"
-  JsonExt* = "json"
-  TagsExt* = "tags"
-  TexExt* = "tex"
-  IniExt* = "ini"
-  DefaultConfig* = "nim.cfg"
-  DocConfig* = "nimdoc.cfg"
-  DocTexConfig* = "nimdoc.tex.cfg"
-
-const oKeepVariableNames* = true
-
-template compilingLib*(conf: ConfigRef): bool =
-  gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}
-
 proc mainCommandArg*(conf: ConfigRef): string =
   ## This is intended for commands like check or parse
   ## which will work on the main project file unless
@@ -406,186 +701,318 @@ proc mainCommandArg*(conf: ConfigRef): string =
 proc existsConfigVar*(conf: ConfigRef; key: string): bool =
   result = hasKey(conf.configVars, key)
 
-proc getConfigVar*(conf: ConfigRef; key: string): string =
-  result = conf.configVars.getOrDefault key
+proc getConfigVar*(conf: ConfigRef; key: string, default = ""): string =
+  result = conf.configVars.getOrDefault(key, default)
 
 proc setConfigVar*(conf: ConfigRef; key, val: string) =
   conf.configVars[key] = val
 
-proc getOutFile*(conf: ConfigRef; filename, ext: string): string =
-  if conf.outFile != "": result = conf.outFile
-  else: result = changeFileExt(filename, ext)
-
-proc getPrefixDir*(conf: ConfigRef): string =
+proc getOutFile*(conf: ConfigRef; filename: RelativeFile, ext: string): AbsoluteFile =
+  # explains regression https://github.com/nim-lang/Nim/issues/6583#issuecomment-625711125
+  # Yet another reason why "" should not mean ".";  `""/something` should raise
+  # instead of implying "" == "." as it's bug prone.
+  doAssert conf.outDir.string.len > 0
+  result = conf.outDir / changeFileExt(filename, ext)
+
+proc absOutFile*(conf: ConfigRef): AbsoluteFile =
+  doAssert not conf.outDir.isEmpty
+  doAssert not conf.outFile.isEmpty
+  result = conf.outDir / conf.outFile
+  when defined(posix):
+    if dirExists(result.string): result.string.add ".out"
+
+proc prepareToWriteOutput*(conf: ConfigRef): AbsoluteFile =
+  ## Create the output directory and returns a full path to the output file
+  result = conf.absOutFile
+  createDir result.string.parentDir
+
+proc getPrefixDir*(conf: ConfigRef): AbsoluteDir =
   ## Gets the prefix dir, usually the parent directory where the binary resides.
   ##
   ## This is overridden by some tools (namely nimsuggest) via the ``conf.prefixDir``
-  ## global.
-  if conf.prefixDir != "": result = conf.prefixDir
-  else: result = splitPath(getAppDir()).head
+  ## field.
+  ## This should resolve to root of nim sources, whether running nim from a local
+  ##  clone or using installed nim, so that these exist: `result/doc/advopt.txt`
+  ## and `result/lib/system.nim`
+  if not conf.prefixDir.isEmpty: result = conf.prefixDir
+  else:
+    let binParent = AbsoluteDir splitPath(getAppDir()).head
+    when defined(posix):
+      if binParent == AbsoluteDir"/usr":
+        result = AbsoluteDir"/usr/lib/nim"
+      elif binParent == AbsoluteDir"/usr/local":
+        result = AbsoluteDir"/usr/local/lib/nim"
+      else:
+        result = binParent
+    else:
+      result = binParent
 
 proc setDefaultLibpath*(conf: ConfigRef) =
   # set default value (can be overwritten):
-  if conf.libpath == "":
+  if conf.libpath.isEmpty:
     # choose default libpath:
     var prefix = getPrefixDir(conf)
-    when defined(posix):
-      if prefix == "/usr": conf.libpath = "/usr/lib/nim"
-      elif prefix == "/usr/local": conf.libpath = "/usr/local/lib/nim"
-      else: conf.libpath = joinPath(prefix, "lib")
-    else: conf.libpath = joinPath(prefix, "lib")
+    conf.libpath = prefix / RelativeDir"lib"
 
     # Special rule to support other tools (nimble) which import the compiler
     # modules and make use of them.
     let realNimPath = findExe("nim")
     # Find out if $nim/../../lib/system.nim exists.
     let parentNimLibPath = realNimPath.parentDir.parentDir / "lib"
-    if not fileExists(conf.libpath / "system.nim") and
-        fileExists(parentNimlibPath / "system.nim"):
-      conf.libpath = parentNimLibPath
-
-proc canonicalizePath*(conf: ConfigRef; path: string): string =
-  # 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 = path.expandFilename
-    for x in walkFiles(result):
-      return x
-  else:
-    result = path.expandFilename
-
-proc shortenDir*(conf: ConfigRef; dir: string): string =
-  ## returns the interesting part of a dir
-  var prefix = conf.projectPath & DirSep
-  if startsWith(dir, prefix):
-    return substr(dir, len(prefix))
-  prefix = getPrefixDir(conf) & DirSep
-  if startsWith(dir, prefix):
-    return substr(dir, len(prefix))
-  result = dir
+    if not fileExists(conf.libpath.string / "system.nim") and
+        fileExists(parentNimLibPath / "system.nim"):
+      conf.libpath = AbsoluteDir parentNimLibPath
+
+proc canonicalizePath*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile =
+  result = AbsoluteFile path.string.expandFilename
+
+proc setFromProjectName*(conf: ConfigRef; projectName: string) =
+  try:
+    conf.projectFull = canonicalizePath(conf, AbsoluteFile projectName)
+  except OSError:
+    conf.projectFull = AbsoluteFile projectName
+  let p = splitFile(conf.projectFull)
+  let dir = if p.dir.isEmpty: AbsoluteDir getCurrentDir() else: p.dir
+  try:
+    conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile dir)
+  except OSError:
+    conf.projectPath = dir
+  conf.projectName = p.name
 
 proc removeTrailingDirSep*(path: string): string =
-  if (len(path) > 0) and (path[len(path) - 1] == DirSep):
-    result = substr(path, 0, len(path) - 2)
+  if (path.len > 0) and (path[^1] == DirSep):
+    result = substr(path, 0, path.len - 2)
   else:
     result = path
 
 proc disableNimblePath*(conf: ConfigRef) =
   incl conf.globalOptions, optNoNimblePath
   conf.lazyPaths.setLen(0)
+  conf.nimblePaths.setLen(0)
+
+proc clearNimblePath*(conf: ConfigRef) =
+  conf.lazyPaths.setLen(0)
+  conf.nimblePaths.setLen(0)
 
 include packagehandling
 
-proc getNimcacheDir*(conf: ConfigRef): string =
-  result = if conf.nimcacheDir.len > 0: conf.nimcacheDir
-           else: shortenDir(conf, conf.projectPath) / genSubDir
+proc getOsCacheDir(): string =
+  when defined(posix):
+    result = getEnv("XDG_CACHE_HOME", getHomeDir() / ".cache") / "nim"
+  else:
+    result = getHomeDir() / genSubDir.string
+
+proc getNimcacheDir*(conf: ConfigRef): AbsoluteDir =
+  proc nimcacheSuffix(conf: ConfigRef): string =
+    if conf.cmd == cmdCheck: "_check"
+    elif isDefined(conf, "release") or isDefined(conf, "danger"): "_r"
+    else: "_d"
+
+  # XXX projectName should always be without a file extension!
+  result =
+    if not conf.nimcacheDir.isEmpty:
+      conf.nimcacheDir
+    elif conf.backend == backendJs:
+      if conf.outDir.isEmpty:
+        conf.projectPath / genSubDir
+      else:
+        conf.outDir / genSubDir
+    else:
+      AbsoluteDir(getOsCacheDir() / splitFile(conf.projectName).name &
+        nimcacheSuffix(conf))
 
 proc pathSubs*(conf: ConfigRef; p, config: string): string =
   let home = removeTrailingDirSep(os.getHomeDir())
   result = unixToNativePath(p % [
-    "nim", getPrefixDir(conf),
-    "lib", conf.libpath,
+    "nim", getPrefixDir(conf).string,
+    "lib", conf.libpath.string,
     "home", home,
     "config", config,
     "projectname", conf.projectName,
-    "projectpath", conf.projectPath,
-    "projectdir", conf.projectPath,
-    "nimcache", getNimcacheDir(conf)])
-  if "~/" in result:
-    result = result.replace("~/", home & '/')
+    "projectpath", conf.projectPath.string,
+    "projectdir", conf.projectPath.string,
+    "nimcache", getNimcacheDir(conf).string]).expandTilde
+
+iterator nimbleSubs*(conf: ConfigRef; p: string): string =
+  let pl = p.toLowerAscii
+  if "$nimblepath" in pl or "$nimbledir" in pl:
+    for i in countdown(conf.nimblePaths.len-1, 0):
+      let nimblePath = removeTrailingDirSep(conf.nimblePaths[i].string)
+      yield p % ["nimblepath", nimblePath, "nimbledir", nimblePath]
+  else:
+    yield p
 
-proc toGeneratedFile*(conf: ConfigRef; path, ext: string): string =
+proc toGeneratedFile*(conf: ConfigRef; path: AbsoluteFile,
+                      ext: string): AbsoluteFile =
   ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
-  var (head, tail) = splitPath(path)
-  #if len(head) > 0: head = shortenDir(head & dirSep)
-  result = joinPath([getNimcacheDir(conf), changeFileExt(tail, ext)])
-  #echo "toGeneratedFile(", path, ", ", ext, ") = ", result
-
-proc completeGeneratedFilePath*(conf: ConfigRef; f: string, createSubDir: bool = true): string =
-  var (head, tail) = splitPath(f)
-  #if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep))
-  var subdir = getNimcacheDir(conf) # / head
+  result = getNimcacheDir(conf) / RelativeFile path.string.splitPath.tail.changeFileExt(ext)
+
+proc completeGeneratedFilePath*(conf: ConfigRef; f: AbsoluteFile,
+                                createSubDir: bool = true): AbsoluteFile =
+  ## Return an absolute path of a generated intermediary file.
+  ## Optionally creates the cache directory if `createSubDir` is `true`.
+  let subdir = getNimcacheDir(conf)
   if createSubDir:
     try:
-      createDir(subdir)
+      createDir(subdir.string)
     except OSError:
-      writeLine(stdout, "cannot create directory: " & subdir)
-      quit(1)
-  result = joinPath(subdir, tail)
-  #echo "completeGeneratedFilePath(", f, ") = ", result
+      conf.quitOrRaise "cannot create directory: " & subdir.string
+  result = subdir / RelativeFile f.string.splitPath.tail
 
-proc rawFindFile(conf: ConfigRef; f: string; suppressStdlib: bool): string =
+proc rawFindFile(conf: ConfigRef; f: RelativeFile; suppressStdlib: bool): AbsoluteFile =
   for it in conf.searchPaths:
-    if suppressStdlib and it.startsWith(conf.libpath):
+    if suppressStdlib and it.string.startsWith(conf.libpath.string):
       continue
-    result = joinPath(it, f)
-    if existsFile(result):
+    result = it / f
+    if fileExists(result):
       return canonicalizePath(conf, result)
-  result = ""
+  result = AbsoluteFile""
 
-proc rawFindFile2(conf: ConfigRef; f: string): string =
+proc rawFindFile2(conf: ConfigRef; f: RelativeFile): AbsoluteFile =
   for i, it in conf.lazyPaths:
-    result = joinPath(it, f)
-    if existsFile(result):
+    result = it / f
+    if fileExists(result):
       # bring to front
-      for j in countDown(i,1):
+      for j in countdown(i, 1):
         swap(conf.lazyPaths[j], conf.lazyPaths[j-1])
 
       return canonicalizePath(conf, result)
-  result = ""
+  result = AbsoluteFile""
 
 template patchModule(conf: ConfigRef) {.dirty.} =
-  if result.len > 0 and conf.moduleOverrides.len > 0:
-    let key = getPackageName(conf, result) & "_" & splitFile(result).name
+  if not result.isEmpty and conf.moduleOverrides.len > 0:
+    let key = getPackageName(conf, result.string) & "_" & splitFile(result).name
     if conf.moduleOverrides.hasKey(key):
       let ov = conf.moduleOverrides[key]
-      if ov.len > 0: result = ov
+      if ov.len > 0: result = AbsoluteFile(ov)
+
+const stdlibDirs* = [
+  "pure", "core", "arch",
+  "pure/collections",
+  "pure/concurrency",
+  "pure/unidecode", "impure",
+  "wrappers", "wrappers/linenoise",
+  "windows", "posix", "js",
+  "deprecated/pure"]
 
-proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): string {.procvar.} =
+const
+  pkgPrefix = "pkg/"
+  stdPrefix* = "std/"
+
+proc getRelativePathFromConfigPath*(conf: ConfigRef; f: AbsoluteFile, isTitle = false): RelativeFile =
+  result = RelativeFile("")
+  let f = $f
+  if isTitle:
+    for dir in stdlibDirs:
+      let path = conf.libpath.string / dir / f.lastPathPart
+      if path.cmpPaths(f) == 0:
+        return RelativeFile(stdPrefix & f.splitFile.name)
+  template search(paths) =
+    for it in paths:
+      let it = $it
+      if f.isRelativeTo(it):
+        return relativePath(f, it).RelativeFile
+  search(conf.searchPaths)
+  search(conf.lazyPaths)
+
+proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): AbsoluteFile =
   if f.isAbsolute:
-    result = if f.existsFile: f else: ""
+    result = if f.fileExists: AbsoluteFile(f) else: AbsoluteFile""
   else:
-    result = rawFindFile(conf, f, suppressStdlib)
-    if result.len == 0:
-      result = rawFindFile(conf, f.toLowerAscii, suppressStdlib)
-      if result.len == 0:
-        result = rawFindFile2(conf, f)
-        if result.len == 0:
-          result = rawFindFile2(conf, f.toLowerAscii)
+    result = rawFindFile(conf, RelativeFile f, suppressStdlib)
+    if result.isEmpty:
+      result = rawFindFile(conf, RelativeFile f.toLowerAscii, suppressStdlib)
+      if result.isEmpty:
+        result = rawFindFile2(conf, RelativeFile f)
+        if result.isEmpty:
+          result = rawFindFile2(conf, RelativeFile f.toLowerAscii)
   patchModule(conf)
 
-proc findModule*(conf: ConfigRef; modulename, currentModule: string): string =
+proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFile =
   # returns path to module
-  const pkgPrefix = "pkg/"
-  let m = addFileExt(modulename, NimExt)
+  var m = addFileExt(modulename, NimExt)
+  var hasRelativeDot = false
   if m.startsWith(pkgPrefix):
     result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true)
   else:
-    let currentPath = currentModule.splitFile.dir
-    result = currentPath / m
-    if not existsFile(result):
+    if m.startsWith(stdPrefix):
+      result = AbsoluteFile("")
+      let stripped = m.substr(stdPrefix.len)
+      for candidate in stdlibDirs:
+        let path = (conf.libpath.string / candidate / stripped)
+        if fileExists(path):
+          result = AbsoluteFile path
+          break
+    else: # If prefixed with std/ why would we add the current module path!
+      let currentPath = currentModule.splitFile.dir
+      result = AbsoluteFile currentPath / m
+      if m.startsWith('.') and not fileExists(result):
+        result = AbsoluteFile ""
+        hasRelativeDot = true
+
+    if not fileExists(result) and not hasRelativeDot:
       result = findFile(conf, m)
   patchModule(conf)
 
 proc findProjectNimFile*(conf: ConfigRef; pkg: string): string =
   const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
-  var candidates: seq[string] = @[]
-  for k, f in os.walkDir(pkg, relative=true):
-    if k == pcFile and f != "config.nims":
-      let (_, name, ext) = splitFile(f)
-      if ext in extensions:
-        let x = changeFileExt(pkg / name, ".nim")
-        if fileExists(x):
-          candidates.add x
-  for c in candidates:
-    # nim-foo foo  or  foo  nfoo
-    if (pkg in c) or (c in pkg): return c
-  if candidates.len >= 1:
-    return candidates[0]
+  var
+    candidates: seq[string] = @[]
+    dir = pkg
+    prev = dir
+    nimblepkg = ""
+  let pkgname = pkg.lastPathPart()
+  while true:
+    for k, f in os.walkDir(dir, relative = true):
+      if k == pcFile and f != "config.nims":
+        let (_, name, ext) = splitFile(f)
+        if ext in extensions:
+          let x = changeFileExt(dir / name, ".nim")
+          if fileExists(x):
+            candidates.add x
+          if ext == ".nimble":
+            if nimblepkg.len == 0:
+              nimblepkg = name
+              # Since nimble packages can have their source in a subfolder,
+              # check the last folder we were in for a possible match.
+              if dir != prev:
+                let x = prev / x.extractFilename()
+                if fileExists(x):
+                  candidates.add x
+            else:
+              # If we found more than one nimble file, chances are that we
+              # missed the real project file, or this is an invalid nimble
+              # package. Either way, bailing is the better choice.
+              return ""
+    let pkgname = if nimblepkg.len > 0: nimblepkg else: pkgname
+    for c in candidates:
+      if pkgname in c.extractFilename(): return c
+    if candidates.len > 0:
+      return candidates[0]
+    prev = dir
+    dir = parentDir(dir)
+    if dir == "": break
   return ""
 
+proc canonicalImportAux*(conf: ConfigRef, file: AbsoluteFile): string =
+  ##[
+  Shows the canonical module import, e.g.:
+  system, std/tables, fusion/pointers, system/assertions, std/private/asciitables
+  ]##
+  var ret = getRelativePathFromConfigPath(conf, file, isTitle = true)
+  let dir = getNimbleFile(conf, $file).parentDir.AbsoluteDir
+  if not dir.isEmpty:
+    let relPath = relativeTo(file, dir)
+    if not relPath.isEmpty and (ret.isEmpty or relPath.string.len < ret.string.len):
+      ret = relPath
+  if ret.isEmpty:
+    ret = relativeTo(file, conf.projectPath)
+  result = ret.string
+
+proc canonicalImport*(conf: ConfigRef, file: AbsoluteFile): string =
+  let ret = canonicalImportAux(conf, file)
+  result = ret.nativeToUnixPath.changeFileExt("")
+
 proc canonDynlibName(s: string): string =
   let start = if s.startsWith("lib"): 3 else: 0
   let ende = strutils.find(s, {'(', ')', '.'})
@@ -601,6 +1028,12 @@ proc isDynlibOverride*(conf: ConfigRef; lib: string): bool =
   result = optDynlibOverrideAll in conf.globalOptions or
      conf.dllOverrides.hasKey(lib.canonDynlibName)
 
+proc showNonExportedFields*(conf: ConfigRef) =
+  incl(conf.globalOptions, optShowNonExportedFields)
+
+proc expandDone*(conf: ConfigRef): bool =
+  result = conf.ideCmd == ideExpand and conf.expandLevels == 0 and conf.expandProgress
+
 proc parseIdeCmd*(s: string): IdeCmd =
   case s:
   of "sug": ideSug
@@ -609,11 +1042,17 @@ proc parseIdeCmd*(s: string): IdeCmd =
   of "use": ideUse
   of "dus": ideDus
   of "chk": ideChk
+  of "chkFile": ideChkFile
   of "mod": ideMod
   of "highlight": ideHighlight
   of "outline": ideOutline
   of "known": ideKnown
   of "msg": ideMsg
+  of "project": ideProject
+  of "globalSymbols": ideGlobalSymbols
+  of "recompile": ideRecompile
+  of "changed": ideChanged
+  of "type": ideType
   else: ideNone
 
 proc `$`*(c: IdeCmd): string =
@@ -624,9 +1063,28 @@ proc `$`*(c: IdeCmd): string =
   of ideUse: "use"
   of ideDus: "dus"
   of ideChk: "chk"
+  of ideChkFile: "chkFile"
   of ideMod: "mod"
   of ideNone: "none"
   of ideHighlight: "highlight"
   of ideOutline: "outline"
   of ideKnown: "known"
   of ideMsg: "msg"
+  of ideProject: "project"
+  of ideGlobalSymbols: "globalSymbols"
+  of ideDeclaration: "declaration"
+  of ideExpand: "expand"
+  of ideRecompile: "recompile"
+  of ideChanged: "changed"
+  of ideType: "type"
+  of ideInlayHints: "inlayHints"
+
+proc floatInt64Align*(conf: ConfigRef): int16 =
+  ## Returns either 4 or 8 depending on reasons.
+  if conf != nil and conf.target.targetCPU == cpuI386:
+    #on Linux/BSD i386, double are aligned to 4bytes (except with -malign-double)
+    if conf.target.targetOS != osWindows:
+      # on i386 for all known POSIX systems, 64bits ints are aligned
+      # to 4bytes (except with -malign-double)
+      return 4
+  return 8
diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim
index 2efab58b0..30f407792 100644
--- a/compiler/packagehandling.nim
+++ b/compiler/packagehandling.nim
@@ -15,10 +15,9 @@ iterator myParentDirs(p: string): string =
     if current.len == 0: break
     yield current
 
-proc resetPackageCache*(conf: ConfigRef) =
-  conf.packageCache = newPackageCache()
-
-proc getPackageName*(conf: ConfigRef; path: string): string =
+proc getNimbleFile*(conf: ConfigRef; path: string): string =
+  ## returns absolute path to nimble file, e.g.: /pathto/cligen.nimble
+  result = ""
   var parents = 0
   block packageSearch:
     for d in myParentDirs(path):
@@ -27,23 +26,19 @@ proc getPackageName*(conf: ConfigRef; path: string): string =
         return conf.packageCache[d]
       inc parents
       for file in walkFiles(d / "*.nimble"):
-        result = file.splitFile.name
-        break packageSearch
-      for file in walkFiles(d / "*.babel"):
-        result = file.splitFile.name
+        result = file
         break packageSearch
   # we also store if we didn't find anything:
-  if result.isNil: result = ""
   for d in myParentDirs(path):
     #echo "set cache ", d, " |", result, "|", parents
     conf.packageCache[d] = result
     dec parents
     if parents <= 0: break
 
-proc withPackageName*(conf: ConfigRef; path: string): string =
-  let x = getPackageName(conf, path)
-  if x.len == 0:
-    result = path
+proc getPackageName*(conf: ConfigRef; path: string): string =
+  ## returns nimble package name, e.g.: `cligen`
+  let path = getNimbleFile(conf, path)
+  if path.len > 0:
+    return path.splitFile.name
   else:
-    let (p, file, ext) = path.splitFile
-    result = (p / (x & '_' & file)) & ext
+    return "unknown"
diff --git a/compiler/packages.nim b/compiler/packages.nim
new file mode 100644
index 000000000..bb54d6154
--- /dev/null
+++ b/compiler/packages.nim
@@ -0,0 +1,53 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2022 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Package related procs.
+##
+## See Also:
+## * `packagehandling` for package path handling
+## * `modulegraphs.getPackage`
+## * `modulegraphs.belongsToStdlib`
+
+import "." / [options, ast, lineinfos, idents, pathutils, msgs]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
+proc getPackage*(conf: ConfigRef; cache: IdentCache; fileIdx: FileIndex): PSym =
+  ## Return a new package symbol.
+  ##
+  ## See Also:
+  ## * `modulegraphs.getPackage`
+  let
+    filename = AbsoluteFile toFullPath(conf, fileIdx)
+    name = getIdent(cache, splitFile(filename).name)
+    info = newLineInfo(fileIdx, 1, 1)
+    pkgName = getPackageName(conf, filename.string)
+    pkgIdent = getIdent(cache, pkgName)
+  newSym(skPackage, pkgIdent, idGeneratorForPackage(int32(fileIdx)), nil, info)
+
+func getPackageSymbol*(sym: PSym): PSym =
+  ## Return the owning package symbol.
+  assert sym != nil
+  result = sym
+  while result.kind != skPackage:
+    result = result.owner
+    assert result != nil, repr(sym.info)
+
+func getPackageId*(sym: PSym): int =
+  ## Return the owning package ID.
+  sym.getPackageSymbol.id
+
+func belongsToProjectPackage*(conf: ConfigRef, sym: PSym): bool =
+  ## Return whether the symbol belongs to the project's package.
+  ##
+  ## See Also:
+  ## * `modulegraphs.belongsToStdlib`
+  conf.mainPackageId == sym.getPackageId
diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim
index 944aec048..e8ec22fe1 100644
--- a/compiler/parampatterns.nim
+++ b/compiler/parampatterns.nim
@@ -10,9 +10,11 @@
 ## This module implements the pattern matching features for term rewriting
 ## macro support.
 
-import strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg, trees,
+import ast, types, msgs, idents, renderer, wordrecg, trees,
   options
 
+import std/strutils
+
 # we precompile the pattern here for efficiency into some internal
 # stack based VM :-) Why? Because it's fun; I did no benchmarks to see if that
 # actually improves performance.
@@ -46,7 +48,7 @@ proc patternError(n: PNode; conf: ConfigRef) =
   localError(conf, n.info, "illformed AST: " & renderTree(n, {renderNoComments}))
 
 proc add(code: var TPatternCode, op: TOpcode) {.inline.} =
-  add(code, chr(ord(op)))
+  code.add chr(ord(op))
 
 proc whichAlias*(p: PSym): TAliasRequest =
   if p.constraint != nil:
@@ -57,29 +59,29 @@ proc whichAlias*(p: PSym): TAliasRequest =
 proc compileConstraints(p: PNode, result: var TPatternCode; conf: ConfigRef) =
   case p.kind
   of nkCallKinds:
-    if p.sons[0].kind != nkIdent:
-      patternError(p.sons[0], conf)
+    if p[0].kind != nkIdent:
+      patternError(p[0], conf)
       return
-    let op = p.sons[0].ident
+    let op = p[0].ident
     if p.len == 3:
       if op.s == "|" or op.id == ord(wOr):
-        compileConstraints(p.sons[1], result, conf)
-        compileConstraints(p.sons[2], result, conf)
+        compileConstraints(p[1], result, conf)
+        compileConstraints(p[2], result, conf)
         result.add(ppOr)
       elif op.s == "&" or op.id == ord(wAnd):
-        compileConstraints(p.sons[1], result, conf)
-        compileConstraints(p.sons[2], result, conf)
+        compileConstraints(p[1], result, conf)
+        compileConstraints(p[2], result, conf)
         result.add(ppAnd)
       else:
         patternError(p, conf)
     elif p.len == 2 and (op.s == "~" or op.id == ord(wNot)):
-      compileConstraints(p.sons[1], result, conf)
+      compileConstraints(p[1], result, conf)
       result.add(ppNot)
     else:
       patternError(p, conf)
   of nkAccQuoted, nkPar:
     if p.len == 1:
-      compileConstraints(p.sons[0], result, conf)
+      compileConstraints(p[0], result, conf)
     else:
       patternError(p, conf)
   of nkIdent:
@@ -99,14 +101,14 @@ proc compileConstraints(p: PNode, result: var TPatternCode; conf: ConfigRef) =
     else:
       # check all symkinds:
       internalAssert conf, int(high(TSymKind)) < 255
-      for i in low(TSymKind)..high(TSymKind):
-        if cmpIgnoreStyle(($i).substr(2), spec) == 0:
+      for i in TSymKind:
+        if cmpIgnoreStyle(i.toHumanStr, spec) == 0:
           result.add(ppSymKind)
           result.add(chr(i.ord))
           return
       # check all nodekinds:
       internalAssert conf, int(high(TNodeKind)) < 255
-      for i in low(TNodeKind)..high(TNodeKind):
+      for i in TNodeKind:
         if cmpIgnoreStyle($i, spec) == 0:
           result.add(ppNodeKind)
           result.add(chr(i.ord))
@@ -115,20 +117,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
@@ -139,13 +140,16 @@ proc checkForSideEffects*(n: PNode): TSideEffectAnalysis =
   case n.kind
   of nkCallKinds:
     # only calls can produce side effects:
-    let op = n.sons[0]
+    let op = n[0]
     if op.kind == nkSym and isRoutine(op.sym):
       let s = op.sym
       if sfSideEffect in s.flags:
         return seSideEffect
-      # assume no side effect:
-      result = seNoSideEffect
+      elif tfNoSideEffect in op.typ.flags:
+        result = seNoSideEffect
+      else:
+        # assume side effect:
+        result = seSideEffect
     elif tfNoSideEffect in op.typ.flags:
       # indirect call without side effects:
       result = seNoSideEffect
@@ -153,8 +157,8 @@ proc checkForSideEffects*(n: PNode): TSideEffectAnalysis =
       # indirect call: assume side effect:
       return seSideEffect
     # we need to check n[0] too: (FwithSideEffectButReturnsProcWithout)(args)
-    for i in 0 ..< n.len:
-      let ret = checkForSideEffects(n.sons[i])
+    for i in 0..<n.len:
+      let ret = checkForSideEffects(n[i])
       if ret == seSideEffect: return ret
       elif ret == seUnknown and result == seNoSideEffect:
         result = seUnknown
@@ -164,8 +168,8 @@ proc checkForSideEffects*(n: PNode): TSideEffectAnalysis =
   else:
     # assume no side effect:
     result = seNoSideEffect
-    for i in 0 ..< n.len:
-      let ret = checkForSideEffects(n.sons[i])
+    for i in 0..<n.len:
+      let ret = checkForSideEffects(n[i])
       if ret == seSideEffect: return ret
       elif ret == seUnknown and result == seNoSideEffect:
         result = seUnknown
@@ -177,9 +181,12 @@ type
     arLocalLValue,            # is an l-value, but local var; must not escape
                               # its stack frame!
     arDiscriminant,           # is a discriminant
+    arAddressableConst,       # an addressable const
+    arLentValue,              # lent value
     arStrange                 # it is a strange beast like 'typedesc[var T]'
 
-proc exprRoot*(n: PNode): PSym =
+proc exprRoot*(n: PNode; allowCalls = true): PSym =
+  result = nil
   var it = n
   while true:
     case it.kind
@@ -200,7 +207,7 @@ proc exprRoot*(n: PNode): PSym =
       if it.len > 0 and it.typ != nil: it = it.lastSon
       else: break
     of nkCallKinds:
-      if it.typ != nil and it.typ.kind == tyVar and it.len > 1:
+      if allowCalls and it.typ != nil and it.typ.kind in {tyVar, tyLent} and it.len > 1:
         # See RFC #7373, calls returning 'var T' are assumed to
         # return a view into the first argument (if there is one):
         it = it[1]
@@ -209,67 +216,91 @@ proc exprRoot*(n: PNode): PSym =
     else:
       break
 
-proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult =
+proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
   ## 'owner' can be nil!
   result = arNone
   case n.kind
   of nkEmpty:
-    if n.typ != nil and n.typ.kind == tyVar:
+    if n.typ != nil and n.typ.kind in {tyVar}:
       result = arLValue
   of nkSym:
-    let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet}
-                else: {skVar, skResult, skTemp}
-    if n.sym.kind in kinds:
-      if owner != nil and owner == n.sym.owner and
-          sfGlobal notin n.sym.flags:
-        result = arLocalLValue
+    const kinds = {skVar, skResult, skTemp, skParam, skLet, skForVar}
+    if n.sym.kind == skParam:
+      result = if n.sym.typ.kind in {tyVar, tySink}: arLValue else: arAddressableConst
+    elif n.sym.kind == skConst and dontInlineConstant(n, n.sym.astdef):
+      result = arAddressableConst
+    elif n.sym.kind in kinds:
+      if n.sym.kind in {skParam, skLet, skForVar}:
+        result = arAddressableConst
       else:
-        result = arLValue
-    elif n.sym.kind == skParam and n.sym.typ.kind == tyVar:
-      result = arLValue
+        if owner != nil and owner == n.sym.owner and
+            sfGlobal notin n.sym.flags:
+          result = arLocalLValue
+        else:
+          result = arLValue
     elif n.sym.kind == skType:
       let t = n.sym.typ.skipTypes({tyTypeDesc})
-      if t.kind == tyVar: result = arStrange
+      if t.kind in {tyVar}: result = arStrange
   of nkDotExpr:
-    if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in
-        {tyVar, tyPtr, tyRef}:
+    let t = skipTypes(n[0].typ, abstractInst-{tyTypeDesc})
+    if t.kind in {tyVar, tySink, tyPtr, tyRef}:
       result = arLValue
+    elif t.kind == tyLent:
+      result = arAddressableConst
     else:
-      result = isAssignable(owner, n.sons[0], isUnsafeAddr)
+      result = isAssignable(owner, n[0])
     if result != arNone and n[1].kind == nkSym and
         sfDiscriminant in n[1].sym.flags:
       result = arDiscriminant
   of nkBracketExpr:
-    if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in
-        {tyVar, tyPtr, tyRef}:
+    let t = skipTypes(n[0].typ, abstractInst-{tyTypeDesc})
+    if t.kind in {tyVar, tySink, tyPtr, tyRef}:
       result = arLValue
+    elif t.kind == tyLent:
+      result = arAddressableConst
     else:
-      result = isAssignable(owner, n.sons[0], isUnsafeAddr)
+      result = isAssignable(owner, n[0])
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
     # Object and tuple conversions are still addressable, so we skip them
     # XXX why is 'tyOpenArray' allowed here?
     if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in
         {tyOpenArray, tyTuple, tyObject}:
-      result = isAssignable(owner, n.sons[1], isUnsafeAddr)
-    elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct):
+      result = isAssignable(owner, n[1])
+    elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct, {IgnoreRangeShallow}):
       # types that are equal modulo distinction preserve l-value:
-      result = isAssignable(owner, n.sons[1], isUnsafeAddr)
+      result = isAssignable(owner, n[1])
   of nkHiddenDeref:
-    if n[0].typ.kind == tyLent: result = arDiscriminant
-    else: result = arLValue
+    let n0 = n[0]
+    if n0.typ.kind == tyLent:
+      if n0.kind == nkSym and n0.sym.kind == skResult:
+        result = arLValue
+      else:
+        result = arLentValue
+    else:
+      result = arLValue
   of nkDerefExpr, nkHiddenAddr:
     result = arLValue
   of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
-    result = isAssignable(owner, n.sons[0], isUnsafeAddr)
+    result = isAssignable(owner, n[0])
   of nkCallKinds:
-    # builtin slice keeps lvalue-ness:
-    if getMagic(n) in {mArrGet, mSlice}:
-      result = isAssignable(owner, n.sons[1], isUnsafeAddr)
-    elif n.typ != nil and n.typ.kind == tyVar:
-      result = arLValue
+    let m = getMagic(n)
+    if m == mSlice:
+      # builtin slice keeps l-value-ness
+      # except for pointers because slice dereferences
+      if n[1].typ.kind == tyPtr:
+        result = arLValue
+      else:
+        result = isAssignable(owner, n[1])
+    elif m == mArrGet:
+      result = isAssignable(owner, n[1])
+    elif n.typ != nil:
+      case n.typ.kind
+      of tyVar: result = arLValue
+      of tyLent: result = arLentValue
+      else: discard
   of nkStmtList, nkStmtListExpr:
     if n.typ != nil:
-      result = isAssignable(owner, n.lastSon, isUnsafeAddr)
+      result = isAssignable(owner, n.lastSon)
   of nkVarTy:
     # XXX: The fact that this is here is a bit of a hack.
     # The goal is to allow the use of checks such as "foo(var T)"
diff --git a/compiler/parser.nim b/compiler/parser.nim
index f575f3d7e..747505097 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -16,118 +16,166 @@
 
 
 # In fact the grammar is generated from this file:
-when isMainModule:
+when isMainModule or defined(nimTestGrammar):
   # Leave a note in grammar.txt that it is generated:
   #| # This file is generated by compiler/parser.nim.
-  import pegs
-  var outp = open("doc/grammar.txt", fmWrite)
-  for line in lines("compiler/parser.nim"):
-    if line =~ peg" \s* '#| ' {.*}":
-      outp.write matches[0], "\L"
-  outp.close
+  import std/pegs
+  when defined(nimPreviewSlimSystem):
+    import std/syncio
+
+  proc writeGrammarFile(x: string) =
+    var outp = open(x, fmWrite)
+    for line in lines("compiler/parser.nim"):
+      if line =~ peg" \s* '#| ' {.*}":
+        outp.write matches[0], "\L"
+    outp.close
+
+  when defined(nimTestGrammar):
+    import std/os
+    from ../testament/lib/stdtest/specialpaths import buildDir
+    const newGrammarText = buildDir / "grammar.txt"
+
+    if not dirExists(buildDir):
+      createDir(buildDir)
+
+    writeGrammarFile(newGrammarText)
+
+    proc checkSameGrammar*() =
+      doAssert sameFileContent(newGrammarText, "doc/grammar.txt"),
+              "execute 'nim r compiler/parser.nim' to keep grammar.txt up-to-date"
+  else:
+    writeGrammarFile("doc/grammar.txt")
+    import ".." / tools / grammar_nanny
+    checkGrammarFile()
 
 import
-  llstream, lexer, idents, strutils, ast, astalgo, msgs, options, lineinfos
+  llstream, lexer, idents, msgs, options, lineinfos,
+  pathutils
+
+when not defined(nimCustomAst):
+  import ast
+else:
+  import plugins / customast
+
+import std/strutils
 
-when defined(nimpretty2):
+when defined(nimpretty):
   import layouter
 
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 type
-  TParser* = object            # A TParser object represents a file that
-                               # is being parsed
-    currInd: int               # current indentation level
-    firstTok, strongSpaces: bool # Has the first token been read?
-                                 # Is strongSpaces on?
-    hasProgress: bool          # some while loop requires progress ensurance
-    lex*: TLexer               # The lexer that is used for parsing
-    tok*: TToken               # The current token
-    inPragma*: int             # Pragma level
+  Parser* = object            # A Parser object represents a file that
+                              # is being parsed
+    currInd: int              # current indentation level
+    firstTok: bool            # Has the first token been read?
+    hasProgress: bool         # some while loop requires progress ensurance
+    lex*: Lexer               # The lexer that is used for parsing
+    tok*: Token               # The current token
+    lineStartPrevious*: int
+    lineNumberPrevious*: int
+    bufposPrevious*: int
+    inPragma*: int            # Pragma level
     inSemiStmtList*: int
-    emptyNode: PNode
-    when defined(nimpretty2):
-      em: Emitter
+    when not defined(nimCustomAst):
+      emptyNode: PNode
+    when defined(nimpretty):
+      em*: Emitter
 
   SymbolMode = enum
     smNormal, smAllowNil, smAfterDot
 
-proc parseAll*(p: var TParser): PNode
-proc closeParser*(p: var TParser)
-proc parseTopLevelStmt*(p: var TParser): PNode
+  PrimaryMode = enum
+    pmNormal, pmTypeDesc, pmTypeDef, pmTrySimple
+
+when defined(nimCustomAst):
+  # For the `customast` version we cannot share nodes, not even empty nodes:
+  template emptyNode(p: Parser): PNode = newNode(nkEmpty)
 
 # helpers for the other parsers
-proc isOperator*(tok: TToken): bool
-proc getTok*(p: var TParser)
-proc parMessage*(p: TParser, msg: TMsgKind, arg: string = "")
-proc skipComment*(p: var TParser, node: PNode)
-proc newNodeP*(kind: TNodeKind, p: TParser): PNode
-proc newIntNodeP*(kind: TNodeKind, intVal: BiggestInt, p: TParser): PNode
-proc newFloatNodeP*(kind: TNodeKind, floatVal: BiggestFloat, p: TParser): PNode
-proc newStrNodeP*(kind: TNodeKind, strVal: string, p: TParser): PNode
-proc newIdentNodeP*(ident: PIdent, p: TParser): PNode
-proc expectIdentOrKeyw*(p: TParser)
-proc expectIdent*(p: TParser)
-proc parLineInfo*(p: TParser): TLineInfo
-proc eat*(p: var TParser, tokType: TTokType)
-proc skipInd*(p: var TParser)
-proc optPar*(p: var TParser)
-proc optInd*(p: var TParser, n: PNode)
-proc indAndComment*(p: var TParser, n: PNode)
-proc setBaseFlags*(n: PNode, base: TNumericalBase)
-proc parseSymbol*(p: var TParser, mode = smNormal): PNode
-proc parseTry(p: var TParser; isExpr: bool): PNode
-proc parseCase(p: var TParser): PNode
-proc parseStmtPragma(p: var TParser): PNode
-proc parsePragma(p: var TParser): PNode
-proc postExprBlocks(p: var TParser, x: PNode): PNode
-proc parseExprStmt(p: var TParser): PNode
-proc parseBlock(p: var TParser): PNode
+proc isOperator*(tok: Token): bool
+proc getTok*(p: var Parser)
+proc parMessage*(p: Parser, msg: TMsgKind, arg: string = "")
+proc skipComment*(p: var Parser, node: PNode)
+proc newNodeP*(kind: TNodeKind, p: Parser): PNode
+proc newIntNodeP*(kind: TNodeKind, intVal: BiggestInt, p: Parser): PNode
+proc newFloatNodeP*(kind: TNodeKind, floatVal: BiggestFloat, p: Parser): PNode
+proc newStrNodeP*(kind: TNodeKind, strVal: sink string, p: Parser): PNode
+proc newIdentNodeP*(ident: PIdent, p: Parser): PNode
+proc expectIdentOrKeyw*(p: Parser)
+proc expectIdent*(p: Parser)
+proc parLineInfo*(p: Parser): TLineInfo
+proc eat*(p: var Parser, tokType: TokType)
+proc skipInd*(p: var Parser)
+proc optPar*(p: var Parser)
+proc optInd*(p: var Parser, n: PNode)
+proc indAndComment*(p: var Parser, n: PNode, maybeMissEquals = false)
+proc setBaseFlags*(n: PNode, base: NumericalBase)
+proc parseSymbol*(p: var Parser, mode = smNormal): PNode
+proc parseTry(p: var Parser; isExpr: bool): PNode
+proc parseCase(p: var Parser): PNode
+proc parseStmtPragma(p: var Parser): PNode
+proc parsePragma(p: var Parser): PNode
+proc postExprBlocks(p: var Parser, x: PNode): PNode
+proc parseExprStmt(p: var Parser): PNode
+proc parseBlock(p: var Parser): PNode
+proc primary(p: var Parser, mode: PrimaryMode): PNode
+proc simpleExprAux(p: var Parser, limit: int, mode: PrimaryMode): PNode
+
 # implementation
 
-proc getTok(p: var TParser) =
+template prettySection(body) =
+  when defined(nimpretty): beginSection(p.em)
+  body
+  when defined(nimpretty): endSection(p.em)
+
+proc getTok(p: var Parser) =
   ## Get the next token from the parser's lexer, and store it in the parser's
   ## `tok` member.
+  p.lineNumberPrevious = p.lex.lineNumber
+  p.lineStartPrevious = p.lex.lineStart
+  p.bufposPrevious = p.lex.bufpos
   rawGetTok(p.lex, p.tok)
   p.hasProgress = true
-  when defined(nimpretty2):
+  when defined(nimpretty):
     emitTok(p.em, p.lex, p.tok)
+    # skip the additional tokens that nimpretty needs but the parser has no
+    # interest in:
     while p.tok.tokType == tkComment:
       rawGetTok(p.lex, p.tok)
       emitTok(p.em, p.lex, p.tok)
 
-proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream,
-                 cache: IdentCache; config: ConfigRef;
-                 strongSpaces=false) =
+proc openParser*(p: var Parser, fileIdx: FileIndex, inputStream: PLLStream,
+                 cache: IdentCache; config: ConfigRef) =
   ## Open a parser, using the given arguments to set up its internal state.
   ##
-  initToken(p.tok)
+  reset(p.tok)
   openLexer(p.lex, fileIdx, inputStream, cache, config)
-  when defined(nimpretty2):
+  when defined(nimpretty):
     openEmitter(p.em, cache, config, fileIdx)
   getTok(p)                   # read the first token
   p.firstTok = true
-  p.strongSpaces = strongSpaces
-  p.emptyNode = newNode(nkEmpty)
+  when not defined(nimCustomAst):
+    p.emptyNode = newNode(nkEmpty)
 
-proc openParser*(p: var TParser, filename: string, inputStream: PLLStream,
-                 cache: IdentCache; config: ConfigRef;
-                 strongSpaces=false) =
-  openParser(p, fileInfoIdx(config, filename), inputStream, cache, config, strongSpaces)
+proc openParser*(p: var Parser, filename: AbsoluteFile, inputStream: PLLStream,
+                 cache: IdentCache; config: ConfigRef) =
+  openParser(p, fileInfoIdx(config, filename), inputStream, cache, config)
 
-proc closeParser(p: var TParser) =
+proc closeParser*(p: var Parser) =
   ## Close a parser, freeing up its resources.
   closeLexer(p.lex)
-  when defined(nimpretty2):
-    closeEmitter(p.em)
 
-proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
+proc parMessage(p: Parser, msg: TMsgKind, arg = "") =
   ## Produce and emit the parser message `arg` to output.
   lexMessageTok(p.lex, msg, p.tok, arg)
 
-proc parMessage(p: TParser, msg: string, tok: TToken) =
+proc parMessage(p: Parser, msg: string, tok: Token) =
   ## Produce and emit a parser message to output about the token `tok`
   parMessage(p, errGenerated, msg % prettyTok(tok))
 
-proc parMessage(p: TParser, arg: string) =
+proc parMessage(p: Parser, arg: string) =
   ## Produce and emit the parser message `arg` to output.
   lexMessageTok(p.lex, errGenerated, p.tok, arg)
 
@@ -137,169 +185,135 @@ template withInd(p, body: untyped) =
   body
   p.currInd = oldInd
 
+template newlineWasSplitting(p: var Parser) =
+  when defined(nimpretty):
+    layouter.newlineWasSplitting(p.em)
+
 template realInd(p): bool = p.tok.indent > p.currInd
 template sameInd(p): bool = p.tok.indent == p.currInd
 template sameOrNoInd(p): bool = p.tok.indent == p.currInd or p.tok.indent < 0
 
-proc rawSkipComment(p: var TParser, node: PNode) =
+proc validInd(p: var Parser): bool {.inline.} =
+  result = p.tok.indent < 0 or p.tok.indent > p.currInd
+
+proc rawSkipComment(p: var Parser, node: PNode) =
   if p.tok.tokType == tkComment:
     if node != nil:
-      if node.comment == nil: node.comment = ""
+      var rhs = node.comment
       when defined(nimpretty):
         if p.tok.commentOffsetB > p.tok.commentOffsetA:
-          add node.comment, fileSection(p.lex.config, p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB)
+          rhs.add fileSection(p.lex.config, p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB)
         else:
-          add node.comment, p.tok.literal
+          rhs.add p.tok.literal
       else:
-        add(node.comment, p.tok.literal)
+        rhs.add p.tok.literal
+      node.comment = move rhs
     else:
       parMessage(p, errInternal, "skipComment")
     getTok(p)
 
-proc skipComment(p: var TParser, node: PNode) =
+proc skipComment(p: var Parser, node: PNode) =
   if p.tok.indent < 0: rawSkipComment(p, node)
 
-proc flexComment(p: var TParser, node: PNode) =
+proc flexComment(p: var Parser, node: PNode) =
   if p.tok.indent < 0 or realInd(p): rawSkipComment(p, node)
 
 const
   errInvalidIndentation = "invalid indentation"
   errIdentifierExpected = "identifier expected, but got '$1'"
   errExprExpected = "expression expected, but found '$1'"
-  errTokenExpected = "'$1' expected"
 
-proc skipInd(p: var TParser) =
+proc skipInd(p: var Parser) =
   if p.tok.indent >= 0:
     if not realInd(p): parMessage(p, errInvalidIndentation)
 
-proc optPar(p: var TParser) =
+proc optPar(p: var Parser) =
   if p.tok.indent >= 0:
     if p.tok.indent < p.currInd: parMessage(p, errInvalidIndentation)
 
-proc optInd(p: var TParser, n: PNode) =
+proc optInd(p: var Parser, n: PNode) =
   skipComment(p, n)
   skipInd(p)
 
-proc getTokNoInd(p: var TParser) =
+proc getTokNoInd(p: var Parser) =
   getTok(p)
   if p.tok.indent >= 0: parMessage(p, errInvalidIndentation)
 
-proc expectIdentOrKeyw(p: TParser) =
+proc expectIdentOrKeyw(p: Parser) =
   if p.tok.tokType != tkSymbol and not isKeyword(p.tok.tokType):
     lexMessage(p.lex, errGenerated, errIdentifierExpected % prettyTok(p.tok))
 
-proc expectIdent(p: TParser) =
+proc expectIdent(p: Parser) =
   if p.tok.tokType != tkSymbol:
     lexMessage(p.lex, errGenerated, errIdentifierExpected % prettyTok(p.tok))
 
-proc eat(p: var TParser, tokType: TTokType) =
+proc eat(p: var Parser, tokType: TokType) =
   ## Move the parser to the next token if the current token is of type
   ## `tokType`, otherwise error.
   if p.tok.tokType == tokType:
     getTok(p)
   else:
     lexMessage(p.lex, errGenerated,
-      "expected: '" & TokTypeToStr[tokType] & "', but got: '" & prettyTok(p.tok) & "'")
+      "expected: '" & $tokType & "', but got: '" & prettyTok(p.tok) & "'")
 
-proc parLineInfo(p: TParser): TLineInfo =
+proc parLineInfo(p: Parser): TLineInfo =
   ## Retrieve the line information associated with the parser's current state.
   result = getLineInfo(p.lex, p.tok)
 
-proc indAndComment(p: var TParser, n: PNode) =
+proc indAndComment(p: var Parser, n: PNode, maybeMissEquals = false) =
   if p.tok.indent > p.currInd:
     if p.tok.tokType == tkComment: rawSkipComment(p, n)
+    elif maybeMissEquals:
+      let col = p.bufposPrevious - p.lineStartPrevious
+      var info = newLineInfo(p.lex.fileIdx, p.lineNumberPrevious, col)
+      parMessage(p, "invalid indentation, maybe you forgot a '=' at $1 ?" % [p.lex.config$info])
     else: parMessage(p, errInvalidIndentation)
   else:
     skipComment(p, n)
 
-proc newNodeP(kind: TNodeKind, p: TParser): PNode =
-  result = newNodeI(kind, parLineInfo(p))
+proc newNodeP(kind: TNodeKind, p: Parser): PNode =
+  result = newNode(kind, parLineInfo(p))
 
-proc newIntNodeP(kind: TNodeKind, intVal: BiggestInt, p: TParser): PNode =
-  result = newNodeP(kind, p)
-  result.intVal = intVal
+proc newIntNodeP(kind: TNodeKind, intVal: BiggestInt, p: Parser): PNode =
+  result = newAtom(kind, intVal, parLineInfo(p))
 
 proc newFloatNodeP(kind: TNodeKind, floatVal: BiggestFloat,
-                   p: TParser): PNode =
-  result = newNodeP(kind, p)
-  result.floatVal = floatVal
+                   p: Parser): PNode =
+  result = newAtom(kind, floatVal, parLineInfo(p))
 
-proc newStrNodeP(kind: TNodeKind, strVal: string, p: TParser): PNode =
-  result = newNodeP(kind, p)
-  result.strVal = strVal
+proc newStrNodeP(kind: TNodeKind, strVal: sink string, p: Parser): PNode =
+  result = newAtom(kind, strVal, parLineInfo(p))
 
-proc newIdentNodeP(ident: PIdent, p: TParser): PNode =
-  result = newNodeP(nkIdent, p)
-  result.ident = ident
+proc newIdentNodeP(ident: PIdent, p: Parser): PNode =
+  result = newAtom(ident, parLineInfo(p))
 
-proc parseExpr(p: var TParser): PNode
-proc parseStmt(p: var TParser): PNode
-proc parseTypeDesc(p: var TParser): PNode
-proc parseParamList(p: var TParser, retColon = true): PNode
+proc parseExpr(p: var Parser): PNode
+proc parseStmt(p: var Parser): PNode
+proc parseTypeDesc(p: var Parser, fullExpr = false): PNode
+proc parseTypeDefValue(p: var Parser): PNode
+proc parseParamList(p: var Parser, retColon = true): PNode
 
-proc isSigilLike(tok: TToken): bool {.inline.} =
+proc isSigilLike(tok: Token): bool {.inline.} =
   result = tok.tokType == tkOpr and tok.ident.s[0] == '@'
 
-proc isRightAssociative(tok: TToken): bool {.inline.} =
+proc isRightAssociative(tok: Token): bool {.inline.} =
   ## Determines whether the token is right assocative.
   result = tok.tokType == tkOpr and tok.ident.s[0] == '^'
-  # or (let L = tok.ident.s.len; L > 1 and tok.ident.s[L-1] == '>'))
-
-proc getPrecedence(tok: TToken, strongSpaces: bool): int =
-  ## Calculates the precedence of the given token.
-  template considerStrongSpaces(x): untyped =
-    x + (if strongSpaces: 100 - tok.strongSpaceA.int*10 else: 0)
-
-  case tok.tokType
-  of tkOpr:
-    let L = tok.ident.s.len
-    let relevantChar = tok.ident.s[0]
-
-    # arrow like?
-    if L > 1 and tok.ident.s[L-1] == '>' and
-      tok.ident.s[L-2] in {'-', '~', '='}: return considerStrongSpaces(1)
-
-    template considerAsgn(value: untyped) =
-      result = if tok.ident.s[L-1] == '=': 1 else: value
-
-    case relevantChar
-    of '$', '^': considerAsgn(10)
-    of '*', '%', '/', '\\': considerAsgn(9)
-    of '~': result = 8
-    of '+', '-', '|': considerAsgn(8)
-    of '&': considerAsgn(7)
-    of '=', '<', '>', '!': result = 5
-    of '.': considerAsgn(6)
-    of '?': result = 2
-    else: considerAsgn(2)
-  of tkDiv, tkMod, tkShl, tkShr: result = 9
-  of tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs: result = 5
-  of tkDotDot: result = 6
-  of tkAnd: result = 4
-  of tkOr, tkXor, tkPtr, tkRef: result = 3
-  else: return -10
-  result = considerStrongSpaces(result)
-
-proc isOperator(tok: TToken): bool =
-  ## Determines if the given token is an operator type token.
-  tok.tokType in {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs,
-                  tkIsnot, tkNot, tkOf, tkAs, tkDotDot, tkAnd, tkOr, tkXor}
+  # or (tok.ident.s.len > 1 and tok.ident.s[^1] == '>')
 
-proc isUnary(p: TParser): bool =
-  ## Check if the current parser token is a unary operator
-  if p.tok.tokType in {tkOpr, tkDotDot} and
-     p.tok.strongSpaceB == 0 and
-     p.tok.strongSpaceA > 0:
-      result = true
+proc isUnary(tok: Token): bool =
+  ## Check if the given token is a unary operator
+  tok.tokType in {tkOpr, tkDotDot} and
+  tok.spacing == {tsLeading}
 
-proc checkBinary(p: TParser) {.inline.} =
+proc checkBinary(p: Parser) {.inline.} =
   ## Check if the current parser token is a binary operator.
   # we don't check '..' here as that's too annoying
   if p.tok.tokType == tkOpr:
-    if p.tok.strongSpaceB > 0 and p.tok.strongSpaceA != p.tok.strongSpaceB:
+    if p.tok.spacing == {tsTrailing}:
       parMessage(p, warnInconsistentSpacing, prettyTok(p.tok))
 
-#| module = stmt ^* (';' / IND{=})
+#| module = complexOrSimpleStmt ^* (';' / IND{=})
 #|
 #| comma = ',' COMMENT?
 #| semicolon = ';' COMMENT?
@@ -308,12 +322,12 @@ proc checkBinary(p: TParser) {.inline.} =
 #|
 #| operator =  OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9
 #|          | 'or' | 'xor' | 'and'
-#|          | 'is' | 'isnot' | 'in' | 'notin' | 'of'
-#|          | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..'
+#|          | 'is' | 'isnot' | 'in' | 'notin' | 'of' | 'as' | 'from'
+#|          | 'div' | 'mod' | 'shl' | 'shr' | 'not' | '..'
 #|
 #| prefixOperator = operator
 #|
-#| optInd = COMMENT?
+#| optInd = COMMENT? IND?
 #| optPar = (IND{>} | IND{=})?
 #|
 #| simpleExpr = arrowExpr (OP0 optInd arrowExpr)* pragma?
@@ -328,19 +342,36 @@ proc checkBinary(p: TParser) {.inline.} =
 #| mulExpr = dollarExpr (OP9 optInd dollarExpr)*
 #| dollarExpr = primary (OP10 optInd primary)*
 
-proc colcom(p: var TParser, n: PNode) =
+proc isOperator(tok: Token): bool =
+  #| operatorB = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9 |
+  #|             'div' | 'mod' | 'shl' | 'shr' | 'in' | 'notin' |
+  #|             'is' | 'isnot' | 'not' | 'of' | 'as' | 'from' | '..' | 'and' | 'or' | 'xor'
+  tok.tokType in {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs,
+                  tkIsnot, tkNot, tkOf, tkAs, tkFrom, tkDotDot, tkAnd,
+                  tkOr, tkXor}
+
+proc colcom(p: var Parser, n: PNode) =
   eat(p, tkColon)
   skipComment(p, n)
 
-proc parseSymbol(p: var TParser, mode = smNormal): PNode =
+const tkBuiltInMagics = {tkType, tkStatic, tkAddr}
+
+template setEndInfo() =
+  when defined(nimsuggest):
+    result.endInfo = TLineInfo(fileIndex: p.lex.fileIdx,
+                     line: p.lex.previousTokenEnd.line,
+                     col: p.lex.previousTokenEnd.col)
+
+proc parseSymbol(p: var Parser, mode = smNormal): PNode =
   #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
-  #|        | IDENT | KEYW
+  #|        | IDENT | 'addr' | 'type' | 'static'
+  #| symbolOrKeyword = symbol | KEYW
   case p.tok.tokType
   of tkSymbol:
     result = newIdentNodeP(p.tok.ident, p)
     getTok(p)
   of tokKeywordLow..tokKeywordHigh:
-    if p.tok.tokType == tkAddr or p.tok.tokType == tkType or mode == smAfterDot:
+    if p.tok.tokType in tkBuiltInMagics or mode == smAfterDot:
       # for backwards compatibility these 2 are always valid:
       result = newIdentNodeP(p.tok.ident, p)
       getTok(p)
@@ -357,18 +388,20 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode =
     while true:
       case p.tok.tokType
       of tkAccent:
-        if result.len == 0:
+        if not result.hasSon:
           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))
+          accm.add($p.tok)
           getTok(p)
-        result.add(newIdentNodeP(p.lex.cache.getIdent(accm), p))
-      of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCharLit:
-        result.add(newIdentNodeP(p.lex.cache.getIdent(tokToStr(p.tok)), p))
+        let node = newAtom(p.lex.cache.getIdent(accm), lineinfo)
+        result.add(node)
+      of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCustomLit:
+        result.add(newIdentNodeP(p.lex.cache.getIdent($p.tok), p))
         getTok(p)
       else:
         parMessage(p, errIdentifierExpected, p.tok)
@@ -381,180 +414,251 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode =
     # if it is a keyword:
     #if not isKeyword(p.tok.tokType): getTok(p)
     result = p.emptyNode
+  setEndInfo()
 
-proc colonOrEquals(p: var TParser, a: PNode): PNode =
-  if p.tok.tokType == tkColon:
-    result = newNodeP(nkExprColonExpr, p)
-    getTok(p)
-    #optInd(p, result)
-    addSon(result, a)
-    addSon(result, parseExpr(p))
-  elif p.tok.tokType == tkEquals:
+proc equals(p: var Parser, a: PNode): PNode =
+  if p.tok.tokType == tkEquals:
     result = newNodeP(nkExprEqExpr, p)
     getTok(p)
     #optInd(p, result)
-    addSon(result, a)
-    addSon(result, parseExpr(p))
+    result.add(a)
+    result.add(parseExpr(p))
   else:
     result = a
 
-proc exprColonEqExpr(p: var TParser): PNode =
-  #| exprColonEqExpr = expr (':'|'=' expr)?
+proc colonOrEquals(p: var Parser, a: PNode): PNode =
+  if p.tok.tokType == tkColon:
+    result = newNodeP(nkExprColonExpr, p)
+    getTok(p)
+    newlineWasSplitting(p)
+    #optInd(p, result)
+    result.add(a)
+    result.add(parseExpr(p))
+  else:
+    result = equals(p, a)
+
+proc exprColonEqExpr(p: var Parser): PNode =
+  #| exprColonEqExpr = expr ((':'|'=') expr
+  #|                        / doBlock extraPostExprBlock*)?
   var a = parseExpr(p)
   if p.tok.tokType == tkDo:
     result = postExprBlocks(p, a)
   else:
     result = colonOrEquals(p, a)
 
-proc exprList(p: var TParser, endTok: TTokType, result: PNode) =
+proc exprEqExpr(p: var Parser): PNode =
+  #| exprEqExpr = expr ('=' expr
+  #|                   / doBlock extraPostExprBlock*)?
+  var a = parseExpr(p)
+  if p.tok.tokType == tkDo:
+    result = postExprBlocks(p, a)
+  else:
+    result = equals(p, a)
+
+proc exprList(p: var Parser, endTok: TokType, result: PNode) =
   #| exprList = expr ^+ comma
-  when defined(nimpretty2):
+  when defined(nimpretty):
+    inc p.em.doIndentMore
+  getTok(p)
+  optInd(p, result)
+  # progress guaranteed
+  var a = parseExpr(p)
+  result.add(a)
+  while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof):
+    if p.tok.tokType != tkComma: break
+    getTok(p)
+    optInd(p, a)
+    var a = parseExpr(p)
+    result.add(a)
+  when defined(nimpretty):
+    dec p.em.doIndentMore
+
+proc optionalExprList(p: var Parser, endTok: TokType, result: PNode) =
+  #| optionalExprList = expr ^* comma
+  when defined(nimpretty):
     inc p.em.doIndentMore
   getTok(p)
   optInd(p, result)
   # progress guaranteed
   while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof):
     var a = parseExpr(p)
-    addSon(result, a)
+    result.add(a)
     if p.tok.tokType != tkComma: break
     getTok(p)
     optInd(p, a)
-  when defined(nimpretty2):
+  when defined(nimpretty):
     dec p.em.doIndentMore
 
-proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) =
+proc exprColonEqExprListAux(p: var Parser, endTok: TokType, result: PNode) =
   assert(endTok in {tkCurlyRi, tkCurlyDotRi, tkBracketRi, tkParRi})
   getTok(p)
-  optInd(p, result)
+  flexComment(p, result)
+  optPar(p)
   # progress guaranteed
   while p.tok.tokType != endTok and p.tok.tokType != tkEof:
     var a = exprColonEqExpr(p)
-    addSon(result, a)
+    result.add(a)
     if p.tok.tokType != tkComma: break
+    elif result.kind == nkPar:
+      result.transitionSonsKind(nkTupleConstr)
     getTok(p)
-    # (1,) produces a tuple expression
-    if endTok == tkParRi and p.tok.tokType == tkParRi and result.kind == nkPar:
-      result.kind = nkTupleConstr
     skipComment(p, a)
   optPar(p)
   eat(p, endTok)
 
-proc exprColonEqExprList(p: var TParser, kind: TNodeKind,
-                         endTok: TTokType): PNode =
+proc exprColonEqExprList(p: var Parser, kind: TNodeKind,
+                         endTok: TokType): PNode =
   #| exprColonEqExprList = exprColonEqExpr (comma exprColonEqExpr)* (comma)?
   result = newNodeP(kind, p)
   exprColonEqExprListAux(p, endTok, result)
 
-proc dotExpr(p: var TParser, a: PNode): PNode =
-  #| dotExpr = expr '.' optInd (symbol | '[:' exprList ']')
-  #| explicitGenericInstantiation = '[:' exprList ']' ( '(' exprColonEqExpr ')' )?
+proc dotExpr(p: var Parser, a: PNode): PNode =
   var info = p.parLineInfo
   getTok(p)
-  result = newNodeI(nkDotExpr, info)
+  result = newNode(nkDotExpr, info)
   optInd(p, result)
-  addSon(result, a)
-  addSon(result, parseSymbol(p, smAfterDot))
-  if p.tok.tokType == tkBracketLeColon and p.tok.strongSpaceA <= 0:
-    var x = newNodeI(nkBracketExpr, p.parLineInfo)
+  result.add(a)
+  result.add(parseSymbol(p, smAfterDot))
+  if p.tok.tokType == tkBracketLeColon and tsLeading notin p.tok.spacing:
+    var x = newNode(nkBracketExpr, p.parLineInfo)
     # rewrite 'x.y[:z]()' to 'y[z](x)'
-    x.add result[1]
+    x.add result.secondSon
     exprList(p, tkBracketRi, x)
     eat(p, tkBracketRi)
-    var y = newNodeI(nkCall, p.parLineInfo)
+    var y = newNode(nkCall, p.parLineInfo)
     y.add x
-    y.add result[0]
-    if p.tok.tokType == tkParLe and p.tok.strongSpaceA <= 0:
+    y.add result.firstSon
+    if p.tok.tokType == tkParLe and tsLeading notin p.tok.spacing:
       exprColonEqExprListAux(p, tkParRi, y)
     result = y
 
-proc qualifiedIdent(p: var TParser): PNode =
-  #| qualifiedIdent = symbol ('.' optInd symbol)?
+proc dotLikeExpr(p: var Parser, a: PNode): PNode =
+  var info = p.parLineInfo
+  result = newNode(nkInfix, info)
+  optInd(p, result)
+  var opNode = newIdentNodeP(p.tok.ident, p)
+  getTok(p)
+  result.add(opNode)
+  result.add(a)
+  result.add(parseSymbol(p, smAfterDot))
+
+proc qualifiedIdent(p: var Parser): PNode =
+  #| qualifiedIdent = symbol ('.' optInd symbolOrKeyword)?
   result = parseSymbol(p)
   if p.tok.tokType == tkDot: result = dotExpr(p, result)
 
-proc setOrTableConstr(p: var TParser): PNode =
+proc setOrTableConstr(p: var Parser): PNode =
   #| setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}'
   result = newNodeP(nkCurly, p)
   getTok(p) # skip '{'
   optInd(p, result)
   if p.tok.tokType == tkColon:
     getTok(p) # skip ':'
-    result.kind = nkTableConstr
+    result.transitionSonsKind(nkTableConstr)
   else:
     # progress guaranteed
     while p.tok.tokType notin {tkCurlyRi, tkEof}:
       var a = exprColonEqExpr(p)
-      if a.kind == nkExprColonExpr: result.kind = nkTableConstr
-      addSon(result, a)
+      if a.kind == nkExprColonExpr: result.transitionSonsKind(nkTableConstr)
+      result.add(a)
       if p.tok.tokType != tkComma: break
       getTok(p)
       skipComment(p, a)
   optPar(p)
   eat(p, tkCurlyRi) # skip '}'
 
-proc parseCast(p: var TParser): PNode =
-  #| castExpr = 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')'
+proc parseCast(p: var Parser): PNode =
+  #| castExpr = 'cast' ('[' optInd typeDesc optPar ']' '(' optInd expr optPar ')') /
+  #                    ('(' optInd exprColonEqExpr optPar ')')
   result = newNodeP(nkCast, p)
   getTok(p)
-  eat(p, tkBracketLe)
-  optInd(p, result)
-  addSon(result, parseTypeDesc(p))
-  optPar(p)
-  eat(p, tkBracketRi)
-  eat(p, tkParLe)
-  optInd(p, result)
-  addSon(result, parseExpr(p))
+  if p.tok.tokType == tkBracketLe:
+    getTok(p)
+    optInd(p, result)
+    result.add(parseTypeDesc(p))
+    optPar(p)
+    eat(p, tkBracketRi)
+    eat(p, tkParLe)
+    optInd(p, result)
+    result.add(parseExpr(p))
+  else:
+    result.add p.emptyNode
+    eat(p, tkParLe)
+    optInd(p, result)
+    result.add(exprColonEqExpr(p))
   optPar(p)
   eat(p, tkParRi)
+  setEndInfo()
+
+template setNodeFlag(n: PNode; f: untyped) =
+  when defined(nimCustomAst):
+    discard
+  else:
+    incl n.flags, f
 
-proc setBaseFlags(n: PNode, base: TNumericalBase) =
+proc setBaseFlags(n: PNode, base: NumericalBase) =
   case base
   of base10: discard
-  of base2: incl(n.flags, nfBase2)
-  of base8: incl(n.flags, nfBase8)
-  of base16: incl(n.flags, nfBase16)
+  of base2: setNodeFlag(n, nfBase2)
+  of base8: setNodeFlag(n, nfBase8)
+  of base16: setNodeFlag(n, nfBase16)
 
-proc parseGStrLit(p: var TParser, a: PNode): PNode =
+proc parseGStrLit(p: var Parser, a: PNode): PNode =
   case p.tok.tokType
   of tkGStrLit:
     result = newNodeP(nkCallStrLit, p)
-    addSon(result, a)
-    addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p))
+    result.add(a)
+    result.add(newStrNodeP(nkRStrLit, p.tok.literal, p))
     getTok(p)
   of tkGTripleStrLit:
     result = newNodeP(nkCallStrLit, p)
-    addSon(result, a)
-    addSon(result, newStrNodeP(nkTripleStrLit, p.tok.literal, p))
+    result.add(a)
+    result.add(newStrNodeP(nkTripleStrLit, p.tok.literal, p))
     getTok(p)
   else:
     result = a
+  setEndInfo()
 
-type
-  TPrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix
-
-proc complexOrSimpleStmt(p: var TParser): PNode
-proc simpleExpr(p: var TParser, mode = pmNormal): PNode
+proc complexOrSimpleStmt(p: var Parser): PNode
+proc simpleExpr(p: var Parser, mode = pmNormal): PNode
+proc parseIfOrWhenExpr(p: var Parser, kind: TNodeKind): PNode
 
-proc semiStmtList(p: var TParser, result: PNode) =
+proc semiStmtList(p: var Parser, result: PNode) =
   inc p.inSemiStmtList
-  result.add(complexOrSimpleStmt(p))
-  # progress guaranteed
-  while p.tok.tokType == tkSemiColon:
-    getTok(p)
-    optInd(p, result)
-    result.add(complexOrSimpleStmt(p))
+  withInd(p):
+    # Be lenient with the first stmt/expr
+    let a = case p.tok.tokType
+            of tkIf: parseIfOrWhenExpr(p, nkIfStmt)
+            of tkWhen: parseIfOrWhenExpr(p, nkWhenStmt)
+            else: complexOrSimpleStmt(p)
+    result.add a
+
+    while p.tok.tokType != tkEof:
+      if p.tok.tokType == tkSemiColon:
+        getTok(p)
+      if p.tok.tokType == tkParRi:
+        break
+      elif not (sameInd(p) or realInd(p)):
+        parMessage(p, errInvalidIndentation)
+      let a = complexOrSimpleStmt(p)
+      if a.kind == nkEmpty:
+        parMessage(p, errExprExpected, p.tok)
+        getTok(p)
+      else:
+        result.add a
   dec p.inSemiStmtList
-  result.kind = nkStmtListExpr
+  result.transitionSonsKind(nkStmtListExpr)
 
-proc parsePar(p: var TParser): PNode =
+proc parsePar(p: var Parser): PNode =
   #| parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
   #|         | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let'
   #|         | 'when' | 'var' | 'mixin'
   #| par = '(' optInd
-  #|           ( &parKeyw complexOrSimpleStmt ^+ ';'
-  #|           | ';' complexOrSimpleStmt ^+ ';'
+  #|           ( &parKeyw (ifExpr / complexOrSimpleStmt) ^+ ';'
+  #|           | ';' (ifExpr / complexOrSimpleStmt) ^+ ';'
   #|           | pragmaStmt
-  #|           | simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )
+  #|           | simpleExpr ( (doBlock extraPostExprBlock*)
+  #|                        | ('=' expr (';' (ifExpr / complexOrSimpleStmt) ^+ ';' )? )
   #|                        | (':' expr (',' exprColonEqExpr     ^+ ',' )? ) ) )
   #|           optPar ')'
   #
@@ -563,9 +667,10 @@ proc parsePar(p: var TParser): PNode =
   result = newNodeP(nkPar, p)
   getTok(p)
   optInd(p, result)
+  flexComment(p, result)
   if p.tok.tokType in {tkDiscard, tkInclude, tkIf, tkWhile, tkCase,
-                       tkTry, tkDefer, tkFinally, tkExcept, tkFor, tkBlock,
-                       tkConst, tkLet, tkWhen, tkVar,
+                       tkTry, tkDefer, tkFinally, tkExcept, tkBlock,
+                       tkConst, tkLet, tkWhen, tkVar, tkFor,
                        tkMixin}:
     # XXX 'bind' used to be an expression, so we exclude it here;
     # tests/reject/tbind2 fails otherwise.
@@ -577,7 +682,10 @@ proc parsePar(p: var TParser): PNode =
     semiStmtList(p, result)
   elif p.tok.tokType == tkCurlyDotLe:
     result.add(parseStmtPragma(p))
-  elif p.tok.tokType != tkParRi:
+  elif p.tok.tokType == tkParRi:
+    # Empty tuple '()'
+    result.transitionSonsKind(nkTupleConstr)
+  else:
     var a = simpleExpr(p)
     if p.tok.tokType == tkDo:
       result = postExprBlocks(p, a)
@@ -598,38 +706,40 @@ proc parsePar(p: var TParser): PNode =
       semiStmtList(p, result)
     else:
       a = colonOrEquals(p, a)
+      if a.kind == nkExprColonExpr:
+        result.transitionSonsKind(nkTupleConstr)
       result.add(a)
       if p.tok.tokType == tkComma:
         getTok(p)
         skipComment(p, a)
         # (1,) produces a tuple expression:
-        if p.tok.tokType == tkParRi:
-          result.kind = nkTupleConstr
+        result.transitionSonsKind(nkTupleConstr)
         # progress guaranteed
         while p.tok.tokType != tkParRi and p.tok.tokType != tkEof:
           var a = exprColonEqExpr(p)
-          addSon(result, a)
+          result.add(a)
           if p.tok.tokType != tkComma: break
           getTok(p)
           skipComment(p, a)
   optPar(p)
   eat(p, tkParRi)
+  setEndInfo()
 
-proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode =
+proc identOrLiteral(p: var Parser, mode: PrimaryMode): PNode =
   #| literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
   #|           | UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT
   #|           | FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT
   #|           | STR_LIT | RSTR_LIT | TRIPLESTR_LIT
-  #|           | CHAR_LIT
+  #|           | CHAR_LIT | CUSTOM_NUMERIC_LIT
   #|           | NIL
   #| generalizedLit = GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT
   #| identOrLiteral = generalizedLit | symbol | literal
-  #|                | par | arrayConstr | setOrTableConstr
+  #|                | par | arrayConstr | setOrTableConstr | tupleConstr
   #|                | castExpr
   #| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')'
   #| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']'
   case p.tok.tokType
-  of tkSymbol, tkType, tkAddr:
+  of tkSymbol, tkBuiltInMagics, tkOut:
     result = newIdentNodeP(p.tok.ident, p)
     getTok(p)
     result = parseGStrLit(p, result)
@@ -703,6 +813,14 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode =
   of tkCharLit:
     result = newIntNodeP(nkCharLit, ord(p.tok.literal[0]), p)
     getTok(p)
+  of tkCustomLit:
+    let splitPos = p.tok.iNumber.int
+    let str = newStrNodeP(nkRStrLit, p.tok.literal.substr(0, splitPos-1), p)
+    let callee = newIdentNodeP(getIdent(p.lex.cache, p.tok.literal.substr(splitPos)), p)
+    result = newNodeP(nkDotExpr, p)
+    result.add str
+    result.add callee
+    getTok(p)
   of tkNil:
     result = newNodeP(nkNilLit, p)
     getTok(p)
@@ -722,286 +840,254 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode =
     result = parseCast(p)
   else:
     parMessage(p, errExprExpected, p.tok)
-    getTok(p)  # we must consume a token here to prevend endless loops!
+    getTok(p)  # we must consume a token here to prevent endless loops!
     result = p.emptyNode
 
-proc namedParams(p: var TParser, callee: PNode,
-                 kind: TNodeKind, endTok: TTokType): PNode =
+proc namedParams(p: var Parser, callee: PNode,
+                 kind: TNodeKind, endTok: TokType): PNode =
   let a = callee
   result = newNodeP(kind, p)
-  addSon(result, a)
+  result.add(a)
   # progress guaranteed
   exprColonEqExprListAux(p, endTok, result)
 
-proc commandParam(p: var TParser, isFirstParam: var bool): PNode =
-  result = parseExpr(p)
-  if p.tok.tokType == tkDo:
-    result = postExprBlocks(p, result)
-  elif p.tok.tokType == tkEquals and not isFirstParam:
-    let lhs = result
-    result = newNodeP(nkExprEqExpr, p)
-    getTok(p)
-    addSon(result, lhs)
-    addSon(result, parseExpr(p))
+proc commandParam(p: var Parser, isFirstParam: var bool; mode: PrimaryMode): PNode =
+  if mode == pmTypeDesc:
+    result = simpleExpr(p, mode)
+  elif not isFirstParam:
+    result = exprEqExpr(p)
+  else:
+    result = parseExpr(p)
+    if p.tok.tokType == tkDo:
+      result = postExprBlocks(p, result)
   isFirstParam = false
 
-proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
-  #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
-  #|       | doBlocks
-  #|       | '.' optInd symbol generalizedLit?
-  #|       | '[' optInd indexExprList optPar ']'
-  #|       | '{' optInd indexExprList optPar '}'
-  #|       | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax
+proc commandExpr(p: var Parser; r: PNode; mode: PrimaryMode): PNode =
+  if mode == pmTrySimple:
+    result = r
+  else:
+    result = newNodeP(nkCommand, p)
+    result.add(r)
+    var isFirstParam = true
+    # progress NOT guaranteed
+    p.hasProgress = false
+    result.add commandParam(p, isFirstParam, mode)
+
+proc isDotLike(tok: Token): bool =
+  result = tok.tokType == tkOpr and tok.ident.s.len > 1 and
+    tok.ident.s[0] == '.' and tok.ident.s[1] != '.'
+
+proc primarySuffix(p: var Parser, r: PNode,
+                   baseIndent: int, mode: PrimaryMode): PNode =
+  #| primarySuffix = '(' (exprColonEqExpr comma?)* ')'
+  #|       | '.' optInd symbolOrKeyword ('[:' exprList ']' ( '(' exprColonEqExpr ')' )?)? generalizedLit?
+  #|       | DOTLIKEOP optInd symbolOrKeyword generalizedLit?
+  #|       | '[' optInd exprColonEqExprList optPar ']'
+  #|       | '{' optInd exprColonEqExprList optPar '}'
+  # XXX strong spaces need to be reflected above
   result = r
 
-  template somePar() =
-    if p.tok.strongSpaceA > 0: break
   # progress guaranteed
   while p.tok.indent < 0 or
        (p.tok.tokType == tkDot and p.tok.indent >= baseIndent):
     case p.tok.tokType
     of tkParLe:
       # progress guaranteed
-      somePar()
+      if tsLeading in p.tok.spacing:
+        result = commandExpr(p, result, mode)
+        break
       result = namedParams(p, result, nkCall, tkParRi)
-      if result.len > 1 and result.sons[1].kind == nkExprColonExpr:
-        result.kind = nkObjConstr
+      if result.has2Sons and result.secondSon.kind == nkExprColonExpr:
+        result.transitionSonsKind(nkObjConstr)
     of tkDot:
       # progress guaranteed
       result = dotExpr(p, result)
       result = parseGStrLit(p, result)
     of tkBracketLe:
       # progress guaranteed
-      somePar()
+      if tsLeading in p.tok.spacing:
+        result = commandExpr(p, result, mode)
+        break
       result = namedParams(p, result, nkBracketExpr, tkBracketRi)
     of tkCurlyLe:
       # progress guaranteed
-      somePar()
+      if tsLeading in p.tok.spacing:
+        result = commandExpr(p, result, mode)
+        break
       result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
-    of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType,
-       tkOpr, tkDotDot:
-      if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot}):
-        # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
-        # solution, but pragmas.nim can't handle that
-        let a = result
-        result = newNodeP(nkCommand, p)
-        addSon(result, a)
-        var isFirstParam = true
-        when true:
-          # progress NOT guaranteed
-          p.hasProgress = false
-          addSon result, commandParam(p, isFirstParam)
-          if not p.hasProgress: break
-        else:
-          while p.tok.tokType != tkEof:
-            let x = parseExpr(p)
-            addSon(result, x)
-            if p.tok.tokType != tkComma: break
-            getTok(p)
-            optInd(p, x)
-          result = postExprBlocks(p, result)
-      break
+    of tkSymbol, tkAccent, tkIntLit..tkCustomLit, tkNil, tkCast,
+       tkOpr, tkDotDot, tkVar, tkOut, tkStatic, tkType, tkEnum, tkTuple,
+       tkObject, tkProc:
+      # XXX: In type sections we allow the free application of the
+      # command syntax, with the exception of expressions such as
+      # `foo ref` or `foo ptr`. Unfortunately, these two are also
+      # used as infix operators for the memory regions feature and
+      # the current parsing rules don't play well here.
+      let isDotLike2 = p.tok.isDotLike
+      if isDotLike2 and p.lex.config.isDefined("nimPreviewDotLikeOps"):
+        # synchronize with `tkDot` branch
+        result = dotLikeExpr(p, result)
+        result = parseGStrLit(p, result)
+      else:
+        if isDotLike2:
+          parMessage(p, warnDotLikeOps, "dot-like operators will be parsed differently with `-d:nimPreviewDotLikeOps`")
+        if p.inPragma == 0 and (isUnary(p.tok) or p.tok.tokType notin {tkOpr, tkDotDot}):
+          # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
+          # solution, but pragmas.nim can't handle that
+          result = commandExpr(p, result, mode)
+        break
     else:
       break
 
-proc primary(p: var TParser, mode: TPrimaryMode): PNode
-proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode
-
-proc parseOperators(p: var TParser, headNode: PNode,
-                    limit: int, mode: TPrimaryMode): PNode =
+proc parseOperators(p: var Parser, headNode: PNode,
+                    limit: int, mode: PrimaryMode): PNode =
   result = headNode
   # expand while operators have priorities higher than 'limit'
-  var opPrec = getPrecedence(p.tok, p.strongSpaces)
+  var opPrec = getPrecedence(p.tok)
   let modeB = if mode == pmTypeDef: pmTypeDesc else: mode
   # the operator itself must not start on a new line:
   # progress guaranteed
-  while opPrec >= limit and p.tok.indent < 0 and not isUnary(p):
+  while opPrec >= limit and p.tok.indent < 0 and not isUnary(p.tok):
     checkBinary(p)
-    var leftAssoc = 1-ord(isRightAssociative(p.tok))
+    let leftAssoc = ord(not isRightAssociative(p.tok))
     var a = newNodeP(nkInfix, p)
     var opNode = newIdentNodeP(p.tok.ident, p) # skip operator:
     getTok(p)
-    optInd(p, a)
+    flexComment(p, a)
+    optPar(p)
     # read sub-expression with higher priority:
     var b = simpleExprAux(p, opPrec + leftAssoc, modeB)
-    addSon(a, opNode)
-    addSon(a, result)
-    addSon(a, b)
+    a.add(opNode)
+    a.add(result)
+    a.add(b)
     result = a
-    opPrec = getPrecedence(p.tok, p.strongSpaces)
+    opPrec = getPrecedence(p.tok)
+  setEndInfo()
 
-proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode =
+proc simpleExprAux(p: var Parser, limit: int, mode: PrimaryMode): PNode =
+  var mode = mode
   result = primary(p, mode)
+  if mode == pmTrySimple:
+    mode = pmNormal
   if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)) and
      mode == pmNormal:
     var pragmaExp = newNodeP(nkPragmaExpr, p)
-    pragmaExp.addSon result
-    pragmaExp.addSon p.parsePragma
+    pragmaExp.add result
+    pragmaExp.add p.parsePragma
     result = pragmaExp
   result = parseOperators(p, result, limit, mode)
 
-proc simpleExpr(p: var TParser, mode = pmNormal): PNode =
-  when defined(nimpretty2):
+proc simpleExpr(p: var Parser, mode = pmNormal): PNode =
+  when defined(nimpretty):
     inc p.em.doIndentMore
   result = simpleExprAux(p, -1, mode)
-  when defined(nimpretty2):
+  when defined(nimpretty):
     dec p.em.doIndentMore
 
-proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode =
-  #| condExpr = expr colcom expr optInd
-  #|         ('elif' expr colcom expr optInd)*
-  #|          'else' colcom expr
-  #| ifExpr = 'if' condExpr
-  #| whenExpr = 'when' condExpr
-  when true:
-    result = newNodeP(kind, p)
-    while true:
-      getTok(p)                 # skip `if`, `when`, `elif`
-      var branch = newNodeP(nkElifExpr, p)
-      optInd(p, branch)
-      addSon(branch, parseExpr(p))
-      colcom(p, branch)
-      addSon(branch, parseStmt(p))
-      skipComment(p, branch)
-      addSon(result, branch)
-      if p.tok.tokType != tkElif: break # or not sameOrNoInd(p): break
-    if p.tok.tokType == tkElse: # and sameOrNoInd(p):
-      var branch = newNodeP(nkElseExpr, p)
-      eat(p, tkElse)
-      colcom(p, branch)
-      addSon(branch, parseStmt(p))
-      addSon(result, branch)
-  else:
-    var
-      b: PNode
-      wasIndented = false
-    result = newNodeP(kind, p)
-
-    getTok(p)
-    let branch = newNodeP(nkElifExpr, p)
-    addSon(branch, parseExpr(p))
-    colcom(p, branch)
-    let oldInd = p.currInd
-    if realInd(p):
-      p.currInd = p.tok.indent
-      wasIndented = true
-    addSon(branch, parseExpr(p))
-    result.add branch
-    while sameInd(p) or not wasIndented:
-      case p.tok.tokType
-      of tkElif:
-        b = newNodeP(nkElifExpr, p)
-        getTok(p)
-        optInd(p, b)
-        addSon(b, parseExpr(p))
-      of tkElse:
-        b = newNodeP(nkElseExpr, p)
-        getTok(p)
-      else: break
-      colcom(p, b)
-      addSon(b, parseStmt(p))
-      addSon(result, b)
-      if b.kind == nkElseExpr: break
-    if wasIndented:
-      p.currInd = oldInd
-
-proc parsePragma(p: var TParser): PNode =
-  #| pragma = '{.' optInd (exprColonExpr comma?)* optPar ('.}' | '}')
+proc parsePragma(p: var Parser): PNode =
+  #| pragma = '{.' optInd (exprColonEqExpr comma?)* optPar ('.}' | '}')
   result = newNodeP(nkPragma, p)
   inc p.inPragma
+  when defined(nimpretty):
+    inc p.em.doIndentMore
+    inc p.em.keepIndents
   getTok(p)
   optInd(p, result)
   while p.tok.tokType notin {tkCurlyDotRi, tkCurlyRi, tkEof}:
     p.hasProgress = false
     var a = exprColonEqExpr(p)
     if not p.hasProgress: break
-    addSon(result, a)
+    result.add(a)
     if p.tok.tokType == tkComma:
       getTok(p)
       skipComment(p, a)
   optPar(p)
   if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}:
-    when defined(nimpretty2):
+    when defined(nimpretty):
       if p.tok.tokType == tkCurlyRi: curlyRiWasPragma(p.em)
     getTok(p)
   else:
     parMessage(p, "expected '.}'")
   dec p.inPragma
+  when defined(nimpretty):
+    dec p.em.doIndentMore
+    dec p.em.keepIndents
+  setEndInfo()
 
-proc identVis(p: var TParser; allowDot=false): PNode =
-  #| identVis = symbol opr?  # postfix position
-  #| identVisDot = symbol '.' optInd symbol opr?
+proc identVis(p: var Parser; allowDot=false): PNode =
+  #| identVis = symbol OPR?  # postfix position
+  #| identVisDot = symbol '.' optInd symbolOrKeyword OPR?
   var a = parseSymbol(p)
   if p.tok.tokType == tkOpr:
-    when defined(nimpretty2):
+    when defined(nimpretty):
       starWasExportMarker(p.em)
     result = newNodeP(nkPostfix, p)
-    addSon(result, newIdentNodeP(p.tok.ident, p))
-    addSon(result, a)
+    result.add(newIdentNodeP(p.tok.ident, p))
+    result.add(a)
     getTok(p)
   elif p.tok.tokType == tkDot and allowDot:
     result = dotExpr(p, a)
   else:
     result = a
 
-proc identWithPragma(p: var TParser; allowDot=false): PNode =
+proc identWithPragma(p: var Parser; allowDot=false): PNode =
   #| identWithPragma = identVis pragma?
   #| identWithPragmaDot = identVisDot pragma?
   var a = identVis(p, allowDot)
   if p.tok.tokType == tkCurlyDotLe:
     result = newNodeP(nkPragmaExpr, p)
-    addSon(result, a)
-    addSon(result, parsePragma(p))
+    result.add(a)
+    result.add(parsePragma(p))
   else:
     result = a
 
 type
-  TDeclaredIdentFlag = enum
+  DeclaredIdentFlag = enum
     withPragma,               # identifier may have pragma
     withBothOptional          # both ':' and '=' parts are optional
     withDot                   # allow 'var ident.ident = value'
-  TDeclaredIdentFlags = set[TDeclaredIdentFlag]
+  DeclaredIdentFlags = set[DeclaredIdentFlag]
 
-proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode =
+proc parseIdentColonEquals(p: var Parser, flags: DeclaredIdentFlags): PNode =
   #| declColonEquals = identWithPragma (comma identWithPragma)* comma?
-  #|                   (':' optInd typeDesc)? ('=' optInd expr)?
-  #| identColonEquals = ident (comma ident)* comma?
-  #|      (':' optInd typeDesc)? ('=' optInd expr)?)
+  #|                   (':' optInd typeDescExpr)? ('=' optInd expr)?
+  #| identColonEquals = IDENT (comma IDENT)* comma?
+  #|      (':' optInd typeDescExpr)? ('=' optInd expr)?)
   var a: PNode
   result = newNodeP(nkIdentDefs, p)
   # progress guaranteed
   while true:
     case p.tok.tokType
     of tkSymbol, tkAccent:
-      if withPragma in flags: a = identWithPragma(p, allowDot=withdot in flags)
+      if withPragma in flags: a = identWithPragma(p, allowDot=withDot in flags)
       else: a = parseSymbol(p)
       if a.kind == nkEmpty: return
     else: break
-    addSon(result, a)
+    result.add(a)
     if p.tok.tokType != tkComma: break
     getTok(p)
     optInd(p, a)
   if p.tok.tokType == tkColon:
     getTok(p)
     optInd(p, result)
-    addSon(result, parseTypeDesc(p))
+    result.add(parseTypeDesc(p, fullExpr = true))
   else:
-    addSon(result, newNodeP(nkEmpty, p))
+    result.add(newNodeP(nkEmpty, p))
     if p.tok.tokType != tkEquals and withBothOptional notin flags:
       parMessage(p, "':' or '=' expected, but got '$1'", p.tok)
   if p.tok.tokType == tkEquals:
     getTok(p)
     optInd(p, result)
-    addSon(result, parseExpr(p))
+    result.add(parseExpr(p))
   else:
-    addSon(result, newNodeP(nkEmpty, p))
-
-proc parseTuple(p: var TParser, indentAllowed = false): PNode =
-  #| inlTupleDecl = 'tuple'
-  #|     [' optInd  (identColonEquals (comma/semicolon)?)*  optPar ']'
-  #| extTupleDecl = 'tuple'
-  #|     COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)?
-  #| tupleClass = 'tuple'
+    result.add(newNodeP(nkEmpty, p))
+  setEndInfo()
+
+proc parseTuple(p: var Parser, indentAllowed = false): PNode =
+  #| tupleTypeBracket = '[' optInd (identColonEquals (comma/semicolon)?)* optPar ']'
+  #| tupleType = 'tuple' tupleTypeBracket
+  #| tupleDecl = 'tuple' (tupleTypeBracket /
+  #|     COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)?)
   result = newNodeP(nkTupleTy, p)
   getTok(p)
   if p.tok.tokType == tkBracketLe:
@@ -1010,9 +1096,9 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode =
     # progress guaranteed
     while p.tok.tokType in {tkSymbol, tkAccent}:
       var a = parseIdentColonEquals(p, {})
-      addSon(result, a)
+      result.add(a)
       if p.tok.tokType notin {tkComma, tkSemiColon}: break
-      when defined(nimpretty2):
+      when defined(nimpretty):
         commaWasSemicolon(p.em)
       getTok(p)
       skipComment(p, a)
@@ -1030,7 +1116,7 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode =
             var a = parseIdentColonEquals(p, {})
             if p.tok.indent < 0 or p.tok.indent >= p.currInd:
               rawSkipComment(p, a)
-            addSon(result, a)
+            result.add(a)
           of tkEof: break
           else:
             parMessage(p, errIdentifierExpected, p.tok)
@@ -1040,16 +1126,18 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode =
     parMessage(p, errGenerated, "the syntax for tuple types is 'tuple[...]', not 'tuple(...)'")
   else:
     result = newNodeP(nkTupleClassTy, p)
+  setEndInfo()
 
-proc parseParamList(p: var TParser, retColon = true): PNode =
+proc parseParamList(p: var Parser, retColon = true): PNode =
   #| paramList = '(' declColonEquals ^* (comma/semicolon) ')'
   #| paramListArrow = paramList? ('->' optInd typeDesc)?
   #| paramListColon = paramList? (':' optInd typeDesc)?
   var a: PNode
   result = newNodeP(nkFormalParams, p)
-  addSon(result, p.emptyNode) # return type
-  when defined(nimpretty2):
+  result.add(p.emptyNode) # return type
+  when defined(nimpretty):
     inc p.em.doIndentMore
+    inc p.em.keepIndents
   let hasParLe = p.tok.tokType == tkParLe and p.tok.indent < 0
   if hasParLe:
     getTok(p)
@@ -1067,9 +1155,9 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
       else:
         parMessage(p, "expected closing ')'")
         break
-      addSon(result, a)
+      result.add(a)
       if p.tok.tokType notin {tkComma, tkSemiColon}: break
-      when defined(nimpretty2):
+      when defined(nimpretty):
         commaWasSemicolon(p.em)
       getTok(p)
       skipComment(p, a)
@@ -1080,78 +1168,96 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
   if hasRet and p.tok.indent < 0:
     getTok(p)
     optInd(p, result)
-    result.sons[0] = parseTypeDesc(p)
-  elif not retColon and not hasParle:
+    result.replaceFirstSon parseTypeDesc(p)
+  elif not retColon and not hasParLe:
     # Mark as "not there" in order to mark for deprecation in the semantic pass:
     result = p.emptyNode
-  when defined(nimpretty2):
+  when defined(nimpretty):
     dec p.em.doIndentMore
+    dec p.em.keepIndents
+  setEndInfo()
 
-proc optPragmas(p: var TParser): PNode =
+proc optPragmas(p: var Parser): PNode =
   if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)):
     result = parsePragma(p)
   else:
     result = p.emptyNode
 
-proc parseDoBlock(p: var TParser; info: TLineInfo): PNode =
-  #| doBlock = 'do' paramListArrow pragmas? colcom stmt
-  let params = parseParamList(p, retColon=false)
+proc parseDoBlock(p: var Parser; info: TLineInfo): PNode =
+  #| doBlock = 'do' paramListArrow pragma? colcom stmt
+  result = nil
+  var params = parseParamList(p, retColon=false)
   let pragmas = optPragmas(p)
   colcom(p, result)
   result = parseStmt(p)
-  if params.kind != nkEmpty:
+  if params.kind != nkEmpty or pragmas.kind != nkEmpty:
+    if params.kind == nkEmpty:
+      params = newNodeP(nkFormalParams, p)
+      params.add(p.emptyNode) # return type
     result = newProcNode(nkDo, info,
       body = result, params = params, name = p.emptyNode, pattern = p.emptyNode,
       genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode)
+  setEndInfo()
 
-proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode =
-  #| procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)?
+proc parseProcExpr(p: var Parser; isExpr: bool; kind: TNodeKind): PNode =
+  #| routineExpr = ('proc' | 'func' | 'iterator') paramListColon pragma? ('=' COMMENT? stmt)?
+  #| routineType = ('proc' | 'iterator') paramListColon pragma?
   # either a proc type or a anonymous proc
   let info = parLineInfo(p)
-  getTok(p)
   let hasSignature = p.tok.tokType in {tkParLe, tkColon} and p.tok.indent < 0
   let params = parseParamList(p)
   let pragmas = optPragmas(p)
   if p.tok.tokType == tkEquals and isExpr:
     getTok(p)
-    skipComment(p, result)
-    result = newProcNode(kind, info, body = parseStmt(p),
+    result = newProcNode(kind, info, body = p.emptyNode,
       params = params, name = p.emptyNode, pattern = p.emptyNode,
       genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode)
+    skipComment(p, result)
+    result.replaceSon bodyPos, parseStmt(p)
   else:
-    result = newNodeI(nkProcTy, info)
-    if hasSignature:
-      addSon(result, params)
-      addSon(result, pragmas)
-
-proc isExprStart(p: TParser): bool =
+    result = newNode(if kind == nkIteratorDef: nkIteratorTy else: nkProcTy, info)
+    if hasSignature or pragmas.kind != nkEmpty:
+      if hasSignature:
+        result.add(params)
+      else: # pragmas but no param list, implies typeclass with pragmas
+        result.add(p.emptyNode)
+      if kind == nkFuncDef:
+        parMessage(p, "func keyword is not allowed in type descriptions, use proc with {.noSideEffect.} pragma instead")
+      result.add(pragmas)
+  setEndInfo()
+
+proc isExprStart(p: Parser): bool =
   case p.tok.tokType
-  of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf,
-     tkProc, tkFunc, tkIterator, tkBind, tkAddr,
-     tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr,
-     tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut:
+  of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkFor,
+     tkProc, tkFunc, tkIterator, tkBind, tkBuiltInMagics,
+     tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCustomLit, tkVar, tkRef, tkPtr,
+     tkEnum, tkTuple, tkObject, tkWhen, tkCase, tkOut, tkTry, tkBlock:
     result = true
   else: result = false
 
-proc parseSymbolList(p: var TParser, result: PNode) =
+proc parseSymbolList(p: var Parser, result: PNode) =
   # progress guaranteed
   while true:
     var s = parseSymbol(p, smAllowNil)
     if s.kind == nkEmpty: break
-    addSon(result, s)
+    result.add(s)
     if p.tok.tokType != tkComma: break
     getTok(p)
     optInd(p, s)
+  setEndInfo()
 
-proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
-                       mode: TPrimaryMode): PNode =
-  #| distinct = 'distinct' optInd typeDesc
+proc parseTypeDescKAux(p: var Parser, kind: TNodeKind,
+                       mode: PrimaryMode): PNode =
   result = newNodeP(kind, p)
   getTok(p)
   if p.tok.indent != -1 and p.tok.indent <= p.currInd: return
   optInd(p, result)
+  let isTypedef = mode == pmTypeDef and p.tok.tokType in {tkObject, tkTuple}
   if not isOperator(p.tok) and isExprStart(p):
-    addSon(result, primary(p, mode))
+    if isTypedef:
+      result.add(parseTypeDefValue(p))
+    else:
+      result.add(primary(p, mode))
   if kind == nkDistinctTy and p.tok.tokType == tkSymbol:
     # XXX document this feature!
     var nodeKind: TNodeKind
@@ -1163,123 +1269,235 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
       return result
     getTok(p)
     let list = newNodeP(nodeKind, p)
-    result.addSon list
+    result.add list
     parseSymbolList(p, list)
+  if mode == pmTypeDef and not isTypedef:
+    result = parseOperators(p, result, -1, mode)
+  setEndInfo()
 
-proc parseExpr(p: var TParser): PNode =
+proc parseVarTuple(p: var Parser): PNode
+
+proc parseFor(p: var Parser): PNode =
+  #| forStmt = 'for' ((varTuple / identWithPragma) ^+ comma) 'in' expr colcom stmt
+  #| forExpr = forStmt
+  getTokNoInd(p)
+  result = newNodeP(nkForStmt, p)
+  if p.tok.tokType == tkParLe:
+    result.add(parseVarTuple(p))
+  else:
+    var a = identWithPragma(p)
+    result.add(a)
+    while p.tok.tokType == tkComma:
+      getTok(p)
+      optInd(p, a)
+      if p.tok.tokType == tkParLe:
+        result.add(parseVarTuple(p))
+        break
+      a = identWithPragma(p)
+      result.add(a)
+  eat(p, tkIn)
+  result.add(parseExpr(p))
+  colcom(p, result)
+  result.add(parseStmt(p))
+  setEndInfo()
+
+template nimprettyDontTouch(body) =
+  when defined(nimpretty):
+    inc p.em.keepIndents
+  body
+  when defined(nimpretty):
+    dec p.em.keepIndents
+
+proc parseExpr(p: var Parser): PNode =
   #| expr = (blockExpr
   #|       | ifExpr
   #|       | whenExpr
-  #|       | caseExpr
+  #|       | caseStmt
+  #|       | forExpr
   #|       | tryExpr)
   #|       / simpleExpr
-  case p.tok.tokType:
-  of tkBlock: result = parseBlock(p)
-  of tkIf: result = parseIfExpr(p, nkIfExpr)
-  of tkWhen: result = parseIfExpr(p, nkWhenExpr)
-  of tkCase: result = parseCase(p)
-  of tkTry: result = parseTry(p, isExpr=true)
+  case p.tok.tokType
+  of tkBlock:
+    nimprettyDontTouch:
+      result = parseBlock(p)
+  of tkIf:
+    nimprettyDontTouch:
+      result = parseIfOrWhenExpr(p, nkIfExpr)
+  of tkFor:
+    nimprettyDontTouch:
+      result = parseFor(p)
+  of tkWhen:
+    nimprettyDontTouch:
+      result = parseIfOrWhenExpr(p, nkWhenStmt)
+  of tkCase:
+    # Currently we think nimpretty is good enough with case expressions,
+    # so it is allowed to touch them:
+    #nimprettyDontTouch:
+    result = parseCase(p)
+  of tkTry:
+    nimprettyDontTouch:
+      result = parseTry(p, isExpr=true)
   else: result = simpleExpr(p)
-
-proc parseEnum(p: var TParser): PNode
-proc parseObject(p: var TParser): PNode
-proc parseTypeClass(p: var TParser): PNode
-
-proc primary(p: var TParser, mode: TPrimaryMode): PNode =
-  #| typeKeyw = 'var' | 'out' | 'ref' | 'ptr' | 'shared' | 'tuple'
-  #|          | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
-  #| primary = typeKeyw typeDescK
-  #|         /  prefixOperator* identOrLiteral primarySuffix*
-  #|         / 'static' primary
-  #|         / 'bind' primary
+  setEndInfo()
+
+proc parseEnum(p: var Parser): PNode
+proc parseObject(p: var Parser): PNode
+proc parseTypeClass(p: var Parser): PNode
+
+proc primary(p: var Parser, mode: PrimaryMode): PNode =
+  #| simplePrimary = SIGILLIKEOP? identOrLiteral primarySuffix*
+  #| commandStart = &('`'|IDENT|literal|'cast'|'addr'|'type'|'var'|'out'|
+  #|                  'static'|'enum'|'tuple'|'object'|'proc')
+  #| primary = simplePrimary (commandStart expr (doBlock extraPostExprBlock*)?)?
+  #|         / operatorB primary
+  #|         / routineExpr
+  #|         / rawTypeDesc
+  #|         / prefixOperator primary
+  # XXX strong spaces need to be reflected in commandStart
+  # command part is handled in the primarySuffix proc
+
+  # prefix operators:
   if isOperator(p.tok):
+    # Note 'sigil like' operators are currently not reflected in the grammar
+    # and should be removed for Nim 2.0, I don't think anybody uses them.
     let isSigil = isSigilLike(p.tok)
     result = newNodeP(nkPrefix, p)
     var a = newIdentNodeP(p.tok.ident, p)
-    addSon(result, a)
+    result.add(a)
     getTok(p)
     optInd(p, a)
-    if isSigil:
-      #XXX prefix operators
+    const identOrLiteralKinds = tkBuiltInMagics + {tkSymbol, tkAccent, tkNil,
+      tkIntLit..tkCustomLit, tkCast, tkOut, tkParLe, tkBracketLe, tkCurlyLe}
+    if isSigil and p.tok.tokType in identOrLiteralKinds:
       let baseInd = p.lex.currLineIndent
-      addSon(result, primary(p, pmSkipSuffix))
-      result = primarySuffix(p, result, baseInd)
+      result.add(identOrLiteral(p, mode))
+      result = primarySuffix(p, result, baseInd, mode)
     else:
-      addSon(result, primary(p, pmNormal))
+      result.add(primary(p, pmNormal))
     return
 
-  case p.tok.tokType:
-  of tkTuple: result = parseTuple(p, mode == pmTypeDef)
-  of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkLambda)
-  of tkFunc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkFuncDef)
+  case p.tok.tokType
+  of tkProc:
+    getTok(p)
+    result = parseProcExpr(p, mode != pmTypeDesc, nkLambda)
+  of tkFunc:
+    getTok(p)
+    result = parseProcExpr(p, mode != pmTypeDesc, nkFuncDef)
   of tkIterator:
-    result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkLambda)
-    if result.kind == nkLambda: result.kind = nkIteratorDef
-    else: result.kind = nkIteratorTy
-  of tkEnum:
-    if mode == pmTypeDef:
-      result = parseEnum(p)
-    else:
-      result = newNodeP(nkEnumTy, p)
-      getTok(p)
-  of tkObject:
-    if mode == pmTypeDef:
-      result = parseObject(p)
-    else:
-      result = newNodeP(nkObjectTy, p)
-      getTok(p)
-  of tkConcept:
-    if mode == pmTypeDef:
-      result = parseTypeClass(p)
-    else:
-      parMessage(p, "the 'concept' keyword is only valid in 'type' sections")
-  of tkStatic:
-    let info = parLineInfo(p)
-    getTokNoInd(p)
-    let next = primary(p, pmNormal)
-    if next.kind == nkBracket and next.sonsLen == 1:
-      result = newNode(nkStaticTy, info, @[next.sons[0]])
-    else:
-      result = newNode(nkStaticExpr, info, @[next])
+    getTok(p)
+    result = parseProcExpr(p, mode != pmTypeDesc, nkIteratorDef)
   of tkBind:
+    # legacy syntax, no-op in current nim
     result = newNodeP(nkBind, p)
     getTok(p)
     optInd(p, result)
-    addSon(result, primary(p, pmNormal))
-  of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode)
-  of tkOut: result = parseTypeDescKAux(p, nkVarTy, mode)
-  of tkRef: result = parseTypeDescKAux(p, nkRefTy, mode)
-  of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, mode)
-  of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, mode)
+    result.add(primary(p, pmNormal))
+  of tkTuple, tkEnum, tkObject, tkConcept,
+    tkVar, tkOut, tkRef, tkPtr, tkDistinct:
+    result = parseTypeDesc(p)
   else:
     let baseInd = p.lex.currLineIndent
     result = identOrLiteral(p, mode)
-    if mode != pmSkipSuffix:
-      result = primarySuffix(p, result, baseInd)
+    result = primarySuffix(p, result, baseInd, mode)
 
-proc parseTypeDesc(p: var TParser): PNode =
-  #| typeDesc = simpleExpr
-  result = simpleExpr(p, pmTypeDesc)
+proc binaryNot(p: var Parser; a: PNode): PNode =
+  if p.tok.tokType == tkNot and p.tok.indent < 0:
+    let notOpr = newIdentNodeP(p.tok.ident, p)
+    getTok(p)
+    optInd(p, notOpr)
+    let b = primary(p, pmTypeDesc)
+    result = newNodeP(nkInfix, p)
+    result.add notOpr
+    result.add a
+    result.add b
+  else:
+    result = a
 
-proc parseTypeDefAux(p: var TParser): PNode =
-  #| typeDefAux = simpleExpr
-  #|            | 'concept' typeClass
-  result = simpleExpr(p, pmTypeDef)
+proc parseTypeDesc(p: var Parser, fullExpr = false): PNode =
+  #| rawTypeDesc = (tupleType | routineType | 'enum' | 'object' |
+  #|                 ('var' | 'out' | 'ref' | 'ptr' | 'distinct') typeDesc?)
+  #|                 ('not' primary)?
+  #| typeDescExpr = (routineType / simpleExpr) ('not' primary)?
+  #| typeDesc = rawTypeDesc / typeDescExpr
+  newlineWasSplitting(p)
+  if fullExpr:
+    result = simpleExpr(p, pmTypeDesc)
+  else:
+    case p.tok.tokType
+    of tkTuple:
+      result = parseTuple(p, false)
+    of tkProc:
+      getTok(p)
+      result = parseProcExpr(p, false, nkLambda)
+    of tkIterator:
+      getTok(p)
+      result = parseProcExpr(p, false, nkIteratorDef)
+    of tkEnum:
+      result = newNodeP(nkEnumTy, p)
+      getTok(p)
+    of tkObject:
+      result = newNodeP(nkObjectTy, p)
+      getTok(p)
+    of tkConcept:
+      result = p.emptyNode
+      parMessage(p, "the 'concept' keyword is only valid in 'type' sections")
+    of tkVar: result = parseTypeDescKAux(p, nkVarTy, pmTypeDesc)
+    of tkOut: result = parseTypeDescKAux(p, nkOutTy, pmTypeDesc)
+    of tkRef: result = parseTypeDescKAux(p, nkRefTy, pmTypeDesc)
+    of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, pmTypeDesc)
+    of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, pmTypeDesc)
+    else:
+      result = simpleExpr(p, pmTypeDesc)
+  result = binaryNot(p, result)
+  setEndInfo()
+
+proc parseTypeDefValue(p: var Parser): PNode =
+  #| typeDefValue = ((tupleDecl | enumDecl | objectDecl | conceptDecl |
+  #|                  ('ref' | 'ptr' | 'distinct') (tupleDecl | objectDecl))
+  #|                / (simpleExpr (exprEqExpr ^+ comma postExprBlocks?)?))
+  #|                ('not' primary)?
+  case p.tok.tokType
+  of tkTuple: result = parseTuple(p, true)
+  of tkRef: result = parseTypeDescKAux(p, nkRefTy, pmTypeDef)
+  of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, pmTypeDef)
+  of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, pmTypeDef)
+  of tkEnum:
+    prettySection:
+      result = parseEnum(p)
+  of tkObject:
+    prettySection:
+      result = parseObject(p)
+  of tkConcept:
+    result = parseTypeClass(p)
+  else:
+    result = simpleExpr(p, pmTypeDef)
+    if p.tok.tokType != tkNot:
+      if result.kind == nkCommand:
+        var isFirstParam = false
+        while p.tok.tokType == tkComma:
+          getTok(p)
+          optInd(p, result)
+          result.add(commandParam(p, isFirstParam, pmTypeDef))
+      result = postExprBlocks(p, result)
+  result = binaryNot(p, result)
+  setEndInfo()
 
 proc makeCall(n: PNode): PNode =
   ## Creates a call if the given node isn't already a call.
   if n.kind in nkCallKinds:
     result = n
   else:
-    result = newNodeI(nkCall, n.info)
+    result = newNode(nkCall, n.info)
     result.add n
 
-proc postExprBlocks(p: var TParser, x: PNode): PNode =
-  #| postExprBlocks = ':' stmt? ( IND{=} doBlock
-  #|                            | IND{=} 'of' exprList ':' stmt
-  #|                            | IND{=} 'elif' expr ':' stmt
-  #|                            | IND{=} 'except' exprList ':' stmt
-  #|                            | IND{=} 'else' ':' stmt )*
+proc postExprBlocks(p: var Parser, x: PNode): PNode =
+  #| extraPostExprBlock = ( IND{=} doBlock
+  #|                      | IND{=} 'of' exprList ':' stmt
+  #|                      | IND{=} 'elif' expr ':' stmt
+  #|                      | IND{=} 'except' optionalExprList ':' stmt
+  #|                      | IND{=} 'finally' ':' stmt
+  #|                      | IND{=} 'else' ':' stmt )
+  #| postExprBlocks = (doBlock / ':' (extraPostExprBlock / stmt)) extraPostExprBlock*
   result = x
   if p.tok.indent >= 0: return
 
@@ -1296,14 +1514,17 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode =
     result = makeCall(result)
     getTok(p)
     skipComment(p, result)
-    if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}:
+    if not (p.tok.tokType in {tkOf, tkElif, tkElse, tkExcept, tkFinally} and sameInd(p)):
       var stmtList = newNodeP(nkStmtList, p)
       stmtList.add parseStmt(p)
       # to keep backwards compatibility (see tests/vm/tstringnil)
-      if stmtList[0].kind == nkStmtList: stmtList = stmtList[0]
+      if stmtList.firstSon.kind == nkStmtList: stmtList = stmtList.firstSon
 
-      stmtList.flags.incl nfBlockArg
-      if openingParams.kind != nkEmpty:
+      setNodeFlag stmtList, nfBlockArg
+      if openingParams.kind != nkEmpty or openingPragmas.kind != nkEmpty:
+        if openingParams.kind == nkEmpty:
+          openingParams = newNodeP(nkFormalParams, p)
+          openingParams.add(p.emptyNode) # return type
         result.add newProcNode(nkDo, stmtList.info, body = stmtList,
                                params = openingParams,
                                name = p.emptyNode, pattern = p.emptyNode,
@@ -1321,7 +1542,7 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode =
         getTok(p)
         nextBlock = parseDoBlock(p, info)
       else:
-        case nextToken:
+        case nextToken
         of tkOf:
           nextBlock = newNodeP(nkOfBranch, p)
           exprList(p, tkColon, nextBlock)
@@ -1329,63 +1550,61 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode =
           nextBlock = newNodeP(nkElifBranch, p)
           getTok(p)
           optInd(p, nextBlock)
-          nextBlock.addSon parseExpr(p)
+          nextBlock.add parseExpr(p)
         of tkExcept:
           nextBlock = newNodeP(nkExceptBranch, p)
-          exprList(p, tkColon, nextBlock)
+          optionalExprList(p, tkColon, nextBlock)
+        of tkFinally:
+          nextBlock = newNodeP(nkFinally, p)
+          getTok(p)
         of tkElse:
           nextBlock = newNodeP(nkElse, p)
           getTok(p)
         else: break
         eat(p, tkColon)
-        nextBlock.addSon parseStmt(p)
+        nextBlock.add parseStmt(p)
 
-      nextBlock.flags.incl nfBlockArg
+      setNodeFlag nextBlock, nfBlockArg
       result.add nextBlock
 
-      if nextBlock.kind == nkElse: break
+      if nextBlock.kind in {nkElse, nkFinally}: break
   else:
     if openingParams.kind != nkEmpty:
       parMessage(p, "expected ':'")
 
-proc parseExprStmt(p: var TParser): PNode =
-  #| exprStmt = simpleExpr
-  #|          (( '=' optInd expr colonBody? )
-  #|          / ( expr ^+ comma
-  #|              doBlocks
-  #|               / macroColon
-  #|            ))?
-  var a = simpleExpr(p)
+proc parseExprStmt(p: var Parser): PNode =
+  #| exprStmt = simpleExpr postExprBlocks?
+  #|          / simplePrimary (exprEqExpr ^+ comma) postExprBlocks?
+  #|          / simpleExpr '=' optInd (expr postExprBlocks?)
+  var a = simpleExpr(p, pmTrySimple)
   if p.tok.tokType == tkEquals:
     result = newNodeP(nkAsgn, p)
     getTok(p)
     optInd(p, result)
     var b = parseExpr(p)
     b = postExprBlocks(p, b)
-    addSon(result, a)
-    addSon(result, b)
+    result.add(a)
+    result.add(b)
   else:
-    # simpleExpr parsed 'p a' from 'p a, b'?
     var isFirstParam = false
-    if p.tok.indent < 0 and p.tok.tokType == tkComma and a.kind == nkCommand:
-      result = a
-      while true:
-        getTok(p)
-        optInd(p, result)
-        addSon(result, commandParam(p, isFirstParam))
-        if p.tok.tokType != tkComma: break
-    elif p.tok.indent < 0 and isExprStart(p):
-      result = newNode(nkCommand, a.info, @[a])
+    # if an expression is starting here, a simplePrimary was parsed and
+    # this is the start of a command
+    if p.tok.indent < 0 and isExprStart(p):
+      result = newTree(nkCommand, a.info, a)
+      let baseIndent = p.currInd
       while true:
-        addSon(result, commandParam(p, isFirstParam))
-        if p.tok.tokType != tkComma: break
+        result.add(commandParam(p, isFirstParam, pmNormal))
+        if p.tok.tokType != tkComma or
+          (p.tok.indent >= 0 and p.tok.indent < baseIndent):
+          break
         getTok(p)
         optInd(p, result)
     else:
       result = a
     result = postExprBlocks(p, result)
+  setEndInfo()
 
-proc parseModuleName(p: var TParser, kind: TNodeKind): PNode =
+proc parseModuleName(p: var Parser, kind: TNodeKind): PNode =
   result = parseExpr(p)
   when false:
     # parseExpr already handles 'as' syntax ...
@@ -1395,19 +1614,23 @@ proc parseModuleName(p: var TParser, kind: TNodeKind): PNode =
       getTok(p)
       result.add(a)
       result.add(parseExpr(p))
+  setEndInfo()
 
-proc parseImport(p: var TParser, kind: TNodeKind): PNode =
+proc parseImport(p: var Parser, kind: TNodeKind): PNode =
   #| importStmt = 'import' optInd expr
   #|               ((comma expr)*
   #|               / 'except' optInd (expr ^+ comma))
+  #| exportStmt = 'export' optInd expr
+  #|               ((comma expr)*
+  #|               / 'except' optInd (expr ^+ comma))
   result = newNodeP(kind, p)
   getTok(p)                   # skip `import` or `export`
   optInd(p, result)
   var a = parseModuleName(p, kind)
-  addSon(result, a)
+  result.add(a)
   if p.tok.tokType in {tkComma, tkExcept}:
     if p.tok.tokType == tkExcept:
-      result.kind = succ(kind)
+      result.transitionSonsKind(succ(kind))
     getTok(p)
     optInd(p, result)
     while true:
@@ -1415,13 +1638,14 @@ proc parseImport(p: var TParser, kind: TNodeKind): PNode =
       p.hasProgress = false
       a = parseModuleName(p, kind)
       if a.kind == nkEmpty or not p.hasProgress: break
-      addSon(result, a)
+      result.add(a)
       if p.tok.tokType != tkComma: break
       getTok(p)
       optInd(p, a)
   #expectNl(p)
+  setEndInfo()
 
-proc parseIncludeStmt(p: var TParser): PNode =
+proc parseIncludeStmt(p: var Parser): PNode =
   #| includeStmt = 'include' optInd expr ^+ comma
   result = newNodeP(nkIncludeStmt, p)
   getTok(p)                   # skip `import` or `include`
@@ -1431,19 +1655,20 @@ proc parseIncludeStmt(p: var TParser): PNode =
     p.hasProgress = false
     var a = parseExpr(p)
     if a.kind == nkEmpty or not p.hasProgress: break
-    addSon(result, a)
+    result.add(a)
     if p.tok.tokType != tkComma: break
     getTok(p)
     optInd(p, a)
   #expectNl(p)
+  setEndInfo()
 
-proc parseFromStmt(p: var TParser): PNode =
-  #| fromStmt = 'from' moduleName 'import' optInd expr (comma expr)*
+proc parseFromStmt(p: var Parser): PNode =
+  #| fromStmt = 'from' expr 'import' optInd expr (comma expr)*
   result = newNodeP(nkFromStmt, p)
   getTok(p)                   # skip `from`
   optInd(p, result)
   var a = parseModuleName(p, nkImportStmt)
-  addSon(result, a)           #optInd(p, a);
+  result.add(a)           #optInd(p, a);
   eat(p, tkImport)
   optInd(p, result)
   while true:
@@ -1451,33 +1676,36 @@ proc parseFromStmt(p: var TParser): PNode =
     p.hasProgress = false
     a = parseExpr(p)
     if a.kind == nkEmpty or not p.hasProgress: break
-    addSon(result, a)
+    result.add(a)
     if p.tok.tokType != tkComma: break
     getTok(p)
     optInd(p, a)
   #expectNl(p)
+  setEndInfo()
 
-proc parseReturnOrRaise(p: var TParser, kind: TNodeKind): PNode =
+proc parseReturnOrRaise(p: var Parser, kind: TNodeKind): PNode =
   #| returnStmt = 'return' optInd expr?
   #| raiseStmt = 'raise' optInd expr?
   #| yieldStmt = 'yield' optInd expr?
   #| discardStmt = 'discard' optInd expr?
   #| breakStmt = 'break' optInd expr?
-  #| continueStmt = 'break' optInd expr?
+  #| continueStmt = 'continue' optInd expr?
   result = newNodeP(kind, p)
   getTok(p)
   if p.tok.tokType == tkComment:
     skipComment(p, result)
-    addSon(result, p.emptyNode)
+    result.add(p.emptyNode)
   elif p.tok.indent >= 0 and p.tok.indent <= p.currInd or not isExprStart(p):
     # NL terminates:
-    addSon(result, p.emptyNode)
+    result.add(p.emptyNode)
+    # nimpretty here!
   else:
     var e = parseExpr(p)
     e = postExprBlocks(p, e)
-    addSon(result, e)
+    result.add(e)
+  setEndInfo()
 
-proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
+proc parseIfOrWhen(p: var Parser, kind: TNodeKind): PNode =
   #| condStmt = expr colcom stmt COMMENT?
   #|            (IND{=} 'elif' expr colcom stmt)*
   #|            (IND{=} 'else' colcom stmt)?
@@ -1488,29 +1716,56 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
     getTok(p)                 # skip `if`, `when`, `elif`
     var branch = newNodeP(nkElifBranch, p)
     optInd(p, branch)
-    addSon(branch, parseExpr(p))
+    branch.add(parseExpr(p))
     colcom(p, branch)
-    addSon(branch, parseStmt(p))
+    branch.add(parseStmt(p))
     skipComment(p, branch)
-    addSon(result, branch)
+    result.add(branch)
     if p.tok.tokType != tkElif or not sameOrNoInd(p): break
   if p.tok.tokType == tkElse and sameOrNoInd(p):
     var branch = newNodeP(nkElse, p)
     eat(p, tkElse)
     colcom(p, branch)
-    addSon(branch, parseStmt(p))
-    addSon(result, branch)
+    branch.add(parseStmt(p))
+    result.add(branch)
+  setEndInfo()
+
+proc parseIfOrWhenExpr(p: var Parser, kind: TNodeKind): PNode =
+  #| condExpr = expr colcom stmt optInd
+  #|         ('elif' expr colcom stmt optInd)*
+  #|          'else' colcom stmt
+  #| ifExpr = 'if' condExpr
+  #| whenExpr = 'when' condExpr
+  result = newNodeP(kind, p)
+  while true:
+    getTok(p)                 # skip `if`, `when`, `elif`
+    var branch = newNodeP(nkElifExpr, p)
+    optInd(p, branch)
+    branch.add(parseExpr(p))
+    colcom(p, branch)
+    branch.add(parseStmt(p))
+    skipComment(p, branch)
+    result.add(branch)
+    if p.tok.tokType != tkElif: break
+  if p.tok.tokType == tkElse:
+    var branch = newNodeP(nkElseExpr, p)
+    eat(p, tkElse)
+    colcom(p, branch)
+    branch.add(parseStmt(p))
+    result.add(branch)
+  setEndInfo()
 
-proc parseWhile(p: var TParser): PNode =
+proc parseWhile(p: var Parser): PNode =
   #| whileStmt = 'while' expr colcom stmt
   result = newNodeP(nkWhileStmt, p)
   getTok(p)
   optInd(p, result)
-  addSon(result, parseExpr(p))
+  result.add(parseExpr(p))
   colcom(p, result)
-  addSon(result, parseStmt(p))
+  result.add(parseStmt(p))
+  setEndInfo()
 
-proc parseCase(p: var TParser): PNode =
+proc parseCase(p: var Parser): PNode =
   #| ofBranch = 'of' exprList colcom stmt
   #| ofBranches = ofBranch (IND{=} ofBranch)*
   #|                       (IND{=} 'elif' expr colcom stmt)*
@@ -1520,11 +1775,11 @@ proc parseCase(p: var TParser): PNode =
   #|             | IND{=} ofBranches)
   var
     b: PNode
-    inElif= false
+    inElif = false
     wasIndented = false
   result = newNodeP(nkCaseStmt, p)
   getTok(p)
-  addSon(result, parseExpr(p))
+  result.add(parseExpr(p))
   if p.tok.tokType == tkColon: getTok(p)
   skipComment(p, result)
 
@@ -1544,105 +1799,94 @@ proc parseCase(p: var TParser): PNode =
       b = newNodeP(nkElifBranch, p)
       getTok(p)
       optInd(p, b)
-      addSon(b, parseExpr(p))
+      b.add(parseExpr(p))
     of tkElse:
       b = newNodeP(nkElse, p)
       getTok(p)
     else: break
     colcom(p, b)
-    addSon(b, parseStmt(p))
-    addSon(result, b)
+    b.add(parseStmt(p))
+    result.add(b)
     if b.kind == nkElse: break
 
   if wasIndented:
     p.currInd = oldInd
+  setEndInfo()
 
-proc parseTry(p: var TParser; isExpr: bool): PNode =
+proc parseTry(p: var Parser; isExpr: bool): PNode =
   #| tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally')
-  #|            (IND{=}? 'except' exprList colcom stmt)*
+  #|            (IND{=}? 'except' optionalExprList colcom stmt)*
   #|            (IND{=}? 'finally' colcom stmt)?
   #| tryExpr = 'try' colcom stmt &(optInd 'except'|'finally')
-  #|            (optInd 'except' exprList colcom stmt)*
+  #|            (optInd 'except' optionalExprList colcom stmt)*
   #|            (optInd 'finally' colcom stmt)?
   result = newNodeP(nkTryStmt, p)
+  let parentIndent = p.currInd # isExpr
   getTok(p)
   colcom(p, result)
-  addSon(result, parseStmt(p))
+  result.add(parseStmt(p))
   var b: PNode = nil
-  while sameOrNoInd(p) or isExpr:
+
+  while sameOrNoInd(p) or (isExpr and parentIndent <= p.tok.indent):
     case p.tok.tokType
     of tkExcept:
       b = newNodeP(nkExceptBranch, p)
-      exprList(p, tkColon, b)
+      optionalExprList(p, tkColon, b)
     of tkFinally:
       b = newNodeP(nkFinally, p)
       getTok(p)
     else: break
     colcom(p, b)
-    addSon(b, parseStmt(p))
-    addSon(result, b)
-    if b.kind == nkFinally: break
+    b.add(parseStmt(p))
+    result.add(b)
   if b == nil: parMessage(p, "expected 'except'")
+  setEndInfo()
 
-proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode =
-  #| exceptBlock = 'except' colcom stmt
+proc parseExceptBlock(p: var Parser, kind: TNodeKind): PNode =
   result = newNodeP(kind, p)
   getTok(p)
   colcom(p, result)
-  addSon(result, parseStmt(p))
+  result.add(parseStmt(p))
+  setEndInfo()
 
-proc parseFor(p: var TParser): PNode =
-  #| forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt
-  result = newNodeP(nkForStmt, p)
-  getTokNoInd(p)
-  var a = identWithPragma(p)
-  addSon(result, a)
-  while p.tok.tokType == tkComma:
-    getTok(p)
-    optInd(p, a)
-    a = identWithPragma(p)
-    addSon(result, a)
-  eat(p, tkIn)
-  addSon(result, parseExpr(p))
-  colcom(p, result)
-  addSon(result, parseStmt(p))
-
-proc parseBlock(p: var TParser): PNode =
+proc parseBlock(p: var Parser): PNode =
   #| blockStmt = 'block' symbol? colcom stmt
   #| blockExpr = 'block' symbol? colcom stmt
   result = newNodeP(nkBlockStmt, p)
   getTokNoInd(p)
-  if p.tok.tokType == tkColon: addSon(result, p.emptyNode)
-  else: addSon(result, parseSymbol(p))
+  if p.tok.tokType == tkColon: result.add(p.emptyNode)
+  else: result.add(parseSymbol(p))
   colcom(p, result)
-  addSon(result, parseStmt(p))
+  result.add(parseStmt(p))
+  setEndInfo()
 
-proc parseStaticOrDefer(p: var TParser; k: TNodeKind): PNode =
+proc parseStaticOrDefer(p: var Parser; k: TNodeKind): PNode =
   #| staticStmt = 'static' colcom stmt
   #| deferStmt = 'defer' colcom stmt
   result = newNodeP(k, p)
   getTok(p)
   colcom(p, result)
-  addSon(result, parseStmt(p))
+  result.add(parseStmt(p))
+  setEndInfo()
 
-proc parseAsm(p: var TParser): PNode =
-  #| asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLE_STR_LIT)
+proc parseAsm(p: var Parser): PNode =
+  #| asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
   result = newNodeP(nkAsmStmt, p)
   getTokNoInd(p)
-  if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p))
-  else: addSon(result, p.emptyNode)
+  if p.tok.tokType == tkCurlyDotLe: result.add(parsePragma(p))
+  else: result.add(p.emptyNode)
   case p.tok.tokType
-  of tkStrLit: addSon(result, newStrNodeP(nkStrLit, p.tok.literal, p))
-  of tkRStrLit: addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p))
-  of tkTripleStrLit: addSon(result,
-                            newStrNodeP(nkTripleStrLit, p.tok.literal, p))
+  of tkStrLit: result.add(newStrNodeP(nkStrLit, p.tok.literal, p))
+  of tkRStrLit: result.add(newStrNodeP(nkRStrLit, p.tok.literal, p))
+  of tkTripleStrLit: result.add(newStrNodeP(nkTripleStrLit, p.tok.literal, p))
   else:
     parMessage(p, "the 'asm' statement takes a string literal")
-    addSon(result, p.emptyNode)
+    result.add(p.emptyNode)
     return
   getTok(p)
+  setEndInfo()
 
-proc parseGenericParam(p: var TParser): PNode =
+proc parseGenericParam(p: var Parser): PNode =
   #| genericParam = symbol (comma symbol)* (colon expr)? ('=' optInd expr)?
   var a: PNode
   result = newNodeP(nkIdentDefs, p)
@@ -1652,32 +1896,33 @@ proc parseGenericParam(p: var TParser): PNode =
     of tkIn, tkOut:
       let x = p.lex.cache.getIdent(if p.tok.tokType == tkIn: "in" else: "out")
       a = newNodeP(nkPrefix, p)
-      a.addSon newIdentNodeP(x, p)
+      a.add newIdentNodeP(x, p)
       getTok(p)
       expectIdent(p)
-      a.addSon(parseSymbol(p))
+      a.add(parseSymbol(p))
     of tkSymbol, tkAccent:
       a = parseSymbol(p)
       if a.kind == nkEmpty: return
     else: break
-    addSon(result, a)
+    result.add(a)
     if p.tok.tokType != tkComma: break
     getTok(p)
     optInd(p, a)
   if p.tok.tokType == tkColon:
     getTok(p)
     optInd(p, result)
-    addSon(result, parseExpr(p))
+    result.add(parseExpr(p))
   else:
-    addSon(result, p.emptyNode)
+    result.add(p.emptyNode)
   if p.tok.tokType == tkEquals:
     getTok(p)
     optInd(p, result)
-    addSon(result, parseExpr(p))
+    result.add(parseExpr(p))
   else:
-    addSon(result, p.emptyNode)
+    result.add(p.emptyNode)
+  setEndInfo()
 
-proc parseGenericParamList(p: var TParser): PNode =
+proc parseGenericParamList(p: var Parser): PNode =
   #| genericParamList = '[' optInd
   #|   genericParam ^* (comma/semicolon) optPar ']'
   result = newNodeP(nkGenericParams, p)
@@ -1686,63 +1931,77 @@ proc parseGenericParamList(p: var TParser): PNode =
   # progress guaranteed
   while p.tok.tokType in {tkSymbol, tkAccent, tkIn, tkOut}:
     var a = parseGenericParam(p)
-    addSon(result, a)
+    result.add(a)
     if p.tok.tokType notin {tkComma, tkSemiColon}: break
-    when defined(nimpretty2):
+    when defined(nimpretty):
       commaWasSemicolon(p.em)
     getTok(p)
     skipComment(p, a)
   optPar(p)
   eat(p, tkBracketRi)
+  setEndInfo()
 
-proc parsePattern(p: var TParser): PNode =
+proc parsePattern(p: var Parser): PNode =
   #| pattern = '{' stmt '}'
   eat(p, tkCurlyLe)
   result = parseStmt(p)
   eat(p, tkCurlyRi)
+  setEndInfo()
 
-proc validInd(p: var TParser): bool =
-  result = p.tok.indent < 0 or p.tok.indent > p.currInd
-
-proc parseRoutine(p: var TParser, kind: TNodeKind): PNode =
+proc parseRoutine(p: var Parser, kind: TNodeKind): PNode =
   #| indAndComment = (IND{>} COMMENT)? | COMMENT?
   #| routine = optInd identVis pattern? genericParamList?
   #|   paramListColon pragma? ('=' COMMENT? stmt)? indAndComment
   result = newNodeP(kind, p)
   getTok(p)
   optInd(p, result)
-  addSon(result, identVis(p))
-  if p.tok.tokType == tkCurlyLe and p.validInd: addSon(result, p.parsePattern)
-  else: addSon(result, p.emptyNode)
+  if kind in {nkProcDef, nkLambda, nkIteratorDef, nkFuncDef} and
+      p.tok.tokType notin {tkSymbol, tokKeywordLow..tokKeywordHigh, tkAccent}:
+    # no name; lambda or proc type
+    # in every context that we can parse a routine, we can also parse these
+    result = parseProcExpr(p, true, if kind == nkProcDef: nkLambda else: kind)
+    return
+  result.add(identVis(p))
+  if p.tok.tokType == tkCurlyLe and p.validInd: result.add(p.parsePattern)
+  else: result.add(p.emptyNode)
   if p.tok.tokType == tkBracketLe and p.validInd:
     result.add(p.parseGenericParamList)
   else:
-    addSon(result, p.emptyNode)
-  addSon(result, p.parseParamList)
-  if p.tok.tokType == tkCurlyDotLe and p.validInd: addSon(result, p.parsePragma)
-  else: addSon(result, p.emptyNode)
+    result.add(p.emptyNode)
+  result.add(p.parseParamList)
+  if p.tok.tokType == tkCurlyDotLe and p.validInd: result.add(p.parsePragma)
+  else: result.add(p.emptyNode)
   # empty exception tracking:
-  addSon(result, p.emptyNode)
-  if p.tok.tokType == tkEquals and p.validInd:
+  result.add(p.emptyNode)
+  let maybeMissEquals = p.tok.tokType != tkEquals
+  if (not maybeMissEquals) and p.validInd:
     getTok(p)
     skipComment(p, result)
-    addSon(result, parseStmt(p))
+    result.add(parseStmt(p))
   else:
-    addSon(result, p.emptyNode)
-  indAndComment(p, result)
-
-proc newCommentStmt(p: var TParser): PNode =
+    result.add(p.emptyNode)
+  indAndComment(p, result, maybeMissEquals)
+  let body = result.lastSon
+  if body.kind == nkStmtList and body.hasSon and body.firstSon.comment.len > 0 and body.firstSon.kind != nkCommentStmt:
+    if result.comment.len == 0:
+      # proc fn*(a: int): int = a ## foo
+      # => moves comment `foo` to `fn`
+      result.comment = body.firstSon.comment
+      body.firstSon.comment = ""
+    #else:
+    #  assert false, p.lex.config$body.info # avoids hard to track bugs, fail early.
+    # Yeah, that worked so well. There IS a bug in this logic, now what?
+  setEndInfo()
+
+proc newCommentStmt(p: var Parser): PNode =
   #| commentStmt = COMMENT
   result = newNodeP(nkCommentStmt, p)
   result.comment = p.tok.literal
   getTok(p)
 
-type
-  TDefParser = proc (p: var TParser): PNode {.nimcall.}
-
-proc parseSection(p: var TParser, kind: TNodeKind,
-                  defparser: TDefParser): PNode =
-  #| section(p) = COMMENT? p / (IND{>} (p / COMMENT)^+IND{=} DED)
+proc parseSection(p: var Parser, kind: TNodeKind,
+                  defparser: proc (p: var Parser): PNode {.nimcall.}): PNode =
+  #| section(RULE) = COMMENT? RULE / (IND{>} (RULE / COMMENT)^+IND{=} DED)
   result = newNodeP(kind, p)
   if kind != nkTypeSection: getTok(p)
   skipComment(p, result)
@@ -1755,73 +2014,70 @@ proc parseSection(p: var TParser, kind: TNodeKind,
         of tkSymbol, tkAccent, tkParLe:
           var a = defparser(p)
           skipComment(p, a)
-          addSon(result, a)
+          result.add(a)
         of tkComment:
           var a = newCommentStmt(p)
-          addSon(result, a)
+          result.add(a)
         else:
           parMessage(p, errIdentifierExpected, p.tok)
           break
-    if result.len == 0: parMessage(p, errIdentifierExpected, p.tok)
+    if not result.hasSon: parMessage(p, errIdentifierExpected, p.tok)
   elif p.tok.tokType in {tkSymbol, tkAccent, tkParLe} and p.tok.indent < 0:
     # tkParLe is allowed for ``var (x, y) = ...`` tuple parsing
-    addSon(result, defparser(p))
+    result.add(defparser(p))
   else:
     parMessage(p, errIdentifierExpected, p.tok)
+  setEndInfo()
 
-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?)+
+proc parseEnum(p: var Parser): PNode =
+  #| enumDecl = 'enum' optInd (symbol pragma? optInd ('=' optInd expr COMMENT?)? comma?)+
   result = newNodeP(nkEnumTy, p)
   getTok(p)
-  addSon(result, p.emptyNode)
+  result.add(p.emptyNode)
   optInd(p, result)
   flexComment(p, result)
   # progress guaranteed
   while true:
     var a = parseSymbol(p)
     if a.kind == nkEmpty: return
+
+    var symPragma = a
+    var pragma: PNode
+    if (p.tok.indent < 0 or p.tok.indent >= p.currInd) and p.tok.tokType == tkCurlyDotLe:
+      pragma = optPragmas(p)
+      symPragma = newNodeP(nkPragmaExpr, p)
+      symPragma.add(a)
+      symPragma.add(pragma)
+    # nimpretty support here
     if p.tok.indent >= 0 and p.tok.indent <= p.currInd:
-      add(result, a)
+      result.add(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)
+      symPragma.add(b)
+      symPragma.add(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)
+    result.add(symPragma)
     if p.tok.indent >= 0 and p.tok.indent <= p.currInd or
         p.tok.tokType == tkEof:
       break
-  if result.len <= 1:
+  if not result.has2Sons:
     parMessage(p, errIdentifierExpected, p.tok)
+  setEndInfo()
 
-proc parseObjectPart(p: var TParser): PNode
-proc parseObjectWhen(p: var TParser): PNode =
+proc parseObjectPart(p: var Parser): PNode
+proc parseObjectWhen(p: var Parser): PNode =
   #| objectWhen = 'when' expr colcom objectPart COMMENT?
   #|             ('elif' expr colcom objectPart COMMENT?)*
   #|             ('else' colcom objectPart COMMENT?)?
@@ -1831,36 +2087,33 @@ proc parseObjectWhen(p: var TParser): PNode =
     getTok(p)                 # skip `when`, `elif`
     var branch = newNodeP(nkElifBranch, p)
     optInd(p, branch)
-    addSon(branch, parseExpr(p))
+    branch.add(parseExpr(p))
     colcom(p, branch)
-    addSon(branch, parseObjectPart(p))
+    branch.add(parseObjectPart(p))
     flexComment(p, branch)
-    addSon(result, branch)
+    result.add(branch)
     if p.tok.tokType != tkElif: break
   if p.tok.tokType == tkElse and sameInd(p):
     var branch = newNodeP(nkElse, p)
     eat(p, tkElse)
     colcom(p, branch)
-    addSon(branch, parseObjectPart(p))
+    branch.add(parseObjectPart(p))
     flexComment(p, branch)
-    addSon(result, branch)
+    result.add(branch)
+  setEndInfo()
 
-proc parseObjectCase(p: var TParser): PNode =
+proc parseObjectCase(p: var Parser): PNode =
   #| objectBranch = 'of' exprList colcom objectPart
   #| objectBranches = objectBranch (IND{=} objectBranch)*
   #|                       (IND{=} 'elif' expr colcom objectPart)*
   #|                       (IND{=} 'else' colcom objectPart)?
-  #| objectCase = 'case' identWithPragma ':' typeDesc ':'? COMMENT?
+  #| objectCase = 'case' declColonEquals ':'? COMMENT?
   #|             (IND{>} objectBranches DED
   #|             | IND{=} objectBranches)
   result = newNodeP(nkRecCase, p)
   getTokNoInd(p)
-  var a = newNodeP(nkIdentDefs, p)
-  addSon(a, identWithPragma(p))
-  eat(p, tkColon)
-  addSon(a, parseTypeDesc(p))
-  addSon(a, p.emptyNode)
-  addSon(result, a)
+  var a = parseIdentColonEquals(p, {withPragma})
+  result.add(a)
   if p.tok.tokType == tkColon: getTok(p)
   flexComment(p, result)
   var wasIndented = false
@@ -1884,13 +2137,14 @@ proc parseObjectCase(p: var TParser): PNode =
     if fields.kind == nkEmpty:
       parMessage(p, errIdentifierExpected, p.tok)
       fields = newNodeP(nkNilLit, p) # don't break further semantic checking
-    addSon(b, fields)
-    addSon(result, b)
+    b.add(fields)
+    result.add(b)
     if b.kind == nkElse: break
   if wasIndented:
     p.currInd = oldInd
+  setEndInfo()
 
-proc parseObjectPart(p: var TParser): PNode =
+proc parseObjectPart(p: var Parser): PNode =
   #| objectPart = IND{>} objectPart^+IND{=} DED
   #|            / objectWhen / objectCase / 'nil' / 'discard' / declColonEquals
   if realInd(p):
@@ -1900,11 +2154,11 @@ proc parseObjectPart(p: var TParser): PNode =
       while sameInd(p):
         case p.tok.tokType
         of tkCase, tkWhen, tkSymbol, tkAccent, tkNil, tkDiscard:
-          addSon(result, parseObjectPart(p))
+          result.add(parseObjectPart(p))
         else:
           parMessage(p, errIdentifierExpected, p.tok)
           break
-  else:
+  elif sameOrNoInd(p):
     case p.tok.tokType
     of tkWhen:
       result = parseObjectWhen(p)
@@ -1919,33 +2173,36 @@ proc parseObjectPart(p: var TParser): PNode =
       getTok(p)
     else:
       result = p.emptyNode
+  else:
+    result = p.emptyNode
+  setEndInfo()
 
-proc parseObject(p: var TParser): PNode =
-  #| object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart
+proc parseObject(p: var Parser): PNode =
+  #| objectDecl = 'object' ('of' typeDesc)? COMMENT? objectPart
   result = newNodeP(nkObjectTy, p)
   getTok(p)
-  if p.tok.tokType == tkCurlyDotLe and p.validInd:
-    addSon(result, parsePragma(p))
-  else:
-    addSon(result, p.emptyNode)
+  result.add(p.emptyNode) # compatibility with old pragma node
   if p.tok.tokType == tkOf and p.tok.indent < 0:
     var a = newNodeP(nkOfInherit, p)
     getTok(p)
-    addSon(a, parseTypeDesc(p))
-    addSon(result, a)
+    a.add(parseTypeDesc(p))
+    result.add(a)
   else:
-    addSon(result, p.emptyNode)
+    result.add(p.emptyNode)
   if p.tok.tokType == tkComment:
     skipComment(p, result)
   # an initial IND{>} HAS to follow:
   if not realInd(p):
-    addSon(result, p.emptyNode)
-    return
-  addSon(result, parseObjectPart(p))
+    result.add(p.emptyNode)
+  else:
+    result.add(parseObjectPart(p))
+  setEndInfo()
 
-proc parseTypeClassParam(p: var TParser): PNode =
-  let modifier = case p.tok.tokType
-    of tkOut, tkVar: nkVarTy
+proc parseTypeClassParam(p: var Parser): PNode =
+  let modifier =
+    case p.tok.tokType
+    of tkVar: nkVarTy
+    of tkOut: nkOutTy
     of tkPtr: nkPtrTy
     of tkRef: nkRefTy
     of tkStatic: nkStaticTy
@@ -1955,92 +2212,151 @@ proc parseTypeClassParam(p: var TParser): PNode =
   if modifier != nkEmpty:
     result = newNodeP(modifier, p)
     getTok(p)
-    result.addSon(p.parseSymbol)
+    result.add(p.parseSymbol)
   else:
     result = p.parseSymbol
+  setEndInfo()
 
-proc parseTypeClass(p: var TParser): PNode =
-  #| typeClassParam = ('var' | 'out')? symbol
-  #| typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
+proc parseTypeClass(p: var Parser): PNode =
+  #| conceptParam = ('var' | 'out' | 'ptr' | 'ref' | 'static' | 'type')? symbol
+  #| conceptDecl = 'concept' conceptParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
   #|               &IND{>} stmt
   result = newNodeP(nkTypeClassTy, p)
   getTok(p)
-  var args = newNodeP(nkArgList, p)
-  addSon(result, args)
-  addSon(args, p.parseTypeClassParam)
-  while p.tok.tokType == tkComma:
-    getTok(p)
-    addSon(args, p.parseTypeClassParam)
+  if p.tok.tokType == tkComment:
+    skipComment(p, result)
+
+  if p.tok.indent < 0:
+    var args = newNodeP(nkArgList, p)
+    result.add(args)
+    args.add(p.parseTypeClassParam)
+    while p.tok.tokType == tkComma:
+      getTok(p)
+      args.add(p.parseTypeClassParam)
+  else:
+    result.add(p.emptyNode) # see ast.isNewStyleConcept
   if p.tok.tokType == tkCurlyDotLe and p.validInd:
-    addSon(result, parsePragma(p))
+    result.add(parsePragma(p))
   else:
-    addSon(result, p.emptyNode)
+    result.add(p.emptyNode)
   if p.tok.tokType == tkOf and p.tok.indent < 0:
     var a = newNodeP(nkOfInherit, p)
     getTok(p)
     # progress guaranteed
     while true:
-      addSon(a, parseTypeDesc(p))
+      a.add(parseTypeDesc(p))
       if p.tok.tokType != tkComma: break
       getTok(p)
-    addSon(result, a)
+    result.add(a)
   else:
-    addSon(result, p.emptyNode)
+    result.add(p.emptyNode)
   if p.tok.tokType == tkComment:
     skipComment(p, result)
   # an initial IND{>} HAS to follow:
   if not realInd(p):
-    addSon(result, p.emptyNode)
+    if result.isNewStyleConcept:
+      parMessage(p, "routine expected, but found '$1' (empty new-styled concepts are not allowed)", p.tok)
+    result.add(p.emptyNode)
   else:
-    addSon(result, parseStmt(p))
+    result.add(parseStmt(p))
+  setEndInfo()
 
-proc parseTypeDef(p: var TParser): PNode =
+proc parseTypeDef(p: var Parser): PNode =
   #|
-  #| typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux
+  #| typeDef = identVisDot genericParamList? pragma '=' optInd typeDefValue
   #|             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
+
   if p.tok.tokType == tkBracketLe and p.validInd:
-    addSon(result, parseGenericParamList(p))
+    genericParam = parseGenericParamList(p)
   else:
-    addSon(result, p.emptyNode)
+    genericParam = p.emptyNode
+
+  pragma = optPragmas(p)
+  if pragma.kind != nkEmpty:
+    identPragma = newNodeP(nkPragmaExpr, p)
+    identPragma.add(identifier)
+    identPragma.add(pragma)
+
+  result.add(identPragma)
+  result.add(genericParam)
+
   if p.tok.tokType == tkEquals:
     result.info = parLineInfo(p)
     getTok(p)
     optInd(p, result)
-    addSon(result, parseTypeDefAux(p))
+    result.add(parseTypeDefValue(p))
   else:
-    addSon(result, p.emptyNode)
+    result.add(p.emptyNode)
   indAndComment(p, result)    # special extension!
+  setEndInfo()
 
-proc parseVarTuple(p: var TParser): PNode =
-  #| varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr
+proc parseVarTuple(p: var Parser): PNode =
+  #| varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')' (':' optInd typeDescExpr)?
+  #| varTuple = varTupleLhs '=' optInd expr
   result = newNodeP(nkVarTuple, p)
   getTok(p)                   # skip '('
   optInd(p, result)
   # progress guaranteed
-  while p.tok.tokType in {tkSymbol, tkAccent}:
-    var a = identWithPragma(p, allowDot=true)
-    addSon(result, a)
+  while p.tok.tokType in {tkSymbol, tkAccent, tkParLe}:
+    var a: PNode
+    if p.tok.tokType == tkParLe:
+      a = parseVarTuple(p)
+      a.add(p.emptyNode)
+    else:
+      a = identWithPragma(p, allowDot=true)
+    result.add(a)
     if p.tok.tokType != tkComma: break
     getTok(p)
     skipComment(p, a)
-  addSon(result, p.emptyNode)         # no type desc
   optPar(p)
   eat(p, tkParRi)
-  eat(p, tkEquals)
-  optInd(p, result)
-  addSon(result, parseExpr(p))
+  if p.tok.tokType == tkColon:
+    getTok(p)
+    optInd(p, result)
+    result.add(parseTypeDesc(p, fullExpr = true))
+  else:
+    result.add(p.emptyNode)         # no type desc
+  setEndInfo()
 
-proc parseVariable(p: var TParser): PNode =
-  #| colonBody = colcom stmt doBlocks?
+proc parseVariable(p: var Parser): PNode =
+  #| colonBody = colcom stmt postExprBlocks?
   #| variable = (varTuple / identColonEquals) colonBody? indAndComment
-  if p.tok.tokType == tkParLe: result = parseVarTuple(p)
+  if p.tok.tokType == tkParLe:
+    result = parseVarTuple(p)
+    eat(p, tkEquals)
+    optInd(p, result)
+    result.add(parseExpr(p))
   else: result = parseIdentColonEquals(p, {withPragma, withDot})
-  result[^1] = postExprBlocks(p, result[^1])
+  result.setLastSon postExprBlocks(p, result.lastSon)
+  indAndComment(p, result)
+  setEndInfo()
+
+proc parseConstant(p: var Parser): PNode =
+  #| constant = (varTuple / identWithPragma) (colon typeDesc)? '=' optInd expr indAndComment
+  if p.tok.tokType == tkParLe: result = parseVarTuple(p)
+  else:
+    result = newNodeP(nkConstDef, p)
+    result.add(identWithPragma(p))
+    if p.tok.tokType == tkColon:
+      getTok(p)
+      optInd(p, result)
+      result.add(parseTypeDesc(p))
+    else:
+      result.add(p.emptyNode)
+  eat(p, tkEquals)
+  optInd(p, result)
+  #add(result, parseStmtListExpr(p))
+  let a = parseExpr(p)
+  result.add postExprBlocks(p, a)
   indAndComment(p, result)
+  setEndInfo()
 
-proc parseBind(p: var TParser, k: TNodeKind): PNode =
+proc parseBind(p: var Parser, k: TNodeKind): PNode =
   #| bindStmt = 'bind' optInd qualifiedIdent ^+ comma
   #| mixinStmt = 'mixin' optInd qualifiedIdent ^+ comma
   result = newNodeP(k, p)
@@ -2049,24 +2365,26 @@ proc parseBind(p: var TParser, k: TNodeKind): PNode =
   # progress guaranteed
   while true:
     var a = qualifiedIdent(p)
-    addSon(result, a)
+    result.add(a)
     if p.tok.tokType != tkComma: break
     getTok(p)
     optInd(p, a)
   #expectNl(p)
+  setEndInfo()
 
-proc parseStmtPragma(p: var TParser): PNode =
+proc parseStmtPragma(p: var Parser): PNode =
   #| pragmaStmt = pragma (':' COMMENT? stmt)?
   result = parsePragma(p)
   if p.tok.tokType == tkColon and p.tok.indent < 0:
     let a = result
-    result = newNodeI(nkPragmaBlock, a.info)
+    result = newNode(nkPragmaBlock, a.info)
     getTok(p)
     skipComment(p, result)
     result.add a
     result.add parseStmt(p)
+  setEndInfo()
 
-proc simpleStmt(p: var TParser): PNode =
+proc simpleStmt(p: var Parser): PNode =
   #| simpleStmt = ((returnStmt | raiseStmt | yieldStmt | discardStmt | breakStmt
   #|            | continueStmt | pragmaStmt | importStmt | exportStmt | fromStmt
   #|            | includeStmt | commentStmt) / exprStmt) COMMENT?
@@ -2089,12 +2407,13 @@ proc simpleStmt(p: var TParser): PNode =
     else: result = p.emptyNode
   if result.kind notin {nkEmpty, nkCommentStmt}: skipComment(p, result)
 
-proc complexOrSimpleStmt(p: var TParser): PNode =
+proc complexOrSimpleStmt(p: var Parser): PNode =
   #| complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt
   #|                     | tryStmt | forStmt
   #|                     | blockStmt | staticStmt | deferStmt | asmStmt
   #|                     | 'proc' routine
   #|                     | 'method' routine
+  #|                     | 'func' routine
   #|                     | 'iterator' routine
   #|                     | 'macro' routine
   #|                     | 'template' routine
@@ -2128,24 +2447,31 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
     if p.tok.tokType == tkParLe:
       getTok(p)
       result = newNodeP(nkTypeOfExpr, p)
-      result.addSon(primary(p, pmTypeDesc))
+      result.add(primary(p, pmTypeDesc))
       eat(p, tkParRi)
       result = parseOperators(p, result, -1, pmNormal)
     else:
       result = parseSection(p, nkTypeSection, parseTypeDef)
-  of tkConst: result = parseSection(p, nkConstSection, parseConstant)
-  of tkLet: result = parseSection(p, nkLetSection, parseVariable)
+  of tkConst:
+    prettySection:
+      result = parseSection(p, nkConstSection, parseConstant)
+  of tkLet:
+    prettySection:
+      result = parseSection(p, nkLetSection, parseVariable)
+  of tkVar:
+    prettySection:
+      result = parseSection(p, nkVarSection, parseVariable)
   of tkWhen: result = parseIfOrWhen(p, nkWhenStmt)
-  of tkVar: result = parseSection(p, nkVarSection, parseVariable)
   of tkBind: result = parseBind(p, nkBindStmt)
   of tkMixin: result = parseBind(p, nkMixinStmt)
   of tkUsing: result = parseSection(p, nkUsingStmt, parseVariable)
   else: result = simpleStmt(p)
 
-proc parseStmt(p: var TParser): PNode =
+proc parseStmt(p: var Parser): PNode =
   #| stmt = (IND{>} complexOrSimpleStmt^+(IND{=} / ';') DED)
   #|      / simpleStmt ^+ ';'
   if p.tok.indent > p.currInd:
+    # nimpretty support here
     result = newNodeP(nkStmtList, p)
     withInd(p):
       while true:
@@ -2164,24 +2490,23 @@ proc parseStmt(p: var TParser): PNode =
           # deprecate this syntax later
           break
         p.hasProgress = false
-        var a = complexOrSimpleStmt(p)
-        if a.kind != nkEmpty:
-          addSon(result, a)
+        if p.tok.tokType in {tkElse, tkElif}:
+          break # Allow this too, see tests/parser/tifexprs
+
+        let a = complexOrSimpleStmt(p)
+        if a.kind == nkEmpty and not p.hasProgress:
+          parMessage(p, errExprExpected, p.tok)
+          break
         else:
-          # This is done to make the new 'if' expressions work better.
-          # XXX Eventually we need to be more strict here.
-          if p.tok.tokType notin {tkElse, tkElif}:
-            parMessage(p, errExprExpected, p.tok)
-            getTok(p)
-          else:
-            break
+          result.add a
+
         if not p.hasProgress and p.tok.tokType == tkEof: break
   else:
     # the case statement is only needed for better error messages:
     case p.tok.tokType
     of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkFunc,
        tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar:
-      parMessage(p, "complex statement requires indentation")
+      parMessage(p, "nestable statement requires indentation")
       result = p.emptyNode
     else:
       if p.inSemiStmtList > 0:
@@ -2200,28 +2525,19 @@ proc parseStmt(p: var TParser): PNode =
           if p.tok.tokType != tkSemiColon: break
           getTok(p)
           if err and p.tok.tokType == tkEof: break
+  setEndInfo()
 
-proc parseAll(p: var TParser): PNode =
-  ## Parses the rest of the input stream held by the parser into a PNode.
-  result = newNodeP(nkStmtList, p)
-  while p.tok.tokType != tkEof:
-    p.hasProgress = false
-    var a = complexOrSimpleStmt(p)
-    if a.kind != nkEmpty and p.hasProgress:
-      addSon(result, a)
-    else:
-      parMessage(p, errExprExpected, p.tok)
-      # bugfix: consume a token here to prevent an endless loop:
-      getTok(p)
-    if p.tok.indent != 0:
-      parMessage(p, errInvalidIndentation)
+proc checkFirstLineIndentation*(p: var Parser) =
+  if p.tok.indent != 0 and tsLeading in p.tok.spacing:
+    parMessage(p, errInvalidIndentation)
 
-proc parseTopLevelStmt(p: var TParser): PNode =
+proc parseTopLevelStmt*(p: var Parser): PNode =
   ## Implements an iterator which, when called repeatedly, returns the next
   ## top-level statement or emptyNode if end of stream.
   result = p.emptyNode
   # progress guaranteed
   while true:
+    # nimpretty support here
     if p.tok.indent != 0:
       if p.firstTok and p.tok.indent < 0: discard
       elif p.tok.tokType != tkSemiColon:
@@ -2243,10 +2559,21 @@ proc parseTopLevelStmt(p: var TParser): PNode =
       result = complexOrSimpleStmt(p)
       if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
       break
+  setEndInfo()
+
+proc parseAll*(p: var Parser): PNode =
+  ## Parses the rest of the input stream held by the parser into a PNode.
+  result = newNodeP(nkStmtList, p)
+  while true:
+    let nextStmt = p.parseTopLevelStmt()
+    if nextStmt.kind == nkEmpty:
+      break
+    result &= nextStmt
+  setEndInfo()
 
 proc parseString*(s: string; cache: IdentCache; config: ConfigRef;
                   filename: string = ""; line: int = 0;
-                  errorHandler: TErrorHandler = nil): PNode =
+                  errorHandler: ErrorHandler = nil): PNode =
   ## Parses a string into an AST, returning the top node.
   ## `filename` and `line`, although optional, provide info so that the
   ## compiler can generate correct error messages referring to the original
@@ -2254,11 +2581,10 @@ proc parseString*(s: string; cache: IdentCache; config: ConfigRef;
   var stream = llStreamOpen(s)
   stream.lineOffset = line
 
-  var parser: TParser
-  # XXX for now the builtin 'parseStmt/Expr' functions do not know about strong
-  # spaces...
-  parser.lex.errorHandler = errorHandler
-  openParser(parser, filename, stream, cache, config, false)
+  var p = Parser()
+  p.lex.errorHandler = errorHandler
+  openParser(p, AbsoluteFile filename, stream, cache, config)
 
-  result = parser.parseAll
-  closeParser(parser)
+  result = p.parseAll
+  closeParser(p)
+  setEndInfo()
diff --git a/compiler/passaux.nim b/compiler/passaux.nim
index eabce8822..af507d210 100644
--- a/compiler/passaux.nim
+++ b/compiler/passaux.nim
@@ -10,26 +10,24 @@
 ## implements some little helper passes
 
 import
-  strutils, ast, astalgo, passes, idents, msgs, options, idgen, lineinfos
+  ast, passes, msgs, options, lineinfos
 
-from modulegraphs import ModuleGraph
+from modulegraphs import ModuleGraph, PPassContext
 
 type
-  VerboseRef = ref object of TPassContext
+  VerboseRef = ref object of PPassContext
     config: ConfigRef
 
-proc verboseOpen(graph: ModuleGraph; s: PSym): PPassContext =
-  #MessageOut('compiling ' + s.name.s);
-  result = VerboseRef(config: graph.config)
-  rawMessage(graph.config, hintProcessing, s.name.s)
+proc verboseOpen(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext =
+  # xxx consider either removing this or keeping for documentation for how to add a pass
+  result = VerboseRef(config: graph.config, idgen: idgen)
+
+import std/objectdollar
 
 proc verboseProcess(context: PPassContext, n: PNode): PNode =
+  # called from `process` in `processTopLevelStmt`.
   result = n
   let v = VerboseRef(context)
-  if v.config.verbosity == 3:
-    # system.nim deactivates all hints, for verbosity:3 we want the processing
-    # messages nonetheless, so we activate them again unconditionally:
-    incl(v.config.notes, hintProcessing)
-    message(v.config, n.info, hintProcessing, $idgen.gFrontendId)
+  message(v.config, n.info, hintProcessingStmt, $v.idgen[])
 
 const verbosePass* = makePass(open = verboseOpen, process = verboseProcess)
diff --git a/compiler/passes.nim b/compiler/passes.nim
index 45c726f2a..d6b141078 100644
--- a/compiler/passes.nim
+++ b/compiler/passes.nim
@@ -11,26 +11,28 @@
 ## `TPass` interface.
 
 import
-  strutils, options, ast, astalgo, llstream, msgs, platform, os,
-  condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
-  nimsets, syntaxes, times, idgen, modulegraphs, reorder, rod,
-  lineinfos
+  options, ast, llstream, msgs,
+  idents,
+  syntaxes, modulegraphs, reorder,
+  lineinfos,
+  pipelineutils,
+  modules, pathutils, packages,
+  sem, semdata
 
+import ic/replayer
 
-type
-  TPassContext* = object of RootObj # the pass's context
+export skipCodegen, resolveMod, prepareConfigNotes
 
-  PPassContext* = ref TPassContext
+when defined(nimsuggest):
+  import ../dist/checksums/src/checksums/sha1
 
-  TPassOpen* = proc (graph: ModuleGraph; module: PSym): PPassContext {.nimcall.}
-  TPassClose* = proc (graph: ModuleGraph; p: PPassContext, n: PNode): PNode {.nimcall.}
-  TPassProcess* = proc (p: PPassContext, topLevelStmt: PNode): PNode {.nimcall.}
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions]
 
-  TPass* = tuple[open: TPassOpen, process: TPassProcess, close: TPassClose,
-                 isFrontend: bool]
+import std/tables
 
+type
   TPassData* = tuple[input: PNode, closeOutput: PNode]
-  TPasses* = openArray[TPass]
 
 # a pass is a tuple of procedure vars ``TPass.close`` may produce additional
 # nodes. These are passed to the other close procedures.
@@ -45,156 +47,209 @@ proc makePass*(open: TPassOpen = nil,
   result.process = process
   result.isFrontend = isFrontend
 
-proc skipCodegen*(config: ConfigRef; n: PNode): bool {.inline.} =
-  # can be used by codegen passes to determine whether they should do
-  # something with `n`. Currently, this ignores `n` and uses the global
-  # error count instead.
-  result = config.errorCounter > 0
-
 const
   maxPasses = 10
 
 type
   TPassContextArray = array[0..maxPasses - 1, PPassContext]
 
-var
-  gPasses: array[0..maxPasses - 1, TPass]
-  gPassesLen*: int
-
 proc clearPasses*(g: ModuleGraph) =
-  gPassesLen = 0
+  g.passes.setLen(0)
 
 proc registerPass*(g: ModuleGraph; p: TPass) =
-  gPasses[gPassesLen] = p
-  inc(gPassesLen)
-
-proc carryPass*(g: ModuleGraph; p: TPass, module: PSym;
-                m: TPassData): TPassData =
-  var c = p.open(g, module)
-  result.input = p.process(c, m.input)
-  result.closeOutput = if p.close != nil: p.close(g, c, m.closeOutput)
-                       else: m.closeOutput
-
-proc carryPasses*(g: ModuleGraph; nodes: PNode, module: PSym;
-                  passes: TPasses) =
-  var passdata: TPassData
-  passdata.input = nodes
-  for pass in passes:
-    passdata = carryPass(g, pass, module, passdata)
+  internalAssert g.config, g.passes.len < maxPasses
+  g.passes.add(p)
 
 proc openPasses(g: ModuleGraph; a: var TPassContextArray;
-                module: PSym) =
-  for i in countup(0, gPassesLen - 1):
-    if not isNil(gPasses[i].open):
-      a[i] = gPasses[i].open(g, module)
+                module: PSym; idgen: IdGenerator) =
+  for i in 0..<g.passes.len:
+    if not isNil(g.passes[i].open):
+      a[i] = g.passes[i].open(g, module, idgen)
     else: a[i] = nil
 
 proc closePasses(graph: ModuleGraph; a: var TPassContextArray) =
   var m: PNode = nil
-  for i in countup(0, gPassesLen - 1):
-    if not isNil(gPasses[i].close): m = gPasses[i].close(graph, a[i], m)
+  for i in 0..<graph.passes.len:
+    if not isNil(graph.passes[i].close):
+      m = graph.passes[i].close(graph, a[i], m)
     a[i] = nil                # free the memory here
 
-proc processTopLevelStmt(n: PNode, a: var TPassContextArray): bool =
+proc processTopLevelStmt(graph: ModuleGraph, n: PNode, a: var TPassContextArray): bool =
   # this implements the code transformation pipeline
   var m = n
-  for i in countup(0, gPassesLen - 1):
-    if not isNil(gPasses[i].process):
-      m = gPasses[i].process(a[i], m)
+  for i in 0..<graph.passes.len:
+    if not isNil(graph.passes[i].process):
+      m = graph.passes[i].process(a[i], m)
       if isNil(m): return false
   result = true
 
-proc resolveMod(conf: ConfigRef; module, relativeTo: string): FileIndex =
-  let fullPath = findModule(conf, module, relativeTo)
-  if fullPath.len == 0:
-    result = InvalidFileIDX
-  else:
-    result = fileInfoIdx(conf, fullPath)
-
-proc processImplicits(conf: ConfigRef; implicits: seq[string], nodeKind: TNodeKind,
+proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
                       a: var TPassContextArray; m: PSym) =
   # XXX fixme this should actually be relative to the config file!
-  let gCmdLineInfo = newLineInfo(FileIndex(0), 1, 1)
-  let relativeTo = toFullPath(conf, m.info)
+  let relativeTo = toFullPath(graph.config, m.info)
   for module in items(implicits):
     # implicit imports should not lead to a module importing itself
-    if m.position != resolveMod(conf, module, relativeTo).int32:
-      var importStmt = newNodeI(nodeKind, gCmdLineInfo)
+    if m.position != resolveMod(graph.config, module, relativeTo).int32:
+      var importStmt = newNodeI(nodeKind, m.info)
       var str = newStrNode(nkStrLit, module)
-      str.info = gCmdLineInfo
-      importStmt.addSon str
-      if not processTopLevelStmt(importStmt, a): break
+      str.info = m.info
+      importStmt.add str
+      if not processTopLevelStmt(graph, importStmt, a): break
 
-proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {.discardable.} =
+proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
+                    stream: PLLStream): bool {.discardable.} =
   if graph.stopCompile(): return true
   var
-    p: TParsers
+    p: Parser
     a: TPassContextArray
     s: PLLStream
     fileIdx = module.fileIdx
-  if module.id < 0:
-    # new module caching mechanism:
-    for i in 0..<gPassesLen:
-      if not isNil(gPasses[i].open) and not gPasses[i].isFrontend:
-        a[i] = gPasses[i].open(graph, module)
-      else:
-        a[i] = nil
-
-    if not graph.stopCompile():
-      let n = loadNode(graph, module)
-      var m = n
-      for i in 0..<gPassesLen:
-        if not isNil(gPasses[i].process) and not gPasses[i].isFrontend:
-          m = gPasses[i].process(a[i], m)
-          if isNil(m):
-            break
-
-    var m: PNode = nil
-    for i in 0..<gPassesLen:
-      if not isNil(gPasses[i].close) and not gPasses[i].isFrontend:
-        m = gPasses[i].close(graph, a[i], m)
-      a[i] = nil
+  prepareConfigNotes(graph, module)
+  openPasses(graph, a, module, idgen)
+  if stream == nil:
+    let filename = toFullPathConsiderDirty(graph.config, fileIdx)
+    s = llStreamOpen(filename, fmRead)
+    if s == nil:
+      rawMessage(graph.config, errCannotOpenFile, filename.string)
+      return false
   else:
-    openPasses(graph, a, module)
-    if stream == nil:
-      let filename = toFullPathConsiderDirty(graph.config, fileIdx)
-      s = llStreamOpen(filename, fmRead)
-      if s == nil:
-        rawMessage(graph.config, errCannotOpenFile, filename)
-        return false
-    else:
-      s = stream
-    while true:
-      openParsers(p, fileIdx, s, graph.cache, graph.config)
-
-      if sfSystemModule notin module.flags:
-        # 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
-        # for the interactive mode.
-        processImplicits graph.config, graph.config.implicitImports, nkImportStmt, a, module
-        processImplicits graph.config, graph.config.implicitIncludes, nkIncludeStmt, a, module
-
+    s = stream
+
+  when defined(nimsuggest):
+    let filename = toFullPathConsiderDirty(graph.config, fileIdx).string
+    msgs.setHash(graph.config, fileIdx, $sha1.secureHashFile(filename))
+
+  while true:
+    openParser(p, fileIdx, s, graph.cache, graph.config)
+
+    if (not belongsToStdlib(graph, module)) 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
+      # for the interactive mode.
+      if module.name.s != "nimscriptapi":
+        processImplicits graph, graph.config.implicitImports, nkImportStmt, a, module
+        processImplicits graph, graph.config.implicitIncludes, nkIncludeStmt, a, module
+
+    checkFirstLineIndentation(p)
+    block processCode:
+      if graph.stopCompile(): break processCode
+      var n = parseTopLevelStmt(p)
+      if n.kind == nkEmpty: break processCode
+
+      # read everything, no streaming possible
+      var sl = newNodeI(nkStmtList, n.info)
+      sl.add n
       while true:
-        if graph.stopCompile(): break
         var n = parseTopLevelStmt(p)
         if n.kind == nkEmpty: break
-        if {sfNoForward, sfReorder} * module.flags != {}:
-          # read everything, no streaming possible
-          var sl = newNodeI(nkStmtList, n.info)
-          sl.add n
-          while true:
-            var n = parseTopLevelStmt(p)
-            if n.kind == nkEmpty: break
-            sl.add n
-          if sfReorder in module.flags:
-            sl = reorder(graph, sl, module)
-          discard processTopLevelStmt(sl, a)
-          break
-        elif not processTopLevelStmt(n, a): break
-      closeParsers(p)
-      if s.kind != llsStdIn: break
-    closePasses(graph, a)
-    # id synchronization point for more consistent code generation:
-    idSynchronizationPoint(1000)
+        sl.add n
+      if sfReorder in module.flags or codeReordering in graph.config.features:
+        sl = reorder(graph, sl, module)
+      discard processTopLevelStmt(graph, sl, a)
+
+    closeParser(p)
+    if s.kind != llsStdIn: break
+  closePasses(graph, a)
+  if graph.config.backend notin {backendC, backendCpp, backendObjc}:
+    # We only write rod files here if no C-like backend is active.
+    # The C-like backends have been patched to support the IC mechanism.
+    # They are responsible for closing the rod files. See `cbackend.nim`.
+    closeRodFile(graph, module)
   result = true
+
+proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fromModule: PSym = nil): PSym =
+  var flags = flags
+  if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule
+  result = graph.getModule(fileIdx)
+
+  template processModuleAux(moduleStatus) =
+    onProcessing(graph, fileIdx, moduleStatus, fromModule = fromModule)
+    var s: PLLStream = nil
+    if sfMainModule in flags:
+      if graph.config.projectIsStdin: s = stdin.llStreamOpen
+      elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput)
+    discard processModule(graph, result, idGeneratorFromModule(result), s)
+  if result == nil:
+    var cachedModules: seq[FileIndex] = @[]
+    result = moduleFromRodFile(graph, fileIdx, cachedModules)
+    let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
+    if result == nil:
+      result = newModule(graph, fileIdx)
+      result.flags.incl flags
+      registerModule(graph, result)
+      processModuleAux("import")
+    else:
+      if sfSystemModule in flags:
+        graph.systemModule = result
+      partialInitModule(result, graph, fileIdx, filename)
+    for m in cachedModules:
+      registerModuleById(graph, m)
+      replayStateChanges(graph.packed.pm[m.int].module, graph)
+      replayGenericCacheInformation(graph, m.int)
+  elif graph.isDirty(result):
+    result.flags.excl sfDirty
+    # reset module fields:
+    initStrTables(graph, result)
+    result.ast = nil
+    processModuleAux("import(dirty)")
+    graph.markClientsDirty(fileIdx)
+
+proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym =
+  # this is called by the semantic checking phase
+  assert graph.config != nil
+  result = compileModule(graph, fileIdx, {}, s)
+  graph.addDep(s, fileIdx)
+  # keep track of import relationships
+  if graph.config.hcrOn:
+    graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx)
+  #if sfSystemModule in result.flags:
+  #  localError(result.info, errAttemptToRedefine, result.name.s)
+  # restore the notes for outer module:
+  graph.config.notes =
+    if graph.config.belongsToProjectPackage(s) or isDefined(graph.config, "booting"): graph.config.mainPackageNotes
+    else: graph.config.foreignPackageNotes
+
+proc connectCallbacks*(graph: ModuleGraph) =
+  graph.includeFileCallback = modules.includeModule
+  graph.importModuleCallback = importModule
+
+proc compileSystemModule*(graph: ModuleGraph) =
+  if graph.systemModule == nil:
+    connectCallbacks(graph)
+    graph.config.m.systemFileIdx = fileInfoIdx(graph.config,
+        graph.config.libpath / RelativeFile"system.nim")
+    discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule})
+
+proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) =
+  connectCallbacks(graph)
+  let conf = graph.config
+  wantMainModule(conf)
+  configComplete(graph)
+
+  let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim")
+  let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx
+  conf.projectMainIdx2 = projectFile
+
+  let packSym = getPackage(graph, projectFile)
+  graph.config.mainPackageId = packSym.getPackageId
+  graph.importStack.add projectFile
+
+  if projectFile == systemFileIdx:
+    discard graph.compileModule(projectFile, {sfMainModule, sfSystemModule})
+  else:
+    graph.compileSystemModule()
+    discard graph.compileModule(projectFile, {sfMainModule})
+
+proc mySemOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
+  result = preparePContext(graph, module, idgen)
+
+proc mySemClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
+  var c = PContext(context)
+  closePContext(graph, c, n)
+
+proc mySemProcess(context: PPassContext, n: PNode): PNode =
+  result = semWithPContext(PContext(context), n)
+
+const semPass* = makePass(mySemOpen, mySemProcess, mySemClose,
+                          isFrontend = true)
diff --git a/compiler/pathutils.nim b/compiler/pathutils.nim
new file mode 100644
index 000000000..5f6212bb2
--- /dev/null
+++ b/compiler/pathutils.nim
@@ -0,0 +1,153 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Path handling utilities for Nim. Strictly typed code in order
+## to avoid the never ending time sink in getting path handling right.
+
+import std/[os, pathnorm, strutils]
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions]
+
+type
+  AbsoluteFile* = distinct string
+  AbsoluteDir* = distinct string
+  RelativeFile* = distinct string
+  RelativeDir* = distinct string
+  AnyPath* = AbsoluteFile|AbsoluteDir|RelativeFile|RelativeDir
+
+proc isEmpty*(x: AnyPath): bool {.inline.} = x.string.len == 0
+
+proc copyFile*(source, dest: AbsoluteFile) =
+  os.copyFile(source.string, dest.string)
+
+proc removeFile*(x: AbsoluteFile) {.borrow.}
+
+proc splitFile*(x: AbsoluteFile): tuple[dir: AbsoluteDir, name, ext: string] =
+  let (a, b, c) = splitFile(x.string)
+  result = (dir: AbsoluteDir(a), name: b, ext: c)
+
+proc extractFilename*(x: AbsoluteFile): string {.borrow.}
+
+proc fileExists*(x: AbsoluteFile): bool {.borrow.}
+proc dirExists*(x: AbsoluteDir): bool {.borrow.}
+
+proc quoteShell*(x: AbsoluteFile): string {.borrow.}
+proc quoteShell*(x: AbsoluteDir): string {.borrow.}
+
+proc cmpPaths*(x, y: AbsoluteDir): int {.borrow.}
+
+proc createDir*(x: AbsoluteDir) {.borrow.}
+
+proc toAbsoluteDir*(path: string): AbsoluteDir =
+  result = if path.isAbsolute: AbsoluteDir(path)
+           else: AbsoluteDir(getCurrentDir() / path)
+
+proc `$`*(x: AnyPath): string = x.string
+
+when true:
+  proc eqImpl(x, y: string): bool {.inline.} =
+    result = cmpPaths(x, y) == 0
+
+  proc `==`*[T: AnyPath](x, y: T): bool = eqImpl(x.string, y.string)
+
+  template postProcessBase(base: AbsoluteDir): untyped =
+    # xxx: as argued here https://github.com/nim-lang/Nim/pull/10018#issuecomment-448192956
+    # empty paths should not mean `cwd` so the correct behavior would be to throw
+    # here and make sure `outDir` is always correctly initialized; for now
+    # we simply preserve pre-existing external semantics and treat it as `cwd`
+    when false:
+      doAssert isAbsolute(base.string), base.string
+      base
+    else:
+      if base.isEmpty: getCurrentDir().AbsoluteDir else: base
+
+  proc `/`*(base: AbsoluteDir; f: RelativeFile): AbsoluteFile =
+    let base = postProcessBase(base)
+    assert(not isAbsolute(f.string), f.string)
+    result = AbsoluteFile newStringOfCap(base.string.len + f.string.len)
+    var state = 0
+    addNormalizePath(base.string, result.string, state)
+    addNormalizePath(f.string, result.string, state)
+
+  proc `/`*(base: AbsoluteDir; f: RelativeDir): AbsoluteDir =
+    let base = postProcessBase(base)
+    assert(not isAbsolute(f.string))
+    result = AbsoluteDir newStringOfCap(base.string.len + f.string.len)
+    var state = 0
+    addNormalizePath(base.string, result.string, state)
+    addNormalizePath(f.string, result.string, state)
+
+  proc relativeTo*(fullPath: AbsoluteFile, baseFilename: AbsoluteDir;
+                   sep = DirSep): RelativeFile =
+    # this currently fails for `tests/compilerapi/tcompilerapi.nim`
+    # it's needed otherwise would returns an absolute path
+    # assert not baseFilename.isEmpty, $fullPath
+    result = RelativeFile(relativePath(fullPath.string, baseFilename.string, sep))
+
+  proc toAbsolute*(file: string; base: AbsoluteDir): AbsoluteFile =
+    if isAbsolute(file): result = AbsoluteFile(file)
+    else: result = base / RelativeFile file
+
+  proc changeFileExt*(x: AbsoluteFile; ext: string): AbsoluteFile {.borrow.}
+  proc changeFileExt*(x: RelativeFile; ext: string): RelativeFile {.borrow.}
+
+  proc addFileExt*(x: AbsoluteFile; ext: string): AbsoluteFile {.borrow.}
+  proc addFileExt*(x: RelativeFile; ext: string): RelativeFile {.borrow.}
+
+  proc writeFile*(x: AbsoluteFile; content: string) {.borrow.}
+
+proc skipHomeDir(x: string): int =
+  when defined(windows):
+    if x.continuesWith("Users/", len("C:/")):
+      result = 3
+    else:
+      result = 0
+  else:
+    if x.startsWith("/home/") or x.startsWith("/Users/"):
+      result = 3
+    elif x.startsWith("/mnt/") and x.continuesWith("/Users/", len("/mnt/c")):
+      result = 5
+    else:
+      result = 0
+
+proc relevantPart(s: string; afterSlashX: int): string =
+  result = newStringOfCap(s.len - 8)
+  var slashes = afterSlashX
+  for i in 0..<s.len:
+    if slashes == 0:
+      result.add s[i]
+    elif s[i] == '/':
+      dec slashes
+
+template canonSlashes(x: string): string =
+  when defined(windows):
+    x.replace('\\', '/')
+  else:
+    x
+
+proc customPathImpl(x: string): string =
+  # Idea: Encode a "protocol" via "//protocol/path" which is not ambiguous
+  # as path canonicalization would have removed the double slashes.
+  # /mnt/X/Users/Y
+  # X:\\Users\Y
+  # /home/Y
+  # -->
+  # //user/
+  if not isAbsolute(x):
+    result = customPathImpl(canonSlashes(getCurrentDir() / x))
+  else:
+    let slashes = skipHomeDir(x)
+    if slashes > 0:
+      result = "//user/" & relevantPart(x, slashes)
+    else:
+      result = x
+
+proc customPath*(x: string): string =
+  customPathImpl canonSlashes x
diff --git a/compiler/patterns.nim b/compiler/patterns.nim
index 2d2aeba76..32ec7fb53 100644
--- a/compiler/patterns.nim
+++ b/compiler/patterns.nim
@@ -11,8 +11,10 @@
 ## macro support.
 
 import
-  ast, astalgo, types, semdata, sigmatch, msgs, idents, aliases, parampatterns,
-  trees
+  ast, types, semdata, sigmatch, idents, aliases, parampatterns, trees
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 type
   TPatternContext = object
@@ -21,31 +23,36 @@ type
     formals: int
     c: PContext
     subMatch: bool       # subnode matches are special
+    mappingIsFull: bool
   PPatternContext = var TPatternContext
 
 proc getLazy(c: PPatternContext, sym: PSym): PNode =
-  if not isNil(c.mapping):
+  if c.mappingIsFull:
     result = c.mapping[sym.position]
+  else:
+    result = nil
 
 proc putLazy(c: PPatternContext, sym: PSym, n: PNode) =
-  if isNil(c.mapping): newSeq(c.mapping, c.formals)
+  if not c.mappingIsFull:
+    newSeq(c.mapping, c.formals)
+    c.mappingIsFull = true
   c.mapping[sym.position] = n
 
 proc matches(c: PPatternContext, p, n: PNode): bool
 
 proc canonKind(n: PNode): TNodeKind =
-  ## nodekind canonilization for pattern matching
+  ## nodekind canonicalization for pattern matching
   result = n.kind
   case result
   of nkCallKinds: result = nkCall
   of nkStrLit..nkTripleStrLit: result = nkStrLit
-  of nkFastAsgn: result = nkAsgn
+  of nkFastAsgn, nkSinkAsgn: result = nkAsgn
   else: discard
 
 proc sameKinds(a, b: PNode): bool {.inline.} =
   result = a.kind == b.kind or a.canonKind == b.canonKind
 
-proc sameTrees(a, b: PNode): bool =
+proc sameTrees*(a, b: PNode): bool =
   if sameKinds(a, b):
     case a.kind
     of nkSym: result = a.sym == b.sym
@@ -56,18 +63,25 @@ proc sameTrees(a, b: PNode): bool =
     of nkEmpty, nkNilLit: result = true
     of nkType: result = sameTypeOrNil(a.typ, b.typ)
     else:
-      if sonsLen(a) == sonsLen(b):
-        for i in countup(0, sonsLen(a) - 1):
-          if not sameTrees(a.sons[i], b.sons[i]): return
+      if a.len == b.len:
+        for i in 0..<a.len:
+          if not sameTrees(a[i], b[i]): return
         result = true
+      else:
+        result = false
+  else:
+    result = false
 
 proc inSymChoice(sc, x: PNode): bool =
   if sc.kind == nkClosedSymChoice:
+    result = false
     for i in 0..<sc.len:
-      if sc.sons[i].sym == x.sym: return true
+      if sc[i].sym == x.sym: return true
   elif sc.kind == nkOpenSymChoice:
     # same name suffices for open sym choices!
-    result = sc.sons[0].sym.name.id == x.sym.name.id
+    result = sc[0].sym.name.id == x.sym.name.id
+  else:
+    result = false
 
 proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool =
   # check param constraints first here as this is quite optimized:
@@ -75,7 +89,7 @@ proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool =
     result = matchNodeKinds(p.constraint, n)
     if not result: return
   if isNil(n.typ):
-    result = p.typ.kind in {tyVoid, tyStmt}
+    result = p.typ.kind in {tyVoid, tyTyped}
   else:
     result = sigmatch.argtypeMatches(c.c, p.typ, n.typ, fromHlo = true)
 
@@ -83,8 +97,9 @@ proc isPatternParam(c: PPatternContext, p: PNode): bool {.inline.} =
   result = p.kind == nkSym and p.sym.kind == skParam and p.sym.owner == c.owner
 
 proc matchChoice(c: PPatternContext, p, n: PNode): bool =
-  for i in 1 ..< p.len:
-    if matches(c, p.sons[i], n): return true
+  result = false
+  for i in 1..<p.len:
+    if matches(c, p[i], n): return true
 
 proc bindOrCheck(c: PPatternContext, param: PSym, n: PNode): bool =
   var pp = getLazy(c, param)
@@ -94,6 +109,8 @@ proc bindOrCheck(c: PPatternContext, param: PSym, n: PNode): bool =
   elif n.kind == nkArgList or checkTypes(c, param, n):
     putLazy(c, param, n)
     result = true
+  else:
+    result = false
 
 proc gather(c: PPatternContext, param: PSym, n: PNode) =
   var pp = getLazy(c, param)
@@ -101,7 +118,7 @@ proc gather(c: PPatternContext, param: PSym, n: PNode) =
     pp.add(n)
   else:
     pp = newNodeI(nkArgList, n.info, 1)
-    pp.sons[0] = n
+    pp[0] = n
     putLazy(c, param, pp)
 
 proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool =
@@ -109,24 +126,28 @@ proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool =
   proc matchStarAux(c: PPatternContext, op, n, arglist: PNode,
                     rpn: bool): bool =
     result = true
-    if n.kind in nkCallKinds and matches(c, op.sons[1], n.sons[0]):
-      for i in 1..sonsLen(n)-1:
+    if n.kind in nkCallKinds and matches(c, op[1], n[0]):
+      for i in 1..<n.len:
         if not matchStarAux(c, op, n[i], arglist, rpn): return false
-      if rpn: arglist.add(n.sons[0])
-    elif n.kind == nkHiddenStdConv and n.sons[1].kind == nkBracket:
-      let n = n.sons[1]
+      if rpn: arglist.add(n[0])
+    elif n.kind == nkHiddenStdConv and n[1].kind == nkBracket:
+      let n = n[1]
       for i in 0..<n.len:
         if not matchStarAux(c, op, n[i], arglist, rpn): return false
-    elif checkTypes(c, p.sons[2].sym, n):
-      add(arglist, n)
+    elif checkTypes(c, p[2].sym, n):
+      arglist.add(n)
     else:
       result = false
 
   if n.kind notin nkCallKinds: return false
-  if matches(c, p.sons[1], n.sons[0]):
+  if matches(c, p[1], n[0]):
     var arglist = newNodeI(nkArgList, n.info)
     if matchStarAux(c, p, n, arglist, rpn):
-      result = bindOrCheck(c, p.sons[2].sym, arglist)
+      result = bindOrCheck(c, p[2].sym, arglist)
+    else:
+      result = false
+  else:
+    result = false
 
 proc matches(c: PPatternContext, p, n: PNode): bool =
   let n = skipHidden(n)
@@ -141,27 +162,34 @@ proc matches(c: PPatternContext, p, n: PNode): bool =
   elif n.kind == nkSym and n.sym.kind == skConst:
     # try both:
     if p.kind == nkSym: result = p.sym == n.sym
-    elif matches(c, p, n.sym.ast): result = true
+    elif matches(c, p, n.sym.astdef): result = true
+    else: result = false
   elif p.kind == nkPattern:
     # pattern operators: | *
-    let opr = p.sons[0].ident.s
+    let opr = p[0].ident.s
     case opr
     of "|": result = matchChoice(c, p, n)
     of "*": result = matchNested(c, p, n, rpn=false)
     of "**": result = matchNested(c, p, n, rpn=true)
-    of "~": result = not matches(c, p.sons[1], n)
-    else: doAssert(false, "invalid pattern")
+    of "~": result = not matches(c, p[1], n)
+    else:
+      result = false
+      doAssert(false, "invalid pattern")
     # template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) =
-    #   add(a, b)
+    #   a.add(b)
   elif p.kind == nkCurlyExpr:
-    if p.sons[1].kind == nkPrefix:
-      if matches(c, p.sons[0], n):
-        gather(c, p.sons[1].sons[1].sym, n)
+    if p[1].kind == nkPrefix:
+      if matches(c, p[0], n):
+        gather(c, p[1][1].sym, n)
         result = true
+      else:
+        result = false
     else:
-      assert isPatternParam(c, p.sons[1])
-      if matches(c, p.sons[0], n):
-        result = bindOrCheck(c, p.sons[1].sym, n)
+      assert isPatternParam(c, p[1])
+      if matches(c, p[0], n):
+        result = bindOrCheck(c, p[1].sym, n)
+      else:
+        result = false
   elif sameKinds(p, n):
     case p.kind
     of nkSym: result = p.sym == n.sym
@@ -172,80 +200,84 @@ proc matches(c: PPatternContext, p, n: PNode): bool =
     of nkEmpty, nkNilLit, nkType:
       result = true
     else:
-      var plen = sonsLen(p)
       # special rule for p(X) ~ f(...); this also works for stuff like
       # partial case statements, etc! - Not really ... :-/
+      result = false
       let v = lastSon(p)
       if isPatternParam(c, v) and v.sym.typ.kind == tyVarargs:
         var arglist: PNode
-        if plen <= sonsLen(n):
-          for i in countup(0, plen - 2):
-            if not matches(c, p.sons[i], n.sons[i]): return
-          if plen == sonsLen(n) and lastSon(n).kind == nkHiddenStdConv and
-              lastSon(n).sons[1].kind == nkBracket:
+        if p.len <= n.len:
+          for i in 0..<p.len - 1:
+            if not matches(c, p[i], n[i]): return
+          if p.len == n.len and lastSon(n).kind == nkHiddenStdConv and
+              lastSon(n)[1].kind == nkBracket:
             # unpack varargs:
-            let n = lastSon(n).sons[1]
+            let n = lastSon(n)[1]
             arglist = newNodeI(nkArgList, n.info, n.len)
-            for i in 0..<n.len: arglist.sons[i] = n.sons[i]
+            for i in 0..<n.len: arglist[i] = n[i]
           else:
-            arglist = newNodeI(nkArgList, n.info, sonsLen(n) - plen + 1)
+            arglist = newNodeI(nkArgList, n.info, n.len - p.len + 1)
             # f(1, 2, 3)
             # p(X)
-            for i in countup(0, sonsLen(n) - plen):
-              arglist.sons[i] = n.sons[i + plen - 1]
+            for i in 0..n.len - p.len:
+              arglist[i] = n[i + p.len - 1]
           return bindOrCheck(c, v.sym, arglist)
-        elif plen-1 == sonsLen(n):
-          for i in countup(0, plen - 2):
-            if not matches(c, p.sons[i], n.sons[i]): return
+        elif p.len-1 == n.len:
+          for i in 0..<p.len - 1:
+            if not matches(c, p[i], n[i]): return
           arglist = newNodeI(nkArgList, n.info)
           return bindOrCheck(c, v.sym, arglist)
-      if plen == sonsLen(n):
-        for i in countup(0, sonsLen(p) - 1):
-          if not matches(c, p.sons[i], n.sons[i]): return
+      if p.len == n.len:
+        for i in 0..<p.len:
+          if not matches(c, p[i], n[i]): return
         result = true
+  else:
+    result = false
 
 proc matchStmtList(c: PPatternContext, p, n: PNode): PNode =
   proc matchRange(c: PPatternContext, p, n: PNode, i: int): bool =
-    for j in 0 ..< p.len:
-      if not matches(c, p.sons[j], n.sons[i+j]):
+    for j in 0..<p.len:
+      if not matches(c, p[j], n[i+j]):
         # we need to undo any bindings:
-        if not isNil(c.mapping): c.mapping = nil
+        c.mapping = @[]
+        c.mappingIsFull = false
         return false
     result = true
 
   if p.kind == nkStmtList and n.kind == p.kind and p.len < n.len:
+    result = nil
     let n = flattenStmts(n)
     # no need to flatten 'p' here as that has already been done
-    for i in 0 .. n.len - p.len:
+    for i in 0..n.len - p.len:
       if matchRange(c, p, n, i):
         c.subMatch = true
         result = newNodeI(nkStmtList, n.info, 3)
-        result.sons[0] = extractRange(nkStmtList, n, 0, i-1)
-        result.sons[1] = extractRange(nkStmtList, n, i, i+p.len-1)
-        result.sons[2] = extractRange(nkStmtList, n, i+p.len, n.len-1)
+        result[0] = extractRange(nkStmtList, n, 0, i-1)
+        result[1] = extractRange(nkStmtList, n, i, i+p.len-1)
+        result[2] = extractRange(nkStmtList, n, i+p.len, n.len-1)
         break
   elif matches(c, p, n):
     result = n
+  else:
+    result = nil
 
 proc aliasAnalysisRequested(params: PNode): bool =
+  result = false
   if params.len >= 2:
-    for i in 1 ..< params.len:
-      let param = params.sons[i].sym
+    for i in 1..<params.len:
+      let param = params[i].sym
       if whichAlias(param) != aqNone: return true
 
 proc addToArgList(result, n: PNode) =
-  if n.typ != nil and n.typ.kind != tyStmt:
+  if n.typ != nil and n.typ.kind != tyTyped:
     if n.kind != nkArgList: result.add(n)
     else:
-      for i in 0 ..< n.len: result.add(n.sons[i])
+      for i in 0..<n.len: result.add(n[i])
 
 proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
   ## returns a tree to semcheck if the rule triggered; nil otherwise
-  var ctx: TPatternContext
-  ctx.owner = s
-  ctx.c = c
-  ctx.formals = sonsLen(s.typ)-1
-  var m = matchStmtList(ctx, s.ast.sons[patternPos], n)
+  var ctx = TPatternContext(owner: s, c: c, formals: s.typ.paramsLen)
+  var m = matchStmtList(ctx, s.ast[patternPos], n)
   if isNil(m): return nil
   # each parameter should have been bound; we simply setup a call and
   # let semantic checking deal with the rest :-)
@@ -253,11 +285,13 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
   result.add(newSymNode(s, n.info))
   let params = s.typ.n
   let requiresAA = aliasAnalysisRequested(params)
-  var args: PNode
-  if requiresAA:
-    args = newNodeI(nkArgList, n.info)
-  for i in 1 ..< params.len:
-    let param = params.sons[i].sym
+  var args: PNode =
+    if requiresAA:
+      newNodeI(nkArgList, n.info)
+    else:
+      nil
+  for i in 1..<params.len:
+    let param = params[i].sym
     let x = getLazy(ctx, param)
     # couldn't bind parameter:
     if isNil(x): return nil
@@ -265,9 +299,9 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
     if requiresAA: addToArgList(args, x)
   # perform alias analysis here:
   if requiresAA:
-    for i in 1 ..< params.len:
-      var rs = result.sons[i]
-      let param = params.sons[i].sym
+    for i in 1..<params.len:
+      var rs = result[i]
+      let param = params[i].sym
       case whichAlias(param)
       of aqNone: discard
       of aqShouldAlias:
@@ -289,8 +323,8 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
         # constraint not fulfilled:
         if not ok: return nil
 
-  markUsed(c.config, n.info, s, c.graph.usageSym)
+  markUsed(c, n.info, s)
   if ctx.subMatch:
     assert m.len == 3
-    m.sons[1] = result
+    m[1] = result
     result = m
diff --git a/compiler/pipelines.nim b/compiler/pipelines.nim
new file mode 100644
index 000000000..55e7fe892
--- /dev/null
+++ b/compiler/pipelines.nim
@@ -0,0 +1,312 @@
+import sem, cgen, modulegraphs, ast, llstream, parser, msgs,
+       lineinfos, reorder, options, semdata, cgendata, modules, pathutils,
+       packages, syntaxes, depends, vm, pragmas, idents, lookups, wordrecg,
+       liftdestructors
+
+import pipelineutils
+
+import ../dist/checksums/src/checksums/sha1
+
+when not defined(leanCompiler):
+  import jsgen, docgen2
+
+import std/[syncio, objectdollar, assertions, tables, strutils, strtabs]
+import renderer
+import ic/replayer
+
+proc setPipeLinePass*(graph: ModuleGraph; pass: PipelinePass) =
+  graph.pipelinePass = pass
+
+proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext): PNode =
+  case graph.pipelinePass
+  of CgenPass:
+    result = semNode
+    if bModule != nil:
+      genTopLevelStmt(BModule(bModule), result)
+  of JSgenPass:
+    when not defined(leanCompiler):
+      result = processJSCodeGen(bModule, semNode)
+    else:
+      result = nil
+  of GenDependPass:
+    result = addDotDependency(bModule, semNode)
+  of SemPass:
+    result = graph.emptyNode
+  of Docgen2Pass, Docgen2TexPass:
+    when not defined(leanCompiler):
+      result = processNode(bModule, semNode)
+    else:
+      result = nil
+  of Docgen2JsonPass:
+    when not defined(leanCompiler):
+      result = processNodeJson(bModule, semNode)
+    else:
+      result = nil
+  of EvalPass, InterpreterPass:
+    result = interpreterCode(bModule, semNode)
+  of NonePass:
+    raiseAssert "use setPipeLinePass to set a proper PipelinePass"
+
+proc processImplicitImports(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
+                      m: PSym, ctx: PContext, bModule: PPassContext, idgen: IdGenerator,
+                      ) =
+  # XXX fixme this should actually be relative to the config file!
+  let relativeTo = toFullPath(graph.config, m.info)
+  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, m.info)
+      var str = newStrNode(nkStrLit, module)
+      str.info = m.info
+      importStmt.add str
+      message(graph.config, importStmt.info, hintProcessingStmt, $idgen[])
+      let semNode = semWithPContext(ctx, importStmt)
+      if semNode == nil or processPipeline(graph, semNode, bModule) == nil:
+        break
+
+proc prePass(c: PContext; n: PNode) =
+  for son in n:
+    if son.kind == nkPragma:
+      for s in son:
+        var key = if s.kind in nkPragmaCallKinds and s.len > 1: s[0] else: s
+        if key.kind in {nkBracketExpr, nkCast} or key.kind notin nkIdentKinds:
+          continue
+        let ident = whichKeyword(considerQuotedIdent(c, key))
+        case ident
+        of wReorder:
+          pragmaNoForward(c, s, flag = sfReorder)
+        of wExperimental:
+          if isTopLevel(c) and s.kind in nkPragmaCallKinds and s.len == 2:
+            let name = c.semConstExpr(c, s[1])
+            case name.kind
+            of nkStrLit, nkRStrLit, nkTripleStrLit:
+              try:
+                let feature = parseEnum[Feature](name.strVal)
+                if feature == codeReordering:
+                  c.features.incl feature
+                  c.module.flags.incl sfReorder
+              except ValueError:
+                discard
+            else:
+              discard
+        else:
+          discard
+
+proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
+                    stream: PLLStream): bool =
+  if graph.stopCompile(): return true
+  var
+    p: Parser = default(Parser)
+    s: PLLStream
+    fileIdx = module.fileIdx
+
+  prepareConfigNotes(graph, module)
+  let ctx = preparePContext(graph, module, idgen)
+  let bModule: PPassContext =
+    case graph.pipelinePass
+    of CgenPass:
+      setupCgen(graph, module, idgen)
+    of JSgenPass:
+      when not defined(leanCompiler):
+        setupJSgen(graph, module, idgen)
+      else:
+        nil
+    of EvalPass, InterpreterPass:
+      setupEvalGen(graph, module, idgen)
+    of GenDependPass:
+      setupDependPass(graph, module, idgen)
+    of Docgen2Pass:
+      when not defined(leanCompiler):
+        openHtml(graph, module, idgen)
+      else:
+        nil
+    of Docgen2TexPass:
+      when not defined(leanCompiler):
+        openTex(graph, module, idgen)
+      else:
+        nil
+    of Docgen2JsonPass:
+      when not defined(leanCompiler):
+        openJson(graph, module, idgen)
+      else:
+        nil
+    of SemPass:
+      nil
+    of NonePass:
+      raiseAssert "use setPipeLinePass to set a proper PipelinePass"
+
+  if stream == nil:
+    let filename = toFullPathConsiderDirty(graph.config, fileIdx)
+    s = llStreamOpen(filename, fmRead)
+    if s == nil:
+      rawMessage(graph.config, errCannotOpenFile, filename.string)
+      return false
+    graph.interactive = false
+  else:
+    s = stream
+    graph.interactive = stream.kind == llsStdIn
+  while true:
+    syntaxes.openParser(p, fileIdx, s, graph.cache, graph.config)
+
+    if not belongsToStdlib(graph, module) or (belongsToStdlib(graph, module) and 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
+      # for the interactive mode.
+      if module.name.s != "nimscriptapi":
+        processImplicitImports graph, graph.config.implicitImports, nkImportStmt, module, ctx, bModule, idgen
+        processImplicitImports graph, graph.config.implicitIncludes, nkIncludeStmt, module, ctx, bModule, idgen
+
+    checkFirstLineIndentation(p)
+    block processCode:
+      if graph.stopCompile(): break processCode
+      var n = parseTopLevelStmt(p)
+      if n.kind == nkEmpty: break processCode
+      # read everything, no streaming possible
+      var sl = newNodeI(nkStmtList, n.info)
+      sl.add n
+      while true:
+        var n = parseTopLevelStmt(p)
+        if n.kind == nkEmpty: break
+        sl.add n
+
+      prePass(ctx, sl)
+      if sfReorder in module.flags or codeReordering in graph.config.features:
+        sl = reorder(graph, sl, module)
+      if graph.pipelinePass != EvalPass:
+        message(graph.config, sl.info, hintProcessingStmt, $idgen[])
+      var semNode = semWithPContext(ctx, sl)
+      discard processPipeline(graph, semNode, bModule)
+
+    closeParser(p)
+    if s.kind != llsStdIn: break
+  let finalNode = closePContext(graph, ctx, nil)
+  case graph.pipelinePass
+  of CgenPass:
+    if bModule != nil:
+      let m = BModule(bModule)
+      finalCodegenActions(graph, m, finalNode)
+      if graph.dispatchers.len > 0:
+        let ctx = preparePContext(graph, module, idgen)
+        for disp in getDispatchers(graph):
+          let retTyp = disp.typ.returnType
+          if retTyp != nil:
+            # TODO: properly semcheck the code of dispatcher?
+            createTypeBoundOps(graph, ctx, retTyp, disp.ast.info, idgen)
+          genProcAux(m, disp)
+        discard closePContext(graph, ctx, nil)
+  of JSgenPass:
+    when not defined(leanCompiler):
+      discard finalJSCodeGen(graph, bModule, finalNode)
+  of EvalPass, InterpreterPass:
+    discard interpreterCode(bModule, finalNode)
+  of SemPass, GenDependPass:
+    discard
+  of Docgen2Pass, Docgen2TexPass:
+    when not defined(leanCompiler):
+      discard closeDoc(graph, bModule, finalNode)
+  of Docgen2JsonPass:
+    when not defined(leanCompiler):
+      discard closeJson(graph, bModule, finalNode)
+  of NonePass:
+    raiseAssert "use setPipeLinePass to set a proper PipelinePass"
+
+  if graph.config.backend notin {backendC, backendCpp, backendObjc}:
+    # We only write rod files here if no C-like backend is active.
+    # The C-like backends have been patched to support the IC mechanism.
+    # They are responsible for closing the rod files. See `cbackend.nim`.
+    closeRodFile(graph, module)
+  result = true
+
+proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags; fromModule: PSym = nil): PSym =
+  var flags = flags
+  if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule
+  result = graph.getModule(fileIdx)
+
+  template processModuleAux(moduleStatus) =
+    onProcessing(graph, fileIdx, moduleStatus, fromModule = fromModule)
+    var s: PLLStream = nil
+    if sfMainModule in flags:
+      if graph.config.projectIsStdin: s = stdin.llStreamOpen
+      elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput)
+    discard processPipelineModule(graph, result, idGeneratorFromModule(result), s)
+  if result == nil:
+    var cachedModules: seq[FileIndex] = @[]
+    result = moduleFromRodFile(graph, fileIdx, cachedModules)
+    let path = toFullPath(graph.config, fileIdx)
+    let filename = AbsoluteFile path
+    if fileExists(filename): # it could be a stdinfile
+      graph.cachedFiles[path] = $secureHashFile(path)
+    if result == nil:
+      result = newModule(graph, fileIdx)
+      result.flags.incl flags
+      registerModule(graph, result)
+      processModuleAux("import")
+    else:
+      if sfSystemModule in flags:
+        graph.systemModule = result
+      if sfMainModule in flags and graph.config.cmd == cmdM:
+        result.flags.incl flags
+        registerModule(graph, result)
+        processModuleAux("import")
+      partialInitModule(result, graph, fileIdx, filename)
+    for m in cachedModules:
+      registerModuleById(graph, m)
+      if sfMainModule in flags and graph.config.cmd == cmdM:
+        discard
+      else:
+        replayStateChanges(graph.packed.pm[m.int].module, graph)
+        replayGenericCacheInformation(graph, m.int)
+  elif graph.isDirty(result):
+    result.flags.excl sfDirty
+    # reset module fields:
+    initStrTables(graph, result)
+    result.ast = nil
+    processModuleAux("import(dirty)")
+    graph.markClientsDirty(fileIdx)
+
+proc importPipelineModule(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym =
+  # this is called by the semantic checking phase
+  assert graph.config != nil
+  result = compilePipelineModule(graph, fileIdx, {}, s)
+  graph.addDep(s, fileIdx)
+  # keep track of import relationships
+  if graph.config.hcrOn:
+    graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx)
+  #if sfSystemModule in result.flags:
+  #  localError(result.info, errAttemptToRedefine, result.name.s)
+  # restore the notes for outer module:
+  graph.config.notes =
+    if graph.config.belongsToProjectPackage(s) or isDefined(graph.config, "booting"): graph.config.mainPackageNotes
+    else: graph.config.foreignPackageNotes
+
+proc connectPipelineCallbacks*(graph: ModuleGraph) =
+  graph.includeFileCallback = modules.includeModule
+  graph.importModuleCallback = importPipelineModule
+
+proc compilePipelineSystemModule*(graph: ModuleGraph) =
+  if graph.systemModule == nil:
+    connectPipelineCallbacks(graph)
+    graph.config.m.systemFileIdx = fileInfoIdx(graph.config,
+        graph.config.libpath / RelativeFile"system.nim")
+    discard graph.compilePipelineModule(graph.config.m.systemFileIdx, {sfSystemModule})
+
+proc compilePipelineProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) =
+  connectPipelineCallbacks(graph)
+  let conf = graph.config
+  wantMainModule(conf)
+  configComplete(graph)
+
+  let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim")
+  let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx
+  conf.projectMainIdx2 = projectFile
+
+  let packSym = getPackage(graph, projectFile)
+  graph.config.mainPackageId = packSym.getPackageId
+  graph.importStack.add projectFile
+
+  if projectFile == systemFileIdx:
+    discard graph.compilePipelineModule(projectFile, {sfMainModule, sfSystemModule})
+  else:
+    graph.compilePipelineSystemModule()
+    discard graph.compilePipelineModule(projectFile, {sfMainModule})
diff --git a/compiler/pipelineutils.nim b/compiler/pipelineutils.nim
new file mode 100644
index 000000000..75ba33f14
--- /dev/null
+++ b/compiler/pipelineutils.nim
@@ -0,0 +1,26 @@
+import ast, options, lineinfos, pathutils, msgs, modulegraphs, packages
+
+proc skipCodegen*(config: ConfigRef; n: PNode): bool {.inline.} =
+  # can be used by codegen passes to determine whether they should do
+  # something with `n`. Currently, this ignores `n` and uses the global
+  # error count instead.
+  result = config.errorCounter > 0
+
+proc resolveMod*(conf: ConfigRef; module, relativeTo: string): FileIndex =
+  let fullPath = findModule(conf, module, relativeTo)
+  if fullPath.isEmpty:
+    result = InvalidFileIdx
+  else:
+    result = fileInfoIdx(conf, fullPath)
+
+proc prepareConfigNotes*(graph: ModuleGraph; module: PSym) =
+  # don't be verbose unless the module belongs to the main package:
+  if graph.config.belongsToProjectPackage(module):
+    graph.config.notes = graph.config.mainPackageNotes
+  else:
+    if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes
+    graph.config.notes = graph.config.foreignPackageNotes
+
+proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} =
+  result = true
+  #module.id >= 0 or isDefined(graph.config, "nimBackendAssumesChange")
diff --git a/compiler/platform.nim b/compiler/platform.nim
index 0db16f26c..03d0cc461 100644
--- a/compiler/platform.nim
+++ b/compiler/platform.nim
@@ -14,15 +14,20 @@
 # Feel free to test for your excentric platform!
 
 import
-  strutils
+  std/strutils
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 
 type
   TSystemOS* = enum # Also add OS in initialization section and alias
                     # conditionals to condsyms (end of module).
     osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris,
-    osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osAix, osPalmos, osQnx,
-    osAmiga, osAtari, osNetware, osMacos, osMacosx, osHaiku, osAndroid, osVxworks
-    osGenode, osJS, osNimVM, osStandalone
+    osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osCrossos, osAix, osPalmos, osQnx,
+    osAmiga, osAtari, osNetware, osMacos, osMacosx, osIos, osHaiku, osAndroid, osVxWorks
+    osGenode, osJS, osNimVM, osStandalone, osNintendoSwitch, osFreeRTOS, osZephyr,
+    osNuttX, osAny
 
 type
   TInfoOSProp* = enum
@@ -105,6 +110,10 @@ const
       scriptExt: ".sh", curDir: ".",
       exeExt: "", extSep: ".",
       props: {ospNeedsPIC, ospPosix}),
+     (name: "CROSSOS", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
+      objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
+      scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
+      props: {ospNeedsPIC, ospPosix}),
      (name: "AIX", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
       objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
       scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
@@ -139,6 +148,10 @@ const
       objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
       scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
       props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}),
+     (name: "iOS", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
+      objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
+      scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
+      props: {ospNeedsPIC, ospPosix}),
      (name: "Haiku", parDir: "..", dllFrmt: "lib$1.so", altDirSep: ":",
       objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
       scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
@@ -168,24 +181,44 @@ const
      (name: "Standalone", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
       objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
       scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
-      props: {})]
+      props: {}),
+     (name: "NintendoSwitch", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
+      objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
+      scriptExt: ".sh", curDir: ".", exeExt: ".elf", extSep: ".",
+      props: {ospNeedsPIC, ospPosix}),
+     (name: "FreeRTOS", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
+      objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
+      scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
+      props: {ospPosix}),
+     (name: "Zephyr", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
+      objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
+      scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
+      props: {ospPosix}),
+     (name: "NuttX", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
+      objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
+      scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
+      props: {ospPosix}),
+     (name: "Any", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
+      objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
+      scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
+      props: {}),
+     ]
 
 type
   TSystemCPU* = enum # Also add CPU for in initialization section and
                      # alias conditionals to condsyms (end of module).
     cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64,
-    cpuPowerpc64el, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel,
-    cpuArm, cpuArm64, cpuJS, cpuNimVM, cpuAVR, cpuMSP430, cpuSparc64,
-    cpuMips64, cpuMips64el, cpuRiscV64
+    cpuPowerpc64el, cpuSparc, cpuVm, cpuHppa, cpuIa64, cpuAmd64, cpuMips,
+    cpuMipsel, cpuArm, cpuArm64, cpuJS, cpuNimVM, cpuAVR, cpuMSP430,
+    cpuSparc64, cpuMips64, cpuMips64el, cpuRiscV32, cpuRiscV64, cpuEsp, cpuWasm32,
+    cpuE2k, cpuLoongArch64
 
 type
-  TEndian* = enum
-    littleEndian, bigEndian
-  TInfoCPU* = tuple[name: string, intSize: int, endian: TEndian,
+  TInfoCPU* = tuple[name: string, intSize: int, endian: Endianness,
                     floatSize, bit: int]
 
 const
-  EndianToStr*: array[TEndian, string] = ["littleEndian", "bigEndian"]
+  EndianToStr*: array[Endianness, string] = ["littleEndian", "bigEndian"]
   CPU*: array[succ(low(TSystemCPU))..high(TSystemCPU), TInfoCPU] = [
     (name: "i386", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
     (name: "m68k", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
@@ -195,20 +228,27 @@ const
     (name: "powerpc64el", intSize: 64, endian: littleEndian, floatSize: 64,bit: 64),
     (name: "sparc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
     (name: "vm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
+    (name: "hppa", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
     (name: "ia64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
-    (name: "amd64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
+    (name: "amd64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), # a.k.a. x86_64, covers both amd and intel
     (name: "mips", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
     (name: "mipsel", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
     (name: "arm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
     (name: "arm64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
-    (name: "js", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32),
+    (name: "js", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
     (name: "nimvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
+      # xxx this seems buggy; on a 64bit machine, sizeof(int) is 64 in nimvm.
     (name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
     (name: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
     (name: "sparc64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
     (name: "mips64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
     (name: "mips64el", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
-    (name: "riscv64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64)]
+    (name: "riscv32", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
+    (name: "riscv64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
+    (name: "esp", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
+    (name: "wasm32", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
+    (name: "e2k", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
+    (name: "loongarch64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64)]
 
 type
   Target* = object
@@ -225,25 +265,34 @@ proc setTarget*(t: var Target; o: TSystemOS, c: TSystemCPU) =
   #echo "new Target: OS: ", o, " CPU: ", c
   t.targetCPU = c
   t.targetOS = o
-  # assume no cross-compiling
-  t.hostCPU = c
-  t.hostOS = o
   t.intSize = CPU[c].intSize div 8
   t.floatSize = CPU[c].floatSize div 8
   t.ptrSize = CPU[c].bit div 8
   t.tnl = OS[o].newLine
 
 proc nameToOS*(name: string): TSystemOS =
-  for i in countup(succ(osNone), high(TSystemOS)):
+  for i in succ(osNone)..high(TSystemOS):
     if cmpIgnoreStyle(name, OS[i].name) == 0:
       return i
   result = osNone
 
+proc listOSnames*(): seq[string] =
+  result = @[]
+  for i in succ(osNone)..high(TSystemOS):
+    result.add OS[i].name
+
 proc nameToCPU*(name: string): TSystemCPU =
-  for i in countup(succ(cpuNone), high(TSystemCPU)):
+  for i in succ(cpuNone)..high(TSystemCPU):
     if cmpIgnoreStyle(name, CPU[i].name) == 0:
       return i
   result = cpuNone
 
+proc listCPUnames*(): seq[string] =
+  result = @[]
+  for i in succ(cpuNone)..high(TSystemCPU):
+    result.add CPU[i].name
+
 proc setTargetFromSystem*(t: var Target) =
-  t.setTarget(nameToOS(system.hostOS), nameToCPU(system.hostCPU))
+  t.hostOS = nameToOS(system.hostOS)
+  t.hostCPU = nameToCPU(system.hostCPU)
+  t.setTarget(t.hostOS, t.hostCPU)
diff --git a/compiler/plugins/active.nim b/compiler/plugins/active.nim
index 7b5306f9c..19c320aae 100644
--- a/compiler/plugins/active.nim
+++ b/compiler/plugins/active.nim
@@ -10,7 +10,7 @@
 ## Include file that imports all plugins that are active.
 
 import
-  "../compiler" / [pluginsupport, idents, ast], locals, itersgen
+  ".." / [pluginsupport, idents, ast], locals, itersgen
 
 const
   plugins: array[2, Plugin] = [
diff --git a/compiler/plugins/customast.nim b/compiler/plugins/customast.nim
new file mode 100644
index 000000000..87461ae39
--- /dev/null
+++ b/compiler/plugins/customast.nim
@@ -0,0 +1,136 @@
+# This file exists to make it overridable via
+# patchFile("plugins", "customast.nim", "customast.nim")
+
+## This also serves as a blueprint for a possible implementation.
+
+import "$nim" / compiler / [lineinfos, idents]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+import "$nim" / compiler / nodekinds
+export nodekinds
+
+type
+  PNode* = ref TNode
+  TNode*{.final, acyclic.} = object
+    case kind*: TNodeKind
+    of nkCharLit..nkUInt64Lit:
+      intVal: BiggestInt
+    of nkFloatLit..nkFloat128Lit:
+      floatVal: BiggestFloat
+    of nkStrLit..nkTripleStrLit:
+      strVal: string
+    of nkSym:
+      discard
+    of nkIdent:
+      ident: PIdent
+    else:
+      son, next, last: PNode # linked structure instead of a `seq`
+    info*: TLineInfo
+
+const
+  bodyPos* = 6
+  paramsPos* = 3
+
+proc comment*(n: PNode): string =
+  result = ""
+
+proc `comment=`*(n: PNode, a: string) =
+  discard "XXX implement me"
+
+proc add*(father, son: PNode) =
+  assert son != nil
+  if father.son == nil:
+    father.son = son
+    father.last = son
+  else:
+    father.last.next = son
+    father.last = son
+
+template firstSon*(n: PNode): PNode = n.son
+template secondSon*(n: PNode): PNode = n.son.next
+
+proc replaceFirstSon*(n, newson: PNode) {.inline.} =
+  let old = n.son
+  n.son = newson
+  newson.next = old
+
+proc replaceSon*(n: PNode; i: int; newson: PNode) =
+  assert i > 0
+  assert newson.next == nil
+  var i = i
+  var it = n.son
+  while i > 0:
+    it = it.next
+    dec i
+  let old = it.next
+  it.next = newson
+  newson.next = old
+
+template newNodeImpl(info2) =
+  result = PNode(kind: kind, info: info2)
+
+proc newNode*(kind: TNodeKind): PNode =
+  ## new node with unknown line info, no type, and no children
+  newNodeImpl(unknownLineInfo)
+
+proc newNode*(kind: TNodeKind, info: TLineInfo): PNode =
+  ## new node with line info, no type, and no children
+  newNodeImpl(info)
+
+proc newTree*(kind: TNodeKind; info: TLineInfo; child: PNode): PNode =
+  result = newNode(kind, info)
+  result.son = child
+
+proc newAtom*(ident: PIdent, info: TLineInfo): PNode =
+  result = newNode(nkIdent)
+  result.ident = ident
+  result.info = info
+
+proc newAtom*(kind: TNodeKind, intVal: BiggestInt, info: TLineInfo): PNode =
+  result = newNode(kind, info)
+  result.intVal = intVal
+
+proc newAtom*(kind: TNodeKind, floatVal: BiggestFloat, info: TLineInfo): PNode =
+  result = newNode(kind, info)
+  result.floatVal = floatVal
+
+proc newAtom*(kind: TNodeKind; strVal: sink string; info: TLineInfo): PNode =
+  result = newNode(kind, info)
+  result.strVal = strVal
+
+proc lastSon*(n: PNode): PNode {.inline.} = n.last
+proc setLastSon*(n: PNode, s: PNode) =
+  assert s.next == nil
+  n.last = s
+  if n.son == nil: n.son = s
+
+proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode,
+                 params,
+                 name, pattern, genericParams,
+                 pragmas, exceptions: PNode): PNode =
+  result = newNode(kind, info)
+  result.add name
+  result.add pattern
+  result.add genericParams
+  result.add params
+  result.add pragmas
+  result.add exceptions
+  result.add body
+
+template transitionNodeKindCommon(k: TNodeKind) =
+  let obj {.inject.} = n[]
+  n[] = TNode(kind: k, info: obj.info)
+  # n.comment = obj.comment # shouldn't be needed, the address doesnt' change
+
+proc transitionSonsKind*(n: PNode, kind: range[nkComesFrom..nkTupleConstr]) =
+  transitionNodeKindCommon(kind)
+  n.son = obj.son
+
+template hasSon*(n: PNode): bool = n.son != nil
+template has2Sons*(n: PNode): bool = n.son != nil and n.son.next != nil
+
+proc isNewStyleConcept*(n: PNode): bool {.inline.} =
+  assert n.kind == nkTypeClassTy
+  result = n.firstSon.kind == nkEmpty
diff --git a/compiler/plugins/itersgen.nim b/compiler/plugins/itersgen.nim
index 440d2e081..e2c97bdc5 100644
--- a/compiler/plugins/itersgen.nim
+++ b/compiler/plugins/itersgen.nim
@@ -9,9 +9,7 @@
 
 ## Plugin to transform an inline iterator into a data structure.
 
-import ".." / [ast, astalgo,
-  magicsys, lookups, semdata,
-  lambdalifting, msgs]
+import ".." / [ast, modulegraphs, lookups, semdata, lambdalifting, msgs]
 
 proc iterToProcImpl*(c: PContext, n: PNode): PNode =
   result = newNodeI(nkStmtList, n.info)
@@ -27,14 +25,14 @@ proc iterToProcImpl*(c: PContext, n: PNode): PNode =
     return
 
   let t = n[2].typ.skipTypes({tyTypeDesc, tyGenericInst})
-  if t.kind notin {tyRef, tyPtr} or t.lastSon.kind != tyObject:
+  if t.kind notin {tyRef, tyPtr} or t.elementType.kind != tyObject:
     localError(c.config, n[2].info,
         "type must be a non-generic ref|ptr to object with state field")
     return
-  let body = liftIterToProc(c.graph, iter.sym, iter.sym.getBody, t)
+  let body = liftIterToProc(c.graph, iter.sym, getBody(c.graph, iter.sym), t, c.idgen)
 
-  let prc = newSym(skProc, n[3].ident, iter.sym.owner, iter.sym.info)
-  prc.typ = copyType(iter.sym.typ, prc, false)
+  let prc = newSym(skProc, n[3].ident, c.idgen, iter.sym.owner, iter.sym.info)
+  prc.typ = copyType(iter.sym.typ, c.idgen, prc)
   excl prc.typ.flags, tfCapturesEnv
   prc.typ.n.add newSymNode(getEnvParam(iter.sym))
   prc.typ.rawAddSon t
@@ -44,5 +42,5 @@ proc iterToProcImpl*(c: PContext, n: PNode): PNode =
               pattern = c.graph.emptyNode, genericParams = c.graph.emptyNode,
               pragmas = orig[pragmasPos], exceptions = c.graph.emptyNode)
 
-  prc.ast.add iter.sym.ast.sons[resultPos]
+  prc.ast.add iter.sym.ast[resultPos]
   addInterfaceDecl(c, prc)
diff --git a/compiler/plugins/locals.nim b/compiler/plugins/locals.nim
index 0048ff985..d3046cd65 100644
--- a/compiler/plugins/locals.nim
+++ b/compiler/plugins/locals.nim
@@ -9,33 +9,31 @@
 
 ## The builtin 'system.locals' implemented as a plugin.
 
-import ".." / [pluginsupport, ast, astalgo,
+import ".." / [ast, astalgo,
   magicsys, lookups, semdata, lowerings]
 
 proc semLocals*(c: PContext, n: PNode): PNode =
   var counter = 0
   var tupleType = newTypeS(tyTuple, c)
-  result = newNodeIT(nkPar, n.info, tupleType)
+  result = newNodeIT(nkTupleConstr, n.info, tupleType)
   tupleType.n = newNodeI(nkRecList, n.info)
+  let owner = getCurrOwner(c)
   # for now we skip openarrays ...
-  for scope in walkScopes(c.currentScope):
-    if scope == c.topLevelScope: break
+  for scope in localScopesFrom(c, c.currentScope):
     for it in items(scope.symbols):
-      # XXX parameters' owners are wrong for generics; this caused some pain
-      # for closures too; we should finally fix it.
-      #if it.owner != c.p.owner: return result
       if it.kind in skLocalVars and
           it.typ.skipTypes({tyGenericInst, tyVar}).kind notin
-            {tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyExpr, tyStmt, tyEmpty}:
+            {tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyUntyped, tyTyped, tyEmpty}:
 
-        var field = newSym(skField, it.name, getCurrOwner(c), n.info)
-        field.typ = it.typ.skipTypes({tyGenericInst, tyVar})
-        field.position = counter
-        inc(counter)
+        if it.owner == owner:
+          var field = newSym(skField, it.name, c.idgen, owner, n.info)
+          field.typ = it.typ.skipTypes({tyVar})
+          field.position = counter
+          inc(counter)
 
-        addSon(tupleType.n, newSymNode(field))
-        addSonSkipIntLit(tupleType, field.typ)
+          tupleType.n.add newSymNode(field)
+          addSonSkipIntLit(tupleType, field.typ, c.idgen)
 
-        var a = newSymNode(it, result.info)
-        if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a)
-        result.add(a)
+          var a = newSymNode(it, result.info)
+          if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a)
+          result.add(a)
diff --git a/tests/modules/tnotuniquename/mnotuniquename.nim b/compiler/plugins/plugins.nimble
index e69de29bb..e69de29bb 100644
--- a/tests/modules/tnotuniquename/mnotuniquename.nim
+++ b/compiler/plugins/plugins.nimble
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index c78a3519c..9a298cd90 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -10,70 +10,95 @@
 # This module implements semantic checking for pragmas
 
 import
-  os, platform, condsyms, ast, astalgo, idents, semdata, msgs, renderer,
-  wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees,
-  types, lookups, lineinfos
+  condsyms, ast, astalgo, idents, semdata, msgs, renderer,
+  wordrecg, ropes, options, extccomp, magicsys, trees,
+  types, lookups, lineinfos, pathutils, linter, modulepaths
+
+from sigmatch import trySuggestPragmas
+
+import std/[os, math, strutils]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+from ic / ic import addCompilerProc
 
 const
   FirstCallConv* = wNimcall
   LastCallConv* = wNoconv
 
 const
-  procPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
-    wMagic, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
-    wCompilerProc, wCore, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
-    wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
-    wAsmNoStackFrame, wError, wDiscardable, wNoInit, wCodegenDecl,
-    wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe, wOverride,
-    wConstructor, wExportNims, wUsed, wLiftLocals, wStacktrace, wLinetrace}
+  declPragmas = {wImportc, wImportObjC, wImportCpp, wImportJs, wExportc, wExportCpp,
+    wExportNims, wExtern, wDeprecated, wNodecl, wError, wUsed}
+    ## common pragmas for declarations, to a good approximation
+  procPragmas* = declPragmas + {FirstCallConv..LastCallConv,
+    wMagic, wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader,
+    wCompilerProc, wNonReloadable, wCore, wProcVar, wVarargs, wCompileTime,
+    wBorrow, wImportCompilerProc, wThread,
+    wAsmNoStackFrame, wDiscardable, wNoInit, wCodegenDecl,
+    wGensym, wInject, wRaises, wEffectsOf, wTags, wForbids, wLocks, wDelegator, wGcSafe,
+    wConstructor, wLiftLocals, wStackTrace, wLineTrace, wNoDestroy,
+    wRequires, wEnsures, wEnforceNoRaises, wSystemRaisesDefect, wVirtual, wQuirky, wMember}
   converterPragmas* = procPragmas
   methodPragmas* = procPragmas+{wBase}-{wImportCpp}
-  templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty,
-    wDelegator, wExportNims, wUsed, wPragma}
-  macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc,
-    wNodecl, wMagic, wNosideeffect, wCompilerProc, wCore, wDeprecated, wExtern,
-    wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator,
-    wExportNims, wUsed}
-  iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect,
-    wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
-    wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises,
-    wTags, wLocks, wGcSafe, wExportNims, wUsed}
-  exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe}
-  stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
-    wBoundchecks, wOverflowchecks, wNilchecks, wMovechecks, wAssertions,
-    wWarnings, wHints,
-    wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError,
+  templatePragmas* = {wDeprecated, wError, wGensym, wInject, wDirty,
+    wDelegator, wExportNims, wUsed, wPragma, wRedefine, wCallsite}
+  macroPragmas* = declPragmas + {FirstCallConv..LastCallConv,
+    wMagic, wNoSideEffect, wCompilerProc, wNonReloadable, wCore,
+    wDiscardable, wGensym, wInject, wDelegator}
+  iteratorPragmas* = declPragmas + {FirstCallConv..LastCallConv, wNoSideEffect, wSideEffect,
+    wMagic, wBorrow,
+    wDiscardable, wGensym, wInject, wRaises, wEffectsOf,
+    wTags, wForbids, wLocks, wGcSafe, wRequires, wEnsures}
+  exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe, wNoSideEffect}
+  stmtPragmas* = {
+    wHint, wWarning, wError,
     wFatal, wDefine, wUndef, wCompile, wLink, wLinksys, wPure, wPush, wPop,
-    wBreakpoint, wWatchPoint, wPassl, wPassc,
+    wPassl, wPassc, wLocalPassc,
     wDeadCodeElimUnused,  # deprecated, always on
     wDeprecated,
-    wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
-    wLinearScanEnd, wPatterns, wEffects, wNoForward, wReorder, wComputedGoto,
-    wInjectStmt, wDeprecated, wExperimental, wThis}
-  lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
-    wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
-    wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame,
-    wRaises, wLocks, wTags, wGcSafe}
-  typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
-    wPure, wHeader, wCompilerProc, wCore, wFinal, wSize, wExtern, wShallow,
-    wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
+    wPragma, wEmit, wUnroll,
+    wLinearScanEnd, wPatterns, wTrMacros, wEffects, wNoForward, wReorder, wComputedGoto,
+    wExperimental, wDoctype, wThis, wUsed, wInvariant, wAssume, wAssert}
+  stmtPragmasTopLevel* = {wChecks, wObjChecks, wFieldChecks, wRangeChecks,
+    wBoundChecks, wOverflowChecks, wNilChecks, wStaticBoundchecks,
+    wStyleChecks, wAssertions,
+    wWarnings, wHints,
+    wLineDir, wStackTrace, wLineTrace, wOptimization,
+    wFloatChecks, wInfChecks, wNanChecks}
+  lambdaPragmas* = {FirstCallConv..LastCallConv,
+    wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader,
+    wThread, wAsmNoStackFrame,
+    wRaises, wLocks, wTags, wForbids, wRequires, wEnsures, wEffectsOf,
+    wGcSafe, wCodegenDecl, wNoInit, wCompileTime}
+  typePragmas* = declPragmas + {wMagic, wAcyclic,
+    wPure, wHeader, wCompilerProc, wCore, wFinal, wSize, wShallow,
+    wIncompleteStruct, wCompleteStruct, wByCopy, wByRef,
     wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
-    wBorrow, wGcSafe, wExportNims, wPartial, wUsed, wExplain, wPackage}
-  fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
-    wImportCpp, wImportObjC, wError, wGuard, wBitsize, wUsed}
-  varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
-    wMagic, wHeader, wDeprecated, wCompilerProc, wCore, wDynlib, wExtern,
-    wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
-    wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims, wUsed}
-  constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
-    wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims,
-    wIntDefine, wStrDefine, wUsed, wCompilerProc, wCore}
+    wCppNonPod, wBorrow, wGcSafe, wPartial, wExplain, wPackage, wCodegenDecl,
+    wSendable, wNoInit}
+  fieldPragmas* = declPragmas + {wGuard, wBitsize, wCursor,
+    wRequiresInit, wNoalias, wAlign, wNoInit} - {wExportNims, wNodecl} # why exclude these?
+  varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar,
+    wMagic, wHeader, wCompilerProc, wCore, wDynlib,
+    wNoInit, wCompileTime, wGlobal, wLiftLocals,
+    wGensym, wInject, wCodegenDecl,
+    wGuard, wGoto, wCursor, wNoalias, wAlign}
+  constPragmas* = declPragmas + {wHeader, wMagic,
+    wGensym, wInject,
+    wIntDefine, wStrDefine, wBoolDefine, wDefine,
+    wCompilerProc, wCore}
+  paramPragmas* = {wNoalias, wInject, wGensym, wByRef, wByCopy, wCodegenDecl, wExportc, wExportCpp}
   letPragmas* = varPragmas
-  procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect,
-                      wThread, wRaises, wLocks, wTags, wGcSafe}
+  procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect,
+                      wThread, wRaises, wEffectsOf, wLocks, wTags, wForbids, wGcSafe,
+                      wRequires, wEnsures}
+  forVarPragmas* = {wInject, wGensym}
   allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas
+  enumFieldPragmas* = {wDeprecated}
 
 proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
+  result = nil
   let p = procAst[pragmasPos]
   if p.kind == nkEmpty: return nil
   for it in p:
@@ -81,50 +106,67 @@ proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
         it[0].ident.id == ord(name):
       return it[1]
 
-proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords)
+proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
+            isStatement: bool = false)
 
-proc recordPragma(c: PContext; n: PNode; key, val: string; val2 = "") =
-  var recorded = newNodeI(nkCommentStmt, n.info)
-  recorded.add newStrNode(key, n.info)
-  recorded.add newStrNode(val, n.info)
-  if val2.len > 0: recorded.add newStrNode(val2, n.info)
-  c.graph.recordStmt(c.graph, c.module, recorded)
+proc recordPragma(c: PContext; n: PNode; args: varargs[string]) =
+  var recorded = newNodeI(nkReplayAction, n.info)
+  for i in 0..args.high:
+    recorded.add newStrNode(args[i], n.info)
+  addPragmaComputation(c, recorded)
 
 const
   errStringLiteralExpected = "string literal expected"
   errIntLiteralExpected = "integer literal expected"
 
 proc invalidPragma*(c: PContext; n: PNode) =
-  localError(c.config, n.info, "invalid pragma: " % renderTree(n, {renderNoComments}))
+  localError(c.config, n.info, "invalid pragma: " & renderTree(n, {renderNoComments}))
+
+proc illegalCustomPragma*(c: PContext, n: PNode, s: PSym) =
+  var msg = "cannot attach a custom pragma to '" & s.name.s & "'"
+  if s != nil:
+    msg.add("; custom pragmas are not supported for ")
+    case s.kind
+    of skForVar: msg.add("`for` loop variables")
+    of skEnumField: msg.add("enum fields")
+    of skModule: msg.add("modules")
+    else: msg.add("symbol kind " & $s.kind)
+  localError(c.config, n.info, msg)
+
+proc pragmaProposition(c: PContext, n: PNode) =
+  if n.kind notin nkPragmaCallKinds or n.len != 2:
+    localError(c.config, n.info, "proposition expected")
+  else:
+    n[1] = c.semExpr(c, n[1])
 
-proc pragmaAsm*(c: PContext, n: PNode): char =
-  result = '\0'
-  if n != nil:
-    for i in countup(0, sonsLen(n) - 1):
-      let it = n.sons[i]
-      if it.kind in nkPragmaCallKinds and it.len == 2 and it.sons[0].kind == nkIdent:
-        case whichKeyword(it.sons[0].ident)
-        of wSubsChar:
-          if it.sons[1].kind == nkCharLit: result = chr(int(it.sons[1].intVal))
-          else: invalidPragma(c, it)
-        else: invalidPragma(c, it)
-      else:
-        invalidPragma(c, it)
+proc pragmaEnsures(c: PContext, n: PNode) =
+  if n.kind notin nkPragmaCallKinds or n.len != 2:
+    localError(c.config, n.info, "proposition expected")
+  else:
+    openScope(c)
+    let o = getCurrOwner(c)
+    if o.kind in routineKinds and o.typ != nil and o.typ.returnType != nil:
+      var s = newSym(skResult, getIdent(c.cache, "result"), c.idgen, o, n.info)
+      s.typ = o.typ.returnType
+      incl(s.flags, sfUsed)
+      addDecl(c, s)
+    n[1] = c.semExpr(c, n[1])
+    closeScope(c)
 
 proc setExternName(c: PContext; s: PSym, extname: string, info: TLineInfo) =
   # special cases to improve performance:
   if extname == "$1":
-    s.loc.r = rope(s.name.s)
+    s.loc.snippet = rope(s.name.s)
   elif '$' notin extname:
-    s.loc.r = rope(extname)
+    s.loc.snippet = rope(extname)
   else:
     try:
-      s.loc.r = rope(extname % s.name.s)
+      s.loc.snippet = rope(extname % s.name.s)
     except ValueError:
       localError(c.config, info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)")
-  if c.config.cmd == cmdPretty and '$' notin extname:
-    # note that '{.importc.}' is transformed into '{.importc: "$1".}'
-    s.loc.flags.incl(lfFullExternalName)
+  when hasFFI:
+    s.cname = $s.loc.snippet
+
 
 proc makeExternImport(c: PContext; s: PSym, extname: string, info: TLineInfo) =
   setExternName(c, s, extname, info)
@@ -146,7 +188,7 @@ proc processImportCpp(c: PContext; s: PSym, extname: string, info: TLineInfo) =
   incl(s.flags, sfImportc)
   incl(s.flags, sfInfixCall)
   excl(s.flags, sfForward)
-  if c.config.cmd == cmdCompileToC:
+  if c.config.backend == backendC:
     let m = s.getModule()
     incl(m.flags, sfCompileToCpp)
   incl c.config.globalOptions, optMixedMode
@@ -157,11 +199,11 @@ proc processImportObjC(c: PContext; s: PSym, extname: string, info: TLineInfo) =
   incl(s.flags, sfNamedParamCall)
   excl(s.flags, sfForward)
   let m = s.getModule()
-  incl(m.flags, sfCompileToObjC)
+  incl(m.flags, sfCompileToObjc)
 
-proc newEmptyStrNode(c: PContext; n: PNode): PNode {.noinline.} =
+proc newEmptyStrNode(c: PContext; n: PNode, strVal: string = ""): PNode {.noinline.} =
   result = newNodeIT(nkStrLit, n.info, getSysType(c.graph, n.info, tyString))
-  result.strVal = ""
+  result.strVal = strVal
 
 proc getStrLitNode(c: PContext, n: PNode): PNode =
   if n.kind notin nkPragmaCallKinds or n.len != 2:
@@ -169,9 +211,9 @@ proc getStrLitNode(c: PContext, n: PNode): PNode =
     # error correction:
     result = newEmptyStrNode(c, n)
   else:
-    n.sons[1] = c.semConstExpr(c, n.sons[1])
-    case n.sons[1].kind
-    of nkStrLit, nkRStrLit, nkTripleStrLit: result = n.sons[1]
+    n[1] = c.semConstExpr(c, n[1])
+    case n[1].kind
+    of nkStrLit, nkRStrLit, nkTripleStrLit: result = n[1]
     else:
       localError(c.config, n.info, errStringLiteralExpected)
       # error correction:
@@ -181,20 +223,30 @@ proc expectStrLit(c: PContext, n: PNode): string =
   result = getStrLitNode(c, n).strVal
 
 proc expectIntLit(c: PContext, n: PNode): int =
+  result = 0
   if n.kind notin nkPragmaCallKinds or n.len != 2:
     localError(c.config, n.info, errIntLiteralExpected)
   else:
-    n.sons[1] = c.semConstExpr(c, n.sons[1])
-    case n.sons[1].kind
-    of nkIntLit..nkInt64Lit: result = int(n.sons[1].intVal)
+    n[1] = c.semConstExpr(c, n[1])
+    case n[1].kind
+    of nkIntLit..nkInt64Lit: result = int(n[1].intVal)
     else: localError(c.config, n.info, errIntLiteralExpected)
 
 proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string =
   if n.kind in nkPragmaCallKinds: result = expectStrLit(c, n)
   else: result = defaultStr
 
+proc processVirtual(c: PContext, n: PNode, s: PSym, flag: TSymFlag) =
+  s.constraint = newEmptyStrNode(c, n, getOptionalStr(c, n, "$1"))
+  s.constraint.strVal = s.constraint.strVal % s.name.s
+  s.flags.incl {flag, sfInfixCall, sfExportc, sfMangleCpp}
+
+  s.typ.callConv = ccMember
+  incl c.config.globalOptions, optMixedMode
+
 proc processCodegenDecl(c: PContext, n: PNode, sym: PSym) =
   sym.constraint = getStrLitNode(c, n)
+  sym.flags.incl sfCodegenDecl
 
 proc processMagic(c: PContext, n: PNode, s: PSym) =
   #if sfSystemModule notin c.module.flags:
@@ -203,9 +255,9 @@ proc processMagic(c: PContext, n: PNode, s: PSym) =
     localError(c.config, n.info, errStringLiteralExpected)
     return
   var v: string
-  if n.sons[1].kind == nkIdent: v = n.sons[1].ident.s
+  if n[1].kind == nkIdent: v = n[1].ident.s
   else: v = expectStrLit(c, n)
-  for m in countup(low(TMagic), high(TMagic)):
+  for m in TMagic:
     if substr($m, 1) == v:
       s.magic = m
       break
@@ -214,26 +266,54 @@ proc processMagic(c: PContext, n: PNode, s: PSym) =
 proc wordToCallConv(sw: TSpecialWord): TCallingConvention =
   # this assumes that the order of special words and calling conventions is
   # the same
-  result = TCallingConvention(ord(ccDefault) + ord(sw) - ord(wNimcall))
+  TCallingConvention(ord(ccNimCall) + ord(sw) - ord(wNimcall))
 
 proc isTurnedOn(c: PContext, n: PNode): bool =
+  result = false
   if n.kind in nkPragmaCallKinds and n.len == 2:
-    let x = c.semConstBoolExpr(c, n.sons[1])
-    n.sons[1] = x
+    let x = c.semConstBoolExpr(c, n[1])
+    n[1] = x
     if x.kind == nkIntLit: return x.intVal != 0
   localError(c.config, n.info, "'on' or 'off' expected")
 
 proc onOff(c: PContext, n: PNode, op: TOptions, resOptions: var TOptions) =
-  if isTurnedOn(c, n): resOptions = resOptions + op
-  else: resOptions = resOptions - op
+  if isTurnedOn(c, n): resOptions.incl op
+  else: resOptions.excl op
 
-proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) =
-  if isTurnedOn(c, n): incl(c.module.flags, flag)
-  else: excl(c.module.flags, flag)
+proc pragmaNoForward*(c: PContext, n: PNode; flag=sfNoForward) =
+  if isTurnedOn(c, n):
+    incl(c.module.flags, flag)
+    c.features.incl codeReordering
+  else:
+    excl(c.module.flags, flag)
+    # c.features.excl codeReordering
+
+  # deprecated as of 0.18.1
+  message(c.config, n.info, warnDeprecated,
+          "use {.experimental: \"codeReordering\".} instead; " &
+          (if flag == sfNoForward: "{.noForward.}" else: "{.reorder.}") & " is deprecated")
+
+proc pragmaAsm*(c: PContext, n: PNode): char =
+  ## Checks asm pragmas and get's the asm subschar (default: '`').
+  result = '\0'
+  if n != nil:
+    for i in 0..<n.len:
+      let it = n[i]
+      if it.kind in nkPragmaCallKinds and it.len == 2 and it[0].kind == nkIdent:
+        case whichKeyword(it[0].ident)
+        of wSubsChar:
+          if it[1].kind == nkCharLit: result = chr(int(it[1].intVal))
+          else: invalidPragma(c, it)
+        of wAsmSyntax:
+          let s = expectStrLit(c, it)
+          if s notin ["gcc", "vcc"]: invalidPragma(c, it)
+        else: invalidPragma(c, it)
+      else:
+        invalidPragma(c, it)
 
 proc processCallConv(c: PContext, n: PNode) =
-  if n.kind in nkPragmaCallKinds and n.len == 2 and n.sons[1].kind == nkIdent:
-    let sw = whichKeyword(n.sons[1].ident)
+  if n.kind in nkPragmaCallKinds and n.len == 2 and n[1].kind == nkIdent:
+    let sw = whichKeyword(n[1].ident)
     case sw
     of FirstCallConv..LastCallConv:
       c.optionStack[^1].defaultCC = wordToCallConv(sw)
@@ -250,7 +330,7 @@ proc getLib(c: PContext, kind: TLibKind, path: PNode): PLib =
   result.path = path
   c.libs.add result
   if path.kind in {nkStrLit..nkTripleStrLit}:
-    result.isOverriden = options.isDynlibOverride(c.config, path.strVal)
+    result.isOverridden = options.isDynlibOverride(c.config, path.strVal)
 
 proc expectDynlibNode(c: PContext, n: PNode): PNode =
   if n.kind notin nkPragmaCallKinds or n.len != 2:
@@ -260,9 +340,9 @@ proc expectDynlibNode(c: PContext, n: PNode): PNode =
   else:
     # For the OpenGL wrapper we support:
     # {.dynlib: myGetProcAddr(...).}
-    result = c.semExpr(c, n.sons[1])
+    result = c.semExpr(c, n[1])
     if result.kind == nkSym and result.sym.kind == skConst:
-      result = result.sym.ast # look it up
+      result = c.semConstExpr(c, result) # fold const
     if result.typ == nil or result.typ.kind notin {tyPointer, tyString, tyProc}:
       localError(c.config, n.info, errStringLiteralExpected)
       result = newEmptyStrNode(c, n)
@@ -270,12 +350,12 @@ proc expectDynlibNode(c: PContext, n: PNode): PNode =
 proc processDynLib(c: PContext, n: PNode, sym: PSym) =
   if (sym == nil) or (sym.kind == skModule):
     let lib = getLib(c, libDynamic, expectDynlibNode(c, n))
-    if not lib.isOverriden:
+    if not lib.isOverridden:
       c.optionStack[^1].dynlib = lib
   else:
     if n.kind in nkPragmaCallKinds:
       var lib = getLib(c, libDynamic, expectDynlibNode(c, n))
-      if not lib.isOverriden:
+      if not lib.isOverridden:
         addToLib(lib, sym)
         incl(sym.loc.flags, lfDynamicLib)
     else:
@@ -284,69 +364,93 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) =
     # a calling convention that doesn't introduce custom name mangling
     # cdecl is the default - the user can override this explicitly
     if sym.kind in routineKinds and sym.typ != nil and
-        sym.typ.callConv == ccDefault:
+       tfExplicitCallConv notin sym.typ.flags:
       sym.typ.callConv = ccCDecl
 
 proc processNote(c: PContext, n: PNode) =
-  if n.kind in nkPragmaCallKinds and len(n) == 2 and
+  template handleNote(enumVals, notes) =
+    let x = findStr(enumVals.a, enumVals.b, n[0][1].ident.s, errUnknown)
+    if x != errUnknown:
+      nk = TNoteKind(x)
+      let x = c.semConstBoolExpr(c, n[1])
+      n[1] = x
+      if x.kind == nkIntLit and x.intVal != 0: incl(notes, nk)
+      else: excl(notes, nk)
+    else:
+      invalidPragma(c, n)
+
+  if n.kind in nkPragmaCallKinds and n.len == 2 and
       n[0].kind == nkBracketExpr and
       n[0].len == 2 and
       n[0][1].kind == nkIdent and n[0][0].kind == nkIdent:
     var nk: TNoteKind
     case whichKeyword(n[0][0].ident)
-    of wHint:
-      var x = findStr(HintsToStr, n[0][1].ident.s)
-      if x >= 0: nk = TNoteKind(x + ord(hintMin))
-      else: invalidPragma(c, n); return
-    of wWarning:
-      var x = findStr(WarningsToStr, n[0][1].ident.s)
-      if x >= 0: nk = TNoteKind(x + ord(warnMin))
-      else: invalidPragma(c, n); return
-    else:
-      invalidPragma(c, n)
-      return
-
-    let x = c.semConstBoolExpr(c, n[1])
-    n.sons[1] = x
-    if x.kind == nkIntLit and x.intVal != 0: incl(c.config.notes, nk)
-    else: excl(c.config.notes, nk)
-  else:
-    invalidPragma(c, n)
-
-proc pragmaToOptions(w: TSpecialWord): TOptions {.inline.} =
+    of wHint: handleNote(hintMin .. hintMax, c.config.notes)
+    of wWarning: handleNote(warnMin .. warnMax, c.config.notes)
+    of wWarningAsError: handleNote(warnMin .. warnMax, c.config.warningAsErrors)
+    of wHintAsError: handleNote(hintMin .. hintMax, c.config.warningAsErrors)
+    else: invalidPragma(c, n)
+  else: invalidPragma(c, n)
+
+proc pragmaToOptions*(w: TSpecialWord): TOptions {.inline.} =
   case w
   of wChecks: ChecksOptions
   of wObjChecks: {optObjCheck}
   of wFieldChecks: {optFieldCheck}
-  of wRangechecks: {optRangeCheck}
-  of wBoundchecks: {optBoundsCheck}
-  of wOverflowchecks: {optOverflowCheck}
-  of wNilchecks: {optNilCheck}
-  of wFloatchecks: {optNaNCheck, optInfCheck}
+  of wRangeChecks: {optRangeCheck}
+  of wBoundChecks: {optBoundsCheck}
+  of wOverflowChecks: {optOverflowCheck}
+  of wFloatChecks: {optNaNCheck, optInfCheck}
   of wNanChecks: {optNaNCheck}
   of wInfChecks: {optInfCheck}
-  of wMovechecks: {optMoveCheck}
+  of wStaticBoundchecks: {optStaticBoundsCheck}
+  of wStyleChecks: {optStyleCheck}
   of wAssertions: {optAssert}
   of wWarnings: {optWarns}
   of wHints: {optHints}
-  of wLinedir: {optLineDir}
-  of wStacktrace: {optStackTrace}
-  of wLinetrace: {optLineTrace}
-  of wDebugger: {optEndb}
+  of wLineDir: {optLineDir}
+  of wStackTrace: {optStackTrace}
+  of wLineTrace: {optLineTrace}
+  of wDebugger: {optNone}
   of wProfiler: {optProfiler, optMemTracker}
   of wMemTracker: {optMemTracker}
   of wByRef: {optByRef}
   of wImplicitStatic: {optImplicitStatic}
-  of wPatterns: {optPatterns}
+  of wPatterns, wTrMacros: {optTrMacros}
+  of wSinkInference: {optSinkInference}
+  of wQuirky: {optQuirky}
   else: {}
 
+proc processExperimental(c: PContext; n: PNode) =
+  if n.kind notin nkPragmaCallKinds or n.len != 2:
+    c.features.incl oldExperimentalFeatures
+  else:
+    n[1] = c.semConstExpr(c, n[1])
+    case n[1].kind
+    of nkStrLit, nkRStrLit, nkTripleStrLit:
+      try:
+        let feature = parseEnum[Feature](n[1].strVal)
+        c.features.incl feature
+        if feature == codeReordering:
+          if not isTopLevel(c):
+              localError(c.config, n.info,
+                         "Code reordering experimental pragma only valid at toplevel")
+          c.module.flags.incl sfReorder
+      except ValueError:
+        localError(c.config, n[1].info, "unknown experimental feature")
+    else:
+      localError(c.config, n.info, errStringLiteralExpected)
+
 proc tryProcessOption(c: PContext, n: PNode, resOptions: var TOptions): bool =
   result = true
   if n.kind notin nkPragmaCallKinds or n.len != 2: result = false
-  elif n.sons[0].kind == nkBracketExpr: processNote(c, n)
-  elif n.sons[0].kind != nkIdent: result = false
+  elif n[0].kind == nkBracketExpr: processNote(c, n)
+  elif n[0].kind != nkIdent: result = false
   else:
-    let sw = whichKeyword(n.sons[0].ident)
+    let sw = whichKeyword(n[0].ident)
+    if sw == wExperimental:
+      processExperimental(c, n)
+      return true
     let opts = pragmaToOptions(sw)
     if opts != {}:
       onOff(c, n, opts, resOptions)
@@ -355,10 +459,10 @@ proc tryProcessOption(c: PContext, n: PNode, resOptions: var TOptions): bool =
       of wCallconv: processCallConv(c, n)
       of wDynlib: processDynLib(c, n, nil)
       of wOptimization:
-        if n.sons[1].kind != nkIdent:
+        if n[1].kind != nkIdent:
           invalidPragma(c, n)
         else:
-          case n.sons[1].ident.s.normalize
+          case n[1].ident.s.normalize
           of "speed":
             incl(resOptions, optOptimizeSpeed)
             excl(resOptions, optOptimizeSize)
@@ -376,125 +480,144 @@ proc processOption(c: PContext, n: PNode, resOptions: var TOptions) =
     # calling conventions (boring...):
     localError(c.config, n.info, "option expected")
 
+proc checkPushedPragma(c: PContext, n: PNode) =
+  let keyDeep = n.kind in nkPragmaCallKinds and n.len > 1
+  var key = if keyDeep: n[0] else: n
+  if key.kind in nkIdentKinds:
+    let ident = considerQuotedIdent(c, key)
+    var userPragma = strTableGet(c.userPragmas, ident)
+    if userPragma == nil:
+      let k = whichKeyword(ident)
+      # TODO: might as well make a list which is not accepted by `push`: emit, cast etc.
+      if k == wEmit:
+        localError(c.config, n.info, "an 'emit' pragma cannot be pushed")
+
 proc processPush(c: PContext, n: PNode, start: int) =
-  if n.sons[start-1].kind in nkPragmaCallKinds:
+  if n[start-1].kind in nkPragmaCallKinds:
     localError(c.config, n.info, "'push' cannot have arguments")
-  var x = newOptionEntry(c.config)
-  var y = c.optionStack[^1]
-  x.options = c.config.options
-  x.defaultCC = y.defaultCC
-  x.dynlib = y.dynlib
-  x.notes = c.config.notes
-  c.optionStack.add(x)
-  for i in countup(start, sonsLen(n) - 1):
-    if not tryProcessOption(c, n.sons[i], c.config.options):
+  var x = pushOptionEntry(c)
+  for i in start..<n.len:
+    if not tryProcessOption(c, n[i], c.config.options):
       # simply store it somewhere:
+      checkPushedPragma(c, n[i])
       if x.otherPragmas.isNil:
         x.otherPragmas = newNodeI(nkPragma, n.info)
-      x.otherPragmas.add n.sons[i]
+      x.otherPragmas.add n[i]
     #localError(c.config, n.info, errOptionExpected)
 
   # If stacktrace is disabled globally we should not enable it
   if optStackTrace notin c.optionStack[0].options:
     c.config.options.excl(optStackTrace)
+  when defined(debugOptions):
+    echo c.config $ n.info, " PUSH config is now ", c.config.options
 
 proc processPop(c: PContext, n: PNode) =
   if c.optionStack.len <= 1:
     localError(c.config, n.info, "{.pop.} without a corresponding {.push.}")
   else:
-    c.config.options = c.optionStack[^1].options
-    c.config.notes = c.optionStack[^1].notes
-    c.optionStack.setLen(c.optionStack.len - 1)
+    popOptionEntry(c)
+  when defined(debugOptions):
+    echo c.config $ n.info, " POP config is now ", c.config.options
 
-proc processDefine(c: PContext, n: PNode) =
-  if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
+proc processDefineConst(c: PContext, n: PNode, sym: PSym, kind: TMagic) =
+  sym.magic = kind
+  if n.kind in nkPragmaCallKinds and n.len == 2:
+    # could also use TLib
+    n[1] = getStrLitNode(c, n)
+
+proc processDefine(c: PContext, n: PNode, sym: PSym) =
+  if sym != nil and sym.kind == skConst:
+    processDefineConst(c, n, sym, mGenericDefine)
+  elif (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")
   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")
   else:
     invalidPragma(c, n)
 
-type
-  TLinkFeature = enum
-    linkNormal, linkSys
-
-proc relativeFile(c: PContext; n: PNode; ext=""): string =
+proc relativeFile(c: PContext; n: PNode; ext=""): AbsoluteFile =
   var s = expectStrLit(c, n)
   if ext.len > 0 and splitFile(s).ext == "":
     s = addFileExt(s, ext)
-  result = parentDir(toFullPath(c.config, n.info)) / s
+  result = AbsoluteFile parentDir(toFullPath(c.config, n.info)) / s
   if not fileExists(result):
-    if isAbsolute(s): result = s
+    if isAbsolute(s): result = AbsoluteFile s
     else:
       result = findFile(c.config, s)
-      if result.len == 0: result = s
+      if result.isEmpty: result = AbsoluteFile s
 
 proc processCompile(c: PContext, n: PNode) =
-  proc docompile(c: PContext; it: PNode; src, dest: string) =
-    var cf = Cfile(cname: src, obj: dest, flags: {CfileFlag.External})
-    extccomp.addExternalFileToCompile(c.config, cf)
-    recordPragma(c, it, "compile", src, dest)
+  ## This pragma can take two forms. The first is a simple file input:
+  ##     {.compile: "file.c".}
+  ## The second is a tuple where the second arg is the output name strutils formatter:
+  ##     {.compile: ("file.c", "$1.o").}
+  proc docompile(c: PContext; it: PNode; src, dest: AbsoluteFile; customArgs: string) =
+    var cf = Cfile(nimname: splitFile(src).name,
+                   cname: src, obj: dest, flags: {CfileFlag.External},
+                   customArgs: customArgs)
+    if not fileExists(src):
+      localError(c.config, n.info, "cannot find: " & src.string)
+    else:
+      extccomp.addExternalFileToCompile(c.config, cf)
+      recordPragma(c, it, "compile", src.string, dest.string, customArgs)
 
   proc getStrLit(c: PContext, n: PNode; i: int): string =
-    n.sons[i] = c.semConstExpr(c, n[i])
+    n[i] = c.semConstExpr(c, n[i])
     case n[i].kind
     of nkStrLit, nkRStrLit, nkTripleStrLit:
-      shallowCopy(result, n[i].strVal)
+      when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
+        result = n[i].strVal
+      else:
+        shallowCopy(result, n[i].strVal)
     else:
       localError(c.config, n.info, errStringLiteralExpected)
       result = ""
 
-  let it = if n.kind in nkPragmaCallKinds and n.len == 2: n.sons[1] else: n
+  let it = if n.kind in nkPragmaCallKinds and n.len == 2: n[1] else: n
   if it.kind in {nkPar, nkTupleConstr} and it.len == 2:
     let s = getStrLit(c, it, 0)
     let dest = getStrLit(c, it, 1)
     var found = parentDir(toFullPath(c.config, n.info)) / s
     for f in os.walkFiles(found):
-      let obj = completeCFilePath(c.config, dest % extractFilename(f))
-      docompile(c, it, f, obj)
+      let obj = completeCfilePath(c.config, AbsoluteFile(dest % extractFilename(f)))
+      docompile(c, it, AbsoluteFile f, obj, "")
   else:
-    let s = expectStrLit(c, n)
-    var found = parentDir(toFullPath(c.config, n.info)) / s
+    var s = ""
+    var customArgs = ""
+    if n.kind in nkCallKinds:
+      s = getStrLit(c, n, 1)
+      if n.len <= 3:
+        customArgs = getStrLit(c, n, 2)
+      else:
+        localError(c.config, n.info, "'.compile' pragma takes up 2 arguments")
+    else:
+      s = expectStrLit(c, n)
+
+    var found = AbsoluteFile(parentDir(toFullPath(c.config, n.info)) / s)
     if not fileExists(found):
-      if isAbsolute(s): found = s
+      if isAbsolute(s): found = AbsoluteFile s
       else:
         found = findFile(c.config, s)
-        if found.len == 0: found = s
-    let obj = toObjFile(c.config, completeCFilePath(c.config, changeFileExt(found, ""), false))
-    docompile(c, it, found, obj)
+        if found.isEmpty: found = AbsoluteFile s
+    let mangled = completeCfilePath(c.config, mangleModuleName(c.config, found).AbsoluteFile)
+    let obj = toObjFile(c.config, mangled)
+    docompile(c, it, found, obj, customArgs)
 
-proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
+proc processLink(c: PContext, n: PNode) =
   let found = relativeFile(c, n, CC[c.config.cCompiler].objExt)
-  case feature
-  of linkNormal:
-    extccomp.addExternalFileToLink(c.config, found)
-    recordPragma(c, n, "link", found)
-  of linkSys:
-    let dest = c.config.libpath / completeCFilePath(c.config, found, false)
-    extccomp.addExternalFileToLink(c.config, dest)
-    recordPragma(c, n, "link", dest)
-  else: internalError(c.config, n.info, "processCommonLink")
-
-proc pragmaBreakpoint(c: PContext, n: PNode) =
-  discard getOptionalStr(c, n, "")
-
-proc pragmaWatchpoint(c: PContext, n: PNode) =
-  if n.kind in nkPragmaCallKinds and n.len == 2:
-    n.sons[1] = c.semExpr(c, n.sons[1])
-  else:
-    invalidPragma(c, n)
+  extccomp.addExternalFileToLink(c.config, found)
+  recordPragma(c, n, "link", found.string)
 
 proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
-  case n.sons[1].kind
+  case n[1].kind
   of nkStrLit, nkRStrLit, nkTripleStrLit:
-    result = newNode(if n.kind == nkAsmStmt: nkAsmStmt else: nkArgList, n.info)
-    var str = n.sons[1].strVal
+    result = newNodeI(if n.kind == nkAsmStmt: nkAsmStmt else: nkArgList, n.info)
+    if n.kind == nkAsmStmt: result.add n[0] # save asm pragmas for NIR
+    var str = n[1].strVal
     if str == "":
       localError(con.config, n.info, "empty 'asm' statement")
       return
@@ -503,28 +626,29 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
     while true:
       var b = strutils.find(str, marker, a)
       var sub = if b < 0: substr(str, a) else: substr(str, a, b - 1)
-      if sub != "": addSon(result, newStrNode(nkStrLit, sub))
+      if sub != "": result.add newStrNode(nkStrLit, sub)
       if b < 0: break
       var c = strutils.find(str, marker, b + 1)
       if c < 0: sub = substr(str, b + 1)
       else: sub = substr(str, b + 1, c - 1)
       if sub != "":
-        var e = searchInScopes(con, getIdent(con.cache, sub))
+        var amb = false
+        var e = searchInScopes(con, getIdent(con.cache, sub), amb)
+        # XXX what to do here if 'amb' is true?
         if e != nil:
-          when false:
-            if e.kind == skStub: loadStub(e)
           incl(e.flags, sfUsed)
-          addSon(result, newSymNode(e))
+          result.add newSymNode(e)
         else:
-          addSon(result, newStrNode(nkStrLit, sub))
+          result.add newStrNode(nkStrLit, sub)
       else:
         # an empty '``' produces a single '`'
-        addSon(result, newStrNode(nkStrLit, $marker))
+        result.add newStrNode(nkStrLit, $marker)
       if c < 0: break
       a = c + 1
   else:
     illFormedAstLocal(n, con.config)
-    result = newNode(nkAsmStmt, n.info)
+    result = newNodeI(nkAsmStmt, n.info)
+    if n.kind == nkAsmStmt: result.add n[0]
 
 proc pragmaEmit(c: PContext, n: PNode) =
   if n.kind notin nkPragmaCallKinds or n.len != 2:
@@ -534,13 +658,13 @@ proc pragmaEmit(c: PContext, n: PNode) =
     if n1.kind == nkBracket:
       var b = newNodeI(nkBracket, n1.info, n1.len)
       for i in 0..<n1.len:
-        b.sons[i] = c.semExpr(c, n1[i])
-      n.sons[1] = b
+        b[i] = c.semExprWithType(c, n1[i], {efTypeAllowed})
+      n[1] = b
     else:
-      n.sons[1] = c.semConstExpr(c, n1)
-      case n.sons[1].kind
+      n[1] = c.semConstExpr(c, n1)
+      case n[1].kind
       of nkStrLit, nkRStrLit, nkTripleStrLit:
-        n.sons[1] = semAsmOrEmit(c, n, '`')
+        n[1] = semAsmOrEmit(c, n, '`')
       else:
         localError(c.config, n.info, errStringLiteralExpected)
 
@@ -553,27 +677,26 @@ proc pragmaUnroll(c: PContext, n: PNode) =
   elif n.kind in nkPragmaCallKinds and n.len == 2:
     var unrollFactor = expectIntLit(c, n)
     if unrollFactor <% 32:
-      n.sons[1] = newIntNode(nkIntLit, unrollFactor)
+      n[1] = newIntNode(nkIntLit, unrollFactor)
     else:
       invalidPragma(c, n)
 
 proc pragmaLine(c: PContext, n: PNode) =
   if n.kind in nkPragmaCallKinds and n.len == 2:
-    n.sons[1] = c.semConstExpr(c, n.sons[1])
-    let a = n.sons[1]
+    n[1] = c.semConstExpr(c, n[1])
+    let a = n[1]
     if a.kind in {nkPar, nkTupleConstr}:
       # unpack the tuple
-      var x = a.sons[0]
-      var y = a.sons[1]
-      if x.kind == nkExprColonExpr: x = x.sons[1]
-      if y.kind == nkExprColonExpr: y = y.sons[1]
+      var x = a[0]
+      var y = a[1]
+      if x.kind == nkExprColonExpr: x = x[1]
+      if y.kind == nkExprColonExpr: y = y[1]
       if x.kind != nkStrLit:
         localError(c.config, n.info, errStringLiteralExpected)
       elif y.kind != nkIntLit:
         localError(c.config, n.info, errIntLiteralExpected)
       else:
-        # XXX this produces weird paths which are not properly resolved:
-        n.info.fileIndex = msgs.fileInfoIdx(c.config, x.strVal)
+        n.info.fileIndex = fileInfoIdx(c.config, AbsoluteFile(x.strVal))
         n.info.line = uint16(y.intVal)
     else:
       localError(c.config, n.info, "tuple expected")
@@ -582,24 +705,32 @@ proc pragmaLine(c: PContext, n: PNode) =
     n.info = getInfoContext(c.config, -1)
 
 proc processPragma(c: PContext, n: PNode, i: int) =
+  ## Create and add a new custom pragma `{.pragma: name.}` node to the module's context.
   let it = n[i]
-  if it.kind notin nkPragmaCallKinds and it.len == 2: invalidPragma(c, n)
-  elif it[0].kind != nkIdent: invalidPragma(c, n)
-  elif it[1].kind != nkIdent: invalidPragma(c, n)
+  if it.kind notin nkPragmaCallKinds and it.safeLen == 2:
+    invalidPragma(c, n)
+    return
+  elif it.safeLen != 2 or it[0].kind != nkIdent or it[1].kind != nkIdent:
+    invalidPragma(c, n)
+    return
 
-  var userPragma = newSym(skTemplate, it[1].ident, nil, it.info, c.config.options)
-  userPragma.ast = newNode(nkPragma, n.info, n.sons[i+1..^1])
+  var userPragma = newSym(skTemplate, it[1].ident, c.idgen, c.module, it.info, c.config.options)
+  styleCheckDef(c, userPragma)
+  userPragma.ast = newTreeI(nkPragma, n.info, n.sons[i+1..^1])
   strTableAdd(c.userPragmas, userPragma)
 
 proc pragmaRaisesOrTags(c: PContext, n: PNode) =
   proc processExc(c: PContext, x: PNode) =
-    var t = skipTypes(c.semTypeNode(c, x, nil), skipPtrs)
-    if t.kind != tyObject:
-      localError(c.config, x.info, errGenerated, "invalid type for raises/tags list")
-    x.typ = t
+    if c.hasUnresolvedArgs(c, x):
+      x.typ = makeTypeFromExpr(c, x)
+    else:
+      var t = skipTypes(c.semTypeNode(c, x, nil), skipPtrs)
+      if t.kind notin {tyObject, tyOr}:
+        localError(c.config, x.info, errGenerated, "invalid type for raises/tags list")
+      x.typ = t
 
   if n.kind in nkPragmaCallKinds and n.len == 2:
-    let it = n.sons[1]
+    let it = n[1]
     if it.kind notin {nkCurly, nkBracket}:
       processExc(c, it)
     else:
@@ -615,41 +746,26 @@ proc pragmaLockStmt(c: PContext; it: PNode) =
     if n.kind != nkBracket:
       localError(c.config, n.info, errGenerated, "locks pragma takes a list of expressions")
     else:
-      for i in 0 ..< n.len:
-        n.sons[i] = c.semExpr(c, n.sons[i])
-
-proc pragmaLocks(c: PContext, it: PNode): TLockLevel =
-  if it.kind notin nkPragmaCallKinds or it.len != 2:
-    invalidPragma(c, it)
-  else:
-    case it[1].kind
-    of nkStrLit, nkRStrLit, nkTripleStrLit:
-      if it[1].strVal == "unknown":
-        result = UnknownLockLevel
-      else:
-        localError(c.config, it[1].info, "invalid string literal for locks pragma (only allowed string is \"unknown\")")
-    else:
-      let x = expectIntLit(c, it)
-      if x < 0 or x > MaxLockLevel:
-        localError(c.config, it[1].info, "integer must be within 0.." & $MaxLockLevel)
-      else:
-        result = TLockLevel(x)
+      for i in 0..<n.len:
+        n[i] = c.semExpr(c, n[i])
 
 proc typeBorrow(c: PContext; sym: PSym, n: PNode) =
   if n.kind in nkPragmaCallKinds and n.len == 2:
-    let it = n.sons[1]
+    let it = n[1]
     if it.kind != nkAccQuoted:
       localError(c.config, n.info, "a type can only borrow `.` for now")
   incl(sym.typ.flags, tfBorrowDot)
 
 proc markCompilerProc(c: PContext; s: PSym) =
-  # minor hack ahead: FlowVar is the only generic .compilerProc type which
+  # minor hack ahead: FlowVar is the only generic .compilerproc type which
   # should not have an external name set:
   if s.kind != skType or s.name.s != "FlowVar":
     makeExternExport(c, s, "$1", s.info)
   incl(s.flags, sfCompilerProc)
   incl(s.flags, sfUsed)
   registerCompilerProc(c.graph, s)
+  if c.config.symbolFiles != disabledSf:
+    addCompilerProc(c.encoder, c.packedRepr, s)
 
 proc deprecatedStmt(c: PContext; outerPragma: PNode) =
   let pragma = outerPragma[1]
@@ -659,19 +775,8 @@ proc deprecatedStmt(c: PContext; outerPragma: PNode) =
     return
   if pragma.kind != nkBracket:
     localError(c.config, pragma.info, "list of key:value pairs expected"); return
-  for n in pragma:
-    if n.kind in nkPragmaCallKinds and n.len == 2:
-      let dest = qualifiedLookUp(c, n[1], {checkUndeclared})
-      if dest == nil or dest.kind in routineKinds:
-        localError(c.config, n.info, warnUser, "the .deprecated pragma is unreliable for routines")
-      let src = considerQuotedIdent(c, n[0])
-      let alias = newSym(skAlias, src, dest, n[0].info, c.config.options)
-      incl(alias.flags, sfExported)
-      if sfCompilerProc in dest.flags: markCompilerProc(c, alias)
-      addInterfaceDecl(c, alias)
-      n.sons[1] = newSymNode(dest)
-    else:
-      localError(c.config, n.info, "key:value pair expected")
+  message(c.config, pragma.info, warnDeprecated,
+    "deprecated statement is now a no-op, use regular deprecated pragma")
 
 proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
   if it.kind notin nkPragmaCallKinds or it.len != 2:
@@ -687,77 +792,119 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
       # We return a dummy symbol; later passes over the type will repair it.
       # Generic instantiation needs to know about this too. But we're lazy
       # and perform the lookup on demand instead.
-      result = newSym(skUnknown, considerQuotedIdent(c, n), nil, n.info,
+      result = newSym(skUnknown, considerQuotedIdent(c, n), c.idgen, nil, n.info,
         c.config.options)
   else:
     result = qualifiedLookUp(c, n, {checkUndeclared})
 
-proc semCustomPragma(c: PContext, n: PNode): PNode =
-  if n.kind == nkIdent:
-    result = newTree(nkCall, n)
-  elif n.kind == nkExprColonExpr:
+proc semCustomPragma(c: PContext, n: PNode, sym: PSym): PNode =
+  var callNode: PNode
+
+  case n.kind
+  of nkIdentKinds:
+    # pragma -> pragma()
+    callNode = newTree(nkCall, n)
+  of nkExprColonExpr:
     # pragma: arg -> pragma(arg)
-    result = newTree(nkCall, n[0], n[1])
-  elif n.kind in nkPragmaCallKinds + {nkIdent}:
-    result = n
+    callNode = newTree(nkCall, n[0], n[1])
+  of nkPragmaCallKinds - {nkExprColonExpr}:
+    callNode = n
   else:
     invalidPragma(c, n)
     return n
 
-  let r = c.semOverloadedCall(c, result, n, {skTemplate}, {})
+  trySuggestPragmas(c, callNode[0])
+
+  let r = c.semOverloadedCall(c, callNode, n, {skTemplate}, {efNoUndeclared})
   if r.isNil or sfCustomPragma notin r[0].sym.flags:
     invalidPragma(c, n)
-  else:
-    result = r
-    if n.kind == nkIdent:
-      result = result[0]
-    elif n.kind == nkExprColonExpr:
-      result.kind = n.kind # pragma(arg) -> pragma: arg
-
-proc processExperimental(c: PContext; n: PNode; s: PSym) =
-  if not isTopLevel(c):
-    localError(c.config, n.info, "'experimental' pragma only valid as toplevel statement")
+    return n
+
+  # we have a valid custom pragma
+  if sym != nil and sym.kind in {skEnumField, skForVar, skModule}:
+    illegalCustomPragma(c, n, sym)
+    return n
+
+  result = r
+  # Transform the nkCall node back to its original form if possible
+  if n.kind == nkIdent and r.len == 1:
+    # pragma() -> pragma
+    result = result[0]
+  elif n.kind == nkExprColonExpr and r.len == 2:
+    # pragma(arg) -> pragma: arg
+    result.transitionSonsKind(n.kind)
+
+proc processEffectsOf(c: PContext, n: PNode; owner: PSym) =
+  proc processParam(c: PContext; n: PNode) =
+    let r = c.semExpr(c, n)
+    if r.kind == nkSym and r.sym.kind == skParam:
+      if r.sym.owner == owner:
+        incl r.sym.flags, sfEffectsDelayed
+      else:
+        localError(c.config, n.info, errGenerated, "parameter cannot be declared as .effectsOf")
+    else:
+      localError(c.config, n.info, errGenerated, "parameter name expected")
+
   if n.kind notin nkPragmaCallKinds or n.len != 2:
-    c.features.incl oldExperimentalFeatures
+    localError(c.config, n.info, errGenerated, "parameter name expected")
   else:
-    n[1] = c.semConstExpr(c, n[1])
-    case n[1].kind
-    of nkStrLit, nkRStrLit, nkTripleStrLit:
-      try:
-        c.features.incl parseEnum[Feature](n[1].strVal)
-      except ValueError:
-        localError(c.config, n[1].info, "unknown experimental feature")
+    let it = n[1]
+    if it.kind in {nkCurly, nkBracket}:
+      for x in items(it): processParam(c, x)
     else:
-      localError(c.config, n.info, errStringLiteralExpected)
+      processParam(c, it)
 
 proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
-                  validPragmas: TSpecialWords): bool =
-  var it = n.sons[i]
-  var key = if it.kind in nkPragmaCallKinds and it.len > 1: it.sons[0] else: it
+                  validPragmas: TSpecialWords,
+                  comesFromPush, isStatement: bool): bool =
+  result = false
+  var it = n[i]
+  let keyDeep = it.kind in nkPragmaCallKinds and it.len > 1
+  var key = if keyDeep: it[0] else: it
   if key.kind == nkBracketExpr:
     processNote(c, it)
     return
+  elif key.kind == nkCast:
+    if comesFromPush:
+      localError(c.config, n.info, "a 'cast' pragma cannot be pushed")
+    elif not isStatement:
+      localError(c.config, n.info, "'cast' pragma only allowed in a statement context")
+    case whichPragma(key[1])
+    of wRaises, wTags, wForbids: pragmaRaisesOrTags(c, key[1])
+    else: discard
+    return
   elif key.kind notin nkIdentKinds:
-    n.sons[i] = semCustomPragma(c, it)
+    n[i] = semCustomPragma(c, it, sym)
     return
   let ident = considerQuotedIdent(c, key)
   var userPragma = strTableGet(c.userPragmas, ident)
   if userPragma != nil:
+    styleCheckUse(c, key.info, userPragma)
+
     # number of pragmas increase/decrease with user pragma expansion
     inc c.instCounter
+    defer: dec c.instCounter
     if c.instCounter > 100:
       globalError(c.config, it.info, "recursive dependency: " & userPragma.name.s)
 
-    pragma(c, sym, userPragma.ast, validPragmas)
+    if keyDeep:
+      localError(c.config, it.info, "user pragma cannot have arguments")
+
+    pragma(c, sym, userPragma.ast, validPragmas, isStatement)
     n.sons[i..i] = userPragma.ast.sons # expand user pragma with its content
     i.inc(userPragma.ast.len - 1) # inc by -1 is ok, user pragmas was empty
-    dec c.instCounter
   else:
     let k = whichKeyword(ident)
     if k in validPragmas:
+      checkPragmaUse(c, key.info, k, ident.s, (if sym != nil: sym else: c.module))
       case k
-      of wExportc:
+      of wExportc, wExportCpp:
         makeExternExport(c, sym, getOptionalStr(c, it, "$1"), it.info)
+        if k == wExportCpp:
+          if c.config.backend != backendCpp:
+            localError(c.config, it.info, "exportcpp requires `cpp` backend, got: " & $c.config.backend)
+          else:
+            incl(sym.flags, sfMangleCpp)
         incl(sym.flags, sfUsed) # avoid wrong hints
       of wImportc:
         let name = getOptionalStr(c, it, "$1")
@@ -770,33 +917,48 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         recordPragma(c, it, "cppdefine", name)
         processImportCompilerProc(c, sym, name, it.info)
       of wExtern: setExternName(c, sym, expectStrLit(c, it), it.info)
-      of wImmediate:
-        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")
-        else: invalidPragma(c, it)
       of wDirty:
         if sym.kind == skTemplate: incl(sym.flags, sfDirty)
         else: invalidPragma(c, it)
+      of wRedefine:
+        if sym.kind == skTemplate: incl(sym.flags, sfTemplateRedefinition)
+        else: invalidPragma(c, it)
+      of wCallsite:
+        if sym.kind == skTemplate: incl(sym.flags, sfCallsite)
+        else: invalidPragma(c, it)
       of wImportCpp:
         processImportCpp(c, sym, getOptionalStr(c, it, "$1"), it.info)
+      of wCppNonPod:
+        incl(sym.flags, sfCppNonPod)
+      of wImportJs:
+        if c.config.backend != backendJs:
+          localError(c.config, it.info, "`importjs` pragma requires the JavaScript target")
+        let name = getOptionalStr(c, it, "$1")
+        incl(sym.flags, sfImportc)
+        incl(sym.flags, sfInfixCall)
+        if sym.kind in skProcKinds and {'(', '#', '@'} notin name:
+          localError(c.config, n.info, "`importjs` for routines requires a pattern")
+        setExternName(c, sym, name, it.info)
       of wImportObjC:
         processImportObjC(c, sym, getOptionalStr(c, it, "$1"), it.info)
-      of wAlign:
-        if sym.typ == nil: invalidPragma(c, it)
-        var align = expectIntLit(c, it)
-        if (not isPowerOfTwo(align) and align != 0) or align >% high(int16):
-          localError(c.config, it.info, "power of two expected")
-        else:
-          sym.typ.align = align.int16
       of wSize:
         if sym.typ == nil: invalidPragma(c, it)
         var size = expectIntLit(c, it)
-        if not isPowerOfTwo(size) or size <= 0 or size > 8:
-          localError(c.config, it.info, "power of two expected")
-        else:
+        case size
+        of 1, 2, 4:
           sym.typ.size = size
+          sym.typ.align = int16 size
+        of 8:
+          sym.typ.size = 8
+          sym.typ.align = floatInt64Align(c.config)
+        else:
+          localError(c.config, it.info, "size may only be 1, 2, 4 or 8")
+      of wAlign:
+        let alignment = expectIntLit(c, it)
+        if isPowerOfTwo(alignment) and alignment > 0:
+          sym.alignment = max(sym.alignment, alignment)
+        else:
+          localError(c.config, it.info, "power of two expected")
       of wNodecl:
         noVal(c, it)
         incl(sym.loc.flags, lfNoDecl)
@@ -808,30 +970,44 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wVolatile:
         noVal(c, it)
         incl(sym.flags, sfVolatile)
+      of wCursor:
+        noVal(c, it)
+        incl(sym.flags, sfCursor)
       of wRegister:
         noVal(c, it)
         incl(sym.flags, sfRegister)
+      of wNoalias:
+        noVal(c, it)
+        incl(sym.flags, sfNoalias)
+      of wEffectsOf:
+        processEffectsOf(c, it, sym)
       of wThreadVar:
         noVal(c, it)
         incl(sym.flags, {sfThread, sfGlobal})
-      of wDeadCodeElimUnused: discard  # deprecated, dead code elim always on
+      of wDeadCodeElimUnused:
+        warningDeprecated(c.config, n.info, "'{.deadcodeelim: on.}' is deprecated, now a noop")  # deprecated, dead code elim always on
       of wNoForward: pragmaNoForward(c, it)
-      of wReorder: pragmaNoForward(c, it, sfReorder)
+      of wReorder: pragmaNoForward(c, it, flag = sfReorder)
       of wMagic: processMagic(c, it, sym)
       of wCompileTime:
         noVal(c, it)
-        incl(sym.flags, sfCompileTime)
-        incl(sym.loc.flags, lfNoDecl)
+        if comesFromPush:
+          if sym.kind in {skProc, skFunc}:
+            incl(sym.flags, sfCompileTime)
+        else:
+          incl(sym.flags, sfCompileTime)
+        #incl(sym.loc.flags, lfNoDecl)
       of wGlobal:
         noVal(c, it)
         incl(sym.flags, sfGlobal)
         incl(sym.flags, sfPure)
-      of wMerge:
-        # only supported for backwards compat, doesn't do anything anymore
-        noVal(c, it)
       of wConstructor:
-        noVal(c, it)
         incl(sym.flags, sfConstructor)
+        if sfImportc notin sym.flags:
+          sym.constraint = newEmptyStrNode(c, it, getOptionalStr(c, it, ""))
+          sym.constraint.strVal = sym.constraint.strVal
+          sym.flags.incl {sfExportc, sfMangleCpp}
+          sym.typ.callConv = ccNoConvention
       of wHeader:
         var lib = getLib(c, libHeader, getStrLitNode(c, it))
         addToLib(lib, sym)
@@ -839,22 +1015,29 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         incl(sym.loc.flags, lfHeader)
         incl(sym.loc.flags, lfNoDecl)
         # implies nodecl, because otherwise header would not make sense
-        if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
-      of wOverride:
-        sym.flags.incl sfOverriden
-      of wNosideeffect:
+        if sym.loc.snippet == "": sym.loc.snippet = rope(sym.name.s)
+      of wNoSideEffect:
         noVal(c, it)
-        incl(sym.flags, sfNoSideEffect)
-        if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
-      of wSideeffect:
+        if sym != nil:
+          incl(sym.flags, sfNoSideEffect)
+          if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
+      of wSideEffect:
         noVal(c, it)
         incl(sym.flags, sfSideEffect)
       of wNoreturn:
         noVal(c, it)
-        incl(sym.flags, sfNoReturn)
-        if sym.typ[0] != nil:
+        # Disable the 'noreturn' annotation when in the "Quirky Exceptions" mode!
+        if c.config.exc != excQuirky:
+          incl(sym.flags, sfNoReturn)
+        if sym.typ.returnType != nil:
           localError(c.config, sym.ast[paramsPos][0].info,
             ".noreturn with return type not allowed")
+      of wNoDestroy:
+        noVal(c, it)
+        incl(sym.flags, sfGeneratedOp)
+      of wNosinks:
+        noVal(c, it)
+        incl(sym.flags, sfWasForwarded)
       of wDynlib:
         processDynLib(c, it, sym)
       of wCompilerProc, wCore:
@@ -862,17 +1045,25 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         cppDefine(c.graph.config, sym.name.s)
         recordPragma(c, it, "cppdefine", sym.name.s)
         if sfFromGeneric notin sym.flags: markCompilerProc(c, sym)
+      of wNonReloadable:
+        sym.flags.incl sfNonReloadable
       of wProcVar:
+        # old procvar annotation, no longer needed
         noVal(c, it)
-        incl(sym.flags, sfProcvar)
       of wExplain:
         sym.flags.incl sfExplain
       of wDeprecated:
-        if sym != nil and sym.kind in routineKinds:
+        if sym != nil and sym.kind in routineKinds + {skType, skVar, skLet, skConst}:
           if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it)
           incl(sym.flags, sfDeprecated)
+        elif sym != nil and sym.kind != skModule:
+          # We don't support the extra annotation field
+          if it.kind in nkPragmaCallKinds:
+            localError(c.config, it.info, "annotation to deprecated not supported here")
+          incl(sym.flags, sfDeprecated)
+        # At this point we're quite sure this is a statement and applies to the
+        # whole module
         elif it.kind in nkPragmaCallKinds: deprecatedStmt(c, it)
-        elif sym != nil: incl(sym.flags, sfDeprecated)
         else: incl(c.module.flags, sfDeprecated)
       of wVarargs:
         noVal(c, it)
@@ -907,10 +1098,15 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wThread:
         noVal(c, it)
         incl(sym.flags, sfThread)
-        incl(sym.flags, sfProcvar)
         if sym.typ != nil:
           incl(sym.typ.flags, tfThread)
-          if sym.typ.callConv == ccClosure: sym.typ.callConv = ccDefault
+          if sym.typ.callConv == ccClosure: sym.typ.callConv = ccNimCall
+      of wSendable:
+        noVal(c, it)
+        if sym != nil and sym.typ != nil:
+          incl(sym.typ.flags, tfSendable)
+        else:
+          invalidPragma(c, it)
       of wGcSafe:
         noVal(c, it)
         if sym != nil:
@@ -932,37 +1128,50 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         recordPragma(c, it, "warning", s)
         message(c.config, it.info, warnUser, s)
       of wError:
-        if sym != nil and sym.isRoutine:
+        if sym != nil and (sym.isRoutine or sym.kind == skType) and not isStatement:
           # This is subtle but correct: the error *statement* is only
-          # allowed for top level statements. Seems to be easier than
-          # distinguishing properly between
+          # allowed when 'wUsed' is not in validPragmas. Here this is the easiest way to
+          # distinguish properly between
           # ``proc p() {.error}`` and ``proc p() = {.error: "msg".}``
-          noVal(c, it)
+          if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it)
           incl(sym.flags, sfError)
+          excl(sym.flags, sfForward)
         else:
           let s = expectStrLit(c, it)
           recordPragma(c, it, "error", s)
           localError(c.config, it.info, errUser, s)
-      of wFatal: fatal(c.config, it.info, errUser, expectStrLit(c, it))
-      of wDefine: processDefine(c, it)
+      of wFatal: fatal(c.config, it.info, expectStrLit(c, it))
+      of wDefine: processDefine(c, it, sym)
       of wUndef: processUndef(c, it)
-      of wCompile: processCompile(c, it)
-      of wLink: processCommonLink(c, it, linkNormal)
-      of wLinksys: processCommonLink(c, it, linkSys)
+      of wCompile:
+        let m = sym.getModule()
+        incl(m.flags, sfUsed)
+        processCompile(c, it)
+      of wLink: processLink(c, it)
       of wPassl:
+        let m = sym.getModule()
+        incl(m.flags, sfUsed)
         let s = expectStrLit(c, it)
         extccomp.addLinkOption(c.config, s)
         recordPragma(c, it, "passl", s)
       of wPassc:
+        let m = sym.getModule()
+        incl(m.flags, sfUsed)
         let s = expectStrLit(c, it)
         extccomp.addCompileOption(c.config, s)
         recordPragma(c, it, "passc", s)
-      of wBreakpoint: pragmaBreakpoint(c, it)
-      of wWatchPoint: pragmaWatchpoint(c, it)
+      of wLocalPassc:
+        assert sym != nil and sym.kind == skModule
+        let s = expectStrLit(c, it)
+        appendToModule(sym, n)
+        extccomp.addLocalCompileOption(c.config, s, toFullPathConsiderDirty(c.config, sym.info.fileIndex))
+        recordPragma(c, it, "localpassl", s)
       of wPush:
         processPush(c, n, i + 1)
         result = true
-      of wPop: processPop(c, it)
+      of wPop:
+        processPop(c, it)
+        result = true
       of wPragma:
         if not sym.isNil and sym.kind == skTemplate:
           sym.flags.incl sfCustomPragma
@@ -976,12 +1185,13 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         noVal(c, it)
         if sym != nil: incl(sym.flags, sfNoInit)
       of wCodegenDecl: processCodegenDecl(c, it, sym)
-      of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
-         wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
-         wLinedir, wOptimization, wMovechecks, wCallconv, wDebugger, wProfiler,
-         wFloatchecks, wNanChecks, wInfChecks, wPatterns:
+      of wChecks, wObjChecks, wFieldChecks, wRangeChecks, wBoundChecks,
+         wOverflowChecks, wNilChecks, wAssertions, wWarnings, wHints,
+         wLineDir, wOptimization, wStaticBoundchecks, wStyleChecks,
+         wCallconv, wDebugger, wProfiler,
+         wFloatChecks, wNanChecks, wInfChecks, wPatterns, wTrMacros:
         processOption(c, it, c.config.options)
-      of wStacktrace, wLinetrace:
+      of wStackTrace, wLineTrace:
         if sym.kind in {skProc, skMethod, skConverter}:
           processOption(c, it, sym.options)
         else:
@@ -989,7 +1199,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of FirstCallConv..LastCallConv:
         assert(sym != nil)
         if sym.typ == nil: invalidPragma(c, it)
-        else: sym.typ.callConv = wordToCallConv(k)
+        else:
+          sym.typ.callConv = wordToCallConv(k)
+          sym.typ.flags.incl tfExplicitCallConv
       of wEmit: pragmaEmit(c, it)
       of wUnroll: pragmaUnroll(c, it)
       of wLinearScanEnd, wComputedGoto: noVal(c, it)
@@ -1000,27 +1212,44 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         noVal(c, it)
         if sym.typ == nil: invalidPragma(c, it)
         else: incl(sym.typ.flags, tfIncompleteStruct)
-      of wUnchecked:
+      of wCompleteStruct:
         noVal(c, it)
         if sym.typ == nil: invalidPragma(c, it)
-        else: incl(sym.typ.flags, tfUncheckedArray)
-      of wUnion:
+        else: incl(sym.typ.flags, tfCompleteStruct)
+      of wUnchecked:
         noVal(c, it)
-        if sym.typ == nil: invalidPragma(c, it)
-        else: incl(sym.typ.flags, tfUnion)
+        if sym.typ == nil or sym.typ.kind notin {tyArray, tyUncheckedArray}:
+          invalidPragma(c, it)
+        else:
+          sym.typ.kind = tyUncheckedArray
+      of wUnion:
+        if c.config.backend == backendJs:
+          localError(c.config, it.info, "`{.union.}` is not implemented for js backend.")
+        else:
+          noVal(c, it)
+          if sym.typ == nil: invalidPragma(c, it)
+          else: incl(sym.typ.flags, tfUnion)
       of wRequiresInit:
         noVal(c, it)
-        if sym.typ == nil: invalidPragma(c, it)
-        else: incl(sym.typ.flags, tfNeedsInit)
+        if sym.kind == skField:
+          sym.flags.incl sfRequiresInit
+        elif sym.typ != nil:
+          incl(sym.typ.flags, tfNeedsFullInit)
+        else:
+          invalidPragma(c, it)
       of wByRef:
         noVal(c, it)
-        if sym == nil or sym.typ == nil:
+        if sym != nil and sym.kind == skParam:
+          sym.options.incl optByRef
+        elif sym == nil or sym.typ == nil:
           processOption(c, it, c.config.options)
         else:
           incl(sym.typ.flags, tfByRef)
       of wByCopy:
         noVal(c, it)
-        if sym.kind != skType or sym.typ == nil: invalidPragma(c, it)
+        if sym.kind == skParam:
+          incl(sym.flags, sfByCopy)
+        elif sym.kind != skType or sym.typ == nil: invalidPragma(c, it)
         else: incl(sym.typ.flags, tfByCopy)
       of wPartial:
         noVal(c, it)
@@ -1033,16 +1262,18 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         noVal(c, it)
         if sym == nil: invalidPragma(c, it)
       of wLine: pragmaLine(c, it)
-      of wRaises, wTags: pragmaRaisesOrTags(c, it)
+      of wRaises, wTags, wForbids: pragmaRaisesOrTags(c, it)
       of wLocks:
         if sym == nil: pragmaLockStmt(c, it)
         elif sym.typ == nil: invalidPragma(c, it)
-        else: sym.typ.lockLevel = pragmaLocks(c, it)
+        else: warningDeprecated(c.config, n.info, "'Lock levels' are deprecated, now a noop")
       of wBitsize:
         if sym == nil or sym.kind != skField:
           invalidPragma(c, it)
         else:
           sym.bitsize = expectIntLit(c, it)
+          if sym.bitsize <= 0:
+            localError(c.config, it.info, "bitsize needs to be positive")
       of wGuard:
         if sym == nil or sym.kind notin {skVar, skLet, skField}:
           invalidPragma(c, it)
@@ -1056,61 +1287,97 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wExportNims:
         if sym == nil: invalidPragma(c, it)
         else: magicsys.registerNimScriptSymbol(c.graph, sym)
-      of wInjectStmt:
-        if it.kind notin nkPragmaCallKinds or it.len != 2:
-          localError(c.config, it.info, "expression expected")
-        else:
-          it.sons[1] = c.semExpr(c, it.sons[1])
       of wExperimental:
-        processExperimental(c, it, sym)
-      of wThis:
-        if it.kind in nkPragmaCallKinds and it.len == 2:
-          c.selfName = considerQuotedIdent(c, it[1])
-        elif it.kind == nkIdent or it.len == 1:
-          c.selfName = getIdent(c.cache, "self")
-        else:
-          localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments")
+        if not isTopLevel(c):
+          localError(c.config, n.info, "'experimental' pragma only valid as toplevel statement or in a 'push' environment")
+        processExperimental(c, it)
+      of wDoctype:
+        if not isTopLevel(c):
+          localError(c.config, n.info, "\"doctype\" pragma only valid as top-level statement")
       of wNoRewrite:
         noVal(c, it)
       of wBase:
         noVal(c, it)
         sym.flags.incl sfBase
       of wIntDefine:
-        sym.magic = mIntDefine
+        processDefineConst(c, n, sym, mIntDefine)
       of wStrDefine:
-        sym.magic = mStrDefine
+        processDefineConst(c, n, sym, mStrDefine)
+      of wBoolDefine:
+        processDefineConst(c, n, sym, mBoolDefine)
       of wUsed:
         noVal(c, it)
         if sym == nil: invalidPragma(c, it)
         else: sym.flags.incl sfUsed
-      of wLiftLocals: discard
+      of wLiftLocals:
+        sym.flags.incl(sfForceLift)
+      of wRequires, wInvariant, wAssume, wAssert:
+        pragmaProposition(c, it)
+      of wEnsures:
+        pragmaEnsures(c, it)
+      of wEnforceNoRaises, wQuirky:
+        sym.flags.incl sfNeverRaises
+      of wSystemRaisesDefect:
+        sym.flags.incl sfSystemRaisesDefect
+      of wVirtual:
+        processVirtual(c, it, sym, sfVirtual)
+      of wMember:
+        processVirtual(c, it, sym, sfMember)
+
       else: invalidPragma(c, it)
+    elif comesFromPush and whichKeyword(ident) != wInvalid:
+      discard "ignore the .push pragma; it doesn't apply"
     else:
-      n.sons[i] = semCustomPragma(c, it)
-
-
-proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
+      # semCustomPragma gives appropriate error for invalid pragmas
+      n[i] = semCustomPragma(c, it, sym)
+
+proc overwriteLineInfo(n: PNode; info: TLineInfo) =
+  n.info = info
+  for i in 0..<n.safeLen:
+    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:
+    for p in pragmas: n[pragmasPos].add p
+
+proc mergeValidPragmas(n, pragmas: PNode, validPragmas: TSpecialWords) =
+  if n[pragmasPos].kind == nkEmpty:
+    n[pragmasPos] = newNodeI(nkPragma, n.info)
+  for p in pragmas:
+    let prag = whichPragma(p)
+    if prag in validPragmas:
+      let copy = copyTree(p)
+      overwriteLineInfo copy, n.info
+      n[pragmasPos].add copy
+
+proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
                       validPragmas: TSpecialWords) =
   if sym != nil and sym.kind != skModule:
     for it in c.optionStack:
       let o = it.otherPragmas
-      if not o.isNil:
-        pushInfoContext(c.config, n.info)
+      if not o.isNil and sfFromGeneric notin sym.flags: # bug #23019
+        pushInfoContext(c.config, info)
         var i = 0
-        while i < o.len():
-          if singlePragma(c, sym, o, i, validPragmas):
-            internalError(c.config, n.info, "implicitPragmas")
+        while i < o.len:
+          if singlePragma(c, sym, o, i, validPragmas, true, false):
+            internalError(c.config, info, "implicitPragmas")
           inc i
         popInfoContext(c.config)
+        if sym.kind in routineKinds and sym.ast != nil:
+          mergeValidPragmas(sym.ast, o, validPragmas)
 
     if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
-      localError(c.config, n.info, ".dynlib requires .exportc")
+      localError(c.config, info, ".dynlib requires .exportc")
     var lib = c.optionStack[^1].dynlib
     if {lfDynamicLib, lfHeader} * sym.loc.flags == {} and
         sfImportc in sym.flags and lib != nil:
       incl(sym.loc.flags, lfDynamicLib)
       addToLib(lib, sym)
-      if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
+      if sym.loc.snippet == "": sym.loc.snippet = rope(sym.name.s)
 
 proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
   if n == nil: return false
@@ -1122,14 +1389,23 @@ proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
 
   return false
 
-proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
+proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
+               isStatement: bool) =
   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, isStatement): break
     inc i
 
-proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
+proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
+            isStatement: bool) =
+  if n == nil: return
+  pragmaRec(c, sym, n, validPragmas, isStatement)
+  # XXX: in the case of a callable def, this should use its info
+  implicitPragmas(c, sym, n.info, validPragmas)
+
+proc pragmaCallable*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords,
+                    isStatement: bool = false) =
   if n == nil: return
-  pragmaRec(c, sym, n, validPragmas)
-  implicitPragmas(c, sym, n, validPragmas)
+  if n[pragmasPos].kind != nkEmpty:
+    pragmaRec(c, sym, n[pragmasPos], validPragmas, isStatement)
diff --git a/compiler/prefixmatches.nim b/compiler/prefixmatches.nim
index 246d1ae5e..bfbe3d888 100644
--- a/compiler/prefixmatches.nim
+++ b/compiler/prefixmatches.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-from strutils import toLowerAscii
+from std/strutils import toLowerAscii
 
 type
   PrefixMatch* {.pure.} = enum
@@ -20,14 +20,13 @@ proc prefixMatch*(p, s: string): PrefixMatch =
   template eq(a, b): bool = a.toLowerAscii == b.toLowerAscii
   if p.len > s.len: return PrefixMatch.None
   var i = 0
-  let L = s.len
   # check for prefix/contains:
-  while i < L:
+  while i < s.len:
     if s[i] == '_': inc i
-    if i < L and eq(s[i], p[0]):
+    if i < s.len and eq(s[i], p[0]):
       var ii = i+1
       var jj = 1
-      while ii < L and jj < p.len:
+      while ii < s.len and jj < p.len:
         if p[jj] == '_': inc jj
         if s[ii] == '_': inc ii
         if not eq(s[ii], p[jj]): break
@@ -55,36 +54,3 @@ proc prefixMatch*(p, s: string): PrefixMatch =
     else:
       return PrefixMatch.None
   return PrefixMatch.None
-
-when isMainModule:
-  import macros
-
-  macro check(val, body: untyped): untyped =
-    result = newStmtList()
-    expectKind body, nnkStmtList
-    for b in body:
-      expectKind b, nnkPar
-      expectLen b, 2
-      let p = b[0]
-      let s = b[1]
-      result.add quote do:
-        echo prefixMatch(`p`, `s`) == `val`
-
-  check PrefixMatch.Prefix:
-    ("abc", "abc")
-    ("a", "abc")
-    ("xyz", "X_yzzzZe")
-
-  check PrefixMatch.Substr:
-    ("b", "abc")
-    ("abc", "fooabcabc")
-    ("abC", "foo_AB_c")
-
-  check PrefixMatch.Abbrev:
-    ("abc", "AxxxBxxxCxxx")
-    ("xyz", "X_yabcZe")
-
-  check PrefixMatch.None:
-    ("foobar", "afkslfjd_as")
-    ("xyz", "X_yuuZuuZe")
-    ("ru", "remotes")
diff --git a/compiler/procfind.nim b/compiler/procfind.nim
index 3f47e7e8a..c2cc6e71f 100644
--- a/compiler/procfind.nim
+++ b/compiler/procfind.nim
@@ -11,60 +11,29 @@
 # This is needed for proper handling of forward declarations.
 
 import
-  ast, astalgo, msgs, semdata, types, trees, strutils
+  ast, astalgo, msgs, semdata, types, trees, lookups
+
+import std/strutils
 
 proc equalGenericParams(procA, procB: PNode): bool =
-  if sonsLen(procA) != sonsLen(procB): return false
-  for i in countup(0, sonsLen(procA) - 1):
-    if procA.sons[i].kind != nkSym:
+  if procA.len != procB.len: return false
+  for i in 0..<procA.len:
+    if procA[i].kind != nkSym:
       return false
-    if procB.sons[i].kind != nkSym:
+    if procB[i].kind != nkSym:
       return false
-    let a = procA.sons[i].sym
-    let b = procB.sons[i].sym
+    let a = procA[i].sym
+    let b = procB[i].sym
     if a.name.id != b.name.id or
         not sameTypeOrNil(a.typ, b.typ, {ExactTypeDescValues}): return
     if a.ast != nil and b.ast != nil:
       if not exprStructuralEquivalent(a.ast, b.ast): return
   result = true
 
-proc searchForProcOld*(c: PContext, scope: PScope, fn: PSym): PSym =
-  # Searchs for a forward declaration or a "twin" symbol of fn
-  # in the symbol table. If the parameter lists are exactly
-  # the same the sym in the symbol table is returned, else nil.
-  var it: TIdentIter
-  result = initIdentIter(it, scope.symbols, fn.name)
-  if isGenericRoutine(fn):
-    # we simply check the AST; this is imprecise but nearly the best what
-    # can be done; this doesn't work either though as type constraints are
-    # not kept in the AST ..
-    while result != nil:
-      if result.kind == fn.kind and isGenericRoutine(result):
-        let genR = result.ast.sons[genericParamsPos]
-        let genF = fn.ast.sons[genericParamsPos]
-        if exprStructuralEquivalent(genR, genF) and
-           exprStructuralEquivalent(result.ast.sons[paramsPos],
-                                    fn.ast.sons[paramsPos]) and
-           equalGenericParams(genR, genF):
-            return
-      result = nextIdentIter(it, scope.symbols)
-  else:
-    while result != nil:
-      if result.kind == fn.kind and not isGenericRoutine(result):
-        case equalParams(result.typ.n, fn.typ.n)
-        of paramsEqual:
-          return
-        of paramsIncompatible:
-          localError(c.config, fn.info, "overloaded '$1' leads to ambiguous calls" % fn.name.s)
-          return
-        of paramsNotEqual:
-          discard
-      result = nextIdentIter(it, scope.symbols)
-
-proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym =
+proc searchForProcAux(c: PContext, scope: PScope, fn: PSym): PSym =
   const flags = {ExactGenericParams, ExactTypeDescValues,
                  ExactConstraints, IgnoreCC}
-  var it: TIdentIter
+  var it: TIdentIter = default(TIdentIter)
   result = initIdentIter(it, scope.symbols, fn.name)
   while result != nil:
     if result.kind == fn.kind: #and sameType(result.typ, fn.typ, flags):
@@ -72,8 +41,8 @@ proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym =
       of paramsEqual:
         if (sfExported notin result.flags) and (sfExported in fn.flags):
           let message = ("public implementation '$1' has non-public " &
-                         "forward declaration in $2") %
-                        [getProcHeader(c.config, result), c.config$result.info]
+                         "forward declaration at $2") %
+                        [getProcHeader(c.config, result, getDeclarationPath = false), c.config$result.info]
           localError(c.config, fn.info, message)
         return
       of paramsIncompatible:
@@ -83,40 +52,37 @@ proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym =
         discard
     result = nextIdentIter(it, scope.symbols)
 
-proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym =
-  result = searchForProcNew(c, scope, fn)
-  when false:
-    let old = searchForProcOld(c, scope, fn)
-    if old != result:
-      echo "Mismatch in searchForProc: ", fn.info
-      debug fn.typ
-      debug if result != nil: result.typ else: nil
-      debug if old != nil: old.typ else: nil
+proc searchForProc*(c: PContext, scope: PScope, fn: PSym): tuple[proto: PSym, comesFromShadowScope: bool] =
+  var scope = scope
+  result = (searchForProcAux(c, scope, fn), false)
+  while result.proto == nil and scope.isShadowScope:
+    scope = scope.parent
+    result.proto = searchForProcAux(c, scope, fn)
+    result.comesFromShadowScope = true
 
 when false:
   proc paramsFitBorrow(child, parent: PNode): bool =
-    var length = sonsLen(child)
     result = false
-    if length == sonsLen(parent):
-      for i in countup(1, length - 1):
-        var m = child.sons[i].sym
-        var n = parent.sons[i].sym
+    if child.len == parent.len:
+      for i in 1..<child.len:
+        var m = child[i].sym
+        var n = parent[i].sym
         assert((m.kind == skParam) and (n.kind == skParam))
         if not compareTypes(m.typ, n.typ, dcEqOrDistinctOf): return
-      if not compareTypes(child.sons[0].typ, parent.sons[0].typ,
+      if not compareTypes(child[0].typ, parent[0].typ,
                           dcEqOrDistinctOf): return
       result = true
 
   proc searchForBorrowProc*(c: PContext, startScope: PScope, fn: PSym): PSym =
-    # Searchs for the fn in the symbol table. If the parameter lists are suitable
+    # Searches for the fn in the symbol table. If the parameter lists are suitable
     # for borrowing the sym in the symbol table is returned, else nil.
-    var it: TIdentIter
+    var it: TIdentIter = default(TIdentIter)
     for scope in walkScopes(startScope):
       result = initIdentIter(it, scope.symbols, fn.Name)
       while result != nil:
         # watchout! result must not be the same as fn!
         if (result.Kind == fn.kind) and (result.id != fn.id):
-          if equalGenericParams(result.ast.sons[genericParamsPos],
-                                fn.ast.sons[genericParamsPos]):
+          if equalGenericParams(result.ast[genericParamsPos],
+                                fn.ast[genericParamsPos]):
             if paramsFitBorrow(fn.typ.n, result.typ.n): return
         result = NextIdentIter(it, scope.symbols)
diff --git a/compiler/pushpoppragmas.nim b/compiler/pushpoppragmas.nim
new file mode 100644
index 000000000..773e7013b
--- /dev/null
+++ b/compiler/pushpoppragmas.nim
@@ -0,0 +1,54 @@
+import pragmas, options, ast, trees, lineinfos, idents, wordrecg
+import std/assertions
+
+import renderer
+
+
+proc processNote(config: ConfigRef, n: PNode) =
+  template handleNote(enumVals, notes) =
+    let x = findStr(enumVals.a, enumVals.b, n[0][1].ident.s, errUnknown)
+    assert x != errUnknown
+    assert n[1].kind == nkIntLit
+
+    nk = TNoteKind(x)
+    if n[1].intVal != 0: incl(notes, nk)
+    else: excl(notes, nk)
+
+  var nk: TNoteKind
+  case whichKeyword(n[0][0].ident)
+  of wHint: handleNote(hintMin .. hintMax, config.notes)
+  of wWarning: handleNote(warnMin .. warnMax, config.notes)
+  of wWarningAsError: handleNote(warnMin .. warnMax, config.warningAsErrors)
+  of wHintAsError: handleNote(hintMin .. hintMax, config.warningAsErrors)
+  else: discard
+
+proc pushBackendOption(optionsStack: var seq[(TOptions, TNoteKinds)], options: TOptions, notes: TNoteKinds) =
+  optionsStack.add (options, notes)
+
+proc popBackendOption(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions) =
+  let entry = optionsStack[^1]
+  options = entry[0]
+  config.notes = entry[1]
+  optionsStack.setLen(optionsStack.len-1)
+
+proc processPushBackendOption*(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions,
+                           n: PNode, start: int) =
+  pushBackendOption(optionsStack, options, config.notes)
+  for i in start..<n.len:
+    let it = n[i]
+    if it.kind in nkPragmaCallKinds and it.len == 2:
+      if it[0].kind == nkBracketExpr and
+          it[0].len == 2 and
+          it[0][1].kind == nkIdent and it[0][0].kind == nkIdent:
+        processNote(config, it)
+      elif it[1].kind == nkIntLit:
+        let sw = whichPragma(it[0])
+        let opts = pragmaToOptions(sw)
+        if opts != {}:
+          if it[1].intVal != 0:
+            options.incl opts
+          else:
+            options.excl opts
+
+template processPopBackendOption*(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions) =
+  popBackendOption(config, optionsStack, options)
diff --git a/compiler/readme.md b/compiler/readme.md
new file mode 100644
index 000000000..4a197991c
--- /dev/null
+++ b/compiler/readme.md
@@ -0,0 +1,7 @@
+## Nim Compiler
+
+- This directory contains the Nim compiler written in Nim.
+- Note that this code has been translated from a bootstrapping version written in Pascal.
+- So the code is **not** a poster child of good Nim code.
+
+See [Internals of the Nim Compiler](https://nim-lang.github.io/Nim/intern.html) for more information.
diff --git a/compiler/readme.txt b/compiler/readme.txt
deleted file mode 100644
index c4934e031..000000000
--- a/compiler/readme.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This directory contains the Nim compiler written in Nim. Note that this
-code has been translated from a bootstrapping version written in Pascal, so
-the code is **not** a poster child of good Nim code.
-
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index ba87838db..cc07c0c2d 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -9,22 +9,39 @@
 
 # This module implements the renderer of the standard Nim representation.
 
+# 'import renderer' is so useful for debugging
+# that Nim shouldn't produce a warning for that:
+{.used.}
+
 import
-  lexer, options, idents, strutils, ast, msgs, lineinfos
+  lexer, options, idents, ast, msgs, lineinfos, wordrecg
+
+import std/[strutils]
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions, formatfloat]
 
 type
   TRenderFlag* = enum
     renderNone, renderNoBody, renderNoComments, renderDocComments,
-    renderNoPragmas, renderIds, renderNoProcDefs
+    renderNoPragmas, renderIds, renderNoProcDefs, renderSyms, renderRunnableExamples,
+    renderIr, renderNonExportedFields, renderExpandUsing, renderNoPostfix
+
   TRenderFlags* = set[TRenderFlag]
   TRenderTok* = object
-    kind*: TTokType
+    kind*: TokType
     length*: int16
+    sym*: PSym
+
+  Section = enum
+    GenericParams
+    ObjectDef
 
   TRenderTokSeq* = seq[TRenderTok]
   TSrcGen* = object
     indent*: int
     lineLen*: int
+    col: int
     pos*: int              # current position for iteration over the buffer
     idx*: int              # current token index for iteration over the buffer
     tokens*: TRenderTokSeq
@@ -34,22 +51,45 @@ type
     pendingWhitespace: int
     comStack*: seq[PNode]  # comment stack
     flags*: TRenderFlags
-    inGenericParams: bool
+    inside: set[Section] # Keeps track of contexts we are in
     checkAnon: bool        # we're in a context that can contain sfAnon
     inPragma: int
     when defined(nimpretty):
       pendingNewlineCount: int
     fid*: FileIndex
     config*: ConfigRef
+    mangler: seq[PSym]
+
+proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string
 
 # We render the source code in a two phases: The first
 # determines how long the subtree will likely be, the second
 # phase appends to a buffer that will be the output.
 
+proc disamb(g: var TSrcGen; s: PSym): int =
+  # we group by 's.name.s' to compute the stable name ID.
+  result = 0
+  for i in 0 ..< g.mangler.len:
+    if s == g.mangler[i]: return result
+    if s.name.s == g.mangler[i].name.s: inc result
+  g.mangler.add s
+
 proc isKeyword*(i: PIdent): bool =
   if (i.id >= ord(tokKeywordLow) - ord(tkSymbol)) and
       (i.id <= ord(tokKeywordHigh) - ord(tkSymbol)):
     result = true
+  else:
+    result = false
+
+proc isExported(n: PNode): bool =
+  ## Checks if an ident is exported.
+  ## This is meant to be used with idents in nkIdentDefs.
+  case n.kind
+  of nkPostfix:
+    n[0].ident.s == "*" and n[1].kind == nkIdent
+  of nkPragmaExpr:
+    n[0].isExported()
+  else: false
 
 proc renderDefinitionName*(s: PSym, noQuotes = false): string =
   ## Returns the definition name of the symbol.
@@ -63,14 +103,32 @@ proc renderDefinitionName*(s: PSym, noQuotes = false): string =
   else:
     result = '`' & x & '`'
 
-when not defined(nimpretty):
-  const
-    IndentWidth = 2
-    longIndentWid = IndentWidth * 2
-else:
-  template IndentWidth: untyped = lexer.gIndentationWidth
-  template longIndentWid: untyped = IndentWidth() * 2
+template inside(g: var TSrcGen, section: Section, body: untyped) =
+  ## Runs `body` with `section` included in `g.inside`.
+  ## Removes it at the end of the body if `g` wasn't inside it
+  ## before the template.
+  let wasntInSection = section notin g.inside
+  g.inside.incl section
+  body
+  if wasntInSection:
+    g.inside.excl section
+
+template outside(g: var TSrcGen, section: Section, body: untyped) =
+  ## Temporarily removes `section` from `g.inside`. Adds it back
+  ## at the end of the body if `g` was inside it before the template
+  let wasInSection = section in g.inside
+  g.inside.excl section
+  body
+  if wasInSection:
+    g.inside.incl section
 
+const
+  IndentWidth = 2
+  longIndentWid = IndentWidth * 2
+  MaxLineLen = 80
+  LineCommentColumn = 30
+
+when defined(nimpretty):
   proc minmaxLine(n: PNode): (int, int) =
     case n.kind
     of nkTripleStrLit:
@@ -79,7 +137,7 @@ else:
       result = (n.info.line.int, n.info.line.int + countLines(n.comment))
     else:
       result = (n.info.line.int, n.info.line.int)
-    for i in 0 ..< safeLen(n):
+    for i in 0..<n.safeLen:
       let (currMin, currMax) = minmaxLine(n[i])
       if currMin < result[0]: result[0] = currMin
       if currMax > result[1]: result[1] = currMax
@@ -87,30 +145,19 @@ else:
   proc lineDiff(a, b: PNode): int =
     result = minmaxLine(b)[0] - minmaxLine(a)[1]
 
-const
-  MaxLineLen = 80
-  LineCommentColumn = 30
-
-proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags; config: ConfigRef) =
-  g.comStack = @[]
-  g.tokens = @[]
-  g.indent = 0
-  g.lineLen = 0
-  g.pos = 0
-  g.idx = 0
-  g.buf = ""
-  g.flags = renderFlags
-  g.pendingNL = -1
-  g.pendingWhitespace = -1
-  g.inGenericParams = false
-  g.config = config
-
-proc addTok(g: var TSrcGen, kind: TTokType, s: string) =
-  var length = len(g.tokens)
-  setLen(g.tokens, length + 1)
-  g.tokens[length].kind = kind
-  g.tokens[length].length = int16(len(s))
-  add(g.buf, s)
+proc initSrcGen(renderFlags: TRenderFlags; config: ConfigRef): TSrcGen =
+  result = TSrcGen(comStack: @[], tokens: @[], indent: 0,
+                   lineLen: 0, pos: 0, idx: 0, buf: "",
+                   flags: renderFlags, pendingNL: -1,
+                   pendingWhitespace: -1, inside: {},
+                   config: config
+                   )
+
+proc addTok(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) =
+  g.tokens.add TRenderTok(kind: kind, length: int16(s.len), sym: sym)
+  g.buf.add(s)
+  if kind != tkSpaces:
+    inc g.col, s.len
 
 proc addPendingNL(g: var TSrcGen) =
   if g.pendingNL >= 0:
@@ -120,6 +167,7 @@ proc addPendingNL(g: var TSrcGen) =
       const newlines = "\n"
     addTok(g, tkSpaces, newlines & spaces(g.pendingNL))
     g.lineLen = g.pendingNL
+    g.col = g.pendingNL
     g.pendingNL = - 1
     g.pendingWhitespace = -1
   elif g.pendingWhitespace >= 0:
@@ -128,7 +176,10 @@ proc addPendingNL(g: var TSrcGen) =
 
 proc putNL(g: var TSrcGen, indent: int) =
   if g.pendingNL >= 0: addPendingNL(g)
-  else: addTok(g, tkSpaces, "\n")
+  else:
+    addTok(g, tkSpaces, "\n")
+    g.col = 0
+
   g.pendingNL = indent
   g.lineLen = indent
   g.pendingWhitespace = -1
@@ -143,6 +194,7 @@ proc putNL(g: var TSrcGen) =
 proc optNL(g: var TSrcGen, indent: int) =
   g.pendingNL = indent
   g.lineLen = indent
+  g.col = g.indent
   when defined(nimpretty): g.pendingNewlineCount = 0
 
 proc optNL(g: var TSrcGen) =
@@ -151,6 +203,7 @@ proc optNL(g: var TSrcGen) =
 proc optNL(g: var TSrcGen; a, b: PNode) =
   g.pendingNL = g.indent
   g.lineLen = g.indent
+  g.col = g.indent
   when defined(nimpretty): g.pendingNewlineCount = lineDiff(a, b)
 
 proc indentNL(g: var TSrcGen) =
@@ -165,39 +218,40 @@ proc dedent(g: var TSrcGen) =
     dec(g.pendingNL, IndentWidth)
     dec(g.lineLen, IndentWidth)
 
-proc put(g: var TSrcGen, kind: TTokType, s: string) =
+proc put(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) =
   if kind != tkSpaces:
     addPendingNL(g)
-    if len(s) > 0:
-      addTok(g, kind, s)
-      inc(g.lineLen, len(s))
+    if s.len > 0 or kind in {tkHideableStart, tkHideableEnd}:
+      addTok(g, kind, s, sym)
   else:
     g.pendingWhitespace = s.len
+    inc g.col, s.len
+  inc(g.lineLen, s.len)
 
 proc putComment(g: var TSrcGen, s: string) =
-  if s.isNil: return
+  if s.len == 0: return
   var i = 0
-  let hi = len(s) - 1
-  var isCode = (len(s) >= 2) and (s[1] != ' ')
-  var ind = g.lineLen
+  let hi = s.len - 1
+  let isCode = (s.len >= 2) and (s[1] != ' ')
+  let ind = g.col
   var com = "## "
   while i <= hi:
     case s[i]
     of '\0':
       break
-    of '\x0D':
+    of '\r':
       put(g, tkComment, com)
       com = "## "
       inc(i)
-      if i < s.len and s[i] == '\x0A': inc(i)
+      if i <= hi and s[i] == '\n': inc(i)
       optNL(g, ind)
-    of '\x0A':
+    of '\n':
       put(g, tkComment, com)
       com = "## "
       inc(i)
       optNL(g, ind)
-    of ' ', '\x09':
-      add(com, s[i])
+    of ' ', '\t':
+      com.add(s[i])
       inc(i)
     else:
       # we may break the comment into a multi-line comment if the line
@@ -205,31 +259,32 @@ proc putComment(g: var TSrcGen, s: string) =
       # compute length of the following word:
       var j = i
       while j <= hi and s[j] > ' ': inc(j)
-      if not isCode and (g.lineLen + (j - i) > MaxLineLen):
+      if not isCode and (g.col + (j - i) > MaxLineLen):
         put(g, tkComment, com)
         optNL(g, ind)
         com = "## "
       while i <= hi and s[i] > ' ':
-        add(com, s[i])
+        com.add(s[i])
         inc(i)
   put(g, tkComment, com)
   optNL(g)
 
 proc maxLineLength(s: string): int =
-  if s.isNil: return 0
+  result = 0
+  if s.len == 0: return 0
   var i = 0
-  let hi = len(s) - 1
+  let hi = s.len - 1
   var lineLen = 0
   while i <= hi:
     case s[i]
     of '\0':
       break
-    of '\x0D':
+    of '\r':
       inc(i)
-      if s[i] == '\x0A': inc(i)
+      if i <= hi and s[i] == '\n': inc(i)
       result = max(result, lineLen)
       lineLen = 0
-    of '\x0A':
+    of '\n':
       inc(i)
       result = max(result, lineLen)
       lineLen = 0
@@ -237,41 +292,40 @@ proc maxLineLength(s: string): int =
       inc(lineLen)
       inc(i)
 
-proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) =
+proc putRawStr(g: var TSrcGen, kind: TokType, s: string) =
   var i = 0
-  let hi = len(s) - 1
+  let hi = s.len - 1
   var str = ""
   while i <= hi:
     case s[i]
-    of '\x0D':
+    of '\r':
       put(g, kind, str)
       str = ""
       inc(i)
-      if (i <= hi) and (s[i] == '\x0A'): inc(i)
+      if i <= hi and s[i] == '\n': inc(i)
       optNL(g, 0)
-    of '\x0A':
+    of '\n':
       put(g, kind, str)
       str = ""
       inc(i)
       optNL(g, 0)
     else:
-      add(str, s[i])
+      str.add(s[i])
       inc(i)
   put(g, kind, str)
 
 proc containsNL(s: string): bool =
-  for i in countup(0, len(s) - 1):
+  for i in 0..<s.len:
     case s[i]
-    of '\x0D', '\x0A':
+    of '\r', '\n':
       return true
     else:
       discard
   result = false
 
 proc pushCom(g: var TSrcGen, n: PNode) =
-  var length = len(g.comStack)
-  setLen(g.comStack, length + 1)
-  g.comStack[length] = n
+  setLen(g.comStack, g.comStack.len + 1)
+  g.comStack[^1] = n
 
 proc popAllComs(g: var TSrcGen) =
   setLen(g.comStack, 0)
@@ -279,45 +333,58 @@ proc popAllComs(g: var TSrcGen) =
 const
   Space = " "
 
-proc shouldRenderComment(g: var TSrcGen, n: PNode): bool =
-  result = false
-  if n.comment != nil:
-    result = (renderNoComments notin g.flags) or
-        (renderDocComments in g.flags)
+proc shouldRenderComment(g: TSrcGen): bool {.inline.} =
+  (renderNoComments notin g.flags or renderDocComments in g.flags)
+
+proc shouldRenderComment(g: TSrcGen, n: PNode): bool {.inline.} =
+  shouldRenderComment(g) and n.comment.len > 0
 
 proc gcom(g: var TSrcGen, n: PNode) =
   assert(n != nil)
   if shouldRenderComment(g, n):
-    if (g.pendingNL < 0) and (len(g.buf) > 0) and (g.buf[len(g.buf)-1] != ' '):
+    var oneSpaceAdded = 0
+    if (g.pendingNL < 0) and (g.buf.len > 0) and (g.buf[^1] != ' '):
       put(g, tkSpaces, Space)
+      oneSpaceAdded = 1
       # Before long comments we cannot make sure that a newline is generated,
       # because this might be wrong. But it is no problem in practice.
-    if (g.pendingNL < 0) and (len(g.buf) > 0) and
-        (g.lineLen < LineCommentColumn):
+    if (g.pendingNL < 0) and (g.buf.len > 0) and
+        (g.col < LineCommentColumn):
       var ml = maxLineLength(n.comment)
       if ml + LineCommentColumn <= MaxLineLen:
-        put(g, tkSpaces, spaces(LineCommentColumn - g.lineLen))
+        put(g, tkSpaces, spaces(LineCommentColumn - g.col))
+        dec g.col, oneSpaceAdded
     putComment(g, n.comment)  #assert(g.comStack[high(g.comStack)] = n);
 
 proc gcoms(g: var TSrcGen) =
-  for i in countup(0, high(g.comStack)): gcom(g, g.comStack[i])
+  for i in 0..high(g.comStack): gcom(g, g.comStack[i])
   popAllComs(g)
 
 proc lsub(g: TSrcGen; n: PNode): int
 proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
   proc skip(t: PType): PType =
     result = t
-    while result.kind in {tyGenericInst, tyRange, tyVar, tyLent, tyDistinct,
-                          tyOrdinal, tyAlias, tySink}:
-      result = lastSon(result)
-  if n.typ != nil and n.typ.skip.kind in {tyBool, tyEnum}:
-    let enumfields = n.typ.skip.n
+    while result != nil and result.kind in {tyGenericInst, tyRange, tyVar,
+                          tyLent, tyDistinct, tyOrdinal, tyAlias, tySink}:
+      result = skipModifier(result)
+
+  result = ""
+  let typ = n.typ.skip
+  if typ != nil and typ.kind in {tyBool, tyEnum}:
+    if sfPure in typ.sym.flags:
+      result = typ.sym.name.s & '.'
+    let enumfields = typ.n
     # we need a slow linear search because of enums with holes:
     for e in items(enumfields):
-      if e.sym.position == x: return e.sym.name.s
+      if e.sym.position == x:
+        result &= e.sym.name.s
+        return
 
   if nfBase2 in n.flags: result = "0b" & toBin(x, size * 8)
-  elif nfBase8 in n.flags: result = "0o" & toOct(x, size * 3)
+  elif nfBase8 in n.flags:
+    var y = if size < sizeof(BiggestInt): x and ((1.BiggestInt shl (size*8)) - 1)
+            else: x
+    result = "0o" & toOct(y, size * 3)
   elif nfBase16 in n.flags: result = "0x" & toHex(x, size * 2)
   else: result = $x
 
@@ -325,8 +392,7 @@ proc ulitAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
   if nfBase2 in n.flags: result = "0b" & toBin(x, size * 8)
   elif nfBase8 in n.flags: result = "0o" & toOct(x, size * 3)
   elif nfBase16 in n.flags: result = "0x" & toHex(x, size * 2)
-  else: result = $x
-  # XXX proper unsigned output!
+  else: result = $cast[BiggestUInt](x)
 
 proc atom(g: TSrcGen; n: PNode): string =
   when defined(nimpretty):
@@ -344,6 +410,7 @@ proc atom(g: TSrcGen; n: PNode): string =
   of nkEmpty: result = ""
   of nkIdent: result = n.ident.s
   of nkSym: result = n.sym.name.s
+  of nkClosedSymChoice, nkOpenSymChoice: result = n[0].sym.name.s
   of nkStrLit: result = ""; result.addQuoted(n.strVal)
   of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"'
   of nkTripleStrLit: result = "\"\"\"" & n.strVal & "\"\"\""
@@ -363,59 +430,85 @@ proc atom(g: TSrcGen; n: PNode): string =
   of nkUInt64Lit: result = ulitAux(g, n, n.intVal, 8) & "\'u64"
   of nkFloatLit:
     if n.flags * {nfBase2, nfBase8, nfBase16} == {}: result = $(n.floatVal)
-    else: result = litAux(g, n, (cast[PInt64](addr(n.floatVal)))[] , 8)
+    else: result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[] , 8)
   of nkFloat32Lit:
     if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
       result = $n.floatVal & "\'f32"
     else:
       f = n.floatVal.float32
-      result = litAux(g, n, (cast[PInt32](addr(f)))[], 4) & "\'f32"
+      result = litAux(g, n, (cast[ptr int32](addr(f)))[], 4) & "\'f32"
   of nkFloat64Lit:
     if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
       result = $n.floatVal & "\'f64"
     else:
-      result = litAux(g, n, (cast[PInt64](addr(n.floatVal)))[], 8) & "\'f64"
+      result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f64"
+  of nkFloat128Lit:
+    if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
+      result = $n.floatVal & "\'f128"
+    else:
+      result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f128"
   of nkNilLit: result = "nil"
   of nkType:
     if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s
     else: result = "[type node]"
   else:
-    internalError(g.config, "rnimsyn.atom " & $n.kind)
+    internalError(g.config, "renderer.atom " & $n.kind)
     result = ""
 
 proc lcomma(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
   assert(theEnd < 0)
   result = 0
-  for i in countup(start, sonsLen(n) + theEnd):
-    inc(result, lsub(g, n.sons[i]))
-    inc(result, 2)            # for ``, ``
+  for i in start..n.len + theEnd:
+    let param = n[i]
+    if nfDefaultParam notin param.flags:
+      inc(result, lsub(g, param))
+      inc(result, 2)          # for ``, ``
   if result > 0:
     dec(result, 2)            # last does not get a comma!
 
 proc lsons(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
   assert(theEnd < 0)
   result = 0
-  for i in countup(start, sonsLen(n) + theEnd): inc(result, lsub(g, n.sons[i]))
+  for i in start..n.len + theEnd: inc(result, lsub(g, n[i]))
+
+proc origUsingType(n: PNode): PSym {.inline.} =
+  ## Returns the type that a parameter references. Check with referencesUsing first
+  ## to check `n` is actually referencing a using node
+  # If the node is untyped the typ field will be nil
+  if n[0].sym.typ != nil:
+    n[0].sym.typ.sym
+  else: nil
+
+proc referencesUsing(n: PNode): bool =
+  ## Returns true if n references a using statement.
+  ## e.g. proc foo(x) # x doesn't have type or def value so it references a using
+  result = n.kind == nkIdentDefs and
+           # Sometimes the node might not have been semmed (e.g. doc0) and will be nkIdent instead
+           n[0].kind == nkSym and
+           # Templates/macros can have parameters with no type (But their orig type will be nil)
+           n.origUsingType != nil and
+           n[1].kind == nkEmpty and n[2].kind == nkEmpty
 
 proc lsub(g: TSrcGen; n: PNode): int =
   # computes the length of a tree
+  result = 0
   if isNil(n): return 0
-  if n.comment != nil: return MaxLineLen + 1
+  if shouldRenderComment(g, n): return MaxLineLen + 1
   case n.kind
   of nkEmpty: result = 0
   of nkTripleStrLit:
     if containsNL(n.strVal): result = MaxLineLen + 1
-    else: result = len(atom(g, n))
+    else: result = atom(g, n).len
   of succ(nkEmpty)..pred(nkTripleStrLit), succ(nkTripleStrLit)..nkNilLit:
-    result = len(atom(g, n))
+    result = atom(g, n).len
   of nkCall, nkBracketExpr, nkCurlyExpr, nkConv, nkPattern, nkObjConstr:
-    result = lsub(g, n.sons[0]) + lcomma(g, n, 1) + 2
+    result = lsub(g, n[0]) + lcomma(g, n, 1) + 2
   of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: result = lsub(g, n[1])
-  of nkCast: result = lsub(g, n.sons[0]) + lsub(g, n.sons[1]) + len("cast[]()")
-  of nkAddr: result = (if n.len>0: lsub(g, n.sons[0]) + len("addr()") else: 4)
-  of nkStaticExpr: result = lsub(g, n.sons[0]) + len("static_")
-  of nkHiddenAddr, nkHiddenDeref: result = lsub(g, n.sons[0])
-  of nkCommand: result = lsub(g, n.sons[0]) + lcomma(g, n, 1) + 1
+  of nkCast: result = lsub(g, n[0]) + lsub(g, n[1]) + len("cast[]()")
+  of nkAddr: result = (if n.len>0: lsub(g, n[0]) + len("addr()") else: 4)
+  of nkStaticExpr: result = lsub(g, n[0]) + len("static_")
+  of nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString: result = lsub(g, n[0])
+  of nkCommand: result = lsub(g, n[0]) + lcomma(g, n, 1) + 1
   of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(g, n) + 3
   of nkPar, nkCurly, nkBracket, nkClosure: result = lcomma(g, n) + 2
   of nkTupleConstr:
@@ -425,95 +518,112 @@ proc lsub(g: TSrcGen; n: PNode): int =
   of nkTableConstr:
     result = if n.len > 0: lcomma(g, n) + 2 else: len("{:}")
   of nkClosedSymChoice, nkOpenSymChoice:
-    result = lsons(g, n) + len("()") + sonsLen(n) - 1
+    if n.len > 0: result += lsub(g, n[0])
+  of nkOpenSym: result = lsub(g, n[0])
   of nkTupleTy: result = lcomma(g, n) + len("tuple[]")
   of nkTupleClassTy: result = len("tuple")
   of nkDotExpr: result = lsons(g, n) + 1
   of nkBind: result = lsons(g, n) + len("bind_")
   of nkBindStmt: result = lcomma(g, n) + len("bind_")
   of nkMixinStmt: result = lcomma(g, n) + len("mixin_")
-  of nkCheckedFieldExpr: result = lsub(g, n.sons[0])
+  of nkCheckedFieldExpr: result = lsub(g, n[0])
   of nkLambda: result = lsons(g, n) + len("proc__=_")
   of nkDo: result = lsons(g, n) + len("do__:_")
   of nkConstDef, nkIdentDefs:
     result = lcomma(g, n, 0, - 3)
-    var L = sonsLen(n)
-    if n.sons[L - 2].kind != nkEmpty: result = result + lsub(g, n.sons[L - 2]) + 2
-    if n.sons[L - 1].kind != nkEmpty: result = result + lsub(g, n.sons[L - 1]) + 3
-  of nkVarTuple: result = lcomma(g, n, 0, - 3) + len("() = ") + lsub(g, lastSon(n))
+    if n.referencesUsing:
+      result += lsub(g, newSymNode(n.origUsingType)) + 2
+    else:
+      if n[^2].kind != nkEmpty: result += lsub(g, n[^2]) + 2
+      if n[^1].kind != nkEmpty: result += lsub(g, n[^1]) + 3
+  of nkVarTuple:
+    if n[^1].kind == nkEmpty:
+      result = lcomma(g, n, 0, - 2) + len("()")
+    else:
+      result = lcomma(g, n, 0, - 3) + len("() = ") + lsub(g, lastSon(n))
   of nkChckRangeF: result = len("chckRangeF") + 2 + lcomma(g, n)
   of nkChckRange64: result = len("chckRange64") + 2 + lcomma(g, n)
   of nkChckRange: result = len("chckRange") + 2 + lcomma(g, n)
-  of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
+  of nkObjDownConv, nkObjUpConv:
     result = 2
-    if sonsLen(n) >= 1: result = result + lsub(g, n.sons[0])
-    result = result + lcomma(g, n, 1)
+    if n.len >= 1: result += lsub(g, n[0])
+    result += lcomma(g, n, 1)
   of nkExprColonExpr: result = lsons(g, n) + 2
   of nkInfix: result = lsons(g, n) + 2
   of nkPrefix:
-    result = lsons(g, n)+1+(if n.len > 0 and n.sons[1].kind == nkInfix: 2 else: 0)
-  of nkPostfix: result = lsons(g, n)
+    result = lsons(g, n)+1+(if n.len > 0 and n[1].kind == nkInfix: 2 else: 0)
+  of nkPostfix:
+    if renderNoPostfix notin g.flags:
+      result = lsons(g, n)
+    else:
+      result = lsub(g, n[1])
   of nkCallStrLit: result = lsons(g, n)
-  of nkPragmaExpr: result = lsub(g, n.sons[0]) + lcomma(g, n, 1)
+  of nkPragmaExpr: result = lsub(g, n[0]) + lcomma(g, n, 1)
   of nkRange: result = lsons(g, n) + 2
-  of nkDerefExpr: result = lsub(g, n.sons[0]) + 2
+  of nkDerefExpr: result = lsub(g, n[0]) + 2
   of nkAccQuoted: result = lsons(g, n) + 2
   of nkIfExpr:
-    result = lsub(g, n.sons[0].sons[0]) + lsub(g, n.sons[0].sons[1]) + lsons(g, n, 1) +
+    result = lsub(g, n[0][0]) + lsub(g, n[0][1]) + lsons(g, n, 1) +
         len("if_:_")
   of nkElifExpr: result = lsons(g, n) + len("_elif_:_")
-  of nkElseExpr: result = lsub(g, n.sons[0]) + len("_else:_") # type descriptions
-  of nkTypeOfExpr: result = (if n.len > 0: lsub(g, n.sons[0]) else: 0)+len("type()")
-  of nkRefTy: result = (if n.len > 0: lsub(g, n.sons[0])+1 else: 0) + len("ref")
-  of nkPtrTy: result = (if n.len > 0: lsub(g, n.sons[0])+1 else: 0) + len("ptr")
-  of nkVarTy: result = (if n.len > 0: lsub(g, n.sons[0])+1 else: 0) + len("var")
+  of nkElseExpr: result = lsub(g, n[0]) + len("_else:_") # type descriptions
+  of nkTypeOfExpr: result = (if n.len > 0: lsub(g, n[0]) else: 0)+len("typeof()")
+  of nkRefTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("ref")
+  of nkPtrTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("ptr")
+  of nkVarTy, nkOutTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("var")
   of nkDistinctTy:
-    result = len("distinct") + (if n.len > 0: lsub(g, n.sons[0])+1 else: 0)
+    result = len("distinct") + (if n.len > 0: lsub(g, n[0])+1 else: 0)
     if n.len > 1:
       result += (if n[1].kind == nkWith: len("_with_") else: len("_without_"))
       result += lcomma(g, n[1])
-  of nkStaticTy: result = (if n.len > 0: lsub(g, n.sons[0]) else: 0) +
+  of nkStaticTy: result = (if n.len > 0: lsub(g, n[0]) else: 0) +
                                                          len("static[]")
   of nkTypeDef: result = lsons(g, n) + 3
-  of nkOfInherit: result = lsub(g, n.sons[0]) + len("of_")
+  of nkOfInherit: result = lsub(g, n[0]) + len("of_")
   of nkProcTy: result = lsons(g, n) + len("proc_")
   of nkIteratorTy: result = lsons(g, n) + len("iterator_")
-  of nkSharedTy: result = lsons(g, n) + len("shared_")
+  of nkSinkAsgn: result = lsons(g, n) + len("`=sink`(, )")
   of nkEnumTy:
-    if sonsLen(n) > 0:
-      result = lsub(g, n.sons[0]) + lcomma(g, n, 1) + len("enum_")
+    if n.len > 0:
+      result = lsub(g, n[0]) + lcomma(g, n, 1) + len("enum_")
     else:
       result = len("enum")
   of nkEnumFieldDef: result = lsons(g, n) + 3
   of nkVarSection, nkLetSection:
-    if sonsLen(n) > 1: result = MaxLineLen + 1
+    if n.len > 1: result = MaxLineLen + 1
     else: result = lsons(g, n) + len("var_")
   of nkUsingStmt:
-    if sonsLen(n) > 1: result = MaxLineLen + 1
+    if n.len > 1: result = MaxLineLen + 1
     else: result = lsons(g, n) + len("using_")
-  of nkReturnStmt: result = lsub(g, n.sons[0]) + len("return_")
-  of nkRaiseStmt: result = lsub(g, n.sons[0]) + len("raise_")
-  of nkYieldStmt: result = lsub(g, n.sons[0]) + len("yield_")
-  of nkDiscardStmt: result = lsub(g, n.sons[0]) + len("discard_")
-  of nkBreakStmt: result = lsub(g, n.sons[0]) + len("break_")
-  of nkContinueStmt: result = lsub(g, n.sons[0]) + len("continue_")
+  of nkReturnStmt:
+    if n.len > 0 and n[0].kind == nkAsgn and renderIr notin g.flags:
+      result = len("return_") + lsub(g, n[0][1])
+    else:
+      result = len("return_") + lsub(g, n[0])
+  of nkRaiseStmt: result = lsub(g, n[0]) + len("raise_")
+  of nkYieldStmt: result = lsub(g, n[0]) + len("yield_")
+  of nkDiscardStmt: result = lsub(g, n[0]) + len("discard_")
+  of nkBreakStmt: result = lsub(g, n[0]) + len("break_")
+  of nkContinueStmt: result = lsub(g, n[0]) + len("continue_")
   of nkPragma: result = lcomma(g, n) + 4
-  of nkCommentStmt: result = if n.comment.isNil: 0 else: len(n.comment)
+  of nkCommentStmt: result = n.comment.len
   of nkOfBranch: result = lcomma(g, n, 0, - 2) + lsub(g, lastSon(n)) + len("of_:_")
-  of nkImportAs: result = lsub(g, n.sons[0]) + len("_as_") + lsub(g, n.sons[1])
+  of nkImportAs: result = lsub(g, n[0]) + len("_as_") + lsub(g, n[1])
   of nkElifBranch: result = lsons(g, n) + len("elif_:_")
-  of nkElse: result = lsub(g, n.sons[0]) + len("else:_")
-  of nkFinally: result = lsub(g, n.sons[0]) + len("finally:_")
+  of nkElse: result = lsub(g, n[0]) + len("else:_")
+  of nkFinally: result = lsub(g, n[0]) + len("finally:_")
   of nkGenericParams: result = lcomma(g, n) + 2
   of nkFormalParams:
     result = lcomma(g, n, 1) + 2
-    if n.sons[0].kind != nkEmpty: result = result + lsub(g, n.sons[0]) + 2
+    if n[0].kind != nkEmpty: result += lsub(g, n[0]) + 2
   of nkExceptBranch:
     result = lcomma(g, n, 0, -2) + lsub(g, lastSon(n)) + len("except_:_")
+  of nkObjectTy:
+    result = len("object_")
   else: result = MaxLineLen + 1
 
 proc fits(g: TSrcGen, x: int): bool =
-  result = x + g.lineLen <= MaxLineLen
+  result = x <= MaxLineLen
 
 type
   TSubFlag = enum
@@ -524,47 +634,67 @@ type
 const
   emptyContext: TContext = (spacing: 0, flags: {})
 
-proc initContext(c: var TContext) =
-  c.spacing = 0
-  c.flags = {}
+proc initContext(): TContext =
+  result = (spacing: 0, flags: {})
 
-proc gsub(g: var TSrcGen, n: PNode, c: TContext)
-proc gsub(g: var TSrcGen, n: PNode) =
-  var c: TContext
-  initContext(c)
-  gsub(g, n, c)
+proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false)
+proc gsub(g: var TSrcGen, n: PNode, fromStmtList = false) =
+  var c: TContext = initContext()
+  gsub(g, n, c, fromStmtList = fromStmtList)
 
 proc hasCom(n: PNode): bool =
   result = false
   if n.isNil: return false
-  if n.comment != nil: return true
+  if n.comment.len > 0: return true
   case n.kind
   of nkEmpty..nkNilLit: discard
   else:
-    for i in countup(0, sonsLen(n) - 1):
-      if hasCom(n.sons[i]): return true
+    for i in 0..<n.len:
+      if hasCom(n[i]): return true
 
-proc putWithSpace(g: var TSrcGen, kind: TTokType, s: string) =
+proc putWithSpace(g: var TSrcGen, kind: TokType, s: string) =
   put(g, kind, s)
   put(g, tkSpaces, Space)
 
+proc isHideable(config: ConfigRef, n: PNode): bool =
+  # xxx compare `ident` directly with `getIdent(cache, wRaises)`, but
+  # this requires a `cache`.
+  case n.kind
+  of nkExprColonExpr:
+    result = n[0].kind == nkIdent and
+             n[0].ident.s.nimIdentNormalize in ["raises", "tags", "extern", "deprecated", "forbids", "stacktrace"]
+  of nkIdent: result = n.ident.s in ["gcsafe", "deprecated"]
+  else: result = false
+
 proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0,
                theEnd: int = - 1, separator = tkComma) =
-  for i in countup(start, sonsLen(n) + theEnd):
-    var c = i < sonsLen(n) + theEnd
-    var sublen = lsub(g, n.sons[i]) + ord(c)
-    if not fits(g, sublen) and (ind + sublen < MaxLineLen): optNL(g, ind)
+  let inPragma = g.inPragma == 1 # just the top-level
+  var inHideable = false
+  for i in start..n.len + theEnd:
+    let c = i < n.len + theEnd
+    let sublen = lsub(g, n[i]) + ord(c)
+    if not fits(g, g.lineLen + sublen) and (ind + sublen < MaxLineLen): optNL(g, ind)
     let oldLen = g.tokens.len
-    gsub(g, n.sons[i])
+    if inPragma:
+      if not inHideable and isHideable(g.config, n[i]):
+        inHideable = true
+        put(g, tkHideableStart, "")
+      elif inHideable and not isHideable(g.config, n[i]):
+        inHideable = false
+        put(g, tkHideableEnd, "")
+    gsub(g, n[i])
     if c:
       if g.tokens.len > oldLen:
-        putWithSpace(g, separator, TokTypeToStr[separator])
-      if hasCom(n.sons[i]):
+        putWithSpace(g, separator, $separator)
+      if shouldRenderComment(g) and hasCom(n[i]):
         gcoms(g)
         optNL(g, ind)
+  if inHideable:
+    put(g, tkHideableEnd, "")
+    inHideable = false
 
 proc gcomma(g: var TSrcGen, n: PNode, c: TContext, start: int = 0,
-            theEnd: int = - 1) =
+            theEnd: int = -1) =
   var ind: int
   if rfInConstExpr in c.flags:
     ind = g.indent + IndentWidth
@@ -585,26 +715,26 @@ proc gsemicolon(g: var TSrcGen, n: PNode, start: int = 0, theEnd: int = - 1) =
 
 proc gsons(g: var TSrcGen, n: PNode, c: TContext, start: int = 0,
            theEnd: int = - 1) =
-  for i in countup(start, sonsLen(n) + theEnd): gsub(g, n.sons[i], c)
+  for i in start..n.len + theEnd: gsub(g, n[i], c)
 
-proc gsection(g: var TSrcGen, n: PNode, c: TContext, kind: TTokType,
+proc gsection(g: var TSrcGen, n: PNode, c: TContext, kind: TokType,
               k: string) =
-  if sonsLen(n) == 0: return # empty var sections are possible
+  if n.len == 0: return # empty var sections are possible
   putWithSpace(g, kind, k)
   gcoms(g)
   indentNL(g)
-  for i in countup(0, sonsLen(n) - 1):
+  for i in 0..<n.len:
     optNL(g)
-    gsub(g, n.sons[i], c)
+    gsub(g, n[i], c)
     gcoms(g)
   dedent(g)
 
 proc longMode(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): bool =
-  result = n.comment != nil
+  result = shouldRenderComment(g, n)
   if not result:
     # check further
-    for i in countup(start, sonsLen(n) + theEnd):
-      if (lsub(g, n.sons[i]) > MaxLineLen):
+    for i in start..n.len + theEnd:
+      if (lsub(g, n[i]) > MaxLineLen):
         result = true
         break
 
@@ -612,8 +742,7 @@ proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) =
   if n.kind == nkEmpty: return
   if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
     if doIndent: indentNL(g)
-    let L = n.len
-    for i in 0 .. L-1:
+    for i in 0..<n.len:
       if i > 0:
         optNL(g, n[i-1], n[i])
       else:
@@ -621,137 +750,138 @@ proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) =
       if n[i].kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
         gstmts(g, n[i], c, doIndent=false)
       else:
-        gsub(g, n[i])
+        gsub(g, n[i], fromStmtList = true)
       gcoms(g)
     if doIndent: dedent(g)
   else:
-    if rfLongMode in c.flags: indentNL(g)
+    indentNL(g)
     gsub(g, n)
     gcoms(g)
+    dedent(g)
     optNL(g)
-    if rfLongMode in c.flags: dedent(g)
+
+
+proc gcond(g: var TSrcGen, n: PNode) =
+  if n.kind == nkStmtListExpr:
+    put(g, tkParLe, "(")
+  gsub(g, n)
+  if n.kind == nkStmtListExpr:
+    put(g, tkParRi, ")")
 
 proc gif(g: var TSrcGen, n: PNode) =
-  var c: TContext
-  gsub(g, n.sons[0].sons[0])
-  initContext(c)
+  var c: TContext = initContext()
+  gcond(g, n[0][0])
   putWithSpace(g, tkColon, ":")
-  if longMode(g, n) or (lsub(g, n.sons[0].sons[1]) + g.lineLen > MaxLineLen):
+  if longMode(g, n) or (lsub(g, n[0][1]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)                    # a good place for comments
-  gstmts(g, n.sons[0].sons[1], c)
-  var length = sonsLen(n)
-  for i in countup(1, length - 1):
+  gstmts(g, n[0][1], c)
+  for i in 1..<n.len:
     optNL(g)
-    gsub(g, n.sons[i], c)
+    gsub(g, n[i], c)
 
 proc gwhile(g: var TSrcGen, n: PNode) =
-  var c: TContext
+  var c: TContext = initContext()
   putWithSpace(g, tkWhile, "while")
-  gsub(g, n.sons[0])
+  gcond(g, n[0])
   putWithSpace(g, tkColon, ":")
-  initContext(c)
-  if longMode(g, n) or (lsub(g, n.sons[1]) + g.lineLen > MaxLineLen):
+  if longMode(g, n) or (lsub(g, n[1]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)                    # a good place for comments
-  gstmts(g, n.sons[1], c)
+  gstmts(g, n[1], c)
 
 proc gpattern(g: var TSrcGen, n: PNode) =
-  var c: TContext
+  var c: TContext = initContext()
   put(g, tkCurlyLe, "{")
-  initContext(c)
-  if longMode(g, n) or (lsub(g, n.sons[0]) + g.lineLen > MaxLineLen):
+  if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)                    # a good place for comments
   gstmts(g, n, c)
   put(g, tkCurlyRi, "}")
 
 proc gpragmaBlock(g: var TSrcGen, n: PNode) =
-  var c: TContext
-  gsub(g, n.sons[0])
+  var c: TContext = initContext()
+  gsub(g, n[0])
   putWithSpace(g, tkColon, ":")
-  initContext(c)
-  if longMode(g, n) or (lsub(g, n.sons[1]) + g.lineLen > MaxLineLen):
+  if longMode(g, n) or (lsub(g, n[1]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)                    # a good place for comments
-  gstmts(g, n.sons[1], c)
+  gstmts(g, n[1], c)
 
 proc gtry(g: var TSrcGen, n: PNode) =
-  var c: TContext
+  var c: TContext = initContext()
   put(g, tkTry, "try")
   putWithSpace(g, tkColon, ":")
-  initContext(c)
-  if longMode(g, n) or (lsub(g, n.sons[0]) + g.lineLen > MaxLineLen):
+  if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)                    # a good place for comments
-  gstmts(g, n.sons[0], c)
+  gstmts(g, n[0], c)
   gsons(g, n, c, 1)
 
 proc gfor(g: var TSrcGen, n: PNode) =
-  var c: TContext
-  var length = sonsLen(n)
+  var c: TContext = initContext()
   putWithSpace(g, tkFor, "for")
-  initContext(c)
   if longMode(g, n) or
-      (lsub(g, n.sons[length - 1]) + lsub(g, n.sons[length - 2]) + 6 + g.lineLen >
-      MaxLineLen):
+      (lsub(g, n[^1]) + lsub(g, n[^2]) + 6 + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcomma(g, n, c, 0, - 3)
   put(g, tkSpaces, Space)
   putWithSpace(g, tkIn, "in")
-  gsub(g, n.sons[length - 2], c)
+  gsub(g, n[^2], c)
   putWithSpace(g, tkColon, ":")
   gcoms(g)
-  gstmts(g, n.sons[length - 1], c)
+  gstmts(g, n[^1], c)
 
 proc gcase(g: var TSrcGen, n: PNode) =
-  var c: TContext
-  initContext(c)
-  var length = sonsLen(n)
-  if length == 0: return
-  var last = if n.sons[length-1].kind == nkElse: -2 else: -1
+  var c: TContext = initContext()
+  if n.len == 0: return
+  var last = if n[^1].kind == nkElse: -2 else: -1
   if longMode(g, n, 0, last): incl(c.flags, rfLongMode)
   putWithSpace(g, tkCase, "case")
-  gsub(g, n.sons[0])
+  gcond(g, n[0])
   gcoms(g)
   optNL(g)
   gsons(g, n, c, 1, last)
   if last == - 2:
-    initContext(c)
-    if longMode(g, n.sons[length - 1]): incl(c.flags, rfLongMode)
-    gsub(g, n.sons[length - 1], c)
+    c = initContext()
+    if longMode(g, n[^1]): incl(c.flags, rfLongMode)
+    gsub(g, n[^1], c)
+
+proc genSymSuffix(result: var string, s: PSym) {.inline.} =
+  if sfGenSym in s.flags and s.name.id != ord(wUnderscore):
+    result.add '_'
+    result.addInt s.id
 
 proc gproc(g: var TSrcGen, n: PNode) =
-  var c: TContext
-  if n.sons[namePos].kind == nkSym:
-    let s = n.sons[namePos].sym
-    put(g, tkSymbol, renderDefinitionName(s))
-    if sfGenSym in s.flags:
-      put(g, tkIntLit, $s.id)
-  else:
-    gsub(g, n.sons[namePos])
-
-  if n.sons[patternPos].kind != nkEmpty:
-    gpattern(g, n.sons[patternPos])
-  let oldInGenericParams = g.inGenericParams
-  g.inGenericParams = true
-  if renderNoBody in g.flags and n[miscPos].kind != nkEmpty and
-      n[miscPos][1].kind != nkEmpty:
-    gsub(g, n[miscPos][1])
+  var c: TContext = initContext()
+  if n[namePos].kind == nkSym:
+    let s = n[namePos].sym
+    var ret = renderDefinitionName(s)
+    ret.genSymSuffix(s)
+    put(g, tkSymbol, ret)
   else:
-    gsub(g, n.sons[genericParamsPos])
-  g.inGenericParams = oldInGenericParams
-  gsub(g, n.sons[paramsPos])
-  gsub(g, n.sons[pragmasPos])
+    gsub(g, n[namePos])
+
+  if n[patternPos].kind != nkEmpty:
+    gpattern(g, n[patternPos])
+  g.inside(GenericParams):
+    if renderNoBody in g.flags and n[miscPos].kind != nkEmpty and
+        n[miscPos][1].kind != nkEmpty:
+      gsub(g, n[miscPos][1])
+    else:
+      gsub(g, n[genericParamsPos])
+  gsub(g, n[paramsPos])
+  if renderNoPragmas notin g.flags:
+    gsub(g, n[pragmasPos])
   if renderNoBody notin g.flags:
-    if n.sons[bodyPos].kind != nkEmpty:
+    if n.len > bodyPos and n[bodyPos].kind != nkEmpty:
       put(g, tkSpaces, Space)
       putWithSpace(g, tkEquals, "=")
       indentNL(g)
       gcoms(g)
       dedent(g)
-      initContext(c)
-      gstmts(g, n.sons[bodyPos], c)
+      c = initContext()
+      gstmts(g, n[bodyPos], c)
       putNL(g)
     else:
       indentNL(g)
@@ -759,8 +889,7 @@ proc gproc(g: var TSrcGen, n: PNode) =
       dedent(g)
 
 proc gTypeClassTy(g: var TSrcGen, n: PNode) =
-  var c: TContext
-  initContext(c)
+  var c: TContext = initContext()
   putWithSpace(g, tkConcept, "concept")
   gsons(g, n[0], c) # arglist
   gsub(g, n[1]) # pragmas
@@ -772,45 +901,54 @@ proc gTypeClassTy(g: var TSrcGen, n: PNode) =
   dedent(g)
 
 proc gblock(g: var TSrcGen, n: PNode) =
-  var c: TContext
-  initContext(c)
-  if n.sons[0].kind != nkEmpty:
+  # you shouldn't simplify it to `n.len < 2`
+  # because the following codes should be executed
+  # even when block stmt has only one child for getting
+  # better error messages.
+  if n.len == 0:
+    return
+
+  var c: TContext = initContext()
+
+  if n[0].kind != nkEmpty:
     putWithSpace(g, tkBlock, "block")
-    gsub(g, n.sons[0])
+    gsub(g, n[0])
   else:
     put(g, tkBlock, "block")
+
+  # block stmt should have two children
+  if n.len == 1:
+    return
+
   putWithSpace(g, tkColon, ":")
-  if longMode(g, n) or (lsub(g, n.sons[1]) + g.lineLen > MaxLineLen):
+
+  if longMode(g, n) or (lsub(g, n[1]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)
-  # XXX I don't get why this is needed here! gstmts should already handle this!
-  indentNL(g)
-  gstmts(g, n.sons[1], c)
-  dedent(g)
+  gstmts(g, n[1], c)
 
 proc gstaticStmt(g: var TSrcGen, n: PNode) =
-  var c: TContext
+  var c: TContext = initContext()
   putWithSpace(g, tkStatic, "static")
   putWithSpace(g, tkColon, ":")
-  initContext(c)
-  if longMode(g, n) or (lsub(g, n.sons[0]) + g.lineLen > MaxLineLen):
+  if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)                    # a good place for comments
-  gstmts(g, n.sons[0], c)
+  gstmts(g, n[0], c)
 
 proc gasm(g: var TSrcGen, n: PNode) =
   putWithSpace(g, tkAsm, "asm")
-  gsub(g, n.sons[0])
+  gsub(g, n[0])
   gcoms(g)
-  if n.sons.len > 1:
-    gsub(g, n.sons[1])
+  if n.len > 1:
+    gsub(g, n[1])
 
 proc gident(g: var TSrcGen, n: PNode) =
-  if g.inGenericParams and n.kind == nkSym:
+  if GenericParams in g.inside and n.kind == nkSym:
     if sfAnon in n.sym.flags or
       (n.typ != nil and tfImplicitTypeParam in n.typ.flags): return
 
-  var t: TTokType
+  var t: TokType
   var s = atom(g, n)
   if s.len > 0 and s[0] in lexer.SymChars:
     if n.kind == nkIdent:
@@ -818,17 +956,27 @@ proc gident(g: var TSrcGen, n: PNode) =
           (n.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)):
         t = tkSymbol
       else:
-        t = TTokType(n.ident.id + ord(tkSymbol))
+        t = TokType(n.ident.id + ord(tkSymbol))
     else:
       t = tkSymbol
   else:
     t = tkOpr
-  put(g, t, s)
-  if n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags):
+  if renderIr in g.flags and n.kind == nkSym:
+    let localId = disamb(g, n.sym)
+    if localId != 0 and n.sym.magic == mNone:
+      s.add '_'
+      s.addInt localId
+    if sfCursor in n.sym.flags:
+      s.add "_cursor"
+  elif n.kind == nkSym and (renderIds in g.flags or
+      (sfGenSym in n.sym.flags and n.sym.name.id != ord(wUnderscore)) or
+      n.sym.kind == skTemp):
+    s.add '_'
+    s.addInt n.sym.id
     when defined(debugMagics):
-      put(g, tkIntLit, $n.sym.id & $n.sym.magic)
-    else:
-      put(g, tkIntLit, $n.sym.id)
+      s.add '_'
+      s.add $n.sym.magic
+  put(g, t, s, if n.kind == nkSym and renderSyms in g.flags: n.sym else: nil)
 
 proc doParamsAux(g: var TSrcGen, params: PNode) =
   if params.len > 1:
@@ -836,9 +984,10 @@ proc doParamsAux(g: var TSrcGen, params: PNode) =
     gsemicolon(g, params, 1)
     put(g, tkParRi, ")")
 
-  if params.len > 0 and params.sons[0].kind != nkEmpty:
+  if params.len > 0 and params[0].kind != nkEmpty:
+    put(g, tkSpaces, Space)
     putWithSpace(g, tkOpr, "->")
-    gsub(g, params.sons[0])
+    gsub(g, params[0])
 
 proc gsub(g: var TSrcGen; n: PNode; i: int) =
   if i < n.len:
@@ -846,18 +995,106 @@ proc gsub(g: var TSrcGen; n: PNode; i: int) =
   else:
     put(g, tkOpr, "<<" & $i & "th child missing for " & $n.kind & " >>")
 
-proc isBracket*(n: PNode): bool =
-  case n.kind
-  of nkClosedSymChoice, nkOpenSymChoice:
-    if n.len > 0: result = isBracket(n[0])
-  of nkSym: result = n.sym.name.s == "[]"
-  else: result = false
+type
+  BracketKind = enum
+    bkNone, bkBracket, bkBracketAsgn, bkCurly, bkCurlyAsgn
+
+proc bracketKind*(g: TSrcGen, n: PNode): BracketKind =
+  if renderIds notin g.flags:
+    case n.kind
+    of nkClosedSymChoice, nkOpenSymChoice:
+      if n.len > 0: result = bracketKind(g, n[0])
+      else: result = bkNone
+    of nkSym:
+      result = case n.sym.name.s
+        of "[]": bkBracket
+        of "[]=": bkBracketAsgn
+        of "{}": bkCurly
+        of "{}=": bkCurlyAsgn
+        else: bkNone
+    else: result = bkNone
+  else:
+    result = bkNone
+
+proc skipHiddenNodes(n: PNode): PNode =
+  result = n
+  while result != nil:
+    if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv, nkOpenSym} and result.len > 1:
+      result = result[1]
+    elif result.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString} and
+        result.len > 0:
+      result = result[0]
+    else: break
+
+proc accentedName(g: var TSrcGen, n: PNode) =
+  # This is for cases where ident should've really been a `nkAccQuoted`, e.g. `:tmp`
+  # or if user writes a macro with `ident":foo"`. It's unclear whether these should be legal.
+  const backticksNeeded = OpChars + {'[', '{', '\''}
+  if n == nil: return
+  let ident = n.getPIdent
+  if ident != nil and ident.s[0] in backticksNeeded:
+    put(g, tkAccent, "`")
+    gident(g, n)
+    put(g, tkAccent, "`")
+  else:
+    gsub(g, n)
+
+proc infixArgument(g: var TSrcGen, n: PNode, i: int) =
+  if i < 1 or i > 2: return
+  var needsParenthesis = false
+  let nNext = n[i].skipHiddenNodes
+  if nNext.kind == nkInfix:
+    if nNext[0].kind in {nkSym, nkIdent} and n[0].kind in {nkSym, nkIdent}:
+      let nextId = if nNext[0].kind == nkSym: nNext[0].sym.name else: nNext[0].ident
+      let nnId = if n[0].kind == nkSym: n[0].sym.name else: n[0].ident
+      if i == 1:
+        if getPrecedence(nextId) < getPrecedence(nnId):
+          needsParenthesis = true
+      elif i == 2:
+        if getPrecedence(nextId) <= getPrecedence(nnId):
+          needsParenthesis = true
+  if needsParenthesis:
+    put(g, tkParLe, "(")
+  gsub(g, n, i)
+  if needsParenthesis:
+    put(g, tkParRi, ")")
+
+const postExprBlocks = {nkStmtList, nkStmtListExpr,
+  nkOfBranch, nkElifBranch, nkElse,
+  nkExceptBranch, nkFinally, nkDo}
+
+proc postStatements(g: var TSrcGen, n: PNode, i: int, fromStmtList: bool) =
+  var i = i
+  if n[i].kind in {nkStmtList, nkStmtListExpr}:
+    if fromStmtList:
+      put(g, tkColon, ":")
+    else:
+      put(g, tkSpaces, Space)
+      put(g, tkDo, "do")
+      put(g, tkColon, ":")
+  gsub(g, n, i)
+  i.inc
+  for j in i ..< n.len:
+    if n[j].kind == nkDo:
+      optNL(g)
+    elif n[j].kind in {nkStmtList, nkStmtListExpr}:
+      optNL(g)
+      put(g, tkDo, "do")
+      put(g, tkColon, ":")
+    gsub(g, n, j)
 
-proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
+proc isCustomLit(n: PNode): bool =
+  if n.len == 2 and n[0].kind == nkRStrLit:
+    let ident = n[1].getPIdent
+    result = ident != nil and ident.s.startsWith('\'')
+  else:
+    result = false
+
+proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
   if isNil(n): return
   var
-    a: TContext
-  if n.comment != nil: pushCom(g, n)
+    a: TContext = default(TContext)
+  if shouldRenderComment(g, n): pushCom(g, n)
   case n.kind                 # atoms:
   of nkTripleStrLit: put(g, tkTripleStrLit, atom(g, n))
   of nkEmpty: discard
@@ -882,40 +1119,85 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
   of nkCharLit: put(g, tkCharLit, atom(g, n))
   of nkNilLit: put(g, tkNil, atom(g, n))    # complex expressions
   of nkCall, nkConv, nkDotCall, nkPattern, nkObjConstr:
-    if n.len > 0 and isBracket(n[0]):
-      gsub(g, n, 1)
-      put(g, tkBracketLe, "[")
-      gcomma(g, n, 2)
-      put(g, tkBracketRi, "]")
-    elif n.len > 1 and n.lastSon.kind == nkStmtList:
-      gsub(g, n[0])
-      if n.len > 2:
+    if n.len > 1 and n.lastSon.kind in postExprBlocks:
+      accentedName(g, n[0])
+      var i = 1
+      while i < n.len and n[i].kind notin postExprBlocks: i.inc
+      if i > 1:
         put(g, tkParLe, "(")
-        gcomma(g, n, 1, -2)
+        gcomma(g, n, 1, i - 1 - n.len)
+        put(g, tkParRi, ")")
+      postStatements(g, n, i, fromStmtList)
+    elif n.len >= 1:
+      case bracketKind(g, n[0])
+      of bkBracket:
+        gsub(g, n, 1)
+        put(g, tkBracketLe, "[")
+        gcomma(g, n, 2)
+        put(g, tkBracketRi, "]")
+      of bkBracketAsgn:
+        gsub(g, n, 1)
+        put(g, tkBracketLe, "[")
+        gcomma(g, n, 2, -2)
+        put(g, tkBracketRi, "]")
+        put(g, tkSpaces, Space)
+        putWithSpace(g, tkEquals, "=")
+        gsub(g, n, n.len - 1)
+      of bkCurly:
+        gsub(g, n, 1)
+        put(g, tkCurlyLe, "{")
+        gcomma(g, n, 2)
+        put(g, tkCurlyRi, "}")
+      of bkCurlyAsgn:
+        gsub(g, n, 1)
+        put(g, tkCurlyLe, "{")
+        gcomma(g, n, 2, -2)
+        put(g, tkCurlyRi, "}")
+        put(g, tkSpaces, Space)
+        putWithSpace(g, tkEquals, "=")
+        gsub(g, n, n.len - 1)
+      of bkNone:
+        accentedName(g, n[0])
+        put(g, tkParLe, "(")
+        gcomma(g, n, 1)
         put(g, tkParRi, ")")
-      put(g, tkColon, ":")
-      gsub(g, n, n.len-1)
     else:
-      if sonsLen(n) >= 1: gsub(g, n.sons[0])
       put(g, tkParLe, "(")
-      gcomma(g, n, 1)
       put(g, tkParRi, ")")
   of nkCallStrLit:
-    gsub(g, n, 0)
-    if n.len > 1 and n.sons[1].kind == nkRStrLit:
+    if n.len > 0: accentedName(g, n[0])
+    if n.len > 1 and n[1].kind == nkRStrLit:
       put(g, tkRStrLit, '\"' & replace(n[1].strVal, "\"", "\"\"") & '\"')
     else:
-      gsub(g, n.sons[1])
-  of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv:
+      gsub(g, n, 1)
+  of nkHiddenStdConv, nkHiddenSubConv:
     if n.len >= 2:
-      gsub(g, n.sons[1])
+      when false:
+        # if {renderIds, renderIr} * g.flags != {}:
+        put(g, tkSymbol, "(conv)")
+        put(g, tkParLe, "(")
+        gsub(g, n[1])
+        put(g, tkParRi, ")")
+      else:
+        gsub(g, n[1])
+    else:
+      put(g, tkSymbol, "(wrong conv)")
+  of nkHiddenCallConv:
+    if {renderIds, renderIr} * g.flags != {}:
+      accentedName(g, n[0])
+      put(g, tkParLe, "(")
+      gcomma(g, n, 1)
+      put(g, tkParRi, ")")
+    elif n.len >= 2:
+      gsub(g, n[1])
     else:
       put(g, tkSymbol, "(wrong conv)")
   of nkCast:
     put(g, tkCast, "cast")
-    put(g, tkBracketLe, "[")
-    gsub(g, n, 0)
-    put(g, tkBracketRi, "]")
+    if n.len > 0 and n[0].kind != nkEmpty:
+      put(g, tkBracketLe, "[")
+      gsub(g, n, 0)
+      put(g, tkBracketRi, "]")
     put(g, tkParLe, "(")
     gsub(g, n, 1)
     put(g, tkParRi, ")")
@@ -923,7 +1205,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     put(g, tkAddr, "addr")
     if n.len > 0:
       put(g, tkParLe, "(")
-      gsub(g, n.sons[0])
+      gsub(g, n[0])
       put(g, tkParRi, ")")
   of nkStaticExpr:
     put(g, tkStatic, "static")
@@ -943,14 +1225,26 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     gsub(g, n, 0)
     gcomma(g, n, 1)
   of nkCommand:
-    gsub(g, n, 0)
+    accentedName(g, n[0])
     put(g, tkSpaces, Space)
-    gcomma(g, n, 1)
+    if n.len > 1 and n.lastSon.kind in postExprBlocks:
+      var i = 1
+      while i < n.len and n[i].kind notin postExprBlocks: i.inc
+      if i > 1:
+        gcomma(g, n, 1, i - 1 - n.len)
+      postStatements(g, n, i, fromStmtList)
+    else:
+      gcomma(g, n, 1)
   of nkExprEqExpr, nkAsgn, nkFastAsgn:
     gsub(g, n, 0)
     put(g, tkSpaces, Space)
     putWithSpace(g, tkEquals, "=")
     gsub(g, n, 1)
+  of nkSinkAsgn:
+    put(g, tkSymbol, "`=sink`")
+    put(g, tkParLe, "(")
+    gcomma(g, n)
+    put(g, tkParRi, ")")
   of nkChckRangeF:
     put(g, tkSymbol, "chckRangeF")
     put(g, tkParLe, "(")
@@ -966,27 +1260,28 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     put(g, tkParLe, "(")
     gcomma(g, n)
     put(g, tkParRi, ")")
-  of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
-    if sonsLen(n) >= 1: gsub(g, n.sons[0])
-    put(g, tkParLe, "(")
-    gcomma(g, n, 1)
+  of nkObjDownConv, nkObjUpConv:
+    let typ = if (n.typ != nil) and (n.typ.sym != nil): n.typ.sym.name.s else: ""
+    put(g, tkParLe, typ & "(")
+    if n.len >= 1: gsub(g, n[0])
     put(g, tkParRi, ")")
   of nkClosedSymChoice, nkOpenSymChoice:
     if renderIds in g.flags:
       put(g, tkParLe, "(")
-      for i in countup(0, sonsLen(n) - 1):
+      for i in 0..<n.len:
         if i > 0: put(g, tkOpr, "|")
-        if n.sons[i].kind == nkSym:
+        if n[i].kind == nkSym:
           let s = n[i].sym
           if s.owner != nil:
             put g, tkSymbol, n[i].sym.owner.name.s
             put g, tkOpr, "."
           put g, tkSymbol, n[i].sym.name.s
         else:
-          gsub(g, n.sons[i], c)
+          gsub(g, n[i], c)
       put(g, tkParRi, if n.kind == nkOpenSymChoice: "|...)" else: ")")
     else:
       gsub(g, n, 0)
+  of nkOpenSym: gsub(g, n, 0)
   of nkPar, nkClosure:
     put(g, tkParLe, "(")
     gcomma(g, n, c)
@@ -994,7 +1289,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
   of nkTupleConstr:
     put(g, tkParLe, "(")
     gcomma(g, n, c)
-    if n.len == 1: put(g, tkComma, ",")
+    if n.len == 1 and n[0].kind != nkExprColonExpr: put(g, tkComma, ",")
     put(g, tkParRi, ")")
   of nkCurly:
     put(g, tkCurlyLe, "{")
@@ -1012,14 +1307,25 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     gcomma(g, n, c)
     put(g, tkBracketRi, "]")
   of nkDotExpr:
-    gsub(g, n, 0)
-    put(g, tkDot, ".")
-    gsub(g, n, 1)
+    if isCustomLit(n):
+      put(g, tkCustomLit, n[0].strVal)
+      gsub(g, n, 1)
+    else:
+      gsub(g, n, 0)
+      put(g, tkDot, ".")
+      assert n.len == 2, $n.len
+      accentedName(g, n[1])
   of nkBind:
     putWithSpace(g, tkBind, "bind")
     gsub(g, n, 0)
-  of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref:
+  of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString:
+    if renderIds in g.flags:
+      put(g, tkAddr, $n.kind)
+      put(g, tkParLe, "(")
     gsub(g, n, 0)
+    if renderIds in g.flags:
+      put(g, tkParRi, ")")
+
   of nkLambda:
     putWithSpace(g, tkProc, "proc")
     gsub(g, n, paramsPos)
@@ -1030,40 +1336,81 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
   of nkDo:
     putWithSpace(g, tkDo, "do")
     if paramsPos < n.len:
-      doParamsAux(g, n.sons[paramsPos])
+      doParamsAux(g, n[paramsPos])
     gsub(g, n, pragmasPos)
     put(g, tkColon, ":")
     gsub(g, n, bodyPos)
-  of nkConstDef, nkIdentDefs:
+  of nkIdentDefs:
+    var exclFlags: TRenderFlags = {}
+    if ObjectDef in g.inside:
+      if not n[0].isExported() and renderNonExportedFields notin g.flags:
+        # Skip if this is a property in a type and its not exported
+        # (While also not allowing rendering of non exported fields)
+        return
+      # render postfix for object fields:
+      exclFlags = g.flags * {renderNoPostfix}
+    # We render the identDef without being inside the section incase we render something like
+    # y: proc (x: string) # (We wouldn't want to check if x is exported)
+    g.outside(ObjectDef):
+      g.flags.excl(exclFlags)
+      gcomma(g, n, 0, -3)
+      g.flags.incl(exclFlags)
+      if n.len >= 2 and n[^2].kind != nkEmpty:
+        putWithSpace(g, tkColon, ":")
+        gsub(g, n[^2], c)
+      elif n.referencesUsing and renderExpandUsing in g.flags:
+        putWithSpace(g, tkColon, ":")
+        gsub(g, newSymNode(n.origUsingType), c)
+
+      if n.len >= 1 and n[^1].kind != nkEmpty:
+        put(g, tkSpaces, Space)
+        putWithSpace(g, tkEquals, "=")
+        gsub(g, n[^1], c)
+  of nkConstDef:
     gcomma(g, n, 0, -3)
-    var L = sonsLen(n)
-    if L >= 2 and n.sons[L - 2].kind != nkEmpty:
+    if n.len >= 2 and n[^2].kind != nkEmpty:
       putWithSpace(g, tkColon, ":")
-      gsub(g, n, L - 2)
-    if L >= 1 and n.sons[L - 1].kind != nkEmpty:
+      gsub(g, n[^2], c)
+
+    if n.len >= 1 and n[^1].kind != nkEmpty:
       put(g, tkSpaces, Space)
       putWithSpace(g, tkEquals, "=")
-      gsub(g, n.sons[L - 1], c)
+      gsub(g, n[^1], c)
   of nkVarTuple:
-    put(g, tkParLe, "(")
-    gcomma(g, n, 0, -3)
-    put(g, tkParRi, ")")
-    put(g, tkSpaces, Space)
-    putWithSpace(g, tkEquals, "=")
-    gsub(g, lastSon(n), c)
+    if n[^1].kind == nkEmpty:
+      put(g, tkParLe, "(")
+      gcomma(g, n, 0, -2)
+      put(g, tkParRi, ")")
+    else:
+      put(g, tkParLe, "(")
+      gcomma(g, n, 0, -3)
+      put(g, tkParRi, ")")
+      put(g, tkSpaces, Space)
+      putWithSpace(g, tkEquals, "=")
+      gsub(g, lastSon(n), c)
   of nkExprColonExpr:
     gsub(g, n, 0)
     putWithSpace(g, tkColon, ":")
     gsub(g, n, 1)
   of nkInfix:
-    gsub(g, n, 1)
+    if n.len < 3:
+      var i = 0
+      put(g, tkOpr, "Too few children for nkInfix")
+      return
+    let oldLineLen = g.lineLen # we cache this because lineLen gets updated below
+    infixArgument(g, n, 1)
     put(g, tkSpaces, Space)
     gsub(g, n, 0)        # binary operator
-    if not fits(g, lsub(g, n.sons[2]) + lsub(g, n.sons[0]) + 1):
+    # e.g.: `n1 == n2` decompses as following sum:
+    if n.len == 3 and not fits(g, oldLineLen + lsub(g, n[1]) + lsub(g, n[2]) + lsub(g, n[0]) + len("  ")):
       optNL(g, g.indent + longIndentWid)
     else:
       put(g, tkSpaces, Space)
-    gsub(g, n, 2)
+    infixArgument(g, n, 2)
+    if n.len > 3 and n.lastSon.kind in postExprBlocks:
+      var i = 3
+      while i < n.len and n[i].kind notin postExprBlocks: i.inc
+      postStatements(g, n, i, fromStmtList)
   of nkPrefix:
     gsub(g, n, 0)
     if n.len > 1:
@@ -1071,17 +1418,23 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
                 elif n[0].kind == nkSym: n[0].sym.name
                 elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
                 else: nil
-      if n[1].kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
+      let nNext = skipHiddenNodes(n[1])
+      if nNext.kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
         put(g, tkSpaces, Space)
-      if n.sons[1].kind == nkInfix:
+      if nNext.kind == nkInfix:
         put(g, tkParLe, "(")
-        gsub(g, n.sons[1])
+        gsub(g, n[1])
         put(g, tkParRi, ")")
       else:
-        gsub(g, n.sons[1])
+        gsub(g, n[1])
+    if n.len > 2 and n.lastSon.kind in postExprBlocks:
+      var i = 2
+      while i < n.len and n[i].kind notin postExprBlocks: i.inc
+      postStatements(g, n, i, fromStmtList)
   of nkPostfix:
     gsub(g, n, 1)
-    gsub(g, n, 0)
+    if renderNoPostfix notin g.flags:
+      gsub(g, n, 0)
   of nkRange:
     gsub(g, n, 0)
     put(g, tkDotDot, "..")
@@ -1091,20 +1444,33 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     put(g, tkOpr, "[]")
   of nkAccQuoted:
     put(g, tkAccent, "`")
-    if n.len > 0: gsub(g, n.sons[0])
-    for i in 1 ..< n.len:
-      put(g, tkSpaces, Space)
-      gsub(g, n.sons[i])
+    for i in 0..<n.len:
+      proc isAlpha(n: PNode): bool =
+        if n.kind in {nkIdent, nkSym}:
+          let tmp = n.getPIdent.s
+          result = tmp.len > 0 and tmp[0] in {'a'..'z', 'A'..'Z'}
+        else:
+          result = false
+      var useSpace = false
+      if i == 1 and n[0].kind == nkIdent and n[0].ident.s in ["=", "'"]:
+        if not n[1].isAlpha: # handle `=destroy`, `'big'
+          useSpace = true
+      elif i == 1 and n[1].kind == nkIdent and n[1].ident.s == "=":
+        if not n[0].isAlpha: # handle setters, e.g. `foo=`
+          useSpace = true
+      elif i > 0: useSpace = true
+      if useSpace:  put(g, tkSpaces, Space)
+      gsub(g, n[i])
     put(g, tkAccent, "`")
   of nkIfExpr:
     putWithSpace(g, tkIf, "if")
-    if n.len > 0: gsub(g, n.sons[0], 0)
+    if n.len > 0: gcond(g, n[0][0])
     putWithSpace(g, tkColon, ":")
-    if n.len > 0: gsub(g, n.sons[0], 1)
+    if n.len > 0: gsub(g, n[0], 1)
     gsons(g, n, emptyContext, 1)
   of nkElifExpr:
     putWithSpace(g, tkElif, " elif")
-    gsub(g, n, 0)
+    gcond(g, n[0])
     putWithSpace(g, tkColon, ":")
     gsub(g, n, 1)
   of nkElseExpr:
@@ -1112,32 +1478,38 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     putWithSpace(g, tkColon, ":")
     gsub(g, n, 0)
   of nkTypeOfExpr:
-    put(g, tkType, "type")
+    put(g, tkType, "typeof")
     put(g, tkParLe, "(")
-    if n.len > 0: gsub(g, n.sons[0])
+    if n.len > 0: gsub(g, n[0])
     put(g, tkParRi, ")")
   of nkRefTy:
-    if sonsLen(n) > 0:
+    if n.len > 0:
       putWithSpace(g, tkRef, "ref")
-      gsub(g, n.sons[0])
+      gsub(g, n[0])
     else:
       put(g, tkRef, "ref")
   of nkPtrTy:
-    if sonsLen(n) > 0:
+    if n.len > 0:
       putWithSpace(g, tkPtr, "ptr")
-      gsub(g, n.sons[0])
+      gsub(g, n[0])
     else:
       put(g, tkPtr, "ptr")
   of nkVarTy:
-    if sonsLen(n) > 0:
+    if n.len > 0:
       putWithSpace(g, tkVar, "var")
-      gsub(g, n.sons[0])
+      gsub(g, n[0])
     else:
       put(g, tkVar, "var")
+  of nkOutTy:
+    if n.len > 0:
+      putWithSpace(g, tkOut, "out")
+      gsub(g, n[0])
+    else:
+      put(g, tkOut, "out")
   of nkDistinctTy:
     if n.len > 0:
       putWithSpace(g, tkDistinct, "distinct")
-      gsub(g, n.sons[0])
+      gsub(g, n[0])
       if n.len > 1:
         if n[1].kind == nkWith:
           putWithSpace(g, tkSymbol, " with")
@@ -1147,41 +1519,47 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     else:
       put(g, tkDistinct, "distinct")
   of nkTypeDef:
-    gsub(g, n, 0)
-    gsub(g, n, 1)
+    if n[0].kind == nkPragmaExpr:
+      # generate pragma after generic
+      gsub(g, n[0], 0)
+      gsub(g, n, 1)
+      gsub(g, n[0], 1)
+    else:
+      gsub(g, n, 0)
+      gsub(g, n, 1)
     put(g, tkSpaces, Space)
-    if n.len > 2 and n.sons[2].kind != nkEmpty:
+    if n.len > 2 and n[2].kind != nkEmpty:
       putWithSpace(g, tkEquals, "=")
-      gsub(g, n.sons[2])
+      gsub(g, n[2])
   of nkObjectTy:
-    if sonsLen(n) > 0:
+    if n.len > 0:
       putWithSpace(g, tkObject, "object")
-      gsub(g, n.sons[0])
-      gsub(g, n.sons[1])
-      gcoms(g)
-      gsub(g, n.sons[2])
+      g.inside(ObjectDef):
+        gsub(g, n[0])
+        gsub(g, n[1])
+        gcoms(g)
+        indentNL(g)
+        gsub(g, n[2])
+        dedent(g)
     else:
       put(g, tkObject, "object")
   of nkRecList:
-    indentNL(g)
-    for i in countup(0, sonsLen(n) - 1):
+    for i in 0..<n.len:
       optNL(g)
-      gsub(g, n.sons[i], c)
+      gsub(g, n[i], c)
       gcoms(g)
-    dedent(g)
-    putNL(g)
   of nkOfInherit:
     putWithSpace(g, tkOf, "of")
     gsub(g, n, 0)
   of nkProcTy:
-    if sonsLen(n) > 0:
+    if n.len > 0:
       putWithSpace(g, tkProc, "proc")
       gsub(g, n, 0)
       gsub(g, n, 1)
     else:
       put(g, tkProc, "proc")
   of nkIteratorTy:
-    if sonsLen(n) > 0:
+    if n.len > 0:
       putWithSpace(g, tkIterator, "iterator")
       gsub(g, n, 0)
       gsub(g, n, 1)
@@ -1191,12 +1569,12 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     put(g, tkStatic, "static")
     put(g, tkBracketLe, "[")
     if n.len > 0:
-      gsub(g, n.sons[0])
+      gsub(g, n[0])
     put(g, tkBracketRi, "]")
   of nkEnumTy:
-    if sonsLen(n) > 0:
+    if n.len > 0:
       putWithSpace(g, tkEnum, "enum")
-      gsub(g, n.sons[0])
+      gsub(g, n[0])
       gcoms(g)
       indentNL(g)
       gcommaAux(g, n, g.indent, 1)
@@ -1209,7 +1587,13 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     put(g, tkSpaces, Space)
     putWithSpace(g, tkEquals, "=")
     gsub(g, n, 1)
-  of nkStmtList, nkStmtListExpr, nkStmtListType: gstmts(g, n, emptyContext)
+  of nkStmtList, nkStmtListExpr, nkStmtListType:
+    if n.len == 1 and n[0].kind == nkDiscardStmt:
+      put(g, tkParLe, "(")
+      gsub(g, n[0])
+      put(g, tkParRi, ")")
+    else:
+      gstmts(g, n, emptyContext)
   of nkIfStmt:
     putWithSpace(g, tkIf, "if")
     gif(g, n)
@@ -1219,7 +1603,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
   of nkWhileStmt: gwhile(g, n)
   of nkPragmaBlock: gpragmaBlock(g, n)
   of nkCaseStmt, nkRecCase: gcase(g, n)
-  of nkTryStmt: gtry(g, n)
+  of nkTryStmt, nkHiddenTryStmt: gtry(g, n)
   of nkForStmt, nkParForStmt: gfor(g, n)
   of nkBlockStmt, nkBlockExpr: gblock(g, n)
   of nkStaticStmt: gstaticStmt(g, n)
@@ -1248,28 +1632,30 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
   of nkTypeSection:
     gsection(g, n, emptyContext, tkType, "type")
   of nkConstSection:
-    initContext(a)
+    a = initContext()
     incl(a.flags, rfInConstExpr)
     gsection(g, n, a, tkConst, "const")
   of nkVarSection, nkLetSection, nkUsingStmt:
-    var L = sonsLen(n)
-    if L == 0: return
+    if n.len == 0: return
     if n.kind == nkVarSection: putWithSpace(g, tkVar, "var")
     elif n.kind == nkLetSection: putWithSpace(g, tkLet, "let")
     else: putWithSpace(g, tkUsing, "using")
-    if L > 1:
+    if n.len > 1:
       gcoms(g)
       indentNL(g)
-      for i in countup(0, L - 1):
+      for i in 0..<n.len:
         optNL(g)
-        gsub(g, n.sons[i])
+        gsub(g, n[i])
         gcoms(g)
       dedent(g)
     else:
-      gsub(g, n.sons[0])
+      gsub(g, n[0])
   of nkReturnStmt:
     putWithSpace(g, tkReturn, "return")
-    gsub(g, n, 0)
+    if n.len > 0 and n[0].kind == nkAsgn and renderIr notin g.flags:
+      gsub(g, n[0], 1)
+    else:
+      gsub(g, n, 0)
   of nkRaiseStmt:
     putWithSpace(g, tkRaise, "raise")
     gsub(g, n, 0)
@@ -1286,17 +1672,16 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     putWithSpace(g, tkContinue, "continue")
     gsub(g, n, 0)
   of nkPragma:
-    if renderNoPragmas notin g.flags:
-      if g.inPragma <= 0:
-        inc g.inPragma
-        #if not previousNL(g):
-        put(g, tkSpaces, Space)
-        put(g, tkCurlyDotLe, "{.")
-        gcomma(g, n, emptyContext)
-        put(g, tkCurlyDotRi, ".}")
-        dec g.inPragma
-      else:
-        gcomma(g, n, emptyContext)
+    if g.inPragma <= 0:
+      inc g.inPragma
+      #if not previousNL(g):
+      put(g, tkSpaces, Space)
+      put(g, tkCurlyDotLe, "{.")
+      gcomma(g, n, emptyContext)
+      put(g, tkCurlyDotRi, ".}")
+      dec g.inPragma
+    else:
+      gcomma(g, n, emptyContext)
   of nkImportStmt, nkExportStmt:
     if n.kind == nkImportStmt:
       putWithSpace(g, tkImport, "import")
@@ -1359,13 +1744,13 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     gsub(g, n, 0)
     putWithSpace(g, tkColon, ":")
     gcoms(g)
-    gstmts(g, n.sons[1], c)
+    gstmts(g, n[1], c)
   of nkElse:
     optNL(g)
     put(g, tkElse, "else")
     putWithSpace(g, tkColon, ":")
     gcoms(g)
-    gstmts(g, n.sons[0], c)
+    gstmts(g, n[0], c)
   of nkFinally, nkDefer:
     optNL(g)
     if n.kind == nkFinally:
@@ -1374,7 +1759,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
       put(g, tkDefer, "defer")
     putWithSpace(g, tkColon, ":")
     gcoms(g)
-    gstmts(g, n.sons[0], c)
+    gstmts(g, n[0], c)
   of nkExceptBranch:
     optNL(g)
     if n.len != 1:
@@ -1400,9 +1785,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     put(g, tkParLe, "(")
     gsemicolon(g, n, 1)
     put(g, tkParRi, ")")
-    if n.len > 0 and n.sons[0].kind != nkEmpty:
+    if n.len > 0 and n[0].kind != nkEmpty:
       putWithSpace(g, tkColon, ":")
-      gsub(g, n.sons[0])
+      gsub(g, n[0])
   of nkTupleTy:
     put(g, tkTuple, "tuple")
     put(g, tkBracketLe, "[")
@@ -1415,13 +1800,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     gsub(g, n, 0)
     put(g, tkParRi, ")")
   of nkGotoState:
-    var c: TContext
-    initContext c
+    var c: TContext = initContext()
     putWithSpace g, tkSymbol, "goto"
     gsons(g, n, c)
   of nkState:
-    var c: TContext
-    initContext c
+    var c: TContext = initContext()
     putWithSpace g, tkSymbol, "state"
     gsub(g, n[0], c)
     putWithSpace(g, tkColon, ":")
@@ -1431,15 +1814,21 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
 
   of nkBreakState:
     put(g, tkTuple, "breakstate")
+    if renderIds in g.flags:
+      gsons(g, n, c, 0)
   of nkTypeClassTy:
     gTypeClassTy(g, n)
+  of nkError:
+    putWithSpace(g, tkSymbol, "error")
+    #gcomma(g, n, c)
+    gsub(g, n[0], c)
   else:
     #nkNone, nkExplicitTypeListCall:
-    internalError(g.config, n.info, "rnimsyn.gsub(" & $n.kind & ')')
+    internalError(g.config, n.info, "renderer.gsub(" & $n.kind & ')')
 
 proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string =
-  var g: TSrcGen
-  initSrcGen(g, renderFlags, newPartialConfigRef())
+  if n == nil: return "<nil tree>"
+  var g: TSrcGen = initSrcGen(renderFlags, newPartialConfigRef())
   # do not indent the initial statement list so that
   # writeFile("file.nim", repr n)
   # produces working Nim code:
@@ -1451,19 +1840,18 @@ proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string =
 
 proc `$`*(n: PNode): string = n.renderTree
 
-proc renderModule*(n: PNode, infile, outfile: string,
+proc renderModule*(n: PNode, outfile: string,
                    renderFlags: TRenderFlags = {};
                    fid = FileIndex(-1);
                    conf: ConfigRef = nil) =
   var
-    f: File
-    g: TSrcGen
-  initSrcGen(g, renderFlags, conf)
+    f: File = default(File)
+    g: TSrcGen = initSrcGen(renderFlags, conf)
   g.fid = fid
-  for i in countup(0, sonsLen(n) - 1):
-    gsub(g, n.sons[i])
+  for i in 0..<n.len:
+    gsub(g, n[i])
     optNL(g)
-    case n.sons[i].kind
+    case n[i].kind
     of nkTypeSection, nkConstSection, nkVarSection, nkLetSection,
        nkCommentStmt: putNL(g)
     else: discard
@@ -1474,16 +1862,22 @@ proc renderModule*(n: PNode, infile, outfile: string,
   else:
     rawMessage(g.config, errGenerated, "cannot open file: " & outfile)
 
-proc initTokRender*(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {}) =
-  initSrcGen(r, renderFlags, newPartialConfigRef())
-  gsub(r, n)
+proc initTokRender*(n: PNode, renderFlags: TRenderFlags = {}): TSrcGen =
+  result = initSrcGen(renderFlags, newPartialConfigRef())
+  gsub(result, n)
 
-proc getNextTok*(r: var TSrcGen, kind: var TTokType, literal: var string) =
-  if r.idx < len(r.tokens):
+proc getNextTok*(r: var TSrcGen, kind: var TokType, literal: var string) =
+  if r.idx < r.tokens.len:
     kind = r.tokens[r.idx].kind
-    var length = r.tokens[r.idx].length.int
+    let length = r.tokens[r.idx].length.int
     literal = substr(r.buf, r.pos, r.pos + length - 1)
     inc(r.pos, length)
     inc(r.idx)
   else:
     kind = tkEof
+
+proc getTokSym*(r: TSrcGen): PSym =
+  if r.idx > 0 and r.idx <= r.tokens.len:
+    result = r.tokens[r.idx-1].sym
+  else:
+    result = nil
diff --git a/compiler/renderverbatim.nim b/compiler/renderverbatim.nim
new file mode 100644
index 000000000..c12595156
--- /dev/null
+++ b/compiler/renderverbatim.nim
@@ -0,0 +1,137 @@
+import std/strutils
+
+import ast, options, msgs
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
+const isDebug = false
+when isDebug:
+  import renderer
+  import astalgo
+
+proc lastNodeRec(n: PNode): PNode =
+  result = n
+  while result.safeLen > 0: result = result[^1]
+
+proc isInIndentationBlock(src: string, indent: int): bool =
+  #[
+  we stop at the first de-indentation; there's an inherent ambiguity with non
+  doc comments since they can have arbitrary indentation, so we just take the
+  practical route and require a runnableExamples to keep its code (including non
+  doc comments) to its indentation level.
+  ]#
+  for j in 0..<indent:
+    if src.len <= j: return true
+    if src[j] != ' ': return false
+  return true
+
+type LineData = object
+  ## keep track of which lines are starting inside a multiline doc comment.
+  ## We purposefully avoid re-doing parsing which is already done (we get a PNode)
+  ## so we don't worry about whether we're inside (nested) doc comments etc.
+  ## But we sill need some logic to disambiguate different multiline styles.
+  conf: ConfigRef
+  lineFirst: int
+  lines: seq[bool]
+    ## lines[index] is true if line `lineFirst+index` starts inside a multiline string
+    ## Using a HashSet (extra dependency) would simplify but not by much.
+
+proc tripleStrLitStartsAtNextLine(conf: ConfigRef, n: PNode): bool =
+  # enabling TLineInfo.offsetA,offsetB would probably make this easier
+  result = false
+  const tripleQuote = "\"\"\""
+  let src = sourceLine(conf, n.info)
+  let col = n.info.col
+  doAssert src.continuesWith(tripleQuote, col) # sanity check
+  var i = col + 3
+  var onlySpace = true
+  while true:
+    if src.len <= i:
+      doAssert src.len == i
+      return onlySpace
+    elif src.continuesWith(tripleQuote, i) and (src.len == i+3 or src[i+3] != '\"'):
+      return false # triple lit is in 1 line
+    elif src[i] != ' ': onlySpace = false
+    i.inc
+
+proc visitMultilineStrings(ldata: var LineData, n: PNode) =
+  var cline = ldata.lineFirst
+
+  template setLine() =
+    let index = cline - ldata.lineFirst
+    if ldata.lines.len < index+1: ldata.lines.setLen index+1
+    ldata.lines[index] = true
+
+  case n.kind
+  of nkTripleStrLit:
+    # same logic should be applied for any multiline token
+    # we could also consider nkCommentStmt but right now we just assume doc comments,
+    # unlike triple string litterals, don't de-indent from runnableExamples.
+    cline = n.info.line.int
+    if tripleStrLitStartsAtNextLine(ldata.conf, n):
+      cline.inc
+      setLine()
+    for ai in n.strVal:
+      case ai
+      of '\n':
+        cline.inc
+        setLine()
+      else: discard
+  else:
+    for i in 0..<n.safeLen:
+      visitMultilineStrings(ldata, n[i])
+
+proc startOfLineInsideTriple(ldata: LineData, line: int): bool =
+  let index = line - ldata.lineFirst
+  if index >= ldata.lines.len: false
+  else: ldata.lines[index]
+
+proc extractRunnableExamplesSource*(conf: ConfigRef; n: PNode, indent = 0): string =
+  ## TLineInfo.offsetA,offsetB would be cleaner but it's only enabled for nimpretty,
+  ## we'd need to check performance impact to enable it for nimdoc.
+  var first = n.lastSon.info
+  if first.line == n[0].info.line:
+    #[
+    runnableExamples: assert true
+    ]#
+    discard
+  else:
+    #[
+    runnableExamples:
+      # non-doc comment that we want to capture even though `first` points to `assert true`
+      assert true
+    ]#
+    first.line = n[0].info.line + 1
+
+  let last = n.lastNodeRec.info
+  var info = first
+  var indent2 = info.col
+  let numLines = numLines(conf, info.fileIndex).uint16
+  var lastNonemptyPos = 0
+
+  var ldata = LineData(lineFirst: first.line.int, conf: conf)
+  visitMultilineStrings(ldata, n[^1])
+  when isDebug:
+    debug(n)
+    for i in 0..<ldata.lines.len:
+      echo (i+ldata.lineFirst, ldata.lines[i])
+
+  result = ""
+  for line in first.line..numLines: # bugfix, see `testNimDocTrailingExample`
+    info.line = line
+    let src = sourceLine(conf, info)
+    let special = startOfLineInsideTriple(ldata, line.int)
+    if line > last.line and not special and not isInIndentationBlock(src, indent2):
+      break
+    if line > first.line: result.add "\n"
+    if special:
+      result.add src
+      lastNonemptyPos = result.len
+    elif src.len > indent2:
+      for i in 0..<indent: result.add ' '
+      result.add src[indent2..^1]
+      lastNonemptyPos = result.len
+  result.setLen lastNonemptyPos
+
diff --git a/compiler/reorder.nim b/compiler/reorder.nim
index 27b19a373..2f7c04af1 100644
--- a/compiler/reorder.nim
+++ b/compiler/reorder.nim
@@ -1,9 +1,17 @@
 
 import
-  intsets, ast, idents, algorithm, renderer, parser, ospaths, strutils,
-  sequtils, msgs, modulegraphs, syntaxes, options, modulepaths, tables,
+  ast, idents, renderer,
+  msgs, modulegraphs, syntaxes, options, modulepaths,
   lineinfos
 
+import std/[algorithm, strutils, intsets]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+when defined(nimDebugReorder):
+  import std/tables
+
 type
   DepN = ref object
     pnode: PNode
@@ -11,36 +19,27 @@ type
     onStack: bool
     kids: seq[DepN]
     hAQ, hIS, hB, hCmd: int
-    when defined(debugReorder):
+    when defined(nimDebugReorder):
       expls: seq[string]
   DepG = seq[DepN]
 
-when defined(debugReorder):
+when defined(nimDebugReorder):
   var idNames = newTable[int, string]()
 
 proc newDepN(id: int, pnode: PNode): DepN =
-  new(result)
-  result.id = id
-  result.pnode = pnode
-  result.idx = -1
-  result.lowLink = -1
-  result.onStack = false
-  result.kids = @[]
-  result.hAQ = -1
-  result.hIS = -1
-  result.hB = -1
-  result.hCmd = -1
-  when defined(debugReorder):
+  result = DepN(id: id, pnode: pnode, idx: -1,
+                lowLink: -1, onStack: false,
+                kids: @[], hAQ: -1, hIS: -1,
+                hB: -1, hCmd: -1
+  )
+  when defined(nimDebugReorder):
     result.expls = @[]
 
 proc accQuoted(cache: IdentCache; n: PNode): PIdent =
   var id = ""
-  for i in 0 ..< n.len:
-    let x = n[i]
-    case x.kind
-    of nkIdent: id.add(x.ident.s)
-    of nkSym: id.add(x.sym.name.s)
-    else: discard
+  for i in 0..<n.len:
+    let ident = n[i].getPIdent
+    if ident != nil: id.add(ident.s)
   result = getIdent(cache, id)
 
 proc addDecl(cache: IdentCache; n: PNode; declares: var IntSet) =
@@ -49,16 +48,16 @@ proc addDecl(cache: IdentCache; n: PNode; declares: var IntSet) =
   of nkPragmaExpr: addDecl(cache, n[0], declares)
   of nkIdent:
     declares.incl n.ident.id
-    when defined(debugReorder):
+    when defined(nimDebugReorder):
       idNames[n.ident.id] = n.ident.s
   of nkSym:
     declares.incl n.sym.name.id
-    when defined(debugReorder):
+    when defined(nimDebugReorder):
       idNames[n.sym.name.id] = n.sym.name.s
   of nkAccQuoted:
     let a = accQuoted(cache, n)
     declares.incl a.id
-    when defined(debugReorder):
+    when defined(nimDebugReorder):
       idNames[a.id] = a.s
   of nkEnumFieldDef:
     addDecl(cache, n[0], declares)
@@ -75,8 +74,8 @@ proc computeDeps(cache: IdentCache; n: PNode, declares, uses: var IntSet; topLev
   of nkLetSection, nkVarSection, nkUsingStmt:
     for a in n:
       if a.kind in {nkIdentDefs, nkVarTuple}:
-        for j in countup(0, a.len-3): decl(a[j])
-        for j in a.len-2..a.len-1: deps(a[j])
+        for j in 0..<a.len-2: decl(a[j])
+        for j in a.len-2..<a.len: deps(a[j])
   of nkConstSection, nkTypeSection:
     for a in n:
       if a.len >= 3:
@@ -95,48 +94,29 @@ proc computeDeps(cache: IdentCache; n: PNode, declares, uses: var IntSet; topLev
   of nkSym: uses.incl n.sym.name.id
   of nkAccQuoted: uses.incl accQuoted(cache, n).id
   of nkOpenSymChoice, nkClosedSymChoice:
-    uses.incl n.sons[0].sym.name.id
+    uses.incl n[0].sym.name.id
   of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
-    for i in 0..<len(n): computeDeps(cache, n[i], declares, uses, topLevel)
+    for i in 0..<n.len: computeDeps(cache, n[i], declares, uses, topLevel)
   of nkPragma:
-    let a = n.sons[0]
-    if a.kind == nkExprColonExpr and a.sons[0].kind == nkIdent and
-       a.sons[0].ident.s == "pragma":
-        # user defined pragma
-        decl(a.sons[1])
+    let a = n[0]
+    if a.kind == nkExprColonExpr and a[0].kind == nkIdent and a[0].ident.s == "pragma":
+      # user defined pragma
+      decl(a[1])
+      for i in 1..<n.safeLen: deps(n[i])
     else:
-      for i in 0..<safeLen(n): deps(n[i])
+      for i in 0..<n.safeLen: deps(n[i])
+  of nkMixinStmt, nkBindStmt: discard
   else:
-    for i in 0..<safeLen(n): deps(n[i])
-
-proc cleanPath(s: string): string =
-  # Here paths may have the form A / B or "A/B"
-  result = ""
-  for c in s:
-    if c != ' ' and c != '\"':
-      result.add c
-
-proc joinPath(parts: seq[string]): string =
-  let nb = parts.len
-  assert nb > 0
-  if nb == 1:
-    return parts[0]
-  result = parts[0] / parts[1]
-  for i in 2..<parts.len:
-    result = result / parts[i]
-
-proc getIncludePath(n: PNode, modulePath: string): string =
-  let istr = n.renderTree.cleanPath
-  let (pdir, _) = modulePath.splitPath
-  let p = istr.split('/').joinPath.addFileExt("nim")
-  result = pdir / p
-
-proc hasIncludes(n:PNode): bool =
+    # XXX: for callables, this technically adds the return type dep before args
+    for i in 0..<n.safeLen: deps(n[i])
+
+proc hasIncludes(n: PNode): bool =
+  result = false
   for a in n:
     if a.kind == nkIncludeStmt:
       return true
 
-proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode {.procvar.} =
+proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode =
   result = syntaxes.parseFile(fileIdx, graph.cache, graph.config)
   graph.addDep(s, fileIdx)
   graph.addIncludeDep(FileIndex s.position, fileIdx)
@@ -150,11 +130,11 @@ proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode,
   for a in n:
     if a.kind == nkIncludeStmt:
       for i in 0..<a.len:
-        var f = checkModuleName(graph.config, a.sons[i])
-        if f != InvalidFileIDX:
+        var f = checkModuleName(graph.config, a[i])
+        if f != InvalidFileIdx:
           if containsOrIncl(includedFiles, f.int):
             localError(graph.config, a.info, "recursive dependency: '$1'" %
-              toFilename(graph.config, f))
+              toMsgFilename(graph.config, f))
           else:
             let nn = includeModule(graph, module, f)
             let nnn = expandIncludes(graph, module, nn, modulePath,
@@ -206,53 +186,54 @@ proc mergeSections(conf: ConfigRef; comps: seq[seq[DepN]], res: PNode) =
         # need to merge them
         var sn = newNode(kind)
         for dn in cs:
-          sn.add dn.pnode.sons[0]
+          sn.add dn.pnode[0]
         res.add sn
       else:
-          # Problematic circular dependency, we arrange the nodes into
-          # their original relative order and make sure to re-merge
-          # consecutive type and const sections
-          var wmsg = "Circular dependency detected. reorder pragma may not be able to" &
-            " reorder some nodes properely"
-          when defined(debugReorder):
-            wmsg &= ":\n"
-            for i in 0..<cs.len-1:
-                for j in i..<cs.len:
-                  for ci in 0..<cs[i].kids.len:
-                    if cs[i].kids[ci].id == cs[j].id:
-                      wmsg &= "line " & $cs[i].pnode.info.line &
-                        " depends on line " & $cs[j].pnode.info.line &
-                        ": " & cs[i].expls[ci] & "\n"
-            for j in 0..<cs.len-1:
-                for ci in 0..<cs[^1].kids.len:
-                  if cs[^1].kids[ci].id == cs[j].id:
-                    wmsg &= "line " & $cs[^1].pnode.info.line &
-                      " depends on line " & $cs[j].pnode.info.line &
-                      ": " & cs[^1].expls[ci] & "\n"
-          message(conf, cs[0].pnode.info, warnUser, wmsg)
-
-          var i = 0
-          while i < cs.len:
-            if cs[i].pnode.kind in {nkTypeSection, nkConstSection}:
-              let ckind = cs[i].pnode.kind
-              var sn = newNode(ckind)
+        # Problematic circular dependency, we arrange the nodes into
+        # their original relative order and make sure to re-merge
+        # consecutive type and const sections
+        var wmsg = "Circular dependency detected. `codeReordering` pragma may not be able to" &
+          " reorder some nodes properly"
+        when defined(nimDebugReorder):
+          wmsg &= ":\n"
+          for i in 0..<cs.len-1:
+            for j in i..<cs.len:
+              for ci in 0..<cs[i].kids.len:
+                if cs[i].kids[ci].id == cs[j].id:
+                  wmsg &= "line " & $cs[i].pnode.info.line &
+                    " depends on line " & $cs[j].pnode.info.line &
+                    ": " & cs[i].expls[ci] & "\n"
+          for j in 0..<cs.len-1:
+            for ci in 0..<cs[^1].kids.len:
+              if cs[^1].kids[ci].id == cs[j].id:
+                wmsg &= "line " & $cs[^1].pnode.info.line &
+                  " depends on line " & $cs[j].pnode.info.line &
+                  ": " & cs[^1].expls[ci] & "\n"
+        message(conf, cs[0].pnode.info, warnUser, wmsg)
+
+        var i = 0
+        while i < cs.len:
+          if cs[i].pnode.kind in {nkTypeSection, nkConstSection}:
+            let ckind = cs[i].pnode.kind
+            var sn = newNode(ckind)
+            sn.add cs[i].pnode[0]
+            inc i
+            while i < cs.len and cs[i].pnode.kind == ckind:
               sn.add cs[i].pnode[0]
               inc i
-              while i < cs.len and cs[i].pnode.kind == ckind :
-                sn.add cs[i].pnode[0]
-                inc i
-              res.add sn
-            else:
-              res.add cs[i].pnode
-              inc i
+            res.add sn
+          else:
+            res.add cs[i].pnode
+            inc i
 
 proc hasImportStmt(n: PNode): bool =
   # Checks if the node is an import statement or
   # i it contains one
   case n.kind
   of nkImportStmt, nkFromStmt, nkImportExceptStmt:
-    return true
+    result = true
   of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
+    result = false
     for a in n:
       if a.hasImportStmt:
         return true
@@ -273,6 +254,7 @@ proc hasCommand(n: PNode): bool =
   of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse,
       nkStaticStmt, nkLetSection, nkConstSection, nkVarSection,
       nkIdentDefs:
+    result = false
     for a in n:
       if a.hasCommand:
         return true
@@ -285,23 +267,25 @@ proc hasCommand(n: DepN): bool =
   result = bool(n.hCmd)
 
 proc hasAccQuoted(n: PNode): bool =
+  result = false
   if n.kind == nkAccQuoted:
     return true
   for a in n:
     if hasAccQuoted(a):
       return true
 
-const extandedProcDefs = procDefs + {nkMacroDef,  nkTemplateDef}
+const extendedProcDefs = procDefs + {nkMacroDef, nkTemplateDef}
 
 proc hasAccQuotedDef(n: PNode): bool =
   # Checks if the node is a function, macro, template ...
   # with a quoted name or if it contains one
   case n.kind
-  of extandedProcDefs:
+  of extendedProcDefs:
     result = n[0].hasAccQuoted
   of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
+    result = false
     for a in n:
-      if a.hasAccQuotedDef:
+      if hasAccQuotedDef(a):
         return true
   else:
     result = false
@@ -317,9 +301,10 @@ proc hasBody(n: PNode): bool =
   case n.kind
   of nkCommand, nkCall:
     result = true
-  of extandedProcDefs:
+  of extendedProcDefs:
     result = n[^1].kind == nkStmtList
   of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
+    result = false
     for a in n:
       if a.hasBody:
         return true
@@ -332,15 +317,24 @@ proc hasBody(n: DepN): bool =
   result = bool(n.hB)
 
 proc intersects(s1, s2: IntSet): bool =
+  result = false
   for a in s1:
     if s2.contains(a):
       return true
 
+proc hasPushOrPopPragma(n: DepN): bool =
+  # Checks if the tree node has some pragmas that do not
+  # play well with reordering, like the push/pop pragma
+  # no crossing for push/pop barrier
+  let a = n.pnode
+  result = a.kind == nkPragma and a[0].kind == nkIdent and
+      (a[0].ident.s == "push" or a[0].ident.s == "pop")
+
 proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
   # Build a dependency graph
   result = newSeqOfCap[DepN](deps.len)
   for i in 0..<deps.len:
-    result.add newDepN(i, n.sons[i])
+    result.add newDepN(i, n[i])
   for i in 0..<deps.len:
     var ni = result[i]
     let uses = deps[i][1]
@@ -353,13 +347,13 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
       if j < i and nj.hasCommand and niHasCmd:
         # Preserve order for commands and calls
         ni.kids.add nj
-        when defined(debugReorder):
+        when defined(nimDebugReorder):
           ni.expls.add "both have commands and one comes after the other"
       elif j < i and nj.hasImportStmt:
         # Every node that comes after an import statement must
         # depend on that import
         ni.kids.add nj
-        when defined(debugReorder):
+        when defined(nimDebugReorder):
           ni.expls.add "parent is, or contains, an import statement and child comes after it"
       elif j < i and niHasBody and nj.hasAccQuotedDef:
         # Every function, macro, template... with a body depends
@@ -367,21 +361,28 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
         # That's because it is hard to detect the use of functions
         # like "[]=", "[]", "or" ... in their bodies.
         ni.kids.add nj
-        when defined(debugReorder):
+        when defined(nimDebugReorder):
           ni.expls.add "one declares a quoted identifier and the other has a body and comes after it"
       elif j < i and niHasBody and not nj.hasBody and
         intersects(deps[i][0], declares):
           # Keep function declaration before function definition
           ni.kids.add nj
-          when defined(debugReorder):
+          when defined(nimDebugReorder):
             for dep in deps[i][0]:
               if dep in declares:
                 ni.expls.add "one declares \"" & idNames[dep] & "\" and the other defines it"
+      elif hasPushOrPopPragma(nj):
+        # Every node that comes after a push/pop pragma must
+        # depend on it; vice versa
+        if j < i:
+          ni.kids.add nj
+        else:
+          nj.kids.add ni
       else:
         for d in declares:
           if uses.contains(d):
             ni.kids.add nj
-            when defined(debugReorder):
+            when defined(nimDebugReorder):
               ni.expls.add "one declares \"" & idNames[d] & "\" and the other uses it"
 
 proc strongConnect(v: var DepN, idx: var int, s: var seq[DepN],
@@ -410,24 +411,14 @@ proc strongConnect(v: var DepN, idx: var int, s: var seq[DepN],
 proc getStrongComponents(g: var DepG): seq[seq[DepN]] =
   ## Tarjan's algorithm. Performs a topological sort
   ## and detects strongly connected components.
-  result = newSeq[seq[DepN]]()
-  var s = newSeq[DepN]()
+  result = @[]
+  var s: seq[DepN] = @[]
   var idx = 0
   for v in g.mitems:
     if v.idx < 0:
       strongConnect(v, idx, s, result)
 
-proc hasForbiddenPragma(n: PNode): bool =
-  # Checks if the tree node has some pragmas that do not
-  # play well with reordering, like the push/pop pragma
-  for a in n:
-    if a.kind == nkPragma and a[0].kind == nkIdent and
-        a[0].ident.s == "push":
-          return true
-
 proc reorder*(graph: ModuleGraph, n: PNode, module: PSym): PNode =
-  if n.hasForbiddenPragma:
-    return n
   var includedFiles = initIntSet()
   let mpath = toFullPath(graph.config, module.fileIdx)
   let n = expandIncludes(graph, module, n, mpath,
diff --git a/compiler/rod.nim b/compiler/rod.nim
deleted file mode 100644
index f9208f5dc..000000000
--- a/compiler/rod.nim
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2017 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module implements the canonalization for the various caching mechanisms.
-
-import ast, idgen, lineinfos, msgs, incremental, modulegraphs
-
-when not nimIncremental:
-  template setupModuleCache*(g: ModuleGraph) = discard
-  template storeNode*(g: ModuleGraph; module: PSym; n: PNode) = discard
-  template loadNode*(g: ModuleGraph; module: PSym): PNode = newNode(nkStmtList)
-
-  template getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: string): int = getID()
-
-  template addModuleDep*(g: ModuleGraph; module, fileIdx: FileIndex; isIncludeFile: bool) = discard
-
-  template storeRemaining*(g: ModuleGraph; module: PSym) = discard
-
-else:
-  include rodimpl
-
-  # idea for testing all this logic: *Always* load the AST from the DB, whether
-  # we already have it in RAM or not!
diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim
deleted file mode 100644
index 7d24e4e67..000000000
--- a/compiler/rodimpl.nim
+++ /dev/null
@@ -1,885 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2018 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module implements the new compilation cache.
-
-import strutils, os, intsets, tables, ropes, db_sqlite, msgs, options, types,
-  renderer, rodutils, idents, astalgo, btrees, magicsys, cgmeth, extccomp,
-  btrees, trees, condsyms, nversion
-
-## Todo:
-## - Dependency computation should use *signature* hashes in order to
-##   avoid recompiling dependent modules.
-## - Patch the rest of the compiler to do lazy loading of proc bodies.
-## - Patch the C codegen to cache proc bodies and maybe types.
-
-template db(): DbConn = g.incr.db
-
-proc encodeConfig(g: ModuleGraph): string =
-  result = newStringOfCap(100)
-  result.add RodFileVersion
-  for d in definedSymbolNames(g.config.symbols):
-    result.add ' '
-    result.add d
-
-  template serialize(field) =
-    result.add ' '
-    result.add($g.config.field)
-
-  depConfigFields(serialize)
-
-proc needsRecompile(g: ModuleGraph; fileIdx: FileIndex; fullpath: string;
-                    cycleCheck: var IntSet): bool =
-  let root = db.getRow(sql"select id, fullhash from filenames where fullpath = ?",
-    fullpath)
-  if root[0].len == 0: return true
-  if root[1] != hashFileCached(g.config, fileIdx, fullpath):
-    return true
-  # cycle detection: assume "not changed" is correct.
-  if cycleCheck.containsOrIncl(int fileIdx):
-    return false
-  # check dependencies (recursively):
-  for row in db.fastRows(sql"select fullpath from filenames where id in (select dependency from deps where module = ?)",
-                         root[0]):
-    let dep = row[0]
-    if needsRecompile(g, g.config.fileInfoIdx(dep), dep, cycleCheck):
-      return true
-  return false
-
-proc getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: string): int =
-  if g.config.symbolFiles in {disabledSf, writeOnlySf} or
-     g.incr.configChanged:
-    return getID()
-  let module = g.incr.db.getRow(
-    sql"select id, fullHash, nimid from modules where fullpath = ?", fullpath)
-  let currentFullhash = hashFileCached(g.config, fileIdx, fullpath)
-  if module[0].len == 0:
-    result = getID()
-    db.exec(sql"insert into modules(fullpath, interfHash, fullHash, nimid) values (?, ?, ?, ?)",
-      fullpath, "", currentFullhash, result)
-  else:
-    result = parseInt(module[2])
-    if currentFullhash == module[1]:
-      # not changed, so use the cached AST:
-      doAssert(result != 0)
-      var cycleCheck = initIntSet()
-      if not needsRecompile(g, fileIdx, fullpath, cycleCheck):
-        echo "cached successfully! ", fullpath
-        return -result
-    db.exec(sql"update modules set fullHash = ? where id = ?", currentFullhash, module[0])
-    db.exec(sql"delete from deps where module = ?", module[0])
-    db.exec(sql"delete from types where module = ?", module[0])
-    db.exec(sql"delete from syms where module = ?", module[0])
-    db.exec(sql"delete from toplevelstmts where module = ?", module[0])
-    db.exec(sql"delete from statics where module = ?", module[0])
-
-proc pushType(w: var Writer, t: PType) =
-  if not containsOrIncl(w.tmarks, t.id):
-    w.tstack.add(t)
-
-proc pushSym(w: var Writer, s: PSym) =
-  if not containsOrIncl(w.smarks, s.id):
-    w.sstack.add(s)
-
-template w: untyped = g.incr.w
-
-proc encodeNode(g: ModuleGraph; fInfo: TLineInfo, n: PNode,
-                result: var string) =
-  if n == nil:
-    # nil nodes have to be stored too:
-    result.add("()")
-    return
-  result.add('(')
-  encodeVInt(ord(n.kind), result)
-  # we do not write comments for now
-  # Line information takes easily 20% or more of the filesize! Therefore we
-  # omit line information if it is the same as the parent's line information:
-  if fInfo.fileIndex != n.info.fileIndex:
-    result.add('?')
-    encodeVInt(n.info.col, result)
-    result.add(',')
-    encodeVInt(int n.info.line, result)
-    result.add(',')
-    encodeVInt(toDbFileId(g.incr, g.config, n.info.fileIndex), result)
-  elif fInfo.line != n.info.line:
-    result.add('?')
-    encodeVInt(n.info.col, result)
-    result.add(',')
-    encodeVInt(int n.info.line, result)
-  elif fInfo.col != n.info.col:
-    result.add('?')
-    encodeVInt(n.info.col, result)
-  # No need to output the file index, as this is the serialization of one
-  # file.
-  let f = n.flags * PersistentNodeFlags
-  if f != {}:
-    result.add('$')
-    encodeVInt(cast[int32](f), result)
-  if n.typ != nil:
-    result.add('^')
-    encodeVInt(n.typ.id, result)
-    pushType(w, n.typ)
-  case n.kind
-  of nkCharLit..nkUInt64Lit:
-    if n.intVal != 0:
-      result.add('!')
-      encodeVBiggestInt(n.intVal, result)
-  of nkFloatLit..nkFloat64Lit:
-    if n.floatVal != 0.0:
-      result.add('!')
-      encodeStr($n.floatVal, result)
-  of nkStrLit..nkTripleStrLit:
-    if n.strVal != "":
-      result.add('!')
-      encodeStr(n.strVal, result)
-  of nkIdent:
-    result.add('!')
-    encodeStr(n.ident.s, result)
-  of nkSym:
-    result.add('!')
-    encodeVInt(n.sym.id, result)
-    pushSym(w, n.sym)
-  else:
-    for i in countup(0, sonsLen(n) - 1):
-      encodeNode(g, n.info, n.sons[i], result)
-  add(result, ')')
-
-proc encodeLoc(g: ModuleGraph; loc: TLoc, result: var string) =
-  var oldLen = result.len
-  result.add('<')
-  if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
-  if loc.storage != low(loc.storage):
-    add(result, '*')
-    encodeVInt(ord(loc.storage), result)
-  if loc.flags != {}:
-    add(result, '$')
-    encodeVInt(cast[int32](loc.flags), result)
-  if loc.lode != nil:
-    add(result, '^')
-    encodeNode(g, unknownLineInfo(), loc.lode, result)
-  if loc.r != nil:
-    add(result, '!')
-    encodeStr($loc.r, result)
-  if oldLen + 1 == result.len:
-    # no data was necessary, so remove the '<' again:
-    setLen(result, oldLen)
-  else:
-    add(result, '>')
-
-proc encodeType(g: ModuleGraph, t: PType, result: var string) =
-  if t == nil:
-    # nil nodes have to be stored too:
-    result.add("[]")
-    return
-  # we need no surrounding [] here because the type is in a line of its own
-  if t.kind == tyForward: internalError(g.config, "encodeType: tyForward")
-  # for the new rodfile viewer we use a preceding [ so that the data section
-  # can easily be disambiguated:
-  add(result, '[')
-  encodeVInt(ord(t.kind), result)
-  add(result, '+')
-  encodeVInt(t.id, result)
-  if t.n != nil:
-    encodeNode(g, unknownLineInfo(), t.n, result)
-  if t.flags != {}:
-    add(result, '$')
-    encodeVInt(cast[int32](t.flags), result)
-  if t.callConv != low(t.callConv):
-    add(result, '?')
-    encodeVInt(ord(t.callConv), result)
-  if t.owner != nil:
-    add(result, '*')
-    encodeVInt(t.owner.id, result)
-    pushSym(w, t.owner)
-  if t.sym != nil:
-    add(result, '&')
-    encodeVInt(t.sym.id, result)
-    pushSym(w, t.sym)
-  if t.size != - 1:
-    add(result, '/')
-    encodeVBiggestInt(t.size, result)
-  if t.align != 2:
-    add(result, '=')
-    encodeVInt(t.align, result)
-  if t.lockLevel.ord != UnspecifiedLockLevel.ord:
-    add(result, '\14')
-    encodeVInt(t.lockLevel.int16, result)
-  if t.destructor != nil and t.destructor.id != 0:
-    add(result, '\15')
-    encodeVInt(t.destructor.id, result)
-    pushSym(w, t.destructor)
-  if t.deepCopy != nil:
-    add(result, '\16')
-    encodeVInt(t.deepcopy.id, result)
-    pushSym(w, t.deepcopy)
-  if t.assignment != nil:
-    add(result, '\17')
-    encodeVInt(t.assignment.id, result)
-    pushSym(w, t.assignment)
-  if t.sink != nil:
-    add(result, '\18')
-    encodeVInt(t.sink.id, result)
-    pushSym(w, t.sink)
-  for i, s in items(t.methods):
-    add(result, '\19')
-    encodeVInt(i, result)
-    add(result, '\20')
-    encodeVInt(s.id, result)
-    pushSym(w, s)
-  encodeLoc(g, t.loc, result)
-  for i in countup(0, sonsLen(t) - 1):
-    if t.sons[i] == nil:
-      add(result, "^()")
-    else:
-      add(result, '^')
-      encodeVInt(t.sons[i].id, result)
-      pushType(w, t.sons[i])
-
-proc encodeLib(g: ModuleGraph, lib: PLib, info: TLineInfo, result: var string) =
-  add(result, '|')
-  encodeVInt(ord(lib.kind), result)
-  add(result, '|')
-  encodeStr($lib.name, result)
-  add(result, '|')
-  encodeNode(g, info, lib.path, result)
-
-proc encodeInstantiations(g: ModuleGraph; s: seq[PInstantiation];
-                          result: var string) =
-  for t in s:
-    result.add('\15')
-    encodeVInt(t.sym.id, result)
-    pushSym(w, t.sym)
-    for tt in t.concreteTypes:
-      result.add('\17')
-      encodeVInt(tt.id, result)
-      pushType(w, tt)
-    result.add('\20')
-    encodeVInt(t.compilesId, result)
-
-proc encodeSym(g: ModuleGraph, s: PSym, result: var string) =
-  if s == nil:
-    # nil nodes have to be stored too:
-    result.add("{}")
-    return
-  # we need no surrounding {} here because the symbol is in a line of its own
-  encodeVInt(ord(s.kind), result)
-  result.add('+')
-  encodeVInt(s.id, result)
-  result.add('&')
-  encodeStr(s.name.s, result)
-  if s.typ != nil:
-    result.add('^')
-    encodeVInt(s.typ.id, result)
-    pushType(w, s.typ)
-  result.add('?')
-  if s.info.col != -1'i16: encodeVInt(s.info.col, result)
-  result.add(',')
-  encodeVInt(int s.info.line, result)
-  result.add(',')
-  encodeVInt(toDbFileId(g.incr, g.config, s.info.fileIndex), result)
-  if s.owner != nil:
-    result.add('*')
-    encodeVInt(s.owner.id, result)
-    pushSym(w, s.owner)
-  if s.flags != {}:
-    result.add('$')
-    encodeVInt(cast[int32](s.flags), result)
-  if s.magic != mNone:
-    result.add('@')
-    encodeVInt(ord(s.magic), result)
-  result.add('!')
-  encodeVInt(cast[int32](s.options), result)
-  if s.position != 0:
-    result.add('%')
-    encodeVInt(s.position, result)
-  if s.offset != - 1:
-    result.add('`')
-    encodeVInt(s.offset, result)
-  encodeLoc(g, s.loc, result)
-  if s.annex != nil: encodeLib(g, s.annex, s.info, result)
-  if s.constraint != nil:
-    add(result, '#')
-    encodeNode(g, unknownLineInfo(), s.constraint, result)
-  case s.kind
-  of skType, skGenericParam:
-    for t in s.typeInstCache:
-      result.add('\14')
-      encodeVInt(t.id, result)
-      pushType(w, t)
-  of routineKinds:
-    encodeInstantiations(g, s.procInstCache, result)
-    if s.gcUnsafetyReason != nil:
-      result.add('\16')
-      encodeVInt(s.gcUnsafetyReason.id, result)
-      pushSym(w, s.gcUnsafetyReason)
-  of skModule, skPackage:
-    encodeInstantiations(g, s.usedGenerics, result)
-    # we don't serialize:
-    #tab*: TStrTable         # interface table for modules
-  of skLet, skVar, skField, skForVar:
-    if s.guard != nil:
-      result.add('\18')
-      encodeVInt(s.guard.id, result)
-      pushSym(w, s.guard)
-    if s.bitsize != 0:
-      result.add('\19')
-      encodeVInt(s.bitsize, result)
-  else: discard
-  # lazy loading will soon reload the ast lazily, so the ast needs to be
-  # the last entry of a symbol:
-  if s.ast != nil:
-    # we used to attempt to save space here by only storing a dummy AST if
-    # it is not necessary, but Nim's heavy compile-time evaluation features
-    # make that unfeasible nowadays:
-    encodeNode(g, s.info, s.ast, result)
-
-proc storeSym(g: ModuleGraph; s: PSym) =
-  if sfForward in s.flags and s.kind != skModule:
-    w.forwardedSyms.add s
-    return
-  var buf = newStringOfCap(160)
-  encodeSym(g, s, buf)
-  # XXX only store the name for exported symbols in order to speed up lookup
-  # times once we enable the skStub logic.
-  let m = getModule(s)
-  let mid = if m == nil: 0 else: abs(m.id)
-  db.exec(sql"insert into syms(nimid, module, name, data, exported) values (?, ?, ?, ?, ?)",
-    s.id, mid, s.name.s, buf, ord(sfExported in s.flags))
-
-proc storeType(g: ModuleGraph; t: PType) =
-  var buf = newStringOfCap(160)
-  encodeType(g, t, buf)
-  let m = if t.owner != nil: getModule(t.owner) else: nil
-  let mid = if m == nil: 0 else: abs(m.id)
-  db.exec(sql"insert into types(nimid, module, data) values (?, ?, ?)",
-    t.id, mid, buf)
-
-proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) =
-  if g.config.symbolFiles == disabledSf: return
-  var buf = newStringOfCap(160)
-  encodeNode(g, module.info, n, buf)
-  db.exec(sql"insert into toplevelstmts(module, position, data) values (?, ?, ?)",
-    abs(module.id), module.offset, buf)
-  inc module.offset
-  var i = 0
-  while true:
-    if i > 10_000:
-      doAssert false, "loop never ends!"
-    if w.sstack.len > 0:
-      let s = w.sstack.pop()
-      when false:
-        echo "popped ", s.name.s, " ", s.id
-      storeSym(g, s)
-    elif w.tstack.len > 0:
-      let t = w.tstack.pop()
-      storeType(g, t)
-      when false:
-        echo "popped type ", typeToString(t), " ", t.id
-    else:
-      break
-    inc i
-
-proc recordStmt*(g: ModuleGraph; module: PSym; n: PNode) =
-  storeNode(g, module, n)
-
-proc storeRemaining*(g: ModuleGraph; module: PSym) =
-  if g.config.symbolFiles == disabledSf: return
-  var stillForwarded: seq[PSym] = @[]
-  for s in w.forwardedSyms:
-    if sfForward notin s.flags:
-      storeSym(g, s)
-    else:
-      stillForwarded.add s
-  swap w.forwardedSyms, stillForwarded
-
-# ---------------- decoder -----------------------------------
-
-type
-  BlobReader = object
-    s: string
-    pos: int
-
-using
-  b: var BlobReader
-  g: ModuleGraph
-
-proc loadSym(g; id: int, info: TLineInfo): PSym
-proc loadType(g; id: int, info: TLineInfo): PType
-
-proc decodeLineInfo(g; b; info: var TLineInfo) =
-  if b.s[b.pos] == '?':
-    inc(b.pos)
-    if b.s[b.pos] == ',': info.col = -1'i16
-    else: info.col = int16(decodeVInt(b.s, b.pos))
-    if b.s[b.pos] == ',':
-      inc(b.pos)
-      if b.s[b.pos] == ',': info.line = 0'u16
-      else: info.line = uint16(decodeVInt(b.s, b.pos))
-      if b.s[b.pos] == ',':
-        inc(b.pos)
-        info.fileIndex = fromDbFileId(g.incr, g.config, decodeVInt(b.s, b.pos))
-
-proc skipNode(b) =
-  assert b.s[b.pos] == '('
-  var par = 0
-  var pos = b.pos+1
-  while true:
-    case b.s[pos]
-    of ')':
-      if par == 0: break
-      dec par
-    of '(': inc par
-    else: discard
-    inc pos
-  b.pos = pos+1 # skip ')'
-
-proc decodeNodeLazyBody(g; b; fInfo: TLineInfo,
-                        belongsTo: PSym): PNode =
-  result = nil
-  if b.s[b.pos] == '(':
-    inc(b.pos)
-    if b.s[b.pos] == ')':
-      inc(b.pos)
-      return                  # nil node
-    result = newNodeI(TNodeKind(decodeVInt(b.s, b.pos)), fInfo)
-    decodeLineInfo(g, b, result.info)
-    if b.s[b.pos] == '$':
-      inc(b.pos)
-      result.flags = cast[TNodeFlags](int32(decodeVInt(b.s, b.pos)))
-    if b.s[b.pos] == '^':
-      inc(b.pos)
-      var id = decodeVInt(b.s, b.pos)
-      result.typ = loadType(g, id, result.info)
-    case result.kind
-    of nkCharLit..nkUInt64Lit:
-      if b.s[b.pos] == '!':
-        inc(b.pos)
-        result.intVal = decodeVBiggestInt(b.s, b.pos)
-    of nkFloatLit..nkFloat64Lit:
-      if b.s[b.pos] == '!':
-        inc(b.pos)
-        var fl = decodeStr(b.s, b.pos)
-        result.floatVal = parseFloat(fl)
-    of nkStrLit..nkTripleStrLit:
-      if b.s[b.pos] == '!':
-        inc(b.pos)
-        result.strVal = decodeStr(b.s, b.pos)
-      else:
-        result.strVal = ""
-    of nkIdent:
-      if b.s[b.pos] == '!':
-        inc(b.pos)
-        var fl = decodeStr(b.s, b.pos)
-        result.ident = g.cache.getIdent(fl)
-      else:
-        internalError(g.config, result.info, "decodeNode: nkIdent")
-    of nkSym:
-      if b.s[b.pos] == '!':
-        inc(b.pos)
-        var id = decodeVInt(b.s, b.pos)
-        result.sym = loadSym(g, id, result.info)
-      else:
-        internalError(g.config, result.info, "decodeNode: nkSym")
-    else:
-      var i = 0
-      while b.s[b.pos] != ')':
-        when false:
-          if belongsTo != nil and i == bodyPos:
-            addSonNilAllowed(result, nil)
-            belongsTo.offset = b.pos
-            skipNode(b)
-          else:
-            discard
-        addSonNilAllowed(result, decodeNodeLazyBody(g, b, result.info, nil))
-        inc i
-    if b.s[b.pos] == ')': inc(b.pos)
-    else: internalError(g.config, result.info, "decodeNode: ')' missing")
-  else:
-    internalError(g.config, fInfo, "decodeNode: '(' missing " & $b.pos)
-
-proc decodeNode(g; b; fInfo: TLineInfo): PNode =
-  result = decodeNodeLazyBody(g, b, fInfo, nil)
-
-proc decodeLoc(g; b; loc: var TLoc, info: TLineInfo) =
-  if b.s[b.pos] == '<':
-    inc(b.pos)
-    if b.s[b.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}:
-      loc.k = TLocKind(decodeVInt(b.s, b.pos))
-    else:
-      loc.k = low(loc.k)
-    if b.s[b.pos] == '*':
-      inc(b.pos)
-      loc.storage = TStorageLoc(decodeVInt(b.s, b.pos))
-    else:
-      loc.storage = low(loc.storage)
-    if b.s[b.pos] == '$':
-      inc(b.pos)
-      loc.flags = cast[TLocFlags](int32(decodeVInt(b.s, b.pos)))
-    else:
-      loc.flags = {}
-    if b.s[b.pos] == '^':
-      inc(b.pos)
-      loc.lode = decodeNode(g, b, info)
-      # rrGetType(b, decodeVInt(b.s, b.pos), info)
-    else:
-      loc.lode = nil
-    if b.s[b.pos] == '!':
-      inc(b.pos)
-      loc.r = rope(decodeStr(b.s, b.pos))
-    else:
-      loc.r = nil
-    if b.s[b.pos] == '>': inc(b.pos)
-    else: internalError(g.config, info, "decodeLoc " & b.s[b.pos])
-
-proc loadBlob(g; query: SqlQuery; id: int): BlobReader =
-  let blob = db.getValue(query, id)
-  if blob.len == 0:
-    internalError(g.config, "symbolfiles: cannot find ID " & $ id)
-  result = BlobReader(pos: 0)
-  shallowCopy(result.s, blob)
-  # ensure we can read without index checks:
-  result.s.add '\0'
-
-proc loadType(g; id: int; info: TLineInfo): PType =
-  result = g.incr.r.types.getOrDefault(id)
-  if result != nil: return result
-  var b = loadBlob(g, sql"select data from types where nimid = ?", id)
-
-  if b.s[b.pos] == '[':
-    inc(b.pos)
-    if b.s[b.pos] == ']':
-      inc(b.pos)
-      return                  # nil type
-  new(result)
-  result.kind = TTypeKind(decodeVInt(b.s, b.pos))
-  if b.s[b.pos] == '+':
-    inc(b.pos)
-    result.id = decodeVInt(b.s, b.pos)
-    setId(result.id)
-    #if debugIds: registerID(result)
-  else:
-    internalError(g.config, info, "decodeType: no id")
-  # here this also avoids endless recursion for recursive type
-  g.incr.r.types.add(result.id, result)
-  if b.s[b.pos] == '(': result.n = decodeNode(g, b, unknownLineInfo())
-  if b.s[b.pos] == '$':
-    inc(b.pos)
-    result.flags = cast[TTypeFlags](int32(decodeVInt(b.s, b.pos)))
-  if b.s[b.pos] == '?':
-    inc(b.pos)
-    result.callConv = TCallingConvention(decodeVInt(b.s, b.pos))
-  if b.s[b.pos] == '*':
-    inc(b.pos)
-    result.owner = loadSym(g, decodeVInt(b.s, b.pos), info)
-  if b.s[b.pos] == '&':
-    inc(b.pos)
-    result.sym = loadSym(g, decodeVInt(b.s, b.pos), info)
-  if b.s[b.pos] == '/':
-    inc(b.pos)
-    result.size = decodeVInt(b.s, b.pos)
-  else:
-    result.size = -1
-  if b.s[b.pos] == '=':
-    inc(b.pos)
-    result.align = decodeVInt(b.s, b.pos).int16
-  else:
-    result.align = 2
-
-  if b.s[b.pos] == '\14':
-    inc(b.pos)
-    result.lockLevel = decodeVInt(b.s, b.pos).TLockLevel
-  else:
-    result.lockLevel = UnspecifiedLockLevel
-
-  if b.s[b.pos] == '\15':
-    inc(b.pos)
-    result.destructor = loadSym(g, decodeVInt(b.s, b.pos), info)
-  if b.s[b.pos] == '\16':
-    inc(b.pos)
-    result.deepCopy = loadSym(g, decodeVInt(b.s, b.pos), info)
-  if b.s[b.pos] == '\17':
-    inc(b.pos)
-    result.assignment = loadSym(g, decodeVInt(b.s, b.pos), info)
-  if b.s[b.pos] == '\18':
-    inc(b.pos)
-    result.sink = loadSym(g, decodeVInt(b.s, b.pos), info)
-  while b.s[b.pos] == '\19':
-    inc(b.pos)
-    let x = decodeVInt(b.s, b.pos)
-    doAssert b.s[b.pos] == '\20'
-    inc(b.pos)
-    let y = loadSym(g, decodeVInt(b.s, b.pos), info)
-    result.methods.safeAdd((x, y))
-  decodeLoc(g, b, result.loc, info)
-  while b.s[b.pos] == '^':
-    inc(b.pos)
-    if b.s[b.pos] == '(':
-      inc(b.pos)
-      if b.s[b.pos] == ')': inc(b.pos)
-      else: internalError(g.config, info, "decodeType ^(" & b.s[b.pos])
-      rawAddSon(result, nil)
-    else:
-      let d = decodeVInt(b.s, b.pos)
-      rawAddSon(result, loadType(g, d, info))
-
-proc decodeLib(g; b; info: TLineInfo): PLib =
-  result = nil
-  if b.s[b.pos] == '|':
-    new(result)
-    inc(b.pos)
-    result.kind = TLibKind(decodeVInt(b.s, b.pos))
-    if b.s[b.pos] != '|': internalError(g.config, "decodeLib: 1")
-    inc(b.pos)
-    result.name = rope(decodeStr(b.s, b.pos))
-    if b.s[b.pos] != '|': internalError(g.config, "decodeLib: 2")
-    inc(b.pos)
-    result.path = decodeNode(g, b, info)
-
-proc decodeInstantiations(g; b; info: TLineInfo;
-                          s: var seq[PInstantiation]) =
-  while b.s[b.pos] == '\15':
-    inc(b.pos)
-    var ii: PInstantiation
-    new ii
-    ii.sym = loadSym(g, decodeVInt(b.s, b.pos), info)
-    ii.concreteTypes = @[]
-    while b.s[b.pos] == '\17':
-      inc(b.pos)
-      ii.concreteTypes.add loadType(g, decodeVInt(b.s, b.pos), info)
-    if b.s[b.pos] == '\20':
-      inc(b.pos)
-      ii.compilesId = decodeVInt(b.s, b.pos)
-    s.safeAdd ii
-
-proc loadSymFromBlob(g; b; info: TLineInfo): PSym =
-  if b.s[b.pos] == '{':
-    inc(b.pos)
-    if b.s[b.pos] == '}':
-      inc(b.pos)
-      return                  # nil sym
-  var k = TSymKind(decodeVInt(b.s, b.pos))
-  var id: int
-  if b.s[b.pos] == '+':
-    inc(b.pos)
-    id = decodeVInt(b.s, b.pos)
-    setId(id)
-  else:
-    internalError(g.config, info, "decodeSym: no id")
-  var ident: PIdent
-  if b.s[b.pos] == '&':
-    inc(b.pos)
-    ident = g.cache.getIdent(decodeStr(b.s, b.pos))
-  else:
-    internalError(g.config, info, "decodeSym: no ident")
-  #echo "decoding: {", ident.s
-  new(result)
-  result.id = id
-  result.kind = k
-  result.name = ident         # read the rest of the symbol description:
-  g.incr.r.syms.add(result.id, result)
-  if b.s[b.pos] == '^':
-    inc(b.pos)
-    result.typ = loadType(g, decodeVInt(b.s, b.pos), info)
-  decodeLineInfo(g, b, result.info)
-  if b.s[b.pos] == '*':
-    inc(b.pos)
-    result.owner = loadSym(g, decodeVInt(b.s, b.pos), result.info)
-  if b.s[b.pos] == '$':
-    inc(b.pos)
-    result.flags = cast[TSymFlags](int32(decodeVInt(b.s, b.pos)))
-  if b.s[b.pos] == '@':
-    inc(b.pos)
-    result.magic = TMagic(decodeVInt(b.s, b.pos))
-  if b.s[b.pos] == '!':
-    inc(b.pos)
-    result.options = cast[TOptions](int32(decodeVInt(b.s, b.pos)))
-  if b.s[b.pos] == '%':
-    inc(b.pos)
-    result.position = decodeVInt(b.s, b.pos)
-  if b.s[b.pos] == '`':
-    inc(b.pos)
-    result.offset = decodeVInt(b.s, b.pos)
-  else:
-    result.offset = -1
-  decodeLoc(g, b, result.loc, result.info)
-  result.annex = decodeLib(g, b, info)
-  if b.s[b.pos] == '#':
-    inc(b.pos)
-    result.constraint = decodeNode(g, b, unknownLineInfo())
-  case result.kind
-  of skType, skGenericParam:
-    while b.s[b.pos] == '\14':
-      inc(b.pos)
-      result.typeInstCache.safeAdd loadType(g, decodeVInt(b.s, b.pos), result.info)
-  of routineKinds:
-    decodeInstantiations(g, b, result.info, result.procInstCache)
-    if b.s[b.pos] == '\16':
-      inc(b.pos)
-      result.gcUnsafetyReason = loadSym(g, decodeVInt(b.s, b.pos), result.info)
-  of skModule, skPackage:
-    decodeInstantiations(g, b, result.info, result.usedGenerics)
-  of skLet, skVar, skField, skForVar:
-    if b.s[b.pos] == '\18':
-      inc(b.pos)
-      result.guard = loadSym(g, decodeVInt(b.s, b.pos), result.info)
-    if b.s[b.pos] == '\19':
-      inc(b.pos)
-      result.bitsize = decodeVInt(b.s, b.pos).int16
-  else: discard
-
-  if b.s[b.pos] == '(':
-    #if result.kind in routineKinds:
-    #  result.ast = decodeNodeLazyBody(b, result.info, result)
-    #else:
-    result.ast = decodeNode(g, b, result.info)
-  if sfCompilerProc in result.flags:
-    registerCompilerProc(g, result)
-    #echo "loading ", result.name.s
-
-proc loadSym(g; id: int; info: TLineInfo): PSym =
-  result = g.incr.r.syms.getOrDefault(id)
-  if result != nil: return result
-  var b = loadBlob(g, sql"select data from syms where nimid = ?", id)
-  result = loadSymFromBlob(g, b, info)
-  doAssert id == result.id, "symbol ID is not consistent!"
-
-proc loadModuleSymTab(g; module: PSym) =
-  ## goal: fill  module.tab
-  g.incr.r.syms.add(module.id, module)
-  for row in db.fastRows(sql"select nimid, data from syms where module = ? and exported = 1", abs(module.id)):
-    let id = parseInt(row[0])
-    var s = g.incr.r.syms.getOrDefault(id)
-    if s == nil:
-      var b = BlobReader(pos: 0)
-      shallowCopy(b.s, row[1])
-      # ensure we can read without index checks:
-      b.s.add '\0'
-      s = loadSymFromBlob(g, b, module.info)
-    assert s != nil
-    strTableAdd(module.tab, s)
-  if sfSystemModule in module.flags:
-    g.systemModule = module
-
-proc replay(g: ModuleGraph; module: PSym; n: PNode) =
-  # XXX check if we need to replay nkStaticStmt here.
-  case n.kind
-  #of nkStaticStmt:
-    #evalStaticStmt(module, g, n[0], module)
-    #of nkVarSection, nkLetSection:
-    #  nkVarSections are already covered by the vmgen which produces nkStaticStmt
-  of nkMethodDef:
-    methodDef(g, n[namePos].sym, fromCache=true)
-  of nkCommentStmt:
-    # pragmas are complex and can be user-overriden via templates. So
-    # instead of using the original ``nkPragma`` nodes, we rely on the
-    # fact that pragmas.nim was patched to produce specialized recorded
-    # statements for us in the form of ``nkCommentStmt`` with (key, value)
-    # pairs. Ordinary nkCommentStmt nodes never have children so this is
-    # not ambiguous.
-    # Fortunately only a tiny subset of the available pragmas need to
-    # be replayed here. This is always a subset of ``pragmas.stmtPragmas``.
-    if n.len >= 2:
-      internalAssert g.config, n[0].kind == nkStrLit and n[1].kind == nkStrLit
-      case n[0].strVal
-      of "hint": message(g.config, n.info, hintUser, n[1].strVal)
-      of "warning": message(g.config, n.info, warnUser, n[1].strVal)
-      of "error": localError(g.config, n.info, errUser, n[1].strVal)
-      of "compile":
-        internalAssert g.config, n.len == 3 and n[2].kind == nkStrLit
-        var cf = Cfile(cname: n[1].strVal, obj: n[2].strVal,
-                       flags: {CfileFlag.External})
-        extccomp.addExternalFileToCompile(g.config, cf)
-      of "link":
-        extccomp.addExternalFileToLink(g.config, n[1].strVal)
-      of "passl":
-        extccomp.addLinkOption(g.config, n[1].strVal)
-      of "passc":
-        extccomp.addCompileOption(g.config, n[1].strVal)
-      of "cppdefine":
-        options.cppDefine(g.config, n[1].strVal)
-      of "inc":
-        let destKey = n[1].strVal
-        let by = n[2].intVal
-        let v = getOrDefault(g.cacheCounters, destKey)
-        g.cacheCounters[destKey] = v+by
-      of "put":
-        let destKey = n[1].strVal
-        let key = n[2].strVal
-        let val = n[3]
-        if not contains(g.cacheTables, destKey):
-          g.cacheTables[destKey] = initBTree[string, PNode]()
-        if not contains(g.cacheTables[destKey], key):
-          g.cacheTables[destKey].add(key, val)
-        else:
-          internalError(g.config, n.info, "key already exists: " & key)
-      of "incl":
-        let destKey = n[1].strVal
-        let val = n[2]
-        if not contains(g.cacheSeqs, destKey):
-          g.cacheSeqs[destKey] = newTree(nkStmtList, val)
-        else:
-          block search:
-            for existing in g.cacheSeqs[destKey]:
-              if exprStructuralEquivalent(existing, val, strictSymEquality=true):
-                break search
-            g.cacheSeqs[destKey].add val
-      of "add":
-        let destKey = n[1].strVal
-        let val = n[2]
-        if not contains(g.cacheSeqs, destKey):
-          g.cacheSeqs[destKey] = newTree(nkStmtList, val)
-        else:
-          g.cacheSeqs[destKey].add val
-      else:
-        internalAssert g.config, false
-  of nkImportStmt:
-    for x in n:
-      internalAssert g.config, x.kind == nkStrLit
-      let imported = g.importModuleCallback(g, module, fileInfoIdx(g.config, n[0].strVal))
-      internalAssert g.config, imported.id < 0
-  of nkStmtList, nkStmtListExpr:
-    for x in n: replay(g, module, x)
-  else: discard "nothing to do for this node"
-
-proc loadNode*(g: ModuleGraph; module: PSym): PNode =
-  loadModuleSymTab(g, module)
-  result = newNodeI(nkStmtList, module.info)
-  for row in db.rows(sql"select data from toplevelstmts where module = ? order by position asc",
-                        abs module.id):
-
-    var b = BlobReader(pos: 0)
-    # ensure we can read without index checks:
-    b.s = row[0] & '\0'
-    result.add decodeNode(g, b, module.info)
-
-  db.exec(sql"insert into controlblock(idgen) values (?)", gFrontEndId)
-  replay(g, module, result)
-
-proc setupModuleCache*(g: ModuleGraph) =
-  if g.config.symbolFiles == disabledSf: return
-  g.recordStmt = recordStmt
-  let dbfile = getNimcacheDir(g.config) / "rodfiles.db"
-  if g.config.symbolFiles == writeOnlySf:
-    removeFile(dbfile)
-  if not fileExists(dbfile):
-    db = open(connection=dbfile, user="nim", password="",
-              database="nim")
-    createDb(db)
-    db.exec(sql"insert into config(config) values (?)", encodeConfig(g))
-  else:
-    db = open(connection=dbfile, user="nim", password="",
-              database="nim")
-    let oldConfig = db.getValue(sql"select config from config")
-    g.incr.configChanged = oldConfig != encodeConfig(g)
-  db.exec(sql"pragma journal_mode=off")
-  db.exec(sql"pragma SYNCHRONOUS=off")
-  db.exec(sql"pragma LOCKING_MODE=exclusive")
-  let lastId = db.getValue(sql"select max(idgen) from controlblock")
-  if lastId.len > 0:
-    idgen.setId(parseInt lastId)
diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim
index 66d7f63c2..5355829c1 100644
--- a/compiler/rodutils.nim
+++ b/compiler/rodutils.nim
@@ -8,14 +8,50 @@
 #
 
 ## Serialization utilities for the compiler.
-import strutils, math
-
-proc c_snprintf(s: cstring; n:uint; frmt: cstring): cint {.importc: "snprintf", header: "<stdio.h>", nodecl, varargs.}
-
-proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =
+import std/[strutils, math]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+# bcc on windows doesn't have C99 functions
+when defined(windows) and defined(bcc):
+  {.emit: """#if defined(_MSC_VER) && _MSC_VER < 1900
+  #include <stdarg.h>
+  static int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) {
+    int count = -1;
+    if (size != 0) count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
+    if (count == -1) count = _vscprintf(format, ap);
+    return count;
+  }
+  int snprintf(char *outBuf, size_t size, const char *format, ...) {
+    int count;
+    va_list ap;
+    va_start(ap, format);
+    count = c99_vsnprintf(outBuf, size, format, ap);
+    va_end(ap);
+    return count;
+  }
+  #endif
+  """.}
+
+proc c_snprintf(s: cstring; n: uint; frmt: cstring): cint {.importc: "snprintf", header: "<stdio.h>", nodecl, varargs.}
+
+
+when not declared(signbit):
+  proc c_signbit(x: SomeFloat): cint {.importc: "signbit", header: "<math.h>".}
+  proc signbit*(x: SomeFloat): bool {.inline.} =
+    result = c_signbit(x) != 0
+
+import std/formatfloat
+
+proc toStrMaxPrecision*(f: BiggestFloat | float32): string =
+  const literalPostfix = when f is float32: "f" else: ""
   case classify(f)
-  of fcNaN:
-    result = "NAN"
+  of fcNan:
+    if signbit(f):
+      result = "-NAN"
+    else:
+      result = "NAN"
   of fcNegZero:
     result = "-0.0" & literalPostfix
   of fcZero:
@@ -25,20 +61,15 @@ proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =
   of fcNegInf:
     result = "-INF"
   else:
-    when defined(nimNoArrayToCstringConversion):
-      result = newString(81)
-      let n = c_snprintf(result.cstring, result.len.uint, "%#.16e%s", f, literalPostfix.cstring)
-      setLen(result, n)
-    else:
-      var buf: array[0..80, char]
-      discard c_snprintf(buf.cstring, buf.len.uint, "%#.16e%s", f, literalPostfix.cstring)
-      result = $buf.cstring
+    result = ""
+    result.addFloatRoundtrip(f)
+    result.add literalPostfix
 
 proc encodeStr*(s: string, result: var string) =
-  for i in countup(0, len(s) - 1):
+  for i in 0..<s.len:
     case s[i]
-    of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i])
-    else: add(result, '\\' & toHex(ord(s[i]), 2))
+    of 'a'..'z', 'A'..'Z', '0'..'9', '_': result.add(s[i])
+    else: result.add('\\' & toHex(ord(s[i]), 2))
 
 proc hexChar(c: char, xi: var int) =
   case c
@@ -57,30 +88,28 @@ proc decodeStr*(s: cstring, pos: var int): string =
       var xi = 0
       hexChar(s[i-2], xi)
       hexChar(s[i-1], xi)
-      add(result, chr(xi))
+      result.add(chr(xi))
     of 'a'..'z', 'A'..'Z', '0'..'9', '_':
-      add(result, s[i])
+      result.add(s[i])
       inc(i)
     else: break
   pos = i
 
-const
-  chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+const chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
 
 {.push overflowChecks: off.}
 
 # since negative numbers require a leading '-' they use up 1 byte. Thus we
 # subtract/add `vintDelta` here to save space for small negative numbers
 # which are common in ROD files:
-const
-  vintDelta = 5
+const vintDelta = 5
 
 template encodeIntImpl(self) =
   var d: char
   var v = x
   var rem = v mod 190
   if rem < 0:
-    add(result, '-')
+    result.add('-')
     v = - (v div 190)
     rem = - rem
   else:
@@ -89,7 +118,7 @@ template encodeIntImpl(self) =
   if idx < 62: d = chars[idx]
   else: d = chr(idx - 62 + 128)
   if v != 0: self(v, result)
-  add(result, d)
+  result.add(d)
 
 proc encodeVBiggestIntAux(x: BiggestInt, result: var string) =
   ## encode a biggest int as a variable length base 190 int.
diff --git a/compiler/ropes.nim b/compiler/ropes.nim
index 973f16916..e0d5aa0d3 100644
--- a/compiler/ropes.nim
+++ b/compiler/ropes.nim
@@ -7,229 +7,66 @@
 #    distribution, for details about the copyright.
 #
 
-# Ropes for the C code generator
-#
-#  Ropes are a data structure that represents a very long string
-#  efficiently; especially concatenation is done in O(1) instead of O(N).
-#  Ropes make use a lazy evaluation: They are essentially concatenation
-#  trees that are only flattened when converting to a native Nim
-#  string or when written to disk. The empty string is represented by a
-#  nil pointer.
-#  A little picture makes everything clear:
-#
-#  "this string" & " is internally " & "represented as"
-#
-#             con  -- inner nodes do not contain raw data
-#            /   \
-#           /     \
-#          /       \
-#        con       "represented as"
-#       /   \
-#      /     \
-#     /       \
-#    /         \
-#   /           \
-#"this string"  " is internally "
-#
-#  Note that this is the same as:
-#  "this string" & (" is internally " & "represented as")
-#
-#             con
-#            /   \
-#           /     \
-#          /       \
-# "this string"    con
-#                 /   \
-#                /     \
-#               /       \
-#              /         \
-#             /           \
-#" is internally "        "represented as"
-#
-#  The 'con' operator is associative! This does not matter however for
-#  the algorithms we use for ropes.
-#
-#  Note that the left and right pointers are not needed for leaves.
-#  Leaves have relatively high memory overhead (~30 bytes on a 32
-#  bit machines) and we produce many of them. This is why we cache and
-#  share leaves across different rope trees.
-#  To cache them they are inserted in a `cache` array.
+# Ropes for the C code generator. Ropes are mapped to `string` directly nowadays.
 
-import
-  hashes
+from pathutils import AbsoluteFile
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio, formatfloat]
 
 type
   FormatStr* = string  # later we may change it to CString for better
                        # performance of the code generator (assignments
                        # copy the format strings
                        # though it is not necessary)
-  Rope* = ref RopeObj
-  RopeObj*{.acyclic.} = object of RootObj # the empty rope is represented
-                                          # by nil to safe space
-    left*, right*: Rope
-    length*: int
-    data*: string             # != nil if a leaf
-
-proc len*(a: Rope): int =
-  ## the rope's length
-  if a == nil: result = 0
-  else: result = a.length
+  Rope* = string
 
-proc newRope(data: string = nil): Rope =
-  new(result)
-  if data != nil:
-    result.length = len(data)
-    result.data = data
+proc newRopeAppender*(cap = 80): string {.inline.} =
+  result = newStringOfCap(cap)
 
-proc newMutableRope*(capacity = 30): Rope =
-  ## creates a new rope that supports direct modifications of the rope's
-  ## 'data' and 'length' fields.
-  new(result)
-  result.data = newStringOfCap(capacity)
+proc freeze*(r: Rope) {.inline.} = discard
 
-proc freezeMutableRope*(r: Rope) {.inline.} =
-  r.length = r.data.len
+proc resetRopeCache* = discard
 
-var
-  cache: array[0..2048*2 - 1, Rope] # XXX Global here!
-
-proc resetRopeCache* =
-  for i in low(cache)..high(cache):
-    cache[i] = nil
-
-proc ropeInvariant(r: Rope): bool =
-  if r == nil:
-    result = true
-  else:
-    result = true #
-                  #    if r.data <> snil then
-                  #      result := true
-                  #    else begin
-                  #      result := (r.left <> nil) and (r.right <> nil);
-                  #      if result then result := ropeInvariant(r.left);
-                  #      if result then result := ropeInvariant(r.right);
-                  #    end
-
-var gCacheTries* = 0
-var gCacheMisses* = 0
-var gCacheIntTries* = 0
-
-proc insertInCache(s: string): Rope =
-  inc gCacheTries
-  var h = hash(s) and high(cache)
-  result = cache[h]
-  if isNil(result) or result.data != s:
-    inc gCacheMisses
-    result = newRope(s)
-    cache[h] = result
-
-proc rope*(s: string): Rope =
-  ## Converts a string to a rope.
-  if s.len == 0:
-    result = nil
-  else:
-    result = insertInCache(s)
-  assert(ropeInvariant(result))
+template rope*(s: string): string = s
 
 proc rope*(i: BiggestInt): Rope =
   ## Converts an int to a rope.
-  inc gCacheIntTries
   result = rope($i)
 
 proc rope*(f: BiggestFloat): Rope =
   ## Converts a float to a rope.
   result = rope($f)
 
-proc `&`*(a, b: Rope): Rope =
-  if a == nil:
-    result = b
-  elif b == nil:
-    result = a
-  else:
-    result = newRope()
-    result.length = a.length + b.length
-    result.left = a
-    result.right = b
-
-proc `&`*(a: Rope, b: string): Rope =
-  ## the concatenation operator for ropes.
-  result = a & rope(b)
-
-proc `&`*(a: string, b: Rope): Rope =
-  ## the concatenation operator for ropes.
-  result = rope(a) & b
-
-proc `&`*(a: openArray[Rope]): Rope =
-  ## the concatenation operator for an openarray of ropes.
-  for i in countup(0, high(a)): result = result & a[i]
-
-proc add*(a: var Rope, b: Rope) =
-  ## adds `b` to the rope `a`.
-  a = a & b
-
-proc add*(a: var Rope, b: string) =
-  ## adds `b` to the rope `a`.
-  a = a & b
-
-iterator leaves*(r: Rope): string =
-  ## iterates over any leaf string in the rope `r`.
-  if r != nil:
-    var stack = @[r]
-    while stack.len > 0:
-      var it = stack.pop
-      while it.left != nil:
-        assert it.right != nil
-        stack.add(it.right)
-        it = it.left
-        assert(it != nil)
-      yield it.data
-
-iterator items*(r: Rope): char =
-  ## iterates over any character in the rope `r`.
-  for s in leaves(r):
-    for c in items(s): yield c
-
 proc writeRope*(f: File, r: Rope) =
   ## writes a rope to a file.
-  for s in leaves(r): write(f, s)
+  write(f, r)
 
-proc writeRope*(head: Rope, filename: string): bool =
-  var f: File
-  if open(f, filename, fmWrite):
-    if head != nil: writeRope(f, head)
+proc writeRope*(head: Rope, filename: AbsoluteFile): bool =
+  var f: File = default(File)
+  if open(f, filename.string, fmWrite):
+    writeRope(f, head)
     close(f)
     result = true
   else:
     result = false
 
-proc `$`*(r: Rope): string =
-  ## converts a rope back to a string.
-  result = newString(r.len)
-  setLen(result, 0)
-  for s in leaves(r): add(result, s)
-
-proc ropeConcat*(a: varargs[Rope]): Rope =
-  # not overloaded version of concat to speed-up `rfmt` a little bit
-  for i in countup(0, high(a)): result = result & a[i]
-
-proc prepend*(a: var Rope, b: Rope) = a = b & a
 proc prepend*(a: var Rope, b: string) = a = b & a
 
-proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
+proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope =
   var i = 0
-  var length = len(frmt)
-  result = nil
+  result = newRopeAppender()
   var num = 0
-  while i < length:
+  while i < frmt.len:
     if frmt[i] == '$':
       inc(i)                  # skip '$'
       case frmt[i]
       of '$':
-        add(result, "$")
+        result.add("$")
         inc(i)
       of '#':
         inc(i)
-        add(result, args[num])
+        result.add(args[num])
         inc(num)
       of '0'..'9':
         var j = 0
@@ -239,9 +76,9 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
           if i >= frmt.len or frmt[i] notin {'0'..'9'}: break
         num = j
         if j > high(args) + 1:
-          doAssert false, "invalid format string: " & frmt
+          raiseAssert "invalid format string: " & frmt
         else:
-          add(result, args[j-1])
+          result.add(args[j-1])
       of '{':
         inc(i)
         var j = 0
@@ -251,60 +88,47 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
         num = j
         if frmt[i] == '}': inc(i)
         else:
-          doAssert false, "invalid format string: " & frmt
+          raiseAssert "invalid format string: " & frmt
 
         if j > high(args) + 1:
-          doAssert false, "invalid format string: " & frmt
+          raiseAssert "invalid format string: " & frmt
         else:
-          add(result, args[j-1])
+          result.add(args[j-1])
       of 'n':
-        add(result, "\n")
+        result.add("\n")
         inc(i)
       of 'N':
-        add(result, "\n")
+        result.add("\n")
         inc(i)
       else:
-        doAssert false, "invalid format string: " & frmt
-    var start = i
-    while i < length:
-      if frmt[i] != '$': inc(i)
-      else: break
-    if i - 1 >= start:
-      add(result, substr(frmt, start, i - 1))
-  assert(ropeInvariant(result))
+        raiseAssert "invalid format string: " & frmt
+    else:
+      result.add(frmt[i])
+      inc(i)
 
-proc addf*(c: var Rope, frmt: FormatStr, args: openArray[Rope]) =
-  ## shortcut for ``add(c, frmt % args)``.
-  add(c, frmt % args)
+proc `%`*(frmt: static[FormatStr], args: openArray[Rope]): Rope =
+  runtimeFormat(frmt, args)
 
-when true:
-  template `~`*(r: string): Rope = r % []
-else:
-  {.push stack_trace: off, line_trace: off.}
-  proc `~`*(r: static[string]): Rope =
-    # this is the new optimized "to rope" operator
-    # the mnemonic is that `~` looks a bit like a rope :)
-    var r {.global.} = r % []
-    return r
-  {.pop.}
+template addf*(c: var Rope, frmt: FormatStr, args: openArray[Rope]) =
+  ## shortcut for ``add(c, frmt % args)``.
+  c.add(frmt % args)
 
 const
   bufSize = 1024              # 1 KB is reasonable
 
-proc equalsFile*(r: Rope, f: File): bool =
+proc equalsFile*(s: Rope, f: File): bool =
   ## returns true if the contents of the file `f` equal `r`.
   var
-    buf: array[bufSize, char]
+    buf: array[bufSize, char] = default(array[bufSize, char])
     bpos = buf.len
     blen = buf.len
     btotal = 0
     rtotal = 0
 
-  for s in leaves(r):
+  when true:
     var spos = 0
-    let slen = s.len
-    rtotal += slen
-    while spos < slen:
+    rtotal += s.len
+    while spos < s.len:
       if bpos == blen:
         # Read more data
         bpos = 0
@@ -313,7 +137,7 @@ proc equalsFile*(r: Rope, f: File): bool =
         if blen == 0:  # no more data in file
           result = false
           return
-      let n = min(blen - bpos, slen - spos)
+      let n = min(blen - bpos, s.len - spos)
       # TODO There's gotta be a better way of comparing here...
       if not equalMem(addr(buf[bpos]), cast[pointer](cast[int](cstring(s))+spos), n):
         result = false
@@ -324,18 +148,11 @@ proc equalsFile*(r: Rope, f: File): bool =
   result = readBuffer(f, addr(buf[0]), 1) == 0 and
       btotal == rtotal # check that we've read all
 
-proc equalsFile*(r: Rope, filename: string): bool =
+proc equalsFile*(r: Rope, filename: AbsoluteFile): bool =
   ## returns true if the contents of the file `f` equal `r`. If `f` does not
   ## exist, false is returned.
-  var f: File
-  result = open(f, filename)
+  var f: File = default(File)
+  result = open(f, filename.string)
   if result:
     result = equalsFile(r, f)
     close(f)
-
-proc writeRopeIfNotEqual*(r: Rope, filename: string): bool =
-  # returns true if overwritten
-  if not equalsFile(r, filename):
-    result = writeRope(r, filename)
-  else:
-    result = false
diff --git a/compiler/saturate.nim b/compiler/saturate.nim
index 065cb5128..fe6e03c8b 100644
--- a/compiler/saturate.nim
+++ b/compiler/saturate.nim
@@ -15,33 +15,33 @@ proc `|+|`*(a, b: BiggestInt): BiggestInt =
   if (result xor a) >= 0'i64 or (result xor b) >= 0'i64:
     return result
   if a < 0 or b < 0:
-    result = low(result)
+    result = low(typeof(result))
   else:
-    result = high(result)
+    result = high(typeof(result))
 
 proc `|-|`*(a, b: BiggestInt): BiggestInt =
   result = a -% b
   if (result xor a) >= 0'i64 or (result xor not b) >= 0'i64:
     return result
   if b > 0:
-    result = low(result)
+    result = low(typeof(result))
   else:
-    result = high(result)
+    result = high(typeof(result))
 
 proc `|abs|`*(a: BiggestInt): BiggestInt =
-  if a != low(a):
+  if a != low(typeof(a)):
     if a >= 0: result = a
     else: result = -a
   else:
-    result = low(a)
+    result = low(typeof(a))
 
 proc `|div|`*(a, b: BiggestInt): BiggestInt =
-  # (0..5) div (0..4) == (0..5) div (1..4) == (0 div 4) .. (5 div 1)
+  # (0..5) div (0..4) == (0..5) div (1..4) == (0 div 4)..(5 div 1)
   if b == 0'i64:
     # make the same as ``div 1``:
     result = a
-  elif a == low(a) and b == -1'i64:
-    result = high(result)
+  elif a == low(typeof(a)) and b == -1'i64:
+    result = high(typeof(result))
   else:
     result = a div b
 
@@ -74,6 +74,6 @@ proc `|*|`*(a, b: BiggestInt): BiggestInt =
     return result
 
   if floatProd >= 0.0:
-    result = high(result)
+    result = high(typeof(result))
   else:
-    result = low(result)
+    result = low(typeof(result))
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
index ae7e030b8..e3d2bcd45 100644
--- a/compiler/scriptconfig.nim
+++ b/compiler/scriptconfig.nim
@@ -11,12 +11,18 @@
 ## language.
 
 import
-  ast, modules, idents, passes, passaux, condsyms,
-  options, nimconf, sem, semdata, llstream, vm, vmdef, commands, msgs,
-  os, times, osproc, wordrecg, strtabs, modulegraphs, lineinfos
+  ast, modules, idents, condsyms,
+  options, llstream, vm, vmdef, commands,
+  wordrecg, modulegraphs,
+  pathutils, pipelines
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions]
+
+import std/[strtabs, os, times, osproc]
 
 # we support 'cmpIgnoreStyle' natively for efficiency:
-from strutils import cmpIgnoreStyle, contains
+from std/strutils import cmpIgnoreStyle, contains
 
 proc listDirs(a: VmArgs, filter: set[PathComponent]) =
   let dir = getString(a, 0)
@@ -26,9 +32,9 @@ proc listDirs(a: VmArgs, filter: set[PathComponent]) =
   setResult(a, result)
 
 proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
-              graph: ModuleGraph): PEvalContext =
+              graph: ModuleGraph; idgen: IdGenerator): PEvalContext =
   # For Nimble we need to export 'setupVM'.
-  result = newCtx(module, cache, graph)
+  result = newCtx(module, cache, graph, idgen)
   result.mode = emRepl
   registerAdditionalOps(result)
   let conf = graph.config
@@ -42,48 +48,74 @@ 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 = nil
+        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:
+  cbos listFilesImpl:
     listDirs(a, {pcFile, pcLinkToFile})
-  cbos listDirs:
+  cbos listDirsImpl:
     listDirs(a, {pcDir})
   cbos removeDir:
-    os.removeDir getString(a, 0)
+    if defined(nimsuggest) or graph.config.cmd == cmdCheck:
+      discard
+    else:
+      os.removeDir(getString(a, 0), getBool(a, 1))
   cbos removeFile:
-    os.removeFile getString(a, 0)
+    if defined(nimsuggest) or graph.config.cmd == cmdCheck:
+      discard
+    else:
+      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:
     setResult(a, os.getCurrentDir())
   cbos moveFile:
-    os.moveFile(getString(a, 0), getString(a, 1))
+    if defined(nimsuggest) or graph.config.cmd == cmdCheck:
+      discard
+    else:
+      os.moveFile(getString(a, 0), getString(a, 1))
   cbos moveDir:
-    os.moveDir(getString(a, 0), getString(a, 1))
+    if defined(nimsuggest) or graph.config.cmd == cmdCheck:
+      discard
+    else:
+      os.moveDir(getString(a, 0), getString(a, 1))
   cbos copyFile:
-    os.copyFile(getString(a, 0), getString(a, 1))
+    if defined(nimsuggest) or graph.config.cmd == cmdCheck:
+      discard
+    else:
+      os.copyFile(getString(a, 0), getString(a, 1))
   cbos copyDir:
-    os.copyDir(getString(a, 0), getString(a, 1))
+    if defined(nimsuggest) or graph.config.cmd == cmdCheck:
+      discard
+    else:
+      os.copyDir(getString(a, 0), getString(a, 1))
   cbos getLastModificationTime:
     setResult(a, getLastModificationTime(getString(a, 0)).toUnix)
   cbos findExe:
     setResult(a, os.findExe(getString(a, 0)))
 
   cbos rawExec:
-    setResult(a, osproc.execCmd getString(a, 0))
+    if defined(nimsuggest) or graph.config.cmd == cmdCheck:
+      discard
+    else:
+      setResult(a, osproc.execCmd getString(a, 0))
 
   cbconf getEnv:
     setResult(a, os.getEnv(a.getString 0, a.getString 1))
@@ -91,11 +123,19 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
     setResult(a, os.existsEnv(a.getString 0))
   cbconf putEnv:
     os.putEnv(a.getString 0, a.getString 1)
+  cbconf delEnv:
+    os.delEnv(a.getString 0)
   cbconf dirExists:
     setResult(a, os.dirExists(a.getString 0))
   cbconf fileExists:
     setResult(a, os.fileExists(a.getString 0))
 
+  cbconf projectName:
+    setResult(a, conf.projectName)
+  cbconf projectDir:
+    setResult(a, conf.projectPath.string)
+  cbconf projectPath:
+    setResult(a, conf.projectFull.string)
   cbconf thisDir:
     setResult(a, vthisDir)
   cbconf put:
@@ -105,7 +145,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
   cbconf exists:
     setResult(a, options.existsConfigVar(conf, a.getString 0))
   cbconf nimcacheDir:
-    setResult(a, options.getNimcacheDir(conf))
+    setResult(a, options.getNimcacheDir(conf).string)
   cbconf paramStr:
     setResult(a, os.paramStr(int a.getInt 0))
   cbconf paramCount:
@@ -115,20 +155,14 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
   cbconf cmpIgnoreCase:
     setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1))
   cbconf setCommand:
-    conf.command = a.getString 0
+    conf.setCommandEarly(a.getString 0)
     let arg = a.getString 1
-    if arg.len > 0:
-      conf.projectName = arg
-      let path =
-        if conf.projectName.isAbsolute: conf.projectName
-        else: conf.projectPath / conf.projectName
-      try:
-        conf.projectFull = canonicalizePath(conf, path)
-      except OSError:
-        conf.projectFull = path
+    incl(conf.globalOptions, optWasNimscript)
+    if arg.len > 0: setFromProjectName(conf, arg)
   cbconf getCommand:
     setResult(a, conf.command)
   cbconf switch:
+    conf.currentConfigDir = vthisDir
     processSwitch(a.getString 0, a.getString 1, passPP, module.info, conf)
   cbconf hintImpl:
     processSpecificNote(a.getString 0, wHint, passPP, module.info,
@@ -148,28 +182,68 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
     setResult(a, os.getAppFilename())
   cbconf cppDefine:
     options.cppDefine(conf, a.getString(0))
-
-proc runNimScript*(cache: IdentCache; scriptName: string;
-                   freshDefines=true; conf: ConfigRef) =
-  rawMessage(conf, hintConf, scriptName)
+  cbexc stdinReadLine, EOFError:
+    if defined(nimsuggest) or graph.config.cmd == cmdCheck:
+      setResult(a, "")
+    else:
+      setResult(a, stdin.readLine())
+  cbexc stdinReadAll, EOFError:
+    if defined(nimsuggest) or graph.config.cmd == cmdCheck:
+      setResult(a, "")
+    else:
+      setResult(a, stdin.readAll())
+
+proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
+                   idgen: IdGenerator;
+                   freshDefines=true; conf: ConfigRef, stream: PLLStream) =
+  let oldSymbolFiles = conf.symbolFiles
+  conf.symbolFiles = disabledSf
 
   let graph = newModuleGraph(cache, conf)
-  connectCallbacks(graph)
+  connectPipelineCallbacks(graph)
   if freshDefines: initDefines(conf.symbols)
 
   defineSymbol(conf.symbols, "nimscript")
   defineSymbol(conf.symbols, "nimconfig")
-  registerPass(graph, semPass)
-  registerPass(graph, evalPass)
 
   conf.searchPaths.add(conf.libpath)
 
+  let oldGlobalOptions = conf.globalOptions
+  let oldSelectedGC = conf.selectedGC
+  unregisterArcOrc(conf)
+  conf.globalOptions.excl optOwnedRefs
+  conf.selectedGC = gcUnselected
+
   var m = graph.makeModule(scriptName)
   incl(m.flags, sfMainModule)
-  graph.vm = setupVM(m, cache, scriptName, graph)
-
-  graph.compileSystemModule()
-  discard graph.processModule(m, llStreamOpen(scriptName, fmRead))
+  var vm = setupVM(m, cache, scriptName.string, graph, idgen)
+  graph.vm = vm
+
+  graph.setPipeLinePass(EvalPass)
+  graph.compilePipelineSystemModule()
+  discard graph.processPipelineModule(m, vm.idgen, stream)
+
+  # watch out, "newruntime" can be set within NimScript itself and then we need
+  # to remember this:
+  if conf.selectedGC == gcUnselected:
+    conf.selectedGC = oldSelectedGC
+  if optOwnedRefs in oldGlobalOptions:
+    conf.globalOptions.incl {optTinyRtti, optOwnedRefs, optSeqDestructors}
+    defineSymbol(conf.symbols, "nimv2")
+  if conf.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
+    conf.globalOptions.incl {optTinyRtti, optSeqDestructors}
+    defineSymbol(conf.symbols, "nimv2")
+    defineSymbol(conf.symbols, "gcdestructors")
+    defineSymbol(conf.symbols, "nimSeqsV2")
+    case conf.selectedGC
+    of gcArc:
+      defineSymbol(conf.symbols, "gcarc")
+    of gcOrc:
+      defineSymbol(conf.symbols, "gcorc")
+    of gcAtomicArc:
+      defineSymbol(conf.symbols, "gcatomicarc")
+    else:
+      raiseAssert "unreachable"
 
   # ensure we load 'system.nim' again for the real non-config stuff!
   resetSystemArtifacts(graph)
@@ -177,3 +251,4 @@ proc runNimScript*(cache: IdentCache; scriptName: string;
   #initDefines()
   undefSymbol(conf.symbols, "nimscript")
   undefSymbol(conf.symbols, "nimconfig")
+  conf.symbolFiles = oldSymbolFiles
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 3b16e0938..2cf93d365 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -10,34 +10,41 @@
 # This module implements the semantic checking pass.
 
 import
-  ast, strutils, hashes, options, lexer, astalgo, trees, treetab,
-  wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math,
-  magicsys, parser, nversion, nimsets, semfold, modulepaths, importer,
-  procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch,
-  intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
+  ast, options, astalgo, trees,
+  wordrecg, ropes, msgs, idents, renderer, types, platform,
+  magicsys, nversion, nimsets, semfold, modulepaths, importer,
+  procfind, lookups, pragmas, semdata, semtypinst, sigmatch,
+  transf, vmdef, vm, aliases, cgmeth, lambdalifting,
   evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
-  semparallel, lowerings, pluginsupport, plugins.active, rod, lineinfos
+  lowerings, plugins/active, lineinfos, int128,
+  isolation_check, typeallowed, modulegraphs, enumtostr, concepts, astmsgs,
+  extccomp
 
-from modulegraphs import ModuleGraph
+import vtables
+import std/[strtabs, math, tables, intsets, strutils, packedsets]
 
-when defined(nimfix):
-  import nimfix.prettybase
+when not defined(leanCompiler):
+  import spawn
+
+when defined(nimPreviewSlimSystem):
+  import std/[
+    formatfloat,
+    assertions,
+  ]
 
 # implementation
 
-proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.procvar.}
-proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.
-  procvar.}
+proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode
+proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode
 proc semExprNoType(c: PContext, n: PNode): PNode
 proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
-proc semProcBody(c: PContext, n: PNode): PNode
+proc semProcBody(c: PContext, n: PNode; expectedType: PType = nil): PNode
 
 proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode
 proc changeType(c: PContext; n: PNode, newType: PType, check: bool)
 
-proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode
 proc semTypeNode(c: PContext, n: PNode, prev: PType): PType
-proc semStmt(c: PContext, n: PNode): PNode
+proc semStmt(c: PContext, n: PNode; flags: TExprFlags): PNode
 proc semOpAux(c: PContext, n: PNode)
 proc semParamList(c: PContext, n, genericParams: PNode, s: PSym)
 proc addParams(c: PContext, n: PNode, kind: TSymKind)
@@ -48,7 +55,12 @@ proc semQuoteAst(c: PContext, n: PNode): PNode
 proc finishMethod(c: PContext, s: PSym)
 proc evalAtCompileTime(c: PContext, n: PNode): PNode
 proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode
-
+proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode
+proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType
+proc semTypeOf(c: PContext; n: PNode): PNode
+proc computeRequiresInit(c: PContext, t: PType): bool
+proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo)
+proc hasUnresolvedArgs(c: PContext, n: PNode): bool
 proc isArrayConstr(n: PNode): bool {.inline.} =
   result = n.kind == nkBracket and
     n.typ.skipTypes(abstractInst).kind == tyArray
@@ -70,6 +82,15 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
       #  echo "passing to safeSemExpr: ", renderTree(n)
       discard safeSemExpr(c, n)
 
+proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode =
+  let x = arg.skipConv
+  if (x.kind == nkCurly and formal.kind == tySet and formal.base.kind != tyGenericParam) or
+    (x.kind in {nkPar, nkTupleConstr}) and formal.kind notin {tyUntyped, tyBuiltInTypeClass, tyAnything}:
+    changeType(c, x, formal, check=true)
+  result = arg
+  result = skipHiddenSubConv(result, c.graph, c.idgen)
+
+
 proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
   if arg.typ.isNil:
     localError(c.config, arg.info, "expression has no type: " &
@@ -77,28 +98,39 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
     # error correction:
     result = copyTree(arg)
     result.typ = formal
+  elif arg.kind in nkSymChoices and formal.skipTypes(abstractInst).kind == tyEnum:
+    # Pick the right 'sym' from the sym choice by looking at 'formal' type:
+    result = nil
+    for ch in arg:
+      if sameType(ch.typ, formal):
+        return ch
+    typeMismatch(c.config, info, formal, arg.typ, arg)
   else:
     result = indexTypesMatch(c, formal, arg.typ, arg)
     if result == nil:
-      typeMismatch(c.config, info, formal, arg.typ)
+      typeMismatch(c.config, info, formal, arg.typ, arg)
       # error correction:
       result = copyTree(arg)
       result.typ = formal
     else:
-      let x = result.skipConv
-      if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr:
-        changeType(c, x, formal, check=true)
-      else:
-        result = skipHiddenSubConv(result)
-        #result.typ = takeType(formal, arg.typ)
-        #echo arg.info, " picked ", result.typ.typeToString
+      result = fitNodePostMatch(c, formal, result)
+
+proc fitNodeConsiderViewType(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
+  let a = fitNode(c, formal, arg, info)
+  if formal.kind in {tyVar, tyLent}:
+    #classifyViewType(formal) != noView:
+    result = newNodeIT(nkHiddenAddr, a.info, formal)
+    result.add a
+    formal.flags.incl tfVarIsPtr
+  else:
+   result = a
 
 proc inferWithMetatype(c: PContext, formal: PType,
                        arg: PNode, coerceDistincts = false): PNode
 
-template commonTypeBegin*(): PType = PType(kind: tyExpr)
+template commonTypeBegin*(): PType = PType(kind: tyUntyped)
 
-proc commonType*(x, y: PType): PType =
+proc commonType*(c: PContext; x, y: PType): PType =
   # new type relation that is used for array constructors,
   # if expressions, etc.:
   if x == nil: return x
@@ -106,40 +138,43 @@ proc commonType*(x, y: PType): PType =
   var a = skipTypes(x, {tyGenericInst, tyAlias, tySink})
   var b = skipTypes(y, {tyGenericInst, tyAlias, tySink})
   result = x
-  if a.kind in {tyExpr, tyNil}: result = y
-  elif b.kind in {tyExpr, tyNil}: result = x
-  elif a.kind == tyStmt: result = a
-  elif b.kind == tyStmt: result = b
+  if a.kind in {tyUntyped, tyNil}: result = y
+  elif b.kind in {tyUntyped, tyNil}: result = x
+  elif a.kind == tyTyped: result = a
+  elif b.kind == tyTyped: result = b
   elif a.kind == tyTypeDesc:
     # turn any concrete typedesc into the abstract typedesc type
-    if a.sons == nil: result = a
+    if not a.hasElementType: result = a
     else:
-      result = newType(tyTypeDesc, a.owner)
-      rawAddSon(result, newType(tyNone, a.owner))
+      result = newType(tyTypeDesc, c.idgen, a.owner)
+      rawAddSon(result, newType(tyNone, c.idgen, a.owner))
   elif b.kind in {tyArray, tySet, tySequence} and
       a.kind == b.kind:
     # check for seq[empty] vs. seq[int]
     let idx = ord(b.kind == tyArray)
-    if a.sons[idx].kind == tyEmpty: return y
-  elif a.kind == tyTuple and b.kind == tyTuple and a.len == b.len:
-    var nt: PType
-    for i in 0..<a.len:
-      let aEmpty = isEmptyContainer(a.sons[i])
-      let bEmpty = isEmptyContainer(b.sons[i])
+    if a[idx].kind == tyEmpty: return y
+  elif a.kind == tyTuple and b.kind == tyTuple and sameTupleLengths(a, b):
+    var nt: PType = nil
+    for i, aa, bb in tupleTypePairs(a, b):
+      let aEmpty = isEmptyContainer(aa)
+      let bEmpty = isEmptyContainer(bb)
       if aEmpty != bEmpty:
-        if nt.isNil: nt = copyType(a, a.owner, false)
-        nt.sons[i] = if aEmpty: b.sons[i] else: a.sons[i]
+        if nt.isNil:
+          nt = copyType(a, c.idgen, a.owner)
+          copyTypeProps(c.graph, c.idgen.module, nt, a)
+
+        nt[i] = if aEmpty: bb else: aa
     if not nt.isNil: result = nt
-    #elif b.sons[idx].kind == tyEmpty: return x
+    #elif b[idx].kind == tyEmpty: return x
   elif a.kind == tyRange and b.kind == tyRange:
     # consider:  (range[0..3], range[0..4]) here. We should make that
     # range[0..4]. But then why is (range[0..4], 6) not range[0..6]?
     # But then why is (2,4) not range[2..4]? But I think this would break
     # too much code. So ... it's the same range or the base type. This means
-    #  type(if b: 0 else 1) == int and not range[0..1]. For now. In the long
+    #  typeof(if b: 0 else 1) == int and not range[0..1]. For now. In the long
     # run people expect ranges to work properly within a tuple.
     if not sameType(a, b):
-      result = skipTypes(a, {tyRange}).skipIntLit
+      result = skipTypes(a, {tyRange}).skipIntLit(c.idgen)
     when false:
       if a.kind != tyRange and b.kind == tyRange:
         # XXX This really needs a better solution, but a proper fix now breaks
@@ -149,14 +184,20 @@ proc commonType*(x, y: PType): PType =
         result = b #.skipIntLit
       elif a.kind in IntegralTypes and a.n != nil:
         result = a #.skipIntLit
+  elif a.kind == tyProc and b.kind == tyProc:
+    if a.callConv == ccClosure and b.callConv != ccClosure:
+      result = x
+    elif compatibleEffects(a, b) != efCompat or
+        (b.flags * {tfNoSideEffect, tfGcSafe}) < (a.flags * {tfNoSideEffect, tfGcSafe}):
+      result = y
   else:
     var k = tyNone
     if a.kind in {tyRef, tyPtr}:
       k = a.kind
       if b.kind != a.kind: return x
       # bug #7601, array construction of ptr generic
-      a = a.lastSon.skipTypes({tyGenericInst})
-      b = b.lastSon.skipTypes({tyGenericInst})
+      a = a.elementType.skipTypes({tyGenericInst})
+      b = b.elementType.skipTypes({tyGenericInst})
     if a.kind == tyObject and b.kind == tyObject:
       result = commonSuperclass(a, b)
       # this will trigger an error later:
@@ -166,48 +207,55 @@ proc commonType*(x, y: PType): PType =
       # ill-formed AST, no need for additional tyRef/tyPtr
       if k != tyNone and x.kind != tyGenericInst:
         let r = result
-        result = newType(k, r.owner)
-        result.addSonSkipIntLit(r)
-
-proc endsInNoReturn(n: PNode): bool =
-  # check if expr ends in raise exception or call of noreturn proc
-  var it = n
-  while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
-    it = it.lastSon
-  result = it.kind == nkRaiseStmt or
-    it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
-
-proc commonType*(x: PType, y: PNode): PType =
+        result = newType(k, c.idgen, r.owner)
+        result.addSonSkipIntLit(r, c.idgen)
+
+const shouldChckCovered = {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt64, tyBool}
+proc shouldCheckCaseCovered(caseTyp: PType): bool =
+  result = false
+  case caseTyp.kind
+  of shouldChckCovered:
+    result = true
+  of tyRange:
+    if skipTypes(caseTyp[0], abstractInst).kind in shouldChckCovered:
+      result = true
+  else:
+    discard
+
+proc endsInNoReturn(n: PNode): bool
+
+proc commonType*(c: PContext; x: PType, y: PNode): PType =
   # ignore exception raising branches in case/if expressions
   if endsInNoReturn(y): return x
-  commonType(x, y.typ)
+  commonType(c, x, y.typ)
 
 proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
-  result = newSym(kind, considerQuotedIdent(c, n), getCurrOwner(c), n.info)
+  result = newSym(kind, considerQuotedIdent(c, n), c.idgen, getCurrOwner(c), n.info)
   when defined(nimsuggest):
     suggestDecl(c, n, result)
 
 proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
-  proc `$`(kind: TSymKind): string = substr(system.`$`(kind), 2).toLowerAscii
-
   # like newSymS, but considers gensym'ed symbols
   if n.kind == nkSym:
     # and sfGenSym in n.sym.flags:
     result = n.sym
-    if result.kind != kind:
-      localError(c.config, n.info, "cannot use symbol of kind '" &
-                 $result.kind & "' as a '" & $kind & "'")
-    if sfGenSym in result.flags and result.kind notin {skTemplate, skMacro, skParam}:
-      # declarative context, so produce a fresh gensym:
-      result = copySym(result)
-      result.ast = n.sym.ast
-      put(c.p, n.sym, result)
+    if result.kind notin {kind, skTemp}:
+      localError(c.config, n.info, "cannot use symbol of kind '$1' as a '$2'" %
+        [result.kind.toHumanStr, kind.toHumanStr])
+    when false:
+      if sfGenSym in result.flags and result.kind notin {skTemplate, skMacro, skParam}:
+        # declarative context, so produce a fresh gensym:
+        result = copySym(result)
+        result.ast = n.sym.ast
+        put(c.p, n.sym, result)
     # when there is a nested proc inside a template, semtmpl
     # will assign a wrong owner during the first pass over the
     # template; we must fix it here: see #909
     result.owner = getCurrOwner(c)
   else:
-    result = newSym(kind, considerQuotedIdent(c, n), getCurrOwner(c), n.info)
+    result = newSym(kind, considerQuotedIdent(c, n), c.idgen, getCurrOwner(c), n.info)
+    if find(result.name.s, '`') >= 0:
+      result.flags.incl sfWasGenSym
   #if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
   #  incl(result.flags, sfGlobal)
   when defined(nimsuggest):
@@ -217,40 +265,32 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
                  allowed: TSymFlags): PSym
   # identifier with visibility
 proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
-                        allowed: TSymFlags): PSym
+                        allowed: TSymFlags, fromTopLevel = false): PSym
 
-proc typeAllowedCheck(conf: ConfigRef; info: TLineInfo; typ: PType; kind: TSymKind;
+proc typeAllowedCheck(c: PContext; info: TLineInfo; typ: PType; kind: TSymKind;
                       flags: TTypeAllowedFlags = {}) =
-  let t = typeAllowed(typ, kind, flags)
+  let t = typeAllowed(typ, kind, c, flags)
   if t != nil:
+    var err: string
     if t == typ:
-      localError(conf, info, "invalid type: '" & typeToString(typ) &
-        "' for " & substr($kind, 2).toLowerAscii)
+      err = "invalid type: '$1' for $2" % [typeToString(typ), toHumanStr(kind)]
+      if kind in {skVar, skLet, skConst} and taIsTemplateOrMacro in flags:
+        err &= ". Did you mean to call the $1 with '()'?" % [toHumanStr(typ.owner.kind)]
     else:
-      localError(conf, info, "invalid type: '" & typeToString(t) &
-        "' in this context: '" & typeToString(typ) &
-        "' for " & substr($kind, 2).toLowerAscii)
+      err = "invalid type: '$1' in this context: '$2' for $3" % [typeToString(t),
+              typeToString(typ), toHumanStr(kind)]
+    localError(c.config, info, err)
 
 proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} =
-  typeAllowedCheck(c.config, typ.n.info, typ, skProc)
+  typeAllowedCheck(c, typ.n.info, typ, skProc)
 
 proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
-proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
+proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode
 proc semWhen(c: PContext, n: PNode, semCheck: bool = true): PNode
 proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
-                     flags: TExprFlags = {}): PNode
+                     flags: TExprFlags = {}; expectedType: PType = nil): PNode
 proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
-                  flags: TExprFlags = {}): PNode
-
-proc symFromType(c: PContext; t: PType, info: TLineInfo): PSym =
-  if t.sym != nil: return t.sym
-  result = newSym(skType, getIdent(c.cache, "AnonType"), t.owner, info)
-  result.flags.incl sfAnon
-  result.typ = t
-
-proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode =
-  result = newSymNode(symFromType(c, t, info), info)
-  result.typ = makeTypeDesc(c, t)
+                  flags: TExprFlags = {}; expectedType: PType = nil): PNode
 
 when false:
   proc createEvalContext(c: PContext, mode: TEvalMode): PEvalContext =
@@ -268,8 +308,9 @@ when false:
       result = isOpImpl(c, n)
 
 proc hasCycle(n: PNode): bool =
+  result = false
   incl n.flags, nfNone
-  for i in 0..<safeLen(n):
+  for i in 0..<n.safeLen:
     if nfNone in n[i].flags or hasCycle(n[i]):
       result = true
       break
@@ -279,14 +320,13 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode =
   # recompute the types as 'eval' isn't guaranteed to construct types nor
   # that the types are sound:
   when true:
-    if eOrig.typ.kind in {tyExpr, tyStmt, tyTypeDesc}:
+    if eOrig.typ.kind in {tyUntyped, tyTyped, tyTypeDesc}:
       result = semExprWithType(c, evaluated)
     else:
       result = evaluated
       let expectedType = eOrig.typ.skipTypes({tyStatic})
       if hasCycle(result):
-        globalError(c.config, eOrig.info, "the resulting AST is cyclic and cannot be processed further")
-        result = errorNode(c, eOrig)
+        result = localErrorNode(c, eOrig, "the resulting AST is cyclic and cannot be processed further")
       else:
         semmacrosanity.annotateType(result, expectedType, c.config)
   else:
@@ -301,11 +341,11 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode =
          isArrayConstr(arg):
         arg.typ = eOrig.typ
 
-proc tryConstExpr(c: PContext, n: PNode): PNode =
-  var e = semExprWithType(c, n)
+proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
+  var e = semExprWithType(c, n, expectedType = expectedType)
   if e == nil: return
 
-  result = getConstExpr(c.module, e, c.graph)
+  result = getConstExpr(c.module, e, c.idgen, c.graph)
   if result != nil: return
 
   let oldErrorCount = c.config.errorCounter
@@ -313,10 +353,15 @@ proc tryConstExpr(c: PContext, n: PNode): PNode =
   let oldErrorOutputs = c.config.m.errorOutputs
 
   c.config.m.errorOutputs = {}
-  c.config.errorMax = high(int)
+  c.config.errorMax = high(int) # `setErrorMaxHighMaybe` not appropriate here
+
+  when defined(nimsuggest):
+    # Remove the error hook so nimsuggest doesn't report errors there
+    let tempHook = c.graph.config.structuredErrorHook
+    c.graph.config.structuredErrorHook = nil
 
   try:
-    result = evalConstExpr(c.module, c.graph, e)
+    result = evalConstExpr(c.module, c.idgen, c.graph, e)
     if result == nil or result.kind == nkEmpty:
       result = nil
     else:
@@ -325,6 +370,10 @@ proc tryConstExpr(c: PContext, n: PNode): PNode =
   except ERecoverableError:
     result = nil
 
+  when defined(nimsuggest):
+    # Restore the error hook
+    c.graph.config.structuredErrorHook = tempHook
+
   c.config.errorCounter = oldErrorCount
   c.config.errorMax = oldErrorMax
   c.config.m.errorOutputs = oldErrorOutputs
@@ -332,15 +381,17 @@ proc tryConstExpr(c: PContext, n: PNode): PNode =
 const
   errConstExprExpected = "constant expression expected"
 
-proc semConstExpr(c: PContext, n: PNode): PNode =
-  var e = semExprWithType(c, n)
+proc semConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
+  var e = semExprWithType(c, n, expectedType = expectedType)
   if e == nil:
     localError(c.config, n.info, errConstExprExpected)
     return n
-  result = getConstExpr(c.module, e, c.graph)
+  if e.kind in nkSymChoices and e[0].typ.skipTypes(abstractInst).kind == tyEnum:
+    return e
+  result = getConstExpr(c.module, e, c.idgen, c.graph)
   if result == nil:
     #if e.kind == nkEmpty: globalError(n.info, errConstExprExpected)
-    result = evalConstExpr(c.module, c.graph, e)
+    result = evalConstExpr(c.module, c.idgen, c.graph, e)
     if result == nil or result.kind == nkEmpty:
       if e.info != n.info:
         pushInfoContext(c.config, n.info)
@@ -353,31 +404,32 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
     else:
       result = fixupTypeAfterEval(c, result, e)
 
-proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags): PNode =
+proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
   if efNeedStatic in flags:
     if efPreferNilResult in flags:
-      return tryConstExpr(c, n)
+      return tryConstExpr(c, n, expectedType)
     else:
-      return semConstExpr(c, n)
+      return semConstExpr(c, n, expectedType)
   else:
-    result = semExprWithType(c, n, flags)
+    result = semExprWithType(c, n, flags, expectedType)
     if efPreferStatic in flags:
-      var evaluated = getConstExpr(c.module, result, c.graph)
+      var evaluated = getConstExpr(c.module, result, c.idgen, c.graph)
       if evaluated != nil: return evaluated
       evaluated = evalAtCompileTime(c, result)
       if evaluated != nil: return evaluated
 
+proc semGenericStmt(c: PContext, n: PNode): PNode
+
 include hlo, seminst, semcall
 
-when false:
-  # hopefully not required:
-  proc resetSemFlag(n: PNode) =
+proc resetSemFlag(n: PNode) =
+  if n != nil:
     excl n.flags, nfSem
-    for i in 0 ..< n.safeLen:
+    for i in 0..<n.safeLen:
       resetSemFlag(n[i])
 
 proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
-                       s: PSym, flags: TExprFlags): PNode =
+                       s: PSym, flags: TExprFlags; expectedType: PType = nil): PNode =
   ## Semantically check the output of a macro.
   ## This involves processes such as re-checking the macro output for type
   ## coherence, making sure that variables declared with 'let' aren't
@@ -387,43 +439,59 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
   if c.config.evalTemplateCounter > evalTemplateLimit:
     globalError(c.config, s.info, "template instantiation too nested")
   c.friendModules.add(s.owner.getModule)
-
   result = macroResult
-  excl(result.flags, nfSem)
-  #resetSemFlag n
-  if s.typ.sons[0] == nil:
-    result = semStmt(c, result)
+  resetSemFlag result
+  if s.typ.returnType == nil:
+    result = semStmt(c, result, flags)
   else:
-    case s.typ.sons[0].kind
-    of tyExpr:
-      # BUGFIX: we cannot expect a type here, because module aliases would not
-      # work then (see the ``tmodulealias`` test)
-      # semExprWithType(c, result)
-      result = semExpr(c, result, flags)
-    of tyStmt:
-      result = semStmt(c, result)
+    var retType = s.typ.returnType
+    if retType.kind == tyTypeDesc and tfUnresolved in retType.flags and
+        retType.hasElementType:
+      # bug #11941: template fails(T: type X, v: auto): T
+      # does not mean we expect a tyTypeDesc.
+      retType = retType.skipModifier
+    case retType.kind
+    of tyUntyped, tyAnything:
+      # Not expecting a type here allows templates like in ``tmodulealias.in``.
+      result = semExpr(c, result, flags, expectedType)
+    of tyTyped:
+      # More restrictive version.
+      result = semExprWithType(c, result, flags, expectedType)
     of tyTypeDesc:
-      if result.kind == nkStmtList: result.kind = nkStmtListType
+      if result.kind == nkStmtList: result.transitionSonsKind(nkStmtListType)
       var typ = semTypeNode(c, result, nil)
-      result.typ = makeTypeDesc(c, typ)
+      if typ == nil:
+        localError(c.config, result.info, "expression has no type: " &
+                   renderTree(result, {renderNoComments}))
+        result = newSymNode(errorSym(c, result))
+      else:
+        result.typ = makeTypeDesc(c, typ)
       #result = symNodeFromType(c, typ, n.info)
     else:
-      var retType = s.typ.sons[0]
       if s.ast[genericParamsPos] != nil and retType.isMetaType:
         # The return type may depend on the Macro arguments
         # e.g. template foo(T: typedesc): seq[T]
         # We will instantiate the return type here, because
         # we now know the supplied arguments
-        var paramTypes = newIdTable()
+        var paramTypes = initTypeMapping()
         for param, value in genericParamsInMacroCall(s, call):
-          idTablePut(paramTypes, param.typ, value.typ)
+          var givenType = value.typ
+          # the sym nodes used for the supplied generic arguments for
+          # templates and macros leave type nil so regular sem can handle it
+          # in this case, get the type directly from the sym
+          if givenType == nil and value.kind == nkSym and value.sym.typ != nil:
+            givenType = value.sym.typ
+          idTablePut(paramTypes, param.typ, givenType)
 
         retType = generateTypeInstance(c, paramTypes,
                                        macroResult.info, retType)
 
-      result = semExpr(c, result, flags)
-      result = fitNode(c, retType, result, result.info)
-      #globalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0]))
+      if retType.kind == tyVoid:
+        result = semStmt(c, result, flags)
+      else:
+        result = semExpr(c, result, flags, expectedType)
+        result = fitNode(c, retType, result, result.info)
+      #globalError(s.info, errInvalidParamKindX, typeToString(s.typ.returnType))
   dec(c.config.evalTemplateCounter)
   discard c.friendModules.pop()
 
@@ -431,26 +499,29 @@ const
   errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters"
 
 proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
-                  flags: TExprFlags = {}): PNode =
-  pushInfoContext(c.config, nOrig.info)
+                  flags: TExprFlags = {}; expectedType: PType = nil): PNode =
+  rememberExpansion(c, nOrig.info, sym)
+  pushInfoContext(c.config, nOrig.info, sym.detailedInfo)
 
-  markUsed(c.config, n.info, sym, c.graph.usageSym)
-  styleCheckUse(n.info, sym)
+  let info = getCallLineInfo(n)
+  markUsed(c, info, sym)
+  onUse(info, sym)
   if sym == c.p.owner:
-    globalError(c.config, n.info, "recursive dependency: '$1'" % sym.name.s)
+    globalError(c.config, info, "recursive dependency: '$1'" % sym.name.s)
 
-  let genericParams = if sfImmediate in sym.flags: 0
-                      else: sym.ast[genericParamsPos].len
+  let genericParams = sym.ast[genericParamsPos].len
   let suppliedParams = max(n.safeLen - 1, 0)
 
   if suppliedParams < genericParams:
-    globalError(c.config, n.info, errMissingGenericParamsForTemplate % n.renderTree)
+    globalError(c.config, info, errMissingGenericParamsForTemplate % n.renderTree)
 
   #if c.evalContext == nil:
   #  c.evalContext = c.createEvalContext(emStatic)
-  result = evalMacroCall(c.module, c.graph, n, nOrig, sym)
+  result = evalMacroCall(c.module, c.idgen, c.graph, c.templInstCounter, n, nOrig, sym)
   if efNoSemCheck notin flags:
-    result = semAfterMacroCall(c, n, result, sym, flags)
+    result = semAfterMacroCall(c, n, result, sym, flags, expectedType)
+  if c.config.macrosToExpand.hasKey(sym.name.s):
+    message(c.config, nOrig.info, hintExpandMacro, renderTree(result))
   result = wrapInComesFrom(nOrig.info, sym, result)
   popInfoContext(c.config)
 
@@ -459,84 +530,257 @@ proc forceBool(c: PContext, n: PNode): PNode =
   if result == nil: result = n
 
 proc semConstBoolExpr(c: PContext, n: PNode): PNode =
-  let nn = semExprWithType(c, n)
-  result = fitNode(c, getSysType(c.graph, n.info, tyBool), nn, nn.info)
-  if result == nil:
+  result = forceBool(c, semConstExpr(c, n, getSysType(c.graph, n.info, tyBool)))
+  if result.kind != nkIntLit:
     localError(c.config, n.info, errConstExprExpected)
-    return nn
-  result = getConstExpr(c.module, result, c.graph)
-  if result == nil:
-    localError(c.config, n.info, errConstExprExpected)
-    result = nn
-
-proc semGenericStmt(c: PContext, n: PNode): PNode
 proc semConceptBody(c: PContext, n: PNode): PNode
 
-include semtypes, semtempl, semgnrc, semstmts, semexprs
+include semtypes
+
+proc setGenericParamsMisc(c: PContext; n: PNode) =
+  ## used by call defs (procs, templates, macros, ...) to analyse their generic
+  ## params, and store the originals in miscPos for better error reporting.
+  let orig = n[genericParamsPos]
+
+  doAssert orig.kind in {nkEmpty, nkGenericParams}
+
+  if n[genericParamsPos].kind == nkEmpty:
+    n[genericParamsPos] = newNodeI(nkGenericParams, n.info)
+  else:
+    # we keep the original params around for better error messages, see
+    # issue https://github.com/nim-lang/Nim/issues/1713
+    n[genericParamsPos] = semGenericParamList(c, orig)
+
+  if n[miscPos].kind == nkEmpty:
+    n[miscPos] = newTree(nkBracket, c.graph.emptyNode, orig)
+  else:
+    n[miscPos][1] = orig
+
+proc caseBranchMatchesExpr(branch, matched: PNode): bool =
+  result = false
+  for i in 0 ..< branch.len-1:
+    if branch[i].kind == nkRange:
+      if overlap(branch[i], matched): return true
+    elif exprStructuralEquivalent(branch[i], matched):
+      return true
+
+proc pickCaseBranchIndex(caseExpr, matched: PNode): int =
+  result = 0
+  let endsWithElse = caseExpr[^1].kind == nkElse
+  for i in 1..<caseExpr.len - endsWithElse.int:
+    if caseExpr[i].caseBranchMatchesExpr(matched):
+      return i
+  if endsWithElse:
+    return caseExpr.len - 1
+
+proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault: bool): seq[PNode]
+proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode
+proc defaultNodeField(c: PContext, a: PNode, checkDefault: bool): PNode
+
+const defaultFieldsSkipTypes = {tyGenericInst, tyAlias, tySink}
+
+proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool, checkDefault: bool): seq[PNode] =
+  result = @[]
+  case recNode.kind
+  of nkRecList:
+    for field in recNode:
+      result.add defaultFieldsForTuple(c, field, hasDefault, checkDefault)
+  of nkSym:
+    let field = recNode.sym
+    let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes)
+    if field.ast != nil: #Try to use default value
+      hasDefault = true
+      result.add newTree(nkExprColonExpr, recNode, field.ast)
+    else:
+      if recType.kind in {tyObject, tyArray, tyTuple}:
+        let asgnExpr = defaultNodeField(c, recNode, recNode.typ, checkDefault)
+        if asgnExpr != nil:
+          hasDefault = true
+          asgnExpr.flags.incl nfSkipFieldChecking
+          result.add newTree(nkExprColonExpr, recNode, asgnExpr)
+          return
+
+      let asgnType = newType(tyTypeDesc, c.idgen, recNode.typ.owner)
+      rawAddSon(asgnType, recNode.typ)
+      let asgnExpr = newTree(nkCall,
+                      newSymNode(getSysMagic(c.graph, recNode.info, "zeroDefault", mZeroDefault)),
+                      newNodeIT(nkType, recNode.info, asgnType)
+                    )
+      asgnExpr.flags.incl nfSkipFieldChecking
+      asgnExpr.typ = recNode.typ
+      result.add newTree(nkExprColonExpr, recNode, asgnExpr)
+  else:
+    raiseAssert "unreachable"
+
+proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault: bool): seq[PNode] =
+  result = @[]
+  case recNode.kind
+  of nkRecList:
+    for field in recNode:
+      result.add defaultFieldsForTheUninitialized(c, field, checkDefault)
+  of nkRecCase:
+    let discriminator = recNode[0]
+    var selectedBranch: int
+    var defaultValue = discriminator.sym.ast
+    if defaultValue == nil:
+      # None of the branches were explicitly selected by the user and no value
+      # was given to the discrimator. We can assume that it will be initialized
+      # to zero and this will select a particular branch as a result:
+      if checkDefault: # don't add defaults when checking whether a case branch has default fields
+        return
+      defaultValue = newIntNode(nkIntLit#[c.graph]#, 0)
+      defaultValue.typ = discriminator.typ
+    selectedBranch = recNode.pickCaseBranchIndex defaultValue
+    defaultValue.flags.incl nfSkipFieldChecking
+    result.add newTree(nkExprColonExpr, discriminator, defaultValue)
+    result.add defaultFieldsForTheUninitialized(c, recNode[selectedBranch][^1], checkDefault)
+  of nkSym:
+    let field = recNode.sym
+    let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes)
+    if field.ast != nil: #Try to use default value
+      result.add newTree(nkExprColonExpr, recNode, field.ast)
+    elif recType.kind in {tyObject, tyArray, tyTuple}:
+      let asgnExpr = defaultNodeField(c, recNode, recNode.typ, checkDefault)
+      if asgnExpr != nil:
+        asgnExpr.typ = recNode.typ
+        asgnExpr.flags.incl nfSkipFieldChecking
+        result.add newTree(nkExprColonExpr, recNode, asgnExpr)
+  else:
+    raiseAssert "unreachable"
+
+proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode =
+  let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes)
+  case aTypSkip.kind
+  of tyObject:
+    let child = defaultFieldsForTheUninitialized(c, aTypSkip.n, checkDefault)
+    if child.len > 0:
+      var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTyp))
+      asgnExpr.typ = aTyp
+      asgnExpr.sons.add child
+      result = semExpr(c, asgnExpr)
+    else:
+      result = nil
+  of tyArray:
+    let child = defaultNodeField(c, a, aTypSkip[1], checkDefault)
+
+    if child != nil:
+      let node = newNode(nkIntLit)
+      node.intVal = toInt64(lengthOrd(c.graph.config, aTypSkip))
+      result = semExpr(c, newTree(nkCall, newSymNode(getSysSym(c.graph, a.info, "arrayWith"), a.info),
+              semExprWithType(c, child),
+              node
+                ))
+      result.typ = aTyp
+    else:
+      result = nil
+  of tyTuple:
+    var hasDefault = false
+    if aTypSkip.n != nil:
+      let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault, checkDefault)
+      if hasDefault and children.len > 0:
+        result = newNodeI(nkTupleConstr, a.info)
+        result.typ = aTyp
+        result.sons.add children
+        result = semExpr(c, result)
+      else:
+        result = nil
+    else:
+      result = nil
+  of tyRange:
+    if c.graph.config.isDefined("nimPreviewRangeDefault"):
+      result = firstRange(c.config, aTypSkip)
+    else:
+      result = nil
+  else:
+    result = nil
+
+proc defaultNodeField(c: PContext, a: PNode, checkDefault: bool): PNode =
+  result = defaultNodeField(c, a, a.typ, checkDefault)
+
+include semtempl, semgnrc, semstmts, semexprs
 
 proc addCodeForGenerics(c: PContext, n: PNode) =
-  for i in countup(c.lastGenericIdx, c.generics.len - 1):
+  for i in c.lastGenericIdx..<c.generics.len:
     var prc = c.generics[i].inst.sym
     if prc.kind in {skProc, skFunc, skMethod, skConverter} and prc.magic == mNone:
-      if prc.ast == nil or prc.ast.sons[bodyPos] == nil:
+      if prc.ast == nil or prc.ast[bodyPos] == nil:
         internalError(c.config, prc.info, "no code for " & prc.name.s)
       else:
-        addSon(n, prc.ast)
+        n.add prc.ast
   c.lastGenericIdx = c.generics.len
 
-proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
-  var c = newContext(graph, module)
-  if c.p != nil: internalError(graph.config, module.info, "sem.myOpen")
-  c.semConstExpr = semConstExpr
-  c.semExpr = semExpr
-  c.semTryExpr = tryExpr
-  c.semTryConstExpr = tryConstExpr
-  c.semOperand = semOperand
-  c.semConstBoolExpr = semConstBoolExpr
-  c.semOverloadedCall = semOverloadedCall
-  c.semInferredLambda = semInferredLambda
-  c.semGenerateInstance = generateInstance
-  c.semTypeNode = semTypeNode
-  c.instTypeBoundOp = sigmatch.instTypeBoundOp
-
-  pushProcCon(c, module)
-  pushOwner(c, c.module)
-  c.importTable = openScope(c)
-  c.importTable.addSym(module) # a module knows itself
+proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PContext =
+  result = newContext(graph, module)
+  result.idgen = idgen
+  result.enforceVoidContext = newType(tyTyped, idgen, nil)
+  result.voidType = newType(tyVoid, idgen, nil)
+
+  if result.p != nil: internalError(graph.config, module.info, "sem.preparePContext")
+  result.semConstExpr = semConstExpr
+  result.semExpr = semExpr
+  result.semExprWithType = semExprWithType
+  result.semTryExpr = tryExpr
+  result.semTryConstExpr = tryConstExpr
+  result.computeRequiresInit = computeRequiresInit
+  result.semOperand = semOperand
+  result.semConstBoolExpr = semConstBoolExpr
+  result.semOverloadedCall = semOverloadedCall
+  result.semInferredLambda = semInferredLambda
+  result.semGenerateInstance = generateInstance
+  result.instantiateOnlyProcType = instantiateOnlyProcType
+  result.semTypeNode = semTypeNode
+  result.instTypeBoundOp = sigmatch.instTypeBoundOp
+  result.hasUnresolvedArgs = hasUnresolvedArgs
+  result.templInstCounter = new int
+
+  pushProcCon(result, module)
+  pushOwner(result, result.module)
+
+  result.moduleScope = openScope(result)
+  result.moduleScope.addSym(module) # a module knows itself
+
   if sfSystemModule in module.flags:
     graph.systemModule = module
-  c.topLevelScope = openScope(c)
-  # don't be verbose unless the module belongs to the main package:
-  if module.owner.id == graph.config.mainPackageId:
-    graph.config.notes = graph.config.mainPackageNotes
-  else:
-    if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes
-    graph.config.notes = graph.config.foreignPackageNotes
-  result = c
+  result.topLevelScope = openScope(result)
 
 proc isImportSystemStmt(g: ModuleGraph; n: PNode): bool =
   if g.systemModule == nil: return false
+  var n = n
+  if n.kind == nkStmtList:
+    for i in 0..<n.len-1:
+      if n[i].kind notin {nkCommentStmt, nkEmpty}:
+        n = n[i]
+        break
   case n.kind
   of nkImportStmt:
+    result = false
     for x in n:
       if x.kind == nkIdent:
         let f = checkModuleName(g.config, x, false)
         if f == g.systemModule.info.fileIndex:
           return true
   of nkImportExceptStmt, nkFromStmt:
+    result = false
     if n[0].kind == nkIdent:
       let f = checkModuleName(g.config, n[0], false)
       if f == g.systemModule.info.fileIndex:
         return true
-  else: discard
+  else: result = false
+
+proc isEmptyTree(n: PNode): bool =
+  case n.kind
+  of nkStmtList:
+    for it in n:
+      if not isEmptyTree(it): return false
+    result = true
+  of nkEmpty, nkCommentStmt: result = true
+  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
-        n.kind notin {nkEmpty, nkCommentStmt}:
-      c.importTable.addSym c.graph.systemModule # import the "System" identifier
+    if sfSystemModule notin c.module.flags and not isEmptyTree(n):
+      assert c.graph.systemModule != nil
+      c.moduleScope.addSym c.graph.systemModule # import the "System" identifier
       importAllSymbols(c, c.graph.systemModule)
       inc c.topStmts
   else:
@@ -545,7 +789,7 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
     result = semAllTypeSections(c, n)
   else:
     result = n
-  result = semStmt(c, result)
+  result = semStmt(c, result, {})
   when false:
     # Code generators are lazy now and can deal with undeclared procs, so these
     # steps are not required anymore and actually harmful for the upcoming
@@ -554,16 +798,23 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
     if c.lastGenericIdx < c.generics.len:
       var a = newNodeI(nkStmtList, n.info)
       addCodeForGenerics(c, a)
-      if sonsLen(a) > 0:
+      if a.len > 0:
         # a generic has been added to `a`:
-        if result.kind != nkEmpty: addSon(a, result)
+        if result.kind != nkEmpty: a.add result
         result = a
   result = hloStmt(c, result)
   if c.config.cmd == cmdInteractive and not isEmptyType(result.typ):
     result = buildEchoStmt(c, result)
   if c.config.cmd == cmdIdeTools:
     appendToModule(c.module, result)
-  result = transformStmt(c.graph, c.module, result)
+  trackStmt(c, c.module, result, isTopLevel = true)
+  if optMultiMethods notin c.config.globalOptions and
+      c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
+      Feature.vtables in c.config.features:
+    sortVTableDispatchers(c.graph)
+
+    if sfMainModule in c.module.flags:
+      collectVTableDispatchers(c.graph)
 
 proc recoverContext(c: PContext) =
   # clean up in case of a semantic error: We clean up the stacks, etc. This is
@@ -573,8 +824,7 @@ proc recoverContext(c: PContext) =
   while getCurrOwner(c).kind != skModule: popOwner(c)
   while c.p != nil and c.p.owner.kind != skModule: c.p = c.p.next
 
-proc myProcess(context: PPassContext, n: PNode): PNode =
-  var c = PContext(context)
+proc semWithPContext*(c: PContext, n: PNode): PNode =
   # no need for an expensive 'try' if we stop after the first error anyway:
   if c.config.errorMax <= 1:
     result = semStmtAndGenerateGenerics(c, n)
@@ -593,26 +843,21 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
       else:
         result = newNodeI(nkEmpty, n.info)
       #if c.config.cmd == cmdIdeTools: findSuggest(c, n)
-  rod.storeNode(c.graph, c.module, result)
-
-proc testExamples(c: PContext) =
-  let inp = toFullPath(c.config, c.module.info)
-  let outp = inp.changeFileExt"" & "_examples.nim"
-  renderModule(c.runnableExamples, inp, outp)
-  let backend = if isDefined(c.config, "js"): "js"
-                elif isDefined(c.config, "cpp"): "cpp"
-                elif isDefined(c.config, "objc"): "objc"
-                else: "c"
-  if os.execShellCmd(os.getAppFilename() & " " & backend & " -r " & outp) != 0:
-    quit "[Examples] failed"
-  removeFile(outp)
-
-proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
-  var c = PContext(context)
+  storeRodNode(c, result)
+
+
+proc reportUnusedModules(c: PContext) =
+  if c.config.cmd == cmdM: return
+  for i in 0..high(c.unusedImports):
+    if sfUsed notin c.unusedImports[i][0].flags:
+      message(c.config, c.unusedImports[i][1], warnUnusedImportX, c.unusedImports[i][0].name.s)
+
+proc closePContext*(graph: ModuleGraph; c: PContext, n: PNode): PNode =
   if c.config.cmd == cmdIdeTools and not c.suggestionsMade:
     suggestSentinel(c)
   closeScope(c)         # close module's scope
   rawCloseScope(c)      # imported symbols; don't check for unused ones!
+  reportUnusedModules(c)
   result = newNode(nkStmtList)
   if n != nil:
     internalError(c.config, n.info, "n is not nil") #result := n;
@@ -621,8 +866,4 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
     result.add(c.module.ast)
   popOwner(c)
   popProcCon(c)
-  storeRemaining(c.graph, c.module)
-  if c.runnableExamples != nil: testExamples(c)
-
-const semPass* = makePass(myOpen, myProcess, myClose,
-                          isFrontend = true)
+  sealRodFile(c)
diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim
deleted file mode 100644
index 97ff4a7fc..000000000
--- a/compiler/semasgn.nim
+++ /dev/null
@@ -1,337 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module implements lifting for type-bound operations
-## (``=sink``, ``=``, ``=destroy``, ``=deepCopy``).
-
-# included from sem.nim
-
-type
-  TLiftCtx = object
-    c: PContext
-    info: TLineInfo # for construction
-    kind: TTypeAttachedOp
-    fn: PSym
-    asgnForType: PType
-    recurse: bool
-
-proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode)
-proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
-              info: TLineInfo): PSym {.discardable.}
-
-proc at(a, i: PNode, elemType: PType): PNode =
-  result = newNodeI(nkBracketExpr, a.info, 2)
-  result.sons[0] = a
-  result.sons[1] = i
-  result.typ = elemType
-
-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)
-    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 =
-  result = newNodeI(nkDotExpr, x.info, 2)
-  result.sons[0] = x
-  result.sons[1] = newSymNode(f, x.info)
-  result.typ = f.typ
-
-proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
-  case n.kind
-  of nkSym:
-    let f = n.sym
-    liftBodyAux(c, f.typ, body, x.dotField(f), y.dotField(f))
-  of nkNilLit: discard
-  of nkRecCase:
-    # copy the selector:
-    liftBodyObj(c, n[0], body, x, y)
-    # we need to generate a case statement:
-    var caseStmt = newNodeI(nkCaseStmt, c.info)
-    # XXX generate 'if' that checks same branches
-    # generate selector:
-    var access = dotField(x, n[0].sym)
-    caseStmt.add(access)
-    # copy the branches over, but replace the fields with the for loop body:
-    for i in 1 ..< n.len:
-      var branch = copyTree(n[i])
-      let L = branch.len
-      branch.sons[L-1] = newNodeI(nkStmtList, c.info)
-
-      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)
-
-proc genAddr(c: PContext; x: PNode): PNode =
-  if x.kind == nkHiddenDeref:
-    checkSonsLen(x, 1, c.config)
-    result = x.sons[0]
-  else:
-    result = newNodeIT(nkHiddenAddr, x.info, makeVarType(c, x.typ))
-    addSon(result, x)
-
-proc newAsgnCall(c: PContext; 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 y
-
-proc newAsgnStmt(le, ri: PNode): PNode =
-  result = newNodeI(nkAsgn, le.info, 2)
-  result.sons[0] = le
-  result.sons[1] = ri
-
-proc newOpCall(op: PSym; x: PNode): PNode =
-  result = newNodeIT(nkCall, x.info, op.typ.sons[0])
-  result.add(newSymNode(op))
-  result.add x
-
-proc destructorCall(c: PContext; op: PSym; x: PNode): PNode =
-  result = newNodeIT(nkCall, x.info, op.typ.sons[0])
-  result.add(newSymNode(op))
-  if destructor in c.features:
-    result.add genAddr(c, x)
-  else:
-    result.add x
-
-proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
-  result = newAsgnStmt(x, newOpCall(op, y))
-
-proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
-                        field: PSym): bool =
-  if tfHasAsgn in t.flags:
-    var op: PSym
-    if sameType(t, c.asgnForType):
-      # generate recursive call:
-      if c.recurse:
-        op = c.fn
-      else:
-        c.recurse = true
-        return false
-    else:
-      op = field
-      if op == nil:
-        op = liftBody(c.c, t, c.kind, c.info)
-    markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
-    styleCheckUse(c.info, op)
-    body.add newAsgnCall(c.c, op, x, y)
-    result = true
-
-proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
-  case c.kind
-  of attachedDestructor:
-    let op = t.destructor
-    if op != nil:
-      markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
-      styleCheckUse(c.info, op)
-      body.add destructorCall(c.c, op, x)
-      result = true
-  of attachedAsgn:
-    result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
-  of attachedSink:
-    result = considerAsgnOrSink(c, t, body, x, y, t.sink)
-  of attachedDeepCopy:
-    let op = t.deepCopy
-    if op != nil:
-      markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
-      styleCheckUse(c.info, op)
-      body.add newDeepCopyCall(op, x, y)
-      result = true
-
-proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
-  if c.kind != attachedDestructor:
-    body.add newAsgnStmt(x, y)
-
-proc addVar(father, v, value: PNode) =
-  var vpart = newNodeI(nkIdentDefs, v.info, 3)
-  vpart.sons[0] = v
-  vpart.sons[1] = newNodeI(nkEmpty, v.info)
-  vpart.sons[2] = value
-  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)
-  incl(temp.flags, sfFromGeneric)
-
-  var v = newNodeI(nkVarSection, c.info)
-  result = newSymNode(temp)
-  v.addVar(result, lowerings.newIntLit(c.c.graph, body.info, first))
-  body.add v
-
-proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
-  result = newNodeI(nkCall, i.info)
-  result.add createMagic(g, name, magic).newSymNode
-  result.add i
-
-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)
-  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)
-  body.add incCall
-
-proc newSeqCall(c: PContext; 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.add lenCall
-
-proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
-  case t.kind
-  of tyNone, tyEmpty, tyVoid: discard
-  of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString,
-      tyPtr, tyString, tyRef, tyOpt:
-    defaultOp(c, t, body, x, y)
-  of tyArray, tySequence:
-    if {tfHasAsgn, tfUncheckedArray} * t.flags == {tfHasAsgn}:
-      if t.kind == tySequence:
-        # XXX add 'nil' handling here
-        body.add newSeqCall(c.c, x, y)
-      let i = declareCounter(c, body, firstOrd(c.c.config, t))
-      let whileLoop = genWhileLoop(c, i, x)
-      let elemType = t.lastSon
-      liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
-                                                  y.at(i, elemType))
-      addIncStmt(c, whileLoop.sons[1], i)
-      body.add whileLoop
-    else:
-      defaultOp(c, t, body, x, y)
-  of tyObject, 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)
-  of tyTuple:
-    liftBodyTup(c, t, body, x, y)
-  of tyProc:
-    if t.callConv != ccClosure or c.kind != attachedDeepCopy:
-      defaultOp(c, t, body, x, y)
-    else:
-      # a big problem is that we don't know the enviroment's type here, so we
-      # 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[1] = y
-      body.add newAsgnStmt(x, call)
-  of tyVarargs, tyOpenArray:
-    localError(c.c.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))
-  of tyOrdinal, tyRange, tyInferred,
-     tyGenericInst, tyStatic, tyVar, tyLent, tyAlias, tySink:
-    liftBodyAux(c, lastSon(t), body, x, y)
-  of tyUnused, tyOptAsRef: internalError(c.c.config, "liftBodyAux")
-
-proc newProcType(info: TLineInfo; owner: PSym): PType =
-  result = newType(tyProc, owner)
-  result.n = newNodeI(nkFormalParams, info)
-  rawAddSon(result, nil) # return type
-  # result.n[0] used to be `nkType`, but now it's `nkEffectList` because
-  # the effects are now stored in there too ... this is a bit hacky, but as
-  # usual we desperately try to save memory:
-  addSon(result.n, newNodeI(nkEffectList, info))
-
-proc addParam(procType: PType; param: PSym) =
-  param.position = procType.len-1
-  addSon(procType.n, newSymNode(param))
-  rawAddSon(procType, param.typ)
-
-proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
-              info: TLineInfo): PSym =
-  var a: TLiftCtx
-  a.info = info
-  a.c = c
-  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")
-
-  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)
-  src.typ = typ
-
-  result.typ = newProcType(info, typ.owner)
-  result.typ.addParam dest
-  if kind != attachedDestructor:
-    result.typ.addParam src
-
-  liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
-  # recursion is handled explicitly, do not register the type based operation
-  # before 'liftBodyAux':
-  case kind
-  of attachedAsgn: typ.assignment = result
-  of attachedSink: typ.sink = result
-  of attachedDeepCopy: typ.deepCopy = result
-  of attachedDestructor: typ.destructor = result
-
-  var n = newNodeI(nkProcDef, info, bodyPos+1)
-  for i in 0 ..< n.len: n.sons[i] = newNodeI(nkEmpty, info)
-  n.sons[namePos] = newSymNode(result)
-  n.sons[paramsPos] = result.typ.n
-  n.sons[bodyPos] = body
-  result.ast = n
-  incl result.flags, sfFromGeneric
-
-
-proc getAsgnOrLiftBody(c: PContext; 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)
-
-proc overloadedAsgn(c: PContext; dest, src: PNode): PNode =
-  let a = getAsgnOrLiftBody(c, dest.typ, dest.info)
-  result = newAsgnCall(c, a, dest, src)
-
-proc liftTypeBoundOps*(c: PContext; 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.
-  if destructor notin c.features or not hasDestructor(typ): return
-  when false:
-    # do not produce wrong liftings while we're still instantiating generics:
-    # now disabled; breaks topttree.nim!
-    if c.typesWithOps.len > 0: return
-  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)
-  if typ.assignment == nil:
-    liftBody(c, typ, attachedAsgn, info)
-  if typ.sink == nil:
-    liftBody(c, typ, attachedSink, info)
-
-#proc patchResolvedTypeBoundOp*(c: PContext; n: PNode): PNode =
-#  if n.kind == nkCall and
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 5d3df064f..13f2273a9 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -10,6 +10,9 @@
 ## This module implements semantic checking for calls.
 # included from sem.nim
 
+from std/algorithm import sort
+
+
 proc sameMethodDispatcher(a, b: PSym): bool =
   result = false
   if a.kind == skMethod and b.kind == skMethod:
@@ -40,16 +43,29 @@ proc initCandidateSymbols(c: PContext, headSymbol: PNode,
                           best, alt: var TCandidate,
                           o: var TOverloadIter,
                           diagnostics: bool): seq[tuple[s: PSym, scope: int]] =
+  ## puts all overloads into a seq and prepares best+alt
   result = @[]
   var symx = initOverloadIter(o, c, headSymbol)
   while symx != nil:
     if symx.kind in filter:
       result.add((symx, o.lastOverloadScope))
+    elif symx.kind == skGenericParam:
+      #[
+        This code handles looking up a generic parameter when it's a static callable.
+        For instance:
+          proc name[T: static proc()]() = T()
+          name[proc() = echo"hello"]()
+      ]#
+      for paramSym in searchInScopesAllCandidatesFilterBy(c, symx.name, {skConst}):
+        let paramTyp = paramSym.typ
+        if paramTyp.n.kind == nkSym and paramTyp.n.sym.kind in filter:
+          result.add((paramTyp.n.sym, o.lastOverloadScope))
+
     symx = nextOverloadIter(o, c, headSymbol)
   if result.len > 0:
-    initCandidate(c, best, result[0].s, initialBinding,
+    best = initCandidate(c, result[0].s, initialBinding,
                   result[0].scope, diagnostics)
-    initCandidate(c, alt, result[0].s, initialBinding,
+    alt = initCandidate(c, result[0].s, initialBinding,
                   result[0].scope, diagnostics)
     best.state = csNoMatch
 
@@ -60,44 +76,43 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
                        best, alt: var TCandidate,
                        errors: var CandidateErrors,
                        diagnosticsFlag: bool,
-                       errorsEnabled: bool) =
-  var o: TOverloadIter
-  var sym = initOverloadIter(o, c, headSymbol)
-  var scope = o.lastOverloadScope
-  # Thanks to the lazy semchecking for operands, we need to check whether
-  # 'initCandidate' modifies the symbol table (via semExpr).
-  # This can occur in cases like 'init(a, 1, (var b = new(Type2); b))'
-  let counterInitial = c.currentScope.symbols.counter
-  var syms: seq[tuple[s: PSym, scope: int]]
-  var noSyms = true
-  var nextSymIndex = 0
-  while sym != nil:
-    if sym.kind in filter:
-      # Initialise 'best' and 'alt' with the first available symbol
-      initCandidate(c, best, sym, initialBinding, scope, diagnosticsFlag)
-      initCandidate(c, alt, sym, initialBinding, scope, diagnosticsFlag)
-      best.state = csNoMatch
-      break
-    else:
-      sym = nextOverloadIter(o, c, headSymbol)
-      scope = o.lastOverloadScope
-  var z: TCandidate
-  while sym != nil:
-    if sym.kind notin filter:
-      sym = nextOverloadIter(o, c, headSymbol)
-      scope = o.lastOverloadScope
-      continue
+                       errorsEnabled: bool, flags: TExprFlags) =
+  # `matches` may find new symbols, so keep track of count
+  var symCount = c.currentScope.symbols.counter
+
+  var o: TOverloadIter = default(TOverloadIter)
+  # https://github.com/nim-lang/Nim/issues/21272
+  # prevent mutation during iteration by storing them in a seq
+  # luckily `initCandidateSymbols` does just that
+  var syms = initCandidateSymbols(c, headSymbol, initialBinding, filter,
+                                  best, alt, o, diagnosticsFlag)
+  if len(syms) == 0:
+    return
+  # current overload being considered
+  var sym = syms[0].s
+  var scope = syms[0].scope
+
+  # starts at 1 because 0 is already done with setup, only needs checking
+  var nextSymIndex = 1
+  var z: TCandidate # current candidate
+  while true:
     determineType(c, sym)
-    initCandidate(c, z, sym, initialBinding, scope, diagnosticsFlag)
-    if c.currentScope.symbols.counter == counterInitial or syms != nil:
+    z = initCandidate(c, sym, initialBinding, scope, diagnosticsFlag)
+
+    # this is kinda backwards as without a check here the described
+    # problems in recalc would not happen, but instead it 100%
+    # does check forever in some cases
+    if c.currentScope.symbols.counter == symCount:
+      # may introduce new symbols with caveats described in recalc branch
       matches(c, n, orig, z)
-      if z.state == csMatch:
-        #if sym.name.s == "==" and (n.info ?? "temp3"):
-        #  echo typeToString(sym.typ)
-        #  writeMatches(z)
 
+      if z.state == csMatch:
         # little hack so that iterators are preferred over everything else:
-        if sym.kind == skIterator: inc(z.exactMatches, 200)
+        if sym.kind == skIterator:
+          if not (efWantIterator notin flags and efWantIterable in flags):
+            inc(z.exactMatches, 200)
+          else:
+            dec(z.exactMatches, 200)
         case best.state
         of csEmpty, csNoMatch: best = z
         of csMatch:
@@ -105,29 +120,42 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
           if cmp < 0: best = z   # x is better than the best so far
           elif cmp == 0: alt = z # x is as good as the best so far
       elif errorsEnabled or z.diagnosticsEnabled:
-        errors.safeAdd(CandidateError(
+        errors.add(CandidateError(
           sym: sym,
-          unmatchedVarParam: int z.mutabilityProblem,
           firstMismatch: z.firstMismatch,
           diagnostics: z.diagnostics))
     else:
+      # this branch feels like a ticking timebomb
+      # one of two bad things could happen
+      # 1) new symbols are discovered but the loop ends before we recalc
+      # 2) new symbols are discovered and resemmed forever
+      # not 100% sure if these are possible though as they would rely
+      #  on somehow introducing a new overload during overload resolution
+
       # Symbol table has been modified. Restart and pre-calculate all syms
       # before any further candidate init and compare. SLOW, but rare case.
       syms = initCandidateSymbols(c, headSymbol, initialBinding, filter,
                                   best, alt, o, diagnosticsFlag)
-      noSyms = false
-    if noSyms:
-      sym = nextOverloadIter(o, c, headSymbol)
-      scope = o.lastOverloadScope
-    elif nextSymIndex < syms.len:
-      # rare case: retrieve the next pre-calculated symbol
-      sym = syms[nextSymIndex].s
-      scope = syms[nextSymIndex].scope
-      nextSymIndex += 1
-    else:
+
+      # reset counter because syms may be in a new order
+      symCount = c.currentScope.symbols.counter
+      nextSymIndex = 0
+
+      # just in case, should be impossible though
+      if syms.len == 0:
+        break
+
+    if nextSymIndex > high(syms):
+      # we have reached the end
       break
 
-proc effectProblem(f, a: PType; result: var string) =
+    # advance to next sym
+    sym = syms[nextSymIndex].s
+    scope = syms[nextSymIndex].scope
+    inc(nextSymIndex)
+
+
+proc effectProblem(f, a: PType; result: var string; c: PContext) =
   if f.kind == tyProc and a.kind == tyProc:
     if tfThread in f.flags and tfThread notin a.flags:
       result.add "\n  This expression is not GC-safe. Annotate the " &
@@ -135,10 +163,33 @@ proc effectProblem(f, a: PType; result: var string) =
     elif tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
       result.add "\n  This expression can have side effects. Annotate the " &
           "proc with {.noSideEffect.} to get extended error information."
+    else:
+      case compatibleEffects(f, a)
+      of efCompat: discard
+      of efRaisesDiffer:
+        result.add "\n  The `.raises` requirements differ."
+      of efRaisesUnknown:
+        result.add "\n  The `.raises` requirements differ. Annotate the " &
+            "proc with {.raises: [].} to get extended error information."
+      of efTagsDiffer:
+        result.add "\n  The `.tags` requirements differ."
+      of efTagsUnknown:
+        result.add "\n  The `.tags` requirements differ. Annotate the " &
+            "proc with {.tags: [].} to get extended error information."
+      of efEffectsDelayed:
+        result.add "\n  The `.effectsOf` annotations differ."
+      of efTagsIllegal:
+        result.add "\n  The `.forbids` requirements caught an illegal tag."
+      when defined(drnim):
+        if not c.graph.compatibleProps(c.graph, f, a):
+          result.add "\n  The `.requires` or `.ensures` properties are incompatible."
 
 proc renderNotLValue(n: PNode): string =
   result = $n
-  if n.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv} and n.len == 2:
+  let n = if n.kind == nkHiddenDeref: n[0] else: n
+  if n.kind == nkHiddenCallConv and n.len > 1:
+    result = $n[0] & "(" & result & ")"
+  elif n.kind in {nkHiddenStdConv, nkHiddenSubConv} and n.len == 2:
     result = typeToString(n.typ.skipTypes(abstractVar)) & "(" & result & ")"
 
 proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
@@ -154,136 +205,329 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
   for err in errors:
     var errProto = ""
     let n = err.sym.typ.n
-    for i in countup(1, n.len - 1):
-      var p = n.sons[i]
+    for i in 1..<n.len:
+      var p = n[i]
       if p.kind == nkSym:
-        add(errProto, typeToString(p.sym.typ, preferName))
-        if i != n.len-1: add(errProto, ", ")
+        errProto.add(typeToString(p.sym.typ, preferName))
+        if i != n.len-1: errProto.add(", ")
       # else: ignore internal error as we're already in error handling mode
     if errProto == proto:
       prefer = preferModuleInfo
       break
 
+  # we pretend procs are attached to the type of the first
+  # argument in order to remove plenty of candidates. This is
+  # comparable to what C# does and C# is doing fine.
+  var filterOnlyFirst = false
+  if optShowAllMismatches notin c.config.globalOptions and verboseTypeMismatch in c.config.legacyFeatures:
+    for err in errors:
+      if err.firstMismatch.arg > 1:
+        filterOnlyFirst = true
+        break
+
+  var maybeWrongSpace = false
+
+  var candidatesAll: seq[string] = @[]
   var candidates = ""
+  var skipped = 0
   for err in errors:
+    candidates.setLen 0
+    if filterOnlyFirst and err.firstMismatch.arg == 1:
+      inc skipped
+      continue
+
+    if verboseTypeMismatch notin c.config.legacyFeatures:
+      candidates.add "[" & $err.firstMismatch.arg & "] "
+
     if err.sym.kind in routineKinds and err.sym.ast != nil:
-      add(candidates, renderTree(err.sym.ast,
+      candidates.add(renderTree(err.sym.ast,
             {renderNoBody, renderNoComments, renderNoPragmas}))
     else:
-      add(candidates, getProcHeader(c.config, err.sym, prefer))
-    add(candidates, "\n")
-    if err.firstMismatch != 0 and n.len > 1:
-      let cond = n.len > 2
-      if cond:
-        candidates.add("  first type mismatch at position: " & $err.firstMismatch &
-          "\n  required type: ")
-      var wanted, got: PType = nil
-      if err.firstMismatch < err.sym.typ.len:
-        wanted = err.sym.typ.sons[err.firstMismatch]
-        if cond: candidates.add typeToString(wanted)
+      candidates.add(getProcHeader(c.config, err.sym, prefer))
+    candidates.addDeclaredLocMaybe(c.config, err.sym)
+    candidates.add("\n")
+    const genericParamMismatches = {kGenericParamTypeMismatch, kExtraGenericParam, kMissingGenericParam}
+    let isGenericMismatch = err.firstMismatch.kind in genericParamMismatches
+    var argList = n
+    if isGenericMismatch and n[0].kind == nkBracketExpr:
+      argList = n[0]
+    let nArg =
+      if err.firstMismatch.arg < argList.len:
+        argList[err.firstMismatch.arg]
+      else:
+        nil
+    let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: ""
+    if n.len > 1:
+      if verboseTypeMismatch notin c.config.legacyFeatures:
+        case err.firstMismatch.kind
+        of kUnknownNamedParam:
+          if nArg == nil:
+            candidates.add("  unknown named parameter")
+          else:
+            candidates.add("  unknown named parameter: " & $nArg[0])
+          candidates.add "\n"
+        of kAlreadyGiven:
+          candidates.add("  named param already provided: " & $nArg[0])
+          candidates.add "\n"
+        of kPositionalAlreadyGiven:
+          candidates.add("  positional param was already given as named param")
+          candidates.add "\n"
+        of kExtraArg:
+          candidates.add("  extra argument given")
+          candidates.add "\n"
+        of kMissingParam:
+          candidates.add("  missing parameter: " & nameParam)
+          candidates.add "\n"
+        of kExtraGenericParam:
+          candidates.add("  extra generic param given")
+          candidates.add "\n"
+        of kMissingGenericParam:
+          candidates.add("  missing generic parameter: " & nameParam)
+          candidates.add "\n"
+        of kVarNeeded:
+          doAssert nArg != nil
+          doAssert err.firstMismatch.formal != nil
+          candidates.add "  expression '"
+          candidates.add renderNotLValue(nArg)
+          candidates.add "' is immutable, not 'var'"
+          candidates.add "\n"
+        of kTypeMismatch:
+          doAssert nArg != nil
+          if nArg.kind in nkSymChoices:
+            candidates.add ambiguousIdentifierMsg(nArg, indent = 2)
+          let wanted = err.firstMismatch.formal.typ
+          doAssert err.firstMismatch.formal != nil
+          doAssert wanted != nil
+          let got = nArg.typ
+          if got != nil and got.kind == tyProc and wanted.kind == tyProc:
+            # These are proc mismatches so,
+            # add the extra explict detail of the mismatch
+            candidates.add "  expression '"
+            candidates.add renderTree(nArg)
+            candidates.add "' is of type: "
+            candidates.addTypeDeclVerboseMaybe(c.config, got)
+            candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
+            effectProblem(wanted, got, candidates, c)
+            candidates.add "\n"
+        of kGenericParamTypeMismatch:
+          let pos = err.firstMismatch.arg
+          doAssert n[0].kind == nkBracketExpr and pos < n[0].len
+          let arg = n[0][pos]
+          doAssert arg != nil
+          var wanted = err.firstMismatch.formal.typ
+          if wanted.kind == tyGenericParam and wanted.genericParamHasConstraints:
+            wanted = wanted.genericConstraint
+          let got = arg.typ.skipTypes({tyTypeDesc})
+          doAssert err.firstMismatch.formal != nil
+          doAssert wanted != nil
+          doAssert got != nil
+          candidates.add "  generic parameter mismatch, expected "
+          candidates.addTypeDeclVerboseMaybe(c.config, wanted)
+          candidates.add " but got '"
+          candidates.add renderTree(arg)
+          candidates.add "' of type: "
+          candidates.addTypeDeclVerboseMaybe(c.config, got)
+          if nArg.kind in nkSymChoices:
+            candidates.add "\n"
+            candidates.add ambiguousIdentifierMsg(nArg, indent = 2)
+          if got != nil and got.kind == tyProc and wanted.kind == tyProc:
+            # These are proc mismatches so,
+            # add the extra explict detail of the mismatch
+            candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
+          if got != nil:
+            effectProblem(wanted, got, candidates, c)
+          candidates.add "\n"
+        of kUnknown: discard "do not break 'nim check'"
       else:
-        if cond: candidates.add "none"
-      if err.firstMismatch < n.len:
-        if cond:
+        candidates.add("  first type mismatch at position: " & $err.firstMismatch.arg)
+        if err.firstMismatch.kind in genericParamMismatches:
+          candidates.add(" in generic parameters")
+        # candidates.add "\n  reason: " & $err.firstMismatch.kind # for debugging
+        case err.firstMismatch.kind
+        of kUnknownNamedParam:
+          if nArg == nil:
+            candidates.add("\n  unknown named parameter")
+          else:
+            candidates.add("\n  unknown named parameter: " & $nArg[0])
+        of kAlreadyGiven: candidates.add("\n  named param already provided: " & $nArg[0])
+        of kPositionalAlreadyGiven: candidates.add("\n  positional param was already given as named param")
+        of kExtraArg: candidates.add("\n  extra argument given")
+        of kMissingParam: candidates.add("\n  missing parameter: " & nameParam)
+        of kExtraGenericParam:
+          candidates.add("\n  extra generic param given")
+        of kMissingGenericParam:
+          candidates.add("\n  missing generic parameter: " & nameParam)
+        of kTypeMismatch, kGenericParamTypeMismatch, kVarNeeded:
+          doAssert nArg != nil
+          var wanted = err.firstMismatch.formal.typ
+          if isGenericMismatch and wanted.kind == tyGenericParam and
+              wanted.genericParamHasConstraints:
+            wanted = wanted.genericConstraint
+          doAssert err.firstMismatch.formal != nil
+          candidates.add("\n  required type for " & nameParam &  ": ")
+          candidates.addTypeDeclVerboseMaybe(c.config, wanted)
           candidates.add "\n  but expression '"
-          candidates.add renderTree(n[err.firstMismatch])
-          candidates.add "' is of type: "
-        got = n[err.firstMismatch].typ
-        if cond: candidates.add typeToString(got)
-      if wanted != nil and got != nil:
-        effectProblem(wanted, got, candidates)
-      if cond: candidates.add "\n"
-    if err.unmatchedVarParam != 0 and err.unmatchedVarParam < n.len:
-      candidates.add("  for a 'var' type a variable needs to be passed, but '" &
-                      renderNotLValue(n[err.unmatchedVarParam]) &
-                      "' is immutable\n")
+          if err.firstMismatch.kind == kVarNeeded:
+            candidates.add renderNotLValue(nArg)
+            candidates.add "' is immutable, not 'var'"
+          else:
+            candidates.add renderTree(nArg)
+            candidates.add "' is of type: "
+            var got = nArg.typ
+            if isGenericMismatch: got = got.skipTypes({tyTypeDesc})
+            candidates.addTypeDeclVerboseMaybe(c.config, got)
+            if nArg.kind in nkSymChoices:
+              candidates.add "\n"
+              candidates.add ambiguousIdentifierMsg(nArg, indent = 2)
+            doAssert wanted != nil
+            if got != nil:
+              if got.kind == tyProc and wanted.kind == tyProc:
+                # These are proc mismatches so,
+                # add the extra explict detail of the mismatch
+                candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
+              effectProblem(wanted, got, candidates, c)
+
+        of kUnknown: discard "do not break 'nim check'"
+        candidates.add "\n"
+      if err.firstMismatch.arg == 1 and nArg != nil and
+          nArg.kind == nkTupleConstr and n.kind == nkCommand:
+        maybeWrongSpace = true
     for diag in err.diagnostics:
       candidates.add(diag & "\n")
+    candidatesAll.add candidates
+  candidatesAll.sort # fix #13538
+  candidates = join(candidatesAll)
+  if skipped > 0:
+    candidates.add($skipped & " other mismatching symbols have been " &
+        "suppressed; compile with --showAllMismatches:on to see them\n")
+  if maybeWrongSpace:
+    candidates.add("maybe misplaced space between " & renderTree(n[0]) & " and '(' \n")
 
   result = (prefer, candidates)
 
 const
   errTypeMismatch = "type mismatch: got <"
-  errButExpected = "but expected one of: "
+  errButExpected = "but expected one of:"
+  errExpectedPosition = "Expected one of (first mismatch at [position]):"
   errUndeclaredField = "undeclared field: '$1'"
   errUndeclaredRoutine = "attempting to call undeclared routine: '$1'"
+  errBadRoutine = "attempting to call routine: '$1'$2"
   errAmbiguousCallXYZ = "ambiguous call; both $1 and $2 match for: $3"
 
+proc describeParamList(c: PContext, n: PNode, startIdx = 1; prefer = preferName): string =
+  result = "Expression: " & $n
+  for i in startIdx..<n.len:
+    result.add "\n  [" & $i & "] " & renderTree(n[i]) & ": "
+    result.add describeArg(c, n, i, startIdx, prefer)
+  result.add "\n"
+
+template legacynotFoundError(c: PContext, n: PNode, errors: CandidateErrors) =
+  let (prefer, candidates) = presentFailedCandidates(c, n, errors)
+  var result = errTypeMismatch
+  result.add(describeArgs(c, n, 1, prefer))
+  result.add('>')
+  if candidates != "":
+    result.add("\n" & errButExpected & "\n" & candidates)
+  localError(c.config, n.info, result & "\nexpression: " & $n)
+
 proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
   # Gives a detailed error message; this is separated from semOverloadedCall,
-  # as semOverlodedCall is already pretty slow (and we need this information
+  # as semOverloadedCall is already pretty slow (and we need this information
   # only in case of an error).
   if c.config.m.errorOutputs == {}:
     # fail fast:
     globalError(c.config, n.info, "type mismatch")
+    return
+  # see getMsgDiagnostic:
+  if nfExplicitCall notin n.flags and {nfDotField, nfDotSetter} * n.flags != {}:
+    let ident = considerQuotedIdent(c, n[0], n).s
+    let sym = n[1].typ.typSym
+    var typeHint = ""
+    if sym == nil:
+      discard
+    else:
+      typeHint = " for type " & getProcHeader(c.config, sym)
+    localError(c.config, n.info, errUndeclaredField % ident & typeHint)
+    return
   if errors.len == 0:
-    localError(c.config, n.info, "expression '$1' cannot be called" % n[0].renderTree)
+    if n[0].kind in nkIdentKinds:
+      let ident = considerQuotedIdent(c, n[0], n).s
+      localError(c.config, n.info, errUndeclaredRoutine % ident)
+    else:
+      localError(c.config, n.info, "expression '$1' cannot be called" % n[0].renderTree)
     return
 
-  let (prefer, candidates) = presentFailedCandidates(c, n, errors)
-  var result = errTypeMismatch
-  add(result, describeArgs(c, n, 1, prefer))
-  add(result, '>')
-  if candidates != "":
-    add(result, "\n" & errButExpected & "\n" & candidates)
-  localError(c.config, n.info, result & "\nexpression: " & $n)
-
-proc bracketNotFoundError(c: PContext; n: PNode) =
-  var errors: CandidateErrors = @[]
-  var o: TOverloadIter
-  let headSymbol = n[0]
-  var symx = initOverloadIter(o, c, headSymbol)
-  while symx != nil:
-    if symx.kind in routineKinds:
-      errors.add(CandidateError(sym: symx,
-                                unmatchedVarParam: 0, firstMismatch: 0,
-                                diagnostics: nil,
-                                enabled: false))
-    symx = nextOverloadIter(o, c, headSymbol)
-  if errors.len == 0:
-    localError(c.config, n.info, "could not resolve: " & $n)
+  if verboseTypeMismatch in c.config.legacyFeatures:
+    legacynotFoundError(c, n, errors)
   else:
-    notFoundError(c, n, errors)
+    let (prefer, candidates) = presentFailedCandidates(c, n, errors)
+    var result = "type mismatch\n"
+    result.add describeParamList(c, n, 1, prefer)
+    if candidates != "":
+      result.add("\n" & errExpectedPosition & "\n" & candidates)
+    localError(c.config, n.info, result)
+
+proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string =
+  result = ""
+  if c.compilesContextId > 0:
+    # we avoid running more diagnostic when inside a `compiles(expr)`, to
+    # errors while running diagnostic (see test D20180828T234921), and
+    # also avoid slowdowns in evaluating `compiles(expr)`.
+    discard
+  else:
+    var o: TOverloadIter = default(TOverloadIter)
+    var sym = initOverloadIter(o, c, f)
+    while sym != nil:
+      result &= "\n  found $1" % [getSymRepr(c.config, sym)]
+      sym = nextOverloadIter(o, c, f)
+
+  let ident = considerQuotedIdent(c, f, n).s
+  if nfExplicitCall notin n.flags and {nfDotField, nfDotSetter} * n.flags != {}:
+    let sym = n[1].typ.typSym
+    var typeHint = ""
+    if sym == nil:
+      # Perhaps we're in a `compiles(foo.bar)` expression, or
+      # in a concept, e.g.:
+      #   ExplainedConcept {.explain.} = concept x
+      #     x.foo is int
+      # We could use: `(c.config $ n[1].info)` to get more context.
+      discard
+    else:
+      typeHint = " for type " & getProcHeader(c.config, sym)
+    let suffix = if result.len > 0: " " & result else: ""
+    result = errUndeclaredField % ident & typeHint & suffix
+  else:
+    if result.len == 0: result = errUndeclaredRoutine % ident
+    else: result = errBadRoutine % [ident, result]
 
 proc resolveOverloads(c: PContext, n, orig: PNode,
                       filter: TSymKinds, flags: TExprFlags,
                       errors: var CandidateErrors,
                       errorsEnabled: bool): TCandidate =
+  result = default(TCandidate)
   var initialBinding: PNode
-  var alt: TCandidate
-  var f = n.sons[0]
+  var alt: TCandidate = default(TCandidate)
+  var f = n[0]
   if f.kind == nkBracketExpr:
     # fill in the bindings:
     semOpAux(c, f)
     initialBinding = f
-    f = f.sons[0]
+    f = f[0]
   else:
     initialBinding = nil
 
-  template pickBest(headSymbol) =
+  pickBestCandidate(c, f, n, orig, initialBinding,
+                    filter, result, alt, errors, efExplain in flags,
+                    errorsEnabled, flags)
+
+  var dummyErrors: CandidateErrors = @[]
+  template pickSpecialOp(headSymbol) =
     pickBestCandidate(c, headSymbol, n, orig, initialBinding,
-                      filter, result, alt, errors, efExplain in flags,
-                      errorsEnabled)
-  pickBest(f)
+                      filter, result, alt, dummyErrors, efExplain in flags,
+                      false, flags)
 
   let overloadsState = result.state
   if overloadsState != csMatch:
-    if c.p != nil and c.p.selfSym != nil:
-      # we need to enforce semchecking of selfSym again because it
-      # might need auto-deref:
-      var hiddenArg = newSymNode(c.p.selfSym)
-      hiddenArg.typ = nil
-      n.sons.insert(hiddenArg, 1)
-      orig.sons.insert(hiddenArg, 1)
-
-      pickBest(f)
-
-      if result.state != csMatch:
-        n.sons.delete(1)
-        orig.sons.delete(1)
-        excl n.flags, nfExprCall
-      else: return
-
     if nfDotField in n.flags:
       internalAssert c.config, f.kind == nkIdent and n.len >= 2
 
@@ -294,9 +538,9 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
 
       template tryOp(x) =
         let op = newIdentNode(getIdent(c.cache, x), n.info)
-        n.sons[0] = op
-        orig.sons[0] = op
-        pickBest(op)
+        n[0] = op
+        orig[0] = op
+        pickSpecialOp(op)
 
       if nfExplicitCall in n.flags:
         tryOp ".()"
@@ -306,17 +550,20 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
 
     elif nfDotSetter in n.flags and f.kind == nkIdent and n.len == 3:
       # we need to strip away the trailing '=' here:
-      let calleeName = newIdentNode(getIdent(c.cache, f.ident.s[0..f.ident.s.len-2]), n.info)
+      let calleeName = newIdentNode(getIdent(c.cache, f.ident.s[0..^2]), n.info)
       let callOp = newIdentNode(getIdent(c.cache, ".="), n.info)
       n.sons[0..1] = [callOp, n[1], calleeName]
       orig.sons[0..1] = [callOp, orig[1], calleeName]
-      pickBest(callOp)
+      pickSpecialOp(callOp)
 
     if overloadsState == csEmpty and result.state == csEmpty:
-      if nfDotField in n.flags and nfExplicitCall notin n.flags:
-        localError(c.config, n.info, errUndeclaredField % considerQuotedIdent(c, f, n).s)
-      else:
-        localError(c.config, n.info, errUndeclaredRoutine % considerQuotedIdent(c, f, n).s)
+      if efNoUndeclared notin flags: # for tests/pragmas/tcustom_pragma.nim
+        result.state = csNoMatch
+        if c.inGenericContext > 0 and nfExprCall in n.flags:
+          # untyped expression calls end up here, see #24099
+          return
+        # xxx adapt/use errorUndeclaredIdentifierHint(c, n, f.ident)
+        localError(c.config, n.info, getMsgDiagnostic(c, flags, n, f))
       return
     elif result.state != csMatch:
       if nfExprCall in n.flags:
@@ -326,7 +573,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
         if {nfDotField, nfDotSetter} * n.flags != {}:
           # clean up the inserted ops
           n.sons.delete(2)
-          n.sons[0] = f
+          n[0] = f
       return
   if alt.state == csMatch and cmpCandidates(result, alt) == 0 and
       not sameMethodDispatcher(result.calleeSym, alt.calleeSym):
@@ -339,42 +586,74 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
     elif c.config.errorCounter == 0:
       # don't cascade errors
       var args = "("
-      for i in countup(1, sonsLen(n) - 1):
-        if i > 1: add(args, ", ")
-        add(args, typeToString(n.sons[i].typ))
-      add(args, ")")
+      for i in 1..<n.len:
+        if i > 1: args.add(", ")
+        args.add(typeToString(n[i].typ))
+      args.add(")")
 
       localError(c.config, n.info, errAmbiguousCallXYZ % [
         getProcHeader(c.config, result.calleeSym),
         getProcHeader(c.config, alt.calleeSym),
         args])
 
+proc bracketNotFoundError(c: PContext; n: PNode; flags: TExprFlags) =
+  var errors: CandidateErrors = @[]
+  let headSymbol = n[0]
+  block:
+    # we build a closed symchoice of all `[]` overloads for their errors,
+    # except add a custom error for the magics which always match
+    var choice = newNodeIT(nkClosedSymChoice, headSymbol.info, newTypeS(tyNone, c))
+    var o: TOverloadIter = default(TOverloadIter)
+    var symx = initOverloadIter(o, c, headSymbol)
+    while symx != nil:
+      if symx.kind in routineKinds:
+        if symx.magic in {mArrGet, mArrPut}:
+          errors.add(CandidateError(sym: symx,
+                                    firstMismatch: MismatchInfo(),
+                                    diagnostics: @[],
+                                    enabled: false))
+        else:
+          choice.add newSymNode(symx, headSymbol.info)
+      symx = nextOverloadIter(o, c, headSymbol)
+    n[0] = choice
+  # copied from semOverloadedCallAnalyzeEffects, might be overkill:
+  const baseFilter = {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}
+  let filter =
+    if flags*{efInTypeof, efWantIterator, efWantIterable} != {}:
+      baseFilter + {skIterator}
+    else: baseFilter
+  # this will add the errors:
+  var r = resolveOverloads(c, n, n, filter, flags, errors, true)
+  if errors.len == 0:
+    localError(c.config, n.info, "could not resolve: " & $n)
+  else:
+    notFoundError(c, n, errors)
+
 proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
-  if a.kind == nkHiddenCallConv and a.sons[0].kind == nkSym:
-    let s = a.sons[0].sym
-    if s.ast != nil and s.ast[genericParamsPos].kind != nkEmpty:
+  let a = if a.kind == nkHiddenDeref: a[0] else: a
+  if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
+    let s = a[0].sym
+    if s.isGenericRoutineStrict:
       let finalCallee = generateInstance(c, s, x.bindings, a.info)
-      a.sons[0].sym = finalCallee
-      a.sons[0].typ = finalCallee.typ
-      #a.typ = finalCallee.typ.sons[0]
+      a[0].sym = finalCallee
+      a[0].typ = finalCallee.typ
+      #a.typ = finalCallee.typ.returnType
 
 proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) =
   assert n.kind in nkCallKinds
   if x.genericConverter:
-    for i in 1 ..< n.len:
-      instGenericConvertersArg(c, n.sons[i], x)
+    for i in 1..<n.len:
+      instGenericConvertersArg(c, n[i], x)
 
 proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode =
-  var m: TCandidate
-  initCandidate(c, m, f)
+  var m = newCandidate(c, f)
   result = paramTypesMatch(m, f, a, arg, nil)
   if m.genericConverter and result != nil:
     instGenericConvertersArg(c, result, m)
 
 proc inferWithMetatype(c: PContext, formal: PType,
                        arg: PNode, coerceDistincts = false): PNode =
-  var m: TCandidate
-  initCandidate(c, m, formal)
+  var m = newCandidate(c, formal)
   m.coerceDistincts = coerceDistincts
   result = paramTypesMatch(m, formal, arg.typ, arg, nil)
   if m.genericConverter and result != nil:
@@ -386,46 +665,154 @@ proc inferWithMetatype(c: PContext, formal: PType,
     result.typ = generateTypeInstance(c, m.bindings, arg.info,
                                       formal.skipTypes({tyCompositeTypeClass}))
   else:
-    typeMismatch(c.config, arg.info, formal, arg.typ)
+    typeMismatch(c.config, arg.info, formal, arg.typ, arg)
     # error correction:
     result = copyTree(arg)
     result.typ = formal
 
-proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
+proc updateDefaultParams(c: PContext, call: PNode) =
+  # In generic procs, the default parameter may be unique for each
+  # instantiation (see tlateboundgenericparams).
+  # After a call is resolved, we need to re-assign any default value
+  # that was used during sigmatch. sigmatch is responsible for marking
+  # the default params with `nfDefaultParam` and `instantiateProcType`
+  # computes correctly the default values for each instantiation.
+  let calleeParams = call[0].sym.typ.n
+  for i in 1..<call.len:
+    if nfDefaultParam in call[i].flags:
+      let formal = calleeParams[i].sym
+      let def = formal.ast
+      if nfDefaultRefsParam in def.flags: call.flags.incl nfDefaultRefsParam
+      # mirrored with sigmatch:
+      if def.kind == nkEmpty:
+        # The default param value is set to empty in `instantiateProcType`
+        # when the type of the default expression doesn't match the type
+        # of the instantiated proc param:
+        pushInfoContext(c.config, call.info, call[0].sym.detailedInfo)
+        typeMismatch(c.config, def.info, formal.typ, def.typ, formal.ast)
+        popInfoContext(c.config)
+        def.typ = errorType(c)
+      call[i] = def
+
+proc getCallLineInfo(n: PNode): TLineInfo =
+  case n.kind
+  of nkAccQuoted, nkBracketExpr, nkCall, nkCallStrLit, nkCommand:
+    if len(n) > 0:
+      return getCallLineInfo(n[0])
+  of nkDotExpr:
+    if len(n) > 1:
+      return getCallLineInfo(n[1])
+  else:
+    discard
+  result = n.info
+
+proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) =
+  ## Helper proc to inherit bound generic parameters from expectedType into x.
+  ## Does nothing if 'inferGenericTypes' isn't in c.features.
+  if inferGenericTypes notin c.features: return
+  if expectedType == nil or x.callee.returnType == nil: return # required for inference
+
+  var
+    flatUnbound: seq[PType] = @[]
+    flatBound: seq[PType] = @[]
+  # seq[(result type, expected type)]
+  var typeStack = newSeq[(PType, PType)]()
+
+  template stackPut(a, b) =
+    ## skips types and puts the skipped version on stack
+    # It might make sense to skip here one by one. It's not part of the main
+    #  type reduction because the right side normally won't be skipped
+    const toSkip = {tyVar, tyLent, tyStatic, tyCompositeTypeClass, tySink}
+    let
+      x = a.skipTypes(toSkip)
+      y = if a.kind notin toSkip: b
+          else: b.skipTypes(toSkip)
+    typeStack.add((x, y))
+
+  stackPut(x.callee.returnType, expectedType)
+
+  while typeStack.len() > 0:
+    let (t, u) = typeStack.pop()
+    if t == u or t == nil or u == nil or t.kind == tyAnything or u.kind == tyAnything:
+      continue
+    case t.kind
+    of ConcreteTypes, tyGenericInvocation, tyUncheckedArray:
+      # XXX This logic makes no sense for `tyUncheckedArray`
+      # nested, add all the types to stack
+      let
+        startIdx = if u.kind in ConcreteTypes: 0 else: 1
+        endIdx = min(u.kidsLen() - startIdx, t.kidsLen())
+
+      for i in startIdx ..< endIdx:
+        # early exit with current impl
+        if t[i] == nil or u[i] == nil: return
+        stackPut(t[i], u[i])
+    of tyGenericParam:
+      let prebound = x.bindings.idTableGet(t)
+      if prebound != nil:
+        continue # Skip param, already bound
+
+      # fully reduced generic param, bind it
+      if t notin flatUnbound:
+        flatUnbound.add(t)
+        flatBound.add(u)
+    else:
+      discard
+  # update bindings
+  for i in 0 ..< flatUnbound.len():
+    x.bindings.idTablePut(flatUnbound[i], flatBound[i])
+
+proc semResolvedCall(c: PContext, x: var TCandidate,
+                     n: PNode, flags: TExprFlags;
+                     expectedType: PType = nil): PNode =
   assert x.state == csMatch
   var finalCallee = x.calleeSym
-  markUsed(c.config, n.sons[0].info, finalCallee, c.graph.usageSym)
-  styleCheckUse(n.sons[0].info, finalCallee)
+  let info = getCallLineInfo(n)
+  markUsed(c, info, finalCallee)
+  onUse(info, finalCallee)
   assert finalCallee.ast != nil
-  if x.hasFauxMatch:
+  if x.matchedErrorType:
     result = x.call
-    result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
-    if containsGenericType(result.typ) or x.fauxMatch == tyUnknown:
-      result.typ = newTypeS(x.fauxMatch, c)
+    result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
+    if containsGenericType(result.typ):
+      result.typ = newTypeS(tyError, c)
+      incl result.typ.flags, tfCheckedForDestructor
     return
-  let gp = finalCallee.ast.sons[genericParamsPos]
-  if gp.kind != nkEmpty:
+  let gp = finalCallee.ast[genericParamsPos]
+  if gp.isGenericParams:
     if x.calleeSym.kind notin {skMacro, skTemplate}:
       if x.calleeSym.magic in {mArrGet, mArrPut}:
         finalCallee = x.calleeSym
       else:
+        c.inheritBindings(x, expectedType)
         finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
     else:
       # For macros and templates, the resolved generic params
       # are added as normal params.
+      c.inheritBindings(x, expectedType)
       for s in instantiateGenericParamList(c, gp, x.bindings):
         case s.kind
         of skConst:
-          x.call.add s.ast
+          if not s.astdef.isNil:
+            x.call.add s.astdef
+          else:
+            x.call.add c.graph.emptyNode
         of skType:
-          x.call.add newSymNode(s, n.info)
+          var tn = newSymNode(s, n.info)
+          # this node will be used in template substitution,
+          # pretend this is an untyped node and let regular sem handle the type
+          # to prevent problems where a generic parameter is treated as a value
+          tn.typ = nil
+          x.call.add tn
         else:
           internalAssert c.config, false
 
   result = x.call
   instGenericConvertersSons(c, result, x)
-  result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
-  result.typ = finalCallee.typ.sons[0]
+  result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
+  if finalCallee.magic notin {mArrGet, mArrPut}:
+    result.typ = finalCallee.typ.returnType
+  updateDefaultParams(c, result)
 
 proc canDeref(n: PNode): bool {.inline.} =
   result = n.len >= 2 and (let t = n[1].typ;
@@ -433,12 +820,13 @@ proc canDeref(n: PNode): bool {.inline.} =
 
 proc tryDeref(n: PNode): PNode =
   result = newNodeI(nkHiddenDeref, n.info)
-  result.typ = n.typ.skipTypes(abstractInst).sons[0]
-  result.addSon(n)
+  result.typ = n.typ.skipTypes(abstractInst)[0]
+  result.add n
 
 proc semOverloadedCall(c: PContext, n, nOrig: PNode,
-                       filter: TSymKinds, flags: TExprFlags): PNode =
-  var errors: CandidateErrors = if efExplain in flags: @[] else: nil
+                       filter: TSymKinds, flags: TExprFlags;
+                       expectedType: PType = nil): PNode =
+  var errors: CandidateErrors = @[] # if efExplain in flags: @[] else: nil
   var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
   if r.state == csMatch:
     # this may be triggered, when the explain pragma is used
@@ -447,70 +835,70 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
       message(c.config, n.info, hintUserRaw,
               "Non-matching candidates for " & renderTree(n) & "\n" &
               candidates)
-    result = semResolvedCall(c, n, r)
-  elif implicitDeref in c.features and canDeref(n):
-    # try to deref the first argument and then try overloading resolution again:
-    #
-    # XXX: why is this here?
-    # it could be added to the long list of alternatives tried
-    # inside `resolveOverloads` or it could be moved all the way
-    # into sigmatch with hidden conversion produced there
-    #
-    n.sons[1] = n.sons[1].tryDeref
-    var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
-    if r.state == csMatch: result = semResolvedCall(c, n, r)
-    else:
-      # get rid of the deref again for a better error message:
-      n.sons[1] = n.sons[1].sons[0]
-      #notFoundError(c, n, errors)
-      if efExplain notin flags:
-        # repeat the overload resolution,
-        # this time enabling all the diagnostic output (this should fail again)
-        discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})
-      else:
-        notFoundError(c, n, errors)
+    result = semResolvedCall(c, r, n, flags, expectedType)
   else:
-    if efExplain notin flags:
+    if c.inGenericContext > 0 and c.matchedConcept == nil:
+      result = semGenericStmt(c, n)
+      result.typ = makeTypeFromExpr(c, result.copyTree)
+    elif efExplain notin flags:
       # repeat the overload resolution,
       # this time enabling all the diagnostic output (this should fail again)
-      discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})
-    else:
+      result = semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})
+    elif efNoUndeclared notin flags:
+      result = nil
       notFoundError(c, n, errors)
+    else:
+      result = nil
 
 proc explicitGenericInstError(c: PContext; n: PNode): PNode =
-  localError(c.config, n.info, errCannotInstantiateX % renderTree(n))
+  localError(c.config, getCallLineInfo(n), errCannotInstantiateX % renderTree(n))
   result = n
 
 proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
-  var m: TCandidate
+  if s.kind in {skTemplate, skMacro}:
+    internalError c.config, n.info, "cannot get explicitly instantiated symbol of " &
+      (if s.kind == skTemplate: "template" else: "macro")
   # binding has to stay 'nil' for this to work!
-  initCandidate(c, m, s, nil)
-
-  for i in 1..sonsLen(n)-1:
-    let formal = s.ast.sons[genericParamsPos].sons[i-1].typ
-    let arg = n[i].typ
-    let tm = typeRel(m, formal, arg)
-    if tm in {isNone, isConvertible}: return nil
+  var m = newCandidate(c, s, nil)
+  matchGenericParams(m, n, s)
+  if m.state != csMatch:
+    # state is csMatch only if *all* generic params were matched,
+    # including implicit parameters
+    return nil
   var newInst = generateInstance(c, s, m.bindings, n.info)
   newInst.typ.flags.excl tfUnresolved
-  markUsed(c.config, n.info, s, c.graph.usageSym)
-  styleCheckUse(n.info, s)
-  result = newSymNode(newInst, n.info)
+  let info = getCallLineInfo(n)
+  markUsed(c, info, s)
+  onUse(info, s)
+  result = newSymNode(newInst, info)
+
+proc setGenericParams(c: PContext, n, expectedParams: PNode) =
+  ## sems generic params in subscript expression
+  for i in 1..<n.len:
+    let
+      constraint =
+        if expectedParams != nil and i <= expectedParams.len:
+          expectedParams[i - 1].typ
+        else:
+          nil
+      e = semExprWithType(c, n[i], expectedType = constraint)
+    if e.typ == nil:
+      n[i].typ = errorType(c)
+    else:
+      n[i].typ = e.typ.skipTypes({tyTypeDesc})
 
 proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
   assert n.kind == nkBracketExpr
-  for i in 1..sonsLen(n)-1:
-    let e = semExpr(c, n.sons[i])
-    n.sons[i].typ = e.typ.skipTypes({tyTypeDesc})
+  setGenericParams(c, n, s.ast[genericParamsPos])
   var s = s
-  var a = n.sons[0]
+  var a = n[0]
   if a.kind == nkSym:
     # common case; check the only candidate has the right
     # number of generic type parameters:
-    if safeLen(s.ast.sons[genericParamsPos]) != n.len-1:
-      let expected = safeLen(s.ast.sons[genericParamsPos])
-      localError(c.config, n.info, errGenerated, "cannot instantiate: '" & renderTree(n) &
-         "'; got " & $(n.len-1) & " type(s) but expected " & $expected)
+    if s.ast[genericParamsPos].safeLen != n.len-1:
+      let expected = s.ast[genericParamsPos].safeLen
+      localError(c.config, getCallLineInfo(n), errGenerated, "cannot instantiate: '" & renderTree(n) &
+         "'; got " & $(n.len-1) & " typeof(s) but expected " & $expected)
       return n
     result = explicitGenericSym(c, n, s)
     if result == nil: result = explicitGenericInstError(c, n)
@@ -518,14 +906,14 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
     # choose the generic proc with the proper number of type parameters.
     # XXX I think this could be improved by reusing sigmatch.paramTypesMatch.
     # It's good enough for now.
-    result = newNodeI(a.kind, n.info)
-    for i in countup(0, len(a)-1):
-      var candidate = a.sons[i].sym
+    result = newNodeI(a.kind, getCallLineInfo(n))
+    for i in 0..<a.len:
+      var candidate = a[i].sym
       if candidate.kind in {skProc, skMethod, skConverter,
                             skFunc, skIterator}:
         # it suffices that the candidate has the proper number of generic
         # type parameters:
-        if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1:
+        if candidate.ast[genericParamsPos].safeLen == n.len-1:
           let x = explicitGenericSym(c, n, candidate)
           if x != nil: result.add(x)
     # get rid of nkClosedSymChoice if not ambiguous:
@@ -536,31 +924,58 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
   else:
     result = explicitGenericInstError(c, n)
 
-proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
-  # Searchs for the fn in the symbol table. If the parameter lists are suitable
+proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): tuple[s: PSym, state: TBorrowState] =
+  # Searches for the fn in the symbol table. If the parameter lists are suitable
   # for borrowing the sym in the symbol table is returned, else nil.
   # New approach: generate fn(x, y, z) where x, y, z have the proper types
   # and use the overloading resolution mechanism:
+  const desiredTypes = abstractVar + {tyCompositeTypeClass} - {tyTypeDesc, tyDistinct}
+
+  template getType(isDistinct: bool; t: PType):untyped =
+    if isDistinct: t.baseOfDistinct(c.graph, c.idgen) else: t
+
+  result = default(tuple[s: PSym, state: TBorrowState])
   var call = newNodeI(nkCall, fn.info)
   var hasDistinct = false
+  var isDistinct: bool
+  var x: PType
+  var t: PType
   call.add(newIdentNode(fn.name, fn.info))
   for i in 1..<fn.typ.n.len:
-    let param = fn.typ.n.sons[i]
-    let t = skipTypes(param.typ, abstractVar-{tyTypeDesc, tyDistinct})
-    if t.kind == tyDistinct or param.typ.kind == tyDistinct: hasDistinct = true
-    var x: PType
+    let param = fn.typ.n[i]
+    #[.
+      # We only want the type not any modifiers such as `ptr`, `var`, `ref` ...
+      # tyCompositeTypeClass is here for
+      # when using something like:
+      type Foo[T] = distinct int
+      proc `$`(f: Foo): string {.borrow.}
+      # We want to skip the `Foo` to get `int`
+    ]#
+    t = skipTypes(param.typ, desiredTypes)
+    isDistinct = t.kind == tyDistinct or param.typ.kind == tyDistinct
+    if t.kind == tyGenericInvocation and t.genericHead.last.kind == tyDistinct:
+      result.state = bsGeneric
+      return
+    if isDistinct: hasDistinct = true
     if param.typ.kind == tyVar:
-      x = newTypeS(tyVar, c)
-      x.addSonSkipIntLit t.baseOfDistinct
+      x = newTypeS(param.typ.kind, c)
+      x.addSonSkipIntLit(getType(isDistinct, t), c.idgen)
     else:
-      x = t.baseOfDistinct
-    call.add(newNodeIT(nkEmpty, fn.info, x))
+      x = getType(isDistinct, t)
+    var s = copySym(param.sym, c.idgen)
+    s.typ = x
+    s.info = param.info
+    call.add(newSymNode(s))
   if hasDistinct:
-    var resolved = semOverloadedCall(c, call, call, {fn.kind}, {})
+    let filter = if fn.kind in {skProc, skFunc}: {skProc, skFunc} else: {fn.kind}
+    var resolved = semOverloadedCall(c, call, call, filter, {})
     if resolved != nil:
-      result = resolved.sons[0].sym
-      if not compareTypes(result.typ.sons[0], fn.typ.sons[0], dcEqIgnoreDistinct):
-        result = nil
-      elif result.magic in {mArrPut, mArrGet}:
+      result.s = resolved[0].sym
+      result.state = bsMatch
+      if not compareTypes(result.s.typ.returnType, fn.typ.returnType, dcEqIgnoreDistinct, {IgnoreFlags}):
+        result.state = bsReturnNotMatch
+      elif result.s.magic in {mArrPut, mArrGet}:
         # cannot borrow these magics for now
-        result = nil
+        result.state = bsNotSupported
+  else:
+    result.state = bsNoDistinct
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index c858b6839..ca35ddc53 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -9,12 +9,16 @@
 
 ## This module contains the data structures for the semantic checking phase.
 
+import std/[tables, intsets, sets]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 import
-  strutils, intsets, options, lexer, ast, astalgo, trees, treetab,
-  wordrecg,
-  ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math,
-  magicsys, nversion, nimsets, parser, times, passes, vmdef,
-  modulegraphs, lineinfos
+  options, ast, astalgo, msgs, idents, renderer,
+  magicsys, vmdef, modulegraphs, lineinfos, pathutils
+
+import ic / ic
 
 type
   TOptionEntry* = object      # entries to put on a stack for pragma parsing
@@ -22,22 +26,24 @@ type
     defaultCC*: TCallingConvention
     dynlib*: PLib
     notes*: TNoteKinds
+    features*: set[Feature]
     otherPragmas*: PNode      # every pragma can be pushed
+    warningAsErrors*: TNoteKinds
 
   POptionEntry* = ref TOptionEntry
   PProcCon* = ref TProcCon
-  TProcCon* = object          # procedure context; also used for top-level
-                              # statements
+  TProcCon* {.acyclic.} = object # procedure context; also used for top-level
+                                 # statements
     owner*: PSym              # the symbol this context belongs to
     resultSym*: PSym          # the result symbol (if we are in a proc)
-    selfSym*: PSym            # the 'self' symbol (if available)
     nestedLoopCounter*: int   # whether we are in a loop or not
     nestedBlockCounter*: int  # whether we are in a block or not
-    inTryStmt*: int           # whether we are in a try statement; works also
-                              # in standalone ``except`` and ``finally``
+    breakInLoop*: bool        # whether we are in a loop without block
     next*: PProcCon           # used for stacking procedure contexts
-    wasForwarded*: bool       # whether the current proc has a separate header
-    mapping*: TIdTable
+    mappingExists*: bool
+    mapping*: Table[ItemId, PSym]
+    caseContext*: seq[tuple[n: PNode, idx: int]]
+    localBindStmts*: seq[PNode]
 
   TMatchedConcept* = object
     candidateType*: PType
@@ -49,7 +55,7 @@ type
     inst*: PInstantiation
 
   TExprFlag* = enum
-    efLValue, efWantIterator, efInTypeof,
+    efLValue, efWantIterator, efWantIterable, efInTypeof,
     efNeedStatic,
       # Use this in contexts where a static value is mandatory
     efPreferStatic,
@@ -62,57 +68,80 @@ type
       # you may be in position to supply a better error message
       # to the user.
     efWantStmt, efAllowStmt, efDetermineType, efExplain,
-    efAllowDestructor, efWantValue, efOperand, efNoSemCheck,
-    efNoEvaluateGeneric, efInCall, efFromHlo
+    efWantValue, efOperand, efNoSemCheck,
+    efNoEvaluateGeneric, efInCall, efFromHlo, efNoSem2Check,
+    efNoUndeclared, efIsDotCall, efCannotBeDotCall,
+      # Use this if undeclared identifiers should not raise an error during
+      # overload resolution.
+    efTypeAllowed # typeAllowed will be called after
+    efWantNoDefaults
+    efIgnoreDefaults # var statements without initialization
+    efAllowSymChoice # symchoice node should not be resolved
 
   TExprFlags* = set[TExprFlag]
 
-  TTypeAttachedOp* = enum
-    attachedAsgn,
-    attachedSink,
-    attachedDeepCopy,
-    attachedDestructor
+  ImportMode* = enum
+    importAll, importSet, importExcept
+  ImportedModule* = object
+    m*: PSym
+    case mode*: ImportMode
+    of importAll: discard
+    of importSet:
+      imported*: IntSet          # of PIdent.id
+    of importExcept:
+      exceptSet*: IntSet         # of PIdent.id
 
   PContext* = ref TContext
-  TContext* = object of TPassContext # a context represents a module
+  TContext* = object of TPassContext # a context represents the module
+                                     # that is currently being compiled
     enforceVoidContext*: PType
+      # for `if cond: stmt else: foo`, `foo` will be evaluated under
+      # enforceVoidContext != nil
+    voidType*: PType # for typeof(stmt)
     module*: PSym              # the module sym belonging to the context
     currentScope*: PScope      # current scope
-    importTable*: PScope       # scope for all imported symbols
+    moduleScope*: PScope       # scope for modules
+    imports*: seq[ImportedModule] # scope for all imported symbols
     topLevelScope*: PScope     # scope for all top-level symbols
     p*: PProcCon               # procedure context
+    intTypeCache*: array[-5..32, PType] # cache some common integer types
+                                        # to avoid type allocations
+    nilTypeCache*: PType
     matchedConcept*: ptr TMatchedConcept # the current concept being matched
     friendModules*: seq[PSym]  # friend modules; may access private data;
                                # this is used so that generic instantiations
                                # can access private object fields
     instCounter*: int          # to prevent endless instantiations
-
-    ambiguousSymbols*: IntSet  # ids of all ambiguous symbols (cannot
-                               # store this info in the syms themselves!)
+    templInstCounter*: ref int # gives every template instantiation a unique id
     inGenericContext*: int     # > 0 if we are in a generic type
     inStaticContext*: int      # > 0 if we are inside a static: block
     inUnrolledContext*: int    # > 0 if we are unrolling a loop
     compilesContextId*: int    # > 0 if we are in a ``compiles`` magic
     compilesContextIdGenerator*: int
     inGenericInst*: int        # > 0 if we are instantiating a generic
-    converters*: TSymSeq       # sequence of converters
-    patterns*: TSymSeq         # sequence of pattern matchers
+    converters*: seq[PSym]
+    patterns*: seq[PSym]       # sequence of pattern matchers
     optionStack*: seq[POptionEntry]
-    symMapping*: TIdTable      # every gensym'ed symbol needs to be mapped
-                               # to some new symbol in a generic instantiation
     libs*: seq[PLib]           # all libs used by this module
-    semConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas
-    semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
+    semConstExpr*: proc (c: PContext, n: PNode; expectedType: PType = nil): PNode {.nimcall.} # for the pragmas
+    semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode {.nimcall.}
+    semExprWithType*: proc (c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode {.nimcall.}
     semTryExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
-    semTryConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.}
+    semTryConstExpr*: proc (c: PContext, n: PNode; expectedType: PType = nil): PNode {.nimcall.}
+    computeRequiresInit*: proc (c: PContext, t: PType): bool {.nimcall.}
+    hasUnresolvedArgs*: proc (c: PContext, n: PNode): bool
+
     semOperand*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
     semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet
     semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
-                              filter: TSymKinds, flags: TExprFlags): PNode {.nimcall.}
+                              filter: TSymKinds, flags: TExprFlags, expectedType: PType = nil): PNode {.nimcall.}
     semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.}
-    semInferredLambda*: proc(c: PContext, pt: TIdTable, n: PNode): PNode
-    semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable,
+    semInferredLambda*: proc(c: PContext, pt: Table[ItemId, PType], n: PNode): PNode
+    semGenerateInstance*: proc (c: PContext, fn: PSym, pt: Table[ItemId, PType],
                                 info: TLineInfo): PSym
+    instantiateOnlyProcType*: proc (c: PContext, pt: TypeMapping,
+                                    prc: PSym, info: TLineInfo): PType
+      # used by sigmatch for explicit generic instantiations
     includedFiles*: IntSet    # used to detect recursive include files
     pureEnumFields*: TStrTable   # pure enum fields that can be used unambiguously
     userPragmas*: TStrTable
@@ -126,30 +155,78 @@ type
     inParallelStmt*: int
     instTypeBoundOp*: proc (c: PContext; dc: PSym; t: PType; info: TLineInfo;
                             op: TTypeAttachedOp; col: int): PSym {.nimcall.}
-    selfName*: PIdent
     cache*: IdentCache
     graph*: ModuleGraph
     signatures*: TStrTable
     recursiveDep*: string
     suggestionsMade*: bool
+    isAmbiguous*: bool # little hack
     features*: set[Feature]
-    inTypeContext*: int
-    typesWithOps*: seq[(PType, PType)] #\
-      # We need to instantiate the type bound ops lazily after
-      # the generic type has been constructed completely. See
-      # tests/destructor/topttree.nim for an example that
-      # would otherwise fail.
-    runnableExamples*: PNode
+    inTypeContext*, inConceptDecl*: int
+    unusedImports*: seq[(PSym, TLineInfo)]
+    exportIndirections*: HashSet[(int, int)] # (module.id, symbol.id)
+    importModuleMap*: Table[int, int] # (module.id, module.id)
+    lastTLineInfo*: TLineInfo
+    sideEffects*: Table[int, seq[(TLineInfo, PSym)]] # symbol.id index
+    inUncheckedAssignSection*: int
+    importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id])
+    skipTypes*: seq[PNode] # used to skip types between passes in type section. So far only used for inheritance, sets and generic bodies.
+    inTypeofContext*: int
+  TBorrowState* = enum
+    bsNone, bsReturnNotMatch, bsNoDistinct, bsGeneric, bsNotSupported, bsMatch
 
 template config*(c: PContext): ConfigRef = c.graph.config
 
+proc getIntLitType*(c: PContext; literal: PNode): PType =
+  # we cache some common integer literal types for performance:
+  let value = literal.intVal
+  if value >= low(c.intTypeCache) and value <= high(c.intTypeCache):
+    result = c.intTypeCache[value.int]
+    if result == nil:
+      let ti = getSysType(c.graph, literal.info, tyInt)
+      result = copyType(ti, c.idgen, ti.owner)
+      result.n = literal
+      c.intTypeCache[value.int] = result
+  else:
+    let ti = getSysType(c.graph, literal.info, tyInt)
+    result = copyType(ti, c.idgen, ti.owner)
+    result.n = literal
+
+proc setIntLitType*(c: PContext; result: PNode) =
+  let i = result.intVal
+  case c.config.target.intSize
+  of 8: result.typ = getIntLitType(c, result)
+  of 4:
+    if i >= low(int32) and i <= high(int32):
+      result.typ = getIntLitType(c, result)
+    else:
+      result.typ = getSysType(c.graph, result.info, tyInt64)
+  of 2:
+    if i >= low(int16) and i <= high(int16):
+      result.typ = getIntLitType(c, result)
+    elif i >= low(int32) and i <= high(int32):
+      result.typ = getSysType(c.graph, result.info, tyInt32)
+    else:
+      result.typ = getSysType(c.graph, result.info, tyInt64)
+  of 1:
+    # 8 bit CPUs are insane ...
+    if i >= low(int8) and i <= high(int8):
+      result.typ = getIntLitType(c, result)
+    elif i >= low(int16) and i <= high(int16):
+      result.typ = getSysType(c.graph, result.info, tyInt16)
+    elif i >= low(int32) and i <= high(int32):
+      result.typ = getSysType(c.graph, result.info, tyInt32)
+    else:
+      result.typ = getSysType(c.graph, result.info, tyInt64)
+  else:
+    internalError(c.config, result.info, "invalid int size")
+
 proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
-  result.genericSym = s
-  result.inst = inst
+  result = TInstantiationPair(genericSym: s, inst: inst)
 
 proc filename*(c: PContext): string =
   # the module's filename
-  return toFilename(c.config, FileIndex c.module.position)
+  result = toFilename(c.config, FileIndex c.module.position)
 
 proc scopeDepth*(c: PContext): int {.inline.} =
   result = if c.currentScope != nil: c.currentScope.depthLevel
@@ -163,11 +240,10 @@ proc getCurrOwner*(c: PContext): PSym =
   result = c.graph.owners[^1]
 
 proc pushOwner*(c: PContext; owner: PSym) =
-  add(c.graph.owners, owner)
+  c.graph.owners.add(owner)
 
 proc popOwner*(c: PContext) =
-  var length = len(c.graph.owners)
-  if length > 0: setLen(c.graph.owners, length - 1)
+  if c.graph.owners.len > 0: setLen(c.graph.owners, c.graph.owners.len - 1)
   else: internalError(c.config, "popOwner")
 
 proc lastOptionEntry*(c: PContext): POptionEntry =
@@ -176,13 +252,15 @@ proc lastOptionEntry*(c: PContext): POptionEntry =
 proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next
 
 proc put*(p: PProcCon; key, val: PSym) =
-  if p.mapping.data == nil: initIdTable(p.mapping)
+  if not p.mappingExists:
+    p.mapping = initTable[ItemId, PSym]()
+    p.mappingExists = true
   #echo "put into table ", key.info
-  p.mapping.idTablePut(key, val)
+  p.mapping[key.itemId] = val
 
 proc get*(p: PProcCon; key: PSym): PSym =
-  if p.mapping.data == nil: return nil
-  result = PSym(p.mapping.idTableGet(key))
+  if not p.mappingExists: return nil
+  result = p.mapping.getOrDefault(key.itemId)
 
 proc getGenSym*(c: PContext; s: PSym): PSym =
   if sfGenSym notin s.flags: return s
@@ -196,71 +274,138 @@ proc getGenSym*(c: PContext; s: PSym): PSym =
   result = s
 
 proc considerGenSyms*(c: PContext; n: PNode) =
-  if n.kind == nkSym:
+  if n == nil:
+    discard "can happen for nkFormalParams/nkArgList"
+  elif n.kind == nkSym:
     let s = getGenSym(c, n.sym)
     if n.sym != s:
       n.sym = s
   else:
     for i in 0..<n.safeLen:
-      considerGenSyms(c, n.sons[i])
+      considerGenSyms(c, n[i])
 
 proc newOptionEntry*(conf: ConfigRef): POptionEntry =
   new(result)
   result.options = conf.options
-  result.defaultCC = ccDefault
+  result.defaultCC = ccNimCall
   result.dynlib = nil
   result.notes = conf.notes
+  result.warningAsErrors = conf.warningAsErrors
+
+proc pushOptionEntry*(c: PContext): POptionEntry =
+  new(result)
+  var prev = c.optionStack[^1]
+  result.options = c.config.options
+  result.defaultCC = prev.defaultCC
+  result.dynlib = prev.dynlib
+  result.notes = c.config.notes
+  result.warningAsErrors = c.config.warningAsErrors
+  result.features = c.features
+  c.optionStack.add(result)
+
+proc popOptionEntry*(c: PContext) =
+  c.config.options = c.optionStack[^1].options
+  c.config.notes = c.optionStack[^1].notes
+  c.config.warningAsErrors = c.optionStack[^1].warningAsErrors
+  c.features = c.optionStack[^1].features
+  c.optionStack.setLen(c.optionStack.len - 1)
 
 proc newContext*(graph: ModuleGraph; module: PSym): PContext =
   new(result)
-  result.enforceVoidContext = PType(kind: tyStmt)
-  result.ambiguousSymbols = initIntSet()
-  result.optionStack = @[]
+  result.optionStack = @[newOptionEntry(graph.config)]
   result.libs = @[]
-  result.optionStack.add(newOptionEntry(graph.config))
   result.module = module
   result.friendModules = @[module]
   result.converters = @[]
   result.patterns = @[]
   result.includedFiles = initIntSet()
-  initStrTable(result.pureEnumFields)
-  initStrTable(result.userPragmas)
+  result.pureEnumFields = initStrTable()
+  result.userPragmas = initStrTable()
   result.generics = @[]
   result.unknownIdents = initIntSet()
   result.cache = graph.cache
   result.graph = graph
-  initStrTable(result.signatures)
-  result.typesWithOps = @[]
+  result.signatures = initStrTable()
   result.features = graph.config.features
-
-proc inclSym(sq: var TSymSeq, s: PSym) =
-  var L = len(sq)
-  for i in countup(0, L - 1):
-    if sq[i].id == s.id: return
-  setLen(sq, L + 1)
-  sq[L] = s
-
-proc addConverter*(c: PContext, conv: PSym) =
-  inclSym(c.converters, conv)
-
-proc addPattern*(c: PContext, p: PSym) =
-  inclSym(c.patterns, p)
+  if graph.config.symbolFiles != disabledSf:
+    let id = module.position
+    if graph.config.cmd != cmdM:
+      assert graph.packed[id].status in {undefined, outdated}
+    graph.packed[id].status = storing
+    graph.packed[id].module = module
+    initEncoder graph, module
+
+template packedRepr*(c): untyped = c.graph.packed[c.module.position].fromDisk
+template encoder*(c): untyped = c.graph.encoders[c.module.position]
+
+proc addIncludeFileDep*(c: PContext; f: FileIndex) =
+  if c.config.symbolFiles != disabledSf:
+    addIncludeFileDep(c.encoder, c.packedRepr, f)
+
+proc addImportFileDep*(c: PContext; f: FileIndex) =
+  if c.config.symbolFiles != disabledSf:
+    addImportFileDep(c.encoder, c.packedRepr, f)
+
+proc addPragmaComputation*(c: PContext; n: PNode) =
+  if c.config.symbolFiles != disabledSf:
+    addPragmaComputation(c.encoder, c.packedRepr, n)
+
+proc inclSym(sq: var seq[PSym], s: PSym): bool =
+  for i in 0..<sq.len:
+    if sq[i].id == s.id: return false
+  sq.add s
+  result = true
+
+proc addConverter*(c: PContext, conv: LazySym) =
+  assert conv.sym != nil
+  if inclSym(c.converters, conv.sym):
+    add(c.graph.ifaces[c.module.position].converters, conv)
+
+proc addConverterDef*(c: PContext, conv: LazySym) =
+  addConverter(c, conv)
+  if c.config.symbolFiles != disabledSf:
+    addConverter(c.encoder, c.packedRepr, conv.sym)
+
+proc addPureEnum*(c: PContext, e: LazySym) =
+  assert e.sym != nil
+  add(c.graph.ifaces[c.module.position].pureEnums, e)
+  if c.config.symbolFiles != disabledSf:
+    addPureEnum(c.encoder, c.packedRepr, e.sym)
+
+proc addPattern*(c: PContext, p: LazySym) =
+  assert p.sym != nil
+  if inclSym(c.patterns, p.sym):
+    add(c.graph.ifaces[c.module.position].patterns, p)
+  if c.config.symbolFiles != disabledSf:
+    addTrmacro(c.encoder, c.packedRepr, p.sym)
+
+proc exportSym*(c: PContext; s: PSym) =
+  strTableAdds(c.graph, c.module, s)
+  if c.config.symbolFiles != disabledSf:
+    addExported(c.encoder, c.packedRepr, s)
+
+proc reexportSym*(c: PContext; s: PSym) =
+  strTableAdds(c.graph, c.module, s)
+  if c.config.symbolFiles != disabledSf:
+    addReexport(c.encoder, c.packedRepr, s)
 
 proc newLib*(kind: TLibKind): PLib =
   new(result)
-  result.kind = kind          #initObjectSet(result.syms)
+  result.kind = kind          #result.syms = initObjectSet()
 
 proc addToLib*(lib: PLib, sym: PSym) =
   #if sym.annex != nil and not isGenericRoutine(sym):
   #  LocalError(sym.info, errInvalidPragma)
   sym.annex = lib
 
-proc newTypeS*(kind: TTypeKind, c: PContext): PType =
-  result = newType(kind, getCurrOwner(c))
+proc newTypeS*(kind: TTypeKind; c: PContext; son: sink PType = nil): PType =
+  result = newType(kind, c.idgen, getCurrOwner(c), son = son)
+
+proc makePtrType*(owner: PSym, baseType: PType; idgen: IdGenerator): PType =
+  result = newType(tyPtr, idgen, owner, skipIntLit(baseType, idgen))
 
 proc makePtrType*(c: PContext, baseType: PType): PType =
-  result = newTypeS(tyPtr, c)
-  addSonSkipIntLit(result, baseType)
+  makePtrType(getCurrOwner(c), baseType, c.idgen)
 
 proc makeTypeWithModifier*(c: PContext,
                            modifier: TTypeKind,
@@ -270,65 +415,62 @@ proc makeTypeWithModifier*(c: PContext,
   if modifier in {tyVar, tyLent, tyTypeDesc} and baseType.kind == modifier:
     result = baseType
   else:
-    result = newTypeS(modifier, c)
-    addSonSkipIntLit(result, baseType)
+    result = newTypeS(modifier, c, skipIntLit(baseType, c.idgen))
 
 proc makeVarType*(c: PContext, baseType: PType; kind = tyVar): PType =
   if baseType.kind == kind:
     result = baseType
   else:
-    result = newTypeS(kind, c)
-    addSonSkipIntLit(result, baseType)
-
-proc makeTypeDesc*(c: PContext, typ: PType): PType =
-  if typ.kind == tyTypeDesc:
-    result = typ
-  else:
-    result = newTypeS(tyTypeDesc, c)
-    result.addSonSkipIntLit(typ)
+    result = newTypeS(kind, c, skipIntLit(baseType, c.idgen))
 
 proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
-  let typedesc = makeTypeDesc(c, typ)
-  let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info,
+  let typedesc = newTypeS(tyTypeDesc, c)
+  incl typedesc.flags, tfCheckedForDestructor
+  internalAssert(c.config, typ != nil)
+  typedesc.addSonSkipIntLit(typ, c.idgen)
+  let sym = newSym(skType, c.cache.idAnon, c.idgen, getCurrOwner(c), info,
                    c.config.options).linkTo(typedesc)
-  return newSymNode(sym, info)
+  result = newSymNode(sym, info)
 
 proc makeTypeFromExpr*(c: PContext, n: PNode): PType =
   result = newTypeS(tyFromExpr, c)
   assert n != nil
   result.n = n
 
-proc newTypeWithSons*(owner: PSym, kind: TTypeKind, sons: seq[PType]): PType =
-  result = newType(kind, owner)
-  result.sons = sons
+when false:
+  proc newTypeWithSons*(owner: PSym, kind: TTypeKind, sons: seq[PType];
+                        idgen: IdGenerator): PType =
+    result = newType(kind, idgen, owner, sons = sons)
 
-proc newTypeWithSons*(c: PContext, kind: TTypeKind,
-                      sons: seq[PType]): PType =
-  result = newType(kind, getCurrOwner(c))
-  result.sons = sons
+  proc newTypeWithSons*(c: PContext, kind: TTypeKind,
+                        sons: seq[PType]): PType =
+    result = newType(kind, c.idgen, getCurrOwner(c), sons = sons)
 
 proc makeStaticExpr*(c: PContext, n: PNode): PNode =
   result = newNodeI(nkStaticExpr, n.info)
   result.sons = @[n]
   result.typ = if n.typ != nil and n.typ.kind == tyStatic: n.typ
-               else: newTypeWithSons(c, tyStatic, @[n.typ])
+               else: newTypeS(tyStatic, c, n.typ)
 
 proc makeAndType*(c: PContext, t1, t2: PType): PType =
   result = newTypeS(tyAnd, c)
-  result.sons = @[t1, t2]
+  result.rawAddSon t1
+  result.rawAddSon t2
   propagateToOwner(result, t1)
   propagateToOwner(result, t2)
   result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
   result.flags.incl tfHasMeta
 
 proc makeOrType*(c: PContext, t1, t2: PType): PType =
-  result = newTypeS(tyOr, c)
   if t1.kind != tyOr and t2.kind != tyOr:
-    result.sons = @[t1, t2]
+    result = newTypeS(tyOr, c)
+    result.rawAddSon t1
+    result.rawAddSon t2
   else:
+    result = newTypeS(tyOr, c)
     template addOr(t1) =
       if t1.kind == tyOr:
-        for x in t1.sons: result.rawAddSon x
+        for x in t1.kids: result.rawAddSon x
       else:
         result.rawAddSon t1
     addOr(t1)
@@ -339,26 +481,22 @@ proc makeOrType*(c: PContext, t1, t2: PType): PType =
   result.flags.incl tfHasMeta
 
 proc makeNotType*(c: PContext, t1: PType): PType =
-  result = newTypeS(tyNot, c)
-  result.sons = @[t1]
+  result = newTypeS(tyNot, c, son = t1)
   propagateToOwner(result, t1)
   result.flags.incl(t1.flags * {tfHasStatic})
   result.flags.incl tfHasMeta
 
 proc nMinusOne(c: PContext; n: PNode): PNode =
-  result = newNode(nkCall, n.info, @[
-    newSymNode(getSysMagic(c.graph, n.info, "pred", mPred)), n])
+  result = newTreeI(nkCall, n.info, newSymNode(getSysMagic(c.graph, n.info, "pred", mPred)), n)
 
 # Remember to fix the procs below this one when you make changes!
 proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
   let intType = getSysType(c.graph, n.info, tyInt)
-  result = newTypeS(tyRange, c)
-  result.sons = @[intType]
+  result = newTypeS(tyRange, c, son = intType)
   if n.typ != nil and n.typ.n == nil:
     result.flags.incl tfUnresolved
-  result.n = newNode(nkRange, n.info, @[
-    newIntTypeNode(nkIntLit, 0, intType),
-    makeStaticExpr(c, nMinusOne(c, n))])
+  result.n = newTreeI(nkRange, n.info, newIntTypeNode(0, intType),
+    makeStaticExpr(c, nMinusOne(c, n)))
 
 template rangeHasUnresolvedStatic*(t: PType): bool =
   tfUnresolved in t.flags
@@ -366,11 +504,31 @@ template rangeHasUnresolvedStatic*(t: PType): bool =
 proc errorType*(c: PContext): PType =
   ## creates a type representing an error state
   result = newTypeS(tyError, c)
+  result.flags.incl tfCheckedForDestructor
 
 proc errorNode*(c: PContext, n: PNode): PNode =
   result = newNodeI(nkEmpty, n.info)
   result.typ = errorType(c)
 
+# These mimic localError
+template localErrorNode*(c: PContext, n: PNode, info: TLineInfo, msg: TMsgKind, arg: string): PNode =
+  liMessage(c.config, info, msg, arg, doNothing, instLoc())
+  errorNode(c, n)
+
+template localErrorNode*(c: PContext, n: PNode, info: TLineInfo, arg: string): PNode =
+  liMessage(c.config, info, errGenerated, arg, doNothing, instLoc())
+  errorNode(c, n)
+
+template localErrorNode*(c: PContext, n: PNode, msg: TMsgKind, arg: string): PNode =
+  let n2 = n
+  liMessage(c.config, n2.info, msg, arg, doNothing, instLoc())
+  errorNode(c, n2)
+
+template localErrorNode*(c: PContext, n: PNode, arg: string): PNode =
+  let n2 = n
+  liMessage(c.config, n2.info, errGenerated, arg, doNothing, instLoc())
+  errorNode(c, n2)
+
 proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) =
   dest.kind = kind
   dest.owner = getCurrOwner(c)
@@ -380,11 +538,32 @@ proc makeRangeType*(c: PContext; first, last: BiggestInt;
                     info: TLineInfo; intType: PType = nil): PType =
   let intType = if intType != nil: intType else: getSysType(c.graph, info, tyInt)
   var n = newNodeI(nkRange, info)
-  addSon(n, newIntTypeNode(nkIntLit, first, intType))
-  addSon(n, newIntTypeNode(nkIntLit, last, intType))
+  n.add newIntTypeNode(first, intType)
+  n.add newIntTypeNode(last, intType)
   result = newTypeS(tyRange, c)
   result.n = n
-  addSonSkipIntLit(result, intType) # basetype of range
+  addSonSkipIntLit(result, intType, c.idgen) # basetype of range
+
+proc isSelf*(t: PType): bool {.inline.} =
+  ## Is this the magical 'Self' type from concepts?
+  t.kind == tyTypeDesc and tfPacked in t.flags
+
+proc makeTypeDesc*(c: PContext, typ: PType): PType =
+  if typ.kind == tyTypeDesc and not isSelf(typ):
+    result = typ
+  else:
+    result = newTypeS(tyTypeDesc, c, skipIntLit(typ, c.idgen))
+    incl result.flags, tfCheckedForDestructor
+
+proc symFromType*(c: PContext; t: PType, info: TLineInfo): PSym =
+  if t.sym != nil: return t.sym
+  result = newSym(skType, getIdent(c.cache, "AnonType"), c.idgen, t.owner, info)
+  result.flags.incl sfAnon
+  result.typ = t
+
+proc symNodeFromType*(c: PContext, t: PType, info: TLineInfo): PNode =
+  result = newSymNode(symFromType(c, t, info), info)
+  result.typ = makeTypeDesc(c, t)
 
 proc markIndirect*(c: PContext, s: PSym) {.inline.} =
   if s.kind in {skProc, skFunc, skConverter, skMethod, skIterator}:
@@ -398,10 +577,59 @@ proc illFormedAstLocal*(n: PNode; conf: ConfigRef) =
   localError(conf, n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
 
 proc checkSonsLen*(n: PNode, length: int; conf: ConfigRef) =
-  if sonsLen(n) != length: illFormedAst(n, conf)
+  if n.len != length: illFormedAst(n, conf)
 
 proc checkMinSonsLen*(n: PNode, length: int; conf: ConfigRef) =
-  if sonsLen(n) < length: illFormedAst(n, conf)
+  if n.len < length: illFormedAst(n, conf)
 
 proc isTopLevel*(c: PContext): bool {.inline.} =
   result = c.currentScope.depthLevel <= 2
+
+proc isTopLevelInsideDeclaration*(c: PContext, sym: PSym): bool {.inline.} =
+  # for routeKinds the scope isn't closed yet:
+  c.currentScope.depthLevel <= 2 + ord(sym.kind in routineKinds)
+
+proc pushCaseContext*(c: PContext, caseNode: PNode) =
+  c.p.caseContext.add((caseNode, 0))
+
+proc popCaseContext*(c: PContext) =
+  discard pop(c.p.caseContext)
+
+proc setCaseContextIdx*(c: PContext, idx: int) =
+  c.p.caseContext[^1].idx = idx
+
+template addExport*(c: PContext; s: PSym) =
+  ## convenience to export a symbol from the current module
+  addExport(c.graph, c.module, s)
+
+proc storeRodNode*(c: PContext, n: PNode) =
+  if c.config.symbolFiles != disabledSf:
+    toPackedNodeTopLevel(n, c.encoder, c.packedRepr)
+
+proc addToGenericProcCache*(c: PContext; s: PSym; inst: PInstantiation) =
+  c.graph.procInstCache.mgetOrPut(s.itemId, @[]).add LazyInstantiation(module: c.module.position, inst: inst)
+  if c.config.symbolFiles != disabledSf:
+    storeInstantiation(c.encoder, c.packedRepr, s, inst)
+
+proc addToGenericCache*(c: PContext; s: PSym; inst: PType) =
+  c.graph.typeInstCache.mgetOrPut(s.itemId, @[]).add LazyType(typ: inst)
+  if c.config.symbolFiles != disabledSf:
+    storeTypeInst(c.encoder, c.packedRepr, s, inst)
+
+proc sealRodFile*(c: PContext) =
+  if c.config.symbolFiles != disabledSf:
+    if c.graph.vm != nil:
+      for (m, n) in PCtx(c.graph.vm).vmstateDiff:
+        if m == c.module:
+          addPragmaComputation(c, n)
+    c.idgen.sealed = true # no further additions are allowed
+
+proc rememberExpansion*(c: PContext; info: TLineInfo; expandedSym: PSym) =
+  ## Templates and macros are very special in Nim; these have
+  ## inlining semantics so after semantic checking they leave no trace
+  ## in the sem'checked AST. This is very bad for IDE-like tooling
+  ## ("find all usages of this template" would not work). We need special
+  ## logic to remember macro/template expansions. This is done here and
+  ## delegated to the "rod" file mechanism.
+  if c.config.symbolFiles != disabledSf:
+    storeExpansion(c.encoder, c.packedRepr, info, expandedSym)
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 9d7c493a7..2885142a7 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -10,12 +10,15 @@
 # this module does the semantic checking for expressions
 # included from sem.nim
 
+when defined(nimCompilerStacktraceHints):
+  import std/stackframes
+
 const
   errExprXHasNoType = "expression '$1' has no type (or is ambiguous)"
   errXExpectsTypeOrValue = "'$1' expects a type or value"
   errVarForOutParamNeededX = "for a 'var' type a variable needs to be passed; but '$1' is immutable"
   errXStackEscape = "address of '$1' may not escape its stack frame"
-  errExprHasNoAddress = "expression has no address; maybe use 'unsafeAddr'"
+  errExprHasNoAddress = "expression has no address"
   errCannotInterpretNodeX = "cannot evaluate '$1'"
   errNamedExprExpected = "named expression expected"
   errNamedExprNotAllowed = "named expression not allowed here"
@@ -23,30 +26,34 @@ const
   errUndeclaredFieldX = "undeclared field: '$1'"
 
 proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
-                     flags: TExprFlags = {}): PNode =
-  markUsed(c.config, n.info, s, c.graph.usageSym)
-  styleCheckUse(n.info, s)
-  pushInfoContext(c.config, n.info)
-  result = evalTemplate(n, s, getCurrOwner(c), c.config, efFromHlo in flags)
-  if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags)
+                     flags: TExprFlags = {}; expectedType: PType = nil): PNode =
+  rememberExpansion(c, n.info, s)
+  let info = getCallLineInfo(n)
+  markUsed(c, info, s)
+  onUse(info, s)
+  # Note: This is n.info on purpose. It prevents template from creating an info
+  # context when called from an another template
+  pushInfoContext(c.config, n.info, s.detailedInfo)
+  result = evalTemplate(n, s, getCurrOwner(c), c.config, c.cache,
+                        c.templInstCounter, c.idgen, efFromHlo in flags)
+  if efNoSemCheck notin flags:
+    result = semAfterMacroCall(c, n, result, s, flags, expectedType)
   popInfoContext(c.config)
 
   # XXX: A more elaborate line info rewrite might be needed
-  result.info = n.info
+  result.info = info
 
 proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
 
+template rejectEmptyNode(n: PNode) =
+  # No matter what a nkEmpty node is not what we want here
+  if n.kind == nkEmpty: illFormedAst(n, c.config)
+
 proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
+  rejectEmptyNode(n)
   # same as 'semExprWithType' but doesn't check for proc vars
-  result = semExpr(c, n, flags + {efOperand})
-  #if result.kind == nkEmpty and result.typ.isNil:
-    # do not produce another redundant error message:
-    #raiseRecoverableError("")
-  #  result = errorNode(c, n)
+  result = semExpr(c, n, flags + {efOperand, efAllowSymChoice})
   if result.typ != nil:
-    # XXX tyGenericInst here?
-    if result.typ.kind == tyProc and tfUnresolved in result.typ.flags:
-      localError(c.config, n.info, errProcHasNoConcreteType % n.renderTree)
     if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
   elif {efWantStmt, efAllowStmt} * flags != {}:
     result.typ = newTypeS(tyVoid, c)
@@ -55,24 +62,51 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
                renderTree(result, {renderNoComments}))
     result.typ = errorType(c)
 
-proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
-  result = semExpr(c, n, flags+{efWantValue})
-  if result.isNil or result.kind == nkEmpty:
+proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType = nil): PNode =
+  rejectEmptyNode(n)
+  result = semExpr(c, n, flags+{efWantValue}, expectedType)
+
+  let
+    isEmpty = result.kind == nkEmpty
+    isTypeError = result.typ != nil and result.typ.kind == tyError
+
+  if isEmpty or isTypeError:
+    # bug #12741, redundant error messages are the lesser evil here:
+    localError(c.config, n.info, errExprXHasNoType %
+                renderTree(result, {renderNoComments}))
+
+  if isEmpty:
     # do not produce another redundant error message:
-    #raiseRecoverableError("")
     result = errorNode(c, n)
-  if result.typ == nil or result.typ == c.enforceVoidContext:
+
+proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
+  result = semExprCheck(c, n, flags-{efTypeAllowed}, expectedType)
+  if result.typ == nil and efInTypeof in flags:
+    result.typ = c.voidType
+  elif result.typ == nil or result.typ == c.enforceVoidContext:
     localError(c.config, n.info, errExprXHasNoType %
                 renderTree(result, {renderNoComments}))
     result.typ = errorType(c)
+  elif result.typ.kind == tyError:
+    # associates the type error to the current owner
+    result.typ = errorType(c)
+  elif efTypeAllowed in flags and result.typ.kind == tyProc and
+      hasUnresolvedParams(result, {}):
+    # mirrored with semOperand but only on efTypeAllowed
+    let owner = result.typ.owner
+    let err =
+      # consistent error message with evaltempl/semMacroExpr
+      if owner != nil and owner.kind in {skTemplate, skMacro}:
+        errMissingGenericParamsForTemplate % n.renderTree
+      else:
+        errProcHasNoConcreteType % n.renderTree
+    localError(c.config, n.info, err)
+    result.typ = errorType(c)
   else:
     if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
 
 proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
-  result = semExpr(c, n, flags)
-  if result.kind == nkEmpty:
-    # do not produce another redundant error message:
-    result = errorNode(c, n)
+  result = semExprCheck(c, n, flags)
   if result.typ == nil:
     localError(c.config, n.info, errExprXHasNoType %
                renderTree(result, {renderNoComments}))
@@ -81,8 +115,114 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
 proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
   result = symChoice(c, n, s, scClosed)
 
+proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode
+
+proc isSymChoice(n: PNode): bool {.inline.} =
+  result = n.kind in nkSymChoices
+
+proc resolveSymChoice(c: PContext, n: var PNode, flags: TExprFlags = {}, expectedType: PType = nil) =
+  ## Attempts to resolve a symchoice `n`, `n` remains a symchoice if
+  ## it cannot be resolved (this is the case even when `n.len == 1`).
+  if expectedType != nil:
+    # resolve from type inference, see paramTypesMatch
+    n = fitNode(c, expectedType, n, n.info)
+  if isSymChoice(n) and efAllowSymChoice notin flags:
+    # some contexts might want sym choices preserved for later disambiguation
+    # in general though they are ambiguous
+    let first = n[0].sym
+    var foundSym: PSym = nil
+    if first.kind == skEnumField and
+        not isAmbiguous(c, first.name, {skEnumField}, foundSym) and
+        foundSym == first:
+      # choose the first resolved enum field, i.e. the latest in scope
+      # to mirror behavior before overloadable enums
+      n = n[0]
+
+proc semOpenSym(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType,
+                warnDisabled = false): PNode =
+  ## sem the child of an `nkOpenSym` node, that is, captured symbols that can be
+  ## replaced by newly injected symbols in generics. `s` must be the captured
+  ## symbol if the original node is an `nkSym` node; and `nil` if it is an
+  ## `nkOpenSymChoice`, in which case only non-overloadable injected symbols
+  ## will be considered.
+  let isSym = n.kind == nkSym
+  let ident = n.getPIdent
+  assert ident != nil
+  let id = newIdentNode(ident, n.info)
+  c.isAmbiguous = false
+  let s2 = qualifiedLookUp(c, id, {})
+  # for `nkSym`, the first found symbol being different and unambiguous is
+  # enough to replace the original
+  # for `nkOpenSymChoice`, the first found symbol must be non-overloadable,
+  # since otherwise we have to use regular `nkOpenSymChoice` functionality
+  # but of the overloadable sym kinds, semExpr does not handle skModule, skMacro, skTemplate
+  # as overloaded in the case where `nkIdent` finds them first
+  if s2 != nil and not c.isAmbiguous and
+      ((isSym and s2 != n.sym) or
+        (not isSym and s2.kind notin OverloadableSyms-{skModule, skMacro, skTemplate})):
+    # only consider symbols defined under current proc:
+    var o = s2.owner
+    while o != nil:
+      if o == c.p.owner:
+        if not warnDisabled:
+          result = semExpr(c, id, flags, expectedType)
+          return
+        else:
+          var msg =
+            "a new symbol '" & ident.s & "' has been injected during " &
+            # msgContext should show what is being instantiated:
+            "template or generic instantiation, however "
+          if isSym:
+            msg.add(
+              getSymRepr(c.config, n.sym) & " captured at " &
+              "the proc declaration will be used instead; " &
+              "either enable --experimental:openSym to use the injected symbol, " &
+              "or `bind` this captured symbol explicitly")
+          else:
+            msg.add(
+              "overloads of " & ident.s & " will be used instead; " &
+              "either enable --experimental:openSym to use the injected symbol, " &
+              "or `bind` this symbol explicitly")
+          message(c.config, n.info, warnIgnoredSymbolInjection, msg)
+          break
+      o = o.owner
+  # nothing found
+  n.flags.excl nfDisabledOpenSym
+  if not warnDisabled and isSym:
+    result = semExpr(c, n, flags, expectedType)
+  else:
+    result = nil
+    if not isSym:
+      # set symchoice node type back to None
+      n.typ = newTypeS(tyNone, c)
+
+proc semSymChoice(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
+  if n.kind == nkOpenSymChoice:
+    result = semOpenSym(c, n, flags, expectedType,
+      warnDisabled = nfDisabledOpenSym in n.flags and
+        genericsOpenSym notin c.features)
+    if result != nil:
+      return
+  result = n
+  resolveSymChoice(c, result, flags, expectedType)
+  if isSymChoice(result) and result.len == 1:
+    # resolveSymChoice can leave 1 sym
+    result = result[0]
+  if isSymChoice(result) and efAllowSymChoice notin flags:
+    var err = "ambiguous identifier: '" & result[0].sym.name.s &
+      "' -- use one of the following:\n"
+    for child in n:
+      let candidate = child.sym
+      err.add "  " & candidate.owner.name.s & "." & candidate.name.s
+      err.add ": " & typeToString(candidate.typ) & "\n"
+    localError(c.config, n.info, err)
+    n.typ = errorType(c)
+    result = n
+  if result.kind == nkSym:
+    result = semSym(c, result, result.sym, flags)
+
 proc inlineConst(c: PContext, n: PNode, s: PSym): PNode {.inline.} =
-  result = copyTree(s.ast)
+  result = copyTree(s.astdef)
   if result.isNil:
     localError(c.config, n.info, "constant of type '" & typeToString(s.typ) & "' has no value")
     result = newSymNode(s)
@@ -94,7 +234,8 @@ type
   TConvStatus = enum
     convOK,
     convNotNeedeed,
-    convNotLegal
+    convNotLegal,
+    convNotInRange
 
 proc checkConversionBetweenObjects(castDest, src: PType; pointers: int): TConvStatus =
   let diff = inheritanceDiff(castDest, src)
@@ -106,44 +247,79 @@ proc checkConversionBetweenObjects(castDest, src: PType; pointers: int): TConvSt
 const
   IntegralTypes = {tyBool, tyEnum, tyChar, tyInt..tyUInt64}
 
-proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus =
+proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus =
+  let srcTyp = src.typ.skipTypes({tyStatic})
   result = convOK
-  if sameType(castDest, src) and castDest.sym == src.sym:
+  if sameType(targetTyp, srcTyp) and targetTyp.sym == srcTyp.sym:
     # don't annoy conversions that may be needed on another processor:
-    if castDest.kind notin IntegralTypes+{tyRange}:
+    if targetTyp.kind notin IntegralTypes+{tyRange}:
       result = convNotNeedeed
     return
-  var d = skipTypes(castDest, abstractVar)
-  var s = src
+  var d = skipTypes(targetTyp, abstractVar)
+  var s = srcTyp
   if s.kind in tyUserTypeClasses and s.isResolvedUserTypeClass:
-    s = s.lastSon
-  s = skipTypes(s, abstractVar-{tyTypeDesc})
+    s = s.last
+  s = skipTypes(s, abstractVar-{tyTypeDesc, tyOwned})
+  if s.kind == tyOwned and d.kind != tyOwned:
+    s = s.skipModifier
   var pointers = 0
-  while (d != nil) and (d.kind in {tyPtr, tyRef}) and (d.kind == s.kind):
-    d = d.lastSon
-    s = s.lastSon
+  while (d != nil) and (d.kind in {tyPtr, tyRef, tyOwned}):
+    if s.kind == tyOwned and d.kind != tyOwned:
+      s = s.skipModifier
+    elif d.kind != s.kind:
+      break
+    else:
+      d = d.elementType
+      s = s.elementType
     inc pointers
+
+  let targetBaseTyp = skipTypes(targetTyp, abstractVarRange)
+  let srcBaseTyp = skipTypes(srcTyp, abstractVarRange-{tyTypeDesc})
+
   if d == nil:
     result = convNotLegal
-  elif d.kind == tyObject and s.kind == tyObject:
-    result = checkConversionBetweenObjects(d, s, pointers)
-  elif (skipTypes(castDest, abstractVarRange).kind in IntegralTypes) and
-      (skipTypes(src, abstractVarRange-{tyTypeDesc}).kind in IntegralTypes):
-    # accept conversion between integral types
-    discard
+  elif d.skipTypes(abstractInst).kind == tyObject and s.skipTypes(abstractInst).kind == tyObject:
+    result = checkConversionBetweenObjects(d.skipTypes(abstractInst), s.skipTypes(abstractInst), pointers)
+  elif (targetBaseTyp.kind in IntegralTypes) and
+      (srcBaseTyp.kind in IntegralTypes):
+    if targetTyp.kind == tyEnum and srcBaseTyp.kind == tyEnum and
+        not sameType(targetTyp, srcBaseTyp):
+      message(c.config, src.info, warnSuspiciousEnumConv, "suspicious code: enum to enum conversion")
+    # `elif` would be incorrect here
+    if targetTyp.kind == tyBool:
+      discard "convOk"
+    elif targetTyp.isOrdinalType:
+      if src.kind in nkCharLit..nkUInt64Lit and
+          src.getInt notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp) and
+          targetTyp.kind notin {tyUInt..tyUInt64}:
+        result = convNotInRange
+      elif src.kind in nkFloatLit..nkFloat64Lit and
+          (classify(src.floatVal) in {fcNan, fcNegInf, fcInf} or
+            src.floatVal.int64 notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp)):
+        result = convNotInRange
+    elif targetBaseTyp.kind in tyFloat..tyFloat64:
+      if src.kind in nkFloatLit..nkFloat64Lit and
+          not floatRangeCheck(src.floatVal, targetTyp):
+        result = convNotInRange
+      elif src.kind in nkCharLit..nkUInt64Lit and
+          not floatRangeCheck(src.intVal.float, targetTyp):
+        result = convNotInRange
   else:
     # we use d, s here to speed up that operation a bit:
+    if d.kind == tyFromExpr:
+      result = convNotLegal
+      return
     case cmpTypes(c, d, s)
     of isNone, isGeneric:
-      if not compareTypes(castDest, src, dcEqIgnoreDistinct):
+      if not compareTypes(targetTyp.skipTypes(abstractVar), srcTyp.skipTypes({tyOwned}), dcEqIgnoreDistinct):
         result = convNotLegal
     else:
       discard
 
-proc isCastable(conf: ConfigRef; dst, src: PType): bool =
+proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool =
   ## Checks whether the source type can be cast to the destination type.
   ## Casting is very unrestrictive; casts are allowed as long as
-  ## castDest.size >= src.size, and typeAllowed(dst, skParam)
+  ## dst.size >= src.size, and typeAllowed(dst, skParam)
   #const
   #  castableTypeKinds = {tyInt, tyPtr, tyRef, tyCstring, tyString,
   #                       tySequence, tyPointer, tyNil, tyOpenArray,
@@ -153,27 +329,38 @@ proc isCastable(conf: ConfigRef; dst, src: PType): bool =
     return false
   if skipTypes(src, abstractInst-{tyTypeDesc}).kind == tyTypeDesc:
     return false
+  if skipTypes(dst, abstractInst).kind == tyBuiltInTypeClass:
+    return false
+  let conf = c.config
+  if conf.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
+    let d = skipTypes(dst, abstractInst)
+    let s = skipTypes(src, abstractInst)
+    if d.kind == tyRef and s.kind == tyRef and s[0].isFinal != d[0].isFinal:
+      return false
+    elif d.kind in IntegralTypes and s.kind in {tyString, tySequence}:
+      return false
 
   var dstSize, srcSize: BiggestInt
   dstSize = computeSize(conf, dst)
   srcSize = computeSize(conf, src)
+  if dstSize == -3 or srcSize == -3: # szUnknownSize
+    # The Nim compiler can't detect if it's legal or not.
+    # Just assume the programmer knows what he is doing.
+    return true
   if dstSize < 0:
-    result = false
+    return false
   elif srcSize < 0:
-    result = false
-  elif typeAllowed(dst, skParam) != nil:
-    result = false
+    return false
+  elif typeAllowed(dst, skParam, c, {taIsCastable}) != nil:
+    return false
   elif dst.kind == tyProc and dst.callConv == ccClosure:
-    result = src.kind == tyProc and src.callConv == ccClosure
+    return src.kind == tyProc and src.callConv == ccClosure
   else:
     result = (dstSize >= srcSize) or
         (skipTypes(dst, abstractInst).kind in IntegralTypes) or
         (skipTypes(src, abstractInst-{tyTypeDesc}).kind in IntegralTypes)
   if result and src.kind == tyNil:
-    result = dst.size <= conf.target.ptrSize
-
-proc isSymChoice(n: PNode): bool {.inline.} =
-  result = n.kind in nkSymChoices
+    return dst.size <= conf.target.ptrSize
 
 proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) =
   # XXX: liftParamType started to perform addDecl
@@ -185,101 +372,145 @@ proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) =
   closeScope(c)
   if lifted != nil: t = lifted
 
-proc semConv(c: PContext, n: PNode): PNode =
-  if sonsLen(n) != 2:
+proc isOwnedSym(c: PContext; n: PNode): bool =
+  let s = qualifiedLookUp(c, n, {})
+  result = s != nil and sfSystemModule in s.owner.flags and s.name.s == "owned"
+
+proc semConv(c: PContext, n: PNode; flags: TExprFlags = {}, expectedType: PType = nil): PNode =
+  if n.len != 2:
     localError(c.config, n.info, "a type conversion takes exactly one argument")
     return n
 
   result = newNodeI(nkConv, n.info)
-  var targetType = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
+
+  var targetType = semTypeNode(c, n[0], nil)
+  case targetType.skipTypes({tyDistinct}).kind
+  of tyTypeDesc:
+    internalAssert c.config, targetType.len > 0
+    if targetType.base.kind == tyNone:
+      return semTypeOf(c, n)
+    else:
+      targetType = targetType.base
+  of tyStatic:
+    var evaluated = semStaticExpr(c, n[1], expectedType)
+    if evaluated.kind == nkType or evaluated.typ.kind == tyTypeDesc:
+      result = n
+      result.typ = c.makeTypeDesc semStaticType(c, evaluated, nil)
+      return
+    elif targetType.base.kind == tyNone:
+      return evaluated
+    else:
+      targetType = targetType.base
+  of tyAnything, tyUntyped, tyTyped:
+    localError(c.config, n.info, "illegal type conversion to '$1'" % typeToString(targetType))
+  else: discard
+
   maybeLiftType(targetType, c, n[0].info)
 
-  if targetType.kind in {tySink, tyLent}:
-    let baseType = semTypeNode(c, n.sons[1], nil).skipTypes({tyTypeDesc})
-    let t = newTypeS(targetType.kind, c)
-    t.rawAddSonNoPropagationOfTypeFlags baseType
+  if targetType.kind in {tySink, tyLent} or isOwnedSym(c, n[0]):
+    let baseType = semTypeNode(c, n[1], nil).skipTypes({tyTypeDesc})
+    let t = newTypeS(targetType.kind, c, baseType)
+    if targetType.kind == tyOwned:
+      t.flags.incl tfHasOwned
     result = newNodeI(nkType, n.info)
     result.typ = makeTypeDesc(c, t)
     return
 
-  result.addSon copyTree(n.sons[0])
+  result.add copyTree(n[0])
 
   # 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 '='")
-  var op = semExprWithType(c, n.sons[1])
-  if targetType.isMetaType:
+    localError(c.config, n.info, "object construction uses ':', not '='")
+  var op = semExprWithType(c, n[1], flags * {efDetermineType} + {efAllowSymChoice})
+  if isSymChoice(op) and op[0].sym.kind notin routineKinds:
+    # T(foo) disambiguation syntax only allowed for routines
+    op = semSymChoice(c, op)
+  if targetType.kind != tyGenericParam and targetType.isMetaType:
     let final = inferWithMetatype(c, targetType, op, true)
-    result.addSon final
+    result.add final
     result.typ = final.typ
     return
 
   result.typ = targetType
   # XXX op is overwritten later on, this is likely added too early
   # here or needs to be overwritten too then.
-  addSon(result, op)
+  result.add op
+
+  if targetType.kind == tyGenericParam or
+      (op.typ != nil and op.typ.kind == tyFromExpr and c.inGenericContext > 0):
+    # expression is compiled early in a generic body
+    result.typ = makeTypeFromExpr(c, copyTree(result))
+    return result
 
   if not isSymChoice(op):
-    let status = checkConvertible(c, result.typ, op.typ)
+    let status = checkConvertible(c, result.typ, op)
     case status
     of convOK:
       # handle SomeProcType(SomeGenericProc)
       if op.kind == nkSym and op.sym.isGenericRoutine:
-        result.sons[1] = fitNode(c, result.typ, result.sons[1], result.info)
+        result[1] = fitNode(c, result.typ, result[1], result.info)
       elif op.kind in {nkPar, nkTupleConstr} and targetType.kind == tyTuple:
         op = fitNode(c, targetType, op, result.info)
     of convNotNeedeed:
-      message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
+      if efNoSem2Check notin flags:
+        message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
     of convNotLegal:
-      result = fitNode(c, result.typ, result.sons[1], result.info)
+      result = fitNode(c, result.typ, result[1], result.info)
       if result == nil:
         localError(c.config, n.info, "illegal conversion from '$1' to '$2'" %
           [op.typ.typeToString, result.typ.typeToString])
+    of convNotInRange:
+      let value =
+        if op.kind in {nkCharLit..nkUInt64Lit}: $op.getInt else: $op.getFloat
+      localError(c.config, n.info, errGenerated, value & " can't be converted to " &
+        result.typ.typeToString)
   else:
-    for i in countup(0, sonsLen(op) - 1):
-      let it = op.sons[i]
-      let status = checkConvertible(c, result.typ, it.typ)
+    for i in 0..<op.len:
+      let it = op[i]
+      let status = checkConvertible(c, result.typ, it)
       if status in {convOK, convNotNeedeed}:
-        markUsed(c.config, n.info, it.sym, c.graph.usageSym)
-        styleCheckUse(n.info, it.sym)
+        markUsed(c, n.info, it.sym)
+        onUse(n.info, it.sym)
         markIndirect(c, it.sym)
         return it
-    errorUseQualifier(c, n.info, op.sons[0].sym)
+    errorUseQualifier(c, n.info, op[0].sym)
 
 proc semCast(c: PContext, n: PNode): PNode =
   ## Semantically analyze a casting ("cast[type](param)")
   checkSonsLen(n, 2, c.config)
-  let targetType = semTypeNode(c, n.sons[0], nil)
-  let castedExpr = semExprWithType(c, n.sons[1])
+  let targetType = semTypeNode(c, n[0], nil)
+  let castedExpr = semExprWithType(c, n[1])
+  if castedExpr.kind == nkClosedSymChoice:
+    errorUseQualifier(c, n[1].info, castedExpr)
+  if targetType == nil:
+    localError(c.config, n.info, "Invalid usage of cast, cast requires a type to convert to, e.g., cast[int](0d).")
   if tfHasMeta in targetType.flags:
-    localError(c.config, n.sons[0].info, "cannot cast to a non concrete type: '$1'" % $targetType)
-  if not isCastable(c.config, targetType, castedExpr.typ):
-    let tar = $targetType
-    let alt = typeToString(targetType, preferDesc)
-    let msg = if tar != alt: tar & "=" & alt else: tar
-    localError(c.config, n.info, "expression cannot be cast to " & msg)
+    localError(c.config, n[0].info, "cannot cast to a non concrete type: '$1'" % $targetType)
+  if not isCastable(c, targetType, castedExpr.typ, n.info):
+    localError(c.config, n.info, "expression cannot be cast to '$1'" % $targetType)
   result = newNodeI(nkCast, n.info)
   result.typ = targetType
-  addSon(result, copyTree(n.sons[0]))
-  addSon(result, castedExpr)
+  result.add copyTree(n[0])
+  result.add castedExpr
 
 proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
   const
     opToStr: array[mLow..mHigh, string] = ["low", "high"]
-  if sonsLen(n) != 2:
+  if n.len != 2:
     localError(c.config, n.info, errXExpectsTypeOrValue % opToStr[m])
   else:
-    n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType})
-    var typ = skipTypes(n.sons[1].typ, abstractVarRange + {tyTypeDesc})
+    n[1] = semExprWithType(c, n[1], {efDetermineType})
+    var typ = skipTypes(n[1].typ, abstractVarRange + {tyTypeDesc, tyUserTypeClassInst})
     case typ.kind
-    of tySequence, tyString, tyCString, tyOpenArray, tyVarargs:
+    of tySequence, tyString, tyCstring, tyOpenArray, tyVarargs:
       n.typ = getSysType(c.graph, n.info, tyInt)
     of tyArray:
-      n.typ = typ.sons[0] # indextype
-    of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32:
-      # do not skip the range!
-      n.typ = n.sons[1].typ.skipTypes(abstractVar)
+      n.typ = typ.indexType
+      if n.typ.kind == tyRange and emptyRange(n.typ.n[0], n.typ.n[1]): #Invalid range
+        n.typ = getSysType(c.graph, n.info, tyInt)
+    of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt..tyUInt64, tyFloat..tyFloat64:
+      n.typ = n[1].typ.skipTypes({tyTypeDesc})
     of tyGenericParam:
       # prepare this for resolving in semtypinst:
       # we must use copyTree here in order to avoid creating a cycle
@@ -289,104 +520,157 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
       localError(c.config, n.info, "invalid argument for: " & opToStr[m])
   result = n
 
-proc semSizeof(c: PContext, n: PNode): PNode =
-  if sonsLen(n) != 2:
-    localError(c.config, n.info, errXExpectsTypeOrValue % "sizeof")
-  else:
-    n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType})
-    #restoreOldStyleType(n.sons[1])
-  n.typ = getSysType(c.graph, n.info, tyInt)
-  result = n
+proc fixupStaticType(c: PContext, n: PNode) =
+  # This proc can be applied to evaluated expressions to assign
+  # them a static type.
+  #
+  # XXX: with implicit static, this should not be necessary,
+  # because the output type of operations such as `semConstExpr`
+  # should be a static type (as well as the type of any other
+  # expression that can be implicitly evaluated). For now, we
+  # apply this measure only in code that is enlightened to work
+  # with static types.
+  if n.typ.kind != tyStatic:
+    n.typ = newTypeS(tyStatic, c, n.typ)
+    n.typ.n = n # XXX: cycles like the one here look dangerous.
+                # Consider using `n.copyTree`
 
 proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
-  internalAssert c.config, n.sonsLen == 3 and
-    n[1].typ != nil and n[1].typ.kind == tyTypeDesc and
+  internalAssert c.config,
+    n.len == 3 and
+    n[1].typ != nil and
     n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
 
-  let t1 = n[1].typ.skipTypes({tyTypeDesc})
+  var
+    res = false
+    t1 = n[1].typ
+    t2 = n[2].typ
+
+  if t1.kind == tyTypeDesc and t2.kind != tyTypeDesc:
+    t1 = t1.base
 
   if n[2].kind in {nkStrLit..nkTripleStrLit}:
     case n[2].strVal.normalize
     of "closure":
       let t = skipTypes(t1, abstractRange)
-      result = newIntNode(nkIntLit, ord(t.kind == tyProc and
-                                        t.callConv == ccClosure and
-                                        tfIterator notin t.flags))
+      res = t.kind == tyProc and
+            t.callConv == ccClosure
+    of "iterator":
+      # holdover from when `is iterator` didn't work
+      let t = skipTypes(t1, abstractRange)
+      res = t.kind == tyProc and
+            t.callConv == ccClosure and
+            tfIterator in t.flags
     else:
-      result = newIntNode(nkIntLit, 0)
+      res = false
   else:
-    var rhsOrigType = n[2].typ
-    var t2 = rhsOrigType.skipTypes({tyTypeDesc})
-    maybeLiftType(t2, c, n.info)
-    var m: TCandidate
-    initCandidate(c, m, t2)
+    if t1.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct}).kind != tyGenericBody:
+      maybeLiftType(t2, c, n.info)
+    else:
+      #[
+      for this case:
+      type Foo = object[T]
+      Foo is Foo
+      ]#
+      discard
+    var m = newCandidate(c, t2)
     if efExplain in flags:
       m.diagnostics = @[]
       m.diagnosticsEnabled = true
-    let match = typeRel(m, t2, t1) >= isSubtype # isNone
-    result = newIntNode(nkIntLit, ord(match))
+    res = typeRel(m, t2, t1) >= isSubtype # isNone
+    # `res = sameType(t1, t2)` would be wrong, e.g. for `int is (int|float)`
 
+  result = newIntNode(nkIntLit, ord(res))
   result.typ = n.typ
 
 proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
-  if sonsLen(n) != 3:
+  if n.len != 3 or n[2].kind == nkEmpty:
     localError(c.config, n.info, "'is' operator takes 2 arguments")
+    return errorNode(c, n)
 
+  let boolType = getSysType(c.graph, n.info, tyBool)
   result = n
-  n.typ = getSysType(c.graph, n.info, tyBool)
+  n.typ = boolType
+  var liftLhs = true
 
-  n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator})
+  n[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator})
   if n[2].kind notin {nkStrLit..nkTripleStrLit}:
     let t2 = semTypeNode(c, n[2], nil)
-    n.sons[2] = newNodeIT(nkType, n[2].info, t2)
+    n[2] = newNodeIT(nkType, n[2].info, t2)
+    if t2.kind == tyStatic:
+      let evaluated = tryConstExpr(c, n[1])
+      if evaluated != nil:
+        c.fixupStaticType(evaluated)
+        n[1] = evaluated
+      else:
+        result = newIntNode(nkIntLit, 0)
+        result.typ = boolType
+        return
+    elif t2.kind == tyTypeDesc and
+        (t2.base.kind == tyNone or tfExplicit in t2.flags):
+      # When the right-hand side is an explicit type, we must
+      # not allow regular values to be matched against the type:
+      liftLhs = false
+  else:
+    n[2] = semExpr(c, n[2])
 
-  let lhsType = n[1].typ
+  var lhsType = n[1].typ
   if lhsType.kind != tyTypeDesc:
-    n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info)
-  elif lhsType.base.kind == tyNone:
-    # this is a typedesc variable, leave for evals
-    return
+    if liftLhs:
+      n[1] = makeTypeSymNode(c, lhsType, n[1].info)
+      lhsType = n[1].typ
+  else:
+    if c.inGenericContext > 0 and lhsType.base.containsUnresolvedType:
+      # BUGFIX: don't evaluate this too early: ``T is void``
+      return
 
-  # BUGFIX: don't evaluate this too early: ``T is void``
-  if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n, flags)
+  result = isOpImpl(c, n, flags)
 
 proc semOpAux(c: PContext, n: PNode) =
-  const flags = {efDetermineType}
-  for i in countup(1, n.sonsLen-1):
-    var a = n.sons[i]
-    if a.kind == nkExprEqExpr and sonsLen(a) == 2:
-      let info = a.sons[0].info
-      a.sons[0] = newIdentNode(considerQuotedIdent(c, a.sons[0], a), info)
-      a.sons[1] = semExprWithType(c, a.sons[1], flags)
-      a.typ = a.sons[1].typ
+  const flags = {efDetermineType, efAllowSymChoice}
+  for i in 1..<n.len:
+    var a = n[i]
+    if a.kind == nkExprEqExpr and a.len == 2:
+      let info = a[0].info
+      a[0] = newIdentNode(considerQuotedIdent(c, a[0], a), info)
+      a[1] = semExprWithType(c, a[1], flags)
+      a.typ = a[1].typ
     else:
-      n.sons[i] = semExprWithType(c, a, flags)
+      n[i] = semExprWithType(c, a, flags)
 
 proc overloadedCallOpr(c: PContext, n: PNode): PNode =
   # quick check if there is *any* () operator overloaded:
   var par = getIdent(c.cache, "()")
-  if searchInScopes(c, par) == nil:
+  var amb = false
+  if searchInScopes(c, par, amb) == nil:
     result = nil
   else:
     result = newNodeI(nkCall, n.info)
-    addSon(result, newIdentNode(par, n.info))
-    for i in countup(0, sonsLen(n) - 1): addSon(result, n.sons[i])
-    result = semExpr(c, result)
+    result.add newIdentNode(par, n.info)
+    for i in 0..<n.len: result.add n[i]
+    result = semExpr(c, result, flags = {efNoUndeclared})
 
 proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
   case n.kind
-  of nkCurly, nkBracket:
-    for i in countup(0, sonsLen(n) - 1):
-      changeType(c, n.sons[i], elemType(newType), check)
+  of nkCurly:
+    for i in 0..<n.len:
+      if n[i].kind == nkRange:
+        changeType(c, n[i][0], elemType(newType), check)
+        changeType(c, n[i][1], elemType(newType), check)
+      else:
+        changeType(c, n[i], elemType(newType), check)
+  of nkBracket:
+    for i in 0..<n.len:
+      changeType(c, n[i], elemType(newType), check)
   of nkPar, nkTupleConstr:
-    let tup = newType.skipTypes({tyGenericInst, tyAlias, tySink})
+    let tup = newType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
     if tup.kind != tyTuple:
       if tup.kind == tyObject: return
       globalError(c.config, n.info, "no tuple type for constructor")
-    elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr:
+    elif n.len > 0 and n[0].kind == nkExprColonExpr:
       # named tuple?
-      for i in countup(0, sonsLen(n) - 1):
-        var m = n.sons[i].sons[0]
+      for i in 0..<n.len:
+        var m = n[i][0]
         if m.kind != nkSym:
           globalError(c.config, m.info, "invalid tuple constructor")
           return
@@ -395,106 +679,212 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
           if f == nil:
             globalError(c.config, m.info, "unknown identifier: " & m.sym.name.s)
             return
-          changeType(c, n.sons[i].sons[1], f.typ, check)
+          changeType(c, n[i][1], f.typ, check)
         else:
-          changeType(c, n.sons[i].sons[1], tup.sons[i], check)
+          changeType(c, n[i][1], tup[i], check)
     else:
-      for i in countup(0, sonsLen(n) - 1):
-        changeType(c, n.sons[i], tup.sons[i], check)
+      for i in 0..<n.len:
+        changeType(c, n[i], tup[i], check)
         when false:
-          var m = n.sons[i]
-          var a = newNodeIT(nkExprColonExpr, m.info, newType.sons[i])
-          addSon(a, newSymNode(newType.n.sons[i].sym))
-          addSon(a, m)
-          changeType(m, tup.sons[i], check)
+          var m = n[i]
+          var a = newNodeIT(nkExprColonExpr, m.info, newType[i])
+          a.add newSymNode(newType.n[i].sym)
+          a.add m
+          changeType(m, tup[i], check)
   of nkCharLit..nkUInt64Lit:
-    if check and n.kind != nkUInt64Lit:
+    if check and n.kind != nkUInt64Lit and not sameTypeOrNil(n.typ, newType):
       let value = n.intVal
       if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
         localError(c.config, n.info, "cannot convert " & $value &
-                                         " to " & typeToString(newType))
+                                         " to " & typeNameAndDesc(newType))
+  of nkFloatLit..nkFloat64Lit:
+    if check and not floatRangeCheck(n.floatVal, newType):
+      localError(c.config, n.info, errFloatToString % [$n.floatVal, typeNameAndDesc(newType)])
+  of nkSym:
+    if check and n.sym.kind == skEnumField and not sameTypeOrNil(n.sym.typ, newType):
+      let value = n.sym.position
+      if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
+        localError(c.config, n.info, "cannot convert '" & n.sym.name.s &
+                                         "' to '" & typeNameAndDesc(newType) & "'")
   else: discard
   n.typ = newType
 
 proc arrayConstrType(c: PContext, n: PNode): PType =
   var typ = newTypeS(tyArray, c)
   rawAddSon(typ, nil)     # index type
-  if sonsLen(n) == 0:
+  if n.len == 0:
     rawAddSon(typ, newTypeS(tyEmpty, c)) # needs an empty basetype!
   else:
-    var t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
-    addSonSkipIntLit(typ, t)
-  typ.sons[0] = makeRangeType(c, 0, sonsLen(n) - 1, n.info)
+    var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
+    addSonSkipIntLit(typ, t, c.idgen)
+  typ.setIndexType makeRangeType(c, 0, n.len - 1, n.info)
   result = typ
 
-proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
+proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
   result = newNodeI(nkBracket, n.info)
-  result.typ = newTypeS(tyArray, c)
-  rawAddSon(result.typ, nil)     # index type
-  if sonsLen(n) == 0:
-    rawAddSon(result.typ, newTypeS(tyEmpty, c)) # needs an empty basetype!
+  # nkBracket nodes can also be produced by the VM as seq constant nodes
+  # in which case, we cannot produce a new array type for the node,
+  # as this might lose type info even when the node has array type
+  let constructType = n.typ.isNil
+  var expectedElementType, expectedIndexType: PType = nil
+  var expectedBase: PType = nil
+  if constructType:
+    result.typ = newTypeS(tyArray, c)
+    rawAddSon(result.typ, nil)     # index type
+    if expectedType != nil:
+      expectedBase = expectedType.skipTypes(abstractRange-{tyDistinct})
+  else:
+    result.typ = n.typ
+    expectedBase = n.typ.skipTypes(abstractRange) # include tyDistinct this time
+  if expectedBase != nil:
+    case expectedBase.kind
+    of tyArray:
+      expectedIndexType = expectedBase[0]
+      expectedElementType = expectedBase[1]
+    of tyOpenArray, tySequence:
+      # typed bracket expressions can also have seq type
+      expectedElementType = expectedBase[0]
+    else: discard
+  var
+    firstIndex, lastIndex: Int128 = Zero
+    indexType = getSysType(c.graph, n.info, tyInt)
+    lastValidIndex = lastOrd(c.config, indexType)
+  if n.len == 0:
+    if constructType:
+      rawAddSon(result.typ,
+        if expectedElementType != nil and
+            typeAllowed(expectedElementType, skLet, c) == nil:
+          expectedElementType
+        else:
+          newTypeS(tyEmpty, c)) # needs an empty basetype!
+    lastIndex = toInt128(-1)
   else:
-    var x = n.sons[0]
-    var lastIndex: BiggestInt = 0
-    var indexType = getSysType(c.graph, n.info, tyInt)
-    if x.kind == nkExprColonExpr and sonsLen(x) == 2:
-      var idx = semConstExpr(c, x.sons[0])
-      lastIndex = getOrdValue(idx)
-      indexType = idx.typ
-      x = x.sons[1]
-
-    let yy = semExprWithType(c, x)
-    var typ = yy.typ
-    addSon(result, yy)
-    #var typ = skipTypes(result.sons[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal})
-    for i in countup(1, sonsLen(n) - 1):
-      x = n.sons[i]
-      if x.kind == nkExprColonExpr and sonsLen(x) == 2:
-        var idx = semConstExpr(c, x.sons[0])
+    var x = n[0]
+    if x.kind == nkExprColonExpr and x.len == 2:
+      var idx = semConstExpr(c, x[0], expectedIndexType)
+      if not isOrdinalType(idx.typ):
+        localError(c.config, idx.info, "expected ordinal value for array " &
+                   "index, got '$1'" % renderTree(idx))
+      else:
+        firstIndex = getOrdValue(idx)
+        lastIndex = firstIndex
+        indexType = idx.typ
+        lastValidIndex = lastOrd(c.config, indexType)
+        x = x[1]
+
+    let yy = semExprWithType(c, x, {efTypeAllowed}, expectedElementType)
+    var typ: PType
+    if constructType:
+      typ = yy.typ
+      if expectedElementType == nil:
+        expectedElementType = typ
+    else:
+      typ = expectedElementType
+    result.add yy
+    #var typ = skipTypes(result[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal})
+    for i in 1..<n.len:
+      if lastIndex == lastValidIndex:
+        let validIndex = makeRangeType(c, toInt64(firstIndex), toInt64(lastValidIndex), n.info,
+                                       indexType)
+        localError(c.config, n.info, "size of array exceeds range of index " &
+          "type '$1' by $2 elements" % [typeToString(validIndex), $(n.len-i)])
+
+      x = n[i]
+      if x.kind == nkExprColonExpr and x.len == 2:
+        var idx = semConstExpr(c, x[0], indexType)
         idx = fitNode(c, indexType, idx, x.info)
         if lastIndex+1 != getOrdValue(idx):
           localError(c.config, x.info, "invalid order in array constructor")
-        x = x.sons[1]
+        x = x[1]
 
-      let xx = semExprWithType(c, x, flags*{efAllowDestructor})
+      let xx = semExprWithType(c, x, {efTypeAllowed}, expectedElementType)
       result.add xx
-      typ = commonType(typ, xx.typ)
-      #n.sons[i] = semExprWithType(c, x, flags*{efAllowDestructor})
-      #addSon(result, fitNode(c, typ, n.sons[i]))
+      if constructType:
+        typ = commonType(c, typ, xx.typ)
+      #n[i] = semExprWithType(c, x, {})
+      #result.add fitNode(c, typ, n[i])
       inc(lastIndex)
-    addSonSkipIntLit(result.typ, typ)
-    for i in 0 ..< result.len:
-      result.sons[i] = fitNode(c, typ, result.sons[i], result.sons[i].info)
-  result.typ.sons[0] = makeRangeType(c, 0, sonsLen(result) - 1, n.info)
+    if constructType:
+      addSonSkipIntLit(result.typ, typ, c.idgen)
+    for i in 0..<result.len:
+      result[i] = fitNode(c, typ, result[i], result[i].info)
+  if constructType:
+    result.typ.setIndexType(
+      makeRangeType(c,
+        toInt64(firstIndex), toInt64(lastIndex),
+        n.info, indexType))
 
 proc fixAbstractType(c: PContext, n: PNode) =
-  for i in 1 ..< n.len:
-    let it = n.sons[i]
+  for i in 1..<n.len:
+    let it = n[i]
+    if it == nil:
+      localError(c.config, n.info, "'$1' has nil child at index $2" % [renderTree(n, {renderNoComments}), $i])
+      return
     # do not get rid of nkHiddenSubConv for OpenArrays, the codegen needs it:
     if it.kind == nkHiddenSubConv and
         skipTypes(it.typ, abstractVar).kind notin {tyOpenArray, tyVarargs}:
-      if skipTypes(it.sons[1].typ, abstractVar).kind in
+      if skipTypes(it[1].typ, abstractVar).kind in
             {tyNil, tyTuple, tySet} or it[1].isArrayConstr:
-        var s = skipTypes(it.typ, abstractVar)
-        if s.kind != tyExpr:
-          changeType(c, it.sons[1], s, check=true)
-        n.sons[i] = it.sons[1]
-
-proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult =
-  result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr)
+        var s = skipTypes(it.typ, abstractVar + tyUserTypeClasses)
+        if s.kind != tyUntyped:
+          changeType(c, it[1], s, check=true)
+        n[i] = it[1]
+
+proc isAssignable(c: PContext, n: PNode): TAssignableResult =
+  result = parampatterns.isAssignable(c.p.owner, n)
+
+proc isUnresolvedSym(s: PSym): bool =
+  result = s.kind == skGenericParam
+  if not result and s.typ != nil:
+    result = tfInferrableStatic in s.typ.flags or
+        (s.kind == skParam and (s.typ.isMetaType or sfTemplateParam in s.flags)) or
+        (s.kind == skType and
+        s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {})
+
+proc hasUnresolvedArgs(c: PContext, n: PNode): bool =
+  # Checks whether an expression depends on generic parameters that
+  # don't have bound values yet. E.g. this could happen in situations
+  # such as:
+  #  type Slot[T] = array[T.size, byte]
+  #  proc foo[T](x: default(T))
+  #
+  # Both static parameter and type parameters can be unresolved.
+  case n.kind
+  of nkSym:
+    return isUnresolvedSym(n.sym)
+  of nkIdent, nkAccQuoted:
+    let ident = considerQuotedIdent(c, n)
+    var amb = false
+    let sym = searchInScopes(c, ident, amb)
+    if sym != nil:
+      return isUnresolvedSym(sym)
+    else:
+      return false
+  else:
+    for i in 0..<n.safeLen:
+      if hasUnresolvedArgs(c, n[i]): return true
+    return false
 
-proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
-  if n.kind == nkHiddenDeref and not (c.config.cmd == cmdCompileToCpp or
+proc newHiddenAddrTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
+  if n.kind == nkHiddenDeref and not (c.config.backend == backendCpp or
                                       sfCompileToCpp in c.module.flags):
     checkSonsLen(n, 1, c.config)
-    result = n.sons[0]
+    result = n[0]
   else:
     result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ))
-    addSon(result, n)
-    if isAssignable(c, n) notin {arLValue, arLocalLValue}:
-      localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n))
+    result.add n
+    let aa = isAssignable(c, n)
+    let sym = getRoot(n)
+    if aa notin {arLValue, arLocalLValue}:
+      if aa == arDiscriminant and c.inUncheckedAssignSection > 0:
+        discard "allow access within a cast(unsafeAssign) section"
+      elif strictDefs in c.features and aa == arAddressableConst and
+              sym != nil and sym.kind == skLet and isOutParam:
+        discard "allow let varaibles to be passed to out parameters"
+      else:
+        localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n))
 
-proc analyseIfAddressTaken(c: PContext, n: PNode): PNode =
+proc analyseIfAddressTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
   result = n
   case n.kind
   of nkSym:
@@ -502,44 +892,59 @@ proc analyseIfAddressTaken(c: PContext, n: PNode): PNode =
     if n.sym.typ != nil and
         skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
       incl(n.sym.flags, sfAddrTaken)
-      result = newHiddenAddrTaken(c, n)
+      result = newHiddenAddrTaken(c, n, isOutParam)
   of nkDotExpr:
     checkSonsLen(n, 2, c.config)
-    if n.sons[1].kind != nkSym:
+    if n[1].kind != nkSym:
       internalError(c.config, n.info, "analyseIfAddressTaken")
       return
-    if skipTypes(n.sons[1].sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
-      incl(n.sons[1].sym.flags, sfAddrTaken)
-      result = newHiddenAddrTaken(c, n)
+    if skipTypes(n[1].sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
+      incl(n[1].sym.flags, sfAddrTaken)
+      result = newHiddenAddrTaken(c, n, isOutParam)
   of nkBracketExpr:
     checkMinSonsLen(n, 1, c.config)
-    if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
-      if n.sons[0].kind == nkSym: incl(n.sons[0].sym.flags, sfAddrTaken)
-      result = newHiddenAddrTaken(c, n)
+    if skipTypes(n[0].typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
+      if n[0].kind == nkSym: incl(n[0].sym.flags, sfAddrTaken)
+      result = newHiddenAddrTaken(c, n, isOutParam)
   else:
-    result = newHiddenAddrTaken(c, n)
+    result = newHiddenAddrTaken(c, n, isOutParam)
 
-proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
+proc analyseIfAddressTakenInCall(c: PContext, n: PNode, isConverter = false) =
   checkMinSonsLen(n, 1, c.config)
+  if n[0].typ == nil:
+    # n[0] might be erroring node in nimsuggest
+    return
   const
     FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl,
       mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap,
-      mAppendSeqElem, mNewSeq, mReset, mShallowCopy, mDeepCopy}
-
+      mAppendSeqElem, mNewSeq, mShallowCopy, mDeepCopy, mMove,
+      mWasMoved}
+
+  template checkIfConverterCalled(c: PContext, n: PNode) =
+    ## Checks if there is a converter call which wouldn't be checked otherwise
+    # Call can sometimes be wrapped in a deref
+    let node = if n.kind == nkHiddenDeref: n[0] else: n
+    if node.kind == nkHiddenCallConv:
+      analyseIfAddressTakenInCall(c, node, true)
   # get the real type of the callee
   # it may be a proc var with a generic alias type, so we skip over them
-  var t = n.sons[0].typ.skipTypes({tyGenericInst, tyAlias, tySink})
-
-  if n.sons[0].kind == nkSym and n.sons[0].sym.magic in FakeVarParams:
+  var t = n[0].typ.skipTypes({tyGenericInst, tyAlias, tySink})
+  if n[0].kind == nkSym and n[0].sym.magic in FakeVarParams:
     # BUGFIX: check for L-Value still needs to be done for the arguments!
     # note sometimes this is eval'ed twice so we check for nkHiddenAddr here:
-    for i in countup(1, sonsLen(n) - 1):
-      if i < sonsLen(t) and t.sons[i] != nil and
-          skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar:
+    for i in 1..<n.len:
+      if i < t.len and t[i] != nil and
+          skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
         let it = n[i]
-        if isAssignable(c, it) notin {arLValue, arLocalLValue}:
+        let aa = isAssignable(c, it)
+        if aa notin {arLValue, arLocalLValue}:
           if it.kind != nkHiddenAddr:
-            localError(c.config, it.info, errVarForOutParamNeededX % $it)
+            if aa == arDiscriminant and c.inUncheckedAssignSection > 0:
+              discard "allow access within a cast(unsafeAssign) section"
+            else:
+              localError(c.config, it.info, errVarForOutParamNeededX % $it)
+        # Make sure to still check arguments for converters
+        c.checkIfConverterCalled(n[i])
     # bug #5113: disallow newSeq(result) where result is a 'var T':
     if n[0].sym.magic in {mNew, mNewFinalize, mNewSeq}:
       var arg = n[1] #.skipAddr
@@ -549,22 +954,22 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
         localError(c.config, n.info, errXStackEscape % renderTree(n[1], {renderNoComments}))
 
     return
-  for i in countup(1, sonsLen(n) - 1):
-    if n.sons[i].kind == nkHiddenCallConv:
-      # we need to recurse explicitly here as converters can create nested
-      # calls and then they wouldn't be analysed otherwise
-      analyseIfAddressTakenInCall(c, n.sons[i])
-    if i < sonsLen(t) and
-        skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar:
-      if n.sons[i].kind != nkHiddenAddr:
-        n.sons[i] = analyseIfAddressTaken(c, n.sons[i])
+  for i in 1..<n.len:
+    let n = if n.kind == nkHiddenDeref: n[0] else: n
+    c.checkIfConverterCalled(n[i])
+    if i < t.len and
+        skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
+      # Converters wrap var parameters in nkHiddenAddr but they haven't been analysed yet.
+      # So we need to make sure we are checking them still when in a converter call
+      if n[i].kind != nkHiddenAddr or isConverter:
+        n[i] = analyseIfAddressTaken(c, n[i].skipAddr(), isOutParam(skipTypes(t[i], abstractInst-{tyTypeDesc})))
 
 include semmagic
 
 proc evalAtCompileTime(c: PContext, n: PNode): PNode =
   result = n
-  if n.kind notin nkCallKinds or n.sons[0].kind != nkSym: return
-  var callee = n.sons[0].sym
+  if n.kind notin nkCallKinds or n[0].kind != nkSym: return
+  var callee = n[0].sym
   # workaround for bug #537 (overly aggressive inlining leading to
   # wrong NimNode semantics):
   if n.typ != nil and tfTriggersCompileTime in n.typ.flags: return
@@ -572,17 +977,17 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
   # constant folding that is necessary for correctness of semantic pass:
   if callee.magic != mNone and callee.magic in ctfeWhitelist and n.typ != nil:
     var call = newNodeIT(nkCall, n.info, n.typ)
-    call.add(n.sons[0])
+    call.add(n[0])
     var allConst = true
-    for i in 1 ..< n.len:
-      var a = getConstExpr(c.module, n.sons[i], c.graph)
+    for i in 1..<n.len:
+      var a = getConstExpr(c.module, n[i], c.idgen, c.graph)
       if a == nil:
         allConst = false
-        a = n.sons[i]
-        if a.kind == nkHiddenStdConv: a = a.sons[1]
+        a = n[i]
+        if a.kind == nkHiddenStdConv: a = a[1]
       call.add(a)
     if allConst:
-      result = semfold.getConstExpr(c.module, call, c.graph)
+      result = semfold.getConstExpr(c.module, call, c.idgen, c.graph)
       if result.isNil: result = n
       else: return result
 
@@ -592,49 +997,62 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
     # done until we have a more robust infrastructure for
     # implicit statics.
     if n.len > 1:
-      for i in 1 ..< n.len:
+      for i in 1..<n.len:
         # see bug #2113, it's possible that n[i].typ for errornous code:
         if n[i].typ.isNil or n[i].typ.kind != tyStatic or
             tfUnresolved notin n[i].typ.flags:
           break maybeLabelAsStatic
-      n.typ = newTypeWithSons(c, tyStatic, @[n.typ])
+      n.typ = newTypeS(tyStatic, c, n.typ)
       n.typ.flags.incl tfUnresolved
 
   # optimization pass: not necessary for correctness of the semantic pass
-  if {sfNoSideEffect, sfCompileTime} * callee.flags != {} and
+  if callee.kind == skConst or
+     {sfNoSideEffect, sfCompileTime} * callee.flags != {} and
      {sfForward, sfImportc} * callee.flags == {} and n.typ != nil:
-    if sfCompileTime notin callee.flags and
-        optImplicitStatic notin c.config.options: return
+
+    if callee.kind != skConst and
+       sfCompileTime notin callee.flags and
+       optImplicitStatic notin c.config.options: return
 
     if callee.magic notin ctfeWhitelist: return
-    if callee.kind notin {skProc, skFunc, skConverter} or callee.isGenericRoutine:
+
+    if callee.kind notin {skProc, skFunc, skConverter, skConst} or
+        callee.isGenericRoutineStrict:
       return
 
-    if n.typ != nil and typeAllowed(n.typ, skConst) != nil: return
+    if n.typ != nil and typeAllowed(n.typ, skConst, c) != nil: return
 
     var call = newNodeIT(nkCall, n.info, n.typ)
-    call.add(n.sons[0])
-    for i in 1 ..< n.len:
-      let a = getConstExpr(c.module, n.sons[i], c.graph)
+    call.add(n[0])
+    for i in 1..<n.len:
+      let a = getConstExpr(c.module, n[i], c.idgen, 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.idgen, 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.idgen, 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
 
-proc semStaticExpr(c: PContext, n: PNode): PNode =
-  let a = semExpr(c, n.sons[0])
+proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
+  inc c.inStaticContext
+  openScope(c)
+  let a = semExprWithType(c, n, expectedType = expectedType)
+  closeScope(c)
+  dec c.inStaticContext
   if a.findUnresolvedStatic != nil: return a
-  result = evalStaticExpr(c.module, c.graph, a, c.p.owner)
+  result = evalStaticExpr(c.module, c.idgen, c.graph, a, c.p.owner)
   if result.isNil:
     localError(c.config, n.info, errCannotInterpretNodeX % renderTree(n))
     result = c.graph.emptyNode
@@ -642,105 +1060,146 @@ proc semStaticExpr(c: PContext, n: PNode): PNode =
     result = fixupTypeAfterEval(c, result, a)
 
 proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
-                                     flags: TExprFlags): PNode =
-  if flags*{efInTypeof, efWantIterator} != {}:
+                                     flags: TExprFlags; expectedType: PType = nil): PNode =
+  if flags*{efInTypeof, efWantIterator, efWantIterable} != {}:
     # consider: 'for x in pReturningArray()' --> we don't want the restriction
     # to 'skIterator' anymore; skIterator is preferred in sigmatch already
     # for typeof support.
-    # for ``type(countup(1,3))``, see ``tests/ttoseq``.
+    # for ``typeof(countup(1,3))``, see ``tests/ttoseq``.
     result = semOverloadedCall(c, n, nOrig,
-      {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags)
+      {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags, expectedType)
   else:
     result = semOverloadedCall(c, n, nOrig,
-      {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags)
+      {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags, expectedType)
 
   if result != nil:
-    if result.sons[0].kind != nkSym:
-      internalError(c.config, "semOverloadedCallAnalyseEffects")
+    if result[0].kind != nkSym:
+      if not (c.inGenericContext > 0): # see generic context check in semOverloadedCall
+        internalError(c.config, "semOverloadedCallAnalyseEffects")
       return
-    let callee = result.sons[0].sym
+    let callee = result[0].sym
     case callee.kind
     of skMacro, skTemplate: discard
     else:
-      if callee.kind == skIterator and callee.id == c.p.owner.id:
-        localError(c.config, n.info, errRecursiveDependencyX % callee.name.s)
+      if callee.kind == skIterator and callee.id == c.p.owner.id and
+          not isClosureIterator(c.p.owner.typ):
+        localError(c.config, n.info, errRecursiveDependencyIteratorX % callee.name.s)
         # error correction, prevents endless for loop elimination in transf.
         # See bug #2051:
-        result.sons[0] = newSymNode(errorSym(c, n))
-
-proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode
+        result[0] = newSymNode(errorSym(c, n))
+      elif callee.kind == skIterator:
+        if efWantIterable in flags:
+          let typ = newTypeS(tyIterable, c)
+          rawAddSon(typ, result.typ)
+          result.typ = typ
 
 proc resolveIndirectCall(c: PContext; n, nOrig: PNode;
                          t: PType): TCandidate =
-  initCandidate(c, result, t)
+  result = initCandidate(c, t)
   matches(c, n, nOrig, result)
-  if result.state != csMatch:
-    # try to deref the first argument:
-    if implicitDeref in c.features and canDeref(n):
-      n.sons[1] = n.sons[1].tryDeref
-      initCandidate(c, result, t)
-      matches(c, n, nOrig, result)
-
-proc bracketedMacro(n: PNode): PSym =
-  if n.len >= 1 and n[0].kind == nkSym:
-    result = n[0].sym
-    if result.kind notin {skMacro, skTemplate}:
-      result = nil
 
-proc setGenericParams(c: PContext, n: PNode) =
-  for i in 1 ..< n.len:
-    n[i].typ = semTypeNode(c, n[i], nil)
+proc finishOperand(c: PContext, a: PNode): PNode =
+  if a.typ.isNil:
+    result = c.semOperand(c, a, {efDetermineType})
+  else:
+    result = a
+  # XXX tyGenericInst here?
+  if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}):
+    #and tfUnresolved in result.typ.flags:
+    let owner = result.typ.owner
+    let err =
+      # consistent error message with evaltempl/semMacroExpr
+      if owner != nil and owner.kind in {skTemplate, skMacro}:
+        errMissingGenericParamsForTemplate % a.renderTree
+      else:
+        errProcHasNoConcreteType % a.renderTree
+    localError(c.config, a.info, err)
+  considerGenSyms(c, result)
+
+proc semFinishOperands(c: PContext; n: PNode; isBracketExpr = false) =
+  # this needs to be called to ensure that after overloading resolution every
+  # argument has been sem'checked
+
+  # skip the first argument for operands of `[]` since it may be an unresolved
+  # generic proc, which is handled in semMagic
+  let start = 1 + ord(isBracketExpr)
+  for i in start..<n.len:
+    n[i] = finishOperand(c, n[i])
+
+proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
+  if efNoSemCheck notin flags and n.typ != nil and n.typ.kind == tyError:
+    return errorNode(c, n)
+  if n.typ != nil and n.typ.kind == tyFromExpr and c.inGenericContext > 0:
+    return n
 
-proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
   result = n
-  let callee = result.sons[0].sym
+
+  when defined(nimsuggest):
+    if c.config.expandProgress:
+      if c.config.expandLevels == 0:
+        return n
+      else:
+        c.config.expandLevels -= 1
+
+  let callee = result[0].sym
   case callee.kind
-  of skMacro: result = semMacroExpr(c, result, orig, callee, flags)
-  of skTemplate: result = semTemplateExpr(c, result, callee, flags)
+  of skMacro: result = semMacroExpr(c, result, orig, callee, flags, expectedType)
+  of skTemplate: result = semTemplateExpr(c, result, callee, flags, expectedType)
   else:
-    semFinishOperands(c, result)
+    semFinishOperands(c, result, isBracketExpr = callee.magic in {mArrGet, mArrPut})
     activate(c, result)
     fixAbstractType(c, result)
     analyseIfAddressTakenInCall(c, result)
     if callee.magic != mNone:
-      result = magicsAfterOverloadResolution(c, result, flags)
-    if result.typ != nil: liftTypeBoundOps(c, result.typ, n.info)
+      result = magicsAfterOverloadResolution(c, result, flags, expectedType)
+    when false:
+      if result.typ != nil and
+          not (result.typ.kind == tySequence and result.elementType.kind == tyEmpty):
+        liftTypeBoundOps(c, result.typ, n.info)
     #result = patchResolvedTypeBoundOp(c, result)
-  if c.matchedConcept == nil:
+  if c.matchedConcept == nil and (c.inTypeofContext == 0 or callee.magic != mNone):
+    # don't fold calls in concepts and typeof
     result = evalAtCompileTime(c, result)
 
-proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
+proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
   result = nil
   checkMinSonsLen(n, 1, c.config)
-  var prc = n.sons[0]
-  if n.sons[0].kind == nkDotExpr:
-    checkSonsLen(n.sons[0], 2, c.config)
-    let n0 = semFieldAccess(c, n.sons[0])
+  var prc = n[0]
+  if n[0].kind == nkDotExpr:
+    checkSonsLen(n[0], 2, c.config)
+    let n0 = semFieldAccess(c, n[0], {efIsDotCall})
     if n0.kind == nkDotCall:
       # it is a static call!
       result = n0
-      result.kind = nkCall
+      result.transitionSonsKind(nkCall)
       result.flags.incl nfExplicitCall
-      for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i])
-      return semExpr(c, result, flags)
+      for i in 1..<n.len: result.add n[i]
+      return semExpr(c, result, flags, expectedType)
+    elif n0.typ.kind == tyFromExpr and c.inGenericContext > 0:
+      # don't make assumptions, entire expression needs to be tyFromExpr
+      result = semGenericStmt(c, n)
+      result.typ = makeTypeFromExpr(c, result.copyTree)
+      return
     else:
-      n.sons[0] = n0
+      n[0] = n0
   else:
-    n.sons[0] = semExpr(c, n.sons[0], {efInCall})
-    let t = n.sons[0].typ
+    n[0] = semExpr(c, n[0], {efInCall, efAllowSymChoice})
+    let t = n[0].typ
     if t != nil and t.kind in {tyVar, tyLent}:
-      n.sons[0] = newDeref(n.sons[0])
-    elif n.sons[0].kind == nkBracketExpr:
-      let s = bracketedMacro(n.sons[0])
-      if s != nil:
-        setGenericParams(c, n[0])
-        return semDirectOp(c, n, flags)
+      n[0] = newDeref(n[0])
+    elif isSymChoice(n[0]) and nfDotField notin n.flags:
+      # overloaded generic procs e.g. newSeq[int] can end up here
+      return semDirectOp(c, n, flags, expectedType)
+
+  var t: PType = nil
+  if n[0].typ != nil:
+    t = skipTypes(n[0].typ, abstractInst+{tyOwned}-{tyTypeDesc, tyDistinct})
+  if t != nil and t.kind == tyTypeDesc:
+    if n.len == 1: return semObjConstr(c, n, flags, expectedType)
+    return semConv(c, n, flags)
 
   let nOrig = n.copyTree
   semOpAux(c, n)
-  var t: PType = nil
-  if n.sons[0].typ != nil:
-    t = skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc})
   if t != nil and t.kind == tyProc:
     # This is a proc variable, apply normal overload resolution
     let m = resolveIndirectCall(c, n, nOrig, t)
@@ -752,34 +1211,41 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
       else:
         var hasErrorType = false
         var msg = "type mismatch: got <"
-        for i in countup(1, sonsLen(n) - 1):
-          if i > 1: add(msg, ", ")
-          let nt = n.sons[i].typ
-          add(msg, typeToString(nt))
+        for i in 1..<n.len:
+          if i > 1: msg.add(", ")
+          let nt = n[i].typ
+          msg.add(typeToString(nt))
           if nt.kind == tyError:
             hasErrorType = true
             break
         if not hasErrorType:
-          add(msg, ">\nbut expected one of: \n" &
-              typeToString(n.sons[0].typ))
+          let typ = n[0].typ
+          msg.add(">\nbut expected one of:\n" &
+              typeToString(typ))
+          # prefer notin preferToResolveSymbols
+          # t.sym != nil
+          # sfAnon notin t.sym.flags
+          # t.kind != tySequence(It is tyProc)
+          if typ.sym != nil and sfAnon notin typ.sym.flags and
+                                typ.kind == tyProc:
+            # when can `typ.sym != nil` ever happen?
+            msg.add(" = " & typeToString(typ, preferDesc))
+          msg.addDeclaredLocMaybe(c.config, typ)
           localError(c.config, n.info, msg)
         return errorNode(c, n)
-      result = nil
     else:
       result = m.call
       instGenericConvertersSons(c, result, m)
-  elif t != nil and t.kind == tyTypeDesc:
-    if n.len == 1: return semObjConstr(c, n, flags)
-    return semConv(c, n)
+
   else:
-    result = overloadedCallOpr(c, n)
+    result = overloadedCallOpr(c, n) # this uses efNoUndeclared
     # Now that nkSym does not imply an iteration over the proc/iterator space,
     # the old ``prc`` (which is likely an nkIdent) has to be restored:
-    if result == nil:
+    if result == nil or result.kind == nkEmpty:
       # XXX: hmm, what kind of symbols will end up here?
       # do we really need to try the overload resolution?
-      n.sons[0] = prc
-      nOrig.sons[0] = prc
+      n[0] = prc
+      nOrig[0] = prc
       n.flags.incl nfExprCall
       result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags)
       if result == nil: return errorNode(c, n)
@@ -788,48 +1254,38 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
       # See bug #904 of how to trigger it:
       return result
   #result = afterCallActions(c, result, nOrig, flags)
-  if result.sons[0].kind == nkSym:
-    result = afterCallActions(c, result, nOrig, flags)
+  if result[0].kind == nkSym:
+    result = afterCallActions(c, result, nOrig, flags, expectedType)
   else:
     fixAbstractType(c, result)
     analyseIfAddressTakenInCall(c, result)
 
-proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
+proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
   # this seems to be a hotspot in the compiler!
   let nOrig = n.copyTree
   #semLazyOpAux(c, n)
-  result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags)
-  if result != nil: result = afterCallActions(c, result, nOrig, flags)
+  result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags, expectedType)
+  if result != nil: result = afterCallActions(c, result, nOrig, flags, expectedType)
   else: result = errorNode(c, n)
 
 proc buildEchoStmt(c: PContext, n: PNode): PNode =
   # we MUST not check 'n' for semantics again here! But for now we give up:
   result = newNodeI(nkCall, n.info)
-  var e = strTableGet(c.graph.systemModule.tab, getIdent(c.cache, "echo"))
+  let e = systemModuleSym(c.graph, getIdent(c.cache, "echo"))
   if e != nil:
-    add(result, newSymNode(e))
+    result.add(newSymNode(e))
   else:
-    localError(c.config, n.info, "system needs: echo")
-    add(result, errorNode(c, n))
-  add(result, n)
+    result.add localErrorNode(c, n, "system needs: echo")
+  result.add(n)
+  result.add(newStrNode(nkStrLit, ": " & n.typ.typeToString))
   result = semExpr(c, result)
 
 proc semExprNoType(c: PContext, n: PNode): PNode =
+  let isPush = c.config.hasHint(hintExtendedContext)
+  if isPush: pushInfoContext(c.config, n.info)
   result = semExpr(c, n, {efWantStmt})
-  # make an 'if' expression an 'if' statement again for backwards
-  # compatibility (.discardable was a bad idea!); bug #6980
-  var isStmt = false
-  if result.kind == nkIfExpr:
-    isStmt = true
-    for condActionPair in result:
-      let action = condActionPair.lastSon
-      if not implicitlyDiscardable(action) and not
-          endsInNoReturn(action):
-        isStmt = false
-    if isStmt:
-      result.kind = nkIfStmt
-      result.typ = nil
-  discardCheck(c, result)
+  discardCheck(c, result, {})
+  if isPush: popInfoContext(c.config)
 
 proc isTypeExpr(n: PNode): bool =
   case n.kind
@@ -849,50 +1305,50 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
   result = nil
   case r.kind
   of nkRecList:
-    for i in countup(0, sonsLen(r) - 1):
-      result = lookupInRecordAndBuildCheck(c, n, r.sons[i], field, check)
+    for i in 0..<r.len:
+      result = lookupInRecordAndBuildCheck(c, n, r[i], field, check)
       if result != nil: return
   of nkRecCase:
     checkMinSonsLen(r, 2, c.config)
-    if (r.sons[0].kind != nkSym): illFormedAst(r, c.config)
-    result = lookupInRecordAndBuildCheck(c, n, r.sons[0], field, check)
+    if (r[0].kind != nkSym): illFormedAst(r, c.config)
+    result = lookupInRecordAndBuildCheck(c, n, r[0], field, check)
     if result != nil: return
-    let setType = createSetType(c, r.sons[0].typ)
+    let setType = createSetType(c, r[0].typ)
     var s = newNodeIT(nkCurly, r.info, setType)
-    for i in countup(1, sonsLen(r) - 1):
-      var it = r.sons[i]
+    for i in 1..<r.len:
+      var it = r[i]
       case it.kind
       of nkOfBranch:
         result = lookupInRecordAndBuildCheck(c, n, lastSon(it), field, check)
         if result == nil:
-          for j in 0..sonsLen(it)-2: addSon(s, copyTree(it.sons[j]))
+          for j in 0..<it.len-1: s.add copyTree(it[j])
         else:
           if check == nil:
             check = newNodeI(nkCheckedFieldExpr, n.info)
-            addSon(check, c.graph.emptyNode) # make space for access node
+            check.add c.graph.emptyNode # make space for access node
           s = newNodeIT(nkCurly, n.info, setType)
-          for j in countup(0, sonsLen(it) - 2): addSon(s, copyTree(it.sons[j]))
+          for j in 0..<it.len - 1: s.add copyTree(it[j])
           var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool))
-          addSon(inExpr, newSymNode(c.graph.opContains, n.info))
-          addSon(inExpr, s)
-          addSon(inExpr, copyTree(r.sons[0]))
-          addSon(check, inExpr)
-          #addSon(check, semExpr(c, inExpr))
+          inExpr.add newSymNode(getSysMagic(c.graph, n.info, "contains", mInSet), n.info)
+          inExpr.add s
+          inExpr.add copyTree(r[0])
+          check.add inExpr
+          #check.add semExpr(c, inExpr)
           return
       of nkElse:
         result = lookupInRecordAndBuildCheck(c, n, lastSon(it), field, check)
         if result != nil:
           if check == nil:
             check = newNodeI(nkCheckedFieldExpr, n.info)
-            addSon(check, c.graph.emptyNode) # make space for access node
+            check.add c.graph.emptyNode # make space for access node
           var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool))
-          addSon(inExpr, newSymNode(c.graph.opContains, n.info))
-          addSon(inExpr, s)
-          addSon(inExpr, copyTree(r.sons[0]))
+          inExpr.add newSymNode(getSysMagic(c.graph, n.info, "contains", mInSet), n.info)
+          inExpr.add s
+          inExpr.add copyTree(r[0])
           var notExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool))
-          addSon(notExpr, newSymNode(c.graph.opNot, n.info))
-          addSon(notExpr, inExpr)
-          addSon(check, notExpr)
+          notExpr.add newSymNode(getSysMagic(c.graph, n.info, "not", mNot), n.info)
+          notExpr.add inExpr
+          check.add notExpr
           return
       else: illFormedAst(it, c.config)
   of nkSym:
@@ -901,7 +1357,7 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
 
 const
   tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass}
-  tyDotOpTransparent = {tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink}
+  tyDotOpTransparent = {tyVar, tyLent, tyPtr, tyRef, tyOwned, tyAlias, tySink}
 
 proc readTypeParameter(c: PContext, typ: PType,
                        paramName: PIdent, info: TLineInfo): PNode =
@@ -919,7 +1375,7 @@ proc readTypeParameter(c: PContext, typ: PType,
             # This seems semantically correct and then we'll be able
             # to return the section symbol directly here
             let foundType = makeTypeDesc(c, def[2].typ)
-            return newSymNode(copySym(def[0].sym).linkTo(foundType), info)
+            return newSymNode(copySym(def[0].sym, c.idgen).linkTo(foundType), info)
 
       of nkConstSection:
         for def in statement:
@@ -930,13 +1386,13 @@ proc readTypeParameter(c: PContext, typ: PType,
         discard
 
   if typ.kind != tyUserTypeClass:
-    let ty = if typ.kind == tyCompositeTypeClass: typ.sons[1].skipGenericAlias
+    let ty = if typ.kind == tyCompositeTypeClass: typ.firstGenericParam.skipGenericAlias
              else: typ.skipGenericAlias
-    let tbody = ty.sons[0]
-    for s in countup(0, tbody.len-2):
-      let tParam = tbody.sons[s]
+    let tbody = ty[0]
+    for s in 0..<tbody.len-1:
+      let tParam = tbody[s]
       if tParam.sym.name.id == paramName.id:
-        let rawTyp = ty.sons[s + 1]
+        let rawTyp = ty[s + 1]
         if rawTyp.kind == tyStatic:
           if rawTyp.n != nil:
             return rawTyp.n
@@ -944,18 +1400,22 @@ proc readTypeParameter(c: PContext, typ: PType,
             return c.graph.emptyNode
         else:
           let foundTyp = makeTypeDesc(c, rawTyp)
-          return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info)
+          return newSymNode(copySym(tParam.sym, c.idgen).linkTo(foundTyp), info)
 
   return nil
 
 proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
+  result = nil
+  assert n.kind in nkIdentKinds + {nkDotExpr}
   let s = getGenSym(c, sym)
   case s.kind
   of skConst:
-    markUsed(c.config, n.info, s, c.graph.usageSym)
-    styleCheckUse(n.info, s)
-    case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind
-    of  tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
+    if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess
+      markUsed(c, n.info, s)
+    onUse(n.info, s)
+    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)
       else: result = newSymNode(s, n.info)
@@ -970,59 +1430,55 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
       # It is clear that ``[]`` means two totally different things. Thus, we
       # copy `x`'s AST into each context, so that the type fixup phase can
       # deal with two different ``[]``.
-      if s.ast.len == 0: result = inlineConst(c, n, s)
+      if s.astdef.safeLen == 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:
-    if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or
-       (n.kind notin nkCallKinds and s.requiredParams > 0):
-      markUsed(c.config, n.info, s, c.graph.usageSym)
-      styleCheckUse(n.info, s)
-      result = symChoice(c, n, s, scClosed)
-    else:
-      result = semMacroExpr(c, n, n, s, flags)
-  of skTemplate:
-    if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or
-       (n.kind notin nkCallKinds and s.requiredParams > 0) or
-       sfCustomPragma in sym.flags:
-      markUsed(c.config, n.info, s, c.graph.usageSym)
-      styleCheckUse(n.info, s)
+  of skMacro, skTemplate:
+    # check if we cannot use alias syntax (no required args or generic params)
+    if sfNoalias in s.flags:
+      let info = getCallLineInfo(n)
+      markUsed(c, info, s)
+      onUse(info, s)
       result = symChoice(c, n, s, scClosed)
     else:
-      result = semTemplateExpr(c, n, s, flags)
+      case s.kind
+      of skMacro: result = semMacroExpr(c, n, n, s, flags)
+      of skTemplate: result = semTemplateExpr(c, n, s, flags)
+      else: discard # unreachable
   of skParam:
-    markUsed(c.config, n.info, s, c.graph.usageSym)
-    styleCheckUse(n.info, s)
+    markUsed(c, n.info, s)
+    onUse(n.info, s)
     if s.typ != nil and s.typ.kind == tyStatic and s.typ.n != nil:
       # XXX see the hack in sigmatch.nim ...
       return s.typ.n
     elif sfGenSym in s.flags:
-      if c.p.wasForwarded:
-        # gensym'ed parameters that nevertheless have been forward declared
-        # need a special fixup:
-        let realParam = c.p.owner.typ.n[s.position+1]
-        internalAssert c.config, realParam.kind == nkSym and realParam.sym.kind == skParam
-        return newSymNode(c.p.owner.typ.n[s.position+1].sym, n.info)
-      elif c.p.owner.kind == skMacro:
-        # gensym'ed macro parameters need a similar hack (see bug #1944):
-        var u = searchInScopes(c, s.name)
-        internalAssert c.config, u != nil and u.kind == skParam and u.owner == s.owner
-        return newSymNode(u, n.info)
+      # the owner should have been set by now by addParamOrResult
+      internalAssert c.config, s.owner != nil
     result = newSymNode(s, n.info)
   of skVar, skLet, skResult, skForVar:
     if s.magic == mNimvm:
       localError(c.config, n.info, "illegal context for 'nimvm' magic")
 
-    markUsed(c.config, n.info, s, c.graph.usageSym)
-    styleCheckUse(n.info, s)
+    if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess
+      markUsed(c, n.info, s)
+    onUse(n.info, s)
     result = newSymNode(s, n.info)
     # We cannot check for access to outer vars for example because it's still
     # not sure the symbol really ends up being used:
     # var len = 0 # but won't be called
     # genericThatUsesLen(x) # marked as taking a closure?
+    if hasWarn(c.config, warnResultUsed):
+      message(c.config, n.info, warnResultUsed)
+
   of skGenericParam:
-    styleCheckUse(n.info, s)
+    onUse(n.info, s)
     if s.typ.kind == tyStatic:
       result = newSymNode(s, n.info)
       result.typ = s.typ
@@ -1032,56 +1488,101 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
       n.typ = s.typ
       return n
   of skType:
-    markUsed(c.config, n.info, s, c.graph.usageSym)
-    styleCheckUse(n.info, s)
-    if s.typ.kind == tyStatic and s.typ.n != nil:
+    if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess
+      markUsed(c, n.info, s)
+    onUse(n.info, s)
+    if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil:
       return s.typ.n
     result = newSymNode(s, n.info)
     result.typ = makeTypeDesc(c, s.typ)
   of skField:
-    var p = c.p
-    while p != nil and p.selfSym == nil:
-      p = p.next
-    if p != nil and p.selfSym != nil:
-      var ty = skipTypes(p.selfSym.typ, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef,
-                                         tyAlias, tySink})
-      while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct})
-      var check: PNode = nil
-      if ty.kind == tyObject:
-        while true:
-          check = nil
-          let f = lookupInRecordAndBuildCheck(c, n, ty.n, s.name, check)
-          if f != nil and fieldVisible(c, f):
-            # is the access to a public field or in the same module or in a friend?
-            doAssert f == s
-            markUsed(c.config, n.info, f, c.graph.usageSym)
-            styleCheckUse(n.info, f)
-            result = newNodeIT(nkDotExpr, n.info, f.typ)
-            result.add makeDeref(newSymNode(p.selfSym))
-            result.add newSymNode(f) # we now have the correct field
-            if check != nil:
-              check.sons[0] = result
-              check.typ = result.typ
-              result = check
-            return result
-          if ty.sons[0] == nil: break
-          ty = skipTypes(ty.sons[0], skipPtrs)
     # old code, not sure if it's live code:
-    markUsed(c.config, n.info, s, c.graph.usageSym)
-    styleCheckUse(n.info, s)
+    markUsed(c, n.info, s)
+    onUse(n.info, s)
     result = newSymNode(s, n.info)
-  else:
-    markUsed(c.config, n.info, s, c.graph.usageSym)
-    styleCheckUse(n.info, s)
+  of skModule:
+    # make sure type is None and not nil for discard checking
+    if efWantStmt in flags: s.typ = newTypeS(tyNone, c)
+    markUsed(c, n.info, s)
+    onUse(n.info, s)
     result = newSymNode(s, n.info)
+  else:
+    let info = getCallLineInfo(n)
+    #if efInCall notin flags:
+    markUsed(c, info, s)
+    onUse(info, s)
+    result = newSymNode(s, info)
 
-proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
+proc tryReadingGenericParam(c: PContext, n: PNode, i: PIdent, t: PType): PNode =
+  case t.kind
+  of tyGenericInst:
+    result = readTypeParameter(c, t, i, n.info)
+    if result == c.graph.emptyNode:
+      if c.inGenericContext > 0:
+        result = semGenericStmt(c, n)
+        result.typ = makeTypeFromExpr(c, result.copyTree)
+      else:
+        result = nil
+  of tyUserTypeClasses:
+    if t.isResolvedUserTypeClass:
+      result = readTypeParameter(c, t, i, n.info)
+    elif c.inGenericContext > 0:
+      result = semGenericStmt(c, n)
+      result.typ = makeTypeFromExpr(c, copyTree(result))
+    else:
+      result = nil
+  of tyGenericBody, tyCompositeTypeClass:
+    if c.inGenericContext > 0:
+      result = readTypeParameter(c, t, i, n.info)
+      if result != nil:
+        # generic parameter exists, stop here but delay until instantiation
+        result = semGenericStmt(c, n)
+        result.typ = makeTypeFromExpr(c, copyTree(result))
+    else:
+      result = nil
+  elif c.inGenericContext > 0 and t.containsUnresolvedType:
+    result = semGenericStmt(c, n)
+    result.typ = makeTypeFromExpr(c, copyTree(result))
+  else:
+    result = nil
+
+proc tryReadingTypeField(c: PContext, n: PNode, i: PIdent, ty: PType): PNode =
+  result = nil
+  var ty = ty.skipTypes(tyDotOpTransparent)
+  case ty.kind
+  of tyEnum:
+    # look up if the identifier belongs to the enum:
+    var f = PSym(nil)
+    while ty != nil:
+      f = getSymFromList(ty.n, i)
+      if f != nil: break
+      ty = ty[0]         # enum inheritance
+    if f != nil:
+      result = newSymNode(f)
+      result.info = n.info
+      result.typ = ty
+      markUsed(c, n.info, f)
+      onUse(n.info, f)
+  of tyObject, tyTuple:
+    if ty.n != nil and ty.n.kind == nkRecList:
+      let field = lookupInRecord(ty.n, i)
+      if field != nil:
+        n.typ = makeTypeDesc(c, field.typ)
+        result = n
+  of tyGenericInst:
+    result = tryReadingTypeField(c, n, i, ty.skipModifier)
+    if result == nil:
+      result = tryReadingGenericParam(c, n, i, ty)
+  else:
+    result = tryReadingGenericParam(c, n, i, ty)
+
+proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode =
   ## returns nil if it's not a built-in field access
   checkSonsLen(n, 2, c.config)
   # tests/bind/tbindoverload.nim wants an early exit here, but seems to
   # work without now. template/tsymchoicefield doesn't like an early exit
   # here at all!
-  #if isSymChoice(n.sons[1]): return
+  #if isSymChoice(n[1]): return
   when defined(nimsuggest):
     if c.config.cmd == cmdIdeTools:
       suggestExpr(c, n)
@@ -1093,40 +1594,19 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
       result = symChoice(c, n, s, scClosed)
       if result.kind == nkSym: result = semSym(c, n, s, flags)
     else:
-      markUsed(c.config, n.sons[1].info, s, c.graph.usageSym)
+      markUsed(c, n[1].info, s)
       result = semSym(c, n, s, flags)
-    styleCheckUse(n.sons[1].info, s)
+    onUse(n[1].info, s)
     return
 
-  n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType})
-  #restoreOldStyleType(n.sons[0])
-  var i = considerQuotedIdent(c, n.sons[1], n)
-  var ty = n.sons[0].typ
+  # extra flags since LHS may become a call operand:
+  n[0] = semExprWithType(c, n[0], flags+{efDetermineType, efWantIterable, efAllowSymChoice})
+  #restoreOldStyleType(n[0])
+  var i = considerQuotedIdent(c, n[1], n)
+  var ty = n[0].typ
   var f: PSym = nil
   result = nil
 
-  template tryReadingGenericParam(t: PType) =
-    case t.kind
-    of tyTypeParamsHolders:
-      result = readTypeParameter(c, t, i, n.info)
-      if result == c.graph.emptyNode:
-        result = n
-        n.typ = makeTypeFromExpr(c, n.copyTree)
-      return
-    of tyUserTypeClasses:
-      if t.isResolvedUserTypeClass:
-        return readTypeParameter(c, t, i, n.info)
-      else:
-        n.typ = makeTypeFromExpr(c, copyTree(n))
-        return n
-    of tyGenericParam, tyAnything:
-      n.typ = makeTypeFromExpr(c, copyTree(n))
-      return n
-    else:
-      discard
-
-  var argIsType = false
-
   if ty.kind == tyTypeDesc:
     if ty.base.kind == tyNone:
       # This is a still unresolved typedesc parameter.
@@ -1135,149 +1615,183 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
       # field access and we leave the compiler to compile a normal call:
       if getCurrOwner(c).kind != skMacro:
         n.typ = makeTypeFromExpr(c, n.copyTree)
+        flags.incl efCannotBeDotCall
         return n
       else:
         return nil
     else:
-      ty = ty.base
-      argIsType = true
-  else:
-    argIsType = isTypeExpr(n.sons[0])
-
-  if argIsType:
-    ty = ty.skipTypes(tyDotOpTransparent)
-    case ty.kind
-    of tyEnum:
-      # look up if the identifier belongs to the enum:
-      while ty != nil:
-        f = getSymFromList(ty.n, i)
-        if f != nil: break
-        ty = ty.sons[0]         # enum inheritance
-      if f != nil:
-        result = newSymNode(f)
-        result.info = n.info
-        result.typ = ty
-        markUsed(c.config, n.info, f, c.graph.usageSym)
-        styleCheckUse(n.info, f)
-        return
-    of tyObject, tyTuple:
-      if ty.n != nil and ty.n.kind == nkRecList:
-        let field = lookupInRecord(ty.n, i)
-        if field != nil:
-          n.typ = makeTypeDesc(c, field.typ)
-          return n
-    else:
-      tryReadingGenericParam(ty)
-      return
-    # XXX: This is probably not relevant any more
-    # reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim":
-    ty = n.sons[0].typ
+      flags.incl efCannotBeDotCall
+      return tryReadingTypeField(c, n, i, ty.base)
+  elif isTypeExpr(n.sons[0]):
+    flags.incl efCannotBeDotCall
+    return tryReadingTypeField(c, n, i, ty)
+  elif ty.kind == tyError:
+    # a type error doesn't have any builtin fields
     return nil
+
   if ty.kind in tyUserTypeClasses and ty.isResolvedUserTypeClass:
-    ty = ty.lastSon
-  ty = skipTypes(ty, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
-  while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct})
+    ty = ty.last
+  ty = skipTypes(ty, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyOwned, tyAlias, tySink, tyStatic})
+  while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct, tyGenericInst, tyAlias})
   var check: PNode = nil
   if ty.kind == tyObject:
     while true:
       check = nil
       f = lookupInRecordAndBuildCheck(c, n, ty.n, i, check)
       if f != nil: break
-      if ty.sons[0] == nil: break
-      ty = skipTypes(ty.sons[0], skipPtrs)
+      if ty[0] == nil: break
+      ty = skipTypes(ty[0], skipPtrs)
     if f != nil:
-      if fieldVisible(c, f):
+      let visibilityCheckNeeded =
+        if n[1].kind == nkSym and n[1].sym == f:
+          false # field lookup was done already, likely by hygienic template or bindSym
+        else: true
+      if not visibilityCheckNeeded or fieldVisible(c, f):
         # is the access to a public field or in the same module or in a friend?
-        markUsed(c.config, n.sons[1].info, f, c.graph.usageSym)
-        styleCheckUse(n.sons[1].info, f)
-        n.sons[0] = makeDeref(n.sons[0])
-        n.sons[1] = newSymNode(f) # we now have the correct field
+        markUsed(c, n[1].info, f)
+        onUse(n[1].info, f)
+        let info = n[1].info
+        n[0] = makeDeref(n[0])
+        n[1] = newSymNode(f) # we now have the correct field
+        n[1].info = info # preserve the original info
         n.typ = f.typ
         if check == nil:
           result = n
         else:
-          check.sons[0] = n
+          check[0] = n
           check.typ = n.typ
           result = check
   elif ty.kind == tyTuple and ty.n != nil:
     f = getSymFromList(ty.n, i)
     if f != nil:
-      markUsed(c.config, n.sons[1].info, f, c.graph.usageSym)
-      styleCheckUse(n.sons[1].info, f)
-      n.sons[0] = makeDeref(n.sons[0])
-      n.sons[1] = newSymNode(f)
+      markUsed(c, n[1].info, f)
+      onUse(n[1].info, f)
+      n[0] = makeDeref(n[0])
+      n[1] = newSymNode(f)
       n.typ = f.typ
       result = n
 
   # we didn't find any field, let's look for a generic param
   if result == nil:
-    let t = n.sons[0].typ.skipTypes(tyDotOpTransparent)
-    tryReadingGenericParam(t)
+    let t = n[0].typ.skipTypes(tyDotOpTransparent)
+    result = tryReadingGenericParam(c, n, i, t)
+    flags.incl efCannotBeDotCall
 
 proc dotTransformation(c: PContext, n: PNode): PNode =
-  if isSymChoice(n.sons[1]):
+  if isSymChoice(n[1]) or
+      # generics usually leave field names as symchoices, but not types
+      (n[1].kind == nkSym and n[1].sym.kind == skType):
     result = newNodeI(nkDotCall, n.info)
-    addSon(result, n.sons[1])
-    addSon(result, copyTree(n[0]))
+    result.add n[1]
+    result.add copyTree(n[0])
   else:
-    var i = considerQuotedIdent(c, n.sons[1], n)
+    var i = considerQuotedIdent(c, n[1], n)
     result = newNodeI(nkDotCall, n.info)
     result.flags.incl nfDotField
-    addSon(result, newIdentNode(i, n[1].info))
-    addSon(result, copyTree(n[0]))
+    result.add newIdentNode(i, n[1].info)
+    result.add copyTree(n[0])
 
 proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
   # this is difficult, because the '.' is used in many different contexts
   # in Nim. We first allow types in the semantic checking.
-  result = builtinFieldAccess(c, n, flags)
-  if result == nil:
+  var f = flags - {efIsDotCall}
+  result = builtinFieldAccess(c, n, f)
+  if result == nil or ((result.typ == nil or result.typ.skipTypes(abstractInst).kind != tyProc) and
+      efIsDotCall in flags and callOperator notin c.features and
+      efCannotBeDotCall notin f):
     result = dotTransformation(c, n)
 
 proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode =
   result = newNodeI(nkCall, n.info)
   result.add(newIdentNode(ident, n.info))
-  for i in 0 .. n.len-1: result.add(n[i])
+  for s in n: result.add s
 
-proc semDeref(c: PContext, n: PNode): PNode =
+proc semDeref(c: PContext, n: PNode, flags: TExprFlags): PNode =
   checkSonsLen(n, 1, c.config)
-  n.sons[0] = semExprWithType(c, n.sons[0])
+  n[0] = semExprWithType(c, n[0])
+  let a = getConstExpr(c.module, n[0], c.idgen, c.graph)
+  if a != nil:
+    if a.kind == nkNilLit and efInTypeof notin flags:
+      localError(c.config, n.info, "nil dereference is not allowed")
+    n[0] = a
   result = n
-  var t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyLent, tyAlias, tySink})
+  var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyAlias, tySink, tyOwned})
   case t.kind
-  of tyRef, tyPtr: n.typ = t.lastSon
+  of tyRef, tyPtr: n.typ = t.elementType
+  of tyMetaTypes, tyFromExpr:
+    n.typ = makeTypeFromExpr(c, n.copyTree)
   else: result = nil
-  #GlobalError(n.sons[0].info, errCircumNeedsPointer)
+  #GlobalError(n[0].info, errCircumNeedsPointer)
+
+proc maybeInstantiateGeneric(c: PContext, n: PNode, s: PSym): PNode =
+  ## Instantiates generic if not lacking implicit generics,
+  ## otherwise returns n.
+  let
+    neededGenParams = s.ast[genericParamsPos].len
+    heldGenParams = n.len - 1
+  var implicitParams = 0
+  for x in s.ast[genericParamsPos]:
+    if tfImplicitTypeParam in x.typ.flags:
+      inc implicitParams
+  if heldGenParams != neededGenParams and implicitParams + heldGenParams == neededGenParams:
+    # This is an implicit + explicit generic procedure without all args passed,
+    # kicking back the sem'd symbol fixes #17212
+    # Uncertain the hackiness of this solution.
+    result = n
+  else:
+    result = explicitGenericInstantiation(c, n, s)
+    if result == n:
+      n[0] = copyTree(result[0])
 
 proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
   ## returns nil if not a built-in subscript operator; also called for the
   ## checking of assignments
-  if sonsLen(n) == 1:
-    let x = semDeref(c, n)
+  result = nil
+  if n.len == 1:
+    let x = semDeref(c, n, flags)
     if x == nil: return nil
+    if x.typ.kind == tyFromExpr:
+      # depends on generic type
+      return x
     result = newNodeIT(nkDerefExpr, x.info, x.typ)
     result.add(x[0])
     return
   checkMinSonsLen(n, 2, c.config)
-  # make sure we don't evaluate generic macros/templates
-  n.sons[0] = semExprWithType(c, n.sons[0],
-                              {efNoEvaluateGeneric})
-  let arr = skipTypes(n.sons[0].typ, {tyGenericInst,
+  # signal that generic parameters may be applied after
+  n[0] = semExprWithType(c, n[0], {efNoEvaluateGeneric, efAllowSymChoice})
+  var arr = skipTypes(n[0].typ, {tyGenericInst, tyUserTypeClassInst, tyOwned,
                                       tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
+  if arr.kind == tyStatic:
+    if arr.base.kind == tyNone:
+      result = n
+      result.typ = semStaticType(c, n[1], nil)
+      return
+    elif arr.n != nil:
+      return semSubscript(c, arr.n, flags)
+    else:
+      arr = arr.base
+
   case arr.kind
-  of tyArray, tyOpenArray, tyVarargs, tySequence, tyString,
-     tyCString:
+  of tyArray, tyOpenArray, tyVarargs, tySequence, tyString, tyCstring,
+    tyUncheckedArray:
     if n.len != 2: return nil
-    n.sons[0] = makeDeref(n.sons[0])
-    for i in countup(1, sonsLen(n) - 1):
-      n.sons[i] = semExprWithType(c, n.sons[i],
+    n[0] = makeDeref(n[0])
+    for i in 1..<n.len:
+      n[i] = semExprWithType(c, n[i],
                                   flags*{efInTypeof, efDetermineType})
-    var indexType = if arr.kind == tyArray: arr.sons[0] else: getSysType(c.graph, n.info, tyInt)
-    var arg = indexTypesMatch(c, indexType, n.sons[1].typ, n.sons[1])
-    if arg != nil:
-      n.sons[1] = arg
+    # Arrays index type is dictated by the range's type
+    if arr.kind == tyArray:
+      var indexType = arr[0]
+      var arg = indexTypesMatch(c, indexType, n[1].typ, n[1])
+      if arg != nil:
+        n[1] = arg
+        result = n
+        result.typ = elemType(arr)
+    # Other types have a bit more of leeway
+    elif n[1].typ.skipTypes(abstractRange-{tyDistinct}).kind in
+        {tyInt..tyInt64, tyUInt..tyUInt64}:
       result = n
       result.typ = elemType(arr)
-    #GlobalError(n.info, errIndexTypesDoNotMatch)
   of tyTypeDesc:
     # The result so far is a tyTypeDesc bound
     # a tyGenericBody. The line below will substitute
@@ -1287,27 +1801,30 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
     #result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
   of tyTuple:
     if n.len != 2: return nil
-    n.sons[0] = makeDeref(n.sons[0])
+    n[0] = makeDeref(n[0])
     # [] operator for tuples requires constant expression:
-    n.sons[1] = semConstExpr(c, n.sons[1])
-    if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal, tyAlias, tySink}).kind in
+    n[1] = semConstExpr(c, n[1])
+    if skipTypes(n[1].typ, {tyGenericInst, tyRange, tyOrdinal, tyAlias, tySink}).kind in
         {tyInt..tyInt64}:
-      var idx = getOrdValue(n.sons[1])
-      if idx >= 0 and idx < sonsLen(arr): n.typ = arr.sons[int(idx)]
-      else: localError(c.config, n.info, "invalid index value for tuple subscript")
+      let idx = getOrdValue(n[1])
+      if idx >= 0 and idx < arr.len: n.typ = arr[toInt(idx)]
+      else:
+        localError(c.config, n.info,
+          "invalid index $1 in subscript for tuple of length $2" %
+            [$idx, $arr.len])
       result = n
     else:
       result = nil
   else:
-    let s = if n.sons[0].kind == nkSym: n.sons[0].sym
-            elif n[0].kind in nkSymChoices: n.sons[0][0].sym
+    let s = if n[0].kind == nkSym: n[0].sym
+            elif n[0].kind in nkSymChoices + {nkOpenSym}: n[0][0].sym
             else: nil
     if s != nil:
       case s.kind
       of skProc, skFunc, skMethod, skConverter, skIterator:
         # type parameters: partial generic specialization
-        n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
-        result = explicitGenericInstantiation(c, n, s)
+        n[0] = semSymGenericInstantiation(c, n[0], s)
+        result = maybeInstantiateGeneric(c, n, s)
       of skMacro, skTemplate:
         if efInCall in flags:
           # We are processing macroOrTmpl[] in macroOrTmpl[](...) call.
@@ -1317,7 +1834,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
         else:
           # We are processing macroOrTmpl[] not in call. Transform it to the
           # macro or template call with generic arguments here.
-          n.kind = nkCall
+          n.transitionSonsKind(nkCall)
           case s.kind
           of skMacro: result = semMacroExpr(c, n, n, s, flags)
           of skTemplate: result = semTemplateExpr(c, n, s, flags)
@@ -1327,11 +1844,11 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
       else:
         discard
 
-proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
+proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
   result = semSubscript(c, n, flags)
   if result == nil:
     # overloaded [] operator:
-    result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "[]")))
+    result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "[]")), flags, expectedType)
 
 proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
   var id = considerQuotedIdent(c, a[1], a)
@@ -1340,10 +1857,9 @@ proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
   # this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
   # nodes?
   let aOrig = nOrig[0]
-  result = newNode(nkCall, n.info, sons = @[setterId, a[0],
-                                            semExprWithType(c, n[1])])
+  result = newTreeI(nkCall, n.info, setterId, a[0], n[1])
   result.flags.incl nfDotSetter
-  let orig = newNode(nkCall, n.info, sons = @[setterId, aOrig[0], nOrig[1]])
+  let orig = newTreeI(nkCall, n.info, setterId, aOrig[0], nOrig[1])
   result = semOverloadedCallAnalyseEffects(c, result, orig, {})
 
   if result != nil:
@@ -1356,48 +1872,143 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode =
   # return a view into the first argument (if there is one):
   let root = exprRoot(n)
   if root != nil and root.owner == c.p.owner:
+    template url: string = "var_t_return.html".createDocLink
     if root.kind in {skLet, skVar, skTemp} and sfGlobal notin root.flags:
-      localError(c.config, n.info, "'$1' escapes its stack frame; context: '$2'; see $3/var_t_return.html" % [
-        root.name.s, renderTree(n, {renderNoComments}), explanationsBaseUrl])
+      localError(c.config, n.info, "'$1' escapes its stack frame; context: '$2'; see $3" % [
+        root.name.s, renderTree(n, {renderNoComments}), url])
     elif root.kind == skParam and root.position != 0:
-      localError(c.config, n.info, "'$1' is not the first parameter; context: '$2'; see $3/var_t_return.html" % [
-        root.name.s, renderTree(n, {renderNoComments}), explanationsBaseUrl])
+      localError(c.config, n.info, "'$1' is not the first parameter; context: '$2'; see $3" % [
+        root.name.s, renderTree(n, {renderNoComments}), url])
   case n.kind
   of nkHiddenAddr, nkAddr: return n
-  of nkHiddenDeref, nkDerefExpr: return n.sons[0]
+  of nkDerefExpr: return n[0]
   of nkBracketExpr:
-    if len(n) == 1: return n.sons[0]
+    if n.len == 1: return n[0]
+  of nkHiddenDeref:
+    # issue #13848
+    # `proc fun(a: var int): var int = a`
+    discard
   else: discard
   let valid = isAssignable(c, n)
   if valid != arLValue:
-    if valid == arLocalLValue:
+    if valid in {arAddressableConst, arLentValue} and isLent:
+      discard "ok"
+    elif valid == arLocalLValue:
       localError(c.config, n.info, errXStackEscape % renderTree(n, {renderNoComments}))
-    elif not isLent:
+    else:
       localError(c.config, n.info, errExprHasNoAddress)
-  result = newNodeIT(nkHiddenAddr, n.info, makePtrType(c, n.typ))
+  result = newNodeIT(nkHiddenAddr, n.info, if n.typ.kind in {tyVar, tyLent}: n.typ else: makePtrType(c, n.typ))
+  if n.typ.kind in {tyVar, tyLent}:
+    n.typ = n.typ.elementType
   result.add(n)
 
 proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
   if le.kind == nkHiddenDeref:
-    var x = le.sons[0]
-    if x.typ.kind in {tyVar, tyLent} and x.kind == nkSym and x.sym.kind == skResult:
-      n.sons[0] = x # 'result[]' --> 'result'
-      n.sons[1] = takeImplicitAddr(c, ri, x.typ.kind == tyLent)
-      x.typ.flags.incl tfVarIsPtr
-      #echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info
+    var x = le[0]
+    if x.kind == nkSym:
+      if x.sym.kind == skResult and (x.typ.kind in {tyVar, tyLent} or classifyViewType(x.typ) != noView):
+        n[0] = x # 'result[]' --> 'result'
+        n[1] = takeImplicitAddr(c, ri, x.typ.kind == tyLent)
+        x.typ.flags.incl tfVarIsPtr
+        #echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info
+      elif sfGlobal in x.sym.flags:
+        x.typ.flags.incl tfVarIsPtr
+
+proc borrowCheck(c: PContext, n, le, ri: PNode) =
+  const
+    PathKinds0 = {nkDotExpr, nkCheckedFieldExpr,
+                  nkBracketExpr, nkAddr, nkHiddenAddr,
+                  nkObjDownConv, nkObjUpConv}
+    PathKinds1 = {nkHiddenStdConv, nkHiddenSubConv}
+
+  proc getRoot(n: PNode; followDeref: bool): PNode =
+    result = n
+    while true:
+      case result.kind
+      of nkDerefExpr, nkHiddenDeref:
+        if followDeref: result = result[0]
+        else: break
+      of PathKinds0:
+        result = result[0]
+      of PathKinds1:
+        result = result[1]
+      else: break
+
+  proc scopedLifetime(c: PContext; ri: PNode): bool {.inline.} =
+    let n = getRoot(ri, followDeref = false)
+    result = (ri.kind in nkCallKinds+{nkObjConstr}) or
+      (n.kind == nkSym and n.sym.owner == c.p.owner and n.sym.kind != skResult)
+
+  proc escapes(c: PContext; le: PNode): bool {.inline.} =
+    # param[].foo[] = self  definitely escapes, we don't need to
+    # care about pointer derefs:
+    let n = getRoot(le, followDeref = true)
+    result = n.kind == nkSym and n.sym.kind == skParam
+
+  # Special typing rule: do not allow to pass 'owned T' to 'T' in 'result = x':
+  const absInst = abstractInst - {tyOwned}
+  if ri.typ != nil and ri.typ.skipTypes(absInst).kind == tyOwned and
+      le.typ != nil and le.typ.skipTypes(absInst).kind != tyOwned and
+      scopedLifetime(c, ri):
+    if le.kind == nkSym and le.sym.kind == skResult:
+      localError(c.config, n.info, "cannot return an owned pointer as an unowned pointer; " &
+        "use 'owned(" & typeToString(le.typ) & ")' as the return type")
+    elif escapes(c, le):
+      localError(c.config, n.info,
+        "assignment produces a dangling ref: the unowned ref lives longer than the owned ref")
 
 template resultTypeIsInferrable(typ: PType): untyped =
   typ.isMetaType and typ.kind != tyTypeDesc
 
+proc goodLineInfo(arg: PNode): TLineInfo =
+  if arg.kind == nkStmtListExpr and arg.len > 0:
+    goodLineInfo(arg[^1])
+  else:
+    arg.info
+
+proc makeTupleAssignments(c: PContext; n: PNode): PNode =
+  ## expand tuple unpacking assignment into series of assignments
+  ##
+  ## mirrored with semstmts.makeVarTupleSection
+  let lhs = n[0]
+  let value = semExprWithType(c, n[1], {efTypeAllowed})
+  if value.typ.kind != tyTuple:
+    localError(c.config, n[1].info, errTupleUnpackingTupleExpected %
+      [typeToString(value.typ, preferDesc)])
+  elif lhs.len != value.typ.len:
+    localError(c.config, n.info, errTupleUnpackingDifferentLengths %
+      [$lhs.len, typeToString(value.typ, preferDesc), $value.typ.len])
+  result = newNodeI(nkStmtList, n.info)
+
+  let temp = newSym(skTemp, getIdent(c.cache, "tmpTupleAsgn"), c.idgen, getCurrOwner(c), n.info)
+  temp.typ = value.typ
+  temp.flags.incl(sfGenSym)
+  var v = newNodeI(nkLetSection, value.info)
+  let tempNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
+  var vpart = newNodeI(nkIdentDefs, v.info, 3)
+  vpart[0] = tempNode
+  vpart[1] = c.graph.emptyNode
+  vpart[2] = value
+  v.add vpart
+  result.add(v)
+
+  for i in 0..<lhs.len:
+    if lhs[i].kind == nkIdent and lhs[i].ident.id == ord(wUnderscore):
+      # skip _ assignments if we are using a temp as they are already evaluated
+      discard
+    else:
+      result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempNode, i))
+
 proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
   checkSonsLen(n, 2, c.config)
-  var a = n.sons[0]
+  var a = n[0]
   case a.kind
   of nkDotExpr:
     # r.f = x
     # --> `f=` (r, x)
     let nOrig = n.copyTree
-    a = builtinFieldAccess(c, a, {efLValue})
+    var flags = {efLValue}
+    a = builtinFieldAccess(c, a, flags)
     if a == nil:
       a = propertyWriteAccess(c, n, nOrig, n[0])
       if a != nil: return a
@@ -1405,102 +2016,112 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
       # possible:
       a = dotTransformation(c, n[0])
       if a.kind == nkDotCall:
-        a.kind = nkCall
+        a.transitionSonsKind(nkCall)
         a = semExprWithType(c, a, {efLValue})
   of nkBracketExpr:
     # a[i] = x
     # --> `[]=`(a, i, x)
     a = semSubscript(c, a, {efLValue})
     if a == nil:
-      result = buildOverloadedSubscripts(n.sons[0], getIdent(c.cache, "[]="))
-      add(result, n[1])
+      result = buildOverloadedSubscripts(n[0], getIdent(c.cache, "[]="))
+      result.add(n[1])
       if mode == noOverloadedSubscript:
-        bracketNotFoundError(c, result)
-        return n
+        bracketNotFoundError(c, result, {})
+        return errorNode(c, n)
       else:
         result = semExprNoType(c, result)
         return result
   of nkCurlyExpr:
     # a{i} = x -->  `{}=`(a, i, x)
-    result = buildOverloadedSubscripts(n.sons[0], getIdent(c.cache, "{}="))
-    add(result, n[1])
+    result = buildOverloadedSubscripts(n[0], getIdent(c.cache, "{}="))
+    result.add(n[1])
     return semExprNoType(c, result)
   of nkPar, nkTupleConstr:
-    if a.len >= 2:
+    if a.len >= 2 or a.kind == nkTupleConstr:
       # unfortunately we need to rewrite ``(x, y) = foo()`` already here so
       # that overloading of the assignment operator still works. Usually we
       # prefer to do these rewritings in transf.nim:
-      return semStmt(c, lowerTupleUnpackingForAsgn(c.graph, n, c.p.owner))
+      return semStmt(c, makeTupleAssignments(c, n), {})
     else:
       a = semExprWithType(c, a, {efLValue})
   else:
     a = semExprWithType(c, a, {efLValue})
-  n.sons[0] = a
+  n[0] = a
   # a = b # both are vars, means: a[] = b[]
   # a = b # b no 'var T' means: a = addr(b)
   var le = a.typ
-  if (skipTypes(le, {tyGenericInst, tyAlias, tySink}).kind != tyVar and
-        isAssignable(c, a) == arNone) or
-      skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs}:
+  let assignable = isAssignable(c, a)
+  let root = getRoot(a)
+  let useStrictDefLet = root != nil and root.kind == skLet and
+                       assignable == arAddressableConst and
+                       strictDefs in c.features and isLocalSym(root)
+  if le == nil:
+    localError(c.config, a.info, "expression has no type")
+  elif (skipTypes(le, {tyGenericInst, tyAlias, tySink}).kind notin {tyVar} and
+        assignable in {arNone, arLentValue, arAddressableConst} and not useStrictDefLet
+        ) or (skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs} and views notin c.features):
     # Direct assignment to a discriminant is allowed!
     localError(c.config, a.info, errXCannotBeAssignedTo %
                renderTree(a, {renderNoComments}))
   else:
-    let
-      lhs = n.sons[0]
-      lhsIsResult = lhs.kind == nkSym and lhs.sym.kind == skResult
-    var
-      rhs = semExprWithType(c, n.sons[1],
-        if lhsIsResult: {efAllowDestructor} else: {})
-    if lhsIsResult:
+    let lhs = n[0]
+    let rhs = semExprWithType(c, n[1], {efTypeAllowed}, le)
+    if lhs.kind == nkSym and lhs.sym.kind == skResult:
       n.typ = c.enforceVoidContext
       if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ):
         var rhsTyp = rhs.typ
         if rhsTyp.kind in tyUserTypeClasses and rhsTyp.isResolvedUserTypeClass:
-          rhsTyp = rhsTyp.lastSon
+          rhsTyp = rhsTyp.last
+        if lhs.sym.typ.kind == tyAnything:
+          rhsTyp = rhsTyp.skipTypes({tySink}).skipIntLit(c.idgen)
         if cmpTypes(c, lhs.typ, rhsTyp) in {isGeneric, isEqual}:
           internalAssert c.config, c.p.resultSym != nil
+          # Make sure the type is valid for the result variable
+          typeAllowedCheck(c, n.info, rhsTyp, skResult)
           lhs.typ = rhsTyp
           c.p.resultSym.typ = rhsTyp
-          c.p.owner.typ.sons[0] = rhsTyp
+          c.p.owner.typ.setReturnType rhsTyp
         else:
-          typeMismatch(c.config, n.info, lhs.typ, rhsTyp)
+          typeMismatch(c.config, n.info, lhs.typ, rhsTyp, rhs)
+    borrowCheck(c, n, lhs, rhs)
 
-    n.sons[1] = fitNode(c, le, rhs, n.info)
-    if destructor notin c.features:
-      if tfHasAsgn in lhs.typ.flags and not lhsIsResult and
-          mode != noOverloadedAsgn:
-        return overloadedAsgn(c, lhs, n.sons[1])
-    else:
-      liftTypeBoundOps(c, lhs.typ, lhs.info)
+    n[1] = fitNode(c, le, rhs, goodLineInfo(n[1]))
+    when false: liftTypeBoundOps(c, lhs.typ, lhs.info)
 
     fixAbstractType(c, n)
-    asgnToResultVar(c, n, n.sons[0], n.sons[1])
+    asgnToResultVar(c, n, n[0], n[1])
   result = n
 
 proc semReturn(c: PContext, n: PNode): PNode =
   result = n
   checkSonsLen(n, 1, c.config)
-  if c.p.owner.kind in {skConverter, skMethod, skProc, skFunc, skMacro} or (
-     c.p.owner.kind == skIterator and c.p.owner.typ.callConv == ccClosure):
-    if n.sons[0].kind != nkEmpty:
-      # transform ``return expr`` to ``result = expr; return``
-      if c.p.resultSym != nil:
-        var a = newNodeI(nkAsgn, n.sons[0].info)
-        addSon(a, newSymNode(c.p.resultSym))
-        addSon(a, n.sons[0])
-        n.sons[0] = semAsgn(c, a)
-        # optimize away ``result = result``:
-        if n[0][1].kind == nkSym and n[0][1].sym == c.p.resultSym:
-          n.sons[0] = c.graph.emptyNode
+  if c.p.owner.kind in {skConverter, skMethod, skProc, skFunc, skMacro} or
+      (not c.p.owner.typ.isNil and isClosureIterator(c.p.owner.typ)):
+    if n[0].kind != nkEmpty:
+      if n[0].kind == nkAsgn and n[0][0].kind == nkSym and c.p.resultSym == n[0][0].sym:
+        discard "return is already transformed"
+      elif c.p.resultSym != nil:
+        # transform ``return expr`` to ``result = expr; return``
+        var a = newNodeI(nkAsgn, n[0].info)
+        a.add newSymNode(c.p.resultSym)
+        a.add n[0]
+        n[0] = a
       else:
         localError(c.config, n.info, errNoReturnTypeDeclared)
+        return
+      result[0] = semAsgn(c, n[0])
+      # optimize away ``result = result``:
+      if result[0][1].kind == nkSym and result[0][1].sym == c.p.resultSym:
+        result[0] = c.graph.emptyNode
   else:
     localError(c.config, n.info, "'return' not allowed here")
 
-proc semProcBody(c: PContext, n: PNode): PNode =
+proc semProcBody(c: PContext, n: PNode; expectedType: PType = nil): PNode =
+  when defined(nimsuggest):
+    if c.graph.config.expandDone():
+      return n
   openScope(c)
-  result = semExpr(c, n)
+  result = semExpr(c, n, expectedType = expectedType)
   if c.p.resultSym != nil and not isEmptyType(result.typ):
     if result.kind == nkNilLit:
       # or ImplicitlyDiscardable(result):
@@ -1514,110 +2135,137 @@ proc semProcBody(c: PContext, n: PNode): PNode =
       fixNilType(c, result)
     else:
       var a = newNodeI(nkAsgn, n.info, 2)
-      a.sons[0] = newSymNode(c.p.resultSym)
-      a.sons[1] = result
+      a[0] = newSymNode(c.p.resultSym)
+      a[1] = result
       result = semAsgn(c, a)
   else:
-    discardCheck(c, result)
+    discardCheck(c, result, {})
 
   if c.p.owner.kind notin {skMacro, skTemplate} and
      c.p.resultSym != nil and c.p.resultSym.typ.isMetaType:
     if isEmptyType(result.typ):
       # we inferred a 'void' return type:
       c.p.resultSym.typ = errorType(c)
-      c.p.owner.typ.sons[0] = nil
+      c.p.owner.typ.setReturnType nil
     else:
-      localError(c.config, c.p.resultSym.info, errCannotInferReturnType)
-
+      localError(c.config, c.p.resultSym.info, errCannotInferReturnType %
+        c.p.owner.name.s)
+  if isIterator(c.p.owner.typ) and c.p.owner.typ.returnType != nil and
+      c.p.owner.typ.returnType.kind == tyAnything:
+    localError(c.config, c.p.owner.info, errCannotInferReturnType %
+      c.p.owner.name.s)
   closeScope(c)
 
 proc semYieldVarResult(c: PContext, n: PNode, restype: PType) =
   var t = skipTypes(restype, {tyGenericInst, tyAlias, tySink})
   case t.kind
   of tyVar, tyLent:
-    if t.kind == tyVar: t.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892
-    if n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv}:
-      n.sons[0] = n.sons[0].sons[1]
-    n.sons[0] = takeImplicitAddr(c, n.sons[0], t.kind == tyLent)
+    t.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892
+    if n[0].kind in {nkHiddenStdConv, nkHiddenSubConv}:
+      n[0] = n[0][1]
+    n[0] = takeImplicitAddr(c, n[0], t.kind == tyLent)
   of tyTuple:
-    for i in 0..<t.sonsLen:
-      var e = skipTypes(t.sons[i], {tyGenericInst, tyAlias, tySink})
+    for i in 0..<t.len:
+      let e = skipTypes(t[i], {tyGenericInst, tyAlias, tySink})
       if e.kind in {tyVar, tyLent}:
-        if e.kind == tyVar: e.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892
-        if n.sons[0].kind in {nkPar, nkTupleConstr}:
-          n.sons[0].sons[i] = takeImplicitAddr(c, n.sons[0].sons[i], e.kind == tyLent)
-        elif n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv} and
-             n.sons[0].sons[1].kind in {nkPar, nkTupleConstr}:
-          var a = n.sons[0].sons[1]
-          a.sons[i] = takeImplicitAddr(c, a.sons[i], false)
+        e.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892
+        let tupleConstr = if n[0].kind in {nkHiddenStdConv, nkHiddenSubConv}: n[0][1] else: n[0]
+        if tupleConstr.kind in {nkPar, nkTupleConstr}:
+          if tupleConstr[i].kind == nkExprColonExpr:
+            tupleConstr[i][1] = takeImplicitAddr(c, tupleConstr[i][1], e.kind == tyLent)
+          else:
+            tupleConstr[i] = takeImplicitAddr(c, tupleConstr[i], e.kind == tyLent)
         else:
-          localError(c.config, n.sons[0].info, errXExpected, "tuple constructor")
-  else: discard
+          localError(c.config, n[0].info, errXExpected, "tuple constructor")
+      elif e.kind == tyEmpty:
+        localError(c.config, n[0].info, errTypeExpected)
+  else:
+    when false:
+      # XXX investigate what we really need here.
+      if isViewType(t):
+        n[0] = takeImplicitAddr(c, n[0], false)
 
 proc semYield(c: PContext, n: PNode): PNode =
   result = n
   checkSonsLen(n, 1, c.config)
   if c.p.owner == nil or c.p.owner.kind != skIterator:
     localError(c.config, n.info, errYieldNotAllowedHere)
-  elif n.sons[0].kind != nkEmpty:
-    n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility:
+  elif n[0].kind != nkEmpty:
     var iterType = c.p.owner.typ
-    let restype = iterType.sons[0]
+    let restype = iterType[0]
+    n[0] = semExprWithType(c, n[0], {}, restype) # check for type compatibility:
     if restype != nil:
-      if restype.kind != tyExpr:
-        n.sons[0] = fitNode(c, restype, n.sons[0], n.info)
-      if n.sons[0].typ == nil: internalError(c.config, n.info, "semYield")
+      if n[0].typ == nil: internalError(c.config, n.info, "semYield")
 
       if resultTypeIsInferrable(restype):
-        let inferred = n.sons[0].typ
-        iterType.sons[0] = inferred
+        let inferred = n[0].typ
+        iterType[0] = inferred
+        if c.p.resultSym != nil:
+          c.p.resultSym.typ = inferred
+      else:
+        n[0] = fitNode(c, restype, n[0], n.info)
 
       semYieldVarResult(c, n, restype)
     else:
       localError(c.config, n.info, errCannotReturnExpr)
-  elif c.p.owner.typ.sons[0] != nil:
+  elif c.p.owner.typ.returnType != nil:
     localError(c.config, n.info, errGenerated, "yield statement must yield a value")
 
-proc lookUpForDefined(c: PContext, i: PIdent, onlyCurrentScope: bool): PSym =
-  if onlyCurrentScope:
-    result = localSearchInScope(c, i)
+proc considerQuotedIdentOrDot(c: PContext, n: PNode, origin: PNode = nil): PIdent =
+  if n.kind == nkDotExpr:
+    let a = considerQuotedIdentOrDot(c, n[0], origin).s
+    let b = considerQuotedIdentOrDot(c, n[1], origin).s
+    var s = newStringOfCap(a.len + b.len + 1)
+    s.add(a)
+    s.add('.')
+    s.add(b)
+    result = getIdent(c.cache, s)
   else:
-    result = searchInScopes(c, i) # no need for stub loading
+    result = considerQuotedIdent(c, n, origin)
+
+proc semDefined(c: PContext, n: PNode): PNode =
+  checkSonsLen(n, 2, c.config)
+  # we replace this node by a 'true' or 'false' node:
+  result = newIntNode(nkIntLit, 0)
+  result.intVal = ord isDefined(c.config, considerQuotedIdentOrDot(c, n[1], n).s)
+  result.info = n.info
+  result.typ = getSysType(c.graph, n.info, tyBool)
 
-proc lookUpForDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
+proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
   case n.kind
-  of nkIdent:
-    result = lookUpForDefined(c, n.ident, onlyCurrentScope)
+  of nkIdent, nkAccQuoted:
+    var amb = false
+    let ident = considerQuotedIdent(c, n)
+    result = if onlyCurrentScope:
+               localSearchInScope(c, ident)
+             else:
+               searchInScopes(c, ident, amb)
   of nkDotExpr:
     result = nil
     if onlyCurrentScope: return
     checkSonsLen(n, 2, c.config)
-    var m = lookUpForDefined(c, n.sons[0], onlyCurrentScope)
+    var m = lookUpForDeclared(c, n[0], onlyCurrentScope)
     if m != nil and m.kind == skModule:
       let ident = considerQuotedIdent(c, n[1], n)
       if m == c.module:
         result = strTableGet(c.topLevelScope.symbols, ident)
       else:
-        result = strTableGet(m.tab, ident)
-  of nkAccQuoted:
-    result = lookUpForDefined(c, considerQuotedIdent(c, n), onlyCurrentScope)
+        result = someSym(c.graph, m, ident)
   of nkSym:
     result = n.sym
   of nkOpenSymChoice, nkClosedSymChoice:
-    result = n.sons[0].sym
+    result = n[0].sym
+  of nkOpenSym:
+    result = lookUpForDeclared(c, n[0], onlyCurrentScope)
   else:
     localError(c.config, n.info, "identifier expected, but got: " & renderTree(n))
     result = nil
 
-proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode =
+proc semDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PNode =
   checkSonsLen(n, 2, c.config)
   # we replace this node by a 'true' or 'false' node:
   result = newIntNode(nkIntLit, 0)
-  if not onlyCurrentScope and considerQuotedIdent(c, n[0], n).s == "defined":
-    let d = considerQuotedIdent(c, n[1], n)
-    result.intVal = ord isDefined(c.config, d.s)
-  elif lookUpForDefined(c, n.sons[1], onlyCurrentScope) != nil:
-    result.intVal = 1
+  result.intVal = ord lookUpForDeclared(c, n[1], onlyCurrentScope) != nil
   result.info = n.info
   result.typ = getSysType(c.graph, n.info, tyBool)
 
@@ -1644,11 +2292,11 @@ proc expectString(c: PContext, n: PNode): string =
   if n.kind in nkStrKinds:
     return n.strVal
   else:
+    result = ""
     localError(c.config, n.info, errStringLiteralExpected)
 
 proc newAnonSym(c: PContext; kind: TSymKind, info: TLineInfo): PSym =
-  result = newSym(kind, c.cache.idAnon, getCurrOwner(c), info)
-  result.flags = {sfGenSym}
+  result = newSym(kind, c.cache.idAnon, c.idgen, getCurrOwner(c), info)
 
 proc semExpandToAst(c: PContext, n: PNode): PNode =
   let macroCall = n[1]
@@ -1657,19 +2305,19 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
     let expandedSym = expectMacroOrTemplateCall(c, macroCall)
     if expandedSym.kind == skError: return n
 
-    macroCall.sons[0] = newSymNode(expandedSym, macroCall.info)
-    markUsed(c.config, n.info, expandedSym, c.graph.usageSym)
-    styleCheckUse(n.info, expandedSym)
+    macroCall[0] = newSymNode(expandedSym, macroCall.info)
+    markUsed(c, n.info, expandedSym)
+    onUse(n.info, expandedSym)
 
   if isCallExpr(macroCall):
-    for i in countup(1, macroCall.len-1):
-      #if macroCall.sons[0].typ.sons[i].kind != tyExpr:
-      macroCall.sons[i] = semExprWithType(c, macroCall[i], {})
+    for i in 1..<macroCall.len:
+      #if macroCall[0].typ[i].kind != tyUntyped:
+      macroCall[i] = semExprWithType(c, macroCall[i], {})
     # performing overloading resolution here produces too serious regressions:
     let headSymbol = macroCall[0]
     var cands = 0
     var cand: PSym = nil
-    var o: TOverloadIter
+    var o: TOverloadIter = default(TOverloadIter)
     var symx = initOverloadIter(o, c, headSymbol)
     while symx != nil:
       if symx.kind in {skTemplate, skMacro} and symx.typ.len == macroCall.len:
@@ -1681,17 +2329,17 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
     elif cands >= 2:
       localError(c.config, n.info, "ambiguous symbol in 'getAst' context: " & $macroCall)
     else:
-      let info = macroCall.sons[0].info
-      macroCall.sons[0] = newSymNode(cand, info)
-      markUsed(c.config, info, cand, c.graph.usageSym)
-      styleCheckUse(info, cand)
+      let info = macroCall[0].info
+      macroCall[0] = newSymNode(cand, info)
+      markUsed(c, info, cand)
+      onUse(info, cand)
 
     # we just perform overloading resolution here:
-    #n.sons[1] = semOverloadedCall(c, macroCall, macroCall, {skTemplate, skMacro})
+    #n[1] = semOverloadedCall(c, macroCall, macroCall, {skTemplate, skMacro})
   else:
     localError(c.config, n.info, "getAst takes a call, but got " & n.renderTree)
   # Preserve the magic symbol in order to be handled in evals.nim
-  internalAssert c.config, n.sons[0].sym.magic == mExpandToAst
+  internalAssert c.config, n[0].sym.magic == mExpandToAst
   #n.typ = getSysSym("NimNode").typ # expandedSym.getReturnType
   if n.kind == nkStmtList and n.len == 1: result = n[0]
   else: result = n
@@ -1699,8 +2347,8 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
 
 proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
                     flags: TExprFlags = {}): PNode =
-  if sonsLen(n) == 2:
-    n.sons[0] = newSymNode(magicSym, n.info)
+  if n.len == 2:
+    n[0] = newSymNode(magicSym, n.info)
     result = semExpandToAst(c, n)
   else:
     result = semDirectOp(c, n, flags)
@@ -1714,73 +2362,113 @@ proc processQuotations(c: PContext; n: var PNode, op: string,
     ids.add n
     return
 
-  if n.kind == nkPrefix:
-    checkSonsLen(n, 2, c.config)
-    if n[0].kind == nkIdent:
-      var examinedOp = n[0].ident.s
+  template handlePrefixOp(prefixed) =
+    if prefixed[0].kind == nkIdent:
+      let examinedOp = prefixed[0].ident.s
       if examinedOp == op:
-        returnQuote n[1]
+        returnQuote prefixed[1]
       elif examinedOp.startsWith(op):
-        n.sons[0] = newIdentNode(getIdent(c.cache, examinedOp.substr(op.len)), n.info)
-  elif n.kind == nkAccQuoted and op == "``":
-    returnQuote n[0]
+        prefixed[0] = newIdentNode(getIdent(c.cache, examinedOp.substr(op.len)), prefixed.info)
 
-  for i in 0 ..< n.safeLen:
-    processQuotations(c, n.sons[i], op, quotes, ids)
+  if n.kind == nkPrefix:
+    checkSonsLen(n, 2, c.config)
+    handlePrefixOp(n)
+  elif n.kind == nkAccQuoted:
+    if op == "``":
+      returnQuote n[0]
+    else: # [bug #7589](https://github.com/nim-lang/Nim/issues/7589)
+      if n.len == 2 and n[0].ident.s == op:
+        var tempNode = nkPrefix.newTree()
+        tempNode.newSons(2)
+        tempNode[0] = n[0]
+        tempNode[1] = n[1]
+        handlePrefixOp(tempNode)
+  elif n.kind == nkIdent:
+    if n.ident.s == "result":
+      n = ids[0]
+
+  for i in 0..<n.safeLen:
+    processQuotations(c, n[i], op, quotes, ids)
 
 proc semQuoteAst(c: PContext, n: PNode): PNode =
-  internalAssert c.config, n.len == 2 or n.len == 3
+  if n.len != 2 and n.len != 3:
+    localError(c.config, n.info, "'quote' expects 1 or 2 arguments")
+    return n
   # We transform the do block into a template with a param for
   # each interpolation. We'll pass this template to getAst.
   var
     quotedBlock = n[^1]
     op = if n.len == 3: expectString(c, n[1]) else: "``"
-    quotes = newSeq[PNode](1)
+    quotes = newSeq[PNode](2)
       # the quotes will be added to a nkCall statement
-      # leave some room for the callee symbol
-    ids = newSeq[PNode]()
+      # leave some room for the callee symbol and the result symbol
+    ids = newSeq[PNode](1)
       # this will store the generated param names
+      # leave some room for the result symbol
 
   if quotedBlock.kind != nkStmtList:
     localError(c.config, n.info, errXExpected, "block")
 
+  # This adds a default first field to pass the result symbol
+  ids[0] = newAnonSym(c, skParam, n.info).newSymNode
   processQuotations(c, quotedBlock, op, quotes, ids)
 
+  let dummyTemplateSym = newAnonSym(c, skTemplate, n.info)
+  incl(dummyTemplateSym.flags, sfTemplateRedefinition)
   var dummyTemplate = newProcNode(
     nkTemplateDef, quotedBlock.info, body = quotedBlock,
     params = c.graph.emptyNode,
-    name = newAnonSym(c, skTemplate, n.info).newSymNode,
+    name = dummyTemplateSym.newSymNode,
               pattern = c.graph.emptyNode, genericParams = c.graph.emptyNode,
               pragmas = c.graph.emptyNode, exceptions = c.graph.emptyNode)
 
   if ids.len > 0:
-    dummyTemplate.sons[paramsPos] = newNodeI(nkFormalParams, n.info)
-    dummyTemplate[paramsPos].add getSysSym(c.graph, n.info, "typed").newSymNode # return type
-    ids.add getSysSym(c.graph, n.info, "untyped").newSymNode # params type
-    ids.add c.graph.emptyNode # no default value
-    dummyTemplate[paramsPos].add newNode(nkIdentDefs, n.info, ids)
-
+    dummyTemplate[paramsPos] = newNodeI(nkFormalParams, n.info)
+    dummyTemplate[paramsPos].add getSysSym(c.graph, n.info, "untyped").newSymNode # return type
+    dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[0], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode)
+    for i in 1..<ids.len:
+      let exp = semExprWithType(c, quotes[i+1], {})
+      let typ = exp.typ
+      if tfTriggersCompileTime notin typ.flags and typ.kind != tyStatic and exp.kind == nkSym and exp.sym.kind notin routineKinds + {skType}:
+        dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[i], newNodeIT(nkType, n.info, typ), c.graph.emptyNode)
+      else:
+        dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[i], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode)
   var tmpl = semTemplateDef(c, dummyTemplate)
   quotes[0] = tmpl[namePos]
-  result = newNode(nkCall, n.info, @[
-     createMagic(c.graph, "getAst", mExpandToAst).newSymNode,
-    newNode(nkCall, n.info, quotes)])
+  # This adds a call to newIdentNode("result") as the first argument to the template call
+  let identNodeSym = getCompilerProc(c.graph, "newIdentNode")
+  # so that new Nim compilers can compile old macros.nim versions, we check for 'nil'
+  # here and provide the old fallback solution:
+  let identNode = if identNodeSym == nil:
+                    newIdentNode(getIdent(c.cache, "newIdentNode"), n.info)
+                  else:
+                    identNodeSym.newSymNode
+  quotes[1] = newTreeI(nkCall, n.info, identNode, newStrNode(nkStrLit, "result"))
+  result = newTreeI(nkCall, n.info,
+     createMagic(c.graph, c.idgen, "getAst", mExpandToAst).newSymNode,
+     newTreeI(nkCall, n.info, quotes))
   result = semExpandToAst(c, result)
 
 proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   # watch out, hacks ahead:
+  when defined(nimsuggest):
+    # Remove the error hook so nimsuggest doesn't report errors there
+    let tempHook = c.graph.config.structuredErrorHook
+    c.graph.config.structuredErrorHook = nil
   let oldErrorCount = c.config.errorCounter
   let oldErrorMax = c.config.errorMax
   let oldCompilesId = c.compilesContextId
-  inc c.compilesContextIdGenerator
-  c.compilesContextId = c.compilesContextIdGenerator
-  # do not halt after first error:
-  c.config.errorMax = high(int)
+  # if this is a nested 'when compiles', do not increase the ID so that
+  # generic instantiations can still be cached for this level.
+  if c.compilesContextId == 0:
+    inc c.compilesContextIdGenerator
+    c.compilesContextId = c.compilesContextIdGenerator
+  c.config.errorMax = high(int) # `setErrorMaxHighMaybe` not appropriate here
 
   # open a scope for temporary symbol inclusions:
   let oldScope = c.currentScope
   openScope(c)
-  let oldOwnerLen = len(c.graph.owners)
+  let oldOwnerLen = c.graph.owners.len
   let oldGenerics = c.generics
   let oldErrorOutputs = c.config.m.errorOutputs
   if efExplain notin flags: c.config.m.errorOutputs = {}
@@ -1795,9 +2483,12 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   var err: string
   try:
     result = semExpr(c, n, flags)
-    if c.config.errorCounter != oldErrorCount: result = nil
+    if result != nil and efNoSem2Check notin flags:
+      trackStmt(c, c.module, result, isTopLevel = false)
+    if c.config.errorCounter != oldErrorCount:
+      result = nil
   except ERecoverableError:
-    discard
+    result = nil
   # undo symbol table changes (as far as it's possible):
   c.compilesContextId = oldCompilesId
   c.generics = oldGenerics
@@ -1812,17 +2503,20 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   c.config.m.errorOutputs = oldErrorOutputs
   c.config.errorCounter = oldErrorCount
   c.config.errorMax = oldErrorMax
+  when defined(nimsuggest):
+    # Restore the error hook
+    c.graph.config.structuredErrorHook = tempHook
 
 proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
   # we replace this node by a 'true' or 'false' node:
-  if sonsLen(n) != 2: return semDirectOp(c, n, flags)
+  if n.len != 2: return semDirectOp(c, n, flags)
 
   result = newIntNode(nkIntLit, ord(tryExpr(c, n[1], flags) != nil))
   result.info = n.info
   result.typ = getSysType(c.graph, n.info, tyBool)
 
 proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode =
-  if sonsLen(n) == 3:
+  if n.len == 3:
     # XXX ugh this is really a hack: shallowCopy() can be overloaded only
     # with procs that take not 2 parameters:
     result = newNodeI(nkFastAsgn, n.info)
@@ -1833,9 +2527,9 @@ proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode =
     result = semDirectOp(c, n, flags)
 
 proc createFlowVar(c: PContext; t: PType; info: TLineInfo): PType =
-  result = newType(tyGenericInvocation, c.module)
-  addSonSkipIntLit(result, magicsys.getCompilerProc(c.graph, "FlowVar").typ)
-  addSonSkipIntLit(result, t)
+  result = newType(tyGenericInvocation, c.idgen, c.module)
+  addSonSkipIntLit(result, magicsys.getCompilerProc(c.graph, "FlowVar").typ, c.idgen)
+  addSonSkipIntLit(result, t, c.idgen)
   result = instGenericContainer(c, info, result, allowMetaTypes = false)
 
 proc instantiateCreateFlowVarCall(c: PContext; t: PType;
@@ -1843,84 +2537,107 @@ proc instantiateCreateFlowVarCall(c: PContext; t: PType;
   let sym = magicsys.getCompilerProc(c.graph, "nimCreateFlowVar")
   if sym == nil:
     localError(c.config, info, "system needs: nimCreateFlowVar")
-  var bindings: TIdTable
-  initIdTable(bindings)
-  bindings.idTablePut(sym.ast[genericParamsPos].sons[0].typ, t)
+  var bindings = initTypeMapping()
+  bindings.idTablePut(sym.ast[genericParamsPos][0].typ, t)
   result = c.semGenerateInstance(c, sym, bindings, info)
   # since it's an instantiation, we unmark it as a compilerproc. Otherwise
   # codegen would fail:
   if sfCompilerProc in result.flags:
-    result.flags = result.flags - {sfCompilerProc, sfExportC, sfImportC}
-    result.loc.r = nil
+    result.flags.excl {sfCompilerProc, sfExportc, sfImportc}
+    result.loc.snippet = ""
 
 proc setMs(n: PNode, s: PSym): PNode =
   result = n
-  n.sons[0] = newSymNode(s)
-  n.sons[0].info = n.info
+  n[0] = newSymNode(s)
+  n[0].info = n.info
 
-proc extractImports(n: PNode; result: PNode) =
-  if n.kind in {nkImportStmt, nkImportExceptStmt, nkFromStmt}:
-    result.add copyTree(n)
-    n.kind = nkEmpty
-    return
-  for i in 0..<n.safeLen: extractImports(n[i], result)
+proc semSizeof(c: PContext, n: PNode): PNode =
+  if n.len != 2:
+    localError(c.config, n.info, errXExpectsTypeOrValue % "sizeof")
+  else:
+    n[1] = semExprWithType(c, n[1], {efDetermineType})
+    #restoreOldStyleType(n[1])
+  n.typ = getSysType(c.graph, n.info, tyInt)
+  result = foldSizeOf(c.config, n, n)
 
-proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
+proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags; expectedType: PType = nil): PNode =
   # this is a hotspot in the compiler!
-  # DON'T forget to update ast.SpecialSemMagics if you add a magic here!
   result = n
   case s.magic # magics that need special treatment
   of mAddr:
+    markUsed(c, n.info, s)
     checkSonsLen(n, 2, c.config)
-    result = semAddr(c, n.sons[1], s.name.s == "unsafeAddr")
+    result = semAddr(c, n[1])
   of mTypeOf:
-    checkSonsLen(n, 2, c.config)
-    result = semTypeOf(c, n.sons[1])
-  #of mArrGet: result = semArrGet(c, n, flags)
-  #of mArrPut: result = semArrPut(c, n, flags)
-  #of mAsgn: result = semAsgnOpr(c, n)
-  of mDefined: result = semDefined(c, setMs(n, s), false)
-  of mDefinedInScope: result = semDefined(c, setMs(n, s), true)
-  of mCompiles: result = semCompiles(c, setMs(n, s), flags)
-  #of mLow: result = semLowHigh(c, setMs(n, s), mLow)
-  #of mHigh: result = semLowHigh(c, setMs(n, s), mHigh)
-  of mSizeOf: result = semSizeof(c, setMs(n, s))
-  of mIs: result = semIs(c, setMs(n, s), flags)
-  #of mOf: result = semOf(c, setMs(n, s))
-  of mShallowCopy: result = semShallowCopy(c, n, flags)
-  of mExpandToAst: result = semExpandToAst(c, n, s, flags)
-  of mQuoteAst: result = semQuoteAst(c, n)
+    markUsed(c, n.info, s)
+    result = semTypeOf(c, n)
+  of mDefined:
+    markUsed(c, n.info, s)
+    result = semDefined(c, setMs(n, s))
+  of mDeclared:
+    markUsed(c, n.info, s)
+    result = semDeclared(c, setMs(n, s), false)
+  of mDeclaredInScope:
+    markUsed(c, n.info, s)
+    result = semDeclared(c, setMs(n, s), true)
+  of mCompiles:
+    markUsed(c, n.info, s)
+    result = semCompiles(c, setMs(n, s), flags)
+  of mIs:
+    markUsed(c, n.info, s)
+    result = semIs(c, setMs(n, s), flags)
+  of mShallowCopy:
+    markUsed(c, n.info, s)
+    result = semShallowCopy(c, n, flags)
+  of mExpandToAst:
+    markUsed(c, n.info, s)
+    result = semExpandToAst(c, n, s, flags)
+  of mQuoteAst:
+    markUsed(c, n.info, s)
+    result = semQuoteAst(c, n)
   of mAstToStr:
+    markUsed(c, n.info, s)
     checkSonsLen(n, 2, c.config)
     result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, c.graph)
     result.typ = getSysType(c.graph, n.info, tyString)
   of mParallel:
+    markUsed(c, n.info, s)
     if parallel notin c.features:
       localError(c.config, n.info, "use the {.experimental.} pragma to enable 'parallel'")
     result = setMs(n, s)
     var x = n.lastSon
-    if x.kind == nkDo: x = x.sons[bodyPos]
+    if x.kind == nkDo: x = x[bodyPos]
     inc c.inParallelStmt
-    result.sons[1] = semStmt(c, x)
+    result[1] = semStmt(c, x, {})
     dec c.inParallelStmt
   of mSpawn:
-    result = setMs(n, s)
-    for i in 1 ..< n.len:
-      result.sons[i] = semExpr(c, n.sons[i])
-    let typ = result[^1].typ
-    if not typ.isEmptyType:
-      if spawnResult(typ, c.inParallelStmt > 0) == srFlowVar:
-        result.typ = createFlowVar(c, typ, n.info)
-      else:
-        result.typ = typ
-      result.add instantiateCreateFlowVarCall(c, typ, n.info).newSymNode
+    markUsed(c, n.info, s)
+    when defined(leanCompiler):
+      result = localErrorNode(c, n, "compiler was built without 'spawn' support")
     else:
-      result.add c.graph.emptyNode
+      result = setMs(n, s)
+      for i in 1..<n.len:
+        result[i] = semExpr(c, n[i])
+
+      if n.len > 1 and n[1].kind notin nkCallKinds:
+        return localErrorNode(c, n, n[1].info, "'spawn' takes a call expression; got: " & $n[1])
+
+      let typ = result[^1].typ
+      if not typ.isEmptyType:
+        if spawnResult(typ, c.inParallelStmt > 0) == srFlowVar:
+          result.typ = createFlowVar(c, typ, n.info)
+        else:
+          result.typ = typ
+        result.add instantiateCreateFlowVarCall(c, typ, n.info).newSymNode
+      else:
+        result.add c.graph.emptyNode
   of mProcCall:
+    markUsed(c, n.info, s)
     result = setMs(n, s)
-    result.sons[1] = semExpr(c, n.sons[1])
+    result[1] = semExpr(c, n[1])
     result.typ = n[1].typ
   of mPlugin:
+    markUsed(c, n.info, s)
     # semDirectOp with conditional 'afterCallActions':
     let nOrig = n.copyTree
     #semLazyOpAux(c, n)
@@ -1928,7 +2645,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
     if result == nil:
       result = errorNode(c, n)
     else:
-      let callee = result.sons[0].sym
+      let callee = result[0].sym
       if callee.magic == mNone:
         semFinishOperands(c, result)
       activate(c, result)
@@ -1937,29 +2654,50 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
       if callee.magic != mNone:
         result = magicsAfterOverloadResolution(c, result, flags)
   of mRunnableExamples:
-    if c.config.cmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList:
-      if sfMainModule in c.module.flags:
-        let inp = toFullPath(c.config, c.module.info)
-        if c.runnableExamples == nil:
-          c.runnableExamples = newTree(nkStmtList,
-            newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp))))
-        let imports = newTree(nkStmtList)
-        extractImports(n.lastSon, imports)
-        for imp in imports: c.runnableExamples.add imp
-        c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree n.lastSon)
+    markUsed(c, n.info, s)
+    if c.config.cmd in cmdDocLike and n.len >= 2 and n.lastSon.kind == nkStmtList:
+      when false:
+        # some of this dead code was moved to `prepareExamples`
+        if sfMainModule in c.module.flags:
+          let inp = toFullPath(c.config, c.module.info)
+          if c.runnableExamples == nil:
+            c.runnableExamples = newTree(nkStmtList,
+              newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp))))
+          let imports = newTree(nkStmtList)
+          var savedLastSon = copyTree n.lastSon
+          extractImports(savedLastSon, imports)
+          for imp in imports: c.runnableExamples.add imp
+          c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree savedLastSon)
       result = setMs(n, s)
     else:
       result = c.graph.emptyNode
+  of mSizeOf:
+    markUsed(c, n.info, s)
+    result = semSizeof(c, setMs(n, s))
+  of mArrToSeq, mOpenArrayToSeq:
+    if expectedType != nil and (
+        let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
+        expected.kind in {tySequence, tyOpenArray}):
+      # seq type inference
+      var arrayType = newType(tyOpenArray, c.idgen, expected.owner)
+      arrayType.rawAddSon(expected[0])
+      if n[0].kind == nkSym and sfFromGeneric in n[0].sym.flags:
+        # may have been resolved to `@`[empty] at some point,
+        # reset to `@` to deal with this
+        n[0] = newSymNode(n[0].sym.instantiatedFrom, n[0].info)
+      n[1] = semExpr(c, n[1], flags, arrayType)
+    result = semDirectOp(c, n, flags, expectedType)
   else:
-    result = semDirectOp(c, n, flags)
+    result = semDirectOp(c, n, flags, expectedType)
 
 proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
   # If semCheck is set to false, ``when`` will return the verbatim AST of
   # the correct branch. Otherwise the AST will be passed through semStmt.
   result = nil
+  let flags = if semCheck: {efWantStmt} else: {}
 
   template setResult(e: untyped) =
-    if semCheck: result = semExpr(c, e) # do not open a new scope!
+    if semCheck: result = semExpr(c, e, flags) # do not open a new scope!
     else: result = e
 
   # Check if the node is "when nimvm"
@@ -1969,105 +2707,151 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
   #   ...
   var whenNimvm = false
   var typ = commonTypeBegin
-  if n.sons.len == 2 and n.sons[0].kind == nkElifBranch and
-      n.sons[1].kind == nkElse:
-    let exprNode = n.sons[0].sons[0]
+  if n.len in 1..2 and n[0].kind == nkElifBranch and (
+      n.len == 1 or n[1].kind == nkElse):
+    var exprNode = n[0][0]
+    if exprNode.kind == nkOpenSym:
+      exprNode = exprNode[0]
     if exprNode.kind == nkIdent:
       whenNimvm = lookUp(c, exprNode).magic == mNimvm
     elif exprNode.kind == nkSym:
       whenNimvm = exprNode.sym.magic == mNimvm
     if whenNimvm: n.flags.incl nfLL
 
-  for i in countup(0, sonsLen(n) - 1):
-    var it = n.sons[i]
+  var cannotResolve = false
+  for i in 0..<n.len:
+    var it = n[i]
     case it.kind
     of nkElifBranch, nkElifExpr:
       checkSonsLen(it, 2, c.config)
       if whenNimvm:
         if semCheck:
-          it.sons[1] = semExpr(c, it.sons[1])
-          typ = commonType(typ, it.sons[1].typ)
+          it[1] = semExpr(c, it[1], flags)
+          typ = commonType(c, typ, it[1].typ)
         result = n # when nimvm is not elimited until codegen
+      elif c.inGenericContext > 0:
+        let e = semExprWithType(c, it[0])
+        if e.typ.kind == tyFromExpr:
+          it[0] = makeStaticExpr(c, e)
+          cannotResolve = true
+        else:
+          it[0] = forceBool(c, e)
+          let val = getConstExpr(c.module, it[0], c.idgen, c.graph)
+          if val == nil or val.kind != nkIntLit:
+            cannotResolve = true
+          elif not cannotResolve and val.intVal != 0 and result == nil:
+            setResult(it[1])
+            return # we're not in nimvm and we already have a result
       else:
-        var e = semConstExpr(c, it.sons[0])
+        let e = forceBool(c, semConstExpr(c, it[0]))
         if e.kind != nkIntLit:
           # can happen for cascading errors, assume false
           # InternalError(n.info, "semWhen")
           discard
         elif e.intVal != 0 and result == nil:
-          setResult(it.sons[1])
+          setResult(it[1])
+          return # we're not in nimvm and we already have a result
     of nkElse, nkElseExpr:
       checkSonsLen(it, 1, c.config)
-      if result == nil or whenNimvm:
+      if cannotResolve:
+        discard
+      elif result == nil or whenNimvm:
         if semCheck:
-          it.sons[0] = semExpr(c, it.sons[0])
-          typ = commonType(typ, it.sons[0].typ)
+          it[0] = semExpr(c, it[0], flags)
+          typ = commonType(c, typ, it[0].typ)
+          if typ != nil and typ.kind != tyUntyped:
+            it[0] = fitNode(c, typ, it[0], it[0].info)
         if result == nil:
-          result = it.sons[0]
+          result = it[0]
     else: illFormedAst(n, c.config)
+  if cannotResolve:
+    result = semGenericStmt(c, n)
+    result.typ = makeTypeFromExpr(c, result.copyTree)
+    return
   if result == nil:
     result = newNodeI(nkEmpty, n.info)
-  if whenNimvm: result.typ = typ
-  # The ``when`` statement implements the mechanism for platform dependent
-  # code. Thus we try to ensure here consistent ID allocation after the
-  # ``when`` statement.
-  idSynchronizationPoint(200)
+  if whenNimvm:
+    result.typ = typ
+    if n.len == 1:
+      result.add(newTree(nkElse, newNode(nkStmtList)))
 
-proc semSetConstr(c: PContext, n: PNode): PNode =
+proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
   result = newNodeI(nkCurly, n.info)
   result.typ = newTypeS(tySet, c)
-  if sonsLen(n) == 0:
-    rawAddSon(result.typ, newTypeS(tyEmpty, c))
+  result.typ.flags.incl tfIsConstructor
+  var expectedElementType: PType = nil
+  if expectedType != nil and (
+      let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
+      expected.kind == tySet):
+    expectedElementType = expected[0]
+  if n.len == 0:
+    rawAddSon(result.typ,
+      if expectedElementType != nil and
+          typeAllowed(expectedElementType, skLet, c) == nil:
+        expectedElementType
+      else:
+        newTypeS(tyEmpty, c))
   else:
     # only semantic checking for all elements, later type checking:
     var typ: PType = nil
-    for i in countup(0, sonsLen(n) - 1):
-      if isRange(n.sons[i]):
-        checkSonsLen(n.sons[i], 3, c.config)
-        n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1])
-        n.sons[i].sons[2] = semExprWithType(c, n.sons[i].sons[2])
-        if typ == nil:
-          typ = skipTypes(n.sons[i].sons[1].typ,
+    for i in 0..<n.len:
+      let doSetType = typ == nil
+      if isRange(n[i]):
+        checkSonsLen(n[i], 3, c.config)
+        n[i][1] = semExprWithType(c, n[i][1], {efTypeAllowed}, expectedElementType)
+        n[i][2] = semExprWithType(c, n[i][2], {efTypeAllowed}, expectedElementType)
+        if doSetType:
+          typ = skipTypes(n[i][1].typ,
                           {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
-        n.sons[i].typ = n.sons[i].sons[2].typ # range node needs type too
-      elif n.sons[i].kind == nkRange:
+        n[i].typ = n[i][2].typ # range node needs type too
+      elif n[i].kind == nkRange:
         # already semchecked
-        if typ == nil:
-          typ = skipTypes(n.sons[i].sons[0].typ,
+        if doSetType:
+          typ = skipTypes(n[i][0].typ,
                           {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
       else:
-        n.sons[i] = semExprWithType(c, n.sons[i])
-        if typ == nil:
-          typ = skipTypes(n.sons[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
-    if not isOrdinalType(typ):
-      localError(c.config, n.info, errOrdinalTypeExpected)
-      typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
-    elif lengthOrd(c.config, typ) > MaxSetElements:
-      typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
-    addSonSkipIntLit(result.typ, typ)
-    for i in countup(0, sonsLen(n) - 1):
+        n[i] = semExprWithType(c, n[i], {efTypeAllowed}, expectedElementType)
+        if doSetType:
+          typ = skipTypes(n[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
+      if doSetType:
+        if not isOrdinalType(typ, allowEnumWithHoles=true):
+          localError(c.config, n.info, errOrdinalTypeExpected % typeToString(typ, preferDesc))
+          typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
+        elif isIntLit(typ):
+          # set of int literal, use a default range smaller than the max range
+          typ = makeRangeType(c, 0, DefaultSetElements-1, n.info)
+        elif lengthOrd(c.config, typ) > MaxSetElements:
+          message(c.config, n.info, warnAboveMaxSizeSet, "type '" &
+            typeToString(typ, preferDesc) & "' is too big to be a `set` element, " &
+            "assuming a range of 0.." & $(MaxSetElements - 1) &
+            ", explicitly write this range to get rid of warning")
+          typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
+        if expectedElementType == nil:
+          expectedElementType = typ
+    addSonSkipIntLit(result.typ, typ, c.idgen)
+    for i in 0..<n.len:
       var m: PNode
-      let info = n.sons[i].info
-      if isRange(n.sons[i]):
+      let info = n[i].info
+      if isRange(n[i]):
         m = newNodeI(nkRange, info)
-        addSon(m, fitNode(c, typ, n.sons[i].sons[1], info))
-        addSon(m, fitNode(c, typ, n.sons[i].sons[2], info))
-      elif n.sons[i].kind == nkRange: m = n.sons[i] # already semchecked
+        m.add fitNode(c, typ, n[i][1], info)
+        m.add fitNode(c, typ, n[i][2], info)
+      elif n[i].kind == nkRange: m = n[i] # already semchecked
       else:
-        m = fitNode(c, typ, n.sons[i], info)
-      addSon(result, m)
+        m = fitNode(c, typ, n[i], info)
+      result.add m
 
-proc semTableConstr(c: PContext, n: PNode): PNode =
+proc semTableConstr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
   # we simply transform ``{key: value, key2, key3: value}`` to
   # ``[(key, value), (key2, value2), (key3, value2)]``
   result = newNodeI(nkBracket, n.info)
   var lastKey = 0
-  for i in 0..n.len-1:
-    var x = n.sons[i]
-    if x.kind == nkExprColonExpr and sonsLen(x) == 2:
-      for j in countup(lastKey, i-1):
+  for i in 0..<n.len:
+    var x = n[i]
+    if x.kind == nkExprColonExpr and x.len == 2:
+      for j in lastKey..<i:
         var pair = newNodeI(nkTupleConstr, x.info)
-        pair.add(n.sons[j])
+        pair.add(n[j])
         pair.add(x[1])
         result.add(pair)
 
@@ -2079,94 +2863,118 @@ proc semTableConstr(c: PContext, n: PNode): PNode =
       lastKey = i+1
 
   if lastKey != n.len: illFormedAst(n, c.config)
-  result = semExpr(c, result)
+  result = semExpr(c, result, expectedType = expectedType)
 
 type
   TParKind = enum
     paNone, paSingle, paTupleFields, paTuplePositions
 
 proc checkPar(c: PContext; n: PNode): TParKind =
-  var length = sonsLen(n)
-  if length == 0:
+  if n.len == 0:
     result = paTuplePositions # ()
-  elif length == 1:
-    if n.sons[0].kind == nkExprColonExpr: result = paTupleFields
+  elif n.len == 1:
+    if n[0].kind == nkExprColonExpr: result = paTupleFields
     elif n.kind == nkTupleConstr: result = paTuplePositions
     else: result = paSingle         # (expr)
   else:
-    if n.sons[0].kind == nkExprColonExpr: result = paTupleFields
+    if n[0].kind == nkExprColonExpr: result = paTupleFields
     else: result = paTuplePositions
-    for i in countup(0, length - 1):
+    for i in 0..<n.len:
       if result == paTupleFields:
-        if (n.sons[i].kind != nkExprColonExpr) or
-            not (n.sons[i].sons[0].kind in {nkSym, nkIdent}):
-          localError(c.config, n.sons[i].info, errNamedExprExpected)
+        if (n[i].kind != nkExprColonExpr) or
+            n[i][0].kind notin {nkSym, nkIdent, nkAccQuoted}:
+          localError(c.config, n[i].info, errNamedExprExpected)
           return paNone
       else:
-        if n.sons[i].kind == nkExprColonExpr:
-          localError(c.config, n.sons[i].info, errNamedExprNotAllowed)
+        if n[i].kind == nkExprColonExpr:
+          localError(c.config, n[i].info, errNamedExprNotAllowed)
           return paNone
 
-proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
+proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
   result = newNodeI(nkTupleConstr, n.info)
+  var expected: PType = nil
+  if expectedType != nil:
+    expected = expectedType.skipTypes(abstractRange-{tyDistinct})
+    if not (expected.kind == tyTuple and expected.len == n.len):
+      expected = nil
   var typ = newTypeS(tyTuple, c)
   typ.n = newNodeI(nkRecList, n.info) # nkIdentDefs
   var ids = initIntSet()
-  for i in countup(0, sonsLen(n) - 1):
-    if n[i].kind != nkExprColonExpr or n[i][0].kind notin {nkSym, nkIdent}:
-      illFormedAst(n.sons[i], c.config)
-    var id: PIdent
-    if n.sons[i].sons[0].kind == nkIdent: id = n.sons[i].sons[0].ident
-    else: id = n.sons[i].sons[0].sym.name
+  for i in 0..<n.len:
+    if n[i].kind != nkExprColonExpr:
+      illFormedAst(n[i], c.config)
+    let id = considerQuotedIdent(c, n[i][0])
     if containsOrIncl(ids, id.id):
-      localError(c.config, n.sons[i].info, errFieldInitTwice % id.s)
-    n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1],
-                                        flags*{efAllowDestructor})
-    var f = newSymS(skField, n.sons[i].sons[0], c)
-    f.typ = skipIntLit(n.sons[i].sons[1].typ)
+      localError(c.config, n[i].info, errFieldInitTwice % id.s)
+    # can check if field name matches expected type here
+    let expectedElemType = if expected != nil: expected[i] else: nil
+    n[i][1] = semExprWithType(c, n[i][1], {}, expectedElemType)
+    if expectedElemType != nil and
+        (expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)):
+      # hasEmpty/nil check is to not break existing code like
+      # `const foo = [(1, {}), (2, {false})]`,
+      # `const foo = if true: (0, nil) else: (1, new(int))`
+      n[i][1] = fitNode(c, expectedElemType, n[i][1], n[i][1].info)
+
+    if n[i][1].typ.kind == tyTypeDesc:
+      localError(c.config, n[i][1].info, "typedesc not allowed as tuple field.")
+      n[i][1].typ = errorType(c)
+
+    var f = newSymS(skField, n[i][0], c)
+    f.typ = skipIntLit(n[i][1].typ.skipTypes({tySink}), c.idgen)
     f.position = i
     rawAddSon(typ, f.typ)
-    addSon(typ.n, newSymNode(f))
-    n.sons[i].sons[0] = newSymNode(f)
-    addSon(result, n.sons[i])
+    typ.n.add newSymNode(f)
+    n[i][0] = newSymNode(f)
+    result.add n[i]
   result.typ = typ
 
-proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
+proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
   result = n                  # we don't modify n, but compute the type:
-  result.kind = nkTupleConstr
+  result.transitionSonsKind(nkTupleConstr)
+  var expected: PType = nil
+  if expectedType != nil:
+    expected = expectedType.skipTypes(abstractRange-{tyDistinct})
+    if not (expected.kind == tyTuple and expected.len == n.len):
+      expected = nil
   var typ = newTypeS(tyTuple, c)  # leave typ.n nil!
-  for i in countup(0, sonsLen(n) - 1):
-    n.sons[i] = semExprWithType(c, n.sons[i], flags*{efAllowDestructor})
-    addSonSkipIntLit(typ, n.sons[i].typ)
+  for i in 0..<n.len:
+    let expectedElemType = if expected != nil: expected[i] else: nil
+    n[i] = semExprWithType(c, n[i], {}, expectedElemType)
+    if expectedElemType != nil and
+        (expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)):
+      # hasEmpty/nil check is to not break existing code like
+      # `const foo = [(1, {}), (2, {false})]`,
+      # `const foo = if true: (0, nil) else: (1, new(int))`
+      n[i] = fitNode(c, expectedElemType, n[i], n[i].info)
+    addSonSkipIntLit(typ, n[i].typ.skipTypes({tySink}), c.idgen)
   result.typ = typ
 
-proc isTupleType(n: PNode): bool =
-  if n.len == 0:
-    return false # don't interpret () as type
-  for i in countup(0, n.len - 1):
-    if n[i].typ == nil or n[i].typ.kind != tyTypeDesc:
-      return false
-  return true
-
 include semobjconstr
 
-proc semBlock(c: PContext, n: PNode): PNode =
+proc semBlock(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
   result = n
   inc(c.p.nestedBlockCounter)
+  let oldBreakInLoop = c.p.breakInLoop
+  c.p.breakInLoop = false
   checkSonsLen(n, 2, c.config)
   openScope(c) # BUGFIX: label is in the scope of block!
-  if n.sons[0].kind != nkEmpty:
-    var labl = newSymG(skLabel, n.sons[0], c)
+  if n[0].kind != nkEmpty:
+    var labl = newSymG(skLabel, n[0], c)
     if sfGenSym notin labl.flags:
       addDecl(c, labl)
-    n.sons[0] = newSymNode(labl, n.sons[0].info)
-    suggestSym(c.config, n.sons[0].info, labl, c.graph.usageSym)
-    styleCheckDef(c.config, labl)
-  n.sons[1] = semExpr(c, n.sons[1])
-  n.typ = n.sons[1].typ
-  if isEmptyType(n.typ): n.kind = nkBlockStmt
-  else: n.kind = nkBlockExpr
+    elif labl.owner == nil:
+      labl.owner = c.p.owner
+    n[0] = newSymNode(labl, n[0].info)
+    suggestSym(c.graph, n[0].info, labl, c.graph.usageSym)
+    styleCheckDef(c, labl)
+    onDef(n[0].info, labl)
+  n[1] = semExpr(c, n[1], flags, expectedType)
+  n.typ = n[1].typ
+  if isEmptyType(n.typ): n.transitionSonsKind(nkBlockStmt)
+  else: n.transitionSonsKind(nkBlockExpr)
   closeScope(c)
+  c.p.breakInLoop = oldBreakInLoop
   dec(c.p.nestedBlockCounter)
 
 proc semExportExcept(c: PContext, n: PNode): PNode =
@@ -2176,181 +2984,478 @@ proc semExportExcept(c: PContext, n: PNode): PNode =
     return n
   let exceptSet = readExceptSet(c, n)
   let exported = moduleName.sym
-  strTableAdd(c.module.tab, exported)
-  var i: TTabIter
-  var s = initTabIter(i, exported.tab)
-  while s != nil:
+  result = newNodeI(nkExportStmt, n.info)
+  reexportSym(c, exported)
+  for s in allSyms(c.graph, exported):
     if s.kind in ExportableSymKinds+{skModule} and
-       s.name.id notin exceptSet:
-      strTableAdd(c.module.tab, s)
-    s = nextIter(i, exported.tab)
-  result = n
+       s.name.id notin exceptSet and sfError notin s.flags:
+      reexportSym(c, s)
+      result.add newSymNode(s, n.info)
+  markUsed(c, n.info, exported)
 
 proc semExport(c: PContext, n: PNode): PNode =
-  var x = newNodeI(n.kind, n.info)
+  proc specialSyms(c: PContext; s: PSym) {.inline.} =
+    if s.kind == skConverter: addConverter(c, LazySym(sym: s))
+    elif s.kind == skType and s.typ != nil and s.typ.kind == tyEnum and sfPure in s.flags:
+      addPureEnum(c, LazySym(sym: s))
+
+  result = newNodeI(nkExportStmt, n.info)
   for i in 0..<n.len:
-    let a = n.sons[i]
-    var o: TOverloadIter
+    let a = n[i]
+    var o: TOverloadIter = default(TOverloadIter)
     var s = initOverloadIter(o, c, a)
     if s == nil:
       localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a))
     elif s.kind == skModule:
       # forward everything from that module:
-      strTableAdd(c.module.tab, s)
-      x.add(newSymNode(s, a.info))
-      var ti: TTabIter
-      var it = initTabIter(ti, s.tab)
-      while it != nil:
+      reexportSym(c, s)
+      for it in allSyms(c.graph, s):
         if it.kind in ExportableSymKinds+{skModule}:
-          strTableAdd(c.module.tab, it)
-        it = nextIter(ti, s.tab)
+          reexportSym(c, it)
+          result.add newSymNode(it, a.info)
+          specialSyms(c, it)
+      markUsed(c, n.info, s)
     else:
       while s != nil:
-        if s.kind in ExportableSymKinds+{skModule}:
-          x.add(newSymNode(s, a.info))
-          strTableAdd(c.module.tab, s)
+        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} and sfError notin s.flags:
+          result.add(newSymNode(s, a.info))
+          reexportSym(c, s)
+          markUsed(c, n.info, s)
+          specialSyms(c, s)
+          if s.kind == skType and sfPure notin s.flags:
+            var etyp = s.typ
+            if etyp.kind in {tyBool, tyEnum}:
+              for j in 0..<etyp.n.len:
+                var e = etyp.n[j].sym
+                if e.kind != skEnumField:
+                  internalError(c.config, s.info, "rawImportSymbol")
+                reexportSym(c, e)
+
         s = nextOverloadIter(o, c, a)
-  result = n
 
-proc shouldBeBracketExpr(n: PNode): bool =
+proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
+  var tupexp = semTuplePositionsConstr(c, n, flags, expectedType)
+  var isTupleType: bool = false
+  if tupexp.len > 0: # don't interpret () as type
+    isTupleType = tupexp[0].typ.kind == tyTypeDesc
+    # check if either everything or nothing is tyTypeDesc
+    for i in 1..<tupexp.len:
+      if isTupleType != (tupexp[i].typ.kind == tyTypeDesc):
+        return localErrorNode(c, n, tupexp[i].info, "Mixing types and values in tuples is not allowed.")
+  if isTupleType: # expressions as ``(int, string)`` are reinterpret as type expressions
+    result = n
+    var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
+    result.typ = makeTypeDesc(c, typ)
+  else:
+    result = tupexp
+
+proc isExplicitGenericCall(c: PContext, n: PNode): bool =
+  ## checks if a call node `n` is a routine call with explicit generic params
+  ## 
+  ## the callee node needs to be either an nkBracketExpr or a call to a
+  ## symchoice of `[]` in which case it will be transformed into nkBracketExpr
+  ## 
+  ## the LHS of the bracket expr has to either be a symchoice or resolve to
+  ## a routine symbol
+  template checkCallee(n: PNode) =
+    # check subscript LHS, `n` must be mutable
+    if isSymChoice(n):
+      result = true
+    else:
+      let s = qualifiedLookUp(c, n, {})
+      if s != nil and s.kind in routineKinds:
+        result = true
+        n = semSymGenericInstantiation(c, n, s)
   assert n.kind in nkCallKinds
-  let a = n.sons[0]
-  if a.kind in nkCallKinds:
+  result = false
+  let a = n[0]
+  case a.kind
+  of nkBracketExpr:
+    checkCallee(a[0])
+  of nkCallKinds:
     let b = a[0]
     if b.kind in nkSymChoices:
+      let name = b.getPIdent
+      if name != nil and name.s == "[]":
+        checkCallee(a[1])
+        if result:
+          # transform callee into normal bracket expr, only on success
+          let be = newNodeI(nkBracketExpr, a.info)
+          for i in 1..<a.len: be.add(a[i])
+          n[0] = be
+  else:
+    result = false
+
+proc asBracketExpr(c: PContext; n: PNode): PNode =
+  proc isGeneric(c: PContext; n: PNode): bool =
+    if n.kind in {nkIdent, nkAccQuoted}:
+      let s = qualifiedLookUp(c, n, {})
+      result = s != nil and isGenericRoutineStrict(s)
+    else:
+      result = false
+
+  assert n.kind in nkCallKinds
+  if n.len > 1 and isGeneric(c, n[1]):
+    let b = n[0]
+    if b.kind in nkSymChoices:
       for i in 0..<b.len:
         if b[i].kind == nkSym and b[i].sym.magic == mArrGet:
-          let be = newNodeI(nkBracketExpr, n.info)
-          for i in 1..<a.len: be.add(a[i])
-          n.sons[0] = be
-          return true
+          result = newNodeI(nkBracketExpr, n.info)
+          for i in 1..<n.len: result.add(n[i])
+          return result
+  return nil
+
+proc isOpenArraySym(x: PNode): bool =
+  var x = x
+  while true:
+    case x.kind
+    of {nkAddr, nkHiddenAddr}:
+      x = x[0]
+    of {nkHiddenStdConv, nkHiddenDeref}:
+      x = x[1]
+    else:
+      break
+  result = x.kind == nkSym
+
+proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode) =
+  # This takes care of complicated signatures such as:
+  # proc foo(a: int, b = a)
+  # proc bar(a: int, b: int, c = a + b)
+  #
+  # The recursion may confuse you. It performs two duties:
+  #
+  # 1) extracting all referenced params from default expressions
+  #    into a let section preceding the call
+  #
+  # 2) replacing the "references" within the default expression
+  #    with these extracted skLet symbols.
+  #
+  # The first duty is carried out directly in the code here, while the second
+  # duty is activated by returning a non-nil value. The caller is responsible
+  # for replacing the input to the function with the returned non-nil value.
+  # (which is the hoisted symbol)
+  if defExpr.kind == nkSym and defExpr.sym.kind == skParam and
+      (defExpr.sym.owner == call[0].sym or
+        # symbol was resolved before proc was instantiated:
+        (sfFromGeneric in call[0].sym.flags and
+          defExpr.sym.owner == call[0].sym.instantiatedFrom)):
+    let paramPos = defExpr.sym.position + 1
+
+    if call[paramPos].skipAddr.kind != nkSym and not (
+      skipTypes(call[paramPos].typ, abstractVar).kind in {tyOpenArray, tyVarargs} and
+      isOpenArraySym(call[paramPos])
+    ):
+      let hoistedVarSym = newSym(skLet, getIdent(c.graph.cache, genPrefix), c.idgen,
+                                 c.p.owner, letSection.info, c.p.owner.options)
+      hoistedVarSym.typ = call[paramPos].typ
+
+      letSection.add newTreeI(nkIdentDefs, letSection.info,
+        newSymNode(hoistedVarSym),
+        newNodeI(nkEmpty, letSection.info),
+        call[paramPos])
+
+      call[paramPos] = newSymNode(hoistedVarSym) # Refer the original arg to its hoisted sym
+
+    # arg we refer to is a sym, whether introduced by hoisting or not doesn't matter, we simply reuse it
+    defExpr = call[paramPos]
+  else:
+    for i in 0..<defExpr.safeLen:
+      hoistParamsUsedInDefault(c, call, letSection, defExpr[i])
+
+proc getNilType(c: PContext): PType =
+  result = c.nilTypeCache
+  if result == nil:
+    result = newTypeS(tyNil, c)
+    result.size = c.config.target.ptrSize
+    result.align = c.config.target.ptrSize.int16
+    c.nilTypeCache = result
+
+proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym; flags: TExprFlags): PNode =
+  var o: TOverloadIter = default(TOverloadIter)
+  var i = 0
+  var a = initOverloadIter(o, c, n)
+  while a != nil:
+    if a.kind == skEnumField:
+      inc(i)
+      if i > 1: break
+    a = nextOverloadIter(o, c, n)
+  let info = getCallLineInfo(n)
+  if i <= 1:
+    if sfGenSym notin s.flags:
+      result = newSymNode(s, info)
+      markUsed(c, info, s, efInCall notin flags)
+      onUse(info, s)
+    else:
+      result = n
+  else:
+    result = newNodeIT(nkClosedSymChoice, info, newTypeS(tyNone, c))
+    a = initOverloadIter(o, c, n)
+    while a != nil:
+      if a.kind == skEnumField:
+        incl(a.flags, sfUsed)
+        markOwnerModuleAsUsed(c, a)
+        result.add newSymNode(a, info)
+        onUse(info, a)
+      a = nextOverloadIter(o, c, n)
+
+proc semPragmaStmt(c: PContext; n: PNode) =
+  if c.p.owner.kind == skModule:
+    pragma(c, c.p.owner, n, stmtPragmas+stmtPragmasTopLevel, true)
+  else:
+    pragma(c, c.p.owner, n, stmtPragmas, true)
+
+proc resolveIdentToSym(c: PContext, n: PNode, resultNode: var PNode,
+                       flags: TExprFlags, expectedType: PType): PSym =
+  # result is nil on error or if a node that can't produce a sym is resolved
+  let ident = considerQuotedIdent(c, n)
+  var filter = {low(TSymKind)..high(TSymKind)}
+  if efNoEvaluateGeneric in flags or expectedType != nil:
+    # `a[...]` where `a` is a module or package is not possible
+    filter.excl {skModule, skPackage}
+  let includePureEnum = expectedType != nil and
+    expectedType.skipTypes(abstractRange-{tyDistinct}).kind == tyEnum
+  let candidates = lookUpCandidates(c, ident, filter,
+    includePureEnum = includePureEnum)
+  if candidates.len == 0:
+    result = errorUndeclaredIdentifierHint(c, ident, n.info)
+  elif candidates.len == 1 or {efNoEvaluateGeneric, efInCall} * flags != {}:
+    # unambiguous, or we don't care about ambiguity
+    result = candidates[0]
+  else:
+    # ambiguous symbols have 1 last chance as a symchoice
+    var choice = newNodeIT(nkClosedSymChoice, n.info, newTypeS(tyNone, c))
+    for cand in candidates:
+      case cand.kind
+      of skModule, skPackage:
+        discard
+      of skType:
+        choice.add newSymNodeTypeDesc(cand, c.idgen, n.info)
+      else:
+        choice.add newSymNode(cand, n.info)
+    if choice.len == 0:
+      # we know candidates.len > 1, we just couldn't put any in a symchoice
+      errorUseQualifier(c, n.info, candidates)
+      return nil
+    resolveSymChoice(c, choice, flags, expectedType)
+    # choice.len == 1 can be true here but as long as it's a symchoice
+    # it's still not resolved
+    if isSymChoice(choice):
+      result = nil
+      if efAllowSymChoice in flags:
+        resultNode = choice
+      else:
+        errorUseQualifier(c, n.info, candidates)
+    else:
+      if choice.kind == nkSym:
+        result = choice.sym
+      else:
+        # resolution could have generated nkHiddenStdConv etc
+        resultNode = semExpr(c, choice, flags, expectedType)
+        result = nil
+
+proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
+  when defined(nimCompilerStacktraceHints):
+    setFrameMsg c.config$n.info & " " & $n.kind
+  when false: # see `tdebugutils`
+    if isCompilerDebug():
+      echo (">", c.config$n.info, n, flags, n.kind)
+    defer:
+      if isCompilerDebug():
+        echo ("<", c.config$n.info, n, ?.result.typ)
+  template directLiteral(typeKind: TTypeKind) =
+    if result.typ == nil:
+      if expectedType != nil and (
+          let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
+          expected.kind == typeKind):
+        result.typ = expected
+        changeType(c, result, expectedType, check=true)
+      else:
+        result.typ = getSysType(c.graph, n.info, typeKind)
 
-proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   result = n
+  when defined(nimsuggest):
+    var expandStarted = false
+    if c.config.ideCmd == ideExpand and not c.config.expandProgress and
+        ((n.kind in {nkFuncDef, nkProcDef, nkIteratorDef, nkTemplateDef, nkMethodDef, nkConverterDef} and
+          n.info.exactEquals(c.config.expandPosition)) or
+         (n.kind in {nkCall, nkCommand} and
+          n[0].info.exactEquals(c.config.expandPosition))):
+      expandStarted = true
+      c.config.expandProgress = true
+      if c.config.expandLevels == 0:
+        c.config.expandNodeResult = $n
+        suggestQuit()
+
   if c.config.cmd == cmdIdeTools: suggestExpr(c, n)
   if nfSem in n.flags: return
   case n.kind
   of nkIdent, nkAccQuoted:
-    let checks = if efNoEvaluateGeneric in flags:
-        {checkUndeclared, checkPureEnumFields}
-      elif efInCall in flags:
-        {checkUndeclared, checkModule, checkPureEnumFields}
-      else:
-        {checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields}
-    var s = qualifiedLookUp(c, n, checks)
+    let s = resolveIdentToSym(c, n, result, flags, expectedType)
+    if s == nil:
+      # resolveIdentToSym either errored or gave a result node
+      return
     if c.matchedConcept == nil: semCaptureSym(s, c.p.owner)
-    result = semSym(c, n, s, flags)
-    if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator}:
+    case s.kind
+    of skProc, skFunc, skMethod, skConverter, skIterator:
       #performProcvarCheck(c, n, s)
       result = symChoice(c, n, s, scClosed)
       if result.kind == nkSym:
         markIndirect(c, result.sym)
         # if isGenericRoutine(result.sym):
         #   localError(c.config, n.info, errInstantiateXExplicitly, s.name.s)
+      # "procs literals" are 'owned'
+      if optOwnedRefs in c.config.globalOptions:
+        result.typ = makeVarType(c, result.typ, tyOwned)
+    of skEnumField:
+      result = enumFieldSymChoice(c, n, s, flags)
+    else:
+      result = semSym(c, n, s, flags)
+    if isSymChoice(result):
+      result = semSymChoice(c, result, flags, expectedType)
+  of nkClosedSymChoice, nkOpenSymChoice:
+    result = semSymChoice(c, n, flags, expectedType)
   of nkSym:
+    let s = n.sym
+    if nfDisabledOpenSym in n.flags:
+      let override = genericsOpenSym in c.features
+      let res = semOpenSym(c, n, flags, expectedType,
+        warnDisabled = not override)
+      if res != nil:
+        assert override
+        return res
     # because of the changed symbol binding, this does not mean that we
     # don't have to check the symbol for semantics here again!
-    result = semSym(c, n, n.sym, flags)
+    result = semSym(c, n, s, flags)
+  of nkOpenSym:
+    assert n.len == 1
+    let inner = n[0]
+    result = semOpenSym(c, inner, flags, expectedType)
   of nkEmpty, nkNone, nkCommentStmt, nkType:
     discard
   of nkNilLit:
-    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyNil)
+    if result.typ == nil:
+      result.typ = getNilType(c)
+      if expectedType != nil and expectedType.kind notin {tyUntyped, tyTyped}:
+        var m = newCandidate(c, result.typ)
+        if typeRel(m, expectedType, result.typ) >= isSubtype:
+          result.typ = expectedType
+        # or: result = fitNode(c, expectedType, result, n.info)
   of nkIntLit:
-    if result.typ == nil: setIntLitType(c.graph, result)
-  of nkInt8Lit:
-    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt8)
-  of nkInt16Lit:
-    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt16)
-  of nkInt32Lit:
-    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt32)
-  of nkInt64Lit:
-    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt64)
-  of nkUIntLit:
-    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt)
-  of nkUInt8Lit:
-    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt8)
-  of nkUInt16Lit:
-    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt16)
-  of nkUInt32Lit:
-    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt32)
-  of nkUInt64Lit:
-    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyUInt64)
-  #of nkFloatLit:
-  #  if result.typ == nil: result.typ = getFloatLitType(result)
-  of nkFloat32Lit:
-    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyFloat32)
-  of nkFloat64Lit, nkFloatLit:
-    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyFloat64)
-  of nkFloat128Lit:
-    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyFloat128)
+    if result.typ == nil:
+      if expectedType != nil and (
+          let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
+          expected.kind in {tyInt..tyInt64,
+            tyUInt..tyUInt64,
+            tyFloat..tyFloat128}):
+        if expected.kind in {tyFloat..tyFloat128}:
+          n.transitionIntToFloatKind(nkFloatLit)
+        changeType(c, result, expectedType, check=true)
+      else:
+        setIntLitType(c, result)
+  of nkInt8Lit: directLiteral(tyInt8)
+  of nkInt16Lit: directLiteral(tyInt16)
+  of nkInt32Lit: directLiteral(tyInt32)
+  of nkInt64Lit: directLiteral(tyInt64)
+  of nkUIntLit: directLiteral(tyUInt)
+  of nkUInt8Lit: directLiteral(tyUInt8)
+  of nkUInt16Lit: directLiteral(tyUInt16)
+  of nkUInt32Lit: directLiteral(tyUInt32)
+  of nkUInt64Lit: directLiteral(tyUInt64)
+  of nkFloatLit:
+    if result.typ == nil:
+      if expectedType != nil and (
+          let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
+          expected.kind in {tyFloat..tyFloat128}):
+        result.typ = expected
+        changeType(c, result, expectedType, check=true)
+      else:
+        result.typ = getSysType(c.graph, n.info, tyFloat64)
+  of nkFloat32Lit: directLiteral(tyFloat32)
+  of nkFloat64Lit: directLiteral(tyFloat64)
+  of nkFloat128Lit: directLiteral(tyFloat128)
   of nkStrLit..nkTripleStrLit:
-    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyString)
-  of nkCharLit:
-    if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyChar)
+    if result.typ == nil:
+      if expectedType != nil and (
+          let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
+          expected.kind in {tyString, tyCstring}):
+        result.typ = expectedType
+      else:
+        result.typ = getSysType(c.graph, n.info, tyString)
+  of nkCharLit: directLiteral(tyChar)
   of nkDotExpr:
     result = semFieldAccess(c, n, flags)
     if result.kind == nkDotCall:
-      result.kind = nkCall
-      result = semExpr(c, result, flags)
+      result.transitionSonsKind(nkCall)
+      result = semExpr(c, result, flags, expectedType)
   of nkBind:
-    message(c.config, n.info, warnDeprecated, "bind")
-    result = semExpr(c, n.sons[0], flags)
-  of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy:
+    message(c.config, n.info, warnDeprecated, "bind is deprecated")
+    result = semExpr(c, n[0], flags, expectedType)
+  of nkTypeOfExpr..nkTupleClassTy, nkStaticTy, nkRefTy..nkEnumTy:
     if c.matchedConcept != nil and n.len == 1:
       let modifier = n.modifierTypeKindOfNode
       if modifier != tyNone:
         var baseType = semExpr(c, n[0]).typ.skipTypes({tyTypeDesc})
-        result.typ = c.makeTypeDesc(c.newTypeWithSons(modifier, @[baseType]))
+        result.typ = c.makeTypeDesc(newTypeS(modifier, c, baseType))
         return
     var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
     result.typ = makeTypeDesc(c, typ)
+  of nkStmtListType:
+    let typ = semTypeNode(c, n, nil)
+    result.typ = makeTypeDesc(c, typ)
   of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
     # check if it is an expression macro:
     checkMinSonsLen(n, 1, c.config)
     #when defined(nimsuggest):
     #  if gIdeCmd == ideCon and c.config.m.trackPos == n.info: suggestExprNoCheck(c, n)
     let mode = if nfDotField in n.flags: {} else: {checkUndeclared}
-    var s = qualifiedLookUp(c, n.sons[0], mode)
+    c.isAmbiguous = false
+    var s = qualifiedLookUp(c, n[0], mode)
     if s != nil:
-      #if c.config.cmd == cmdPretty and n.sons[0].kind == nkDotExpr:
-      #  pretty.checkUse(n.sons[0].sons[1].info, s)
       case s.kind
-      of skMacro:
-        if sfImmediate notin s.flags:
-          result = semDirectOp(c, n, flags)
-        else:
-          result = semMacroExpr(c, n, n, s, flags)
-      of skTemplate:
-        if sfImmediate notin s.flags:
-          result = semDirectOp(c, n, flags)
-        else:
-          result = semTemplateExpr(c, n, s, flags)
+      of skMacro, skTemplate:
+        result = semDirectOp(c, n, flags, expectedType)
       of skType:
         # XXX think about this more (``set`` procs)
-        if n.len == 2:
-          result = semConv(c, n)
-        elif contains(c.ambiguousSymbols, s.id) and n.len == 1:
-          errorUseQualifier(c, n.info, s)
+        let ambig = c.isAmbiguous
+        if not (n[0].kind in nkSymChoices + {nkIdent, nkDotExpr} and ambig) and n.len == 2:
+          result = semConv(c, n, flags, expectedType)
         elif n.len == 1:
-          result = semObjConstr(c, n, flags)
-        elif s.magic == mNone: result = semDirectOp(c, n, flags)
-        else: result = semMagic(c, n, s, flags)
+          if ambig:
+            errorUseQualifier(c, n.info, s)
+          else:
+            result = semObjConstr(c, n, flags, expectedType)
+        elif s.magic == mNone: result = semDirectOp(c, n, flags, expectedType)
+        else: result = semMagic(c, n, s, flags, expectedType)
       of skProc, skFunc, skMethod, skConverter, skIterator:
-        if s.magic == mNone: result = semDirectOp(c, n, flags)
-        else: result = semMagic(c, n, s, flags)
+        if s.magic == mNone: result = semDirectOp(c, n, flags, expectedType)
+        else: result = semMagic(c, n, s, flags, expectedType)
       else:
         #liMessage(n.info, warnUser, renderTree(n));
-        result = semIndirectOp(c, n, flags)
-    elif (n[0].kind == nkBracketExpr or shouldBeBracketExpr(n)) and
-        isSymChoice(n[0][0]):
-      # indirectOp can deal with explicit instantiations; the fixes
-      # the 'newSeq[T](x)' bug
-      setGenericParams(c, n.sons[0])
-      result = semDirectOp(c, n, flags)
-    elif isSymChoice(n.sons[0]) or nfDotField in n.flags:
-      result = semDirectOp(c, n, flags)
+        result = semIndirectOp(c, n, flags, expectedType)
+    elif isExplicitGenericCall(c, n): # this modifies `n` if true
+      result = semDirectOp(c, n, flags, expectedType)
+    elif nfDotField in n.flags:
+      result = semDirectOp(c, n, flags, expectedType)
+    elif isSymChoice(n[0]):
+      let b = asBracketExpr(c, n)
+      if b != nil:
+        result = semExpr(c, b, flags, expectedType)
+      else:
+        result = semDirectOp(c, n, flags, expectedType)
     else:
-      result = semIndirectOp(c, n, flags)
+      result = semIndirectOp(c, n, flags, expectedType)
+
+    if nfDefaultRefsParam in result.flags:
+      result = result.copyTree #XXX: Figure out what causes default param nodes to be shared.. (sigmatch bug?)
+      # We've found a default value that references another param.
+      # See the notes in `hoistParamsUsedInDefault` for more details.
+      var hoistedParams = newNodeI(nkLetSection, result.info)
+      for i in 1..<result.len:
+        hoistParamsUsedInDefault(c, result, hoistedParams, result[i])
+      result = newTreeIT(nkStmtListExpr, result.info, result.typ, hoistedParams, result)
   of nkWhen:
     if efWantStmt in flags:
       result = semWhen(c, n, true)
@@ -2360,54 +3465,51 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
         # This is a "when nimvm" stmt.
         result = semWhen(c, n, true)
       else:
-        result = semExpr(c, result, flags)
+        result = semExpr(c, result, flags, expectedType)
   of nkBracketExpr:
     checkMinSonsLen(n, 1, c.config)
-    result = semArrayAccess(c, n, flags)
+    result = semArrayAccess(c, n, flags, expectedType)
   of nkCurlyExpr:
-    result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "{}")), flags)
+    result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "{}")), flags, expectedType)
   of nkPragmaExpr:
     var
-      expr = n[0]
       pragma = n[1]
       pragmaName = considerQuotedIdent(c, pragma[0])
       flags = flags
+      finalNodeFlags: TNodeFlags = {}
 
     case whichKeyword(pragmaName)
     of wExplain:
       flags.incl efExplain
+    of wExecuteOnReload:
+      finalNodeFlags.incl nfExecuteOnReload
     else:
       # what other pragmas are allowed for expressions? `likely`, `unlikely`
       invalidPragma(c, n)
 
     result = semExpr(c, n[0], flags)
+    result.flags.incl finalNodeFlags
   of nkPar, nkTupleConstr:
     case checkPar(c, n)
     of paNone: result = errorNode(c, n)
-    of paTuplePositions:
-      var tupexp = semTuplePositionsConstr(c, n, flags)
-      if isTupleType(tupexp):
-        # reinterpret as type
-        var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
-        result.typ = makeTypeDesc(c, typ)
-      else:
-        result = tupexp
-    of paTupleFields: result = semTupleFieldsConstr(c, n, flags)
-    of paSingle: result = semExpr(c, n.sons[0], flags)
-  of nkCurly: result = semSetConstr(c, n)
-  of nkBracket: result = semArrayConstr(c, n, flags)
-  of nkObjConstr: result = semObjConstr(c, n, flags)
-  of nkLambdaKinds: result = semLambda(c, n, flags)
-  of nkDerefExpr: result = semDeref(c, n)
+    of paTuplePositions: result = semTupleConstr(c, n, flags, expectedType)
+    of paTupleFields: result = semTupleFieldsConstr(c, n, flags, expectedType)
+    of paSingle: result = semExpr(c, n[0], flags, expectedType)
+  of nkCurly: result = semSetConstr(c, n, expectedType)
+  of nkBracket:
+    result = semArrayConstr(c, n, flags, expectedType)
+  of nkObjConstr: result = semObjConstr(c, n, flags, expectedType)
+  of nkLambdaKinds: result = semProcAux(c, n, skProc, lambdaPragmas, flags)
+  of nkDerefExpr: result = semDeref(c, n, flags)
   of nkAddr:
     result = n
     checkSonsLen(n, 1, c.config)
-    result = semAddr(c, n.sons[0])
+    result = semAddr(c, n[0])
   of nkHiddenAddr, nkHiddenDeref:
     checkSonsLen(n, 1, c.config)
-    n.sons[0] = semExpr(c, n.sons[0], flags)
+    n[0] = semExpr(c, n[0], flags, expectedType)
   of nkCast: result = semCast(c, n)
-  of nkIfExpr, nkIfStmt: result = semIf(c, n)
+  of nkIfExpr, nkIfStmt: result = semIf(c, n, flags, expectedType)
   of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv:
     checkSonsLen(n, 2, c.config)
     considerGenSyms(c, n)
@@ -2421,32 +3523,27 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     checkMinSonsLen(n, 2, c.config)
     considerGenSyms(c, n)
   of nkTableConstr:
-    result = semTableConstr(c, n)
-  of nkClosedSymChoice, nkOpenSymChoice:
-    # handling of sym choices is context dependent
-    # the node is left intact for now
-    discard
-  of nkStaticExpr:
-    result = semStaticExpr(c, n)
-  of nkAsgn: result = semAsgn(c, n)
-  of nkBlockStmt, nkBlockExpr: result = semBlock(c, n)
-  of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags)
+    result = semTableConstr(c, n, expectedType)
+  of nkStaticExpr: result = semStaticExpr(c, n[0], expectedType)
+  of nkAsgn, nkFastAsgn: result = semAsgn(c, n)
+  of nkBlockStmt, nkBlockExpr: result = semBlock(c, n, flags, expectedType)
+  of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags, expectedType)
   of nkRaiseStmt: result = semRaise(c, n)
   of nkVarSection: result = semVarOrLet(c, n, skVar)
   of nkLetSection: result = semVarOrLet(c, n, skLet)
   of nkConstSection: result = semConst(c, n)
   of nkTypeSection: result = semTypeSection(c, n)
   of nkDiscardStmt: result = semDiscard(c, n)
-  of nkWhileStmt: result = semWhile(c, n)
-  of nkTryStmt: result = semTry(c, n)
+  of nkWhileStmt: result = semWhile(c, n, flags)
+  of nkTryStmt, nkHiddenTryStmt: result = semTry(c, n, flags, expectedType)
   of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n)
-  of nkForStmt, nkParForStmt: result = semFor(c, n)
-  of nkCaseStmt: result = semCase(c, n)
+  of nkForStmt, nkParForStmt: result = semFor(c, n, flags)
+  of nkCaseStmt: result = semCase(c, n, flags, expectedType)
   of nkReturnStmt: result = semReturn(c, n)
   of nkUsingStmt: result = semUsing(c, n)
   of nkAsmStmt: result = semAsm(c, n)
   of nkYieldStmt: result = semYield(c, n)
-  of nkPragma: pragma(c, c.p.owner, n, stmtPragmas)
+  of nkPragma: semPragmaStmt(c, n)
   of nkIteratorDef: result = semIterator(c, n)
   of nkProcDef: result = semProc(c, n)
   of nkFuncDef: result = semFunc(c, n)
@@ -2480,20 +3577,37 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "export")
     result = semExportExcept(c, n)
   of nkPragmaBlock:
-    result = semPragmaBlock(c, n)
+    result = semPragmaBlock(c, n, expectedType)
   of nkStaticStmt:
     result = semStaticStmt(c, n)
   of nkDefer:
-    n.sons[0] = semExpr(c, n.sons[0])
-    if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]):
+    if c.currentScope == c.topLevelScope:
+      localError(c.config, n.info, "defer statement not supported at top level")
+    openScope(c)
+    n[0] = semExpr(c, n[0])
+    closeScope(c)
+    if not n[0].typ.isEmptyType and not implicitlyDiscardable(n[0]):
       localError(c.config, n.info, "'defer' takes a 'void' expression")
     #localError(c.config, n.info, errGenerated, "'defer' not allowed in this context")
   of nkGotoState, nkState:
     if n.len != 1 and n.len != 2: illFormedAst(n, c.config)
-    for i in 0 ..< n.len:
-      n.sons[i] = semExpr(c, n.sons[i])
+    for i in 0..<n.len:
+      n[i] = semExpr(c, n[i])
   of nkComesFrom: discard "ignore the comes from information for now"
+  of nkMixinStmt: discard
+  of nkBindStmt:
+    if c.p != nil:
+      if n.len > 0 and n[0].kind == nkSym:
+        c.p.localBindStmts.add n
+    else:
+      localError(c.config, n.info, "invalid context for 'bind' statement: " &
+                renderTree(n, {renderNoComments}))
   else:
     localError(c.config, n.info, "invalid expression: " &
                renderTree(n, {renderNoComments}))
   if result != nil: incl(result.flags, nfSem)
+
+  when defined(nimsuggest):
+    if expandStarted:
+      c.config.expandNodeResult = $result
+      suggestQuit()
diff --git a/compiler/semfields.nim b/compiler/semfields.nim
index 869f5ae74..874055cdc 100644
--- a/compiler/semfields.nim
+++ b/compiler/semfields.nim
@@ -19,24 +19,26 @@ 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 nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = copyNode(n)
   of nkIdent, nkSym:
     result = n
     let ident = considerQuotedIdent(c.c, n)
-    var L = sonsLen(forLoop)
     if c.replaceByFieldName:
       if ident.id == considerQuotedIdent(c.c, forLoop[0]).id:
         let fieldName = if c.tupleType.isNil: c.field.name.s
                         elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
-                        else: c.tupleType.n.sons[c.tupleIndex].sym.name.s
+                        else: c.tupleType.n[c.tupleIndex].sym.name.s
         result = newStrNode(nkStrLit, fieldName)
         return
     # other fields:
-    for i in ord(c.replaceByFieldName)..L-3:
+    for i in ord(c.replaceByFieldName)..<forLoop.len-2:
       if ident.id == considerQuotedIdent(c.c, forLoop[i]).id:
-        var call = forLoop.sons[L-2]
-        var tupl = call.sons[i+1-ord(c.replaceByFieldName)]
+        var call = forLoop[^2]
+        var tupl = call[i+1-ord(c.replaceByFieldName)]
         if c.field.isNil:
           result = newNodeI(nkBracketExpr, n.info)
           result.add(tupl)
@@ -50,10 +52,9 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
     if n.kind == nkContinueStmt:
       localError(c.c.config, n.info,
                  "'continue' not supported in a 'fields' loop")
-    result = copyNode(n)
-    newSons(result, sonsLen(n))
-    for i in countup(0, sonsLen(n)-1):
-      result.sons[i] = instFieldLoopBody(c, n.sons[i], forLoop)
+    result = shallowCopy(n)
+    for i in 0..<n.len:
+      result[i] = instFieldLoopBody(c, n[i], forLoop)
 
 type
   TFieldsCtx = object
@@ -63,20 +64,21 @@ type
 proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
   case typ.kind
   of nkSym:
-    var fc: TFieldInstCtx  # either 'tup[i]' or 'field' is valid
-    fc.c = c.c
-    fc.field = typ.sym
-    fc.replaceByFieldName = c.m == mFieldPairs
+    # either 'tup[i]' or 'field' is valid
+    var fc = TFieldInstCtx(
+      c: c.c,
+      field: typ.sym,
+      replaceByFieldName: c.m == mFieldPairs
+    )
     openScope(c.c)
     inc c.c.inUnrolledContext
     let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
-    father.add(semStmt(c.c, body))
+    father.add(semStmt(c.c, body, {}))
     dec c.c.inUnrolledContext
     closeScope(c.c)
   of nkNilLit: discard
   of nkRecCase:
-    let L = forLoop.len
-    let call = forLoop.sons[L-2]
+    let call = forLoop[^2]
     if call.len > 2:
       localError(c.c.config, forLoop.info,
                  "parallel 'fields' iterator does not work for 'case' objects")
@@ -87,15 +89,14 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
     var caseStmt = newNodeI(nkCaseStmt, forLoop.info)
     # generate selector:
     var access = newNodeI(nkDotExpr, forLoop.info, 2)
-    access.sons[0] = call.sons[1]
-    access.sons[1] = newSymNode(typ.sons[0].sym, forLoop.info)
+    access[0] = call[1]
+    access[1] = newSymNode(typ[0].sym, forLoop.info)
     caseStmt.add(semExprWithType(c.c, access))
     # copy the branches over, but replace the fields with the for loop body:
-    for i in 1 ..< typ.len:
+    for i in 1..<typ.len:
       var branch = copyTree(typ[i])
-      let L = branch.len
-      branch.sons[L-1] = newNodeI(nkStmtList, forLoop.info)
-      semForObjectFields(c, typ[i].lastSon, forLoop, branch[L-1])
+      branch[^1] = newNodeI(nkStmtList, forLoop.info)
+      semForObjectFields(c, typ[i].lastSon, forLoop, branch[^1])
       caseStmt.add(branch)
     father.add(caseStmt)
   of nkRecList:
@@ -107,56 +108,58 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
   # so that 'break' etc. work as expected, we produce
   # a 'while true: stmt; break' loop ...
   result = newNodeI(nkWhileStmt, n.info, 2)
-  var trueSymbol = strTableGet(c.graph.systemModule.tab, getIdent(c.cache, "true"))
+  var trueSymbol = systemModuleSym(c.graph, getIdent(c.cache, "true"))
   if trueSymbol == nil:
     localError(c.config, n.info, "system needs: 'true'")
-    trueSymbol = newSym(skUnknown, getIdent(c.cache, "true"), getCurrOwner(c), n.info)
+    trueSymbol = newSym(skUnknown, getIdent(c.cache, "true"), c.idgen, getCurrOwner(c), n.info)
     trueSymbol.typ = getSysType(c.graph, n.info, tyBool)
 
-  result.sons[0] = newSymNode(trueSymbol, n.info)
+  result[0] = newSymNode(trueSymbol, n.info)
   var stmts = newNodeI(nkStmtList, n.info)
-  result.sons[1] = stmts
+  result[1] = stmts
 
-  var length = sonsLen(n)
-  var call = n.sons[length-2]
-  if length-2 != sonsLen(call)-1 + ord(m==mFieldPairs):
+  var call = n[^2]
+  if n.len-2 != call.len-1 + ord(m==mFieldPairs):
     localError(c.config, n.info, errWrongNumberOfVariables)
     return result
 
   const skippedTypesForFields = abstractVar - {tyTypeDesc} + tyUserTypeClasses
-  var tupleTypeA = skipTypes(call.sons[1].typ, skippedTypesForFields)
+  var tupleTypeA = skipTypes(call[1].typ, skippedTypesForFields)
   if tupleTypeA.kind notin {tyTuple, tyObject}:
     localError(c.config, n.info, errGenerated, "no object or tuple type")
     return result
-  for i in 1..call.len-1:
-    var tupleTypeB = skipTypes(call.sons[i].typ, skippedTypesForFields)
+  for i in 1..<call.len:
+    let calli = call[i]
+    var tupleTypeB = skipTypes(calli.typ, skippedTypesForFields)
     if not sameType(tupleTypeA, tupleTypeB):
-      typeMismatch(c.config, call.sons[i].info, tupleTypeA, tupleTypeB)
+      typeMismatch(c.config, calli.info, tupleTypeA, tupleTypeB, calli)
 
   inc(c.p.nestedLoopCounter)
+  let oldBreakInLoop = c.p.breakInLoop
+  c.p.breakInLoop = true
   if tupleTypeA.kind == tyTuple:
-    var loopBody = n.sons[length-1]
-    for i in 0..sonsLen(tupleTypeA)-1:
+    var loopBody = n[^1]
+    for i in 0..<tupleTypeA.len:
       openScope(c)
-      var fc: TFieldInstCtx
-      fc.tupleType = tupleTypeA
-      fc.tupleIndex = i
-      fc.c = c
-      fc.replaceByFieldName = m == mFieldPairs
+      var fc = TFieldInstCtx(
+          tupleType: tupleTypeA,
+          tupleIndex: i,
+          c: c,
+          replaceByFieldName: m == mFieldPairs
+      )
       var body = instFieldLoopBody(fc, loopBody, n)
       inc c.inUnrolledContext
-      stmts.add(semStmt(c, body))
+      stmts.add(semStmt(c, body, {}))
       dec c.inUnrolledContext
       closeScope(c)
   else:
-    var fc: TFieldsCtx
-    fc.m = m
-    fc.c = c
+    var fc = TFieldsCtx(m: m, c: c)
     var t = tupleTypeA
     while t.kind == tyObject:
       semForObjectFields(fc, t.n, n, stmts)
-      if t.sons[0] == nil: break
-      t = skipTypes(t.sons[0], skipPtrs)
+      if t.baseClass == nil: break
+      t = skipTypes(t.baseClass, skipPtrs)
+  c.p.breakInLoop = oldBreakInLoop
   dec(c.p.nestedLoopCounter)
   # for TR macros this 'while true: ...; break' loop is pretty bad, so
   # we avoid it now if we can:
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index eceb10470..80144ccc0 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -11,36 +11,42 @@
 # and evaluation phase
 
 import
-  strutils, options, ast, astalgo, trees, treetab, nimsets, times,
-  nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
-  commands, magicsys, modulegraphs, strtabs, lineinfos
-
-proc newIntNodeT*(intVal: BiggestInt, n: PNode; g: ModuleGraph): PNode =
-  case skipTypes(n.typ, abstractVarRange).kind
-  of tyInt:
-    result = newIntNode(nkIntLit, intVal)
-    # See bug #6989. 'pred' et al only produce an int literal type if the
-    # original type was 'int', not a distinct int etc.
-    if n.typ.kind == tyInt:
-      result.typ = getIntLitType(g, result)
-    else:
-      result.typ = n.typ
-    # hrm, this is not correct: 1 + high(int) shouldn't produce tyInt64 ...
-    #setIntLitType(result)
-  of tyChar:
-    result = newIntNode(nkCharLit, intVal)
-    result.typ = n.typ
-  else:
-    result = newIntNode(nkIntLit, intVal)
-    result.typ = n.typ
+  options, ast, trees, nimsets,
+  platform, msgs, idents, renderer, types,
+  commands, magicsys, modulegraphs, lineinfos, wordrecg
+
+import std/[strutils, math, strtabs]
+from system/memory import nimCStrLen
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, formatfloat]
+
+proc errorType*(g: ModuleGraph): PType =
+  ## creates a type representing an error state
+  result = newType(tyError, g.idgen, g.owners[^1])
+  result.flags.incl tfCheckedForDestructor
+
+proc getIntLitTypeG(g: ModuleGraph; literal: PNode; idgen: IdGenerator): PType =
+  # we cache some common integer literal types for performance:
+  let ti = getSysType(g, literal.info, tyInt)
+  result = copyType(ti, idgen, ti.owner)
+  result.n = literal
+
+proc newIntNodeT*(intVal: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
+  result = newIntTypeNode(intVal, n.typ)
+  # See bug #6989. 'pred' et al only produce an int literal type if the
+  # original type was 'int', not a distinct int etc.
+  if n.typ.kind == tyInt:
+    # access cache for the int lit type
+    result.typ = getIntLitTypeG(g, result, idgen)
   result.info = n.info
 
 proc newFloatNodeT*(floatVal: BiggestFloat, n: PNode; g: ModuleGraph): PNode =
-  result = newFloatNode(nkFloatLit, floatVal)
-  if skipTypes(n.typ, abstractVarRange).kind == tyFloat:
-    result.typ = getFloatLitType(g, result)
+  if n.typ.skipTypes(abstractInst).kind == tyFloat32:
+    result = newFloatNode(nkFloat32Lit, floatVal)
   else:
-    result.typ = n.typ
+    result = newFloatNode(nkFloatLit, floatVal)
+  result.typ = n.typ
   result.info = n.info
 
 proc newStrNodeT*(strVal: string, n: PNode; g: ModuleGraph): PNode =
@@ -48,67 +54,46 @@ proc newStrNodeT*(strVal: string, n: PNode; g: ModuleGraph): PNode =
   result.typ = n.typ
   result.info = n.info
 
-proc getConstExpr*(m: PSym, n: PNode; g: ModuleGraph): PNode
+proc getConstExpr*(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
   # evaluates the constant expression or returns nil if it is no constant
   # expression
-proc evalOp*(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode
+proc evalOp*(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
 
-proc checkInRange(conf: ConfigRef; n: PNode, res: BiggestInt): bool =
-  if res in firstOrd(conf, n.typ)..lastOrd(conf, n.typ):
-    result = true
+proc checkInRange(conf: ConfigRef; n: PNode, res: Int128): bool =
+  res in firstOrd(conf, n.typ)..lastOrd(conf, n.typ)
 
-proc foldAdd(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
-  let res = a +% b
-  if ((res xor a) >= 0'i64 or (res xor b) >= 0'i64) and
-      checkInRange(g.config, n, res):
-    result = newIntNodeT(res, n, g)
+proc foldAdd(a, b: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
+  let res = a + b
+  if checkInRange(g.config, n, res):
+    result = newIntNodeT(res, n, idgen, g)
+  else:
+    result = nil
 
-proc foldSub*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
-  let res = a -% b
-  if ((res xor a) >= 0'i64 or (res xor not b) >= 0'i64) and
-      checkInRange(g.config, n, res):
-    result = newIntNodeT(res, n, g)
+proc foldSub(a, b: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
+  let res = a - b
+  if checkInRange(g.config, n, res):
+    result = newIntNodeT(res, n, idgen, g)
+  else:
+    result = nil
 
-proc foldAbs*(a: BiggestInt, n: PNode; g: ModuleGraph): PNode =
+proc foldUnarySub(a: Int128, n: PNode; idgen: IdGenerator, g: ModuleGraph): PNode =
   if a != firstOrd(g.config, n.typ):
-    result = newIntNodeT(a, n, g)
-
-proc foldMod*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
-  if b != 0'i64:
-    result = newIntNodeT(a mod b, n, g)
-
-proc foldModU*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
-  if b != 0'i64:
-    result = newIntNodeT(a %% b, n, g)
-
-proc foldDiv*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
-  if b != 0'i64 and (a != firstOrd(g.config, n.typ) or b != -1'i64):
-    result = newIntNodeT(a div b, n, g)
-
-proc foldDivU*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
-  if b != 0'i64:
-    result = newIntNodeT(a /% b, n, g)
-
-proc foldMul*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
-  let res = a *% b
-  let floatProd = toBiggestFloat(a) * toBiggestFloat(b)
-  let resAsFloat = toBiggestFloat(res)
-
-  # Fast path for normal case: small multiplicands, and no info
-  # is lost in either method.
-  if resAsFloat == floatProd and checkInRange(g.config, n, res):
-    return newIntNodeT(res, n, g)
+    result = newIntNodeT(-a, n, idgen, g)
+  else:
+    result = nil
 
-  # Somebody somewhere lost info. Close enough, or way off? Note
-  # that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
-  # The difference either is or isn't significant compared to the
-  # true value (of which floatProd is a good approximation).
+proc foldAbs(a: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
+  if a != firstOrd(g.config, n.typ):
+    result = newIntNodeT(abs(a), n, idgen, g)
+  else:
+    result = nil
 
-  # abs(diff)/abs(prod) <= 1/32 iff
-  #   32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
-  if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd) and
-      checkInRange(g.config, n, res):
-    return newIntNodeT(res, n, g)
+proc foldMul(a, b: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
+  let res = a * b
+  if checkInRange(g.config, n, res):
+    return newIntNodeT(res, n, idgen, g)
+  else:
+    result = nil
 
 proc ordinalValToString*(a: PNode; g: ModuleGraph): string =
   # because $ has the param ordinal[T], `a` is not necessarily an enum, but an
@@ -118,26 +103,29 @@ proc ordinalValToString*(a: PNode; g: ModuleGraph): string =
   var t = skipTypes(a.typ, abstractRange)
   case t.kind
   of tyChar:
-    result = $chr(int(x) and 0xff)
+    result = $chr(toInt64(x) and 0xff)
   of tyEnum:
+    result = ""
     var n = t.n
-    for i in countup(0, sonsLen(n) - 1):
-      if n.sons[i].kind != nkSym: internalError(g.config, a.info, "ordinalValToString")
-      var field = n.sons[i].sym
+    for i in 0..<n.len:
+      if n[i].kind != nkSym: internalError(g.config, a.info, "ordinalValToString")
+      var field = n[i].sym
       if field.position == x:
         if field.ast == nil:
           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
 
 proc isFloatRange(t: PType): bool {.inline.} =
-  result = t.kind == tyRange and t.sons[0].kind in {tyFloat..tyFloat128}
+  result = t.kind == tyRange and t.elementType.kind in {tyFloat..tyFloat128}
 
 proc isIntRange(t: PType): bool {.inline.} =
-  result = t.kind == tyRange and t.sons[0].kind in {
+  result = t.kind == tyRange and t.elementType.kind in {
       tyInt..tyInt64, tyUInt8..tyUInt32}
 
 proc pickIntRange(a, b: PType): PType =
@@ -148,173 +136,163 @@ proc pickIntRange(a, b: PType): PType =
 proc isIntRangeOrLit(t: PType): bool =
   result = isIntRange(t) or isIntLit(t)
 
-proc makeRange(typ: PType, first, last: BiggestInt; g: ModuleGraph): PType =
-  let minA = min(first, last)
-  let maxA = max(first, last)
-  let lowerNode = newIntNode(nkIntLit, minA)
-  if typ.kind == tyInt and minA == maxA:
-    result = getIntLitType(g, lowerNode)
-  elif typ.kind in {tyUint, tyUInt64}:
-    # these are not ordinal types, so you get no subrange type for these:
-    result = typ
-  else:
-    var n = newNode(nkRange)
-    addSon(n, lowerNode)
-    addSon(n, newIntNode(nkIntLit, maxA))
-    result = newType(tyRange, typ.owner)
-    result.n = n
-    addSonSkipIntLit(result, skipTypes(typ, {tyRange}))
-
-proc makeRangeF(typ: PType, first, last: BiggestFloat; g: ModuleGraph): PType =
-  var n = newNode(nkRange)
-  addSon(n, newFloatNode(nkFloatLit, min(first.float, last.float)))
-  addSon(n, newFloatNode(nkFloatLit, max(first.float, last.float)))
-  result = newType(tyRange, typ.owner)
-  result.n = n
-  addSonSkipIntLit(result, skipTypes(typ, {tyRange}))
-
-proc evalIs(n, a: PNode): PNode =
-  # XXX: This should use the standard isOpImpl
-  #internalAssert a.kind == nkSym and a.sym.kind == skType
-  #internalAssert n.sonsLen == 3 and
-  #  n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
-
-  let t1 = a.sym.typ
-
-  if n[2].kind in {nkStrLit..nkTripleStrLit}:
-    case n[2].strVal.normalize
-    of "closure":
-      let t = skipTypes(t1, abstractRange)
-      result = newIntNode(nkIntLit, ord(t.kind == tyProc and
-                                        t.callConv == ccClosure and
-                                        tfIterator notin t.flags))
-    of "iterator":
-      let t = skipTypes(t1, abstractRange)
-      result = newIntNode(nkIntLit, ord(t.kind == tyProc and
-                                        t.callConv == ccClosure and
-                                        tfIterator in t.flags))
-    else: discard
-  else:
-    # XXX semexprs.isOpImpl is slightly different and requires a context. yay.
-    let t2 = n[2].typ
-    var match = sameType(t1, t2)
-    result = newIntNode(nkIntLit, ord(match))
-  result.typ = n.typ
-
-proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
+proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
   # b and c may be nil
   result = nil
   case m
-  of mOrd: result = newIntNodeT(getOrdValue(a), n, g)
-  of mChr: result = newIntNodeT(getInt(a), n, g)
-  of mUnaryMinusI, mUnaryMinusI64: result = newIntNodeT(- getInt(a), n, g)
-  of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n, g)
-  of mNot: result = newIntNodeT(1 - getInt(a), n, g)
-  of mCard: result = newIntNodeT(nimsets.cardSet(g.config, a), n, g)
+  of mOrd: result = newIntNodeT(getOrdValue(a), n, idgen, g)
+  of mChr: result = newIntNodeT(getInt(a), n, idgen, g)
+  of mUnaryMinusI, mUnaryMinusI64: result = foldUnarySub(getInt(a), n, idgen, g)
+  of mUnaryMinusF64: result = newFloatNodeT(-getFloat(a), n, g)
+  of mNot: result = newIntNodeT(One - getInt(a), n, idgen, g)
+  of mCard: result = newIntNodeT(toInt128(nimsets.cardSet(g.config, a)), n, idgen, g)
   of mBitnotI:
-    case skipTypes(n.typ, abstractRange).kind
-    of tyUInt..tyUInt64:
-      result = newIntNodeT((not getInt(a)) and lastOrd(g.config, a.typ, fixedUnsigned=true), n, g)
+    if n.typ.isUnsigned:
+      result = newIntNodeT(bitnot(getInt(a)).maskBytes(int(getSize(g.config, n.typ))), n, idgen, g)
     else:
-      result = newIntNodeT(not getInt(a), n, g)
-  of mLengthArray: result = newIntNodeT(lengthOrd(g.config, a.typ), n, g)
-  of mLengthSeq, mLengthOpenArray, mXLenSeq, mLengthStr, mXLenStr:
+      result = newIntNodeT(bitnot(getInt(a)), n, idgen, g)
+  of mLengthArray: result = newIntNodeT(lengthOrd(g.config, a.typ), n, idgen, g)
+  of mLengthSeq, mLengthOpenArray, mLengthStr:
     if a.kind == nkNilLit:
-      result = newIntNodeT(0, n, g)
+      result = newIntNodeT(Zero, n, idgen, g)
     elif a.kind in {nkStrLit..nkTripleStrLit}:
-      result = newIntNodeT(len a.strVal, n, g)
+      if a.typ.kind == tyString:
+        result = newIntNodeT(toInt128(a.strVal.len), n, idgen, g)
+      elif a.typ.kind == tyCstring:
+        result = newIntNodeT(toInt128(nimCStrLen(a.strVal.cstring)), n, idgen, g)
     else:
-      result = newIntNodeT(sonsLen(a), n, g)
+      result = newIntNodeT(toInt128(a.len), n, idgen, g)
   of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away
-  of mToFloat, mToBiggestFloat:
-    result = newFloatNodeT(toFloat(int(getInt(a))), n, g)
   # XXX: Hides overflow/underflow
-  of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n, g)
-  of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n, g)
-  of mAbsI: result = foldAbs(getInt(a), n, g)
-  of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
-    # byte(-128) = 1...1..1000_0000'64 --> 0...0..1000_0000'64
-    result = newIntNodeT(getInt(a) and (`shl`(1, getSize(g.config, a.typ) * 8) - 1), n, g)
-  of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n, g)
-  of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n, g)
-  of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n, g)
-  of mUnaryLt: result = foldSub(getOrdValue(a), 1, n, g)
-  of mSucc: result = foldAdd(getOrdValue(a), getInt(b), n, g)
-  of mPred: result = foldSub(getOrdValue(a), getInt(b), n, g)
-  of mAddI: result = foldAdd(getInt(a), getInt(b), n, g)
-  of mSubI: result = foldSub(getInt(a), getInt(b), n, g)
-  of mMulI: result = foldMul(getInt(a), getInt(b), n, g)
+  of mAbsI: result = foldAbs(getInt(a), n, idgen, g)
+  of mSucc: result = foldAdd(getOrdValue(a), getInt(b), n, idgen, g)
+  of mPred: result = foldSub(getOrdValue(a), getInt(b), n, idgen, g)
+  of mAddI: result = foldAdd(getInt(a), getInt(b), n, idgen, g)
+  of mSubI: result = foldSub(getInt(a), getInt(b), n, idgen, g)
+  of mMulI: result = foldMul(getInt(a), getInt(b), n, idgen, g)
   of mMinI:
-    if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n, g)
-    else: result = newIntNodeT(getInt(a), n, g)
+    let argA = getInt(a)
+    let argB = getInt(b)
+    result = newIntNodeT(if argA < argB: argA else: argB, n, idgen, g)
   of mMaxI:
-    if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n, g)
-    else: result = newIntNodeT(getInt(b), n, g)
+    let argA = getInt(a)
+    let argB = getInt(b)
+    result = newIntNodeT(if argA > argB: argA else: argB, n, idgen, g)
   of mShlI:
     case skipTypes(n.typ, abstractRange).kind
-    of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n, g)
-    of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n, g)
-    of tyInt32: result = newIntNodeT(int32(getInt(a)) shl int32(getInt(b)), n, g)
-    of tyInt64, tyInt:
-      result = newIntNodeT(`shl`(getInt(a), getInt(b)), n, g)
-    of tyUInt..tyUInt64:
-      result = newIntNodeT(`shl`(getInt(a), getInt(b)) and lastOrd(g.config, a.typ, fixedUnsigned=true), n, g)
+    of tyInt8: result = newIntNodeT(toInt128(toInt8(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
+    of tyInt16: result = newIntNodeT(toInt128(toInt16(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
+    of tyInt32: result = newIntNodeT(toInt128(toInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
+    of tyInt64: result = newIntNodeT(toInt128(toInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
+    of tyInt:
+      if g.config.target.intSize == 4:
+        result = newIntNodeT(toInt128(toInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
+      else:
+        result = newIntNodeT(toInt128(toInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
+    of tyUInt8: result = newIntNodeT(toInt128(toUInt8(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
+    of tyUInt16: result = newIntNodeT(toInt128(toUInt16(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
+    of tyUInt32: result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
+    of tyUInt64: result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
+    of tyUInt:
+      if g.config.target.intSize == 4:
+        result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
+      else:
+        result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
     else: internalError(g.config, n.info, "constant folding for shl")
   of mShrI:
+    var a = cast[uint64](getInt(a))
+    let b = cast[uint64](getInt(b))
+    # To support the ``-d:nimOldShiftRight`` flag, we need to mask the
+    # signed integers to cut off the extended sign bit in the internal
+    # representation.
+    if 0'u64 < b: # do not cut off the sign extension, when there is
+              # no bit shifting happening.
+      case skipTypes(n.typ, abstractRange).kind
+      of tyInt8: a = a and 0xff'u64
+      of tyInt16: a = a and 0xffff'u64
+      of tyInt32: a = a and 0xffffffff'u64
+      of tyInt:
+        if g.config.target.intSize == 4:
+          a = a and 0xffffffff'u64
+      else:
+        # unsigned and 64 bit integers don't need masking
+        discard
+    let c = cast[BiggestInt](a shr b)
+    result = newIntNodeT(toInt128(c), n, idgen, g)
+  of mAshrI:
     case skipTypes(n.typ, abstractRange).kind
-    of tyInt8: result = newIntNodeT(int8(getInt(a)) shr int8(getInt(b)), n, g)
-    of tyInt16: result = newIntNodeT(int16(getInt(a)) shr int16(getInt(b)), n, g)
-    of tyInt32: result = newIntNodeT(int32(getInt(a)) shr int32(getInt(b)), n, g)
-    of tyInt64, tyInt, tyUInt..tyUInt64:
-      result = newIntNodeT(`shr`(getInt(a), getInt(b)), n, g)
-    else: internalError(g.config, n.info, "constant folding for shr")
-  of mDivI: result = foldDiv(getInt(a), getInt(b), n, g)
-  of mModI: result = foldMod(getInt(a), getInt(b), n, g)
+    of tyInt8: result =  newIntNodeT(toInt128(ashr(toInt8(getInt(a)), toInt8(getInt(b)))), n, idgen, g)
+    of tyInt16: result = newIntNodeT(toInt128(ashr(toInt16(getInt(a)), toInt16(getInt(b)))), n, idgen, g)
+    of tyInt32: result = newIntNodeT(toInt128(ashr(toInt32(getInt(a)), toInt32(getInt(b)))), n, idgen, g)
+    of tyInt64, tyInt:
+      result = newIntNodeT(toInt128(ashr(toInt64(getInt(a)), toInt64(getInt(b)))), n, idgen, g)
+    else: internalError(g.config, n.info, "constant folding for ashr")
+  of mDivI:
+    let argA = getInt(a)
+    let argB = getInt(b)
+    if argB != Zero and (argA != firstOrd(g.config, n.typ) or argB != NegOne):
+      result = newIntNodeT(argA div argB, n, idgen, g)
+  of mModI:
+    let argA = getInt(a)
+    let argB = getInt(b)
+    if argB != Zero and (argA != firstOrd(g.config, n.typ) or argB != NegOne):
+      result = newIntNodeT(argA mod argB, n, idgen, g)
   of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n, g)
   of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n, g)
   of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n, g)
   of mDivF64:
-    if getFloat(b) == 0.0:
-      if getFloat(a) == 0.0: result = newFloatNodeT(NaN, n, g)
-      elif getFloat(b).classify == fcNegZero: result = newFloatNodeT(-Inf, n, g)
-      else: result = newFloatNodeT(Inf, n, g)
-    else:
-      result = newFloatNodeT(getFloat(a) / getFloat(b), n, g)
-  of mMaxF64:
-    if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(a), n, g)
-    else: result = newFloatNodeT(getFloat(b), n, g)
-  of mMinF64:
-    if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(b), n, g)
-    else: result = newFloatNodeT(getFloat(a), n, g)
-  of mIsNil: result = newIntNodeT(ord(a.kind == nkNilLit), n, g)
+    result = newFloatNodeT(getFloat(a) / getFloat(b), n, g)
+  of mIsNil:
+    let val = a.kind == nkNilLit or
+      # nil closures have the value (nil, nil)
+      (a.typ != nil and skipTypes(a.typ, abstractRange).kind == tyProc and
+        a.kind == nkTupleConstr and a.len == 2 and
+        a[0].kind == nkNilLit and a[1].kind == nkNilLit)
+    result = newIntNodeT(toInt128(ord(val)), n, idgen, g)
   of mLtI, mLtB, mLtEnum, mLtCh:
-    result = newIntNodeT(ord(getOrdValue(a) < getOrdValue(b)), n, g)
+    result = newIntNodeT(toInt128(ord(getOrdValue(a) < getOrdValue(b))), n, idgen, g)
   of mLeI, mLeB, mLeEnum, mLeCh:
-    result = newIntNodeT(ord(getOrdValue(a) <= getOrdValue(b)), n, g)
+    result = newIntNodeT(toInt128(ord(getOrdValue(a) <= getOrdValue(b))), n, idgen, g)
   of mEqI, mEqB, mEqEnum, mEqCh:
-    result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n, g)
-  of mLtF64: result = newIntNodeT(ord(getFloat(a) < getFloat(b)), n, g)
-  of mLeF64: result = newIntNodeT(ord(getFloat(a) <= getFloat(b)), n, g)
-  of mEqF64: result = newIntNodeT(ord(getFloat(a) == getFloat(b)), n, g)
-  of mLtStr: result = newIntNodeT(ord(getStr(a) < getStr(b)), n, g)
-  of mLeStr: result = newIntNodeT(ord(getStr(a) <= getStr(b)), n, g)
-  of mEqStr: result = newIntNodeT(ord(getStr(a) == getStr(b)), n, g)
-  of mLtU, mLtU64:
-    result = newIntNodeT(ord(`<%`(getOrdValue(a), getOrdValue(b))), n, g)
-  of mLeU, mLeU64:
-    result = newIntNodeT(ord(`<=%`(getOrdValue(a), getOrdValue(b))), n, g)
-  of mBitandI, mAnd: result = newIntNodeT(a.getInt and b.getInt, n, g)
-  of mBitorI, mOr: result = newIntNodeT(getInt(a) or getInt(b), n, g)
-  of mBitxorI, mXor: result = newIntNodeT(a.getInt xor b.getInt, n, g)
-  of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n, g)
-  of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n, g)
-  of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n, g)
-  of mModU: result = foldModU(getInt(a), getInt(b), n, g)
-  of mDivU: result = foldDivU(getInt(a), getInt(b), n, g)
-  of mLeSet: result = newIntNodeT(ord(containsSets(g.config, a, b)), n, g)
-  of mEqSet: result = newIntNodeT(ord(equalSets(g.config, a, b)), n, g)
+    result = newIntNodeT(toInt128(ord(getOrdValue(a) == getOrdValue(b))), n, idgen, g)
+  of mLtF64: result = newIntNodeT(toInt128(ord(getFloat(a) < getFloat(b))), n, idgen, g)
+  of mLeF64: result = newIntNodeT(toInt128(ord(getFloat(a) <= getFloat(b))), n, idgen, g)
+  of mEqF64: result = newIntNodeT(toInt128(ord(getFloat(a) == getFloat(b))), n, idgen, g)
+  of mLtStr: result = newIntNodeT(toInt128(ord(getStr(a) < getStr(b))), n, idgen, g)
+  of mLeStr: result = newIntNodeT(toInt128(ord(getStr(a) <= getStr(b))), n, idgen, g)
+  of mEqStr: result = newIntNodeT(toInt128(ord(getStr(a) == getStr(b))), n, idgen, g)
+  of mLtU:
+    result = newIntNodeT(toInt128(ord(`<%`(toInt64(getOrdValue(a)), toInt64(getOrdValue(b))))), n, idgen, g)
+  of mLeU:
+    result = newIntNodeT(toInt128(ord(`<=%`(toInt64(getOrdValue(a)), toInt64(getOrdValue(b))))), n, idgen, g)
+  of mBitandI, mAnd: result = newIntNodeT(bitand(a.getInt, b.getInt), n, idgen, g)
+  of mBitorI, mOr: result = newIntNodeT(bitor(getInt(a), getInt(b)), n, idgen, g)
+  of mBitxorI, mXor: result = newIntNodeT(bitxor(getInt(a), getInt(b)), n, idgen, g)
+  of mAddU:
+    let val = maskBytes(getInt(a) + getInt(b), int(getSize(g.config, n.typ)))
+    result = newIntNodeT(val, n, idgen, g)
+  of mSubU:
+    let val = maskBytes(getInt(a) - getInt(b), int(getSize(g.config, n.typ)))
+    result = newIntNodeT(val, n, idgen, g)
+    # echo "subU: ", val, " n: ", n, " result: ", val
+  of mMulU:
+    let val = maskBytes(getInt(a) * getInt(b), int(getSize(g.config, n.typ)))
+    result = newIntNodeT(val, n, idgen, g)
+  of mModU:
+    let argA = maskBytes(getInt(a), int(getSize(g.config, a.typ)))
+    let argB = maskBytes(getInt(b), int(getSize(g.config, a.typ)))
+    if argB != Zero:
+      result = newIntNodeT(argA mod argB, n, idgen, g)
+  of mDivU:
+    let argA = maskBytes(getInt(a), int(getSize(g.config, a.typ)))
+    let argB = maskBytes(getInt(b), int(getSize(g.config, a.typ)))
+    if argB != Zero:
+      result = newIntNodeT(argA div argB, n, idgen, g)
+  of mLeSet: result = newIntNodeT(toInt128(ord(containsSets(g.config, a, b))), n, idgen, g)
+  of mEqSet: result = newIntNodeT(toInt128(ord(equalSets(g.config, a, b))), n, idgen, g)
   of mLtSet:
-    result = newIntNodeT(ord(containsSets(g.config, a, b) and not equalSets(g.config, a, b)), n, g)
+    result = newIntNodeT(toInt128(ord(
+      containsSets(g.config, a, b) and not equalSets(g.config, a, b))), n, idgen, g)
   of mMulSet:
     result = nimsets.intersectSets(g.config, a, b)
     result.info = n.info
@@ -324,59 +302,44 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
   of mMinusSet:
     result = nimsets.diffSets(g.config, a, b)
     result.info = n.info
-  of mSymDiffSet:
-    result = nimsets.symdiffSets(g.config, a, b)
-    result.info = n.info
   of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n, g)
-  of mInSet: result = newIntNodeT(ord(inSet(a, b)), n, g)
+  of mInSet: result = newIntNodeT(toInt128(ord(inSet(a, b))), n, idgen, g)
   of mRepr:
     # BUGFIX: we cannot eval mRepr here for reasons that I forgot.
     discard
-  of mIntToStr, mInt64ToStr: result = newStrNodeT($(getOrdValue(a)), n, g)
   of mBoolToStr:
     if getOrdValue(a) == 0: result = newStrNodeT("false", n, g)
     else: result = newStrNodeT("true", n, g)
-  of mCopyStr: result = newStrNodeT(substr(getStr(a), int(getOrdValue(b))), n, g)
-  of mCopyStrLast:
-    result = newStrNodeT(substr(getStr(a), int(getOrdValue(b)),
-                                           int(getOrdValue(c))), n, g)
-  of mFloatToStr: result = newStrNodeT($getFloat(a), n, g)
   of mCStrToStr, mCharToStr:
-    if a.kind == nkBracket:
-      var s = ""
-      for b in a.sons:
-        s.add b.getStrOrChar
-      result = newStrNodeT(s, n, g)
-    else:
-      result = newStrNodeT(getStrOrChar(a), n, g)
-  of mStrToStr: result = a
+    result = newStrNodeT(getStrOrChar(a), n, g)
+  of mStrToStr: result = newStrNodeT(getStrOrChar(a), n, g)
   of mEnumToStr: result = newStrNodeT(ordinalValToString(a, g), n, g)
   of mArrToSeq:
     result = copyTree(a)
     result.typ = n.typ
   of mCompileOption:
-    result = newIntNodeT(ord(commands.testCompileOption(g.config, a.getStr, n.info)), n, g)
+    result = newIntNodeT(toInt128(ord(commands.testCompileOption(g.config, a.getStr, n.info))), n, idgen, g)
   of mCompileOptionArg:
-    result = newIntNodeT(ord(
-      testCompileOptionArg(g.config, getStr(a), getStr(b), n.info)), n, g)
+    result = newIntNodeT(toInt128(ord(
+      testCompileOptionArg(g.config, getStr(a), getStr(b), n.info))), n, idgen, g)
   of mEqProc:
-    result = newIntNodeT(ord(
-        exprStructuralEquivalent(a, b, strictSymEquality=true)), n, g)
+    result = newIntNodeT(toInt128(ord(
+        exprStructuralEquivalent(a, b, strictSymEquality=true))), n, idgen, g)
   else: discard
 
-proc getConstIfExpr(c: PSym, n: PNode; g: ModuleGraph): PNode =
+proc getConstIfExpr(c: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
   result = nil
-  for i in countup(0, sonsLen(n) - 1):
-    var it = n.sons[i]
+  for i in 0..<n.len:
+    var it = n[i]
     if it.len == 2:
-      var e = getConstExpr(c, it.sons[0], g)
+      var e = getConstExpr(c, it[0], idgen, g)
       if e == nil: return nil
       if getOrdValue(e) != 0:
         if result == nil:
-          result = getConstExpr(c, it.sons[1], g)
+          result = getConstExpr(c, it[1], idgen, g)
           if result == nil: return
     elif it.len == 1:
-      if result == nil: result = getConstExpr(c, it.sons[0], g)
+      if result == nil: result = getConstExpr(c, it[0], idgen, g)
     else: internalError(g.config, it.info, "getConstIfExpr()")
 
 proc leValueConv*(a, b: PNode): bool =
@@ -384,30 +347,30 @@ proc leValueConv*(a, b: PNode): bool =
   case a.kind
   of nkCharLit..nkUInt64Lit:
     case b.kind
-    of nkCharLit..nkUInt64Lit: result = a.intVal <= b.intVal
+    of nkCharLit..nkUInt64Lit: result = a.getInt <= b.getInt
     of nkFloatLit..nkFloat128Lit: result = a.intVal <= round(b.floatVal).int
     else: result = false #internalError(a.info, "leValueConv")
   of nkFloatLit..nkFloat128Lit:
     case b.kind
     of nkFloatLit..nkFloat128Lit: result = a.floatVal <= b.floatVal
-    of nkCharLit..nkUInt64Lit: result = a.floatVal <= toFloat(int(b.intVal))
+    of nkCharLit..nkUInt64Lit: result = a.floatVal <= toFloat64(b.getInt)
     else: result = false # internalError(a.info, "leValueConv")
   else: result = false # internalError(a.info, "leValueConv")
 
-proc magicCall(m: PSym, n: PNode; g: ModuleGraph): PNode =
-  if sonsLen(n) <= 1: return
+proc magicCall(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
+  if n.len <= 1: return
 
-  var s = n.sons[0].sym
-  var a = getConstExpr(m, n.sons[1], g)
-  var b, c: PNode
+  var s = n[0].sym
+  var a = getConstExpr(m, n[1], idgen, g)
+  var b, c: PNode = nil
   if a == nil: return
-  if sonsLen(n) > 2:
-    b = getConstExpr(m, n.sons[2], g)
+  if n.len > 2:
+    b = getConstExpr(m, n[2], idgen, g)
     if b == nil: return
-    if sonsLen(n) > 3:
-      c = getConstExpr(m, n.sons[3], g)
+    if n.len > 3:
+      c = getConstExpr(m, n[3], idgen, g)
       if c == nil: return
-  result = evalOp(s.magic, n, a, b, c, g)
+  result = evalOp(s.magic, n, a, b, c, idgen, g)
 
 proc getAppType(n: PNode; g: ModuleGraph): PNode =
   if g.config.globalOptions.contains(optGenDynLib):
@@ -419,168 +382,256 @@ proc getAppType(n: PNode; g: ModuleGraph): PNode =
   else:
     result = newStrNodeT("console", n, g)
 
-proc rangeCheck(n: PNode, value: BiggestInt; g: ModuleGraph) =
-  var err = false
-  if n.typ.skipTypes({tyRange}).kind in {tyUInt..tyUInt64}:
-    err = value <% firstOrd(g.config, n.typ) or value >% lastOrd(g.config, n.typ, fixedUnsigned=true)
-  else:
-    err = value < firstOrd(g.config, n.typ) or value > lastOrd(g.config, n.typ)
-  if err:
+proc rangeCheck(n: PNode, value: Int128; g: ModuleGraph) =
+  if value < firstOrd(g.config, n.typ) or value > lastOrd(g.config, n.typ):
     localError(g.config, n.info, "cannot convert " & $value &
-                                     " to " & typeToString(n.typ))
+                                    " to " & typeToString(n.typ))
 
-proc foldConv*(n, a: PNode; g: ModuleGraph; check = false): PNode =
-  # XXX range checks?
-  case skipTypes(n.typ, abstractRange).kind
+proc floatRangeCheck(n: PNode, value: BiggestFloat; g: ModuleGraph) =
+  if value < firstFloat(n.typ) or value > lastFloat(n.typ):
+    localError(g.config, n.info, "cannot convert " & $value &
+                                    " to " & typeToString(n.typ))
+
+proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): PNode =
+  let dstTyp = skipTypes(n.typ, abstractRange - {tyTypeDesc})
+  let srcTyp = skipTypes(a.typ, abstractRange - {tyTypeDesc})
+
+  # if srcTyp.kind == tyUInt64 and "FFFFFF" in $n:
+  #   echo "n: ", n, " a: ", a
+  #   echo "from: ", srcTyp, " to: ", dstTyp, " check: ", check
+  #   echo getInt(a)
+  #   echo high(int64)
+  #   writeStackTrace()
+  case dstTyp.kind
+  of tyBool:
+    case srcTyp.kind
+    of tyFloat..tyFloat64:
+      result = newIntNodeT(toInt128(getFloat(a) != 0.0), n, idgen, g)
+    of tyChar, tyUInt..tyUInt64, tyInt..tyInt64:
+      result = newIntNodeT(toInt128(a.getOrdValue != 0), n, idgen, g)
+    of tyBool, tyEnum: # xxx shouldn't we disallow `tyEnum`?
+      result = a
+      result.typ = n.typ
+    else:
+      raiseAssert $srcTyp.kind
   of tyInt..tyInt64, tyUInt..tyUInt64:
-    case skipTypes(a.typ, abstractRange).kind
+    case srcTyp.kind
     of tyFloat..tyFloat64:
-      result = newIntNodeT(int(getFloat(a)), n, g)
-    of tyChar: result = newIntNodeT(getOrdValue(a), n, g)
+      result = newIntNodeT(toInt128(getFloat(a)), n, idgen, g)
+    of tyChar, tyUInt..tyUInt64, tyInt..tyInt64:
+      var val = a.getOrdValue
+      if dstTyp.kind in {tyUInt..tyUInt64}:
+        result = newIntNodeT(maskBytes(val, int getSize(g.config, dstTyp)), n, idgen, g)
+        result.transitionIntKind(nkUIntLit)
+      else:
+        if check: rangeCheck(n, val, g)
+        result = newIntNodeT(val, n, idgen, g)
     else:
       result = a
       result.typ = n.typ
-    if check and result.kind in {nkCharLit..nkUInt64Lit}:
-      rangeCheck(n, result.intVal, g)
+    if check and result.kind in {nkCharLit..nkUInt64Lit} and
+          dstTyp.kind notin {tyUInt..tyUInt64}:
+      rangeCheck(n, getInt(result), g)
   of tyFloat..tyFloat64:
-    case skipTypes(a.typ, abstractRange).kind
-    of tyInt..tyInt64, tyEnum, tyBool, tyChar:
-      result = newFloatNodeT(toBiggestFloat(getOrdValue(a)), n, g)
+    case srcTyp.kind
+    of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyBool, tyChar:
+      result = newFloatNodeT(toFloat64(getOrdValue(a)), n, g)
     else:
       result = a
       result.typ = n.typ
-  of tyOpenArray, tyVarargs, tyProc:
-    discard
+  of tyOpenArray, tyVarargs, tyProc, tyPointer:
+    result = nil
   else:
     result = a
     result.typ = n.typ
 
-proc getArrayConstr(m: PSym, n: PNode; g: ModuleGraph): PNode =
+proc getArrayConstr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
   if n.kind == nkBracket:
     result = n
   else:
-    result = getConstExpr(m, n, g)
+    result = getConstExpr(m, n, idgen, g)
     if result == nil: result = n
 
-proc foldArrayAccess(m: PSym, n: PNode; g: ModuleGraph): PNode =
-  var x = getConstExpr(m, n.sons[0], g)
+proc foldArrayAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
+  var x = getConstExpr(m, n[0], idgen, g)
   if x == nil or x.typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyTypeDesc:
     return
 
-  var y = getConstExpr(m, n.sons[1], g)
+  var y = getConstExpr(m, n[1], idgen, g)
   if y == nil: return
 
-  var idx = getOrdValue(y)
+  var idx = toInt64(getOrdValue(y))
   case x.kind
   of nkPar, nkTupleConstr:
-    if idx >= 0 and idx < sonsLen(x):
-      result = x.sons[int(idx)]
-      if result.kind == nkExprColonExpr: result = result.sons[1]
+    if idx >= 0 and idx < x.len:
+      result = x.sons[idx]
+      if result.kind == nkExprColonExpr: result = result[1]
     else:
-      localError(g.config, n.info, "index out of bounds: " & $n)
+      result = nil
+      localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
   of nkBracket:
-    idx = idx - firstOrd(g.config, x.typ)
-    if idx >= 0 and idx < x.len: result = x.sons[int(idx)]
-    else: localError(g.config, n.info, "index out of bounds: " & $n)
+    idx -= toInt64(firstOrd(g.config, x.typ))
+    if idx >= 0 and idx < x.len: result = x[int(idx)]
+    else:
+      result = nil
+      localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
   of nkStrLit..nkTripleStrLit:
     result = newNodeIT(nkCharLit, x.info, n.typ)
-    if idx >= 0 and idx < len(x.strVal):
+    if idx >= 0 and idx < x.strVal.len:
       result.intVal = ord(x.strVal[int(idx)])
-    elif idx == len(x.strVal) and optLaxStrings in g.config.options:
-      discard
     else:
-      localError(g.config, n.info, "index out of bounds: " & $n)
-  else: discard
+      localError(g.config, n.info, formatErrorIndexBound(idx, x.strVal.len-1) & $n)
+  else: result = nil
 
-proc foldFieldAccess(m: PSym, n: PNode; g: ModuleGraph): PNode =
+proc foldFieldAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
   # a real field access; proc calls have already been transformed
-  var x = getConstExpr(m, n.sons[0], g)
+  result = nil
+  if n[1].kind != nkSym: return nil
+  var x = getConstExpr(m, n[0], idgen, g)
   if x == nil or x.kind notin {nkObjConstr, nkPar, nkTupleConstr}: return
 
-  var field = n.sons[1].sym
-  for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1):
-    var it = x.sons[i]
+  var field = n[1].sym
+  for i in ord(x.kind == nkObjConstr)..<x.len:
+    var it = x[i]
     if it.kind != nkExprColonExpr:
       # lookup per index:
-      result = x.sons[field.position]
-      if result.kind == nkExprColonExpr: result = result.sons[1]
+      result = x[field.position]
+      if result.kind == nkExprColonExpr: result = result[1]
       return
-    if it.sons[0].sym.name.id == field.name.id:
-      result = x.sons[i].sons[1]
+    if it[0].sym.name.id == field.name.id:
+      result = x[i][1]
       return
   localError(g.config, n.info, "field not found: " & field.name.s)
 
-proc foldConStrStr(m: PSym, n: PNode; g: ModuleGraph): PNode =
+proc foldConStrStr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
   result = newNodeIT(nkStrLit, n.info, n.typ)
   result.strVal = ""
-  for i in countup(1, sonsLen(n) - 1):
-    let a = getConstExpr(m, n.sons[i], g)
+  for i in 1..<n.len:
+    let a = getConstExpr(m, n[i], idgen, g)
     if a == nil: return nil
     result.strVal.add(getStrOrChar(a))
 
-proc newSymNodeTypeDesc*(s: PSym; info: TLineInfo): PNode =
+proc newSymNodeTypeDesc*(s: PSym; idgen: IdGenerator; info: TLineInfo): PNode =
   result = newSymNode(s, info)
   if s.typ.kind != tyTypeDesc:
-    result.typ = newType(tyTypeDesc, s.owner)
-    result.typ.addSonSkipIntLit(s.typ)
+    result.typ = newType(tyTypeDesc, idgen, s.owner)
+    result.typ.addSonSkipIntLit(s.typ, idgen)
   else:
     result.typ = s.typ
 
-proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
+proc foldDefine(m, s: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
   result = nil
+  var name = s.name.s
+  let prag = extractPragma(s)
+  if prag != nil:
+    for it in prag:
+      if it.kind in nkPragmaCallKinds and it.len == 2 and it[0].kind == nkIdent:
+        let word = whichKeyword(it[0].ident)
+        if word in {wStrDefine, wIntDefine, wBoolDefine, wDefine}:
+          # should be processed in pragmas.nim already
+          if it[1].kind in {nkStrLit, nkRStrLit, nkTripleStrLit}:
+            name = it[1].strVal
+  if isDefined(g.config, name):
+    let str = g.config.symbols[name]
+    case s.magic
+    of mIntDefine:
+      try:
+        result = newIntNodeT(toInt128(str.parseInt), n, idgen, g)
+      except ValueError:
+        localError(g.config, s.info,
+          "{.intdefine.} const was set to an invalid integer: '" &
+            str & "'")
+    of mStrDefine:
+      result = newStrNodeT(str, n, g)
+    of mBoolDefine:
+      try:
+        result = newIntNodeT(toInt128(str.parseBool.int), n, idgen, g)
+      except ValueError:
+        localError(g.config, s.info,
+          "{.booldefine.} const was set to an invalid bool: '" &
+            str & "'")
+    of mGenericDefine:
+      let rawTyp = s.typ
+      # pretend we don't support distinct types
+      let typ = rawTyp.skipTypes(abstractVarRange-{tyDistinct})
+      try:
+        template intNode(value): PNode =
+          let val = toInt128(value)
+          rangeCheck(n, val, g)
+          newIntNodeT(val, n, idgen, g)
+        case typ.kind
+        of tyString, tyCstring:
+          result = newStrNodeT(str, n, g)
+        of tyInt..tyInt64:
+          result = intNode(str.parseBiggestInt)
+        of tyUInt..tyUInt64:
+          result = intNode(str.parseBiggestUInt)
+        of tyBool:
+          result = intNode(str.parseBool.int)
+        of tyEnum:
+          # compile time parseEnum
+          let ident = getIdent(g.cache, str)
+          for e in typ.n:
+            if e.kind != nkSym: internalError(g.config, "foldDefine for enum")
+            let es = e.sym
+            let match =
+              if es.ast.isNil:
+                es.name.id == ident.id
+              else:
+                es.ast.strVal == str
+            if match:
+              result = intNode(es.position)
+              break
+          if result.isNil:
+            raise newException(ValueError, "invalid enum value: " & str)
+        else:
+          localError(g.config, s.info, "unsupported type $1 for define '$2'" %
+            [name, typeToString(rawTyp)])
+      except ValueError as e:
+        localError(g.config, s.info,
+          "could not process define '$1' of type $2; $3" %
+            [name, typeToString(rawTyp), e.msg])
+    else: result = copyTree(s.astdef) # unreachable
+  else:
+    result = copyTree(s.astdef)
+    if result != nil:
+      result.info = n.info
 
-  proc getSrcTimestamp(): DateTime =
-    try:
-      result = utc(fromUnix(parseInt(getEnv("SOURCE_DATE_EPOCH",
-                                            "not a number"))))
-    except ValueError:
-      # Environment variable malformed.
-      # https://reproducible-builds.org/specs/source-date-epoch/: "If the
-      # value is malformed, the build process SHOULD exit with a non-zero
-      # error code", which this doesn't do. This uses local time, because
-      # that maintains compatibility with existing usage.
-      result = local(getTime())
-
+proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
+  result = nil
   case n.kind
   of nkSym:
     var s = n.sym
     case s.kind
     of skEnumField:
-      result = newIntNodeT(s.position, n, g)
+      result = newIntNodeT(toInt128(s.position), n, idgen, g)
     of skConst:
       case s.magic
-      of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n, g)
-      of mCompileDate: result = newStrNodeT(format(getSrcTimestamp(),
-                                                   "yyyy-MM-dd"), n, g)
-      of mCompileTime: result = newStrNodeT(format(getSrcTimestamp(),
-                                                   "HH:mm:ss"), n, g)
-      of mCpuEndian: result = newIntNodeT(ord(CPU[g.config.target.targetCPU].endian), n, g)
+      of mIsMainModule: result = newIntNodeT(toInt128(ord(sfMainModule in m.flags)), n, idgen, g)
+      of mCompileDate: result = newStrNodeT(getDateStr(), n, g)
+      of mCompileTime: result = newStrNodeT(getClockStr(), n, g)
+      of mCpuEndian: result = newIntNodeT(toInt128(ord(CPU[g.config.target.targetCPU].endian)), n, idgen, g)
       of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[g.config.target.targetOS].name), n, g)
       of mHostCPU: result = newStrNodeT(platform.CPU[g.config.target.targetCPU].name.toLowerAscii, n, g)
       of mBuildOS: result = newStrNodeT(toLowerAscii(platform.OS[g.config.target.hostOS].name), n, g)
       of mBuildCPU: result = newStrNodeT(platform.CPU[g.config.target.hostCPU].name.toLowerAscii, n, g)
       of mAppType: result = getAppType(n, g)
-      of mNaN: result = newFloatNodeT(NaN, n, g)
-      of mInf: result = newFloatNodeT(Inf, n, g)
-      of mNegInf: result = newFloatNodeT(NegInf, n, g)
-      of mIntDefine:
-        if isDefined(g.config, s.name.s):
-          try:
-            result = newIntNodeT(g.config.symbols[s.name.s].parseInt, n, g)
-          except ValueError:
-            localError(g.config, n.info, "expression is not an integer literal")
-      of mStrDefine:
-        if isDefined(g.config, s.name.s):
-          result = newStrNodeT(g.config.symbols[s.name.s], n, g)
+      of mIntDefine, mStrDefine, mBoolDefine, mGenericDefine:
+        result = foldDefine(m, s, n, idgen, g)
       else:
-        result = copyTree(s.ast)
+        result = copyTree(s.astdef)
+        if result != nil:
+          result.info = n.info
     of skProc, skFunc, skMethod:
       result = n
+    of skParam:
+      if s.typ != nil and s.typ.kind == tyTypeDesc:
+        result = newSymNodeTypeDesc(s, idgen, n.info)
     of skType:
       # XXX gensym'ed symbols can come here and cannot be resolved. This is
       # dirty, but correct.
       if s.typ != nil:
-        result = newSymNodeTypeDesc(s, n.info)
+        result = newSymNodeTypeDesc(s, idgen, n.info)
     of skGenericParam:
       if s.typ.kind == tyStatic:
         if s.typ.n != nil and tfUnresolved notin s.typ.flags:
@@ -589,147 +640,155 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
       elif s.typ.isIntLit:
         result = s.typ.n
       else:
-        result = newSymNodeTypeDesc(s, n.info)
+        result = newSymNodeTypeDesc(s, idgen, n.info)
     else: discard
   of nkCharLit..nkNilLit:
     result = copyNode(n)
   of nkIfExpr:
-    result = getConstIfExpr(m, n, g)
+    result = getConstIfExpr(m, n, idgen, g)
   of nkCallKinds:
-    if n.sons[0].kind != nkSym: return
-    var s = n.sons[0].sym
+    if n[0].kind != nkSym: return
+    var s = n[0].sym
     if s.kind != skProc and s.kind != skFunc: return
     try:
       case s.magic
       of mNone:
         # If it has no sideEffect, it should be evaluated. But not here.
         return
-      of mSizeOf:
-        var a = n.sons[1]
-        if computeSize(g.config, a.typ) < 0:
-          localError(g.config, a.info, "cannot evaluate 'sizeof' because its type is not defined completely")
-          result = nil
-        elif skipTypes(a.typ, typedescInst+{tyRange}).kind in
-             IntegralTypes+NilableTypes+{tySet}:
-          #{tyArray,tyObject,tyTuple}:
-          result = newIntNodeT(getSize(g.config, a.typ), n, g)
-        else:
-          result = nil
-          # XXX: size computation for complex types is still wrong
       of mLow:
-        result = newIntNodeT(firstOrd(g.config, n.sons[1].typ), n, g)
+        if skipTypes(n[1].typ, abstractVarRange).kind in tyFloat..tyFloat64:
+          result = newFloatNodeT(firstFloat(n[1].typ), n, g)
+        else:
+          result = newIntNodeT(firstOrd(g.config, n[1].typ), n, idgen, g)
       of mHigh:
-        if skipTypes(n.sons[1].typ, abstractVar).kind notin
-            {tySequence, tyString, tyCString, tyOpenArray, tyVarargs}:
-          result = newIntNodeT(lastOrd(g.config, skipTypes(n[1].typ, abstractVar)), n, g)
+        if skipTypes(n[1].typ, abstractVar+{tyUserTypeClassInst}).kind notin
+            {tySequence, tyString, tyCstring, tyOpenArray, tyVarargs}:
+          if skipTypes(n[1].typ, abstractVarRange).kind in tyFloat..tyFloat64:
+            result = newFloatNodeT(lastFloat(n[1].typ), n, g)
+          else:
+            result = newIntNodeT(lastOrd(g.config, skipTypes(n[1].typ, abstractVar)), n, idgen, g)
         else:
-          var a = getArrayConstr(m, n.sons[1], g)
+          var a = getArrayConstr(m, n[1], idgen, g)
           if a.kind == nkBracket:
             # we can optimize it away:
-            result = newIntNodeT(sonsLen(a)-1, n, g)
+            result = newIntNodeT(toInt128(a.len-1), n, idgen, g)
       of mLengthOpenArray:
-        var a = getArrayConstr(m, n.sons[1], g)
+        var a = getArrayConstr(m, n[1], idgen, g)
         if a.kind == nkBracket:
           # we can optimize it away! This fixes the bug ``len(134)``.
-          result = newIntNodeT(sonsLen(a), n, g)
+          result = newIntNodeT(toInt128(a.len), n, idgen, g)
         else:
-          result = magicCall(m, n, g)
+          result = magicCall(m, n, idgen, g)
       of mLengthArray:
         # It doesn't matter if the argument is const or not for mLengthArray.
         # This fixes bug #544.
-        result = newIntNodeT(lengthOrd(g.config, n.sons[1].typ), n, g)
+        result = newIntNodeT(lengthOrd(g.config, n[1].typ), n, idgen, g)
+      of mSizeOf:
+        result = foldSizeOf(g.config, n, nil)
+      of mAlignOf:
+        result = foldAlignOf(g.config, n, nil)
+      of mOffsetOf:
+        result = foldOffsetOf(g.config, n, nil)
       of mAstToStr:
         result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, g)
       of mConStrStr:
-        result = foldConStrStr(m, n, g)
+        result = foldConStrStr(m, n, idgen, g)
       of mIs:
-        let a = getConstExpr(m, n[1], g)
-        if a != nil and a.kind == nkSym and a.sym.kind == skType:
-          result = evalIs(n, a)
+        # The only kind of mIs node that comes here is one depending on some
+        # generic parameter and that's (hopefully) handled at instantiation time
+        discard
       else:
-        result = magicCall(m, n, g)
-    except OverflowError:
+        result = magicCall(m, n, idgen, g)
+    except OverflowDefect:
       localError(g.config, n.info, "over- or underflow")
-    except DivByZeroError:
+    except DivByZeroDefect:
       localError(g.config, n.info, "division by zero")
   of nkAddr:
-    var a = getConstExpr(m, n.sons[0], g)
-    if a != nil:
-      result = n
-      n.sons[0] = a
-  of nkBracket:
-    result = copyTree(n)
-    for i in countup(0, sonsLen(n) - 1):
-      var a = getConstExpr(m, n.sons[i], g)
+    result = nil # don't fold paths containing nkAddr
+  of nkBracket, nkCurly:
+    result = copyNode(n)
+    for son in n.items:
+      var a = getConstExpr(m, son, idgen, g)
       if a == nil: return nil
-      result.sons[i] = a
+      result.add a
     incl(result.flags, nfAllConst)
   of nkRange:
-    var a = getConstExpr(m, n.sons[0], g)
+    var a = getConstExpr(m, n[0], idgen, g)
     if a == nil: return
-    var b = getConstExpr(m, n.sons[1], g)
+    var b = getConstExpr(m, n[1], idgen, g)
     if b == nil: return
     result = copyNode(n)
-    addSon(result, a)
-    addSon(result, b)
-  of nkCurly:
-    result = copyTree(n)
-    for i in countup(0, sonsLen(n) - 1):
-      var a = getConstExpr(m, n.sons[i], g)
-      if a == nil: return nil
-      result.sons[i] = a
-    incl(result.flags, nfAllConst)
+    result.add a
+    result.add b
   #of nkObjConstr:
   #  result = copyTree(n)
-  #  for i in countup(1, sonsLen(n) - 1):
-  #    var a = getConstExpr(m, n.sons[i].sons[1])
+  #  for i in 1..<n.len:
+  #    var a = getConstExpr(m, n[i][1])
   #    if a == nil: return nil
-  #    result.sons[i].sons[1] = a
+  #    result[i][1] = a
   #  incl(result.flags, nfAllConst)
   of nkPar, nkTupleConstr:
     # tuple constructor
-    result = copyTree(n)
-    if (sonsLen(n) > 0) and (n.sons[0].kind == nkExprColonExpr):
-      for i in countup(0, sonsLen(n) - 1):
-        var a = getConstExpr(m, n.sons[i].sons[1], g)
+    result = copyNode(n)
+    if (n.len > 0) and (n[0].kind == nkExprColonExpr):
+      for expr in n.items:
+        let exprNew = copyNode(expr) # nkExprColonExpr
+        exprNew.add expr[0]
+        let a = getConstExpr(m, expr[1], idgen, g)
         if a == nil: return nil
-        result.sons[i].sons[1] = a
+        exprNew.add a
+        result.add exprNew
     else:
-      for i in countup(0, sonsLen(n) - 1):
-        var a = getConstExpr(m, n.sons[i], g)
+      for expr in n.items:
+        let a = getConstExpr(m, expr, idgen, g)
         if a == nil: return nil
-        result.sons[i] = a
+        result.add a
     incl(result.flags, nfAllConst)
   of nkChckRangeF, nkChckRange64, nkChckRange:
-    var a = getConstExpr(m, n.sons[0], g)
+    var a = getConstExpr(m, n[0], idgen, g)
     if a == nil: return
-    if leValueConv(n.sons[1], a) and leValueConv(a, n.sons[2]):
+    if leValueConv(n[1], a) and leValueConv(a, n[2]):
       result = a              # a <= x and x <= b
       result.typ = n.typ
+    elif n.typ.kind in {tyUInt..tyUInt64}:
+      discard "don't check uints"
     else:
       localError(g.config, n.info,
         "conversion from $1 to $2 is invalid" %
-          [typeToString(n.sons[0].typ), typeToString(n.typ)])
+          [typeToString(n[0].typ), typeToString(n.typ)])
   of nkStringToCString, nkCStringToString:
-    var a = getConstExpr(m, n.sons[0], g)
+    var a = getConstExpr(m, n[0], idgen, g)
     if a == nil: return
     result = a
     result.typ = n.typ
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
-    var a = getConstExpr(m, n.sons[1], g)
+    var a = getConstExpr(m, n[1], idgen, g)
     if a == nil: return
-    result = foldConv(n, a, g, check=n.kind == nkHiddenStdConv)
+    result = foldConv(n, a, idgen, g, check=true)
+  of nkDerefExpr, nkHiddenDeref:
+    let a = getConstExpr(m, n[0], idgen, g)
+    if a != nil and a.kind == nkNilLit:
+      result = nil
+      #localError(g.config, n.info, "nil dereference is not allowed")
   of nkCast:
-    var a = getConstExpr(m, n.sons[1], g)
+    var a = getConstExpr(m, n[1], idgen, g)
     if a == nil: return
-    if n.typ != nil and n.typ.kind in NilableTypes:
+    if n.typ != nil and n.typ.kind in NilableTypes and
+        not (n.typ.kind == tyProc and a.typ.kind == tyProc):
       # we allow compile-time 'cast' for pointer types:
       result = a
       result.typ = n.typ
-  of nkBracketExpr: result = foldArrayAccess(m, n, g)
-  of nkDotExpr: result = foldFieldAccess(m, n, g)
+  of nkBracketExpr: result = foldArrayAccess(m, n, idgen, g)
+  of nkDotExpr: result = foldFieldAccess(m, n, idgen, g)
+  of nkCheckedFieldExpr:
+    assert n[0].kind == nkDotExpr
+    result = foldFieldAccess(m, n[0], idgen, g)
   of nkStmtListExpr:
-    if n.len == 2 and n[0].kind == nkComesFrom:
-      result = getConstExpr(m, n[1], g)
+    var i = 0
+    while i <= n.len - 2:
+      if n[i].kind in {nkComesFrom, nkCommentStmt, nkEmpty}: i.inc
+      else: break
+    if i == n.len - 1:
+      result = getConstExpr(m, n[i], idgen, g)
   else:
     discard
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index cc03db1c2..2639aba6c 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -19,8 +19,8 @@
 
 proc getIdentNode(c: PContext; n: PNode): PNode =
   case n.kind
-  of nkPostfix: result = getIdentNode(c, n.sons[1])
-  of nkPragmaExpr: result = getIdentNode(c, n.sons[0])
+  of nkPostfix: result = getIdentNode(c, n[1])
+  of nkPragmaExpr: result = getIdentNode(c, n[0])
   of nkIdent, nkAccQuoted, nkSym: result = n
   else:
     illFormedAst(n, c.config)
@@ -28,13 +28,16 @@ proc getIdentNode(c: PContext; n: PNode): PNode =
 
 type
   GenericCtx = object
-    toMixin: IntSet
+    toMixin, toBind: IntSet
     cursorInBody: bool # only for nimsuggest
     bracketExpr: PNode
 
-type
   TSemGenericFlag = enum
-    withinBind, withinTypeDesc, withinMixin, withinConcept
+    withinBind,
+    withinTypeDesc,
+    withinMixin,
+    withinConcept
+
   TSemGenericFlags = set[TSemGenericFlag]
 
 proc semGenericStmt(c: PContext, n: PNode,
@@ -47,120 +50,214 @@ proc semGenericStmtScope(c: PContext, n: PNode,
   result = semGenericStmt(c, n, flags, ctx)
   closeScope(c)
 
-template macroToExpand(s): untyped =
-  s.kind in {skMacro, skTemplate} and (s.typ.len == 1 or sfAllUntyped in s.flags)
+template isMixedIn(sym): bool =
+  let s = sym
+  s.name.id in ctx.toMixin or (withinConcept in flags and
+                               s.magic == mNone and
+                               s.kind in OverloadableSyms)
 
-template macroToExpandSym(s): untyped =
-  s.kind in {skMacro, skTemplate} and (s.typ.len == 1) and not fromDotExpr
+template canOpenSym(s): bool =
+  {withinMixin, withinConcept} * flags == {withinMixin} and s.id notin ctx.toBind
 
 proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
-                          ctx: var GenericCtx; fromDotExpr=false): PNode =
+                          ctx: var GenericCtx; flags: TSemGenericFlags,
+                          isAmbiguous: bool,
+                          fromDotExpr=false): PNode =
+  result = nil
   semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
   incl(s.flags, sfUsed)
+  template maybeDotChoice(c: PContext, n: PNode, s: PSym, fromDotExpr: bool) =
+    if fromDotExpr:
+      result = symChoice(c, n, s, scForceOpen)
+      if result.kind == nkOpenSymChoice and result.len == 1:
+        result.transitionSonsKind(nkClosedSymChoice)
+    else:
+      result = symChoice(c, n, s, scOpen)
+      if canOpenSym(s):
+        if openSym in c.features:
+          if result.kind == nkSym:
+            result = newOpenSym(result)
+          else:
+            result.typ = nil
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
   case s.kind
   of skUnknown:
     # Introduced in this pass! Leave it as an identifier.
     result = n
-  of skProc, skFunc, skMethod, skIterator, skConverter, skModule:
-    result = symChoice(c, n, s, scOpen)
-  of skTemplate:
-    if macroToExpandSym(s):
-      styleCheckUse(n.info, s)
-      result = semTemplateExpr(c, n, s, {efNoSemCheck})
-      result = semGenericStmt(c, result, {}, ctx)
-    else:
-      result = symChoice(c, n, s, scOpen)
-  of skMacro:
-    if macroToExpandSym(s):
-      styleCheckUse(n.info, s)
-      result = semMacroExpr(c, n, n, s, {efNoSemCheck})
+  of skProc, skFunc, skMethod, skIterator, skConverter, skModule, skEnumField:
+    maybeDotChoice(c, n, s, fromDotExpr)
+  of skTemplate, skMacro:
+    # alias syntax, see semSym for skTemplate, skMacro
+    if sfNoalias notin s.flags and not fromDotExpr:
+      onUse(n.info, s)
+      case s.kind
+      of skTemplate: result = semTemplateExpr(c, n, s, {efNoSemCheck})
+      of skMacro: result = semMacroExpr(c, n, n, s, {efNoSemCheck})
+      else: discard # unreachable
+      c.friendModules.add(s.owner.getModule)
       result = semGenericStmt(c, result, {}, ctx)
+      discard c.friendModules.pop()
     else:
-      result = symChoice(c, n, s, scOpen)
+      maybeDotChoice(c, n, s, fromDotExpr)
   of skGenericParam:
     if s.typ != nil and s.typ.kind == tyStatic:
       if s.typ.n != nil:
         result = s.typ.n
+      elif c.inGenericContext > 0 and withinConcept notin flags:
+        # don't leave generic param as identifier node in generic type,
+        # sigmatch will try to instantiate generic type AST without all params
+        # fine to give a symbol node a generic type here since
+        # we are in a generic context and `prepareNode` will be called
+        result = newSymNodeTypeDesc(s, c.idgen, n.info)
+        if canOpenSym(result.sym):
+          if openSym in c.features:
+            result = newOpenSym(result)
+          else:
+            result.flags.incl nfDisabledOpenSym
+            result.typ = nil
       else:
         result = n
     else:
-      result = newSymNodeTypeDesc(s, n.info)
-    styleCheckUse(n.info, s)
+      result = newSymNodeTypeDesc(s, c.idgen, n.info)
+      if canOpenSym(result.sym):
+        if openSym in c.features:
+          result = newOpenSym(result)
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
+    onUse(n.info, s)
   of skParam:
     result = n
-    styleCheckUse(n.info, s)
+    onUse(n.info, s)
   of skType:
     if (s.typ != nil) and
        (s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
-      result = newSymNodeTypeDesc(s, n.info)
+      if isAmbiguous:
+        # ambiguous types should be symchoices since lookup behaves
+        # differently for them in regular expressions
+        maybeDotChoice(c, n, s, fromDotExpr)
+        return
+      result = newSymNodeTypeDesc(s, c.idgen, n.info)
+      if canOpenSym(result.sym):
+        if openSym in c.features:
+          result = newOpenSym(result)
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
+    elif c.inGenericContext > 0 and withinConcept notin flags:
+      # don't leave generic param as identifier node in generic type,
+      # sigmatch will try to instantiate generic type AST without all params
+      # fine to give a symbol node a generic type here since
+      # we are in a generic context and `prepareNode` will be called
+      result = newSymNodeTypeDesc(s, c.idgen, n.info)
+      if canOpenSym(result.sym):
+        if openSym in c.features:
+          result = newOpenSym(result)
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
     else:
       result = n
-    styleCheckUse(n.info, s)
+    onUse(n.info, s)
   else:
     result = newSymNode(s, n.info)
-    styleCheckUse(n.info, s)
+    if canOpenSym(result.sym):
+      if openSym in c.features:
+        result = newOpenSym(result)
+      else:
+        result.flags.incl nfDisabledOpenSym
+        result.typ = nil
+    onUse(n.info, s)
 
 proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
             ctx: var GenericCtx): PNode =
   result = n
   let ident = considerQuotedIdent(c, n)
-  var s = searchInScopes(c, ident).skipAlias(n, c.config)
+  var amb = false
+  var s = searchInScopes(c, ident, amb)
   if s == nil:
     s = strTableGet(c.pureEnumFields, ident)
-    if s != nil and contains(c.ambiguousSymbols, s.id):
-      s = nil
+    #if s != nil and contains(c.ambiguousSymbols, s.id):
+    #  s = nil
   if s == nil:
     if ident.id notin ctx.toMixin and withinMixin notin flags:
       errorUndeclaredIdentifier(c, n.info, ident.s)
   else:
-    if withinBind in flags:
+    if withinBind in flags or s.id in ctx.toBind:
       result = symChoice(c, n, s, scClosed)
-    elif s.name.id in ctx.toMixin:
+    elif s.isMixedIn:
       result = symChoice(c, n, s, scForceOpen)
     else:
-      result = semGenericStmtSymbol(c, n, s, ctx)
+      result = semGenericStmtSymbol(c, n, s, ctx, flags, amb)
   # else: leave as nkIdent
 
 proc newDot(n, b: PNode): PNode =
   result = newNodeI(nkDotExpr, n.info)
-  result.add(n.sons[0])
+  result.add(n[0])
   result.add(b)
 
 proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
-                 ctx: var GenericCtx; isMacro: var bool): PNode =
+                 ctx: var GenericCtx; isMacro: var bool;
+                 inCall = false): PNode =
   assert n.kind == nkDotExpr
   semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
 
   let luf = if withinMixin notin flags: {checkUndeclared, checkModule} else: {checkModule}
 
+  c.isAmbiguous = false
   var s = qualifiedLookUp(c, n, luf)
   if s != nil:
-    result = semGenericStmtSymbol(c, n, s, ctx)
+    isMacro = s.kind in {skTemplate, skMacro}
+    result = semGenericStmtSymbol(c, n, s, ctx, flags, c.isAmbiguous)
   else:
-    n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
+    n[0] = semGenericStmt(c, n[0], flags, ctx)
     result = n
     let n = n[1]
     let ident = considerQuotedIdent(c, n)
-    var s = searchInScopes(c, ident).skipAlias(n, c.config)
-    if s != nil and s.kind in routineKinds:
+    # could be type conversion if like a.T and not a.T()
+    let symKinds = if inCall: routineKinds else: routineKinds+{skType}
+    var candidates = searchInScopesFilterBy(c, ident, symKinds)
+    if candidates.len > 0:
+      let s = candidates[0] # XXX take into account the other candidates!
       isMacro = s.kind in {skTemplate, skMacro}
-      if withinBind in flags:
-        result = newDot(result, symChoice(c, n, s, scClosed))
-      elif s.name.id in ctx.toMixin:
+      if withinBind in flags or s.id in ctx.toBind:
+        if s.kind == skType: # don't put types in sym choice
+          var ambig = false
+          if candidates.len > 1:
+            let s2 = searchInScopes(c, ident, ambig)
+          result = newDot(result, semGenericStmtSymbol(c, n, s, ctx, flags,
+            isAmbiguous = ambig, fromDotExpr = true))
+        else:
+          result = newDot(result, symChoice(c, n, s, scClosed))
+      elif s.isMixedIn:
         result = newDot(result, symChoice(c, n, s, scForceOpen))
       else:
-        let syms = semGenericStmtSymbol(c, n, s, ctx, fromDotExpr=true)
-        if syms.kind == nkSym:
-          let choice = symChoice(c, n, s, scForceOpen)
-          choice.kind = nkClosedSymChoice
-          result = newDot(result, choice)
-        else:
-          result = newDot(result, syms)
+        var ambig = false
+        if s.kind == skType and candidates.len > 1:
+          discard searchInScopes(c, ident, ambig)
+        let syms = semGenericStmtSymbol(c, n, s, ctx, flags,
+          isAmbiguous = ambig, fromDotExpr = true)
+        result = newDot(result, syms)
 
 proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
   let s = newSymS(skUnknown, getIdentNode(c, n), c)
   addPrelimDecl(c, s)
-  styleCheckDef(c.config, n.info, s, kind)
+  styleCheckDef(c, n.info, s, kind)
+  onDef(n.info, s)
+
+proc addTempDeclToIdents(c: PContext; n: PNode; kind: TSymKind; inCall: bool) =
+  case n.kind 
+  of nkIdent:
+    if inCall:
+      addTempDecl(c, n, kind)
+  of nkCallKinds:
+    for s in n:
+      addTempDeclToIdents(c, s, kind, true)  
+  else:
+    for s in n:
+      addTempDeclToIdents(c, s, kind, inCall)
 
 proc semGenericStmt(c: PContext, n: PNode,
                     flags: TSemGenericFlags, ctx: var GenericCtx): PNode =
@@ -175,12 +272,15 @@ proc semGenericStmt(c: PContext, n: PNode,
   case n.kind
   of nkIdent, nkAccQuoted:
     result = lookup(c, n, flags, ctx)
+    if result != nil and result.kind == nkSym:
+      assert result.sym != nil
+      markUsed(c, n.info, result.sym)
   of nkDotExpr:
     #let luf = if withinMixin notin flags: {checkUndeclared} else: {}
     #var s = qualifiedLookUp(c, n, luf)
     #if s != nil: result = semGenericStmtSymbol(c, n, s)
     # XXX for example: ``result.add`` -- ``add`` needs to be looked up here...
-    var dummy: bool
+    var dummy: bool = false
     result = fuzzyLookup(c, n, flags, ctx, dummy)
   of nkSym:
     let a = n.sym
@@ -196,15 +296,19 @@ proc semGenericStmt(c: PContext, n: PNode,
     # in the generic instantiation process...
     discard
   of nkBind:
-    result = semGenericStmt(c, n.sons[0], flags+{withinBind}, ctx)
+    result = semGenericStmt(c, n[0], flags+{withinBind}, ctx)
   of nkMixinStmt:
     result = semMixinStmt(c, n, ctx.toMixin)
+  of nkBindStmt:
+    result = semBindStmt(c, n, ctx.toBind)
   of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit:
     # check if it is an expression macro:
     checkMinSonsLen(n, 1, c.config)
-    let fn = n.sons[0]
+    let fn = n[0]
+    c.isAmbiguous = false
     var s = qualifiedLookUp(c, fn, {})
-    if  s == nil and
+    let ambig = c.isAmbiguous
+    if s == nil and
         {withinMixin, withinConcept}*flags == {} and
         fn.kind in {nkIdent, nkAccQuoted} and
         considerQuotedIdent(c, fn).id notin ctx.toMixin:
@@ -214,26 +318,25 @@ proc semGenericStmt(c: PContext, n: PNode,
     var mixinContext = false
     if s != nil:
       incl(s.flags, sfUsed)
-      mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles}
-      let sc = symChoice(c, fn, s,
-            if s.name.id in ctx.toMixin: scForceOpen else: scOpen)
+      mixinContext = s.magic in {mDefined, mDeclared, mDeclaredInScope, mCompiles, mAstToStr}
+      let whichChoice = if s.id in ctx.toBind: scClosed
+                        elif s.isMixedIn: scForceOpen
+                        else: scOpen
+      let sc = symChoice(c, fn, s, whichChoice)
       case s.kind
-      of skMacro:
-        if macroToExpand(s) and sc.safeLen <= 1:
-          styleCheckUse(fn.info, s)
-          result = semMacroExpr(c, n, n, s, {efNoSemCheck})
+      of skMacro, skTemplate:
+        # unambiguous macros/templates are expanded if all params are untyped
+        if sfAllUntyped in s.flags and sc.safeLen <= 1:
+          onUse(fn.info, s)
+          case s.kind
+          of skMacro: result = semMacroExpr(c, n, n, s, {efNoSemCheck})
+          of skTemplate: result = semTemplateExpr(c, n, s, {efNoSemCheck})
+          else: discard # unreachable
+          c.friendModules.add(s.owner.getModule)
           result = semGenericStmt(c, result, flags, ctx)
+          discard c.friendModules.pop()
         else:
-          n.sons[0] = sc
-          result = n
-        mixinContext = true
-      of skTemplate:
-        if macroToExpand(s) and sc.safeLen <= 1:
-          styleCheckUse(fn.info, s)
-          result = semTemplateExpr(c, n, s, {efNoSemCheck})
-          result = semGenericStmt(c, result, flags, ctx)
-        else:
-          n.sons[0] = sc
+          n[0] = sc
           result = n
         # BUGFIX: we must not return here, we need to do first phase of
         # symbol lookup. Also since templates and macros can do scope injections
@@ -244,243 +347,313 @@ proc semGenericStmt(c: PContext, n: PNode,
         # Leave it as an identifier.
         discard
       of skProc, skFunc, skMethod, skIterator, skConverter, skModule:
-        result.sons[0] = sc
-        # do not check of 's.magic==mRoof' here because it might be some
-        # other '^' but after overload resolution the proper one:
-        if ctx.bracketExpr != nil and n.len == 2 and s.name.s == "^":
-          result.add ctx.bracketExpr
+        result[0] = sc
         first = 1
+        # We're not interested in the example code during this pass so let's
+        # skip it
+        if s.magic == mRunnableExamples:
+          first = result.safeLen # see trunnableexamples.fun3
       of skGenericParam:
-        result.sons[0] = newSymNodeTypeDesc(s, fn.info)
-        styleCheckUse(fn.info, s)
+        result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info)
+        onUse(fn.info, s)
         first = 1
       of skType:
         # bad hack for generics:
         if (s.typ != nil) and (s.typ.kind != tyGenericParam):
-          result.sons[0] = newSymNodeTypeDesc(s, fn.info)
-          styleCheckUse(fn.info, s)
+          if ambig:
+            # ambiguous types should be symchoices since lookup behaves
+            # differently for them in regular expressions
+            result[0] = sc
+          else:
+            result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info)
+          onUse(fn.info, s)
           first = 1
       else:
-        result.sons[0] = newSymNode(s, fn.info)
-        styleCheckUse(fn.info, s)
+        result[0] = newSymNode(s, fn.info)
+        onUse(fn.info, s)
         first = 1
     elif fn.kind == nkDotExpr:
-      result.sons[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext)
+      result[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext, inCall = true)
       first = 1
     # Consider 'when declared(globalsSlot): ThreadVarSetValue(globalsSlot, ...)'
     # in threads.nim: the subtle preprocessing here binds 'globalsSlot' which
     # is not exported and yet the generic 'threadProcWrapper' works correctly.
     let flags = if mixinContext: flags+{withinMixin} else: flags
-    for i in countup(first, sonsLen(result) - 1):
-      result.sons[i] = semGenericStmt(c, result.sons[i], flags, ctx)
+    for i in first..<result.safeLen:
+      result[i] = semGenericStmt(c, result[i], flags, ctx)
   of nkCurlyExpr:
     result = newNodeI(nkCall, n.info)
     result.add newIdentNode(getIdent(c.cache, "{}"), n.info)
-    for i in 0 ..< n.len: result.add(n[i])
+    for i in 0..<n.len: result.add(n[i])
     result = semGenericStmt(c, result, flags, ctx)
   of nkBracketExpr:
     result = newNodeI(nkCall, n.info)
     result.add newIdentNode(getIdent(c.cache, "[]"), n.info)
-    for i in 0 ..< n.len: result.add(n[i])
-    withBracketExpr ctx, n.sons[0]:
-      result = semGenericStmt(c, result, flags, ctx)
-  of nkAsgn, nkFastAsgn:
+    for i in 0..<n.len: result.add(n[i])
+    result = semGenericStmt(c, result, flags, ctx)
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     checkSonsLen(n, 2, c.config)
-    let a = n.sons[0]
-    let b = n.sons[1]
+    let a = n[0]
+    let b = n[1]
 
     let k = a.kind
     case k
     of nkCurlyExpr:
       result = newNodeI(nkCall, n.info)
       result.add newIdentNode(getIdent(c.cache, "{}="), n.info)
-      for i in 0 ..< a.len: result.add(a[i])
+      for i in 0..<a.len: result.add(a[i])
       result.add(b)
       result = semGenericStmt(c, result, flags, ctx)
     of nkBracketExpr:
       result = newNodeI(nkCall, n.info)
       result.add newIdentNode(getIdent(c.cache, "[]="), n.info)
-      for i in 0 ..< a.len: result.add(a[i])
+      for i in 0..<a.len: result.add(a[i])
       result.add(b)
-      withBracketExpr ctx, a.sons[0]:
-        result = semGenericStmt(c, result, flags, ctx)
+      result = semGenericStmt(c, result, flags, ctx)
     else:
-      for i in countup(0, sonsLen(n) - 1):
-        result.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx)
+      for i in 0..<n.len:
+        result[i] = semGenericStmt(c, n[i], flags, ctx)
   of nkIfStmt:
-    for i in countup(0, sonsLen(n)-1):
-      n.sons[i] = semGenericStmtScope(c, n.sons[i], flags, ctx)
+    for i in 0..<n.len:
+      n[i] = semGenericStmtScope(c, n[i], flags, ctx)
   of nkWhenStmt:
-    for i in countup(0, sonsLen(n)-1):
-      n.sons[i] = semGenericStmt(c, n.sons[i], flags+{withinMixin}, ctx)
+    for i in 0..<n.len:
+      # bug #8603: conditions of 'when' statements are not
+      # in a 'mixin' context:
+      let it = n[i]
+      if it.kind in {nkElifExpr, nkElifBranch}:
+        n[i][0] = semGenericStmt(c, it[0], flags, ctx)
+        n[i][1] = semGenericStmt(c, it[1], flags+{withinMixin}, ctx)
+      else:
+        n[i] = semGenericStmt(c, it, flags+{withinMixin}, ctx)
   of nkWhileStmt:
     openScope(c)
-    for i in countup(0, sonsLen(n)-1):
-      n.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx)
+    for i in 0..<n.len:
+      n[i] = semGenericStmt(c, n[i], flags, ctx)
     closeScope(c)
   of nkCaseStmt:
     openScope(c)
-    n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
-    for i in countup(1, sonsLen(n)-1):
-      var a = n.sons[i]
+    n[0] = semGenericStmt(c, n[0], flags, ctx)
+    for i in 1..<n.len:
+      var a = n[i]
       checkMinSonsLen(a, 1, c.config)
-      var L = sonsLen(a)
-      for j in countup(0, L-2):
-        a.sons[j] = semGenericStmt(c, a.sons[j], flags, ctx)
-      a.sons[L - 1] = semGenericStmtScope(c, a.sons[L-1], flags, ctx)
+      for j in 0..<a.len-1:
+        a[j] = semGenericStmt(c, a[j], flags+{withinMixin}, ctx)
+        addTempDeclToIdents(c, a[j], skVar, false)
+
+      a[^1] = semGenericStmtScope(c, a[^1], flags, ctx)
     closeScope(c)
   of nkForStmt, nkParForStmt:
-    var L = sonsLen(n)
     openScope(c)
-    n.sons[L - 2] = semGenericStmt(c, n.sons[L-2], flags, ctx)
-    for i in countup(0, L - 3):
-      addTempDecl(c, n.sons[i], skForVar)
+    n[^2] = semGenericStmt(c, n[^2], flags, ctx)
+    for i in 0..<n.len - 2:
+      if (n[i].kind == nkVarTuple):
+        for s in n[i]:
+          if (s.kind == nkIdent):
+            addTempDecl(c,s,skForVar)
+      else:
+        addTempDecl(c, n[i], skForVar)
     openScope(c)
-    n.sons[L - 1] = semGenericStmt(c, n.sons[L-1], flags, ctx)
+    n[^1] = semGenericStmt(c, n[^1], flags, ctx)
     closeScope(c)
     closeScope(c)
   of nkBlockStmt, nkBlockExpr, nkBlockType:
     checkSonsLen(n, 2, c.config)
     openScope(c)
-    if n.sons[0].kind != nkEmpty:
-      addTempDecl(c, n.sons[0], skLabel)
-    n.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx)
+    if n[0].kind != nkEmpty:
+      addTempDecl(c, n[0], skLabel)
+    n[1] = semGenericStmt(c, n[1], flags, ctx)
     closeScope(c)
-  of nkTryStmt:
+  of nkTryStmt, nkHiddenTryStmt:
     checkMinSonsLen(n, 2, c.config)
-    n.sons[0] = semGenericStmtScope(c, n.sons[0], flags, ctx)
-    for i in countup(1, sonsLen(n)-1):
-      var a = n.sons[i]
+    n[0] = semGenericStmtScope(c, n[0], flags, ctx)
+    for i in 1..<n.len:
+      var a = n[i]
       checkMinSonsLen(a, 1, c.config)
-      var L = sonsLen(a)
       openScope(c)
-      for j in countup(0, L-2):
-        if a.sons[j].isInfixAs():
-          addTempDecl(c, getIdentNode(c, a.sons[j][2]), skLet)
-          a.sons[j].sons[1] = semGenericStmt(c, a.sons[j][1], flags+{withinTypeDesc}, ctx)
+      for j in 0..<a.len-1:
+        if a[j].isInfixAs():
+          addTempDecl(c, getIdentNode(c, a[j][2]), skLet)
+          a[j][1] = semGenericStmt(c, a[j][1], flags+{withinTypeDesc}, ctx)
         else:
-          a.sons[j] = semGenericStmt(c, a.sons[j], flags+{withinTypeDesc}, ctx)
-      a.sons[L-1] = semGenericStmtScope(c, a.sons[L-1], flags, ctx)
+          a[j] = semGenericStmt(c, a[j], flags+{withinTypeDesc}, ctx)
+      a[^1] = semGenericStmtScope(c, a[^1], flags, ctx)
       closeScope(c)
 
-  of nkVarSection, nkLetSection:
-    for i in countup(0, sonsLen(n) - 1):
-      var a = n.sons[i]
-      if a.kind == nkCommentStmt: continue
-      if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a, c.config)
-      checkMinSonsLen(a, 3, c.config)
-      var L = sonsLen(a)
-      a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
-      a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
-      for j in countup(0, L-3):
-        addTempDecl(c, getIdentNode(c, a.sons[j]), skVar)
+  of nkVarSection, nkLetSection, nkConstSection:
+    let varKind =
+      case n.kind
+      of nkVarSection: skVar
+      of nkLetSection: skLet
+      else: skConst
+    for i in 0..<n.len:
+      var a = n[i]
+      case a.kind:
+      of nkCommentStmt: continue
+      of nkIdentDefs, nkVarTuple, nkConstDef:
+        checkMinSonsLen(a, 3, c.config)
+        a[^2] = semGenericStmt(c, a[^2], flags+{withinTypeDesc}, ctx)
+        a[^1] = semGenericStmt(c, a[^1], flags, ctx)
+        for j in 0..<a.len-2:
+          addTempDecl(c, getIdentNode(c, a[j]), varKind)
+      else:
+        illFormedAst(a, c.config)
   of nkGenericParams:
-    for i in countup(0, sonsLen(n) - 1):
-      var a = n.sons[i]
+    for i in 0..<n.len:
+      var a = n[i]
       if (a.kind != nkIdentDefs): illFormedAst(a, c.config)
       checkMinSonsLen(a, 3, c.config)
-      var L = sonsLen(a)
-      a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
+      a[^2] = semGenericStmt(c, a[^2], flags+{withinTypeDesc}, ctx)
       # do not perform symbol lookup for default expressions
-      for j in countup(0, L-3):
-        addTempDecl(c, getIdentNode(c, a.sons[j]), skType)
-  of nkConstSection:
-    for i in countup(0, sonsLen(n) - 1):
-      var a = n.sons[i]
-      if a.kind == nkCommentStmt: continue
-      if (a.kind != nkConstDef): illFormedAst(a, c.config)
-      checkSonsLen(a, 3, c.config)
-      addTempDecl(c, getIdentNode(c, a.sons[0]), skConst)
-      a.sons[1] = semGenericStmt(c, a.sons[1], flags+{withinTypeDesc}, ctx)
-      a.sons[2] = semGenericStmt(c, a.sons[2], flags, ctx)
+      for j in 0..<a.len-2:
+        addTempDecl(c, getIdentNode(c, a[j]), skType)
   of nkTypeSection:
-    for i in countup(0, sonsLen(n) - 1):
-      var a = n.sons[i]
+    for i in 0..<n.len:
+      var a = n[i]
       if a.kind == nkCommentStmt: continue
       if (a.kind != nkTypeDef): illFormedAst(a, c.config)
       checkSonsLen(a, 3, c.config)
-      addTempDecl(c, getIdentNode(c, a.sons[0]), skType)
-    for i in countup(0, sonsLen(n) - 1):
-      var a = n.sons[i]
+      addTempDecl(c, getIdentNode(c, a[0]), skType)
+    for i in 0..<n.len:
+      var a = n[i]
       if a.kind == nkCommentStmt: continue
       if (a.kind != nkTypeDef): illFormedAst(a, c.config)
       checkSonsLen(a, 3, c.config)
-      if a.sons[1].kind != nkEmpty:
+      if a[1].kind != nkEmpty:
         openScope(c)
-        a.sons[1] = semGenericStmt(c, a.sons[1], flags, ctx)
-        a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, ctx)
+        a[1] = semGenericStmt(c, a[1], flags, ctx)
+        a[2] = semGenericStmt(c, a[2], flags+{withinTypeDesc}, ctx)
         closeScope(c)
       else:
-        a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, ctx)
+        a[2] = semGenericStmt(c, a[2], flags+{withinTypeDesc}, ctx)
   of nkEnumTy:
-    if n.sonsLen > 0:
-      if n.sons[0].kind != nkEmpty:
-        n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx)
-      for i in countup(1, sonsLen(n) - 1):
-        var a: PNode
-        case n.sons[i].kind
-        of nkEnumFieldDef: a = n.sons[i].sons[0]
-        of nkIdent: a = n.sons[i]
+    if n.len > 0:
+      if n[0].kind != nkEmpty:
+        n[0] = semGenericStmt(c, n[0], flags+{withinTypeDesc}, ctx)
+      for i in 1..<n.len:
+        var a: PNode = nil
+        case n[i].kind
+        of nkEnumFieldDef: a = n[i][0]
+        of nkIdent: a = n[i]
         else: illFormedAst(n, c.config)
         addDecl(c, newSymS(skUnknown, getIdentNode(c, a), c))
-  of nkObjectTy, nkTupleTy, nkTupleClassTy:
-    discard
+  of nkTupleTy:
+    for i in 0..<n.len:
+      var a = n[i]
+      case a.kind:
+      of nkCommentStmt, nkNilLit, nkSym, nkEmpty: continue
+      of nkIdentDefs:
+        checkMinSonsLen(a, 3, c.config)
+        a[^2] = semGenericStmt(c, a[^2], flags+{withinTypeDesc}, ctx)
+        a[^1] = semGenericStmt(c, a[^1], flags, ctx)
+        for j in 0..<a.len-2:
+          addTempDecl(c, getIdentNode(c, a[j]), skField)
+      else:
+        illFormedAst(a, c.config)
+  of nkObjectTy:
+    if n.len > 0:
+      openScope(c)
+      for i in 0..<n.len:
+        result[i] = semGenericStmt(c, n[i], flags, ctx)
+      closeScope(c)
+  of nkRecList:
+    for i in 0..<n.len:
+      var a = n[i]
+      case a.kind:
+      of nkCommentStmt, nkNilLit, nkSym, nkEmpty: continue
+      of nkIdentDefs:
+        checkMinSonsLen(a, 3, c.config)
+        a[^2] = semGenericStmt(c, a[^2], flags+{withinTypeDesc}, ctx)
+        a[^1] = semGenericStmt(c, a[^1], flags, ctx)
+        for j in 0..<a.len-2:
+          addTempDecl(c, getIdentNode(c, a[j]), skField)
+      of nkRecCase, nkRecWhen:
+        n[i] = semGenericStmt(c, a, flags, ctx)
+      else:
+        illFormedAst(a, c.config)
+  of nkRecCase:
+    checkSonsLen(n[0], 3, c.config)
+    n[0][^2] = semGenericStmt(c, n[0][^2], flags+{withinTypeDesc}, ctx)
+    n[0][^1] = semGenericStmt(c, n[0][^1], flags, ctx)
+    addTempDecl(c, getIdentNode(c, n[0][0]), skField)
+    for i in 1..<n.len:
+      n[i] = semGenericStmt(c, n[i], flags, ctx)
   of nkFormalParams:
     checkMinSonsLen(n, 1, c.config)
-    if n.sons[0].kind != nkEmpty:
-      n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx)
-    for i in countup(1, sonsLen(n) - 1):
-      var a = n.sons[i]
+    for i in 1..<n.len:
+      var a = n[i]
       if (a.kind != nkIdentDefs): illFormedAst(a, c.config)
       checkMinSonsLen(a, 3, c.config)
-      var L = sonsLen(a)
-      a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
-      a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
-      for j in countup(0, L-3):
-        addTempDecl(c, getIdentNode(c, a.sons[j]), skParam)
+      a[^2] = semGenericStmt(c, a[^2], flags+{withinTypeDesc}, ctx)
+      a[^1] = semGenericStmt(c, a[^1], flags, ctx)
+      for j in 0..<a.len-2:
+        addTempDecl(c, getIdentNode(c, a[j]), skParam)
+    # XXX: last change was moving this down here, search for "1.." to keep
+    #      going from this file onward
+    if n[0].kind != nkEmpty:
+      n[0] = semGenericStmt(c, n[0], flags+{withinTypeDesc}, ctx)
   of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
      nkFuncDef, nkIteratorDef, nkLambdaKinds:
     checkSonsLen(n, bodyPos + 1, c.config)
-    if n.sons[namePos].kind != nkEmpty:
-      addTempDecl(c, getIdentNode(c, n.sons[0]), skProc)
+    if n[namePos].kind != nkEmpty:
+      addTempDecl(c, getIdentNode(c, n[0]), skProc)
     openScope(c)
-    n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos],
+    n[genericParamsPos] = semGenericStmt(c, n[genericParamsPos],
                                               flags, ctx)
-    if n.sons[paramsPos].kind != nkEmpty:
-      if n.sons[paramsPos].sons[0].kind != nkEmpty:
-        addPrelimDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nil, n.info))
-      n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos], flags, ctx)
-    n.sons[pragmasPos] = semGenericStmt(c, n.sons[pragmasPos], flags, ctx)
+    if n[paramsPos].kind != nkEmpty:
+      if n[paramsPos][0].kind != nkEmpty:
+        addPrelimDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), c.idgen, nil, n.info))
+      n[paramsPos] = semGenericStmt(c, n[paramsPos], flags, ctx)
+    n[pragmasPos] = semGenericStmt(c, n[pragmasPos], flags, ctx)
     var body: PNode
-    if n.sons[namePos].kind == nkSym:
-      let s = n.sons[namePos].sym
+    if n[namePos].kind == nkSym:
+      let s = n[namePos].sym
       if sfGenSym in s.flags and s.ast == nil:
-        body = n.sons[bodyPos]
+        body = n[bodyPos]
       else:
-        body = s.getBody
-    else: body = n.sons[bodyPos]
-    n.sons[bodyPos] = semGenericStmtScope(c, body, flags, ctx)
+        body = getBody(c.graph, s)
+    else: body = n[bodyPos]
+    let bodyFlags = if n.kind == nkTemplateDef: flags + {withinMixin} else: flags
+    n[bodyPos] = semGenericStmtScope(c, body, bodyFlags, ctx)
     closeScope(c)
   of nkPragma, nkPragmaExpr: discard
   of nkExprColonExpr, nkExprEqExpr:
     checkMinSonsLen(n, 2, c.config)
-    result.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx)
+    result[1] = semGenericStmt(c, n[1], flags, ctx)
+  of nkObjConstr:
+    for i in 0..<n.len:
+      result[i] = semGenericStmt(c, n[i], flags, ctx)
+    if result[0].kind == nkSym:
+      let fmoduleId = getModule(result[0].sym).id
+      var isVisable = false
+      for module in c.friendModules:
+        if module.id == fmoduleId:
+          isVisable = true
+          break
+      if isVisable:
+        for i in 1..<result.len:
+          if result[i].kind == nkExprColonExpr:
+            result[i][1].flags.incl nfSkipFieldChecking
   else:
-    for i in countup(0, sonsLen(n) - 1):
-      result.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx)
+    for i in 0..<n.len:
+      result[i] = semGenericStmt(c, n[i], flags, ctx)
 
   when defined(nimsuggest):
     if withinTypeDesc in flags: dec c.inTypeContext
 
-
 proc semGenericStmt(c: PContext, n: PNode): PNode =
-  var ctx: GenericCtx
-  ctx.toMixin = initIntset()
+  var ctx = GenericCtx(
+    toMixin: initIntSet(),
+    toBind: initIntSet()
+  )
   result = semGenericStmt(c, n, {}, ctx)
   semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)
 
 proc semConceptBody(c: PContext, n: PNode): PNode =
-  var ctx: GenericCtx
-  ctx.toMixin = initIntset()
+  var ctx = GenericCtx(
+    toMixin: initIntSet(),
+    toBind: initIntSet()
+  )
   result = semGenericStmt(c, n, {withinConcept}, ctx)
   semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)
+
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 95b631850..1bc6d31a2 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -14,11 +14,11 @@ proc addObjFieldsToLocalScope(c: PContext; n: PNode) =
   template rec(n) = addObjFieldsToLocalScope(c, n)
   case n.kind
   of nkRecList:
-    for i in countup(0, len(n)-1):
+    for i in 0..<n.len:
       rec n[i]
   of nkRecCase:
-    if n.len > 0: rec n.sons[0]
-    for i in countup(1, len(n)-1):
+    if n.len > 0: rec n[0]
+    for i in 1..<n.len:
       if n[i].kind in {nkOfBranch, nkElse}: rec lastSon(n[i])
   of nkSym:
     let f = n.sym
@@ -28,85 +28,74 @@ proc addObjFieldsToLocalScope(c: PContext; n: PNode) =
       # it is not an error to shadow fields via parameters
   else: discard
 
-proc rawPushProcCon(c: PContext, owner: PSym) =
-  var x: PProcCon
-  new(x)
-  x.owner = owner
-  x.next = c.p
-  c.p = x
-
-proc rawHandleSelf(c: PContext; owner: PSym) =
-  const callableSymbols = {skProc, skFunc, skMethod, skConverter, skIterator, skMacro}
-  if c.selfName != nil and owner.kind in callableSymbols and owner.typ != nil:
-    let params = owner.typ.n
-    if params.len > 1:
-      let arg = params[1].sym
-      if arg.name.id == c.selfName.id:
-        c.p.selfSym = arg
-        arg.flags.incl sfIsSelf
-        var t = c.p.selfSym.typ.skipTypes(abstractPtrs)
-        while t.kind == tyObject:
-          addObjFieldsToLocalScope(c, t.n)
-          if t.sons[0] == nil: break
-          t = t.sons[0].skipTypes(skipPtrs)
-
 proc pushProcCon*(c: PContext; owner: PSym) =
-  rawPushProcCon(c, owner)
-  rawHandleSelf(c, owner)
+  c.p = PProcCon(owner: owner, next: c.p)
 
 const
   errCannotInstantiateX = "cannot instantiate: '$1'"
 
-iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym =
+iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TypeMapping): PSym =
   internalAssert c.config, n.kind == nkGenericParams
-  for i, a in n.pairs:
+  for a in n.items:
     internalAssert c.config, a.kind == nkSym
     var q = a.sym
-    if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic}+tyTypeClasses:
-      continue
-    let symKind = if q.typ.kind == tyStatic: skConst else: skType
-    var s = newSym(symKind, q.name, getCurrOwner(c), q.info)
-    s.flags = s.flags + {sfUsed, sfFromGeneric}
-    var t = PType(idTableGet(pt, q.typ))
-    if t == nil:
-      if tfRetType in q.typ.flags:
-        # keep the generic type and allow the return type to be bound
-        # later by semAsgn in return type inference scenario
-        t = q.typ
-      else:
-        localError(c.config, a.info, errCannotInstantiateX % s.name.s)
+    if q.typ.kind in {tyTypeDesc, tyGenericParam, tyStatic, tyConcept}+tyTypeClasses:
+      let symKind = if q.typ.kind == tyStatic: skConst else: skType
+      var s = newSym(symKind, q.name, c.idgen, getCurrOwner(c), q.info)
+      s.flags.incl {sfUsed, sfFromGeneric}
+      var t = idTableGet(pt, q.typ)
+      if t == nil:
+        if tfRetType in q.typ.flags:
+          # keep the generic type and allow the return type to be bound
+          # later by semAsgn in return type inference scenario
+          t = q.typ
+        else:
+          if q.typ.kind != tyCompositeTypeClass:
+            localError(c.config, a.info, errCannotInstantiateX % s.name.s)
+          t = errorType(c)
+      elif t.kind in {tyGenericParam, tyConcept, tyFromExpr}:
+        localError(c.config, a.info, errCannotInstantiateX % q.name.s)
         t = errorType(c)
-    elif t.kind == tyGenericParam:
-      localError(c.config, a.info, errCannotInstantiateX % q.name.s)
-      t = errorType(c)
-    elif t.kind == tyGenericInvocation:
-      #t = instGenericContainer(c, a, t)
-      t = generateTypeInstance(c, pt, a, t)
-      #t = ReplaceTypeVarsT(cl, t)
-    s.typ = t
-    if t.kind == tyStatic: s.ast = t.n
-    yield s
+      elif isUnresolvedStatic(t) and (q.typ.kind == tyStatic or
+            (q.typ.kind == tyGenericParam and
+              q.typ.genericParamHasConstraints and
+              q.typ.genericConstraint.kind == tyStatic)) and
+          c.inGenericContext == 0 and c.matchedConcept == nil:
+        # generic/concept type bodies will try to instantiate static values but
+        # won't actually use them
+        localError(c.config, a.info, errCannotInstantiateX % q.name.s)
+        t = errorType(c)
+      elif t.kind == tyGenericInvocation:
+        #t = instGenericContainer(c, a, t)
+        t = generateTypeInstance(c, pt, a, t)
+        #t = ReplaceTypeVarsT(cl, t)
+      s.typ = t
+      if t.kind == tyStatic: s.ast = t.n
+      yield s
 
 proc sameInstantiation(a, b: TInstantiation): bool =
   if a.concreteTypes.len == b.concreteTypes.len:
     for i in 0..a.concreteTypes.high:
       if not compareTypes(a.concreteTypes[i], b.concreteTypes[i],
                           flags = {ExactTypeDescValues,
-                                   ExactGcSafety}): return
+                                   ExactGcSafety,
+                                   PickyCAliases}): return
     result = true
+  else:
+    result = false
 
-proc genericCacheGet(genericSym: PSym, entry: TInstantiation;
+proc genericCacheGet(g: ModuleGraph; genericSym: PSym, entry: TInstantiation;
                      id: CompilesId): PSym =
-  if genericSym.procInstCache != nil:
-    for inst in genericSym.procInstCache:
-      if inst.compilesId == id and sameInstantiation(entry, inst[]):
-        return inst.sym
+  result = nil
+  for inst in procInstCacheItems(g, genericSym):
+    if (inst.compilesId == 0 or inst.compilesId == id) and sameInstantiation(entry, inst[]):
+      return inst.sym
 
 when false:
   proc `$`(x: PSym): string =
     result = x.name.s & " " & " id " & $x.id
 
-proc freshGenSyms(n: PNode, owner, orig: PSym, symMap: var TIdTable) =
+proc freshGenSyms(c: PContext; n: PNode, owner, orig: PSym, symMap: var SymMapping) =
   # we need to create a fresh set of gensym'ed symbols:
   #if n.kind == nkSym and sfGenSym in n.sym.flags:
   #  if n.sym.owner != orig:
@@ -114,47 +103,56 @@ proc freshGenSyms(n: PNode, owner, orig: PSym, symMap: var TIdTable) =
   if n.kind == nkSym and sfGenSym in n.sym.flags: # and
     #  (n.sym.owner == orig or n.sym.owner.kind in {skPackage}):
     let s = n.sym
-    var x = PSym(idTableGet(symMap, s))
+    var x = idTableGet(symMap, s)
     if x != nil:
       n.sym = x
-    elif s.owner.kind == skPackage:
+    elif s.owner == nil or s.owner.kind == skPackage:
       #echo "copied this ", s.name.s
-      x = copySym(s, false)
+      x = copySym(s, c.idgen)
       x.owner = owner
       idTablePut(symMap, s, x)
       n.sym = x
   else:
-    for i in 0 ..< safeLen(n): freshGenSyms(n.sons[i], owner, orig, symMap)
+    for i in 0..<n.safeLen: freshGenSyms(c, n[i], owner, orig, symMap)
 
 proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind)
 
 proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) =
-  if n.sons[bodyPos].kind != nkEmpty:
+  if n[bodyPos].kind != nkEmpty:
     let procParams = result.typ.n
-    for i in 1 ..< procParams.len:
+    for i in 1..<procParams.len:
       addDecl(c, procParams[i].sym)
     maybeAddResult(c, result, result.ast)
 
     inc c.inGenericInst
     # add it here, so that recursive generic procs are possible:
-    var b = n.sons[bodyPos]
-    var symMap: TIdTable
-    initIdTable symMap
+    var b = n[bodyPos]
+    var symMap = initSymMapping()
     if params != nil:
-      for i in 1 ..< params.len:
+      for i in 1..<params.len:
         let param = params[i].sym
         if sfGenSym in param.flags:
           idTablePut(symMap, params[i].sym, result.typ.n[param.position+1].sym)
-    freshGenSyms(b, result, orig, symMap)
-    b = semProcBody(c, b)
-    b = hloBody(c, b)
-    n.sons[bodyPos] = transformBody(c.graph, c.module, b, result)
-    #echo "code instantiated ", result.name.s
+    freshGenSyms(c, b, result, orig, symMap)
+
+    if sfBorrow notin orig.flags:
+      # We do not want to generate a body for generic borrowed procs.
+      # As body is a sym to the borrowed proc.
+      let resultType = # todo probably refactor it into a function
+        if result.kind == skMacro:
+          sysTypeFromName(c.graph, n.info, "NimNode")
+        elif not isInlineIterator(result.typ):
+          result.typ.returnType
+        else:
+          nil
+      b = semProcBody(c, b, resultType)
+    result.ast[bodyPos] = hloBody(c, b)
     excl(result.flags, sfForward)
+    trackProc(c, result, result.ast[bodyPos])
     dec c.inGenericInst
 
 proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
-  for i in countup(0, c.generics.len - 1):
+  for i in 0..<c.generics.len:
     if c.generics[i].genericSym.id == s.id:
       var oldPrc = c.generics[i].inst.sym
       pushProcCon(c, oldPrc)
@@ -162,7 +160,7 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
       pushInfoContext(c.config, oldPrc.info)
       openScope(c)
       var n = oldPrc.ast
-      n.sons[bodyPos] = copyTree(s.getBody)
+      n[bodyPos] = copyTree(getBody(c.graph, s))
       instantiateBody(c, n, oldPrc.typ.n, oldPrc, s)
       closeScope(c)
       popInfoContext(c.config)
@@ -179,17 +177,12 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
                           allowMetaTypes = false): PType =
   internalAssert c.config, header.kind == tyGenericInvocation
 
-  var
-    typeMap: LayeredIdTable
-    cl: TReplTypeVars
+  var cl: TReplTypeVars = TReplTypeVars(symMap: initSymMapping(),
+        localCache: initTypeMapping(), typeMap: LayeredIdTable(),
+        info: info, c: c, allowMetaTypes: allowMetaTypes
+      )
 
-  initIdTable(cl.symMap)
-  initIdTable(cl.localCache)
-  initIdTable(typeMap.topLayer)
-  cl.typeMap = addr(typeMap)
-  cl.info = info
-  cl.c = c
-  cl.allowMetaTypes = allowMetaTypes
+  cl.typeMap.topLayer = initTypeMapping()
 
   # We must add all generic params in scope, because the generic body
   # may include tyFromExpr nodes depending on these generic params.
@@ -197,12 +190,11 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
   # perhaps the code can be extracted in a shared function.
   openScope(c)
   let genericTyp = header.base
-  for i in 0 .. (genericTyp.len - 2):
-    let genParam = genericTyp[i]
+  for i, genParam in genericBodyParams(genericTyp):
     var param: PSym
 
     template paramSym(kind): untyped =
-      newSym(kind, genParam.sym.name, genericTyp.sym, genParam.sym.info)
+      newSym(kind, genParam.sym.name, c.idgen, genericTyp.sym, genParam.sym.info)
 
     if genParam.kind == tyStatic:
       param = paramSym skConst
@@ -213,24 +205,32 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
       param.typ = makeTypeDesc(c, header[i+1])
 
     # this scope was not created by the user,
-    # unused params shoudn't be reported.
+    # unused params shouldn't be reported.
     param.flags.incl sfUsed
     addDecl(c, param)
 
   result = replaceTypeVarsT(cl, header)
   closeScope(c)
 
-proc instantiateProcType(c: PContext, pt: TIdTable,
+proc referencesAnotherParam(n: PNode, p: PSym): bool =
+  if n.kind == nkSym:
+    return n.sym.kind == skParam and n.sym.owner == p
+  else:
+    for i in 0..<n.safeLen:
+      if referencesAnotherParam(n[i], p): return true
+    return false
+
+proc instantiateProcType(c: PContext, pt: TypeMapping,
                          prc: PSym, info: TLineInfo) =
   # XXX: Instantiates a generic proc signature, while at the same
   # time adding the instantiated proc params into the current scope.
   # This is necessary, because the instantiation process may refer to
   # these params in situations like this:
-  # proc foo[Container](a: Container, b: a.type.Item): type(b.x)
+  # proc foo[Container](a: Container, b: a.type.Item): typeof(b.x)
   #
   # Alas, doing this here is probably not enough, because another
   # proc signature could appear in the params:
-  # proc foo[T](a: proc (x: T, b: type(x.y))
+  # proc foo[T](a: proc (x: T, b: typeof(x.y))
   #
   # The solution would be to move this logic into semtypinst, but
   # at this point semtypinst have to become part of sem, because it
@@ -238,49 +238,129 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
   #addDecl(c, prc)
   pushInfoContext(c.config, info)
   var typeMap = initLayeredTypeMap(pt)
-  var cl = initTypeVars(c, addr(typeMap), info, nil)
+  var cl = initTypeVars(c, typeMap, info, nil)
   var result = instCopyType(cl, prc.typ)
   let originalParams = result.n
   result.n = originalParams.shallowCopy
-  for i in 1 ..< result.len:
+  for i, resulti in paramTypes(result):
     # twrong_field_caching requires these 'resetIdTable' calls:
-    if i > 1:
+    if i > FirstParamAt:
       resetIdTable(cl.symMap)
       resetIdTable(cl.localCache)
-    result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
-    propagateToOwner(result, result.sons[i])
+
+    # take a note of the original type. If't a free type or static parameter
+    # we'll need to keep it unbound for the `fitNode` operation below...
+    var typeToFit = resulti
+
+    let needsStaticSkipping = resulti.kind == tyFromExpr
+    let needsTypeDescSkipping = resulti.kind == tyTypeDesc and tfUnresolved in resulti.flags
+    if resulti.kind == tyFromExpr:
+      resulti.flags.incl tfNonConstExpr
+    result[i] = replaceTypeVarsT(cl, resulti)
+    if needsStaticSkipping:
+      result[i] = result[i].skipTypes({tyStatic})
+    if needsTypeDescSkipping:
+      result[i] = result[i].skipTypes({tyTypeDesc})
+      typeToFit = result[i]
+
+    # ...otherwise, we use the instantiated type in `fitNode`
+    if (typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone) and
+       (typeToFit.kind != tyStatic):
+      typeToFit = result[i]
+
     internalAssert c.config, originalParams[i].kind == nkSym
-    when true:
-      let oldParam = originalParams[i].sym
-      let param = copySym(oldParam)
-      param.owner = prc
-      param.typ = result.sons[i]
-      if oldParam.ast != nil:
-        param.ast = fitNode(c, param.typ, oldParam.ast, oldParam.ast.info)
-
-      # don't be lazy here and call replaceTypeVarsN(cl, originalParams[i])!
-      result.n.sons[i] = newSymNode(param)
-      addDecl(c, param)
-    else:
-      let param = replaceTypeVarsN(cl, originalParams[i])
-      result.n.sons[i] = param
-      param.sym.owner = prc
-      addDecl(c, result.n.sons[i].sym)
+    let oldParam = originalParams[i].sym
+    let param = copySym(oldParam, c.idgen)
+    param.owner = prc
+    param.typ = result[i]
+
+    # The default value is instantiated and fitted against the final
+    # concrete param type. We avoid calling `replaceTypeVarsN` on the
+    # call head symbol, because this leads to infinite recursion.
+    if oldParam.ast != nil:
+      var def = oldParam.ast.copyTree
+      if def.typ.kind == tyFromExpr:
+        def.typ.flags.incl tfNonConstExpr
+      if not isIntLit(def.typ):
+        def = prepareNode(cl, def)
+
+      # allow symchoice since node will be fit later
+      # although expectedType should cover it
+      def = semExprWithType(c, def, {efAllowSymChoice}, typeToFit)
+      if def.referencesAnotherParam(getCurrOwner(c)):
+        def.flags.incl nfDefaultRefsParam
+
+      var converted = indexTypesMatch(c, typeToFit, def.typ, def)
+      if converted == nil:
+        # The default value doesn't match the final instantiated type.
+        # As an example of this, see:
+        # https://github.com/nim-lang/Nim/issues/1201
+        # We are replacing the default value with an error node in case
+        # the user calls an explicit instantiation of the proc (this is
+        # the only way the default value might be inserted).
+        param.ast = errorNode(c, def)
+        # we know the node is empty, we need the actual type for error message
+        param.ast.typ = def.typ
+      else:
+        param.ast = fitNodePostMatch(c, typeToFit, converted)
+      param.typ = result[i]
+
+    result.n[i] = newSymNode(param)
+    propagateToOwner(result, result[i])
+    addDecl(c, param)
 
   resetIdTable(cl.symMap)
   resetIdTable(cl.localCache)
-  result.sons[0] = replaceTypeVarsT(cl, result.sons[0])
-  result.n.sons[0] = originalParams[0].copyTree
-  if result.sons[0] != nil:
-    propagateToOwner(result, result.sons[0])
+  cl.isReturnType = true
+  result.setReturnType replaceTypeVarsT(cl, result.returnType)
+  cl.isReturnType = false
+  result.n[0] = originalParams[0].copyTree
+  if result[0] != nil:
+    propagateToOwner(result, result[0])
 
   eraseVoidParams(result)
-  skipIntLiteralParams(result)
+  skipIntLiteralParams(result, c.idgen)
 
   prc.typ = result
   popInfoContext(c.config)
 
-proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
+proc instantiateOnlyProcType(c: PContext, pt: TypeMapping, prc: PSym, info: TLineInfo): PType =
+  # instantiates only the type of a given proc symbol
+  # used by sigmatch for explicit generics
+  # wouldn't be needed if sigmatch could handle complex cases,
+  # examples are in texplicitgenerics
+  # might be buggy, see rest of generateInstance if problems occur
+  let fakeSym = copySym(prc, c.idgen)
+  incl(fakeSym.flags, sfFromGeneric)
+  fakeSym.instantiatedFrom = prc
+  openScope(c)
+  for s in instantiateGenericParamList(c, prc.ast[genericParamsPos], pt):
+    addDecl(c, s)
+  instantiateProcType(c, pt, fakeSym, info)
+  closeScope(c)
+  result = fakeSym.typ
+
+proc fillMixinScope(c: PContext) =
+  var p = c.p
+  while p != nil:
+    for bnd in p.localBindStmts:
+      for n in bnd:
+        addSym(c.currentScope, n.sym)
+    p = p.next
+
+proc getLocalPassC(c: PContext, s: PSym): string =
+  when defined(nimsuggest): return ""
+  if s.ast == nil or s.ast.len == 0: return ""
+  result = ""
+  template extractPassc(p: PNode) =
+    if p.kind == nkPragma and p[0][0].ident == c.cache.getIdent"localpassc":
+      return p[0][1].strVal
+  extractPassc(s.ast[0]) #it is set via appendToModule in pragmas (fast access)
+  for n in s.ast:
+    for p in n:
+      extractPassc(p)
+
+proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping,
                       info: TLineInfo): PSym =
   ## Generates a new instance of a generic procedure.
   ## The `pt` parameter is a type-unsafe mapping table used to link generic
@@ -288,76 +368,105 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   # no need to instantiate generic templates/macros:
   internalAssert c.config, fn.kind notin {skMacro, skTemplate}
   # generates an instantiated proc
-  if c.instCounter > 1000: internalError(c.config, fn.ast.info, "nesting too deep")
-  inc(c.instCounter)
+  if c.instCounter > 50:
+    globalError(c.config, info, "generic instantiation too nested")
+  inc c.instCounter
+  defer: dec c.instCounter
   # careful! we copy the whole AST including the possibly nil body!
   var n = copyTree(fn.ast)
   # NOTE: for access of private fields within generics from a different module
   # we set the friend module:
-  c.friendModules.add(getModule(fn))
+  let producer = getModule(fn)
+  c.friendModules.add(producer)
   let oldMatchedConcept = c.matchedConcept
   c.matchedConcept = nil
   let oldScope = c.currentScope
   while not isTopLevel(c): c.currentScope = c.currentScope.parent
-  result = copySym(fn, false)
+  result = copySym(fn, c.idgen)
   incl(result.flags, sfFromGeneric)
-  result.owner = fn
+  result.instantiatedFrom = fn
+  if sfGlobal in result.flags and c.config.symbolFiles != disabledSf:
+    let passc = getLocalPassC(c, producer)
+    if passc != "": #pass the local compiler options to the consumer module too
+      extccomp.addLocalCompileOption(c.config, passc, toFullPathConsiderDirty(c.config, c.module.info.fileIndex))
+    result.owner = c.module
+  else:
+    result.owner = fn
   result.ast = n
   pushOwner(c, result)
 
+  # mixin scope:
   openScope(c)
-  let gp = n.sons[genericParamsPos]
-  internalAssert c.config, gp.kind != nkEmpty
-  n.sons[namePos] = newSymNode(result)
-  pushInfoContext(c.config, info)
+  fillMixinScope(c)
+
+  openScope(c)
+  let gp = n[genericParamsPos]
+  if gp.kind != nkGenericParams:
+    # bug #22137
+    globalError(c.config, info, "generic instantiation too nested")
+  n[namePos] = newSymNode(result)
+  pushInfoContext(c.config, info, fn.detailedInfo)
   var entry = TInstantiation.new
   entry.sym = result
   # we need to compare both the generic types and the concrete types:
   # generic[void](), generic[int]()
   # see ttypeor.nim test.
   var i = 0
-  newSeq(entry.concreteTypes, fn.typ.len+gp.len-1)
+  newSeq(entry.concreteTypes, fn.typ.paramsLen+gp.len)
+  # let param instantiation know we are in a concept for unresolved statics:
+  c.matchedConcept = oldMatchedConcept
   for s in instantiateGenericParamList(c, gp, pt):
     addDecl(c, s)
     entry.concreteTypes[i] = s.typ
     inc i
-  rawPushProcCon(c, result)
+  c.matchedConcept = nil
+  pushProcCon(c, result)
   instantiateProcType(c, pt, result, info)
-  for j in 1 .. result.typ.len-1:
-    entry.concreteTypes[i] = result.typ.sons[j]
+  for _, param in paramTypes(result.typ):
+    entry.concreteTypes[i] = param
     inc i
+  #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " ", entry.concreteTypes.len
   if tfTriggersCompileTime in result.typ.flags:
     incl(result.flags, sfCompileTime)
-  n.sons[genericParamsPos] = c.graph.emptyNode
-  var oldPrc = genericCacheGet(fn, entry[], c.compilesContextId)
+  n[genericParamsPos] = c.graph.emptyNode
+  var oldPrc = genericCacheGet(c.graph, fn, entry[], c.compilesContextId)
   if oldPrc == nil:
     # we MUST not add potentially wrong instantiations to the caching mechanism.
     # This means recursive instantiations behave differently when in
     # a ``compiles`` context but this is the lesser evil. See
     # bug #1055 (tevilcompiles).
     #if c.compilesContextId == 0:
-    rawHandleSelf(c, result)
     entry.compilesId = c.compilesContextId
-    fn.procInstCache.safeAdd(entry)
+    addToGenericProcCache(c, fn, entry)
     c.generics.add(makeInstPair(fn, entry))
-    if n.sons[pragmasPos].kind != nkEmpty:
-      pragma(c, result, n.sons[pragmasPos], allRoutinePragmas)
-    if isNil(n.sons[bodyPos]):
-      n.sons[bodyPos] = copyTree(fn.getBody)
-    if c.inGenericContext == 0:
-      instantiateBody(c, n, fn.typ.n, result, fn)
+    # bug #12985 bug #22913
+    # TODO: use the context of the declaration of generic functions instead
+    # TODO: consider fixing options as well
+    let otherPragmas = c.optionStack[^1].otherPragmas
+    c.optionStack[^1].otherPragmas = nil
+    if n[pragmasPos].kind != nkEmpty:
+      pragma(c, result, n[pragmasPos], allRoutinePragmas)
+    if isNil(n[bodyPos]):
+      n[bodyPos] = copyTree(getBody(c.graph, fn))
+    instantiateBody(c, n, fn.typ.n, result, fn)
+    c.optionStack[^1].otherPragmas = otherPragmas
     sideEffectsCheck(c, result)
-    if result.magic != mSlice:
+    if result.magic notin {mSlice, mTypeOf}:
       # 'toOpenArray' is special and it is allowed to return 'openArray':
       paramsTypeCheck(c, result.typ)
+    #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- NEW PROC!", " ", entry.concreteTypes.len
   else:
+    #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- CACHED! ", typeToString(oldPrc.typ), " ", entry.concreteTypes.len
     result = oldPrc
   popProcCon(c)
   popInfoContext(c.config)
   closeScope(c)           # close scope for parameters
+  closeScope(c)           # close scope for 'mixin' declarations
   popOwner(c)
   c.currentScope = oldScope
   discard c.friendModules.pop()
-  dec(c.instCounter)
   c.matchedConcept = oldMatchedConcept
   if result.kind == skMethod: finishMethod(c, result)
+
+  # inform IC of the generic
+  #addGeneric(c.ic, result, entry.concreteTypes)
diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim
index 02c56c035..727f36470 100644
--- a/compiler/semmacrosanity.nim
+++ b/compiler/semmacrosanity.nim
@@ -10,23 +10,23 @@
 ## Implements type sanity checking for ASTs resulting from macros. Lots of
 ## room for improvement here.
 
-import ast, astalgo, msgs, types, options
+import ast, msgs, types, options
 
 proc ithField(n: PNode, field: var int): PSym =
   result = nil
   case n.kind
   of nkRecList:
-    for i in countup(0, sonsLen(n) - 1):
-      result = ithField(n.sons[i], field)
+    for i in 0..<n.len:
+      result = ithField(n[i], field)
       if result != nil: return
   of nkRecCase:
-    if n.sons[0].kind != nkSym: return
-    result = ithField(n.sons[0], field)
+    if n[0].kind != nkSym: return
+    result = ithField(n[0], field)
     if result != nil: return
-    for i in countup(1, sonsLen(n) - 1):
-      case n.sons[i].kind
+    for i in 1..<n.len:
+      case n[i].kind
       of nkOfBranch, nkElse:
-        result = ithField(lastSon(n.sons[i]), field)
+        result = ithField(lastSon(n[i]), field)
         if result != nil: return
       else: discard
   of nkSym:
@@ -34,6 +34,15 @@ proc ithField(n: PNode, field: var int): PSym =
     else: dec(field)
   else: discard
 
+proc ithField(t: PType, field: var int): PSym =
+  var base = t.baseClass
+  while base != nil:
+    let b = skipTypes(base, skipPtrs)
+    result = ithField(b.n, field)
+    if result != nil: return result
+    base = b.baseClass
+  result = ithField(t.n, field)
+
 proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
   let x = t.skipTypes(abstractInst+{tyRange})
   # Note: x can be unequal to t and we need to be careful to use 't'
@@ -42,22 +51,44 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
   of nkObjConstr:
     let x = t.skipTypes(abstractPtrs)
     n.typ = t
-    for i in 1 ..< n.len:
+    n[0].typ = t
+    for i in 1..<n.len:
       var j = i-1
-      let field = x.n.ithField(j)
+      let field = x.ithField(j)
       if field.isNil:
         globalError conf, n.info, "invalid field at index " & $i
       else:
-        internalAssert(conf, n.sons[i].kind == nkExprColonExpr)
-        annotateType(n.sons[i].sons[1], field.typ, conf)
+        internalAssert(conf, n[i].kind == nkExprColonExpr)
+        annotateType(n[i][1], field.typ, conf)
   of nkPar, nkTupleConstr:
     if x.kind == tyTuple:
       n.typ = t
-      for i in 0 ..< n.len:
-        if i >= x.len: globalError conf, n.info, "invalid field at index " & $i
-        else: annotateType(n.sons[i], x.sons[i], conf)
+      for i in 0..<n.len:
+        if i >= x.kidsLen: globalError conf, n.info, "invalid field at index " & $i
+        else: annotateType(n[i], x[i], conf)
     elif x.kind == tyProc and x.callConv == ccClosure:
       n.typ = t
+    elif x.kind == tyOpenArray: # `opcSlice` transforms slices into tuples
+      if n.kind == nkTupleConstr:
+        let
+          bracketExpr = newNodeI(nkBracket, n.info)
+          left = int n[1].intVal
+          right = int n[2].intVal
+        bracketExpr.flags = n.flags
+        case n[0].kind # is this a string slice or a array slice
+        of nkStrKinds:
+          for i in left..right:
+            bracketExpr.add newIntNode(nkCharLit, BiggestInt n[0].strVal[i])
+            annotateType(bracketExpr[^1], x.elementType, conf)
+        of nkBracket:
+          for i in left..right:
+            bracketExpr.add n[0][i]
+            annotateType(bracketExpr[^1], x.elementType, conf)
+        else:
+          globalError(conf, n.info, "Incorrectly generated tuple constr")
+        n[] = bracketExpr[]
+
+      n.typ = t
     else:
       globalError(conf, n.info, "() must have a tuple type")
   of nkBracket:
@@ -83,12 +114,12 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
     else:
       globalError(conf, n.info, "integer literal must have some int type")
   of nkStrLit..nkTripleStrLit:
-    if x.kind in {tyString, tyCString}:
+    if x.kind in {tyString, tyCstring}:
       n.typ = t
     else:
       globalError(conf, n.info, "string literal must be of some string type")
   of nkNilLit:
-    if x.kind in NilableTypes:
+    if x.kind in NilableTypes+{tyString, tySequence}:
       n.typ = t
     else:
       globalError(conf, n.info, "nil literal must be of some pointer type")
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 1975fb77b..a12e933e7 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -10,20 +10,51 @@
 # This include file implements the semantic checking for magics.
 # included from sem.nim
 
-proc semAddr(c: PContext; n: PNode; isUnsafeAddr=false): PNode =
+proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode
+
+
+proc addDefaultFieldForNew(c: PContext, n: PNode): PNode =
+  result = n
+  let typ = result[1].typ # new(x)
+  if typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyRef and typ.skipTypes({tyGenericInst, tyAlias, tySink})[0].kind == tyObject:
+    var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, result[1].info, typ))
+    asgnExpr.typ = typ
+    var t = typ.skipTypes({tyGenericInst, tyAlias, tySink})[0]
+    while true:
+      asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n, false)
+      let base = t.baseClass
+      if base == nil:
+        break
+      t = skipTypes(base, skipPtrs)
+
+    if asgnExpr.sons.len > 1:
+      result = newTree(nkAsgn, result[1], asgnExpr)
+
+proc semAddr(c: PContext; n: PNode): PNode =
   result = newNodeI(nkAddr, n.info)
   let x = semExprWithType(c, n)
   if x.kind == nkSym:
     x.sym.flags.incl(sfAddrTaken)
-  if isAssignable(c, x, isUnsafeAddr) notin {arLValue, arLocalLValue}:
+  if isAssignable(c, x) notin {arLValue, arLocalLValue, arAddressableConst, arLentValue}:
     localError(c.config, n.info, errExprHasNoAddress)
   result.add x
   result.typ = makePtrType(c, x.typ)
 
 proc semTypeOf(c: PContext; n: PNode): PNode =
+  var m = BiggestInt 1 # typeOfIter
+  if n.len == 3:
+    let mode = semConstExpr(c, n[2])
+    if mode.kind != nkIntLit:
+      localError(c.config, n.info, "typeof: cannot evaluate 'mode' parameter at compile-time")
+    else:
+      m = mode.intVal
   result = newNodeI(nkTypeOfExpr, n.info)
-  let typExpr = semExprWithType(c, n, {efInTypeof})
+  inc c.inTypeofContext
+  defer: dec c.inTypeofContext # compiles can raise an exception
+  let typExpr = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
   result.add typExpr
+  if typExpr.typ.kind == tyFromExpr:
+    typExpr.typ.flags.incl tfNonConstExpr
   result.typ = makeTypeDesc(c, typExpr.typ)
 
 type
@@ -32,50 +63,58 @@ type
 proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode
 proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode
 
-proc skipAddr(n: PNode): PNode {.inline.} =
-  (if n.kind == nkHiddenAddr: n.sons[0] else: n)
-
 proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
   result = newNodeI(nkBracketExpr, n.info)
   for i in 1..<n.len: result.add(n[i])
   result = semSubscript(c, result, flags)
   if result.isNil:
     let x = copyTree(n)
-    x.sons[0] = newIdentNode(getIdent(c.cache, "[]"), n.info)
-    bracketNotFoundError(c, x)
+    x[0] = newIdentNode(getIdent(c.cache, "[]"), n.info)
+    if c.inGenericContext > 0:
+      for i in 0..<n.len:
+        let a = n[i]
+        if a.typ != nil and a.typ.kind in {tyGenericParam, tyFromExpr}:
+          # expression is compiled early in a generic body
+          result = semGenericStmt(c, x)
+          result.typ = makeTypeFromExpr(c, copyTree(result))
+          result.typ.flags.incl tfNonConstExpr
+          return
+    bracketNotFoundError(c, x, flags)
     #localError(c.config, n.info, "could not resolve: " & $n)
-    result = n
+    result = errorNode(c, n)
 
 proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode =
   # rewrite `[]=`(a, i, x)  back to ``a[i] = x``.
   let b = newNodeI(nkBracketExpr, n.info)
-  b.add(n[1].skipAddr)
-  for i in 2..n.len-2: b.add(n[i])
+  b.add(n[1].skipHiddenAddr)
+  for i in 2..<n.len-1: b.add(n[i])
   result = newNodeI(nkAsgn, n.info, 2)
-  result.sons[0] = b
-  result.sons[1] = n.lastSon
+  result[0] = b
+  result[1] = n.lastSon
   result = semAsgn(c, result, noOverloadedSubscript)
 
-proc semAsgnOpr(c: PContext; n: PNode): PNode =
-  result = newNodeI(nkAsgn, n.info, 2)
-  result.sons[0] = n[1]
-  result.sons[1] = n[2]
+proc semAsgnOpr(c: PContext; n: PNode; k: TNodeKind): PNode =
+  result = newNodeI(k, n.info, 2)
+  result[0] = n[1]
+  result[1] = n[2]
   result = semAsgn(c, result, noOverloadedAsgn)
 
 proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode =
   var r = isPartOf(n[1], n[2])
-  result = newIntNodeT(ord(r), n, c.graph)
+  result = newIntNodeT(toInt128(ord(r)), n, c.idgen, c.graph)
 
 proc expectIntLit(c: PContext, n: PNode): int =
   let x = c.semConstExpr(c, n)
   case x.kind
   of nkIntLit..nkInt64Lit: result = int(x.intVal)
-  else: localError(c.config, n.info, errIntLiteralExpected)
+  else:
+    result = 0
+    localError(c.config, n.info, errIntLiteralExpected)
 
 proc semInstantiationInfo(c: PContext, n: PNode): PNode =
   result = newNodeIT(nkTupleConstr, n.info, n.typ)
-  let idx = expectIntLit(c, n.sons[1])
-  let useFullPaths = expectIntLit(c, n.sons[2])
+  let idx = expectIntLit(c, n[1])
+  let useFullPaths = expectIntLit(c, n[2])
   let info = getInfoContext(c.config, idx)
   var filename = newNodeIT(nkStrLit, n.info, getSysType(c.graph, n.info, tyString))
   filename.strVal = if useFullPaths != 0: toFullPath(c.config, info) else: toFilename(c.config, info)
@@ -83,9 +122,10 @@ proc semInstantiationInfo(c: PContext, n: PNode): PNode =
   line.intVal = toLinenumber(info)
   var column = newNodeIT(nkIntLit, n.info, getSysType(c.graph, n.info, tyInt))
   column.intVal = toColumn(info)
-  result.add(filename)
-  result.add(line)
-  result.add(column)
+  # filename: string, line: int, column: int
+  result.add(newTree(nkExprColonExpr, n.typ.n[0], filename))
+  result.add(newTree(nkExprColonExpr, n.typ.n[1], line))
+  result.add(newTree(nkExprColonExpr, n.typ.n[2], column))
 
 proc toNode(t: PType, i: TLineInfo): PNode =
   result = newNodeIT(nkType, i, t)
@@ -107,9 +147,22 @@ proc uninstantiate(t: PType): PType =
   result = case t.kind
     of tyMagicGenerics: t
     of tyUserDefinedGenerics: t.base
-    of tyCompositeTypeClass: uninstantiate t.sons[1]
+    of tyCompositeTypeClass: uninstantiate t.firstGenericParam
     else: t
 
+proc getTypeDescNode(c: PContext; typ: PType, sym: PSym, info: TLineInfo): PNode =
+  var resType = newType(tyTypeDesc, c.idgen, sym)
+  rawAddSon(resType, typ)
+  result = toNode(resType, info)
+
+proc buildBinaryPredicate(kind: TTypeKind; c: PContext; context: PSym; a, b: sink PType): PType =
+  result = newType(kind, c.idgen, context)
+  result.rawAddSon a
+  result.rawAddSon b
+
+proc buildNotPredicate(c: PContext; context: PSym; a: sink PType): PType =
+  result = newType(tyNot, c.idgen, context, a)
+
 proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym): PNode =
   const skippedTypes = {tyTypeDesc, tyAlias, tySink}
   let trait = traitCall[0]
@@ -117,50 +170,89 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
   var operand = operand.skipTypes(skippedTypes)
 
   template operand2: PType =
-    traitCall.sons[2].typ.skipTypes({tyTypeDesc})
+    traitCall[2].typ.skipTypes({tyTypeDesc})
 
-  template typeWithSonsResult(kind, sons): PNode =
-    newTypeWithSons(context, kind, sons).toNode(traitCall.info)
+  if operand.kind == tyGenericParam or (traitCall.len > 2 and operand2.kind == tyGenericParam):
+    return traitCall  ## too early to evaluate
 
-  case trait.sym.name.s
+  let s = trait.sym.name.s
+  case s
   of "or", "|":
-    return typeWithSonsResult(tyOr, @[operand, operand2])
+    return buildBinaryPredicate(tyOr, c, context, operand, operand2).toNode(traitCall.info)
   of "and":
-    return typeWithSonsResult(tyAnd, @[operand, operand2])
+    return buildBinaryPredicate(tyAnd, c, context, operand, operand2).toNode(traitCall.info)
   of "not":
-    return typeWithSonsResult(tyNot, @[operand])
-  of "name":
+    return buildNotPredicate(c, context, operand).toNode(traitCall.info)
+  of "typeToString":
+    var prefer = preferTypeName
+    if traitCall.len >= 2:
+      let preferStr = traitCall[2].strVal
+      prefer = parseEnum[TPreferedDesc](preferStr)
+    result = newStrNode(nkStrLit, operand.typeToString(prefer))
+    result.typ = getSysType(c.graph, traitCall[1].info, tyString)
+    result.info = traitCall.info
+  of "name", "$":
     result = newStrNode(nkStrLit, operand.typeToString(preferTypeName))
-    result.typ = newType(tyString, context)
+    result.typ = getSysType(c.graph, traitCall[1].info, tyString)
     result.info = traitCall.info
   of "arity":
     result = newIntNode(nkIntLit, operand.len - ord(operand.kind==tyProc))
-    result.typ = newType(tyInt, context)
+    result.typ = newType(tyInt, c.idgen, context)
     result.info = traitCall.info
   of "genericHead":
-    var res = uninstantiate(operand)
-    if res == operand and res.kind notin tyMagicGenerics:
-      localError(c.config, traitCall.info,
-        "genericHead expects a generic type. The given type was " &
-        typeToString(operand))
-      return newType(tyError, context).toNode(traitCall.info)
-    result = res.base.toNode(traitCall.info)
+    var arg = operand
+    case arg.kind
+    of tyGenericInst:
+      result = getTypeDescNode(c, arg.base, operand.owner, traitCall.info)
+    # of tySequence: # this doesn't work
+    #   var resType = newType(tySequence, operand.owner)
+    #   result = toNode(resType, traitCall.info) # doesn't work yet
+    else:
+      localError(c.config, traitCall.info, "expected generic type, got: type $2 of kind $1" % [arg.kind.toHumanStr, typeToString(operand)])
+      result = newType(tyError, c.idgen, context).toNode(traitCall.info)
   of "stripGenericParams":
     result = uninstantiate(operand).toNode(traitCall.info)
   of "supportsCopyMem":
     let t = operand.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink, tyInferred})
     let complexObj = containsGarbageCollectedRef(t) or
                      hasDestructor(t)
-    result = newIntNodeT(ord(not complexObj), traitCall, c.graph)
+    result = newIntNodeT(toInt128(ord(not complexObj)), traitCall, c.idgen, c.graph)
+  of "hasDefaultValue":
+    result = newIntNodeT(toInt128(ord(not operand.requiresInit)), traitCall, c.idgen, c.graph)
+  of "isNamedTuple":
+    var operand = operand.skipTypes({tyGenericInst})
+    let cond = operand.kind == tyTuple and operand.n != nil
+    result = newIntNodeT(toInt128(ord(cond)), traitCall, c.idgen, c.graph)
+  of "tupleLen":
+    var operand = operand.skipTypes({tyGenericInst})
+    assert operand.kind == tyTuple, $operand.kind
+    result = newIntNodeT(toInt128(operand.len), traitCall, c.idgen, c.graph)
+  of "distinctBase":
+    var arg = operand.skipTypes({tyGenericInst})
+    let rec = semConstExpr(c, traitCall[2]).intVal != 0
+    while arg.kind == tyDistinct:
+      arg = arg.base.skipTypes(skippedTypes + {tyGenericInst})
+      if not rec: break
+    result = getTypeDescNode(c, arg, operand.owner, traitCall.info)
+  of "rangeBase":
+    # return the base type of a range type
+    var arg = operand.skipTypes({tyGenericInst})
+    if arg.kind == tyRange:
+      arg = arg.base
+    result = getTypeDescNode(c, arg, operand.owner, traitCall.info)
+  of "isCyclic":
+    var operand = operand.skipTypes({tyGenericInst})
+    let isCyclic = canFormAcycle(c.graph, operand)
+    result = newIntNodeT(toInt128(ord(isCyclic)), traitCall, c.idgen, 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 =
   checkMinSonsLen(n, 2, c.config)
-  let t = n.sons[1].typ
-  internalAssert c.config, t != nil and t.kind == tyTypeDesc
-  if t.sonsLen > 0:
+  let t = n[1].typ
+  internalAssert c.config, t != nil and t.skipTypes({tyAlias}).kind == tyTypeDesc
+  if t.len > 0:
     # This is either a type known to sem or a typedesc
     # param to a regular proc (again, known at instantiation)
     result = evalTypeTrait(c, n, t, getCurrOwner(c))
@@ -170,29 +262,25 @@ proc semTypeTraits(c: PContext, n: PNode): PNode =
 
 proc semOrd(c: PContext, n: PNode): PNode =
   result = n
-  let parType = n.sons[1].typ
-  if isOrdinalType(parType):
+  let parType = n[1].typ
+  if isOrdinalType(parType, allowEnumWithHoles=true):
     discard
-  elif parType.kind == tySet:
-    result.typ = makeRangeType(c, firstOrd(c.config, parType), lastOrd(c.config, parType), n.info)
   else:
-    localError(c.config, n.info, errOrdinalTypeExpected)
+    localError(c.config, n.info, errOrdinalTypeExpected % typeToString(parType, preferDesc))
     result.typ = errorType(c)
 
 proc semBindSym(c: PContext, n: PNode): PNode =
   result = copyNode(n)
-  result.add(n.sons[0])
+  result.add(n[0])
 
-  let sl = semConstExpr(c, n.sons[1])
+  let sl = semConstExpr(c, n[1])
   if sl.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
-    localError(c.config, n.sons[1].info, errStringLiteralExpected)
-    return errorNode(c, n)
+    return localErrorNode(c, n, n[1].info, errStringLiteralExpected)
 
-  let isMixin = semConstExpr(c, n.sons[2])
+  let isMixin = semConstExpr(c, n[2])
   if isMixin.kind != nkIntLit or isMixin.intVal < 0 or
       isMixin.intVal > high(TSymChoiceRule).int:
-    localError(c.config, n.sons[2].info, errConstExprExpected)
-    return errorNode(c, n)
+    return localErrorNode(c, n, n[2].info, errConstExprExpected)
 
   let id = newIdentNode(getIdent(c.cache, sl.strVal), n.info)
   let s = qualifiedLookUp(c, id, {checkUndeclared})
@@ -205,20 +293,80 @@ proc semBindSym(c: PContext, n: PNode): PNode =
       return sc
     result.add(sc)
   else:
-    errorUndeclaredIdentifier(c, n.sons[1].info, sl.strVal)
+    errorUndeclaredIdentifier(c, n[1].info, sl.strVal)
+
+proc opBindSym(c: PContext, scope: PScope, n: PNode, isMixin: int, info: PNode): PNode =
+  if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit, nkIdent}:
+    return localErrorNode(c, n, info.info, errStringOrIdentNodeExpected)
+
+  if isMixin < 0 or isMixin > high(TSymChoiceRule).int:
+    return localErrorNode(c, n, info.info, errConstExprExpected)
+
+  let id = if n.kind == nkIdent: n
+    else: newIdentNode(getIdent(c.cache, n.strVal), info.info)
+
+  let tmpScope = c.currentScope
+  c.currentScope = scope
+  let s = qualifiedLookUp(c, id, {checkUndeclared})
+  if s != nil:
+    # we need to mark all symbols:
+    result = symChoice(c, id, s, TSymChoiceRule(isMixin))
+  else:
+    result = nil
+    errorUndeclaredIdentifier(c, info.info, if n.kind == nkIdent: n.ident.s
+      else: n.strVal)
+  c.currentScope = tmpScope
+
+proc semDynamicBindSym(c: PContext, n: PNode): PNode =
+  # inside regular code, bindSym resolves to the sym-choice
+  # nodes (see tinspectsymbol)
+  if not (c.inStaticContext > 0 or getCurrOwner(c).isCompileTimeProc):
+    return semBindSym(c, n)
+
+  if c.graph.vm.isNil:
+    setupGlobalCtx(c.module, c.graph, c.idgen)
+
+  let
+    vm = PCtx c.graph.vm
+    # cache the current scope to
+    # prevent it lost into oblivion
+    scope = c.currentScope
+
+  # cannot use this
+  # vm.config.features.incl dynamicBindSym
+
+  proc bindSymWrapper(a: VmArgs) =
+    # capture PContext and currentScope
+    # param description:
+    #   0. ident, a string literal / computed string / or ident node
+    #   1. bindSym rule
+    #   2. info node
+    a.setResult opBindSym(c, scope, a.getNode(0), a.getInt(1).int, a.getNode(2))
+
+  let
+    # although we use VM callback here, it is not
+    # executed like 'normal' VM callback
+    idx = vm.registerCallback("bindSymImpl", bindSymWrapper)
+    # dummy node to carry idx information to VM
+    idxNode = newIntTypeNode(idx, c.graph.getSysType(TLineInfo(), tyInt))
+
+  result = copyNode(n)
+  for x in n: result.add x
+  result.add n # info node
+  result.add idxNode
 
 proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
 
 proc semOf(c: PContext, n: PNode): PNode =
-  if sonsLen(n) == 3:
-    n.sons[1] = semExprWithType(c, n.sons[1])
-    n.sons[2] = semExprWithType(c, n.sons[2], {efDetermineType})
-    #restoreOldStyleType(n.sons[1])
-    #restoreOldStyleType(n.sons[2])
-    let a = skipTypes(n.sons[1].typ, abstractPtrs)
-    let b = skipTypes(n.sons[2].typ, abstractPtrs)
-    let x = skipTypes(n.sons[1].typ, abstractPtrs-{tyTypeDesc})
-    let y = skipTypes(n.sons[2].typ, abstractPtrs-{tyTypeDesc})
+  if n.len == 3:
+    n[1] = semExprWithType(c, n[1])
+    n[2] = semExprWithType(c, n[2], {efDetermineType})
+    #restoreOldStyleType(n[1])
+    #restoreOldStyleType(n[2])
+    let a = skipTypes(n[1].typ, abstractPtrs)
+    let b = skipTypes(n[2].typ, abstractPtrs)
+    let x = skipTypes(n[1].typ, abstractPtrs-{tyTypeDesc})
+    let y = skipTypes(n[2].typ, abstractPtrs-{tyTypeDesc})
 
     if x.kind == tyTypeDesc or y.kind != tyTypeDesc:
       localError(c.config, n.info, "'of' takes object types")
@@ -238,28 +386,217 @@ proc semOf(c: PContext, n: PNode): PNode =
         result.typ = getSysType(c.graph, n.info, tyBool)
         return result
       elif diff == high(int):
-        localError(c.config, n.info, "'$1' cannot be of this subtype" % typeToString(a))
+        if commonSuperclass(a, b) == nil:
+          localError(c.config, n.info, "'$1' cannot be of this subtype" % typeToString(a))
+        else:
+          message(c.config, n.info, hintConditionAlwaysFalse, renderTree(n))
+          result = newIntNode(nkIntLit, 0)
+          result.info = n.info
+          result.typ = getSysType(c.graph, n.info, tyBool)
   else:
     localError(c.config, n.info, "'of' takes 2 arguments")
   n.typ = getSysType(c.graph, n.info, tyBool)
   result = n
 
+proc semUnown(c: PContext; n: PNode): PNode =
+  proc unownedType(c: PContext; t: PType): PType =
+    case t.kind
+    of tyTuple:
+      var elems = newSeq[PType](t.len)
+      var someChange = false
+      for i in 0..<t.len:
+        elems[i] = unownedType(c, t[i])
+        if elems[i] != t[i]: someChange = true
+      if someChange:
+        result = newType(tyTuple, c.idgen, t.owner)
+        # we have to use 'rawAddSon' here so that type flags are
+        # properly computed:
+        for e in elems: result.rawAddSon(e)
+      else:
+        result = t
+    of tyOwned: result = t.elementType
+    of tySequence, tyOpenArray, tyArray, tyVarargs, tyVar, tyLent,
+       tyGenericInst, tyAlias:
+      let b = unownedType(c, t[^1])
+      if b != t[^1]:
+        result = copyType(t, c.idgen, t.owner)
+        copyTypeProps(c.graph, c.idgen.module, result, t)
+
+        result[^1] = b
+        result.flags.excl tfHasOwned
+      else:
+        result = t
+    else:
+      result = t
+
+  result = copyTree(n[1])
+  result.typ = unownedType(c, result.typ)
+  # little hack for injectdestructors.nim (see bug #11350):
+  #result[0].typ = nil
+
+proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym =
+  # We need to do 2 things: Replace n.typ which is a 'ref T' by a 'var T' type.
+  # Replace nkDerefExpr by nkHiddenDeref
+  # nkDeref is for 'ref T':  x[].field
+  # nkHiddenDeref is for 'var T': x<hidden deref [] here>.field
+  proc transform(c: PContext; n: PNode; old, fresh: PType; oldParam, newParam: PSym): PNode =
+    result = shallowCopy(n)
+    if sameTypeOrNil(n.typ, old):
+      result.typ = fresh
+    if n.kind == nkSym and n.sym == oldParam:
+      result.sym = newParam
+    for i in 0 ..< safeLen(n):
+      result[i] = transform(c, n[i], old, fresh, oldParam, newParam)
+    #if n.kind == nkDerefExpr and sameType(n[0].typ, old):
+    #  result =
+
+  result = copySym(orig, c.idgen)
+  result.info = info
+  result.flags.incl sfFromGeneric
+  result.owner = orig
+  let origParamType = orig.typ.firstParamType
+  let newParamType = makeVarType(result, origParamType.skipTypes(abstractPtrs), c.idgen)
+  let oldParam = orig.typ.n[1].sym
+  let newParam = newSym(skParam, oldParam.name, c.idgen, result, result.info)
+  newParam.typ = newParamType
+  # proc body:
+  result.ast = transform(c, orig.ast, origParamType, newParamType, oldParam, newParam)
+  # proc signature:
+  result.typ = newProcType(result.info, c.idgen, result)
+  result.typ.addParam newParam
+
+proc semQuantifier(c: PContext; n: PNode): PNode =
+  checkSonsLen(n, 2, c.config)
+  openScope(c)
+  result = newNodeIT(n.kind, n.info, n.typ)
+  result.add n[0]
+  let args = n[1]
+  assert args.kind == nkArgList
+  for i in 0..args.len-2:
+    let it = args[i]
+    var valid = false
+    if it.kind == nkInfix:
+      let op = considerQuotedIdent(c, it[0])
+      if op.id == ord(wIn):
+        let v = newSymS(skForVar, it[1], c)
+        styleCheckDef(c, v)
+        onDef(it[1].info, v)
+        let domain = semExprWithType(c, it[2], {efWantIterator})
+        v.typ = domain.typ
+        valid = true
+        addDecl(c, v)
+        result.add newTree(nkInfix, it[0], newSymNode(v), domain)
+    if not valid:
+      localError(c.config, n.info, "<quantifier> 'in' <range> expected")
+  result.add forceBool(c, semExprWithType(c, args[^1]))
+  closeScope(c)
+
+proc semOld(c: PContext; n: PNode): PNode =
+  if n[1].kind == nkHiddenDeref:
+    n[1] = n[1][0]
+  if n[1].kind != nkSym or n[1].sym.kind != skParam:
+    localError(c.config, n[1].info, "'old' takes a parameter name")
+  elif n[1].sym.owner != getCurrOwner(c):
+    localError(c.config, n[1].info, n[1].sym.name.s & " does not belong to " & getCurrOwner(c).name.s)
+  result = n
+
+proc semNewFinalize(c: PContext; n: PNode): PNode =
+  # Make sure the finalizer procedure refers to a procedure
+  if n[^1].kind == nkSym and n[^1].sym.kind notin {skProc, skFunc}:
+    localError(c.config, n.info, "finalizer must be a direct reference to a proc")
+  elif optTinyRtti in c.config.globalOptions:
+    let nfin = skipConvCastAndClosure(n[^1])
+    let fin = case nfin.kind
+      of nkSym: nfin.sym
+      of nkLambda, nkDo: nfin[namePos].sym
+      else:
+        localError(c.config, n.info, "finalizer must be a direct reference to a proc")
+        nil
+    if fin != nil:
+      if fin.kind notin {skProc, skFunc}:
+        # calling convention is checked in codegen
+        localError(c.config, n.info, "finalizer must be a direct reference to a proc")
+
+      # check if we converted this finalizer into a destructor already:
+      let t = whereToBindTypeHook(c, fin.typ.firstParamType.skipTypes(abstractInst+{tyRef}))
+      if t != nil and getAttachedOp(c.graph, t, attachedDestructor) != nil and
+          getAttachedOp(c.graph, t, attachedDestructor).owner == fin:
+        discard "already turned this one into a finalizer"
+      else:
+        if fin.instantiatedFrom != nil and fin.instantiatedFrom != fin.owner: #undo move
+          fin.owner = fin.instantiatedFrom
+        let wrapperSym = newSym(skProc, getIdent(c.graph.cache, fin.name.s & "FinalizerWrapper"), c.idgen, fin.owner, fin.info)
+        let selfSymNode = newSymNode(copySym(fin.ast[paramsPos][1][0].sym, c.idgen))
+        selfSymNode.typ = fin.typ.firstParamType
+        wrapperSym.flags.incl sfUsed
+
+        let wrapper = c.semExpr(c, newProcNode(nkProcDef, fin.info, body = newTree(nkCall, newSymNode(fin), selfSymNode),
+          params = nkFormalParams.newTree(c.graph.emptyNode,
+                  newTree(nkIdentDefs, selfSymNode, newNodeIT(nkType,
+                  fin.ast[paramsPos][1][1].info, fin.typ.firstParamType), c.graph.emptyNode)
+                  ),
+          name = newSymNode(wrapperSym), pattern = fin.ast[patternPos],
+          genericParams = fin.ast[genericParamsPos], pragmas = fin.ast[pragmasPos], exceptions = fin.ast[miscPos]), {})
+
+        var transFormedSym = turnFinalizerIntoDestructor(c, wrapperSym, wrapper.info)
+        transFormedSym.owner = fin
+        if c.config.backend == backendCpp or sfCompileToCpp in c.module.flags:
+          let origParamType = transFormedSym.ast[bodyPos][1].typ
+          let selfSymbolType = makePtrType(c, origParamType.skipTypes(abstractPtrs))
+          let selfPtr = newNodeI(nkHiddenAddr, transFormedSym.ast[bodyPos][1].info)
+          selfPtr.add transFormedSym.ast[bodyPos][1]
+          selfPtr.typ = selfSymbolType
+          transFormedSym.ast[bodyPos][1] = c.semExpr(c, selfPtr)
+        # TODO: suppress var destructor warnings; if newFinalizer is not
+        # TODO: deprecated, try to implement plain T destructor
+        bindTypeHook(c, transFormedSym, n, attachedDestructor, suppressVarDestructorWarning = true)
+  result = addDefaultFieldForNew(c, n)
+
+proc semPrivateAccess(c: PContext, n: PNode): PNode =
+  let t = n[1].typ.elementType.toObjectFromRefPtrGeneric
+  if t.kind == tyObject:
+    assert t.sym != nil
+    c.currentScope.allowPrivateAccess.add t.sym
+  result = newNodeIT(nkEmpty, n.info, getSysType(c.graph, n.info, tyVoid))
+
+proc checkDefault(c: PContext, n: PNode): PNode =
+  result = n
+  c.config.internalAssert result[1].typ.kind == tyTypeDesc
+  let constructed = result[1].typ.base
+  if constructed.requiresInit:
+    message(c.config, n.info, warnUnsafeDefault, typeToString(constructed))
+
 proc magicsAfterOverloadResolution(c: PContext, n: PNode,
-                                   flags: TExprFlags): PNode =
+                                   flags: TExprFlags; expectedType: PType = nil): PNode =
+  ## This is the preferred code point to implement magics.
+  ## ``c`` the current module, a symbol table to a very good approximation
+  ## ``n`` the ast like it would be passed to a real macro
+  ## ``flags`` Some flags for more contextual information on how the
+  ## "macro" is calld.
+
   case n[0].sym.magic
   of mAddr:
     checkSonsLen(n, 2, c.config)
-    result = semAddr(c, n.sons[1], n[0].sym.name.s == "unsafeAddr")
+    result = semAddr(c, n[1])
   of mTypeOf:
-    checkSonsLen(n, 2, c.config)
-    result = semTypeOf(c, n.sons[1])
-  of mArrGet: result = semArrGet(c, n, flags)
-  of mArrPut: result = semArrPut(c, n, flags)
+    result = semTypeOf(c, n)
+  of mSizeOf:
+    result = foldSizeOf(c.config, n, n)
+  of mAlignOf:
+    result = foldAlignOf(c.config, n, n)
+  of mOffsetOf:
+    result = foldOffsetOf(c.config, n, n)
+  of mArrGet:
+    result = semArrGet(c, n, flags)
+  of mArrPut:
+    result = semArrPut(c, n, flags)
   of mAsgn:
     if n[0].sym.name.s == "=":
-      result = semAsgnOpr(c, n)
+      result = semAsgnOpr(c, n, nkAsgn)
+    elif n[0].sym.name.s == "=sink":
+      result = semAsgnOpr(c, n, nkSinkAsgn)
     else:
-      result = n
+      result = semShallowCopy(c, n, flags)
   of mIsPartOf: result = semIsPartOf(c, n, flags)
   of mTypeTrait: result = semTypeTraits(c, n)
   of mAstToStr:
@@ -270,14 +607,16 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
   of mOf: result = semOf(c, n)
   of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic)
   of mShallowCopy: result = semShallowCopy(c, n, flags)
-  of mNBindSym: result = semBindSym(c, n)
+  of mNBindSym:
+    if dynamicBindSym notin c.features:
+      result = semBindSym(c, n)
+    else:
+      result = semDynamicBindSym(c, n)
   of mProcCall:
     result = n
     result.typ = n[1].typ
   of mDotDot:
     result = n
-  of mRoof:
-    localError(c.config, n.info, "builtin roof operator is not supported anymore")
   of mPlugin:
     let plugin = getPlugin(c.cache, n[0].sym)
     if plugin.isNil:
@@ -285,4 +624,86 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
       result = n
     else:
       result = plugin(c, n)
-  else: result = n
+  of mNew:
+    if n[0].sym.name.s == "unsafeNew": # special case for unsafeNew
+      result = n
+    else:
+      result = addDefaultFieldForNew(c, n)
+  of mNewFinalize:
+    result = semNewFinalize(c, n)
+  of mDestroy:
+    result = n
+    let t = n[1].typ.skipTypes(abstractVar)
+    let op = getAttachedOp(c.graph, t, attachedDestructor)
+    if op != nil:
+      result[0] = newSymNode(op)
+      if op.typ != nil and op.typ.len == 2 and op.typ.firstParamType.kind != tyVar:
+        if n[1].kind == nkSym and n[1].sym.kind == skParam and
+            n[1].typ.kind == tyVar:
+          result[1] = genDeref(n[1])
+        else:
+          result[1] = skipAddr(n[1])
+  of mTrace:
+    result = n
+    let t = n[1].typ.skipTypes(abstractVar)
+    let op = getAttachedOp(c.graph, t, attachedTrace)
+    if op != nil:
+      result[0] = newSymNode(op)
+  of mDup:
+    result = n
+    let t = n[1].typ.skipTypes(abstractVar)
+    let op = getAttachedOp(c.graph, t, attachedDup)
+    if op != nil:
+      result[0] = newSymNode(op)
+      if op.typ.len == 3:
+        let boolLit = newIntLit(c.graph, n.info, 1)
+        boolLit.typ = getSysType(c.graph, n.info, tyBool)
+        result.add boolLit
+  of mWasMoved:
+    result = n
+    let t = n[1].typ.skipTypes(abstractVar)
+    let op = getAttachedOp(c.graph, t, attachedWasMoved)
+    if op != nil:
+      result[0] = newSymNode(op)
+      let addrExp = newNodeIT(nkHiddenAddr, result[1].info, makePtrType(c, t))
+      addrExp.add result[1]
+      result[1] = addrExp
+  of mUnown:
+    result = semUnown(c, n)
+  of mExists, mForall:
+    result = semQuantifier(c, n)
+  of mOld:
+    result = semOld(c, n)
+  of mSetLengthSeq:
+    result = n
+    let seqType = result[1].typ.skipTypes({tyPtr, tyRef, # in case we had auto-dereferencing
+                                           tyVar, tyGenericInst, tyOwned, tySink,
+                                           tyAlias, tyUserTypeClassInst})
+    if seqType.kind == tySequence and seqType.base.requiresInit:
+      message(c.config, n.info, warnUnsafeSetLen, typeToString(seqType.base))
+  of mDefault:
+    result = checkDefault(c, n)
+    let typ = result[^1].typ.skipTypes({tyTypeDesc})
+    let defaultExpr = defaultNodeField(c, result[^1], typ, false)
+    if defaultExpr != nil:
+      result = defaultExpr
+  of mZeroDefault:
+    result = checkDefault(c, n)
+  of mIsolate:
+    if not checkIsolate(n[1]):
+      localError(c.config, n.info, "expression cannot be isolated: " & $n[1])
+    result = n
+  of mPrivateAccess:
+    result = semPrivateAccess(c, n)
+  of mArrToSeq:
+    result = n
+    if result.typ != nil and expectedType != nil and result.typ.kind == tySequence and
+        expectedType.kind == tySequence and result.typ.elementType.kind == tyEmpty:
+      result.typ = expectedType # type inference for empty sequence # bug #21377
+  of mEnsureMove:
+    result = n
+    if n[1].kind in {nkStmtListExpr, nkBlockExpr,
+              nkIfExpr, nkCaseStmt, nkTryStmt}:
+      localError(c.config, n.info, "Nested expressions cannot be moved: '" & $n[1] & "'")
+  else:
+    result = n
diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim
index 8b639806d..048053115 100644
--- a/compiler/semobjconstr.nim
+++ b/compiler/semobjconstr.nim
@@ -11,14 +11,29 @@
 
 # included from sem.nim
 
+from std/sugar import dup
+
 type
-  InitStatus = enum
+  ObjConstrContext = object
+    typ: PType               # The constructed type
+    initExpr: PNode          # The init expression (nkObjConstr)
+    needsFullInit: bool      # A `requiresInit` derived type will
+                             # set this to true while visiting
+                             # parent types.
+    missingFields: seq[PSym] # Fields that the user failed to specify
+    checkDefault: bool       # Checking defaults
+
+  InitStatus = enum # This indicates the result of object construction
     initUnknown
     initFull     # All  of the fields have been initialized
     initPartial  # Some of the fields have been initialized
     initNone     # None of the fields have been initialized
     initConflict # Fields from different branches have been initialized
 
+
+proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
+                        flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]]
+
 proc mergeInitStatus(existing: var InitStatus, newStatus: InitStatus) =
   case newStatus
   of initConflict:
@@ -47,14 +62,13 @@ proc invalidObjConstr(c: PContext, n: PNode) =
 
 proc locateFieldInInitExpr(c: PContext, field: PSym, initExpr: PNode): PNode =
   # Returns the assignment nkExprColonExpr node or nil
+  result = nil
   let fieldId = field.name.id
-  for i in 1 ..< initExpr.len:
+  for i in 1..<initExpr.len:
     let assignment = initExpr[i]
     if assignment.kind != nkExprColonExpr:
       invalidObjConstr(c, assignment)
-      continue
-
-    if fieldId == considerQuotedIdent(c, assignment[0]).id:
+    elif fieldId == considerQuotedIdent(c, assignment[0]).id:
       return assignment
 
 proc semConstrField(c: PContext, flags: TExprFlags,
@@ -62,37 +76,56 @@ proc semConstrField(c: PContext, flags: TExprFlags,
   let assignment = locateFieldInInitExpr(c, field, initExpr)
   if assignment != nil:
     if nfSem in assignment.flags: return assignment[1]
-    if not fieldVisible(c, field):
+    if nfSkipFieldChecking in assignment[1].flags:
+      discard
+    elif not fieldVisible(c, field):
       localError(c.config, initExpr.info,
         "the field '$1' is not accessible." % [field.name.s])
       return
 
-    var initValue = semExprFlagDispatched(c, assignment[1], flags)
+    var initValue = semExprFlagDispatched(c, assignment[1], flags, field.typ)
     if initValue != nil:
-      initValue = fitNode(c, field.typ, initValue, assignment.info)
-    assignment.sons[0] = newSymNode(field)
-    assignment.sons[1] = initValue
+      initValue = fitNodeConsiderViewType(c, field.typ, initValue, assignment.info)
+    initValue.flags.incl nfSkipFieldChecking
+    assignment[0] = newSymNode(field)
+    assignment[1] = initValue
     assignment.flags.incl nfSem
-    return initValue
-
-proc caseBranchMatchesExpr(branch, matched: PNode): bool =
-  for i in 0 .. branch.len-2:
-    if branch[i].kind == nkRange:
-      if overlap(branch[i], matched): return true
-    elif exprStructuralEquivalent(branch[i], matched):
-      return true
-
-  return false
+    result = initValue
+  else:
+    result = nil
+
+proc branchVals(c: PContext, caseNode: PNode, caseIdx: int,
+                isStmtBranch: bool): IntSet =
+  if caseNode[caseIdx].kind == nkOfBranch:
+    result = initIntSet()
+    for val in processBranchVals(caseNode[caseIdx]):
+      result.incl(val)
+  else:
+    result = c.getIntSetOfType(caseNode[0].typ)
+    for i in 1..<caseNode.len-1:
+      for val in processBranchVals(caseNode[i]):
+        result.excl(val)
+
+proc findUsefulCaseContext(c: PContext, discrimator: PNode): (PNode, int) =
+  result = (nil, 0)
+  for i in countdown(c.p.caseContext.high, 0):
+    let
+      (caseNode, index) = c.p.caseContext[i]
+      skipped = caseNode[0].skipHidden
+    if skipped.kind == nkSym and skipped.sym == discrimator.sym:
+      return (caseNode, index)
 
 proc pickCaseBranch(caseExpr, matched: PNode): PNode =
   # XXX: Perhaps this proc already exists somewhere
   let endsWithElse = caseExpr[^1].kind == nkElse
-  for i in 1 .. caseExpr.len - 1 - int(endsWithElse):
+  for i in 1..<caseExpr.len - int(endsWithElse):
     if caseExpr[i].caseBranchMatchesExpr(matched):
       return caseExpr[i]
 
   if endsWithElse:
-    return caseExpr[^1]
+    result = caseExpr[^1]
+  else:
+    result = nil
 
 iterator directFieldsInRecList(recList: PNode): PNode =
   # XXX: We can remove this case by making all nkOfBranch nodes
@@ -103,188 +136,371 @@ iterator directFieldsInRecList(recList: PNode): PNode =
   else:
     doAssert recList.kind == nkRecList
     for field in recList:
-      if field.kind != nkSym: continue
-      yield field
+      if field.kind == nkSym:
+        yield field
 
 template quoteStr(s: string): string = "'" & s & "'"
 
 proc fieldsPresentInInitExpr(c: PContext, fieldsRecList, initExpr: PNode): string =
   result = ""
   for field in directFieldsInRecList(fieldsRecList):
-    let assignment = locateFieldInInitExpr(c, field.sym, initExpr)
-    if assignment != nil:
+    if locateFieldInInitExpr(c, field.sym, initExpr) != nil:
       if result.len != 0: result.add ", "
       result.add field.sym.name.s.quoteStr
 
-proc missingMandatoryFields(c: PContext, fieldsRecList, initExpr: PNode): string =
-  for r in directFieldsInRecList(fieldsRecList):
-    if {tfNotNil, tfNeedsInit} * r.sym.typ.flags != {}:
-      let assignment = locateFieldInInitExpr(c, r.sym, initExpr)
-      if assignment == nil:
-        if result == nil:
-          result = r.sym.name.s
-        else:
-          result.add ", "
-          result.add r.sym.name.s
-
-proc checkForMissingFields(c: PContext, recList, initExpr: PNode) =
-  let missing = missingMandatoryFields(c, recList, initExpr)
-  if missing != nil:
-    localError(c.config, initExpr.info, "fields not initialized: $1.", [missing])
+proc locateFieldInDefaults(sym: PSym, defaults: seq[PNode]): bool =
+  result = false
+  for d in defaults:
+    if sym.id == d[0].sym.id:
+      return true
 
-proc semConstructFields(c: PContext, recNode: PNode,
-                        initExpr: PNode, flags: TExprFlags): InitStatus =
-  result = initUnknown
+proc collectMissingFields(c: PContext, fieldsRecList: PNode,
+                          constrCtx: var ObjConstrContext, defaults: seq[PNode]
+                          ): seq[PSym] =
+  result = @[]
+  for r in directFieldsInRecList(fieldsRecList):
+    let assignment = locateFieldInInitExpr(c, r.sym, constrCtx.initExpr)
+    if assignment == nil and not locateFieldInDefaults(r.sym, defaults):
+      if constrCtx.needsFullInit or
+        sfRequiresInit in r.sym.flags or
+          r.sym.typ.requiresInit:
+        constrCtx.missingFields.add r.sym
+      else:
+        result.add r.sym
 
-  case recNode.kind
+proc collectMissingCaseFields(c: PContext, branchNode: PNode,
+                          constrCtx: var ObjConstrContext, defaults: seq[PNode]): seq[PSym] =
+  if branchNode != nil:
+    let fieldsRecList = branchNode[^1]
+    result = collectMissingFields(c, fieldsRecList, constrCtx, defaults)
+  else:
+    result = @[]
+
+proc collectOrAddMissingCaseFields(c: PContext, branchNode: PNode,
+                          constrCtx: var ObjConstrContext, defaults: var seq[PNode]) =
+  let res = collectMissingCaseFields(c, branchNode, constrCtx, defaults)
+  for sym in res:
+    let asgnType = newType(tyTypeDesc, c.idgen, sym.typ.owner)
+    let recTyp = sym.typ.skipTypes(defaultFieldsSkipTypes)
+    rawAddSon(asgnType, recTyp)
+    let asgnExpr = newTree(nkCall,
+          newSymNode(getSysMagic(c.graph, constrCtx.initExpr.info, "zeroDefault", mZeroDefault)),
+          newNodeIT(nkType, constrCtx.initExpr.info, asgnType)
+        )
+    asgnExpr.flags.incl nfSkipFieldChecking
+    asgnExpr.typ = recTyp
+    defaults.add newTree(nkExprColonExpr, newSymNode(sym), asgnExpr)
+
+proc collectBranchFields(c: PContext, n: PNode, discriminatorVal: PNode,
+                          constrCtx: var ObjConstrContext, flags: TExprFlags) =
+  # All bets are off. If any of the branches has a mandatory
+  # fields we must produce an error:
+  for i in 1..<n.len:
+    let branchNode = n[i]
+    if branchNode != nil:
+      let oldCheckDefault = constrCtx.checkDefault
+      constrCtx.checkDefault = true
+      let (_, defaults) = semConstructFields(c, branchNode[^1], constrCtx, flags)
+      constrCtx.checkDefault = oldCheckDefault
+      if len(defaults) > 0:
+        localError(c.config, discriminatorVal.info, "branch initialization " &
+                    "with a runtime discriminator is not supported " &
+                    "for a branch whose fields have default values.")
+    discard collectMissingCaseFields(c, n[i], constrCtx, @[])
+
+proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
+                        flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] =
+  result = (initUnknown, @[])
+  case n.kind
   of nkRecList:
-    for field in recNode:
-      let status = semConstructFields(c, field, initExpr, flags)
-      mergeInitStatus(result, status)
-
+    for field in n:
+      let (subSt, subDf) = semConstructFields(c, field, constrCtx, flags)
+      result.status.mergeInitStatus subSt
+      result.defaults.add subDf
   of nkRecCase:
     template fieldsPresentInBranch(branchIdx: int): string =
-      let branch = recNode[branchIdx]
-      let fields = branch[branch.len - 1]
-      fieldsPresentInInitExpr(c, fields, initExpr)
+      let branch = n[branchIdx]
+      let fields = branch[^1]
+      fieldsPresentInInitExpr(c, fields, constrCtx.initExpr)
 
-    template checkMissingFields(branchNode: PNode) =
-      let fields = branchNode[branchNode.len - 1]
-      checkForMissingFields(c, fields, initExpr)
-
-    let discriminator = recNode.sons[0]
+    let discriminator = n[0]
     internalAssert c.config, discriminator.kind == nkSym
     var selectedBranch = -1
 
-    for i in 1 ..< recNode.len:
-      let innerRecords = recNode[i][^1]
-      let status = semConstructFields(c, innerRecords, initExpr, flags)
+    for i in 1..<n.len:
+      let innerRecords = n[i][^1]
+      let (status, _) = semConstructFields(c, innerRecords, constrCtx, flags) # todo
       if status notin {initNone, initUnknown}:
-        mergeInitStatus(result, status)
+        result.status.mergeInitStatus status
         if selectedBranch != -1:
           let prevFields = fieldsPresentInBranch(selectedBranch)
           let currentFields = fieldsPresentInBranch(i)
-          localError(c.config, initExpr.info,
+          localError(c.config, constrCtx.initExpr.info,
             ("The fields '$1' and '$2' cannot be initialized together, " &
             "because they are from conflicting branches in the case object.") %
             [prevFields, currentFields])
-          result = initConflict
+          result.status = initConflict
         else:
           selectedBranch = i
 
     if selectedBranch != -1:
-      let branchNode = recNode[selectedBranch]
-      let flags = flags*{efAllowDestructor} + {efNeedStatic, efPreferNilResult}
-      let discriminatorVal = semConstrField(c, flags,
-                                            discriminator.sym, initExpr)
-      if discriminatorVal == nil:
-        let fields = fieldsPresentInBranch(selectedBranch)
-        localError(c.config, initExpr.info,
-          ("you must provide a compile-time value for the discriminator '$1' " &
-          "in order to prove that it's safe to initialize $2.") %
-          [discriminator.sym.name.s, fields])
-        mergeInitStatus(result, initNone)
-      else:
-        let discriminatorVal = discriminatorVal.skipHidden
-
-        template wrongBranchError(i) =
+      template badDiscriminatorError =
+        if c.inUncheckedAssignSection == 0:
+          let fields = fieldsPresentInBranch(selectedBranch)
+          localError(c.config, constrCtx.initExpr.info,
+            ("cannot prove that it's safe to initialize $1 with " &
+            "the runtime value for the discriminator '$2' ") %
+            [fields, discriminator.sym.name.s])
+        mergeInitStatus(result.status, initNone)
+
+      template wrongBranchError(i) =
+        if c.inUncheckedAssignSection == 0:
           let fields = fieldsPresentInBranch(i)
-          localError(c.config, initExpr.info,
-            "a case selecting discriminator '$1' with value '$2' " &
+          localError(c.config, constrCtx.initExpr.info,
+            ("a case selecting discriminator '$1' with value '$2' " &
             "appears in the object construction, but the field(s) $3 " &
-            "are in conflict with this value.",
+            "are in conflict with this value.") %
             [discriminator.sym.name.s, discriminatorVal.renderTree, fields])
 
+      template valuesInConflictError(valsDiff) =
+        localError(c.config, discriminatorVal.info, ("possible values " &
+          "$2 are in conflict with discriminator values for " &
+          "selected object branch $1.") % [$selectedBranch,
+          valsDiff.renderAsType(n[0].typ)])
+
+      let branchNode = n[selectedBranch]
+      let flags = {efPreferStatic, efPreferNilResult}
+      var discriminatorVal = semConstrField(c, flags,
+                                            discriminator.sym,
+                                            constrCtx.initExpr)
+      if discriminatorVal != nil:
+        discriminatorVal = discriminatorVal.skipHidden
+        if discriminatorVal.kind notin nkLiterals and (
+            not isOrdinalType(discriminatorVal.typ, true) or
+            lengthOrd(c.config, discriminatorVal.typ) > MaxSetElements or
+            lengthOrd(c.config, n[0].typ) > MaxSetElements):
+          localError(c.config, discriminatorVal.info,
+            "branch initialization with a runtime discriminator only " &
+            "supports ordinal types with 2^16 elements or less.")
+
+      if discriminatorVal == nil:
+        badDiscriminatorError()
+      elif discriminatorVal.kind == nkSym:
+        let (ctorCase, ctorIdx) = findUsefulCaseContext(c, discriminatorVal)
+        if ctorCase == nil:
+          if discriminatorVal.typ.kind == tyRange:
+            let rangeVals = c.getIntSetOfType(discriminatorVal.typ)
+            let recBranchVals = branchVals(c, n, selectedBranch, false)
+            let diff = rangeVals - recBranchVals
+            if diff.len != 0:
+              valuesInConflictError(diff)
+          else:
+            badDiscriminatorError()
+        elif discriminatorVal.sym.kind notin {skLet, skParam} or
+            discriminatorVal.sym.typ.kind in {tyVar}:
+          if c.inUncheckedAssignSection == 0:
+            localError(c.config, discriminatorVal.info,
+              "runtime discriminator must be immutable if branch fields are " &
+              "initialized, a 'let' binding is required.")
+        elif ctorCase[ctorIdx].kind == nkElifBranch:
+          localError(c.config, discriminatorVal.info, "branch initialization " &
+            "with a runtime discriminator is not supported inside of an " &
+            "`elif` branch.")
+        else:
+          var
+            ctorBranchVals = branchVals(c, ctorCase, ctorIdx, true)
+            recBranchVals = branchVals(c, n, selectedBranch, false)
+            branchValsDiff = ctorBranchVals - recBranchVals
+          if branchValsDiff.len != 0:
+            valuesInConflictError(branchValsDiff)
+      else:
+        var failedBranch = -1
         if branchNode.kind != nkElse:
           if not branchNode.caseBranchMatchesExpr(discriminatorVal):
-            wrongBranchError(selectedBranch)
+            failedBranch = selectedBranch
         else:
           # With an else clause, check that all other branches don't match:
-          for i in 1 .. (recNode.len - 2):
-            if recNode[i].caseBranchMatchesExpr(discriminatorVal):
-              wrongBranchError(i)
+          for i in 1..<n.len - 1:
+            if n[i].caseBranchMatchesExpr(discriminatorVal):
+              failedBranch = i
               break
+        if failedBranch != -1:
+          if discriminatorVal.typ.kind == tyRange:
+            let rangeVals = c.getIntSetOfType(discriminatorVal.typ)
+            let recBranchVals = branchVals(c, n, selectedBranch, false)
+            let diff = rangeVals - recBranchVals
+            if diff.len != 0:
+              valuesInConflictError(diff)
+          else:
+            wrongBranchError(failedBranch)
+
+      let (_, defaults) = semConstructFields(c, branchNode[^1], constrCtx, flags)
+      result.defaults.add defaults
 
       # When a branch is selected with a partial match, some of the fields
       # that were not initialized may be mandatory. We must check for this:
-      if result == initPartial:
-        checkMissingFields branchNode
-
+      if result.status == initPartial:
+        collectOrAddMissingCaseFields(c, branchNode, constrCtx, result.defaults)
     else:
-      result = initNone
+      result.status = initNone
       let discriminatorVal = semConstrField(c, flags + {efPreferStatic},
-                                            discriminator.sym, initExpr)
+                                            discriminator.sym,
+                                            constrCtx.initExpr)
       if discriminatorVal == nil:
-        # None of the branches were explicitly selected by the user and no
-        # value was given to the discrimator. We can assume that it will be
-        # initialized to zero and this will select a particular branch as
-        # a result:
-        let matchedBranch = recNode.pickCaseBranch newIntLit(c.graph, initExpr.info, 0)
-        checkMissingFields matchedBranch
+        if discriminator.sym.ast != nil:
+          # branch is selected by the default field value of discriminator
+          let discriminatorDefaultVal = discriminator.sym.ast
+          result.status = initUnknown
+          result.defaults.add newTree(nkExprColonExpr, n[0], discriminatorDefaultVal)
+          if discriminatorDefaultVal.kind == nkIntLit:
+            let matchedBranch = n.pickCaseBranch discriminatorDefaultVal
+            if matchedBranch != nil:
+              let (_, defaults) = semConstructFields(c, matchedBranch[^1], constrCtx, flags)
+              result.defaults.add defaults
+              collectOrAddMissingCaseFields(c, matchedBranch, constrCtx, result.defaults)
+          else:
+            collectBranchFields(c, n, discriminatorDefaultVal, constrCtx, flags)
+        else:
+          # None of the branches were explicitly selected by the user and no
+          # value was given to the discrimator. We can assume that it will be
+          # initialized to zero and this will select a particular branch as
+          # a result:
+          let defaultValue = newIntLit(c.graph, constrCtx.initExpr.info, 0)
+          let matchedBranch = n.pickCaseBranch defaultValue
+          discard collectMissingCaseFields(c, matchedBranch, constrCtx, @[])
       else:
-        result = initPartial
+        result.status = initPartial
         if discriminatorVal.kind == nkIntLit:
           # When the discriminator is a compile-time value, we also know
-          # which brach will be selected:
-          let matchedBranch = recNode.pickCaseBranch discriminatorVal
-          if matchedBranch != nil: checkMissingFields matchedBranch
+          # which branch will be selected:
+          let matchedBranch = n.pickCaseBranch discriminatorVal
+          if matchedBranch != nil:
+            let (_, defaults) = semConstructFields(c, matchedBranch[^1], constrCtx, flags)
+            result.defaults.add defaults
+            collectOrAddMissingCaseFields(c, matchedBranch, constrCtx, result.defaults)
         else:
-          # All bets are off. If any of the branches has a mandatory
-          # fields we must produce an error:
-          for i in 1 ..< recNode.len: checkMissingFields recNode[i]
+          collectBranchFields(c, n, discriminatorVal, constrCtx, flags)
 
   of nkSym:
-    let field = recNode.sym
-    let e = semConstrField(c, flags, field, initExpr)
-    result = if e != nil: initFull else: initNone
-
+    let field = n.sym
+    let e = semConstrField(c, flags, field, constrCtx.initExpr)
+    if e != nil:
+      result.status = initFull
+    elif field.ast != nil:
+      if efIgnoreDefaults notin flags:
+        result.status = initUnknown
+        result.defaults.add newTree(nkExprColonExpr, n, field.ast)
+      else:
+        result.status = initNone
+    else:
+      if {efWantNoDefaults, efIgnoreDefaults} * flags == {}: # cannot compute defaults at the typeRightPass
+        let defaultExpr = defaultNodeField(c, n, constrCtx.checkDefault)
+        if defaultExpr != nil:
+          result.status = initUnknown
+          result.defaults.add newTree(nkExprColonExpr, n, defaultExpr)
+        else:
+          result.status = initNone
+      else:
+        result.status = initNone
   else:
     internalAssert c.config, false
 
-proc semConstructType(c: PContext, initExpr: PNode,
-                      t: PType, flags: TExprFlags): InitStatus =
-  var t = t
-  result = initUnknown
+proc semConstructTypeAux(c: PContext,
+                         constrCtx: var ObjConstrContext,
+                         flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] =
+  result = (initUnknown, @[])
+  var t = constrCtx.typ
   while true:
-    let status = semConstructFields(c, t.n, initExpr, flags)
-    mergeInitStatus(result, status)
+    let (status, defaults) = semConstructFields(c, t.n, constrCtx, flags)
+    result.status.mergeInitStatus status
+    result.defaults.add defaults
     if status in {initPartial, initNone, initUnknown}:
-      checkForMissingFields c, t.n, initExpr
-    let base = t.sons[0]
-    if base == nil: break
+      discard collectMissingFields(c, t.n, constrCtx, result.defaults)
+    let base = t.baseClass
+    if base == nil or base.id == t.id or
+        base.kind in {tyRef, tyPtr} and base.elementType.id == t.id:
+      break
     t = skipTypes(base, skipPtrs)
+    if t.kind != tyObject:
+      # XXX: This is not supposed to happen, but apparently
+      # there are some issues in semtypinst. Luckily, it
+      # seems to affect only `computeRequiresInit`.
+      return
+    constrCtx.needsFullInit = constrCtx.needsFullInit or
+                              tfNeedsFullInit in t.flags
+
+proc initConstrContext(t: PType, initExpr: PNode): ObjConstrContext =
+  ObjConstrContext(typ: t, initExpr: initExpr,
+                   needsFullInit: tfNeedsFullInit in t.flags)
+
+proc computeRequiresInit(c: PContext, t: PType): bool =
+  assert t.kind == tyObject
+  var constrCtx = initConstrContext(t, newNode(nkObjConstr))
+  let initResult = semConstructTypeAux(c, constrCtx, {efWantNoDefaults})
+  constrCtx.missingFields.len > 0
+
+proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) =
+  var objType = t
+  while objType.kind notin {tyObject, tyDistinct}:
+    objType = objType.last
+    assert objType != nil
+  if objType.kind == tyObject:
+    var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info))
+    let initResult = semConstructTypeAux(c, constrCtx, {efIgnoreDefaults})
+    if constrCtx.missingFields.len > 0:
+      localError(c.config, info,
+        "The $1 type doesn't have a default value. The following fields must be initialized: $2." % [typeToString(t), listSymbolNames(constrCtx.missingFields)])
+  elif objType.kind == tyDistinct:
+    localError(c.config, info,
+      "The $1 distinct type doesn't have a default value." % typeToString(t))
+  else:
+    assert false, "Must not enter here."
 
-proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
-  var t = semTypeNode(c, n.sons[0], nil)
+proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
+  var t = semTypeNode(c, n[0], nil)
   result = newNodeIT(nkObjConstr, n.info, t)
-  for child in n: result.add child
+  for i in 0..<n.len:
+    result.add n[i]
 
   if t == nil:
-    localError(c.config, n.info, errGenerated, "object constructor needs an object type")
-    return
-
-  t = skipTypes(t, {tyGenericInst, tyAlias, tySink})
-  if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias, tySink})
+    return localErrorNode(c, result, "object constructor needs an object type")
+
+  if t.skipTypes({tyGenericInst,
+      tyAlias, tySink, tyOwned, tyRef}).kind != tyObject and
+      expectedType != nil and expectedType.skipTypes({tyGenericInst,
+      tyAlias, tySink, tyOwned, tyRef}).kind == tyObject:
+    t = expectedType
+
+  t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned})
+  if t.kind == tyRef:
+    t = skipTypes(t.elementType, {tyGenericInst, tyAlias, tySink, tyOwned})
+    if optOwnedRefs in c.config.globalOptions:
+      result.typ = makeVarType(c, result.typ, tyOwned)
+      # we have to watch out, there are also 'owned proc' types that can be used
+      # multiple times as long as they don't have closures.
+      result.typ.flags.incl tfHasOwned
   if t.kind != tyObject:
-    localError(c.config, n.info, errGenerated, "object constructor needs an object type")
-    return
+    return localErrorNode(c, result, if t.kind != tyGenericBody:
+      "object constructor needs an object type".dup(addTypeNodeDeclaredLoc(c.config, t))
+      else: "cannot instantiate: '" &
+        typeToString(t, preferDesc) &
+        "'; the object's generic parameters cannot be inferred and must be explicitly given"
+      )
 
   # Check if the object is fully initialized by recursively testing each
   # field (if this is a case object, initialized fields in two different
   # branches will be reported as an error):
-  let initResult = semConstructType(c, result, t, flags)
+  var constrCtx = initConstrContext(t, result)
+  let (initResult, defaults) = semConstructTypeAux(c, constrCtx, flags)
+  var hasError = false # needed to split error detect/report for better msgs
 
   # It's possible that the object was not fully initialized while
-  # specifying a .requiresInit. pragma.
-  # XXX: Turn this into an error in the next release
-  if tfNeedsInit in t.flags and initResult != initFull:
-    # XXX: Disable this warning for now, because tfNeedsInit is propagated
-    # too aggressively from fields to object types (and this is not correct
-    # in case objects)
-    when false: message(n.info, warnUser,
-      "object type uses the 'requiresInit' pragma, but not all fields " &
-      "have been initialized. future versions of Nim will treat this as " &
-      "an error")
+  # specifying a .requiresInit. pragma:
+  if constrCtx.missingFields.len > 0:
+    hasError = true
+    localError(c.config, result.info,
+      "The $1 type requires the following fields to be initialized: $2." %
+      [t.sym.name.s, listSymbolNames(constrCtx.missingFields)])
 
   # Since we were traversing the object fields, it's possible that
   # not all of the fields specified in the constructor was visited.
@@ -294,15 +510,27 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
     if nfSem notin field.flags:
       if field.kind != nkExprColonExpr:
         invalidObjConstr(c, field)
+        hasError = true
         continue
       let id = considerQuotedIdent(c, field[0])
       # This node was not processed. There are two possible reasons:
       # 1) It was shadowed by a field with the same name on the left
-      for j in 1 ..< i:
+      for j in 1..<i:
         let prevId = considerQuotedIdent(c, result[j][0])
         if prevId.id == id.id:
           localError(c.config, field.info, errFieldInitTwice % id.s)
-          return
+          hasError = true
+          break
       # 2) No such field exists in the constructed type
-      localError(c.config, field.info, errUndeclaredFieldX % id.s)
-      return
+      let msg = errUndeclaredField % id.s & " for type " & getProcHeader(c.config, t.sym)
+      localError(c.config, field.info, msg)
+      hasError = true
+      break
+
+  result.sons.add defaults
+
+  if initResult == initFull:
+    incl result.flags, nfAllFieldsSet
+
+  # wrap in an error see #17437
+  if hasError: result = errorNode(c, result)
diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim
index 0d780bdee..23a8e6362 100644
--- a/compiler/semparallel.nim
+++ b/compiler/semparallel.nim
@@ -22,11 +22,11 @@
 # - output slices need special logic (+)
 
 import
-  ast, astalgo, idents, lowerings, magicsys, guards, sempass2, msgs,
-  renderer, types, modulegraphs, options
+  ast, astalgo, idents, lowerings, magicsys, guards, msgs,
+  renderer, types, modulegraphs, options, spawn, lineinfos
 
-from trees import getMagic
-from strutils import `%`
+from trees import getMagic, getRoot
+from std/strutils import `%`
 
 discard """
 
@@ -77,12 +77,12 @@ type
     graph: ModuleGraph
 
 proc initAnalysisCtx(g: ModuleGraph): AnalysisCtx =
-  result.locals = @[]
-  result.slices = @[]
-  result.args = @[]
+  result = AnalysisCtx(locals: @[],
+    slices: @[],
+    args: @[],
+    graph: g)
   result.guards.s = @[]
-  result.guards.o = initOperators(g)
-  result.graph = g
+  result.guards.g = g
 
 proc lookupSlot(c: AnalysisCtx; s: PSym): int =
   for i in 0..<c.locals.len:
@@ -92,10 +92,9 @@ proc lookupSlot(c: AnalysisCtx; s: PSym): int =
 proc getSlot(c: var AnalysisCtx; v: PSym): ptr MonotonicVar =
   let s = lookupSlot(c, v)
   if s >= 0: return addr(c.locals[s])
-  let L = c.locals.len
-  c.locals.setLen(L+1)
-  c.locals[L].v = v
-  return addr(c.locals[L])
+  c.locals.setLen(c.locals.len+1)
+  c.locals[^1].v = v
+  return addr(c.locals[^1])
 
 proc gatherArgs(c: var AnalysisCtx; n: PNode) =
   for i in 0..<n.safeLen:
@@ -123,21 +122,23 @@ proc checkLocal(c: AnalysisCtx; n: PNode) =
     if s >= 0 and c.locals[s].stride != nil:
       localError(c.graph.config, n.info, "invalid usage of counter after increment")
   else:
-    for i in 0 ..< n.safeLen: checkLocal(c, n.sons[i])
+    for i in 0..<n.safeLen: checkLocal(c, n[i])
 
 template `?`(x): untyped = x.renderTree
 
 proc checkLe(c: AnalysisCtx; a, b: PNode) =
   case proveLe(c.guards, a, b)
   of impUnknown:
-    localError(c.graph.config, a.info, "cannot prove: " & ?a & " <= " & ?b & " (bounds check)")
+    message(c.graph.config, a.info, warnStaticIndexCheck,
+      "cannot prove: " & ?a & " <= " & ?b)
   of impYes: discard
   of impNo:
-    localError(c.graph.config, a.info, "can prove: " & ?a & " > " & ?b & " (bounds check)")
+    message(c.graph.config, a.info, warnStaticIndexCheck,
+      "can prove: " & ?a & " > " & ?b)
 
 proc checkBounds(c: AnalysisCtx; arr, idx: PNode) =
   checkLe(c, lowBound(c.graph.config, arr), idx)
-  checkLe(c, idx, highBound(c.graph.config, arr, c.guards.o))
+  checkLe(c, idx, highBound(c.graph.config, arr, c.graph.operators))
 
 proc addLowerBoundAsFacts(c: var AnalysisCtx) =
   for v in c.locals:
@@ -146,8 +147,8 @@ proc addLowerBoundAsFacts(c: var AnalysisCtx) =
 
 proc addSlice(c: var AnalysisCtx; n: PNode; x, le, ri: PNode) =
   checkLocal(c, n)
-  let le = le.canon(c.guards.o)
-  let ri = ri.canon(c.guards.o)
+  let le = le.canon(c.graph.operators)
+  let ri = ri.canon(c.graph.operators)
   # perform static bounds checking here; and not later!
   let oldState = c.guards.s.len
   addLowerBoundAsFacts(c)
@@ -163,17 +164,17 @@ proc overlap(m: TModel; conf: ConfigRef; x,y,c,d: PNode) =
     case proveLe(m, x, d)
     of impNo: discard
     of impUnknown, impYes:
-      localError(conf, x.info,
+      message(conf, x.info, warnStaticIndexCheck,
         "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" %
             [?c, ?y, ?x, ?y, ?c, ?d])
   of impYes:
     case proveLe(m, x, d)
     of impUnknown:
-      localError(conf, x.info,
+      message(conf, x.info, warnStaticIndexCheck,
         "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" %
           [?x, ?d, ?x, ?y, ?c, ?d])
     of impYes:
-      localError(conf, x.info, "($#)..($#) not disjoint from ($#)..($#)" %
+      message(conf, x.info, warnStaticIndexCheck, "($#)..($#) not disjoint from ($#)..($#)" %
                 [?c, ?y, ?x, ?y, ?c, ?d])
     of impNo: discard
   of impNo: discard
@@ -183,20 +184,23 @@ proc stride(c: AnalysisCtx; n: PNode): BiggestInt =
     let s = c.lookupSlot(n.sym)
     if s >= 0 and c.locals[s].stride != nil:
       result = c.locals[s].stride.intVal
+    else:
+      result = 0
   else:
-    for i in 0 ..< n.safeLen: result += stride(c, n.sons[i])
+    result = 0
+    for i in 0..<n.safeLen: result += stride(c, n[i])
 
 proc subStride(c: AnalysisCtx; n: PNode): PNode =
   # substitute with stride:
   if isLocal(n):
     let s = c.lookupSlot(n.sym)
     if s >= 0 and c.locals[s].stride != nil:
-      result = buildAdd(n, c.locals[s].stride.intVal, c.guards.o)
+      result = buildAdd(n, c.locals[s].stride.intVal, c.graph.operators)
     else:
       result = n
   elif n.safeLen > 0:
     result = shallowCopy(n)
-    for i in 0 ..< n.len: result.sons[i] = subStride(c, n.sons[i])
+    for i in 0..<n.len: result[i] = subStride(c, n[i])
   else:
     result = n
 
@@ -216,8 +220,8 @@ proc checkSlicesAreDisjoint(c: var AnalysisCtx) =
   #
   # Or even worse:
   #   while true:
-  #     spawn f(a[i+1 .. i+3])
-  #     spawn f(a[i+4 .. i+5])
+  #     spawn f(a[i+1..i+3])
+  #     spawn f(a[i+4..i+5])
   #     inc i, 4
   # Prove that i*k*stride + 3 != i*k'*stride + 5
   # For the correct example this amounts to
@@ -226,15 +230,15 @@ proc checkSlicesAreDisjoint(c: var AnalysisCtx) =
   # For now, we don't try to prove things like that at all, even though it'd
   # be feasible for many useful examples. Instead we attach the slice to
   # a spawn and if the attached spawns differ, we bail out:
-  for i in 0 .. high(c.slices):
-    for j in i+1 .. high(c.slices):
+  for i in 0..high(c.slices):
+    for j in i+1..high(c.slices):
       let x = c.slices[i]
       let y = c.slices[j]
       if x.spawnId != y.spawnId and guards.sameTree(x.x, y.x):
         if not x.inLoop or not y.inLoop:
           # XXX strictly speaking, 'or' is not correct here and it needs to
           # be 'and'. However this prevents too many obviously correct programs
-          # like f(a[0..x]); for i in x+1 .. a.high: f(a[i])
+          # like f(a[0..x]); for i in x+1..a.high: f(a[i])
           overlap(c.guards, c.graph.config, x.a, x.b, y.a, y.b)
         elif (let k = simpleSlice(x.a, x.b); let m = simpleSlice(y.a, y.b);
               k >= 0 and m >= 0):
@@ -255,15 +259,13 @@ proc checkSlicesAreDisjoint(c: var AnalysisCtx) =
 proc analyse(c: var AnalysisCtx; n: PNode)
 
 proc analyseSons(c: var AnalysisCtx; n: PNode) =
-  for i in 0 ..< safeLen(n): analyse(c, n[i])
+  for i in 0..<n.safeLen: analyse(c, n[i])
 
 proc min(a, b: PNode): PNode =
   if a.isNil: result = b
   elif a.intVal < b.intVal: result = a
   else: result = b
 
-proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags
-
 template pushSpawnId(c, body) {.dirty.} =
   inc c.spawns
   let oldSpawnId = c.currentSpawnId
@@ -295,36 +297,36 @@ proc analyseCall(c: var AnalysisCtx; n: PNode; op: PSym) =
     analyseSons(c, n)
 
 proc analyseCase(c: var AnalysisCtx; n: PNode) =
-  analyse(c, n.sons[0])
+  analyse(c, n[0])
   let oldFacts = c.guards.s.len
   for i in 1..<n.len:
-    let branch = n.sons[i]
+    let branch = n[i]
     setLen(c.guards.s, oldFacts)
     addCaseBranchFacts(c.guards, n, i)
-    for i in 0 ..< branch.len:
-      analyse(c, branch.sons[i])
+    for i in 0..<branch.len:
+      analyse(c, branch[i])
   setLen(c.guards.s, oldFacts)
 
 proc analyseIf(c: var AnalysisCtx; n: PNode) =
-  analyse(c, n.sons[0].sons[0])
+  analyse(c, n[0][0])
   let oldFacts = c.guards.s.len
-  addFact(c.guards, canon(n.sons[0].sons[0], c.guards.o))
+  addFact(c.guards, canon(n[0][0], c.graph.operators))
 
-  analyse(c, n.sons[0].sons[1])
+  analyse(c, n[0][1])
   for i in 1..<n.len:
-    let branch = n.sons[i]
+    let branch = n[i]
     setLen(c.guards.s, oldFacts)
     for j in 0..i-1:
-      addFactNeg(c.guards, canon(n.sons[j].sons[0], c.guards.o))
+      addFactNeg(c.guards, canon(n[j][0], c.graph.operators))
     if branch.len > 1:
-      addFact(c.guards, canon(branch.sons[0], c.guards.o))
-    for i in 0 ..< branch.len:
-      analyse(c, branch.sons[i])
+      addFact(c.guards, canon(branch[0], c.graph.operators))
+    for i in 0..<branch.len:
+      analyse(c, branch[i])
   setLen(c.guards.s, oldFacts)
 
 proc analyse(c: var AnalysisCtx; n: PNode) =
   case n.kind
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     let y = n[1].skipConv
     if n[0].isSingleAssignable and y.isLocal:
       let slot = c.getSlot(y.sym)
@@ -352,7 +354,7 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
     if n[0].typ != nil and skipTypes(n[0].typ, abstractVar).kind != tyTuple:
       c.addSlice(n, n[0], n[1], n[1])
     analyseSons(c, n)
-  of nkReturnStmt, nkRaiseStmt, nkTryStmt:
+  of nkReturnStmt, nkRaiseStmt, nkTryStmt, nkHiddenTryStmt:
     localError(c.graph.config, n.info, "invalid control flow for 'parallel'")
     # 'break' that leaves the 'parallel' section is not valid either
     # or maybe we should generate a 'try' XXX
@@ -365,7 +367,7 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
           gatherArgs(c, value[1])
           analyseSons(c, value[1])
       if value.kind != nkEmpty:
-        for j in 0 .. it.len-3:
+        for j in 0..<it.len-2:
           if it[j].isLocal:
             let slot = c.getSlot(it[j].sym)
             if slot.lower.isNil: slot.lower = value
@@ -374,35 +376,39 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
   of nkCaseStmt: analyseCase(c, n)
   of nkWhen, nkIfStmt, nkIfExpr: analyseIf(c, n)
   of nkWhileStmt:
-    analyse(c, n.sons[0])
+    analyse(c, n[0])
     # 'while true' loop?
     inc c.inLoop
-    if isTrue(n.sons[0]):
-      analyseSons(c, n.sons[1])
+    if isTrue(n[0]):
+      analyseSons(c, n[1])
     else:
       # loop may never execute:
       let oldState = c.locals.len
       let oldFacts = c.guards.s.len
-      addFact(c.guards, canon(n.sons[0], c.guards.o))
-      analyse(c, n.sons[1])
+      addFact(c.guards, canon(n[0], c.graph.operators))
+      analyse(c, n[1])
       setLen(c.locals, oldState)
       setLen(c.guards.s, oldFacts)
       # we know after the loop the negation holds:
-      if not hasSubnodeWith(n.sons[1], nkBreakStmt):
-        addFactNeg(c.guards, canon(n.sons[0], c.guards.o))
+      if not hasSubnodeWith(n[1], nkBreakStmt):
+        addFactNeg(c.guards, canon(n[0], c.graph.operators))
     dec c.inLoop
   of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
-      nkMacroDef, nkTemplateDef, nkConstSection, nkPragma, nkFuncDef:
+      nkMacroDef, nkTemplateDef, nkConstSection, nkPragma, nkFuncDef,
+      nkMixinStmt, nkBindStmt, nkExportStmt:
     discard
   else:
     analyseSons(c, n)
 
-proc transformSlices(g: ModuleGraph; n: PNode): PNode =
+proc transformSlices(g: ModuleGraph; idgen: IdGenerator; n: PNode): PNode =
   if n.kind in nkCallKinds and n[0].kind == nkSym:
     let op = n[0].sym
     if op.name.s == "[]" and op.fromSystem:
       result = copyNode(n)
-      let opSlice = newSymNode(createMagic(g, "slice", mSlice))
+      var typ = newType(tyOpenArray, idgen, result.typ.owner)
+      typ.add result.typ.elementType
+      result.typ = typ
+      let opSlice = newSymNode(createMagic(g, idgen, "slice", mSlice))
       opSlice.typ = getSysType(g, n.info, tyInt)
       result.add opSlice
       result.add n[1]
@@ -412,18 +418,18 @@ proc transformSlices(g: ModuleGraph; n: PNode): PNode =
       return result
   if n.safeLen > 0:
     result = shallowCopy(n)
-    for i in 0 ..< n.len:
-      result.sons[i] = transformSlices(g, n.sons[i])
+    for i in 0..<n.len:
+      result[i] = transformSlices(g, idgen, n[i])
   else:
     result = n
 
-proc transformSpawn(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode
-proc transformSpawnSons(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode =
+proc transformSpawn(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n, barrier: PNode): PNode
+proc transformSpawnSons(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n, barrier: PNode): PNode =
   result = shallowCopy(n)
-  for i in 0 ..< n.len:
-    result.sons[i] = transformSpawn(g, owner, n.sons[i], barrier)
+  for i in 0..<n.len:
+    result[i] = transformSpawn(g, idgen, owner, n[i], barrier)
 
-proc transformSpawn(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode =
+proc transformSpawn(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n, barrier: PNode): PNode =
   case n.kind
   of nkVarSection, nkLetSection:
     result = nil
@@ -431,41 +437,41 @@ proc transformSpawn(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode =
       let b = it.lastSon
       if getMagic(b) == mSpawn:
         if it.len != 3: localError(g.config, it.info, "invalid context for 'spawn'")
-        let m = transformSlices(g, b)
+        let m = transformSlices(g, idgen, b)
         if result.isNil:
           result = newNodeI(nkStmtList, n.info)
           result.add n
-        let t = b[1][0].typ.sons[0]
+        let t = b[1][0].typ.returnType
         if spawnResult(t, true) == srByVar:
-          result.add wrapProcForSpawn(g, owner, m, b.typ, barrier, it[0])
-          it.sons[it.len-1] = newNodeI(nkEmpty, it.info)
+          result.add wrapProcForSpawn(g, idgen, owner, m, b.typ, barrier, it[0])
+          it[^1] = newNodeI(nkEmpty, it.info)
         else:
-          it.sons[it.len-1] = wrapProcForSpawn(g, owner, m, b.typ, barrier, nil)
+          it[^1] = wrapProcForSpawn(g, idgen, owner, m, b.typ, barrier, nil)
     if result.isNil: result = n
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     let b = n[1]
-    if getMagic(b) == mSpawn and (let t = b[1][0].typ.sons[0];
+    if getMagic(b) == mSpawn and (let t = b[1][0].typ.returnType;
         spawnResult(t, true) == srByVar):
-      let m = transformSlices(g, b)
-      return wrapProcForSpawn(g, owner, m, b.typ, barrier, n[0])
-    result = transformSpawnSons(g, owner, n, barrier)
+      let m = transformSlices(g, idgen, b)
+      return wrapProcForSpawn(g, idgen, owner, m, b.typ, barrier, n[0])
+    result = transformSpawnSons(g, idgen, owner, n, barrier)
   of nkCallKinds:
     if getMagic(n) == mSpawn:
-      result = transformSlices(g, n)
-      return wrapProcForSpawn(g, owner, result, n.typ, barrier, nil)
-    result = transformSpawnSons(g, owner, n, barrier)
+      result = transformSlices(g, idgen, n)
+      return wrapProcForSpawn(g, idgen, owner, result, n.typ, barrier, nil)
+    result = transformSpawnSons(g, idgen, owner, n, barrier)
   elif n.safeLen > 0:
-    result = transformSpawnSons(g, owner, n, barrier)
+    result = transformSpawnSons(g, idgen, owner, n, barrier)
   else:
     result = n
 
 proc checkArgs(a: var AnalysisCtx; n: PNode) =
-  discard "too implement"
+  discard "to implement"
 
 proc generateAliasChecks(a: AnalysisCtx; result: PNode) =
-  discard "too implement"
+  discard "to implement"
 
-proc liftParallel*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
+proc liftParallel*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): PNode =
   # this needs to be called after the 'for' loop elimination
 
   # first pass:
@@ -483,16 +489,16 @@ proc liftParallel*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
   checkArgs(a, body)
 
   var varSection = newNodeI(nkVarSection, n.info)
-  var temp = newSym(skTemp, getIdent(g.cache, "barrier"), owner, n.info)
+  var temp = newSym(skTemp, getIdent(g.cache, "barrier"), idgen, owner, n.info)
   temp.typ = magicsys.getCompilerProc(g, "Barrier").typ
   incl(temp.flags, sfFromGeneric)
   let tempNode = newSymNode(temp)
   varSection.addVar tempNode
 
-  let barrier = genAddrOf(tempNode)
+  let barrier = genAddrOf(tempNode, idgen)
   result = newNodeI(nkStmtList, n.info)
   generateAliasChecks(a, result)
   result.add varSection
-  result.add callCodegenProc(g, "openBarrier", barrier)
-  result.add transformSpawn(g, owner, body, barrier)
-  result.add callCodegenProc(g, "closeBarrier", barrier)
+  result.add callCodegenProc(g, "openBarrier", barrier.info, barrier)
+  result.add transformSpawn(g, idgen, owner, body, barrier)
+  result.add callCodegenProc(g, "closeBarrier", barrier.info, barrier)
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index 4d3ee0408..0a160897f 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -8,18 +8,41 @@
 #
 
 import
-  intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
-  wordrecg, strutils, options, guards, writetracking, lineinfos,
-  modulegraphs
+  ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
+  wordrecg, options, guards, lineinfos, semfold, semdata,
+  modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling,
+  semstrictfuncs, suggestsymdb, pushpoppragmas
+
+import std/[tables, intsets, strutils, sequtils]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 when defined(useDfa):
   import dfa
 
-# Second semantic checking pass over the AST. Necessary because the old
-# way had some inherent problems. Performs:
-#
-# * effect+exception tracking
-# * "usage before definition" checking
+import liftdestructors
+include sinkparameter_inference
+
+#[ Second semantic checking pass over the AST. Necessary because the old
+   way had some inherent problems. Performs:
+
+* effect+exception tracking
+* "usage before definition" checking
+* also now calls the "lift destructor logic" at strategic positions, this
+  is about to be put into the spec:
+
+We treat assignment and sinks and destruction as identical.
+
+In the construct let/var x = expr() x's type is marked.
+
+In x = y the type of x is marked.
+
+For every sink parameter of type T T is marked.
+
+For every call f() the return type of f() is marked.
+
+]#
 
 # ------------------------ exception and tag tracking -------------------------
 
@@ -43,56 +66,90 @@ discard """
 """
 
 type
+  CaughtExceptionsStack = object
+    nodes: seq[seq[PType]]
   TEffects = object
     exc: PNode  # stack of exceptions
+    when defined(nimsuggest):
+      caughtExceptions: CaughtExceptionsStack
     tags: PNode # list of tags
-    bottom, inTryStmt: int
+    forbids: PNode # list of tags
+    bottom, inTryStmt, inExceptOrFinallyStmt, leftPartOfAsgn, inIfStmt, currentBlock: int
     owner: PSym
+    ownerModule: PSym
     init: seq[int] # list of initialized variables
+    scopes: Table[int, int] # maps var-id to its scope (see also `currentBlock`).
     guards: TModel # nested guards
     locked: seq[PNode] # locked locations
-    gcUnsafe, isRecursive, isToplevel, hasSideEffect, inEnforcedGcSafe: bool
-    maxLockLevel, currLockLevel: TLockLevel
+    gcUnsafe, isRecursive, isTopLevel, hasSideEffect, inEnforcedGcSafe: bool
+    isInnerProc: bool
+    inEnforcedNoSideEffects: bool
+    currOptions: TOptions
+    optionsStack: seq[(TOptions, TNoteKinds)]
     config: ConfigRef
     graph: ModuleGraph
+    c: PContext
+    escapingParams: IntSet
   PEffects = var TEffects
 
-proc `<`(a, b: TLockLevel): bool {.borrow.}
-proc `<=`(a, b: TLockLevel): bool {.borrow.}
-proc `==`(a, b: TLockLevel): bool {.borrow.}
-proc max(a, b: TLockLevel): TLockLevel {.borrow.}
+const
+  errXCannotBeAssignedTo = "'$1' cannot be assigned to"
+  errLetNeedsInit = "'let' symbol requires an initialization"
+
+proc getObjDepth(t: PType): (int, ItemId) =
+  var x = t
+  result = (-1, default(ItemId))
+  var stack = newSeq[ItemId]()
+  while x != nil:
+    x = skipTypes(x, skipPtrs)
+    if x.kind != tyObject:
+      return (-3, default(ItemId))
+    stack.add x.itemId
+    x = x.baseClass
+    inc(result[0])
+  result[1] = stack[^2]
+
+proc collectObjectTree(graph: ModuleGraph, n: PNode) =
+  for section in n:
+    if section.kind == nkTypeDef and section[^1].kind in {nkObjectTy, nkRefTy, nkPtrTy} and section[^1].typ != nil:
+      let typ = section[^1].typ.skipTypes(skipPtrs)
+      if typ.kind == tyObject and typ.baseClass != nil:
+        let (depthLevel, root) = getObjDepth(typ)
+        if depthLevel != -3:
+          if depthLevel == 1:
+            graph.objectTree[root] = @[]
+          else:
+            if root notin graph.objectTree:
+              graph.objectTree[root] = @[(depthLevel, typ)]
+            else:
+              graph.objectTree[root].add (depthLevel, typ)
+
+proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
+  if typ == nil or sfGeneratedOp in tracked.owner.flags:
+    # don't create type bound ops for anything in a function with a `nodestroy` pragma
+    # bug #21987
+    return
+  when false:
+    let realType = typ.skipTypes(abstractInst)
+    if realType.kind == tyRef and
+        optSeqDestructors in tracked.config.globalOptions:
+      createTypeBoundOps(tracked.graph, tracked.c, realType.lastSon, info)
 
-proc isLocalVar(a: PEffects, s: PSym): bool =
-  s.kind in {skVar, skResult} and sfGlobal notin s.flags and
-    s.owner == a.owner and s.typ != nil
+  createTypeBoundOps(tracked.graph, tracked.c, typ, info, tracked.c.idgen)
+  if (tfHasAsgn in typ.flags) or
+      optSeqDestructors in tracked.config.globalOptions:
+    tracked.owner.flags.incl sfInjectDestructors
 
-proc getLockLevel(t: PType): TLockLevel =
-  var t = t
-  # tyGenericInst(TLock {tyGenericBody}, tyStatic, tyObject):
-  if t.kind == tyGenericInst and t.len == 3: t = t.sons[1]
-  if t.kind == tyStatic and t.n != nil and t.n.kind in {nkCharLit..nkInt64Lit}:
-    result = t.n.intVal.TLockLevel
+proc isLocalSym(a: PEffects, s: PSym): bool =
+  s.typ != nil and (s.kind in {skLet, skVar, skResult} or (s.kind == skParam and isOutParam(s.typ))) and
+    sfGlobal notin s.flags and s.owner == a.owner
 
 proc lockLocations(a: PEffects; pragma: PNode) =
   if pragma.kind != nkExprColonExpr:
     localError(a.config, pragma.info, "locks pragma without argument")
     return
-  var firstLL = TLockLevel(-1'i16)
   for x in pragma[1]:
-    let thisLL = getLockLevel(x.typ)
-    if thisLL != 0.TLockLevel:
-      if thisLL < 0.TLockLevel or thisLL > MaxLockLevel.TLockLevel:
-        localError(a.config, x.info, "invalid lock level: " & $thisLL)
-      elif firstLL < 0.TLockLevel: firstLL = thisLL
-      elif firstLL != thisLL:
-        localError(a.config, x.info,
-          "multi-lock requires the same static lock level for every operand")
-      a.maxLockLevel = max(a.maxLockLevel, firstLL)
     a.locked.add x
-  if firstLL >= 0.TLockLevel and firstLL != a.currLockLevel:
-    if a.currLockLevel > 0.TLockLevel and a.currLockLevel <= firstLL:
-      localError(a.config, pragma.info, "invalid nested locking")
-    a.currLockLevel = firstLL
 
 proc guardGlobal(a: PEffects; n: PNode; guard: PSym) =
   # check whether the corresponding lock is held:
@@ -101,7 +158,7 @@ proc guardGlobal(a: PEffects; n: PNode; guard: PSym) =
   # we allow accesses nevertheless in top level statements for
   # easier initialization:
   #if a.isTopLevel:
-  #  message(n.info, warnUnguardedAccess, renderTree(n))
+  #  message(a.config, n.info, warnUnguardedAccess, renderTree(n))
   #else:
   if not a.isTopLevel:
     localError(a.config, n.info, "unguarded access: " & renderTree(n))
@@ -109,21 +166,21 @@ proc guardGlobal(a: PEffects; n: PNode; guard: PSym) =
 # 'guard*' are checks which are concerned with 'guard' annotations
 # (var x{.guard: y.}: int)
 proc guardDotAccess(a: PEffects; n: PNode) =
-  let ri = n.sons[1]
+  let ri = n[1]
   if ri.kind != nkSym or ri.sym.kind != skField: return
   var g = ri.sym.guard
   if g.isNil or a.isTopLevel: return
   # fixup guard:
   if g.kind == skUnknown:
     var field: PSym = nil
-    var ty = n.sons[0].typ.skipTypes(abstractPtrs)
+    var ty = n[0].typ.skipTypes(abstractPtrs)
     if ty.kind == tyTuple and not ty.n.isNil:
       field = lookupInRecord(ty.n, g.name)
     else:
       while ty != nil and ty.kind == tyObject:
         field = lookupInRecord(ty.n, g.name)
         if field != nil: break
-        ty = ty.sons[0]
+        ty = ty[0]
         if ty == nil: break
         ty = ty.skipTypes(skipPtrs)
     if field == nil:
@@ -134,8 +191,8 @@ proc guardDotAccess(a: PEffects; n: PNode) =
     # XXX unfortunately this is not correct for generic instantiations!
   if g.kind == skField:
     let dot = newNodeI(nkDotExpr, n.info, 2)
-    dot.sons[0] = n.sons[0]
-    dot.sons[1] = newSymNode(g)
+    dot[0] = n[0]
+    dot[1] = newSymNode(g)
     dot.typ = g.typ
     for L in a.locked:
       #if a.guards.sameSubexprs(dot, L): return
@@ -145,28 +202,57 @@ proc guardDotAccess(a: PEffects; n: PNode) =
     guardGlobal(a, n, g)
 
 proc makeVolatile(a: PEffects; s: PSym) {.inline.} =
-  template compileToCpp(a): untyped =
-    a.config.cmd == cmdCompileToCpp or sfCompileToCpp in getModule(a.owner).flags
-  if a.inTryStmt > 0 and not compileToCpp(a):
+  if a.inTryStmt > 0 and a.config.exc == excSetjmp:
     incl(s.flags, sfVolatile)
 
+proc varDecl(a: PEffects; n: PNode) {.inline.} =
+  if n.kind == nkSym:
+    a.scopes[n.sym.id] = a.currentBlock
+
+proc skipHiddenDeref(n: PNode): PNode {.inline.} =
+  result = if n.kind == nkHiddenDeref: n[0] else: n
+
 proc initVar(a: PEffects, n: PNode; volatileCheck: bool) =
+  let n = skipHiddenDeref(n)
   if n.kind != nkSym: return
   let s = n.sym
-  if isLocalVar(a, s):
+  if isLocalSym(a, s):
     if volatileCheck: makeVolatile(a, s)
     for x in a.init:
-      if x == s.id: return
+      if x == s.id:
+        if strictDefs in a.c.features and s.kind == skLet:
+          localError(a.config, n.info, errXCannotBeAssignedTo %
+                    renderTree(n, {renderNoComments}
+                ))
+        return
     a.init.add s.id
+    if a.scopes.getOrDefault(s.id) == a.currentBlock:
+      #[ Consider this case:
+
+      var x: T
+      while true:
+        if cond:
+          x = T() #1
+        else:
+          x = T() #2
+        use x
+
+      Even though both #1 and #2 are first writes we must use the `=copy`
+      here so that the old value is destroyed because `x`'s destructor is
+      run outside of the while loop. This is why we need the check here that
+      the assignment is done in the same logical block as `x` was declared in.
+      ]#
+      n.flags.incl nfFirstWrite
 
 proc initVarViaNew(a: PEffects, n: PNode) =
+  let n = skipHiddenDeref(n)
   if n.kind != nkSym: return
   let s = n.sym
-  if {tfNeedsInit, tfNotNil} * s.typ.flags <= {tfNotNil}:
+  if {tfRequiresInit, tfNotNil} * s.typ.flags <= {tfNotNil}:
     # 'x' is not nil, but that doesn't mean its "not nil" children
     # are initialized:
     initVar(a, n, volatileCheck=true)
-  elif isLocalVar(a, s):
+  elif isLocalSym(a, s):
     makeVolatile(a, s)
 
 proc warnAboutGcUnsafe(n: PNode; conf: ConfigRef) =
@@ -185,16 +271,24 @@ proc markGcUnsafe(a: PEffects; reason: PNode) =
       if reason.kind == nkSym:
         a.owner.gcUnsafetyReason = reason.sym
       else:
-        a.owner.gcUnsafetyReason = newSym(skUnknown, a.owner.name,
+        a.owner.gcUnsafetyReason = newSym(skUnknown, a.owner.name, a.c.idgen,
                                           a.owner, reason.info, {})
 
-when true:
-  template markSideEffect(a: PEffects; reason: typed) =
+proc markSideEffect(a: PEffects; reason: PNode | PSym; useLoc: TLineInfo) =
+  if not a.inEnforcedNoSideEffects:
     a.hasSideEffect = true
-else:
-  template markSideEffect(a: PEffects; reason: typed) =
-    a.hasSideEffect = true
-    markGcUnsafe(a, reason)
+    if a.owner.kind in routineKinds:
+      var sym: PSym
+      when reason is PNode:
+        if reason.kind == nkSym:
+          sym = reason.sym
+        else:
+          let kind = if reason.kind == nkHiddenDeref: skParam else: skUnknown
+          sym = newSym(kind, a.owner.name, a.c.idgen, a.owner, reason.info, {})
+      else:
+        sym = reason
+      a.c.sideEffects.mgetOrPut(a.owner.id, @[]).add (useLoc, sym)
+    when false: markGcUnsafe(a, reason)
 
 proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet; conf: ConfigRef) =
   let u = s.gcUnsafetyReason
@@ -202,16 +296,26 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet; conf: Co
     let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated
     case u.kind
     of skLet, skVar:
-      message(conf, s.info, msgKind,
-        ("'$#' is not GC-safe as it accesses '$#'" &
-        " which is a global using GC'ed memory") % [s.name.s, u.name.s])
+      if u.typ.skipTypes(abstractInst).kind == tyProc:
+        message(conf, s.info, msgKind,
+          "'$#' is not GC-safe as it calls '$#'" %
+          [s.name.s, u.name.s])
+      else:
+        message(conf, s.info, msgKind,
+          ("'$#' is not GC-safe as it accesses '$#'" &
+          " which is a global using GC'ed memory") % [s.name.s, u.name.s])
     of routineKinds:
       # recursive call *always* produces only a warning so the full error
       # message is printed:
-      listGcUnsafety(u, true, cycleCheck, conf)
-      message(conf, s.info, msgKind,
-        "'$#' is not GC-safe as it calls '$#'" %
-        [s.name.s, u.name.s])
+      if u.kind == skMethod and {sfBase, sfThread} * u.flags == {sfBase}:
+        message(conf, u.info, msgKind,
+          "Base method '$#' requires explicit '{.gcsafe.}' to be GC-safe" %
+          [u.name.s])
+      else:
+        listGcUnsafety(u, true, cycleCheck, conf)
+        message(conf, s.info, msgKind,
+          "'$#' is not GC-safe as it calls '$#'" %
+          [s.name.s, u.name.s])
     of skParam, skForVar:
       message(conf, s.info, msgKind,
         "'$#' is not GC-safe as it performs an indirect call via '$#'" %
@@ -224,42 +328,95 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool; conf: ConfigRef) =
   var cycleCheck = initIntSet()
   listGcUnsafety(s, onlyWarning, cycleCheck, conf)
 
-proc useVar(a: PEffects, n: PNode) =
-  let s = n.sym
-  if isLocalVar(a, s):
-    if s.id notin a.init:
-      if {tfNeedsInit, tfNotNil} * s.typ.flags != {}:
-        message(a.config, n.info, warnProveInit, s.name.s)
-      else:
-        message(a.config, n.info, warnUninit, s.name.s)
-      # prevent superfluous warnings about the same variable:
-      a.init.add s.id
+proc listSideEffects(result: var string; s: PSym; cycleCheck: var IntSet;
+                     conf: ConfigRef; context: PContext; indentLevel: int) =
+  template addHint(msg; lineInfo; sym; level = indentLevel) =
+    result.addf("$# $# Hint: '$#' $#\n", repeat(">", level), conf $ lineInfo, sym, msg)
+  if context.sideEffects.hasKey(s.id):
+    for (useLineInfo, u) in context.sideEffects[s.id]:
+      if u != nil and not cycleCheck.containsOrIncl(u.id):
+        case u.kind
+        of skLet, skVar:
+          addHint("accesses global state '$#'" % u.name.s, useLineInfo, s.name.s)
+          addHint("accessed by '$#'" % s.name.s, u.info, u.name.s, indentLevel + 1)
+        of routineKinds:
+          addHint("calls `.sideEffect` '$#'" % u.name.s, useLineInfo, s.name.s)
+          addHint("called by '$#'" % s.name.s, u.info, u.name.s, indentLevel + 1)
+          listSideEffects(result, u, cycleCheck, conf, context, indentLevel + 2)
+        of skParam, skForVar:
+          addHint("calls routine via hidden pointer indirection", useLineInfo, s.name.s)
+        else:
+          addHint("calls routine via pointer indirection", useLineInfo, s.name.s)
+
+proc listSideEffects(result: var string; s: PSym; conf: ConfigRef; context: PContext) =
+  var cycleCheck = initIntSet()
+  result.addf("'$#' can have side effects\n", s.name.s)
+  listSideEffects(result, s, cycleCheck, conf, context, 1)
+
+proc useVarNoInitCheck(a: PEffects; n: PNode; s: PSym) =
   if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and
-      s.magic != mNimVm:
+      s.magic != mNimvm:
     if s.guard != nil: guardGlobal(a, n, s.guard)
     if {sfGlobal, sfThread} * s.flags == {sfGlobal} and
         (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
-      #if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
+      #if a.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n)
       markGcUnsafe(a, s)
-      markSideEffect(a, s)
-    else:
-      markSideEffect(a, s)
+    markSideEffect(a, s, n.info)
+  if s.owner != a.owner and s.kind in {skVar, skLet, skForVar, skResult, skParam} and
+     {sfGlobal, sfThread} * s.flags == {}:
+    a.isInnerProc = true
 
+proc useVar(a: PEffects, n: PNode) =
+  let s = n.sym
+  if a.inExceptOrFinallyStmt > 0:
+    incl s.flags, sfUsedInFinallyOrExcept
+  if isLocalSym(a, s):
+    if sfNoInit in s.flags:
+      # If the variable is explicitly marked as .noinit. do not emit any error
+      a.init.add s.id
+    elif s.id notin a.init:
+      if s.typ.requiresInit:
+        message(a.config, n.info, warnProveInit, s.name.s)
+      elif a.leftPartOfAsgn <= 0:
+        if strictDefs in a.c.features:
+          if s.kind == skLet:
+            localError(a.config, n.info, errLetNeedsInit)
+          else:
+            message(a.config, n.info, warnUninit, s.name.s)
+      # prevent superfluous warnings about the same variable:
+      a.init.add s.id
+  useVarNoInitCheck(a, n, s)
+
+type
+  BreakState = enum
+    bsNone
+    bsBreakOrReturn
+    bsNoReturn
 
 type
   TIntersection = seq[tuple[id, count: int]] # a simple count table
 
-proc addToIntersection(inter: var TIntersection, s: int) =
+proc addToIntersection(inter: var TIntersection, s: int, state: BreakState) =
   for j in 0..<inter.len:
     if s == inter[j].id:
-      inc inter[j].count
+      if state == bsNone:
+        inc inter[j].count
       return
-  inter.add((id: s, count: 1))
-
-proc throws(tracked, n: PNode) =
-  if n.typ == nil or n.typ.kind != tyError: tracked.add n
+  if state == bsNone:
+    inter.add((id: s, count: 1))
+  else:
+    inter.add((id: s, count: 0))
+
+proc throws(tracked, n, orig: PNode) =
+  if n.typ == nil or n.typ.kind != tyError:
+    if orig != nil:
+      let x = copyTree(orig)
+      x.typ = n.typ
+      tracked.add x
+    else:
+      tracked.add n
 
-proc getEbase(g: ModuleGraph; info: TLineInfo): PType =
+proc getEbase*(g: ModuleGraph; info: TLineInfo): PType =
   result = g.sysTypeFromName(info, "Exception")
 
 proc excType(g: ModuleGraph; n: PNode): PType =
@@ -277,40 +434,48 @@ proc createTag(g: ModuleGraph; n: PNode): PNode =
   result.typ = g.sysTypeFromName(n.info, "RootEffect")
   if not n.isNil: result.info = n.info
 
-proc addEffect(a: PEffects, e: PNode, useLineInfo=true) =
-  assert e.kind != nkRaiseStmt
+proc addRaiseEffect(a: PEffects, e, comesFrom: PNode) =
+  #assert e.kind != nkRaiseStmt
   var aa = a.exc
-  for i in a.bottom ..< aa.len:
-    if sameType(a.graph.excType(aa[i]), a.graph.excType(e)):
-      if not useLineInfo or a.config.cmd == cmdDoc: return
-      elif aa[i].info == e.info: return
-  throws(a.exc, e)
+  for i in a.bottom..<aa.len:
+    # we only track the first node that can have the effect E in order
+    # to safe space and time.
+    if sameType(a.graph.excType(aa[i]), a.graph.excType(e)): return
 
-proc addTag(a: PEffects, e: PNode, useLineInfo=true) =
-  var aa = a.tags
-  for i in 0 ..< aa.len:
-    if sameType(aa[i].typ.skipTypes(skipPtrs), e.typ.skipTypes(skipPtrs)):
-      if not useLineInfo or a.config.cmd == cmdDoc: return
-      elif aa[i].info == e.info: return
-  throws(a.tags, e)
+  if e.typ != nil:
+    if not isDefectException(e.typ):
+      throws(a.exc, e, comesFrom)
 
-proc mergeEffects(a: PEffects, b, comesFrom: PNode) =
+proc addTag(a: PEffects, e, comesFrom: PNode) =
+  var aa = a.tags
+  for i in 0..<aa.len:
+    # we only track the first node that can have the effect E in order
+    # to safe space and time.
+    if sameType(aa[i].typ.skipTypes(skipPtrs), e.typ.skipTypes(skipPtrs)): return
+  throws(a.tags, e, comesFrom)
+
+proc addNotTag(a: PEffects, e, comesFrom: PNode) =
+  var aa = a.forbids
+  for i in 0..<aa.len:
+    if sameType(aa[i].typ.skipTypes(skipPtrs), e.typ.skipTypes(skipPtrs)): return
+  throws(a.forbids, e, comesFrom)
+
+proc mergeRaises(a: PEffects, b, comesFrom: PNode) =
   if b.isNil:
-    addEffect(a, createRaise(a.graph, comesFrom))
+    addRaiseEffect(a, createRaise(a.graph, comesFrom), comesFrom)
   else:
-    for effect in items(b): addEffect(a, effect, useLineInfo=comesFrom != nil)
+    for effect in items(b): addRaiseEffect(a, effect, comesFrom)
 
 proc mergeTags(a: PEffects, b, comesFrom: PNode) =
   if b.isNil:
-    addTag(a, createTag(a.graph, comesFrom))
+    addTag(a, createTag(a.graph, comesFrom), comesFrom)
   else:
-    for effect in items(b): addTag(a, effect, useLineInfo=comesFrom != nil)
+    for effect in items(b): addTag(a, effect, comesFrom)
 
 proc listEffects(a: PEffects) =
   for e in items(a.exc):  message(a.config, e.info, hintUser, typeToString(e.typ))
   for e in items(a.tags): message(a.config, e.info, hintUser, typeToString(e.typ))
-  #if a.maxLockLevel != 0:
-  #  message(e.info, hintUser, "lockLevel: " & a.maxLockLevel)
+  for e in items(a.forbids): message(a.config, e.info, hintUser, typeToString(e.typ))
 
 proc catches(tracked: PEffects, e: PType) =
   let e = skipTypes(e, skipPtrs)
@@ -319,19 +484,31 @@ proc catches(tracked: PEffects, e: PType) =
   while i < L:
     # r supertype of e?
     if safeInheritanceDiff(tracked.graph.excType(tracked.exc[i]), e) <= 0:
-      tracked.exc.sons[i] = tracked.exc.sons[L-1]
+      tracked.exc[i] = tracked.exc[L-1]
       dec L
     else:
       inc i
-  if not isNil(tracked.exc.sons):
+  if tracked.exc.len > 0:
     setLen(tracked.exc.sons, L)
   else:
     assert L == 0
 
 proc catchesAll(tracked: PEffects) =
-  if not isNil(tracked.exc.sons):
+  if tracked.exc.len > 0:
     setLen(tracked.exc.sons, tracked.bottom)
 
+proc push(s: var CaughtExceptionsStack) =
+  s.nodes.add(@[])
+
+proc pop(s: var CaughtExceptionsStack) =
+  s.nodes.del(high(s.nodes))
+
+proc addCatch(s: var CaughtExceptionsStack, e: PType) =
+  s.nodes[high(s.nodes)].add(e)
+
+proc addCatchAll(s: var CaughtExceptionsStack) =
+  s.nodes[high(s.nodes)].add(nil)
+
 proc track(tracked: PEffects, n: PNode)
 proc trackTryStmt(tracked: PEffects, n: PNode) =
   let oldBottom = tracked.bottom
@@ -340,132 +517,115 @@ proc trackTryStmt(tracked: PEffects, n: PNode) =
   let oldState = tracked.init.len
   var inter: TIntersection = @[]
 
+  when defined(nimsuggest):
+    tracked.caughtExceptions.push
+    for i in 1..<n.len:
+      let b = n[i]
+      if b.kind == nkExceptBranch:
+        if b.len == 1:
+          tracked.caughtExceptions.addCatchAll
+        else:
+          for j in 0..<b.len - 1:
+            if b[j].isInfixAs():
+              assert(b[j][1].kind == nkType)
+              tracked.caughtExceptions.addCatch(b[j][1].typ)
+            else:
+              assert(b[j].kind == nkType)
+              tracked.caughtExceptions.addCatch(b[j].typ)
+      else:
+        assert b.kind == nkFinally
+
   inc tracked.inTryStmt
-  track(tracked, n.sons[0])
+  track(tracked, n[0])
   dec tracked.inTryStmt
   for i in oldState..<tracked.init.len:
-    addToIntersection(inter, tracked.init[i])
+    addToIntersection(inter, tracked.init[i], bsNone)
+
+  when defined(nimsuggest):
+    tracked.caughtExceptions.pop
 
   var branches = 1
   var hasFinally = false
-  for i in 1 ..< n.len:
-    let b = n.sons[i]
-    let blen = sonsLen(b)
+  inc tracked.inExceptOrFinallyStmt
+
+  # Collect the exceptions caught by the except branches
+  for i in 1..<n.len:
+    let b = n[i]
     if b.kind == nkExceptBranch:
       inc branches
-      if blen == 1:
+      if b.len == 1:
         catchesAll(tracked)
       else:
-        for j in countup(0, blen - 2):
-          if b.sons[j].isInfixAs():
-            assert(b.sons[j][1].kind == nkType)
-            catches(tracked, b.sons[j][1].typ)
+        for j in 0..<b.len - 1:
+          if b[j].isInfixAs():
+            assert(b[j][1].kind == nkType)
+            catches(tracked, b[j][1].typ)
+            createTypeBoundOps(tracked, b[j][2].typ, b[j][2].info)
           else:
-            assert(b.sons[j].kind == nkType)
-            catches(tracked, b.sons[j].typ)
+            assert(b[j].kind == nkType)
+            catches(tracked, b[j].typ)
+    else:
+      assert b.kind == nkFinally
+  # Add any other exception raised in the except bodies
+  for i in 1..<n.len:
+    let b = n[i]
+    if b.kind == nkExceptBranch:
       setLen(tracked.init, oldState)
-      track(tracked, b.sons[blen-1])
+      for j in 0..<b.len - 1:
+        if b[j].isInfixAs(): # skips initialization checks
+          assert(b[j][2].kind == nkSym)
+          tracked.init.add b[j][2].sym.id
+      track(tracked, b[^1])
       for i in oldState..<tracked.init.len:
-        addToIntersection(inter, tracked.init[i])
+        addToIntersection(inter, tracked.init[i], bsNone)
     else:
-      assert b.kind == nkFinally
       setLen(tracked.init, oldState)
-      track(tracked, b.sons[blen-1])
+      track(tracked, b[^1])
       hasFinally = true
 
   tracked.bottom = oldBottom
+  dec tracked.inExceptOrFinallyStmt
   if not hasFinally:
     setLen(tracked.init, oldState)
   for id, count in items(inter):
     if count == branches: tracked.init.add id
 
-proc isIndirectCall(n: PNode, owner: PSym): bool =
+proc isIndirectCall(tracked: PEffects; n: PNode): bool =
   # we don't count f(...) as an indirect call if 'f' is an parameter.
   # Instead we track expressions of type tyProc too. See the manual for
   # details:
   if n.kind != nkSym:
     result = true
   elif n.sym.kind == skParam:
-    result = owner != n.sym.owner or owner == nil
+    if laxEffects notin tracked.c.config.legacyFeatures:
+      if tracked.owner == n.sym.owner and sfEffectsDelayed in n.sym.flags:
+        result = false # it is not a harmful call
+      else:
+        result = true
+    else:
+      result = tracked.owner != n.sym.owner or tracked.owner == nil
   elif n.sym.kind notin routineKinds:
     result = true
+  else:
+    result = false
 
 proc isForwardedProc(n: PNode): bool =
   result = n.kind == nkSym and sfForward in n.sym.flags
 
 proc trackPragmaStmt(tracked: PEffects, n: PNode) =
-  for i in countup(0, sonsLen(n) - 1):
-    var it = n.sons[i]
-    if whichPragma(it) == wEffects:
+  for i in 0..<n.len:
+    var it = n[i]
+    let pragma = whichPragma(it)
+    case pragma
+    of wEffects:
       # list the computed effects up to here:
       listEffects(tracked)
-
-proc effectSpec(n: PNode, effectType: TSpecialWord): PNode =
-  for i in countup(0, sonsLen(n) - 1):
-    var it = n.sons[i]
-    if it.kind == nkExprColonExpr and whichPragma(it) == effectType:
-      result = it.sons[1]
-      if result.kind notin {nkCurly, nkBracket}:
-        result = newNodeI(nkCurly, result.info)
-        result.add(it.sons[1])
-      return
-
-proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, idx: int): PNode =
-  let spec = effectSpec(x, effectType)
-  if isNil(spec):
-    let s = n.sons[namePos].sym
-
-    let actual = s.typ.n.sons[0]
-    if actual.len != effectListLen: return
-    let real = actual.sons[idx]
-
-    # warning: hack ahead:
-    var effects = newNodeI(nkBracket, n.info, real.len)
-    for i in 0 ..< real.len:
-      var t = typeToString(real[i].typ)
-      if t.startsWith("ref "): t = substr(t, 4)
-      effects.sons[i] = newIdentNode(getIdent(cache, t), n.info)
-      # set the type so that the following analysis doesn't screw up:
-      effects.sons[i].typ = real[i].typ
-
-    result = newNode(nkExprColonExpr, n.info, @[
-      newIdentNode(getIdent(cache, specialWords[effectType]), n.info), effects])
-
-proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode =
-  let s = n.sons[namePos].sym
-  let params = s.typ.n
-
-  var effects = newNodeI(nkBracket, n.info)
-  for i in 1 ..< params.len:
-    if params[i].kind == nkSym and flag in params[i].sym.flags:
-      effects.add params[i]
-
-  if effects.len > 0:
-    result = newNode(nkExprColonExpr, n.info, @[
-      newIdentNode(getIdent(cache, pragmaName), n.info), effects])
-
-proc documentNewEffect(cache: IdentCache; n: PNode): PNode =
-  let s = n.sons[namePos].sym
-  if tfReturnsNew in s.typ.flags:
-    result = newIdentNode(getIdent(cache, "new"), n.info)
-
-proc documentRaises*(cache: IdentCache; n: PNode) =
-  if n.sons[namePos].kind != nkSym: return
-  let pragmas = n.sons[pragmasPos]
-  let p1 = documentEffect(cache, n, pragmas, wRaises, exceptionEffects)
-  let p2 = documentEffect(cache, n, pragmas, wTags, tagEffects)
-  let p3 = documentWriteEffect(cache, n, sfWrittenTo, "writes")
-  let p4 = documentNewEffect(cache, n)
-  let p5 = documentWriteEffect(cache, n, sfEscapes, "escapes")
-
-  if p1 != nil or p2 != nil or p3 != nil or p4 != nil or p5 != nil:
-    if pragmas.kind == nkEmpty:
-      n.sons[pragmasPos] = newNodeI(nkPragma, n.info)
-    if p1 != nil: n.sons[pragmasPos].add p1
-    if p2 != nil: n.sons[pragmasPos].add p2
-    if p3 != nil: n.sons[pragmasPos].add p3
-    if p4 != nil: n.sons[pragmasPos].add p4
-    if p5 != nil: n.sons[pragmasPos].add p5
+    of wPush:
+      processPushBackendOption(tracked.c.config, tracked.optionsStack, tracked.currOptions, n, i+1)
+    of wPop:
+      processPopBackendOption(tracked.c.config, tracked.optionsStack, tracked.currOptions)
+    else:
+      discard
 
 template notGcSafe(t): untyped = {tfGcSafe, tfNoSideEffect} * t.flags == {}
 
@@ -473,118 +633,117 @@ proc importedFromC(n: PNode): bool =
   # when imported from C, we assume GC-safety.
   result = n.kind == nkSym and sfImportc in n.sym.flags
 
-proc getLockLevel(s: PSym): TLockLevel =
-  result = s.typ.lockLevel
-  if result == UnspecifiedLockLevel:
-    if {sfImportc, sfNoSideEffect} * s.flags != {} or
-       tfNoSideEffect in s.typ.flags:
-      result = 0.TLockLevel
-    else:
-      result = UnknownLockLevel
-      #message(s.info, warnUser, "FOR THIS " & s.name.s)
-
-proc mergeLockLevels(tracked: PEffects, n: PNode, lockLevel: TLockLevel) =
-  if lockLevel >= tracked.currLockLevel:
-    # if in lock section:
-    if tracked.currLockLevel > 0.TLockLevel:
-      localError tracked.config, n.info, errGenerated,
-        "expected lock level < " & $tracked.currLockLevel &
-        " but got lock level " & $lockLevel
-    tracked.maxLockLevel = max(tracked.maxLockLevel, lockLevel)
-
 proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
-  let pragma = s.ast.sons[pragmasPos]
+  let pragma = s.ast[pragmasPos]
   let spec = effectSpec(pragma, wRaises)
-  mergeEffects(tracked, spec, n)
+  mergeRaises(tracked, spec, n)
 
   let tagSpec = effectSpec(pragma, wTags)
   mergeTags(tracked, tagSpec, n)
 
   if notGcSafe(s.typ) and sfImportc notin s.flags:
-    if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
+    if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config)
     markGcUnsafe(tracked, s)
   if tfNoSideEffect notin s.typ.flags:
-    markSideEffect(tracked, s)
-  mergeLockLevels(tracked, n, s.getLockLevel)
+    markSideEffect(tracked, s, n.info)
 
-proc procVarcheck(n: PNode; conf: ConfigRef) =
+proc procVarCheck(n: PNode; conf: ConfigRef) =
   if n.kind in nkSymChoices:
     for x in n: procVarCheck(x, conf)
   elif n.kind == nkSym and n.sym.magic != mNone and n.sym.kind in routineKinds:
-    localError(conf, n.info, "'$1' cannot be passed to a procvar" % n.sym.name.s)
+    localError(conf, n.info, ("'$1' is a built-in and cannot be used as " &
+      "a first-class procedure") % n.sym.name.s)
 
 proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
   let n = n.skipConv
   if paramType.isNil or paramType.kind != tyTypeDesc:
-    procVarcheck skipConvAndClosure(n), tracked.config
+    procVarCheck skipConvCastAndClosure(n), tracked.config
   #elif n.kind in nkSymChoices:
   #  echo "came here"
   let paramType = paramType.skipTypesOrNil(abstractInst)
-  if paramType != nil and tfNotNil in paramType.flags and
-      n.typ != nil and tfNotNil notin n.typ.flags:
-    if n.kind == nkAddr:
-      # addr(x[]) can't be proven, but addr(x) can:
-      if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return
-    elif (n.kind == nkSym and n.sym.kind in routineKinds) or
-         (n.kind in procDefs+{nkObjConstr, nkBracket, nkClosure, nkStrLit..nkTripleStrLit}) or
-         (n.kind in nkCallKinds and n[0].kind == nkSym and n[0].sym.magic == mArrToSeq):
-      # 'p' is not nil obviously:
-      return
-    case impliesNotNil(tracked.guards, n)
-    of impUnknown:
-      message(tracked.config, n.info, errGenerated,
-              "cannot prove '$1' is not nil" % n.renderTree)
-    of impNo:
-      message(tracked.config, n.info, errGenerated,
-              "'$1' is provably nil" % n.renderTree)
-    of impYes: discard
+  if paramType != nil and tfNotNil in paramType.flags and n.typ != nil:
+    let ntyp = n.typ.skipTypesOrNil({tyVar, tyLent, tySink})
+    if ntyp != nil and tfNotNil notin ntyp.flags:
+      if n.kind in {nkAddr, nkHiddenAddr}:
+        # addr(x[]) can't be proven, but addr(x) can:
+        if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return
+      elif (n.kind == nkSym and n.sym.kind in routineKinds) or
+          (n.kind in procDefs+{nkObjConstr, nkBracket, nkClosure, nkStrLit..nkTripleStrLit}) or
+          (n.kind in nkCallKinds and n[0].kind == nkSym and n[0].sym.magic == mArrToSeq) or
+          n.typ.kind == tyTypeDesc:
+        # 'p' is not nil obviously:
+        return
+      case impliesNotNil(tracked.guards, n)
+      of impUnknown:
+        message(tracked.config, n.info, errGenerated,
+                "cannot prove '$1' is not nil" % n.renderTree)
+      of impNo:
+        message(tracked.config, n.info, errGenerated,
+                "'$1' is provably nil" % n.renderTree)
+      of impYes: discard
 
 proc assumeTheWorst(tracked: PEffects; n: PNode; op: PType) =
-  addEffect(tracked, createRaise(tracked.graph, n))
-  addTag(tracked, createTag(tracked.graph, n))
-  let lockLevel = if op.lockLevel == UnspecifiedLockLevel: UnknownLockLevel
-                  else: op.lockLevel
-  #if lockLevel == UnknownLockLevel:
-  #  message(n.info, warnUser, "had to assume the worst here")
-  mergeLockLevels(tracked, n, lockLevel)
-
-proc isOwnedProcVar(n: PNode; owner: PSym): bool =
-  # XXX prove the soundness of this effect system rule
-  result = n.kind == nkSym and n.sym.kind == skParam and owner == n.sym.owner
+  addRaiseEffect(tracked, createRaise(tracked.graph, n), nil)
+  addTag(tracked, createTag(tracked.graph, n), nil)
 
-proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
-  let a = skipConvAndClosure(n)
+proc isOwnedProcVar(tracked: PEffects; n: PNode): bool =
+  # XXX prove the soundness of this effect system rule
+  result = n.kind == nkSym and n.sym.kind == skParam and
+    tracked.owner == n.sym.owner
+  #if result and sfPolymorphic notin n.sym.flags:
+  #  echo tracked.config $ n.info, " different here!"
+  if laxEffects notin tracked.c.config.legacyFeatures:
+    result = result and sfEffectsDelayed in n.sym.flags
+
+proc isNoEffectList(n: PNode): bool {.inline.} =
+  assert n.kind == nkEffectList
+  n.len == 0 or (n[tagEffects] == nil and n[exceptionEffects] == nil and n[forbiddenEffects] == nil)
+
+proc isTrival(caller: PNode): bool {.inline.} =
+  result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil, mMove, mWasMoved, mSwap}
+
+proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; argIndex: int; caller: PNode) =
+  let a = skipConvCastAndClosure(n)
   let op = a.typ
-  if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit:
-    internalAssert tracked.config, op.n.sons[0].kind == nkEffectList
-    var effectList = op.n.sons[0]
-    let s = n.skipConv
-    if s.kind == nkSym and s.sym.kind in routineKinds:
+  let param = if formals != nil and formals.n != nil and argIndex < formals.n.len: formals.n[argIndex].sym else: nil
+  # assume indirect calls are taken here:
+  if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit and
+      not isTrival(caller) and
+      ((param != nil and sfEffectsDelayed in param.flags) or laxEffects in tracked.c.config.legacyFeatures):
+
+    internalAssert tracked.config, op.n[0].kind == nkEffectList
+    var effectList = op.n[0]
+    var s = n.skipConv
+    if s.kind == nkCast and s[1].typ.kind == tyProc:
+      s = s[1]
+    if s.kind == nkSym and s.sym.kind in routineKinds and isNoEffectList(effectList):
       propagateEffects(tracked, n, s.sym)
-    elif effectList.len == 0:
+    elif isNoEffectList(effectList):
       if isForwardedProc(n):
         # we have no explicit effects but it's a forward declaration and so it's
         # stated there are no additional effects, so simply propagate them:
         propagateEffects(tracked, n, n.sym)
-      elif not isOwnedProcVar(a, tracked.owner):
+      elif not isOwnedProcVar(tracked, a):
         # we have no explicit effects so assume the worst:
         assumeTheWorst(tracked, n, op)
       # assume GcUnsafe unless in its type; 'forward' does not matter:
-      if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner):
-        if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
+      if notGcSafe(op) and not isOwnedProcVar(tracked, a):
+        if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config)
         markGcUnsafe(tracked, a)
-      elif tfNoSideEffect notin op.flags and not isOwnedProcVar(a, tracked.owner):
-        markSideEffect(tracked, a)
+      elif tfNoSideEffect notin op.flags and not isOwnedProcVar(tracked, a):
+        markSideEffect(tracked, a, n.info)
     else:
-      mergeEffects(tracked, effectList.sons[exceptionEffects], n)
-      mergeTags(tracked, effectList.sons[tagEffects], n)
+      mergeRaises(tracked, effectList[exceptionEffects], n)
+      mergeTags(tracked, effectList[tagEffects], n)
       if notGcSafe(op):
-        if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
+        if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config)
         markGcUnsafe(tracked, a)
       elif tfNoSideEffect notin op.flags:
-        markSideEffect(tracked, a)
-  if paramType != nil and paramType.kind == tyVar:
-    if n.kind == nkSym and isLocalVar(tracked, n.sym):
+        markSideEffect(tracked, a, n.info)
+  let paramType = if formals != nil and argIndex < formals.signatureLen: formals[argIndex] else: nil
+  if paramType != nil and paramType.kind in {tyVar}:
+    invalidateFacts(tracked.guards, n)
+    if n.kind == nkSym and isLocalSym(tracked, n.sym):
       makeVolatile(tracked, n.sym)
   if paramType != nil and paramType.kind == tyProc and tfGcSafe in paramType.flags:
     let argtype = skipTypes(a.typ, abstractInst)
@@ -594,193 +753,547 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
       localError(tracked.config, n.info, $n & " is not GC safe")
   notNilCheck(tracked, n, paramType)
 
-proc breaksBlock(n: PNode): bool =
-  # sematic check doesn't allow statements after raise, break, return or
+
+proc breaksBlock(n: PNode): BreakState =
+  # semantic check doesn't allow statements after raise, break, return or
   # call to noreturn proc, so it is safe to check just the last statements
   var it = n
   while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
     it = it.lastSon
 
-  result = it.kind in {nkBreakStmt, nkReturnStmt, nkRaiseStmt} or
-    it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
+  case it.kind
+  of nkBreakStmt, nkReturnStmt:
+    result = bsBreakOrReturn
+  of nkRaiseStmt:
+    result = bsNoReturn
+  of nkCallKinds:
+    if it[0].kind == nkSym and sfNoReturn in it[0].sym.flags:
+      result = bsNoReturn
+    else:
+      result = bsNone
+  else:
+    result = bsNone
+
+proc addIdToIntersection(tracked: PEffects, inter: var TIntersection, resCounter: var int,
+            hasBreaksBlock: BreakState, oldState: int, resSym: PSym, hasResult: bool) =
+  if hasResult:
+    var alreadySatisfy = false
+
+    if hasBreaksBlock == bsNoReturn:
+      alreadySatisfy = true
+      inc resCounter
+
+    for i in oldState..<tracked.init.len:
+      if tracked.init[i] == resSym.id:
+        if not alreadySatisfy:
+          inc resCounter
+          alreadySatisfy = true
+      else:
+        addToIntersection(inter, tracked.init[i], hasBreaksBlock)
+  else:
+    for i in oldState..<tracked.init.len:
+      addToIntersection(inter, tracked.init[i], hasBreaksBlock)
+
+template hasResultSym(s: PSym): bool =
+  s != nil and s.kind in {skProc, skFunc, skConverter, skMethod} and
+    not isEmptyType(s.typ.returnType)
 
 proc trackCase(tracked: PEffects, n: PNode) =
-  track(tracked, n.sons[0])
+  track(tracked, n[0])
+  inc tracked.inIfStmt
   let oldState = tracked.init.len
   let oldFacts = tracked.guards.s.len
-  let stringCase = skipTypes(n.sons[0].typ,
-        abstractVarRange-{tyTypeDesc}).kind in {tyFloat..tyFloat128, tyString}
-  let interesting = not stringCase and interestingCaseExpr(n.sons[0]) and
-        warnProveField in tracked.config.notes
+  let stringCase = n[0].typ != nil and skipTypes(n[0].typ,
+        abstractVarRange-{tyTypeDesc}).kind in {tyFloat..tyFloat128, tyString, tyCstring}
+  let interesting = not stringCase and interestingCaseExpr(n[0]) and
+        (tracked.config.hasWarn(warnProveField) or strictCaseObjects in tracked.c.features)
   var inter: TIntersection = @[]
   var toCover = 0
+  let hasResult = hasResultSym(tracked.owner)
+  let resSym = if hasResult: tracked.owner.ast[resultPos].sym else: nil
+  var resCounter = 0
+
   for i in 1..<n.len:
-    let branch = n.sons[i]
+    let branch = n[i]
     setLen(tracked.init, oldState)
     if interesting:
       setLen(tracked.guards.s, oldFacts)
       addCaseBranchFacts(tracked.guards, n, i)
-    for i in 0 ..< branch.len:
-      track(tracked, branch.sons[i])
-    if not breaksBlock(branch.lastSon): inc toCover
-    for i in oldState..<tracked.init.len:
-      addToIntersection(inter, tracked.init[i])
+    for i in 0..<branch.len:
+      track(tracked, branch[i])
+    let hasBreaksBlock = breaksBlock(branch.lastSon)
+    if hasBreaksBlock == bsNone:
+      inc toCover
+    addIdToIntersection(tracked, inter, resCounter, hasBreaksBlock, oldState, resSym, hasResult)
 
   setLen(tracked.init, oldState)
   if not stringCase or lastSon(n).kind == nkElse:
+    if hasResult and resCounter == n.len-1:
+        tracked.init.add resSym.id
     for id, count in items(inter):
       if count >= toCover: tracked.init.add id
     # else we can't merge
   setLen(tracked.guards.s, oldFacts)
+  dec tracked.inIfStmt
 
 proc trackIf(tracked: PEffects, n: PNode) =
-  track(tracked, n.sons[0].sons[0])
+  track(tracked, n[0][0])
+  inc tracked.inIfStmt
   let oldFacts = tracked.guards.s.len
-  addFact(tracked.guards, n.sons[0].sons[0])
+  addFact(tracked.guards, n[0][0])
   let oldState = tracked.init.len
 
+  let hasResult = hasResultSym(tracked.owner)
+  let resSym = if hasResult: tracked.owner.ast[resultPos].sym else: nil
+  var resCounter = 0
+
   var inter: TIntersection = @[]
   var toCover = 0
-  track(tracked, n.sons[0].sons[1])
-  if not breaksBlock(n.sons[0].sons[1]): inc toCover
-  for i in oldState..<tracked.init.len:
-    addToIntersection(inter, tracked.init[i])
+  track(tracked, n[0][1])
+  let hasBreaksBlock = breaksBlock(n[0][1])
+  if hasBreaksBlock == bsNone:
+    inc toCover
+  addIdToIntersection(tracked, inter, resCounter, hasBreaksBlock, oldState, resSym, hasResult)
 
   for i in 1..<n.len:
-    let branch = n.sons[i]
+    let branch = n[i]
     setLen(tracked.guards.s, oldFacts)
     for j in 0..i-1:
-      addFactNeg(tracked.guards, n.sons[j].sons[0])
+      addFactNeg(tracked.guards, n[j][0])
     if branch.len > 1:
-      addFact(tracked.guards, branch.sons[0])
+      addFact(tracked.guards, branch[0])
     setLen(tracked.init, oldState)
-    for i in 0 ..< branch.len:
-      track(tracked, branch.sons[i])
-    if not breaksBlock(branch.lastSon): inc toCover
-    for i in oldState..<tracked.init.len:
-      addToIntersection(inter, tracked.init[i])
+    for i in 0..<branch.len:
+      track(tracked, branch[i])
+    let hasBreaksBlock = breaksBlock(branch.lastSon)
+    if hasBreaksBlock == bsNone:
+      inc toCover
+    addIdToIntersection(tracked, inter, resCounter, hasBreaksBlock, oldState, resSym, hasResult)
+
   setLen(tracked.init, oldState)
   if lastSon(n).len == 1:
+    if hasResult and resCounter == n.len:
+        tracked.init.add resSym.id
     for id, count in items(inter):
       if count >= toCover: tracked.init.add id
     # else we can't merge as it is not exhaustive
   setLen(tracked.guards.s, oldFacts)
+  dec tracked.inIfStmt
 
 proc trackBlock(tracked: PEffects, n: PNode) =
   if n.kind in {nkStmtList, nkStmtListExpr}:
     var oldState = -1
     for i in 0..<n.len:
-      if hasSubnodeWith(n.sons[i], nkBreakStmt):
+      if hasSubnodeWith(n[i], nkBreakStmt):
         # block:
         #   x = def
         #   if ...: ... break # some nested break
         #   y = def
         # --> 'y' not defined after block!
         if oldState < 0: oldState = tracked.init.len
-      track(tracked, n.sons[i])
+      track(tracked, n[i])
     if oldState > 0: setLen(tracked.init, oldState)
   else:
     track(tracked, n)
 
-proc isTrue*(n: PNode): bool =
-  n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
-    n.kind == nkIntLit and n.intVal != 0
-
-proc paramType(op: PType, i: int): PType =
-  if op != nil and i < op.len: result = op.sons[i]
-
 proc cstringCheck(tracked: PEffects; n: PNode) =
-  if n.sons[0].typ.kind == tyCString and (let a = skipConv(n[1]);
+  if n[0].typ.kind == tyCstring and (let a = skipConv(n[1]);
       a.typ.kind == tyString and a.kind notin {nkStrLit..nkTripleStrLit}):
     message(tracked.config, n.info, warnUnsafeCode, renderTree(n))
 
-proc track(tracked: PEffects, n: PNode) =
-  case n.kind
-  of nkSym:
-    useVar(tracked, n)
-  of nkRaiseStmt:
-    n.sons[0].info = n.info
-    #throws(tracked.exc, n.sons[0])
-    addEffect(tracked, n.sons[0], useLineInfo=false)
-    for i in 0 ..< safeLen(n):
-      track(tracked, n.sons[i])
-  of nkCallKinds:
-    # p's effects are ours too:
-    let a = n.sons[0]
-    let op = a.typ
+proc patchResult(c: PEffects; n: PNode) =
+  if n.kind == nkSym and n.sym.kind == skResult:
+    let fn = c.owner
+    if fn != nil and fn.kind in routineKinds and fn.ast != nil and resultPos < fn.ast.len:
+      n.sym = fn.ast[resultPos].sym
+    else:
+      localError(c.config, n.info, "routine has no return type, but .requires contains 'result'")
+  else:
+    for i in 0..<safeLen(n):
+      patchResult(c, n[i])
+
+proc checkLe(c: PEffects; a, b: PNode) =
+  case proveLe(c.guards, a, b)
+  of impUnknown:
+    #for g in c.guards.s:
+    #  if g != nil: echo "I Know ", g
+    message(c.config, a.info, warnStaticIndexCheck,
+      "cannot prove: " & $a & " <= " & $b)
+  of impYes:
+    discard
+  of impNo:
+    message(c.config, a.info, warnStaticIndexCheck,
+      "can prove: " & $a & " > " & $b)
+
+proc checkBounds(c: PEffects; arr, idx: PNode) =
+  checkLe(c, lowBound(c.config, arr), idx)
+  checkLe(c, idx, highBound(c.config, arr, c.guards.g.operators))
+
+proc checkRange(c: PEffects; value: PNode; typ: PType) =
+  let t = typ.skipTypes(abstractInst - {tyRange})
+  if t.kind == tyRange:
+    let lowBound = copyTree(t.n[0])
+    lowBound.info = value.info
+    let highBound = copyTree(t.n[1])
+    highBound.info = value.info
+    checkLe(c, lowBound, value)
+    checkLe(c, value, highBound)
+
+#[
+proc passedToEffectsDelayedParam(tracked: PEffects; n: PNode) =
+  let t = n.typ.skipTypes(abstractInst)
+  if t.kind == tyProc:
+    if n.kind == nkSym and tracked.owner == n.sym.owner and sfEffectsDelayed in n.sym.flags:
+      discard "the arg is itself a delayed parameter, so do nothing"
+    else:
+      var effectList = t.n[0]
+      if effectList.len == effectListLen:
+        mergeRaises(tracked, effectList[exceptionEffects], n)
+        mergeTags(tracked, effectList[tagEffects], n)
+      if not importedFromC(n):
+        if notGcSafe(t):
+          if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config)
+          markGcUnsafe(tracked, n)
+        if tfNoSideEffect notin t.flags:
+          markSideEffect(tracked, n, n.info)
+]#
+
+proc checkForSink(tracked: PEffects; n: PNode) =
+  if tracked.inIfStmt == 0 and optSinkInference in tracked.config.options:
+    checkForSink(tracked.config, tracked.c.idgen, tracked.owner, n)
+
+proc markCaughtExceptions(tracked: PEffects; g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym) =
+  when defined(nimsuggest):
+    proc internalMarkCaughtExceptions(tracked: PEffects; q: var SuggestFileSymbolDatabase; info: TLineInfo) =
+      var si = q.findSymInfoIndex(info)
+      if si != -1:
+        q.caughtExceptionsSet[si] = true
+        for w1 in tracked.caughtExceptions.nodes:
+          for w2 in w1:
+            q.caughtExceptions[si].add(w2)
+
+    if optIdeExceptionInlayHints in tracked.config.globalOptions:
+      internalMarkCaughtExceptions(tracked, g.suggestSymbols.mgetOrPut(info.fileIndex, newSuggestFileSymbolDatabase(info.fileIndex, true)), info)
+
+proc trackCall(tracked: PEffects; n: PNode) =
+  template gcsafeAndSideeffectCheck() =
+    if notGcSafe(op) and not importedFromC(a):
+      # and it's not a recursive call:
+      if not (a.kind == nkSym and a.sym == tracked.owner):
+        if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config)
+        markGcUnsafe(tracked, a)
+    if tfNoSideEffect notin op.flags and not importedFromC(a):
+      # and it's not a recursive call:
+      if not (a.kind == nkSym and a.sym == tracked.owner):
+        markSideEffect(tracked, a, n.info)
+  # p's effects are ours too:
+  var a = n[0]
+  #if canRaise(a):
+  #  echo "this can raise ", tracked.config $ n.info
+  let op = a.typ
+  if n.typ != nil:
+    if tracked.owner.kind != skMacro and n.typ.skipTypes(abstractVar).kind != tyOpenArray:
+      createTypeBoundOps(tracked, n.typ, n.info)
+
+  when defined(nimsuggest):
+    var actualLoc = a.info
+    if n.kind == nkHiddenCallConv:
+      actualLoc = n.info
+    if a.kind == nkSym:
+      markCaughtExceptions(tracked, tracked.graph, actualLoc, a.sym, tracked.graph.usageSym)
+
+  let notConstExpr = getConstExpr(tracked.ownerModule, n, tracked.c.idgen, tracked.graph) == nil
+  if notConstExpr:
+    if a.kind == nkCast and a[1].typ.kind == tyProc:
+      a = a[1]
     # XXX: in rare situations, templates and macros will reach here after
     # calling getAst(templateOrMacro()). Currently, templates and macros
     # are indistinguishable from normal procs (both have tyProc type) and
     # we can detect them only by checking for attached nkEffectList.
-    if op != nil and op.kind == tyProc and op.n.sons[0].kind == nkEffectList:
+    if op != nil and op.kind == tyProc and op.n[0].kind == nkEffectList:
       if a.kind == nkSym:
         if a.sym == tracked.owner: tracked.isRecursive = true
         # even for recursive calls we need to check the lock levels (!):
-        mergeLockLevels(tracked, n, a.sym.getLockLevel)
-        if sfSideEffect in a.sym.flags: markSideEffect(tracked, a)
+        if sfSideEffect in a.sym.flags: markSideEffect(tracked, a, n.info)
       else:
-        mergeLockLevels(tracked, n, op.lockLevel)
-      var effectList = op.n.sons[0]
+        discard
+      var effectList = op.n[0]
       if a.kind == nkSym and a.sym.kind == skMethod:
+        if {sfBase, sfThread} * a.sym.flags == {sfBase}:
+          if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config)
+          markGcUnsafe(tracked, a)
         propagateEffects(tracked, n, a.sym)
-      elif effectList.len == 0:
+      elif isNoEffectList(effectList):
         if isForwardedProc(a):
           propagateEffects(tracked, n, a.sym)
-        elif isIndirectCall(a, tracked.owner):
+        elif isIndirectCall(tracked, a):
           assumeTheWorst(tracked, n, op)
+          gcsafeAndSideeffectCheck()
+        else:
+          if laxEffects notin tracked.c.config.legacyFeatures and a.kind == nkSym and
+              a.sym.kind in routineKinds:
+            propagateEffects(tracked, n, a.sym)
       else:
-        mergeEffects(tracked, effectList.sons[exceptionEffects], n)
-        mergeTags(tracked, effectList.sons[tagEffects], n)
-        if notGcSafe(op) and not importedFromC(a):
-          # and it's not a recursive call:
-          if not (a.kind == nkSym and a.sym == tracked.owner):
-            if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
-            markGcUnsafe(tracked, a)
-        if tfNoSideEffect notin op.flags and not importedFromC(a):
-          # and it's not a recursive call:
-          if not (a.kind == nkSym and a.sym == tracked.owner):
-            markSideEffect(tracked, a)
-    if a.kind != nkSym or a.sym.magic != mNBindSym:
-      for i in 1 ..< len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
+        mergeRaises(tracked, effectList[exceptionEffects], n)
+        mergeTags(tracked, effectList[tagEffects], n)
+        gcsafeAndSideeffectCheck()
+    if a.kind != nkSym or a.sym.magic notin {mNBindSym, mFinished, mExpandToAst, mQuoteAst}:
+      for i in 1..<n.len:
+        trackOperandForIndirectCall(tracked, n[i], op, i, a)
     if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
       # may not look like an assignment, but it is:
-      let arg = n.sons[1]
+      let arg = n[1]
       initVarViaNew(tracked, arg)
-      if arg.typ.len != 0 and {tfNeedsInit} * arg.typ.lastSon.flags != {}:
+      if arg.typ.hasElementType and {tfRequiresInit} * arg.typ.elementType.flags != {}:
         if a.sym.magic == mNewSeq and n[2].kind in {nkCharLit..nkUInt64Lit} and
             n[2].intVal == 0:
           # var s: seq[notnil];  newSeq(s, 0)  is a special case!
           discard
         else:
           message(tracked.config, arg.info, warnProveInit, $arg)
-    for i in 0 ..< safeLen(n):
-      track(tracked, n.sons[i])
+
+      # check required for 'nim check':
+      if n[1].typ.hasElementType:
+        createTypeBoundOps(tracked, n[1].typ.elementType, n.info)
+        createTypeBoundOps(tracked, n[1].typ, n.info)
+        # new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'?
+
+    elif a.kind == nkSym and a.sym.magic in {mArrGet, mArrPut} and
+        optStaticBoundsCheck in tracked.currOptions:
+      checkBounds(tracked, n[1], n[2])
+
+
+  if a.kind == nkSym and a.sym.name.s.len > 0 and a.sym.name.s[0] == '=' and
+        tracked.owner.kind != skMacro:
+    var opKind = find(AttachedOpToStr, a.sym.name.s.normalize)
+    if a.sym.name.s == "=": opKind = attachedAsgn.int
+    if opKind != -1:
+      # rebind type bounds operations after createTypeBoundOps call
+      let t = n[1].typ.skipTypes({tyAlias, tyVar})
+      if a.sym != getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind)):
+        createTypeBoundOps(tracked, t, n.info)
+        let op = getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind))
+        if op != nil:
+          n[0].sym = op
+
+  if op != nil and op.kind == tyProc:
+    for i in 1..<min(n.safeLen, op.signatureLen):
+      let paramType = op[i]
+      case paramType.kind
+      of tySink:
+        createTypeBoundOps(tracked, paramType.elementType, n.info)
+        checkForSink(tracked, n[i])
+      of tyVar:
+        if isOutParam(paramType):
+          # consider this case: p(out x, x); we want to remark that 'x' is not
+          # initialized until after the call. Since we do this after we analysed the
+          # call, this is fine.
+          initVar(tracked, n[i].skipHiddenAddr, false)
+        if strictFuncs in tracked.c.features and not tracked.inEnforcedNoSideEffects and
+           isDangerousLocation(n[i].skipHiddenAddr, tracked.owner):
+          if sfNoSideEffect in tracked.owner.flags:
+            localError(tracked.config, n[i].info,
+              "cannot pass $1 to `var T` parameter within a strict func" % renderTree(n[i]))
+          tracked.hasSideEffect = true
+      else: discard
+
+  if notConstExpr and (a.kind != nkSym or
+      a.sym.magic notin {mRunnableExamples, mNBindSym, mExpandToAst, mQuoteAst}
+  ):
+    # tracked after out analysis
+    for i in 0..<n.safeLen:
+      track(tracked, n[i])
+
+type
+  PragmaBlockContext = object
+    oldLocked: int
+    enforcedGcSafety, enforceNoSideEffects: bool
+    oldExc, oldTags, oldForbids: int
+    exc, tags, forbids: PNode
+
+proc createBlockContext(tracked: PEffects): PragmaBlockContext =
+  var oldForbidsLen = 0
+  if tracked.forbids != nil: oldForbidsLen = tracked.forbids.len
+  result = PragmaBlockContext(oldLocked: tracked.locked.len,
+    enforcedGcSafety: false, enforceNoSideEffects: false,
+    oldExc: tracked.exc.len, oldTags: tracked.tags.len,
+    oldForbids: oldForbidsLen)
+
+proc applyBlockContext(tracked: PEffects, bc: PragmaBlockContext) =
+  if bc.enforcedGcSafety: tracked.inEnforcedGcSafe = true
+  if bc.enforceNoSideEffects: tracked.inEnforcedNoSideEffects = true
+
+proc unapplyBlockContext(tracked: PEffects; bc: PragmaBlockContext) =
+  if bc.enforcedGcSafety: tracked.inEnforcedGcSafe = false
+  if bc.enforceNoSideEffects: tracked.inEnforcedNoSideEffects = false
+  setLen(tracked.locked, bc.oldLocked)
+  if bc.exc != nil:
+    # beware that 'raises: []' is very different from not saying
+    # anything about 'raises' in the 'cast' at all. Same applies for 'tags'.
+    setLen(tracked.exc.sons, bc.oldExc)
+    for e in bc.exc:
+      addRaiseEffect(tracked, e, e)
+  if bc.tags != nil:
+    setLen(tracked.tags.sons, bc.oldTags)
+    for t in bc.tags:
+      addTag(tracked, t, t)
+  if bc.forbids != nil:
+    setLen(tracked.forbids.sons, bc.oldForbids)
+    for t in bc.forbids:
+      addNotTag(tracked, t, t)
+
+proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) =
+  case whichPragma(pragma)
+  of wGcSafe:
+    bc.enforcedGcSafety = true
+  of wNoSideEffect:
+    bc.enforceNoSideEffects = true
+  of wTags:
+    let n = pragma[1]
+    if n.kind in {nkCurly, nkBracket}:
+      bc.tags = n
+    else:
+      bc.tags = newNodeI(nkArgList, pragma.info)
+      bc.tags.add n
+  of wForbids:
+    let n = pragma[1]
+    if n.kind in {nkCurly, nkBracket}:
+      bc.forbids = n
+    else:
+      bc.forbids = newNodeI(nkArgList, pragma.info)
+      bc.forbids.add n
+  of wRaises:
+    let n = pragma[1]
+    if n.kind in {nkCurly, nkBracket}:
+      bc.exc = n
+    else:
+      bc.exc = newNodeI(nkArgList, pragma.info)
+      bc.exc.add n
+  of wUncheckedAssign:
+    discard "handled in sempass1"
+  else:
+    localError(tracked.config, pragma.info,
+        "invalid pragma block: " & $pragma)
+
+proc trackInnerProc(tracked: PEffects, n: PNode) =
+  case n.kind
+  of nkSym:
+    let s = n.sym
+    if s.kind == skParam and s.owner == tracked.owner:
+      tracked.escapingParams.incl s.id
+  of nkNone..pred(nkSym), succ(nkSym)..nkNilLit:
+    discard
+  of nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, nkLambda, nkFuncDef, nkDo:
+    if n[0].kind == nkSym and n[0].sym.ast != nil:
+      trackInnerProc(tracked, getBody(tracked.graph, n[0].sym))
+  of nkTypeSection, nkMacroDef, nkTemplateDef, nkError,
+     nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
+     nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
+     nkTypeOfExpr, nkMixinStmt, nkBindStmt:
+    discard
+  else:
+    for ch in n: trackInnerProc(tracked, ch)
+
+proc allowCStringConv(n: PNode): bool =
+  case n.kind
+  of nkStrLit..nkTripleStrLit: result = true
+  of nkSym: result = n.sym.kind in {skConst, skParam}
+  of nkAddr: result = isCharArrayPtr(n.typ, true)
+  of nkCallKinds:
+    result = isCharArrayPtr(n.typ, n[0].kind == nkSym and n[0].sym.magic == mAddr)
+  else: result = isCharArrayPtr(n.typ, false)
+
+proc track(tracked: PEffects, n: PNode) =
+  case n.kind
+  of nkSym:
+    useVar(tracked, n)
+    if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags:
+      tracked.owner.flags.incl sfInjectDestructors
+      # bug #15038: ensure consistency
+      if n.typ == nil or (not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ)): n.typ = n.sym.typ
+  of nkHiddenAddr, nkAddr:
+    if n[0].kind == nkSym and isLocalSym(tracked, n[0].sym) and
+          n.typ.kind notin {tyVar, tyLent}:
+      useVarNoInitCheck(tracked, n[0], n[0].sym)
+    else:
+      track(tracked, n[0])
+  of nkRaiseStmt:
+    if n[0].kind != nkEmpty:
+      n[0].info = n.info
+      #throws(tracked.exc, n[0])
+      addRaiseEffect(tracked, n[0], n)
+      for i in 0..<n.safeLen:
+        track(tracked, n[i])
+      createTypeBoundOps(tracked, n[0].typ, n.info)
+    else:
+      # A `raise` with no arguments means we're going to re-raise the exception
+      # being handled or, if outside of an `except` block, a `ReraiseDefect`.
+      # Here we add a `Exception` tag in order to cover both the cases.
+      addRaiseEffect(tracked, createRaise(tracked.graph, n), nil)
+  of nkCallKinds:
+    trackCall(tracked, n)
   of nkDotExpr:
     guardDotAccess(tracked, n)
-    for i in 0 ..< len(n): track(tracked, n.sons[i])
+    let oldLeftPartOfAsgn = tracked.leftPartOfAsgn
+    tracked.leftPartOfAsgn = 0
+    for i in 0..<n.len: track(tracked, n[i])
+    tracked.leftPartOfAsgn = oldLeftPartOfAsgn
   of nkCheckedFieldExpr:
-    track(tracked, n.sons[0])
-    if warnProveField in tracked.config.notes:
-      checkFieldAccess(tracked.guards, n, tracked.config)
+    track(tracked, n[0])
+    if tracked.config.hasWarn(warnProveField) or strictCaseObjects in tracked.c.features:
+      checkFieldAccess(tracked.guards, n, tracked.config, strictCaseObjects in tracked.c.features)
   of nkTryStmt: trackTryStmt(tracked, n)
   of nkPragma: trackPragmaStmt(tracked, n)
-  of nkAsgn, nkFastAsgn:
-    track(tracked, n.sons[1])
-    initVar(tracked, n.sons[0], volatileCheck=true)
-    invalidateFacts(tracked.guards, n.sons[0])
-    track(tracked, n.sons[0])
-    addAsgnFact(tracked.guards, n.sons[0], n.sons[1])
-    notNilCheck(tracked, n.sons[1], n.sons[0].typ)
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
+    track(tracked, n[1])
+    initVar(tracked, n[0], volatileCheck=true)
+    invalidateFacts(tracked.guards, n[0])
+    inc tracked.leftPartOfAsgn
+    track(tracked, n[0])
+    dec tracked.leftPartOfAsgn
+    addAsgnFact(tracked.guards, n[0], n[1])
+    notNilCheck(tracked, n[1], n[0].typ)
     when false: cstringCheck(tracked, n)
+    if tracked.owner.kind != skMacro and n[0].typ.kind notin {tyOpenArray, tyVarargs}:
+      createTypeBoundOps(tracked, n[0].typ, n.info)
+    if n[0].kind != nkSym or not isLocalSym(tracked, n[0].sym):
+      checkForSink(tracked, n[1])
+      if strictFuncs in tracked.c.features and not tracked.inEnforcedNoSideEffects and
+         isDangerousLocation(n[0], tracked.owner):
+        tracked.hasSideEffect = true
+        if sfNoSideEffect in tracked.owner.flags:
+          localError(tracked.config, n[0].info,
+              "cannot mutate location $1 within a strict func" % renderTree(n[0]))
   of nkVarSection, nkLetSection:
     for child in n:
       let last = lastSon(child)
       if last.kind != nkEmpty: track(tracked, last)
-      if child.kind == nkIdentDefs and last.kind != nkEmpty:
-        for i in 0 .. child.len-3:
-          initVar(tracked, child.sons[i], volatileCheck=false)
-          addAsgnFact(tracked.guards, child.sons[i], last)
-          notNilCheck(tracked, last, child.sons[i].typ)
+      if tracked.owner.kind != skMacro:
+        if child.kind == nkVarTuple:
+          createTypeBoundOps(tracked, child[^1].typ, child.info)
+          for i in 0..<child.len-2:
+            createTypeBoundOps(tracked, child[i].typ, child.info)
+        else:
+          createTypeBoundOps(tracked, skipPragmaExpr(child[0]).typ, child.info)
+      if child.kind == nkIdentDefs:
+        for i in 0..<child.len-2:
+          let a = skipPragmaExpr(child[i])
+          varDecl(tracked, a)
+          if last.kind != nkEmpty:
+            initVar(tracked, a, volatileCheck=false)
+            addAsgnFact(tracked.guards, a, last)
+            notNilCheck(tracked, last, a.typ)
+      elif child.kind == nkVarTuple:
+        for i in 0..<child.len-1:
+          if child[i].kind == nkEmpty or
+            child[i].kind == nkSym and child[i].sym.name.id == ord(wUnderscore):
+            continue
+          varDecl(tracked, child[i])
+          if last.kind != nkEmpty:
+            initVar(tracked, child[i], volatileCheck=false)
+          if last.kind in {nkPar, nkTupleConstr}:
+            addAsgnFact(tracked.guards, child[i], last[i])
+            notNilCheck(tracked, last[i], child[i].typ)
       # since 'var (a, b): T = ()' is not even allowed, there is always type
       # inference for (a, b) and thus no nil checking is necessary.
   of nkConstSection:
@@ -789,175 +1302,408 @@ proc track(tracked: PEffects, n: PNode) =
       track(tracked, last)
   of nkCaseStmt: trackCase(tracked, n)
   of nkWhen, nkIfStmt, nkIfExpr: trackIf(tracked, n)
-  of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n.sons[1])
+  of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n[1])
   of nkWhileStmt:
-    track(tracked, n.sons[0])
     # 'while true' loop?
-    if isTrue(n.sons[0]):
-      trackBlock(tracked, n.sons[1])
+    inc tracked.currentBlock
+    if isTrue(n[0]):
+      trackBlock(tracked, n[1])
     else:
       # loop may never execute:
       let oldState = tracked.init.len
       let oldFacts = tracked.guards.s.len
-      addFact(tracked.guards, n.sons[0])
-      track(tracked, n.sons[1])
+      addFact(tracked.guards, n[0])
+      track(tracked, n[0])
+      track(tracked, n[1])
       setLen(tracked.init, oldState)
       setLen(tracked.guards.s, oldFacts)
+    dec tracked.currentBlock
   of nkForStmt, nkParForStmt:
     # we are very conservative here and assume the loop is never executed:
+    inc tracked.currentBlock
     let oldState = tracked.init.len
-    for i in 0 ..< len(n):
-      track(tracked, n.sons[i])
+
+    let oldFacts = tracked.guards.s.len
+    let iterCall = n[n.len-2]
+    if optStaticBoundsCheck in tracked.currOptions and iterCall.kind in nkCallKinds:
+      let op = iterCall[0]
+      if op.kind == nkSym and fromSystem(op.sym):
+        let iterVar = n[0]
+        case op.sym.name.s
+        of "..", "countup", "countdown":
+          let lower = iterCall[1]
+          let upper = iterCall[2]
+          # for i in 0..n   means  0 <= i and i <= n. Countdown is
+          # the same since only the iteration direction changes.
+          addFactLe(tracked.guards, lower, iterVar)
+          addFactLe(tracked.guards, iterVar, upper)
+        of "..<":
+          let lower = iterCall[1]
+          let upper = iterCall[2]
+          addFactLe(tracked.guards, lower, iterVar)
+          addFactLt(tracked.guards, iterVar, upper)
+        else: discard
+
+    for i in 0..<n.len-2:
+      let it = n[i]
+      track(tracked, it)
+      if tracked.owner.kind != skMacro:
+        if it.kind == nkVarTuple:
+          for x in it:
+            createTypeBoundOps(tracked, x.typ, x.info)
+        else:
+          createTypeBoundOps(tracked, it.typ, it.info)
+    let loopBody = n[^1]
+    if tracked.owner.kind != skMacro and iterCall.safeLen > 1:
+      # XXX this is a bit hacky:
+      if iterCall[1].typ != nil and iterCall[1].typ.skipTypes(abstractVar).kind notin {tyVarargs, tyOpenArray}:
+        createTypeBoundOps(tracked, iterCall[1].typ, iterCall[1].info)
+    track(tracked, iterCall)
+    track(tracked, loopBody)
     setLen(tracked.init, oldState)
+    setLen(tracked.guards.s, oldFacts)
+    dec tracked.currentBlock
+
   of nkObjConstr:
-    when false: track(tracked, n.sons[0])
+    when false: track(tracked, n[0])
     let oldFacts = tracked.guards.s.len
-    for i in 1 ..< len(n):
-      let x = n.sons[i]
+    for i in 1..<n.len:
+      let x = n[i]
       track(tracked, x)
-      if x.sons[0].kind == nkSym and sfDiscriminant in x.sons[0].sym.flags:
+      if x[0].kind == nkSym and sfDiscriminant in x[0].sym.flags:
         addDiscriminantFact(tracked.guards, x)
+      if tracked.owner.kind != skMacro:
+        createTypeBoundOps(tracked, x[1].typ, n.info)
+
+      if x.kind == nkExprColonExpr:
+        if x[0].kind == nkSym:
+          notNilCheck(tracked, x[1], x[0].sym.typ)
+        checkForSink(tracked, x[1])
+      else:
+        checkForSink(tracked, x)
     setLen(tracked.guards.s, oldFacts)
+    if tracked.owner.kind != skMacro:
+      # XXX n.typ can be nil in runnableExamples, we need to do something about it.
+      if n.typ != nil and n.typ.skipTypes(abstractInst).kind == tyRef:
+        createTypeBoundOps(tracked, n.typ.elementType, n.info)
+      createTypeBoundOps(tracked, n.typ, n.info)
+  of nkTupleConstr:
+    for i in 0..<n.len:
+      track(tracked, n[i])
+      notNilCheck(tracked, n[i].skipColon, n[i].typ)
+      if tracked.owner.kind != skMacro:
+        if n[i].kind == nkExprColonExpr:
+          createTypeBoundOps(tracked, n[i][0].typ, n.info)
+        else:
+          createTypeBoundOps(tracked, n[i].typ, n.info)
+      checkForSink(tracked, n[i])
   of nkPragmaBlock:
-    let pragmaList = n.sons[0]
-    let oldLocked = tracked.locked.len
-    let oldLockLevel = tracked.currLockLevel
-    var enforcedGcSafety = false
-    for i in 0 ..< pragmaList.len:
-      let pragma = whichPragma(pragmaList.sons[i])
-      if pragma == wLocks:
-        lockLocations(tracked, pragmaList.sons[i])
-      elif pragma == wGcSafe:
-        enforcedGcSafety = true
-    if enforcedGcSafety: tracked.inEnforcedGcSafe = true
+    let pragmaList = n[0]
+    var bc = createBlockContext(tracked)
+    for i in 0..<pragmaList.len:
+      let pragma = whichPragma(pragmaList[i])
+      case pragma
+      of wLocks:
+        lockLocations(tracked, pragmaList[i])
+      of wGcSafe:
+        bc.enforcedGcSafety = true
+      of wNoSideEffect:
+        bc.enforceNoSideEffects = true
+      of wCast:
+        castBlock(tracked, pragmaList[i][1], bc)
+      else:
+        discard
+    applyBlockContext(tracked, bc)
     track(tracked, n.lastSon)
-    if enforcedGcSafety: tracked.inEnforcedGcSafe = false
-    setLen(tracked.locked, oldLocked)
-    tracked.currLockLevel = oldLockLevel
-  of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
-      nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
+    unapplyBlockContext(tracked, bc)
+
+  of nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, nkLambda, nkFuncDef, nkDo:
+    if n[0].kind == nkSym and n[0].sym.ast != nil:
+      trackInnerProc(tracked, getBody(tracked.graph, n[0].sym))
+  of nkMacroDef, nkTemplateDef:
     discard
-  of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv:
-    if n.len == 2: track(tracked, n.sons[1])
+  of nkTypeSection:
+    if tracked.isTopLevel:
+      collectObjectTree(tracked.graph, n)
+  of nkCast:
+    if n.len == 2:
+      track(tracked, n[1])
+      if tracked.owner.kind != skMacro:
+        createTypeBoundOps(tracked, n.typ, n.info)
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+    if n.kind in {nkHiddenStdConv, nkHiddenSubConv} and
+        n.typ.skipTypes(abstractInst).kind == tyCstring and
+        not allowCStringConv(n[1]):
+      message(tracked.config, n.info, warnCstringConv,
+        "implicit conversion to 'cstring' from a non-const location: $1; this will become a compile time error in the future" %
+          $n[1])
+    if n.typ.skipTypes(abstractInst).kind == tyCstring and
+        isCharArrayPtr(n[1].typ, true):
+      message(tracked.config, n.info, warnPtrToCstringConv,
+          $n[1].typ)
+
+
+    let t = n.typ.skipTypes(abstractInst)
+    if t.kind == tyEnum:
+      if tfEnumHasHoles in t.flags:
+        message(tracked.config, n.info, warnHoleEnumConv, "conversion to enum with holes is unsafe: $1" % $n)
+      else:
+        message(tracked.config, n.info, warnAnyEnumConv, "enum conversion: $1" % $n)
+
+    if n.len == 2:
+      track(tracked, n[1])
+      if tracked.owner.kind != skMacro:
+        createTypeBoundOps(tracked, n.typ, n.info)
+        # This is a hacky solution in order to fix bug #13110. Hopefully
+        # a better solution will come up eventually.
+        if n[1].typ.kind != tyString:
+          createTypeBoundOps(tracked, n[1].typ, n[1].info)
+      if optStaticBoundsCheck in tracked.currOptions:
+        checkRange(tracked, n[1], n.typ)
   of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
-    if n.len == 1: track(tracked, n.sons[0])
+    if n.len == 1:
+      track(tracked, n[0])
+      if tracked.owner.kind != skMacro:
+        createTypeBoundOps(tracked, n.typ, n.info)
+        createTypeBoundOps(tracked, n[0].typ, n[0].info)
+      if optStaticBoundsCheck in tracked.currOptions:
+        checkRange(tracked, n[0], n.typ)
+  of nkBracket:
+    for i in 0..<n.safeLen:
+      track(tracked, n[i])
+      checkForSink(tracked, n[i])
+    if tracked.owner.kind != skMacro:
+      createTypeBoundOps(tracked, n.typ, n.info)
+  of nkBracketExpr:
+    if optStaticBoundsCheck in tracked.currOptions and n.len == 2:
+      if n[0].typ != nil and skipTypes(n[0].typ, abstractVar).kind != tyTuple:
+        checkBounds(tracked, n[0], n[1])
+    track(tracked, n[0])
+    dec tracked.leftPartOfAsgn
+    for i in 1 ..< n.len: track(tracked, n[i])
+    inc tracked.leftPartOfAsgn
+  of nkError:
+    localError(tracked.config, n.info, errorToString(tracked.config, n))
   else:
-    for i in 0 ..< safeLen(n): track(tracked, n.sons[i])
+    for i in 0..<n.safeLen: track(tracked, n[i])
 
 proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool =
-  result = safeInheritanceDiff(g.excType(real), spec.typ) <= 0
+  if spec.typ.kind == tyOr:
+    result = false
+    for t in spec.typ.kids:
+      if safeInheritanceDiff(g.excType(real), t) <= 0:
+        return true
+  else:
+    return safeInheritanceDiff(g.excType(real), spec.typ) <= 0
 
-proc checkRaisesSpec(g: ModuleGraph; spec, real: PNode, msg: string, hints: bool;
-                     effectPredicate: proc (g: ModuleGraph; a, b: PNode): bool {.nimcall.}) =
+proc checkRaisesSpec(g: ModuleGraph; emitWarnings: bool; spec, real: PNode, msg: string, hints: bool;
+                     effectPredicate: proc (g: ModuleGraph; a, b: PNode): bool {.nimcall.};
+                     hintsArg: PNode = nil; isForbids: bool = false) =
   # check that any real exception is listed in 'spec'; mark those as used;
   # report any unused exception
   var used = initIntSet()
   for r in items(real):
     block search:
-      for s in 0 ..< spec.len:
+      for s in 0..<spec.len:
         if effectPredicate(g, spec[s], r):
+          if isForbids: break
           used.incl(s)
           break search
+        if isForbids:
+          break search
       # XXX call graph analysis would be nice here!
       pushInfoContext(g.config, spec.info)
-      localError(g.config, r.info, errGenerated, msg & typeToString(r.typ))
+      var rr = if r.kind == nkRaiseStmt: r[0] else: r
+      while rr.kind in {nkStmtList, nkStmtListExpr} and rr.len > 0: rr = rr.lastSon
+      message(g.config, r.info, if emitWarnings: warnEffect else: errGenerated,
+              renderTree(rr) & " " & msg & typeToString(r.typ))
       popInfoContext(g.config)
   # hint about unnecessarily listed exception types:
   if hints:
-    for s in 0 ..< spec.len:
+    for s in 0..<spec.len:
       if not used.contains(s):
-        message(g.config, spec[s].info, hintXDeclaredButNotUsed, renderTree(spec[s]))
+        message(g.config, spec[s].info, hintXCannotRaiseY,
+                "'$1' cannot raise '$2'" % [renderTree(hintsArg), renderTree(spec[s])])
 
 proc checkMethodEffects*(g: ModuleGraph; disp, branch: PSym) =
   ## checks for consistent effects for multi methods.
-  let actual = branch.typ.n.sons[0]
+  let actual = branch.typ.n[0]
   if actual.len != effectListLen: return
 
-  let p = disp.ast.sons[pragmasPos]
+  let p = disp.ast[pragmasPos]
   let raisesSpec = effectSpec(p, wRaises)
   if not isNil(raisesSpec):
-    checkRaisesSpec(g, raisesSpec, actual.sons[exceptionEffects],
+    checkRaisesSpec(g, false, raisesSpec, actual[exceptionEffects],
       "can raise an unlisted exception: ", hints=off, subtypeRelation)
   let tagsSpec = effectSpec(p, wTags)
   if not isNil(tagsSpec):
-    checkRaisesSpec(g, tagsSpec, actual.sons[tagEffects],
+    checkRaisesSpec(g, false, tagsSpec, actual[tagEffects],
       "can have an unlisted effect: ", hints=off, subtypeRelation)
+  let forbidsSpec = effectSpec(p, wForbids)
+  if not isNil(forbidsSpec):
+    checkRaisesSpec(g, false, forbidsSpec, actual[tagEffects],
+      "has an illegal effect: ", hints=off, subtypeRelation, isForbids=true)
   if sfThread in disp.flags and notGcSafe(branch.typ):
     localError(g.config, branch.info, "base method is GC-safe, but '$1' is not" %
                                 branch.name.s)
-  if branch.typ.lockLevel > disp.typ.lockLevel:
-    when true:
-      message(g.config, branch.info, warnLockLevel,
-        "base method has lock level $1, but dispatcher has $2" %
-          [$branch.typ.lockLevel, $disp.typ.lockLevel])
-    else:
-      # XXX make this an error after bigbreak has been released:
-      localError(g.config, branch.info,
-        "base method has lock level $1, but dispatcher has $2" %
-          [$branch.typ.lockLevel, $disp.typ.lockLevel])
+  when defined(drnim):
+    if not g.compatibleProps(g, disp.typ, branch.typ):
+      localError(g.config, branch.info, "for method '" & branch.name.s &
+        "' the `.requires` or `.ensures` properties are incompatible.")
 
-proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) =
-  var effects = t.n.sons[0]
+proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode; s: PSym = nil) =
+  var effects = t.n[0]
   if t.kind != tyProc or effects.kind != nkEffectList: return
-
-  let
-    raisesSpec = effectSpec(n, wRaises)
-    tagsSpec = effectSpec(n, wTags)
-  if not isNil(raisesSpec) or not isNil(tagsSpec):
+  if n.kind != nkEmpty:
     internalAssert g.config, effects.len == 0
     newSeq(effects.sons, effectListLen)
+    let raisesSpec = effectSpec(n, wRaises)
     if not isNil(raisesSpec):
-      effects.sons[exceptionEffects] = raisesSpec
-    if not isNil(tagsSpec):
-      effects.sons[tagEffects] = tagsSpec
+      effects[exceptionEffects] = raisesSpec
+    elif s != nil and (s.magic != mNone or {sfImportc, sfExportc} * s.flags == {sfImportc}):
+      effects[exceptionEffects] = newNodeI(nkArgList, effects.info)
 
-proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) =
+    let tagsSpec = effectSpec(n, wTags)
+    if not isNil(tagsSpec):
+      effects[tagEffects] = tagsSpec
+    elif s != nil and (s.magic != mNone or {sfImportc, sfExportc} * s.flags == {sfImportc}):
+      effects[tagEffects] = newNodeI(nkArgList, effects.info)
+
+    let forbidsSpec = effectSpec(n, wForbids)
+    if not isNil(forbidsSpec):
+      effects[forbiddenEffects] = forbidsSpec
+    elif s != nil and (s.magic != mNone or {sfImportc, sfExportc} * s.flags == {sfImportc}):
+      effects[forbiddenEffects] = newNodeI(nkArgList, effects.info)
+
+    let requiresSpec = propSpec(n, wRequires)
+    if not isNil(requiresSpec):
+      effects[requiresEffects] = requiresSpec
+    let ensuresSpec = propSpec(n, wEnsures)
+    if not isNil(ensuresSpec):
+      effects[ensuresEffects] = ensuresSpec
+
+    effects[pragmasEffects] = n
+  if s != nil and s.magic != mNone:
+    if s.magic != mEcho:
+      t.flags.incl tfNoSideEffect
+
+proc rawInitEffects(g: ModuleGraph; effects: PNode) =
   newSeq(effects.sons, effectListLen)
-  effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
-  effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
-  effects.sons[usesEffects] = g.emptyNode
-  effects.sons[writeEffects] = g.emptyNode
-
-  t.exc = effects.sons[exceptionEffects]
-  t.tags = effects.sons[tagEffects]
-  t.owner = s
-  t.init = @[]
-  t.guards.s = @[]
-  t.guards.o = initOperators(g)
-  t.locked = @[]
-  t.graph = g
-  t.config = g.config
-
-proc trackProc*(g: ModuleGraph; s: PSym, body: PNode) =
-  var effects = s.typ.n.sons[0]
+  effects[exceptionEffects] = newNodeI(nkArgList, effects.info)
+  effects[tagEffects] = newNodeI(nkArgList, effects.info)
+  effects[forbiddenEffects] = newNodeI(nkArgList, effects.info)
+  effects[requiresEffects] = g.emptyNode
+  effects[ensuresEffects] = g.emptyNode
+  effects[pragmasEffects] = g.emptyNode
+
+proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; c: PContext): TEffects =
+  rawInitEffects(g, effects)
+
+  result = TEffects(exc: effects[exceptionEffects], tags: effects[tagEffects],
+            forbids: effects[forbiddenEffects], owner: s, ownerModule: s.getModule,
+            init: @[], locked: @[], graph: g, config: g.config, c: c,
+            currentBlock: 1, optionsStack: @[(g.config.options, g.config.notes)]
+  )
+  result.guards.s = @[]
+  result.guards.g = g
+  when defined(drnim):
+    result.currOptions = g.config.options + s.options - {optStaticBoundsCheck}
+  else:
+    result.currOptions = g.config.options + s.options
+  result.guards.beSmart = optStaticBoundsCheck in result.currOptions
+
+proc hasRealBody(s: PSym): bool =
+  ## also handles importc procs with runnableExamples, which requires `=`,
+  ## which is not a real implementation, refs #14314
+  result = {sfForward, sfImportc} * s.flags == {}
+
+proc trackProc*(c: PContext; s: PSym, body: PNode) =
+  let g = c.graph
+  when defined(nimsuggest):
+    if g.config.expandDone():
+      return
+  var effects = s.typ.n[0]
   if effects.kind != nkEffectList: return
   # effects already computed?
-  if sfForward in s.flags: return
-  if effects.len == effectListLen: return
+  if not s.hasRealBody: return
+  let emitWarnings = tfEffectSystemWorkaround in s.typ.flags
+  if effects.len == effectListLen and not emitWarnings: return
+
+  var inferredEffects = newNodeI(nkEffectList, s.info)
+
+  var t: TEffects = initEffects(g, inferredEffects, s, c)
+  rawInitEffects g, effects
+
+  if not isEmptyType(s.typ.returnType) and
+     s.kind in {skProc, skFunc, skConverter, skMethod}:
+    var res = s.ast[resultPos].sym # get result symbol
+    t.scopes[res.id] = t.currentBlock
 
-  var t: TEffects
-  initEffects(g, effects, s, t)
   track(t, body)
-  if not isEmptyType(s.typ.sons[0]) and
-      {tfNeedsInit, tfNotNil} * s.typ.sons[0].flags != {} and
-      s.kind in {skProc, skFunc, skConverter, skMethod}:
-    var res = s.ast.sons[resultPos].sym # get result symbol
-    if res.id notin t.init:
-      message(g.config, body.info, warnProveInit, "result")
-  let p = s.ast.sons[pragmasPos]
+
+  if s.kind != skMacro:
+    let params = s.typ.n
+    for i in 1..<params.len:
+      let param = params[i].sym
+      let typ = param.typ
+      if isSinkTypeForParam(typ) or
+          (t.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
+            (isClosure(typ.skipTypes(abstractInst)) or param.id in t.escapingParams)):
+        createTypeBoundOps(t, typ, param.info)
+      if isOutParam(typ) and param.id notin t.init:
+        message(g.config, param.info, warnProveInit, param.name.s)
+
+  if not isEmptyType(s.typ.returnType) and
+     (s.typ.returnType.requiresInit or s.typ.returnType.skipTypes(abstractInst).kind == tyVar or
+       strictDefs in c.features) and
+     s.kind in {skProc, skFunc, skConverter, skMethod} and s.magic == mNone:
+    var res = s.ast[resultPos].sym # get result symbol
+    if res.id notin t.init and breaksBlock(body) != bsNoReturn:
+      if tfRequiresInit in s.typ.returnType.flags:
+        localError(g.config, body.info, "'$1' requires explicit initialization" % "result")
+      else:
+        message(g.config, body.info, warnProveInit, "result")
+  let p = s.ast[pragmasPos]
   let raisesSpec = effectSpec(p, wRaises)
   if not isNil(raisesSpec):
-    checkRaisesSpec(g, raisesSpec, t.exc, "can raise an unlisted exception: ",
-                    hints=on, subtypeRelation)
+    let useWarning = s.name.s == "=destroy"
+    checkRaisesSpec(g, useWarning, raisesSpec, t.exc, "can raise an unlisted exception: ",
+                    hints=on, subtypeRelation, hintsArg=s.ast[0])
     # after the check, use the formal spec:
-    effects.sons[exceptionEffects] = raisesSpec
+    effects[exceptionEffects] = raisesSpec
+  else:
+    effects[exceptionEffects] = t.exc
 
   let tagsSpec = effectSpec(p, wTags)
   if not isNil(tagsSpec):
-    checkRaisesSpec(g, tagsSpec, t.tags, "can have an unlisted effect: ",
+    checkRaisesSpec(g, false, tagsSpec, t.tags, "can have an unlisted effect: ",
                     hints=off, subtypeRelation)
     # after the check, use the formal spec:
-    effects.sons[tagEffects] = tagsSpec
+    effects[tagEffects] = tagsSpec
+  else:
+    effects[tagEffects] = t.tags
+
+  let forbidsSpec = effectSpec(p, wForbids)
+  if not isNil(forbidsSpec):
+    checkRaisesSpec(g, false, forbidsSpec, t.tags, "has an illegal effect: ",
+                    hints=off, subtypeRelation, isForbids=true)
+    # after the check, use the formal spec:
+    effects[forbiddenEffects] = forbidsSpec
+  else:
+    effects[forbiddenEffects] = t.forbids
+
+  let requiresSpec = propSpec(p, wRequires)
+  if not isNil(requiresSpec):
+    effects[requiresEffects] = requiresSpec
+  let ensuresSpec = propSpec(p, wEnsures)
+  if not isNil(ensuresSpec):
+    patchResult(t, ensuresSpec)
+    effects[ensuresEffects] = ensuresSpec
+
+  var mutationInfo = MutationInfo()
+  if views in c.features:
+    var partitions = computeGraphPartitions(s, body, g, {borrowChecking})
+    checkBorrowedLocations(partitions, body, g.config)
 
   if sfThread in s.flags and t.gcUnsafe:
     if optThreads in g.config.globalOptions and optThreadAnalysis in g.config.globalOptions:
@@ -970,29 +1716,39 @@ proc trackProc*(g: ModuleGraph; s: PSym, body: PNode) =
     when false:
       listGcUnsafety(s, onlyWarning=false, g.config)
     else:
-      localError(g.config, s.info, "'$1' can have side effects" % s.name.s)
+      if c.compilesContextId == 0: # don't render extended diagnostic messages in `system.compiles` context
+        var msg = ""
+        listSideEffects(msg, s, g.config, t.c)
+        message(g.config, s.info, errGenerated, msg)
+      else:
+        localError(g.config, s.info, "") # simple error for `system.compiles` context
   if not t.gcUnsafe:
     s.typ.flags.incl tfGcSafe
   if not t.hasSideEffect and sfSideEffect notin s.flags:
     s.typ.flags.incl tfNoSideEffect
-  if s.typ.lockLevel == UnspecifiedLockLevel:
-    s.typ.lockLevel = t.maxLockLevel
-  elif t.maxLockLevel > s.typ.lockLevel:
-    #localError(s.info,
-    message(g.config, s.info, warnLockLevel,
-      "declared lock level is $1, but real lock level is $2" %
-        [$s.typ.lockLevel, $t.maxLockLevel])
+  when defined(drnim):
+    if c.graph.strongSemCheck != nil: c.graph.strongSemCheck(c.graph, s, body)
   when defined(useDfa):
-    if s.kind == skFunc:
+    if s.name.s == "testp":
       dataflowAnalysis(s, body)
+
       when false: trackWrites(s, body)
+  if strictNotNil in c.features and s.kind in {skProc, skFunc, skMethod, skConverter}:
+    checkNil(s, body, g.config, c.idgen)
 
-proc trackTopLevelStmt*(g: ModuleGraph; module: PSym; n: PNode) =
-  if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
-                nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
-    return
-  var effects = newNode(nkEffectList, n.info)
-  var t: TEffects
-  initEffects(g, effects, module, t)
-  t.isToplevel = true
-  track(t, n)
+proc trackStmt*(c: PContext; module: PSym; n: PNode, isTopLevel: bool) =
+  case n.kind
+  of {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
+                nkConverterDef, nkMethodDef, nkIteratorDef}:
+    discard
+  of nkTypeSection:
+    if isTopLevel:
+      collectObjectTree(c.graph, n)
+  else:
+    let g = c.graph
+    var effects = newNodeI(nkEffectList, n.info)
+    var t: TEffects = initEffects(g, effects, module, c)
+    t.isTopLevel = isTopLevel
+    track(t, n)
+    when defined(drnim):
+      if c.graph.strongSemCheck != nil: c.graph.strongSemCheck(c.graph, module, n)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 292238dc9..f5f8fea0c 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -12,7 +12,7 @@
 
 const
   errNoSymbolToBorrowFromFound = "no symbol to borrow from found"
-  errDiscardValueX = "value of type '$1' has to be discarded"
+  errDiscardValueX = "value of type '$1' has to be used (or discarded)"
   errInvalidDiscard = "statement returns no value that can be discarded"
   errInvalidControlFlowX = "invalid control flow: $1"
   errSelectorMustBeOfCertainTypes = "selector must be of an ordinal type, float or string"
@@ -27,314 +27,441 @@ const
     "it is used as an operand to another routine and the types " &
     "of the generic paramers can be inferred from the expected signature."
   errCannotInferTypeOfTheLiteral = "cannot infer the type of the $1"
-  errCannotInferReturnType = "cannot infer the return type of the proc"
+  errCannotInferReturnType = "cannot infer the return type of '$1'"
   errCannotInferStaticParam = "cannot infer the value of the static param '$1'"
   errProcHasNoConcreteType = "'$1' doesn't have a concrete type, due to unspecified generic parameters."
   errLetNeedsInit = "'let' symbol requires an initialization"
   errThreadvarCannotInit = "a thread var cannot be initialized explicitly; this would only run for the main thread"
   errImplOfXexpected = "implementation of '$1' expected"
   errRecursiveDependencyX = "recursive dependency: '$1'"
+  errRecursiveDependencyIteratorX = "recursion is not supported in iterators: '$1'"
   errPragmaOnlyInHeaderOfProcX = "pragmas are only allowed in the header of a proc; redefinition of $1"
+  errCannotAssignToGlobal = "cannot assign local to global variable"
+
+proc implicitlyDiscardable(n: PNode): bool
+
+proc hasEmpty(typ: PType): bool =
+  if typ.kind in {tySequence, tyArray, tySet}:
+    result = typ.elementType.kind == tyEmpty
+  elif typ.kind == tyTuple:
+    result = false
+    for s in typ.kids:
+      result = result or hasEmpty(s)
+  else:
+    result = false
 
 proc semDiscard(c: PContext, n: PNode): PNode =
   result = n
   checkSonsLen(n, 1, c.config)
-  if n.sons[0].kind != nkEmpty:
-    n.sons[0] = semExprWithType(c, n.sons[0])
-    if isEmptyType(n.sons[0].typ) or n.sons[0].typ.kind == tyNone or n.sons[0].kind == nkTypeOfExpr:
+  if n[0].kind != nkEmpty:
+    n[0] = semExprWithType(c, n[0])
+    let sonType = n[0].typ
+    let sonKind = n[0].kind
+    if isEmptyType(sonType) or hasEmpty(sonType) or
+          sonType.kind in {tyNone, tyTypeDesc} or
+          sonKind == nkTypeOfExpr:
       localError(c.config, n.info, errInvalidDiscard)
+    if sonType.kind == tyProc and sonKind notin nkCallKinds:
+      # tyProc is disallowed to prevent ``discard foo`` to be valid, when ``discard foo()`` is meant.
+      localError(c.config, n.info, "illegal discard proc, did you mean: " & $n[0] & "()")
 
 proc semBreakOrContinue(c: PContext, n: PNode): PNode =
   result = n
   checkSonsLen(n, 1, c.config)
-  if n.sons[0].kind != nkEmpty:
+  if n[0].kind != nkEmpty:
     if n.kind != nkContinueStmt:
-      var s: PSym
-      case n.sons[0].kind
-      of nkIdent: s = lookUp(c, n.sons[0])
-      of nkSym: s = n.sons[0].sym
+      var s: PSym = nil
+      case n[0].kind
+      of nkIdent: s = lookUp(c, n[0])
+      of nkSym: s = n[0].sym
       else: illFormedAst(n, c.config)
       s = getGenSym(c, s)
       if s.kind == skLabel and s.owner.id == c.p.owner.id:
         var x = newSymNode(s)
         x.info = n.info
         incl(s.flags, sfUsed)
-        n.sons[0] = x
-        suggestSym(c.config, x.info, s, c.graph.usageSym)
-        styleCheckUse(x.info, s)
+        n[0] = x
+        suggestSym(c.graph, x.info, s, c.graph.usageSym)
+        onUse(x.info, s)
       else:
         localError(c.config, n.info, errInvalidControlFlowX % s.name.s)
     else:
       localError(c.config, n.info, errGenerated, "'continue' cannot have a label")
-  elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0):
+  elif c.p.nestedBlockCounter > 0 and n.kind == nkBreakStmt and not c.p.breakInLoop:
+    localError(c.config, n.info, warnUnnamedBreak)
+  elif (c.p.nestedLoopCounter <= 0) and ((c.p.nestedBlockCounter <= 0) or n.kind == nkContinueStmt):
     localError(c.config, n.info, errInvalidControlFlowX %
                renderTree(n, {renderNoComments}))
 
 proc semAsm(c: PContext, n: PNode): PNode =
   checkSonsLen(n, 2, c.config)
-  var marker = pragmaAsm(c, n.sons[0])
+  var marker = pragmaAsm(c, n[0])
   if marker == '\0': marker = '`' # default marker
   result = semAsmOrEmit(c, n, marker)
 
-proc semWhile(c: PContext, n: PNode): PNode =
+proc semWhile(c: PContext, n: PNode; flags: TExprFlags): PNode =
   result = n
   checkSonsLen(n, 2, c.config)
   openScope(c)
-  n.sons[0] = forceBool(c, semExprWithType(c, n.sons[0]))
+  n[0] = forceBool(c, semExprWithType(c, n[0], expectedType = getSysType(c.graph, n.info, tyBool)))
   inc(c.p.nestedLoopCounter)
-  n.sons[1] = semStmt(c, n.sons[1])
+  let oldBreakInLoop = c.p.breakInLoop
+  c.p.breakInLoop = true
+  n[1] = semStmt(c, n[1], flags)
+  c.p.breakInLoop = oldBreakInLoop
   dec(c.p.nestedLoopCounter)
   closeScope(c)
-  if n.sons[1].typ == c.enforceVoidContext:
+  if n[1].typ == c.enforceVoidContext:
     result.typ = c.enforceVoidContext
-
-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}))
+  elif efInTypeof in flags:
+    result.typ = n[1].typ
+  elif implicitlyDiscardable(n[1]):
+    result[1].typ = c.enforceVoidContext
 
 proc semProc(c: PContext, n: PNode): PNode
 
-proc semExprBranch(c: PContext, n: PNode): PNode =
-  result = semExpr(c, n)
+proc semExprBranch(c: PContext, n: PNode; flags: TExprFlags = {}; expectedType: PType = nil): PNode =
+  result = semExpr(c, n, flags, expectedType)
   if result.typ != nil:
     # XXX tyGenericInst here?
     if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
 
-proc semExprBranchScope(c: PContext, n: PNode): PNode =
+proc semExprBranchScope(c: PContext, n: PNode; expectedType: PType = nil): PNode =
   openScope(c)
-  result = semExprBranch(c, n)
+  result = semExprBranch(c, n, expectedType = expectedType)
   closeScope(c)
 
 const
-  skipForDiscardable = {nkIfStmt, nkIfExpr, nkCaseStmt, nkOfBranch,
-    nkElse, nkStmtListExpr, nkTryStmt, nkFinally, nkExceptBranch,
-    nkElifBranch, nkElifExpr, nkElseExpr, nkBlockStmt, nkBlockExpr}
+  skipForDiscardable = {nkStmtList, nkStmtListExpr,
+    nkOfBranch, nkElse, nkFinally, nkExceptBranch,
+    nkElifBranch, nkElifExpr, nkElseExpr, nkBlockStmt, nkBlockExpr,
+    nkHiddenStdConv, nkHiddenSubConv, nkHiddenDeref}
 
 proc implicitlyDiscardable(n: PNode): bool =
-  var n = n
-  while n.kind in skipForDiscardable: n = n.lastSon
-  result = isCallExpr(n) and n.sons[0].kind == nkSym and
-           sfDiscardable in n.sons[0].sym.flags
+  # same traversal as endsInNoReturn
+  template checkBranch(branch) =
+    if not implicitlyDiscardable(branch):
+      return false
+
+  var it = n
+  # skip these beforehand, no special handling needed
+  while it.kind in skipForDiscardable and it.len > 0:
+    it = it.lastSon
+
+  case it.kind
+  of nkIfExpr, nkIfStmt:
+    for branch in it:
+      checkBranch:
+        if branch.len == 2:
+          branch[1]
+        elif branch.len == 1:
+          branch[0]
+        else:
+          raiseAssert "Malformed `if` statement during implicitlyDiscardable"
+    # all branches are discardable
+    result = true
+  of nkCaseStmt:
+    for i in 1 ..< it.len:
+      let branch = it[i]
+      checkBranch:
+        case branch.kind
+        of nkOfBranch:
+          branch[^1]
+        of nkElifBranch:
+          branch[1]
+        of nkElse:
+          branch[0]
+        else:
+          raiseAssert "Malformed `case` statement in implicitlyDiscardable"
+    # all branches are discardable
+    result = true
+  of nkTryStmt:
+    checkBranch(it[0])
+    for i in 1 ..< it.len:
+      let branch = it[i]
+      if branch.kind != nkFinally:
+        checkBranch(branch[^1])
+    # all branches are discardable
+    result = true
+  of nkCallKinds:
+    result = it[0].kind == nkSym and {sfDiscardable, sfNoReturn} * it[0].sym.flags != {}
+  of nkLastBlockStmts:
+    result = true
+  else:
+    result = false
+
+proc endsInNoReturn(n: PNode, returningNode: var PNode; discardableCheck = false): bool =
+  ## check if expr ends the block like raising or call of noreturn procs do
+  result = false # assume it does return
+
+  template checkBranch(branch) =
+    if not endsInNoReturn(branch, returningNode, discardableCheck):
+      # proved a branch returns
+      return false
+
+  var it = n
+  # skip these beforehand, no special handling needed
+  let skips = if discardableCheck: skipForDiscardable else: skipForDiscardable-{nkBlockExpr, nkBlockStmt}
+  while it.kind in skips and it.len > 0:
+    it = it.lastSon
+
+  case it.kind
+  of nkIfExpr, nkIfStmt:
+    var hasElse = false
+    for branch in it:
+      checkBranch:
+        if branch.len == 2:
+          branch[1]
+        elif branch.len == 1:
+          hasElse = true
+          branch[0]
+        else:
+          raiseAssert "Malformed `if` statement during endsInNoReturn"
+    # none of the branches returned
+    result = hasElse # Only truly a no-return when it's exhaustive
+  of nkCaseStmt:
+    let caseTyp = skipTypes(it[0].typ, abstractVar-{tyTypeDesc})
+    # semCase should already have checked for exhaustiveness in this case
+    # effectively the same as having an else
+    var hasElse = caseTyp.shouldCheckCaseCovered()
+
+    # actual noreturn checks
+    for i in 1 ..< it.len:
+      let branch = it[i]
+      checkBranch:
+        case branch.kind
+        of nkOfBranch:
+          branch[^1]
+        of nkElifBranch:
+          branch[1]
+        of nkElse:
+          hasElse = true
+          branch[0]
+        else:
+          raiseAssert "Malformed `case` statement in endsInNoReturn"
+    # Can only guarantee a noreturn if there is an else or it's exhaustive
+    result = hasElse
+  of nkTryStmt:
+    checkBranch(it[0])
+    var lastIndex = it.len - 1
+    if it[lastIndex].kind == nkFinally:
+      # if finally is noreturn, then the entire statement is noreturn
+      if endsInNoReturn(it[lastIndex][^1], returningNode, discardableCheck):
+        return true
+      dec lastIndex
+    for i in 1 .. lastIndex:
+      let branch = it[i]
+      checkBranch(branch[^1])
+    # none of the branches returned
+    result = true
+  of nkLastBlockStmts:
+    result = true
+  of nkCallKinds:
+    result = it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
+    if not result:
+      returningNode = it
+  else:
+    result = false
+    returningNode = it
+
+proc endsInNoReturn(n: PNode): bool =
+  var dummy: PNode = nil
+  result = endsInNoReturn(n, dummy)
 
 proc fixNilType(c: PContext; n: PNode) =
   if isAtom(n):
     if n.kind != nkNilLit and n.typ != nil:
       localError(c.config, n.info, errDiscardValueX % n.typ.typeToString)
   elif n.kind in {nkStmtList, nkStmtListExpr}:
-    n.kind = nkStmtList
+    n.transitionSonsKind(nkStmtList)
     for it in n: fixNilType(c, it)
   n.typ = nil
 
-proc discardCheck(c: PContext, result: PNode) =
-  if c.matchedConcept != nil: return
-  if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
+proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) =
+  if c.matchedConcept != nil or efInTypeof in flags: return
+
+  if result.typ != nil and result.typ.kind notin {tyTyped, tyVoid}:
     if implicitlyDiscardable(result):
-      var n = result
-      result.typ = nil
-      while n.kind in skipForDiscardable:
-        n = n.lastSon
-        n.typ = nil
+      var n = newNodeI(nkDiscardStmt, result.info, 1)
+      n[0] = result
+      # notes that it doesn't transform nodes into discard statements
     elif result.typ.kind != tyError and c.config.cmd != cmdInteractive:
-      var n = result
-      while n.kind in skipForDiscardable: n = n.lastSon
-      var s = "expression '" & $n & "' is of type '" &
-          result.typ.typeToString & "' and has to be discarded"
-      if result.info.line != n.info.line or
-          result.info.fileIndex != n.info.fileIndex:
-        s.add "; start of expression here: " & c.config$result.info
-      if result.typ.kind == tyProc:
-        s.add "; for a function call use ()"
-      localError(c.config, n.info, s)
-
-proc semIf(c: PContext, n: PNode): PNode =
+      if result.typ.kind == tyNone:
+        localError(c.config, result.info, "expression has no type: " &
+               renderTree(result, {renderNoComments}))
+      else:
+        # Ignore noreturn procs since they don't have a type
+        var n = result
+        if result.endsInNoReturn(n, discardableCheck = true):
+          return
+
+        var s = "expression '" & $n & "' is of type '" &
+            result.typ.typeToString & "' and has to be used (or discarded)"
+        if result.info.line != n.info.line or
+            result.info.fileIndex != n.info.fileIndex:
+          s.add "; start of expression here: " & c.config$result.info
+        if result.typ.kind == tyProc:
+          s.add "; for a function call use ()"
+        localError(c.config, n.info, s)
+
+proc semIf(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
   result = n
   var typ = commonTypeBegin
+  var expectedType = expectedType
   var hasElse = false
-  for i in countup(0, sonsLen(n) - 1):
-    var it = n.sons[i]
+  for i in 0..<n.len:
+    var it = n[i]
     if it.len == 2:
-      when newScopeForIf: openScope(c)
-      it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
-      when not newScopeForIf: openScope(c)
-      it.sons[1] = semExprBranch(c, it.sons[1])
-      typ = commonType(typ, it.sons[1])
+      openScope(c)
+      it[0] = forceBool(c, semExprWithType(c, it[0], expectedType = getSysType(c.graph, n.info, tyBool)))
+      it[1] = semExprBranch(c, it[1], flags, expectedType)
+      typ = commonType(c, typ, it[1])
+      if not endsInNoReturn(it[1]):
+        expectedType = typ
       closeScope(c)
     elif it.len == 1:
       hasElse = true
-      it.sons[0] = semExprBranchScope(c, it.sons[0])
-      typ = commonType(typ, it.sons[0])
+      it[0] = semExprBranchScope(c, it[0], expectedType)
+      typ = commonType(c, typ, it[0])
+      if not endsInNoReturn(it[0]):
+        expectedType = typ
     else: illFormedAst(it, c.config)
-  if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
-    for it in n: discardCheck(c, it.lastSon)
-    result.kind = nkIfStmt
+  if isEmptyType(typ) or typ.kind in {tyNil, tyUntyped} or
+      (not hasElse and efInTypeof notin flags):
+    for it in n: discardCheck(c, it.lastSon, flags)
+    result.transitionSonsKind(nkIfStmt)
     # propagate any enforced VoidContext:
     if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext
   else:
     for it in n:
       let j = it.len-1
-      if not endsInNoReturn(it.sons[j]):
-        it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
-    result.kind = nkIfExpr
-    result.typ = typ
-
-proc semCase(c: PContext, n: PNode): PNode =
-  result = n
-  checkMinSonsLen(n, 2, c.config)
-  openScope(c)
-  n.sons[0] = semExprWithType(c, n.sons[0])
-  var chckCovered = false
-  var covered: BiggestInt = 0
-  var typ = commonTypeBegin
-  var hasElse = false
-  let caseTyp = skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc})
-  case caseTyp.kind
-  of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool:
-    chckCovered = true
-  of tyFloat..tyFloat128, tyString, tyError:
-    discard
-  else:
-    localError(c.config, n.info, errSelectorMustBeOfCertainTypes)
-    return
-  for i in countup(1, sonsLen(n) - 1):
-    var x = n.sons[i]
-    when defined(nimsuggest):
-      if c.config.ideCmd == ideSug and exactEquals(c.config.m.trackPos, x.info) and caseTyp.kind == tyEnum:
-        suggestEnum(c, x, caseTyp)
-    case x.kind
-    of nkOfBranch:
-      checkMinSonsLen(x, 2, c.config)
-      semCaseBranch(c, n, x, i, covered)
-      var last = sonsLen(x)-1
-      x.sons[last] = semExprBranchScope(c, x.sons[last])
-      typ = commonType(typ, x.sons[last])
-    of nkElifBranch:
-      chckCovered = false
-      checkSonsLen(x, 2, c.config)
-      when newScopeForIf: openScope(c)
-      x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
-      when not newScopeForIf: openScope(c)
-      x.sons[1] = semExprBranch(c, x.sons[1])
-      typ = commonType(typ, x.sons[1])
-      closeScope(c)
-    of nkElse:
-      chckCovered = false
-      checkSonsLen(x, 1, c.config)
-      x.sons[0] = semExprBranchScope(c, x.sons[0])
-      typ = commonType(typ, x.sons[0])
-      hasElse = true
-    else:
-      illFormedAst(x, c.config)
-  if chckCovered:
-    if covered == toCover(c, n.sons[0].typ):
-      hasElse = true
-    else:
-      localError(c.config, n.info, "not all cases are covered")
-  closeScope(c)
-  if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
-    for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
-    # propagate any enforced VoidContext:
-    if typ == c.enforceVoidContext:
-      result.typ = c.enforceVoidContext
-  else:
-    for i in 1..n.len-1:
-      var it = n.sons[i]
-      let j = it.len-1
-      if not endsInNoReturn(it.sons[j]):
-        it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
+      if not endsInNoReturn(it[j]):
+        it[j] = fitNode(c, typ, it[j], it[j].info)
+    result.transitionSonsKind(nkIfExpr)
     result.typ = typ
 
-proc semTry(c: PContext, n: PNode): PNode =
-
+proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
   var check = initIntSet()
   template semExceptBranchType(typeNode: PNode): bool =
     # returns true if exception type is imported type
     let typ = semTypeNode(c, typeNode, nil).toObject()
-    var is_imported = false
+    var isImported = false
     if isImportedException(typ, c.config):
-      is_imported = true
+      isImported = true
     elif not isException(typ):
       localError(c.config, typeNode.info, errExprCannotBeRaised)
+    elif not isDefectOrCatchableError(typ):
+      message(c.config, a.info, warnBareExcept, "catch a more precise Exception deriving from CatchableError or Defect.")
 
     if containsOrIncl(check, typ.id):
       localError(c.config, typeNode.info, errExceptionAlreadyHandled)
     typeNode = newNodeIT(nkType, typeNode.info, typ)
-    is_imported
+    isImported
 
   result = n
-  inc c.p.inTryStmt
   checkMinSonsLen(n, 2, c.config)
 
   var typ = commonTypeBegin
-  n[0] = semExprBranchScope(c, n[0])
-  typ = commonType(typ, n[0].typ)
+  var expectedType = expectedType
+  n[0] = semExprBranchScope(c, n[0], expectedType)
+  if not endsInNoReturn(n[0]):
+    typ = commonType(c, typ, n[0].typ)
+    expectedType = typ
 
-  var last = sonsLen(n) - 1
-  for i in countup(1, last):
-    let a = n.sons[i]
+  var last = n.len - 1
+  var catchAllExcepts = 0
+
+  for i in 1..last:
+    let a = n[i]
     checkMinSonsLen(a, 1, c.config)
     openScope(c)
     if a.kind == nkExceptBranch:
 
       if a.len == 2 and a[0].kind == nkBracket:
         # rewrite ``except [a, b, c]: body`` -> ```except a, b, c: body```
-        a.sons[0..0] = a[0].sons
+        a.sons[0..0] = move a[0].sons
 
       if a.len == 2 and a[0].isInfixAs():
         # support ``except Exception as ex: body``
-        let is_imported = semExceptBranchType(a[0][1])
+        let isImported = semExceptBranchType(a[0][1])
         let symbol = newSymG(skLet, a[0][2], c)
-        symbol.typ = if is_imported: a[0][1].typ
-                     else: a[0][1].typ.toRef()
+        symbol.typ = if isImported: a[0][1].typ
+                     else: a[0][1].typ.toRef(c.idgen)
         addDecl(c, symbol)
         # Overwrite symbol in AST with the symbol in the symbol table.
         a[0][2] = newSymNode(symbol, a[0][2].info)
 
+      elif a.len == 1:
+        # count number of ``except: body`` blocks
+        inc catchAllExcepts
+        message(c.config, a.info, warnBareExcept,
+                  "The bare except clause is deprecated; use `except CatchableError:` instead")
       else:
         # support ``except KeyError, ValueError, ... : body``
-        var is_native, is_imported: bool
-        for j in 0..a.len-2:
+        if catchAllExcepts > 0:
+          # if ``except: body`` already encountered,
+          # cannot be followed by a ``except KeyError, ... : body`` block
+          inc catchAllExcepts
+        var isNative, isImported: bool = false
+        for j in 0..<a.len-1:
           let tmp = semExceptBranchType(a[j])
-          if tmp: is_imported = true
-          else: is_native = true
+          if tmp: isImported = true
+          else: isNative = true
 
-        if is_native and is_imported:
+        if isNative and isImported:
           localError(c.config, a[0].info, "Mix of imported and native exception types is not allowed in one except branch")
 
-    elif a.kind != nkFinally:
+    elif a.kind == nkFinally:
+      if i != n.len-1:
+        localError(c.config, a.info, "Only one finally is allowed after all other branches")
+
+    else:
       illFormedAst(n, c.config)
 
+    if catchAllExcepts > 1:
+      # if number of ``except: body`` blocks is greater than 1
+      # or more specific exception follows a general except block, it is invalid
+      localError(c.config, a.info, "Only one general except clause is allowed after more specific exceptions")
+
     # last child of an nkExcept/nkFinally branch is a statement:
-    a[^1] = semExprBranchScope(c, a[^1])
-    if a.kind != nkFinally: typ = commonType(typ, a[^1])
-    else: dec last
+    if a.kind != nkFinally:
+      a[^1] = semExprBranchScope(c, a[^1], expectedType)
+      typ = commonType(c, typ, a[^1])
+      if not endsInNoReturn(a[^1]):
+        expectedType = typ
+    else:
+      a[^1] = semExprBranchScope(c, a[^1])
+      dec last
     closeScope(c)
 
-  dec c.p.inTryStmt
-  if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}:
-    discardCheck(c, n.sons[0])
-    for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
+  if isEmptyType(typ) or typ.kind in {tyNil, tyUntyped}:
+    discardCheck(c, n[0], flags)
+    for i in 1..<n.len: discardCheck(c, n[i].lastSon, flags)
     if typ == c.enforceVoidContext:
       result.typ = c.enforceVoidContext
   else:
-    if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon)
-    n.sons[0] = fitNode(c, typ, n.sons[0], n.sons[0].info)
+    if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon, flags)
+    if not endsInNoReturn(n[0]):
+      n[0] = fitNode(c, typ, n[0], n[0].info)
     for i in 1..last:
-      var it = n.sons[i]
+      var it = n[i]
       let j = it.len-1
-      it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
+      if not endsInNoReturn(it[j]):
+        it[j] = fitNode(c, typ, it[j], it[j].info)
     result.typ = typ
 
 proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode =
   result = fitNode(c, typ, n, n.info)
   if result.kind in {nkHiddenStdConv, nkHiddenSubConv}:
-    let r1 = result.sons[1]
+    let r1 = result[1]
     if r1.kind in {nkCharLit..nkUInt64Lit} and typ.skipTypes(abstractRange).kind in {tyFloat..tyFloat128}:
       result = newFloatNode(nkFloatLit, BiggestFloat r1.intVal)
       result.info = n.info
       result.typ = typ
+      if not floatRangeCheck(result.floatVal, typ):
+        localError(c.config, n.info, errFloatToString % [$result.floatVal, typeToString(typ)])
+    elif r1.kind == nkSym and typ.skipTypes(abstractRange).kind == tyCstring:
+      discard "keep nkHiddenStdConv for cstring conversions"
     else:
       changeType(c, r1, typ, check=true)
       result = r1
@@ -342,20 +469,20 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode =
     changeType(c, result, typ, check=false)
 
 proc findShadowedVar(c: PContext, v: PSym): PSym =
-  for scope in walkScopes(c.currentScope.parent):
-    if scope == c.topLevelScope: break
+  result = nil
+  for scope in localScopesFrom(c, c.currentScope.parent):
     let shadowed = strTableGet(scope.symbols, v.name)
     if shadowed != nil and shadowed.kind in skLocalVars:
       return shadowed
 
 proc identWithin(n: PNode, s: PIdent): bool =
-  for i in 0 .. n.safeLen-1:
-    if identWithin(n.sons[i], s): return true
+  for i in 0..n.safeLen-1:
+    if identWithin(n[i], s): return true
   result = n.kind == nkSym and n.sym.name.id == s.id
 
-proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
+proc semIdentDef(c: PContext, n: PNode, kind: TSymKind, reportToNimsuggest = true): PSym =
   if isTopLevel(c):
-    result = semIdentWithPragma(c, kind, n, {sfExported})
+    result = semIdentWithPragma(c, kind, n, {sfExported}, fromTopLevel = true)
     incl(result.flags, sfGlobal)
     #if kind in {skVar, skLet}:
     #  echo "global variable here ", n.info, " ", result.name.s
@@ -363,22 +490,38 @@ 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)
-  styleCheckDef(c.config, result)
+  result.options = c.config.options
+
+  proc getLineInfo(n: PNode): TLineInfo =
+    case n.kind
+    of nkPostfix:
+      if len(n) > 1:
+        return getLineInfo(n[1])
+    of nkAccQuoted, nkPragmaExpr:
+      if len(n) > 0:
+        return getLineInfo(n[0])
+    else:
+      discard
+    result = n.info
+  let info = getLineInfo(n)
+  if reportToNimsuggest:
+    suggestSym(c.graph, 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 {sfGlobal, sfImportc} * v.flags == {sfGlobal} and v.typ.requiresInit:
+    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 not v.astdef.typ.isNil and tfNotNil notin v.astdef.typ.flags:
       message(c.config, v.info, warnProveInit, v.name.s)
 
-include semasgn
+#include liftdestructors
+
+proc addToVarSection(c: PContext; result: var PNode; n: PNode) =
+  if result.kind != nkStmtList:
+    result = makeStmtList(result)
+  result.add n
 
-proc addToVarSection(c: PContext; result: PNode; orig, identDefs: PNode) =
-  let L = identDefs.len
-  let value = identDefs[L-1]
+proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
   if result.kind == nkStmtList:
     let o = copyNode(orig)
     o.add identDefs
@@ -387,55 +530,68 @@ proc addToVarSection(c: PContext; result: PNode; orig, identDefs: PNode) =
     result.add identDefs
 
 proc isDiscardUnderscore(v: PSym): bool =
-  if v.name.s == "_":
+  if v.name.id == ord(wUnderscore):
     v.flags.incl(sfGenSym)
     result = true
+  else:
+    result = false
 
 proc semUsing(c: PContext; n: PNode): PNode =
   result = c.graph.emptyNode
   if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "using")
-  for i in countup(0, sonsLen(n)-1):
-    var a = n.sons[i]
+  for i in 0..<n.len:
+    var a = n[i]
     if c.config.cmd == cmdIdeTools: suggestStmt(c, a)
     if a.kind == nkCommentStmt: continue
     if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: illFormedAst(a, c.config)
     checkMinSonsLen(a, 3, c.config)
-    var length = sonsLen(a)
-    if a.sons[length-2].kind != nkEmpty:
-      let typ = semTypeNode(c, a.sons[length-2], nil)
-      for j in countup(0, length-3):
-        let v = semIdentDef(c, a.sons[j], skParam)
+    if a[^2].kind != nkEmpty:
+      let typ = semTypeNode(c, a[^2], nil)
+      for j in 0..<a.len-2:
+        let v = semIdentDef(c, a[j], skParam)
+        styleCheckDef(c, v)
+        onDef(a[j].info, v)
         v.typ = typ
         strTableIncl(c.signatures, v)
     else:
       localError(c.config, a.info, "'using' section must have a type")
     var def: PNode
-    if a.sons[length-1].kind != nkEmpty:
+    if a[^1].kind != nkEmpty:
       localError(c.config, a.info, "'using' sections cannot contain assignments")
 
-proc hasEmpty(typ: PType): bool =
-  if typ.kind in {tySequence, tyArray, tySet}:
-    result = typ.lastSon.kind == tyEmpty
-  elif typ.kind == tyTuple:
-    for s in typ.sons:
-      result = result or hasEmpty(s)
+proc hasUnresolvedParams(n: PNode; flags: TExprFlags): bool =
+  result = tfUnresolved in n.typ.flags
+  when false:
+    case n.kind
+    of nkSym:
+      result = isGenericRoutineStrict(n.sym)
+    of nkSymChoices:
+      for ch in n:
+        if hasUnresolvedParams(ch, flags):
+          return true
+      result = false
+    else:
+      result = false
+    if efOperand in flags:
+      if tfUnresolved notin n.typ.flags:
+        result = false
 
 proc makeDeref(n: PNode): PNode =
   var t = n.typ
   if t.kind in tyUserTypeClasses and t.isResolvedUserTypeClass:
-    t = t.lastSon
-  t = skipTypes(t, {tyGenericInst, tyAlias, tySink})
+    t = t.last
+  t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned})
   result = n
   if t.kind in {tyVar, tyLent}:
-    result = newNodeIT(nkHiddenDeref, n.info, t.sons[0])
-    addSon(result, n)
-    t = skipTypes(t.sons[0], {tyGenericInst, tyAlias, tySink})
+    result = newNodeIT(nkHiddenDeref, n.info, t.elementType)
+    result.add n
+    t = skipTypes(t.elementType, {tyGenericInst, tyAlias, tySink, tyOwned})
   while t.kind in {tyPtr, tyRef}:
     var a = result
-    let baseTyp = t.lastSon
+    let baseTyp = t.elementType
     result = newNodeIT(nkHiddenDeref, n.info, baseTyp)
-    addSon(result, a)
-    t = skipTypes(baseTyp, {tyGenericInst, tyAlias, tySink})
+    result.add a
+    t = skipTypes(baseTyp, {tyGenericInst, tyAlias, tySink, tyOwned})
 
 proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
   if n.len == 2:
@@ -443,12 +599,12 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
     let y = considerQuotedIdent(c, n[1])
     let obj = x.typ.skipTypes(abstractPtrs)
     if obj.kind == tyObject and tfPartial in obj.flags:
-      let field = newSym(skField, getIdent(c.cache, y.s), obj.sym, n[1].info)
-      field.typ = skipIntLit(typ)
-      field.position = sonsLen(obj.n)
-      addSon(obj.n, newSymNode(field))
-      n.sons[0] = makeDeref x
-      n.sons[1] = newSymNode(field)
+      let field = newSym(skField, getIdent(c.cache, y.s), c.idgen, obj.sym, n[1].info)
+      field.typ = skipIntLit(typ, c.idgen)
+      field.position = obj.n.len
+      obj.n.add newSymNode(field)
+      n[0] = makeDeref x
+      n[1] = newSymNode(field)
       n.typ = field.typ
     else:
       localError(c.config, n.info, "implicit object field construction " &
@@ -459,33 +615,233 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
 proc setVarType(c: PContext; v: PSym, typ: PType) =
   if v.typ != nil and not sameTypeOrNil(v.typ, typ):
     localError(c.config, v.info, "inconsistent typing for reintroduced symbol '" &
-        v.name.s & "': previous type was: " & typeToString(v.typ) &
-        "; new type is: " & typeToString(typ))
+        v.name.s & "': previous type was: " & typeToString(v.typ, preferDesc) &
+        "; new type is: " & typeToString(typ, preferDesc))
   v.typ = typ
 
+proc isPossibleMacroPragma(c: PContext, it: PNode, key: PNode): bool =
+  # make sure it's not a normal pragma, and calls an identifier
+  # considerQuotedIdent below will fail on non-identifiers
+  result = whichPragma(it) == wInvalid and key.kind in nkIdentKinds+{nkDotExpr}
+  if result:
+    # make sure it's not a user pragma
+    if key.kind != nkDotExpr:
+      let ident = considerQuotedIdent(c, key)
+      result = strTableGet(c.userPragmas, ident) == nil
+    if result:
+      # make sure it's not a custom pragma
+      let sym = qualifiedLookUp(c, key, {})
+      result = sym == nil or sfCustomPragma notin sym.flags
+
+proc copyExcept(n: PNode, i: int): PNode =
+  result = copyNode(n)
+  for j in 0..<n.len:
+    if j != i: result.add(n[j])
+
+proc semVarMacroPragma(c: PContext, a: PNode, n: PNode): PNode =
+  # Mirrored with semProcAnnotation
+  result = nil
+  # a, b {.prag.}: int = 3 not allowed
+  const lhsPos = 0
+  if a.len == 3 and a[lhsPos].kind == nkPragmaExpr:
+    var b = a[lhsPos]
+    const
+      namePos = 0
+      pragmaPos = 1
+    let pragmas = b[pragmaPos]
+    for i in 0 ..< pragmas.len:
+      let it = pragmas[i]
+      let key = if it.kind in nkPragmaCallKinds and it.len >= 1: it[0] else: it
+
+      trySuggestPragmas(c, key)
+
+      if isPossibleMacroPragma(c, it, key):
+        # we transform ``var p {.m, rest.}`` into ``m(do: var p {.rest.})`` and
+        # let the semantic checker deal with it:
+        var x = newNodeI(nkCall, key.info)
+        x.add(key)
+
+        if it.kind in nkPragmaCallKinds and it.len > 1:
+          # pass pragma arguments to the macro too:
+          for i in 1..<it.len:
+            x.add(it[i])
+
+        # Drop the pragma from the list, this prevents getting caught in endless
+        # recursion when the nkCall is semanticized
+        let oldExpr = a[lhsPos]
+        let newPragmas = copyExcept(pragmas, i)
+        if newPragmas.kind != nkEmpty and newPragmas.len == 0:
+          a[lhsPos] = oldExpr[namePos]
+        else:
+          a[lhsPos] = copyNode(oldExpr)
+          a[lhsPos].add(oldExpr[namePos])
+          a[lhsPos].add(newPragmas)
+
+        var unarySection = newNodeI(n.kind, a.info)
+        unarySection.add(a)
+        x.add(unarySection)
+
+        # recursion assures that this works for multiple macro annotations too:
+        var r = semOverloadedCall(c, x, x, {skMacro, skTemplate}, {efNoUndeclared})
+        if r == nil:
+          # Restore the old list of pragmas since we couldn't process this
+          a[lhsPos] = oldExpr
+          # No matching macro was found but there's always the possibility this may
+          # be a .pragma. template instead
+          continue
+
+        doAssert r[0].kind == nkSym
+        let m = r[0].sym
+        case m.kind
+        of skMacro: result = semMacroExpr(c, r, r, m, {})
+        of skTemplate: result = semTemplateExpr(c, r, m, {})
+        else:
+          a[lhsPos] = oldExpr
+          continue
+
+        doAssert result != nil
+
+        return result
+
+template isLocalSym(sym: PSym): bool =
+  sym.kind in {skVar, skLet, skParam} and not
+    ({sfGlobal, sfPure} * sym.flags != {} or
+      sym.typ.kind == tyTypeDesc or
+      sfCompileTime in sym.flags) or
+      sym.kind in {skProc, skFunc, skIterator} and
+      sfGlobal notin sym.flags
+
+template isLocalVarSym(n: PNode): bool =
+  n.kind == nkSym and isLocalSym(n.sym)
+
+proc usesLocalVar(n: PNode): bool =
+  result = false
+  for z in 1 ..< n.len:
+    if n[z].isLocalVarSym:
+      return true
+    elif n[z].kind in nkCallKinds:
+      if usesLocalVar(n[z]):
+        return true
+
+proc globalVarInitCheck(c: PContext, n: PNode) =
+  if n.isLocalVarSym or n.kind in nkCallKinds and usesLocalVar(n):
+    localError(c.config, n.info, errCannotAssignToGlobal)
+
+const
+  errTupleUnpackingTupleExpected = "tuple expected for tuple unpacking, but got '$1'"
+  errTupleUnpackingDifferentLengths = "tuple with $1 elements expected, but got '$2' with $3 elements"
+
+proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSymKind, origResult: var PNode): PNode =
+  ## expand tuple unpacking assignments into new var/let/const section
+  ##
+  ## mirrored with semexprs.makeTupleAssignments
+  if typ.kind != tyTuple:
+    localError(c.config, a.info, errTupleUnpackingTupleExpected %
+      [typeToString(typ, preferDesc)])
+  elif a.len-2 != typ.len:
+    localError(c.config, a.info, errTupleUnpackingDifferentLengths %
+      [$(a.len-2), typeToString(typ, preferDesc), $typ.len])
+  var
+    tempNode: PNode = nil
+    lastDef: PNode
+  let defkind = if symkind == skConst: nkConstDef else: nkIdentDefs
+  # temporary not needed if not const and RHS is tuple literal
+  # const breaks with seqs without temporary
+  let useTemp = def.kind notin {nkPar, nkTupleConstr} or symkind == skConst
+  if useTemp:
+    # use same symkind for compatibility with original section
+    let temp = newSym(symkind, getIdent(c.cache, "tmpTuple"), c.idgen, getCurrOwner(c), n.info)
+    temp.typ = typ
+    temp.flags.incl(sfGenSym)
+    lastDef = newNodeI(defkind, a.info)
+    newSons(lastDef, 3)
+    lastDef[0] = newSymNode(temp)
+    # NOTE: at the moment this is always ast.emptyNode, see parser.nim
+    lastDef[1] = a[^2]
+    lastDef[2] = def
+    temp.ast = lastDef
+    addToVarSection(c, origResult, n, lastDef)
+    tempNode = newSymNode(temp)
+  result = newNodeI(n.kind, a.info)
+  for j in 0..<a.len-2:
+    let name = a[j]
+    if useTemp and name.kind == nkIdent and name.ident.id == ord(wUnderscore):
+      # skip _ assignments if we are using a temp as they are already evaluated
+      continue
+    if name.kind == nkVarTuple:
+      # nested tuple
+      lastDef = newNodeI(nkVarTuple, name.info)
+      newSons(lastDef, name.len)
+      for k in 0..<name.len-2:
+        lastDef[k] = name[k]
+    else:
+      lastDef = newNodeI(defkind, name.info)
+      newSons(lastDef, 3)
+      lastDef[0] = name
+    lastDef[^2] = c.graph.emptyNode
+    if useTemp:
+      lastDef[^1] = newTupleAccessRaw(tempNode, j)
+    else:
+      var val = def[j]
+      if val.kind == nkExprColonExpr: val = val[1]
+      lastDef[^1] = val
+    result.add(lastDef)
+
 proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
   var b: PNode
   result = copyNode(n)
-  var hasCompileTime = false
-  for i in countup(0, sonsLen(n)-1):
-    var a = n.sons[i]
+
+  # transform var x, y = 12 into var x = 12; var y = 12
+  # bug #18104; transformation should be finished before templates expansion
+  # TODO: move warnings for tuple here
+  var transformed = copyNode(n)
+  for i in 0..<n.len:
+    var a = n[i]
+    if a.kind == nkIdentDefs and a.len > 3 and a[^1].kind != nkEmpty:
+      for j in 0..<a.len-2:
+        var b = newNodeI(nkIdentDefs, a.info)
+        b.add a[j]
+        b.add a[^2]
+        b.add copyTree(a[^1])
+        transformed.add b
+    else:
+      transformed.add a
+  let n = transformed
+
+  for i in 0..<n.len:
+    var a = n[i]
     if c.config.cmd == cmdIdeTools: suggestStmt(c, a)
     if a.kind == nkCommentStmt: continue
-    if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: illFormedAst(a, c.config)
+    if a.kind notin {nkIdentDefs, nkVarTuple}: illFormedAst(a, c.config)
     checkMinSonsLen(a, 3, c.config)
-    var length = sonsLen(a)
-    var typ: PType
-    if a.sons[length-2].kind != nkEmpty:
-      typ = semTypeNode(c, a.sons[length-2], nil)
-    else:
-      typ = nil
+
+    b = semVarMacroPragma(c, a, n)
+    if b != nil:
+      addToVarSection(c, result, b)
+      continue
+
+    var hasUserSpecifiedType = false
+    var typ: PType = nil
+    if a[^2].kind != nkEmpty:
+      typ = semTypeNode(c, a[^2], nil)
+      hasUserSpecifiedType = true
+
+    var typFlags: TTypeAllowedFlags = {}
+
     var def: PNode = c.graph.emptyNode
-    if a.sons[length-1].kind != nkEmpty:
-      def = semExprWithType(c, a.sons[length-1], {efAllowDestructor})
-      if def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
-        # prevent the all too common 'var x = int' bug:
-        localError(c.config, def.info, "'typedesc' metatype is not valid here; typed '=' instead of ':'?")
-        def.typ = errorType(c)
+    if typ != nil and typ.kind == tyRange and
+        c.graph.config.isDefined("nimPreviewRangeDefault") and
+        a[^1].kind == nkEmpty:
+      a[^1] = firstRange(c.config, typ)
+
+    if a[^1].kind != nkEmpty:
+      def = semExprWithType(c, a[^1], {efTypeAllowed}, typ)
+
+      if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}:
+        typFlags.incl taIsTemplateOrMacro
+      elif def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
+        typFlags.incl taProcContextIsNotMacro
+
       if typ != nil:
         if typ.isMetaType:
           def = inferWithMetatype(c, typ, def)
@@ -493,175 +849,323 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
         else:
           # BUGFIX: ``fitNode`` is needed here!
           # check type compatibility between def.typ and typ
-          def = fitNode(c, typ, def, def.info)
+          def = fitNodeConsiderViewType(c, typ, def, def.info)
           #changeType(def.skipConv, typ, check=true)
       else:
-        typ = def.typ.skipTypes({tyStatic}).skipIntLit
+        typ = def.typ.skipTypes({tyStatic, tySink}).skipIntLit(c.idgen)
         if typ.kind in tyUserTypeClasses and typ.isResolvedUserTypeClass:
-          typ = typ.lastSon
+          typ = typ.last
         if hasEmpty(typ):
-          localError(c.config, def.info, errCannotInferTypeOfTheLiteral %
-                     ($typ.kind).substr(2).toLowerAscii)
-        elif typ.kind == tyProc and tfUnresolved in typ.flags:
-          localError(c.config, def.info, errProcHasNoConcreteType % def.renderTree)
-    else:
-      if symkind == skLet: localError(c.config, a.info, errLetNeedsInit)
+          localError(c.config, def.info, errCannotInferTypeOfTheLiteral % typ.kind.toHumanStr)
+        elif typ.kind == tyProc and def.kind == nkSym and isGenericRoutine(def.sym.ast):
+          let owner = typ.owner
+          let err =
+            # consistent error message with evaltempl/semMacroExpr
+            if owner != nil and owner.kind in {skTemplate, skMacro}:
+              errMissingGenericParamsForTemplate % def.renderTree
+            else:
+              errProcHasNoConcreteType % def.renderTree
+          localError(c.config, def.info, err)
+        when false:
+          # XXX This typing rule is neither documented nor complete enough to
+          # justify it. Instead use the newer 'unowned x' until we figured out
+          # a more general solution.
+          if symkind == skVar and typ.kind == tyOwned and def.kind notin nkCallKinds:
+            # special type inference rule: 'var it = ownedPointer' is turned
+            # into an unowned pointer.
+            typ = typ.lastSon
 
     # 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)
+
+    if c.matchedConcept != nil:
+      typFlags.incl taConcept
+    typeAllowedCheck(c, a.info, typ, symkind, typFlags)
+
     var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink})
     if a.kind == nkVarTuple:
-      if tup.kind != tyTuple:
-        localError(c.config, a.info, errXExpected, "tuple")
-      elif length-2 != sonsLen(tup):
-        localError(c.config, a.info, errWrongNumberOfVariables)
-      b = newNodeI(nkVarTuple, a.info)
-      newSons(b, length)
-      b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator
-      b.sons[length-1] = def
-      addToVarSection(c, result, n, b)
-    elif tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and
-        a.kind == nkIdentDefs and a.len > 3:
-      message(c.config, a.info, warnEachIdentIsTuple)
-
-    for j in countup(0, length-3):
-      if a[j].kind == nkDotExpr:
-        fillPartialObject(c, a[j],
-          if a.kind != nkVarTuple: typ else: tup.sons[j])
-        addToVarSection(c, result, n, a)
-        continue
-      var v = semIdentDef(c, a.sons[j], symkind)
-      if sfGenSym notin v.flags and not isDiscardUnderscore(v):
-        addInterfaceDecl(c, v)
-      when oKeepVariableNames:
-        if c.inUnrolledContext > 0: v.flags.incl(sfShadowed)
+      # generate new section from tuple unpacking and embed it into this one
+      let assignments = makeVarTupleSection(c, n, a, def, tup, symkind, result)
+      let resSection = semVarOrLet(c, assignments, symkind)
+      for resDef in resSection:
+        addToVarSection(c, result, n, resDef)
+    else:
+      if tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and
+          a.len > 3:
+        # var a, b = (1, 2)
+        message(c.config, a.info, warnEachIdentIsTuple)
+
+      for j in 0..<a.len-2:
+        if a[j].kind == nkDotExpr:
+          fillPartialObject(c, a[j], typ)
+          addToVarSection(c, result, n, a)
+          continue
+        var v = semIdentDef(c, a[j], symkind, false)
+        when defined(nimsuggest):
+          v.hasUserSpecifiedType = hasUserSpecifiedType
+        styleCheckDef(c, v)
+        onDef(a[j].info, v)
+        if sfGenSym notin v.flags:
+          if not isDiscardUnderscore(v): addInterfaceDecl(c, v)
         else:
-          let shadowed = findShadowedVar(c, v)
-          if shadowed != nil:
-            shadowed.flags.incl(sfShadowed)
-            if shadowed.kind == skResult and sfGenSym notin v.flags:
-              message(c.config, a.info, warnResultShadowed)
-            # a shadowed variable is an error unless it appears on the right
-            # side of the '=':
-            if warnShadowIdent in c.config.notes and not identWithin(def, v.name):
-              message(c.config, a.info, warnShadowIdent, v.name.s)
-      if a.kind != nkVarTuple:
+          if v.owner == nil: v.owner = c.p.owner
+        when oKeepVariableNames:
+          if c.inUnrolledContext > 0: v.flags.incl(sfShadowed)
+          else:
+            let shadowed = findShadowedVar(c, v)
+            if shadowed != nil:
+              shadowed.flags.incl(sfShadowed)
+              if shadowed.kind == skResult and sfGenSym notin v.flags:
+                message(c.config, a.info, warnResultShadowed)
         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)
+        # this is needed for the evaluation pass, guard checking
+        #  and custom pragmas:
         b = newNodeI(nkIdentDefs, a.info)
         if importantComments(c.config):
           # keep documentation information:
           b.comment = a.comment
-        addSon(b, newSymNode(v))
-        addSon(b, a.sons[length-2])      # keep type desc for doc generator
-        addSon(b, copyTree(def))
+        # postfix not generated here (to generate, get rid of it in transf)
+        if a[j].kind == nkPragmaExpr:
+          var p = newNodeI(nkPragmaExpr, a.info)
+          p.add newSymNode(v)
+          p.add a[j][1]
+          b.add p
+        else:
+          b.add newSymNode(v)
+        # keep type desc for doc generator
+        b.add a[^2]
+        b.add copyTree(def)
         addToVarSection(c, result, n, b)
-      else:
-        if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j]
-        # bug #7663, for 'nim check' this can be a non-tuple:
-        if tup.kind == tyTuple: setVarType(c, v, tup.sons[j])
-        else: v.typ = tup
-        b.sons[j] = newSymNode(v)
-      checkNilable(c, v)
-      if sfCompileTime in v.flags: hasCompileTime = true
-      if v.flags * {sfGlobal, sfThread} == {sfGlobal}:
-        message(c.config, v.info, hintGlobalVar)
-  if hasCompileTime:
-    vm.setupCompileTimeVar(c.module, c.graph, result)
-    # handled by the VM codegen:
-    #c.graph.recordStmt(c.graph, c.module, result)
+        v.ast = b
+        if def.kind == nkEmpty:
+          let actualType = v.typ.skipTypes({tyGenericInst, tyAlias,
+                                            tyUserTypeClassInst})
+          if actualType.kind in {tyObject, tyDistinct} and
+            actualType.requiresInit:
+            defaultConstructionError(c, v.typ, v.info)
+          else:
+            checkNilable(c, v)
+          # allow let to not be initialised if imported from C:
+          if v.kind == skLet and sfImportc notin v.flags and (strictDefs notin c.features or not isLocalSym(v)):
+            localError(c.config, a.info, errLetNeedsInit)
+        if sfCompileTime in v.flags:
+          var x = newNodeI(result.kind, v.info)
+          x.add result[i]
+          vm.setupCompileTimeVar(c.module, c.idgen, c.graph, x)
+        if v.flags * {sfGlobal, sfThread} == {sfGlobal}:
+          message(c.config, v.info, hintGlobalVar)
+        if {sfGlobal, sfPure} <= v.flags:
+          globalVarInitCheck(c, def)
+        suggestSym(c.graph, v.info, v, c.graph.usageSym)
 
 proc semConst(c: PContext, n: PNode): PNode =
   result = copyNode(n)
-  for i in countup(0, sonsLen(n) - 1):
-    var a = n.sons[i]
+  inc c.inStaticContext
+  var b: PNode
+  for i in 0..<n.len:
+    var a = n[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)
-    var typ: PType = nil
-    if a.sons[1].kind != nkEmpty: typ = semTypeNode(c, a.sons[1], nil)
+    if a.kind notin {nkConstDef, nkVarTuple}: illFormedAst(a, c.config)
+    checkMinSonsLen(a, 3, c.config)
 
-    var def = semConstExpr(c, a.sons[2])
-    if def == nil:
-      localError(c.config, a.sons[2].info, errConstExprExpected)
+    b = semVarMacroPragma(c, a, n)
+    if b != nil:
+      addToVarSection(c, result, b)
       continue
+
+    var hasUserSpecifiedType = false
+    var typ: PType = nil
+    if a[^2].kind != nkEmpty:
+      typ = semTypeNode(c, a[^2], nil)
+      hasUserSpecifiedType = true
+
+    var typFlags: TTypeAllowedFlags = {}
+
+    # don't evaluate here since the type compatibility check below may add a converter
+    openScope(c)
+    var def = semExprWithType(c, a[^1], {efTypeAllowed}, typ)
+
+    if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}:
+      typFlags.incl taIsTemplateOrMacro
+    elif def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
+      typFlags.incl taProcContextIsNotMacro
+
     # check type compatibility between def.typ and typ:
     if typ != nil:
-      def = fitRemoveHiddenConv(c, typ, def)
+      if typ.isMetaType:
+        def = inferWithMetatype(c, typ, def)
+        typ = def.typ
+      else:
+        def = fitRemoveHiddenConv(c, typ, def)
     else:
       typ = def.typ
-    if typ == nil:
-      localError(c.config, a.sons[2].info, errConstExprExpected)
-      continue
-    if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit:
-      localError(c.config, a.info, "invalid type for const: " & typeToString(typ))
+
+    # evaluate the node
+    def = semConstExpr(c, def)
+    if def == nil:
+      localError(c.config, a[^1].info, errConstExprExpected)
       continue
-    setVarType(c, v, typ)
-    v.ast = def               # no need to copy
-    if sfGenSym notin v.flags: addInterfaceDecl(c, v)
-    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)
+    if def.kind != nkNilLit:
+      if c.matchedConcept != nil:
+        typFlags.incl taConcept
+      typeAllowedCheck(c, a.info, typ, skConst, typFlags)
+    closeScope(c)
+
+    if a.kind == nkVarTuple:
+      # generate new section from tuple unpacking and embed it into this one
+      let assignments = makeVarTupleSection(c, n, a, def, typ, skConst, result)
+      let resSection = semConst(c, assignments)
+      for resDef in resSection:
+        addToVarSection(c, result, n, resDef)
+    else:
+      for j in 0..<a.len-2:
+        var v = semIdentDef(c, a[j], skConst)
+        when defined(nimsuggest):
+          v.hasUserSpecifiedType = hasUserSpecifiedType
+        if sfGenSym notin v.flags: addInterfaceDecl(c, v)
+        elif v.owner == nil: v.owner = getCurrOwner(c)
+        styleCheckDef(c, v)
+        onDef(a[j].info, v)
+
+        var fillSymbol = true
+        if v.typ != nil:
+          # symbol already has type and probably value
+          # don't mutate
+          fillSymbol = false
+        else:
+          setVarType(c, v, typ)
+        b = newNodeI(nkConstDef, a.info)
+        if importantComments(c.config): b.comment = a.comment
+        # postfix not generated here (to generate, get rid of it in transf)
+        if a[j].kind == nkPragmaExpr:
+          var p = newNodeI(nkPragmaExpr, a.info)
+          p.add newSymNode(v)
+          p.add a[j][1].copyTree
+          b.add p
+        else:
+          b.add newSymNode(v)
+        b.add a[1]
+        b.add copyTree(def)
+        if fillSymbol:
+          v.ast = b
+        addToVarSection(c, result, n, b)
+  dec c.inStaticContext
 
 include semfields
 
-proc addForVarDecl(c: PContext, v: PSym) =
-  if warnShadowIdent in c.config.notes:
-    let shadowed = findShadowedVar(c, v)
-    if shadowed != nil:
-      # XXX should we do this here?
-      #shadowed.flags.incl(sfShadowed)
-      message(c.config, v.info, warnShadowIdent, v.name.s)
-  addDecl(c, v)
 
 proc symForVar(c: PContext, n: PNode): PSym =
-  let m = if n.kind == nkPragmaExpr: n.sons[0] else: n
+  let m = if n.kind == nkPragmaExpr: n[0] else: n
   result = newSymG(skForVar, m, c)
-  styleCheckDef(c.config, result)
+  styleCheckDef(c, result)
+  onDef(n.info, result)
+  if n.kind == nkPragmaExpr:
+    pragma(c, result, n[1], forVarPragmas)
 
-proc semForVars(c: PContext, n: PNode): PNode =
+proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
   result = n
-  var length = sonsLen(n)
-  let iterBase = n.sons[length-2].typ
-  var iter = skipTypes(iterBase, {tyGenericInst, tyAlias, tySink})
-  # length == 3 means that there is one for loop variable
+  let iterBase = n[^2].typ
+  var iter = skipTypes(iterBase, {tyGenericInst, tyAlias, tySink, tyOwned})
+  var iterAfterVarLent = iter.skipTypes({tyGenericInst, tyAlias, tyLent, tyVar})
+  # n.len == 3 means that there is one for loop variable
   # and thus no tuple unpacking:
-  if iter.kind != tyTuple or length == 3:
-    if length == 3:
-      var v = symForVar(c, n.sons[0])
-      if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
-      # BUGFIX: don't use `iter` here as that would strip away
-      # the ``tyGenericInst``! See ``tests/compile/tgeneric.nim``
-      # for an example:
-      v.typ = iterBase
-      n.sons[0] = newSymNode(v)
-      if sfGenSym notin v.flags: addForVarDecl(c, v)
+  if iterAfterVarLent.kind == tyEmpty:
+    localError(c.config, n[^2].info, "cannot infer element type of $1" %
+               renderTree(n[^2], {renderNoComments}))
+  if iterAfterVarLent.kind != tyTuple or n.len == 3:
+    if n.len == 3:
+      if n[0].kind == nkVarTuple:
+        if iterAfterVarLent.kind != tyTuple:
+          return localErrorNode(c, n, n[0].info, errTupleUnpackingTupleExpected %
+              [typeToString(n[1].typ, preferDesc)])
+        elif n[0].len-1 != iterAfterVarLent.len:
+          return localErrorNode(c, n, n[0].info, errWrongNumberOfVariables)
+
+        for i in 0..<n[0].len-1:
+          var v = symForVar(c, n[0][i])
+          if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
+          case iter.kind
+          of tyVar, tyLent:
+            v.typ = newTypeS(iter.kind, c)
+            v.typ.add iterAfterVarLent[i]
+            if tfVarIsPtr in iter.flags:
+              v.typ.flags.incl tfVarIsPtr
+          else:
+            v.typ = iter[i]
+          n[0][i] = newSymNode(v)
+          if sfGenSym notin v.flags and not isDiscardUnderscore(v): addDecl(c, v)
+          elif v.owner == nil: v.owner = getCurrOwner(c)
+      else:
+        var v = symForVar(c, n[0])
+        if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
+        # BUGFIX: don't use `iter` here as that would strip away
+        # the ``tyGenericInst``! See ``tests/compile/tgeneric.nim``
+        # for an example:
+        v.typ = iterBase
+        n[0] = newSymNode(v)
+        if sfGenSym notin v.flags and not isDiscardUnderscore(v): addDecl(c, v)
+        elif v.owner == nil: v.owner = getCurrOwner(c)
     else:
       localError(c.config, n.info, errWrongNumberOfVariables)
-  elif length-2 != sonsLen(iter):
+  elif n.len-2 != iterAfterVarLent.len:
     localError(c.config, n.info, errWrongNumberOfVariables)
   else:
-    for i in countup(0, length - 3):
-      var v = symForVar(c, n.sons[i])
-      if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
-      v.typ = iter.sons[i]
-      n.sons[i] = newSymNode(v)
-      if sfGenSym notin v.flags and not isDiscardUnderscore(v):
-        addForVarDecl(c, v)
+    for i in 0..<n.len - 2:
+      if n[i].kind == nkVarTuple:
+        var mutable = false
+        var isLent = false
+        case iter[i].kind
+        of tyVar:
+          mutable = true
+          iter[i] = iter[i].skipTypes({tyVar})
+        of tyLent:
+          isLent = true
+          iter[i] = iter[i].skipTypes({tyLent})
+        else: discard
+
+        if n[i].len-1 != iter[i].len:
+          localError(c.config, n[i].info, errWrongNumberOfVariables)
+        for j in 0..<n[i].len-1:
+          var v = symForVar(c, n[i][j])
+          if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
+          if mutable:
+            v.typ = newTypeS(tyVar, c)
+            v.typ.add iter[i][j]
+          elif isLent:
+            v.typ = newTypeS(tyLent, c)
+            v.typ.add iter[i][j]
+          else:
+            v.typ = iter[i][j]
+          n[i][j] = newSymNode(v)
+          if not isDiscardUnderscore(v): addDecl(c, v)
+          elif v.owner == nil: v.owner = getCurrOwner(c)
+      else:
+        var v = symForVar(c, n[i])
+        if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
+        case iter.kind
+        of tyVar, tyLent:
+          v.typ = newTypeS(iter.kind, c)
+          v.typ.add iterAfterVarLent[i]
+          if tfVarIsPtr in iter.flags:
+            v.typ.flags.incl tfVarIsPtr
+        else:
+          v.typ = iter[i]
+        n[i] = newSymNode(v)
+        if sfGenSym notin v.flags:
+          if not isDiscardUnderscore(v): addDecl(c, v)
+        elif v.owner == nil: v.owner = getCurrOwner(c)
   inc(c.p.nestedLoopCounter)
+  let oldBreakInLoop = c.p.breakInLoop
+  c.p.breakInLoop = true
   openScope(c)
-  n.sons[length-1] = semStmt(c, n.sons[length-1])
+  n[^1] = semExprBranch(c, n[^1], flags)
+  if efInTypeof notin flags:
+    discardCheck(c, n[^1], flags)
   closeScope(c)
+  c.p.breakInLoop = oldBreakInLoop
   dec(c.p.nestedLoopCounter)
 
 proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =
@@ -674,34 +1178,34 @@ proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =
   result = semExprNoDeref(c, result, {efWantIterator})
 
 proc isTrivalStmtExpr(n: PNode): bool =
-  for i in 0 .. n.len-2:
+  for i in 0..<n.len-1:
     if n[i].kind notin {nkEmpty, nkCommentStmt}:
       return false
   result = true
 
-proc handleForLoopMacro(c: PContext; n: PNode): PNode =
-  let iterExpr = n[^2]
-  if iterExpr.kind in nkCallKinds:
+proc handleStmtMacro(c: PContext; n, selector: PNode; magicType: string;
+                     flags: TExprFlags): PNode =
+  if selector.kind in nkCallKinds:
     # we transform
     # n := for a, b, c in m(x, y, z): Y
     # to
     # m(n)
-    let forLoopStmt = magicsys.getCompilerProc(c.graph, "ForLoopStmt")
-    if forLoopStmt == nil: return
+    let maType = magicsys.getCompilerProc(c.graph, magicType)
+    if maType == nil: return
 
-    let headSymbol = iterExpr[0]
-    var o: TOverloadIter
+    let headSymbol = selector[0]
+    var o: TOverloadIter = default(TOverloadIter)
     var match: PSym = nil
     var symx = initOverloadIter(o, c, headSymbol)
     while symx != nil:
       if symx.kind in {skTemplate, skMacro}:
-        if symx.typ.len == 2 and symx.typ[1] == forLoopStmt.typ:
+        if symx.typ.len == 2 and symx.typ.firstParamType == maType.typ:
           if match == nil:
             match = symx
           else:
             localError(c.config, n.info, errAmbiguousCallXYZ % [
               getProcHeader(c.config, match),
-              getProcHeader(c.config, symx), $iterExpr])
+              getProcHeader(c.config, symx), $selector])
       symx = nextOverloadIter(o, c, headSymbol)
 
     if match == nil: return
@@ -709,146 +1213,300 @@ proc handleForLoopMacro(c: PContext; n: PNode): PNode =
     callExpr.add newSymNode(match)
     callExpr.add n
     case match.kind
-    of skMacro: result = semMacroExpr(c, callExpr, callExpr, match, {})
-    of skTemplate: result = semTemplateExpr(c, callExpr, match, {})
+    of skMacro: result = semMacroExpr(c, callExpr, callExpr, match, flags)
+    of skTemplate: result = semTemplateExpr(c, callExpr, match, flags)
     else: result = nil
+  else:
+    result = nil
+
+proc handleForLoopMacro(c: PContext; n: PNode; flags: TExprFlags): PNode =
+  result = handleStmtMacro(c, n, n[^2], "ForLoopStmt", flags)
+
+proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode =
+  # n[0] has been sem'checked and has a type. We use this to resolve
+  # '`case`(n[0])' but then we pass 'n' to the `case` macro. This seems to
+  # be the best solution.
+  var toResolve = newNodeI(nkCall, n.info)
+  toResolve.add newIdentNode(getIdent(c.cache, "case"), n.info)
+  toResolve.add n[0]
+
+  var errors: CandidateErrors = @[]
+  var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {efNoUndeclared},
+                           errors, false)
+  if r.state == csMatch:
+    var match = r.calleeSym
+    markUsed(c, n[0].info, match)
+    onUse(n[0].info, match)
+
+    # but pass 'n' to the `case` macro, not 'n[0]':
+    r.call[1] = n
+    let toExpand = semResolvedCall(c, r, r.call, {})
+    case match.kind
+    of skMacro: result = semMacroExpr(c, toExpand, toExpand, match, flags)
+    of skTemplate: result = semTemplateExpr(c, toExpand, match, flags)
+    else: result = errorNode(c, n[0])
+  else:
+    result = errorNode(c, n[0])
+  if result.kind == nkEmpty:
+    localError(c.config, n[0].info, errSelectorMustBeOfCertainTypes)
+  # this would be the perfectly consistent solution with 'for loop macros',
+  # but it kinda sucks for pattern matching as the matcher is not attached to
+  # a type then:
+  when false:
+    result = handleStmtMacro(c, n, n[0], "CaseStmt")
 
-proc semFor(c: PContext, n: PNode): PNode =
+proc semFor(c: PContext, n: PNode; flags: TExprFlags): PNode =
   checkMinSonsLen(n, 3, c.config)
-  var length = sonsLen(n)
-  result = handleForLoopMacro(c, n)
+  result = handleForLoopMacro(c, n, flags)
   if result != nil: return result
   openScope(c)
   result = n
-  n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator})
-  var call = n.sons[length-2]
-  if call.kind == nkStmtListExpr and isTrivalStmtExpr(call):
+  n[^2] = semExprNoDeref(c, n[^2], {efWantIterator})
+  var call = n[^2]
+
+  if call.kind == nkStmtListExpr and (isTrivalStmtExpr(call) or (call.lastSon.kind in nkCallKinds and call.lastSon[0].sym.kind == skIterator)):
     call = call.lastSon
-    n.sons[length-2] = call
+    n[^2] = call
   let isCallExpr = call.kind in nkCallKinds
   if isCallExpr and call[0].kind == nkSym and
       call[0].sym.magic in {mFields, mFieldPairs, mOmpParFor}:
-    if call.sons[0].sym.magic == mOmpParFor:
-      result = semForVars(c, n)
-      result.kind = nkParForStmt
+    if call[0].sym.magic == mOmpParFor:
+      result = semForVars(c, n, flags)
+      result.transitionSonsKind(nkParForStmt)
     else:
-      result = semForFields(c, n, call.sons[0].sym.magic)
-  elif isCallExpr and call.sons[0].typ.callConv == ccClosure and
-      tfIterator in call.sons[0].typ.flags:
+      result = semForFields(c, n, call[0].sym.magic)
+  elif isCallExpr and isClosureIterator(call[0].typ.skipTypes(abstractInst)):
     # first class iterator:
-    result = semForVars(c, n)
-  elif not isCallExpr or call.sons[0].kind != nkSym or
-      call.sons[0].sym.kind != skIterator:
-    if length == 3:
-      n.sons[length-2] = implicitIterator(c, "items", n.sons[length-2])
-    elif length == 4:
-      n.sons[length-2] = implicitIterator(c, "pairs", n.sons[length-2])
+    result = semForVars(c, n, flags)
+  elif not isCallExpr or call[0].kind != nkSym or
+      call[0].sym.kind != skIterator:
+    if n.len == 3:
+      n[^2] = implicitIterator(c, "items", n[^2])
+    elif n.len == 4:
+      n[^2] = implicitIterator(c, "pairs", n[^2])
     else:
-      localError(c.config, n.sons[length-2].info, "iterator within for loop context expected")
-    result = semForVars(c, n)
+      localError(c.config, n[^2].info, "iterator within for loop context expected")
+    result = semForVars(c, n, flags)
   else:
-    result = semForVars(c, n)
+    result = semForVars(c, n, flags)
   # propagate any enforced VoidContext:
-  if n.sons[length-1].typ == c.enforceVoidContext:
+  if n[^1].typ == c.enforceVoidContext:
     result.typ = c.enforceVoidContext
+  elif efInTypeof in flags:
+    result.typ = result.lastSon.typ
   closeScope(c)
 
+proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
+  result = n
+  checkMinSonsLen(n, 2, c.config)
+  openScope(c)
+  pushCaseContext(c, n)
+  n[0] = semExprWithType(c, n[0])
+  var covered: Int128 = toInt128(0)
+  var typ = commonTypeBegin
+  var expectedType = expectedType
+  var hasElse = false
+  let caseTyp = skipTypes(n[0].typ, abstractVar-{tyTypeDesc})
+  var chckCovered = caseTyp.shouldCheckCaseCovered()
+  case caseTyp.kind
+  of tyFloat..tyFloat128, tyString, tyCstring, tyError, shouldChckCovered, tyRange:
+    discard
+  else:
+    popCaseContext(c)
+    closeScope(c)
+    return handleCaseStmtMacro(c, n, flags)
+  template invalidOrderOfBranches(n: PNode) =
+    localError(c.config, n.info, "invalid order of case branches")
+    break
+
+  for i in 1..<n.len:
+    setCaseContextIdx(c, i)
+    var x = n[i]
+    when defined(nimsuggest):
+      if c.config.ideCmd == ideSug and exactEquals(c.config.m.trackPos, x.info) and caseTyp.kind == tyEnum:
+        suggestEnum(c, x, caseTyp)
+    case x.kind
+    of nkOfBranch:
+      if hasElse: invalidOrderOfBranches(x)
+      checkMinSonsLen(x, 2, c.config)
+      semCaseBranch(c, n, x, i, covered)
+      var last = x.len-1
+      x[last] = semExprBranchScope(c, x[last], expectedType)
+      typ = commonType(c, typ, x[last])
+      if not endsInNoReturn(x[last]):
+        expectedType = typ
+    of nkElifBranch:
+      if hasElse: invalidOrderOfBranches(x)
+      chckCovered = false
+      checkSonsLen(x, 2, c.config)
+      openScope(c)
+      x[0] = forceBool(c, semExprWithType(c, x[0], expectedType = getSysType(c.graph, n.info, tyBool)))
+      x[1] = semExprBranch(c, x[1], expectedType = expectedType)
+      typ = commonType(c, typ, x[1])
+      if not endsInNoReturn(x[1]):
+        expectedType = typ
+      closeScope(c)
+    of nkElse:
+      checkSonsLen(x, 1, c.config)
+      x[0] = semExprBranchScope(c, x[0], expectedType)
+      typ = commonType(c, typ, x[0])
+      if not endsInNoReturn(x[0]):
+        expectedType = typ
+      if (chckCovered and covered == toCover(c, n[0].typ)) or hasElse:
+        message(c.config, x.info, warnUnreachableElse)
+      hasElse = true
+      chckCovered = false
+    else:
+      illFormedAst(x, c.config)
+  if chckCovered:
+    if covered == toCover(c, n[0].typ):
+      hasElse = true
+    elif n[0].typ.skipTypes(abstractRange).kind in {tyEnum, tyChar}:
+      localError(c.config, n.info, "not all cases are covered; missing: $1" %
+                 formatMissingEnums(c, n))
+    else:
+      localError(c.config, n.info, "not all cases are covered")
+  popCaseContext(c)
+  closeScope(c)
+  if isEmptyType(typ) or typ.kind in {tyNil, tyUntyped} or
+      (not hasElse and efInTypeof notin flags):
+    for i in 1..<n.len: discardCheck(c, n[i].lastSon, flags)
+    # propagate any enforced VoidContext:
+    if typ == c.enforceVoidContext:
+      result.typ = c.enforceVoidContext
+  else:
+    for i in 1..<n.len:
+      var it = n[i]
+      let j = it.len-1
+      if not endsInNoReturn(it[j]):
+        it[j] = fitNode(c, typ, it[j], it[j].info)
+    result.typ = typ
+
 proc semRaise(c: PContext, n: PNode): PNode =
   result = n
   checkSonsLen(n, 1, c.config)
   if n[0].kind != nkEmpty:
     n[0] = semExprWithType(c, n[0])
-    let typ = n[0].typ
+    var typ = n[0].typ
     if not isImportedException(typ, c.config):
-      if typ.kind != tyRef or typ.lastSon.kind != tyObject:
+      typ = typ.skipTypes({tyAlias, tyGenericInst, tyOwned})
+      if typ.kind != tyRef:
         localError(c.config, n.info, errExprCannotBeRaised)
-      if not isException(typ.lastSon):
-        localError(c.config, n.info, "raised object of type $1 does not inherit from Exception",
-                          [typeToString(typ)])
+      if typ.len > 0 and not isException(typ.elementType):
+        localError(c.config, n.info, "raised object of type $1 does not inherit from Exception" % typeToString(typ))
 
 proc addGenericParamListToScope(c: PContext, n: PNode) =
   if n.kind != nkGenericParams: illFormedAst(n, c.config)
-  for i in countup(0, sonsLen(n)-1):
-    var a = n.sons[i]
+  for i in 0..<n.len:
+    var a = n[i]
     if a.kind == nkSym: addDecl(c, a.sym)
     else: illFormedAst(a, c.config)
 
 proc typeSectionTypeName(c: PContext; n: PNode): PNode =
   if n.kind == nkPragmaExpr:
     if n.len == 0: illFormedAst(n, c.config)
-    result = n.sons[0]
+    result = n[0]
   else:
     result = n
+  if result.kind == nkPostfix:
+    if result.len != 2: illFormedAst(n, c.config)
+    result = result[1]
   if result.kind != nkSym: illFormedAst(n, c.config)
 
+proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
+  let typeDef = typeSection[i]
+  checkSonsLen(typeDef, 3, c.config)
+  var name = typeDef[0]
+  var s: PSym = nil
+  if name.kind == nkDotExpr and typeDef[2].kind == nkObjectTy:
+    let pkgName = considerQuotedIdent(c, name[0])
+    let typName = considerQuotedIdent(c, name[1])
+    let pkg = c.graph.packageSyms.strTableGet(pkgName)
+    if pkg.isNil or pkg.kind != skPackage:
+      localError(c.config, name.info, "unknown package name: " & pkgName.s)
+    else:
+      let typsym = c.graph.packageTypes.strTableGet(typName)
+      if typsym.isNil:
+        s = semIdentDef(c, name[1], skType)
+        onDef(name[1].info, s)
+        s.typ = newTypeS(tyObject, c)
+        s.typ.sym = s
+        s.flags.incl sfForward
+        c.graph.packageTypes.strTableAdd s
+        addInterfaceDecl(c, s)
+      elif typsym.kind == skType and sfForward in typsym.flags:
+        s = typsym
+        addInterfaceDecl(c, s)
+        # PRTEMP no onDef here?
+      else:
+        localError(c.config, name.info, typsym.name.s & " is not a type that can be forwarded")
+        s = typsym
+  else:
+    s = semIdentDef(c, name, skType)
+    onDef(name.info, s)
+    s.typ = newTypeS(tyForward, c)
+    s.typ.sym = s             # process pragmas:
+    if name.kind == nkPragmaExpr:
+      let rewritten = applyTypeSectionPragmas(c, name[1], typeDef)
+      if rewritten != nil:
+        case rewritten.kind
+        of nkTypeDef:
+          typeSection[i] = rewritten
+        of nkTypeSection:
+          typeSection.sons[i .. i] = rewritten.sons
+        else: illFormedAst(rewritten, c.config)
+        typeDefLeftSidePass(c, typeSection, i)
+        return
+      pragma(c, s, name[1], typePragmas)
+    if sfForward in s.flags:
+      # check if the symbol already exists:
+      let pkg = c.module.owner
+      if not isTopLevel(c) or pkg.isNil:
+        localError(c.config, name.info, "only top level types in a package can be 'package'")
+      else:
+        let typsym = c.graph.packageTypes.strTableGet(s.name)
+        if typsym != nil:
+          if sfForward notin typsym.flags or sfNoForward notin typsym.flags:
+            typeCompleted(typsym)
+            typsym.info = s.info
+          else:
+            localError(c.config, name.info, "cannot complete type '" & s.name.s & "' twice; " &
+                    "previous type completion was here: " & c.config$typsym.info)
+          s = typsym
+    # add it here, so that recursive types are possible:
+    if sfGenSym notin s.flags: addInterfaceDecl(c, s)
+    elif s.owner == nil: s.owner = getCurrOwner(c)
+
+  if name.kind == nkPragmaExpr:
+    if name[0].kind == nkPostfix:
+      typeDef[0][0][1] = newSymNode(s)
+    else:
+      typeDef[0][0] = newSymNode(s)
+  else:
+    if name.kind == nkPostfix:
+      typeDef[0][1] = newSymNode(s)
+    else:
+      typeDef[0] = newSymNode(s)
 
 proc typeSectionLeftSidePass(c: PContext, n: PNode) =
   # process the symbols on the left side for the whole type section, before
   # we even look at the type definitions on the right
-  for i in countup(0, sonsLen(n) - 1):
-    var a = n.sons[i]
+  var i = 0
+  while i < n.len: # n may grow due to type pragma macros
+    var a = n[i]
     when defined(nimsuggest):
       if c.config.cmd == cmdIdeTools:
         inc c.inTypeContext
         suggestStmt(c, a)
         dec c.inTypeContext
-    if a.kind == nkCommentStmt: continue
-    if a.kind != nkTypeDef: illFormedAst(a, c.config)
-    checkSonsLen(a, 3, c.config)
-    let name = a.sons[0]
-    var s: PSym
-    if name.kind == nkDotExpr and a[2].kind == nkObjectTy:
-      let pkgName = considerQuotedIdent(c, name[0])
-      let typName = considerQuotedIdent(c, name[1])
-      let pkg = c.graph.packageSyms.strTableGet(pkgName)
-      if pkg.isNil or pkg.kind != skPackage:
-        localError(c.config, name.info, "unknown package name: " & pkgName.s)
-      else:
-        let typsym = pkg.tab.strTableGet(typName)
-        if typsym.isNil:
-          s = semIdentDef(c, name[1], skType)
-          s.typ = newTypeS(tyObject, c)
-          s.typ.sym = s
-          s.flags.incl sfForward
-          pkg.tab.strTableAdd s
-          addInterfaceDecl(c, s)
-        elif typsym.kind == skType and sfForward in typsym.flags:
-          s = typsym
-          addInterfaceDecl(c, s)
-        else:
-          localError(c.config, name.info, typsym.name.s & " is not a type that can be forwarded")
-          s = typsym
-    else:
-      s = semIdentDef(c, name, skType)
-      s.typ = newTypeS(tyForward, c)
-      s.typ.sym = s             # process pragmas:
-      if name.kind == nkPragmaExpr:
-        pragma(c, s, name.sons[1], typePragmas)
-      if sfForward in s.flags:
-        # check if the symbol already exists:
-        let pkg = c.module.owner
-        if not isTopLevel(c) or pkg.isNil:
-          localError(c.config, name.info, "only top level types in a package can be 'package'")
-        else:
-          let typsym = pkg.tab.strTableGet(s.name)
-          if typsym != nil:
-            if sfForward notin typsym.flags or sfNoForward notin typsym.flags:
-              typeCompleted(typsym)
-              typsym.info = s.info
-            else:
-              localError(c.config, name.info, "cannot complete type '" & s.name.s & "' twice; " &
-                      "previous type completion was here: " & c.config$typsym.info)
-            s = typsym
-      # add it here, so that recursive types are possible:
-      if sfGenSym notin s.flags: addInterfaceDecl(c, s)
-
-    if name.kind == nkPragmaExpr:
-      a.sons[0].sons[0] = newSymNode(s)
-    else:
-      a.sons[0] = newSymNode(s)
+    case a.kind
+    of nkCommentStmt: discard
+    of nkTypeDef: typeDefLeftSidePass(c, n, i)
+    else: illFormedAst(a, c.config)
+    inc i
 
 proc checkCovariantParamsUsages(c: PContext; genericType: PType) =
-  var body = genericType[^1]
+  var body = genericType.typeBodyImpl
 
   proc traverseSubTypes(c: PContext; t: PType): bool =
     template error(msg) = localError(c.config, genericType.sym.info, msg)
@@ -865,18 +1523,18 @@ proc checkCovariantParamsUsages(c: PContext; genericType: PType) =
       for field in t.n:
         subresult traverseSubTypes(c, field.typ)
     of tyArray:
-      return traverseSubTypes(c, t[1])
+      return traverseSubTypes(c, t.elementType)
     of tyProc:
-      for subType in t.sons:
+      for subType in t.signature:
         if subType != nil:
           subresult traverseSubTypes(c, subType)
       if result:
         error("non-invariant type param used in a proc type: " & $t)
     of tySequence:
-      return traverseSubTypes(c, t[0])
+      return traverseSubTypes(c, t.elementType)
     of tyGenericInvocation:
-      let targetBody = t[0]
-      for i in 1 ..< t.len:
+      let targetBody = t.genericHead
+      for i in 1..<t.len:
         let param = t[i]
         if param.kind == tyGenericParam:
           if tfCovariant in param.flags:
@@ -900,13 +1558,13 @@ proc checkCovariantParamsUsages(c: PContext; genericType: PType) =
     of tyUserTypeClass, tyUserTypeClassInst:
       error("non-invariant type parameters are not supported in concepts")
     of tyTuple:
-      for fieldType in t.sons:
+      for fieldType in t.kids:
         subresult traverseSubTypes(c, fieldType)
     of tyPtr, tyRef, tyVar, tyLent:
-      if t.base.kind == tyGenericParam: return true
-      return traverseSubTypes(c, t.base)
-    of tyDistinct, tyAlias, tySink:
-      return traverseSubTypes(c, t.lastSon)
+      if t.elementType.kind == tyGenericParam: return true
+      return traverseSubTypes(c, t.elementType)
+    of tyDistinct, tyAlias, tySink, tyOwned:
+      return traverseSubTypes(c, t.skipModifier)
     of tyGenericInst:
       internalAssert c.config, false
     else:
@@ -914,17 +1572,18 @@ proc checkCovariantParamsUsages(c: PContext; genericType: PType) =
   discard traverseSubTypes(c, body)
 
 proc typeSectionRightSidePass(c: PContext, n: PNode) =
-  for i in countup(0, sonsLen(n) - 1):
-    var a = n.sons[i]
+  for i in 0..<n.len:
+    var a = n[i]
     if a.kind == nkCommentStmt: continue
     if a.kind != nkTypeDef: illFormedAst(a, c.config)
     checkSonsLen(a, 3, c.config)
-    let name = typeSectionTypeName(c, a.sons[0])
+    let name = typeSectionTypeName(c, a[0])
     var s = name.sym
-    if s.magic == mNone and a.sons[2].kind == nkEmpty:
+    if s.magic == mNone and a[2].kind == nkEmpty:
       localError(c.config, a.info, errImplOfXexpected % s.name.s)
     if s.magic != mNone: processMagicType(c, s)
-    if a.sons[1].kind != nkEmpty:
+    let oldFlags = s.typ.flags
+    if a[1].kind != nkEmpty:
       # We have a generic type declaration here. In generic types,
       # symbol lookup needs to be done here.
       openScope(c)
@@ -937,21 +1596,32 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
       #   TGObj[T] = object
       #   TAlias[T] = TGObj[T]
       #
-      s.typ.n = semGenericParamList(c, a.sons[1], s.typ)
-      a.sons[1] = s.typ.n
+      s.typ.n = semGenericParamList(c, a[1], s.typ)
+      a[1] = s.typ.n
       s.typ.size = -1 # could not be computed properly
       # we fill it out later. For magic generics like 'seq', it won't be filled
       # so we use tyNone instead of nil to not crash for strange conversions
       # like: mydata.seq
-      rawAddSon(s.typ, newTypeS(tyNone, c))
+      if s.typ.kind in {tyOpenArray, tyVarargs} and s.typ.len == 1:
+        # XXX investigate why `tySequence` cannot be added here for now.
+        discard
+      else:
+        rawAddSon(s.typ, newTypeS(tyNone, c))
       s.ast = a
       inc c.inGenericContext
-      var body = semTypeNode(c, a.sons[2], nil)
+      var body = semTypeNode(c, a[2], s.typ)
       dec c.inGenericContext
       if body != nil:
         body.sym = s
         body.size = -1 # could not be computed properly
-        s.typ.sons[sonsLen(s.typ) - 1] = body
+        if body.kind == tyObject:
+          # add flags applied to generic type to object (nominal) type
+          incl(body.flags, oldFlags)
+          # {.inheritable, final.} is already disallowed, but
+          # object might have been assumed to be final
+          if tfInheritable in oldFlags and tfFinal in body.flags:
+            excl(body.flags, tfFinal)
+        s.typ[^1] = body
         if tfCovariant in s.typ.flags:
           checkCovariantParamsUsages(c, s.typ)
           # XXX: This is a temporary limitation:
@@ -963,17 +1633,17 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
           # possibilities such as instantiating C++ generic types with
           # garbage collected Nim types.
           if sfImportc in s.flags:
-            var body = s.typ.lastSon
+            var body = s.typ.last
             if body.kind == tyObject:
               # erases all declared fields
-              body.n.sons = nil
+              body.n.sons = @[]
 
       popOwner(c)
       closeScope(c)
-    elif a.sons[2].kind != nkEmpty:
+    elif a[2].kind != nkEmpty:
       # process the type's body:
       pushOwner(c, s)
-      var t = semTypeNode(c, a.sons[2], s.typ)
+      var t = semTypeNode(c, a[2], s.typ)
       if s.typ == nil:
         s.typ = t
       elif t != s.typ and (s.typ == nil or s.typ.kind != tyAlias):
@@ -982,73 +1652,145 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
         #debug s.typ
       s.ast = a
       popOwner(c)
-    let aa = a.sons[2]
+      # If the right hand side expression was a macro call we replace it with
+      # its evaluated result here so that we don't execute it once again in the
+      # final pass
+      if a[2].kind in nkCallKinds:
+        incl a[2].flags, nfSem # bug #10548
+    if sfExportc in s.flags:
+      if s.typ.kind == tyAlias:
+        localError(c.config, name.info, "{.exportc.} not allowed for type aliases")
+      elif s.typ.kind == tyGenericBody:
+        localError(c.config, name.info, "{.exportc.} not allowed for generic types")
+
+    if tfBorrowDot in s.typ.flags:
+      let body = s.typ.skipTypes({tyGenericBody})
+      if body.kind != tyDistinct:
+        # flag might be copied from alias/instantiation:
+        let t = body.skipTypes({tyAlias, tyGenericInst})
+        if not (t.kind == tyDistinct and tfBorrowDot in t.flags):
+          excl s.typ.flags, tfBorrowDot
+          localError(c.config, name.info, "only a 'distinct' type can borrow `.`")
+    let aa = a[2]
     if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
-       aa.sons[0].kind == nkObjectTy:
+       aa[0].kind == nkObjectTy:
       # give anonymous object a dummy symbol:
       var st = s.typ
-      if st.kind == tyGenericBody: st = st.lastSon
+      if st.kind == tyGenericBody: st = st.typeBodyImpl
       internalAssert c.config, st.kind in {tyPtr, tyRef}
-      internalAssert c.config, st.lastSon.sym == nil
+      internalAssert c.config, st.last.sym == nil
       incl st.flags, tfRefsAnonObj
+      let objTy = st.last
+      # add flags for `ref object` etc to underlying `object`
+      incl(objTy.flags, oldFlags)
+      # {.inheritable, final.} is already disallowed, but
+      # object might have been assumed to be final
+      if tfInheritable in oldFlags and tfFinal in objTy.flags:
+        excl(objTy.flags, tfFinal)
       let obj = newSym(skType, getIdent(c.cache, s.name.s & ":ObjectType"),
-                              getCurrOwner(c), s.info)
-      obj.typ = st.lastSon
-      st.lastSon.sym = obj
-
-
-proc checkForMetaFields(c: PContext; n: PNode) =
-  template checkMeta(t) =
-    if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags:
-      localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString)
+                       c.idgen, getCurrOwner(c), s.info)
+      obj.flags.incl sfGeneratedType
+      let symNode = newSymNode(obj)
+      obj.ast = a.shallowCopy
+      case a[0].kind
+      of nkSym: obj.ast[0] = symNode
+      of nkPragmaExpr:
+        obj.ast[0] = a[0].shallowCopy
+        if a[0][0].kind == nkPostfix:
+          obj.ast[0][0] = a[0][0].shallowCopy
+          obj.ast[0][0][1] = symNode
+        else:
+          obj.ast[0][0] = symNode
+        obj.ast[0][1] = a[0][1]
+      of nkPostfix:
+        obj.ast[0] = a[0].shallowCopy
+        obj.ast[0][1] = symNode
+      else: assert(false)
+      obj.ast[1] = a[1]
+      obj.ast[2] = a[2][0]
+      if sfPure in s.flags:
+        obj.flags.incl sfPure
+      obj.typ = objTy
+      objTy.sym = obj
+  for sk in c.skipTypes:
+    discard semTypeNode(c, sk, nil)
+  c.skipTypes = @[]
+
+proc checkForMetaFields(c: PContext; n: PNode; hasError: var bool) =
+  proc checkMeta(c: PContext; n: PNode; t: PType; hasError: var bool; parent: PType) =
+    if t != nil and (t.isMetaType or t.kind == tyNone) and tfGenericTypeParam notin t.flags:
+      if t.kind == tyBuiltInTypeClass and t.len == 1 and t.elementType.kind == tyProc:
+        localError(c.config, n.info, ("'$1' is not a concrete type; " &
+          "for a callback without parameters use 'proc()'") % t.typeToString)
+      elif t.kind == tyNone and parent != nil:
+        # TODO: openarray has the `tfGenericTypeParam` flag & generics
+        # TODO: handle special cases (sink etc.) and views
+        localError(c.config, n.info, errTIsNotAConcreteType % parent.typeToString)
+      else:
+        localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString)
+      hasError = true
 
   if n.isNil: return
   case n.kind
   of nkRecList, nkRecCase:
-    for s in n: checkForMetaFields(c, s)
+    for s in n: checkForMetaFields(c, s, hasError)
   of nkOfBranch, nkElse:
-    checkForMetaFields(c, n.lastSon)
+    checkForMetaFields(c, n.lastSon, hasError)
   of nkSym:
     let t = n.sym.typ
     case t.kind
     of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyLent, tyPtr, tyRef,
-       tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink:
-      let start = int ord(t.kind in {tyGenericInvocation, tyGenericInst})
-      for i in start ..< t.sons.len:
-        checkMeta(t.sons[i])
+       tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink, tyOwned:
+      let start = ord(t.kind in {tyGenericInvocation, tyGenericInst})
+      for i in start..<t.len:
+        checkMeta(c, n, t[i], hasError, t)
     else:
-      checkMeta(t)
+      checkMeta(c, n, t, hasError, nil)
   else:
     internalAssert c.config, false
 
 proc typeSectionFinalPass(c: PContext, n: PNode) =
-  for i in countup(0, sonsLen(n) - 1):
-    var a = n.sons[i]
+  for i in 0..<n.len:
+    var a = n[i]
     if a.kind == nkCommentStmt: continue
-    let name = typeSectionTypeName(c, a.sons[0])
+    let name = typeSectionTypeName(c, a[0])
     var s = name.sym
+    # check the style here after the pragmas have been processed:
+    styleCheckDef(c, s)
     # compute the type's size and check for illegal recursions:
-    if a.sons[1].kind == nkEmpty:
+    if a[1].kind == nkEmpty:
       var x = a[2]
-      while x.kind in {nkStmtList, nkStmtListExpr} and x.len > 0:
-        x = x.lastSon
-      if x.kind notin {nkObjectTy, nkDistinctTy, nkEnumTy, nkEmpty} and
-          s.typ.kind notin {tyObject, tyEnum}:
-        # type aliases are hard:
-        var t = semTypeNode(c, x, nil)
-        assert t != nil
-        if s.typ != nil and s.typ.kind notin {tyAlias, tySink}:
-          if t.kind in {tyProc, tyGenericInst} and not t.isMetaType:
-            assignType(s.typ, t)
-            s.typ.id = t.id
-          elif t.kind in {tyObject, tyEnum, tyDistinct}:
-            assert s.typ != nil
-            assignType(s.typ, t)
-            s.typ.id = t.id     # same id
-      checkConstructedType(c.config, s.info, s.typ)
-      if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
-        checkForMetaFields(c, s.typ.n)
-  instAllTypeBoundOp(c, n.info)
+      if x.kind in nkCallKinds and nfSem in x.flags:
+        discard "already semchecked, see line marked with bug #10548"
+      else:
+        while x.kind in {nkStmtList, nkStmtListExpr} and x.len > 0:
+          x = x.lastSon
+        # we need the 'safeSkipTypes' here because illegally recursive types
+        # can enter at this point, see bug #13763
+        if x.kind notin {nkObjectTy, nkDistinctTy, nkEnumTy, nkEmpty} and
+            s.typ.safeSkipTypes(abstractPtrs).kind notin {tyObject, tyEnum}:
+          # type aliases are hard:
+          var t = semTypeNode(c, x, nil)
+          assert t != nil
+          if s.typ != nil and s.typ.kind notin {tyAlias, tySink}:
+            if t.kind in {tyProc, tyGenericInst} and not t.isMetaType:
+              assignType(s.typ, t)
+              s.typ.itemId = t.itemId
+            elif t.kind in {tyObject, tyEnum, tyDistinct}:
+              assert s.typ != nil
+              assignType(s.typ, t)
+              s.typ.itemId = t.itemId     # same id
+        var hasError = false
+        let baseType = s.typ.safeSkipTypes(abstractPtrs)
+        if baseType.kind in {tyObject, tyTuple} and not baseType.n.isNil and
+          (x.kind in {nkObjectTy, nkTupleTy} or
+           (x.kind in {nkRefTy, nkPtrTy} and x.len == 1 and
+           x[0].kind in {nkObjectTy, nkTupleTy})
+          ):
+          checkForMetaFields(c, baseType.n, hasError)
+        if not hasError:
+          checkConstructedType(c.config, s.info, s.typ)
+  #instAllTypeBoundOp(c, n.info)
 
 
 proc semAllTypeSections(c: PContext; n: PNode): PNode =
@@ -1056,17 +1798,17 @@ proc semAllTypeSections(c: PContext; n: PNode): PNode =
     case n.kind
     of nkIncludeStmt:
       for i in 0..<n.len:
-        var f = checkModuleName(c.config, n.sons[i])
-        if f != InvalidFileIDX:
+        var f = checkModuleName(c.config, n[i])
+        if f != InvalidFileIdx:
           if containsOrIncl(c.includedFiles, f.int):
-            localError(c.config, n.info, errRecursiveDependencyX % toFilename(c.config, f))
+            localError(c.config, n.info, errRecursiveDependencyX % toMsgFilename(c.config, f))
           else:
             let code = c.graph.includeFileCallback(c.graph, c.module, f)
             gatherStmts c, code, result
             excl(c.includedFiles, f.int)
     of nkStmtList:
-      for i in 0 ..< n.len:
-        gatherStmts(c, n.sons[i], result)
+      for i in 0..<n.len:
+        gatherStmts(c, n[i], result)
     of nkTypeSection:
       incl n.flags, nfSem
       typeSectionLeftSidePass(c, n)
@@ -1078,7 +1820,7 @@ proc semAllTypeSections(c: PContext; n: PNode): PNode =
   gatherStmts(c, n, result)
 
   template rec(name) =
-    for i in 0 ..< result.len:
+    for i in 0..<result.len:
       if result[i].kind == nkTypeSection:
         name(c, result[i])
 
@@ -1092,8 +1834,8 @@ proc semAllTypeSections(c: PContext; n: PNode): PNode =
           when setbit: incl n.flags, nfSem
           name(c, n)
         elif n.kind == nkStmtList:
-          for i in 0 ..< n.len:
-            `name rec`(c, n.sons[i])
+          for i in 0..<n.len:
+            `name rec`(c, n[i])
       `name rec`(c, n)
     rec typeSectionLeftSidePass, true
     rec typeSectionRightSidePass
@@ -1113,158 +1855,124 @@ 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):
-    if n.sons[i].kind == nkSym: addParamOrResult(c, n.sons[i].sym, kind)
+  for i in 1..<n.len:
+    if n[i].kind == nkSym: addParamOrResult(c, n[i].sym, kind)
     else: illFormedAst(n, c.config)
 
 proc semBorrow(c: PContext, n: PNode, s: PSym) =
   # search for the correct alias:
-  var b = searchForBorrowProc(c, c.currentScope.parent, s)
-  if b != nil:
+  var (b, state) = searchForBorrowProc(c, c.currentScope.parent, s)
+  case state
+  of bsMatch:
     # store the alias:
-    n.sons[bodyPos] = newSymNode(b)
+    n[bodyPos] = newSymNode(b)
+    # Carry over the original symbol magic, this is necessary in order to ensure
+    # the semantic pass is correct
+    s.magic = b.magic
+    if b.typ != nil and b.typ.len > 0:
+      s.typ.n[0] = b.typ.n[0]
+    s.typ.flags = b.typ.flags
+  of bsNoDistinct:
+    localError(c.config, n.info, "borrow proc without distinct type parameter is meaningless")
+  of bsReturnNotMatch:
+    localError(c.config, n.info, "borrow from proc return type mismatch: '$1'" % typeToString(b.typ.returnType))
+  of bsGeneric:
+    localError(c.config, n.info, "borrow with generic parameter is not supported")
+  of bsNotSupported:
+    localError(c.config, n.info, "borrow from '$1' is not supported" % $b.name.s)
   else:
     localError(c.config, n.info, errNoSymbolToBorrowFromFound)
 
-proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) =
-  if t != nil:
-    var s = newSym(skResult, getIdent(c.cache, "result"), getCurrOwner(c), info)
+proc swapResult(n: PNode, sRes: PSym, dNode: PNode) =
+  ## Swap nodes that are (skResult) symbols to d(estination)Node.
+  for i in 0..<n.safeLen:
+    if n[i].kind == nkSym and n[i].sym == sRes:
+        n[i] = dNode
+    swapResult(n[i], sRes, dNode)
+
+proc addResult(c: PContext, n: PNode, t: PType, owner: TSymKind) =
+  template genResSym(s) =
+    var s = newSym(skResult, getIdent(c.cache, "result"), c.idgen,
+                   getCurrOwner(c), n.info)
     s.typ = t
     incl(s.flags, sfUsed)
-    addParamOrResult(c, s, owner)
-    c.p.resultSym = s
 
-proc addResultNode(c: PContext, n: PNode) =
-  if c.p.resultSym != nil: addSon(n, newSymNode(c.p.resultSym))
-
-proc copyExcept(n: PNode, i: int): PNode =
-  result = copyNode(n)
-  for j in 0..<n.len:
-    if j != i: result.add(n.sons[j])
-
-proc lookupMacro(c: PContext, n: PNode): PSym =
-  if n.kind == nkSym:
-    result = n.sym
-    if result.kind notin {skMacro, skTemplate}: result = nil
-  else:
-    result = searchInScopes(c, considerQuotedIdent(c, n), {skMacro, skTemplate})
+  if owner == skMacro or t != nil:
+    if n.len > resultPos and n[resultPos] != nil:
+      if n[resultPos].sym.kind != skResult:
+        localError(c.config, n.info, "incorrect result proc symbol")
+      if n[resultPos].sym.owner != getCurrOwner(c):
+        # re-write result with new ownership, and re-write the proc accordingly
+        let sResSym = n[resultPos].sym
+        genResSym(s)
+        n[resultPos] = newSymNode(s)
+        swapResult(n, sResSym, n[resultPos])
+      c.p.resultSym = n[resultPos].sym
+    else:
+      genResSym(s)
+      c.p.resultSym = s
+      n.add newSymNode(c.p.resultSym)
+    addParamOrResult(c, c.p.resultSym, owner)
 
 proc semProcAnnotation(c: PContext, prc: PNode;
                        validPragmas: TSpecialWords): PNode =
-  var n = prc.sons[pragmasPos]
+  # Mirrored with semVarMacroPragma
+  result = nil
+  var n = prc[pragmasPos]
   if n == nil or n.kind == nkEmpty: return
-  for i in countup(0, n.len-1):
-    var it = n.sons[i]
-    var key = if it.kind in nkPragmaCallKinds and it.len >= 1: it.sons[0] else: it
-    let m = lookupMacro(c, key)
-    if m == nil:
-      if key.kind == nkIdent and key.ident.id == ord(wDelegator):
-        if considerQuotedIdent(c, prc.sons[namePos]).s == "()":
-          prc.sons[namePos] = newIdentNode(c.cache.idDelegator, prc.info)
-          prc.sons[pragmasPos] = copyExcept(n, i)
-        else:
-          localError(c.config, prc.info, "only a call operator can be a delegator")
-      continue
-    elif sfCustomPragma in m.flags:
-      continue # semantic check for custom pragma happens later in semProcAux
-
-    # we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and
-    # let the semantic checker deal with it:
-    var x = newNodeI(nkCall, n.info)
-    x.add(newSymNode(m))
-    prc.sons[pragmasPos] = copyExcept(n, i)
-    if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0:
-      prc.sons[pragmasPos] = c.graph.emptyNode
-
-    if it.kind in nkPragmaCallKinds and it.len > 1:
-      # pass pragma arguments to the macro too:
-      for i in 1..<it.len:
-        x.add(it.sons[i])
-    x.add(prc)
-
-    # recursion assures that this works for multiple macro annotations too:
-    result = semExpr(c, x)
-    # since a proc annotation can set pragmas, we process these here again.
-    # This is required for SqueakNim-like export pragmas.
-    if result.kind in procDefs and result[namePos].kind == nkSym and
-        result[pragmasPos].kind != nkEmpty:
-      pragma(c, result[namePos].sym, result[pragmasPos], validPragmas)
-    return
-
-proc setGenericParamsMisc(c: PContext; n: PNode): PNode =
-  let orig = n.sons[genericParamsPos]
-  # we keep the original params around for better error messages, see
-  # issue https://github.com/nim-lang/Nim/issues/1713
-  result = semGenericParamList(c, orig)
-  if n.sons[miscPos].kind == nkEmpty:
-    n.sons[miscPos] = newTree(nkBracket, c.graph.emptyNode, orig)
-  else:
-    n.sons[miscPos].sons[1] = orig
-  n.sons[genericParamsPos] = result
+  for i in 0..<n.len:
+    let it = n[i]
+    let key = if it.kind in nkPragmaCallKinds and it.len >= 1: it[0] else: it
+
+    trySuggestPragmas(c, key)
+
+    if isPossibleMacroPragma(c, it, key):
+      # we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and
+      # let the semantic checker deal with it:
+      var x = newNodeI(nkCall, key.info)
+      x.add(key)
+
+      if it.kind in nkPragmaCallKinds and it.len > 1:
+        # pass pragma arguments to the macro too:
+        for i in 1..<it.len:
+          x.add(it[i])
+
+      # Drop the pragma from the list, this prevents getting caught in endless
+      # recursion when the nkCall is semanticized
+      prc[pragmasPos] = copyExcept(n, i)
+      if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0:
+        prc[pragmasPos] = c.graph.emptyNode
+
+      x.add(prc)
+
+      # recursion assures that this works for multiple macro annotations too:
+      var r = semOverloadedCall(c, x, x, {skMacro, skTemplate}, {efNoUndeclared})
+      if r == nil:
+        # Restore the old list of pragmas since we couldn't process this
+        prc[pragmasPos] = n
+        # No matching macro was found but there's always the possibility this may
+        # be a .pragma. template instead
+        continue
 
-proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
-  # XXX semProcAux should be good enough for this now, we will eventually
-  # remove semLambda
-  result = semProcAnnotation(c, n, lambdaPragmas)
-  if result != nil: return result
-  result = n
-  checkSonsLen(n, bodyPos + 1, c.config)
-  var s: PSym
-  if n[namePos].kind != nkSym:
-    s = newSym(skProc, c.cache.idAnon, getCurrOwner(c), n.info)
-    s.ast = n
-    n.sons[namePos] = newSymNode(s)
-  else:
-    s = n[namePos].sym
-  pushOwner(c, s)
-  openScope(c)
-  var gp: PNode
-  if n.sons[genericParamsPos].kind != nkEmpty:
-    gp = setGenericParamsMisc(c, n)
-  else:
-    gp = newNodeI(nkGenericParams, n.info)
-
-  if n.sons[paramsPos].kind != nkEmpty:
-    semParamList(c, n.sons[paramsPos], gp, s)
-    # paramsTypeCheck(c, s.typ)
-    if sonsLen(gp) > 0 and n.sons[genericParamsPos].kind == nkEmpty:
-      # we have a list of implicit type parameters:
-      n.sons[genericParamsPos] = gp
-  else:
-    s.typ = newProcType(c, n.info)
-  if n.sons[pragmasPos].kind != nkEmpty:
-    pragma(c, s, n.sons[pragmasPos], lambdaPragmas)
-  s.options = c.config.options
-  if n.sons[bodyPos].kind != nkEmpty:
-    if sfImportc in s.flags:
-      localError(c.config, n.sons[bodyPos].info, errImplOfXNotAllowed % s.name.s)
-    #if efDetermineType notin flags:
-    # XXX not good enough; see tnamedparamanonproc.nim
-    if gp.len == 0 or (gp.len == 1 and tfRetType in gp[0].typ.flags):
-      pushProcCon(c, s)
-      addResult(c, s.typ.sons[0], n.info, skProc)
-      addResultNode(c, n)
-      let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
-      n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s)
-      popProcCon(c)
-    elif efOperand notin flags:
-      localError(c.config, n.info, errGenericLambdaNotAllowed)
-    sideEffectsCheck(c, s)
-  else:
-    localError(c.config, n.info, errImplOfXexpected % s.name.s)
-  closeScope(c)           # close scope for parameters
-  popOwner(c)
-  result.typ = s.typ
+      doAssert r[0].kind == nkSym
+      let m = r[0].sym
+      case m.kind
+      of skMacro: result = semMacroExpr(c, r, r, m, {})
+      of skTemplate: result = semTemplateExpr(c, r, m, {})
+      else:
+        prc[pragmasPos] = n
+        continue
 
-proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
-  var n = n
+      doAssert result != nil
 
-  let original = n.sons[namePos].sym
+      return result
+
+proc semInferredLambda(c: PContext, pt: TypeMapping, n: PNode): PNode =
+  ## used for resolving 'auto' in lambdas based on their callsite
+  var n = n
+  let original = n[namePos].sym
   let s = original #copySym(original, false)
   #incl(s.flags, sfFromGeneric)
   #s.owner = original
@@ -1272,11 +1980,10 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
   n = replaceTypesInBody(c, pt, n, original)
   result = n
   s.ast = result
-  n.sons[namePos].sym = s
-  n.sons[genericParamsPos] = c.graph.emptyNode
+  n[namePos].sym = s
+  n[genericParamsPos] = c.graph.emptyNode
   # for LL we need to avoid wrong aliasing
   let params = copyTree n.typ.n
-  n.sons[paramsPos] = params
   s.typ = n.typ
   for i in 1..<params.len:
     if params[i].typ.kind in {tyTypeDesc, tyGenericParam,
@@ -1288,14 +1995,14 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
   pushOwner(c, s)
   addParams(c, params, skProc)
   pushProcCon(c, s)
-  addResult(c, n.typ.sons[0], n.info, skProc)
-  addResultNode(c, n)
-  let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
-  n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s)
+  addResult(c, n, n.typ.returnType, skProc)
+  s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], n.typ.returnType))
+  trackProc(c, s, s.ast[bodyPos])
   popProcCon(c)
   popOwner(c)
   closeScope(c)
-
+  if optOwnedRefs in c.config.globalOptions and result.typ != nil:
+    result.typ = makeVarType(c, result.typ, tyOwned)
   # alternative variant (not quite working):
   # var prc = arg[0].sym
   # let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info)
@@ -1311,96 +2018,233 @@ proc activate(c: PContext, n: PNode) =
     of nkLambdaKinds:
       discard semLambda(c, n, {})
     of nkCallKinds:
-      for i in 1 ..< n.len: activate(c, n[i])
+      for i in 1..<n.len: activate(c, n[i])
     else:
       discard
 
 proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
-  if s.typ.sons[0] != nil and not
-      (s.kind == skIterator and s.typ.callConv != ccClosure):
-    addResult(c, s.typ.sons[0], n.info, s.kind)
-    addResultNode(c, n)
+  if s.kind == skMacro:
+    let resultType = sysTypeFromName(c.graph, n.info, "NimNode")
+    addResult(c, n, resultType, s.kind)
+  elif s.typ.returnType != nil and not isInlineIterator(s.typ):
+    addResult(c, n, s.typ.returnType, s.kind)
+
+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
-  of "=destroy":
-    let t = s.typ
-    var noError = false
-    if t.len == 2 and t.sons[0] == nil and t.sons[1].kind == tyVar:
-      var obj = t.sons[1].sons[0]
-      while true:
-        incl(obj.flags, tfHasAsgn)
-        if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.lastSon
-        elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
-        else: break
-      if obj.kind in {tyObject, tyDistinct}:
-        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 not noError and sfSystemModule notin s.owner.flags:
+proc prevDestructor(c: PContext; prevOp: PSym; obj: PType; info: TLineInfo) =
+  var msg = "cannot bind another '" & prevOp.name.s & "' to: " & typeToString(obj)
+  if sfOverridden notin prevOp.flags:
+    msg.add "; previous declaration was constructed here implicitly: " & (c.config $ prevOp.info)
+  else:
+    msg.add "; previous declaration was here: " & (c.config $ prevOp.info)
+  localError(c.config, info, errGenerated, msg)
+
+proc whereToBindTypeHook(c: PContext; t: PType): PType =
+  result = t
+  while true:
+    if result.kind in {tyGenericBody, tyGenericInst}: result = result.skipModifier
+    elif result.kind == tyGenericInvocation: result = result[0]
+    else: break
+  if result.kind in {tyObject, tyDistinct, tySequence, tyString}:
+    result = canonType(c, result)
+
+proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
+  let t = s.typ
+  var noError = false
+  let cond = t.len == 2 and t.returnType != nil
+
+  if cond:
+    var obj = t.firstParamType
+    while true:
+      incl(obj.flags, tfHasAsgn)
+      if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.skipModifier
+      elif obj.kind == tyGenericInvocation: obj = obj.genericHead
+      else: break
+
+    var res = t.returnType
+    while true:
+      if res.kind in {tyGenericBody, tyGenericInst}: res = res.skipModifier
+      elif res.kind == tyGenericInvocation: res = res.genericHead
+      else: break
+
+    if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, res):
+      obj = canonType(c, obj)
+      let ao = getAttachedOp(c.graph, obj, op)
+      if ao == s:
+        discard "forward declared destructor"
+      elif ao.isNil and tfCheckedForDestructor notin obj.flags:
+        setAttachedOp(c.graph, c.module.position, obj, op, s)
+      else:
+        prevDestructor(c, ao, obj, n.info)
+      noError = true
+      if obj.owner.getModule != s.getModule:
+        localError(c.config, n.info, errGenerated,
+          "type bound operation `" & s.name.s & "` 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 '=dup' must be proc[T: object](x: T): T")
+
+  incl(s.flags, sfUsed)
+  incl(s.flags, sfOverridden)
+
+proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp; suppressVarDestructorWarning = false) =
+  let t = s.typ
+  var noError = false
+  let cond = case op
+             of attachedWasMoved:
+               t.len == 2 and t.returnType == nil and t.firstParamType.kind == tyVar
+             of attachedTrace:
+               t.len == 3 and t.returnType == nil and t.firstParamType.kind == tyVar and t[2].kind == tyPointer
+             of attachedDestructor:
+               if c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
+                 t.len == 2 and t.returnType == nil
+               else:
+                 t.len == 2 and t.returnType == nil and t.firstParamType.kind == tyVar
+             else:
+               t.len >= 2 and t.returnType == nil
+
+  if cond:
+    var obj = t.firstParamType.skipTypes({tyVar})
+    while true:
+      incl(obj.flags, tfHasAsgn)
+      if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.skipModifier
+      elif obj.kind == tyGenericInvocation: obj = obj.genericHead
+      else: break
+    if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
+      if (not suppressVarDestructorWarning) and op == attachedDestructor and t.firstParamType.kind == tyVar and
+          c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
+        message(c.config, n.info, warnDeprecated, "A custom '=destroy' hook which takes a 'var T' parameter is deprecated; it should take a 'T' parameter")
+      obj = canonType(c, obj)
+      let ao = getAttachedOp(c.graph, obj, op)
+      if ao == s:
+        discard "forward declared destructor"
+      elif ao.isNil and tfCheckedForDestructor notin obj.flags:
+        setAttachedOp(c.graph, c.module.position, obj, op, s)
+      else:
+        prevDestructor(c, ao, obj, n.info)
+      noError = true
+      if obj.owner.getModule != s.getModule:
+        localError(c.config, n.info, errGenerated,
+          "type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
+  if not noError and sfSystemModule notin s.owner.flags:
+    case op
+    of attachedTrace:
+      localError(c.config, n.info, errGenerated,
+        "signature for '=trace' must be proc[T: object](x: var T; env: pointer)")
+    of attachedDestructor:
+      if c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
+        localError(c.config, n.info, errGenerated,
+          "signature for '=destroy' must be proc[T: object](x: var T) or proc[T: object](x: T)")
+      else:
+        localError(c.config, n.info, errGenerated,
+          "signature for '=destroy' must be proc[T: object](x: var T)")
+    else:
       localError(c.config, n.info, errGenerated,
         "signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
-    incl(s.flags, sfUsed)
+  incl(s.flags, sfUsed)
+  incl(s.flags, sfOverridden)
+
+proc semOverride(c: PContext, s: PSym, n: PNode) =
+  let name = s.name.s.normalize
+  case name
+  of "=destroy":
+    bindTypeHook(c, s, n, attachedDestructor)
+    if s.ast != nil:
+      if s.ast[pragmasPos].kind == nkEmpty:
+        s.ast[pragmasPos] = newNodeI(nkPragma, s.info)
+      s.ast[pragmasPos].add newTree(nkExprColonExpr,
+          newIdentNode(c.cache.getIdent("raises"),  s.info), newNodeI(nkBracket, s.info))
   of "deepcopy", "=deepcopy":
     if s.typ.len == 2 and
-        s.typ.sons[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and
-        sameType(s.typ.sons[1], s.typ.sons[0]):
+        s.typ.firstParamType.skipTypes(abstractInst).kind in {tyRef, tyPtr} and
+        sameType(s.typ.firstParamType, s.typ.returnType):
       # Note: we store the deepCopy in the base of the pointer to mitigate
       # the problem that pointers are structural types:
-      var t = s.typ.sons[1].skipTypes(abstractInst).lastSon.skipTypes(abstractInst)
+      var t = s.typ.firstParamType.skipTypes(abstractInst).elementType.skipTypes(abstractInst)
       while true:
-        if t.kind == tyGenericBody: t = t.lastSon
-        elif t.kind == tyGenericInvocation: t = t.sons[0]
+        if t.kind == tyGenericBody: t = t.typeBodyImpl
+        elif t.kind == tyGenericInvocation: t = t.genericHead
         else: break
-      if t.kind in {tyObject, tyDistinct, tyEnum}:
-        if t.deepCopy.isNil: t.deepCopy = s
+      if t.kind in {tyObject, tyDistinct, tyEnum, tySequence, tyString}:
+        if getAttachedOp(c.graph, t, attachedDeepCopy).isNil:
+          setAttachedOp(c.graph, c.module.position, t, attachedDeepCopy, s)
         else:
           localError(c.config, n.info, errGenerated,
                      "cannot bind another 'deepCopy' to: " & typeToString(t))
       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")
     incl(s.flags, sfUsed)
-  of "=", "=sink":
+    incl(s.flags, sfOverridden)
+  of "=", "=copy", "=sink":
     if s.magic == mAsgn: return
     incl(s.flags, sfUsed)
+    incl(s.flags, sfOverridden)
+    if name == "=":
+      message(c.config, n.info, warnDeprecated, "Overriding `=` hook is deprecated; Override `=copy` hook instead")
     let t = s.typ
-    if t.len == 3 and t.sons[0] == nil and t.sons[1].kind == tyVar:
-      var obj = t.sons[1].sons[0]
+    if t.len == 3 and t.returnType == nil and t.firstParamType.kind == tyVar:
+      var obj = t.firstParamType.elementType
       while true:
         incl(obj.flags, tfHasAsgn)
-        if obj.kind == tyGenericBody: obj = obj.lastSon
-        elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
+        if obj.kind == tyGenericBody: obj = obj.skipModifier
+        elif obj.kind == tyGenericInvocation: obj = obj.genericHead
         else: break
-      var objB = t.sons[2]
+      var objB = t[2]
       while true:
-        if objB.kind == tyGenericBody: objB = objB.lastSon
+        if objB.kind == tyGenericBody: objB = objB.skipModifier
         elif objB.kind in {tyGenericInvocation, tyGenericInst}:
-          objB = objB.sons[0]
+          objB = objB.genericHead
         else: break
-      if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB):
-        let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink)
-        if opr[].isNil:
-          opr[] = s
+      if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB):
+        # attach these ops to the canonical tySequence
+        obj = canonType(c, obj)
+        #echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj)
+        let k = if name == "=" or name == "=copy": attachedAsgn else: attachedSink
+        let ao = getAttachedOp(c.graph, obj, k)
+        if ao == s:
+          discard "forward declared op"
+        elif ao.isNil and tfCheckedForDestructor notin obj.flags:
+          setAttachedOp(c.graph, c.module.position, obj, k, s)
         else:
+          prevDestructor(c, ao, obj, n.info)
+        if obj.owner.getModule != s.getModule:
           localError(c.config, n.info, errGenerated,
-                     "cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
+            "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,
                 "signature for '" & s.name.s & "' must be proc[T: object](x: var T; y: T)")
+  of "=trace":
+    if s.magic != mTrace:
+      bindTypeHook(c, s, n, attachedTrace)
+  of "=wasmoved":
+    if s.magic != mWasMoved:
+      bindTypeHook(c, s, n, attachedWasMoved)
+  of "=dup":
+    if s.magic != mDup:
+      bindDupHook(c, s, n, attachedDup)
   else:
-    if sfOverriden in s.flags:
+    if sfOverridden in s.flags:
       localError(c.config, n.info, errGenerated,
                  "'destroy' or 'deepCopy' expected for 'override'")
 
 proc cursorInProcAux(conf: ConfigRef; n: PNode): bool =
+  result = false
   if inCheckpoint(n.info, conf.m.trackPos) != cpNone: return true
   for i in 0..<n.safeLen:
     if cursorInProcAux(conf, n[i]): return true
@@ -1408,70 +2252,123 @@ proc cursorInProcAux(conf: ConfigRef; n: PNode): bool =
 proc cursorInProc(conf: ConfigRef; n: PNode): bool =
   if n.info.fileIndex == conf.m.trackPos.fileIndex:
     result = cursorInProcAux(conf, n)
-
-type
-  TProcCompilationSteps = enum
-    stepRegisterSymbol,
-    stepDetermineType,
+  else:
+    result = false
 
 proc hasObjParam(s: PSym): bool =
+  result = false
   var t = s.typ
-  for col in countup(1, sonsLen(t)-1):
-    if skipTypes(t.sons[col], skipPtrs).kind == tyObject:
+  for col in 1..<t.len:
+    if skipTypes(t[col], skipPtrs).kind == tyObject:
       return true
 
 proc finishMethod(c: PContext, s: PSym) =
   if hasObjParam(s):
-    methodDef(c.graph, s, false)
+    methodDef(c.graph, c.idgen, s)
+
+proc semCppMember(c: PContext; s: PSym; n: PNode) =
+  if sfImportc notin s.flags:
+    let isVirtual = sfVirtual in s.flags
+    let isCtor = sfConstructor in s.flags
+    let pragmaName = if isVirtual: "virtual" elif isCtor: "constructor" else: "member"
+    if c.config.backend == backendCpp:
+      if s.typ.len < 2 and not isCtor:
+        localError(c.config, n.info, pragmaName & " must have at least one parameter")
+      for son in s.typ.signature:
+        if son!=nil and son.isMetaType:
+          localError(c.config, n.info, pragmaName & " unsupported for generic routine")
+      var typ: PType
+      if isCtor:
+        typ = s.typ.returnType
+        if typ == nil or typ.kind != tyObject:
+          localError(c.config, n.info, "constructor must return an object")
+        if sfImportc in typ.sym.flags:
+          localError(c.config, n.info, "constructor in an imported type needs importcpp pragma")
+      else:
+        typ = s.typ.firstParamType
+      if typ.kind == tyPtr and not isCtor:
+        typ = typ.elementType
+      if typ.kind != tyObject:
+        localError(c.config, n.info, pragmaName & " must be either ptr to object or object type.")
+      if typ.owner.id == s.owner.id and c.module.id == s.owner.id:
+        c.graph.memberProcsPerType.mgetOrPut(typ.itemId, @[]).add s
+      else:
+        localError(c.config, n.info,
+          pragmaName & " procs must be defined in the same scope as the type they are virtual for and it must be a top level scope")
+    else:
+      localError(c.config, n.info, pragmaName & " procs are only supported in C++")
+  else:
+    var typ = s.typ.returnType
+    if typ != nil and typ.kind == tyObject and typ.itemId notin c.graph.initializersPerType:
+      var initializerCall = newTree(nkCall, newSymNode(s))
+      var isInitializer = n[paramsPos].len > 1
+      for i in  1..<n[paramsPos].len:
+        let p = n[paramsPos][i]
+        let val = p[^1]
+        if val.kind == nkEmpty:
+          isInitializer = false
+          break
+        var j = 0
+        while p[j].sym.kind == skParam:
+          initializerCall.add val
+          inc j
+      if isInitializer:
+        c.graph.initializersPerType[typ.itemId] = initializerCall
 
 proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
-  if isGenericRoutine(s):
+  if s.isGenericRoutine:
     let tt = s.typ
     var foundObj = false
     # we start at 1 for now so that tparsecombnum continues to compile.
     # XXX Revisit this problem later.
-    for col in countup(1, sonsLen(tt)-1):
-      let t = tt.sons[col]
+    for col in 1..<tt.len:
+      let t = tt[col]
       if t != nil and t.kind == tyGenericInvocation:
-        var x = skipTypes(t.sons[0], {tyVar, tyLent, tyPtr, tyRef, tyGenericInst,
-                                      tyGenericInvocation, tyGenericBody,
-                                      tyAlias, tySink})
-        if x.kind == tyObject and t.len-1 == n.sons[genericParamsPos].len:
+        var x = skipTypes(t.genericHead, {tyVar, tyLent, tyPtr, tyRef, tyGenericInst,
+                                 tyGenericInvocation, tyGenericBody,
+                                 tyAlias, tySink, tyOwned})
+        if x.kind == tyObject and t.len-1 == n[genericParamsPos].len:
           foundObj = true
-          x.methods.safeAdd((col,s))
-    if not foundObj:
-      message(c.config, n.info, warnDeprecated, "generic method not attachable to object type")
+          addMethodToGeneric(c.graph, c.module.position, x, col, s)
+    message(c.config, n.info, warnDeprecated, "generic methods are deprecated")
+    #if not foundObj:
+    #  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.
-    # and result.sons[bodyPos].kind != nkEmpty:
+    # and result[bodyPos].kind != nkEmpty:
     if hasObjParam(s):
-      methodDef(c.graph, s, fromCache=false)
+      methodDef(c.graph, c.idgen, s)
     else:
       localError(c.config, n.info, "'method' needs a parameter that has an object type")
 
 proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
-                validPragmas: TSpecialWords,
-                phase = stepRegisterSymbol): PNode =
+                validPragmas: TSpecialWords, flags: TExprFlags = {}): PNode =
   result = semProcAnnotation(c, n, validPragmas)
   if result != nil: return result
   result = n
-  checkSonsLen(n, bodyPos + 1, c.config)
+  checkMinSonsLen(n, bodyPos + 1, c.config)
+
+  let
+    isAnon = n[namePos].kind == nkEmpty
+    isHighlight = c.config.ideCmd == ideHighlight
+
   var s: PSym
-  var typeIsDetermined = false
-  var isAnon = false
-  if n[namePos].kind != nkSym:
-    assert phase == stepRegisterSymbol
-
-    if n[namePos].kind == nkEmpty:
-      s = newSym(kind, c.cache.idAnon, getCurrOwner(c), n.info)
-      incl(s.flags, sfUsed)
-      isAnon = true
-    else:
-      s = semIdentDef(c, n.sons[0], kind)
-    n.sons[namePos] = newSymNode(s)
-    s.ast = n
-    #s.scope = c.currentScope
+
+  case n[namePos].kind
+  of nkEmpty:
+    s = newSym(kind, c.cache.idAnon, c.idgen, c.getCurrOwner, n.info)
+    s.flags.incl sfUsed
+    n[namePos] = newSymNode(s)
+  of nkSym:
+    s = n[namePos].sym
+    s.owner = c.getCurrOwner
+  else:
+    # Highlighting needs to be done early so the position for
+    # name isn't changed (see taccent_highlight). We don't want to check if this is the
+    # defintion yet since we are missing some info (comments, side effects)
+    s = semIdentDef(c, n[namePos], kind, reportToNimsuggest=isHighlight)
+    n[namePos] = newSymNode(s)
     when false:
       # disable for now
       if sfNoForward in c.module.flags and
@@ -1479,191 +2376,270 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
         addInterfaceOverloadableSymAt(c, c.currentScope, s)
         s.flags.incl sfForward
         return
-  else:
-    s = n[namePos].sym
-    s.owner = getCurrOwner(c)
-    typeIsDetermined = s.typ == nil
-    s.ast = n
-    #s.scope = c.currentScope
 
+  assert s.kind in skProcKinds
+
+  s.ast = n
   s.options = c.config.options
+  #s.scope = c.currentScope
+  if s.kind in {skMacro, skTemplate}:
+    # push noalias flag at first to prevent unwanted recursive calls:
+    incl(s.flags, sfNoalias)
 
-  # before compiling the proc body, set as current the scope
+  # before compiling the proc params & body, set as current the scope
   # where the proc was declared
-  let oldScope = c.currentScope
-  #c.currentScope = s.scope
+  let declarationScope = c.currentScope
   pushOwner(c, s)
   openScope(c)
-  var gp: PNode
-  if n.sons[genericParamsPos].kind != nkEmpty:
-    gp = setGenericParamsMisc(c, n)
-  else:
-    gp = newNodeI(nkGenericParams, n.info)
+
   # process parameters:
-  if n.sons[paramsPos].kind != nkEmpty:
-    semParamList(c, n.sons[paramsPos], gp, s)
-    if sonsLen(gp) > 0:
-      if n.sons[genericParamsPos].kind == nkEmpty:
-        # we have a list of implicit type parameters:
-        n.sons[genericParamsPos] = gp
-        # check for semantics again:
-        # semParamList(c, n.sons[ParamsPos], nil, s)
+  # generic parameters, parameters, and also the implicit generic parameters
+  # within are analysed. This is often the entirety of their semantic analysis
+  # but later we will have to do a check for forward declarations, which can by
+  # way of pragmas, default params, and so on invalidate this parsing.
+  # Nonetheless, we need to carry out this analysis to perform the search for a
+  # potential forward declaration.
+  setGenericParamsMisc(c, n)
+
+  if n[paramsPos].kind != nkEmpty:
+    semParamList(c, n[paramsPos], n[genericParamsPos], s)
   else:
     s.typ = newProcType(c, n.info)
+
+  if n[genericParamsPos].safeLen == 0:
+    # if there exist no explicit or implicit generic parameters, then this is
+    # at most a nullary generic (generic with no type params). Regardless of
+    # whether it's a nullary generic or non-generic, we restore the original.
+    # In the case of `nkEmpty` it's non-generic and an empty `nkGeneircParams`
+    # is a nullary generic.
+    #
+    # Remarks about nullary generics vs non-generics:
+    # The difference between a non-generic and nullary generic is minor in
+    # most cases but there are subtle and significant differences as well.
+    # Due to instantiation that generic procs go through, a static echo in the
+    # body of a nullary  generic will not be executed immediately, as it's
+    # instantiated and not immediately evaluated.
+    n[genericParamsPos] = n[miscPos][1]
+    n[miscPos] = c.graph.emptyNode
+
   if tfTriggersCompileTime in s.typ.flags: incl(s.flags, sfCompileTime)
-  if n.sons[patternPos].kind != nkEmpty:
-    n.sons[patternPos] = semPattern(c, n.sons[patternPos])
+  if n[patternPos].kind != nkEmpty:
+    n[patternPos] = semPattern(c, n[patternPos], s)
   if s.kind == skIterator:
     s.typ.flags.incl(tfIterator)
   elif s.kind == skFunc:
     incl(s.flags, sfNoSideEffect)
     incl(s.typ.flags, tfNoSideEffect)
-  var proto = searchForProc(c, oldScope, s)
-  if proto == nil or isAnon:
-    if s.kind == skIterator:
-      if s.typ.callConv != ccClosure:
-        s.typ.callConv = if isAnon: ccClosure else: ccInline
-    else:
+
+  var (proto, comesFromShadowScope) =
+      if isAnon: (nil, false)
+      else: searchForProc(c, declarationScope, s)
+  if proto == nil and sfForward in s.flags and n[bodyPos].kind != nkEmpty:
+    ## In cases such as a macro generating a proc with a gensymmed name we
+    ## know `searchForProc` will not find it and sfForward will be set. In
+    ## such scenarios the sym is shared between forward declaration and we
+    ## can treat the `s` as the proto.
+    ## To differentiate between that happening and a macro just returning a
+    ## forward declaration that has been typed before we check if the body
+    ## is not empty. This has the sideeffect of allowing multiple forward
+    ## declarations if they share the same sym.
+    ## See the "doubly-typed forward decls" case in tmacros_issues.nim
+    proto = s
+  let hasProto = proto != nil
+
+  # set the default calling conventions
+  case s.kind
+  of skIterator:
+    if s.typ.callConv != ccClosure:
+      s.typ.callConv = if isAnon: ccClosure else: ccInline
+  of skMacro, skTemplate:
+    # we don't bother setting calling conventions for macros and templates
+    discard
+  else:
+    # NB: procs with a forward decl have theirs determined by the forward decl
+    if not hasProto:
+      # in this case we're either a forward declaration or we're an impl without
+      # a forward decl. We set the calling convention or will be set during
+      # pragma analysis further down.
       s.typ.callConv = lastOptionEntry(c).defaultCC
-    # add it here, so that recursive procs are possible:
-    if sfGenSym in s.flags: discard
-    elif kind in OverloadableSyms:
-      if not typeIsDetermined:
-        addInterfaceOverloadableSymAt(c, oldScope, s)
-    else:
-      if not typeIsDetermined:
-        addInterfaceDeclAt(c, oldScope, s)
-    if n.sons[pragmasPos].kind != nkEmpty:
-      pragma(c, s, n.sons[pragmasPos], validPragmas)
+
+  if not hasProto and sfGenSym notin s.flags: #and not isAnon:
+    if s.kind in OverloadableSyms:
+      addInterfaceOverloadableSymAt(c, declarationScope, s)
     else:
-      implicitPragmas(c, s, n, validPragmas)
+      addInterfaceDeclAt(c, declarationScope, s)
+
+  pragmaCallable(c, s, n, validPragmas)
+  if not hasProto:
+    implicitPragmas(c, s, n.info, validPragmas)
+
+  if n[pragmasPos].kind != nkEmpty and sfBorrow notin s.flags:
+    setEffectsForProcType(c.graph, s.typ, n[pragmasPos], s)
+  s.typ.flags.incl tfEffectSystemWorkaround
+
+  # To ease macro generation that produce forwarded .async procs we now
+  # allow a bit redundancy in the pragma declarations. The rule is
+  # a prototype's pragma list must be a superset of the current pragma
+  # list.
+  # XXX This needs more checks eventually, for example that external
+  # linking names do agree:
+  if hasProto and (
+      # calling convention mismatch
+      tfExplicitCallConv in s.typ.flags and proto.typ.callConv != s.typ.callConv or
+      # implementation has additional pragmas
+      proto.typ.flags < s.typ.flags):
+    localError(c.config, n[pragmasPos].info, errPragmaOnlyInHeaderOfProcX %
+      ("'" & proto.name.s & "' from " & c.config$proto.info &
+        " '" & s.name.s & "' from " & c.config$s.info))
+
+  styleCheckDef(c, s)
+  if hasProto:
+    onDefResolveForward(n[namePos].info, proto)
   else:
-    if n.sons[pragmasPos].kind != nkEmpty:
-      pragma(c, s, n.sons[pragmasPos], validPragmas)
-      # To ease macro generation that produce forwarded .async procs we now
-      # allow a bit redudancy in the pragma declarations. The rule is
-      # a prototype's pragma list must be a superset of the current pragma
-      # list.
-      # XXX This needs more checks eventually, for example that external
-      # linking names do agree:
-      if proto.typ.callConv != s.typ.callConv or proto.typ.flags < s.typ.flags:
-        localError(c.config, n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX %
-          ("'" & proto.name.s & "' from " & c.config$proto.info))
-    if sfForward notin proto.flags:
-      wrongRedefinition(c, n.info, proto.name.s)
-    excl(proto.flags, sfForward)
+    onDef(n[namePos].info, s)
+
+  if hasProto:
+    if sfForward notin proto.flags and proto.magic == mNone:
+      wrongRedefinition(c, n.info, proto.name.s, proto.info)
+    if not comesFromShadowScope:
+      excl(proto.flags, sfForward)
+      incl(proto.flags, sfWasForwarded)
+    suggestSym(c.graph, s.info, proto, c.graph.usageSym)
     closeScope(c)         # close scope with wrong parameter symbols
     openScope(c)          # open scope for old (correct) parameter symbols
-    if proto.ast.sons[genericParamsPos].kind != nkEmpty:
-      addGenericParamListToScope(c, proto.ast.sons[genericParamsPos])
+    if proto.ast[genericParamsPos].isGenericParams:
+      addGenericParamListToScope(c, proto.ast[genericParamsPos])
     addParams(c, proto.typ.n, proto.kind)
     proto.info = s.info       # more accurate line information
-    s.typ = proto.typ
     proto.options = s.options
     s = proto
-    n.sons[genericParamsPos] = proto.ast.sons[genericParamsPos]
-    n.sons[paramsPos] = proto.ast.sons[paramsPos]
-    n.sons[pragmasPos] = proto.ast.sons[pragmasPos]
-    if n.sons[namePos].kind != nkSym: internalError(c.config, n.info, "semProcAux")
-    n.sons[namePos].sym = proto
-    if importantComments(c.config) and not isNil(proto.ast.comment):
+    n[genericParamsPos] = proto.ast[genericParamsPos]
+    n[paramsPos] = proto.ast[paramsPos]
+    n[pragmasPos] = proto.ast[pragmasPos]
+    if n[namePos].kind != nkSym: internalError(c.config, n.info, "semProcAux")
+    n[namePos].sym = proto
+    if importantComments(c.config) and proto.ast.comment.len > 0:
       n.comment = proto.ast.comment
     proto.ast = n             # needed for code generation
     popOwner(c)
     pushOwner(c, s)
 
-  if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
-  if s.name.s[0] in {'.', '('}:
-    if s.name.s in [".", ".()", ".="] and {destructor, dotOperators} * c.features == {}:
-      localError(c.config, n.info, "the overloaded " & s.name.s &
-        " operator has to be enabled with {.experimental: \"dotOperators\".}")
-    elif s.name.s == "()" and callOperator notin c.features:
-      localError(c.config, n.info, "the overloaded " & s.name.s &
-        " operator has to be enabled with {.experimental: \"callOperator\".}")
-
-  if n.sons[bodyPos].kind != nkEmpty:
-    # for DLL generation it is annoying to check for sfImportc!
-    if sfBorrow in s.flags:
-      localError(c.config, n.sons[bodyPos].info, errImplOfXNotAllowed % s.name.s)
-    let usePseudoGenerics = kind in {skMacro, skTemplate}
-    # Macros and Templates can have generic parameters, but they are
-    # only used for overload resolution (there is no instantiation of
-    # the symbol, so we must process the body now)
-    if not usePseudoGenerics and c.config.ideCmd in {ideSug, ideCon} and not
-        cursorInProc(c.config, n.sons[bodyPos]):
-      discard "speed up nimsuggest"
+  if not isAnon:
+    if sfOverridden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
+    elif s.name.s[0] in {'.', '('}:
+      if s.name.s in [".", ".()", ".="] and {Feature.destructor, dotOperators} * c.features == {}:
+        localError(c.config, n.info, "the overloaded " & s.name.s &
+          " operator has to be enabled with {.experimental: \"dotOperators\".}")
+      elif s.name.s == "()" and callOperator notin c.features:
+        localError(c.config, n.info, "the overloaded " & s.name.s &
+          " operator has to be enabled with {.experimental: \"callOperator\".}")
+
+  if sfBorrow in s.flags and c.config.cmd notin cmdDocLike:
+    result[bodyPos] = c.graph.emptyNode
+
+  if sfCppMember * s.flags != {} and sfWasForwarded notin s.flags:
+    semCppMember(c, s, n)
+
+  if n[bodyPos].kind != nkEmpty and sfError notin s.flags:
+    # for DLL generation we allow sfImportc to have a body, for use in VM
+    if c.config.ideCmd in {ideSug, ideCon} and s.kind notin {skMacro, skTemplate} and not
+        cursorInProc(c.config, n[bodyPos]):
+      # speed up nimsuggest
       if s.kind == skMethod: semMethodPrototype(c, s, n)
+    elif isAnon:
+      let gp = n[genericParamsPos]
+      if gp.kind == nkEmpty or (gp.len == 1 and tfRetType in gp[0].typ.flags):
+        # absolutely no generics (empty) or a single generic return type are
+        # allowed, everything else, including a nullary generic is an error.
+        pushProcCon(c, s)
+        addResult(c, n, s.typ.returnType, skProc)
+        s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], s.typ.returnType))
+        trackProc(c, s, s.ast[bodyPos])
+        popProcCon(c)
+      elif efOperand notin flags:
+        localError(c.config, n.info, errGenericLambdaNotAllowed)
     else:
       pushProcCon(c, s)
-      if n.sons[genericParamsPos].kind == nkEmpty or usePseudoGenerics:
-        if not usePseudoGenerics: paramsTypeCheck(c, s.typ)
-
-        c.p.wasForwarded = proto != nil
+      if n[genericParamsPos].kind == nkEmpty or s.kind in {skMacro, skTemplate}:
+        # Macros and Templates can have generic parameters, but they are only
+        # used for overload resolution (there is no instantiation of the symbol)
+        if s.kind notin {skMacro, skTemplate} and s.magic == mNone: paramsTypeCheck(c, s.typ)
         maybeAddResult(c, s, n)
-        if s.kind == skMethod: semMethodPrototype(c, s, n)
-
-        if lfDynamicLib notin s.loc.flags:
-          # no semantic checking for importc:
-          let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
-          # unfortunately we cannot skip this step when in 'system.compiles'
-          # context as it may even be evaluated in 'system.compiles':
-          n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s)
+        let resultType =
+          if s.kind == skMacro:
+            sysTypeFromName(c.graph, n.info, "NimNode")
+          elif not isInlineIterator(s.typ):
+            s.typ.returnType
+          else:
+            nil
+        # semantic checking also needed with importc in case used in VM
+        s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], resultType))
+        # unfortunately we cannot skip this step when in 'system.compiles'
+        # context as it may even be evaluated in 'system.compiles':
+        trackProc(c, s, s.ast[bodyPos])
       else:
-        if s.typ.sons[0] != nil and kind != skIterator:
-          addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nil, n.info))
+        if (s.typ.returnType != nil and s.kind != skIterator):
+          addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), c.idgen, s, n.info))
 
         openScope(c)
-        n.sons[bodyPos] = semGenericStmt(c, n.sons[bodyPos])
+        n[bodyPos] = semGenericStmt(c, n[bodyPos])
         closeScope(c)
-        fixupInstantiatedSymbols(c, s)
-        if s.kind == skMethod: semMethodPrototype(c, s, n)
-      if sfImportc in s.flags:
-        # so we just ignore the body after semantic checking for importc:
-        n.sons[bodyPos] = c.graph.emptyNode
+        if s.magic == mNone:
+          fixupInstantiatedSymbols(c, s)
+      if s.kind == skMethod: semMethodPrototype(c, s, n)
       popProcCon(c)
   else:
     if s.kind == skMethod: semMethodPrototype(c, s, n)
-    if proto != nil: localError(c.config, n.info, errImplOfXexpected % proto.name.s)
-    if {sfImportc, sfBorrow} * s.flags == {} and s.magic == mNone:
+    if hasProto: localError(c.config, n.info, errImplOfXexpected % proto.name.s)
+    if {sfImportc, sfBorrow, sfError} * s.flags == {} and s.magic == mNone:
+      # this is a forward declaration and we're building the prototype
+      if s.kind in {skProc, skFunc} and s.typ.returnType != nil and s.typ.returnType.kind == tyAnything:
+        localError(c.config, n[paramsPos][0].info, "return type 'auto' cannot be used in forward declarations")
+
       incl(s.flags, sfForward)
+      incl(s.flags, sfWasForwarded)
     elif sfBorrow in s.flags: semBorrow(c, n, s)
   sideEffectsCheck(c, s)
+
   closeScope(c)           # close scope for parameters
   # c.currentScope = oldScope
   popOwner(c)
-  if n.sons[patternPos].kind != nkEmpty:
+  if n[patternPos].kind != nkEmpty:
     c.patterns.add(s)
   if isAnon:
-    n.kind = nkLambda
+    n.transitionSonsKind(nkLambda)
     result.typ = s.typ
-  if isTopLevel(c) and s.kind != skIterator and
-      s.typ.callConv == ccClosure:
+    if optOwnedRefs in c.config.globalOptions:
+      result.typ = makeVarType(c, result.typ, tyOwned)
+  elif isTopLevel(c) and s.kind != skIterator and s.typ.callConv == ccClosure:
     localError(c.config, s.info, "'.closure' calling convention for top level routines is invalid")
 
+  # Prevent double highlights. We already highlighted before.
+  # When not highlighting we still need to allow for suggestions though
+  if not isHighlight:
+    suggestSym(c.graph, s.info, s, c.graph.usageSym)
+
 proc determineType(c: PContext, s: PSym) =
   if s.typ != nil: return
   #if s.magic != mNone: return
   #if s.ast.isNil: return
-  discard semProcAux(c, s.ast, s.kind, {}, stepDetermineType)
+  discard semProcAux(c, s.ast, s.kind, {})
 
 proc semIterator(c: PContext, n: PNode): PNode =
   # gensym'ed iterator?
-  let isAnon = n[namePos].kind == nkEmpty
   if n[namePos].kind == nkSym:
     # gensym'ed iterators might need to become closure iterators:
     n[namePos].sym.owner = getCurrOwner(c)
-    n[namePos].sym.kind = skIterator
+    n[namePos].sym.transitionRoutineSymKind(skIterator)
   result = semProcAux(c, n, skIterator, iteratorPragmas)
   # bug #7093: if after a macro transformation we don't have an
   # nkIteratorDef aynmore, return. The iterator then might have been
   # sem'checked already. (Or not, if the macro skips it.)
   if result.kind != n.kind: return
-  var s = result.sons[namePos].sym
+  var s = result[namePos].sym
   var t = s.typ
-  if t.sons[0] == nil and s.typ.callConv != ccClosure:
+  if t.returnType == nil and s.typ.callConv != ccClosure:
     localError(c.config, n.info, "iterator needs a return type")
-  if isAnon and s.typ.callConv == ccInline:
-    localError(c.config, n.info, "inline iterators are not first-class / cannot be assigned to variables")
   # iterators are either 'inline' or 'closure'; for backwards compatibility,
   # we require first class iterators to be marked with 'closure' explicitly
   # -- at least for 0.9.2.
@@ -1671,14 +2647,19 @@ proc semIterator(c: PContext, n: PNode): PNode =
     incl(s.typ.flags, tfCapturesEnv)
   else:
     s.typ.callConv = ccInline
-  if n.sons[bodyPos].kind == nkEmpty and s.magic == mNone:
+  if n[bodyPos].kind == nkEmpty and s.magic == mNone and c.inConceptDecl == 0:
     localError(c.config, n.info, errImplOfXexpected % s.name.s)
+  if optOwnedRefs in c.config.globalOptions and result.typ != nil:
+    result.typ = makeVarType(c, result.typ, tyOwned)
+    result.typ.callConv = ccClosure
 
 proc semProc(c: PContext, n: PNode): PNode =
   result = semProcAux(c, n, skProc, procPragmas)
 
 proc semFunc(c: PContext, n: PNode): PNode =
-  result = semProcAux(c, n, skFunc, procPragmas)
+  let validPragmas = if n[namePos].kind != nkEmpty: procPragmas
+                     else: lambdaPragmas
+  result = semProcAux(c, n, skFunc, validPragmas)
 
 proc semMethod(c: PContext, n: PNode): PNode =
   if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "method")
@@ -1689,21 +2670,20 @@ proc semMethod(c: PContext, n: PNode): PNode =
   # nkIteratorDef aynmore, return. The iterator then might have been
   # sem'checked already. (Or not, if the macro skips it.)
   if result.kind != nkMethodDef: return
-  var s = result.sons[namePos].sym
+  var s = result[namePos].sym
   # we need to fix the 'auto' return type for the dispatcher here (see tautonotgeneric
   # test case):
   let disp = getDispatcher(s)
   # auto return type?
-  if disp != nil and disp.typ.sons[0] != nil and disp.typ.sons[0].kind == tyExpr:
-    let ret = s.typ.sons[0]
-    disp.typ.sons[0] = ret
+  if disp != nil and disp.typ.returnType != nil and disp.typ.returnType.kind == tyUntyped:
+    let ret = s.typ.returnType
+    disp.typ.setReturnType ret
     if disp.ast[resultPos].kind == nkSym:
-      if isEmptyType(ret): disp.ast.sons[resultPos] = c.graph.emptyNode
+      if isEmptyType(ret): disp.ast[resultPos] = c.graph.emptyNode
       else: disp.ast[resultPos].sym.typ = ret
 
 proc semConverterDef(c: PContext, n: PNode): PNode =
   if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "converter")
-  checkSonsLen(n, bodyPos + 1, c.config)
   result = semProcAux(c, n, skConverter, converterPragmas)
   # macros can transform converters to nothing:
   if namePos >= result.safeLen: return result
@@ -1711,14 +2691,13 @@ proc semConverterDef(c: PContext, n: PNode): PNode =
   # nkIteratorDef aynmore, return. The iterator then might have been
   # sem'checked already. (Or not, if the macro skips it.)
   if result.kind != nkConverterDef: return
-  var s = result.sons[namePos].sym
+  var s = result[namePos].sym
   var t = s.typ
-  if t.sons[0] == nil: localError(c.config, n.info, errXNeedsReturnType % "converter")
-  if sonsLen(t) != 2: localError(c.config, n.info, "a converter takes exactly one argument")
-  addConverter(c, s)
+  if t.returnType == nil: localError(c.config, n.info, errXNeedsReturnType % "converter")
+  if t.len != 2: localError(c.config, n.info, "a converter takes exactly one argument")
+  addConverterDef(c, LazySym(sym: s))
 
 proc semMacroDef(c: PContext, n: PNode): PNode =
-  checkSonsLen(n, bodyPos + 1, c.config)
   result = semProcAux(c, n, skMacro, macroPragmas)
   # macros can transform macros to nothing:
   if namePos >= result.safeLen: return result
@@ -1726,62 +2705,105 @@ proc semMacroDef(c: PContext, n: PNode): PNode =
   # nkIteratorDef aynmore, return. The iterator then might have been
   # sem'checked already. (Or not, if the macro skips it.)
   if result.kind != nkMacroDef: return
-  var s = result.sons[namePos].sym
+  var s = result[namePos].sym
   var t = s.typ
   var allUntyped = true
-  for i in 1 .. t.n.len-1:
-    let param = t.n.sons[i].sym
-    if param.typ.kind != tyExpr: allUntyped = false
+  var nullary = true
+  for i in 1..<t.n.len:
+    let param = t.n[i].sym
+    if param.typ.kind != tyUntyped: allUntyped = false
+    # no default value, parameters required in call
+    if param.ast == nil: nullary = false
   if allUntyped: incl(s.flags, sfAllUntyped)
-  if t.sons[0] == nil: localError(c.config, n.info, "macro needs a return type")
-  if n.sons[bodyPos].kind == nkEmpty:
+  if nullary and n[genericParamsPos].kind == nkEmpty:
+    # macro can be called with alias syntax, remove pushed noalias flag
+    excl(s.flags, sfNoalias)
+  if n[bodyPos].kind == nkEmpty:
     localError(c.config, n.info, errImplOfXexpected % s.name.s)
 
+proc incMod(c: PContext, n: PNode, it: PNode, includeStmtResult: PNode) =
+  var f = checkModuleName(c.config, it)
+  if f != InvalidFileIdx:
+    addIncludeFileDep(c, f)
+    onProcessing(c.graph, f, "include", c.module)
+    if containsOrIncl(c.includedFiles, f.int):
+      localError(c.config, n.info, errRecursiveDependencyX % toMsgFilename(c.config, f))
+    else:
+      includeStmtResult.add semStmt(c, c.graph.includeFileCallback(c.graph, c.module, f), {})
+      excl(c.includedFiles, f.int)
+
 proc evalInclude(c: PContext, n: PNode): PNode =
   result = newNodeI(nkStmtList, n.info)
-  addSon(result, n)
-  for i in countup(0, sonsLen(n) - 1):
-    var f = checkModuleName(c.config, n.sons[i])
-    if f != InvalidFileIDX:
-      if containsOrIncl(c.includedFiles, f.int):
-        localError(c.config, n.info, errRecursiveDependencyX % toFilename(c.config, f))
+  result.add n
+  template checkAs(it: PNode) =
+    if it.kind == nkInfix and it.len == 3:
+      let op = it[0].getPIdent
+      if op != nil and op.id == ord(wAs):
+        localError(c.config, it.info, "Cannot use '" & it[0].renderTree & "' in 'include'.")
+  for i in 0..<n.len:
+    let it = n[i]
+    checkAs(it)
+    if it.kind in {nkInfix, nkPrefix} and it[^1].kind == nkBracket:
+      let lastPos = it.len - 1
+      var imp = copyNode(it)
+      newSons(imp, it.len)
+      for i in 0 ..< lastPos: imp[i] = it[i]
+      imp[lastPos] = imp[0] # dummy entry, replaced in the loop
+      for x in it[lastPos]:
+        checkAs(x)
+        imp[lastPos] = x
+        incMod(c, n, imp, result)
+    else:
+      incMod(c, n, it, result)
+
+proc recursiveSetFlag(n: PNode, flag: TNodeFlag) =
+  if n != nil:
+    for i in 0..<n.safeLen: recursiveSetFlag(n[i], flag)
+    incl(n.flags, flag)
+
+proc semPragmaBlock(c: PContext, n: PNode; expectedType: PType = nil): PNode =
+  checkSonsLen(n, 2, c.config)
+  let pragmaList = n[0]
+  pragma(c, nil, pragmaList, exprPragmas, isStatement = true)
+
+  var inUncheckedAssignSection = 0
+  for p in pragmaList:
+    if whichPragma(p) == wCast:
+      case whichPragma(p[1])
+      of wGcSafe, wNoSideEffect, wTags, wForbids, wRaises:
+        discard "handled in sempass2"
+      of wUncheckedAssign:
+        inUncheckedAssignSection = 1
       else:
-        addSon(result, semStmt(c, c.graph.includeFileCallback(c.graph, c.module, f)))
-        excl(c.includedFiles, f.int)
-
-proc setLine(n: PNode, info: TLineInfo) =
-  for i in 0 ..< safeLen(n): setLine(n.sons[i], info)
-  n.info = info
-
-proc semPragmaBlock(c: PContext, n: PNode): PNode =
-  let pragmaList = n.sons[0]
-  pragma(c, nil, pragmaList, exprPragmas)
-  result = semExpr(c, n.sons[1])
-  n.sons[1] = result
-  for i in 0 ..< pragmaList.len:
-    case whichPragma(pragmaList.sons[i])
-    of wLine: setLine(result, pragmaList.sons[i].info)
-    of wLocks, wGcSafe:
-      result = n
-      result.typ = n.sons[1].typ
-    of wNoRewrite:
-      incl(result.flags, nfNoRewrite)
+        localError(c.config, p.info, "invalid pragma block: " & $p)
+
+  inc c.inUncheckedAssignSection, inUncheckedAssignSection
+  n[1] = semExpr(c, n[1], expectedType = expectedType)
+  dec c.inUncheckedAssignSection, inUncheckedAssignSection
+  result = n
+  result.typ = n[1].typ
+  for i in 0..<pragmaList.len:
+    case whichPragma(pragmaList[i])
+    of wLine: setInfoRecursive(result, pragmaList[i].info)
+    of wNoRewrite: recursiveSetFlag(result, nfNoRewrite)
     else: discard
 
 proc semStaticStmt(c: PContext, n: PNode): PNode =
   #echo "semStaticStmt"
   #writeStackTrace()
   inc c.inStaticContext
-  let a = semStmt(c, n.sons[0])
+  openScope(c)
+  let a = semStmt(c, n[0], {})
+  closeScope(c)
   dec c.inStaticContext
-  n.sons[0] = a
-  evalStaticStmt(c.module, c.graph, a, c.p.owner)
+  n[0] = a
+  evalStaticStmt(c.module, c.idgen, c.graph, a, c.p.owner)
   when false:
     # for incremental replays, keep the AST as required for replays:
     result = n
   else:
     result = newNodeI(nkDiscardStmt, n.info, 1)
-    result.sons[0] = c.graph.emptyNode
+    result[0] = c.graph.emptyNode
 
 proc usesResult(n: PNode): bool =
   # nkStmtList(expr) properly propagates the void context,
@@ -1793,8 +2815,11 @@ proc usesResult(n: PNode): bool =
     elif n.kind == nkReturnStmt:
       result = true
     else:
+      result = false
       for c in n:
         if usesResult(c): return true
+  else:
+    result = false
 
 proc inferConceptStaticParam(c: PContext, inferred, n: PNode) =
   var typ = inferred.typ
@@ -1802,67 +2827,62 @@ proc inferConceptStaticParam(c: PContext, inferred, n: PNode) =
   if not sameType(res.typ, typ.base):
     localError(c.config, n.info,
       "cannot infer the concept parameter '%s', due to a type mismatch. " &
-      "attempt to equate '%s' and '%s'.",
-      [inferred.renderTree, $res.typ, $typ.base])
+      "attempt to equate '%s' and '%s'." % [inferred.renderTree, $res.typ, $typ.base])
   typ.n = res
 
-proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
-  # these must be last statements in a block:
-  const
-    LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
+proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType = nil): PNode =
   result = n
-  result.kind = nkStmtList
-  var length = sonsLen(n)
+  result.transitionSonsKind(nkStmtList)
   var voidContext = false
-  var last = length-1
+  var last = n.len-1
   # by not allowing for nkCommentStmt etc. we ensure nkStmtListExpr actually
   # really *ends* in the expression that produces the type: The compiler now
   # relies on this fact and it's too much effort to change that. And arguably
   #  'R(); #comment' shouldn't produce R's type anyway.
-  #while last > 0 and n.sons[last].kind in {nkPragma, nkCommentStmt,
+  #while last > 0 and n[last].kind in {nkPragma, nkCommentStmt,
   #                                         nkNilLit, nkEmpty}:
   #  dec last
-  for i in countup(0, length - 1):
-    var expr = semExpr(c, n.sons[i], flags)
-    n.sons[i] = expr
-    if c.matchedConcept != nil and expr.typ != nil and
+  for i in 0..<n.len:
+    var x = semExpr(c, n[i], flags, if i == n.len - 1: expectedType else: nil)
+    n[i] = x
+    if c.matchedConcept != nil and x.typ != nil and
         (nfFromTemplate notin n.flags or i != last):
-      case expr.typ.kind
+      case x.typ.kind
       of tyBool:
-        if expr.kind == nkInfix and
-            expr[0].kind == nkSym and
-            expr[0].sym.name.s == "==":
-          if expr[1].typ.isUnresolvedStatic:
-            inferConceptStaticParam(c, expr[1], expr[2])
+        if x.kind == nkInfix and
+            x[0].kind == nkSym and
+            x[0].sym.name.s == "==":
+          if x[1].typ.isUnresolvedStatic:
+            inferConceptStaticParam(c, x[1], x[2])
             continue
-          elif expr[2].typ.isUnresolvedStatic:
-            inferConceptStaticParam(c, expr[2], expr[1])
+          elif x[2].typ.isUnresolvedStatic:
+            inferConceptStaticParam(c, x[2], x[1])
             continue
 
         let verdict = semConstExpr(c, n[i])
-        if verdict.intVal == 0:
+        if verdict == nil or verdict.kind != nkIntLit or verdict.intVal == 0:
           localError(c.config, result.info, "concept predicate failed")
-      of tyUnknown: continue
+      of tyFromExpr: continue
       else: discard
-    if n.sons[i].typ == c.enforceVoidContext: #or usesResult(n.sons[i]):
+    if n[i].typ == c.enforceVoidContext: #or usesResult(n[i]):
       voidContext = true
       n.typ = c.enforceVoidContext
-    if i == last and (length == 1 or efWantValue in flags):
-      n.typ = n.sons[i].typ
-      if not isEmptyType(n.typ): n.kind = nkStmtListExpr
+    if i == last and (n.len == 1 or ({efWantValue, efInTypeof} * flags != {})):
+      n.typ = n[i].typ
+      if not isEmptyType(n.typ): n.transitionSonsKind(nkStmtListExpr)
     elif i != last or voidContext:
-      discardCheck(c, n.sons[i])
+      discardCheck(c, n[i], flags)
     else:
-      n.typ = n.sons[i].typ
-      if not isEmptyType(n.typ): n.kind = nkStmtListExpr
-    if n.sons[i].kind in LastBlockStmts or
-        n.sons[i].kind in nkCallKinds and n.sons[i][0].kind == nkSym and
-        sfNoReturn in n.sons[i][0].sym.flags:
-      for j in countup(i + 1, length - 1):
-        case n.sons[j].kind
-        of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr,
-            nkBlockStmt, nkState: discard
-        else: localError(c.config, n.sons[j].info, "unreachable statement after 'return'")
+      n.typ = n[i].typ
+      if not isEmptyType(n.typ): n.transitionSonsKind(nkStmtListExpr)
+    var m = n[i]
+    while m.kind in {nkStmtListExpr, nkStmtList} and m.len > 0: # from templates
+      m = m.lastSon
+    if endsInNoReturn(m):
+      for j in i + 1..<n.len:
+        case n[j].kind
+        of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkState: discard
+        else: message(c.config, n[j].info, warnUnreachableCode)
     else: discard
 
   if result.len == 1 and
@@ -1871,15 +2891,11 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
      # also, don't make life complicated for macros.
      # they will always expect a proper stmtlist:
      nfBlockArg notin n.flags and
-     result.sons[0].kind != nkDefer:
-    result = result.sons[0]
-
-  when defined(nimfix):
-    if result.kind == nkCommentStmt and not result.comment.isNil and
-        not (result.comment[0] == '#' and result.comment[1] == '#'):
-      # it is an old-style comment statement: we replace it with 'discard ""':
-      prettybase.replaceComment(result.info)
-
-proc semStmt(c: PContext, n: PNode): PNode =
-  # now: simply an alias:
-  result = semExprNoType(c, n)
+     result[0].kind != nkDefer:
+    result = result[0]
+
+proc semStmt(c: PContext, n: PNode; flags: TExprFlags): PNode =
+  if efInTypeof notin flags:
+    result = semExprNoType(c, n)
+  else:
+    result = semExpr(c, n, flags)
diff --git a/compiler/semstrictfuncs.nim b/compiler/semstrictfuncs.nim
new file mode 100644
index 000000000..c54196283
--- /dev/null
+++ b/compiler/semstrictfuncs.nim
@@ -0,0 +1,55 @@
+#

+#

+#           The Nim Compiler

+#        (c) Copyright 2022 Andreas Rumpf

+#

+#    See the file "copying.txt", included in this

+#    distribution, for details about the copyright.

+#

+

+## New "strict funcs" checking. Much simpler and hopefully easier to teach than

+## the old but more advanced algorithm that can/could be found in `varpartitions.nim`.

+

+import ast, typeallowed, renderer

+from aliasanalysis import PathKinds0, PathKinds1

+from trees import getMagic

+

+proc isDangerousLocation*(n: PNode; owner: PSym): bool =

+  var n = n

+  var hasDeref = false

+  while true:

+    case n.kind

+    of nkDerefExpr, nkHiddenDeref:

+      if n[0].typ.kind != tyVar:

+        hasDeref = true

+      n = n[0]

+    of PathKinds0 - {nkDerefExpr, nkHiddenDeref}:

+      n = n[0]

+    of PathKinds1:

+      n = n[1]

+    of nkCallKinds:

+      if n.len > 1:

+        if (n.typ != nil and classifyViewType(n.typ) != noView) or getMagic(n) == mSlice:

+          # borrow from first parameter:

+          n = n[1]

+        else:

+          break

+      else:

+        break

+    else:

+      break

+  if n.kind == nkSym:

+    # dangerous if contains a pointer deref or if it doesn't belong to us:

+    result = hasDeref or n.sym.owner != owner

+    when false:

+      # store to something that belongs to a `var` parameter is fine:

+      let s = n.sym

+      if s.kind == skParam:

+        # dangerous unless a `var T` parameter:

+        result = s.typ.kind != tyVar

+      else:

+        # dangerous if contains a pointer deref or if it doesn't belong to us:

+        result = hasDeref or s.owner != owner

+  else:

+    # dangerous if it contains a pointer deref

+    result = hasDeref

diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index 75c6bc4bb..817cb6249 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -34,9 +34,10 @@ type
     spNone, spGenSym, spInject
 
 proc symBinding(n: PNode): TSymBinding =
-  for i in countup(0, sonsLen(n) - 1):
-    var it = n.sons[i]
-    var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
+  result = spNone
+  for i in 0..<n.len:
+    var it = n[i]
+    var key = if it.kind == nkExprColonExpr: it[0] else: it
     if key.kind == nkIdent:
       case whichKeyword(key.ident)
       of wGensym: return spGenSym
@@ -47,10 +48,11 @@ type
   TSymChoiceRule = enum
     scClosed, scOpen, scForceOpen
 
-proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode =
+proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule;
+               isField = false): PNode =
   var
     a: PSym
-    o: TOverloadIter
+    o: TOverloadIter = default(TOverloadIter)
   var i = 0
   a = initOverloadIter(o, c, n)
   while a != nil:
@@ -58,28 +60,39 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode =
       inc(i)
       if i > 1: break
     a = nextOverloadIter(o, c, n)
+  let info = getCallLineInfo(n)
   if i <= 1 and r != scForceOpen:
     # XXX this makes more sense but breaks bootstrapping for now:
     # (s.kind notin routineKinds or s.magic != mNone):
     # for instance 'nextTry' is both in tables.nim and astalgo.nim ...
-    result = newSymNode(s, n.info)
-    markUsed(c.config, n.info, s, c.graph.usageSym)
+    if not isField or sfGenSym notin s.flags:
+      result = newSymNode(s, info)
+      markUsed(c, info, s)
+      onUse(info, s)
+    else:
+      result = n
+  elif i == 0:
+    # forced open but symbol not in scope, retain information
+    result = n
   else:
     # semantic checking requires a type; ``fitNode`` deals with it
     # appropriately
     let kind = if r == scClosed or n.kind == nkDotExpr: nkClosedSymChoice
                else: nkOpenSymChoice
-    result = newNodeIT(kind, n.info, newTypeS(tyNone, c))
+    result = newNodeIT(kind, info, newTypeS(tyNone, c))
     a = initOverloadIter(o, c, n)
     while a != nil:
-      if a.kind != skModule:
+      if a.kind != skModule and (not isField or sfGenSym notin a.flags):
         incl(a.flags, sfUsed)
-        addSon(result, newSymNode(a, n.info))
+        markOwnerModuleAsUsed(c, a)
+        result.add newSymNode(a, info)
+        onUse(info, a)
       a = nextOverloadIter(o, c, n)
 
 proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode =
-  for i in 0 ..< n.len:
-    var a = n.sons[i]
+  result = copyNode(n)
+  for i in 0..<n.len:
+    var a = n[i]
     # If 'a' is an overloaded symbol, we used to use the first symbol
     # as a 'witness' and use the fact that subsequent lookups will yield
     # the same symbol!
@@ -91,21 +104,25 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode =
       let sc = symChoice(c, n, s, scClosed)
       if sc.kind == nkSym:
         toBind.incl(sc.sym.id)
+        result.add sc
       else:
-        for x in items(sc): toBind.incl(x.sym.id)
+        for x in items(sc):
+          toBind.incl(x.sym.id)
+          result.add x
     else:
       illFormedAst(a, c.config)
-  result = newNodeI(nkEmpty, n.info)
 
 proc semMixinStmt(c: PContext, n: PNode, toMixin: var IntSet): PNode =
-  for i in 0 ..< n.len:
-    toMixin.incl(considerQuotedIdent(c, n.sons[i]).id)
-  result = newNodeI(nkEmpty, n.info)
+  result = copyNode(n)
+  for i in 0..<n.len:
+    toMixin.incl(considerQuotedIdent(c, n[i]).id)
+    let x = symChoice(c, n[i], nil, scForceOpen)
+    result.add x
 
 proc replaceIdentBySym(c: PContext; n: var PNode, s: PNode) =
   case n.kind
-  of nkPostfix: replaceIdentBySym(c, n.sons[1], s)
-  of nkPragmaExpr: replaceIdentBySym(c, n.sons[0], s)
+  of nkPostfix: replaceIdentBySym(c, n[1], s)
+  of nkPragmaExpr: replaceIdentBySym(c, n[0], s)
   of nkIdent, nkAccQuoted, nkSym: n = s
   else: illFormedAst(n, c.config)
 
@@ -116,28 +133,35 @@ type
     owner: PSym
     cursorInBody: bool # only for nimsuggest
     scopeN: int
+    noGenSym: int
+    inTemplateHeader: int
 
-template withBracketExpr(ctx, x, body: untyped) =
-  body
+proc isTemplParam(c: TemplCtx, s: PSym): bool {.inline.} =
+  result = s.kind == skParam and
+           s.owner == c.owner and sfTemplateParam in s.flags
 
-proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
+proc getIdentReplaceParams(c: var TemplCtx, n: var PNode): tuple[node: PNode, hasParam: bool] =
   case n.kind
-  of nkPostfix: result = getIdentNode(c, n.sons[1])
-  of nkPragmaExpr: result = getIdentNode(c, n.sons[0])
+  of nkPostfix: result = getIdentReplaceParams(c, n[1])
+  of nkPragmaExpr: result = getIdentReplaceParams(c, n[0])
   of nkIdent:
-    result = n
+    result = (n, false)
     let s = qualifiedLookUp(c.c, n, {})
-    if s != nil:
-      if s.owner == c.owner and s.kind == skParam:
-        result = newSymNode(s, n.info)
-  of nkAccQuoted, nkSym: result = n
+    if s != nil and isTemplParam(c, s):
+      n = newSymNode(s, n.info)
+      result = (n, true)
+  of nkSym:
+    result = (n, isTemplParam(c, n.sym))
+  of nkAccQuoted:
+    result = (n, false)
+    for i in 0..<n.safeLen:
+      let (ident, hasParam) = getIdentReplaceParams(c, n[i])
+      if hasParam:
+        result.node[i] = ident
+        result.hasParam = true
   else:
     illFormedAst(n, c.c.config)
-    result = n
-
-proc isTemplParam(c: TemplCtx, n: PNode): bool {.inline.} =
-  result = n.kind == nkSym and n.sym.kind == skParam and
-           n.sym.owner == c.owner and sfGenSym notin n.sym.flags
+    result = (n, false)
 
 proc semTemplBody(c: var TemplCtx, n: PNode): PNode
 
@@ -152,156 +176,201 @@ proc semTemplBodyScope(c: var TemplCtx, n: PNode): PNode =
   result = semTemplBody(c, n)
   closeScope(c)
 
-proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode =
-  result = n
-  if n.kind == nkIdent:
-    let s = qualifiedLookUp(c.c, n, {})
-    if s != nil:
-      if s.owner == c.owner and s.kind == skParam:
-        incl(s.flags, sfUsed)
-        result = newSymNode(s, n.info)
-        styleCheckUse(n.info, s)
-  else:
-    for i in 0 ..< n.safeLen:
-      result.sons[i] = onlyReplaceParams(c, n.sons[i])
-
 proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym =
-  result = newSym(kind, considerQuotedIdent(c.c, n), c.owner, n.info)
+  result = newSym(kind, considerQuotedIdent(c.c, n), c.c.idgen, c.owner, n.info)
   incl(result.flags, sfGenSym)
   incl(result.flags, sfShadowed)
 
 proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
-  # locals default to 'gensym':
-  if n.kind == nkPragmaExpr and symBinding(n.sons[1]) == spInject:
+  # locals default to 'gensym', fields default to 'inject':
+  if (n.kind == nkPragmaExpr and symBinding(n[1]) == spInject) or
+      k == skField:
     # even if injected, don't produce a sym choice here:
     #n = semTemplBody(c, n)
-    var x = n[0]
-    while true:
-      case x.kind
-      of nkPostfix: x = x[1]
-      of nkPragmaExpr: x = x[0]
-      of nkIdent: break
-      of nkAccQuoted:
-        # consider:  type `T TemplParam` {.inject.}
-        # it suffices to return to treat it like 'inject':
-        n = onlyReplaceParams(c, n)
-        return
-      else:
-        illFormedAst(x, c.c.config)
-    let ident = getIdentNode(c, x)
-    if not isTemplParam(c, ident):
-      c.toInject.incl(x.ident.id)
-    else:
-      replaceIdentBySym(c.c, n, ident)
+    let (ident, hasParam) = getIdentReplaceParams(c, n)
+    if not hasParam:
+      if k != skField:
+        c.toInject.incl(considerQuotedIdent(c.c, ident).id)
   else:
-    let ident = getIdentNode(c, n)
-    if not isTemplParam(c, ident):
-      # fix #2670, consider:
-      #
-      # when b:
-      #    var a = "hi"
-      # else:
-      #    var a = 5
-      # echo a
-      #
-      # We need to ensure that both 'a' produce the same gensym'ed symbol.
-      # So we need only check the *current* scope.
-      let s = localSearchInScope(c.c, considerQuotedIdent(c.c, ident))
-      if s != nil and s.owner == c.owner and sfGenSym in s.flags:
-        styleCheckUse(n.info, s)
-        replaceIdentBySym(c.c, n, newSymNode(s, n.info))
-      else:
+    if (n.kind == nkPragmaExpr and n.len >= 2 and n[1].kind == nkPragma):
+      let pragmaNode = n[1]
+      for i in 0..<pragmaNode.len:
+        let ni = pragmaNode[i]
+        # see D20210801T100514
+        var found = false
+        if ni.kind == nkIdent:
+          for a in templatePragmas:
+            if ni.ident.id == ord(a):
+              found = true
+              break
+        if not found:
+          openScope(c)
+          pragmaNode[i] = semTemplBody(c, pragmaNode[i])
+          closeScope(c)
+    let (ident, hasParam) = getIdentReplaceParams(c, n)
+    if not hasParam:
+      if n.kind != nkSym and not (n.kind == nkIdent and n.ident.id == ord(wUnderscore)):
         let local = newGenSym(k, ident, c)
         addPrelimDecl(c.c, local)
-        styleCheckDef(c.c.config, n.info, local)
+        styleCheckDef(c.c, n.info, local)
+        onDef(n.info, local)
         replaceIdentBySym(c.c, n, newSymNode(local, n.info))
-    else:
-      replaceIdentBySym(c.c, n, ident)
+        if k == skParam and c.inTemplateHeader > 0:
+          local.flags.incl sfTemplateParam
 
-proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode =
+proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField, isAmbiguous: bool): PNode =
   incl(s.flags, sfUsed)
-  # we do not call styleCheckUse here, as the identifier is not really
+  # bug #12885; ideally sem'checking is performed again afterwards marking
+  # the symbol as used properly, but the nfSem mechanism currently prevents
+  # that from happening, so we mark the module as used here already:
+  markOwnerModuleAsUsed(c.c, s)
+  # we do not call onUse here, as the identifier is not really
   # resolved here. We will fixup the used identifiers later.
   case s.kind
   of skUnknown:
     # Introduced in this pass! Leave it as an identifier.
     result = n
   of OverloadableSyms:
-    result = symChoice(c, n, s, scOpen)
+    result = symChoice(c.c, n, s, scOpen, isField)
+    if not isField and result.kind in {nkSym, nkOpenSymChoice}:
+      if openSym in c.c.features:
+        if result.kind == nkSym:
+          result = newOpenSym(result)
+        else:
+          result.typ = nil
+      else:
+        result.flags.incl nfDisabledOpenSym
+        result.typ = nil
   of skGenericParam:
-    result = newSymNodeTypeDesc(s, n.info)
+    if isField and sfGenSym in s.flags: result = n
+    else:
+      result = newSymNodeTypeDesc(s, c.c.idgen, n.info)
+      if not isField and s.owner != c.owner:
+        if openSym in c.c.features:
+          result = newOpenSym(result)
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
   of skParam:
     result = n
   of skType:
-    result = newSymNodeTypeDesc(s, n.info)
+    if isField and sfGenSym in s.flags: result = n
+    else:
+      if isAmbiguous:
+        # ambiguous types should be symchoices since lookup behaves
+        # differently for them in regular expressions
+        result = symChoice(c.c, n, s, scOpen, isField)
+      else: result = newSymNodeTypeDesc(s, c.c.idgen, n.info)
+      if not isField and not (s.owner == c.owner and
+          s.typ != nil and s.typ.kind == tyGenericParam) and
+          result.kind in {nkSym, nkOpenSymChoice}:
+        if openSym in c.c.features:
+          if result.kind == nkSym:
+            result = newOpenSym(result)
+          else:
+            result.typ = nil
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
   else:
-    result = newSymNode(s, n.info)
+    if isField and sfGenSym in s.flags: result = n
+    else:
+      result = newSymNode(s, n.info)
+      if not isField:
+        if openSym in c.c.features:
+          result = newOpenSym(result)
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
+    # Issue #12832
+    when defined(nimsuggest):
+      suggestSym(c.c.graph, n.info, s, c.c.graph.usageSym, false)
+    # field access (dot expr) will be handled by builtinFieldAccess
+    if not isField:
+      styleCheckUse(c.c, n.info, s)
 
-proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode =
+proc semRoutineInTemplName(c: var TemplCtx, n: PNode, explicitInject: bool): PNode =
   result = n
   if n.kind == nkIdent:
     let s = qualifiedLookUp(c.c, n, {})
     if s != nil:
-      if s.owner == c.owner and (s.kind == skParam or sfGenSym in s.flags):
+      if s.owner == c.owner and (s.kind == skParam or
+          (sfGenSym in s.flags and not explicitInject)):
         incl(s.flags, sfUsed)
         result = newSymNode(s, n.info)
-        styleCheckUse(n.info, s)
+        onUse(n.info, s)
   else:
-    for i in countup(0, safeLen(n) - 1):
-      result.sons[i] = semRoutineInTemplName(c, n.sons[i])
+    for i in 0..<n.safeLen:
+      result[i] = semRoutineInTemplName(c, n[i], explicitInject)
 
 proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
   result = n
   checkSonsLen(n, bodyPos + 1, c.c.config)
-  # routines default to 'inject':
-  if n.kind notin nkLambdaKinds and symBinding(n.sons[pragmasPos]) == spGenSym:
-    let ident = getIdentNode(c, n.sons[namePos])
-    if not isTemplParam(c, ident):
-      var s = newGenSym(k, ident, c)
-      s.ast = n
-      addPrelimDecl(c.c, s)
-      styleCheckDef(c.c.config, n.info, s)
-      n.sons[namePos] = newSymNode(s, n.sons[namePos].info)
+  if n.kind notin nkLambdaKinds:
+    # routines default to 'inject':
+    let binding = symBinding(n[pragmasPos])
+    if binding == spGenSym:
+      let (ident, hasParam) = getIdentReplaceParams(c, n[namePos])
+      if not hasParam:
+        var s = newGenSym(k, ident, c)
+        s.ast = n
+        addPrelimDecl(c.c, s)
+        styleCheckDef(c.c, n.info, s)
+        onDef(n.info, s)
+        n[namePos] = newSymNode(s, n[namePos].info)
+      else:
+        n[namePos] = ident
     else:
-      n.sons[namePos] = ident
-  else:
-    n.sons[namePos] = semRoutineInTemplName(c, n.sons[namePos])
+      n[namePos] = semRoutineInTemplName(c, n[namePos], binding == spInject)
   # open scope for parameters
   openScope(c)
-  for i in patternPos..miscPos:
-    n.sons[i] = semTemplBody(c, n.sons[i])
+  for i in patternPos..paramsPos-1:
+    n[i] = semTemplBody(c, n[i])
+
+  if k == skTemplate: inc(c.inTemplateHeader)
+  n[paramsPos] = semTemplBody(c, n[paramsPos])
+  if k == skTemplate: dec(c.inTemplateHeader)
+
+  for i in paramsPos+1..miscPos:
+    n[i] = semTemplBody(c, n[i])
   # open scope for locals
   inc c.scopeN
   openScope(c)
-  n.sons[bodyPos] = semTemplBody(c, n.sons[bodyPos])
+  n[bodyPos] = semTemplBody(c, n[bodyPos])
   # close scope for locals
   closeScope(c)
   dec c.scopeN
   # close scope for parameters
   closeScope(c)
 
-proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind; start=0) =
-  for i in countup(start, sonsLen(n) - 1):
-    var a = n.sons[i]
-    if a.kind == nkCommentStmt: continue
-    if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a, c.c.config)
-    checkMinSonsLen(a, 3, c.c.config)
-    var L = sonsLen(a)
-    when defined(nimsuggest):
-      inc c.c.inTypeContext
-    a.sons[L-2] = semTemplBody(c, a.sons[L-2])
-    when defined(nimsuggest):
-      dec c.c.inTypeContext
-    a.sons[L-1] = semTemplBody(c, a.sons[L-1])
-    for j in countup(0, L-3):
-      addLocalDecl(c, a.sons[j], symKind)
+proc semTemplIdentDef(c: var TemplCtx, a: PNode, symKind: TSymKind) =
+  checkMinSonsLen(a, 3, c.c.config)
+  when defined(nimsuggest):
+    inc c.c.inTypeContext
+  a[^2] = semTemplBody(c, a[^2])
+  when defined(nimsuggest):
+    dec c.c.inTypeContext
+  a[^1] = semTemplBody(c, a[^1])
+  for j in 0..<a.len-2:
+    addLocalDecl(c, a[j], symKind)
+
+proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind; start = 0) =
+  for i in start..<n.len:
+    var a = n[i]
+    case a.kind:
+    of nkCommentStmt: continue
+    of nkIdentDefs, nkVarTuple, nkConstDef:
+      semTemplIdentDef(c, a, symKind)
+    else:
+      illFormedAst(a, c.c.config)
+
 
-proc semPattern(c: PContext, n: PNode): PNode
+proc semPattern(c: PContext, n: PNode; s: PSym): PNode
 
 proc semTemplBodySons(c: var TemplCtx, n: PNode): PNode =
   result = n
-  for i in 0 ..< n.len:
-    result.sons[i] = semTemplBody(c, n.sons[i])
+  for i in 0..<n.len:
+    result[i] = semTemplBody(c, n[i])
 
 proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
   result = n
@@ -309,26 +378,29 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
   case n.kind
   of nkIdent:
     if n.ident.id in c.toInject: return n
+    c.c.isAmbiguous = false
     let s = qualifiedLookUp(c.c, n, {})
     if s != nil:
-      if s.owner == c.owner and s.kind == skParam:
+      if s.owner == c.owner and s.kind == skParam and sfTemplateParam in s.flags:
         incl(s.flags, sfUsed)
         result = newSymNode(s, n.info)
-        styleCheckUse(n.info, s)
+        onUse(n.info, s)
       elif contains(c.toBind, s.id):
-        result = symChoice(c.c, n, s, scClosed)
+        result = symChoice(c.c, n, s, scClosed, c.noGenSym > 0)
       elif contains(c.toMixin, s.name.id):
-        result = symChoice(c.c, n, s, scForceOpen)
-      elif s.owner == c.owner and sfGenSym in s.flags:
+        result = symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0)
+      elif s.owner == c.owner and sfGenSym in s.flags and c.noGenSym == 0:
         # template tmp[T](x: var seq[T]) =
         # var yz: T
         incl(s.flags, sfUsed)
         result = newSymNode(s, n.info)
-        styleCheckUse(n.info, s)
+        onUse(n.info, s)
       else:
-        result = semTemplSymbol(c.c, n, s)
+        if s.kind in {skVar, skLet, skConst}:
+          discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule})
+        result = semTemplSymbol(c, n, s, c.noGenSym > 0, c.c.isAmbiguous)
   of nkBind:
-    result = semTemplBody(c, n.sons[0])
+    result = semTemplBody(c, n[0])
   of nkBindStmt:
     result = semBindStmt(c.c, n, c.toBind)
   of nkMixinStmt:
@@ -337,105 +409,117 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
   of nkEmpty, nkSym..nkNilLit, nkComesFrom:
     discard
   of nkIfStmt:
-    for i in countup(0, sonsLen(n)-1):
-      var it = n.sons[i]
+    for i in 0..<n.len:
+      var it = n[i]
       if it.len == 2:
-        when newScopeForIf: openScope(c)
-        it.sons[0] = semTemplBody(c, it.sons[0])
-        when not newScopeForIf: openScope(c)
-        it.sons[1] = semTemplBody(c, it.sons[1])
+        openScope(c)
+        it[0] = semTemplBody(c, it[0])
+        it[1] = semTemplBody(c, it[1])
         closeScope(c)
       else:
-        n.sons[i] = semTemplBodyScope(c, it)
+        n[i] = semTemplBodyScope(c, it)
   of nkWhileStmt:
     openScope(c)
-    for i in countup(0, sonsLen(n)-1):
-      n.sons[i] = semTemplBody(c, n.sons[i])
+    for i in 0..<n.len:
+      n[i] = semTemplBody(c, n[i])
     closeScope(c)
   of nkCaseStmt:
     openScope(c)
-    n.sons[0] = semTemplBody(c, n.sons[0])
-    for i in countup(1, sonsLen(n)-1):
-      var a = n.sons[i]
+    n[0] = semTemplBody(c, n[0])
+    for i in 1..<n.len:
+      var a = n[i]
       checkMinSonsLen(a, 1, c.c.config)
-      var L = sonsLen(a)
-      for j in countup(0, L-2):
-        a.sons[j] = semTemplBody(c, a.sons[j])
-      a.sons[L-1] = semTemplBodyScope(c, a.sons[L-1])
+      for j in 0..<a.len-1:
+        a[j] = semTemplBody(c, a[j])
+      a[^1] = semTemplBodyScope(c, a[^1])
     closeScope(c)
   of nkForStmt, nkParForStmt:
-    var L = sonsLen(n)
     openScope(c)
-    n.sons[L-2] = semTemplBody(c, n.sons[L-2])
-    for i in countup(0, L - 3):
-      addLocalDecl(c, n.sons[i], skForVar)
+    n[^2] = semTemplBody(c, n[^2])
+    for i in 0..<n.len - 2:
+      if n[i].kind == nkVarTuple:
+        for j in 0..<n[i].len-1:
+          addLocalDecl(c, n[i][j], skForVar)
+      else:
+        addLocalDecl(c, n[i], skForVar)
     openScope(c)
-    n.sons[L-1] = semTemplBody(c, n.sons[L-1])
+    n[^1] = semTemplBody(c, n[^1])
     closeScope(c)
     closeScope(c)
   of nkBlockStmt, nkBlockExpr, nkBlockType:
     checkSonsLen(n, 2, c.c.config)
     openScope(c)
-    if n.sons[0].kind != nkEmpty:
-      addLocalDecl(c, n.sons[0], skLabel)
+    if n[0].kind != nkEmpty:
+      addLocalDecl(c, n[0], skLabel)
       when false:
         # labels are always 'gensym'ed:
-        let s = newGenSym(skLabel, n.sons[0], c)
+        let s = newGenSym(skLabel, n[0], c)
         addPrelimDecl(c.c, s)
-        styleCheckDef(c.c.config, s)
-        n.sons[0] = newSymNode(s, n.sons[0].info)
-    n.sons[1] = semTemplBody(c, n.sons[1])
+        styleCheckDef(c.c, s)
+        onDef(n[0].info, s)
+        n[0] = newSymNode(s, n[0].info)
+    n[1] = semTemplBody(c, n[1])
     closeScope(c)
-  of nkTryStmt:
+  of nkTryStmt, nkHiddenTryStmt:
     checkMinSonsLen(n, 2, c.c.config)
-    n.sons[0] = semTemplBodyScope(c, n.sons[0])
-    for i in countup(1, sonsLen(n)-1):
-      var a = n.sons[i]
+    n[0] = semTemplBodyScope(c, n[0])
+    for i in 1..<n.len:
+      var a = n[i]
       checkMinSonsLen(a, 1, c.c.config)
-      var L = sonsLen(a)
       openScope(c)
-      for j in countup(0, L-2):
-        if a.sons[j].isInfixAs():
-          addLocalDecl(c, a.sons[j].sons[2], skLet)
-          a.sons[j].sons[1] = semTemplBody(c, a.sons[j][1])
+      for j in 0..<a.len-1:
+        if a[j].isInfixAs():
+          addLocalDecl(c, a[j][2], skLet)
+          a[j][1] = semTemplBody(c, a[j][1])
         else:
-          a.sons[j] = semTemplBody(c, a.sons[j])
-      a.sons[L-1] = semTemplBodyScope(c, a.sons[L-1])
+          a[j] = semTemplBody(c, a[j])
+      a[^1] = semTemplBodyScope(c, a[^1])
       closeScope(c)
   of nkVarSection: semTemplSomeDecl(c, n, skVar)
   of nkLetSection: semTemplSomeDecl(c, n, skLet)
   of nkFormalParams:
     checkMinSonsLen(n, 1, c.c.config)
-    n.sons[0] = semTemplBody(c, n.sons[0])
     semTemplSomeDecl(c, n, skParam, 1)
-  of nkConstSection:
-    for i in countup(0, sonsLen(n) - 1):
-      var a = n.sons[i]
-      if a.kind == nkCommentStmt: continue
-      if (a.kind != nkConstDef): illFormedAst(a, c.c.config)
-      checkSonsLen(a, 3, c.c.config)
-      addLocalDecl(c, a.sons[0], skConst)
-      a.sons[1] = semTemplBody(c, a.sons[1])
-      a.sons[2] = semTemplBody(c, a.sons[2])
+    n[0] = semTemplBody(c, n[0])
+  of nkConstSection: semTemplSomeDecl(c, n, skConst)
   of nkTypeSection:
-    for i in countup(0, sonsLen(n) - 1):
-      var a = n.sons[i]
+    for i in 0..<n.len:
+      var a = n[i]
       if a.kind == nkCommentStmt: continue
       if (a.kind != nkTypeDef): illFormedAst(a, c.c.config)
       checkSonsLen(a, 3, c.c.config)
-      addLocalDecl(c, a.sons[0], skType)
-    for i in countup(0, sonsLen(n) - 1):
-      var a = n.sons[i]
+      addLocalDecl(c, a[0], skType)
+    for i in 0..<n.len:
+      var a = n[i]
       if a.kind == nkCommentStmt: continue
       if (a.kind != nkTypeDef): illFormedAst(a, c.c.config)
       checkSonsLen(a, 3, c.c.config)
-      if a.sons[1].kind != nkEmpty:
+      if a[1].kind != nkEmpty:
         openScope(c)
-        a.sons[1] = semTemplBody(c, a.sons[1])
-        a.sons[2] = semTemplBody(c, a.sons[2])
+        a[1] = semTemplBody(c, a[1])
+        a[2] = semTemplBody(c, a[2])
         closeScope(c)
       else:
-        a.sons[2] = semTemplBody(c, a.sons[2])
+        a[2] = semTemplBody(c, a[2])
+  of nkObjectTy:
+    openScope(c)
+    result = semTemplBodySons(c, n)
+    closeScope(c)
+  of nkRecList:
+    for i in 0..<n.len:
+      var a = n[i]
+      case a.kind:
+      of nkCommentStmt, nkNilLit, nkSym, nkEmpty: continue
+      of nkIdentDefs:
+        semTemplIdentDef(c, a, skField)
+      of nkRecCase, nkRecWhen:
+        n[i] = semTemplBody(c, a)
+      else:
+        illFormedAst(a, c.c.config)
+  of nkRecCase:
+    semTemplIdentDef(c, n[0], skField)
+    for i in 1..<n.len:
+      n[i] = semTemplBody(c, n[i])
   of nkProcDef, nkLambdaKinds:
     result = semRoutineInTemplBody(c, n, skProc)
   of nkFuncDef:
@@ -451,68 +535,109 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
   of nkConverterDef:
     result = semRoutineInTemplBody(c, n, skConverter)
   of nkPragmaExpr:
-    result.sons[0] = semTemplBody(c, n.sons[0])
+    result[0] = semTemplBody(c, n[0])
   of nkPostfix:
-    result.sons[1] = semTemplBody(c, n.sons[1])
+    result[1] = semTemplBody(c, n[1])
   of nkPragma:
     for x in n:
       if x.kind == nkExprColonExpr:
-        x.sons[1] = semTemplBody(c, x.sons[1])
+        x[1] = semTemplBody(c, x[1])
   of nkBracketExpr:
-    result = newNodeI(nkCall, n.info)
-    result.add newIdentNode(getIdent(c.c.cache, "[]"), n.info)
-    for i in 0 ..< n.len: result.add(n[i])
-    let n0 = semTemplBody(c, n.sons[0])
-    withBracketExpr c, n0:
-      result = semTemplBodySons(c, result)
+    if n.typ == nil:
+      # if a[b] is nested inside a typed expression, don't convert it
+      # back to `[]`(a, b), prepareOperand will not typecheck it again
+      # and so `[]` will not be resolved
+      # checking if a[b] is typed should be enough to cover this case
+      result = newNodeI(nkCall, n.info)
+      result.add newIdentNode(getIdent(c.c.cache, "[]"), n.info)
+      for i in 0..<n.len: result.add(n[i])
+    result = semTemplBodySons(c, result)
   of nkCurlyExpr:
-    result = newNodeI(nkCall, n.info)
-    result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info)
-    for i in 0 ..< n.len: result.add(n[i])
+    if n.typ == nil:
+      # see nkBracketExpr case for explanation
+      result = newNodeI(nkCall, n.info)
+      result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info)
+      for i in 0..<n.len: result.add(n[i])
     result = semTemplBodySons(c, result)
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     checkSonsLen(n, 2, c.c.config)
-    let a = n.sons[0]
-    let b = n.sons[1]
+    let a = n[0]
+    let b = n[1]
 
     let k = a.kind
     case k
     of nkBracketExpr:
-      result = newNodeI(nkCall, n.info)
-      result.add newIdentNode(getIdent(c.c.cache, "[]="), n.info)
-      for i in 0 ..< a.len: result.add(a[i])
-      result.add(b)
-      let a0 = semTemplBody(c, a.sons[0])
-      withBracketExpr c, a0:
-        result = semTemplBodySons(c, result)
+      if a.typ == nil:
+        # see nkBracketExpr case above for explanation
+        result = newNodeI(nkCall, n.info)
+        result.add newIdentNode(getIdent(c.c.cache, "[]="), n.info)
+        for i in 0..<a.len: result.add(a[i])
+        result.add(b)
+      let a0 = semTemplBody(c, a[0])
+      result = semTemplBodySons(c, result)
     of nkCurlyExpr:
-      result = newNodeI(nkCall, n.info)
-      result.add newIdentNode(getIdent(c.c.cache, "{}="), n.info)
-      for i in 0 ..< a.len: result.add(a[i])
-      result.add(b)
+      if a.typ == nil:
+        # see nkBracketExpr case above for explanation
+        result = newNodeI(nkCall, n.info)
+        result.add newIdentNode(getIdent(c.c.cache, "{}="), n.info)
+        for i in 0..<a.len: result.add(a[i])
+        result.add(b)
       result = semTemplBodySons(c, result)
     else:
       result = semTemplBodySons(c, n)
   of nkCallKinds-{nkPostfix}:
-    result = semTemplBodySons(c, n)
+    # do not transform runnableExamples (bug #9143)
+    if not isRunnableExamples(n[0]):
+      result = semTemplBodySons(c, n)
   of nkDotExpr, nkAccQuoted:
     # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
     # so we use the generic code for nkDotExpr too
+    c.c.isAmbiguous = false
     let s = qualifiedLookUp(c.c, n, {})
     if s != nil:
+      # mirror the nkIdent case
       # do not symchoice a quoted template parameter (bug #2390):
       if s.owner == c.owner and s.kind == skParam and
           n.kind == nkAccQuoted and n.len == 1:
         incl(s.flags, sfUsed)
-        styleCheckUse(n.info, s)
+        onUse(n.info, s)
         return newSymNode(s, n.info)
       elif contains(c.toBind, s.id):
-        return symChoice(c.c, n, s, scClosed)
+        return symChoice(c.c, n, s, scClosed, c.noGenSym > 0)
       elif contains(c.toMixin, s.name.id):
-        return symChoice(c.c, n, s, scForceOpen)
+        return symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0)
       else:
-        return symChoice(c.c, n, s, scOpen)
-    result = semTemplBodySons(c, n)
+        if s.kind in {skVar, skLet, skConst}:
+          discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule})
+        return semTemplSymbol(c, n, s, c.noGenSym > 0, c.c.isAmbiguous)
+    if n.kind == nkDotExpr:
+      result = n
+      result[0] = semTemplBody(c, n[0])
+      inc c.noGenSym
+      result[1] = semTemplBody(c, n[1])
+      dec c.noGenSym
+      if result[1].kind == nkSym and result[1].sym.kind in routineKinds:
+        # prevent `dotTransformation` from rewriting this node to `nkIdent`
+        # by making it a symchoice
+        # in generics this becomes `nkClosedSymChoice` but this breaks code
+        # as the old behavior here was that this became `nkIdent`
+        var choice = newNodeIT(nkOpenSymChoice, n[1].info, newTypeS(tyNone, c.c))
+        choice.add result[1]
+        result[1] = choice
+    else:
+      result = semTemplBodySons(c, n)
+  of nkExprColonExpr, nkExprEqExpr:
+    if n.len == 2:
+      inc c.noGenSym
+      result[0] = semTemplBody(c, n[0])
+      dec c.noGenSym
+      result[1] = semTemplBody(c, n[1])
+    else:
+      result = semTemplBodySons(c, n)
+  of nkTableConstr:
+    # also transform the keys (bug #12595)
+    for i in 0..<n.len:
+      result[i] = semTemplBodySons(c, n[i])
   else:
     result = semTemplBodySons(c, n)
 
@@ -528,7 +653,7 @@ proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
       elif contains(c.toBind, s.id):
         result = symChoice(c.c, n, s, scClosed)
   of nkBind:
-    result = semTemplBodyDirty(c, n.sons[0])
+    result = semTemplBodyDirty(c, n[0])
   of nkBindStmt:
     result = semBindStmt(c.c, n, c.toBind)
   of nkEmpty, nkSym..nkNilLit, nkComesFrom:
@@ -541,87 +666,118 @@ proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
       if s != nil and contains(c.toBind, s.id):
         return symChoice(c.c, n, s, scClosed)
     result = n
-    for i in countup(0, sonsLen(n) - 1):
-      result.sons[i] = semTemplBodyDirty(c, n.sons[i])
+    for i in 0..<n.len:
+      result[i] = semTemplBodyDirty(c, n[i])
+
+# in semstmts.nim:
+proc semProcAnnotation(c: PContext, prc: PNode; validPragmas: TSpecialWords): PNode
 
 proc semTemplateDef(c: PContext, n: PNode): PNode =
+  result = semProcAnnotation(c, n, templatePragmas)
+  if result != nil: return result
+  result = n
   var s: PSym
   if isTopLevel(c):
-    s = semIdentVis(c, skTemplate, n.sons[0], {sfExported})
+    s = semIdentVis(c, skTemplate, n[namePos], {sfExported})
     incl(s.flags, sfGlobal)
   else:
-    s = semIdentVis(c, skTemplate, n.sons[0], {})
-  styleCheckDef(c.config, s)
+    s = semIdentVis(c, skTemplate, n[namePos], {})
+  assert s.kind == skTemplate
+
+  styleCheckDef(c, s)
+  onDef(n[namePos].info, s)
   # check parameter list:
   #s.scope = c.currentScope
+  # push noalias flag at first to prevent unwanted recursive calls:
+  incl(s.flags, sfNoalias)
   pushOwner(c, s)
   openScope(c)
-  n.sons[namePos] = newSymNode(s, n.sons[namePos].info)
-  if n.sons[pragmasPos].kind != nkEmpty:
-    pragma(c, s, n.sons[pragmasPos], templatePragmas)
-
-  var gp: PNode
-  if n.sons[genericParamsPos].kind != nkEmpty:
-    n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos])
-    gp = n.sons[genericParamsPos]
-  else:
-    gp = newNodeI(nkGenericParams, n.info)
+  n[namePos] = newSymNode(s)
+  s.ast = n # for implicitPragmas to use
+  pragmaCallable(c, s, n, templatePragmas)
+  implicitPragmas(c, s, n.info, templatePragmas)
+
+  setGenericParamsMisc(c, n)
   # process parameters:
   var allUntyped = true
-  if n.sons[paramsPos].kind != nkEmpty:
-    semParamList(c, n.sons[paramsPos], gp, s)
+  var nullary = true
+  if n[paramsPos].kind != nkEmpty:
+    semParamList(c, n[paramsPos], n[genericParamsPos], s)
     # a template's parameters are not gensym'ed even if that was originally the
     # case as we determine whether it's a template parameter in the template
     # body by the absence of the sfGenSym flag:
-    for i in 1 .. s.typ.n.len-1:
-      let param = s.typ.n.sons[i].sym
-      param.flags.excl sfGenSym
-      if param.typ.kind != tyExpr: allUntyped = false
-    if sonsLen(gp) > 0:
-      if n.sons[genericParamsPos].kind == nkEmpty:
-        # we have a list of implicit type parameters:
-        n.sons[genericParamsPos] = gp
-    # no explicit return type? -> use tyStmt
-    if n.sons[paramsPos].sons[0].kind == nkEmpty:
-      # use ``stmt`` as implicit result type
-      s.typ.sons[0] = newTypeS(tyStmt, c)
-      s.typ.n.sons[0] = newNodeIT(nkType, n.info, s.typ.sons[0])
+    let retType = s.typ.returnType
+    if retType != nil and retType.kind != tyUntyped:
+      allUntyped = false
+    for i in 1..<s.typ.n.len:
+      let param = s.typ.n[i].sym
+      if param.name.id != ord(wUnderscore):
+        param.flags.incl sfTemplateParam
+        param.flags.excl sfGenSym
+      if param.typ.kind != tyUntyped: allUntyped = false
+      # no default value, parameters required in call
+      if param.ast == nil: nullary = false
   else:
     s.typ = newTypeS(tyProc, c)
-    # XXX why do we need tyStmt as a return type again?
+    # XXX why do we need tyTyped as a return type again?
     s.typ.n = newNodeI(nkFormalParams, n.info)
-    rawAddSon(s.typ, newTypeS(tyStmt, c))
-    addSon(s.typ.n, newNodeIT(nkType, n.info, s.typ.sons[0]))
+    rawAddSon(s.typ, newTypeS(tyTyped, c))
+    s.typ.n.add newNodeIT(nkType, n.info, s.typ.returnType)
+  if n[genericParamsPos].safeLen == 0:
+    # restore original generic type params as no explicit or implicit were found
+    n[genericParamsPos] = n[miscPos][1]
+    n[miscPos] = c.graph.emptyNode
   if allUntyped: incl(s.flags, sfAllUntyped)
-  if n.sons[patternPos].kind != nkEmpty:
-    n.sons[patternPos] = semPattern(c, n.sons[patternPos])
-  var ctx: TemplCtx
-  ctx.toBind = initIntSet()
-  ctx.toMixin = initIntSet()
-  ctx.toInject = initIntSet()
-  ctx.c = c
-  ctx.owner = s
+  if nullary and
+      n[genericParamsPos].kind == nkEmpty and
+      n[bodyPos].kind != nkEmpty:
+    # template can be called with alias syntax, remove pushed noalias flag
+    excl(s.flags, sfNoalias)
+
+  if n[patternPos].kind != nkEmpty:
+    n[patternPos] = semPattern(c, n[patternPos], s)
+
+  var ctx = TemplCtx(
+    toBind: initIntSet(),
+    toMixin: initIntSet(),
+    toInject: initIntSet(),
+    c: c,
+    owner: s
+  )
+  # handle default params:
+  for i in 1..<s.typ.n.len:
+    let param = s.typ.n[i].sym
+    if param.ast != nil:
+      # param default values need to be treated like template body:
+      if sfDirty in s.flags:
+        param.ast = semTemplBodyDirty(ctx, param.ast)
+      else:
+        param.ast = semTemplBody(ctx, param.ast)
+      if param.ast.referencesAnotherParam(s):
+        param.ast.flags.incl nfDefaultRefsParam
   if sfDirty in s.flags:
-    n.sons[bodyPos] = semTemplBodyDirty(ctx, n.sons[bodyPos])
+    n[bodyPos] = semTemplBodyDirty(ctx, n[bodyPos])
   else:
-    n.sons[bodyPos] = semTemplBody(ctx, n.sons[bodyPos])
+    n[bodyPos] = semTemplBody(ctx, n[bodyPos])
   # only parameters are resolved, no type checking is performed
-  semIdeForTemplateOrGeneric(c, n.sons[bodyPos], ctx.cursorInBody)
+  semIdeForTemplateOrGeneric(c, n[bodyPos], ctx.cursorInBody)
   closeScope(c)
   popOwner(c)
-  s.ast = n
-  result = n
+
   if sfCustomPragma in s.flags:
-    if n.sons[bodyPos].kind != nkEmpty:
-      localError(c.config, n.sons[bodyPos].info, errImplOfXNotAllowed % s.name.s)
-  elif n.sons[bodyPos].kind == nkEmpty:
+    if n[bodyPos].kind != nkEmpty:
+      localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s)
+  elif n[bodyPos].kind == nkEmpty:
     localError(c.config, n.info, "implementation of '$1' expected" % s.name.s)
-  var proto = searchForProc(c, c.currentScope, s)
+  var (proto, comesFromShadowscope) = searchForProc(c, c.currentScope, s)
   if proto == nil:
     addInterfaceOverloadableSymAt(c, c.currentScope, s)
-  else:
+  elif not comesFromShadowscope:
+    if {sfTemplateRedefinition, sfGenSym} * s.flags == {}:
+      #wrongRedefinition(c, n.info, proto.name.s, proto.info)
+      message(c.config, n.info, warnImplicitTemplateRedefinition, s.name.s)
     symTabReplace(c.currentScope.symbols, proto, s)
-  if n.sons[patternPos].kind != nkEmpty:
+  if n[patternPos].kind != nkEmpty:
     c.patterns.add(s)
 
 proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
@@ -634,8 +790,8 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
     # semtypes.addParamOrResult). Within the pattern we have to ensure
     # to use the param with the proper type though:
     incl(s.flags, sfUsed)
-    styleCheckUse(n.info, s)
-    let x = c.owner.typ.n.sons[s.position+1].sym
+    onUse(n.info, s)
+    let x = c.owner.typ.n[s.position+1].sym
     assert x.name == s.name
     result = newSymNode(x, n.info)
 
@@ -661,11 +817,6 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
       localError(c.c.config, n.info, "invalid expression")
       result = n
 
-  proc stupidStmtListExpr(n: PNode): bool =
-    for i in 0 .. n.len-2:
-      if n[i].kind notin {nkEmpty, nkCommentStmt}: return false
-    result = true
-
   result = n
   case n.kind
   of nkIdent:
@@ -679,14 +830,14 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
     # '(pattern){|x}' does the same but the matches will be gathered in 'x'
     if n.len != 2:
       localError(c.c.config, n.info, "invalid expression")
-    elif n.sons[1].kind == nkIdent:
-      n.sons[0] = semPatternBody(c, n.sons[0])
-      n.sons[1] = expectParam(c, n.sons[1])
-    elif n.sons[1].kind == nkPrefix and n.sons[1].sons[0].kind == nkIdent:
-      let opr = n.sons[1].sons[0]
+    elif n[1].kind == nkIdent:
+      n[0] = semPatternBody(c, n[0])
+      n[1] = expectParam(c, n[1])
+    elif n[1].kind == nkPrefix and n[1][0].kind == nkIdent:
+      let opr = n[1][0]
       if opr.ident.s == "|":
-        n.sons[0] = semPatternBody(c, n.sons[0])
-        n.sons[1].sons[1] = expectParam(c, n.sons[1].sons[1])
+        n[0] = semPatternBody(c, n[0])
+        n[1][1] = expectParam(c, n[1][1])
       else:
         localError(c.c.config, n.info, "invalid expression")
     else:
@@ -695,43 +846,41 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
     if stupidStmtListExpr(n):
       result = semPatternBody(c, n.lastSon)
     else:
-      for i in countup(0, sonsLen(n) - 1):
-        result.sons[i] = semPatternBody(c, n.sons[i])
+      for i in 0..<n.len:
+        result[i] = semPatternBody(c, n[i])
   of nkCallKinds:
-    let s = qualifiedLookUp(c.c, n.sons[0], {})
+    let s = qualifiedLookUp(c.c, n[0], {})
     if s != nil:
       if s.owner == c.owner and s.kind == skParam: discard
       elif contains(c.toBind, s.id): discard
       elif templToExpand(s):
         return semPatternBody(c, semTemplateExpr(c.c, n, s, {efNoSemCheck}))
 
-    if n.kind == nkInfix and n.sons[0].kind == nkIdent:
+    if n.kind == nkInfix and (let id = considerQuotedIdent(c.c, n[0]); id != nil):
       # we interpret `*` and `|` only as pattern operators if they occur in
       # infix notation, so that '`*`(a, b)' can be used for verbatim matching:
-      let opr = n.sons[0]
-      if opr.ident.s == "*" or opr.ident.s == "**":
+      if id.s == "*" or id.s == "**":
         result = newNodeI(nkPattern, n.info, n.len)
-        result.sons[0] = opr
-        result.sons[1] = semPatternBody(c, n.sons[1])
-        result.sons[2] = expectParam(c, n.sons[2])
+        result[0] = newIdentNode(id, n.info)
+        result[1] = semPatternBody(c, n[1])
+        result[2] = expectParam(c, n[2])
         return
-      elif opr.ident.s == "|":
+      elif id.s == "|":
         result = newNodeI(nkPattern, n.info, n.len)
-        result.sons[0] = opr
-        result.sons[1] = semPatternBody(c, n.sons[1])
-        result.sons[2] = semPatternBody(c, n.sons[2])
+        result[0] = newIdentNode(id, n.info)
+        result[1] = semPatternBody(c, n[1])
+        result[2] = semPatternBody(c, n[2])
         return
 
-    if n.kind == nkPrefix and n.sons[0].kind == nkIdent:
-      let opr = n.sons[0]
-      if opr.ident.s == "~":
+    if n.kind == nkPrefix and (let id = considerQuotedIdent(c.c, n[0]); id != nil):
+      if id.s == "~":
         result = newNodeI(nkPattern, n.info, n.len)
-        result.sons[0] = opr
-        result.sons[1] = semPatternBody(c, n.sons[1])
+        result[0] = newIdentNode(id, n.info)
+        result[1] = semPatternBody(c, n[1])
         return
 
-    for i in countup(0, sonsLen(n) - 1):
-      result.sons[i] = semPatternBody(c, n.sons[i])
+    for i in 0..<n.len:
+      result[i] = semPatternBody(c, n[i])
   else:
     # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
     # so we use the generic code for nkDotExpr too
@@ -744,23 +893,25 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
         else:
           return newIdentNode(s.name, n.info)
     of nkPar:
-      if n.len == 1: return semPatternBody(c, n.sons[0])
+      if n.len == 1: return semPatternBody(c, n[0])
     else: discard
-    for i in countup(0, sonsLen(n) - 1):
-      result.sons[i] = semPatternBody(c, n.sons[i])
+    for i in 0..<n.len:
+      result[i] = semPatternBody(c, n[i])
 
-proc semPattern(c: PContext, n: PNode): PNode =
+proc semPattern(c: PContext, n: PNode; s: PSym): PNode =
   openScope(c)
-  var ctx: TemplCtx
-  ctx.toBind = initIntSet()
-  ctx.toMixin = initIntSet()
-  ctx.toInject = initIntSet()
-  ctx.c = c
-  ctx.owner = getCurrOwner(c)
+  var ctx = TemplCtx(
+    toBind: initIntSet(),
+    toMixin: initIntSet(),
+    toInject: initIntSet(),
+    c: c,
+    owner: getCurrOwner(c)
+  )
   result = flattenStmts(semPatternBody(ctx, n))
   if result.kind in {nkStmtList, nkStmtListExpr}:
     if result.len == 1:
-      result = result.sons[0]
+      result = result[0]
     elif result.len == 0:
       localError(c.config, n.info, "a pattern cannot be empty")
   closeScope(c)
+  addPattern(c, LazySym(sym: s))
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 3e62652a7..113946fef 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -11,18 +11,19 @@
 # included from sem.nim
 
 const
+  errStringOrIdentNodeExpected = "string or ident node expected"
   errStringLiteralExpected = "string literal expected"
   errIntLiteralExpected = "integer literal expected"
   errWrongNumberOfVariables = "wrong number of variables"
-  errInvalidOrderInEnumX = "invalid order in enum '$1'"
-  errOrdinalTypeExpected = "ordinal type expected"
-  errSetTooBig = "set is too large"
+  errDuplicateAliasInEnumX = "duplicate value in enum '$1'"
+  errOverflowInEnumX = "The enum '$1' exceeds its maximum value ($2)"
+  errOrdinalTypeExpected = "ordinal type expected; given: $1"
+  errSetTooBig = "set is too large; use `std/sets` for ordinal types with more than 2^16 elements"
   errBaseTypeMustBeOrdinal = "base type of a set must be an ordinal"
   errInheritanceOnlyWithNonFinalObjects = "inheritance only works with non-final objects"
   errXExpectsOneTypeParam = "'$1' expects one type parameter"
   errArrayExpectsTwoTypeParams = "array expects two type parameters"
   errInvalidVisibilityX = "invalid visibility: '$1'"
-  errInitHereNotAllowed = "initialization not allowed here"
   errXCannotBeAssignedTo = "'$1' cannot be assigned to"
   errIteratorNotAllowed = "iterators can only be defined at the module's top level"
   errXNeedsReturnType = "$1 needs a return type"
@@ -37,8 +38,17 @@ const
   errNoGenericParamsAllowedForX = "no generic parameters allowed for $1"
   errInOutFlagNotExtern = "the '$1' modifier can be used only with imported types"
 
+proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext, son: sink PType): PType =
+  if prev == nil or prev.kind == tyGenericBody:
+    result = newTypeS(kind, c, son)
+  else:
+    result = prev
+    result.setSon(son)
+    if result.kind == tyForward: result.kind = kind
+  #if kind == tyError: result.flags.incl tfCheckedForDestructor
+
 proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
-  if prev == nil:
+  if prev == nil or prev.kind == tyGenericBody:
     result = newTypeS(kind, c)
   else:
     result = prev
@@ -46,164 +56,237 @@ proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
 
 proc newConstraint(c: PContext, k: TTypeKind): PType =
   result = newTypeS(tyBuiltInTypeClass, c)
-  result.addSonSkipIntLit(newTypeS(k, c))
+  result.flags.incl tfCheckedForDestructor
+  result.addSonSkipIntLit(newTypeS(k, c), c.idgen)
 
 proc semEnum(c: PContext, n: PNode, prev: PType): PType =
-  if n.sonsLen == 0: return newConstraint(c, tyEnum)
-  elif n.sonsLen == 1:
+  if n.len == 0: return newConstraint(c, tyEnum)
+  elif n.len == 1:
     # don't create an empty tyEnum; fixes #3052
     return errorType(c)
   var
-    counter, x: BiggestInt
-    e: PSym
-    base: PType
+    counter, x: BiggestInt = 0
+    e: PSym = nil
+    base: PType = nil
+    identToReplace: ptr PNode = nil
+    counterSet = initPackedSet[BiggestInt]()
   counter = 0
   base = nil
   result = newOrPrevType(tyEnum, prev, c)
   result.n = newNodeI(nkEnumTy, n.info)
   checkMinSonsLen(n, 1, c.config)
-  if n.sons[0].kind != nkEmpty:
-    base = semTypeNode(c, n.sons[0].sons[0], nil)
+  if n[0].kind != nkEmpty:
+    base = semTypeNode(c, n[0][0], nil)
     if base.kind != tyEnum:
-      localError(c.config, n.sons[0].info, "inheritance only works with an enum")
-    counter = lastOrd(c.config, base) + 1
+      localError(c.config, n[0].info, "inheritance only works with an enum")
+    counter = toInt64(lastOrd(c.config, base)) + 1
   rawAddSon(result, base)
   let isPure = result.sym != nil and sfPure in result.sym.flags
-  var symbols: TStrTable
-  if isPure: initStrTable(symbols)
+  var symbols: TStrTable = initStrTable()
   var hasNull = false
-  for i in countup(1, sonsLen(n) - 1):
-    case n.sons[i].kind
+  for i in 1..<n.len:
+    if n[i].kind == nkEmpty: continue
+    var useAutoCounter = false
+    case n[i].kind
     of nkEnumFieldDef:
-      e = newSymS(skEnumField, n.sons[i].sons[0], c)
-      var v = semConstExpr(c, n.sons[i].sons[1])
+      if n[i][0].kind == nkPragmaExpr:
+        e = newSymS(skEnumField, n[i][0][0], c)
+        identToReplace = addr n[i][0][0]
+        pragma(c, e, n[i][0][1], enumFieldPragmas)
+      else:
+        e = newSymS(skEnumField, n[i][0], c)
+        identToReplace = addr n[i][0]
+      var v = semConstExpr(c, n[i][1])
       var strVal: PNode = nil
       case skipTypes(v.typ, abstractInst-{tyTypeDesc}).kind
       of tyTuple:
-        if sonsLen(v) == 2:
-          strVal = v.sons[1] # second tuple part is the string value
-          if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCString}:
-            x = getOrdValue(v.sons[0]) # first tuple part is the ordinal
+        if v.len == 2:
+          strVal = v[1] # second tuple part is the string value
+          if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCstring}:
+            if not isOrdinalType(v[0].typ, allowEnumWithHoles=true):
+              localError(c.config, v[0].info, errOrdinalTypeExpected % typeToString(v[0].typ, preferDesc))
+            x = toInt64(getOrdValue(v[0])) # first tuple part is the ordinal
+            n[i][1][0] = newIntTypeNode(x, getSysType(c.graph, unknownLineInfo, tyInt))
           else:
             localError(c.config, strVal.info, errStringLiteralExpected)
         else:
           localError(c.config, v.info, errWrongNumberOfVariables)
-      of tyString, tyCString:
+      of tyString, tyCstring:
         strVal = v
         x = counter
+        useAutoCounter = true
       else:
-        x = getOrdValue(v)
+        if isOrdinalType(v.typ, allowEnumWithHoles=true):
+          x = toInt64(getOrdValue(v))
+          n[i][1] = newIntTypeNode(x, getSysType(c.graph, unknownLineInfo, tyInt))
+        else:
+          localError(c.config, v.info, errOrdinalTypeExpected % typeToString(v.typ, preferDesc))
       if i != 1:
         if x != counter: incl(result.flags, tfEnumHasHoles)
-        if x < counter:
-          localError(c.config, n.sons[i].info, errInvalidOrderInEnumX % e.name.s)
-          x = counter
       e.ast = strVal # might be nil
       counter = x
     of nkSym:
-      e = n.sons[i].sym
+      e = n[i].sym
+      useAutoCounter = true
     of nkIdent, nkAccQuoted:
-      e = newSymS(skEnumField, n.sons[i], c)
+      e = newSymS(skEnumField, n[i], c)
+      identToReplace = addr n[i]
+      useAutoCounter = true
+    of nkPragmaExpr:
+      e = newSymS(skEnumField, n[i][0], c)
+      pragma(c, e, n[i][1], enumFieldPragmas)
+      identToReplace = addr n[i][0]
+      useAutoCounter = true
     else:
       illFormedAst(n[i], c.config)
+
+    if useAutoCounter:
+      while counter in counterSet and counter != high(typeof(counter)):
+        inc counter
+      counterSet.incl counter
+    elif counterSet.containsOrIncl(counter):
+      localError(c.config, n[i].info, errDuplicateAliasInEnumX % e.name.s)
+
     e.typ = result
     e.position = int(counter)
+    let symNode = newSymNode(e)
+    if identToReplace != nil and c.config.cmd notin cmdDocLike:
+      # A hack to produce documentation for enum fields.
+      identToReplace[] = symNode
     if e.position == 0: hasNull = true
     if result.sym != nil and sfExported in result.sym.flags:
-      incl(e.flags, sfUsed)
-      incl(e.flags, sfExported)
-      if not isPure: strTableAdd(c.module.tab, e)
-    addSon(result.n, newSymNode(e))
-    styleCheckDef(c.config, e)
+      e.flags.incl {sfUsed, sfExported}
+
+    result.n.add symNode
+    styleCheckDef(c, e)
+    onDef(e.info, e)
+    suggestSym(c.graph, e.info, e, c.graph.usageSym)
     if sfGenSym notin e.flags:
-      if not isPure: addDecl(c, e)
-      else: importPureEnumField(c, e)
-    if isPure and strTableIncl(symbols, e):
-      wrongRedefinition(c, e.info, e.name.s)
-    inc(counter)
-  if not hasNull: incl(result.flags, tfNeedsInit)
+      if not isPure:
+        addInterfaceOverloadableSymAt(c, c.currentScope, e)
+      else:
+        declarePureEnumField(c, e)
+    if (let conflict = strTableInclReportConflict(symbols, e); conflict != nil):
+      wrongRedefinition(c, e.info, e.name.s, conflict.info)
+    if counter == high(typeof(counter)):
+      if i > 1 and result.n[i-2].sym.position == high(int):
+        localError(c.config, n[i].info, errOverflowInEnumX % [e.name.s, $high(typeof(counter))])
+    else:
+      inc(counter)
+  if isPure and sfExported in result.sym.flags:
+    addPureEnum(c, LazySym(sym: result.sym))
+  if tfNotNil in e.typ.flags and not hasNull:
+    result.flags.incl tfRequiresInit
+  setToStringProc(c.graph, result, genEnumToStrProc(result, n.info, c.graph, c.idgen))
 
 proc semSet(c: PContext, n: PNode, prev: PType): PType =
   result = newOrPrevType(tySet, prev, c)
-  if sonsLen(n) == 2:
-    var base = semTypeNode(c, n.sons[1], nil)
-    addSonSkipIntLit(result, base)
-    if base.kind in {tyGenericInst, tyAlias, tySink}: base = lastSon(base)
-    if base.kind != tyGenericParam:
-      if not isOrdinalType(base):
-        localError(c.config, n.info, errOrdinalTypeExpected)
+  if n.len == 2 and n[1].kind != nkEmpty:
+    var base = semTypeNode(c, n[1], nil)
+    addSonSkipIntLit(result, base, c.idgen)
+    if base.kind in {tyGenericInst, tyAlias, tySink}: base = skipModifier(base)
+    if base.kind notin {tyGenericParam, tyGenericInvocation}:
+      if base.kind == tyForward:
+        c.skipTypes.add n
+      elif not isOrdinalType(base, allowEnumWithHoles = true):
+        localError(c.config, n.info, errOrdinalTypeExpected % typeToString(base, preferDesc))
       elif lengthOrd(c.config, base) > MaxSetElements:
         localError(c.config, n.info, errSetTooBig)
   else:
     localError(c.config, n.info, errXExpectsOneTypeParam % "set")
-    addSonSkipIntLit(result, errorType(c))
+    addSonSkipIntLit(result, errorType(c), c.idgen)
 
-proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string,
-                  prev: PType): PType =
-  result = newOrPrevType(kind, prev, c)
-  if sonsLen(n) == 2:
-    var base = semTypeNode(c, n.sons[1], nil)
+proc semContainerArg(c: PContext; n: PNode, kindStr: string; result: PType) =
+  if n.len == 2:
+    var base = semTypeNode(c, n[1], nil)
     if base.kind == tyVoid:
       localError(c.config, n.info, errTIsNotAConcreteType % typeToString(base))
-    addSonSkipIntLit(result, base)
+    addSonSkipIntLit(result, base, c.idgen)
   else:
     localError(c.config, n.info, errXExpectsOneTypeParam % kindStr)
-    addSonSkipIntLit(result, errorType(c))
+    addSonSkipIntLit(result, errorType(c), c.idgen)
+
+proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string,
+                  prev: PType): PType =
+  result = newOrPrevType(kind, prev, c)
+  semContainerArg(c, n, kindStr, result)
 
 proc semVarargs(c: PContext, n: PNode, prev: PType): PType =
   result = newOrPrevType(tyVarargs, prev, c)
-  if sonsLen(n) == 2 or sonsLen(n) == 3:
-    var base = semTypeNode(c, n.sons[1], nil)
-    addSonSkipIntLit(result, base)
-    if sonsLen(n) == 3:
-      result.n = newIdentNode(considerQuotedIdent(c, n.sons[2]), n.sons[2].info)
+  if n.len == 2 or n.len == 3:
+    var base = semTypeNode(c, n[1], nil)
+    addSonSkipIntLit(result, base, c.idgen)
+    if n.len == 3:
+      result.n = newIdentNode(considerQuotedIdent(c, n[2]), n[2].info)
   else:
     localError(c.config, n.info, errXExpectsOneTypeParam % "varargs")
-    addSonSkipIntLit(result, errorType(c))
-
-proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
-  if n.len < 1:
-    result = newConstraint(c, kind)
-  else:
-    let isCall = int ord(n.kind in nkCallKinds+{nkBracketExpr})
-    let n = if n[0].kind == nkBracket: n[0] else: n
-    checkMinSonsLen(n, 1, c.config)
-    var t = semTypeNode(c, n.lastSon, nil)
-    if t.kind == tyTypeDesc and tfUnresolved notin t.flags:
-      t = t.base
-    result = newOrPrevType(kind, prev, c)
-    var isNilable = false
-    # check every except the last is an object:
-    for i in isCall .. n.len-2:
-      let ni = n[i]
-      if ni.kind == nkNilLit:
-        isNilable = true
-      else:
-        let region = semTypeNode(c, ni, nil)
-        if region.skipTypes({tyGenericInst, tyAlias, tySink}).kind notin {
-              tyError, tyObject}:
-          message c.config, n[i].info, errGenerated, "region needs to be an object type"
-        addSonSkipIntLit(result, region)
-    addSonSkipIntLit(result, t)
-    if tfPartial in result.flags:
-      if result.lastSon.kind == tyObject: incl(result.lastSon.flags, tfPartial)
-    #if not isNilable: result.flags.incl tfNotNil
+    addSonSkipIntLit(result, errorType(c), c.idgen)
 
-proc semVarType(c: PContext, n: PNode, prev: PType): PType =
-  if sonsLen(n) == 1:
+proc semVarOutType(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType =
+  if n.len == 1:
     result = newOrPrevType(tyVar, prev, c)
-    var base = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
+    result.flags = flags
+    var base = semTypeNode(c, n[0], nil)
+    if base.kind == tyTypeDesc and not isSelf(base):
+      base = base[0]
     if base.kind == tyVar:
       localError(c.config, n.info, "type 'var var' is not allowed")
-      base = base.sons[0]
-    addSonSkipIntLit(result, base)
+      base = base[0]
+    addSonSkipIntLit(result, base, c.idgen)
   else:
     result = newConstraint(c, tyVar)
 
+proc isRecursiveType(t: PType, cycleDetector: var IntSet): bool =
+  if t == nil:
+    return false
+  if cycleDetector.containsOrIncl(t.id):
+    return true
+  case t.kind
+  of tyAlias, tyGenericInst, tyDistinct:
+    return isRecursiveType(t.skipModifier, cycleDetector)
+  else:
+    return false
+
+proc fitDefaultNode(c: PContext, n: PNode): PType =
+  inc c.inStaticContext
+  let expectedType = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
+  n[^1] = semConstExpr(c, n[^1], expectedType = expectedType)
+  let oldType = n[^1].typ
+  n[^1].flags.incl nfSem
+  if n[^2].kind != nkEmpty:
+    if expectedType != nil and oldType != expectedType:
+      n[^1] = fitNodeConsiderViewType(c, expectedType, n[^1], n[^1].info)
+      changeType(c, n[^1], expectedType, true) # infer types for default fields value
+        # bug #22926; be cautious that it uses `semConstExpr` to
+        # evaulate the default fields; it's only natural to use
+        # `changeType` to infer types for constant values
+        # that's also the reason why we don't use `semExpr` to check
+        # the type since two overlapping error messages might be produced
+    result = n[^1].typ
+  else:
+    result = n[^1].typ
+  # xxx any troubles related to defaults fields, consult `semConst` for a potential answer
+  if n[^1].kind != nkNilLit:
+    typeAllowedCheck(c, n.info, result, skConst, {taProcContextIsNotMacro, taIsDefaultField})
+  dec c.inStaticContext
+
+proc isRecursiveType*(t: PType): bool =
+  # handle simple recusive types before typeFinalPass
+  var cycleDetector = initIntSet()
+  isRecursiveType(t, cycleDetector)
+
+proc addSonSkipIntLitChecked(c: PContext; father, son: PType; it: PNode, id: IdGenerator) =
+  let s = son.skipIntLit(id)
+  father.add(s)
+  if isRecursiveType(s):
+    localError(c.config, it.info, "illegal recursion in type '" & typeToString(s) & "'")
+  else:
+    propagateToOwner(father, s)
+
 proc semDistinct(c: PContext, n: PNode, prev: PType): PType =
   if n.len == 0: return newConstraint(c, tyDistinct)
   result = newOrPrevType(tyDistinct, prev, c)
-  addSonSkipIntLit(result, semTypeNode(c, n.sons[0], nil))
+  addSonSkipIntLitChecked(c, result, semTypeNode(c, n[0], nil), n[0], c.idgen)
   if n.len > 1: result.n = n[1]
 
 proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
@@ -213,36 +296,43 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
   result.n = newNodeI(nkRange, n.info)
   # always create a 'valid' range type, but overwrite it later
   # because 'semExprWithType' can raise an exception. See bug #6895.
-  addSonSkipIntLit(result, errorType(c))
+  addSonSkipIntLit(result, errorType(c), c.idgen)
 
   if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty):
     localError(c.config, n.info, "range is empty")
 
   var range: array[2, PNode]
+  # XXX this is still a hard compilation in a generic context, this can
+  # result in unresolved generic parameters being treated like real types
   range[0] = semExprWithType(c, n[1], {efDetermineType})
   range[1] = semExprWithType(c, n[2], {efDetermineType})
 
-  var rangeT: array[2, PType]
+  var rangeT: array[2, PType] = default(array[2, PType])
   for i in 0..1:
-    rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit
+    rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit(c.idgen)
 
   let hasUnknownTypes = c.inGenericContext > 0 and
-    rangeT[0].kind == tyFromExpr or rangeT[1].kind == tyFromExpr
+    (rangeT[0].kind == tyFromExpr or rangeT[1].kind == tyFromExpr)
 
   if not hasUnknownTypes:
     if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})):
-      localError(c.config, n.info, "type mismatch")
-    elif not rangeT[0].isOrdinalType:
-      localError(c.config, n.info, "ordinal type expected")
+      typeMismatch(c.config, n.info, rangeT[0], rangeT[1], n)
+
+    elif not isOrdinalType(rangeT[0]) and rangeT[0].kind notin {tyFloat..tyFloat128} or
+        rangeT[0].kind == tyBool:
+      localError(c.config, n.info, "ordinal or float type expected, but got " & typeToString(rangeT[0]))
     elif enumHasHoles(rangeT[0]):
       localError(c.config, n.info, "enum '$1' has holes" % typeToString(rangeT[0]))
 
   for i in 0..1:
-    if hasGenericArguments(range[i]):
-      result.n.addSon makeStaticExpr(c, range[i])
+    if hasUnresolvedArgs(c, range[i]):
+      result.n.add makeStaticExpr(c, range[i])
       result.flags.incl tfUnresolved
     else:
-      result.n.addSon semConstExpr(c, range[i])
+      result.n.add semConstExpr(c, range[i])
+
+    if result.n[i].kind in {nkFloatLit..nkFloat64Lit} and result.n[i].floatVal.isNaN:
+      localError(c.config, n.info, "NaN is not a valid range " & (if i == 0: "start" else: "end"))
 
   if weakLeValue(result.n[0], result.n[1]) == impNo:
     localError(c.config, n.info, "range is empty")
@@ -251,55 +341,72 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
 
 proc semRange(c: PContext, n: PNode, prev: PType): PType =
   result = nil
-  if sonsLen(n) == 2:
+  if n.len == 2:
     if isRange(n[1]):
       result = semRangeAux(c, n[1], prev)
-      let n = result.n
-      if n.sons[0].kind in {nkCharLit..nkUInt64Lit} and n.sons[0].intVal > 0:
-        incl(result.flags, tfNeedsInit)
-      elif n.sons[1].kind in {nkCharLit..nkUInt64Lit} and n.sons[1].intVal < 0:
-        incl(result.flags, tfNeedsInit)
-      elif n.sons[0].kind in {nkFloatLit..nkFloat64Lit} and
-          n.sons[0].floatVal > 0.0:
-        incl(result.flags, tfNeedsInit)
-      elif n.sons[1].kind in {nkFloatLit..nkFloat64Lit} and
-          n.sons[1].floatVal < 0.0:
-        incl(result.flags, tfNeedsInit)
+      if not isDefined(c.config, "nimPreviewRangeDefault"):
+        let n = result.n
+        if n[0].kind in {nkCharLit..nkUInt64Lit} and n[0].intVal > 0:
+          incl(result.flags, tfRequiresInit)
+        elif n[1].kind in {nkCharLit..nkUInt64Lit} and n[1].intVal < 0:
+          incl(result.flags, tfRequiresInit)
+        elif n[0].kind in {nkFloatLit..nkFloat64Lit} and
+            n[0].floatVal > 0.0:
+          incl(result.flags, tfRequiresInit)
+        elif n[1].kind in {nkFloatLit..nkFloat64Lit} and
+            n[1].floatVal < 0.0:
+          incl(result.flags, tfRequiresInit)
     else:
       if n[1].kind == nkInfix and considerQuotedIdent(c, n[1][0]).s == "..<":
         localError(c.config, n[0].info, "range types need to be constructed with '..', '..<' is not supported")
       else:
-        localError(c.config, n.sons[0].info, "expected range")
+        localError(c.config, n[0].info, "expected range")
       result = newOrPrevType(tyError, prev, c)
   else:
     localError(c.config, n.info, errXExpectsOneTypeParam % "range")
     result = newOrPrevType(tyError, prev, c)
 
+proc semArrayIndexConst(c: PContext, e: PNode, info: TLineInfo): PType =
+  let x = semConstExpr(c, e)
+  if x.kind in {nkIntLit..nkUInt64Lit}:
+    result = makeRangeType(c, 0, x.intVal-1, info,
+                        x.typ.skipTypes({tyTypeDesc}))
+  else:
+    result = x.typ.skipTypes({tyTypeDesc})
+
 proc semArrayIndex(c: PContext, n: PNode): PType =
   if isRange(n):
     result = semRangeAux(c, n, nil)
+  elif n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "..<":
+    result = errorType(c)
   else:
+    # XXX this is still a hard compilation in a generic context, this can
+    # result in unresolved generic parameters being treated like real types
     let e = semExprWithType(c, n, {efDetermineType})
     if e.typ.kind == tyFromExpr:
       result = makeRangeWithStaticExpr(c, e.typ.n)
     elif e.kind in {nkIntLit..nkUInt64Lit}:
       if e.intVal < 0:
-        localError(c.config, n[1].info,
+        localError(c.config, n.info,
           "Array length can't be negative, but was " & $e.intVal)
       result = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
-    elif e.kind == nkSym and e.typ.kind == tyStatic:
-      if e.sym.ast != nil:
-        return semArrayIndex(c, e.sym.ast)
-      if not isOrdinalType(e.typ.lastSon):
-        let info = if n.safeLen > 1: n[1].info else: n.info
-        localError(c.config, info, errOrdinalTypeExpected)
-      result = makeRangeWithStaticExpr(c, e)
-      if c.inGenericContext > 0: result.flags.incl tfUnresolved
-    elif e.kind in nkCallKinds and hasGenericArguments(e):
-      if not isOrdinalType(e.typ):
-        localError(c.config, n[1].info, errOrdinalTypeExpected)
+    elif e.kind == nkSym and (e.typ.kind == tyStatic or e.typ.kind == tyTypeDesc):
+      if e.typ.kind == tyStatic:
+        if e.sym.ast != nil:
+          return semArrayIndex(c, e.sym.ast)
+        if e.typ.skipModifier.kind != tyGenericParam and not isOrdinalType(e.typ.skipModifier):
+          let info = if n.safeLen > 1: n[1].info else: n.info
+          localError(c.config, info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc))
+        result = makeRangeWithStaticExpr(c, e)
+        if c.inGenericContext > 0: result.flags.incl tfUnresolved
+      else:
+        result = e.typ.skipTypes({tyTypeDesc})
+        result.flags.incl tfImplicitStatic
+    elif e.kind in (nkCallKinds + {nkBracketExpr}) and hasUnresolvedArgs(c, e):
+      if not isOrdinalType(e.typ.skipTypes({tyStatic, tyAlias, tyGenericInst, tySink})):
+        localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc))
       # This is an int returning call, depending on an
-      # yet unknown generic param (see tgenericshardcases).
+      # yet unknown generic param (see tuninstantiatedgenericcalls).
       # We are going to construct a range type that will be
       # properly filled-out in semtypinst (see how tyStaticExpr
       # is handled there).
@@ -307,120 +414,73 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
     elif e.kind == nkIdent:
       result = e.typ.skipTypes({tyTypeDesc})
     else:
-      let x = semConstExpr(c, e)
-      if x.kind in {nkIntLit..nkUInt64Lit}:
-        result = makeRangeType(c, 0, x.intVal-1, n.info,
-                             x.typ.skipTypes({tyTypeDesc}))
-      else:
-        result = x.typ.skipTypes({tyTypeDesc})
+      result = semArrayIndexConst(c, e, n.info)
         #localError(c.config, n[1].info, errConstExprExpected)
 
 proc semArray(c: PContext, n: PNode, prev: PType): PType =
   var base: PType
-  if sonsLen(n) == 3:
+  if n.len == 3:
     # 3 = length(array indx base)
     let indx = semArrayIndex(c, n[1])
     var indxB = indx
-    if indxB.kind in {tyGenericInst, tyAlias, tySink}: indxB = lastSon(indxB)
-    if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr}:
-      if indxB.skipTypes({tyRange}).kind in {tyUInt, tyUInt64}:
-        discard
-      elif not isOrdinalType(indxB):
-        localError(c.config, n.sons[1].info, errOrdinalTypeExpected)
+    if indxB.kind in {tyGenericInst, tyAlias, tySink}: indxB = skipModifier(indxB)
+    if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr} and
+        tfUnresolved notin indxB.flags:
+      if not isOrdinalType(indxB):
+        localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(indxB, preferDesc))
       elif enumHasHoles(indxB):
-        localError(c.config, n.sons[1].info, "enum '$1' has holes" %
+        localError(c.config, n[1].info, "enum '$1' has holes" %
                    typeToString(indxB.skipTypes({tyRange})))
-    base = semTypeNode(c, n.sons[2], nil)
+      elif indxB.kind != tyRange and
+          lengthOrd(c.config, indxB) > high(uint16).int:
+        # assume range type is intentional
+        localError(c.config, n[1].info,
+          "index type '$1' for array is too large" % typeToString(indxB))
+    base = semTypeNode(c, n[2], nil)
     # ensure we only construct a tyArray when there was no error (bug #3048):
-    result = newOrPrevType(tyArray, prev, c)
     # bug #6682: Do not propagate initialization requirements etc for the
     # index type:
-    rawAddSonNoPropagationOfTypeFlags(result, indx)
-    addSonSkipIntLit(result, base)
+    result = newOrPrevType(tyArray, prev, c, indx)
+    addSonSkipIntLit(result, base, c.idgen)
   else:
     localError(c.config, n.info, errArrayExpectsTwoTypeParams)
     result = newOrPrevType(tyError, prev, c)
 
+proc semIterableType(c: PContext, n: PNode, prev: PType): PType =
+  result = newOrPrevType(tyIterable, prev, c)
+  if n.len == 2:
+    let base = semTypeNode(c, n[1], nil)
+    addSonSkipIntLit(result, base, c.idgen)
+  else:
+    localError(c.config, n.info, errXExpectsOneTypeParam % "iterable")
+    result = newOrPrevType(tyError, prev, c)
+
 proc semOrdinal(c: PContext, n: PNode, prev: PType): PType =
   result = newOrPrevType(tyOrdinal, prev, c)
-  if sonsLen(n) == 2:
-    var base = semTypeNode(c, n.sons[1], nil)
+  if n.len == 2:
+    var base = semTypeNode(c, n[1], nil)
     if base.kind != tyGenericParam:
       if not isOrdinalType(base):
-        localError(c.config, n.sons[1].info, errOrdinalTypeExpected)
-    addSonSkipIntLit(result, base)
+        localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(base, preferDesc))
+    addSonSkipIntLit(result, base, c.idgen)
   else:
     localError(c.config, n.info, errXExpectsOneTypeParam % "ordinal")
     result = newOrPrevType(tyError, prev, c)
 
-proc semTypeIdent(c: PContext, n: PNode): PSym =
-  if n.kind == nkSym:
-    result = getGenSym(c, n.sym)
-  else:
-    result = pickSym(c, n, {skType, skGenericParam})
-    if result.isNil:
-      result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared})
-    if result != nil:
-      markUsed(c.config, n.info, result, c.graph.usageSym)
-      styleCheckUse(n.info, result)
-      if result.kind == skParam and result.typ.kind == tyTypeDesc:
-        # This is a typedesc param. is it already bound?
-        # it's not bound when it's used multiple times in the
-        # proc signature for example
-        if c.inGenericInst > 0:
-          let bound = result.typ.sons[0].sym
-          if bound != nil: return bound
-          return result
-        if result.typ.sym == nil:
-          localError(c.config, n.info, errTypeExpected)
-          return errorSym(c, n)
-        result = result.typ.sym.copySym
-        result.typ = copyType(result.typ, result.typ.owner, true)
-        result.typ.flags.incl tfUnresolved
-
-      if result.kind == skGenericParam:
-        if result.typ.kind == tyGenericParam and result.typ.len == 0 and
-           tfWildcard in result.typ.flags:
-          # collapse the wild-card param to a type
-          result.kind = skType
-          result.typ.flags.excl tfWildcard
-          return
-        else:
-          localError(c.config, n.info, errTypeExpected)
-          return errorSym(c, n)
-
-      if result.kind != skType:
-        # this implements the wanted ``var v: V, x: V`` feature ...
-        var ov: TOverloadIter
-        var amb = initOverloadIter(ov, c, n)
-        while amb != nil and amb.kind != skType:
-          amb = nextOverloadIter(ov, c, n)
-        if amb != nil: result = amb
-        else:
-          if result.kind != skError: localError(c.config, n.info, errTypeExpected)
-          return errorSym(c, n)
-      if result.typ.kind != tyGenericParam:
-        # XXX get rid of this hack!
-        var oldInfo = n.info
-        when defined(useNodeIds):
-          let oldId = n.id
-        reset(n[])
-        when defined(useNodeIds):
-          n.id = oldId
-        n.kind = nkSym
-        n.sym = result
-        n.info = oldInfo
-        n.typ = result.typ
-    else:
-      localError(c.config, n.info, "identifier expected")
-      result = errorSym(c, n)
-
 proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType =
-  if sonsLen(n) == 0:
+  if n.len == 0:
     localError(c.config, n.info, errTypeExpected)
   result = newOrPrevType(tyTuple, prev, c)
   for it in n:
-    addSonSkipIntLit(result, semTypeNode(c, it, nil))
+    let t = semTypeNode(c, it, nil)
+    addSonSkipIntLitChecked(c, result, t, it, c.idgen)
+
+proc firstRange(config: ConfigRef, t: PType): PNode =
+  if t.skipModifier().kind in tyFloat..tyFloat64:
+    result = newFloatNode(nkFloatLit, firstFloat(t))
+  else:
+    result = newIntNode(nkIntLit, firstOrd(config, t))
+  result.typ = t
 
 proc semTuple(c: PContext, n: PNode, prev: PType): PType =
   var typ: PType
@@ -428,288 +488,442 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
   result.n = newNodeI(nkRecList, n.info)
   var check = initIntSet()
   var counter = 0
-  for i in countup(ord(n.kind == nkBracketExpr), sonsLen(n) - 1):
-    var a = n.sons[i]
+  for i in ord(n.kind == nkBracketExpr)..<n.len:
+    var a = n[i]
     if (a.kind != nkIdentDefs): illFormedAst(a, c.config)
     checkMinSonsLen(a, 3, c.config)
-    var length = sonsLen(a)
-    if a.sons[length - 2].kind != nkEmpty:
-      typ = semTypeNode(c, a.sons[length - 2], nil)
+    var hasDefaultField = a[^1].kind != nkEmpty
+    if hasDefaultField:
+      typ = fitDefaultNode(c, a)
+    elif a[^2].kind != nkEmpty:
+      typ = semTypeNode(c, a[^2], nil)
+      if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
+        a[^1] = firstRange(c.config, typ)
+        hasDefaultField = true
     else:
       localError(c.config, a.info, errTypeExpected)
       typ = errorType(c)
-    if a.sons[length - 1].kind != nkEmpty:
-      localError(c.config, a.sons[length - 1].info, errInitHereNotAllowed)
-    for j in countup(0, length - 3):
-      var field = newSymG(skField, a.sons[j], c)
+    for j in 0..<a.len - 2:
+      var field = newSymG(skField, a[j], c)
       field.typ = typ
       field.position = counter
       inc(counter)
       if containsOrIncl(check, field.name.id):
-        localError(c.config, a.sons[j].info, "attempt to redefine: '" & field.name.s & "'")
+        localError(c.config, a[j].info, "attempt to redefine: '" & field.name.s & "'")
       else:
-        addSon(result.n, newSymNode(field))
-        addSonSkipIntLit(result, typ)
-      styleCheckDef(c.config, a.sons[j].info, field)
+        let fSym = newSymNode(field)
+        if hasDefaultField:
+          fSym.sym.ast = a[^1]
+          fSym.sym.ast.flags.incl nfSkipFieldChecking
+        result.n.add fSym
+        addSonSkipIntLit(result, typ, c.idgen)
+      styleCheckDef(c, a[j].info, field)
+      onDef(field.info, field)
   if result.n.len == 0: result.n = nil
+  if isTupleRecursive(result):
+    localError(c.config, n.info, errIllegalRecursionInTypeX % typeToString(result))
 
 proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
                  allowed: TSymFlags): PSym =
   # identifier with visibility
   if n.kind == nkPostfix:
-    if sonsLen(n) == 2:
+    if n.len == 2:
       # for gensym'ed identifiers the identifier may already have been
       # transformed to a symbol and we need to use that here:
-      result = newSymG(kind, n.sons[1], c)
-      var v = considerQuotedIdent(c, n.sons[0])
+      result = newSymG(kind, n[1], c)
+      var v = considerQuotedIdent(c, n[0])
       if sfExported in allowed and v.id == ord(wStar):
         incl(result.flags, sfExported)
       else:
         if not (sfExported in allowed):
-          localError(c.config, n.sons[0].info, errXOnlyAtModuleScope % "export")
+          localError(c.config, n[0].info, errXOnlyAtModuleScope % "export")
         else:
-          localError(c.config, n.sons[0].info, errInvalidVisibilityX % renderTree(n[0]))
+          localError(c.config, n[0].info, errInvalidVisibilityX % renderTree(n[0]))
     else:
+      result = nil
       illFormedAst(n, c.config)
   else:
     result = newSymG(kind, n, c)
 
 proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
-                        allowed: TSymFlags): PSym =
+                        allowed: TSymFlags, fromTopLevel = false): PSym =
   if n.kind == nkPragmaExpr:
     checkSonsLen(n, 2, c.config)
-    result = semIdentVis(c, kind, n.sons[0], allowed)
+    result = semIdentVis(c, kind, n[0], allowed)
     case kind
     of skType:
       # process pragmas later, because result.typ has not been set yet
       discard
-    of skField: pragma(c, result, n.sons[1], fieldPragmas)
-    of skVar:   pragma(c, result, n.sons[1], varPragmas)
-    of skLet:   pragma(c, result, n.sons[1], letPragmas)
-    of skConst: pragma(c, result, n.sons[1], constPragmas)
+    of skField: pragma(c, result, n[1], fieldPragmas)
+    of skVar:   pragma(c, result, n[1], varPragmas)
+    of skLet:   pragma(c, result, n[1], letPragmas)
+    of skConst: pragma(c, result, n[1], constPragmas)
     else: discard
   else:
     result = semIdentVis(c, kind, n, allowed)
-  styleCheckDef(c.config, n.info, result)
+    let invalidPragmasForPush = if fromTopLevel and sfWasGenSym notin result.flags:
+      {}
+    else:
+      {wExportc, wExportCpp, wDynlib}
+    case kind
+    of skField: implicitPragmas(c, result, n.info, fieldPragmas)
+    of skVar:   implicitPragmas(c, result, n.info, varPragmas-invalidPragmasForPush)
+    of skLet:   implicitPragmas(c, result, n.info, letPragmas-invalidPragmasForPush)
+    of skConst: implicitPragmas(c, result, n.info, constPragmas-invalidPragmasForPush)
+    else: discard
 
 proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) =
   let ex = t[branchIndex][currentEx].skipConv
-  for i in countup(1, branchIndex):
-    for j in countup(0, sonsLen(t.sons[i]) - 2):
+  for i in 1..branchIndex:
+    for j in 0..<t[i].len - 1:
       if i == branchIndex and j == currentEx: break
-      if overlap(t.sons[i].sons[j].skipConv, ex):
+      if overlap(t[i][j].skipConv, ex):
         localError(c.config, ex.info, errDuplicateCaseLabel)
 
-proc semBranchRange(c: PContext, t, a, b: PNode, covered: var BiggestInt): PNode =
-  checkMinSonsLen(t, 1, c.config)
+proc semBranchRange(c: PContext, n, a, b: PNode, covered: var Int128): PNode =
+  checkMinSonsLen(n, 1, c.config)
   let ac = semConstExpr(c, a)
   let bc = semConstExpr(c, b)
-  let at = fitNode(c, t.sons[0].typ, ac, ac.info).skipConvTakeType
-  let bt = fitNode(c, t.sons[0].typ, bc, bc.info).skipConvTakeType
-
+  if ac.kind in {nkStrLit..nkTripleStrLit} or bc.kind in {nkStrLit..nkTripleStrLit}:
+    localError(c.config, b.info, "range of string is invalid")
+  var at = fitNode(c, n[0].typ, ac, ac.info).skipConvTakeType
+  var bt = fitNode(c, n[0].typ, bc, bc.info).skipConvTakeType
+  # the calls to fitNode may introduce calls to converters
+  # mirrored with semCaseBranch for single elements
+  if at.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
+    at = semConstExpr(c, at)
+  if bt.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
+    bt = semConstExpr(c, bt)
   result = newNodeI(nkRange, a.info)
   result.add(at)
   result.add(bt)
   if emptyRange(ac, bc): localError(c.config, b.info, "range is empty")
-  else: covered = covered + getOrdValue(bc) - getOrdValue(ac) + 1
+  else: covered = covered + getOrdValue(bc) + 1 - getOrdValue(ac)
 
 proc semCaseBranchRange(c: PContext, t, b: PNode,
-                        covered: var BiggestInt): PNode =
+                        covered: var Int128): PNode =
   checkSonsLen(b, 3, c.config)
-  result = semBranchRange(c, t, b.sons[1], b.sons[2], covered)
+  result = semBranchRange(c, t, b[1], b[2], covered)
 
-proc semCaseBranchSetElem(c: PContext, t, b: PNode,
-                          covered: var BiggestInt): PNode =
+proc semCaseBranchSetElem(c: PContext, n, b: PNode,
+                          covered: var Int128): PNode =
   if isRange(b):
     checkSonsLen(b, 3, c.config)
-    result = semBranchRange(c, t, b.sons[1], b.sons[2], covered)
+    result = semBranchRange(c, n, b[1], b[2], covered)
   elif b.kind == nkRange:
     checkSonsLen(b, 2, c.config)
-    result = semBranchRange(c, t, b.sons[0], b.sons[1], covered)
+    result = semBranchRange(c, n, b[0], b[1], covered)
   else:
-    result = fitNode(c, t.sons[0].typ, b, b.info)
+    result = fitNode(c, n[0].typ, b, b.info)
     inc(covered)
 
-proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
-                   covered: var BiggestInt) =
-  let lastIndex = sonsLen(branch) - 2
+proc semCaseBranch(c: PContext, n, branch: PNode, branchIndex: int,
+                   covered: var Int128) =
+  let lastIndex = branch.len - 2
   for i in 0..lastIndex:
-    var b = branch.sons[i]
+    var b = branch[i]
     if b.kind == nkRange:
-      branch.sons[i] = b
+      branch[i] = b
+      # same check as in semBranchRange for exhaustiveness
+      covered = covered + getOrdValue(b[1]) + 1 - getOrdValue(b[0])
     elif isRange(b):
-      branch.sons[i] = semCaseBranchRange(c, t, b, covered)
+      branch[i] = semCaseBranchRange(c, n, b, covered)
     else:
       # constant sets and arrays are allowed:
-      var r = semConstExpr(c, b)
-      if r.kind in {nkCurly, nkBracket} and len(r) == 0  and sonsLen(branch)==2:
+      # set expected type to selector type for type inference
+      # even if it can be a different type like a set or array
+      var r = semConstExpr(c, b, expectedType = n[0].typ)
+      if r.kind in {nkCurly, nkBracket} and r.len == 0 and branch.len == 2:
         # discarding ``{}`` and ``[]`` branches silently
         delSon(branch, 0)
         return
-      elif r.kind notin {nkCurly, nkBracket} or len(r) == 0:
-        checkMinSonsLen(t, 1, c.config)
-        branch.sons[i] = skipConv(fitNode(c, t.sons[0].typ, r, r.info))
+      elif r.kind notin {nkCurly, nkBracket} or r.len == 0:
+        checkMinSonsLen(n, 1, c.config)
+        var tmp = fitNode(c, n[0].typ, r, r.info)
+        # the call to fitNode may introduce a call to a converter
+        # mirrored with semBranchRange
+        if tmp.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
+          tmp = semConstExpr(c, tmp)
+        branch[i] = skipConv(tmp)
         inc(covered)
       else:
         if r.kind == nkCurly:
           r = deduplicate(c.config, r)
 
-        # first element is special and will overwrite: branch.sons[i]:
-        branch.sons[i] = semCaseBranchSetElem(c, t, r[0], covered)
+        # first element is special and will overwrite: branch[i]:
+        branch[i] = semCaseBranchSetElem(c, n, r[0], covered)
 
         # other elements have to be added to ``branch``
-        for j in 1 ..< r.len:
-          branch.add(semCaseBranchSetElem(c, t, r[j], covered))
+        for j in 1..<r.len:
+          branch.add(semCaseBranchSetElem(c, n, r[j], covered))
           # caution! last son of branch must be the actions to execute:
-          swap(branch.sons[^2], branch.sons[^1])
-    checkForOverlap(c, t, i, branchIndex)
+          swap(branch[^2], branch[^1])
+    checkForOverlap(c, n, i, branchIndex)
 
   # Elements added above needs to be checked for overlaps.
-  for i in lastIndex.succ..(sonsLen(branch) - 2):
-    checkForOverlap(c, t, i, branchIndex)
+  for i in lastIndex.succ..<branch.len - 1:
+    checkForOverlap(c, n, i, branchIndex)
+
+proc toCover(c: PContext, t: PType): Int128 =
+  let t2 = skipTypes(t, abstractVarRange-{tyTypeDesc})
+  if t2.kind == tyEnum and enumHasHoles(t2):
+    result = toInt128(t2.n.len)
+  else:
+    # <----
+    let t = skipTypes(t, abstractVar-{tyTypeDesc})
+    # XXX: hack incoming. lengthOrd is incorrect for 64bit integer
+    # types because it doesn't uset Int128 yet.  This entire branching
+    # should be removed as soon as lengthOrd uses int128.
+    if t.kind in {tyInt64, tyUInt64}:
+      result = toInt128(1) shl 64
+    elif t.kind in {tyInt, tyUInt}:
+      result = toInt128(1) shl (c.config.target.intSize * 8)
+    else:
+      result = lengthOrd(c.config, t)
 
 proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
-                      father: PNode, rectype: PType)
+                      father: PNode, rectype: PType, hasCaseFields = false)
+
+proc getIntSetOfType(c: PContext, t: PType): IntSet =
+  result = initIntSet()
+  if t.enumHasHoles:
+    let t = t.skipTypes(abstractRange)
+    for field in t.n.sons:
+      result.incl(field.sym.position)
+  else:
+    assert(lengthOrd(c.config, t) <= BiggestInt(MaxSetElements))
+    for i in toInt64(firstOrd(c.config, t))..toInt64(lastOrd(c.config, t)):
+      result.incl(i.int)
+
+iterator processBranchVals(b: PNode): int =
+  assert b.kind in {nkOfBranch, nkElifBranch, nkElse}
+  if b.kind == nkOfBranch:
+    for i in 0..<b.len-1:
+      if b[i].kind in {nkIntLit, nkCharLit}:
+        yield b[i].intVal.int
+      elif b[i].kind == nkRange:
+        for i in b[i][0].intVal..b[i][1].intVal:
+          yield i.int
+
+proc renderAsType(vals: IntSet, t: PType): string =
+  result = "{"
+  let t = t.skipTypes(abstractRange)
+  var enumSymOffset = 0
+  var i = 0
+  for val in vals:
+    if result.len > 1:
+      result &= ", "
+    case t.kind:
+    of tyEnum, tyBool:
+      while t.n[enumSymOffset].sym.position < val: inc(enumSymOffset)
+      result &= t.n[enumSymOffset].sym.name.s
+    of tyChar:
+      result.addQuoted(char(val))
+    else:
+      if i == 64:
+        result &= "omitted $1 values..." % $(vals.len - i)
+        break
+      else:
+        result &= $val
+    inc(i)
+  result &= "}"
+
+proc formatMissingEnums(c: PContext, n: PNode): string =
+  var coveredCases = initIntSet()
+  for i in 1..<n.len:
+    for val in processBranchVals(n[i]):
+      coveredCases.incl val
+  result = (c.getIntSetOfType(n[0].typ) - coveredCases).renderAsType(n[0].typ)
+
 proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
                    father: PNode, rectype: PType) =
   var a = copyNode(n)
   checkMinSonsLen(n, 2, c.config)
-  semRecordNodeAux(c, n.sons[0], check, pos, a, rectype)
-  if a.sons[0].kind != nkSym:
+  semRecordNodeAux(c, n[0], check, pos, a, rectype, hasCaseFields = true)
+  if a[0].kind != nkSym:
     internalError(c.config, "semRecordCase: discriminant is no symbol")
     return
-  incl(a.sons[0].sym.flags, sfDiscriminant)
-  var covered: BiggestInt = 0
-  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 &
+  incl(a[0].sym.flags, sfDiscriminant)
+  var covered = toInt128(0)
+  var chckCovered = false
+  var typ = skipTypes(a[0].typ, abstractVar-{tyTypeDesc})
+  const shouldChckCovered = {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool}
+  case typ.kind
+  of shouldChckCovered:
+    chckCovered = true
+  of tyFloat..tyFloat128, tyError:
+    discard
+  of tyRange:
+    if skipTypes(typ.elementType, abstractInst).kind in shouldChckCovered:
+      chckCovered = true
+  of tyForward:
+    errorUndeclaredIdentifier(c, n[0].info, typ.sym.name.s)
+  elif not isOrdinalType(typ):
+    localError(c.config, n[0].info, "selector must be of an ordinal type, float")
+  if firstOrd(c.config, typ) != 0:
+    localError(c.config, n.info, "low(" & $a[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
-  for i in countup(1, sonsLen(n) - 1):
-    var b = copyTree(n.sons[i])
-    addSon(a, b)
-    case n.sons[i].kind
+    localError(c.config, n.info, "len($1) must be less than 32768" % a[0].sym.name.s)
+
+  for i in 1..<n.len:
+    var b = copyTree(n[i])
+    a.add b
+    case n[i].kind
     of nkOfBranch:
       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[0].typ):
+        message(c.config, b.info, warnUnreachableElse)
+      chckCovered = false
     else: illFormedAst(n, c.config)
-    delSon(b, sonsLen(b) - 1)
-    semRecordNodeAux(c, lastSon(n.sons[i]), check, pos, b, rectype)
-  if chckCovered and covered != lengthOrd(c.config, a.sons[0].typ):
-    localError(c.config, a.info, "not all cases are covered")
-  addSon(father, a)
+    delSon(b, b.len - 1)
+    semRecordNodeAux(c, lastSon(n[i]), check, pos, b, rectype, hasCaseFields = true)
+  if chckCovered and covered != toCover(c, a[0].typ):
+    if a[0].typ.skipTypes(abstractRange).kind == tyEnum:
+      localError(c.config, a.info, "not all cases are covered; missing: $1" %
+                 formatMissingEnums(c, a))
+    else:
+      localError(c.config, a.info, "not all cases are covered")
+  father.add a
 
 proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
-                      father: PNode, rectype: PType) =
+                      father: PNode, rectype: PType, hasCaseFields: bool) =
   if n == nil: return
   case n.kind
   of nkRecWhen:
+    var a = copyTree(n)
     var branch: PNode = nil   # the branch to take
-    for i in countup(0, sonsLen(n) - 1):
-      var it = n.sons[i]
+    var cannotResolve = false # no branch should be taken
+    for i in 0..<a.len:
+      var it = a[i]
       if it == nil: illFormedAst(n, c.config)
       var idx = 1
       case it.kind
       of nkElifBranch:
         checkSonsLen(it, 2, c.config)
         if c.inGenericContext == 0:
-          var e = semConstBoolExpr(c, it.sons[0])
-          if e.kind != nkIntLit: internalError(c.config, e.info, "semRecordNodeAux")
-          elif e.intVal != 0 and branch == nil: branch = it.sons[1]
+          var e = semConstBoolExpr(c, it[0])
+          if e.kind != nkIntLit: discard "don't report followup error"
+          elif e.intVal != 0 and branch == nil: branch = it[1]
         else:
-          it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
+          # XXX this is still a hard compilation in a generic context, this can
+          # result in unresolved generic parameters being treated like real types
+          let e = semExprWithType(c, it[0], {efDetermineType})
+          if e.typ.kind == tyFromExpr:
+            it[0] = makeStaticExpr(c, e)
+            cannotResolve = true
+          else:
+            it[0] = forceBool(c, e)
+            let val = getConstExpr(c.module, it[0], c.idgen, c.graph)
+            if val == nil or val.kind != nkIntLit:
+              cannotResolve = true
+            elif not cannotResolve and val.intVal != 0 and branch == nil:
+              branch = it[1]
       of nkElse:
         checkSonsLen(it, 1, c.config)
-        if branch == nil: branch = it.sons[0]
+        if branch == nil and not cannotResolve: branch = it[0]
         idx = 0
       else: illFormedAst(n, c.config)
-      if c.inGenericContext > 0:
+      if c.inGenericContext > 0 and cannotResolve:
         # use a new check intset here for each branch:
-        var newCheck: IntSet
-        assign(newCheck, check)
+        var newCheck: IntSet = check
         var newPos = pos
         var newf = newNodeI(nkRecList, n.info)
-        semRecordNodeAux(c, it.sons[idx], newCheck, newPos, newf, rectype)
-        it.sons[idx] = if newf.len == 1: newf[0] else: newf
-    if c.inGenericContext > 0:
-      addSon(father, n)
-    elif branch != nil:
-      semRecordNodeAux(c, branch, check, pos, father, rectype)
+        semRecordNodeAux(c, it[idx], newCheck, newPos, newf, rectype, hasCaseFields)
+        it[idx] = if newf.len == 1: newf[0] else: newf
+    if branch != nil:
+      semRecordNodeAux(c, branch, check, pos, father, rectype, hasCaseFields)
+    elif cannotResolve:
+      father.add a
+    elif father.kind in {nkElse, nkOfBranch}:
+      father.add newNodeI(nkRecList, n.info)
   of nkRecCase:
     semRecordCase(c, n, check, pos, father, rectype)
   of nkNilLit:
-    if father.kind != nkRecList: addSon(father, newNodeI(nkRecList, n.info))
+    if father.kind != nkRecList: father.add newNodeI(nkRecList, n.info)
   of nkRecList:
     # attempt to keep the nesting at a sane level:
     var a = if father.kind == nkRecList: father else: copyNode(n)
-    for i in countup(0, sonsLen(n) - 1):
-      semRecordNodeAux(c, n.sons[i], check, pos, a, rectype)
-    if a != father: addSon(father, a)
+    for i in 0..<n.len:
+      semRecordNodeAux(c, n[i], check, pos, a, rectype, hasCaseFields)
+    if a != father: father.add a
   of nkIdentDefs:
     checkMinSonsLen(n, 3, c.config)
-    var length = sonsLen(n)
     var a: PNode
-    if father.kind != nkRecList and length>=4: a = newNodeI(nkRecList, n.info)
+    if father.kind != nkRecList and n.len >= 4: a = newNodeI(nkRecList, n.info)
     else: a = newNodeI(nkEmpty, n.info)
-    if n.sons[length-1].kind != nkEmpty:
-      localError(c.config, n.sons[length-1].info, errInitHereNotAllowed)
     var typ: PType
-    if n.sons[length-2].kind == nkEmpty:
+    var hasDefaultField = n[^1].kind != nkEmpty
+    if hasDefaultField:
+      typ = fitDefaultNode(c, n)
+      propagateToOwner(rectype, typ)
+    elif n[^2].kind == nkEmpty:
       localError(c.config, n.info, errTypeExpected)
       typ = errorType(c)
     else:
-      typ = semTypeNode(c, n.sons[length-2], nil)
+      typ = semTypeNode(c, n[^2], nil)
+      if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
+        n[^1] = firstRange(c.config, typ)
+        hasDefaultField = true
       propagateToOwner(rectype, typ)
     var fieldOwner = if c.inGenericContext > 0: c.getCurrOwner
                      else: rectype.sym
-    for i in countup(0, sonsLen(n)-3):
-      var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported})
-      suggestSym(c.config, n.sons[i].info, f, c.graph.usageSym)
+    for i in 0..<n.len-2:
+      var f = semIdentWithPragma(c, skField, n[i], {sfExported})
+      let info = if n[i].kind == nkPostfix:
+                   n[i][1].info
+                 else:
+                   n[i].info
+      suggestSym(c.graph, info, f, c.graph.usageSym)
       f.typ = typ
       f.position = pos
+      f.options = c.config.options
       if fieldOwner != nil and
          {sfImportc, sfExportc} * fieldOwner.flags != {} and
-         f.loc.r == nil:
-        f.loc.r = rope(f.name.s)
-        f.flags = f.flags + ({sfImportc, sfExportc} * fieldOwner.flags)
+         not hasCaseFields and f.loc.snippet == "":
+        f.loc.snippet = rope(f.name.s)
+        f.flags.incl {sfImportc, sfExportc} * fieldOwner.flags
       inc(pos)
       if containsOrIncl(check, f.name.id):
-        localError(c.config, n.sons[i].info, "attempt to redefine: '" & f.name.s & "'")
-      if a.kind == nkEmpty: addSon(father, newSymNode(f))
-      else: addSon(a, newSymNode(f))
-      styleCheckDef(c.config, f)
-    if a.kind != nkEmpty: addSon(father, a)
+        localError(c.config, info, "attempt to redefine: '" & f.name.s & "'")
+      let fSym = newSymNode(f)
+      if hasDefaultField:
+        fSym.sym.ast = n[^1]
+        fSym.sym.ast.flags.incl nfSkipFieldChecking
+      if a.kind == nkEmpty: father.add fSym
+      else: a.add fSym
+      styleCheckDef(c, f)
+      onDef(f.info, f)
+    if a.kind != nkEmpty: father.add a
   of nkSym:
     # This branch only valid during generic object
     # inherited from generic/partial specialized parent second check.
     # There is no branch validity check here
     if containsOrIncl(check, n.sym.name.id):
       localError(c.config, n.info, "attempt to redefine: '" & n.sym.name.s & "'")
-    addSon(father, n)
-  of nkEmpty: discard
+    father.add n
+  of nkEmpty:
+    if father.kind in {nkElse, nkOfBranch}:
+      father.add n
   else: illFormedAst(n, c.config)
 
 proc addInheritedFieldsAux(c: PContext, check: var IntSet, pos: var int,
                            n: PNode) =
   case n.kind
   of nkRecCase:
-    if (n.sons[0].kind != nkSym): internalError(c.config, n.info, "addInheritedFieldsAux")
-    addInheritedFieldsAux(c, check, pos, n.sons[0])
-    for i in countup(1, sonsLen(n) - 1):
-      case n.sons[i].kind
+    if (n[0].kind != nkSym): internalError(c.config, n.info, "addInheritedFieldsAux")
+    addInheritedFieldsAux(c, check, pos, n[0])
+    for i in 1..<n.len:
+      case n[i].kind
       of nkOfBranch, nkElse:
-        addInheritedFieldsAux(c, check, pos, lastSon(n.sons[i]))
+        addInheritedFieldsAux(c, check, pos, lastSon(n[i]))
       else: internalError(c.config, n.info, "addInheritedFieldsAux(record case branch)")
-  of nkRecList:
-    for i in countup(0, sonsLen(n) - 1):
-      addInheritedFieldsAux(c, check, pos, n.sons[i])
+  of nkRecList, nkRecWhen, nkElifBranch, nkElse:
+    for i in int(n.kind == nkElifBranch)..<n.len:
+      addInheritedFieldsAux(c, check, pos, n[i])
   of nkSym:
     incl(check, n.sym.name.id)
     inc(pos)
@@ -718,27 +932,35 @@ proc addInheritedFieldsAux(c: PContext, check: var IntSet, pos: var int,
 proc skipGenericInvocation(t: PType): PType {.inline.} =
   result = t
   if result.kind == tyGenericInvocation:
-    result = result.sons[0]
-  while result.kind in {tyGenericInst, tyGenericBody, tyRef, tyPtr, tyAlias, tySink}:
-    result = lastSon(result)
-
-proc addInheritedFields(c: PContext, check: var IntSet, pos: var int,
-                        obj: PType) =
-  assert obj.kind == tyObject
-  if (sonsLen(obj) > 0) and (obj.sons[0] != nil):
-    addInheritedFields(c, check, pos, obj.sons[0].skipGenericInvocation)
-  addInheritedFieldsAux(c, check, pos, obj.n)
-
-proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
-  if n.sonsLen == 0:
+    result = result[0]
+  while result.kind in {tyGenericInst, tyGenericBody, tyRef, tyPtr, tyAlias, tySink, tyOwned}:
+    result = skipModifier(result)
+
+proc tryAddInheritedFields(c: PContext, check: var IntSet, pos: var int,
+                        obj: PType, n: PNode, isPartial = false, innerObj: PType = nil): bool =
+  if ((not isPartial) and (obj.kind notin {tyObject, tyGenericParam} or tfFinal in obj.flags)) or
+    (innerObj != nil and obj.sym.id == innerObj.sym.id):
+    localError(c.config, n.info, "Cannot inherit from: '" & $obj & "'")
+    result = false
+  elif obj.kind == tyObject:
+    result = true
+    if (obj.len > 0) and (obj[0] != nil):
+      result = result and tryAddInheritedFields(c, check, pos, obj[0].skipGenericInvocation, n, false, obj)
+    addInheritedFieldsAux(c, check, pos, obj.n)
+  else:
+    result = true
+
+proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType =
+  result = nil
+  if n.len == 0:
     return newConstraint(c, tyObject)
   var check = initIntSet()
   var pos = 0
   var base, realBase: PType = nil
-  # n.sons[0] contains the pragmas (if any). We process these later...
+  # n[0] contains the pragmas (if any). We process these later...
   checkSonsLen(n, 3, c.config)
-  if n.sons[1].kind != nkEmpty:
-    realBase = semTypeNode(c, n.sons[1].sons[0], nil)
+  if n[1].kind != nkEmpty:
+    realBase = semTypeNode(c, n[1][0], nil)
     base = skipTypesOrNil(realBase, skipPtrs)
     if base.isNil:
       localError(c.config, n.info, "cannot inherit from a type that is not an object type")
@@ -751,37 +973,107 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
         # specialized object, there will be second check after instantiation
         # located in semGeneric.
         if concreteBase.kind == tyObject:
-          addInheritedFields(c, check, pos, concreteBase)
+          if concreteBase.sym != nil and concreteBase.sym.magic == mException and
+              sfSystemModule notin c.module.flags:
+            message(c.config, n.info, warnInheritFromException, "")
+          if not tryAddInheritedFields(c, check, pos, concreteBase, n):
+            return newType(tyError, c.idgen, result.owner)
+
+      elif concreteBase.kind == tyForward:
+        c.skipTypes.add n #we retry in the final pass
       else:
         if concreteBase.kind != tyError:
-          localError(c.config, n.sons[1].info, "inheritance only works with non-final objects; " &
-             "to enable inheritance write '" & typeToString(realBase) & " of RootObj'")
+          localError(c.config, n[1].info, "inheritance only works with non-final objects; " &
+             "for " & typeToString(realBase) & " to be inheritable it must be " &
+             "'object of RootObj' instead of 'object'")
         base = nil
         realBase = nil
   if n.kind != nkObjectTy: internalError(c.config, n.info, "semObjectNode")
   result = newOrPrevType(tyObject, prev, c)
   rawAddSon(result, realBase)
+  if realBase == nil and tfInheritable in flags:
+    result.flags.incl tfInheritable
+  if tfAcyclic in flags: result.flags.incl tfAcyclic
   if result.n.isNil:
     result.n = newNodeI(nkRecList, n.info)
   else:
     # partial object so add things to the check
-    addInheritedFields(c, check, pos, result)
-  semRecordNodeAux(c, n.sons[2], check, pos, result.n, result)
-  if n.sons[0].kind != nkEmpty:
+    if not tryAddInheritedFields(c, check, pos, result, n, isPartial = true):
+      return newType(tyError, c.idgen, result.owner)
+
+  semRecordNodeAux(c, n[2], check, pos, result.n, result)
+  if n[0].kind != nkEmpty:
     # dummy symbol for `pragma`:
     var s = newSymS(skType, newIdentNode(getIdent(c.cache, "dummy"), n.info), c)
     s.typ = result
-    pragma(c, s, n.sons[0], typePragmas)
+    pragma(c, s, n[0], typePragmas)
   if base == nil and tfInheritable notin result.flags:
     incl(result.flags, tfFinal)
+  if c.inGenericContext == 0 and computeRequiresInit(c, result):
+    result.flags.incl tfRequiresInit
+
+proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
+  if n.len < 1:
+    result = newConstraint(c, kind)
+  else:
+    let isCall = int ord(n.kind in nkCallKinds+{nkBracketExpr})
+    let n = if n[0].kind == nkBracket: n[0] else: n
+    checkMinSonsLen(n, 1, c.config)
+    let body = n.lastSon
+    var t = if prev != nil and prev.kind != tyGenericBody and body.kind == nkObjectTy:
+              semObjectNode(c, body, nil, prev.flags)
+            else:
+              semTypeNode(c, body, nil)
+    if t.kind == tyTypeDesc and tfUnresolved notin t.flags:
+      t = t.base
+    if t.kind == tyVoid:
+      localError(c.config, n.info, "type '$1 void' is not allowed" % kind.toHumanStr)
+    result = newOrPrevType(kind, prev, c)
+    var isNilable = false
+    var wrapperKind = tyNone
+    # check every except the last is an object:
+    for i in isCall..<n.len-1:
+      let ni = n[i]
+      # echo "semAnyRef ", "n: ", n, "i: ", i, "ni: ", ni
+      if ni.kind == nkNilLit:
+        isNilable = true
+      else:
+        let region = semTypeNode(c, ni, nil)
+        if region.kind in {tyOwned, tySink}:
+          wrapperKind = region.kind
+        elif region.skipTypes({tyGenericInst, tyAlias, tySink}).kind notin {
+              tyError, tyObject}:
+          message c.config, n[i].info, errGenerated, "region needs to be an object type"
+          addSonSkipIntLit(result, region, c.idgen)
+        else:
+          message(c.config, n.info, warnDeprecated, "region for pointer types is deprecated")
+          addSonSkipIntLit(result, region, c.idgen)
+    addSonSkipIntLit(result, t, c.idgen)
+    if tfPartial in result.flags:
+      if result.elementType.kind == tyObject: incl(result.elementType.flags, tfPartial)
+    # if not isNilable: result.flags.incl tfNotNil
+    case wrapperKind
+    of tyOwned:
+      if optOwnedRefs in c.config.globalOptions:
+        let t = newTypeS(tyOwned, c, result)
+        t.flags.incl tfHasOwned
+        result = t
+    of tySink:
+      let t = newTypeS(tySink, c, result)
+      result = t
+    else: discard
+    if result.kind == tyRef and c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
+      result.flags.incl tfHasAsgn
 
 proc findEnforcedStaticType(t: PType): PType =
   # This handles types such as `static[T] and Foo`,
   # which are subset of `static[T]`, hence they could
   # be treated in the same way
+  result = nil
+  if t == nil: return nil
   if t.kind == tyStatic: return t
   if t.kind == tyAnd:
-    for s in t.sons:
+    for s in t.kids:
       let t = findEnforcedStaticType(s)
       if t != nil: return t
 
@@ -789,196 +1081,225 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
   if kind == skMacro:
     let staticType = findEnforcedStaticType(param.typ)
     if staticType != nil:
-      var a = copySym(param)
+      var a = copySym(param, c.idgen)
       a.typ = staticType.base
       addDecl(c, a)
-    elif param.typ.kind == tyTypeDesc:
-      addDecl(c, param)
+      #elif param.typ != nil and param.typ.kind == tyTypeDesc:
+      #  addDecl(c, param)
     else:
       # within a macro, every param has the type NimNode!
       let nn = getSysSym(c.graph, param.info, "NimNode")
-      var a = copySym(param)
+      var a = copySym(param, c.idgen)
       a.typ = nn.typ
       addDecl(c, a)
   else:
-    if sfGenSym notin param.flags: addDecl(c, param)
+    if sfGenSym in param.flags:
+      # bug #XXX, fix the gensym'ed parameters owner:
+      if param.owner == nil:
+        param.owner = getCurrOwner(c)
+    else: addDecl(c, param)
 
 template shouldHaveMeta(t) =
   internalAssert c.config, tfHasMeta in t.flags
   # result.lastSon.flags.incl tfHasMeta
 
+proc addImplicitGeneric(c: PContext; typeClass: PType, typId: PIdent;
+                        info: TLineInfo; genericParams: PNode;
+                        paramName: string): PType =
+  if genericParams == nil:
+    # This happens with anonymous proc types appearing in signatures
+    # XXX: we need to lift these earlier
+    return
+  let finalTypId = if typId != nil: typId
+                    else: getIdent(c.cache, paramName & ":type")
+  # is this a bindOnce type class already present in the param list?
+  for i in 0..<genericParams.len:
+    if genericParams[i].sym.name.id == finalTypId.id:
+      return genericParams[i].typ
+
+  let owner = if typeClass.sym != nil: typeClass.sym
+              else: getCurrOwner(c)
+  var s = newSym(skType, finalTypId, c.idgen, owner, info)
+  if sfExplain in owner.flags: s.flags.incl sfExplain
+  if typId == nil: s.flags.incl(sfAnon)
+  s.linkTo(typeClass)
+  typeClass.flags.incl tfImplicitTypeParam
+  s.position = genericParams.len
+  genericParams.add newSymNode(s)
+  result = typeClass
+  addDecl(c, s)
+
 proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
                    paramType: PType, paramName: string,
                    info: TLineInfo, anon = false): PType =
   if paramType == nil: return # (e.g. proc return type)
 
-  proc addImplicitGenericImpl(c: PContext; typeClass: PType, typId: PIdent): PType =
-    if genericParams == nil:
-      # This happens with anonymous proc types appearing in signatures
-      # XXX: we need to lift these earlier
-      return
-    let finalTypId = if typId != nil: typId
-                     else: getIdent(c.cache, paramName & ":type")
-    # is this a bindOnce type class already present in the param list?
-    for i in countup(0, genericParams.len - 1):
-      if genericParams.sons[i].sym.name.id == finalTypId.id:
-        return genericParams.sons[i].typ
-
-    let owner = if typeClass.sym != nil: typeClass.sym
-                else: getCurrOwner(c)
-    var s = newSym(skType, finalTypId, owner, info)
-    if sfExplain in owner.flags: s.flags.incl sfExplain
-    if typId == nil: s.flags.incl(sfAnon)
-    s.linkTo(typeClass)
-    typeClass.flags.incl tfImplicitTypeParam
-    s.position = genericParams.len
-    genericParams.addSon(newSymNode(s))
-    result = typeClass
-    addDecl(c, s)
-
-  # XXX: There are codegen errors if this is turned into a nested proc
-  template liftingWalk(typ: PType, anonFlag = false): untyped =
+  template recurse(typ: PType, anonFlag = false): untyped =
     liftParamType(c, procKind, genericParams, typ, paramName, info, anonFlag)
-  #proc liftingWalk(paramType: PType, anon = false): PType =
 
   var paramTypId = if not anon and paramType.sym != nil: paramType.sym.name
                    else: nil
 
-  template maybeLift(typ: PType): untyped =
-    let lifted = liftingWalk(typ)
-    (if lifted != nil: lifted else: typ)
-
-  template addImplicitGeneric(e): untyped =
-    addImplicitGenericImpl(c, e, paramTypId)
-
-  case paramType.kind:
+  case paramType.kind
   of tyAnything:
-    result = addImplicitGenericImpl(c, newTypeS(tyGenericParam, c), nil)
+    result = addImplicitGeneric(c, newTypeS(tyGenericParam, c), nil, info, genericParams, paramName)
 
   of tyStatic:
-    # proc(a: expr{string}, b: expr{nkLambda})
-    # overload on compile time values and AST trees
-    if paramType.n != nil: return # this is a concrete type
+    if paramType.base.kind != tyNone and paramType.n != nil:
+      # this is a concrete static value
+      return
     if tfUnresolved in paramType.flags: return # already lifted
-    let base = paramType.base.maybeLift
+
+    let lifted = recurse(paramType.base)
+    let base = (if lifted != nil: lifted else: paramType.base)
     if base.isMetaType and procKind == skMacro:
       localError(c.config, info, errMacroBodyDependsOnGenericTypes % paramName)
-    result = addImplicitGeneric(c.newTypeWithSons(tyStatic, @[base]))
-    result.flags.incl({tfHasStatic, tfUnresolved})
+    result = addImplicitGeneric(c, newTypeS(tyStatic, c, base),
+        paramTypId, info, genericParams, paramName)
+    if result != nil: result.flags.incl({tfHasStatic, tfUnresolved})
 
   of tyTypeDesc:
     if tfUnresolved notin paramType.flags:
       # naked typedescs are not bindOnce types
       if paramType.base.kind == tyNone and paramTypId != nil and
-          paramTypId.id == getIdent(c.cache, "typedesc").id:
+          (paramTypId.id == getIdent(c.cache, "typedesc").id or
+          paramTypId.id == getIdent(c.cache, "type").id):
         # XXX Why doesn't this check for tyTypeDesc instead?
         paramTypId = nil
-      result = addImplicitGeneric(
-        c.newTypeWithSons(tyTypeDesc, @[paramType.base]))
-
+      let t = newTypeS(tyTypeDesc, c, paramType.base)
+      incl t.flags, tfCheckedForDestructor
+      result = addImplicitGeneric(c, t, paramTypId, info, genericParams, paramName)
+    else:
+      result = nil
   of tyDistinct:
-    if paramType.sonsLen == 1:
+    if paramType.len == 1:
       # disable the bindOnce behavior for the type class
-      result = liftingWalk(paramType.sons[0], true)
+      result = recurse(paramType.base, true)
+    else:
+      result = nil
+  of tyTuple:
+    result = nil
+    for i in 0..<paramType.len:
+      let t = recurse(paramType[i])
+      if t != nil:
+        paramType[i] = t
+        result = paramType
+
+  of tyAlias, tyOwned:
+    result = recurse(paramType.base)
 
   of tySequence, tySet, tyArray, tyOpenArray,
-     tyVar, tyLent, tyPtr, tyRef, tyProc:
+     tyVar, tyLent, tyPtr, tyRef, tyProc, tySink:
     # XXX: this is a bit strange, but proc(s: seq)
     # produces tySequence(tyGenericParam, tyNone).
     # This also seems to be true when creating aliases
     # like: type myseq = distinct seq.
     # Maybe there is another better place to associate
     # the seq type class with the seq identifier.
-    if paramType.kind == tySequence and paramType.lastSon.kind == tyNone:
-      let typ = c.newTypeWithSons(tyBuiltInTypeClass,
-                                  @[newTypeS(paramType.kind, c)])
-      result = addImplicitGeneric(typ)
+    if paramType.kind == tySequence and paramType.elementType.kind == tyNone:
+      let typ = newTypeS(tyBuiltInTypeClass, c,
+                         newTypeS(paramType.kind, c))
+      result = addImplicitGeneric(c, typ, paramTypId, info, genericParams, paramName)
     else:
-      for i in 0 ..< paramType.len:
-        if paramType.sons[i] == paramType:
+      result = nil
+      for i in 0..<paramType.len:
+        if paramType[i] == paramType:
           globalError(c.config, info, errIllegalRecursionInTypeX % typeToString(paramType))
-        var lifted = liftingWalk(paramType.sons[i])
+        var lifted = recurse(paramType[i])
         if lifted != nil:
-          paramType.sons[i] = lifted
+          paramType[i] = lifted
           result = paramType
 
   of tyGenericBody:
     result = newTypeS(tyGenericInvocation, c)
     result.rawAddSon(paramType)
 
-    for i in 0 .. paramType.sonsLen - 2:
-      if paramType.sons[i].kind == tyStatic:
-        var staticCopy = paramType.sons[i].exactReplica
+    for i in 0..<paramType.len - 1:
+      if paramType[i].kind == tyStatic:
+        var staticCopy = paramType[i].exactReplica
         staticCopy.flags.incl tfInferrableStatic
         result.rawAddSon staticCopy
       else:
         result.rawAddSon newTypeS(tyAnything, c)
 
-    if paramType.lastSon.kind == tyUserTypeClass:
+    if paramType.typeBodyImpl.kind == tyUserTypeClass:
       result.kind = tyUserTypeClassInst
-      result.rawAddSon paramType.lastSon
-      return addImplicitGeneric(result)
+      result.rawAddSon paramType.typeBodyImpl
+      return addImplicitGeneric(c, result, paramTypId, info, genericParams, paramName)
 
     let x = instGenericContainer(c, paramType.sym.info, result,
                                   allowMetaTypes = true)
-    result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, x])
-    #result = newTypeS(tyCompositeTypeClass, c)
-    #for i in 0..<x.len: result.rawAddSon(x.sons[i])
-    result = addImplicitGeneric(result)
+    result = newTypeS(tyCompositeTypeClass, c)
+    result.rawAddSon paramType
+    result.rawAddSon x
+    result = addImplicitGeneric(c, result, paramTypId, info, genericParams, paramName)
 
   of tyGenericInst:
-    if paramType.lastSon.kind == tyUserTypeClass:
-      var cp = copyType(paramType, getCurrOwner(c), false)
+    result = nil
+    if paramType.skipModifier.kind == tyUserTypeClass:
+      var cp = copyType(paramType, c.idgen, getCurrOwner(c))
+      copyTypeProps(c.graph, c.idgen.module, cp, paramType)
+
       cp.kind = tyUserTypeClassInst
-      return addImplicitGeneric(cp)
+      return addImplicitGeneric(c, cp, paramTypId, info, genericParams, paramName)
 
-    for i in 1 .. paramType.len-2:
-      var lifted = liftingWalk(paramType.sons[i])
+    for i in 1..<paramType.len-1:
+      var lifted = recurse(paramType[i])
       if lifted != nil:
-        paramType.sons[i] = lifted
+        paramType[i] = lifted
         result = paramType
-        result.lastSon.shouldHaveMeta
+        result.last.shouldHaveMeta
 
-    let liftBody = liftingWalk(paramType.lastSon, true)
+    let liftBody = recurse(paramType.skipModifier, true)
     if liftBody != nil:
       result = liftBody
-      result.shouldHaveMeta
+      result.flags.incl tfHasMeta
+      #result.shouldHaveMeta
 
   of tyGenericInvocation:
-    for i in 1 ..< paramType.len:
-      let lifted = liftingWalk(paramType.sons[i])
-      if lifted != nil: paramType.sons[i] = lifted
+    result = nil
+    for i in 1..<paramType.len:
+      #if paramType[i].kind != tyTypeDesc:
+      let lifted = recurse(paramType[i])
+      if lifted != nil: paramType[i] = lifted
 
     let body = paramType.base
-    if body.kind == tyForward:
+    if body.kind in {tyForward, tyError}:
       # this may happen for proc type appearing in a type section
       # before one of its param types
       return
 
-    if body.lastSon.kind == tyUserTypeClass:
+    if body.last.kind == tyUserTypeClass:
       let expanded = instGenericContainer(c, info, paramType,
                                           allowMetaTypes = true)
-      result = liftingWalk(expanded, true)
+      result = recurse(expanded, true)
 
   of tyUserTypeClasses, tyBuiltInTypeClass, tyCompositeTypeClass,
-     tyAnd, tyOr, tyNot:
-    result = addImplicitGeneric(copyType(paramType, getCurrOwner(c), false))
+     tyAnd, tyOr, tyNot, tyConcept:
+    result = addImplicitGeneric(c,
+        copyType(paramType, c.idgen, getCurrOwner(c)), paramTypId,
+        info, genericParams, paramName)
 
   of tyGenericParam:
-    markUsed(c.config, info, paramType.sym, c.graph.usageSym)
-    styleCheckUse(info, paramType.sym)
+    result = nil
+    markUsed(c, paramType.sym.info, paramType.sym)
+    onUse(paramType.sym.info, paramType.sym)
     if tfWildcard in paramType.flags:
       paramType.flags.excl tfWildcard
-      paramType.sym.kind = skType
+      paramType.sym.transitionGenericParamToType()
 
-  else: discard
-
-  # result = liftingWalk(paramType)
+  else: result = nil
 
 proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType =
+  ## Semchecks the type of parameters.
   if n.kind == nkCurlyExpr:
-    result = semTypeNode(c, n.sons[0], nil)
-    constraint = semNodeKindConstraints(n, c.config)
+    result = semTypeNode(c, n[0], nil)
+    constraint = semNodeKindConstraints(n, c.config, 1)
+  elif n.kind == nkCall and
+      n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice, nkOpenSym} and
+      considerQuotedIdent(c, n[0]).s == "{}":
+    result = semTypeNode(c, n[1], nil)
+    constraint = semNodeKindConstraints(n, c.config, 2)
   else:
     result = semTypeNode(c, n, nil)
 
@@ -990,21 +1311,27 @@ proc newProcType(c: PContext; info: TLineInfo; prev: PType = nil): PType =
   # result.n[0] used to be `nkType`, but now it's `nkEffectList` because
   # the effects are now stored in there too ... this is a bit hacky, but as
   # usual we desperately try to save memory:
-  addSon(result.n, newNodeI(nkEffectList, info))
+  result.n.add newNodeI(nkEffectList, info)
+
+proc isMagic(sym: PSym): bool =
+  if sym.ast == nil: return false
+  let nPragmas = sym.ast[pragmasPos]
+  return hasPragma(nPragmas, wMagic)
 
 proc semProcTypeNode(c: PContext, n, genericParams: PNode,
                      prev: PType, kind: TSymKind; isType=false): PType =
   # for historical reasons (code grows) this is invoked for parameter
   # lists too and then 'isType' is false.
-  var cl: IntSet
   checkMinSonsLen(n, 1, c.config)
   result = newProcType(c, n.info, prev)
-  if genericParams != nil and sonsLen(genericParams) == 0:
-    cl = initIntSet()
   var check = initIntSet()
   var counter = 0
-  for i in countup(1, n.len - 1):
-    var a = n.sons[i]
+  template isCurrentlyGeneric: bool =
+    # genericParams might update as implicit generic params are added
+    genericParams != nil and genericParams.len > 0
+
+  for i in 1..<n.len:
+    var a = n[i]
     if a.kind != nkIdentDefs:
       # for some generic instantiations the passed ':env' parameter
       # for closures has already been produced (see bug #898). We simply
@@ -1012,87 +1339,180 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
       # pass over this instantiation:
       if a.kind == nkSym and sfFromGeneric in a.sym.flags: continue
       illFormedAst(a, c.config)
+
     checkMinSonsLen(a, 3, c.config)
     var
       typ: PType = nil
       def: PNode = nil
       constraint: PNode = nil
-      length = sonsLen(a)
-      hasType = a.sons[length-2].kind != nkEmpty
-      hasDefault = a.sons[length-1].kind != nkEmpty
+      hasType = a[^2].kind != nkEmpty
+      hasDefault = a[^1].kind != nkEmpty
+
     if hasType:
-      typ = semParamType(c, a.sons[length-2], constraint)
+      let isGeneric = isCurrentlyGeneric()
+      inc c.inGenericContext, ord(isGeneric)
+      typ = semParamType(c, a[^2], constraint)
+      dec c.inGenericContext, ord(isGeneric)
+      # TODO: Disallow typed/untyped in procs in the compiler/stdlib
+      if kind in {skProc, skFunc} and (typ.kind == tyTyped or typ.kind == tyUntyped):
+        if not isMagic(getCurrOwner(c)):
+          localError(c.config, a[^2].info, "'" & typ.sym.name.s & "' is only allowed in templates and macros or magic procs")
+
 
     if hasDefault:
-      def = semExprWithType(c, a.sons[length-1])
-      # check type compatibility between def.typ and typ:
+      def = a[^1]
+      if a.len > 3:
+        var msg = ""
+        for j in 0 ..< a.len - 2:
+          if msg.len != 0: msg.add(", ")
+          msg.add($a[j])
+        msg.add(" all have default value '")
+        msg.add(def.renderTree)
+        msg.add("', this may be unintentional, " &
+          "either use ';' (semicolon) or explicitly write each default value")
+        message(c.config, a.info, warnImplicitDefaultValue, msg)
+      block determineType:
+        var canBeVoid = false
+        if kind == skTemplate:
+          if typ != nil and typ.kind == tyUntyped:
+            # don't do any typechecking or assign a type for
+            # `untyped` parameter default value
+            break determineType
+          elif hasUnresolvedArgs(c, def):
+            # template default value depends on other parameter
+            # don't do any typechecking
+            def.typ = makeTypeFromExpr(c, def.copyTree)
+            break determineType
+          elif typ != nil and typ.kind == tyTyped:
+            canBeVoid = true
+        let isGeneric = isCurrentlyGeneric()
+        inc c.inGenericContext, ord(isGeneric)
+        if canBeVoid:
+          def = semExpr(c, def, {efDetermineType, efAllowSymChoice}, typ)
+        else:
+          def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, typ)
+        dec c.inGenericContext, ord(isGeneric)
+        if def.referencesAnotherParam(getCurrOwner(c)):
+          def.flags.incl nfDefaultRefsParam
+
       if typ == nil:
         typ = def.typ
-      elif def != nil:
-        # and def.typ != nil and def.typ.kind != tyNone:
+        if isEmptyContainer(typ):
+          localError(c.config, a.info, "cannot infer the type of parameter '" & $a[0] & "'")
+
+        if typ.kind == tyTypeDesc:
+          # consider a proc such as:
+          # proc takesType(T = int)
+          # a naive analysis may conclude that the proc type is type[int]
+          # which will prevent other types from matching - clearly a very
+          # surprising behavior. We must instead fix the expected type of
+          # the proc to be the unbound typedesc type:
+          typ = newTypeS(tyTypeDesc, c, newTypeS(tyNone, c))
+          typ.flags.incl tfCheckedForDestructor
+
+      elif def.typ != nil and def.typ.kind != tyFromExpr: # def.typ can be void
+        # if def.typ != nil and def.typ.kind != tyNone:
         # example code that triggers it:
         # proc sort[T](cmp: proc(a, b: T): int = cmp)
         if not containsGenericType(typ):
+          # check type compatibility between def.typ and typ:
           def = fitNode(c, typ, def, def.info)
+        elif typ.kind == tyStatic:
+          def = semConstExpr(c, def)
+          def = fitNode(c, typ, def, def.info)
+
     if not hasType and not hasDefault:
       if isType: localError(c.config, a.info, "':' expected")
       if kind in {skTemplate, skMacro}:
-        typ = newTypeS(tyExpr, c)
+        typ = newTypeS(tyUntyped, c)
     elif skipTypes(typ, {tyGenericInst, tyAlias, tySink}).kind == tyVoid:
       continue
-    for j in countup(0, length-3):
-      var arg = newSymG(skParam, a.sons[j], c)
+
+    for j in 0..<a.len-2:
+      var arg = newSymG(skParam, if a[j].kind == nkPragmaExpr: a[j][0] else: a[j], c)
+      if arg.name.id == ord(wUnderscore):
+        arg.flags.incl(sfGenSym)
+      elif containsOrIncl(check, arg.name.id):
+        localError(c.config, a[j].info, "attempt to redefine: '" & arg.name.s & "'")
+      if a[j].kind == nkPragmaExpr:
+        pragma(c, arg, a[j][1], paramPragmas)
       if not hasType and not hasDefault and kind notin {skTemplate, skMacro}:
         let param = strTableGet(c.signatures, arg.name)
         if param != nil: typ = param.typ
         else:
-          localError(c.config, a.info, "typeless parameters are obsolete")
+          localError(c.config, a.info, "parameter '$1' requires a type" % arg.name.s)
           typ = errorType(c)
+      var nameForLift = arg.name.s
+      if sfGenSym in arg.flags:
+        nameForLift.add("`gensym" & $arg.id)
       let lifted = liftParamType(c, kind, genericParams, typ,
-                                 arg.name.s, arg.info)
-      let finalType = if lifted != nil: lifted else: typ.skipIntLit
+                                 nameForLift, arg.info)
+      let finalType = if lifted != nil: lifted else: typ.skipIntLit(c.idgen)
       arg.typ = finalType
       arg.position = counter
-      arg.constraint = constraint
+      if constraint != nil:
+        #only replace the constraint when it has been set as arg could contain codegenDecl
+        arg.constraint = constraint
       inc(counter)
-      if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def)
-      if containsOrIncl(check, arg.name.id):
-        localError(c.config, a.sons[j].info, "attempt to redefine: '" & arg.name.s & "'")
-      addSon(result.n, newSymNode(arg))
+      if def != nil and def.kind != nkEmpty:
+        arg.ast = copyTree(def)
+      result.n.add newSymNode(arg)
       rawAddSon(result, finalType)
       addParamOrResult(c, arg, kind)
-      styleCheckDef(c.config, a.sons[j].info, arg)
-
-  var r: PType
-  if n.sons[0].kind != nkEmpty:
-    r = semTypeNode(c, n.sons[0], nil)
+      styleCheckDef(c, a[j].info, arg)
+      onDef(a[j].info, arg)
+      a[j] = newSymNode(arg)
+
+  var r: PType = nil
+  if n[0].kind != nkEmpty:
+    let isGeneric = isCurrentlyGeneric()
+    inc c.inGenericContext, ord(isGeneric)
+    r = semTypeNode(c, n[0], nil)
+    dec c.inGenericContext, ord(isGeneric)
+
+  if r != nil and kind in {skMacro, skTemplate} and r.kind == tyTyped:
+    # XXX: To implement the proposed change in the warning, just
+    # delete this entire if block. The rest is (at least at time of
+    # writing this comment) already implemented.
+    let info = n[0].info
+    const msg = "`typed` will change its meaning in future versions of Nim. " &
+                "`void` or no return type declaration at all has the same " &
+                "meaning as the current meaning of `typed` as return type " &
+                "declaration."
+    message(c.config, info, warnDeprecated, msg)
+    r = nil
 
   if r != nil:
     # 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 {tyTyped, tyUntyped}:
+        localError(c.config, n[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:
-        # '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:
+      elif r.kind == tyAnything:
+        r = copyType(r, c.idgen, r.owner)
+        r.flags.incl tfRetType
+      elif r.kind == tyStatic:
+        # type allowed should forbid this type
+        discard
+      else:
         if r.sym == nil or sfAnon notin r.sym.flags:
           let lifted = liftParamType(c, kind, genericParams, r, "result",
-                                     n.sons[0].info)
+                                     n[0].info)
           if lifted != nil:
             r = lifted
             #if r.kind != tyGenericParam:
             #echo "came here for ", typeToString(r)
             r.flags.incl tfRetType
-        r = skipIntLit(r)
+        r = skipIntLit(r, c.idgen)
         if kind == skIterator:
           # see tchainediterators
-          # in cases like iterator foo(it: iterator): type(it)
+          # in cases like iterator foo(it: iterator): typeof(it)
           # we don't need to change the return type to iter[T]
           result.flags.incl tfIterator
           # XXX Would be nice if we could get rid of this
-      result.sons[0] = r
+      result[0] = r
       let oldFlags = result.flags
       propagateToOwner(result, r)
       if oldFlags != result.flags:
@@ -1101,85 +1521,101 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
           result.flags.excl tfHasMeta
       result.n.typ = r
 
-  if genericParams != nil and genericParams.len > 0:
+  if isCurrentlyGeneric():
     for n in genericParams:
       if {sfUsed, sfAnon} * n.sym.flags == {}:
         result.flags.incl tfUnresolved
 
       if tfWildcard in n.sym.typ.flags:
-        n.sym.kind = skType
+        n.sym.transitionGenericParamToType()
         n.sym.typ.flags.excl tfWildcard
 
 proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
   checkMinSonsLen(n, 1, c.config)
-  var length = sonsLen(n)
-  for i in countup(0, length - 2):
-    n.sons[i] = semStmt(c, n.sons[i])
-  if length > 0:
-    result = semTypeNode(c, n.sons[length - 1], prev)
+  for i in 0..<n.len - 1:
+    n[i] = semStmt(c, n[i], {})
+  if n.len > 0:
+    result = semTypeNode(c, n[^1], prev)
     n.typ = result
-    n.sons[length - 1].typ = result
+    n[^1].typ = result
   else:
     result = nil
 
 proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
   inc(c.p.nestedBlockCounter)
+  let oldBreakInLoop = c.p.breakInLoop
+  c.p.breakInLoop = false
   checkSonsLen(n, 2, c.config)
   openScope(c)
-  if n.sons[0].kind notin {nkEmpty, nkSym}:
-    addDecl(c, newSymS(skLabel, n.sons[0], c))
-  result = semStmtListType(c, n.sons[1], prev)
-  n.sons[1].typ = result
+  if n[0].kind notin {nkEmpty, nkSym}:
+    addDecl(c, newSymS(skLabel, n[0], c))
+  result = semStmtListType(c, n[1], prev)
+  n[1].typ = result
   n.typ = result
   closeScope(c)
+  c.p.breakInLoop = oldBreakInLoop
   dec(c.p.nestedBlockCounter)
 
 proc semGenericParamInInvocation(c: PContext, n: PNode): PType =
   result = semTypeNode(c, n, nil)
   n.typ = makeTypeDesc(c, result)
 
-proc semObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType) =
+proc trySemObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType): bool =
   var
     check = initIntSet()
     pos = 0
   let
-    realBase = t.sons[0]
+    realBase = t.baseClass
     base = skipTypesOrNil(realBase, skipPtrs)
+  result = true
   if base.isNil:
     localError(c.config, n.info, errIllegalRecursionInTypeX % "object")
   else:
     let concreteBase = skipGenericInvocation(base)
     if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags:
-      addInheritedFields(c, check, pos, concreteBase)
+      if not tryAddInheritedFields(c, check, pos, concreteBase, n):
+        return false
     else:
       if concreteBase.kind != tyError:
         localError(c.config, n.info, errInheritanceOnlyWithNonFinalObjects)
   var newf = newNodeI(nkRecList, n.info)
   semRecordNodeAux(c, t.n, check, pos, newf, t)
 
+proc containsGenericInvocationWithForward(n: PNode): bool =
+  if n.kind == nkSym and n.sym.ast != nil and n.sym.ast.len > 1 and n.sym.ast[2].kind == nkObjectTy:
+    for p in n.sym.ast[2][^1]:
+      if p.kind == nkIdentDefs and p[1].typ != nil and p[1].typ.kind == tyGenericInvocation and
+        p[1][0].kind == nkSym and p[1][0].typ.kind == tyForward:
+          return true
+  return false
+
 proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
   if s.typ == nil:
     localError(c.config, n.info, "cannot instantiate the '$1' $2" %
-                       [s.name.s, ($s.kind).substr(2).toLowerAscii])
+               [s.name.s, s.kind.toHumanStr])
     return newOrPrevType(tyError, prev, c)
 
-  var t = s.typ
+  var t = s.typ.skipTypes({tyAlias})
   if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody:
     t = t.base
-
   result = newOrPrevType(tyGenericInvocation, prev, c)
-  addSonSkipIntLit(result, t)
+  addSonSkipIntLit(result, t, c.idgen)
+
+  template addToResult(typ, skip) =
 
-  template addToResult(typ) =
     if typ.isNil:
       internalAssert c.config, false
       rawAddSon(result, typ)
-    else: addSonSkipIntLit(result, typ)
+    else:
+      if skip:
+        addSonSkipIntLit(result, typ, c.idgen)
+      else:
+        rawAddSon(result, makeRangeWithStaticExpr(c, typ.n))
 
   if t.kind == tyForward:
-    for i in countup(1, sonsLen(n)-1):
-      var elem = semGenericParamInInvocation(c, n.sons[i])
-      addToResult(elem)
+    for i in 1..<n.len:
+      var elem = semGenericParamInInvocation(c, n[i])
+      addToResult(elem, true)
     return
   elif t.kind != tyGenericBody:
     # we likely got code of the form TypeA[TypeB] where TypeA is
@@ -1192,29 +1628,38 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
     matches(c, n, copyTree(n), m)
 
     if m.state != csMatch:
-      let err = "cannot instantiate " & typeToString(t) & "\n" &
-                "got: <" & describeArgs(c, n) & ">\n" &
-                "but expected: <" & describeArgs(c, t.n, 0) & ">"
+      var err = "cannot instantiate "
+      err.addTypeHeader(c.config, t)
+      err.add "\ngot: <$1>\nbut expected: <$2>" % [describeArgs(c, n), describeArgs(c, t.n, 0)]
       localError(c.config, n.info, errGenerated, err)
       return newOrPrevType(tyError, prev, c)
 
     var isConcrete = true
-
-    for i in 1 ..< m.call.len:
+    let rType = m.call[0].typ
+    let mIndex = if rType != nil: rType.len - 1 else: -1
+    for i in 1..<m.call.len:
       var typ = m.call[i].typ
-      if typ.kind == tyTypeDesc and typ.sons[0].kind == tyNone:
+      # is this a 'typedesc' *parameter*? If so, use the typedesc type,
+      # unstripped.
+      if m.call[i].kind == nkSym and m.call[i].sym.kind == skParam and
+          typ.kind == tyTypeDesc and containsGenericType(typ):
         isConcrete = false
-        addToResult(typ)
+        addToResult(typ, true)
       else:
         typ = typ.skipTypes({tyTypeDesc})
         if containsGenericType(typ): isConcrete = false
-        addToResult(typ)
+        var skip = true
+        if mIndex >= i - 1 and tfImplicitStatic in rType[i - 1].flags and isIntLit(typ):
+          skip = false
+        addToResult(typ, skip)
 
     if isConcrete:
       if s.ast == nil and s.typ.kind != tyCompositeTypeClass:
         # XXX: What kind of error is this? is it still relevant?
         localError(c.config, n.info, errCannotInstantiateX % s.name.s)
         result = newOrPrevType(tyError, prev, c)
+      elif containsGenericInvocationWithForward(n[0]):
+        c.skipTypes.add n #fixes 1500
       else:
         result = instGenericContainer(c, n.info, result,
                                       allowMetaTypes = false)
@@ -1222,20 +1667,41 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
   # special check for generic object with
   # generic/partial specialized parent
   let tx = result.skipTypes(abstractPtrs, 50)
-  if tx.isNil:
-    localError(c.config, n.info, "invalid recursion in type '$1'" % typeToString(result[0]))
+  if tx.isNil or isTupleRecursive(tx):
+    localError(c.config, n.info, "illegal recursion in type '$1'" % typeToString(result[0]))
     return errorType(c)
-  if tx != result and tx.kind == tyObject and tx.sons[0] != nil:
-    semObjectTypeForInheritedGenericInst(c, n, tx)
+  if tx != result and tx.kind == tyObject:
+    if tx[0] != nil:
+      if not trySemObjectTypeForInheritedGenericInst(c, n, tx):
+        return newOrPrevType(tyError, prev, c)
+    var position = 0
+    recomputeFieldPositions(tx, tx.n, position)
+
+proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
+  if prev != nil and (prev.kind == tyGenericBody or
+      typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody}):
+    result = newTypeS(tyAlias, c)
+    result.rawAddSon typeExpr
+    result.sym = prev.sym
+    if prev.kind != tyGenericBody:
+      assignType(prev, result)
+  else:
+    result = nil
 
-proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType
+proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) =
+  if prev != nil:
+    let result = newTypeS(tyAlias, c)
+    result.rawAddSon typExpr.typ
+    result.sym = prev.sym
+    if prev.kind != tyGenericBody:
+      assignType(prev, result)
 
 proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
   var n = semExprWithType(c, n, {efDetermineType})
   if n.typ.kind == tyTypeDesc:
     result = n.typ.base
     # fix types constructed by macros/template:
-    if prev != nil and prev.sym != nil:
+    if prev != nil and prev.kind != tyGenericBody and prev.sym != nil:
       if result.sym.isNil:
         # Behold! you're witnessing enormous power yielded
         # by macros. Only macros can summon unnamed types
@@ -1251,13 +1717,18 @@ proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
         # unnecessary new type creation
         let alias = maybeAliasType(c, result, prev)
         if alias != nil: result = alias
+  elif n.typ.kind == tyFromExpr and c.inGenericContext > 0:
+    # sometimes not possible to distinguish type from value in generic body,
+    # for example `T.Foo`, so both are handled under `tyFromExpr`
+    result = n.typ
   else:
     localError(c.config, n.info, "expected type, but got: " & n.renderTree)
     result = errorType(c)
 
-proc freshType(res, prev: PType): PType {.inline.} =
-  if prev.isNil:
-    result = copyType(res, res.owner, keepId=false)
+proc freshType(c: PContext; res, prev: PType): PType {.inline.} =
+  if prev.isNil or prev.kind == tyGenericBody:
+    result = copyType(res, c.idgen, res.owner)
+    copyTypeProps(c.graph, c.idgen.module, result, res)
   else:
     result = res
 
@@ -1271,21 +1742,27 @@ template modifierTypeKindOfNode(n: PNode): TTypeKind =
   else: tyNone
 
 proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
-  # if n.sonsLen == 0: return newConstraint(c, tyTypeClass)
+  # if n.len == 0: return newConstraint(c, tyTypeClass)
+  if isNewStyleConcept(n):
+    result = newOrPrevType(tyConcept, prev, c)
+    result.flags.incl tfCheckedForDestructor
+    result.n = semConceptDeclaration(c, n)
+    return result
+
   let
     pragmas = n[1]
     inherited = n[2]
 
-  result = newOrPrevType(tyUserTypeClass, prev, c)
   var owner = getCurrOwner(c)
-  var candidateTypeSlot = newTypeWithSons(owner, tyAlias, @[c.errorType])
-  result.sons = @[candidateTypeSlot]
+  var candidateTypeSlot = newTypeS(tyAlias, c, c.errorType)
+  result = newOrPrevType(tyUserTypeClass, prev, c, son = candidateTypeSlot)
+  result.flags.incl tfCheckedForDestructor
   result.n = n
 
   if inherited.kind != nkEmpty:
     for n in inherited.sons:
       let typ = semTypeNode(c, n, nil)
-      result.sons.add(typ)
+      result.add(typ)
 
   openScope(c)
   for param in n[0]:
@@ -1298,50 +1775,97 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
     if modifier != tyNone:
       dummyName = param[0]
       dummyType = c.makeTypeWithModifier(modifier, candidateTypeSlot)
-      if modifier == tyTypeDesc: dummyType.flags.incl tfExplicit
+      # if modifier == tyRef:
+        # dummyType.flags.incl tfNotNil
+      if modifier == tyTypeDesc:
+        dummyType.flags.incl tfConceptMatchedTypeSym
+        dummyType.flags.incl tfCheckedForDestructor
     else:
       dummyName = param
       dummyType = candidateTypeSlot
 
+    # this can be true for 'nim check' on incomplete concepts,
+    # see bug #8230
+    if dummyName.kind == nkEmpty: continue
+
     internalAssert c.config, dummyName.kind == nkIdent
     var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar,
-                            dummyName.ident, owner, param.info)
+                            dummyName.ident, c.idgen, owner, param.info)
     dummyParam.typ = dummyType
     incl dummyParam.flags, sfUsed
     addDecl(c, dummyParam)
 
-  result.n.sons[3] = semConceptBody(c, n[3])
+  result.n[3] = semConceptBody(c, n[3])
   closeScope(c)
 
+proc applyTypeSectionPragmas(c: PContext; pragmas, operand: PNode): PNode =
+  result = nil
+  for p in pragmas:
+    let key = if p.kind in nkPragmaCallKinds and p.len >= 1: p[0] else: p
+    if p.kind == nkEmpty or whichPragma(p) != wInvalid:
+      discard "builtin pragma"
+    else:
+      trySuggestPragmas(c, key)
+      let ident =
+        if key.kind in nkIdentKinds:
+          considerQuotedIdent(c, key)
+        else:
+          nil
+      if ident != nil and strTableGet(c.userPragmas, ident) != nil:
+        discard "User-defined pragma"
+      else:
+        let sym = qualifiedLookUp(c, key, {})
+        # XXX: What to do here if amb is true?
+        if sym != nil and sfCustomPragma in sym.flags:
+          discard "Custom user pragma"
+        else:
+          # we transform ``(arg1, arg2: T) {.m, rest.}`` into ``m((arg1, arg2: T) {.rest.})`` and
+          # let the semantic checker deal with it:
+          var x = newNodeI(nkCall, key.info)
+          x.add(key)
+          if p.kind in nkPragmaCallKinds and p.len > 1:
+            # pass pragma arguments to the macro too:
+            for i in 1 ..< p.len:
+              x.add(p[i])
+          # Also pass the node the pragma has been applied to
+          x.add(operand.copyTreeWithoutNode(p))
+          # recursion assures that this works for multiple macro annotations too:
+          var r = semOverloadedCall(c, x, x, {skMacro, skTemplate}, {efNoUndeclared})
+          if r != nil:
+            doAssert r[0].kind == nkSym
+            let m = r[0].sym
+            case m.kind
+            of skMacro: return semMacroExpr(c, r, r, m, {efNoSemCheck})
+            of skTemplate: return semTemplateExpr(c, r, m, {efNoSemCheck})
+            else: doAssert(false, "cannot happen")
+
 proc semProcTypeWithScope(c: PContext, n: PNode,
-                        prev: PType, kind: TSymKind): PType =
+                          prev: PType, kind: TSymKind): PType =
   checkSonsLen(n, 2, c.config)
+
+  if n[1].kind != nkEmpty and n[1].len > 0:
+    let macroEval = applyTypeSectionPragmas(c, n[1], n)
+    if macroEval != nil:
+      return semTypeNode(c, macroEval, prev)
+
   openScope(c)
-  result = semProcTypeNode(c, n.sons[0], nil, prev, kind, isType=true)
+  result = semProcTypeNode(c, n[0], nil, prev, kind, isType=true)
   # start with 'ccClosure', but of course pragmas can overwrite this:
   result.callConv = ccClosure
   # dummy symbol for `pragma`:
   var s = newSymS(kind, newIdentNode(getIdent(c.cache, "dummy"), n.info), c)
   s.typ = result
-  if n.sons[1].kind != nkEmpty and n.sons[1].len > 0:
-    pragma(c, s, n.sons[1], procTypePragmas)
-    when useEffectSystem: setEffectsForProcType(c.graph, result, n.sons[1])
+  if n[1].kind != nkEmpty and n[1].len > 0:
+    pragma(c, s, n[1], procTypePragmas)
+    when useEffectSystem: setEffectsForProcType(c.graph, result, n[1])
+  elif c.optionStack.len > 0:
+    # we construct a fake 'nkProcDef' for the 'mergePragmas' inside 'implicitPragmas'...
+    s.ast = newTree(nkProcDef, newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info),
+        newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info))
+    implicitPragmas(c, s, n.info, {wTags, wRaises})
+    when useEffectSystem: setEffectsForProcType(c.graph, result, s.ast[pragmasPos])
   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)
@@ -1349,32 +1873,128 @@ proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym =
     localError(c.config, n.info, errTypeExpected)
     result = errorSym(c, n)
 
+proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType =
+  result = newOrPrevType(tyStatic, prev, c)
+  var base = semTypeNode(c, childNode, nil).skipTypes({tyTypeDesc, tyAlias})
+  result.rawAddSon(base)
+  result.flags.incl tfHasStatic
+
+proc semTypeOf(c: PContext; n: PNode; prev: PType): PType =
+  openScope(c)
+  inc c.inTypeofContext
+  defer: dec c.inTypeofContext # compiles can raise an exception
+  let t = semExprWithType(c, n, {efInTypeof})
+  closeScope(c)
+  fixupTypeOf(c, prev, t)
+  result = t.typ
+  if result.kind == tyFromExpr:
+    result.flags.incl tfNonConstExpr
+
+proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
+  openScope(c)
+  var m = BiggestInt 1 # typeOfIter
+  if n.len == 3:
+    let mode = semConstExpr(c, n[2])
+    if mode.kind != nkIntLit:
+      localError(c.config, n.info, "typeof: cannot evaluate 'mode' parameter at compile-time")
+    else:
+      m = mode.intVal
+  inc c.inTypeofContext
+  defer: dec c.inTypeofContext # compiles can raise an exception
+  let t = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
+  closeScope(c)
+  fixupTypeOf(c, prev, t)
+  result = t.typ
+  if result.kind == tyFromExpr:
+    result.flags.incl tfNonConstExpr
+
+proc semTypeIdent(c: PContext, n: PNode): PSym =
+  if n.kind == nkSym:
+    result = getGenSym(c, n.sym)
+  else:
+    result = pickSym(c, n, {skType, skGenericParam, skParam})
+    if result.isNil:
+      result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared})
+    if result != nil:
+      markUsed(c, n.info, result)
+      onUse(n.info, result)
+
+      # alias syntax, see semSym for skTemplate, skMacro
+      if result.kind in {skTemplate, skMacro} and sfNoalias notin result.flags:
+        let t = semTypeExpr(c, n, nil)
+        result = symFromType(c, t, n.info)
+
+      if result.kind == skParam and result.typ.kind == tyTypeDesc:
+        # This is a typedesc param. is it already bound?
+        # it's not bound when it's used multiple times in the
+        # proc signature for example
+        if c.inGenericInst > 0:
+          let bound = result.typ.elementType.sym
+          if bound != nil: return bound
+          return result
+        if result.typ.sym == nil:
+          localError(c.config, n.info, errTypeExpected)
+          return errorSym(c, n)
+        result = result.typ.sym.copySym(c.idgen)
+        result.typ = exactReplica(result.typ)
+        result.typ.flags.incl tfUnresolved
+
+      if result.kind == skGenericParam:
+        if result.typ.kind == tyGenericParam and result.typ.len == 0 and
+           tfWildcard in result.typ.flags:
+          # collapse the wild-card param to a type
+          result.transitionGenericParamToType()
+          result.typ.flags.excl tfWildcard
+          return
+        else:
+          localError(c.config, n.info, errTypeExpected)
+          return errorSym(c, n)
+      if result.kind != skType and result.magic notin {mStatic, mType, mTypeOf}:
+        # this implements the wanted ``var v: V, x: V`` feature ...
+        var ov: TOverloadIter = default(TOverloadIter)
+        var amb = initOverloadIter(ov, c, n)
+        while amb != nil and amb.kind != skType:
+          amb = nextOverloadIter(ov, c, n)
+        if amb != nil: result = amb
+        else:
+          if result.kind != skError: localError(c.config, n.info, errTypeExpected)
+          return errorSym(c, n)
+      if result.typ.kind != tyGenericParam:
+        # XXX get rid of this hack!
+        var oldInfo = n.info
+        when defined(useNodeIds):
+          let oldId = n.id
+        reset(n[])
+        when defined(useNodeIds):
+          n.id = oldId
+        n.transitionNoneToSym()
+        n.sym = result
+        n.info = oldInfo
+        n.typ = result.typ
+    else:
+      localError(c.config, n.info, "identifier expected")
+      result = errorSym(c, n)
+
 proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
   result = nil
   inc c.inTypeContext
 
   if c.config.cmd == cmdIdeTools: suggestExpr(c, n)
   case n.kind
-  of nkEmpty: discard
+  of nkEmpty: result = n.typ
   of nkTypeOfExpr:
-    # for ``type(countup(1,3))``, see ``tests/ttoseq``.
+    # for ``typeof(countup(1,3))``, see ``tests/ttoseq``.
     checkSonsLen(n, 1, c.config)
-    let typExpr = semExprWithType(c, n.sons[0], {efInTypeof})
-    fixupTypeOf(c, prev, typExpr)
-    result = typExpr.typ
+    result = semTypeOf(c, n[0], prev)
     if result.kind == tyTypeDesc: result.flags.incl tfExplicit
   of nkPar:
-    if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
+    if n.len == 1: result = semTypeNode(c, n[0], prev)
     else:
       result = semAnonTuple(c, n, prev)
   of nkTupleConstr: result = semAnonTuple(c, n, prev)
   of nkCallKinds:
     let x = n[0]
-    let ident = case x.kind
-                of nkIdent: x.ident
-                of nkSym: x.sym.name
-                of nkClosedSymChoice, nkOpenSymChoice: x[0].sym.name
-                else: nil
+    let ident = x.getPIdent
     if ident != nil and ident.s == "[]":
       let b = newNodeI(nkBracketExpr, n.info)
       for i in 1..<n.len: b.add(n[i])
@@ -1382,27 +2002,27 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     elif ident != nil and ident.id == ord(wDotDot):
       result = semRangeAux(c, n, prev)
     elif n[0].kind == nkNilLit and n.len == 2:
-      result = semTypeNode(c, n.sons[1], prev)
-      if result.skipTypes({tyGenericInst, tyAlias, tySink}).kind in NilableTypes+GenericTypes:
+      result = semTypeNode(c, n[1], prev)
+      if result.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).kind in NilableTypes+GenericTypes:
         if tfNotNil in result.flags:
-          result = freshType(result, prev)
+          result = freshType(c, result, prev)
           result.flags.excl(tfNotNil)
       else:
         localError(c.config, n.info, errGenerated, "invalid type")
     elif n[0].kind notin nkIdentKinds:
       result = semTypeExpr(c, n, prev)
     else:
-      let op = considerQuotedIdent(c, n.sons[0])
-      if op.id in {ord(wAnd), ord(wOr)} or op.s == "|":
+      let op = considerQuotedIdent(c, n[0])
+      if op.id == ord(wAnd) or op.id == ord(wOr) or op.s == "|":
         checkSonsLen(n, 3, c.config)
         var
-          t1 = semTypeNode(c, n.sons[1], nil)
-          t2 = semTypeNode(c, n.sons[2], nil)
+          t1 = semTypeNode(c, n[1], nil)
+          t2 = semTypeNode(c, n[2], nil)
         if t1 == nil:
-          localError(c.config, n.sons[1].info, errTypeExpected)
+          localError(c.config, n[1].info, errTypeExpected)
           result = newOrPrevType(tyError, prev, c)
         elif t2 == nil:
-          localError(c.config, n.sons[2].info, errTypeExpected)
+          localError(c.config, n[2].info, errTypeExpected)
           result = newOrPrevType(tyError, prev, c)
         else:
           result = if op.id == ord(wAnd): makeAndType(c, t1, t2)
@@ -1410,17 +2030,50 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       elif op.id == ord(wNot):
         case n.len
         of 3:
-          result = semTypeNode(c, n.sons[1], prev)
-          if result.skipTypes({tyGenericInst, tyAlias, tySink}).kind in NilableTypes+GenericTypes+{tyForward} and
-              n.sons[2].kind == nkNilLit:
-            result = freshType(result, prev)
+          result = semTypeNode(c, n[1], prev)
+          if result.kind == tyTypeDesc and tfUnresolved notin result.flags:
+            result = result.base
+          if n[2].kind != nkNilLit:
+            localError(c.config, n.info,
+              "Invalid syntax. When used with a type, 'not' can be followed only by 'nil'")
+          if notnil notin c.features and strictNotNil notin c.features:
+            localError(c.config, n.info,
+              "enable the 'not nil' annotation with {.experimental: \"notnil\".} or " &
+              "  the `strict not nil` annotation with {.experimental: \"strictNotNil\".} " &
+              "  the \"notnil\" one is going to be deprecated, so please use \"strictNotNil\"")
+          let resolvedType = result.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned})
+          case resolvedType.kind
+          of tyGenericParam, tyTypeDesc, tyFromExpr:
+            # XXX: This is a really inappropraite hack, but it solves
+            # https://github.com/nim-lang/Nim/issues/4907 for now.
+            #
+            # A proper solution is to introduce a new type kind such
+            # as `tyNotNil[tyRef[SomeGenericParam]]`. This will allow
+            # semtypinst to replace the generic param correctly in
+            # situations like the following:
+            #
+            # type Foo[T] = object
+            #   bar: ref T not nil
+            #   baz: ref T
+            #
+            # The root of the problem is that `T` here must have a specific
+            # ID that is bound to a concrete type during instantiation.
+            # The use of `freshType` below breaks this. Another hack would
+            # be to reuse the same ID for the not nil type, but this will
+            # fail if the `T` parameter is referenced multiple times as in
+            # the example above.
+            #
+            # I suggest revisiting this once the language decides on whether
+            # `not nil` should be the default. We can then map nilable refs
+            # to other types such as `Option[T]`.
+            result = makeTypeFromExpr(c, newTree(nkStmtListType, n.copyTree))
+          of NilableTypes + {tyGenericInvocation, tyForward}:
+            result = freshType(c, result, prev)
             result.flags.incl(tfNotNil)
-            if notnil notin c.features:
-              localError(c.config, n.info, "enable the 'not nil' annotation with {.experimental: \"notnil\".}")
           else:
             localError(c.config, n.info, errGenerated, "invalid type")
         of 2:
-          let negated = semTypeNode(c, n.sons[1], prev)
+          let negated = semTypeNode(c, n[1], prev)
           result = makeNotType(c, negated)
         else:
           localError(c.config, n.info, errGenerated, "invalid type")
@@ -1430,52 +2083,72 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
         result = semAnyRef(c, n, tyRef, prev)
       elif op.id == ord(wType):
         checkSonsLen(n, 2, c.config)
-        let typExpr = semExprWithType(c, n.sons[1], {efInTypeof})
-        fixupTypeOf(c, prev, typExpr)
-        result = typExpr.typ
+        result = semTypeOf(c, n[1], prev)
+      elif op.s == "typeof" and (
+          (n[0].kind == nkSym and n[0].sym.magic == mTypeOf) or
+          (n[0].kind == nkOpenSym and n[0][0].sym.magic == mTypeOf)):
+        result = semTypeOf2(c, n, prev)
+      elif op.s == "owned" and optOwnedRefs notin c.config.globalOptions and n.len == 2:
+        result = semTypeExpr(c, n[1], prev)
       else:
-        if c.inGenericContext > 0 and n.kind == nkCall:
-          result = makeTypeFromExpr(c, n.copyTree)
-        else:
-          result = semTypeExpr(c, n, prev)
+        result = semTypeExpr(c, n, prev)
   of nkWhenStmt:
     var whenResult = semWhen(c, n, false)
-    if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType
-    result = semTypeNode(c, whenResult, prev)
+    if whenResult.kind == nkStmtList: whenResult.transitionSonsKind(nkStmtListType)
+    if whenResult.kind == nkWhenStmt:
+      result = whenResult.typ
+    else:
+      result = semTypeNode(c, whenResult, prev)
   of nkBracketExpr:
     checkMinSonsLen(n, 2, c.config)
-    var head = n.sons[0]
+    var head = n[0]
     var s = if head.kind notin nkCallKinds: semTypeIdent(c, head)
             else: symFromExpectedTypeNode(c, semExpr(c, head))
     case s.magic
     of mArray: result = semArray(c, n, prev)
     of mOpenArray: result = semContainer(c, n, tyOpenArray, "openarray", prev)
+    of mUncheckedArray: result = semContainer(c, n, tyUncheckedArray, "UncheckedArray", prev)
     of mRange: result = semRange(c, n, prev)
     of mSet: result = semSet(c, n, prev)
     of mOrdinal: result = semOrdinal(c, n, prev)
-    of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
-    of mOpt: result = semContainer(c, n, tyOpt, "opt", prev)
+    of mIterableType: result = semIterableType(c, n, prev)
+    of mSeq:
+      result = semContainer(c, n, tySequence, "seq", prev)
+      if optSeqDestructors in c.config.globalOptions:
+        incl result.flags, tfHasAsgn
     of mVarargs: result = semVarargs(c, n, prev)
-    of mTypeDesc: result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
+    of mTypeDesc, mType, mTypeOf:
+      result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
+      result.flags.incl tfExplicit
+    of mStatic:
+      result = semStaticType(c, n[1], prev)
     of mExpr:
-      result = semTypeNode(c, n.sons[0], nil)
+      result = semTypeNode(c, n[0], nil)
       if result != nil:
-        result = copyType(result, getCurrOwner(c), false)
-        for i in countup(1, n.len - 1):
-          result.rawAddSon(semTypeNode(c, n.sons[i], nil))
+        let old = result
+        result = copyType(result, c.idgen, getCurrOwner(c))
+        copyTypeProps(c.graph, c.idgen.module, result, old)
+        for i in 1..<n.len:
+          result.rawAddSon(semTypeNode(c, n[i], nil))
     of mDistinct:
       result = newOrPrevType(tyDistinct, prev, c)
-      addSonSkipIntLit(result, semTypeNode(c, n[1], nil))
+      addSonSkipIntLit(result, semTypeNode(c, n[1], nil), c.idgen)
     of mVar:
       result = newOrPrevType(tyVar, prev, c)
-      var base = semTypeNode(c, n.sons[1], nil)
+      var base = semTypeNode(c, n[1], nil)
       if base.kind in {tyVar, tyLent}:
         localError(c.config, n.info, "type 'var var' is not allowed")
-        base = base.sons[0]
-      addSonSkipIntLit(result, base)
+        base = base[0]
+      addSonSkipIntLit(result, base, c.idgen)
     of mRef: result = semAnyRef(c, n, tyRef, prev)
     of mPtr: result = semAnyRef(c, n, tyPtr, prev)
     of mTuple: result = semTuple(c, n, prev)
+    of mBuiltinType:
+      case s.name.s
+      of "lent": result = semAnyRef(c, n, tyLent, prev)
+      of "sink": result = semAnyRef(c, n, tySink, prev)
+      of "owned": result = semAnyRef(c, n, tyOwned, prev)
+      else: result = semGeneric(c, n, s, prev)
     else: result = semGeneric(c, n, s, prev)
   of nkDotExpr:
     let typeExpr = semExpr(c, n)
@@ -1505,7 +2178,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       if s.kind != skError: localError(c.config, n.info, errTypeExpected)
       result = newOrPrevType(tyError, prev, c)
     elif s.kind == skParam and s.typ.kind == tyTypeDesc:
-      internalAssert c.config, s.typ.base.kind != tyNone and prev == nil
+      internalAssert c.config, s.typ.base.kind != tyNone
       result = s.typ.base
     elif prev == nil:
       result = s.typ
@@ -1513,76 +2186,91 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       let alias = maybeAliasType(c, s.typ, prev)
       if alias != nil:
         result = alias
+      elif prev.kind == tyGenericBody:
+        result = s.typ
       else:
         assignType(prev, s.typ)
         # bugfix: keep the fresh id for aliases to integral types:
         if s.typ.kind notin {tyBool, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
                              tyUInt..tyUInt64}:
-          prev.id = s.typ.id
+          prev.itemId = s.typ.itemId
         result = prev
   of nkSym:
     let s = getGenSym(c, n.sym)
-    if s.kind == skType and s.typ != nil:
-      var t = s.typ
+    if s.typ != nil and (s.kind == skType or s.typ.kind == tyTypeDesc):
+      var t =
+        if s.kind == skType:
+          s.typ
+        else:
+          internalAssert c.config, s.typ.base.kind != tyNone
+          s.typ.base
       let alias = maybeAliasType(c, t, prev)
       if alias != nil:
         result = alias
-      elif prev == nil:
+      elif prev == nil or prev.kind == tyGenericBody:
         result = t
       else:
         assignType(prev, t)
         result = prev
-      markUsed(c.config, n.info, n.sym, c.graph.usageSym)
-      styleCheckUse(n.info, n.sym)
+      markUsed(c, n.info, n.sym)
+      onUse(n.info, n.sym)
     else:
-      if s.kind != skError: localError(c.config, n.info, errTypeExpected)
+      if s.kind != skError:
+        if s.typ == nil:
+          localError(c.config, n.info, "type expected, but symbol '$1' has no type." % [s.name.s])
+        else:
+          localError(c.config, n.info, "type expected, but got symbol '$1' of kind '$2'" %
+            [s.name.s, s.kind.toHumanStr])
       result = newOrPrevType(tyError, prev, c)
-  of nkObjectTy: result = semObjectNode(c, n, prev)
+  of nkObjectTy: result = semObjectNode(c, n, prev, {})
   of nkTupleTy: result = semTuple(c, n, prev)
   of nkTupleClassTy: result = newConstraint(c, tyTuple)
   of nkTypeClassTy: result = semTypeClass(c, n, prev)
   of nkRefTy: result = semAnyRef(c, n, tyRef, prev)
   of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev)
-  of nkVarTy: result = semVarType(c, n, prev)
+  of nkVarTy: result = semVarOutType(c, n, prev, {})
+  of nkOutTy: result = semVarOutType(c, n, prev, {tfIsOutParam})
   of nkDistinctTy: result = semDistinct(c, n, prev)
-  of nkStaticTy:
-    result = newOrPrevType(tyStatic, prev, c)
-    var base = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
-    result.rawAddSon(base)
-    result.flags.incl tfHasStatic
-  of nkIteratorTy:
-    if n.sonsLen == 0:
+  of nkStaticTy: result = semStaticType(c, n[0], prev)
+  of nkProcTy, nkIteratorTy:
+    if n.len == 0 or n[0].kind == nkEmpty:
+      # 0 length or empty param list with possible pragmas imply typeclass
       result = newTypeS(tyBuiltInTypeClass, c)
       let child = newTypeS(tyProc, c)
-      child.flags.incl tfIterator
-      result.addSonSkipIntLit(child)
-    else:
-      result = semProcTypeWithScope(c, n, prev, skIterator)
-      result.flags.incl(tfIterator)
-      if n.lastSon.kind == nkPragma and hasPragma(n.lastSon, wInline):
-        result.callConv = ccInline
-      else:
-        result.callConv = ccClosure
-  of nkProcTy:
-    if n.sonsLen == 0:
-      result = newConstraint(c, tyProc)
+      if n.kind == nkIteratorTy:
+        child.flags.incl tfIterator
+      if n.len > 0 and n[1].kind != nkEmpty and n[1].len > 0:
+        # typeclass with pragma
+        let symKind = if n.kind == nkIteratorTy: skIterator else: skProc
+        # dummy symbol for `pragma`:
+        var s = newSymS(symKind, newIdentNode(getIdent(c.cache, "dummy"), n.info), c)
+        s.typ = child
+        # for now only call convention pragmas supported in proc typeclass
+        pragma(c, s, n[1], {FirstCallConv..LastCallConv})
+      result.addSonSkipIntLit(child, c.idgen)
     else:
-      result = semProcTypeWithScope(c, n, prev, skProc)
+      let symKind = if n.kind == nkIteratorTy: skIterator else: skProc
+      result = semProcTypeWithScope(c, n, prev, symKind)
+      if result == nil:
+        localError(c.config, n.info, "type expected, but got: " & renderTree(n))
+        result = newOrPrevType(tyError, prev, c)
+
+      if n.kind == nkIteratorTy and result.kind == tyProc:
+        result.flags.incl(tfIterator)
+      if result.callConv == ccClosure and c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
+        result.flags.incl tfHasAsgn
   of nkEnumTy: result = semEnum(c, n, prev)
   of nkType: result = n.typ
   of nkStmtListType: result = semStmtListType(c, n, prev)
   of nkBlockType: result = semBlockType(c, n, prev)
+  of nkOpenSym: result = semTypeNode(c, n[0], prev)
   else:
-    localError(c.config, n.info, errTypeExpected)
-    result = newOrPrevType(tyError, prev, c)
+    result = semTypeExpr(c, n, prev)
+    when false:
+      localError(c.config, n.info, "type expected, but got: " & renderTree(n))
+      result = newOrPrevType(tyError, prev, c)
   n.typ = result
   dec c.inTypeContext
-  if c.inTypeContext == 0: instAllTypeBoundOp(c, n.info)
-
-when false:
-  proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
-    result = semTypeNodeInner(c, n, prev)
-    instAllTypeBoundOp(c, n.info)
 
 proc setMagicType(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) =
   # source : https://en.wikipedia.org/wiki/Data_structure_alignment#x86
@@ -1595,160 +2283,180 @@ proc setMagicType(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) =
 
   # FIXME: proper support for clongdouble should be added.
   # long double size can be 8, 10, 12, 16 bytes depending on platform & compiler
-  if conf.target.targetCPU == cpuI386 and size == 8:
-    #on Linux/BSD i386, double are aligned to 4bytes (except with -malign-double)
-    if kind in {tyFloat64, tyFloat} and
-        conf.target.targetOS in {osLinux, osAndroid, osNetbsd, osFreebsd, osOpenbsd, osDragonfly}:
-      m.typ.align = 4
-    # on i386, all known compiler, 64bits ints are aligned to 4bytes (except with -malign-double)
-    elif kind in {tyInt, tyUInt, tyInt64, tyUInt64}:
-      m.typ.align = 4
-  else:
-    discard
+  if kind in {tyFloat64, tyFloat, tyInt, tyUInt, tyInt64, tyUInt64} and size == 8:
+    m.typ.align = int16(conf.floatInt64Align)
+
+proc setMagicIntegral(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) =
+  setMagicType(conf, m, kind, size)
+  incl m.typ.flags, tfCheckedForDestructor
 
 proc processMagicType(c: PContext, m: PSym) =
   case m.magic
-  of mInt: setMagicType(c.config, m, tyInt, c.config.target.intSize)
-  of mInt8: setMagicType(c.config, m, tyInt8, 1)
-  of mInt16: setMagicType(c.config, m, tyInt16, 2)
-  of mInt32: setMagicType(c.config, m, tyInt32, 4)
-  of mInt64: setMagicType(c.config, m, tyInt64, 8)
-  of mUInt: setMagicType(c.config, m, tyUInt, c.config.target.intSize)
-  of mUInt8: setMagicType(c.config, m, tyUInt8, 1)
-  of mUInt16: setMagicType(c.config, m, tyUInt16, 2)
-  of mUInt32: setMagicType(c.config, m, tyUInt32, 4)
-  of mUInt64: setMagicType(c.config, m, tyUInt64, 8)
-  of mFloat: setMagicType(c.config, m, tyFloat, c.config.target.floatSize)
-  of mFloat32: setMagicType(c.config, m, tyFloat32, 4)
-  of mFloat64: setMagicType(c.config, m, tyFloat64, 8)
-  of mFloat128: setMagicType(c.config, m, tyFloat128, 16)
-  of mBool: setMagicType(c.config, m, tyBool, 1)
-  of mChar: setMagicType(c.config, m, tyChar, 1)
+  of mInt: setMagicIntegral(c.config, m, tyInt, c.config.target.intSize)
+  of mInt8: setMagicIntegral(c.config, m, tyInt8, 1)
+  of mInt16: setMagicIntegral(c.config, m, tyInt16, 2)
+  of mInt32: setMagicIntegral(c.config, m, tyInt32, 4)
+  of mInt64: setMagicIntegral(c.config, m, tyInt64, 8)
+  of mUInt: setMagicIntegral(c.config, m, tyUInt, c.config.target.intSize)
+  of mUInt8: setMagicIntegral(c.config, m, tyUInt8, 1)
+  of mUInt16: setMagicIntegral(c.config, m, tyUInt16, 2)
+  of mUInt32: setMagicIntegral(c.config, m, tyUInt32, 4)
+  of mUInt64: setMagicIntegral(c.config, m, tyUInt64, 8)
+  of mFloat: setMagicIntegral(c.config, m, tyFloat, c.config.target.floatSize)
+  of mFloat32: setMagicIntegral(c.config, m, tyFloat32, 4)
+  of mFloat64: setMagicIntegral(c.config, m, tyFloat64, 8)
+  of mFloat128: setMagicIntegral(c.config, m, tyFloat128, 16)
+  of mBool: setMagicIntegral(c.config, m, tyBool, 1)
+  of mChar: setMagicIntegral(c.config, m, tyChar, 1)
   of mString:
-    setMagicType(c.config, m, tyString, c.config.target.ptrSize)
+    setMagicType(c.config, m, tyString, szUncomputedSize)
     rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
+    if optSeqDestructors in c.config.globalOptions:
+      incl m.typ.flags, tfHasAsgn
   of mCstring:
-    setMagicType(c.config, m, tyCString, c.config.target.ptrSize)
+    setMagicIntegral(c.config, m, tyCstring, c.config.target.ptrSize)
     rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
-  of mPointer: setMagicType(c.config, m, tyPointer, c.config.target.ptrSize)
-  of mEmptySet:
-    setMagicType(c.config, m, tySet, 1)
-    rawAddSon(m.typ, newTypeS(tyEmpty, c))
-  of mIntSetBaseType: setMagicType(c.config, m, tyRange, c.config.target.intSize)
+  of mPointer: setMagicIntegral(c.config, m, tyPointer, c.config.target.ptrSize)
   of mNil: setMagicType(c.config, m, tyNil, c.config.target.ptrSize)
   of mExpr:
     if m.name.s == "auto":
-      setMagicType(c.config, m, tyAnything, 0)
+      setMagicIntegral(c.config, m, tyAnything, 0)
     else:
-      setMagicType(c.config, m, tyExpr, 0)
-      if m.name.s == "expr": m.typ.flags.incl tfOldSchoolExprStmt
+      setMagicIntegral(c.config, m, tyUntyped, 0)
   of mStmt:
-    setMagicType(c.config, m, tyStmt, 0)
-    if m.name.s == "stmt": m.typ.flags.incl tfOldSchoolExprStmt
-  of mTypeDesc:
-    setMagicType(c.config, m, tyTypeDesc, 0)
+    setMagicIntegral(c.config, m, tyTyped, 0)
+  of mTypeDesc, mType:
+    setMagicIntegral(c.config, m, tyTypeDesc, 0)
+    rawAddSon(m.typ, newTypeS(tyNone, c))
+  of mStatic:
+    setMagicType(c.config, m, tyStatic, 0)
     rawAddSon(m.typ, newTypeS(tyNone, c))
   of mVoidType:
-    setMagicType(c.config, m, tyVoid, 0)
+    setMagicIntegral(c.config, m, tyVoid, 0)
   of mArray:
-    setMagicType(c.config, m, tyArray, 0)
+    setMagicType(c.config, m, tyArray, szUncomputedSize)
   of mOpenArray:
-    setMagicType(c.config, m, tyOpenArray, 0)
+    setMagicType(c.config, m, tyOpenArray, szUncomputedSize)
   of mVarargs:
-    setMagicType(c.config, m, tyVarargs, 0)
+    setMagicType(c.config, m, tyVarargs, szUncomputedSize)
   of mRange:
-    setMagicType(c.config, m, tyRange, 0)
+    setMagicIntegral(c.config, m, tyRange, szUncomputedSize)
     rawAddSon(m.typ, newTypeS(tyNone, c))
   of mSet:
-    setMagicType(c.config, m, tySet, 0)
+    setMagicIntegral(c.config, m, tySet, szUncomputedSize)
+  of mUncheckedArray:
+    setMagicIntegral(c.config, m, tyUncheckedArray, szUncomputedSize)
   of mSeq:
-    setMagicType(c.config, m, tySequence, 0)
-  of mOpt:
-    setMagicType(c.config, m, tyOpt, 0)
+    setMagicType(c.config, m, tySequence, szUncomputedSize)
+    if optSeqDestructors in c.config.globalOptions:
+      incl m.typ.flags, tfHasAsgn
+    if defined(nimsuggest) or c.config.cmd == cmdCheck: # bug #18985
+      discard
+    else:
+      assert c.graph.sysTypes[tySequence] == nil
+    c.graph.sysTypes[tySequence] = m.typ
   of mOrdinal:
-    setMagicType(c.config, m, tyOrdinal, 0)
+    setMagicIntegral(c.config, m, tyOrdinal, szUncomputedSize)
+    rawAddSon(m.typ, newTypeS(tyNone, c))
+  of mIterableType:
+    setMagicIntegral(c.config, m, tyIterable, 0)
     rawAddSon(m.typ, newTypeS(tyNone, c))
   of mPNimrodNode:
     incl m.typ.flags, tfTriggersCompileTime
+    incl m.typ.flags, tfCheckedForDestructor
   of mException: discard
   of mBuiltinType:
     case m.name.s
     of "lent": setMagicType(c.config, m, tyLent, c.config.target.ptrSize)
-    of "sink": setMagicType(c.config, m, tySink, 0)
+    of "sink": setMagicType(c.config, m, tySink, szUncomputedSize)
+    of "owned":
+      setMagicType(c.config, m, tyOwned, c.config.target.ptrSize)
+      incl m.typ.flags, tfHasOwned
     else: localError(c.config, m.info, errTypeExpected)
   else: localError(c.config, m.info, errTypeExpected)
 
 proc semGenericConstraints(c: PContext, x: PType): PType =
-  result = newTypeWithSons(c, tyGenericParam, @[x])
+  result = newTypeS(tyGenericParam, c, x)
 
 proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
+
+  template addSym(result: PNode, s: PSym): untyped =
+    if father != nil: addSonSkipIntLit(father, s.typ, c.idgen)
+    if sfGenSym notin s.flags: addDecl(c, s)
+    result.add newSymNode(s)
+
   result = copyNode(n)
   if n.kind != nkGenericParams:
     illFormedAst(n, c.config)
     return
-  for i in countup(0, sonsLen(n)-1):
-    var a = n.sons[i]
-    if a.kind != nkIdentDefs: illFormedAst(n, c.config)
-    let L = a.len
-    var def = a[^1]
-    let constraint = a[^2]
-    var typ: PType
+  for i in 0..<n.len:
+    var a = n[i]
+    case a.kind
+    of nkSym: result.addSym(a.sym)
+    of nkIdentDefs:
+      var def = a[^1]
+      let constraint = a[^2]
+      var typ: PType = nil
+
+      if constraint.kind != nkEmpty:
+        typ = semTypeNode(c, constraint, nil)
+        if typ.kind != tyStatic or typ.len == 0:
+          if typ.kind == tyTypeDesc:
+            if typ.elementType.kind == tyNone:
+              typ = newTypeS(tyTypeDesc, c, newTypeS(tyNone, c))
+              incl typ.flags, tfCheckedForDestructor
+          else:
+            typ = semGenericConstraints(c, typ)
 
-    if constraint.kind != nkEmpty:
-      typ = semTypeNode(c, constraint, nil)
-      if typ.kind != tyStatic or typ.len == 0:
-        if typ.kind == tyTypeDesc:
-          if typ.sons[0].kind == tyNone:
-            typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)])
+      if def.kind != nkEmpty:
+        def = semConstExpr(c, def)
+        if typ == nil:
+          if def.typ.kind != tyTypeDesc:
+            typ = newTypeS(tyStatic, c, def.typ)
         else:
-          typ = semGenericConstraints(c, typ)
+          # the following line fixes ``TV2*[T:SomeNumber=TR] = array[0..1, T]``
+          # from manyloc/named_argument_bug/triengine:
+          def.typ = def.typ.skipTypes({tyTypeDesc})
+          if not containsGenericType(def.typ):
+            def = fitNode(c, typ, def, def.info)
 
-    if def.kind != nkEmpty:
-      def = semConstExpr(c, def)
       if typ == nil:
-        if def.typ.kind != tyTypeDesc:
-          typ = newTypeWithSons(c, tyStatic, @[def.typ])
-      else:
-        # the following line fixes ``TV2*[T:SomeNumber=TR] = array[0..1, T]``
-        # from manyloc/named_argument_bug/triengine:
-        def.typ = def.typ.skipTypes({tyTypeDesc})
-        if not containsGenericType(def.typ):
-          def = fitNode(c, typ, def, def.info)
+        typ = newTypeS(tyGenericParam, c)
+        if father == nil: typ.flags.incl tfWildcard
 
-    if typ == nil:
-      typ = newTypeS(tyGenericParam, c)
-      if father == nil: typ.flags.incl tfWildcard
-
-    typ.flags.incl tfGenericTypeParam
-
-    for j in countup(0, L-3):
-      let finalType = if j == 0: typ
-                      else: copyType(typ, typ.owner, false)
-                      # it's important the we create an unique
-                      # type for each generic param. the index
-                      # of the parameter will be stored in the
-                      # attached symbol.
-      var paramName = a.sons[j]
-      var covarianceFlag = tfUnresolved
-
-      if paramName.safeLen == 2:
-        if not nimEnableCovariance or paramName[0].ident.s == "in":
-          if father == nil or sfImportc notin father.sym.flags:
-            localError(c.config, paramName.info, errInOutFlagNotExtern % $paramName[0])
-        covarianceFlag = if paramName[0].ident.s == "in": tfContravariant
-                         else: tfCovariant
-        if father != nil: father.flags.incl tfCovariant
-        paramName = paramName[1]
-
-      var s = if finalType.kind == tyStatic or tfWildcard in typ.flags:
-          newSymG(skGenericParam, paramName, c).linkTo(finalType)
-        else:
-          newSymG(skType, paramName, c).linkTo(finalType)
+      typ.flags.incl tfGenericTypeParam
 
-      if covarianceFlag != tfUnresolved: s.typ.flags.incl(covarianceFlag)
-      if def.kind != nkEmpty: s.ast = def
-      if father != nil: addSonSkipIntLit(father, s.typ)
-      s.position = result.len
-      addSon(result, newSymNode(s))
-      if sfGenSym notin s.flags: addDecl(c, s)
+      for j in 0..<a.len-2:
+        var finalType: PType
+        if j == 0:
+          finalType = typ
+        else:
+          finalType = copyType(typ, c.idgen, typ.owner)
+          copyTypeProps(c.graph, c.idgen.module, finalType, typ)
+        # it's important the we create an unique
+        # type for each generic param. the index
+        # of the parameter will be stored in the
+        # attached symbol.
+        var paramName = a[j]
+        var covarianceFlag = tfUnresolved
+
+        if paramName.safeLen == 2:
+          if not nimEnableCovariance or paramName[0].ident.s == "in":
+            if father == nil or sfImportc notin father.sym.flags:
+              localError(c.config, paramName.info, errInOutFlagNotExtern % $paramName[0])
+          covarianceFlag = if paramName[0].ident.s == "in": tfContravariant
+                          else: tfCovariant
+          if father != nil: father.flags.incl tfCovariant
+          paramName = paramName[1]
+
+        var s = if finalType.kind == tyStatic or tfWildcard in typ.flags:
+            newSymG(skGenericParam, paramName, c).linkTo(finalType)
+          else:
+            newSymG(skType, paramName, c).linkTo(finalType)
 
+        if covarianceFlag != tfUnresolved: s.typ.flags.incl(covarianceFlag)
+        if def.kind != nkEmpty: s.ast = def
+        s.position = result.len
+        result.addSym(s)
+    else:
+      illFormedAst(n, c.config)
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index a24972d04..759e8e6ab 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -9,110 +9,104 @@
 
 # This module does the instantiation of generic types.
 
+import std / tables
+
 import ast, astalgo, msgs, types, magicsys, semdata, renderer, options,
-  lineinfos
+  lineinfos, modulegraphs
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
-const
-  tfInstClearedFlags = {tfHasMeta, tfUnresolved}
+const tfInstClearedFlags = {tfHasMeta, tfUnresolved}
 
 proc checkPartialConstructedType(conf: ConfigRef; info: TLineInfo, t: PType) =
-  if tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
-    localError(conf, info, "invalid pragma: acyclic")
-  elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}:
+  if t.kind in {tyVar, tyLent} and t.elementType.kind in {tyVar, tyLent}:
     localError(conf, info, "type 'var var' is not allowed")
 
 proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) =
   var t = typ.skipTypes({tyDistinct})
   if t.kind in tyTypeClasses: discard
-  elif tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
-    localError(conf, info, "invalid pragma: acyclic")
-  elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}:
+  elif t.kind in {tyVar, tyLent} and t.elementType.kind in {tyVar, tyLent}:
     localError(conf, info, "type 'var var' is not allowed")
-  elif computeSize(conf, t) == szIllegalRecursion:
-    localError(conf, info,  "illegal recursion in type '" & typeToString(t) & "'")
-  when false:
-    if t.kind == tyObject and t.sons[0] != nil:
-      if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags:
-        localError(info, errInheritanceOnlyWithNonFinalObjects)
+  elif computeSize(conf, t) == szIllegalRecursion or isTupleRecursive(t):
+    localError(conf, info, "illegal recursion in type '" & typeToString(t) & "'")
 
-proc searchInstTypes*(key: PType): PType =
-  let genericTyp = key.sons[0]
+proc searchInstTypes*(g: ModuleGraph; key: PType): PType =
+  result = nil
+  let genericTyp = key[0]
   if not (genericTyp.kind == tyGenericBody and
-      key.sons[0] == genericTyp and genericTyp.sym != nil): return
+      genericTyp.sym != nil): return
 
-  if genericTyp.sym.typeInstCache == nil:
-    return
-
-  for inst in genericTyp.sym.typeInstCache:
+  for inst in typeInstCacheItems(g, genericTyp.sym):
     if inst.id == key.id: return inst
-    if inst.sons.len < key.sons.len:
+    if inst.kidsLen < key.kidsLen:
       # XXX: This happens for prematurely cached
       # types such as Channel[empty]. Why?
       # See the notes for PActor in handleGenericInvocation
-      return
+      # if this is return the same type gets cached more than it needs to
+      continue
     if not sameFlags(inst, key):
       continue
 
     block matchType:
-      for j in 1 .. high(key.sons):
+      for j in FirstGenericParamAt..<key.kidsLen:
         # XXX sameType is not really correct for nested generics?
-        if not compareTypes(inst.sons[j], key.sons[j],
-                            flags = {ExactGenericParams}):
+        if not compareTypes(inst[j], key[j],
+                            flags = {ExactGenericParams, PickyCAliases}):
           break matchType
 
       return inst
 
-proc cacheTypeInst*(inst: PType) =
-  # XXX: add to module's generics
-  #      update the refcount
-  let gt = inst.sons[0]
-  let t = if gt.kind == tyGenericBody: gt.lastSon else: gt
-  if t.kind in {tyStatic, tyGenericParam} + tyTypeClasses:
+proc cacheTypeInst(c: PContext; inst: PType) =
+  let gt = inst[0]
+  let t = if gt.kind == tyGenericBody: gt.typeBodyImpl else: gt
+  if t.kind in {tyStatic, tyError, tyGenericParam} + tyTypeClasses:
     return
-  gt.sym.typeInstCache.safeAdd(inst)
-
+  addToGenericCache(c, gt.sym, inst)
 
 type
-  LayeredIdTable* = object
-    topLayer*: TIdTable
-    nextLayer*: ptr LayeredIdTable
+  LayeredIdTable* {.acyclic.} = ref object
+    topLayer*: TypeMapping
+    nextLayer*: LayeredIdTable
 
   TReplTypeVars* = object
     c*: PContext
-    typeMap*: ptr LayeredIdTable # map PType to PType
-    symMap*: TIdTable         # map PSym to PSym
-    localCache*: TIdTable     # local cache for remembering alraedy replaced
+    typeMap*: LayeredIdTable  # map PType to PType
+    symMap*: SymMapping         # map PSym to PSym
+    localCache*: TypeMapping     # local cache for remembering already replaced
                               # types during instantiation of meta types
                               # (they are not stored in the global cache)
     info*: TLineInfo
     allowMetaTypes*: bool     # allow types such as seq[Number]
                               # i.e. the result contains unresolved generics
-    skipTypedesc*: bool       # wether we should skip typeDescs
+    skipTypedesc*: bool       # whether we should skip typeDescs
+    isReturnType*: bool
     owner*: PSym              # where this instantiation comes from
     recursionLimit: int
 
 proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
-proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
-proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0): PNode
+proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym
+proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PType = nil): PNode
 
-proc initLayeredTypeMap*(pt: TIdTable): LayeredIdTable =
-  copyIdTable(result.topLayer, pt)
+proc initLayeredTypeMap*(pt: sink TypeMapping): LayeredIdTable =
+  result = LayeredIdTable()
+  result.topLayer = pt
 
 proc newTypeMapLayer*(cl: var TReplTypeVars): LayeredIdTable =
-  result.nextLayer = cl.typeMap
-  initIdTable(result.topLayer)
+  result = LayeredIdTable(nextLayer: cl.typeMap, topLayer: initTable[ItemId, PType]())
 
-proc lookup(typeMap: ptr LayeredIdTable, key: PType): PType =
+proc lookup(typeMap: LayeredIdTable, key: PType): PType =
+  result = nil
   var tm = typeMap
   while tm != nil:
-    result = PType(idTableGet(tm.topLayer, key))
+    result = getOrDefault(tm.topLayer, key.itemId)
     if result != nil: return
     tm = tm.nextLayer
 
-template put(typeMap: ptr LayeredIdTable, key, value: PType) =
-  idTablePut(typeMap.topLayer, key, value)
+template put(typeMap: LayeredIdTable, key, value: PType) =
+  typeMap.topLayer[key.itemId] = value
 
-template checkMetaInvariants(cl: TReplTypeVars, t: PType) =
+template checkMetaInvariants(cl: TReplTypeVars, t: PType) = # noop code
   when false:
     if t != nil and tfHasMeta in t.flags and
        cl.allowMetaTypes == false:
@@ -124,19 +118,86 @@ proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
   result = replaceTypeVarsTAux(cl, t)
   checkMetaInvariants(cl, result)
 
-proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
+proc prepareNode*(cl: var TReplTypeVars, n: PNode): PNode =
+  ## instantiates a given generic expression, not a type node
+  if n.kind == nkSym and n.sym.kind == skType and
+      n.sym.typ != nil and n.sym.typ.kind == tyGenericBody:
+    # generic body types are allowed as user expressions, see #24090
+    return n
   let t = replaceTypeVarsT(cl, n.typ)
   if t != nil and t.kind == tyStatic and t.n != nil:
     return if tfUnresolved in t.flags: prepareNode(cl, t.n)
            else: t.n
   result = copyNode(n)
   result.typ = t
-  if result.kind == nkSym: result.sym = replaceTypeVarsS(cl, n.sym)
-  let isCall = result.kind in nkCallKinds
-  for i in 0 ..< n.safeLen:
-    # XXX HACK: ``f(a, b)``, avoid to instantiate `f`
-    if isCall and i == 0: result.add(n[i])
-    else: result.add(prepareNode(cl, n[i]))
+  if result.kind == nkSym:
+    result.sym =
+      if n.typ != nil and n.typ == n.sym.typ:
+        replaceTypeVarsS(cl, n.sym, result.typ)
+      else:
+        replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ))
+  # we need to avoid trying to instantiate nodes that can have uninstantiated
+  # types, like generic proc symbols or raw generic type symbols
+  case n.kind
+  of nkSymChoices:
+    # don't try to instantiate symchoice symbols, they can be
+    # generic procs which the compiler will think are uninstantiated
+    # because their type will contain uninstantiated params
+    for i in 0..<n.len:
+      result.add(n[i])
+  of nkCallKinds:
+    # don't try to instantiate call names since they may be generic proc syms
+    # also bracket expressions can turn into calls with symchoice [] and
+    # we need to not instantiate the Generic in Generic[int]
+    # exception exists for the call name being a dot expression since
+    # dot expressions need their LHS instantiated
+    assert n.len != 0
+    # avoid instantiating generic proc symbols, refine condition if needed:
+    let ignoreFirst = n[0].kind notin {nkDotExpr, nkBracketExpr} + nkCallKinds
+    let name = n[0].getPIdent
+    let ignoreSecond = name != nil and name.s == "[]" and n.len > 1 and
+      # generic type instantiation:
+      ((n[1].typ != nil and n[1].typ.kind == tyTypeDesc) or
+        # generic proc instantiation:
+        (n[1].kind == nkSym and n[1].sym.isGenericRoutineStrict))
+    if ignoreFirst:
+      result.add(n[0])
+    else:
+      result.add(prepareNode(cl, n[0]))
+    if n.len > 1:
+      if ignoreSecond:
+        result.add(n[1])
+      else:
+        result.add(prepareNode(cl, n[1]))
+    for i in 2..<n.len:
+      result.add(prepareNode(cl, n[i]))
+  of nkBracketExpr:
+    # don't instantiate Generic body type in expression like Generic[T]
+    # exception exists for the call name being a dot expression since
+    # dot expressions need their LHS instantiated
+    assert n.len != 0
+    let ignoreFirst = n[0].kind != nkDotExpr and
+      # generic type instantiation:
+      ((n[0].typ != nil and n[0].typ.kind == tyTypeDesc) or
+        # generic proc instantiation:
+        (n[0].kind == nkSym and n[0].sym.isGenericRoutineStrict))
+    if ignoreFirst:
+      result.add(n[0])
+    else:
+      result.add(prepareNode(cl, n[0]))
+    for i in 1..<n.len:
+      result.add(prepareNode(cl, n[i]))
+  of nkDotExpr:
+    # don't try to instantiate RHS of dot expression, it can outright be
+    # undeclared, but definitely instantiate LHS
+    assert n.len >= 2
+    result.add(prepareNode(cl, n[0]))
+    result.add(n[1])
+    for i in 2..<n.len:
+      result.add(prepareNode(cl, n[i]))
+  else:
+    for i in 0..<n.safeLen:
+      result.add(prepareNode(cl, n[i]))
 
 proc isTypeParam(n: PNode): bool =
   # XXX: generic params should use skGenericParam instead of skType
@@ -144,69 +205,117 @@ proc isTypeParam(n: PNode): bool =
          (n.sym.kind == skGenericParam or
            (n.sym.kind == skType and sfFromGeneric in n.sym.flags))
 
-proc hasGenericArguments*(n: PNode): bool =
-  if n.kind == nkSym:
-    return n.sym.kind == skGenericParam or
-           tfInferrableStatic in n.sym.typ.flags or
-           (n.sym.kind == skType and
-            n.sym.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {})
-  else:
+when false: # old workaround
+  proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
+    # This is needed for tuninstantiatedgenericcalls
+    # It's possible that a generic param will be used in a proc call to a
+    # typedesc accepting proc. After generic param substitution, such procs
+    # should be optionally instantiated with the correct type. In order to
+    # perform this instantiation, we need to re-run the generateInstance path
+    # in the compiler, but it's quite complicated to do so at the moment so we
+    # resort to a mild hack; the head symbol of the call is temporary reset and
+    # overload resolution is executed again (which may trigger generateInstance).
+    if n.kind in nkCallKinds and sfFromGeneric in n[0].sym.flags:
+      var needsFixing = false
+      for i in 1..<n.safeLen:
+        if isTypeParam(n[i]): needsFixing = true
+      if needsFixing:
+        n[0] = newSymNode(n[0].sym.owner)
+        return cl.c.semOverloadedCall(cl.c, n, n, {skProc, skFunc}, {})
+
     for i in 0..<n.safeLen:
-      if hasGenericArguments(n.sons[i]): return true
-    return false
-
-proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
-  # This is needed for tgenericshardcases
-  # It's possible that a generic param will be used in a proc call to a
-  # typedesc accepting proc. After generic param substitution, such procs
-  # should be optionally instantiated with the correct type. In order to
-  # perform this instantiation, we need to re-run the generateInstance path
-  # in the compiler, but it's quite complicated to do so at the moment so we
-  # resort to a mild hack; the head symbol of the call is temporary reset and
-  # overload resolution is executed again (which may trigger generateInstance).
-  if n.kind in nkCallKinds and sfFromGeneric in n[0].sym.flags:
-    var needsFixing = false
-    for i in 1 ..< n.safeLen:
-      if isTypeParam(n[i]): needsFixing = true
-    if needsFixing:
-      n.sons[0] = newSymNode(n.sons[0].sym.owner)
-      return cl.c.semOverloadedCall(cl.c, n, n, {skProc, skFunc}, {})
-
-  for i in 0 ..< n.safeLen:
-    n.sons[i] = reResolveCallsWithTypedescParams(cl, n[i])
-
-  return n
-
-proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode =
+      n[i] = reResolveCallsWithTypedescParams(cl, n[i])
+
+    return n
+
+proc replaceObjBranches(cl: TReplTypeVars, n: PNode): PNode =
+  result = n
+  case n.kind
+  of nkNone..nkNilLit:
+    discard
+  of nkRecWhen:
+    var branch: PNode = nil              # the branch to take
+    for i in 0..<n.len:
+      var it = n[i]
+      if it == nil: illFormedAst(n, cl.c.config)
+      case it.kind
+      of nkElifBranch:
+        checkSonsLen(it, 2, cl.c.config)
+        var cond = it[0]
+        var e = cl.c.semConstExpr(cl.c, cond)
+        if e.kind != nkIntLit:
+          internalError(cl.c.config, e.info, "ReplaceTypeVarsN: when condition not a bool")
+        if e.intVal != 0 and branch == nil: branch = it[1]
+      of nkElse:
+        checkSonsLen(it, 1, cl.c.config)
+        if branch == nil: branch = it[0]
+      else: illFormedAst(n, cl.c.config)
+    if branch != nil:
+      result = replaceObjBranches(cl, branch)
+    else:
+      result = newNodeI(nkRecList, n.info)
+  else:
+    for i in 0..<n.len:
+      n[i] = replaceObjBranches(cl, n[i])
+
+proc hasValuelessStatics(n: PNode): bool =
+  # We should only attempt to call an expression that has no tyStatics
+  # As those are unresolved generic parameters, which means in the following
+  # The compiler attempts to do `T == 300` which errors since the typeclass `MyThing` lacks a parameter
+  #[
+    type MyThing[T: static int] = object
+      when T == 300:
+        a
+    proc doThing(_: MyThing)
+  ]#
+  if n.safeLen == 0 and n.kind != nkEmpty: # Some empty nodes can get in here
+    n.typ == nil or n.typ.kind == tyStatic
+  else:
+    for x in n:
+      if hasValuelessStatics(x):
+        return true
+    false
+
+proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PType = nil): PNode =
   if n == nil: return
   result = copyNode(n)
   if n.typ != nil:
+    if n.typ.kind == tyFromExpr:
+      # type of node should not be evaluated as a static value
+      n.typ.flags.incl tfNonConstExpr
     result.typ = replaceTypeVarsT(cl, n.typ)
     checkMetaInvariants(cl, result.typ)
   case n.kind
   of nkNone..pred(nkSym), succ(nkSym)..nkNilLit:
     discard
+  of nkOpenSymChoice, nkClosedSymChoice: result = n
   of nkSym:
-    result.sym = replaceTypeVarsS(cl, n.sym)
-    if result.sym.typ.kind == tyVoid:
+    result.sym =
+      if n.typ != nil and n.typ == n.sym.typ:
+        replaceTypeVarsS(cl, n.sym, result.typ)
+      else:
+        replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ))
+    # sym type can be nil if was gensym created by macro, see #24048
+    if result.sym.typ != nil and result.sym.typ.kind == tyVoid:
       # don't add the 'void' field
-      result = newNode(nkRecList, n.info)
+      result = newNodeI(nkRecList, n.info)
   of nkRecWhen:
     var branch: PNode = nil              # the branch to take
-    for i in countup(0, sonsLen(n) - 1):
-      var it = n.sons[i]
+    for i in 0..<n.len:
+      var it = n[i]
       if it == nil: illFormedAst(n, cl.c.config)
       case it.kind
       of nkElifBranch:
         checkSonsLen(it, 2, cl.c.config)
-        var cond = prepareNode(cl, it.sons[0])
-        var e = cl.c.semConstExpr(cl.c, cond)
-        if e.kind != nkIntLit:
-          internalError(cl.c.config, e.info, "ReplaceTypeVarsN: when condition not a bool")
-        if e.intVal != 0 and branch == nil: branch = it.sons[1]
+        var cond = prepareNode(cl, it[0])
+        if not cond.hasValuelessStatics:
+          var e = cl.c.semConstExpr(cl.c, cond)
+          if e.kind != nkIntLit:
+            internalError(cl.c.config, e.info, "ReplaceTypeVarsN: when condition not a bool")
+          if e.intVal != 0 and branch == nil: branch = it[1]
       of nkElse:
         checkSonsLen(it, 1, cl.c.config)
-        if branch == nil: branch = it.sons[0]
+        if branch == nil: branch = it[0]
       else: illFormedAst(n, cl.c.config)
     if branch != nil:
       result = replaceTypeVarsN(cl, branch)
@@ -214,33 +323,69 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode =
       result = newNodeI(nkRecList, n.info)
   of nkStaticExpr:
     var n = prepareNode(cl, n)
-    n = reResolveCallsWithTypedescParams(cl, n)
+    when false:
+      n = reResolveCallsWithTypedescParams(cl, n)
     result = if cl.allowMetaTypes: n
-             else: cl.c.semExpr(cl.c, n)
+             else: cl.c.semExpr(cl.c, n, {}, expectedType)
+    if not cl.allowMetaTypes and expectedType != nil:
+      assert result.kind notin nkCallKinds
   else:
-    var length = sonsLen(n)
-    if length > 0:
-      newSons(result, length)
+    if n.len > 0:
+      newSons(result, n.len)
       if start > 0:
-        result.sons[0] = n.sons[0]
-      for i in countup(start, length - 1):
-        result.sons[i] = replaceTypeVarsN(cl, n.sons[i])
+        result[0] = n[0]
+      for i in start..<n.len:
+        result[i] = replaceTypeVarsN(cl, n[i])
 
-proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
+proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym =
   if s == nil: return nil
   # symbol is not our business:
   if cl.owner != nil and s.owner != cl.owner:
     return s
+
+  # XXX: Bound symbols in default parameter expressions may reach here.
+  # We cannot process them, because `sym.n` may point to a proc body with
+  # cyclic references that will lead to an infinite recursion.
+  # Perhaps we should not use a black-list here, but a whitelist instead
+  # (e.g. skGenericParam and skType).
+  # Note: `s.magic` may be `mType` in an example such as:
+  # proc foo[T](a: T, b = myDefault(type(a)))
+  if s.kind in routineKinds+{skLet, skConst, skVar} or s.magic != mNone:
+    return s
+
   #result = PSym(idTableGet(cl.symMap, s))
   #if result == nil:
-  result = copySym(s, false)
+  #[
+
+  We cannot naively check for symbol recursions, because otherwise
+  object types A, B whould share their fields!
+
+      import tables
+
+      type
+        Table[S, T] = object
+          x: S
+          y: T
+
+        G[T] = object
+          inodes: Table[int, T] # A
+          rnodes: Table[T, int] # B
+
+      var g: G[string]
+
+  ]#
+  result = copySym(s, cl.c.idgen)
   incl(result.flags, sfFromGeneric)
   #idTablePut(cl.symMap, s, result)
   result.owner = s.owner
-  result.typ = replaceTypeVarsT(cl, s.typ)
-  result.ast = replaceTypeVarsN(cl, s.ast)
+  result.typ = t
+  if result.kind != skType:
+    result.ast = replaceTypeVarsN(cl, s.ast)
 
 proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType =
+  if tfRetType in t.flags and t.kind == tyAnything:
+    # don't bind `auto` return type to a previous binding of `auto`
+    return nil
   result = cl.typeMap.lookup(t)
   if result == nil:
     if cl.allowMetaTypes or tfRetType in t.flags: return
@@ -255,89 +400,102 @@ proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType =
 
 proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
   # XXX: relying on allowMetaTypes is a kludge
-  result = copyType(t, t.owner, cl.allowMetaTypes)
+  if cl.allowMetaTypes:
+    result = t.exactReplica
+  else:
+    result = copyType(t, cl.c.idgen, t.owner)
+    copyTypeProps(cl.c.graph, cl.c.idgen.module, result, t)
+    #cl.typeMap.topLayer.idTablePut(result, t)
+
   if cl.allowMetaTypes: return
   result.flags.incl tfFromGeneric
   if not (t.kind in tyMetaTypes or
          (t.kind == tyStatic and t.n == nil)):
     result.flags.excl tfInstClearedFlags
+  else:
+    result.flags.excl tfHasAsgn
   when false:
     if newDestructors:
       result.assignment = nil
-      #result.destructor = nil
+      result.destructor = nil
       result.sink = nil
 
-template typeBound(c, newty, oldty, field, info) =
-  let opr = newty.field
-  if opr != nil and sfFromGeneric notin opr.flags:
-    # '=' needs to be instantiated for generics when the type is constructed:
-    newty.field = c.instTypeBoundOp(c, opr, oldty, info, attachedAsgn, 1)
-
 proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
   # tyGenericInvocation[A, tyGenericInvocation[A, B]]
   # is difficult to handle:
-  const eqFlags = eqTypeFlags + {tfGcSafe}
-  var body = t.sons[0]
-  if body.kind != tyGenericBody: internalError(cl.c.config, cl.info, "no generic body")
-  var header: PType = t
+  var body = t.genericHead
+  if body.kind != tyGenericBody:
+    internalError(cl.c.config, cl.info, "no generic body")
+  var header = t
   # search for some instantiation here:
   if cl.allowMetaTypes:
-    result = PType(idTableGet(cl.localCache, t))
+    result = getOrDefault(cl.localCache, t.itemId)
   else:
-    result = searchInstTypes(t)
+    result = searchInstTypes(cl.c.graph, t)
 
-  if result != nil and eqFlags*result.flags == eqFlags*t.flags: return
-  for i in countup(1, sonsLen(t) - 1):
-    var x = t.sons[i]
+  if result != nil and sameFlags(result, t):
+    when defined(reportCacheHits):
+      echo "Generic instantiation cached ", typeToString(result), " for ", typeToString(t)
+    return
+  for i in FirstGenericParamAt..<t.kidsLen:
+    var x = t[i]
     if x.kind in {tyGenericParam}:
       x = lookupTypeVar(cl, x)
       if x != nil:
         if header == t: header = instCopyType(cl, t)
-        header.sons[i] = x
+        header[i] = x
         propagateToOwner(header, x)
     else:
       propagateToOwner(header, x)
 
   if header != t:
     # search again after first pass:
-    result = searchInstTypes(header)
-    if result != nil and eqFlags*result.flags == eqFlags*t.flags: return
+    result = searchInstTypes(cl.c.graph, header)
+    if result != nil and sameFlags(result, t):
+      when defined(reportCacheHits):
+        echo "Generic instantiation cached ", typeToString(result), " for ",
+          typeToString(t), " header ", typeToString(header)
+      return
   else:
     header = instCopyType(cl, t)
 
-  result = newType(tyGenericInst, t.sons[0].owner)
+  result = newType(tyGenericInst, cl.c.idgen, t.genericHead.owner, son = header.genericHead)
   result.flags = header.flags
   # be careful not to propagate unnecessary flags here (don't use rawAddSon)
-  result.sons = @[header.sons[0]]
   # ugh need another pass for deeply recursive generic types (e.g. PActor)
   # we need to add the candidate here, before it's fully instantiated for
   # recursive instantions:
   if not cl.allowMetaTypes:
-    cacheTypeInst(result)
+    cacheTypeInst(cl.c, result)
   else:
-    idTablePut(cl.localCache, t, result)
+    cl.localCache[t.itemId] = result
 
   let oldSkipTypedesc = cl.skipTypedesc
   cl.skipTypedesc = true
 
-  var typeMapLayer = newTypeMapLayer(cl)
-  cl.typeMap = addr(typeMapLayer)
+  cl.typeMap = newTypeMapLayer(cl)
 
-  for i in countup(1, sonsLen(t) - 1):
-    var x = replaceTypeVarsT(cl, t.sons[i])
+  for i in FirstGenericParamAt..<t.kidsLen:
+    var x = replaceTypeVarsT(cl):
+      if header[i].kind == tyGenericInst:
+        t[i]
+      else:
+        header[i]
     assert x.kind != tyGenericInvocation
-    header.sons[i] = x
+    header[i] = x
     propagateToOwner(header, x)
-    cl.typeMap.put(body.sons[i-1], x)
+    cl.typeMap.put(body[i-1], x)
 
-  for i in countup(1, sonsLen(t) - 1):
+  for i in FirstGenericParamAt..<t.kidsLen:
     # if one of the params is not concrete, we cannot do anything
     # but we already raised an error!
-    rawAddSon(result, header.sons[i])
+    rawAddSon(result, header[i], propagateHasAsgn = false)
+
+  if body.kind == tyError:
+    return
 
-  let bbody = lastSon body
+  let bbody = last body
   var newbody = replaceTypeVarsT(cl, bbody)
-  let bodyIsNew = newbody != bbody
   cl.skipTypedesc = oldSkipTypedesc
   newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags)
   result.flags = result.flags + newbody.flags - tfInstClearedFlags
@@ -350,67 +508,75 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
   # One step is enough, because the recursive nature of
   # handleGenericInvocation will handle the alias-to-alias-to-alias case
   if newbody.isGenericAlias: newbody = newbody.skipGenericAlias
+
   rawAddSon(result, newbody)
   checkPartialConstructedType(cl.c.config, cl.info, newbody)
-  let dc = newbody.deepCopy
-  if cl.allowMetaTypes == false:
-    if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
+  if not cl.allowMetaTypes:
+    let dc = cl.c.graph.getAttachedOp(newbody, attachedDeepCopy)
+    if dc != nil and sfFromGeneric notin dc.flags:
       # 'deepCopy' needs to be instantiated for
       # generics *when the type is constructed*:
-      newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
-                                              attachedDeepCopy, 1)
-    if bodyIsNew and newbody.typeInst == nil:
-      #doassert newbody.typeInst == nil
+      cl.c.graph.setAttachedOp(cl.c.module.position, newbody, attachedDeepCopy,
+          cl.c.instTypeBoundOp(cl.c, dc, result, cl.info, attachedDeepCopy, 1))
+    if newbody.typeInst == nil:
+      # doAssert newbody.typeInst == nil
       newbody.typeInst = result
       if tfRefsAnonObj in newbody.flags and newbody.kind != tyGenericInst:
         # can come here for tyGenericInst too, see tests/metatype/ttypeor.nim
         # need to look into this issue later
         assert newbody.kind in {tyRef, tyPtr}
-        assert newbody.lastSon.typeInst == nil
-        newbody.lastSon.typeInst = result
-    if destructor in cl.c.features:
-      cl.c.typesWithOps.add((newbody, result))
-    else:
-      typeBound(cl.c, newbody, result, assignment, cl.info)
-    let methods = skipTypes(bbody, abstractPtrs).methods
-    for col, meth in items(methods):
-      # we instantiate the known methods belonging to that type, this causes
-      # them to be registered and that's enough, so we 'discard' the result.
-      discard cl.c.instTypeBoundOp(cl.c, meth, result, cl.info,
-        attachedAsgn, col)
+        if newbody.last.typeInst != nil:
+          #internalError(cl.c.config, cl.info, "ref already has a 'typeInst' field")
+          discard
+        else:
+          newbody.last.typeInst = result
+    # DESTROY: adding object|opt for opt[topttree.Tree]
+    # sigmatch: Formal opt[=destroy.T] real opt[topttree.Tree]
+    # adding myseq for myseq[system.int]
+    # sigmatch: Formal myseq[=destroy.T] real myseq[system.int]
+    #echo "DESTROY: adding ", typeToString(newbody), " for ", typeToString(result, preferDesc)
+    let mm = skipTypes(bbody, abstractPtrs)
+    if tfFromGeneric notin mm.flags:
+      # bug #5479, prevent endless recursions here:
+      incl mm.flags, tfFromGeneric
+      for col, meth in methodsForGeneric(cl.c.graph, mm):
+        # we instantiate the known methods belonging to that type, this causes
+        # them to be registered and that's enough, so we 'discard' the result.
+        discard cl.c.instTypeBoundOp(cl.c, meth, result, cl.info,
+          attachedAsgn, col)
+      excl mm.flags, tfFromGeneric
 
 proc eraseVoidParams*(t: PType) =
   # transform '(): void' into '()' because old parts of the compiler really
   # don't deal with '(): void':
-  if t.sons[0] != nil and t.sons[0].kind == tyVoid:
-    t.sons[0] = nil
+  if t.returnType != nil and t.returnType.kind == tyVoid:
+    t.setReturnType nil
 
-  for i in 1 ..< t.sonsLen:
+  for i in FirstParamAt..<t.signatureLen:
     # don't touch any memory unless necessary
-    if t.sons[i].kind == tyVoid:
+    if t[i].kind == tyVoid:
       var pos = i
-      for j in i+1 ..< t.sonsLen:
-        if t.sons[j].kind != tyVoid:
-          t.sons[pos] = t.sons[j]
-          t.n.sons[pos] = t.n.sons[j]
+      for j in i+1..<t.signatureLen:
+        if t[j].kind != tyVoid:
+          t[pos] = t[j]
+          t.n[pos] = t.n[j]
           inc pos
-      setLen t.sons, pos
+      newSons t, pos
       setLen t.n.sons, pos
-      return
+      break
 
-proc skipIntLiteralParams*(t: PType) =
-  for i in 0 ..< t.sonsLen:
-    let p = t.sons[i]
+proc skipIntLiteralParams*(t: PType; idgen: IdGenerator) =
+  for i, p in t.ikids:
     if p == nil: continue
-    let skipped = p.skipIntLit
+    let skipped = p.skipIntLit(idgen)
     if skipped != p:
-      t.sons[i] = skipped
-      if i > 0: t.n.sons[i].sym.typ = skipped
+      t[i] = skipped
+      if i > 0: t.n[i].sym.typ = skipped
 
   # when the typeof operator is used on a static input
   # param, the results gets infected with static as well:
-  if t.sons[0] != nil and t.sons[0].kind == tyStatic:
-    t.sons[0] = t.sons[0].base
+  if t.returnType != nil and t.returnType.kind == tyStatic:
+    t.setReturnType t.returnType.skipModifier
 
 proc propagateFieldFlags(t: PType, n: PNode) =
   # This is meant for objects and tuples
@@ -428,33 +594,54 @@ proc propagateFieldFlags(t: PType, n: PNode) =
 
 proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
   template bailout =
-    if cl.recursionLimit > 100:
-      # bail out, see bug #2509. But note this caching is in general wrong,
-      # look at this example where TwoVectors should not share the generic
-      # instantiations (bug #3112):
-
-      # type
-      #   Vector[N: static[int]] = array[N, float64]
-      #   TwoVectors[Na, Nb: static[int]] = (Vector[Na], Vector[Nb])
-      result = PType(idTableGet(cl.localCache, t))
-      if result != nil: return result
-    inc cl.recursionLimit
+    if (t.sym == nil) or (t.sym != nil and sfGeneratedType in t.sym.flags):
+      # In the first case 't.sym' can be 'nil' if the type is a ref/ptr, see
+      # issue https://github.com/nim-lang/Nim/issues/20416 for more details.
+      # Fortunately for us this works for now because partial ref/ptr types are
+      # not allowed in object construction, eg.
+      #   type
+      #     Container[T] = ...
+      #     O = object
+      #      val: ref Container
+      #
+      # In the second case only consider the recursion limit if the symbol is a
+      # type with generic parameters that have not been explicitly supplied,
+      # typechecking should terminate when generic parameters are explicitly
+      # supplied.
+      if cl.recursionLimit > 100:
+        # bail out, see bug #2509. But note this caching is in general wrong,
+        # look at this example where TwoVectors should not share the generic
+        # instantiations (bug #3112):
+        # type
+        #   Vector[N: static[int]] = array[N, float64]
+        #   TwoVectors[Na, Nb: static[int]] = (Vector[Na], Vector[Nb])
+        result = getOrDefault(cl.localCache, t.itemId)
+        if result != nil: return result
+      inc cl.recursionLimit
 
   result = t
   if t == nil: return
 
-  if t.kind in {tyStatic, tyGenericParam} + tyTypeClasses:
+  const lookupMetas = {tyStatic, tyGenericParam, tyConcept} + tyTypeClasses - {tyAnything}
+  if t.kind in lookupMetas or
+      (t.kind == tyAnything and tfRetType notin t.flags):
     let lookup = cl.typeMap.lookup(t)
     if lookup != nil: return lookup
 
   case t.kind
   of tyGenericInvocation:
     result = handleGenericInvocation(cl, t)
-    if result.lastSon.kind == tyUserTypeClass:
+    if result.last.kind == tyUserTypeClass:
       result.kind = tyUserTypeClassInst
 
   of tyGenericBody:
-    localError(cl.c.config, cl.info, "cannot instantiate: '" & typeToString(t) & "'")
+    if cl.allowMetaTypes: return
+    localError(
+      cl.c.config,
+      cl.info,
+      "cannot instantiate: '" &
+      typeToString(t, preferDesc) &
+      "'; Maybe generic arguments are missing?")
     result = errorType(cl.c)
     #result = replaceTypeVarsT(cl, lastSon(t))
 
@@ -467,46 +654,76 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
     assert t.n.typ != t
     var n = prepareNode(cl, t.n)
     if n.kind != nkEmpty:
-      n = cl.c.semConstExpr(cl.c, n)
+      if tfNonConstExpr in t.flags:
+        n = cl.c.semExprWithType(cl.c, n, flags = {efInTypeof})
+      else:
+        n = cl.c.semConstExpr(cl.c, n)
     if n.typ.kind == tyTypeDesc:
       # XXX: sometimes, chained typedescs enter here.
       # It may be worth investigating why this is happening,
       # because it may cause other bugs elsewhere.
       result = n.typ.skipTypes({tyTypeDesc})
       # result = n.typ.base
+    elif tfNonConstExpr in t.flags:
+      result = n.typ
     else:
-      if n.typ.kind != tyStatic:
+      if n.typ.kind != tyStatic and n.kind != nkType:
         # XXX: In the future, semConstExpr should
         # return tyStatic values to let anyone make
         # use of this knowledge. The patching here
         # won't be necessary then.
-        result = newTypeS(tyStatic, cl.c)
-        result.sons = @[n.typ]
+        result = newTypeS(tyStatic, cl.c, son = n.typ)
         result.n = n
       else:
         result = n.typ
 
   of tyInt, tyFloat:
-    result = skipIntLit(t)
+    result = skipIntLit(t, cl.c.idgen)
 
   of tyTypeDesc:
     let lookup = cl.typeMap.lookup(t)
     if lookup != nil:
       result = lookup
-      if tfUnresolved in t.flags or cl.skipTypedesc: result = result.base
-    elif t.sons[0].kind != tyNone:
-      result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.sons[0]))
-
-  of tyUserTypeClass, tyStatic:
+      if result.kind != tyTypeDesc:
+        result = makeTypeDesc(cl.c, result)
+      elif tfUnresolved in t.flags or cl.skipTypedesc:
+        result = result.base
+    elif t.elementType.kind != tyNone:
+      result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.elementType))
+
+  of tyUserTypeClass:
     result = t
+  
+  of tyStatic:
+    if cl.c.matchedConcept != nil:
+      # allow concepts to not instantiate statics for now
+      # they can't always infer them
+      return
+    if not containsGenericType(t) and (t.n == nil or t.n.kind in nkLiterals):
+      # no need to instantiate
+      return
+    bailout()
+    result = instCopyType(cl, t)
+    cl.localCache[t.itemId] = result
+    for i in FirstGenericParamAt..<result.kidsLen:
+      var r = result[i]
+      if r != nil:
+        r = replaceTypeVarsT(cl, r)
+        result[i] = r
+        propagateToOwner(result, r)
+    result.n = replaceTypeVarsN(cl, result.n)
+    if not cl.allowMetaTypes and result.n != nil and
+        result.base.kind != tyNone:
+      result.n = cl.c.semConstExpr(cl.c, result.n)
+      result.n.typ = result.base
 
   of tyGenericInst, tyUserTypeClassInst:
     bailout()
     result = instCopyType(cl, t)
-    idTablePut(cl.localCache, t, result)
-    for i in 1 ..< result.sonsLen:
-      result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
-    propagateToOwner(result, result.lastSon)
+    cl.localCache[t.itemId] = result
+    for i in FirstGenericParamAt..<result.kidsLen:
+      result[i] = replaceTypeVarsT(cl, result[i])
+    propagateToOwner(result, result.last)
 
   else:
     if containsGenericType(t):
@@ -515,91 +732,135 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
       result = instCopyType(cl, t)
       result.size = -1 # needs to be recomputed
       #if not cl.allowMetaTypes:
-      idTablePut(cl.localCache, t, result)
-
-      for i in countup(0, sonsLen(result) - 1):
-        if result.sons[i] != nil:
-          var r = replaceTypeVarsT(cl, result.sons[i])
+      cl.localCache[t.itemId] = result
+
+      for i, resulti in result.ikids:
+        if resulti != nil:
+          if resulti.kind == tyGenericBody and not cl.allowMetaTypes:
+            localError(cl.c.config, if t.sym != nil: t.sym.info else: cl.info,
+              "cannot instantiate '" &
+              typeToString(result[i], preferDesc) &
+              "' inside of type definition: '" &
+              t.owner.name.s & "'; Maybe generic arguments are missing?")
+          var r = replaceTypeVarsT(cl, resulti)
           if result.kind == tyObject:
             # carefully coded to not skip the precious tyGenericInst:
-            let r2 = r.skipTypes({tyAlias, tySink})
+            let r2 = r.skipTypes({tyAlias, tySink, tyOwned})
             if r2.kind in {tyPtr, tyRef}:
               r = skipTypes(r2, {tyPtr, tyRef})
-          result.sons[i] = r
+          result[i] = r
           if result.kind != tyArray or i != 0:
             propagateToOwner(result, r)
       # bug #4677: Do not instantiate effect lists
       result.n = replaceTypeVarsN(cl, result.n, ord(result.kind==tyProc))
       case result.kind
       of tyArray:
-        let idx = result.sons[0]
+        let idx = result.indexType
         internalAssert cl.c.config, idx.kind != tyStatic
 
       of tyObject, tyTuple:
         propagateFieldFlags(result, result.n)
+        if result.kind == tyObject and cl.c.computeRequiresInit(cl.c, result):
+          result.flags.incl tfRequiresInit
 
       of tyProc:
         eraseVoidParams(result)
-        skipIntLiteralParams(result)
+        skipIntLiteralParams(result, cl.c.idgen)
+
+      of tyRange:
+        result.setIndexType result.indexType.skipTypes({tyStatic, tyDistinct})
 
       else: discard
+    else:
+      # If this type doesn't refer to a generic type we may still want to run it
+      # trough replaceObjBranches in order to resolve any pending nkRecWhen nodes
+      result = t
+
+      # Slow path, we have some work to do
+      if t.kind == tyRef and t.hasElementType and t.elementType.kind == tyObject and t.elementType.n != nil:
+        discard replaceObjBranches(cl, t.elementType.n)
 
-proc instAllTypeBoundOp*(c: PContext, info: TLineInfo) =
-  if destructor notin c.features: return
-  var i = 0
-  while i < c.typesWithOps.len:
-    let (newty, oldty) = c.typesWithOps[i]
-    typeBound(c, newty, oldty, destructor, info)
-    typeBound(c, newty, oldty, sink, info)
-    typeBound(c, newty, oldty, assignment, info)
-    inc i
-  setLen(c.typesWithOps, 0)
-
-proc initTypeVars*(p: PContext, typeMap: ptr LayeredIdTable, info: TLineInfo;
+      elif result.n != nil and t.kind == tyObject:
+        # Invalidate the type size as we may alter its structure
+        result.size = -1
+        result.n = replaceObjBranches(cl, result.n)
+
+proc initTypeVars*(p: PContext, typeMap: LayeredIdTable, info: TLineInfo;
                    owner: PSym): TReplTypeVars =
-  initIdTable(result.symMap)
-  initIdTable(result.localCache)
-  result.typeMap = typeMap
-  result.info = info
-  result.c = p
-  result.owner = owner
-
-proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode;
-                         owner: PSym, allowMetaTypes = false): PNode =
+  result = TReplTypeVars(symMap: initSymMapping(),
+            localCache: initTypeMapping(), typeMap: typeMap,
+            info: info, c: p, owner: owner)
+
+proc replaceTypesInBody*(p: PContext, pt: TypeMapping, n: PNode;
+                         owner: PSym, allowMetaTypes = false,
+                         fromStaticExpr = false, expectedType: PType = nil): PNode =
   var typeMap = initLayeredTypeMap(pt)
-  var cl = initTypeVars(p, addr(typeMap), n.info, owner)
+  var cl = initTypeVars(p, typeMap, n.info, owner)
   cl.allowMetaTypes = allowMetaTypes
   pushInfoContext(p.config, n.info)
-  result = replaceTypeVarsN(cl, n)
+  result = replaceTypeVarsN(cl, n, expectedType = expectedType)
   popInfoContext(p.config)
 
-proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode;
-                            original, new: PSym): PNode =
+proc prepareTypesInBody*(p: PContext, pt: TypeMapping, n: PNode;
+                         owner: PSym = nil): PNode =
   var typeMap = initLayeredTypeMap(pt)
-  var cl = initTypeVars(p, addr(typeMap), n.info, original)
-  idTablePut(cl.symMap, original, new)
+  var cl = initTypeVars(p, typeMap, n.info, owner)
   pushInfoContext(p.config, n.info)
-  result = replaceTypeVarsN(cl, n)
+  result = prepareNode(cl, n)
   popInfoContext(p.config)
 
-proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
+when false:
+  # deadcode
+  proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode;
+                              original, new: PSym): PNode =
+    var typeMap = initLayeredTypeMap(pt)
+    var cl = initTypeVars(p, typeMap, n.info, original)
+    idTablePut(cl.symMap, original, new)
+    pushInfoContext(p.config, n.info)
+    result = replaceTypeVarsN(cl, n)
+    popInfoContext(p.config)
+
+proc recomputeFieldPositions*(t: PType; obj: PNode; currPosition: var int) =
+  if t != nil and t.baseClass != nil:
+    let b = skipTypes(t.baseClass, skipPtrs)
+    recomputeFieldPositions(b, b.n, currPosition)
+  case obj.kind
+  of nkRecList:
+    for i in 0..<obj.len: recomputeFieldPositions(nil, obj[i], currPosition)
+  of nkRecCase:
+    recomputeFieldPositions(nil, obj[0], currPosition)
+    for i in 1..<obj.len:
+      recomputeFieldPositions(nil, lastSon(obj[i]), currPosition)
+  of nkSym:
+    obj.sym.position = currPosition
+    inc currPosition
+  else: discard "cannot happen"
+
+proc generateTypeInstance*(p: PContext, pt: TypeMapping, info: TLineInfo,
                            t: PType): PType =
+  # Given `t` like Foo[T]
+  # pt: Table with type mappings: T -> int
+  # Desired result: Foo[int]
+  # proc (x: T = 0); T -> int ---->  proc (x: int = 0)
   var typeMap = initLayeredTypeMap(pt)
-  var cl = initTypeVars(p, addr(typeMap), info, nil)
+  var cl = initTypeVars(p, typeMap, info, nil)
   pushInfoContext(p.config, info)
   result = replaceTypeVarsT(cl, t)
   popInfoContext(p.config)
+  let objType = result.skipTypes(abstractInst)
+  if objType.kind == tyObject:
+    var position = 0
+    recomputeFieldPositions(objType, objType.n, position)
 
-proc prepareMetatypeForSigmatch*(p: PContext, pt: TIdTable, info: TLineInfo,
+proc prepareMetatypeForSigmatch*(p: PContext, pt: TypeMapping, info: TLineInfo,
                                  t: PType): PType =
   var typeMap = initLayeredTypeMap(pt)
-  var cl = initTypeVars(p, addr(typeMap), info, nil)
+  var cl = initTypeVars(p, typeMap, info, nil)
   cl.allowMetaTypes = true
   pushInfoContext(p.config, info)
   result = replaceTypeVarsT(cl, t)
   popInfoContext(p.config)
 
-template generateTypeInstance*(p: PContext, pt: TIdTable, arg: PNode,
+template generateTypeInstance*(p: PContext, pt: TypeMapping, arg: PNode,
                                t: PType): untyped =
   generateTypeInstance(p, pt, arg.info, t)
-
diff --git a/compiler/service.nim b/compiler/service.nim
deleted file mode 100644
index 0e82c03f8..000000000
--- a/compiler/service.nim
+++ /dev/null
@@ -1,68 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Implements the "compiler as a service" feature.
-
-import
-  times, commands, options, msgs, nimconf,
-  extccomp, strutils, os, platform, parseopt, idents, lineinfos
-
-when useCaas:
-  import net
-
-# We cache modules and the dependency graph. However, we don't check for
-# file changes but expect the client to tell us about them, otherwise the
-# repeated hash calculations may turn out to be too slow.
-
-var
-  curCaasCmd* = ""
-  lastCaasCmd* = ""
-    # in caas mode, the list of defines and options will be given at start-up?
-    # it's enough to check that the previous compilation command is the same?
-
-proc serve*(cache: IdentCache; action: proc (cache: IdentCache){.nimcall.}; config: ConfigRef) =
-  template execute(cmd) =
-    curCaasCmd = cmd
-    processCmdLine(passCmd2, cmd, config)
-    action(cache)
-    config.errorCounter = 0
-
-  let typ = getConfigVar(config, "server.type")
-  case typ
-  of "stdin":
-    while true:
-      var line = stdin.readLine.string
-      if line == "quit": quit()
-      execute line
-      echo ""
-      flushFile(stdout)
-
-  of "tcp", "":
-    when useCaas:
-      var server = newSocket()
-      let p = getConfigVar(config, "server.port")
-      let port = if p.len > 0: parseInt(p).Port else: 6000.Port
-      server.bindAddr(port, getConfigVar(config, "server.address"))
-      var inp = "".TaintedString
-      server.listen()
-      var stdoutSocket = newSocket()
-      config.writelnHook = proc (line: string) =
-        stdoutSocket.send(line & "\c\L")
-
-      while true:
-        accept(server, stdoutSocket)
-        stdoutSocket.readLine(inp)
-        execute inp.string
-        stdoutSocket.send("\c\L")
-        stdoutSocket.close()
-    else:
-      msgQuit "server.type not supported; compiler built without caas support"
-  else:
-    echo "Invalid server.type:", typ
-    msgQuit 1
diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim
index 0bf2b8459..d8dfe1828 100644
--- a/compiler/sighashes.nim
+++ b/compiler/sighashes.nim
@@ -9,88 +9,43 @@
 
 ## Computes hash values for routine (proc, method etc) signatures.
 
-import ast, md5, tables, ropes
-from hashes import Hash
-from astalgo import debug
+import ast, ropes, modulegraphs, options, msgs, pathutils
+from std/hashes import Hash
+import std/tables
 import types
-from strutils import startsWith, contains
-
-when false:
-  type
-    SigHash* = uint32  ## a hash good enough for a filename or a proc signature
-
-  proc sdbmHash(hash: SigHash, c: char): SigHash {.inline.} =
-    return SigHash(c) + (hash shl 6) + (hash shl 16) - hash
-
-  template `&=`*(x: var SigHash, c: char) = x = sdbmHash(x, c)
-  template `&=`*(x: var SigHash, s: string) =
-    for c in s: x = sdbmHash(x, c)
-
-else:
-  type
-    SigHash* = distinct Md5Digest
-
-  const
-    cb64 = [
-      "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",
-      "0", "1", "2", "3", "4", "5", "6", "7", "8", "9a",
-      "9b", "9c"]
-
-  proc toBase64a(s: cstring, len: int): string =
-    ## encodes `s` into base64 representation.
-    result = newStringOfCap(((len + 2) div 3) * 4)
-    result.add '_'
-    var i = 0
-    while i < len - 2:
-      let a = ord(s[i])
-      let b = ord(s[i+1])
-      let c = ord(s[i+2])
-      result.add cb64[a shr 2]
-      result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
-      result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
-      result.add cb64[c and 0x3F]
-      inc(i, 3)
-    if i < len-1:
-      let a = ord(s[i])
-      let b = ord(s[i+1])
-      result.add cb64[a shr 2]
-      result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
-      result.add cb64[((b and 0x0F) shl 2)]
-    elif i < len:
-      let a = ord(s[i])
-      result.add cb64[a shr 2]
-      result.add cb64[(a and 3) shl 4]
-
-  proc `$`*(u: SigHash): string =
-    toBase64a(cast[cstring](unsafeAddr u), sizeof(u))
-  proc `&=`(c: var MD5Context, s: string) = md5Update(c, s, s.len)
-  proc `&=`(c: var MD5Context, ch: char) = md5Update(c, unsafeAddr ch, 1)
-  proc `&=`(c: var MD5Context, i: BiggestInt) =
-    md5Update(c, cast[cstring](unsafeAddr i), sizeof(i))
-
-  template lowlevel(v) =
-    md5Update(c, cast[cstring](unsafeAddr(v)), sizeof(v))
-
-  proc `==`*(a, b: SigHash): bool =
-    # {.borrow.}
-    result = equalMem(unsafeAddr a, unsafeAddr b, sizeof(a))
-
-  proc hash*(u: SigHash): Hash =
-    result = 0
-    for x in 0..3:
-      result = (result shl 8) or u.MD5Digest[x].int
+import ../dist/checksums/src/checksums/md5
+
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
+proc `&=`(c: var MD5Context, s: string) = md5Update(c, s, s.len)
+proc `&=`(c: var MD5Context, ch: char) =
+  # XXX suspicious code here; relies on ch being zero terminated?
+  md5Update(c, cast[cstring](unsafeAddr ch), 1)
+
+proc `&=`(c: var MD5Context, i: BiggestInt) =
+  md5Update(c, cast[cstring](unsafeAddr i), sizeof(i))
+proc `&=`(c: var MD5Context, f: BiggestFloat) =
+  md5Update(c, cast[cstring](unsafeAddr f), sizeof(f))
+proc `&=`(c: var MD5Context, s: SigHash) =
+  md5Update(c, cast[cstring](unsafeAddr s), sizeof(s))
+template lowlevel(v) =
+  md5Update(c, cast[cstring](unsafeAddr(v)), sizeof(v))
+
+
 type
   ConsiderFlag* = enum
     CoProc
     CoType
     CoOwnerSig
     CoIgnoreRange
+    CoConsiderOwned
+    CoDistinct
+    CoHashTypeInsideNode
 
-proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag])
-
+proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: ConfigRef)
 proc hashSym(c: var MD5Context, s: PSym) =
   if sfAnon in s.flags or s.kind == skGenericParam:
     c &= ":anon"
@@ -100,21 +55,26 @@ proc hashSym(c: var MD5Context, s: PSym) =
       c &= it.name.s
       c &= "."
       it = it.owner
+    c &= "#"
+    c &= s.disamb
 
-proc hashTypeSym(c: var MD5Context, s: PSym) =
+proc hashTypeSym(c: var MD5Context, s: PSym; conf: ConfigRef) =
   if sfAnon in s.flags or s.kind == skGenericParam:
     c &= ":anon"
   else:
     var it = s
+    c &= customPath(conf.toFullPath(s.info))
     while it != nil:
       if sfFromGeneric in it.flags and it.kind in routineKinds and
           it.typ != nil:
-        hashType c, it.typ, {CoProc}
+        hashType c, it.typ, {CoProc}, conf
       c &= it.name.s
       c &= "."
       it = it.owner
+    c &= "#"
+    c &= s.disamb
 
-proc hashTree(c: var MD5Context, n: PNode) =
+proc hashTree(c: var MD5Context, n: PNode; flags: set[ConsiderFlag]; conf: ConfigRef) =
   if n == nil:
     c &= "\255"
     return
@@ -128,6 +88,8 @@ proc hashTree(c: var MD5Context, n: PNode) =
     c &= n.ident.s
   of nkSym:
     hashSym(c, n.sym)
+    if CoHashTypeInsideNode in flags and n.sym.typ != nil:
+      hashType(c, n.sym.typ, flags, conf)
   of nkCharLit..nkUInt64Lit:
     let v = n.intVal
     lowlevel v
@@ -137,20 +99,24 @@ proc hashTree(c: var MD5Context, n: PNode) =
   of nkStrLit..nkTripleStrLit:
     c &= n.strVal
   else:
-    for i in 0..<n.len: hashTree(c, n.sons[i])
+    for i in 0..<n.len: hashTree(c, n[i], flags, conf)
 
-proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
+proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: ConfigRef) =
   if t == nil:
     c &= "\254"
     return
 
   case t.kind
   of tyGenericInvocation:
-    for i in countup(0, sonsLen(t) - 1):
-      c.hashType t.sons[i], flags
+    for a in t.kids:
+      c.hashType a, flags, conf
   of tyDistinct:
-    if CoType in flags:
-      c.hashType t.lastSon, flags
+    if CoDistinct in flags:
+      if t.sym != nil: c.hashSym(t.sym)
+      if t.sym == nil or tfFromGeneric in t.flags:
+        c.hashType t.elementType, flags, conf
+    elif CoType in flags or t.sym == nil:
+      c.hashType t.elementType, flags, conf
     else:
       c.hashSym(t.sym)
   of tyGenericInst:
@@ -159,12 +125,17 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
       # We cannot trust the `lastSon` to hold a properly populated and unique
       # value for each instantiation, so we hash the generic parameters here:
       let normalizedType = t.skipGenericAlias
-      for i in 0 .. normalizedType.len - 2:
-        c.hashType t.sons[i], flags
+      c.hashType normalizedType.genericHead, flags, conf
+      for _, a in normalizedType.genericInstParams:
+        c.hashType a, flags, conf
     else:
-      c.hashType t.lastSon, flags
-  of tyAlias, tySink, tyUserTypeClasses:
-    c.hashType t.lastSon, flags
+      c.hashType t.skipModifier, flags, conf
+  of tyAlias, tySink, tyUserTypeClasses, tyInferred:
+    c.hashType t.skipModifier, flags, conf
+  of tyOwned:
+    if CoConsiderOwned in flags:
+      c &= char(t.kind)
+    c.hashType t.skipModifier, flags, conf
   of tyBool, tyChar, tyInt..tyUInt64:
     # no canonicalization for integral types, so that e.g. ``pid_t`` is
     # produced instead of ``NI``:
@@ -173,67 +144,86 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
       c.hashSym(t.sym)
   of tyObject, tyEnum:
     if t.typeInst != nil:
-      assert t.typeInst.kind == tyGenericInst
-      for i in countup(0, sonsLen(t.typeInst) - 2):
-        c.hashType t.typeInst.sons[i], flags
+      # prevent against infinite recursions here, see bug #8883:
+      let inst = t.typeInst
+      t.typeInst = nil
+      assert inst.kind == tyGenericInst
+      c.hashType inst.genericHead, flags, conf
+      for _, a in inst.genericInstParams:
+        c.hashType a, flags, conf
+      t.typeInst = inst
       return
     c &= char(t.kind)
     # Every cyclic type in Nim need to be constructed via some 't.sym', so this
     # is actually safe without an infinite recursion check:
     if t.sym != nil:
-      #if "Future:" in t.sym.name.s and t.typeInst == nil:
-      #  writeStackTrace()
-      #  echo "yes ", t.sym.name.s
-      #  #quit 1
-      if CoOwnerSig in flags:
-        c.hashTypeSym(t.sym)
+      if {sfCompilerProc} * t.sym.flags != {}:
+        doAssert t.sym.loc.snippet != ""
+        # The user has set a specific name for this type
+        c &= t.sym.loc.snippet
+      elif CoOwnerSig in flags:
+        c.hashTypeSym(t.sym, conf)
       else:
         c.hashSym(t.sym)
-      if {sfAnon, sfGenSym} * t.sym.flags != {}:
-        # generated object names can be identical, so we need to
-        # disambiguate furthermore by hashing the field types and names:
-        # mild hack to prevent endless recursions (makes nimforum compile again):
-        let oldFlags = t.sym.flags
-        t.sym.flags = t.sym.flags - {sfAnon, sfGenSym}
-        let n = t.n
-        for i in 0 ..< n.len:
-          assert n[i].kind == nkSym
-          let s = n[i].sym
-          c.hashSym s
-          c.hashType s.typ, flags
-        t.sym.flags = oldFlags
+
+      var symWithFlags: PSym = nil
+      template hasFlag(sym): bool =
+        let ret = {sfAnon, sfGenSym} * sym.flags != {}
+        if ret: symWithFlags = sym
+        ret
+      if hasFlag(t.sym) or (t.kind == tyObject and t.owner.kind == skType and t.owner.typ.kind == tyRef and hasFlag(t.owner)):
+        # for `PFoo:ObjectType`, arising from `type PFoo = ref object`
+        # Generated object names can be identical, so we need to
+        # disambiguate furthermore by hashing the field types and names.
+        if t.n.len > 0:
+          let oldFlags = symWithFlags.flags
+          # Hack to prevent endless recursion
+          # xxx instead, use a hash table to indicate we've already visited a type, which
+          # would also be more efficient.
+          symWithFlags.flags.excl {sfAnon, sfGenSym}
+          hashTree(c, t.n, flags + {CoHashTypeInsideNode}, conf)
+          symWithFlags.flags = oldFlags
+        else:
+          # The object has no fields: we _must_ add something here in order to
+          # make the hash different from the one we produce by hashing only the
+          # type name.
+          c &= ".empty"
     else:
       c &= t.id
-    if t.len > 0 and t.sons[0] != nil:
-      hashType c, t.sons[0], flags
-  of tyRef, tyPtr, tyGenericBody, tyVar:
+    if t.hasElementType and t.baseClass != nil:
+      hashType c, t.baseClass, flags, conf
+  of tyRef, tyPtr, tyVar:
     c &= char(t.kind)
-    c.hashType t.lastSon, flags
+    if t.hasElementType:
+      c.hashType t.elementType, flags, conf
     if tfVarIsPtr in t.flags: c &= ".varisptr"
+  of tyGenericBody:
+    c &= char(t.kind)
+    if t.hasElementType:
+      c.hashType t.typeBodyImpl, flags, conf
   of tyFromExpr:
     c &= char(t.kind)
-    c.hashTree(t.n)
+    c.hashTree(t.n, {}, conf)
   of tyTuple:
     c &= char(t.kind)
     if t.n != nil and CoType notin flags:
-      assert(sonsLen(t.n) == sonsLen(t))
-      for i in countup(0, sonsLen(t.n) - 1):
-        assert(t.n.sons[i].kind == nkSym)
-        c &= t.n.sons[i].sym.name.s
+      for i in 0..<t.n.len:
+        assert(t.n[i].kind == nkSym)
+        c &= t.n[i].sym.name.s
         c &= ':'
-        c.hashType(t.sons[i], flags+{CoIgnoreRange})
+        c.hashType(t.n[i].sym.typ, flags+{CoIgnoreRange}, conf)
         c &= ','
     else:
-      for i in countup(0, sonsLen(t) - 1): c.hashType t.sons[i], flags+{CoIgnoreRange}
+      for a in t.kids: c.hashType a, flags+{CoIgnoreRange}, conf
   of tyRange:
     if CoIgnoreRange notin flags:
       c &= char(t.kind)
-      c.hashTree(t.n)
-    c.hashType(t.sons[0], flags)
+      c.hashTree(t.n, {}, conf)
+    c.hashType(t.elementType, flags, conf)
   of tyStatic:
     c &= char(t.kind)
-    c.hashTree(t.n)
-    c.hashType(t.sons[0], flags)
+    c.hashTree(t.n, {}, conf)
+    c.hashType(t.skipModifier, flags, conf)
   of tyProc:
     c &= char(t.kind)
     c &= (if tfIterator in t.flags: "iterator " else: "proc ")
@@ -243,22 +233,27 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
         let param = params[i].sym
         c &= param.name.s
         c &= ':'
-        c.hashType(param.typ, flags)
+        c.hashType(param.typ, flags, conf)
         c &= ','
-      c.hashType(t.sons[0], flags)
+      c.hashType(t.returnType, flags, conf)
     else:
-      for i in 0..<t.len: c.hashType(t.sons[i], flags)
+      for a in t.signature: c.hashType(a, flags, conf)
     c &= char(t.callConv)
-    if CoType notin flags:
-      if tfNoSideEffect in t.flags: c &= ".noSideEffect"
-      if tfThread in t.flags: c &= ".thread"
+    # purity of functions doesn't have to affect the mangling (which is in fact
+    # problematic for HCR - someone could have cached a pointer to another
+    # function which changes its purity and suddenly the cached pointer is danglign)
+    # IMHO anything that doesn't affect the overload resolution shouldn't be part of the mangling...
+    # if CoType notin flags:
+    #   if tfNoSideEffect in t.flags: c &= ".noSideEffect"
+    #   if tfThread in t.flags: c &= ".thread"
     if tfVarargs in t.flags: c &= ".varargs"
   of tyArray:
     c &= char(t.kind)
-    for i in 0..<t.len: c.hashType(t.sons[i], flags-{CoIgnoreRange})
+    c.hashType(t.indexType, flags-{CoIgnoreRange}, conf)
+    c.hashType(t.elementType, flags-{CoIgnoreRange}, conf)
   else:
     c &= char(t.kind)
-    for i in 0..<t.len: c.hashType(t.sons[i], flags)
+    for a in t.kids: c.hashType(a, flags, conf)
   if tfNotNil in t.flags and CoType notin flags: c &= "not nil"
 
 when defined(debugSigHashes):
@@ -275,19 +270,21 @@ when defined(debugSigHashes):
   #  select hash, type from sighashes where hash in
   # (select hash from sighashes group by hash having count(*) > 1) order by hash;
 
-proc hashType*(t: PType; flags: set[ConsiderFlag] = {CoType}): SigHash =
-  var c: MD5Context
+proc hashType*(t: PType; conf: ConfigRef; flags: set[ConsiderFlag] = {CoType}): SigHash =
+  result = default(SigHash)
+  var c: MD5Context = default(MD5Context)
   md5Init c
-  hashType c, t, flags+{CoOwnerSig}
-  md5Final c, result.Md5Digest
+  hashType c, t, flags+{CoOwnerSig}, conf
+  md5Final c, result.MD5Digest
   when defined(debugSigHashes):
     db.exec(sql"INSERT OR IGNORE INTO sighashes(type, hash) VALUES (?, ?)",
             typeToString(t), $result)
 
-proc hashProc*(s: PSym): SigHash =
-  var c: MD5Context
+proc hashProc(s: PSym; conf: ConfigRef): SigHash =
+  result = default(SigHash)
+  var c: MD5Context = default(MD5Context)
   md5Init c
-  hashType c, s.typ, {CoProc}
+  hashType c, s.typ, {CoProc}, conf
 
   var m = s
   while m.kind != skModule: m = m.owner
@@ -302,10 +299,11 @@ proc hashProc*(s: PSym): SigHash =
   # hash, we also hash the line information. This is pretty bad, but the best
   # solution for now:
   #c &= s.info.line
-  md5Final c, result.Md5Digest
+  md5Final c, result.MD5Digest
 
 proc hashNonProc*(s: PSym): SigHash =
-  var c: MD5Context
+  result = default(SigHash)
+  var c: MD5Context = default(MD5Context)
   md5Init c
   hashSym(c, s)
   var it = s
@@ -318,10 +316,11 @@ proc hashNonProc*(s: PSym): SigHash =
   # might cause:
   if s.kind == skParam:
     c &= s.position
-  md5Final c, result.Md5Digest
+  md5Final c, result.MD5Digest
 
 proc hashOwner*(s: PSym): SigHash =
-  var c: MD5Context
+  result = default(SigHash)
+  var c: MD5Context = default(MD5Context)
   md5Init c
   var m = s
   while m.kind != skModule: m = m.owner
@@ -331,15 +330,90 @@ proc hashOwner*(s: PSym): SigHash =
   c &= "."
   c &= m.name.s
 
-  md5Final c, result.Md5Digest
+  md5Final c, result.MD5Digest
+
+proc sigHash*(s: PSym; conf: ConfigRef): SigHash =
+  if s.kind in routineKinds and s.typ != nil:
+    result = hashProc(s, conf)
+  else:
+    result = hashNonProc(s)
+
+proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash
+
+proc hashBodyTree(graph: ModuleGraph, c: var MD5Context, n: PNode)
+
+proc hashVarSymBody(graph: ModuleGraph, c: var MD5Context, s: PSym) =
+  assert: s.kind in {skParam, skResult, skVar, skLet, skConst, skForVar}
+  if sfGlobal notin s.flags:
+    c &= char(s.kind)
+    c &= s.name.s
+  else:
+    c &= hashNonProc(s)
+    # this one works for let and const but not for var. True variables can change value
+    # later on. it is user resposibility to hash his global state if required
+    if s.ast != nil and s.ast.kind in {nkIdentDefs, nkConstDef}:
+      hashBodyTree(graph, c, s.ast[^1])
+    else:
+      hashBodyTree(graph, c, s.ast)
+
+proc hashBodyTree(graph: ModuleGraph, c: var MD5Context, n: PNode) =
+  # hash Nim tree recursing into simply
+  if n == nil:
+    c &= "nil"
+    return
+  c &= char(n.kind)
+  case n.kind
+  of nkEmpty, nkNilLit, nkType: discard
+  of nkIdent:
+    c &= n.ident.s
+  of nkSym:
+    if n.sym.kind in skProcKinds:
+      c &= symBodyDigest(graph, n.sym)
+    elif n.sym.kind in {skParam, skResult, skVar, skLet, skConst, skForVar}:
+      hashVarSymBody(graph, c, n.sym)
+    else:
+      c &= hashNonProc(n.sym)
+  of nkProcDef, nkFuncDef, nkTemplateDef, nkMacroDef:
+    discard # we track usage of proc symbols not their definition
+  of nkCharLit..nkUInt64Lit:
+    c &= n.intVal
+  of nkFloatLit..nkFloat64Lit:
+    c &= n.floatVal
+  of nkStrLit..nkTripleStrLit:
+    c &= n.strVal
+  else:
+    for i in 0..<n.len:
+      hashBodyTree(graph, c, n[i])
+
+proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash =
+  ## compute unique digest of the proc/func/method symbols
+  ## recursing into invoked symbols as well
+  assert(sym.kind in skProcKinds, $sym.kind)
+  result = default(SigHash)
+  graph.symBodyHashes.withValue(sym.id, value):
+    return value[]
+
+  var c: MD5Context = default(MD5Context)
+  md5Init(c)
+  c.hashType(sym.typ, {CoProc}, graph.config)
+  c &= char(sym.kind)
+  c.md5Final(result.MD5Digest)
+  graph.symBodyHashes[sym.id] = result # protect from recursion in the body
+
+  if sym.ast != nil:
+    md5Init(c)
+    c.md5Update(cast[cstring](result.addr), sizeof(result))
+    hashBodyTree(graph, c, getBody(graph, sym))
+    c.md5Final(result.MD5Digest)
+    graph.symBodyHashes[sym.id] = result
 
 proc idOrSig*(s: PSym, currentModule: string,
-              sigCollisions: var CountTable[SigHash]): Rope =
+              sigCollisions: var CountTable[SigHash]; conf: ConfigRef): Rope =
   if s.kind in routineKinds and s.typ != nil:
     # signatures for exported routines are reliable enough to
-    # produce a unique name and this means produced C++ is more stable wrt
+    # produce a unique name and this means produced C++ is more stable regarding
     # Nim changes:
-    let sig = hashProc(s)
+    let sig = hashProc(s, conf)
     result = rope($sig)
     #let m = if s.typ.callConv != ccInline: findPendingModule(m, s) else: m
     let counter = sigCollisions.getOrDefault(sig)
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 9a3c75261..6ea2c7bb5 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -11,20 +11,33 @@
 ## the call to overloaded procs, generic procs and operators.
 
 import
-  intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
-  magicsys, condsyms, idents, lexer, options, parampatterns, strutils, trees,
-  linter, lineinfos
+  ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
+  magicsys, idents, lexer, options, parampatterns, trees,
+  linter, lineinfos, lowerings, modulegraphs, concepts
 
-when defined(booting) or defined(nimsuggest):
-  import docgen
+import std/[intsets, strutils, tables]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 type
+  MismatchKind* = enum
+    kUnknown, kAlreadyGiven, kUnknownNamedParam, kTypeMismatch, kVarNeeded,
+    kMissingParam, kExtraArg, kPositionalAlreadyGiven,
+    kGenericParamTypeMismatch, kMissingGenericParam, kExtraGenericParam
+
+  MismatchInfo* = object
+    kind*: MismatchKind # reason for mismatch
+    arg*: int           # position of provided arguments that mismatches
+    formal*: PSym       # parameter that mismatches against provided argument
+                        # its position can differ from `arg` because of varargs
+
   TCandidateState* = enum
     csEmpty, csMatch, csNoMatch
 
   CandidateError* = object
     sym*: PSym
-    unmatchedVarParam*, firstMismatch*: int
+    firstMismatch*: MismatchInfo
     diagnostics*: seq[string]
     enabled*: bool
 
@@ -43,12 +56,12 @@ type
     calleeScope*: int        # scope depth:
                              # is this a top-level symbol or a nested proc?
     call*: PNode             # modified call
-    bindings*: TIdTable      # maps types to types
+    bindings*: TypeMapping   # maps types to types
     magic*: TMagic           # magic of operation
     baseTypeMatch: bool      # needed for conversions from T to openarray[T]
                              # for example
-    fauxMatch*: TTypeKind    # the match was successful only due to the use
-                             # of error or wildcard (unknown) types.
+    matchedErrorType*: bool  # match is considered successful after matching
+                             # error type to avoid cascading errors
                              # this is used to prevent instantiations.
     genericConverter*: bool  # true if a generic converter needs to
                              # be instantiated
@@ -56,7 +69,6 @@ type
                              # a distrinct type
     typedescMatched*: bool
     isNoCall*: bool          # misused for generic type instantiations C[T]
-    mutabilityProblem*: uint8 # tyVar mismatch
     inferredTypes: seq[PType] # inferred types during the current signature
                               # matching. they will be reset if the matching
                               # is not successful. may replace the bindings
@@ -69,171 +81,290 @@ type
                               # or when the explain pragma is used. may be
                               # triggered with an idetools command in the
                               # future.
-    inheritancePenalty: int   # to prefer closest father object type
-    firstMismatch*: int       # position of the first type mismatch for
-                              # better error messages
+                              # to prefer closest father object type
+    inheritancePenalty: int
+    firstMismatch*: MismatchInfo # mismatch info for better error messages
     diagnosticsEnabled*: bool
 
   TTypeRelFlag* = enum
     trDontBind
     trNoCovariance
+    trBindGenericParam  # bind tyGenericParam even with trDontBind
+    trIsOutParam
 
   TTypeRelFlags* = set[TTypeRelFlag]
 
-  TTypeRelation* = enum      # order is important!
-    isNone, isConvertible,
-    isIntConv,
-    isSubtype,
-    isSubrange,              # subrange of the wanted type; no type conversion
-                             # but apart from that counts as ``isSubtype``
-    isBothMetaConvertible    # generic proc parameter was matched against
-                             # generic type, e.g., map(mySeq, x=>x+1),
-                             # maybe recoverable by rerun if the parameter is
-                             # the proc's return value
-    isInferred,              # generic proc was matched against a concrete type
-    isInferredConvertible,   # same as above, but requiring proc CC conversion
-    isGeneric,
-    isFromIntLit,            # conversion *from* int literal; proven safe
-    isEqual
 
 const
   isNilConversion = isConvertible # maybe 'isIntConv' fits better?
+  maxInheritancePenalty = high(int) div 2
 
-proc markUsed*(conf: ConfigRef; info: TLineInfo, s: PSym; usageSym: var PSym)
-
-template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone
+proc markUsed*(c: PContext; info: TLineInfo, s: PSym; checkStyle = true)
+proc markOwnerModuleAsUsed*(c: PContext; s: PSym)
 
 proc initCandidateAux(ctx: PContext,
-                      c: var TCandidate, callee: PType) {.inline.} =
-  c.c = ctx
-  c.exactMatches = 0
-  c.subtypeMatches = 0
-  c.convMatches = 0
-  c.intConvMatches = 0
-  c.genericMatches = 0
-  c.state = csEmpty
-  c.callee = callee
-  c.call = nil
-  c.baseTypeMatch = false
-  c.genericConverter = false
-  c.inheritancePenalty = 0
-
-proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PType) =
-  initCandidateAux(ctx, c, callee)
-  c.calleeSym = nil
-  initIdTable(c.bindings)
+                      callee: PType): TCandidate {.inline.} =
+  result = TCandidate(c: ctx, exactMatches: 0, subtypeMatches: 0,
+                      convMatches: 0, intConvMatches: 0, genericMatches: 0,
+                      state: csEmpty, firstMismatch: MismatchInfo(),
+                      callee: callee, call: nil, baseTypeMatch: false,
+                      genericConverter: false, inheritancePenalty: -1
+  )
+
+proc initCandidate*(ctx: PContext, callee: PType): TCandidate =
+  result = initCandidateAux(ctx, callee)
+  result.calleeSym = nil
+  result.bindings = initTypeMapping()
 
 proc put(c: var TCandidate, key, val: PType) {.inline.} =
-  idTablePut(c.bindings, key, val.skipIntLit)
+  ## Given: proc foo[T](x: T); foo(4)
+  ## key: 'T'
+  ## val: 'int' (typeof(4))
+  when false:
+    let old = idTableGet(c.bindings, key)
+    if old != nil:
+      echo "Putting ", typeToString(key), " ", typeToString(val), " and old is ", typeToString(old)
+      if typeToString(old) == "float32":
+        writeStackTrace()
+    if c.c.module.name.s == "temp3":
+      echo "binding ", key, " -> ", val
+  idTablePut(c.bindings, key, val.skipIntLit(c.c.idgen))
+
+proc typeRel*(c: var TCandidate, f, aOrig: PType,
+              flags: TTypeRelFlags = {}): TTypeRelation
+
+proc matchGenericParam(m: var TCandidate, formal: PType, n: PNode) =
+  var arg = n.typ
+  if m.c.inGenericContext > 0:
+    # don't match yet-unresolved generic instantiations
+    while arg != nil and arg.kind == tyGenericParam:
+      arg = idTableGet(m.bindings, arg)
+    if arg == nil or arg.containsUnresolvedType:
+      m.state = csNoMatch
+      return
+  # fix up the type to get ready to match formal:
+  var formalBase = formal
+  while formalBase.kind == tyGenericParam and
+      formalBase.genericParamHasConstraints:
+    formalBase = formalBase.genericConstraint
+  if formalBase.kind == tyStatic and arg.kind != tyStatic:
+    # maybe call `paramTypesMatch` here, for now be conservative
+    if n.kind in nkSymChoices: n.flags.excl nfSem
+    let evaluated = m.c.semTryConstExpr(m.c, n, formalBase.skipTypes({tyStatic}))
+    if evaluated != nil:
+      arg = newTypeS(tyStatic, m.c, son = evaluated.typ)
+      arg.n = evaluated
+  elif formalBase.kind == tyTypeDesc:
+    if arg.kind != tyTypeDesc:
+      arg = makeTypeDesc(m.c, arg)
+  else:
+    arg = arg.skipTypes({tyTypeDesc})
+  let tm = typeRel(m, formal, arg)
+  if tm in {isNone, isConvertible}:
+    m.state = csNoMatch
+    m.firstMismatch.kind = kGenericParamTypeMismatch
+    return
 
-proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
+proc matchGenericParams*(m: var TCandidate, binding: PNode, callee: PSym) =
+  ## matches explicit generic instantiation `binding` against generic params of
+  ## proc symbol `callee`
+  ## state is set to `csMatch` if all generic params match, `csEmpty` if
+  ## implicit generic parameters are missing (matches but cannot instantiate),
+  ## `csNoMatch` if a constraint fails or param count doesn't match
+  let c = m.c
+  let typeParams = callee.ast[genericParamsPos]
+  let paramCount = typeParams.len
+  let bindingCount = binding.len-1
+  if bindingCount > paramCount:
+    m.state = csNoMatch
+    m.firstMismatch.kind = kExtraGenericParam
+    m.firstMismatch.arg = paramCount + 1
+    return
+  for i in 1..bindingCount:
+    matchGenericParam(m, typeParams[i-1].typ, binding[i])
+    if m.state == csNoMatch:
+      m.firstMismatch.arg = i
+      m.firstMismatch.formal = typeParams[i-1].sym
+      return
+  # not enough generic params given, check if remaining have defaults:
+  for i in bindingCount ..< paramCount:
+    let param = typeParams[i]
+    assert param.kind == nkSym
+    let paramSym = param.sym
+    if paramSym.ast != nil:
+      matchGenericParam(m, param.typ, paramSym.ast)
+      if m.state == csNoMatch:
+        m.firstMismatch.arg = i + 1
+        m.firstMismatch.formal = paramSym
+        return
+    elif tfImplicitTypeParam in paramSym.typ.flags:
+      # not a mismatch, but can't create sym
+      m.state = csEmpty
+      return
+    else:
+      m.state = csNoMatch
+      m.firstMismatch.kind = kMissingGenericParam
+      m.firstMismatch.arg = i + 1
+      m.firstMismatch.formal = paramSym
+      return
+  m.state = csMatch
+
+proc copyingEraseVoidParams(m: TCandidate, t: var PType) =
+  ## if `t` is a proc type with void parameters, copies it and erases them
+  assert t.kind == tyProc
+  let original = t
+  var copied = false
+  for i in 1 ..< original.len:
+    var f = original[i]
+    var isVoidParam = f.kind == tyVoid
+    if not isVoidParam:
+      let prev = idTableGet(m.bindings, f)
+      if prev != nil: f = prev
+      isVoidParam = f.kind == tyVoid
+    if isVoidParam:
+      if not copied:
+        # keep first i children
+        t = copyType(original, m.c.idgen, t.owner)
+        t.setSonsLen(i)
+        t.n = copyNode(original.n)
+        t.n.sons = original.n.sons
+        t.n.sons.setLen(i)
+        copied = true
+    elif copied:
+      t.add(f)
+      t.n.add(original.n[i])
+
+proc initCandidate*(ctx: PContext, callee: PSym,
                     binding: PNode, calleeScope = -1,
-                    diagnosticsEnabled = false) =
-  initCandidateAux(ctx, c, callee.typ)
-  c.calleeSym = callee
+                    diagnosticsEnabled = false): TCandidate =
+  result = initCandidateAux(ctx, callee.typ)
+  result.calleeSym = callee
   if callee.kind in skProcKinds and calleeScope == -1:
-    if callee.originatingModule == ctx.module:
-      c.calleeScope = 2
-      var owner = callee
-      while true:
-        owner = owner.skipGenericOwner
-        if owner.kind == skModule: break
-        inc c.calleeScope
-    else:
-      c.calleeScope = 1
+    result.calleeScope = cmpScopes(ctx, callee)
   else:
-    c.calleeScope = calleeScope
-  c.diagnostics = if diagnosticsEnabled: @[] else: nil
-  c.diagnosticsEnabled = diagnosticsEnabled
-  c.magic = c.calleeSym.magic
-  initIdTable(c.bindings)
+    result.calleeScope = calleeScope
+  result.diagnostics = @[] # if diagnosticsEnabled: @[] else: nil
+  result.diagnosticsEnabled = diagnosticsEnabled
+  result.magic = result.calleeSym.magic
+  result.bindings = initTypeMapping()
   if binding != nil and callee.kind in routineKinds:
-    var typeParams = callee.ast[genericParamsPos]
-    for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1):
-      var formalTypeParam = typeParams.sons[i-1].typ
-      var bound = binding[i].typ
-      if bound != nil:
-        if formalTypeParam.kind == tyTypeDesc:
-          if bound.kind != tyTypeDesc:
-            bound = makeTypeDesc(ctx, bound)
-        else:
-          bound = bound.skipTypes({tyTypeDesc})
-        put(c, formalTypeParam, bound)
+    matchGenericParams(result, binding, callee)
+    let genericMatch = result.state
+    if genericMatch != csNoMatch:
+      result.state = csEmpty
+      if genericMatch == csMatch: # csEmpty if not fully instantiated
+        # instantiate the type, emulates old compiler behavior
+        # wouldn't be needed if sigmatch could handle complex cases,
+        # examples are in texplicitgenerics
+        # might be buggy, see rest of generateInstance if problems occur
+        let typ = ctx.instantiateOnlyProcType(ctx, result.bindings, callee, binding.info)
+        result.callee = typ
+      else:
+        # createThread[void] requires this if the above branch is removed:
+        copyingEraseVoidParams(result, result.callee)
 
 proc newCandidate*(ctx: PContext, callee: PSym,
                    binding: PNode, calleeScope = -1): TCandidate =
-  initCandidate(ctx, result, callee, binding, calleeScope)
+  result = initCandidate(ctx, callee, binding, calleeScope)
 
 proc newCandidate*(ctx: PContext, callee: PType): TCandidate =
-  initCandidate(ctx, result, callee)
-
-proc copyCandidate(a: var TCandidate, b: TCandidate) =
-  a.c = b.c
-  a.exactMatches = b.exactMatches
-  a.subtypeMatches = b.subtypeMatches
-  a.convMatches = b.convMatches
-  a.intConvMatches = b.intConvMatches
-  a.genericMatches = b.genericMatches
-  a.state = b.state
-  a.callee = b.callee
-  a.calleeSym = b.calleeSym
-  a.call = copyTree(b.call)
-  a.baseTypeMatch = b.baseTypeMatch
-  copyIdTable(a.bindings, b.bindings)
+  result = initCandidate(ctx, callee)
+
+proc copyCandidate(dest: var TCandidate, src: TCandidate) =
+  dest.c = src.c
+  dest.exactMatches = src.exactMatches
+  dest.subtypeMatches = src.subtypeMatches
+  dest.convMatches = src.convMatches
+  dest.intConvMatches = src.intConvMatches
+  dest.genericMatches = src.genericMatches
+  dest.state = src.state
+  dest.callee = src.callee
+  dest.calleeSym = src.calleeSym
+  dest.call = copyTree(src.call)
+  dest.baseTypeMatch = src.baseTypeMatch
+  dest.bindings = src.bindings
+
+proc checkGeneric(a, b: TCandidate): int =
+  let c = a.c
+  let aa = a.callee
+  let bb = b.callee
+  var winner = 0
+  for aai, bbi in underspecifiedPairs(aa, bb, 1):
+    var ma = newCandidate(c, bbi)
+    let tra = typeRel(ma, bbi, aai, {trDontBind})
+    var mb = newCandidate(c, aai)
+    let trb = typeRel(mb, aai, bbi, {trDontBind})
+    if tra == isGeneric and trb in {isNone, isInferred, isInferredConvertible}:
+      if winner == -1: return 0
+      winner = 1
+    if trb == isGeneric and tra in {isNone, isInferred, isInferredConvertible}:
+      if winner == 1: return 0
+      winner = -1
+  result = winner
 
 proc sumGeneric(t: PType): int =
+  # count the "genericness" so that Foo[Foo[T]] has the value 3
+  # and Foo[T] has the value 2 so that we know Foo[Foo[T]] is more
+  # specific than Foo[T].
+  result = 0
   var t = t
-  var isvar = 1
   while true:
     case t.kind
-    of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct,
-        tyOpenArray, tyVarargs, tySet, tyRange, tySequence, tyGenericBody,
-        tyLent:
-      t = t.lastSon
+    of tyAlias, tySink, tyNot: t = t.skipModifier
+    of tyArray, tyRef, tyPtr, tyDistinct, tyUncheckedArray,
+        tyOpenArray, tyVarargs, tySet, tyRange, tySequence,
+        tyLent, tyOwned, tyVar:
+      t = t.elementType
+      inc result
+    of tyBool, tyChar, tyEnum, tyObject, tyPointer, tyVoid,
+        tyString, tyCstring, tyInt..tyInt64, tyFloat..tyFloat128,
+        tyUInt..tyUInt64, tyCompositeTypeClass, tyBuiltInTypeClass:
+      inc result
+      break
+    of tyGenericBody:
+      t = t.typeBodyImpl
+    of tyGenericInst, tyStatic:
+      t = t.skipModifier
       inc result
     of tyOr:
       var maxBranch = 0
-      for branch in t.sons:
-        let branchSum = branch.sumGeneric
+      for branch in t.kids:
+        let branchSum = sumGeneric(branch)
         if branchSum > maxBranch: maxBranch = branchSum
-      inc result, maxBranch + 1
+      inc result, maxBranch
       break
-    of tyVar:
-      t = t.sons[0]
-      inc result
-      inc isvar
     of tyTypeDesc:
-      t = t.lastSon
+      t = t.elementType
       if t.kind == tyEmpty: break
       inc result
-    of tyGenericInvocation, tyTuple, tyProc, tyAnd:
-      result += ord(t.kind in {tyGenericInvocation, tyAnd})
-      for i in 0 ..< t.len:
-        if t.sons[i] != nil:
-          result += t.sons[i].sumGeneric
+    of tyGenericParam:
+      if t.len > 0:
+        t = t.skipModifier
+      else:
+        inc result
+        break
+    of tyUntyped, tyTyped: break
+    of tyGenericInvocation, tyTuple, tyAnd:
+      result += ord(t.kind == tyAnd)
+      for a in t.kids:
+        if a != nil:
+          result += sumGeneric(a)
+      break
+    of tyProc:
+      if t.returnType != nil:
+        result += sumGeneric(t.returnType)
+      for _, a in t.paramTypes:
+        result += sumGeneric(a)
       break
-    of tyStatic:
-      return t.sons[0].sumGeneric + 1
-    of tyGenericParam, tyExpr, tyStmt: break
-    of tyAlias, tySink: t = t.lastSon
-    of tyBool, tyChar, tyEnum, tyObject, tyPointer,
-        tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
-        tyUInt..tyUInt64, tyCompositeTypeClass:
-      return isvar
     else:
-      return 0
-
-#var ggDebug: bool
+      break
 
 proc complexDisambiguation(a, b: PType): int =
   # 'a' matches better if *every* argument matches better or equal than 'b'.
   var winner = 0
-  for i in 1 ..< min(a.len, b.len):
-    let x = a.sons[i].sumGeneric
-    let y = b.sons[i].sumGeneric
-    #if ggDebug:
-    #echo "came herA ", typeToString(a.sons[i]), " ", x
-    #echo "came herB ", typeToString(b.sons[i]), " ", y
+  for ai, bi in underspecifiedPairs(a, b, 1):
+    let x = ai.sumGeneric
+    let y = bi.sumGeneric
     if x != y:
       if winner == 0:
         if x > y: winner = 1
@@ -248,8 +379,8 @@ proc complexDisambiguation(a, b: PType): int =
   result = winner
   when false:
     var x, y: int
-    for i in 1 ..< a.len: x += a.sons[i].sumGeneric
-    for i in 1 ..< b.len: y += b.sons[i].sumGeneric
+    for i in 1..<a.len: x += ai.sumGeneric
+    for i in 1..<b.len: y += bi.sumGeneric
     result = x - y
 
 proc writeMatches*(c: TCandidate) =
@@ -261,7 +392,16 @@ proc writeMatches*(c: TCandidate) =
   echo "  conv matches: ", c.convMatches
   echo "  inheritance: ", c.inheritancePenalty
 
-proc cmpCandidates*(a, b: TCandidate): int =
+proc cmpInheritancePenalty(a, b: int): int =
+  var eb = b
+  var ea = a
+  if b < 0:
+    eb = maxInheritancePenalty  # defacto max penalty
+  if a < 0:
+    ea = maxInheritancePenalty
+  eb - ea
+
+proc cmpCandidates*(a, b: TCandidate, isFormal=true): int =
   result = a.exactMatches - b.exactMatches
   if result != 0: return
   result = a.genericMatches - b.genericMatches
@@ -272,19 +412,22 @@ proc cmpCandidates*(a, b: TCandidate): int =
   if result != 0: return
   result = a.convMatches - b.convMatches
   if result != 0: return
-  # the other way round because of other semantics:
-  result = b.inheritancePenalty - a.inheritancePenalty
+  result = cmpInheritancePenalty(a.inheritancePenalty, b.inheritancePenalty)
   if result != 0: return
-  # prefer more specialized generic over more general generic:
-  result = complexDisambiguation(a.callee, b.callee)
-  # only as a last resort, consider scoping:
+  if isFormal:
+    # check for generic subclass relation
+    result = checkGeneric(a, b)
+    if result != 0: return
+    # prefer more specialized generic over more general generic:
+    result = complexDisambiguation(a.callee, b.callee)
   if result != 0: return
+  # only as a last resort, consider scoping:
   result = a.calleeScope - b.calleeScope
 
 proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
   if arg.kind in nkSymChoices:
     result = typeToString(arg[0].typ, prefer)
-    for i in 1 ..< arg.len:
+    for i in 1..<arg.len:
       result.add(" | ")
       result.add typeToString(arg[i].typ, prefer)
   elif arg.typ == nil:
@@ -292,129 +435,144 @@ proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
   else:
     result = arg.typ.typeToString(prefer)
 
-proc describeArgs*(c: PContext, n: PNode, startIdx = 1;
-                   prefer: TPreferedDesc = preferName): string =
+template describeArgImpl(c: PContext, n: PNode, i: int, startIdx = 1; prefer = preferName) =
+  var arg = n[i]
+  if n[i].kind == nkExprEqExpr:
+    result.add renderTree(n[i][0])
+    result.add ": "
+    if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}:
+      arg = c.semTryExpr(c, n[i][1])
+      if arg == nil:
+        arg = n[i][1]
+        arg.typ = newTypeS(tyUntyped, c)
+      else:
+        if arg.typ == nil:
+          arg.typ = newTypeS(tyVoid, c)
+        n[i].typ = arg.typ
+        n[i][1] = arg
+  else:
+    if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo, nkElse,
+                                          nkOfBranch, nkElifBranch,
+                                          nkExceptBranch}:
+      arg = c.semTryExpr(c, n[i])
+      if arg == nil:
+        arg = n[i]
+        arg.typ = newTypeS(tyUntyped, c)
+      else:
+        if arg.typ == nil:
+          arg.typ = newTypeS(tyVoid, c)
+        n[i] = arg
+  if arg.typ != nil and arg.typ.kind == tyError: return
+  result.add argTypeToString(arg, prefer)
+
+proc describeArg*(c: PContext, n: PNode, i: int, startIdx = 1; prefer = preferName): string =
   result = ""
-  for i in countup(startIdx, n.len - 1):
-    var arg = n.sons[i]
-    if n.sons[i].kind == nkExprEqExpr:
-      add(result, renderTree(n.sons[i].sons[0]))
-      add(result, ": ")
-      if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}:
-        # XXX we really need to 'tryExpr' here!
-        arg = c.semOperand(c, n.sons[i].sons[1])
-        n.sons[i].typ = arg.typ
-        n.sons[i].sons[1] = arg
-    else:
-      if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo, nkElse,
-                                           nkOfBranch, nkElifBranch,
-                                           nkExceptBranch}:
-        arg = c.semOperand(c, n.sons[i])
-        n.sons[i] = arg
-    if arg.typ != nil and arg.typ.kind == tyError: return
-    add(result, argTypeToString(arg, prefer))
-    if i != sonsLen(n) - 1: add(result, ", ")
-
-proc typeRelImpl*(c: var TCandidate, f, aOrig: PType,
-                  flags: TTypeRelFlags = {}): TTypeRelation
-
-const traceTypeRel = false
-
-when traceTypeRel:
-  var nextTypeRel = 0
-
-template typeRel*(c: var TCandidate, f, aOrig: PType,
-                 flags: TTypeRelFlags = {}): TTypeRelation =
-  when traceTypeRel:
-    var enteringAt = nextTypeRel
-    if mdbg:
-      inc nextTypeRel
-      echo "----- TYPE REL ", enteringAt
-      debug f
-      debug aOrig
-      # writeStackTrace()
-
-  let r = typeRelImpl(c, f, aOrig, flags)
-
-  when traceTypeRel:
-    if enteringAt != nextTypeRel:
-      echo "----- TYPE REL ", enteringAt, " RESULT: ", r
-
-  r
-
-proc concreteType(c: TCandidate, t: PType): PType =
+  describeArgImpl(c, n, i, startIdx, prefer)
+
+proc describeArgs*(c: PContext, n: PNode, startIdx = 1; prefer = preferName): string =
+  result = ""
+  for i in startIdx..<n.len:
+    describeArgImpl(c, n, i, startIdx, prefer)
+    if i != n.len - 1: result.add ", "
+
+proc concreteType(c: TCandidate, t: PType; f: PType = nil): PType =
   case t.kind
-  of tyNil:
-    result = nil              # what should it be?
   of tyTypeDesc:
     if c.isNoCall: result = t
     else: result = nil
   of tySequence, tySet:
-    if t.sons[0].kind == tyEmpty: result = nil
+    if t.elementType.kind == tyEmpty: result = nil
     else: result = t
-  of tyGenericParam, tyAnything:
+  of tyGenericParam, tyAnything, tyConcept:
     result = t
+    if c.isNoCall: return
     while true:
-      result = PType(idTableGet(c.bindings, t))
+      result = idTableGet(c.bindings, t)
       if result == nil:
         break # it's ok, no match
         # example code that triggers it:
         # proc sort[T](cmp: proc(a, b: T): int = cmp)
       if result.kind != tyGenericParam: break
   of tyGenericInvocation:
-    result = t
-    doAssert(false, "cannot resolve type: " & typeToString(t))
+    result = nil
+  of tyOwned:
+    # bug #11257: the comparison system.`==`[T: proc](x, y: T) works
+    # better without the 'owned' type:
+    if f != nil and f.hasElementType and f.elementType.skipTypes({tyBuiltInTypeClass, tyOr}).kind == tyProc:
+      result = t.skipModifier
+    else:
+      result = t
   else:
     result = t                # Note: empty is valid here
 
-proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation =
+proc handleRange(c: PContext, f, a: PType, min, max: TTypeKind): TTypeRelation =
   if a.kind == f.kind:
     result = isEqual
   else:
     let ab = skipTypes(a, {tyRange})
     let k = ab.kind
-    if k == f.kind: result = isSubrange
-    elif k == tyInt and f.kind in {tyRange, tyInt8..tyInt64,
-                                   tyUInt..tyUInt64} and
-        isIntLit(ab) and ab.n.intVal >= firstOrd(nil, f) and
-                         ab.n.intVal <= lastOrd(nil, f):
+    let nf = c.config.normalizeKind(f.kind)
+    let na = c.config.normalizeKind(k)
+    if k == f.kind:
+      # `a` is a range type matching its base type
+      # see very bottom for range types matching different types
+      if isIntLit(ab):
+        # range type can only give isFromIntLit for base type
+        result = isFromIntLit
+      else:
+        result = isSubrange
+    elif a.kind == tyInt and f.kind in {tyRange, tyInt..tyInt64,
+                                        tyUInt..tyUInt64} and
+        isIntLit(ab) and getInt(ab.n) >= firstOrd(nil, f) and
+                         getInt(ab.n) <= lastOrd(nil, f):
       # passing 'nil' to firstOrd/lastOrd here as type checking rules should
-      # not depent on the target integer size configurations!
+      # not depend on the target integer size configurations!
       # integer literal in the proper range; we want ``i16 + 4`` to stay an
       # ``int16`` operation so we declare the ``4`` pseudo-equal to int16
       result = isFromIntLit
-    elif f.kind == tyInt and k in {tyInt8..tyInt32}:
+    elif a.kind == tyInt and nf == c.config.targetSizeSignedToKind:
+      result = isIntConv
+    elif a.kind == tyUInt and nf == c.config.targetSizeUnsignedToKind:
+      result = isIntConv
+    elif f.kind == tyInt and na in {tyInt8 .. pred(c.config.targetSizeSignedToKind)}:
+      result = isIntConv
+    elif f.kind == tyUInt and na in {tyUInt8 .. pred(c.config.targetSizeUnsignedToKind)}:
       result = isIntConv
     elif k >= min and k <= max:
       result = isConvertible
-    elif a.kind == tyRange and a.sons[0].kind in {tyInt..tyInt64,
-                                                  tyUInt8..tyUInt32} and
-                         a.n[0].intVal >= firstOrd(nil, f) and
-                         a.n[1].intVal <= lastOrd(nil, f):
+    elif a.kind == tyRange and
+      # Make sure the conversion happens between types w/ same signedness
+      (f.kind in {tyInt..tyInt64} and a[0].kind in {tyInt..tyInt64} or
+       f.kind in {tyUInt8..tyUInt32} and a[0].kind in {tyUInt8..tyUInt32}) and
+      a.n[0].intVal >= firstOrd(nil, f) and a.n[1].intVal <= lastOrd(nil, f):
       # passing 'nil' to firstOrd/lastOrd here as type checking rules should
-      # not depent on the target integer size configurations!
+      # not depend on the target integer size configurations!
       result = isConvertible
     else: result = isNone
-    #elif f.kind == tyInt and k in {tyInt..tyInt32}: result = isIntConv
-    #elif f.kind == tyUInt and k in {tyUInt..tyUInt32}: result = isIntConv
 
-proc isConvertibleToRange(f, a: PType): bool =
-  # be less picky for tyRange, as that it is used for array indexing:
+proc isConvertibleToRange(c: PContext, f, a: PType): bool =
   if f.kind in {tyInt..tyInt64, tyUInt..tyUInt64} and
      a.kind in {tyInt..tyInt64, tyUInt..tyUInt64}:
     case f.kind
-    of tyInt, tyInt64: result = true
-    of tyInt8: result = a.kind in {tyInt8, tyInt}
-    of tyInt16: result = a.kind in {tyInt8, tyInt16, tyInt}
-    of tyInt32: result = a.kind in {tyInt8, tyInt16, tyInt32, tyInt}
-    of tyUInt, tyUInt64: result = true
-    of tyUInt8: result = a.kind in {tyUInt8, tyUInt}
-    of tyUInt16: result = a.kind in {tyUInt8, tyUInt16, tyUInt}
-    of tyUInt32: result = a.kind in {tyUInt8, tyUInt16, tyUInt32, tyUInt}
+    of tyInt8: result = isIntLit(a) or a.kind in {tyInt8}
+    of tyInt16: result = isIntLit(a) or a.kind in {tyInt8, tyInt16}
+    of tyInt32: result = isIntLit(a) or a.kind in {tyInt8, tyInt16, tyInt32}
+    # This is wrong, but seems like there's a lot of code that relies on it :(
+    of tyInt, tyUInt: result = true
+    # of tyInt: result = isIntLit(a) or a.kind in {tyInt8 .. c.config.targetSizeSignedToKind}
+    of tyInt64: result = isIntLit(a) or a.kind in {tyInt8, tyInt16, tyInt32, tyInt, tyInt64}
+    of tyUInt8: result = isIntLit(a) or a.kind in {tyUInt8}
+    of tyUInt16: result = isIntLit(a) or a.kind in {tyUInt8, tyUInt16}
+    of tyUInt32: result = isIntLit(a) or a.kind in {tyUInt8, tyUInt16, tyUInt32}
+    # of tyUInt: result = isIntLit(a) or a.kind in {tyUInt8 .. c.config.targetSizeUnsignedToKind}
+    of tyUInt64: result = isIntLit(a) or a.kind in {tyUInt8, tyUInt16, tyUInt32, tyUInt64}
     else: result = false
-  elif f.kind in {tyFloat..tyFloat128} and
-       a.kind in {tyFloat..tyFloat128}:
-    result = true
+  elif f.kind in {tyFloat..tyFloat128}:
+    # `isIntLit` is correct and should be used above as well, see PR:
+    # https://github.com/nim-lang/Nim/pull/11197
+    result = isIntLit(a) or a.kind in {tyFloat..tyFloat128}
+  else:
+    result = false
 
 proc handleFloatRange(f, a: PType): TTypeRelation =
   if a.kind == f.kind:
@@ -431,13 +589,52 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
       else: result = isIntConv
     else: result = isNone
 
+proc reduceToBase(f: PType): PType =
+  #[
+    Returns the lowest order (most general) type that that is compatible with the input.
+    E.g.
+    A[T] = ptr object ... A -> ptr object
+    A[N: static[int]] = array[N, int] ... A -> array
+  ]#
+  case f.kind:
+  of tyGenericParam:
+    if f.len <= 0 or f.skipModifier == nil:
+      result = f
+    else:
+      result = reduceToBase(f.skipModifier)
+  of tyGenericInvocation:
+    result = reduceToBase(f.baseClass)
+  of tyCompositeTypeClass, tyAlias:
+    if not f.hasElementType or f.elementType == nil:
+      result = f
+    else:
+      result = reduceToBase(f.elementType)
+  of tyGenericInst:
+    result = reduceToBase(f.skipModifier)
+  of tyGenericBody:
+    result = reduceToBase(f.typeBodyImpl)
+  of tyUserTypeClass:
+    if f.isResolvedUserTypeClass:
+      result = f.base  # ?? idk if this is right
+    else:
+      result = f.skipModifier
+  of tyStatic, tyOwned, tyVar, tyLent, tySink:
+    result = reduceToBase(f.base)
+  of tyInferred:
+    # This is not true "After a candidate type is selected"
+    result = reduceToBase(f.base)
+  of tyRange:
+    result = f.elementType
+  else:
+    result = f
+
 proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) =
- if fGenericOrigin != nil and last.kind == tyGenericInst and
-     last.len-1 == fGenericOrigin.len:
-   for i in countup(1, sonsLen(fGenericOrigin) - 1):
-     let x = PType(idTableGet(c.bindings, fGenericOrigin.sons[i]))
-     if x == nil:
-       put(c, fGenericOrigin.sons[i], last.sons[i])
+  if fGenericOrigin != nil and last.kind == tyGenericInst and
+     last.kidsLen-1 == fGenericOrigin.kidsLen:
+    for i in FirstGenericParamAt..<fGenericOrigin.kidsLen:
+      let x = idTableGet(c.bindings, fGenericOrigin[i])
+      if x == nil:
+        put(c, fGenericOrigin[i], last[i])
 
 proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int =
   var t = a
@@ -445,8 +642,9 @@ proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int =
   var depth = 0
   var last = a
   while t != nil and not sameObjectTypes(f, t):
-    assert t.kind == tyObject
-    t = t.sons[0]
+    if t.kind != tyObject:  # avoid entering generic params etc
+      return -1
+    t = t.baseClass
     if t == nil: break
     last = t
     t = skipTypes(t, skipPtrs)
@@ -467,22 +665,25 @@ proc skipToObject(t: PType; skipped: var SkippedPtr): PType =
   while r != nil:
     case r.kind
     of tyGenericInvocation:
-      r = r.sons[0]
+      r = r.genericHead
     of tyRef:
       inc ptrs
       skipped = skippedRef
-      r = r.lastSon
+      r = r.elementType
     of tyPtr:
       inc ptrs
       skipped = skippedPtr
-      r = r.lastSon
-    of tyGenericBody, tyGenericInst, tyAlias, tySink:
-      r = r.lastSon
+      r = r.elementType
+    of tyGenericInst, tyAlias, tySink, tyOwned:
+      r = r.elementType
+    of tyGenericBody:
+      r = r.typeBodyImpl
     else:
       break
   if r.kind == tyObject and ptrs <= 1: result = r
+  else: result = nil
 
-proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin: PType = nil): bool =
+proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin: PType): bool =
   assert f.kind in {tyGenericInst, tyGenericInvocation, tyGenericBody}
   var askip = skippedNone
   var fskip = skippedNone
@@ -494,7 +695,7 @@ proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin
   # XXX sameObjectType can return false here. Need to investigate
   # why that is but sameObjectType does way too much work here anyway.
   while t != nil and r.sym != t.sym and askip == fskip:
-    t = t.sons[0]
+    t = t.baseClass
     if t == nil: break
     last = t
     t = t.skipToObject(askip)
@@ -503,32 +704,39 @@ proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin
     genericParamPut(c, last, fGenericOrigin)
     d = depth
     result = true
+  else:
+    result = false
 
 proc minRel(a, b: TTypeRelation): TTypeRelation =
   if a <= b: result = a
   else: result = b
 
-proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
+proc recordRel(c: var TCandidate, f, a: PType, flags: TTypeRelFlags): TTypeRelation =
   result = isNone
   if sameType(f, a):
     result = isEqual
-  elif sonsLen(a) == sonsLen(f):
+  elif sameTupleLengths(a, f):
     result = isEqual
     let firstField = if f.kind == tyTuple: 0
                      else: 1
-    for i in countup(firstField, sonsLen(f) - 1):
-      var m = typeRel(c, f.sons[i], a.sons[i])
+    for _, ff, aa in tupleTypePairs(f, a):
+      var m = typeRel(c, ff, aa, flags)
       if m < isSubtype: return isNone
+      if m == isSubtype and aa.kind != tyNil and c.inheritancePenalty > -1:
+        # we can't process individual element type conversions from a
+        # type conversion for the whole tuple
+        # subtype relations need type conversions when inheritance is used
+        return isNone
       result = minRel(result, m)
     if f.n != nil and a.n != nil:
-      for i in countup(0, sonsLen(f.n) - 1):
+      for i in 0..<f.n.len:
         # check field names:
-        if f.n.sons[i].kind != nkSym: return isNone
-        elif a.n.sons[i].kind != nkSym: return isNone
+        if f.n[i].kind != nkSym: return isNone
+        elif a.n[i].kind != nkSym: return isNone
         else:
-          var x = f.n.sons[i].sym
-          var y = a.n.sons[i].sym
-          if f.kind == tyObject and typeRel(c, x.typ, y.typ) < isSubtype:
+          var x = f.n[i].sym
+          var y = a.n[i].sym
+          if f.kind == tyObject and typeRel(c, x.typ, y.typ, flags) < isSubtype:
             return isNone
           if x.name.id != y.name.id: return isNone
 
@@ -536,13 +744,16 @@ proc allowsNil(f: PType): TTypeRelation {.inline.} =
   result = if tfNotNil notin f.flags: isSubtype else: isNone
 
 proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
-  result = f.kind != a.kind and (f.kind in {tyVar, tyLent} or a.kind in {tyVar, tyLent})
+  result = (f.kind != a.kind and
+    (f.kind in {tyVar, tyLent, tySink} or a.kind in {tyVar, tyLent, tySink})) or
+    isOutParam(f) != isOutParam(a)
 
-proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
+proc procParamTypeRel(c: var TCandidate; f, a: PType): TTypeRelation =
   ## For example we have:
-  ## .. code-block:: nim
+  ##   ```nim
   ##   proc myMap[T,S](sIn: seq[T], f: proc(x: T): S): seq[S] = ...
   ##   proc innerProc[Q,W](q: Q): W = ...
+  ##   ```
   ## And we want to match: myMap(@[1,2,3], innerProc)
   ## This proc (procParamTypeRel) will do the following steps in
   ## three different calls:
@@ -559,17 +770,20 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
     a = a
 
   if a.isMetaType:
-    let aResolved = PType(idTableGet(c.bindings, a))
+    let aResolved = idTableGet(c.bindings, a)
     if aResolved != nil:
       a = aResolved
   if a.isMetaType:
     if f.isMetaType:
       # We are matching a generic proc (as proc param)
       # to another generic type appearing in the proc
-      # signature. There is a change that the target
+      # signature. There is a chance that the target
       # type is already fully-determined, so we are
       # going to try resolve it
-      f = generateTypeInstance(c.c, c.bindings, c.call.info, f)
+      if c.call != nil:
+        f = generateTypeInstance(c.c, c.bindings, c.call.info, f)
+      else:
+        f = nil
       if f == nil or f.isMetaType:
         # no luck resolving the type, so the inference fails
         return isBothMetaConvertible
@@ -578,12 +792,14 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
     if reverseRel >= isGeneric:
       result = isInferred
       #inc c.genericMatches
+    else:
+      result = isNone
   else:
     # Note that this typeRel call will save f's resolved type into c.bindings
     # if f is metatype.
     result = typeRel(c, f, a)
 
-  if result <= isSubtype or inconsistentVarTypes(f, a):
+  if result <= isSubrange or inconsistentVarTypes(f, a):
     result = isNone
 
   #if result == isEqual:
@@ -592,72 +808,67 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
 proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
   case a.kind
   of tyProc:
-    if sonsLen(f) != sonsLen(a): return
+    var f = f
+    copyingEraseVoidParams(c, f)
+    if f.signatureLen != a.signatureLen: return
     result = isEqual      # start with maximum; also correct for no
                           # params at all
 
+    if f.flags * {tfIterator} != a.flags * {tfIterator}:
+      return isNone
+
     template checkParam(f, a) =
       result = minRel(result, procParamTypeRel(c, f, a))
       if result == isNone: return
 
     # Note: We have to do unification for the parameters before the
     # return type!
-    for i in 1 ..< f.sonsLen:
-      checkParam(f.sons[i], a.sons[i])
+    for i in 1..<f.len:
+      checkParam(f[i], a[i])
 
-    if f.sons[0] != nil:
-      if a.sons[0] != nil:
-        checkParam(f.sons[0], a.sons[0])
+    if f[0] != nil:
+      if a[0] != nil:
+        checkParam(f[0], a[0])
       else:
         return isNone
-    elif a.sons[0] != nil:
+    elif a[0] != nil:
       return isNone
 
-    if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
-      return isNone
-    elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {} and
-        optThreadAnalysis in c.c.config.globalOptions:
-      # noSideEffect implies ``tfThread``!
-      return isNone
-    elif f.flags * {tfIterator} != a.flags * {tfIterator}:
-      return isNone
-    elif f.callConv != a.callConv:
-      # valid to pass a 'nimcall' thingie to 'closure':
-      if f.callConv == ccClosure and a.callConv == ccDefault:
-        result = if result == isInferred: isInferredConvertible
-                 elif result == isBothMetaConvertible: isBothMetaConvertible
-                 else: isConvertible
-      else:
-        return isNone
+    result = getProcConvMismatch(c.c.config, f, a, result)[1]
+
     when useEffectSystem:
       if compatibleEffects(f, a) != efCompat: return isNone
+    when defined(drnim):
+      if not c.c.graph.compatibleProps(c.c.graph, f, a): return isNone
 
   of tyNil:
     result = f.allowsNil
-  else: discard
+  else: result = isNone
 
 proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
-  let
-    a0 = firstOrd(nil, a)
-    a1 = lastOrd(nil, a)
-    f0 = firstOrd(nil, f)
-    f1 = lastOrd(nil, f)
-  if a0 == f0 and a1 == f1:
-    result = isEqual
-  elif a0 >= f0 and a1 <= f1:
-    result = isConvertible
-  elif a0 <= f1 and f0 <= a1:
-    # X..Y and C..D overlap iff (X <= D and C <= Y)
-    result = isConvertible
+  template checkRange[T](a0, a1, f0, f1: T): TTypeRelation =
+    if a0 == f0 and a1 == f1:
+      isEqual
+    elif a0 >= f0 and a1 <= f1:
+      isConvertible
+    elif a0 <= f1 and f0 <= a1:
+      # X..Y and C..D overlap iff (X <= D and C <= Y)
+      isConvertible
+    else:
+      isNone
+
+  if f.isOrdinalType:
+    checkRange(firstOrd(nil, a), lastOrd(nil, a), firstOrd(nil, f), lastOrd(nil, f))
   else:
-    result = isNone
+    checkRange(firstFloat(a), lastFloat(a), firstFloat(f), lastFloat(f))
+
 
 proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
   var
     c = m.c
     typeClass = ff.skipTypes({tyUserTypeClassInst})
     body = typeClass.n[3]
-    matchedConceptContext: TMatchedConcept
+    matchedConceptContext = TMatchedConcept()
     prevMatchedConcept = c.matchedConcept
     prevCandidateType = typeClass[0][0]
 
@@ -670,27 +881,27 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
 
   openScope(c)
   matchedConceptContext.candidateType = a
-  typeClass[0].sons[0] = a
+  typeClass[0][0] = a
   c.matchedConcept = addr(matchedConceptContext)
   defer:
     c.matchedConcept = prevMatchedConcept
-    typeClass[0].sons[0] = prevCandidateType
+    typeClass[0][0] = prevCandidateType
     closeScope(c)
 
-  var typeParams: seq[(PSym, PType)]
+  var typeParams: seq[(PSym, PType)] = @[]
 
   if ff.kind == tyUserTypeClassInst:
-    for i in 1 ..< (ff.len - 1):
+    for i in 1..<(ff.len - 1):
       var
-        typeParamName = ff.base.sons[i-1].sym.name
-        typ = ff.sons[i]
-        param: PSym
-        alreadyBound = PType(idTableGet(m.bindings, typ))
+        typeParamName = ff.base[i-1].sym.name
+        typ = ff[i]
+        param: PSym = nil
+        alreadyBound = idTableGet(m.bindings, typ)
 
       if alreadyBound != nil: typ = alreadyBound
 
       template paramSym(kind): untyped =
-        newSym(kind, typeParamName, typeClass.sym, typeClass.sym.info, {})
+        newSym(kind, typeParamName, c.idgen, typeClass.sym, typeClass.sym.info, {})
 
       block addTypeParam:
         for prev in typeParams:
@@ -703,27 +914,29 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
         of tyStatic:
           param = paramSym skConst
           param.typ = typ.exactReplica
+          #copyType(typ, c.idgen, typ.owner)
           if typ.n == nil:
             param.typ.flags.incl tfInferrableStatic
           else:
             param.ast = typ.n
-        of tyUnknown:
+        of tyFromExpr:
           param = paramSym skVar
           param.typ = typ.exactReplica
+          #copyType(typ, c.idgen, typ.owner)
         else:
           param = paramSym skType
           param.typ = if typ.isMetaType:
-                        c.newTypeWithSons(tyInferred, @[typ])
+                        newTypeS(tyInferred, c, typ)
                       else:
                         makeTypeDesc(c, typ)
 
-        typeParams.safeAdd((param, typ))
+        typeParams.add((param, typ))
 
       addDecl(c, param)
 
   var
-    oldWriteHook: type(m.c.config.writelnHook)
-    diagnostics: seq[string]
+    oldWriteHook = default typeof(m.c.config.writelnHook)
+    diagnostics: seq[string] = @[]
     errorPrefix: string
     flags: TExprFlags = {}
     collectDiagnostics = m.diagnosticsEnabled or
@@ -736,7 +949,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
     diagnostics = @[]
     flags = {efExplain}
     m.c.config.writelnHook = proc (s: string) =
-      if errorPrefix == nil: errorPrefix = typeClass.sym.name.s & ":"
+      if errorPrefix.len == 0: errorPrefix = typeClass.sym.name.s & ":"
       let msg = s.replace("Error:", errorPrefix)
       if oldWriteHook != nil: oldWriteHook msg
       diagnostics.add msg
@@ -746,7 +959,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
   if collectDiagnostics:
     m.c.config.writelnHook = oldWriteHook
     for msg in diagnostics:
-      m.diagnostics.safeAdd msg
+      m.diagnostics.add msg
       m.diagnosticsEnabled = true
 
   if checkedBody == nil: return nil
@@ -760,7 +973,8 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
   if ff.kind == tyUserTypeClassInst:
     result = generateTypeInstance(c, m.bindings, typeClass.sym.info, ff)
   else:
-    result = copyType(ff, ff.owner, true)
+    result = ff.exactReplica
+    #copyType(ff, c.idgen, ff.owner)
 
   result.n = checkedBody
 
@@ -783,7 +997,9 @@ proc maybeSkipDistinct(m: TCandidate; t: PType, callee: PSym): PType =
     result = t
 
 proc tryResolvingStaticExpr(c: var TCandidate, n: PNode,
-                            allowUnresolved = false): PNode =
+                            allowUnresolved = false,
+                            allowCalls = false,
+                            expectedType: PType = nil): PNode =
   # Consider this example:
   #   type Value[N: static[int]] = object
   #   proc foo[N](a: Value[N], r: range[0..(N-1)])
@@ -792,6 +1008,8 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode,
   # This proc is used to evaluate such static expressions.
   let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil,
                                         allowMetaTypes = allowUnresolved)
+  if not allowCalls and instantiated.kind in nkCallKinds:
+    return nil
   result = c.c.semExpr(c.c, instantiated)
 
 proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool =
@@ -815,9 +1033,6 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool =
   #
   if lhs.kind in nkCallKinds and lhs[0].kind == nkSym:
     case lhs[0].sym.magic
-    of mUnaryLt:
-      return inferStaticParam(c, lhs[1], rhs + 1)
-
     of mAddI, mAddU, mInc, mSucc:
       if lhs[1].kind == nkIntLit:
         return inferStaticParam(c, lhs[2], rhs - lhs[1].intVal)
@@ -853,16 +1068,21 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool =
       if lhs[2].kind == nkIntLit:
         return inferStaticParam(c, lhs[1], rhs shl lhs[2].intVal)
 
+    of mAshrI:
+      if lhs[2].kind == nkIntLit:
+        return inferStaticParam(c, lhs[1], ashr(rhs, lhs[2].intVal))
+
     of mUnaryMinusI:
       return inferStaticParam(c, lhs[1], -rhs)
 
-    of mUnaryPlusI, mToInt, mToBiggestInt:
+    of mUnaryPlusI:
       return inferStaticParam(c, lhs[1], rhs)
 
     else: discard
 
-  elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and lhs.typ.n == nil:
-    var inferred = newTypeWithSons(c.c, tyStatic, lhs.typ.sons)
+  elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and
+      (lhs.typ.n == nil or idTableGet(c.bindings, lhs.typ) == nil):
+    var inferred = newTypeS(tyStatic, c.c, lhs.typ.elementType)
     inferred.n = newIntNode(nkIntLit, rhs)
     put(c, lhs.typ, inferred)
     if c.c.matchedConcept != nil:
@@ -885,14 +1105,15 @@ proc inferStaticsInRange(c: var TCandidate,
                                           allowUnresolved = true)
   let upperBound = tryResolvingStaticExpr(c, inferred.n[1],
                                           allowUnresolved = true)
-  template doInferStatic(e: PNode, r: BiggestInt) =
+  template doInferStatic(e: PNode, r: Int128) =
     var exp = e
     var rhs = r
-    if inferStaticParam(c, exp, rhs):
+    if inferStaticParam(c, exp, toInt64(rhs)):
       return isGeneric
     else:
       failureToInferStaticParam(c.c.config, exp)
 
+  result = isNone
   if lowerBound.kind == nkIntLit:
     if upperBound.kind == nkIntLit:
       if lengthOrd(c.c.config, concrete) == upperBound.intVal - lowerBound.intVal + 1:
@@ -901,29 +1122,42 @@ proc inferStaticsInRange(c: var TCandidate,
         return isNone
     doInferStatic(upperBound, lengthOrd(c.c.config, concrete) + lowerBound.intVal - 1)
   elif upperBound.kind == nkIntLit:
-    doInferStatic(lowerBound, upperBound.intVal + 1 - lengthOrd(c.c.config, concrete))
+    doInferStatic(lowerBound, getInt(upperBound) + 1 - lengthOrd(c.c.config, concrete))
 
 template subtypeCheck() =
-  if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar, tyLent}:
+  case result
+  of isIntConv:
     result = isNone
+  of isSubrange:
+    discard # XXX should be isNone with preview define, warnings
+  of isConvertible:
+    if f.last.skipTypes(abstractInst).kind != tyOpenArray:
+      # exclude var openarray which compiler supports
+      result = isNone
+  of isSubtype:
+    if f.last.skipTypes(abstractInst).kind in {
+        tyRef, tyPtr, tyVar, tyLent, tyOwned}:
+      # compiler can't handle subtype conversions with pointer indirection
+      result = isNone
+  else: discard
 
 proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
   # this proc is always called for a pair of matching types
   assert f.kind == a.kind
 
   template baseTypesCheck(lhs, rhs: PType): bool =
-    lhs.kind notin {tyPtr, tyRef, tyVar, tyLent} and
+    lhs.kind notin {tyPtr, tyRef, tyVar, tyLent, tyOwned} and
       typeRel(c, lhs, rhs, {trNoCovariance}) == isSubtype
 
   case f.kind
-  of tyRef, tyPtr:
+  of tyRef, tyPtr, tyOwned:
     return baseTypesCheck(f.base, a.base)
   of tyGenericInst:
     let body = f.base
     return body == a.base and
-           a.sonsLen == 3 and
-           tfWeakCovariant notin body.sons[0].flags and
-           baseTypesCheck(f.sons[1], a.sons[1])
+           a.len == 3 and
+           tfWeakCovariant notin body[0].flags and
+           baseTypesCheck(f[1], a[1])
   else:
     return false
 
@@ -947,8 +1181,11 @@ when false:
     of tyFloat64: greater({tyFloat128})
     else: discard
 
-proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
-                 flags: TTypeRelFlags = {}): TTypeRelation =
+template skipOwned(a) =
+  if a.kind == tyOwned: a = a.skipTypes({tyOwned, tyGenericInst})
+
+proc typeRel(c: var TCandidate, f, aOrig: PType,
+             flags: TTypeRelFlags = {}): TTypeRelation =
   # typeRel can be used to establish various relationships between types:
   #
   # 1) When used with concrete types, it will check for type equivalence
@@ -959,8 +1196,8 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
   # of the designated type class.
   #
   # 3) When used with two type classes, it will check whether the types
-  # matching the first type class are a strict subset of the types matching
-  # the other. This allows us to compare the signatures of generic procs in
+  # matching the first type class (aOrig) are a strict subset of the types matching
+  # the other (f). This allows us to compare the signatures of generic procs in
   # order to give preferrence to the most specific one:
   #
   # seq[seq[any]] is a strict subset of seq[any] and hence more specific.
@@ -968,7 +1205,12 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
   result = isNone
   assert(f != nil)
 
-  if f.kind == tyExpr:
+  when declared(deallocatedRefId):
+    let corrupt = deallocatedRefId(cast[pointer](f))
+    if corrupt != 0:
+      c.c.config.quitOrRaise "it's corrupt " & $corrupt
+
+  if f.kind == tyUntyped:
     if aOrig != nil: put(c, f, aOrig)
     return isGeneric
 
@@ -978,7 +1220,8 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
     useTypeLoweringRuleInTypeClass = c.c.matchedConcept != nil and
                                      not c.isNoCall and
                                      f.kind != tyTypeDesc and
-                                     tfExplicit notin aOrig.flags
+                                     tfExplicit notin aOrig.flags and
+                                     tfConceptMatchedTypeSym notin aOrig.flags
 
     aOrig = if useTypeLoweringRuleInTypeClass:
           aOrig.skipTypes({tyTypeDesc})
@@ -988,13 +1231,13 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
   if aOrig.kind == tyInferred:
     let prev = aOrig.previouslyInferred
     if prev != nil:
-      return typeRel(c, f, prev)
+      return typeRel(c, f, prev, flags)
     else:
       var candidate = f
 
       case f.kind
       of tyGenericParam:
-        var prev  = PType(idTableGet(c.bindings, f))
+        var prev = idTableGet(c.bindings, f)
         if prev != nil: candidate = prev
       of tyFromExpr:
         let computedType = tryResolvingStaticExpr(c, f.n).typ
@@ -1009,44 +1252,49 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
       else:
         discard
 
-      result = typeRel(c, aOrig.base, candidate)
+      result = typeRel(c, aOrig.base, candidate, flags)
       if result != isNone:
-        c.inferredTypes.safeAdd aOrig
-        aOrig.sons.add candidate
+        c.inferredTypes.add aOrig
+        aOrig.add candidate
         result = isEqual
       return
 
   template doBind: bool = trDontBind notin flags
 
-  # var and static arguments match regular modifier-free types
-  var a = maybeSkipDistinct(c, aOrig.skipTypes({tyStatic, tyVar, tyLent}), c.calleeSym)
+  # var, sink and static arguments match regular modifier-free types
+  var a = maybeSkipDistinct(c, aOrig.skipTypes({tyStatic, tyVar, tyLent, tySink}), c.calleeSym)
   # XXX: Theoretically, maybeSkipDistinct could be called before we even
   # start the param matching process. This could be done in `prepareOperand`
   # for example, but unfortunately `prepareOperand` is not called in certain
   # situation when nkDotExpr are rotated to nkDotCalls
 
   if aOrig.kind in {tyAlias, tySink}:
-    return typeRel(c, f, lastSon(aOrig))
+    return typeRel(c, f, skipModifier(aOrig), flags)
 
   if a.kind == tyGenericInst and
-      skipTypes(f, {tyVar, tyLent}).kind notin {
+      skipTypes(f, {tyStatic, tyVar, tyLent, tySink}).kind notin {
         tyGenericBody, tyGenericInvocation,
         tyGenericInst, tyGenericParam} + tyTypeClasses:
-    return typeRel(c, f, lastSon(a))
+    return typeRel(c, f, skipModifier(a), flags)
 
   if a.isResolvedUserTypeClass:
-    return typeRel(c, f, a.lastSon)
+    return typeRel(c, f, a.skipModifier, flags)
 
   template bindingRet(res) =
     if doBind:
-      let bound = aOrig.skipTypes({tyRange}).skipIntLit
+      let bound = aOrig.skipTypes({tyRange}).skipIntLit(c.c.idgen)
       put(c, f, bound)
     return res
 
   template considerPreviousT(body: untyped) =
-    var prev = PType(idTableGet(c.bindings, f))
+    var prev = idTableGet(c.bindings, f)
     if prev == nil: body
-    else: return typeRel(c, prev, a)
+    else: return typeRel(c, prev, a, flags)
+
+  if c.c.inGenericContext > 0 and not c.isNoCall and
+      (tfUnresolved in a.flags or a.kind in tyTypeClasses):
+    # cheap check for unresolved arg, not nested
+    return isNone
 
   case a.kind
   of tyOr:
@@ -1056,23 +1304,23 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
     # both int and string must match against number
     # but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219):
     result = isGeneric
-    for branch in a.sons:
+    for branch in a.kids:
       let x = typeRel(c, f, branch, flags + {trDontBind})
       if x == isNone: return isNone
       if x < result: result = x
-    return
-
+    return result
   of tyAnd:
     # XXX: deal with the current dual meaning of tyGenericParam
     c.typedescMatched = true
     # seq[Sortable and Iterable] vs seq[Sortable]
     # only one match is enough
-    for branch in a.sons:
+    for branch in a.kids:
       let x = typeRel(c, f, branch, flags + {trDontBind})
       if x != isNone:
         return if x >= isGeneric: isGeneric else: x
     return isNone
-
+  of tyIterable:
+    if f.kind != tyIterable: return isNone
   of tyNot:
     case f.kind
     of tyNot:
@@ -1080,18 +1328,16 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
       # seq[float] matches the first, but not the second
       # we must turn the problem around:
       # is number a subset of int?
-      return typeRel(c, a.lastSon, f.lastSon)
+      return typeRel(c, a.elementType, f.elementType, flags)
 
     else:
       # negative type classes are essentially infinite,
       # so only the `any` type class is their superset
       return if f.kind == tyAnything: isGeneric
              else: isNone
-
   of tyAnything:
-    return if f.kind == tyAnything: isGeneric
-           else: isNone
-
+    if f.kind == tyAnything: return isGeneric
+    else: return isNone
   of tyUserTypeClass, tyUserTypeClassInst:
     if c.c.matchedConcept != nil and c.c.matchedConcept.depth <= 4:
       # consider this: 'var g: Node' *within* a concept where 'Node'
@@ -1100,6 +1346,17 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
       let x = typeRel(c, a, f, flags + {trDontBind})
       if x >= isGeneric:
         return isGeneric
+  of tyFromExpr:
+    if c.c.inGenericContext > 0:
+      if not c.isNoCall:
+        # generic type bodies can sometimes compile call expressions
+        # prevent expressions with unresolved types from
+        # being passed as parameters
+        return isNone
+      else:
+        # Foo[templateCall(T)] shouldn't fail early if Foo has a constraint
+        # and we can't evaluate `templateCall(T)` yet
+        return isGeneric
   else: discard
 
   case f.kind
@@ -1112,60 +1369,74 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
   of tyRange:
     if a.kind == f.kind:
       if f.base.kind == tyNone: return isGeneric
-      result = typeRel(c, base(f), base(a))
+      result = typeRel(c, base(f), base(a), flags)
       # bugfix: accept integer conversions here
       #if result < isGeneric: result = isNone
       if result notin {isNone, isGeneric}:
         # resolve any late-bound static expressions
         # that may appear in the range:
+        let expectedType = base(f)
         for i in 0..1:
           if f.n[i].kind == nkStaticExpr:
-            f.n.sons[i] = tryResolvingStaticExpr(c, f.n[i])
+            let r = tryResolvingStaticExpr(c, f.n[i], expectedType = expectedType)
+            if r != nil:
+              f.n[i] = r
         result = typeRangeRel(f, a)
     else:
-      if skipTypes(f, {tyRange}).kind == a.kind:
+      let f = skipTypes(f, {tyRange})
+      if f.kind == a.kind and (f.kind != tyEnum or sameEnumTypes(f, a)):
         result = isIntConv
-      elif isConvertibleToRange(skipTypes(f, {tyRange}), a):
+      elif isConvertibleToRange(c.c, f, a):
         result = isConvertible  # a convertible to f
-  of tyInt:      result = handleRange(f, a, tyInt8, tyInt32)
-  of tyInt8:     result = handleRange(f, a, tyInt8, tyInt8)
-  of tyInt16:    result = handleRange(f, a, tyInt8, tyInt16)
-  of tyInt32:    result = handleRange(f, a, tyInt8, tyInt32)
-  of tyInt64:    result = handleRange(f, a, tyInt, tyInt64)
-  of tyUInt:     result = handleRange(f, a, tyUInt8, tyUInt32)
-  of tyUInt8:    result = handleRange(f, a, tyUInt8, tyUInt8)
-  of tyUInt16:   result = handleRange(f, a, tyUInt8, tyUInt16)
-  of tyUInt32:   result = handleRange(f, a, tyUInt8, tyUInt32)
-  of tyUInt64:   result = handleRange(f, a, tyUInt, tyUInt64)
+  of tyInt:      result = handleRange(c.c, f, a, tyInt8, c.c.config.targetSizeSignedToKind)
+  of tyInt8:     result = handleRange(c.c, f, a, tyInt8, tyInt8)
+  of tyInt16:    result = handleRange(c.c, f, a, tyInt8, tyInt16)
+  of tyInt32:    result = handleRange(c.c, f, a, tyInt8, tyInt32)
+  of tyInt64:    result = handleRange(c.c, f, a, tyInt, tyInt64)
+  of tyUInt:     result = handleRange(c.c, f, a, tyUInt8, c.c.config.targetSizeUnsignedToKind)
+  of tyUInt8:    result = handleRange(c.c, f, a, tyUInt8, tyUInt8)
+  of tyUInt16:   result = handleRange(c.c, f, a, tyUInt8, tyUInt16)
+  of tyUInt32:   result = handleRange(c.c, f, a, tyUInt8, tyUInt32)
+  of tyUInt64:   result = handleRange(c.c, f, a, tyUInt, tyUInt64)
   of tyFloat:    result = handleFloatRange(f, a)
   of tyFloat32:  result = handleFloatRange(f, a)
   of tyFloat64:  result = handleFloatRange(f, a)
   of tyFloat128: result = handleFloatRange(f, a)
-  of tyVar, tyLent:
-    if aOrig.kind == f.kind: result = typeRel(c, f.base, aOrig.base)
-    else: result = typeRel(c, f.base, aOrig, flags + {trNoCovariance})
+  of tyVar:
+    let flags = if isOutParam(f): flags + {trIsOutParam} else: flags
+    if aOrig.kind == f.kind and (isOutParam(aOrig) == isOutParam(f)):
+      result = typeRel(c, f.base, aOrig.base, flags)
+    else:
+      result = typeRel(c, f.base, aOrig, flags + {trNoCovariance})
+    subtypeCheck()
+  of tyLent:
+    if aOrig.kind == f.kind:
+      result = typeRel(c, f.base, aOrig.base, flags)
+    else:
+      result = typeRel(c, f.base, aOrig, flags + {trNoCovariance})
     subtypeCheck()
   of tyArray:
-    case a.kind
-    of tyArray:
-      var fRange = f.sons[0]
-      var aRange = a.sons[0]
-      if fRange.kind == tyGenericParam:
-        var prev = PType(idTableGet(c.bindings, fRange))
+    a = reduceToBase(a)
+    if a.kind == tyArray:
+      var fRange = f.indexType
+      var aRange = a.indexType
+      if fRange.kind in {tyGenericParam, tyAnything}:
+        var prev = idTableGet(c.bindings, fRange)
         if prev == nil:
-          put(c, fRange, a.sons[0])
+          if typeRel(c, fRange, aRange) == isNone:
+            return isNone
+          put(c, fRange, a.indexType)
           fRange = a
         else:
           fRange = prev
-      let ff = f.sons[1].skipTypes({tyTypeDesc})
+      let ff = f[1].skipTypes({tyTypeDesc})
       # This typeDesc rule is wrong, see bug #7331
-      let aa = a.sons[1] #.skipTypes({tyTypeDesc})
+      let aa = a[1] #.skipTypes({tyTypeDesc})
 
-      if f.sons[0].kind != tyGenericParam and aa.kind == tyEmpty:
+      if f.indexType.kind != tyGenericParam and aa.kind == tyEmpty:
         result = isGeneric
       else:
-        result = typeRel(c, ff, aa)
-
+        result = typeRel(c, ff, aa, flags)
       if result < isGeneric:
         if nimEnableCovariance and
            trNoCovariance notin flags and
@@ -1176,27 +1447,28 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
           return isNone
 
       if fRange.rangeHasUnresolvedStatic:
+        if aRange.kind in {tyGenericParam} and aRange.reduceToBase() == aRange:
+          return
         return inferStaticsInRange(c, fRange, a)
       elif c.c.matchedConcept != nil and aRange.rangeHasUnresolvedStatic:
         return inferStaticsInRange(c, aRange, f)
+      elif result == isGeneric and concreteType(c, aa, ff) == nil:
+        return isNone
       else:
         if lengthOrd(c.c.config, fRange) != lengthOrd(c.c.config, aRange):
           result = isNone
-    else: discard
   of tyOpenArray, tyVarargs:
-    # varargs[expr] is special too but handled earlier. So we only need to
-    # handle varargs[stmt] which is the same as varargs[typed]:
+    # varargs[untyped] is special too but handled earlier. So we only need to
+    # handle varargs[typed]:
     if f.kind == tyVarargs:
       if tfVarargs in a.flags:
-        return typeRel(c, f.base, a.lastSon)
-      if tfOldSchoolExprStmt in f.sons[0].flags:
-        if f.sons[0].kind == tyExpr: return
-      elif f.sons[0].kind == tyStmt: return
+        return typeRel(c, f.base, a.elementType, flags)
+      if f[0].kind == tyTyped: return
 
     template matchArrayOrSeq(aBase: PType) =
       let ff = f.base
       let aa = aBase
-      let baseRel = typeRel(c, ff, aa)
+      let baseRel = typeRel(c, ff, aa, flags)
       if baseRel >= isGeneric:
         result = isConvertible
       elif nimEnableCovariance and
@@ -1207,33 +1479,32 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
 
     case a.kind
     of tyOpenArray, tyVarargs:
-      result = typeRel(c, base(f), base(a))
+      result = typeRel(c, base(f), base(a), flags)
       if result < isGeneric: result = isNone
     of tyArray:
-      if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty):
+      if (f[0].kind != tyGenericParam) and (a.elementType.kind == tyEmpty):
         return isSubtype
-      matchArrayOrSeq(a.sons[1])
+      matchArrayOrSeq(a.elementType)
     of tySequence:
-      if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
+      if (f[0].kind != tyGenericParam) and (a.elementType.kind == tyEmpty):
         return isConvertible
-      matchArrayOrSeq(a.sons[0])
+      matchArrayOrSeq(a.elementType)
     of tyString:
       if f.kind == tyOpenArray:
-        if f.sons[0].kind == tyChar:
+        if f[0].kind == tyChar:
           result = isConvertible
-        elif f.sons[0].kind == tyGenericParam and a.len > 0 and
-            typeRel(c, base(f), base(a)) >= isGeneric:
+        elif f[0].kind == tyGenericParam and a.len > 0 and
+            typeRel(c, base(f), base(a), flags) >= isGeneric:
           result = isConvertible
     else: discard
-  of tySequence:
-    case a.kind
-    of tySequence:
-      if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
+  of tySequence, tyUncheckedArray:
+    if a.kind == f.kind:
+      if (f[0].kind != tyGenericParam) and (a.elementType.kind == tyEmpty):
         result = isSubtype
       else:
-        let ff = f.sons[0]
-        let aa = a.sons[0]
-        result = typeRel(c, ff, aa)
+        let ff = f[0]
+        let aa = a.elementType
+        result = typeRel(c, ff, aa, flags)
         if result < isGeneric:
           if nimEnableCovariance and
              trNoCovariance notin flags and
@@ -1242,17 +1513,15 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
             result = isSubtype
           else:
             result = isNone
-        elif tfNotNil in f.flags and tfNotNil notin a.flags:
-          result = isNilConversion
-    of tyNil: result = f.allowsNil
-    else: discard
+    elif a.kind == tyNil:
+      result = isNone
   of tyOrdinal:
     if isOrdinalType(a):
-      var x = if a.kind == tyOrdinal: a.sons[0] else: a
-      if f.sons[0].kind == tyNone:
+      var x = if a.kind == tyOrdinal: a.elementType else: a
+      if f[0].kind == tyNone:
         result = isGeneric
       else:
-        result = typeRel(c, f.sons[0], x)
+        result = typeRel(c, f[0], x, flags)
         if result < isGeneric: result = isNone
     elif a.kind == tyGenericParam:
       result = isGeneric
@@ -1260,53 +1529,71 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
     #internalError("forward type in typeRel()")
     result = isNone
   of tyNil:
+    skipOwned(a)
     if a.kind == f.kind: result = isEqual
   of tyTuple:
-    if a.kind == tyTuple: result = recordRel(c, f, a)
+    if a.kind == tyTuple: result = recordRel(c, f, a, flags)
   of tyObject:
-    if a.kind == tyObject:
-      if sameObjectTypes(f, a):
+    let effectiveArgType = if useTypeLoweringRuleInTypeClass:
+        a
+      else:
+        reduceToBase(a)
+    if effectiveArgType.kind == tyObject:
+      if sameObjectTypes(f, effectiveArgType):
+        c.inheritancePenalty = if tfFinal in f.flags: -1 else: 0
         result = isEqual
         # elif tfHasMeta in f.flags: result = recordRel(c, f, a)
-      else:
-        var depth = isObjectSubtype(c, a, f, nil)
-        if depth > 0:
-          inc(c.inheritancePenalty, depth)
+      elif trIsOutParam notin flags:
+        c.inheritancePenalty = isObjectSubtype(c, effectiveArgType, f, nil)
+        if c.inheritancePenalty > 0:
           result = isSubtype
   of tyDistinct:
+    a = a.skipTypes({tyOwned, tyGenericInst, tyRange})
     if a.kind == tyDistinct:
       if sameDistinctTypes(f, a): result = isEqual
-      elif f.base.kind == tyAnything: result = isGeneric
-      elif c.coerceDistincts: result = typeRel(c, f.base, a)
-    elif a.kind == tyNil and f.base.kind in NilableTypes:
-      result = f.allowsNil
-    elif c.coerceDistincts: result = typeRel(c, f.base, a)
+      #elif f.base.kind == tyAnything: result = isGeneric  # issue 4435
+      elif c.coerceDistincts: result = typeRel(c, f.base, a, flags)
+    elif c.coerceDistincts: result = typeRel(c, f.base, a, flags)
   of tySet:
     if a.kind == tySet:
-      if f.sons[0].kind != tyGenericParam and a.sons[0].kind == tyEmpty:
+      if f[0].kind != tyGenericParam and a[0].kind == tyEmpty:
         result = isSubtype
       else:
-        result = typeRel(c, f.sons[0], a.sons[0])
-        if result <= isConvertible:
-          result = isNone     # BUGFIX!
+        result = typeRel(c, f[0], a[0], flags)
+        if result < isGeneric:
+          if tfIsConstructor notin a.flags:
+            # set['a'..'z'] and set[char] have different representations
+            result = isNone
+          else:
+            # but we can convert individual elements of the constructor
+            result = isConvertible
   of tyPtr, tyRef:
+    a = reduceToBase(a)
     if a.kind == f.kind:
       # ptr[R, T] can be passed to ptr[T], but not the other way round:
       if a.len < f.len: return isNone
-      for i in 0..f.len-2:
-        if typeRel(c, f.sons[i], a.sons[i]) == isNone: return isNone
-      result = typeRel(c, f.lastSon, a.lastSon, flags + {trNoCovariance})
+      for i in 0..<f.len-1:
+        if typeRel(c, f[i], a[i], flags) == isNone: return isNone
+      result = typeRel(c, f.elementType, a.elementType, flags + {trNoCovariance})
       subtypeCheck()
-      if result <= isConvertible: result = isNone
+      if result <= isIntConv: result = isNone
       elif tfNotNil in f.flags and tfNotNil notin a.flags:
         result = isNilConversion
     elif a.kind == tyNil: result = f.allowsNil
     else: discard
   of tyProc:
+    skipOwned(a)
     result = procTypeRel(c, f, a)
     if result != isNone and tfNotNil in f.flags and tfNotNil notin a.flags:
       result = isNilConversion
+  of tyOwned:
+    case a.kind
+    of tyOwned:
+      result = typeRel(c, skipModifier(f), skipModifier(a), flags)
+    of tyNil: result = f.allowsNil
+    else: discard
   of tyPointer:
+    skipOwned(a)
     case a.kind
     of tyPointer:
       if tfNotNil in f.flags and tfNotNil notin a.flags:
@@ -1315,26 +1602,25 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
         result = isEqual
     of tyNil: result = f.allowsNil
     of tyProc:
-      if a.callConv != ccClosure: result = isConvertible
+      if isDefined(c.c.config, "nimPreviewProcConversion"):
+        result = isNone
+      else:
+        if a.callConv != ccClosure: result = isConvertible
     of tyPtr:
       # 'pointer' is NOT compatible to regionized pointers
       # so 'dealloc(regionPtr)' fails:
       if a.len == 1: result = isConvertible
-    of tyCString: result = isConvertible
+    of tyCstring: result = isConvertible
     else: discard
   of tyString:
     case a.kind
-    of tyString:
-      if tfNotNil in f.flags and tfNotNil notin a.flags:
-        result = isNilConversion
-      else:
-        result = isEqual
-    of tyNil: result = f.allowsNil
+    of tyString: result = isEqual
+    of tyNil: result = isNone
     else: discard
-  of tyCString:
+  of tyCstring:
     # conversion from string to cstring is automatic:
     case a.kind
-    of tyCString:
+    of tyCstring:
       if tfNotNil in f.flags and tfNotNil notin a.flags:
         result = isNilConversion
       else:
@@ -1342,64 +1628,81 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
     of tyNil: result = f.allowsNil
     of tyString: result = isConvertible
     of tyPtr:
-      # ptr[Tag, char] is not convertible to 'cstring' for now:
-      if a.len == 1:
-        let pointsTo = a.sons[0].skipTypes(abstractInst)
-        if pointsTo.kind == tyChar: result = isConvertible
-        elif pointsTo.kind == tyArray and firstOrd(nil, pointsTo.sons[0]) == 0 and
-            skipTypes(pointsTo.sons[0], {tyRange}).kind in {tyInt..tyInt64} and
-            pointsTo.sons[1].kind == tyChar:
-          result = isConvertible
+      if isDefined(c.c.config, "nimPreviewCstringConversion"):
+        result = isNone
+      else:
+        if a.len == 1:
+          let pointsTo = a[0].skipTypes(abstractInst)
+          if pointsTo.kind == tyChar: result = isConvertible
+          elif pointsTo.kind == tyUncheckedArray and pointsTo[0].kind == tyChar:
+            result = isConvertible
+          elif pointsTo.kind == tyArray and firstOrd(nil, pointsTo[0]) == 0 and
+              skipTypes(pointsTo[0], {tyRange}).kind in {tyInt..tyInt64} and
+              pointsTo[1].kind == tyChar:
+            result = isConvertible
     else: discard
-
   of tyEmpty, tyVoid:
     if a.kind == f.kind: result = isEqual
-
   of tyAlias, tySink:
-    result = typeRel(c, lastSon(f), a)
-
+    result = typeRel(c, skipModifier(f), a, flags)
+  of tyIterable:
+    if a.kind == tyIterable:
+      if f.len == 1:
+        result = typeRel(c, skipModifier(f), skipModifier(a), flags)
+      else:
+        # f.len = 3, for some reason
+        result = isGeneric
+    else:
+      result = isNone
   of tyGenericInst:
-    var prev = PType(idTableGet(c.bindings, f))
+    var prev = idTableGet(c.bindings, f)
+    let origF = f
     var f = if prev == nil: f else: prev
 
-    let roota = a.skipGenericAlias
-    let rootf = f.skipGenericAlias
+    let deptha = a.genericAliasDepth()
+    let depthf = f.genericAliasDepth()
+    let skipBoth = deptha == depthf and (a.len > 0 and f.len > 0 and a.base != f.base)
+
+    let roota = if skipBoth or deptha > depthf: a.skipGenericAlias else: a
+    let rootf = if skipBoth or depthf > deptha: f.skipGenericAlias else: f
 
-    var m = c
     if a.kind == tyGenericInst:
       if roota.base == rootf.base:
         let nextFlags = flags + {trNoCovariance}
         var hasCovariance = false
-        for i in 1 .. rootf.sonsLen-2:
-          let ff = rootf.sons[i]
-          let aa = roota.sons[i]
-          result = typeRel(c, ff, aa, nextFlags)
-          if result notin {isEqual, isGeneric}:
+        # YYYY
+        result = isEqual
+
+        for i in 1..<rootf.len-1:
+          let ff = rootf[i]
+          let aa = roota[i]
+          let res = typeRel(c, ff, aa, nextFlags)
+          if res != isNone and res != isEqual: result = isGeneric
+          if res notin {isEqual, isGeneric}:
             if trNoCovariance notin flags and ff.kind == aa.kind:
-              let paramFlags = rootf.base.sons[i-1].flags
+              let paramFlags = rootf.base[i-1].flags
               hasCovariance =
                 if tfCovariant in paramFlags:
                   if tfWeakCovariant in paramFlags:
                     isCovariantPtr(c, ff, aa)
                   else:
-                    ff.kind notin {tyRef, tyPtr} and result == isSubtype
+                    ff.kind notin {tyRef, tyPtr} and res == isSubtype
                 else:
                   tfContravariant in paramFlags and
-                    typeRel(c, aa, ff) == isSubtype
+                    typeRel(c, aa, ff, flags) == isSubtype
               if hasCovariance:
                 continue
 
             return isNone
         if prev == nil: put(c, f, a)
-        result = isGeneric
       else:
-        let fKind = rootf.lastSon.kind
+        let fKind = rootf.last.kind
         if fKind in {tyAnd, tyOr}:
-          result = typeRel(c, lastSon(f), a)
+          result = typeRel(c, last(f), a, flags)
           if result != isNone: put(c, f, a)
           return
 
-        var aAsObject = roota.lastSon
+        var aAsObject = roota.last
 
         if fKind in {tyRef, tyPtr}:
           if aAsObject.kind == tyObject:
@@ -1409,61 +1712,69 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
           elif aAsObject.kind == fKind:
             aAsObject = aAsObject.base
 
-        if aAsObject.kind == tyObject:
+        if aAsObject.kind == tyObject and trIsOutParam notin flags:
           let baseType = aAsObject.base
           if baseType != nil:
-            c.inheritancePenalty += 1
-            return typeRel(c, f, baseType)
+            inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0)
+            let ret = typeRel(c, f, baseType, flags)
+            return if ret in {isEqual,isGeneric}: isSubtype else: ret
 
         result = isNone
     else:
-      result = typeRel(c, lastSon(f), a)
+      assert last(origF) != nil
+      result = typeRel(c, last(origF), a, flags)
       if result != isNone and a.kind != tyNil:
         put(c, f, a)
-
   of tyGenericBody:
     considerPreviousT:
-      if a.kind == tyGenericInst and a.sons[0] == f:
+      if a == f or a.kind == tyGenericInst and a.skipGenericAlias[0] == f:
         bindingRet isGeneric
-      let ff = lastSon(f)
+      let ff = last(f)
       if ff != nil:
-        result = typeRel(c, ff, a)
-
+        result = typeRel(c, ff, a, flags)
   of tyGenericInvocation:
     var x = a.skipGenericAlias
+    if x.kind == tyGenericParam and x.len > 0:
+      x = x.last
+    let concpt = f[0].skipTypes({tyGenericBody})
+    var preventHack = concpt.kind == tyConcept
+    if x.kind == tyOwned and f[0].kind != tyOwned:
+      preventHack = true
+      x = x.last
     # XXX: This is very hacky. It should be moved back into liftTypeParam
     if x.kind in {tyGenericInst, tyArray} and
-       c.calleeSym != nil and
-       c.calleeSym.kind in {skProc, skFunc} and c.call != nil:
+      c.calleeSym != nil and
+      c.calleeSym.kind in {skProc, skFunc} and c.call != nil and not preventHack:
       let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f)
-      return typeRel(c, inst, a)
-
-    var depth = 0
-    if x.kind == tyGenericInvocation or f.sons[0].kind != tyGenericBody:
-      #InternalError("typeRel: tyGenericInvocation -> tyGenericInvocation")
-      # simply no match for now:
-      discard
-    elif x.kind == tyGenericInst and
-          ((f.sons[0] == x.sons[0]) or isGenericSubType(c, x, f, depth)) and
-          (sonsLen(x) - 1 == sonsLen(f)):
-      for i in countup(1, sonsLen(f) - 1):
-        if x.sons[i].kind == tyGenericParam:
+      return typeRel(c, inst, a, flags)
+
+    if x.kind == tyGenericInvocation:
+      if f[0] == x[0]:
+        for i in 1..<f.len:
+          # Handle when checking against a generic that isn't fully instantiated
+          if i >= x.len: return
+          let tr = typeRel(c, f[i], x[i], flags)
+          if tr <= isSubtype: return
+        result = isGeneric
+    elif x.kind == tyGenericInst and f[0] == x[0] and
+          x.len - 1 == f.len:
+      for i in 1..<f.len:
+        if x[i].kind == tyGenericParam:
           internalError(c.c.graph.config, "wrong instantiated type!")
-        elif typeRel(c, f.sons[i], x.sons[i]) <= isSubtype:
+        elif typeRel(c, f[i], x[i], flags) <= isSubtype:
           # Workaround for regression #4589
-          if f.sons[i].kind != tyTypeDesc: return
-      c.inheritancePenalty += depth
+          if f[i].kind != tyTypeDesc: return
       result = isGeneric
+    elif x.kind == tyGenericInst and concpt.kind == tyConcept:
+      result = if concepts.conceptMatch(c.c, concpt, x, c.bindings, f): isGeneric
+               else: isNone
     else:
-      let genericBody = f.sons[0]
+      let genericBody = f[0]
       var askip = skippedNone
       var fskip = skippedNone
       let aobj = x.skipToObject(askip)
-      let fobj = genericBody.lastSon.skipToObject(fskip)
-      var depth = -1
-      if fobj != nil and aobj != nil and askip == fskip:
-        depth = isObjectSubtype(c, aobj, fobj, f)
-      result = typeRel(c, genericBody, x)
+      let fobj = genericBody.last.skipToObject(fskip)
+      result = typeRel(c, genericBody, x, flags)
       if result != isNone:
         # see tests/generics/tgeneric3.nim for an example that triggers this
         # piece of code:
@@ -1475,90 +1786,99 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
         # var it1 = internalFind(root, 312) # cannot instantiate: 'D'
         #
         # we steal the generic parameters from the tyGenericBody:
-        for i in countup(1, sonsLen(f) - 1):
-          let x = PType(idTableGet(c.bindings, genericBody.sons[i-1]))
+        for i in 1..<f.len:
+          let x = idTableGet(c.bindings, genericBody[i-1])
           if x == nil:
-            discard "maybe fine (for eg. a==tyNil)"
+            discard "maybe fine (for e.g. a==tyNil)"
           elif x.kind in {tyGenericInvocation, tyGenericParam}:
             internalError(c.c.graph.config, "wrong instantiated type!")
           else:
-            put(c, f.sons[i], x)
+            let key = f[i]
+            let old = idTableGet(c.bindings, key)
+            if old == nil:
+              put(c, key, x)
+            elif typeRel(c, old, x, flags + {trDontBind}) == isNone:
+              return isNone
+      var depth = -1
+      if fobj != nil and aobj != nil and askip == fskip:
+        depth = isObjectSubtype(c, aobj, fobj, f)
 
       if result == isNone:
         # Here object inheriting from generic/specialized generic object
         # crossing path with metatypes/aliases, so we need to separate them
         # by checking sym.id
-        let genericSubtype = isGenericSubType(c, x, f, depth, f)
+        let genericSubtype = isGenericSubtype(c, x, f, depth, f)
         if not (genericSubtype and aobj.sym.id != fobj.sym.id) and aOrig.kind != tyGenericBody:
           depth = -1
 
       if depth >= 0:
-        c.inheritancePenalty += depth
+        inc c.inheritancePenalty, depth + int(c.inheritancePenalty < 0)
         # bug #4863: We still need to bind generic alias crap, so
         # we cannot return immediately:
         result = if depth == 0: isGeneric else: isSubtype
   of tyAnd:
     considerPreviousT:
       result = isEqual
-      for branch in f.sons:
-        let x = typeRel(c, branch, aOrig)
+      for branch in f.kids:
+        let x = typeRel(c, branch, aOrig, flags)
         if x < isSubtype: return isNone
         # 'and' implies minimum matching result:
         if x < result: result = x
       if result > isGeneric: result = isGeneric
       bindingRet result
-
   of tyOr:
     considerPreviousT:
       result = isNone
       let oldInheritancePenalty = c.inheritancePenalty
-      var maxInheritance = 0
-      for branch in f.sons:
-        c.inheritancePenalty = 0
-        let x = typeRel(c, branch, aOrig)
-        maxInheritance = max(maxInheritance, c.inheritancePenalty)
-
-        # 'or' implies maximum matching result:
-        if x > result: result = x
-      if result >= isSubtype:
+      var minInheritance = maxInheritancePenalty
+      for branch in f.kids:
+        c.inheritancePenalty = -1
+        let x = typeRel(c, branch, aOrig, flags)
+        if x >= result:
+          if  c.inheritancePenalty > -1:
+            minInheritance = min(minInheritance, c.inheritancePenalty)
+          result = x
+      if result >= isIntConv:
+        if minInheritance < maxInheritancePenalty:
+          c.inheritancePenalty = oldInheritancePenalty + minInheritance
         if result > isGeneric: result = isGeneric
         bindingRet result
       else:
         result = isNone
-      c.inheritancePenalty = oldInheritancePenalty + maxInheritance
-
   of tyNot:
     considerPreviousT:
-      for branch in f.sons:
-        if typeRel(c, branch, aOrig) != isNone:
-          return isNone
+      if typeRel(c, f.elementType, aOrig, flags) != isNone:
+        return isNone
 
       bindingRet isGeneric
-
   of tyAnything:
     considerPreviousT:
       var concrete = concreteType(c, a)
       if concrete != nil and doBind:
         put(c, f, concrete)
       return isGeneric
-
   of tyBuiltInTypeClass:
     considerPreviousT:
-      let targetKind = f.sons[0].kind
-      let effectiveArgType = a.skipTypes({tyRange, tyGenericInst,
-                                          tyBuiltInTypeClass, tyAlias, tySink})
-      let typeClassMatches = targetKind == effectiveArgType.kind and
-                             not effectiveArgType.isEmptyContainer
-      if typeClassMatches or
-        (targetKind in {tyProc, tyPointer} and effectiveArgType.kind == tyNil):
-        put(c, f, a)
+      let target = f.genericHead
+      let targetKind = target.kind
+      var effectiveArgType = reduceToBase(a)
+      effectiveArgType = effectiveArgType.skipTypes({tyBuiltInTypeClass})
+      if targetKind == effectiveArgType.kind:
+        if effectiveArgType.isEmptyContainer:
+          return isNone
+        if targetKind == tyProc:
+          if target.flags * {tfIterator} != effectiveArgType.flags * {tfIterator}:
+            return isNone
+          if tfExplicitCallConv in target.flags and
+              target.callConv != effectiveArgType.callConv:
+            return isNone
+        if doBind: put(c, f, a)
         return isGeneric
       else:
         return isNone
-
   of tyUserTypeClassInst, tyUserTypeClass:
     if f.isResolvedUserTypeClass:
-      result = typeRel(c, f.lastSon, a)
+      result = typeRel(c, f.last, a, flags)
     else:
       considerPreviousT:
         if aOrig == f: return isEqual
@@ -1567,31 +1887,41 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
           bindConcreteTypeToUserTypeClass(matched, a)
           if doBind: put(c, f, matched)
           result = isGeneric
+        elif a.len > 0 and a.last == f:
+          # Needed for checking `Y` == `Addable` in the following
+          #[
+            type
+              Addable = concept a, type A
+                a + a is A
+              MyType[T: Addable; Y: static T] = object
+          ]#
+          result = isGeneric
         else:
           result = isNone
-
+  of tyConcept:
+    result = if concepts.conceptMatch(c.c, f, a, c.bindings, nil): isGeneric
+             else: isNone
   of tyCompositeTypeClass:
     considerPreviousT:
       let roota = a.skipGenericAlias
-      let rootf = f.lastSon.skipGenericAlias
+      let rootf = f.last.skipGenericAlias
       if a.kind == tyGenericInst and roota.base == rootf.base:
-        for i in 1 .. rootf.sonsLen-2:
-          let ff = rootf.sons[i]
-          let aa = roota.sons[i]
-          result = typeRel(c, ff, aa)
+        for i in 1..<rootf.len-1:
+          let ff = rootf[i]
+          let aa = roota[i]
+          result = typeRel(c, ff, aa, flags)
           if result == isNone: return
           if ff.kind == tyRange and result != isEqual: return isNone
       else:
-        result = typeRel(c, rootf.lastSon, a)
+        result = typeRel(c, rootf.last, a, flags)
       if result != isNone:
         put(c, f, a)
         result = isGeneric
-
   of tyGenericParam:
-    var x = PType(idTableGet(c.bindings, f))
+    let doBindGP = doBind or trBindGenericParam in flags
+    var x = idTableGet(c.bindings, f)
     if x == nil:
-      if c.callee.kind == tyGenericBody and
-         f.kind == tyGenericParam and not c.typedescMatched:
+      if c.callee.kind == tyGenericBody and not c.typedescMatched:
         # XXX: The fact that generic types currently use tyGenericParam for
         # their parameters is really a misnomer. tyGenericParam means "match
         # any value" and what we need is "match any type", which can be encoded
@@ -1600,48 +1930,60 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
         if tfWildcard in a.flags:
           result = isGeneric
         elif a.kind == tyTypeDesc:
-          if f.sonsLen == 0:
+          if f.len == 0:
             result = isGeneric
           else:
-            internalAssert c.c.graph.config, a.sons != nil and a.sons.len > 0
+            internalAssert c.c.graph.config, a.len > 0
             c.typedescMatched = true
             var aa = a
             while aa.kind in {tyTypeDesc, tyGenericParam} and aa.len > 0:
-              aa = lastSon(aa)
-            if aa.kind == tyGenericParam:
+              aa = last(aa)
+            if aa.kind in {tyGenericParam} + tyTypeClasses:
+              # If the constraint is a genericParam or typeClass this isGeneric
               return isGeneric
-            result = typeRel(c, f.base, aa)
+            result = typeRel(c, f.base, aa, flags)
             if result > isGeneric: result = isGeneric
+        elif c.isNoCall:
+          if doBindGP:
+            let concrete = concreteType(c, a, f)
+            if concrete == nil: return isNone
+            put(c, f, concrete)
+          result = isGeneric
         else:
           result = isNone
       else:
-        if f.sonsLen > 0 and f.sons[0].kind != tyNone:
-          let oldInheritancePenalty = c.inheritancePenalty
-          result = typeRel(c, f.lastSon, a, flags + {trDontBind})
-          if doBind and result notin {isNone, isGeneric}:
-            let concrete = concreteType(c, a)
+        # check if 'T' has a constraint as in 'proc p[T: Constraint](x: T)'
+        if f.len > 0 and f[0].kind != tyNone:
+          result = typeRel(c, f[0], a, flags + {trDontBind, trBindGenericParam})
+          if doBindGP and result notin {isNone, isGeneric}:
+            let concrete = concreteType(c, a, f)
             if concrete == nil: return isNone
             put(c, f, concrete)
-          # bug #6526
           if result in {isEqual, isSubtype}:
-            # 'T: Class' is a *better* match than just 'T'
-            # but 'T: Subclass' is even better:
-            c.inheritancePenalty = oldInheritancePenalty - c.inheritancePenalty -
-                                  100 * ord(result == isEqual)
             result = isGeneric
+        elif a.kind == tyTypeDesc:
+          # somewhat special typing rule, the following is illegal:
+          # proc p[T](x: T)
+          # p(int)
+          result = isNone
         else:
           result = isGeneric
 
       if result == isGeneric:
         var concrete = a
         if tfWildcard in a.flags:
-          a.sym.kind = skType
+          a.sym.transitionGenericParamToType()
           a.flags.excl tfWildcard
-        else:
-          concrete = concreteType(c, a)
+        elif doBind:
+          # careful: `trDontDont` (set by `checkGeneric`) is not always respected in this call graph.
+          # typRel having two different modes (binding and non-binding) can make things harder to
+          # reason about and maintain. Refactoring typeRel to not be responsible for setting, or
+          # at least validating, bindings can have multiple benefits. This is debatable. I'm not 100% sure.
+          # A design that allows a proper complexity analysis of types like `tyOr` would be ideal.
+          concrete = concreteType(c, a, f)
           if concrete == nil:
             return isNone
-        if doBind:
+        if doBindGP:
           put(c, f, concrete)
       elif result > isGeneric:
         result = isGeneric
@@ -1650,117 +1992,167 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
     elif x.kind == tyGenericParam:
       result = isGeneric
     else:
-      # Special type binding rule for numeric types.
-      # See section "Generic type inference for numeric types" of the
-      # manual for further details:
-      when false:
-        let rebinding = maxNumericType(x.skipTypes({tyRange}), a)
-        if rebinding != nil:
-          put(c, f, rebinding)
-          result = isGeneric
-        else:
-          discard
-      result = typeRel(c, x, a) # check if it fits
+      # This is the bound type - can't benifit from these tallies
+      let
+        inheritancePenaltyOld = c.inheritancePenalty
+      result = typeRel(c, x, a, flags) # check if it fits
+      c.inheritancePenalty = inheritancePenaltyOld
       if result > isGeneric: result = isGeneric
   of tyStatic:
-    let prev = PType(idTableGet(c.bindings, f))
+    let prev = idTableGet(c.bindings, f)
     if prev == nil:
       if aOrig.kind == tyStatic:
-        result = typeRel(c, f.lastSon, a)
-        if result != isNone and f.n != nil:
-          if not exprStructuralEquivalent(f.n, aOrig.n):
-            result = isNone
+        if c.c.inGenericContext > 0 and aOrig.n == nil and not c.isNoCall:
+          # don't match unresolved static value to static param to avoid
+          # faulty instantiations in calls in generic bodies
+          # but not for generic invocations as they only check constraints
+          result = isNone
+        elif f.base.kind notin {tyNone, tyGenericParam}:
+          result = typeRel(c, f.base, a, flags)
+          if result != isNone and f.n != nil:
+            var r = tryResolvingStaticExpr(c, f.n)
+            if r == nil: r = f.n
+            if not exprStructuralEquivalent(r, aOrig.n) and
+                not (aOrig.n != nil and aOrig.n.kind == nkIntLit and
+                  inferStaticParam(c, r, aOrig.n.intVal)):
+              result = isNone
+        elif f.base.kind == tyGenericParam:
+          # Handling things like `type A[T; Y: static T] = object`
+          if f.base.len > 0: # There is a constraint, handle it
+            result = typeRel(c, f.base.last, a, flags)
+          else:
+            # No constraint
+            if tfGenericTypeParam in f.flags:
+              result = isGeneric
+            else:
+              # for things like `proc fun[T](a: static[T])`
+              result = typeRel(c, f.base, a, flags)
+        else:
+          result = isGeneric
         if result != isNone: put(c, f, aOrig)
       elif aOrig.n != nil and aOrig.n.typ != nil:
-        result = typeRel(c, f.lastSon, aOrig.n.typ)
+        result = if f.base.kind != tyNone:
+                   typeRel(c, f.last, aOrig.n.typ, flags)
+                 else: isGeneric
         if result != isNone:
-          var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ])
+          var boundType = newTypeS(tyStatic, c.c, aOrig.n.typ)
           boundType.n = aOrig.n
           put(c, f, boundType)
       else:
         result = isNone
     elif prev.kind == tyStatic:
       if aOrig.kind == tyStatic:
-        result = typeRel(c, prev.lastSon, a)
+        result = typeRel(c, prev.last, a, flags)
         if result != isNone and prev.n != nil:
           if not exprStructuralEquivalent(prev.n, aOrig.n):
             result = isNone
       else: result = isNone
     else:
       # XXX endless recursion?
-      #result = typeRel(c, prev, aOrig)
+      #result = typeRel(c, prev, aOrig, flags)
       result = isNone
-
   of tyInferred:
     let prev = f.previouslyInferred
     if prev != nil:
-      result = typeRel(c, prev, a)
+      result = typeRel(c, prev, a, flags)
     else:
-      result = typeRel(c, f.base, a)
+      result = typeRel(c, f.base, a, flags)
       if result != isNone:
-        c.inferredTypes.safeAdd f
-        f.sons.add a
-
+        c.inferredTypes.add f
+        f.add a
   of tyTypeDesc:
-    var prev = PType(idTableGet(c.bindings, f))
+    var prev = idTableGet(c.bindings, f)
     if prev == nil:
       # proc foo(T: typedesc, x: T)
       # when `f` is an unresolved typedesc, `a` could be any
       # type, so we should not perform this check earlier
-      if a.kind != tyTypeDesc: return isNone
-
-      if f.base.kind == tyNone:
+      if c.c.inGenericContext > 0 and a.containsUnresolvedType:
+        # generic type bodies can sometimes compile call expressions
+        # prevent unresolved generic parameters from being passed to procs as
+        # typedesc parameters
+        result = isNone
+      elif a.kind != tyTypeDesc:
+        if a.kind == tyGenericParam and tfWildcard in a.flags:
+          # TODO: prevent `a` from matching as a wildcard again
+          result = isGeneric
+        else:
+          result = isNone
+      elif f.base.kind == tyNone:
         result = isGeneric
       else:
-        result = typeRel(c, f.base, a.base)
+        result = typeRel(c, f.base, a.base, flags)
 
       if result != isNone:
         put(c, f, a)
     else:
       if tfUnresolved in f.flags:
-        result = typeRel(c, prev.base, a)
+        result = typeRel(c, prev.base, a, flags)
       elif a.kind == tyTypeDesc:
-        result = typeRel(c, prev.base, a.base)
+        result = typeRel(c, prev.base, a.base, flags)
       else:
         result = isNone
-
-  of tyStmt:
-    if aOrig != nil and tfOldSchoolExprStmt notin f.flags:
+  of tyTyped:
+    if aOrig != nil:
       put(c, f, aOrig)
     result = isGeneric
-
-  of tyProxy:
+  of tyError:
     result = isEqual
-
   of tyFromExpr:
     # fix the expression, so it contains the already instantiated types
     if f.n == nil or f.n.kind == nkEmpty: return isGeneric
-    let reevaluated = tryResolvingStaticExpr(c, f.n)
-    case reevaluated.typ.kind
+    if c.c.inGenericContext > 0:
+      # need to delay until instantiation
+      # also prevent infinite recursion below
+      return isNone
+    inc c.c.inGenericContext # to generate tyFromExpr again if unresolved
+    # use prepareNode for consistency with other tyFromExpr in semtypinst:
+    let instantiated = prepareTypesInBody(c.c, c.bindings, f.n)
+    let reevaluated = c.c.semExpr(c.c, instantiated).typ
+    dec c.c.inGenericContext
+    case reevaluated.kind
+    of tyFromExpr:
+      # not resolved
+      result = isNone
     of tyTypeDesc:
-      result = typeRel(c, a, reevaluated.typ.base)
+      result = typeRel(c, reevaluated.base, a, flags)
     of tyStatic:
-      result = typeRel(c, a, reevaluated.typ.base)
-      if result != isNone and reevaluated.typ.n != nil:
-        if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n):
+      result = typeRel(c, reevaluated.base, a, flags)
+      if result != isNone and reevaluated.n != nil:
+        if not exprStructuralEquivalent(aOrig.n, reevaluated.n):
           result = isNone
     else:
-      localError(c.c.graph.config, f.n.info, "type expected")
-      result = isNone
-
+      # bug #14136: other types are just like 'tyStatic' here:
+      result = typeRel(c, reevaluated, a, flags)
+      if result != isNone and reevaluated.n != nil:
+        if not exprStructuralEquivalent(aOrig.n, reevaluated.n):
+          result = isNone
   of tyNone:
     if a.kind == tyNone: result = isEqual
   else:
     internalError c.c.graph.config, " unknown type kind " & $f.kind
 
+when false:
+  var nowDebug = false
+  var dbgCount = 0
+
+  proc typeRel(c: var TCandidate, f, aOrig: PType,
+              flags: TTypeRelFlags = {}): TTypeRelation =
+    if nowDebug:
+      echo f, " <- ", aOrig
+      inc dbgCount
+      if dbgCount == 2:
+        writeStackTrace()
+    result = typeRelImpl(c, f, aOrig, flags)
+    if nowDebug:
+      echo f, " <- ", aOrig, " res ", result
+
 proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
-  var m: TCandidate
-  initCandidate(c, m, f)
+  var m = newCandidate(c, f)
   result = typeRel(m, f, a)
 
 proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate,
                          f: PType): PType =
-  result = PType(idTableGet(m.bindings, f))
+  result = idTableGet(m.bindings, f)
   if result == nil:
     result = generateTypeInstance(c, m.bindings, arg, f)
   if result == nil:
@@ -1771,42 +2163,166 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
                   c: PContext): PNode =
   result = newNodeI(kind, arg.info)
   if containsGenericType(f):
-    if not m.hasFauxMatch:
-      result.typ = getInstantiatedType(c, arg, m, f)
+    if not m.matchedErrorType:
+      result.typ = getInstantiatedType(c, arg, m, f).skipTypes({tySink})
     else:
       result.typ = errorType(c)
   else:
-    result.typ = f
+    result.typ = f.skipTypes({tySink})
+  # keep varness
+  if arg.typ != nil and arg.typ.kind == tyVar:
+    result.typ = toVar(result.typ, tyVar, c.idgen)
+  else:
+    result.typ = result.typ.skipTypes({tyVar})
+
   if result.typ == nil: internalError(c.graph.config, arg.info, "implicitConv")
-  addSon(result, c.graph.emptyNode)
-  addSon(result, arg)
+  result.add c.graph.emptyNode
+  if arg.typ != nil and arg.typ.kind == tyLent:
+    let a = newNodeIT(nkHiddenDeref, arg.info, arg.typ.elementType)
+    a.add arg
+    result.add a
+  else:
+    result.add arg
+
+proc convertLiteral(kind: TNodeKind, c: PContext, m: TCandidate; n: PNode, newType: PType): PNode =
+  # based off changeType but generates implicit conversions instead
+  template addConsiderNil(s, node) =
+    let val = node
+    if val.isNil: return nil
+    s.add(val)
+  case n.kind
+  of nkCurly:
+    result = copyNode(n)
+    for i in 0..<n.len:
+      if n[i].kind == nkRange:
+        var x = copyNode(n[i])
+        x.addConsiderNil convertLiteral(kind, c, m, n[i][0], elemType(newType))
+        x.addConsiderNil convertLiteral(kind, c, m, n[i][1], elemType(newType))
+        result.add x
+      else:
+        result.addConsiderNil convertLiteral(kind, c, m, n[i], elemType(newType))
+    result.typ = newType
+    return
+  of nkBracket:
+    result = copyNode(n)
+    for i in 0..<n.len:
+      result.addConsiderNil convertLiteral(kind, c, m, n[i], elemType(newType))
+    result.typ = newType
+    return
+  of nkPar, nkTupleConstr:
+    let tup = newType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
+    if tup.kind == tyTuple:
+      result = copyNode(n)
+      if n.len > 0 and n[0].kind == nkExprColonExpr:
+        # named tuple?
+        for i in 0..<n.len:
+          var name = n[i][0]
+          if name.kind != nkSym:
+            #globalError(c.config, name.info, "invalid tuple constructor")
+            return nil
+          if tup.n != nil:
+            var f = getSymFromList(tup.n, name.sym.name)
+            if f == nil:
+              #globalError(c.config, name.info, "unknown identifier: " & name.sym.name.s)
+              return nil
+            result.addConsiderNil convertLiteral(kind, c, m, n[i][1], f.typ)
+          else:
+            result.addConsiderNil convertLiteral(kind, c, m, n[i][1], tup[i])
+      else:
+        for i in 0..<n.len:
+          result.addConsiderNil convertLiteral(kind, c, m, n[i], tup[i])
+      result.typ = newType
+      return
+  of nkCharLit..nkUInt64Lit:
+    if n.kind != nkUInt64Lit and not sameTypeOrNil(n.typ, newType) and isOrdinalType(newType):
+      let value = n.intVal
+      if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
+        return nil
+      result = copyNode(n)
+      result.typ = newType
+      return
+  of nkFloatLit..nkFloat64Lit:
+    if newType.skipTypes(abstractVarRange-{tyTypeDesc}).kind == tyFloat:
+      if not floatRangeCheck(n.floatVal, newType):
+        return nil
+      result = copyNode(n)
+      result.typ = newType
+      return
+  of nkSym:
+    if n.sym.kind == skEnumField and not sameTypeOrNil(n.sym.typ, newType) and isOrdinalType(newType):
+      let value = n.sym.position
+      if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
+        return nil
+      result = copyNode(n)
+      result.typ = newType
+      return
+  else: discard
+  return implicitConv(kind, newType, n, m, c)
+
+proc isLValue(c: PContext; n: PNode, isOutParam = false): bool {.inline.} =
+  let aa = isAssignable(nil, n)
+  case aa
+  of arLValue, arLocalLValue, arStrange:
+    result = true
+  of arDiscriminant:
+    result = c.inUncheckedAssignSection > 0
+  of arAddressableConst:
+    let sym = getRoot(n)
+    result = strictDefs in c.features and sym != nil and sym.kind == skLet and isOutParam
+  else:
+    result = false
 
 proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
                    arg: PNode): PNode =
   result = nil
-  for i in countup(0, len(c.converters) - 1):
-    var src = c.converters[i].typ.sons[1]
-    var dest = c.converters[i].typ.sons[0]
+  for i in 0..<c.converters.len:
+    var src = c.converters[i].typ.firstParamType
+    var dest = c.converters[i].typ.returnType
     # for generic type converters we need to check 'src <- a' before
     # 'f <- dest' in order to not break the unification:
     # see tests/tgenericconverter:
     let srca = typeRel(m, src, a)
-    if srca notin {isEqual, isGeneric}: continue
+    if srca notin {isEqual, isGeneric, isSubtype}: continue
+
+    # What's done below matches the logic in ``matchesAux``
+    let constraint = c.converters[i].typ.n[1].sym.constraint
+    if not constraint.isNil and not matchNodeKinds(constraint, arg):
+      continue
+    if src.kind in {tyVar, tyLent} and not isLValue(c, arg):
+      continue
 
     let destIsGeneric = containsGenericType(dest)
     if destIsGeneric:
       dest = generateTypeInstance(c, m.bindings, arg, dest)
     let fdest = typeRel(m, f, dest)
-    if fdest in {isEqual, isGeneric}:
-      markUsed(c.config, arg.info, c.converters[i], c.graph.usageSym)
+    if fdest in {isEqual, isGeneric} and not (dest.kind == tyLent and f.kind in {tyVar}):
+      markUsed(c, arg.info, c.converters[i])
       var s = newSymNode(c.converters[i])
       s.typ = c.converters[i].typ
       s.info = arg.info
       result = newNodeIT(nkHiddenCallConv, arg.info, dest)
-      addSon(result, s)
-      addSon(result, copyTree(arg))
+      result.add s
+      # We build the call expression by ourselves in order to avoid passing this
+      # expression trough the semantic check phase once again so let's make sure
+      # it is correct
+      var param: PNode = nil
+      if srca == isSubtype:
+        param = implicitConv(nkHiddenSubConv, src, copyTree(arg), m, c)
+      elif src.kind in {tyVar}:
+        # Analyse the converter return type.
+        param = newNodeIT(nkHiddenAddr, arg.info, s.typ.firstParamType)
+        param.add copyTree(arg)
+      else:
+        param = copyTree(arg)
+      result.add param
+
+      if dest.kind in {tyVar, tyLent}:
+        dest.flags.incl tfVarIsPtr
+        result = newDeref(result)
+
       inc(m.convMatches)
-      m.genericConverter = srca == isGeneric or destIsGeneric
+      if not m.genericConverter:
+        m.genericConverter = srca == isGeneric or destIsGeneric
       return result
 
 proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
@@ -1821,13 +2337,18 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
   var call = newNodeI(nkCall, arg.info)
   call.add(f.n.copyTree)
   call.add(arg.copyTree)
-  result = c.semExpr(c, call)
+  # XXX: This would be much nicer if we don't use `semTryExpr` and
+  # instead we directly search for overloads with `resolveOverloads`:
+  result = c.semTryExpr(c, call, {efNoSem2Check})
+
   if result != nil:
     if result.typ == nil: return nil
+    # bug #13378, ensure we produce a real generic instantiation:
+    result = c.semExpr(c, call, {efNoSem2Check})
     # resulting type must be consistent with the other arguments:
-    var r = typeRel(m, f.sons[0], result.typ)
+    var r = typeRel(m, f[0], result.typ)
     if r < isGeneric: return nil
-    if result.kind == nkCall: result.kind = nkHiddenCallConv
+    if result.kind == nkCall: result.transitionSonsKind(nkHiddenCallConv)
     inc(m.convMatches)
     if r == isGeneric:
       result.typ = getInstantiatedType(c, arg, m, base(f))
@@ -1845,24 +2366,26 @@ proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
   of isNone: discard
 
 template matchesVoidProc(t: PType): bool =
-  (t.kind == tyProc and t.len == 1 and t.sons[0] == nil) or
-    (t.kind == tyBuiltInTypeClass and t.sons[0].kind == tyProc)
+  (t.kind == tyProc and t.len == 1 and t.returnType == nil) or
+    (t.kind == tyBuiltInTypeClass and t.elementType.kind == tyProc)
 
 proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
                         argSemantized, argOrig: PNode): PNode =
+  result = nil
   var
     fMaybeStatic = f.skipTypes({tyDistinct})
     arg = argSemantized
     a = a
     c = m.c
-
   if tfHasStatic in fMaybeStatic.flags:
     # XXX: When implicit statics are the default
     # this will be done earlier - we just have to
     # make sure that static types enter here
 
-    # XXX: weaken tyGenericParam and call it tyGenericPlaceholder
+    # Zahary: weaken tyGenericParam and call it tyGenericPlaceholder
     # and finally start using tyTypedesc for generic types properly.
+    # Araq: This would only shift the problems around, in 'proc p[T](x: T)'
+    # the T is NOT a typedesc.
     if a.kind == tyGenericParam and tfWildcard in a.flags:
       a.assignType(f)
       # put(m.bindings, f, a)
@@ -1873,13 +2396,19 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
          a.n == nil and
          tfGenericTypeParam notin a.flags:
         return newNodeIT(nkType, argOrig.info, makeTypeFromExpr(c, arg))
-    else:
+    elif a.kind == tyFromExpr and c.inGenericContext > 0:
+      # don't try to evaluate
+      discard
+    elif arg.kind != nkEmpty:
       var evaluated = c.semTryConstExpr(c, arg)
       if evaluated != nil:
-        arg.typ = newTypeS(tyStatic, c)
-        arg.typ.sons = @[evaluated.typ]
-        arg.typ.n = evaluated
-        a = arg.typ
+        # Don't build the type in-place because `evaluated` and `arg` may point
+        # to the same object and we'd end up creating recursive types (#9255)
+        let typ = newTypeS(tyStatic, c, son = evaluated.typ)
+        typ.n = evaluated
+        arg = copyTree(arg) # fix #12864
+        arg.typ = typ
+        a = typ
       else:
         if m.callee.kind == tyGenericBody:
           if f.kind == tyStatic and typeRel(m, f.base, a) != isNone:
@@ -1888,6 +2417,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
             result.typ.n = arg
             return
 
+  let oldInheritancePenalty = m.inheritancePenalty
   var r = typeRel(m, f, a)
 
   # This special typing rule for macros and templates is not documented
@@ -1898,7 +2428,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
     # XXX: duplicating this is ugly, but we cannot (!) move this
     # directly into typeRel using return-like templates
     incMatches(m, r)
-    if f.kind == tyStmt:
+    if f.kind == tyTyped:
       return arg
     elif f.kind == tyTypeDesc:
       return arg
@@ -1907,36 +2437,58 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
     else:
       return argSemantized # argOrig
 
-  # If r == isBothMetaConvertible then we rerun typeRel.
-  # bothMetaCounter is for safety to avoid any infinite loop,
-  #  I don't have any example when it is needed.
-  # lastBindingsLenth is used to check whether m.bindings remains the same,
-  #  because in that case there is no point in continuing.
-  var bothMetaCounter = 0
-  var lastBindingsLength = -1
-  while r == isBothMetaConvertible and
-      lastBindingsLength != m.bindings.counter and
-      bothMetaCounter < 100:
-    lastBindingsLength = m.bindings.counter
-    inc(bothMetaCounter)
-    if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds:
-      result = c.semInferredLambda(c, m.bindings, arg)
-    elif arg.kind != nkSym:
-      return nil
-    else:
-      let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
-      result = newSymNode(inferred, arg.info)
-    inc(m.convMatches)
-    arg = result
-    r = typeRel(m, f, arg.typ)
+  block instantiateGenericRoutine:
+    # In the case where the matched value is a generic proc, we need to
+    # fully instantiate it and then rerun typeRel to make sure it matches.
+    # instantiationCounter is for safety to avoid any infinite loop,
+    #  I don't have any example when it is needed.
+    # lastBindingCount is used to check whether m.bindings remains the same,
+    #  because in that case there is no point in continuing.
+    var instantiationCounter = 0
+    var lastBindingCount = -1
+    while r in {isBothMetaConvertible, isInferred, isInferredConvertible} and
+        lastBindingCount != m.bindings.len and
+        instantiationCounter < 100:
+      lastBindingCount = m.bindings.len
+      inc(instantiationCounter)
+      if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds:
+        result = c.semInferredLambda(c, m.bindings, arg)
+      elif arg.kind != nkSym:
+        return nil
+      elif arg.sym.kind in {skMacro, skTemplate}:
+        return nil
+      else:
+        if arg.sym.ast == nil:
+          return nil
+        let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
+        result = newSymNode(inferred, arg.info)
+      arg = result
+      r = typeRel(m, f, arg.typ)
 
   case r
   of isConvertible:
+    if f.skipTypes({tyRange}).kind in {tyInt, tyUInt}:
+      inc(m.convMatches)
     inc(m.convMatches)
-    result = implicitConv(nkHiddenStdConv, f, arg, m, c)
+    if skipTypes(f, abstractVar-{tyTypeDesc}).kind == tySet:
+      if tfIsConstructor in a.flags and arg.kind == nkCurly:
+        # we marked the set as convertible only because the arg is a literal
+        # in which case we individually convert each element
+        let t =
+          if containsGenericType(f):
+            getInstantiatedType(c, arg, m, f).skipTypes({tySink})
+          else:
+            f.skipTypes({tySink})
+        result = convertLiteral(nkHiddenStdConv, c, m, arg, t)
+      else:
+        result = nil
+    else:
+      result = implicitConv(nkHiddenStdConv, f, arg, m, c)
   of isIntConv:
     # I'm too lazy to introduce another ``*matches`` field, so we conflate
     # ``isIntConv`` and ``isIntLit`` here:
+    if f.skipTypes({tyRange}).kind notin {tyInt, tyUInt}:
+      inc(m.intConvMatches)
     inc(m.intConvMatches)
     result = implicitConv(nkHiddenStdConv, f, arg, m, c)
   of isSubtype:
@@ -1947,29 +2499,24 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
       result = implicitConv(nkHiddenSubConv, f, arg, m, c)
   of isSubrange:
     inc(m.subtypeMatches)
-    if f.kind == tyVar:
+    if f.kind in {tyVar}:
       result = arg
     else:
       result = implicitConv(nkHiddenStdConv, f, arg, m, c)
-  of isInferred, isInferredConvertible:
-    if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds:
-      result = c.semInferredLambda(c, m.bindings, arg)
-    elif arg.kind != nkSym:
-      return nil
-    else:
-      let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
-      result = newSymNode(inferred, arg.info)
-    if r == isInferredConvertible:
-      inc(m.convMatches)
-      result = implicitConv(nkHiddenStdConv, f, result, m, c)
-    else:
-      inc(m.genericMatches)
+  of isInferred:
+    # result should be set in above while loop:
+    assert result != nil
+    inc(m.genericMatches)
+  of isInferredConvertible:
+    # result should be set in above while loop:
+    assert result != nil
+    inc(m.convMatches)
+    result = implicitConv(nkHiddenStdConv, f, result, m, c)
   of isGeneric:
     inc(m.genericMatches)
     if arg.typ == nil:
       result = arg
-    elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or
-         m.inheritancePenalty > 0:
+    elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or cmpInheritancePenalty(oldInheritancePenalty, m.inheritancePenalty) > 0:
       result = implicitConv(nkHiddenSubConv, f, arg, m, c)
     elif arg.typ.isEmptyContainer:
       result = arg.copyTree
@@ -1977,8 +2524,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
     else:
       result = arg
   of isBothMetaConvertible:
-    # This is the result for the 101th time.
-    result = nil
+    # result should be set in above while loop:
+    assert result != nil
+    inc(m.convMatches)
+    result = arg
   of isFromIntLit:
     # too lazy to introduce another ``*matches`` field, so we conflate
     # ``isIntConv`` and ``isIntLit`` here:
@@ -1987,19 +2536,24 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
   of isEqual:
     inc(m.exactMatches)
     result = arg
-    if skipTypes(f, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
+    let ff = skipTypes(f, abstractVar-{tyTypeDesc})
+    if ff.kind == tyTuple or
+      (arg.typ != nil and skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple):
       result = implicitConv(nkHiddenSubConv, f, arg, m, c)
   of isNone:
     # do not do this in ``typeRel`` as it then can't infer T in ``ref T``:
-    if a.kind in {tyProxy, tyUnknown}:
+    if a.kind == tyFromExpr: return nil
+    elif a.kind == tyError:
       inc(m.genericMatches)
-      m.fauxMatch = a.kind
+      m.matchedErrorType = true
       return arg
     elif a.kind == tyVoid and f.matchesVoidProc and argOrig.kind == nkStmtList:
       # lift do blocks without params to lambdas
+      # now deprecated
+      message(c.config, argOrig.info, warnStmtListLambda)
       let p = c.graph
       let lifted = c.semExpr(c, newProcNode(nkDo, argOrig.info, body = argOrig,
-          params = p.emptyNode, name = p.emptyNode, pattern = p.emptyNode,
+          params = nkFormalParams.newTree(p.emptyNode), name = p.emptyNode, pattern = p.emptyNode,
           genericParams = p.emptyNode, pragmas = p.emptyNode, exceptions = p.emptyNode), {})
       if f.kind == tyBuiltInTypeClass:
         inc m.genericMatches
@@ -2011,121 +2565,177 @@ 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)
+      elif f[0].kind == tyTyped:
+        inc m.genericMatches
+        result = arg
       else:
         r = typeRel(m, base(f), a)
-        if r >= isGeneric:
+        case r
+        of isGeneric:
+          inc(m.convMatches)
+          result = copyTree(arg)
+          result.typ = getInstantiatedType(c, arg, m, base(f))
+          m.baseTypeMatch = true
+        of isFromIntLit:
+          inc(m.intConvMatches, 256)
+          result = implicitConv(nkHiddenStdConv, f[0], arg, m, c)
+          m.baseTypeMatch = true
+        of isEqual:
           inc(m.convMatches)
           result = copyTree(arg)
-          if r == isGeneric:
-            result.typ = getInstantiatedType(c, arg, m, base(f))
           m.baseTypeMatch = true
-        # bug #4799, varargs accepting subtype relation object
-        elif r == isSubtype:
+        of isSubtype: # bug #4799, varargs accepting subtype relation object
           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)
           if result != nil: m.baseTypeMatch = true
 
+proc staticAwareTypeRel(m: var TCandidate, f: PType, arg: var PNode): TTypeRelation =
+  if f.kind == tyStatic and f.base.kind == tyProc:
+    # The ast of the type does not point to the symbol.
+    # Without this we will never resolve a `static proc` with overloads
+    let copiedNode = copyNode(arg)
+    copiedNode.typ = exactReplica(copiedNode.typ)
+    copiedNode.typ.n = arg
+    arg = copiedNode
+  typeRel(m, f, arg.typ)
+
+
 proc paramTypesMatch*(m: var TCandidate, f, a: PType,
                       arg, argOrig: PNode): PNode =
   if arg == nil or arg.kind notin nkSymChoices:
     result = paramTypesMatchAux(m, f, a, arg, argOrig)
   else:
-    # CAUTION: The order depends on the used hashing scheme. Thus it is
-    # incorrect to simply use the first fitting match. However, to implement
-    # this correctly is inefficient. We have to copy `m` here to be able to
-    # roll back the side effects of the unification algorithm.
-    let c = m.c
-    var x, y, z: TCandidate
-    initCandidate(c, x, m.callee)
-    initCandidate(c, y, m.callee)
-    initCandidate(c, z, m.callee)
-    x.calleeSym = m.calleeSym
-    y.calleeSym = m.calleeSym
-    z.calleeSym = m.calleeSym
+    # symbol kinds that don't participate in symchoice type disambiguation:
+    let matchSet = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
+
     var best = -1
-    for i in 0 ..< arg.len:
-      if arg.sons[i].sym.kind in {skProc, skFunc, skMethod, skConverter,
-                                  skIterator, skMacro, skTemplate}:
-        copyCandidate(z, m)
-        z.callee = arg.sons[i].typ
-        if tfUnresolved in z.callee.flags: continue
-        z.calleeSym = arg.sons[i].sym
-        #if arg.sons[i].sym.name.s == "cmp":
-        #  ggDebug = true
-        #  echo "CALLLEEEEEEEE A ", typeToString(z.callee)
-        # XXX this is still all wrong: (T, T) should be 2 generic matches
-        # and  (int, int) 2 exact matches, etc. Essentially you cannot call
-        # typeRel here and expect things to work!
-        let r = typeRel(z, f, arg.sons[i].typ)
-        incMatches(z, r, 2)
-        #if arg.sons[i].sym.name.s == "cmp": # and arg.info.line == 606:
-        #  echo "M ", r, " ", arg.info, " ", typeToString(arg.sons[i].sym.typ)
-        #  writeMatches(z)
-        if r != isNone:
-          z.state = csMatch
-          case x.state
-          of csEmpty, csNoMatch:
-            x = z
+    result = arg
+
+    var actingF = f
+    if f.kind == tyVarargs:
+      if m.calleeSym.kind in {skTemplate, skMacro}:
+        actingF = f[0]
+    if actingF.kind in {tyTyped, tyUntyped}:
+      var
+        bestScope = -1
+        counts = 0
+      for i in 0..<arg.len:
+        if arg[i].sym.kind in matchSet:
+          let thisScope = cmpScopes(m.c, arg[i].sym)
+          if thisScope > bestScope:
             best = i
-          of csMatch:
-            let cmp = cmpCandidates(x, z)
-            if cmp < 0:
-              best = i
-              x = z
-            elif cmp == 0:
-              y = z           # z is as good as x
-
-    if x.state == csEmpty:
-      result = nil
-    elif y.state == csMatch and cmpCandidates(x, y) == 0:
-      if x.state != csMatch:
-        internalError(m.c.graph.config, arg.info, "x.state is not csMatch")
-      # ambiguous: more than one symbol fits!
-      # See tsymchoice_for_expr as an example. 'f.kind == tyExpr' should match
-      # anyway:
-      if f.kind in {tyExpr, tyStmt}: result = arg
-      else: result = nil
+            bestScope = thisScope
+            counts = 0
+          elif thisScope == bestScope:
+            inc counts
+      if best == -1:
+        result = nil
+      elif counts > 0:
+        m.genericMatches = 1
+        best = -1
     else:
+      # CAUTION: The order depends on the used hashing scheme. Thus it is
+      # incorrect to simply use the first fitting match. However, to implement
+      # this correctly is inefficient. We have to copy `m` here to be able to
+      # roll back the side effects of the unification algorithm.
+      let c = m.c
+      var
+        x = newCandidate(c, m.callee)  # potential "best"
+        y = newCandidate(c, m.callee)  # potential competitor with x
+        z = newCandidate(c, m.callee)  # buffer for copies of m
+      x.calleeSym = m.calleeSym
+      y.calleeSym = m.calleeSym
+      z.calleeSym = m.calleeSym
+
+      for i in 0..<arg.len:
+        if arg[i].sym.kind in matchSet:
+          copyCandidate(z, m)
+          z.callee = arg[i].typ
+          if arg[i].sym.kind == skType and z.callee.kind != tyTypeDesc:
+            # creating the symchoice with the type sym having typedesc type
+            # breaks a lot of stuff, so we make the typedesc type here
+            # mirrored from `newSymNodeTypeDesc`
+            z.callee = newType(tyTypeDesc, c.idgen, arg[i].sym.owner)
+            z.callee.addSonSkipIntLit(arg[i].sym.typ, c.idgen)
+          if tfUnresolved in z.callee.flags: continue
+          z.calleeSym = arg[i].sym
+          z.calleeScope = cmpScopes(m.c, arg[i].sym)
+          # XXX this is still all wrong: (T, T) should be 2 generic matches
+          # and  (int, int) 2 exact matches, etc. Essentially you cannot call
+          # typeRel here and expect things to work!
+          let r = staticAwareTypeRel(z, f, arg[i])
+          incMatches(z, r, 2)
+          if r != isNone:
+            z.state = csMatch
+            case x.state
+            of csEmpty, csNoMatch:
+              x = z
+              best = i
+            of csMatch:
+              let cmp = cmpCandidates(x, z, isFormal=false)
+              if cmp < 0:
+                best = i
+                x = z
+              elif cmp == 0:
+                y = z           # z is as good as x
+
+      if x.state == csEmpty:
+        result = nil
+      elif y.state == csMatch and cmpCandidates(x, y, isFormal=false) == 0:
+        if x.state != csMatch:
+          internalError(m.c.graph.config, arg.info, "x.state is not csMatch")
+        result = nil
+    if best > -1 and result != nil:
       # only one valid interpretation found:
-      markUsed(m.c.config, arg.info, arg.sons[best].sym, m.c.graph.usageSym)
-      styleCheckUse(arg.info, arg.sons[best].sym)
-      result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best],
-                                  argOrig)
+      markUsed(m.c, arg.info, arg[best].sym)
+      onUse(arg.info, arg[best].sym)
+      result = paramTypesMatchAux(m, f, arg[best].typ, arg[best], argOrig)
+  when false:
+    if m.calleeSym != nil and m.calleeSym.name.s == "[]":
+      echo m.c.config $ arg.info, " for ", m.calleeSym.name.s, " ", m.c.config $ m.calleeSym.info
+      writeMatches(m)
 
 proc setSon(father: PNode, at: int, son: PNode) =
   let oldLen = father.len
   if oldLen <= at:
     setLen(father.sons, at + 1)
-  father.sons[at] = son
+  father[at] = son
   # insert potential 'void' parameters:
-  #for i in oldLen ..< at:
-  #  father.sons[i] = newNodeIT(nkEmpty, son.info, getSysType(tyVoid))
+  #for i in oldLen..<at:
+  #  father[i] = newNodeIT(nkEmpty, son.info, getSysType(tyVoid))
 
 # we are allowed to modify the calling node in the 'prepare*' procs:
 proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
-  if formal.kind == tyExpr and formal.len != 1:
-    # {tyTypeDesc, tyExpr, tyStmt, tyProxy}:
+  if formal.kind == tyUntyped and formal.len != 1:
+    # {tyTypeDesc, tyUntyped, tyTyped, tyError}:
     # a.typ == nil is valid
     result = a
   elif a.typ.isNil:
-    # XXX This is unsound! 'formal' can differ from overloaded routine to
-    # overloaded routine!
-    let flags = {efDetermineType, efAllowStmt}
-                #if formal.kind == tyIter: {efDetermineType, efWantIterator}
-                #else: {efDetermineType, efAllowStmt}
-                #elif formal.kind == tyStmt: {efDetermineType, efWantStmt}
-                #else: {efDetermineType}
-    result = c.semOperand(c, a, flags)
+    if formal.kind == tyIterable:
+      let flags = {efDetermineType, efAllowStmt, efWantIterator, efWantIterable}
+      result = c.semOperand(c, a, flags)
+    else:
+      # XXX This is unsound! 'formal' can differ from overloaded routine to
+      # overloaded routine!
+      let flags = {efDetermineType, efAllowStmt}
+                  #if formal.kind == tyIterable: {efDetermineType, efWantIterator}
+                  #else: {efDetermineType, efAllowStmt}
+                  #elif formal.kind == tyTyped: {efDetermineType, efWantStmt}
+                  #else: {efDetermineType}
+      result = c.semOperand(c, a, flags)
   else:
     result = a
     considerGenSyms(c, result)
+    if result.kind != nkHiddenDeref and result.typ.kind in {tyVar, tyLent} and c.matchedConcept == nil:
+      result = newDeref(result)
 
 proc prepareOperand(c: PContext; a: PNode): PNode =
   if a.typ.isNil:
@@ -2135,15 +2745,15 @@ proc prepareOperand(c: PContext; a: PNode): PNode =
     considerGenSyms(c, result)
 
 proc prepareNamedParam(a: PNode; c: PContext) =
-  if a.sons[0].kind != nkIdent:
-    var info = a.sons[0].info
-    a.sons[0] = newIdentNode(considerQuotedIdent(c, a.sons[0]), info)
+  if a[0].kind != nkIdent:
+    var info = a[0].info
+    a[0] = newIdentNode(considerQuotedIdent(c, a[0]), info)
 
 proc arrayConstr(c: PContext, n: PNode): PType =
   result = newTypeS(tyArray, c)
   rawAddSon(result, makeRangeType(c, 0, 0, n.info))
   addSonSkipIntLit(result, skipTypes(n.typ,
-      {tyGenericInst, tyVar, tyLent, tyOrdinal}))
+      {tyVar, tyLent, tyOrdinal}), c.idgen)
 
 proc arrayConstr(c: PContext, info: TLineInfo): PType =
   result = newTypeS(tyArray, c)
@@ -2152,87 +2762,121 @@ proc arrayConstr(c: PContext, info: TLineInfo): PType =
 
 proc incrIndexType(t: PType) =
   assert t.kind == tyArray
-  inc t.sons[0].n.sons[1].intVal
+  inc t.indexType.n[1].intVal
 
 template isVarargsUntyped(x): untyped =
-  x.kind == tyVarargs and x.sons[0].kind == tyExpr and
-    tfOldSchoolExprStmt notin x.sons[0].flags
+  x.kind == tyVarargs and x[0].kind == tyUntyped
+
+template isVarargsTyped(x): untyped =
+  x.kind == tyVarargs and x[0].kind == tyTyped
+
+proc findFirstArgBlock(m: var TCandidate, n: PNode): int =
+  # see https://github.com/nim-lang/RFCs/issues/405
+  result = int.high
+  for a2 in countdown(n.len-1, 0):
+    # checking `nfBlockArg in n[a2].flags` wouldn't work inside templates
+    if n[a2].kind != nkStmtList: break
+    let formalLast = m.callee.n[m.callee.n.len - (n.len - a2)]
+    # parameter has to occupy space (no default value, not void or varargs)
+    if formalLast.kind == nkSym and formalLast.sym.ast == nil and
+        formalLast.sym.typ.kind notin {tyVoid, tyVarargs}:
+      result = a2
+    else: break
+
+proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var IntSet) =
+
+  template noMatch() =
+    c.mergeShadowScope #merge so that we don't have to resem for later overloads
+    m.state = csNoMatch
+    m.firstMismatch.arg = a
+    m.firstMismatch.formal = formal
+    return
 
-proc matchesAux(c: PContext, n, nOrig: PNode,
-                m: var TCandidate, marker: var IntSet) =
   template checkConstraint(n: untyped) {.dirty.} =
-    if not formal.constraint.isNil:
+    if not formal.constraint.isNil and sfCodegenDecl notin formal.flags:
       if matchNodeKinds(formal.constraint, n):
         # better match over other routines with no such restriction:
         inc(m.genericMatches, 100)
       else:
-        m.state = csNoMatch
-        return
-    if formal.typ.kind == tyVar:
-      if not n.isLValue:
-        m.state = csNoMatch
-        m.mutabilityProblem = uint8(f-1)
-        return
-
-  var
-    # iterates over formal parameters
-    f = if m.callee.kind != tyGenericBody: 1
-        else: 0
-    # iterates over the actual given arguments
-    a = 1
+        noMatch()
+
+    if formal.typ.kind in {tyVar}:
+      let argConverter = if arg.kind == nkHiddenDeref: arg[0] else: arg
+      if argConverter.kind == nkHiddenCallConv:
+        if argConverter.typ.kind notin {tyVar}:
+          m.firstMismatch.kind = kVarNeeded
+          noMatch()
+      elif not (isLValue(c, n, isOutParam(formal.typ))):
+        m.firstMismatch.kind = kVarNeeded
+        noMatch()
 
   m.state = csMatch # until proven otherwise
-  m.call = newNodeI(n.kind, n.info)
-  m.call.typ = base(m.callee) # may be nil
-  var formalLen = m.callee.n.len
-  addSon(m.call, copyTree(n.sons[0]))
-  var container: PNode = nil # constructed container
-  var formal: PSym = if formalLen > 1: m.callee.n.sons[1].sym else: nil
+  m.firstMismatch = MismatchInfo()
+  m.call = newNodeIT(n.kind, n.info, m.callee.base)
+  m.call.add n[0]
 
+  var
+    a = 1 # iterates over the actual given arguments
+    f = if m.callee.kind != tyGenericBody: 1
+        else: 0 # iterates over formal parameters
+    arg: PNode = nil # current prepared argument
+    formalLen = m.callee.n.len
+    formal = if formalLen > 1: m.callee.n[1].sym else: nil # current routine parameter
+    container: PNode = nil # constructed container
+  let firstArgBlock = findFirstArgBlock(m, n)
   while a < n.len:
+    c.openShadowScope
+
     if a >= formalLen-1 and f < formalLen and m.callee.n[f].typ.isVarargsUntyped:
-      formal = m.callee.n.sons[f].sym
+      formal = m.callee.n[f].sym
       incl(marker, formal.position)
-      if container.isNil:
-        container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info))
-        setSon(m.call, formal.position + 1, container)
+
+      if n[a].kind == nkHiddenStdConv:
+        doAssert n[a][0].kind == nkEmpty and
+                 n[a][1].kind in {nkBracket, nkArgList}
+        # Steal the container and pass it along
+        setSon(m.call, formal.position + 1, n[a][1])
       else:
-        incrIndexType(container.typ)
-      addSon(container, n.sons[a])
-    elif n.sons[a].kind == nkExprEqExpr:
+        if container.isNil:
+          container = newNodeIT(nkArgList, n[a].info, arrayConstr(c, n.info))
+          setSon(m.call, formal.position + 1, container)
+        else:
+          incrIndexType(container.typ)
+        container.add n[a]
+    elif n[a].kind == nkExprEqExpr:
       # named param
+      m.firstMismatch.kind = kUnknownNamedParam
       # check if m.callee has such a param:
-      prepareNamedParam(n.sons[a], c)
-      if n.sons[a].sons[0].kind != nkIdent:
-        localError(c.config, n.sons[a].info, "named parameter has to be an identifier")
-        m.state = csNoMatch
-        return
-      formal = getSymFromList(m.callee.n, n.sons[a].sons[0].ident, 1)
+      prepareNamedParam(n[a], c)
+      if n[a][0].kind != nkIdent:
+        localError(c.config, n[a].info, "named parameter has to be an identifier")
+        noMatch()
+      formal = getNamedParamFromList(m.callee.n, n[a][0].ident)
       if formal == nil:
         # no error message!
-        m.state = csNoMatch
-        return
+        noMatch()
       if containsOrIncl(marker, formal.position):
+        m.firstMismatch.kind = kAlreadyGiven
         # already in namedParams, so no match
         # we used to produce 'errCannotBindXTwice' here but see
         # bug #3836 of why that is not sound (other overload with
         # different parameter names could match later on):
-        when false: localError(n.sons[a].info, errCannotBindXTwice, formal.name.s)
-        m.state = csNoMatch
-        return
+        when false: localError(n[a].info, errCannotBindXTwice, formal.name.s)
+        noMatch()
       m.baseTypeMatch = false
-      n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1])
-      n.sons[a].typ = n.sons[a].sons[1].typ
-      var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
-                                n.sons[a].sons[1], n.sons[a].sons[1])
+      m.typedescMatched = false
+      n[a][1] = prepareOperand(c, formal.typ, n[a][1])
+      n[a].typ = n[a][1].typ
+      arg = paramTypesMatch(m, formal.typ, n[a].typ,
+                                n[a][1], n[a][1])
+      m.firstMismatch.kind = kTypeMismatch
       if arg == nil:
-        m.state = csNoMatch
-        return
-      checkConstraint(n.sons[a].sons[1])
+        noMatch()
+      checkConstraint(n[a][1])
       if m.baseTypeMatch:
         #assert(container == nil)
-        container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
-        addSon(container, arg)
+        container = newNodeIT(nkBracket, n[a].info, arrayConstr(c, arg))
+        container.add arg
         setSon(m.call, formal.position + 1, container)
         if f != formalLen - 1: container = nil
       else:
@@ -2245,84 +2889,108 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
         if tfVarargs in m.callee.flags:
           # is ok... but don't increment any counters...
           # we have no formal here to snoop at:
-          n.sons[a] = prepareOperand(c, n.sons[a])
-          if skipTypes(n.sons[a].typ, abstractVar-{tyTypeDesc}).kind==tyString:
-            addSon(m.call, implicitConv(nkHiddenStdConv,
-                  getSysType(c.graph, n.sons[a].info, tyCString),
-                  copyTree(n.sons[a]), m, c))
+          n[a] = prepareOperand(c, n[a])
+          if skipTypes(n[a].typ, abstractVar-{tyTypeDesc}).kind==tyString:
+            m.call.add implicitConv(nkHiddenStdConv,
+                  getSysType(c.graph, n[a].info, tyCstring),
+                  copyTree(n[a]), m, c)
           else:
-            addSon(m.call, copyTree(n.sons[a]))
+            m.call.add copyTree(n[a])
         elif formal != nil and formal.typ.kind == tyVarargs:
+          m.firstMismatch.kind = kTypeMismatch
           # beware of the side-effects in 'prepareOperand'! So only do it for
           # varargs matching. See tests/metatype/tstatic_overloading.
           m.baseTypeMatch = false
+          m.typedescMatched = false
           incl(marker, formal.position)
-          n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
-          var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
-                                    n.sons[a], nOrig.sons[a])
+          n[a] = prepareOperand(c, formal.typ, n[a])
+          arg = paramTypesMatch(m, formal.typ, n[a].typ,
+                                    n[a], nOrig[a])
           if arg != nil and m.baseTypeMatch and container != nil:
-            addSon(container, arg)
+            container.add arg
             incrIndexType(container.typ)
-            checkConstraint(n.sons[a])
+            checkConstraint(n[a])
           else:
-            m.state = csNoMatch
-            return
+            noMatch()
         else:
-          m.state = csNoMatch
-          return
+          m.firstMismatch.kind = kExtraArg
+          noMatch()
       else:
-        if m.callee.n.sons[f].kind != nkSym:
-          internalError(c.config, n.sons[a].info, "matches")
-          return
-        formal = m.callee.n.sons[f].sym
+        if m.callee.n[f].kind != nkSym:
+          internalError(c.config, n[a].info, "matches")
+          noMatch()
+        if flexibleOptionalParams in c.features and a >= firstArgBlock:
+          f = max(f, m.callee.n.len - (n.len - a))
+        formal = m.callee.n[f].sym
+        m.firstMismatch.kind = kTypeMismatch
         if containsOrIncl(marker, formal.position) and container.isNil:
-          # already in namedParams: (see above remark)
-          when false: localError(n.sons[a].info, errCannotBindXTwice, formal.name.s)
-          m.state = csNoMatch
-          return
+          m.firstMismatch.kind = kPositionalAlreadyGiven
+          # positional param already in namedParams: (see above remark)
+          when false: localError(n[a].info, errCannotBindXTwice, formal.name.s)
+          noMatch()
 
         if formal.typ.isVarargsUntyped:
           if container.isNil:
-            container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info))
+            container = newNodeIT(nkArgList, n[a].info, arrayConstr(c, n.info))
             setSon(m.call, formal.position + 1, container)
           else:
             incrIndexType(container.typ)
-          addSon(container, n.sons[a])
+          container.add n[a]
         else:
           m.baseTypeMatch = false
-          n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
-          var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
-                                    n.sons[a], nOrig.sons[a])
+          m.typedescMatched = false
+          n[a] = prepareOperand(c, formal.typ, n[a])
+          arg = paramTypesMatch(m, formal.typ, n[a].typ,
+                                    n[a], nOrig[a])
           if arg == nil:
-            m.state = csNoMatch
-            m.firstMismatch = f
-            return
-          if m.baseTypeMatch:
+            noMatch()
+          if formal.typ.isVarargsTyped and m.calleeSym.kind in {skTemplate, skMacro}:
+            if container.isNil:
+              container = newNodeIT(nkBracket, n[a].info, arrayConstr(c, n.info))
+              setSon(m.call, formal.position + 1, implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
+            else:
+              incrIndexType(container.typ)
+            container.add n[a]
+            f = max(f, formalLen - n.len + a + 1)
+          elif m.baseTypeMatch:
+            assert formal.typ.kind == tyVarargs
             #assert(container == nil)
             if container.isNil:
-              container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
+              container = newNodeIT(nkBracket, n[a].info, arrayConstr(c, arg))
               container.typ.flags.incl tfVarargs
             else:
               incrIndexType(container.typ)
-            addSon(container, arg)
+            container.add arg
             setSon(m.call, formal.position + 1,
                    implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
             #if f != formalLen - 1: container = nil
 
             # pick the formal from the end, so that 'x, y, varargs, z' works:
             f = max(f, formalLen - n.len + a + 1)
-          else:
+          elif formal.typ.kind != tyVarargs or container == nil:
             setSon(m.call, formal.position + 1, arg)
-            inc(f)
+            inc f
             container = nil
-        checkConstraint(n.sons[a])
-    inc(a)
+          else:
+            # we end up here if the argument can be converted into the varargs
+            # formal (e.g. seq[T] -> varargs[T]) but we have already instantiated
+            # a container
+            #assert arg.kind == nkHiddenStdConv # for 'nim check'
+            # this assertion can be off
+            localError(c.config, n[a].info, "cannot convert $1 to $2" % [
+              typeToString(n[a].typ), typeToString(formal.typ) ])
+            noMatch()
+        checkConstraint(n[a])
+
+    if m.state == csMatch and not (m.calleeSym != nil and m.calleeSym.kind in {skTemplate, skMacro}):
+      c.mergeShadowScope
+    else:
+      c.closeShadowScope
 
-proc semFinishOperands*(c: PContext, n: PNode) =
-  # this needs to be called to ensure that after overloading resolution every
-  # argument has been sem'checked:
-  for i in 1 ..< n.len:
-    n.sons[i] = prepareOperand(c, n.sons[i])
+    inc a
+  # for some edge cases (see tdont_return_unowned_from_owned test case)
+  m.firstMismatch.arg = a
+  m.firstMismatch.formal = formal
 
 proc partialMatch*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
   # for 'suggest' support:
@@ -2333,42 +3001,67 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
   if m.magic in {mArrGet, mArrPut}:
     m.state = csMatch
     m.call = n
+    # Note the following doesn't work as it would produce ambiguities.
+    # Instead we patch system.nim, see bug #8049.
+    when false:
+      inc m.genericMatches
+      inc m.exactMatches
     return
+  # initCandidate may have given csNoMatch if generic params didn't match:
+  if m.state == csNoMatch: return
   var marker = initIntSet()
   matchesAux(c, n, nOrig, m, marker)
   if m.state == csNoMatch: return
   # check that every formal parameter got a value:
-  var f = 1
-  while f < sonsLen(m.callee.n):
-    var formal = m.callee.n.sons[f].sym
+  for f in 1..<m.callee.n.len:
+    let formal = m.callee.n[f].sym
     if not containsOrIncl(marker, formal.position):
       if formal.ast == nil:
         if formal.typ.kind == tyVarargs:
-          var container = newNodeIT(nkBracket, n.info, arrayConstr(c, n.info))
+          # For consistency with what happens in `matchesAux` select the
+          # container node kind accordingly
+          let cnKind = if formal.typ.isVarargsUntyped: nkArgList else: nkBracket
+          var container = newNodeIT(cnKind, n.info, arrayConstr(c, n.info))
           setSon(m.call, formal.position + 1,
                  implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
         else:
           # no default value
           m.state = csNoMatch
-          m.firstMismatch = f
+          m.firstMismatch.kind = kMissingParam
+          m.firstMismatch.formal = formal
           break
       else:
-        # use default value:
-        var def = copyTree(formal.ast)
-        if def.kind == nkNilLit:
-          def = implicitConv(nkHiddenStdConv, formal.typ, def, m, c)
+        # mirrored with updateDefaultParams:
+        if formal.ast.kind == nkEmpty:
+          # The default param value is set to empty in `instantiateProcType`
+          # when the type of the default expression doesn't match the type
+          # of the instantiated proc param:
+          pushInfoContext(c.config, m.call.info,
+            if m.calleeSym != nil: m.calleeSym.detailedInfo else: "")
+          typeMismatch(c.config, formal.ast.info, formal.typ, formal.ast.typ, formal.ast)
+          popInfoContext(c.config)
+          formal.ast.typ = errorType(c)
+        if nfDefaultRefsParam in formal.ast.flags:
+          m.call.flags.incl nfDefaultRefsParam
+        var defaultValue = copyTree(formal.ast)
+        if defaultValue.kind == nkNilLit:
+          defaultValue = implicitConv(nkHiddenStdConv, formal.typ, defaultValue, m, c)
+        # proc foo(x: T = 0.0)
+        # foo()
         if {tfImplicitTypeParam, tfGenericTypeParam} * formal.typ.flags != {}:
-          put(m, formal.typ, def.typ)
-        setSon(m.call, formal.position + 1, def)
-    inc(f)
+          let existing = idTableGet(m.bindings, formal.typ)
+          if existing == nil or existing.kind == tyTypeDesc:
+            # see bug #11600:
+            put(m, formal.typ, defaultValue.typ)
+        defaultValue.flags.incl nfDefaultParam
+        setSon(m.call, formal.position + 1, defaultValue)
   # forget all inferred types if the overload matching failed
   if m.state == csNoMatch:
     for t in m.inferredTypes:
-      if t.sonsLen > 1: t.sons.setLen 1
+      if t.len > 1: t.newSons 1
 
 proc argtypeMatches*(c: PContext, f, a: PType, fromHlo = false): bool =
-  var m: TCandidate
-  initCandidate(c, m, f)
+  var m = newCandidate(c, f)
   let res = paramTypesMatch(m, f, a, c.graph.emptyNode, nil)
   #instantiateGenericConverters(c, res, m)
   # XXX this is used by patterns.nim too; I think it's better to not
@@ -2379,20 +3072,21 @@ proc argtypeMatches*(c: PContext, f, a: PType, fromHlo = false): bool =
     # pattern templates do not allow for conversions except from int literal
     res != nil and m.convMatches == 0 and m.intConvMatches in [0, 256]
 
+
 proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
-                      op: TTypeAttachedOp; col: int): PSym {.procvar.} =
-  var m: TCandidate
-  initCandidate(c, m, dc.typ)
+                      op: TTypeAttachedOp; col: int): PSym =
+  var m = newCandidate(c, dc.typ)
   if col >= dc.typ.len:
     localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'")
     return nil
-  var f = dc.typ.sons[col]
+  var f = dc.typ[col]
 
   if op == attachedDeepCopy:
-    if f.kind in {tyRef, tyPtr}: f = f.lastSon
+    if f.kind in {tyRef, tyPtr}: f = f.elementType
   else:
-    if f.kind == tyVar: f = f.lastSon
+    if f.kind in {tyVar}: f = f.elementType
   if typeRel(m, f, t) == isNone:
+    result = nil
     localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'")
   else:
     result = c.semGenerateInstance(c, dc, m.bindings, info)
@@ -2405,7 +3099,7 @@ when not declared(tests):
   template tests(s: untyped) = discard
 
 tests:
-  var dummyOwner = newSym(skModule, getIdent("test_module"), nil, UnknownLineInfo())
+  var dummyOwner = newSym(skModule, getIdent("test_module"), nil, unknownLineInfo)
 
   proc `|` (t1, t2: PType): PType =
     result = newType(tyOr, dummyOwner)
@@ -2428,9 +3122,9 @@ tests:
   proc array(x: int, t: PType): PType =
     result = newType(tyArray, dummyOwner)
 
-    var n = newNodeI(nkRange, UnknownLineInfo())
-    addSon(n, newIntNode(nkIntLit, 0))
-    addSon(n, newIntNode(nkIntLit, x))
+    var n = newNodeI(nkRange, unknownLineInfo)
+    n.add newIntNode(nkIntLit, 0)
+    n.add newIntNode(nkIntLit, x)
     let range = newType(tyRange, dummyOwner)
 
     result.rawAddSon(range)
@@ -2446,19 +3140,18 @@ tests:
       number = int | float
 
     var TFoo = newType(tyObject, dummyOwner)
-    TFoo.sym = newSym(skType, getIdent"TFoo", dummyOwner, UnknownLineInfo())
+    TFoo.sym = newSym(skType, getIdent"TFoo", dummyOwner, unknownLineInfo)
 
     var T1 = newType(tyGenericParam, dummyOwner)
-    T1.sym = newSym(skType, getIdent"T1", dummyOwner, UnknownLineInfo())
+    T1.sym = newSym(skType, getIdent"T1", dummyOwner, unknownLineInfo)
     T1.sym.position = 0
 
     var T2 = newType(tyGenericParam, dummyOwner)
-    T2.sym = newSym(skType, getIdent"T2", dummyOwner, UnknownLineInfo())
+    T2.sym = newSym(skType, getIdent"T2", dummyOwner, unknownLineInfo)
     T2.sym.position = 1
 
     setup:
-      var c: TCandidate
-      initCandidate(nil, c, nil)
+      var c = newCandidate(nil, nil)
 
     template yes(x, y) =
       test astToStr(x) & " is " & astToStr(y):
@@ -2520,4 +3213,3 @@ tests:
 
     yes int, ordinal
     no  string, ordinal
-
diff --git a/compiler/sinkparameter_inference.nim b/compiler/sinkparameter_inference.nim
new file mode 100644
index 000000000..09d54ec79
--- /dev/null
+++ b/compiler/sinkparameter_inference.nim
@@ -0,0 +1,68 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+proc checkForSink*(config: ConfigRef; idgen: IdGenerator; owner: PSym; arg: PNode) =
+  #[ Patterns we seek to detect:
+
+    someLocation = p # ---> p: sink T
+    passToSink(p)    # p: sink
+    ObjConstr(fieldName: p)
+    [p, q] # array construction
+
+    # Open question:
+    var local = p # sink parameter?
+    passToSink(local)
+  ]#
+  case arg.kind
+  of nkSym:
+    if arg.sym.kind == skParam and
+        arg.sym.owner == owner and
+        owner.typ != nil and owner.typ.kind == tyProc and
+        arg.sym.typ.hasDestructor and
+        arg.sym.typ.kind notin {tyVar, tySink, tyOwned}:
+      # Watch out: cannot do this inference for procs with forward
+      # declarations.
+      if sfWasForwarded notin owner.flags:
+        let argType = arg.sym.typ
+
+        let sinkType = newType(tySink, idgen, owner)
+        sinkType.size = argType.size
+        sinkType.align = argType.align
+        sinkType.paddingAtEnd = argType.paddingAtEnd
+        sinkType.add argType
+
+        arg.sym.typ = sinkType
+        owner.typ[arg.sym.position+1] = sinkType
+
+        #message(config, arg.info, warnUser,
+        #  ("turned '$1' to a sink parameter") % [$arg])
+        #echo config $ arg.info, " turned into a sink parameter ", arg.sym.name.s
+      elif sfWasForwarded notin arg.sym.flags:
+        # we only report every potential 'sink' parameter only once:
+        incl arg.sym.flags, sfWasForwarded
+        message(config, arg.info, hintPerformance,
+          "could not turn '$1' to a sink parameter" % [arg.sym.name.s])
+      #echo config $ arg.info, " candidate for a sink parameter here"
+  of nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr:
+    if not isEmptyType(arg.typ):
+      checkForSink(config, idgen, owner, arg.lastSon)
+  of nkIfStmt, nkIfExpr, nkWhen:
+    for branch in arg:
+      let value = branch.lastSon
+      if not isEmptyType(value.typ):
+        checkForSink(config, idgen, owner, value)
+  of nkCaseStmt:
+    for i in 1..<arg.len:
+      let value = arg[i].lastSon
+      if not isEmptyType(value.typ):
+        checkForSink(config, idgen, owner, value)
+  of nkTryStmt:
+    checkForSink(config, idgen, owner, arg[0])
+  else:
+    discard "nothing to do"
diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim
new file mode 100644
index 000000000..1dd481ec0
--- /dev/null
+++ b/compiler/sizealignoffsetimpl.nim
@@ -0,0 +1,525 @@
+#
+#
+#           The Nim Compiler
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+## code owner: Arne Döring
+## e-mail: arne.doering@gmx.net
+## included from types.nim
+
+proc align(address, alignment: BiggestInt): BiggestInt =
+  result = (address + (alignment - 1)) and not (alignment - 1)
+
+proc align(address, alignment: int32): int32 =
+  result = (address + (alignment - 1)) and not (alignment - 1)
+
+const
+  ## a size is considered "unknown" when it is an imported type from C
+  ## or C++.
+  szUnknownSize* = -3
+  szIllegalRecursion* = -2
+  szUncomputedSize* = -1
+  szTooBigSize* = -4
+
+type IllegalTypeRecursionError = object of ValueError
+
+proc raiseIllegalTypeRecursion() =
+  raise newException(IllegalTypeRecursionError, "illegal type recursion")
+
+type
+  OffsetAccum* = object
+    maxAlign*: int32
+    offset*: int32
+
+proc inc*(arg: var OffsetAccum; value: int32) =
+  if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion()
+  if value == szUnknownSize or arg.offset == szUnknownSize:
+    arg.offset = szUnknownSize
+  else:
+    arg.offset += value
+
+proc alignmentMax(a, b: int32): int32 =
+  if unlikely(a == szIllegalRecursion or b == szIllegalRecursion): raiseIllegalTypeRecursion()
+  if a == szUnknownSize or b == szUnknownSize:
+    szUnknownSize
+  else:
+    max(a, b)
+
+proc align*(arg: var OffsetAccum; value: int32) =
+  if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion()
+  if value == szUnknownSize or arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize:
+    arg.maxAlign = szUnknownSize
+    arg.offset = szUnknownSize
+  else:
+    arg.maxAlign = max(value, arg.maxAlign)
+    arg.offset = align(arg.offset, value)
+
+proc mergeBranch(arg: var OffsetAccum; value: OffsetAccum) =
+  if value.maxAlign == szUnknownSize or arg.maxAlign == szUnknownSize or
+     value.offset == szUnknownSize or arg.offset == szUnknownSize:
+    arg.maxAlign = szUnknownSize
+    arg.offset = szUnknownSize
+  else:
+    arg.offset = max(arg.offset, value.offset)
+    arg.maxAlign = max(arg.maxAlign, value.maxAlign)
+
+proc finish(arg: var OffsetAccum): int32 =
+  if arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize:
+    result = szUnknownSize
+    arg.offset = szUnknownSize
+  else:
+    result = align(arg.offset, arg.maxAlign) - arg.offset
+    arg.offset += result
+
+proc computeSizeAlign*(conf: ConfigRef; typ: PType)
+
+proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
+  ## returns object alignment
+  case n.kind
+  of nkRecCase:
+    assert(n[0].kind == nkSym)
+    result = computeSubObjectAlign(conf, n[0])
+    for i in 1..<n.len:
+      let child = n[i]
+      case child.kind
+      of nkOfBranch, nkElse:
+        let align = computeSubObjectAlign(conf, child.lastSon)
+        if align < 0:
+          return align
+        result = max(result, align)
+      else:
+        internalError(conf, "computeSubObjectAlign")
+  of nkRecList:
+    result = 1
+    for i, child in n.sons:
+      let align = computeSubObjectAlign(conf, n[i])
+      if align < 0:
+        return align
+      result = max(result, align)
+  of nkSym:
+    computeSizeAlign(conf, n.sym.typ)
+    result = n.sym.typ.align
+  else:
+    result = 1
+
+
+proc setOffsetsToUnknown(n: PNode) =
+  if n.kind == nkSym and n.sym.kind == skField:
+    n.sym.offset = szUnknownSize
+  else:
+    for i in 0..<n.safeLen:
+      setOffsetsToUnknown(n[i])
+
+proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode; packed: bool; accum: var OffsetAccum) =
+  ## ``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:
+    raiseIllegalTypeRecursion()
+  case n.kind
+  of nkRecCase:
+    assert(n[0].kind == nkSym)
+    computeObjectOffsetsFoldFunction(conf, n[0], packed, accum)
+    var maxChildAlign = if accum.offset == szUnknownSize: szUnknownSize.int32 else: 1'i32
+    if not packed:
+      for i in 1..<n.len:
+        let child = n[i]
+        case child.kind
+        of nkOfBranch, nkElse:
+          # offset parameter cannot be known yet, it needs to know the alignment first
+          let align = int32(computeSubObjectAlign(conf, n[i].lastSon))
+          maxChildAlign = alignmentMax(maxChildAlign, align)
+        else:
+          internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)")
+    if maxChildAlign == szUnknownSize:
+      setOffsetsToUnknown(n)
+      accum.offset = szUnknownSize
+      accum.maxAlign = szUnknownSize
+    else:
+      # the union needs to be aligned first, before the offsets can be assigned
+      accum.align(maxChildAlign)
+      let accumRoot = accum # copy, because each branch should start af the same offset
+      for i in 1..<n.len:
+        var branchAccum = OffsetAccum(offset: accumRoot.offset, maxAlign: 1)
+        computeObjectOffsetsFoldFunction(conf, n[i].lastSon, packed, branchAccum)
+        discard finish(branchAccum)
+        accum.mergeBranch(branchAccum)
+  of nkRecList:
+    for i, child in n.sons:
+      computeObjectOffsetsFoldFunction(conf, child, packed, accum)
+  of nkSym:
+    var size = szUnknownSize.int32
+    var align = szUnknownSize.int32
+    if n.sym.bitsize == 0: # 0 represents bitsize not set
+      computeSizeAlign(conf, n.sym.typ)
+      size = n.sym.typ.size.int32
+      align = if packed: 1 else: n.sym.typ.align.int32
+    accum.align(align)
+    if n.sym.alignment > 0:
+      accum.align(n.sym.alignment.int32)
+    n.sym.offset = accum.offset
+    accum.inc(size)
+  else:
+    accum.maxAlign = szUnknownSize
+    accum.offset = szUnknownSize
+
+proc computeUnionObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode; packed: bool; accum: var OffsetAccum) =
+  ## ``accum.offset`` will the offset from the larget member of the union.
+  case n.kind
+  of nkRecCase:
+    accum.offset = szUnknownSize
+    accum.maxAlign = szUnknownSize
+    localError(conf, n.info, "Illegal use of ``case`` in union type.")
+  of nkRecList:
+    let accumRoot = accum # copy, because each branch should start af the same offset
+    for child in n.sons:
+      var branchAccum = OffsetAccum(offset: accumRoot.offset, maxAlign: 1)
+      computeUnionObjectOffsetsFoldFunction(conf, child, packed, branchAccum)
+      discard finish(branchAccum)
+      accum.mergeBranch(branchAccum)
+  of nkSym:
+    var size = szUnknownSize.int32
+    var align = szUnknownSize.int32
+    if n.sym.bitsize == 0: # 0 represents bitsize not set
+      computeSizeAlign(conf, n.sym.typ)
+      size = n.sym.typ.size.int32
+      align = if packed: 1 else: n.sym.typ.align.int32
+    accum.align(align)
+    if n.sym.alignment > 0:
+      accum.align(n.sym.alignment.int32)
+    n.sym.offset = accum.offset
+    accum.inc(size)
+  else:
+    accum.maxAlign = szUnknownSize
+    accum.offset = szUnknownSize
+
+proc computeSizeAlign(conf: ConfigRef; typ: PType) =
+  template setSize(typ, s) =
+    typ.size = s
+    typ.align = s
+    typ.paddingAtEnd = 0
+
+  ## computes and sets ``size`` and ``align`` members of ``typ``
+  assert typ != nil
+  let hasSize = typ.size != szUncomputedSize
+  let hasAlign = typ.align != szUncomputedSize
+
+  if hasSize and hasAlign:
+    # nothing to do, size and align already computed
+    return
+
+  # This function can only calculate both, size and align at the same time.
+  # If one of them is already set this value is stored here and reapplied
+  let revertSize = typ.size
+  let revertAlign = typ.align
+  defer:
+    if hasSize:
+      typ.size = revertSize
+    if hasAlign:
+      typ.align = revertAlign
+
+  if typ.size == szIllegalRecursion or typ.align == szIllegalRecursion:
+    # we are already computing the size of the type
+    # --> illegal recursion in type
+    return
+
+  # mark computation in progress
+  typ.size = szIllegalRecursion
+  typ.align = szIllegalRecursion
+  typ.paddingAtEnd = 0
+
+  var tk = typ.kind
+  case tk
+  of tyProc:
+    if typ.callConv == ccClosure:
+      typ.size = 2 * conf.target.ptrSize
+    else:
+      typ.size = conf.target.ptrSize
+    typ.align = int16(conf.target.ptrSize)
+  of tyNil:
+    typ.size = conf.target.ptrSize
+    typ.align = int16(conf.target.ptrSize)
+  of tyString:
+    if optSeqDestructors in conf.globalOptions:
+      typ.size = conf.target.ptrSize * 2
+    else:
+      typ.size = conf.target.ptrSize
+    typ.align = int16(conf.target.ptrSize)
+  of tyCstring, tySequence, tyPtr, tyRef, tyVar, tyLent:
+    let base = typ.last
+    if base == typ:
+      # this is not the correct location to detect ``type A = ptr A``
+      typ.size = szIllegalRecursion
+      typ.align = szIllegalRecursion
+      typ.paddingAtEnd = szIllegalRecursion
+      return
+    typ.align = int16(conf.target.ptrSize)
+    if typ.kind == tySequence and optSeqDestructors in conf.globalOptions:
+      typ.size = conf.target.ptrSize * 2
+    else:
+      typ.size = conf.target.ptrSize
+
+  of tyArray:
+    computeSizeAlign(conf, typ.elementType)
+    let elemSize = typ.elementType.size
+    let len = lengthOrd(conf, typ.indexType)
+    if elemSize < 0:
+      typ.size = elemSize
+      typ.align = int16(elemSize)
+    elif len < 0:
+      typ.size = szUnknownSize
+      typ.align = szUnknownSize
+    else:
+      typ.size = toInt64Checked(len * int32(elemSize), szTooBigSize)
+      typ.align = typ.elementType.align
+
+  of tyUncheckedArray:
+    let base = typ.last
+    computeSizeAlign(conf, base)
+    typ.size = 0
+    typ.align = base.align
+
+  of tyEnum:
+    if firstOrd(conf, typ) < Zero:
+      typ.size = 4              # use signed int32
+      typ.align = 4
+    else:
+      let lastOrd = toInt64(lastOrd(conf, typ))   # BUGFIX: use lastOrd!
+      if lastOrd < `shl`(1, 8):
+        typ.size = 1
+        typ.align = 1
+      elif lastOrd < `shl`(1, 16):
+        typ.size = 2
+        typ.align = 2
+      elif lastOrd < `shl`(BiggestInt(1), 32):
+        typ.size = 4
+        typ.align = 4
+      else:
+        typ.size = 8
+        typ.align = int16(conf.floatInt64Align)
+  of tySet:
+    if typ.elementType.kind == tyGenericParam:
+      typ.size = szUncomputedSize
+      typ.align = szUncomputedSize
+    else:
+      let length = toInt64(lengthOrd(conf, typ.elementType))
+      if length <= 8:
+        typ.size = 1
+        typ.align = 1
+      elif length <= 16:
+        typ.size = 2
+        typ.align = 2
+      elif length <= 32:
+        typ.size = 4
+        typ.align = 4
+      elif length <= 64:
+        typ.size = 8
+        typ.align = int16(conf.floatInt64Align)
+      elif align(length, 8) mod 8 == 0:
+        typ.size = align(length, 8) div 8
+        typ.align = 1
+      else:
+        typ.size = align(length, 8) div 8 + 1
+        typ.align = 1
+  of tyRange:
+    computeSizeAlign(conf, typ.elementType)
+    typ.size = typ.elementType.size
+    typ.align = typ.elementType.align
+    typ.paddingAtEnd = typ.elementType.paddingAtEnd
+
+  of tyTuple:
+    try:
+      var accum = OffsetAccum(maxAlign: 1)
+      for i, child in typ.ikids:
+        computeSizeAlign(conf, child)
+        accum.align(child.align)
+        if typ.n != nil: # is named tuple (has field symbols)?
+          let sym = typ.n[i].sym
+          sym.offset = accum.offset
+        accum.inc(int32(child.size))
+      typ.paddingAtEnd = int16(accum.finish())
+      typ.size = if accum.offset == 0: 1 else: accum.offset
+      typ.align = int16(accum.maxAlign)
+    except IllegalTypeRecursionError:
+      typ.paddingAtEnd = szIllegalRecursion
+      typ.size = szIllegalRecursion
+      typ.align = szIllegalRecursion
+
+  of tyObject:
+    try:
+      var accum =
+        if typ.baseClass != nil:
+          # compute header size
+          var st = typ.baseClass
+          while st.kind in skipPtrs:
+            st = st.skipModifier
+          computeSizeAlign(conf, st)
+          if conf.backend == backendCpp:
+            OffsetAccum(
+              offset: int32(st.size) - int32(st.paddingAtEnd),
+              maxAlign: st.align
+            )
+          else:
+            OffsetAccum(
+              offset: int32(st.size),
+              maxAlign: st.align
+            )
+        elif isObjectWithTypeFieldPredicate(typ):
+          # this branch is taken for RootObj
+          OffsetAccum(
+            offset: conf.target.intSize.int32,
+            maxAlign: conf.target.intSize.int32
+          )
+        else:
+          OffsetAccum(maxAlign: 1)
+      if tfUnion in typ.flags:
+        if accum.offset != 0:
+          let info = if typ.sym != nil: typ.sym.info else: unknownLineInfo
+          localError(conf, info, "union type may not have an object header")
+          accum = OffsetAccum(offset: szUnknownSize, maxAlign: szUnknownSize)
+        else:
+          computeUnionObjectOffsetsFoldFunction(conf, typ.n, tfPacked in typ.flags, accum)
+      elif tfPacked in typ.flags:
+        accum.maxAlign = 1
+        computeObjectOffsetsFoldFunction(conf, typ.n, true, accum)
+      else:
+        if typ.baseClass == nil and lacksMTypeField(typ) and typ.n.len == 1 and
+            typ.n[0].kind == nkSym and
+            typ.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
+          # a dummy field is generated for an object with a single field
+          # with an UncheckedArray type
+          assert accum.offset == 0
+          accum.offset = 1
+        computeObjectOffsetsFoldFunction(conf, typ.n, false, accum)
+      let paddingAtEnd = int16(accum.finish())
+      if typ.sym != nil and
+         typ.sym.flags * {sfCompilerProc, sfImportc} == {sfImportc} and
+         tfCompleteStruct notin typ.flags:
+        typ.size = szUnknownSize
+        typ.align = szUnknownSize
+        typ.paddingAtEnd = szUnknownSize
+      else:
+        typ.size = if accum.offset == 0: 1 else: accum.offset
+        typ.align = int16(accum.maxAlign)
+        typ.paddingAtEnd = paddingAtEnd
+    except IllegalTypeRecursionError:
+      typ.size = szIllegalRecursion
+      typ.align = szIllegalRecursion
+      typ.paddingAtEnd = szIllegalRecursion
+  of tyInferred:
+    if typ.hasElementType:
+      computeSizeAlign(conf, typ.last)
+      typ.size = typ.last.size
+      typ.align = typ.last.align
+      typ.paddingAtEnd = typ.last.paddingAtEnd
+
+  of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink, tyOwned:
+    computeSizeAlign(conf, typ.skipModifier)
+    typ.size = typ.skipModifier.size
+    typ.align = typ.skipModifier.align
+    typ.paddingAtEnd = typ.last.paddingAtEnd
+
+  of tyTypeClasses:
+    if typ.isResolvedUserTypeClass:
+      computeSizeAlign(conf, typ.last)
+      typ.size = typ.last.size
+      typ.align = typ.last.align
+      typ.paddingAtEnd = typ.last.paddingAtEnd
+    else:
+      typ.size = szUnknownSize
+      typ.align = szUnknownSize
+      typ.paddingAtEnd = szUnknownSize
+
+  of tyTypeDesc:
+    computeSizeAlign(conf, typ.base)
+    typ.size = typ.base.size
+    typ.align = typ.base.align
+    typ.paddingAtEnd = typ.base.paddingAtEnd
+
+  of tyForward:
+    typ.size = szUnknownSize
+    typ.align = szUnknownSize
+    typ.paddingAtEnd = szUnknownSize
+
+  of tyStatic:
+    if typ.n != nil:
+      computeSizeAlign(conf, typ.last)
+      typ.size = typ.last.size
+      typ.align = typ.last.align
+      typ.paddingAtEnd = typ.last.paddingAtEnd
+    else:
+      typ.size = szUnknownSize
+      typ.align = szUnknownSize
+      typ.paddingAtEnd = szUnknownSize
+  of tyInt, tyUInt:
+    setSize typ, conf.target.intSize.int16
+  of tyBool, tyChar, tyUInt8, tyInt8:
+    setSize typ, 1
+  of tyInt16, tyUInt16:
+    setSize typ, 2
+  of tyInt32, tyUInt32, tyFloat32:
+    setSize typ, 4
+  of tyInt64, tyUInt64, tyFloat64, tyFloat:
+    setSize typ, 8
+  else:
+    typ.size = szUnknownSize
+    typ.align = szUnknownSize
+    typ.paddingAtEnd = szUnknownSize
+
+template foldSizeOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
+  let config = conf
+  let node = n
+  let typ = node[1].typ
+  computeSizeAlign(config, typ)
+  let size = typ.size
+  if size >= 0:
+    let res = newIntNode(nkIntLit, size)
+    res.info = node.info
+    res.typ = node.typ
+    res
+  else:
+    fallback
+
+template foldAlignOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
+  let config = conf
+  let node = n
+  let typ = node[1].typ
+  computeSizeAlign(config, typ)
+  let align = typ.align
+  if align >= 0:
+    let res = newIntNode(nkIntLit, align)
+    res.info = node.info
+    res.typ = node.typ
+    res
+  else:
+    fallback
+
+template foldOffsetOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
+  ## Returns an int literal node of the given offsetof expression in `n`.
+  ## Falls back to `fallback`, if the `offsetof` expression can't be processed.
+  let config = conf
+  let node = n
+  var dotExpr: PNode
+  block findDotExpr:
+    if node[1].kind == nkDotExpr:
+      dotExpr = node[1]
+    elif node[1].kind == nkCheckedFieldExpr:
+      dotExpr = node[1][0]
+    else:
+      dotExpr = nil
+      localError(config, node.info, "can't compute offsetof on this ast")
+
+  assert dotExpr != nil
+  let value = dotExpr[0]
+  let member = dotExpr[1]
+  computeSizeAlign(config, value.typ)
+  let offset = member.sym.offset
+  if offset >= 0:
+    let tmp = newIntNode(nkIntLit, offset)
+    tmp.info = node.info
+    tmp.typ = node.typ
+    tmp
+  else:
+    fallback
diff --git a/compiler/sourcemap.nim b/compiler/sourcemap.nim
new file mode 100644
index 000000000..1395168cd
--- /dev/null
+++ b/compiler/sourcemap.nim
@@ -0,0 +1,206 @@
+import std/[strutils, strscans, parseutils, assertions]
+
+type
+  Segment = object
+    ## Segment refers to a block of something in the JS output.
+    ## This could be a token or an entire line
+    original: int # Column in the Nim source
+    generated: int # Column in the generated JS
+    name: int # Index into names list (-1 for no name)
+
+  Mapping = object
+    ## Mapping refers to a line in the JS output.
+    ## It is made up of segments which refer to the tokens in the line
+    case inSource: bool # Whether the line in JS has Nim equivalent
+    of true:
+      file: int # Index into files list
+      line: int # 0 indexed line of code in the Nim source
+      segments: seq[Segment]
+    else: discard
+
+  SourceInfo = object
+    mappings: seq[Mapping]
+    names, files: seq[string]
+
+  SourceMap* = object
+    version*:   int
+    sources*:   seq[string]
+    names*:     seq[string]
+    mappings*:  string
+    file*:      string
+
+func addSegment(info: var SourceInfo, original, generated: int, name: string = "") {.raises: [].} =
+  ## Adds a new segment into the current line
+  assert info.mappings.len > 0, "No lines have been added yet"
+  var segment = Segment(original: original, generated: generated, name: -1)
+  if name != "":
+    # Make name be index into names list
+    segment.name = info.names.find(name)
+    if segment.name == -1:
+      segment.name = info.names.len
+      info.names &= name
+
+  assert info.mappings[^1].inSource, "Current line isn't in Nim source"
+  info.mappings[^1].segments &= segment
+
+func newLine(info: var SourceInfo) {.raises: [].} =
+  ## Add new mapping which doesn't appear in the Nim source
+  info.mappings &= Mapping(inSource: false)
+
+func newLine(info: var SourceInfo, file: string, line: int) {.raises: [].} =
+  ## Starts a new line in the mappings. Call addSegment after this to add
+  ## segments into the line
+  var mapping = Mapping(inSource: true, line: line)
+  # Set file to file position. Add in if needed
+  mapping.file = info.files.find(file)
+  if mapping.file == -1:
+    mapping.file = info.files.len
+    info.files &= file
+  info.mappings &= mapping
+
+
+# base64_VLQ
+func encode*(values: seq[int]): string {.raises: [].} =
+  ## Encodes a series of integers into a VLQ base64 encoded string
+  # References:
+  #   - https://www.lucidchart.com/techblog/2019/08/22/decode-encoding-base64-vlqs-source-maps/
+  #   - https://github.com/rails/sprockets/blob/main/guides/source_maps.md#source-map-file
+  const
+    alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+    shift = 5
+    continueBit = 1 shl 5
+    mask = continueBit - 1
+  result = ""
+  for val in values:
+    # Sign is stored in first bit
+    var newVal = abs(val) shl 1
+    if val < 0:
+      newVal = newVal or 1
+    # Now comes the variable length part
+    # This is how we are able to store large numbers
+    while true:
+      # We only encode 5 bits.
+      var masked = newVal and mask
+      newVal = newVal shr shift
+      # If there is still something left
+      # then signify with the continue bit that the
+      # decoder should keep decoding
+      if newVal > 0:
+        masked = masked or continueBit
+      result &= alphabet[masked]
+      # If the value is zero then we have nothing left to encode
+      if newVal == 0:
+        break
+
+iterator tokenize*(line: string): (int, string) =
+  ## Goes through a line and splits it into Nim identifiers and
+  ## normal JS code. This allows us to map mangled names back to Nim names.
+  ## Yields (column, name). Doesn't yield anything but identifiers.
+  ## See mangleName in compiler/jsgen.nim for how name mangling is done
+  var
+    col = 0
+    token = ""
+  while col < line.len:
+    var
+      token: string = ""
+      name: string = ""
+    # First we find the next identifier
+    col += line.skipWhitespace(col)
+    col += line.skipUntil(IdentStartChars, col)
+    let identStart = col
+    col += line.parseIdent(token, col)
+    # Idents will either be originalName_randomInt or HEXhexCode_randomInt
+    if token.startsWith("HEX"):
+      var hex: int = 0
+      # 3 = "HEX".len and we only want to parse the two integers after it
+      discard token[3 ..< 5].parseHex(hex)
+      name = $chr(hex)
+    elif not token.endsWith("_Idx"): # Ignore address indexes
+      # It might be in the form originalName_randomInt
+      let lastUnderscore = token.rfind('_')
+      if lastUnderscore != -1:
+        name = token[0..<lastUnderscore]
+    if name != "":
+      yield (identStart, name)
+
+func parse*(source: string): SourceInfo =
+  ## Parses the JS output for embedded line info
+  ## So it can convert those into a series of mappings
+  result = default(SourceInfo)
+  var
+    skipFirstLine = true
+    currColumn = 0
+    currLine = 0
+    currFile = ""
+  # Add each line as a node into the output
+  for line in source.splitLines():
+    var
+      lineNumber: int = 0
+      linePath: string = ""
+      column: int = 0
+    if line.strip().scanf("/* line $i:$i \"$+\" */", lineNumber, column, linePath):
+      # When we reach the first line mappinsegmentg then we can assume
+      # we can map the rest of the JS lines to Nim lines
+      currColumn = column # Column is already zero indexed
+      currLine = lineNumber - 1
+      currFile = linePath
+      # Lines are zero indexed
+      result.newLine(currFile, currLine)
+      # Skip whitespace to find the starting column
+      result.addSegment(currColumn, line.skipWhitespace())
+    elif currFile != "":
+      result.newLine(currFile, currLine)
+      # There mightn't be any tokens so add a starting segment
+      result.addSegment(currColumn, line.skipWhitespace())
+      for jsColumn, token in line.tokenize:
+        result.addSegment(currColumn, jsColumn, token)
+    else:
+      result.newLine()
+
+func toSourceMap*(info: SourceInfo, file: string): SourceMap {.raises: [].} =
+  ## Convert from high level SourceInfo into the required SourceMap object
+  # Add basic info
+  result = SourceMap(version: 3, file: file, sources: info.files, names: info.names)
+  # Convert nodes into mappings.
+  # Mappings are split into blocks where each block referes to a line in the outputted JS.
+  # Blocks can be separated into statements which refere to tokens on the line.
+  # Since the mappings depend on previous values we need to
+  # keep track of previous file, name, etc
+  var
+    prevFile = 0
+    prevLine = 0
+    prevName = 0
+    prevNimCol = 0
+
+  for mapping in info.mappings:
+    # We know need to encode segments with the following fields
+    # All these fields are relative to their previous values
+    # - 0: Column in generated code
+    # - 1: Index of Nim file in source list
+    # - 2: Line in Nim source
+    # - 3: Column in Nim source
+    # - 4: Index in names list
+    if mapping.inSource:
+      # JS Column is special in that it is reset after every line
+      var prevJSCol = 0
+      for segment in mapping.segments:
+        var values = @[segment.generated - prevJSCol, mapping.file - prevFile, mapping.line - prevLine, segment.original - prevNimCol]
+        # Add name field if needed
+        if segment.name != -1:
+          values &= segment.name - prevName
+          prevName = segment.name
+        prevJSCol = segment.generated
+        prevNimCol = segment.original
+        prevFile = mapping.file
+        prevLine = mapping.line
+        result.mappings &= encode(values) & ","
+      # Remove trailing ,
+      if mapping.segments.len > 0:
+        result.mappings.setLen(result.mappings.len - 1)
+
+    result.mappings &= ";"
+
+proc genSourceMap*(source: string, outFile: string): SourceMap =
+  let node = parse(source)
+  result = node.toSourceMap(outFile)
+
diff --git a/compiler/spawn.nim b/compiler/spawn.nim
new file mode 100644
index 000000000..58d5a4928
--- /dev/null
+++ b/compiler/spawn.nim
@@ -0,0 +1,445 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements threadpool's ``spawn``.
+
+import ast, types, idents, magicsys, msgs, options, modulegraphs,
+  lowerings, liftdestructors, renderer
+from trees import getMagic, getRoot
+
+proc callProc(a: PNode): PNode =
+  result = newNodeI(nkCall, a.info)
+  result.add a
+  result.typ = a.typ.returnType
+
+# we have 4 cases to consider:
+# - a void proc --> nothing to do
+# - a proc returning GC'ed memory --> requires a flowVar
+# - a proc returning non GC'ed memory --> pass as hidden 'var' parameter
+# - not in a parallel environment --> requires a flowVar for memory safety
+type
+  TSpawnResult* = enum
+    srVoid, srFlowVar, srByVar
+  TFlowVarKind = enum
+    fvInvalid # invalid type T for 'FlowVar[T]'
+    fvGC      # FlowVar of a GC'ed type
+    fvBlob    # FlowVar of a blob type
+
+proc spawnResult*(t: PType; inParallel: bool): TSpawnResult =
+  if t.isEmptyType: srVoid
+  elif inParallel and not containsGarbageCollectedRef(t): srByVar
+  else: srFlowVar
+
+proc flowVarKind(c: ConfigRef, t: PType): TFlowVarKind =
+  if c.selectedGC in {gcArc, gcOrc, gcAtomicArc}: fvBlob
+  elif t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC
+  elif containsGarbageCollectedRef(t): fvInvalid
+  else: fvBlob
+
+proc typeNeedsNoDeepCopy(t: PType): bool =
+  var t = t.skipTypes(abstractInst)
+  # for the tconvexhull example (and others) we're a bit lax here and pretend
+  # seqs and strings are *by value* only and 'shallow' doesn't exist!
+  if t.kind == tyString: return true
+  # note that seq[T] is fine, but 'var seq[T]' is not, so we need to skip 'var'
+  # for the stricter check and likewise we can skip 'seq' for a less
+  # strict check:
+  if t.kind in {tyVar, tyLent, tySequence}: t = t.elementType
+  result = not containsGarbageCollectedRef(t)
+
+proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; idgen: IdGenerator; owner: PSym; typ: PType;
+                 v: PNode; useShallowCopy=false): PSym =
+  result = newSym(skTemp, getIdent(g.cache, genPrefix), idgen, owner, varSection.info,
+                  owner.options)
+  result.typ = typ
+  incl(result.flags, sfFromGeneric)
+
+  var vpart = newNodeI(nkIdentDefs, varSection.info, 3)
+  vpart[0] = newSymNode(result)
+  vpart[1] = newNodeI(nkEmpty, varSection.info)
+  vpart[2] = if varInit.isNil: v else: vpart[1]
+  varSection.add vpart
+  if varInit != nil:
+    if g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
+      # inject destructors pass will do its own analysis
+      varInit.add newFastMoveStmt(g, newSymNode(result), v)
+    else:
+      if useShallowCopy and typeNeedsNoDeepCopy(typ) or optTinyRtti in g.config.globalOptions:
+        varInit.add newFastMoveStmt(g, newSymNode(result), v)
+      else:
+        let deepCopyCall = newNodeI(nkCall, varInit.info, 3)
+        deepCopyCall[0] = newSymNode(getSysMagic(g, varSection.info, "deepCopy", mDeepCopy))
+        deepCopyCall[1] = newSymNode(result)
+        deepCopyCall[2] = v
+        varInit.add deepCopyCall
+
+discard """
+We generate roughly this:
+
+proc f_wrapper(thread, args) =
+  barrierEnter(args.barrier)  # for parallel statement
+  var a = args.a # thread transfer; deepCopy or shallowCopy or no copy
+                 # depending on whether we're in a 'parallel' statement
+  var b = args.b
+  var fv = args.fv
+
+  fv.owner = thread # optional
+  nimArgsPassingDone() # signal parent that the work is done
+  #
+  args.fv.blob = f(a, b, ...)
+  nimFlowVarSignal(args.fv)
+
+  # - or -
+  f(a, b, ...)
+  barrierLeave(args.barrier)  # for parallel statement
+
+stmtList:
+  var scratchObj
+  scratchObj.a = a
+  scratchObj.b = b
+
+  nimSpawn(f_wrapper, addr scratchObj)
+  scratchObj.fv # optional
+
+"""
+
+proc castToVoidPointer(g: ModuleGraph, n: PNode, fvField: PNode): PNode =
+  if g.config.backend == backendCpp:
+    result = fvField
+  else:
+    let ptrType = getSysType(g, n.info, tyPointer)
+    result = newNodeI(nkCast, fvField.info)
+    result.add newNodeI(nkEmpty, fvField.info)
+    result.add fvField
+    result.typ = ptrType
+
+proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
+                       varSection, varInit, call, barrier, fv: PNode;
+                       idgen: IdGenerator;
+                       spawnKind: TSpawnResult, result: PSym) =
+  var body = newNodeI(nkStmtList, f.info)
+  var threadLocalBarrier: PSym = nil
+  if barrier != nil:
+    var varSection2 = newNodeI(nkVarSection, barrier.info)
+    threadLocalBarrier = addLocalVar(g, varSection2, nil, idgen, result,
+                                     barrier.typ, barrier)
+    body.add varSection2
+    body.add callCodegenProc(g, "barrierEnter", threadLocalBarrier.info,
+      threadLocalBarrier.newSymNode)
+  var threadLocalProm: PSym = nil
+  if spawnKind == srByVar:
+    threadLocalProm = addLocalVar(g, varSection, nil, idgen, result, fv.typ, fv)
+  elif fv != nil:
+    internalAssert g.config, fv.typ.kind == tyGenericInst
+    threadLocalProm = addLocalVar(g, varSection, nil, idgen, result, fv.typ, fv)
+  body.add varSection
+  body.add varInit
+  if fv != nil and spawnKind != srByVar:
+    # generate:
+    #   fv.owner = threadParam
+    body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
+      "owner", fv.info, g.cache), threadParam.newSymNode)
+
+  body.add callCodegenProc(g, "nimArgsPassingDone", threadParam.info,
+    threadParam.newSymNode)
+  if spawnKind == srByVar:
+    body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call)
+  elif fv != nil:
+    let fk = flowVarKind(g.config, fv.typ.firstGenericParam)
+    if fk == fvInvalid:
+      localError(g.config, f.info, "cannot create a flowVar of type: " &
+        typeToString(fv.typ.firstGenericParam))
+    body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
+      if fk == fvGC: "data" else: "blob", fv.info, g.cache), call)
+    if fk == fvGC:
+      let incRefCall = newNodeI(nkCall, fv.info, 2)
+      incRefCall[0] = newSymNode(getSysMagic(g, fv.info, "GCref", mGCref))
+      incRefCall[1] = indirectAccess(threadLocalProm.newSymNode,
+                                          "data", fv.info, g.cache)
+      body.add incRefCall
+    if barrier == nil:
+      # by now 'fv' is shared and thus might have beeen overwritten! we need
+      # to use the thread-local view instead:
+      let castExpr = castToVoidPointer(g, f, threadLocalProm.newSymNode)
+      body.add callCodegenProc(g, "nimFlowVarSignal", threadLocalProm.info,
+        castExpr)
+  else:
+    body.add call
+  if barrier != nil:
+    body.add callCodegenProc(g, "barrierLeave", threadLocalBarrier.info,
+      threadLocalBarrier.newSymNode)
+
+  var params = newNodeI(nkFormalParams, f.info)
+  params.add newNodeI(nkEmpty, f.info)
+  params.add threadParam.newSymNode
+  params.add argsParam.newSymNode
+
+  var t = newType(tyProc, idgen, threadParam.owner)
+  t.rawAddSon nil
+  t.rawAddSon threadParam.typ
+  t.rawAddSon argsParam.typ
+  t.n = newNodeI(nkFormalParams, f.info)
+  t.n.add newNodeI(nkEffectList, f.info)
+  t.n.add threadParam.newSymNode
+  t.n.add argsParam.newSymNode
+
+  let emptyNode = newNodeI(nkEmpty, f.info)
+  result.ast = newProcNode(nkProcDef, f.info, body = body,
+      params = params, name = newSymNode(result), pattern = emptyNode,
+      genericParams = emptyNode, pragmas = emptyNode,
+      exceptions = emptyNode)
+  result.typ = t
+
+proc createCastExpr(argsParam: PSym; objType: PType; idgen: IdGenerator): PNode =
+  result = newNodeI(nkCast, argsParam.info)
+  result.add newNodeI(nkEmpty, argsParam.info)
+  result.add newSymNode(argsParam)
+  result.typ = newType(tyPtr, idgen, objType.owner)
+  result.typ.rawAddSon(objType)
+
+template checkMagicProcs(g: ModuleGraph, n: PNode, formal: PNode) =
+  if (formal.typ.kind == tyVarargs and formal.typ.elementType.kind in {tyTyped, tyUntyped}) or
+          formal.typ.kind in {tyTyped, tyUntyped}:
+    localError(g.config, n.info, "'spawn'ed function cannot have a 'typed' or 'untyped' parameter")
+
+proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType;
+                             idgen: IdGenerator; owner: PSym; scratchObj: PSym,
+                             castExpr, call,
+                             varSection, varInit, result: PNode) =
+  let formals = n[0].typ.n
+  let tmpName = getIdent(g.cache, genPrefix)
+  for i in 1..<n.len:
+    # we pick n's type here, which hopefully is 'tyArray' and not
+    # 'tyOpenArray':
+    var argType = n[i].typ.skipTypes(abstractInst)
+    if i < formals.len:
+      if formals[i].typ.kind in {tyVar, tyLent}:
+        localError(g.config, n[i].info, "'spawn'ed function cannot have a 'var' parameter")
+
+      checkMagicProcs(g, n[i], formals[i])
+
+      if formals[i].typ.kind in {tyTypeDesc, tyStatic}:
+        continue
+    #elif containsTyRef(argType):
+    #  localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")
+
+    let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
+    var field = newSym(skField, fieldname, idgen, objType.owner, n.info, g.config.options)
+    field.typ = argType
+    discard objType.addField(field, g.cache, idgen)
+    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
+
+    let temp = addLocalVar(g, varSection, varInit, idgen, owner, argType,
+                           indirectAccess(castExpr, field, n.info))
+    call.add(newSymNode(temp))
+
+proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType;
+                             idgen: IdGenerator;
+                             owner: PSym; scratchObj: PSym;
+                             castExpr, call,
+                             varSection, varInit, result: PNode) =
+  let formals = n[0].typ.n
+  let tmpName = getIdent(g.cache, genPrefix)
+  # we need to copy the foreign scratch object fields into local variables
+  # for correctness: These are called 'threadLocal' here.
+  for i in 1..<n.len:
+    let n = n[i]
+    if i < formals.len and formals[i].typ.kind in {tyStatic, tyTypeDesc}:
+      continue
+
+    checkMagicProcs(g, n, formals[i])
+
+    let argType = skipTypes(if i < formals.len: formals[i].typ else: n.typ,
+                            abstractInst)
+    #if containsTyRef(argType):
+    #  localError(n.info, "'spawn'ed function cannot refer to 'ref'/closure")
+
+    let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
+    var field = newSym(skField, fieldname, idgen, objType.owner, n.info, g.config.options)
+
+    if argType.kind in {tyVarargs, tyOpenArray}:
+      # important special case: we always create a zero-copy slice:
+      let slice = newNodeI(nkCall, n.info, 4)
+      slice.typ = n.typ
+      slice[0] = newSymNode(createMagic(g, idgen, "slice", mSlice))
+      slice[0].typ = getSysType(g, n.info, tyInt) # fake type
+      var fieldB = newSym(skField, tmpName, idgen, objType.owner, n.info, g.config.options)
+      fieldB.typ = getSysType(g, n.info, tyInt)
+      discard objType.addField(fieldB, g.cache, idgen)
+
+      if getMagic(n) == mSlice:
+        let a = genAddrOf(n[1], idgen)
+        field.typ = a.typ
+        discard objType.addField(field, g.cache, idgen)
+        result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
+
+        var fieldA = newSym(skField, tmpName, idgen, objType.owner, n.info, g.config.options)
+        fieldA.typ = getSysType(g, n.info, tyInt)
+        discard objType.addField(fieldA, g.cache, idgen)
+        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2])
+        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3])
+
+        let threadLocal = addLocalVar(g, varSection, nil, idgen, owner, fieldA.typ,
+                                      indirectAccess(castExpr, fieldA, n.info),
+                                      useShallowCopy=true)
+        slice[2] = threadLocal.newSymNode
+      else:
+        let a = genAddrOf(n, idgen)
+        field.typ = a.typ
+        discard objType.addField(field, g.cache, idgen)
+        result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
+        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(g, n))
+
+        slice[2] = newIntLit(g, n.info, 0)
+      # the array itself does not need to go through a thread local variable:
+      slice[1] = genDeref(indirectAccess(castExpr, field, n.info))
+
+      let threadLocal = addLocalVar(g, varSection, nil, idgen, owner, fieldB.typ,
+                                    indirectAccess(castExpr, fieldB, n.info),
+                                    useShallowCopy=true)
+      slice[3] = threadLocal.newSymNode
+      call.add slice
+    elif (let size = computeSize(g.config, argType); size < 0 or size > 16) and
+        n.getRoot != nil:
+      # it is more efficient to pass a pointer instead:
+      let a = genAddrOf(n, idgen)
+      field.typ = a.typ
+      discard objType.addField(field, g.cache, idgen)
+      result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
+      let threadLocal = addLocalVar(g, varSection, nil, idgen, owner, field.typ,
+                                    indirectAccess(castExpr, field, n.info),
+                                    useShallowCopy=true)
+      call.add(genDeref(threadLocal.newSymNode))
+    else:
+      # boring case
+      field.typ = argType
+      discard objType.addField(field, g.cache, idgen)
+      result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n)
+      let threadLocal = addLocalVar(g, varSection, varInit,
+                                    idgen, owner, field.typ,
+                                    indirectAccess(castExpr, field, n.info),
+                                    useShallowCopy=true)
+      call.add(threadLocal.newSymNode)
+
+proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExpr: PNode; retType: PType;
+                       barrier: PNode = nil, dest: PNode = nil): PNode =
+  # if 'barrier' != nil, then it is in a 'parallel' section and we
+  # generate quite different code
+  let n = spawnExpr[^2]
+  let spawnKind = spawnResult(retType, barrier!=nil)
+  case spawnKind
+  of srVoid:
+    internalAssert g.config, dest == nil
+    result = newNodeI(nkStmtList, n.info)
+  of srFlowVar:
+    internalAssert g.config, dest == nil
+    result = newNodeIT(nkStmtListExpr, n.info, retType)
+  of srByVar:
+    if dest == nil: localError(g.config, n.info, "'spawn' must not be discarded")
+    result = newNodeI(nkStmtList, n.info)
+
+  if n.kind notin nkCallKinds:
+    localError(g.config, n.info, "'spawn' takes a call expression; got: " & $n)
+    return
+  if optThreadAnalysis in g.config.globalOptions:
+    if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
+      localError(g.config, n.info, "'spawn' takes a GC safe call expression")
+
+  var fn = n[0]
+  let
+    name = (if fn.kind == nkSym: fn.sym.name.s else: genPrefix) & "Wrapper"
+    wrapperProc = newSym(skProc, getIdent(g.cache, name), idgen, owner, fn.info, g.config.options)
+    threadParam = newSym(skParam, getIdent(g.cache, "thread"), idgen, wrapperProc, n.info, g.config.options)
+    argsParam = newSym(skParam, getIdent(g.cache, "args"), idgen, wrapperProc, n.info, g.config.options)
+
+  wrapperProc.flags.incl sfInjectDestructors
+  block:
+    let ptrType = getSysType(g, n.info, tyPointer)
+    threadParam.typ = ptrType
+    argsParam.typ = ptrType
+    argsParam.position = 1
+
+  var objType = createObj(g, idgen, owner, n.info)
+  incl(objType.flags, tfFinal)
+  let castExpr = createCastExpr(argsParam, objType, idgen)
+
+  var scratchObj = newSym(skVar, getIdent(g.cache, "scratch"), idgen, owner, n.info, g.config.options)
+  block:
+    scratchObj.typ = objType
+    incl(scratchObj.flags, sfFromGeneric)
+    var varSectionB = newNodeI(nkVarSection, n.info)
+    varSectionB.addVar(scratchObj.newSymNode)
+    result.add varSectionB
+
+  var call = newNodeIT(nkCall, n.info, n.typ)
+  # templates and macros are in fact valid here due to the nature of
+  # the transformation:
+  if fn.kind == nkClosure or (fn.typ != nil and fn.typ.callConv == ccClosure):
+    localError(g.config, n.info, "closure in spawn environment is not allowed")
+  if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro,
+                                               skFunc, skMethod, skConverter}):
+    # for indirect calls we pass the function pointer in the scratchObj
+    var argType = n[0].typ.skipTypes(abstractInst)
+    var field = newSym(skField, getIdent(g.cache, "fn"), idgen, owner, n.info, g.config.options)
+    field.typ = argType
+    discard objType.addField(field, g.cache, idgen)
+    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
+    fn = indirectAccess(castExpr, field, n.info)
+  elif fn.kind == nkSym and fn.sym.kind == skIterator:
+    localError(g.config, n.info, "iterator in spawn environment is not allowed")
+
+  call.add(fn)
+  var varSection = newNodeI(nkVarSection, n.info)
+  var varInit = newNodeI(nkStmtList, n.info)
+  if barrier.isNil:
+    setupArgsForConcurrency(g, n, objType, idgen, wrapperProc, scratchObj, castExpr, call,
+                            varSection, varInit, result)
+  else:
+    setupArgsForParallelism(g, n, objType, idgen, wrapperProc, scratchObj, castExpr, call,
+                            varSection, varInit, result)
+
+  var barrierAsExpr: PNode = nil
+  if barrier != nil:
+    let typ = newType(tyPtr, idgen, owner)
+    typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ)
+    var field = newSym(skField, getIdent(g.cache, "barrier"), idgen, owner, n.info, g.config.options)
+    field.typ = typ
+    discard objType.addField(field, g.cache, idgen)
+    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier)
+    barrierAsExpr = indirectAccess(castExpr, field, n.info)
+
+  var fvField, fvAsExpr: PNode = nil
+  if spawnKind == srFlowVar:
+    var field = newSym(skField, getIdent(g.cache, "fv"), idgen, owner, n.info, g.config.options)
+    field.typ = retType
+    discard objType.addField(field, g.cache, idgen)
+    fvField = newDotExpr(scratchObj, field)
+    fvAsExpr = indirectAccess(castExpr, field, n.info)
+    # create flowVar:
+    result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))
+    if barrier == nil:
+      let castExpr = castToVoidPointer(g, n, fvField)
+      result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info, castExpr)
+
+  elif spawnKind == srByVar:
+    var field = newSym(skField, getIdent(g.cache, "fv"), idgen, owner, n.info, g.config.options)
+    field.typ = newType(tyPtr, idgen, objType.owner)
+    field.typ.rawAddSon(retType)
+    discard objType.addField(field, g.cache, idgen)
+    fvAsExpr = indirectAccess(castExpr, field, n.info)
+    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest, idgen))
+
+  createTypeBoundOps(g, nil, objType, n.info, idgen)
+  createWrapperProc(g, fn, threadParam, argsParam,
+                      varSection, varInit, call,
+                      barrierAsExpr, fvAsExpr, idgen, spawnKind, wrapperProc)
+  result.add callCodegenProc(g, "nimSpawn" & $spawnExpr.len, wrapperProc.info,
+    wrapperProc.newSymNode, genAddrOf(scratchObj.newSymNode, idgen), nil, spawnExpr)
+
+  if spawnKind == srFlowVar: result.add fvField
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index a21d64338..a5213086b 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -32,11 +32,13 @@
 
 # included from sigmatch.nim
 
-import algorithm, prefixmatches, lineinfos
-from wordrecg import wDeprecated
+import prefixmatches, suggestsymdb
+from wordrecg import wDeprecated, wError, wAddr, wYield
+
+import std/[algorithm, sets, parseutils, tables]
 
 when defined(nimsuggest):
-  import passes, tables # importer
+  import pathutils # importer
 
 const
   sep = '\t'
@@ -47,19 +49,21 @@ template origModuleName(m: PSym): string = m.name.s
 
 proc findDocComment(n: PNode): PNode =
   if n == nil: return nil
-  if not isNil(n.comment): return n
+  if n.comment.len > 0: return n
   if n.kind in {nkStmtList, nkStmtListExpr, nkObjectTy, nkRecList} and n.len > 0:
-    result = findDocComment(n.sons[0])
+    result = findDocComment(n[0])
     if result != nil: return
     if n.len > 1:
-      result = findDocComment(n.sons[1])
-  elif n.kind in {nkAsgn, nkFastAsgn} and n.len == 2:
-    result = findDocComment(n.sons[1])
+      result = findDocComment(n[1])
+  elif n.kind in {nkAsgn, nkFastAsgn, nkSinkAsgn} and n.len == 2:
+    result = findDocComment(n[1])
+  else:
+    result = nil
 
-proc extractDocComment(s: PSym): string =
+proc extractDocComment(g: ModuleGraph; s: PSym): string =
   var n = findDocComment(s.ast)
   if n.isNil and s.kind in routineKinds and s.ast != nil:
-    n = findDocComment(s.ast[bodyPos])
+    n = findDocComment(getBody(g, s))
   if not n.isNil:
     result = n.comment.replace("\n##", "\n").strip
   else:
@@ -70,25 +74,78 @@ proc cmpSuggestions(a, b: Suggest): int =
     result = b.field.int - a.field.int
     if result != 0: return result
 
-  cf scope
   cf prefix
+  cf contextFits
+  cf scope
   # when the first type matches, it's better when it's a generic match:
   cf quality
-  cf contextFits
   cf localUsages
   cf globalUsages
   # if all is equal, sort alphabetically for deterministic output,
   # independent of hashing order:
   result = cmp(a.name[], b.name[])
 
-proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo;
+proc scanForTrailingAsterisk(line: string, start: int): int =
+  result = 0
+  while start+result < line.len and line[start+result] in {' ', '\t'}:
+    inc result
+  if start+result < line.len and line[start+result] == '*':
+    inc result
+  else:
+    result = 0
+
+proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo; skipTrailingAsterisk: bool = false): int =
+  let
+    line = sourceLine(conf, info)
+    column = toColumn(info)
+
+  proc isOpeningBacktick(col: int): bool =
+    if col >= 0 and col < line.len:
+      if line[col] == '`':
+        not isOpeningBacktick(col - 1)
+      else:
+        isOpeningBacktick(col - 1)
+    else:
+      false
+
+  if column > line.len:
+    result = 0
+  elif column > 0 and line[column - 1] == '`' and isOpeningBacktick(column - 1):
+    result = skipUntil(line, '`', column)
+    if cmpIgnoreStyle(line[column..column + result - 1], ident) != 0:
+      result = 0
+  elif column >= 0 and line[column] == '`' and isOpeningBacktick(column):
+    result = skipUntil(line, '`', column + 1) + 2
+    if cmpIgnoreStyle(line[column + 1..column + result - 2], ident) != 0:
+      result = 0
+  elif ident[0] in linter.Letters and ident[^1] != '=':
+    result = identLen(line, column)
+    if cmpIgnoreStyle(line[column..column + result - 1], ident[0..min(result-1,len(ident)-1)]) != 0:
+      result = 0
+    if skipTrailingAsterisk and result > 0:
+      result += scanForTrailingAsterisk(line, column + result)
+  else:
+    var sourceIdent: string = ""
+    result = parseWhile(line, sourceIdent,
+                        OpChars + {'[', '(', '{', ']', ')', '}'}, column)
+    if ident[^1] == '=' and ident[0] in linter.Letters:
+      if sourceIdent != "=":
+        result = 0
+    elif sourceIdent.len > ident.len and sourceIdent[0..ident.high] == ident:
+      result = ident.len
+    elif sourceIdent != ident:
+      result = 0
+
+proc symToSuggest*(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo;
                   quality: range[0..100]; prefix: PrefixMatch;
-                  inTypeContext: bool; scope: int): Suggest =
+                  inTypeContext: bool; scope: int;
+                  useSuppliedInfo = false,
+                  endLine: uint16 = 0,
+                  endCol = 0, extractDocs = true): Suggest =
   new(result)
   result.section = section
   result.quality = quality
   result.isGlobal = sfGlobal in s.flags
-  result.tokenLen = s.name.s.len
   result.prefix = prefix
   result.contextFits = inTypeContext == (s.kind in {skType, skGenericParam})
   result.scope = scope
@@ -100,7 +157,7 @@ proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info
       if u.fileIndex == info.fileIndex: inc c
     result.localUsages = c
   result.symkind = byte s.kind
-  if optIdeTerse notin conf.globalOptions:
+  if optIdeTerse notin g.config.globalOptions:
     result.qualifiedPath = @[]
     if not isLocal and s.kind != skModule:
       let ow = s.owner
@@ -109,60 +166,146 @@ proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info
         result.qualifiedPath.add(ow2.origModuleName)
       if ow != nil:
         result.qualifiedPath.add(ow.origModuleName)
-    result.qualifiedPath.add(s.name.s)
+    if s.name.s[0] in OpChars + {'[', '{', '('} or
+       s.name.id in ord(wAddr)..ord(wYield):
+      result.qualifiedPath.add('`' & s.name.s & '`')
+    else:
+      result.qualifiedPath.add(s.name.s)
 
     if s.typ != nil:
-      result.forth = typeToString(s.typ)
+      if section == ideInlayHints:
+        result.forth = typeToString(s.typ, preferInlayHint)
+      else:
+        result.forth = typeToString(s.typ, preferInferredEffects)
     else:
       result.forth = ""
-    when defined(nimsuggest) and not defined(noDocgen):
-      result.doc = s.extractDocComment
-  let infox = if section in {ideUse, ideHighlight, ideOutline}: info else: s.info
-  result.filePath = toFullPath(conf, infox)
-  result.line = toLinenumber(infox)
-  result.column = toColumn(infox)
-  result.version = conf.suggestVersion
+    when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler):
+      if extractDocs:
+        result.doc = extractDocComment(g, s)
+  if s.kind == skModule and s.ast.len != 0 and section != ideHighlight:
+    result.filePath = toFullPath(g.config, s.ast[0].info)
+    result.line = 1
+    result.column = 0
+    result.tokenLen = 0
+  else:
+    let infox =
+      if useSuppliedInfo or section in {ideUse, ideHighlight, ideOutline, ideDeclaration}:
+        info
+      else:
+        s.info
+    result.filePath = toFullPath(g.config, infox)
+    result.line = toLinenumber(infox)
+    result.column = toColumn(infox)
+    result.tokenLen = if section notin {ideHighlight, ideInlayHints}:
+                        s.name.s.len
+                      else:
+                        getTokenLenFromSource(g.config, s.name.s, infox, section == ideInlayHints)
+  result.version = g.config.suggestVersion
+  result.endLine = endLine
+  result.endCol = endCol
+
+proc `$`*(suggest: SuggestInlayHint): string =
+  result = $suggest.kind
+  result.add(sep)
+  result.add($suggest.line)
+  result.add(sep)
+  result.add($suggest.column)
+  result.add(sep)
+  result.add(suggest.label)
+  result.add(sep)
+  result.add($suggest.paddingLeft)
+  result.add(sep)
+  result.add($suggest.paddingRight)
+  result.add(sep)
+  result.add($suggest.allowInsert)
+  result.add(sep)
+  result.add(suggest.tooltip)
 
 proc `$`*(suggest: Suggest): string =
-  result = $suggest.section
-  result.add(sep)
-  if suggest.section == ideHighlight:
-    if suggest.symkind.TSymKind == skVar and suggest.isGlobal:
-      result.add("skGlobalVar")
-    elif suggest.symkind.TSymKind == skLet and suggest.isGlobal:
-      result.add("skGlobalLet")
-    else:
-      result.add($suggest.symkind.TSymKind)
-    result.add(sep)
-    result.add($suggest.line)
-    result.add(sep)
-    result.add($suggest.column)
-    result.add(sep)
-    result.add($suggest.tokenLen)
+  if suggest.section == ideInlayHints:
+    result = $suggest.inlayHintInfo
   else:
-    result.add($suggest.symkind.TSymKind)
-    result.add(sep)
-    if suggest.qualifiedPath.len != 0:
-      result.add(suggest.qualifiedPath.join("."))
-    result.add(sep)
-    result.add(suggest.forth)
+    result = $suggest.section
     result.add(sep)
-    result.add(suggest.filePath)
-    result.add(sep)
-    result.add($suggest.line)
-    result.add(sep)
-    result.add($suggest.column)
-    result.add(sep)
-    when defined(nimsuggest) and not defined(noDocgen):
-      result.add(suggest.doc.escape)
-    if suggest.version == 0:
+    if suggest.section == ideHighlight:
+      if suggest.symkind.TSymKind == skVar and suggest.isGlobal:
+        result.add("skGlobalVar")
+      elif suggest.symkind.TSymKind == skLet and suggest.isGlobal:
+        result.add("skGlobalLet")
+      else:
+        result.add($suggest.symkind.TSymKind)
+      result.add(sep)
+      result.add($suggest.line)
+      result.add(sep)
+      result.add($suggest.column)
       result.add(sep)
-      result.add($suggest.quality)
-      if suggest.section == ideSug:
+      result.add($suggest.tokenLen)
+    else:
+      result.add($suggest.symkind.TSymKind)
+      result.add(sep)
+      if suggest.qualifiedPath.len != 0:
+        result.add(suggest.qualifiedPath.join("."))
+      result.add(sep)
+      result.add(suggest.forth)
+      result.add(sep)
+      result.add(suggest.filePath)
+      result.add(sep)
+      result.add($suggest.line)
+      result.add(sep)
+      result.add($suggest.column)
+      result.add(sep)
+      when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler):
+        result.add(suggest.doc.escape)
+      if suggest.version == 0 or suggest.version == 3:
         result.add(sep)
-        result.add($suggest.prefix)
+        result.add($suggest.quality)
+        if suggest.section == ideSug:
+          result.add(sep)
+          result.add($suggest.prefix)
 
-proc suggestResult(conf: ConfigRef; s: Suggest) =
+    if (suggest.version == 3 and suggest.section in {ideOutline, ideExpand}):
+      result.add(sep)
+      result.add($suggest.endLine)
+      result.add(sep)
+      result.add($suggest.endCol)
+
+proc suggestToSuggestInlayTypeHint*(sug: Suggest): SuggestInlayHint =
+  SuggestInlayHint(
+    kind: sihkType,
+    line: sug.line,
+    column: sug.column + sug.tokenLen,
+    label: ": " & sug.forth,
+    paddingLeft: false,
+    paddingRight: false,
+    allowInsert: true,
+    tooltip: ""
+  )
+
+proc suggestToSuggestInlayExceptionHintLeft*(sug: Suggest, propagatedExceptions: seq[PType]): SuggestInlayHint =
+  SuggestInlayHint(
+    kind: sihkException,
+    line: sug.line,
+    column: sug.column,
+    label: "try ",
+    paddingLeft: false,
+    paddingRight: false,
+    allowInsert: false,
+    tooltip: "propagated exceptions: " & $propagatedExceptions
+  )
+
+proc suggestToSuggestInlayExceptionHintRight*(sug: Suggest, propagatedExceptions: seq[PType]): SuggestInlayHint =
+  SuggestInlayHint(
+    kind: sihkException,
+    line: sug.line,
+    column: sug.column + sug.tokenLen,
+    label: "!",
+    paddingLeft: false,
+    paddingRight: false,
+    allowInsert: false,
+    tooltip: "propagated exceptions: " & $propagatedExceptions
+  )
+
+proc suggestResult*(conf: ConfigRef; s: Suggest) =
   if not isNil(conf.suggestionResultHook):
     conf.suggestionResultHook(s)
   else:
@@ -190,13 +333,17 @@ proc filterSym(s: PSym; prefix: PNode; res: var PrefixMatch): bool {.inline.} =
     of nkOpenSymChoice, nkClosedSymChoice, nkAccQuoted:
       if n.len > 0:
         result = prefixMatch(s, n[0])
-    else: discard
+      else:
+        result = default(PrefixMatch)
+    else: result = default(PrefixMatch)
   if s.kind != skModule:
     if prefix != nil:
       res = prefixMatch(s, prefix)
       result = res != PrefixMatch.None
     else:
       result = true
+  else:
+    result = false
 
 proc filterSymNoOpr(s: PSym; prefix: PNode; res: var PrefixMatch): bool {.inline.} =
   result = filterSym(s, prefix, res) and s.name.s[0] in lexer.SymChars and
@@ -205,57 +352,63 @@ proc filterSymNoOpr(s: PSym; prefix: PNode; res: var PrefixMatch): bool {.inline
 proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
   let fmoduleId = getModule(f).id
   result = sfExported in f.flags or fmoduleId == c.module.id
-  for module in c.friendModules:
-    if fmoduleId == module.id:
-      result = true
-      break
 
-proc suggestField(c: PContext, s: PSym; f: PNode; info: TLineInfo; outputs: var Suggestions) =
-  var pm: PrefixMatch
-  if filterSym(s, f, pm) and fieldVisible(c, s):
-    outputs.add(symToSuggest(c.config, s, isLocal=true, ideSug, info, 100, pm, c.inTypeContext > 0, 0))
+  if not result:
+    for module in c.friendModules:
+      if fmoduleId == module.id: return true
+    if f.kind == skField:
+      var symObj = f.owner.typ.toObjectFromRefPtrGeneric.sym
+      assert symObj != nil
+      for scope in allScopes(c.currentScope):
+        for sym in scope.allowPrivateAccess:
+          if symObj.id == sym.id: return true
 
 proc getQuality(s: PSym): range[0..100] =
-  if s.typ != nil and s.typ.len > 1:
-    var exp = s.typ.sons[1].skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
+  result = 100
+  if s.typ != nil and s.typ.paramsLen > 0:
+    var exp = s.typ.firstParamType.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
     if exp.kind == tyVarargs: exp = elemType(exp)
-    if exp.kind in {tyExpr, tyStmt, tyGenericParam, tyAnything}: return 50
-  return 100
-
-template wholeSymTab(cond, section: untyped) =
-  var isLocal = true
-  var scopeN = 0
-  for scope in walkScopes(c.currentScope):
-    if scope == c.topLevelScope: isLocal = false
-    dec scopeN
-    for item in scope.symbols:
-      let it {.inject.} = item
-      var pm {.inject.}: PrefixMatch
-      if cond:
-        outputs.add(symToSuggest(c.config, it, isLocal = isLocal, section, info, getQuality(it),
-                                 pm, c.inTypeContext > 0, scopeN))
+    if exp.kind in {tyUntyped, tyTyped, tyGenericParam, tyAnything}: result = 50
+
+  # penalize deprecated symbols
+  if sfDeprecated in s.flags:
+    result = result - 5
+
+proc suggestField(c: PContext, s: PSym; f: PNode; info: TLineInfo; outputs: var Suggestions) =
+  var pm: PrefixMatch = default(PrefixMatch)
+  if filterSym(s, f, pm) and fieldVisible(c, s):
+    outputs.add(symToSuggest(c.graph, s, isLocal=true, ideSug, info,
+                              s.getQuality, pm, c.inTypeContext > 0, 0))
+
+template wholeSymTab(cond, section: untyped) {.dirty.} =
+  for (item, scopeN, isLocal) in uniqueSyms(c):
+    let it = item
+    var pm: PrefixMatch = default(PrefixMatch)
+    if cond:
+      outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, section, info, getQuality(it),
+                                pm, c.inTypeContext > 0, scopeN))
 
 proc suggestSymList(c: PContext, list, f: PNode; info: TLineInfo, outputs: var Suggestions) =
-  for i in countup(0, sonsLen(list) - 1):
-    if list.sons[i].kind == nkSym:
-      suggestField(c, list.sons[i].sym, f, info, outputs)
+  for i in 0..<list.len:
+    if list[i].kind == nkSym:
+      suggestField(c, list[i].sym, f, info, outputs)
     #else: InternalError(list.info, "getSymFromList")
 
 proc suggestObject(c: PContext, n, f: PNode; info: TLineInfo, outputs: var Suggestions) =
   case n.kind
   of nkRecList:
-    for i in countup(0, sonsLen(n)-1): suggestObject(c, n.sons[i], f, info, outputs)
+    for i in 0..<n.len: suggestObject(c, n[i], f, info, outputs)
   of nkRecCase:
-    var L = sonsLen(n)
-    if L > 0:
-      suggestObject(c, n.sons[0], f, info, outputs)
-      for i in countup(1, L-1): suggestObject(c, lastSon(n.sons[i]), f, info, outputs)
+    if n.len > 0:
+      suggestObject(c, n[0], f, info, outputs)
+      for i in 1..<n.len: suggestObject(c, lastSon(n[i]), f, info, outputs)
   of nkSym: suggestField(c, n.sym, f, info, outputs)
   else: discard
 
 proc nameFits(c: PContext, s: PSym, n: PNode): bool =
-  var op = n.sons[0]
-  if op.kind in {nkOpenSymChoice, nkClosedSymChoice}: op = op.sons[0]
+  var op = if n.kind in nkCallKinds: n[0] else: n
+  if op.kind in {nkOpenSymChoice, nkClosedSymChoice}: op = op[0]
+  if op.kind == nkDotExpr: op = op[1]
   var opr: PIdent
   case op.kind
   of nkSym: opr = op.sym.name
@@ -266,8 +419,7 @@ proc nameFits(c: PContext, s: PSym, n: PNode): bool =
 proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool =
   case candidate.kind
   of OverloadableSyms:
-    var m: TCandidate
-    initCandidate(c, m, candidate, nil)
+    var m = newCandidate(c, candidate, nil)
     sigmatch.partialMatch(c, n, nOrig, m)
     result = m.state != csNoMatch
   else:
@@ -278,18 +430,24 @@ proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var Suggestions) =
   wholeSymTab(filterSym(it, nil, pm) and nameFits(c, it, n) and argsFit(c, it, n, nOrig),
               ideCon)
 
+proc suggestVar(c: PContext, n: PNode, outputs: var Suggestions) =
+  let info = n.info
+  wholeSymTab(nameFits(c, it, n), ideCon)
+
 proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
-  if s.typ != nil and sonsLen(s.typ) > 1 and s.typ.sons[1] != nil:
-    # special rule: if system and some weird generic match via 'tyExpr'
+  if s.typ != nil and s.typ.paramsLen > 0 and s.typ.firstParamType != nil:
+    # special rule: if system and some weird generic match via 'tyUntyped'
     # or 'tyGenericParam' we won't list it either to reduce the noise (nobody
     # wants 'system.`-|` as suggestion
     let m = s.getModule()
     if m != nil and sfSystemModule in m.flags:
       if s.kind == skType: return
-      var exp = s.typ.sons[1].skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
+      var exp = s.typ.firstParamType.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
       if exp.kind == tyVarargs: exp = elemType(exp)
-      if exp.kind in {tyExpr, tyStmt, tyGenericParam, tyAnything}: return
-    result = sigmatch.argtypeMatches(c, s.typ.sons[1], firstArg)
+      if exp.kind in {tyUntyped, tyTyped, tyGenericParam, tyAnything}: return
+    result = sigmatch.argtypeMatches(c, s.typ.firstParamType, firstArg)
+  else:
+    result = false
 
 proc suggestOperations(c: PContext, n, f: PNode, typ: PType, outputs: var Suggestions) =
   assert typ != nil
@@ -298,39 +456,36 @@ proc suggestOperations(c: PContext, n, f: PNode, typ: PType, outputs: var Sugges
 
 proc suggestEverything(c: PContext, n, f: PNode, outputs: var Suggestions) =
   # do not produce too many symbols:
-  var isLocal = true
-  var scopeN = 0
-  for scope in walkScopes(c.currentScope):
-    if scope == c.topLevelScope: isLocal = false
-    dec scopeN
-    for it in items(scope.symbols):
-      var pm: PrefixMatch
-      if filterSym(it, f, pm):
-        outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug, n.info, 0, pm,
-                                 c.inTypeContext > 0, scopeN))
-    #if scope == c.topLevelScope and f.isNil: break
+  for (it, scopeN, isLocal) in uniqueSyms(c):
+    var pm: PrefixMatch = default(PrefixMatch)
+    if filterSym(it, f, pm):
+      outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, ideSug, n.info,
+                               it.getQuality, pm, c.inTypeContext > 0, scopeN))
 
 proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) =
   # special code that deals with ``myObj.``. `n` is NOT the nkDotExpr-node, but
   # ``myObj``.
   var typ = n.typ
-  var pm: PrefixMatch
+  var pm: PrefixMatch = default(PrefixMatch)
   when defined(nimsuggest):
     if n.kind == nkSym and n.sym.kind == skError and c.config.suggestVersion == 0:
       # consider 'foo.|' where 'foo' is some not imported module.
       let fullPath = findModule(c.config, n.sym.name.s, toFullPath(c.config, n.info))
-      if fullPath.len == 0:
+      if fullPath.isEmpty:
         # error: no known module name:
         typ = nil
       else:
-        let m = c.graph.importModuleCallback(c.graph, c.module, fileInfoIdx(c.config, fullpath))
+        let m = c.graph.importModuleCallback(c.graph, c.module, fileInfoIdx(c.config, fullPath))
         if m == nil: typ = nil
         else:
-          for it in items(n.sym.tab):
+          for it in allSyms(c.graph, n.sym):
             if filterSym(it, field, pm):
-              outputs.add(symToSuggest(c.config, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -100))
-          outputs.add(symToSuggest(c.config, m, isLocal=false, ideMod, n.info, 100, PrefixMatch.None,
-            c.inTypeContext > 0, -99))
+              outputs.add(symToSuggest(c.graph, it, isLocal=false, ideSug,
+                                        n.info, it.getQuality, pm,
+                                        c.inTypeContext > 0, -100))
+          outputs.add(symToSuggest(c.graph, m, isLocal=false, ideMod, n.info,
+                                    100, PrefixMatch.None, c.inTypeContext > 0,
+                                    -99))
 
   if typ == nil:
     # a module symbol has no type for example:
@@ -339,32 +494,45 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
         # all symbols accessible, because we are in the current module:
         for it in items(c.topLevelScope.symbols):
           if filterSym(it, field, pm):
-            outputs.add(symToSuggest(c.config, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99))
+            outputs.add(symToSuggest(c.graph, it, isLocal=false, ideSug,
+                                      n.info, it.getQuality, pm,
+                                      c.inTypeContext > 0, -99))
       else:
-        for it in items(n.sym.tab):
+        for it in allSyms(c.graph, n.sym):
           if filterSym(it, field, pm):
-            outputs.add(symToSuggest(c.config, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99))
+            outputs.add(symToSuggest(c.graph, it, isLocal=false, ideSug,
+                                      n.info, it.getQuality, pm,
+                                      c.inTypeContext > 0, -99))
     else:
       # fallback:
       suggestEverything(c, n, field, outputs)
-  elif typ.kind == tyEnum and n.kind == nkSym and n.sym.kind == skType:
-    # look up if the identifier belongs to the enum:
-    var t = typ
-    while t != nil:
-      suggestSymList(c, t.n, field, n.info, outputs)
-      t = t.sons[0]
-    suggestOperations(c, n, field, typ, outputs)
   else:
-    let orig = typ # skipTypes(typ, {tyGenericInst, tyAlias, tySink})
-    typ = skipTypes(typ, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
-    if typ.kind == tyObject:
+    let orig = typ
+    typ = skipTypes(orig, {tyTypeDesc, tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink, tyOwned})
+
+    if typ.kind == tyEnum and n.kind == nkSym and n.sym.kind == skType:
+      # look up if the identifier belongs to the enum:
+      var t = typ
+      while t != nil:
+        suggestSymList(c, t.n, field, n.info, outputs)
+        t = t.baseClass
+    elif typ.kind == tyObject:
       var t = typ
       while true:
         suggestObject(c, t.n, field, n.info, outputs)
-        if t.sons[0] == nil: break
-        t = skipTypes(t.sons[0], skipPtrs)
+        if t.baseClass == nil: break
+        t = skipTypes(t.baseClass, skipPtrs)
     elif typ.kind == tyTuple and typ.n != nil:
-      suggestSymList(c, typ.n, field, n.info, outputs)
+      # All tuple fields are in scope
+      # So go through each field and add it to the suggestions (If it passes the filter)
+      for node in typ.n:
+        if node.kind == nkSym:
+          let s = node.sym
+          var pm: PrefixMatch = default(PrefixMatch)
+          if filterSym(s, field, pm):
+            outputs.add(symToSuggest(c.graph, s, isLocal=true, ideSug, n.info,
+                                     s.getQuality, pm, c.inTypeContext > 0, 0))
+
     suggestOperations(c, n, field, orig, outputs)
     if typ != orig:
       suggestOperations(c, n, field, typ, outputs)
@@ -375,17 +543,34 @@ type
 
 proc inCheckpoint*(current, trackPos: TLineInfo): TCheckPointResult =
   if current.fileIndex == trackPos.fileIndex:
+    result = cpNone
     if current.line == trackPos.line and
         abs(current.col-trackPos.col) < 4:
       return cpExact
     if current.line >= trackPos.line:
       return cpFuzzy
+  else:
+    result = cpNone
 
 proc isTracked*(current, trackPos: TLineInfo, tokenLen: int): bool =
   if current.fileIndex==trackPos.fileIndex and current.line==trackPos.line:
     let col = trackPos.col
     if col >= current.col and col <= current.col+tokenLen-1:
-      return true
+      result = true
+    else:
+      result = false
+  else:
+    result = false
+
+proc isTracked*(current, trackPos: TinyLineInfo, tokenLen: int): bool =
+  if current.line==trackPos.line:
+    let col = trackPos.col
+    if col >= current.col and col <= current.col+tokenLen-1:
+      result = true
+    else:
+      result = false
+  else:
+    result = false
 
 when defined(nimsuggest):
   # Since TLineInfo defined a == operator that doesn't include the column,
@@ -401,28 +586,31 @@ when defined(nimsuggest):
       if infoB.infoToInt == infoAsInt: return
     s.allUsages.add(info)
 
-proc findUsages(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) =
-  if conf.suggestVersion == 1:
-    if usageSym == nil and isTracked(info, conf.m.trackPos, s.name.s.len):
+proc findUsages(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym) =
+  if g.config.suggestVersion == 1:
+    if usageSym == nil and isTracked(info, g.config.m.trackPos, s.name.s.len):
       usageSym = s
-      suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
+      suggestResult(g.config, symToSuggest(g, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
     elif s == usageSym:
-      if conf.lastLineInfo != info:
-        suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
-      conf.lastLineInfo = info
+      if g.config.lastLineInfo != info:
+        suggestResult(g.config, symToSuggest(g, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
+      g.config.lastLineInfo = info
 
 when defined(nimsuggest):
-  proc listUsages*(conf: ConfigRef; s: PSym) =
-    #echo "usages ", len(s.allUsages)
+  proc listUsages*(g: ModuleGraph; s: PSym) =
+    #echo "usages ", s.allUsages.len
     for info in s.allUsages:
       let x = if info == s.info and info.col == s.info.col: ideDef else: ideUse
-      suggestResult(conf, symToSuggest(conf, s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0))
+      suggestResult(g.config, symToSuggest(g, s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0))
 
-proc findDefinition(conf: ConfigRef; info: TLineInfo; s: PSym) =
+proc findDefinition(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym) =
   if s.isNil: return
-  if isTracked(info, conf.m.trackPos, s.name.s.len):
-    suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
-    suggestQuit()
+  if isTracked(info, g.config.m.trackPos, s.name.s.len) or (s == usageSym and sfForward notin s.flags):
+    suggestResult(g.config, symToSuggest(g, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0, useSuppliedInfo = s == usageSym))
+    if sfForward notin s.flags and g.config.suggestVersion < 3:
+      suggestQuit()
+    else:
+      usageSym = s
 
 proc ensureIdx[T](x: var T, y: int) =
   if x.len <= y: x.setLen(y+1)
@@ -430,53 +618,104 @@ proc ensureIdx[T](x: var T, y: int) =
 proc ensureSeq[T](x: var seq[T]) =
   if x == nil: newSeq(x, 0)
 
-proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} =
+proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} =
   ## misnamed: should be 'symDeclared'
+  let conf = g.config
   when defined(nimsuggest):
+    g.suggestSymbols.add SymInfoPair(sym: s, info: info, isDecl: isDecl), optIdeExceptionInlayHints in g.config.globalOptions
+
     if conf.suggestVersion == 0:
-      if s.allUsages.isNil:
+      if s.allUsages.len == 0:
         s.allUsages = @[info]
       else:
         s.addNoDup(info)
 
     if conf.ideCmd == ideUse:
-      findUsages(conf, info, s, usageSym)
+      findUsages(g, info, s, usageSym)
     elif conf.ideCmd == ideDef:
-      findDefinition(conf, info, s)
+      findDefinition(g, info, s, usageSym)
     elif conf.ideCmd == ideDus and s != nil:
       if isTracked(info, conf.m.trackPos, s.name.s.len):
-        suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
-      findUsages(conf, info, s, usageSym)
+        suggestResult(conf, symToSuggest(g, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
+      findUsages(g, info, s, usageSym)
     elif conf.ideCmd == ideHighlight and info.fileIndex == conf.m.trackPos.fileIndex:
-      suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0))
-    elif conf.ideCmd == ideOutline and info.fileIndex == conf.m.trackPos.fileIndex and
-        isDecl:
-      suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))
+      suggestResult(conf, symToSuggest(g, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0))
+    elif conf.ideCmd == ideOutline and isDecl:
+      # if a module is included then the info we have is inside the include and
+      # we need to walk up the owners until we find the outer most module,
+      # which will be the last skModule prior to an skPackage.
+      var
+        parentFileIndex = info.fileIndex # assume we're in the correct module
+        parentModule = s.owner
+      while parentModule != nil and parentModule.kind == skModule:
+        parentFileIndex = parentModule.info.fileIndex
+        parentModule = parentModule.owner
+
+      if parentFileIndex == conf.m.trackPos.fileIndex:
+        suggestResult(conf, symToSuggest(g, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))
 
 proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) =
-  if s.kind in routineKinds:
-    let n = s.ast[pragmasPos]
-    if n.kind != nkEmpty:
-      for it in n:
-        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)
-          return
-  message(conf, info, warnDeprecated, s.name.s)
-
-proc markUsed(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) =
+  var pragmaNode: PNode
+  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 & "; " & name & " is deprecated")
+        return
+  message(conf, info, warnDeprecated, name & " is deprecated")
+
+proc userError(conf: ConfigRef; info: TLineInfo; s: PSym) =
+  let pragmaNode = extractPragma(s)
+  template bail(prefix: string) =
+    localError(conf, info, "$1usage of '$2' is an {.error.} defined at $3" %
+      [prefix, s.name.s, toFileLineCol(conf, s.ast.info)])
+  if pragmaNode != nil:
+    for it in pragmaNode:
+      if whichPragma(it) == wError and it.safeLen == 2 and
+          it[1].kind in {nkStrLit..nkTripleStrLit}:
+        bail(it[1].strVal & "; ")
+        return
+  bail("")
+
+proc markOwnerModuleAsUsed(c: PContext; s: PSym) =
+  var module = s
+  while module != nil and module.kind != skModule:
+    module = module.owner
+  if module != nil and module != c.module:
+    var i = 0
+    while i <= high(c.unusedImports):
+      let candidate = c.unusedImports[i][0]
+      if candidate == module or c.importModuleMap.getOrDefault(candidate.id, int.low) == module.id or
+        c.exportIndirections.contains((candidate.id, s.id)):
+        # mark it as used:
+        c.unusedImports.del(i)
+      else:
+        inc i
+
+proc markUsed(c: PContext; info: TLineInfo; s: PSym; checkStyle = true) =
+  let conf = c.config
   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: localError(conf, info,  "usage of '$1' is a user-defined error" % s.name.s)
-  when defined(nimsuggest):
-    suggestSym(conf, info, s, usageSym, false)
+    if sfDeprecated in s.flags:
+      if not (c.lastTLineInfo.line == info.line and
+              c.lastTLineInfo.col == info.col):
+        warnAboutDeprecated(conf, info, s)
+        c.lastTLineInfo = info
 
-proc useSym*(conf: ConfigRef; sym: PSym; usageSym: var PSym): PNode =
-  result = newSymNode(sym)
-  markUsed(conf, result.info, sym, usageSym)
+    if sfError in s.flags: userError(conf, info, s)
+  when defined(nimsuggest):
+    suggestSym(c.graph, info, s, c.graph.usageSym, false)
+  if checkStyle:
+    styleCheckUse(c, info, s)
+  markOwnerModuleAsUsed(c, s)
 
 proc safeSemExpr*(c: PContext, n: PNode): PNode =
   # use only for idetools support!
@@ -487,7 +726,7 @@ proc safeSemExpr*(c: PContext, n: PNode): PNode =
 
 proc sugExpr(c: PContext, n: PNode, outputs: var Suggestions) =
   if n.kind == nkDotExpr:
-    var obj = safeSemExpr(c, n.sons[0])
+    var obj = safeSemExpr(c, n[0])
     # it can happen that errnously we have collected the fieldname
     # of the next line, so we check the 'field' is actually on the same
     # line as the object to prevent this from happening:
@@ -498,6 +737,11 @@ proc sugExpr(c: PContext, n: PNode, outputs: var Suggestions) =
     #if optIdeDebug in gGlobalOptions:
     #  echo "expression ", renderTree(obj), " has type ", typeToString(obj.typ)
     #writeStackTrace()
+  elif n.kind == nkIdent:
+    let
+      prefix = if c.config.m.trackPosAttached: nil else: n
+      info = n.info
+    wholeSymTab(filterSym(it, prefix, pm), ideSug)
   else:
     let prefix = if c.config.m.trackPosAttached: nil else: n
     suggestEverything(c, n, prefix, outputs)
@@ -512,15 +756,19 @@ proc suggestExprNoCheck*(c: PContext, n: PNode) =
   elif c.config.ideCmd == ideCon:
     if n.kind in nkCallKinds:
       var a = copyNode(n)
-      var x = safeSemExpr(c, n.sons[0])
-      if x.kind == nkEmpty or x.typ == nil: x = n.sons[0]
-      addSon(a, x)
-      for i in 1..sonsLen(n)-1:
+      var x = safeSemExpr(c, n[0])
+      if x.kind == nkEmpty or x.typ == nil: x = n[0]
+      a.add x
+      for i in 1..<n.len:
         # use as many typed arguments as possible:
-        var x = safeSemExpr(c, n.sons[i])
+        var x = safeSemExpr(c, n[i])
         if x.kind == nkEmpty or x.typ == nil: break
-        addSon(a, x)
+        a.add x
       suggestCall(c, a, n, outputs)
+    elif n.kind in nkIdentKinds:
+      var x = safeSemExpr(c, n)
+      if x.kind == nkEmpty or x.typ == nil: x = n
+      suggestVar(c, x, outputs)
 
   dec(c.compilesContextId)
   if outputs.len > 0 and c.config.ideCmd in {ideSug, ideCon, ideDef}:
@@ -535,6 +783,9 @@ proc suggestDecl*(c: PContext, n: PNode; s: PSym) =
   if attached: inc(c.inTypeContext)
   defer:
     if attached: dec(c.inTypeContext)
+  # If user is typing out an enum field, then don't provide suggestions
+  if s.kind == skEnumField and c.config.cmd == cmdIdeTools and exactEquals(c.config.m.trackPos, n.info):
+    suggestQuit()
   suggestExpr(c, n)
 
 proc suggestStmt*(c: PContext, n: PNode) =
@@ -546,23 +797,63 @@ proc suggestEnum*(c: PContext; n: PNode; t: PType) =
   produceOutput(outputs, c.config)
   if outputs.len > 0: suggestQuit()
 
+proc suggestPragmas*(c: PContext, n: PNode) =
+  ## Suggests anything that might be a pragma
+  ## - template that has {.pragma.}
+  ## - macros
+  ## - user pragmas
+  let info = n.info
+  var outputs: Suggestions = @[]
+  # First filter for template/macros
+  wholeSymTab(filterSym(it, n, pm) and
+    (sfCustomPragma in it.flags or it.kind == skMacro),
+    ideSug)
+
+  # Now show suggestions for user pragmas
+  for pragma in c.userPragmas:
+    var pm = default(PrefixMatch)
+    if filterSym(pragma, n, pm):
+      outputs &= symToSuggest(c.graph, pragma, isLocal=true, ideSug, info,
+                                pragma.getQuality, pm, c.inTypeContext > 0, 0,
+                                extractDocs=false)
+
+  produceOutput(outputs, c.config)
+  if outputs.len > 0:
+    suggestQuit()
+
+template trySuggestPragmas*(c: PContext, n: PNode) =
+  ## Runs [suggestPragmas] when compiling nimsuggest and
+  ## we are querying the node
+  when defined(nimsuggest):
+    let tmp = n
+    if c.config.ideCmd == ideSug and exactEquals(c.config.m.trackPos, tmp.info):
+      suggestPragmas(c, tmp)
+
 proc suggestSentinel*(c: PContext) =
   if c.config.ideCmd != ideSug or c.module.position != c.config.m.trackPos.fileIndex.int32: return
   if c.compilesContextId > 0: return
   inc(c.compilesContextId)
   var outputs: Suggestions = @[]
   # suggest everything:
-  var isLocal = true
-  var scopeN = 0
-  for scope in walkScopes(c.currentScope):
-    if scope == c.topLevelScope: isLocal = false
-    dec scopeN
-    for it in items(scope.symbols):
-      var pm: PrefixMatch
-      if filterSymNoOpr(it, nil, pm):
-        outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug,
-            newLineInfo(c.config.m.trackPos.fileIndex, -1, -1), 0,
-            PrefixMatch.None, false, scopeN))
+  for (it, scopeN, isLocal) in uniqueSyms(c):
+    var pm: PrefixMatch = default(PrefixMatch)
+    if filterSymNoOpr(it, nil, pm):
+      outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, ideSug,
+          newLineInfo(c.config.m.trackPos.fileIndex, 0, -1), it.getQuality,
+          PrefixMatch.None, false, scopeN))
 
   dec(c.compilesContextId)
   produceOutput(outputs, c.config)
+
+when defined(nimsuggest):
+  proc onDef(graph: ModuleGraph, s: PSym, info: TLineInfo) =
+    if graph.config.suggestVersion >= 3 and info.exactEquals(s.info):
+       suggestSym(graph, info, s, graph.usageSym)
+
+  template getPContext(): untyped =
+    when c is PContext: c
+    else: c.c
+
+  template onDef*(info: TLineInfo; s: PSym) =
+    let c = getPContext()
+    onDef(c.graph, s, info)
diff --git a/compiler/suggestsymdb.nim b/compiler/suggestsymdb.nim
new file mode 100644
index 000000000..e1e67afbe
--- /dev/null
+++ b/compiler/suggestsymdb.nim
@@ -0,0 +1,212 @@
+import std/[intsets, tables, algorithm, assertions]
+import ast, lineinfos, msgs
+
+type
+  PackedBoolArray* = object
+    s: IntSet
+    len: int
+
+  TinyLineInfo* = object
+    line*: uint16
+    col*: int16
+
+  SymInfoPair* = object
+    sym*: PSym
+    info*: TLineInfo
+    caughtExceptions*: seq[PType]
+    caughtExceptionsSet*: bool
+    isDecl*: bool
+
+  SuggestFileSymbolDatabase* = object
+    lineInfo*: seq[TinyLineInfo]
+    sym*: seq[PSym]
+    caughtExceptions*: seq[seq[PType]]
+    caughtExceptionsSet*: PackedBoolArray
+    isDecl*: PackedBoolArray
+    fileIndex*: FileIndex
+    trackCaughtExceptions*: bool
+    isSorted*: bool
+
+  SuggestSymbolDatabase* = Table[FileIndex, SuggestFileSymbolDatabase]
+
+
+func newPackedBoolArray*(): PackedBoolArray =
+  PackedBoolArray(
+    s: initIntSet(),
+    len: 0
+  )
+
+func low*(s: PackedBoolArray): int =
+  0
+
+func high*(s: PackedBoolArray): int =
+  s.len - 1
+
+func `[]`*(s: PackedBoolArray; idx: int): bool =
+  s.s.contains(idx)
+
+proc `[]=`*(s: var PackedBoolArray; idx: int; v: bool) =
+  if v:
+    s.s.incl(idx)
+  else:
+    s.s.excl(idx)
+
+proc add*(s: var PackedBoolArray; v: bool) =
+  inc(s.len)
+  if v:
+    s.s.incl(s.len - 1)
+
+proc reverse*(s: var PackedBoolArray) =
+  var
+    reversedSet = initIntSet()
+  for i in 0..s.high:
+    if s.s.contains(i):
+      reversedSet.incl(s.high - i)
+  s.s = reversedSet
+
+proc getSymInfoPair*(s: SuggestFileSymbolDatabase; idx: int): SymInfoPair =
+  SymInfoPair(
+    sym: s.sym[idx],
+    info: TLineInfo(
+      line: s.lineInfo[idx].line,
+      col: s.lineInfo[idx].col,
+      fileIndex: s.fileIndex
+    ),
+    caughtExceptions:
+      if s.trackCaughtExceptions:
+        s.caughtExceptions[idx]
+      else:
+        @[],
+    caughtExceptionsSet:
+      if s.trackCaughtExceptions:
+        s.caughtExceptionsSet[idx]
+      else:
+        false,
+    isDecl: s.isDecl[idx]
+  )
+
+proc reverse*(s: var SuggestFileSymbolDatabase) =
+  s.lineInfo.reverse()
+  s.sym.reverse()
+  s.caughtExceptions.reverse()
+  s.caughtExceptionsSet.reverse()
+  s.isDecl.reverse()
+
+proc newSuggestFileSymbolDatabase*(aFileIndex: FileIndex; aTrackCaughtExceptions: bool): SuggestFileSymbolDatabase =
+  SuggestFileSymbolDatabase(
+    lineInfo: @[],
+    sym: @[],
+    caughtExceptions: @[],
+    caughtExceptionsSet: newPackedBoolArray(),
+    isDecl: newPackedBoolArray(),
+    fileIndex: aFileIndex,
+    trackCaughtExceptions: aTrackCaughtExceptions,
+    isSorted: true
+  )
+
+proc exactEquals*(a, b: TinyLineInfo): bool =
+  result = a.line == b.line and a.col == b.col
+
+proc `==`*(a, b: SymInfoPair): bool =
+  result = a.sym == b.sym and a.info.exactEquals(b.info)
+
+func cmp*(a: TinyLineInfo; b: TinyLineInfo): int =
+  result = cmp(a.line, b.line)
+  if result == 0:
+    result = cmp(a.col, b.col)
+
+func compare*(s: var SuggestFileSymbolDatabase; i, j: int): int =
+  result = cmp(s.lineInfo[i], s.lineInfo[j])
+  if result == 0:
+    result = cmp(s.isDecl[i], s.isDecl[j])
+
+proc exchange(s: var SuggestFileSymbolDatabase; i, j: int) =
+  if i == j:
+    return
+  var tmp1 = s.lineInfo[i]
+  s.lineInfo[i] = s.lineInfo[j]
+  s.lineInfo[j] = tmp1
+  if s.trackCaughtExceptions:
+    var tmp2 = s.caughtExceptions[i]
+    s.caughtExceptions[i] = s.caughtExceptions[j]
+    s.caughtExceptions[j] = tmp2
+    var tmp3 = s.caughtExceptionsSet[i]
+    s.caughtExceptionsSet[i] = s.caughtExceptionsSet[j]
+    s.caughtExceptionsSet[j] = tmp3
+  var tmp4 = s.isDecl[i]
+  s.isDecl[i] = s.isDecl[j]
+  s.isDecl[j] = tmp4
+  var tmp5 = s.sym[i]
+  s.sym[i] = s.sym[j]
+  s.sym[j] = tmp5
+
+proc quickSort(s: var SuggestFileSymbolDatabase; ll, rr: int) =
+  var
+    i, j, pivotIdx: int
+    l = ll
+    r = rr
+  while true:
+    i = l
+    j = r
+    pivotIdx = l + ((r - l) shr 1)
+    while true:
+      while (i < pivotIdx) and (s.compare(pivotIdx, i) > 0):
+        inc i
+      while (j > pivotIdx) and (s.compare(pivotIdx, j) < 0):
+        dec j
+      if i < j:
+        s.exchange(i, j)
+        if pivotIdx == i:
+          pivotIdx = j
+          inc i
+        elif pivotIdx == j:
+          pivotIdx = i
+          dec j
+        else:
+          inc i
+          dec j
+      else:
+        break
+    if (pivotIdx - l) < (r - pivotIdx):
+      if (l + 1) < pivotIdx:
+        s.quickSort(l, pivotIdx - 1)
+      l = pivotIdx + 1
+    else:
+      if (pivotIdx + 1) < r:
+        s.quickSort(pivotIdx + 1, r)
+      if (l + 1) < pivotIdx:
+        r = pivotIdx - 1
+      else:
+        break
+    if l >= r:
+      break
+
+proc sort*(s: var SuggestFileSymbolDatabase) =
+  s.quickSort(s.lineInfo.low, s.lineInfo.high)
+  s.isSorted = true
+
+proc add*(s: var SuggestFileSymbolDatabase; v: SymInfoPair) =
+  doAssert(v.info.fileIndex == s.fileIndex)
+  s.lineInfo.add(TinyLineInfo(
+    line: v.info.line,
+    col: v.info.col
+  ))
+  s.sym.add(v.sym)
+  s.isDecl.add(v.isDecl)
+  if s.trackCaughtExceptions:
+    s.caughtExceptions.add(v.caughtExceptions)
+    s.caughtExceptionsSet.add(v.caughtExceptionsSet)
+  s.isSorted = false
+
+proc add*(s: var SuggestSymbolDatabase; v: SymInfoPair; trackCaughtExceptions: bool) =
+  s.mgetOrPut(v.info.fileIndex, newSuggestFileSymbolDatabase(v.info.fileIndex, trackCaughtExceptions)).add(v)
+
+proc findSymInfoIndex*(s: var SuggestFileSymbolDatabase; li: TLineInfo): int =
+  doAssert(li.fileIndex == s.fileIndex)
+  if not s.isSorted:
+    s.sort()
+  var q = TinyLineInfo(
+    line: li.line,
+    col: li.col
+  )
+  result = binarySearch(s.lineInfo, q, cmp)
diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim
index 069f65eee..6b325c77f 100644
--- a/compiler/syntaxes.nim
+++ b/compiler/syntaxes.nim
@@ -10,55 +10,37 @@
 ## Implements the dispatcher for the different parsers.
 
 import
-  strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser,
-  filters, filter_tmpl, renderer, lineinfos
+  llstream, ast, idents, lexer, options, msgs, parser,
+  filters, filter_tmpl, renderer, lineinfos, pathutils
 
-type
-  TFilterKind* = enum
-    filtNone, filtTemplate, filtReplace, filtStrip
-  TParserKind* = enum
-    skinStandard, skinStrongSpaces, skinEndX
+import std/strutils
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions]
 
-const
-  parserNames*: array[TParserKind, string] = ["standard", "strongspaces",
-                                              "endx"]
-  filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace",
-                                              "strip"]
+export Parser, parseAll, parseTopLevelStmt, checkFirstLineIndentation, closeParser
 
 type
-  TParsers* = object
-    skin*: TParserKind
-    parser*: TParser
-
-template config(p: TParsers): ConfigRef = p.parser.lex.config
-
-proc parseAll*(p: var TParsers): PNode =
-  case p.skin
-  of skinStandard, skinStrongSpaces:
-    result = parser.parseAll(p.parser)
-  of skinEndX:
-    internalError(p.config, "parser to implement")
-
-proc parseTopLevelStmt*(p: var TParsers): PNode =
-  case p.skin
-  of skinStandard, skinStrongSpaces:
-    result = parser.parseTopLevelStmt(p.parser)
-  of skinEndX:
-    internalError(p.config, "parser to implement")
+  FilterKind = enum
+    filtNone = "none"
+    filtTemplate = "stdtmpl"
+    filtReplace = "replace"
+    filtStrip = "strip"
 
 proc utf8Bom(s: string): int =
   if s.len >= 3 and s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF':
-    result = 3
+    3
   else:
-    result = 0
+    0
 
 proc containsShebang(s: string, i: int): bool =
   if i+1 < s.len and s[i] == '#' and s[i+1] == '!':
     var j = i + 2
     while j < s.len and s[j] in Whitespace: inc(j)
     result = s[j] == '/'
+  else:
+    result = false
 
-proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache;
+proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache;
                config: ConfigRef): PNode =
   result = newNode(nkEmpty)
   var s = llStreamOpen(filename, fmRead)
@@ -72,98 +54,92 @@ proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache;
       i = 0
       inc linenumber
     if i+1 < line.len and line[i] == '#' and line[i+1] == '?':
-      inc(i, 2)
-      while i < line.len and line[i] in Whitespace: inc(i)
-      var q: TParser
-      parser.openParser(q, filename, llStreamOpen(substr(line, i)), cache, config)
-      result = parser.parseAll(q)
-      parser.closeParser(q)
+      when defined(nimpretty):
+        # XXX this is a bit hacky, but oh well...
+        config.quitOrRaise "can't nimpretty a source code filter: " & $filename
+      else:
+        inc(i, 2)
+        while i < line.len and line[i] in Whitespace: inc(i)
+        var p: Parser = default(Parser)
+        openParser(p, filename, llStreamOpen(substr(line, i)), cache, config)
+        result = parseAll(p)
+        closeParser(p)
     llStreamClose(s)
 
-proc getFilter(ident: PIdent): TFilterKind =
-  for i in countup(low(TFilterKind), high(TFilterKind)):
-    if cmpIgnoreStyle(ident.s, filterNames[i]) == 0:
-      return i
+proc getFilter(ident: PIdent): FilterKind =
   result = filtNone
-
-proc getParser(conf: ConfigRef; n: PNode; ident: PIdent): TParserKind =
-  for i in countup(low(TParserKind), high(TParserKind)):
-    if cmpIgnoreStyle(ident.s, parserNames[i]) == 0:
+  for i in FilterKind:
+    if cmpIgnoreStyle(ident.s, $i) == 0:
       return i
-  localError(conf, n.info, "unknown parser: " & ident.s)
 
 proc getCallee(conf: ConfigRef; n: PNode): PIdent =
-  if n.kind in nkCallKinds and n.sons[0].kind == nkIdent:
-    result = n.sons[0].ident
+  if n.kind in nkCallKinds and n[0].kind == nkIdent:
+    result = n[0].ident
   elif n.kind == nkIdent:
     result = n.ident
   else:
+    result = nil
     localError(conf, n.info, "invalid filter: " & renderTree(n))
 
-proc applyFilter(p: var TParsers, n: PNode, filename: string,
+proc applyFilter(p: var Parser, n: PNode, filename: AbsoluteFile,
                  stdin: PLLStream): PLLStream =
-  var ident = getCallee(p.config, n)
-  var f = getFilter(ident)
-  case f
-  of filtNone:
-    p.skin = getParser(p.config, n, ident)
-    result = stdin
-  of filtTemplate:
-    result = filterTmpl(stdin, filename, n, p.config)
-  of filtStrip:
-    result = filterStrip(p.config, stdin, filename, n)
-  of filtReplace:
-    result = filterReplace(p.config, stdin, filename, n)
+  var f = getFilter(getCallee(p.lex.config, n))
+  result = case f
+           of filtNone:
+             stdin
+           of filtTemplate:
+             filterTmpl(p.lex.config, stdin, filename, n)
+           of filtStrip:
+             filterStrip(p.lex.config, stdin, filename, n)
+           of filtReplace:
+             filterReplace(p.lex.config, stdin, filename, n)
   if f != filtNone:
-    assert p.config != nil
-    if hintCodeBegin in p.config.notes:
-      rawMessage(p.config, hintCodeBegin, [])
-      msgWriteln(p.config, result.s)
-      rawMessage(p.config, hintCodeEnd, [])
+    assert p.lex.config != nil
+    if p.lex.config.hasHint(hintCodeBegin):
+      rawMessage(p.lex.config, hintCodeBegin, "")
+      msgWriteln(p.lex.config, result.s)
+      rawMessage(p.lex.config, hintCodeEnd, "")
 
-proc evalPipe(p: var TParsers, n: PNode, filename: string,
+proc evalPipe(p: var Parser, n: PNode, filename: AbsoluteFile,
               start: PLLStream): PLLStream =
-  assert p.config != nil
+  assert p.lex.config != nil
   result = start
   if n.kind == nkEmpty: return
   if n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "|":
-    for i in countup(1, 2):
-      if n.sons[i].kind == nkInfix:
-        result = evalPipe(p, n.sons[i], filename, result)
+    for i in 1..2:
+      if n[i].kind == nkInfix:
+        result = evalPipe(p, n[i], filename, result)
       else:
-        result = applyFilter(p, n.sons[i], filename, result)
+        result = applyFilter(p, n[i], filename, result)
   elif n.kind == nkStmtList:
-    result = evalPipe(p, n.sons[0], filename, result)
+    result = evalPipe(p, n[0], filename, result)
   else:
     result = applyFilter(p, n, filename, result)
 
-proc openParsers*(p: var TParsers, fileIdx: FileIndex, inputstream: PLLStream;
+proc openParser*(p: var Parser, fileIdx: FileIndex, inputstream: PLLStream;
                   cache: IdentCache; config: ConfigRef) =
   assert config != nil
-  var s: PLLStream
-  p.skin = skinStandard
   let filename = toFullPathConsiderDirty(config, fileIdx)
   var pipe = parsePipe(filename, inputstream, cache, config)
-  p.config() = config
-  if pipe != nil: s = evalPipe(p, pipe, filename, inputstream)
-  else: s = inputstream
-  case p.skin
-  of skinStandard, skinEndX:
-    parser.openParser(p.parser, fileIdx, s, cache, config, false)
-  of skinStrongSpaces:
-    parser.openParser(p.parser, fileIdx, s, cache, config, true)
+  p.lex.config = config
+  let s = if pipe != nil: evalPipe(p, pipe, filename, inputstream)
+          else: inputstream
+  parser.openParser(p, fileIdx, s, cache, config)
 
-proc closeParsers*(p: var TParsers) =
-  parser.closeParser(p.parser)
-
-proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode {.procvar.} =
-  var
-    p: TParsers
-    f: File
+proc setupParser*(p: var Parser; fileIdx: FileIndex; cache: IdentCache;
+                   config: ConfigRef): bool =
   let filename = toFullPathConsiderDirty(config, fileIdx)
-  if not open(f, filename):
-    rawMessage(config, errGenerated, "cannot open file: " & filename)
-    return
-  openParsers(p, fileIdx, llStreamOpen(f), cache, config)
-  result = parseAll(p)
-  closeParsers(p)
+  var f: File = default(File)
+  if not open(f, filename.string):
+    rawMessage(config, errGenerated, "cannot open file: " & filename.string)
+    return false
+  openParser(p, fileIdx, llStreamOpen(f), cache, config)
+  result = true
+
+proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode =
+  var p: Parser = default(Parser)
+  if setupParser(p, fileIdx, cache, config):
+    result = parseAll(p)
+    closeParser(p)
+  else:
+    result = nil
diff --git a/compiler/tccgen.nim b/compiler/tccgen.nim
index ea0fb590f..9ee8516c4 100644
--- a/compiler/tccgen.nim
+++ b/compiler/tccgen.nim
@@ -8,12 +8,20 @@
 #
 
 import
-  os, strutils, options, msgs, tinyc
+  os, strutils, options, msgs, tinyc, lineinfos, sequtils
 
-{.compile: "../tinyc/libtcc.c".}
+const tinyPrefix = "dist/nim-tinyc-archive".unixToNativePath
+const nimRoot = currentSourcePath.parentDir.parentDir
+const tinycRoot = nimRoot / tinyPrefix
+when not dirExists(tinycRoot):
+  static: raiseAssert $(tinycRoot, "requires: ./koch installdeps tinyc")
+{.compile: tinycRoot / "tinyc/libtcc.c".}
+
+var
+  gConf: ConfigRef # ugly but can be cleaned up if this is revived
 
 proc tinyCErrorHandler(closure: pointer, msg: cstring) {.cdecl.} =
-  rawMessage(errGenerated, $msg)
+  rawMessage(gConf, errGenerated, $msg)
 
 proc initTinyCState: PccState =
   result = openCCState()
@@ -25,7 +33,7 @@ var
 
 proc addFile(filename: string) =
   if addFile(gTinyC, filename) != 0'i32:
-    rawMessage(errCannotOpenFile, filename)
+    rawMessage(gConf, errCannotOpenFile, filename)
 
 proc setupEnvironment =
   when defined(amd64):
@@ -35,42 +43,47 @@ proc setupEnvironment =
   when defined(linux):
     defineSymbol(gTinyC, "__linux__", nil)
     defineSymbol(gTinyC, "__linux", nil)
-  var nimDir = getPrefixDir()
 
-  addIncludePath(gTinyC, libpath)
+  var nimDir = getPrefixDir(gConf).string
+  var tinycRoot = nimDir / tinyPrefix
+  let libpath = nimDir / "lib"
+
+  addIncludePath(gTinyC, cstring(libpath))
   when defined(windows):
-    addSysincludePath(gTinyC, nimrodDir / "tinyc/win32/include")
-  addSysincludePath(gTinyC, nimrodDir / "tinyc/include")
+    addSysincludePath(gTinyC, cstring(tinycRoot / "tinyc/win32/include"))
+  addSysincludePath(gTinyC, cstring(tinycRoot / "tinyc/include"))
   when defined(windows):
     defineSymbol(gTinyC, "_WIN32", nil)
     # we need Mingw's headers too:
-    var gccbin = getConfigVar("gcc.path") % ["nim", nimDir]
-    addSysincludePath(gTinyC, gccbin /../ "include")
-    #addFile(nimrodDir / r"tinyc\win32\wincrt1.o")
-    addFile(nimrodDir / r"tinyc\win32\alloca86.o")
-    addFile(nimrodDir / r"tinyc\win32\chkstk.o")
-    #addFile(nimrodDir / r"tinyc\win32\crt1.o")
+    var gccbin = getConfigVar("gcc.path") % ["nim", tinycRoot]
+    addSysincludePath(gTinyC, cstring(gccbin /../ "include"))
+    #addFile(tinycRoot / r"tinyc\win32\wincrt1.o")
+    addFile(tinycRoot / r"tinyc\win32\alloca86.o")
+    addFile(tinycRoot / r"tinyc\win32\chkstk.o")
+    #addFile(tinycRoot / r"tinyc\win32\crt1.o")
 
-    #addFile(nimrodDir / r"tinyc\win32\dllcrt1.o")
-    #addFile(nimrodDir / r"tinyc\win32\dllmain.o")
-    addFile(nimrodDir / r"tinyc\win32\libtcc1.o")
+    #addFile(tinycRoot / r"tinyc\win32\dllcrt1.o")
+    #addFile(tinycRoot / r"tinyc\win32\dllmain.o")
+    addFile(tinycRoot / r"tinyc\win32\libtcc1.o")
 
-    #addFile(nimrodDir / r"tinyc\win32\lib\crt1.c")
-    #addFile(nimrodDir / r"tinyc\lib\libtcc1.c")
+    #addFile(tinycRoot / r"tinyc\win32\lib\crt1.c")
+    #addFile(tinycRoot / r"tinyc\lib\libtcc1.c")
   else:
     addSysincludePath(gTinyC, "/usr/include")
     when defined(amd64):
       addSysincludePath(gTinyC, "/usr/include/x86_64-linux-gnu")
 
-proc compileCCode*(ccode: string) =
+proc compileCCode*(ccode: string, conf: ConfigRef) =
+  gConf = conf
   if not libIncluded:
     libIncluded = true
     setupEnvironment()
   discard compileString(gTinyC, ccode)
 
-proc run*(args: string) =
-  var s = @[cstring(gProjectName)] & map(split(args), proc(x: string): cstring = cstring(x))
-  var err = tinyc.run(gTinyC, cint(len(s)), cast[cstringArray](addr(s[0]))) != 0'i32
+proc run*(conf: ConfigRef, args: string) =
+  doAssert gConf == conf
+  var s = @[cstring(conf.projectName)] & map(split(args), proc(x: string): cstring = cstring(x))
+  var err = tinyc.run(gTinyC, cint(s.len), cast[cstringArray](addr(s[0]))) != 0'i32
   closeCCState(gTinyC)
-  if err: rawMessage(errExecutionOfProgramFailed, "")
+  if err: rawMessage(conf, errUnknown, "")
 
diff --git a/compiler/transf.nim b/compiler/transf.nim
index ad7f38b91..8dd24e090 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -18,68 +18,68 @@
 # * performs lambda lifting for closure support
 # * transforms 'defer' into a 'try finally' statement
 
+import std / tables
+
 import
-  intsets, strutils, options, ast, astalgo, trees, treetab, msgs, lookups,
-  idents, renderer, types, passes, semfold, magicsys, cgmeth,
-  lambdalifting, sempass2, lowerings, destroyer, liftlocals, closureiters,
+  options, ast, astalgo, trees, msgs,
+  idents, renderer, types, semfold, magicsys, cgmeth,
+  lowerings, liftlocals,
   modulegraphs, lineinfos
 
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 type
-  PTransNode* = distinct PNode
+  TransformFlag* = enum
+    useCache, keepOpenArrayConversions, force
+  TransformFlags* = set[TransformFlag]
+
+proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flags: TransformFlags): PNode
 
-  PTransCon = ref TTransCon
-  TTransCon{.final.} = object # part of TContext; stackable
-    mapping: TIdNodeTable     # mapping from symbols to nodes
+import closureiters, lambdalifting
+
+type
+  PTransCon = ref object # part of TContext; stackable
+    mapping: Table[ItemId, PNode]     # mapping from symbols to nodes
     owner: PSym               # current owner
     forStmt: PNode            # current for stmt
-    forLoopBody: PTransNode   # transformed for loop body
+    forLoopBody: PNode   # transformed for loop body
     yieldStmts: int           # we count the number of yield statements,
                               # because we need to introduce new variables
                               # if we encounter the 2nd yield statement
     next: PTransCon           # for stacking
 
-  TTransfContext = object of passes.TPassContext
+  PTransf = ref object
     module: PSym
     transCon: PTransCon      # top of a TransCon stack
     inlining: int            # > 0 if we are in inlining context (copy vars)
-    nestedProcs: int         # > 0 if we are in a nested proc
     contSyms, breakSyms: seq[PSym]  # to transform 'continue' and 'break'
-    deferDetected, tooEarly, needsDestroyPass: bool
+    deferDetected, tooEarly: bool
+    isIntroducingNewLocalVars: bool  # true if we are in `introducingNewLocalVars` (don't transform yields)
+    inAddr: bool
+    flags: TransformFlags
     graph: ModuleGraph
-  PTransf = ref TTransfContext
+    idgen: IdGenerator
 
-proc newTransNode(a: PNode): PTransNode {.inline.} =
-  result = PTransNode(shallowCopy(a))
+proc newTransNode(a: PNode): PNode {.inline.} =
+  result = shallowCopy(a)
 
 proc newTransNode(kind: TNodeKind, info: TLineInfo,
-                  sons: int): PTransNode {.inline.} =
+                  sons: int): PNode {.inline.} =
   var x = newNodeI(kind, info)
   newSeq(x.sons, sons)
-  result = x.PTransNode
+  result = x
 
 proc newTransNode(kind: TNodeKind, n: PNode,
-                  sons: int): PTransNode {.inline.} =
+                  sons: int): PNode {.inline.} =
   var x = newNodeIT(kind, n.info, n.typ)
   newSeq(x.sons, sons)
-  x.typ = n.typ
-  result = x.PTransNode
-
-proc `[]=`(a: PTransNode, i: int, x: PTransNode) {.inline.} =
-  var n = PNode(a)
-  n.sons[i] = PNode(x)
-
-proc `[]`(a: PTransNode, i: int): PTransNode {.inline.} =
-  var n = PNode(a)
-  result = n.sons[i].PTransNode
-
-proc add(a, b: PTransNode) {.inline.} = addSon(PNode(a), PNode(b))
-proc len(a: PTransNode): int {.inline.} = result = sonsLen(a.PNode)
+#  x.flags = n.flags
+  result = x
 
 proc newTransCon(owner: PSym): PTransCon =
   assert owner != nil
-  new(result)
-  initIdNodeTable(result.mapping)
-  result.owner = owner
+  result = PTransCon(mapping: initTable[ItemId, PNode](), owner: owner)
 
 proc pushTransCon(c: PTransf, t: PTransCon) =
   t.next = c.transCon
@@ -94,36 +94,40 @@ proc getCurrOwner(c: PTransf): PSym =
   else: result = c.module
 
 proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode =
-  let r = newSym(skTemp, getIdent(c.graph.cache, genPrefix), getCurrOwner(c), info)
+  let r = newSym(skTemp, getIdent(c.graph.cache, genPrefix), c.idgen, getCurrOwner(c), info)
   r.typ = typ #skipTypes(typ, {tyGenericInst, tyAlias, tySink})
   incl(r.flags, sfFromGeneric)
   let owner = getCurrOwner(c)
-  if owner.isIterator and not c.tooEarly:
-    result = freshVarForClosureIter(c.graph, r, owner)
+  if owner.isIterator and not c.tooEarly and not isDefined(c.graph.config, "nimOptIters"):
+    result = freshVarForClosureIter(c.graph, r, c.idgen, owner)
   else:
     result = newSymNode(r)
 
-proc transform(c: PTransf, n: PNode): PTransNode
+proc transform(c: PTransf, n: PNode): PNode
 
-proc transformSons(c: PTransf, n: PNode): PTransNode =
+proc transformSons(c: PTransf, n: PNode): PNode =
   result = newTransNode(n)
-  for i in countup(0, sonsLen(n)-1):
-    result[i] = transform(c, n.sons[i])
-
-proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode =
-  result = newTransNode(nkFastAsgn, PNode(ri).info, 2)
-  result[0] = PTransNode(le)
+  for i in 0..<n.len:
+    result[i] = transform(c, n[i])
+
+proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PNode; isFirstWrite: bool): PNode =
+  result = newTransNode(kind, ri.info, 2)
+  result[0] = le
+  if isFirstWrite:
+    le.flags.incl nfFirstWrite
   result[1] = ri
 
 proc transformSymAux(c: PTransf, n: PNode): PNode =
   let s = n.sym
   if s.typ != nil and s.typ.callConv == ccClosure:
+    if s.kind in routineKinds:
+      discard transformBody(c.graph, c.idgen, s, {useCache}+c.flags)
     if s.kind == skIterator:
       if c.tooEarly: return n
-      else: return liftIterSym(c.graph, n, getCurrOwner(c))
+      else: return liftIterSym(c.graph, n, c.idgen, getCurrOwner(c))
     elif s.kind in {skProc, skFunc, skConverter, skMethod} and not c.tooEarly:
       # top level .closure procs are still somewhat supported for 'Nake':
-      return makeClosure(c.graph, s, nil, n.info)
+      return makeClosure(c.graph, c.idgen, s, nil, n.info)
   #elif n.sym.kind in {skVar, skLet} and n.sym.typ.callConv == ccClosure:
   #  echo n.info, " come heer for ", c.tooEarly
   #  if not c.tooEarly:
@@ -131,13 +135,34 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
   var tc = c.transCon
   if sfBorrow in s.flags and s.kind in routineKinds:
     # simply exchange the symbol:
-    b = s.getBody
+    var s = s
+    while true:
+      # Skips over all borrowed procs getting the last proc symbol without an implementation
+      let body = getBody(c.graph, s)
+      if body.kind == nkSym and sfBorrow in body.sym.flags and getBody(c.graph, body.sym).kind == nkSym:
+        s = body.sym
+      else:
+        break
+    b = getBody(c.graph, s)
     if b.kind != nkSym: internalError(c.graph.config, n.info, "wrong AST for borrowed symbol")
     b = newSymNode(b.sym, n.info)
+  elif c.inlining > 0:
+    # see bug #13596: we use ref-based equality in the DFA for destruction
+    # injections so we need to ensure unique nodes after iterator inlining
+    # which can lead to duplicated for loop bodies! Consider:
+    #[
+      while remaining > 0:
+        if ending == nil:
+          yield ms
+          break
+        ...
+        yield ms
+    ]#
+    b = newSymNode(n.sym, n.info)
   else:
     b = n
   while tc != nil:
-    result = idNodeTableGet(tc.mapping, b.sym)
+    result = getOrDefault(tc.mapping, b.sym.itemId)
     if result != nil:
       # this slightly convoluted way ensures the line info stays correct:
       if result.kind == nkSym:
@@ -147,38 +172,40 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
     tc = tc.next
   result = b
 
-proc transformSym(c: PTransf, n: PNode): PTransNode =
-  result = PTransNode(transformSymAux(c, n))
+proc transformSym(c: PTransf, n: PNode): PNode =
+  result = transformSymAux(c, n)
 
 proc freshVar(c: PTransf; v: PSym): PNode =
   let owner = getCurrOwner(c)
-  if owner.isIterator and not c.tooEarly:
-    result = freshVarForClosureIter(c.graph, v, owner)
+  if owner.isIterator and not c.tooEarly and not isDefined(c.graph.config, "nimOptIters"):
+    result = freshVarForClosureIter(c.graph, v, c.idgen, owner)
   else:
-    var newVar = copySym(v)
+    var newVar = copySym(v, c.idgen)
     incl(newVar.flags, sfFromGeneric)
     newVar.owner = owner
     result = newSymNode(newVar)
 
-proc transformVarSection(c: PTransf, v: PNode): PTransNode =
+proc transformVarSection(c: PTransf, v: PNode): PNode =
   result = newTransNode(v)
-  for i in countup(0, sonsLen(v)-1):
-    var it = v.sons[i]
+  for i in 0..<v.len:
+    var it = v[i]
     if it.kind == nkCommentStmt:
-      result[i] = PTransNode(it)
+      result[i] = it
     elif it.kind == nkIdentDefs:
-      if it.sons[0].kind == nkSym:
+      var vn = it[0]
+      if vn.kind == nkPragmaExpr: vn = vn[0]
+      if vn.kind == nkSym:
         internalAssert(c.graph.config, it.len == 3)
-        let x = freshVar(c, it.sons[0].sym)
-        idNodeTablePut(c.transCon.mapping, it.sons[0].sym, x)
+        let x = freshVar(c, vn.sym)
+        c.transCon.mapping[vn.sym.itemId] = x
         var defs = newTransNode(nkIdentDefs, it.info, 3)
         if importantComments(c.graph.config):
           # keep documentation information:
-          PNode(defs).comment = it.comment
-        defs[0] = x.PTransNode
-        defs[1] = it.sons[1].PTransNode
-        defs[2] = transform(c, it.sons[2])
-        if x.kind == nkSym: x.sym.ast = defs[2].PNode
+          defs.comment = it.comment
+        defs[0] = x
+        defs[1] = it[1]
+        defs[2] = transform(c, it[2])
+        if x.kind == nkSym: x.sym.ast = defs[2]
         result[i] = defs
       else:
         # has been transformed into 'param.x' for closure iterators, so just
@@ -187,67 +214,64 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode =
     else:
       if it.kind != nkVarTuple:
         internalError(c.graph.config, it.info, "transformVarSection: not nkVarTuple")
-      var L = sonsLen(it)
-      var defs = newTransNode(it.kind, it.info, L)
-      for j in countup(0, L-3):
-        let x = freshVar(c, it.sons[j].sym)
-        idNodeTablePut(c.transCon.mapping, it.sons[j].sym, x)
-        defs[j] = x.PTransNode
-      assert(it.sons[L-2].kind == nkEmpty)
-      defs[L-2] = newNodeI(nkEmpty, it.info).PTransNode
-      defs[L-1] = transform(c, it.sons[L-1])
+      var defs = newTransNode(it.kind, it.info, it.len)
+      for j in 0..<it.len-2:
+        if it[j].kind == nkSym:
+          let x = freshVar(c, it[j].sym)
+          c.transCon.mapping[it[j].sym.itemId] = x
+          defs[j] = x
+        else:
+          defs[j] = transform(c, it[j])
+      assert(it[^2].kind == nkEmpty)
+      defs[^2] = newNodeI(nkEmpty, it.info)
+      defs[^1] = transform(c, it[^1])
       result[i] = defs
 
-proc transformConstSection(c: PTransf, v: PNode): PTransNode =
-  result = newTransNode(v)
-  for i in countup(0, sonsLen(v)-1):
-    var it = v.sons[i]
-    if it.kind == nkCommentStmt:
-      result[i] = PTransNode(it)
-    else:
-      if it.kind != nkConstDef: internalError(c.graph.config, it.info, "transformConstSection")
-      if it.sons[0].kind != nkSym:
-        internalError(c.graph.config, it.info, "transformConstSection")
+proc transformConstSection(c: PTransf, v: PNode): PNode =
+  result = v
+  when false:
+    result = newTransNode(v)
+    for i in 0..<v.len:
+      var it = v[i]
+      if it.kind == nkCommentStmt:
+        result[i] = it
+      else:
+        if it.kind != nkConstDef: internalError(c.graph.config, it.info, "transformConstSection")
+        if it[0].kind != nkSym:
+          debug it[0]
+          internalError(c.graph.config, it.info, "transformConstSection")
 
-      result[i] = PTransNode(it)
+        result[i] = it
 
 proc hasContinue(n: PNode): bool =
   case n.kind
-  of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: discard
+  of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: result = false
   of nkContinueStmt: result = true
   else:
-    for i in countup(0, sonsLen(n) - 1):
-      if hasContinue(n.sons[i]): return true
+    result = false
+    for i in 0..<n.len:
+      if hasContinue(n[i]): return true
 
 proc newLabel(c: PTransf, n: PNode): PSym =
-  result = newSym(skLabel, nil, getCurrOwner(c), n.info)
-  result.name = getIdent(c.graph.cache, genPrefix & $result.id)
-
-proc freshLabels(c: PTransf, n: PNode; symMap: var TIdTable) =
-  if n.kind in {nkBlockStmt, nkBlockExpr}:
-    if n.sons[0].kind == nkSym:
-      let x = newLabel(c, n[0])
-      idTablePut(symMap, n[0].sym, x)
-      n.sons[0].sym = x
-  if n.kind == nkSym and n.sym.kind == skLabel:
-    let x = PSym(idTableGet(symMap, n.sym))
-    if x != nil: n.sym = x
-  else:
-    for i in 0 ..< safeLen(n): freshLabels(c, n.sons[i], symMap)
+  result = newSym(skLabel, getIdent(c.graph.cache, genPrefix), c.idgen, getCurrOwner(c), n.info)
 
-proc transformBlock(c: PTransf, n: PNode): PTransNode =
+proc transformBlock(c: PTransf, n: PNode): PNode =
   var labl: PSym
-  if n.sons[0].kind != nkEmpty:
-    # already named block? -> Push symbol on the stack:
-    labl = n.sons[0].sym
+  if c.inlining > 0:
+    labl = newLabel(c, n[0])
+    c.transCon.mapping[n[0].sym.itemId] = newSymNode(labl)
   else:
-    labl = newLabel(c, n)
+    labl =
+      if n[0].kind != nkEmpty:
+        n[0].sym  # already named block? -> Push symbol on the stack
+      else:
+        newLabel(c, n)
   c.breakSyms.add(labl)
   result = transformSons(c, n)
   discard c.breakSyms.pop
-  result[0] = newSymNode(labl).PTransNode
+  result[0] = newSymNode(labl)
 
-proc transformLoopBody(c: PTransf, n: PNode): PTransNode =
+proc transformLoopBody(c: PTransf, n: PNode): PNode =
   # What if it contains "continue" and "break"? "break" needs
   # an explicit label too, but not the same!
 
@@ -259,59 +283,41 @@ proc transformLoopBody(c: PTransf, n: PNode): PTransNode =
     c.contSyms.add(labl)
 
     result = newTransNode(nkBlockStmt, n.info, 2)
-    result[0] = newSymNode(labl).PTransNode
+    result[0] = newSymNode(labl)
     result[1] = transform(c, n)
     discard c.contSyms.pop()
   else:
     result = transform(c, n)
 
-proc transformWhile(c: PTransf; n: PNode): PTransNode =
+proc transformWhile(c: PTransf; n: PNode): PNode =
   if c.inlining > 0:
     result = transformSons(c, n)
   else:
     let labl = newLabel(c, n)
     c.breakSyms.add(labl)
     result = newTransNode(nkBlockStmt, n.info, 2)
-    result[0] = newSymNode(labl).PTransNode
+    result[0] = newSymNode(labl)
 
     var body = newTransNode(n)
-    for i in 0..n.len-2:
-      body[i] = transform(c, n.sons[i])
-    body[n.len-1] = transformLoopBody(c, n.sons[n.len-1])
+    for i in 0..<n.len-1:
+      body[i] = transform(c, n[i])
+    body[^1] = transformLoopBody(c, n[^1])
     result[1] = body
     discard c.breakSyms.pop
 
-proc transformBreak(c: PTransf, n: PNode): PTransNode =
-  if n.sons[0].kind != nkEmpty or c.inlining > 0:
-    result = n.PTransNode
-    when false:
-      let lablCopy = idNodeTableGet(c.transCon.mapping, n.sons[0].sym)
-      if lablCopy.isNil:
-        result = n.PTransNode
-      else:
-        result = newTransNode(n.kind, n.info, 1)
-        result[0] = lablCopy.PTransNode
-  elif c.breakSyms.len > 0:
-    # this check can fail for 'nim check'
+proc transformBreak(c: PTransf, n: PNode): PNode =
+  result = transformSons(c, n)
+  if n[0].kind == nkEmpty and c.breakSyms.len > 0:
     let labl = c.breakSyms[c.breakSyms.high]
-    result = transformSons(c, n)
-    result[0] = newSymNode(labl).PTransNode
-  else:
-    result = n.PTransNode
+    result[0] = newSymNode(labl)
 
-proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) =
-  # XXX: BUG: what if `n` is an expression with side-effects?
-  for i in countup(0, sonsLen(c.transCon.forStmt) - 3):
-    add(father, newAsgnStmt(c, c.transCon.forStmt.sons[i],
-        transform(c, newTupleAccess(c.graph, n, i))))
-
-proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode =
+proc introduceNewLocalVars(c: PTransf, n: PNode): PNode =
   case n.kind
   of nkSym:
     result = transformSym(c, n)
   of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
     # nothing to be done for leaves:
-    result = PTransNode(n)
+    result = n
   of nkVarSection, nkLetSection:
     result = transformVarSection(c, n)
   of nkClosure:
@@ -320,69 +326,201 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode =
     # (bug #2604). We need to patch this environment here too:
     let a = n[1]
     if a.kind == nkSym:
-      n.sons[1] = transformSymAux(c, a)
-    return PTransNode(n)
+      n[1] = transformSymAux(c, a)
+    return n
+  of nkProcDef: # todo optimize nosideeffects?
+    result = newTransNode(n)
+    let x = newSymNode(copySym(n[namePos].sym, c.idgen))
+    c.transCon.mapping[n[namePos].sym.itemId] = x
+    result[namePos] = x # we have to copy proc definitions for iters
+    for i in 1..<n.len:
+      result[i] = introduceNewLocalVars(c, n[i])
+    result[namePos].sym.ast = result
   else:
     result = newTransNode(n)
-    for i in countup(0, sonsLen(n)-1):
-      result[i] = introduceNewLocalVars(c, n.sons[i])
-
-proc transformYield(c: PTransf, n: PNode): PTransNode =
+    for i in 0..<n.len:
+      result[i] = introduceNewLocalVars(c, n[i])
+
+proc transformAsgn(c: PTransf, n: PNode): PNode =
+  let rhs = n[1]
+
+  if rhs.kind != nkTupleConstr:
+    return transformSons(c, n)
+
+  # Unpack the tuple assignment into N temporary variables and then pack them
+  # into a tuple: this allows us to get the correct results even when the rhs
+  # depends on the value of the lhs
+  let letSection = newTransNode(nkLetSection, n.info, rhs.len)
+  let newTupleConstr = newTransNode(nkTupleConstr, n.info, rhs.len)
+  for i, field in rhs:
+    let val = if field.kind == nkExprColonExpr: field[1] else: field
+    let def = newTransNode(nkIdentDefs, field.info, 3)
+    def[0] = newTemp(c, val.typ, field.info)
+    def[1] = newNodeI(nkEmpty, field.info)
+    def[2] = transform(c, val)
+    letSection[i] = def
+    # NOTE: We assume the constructor fields are in the correct order for the
+    # given tuple type
+    newTupleConstr[i] = def[0]
+
+  newTupleConstr.typ = rhs.typ
+
+  let asgnNode = newTransNode(nkAsgn, n.info, 2)
+  asgnNode[0] = transform(c, n[0])
+  asgnNode[1] = newTupleConstr
+
+  result = newTransNode(nkStmtList, n.info, 2)
+  result[0] = letSection
+  result[1] = asgnNode
+
+proc transformYield(c: PTransf, n: PNode): PNode =
+  proc asgnTo(lhs: PNode, rhs: PNode): PNode =
+    # Choose the right assignment instruction according to the given ``lhs``
+    # node since it may not be a nkSym (a stack-allocated skForVar) but a
+    # nkDotExpr (a heap-allocated slot into the envP block)
+    case lhs.kind
+    of nkSym:
+      internalAssert c.graph.config, lhs.sym.kind == skForVar
+      result = newAsgnStmt(c, nkFastAsgn, lhs, rhs, false)
+    of nkDotExpr:
+      result = newAsgnStmt(c, nkAsgn, lhs, rhs, false)
+    else:
+      result = nil
+      internalAssert c.graph.config, false
   result = newTransNode(nkStmtList, n.info, 0)
-  var e = n.sons[0]
+  var e = n[0]
   # c.transCon.forStmt.len == 3 means that there is one for loop variable
   # and thus no tuple unpacking:
   if e.typ.isNil: return result # can happen in nimsuggest for unknown reasons
-  if skipTypes(e.typ, {tyGenericInst, tyAlias, tySink}).kind == tyTuple and
-      c.transCon.forStmt.len != 3:
+  if c.transCon.forStmt.len != 3:
     e = skipConv(e)
-    if e.kind in {nkPar, nkTupleConstr}:
-      for i in countup(0, sonsLen(e) - 1):
-        var v = e.sons[i]
-        if v.kind == nkExprColonExpr: v = v.sons[1]
-        add(result, newAsgnStmt(c, c.transCon.forStmt.sons[i],
-                                transform(c, v)))
+    if e.kind == nkTupleConstr:
+      for i in 0..<e.len:
+        var v = e[i]
+        if v.kind == nkExprColonExpr: v = v[1]
+        if c.transCon.forStmt[i].kind == nkVarTuple:
+          for j in 0..<c.transCon.forStmt[i].len-1:
+            let lhs = c.transCon.forStmt[i][j]
+            let rhs = transform(c, newTupleAccess(c.graph, v, j))
+            result.add(asgnTo(lhs, rhs))
+        else:
+          let lhs = c.transCon.forStmt[i]
+          let rhs = transform(c, v)
+          result.add(asgnTo(lhs, rhs))
+    elif e.kind notin {nkAddr, nkHiddenAddr}: # no need to generate temp for address operation
+      # TODO do not use temp for nodes which cannot have side-effects
+      var tmp = newTemp(c, e.typ, e.info)
+      let v = newNodeI(nkVarSection, e.info)
+      v.addVar(tmp, e)
+
+      result.add transform(c, v)
+
+      for i in 0..<c.transCon.forStmt.len - 2:
+        if c.transCon.forStmt[i].kind == nkVarTuple:
+          for j in 0..<c.transCon.forStmt[i].len-1:
+            let lhs = c.transCon.forStmt[i][j]
+            let rhs = transform(c, newTupleAccess(c.graph, newTupleAccess(c.graph, tmp, i), j))
+            result.add(asgnTo(lhs, rhs))
+        else:
+          let lhs = c.transCon.forStmt[i]
+          let rhs = transform(c, newTupleAccess(c.graph, tmp, i))
+          result.add(asgnTo(lhs, rhs))
     else:
-      unpackTuple(c, e, result)
+      for i in 0..<c.transCon.forStmt.len - 2:
+        let lhs = c.transCon.forStmt[i]
+        let rhs = transform(c, newTupleAccess(c.graph, e, i))
+        result.add(asgnTo(lhs, rhs))
   else:
-    var x = transform(c, e)
-    add(result, newAsgnStmt(c, c.transCon.forStmt.sons[0], x))
+    if c.transCon.forStmt[0].kind == nkVarTuple:
+      var notLiteralTuple = false # we don't generate temp for tuples with const value: (1, 2, 3)
+      let ev = e.skipConv
+      if ev.kind == nkTupleConstr:
+        for i in ev:
+          if not isConstExpr(i):
+            notLiteralTuple = true
+            break
+      else:
+        notLiteralTuple = true
+
+      if e.kind notin {nkAddr, nkHiddenAddr} and notLiteralTuple:
+        # TODO do not use temp for nodes which cannot have side-effects
+        var tmp = newTemp(c, e.typ, e.info)
+        let v = newNodeI(nkVarSection, e.info)
+        v.addVar(tmp, e)
+
+        result.add transform(c, v)
+        for i in 0..<c.transCon.forStmt[0].len-1:
+          let lhs = c.transCon.forStmt[0][i]
+          let rhs = transform(c, newTupleAccess(c.graph, tmp, i))
+          result.add(asgnTo(lhs, rhs))
+      else:
+        for i in 0..<c.transCon.forStmt[0].len-1:
+          let lhs = c.transCon.forStmt[0][i]
+          let rhs = transform(c, newTupleAccess(c.graph, e, i))
+          result.add(asgnTo(lhs, rhs))
+    else:
+      let lhs = c.transCon.forStmt[0]
+      let rhs = transform(c, e)
+      result.add(asgnTo(lhs, rhs))
+
+
+  # bug #23536; note that the info of forLoopBody should't change
+  for idx in 0 ..< result.len:
+    var changeNode = result[idx]
+    changeNode.info = c.transCon.forStmt.info
+    for i, child in changeNode:
+      child.info = changeNode.info
 
   inc(c.transCon.yieldStmts)
   if c.transCon.yieldStmts <= 1:
     # common case
-    add(result, c.transCon.forLoopBody)
+    result.add(c.transCon.forLoopBody)
   else:
     # we need to introduce new local variables:
-    add(result, introduceNewLocalVars(c, c.transCon.forLoopBody.PNode))
+    c.isIntroducingNewLocalVars = true # don't transform yields when introducing new local vars
+    result.add(introduceNewLocalVars(c, c.transCon.forLoopBody))
+    c.isIntroducingNewLocalVars = false
 
-proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
+proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode =
   result = transformSons(c, n)
-  if c.graph.config.cmd == cmdCompileToCpp or sfCompileToCpp in c.module.flags: return
-  var n = result.PNode
-  case n.sons[0].kind
+  # inlining of 'var openarray' iterators; bug #19977
+  if n.typ.kind != tyOpenArray and (c.graph.config.backend == backendCpp or sfCompileToCpp in c.module.flags): return
+  var n = result
+  case n[0].kind
   of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
-    var m = n.sons[0].sons[0]
-    if m.kind == a or m.kind == b:
+    var m = n[0][0]
+    if m.kind in kinds:
       # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
-      n.sons[0].sons[0] = m.sons[0]
-      result = PTransNode(n.sons[0])
+      n[0][0] = m[0]
+      result = n[0]
       if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
-        PNode(result).typ = n.typ
+        result.typ = n.typ
+      elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
+        result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen)
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
-    var m = n.sons[0].sons[1]
-    if m.kind == a or m.kind == b:
+    var m = n[0][1]
+    if m.kind in kinds:
       # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
-      n.sons[0].sons[1] = m.sons[0]
-      result = PTransNode(n.sons[0])
+      n[0][1] = m[0]
+      result = n[0]
       if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
-        PNode(result).typ = n.typ
+        result.typ = n.typ
+      elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
+        result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen)
   else:
-    if n.sons[0].kind == a or n.sons[0].kind == b:
+    if n[0].kind in kinds and
+        not (n[0][0].kind == nkSym and n[0][0].sym.kind == skForVar and
+          n[0][0].typ.skipTypes(abstractVar).kind == tyTuple
+        ) and not (n[0][0].kind == nkSym and n[0][0].sym.kind == skParam and
+          n.typ.kind == tyVar and
+          n.typ.skipTypes(abstractVar).kind == tyOpenArray and
+          n[0][0].typ.skipTypes(abstractVar).kind == tyString)
+        : # elimination is harmful to `for tuple unpack` because of newTupleAccess
+          # it is also harmful to openArrayLoc (var openArray) for strings
       # addr ( deref ( x )) --> x
-      result = PTransNode(n.sons[0].sons[0])
+      result = n[0][0]
       if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
-        PNode(result).typ = n.typ
+        result.typ = n.typ
 
 proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode =
   ## Converts 'prc' into '(thunk, nil)' so that it's compatible with
@@ -390,7 +528,7 @@ proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode =
 
   # we cannot generate a proper thunk here for GC-safety reasons
   # (see internal documentation):
-  if c.graph.config.cmd == cmdCompileToJS: return prc
+  if jsNoLambdaLifting in c.graph.config.legacyFeatures and c.graph.config.backend == backendJs: return prc
   result = newNodeIT(nkClosure, prc.info, dest)
   var conv = newNodeIT(nkHiddenSubConv, prc.info, dest)
   conv.add(newNodeI(nkEmpty, prc.info))
@@ -400,18 +538,18 @@ proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode =
   result.add(conv)
   result.add(newNodeIT(nkNilLit, prc.info, getSysType(c.graph, prc.info, tyNil)))
 
-proc transformConv(c: PTransf, n: PNode): PTransNode =
+proc transformConv(c: PTransf, n: PNode): PNode =
   # numeric types need range checks:
   var dest = skipTypes(n.typ, abstractVarRange)
-  var source = skipTypes(n.sons[1].typ, abstractVarRange)
+  var source = skipTypes(n[1].typ, abstractVarRange)
   case dest.kind
-  of tyInt..tyInt64, tyEnum, tyChar, tyBool, tyUInt8..tyUInt32:
+  of tyInt..tyInt64, tyEnum, tyChar, tyUInt8..tyUInt32:
     # we don't include uint and uint64 here as these are no ordinal types ;-)
     if not isOrdinalType(source):
       # float -> int conversions. ugh.
       result = transformSons(c, n)
-    elif firstOrd(c.graph.config, n.typ) <= firstOrd(c.graph.config, n.sons[1].typ) and
-        lastOrd(c.graph.config, n.sons[1].typ) <= lastOrd(c.graph.config, n.typ):
+    elif firstOrd(c.graph.config, n.typ) <= firstOrd(c.graph.config, n[1].typ) and
+        lastOrd(c.graph.config, n[1].typ) <= lastOrd(c.graph.config, n.typ):
       # BUGFIX: simply leave n as it is; we need a nkConv node,
       # but no range check:
       result = transformSons(c, n)
@@ -422,34 +560,38 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
       else:
         result = newTransNode(nkChckRange, n, 3)
       dest = skipTypes(n.typ, abstractVar)
-      result[0] = transform(c, n.sons[1])
-      result[1] = newIntTypeNode(nkIntLit, firstOrd(c.graph.config, dest), dest).PTransNode
-      result[2] = newIntTypeNode(nkIntLit, lastOrd(c.graph.config, dest), dest).PTransNode
+      result[0] = transform(c, n[1])
+      result[1] = newIntTypeNode(firstOrd(c.graph.config, dest), dest)
+      result[2] = newIntTypeNode(lastOrd(c.graph.config, dest), dest)
   of tyFloat..tyFloat128:
     # XXX int64 -> float conversion?
     if skipTypes(n.typ, abstractVar).kind == tyRange:
       result = newTransNode(nkChckRangeF, n, 3)
       dest = skipTypes(n.typ, abstractVar)
-      result[0] = transform(c, n.sons[1])
-      result[1] = copyTree(dest.n.sons[0]).PTransNode
-      result[2] = copyTree(dest.n.sons[1]).PTransNode
+      result[0] = transform(c, n[1])
+      result[1] = copyTree(dest.n[0])
+      result[2] = copyTree(dest.n[1])
     else:
       result = transformSons(c, n)
   of tyOpenArray, tyVarargs:
-    result = transform(c, n.sons[1])
-    PNode(result).typ = takeType(n.typ, n.sons[1].typ)
-    #echo n.info, " came here and produced ", typeToString(PNode(result).typ),
-    #   " from ", typeToString(n.typ), " and ", typeToString(n.sons[1].typ)
-  of tyCString:
+    if keepOpenArrayConversions in c.flags:
+      result = transformSons(c, n)
+    else:
+      result = transform(c, n[1])
+      #result = transformSons(c, n)
+      result.typ = takeType(n.typ, n[1].typ, c.graph, c.idgen)
+      #echo n.info, " came here and produced ", typeToString(result.typ),
+      #   " from ", typeToString(n.typ), " and ", typeToString(n[1].typ)
+  of tyCstring:
     if source.kind == tyString:
       result = newTransNode(nkStringToCString, n, 1)
-      result[0] = transform(c, n.sons[1])
+      result[0] = transform(c, n[1])
     else:
       result = transformSons(c, n)
   of tyString:
-    if source.kind == tyCString:
+    if source.kind == tyCstring:
       result = newTransNode(nkCStringToString, n, 1)
-      result[0] = transform(c, n.sons[1])
+      result[0] = transform(c, n[1])
     else:
       result = transformSons(c, n)
   of tyRef, tyPtr:
@@ -459,91 +601,142 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
       var diff = inheritanceDiff(dest, source)
       if diff < 0:
         result = newTransNode(nkObjUpConv, n, 1)
-        result[0] = transform(c, n.sons[1])
+        result[0] = transform(c, n[1])
       elif diff > 0 and diff != high(int):
         result = newTransNode(nkObjDownConv, n, 1)
-        result[0] = transform(c, n.sons[1])
+        result[0] = transform(c, n[1])
       else:
-        result = transform(c, n.sons[1])
+        result = transform(c, n[1])
+        result.typ = n.typ
     else:
       result = transformSons(c, n)
   of tyObject:
     var diff = inheritanceDiff(dest, source)
     if diff < 0:
       result = newTransNode(nkObjUpConv, n, 1)
-      result[0] = transform(c, n.sons[1])
+      result[0] = transform(c, n[1])
     elif diff > 0 and diff != high(int):
       result = newTransNode(nkObjDownConv, n, 1)
-      result[0] = transform(c, n.sons[1])
+      result[0] = transform(c, n[1])
     else:
-      result = transform(c, n.sons[1])
+      result = transform(c, n[1])
+      result.typ = n.typ
   of tyGenericParam, tyOrdinal:
-    result = transform(c, n.sons[1])
+    result = transform(c, n[1])
     # happens sometimes for generated assignments, etc.
   of tyProc:
     result = transformSons(c, n)
-    if dest.callConv == ccClosure and source.callConv == ccDefault:
-      result = generateThunk(c, result[1].PNode, dest).PTransNode
+    if dest.callConv == ccClosure and source.callConv == ccNimCall:
+      result = generateThunk(c, result[1], dest)
   else:
     result = transformSons(c, n)
 
 type
   TPutArgInto = enum
-    paDirectMapping, paFastAsgn, paVarAsgn, paComplexOpenarray
+    paDirectMapping, paFastAsgn, paFastAsgnTakeTypeFromArg
+    paVarAsgn, paComplexOpenarray, paViaIndirection
 
 proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
   # This analyses how to treat the mapping "formal <-> arg" in an
   # inline context.
+  if formal.kind == tyTypeDesc: return paDirectMapping
   if skipTypes(formal, abstractInst).kind in {tyOpenArray, tyVarargs}:
-    if arg.kind == nkStmtListExpr:
+    case arg.kind
+    of nkStmtListExpr:
       return paComplexOpenarray
-    return paDirectMapping    # XXX really correct?
-                              # what if ``arg`` has side-effects?
+    of nkBracket:
+      return paFastAsgnTakeTypeFromArg
+    else:
+      # XXX incorrect, causes #13417 when `arg` has side effects.
+      return paDirectMapping
   case arg.kind
   of nkEmpty..nkNilLit:
     result = paDirectMapping
-  of nkPar, nkTupleConstr, nkCurly, nkBracket:
-    result = paFastAsgn
-    for i in countup(0, sonsLen(arg) - 1):
-      if putArgInto(arg.sons[i], formal) != paDirectMapping: return
+  of nkDotExpr, nkDerefExpr, nkHiddenDeref:
+    result = putArgInto(arg[0], formal)
+  of nkAddr, nkHiddenAddr:
+    result = putArgInto(arg[0], formal)
+    if result == paViaIndirection: result = paFastAsgn
+  of nkCurly, nkBracket:
+    for i in 0..<arg.len:
+      if putArgInto(arg[i], formal) != paDirectMapping:
+        return paFastAsgn
+    result = paDirectMapping
+  of nkPar, nkTupleConstr, nkObjConstr:
+    for i in 0..<arg.len:
+      let a = if arg[i].kind == nkExprColonExpr: arg[i][1]
+              else: arg[0]
+      if putArgInto(a, formal) != paDirectMapping:
+        return paFastAsgn
     result = paDirectMapping
+  of nkBracketExpr:
+    if skipTypes(formal, abstractInst).kind in {tyVar, tyLent}: result = paVarAsgn
+    else: result = paViaIndirection
   else:
     if skipTypes(formal, abstractInst).kind in {tyVar, tyLent}: result = paVarAsgn
     else: result = paFastAsgn
 
 proc findWrongOwners(c: PTransf, n: PNode) =
   if n.kind == nkVarSection:
-    let x = n.sons[0].sons[0]
+    let x = n[0][0]
     if x.kind == nkSym and x.sym.owner != getCurrOwner(c):
       internalError(c.graph.config, x.info, "bah " & x.sym.name.s & " " &
         x.sym.owner.name.s & " " & getCurrOwner(c).name.s)
   else:
-    for i in 0 ..< safeLen(n): findWrongOwners(c, n.sons[i])
+    for i in 0..<n.safeLen: findWrongOwners(c, n[i])
+
+proc isSimpleIteratorVar(c: PTransf; iter: PSym; call: PNode; owner: PSym): bool =
+  proc rec(n: PNode; owner: PSym; dangerousYields: var int) =
+    case n.kind
+    of nkEmpty..nkNilLit: discard
+    of nkYieldStmt:
+      if n[0].kind == nkSym and n[0].sym.owner == owner:
+        discard "good: yield a single variable that we own"
+      else:
+        inc dangerousYields
+    else:
+      for c in n: rec(c, owner, dangerousYields)
+
+  proc recSym(n: PNode; owner: PSym; sameOwner: var bool) =
+    case n.kind
+    of {nkEmpty..nkNilLit} - {nkSym}: discard
+    of nkSym:
+      if n.sym.owner != owner:
+        sameOwner = false
+    else:
+      for c in n: recSym(c, owner, sameOwner)
+
+  var dangerousYields = 0
+  rec(getBody(c.graph, iter), iter, dangerousYields)
+  result = dangerousYields == 0
+  # the parameters should be owned by the owner
+  # bug #22237
+  for i in 1..<call.len:
+    recSym(call[i], owner, result)
+
+template destructor(t: PType): PSym = getAttachedOp(c.graph, t, attachedDestructor)
 
-proc transformFor(c: PTransf, n: PNode): PTransNode =
+proc transformFor(c: PTransf, n: PNode): PNode =
   # generate access statements for the parameters (unless they are constant)
   # put mapping from formal parameters to actual parameters
   if n.kind != nkForStmt: internalError(c.graph.config, n.info, "transformFor")
 
-  var length = sonsLen(n)
-  var call = n.sons[length - 2]
+  var call = n[^2]
 
   let labl = newLabel(c, n)
   result = newTransNode(nkBlockStmt, n.info, 2)
-  result[0] = newSymNode(labl).PTransNode
+  result[0] = newSymNode(labl)
   if call.typ.isNil:
     # see bug #3051
-    result[1] = newNode(nkEmpty).PTransNode
+    result[1] = newNode(nkEmpty)
     return result
   c.breakSyms.add(labl)
-  if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
-      call.sons[0].typ.callConv == ccClosure:
-    n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
-    if not c.tooEarly:
-      n.sons[length-2] = transform(c, n.sons[length-2]).PNode
-      result[1] = lambdalifting.liftForLoop(c.graph, n, getCurrOwner(c)).PTransNode
-    else:
-      result[1] = newNode(nkEmpty).PTransNode
+  if call.kind notin nkCallKinds or call[0].kind != nkSym or
+      call[0].typ.skipTypes(abstractInst).callConv == ccClosure:
+    result[1] = n
+    result[1][^1] = transformLoopBody(c, n[^1])
+    result[1][^2] = transform(c, n[^2])
+    result[1] = lambdalifting.liftForLoop(c.graph, result[1], c.idgen, getCurrOwner(c))
     discard c.breakSyms.pop
     return result
 
@@ -551,18 +744,26 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
   var stmtList = newTransNode(nkStmtList, n.info, 0)
   result[1] = stmtList
 
-  var loopBody = transformLoopBody(c, n.sons[length-1])
+  var loopBody = transformLoopBody(c, n[^1])
 
   discard c.breakSyms.pop
 
+  let iter = call[0].sym
+
   var v = newNodeI(nkVarSection, n.info)
-  for i in countup(0, length - 3):
-    addVar(v, copyTree(n.sons[i])) # declare new vars
-  add(stmtList, v.PTransNode)
+  for i in 0..<n.len - 2:
+    if n[i].kind == nkVarTuple:
+      for j in 0..<n[i].len-1:
+        addVar(v, copyTree(n[i][j])) # declare new vars
+    else:
+      if n[i].kind == nkSym and isSimpleIteratorVar(c, iter, call, n[i].sym.owner):
+        incl n[i].sym.flags, sfCursor
+      addVar(v, copyTree(n[i])) # declare new vars
+  stmtList.add(v)
+
 
   # Bugfix: inlined locals belong to the invoking routine, not to the invoked
   # iterator!
-  let iter = call.sons[0].sym
   var newC = newTransCon(getCurrOwner(c))
   newC.forStmt = n
   newC.forLoopBody = loopBody
@@ -570,217 +771,246 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
   if iter.kind != skIterator: return result
   # generate access statements for the parameters (unless they are constant)
   pushTransCon(c, newC)
-  for i in countup(1, sonsLen(call) - 1):
-    var arg = transform(c, call.sons[i]).PNode
+  for i in 1..<call.len:
+    var arg = transform(c, call[i])
     let ff = skipTypes(iter.typ, abstractInst)
     # can happen for 'nim check':
     if i >= ff.n.len: return result
-    var formal = ff.n.sons[i].sym
-    case putArgInto(arg, formal.typ)
+    var formal = ff.n[i].sym
+    let pa = putArgInto(arg, formal.typ)
+    case pa
     of paDirectMapping:
-      idNodeTablePut(newC.mapping, formal, arg)
-    of paFastAsgn:
+      newC.mapping[formal.itemId] = arg
+    of paFastAsgn, paFastAsgnTakeTypeFromArg:
+      var t = formal.typ
+      if pa == paFastAsgnTakeTypeFromArg:
+        t = arg.typ
+      elif formal.ast != nil and formal.ast.typ.destructor != nil and t.destructor == nil:
+        t = formal.ast.typ # better use the type that actually has a destructor.
+      elif t.destructor == nil and arg.typ.destructor != nil:
+        t = arg.typ
       # generate a temporary and produce an assignment statement:
-      var temp = newTemp(c, formal.typ, formal.info)
+      var temp = newTemp(c, t, formal.info)
+      #incl(temp.sym.flags, sfCursor)
       addVar(v, temp)
-      add(stmtList, newAsgnStmt(c, temp, arg.PTransNode))
-      idNodeTablePut(newC.mapping, formal, temp)
+      stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg, true))
+      newC.mapping[formal.itemId] = temp
     of paVarAsgn:
-      assert(skipTypes(formal.typ, abstractInst).kind == tyVar)
-      idNodeTablePut(newC.mapping, formal, arg)
+      assert(skipTypes(formal.typ, abstractInst).kind in {tyVar, tyLent})
+      newC.mapping[formal.itemId] = arg
       # XXX BUG still not correct if the arg has a side effect!
+    of paViaIndirection:
+      let t = formal.typ
+      let vt = makeVarType(t.owner, t, c.idgen)
+      vt.flags.incl tfVarIsPtr
+      var temp = newTemp(c, vt, formal.info)
+      addVar(v, temp)
+      var addrExp = newNodeIT(nkHiddenAddr, formal.info, makeVarType(t.owner, t, c.idgen, tyPtr))
+      addrExp.add(arg)
+      stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, addrExp, true))
+      newC.mapping[formal.itemId] = newDeref(temp)
     of paComplexOpenarray:
-      let typ = newType(tySequence, formal.owner)
-      addSonSkipIntLit(typ, formal.typ.sons[0])
-      var temp = newTemp(c, typ, formal.info)
+      # arrays will deep copy here (pretty bad).
+      var temp = newTemp(c, arg.typ, formal.info)
       addVar(v, temp)
-      add(stmtList, newAsgnStmt(c, temp, arg.PTransNode))
-      idNodeTablePut(newC.mapping, formal, temp)
+      stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg, true))
+      newC.mapping[formal.itemId] = temp
 
-  var body = iter.getBody.copyTree
+  let body = transformBody(c.graph, c.idgen, iter, {useCache}+c.flags)
   pushInfoContext(c.graph.config, n.info)
-  # XXX optimize this somehow. But the check "c.inlining" is not correct:
-  var symMap: TIdTable
-  initIdTable symMap
-  freshLabels(c, body, symMap)
-
   inc(c.inlining)
-  add(stmtList, transform(c, body))
-  #findWrongOwners(c, stmtList.pnode)
+  stmtList.add(transform(c, body))
+  #findWrongOwners(c, stmtList.PNode)
   dec(c.inlining)
   popInfoContext(c.graph.config)
   popTransCon(c)
-  # echo "transformed: ", stmtList.PNode.renderTree
+  # echo "transformed: ", stmtList.renderTree
 
-proc transformCase(c: PTransf, n: PNode): PTransNode =
+proc transformCase(c: PTransf, n: PNode): PNode =
   # removes `elif` branches of a case stmt
   # adds ``else: nil`` if needed for the code generator
   result = newTransNode(nkCaseStmt, n, 0)
-  var ifs = PTransNode(nil)
-  for i in 0 .. sonsLen(n)-1:
-    var it = n.sons[i]
+  var ifs: PNode = nil
+  for it in n:
     var e = transform(c, it)
     case it.kind
     of nkElifBranch:
-      if ifs.PNode == nil:
-        ifs = newTransNode(nkIfStmt, it.info, 0)
+      if ifs == nil:
+        # Generate the right node depending on whether `n` is used as a stmt or
+        # as an expr
+        let kind = if n.typ != nil: nkIfExpr else: nkIfStmt
+        ifs = newTransNode(kind, it.info, 0)
+        ifs.typ = n.typ
       ifs.add(e)
     of nkElse:
-      if ifs.PNode == nil: result.add(e)
+      if ifs == nil: result.add(e)
       else: ifs.add(e)
     else:
       result.add(e)
-  if ifs.PNode != nil:
+  if ifs != nil:
     var elseBranch = newTransNode(nkElse, n.info, 1)
     elseBranch[0] = ifs
     result.add(elseBranch)
-  elif result.PNode.lastSon.kind != nkElse and not (
-      skipTypes(n.sons[0].typ, abstractVarRange).kind in
-        {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32}):
+  elif result.lastSon.kind != nkElse and not (
+      skipTypes(n[0].typ, abstractVarRange).kind in
+        {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt64}):
     # fix a stupid code gen bug by normalizing:
     var elseBranch = newTransNode(nkElse, n.info, 1)
     elseBranch[0] = newTransNode(nkNilLit, n.info, 0)
-    add(result, elseBranch)
+    result.add(elseBranch)
 
-proc transformArrayAccess(c: PTransf, n: PNode): PTransNode =
+proc transformArrayAccess(c: PTransf, n: PNode): PNode =
   # XXX this is really bad; transf should use a proper AST visitor
-  if n.sons[0].kind == nkSym and n.sons[0].sym.kind == skType:
-    result = n.PTransNode
+  if n[0].kind == nkSym and n[0].sym.kind == skType:
+    result = n
   else:
     result = newTransNode(n)
-    for i in 0 ..< n.len:
-      result[i] = transform(c, skipConv(n.sons[i]))
+    for i in 0..<n.len:
+      result[i] = transform(c, skipConv(n[i]))
 
 proc getMergeOp(n: PNode): PSym =
   case n.kind
   of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
      nkCallStrLit:
-    if n.sons[0].kind == nkSym and n.sons[0].sym.magic == mConStrStr:
-      result = n.sons[0].sym
-  else: discard
+    if n[0].kind == nkSym and n[0].sym.magic == mConStrStr:
+      result = n[0].sym
+    else:
+      result = nil
+  else: result = nil
 
 proc flattenTreeAux(d, a: PNode, op: PSym) =
+  ## Optimizes away the `&` calls in the children nodes and
+  ## lifts the leaf nodes to the same level as `op2`.
   let op2 = getMergeOp(a)
   if op2 != nil and
       (op2.id == op.id or op.magic != mNone and op2.magic == op.magic):
-    for i in countup(1, sonsLen(a)-1): flattenTreeAux(d, a.sons[i], op)
+    for i in 1..<a.len: flattenTreeAux(d, a[i], op)
   else:
-    addSon(d, copyTree(a))
+    d.add copyTree(a)
 
 proc flattenTree(root: PNode): PNode =
   let op = getMergeOp(root)
   if op != nil:
     result = copyNode(root)
-    addSon(result, copyTree(root.sons[0]))
+    result.add copyTree(root[0])
     flattenTreeAux(result, root, op)
   else:
     result = root
 
-proc transformCall(c: PTransf, n: PNode): PTransNode =
+proc transformCall(c: PTransf, n: PNode): PNode =
   var n = flattenTree(n)
   let op = getMergeOp(n)
   let magic = getMagic(n)
   if op != nil and op.magic != mNone and n.len >= 3:
     result = newTransNode(nkCall, n, 0)
-    add(result, transform(c, n.sons[0]))
+    result.add(transform(c, n[0]))
     var j = 1
-    while j < sonsLen(n):
-      var a = transform(c, n.sons[j]).PNode
+    while j < n.len:
+      var a = transform(c, n[j])
       inc(j)
       if isConstExpr(a):
-        while (j < sonsLen(n)):
-          let b = transform(c, n.sons[j]).PNode
+        while (j < n.len):
+          let b = transform(c, n[j])
           if not isConstExpr(b): break
-          a = evalOp(op.magic, n, a, b, nil, c.graph)
+          a = evalOp(op.magic, n, a, b, nil, c.idgen, c.graph)
           inc(j)
-      add(result, a.PTransNode)
-    if len(result) == 2: result = result[1]
+      result.add(a)
+    if result.len == 2: result = result[1]
   elif magic in {mNBindSym, mTypeOf, mRunnableExamples}:
     # for bindSym(myconst) we MUST NOT perform constant folding:
-    result = n.PTransNode
+    result = n
   elif magic == mProcCall:
     # but do not change to its dispatcher:
     result = transformSons(c, n[1])
+  elif magic == mStrToStr:
+    result = transform(c, n[1])
   else:
-    let s = transformSons(c, n).PNode
+    let s = transformSons(c, n)
     # bugfix: check after 'transformSons' if it's still a method call:
     # use the dispatcher for the call:
-    if s.sons[0].kind == nkSym and s.sons[0].sym.kind == skMethod:
+    if s[0].kind == nkSym and s[0].sym.kind == skMethod:
       when false:
-        let t = lastSon(s.sons[0].sym.ast)
+        let t = lastSon(s[0].sym.ast)
         if t.kind != nkSym or sfDispatcher notin t.sym.flags:
-          methodDef(s.sons[0].sym, false)
-      result = methodCall(s, c.graph.config).PTransNode
+          methodDef(s[0].sym, false)
+      result = methodCall(s, c.graph.config)
     else:
-      result = s.PTransNode
+      result = s
 
-proc transformExceptBranch(c: PTransf, n: PNode): PTransNode =
-  result = transformSons(c, n)
+proc transformExceptBranch(c: PTransf, n: PNode): PNode =
   if n[0].isInfixAs() and not isImportedException(n[0][1].typ, c.graph.config):
     let excTypeNode = n[0][1]
     let actions = newTransNode(nkStmtListExpr, n[1], 2)
     # Generating `let exc = (excType)(getCurrentException())`
     # -> getCurrentException()
-    let excCall = PTransNode(callCodegenProc(c.graph, "getCurrentException", newNodeI(nkEmpty, n.info)))
+    let excCall = callCodegenProc(c.graph, "getCurrentException")
     # -> (excType)
     let convNode = newTransNode(nkHiddenSubConv, n[1].info, 2)
-    convNode[0] = PTransNode(newNodeI(nkEmpty, n.info))
+    convNode[0] = newNodeI(nkEmpty, n.info)
     convNode[1] = excCall
-    PNode(convNode).typ = excTypeNode.typ.toRef()
+    convNode.typ = excTypeNode.typ.toRef(c.idgen)
     # -> let exc = ...
     let identDefs = newTransNode(nkIdentDefs, n[1].info, 3)
-    identDefs[0] = PTransNode(n[0][2])
-    identDefs[1] = PTransNode(newNodeI(nkEmpty, n.info))
+    identDefs[0] = n[0][2]
+    identDefs[1] = newNodeI(nkEmpty, n.info)
     identDefs[2] = convNode
 
     let letSection = newTransNode(nkLetSection, n[1].info, 1)
     letSection[0] = identDefs
     # Place the let statement and body of the 'except' branch into new stmtList.
     actions[0] = letSection
-    actions[1] = transformSons(c, n[1])
+    actions[1] = transform(c, n[1])
     # Overwrite 'except' branch body with our stmtList.
-    result[1] = actions
-
+    result = newTransNode(nkExceptBranch, n[1].info, 2)
     # Replace the `Exception as foobar` with just `Exception`.
-    result[0] = result[0][1]
-
-proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} =
-  # symbols that expand to a complex constant (array, etc.) should not be
-  # inlined, unless it's the empty array:
-  result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket} and
-      cnst.len != 0
+    result[0] = transform(c, n[0][1])
+    result[1] = actions
+  else:
+    result = transformSons(c, n)
 
-proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode =
+proc commonOptimizations*(g: ModuleGraph; idgen: IdGenerator; c: PSym, n: PNode): PNode =
+  ## Merges adjacent constant expressions of the children of the `&` call into
+  ## a single constant expression. It also inlines constant expressions which are not
+  ## complex.
   result = n
-  for i in 0 ..< n.safeLen:
-    result.sons[i] = commonOptimizations(g, c, n.sons[i])
+  for i in 0..<n.safeLen:
+    result[i] = commonOptimizations(g, idgen, c, n[i])
   var op = getMergeOp(n)
-  if (op != nil) and (op.magic != mNone) and (sonsLen(n) >= 3):
+  if (op != nil) and (op.magic != mNone) and (n.len >= 3):
     result = newNodeIT(nkCall, n.info, n.typ)
-    add(result, n.sons[0])
+    result.add(n[0])
     var args = newNode(nkArgList)
     flattenTreeAux(args, n, op)
     var j = 0
-    while j < sonsLen(args):
-      var a = args.sons[j]
+    while j < args.len:
+      var a = args[j]
       inc(j)
       if isConstExpr(a):
-        while j < sonsLen(args):
-          let b = args.sons[j]
+        while j < args.len:
+          let b = args[j]
           if not isConstExpr(b): break
-          a = evalOp(op.magic, result, a, b, nil, g)
+          a = evalOp(op.magic, result, a, b, nil, idgen, g)
           inc(j)
-      add(result, a)
-    if len(result) == 2: result = result[1]
+      result.add(a)
+    if result.len == 2: result = result[1]
   else:
-    var cnst = getConstExpr(c, n, g)
+    var cnst = getConstExpr(c, n, idgen, g)
     # we inline constants if they are not complex constants:
     if cnst != nil and not dontInlineConstant(n, cnst):
       result = cnst
     else:
       result = n
 
-proc transform(c: PTransf, n: PNode): PTransNode =
+proc transformDerefBlock(c: PTransf, n: PNode): PNode =
+  # We transform (block: x)[] to (block: x[])
+  let e0 = n[0]
+  result = shallowCopy(e0)
+  result.typ = n.typ
+  for i in 0 ..< e0.len - 1:
+    result[i] = e0[i]
+  result[e0.len-1] = newTreeIT(nkHiddenDeref, n.info, n.typ, e0[e0.len-1])
+
+proc transform(c: PTransf, n: PNode): PNode =
   when false:
     var oldDeferAnchor: PNode
     if n.kind in {nkElifBranch, nkOfBranch, nkExceptBranch, nkElifExpr,
@@ -788,31 +1018,29 @@ proc transform(c: PTransf, n: PNode): PTransNode =
                   nkBlockStmt, nkBlockExpr}:
       oldDeferAnchor = c.deferAnchor
       c.deferAnchor = n
-  if n.typ != nil and tfHasAsgn in n.typ.flags:
-    c.needsDestroyPass = true
   case n.kind
   of nkSym:
     result = transformSym(c, n)
   of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom:
     # nothing to be done for leaves:
-    result = PTransNode(n)
+    result = n
   of nkBracketExpr: result = transformArrayAccess(c, n)
   of procDefs:
-    var s = n.sons[namePos].sym
+    var s = n[namePos].sym
     if n.typ != nil and s.typ.callConv == ccClosure:
-      result = transformSym(c, n.sons[namePos])
+      result = transformSym(c, n[namePos])
       # use the same node as before if still a symbol:
-      if result.PNode.kind == nkSym: result = PTransNode(n)
+      if result.kind == nkSym: result = n
     else:
-      result = PTransNode(n)
+      result = n
   of nkMacroDef:
     # XXX no proper closure support yet:
     when false:
-      if n.sons[genericParamsPos].kind == nkEmpty:
-        var s = n.sons[namePos].sym
-        n.sons[bodyPos] = PNode(transform(c, s.getBody))
+      if n[genericParamsPos].kind == nkEmpty:
+        var s = n[namePos].sym
+        n[bodyPos] = transform(c, s.getBody)
         if n.kind == nkMethodDef: methodDef(s, false)
-    result = PTransNode(n)
+    result = n
   of nkForStmt:
     result = transformFor(c, n)
   of nkParForStmt:
@@ -827,50 +1055,62 @@ proc transform(c: PTransf, n: PNode): PTransNode =
     result = transformSons(c, n)
     when false:
       let deferPart = newNodeI(nkFinally, n.info)
-      deferPart.add n.sons[0]
+      deferPart.add n[0]
       let tryStmt = newNodeI(nkTryStmt, n.info)
       if c.deferAnchor.isNil:
         tryStmt.add c.root
         c.root = tryStmt
-        result = PTransNode(tryStmt)
+        result = tryStmt
       else:
         # modify the corresponding *action*, don't rely on nkStmtList:
-        let L = c.deferAnchor.len-1
-        tryStmt.add c.deferAnchor.sons[L]
-        c.deferAnchor.sons[L] = tryStmt
+        tryStmt.add c.deferAnchor[^1]
+        c.deferAnchor[^1] = tryStmt
         result = newTransNode(nkCommentStmt, n.info, 0)
-      tryStmt.addSon(deferPart)
+      tryStmt.add deferPart
       # disable the original 'defer' statement:
       n.kind = nkEmpty
   of nkContinueStmt:
-    result = PTransNode(newNodeI(nkBreakStmt, n.info))
+    result = newNodeI(nkBreakStmt, n.info)
     var labl = c.contSyms[c.contSyms.high]
-    add(result, PTransNode(newSymNode(labl)))
+    result.add(newSymNode(labl))
   of nkBreakStmt: result = transformBreak(c, n)
   of nkCallKinds:
     result = transformCall(c, n)
-  of nkAddr, nkHiddenAddr:
-    result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
-  of nkDerefExpr, nkHiddenDeref:
-    result = transformAddrDeref(c, n, nkAddr, nkHiddenAddr)
+  of nkHiddenAddr:
+    result = transformAddrDeref(c, n, {nkHiddenDeref})
+  of nkAddr:
+    let oldInAddr = c.inAddr
+    c.inAddr = true
+    result = transformAddrDeref(c, n, {nkDerefExpr, nkHiddenDeref})
+    c.inAddr = oldInAddr
+  of nkDerefExpr:
+    result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr})
+  of nkHiddenDeref:
+    if n[0].kind in {nkBlockExpr, nkBlockStmt}:
+      # bug #20107 bug #21540. Watch out to not deref the pointer too late.
+      let e = transformDerefBlock(c, n)
+      result = transformBlock(c, e)
+    else:
+      result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr})
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
     result = transformConv(c, n)
   of nkDiscardStmt:
-    result = PTransNode(n)
-    if n.sons[0].kind != nkEmpty:
+    result = n
+    if n[0].kind != nkEmpty:
       result = transformSons(c, n)
-      if isConstExpr(PNode(result).sons[0]):
+      if isConstExpr(result[0]):
         # ensure that e.g. discard "some comment" gets optimized away
         # completely:
-        result = PTransNode(newNode(nkCommentStmt))
-  of nkCommentStmt, nkTemplateDef, nkImportStmt, nkStaticStmt:
-    return n.PTransNode
+        result = newNode(nkCommentStmt)
+  of nkCommentStmt, nkTemplateDef, nkImportStmt, nkStaticStmt,
+      nkExportStmt, nkExportExceptStmt:
+    return n
   of nkConstSection:
     # do not replace ``const c = 3`` with ``const 3 = 3``
     return transformConstSection(c, n)
-  of nkTypeSection, nkTypeOfExpr:
+  of nkTypeSection, nkTypeOfExpr, nkMixinStmt, nkBindStmt:
     # no need to transform type sections:
-    return PTransNode(n)
+    return n
   of nkVarSection, nkLetSection:
     if c.inlining > 0:
       # we need to copy the variables for multiple yield statements:
@@ -878,39 +1118,55 @@ proc transform(c: PTransf, n: PNode): PTransNode =
     else:
       result = transformSons(c, n)
   of nkYieldStmt:
-    if c.inlining > 0:
+    if c.inlining > 0 and not c.isIntroducingNewLocalVars:
       result = transformYield(c, n)
     else:
       result = transformSons(c, n)
+  of nkAsgn:
+    result = transformAsgn(c, n)
   of nkIdentDefs, nkConstDef:
-    when true:
-      result = transformSons(c, n)
-    else:
-      result = n.PTransNode
-      let L = n.len-1
-      result[L] = transform(c, n.sons[L])
+    result = newTransNode(n)
+    result[0] = transform(c, skipPragmaExpr(n[0]))
+    # Skip the second son since it only contains an unsemanticized copy of the
+    # variable type used by docgen
+    let last = n.len-1
+    for i in 1..<last: result[i] = n[i]
+    result[last] = transform(c, n[last])
     # XXX comment handling really sucks:
     if importantComments(c.graph.config):
-      PNode(result).comment = n.comment
+      result.comment = n.comment
   of nkClosure:
     # it can happen that for-loop-inlining produced a fresh
     # set of variables, including some computed environment
     # (bug #2604). We need to patch this environment here too:
     let a = n[1]
     if a.kind == nkSym:
-      n.sons[1] = transformSymAux(c, a)
-    return PTransNode(n)
+      result = copyTree(n)
+      result[1] = transformSymAux(c, a)
+    else:
+      result = n
   of nkExceptBranch:
     result = transformExceptBranch(c, n)
+  of nkCheckedFieldExpr:
+    result = transformSons(c, n)
+    if result[0].kind != nkDotExpr:
+      # simplfied beyond a dot expression --> simplify further.
+      result = result[0]
   else:
     result = transformSons(c, n)
   when false:
     if oldDeferAnchor != nil: c.deferAnchor = oldDeferAnchor
 
-  var cnst = getConstExpr(c.module, PNode(result), c.graph)
-  # we inline constants if they are not complex constants:
-  if cnst != nil and not dontInlineConstant(n, cnst):
-    result = PTransNode(cnst) # do not miss an optimization
+  # Constants can be inlined here, but only if they cannot result in a cast
+  # in the back-end (e.g. var p: pointer = someProc)
+  let exprIsPointerCast = n.kind in {nkCast, nkConv, nkHiddenStdConv} and
+                          n.typ != nil and
+                          n.typ.kind == tyPointer
+  if not exprIsPointerCast and not c.inAddr:
+    var cnst = getConstExpr(c.module, result, c.idgen, c.graph)
+    # we inline constants if they are not complex constants:
+    if cnst != nil and not dontInlineConstant(n, cnst):
+      result = cnst # do not miss an optimization
 
 proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
   # Note: For interactive mode we cannot call 'passes.skipCodegen' and skip
@@ -918,16 +1174,12 @@ proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
   # nodes into an empty node.
   if nfTransf in n.flags: return n
   pushTransCon(c, newTransCon(owner))
-  result = PNode(transform(c, n))
+  result = transform(c, n)
   popTransCon(c)
   incl(result.flags, nfTransf)
 
-proc openTransf(g: ModuleGraph; module: PSym, filename: string): PTransf =
-  new(result)
-  result.contSyms = @[]
-  result.breakSyms = @[]
-  result.module = module
-  result.graph = g
+proc openTransf(g: ModuleGraph; module: PSym, filename: string; idgen: IdGenerator; flags: TransformFlags): PTransf =
+  result = PTransf(module: module, graph: g, idgen: idgen, flags: flags)
 
 proc flattenStmts(n: PNode) =
   var goOn = true
@@ -949,71 +1201,75 @@ proc liftDeferAux(n: PNode) =
       goOn = false
       let last = n.len-1
       for i in 0..last:
-        if n.sons[i].kind == nkDefer:
-          let deferPart = newNodeI(nkFinally, n.sons[i].info)
-          deferPart.add n.sons[i].sons[0]
-          var tryStmt = newNodeI(nkTryStmt, n.sons[i].info)
-          var body = newNodeI(n.kind, n.sons[i].info)
+        if n[i].kind == nkDefer:
+          let deferPart = newNodeI(nkFinally, n[i].info)
+          deferPart.add n[i][0]
+          var tryStmt = newNodeIT(nkTryStmt, n[i].info, n.typ)
+          var body = newNodeIT(n.kind, n[i].info, n.typ)
           if i < last:
             body.sons = n.sons[(i+1)..last]
-          tryStmt.addSon(body)
-          tryStmt.addSon(deferPart)
-          n.sons[i] = tryStmt
+          tryStmt.add body
+          tryStmt.add deferPart
+          n[i] = tryStmt
           n.sons.setLen(i+1)
-          n.typ = n.sons[i].typ
+          n.typ = tryStmt.typ
           goOn = true
           break
   for i in 0..n.safeLen-1:
-    liftDeferAux(n.sons[i])
+    liftDeferAux(n[i])
 
 template liftDefer(c, root) =
   if c.deferDetected:
     liftDeferAux(root)
 
-proc transformBody*(g: ModuleGraph; module: PSym, n: PNode, prc: PSym): PNode =
-  if nfTransf in n.flags or prc.kind in {skTemplate}:
-    result = n
+proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flags: TransformFlags): PNode =
+  assert prc.kind in routineKinds
+
+  if prc.transformedBody != nil:
+    result = prc.transformedBody
+  elif nfTransf in getBody(g, prc).flags or prc.kind in {skTemplate}:
+    result = getBody(g, prc)
   else:
-    var c = openTransf(g, module, "")
-    result = liftLambdas(g, prc, n, c.tooEarly)
-    #result = n
+    prc.transformedBody = newNode(nkEmpty) # protects from recursion
+    var c = openTransf(g, prc.getModule, "", idgen, flags)
+    result = liftLambdas(g, prc, getBody(g, prc), c.tooEarly, c.idgen, flags)
     result = processTransf(c, result, prc)
     liftDefer(c, result)
-    #result = liftLambdas(prc, result)
-    when useEffectSystem: trackProc(g, prc, result)
-    result = liftLocalsIfRequested(prc, result, g.cache, g.config)
-    if c.needsDestroyPass: #and newDestructors:
-      result = injectDestructorCalls(g, prc, result)
+    result = liftLocalsIfRequested(prc, result, g.cache, g.config, c.idgen)
 
     if prc.isIterator:
-      result = g.transformClosureIterator(prc, result)
+      result = g.transformClosureIterator(c.idgen, prc, result)
 
     incl(result.flags, nfTransf)
-      #if prc.name.s == "testbody":
-    #  echo renderTree(result)
 
-proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode =
+    if useCache in flags or prc.typ.callConv == ccInline:
+      # genProc for inline procs will be called multiple times from different modules,
+      # it is important to transform exactly once to get sym ids and locations right
+      prc.transformedBody = result
+    else:
+      prc.transformedBody = nil
+    # XXX Rodfile support for transformedBody!
+
+  #if prc.name.s == "main":
+  #  echo "transformed into ", renderTree(result, {renderIds})
+
+proc transformStmt*(g: ModuleGraph; idgen: IdGenerator; module: PSym, n: PNode; flags: TransformFlags = {}): PNode =
   if nfTransf in n.flags:
     result = n
   else:
-    var c = openTransf(g, module, "")
+    var c = openTransf(g, module, "", idgen, flags)
     result = processTransf(c, n, module)
     liftDefer(c, result)
     #result = liftLambdasForTopLevel(module, result)
-    when useEffectSystem: trackTopLevelStmt(g, module, result)
-    #if n.info ?? "temp.nim":
-    #  echo renderTree(result, {renderIds})
-    if c.needsDestroyPass:
-      result = injectDestructorCalls(g, module, result)
     incl(result.flags, nfTransf)
 
-proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode): PNode =
+proc transformExpr*(g: ModuleGraph; idgen: IdGenerator; module: PSym, n: PNode; flags: TransformFlags = {}): PNode =
   if nfTransf in n.flags:
     result = n
   else:
-    var c = openTransf(g, module, "")
+    var c = openTransf(g, module, "", idgen, flags)
     result = processTransf(c, n, module)
     liftDefer(c, result)
-    if c.needsDestroyPass:
-      result = injectDestructorCalls(g, module, result)
+    # expressions are not to be injected with destructor calls as that
+    # the list of top level statements needs to be collected before.
     incl(result.flags, nfTransf)
diff --git a/compiler/trees.nim b/compiler/trees.nim
index fb523de9d..41b54eb09 100644
--- a/compiler/trees.nim
+++ b/compiler/trees.nim
@@ -10,9 +10,10 @@
 # tree helper routines
 
 import
-  ast, astalgo, lexer, msgs, strutils, wordrecg, idents
+  ast, wordrecg, idents
 
 proc cyclicTreeAux(n: PNode, visited: var seq[PNode]): bool =
+  result = false
   if n == nil: return
   for v in visited:
     if v == n: return true
@@ -26,6 +27,10 @@ proc cyclicTree*(n: PNode): bool =
   var visited: seq[PNode] = @[]
   cyclicTreeAux(n, visited)
 
+proc sameFloatIgnoreNan(a, b: BiggestFloat): bool {.inline.} =
+  ## ignores NaN semantics, but ensures 0.0 == -0.0, see #13730
+  cast[uint64](a) == cast[uint64](b) or a == b
+
 proc exprStructuralEquivalent*(a, b: PNode; strictSymEquality=false): bool =
   if a == b:
     result = true
@@ -39,18 +44,23 @@ proc exprStructuralEquivalent*(a, b: PNode; strictSymEquality=false): bool =
         result = a.sym.name.id == b.sym.name.id
     of nkIdent: result = a.ident.id == b.ident.id
     of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal
-    of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
+    of nkFloatLit..nkFloat64Lit: result = sameFloatIgnoreNan(a.floatVal, b.floatVal)
     of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
     of nkCommentStmt: result = a.comment == b.comment
     of nkEmpty, nkNilLit, nkType: result = true
     else:
-      if sonsLen(a) == sonsLen(b):
-        for i in countup(0, sonsLen(a) - 1):
-          if not exprStructuralEquivalent(a.sons[i], b.sons[i],
+      if a.len == b.len:
+        for i in 0..<a.len:
+          if not exprStructuralEquivalent(a[i], b[i],
                                           strictSymEquality): return
         result = true
+      else:
+        result = false
+  else:
+    result = false
 
 proc sameTree*(a, b: PNode): bool =
+  result = false
   if a == b:
     result = true
   elif a != nil and b != nil and a.kind == b.kind:
@@ -64,20 +74,21 @@ proc sameTree*(a, b: PNode): bool =
       result = a.sym.name.id == b.sym.name.id
     of nkIdent: result = a.ident.id == b.ident.id
     of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal
-    of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
+    of nkFloatLit..nkFloat64Lit: result = sameFloatIgnoreNan(a.floatVal, b.floatVal)
     of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
     of nkEmpty, nkNilLit, nkType: result = true
     else:
-      if sonsLen(a) == sonsLen(b):
-        for i in countup(0, sonsLen(a) - 1):
-          if not sameTree(a.sons[i], b.sons[i]): return
+      if a.len == b.len:
+        for i in 0..<a.len:
+          if not sameTree(a[i], b[i]): return
         result = true
 
 proc getMagic*(op: PNode): TMagic =
+  if op == nil: return mNone
   case op.kind
   of nkCallKinds:
-    case op.sons[0].kind
-    of nkSym: result = op.sons[0].sym.magic
+    case op[0].kind
+    of nkSym: result = op[0].sym.magic
     else: result = mNone
   else: result = mNone
 
@@ -86,27 +97,34 @@ proc isConstExpr*(n: PNode): bool =
   n.kind in atomKinds or nfAllConst in n.flags
 
 proc isCaseObj*(n: PNode): bool =
+  result = false
   if n.kind == nkRecCase: return true
-  for i in 0..<safeLen(n):
+  for i in 0..<n.safeLen:
     if n[i].isCaseObj: return true
 
-proc isDeepConstExpr*(n: PNode): bool =
+proc isDeepConstExpr*(n: PNode; preventInheritance = false): bool =
   case n.kind
-  of nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
-      nkFloatLit..nkFloat64Lit, nkNilLit:
+  of nkCharLit..nkNilLit:
     result = true
   of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
-    result = isDeepConstExpr(n.sons[1])
+    result = isDeepConstExpr(n[1], preventInheritance)
   of nkCurly, nkBracket, nkPar, nkTupleConstr, nkObjConstr, nkClosure, nkRange:
-    for i in ord(n.kind == nkObjConstr) ..< n.len:
-      if not isDeepConstExpr(n.sons[i]): return false
+    for i in ord(n.kind == nkObjConstr)..<n.len:
+      if not isDeepConstExpr(n[i], preventInheritance): return false
     if n.typ.isNil: result = true
     else:
-      let t = n.typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink})
-      if t.kind in {tyRef, tyPtr}: return false
-      if t.kind != tyObject or not isCaseObj(t.n):
+      let t = n.typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned})
+      if t.kind in {tyRef, tyPtr} or tfUnion in t.flags: return false
+      if t.kind == tyObject:
+        if preventInheritance and t.baseClass != nil:
+          result = false
+        elif isCaseObj(t.n):
+          result = false
+        else:
+          result = true
+      else:
         result = true
-  else: discard
+  else: result = false
 
 proc isRange*(n: PNode): bool {.inline.} =
   if n.kind in nkCallKinds:
@@ -116,10 +134,53 @@ proc isRange*(n: PNode): bool {.inline.} =
        (callee.kind in {nkClosedSymChoice, nkOpenSymChoice} and
         callee[1].sym.name.id == ord(wDotDot)):
       result = true
+    else:
+      result = false
+  else:
+    result = false
 
 proc whichPragma*(n: PNode): TSpecialWord =
-  let key = if n.kind in nkPragmaCallKinds and n.len > 0: n.sons[0] else: n
-  if key.kind == nkIdent: result = whichKeyword(key.ident)
+  let key = if n.kind in nkPragmaCallKinds and n.len > 0: n[0] else: n
+  case key.kind
+  of nkIdent: result = whichKeyword(key.ident)
+  of nkSym: result = whichKeyword(key.sym.name)
+  of nkCast: return wCast
+  of nkClosedSymChoice, nkOpenSymChoice:
+    return whichPragma(key[0])
+  else: return wInvalid
+  if result in nonPragmaWordsLow..nonPragmaWordsHigh:
+    result = wInvalid
+
+proc isNoSideEffectPragma*(n: PNode): bool =
+  var k = whichPragma(n)
+  if k == wCast:
+    k = whichPragma(n[1])
+  result = k == wNoSideEffect
+
+proc findPragma*(n: PNode, which: TSpecialWord): PNode =
+  result = nil
+  if n.kind == nkPragma:
+    for son in n:
+      if whichPragma(son) == which:
+        return son
+
+proc effectSpec*(n: PNode, effectType: TSpecialWord): PNode =
+  result = nil
+  for i in 0..<n.len:
+    var it = n[i]
+    if it.kind == nkExprColonExpr and whichPragma(it) == effectType:
+      result = it[1]
+      if result.kind notin {nkCurly, nkBracket}:
+        result = newNodeI(nkCurly, result.info)
+        result.add(it[1])
+      return
+
+proc propSpec*(n: PNode, effectType: TSpecialWord): PNode =
+  result = nil
+  for i in 0..<n.len:
+    var it = n[i]
+    if it.kind == nkExprColonExpr and whichPragma(it) == effectType:
+      return it[1]
 
 proc unnestStmts(n, result: PNode) =
   if n.kind == nkStmtList:
@@ -131,8 +192,47 @@ proc flattenStmts*(n: PNode): PNode =
   result = newNodeI(nkStmtList, n.info)
   unnestStmts(n, result)
   if result.len == 1:
-    result = result.sons[0]
+    result = result[0]
 
 proc extractRange*(k: TNodeKind, n: PNode, a, b: int): PNode =
   result = newNodeI(k, n.info, b-a+1)
-  for i in 0 .. b-a: result.sons[i] = n.sons[i+a]
+  for i in 0..b-a: result[i] = n[i+a]
+
+proc getRoot*(n: PNode): PSym =
+  ## ``getRoot`` takes a *path* ``n``. A path is an lvalue expression
+  ## like ``obj.x[i].y``. The *root* of a path is the symbol that can be
+  ## determined as the owner; ``obj`` in the example.
+  case n.kind
+  of nkSym:
+    if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar, skParam}:
+      result = n.sym
+    else:
+      result = nil
+  of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr,
+      nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr, nkHiddenAddr, nkAddr:
+    result = getRoot(n[0])
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+    result = getRoot(n[1])
+  of nkCallKinds:
+    if getMagic(n) == mSlice: result = getRoot(n[1])
+    else: result = nil
+  else: result = nil
+
+proc stupidStmtListExpr*(n: PNode): bool =
+  for i in 0..<n.len-1:
+    if n[i].kind notin {nkEmpty, nkCommentStmt}: return false
+  result = true
+
+proc dontInlineConstant*(orig, cnst: PNode): bool {.inline.} =
+  # symbols that expand to a complex constant (array, etc.) should not be
+  # inlined, unless it's the empty array:
+  result = cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket, nkObjConstr} and
+           cnst.len > ord(cnst.kind == nkObjConstr)
+
+proc isRunnableExamples*(n: PNode): bool =
+  # Templates and generics don't perform symbol lookups.
+  result = n.kind == nkSym and n.sym.magic == mRunnableExamples or
+    n.kind == nkIdent and n.ident.id == ord(wRunnableExamples)
+
+proc skipAddr*(n: PNode): PNode {.inline.} =
+  result = if n.kind in {nkAddr, nkHiddenAddr}: n[0] else: n
diff --git a/compiler/treetab.nim b/compiler/treetab.nim
index e6eb8c666..6685c4a89 100644
--- a/compiler/treetab.nim
+++ b/compiler/treetab.nim
@@ -9,11 +9,16 @@
 
 # Implements a table from trees to trees. Does structural equivalence checking.
 
-import
-  hashes, ast, astalgo, types
+import ast, astalgo, types
 
-proc hashTree(n: PNode): Hash =
-  if n == nil: return
+import std/hashes
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+proc hashTree*(n: PNode): Hash =
+  if n.isNil:
+    return
   result = ord(n.kind)
   case n.kind
   of nkEmpty, nkNilLit, nkType:
@@ -21,7 +26,7 @@ proc hashTree(n: PNode): Hash =
   of nkIdent:
     result = result !& n.ident.h
   of nkSym:
-    result = result !& n.sym.name.h
+    result = result !& n.sym.id
   of nkCharLit..nkUInt64Lit:
     if (n.intVal >= low(int)) and (n.intVal <= high(int)):
       result = result !& int(n.intVal)
@@ -29,11 +34,13 @@ proc hashTree(n: PNode): Hash =
     if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0):
       result = result !& toInt(n.floatVal)
   of nkStrLit..nkTripleStrLit:
-    if not n.strVal.isNil:
-      result = result !& hash(n.strVal)
+    result = result !& hash(n.strVal)
   else:
-    for i in countup(0, sonsLen(n) - 1):
-      result = result !& hashTree(n.sons[i])
+    for i in 0..<n.len:
+      result = result !& hashTree(n[i])
+  result = !$result
+  #echo "hashTree ", result
+  #echo n
 
 proc treesEquivalent(a, b: PNode): bool =
   if a == b:
@@ -47,11 +54,15 @@ proc treesEquivalent(a, b: PNode): bool =
     of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
     of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
     else:
-      if sonsLen(a) == sonsLen(b):
-        for i in countup(0, sonsLen(a) - 1):
-          if not treesEquivalent(a.sons[i], b.sons[i]): return
+      if a.len == b.len:
+        for i in 0..<a.len:
+          if not treesEquivalent(a[i], b[i]): return
         result = true
+      else:
+        result = false
     if result: result = sameTypeOrNil(a.typ, b.typ)
+  else:
+    result = false
 
 proc nodeTableRawGet(t: TNodeTable, k: Hash, key: PNode): int =
   var h: Hash = k and high(t.data)
@@ -76,36 +87,34 @@ proc nodeTableRawInsert(data: var TNodePairSeq, k: Hash, key: PNode,
   data[h].val = val
 
 proc nodeTablePut*(t: var TNodeTable, key: PNode, val: int) =
-  var n: TNodePairSeq
-  var k: Hash = hashTree(key)
-  var index = nodeTableRawGet(t, k, key)
+  let k = hashTree(key)
+  let index = nodeTableRawGet(t, k, key)
   if index >= 0:
     assert(t.data[index].key != nil)
     t.data[index].val = val
   else:
-    if mustRehash(len(t.data), t.counter):
-      newSeq(n, len(t.data) * GrowthFactor)
-      for i in countup(0, high(t.data)):
+    if mustRehash(t.data.len, t.counter):
+      var n = newSeq[TNodePair](t.data.len * GrowthFactor)
+      for i in 0..high(t.data):
         if t.data[i].key != nil:
           nodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val)
-      swap(t.data, n)
+      t.data = move n
     nodeTableRawInsert(t.data, k, key, val)
     inc(t.counter)
 
 proc nodeTableTestOrSet*(t: var TNodeTable, key: PNode, val: int): int =
-  var n: TNodePairSeq
-  var k: Hash = hashTree(key)
-  var index = nodeTableRawGet(t, k, key)
+  let k = hashTree(key)
+  let index = nodeTableRawGet(t, k, key)
   if index >= 0:
     assert(t.data[index].key != nil)
     result = t.data[index].val
   else:
-    if mustRehash(len(t.data), t.counter):
-      newSeq(n, len(t.data) * GrowthFactor)
-      for i in countup(0, high(t.data)):
+    if mustRehash(t.data.len, t.counter):
+      var n = newSeq[TNodePair](t.data.len * GrowthFactor)
+      for i in 0..high(t.data):
         if t.data[i].key != nil:
           nodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val)
-      swap(t.data, n)
+      t.data = move n
     nodeTableRawInsert(t.data, k, key, val)
     result = val
     inc(t.counter)
diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim
new file mode 100644
index 000000000..39193a42d
--- /dev/null
+++ b/compiler/typeallowed.nim
@@ -0,0 +1,296 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module contains 'typeAllowed' and friends which check
+## for invalid types like `openArray[var int]`.
+
+import ast, renderer, options, semdata, types
+import std/intsets
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+type
+  TTypeAllowedFlag* = enum
+    taField,
+    taHeap,
+    taConcept,
+    taIsOpenArray,
+    taNoUntyped
+    taIsTemplateOrMacro
+    taProcContextIsNotMacro
+    taIsCastable
+    taIsDefaultField
+    taVoid # only allow direct void fields of objects/tuples
+
+  TTypeAllowedFlags* = set[TTypeAllowedFlag]
+
+proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind;
+                    c: PContext; flags: TTypeAllowedFlags = {}): PType
+
+proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind,
+                     c: PContext; flags: TTypeAllowedFlags = {}): PType =
+  if n != nil:
+    result = typeAllowedAux(marker, n.typ, kind, c, flags)
+    if result == nil:
+      case n.kind
+      of nkNone..nkNilLit:
+        discard
+      else:
+        #if n.kind == nkRecCase and kind in {skProc, skFunc, skConst}:
+        #  return n[0].typ
+        for i in 0..<n.len:
+          let it = n[i]
+          result = typeAllowedNode(marker, it, kind, c, flags)
+          if result != nil: break
+  else:
+    result = nil
+
+proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
+                    c: PContext; flags: TTypeAllowedFlags = {}): PType =
+  assert(kind in {skVar, skLet, skConst, skProc, skFunc, skParam, skResult})
+  # if we have already checked the type, return true, because we stop the
+  # evaluation if something is wrong:
+  result = nil
+  if typ == nil: return nil
+  if containsOrIncl(marker, typ.id): return nil
+  var t = skipTypes(typ, abstractInst-{tyTypeDesc, tySink})
+
+  let flags = if t.kind == tyVoid: flags else: flags-{taVoid}
+  case t.kind
+  of tyVar, tyLent:
+    if kind in {skProc, skFunc, skConst} and (views notin c.features):
+      result = t
+    elif taIsOpenArray in flags:
+      result = t
+    elif t.kind == tyLent and ((kind != skResult and views notin c.features) or
+      (kind == skParam and {taIsCastable, taField} * flags == {})): # lent cannot be used as parameters.
+                                                       # except in the cast environment and as the field of an object
+      result = t
+    elif isOutParam(t) and kind != skParam:
+      result = t
+    else:
+      var t2 = skipTypes(t.elementType, abstractInst-{tyTypeDesc, tySink})
+      case t2.kind
+      of tyVar, tyLent:
+        if taHeap notin flags: result = t2 # ``var var`` is illegal on the heap
+      of tyOpenArray:
+        if (kind != skParam and views notin c.features) or taIsOpenArray in flags: result = t
+        else: result = typeAllowedAux(marker, t2[0], kind, c, flags+{taIsOpenArray})
+      of tyUncheckedArray:
+        if kind != skParam and views notin c.features: result = t
+        else: result = typeAllowedAux(marker, t2[0], kind, c, flags)
+      of tySink:
+        result = t
+      else:
+        if kind notin {skParam, skResult} and views notin c.features: result = t
+        else: result = typeAllowedAux(marker, t2, kind, c, flags)
+  of tyProc:
+    if kind in {skVar, skLet, skConst} and taIsTemplateOrMacro in flags:
+      result = t
+    else:
+      if isInlineIterator(typ) and kind in {skVar, skLet, skConst, skParam, skResult}:
+        # only closure iterators may be assigned to anything.
+        result = t
+      let f = if kind in {skProc, skFunc}: flags+{taNoUntyped} else: flags
+      for _, a in t.paramTypes:
+        if result != nil: break
+        result = typeAllowedAux(marker, a, skParam, c, f-{taIsOpenArray})
+      if result.isNil and t.returnType != nil:
+        result = typeAllowedAux(marker, t.returnType, skResult, c, flags)
+  of tyTypeDesc:
+    if kind in {skVar, skLet, skConst} and taProcContextIsNotMacro in flags:
+      result = t
+    else:
+      # XXX: This is still a horrible idea...
+      result = nil
+  of tyUntyped, tyTyped:
+    if kind notin {skParam, skResult} or taNoUntyped in flags: result = t
+  of tyIterable:
+    if kind notin {skParam} or taNoUntyped in flags: result = t
+      # tyIterable is only for templates and macros.
+  of tyStatic:
+    if kind notin {skParam}: result = t
+  of tyVoid:
+    if taVoid notin flags: result = t
+  of tyTypeClasses:
+    if tfGenericTypeParam in t.flags or taConcept in flags: #or taField notin flags:
+      discard
+    elif t.isResolvedUserTypeClass:
+      result = typeAllowedAux(marker, t.last, kind, c, flags)
+    elif kind notin {skParam, skResult}:
+      result = t
+  of tyGenericBody, tyGenericParam, tyGenericInvocation,
+     tyNone, tyForward, tyFromExpr:
+    result = t
+  of tyNil:
+    if kind != skConst and kind != skParam: result = t
+  of tyString, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCstring, tyPointer:
+    result = nil
+  of tyOrdinal:
+    if kind != skParam: result = t
+  of tyGenericInst, tyDistinct, tyAlias, tyInferred:
+    result = typeAllowedAux(marker, skipModifier(t), kind, c, flags)
+  of tyRange:
+    if skipTypes(t.elementType, abstractInst-{tyTypeDesc}).kind notin
+      {tyChar, tyEnum, tyInt..tyFloat128, tyInt..tyUInt64, tyRange}: result = t
+  of tyOpenArray:
+    # you cannot nest openArrays/sinks/etc.
+    if (kind != skParam or taIsOpenArray in flags) and views notin c.features:
+      result = t
+    else:
+      result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taIsOpenArray})
+  of tyVarargs:
+    # you cannot nest openArrays/sinks/etc.
+    if kind != skParam or taIsOpenArray in flags:
+      result = t
+    else:
+      result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taIsOpenArray})
+  of tySink:
+    # you cannot nest openArrays/sinks/etc.
+    if kind != skParam or taIsOpenArray in flags or t.elementType.kind in {tySink, tyLent, tyVar}:
+      result = t
+    else:
+      result = typeAllowedAux(marker, t.elementType, kind, c, flags)
+  of tyUncheckedArray:
+    if kind != skParam and taHeap notin flags:
+      result = t
+    else:
+      result = typeAllowedAux(marker, elementType(t), kind, c, flags-{taHeap})
+  of tySequence:
+    if t.elementType.kind != tyEmpty:
+      result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taHeap})
+    elif kind in {skVar, skLet}:
+      result = t.elementType
+  of tyArray:
+    if t.elementType.kind == tyTypeDesc:
+      result = t.elementType
+    elif t.elementType.kind != tyEmpty:
+      result = typeAllowedAux(marker, t.elementType, kind, c, flags)
+    elif kind in {skVar, skLet}:
+      result = t.elementType
+  of tyRef:
+    if kind == skConst and taIsDefaultField notin flags: result = t
+    else: result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taHeap})
+  of tyPtr:
+    result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taHeap})
+  of tySet:
+    result = typeAllowedAux(marker, t.elementType, kind, c, flags)
+  of tyObject:
+    if kind in {skProc, skFunc, skConst} and
+        t.baseClass != nil and taIsDefaultField notin flags:
+      result = t
+    else:
+      let flags = flags+{taField, taVoid}
+      result = typeAllowedAux(marker, t.baseClass, kind, c, flags)
+      if result.isNil and t.n != nil:
+        result = typeAllowedNode(marker, t.n, kind, c, flags)
+  of tyTuple:
+    let flags = flags+{taField, taVoid}
+    for a in t.kids:
+      result = typeAllowedAux(marker, a, kind, c, flags)
+      if result != nil: break
+    if result.isNil and t.n != nil:
+      result = typeAllowedNode(marker, t.n, kind, c, flags)
+  of tyEmpty:
+    if kind in {skVar, skLet}: result = t
+  of tyError:
+    # for now same as error node; we say it's a valid type as it should
+    # prevent cascading errors:
+    result = nil
+  of tyOwned:
+    if t.hasElementType and t.skipModifier.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyProc}:
+      result = typeAllowedAux(marker, t.skipModifier, kind, c, flags+{taHeap})
+    else:
+      result = t
+  of tyConcept:
+    if kind != skParam: result = t
+    else: result = nil
+
+proc typeAllowed*(t: PType, kind: TSymKind; c: PContext; flags: TTypeAllowedFlags = {}): PType =
+  # returns 'nil' on success and otherwise the part of the type that is
+  # wrong!
+  var marker = initIntSet()
+  result = typeAllowedAux(marker, t, kind, c, flags)
+
+type
+  ViewTypeKind* = enum
+    noView, immutableView, mutableView
+
+proc combine(dest: var ViewTypeKind, b: ViewTypeKind) {.inline.} =
+  case dest
+  of noView, mutableView:
+    dest = b
+  of immutableView:
+    if b == mutableView: dest = b
+
+proc classifyViewTypeAux(marker: var IntSet, t: PType): ViewTypeKind
+
+proc classifyViewTypeNode(marker: var IntSet, n: PNode): ViewTypeKind =
+  case n.kind
+  of nkSym:
+    result = classifyViewTypeAux(marker, n.typ)
+  of nkOfBranch:
+    result = classifyViewTypeNode(marker, n.lastSon)
+  else:
+    result = noView
+    for child in n:
+      result.combine classifyViewTypeNode(marker, child)
+      if result == mutableView: break
+
+proc classifyViewTypeAux(marker: var IntSet, t: PType): ViewTypeKind =
+  if containsOrIncl(marker, t.id): return noView
+  case t.kind
+  of tyVar:
+    result = mutableView
+  of tyLent, tyOpenArray, tyVarargs:
+    result = immutableView
+  of tyGenericInst, tyDistinct, tyAlias, tyInferred, tySink, tyOwned,
+     tyUncheckedArray, tySequence, tyArray, tyRef, tyStatic:
+    result = classifyViewTypeAux(marker, skipModifier(t))
+  of tyFromExpr:
+    if t.hasElementType:
+      result = classifyViewTypeAux(marker, skipModifier(t))
+    else:
+      result = noView
+  of tyTuple:
+    result = noView
+    for a in t.kids:
+      result.combine classifyViewTypeAux(marker, a)
+      if result == mutableView: break
+  of tyObject:
+    result = noView
+    if t.n != nil:
+      result = classifyViewTypeNode(marker, t.n)
+    if t.baseClass != nil:
+      result.combine classifyViewTypeAux(marker, t.baseClass)
+  else:
+    # it doesn't matter what these types contain, 'ptr openArray' is not a
+    # view type!
+    result = noView
+
+proc classifyViewType*(t: PType): ViewTypeKind =
+  var marker = initIntSet()
+  result = classifyViewTypeAux(marker, t)
+
+proc directViewType*(t: PType): ViewTypeKind =
+  # does classify 't' without looking recursively into 't'.
+  case t.kind
+  of tyVar:
+    result = mutableView
+  of tyLent, tyOpenArray:
+    result = immutableView
+  of abstractInst-{tyTypeDesc}:
+    result = directViewType(t.skipModifier)
+  else:
+    result = noView
+
+proc requiresInit*(t: PType): bool =
+  (t.flags * {tfRequiresInit, tfNeedsFullInit, tfNotNil} != {}) or
+  classifyViewType(t) != noView
diff --git a/compiler/types.nim b/compiler/types.nim
index 98343c688..a441b0ea2 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -10,30 +10,69 @@
 # this module contains routines for accessing and iterating over types
 
 import
-  intsets, ast, astalgo, trees, msgs, strutils, platform, renderer, options,
-  lineinfos
+  ast, astalgo, trees, msgs, platform, renderer, options,
+  lineinfos, int128, modulegraphs, astmsgs, wordrecg
+
+import std/[intsets, strutils]
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, formatfloat]
 
 type
   TPreferedDesc* = enum
-    preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg,
-    preferTypeName
+    preferName, # default
+    preferDesc, # probably should become what preferResolved is
+    preferExported,
+    preferModuleInfo, # fully qualified
+    preferGenericArg,
+    preferTypeName,
+    preferResolved, # fully resolved symbols
+    preferMixed,
+      # most useful, shows: symbol + resolved symbols if it differs, e.g.:
+      # tuple[a: MyInt{int}, b: float]
+    preferInlayHint,
+    preferInferredEffects,
+
+  TTypeRelation* = enum      # order is important!
+    isNone, isConvertible,
+    isIntConv,
+    isSubtype,
+    isSubrange,              # subrange of the wanted type; no type conversion
+                             # but apart from that counts as ``isSubtype``
+    isBothMetaConvertible    # generic proc parameter was matched against
+                             # generic type, e.g., map(mySeq, x=>x+1),
+                             # maybe recoverable by rerun if the parameter is
+                             # the proc's return value
+    isInferred,              # generic proc was matched against a concrete type
+    isInferredConvertible,   # same as above, but requiring proc CC conversion
+    isGeneric,
+    isFromIntLit,            # conversion *from* int literal; proven safe
+    isEqual
+
+  ProcConvMismatch* = enum
+    pcmNoSideEffect
+    pcmNotGcSafe
+    pcmNotIterator
+    pcmDifferentCallConv
 
 proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string
-template `$`*(typ: PType): string = typeToString(typ)
 
-proc base*(t: PType): PType =
-  result = t.sons[0]
+proc addTypeDeclVerboseMaybe*(result: var string, conf: ConfigRef; typ: PType) =
+  if optDeclaredLocs in conf.globalOptions:
+    result.add typeToString(typ, preferMixed)
+    result.addDeclaredLoc(conf, typ)
+  else:
+    result.add typeToString(typ)
+
+template `$`*(typ: PType): string = typeToString(typ)
 
 # ------------------- type iterator: ----------------------------------------
 type
   TTypeIter* = proc (t: PType, closure: RootRef): bool {.nimcall.} # true if iteration should stop
-  TTypeMutator* = proc (t: PType, closure: RootRef): PType {.nimcall.} # copy t and mutate it
   TTypePredicate* = proc (t: PType): bool {.nimcall.}
 
 proc iterOverType*(t: PType, iter: TTypeIter, closure: RootRef): bool
   # Returns result of `iter`.
-proc mutateType*(t: PType, iter: TTypeMutator, closure: RootRef): PType
-  # Returns result of `iter`.
 
 type
   TParamsEquality* = enum     # they are equal, but their
@@ -52,50 +91,68 @@ const
   # TODO: Remove tyTypeDesc from each abstractX and (where necessary)
   # replace with typedescX
   abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal,
-                   tyTypeDesc, tyAlias, tyInferred, tySink, tyLent}
+                   tyTypeDesc, tyAlias, tyInferred, tySink, tyLent, tyOwned}
   abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc,
-                  tyAlias, tyInferred, tySink, tyLent}
+                  tyAlias, tyInferred, tySink, tyLent, tyOwned}
   abstractRange* = {tyGenericInst, tyRange, tyDistinct, tyOrdinal, tyTypeDesc,
-                    tyAlias, tyInferred, tySink}
-  abstractVarRange* = {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal,
-                       tyTypeDesc, tyAlias, tyInferred, tySink}
-  abstractInst* = {tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias,
-                   tyInferred, tySink}
+                    tyAlias, tyInferred, tySink, tyOwned}
+  abstractInstOwned* = abstractInst + {tyOwned}
   skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyTypeDesc, tyAlias,
-               tyInferred, tySink, tyLent}
+               tyInferred, tySink, tyLent, tyOwned}
   # typedescX is used if we're sure tyTypeDesc should be included (or skipped)
   typedescPtrs* = abstractPtrs + {tyTypeDesc}
-  typedescInst* = abstractInst + {tyTypeDesc}
-
-type
-  TTypeFieldResult* = enum
-    frNone,                   # type has no object type field
-    frHeader,                 # type has an object type field only in the header
-    frEmbedded                # type has an object type field somewhere embedded
-
-proc analyseObjectWithTypeField*(t: PType): TTypeFieldResult
-  # this does a complex analysis whether a call to ``objectInit`` needs to be
-  # made or intializing of the type field suffices or if there is no type field
-  # at all in this type.
+  typedescInst* = abstractInst + {tyTypeDesc, tyOwned, tyUserTypeClass}
 
 proc invalidGenericInst*(f: PType): bool =
-  result = f.kind == tyGenericInst and lastSon(f) == nil
+  result = f.kind == tyGenericInst and skipModifier(f) == nil
 
 proc isPureObject*(typ: PType): bool =
   var t = typ
-  while t.kind == tyObject and t.sons[0] != nil:
-    t = t.sons[0].skipTypes(skipPtrs)
+  while t.kind == tyObject and t.baseClass != nil:
+    t = t.baseClass.skipTypes(skipPtrs)
   result = t.sym != nil and sfPure in t.sym.flags
 
-proc getOrdValue*(n: PNode): BiggestInt =
-  case n.kind
-  of nkCharLit..nkUInt64Lit: result = n.intVal
-  of nkNilLit: result = 0
-  of nkHiddenStdConv: result = getOrdValue(n.sons[1])
+proc isUnsigned*(t: PType): bool =
+  t.skipTypes(abstractInst).kind in {tyChar, tyUInt..tyUInt64}
+
+proc getOrdValueAux*(n: PNode, err: var bool): Int128 =
+  var k = n.kind
+  if n.typ != nil and n.typ.skipTypes(abstractInst).kind in {tyChar, tyUInt..tyUInt64}:
+    k = nkUIntLit
+
+  case k
+  of nkCharLit, nkUIntLit..nkUInt64Lit:
+    # XXX: enable this assert
+    #assert n.typ == nil or isUnsigned(n.typ), $n.typ
+    toInt128(cast[uint64](n.intVal))
+  of nkIntLit..nkInt64Lit:
+    # XXX: enable this assert
+    #assert n.typ == nil or not isUnsigned(n.typ), $n.typ.kind
+    toInt128(n.intVal)
+  of nkNilLit:
+    int128.Zero
+  of nkHiddenStdConv:
+    getOrdValueAux(n[1], err)
   else:
-    #localError(n.info, errOrdinalTypeExpected)
-    # XXX check usages of getOrdValue
-    result = high(BiggestInt)
+    err = true
+    int128.Zero
+
+proc getOrdValue*(n: PNode): Int128 =
+  var err: bool = false
+  result = getOrdValueAux(n, err)
+  #assert err == false
+
+proc getOrdValue*(n: PNode, onError: Int128): Int128 =
+  var err = false
+  result = getOrdValueAux(n, err)
+  if err:
+    result = onError
+
+proc getFloatValue*(n: PNode): BiggestFloat =
+  case n.kind
+  of nkFloatLiterals: n.floatVal
+  of nkHiddenStdConv: getFloatValue(n[1])
+  else: NaN
 
 proc isIntLit*(t: PType): bool {.inline.} =
   result = t.kind == tyInt and t.n != nil and t.n.kind == nkIntLit
@@ -103,46 +160,52 @@ proc isIntLit*(t: PType): bool {.inline.} =
 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))
-  result.add "[declared in "
-  result.add(conf$sym.info)
-  result.add "]"
+proc addTypeHeader*(result: var string, conf: ConfigRef; typ: PType; prefer: TPreferedDesc = preferMixed; getDeclarationPath = true) =
+  result.add typeToString(typ, prefer)
+  if getDeclarationPath: result.addDeclaredLoc(conf, typ.sym)
+
+proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferName; getDeclarationPath = true): string =
+  assert sym != nil
+  # consider using `skipGenericOwner` to avoid fun2.fun2 when fun2 is generic
+  result = sym.owner.name.s & '.' & sym.name.s
+  if sym.kind in routineKinds:
+    result.add '('
+    var n = sym.typ.n
+    for i in 1..<n.len:
+      let p = n[i]
+      if p.kind == nkSym:
+        result.add(p.sym.name.s)
+        result.add(": ")
+        result.add(typeToString(p.sym.typ, prefer))
+        if i != n.len-1: result.add(", ")
+      else:
+        result.add renderTree(p)
+    result.add(')')
+    if n[0].typ != nil:
+      result.add(": " & typeToString(n[0].typ, prefer))
+  if getDeclarationPath: result.addDeclaredLoc(conf, sym)
 
 proc elemType*(t: PType): PType =
   assert(t != nil)
   case t.kind
-  of tyGenericInst, tyDistinct, tyAlias: result = elemType(lastSon(t))
-  of tyArray: result = t.sons[1]
-  else: result = t.lastSon
+  of tyGenericInst, tyDistinct, tyAlias, tySink: result = elemType(skipModifier(t))
+  of tyArray: result = t.elementType
+  of tyError: result = t
+  else: result = t.elementType
   assert(result != nil)
 
-proc isOrdinalType*(t: PType): bool =
-  assert(t != nil)
-  const
-    # caution: uint, uint64 are no ordinal types!
-    baseKinds = {tyChar,tyInt..tyInt64,tyUInt8..tyUInt32,tyBool,tyEnum}
-    parentKinds = {tyRange, tyOrdinal, tyGenericInst, tyAlias, tyDistinct}
-  t.kind in baseKinds or (t.kind in parentKinds and isOrdinalType(t.sons[0]))
-
 proc enumHasHoles*(t: PType): bool =
-  var b = t
-  while b.kind in {tyRange, tyGenericInst, tyAlias}: b = b.sons[0]
+  var b = t.skipTypes({tyRange, tyGenericInst, tyAlias, tySink})
   result = b.kind == tyEnum and tfEnumHasHoles in b.flags
 
+proc isOrdinalType*(t: PType, allowEnumWithHoles: bool = false): bool =
+  assert(t != nil)
+  const
+    baseKinds = {tyChar, tyInt..tyInt64, tyUInt..tyUInt64, tyBool, tyEnum}
+    parentKinds = {tyRange, tyOrdinal, tyGenericInst, tyAlias, tySink, tyDistinct}
+  result = (t.kind in baseKinds and (not t.enumHasHoles or allowEnumWithHoles)) or
+    (t.kind in parentKinds and isOrdinalType(t.skipModifier, allowEnumWithHoles))
+
 proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
                      closure: RootRef): bool
 proc iterOverNode(marker: var IntSet, n: PNode, iter: TTypeIter,
@@ -153,9 +216,13 @@ proc iterOverNode(marker: var IntSet, n: PNode, iter: TTypeIter,
       # a leaf
       result = iterOverTypeAux(marker, n.typ, iter, closure)
     else:
-      for i in countup(0, sonsLen(n) - 1):
-        result = iterOverNode(marker, n.sons[i], iter, closure)
+      result = iterOverTypeAux(marker, n.typ, iter, closure)
+      if result: return
+      for i in 0..<n.len:
+        result = iterOverNode(marker, n[i], iter, closure)
         if result: return
+  else:
+    result = false
 
 proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
                      closure: RootRef): bool =
@@ -165,13 +232,17 @@ proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
   if result: return
   if not containsOrIncl(marker, t.id):
     case t.kind
-    of tyGenericInst, tyGenericBody, tyAlias, tyInferred:
-      result = iterOverTypeAux(marker, lastSon(t), iter, closure)
+    of tyGenericBody:
+      # treat as atomic, containsUnresolvedType wants always false,
+      # containsGenericType always gives true
+      discard
+    of tyGenericInst, tyAlias, tySink, tyInferred:
+      result = iterOverTypeAux(marker, skipModifier(t), iter, closure)
     else:
-      for i in countup(0, sonsLen(t) - 1):
-        result = iterOverTypeAux(marker, t.sons[i], iter, closure)
+      for a in t.kids:
+        result = iterOverTypeAux(marker, a, iter, closure)
         if result: return
-      if t.n != nil: result = iterOverNode(marker, t.n, iter, closure)
+      if t.n != nil and t.kind != tyProc: result = iterOverNode(marker, t.n, iter, closure)
 
 proc iterOverType(t: PType, iter: TTypeIter, closure: RootRef): bool =
   var marker = initIntSet()
@@ -185,17 +256,17 @@ proc searchTypeNodeForAux(n: PNode, p: TTypePredicate,
   result = false
   case n.kind
   of nkRecList:
-    for i in countup(0, sonsLen(n) - 1):
-      result = searchTypeNodeForAux(n.sons[i], p, marker)
+    for i in 0..<n.len:
+      result = searchTypeNodeForAux(n[i], p, marker)
       if result: return
   of nkRecCase:
-    assert(n.sons[0].kind == nkSym)
-    result = searchTypeNodeForAux(n.sons[0], p, marker)
+    assert(n[0].kind == nkSym)
+    result = searchTypeNodeForAux(n[0], p, marker)
     if result: return
-    for i in countup(1, sonsLen(n) - 1):
-      case n.sons[i].kind
+    for i in 1..<n.len:
+      case n[i].kind
       of nkOfBranch, nkElse:
-        result = searchTypeNodeForAux(lastSon(n.sons[i]), p, marker)
+        result = searchTypeNodeForAux(lastSon(n[i]), p, marker)
         if result: return
       else: discard
   of nkSym:
@@ -212,19 +283,19 @@ proc searchTypeForAux(t: PType, predicate: TTypePredicate,
   if result: return
   case t.kind
   of tyObject:
-    if t.sons[0] != nil:
-      result = searchTypeForAux(t.sons[0].skipTypes(skipPtrs), predicate, marker)
+    if t.baseClass != nil:
+      result = searchTypeForAux(t.baseClass.skipTypes(skipPtrs), predicate, marker)
     if not result: result = searchTypeNodeForAux(t.n, predicate, marker)
-  of tyGenericInst, tyDistinct, tyAlias:
-    result = searchTypeForAux(lastSon(t), predicate, marker)
+  of tyGenericInst, tyDistinct, tyAlias, tySink:
+    result = searchTypeForAux(skipModifier(t), predicate, marker)
   of tyArray, tySet, tyTuple:
-    for i in countup(0, sonsLen(t) - 1):
-      result = searchTypeForAux(t.sons[i], predicate, marker)
+    for a in t.kids:
+      result = searchTypeForAux(a, predicate, marker)
       if result: return
   else:
     discard
 
-proc searchTypeFor(t: PType, predicate: TTypePredicate): bool =
+proc searchTypeFor*(t: PType, predicate: TTypePredicate): bool =
   var marker = initIntSet()
   result = searchTypeForAux(t, predicate, marker)
 
@@ -235,13 +306,18 @@ proc containsObject*(t: PType): bool =
   result = searchTypeFor(t, isObjectPredicate)
 
 proc isObjectWithTypeFieldPredicate(t: PType): bool =
-  result = t.kind == tyObject and t.sons[0] == nil and
+  result = t.kind == tyObject and t.baseClass == nil and
       not (t.sym != nil and {sfPure, sfInfixCall} * t.sym.flags != {}) and
       tfFinal notin t.flags
 
+type
+  TTypeFieldResult* = enum
+    frNone,                   # type has no object type field
+    frHeader,                 # type has an object type field only in the header
+    frEmbedded                # type has an object type field somewhere embedded
+
 proc analyseObjectWithTypeFieldAux(t: PType,
                                    marker: var IntSet): TTypeFieldResult =
-  var res: TTypeFieldResult
   result = frNone
   if t == nil: return
   case t.kind
@@ -249,38 +325,49 @@ proc analyseObjectWithTypeFieldAux(t: PType,
     if t.n != nil:
       if searchTypeNodeForAux(t.n, isObjectWithTypeFieldPredicate, marker):
         return frEmbedded
-    for i in countup(0, sonsLen(t) - 1):
-      var x = t.sons[i]
-      if x != nil: x = x.skipTypes(skipPtrs)
-      res = analyseObjectWithTypeFieldAux(x, marker)
-      if res == frEmbedded:
-        return frEmbedded
-      if res == frHeader: result = frHeader
+    var x = t.baseClass
+    if x != nil: x = x.skipTypes(skipPtrs)
+    let res = analyseObjectWithTypeFieldAux(x, marker)
+    if res == frEmbedded:
+      return frEmbedded
+    if res == frHeader: result = frHeader
     if result == frNone:
       if isObjectWithTypeFieldPredicate(t): result = frHeader
-  of tyGenericInst, tyDistinct, tyAlias:
-    result = analyseObjectWithTypeFieldAux(lastSon(t), marker)
+  of tyGenericInst, tyDistinct, tyAlias, tySink:
+    result = analyseObjectWithTypeFieldAux(skipModifier(t), marker)
   of tyArray, tyTuple:
-    for i in countup(0, sonsLen(t) - 1):
-      res = analyseObjectWithTypeFieldAux(t.sons[i], marker)
+    for a in t.kids:
+      let res = analyseObjectWithTypeFieldAux(a, marker)
       if res != frNone:
         return frEmbedded
   else:
     discard
 
-proc analyseObjectWithTypeField(t: PType): TTypeFieldResult =
+proc analyseObjectWithTypeField*(t: PType): TTypeFieldResult =
+  # this does a complex analysis whether a call to ``objectInit`` needs to be
+  # made or initializing of the type field suffices or if there is no type field
+  # at all in this type.
   var marker = initIntSet()
   result = analyseObjectWithTypeFieldAux(t, marker)
 
 proc isGCRef(t: PType): bool =
   result = t.kind in GcTypeKinds or
     (t.kind == tyProc and t.callConv == ccClosure)
+  if result and t.kind in {tyString, tySequence} and tfHasAsgn in t.flags:
+    result = false
 
 proc containsGarbageCollectedRef*(typ: PType): bool =
   # returns true if typ contains a reference, sequence or string (all the
   # things that are garbage-collected)
   result = searchTypeFor(typ, isGCRef)
 
+proc isManagedMemory(t: PType): bool =
+  result = t.kind in GcTypeKinds or
+    (t.kind == tyProc and t.callConv == ccClosure)
+
+proc containsManagedMemory*(typ: PType): bool =
+  result = searchTypeFor(typ, isManagedMemory)
+
 proc isTyRef(t: PType): bool =
   result = t.kind == tyRef or (t.kind == tyProc and t.callConv == ccClosure)
 
@@ -289,123 +376,130 @@ proc containsTyRef*(typ: PType): bool =
   result = searchTypeFor(typ, isTyRef)
 
 proc isHiddenPointer(t: PType): bool =
-  result = t.kind in {tyString, tySequence}
+  result = t.kind in {tyString, tySequence, tyOpenArray, tyVarargs}
 
 proc containsHiddenPointer*(typ: PType): bool =
   # returns true if typ contains a string, table or sequence (all the things
   # that need to be copied deeply)
   result = searchTypeFor(typ, isHiddenPointer)
 
-proc canFormAcycleAux(marker: var IntSet, typ: PType, startId: int): bool
-proc canFormAcycleNode(marker: var IntSet, n: PNode, startId: int): bool =
+proc canFormAcycleAux(g: ModuleGraph; marker: var IntSet, typ: PType, orig: PType, withRef: bool, hasTrace: bool): bool
+proc canFormAcycleNode(g: ModuleGraph; marker: var IntSet, n: PNode, orig: PType, withRef: bool, hasTrace: bool): bool =
   result = false
   if n != nil:
-    result = canFormAcycleAux(marker, n.typ, startId)
-    if not result:
-      case n.kind
-      of nkNone..nkNilLit:
-        discard
-      else:
-        for i in countup(0, sonsLen(n) - 1):
-          result = canFormAcycleNode(marker, n.sons[i], startId)
-          if result: return
+    var hasCursor = n.kind == nkSym and sfCursor in n.sym.flags
+    # cursor fields don't own the refs, which cannot form reference cycles
+    if hasTrace or not hasCursor:
+      result = canFormAcycleAux(g, marker, n.typ, orig, withRef, hasTrace)
+      if not result:
+        case n.kind
+        of nkNone..nkNilLit:
+          discard
+        else:
+          for i in 0..<n.len:
+            result = canFormAcycleNode(g, marker, n[i], orig, withRef, hasTrace)
+            if result: return
 
-proc canFormAcycleAux(marker: var IntSet, typ: PType, startId: int): bool =
+
+proc sameBackendType*(x, y: PType): bool
+proc canFormAcycleAux(g: ModuleGraph, marker: var IntSet, typ: PType, orig: PType, withRef: bool, hasTrace: bool): bool =
   result = false
   if typ == nil: return
   if tfAcyclic in typ.flags: return
-  var t = skipTypes(typ, abstractInst-{tyTypeDesc})
+  var t = skipTypes(typ, abstractInst+{tyOwned}-{tyTypeDesc})
   if tfAcyclic in t.flags: return
   case t.kind
-  of tyTuple, tyObject, tyRef, tySequence, tyArray, tyOpenArray, tyVarargs:
-    if not containsOrIncl(marker, t.id):
-      for i in countup(0, sonsLen(t) - 1):
-        result = canFormAcycleAux(marker, t.sons[i], startId)
+  of tyRef, tyPtr, tyUncheckedArray:
+    if t.kind == tyRef or hasTrace:
+      if withRef and sameBackendType(t, orig):
+        result = true
+      elif not containsOrIncl(marker, t.id):
+        result = canFormAcycleAux(g, marker, t.elementType, orig, withRef or t.kind != tyUncheckedArray, hasTrace)
+  of tyObject:
+    if withRef and sameBackendType(t, orig):
+      result = true
+    elif not containsOrIncl(marker, t.id):
+      var hasTrace = hasTrace
+      let op = getAttachedOp(g, t.skipTypes({tyRef}), attachedTrace)
+      if op != nil and sfOverridden in op.flags:
+        hasTrace = true
+      if t.baseClass != nil:
+        result = canFormAcycleAux(g, marker, t.baseClass, orig, withRef, hasTrace)
         if result: return
-      if t.n != nil: result = canFormAcycleNode(marker, t.n, startId)
-    else:
-      result = t.id == startId
+      if t.n != nil: result = canFormAcycleNode(g, marker, t.n, orig, withRef, hasTrace)
     # Inheritance can introduce cyclic types, however this is not relevant
     # as the type that is passed to 'new' is statically known!
     # er but we use it also for the write barrier ...
-    if t.kind == tyObject and tfFinal notin t.flags:
+    if tfFinal notin t.flags:
       # damn inheritance may introduce cycles:
       result = true
+  of tyTuple:
+    if withRef and sameBackendType(t, orig):
+      result = true
+    elif not containsOrIncl(marker, t.id):
+      for a in t.kids:
+        result = canFormAcycleAux(g, marker, a, orig, withRef, hasTrace)
+        if result: return
+  of tySequence, tyArray, tyOpenArray, tyVarargs:
+    if withRef and sameBackendType(t, orig):
+      result = true
+    elif not containsOrIncl(marker, t.id):
+      result = canFormAcycleAux(g, marker, t.elementType, orig, withRef, hasTrace)
   of tyProc: result = typ.callConv == ccClosure
   else: discard
 
-proc canFormAcycle*(typ: PType): bool =
-  var marker = initIntSet()
-  result = canFormAcycleAux(marker, typ, typ.id)
-
-proc mutateTypeAux(marker: var IntSet, t: PType, iter: TTypeMutator,
-                   closure: RootRef): PType
-proc mutateNode(marker: var IntSet, n: PNode, iter: TTypeMutator,
-                closure: RootRef): PNode =
-  result = nil
-  if n != nil:
-    result = copyNode(n)
-    result.typ = mutateTypeAux(marker, n.typ, iter, closure)
-    case n.kind
-    of nkNone..nkNilLit:
-      # a leaf
-      discard
-    else:
-      for i in countup(0, sonsLen(n) - 1):
-        addSon(result, mutateNode(marker, n.sons[i], iter, closure))
-
-proc mutateTypeAux(marker: var IntSet, t: PType, iter: TTypeMutator,
-                   closure: RootRef): PType =
-  result = nil
-  if t == nil: return
-  result = iter(t, closure)
-  if not containsOrIncl(marker, t.id):
-    for i in countup(0, sonsLen(t) - 1):
-      result.sons[i] = mutateTypeAux(marker, result.sons[i], iter, closure)
-    if t.n != nil: result.n = mutateNode(marker, t.n, iter, closure)
-  assert(result != nil)
+proc isFinal*(t: PType): bool =
+  let t = t.skipTypes(abstractInst)
+  result = t.kind != tyObject or tfFinal in t.flags or isPureObject(t)
 
-proc mutateType(t: PType, iter: TTypeMutator, closure: RootRef): PType =
+proc canFormAcycle*(g: ModuleGraph, typ: PType): bool =
   var marker = initIntSet()
-  result = mutateTypeAux(marker, t, iter, closure)
+  let t = skipTypes(typ, abstractInst+{tyOwned}-{tyTypeDesc})
+  result = canFormAcycleAux(g, marker, t, t, false, false)
 
 proc valueToString(a: PNode): string =
   case a.kind
-  of nkCharLit..nkUInt64Lit: result = $a.intVal
+  of nkCharLit, nkUIntLit..nkUInt64Lit:
+    result = $cast[uint64](a.intVal)
+  of nkIntLit..nkInt64Lit:
+    result = $a.intVal
   of nkFloatLit..nkFloat128Lit: result = $a.floatVal
   of nkStrLit..nkTripleStrLit: result = a.strVal
+  of nkStaticExpr: result = "static(" & a[0].renderTree & ")"
   else: result = "<invalid value>"
 
 proc rangeToStr(n: PNode): string =
   assert(n.kind == nkRange)
-  result = valueToString(n.sons[0]) & ".." & valueToString(n.sons[1])
+  result = valueToString(n[0]) & ".." & valueToString(n[1])
 
 const
-  typeToStr: array[TTypeKind, string] = ["None", "bool", "Char", "empty",
-    "Alias", "nil", "untyped", "typed", "typeDesc",
+  typeToStr: array[TTypeKind, string] = ["None", "bool", "char", "empty",
+    "Alias", "typeof(nil)", "untyped", "typed", "typeDesc",
+    # xxx typeDesc=>typedesc: typedesc is declared as such, and is 10x more common.
     "GenericInvocation", "GenericBody", "GenericInst", "GenericParam",
     "distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple",
     "set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc",
-    "pointer", "OpenArray[$1]", "string", "CString", "Forward",
+    "pointer", "OpenArray[$1]", "string", "cstring", "Forward",
     "int", "int8", "int16", "int32", "int64",
     "float", "float32", "float64", "float128",
     "uint", "uint8", "uint16", "uint32", "uint64",
-    "opt", "sink",
-    "lent", "varargs[$1]", "unused", "Error Type",
+    "owned", "sink",
+    "lent ", "varargs[$1]", "UncheckedArray[$1]", "Error Type",
     "BuiltInTypeClass", "UserTypeClass",
     "UserTypeClassInst", "CompositeTypeClass", "inferred",
-    "and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor",
-    "void"]
+    "and", "or", "not", "any", "static", "TypeFromExpr", "concept", # xxx bugfix
+    "void", "iterable"]
 
-const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo, preferGenericArg}
+const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo,
+  preferGenericArg, preferResolved, preferMixed, preferInlayHint, preferInferredEffects}
 
 template bindConcreteTypeToUserTypeClass*(tc, concrete: PType) =
-  tc.sons.safeAdd concrete
+  tc.add concrete
   tc.flags.incl tfResolved
 
 # TODO: It would be a good idea to kill the special state of a resolved
 # concept by switching to tyAlias within the instantiated procs.
-# Currently, tyAlias is always skipped with lastSon, which means that
+# Currently, tyAlias is always skipped with skipModifier, which means that
 # we can store information about the matched concept in another position.
 # Then builtInFieldAccess can be modified to properly read the derived
 # consts and types stored within the concept.
@@ -416,266 +510,474 @@ proc addTypeFlags(name: var string, typ: PType) {.inline.} =
   if tfNotNil in typ.flags: name.add(" not nil")
 
 proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
-  var t = typ
-  result = ""
-  if t == nil: return
-  if prefer in preferToResolveSymbols and t.sym != nil and
-       sfAnon notin t.sym.flags:
-    if t.kind == tyInt and isIntLit(t):
-      result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
-    elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil:
-      result = t.sym.name.s
-      if t.kind == tyGenericParam and t.sons != nil and t.sonsLen > 0:
-        result.add ": "
-        var first = true
-        for son in t.sons:
-          if not first: result.add " or "
-          result.add son.typeToString
-          first = false
+  let preferToplevel = prefer
+  proc getPrefer(prefer: TPreferedDesc): TPreferedDesc =
+    if preferToplevel in {preferResolved, preferMixed}:
+      preferToplevel # sticky option
     else:
-      result = t.sym.owner.name.s & '.' & t.sym.name.s
+      prefer
+
+  proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
+    result = ""
+    let prefer = getPrefer(prefer)
+    let t = typ
+    if t == nil: return
+    if prefer in preferToResolveSymbols and t.sym != nil and
+         sfAnon notin t.sym.flags and t.kind != tySequence:
+      if t.kind == tyInt and isIntLit(t):
+        if prefer == preferInlayHint:
+          result = t.sym.name.s
+        else:
+          result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
+      elif t.kind == tyAlias and t.elementType.kind != tyAlias:
+        result = typeToString(t.elementType)
+      elif prefer in {preferResolved, preferMixed}:
+        case t.kind
+        of IntegralTypes + {tyFloat..tyFloat128} + {tyString, tyCstring}:
+          result = typeToStr[t.kind]
+        of tyGenericBody:
+          result = typeToString(t.last)
+        of tyCompositeTypeClass:
+          # avoids showing `A[any]` in `proc fun(a: A)` with `A = object[T]`
+          result = typeToString(t.last.last)
+        else:
+          result = t.sym.name.s
+        if prefer == preferMixed and result != t.sym.name.s:
+          result = t.sym.name.s & "{" & result & "}"
+      elif prefer in {preferName, preferTypeName, preferInlayHint, preferInferredEffects} or t.sym.owner.isNil:
+        # note: should probably be: {preferName, preferTypeName, preferGenericArg}
+        result = t.sym.name.s
+        if t.kind == tyGenericParam and t.genericParamHasConstraints:
+          result.add ": "
+          result.add t.elementType.typeToString
+      else:
+        result = t.sym.owner.name.s & '.' & t.sym.name.s
+      result.addTypeFlags(t)
+      return
+    case t.kind
+    of tyInt:
+      if not isIntLit(t) or prefer == preferExported:
+        result = typeToStr[t.kind]
+      else:
+        case prefer:
+        of preferGenericArg:
+          result = $t.n.intVal
+        of preferInlayHint:
+          result = "int"
+        else:
+          result = "int literal(" & $t.n.intVal & ")"
+    of tyGenericInst:
+      result = typeToString(t.genericHead) & '['
+      for needsComma, a in t.genericInstParams:
+        if needsComma: result.add(", ")
+        result.add(typeToString(a, preferGenericArg))
+      result.add(']')
+    of tyGenericInvocation:
+      result = typeToString(t.genericHead) & '['
+      for needsComma, a in t.genericInvocationParams:
+        if needsComma: result.add(", ")
+        result.add(typeToString(a, preferGenericArg))
+      result.add(']')
+    of tyGenericBody:
+      result = typeToString(t.typeBodyImpl) & '['
+      for i, a in t.genericBodyParams:
+        if i > 0: result.add(", ")
+        result.add(typeToString(a, preferTypeName))
+      result.add(']')
+    of tyTypeDesc:
+      if t.elementType.kind == tyNone: result = "typedesc"
+      else: result = "typedesc[" & typeToString(t.elementType) & "]"
+    of tyStatic:
+      if prefer == preferGenericArg and t.n != nil:
+        result = t.n.renderTree
+      else:
+        result = "static[" & (if t.hasElementType: typeToString(t.skipModifier) else: "") & "]"
+        if t.n != nil: result.add "(" & renderTree(t.n) & ")"
+    of tyUserTypeClass:
+      if t.sym != nil and t.sym.owner != nil:
+        if t.isResolvedUserTypeClass: return typeToString(t.last)
+        return t.sym.owner.name.s
+      else:
+        result = "<invalid tyUserTypeClass>"
+    of tyBuiltInTypeClass:
+      result =
+        case t.base.kind
+        of tyVar: "var"
+        of tyRef: "ref"
+        of tyPtr: "ptr"
+        of tySequence: "seq"
+        of tyArray: "array"
+        of tySet: "set"
+        of tyRange: "range"
+        of tyDistinct: "distinct"
+        of tyProc: "proc"
+        of tyObject: "object"
+        of tyTuple: "tuple"
+        of tyOpenArray: "openArray"
+        else: typeToStr[t.base.kind]
+    of tyInferred:
+      let concrete = t.previouslyInferred
+      if concrete != nil: result = typeToString(concrete)
+      else: result = "inferred[" & typeToString(t.base) & "]"
+    of tyUserTypeClassInst:
+      let body = t.base
+      result = body.sym.name.s & "["
+      for needsComma, a in t.userTypeClassInstParams:
+        if needsComma: result.add(", ")
+        result.add(typeToString(a))
+      result.add "]"
+    of tyAnd:
+      for i, son in t.ikids:
+        if i > 0: result.add(" and ")
+        result.add(typeToString(son))
+    of tyOr:
+      for i, son in t.ikids:
+        if i > 0: result.add(" or ")
+        result.add(typeToString(son))
+    of tyNot:
+      result = "not " & typeToString(t.elementType)
+    of tyUntyped:
+      #internalAssert t.len == 0
+      result = "untyped"
+    of tyFromExpr:
+      if t.n == nil:
+        result = "unknown"
+      else:
+        result = "typeof(" & renderTree(t.n) & ")"
+    of tyArray:
+      result = "array"
+      if t.hasElementType:
+        if t.indexType.kind == tyRange:
+          result &= "[" & rangeToStr(t.indexType.n) & ", " &
+              typeToString(t.elementType) & ']'
+        else:
+          result &= "[" & typeToString(t.indexType) & ", " &
+              typeToString(t.elementType) & ']'
+    of tyUncheckedArray:
+      result = "UncheckedArray"
+      if t.hasElementType:
+        result &= "[" & typeToString(t.elementType) & ']'
+    of tySequence:
+      if t.sym != nil and prefer != preferResolved:
+        result = t.sym.name.s
+      else:
+        result = "seq"
+        if t.hasElementType:
+          result &= "[" & typeToString(t.elementType) & ']'
+    of tyOrdinal:
+      result = "ordinal"
+      if t.hasElementType:
+        result &= "[" & typeToString(t.skipModifier) & ']'
+    of tySet:
+      result = "set"
+      if t.hasElementType:
+        result &= "[" & typeToString(t.elementType) & ']'
+    of tyOpenArray:
+      result = "openArray"
+      if t.hasElementType:
+        result &= "[" & typeToString(t.elementType) & ']'
+    of tyDistinct:
+      result = "distinct " & typeToString(t.elementType,
+        if prefer == preferModuleInfo: preferModuleInfo else: preferTypeName)
+    of tyIterable:
+      # xxx factor this pattern
+      result = "iterable"
+      if t.hasElementType:
+        result &= "[" & typeToString(t.skipModifier) & ']'
+    of tyTuple:
+      # we iterate over t.sons here, because t.n may be nil
+      if t.n != nil:
+        result = "tuple["
+        for i in 0..<t.n.len:
+          assert(t.n[i].kind == nkSym)
+          result.add(t.n[i].sym.name.s & ": " & typeToString(t.n[i].sym.typ))
+          if i < t.n.len - 1: result.add(", ")
+        result.add(']')
+      elif t.isEmptyTupleType:
+        result = "tuple[]"
+      elif t.isSingletonTupleType:
+        result = "("
+        for son in t.kids:
+          result.add(typeToString(son))
+        result.add(",)")
+      else:
+        result = "("
+        for i, son in t.ikids:
+          if i > 0: result.add ", "
+          result.add(typeToString(son))
+        result.add(')')
+    of tyPtr, tyRef, tyVar, tyLent:
+      result = if isOutParam(t): "out " else: typeToStr[t.kind]
+      result.add typeToString(t.elementType)
+    of tyRange:
+      result = "range "
+      if t.n != nil and t.n.kind == nkRange:
+        result.add rangeToStr(t.n)
+      if prefer != preferExported:
+        result.add("(" & typeToString(t.elementType) & ")")
+    of tyProc:
+      result = if tfIterator in t.flags: "iterator "
+               elif t.owner != nil:
+                 case t.owner.kind
+                 of skTemplate: "template "
+                 of skMacro: "macro "
+                 of skConverter: "converter "
+                 else: "proc "
+              else:
+                "proc "
+      if tfUnresolved in t.flags: result.add "[*missing parameters*]"
+      result.add "("
+      for i, a in t.paramTypes:
+        if i > FirstParamAt: result.add(", ")
+        let j = paramTypeToNodeIndex(i)
+        if t.n != nil and j < t.n.len and t.n[j].kind == nkSym:
+          result.add(t.n[j].sym.name.s)
+          result.add(": ")
+        result.add(typeToString(a))
+      result.add(')')
+      if t.returnType != nil: result.add(": " & typeToString(t.returnType))
+      var prag = if t.callConv == ccNimCall and tfExplicitCallConv notin t.flags: "" else: $t.callConv
+      var hasImplicitRaises = false
+      if not isNil(t.owner) and not isNil(t.owner.ast) and (t.owner.ast.len - 1) >= pragmasPos:
+        let pragmasNode = t.owner.ast[pragmasPos]
+        let raisesSpec = effectSpec(pragmasNode, wRaises)
+        if not isNil(raisesSpec):
+          addSep(prag)
+          prag.add("raises: ")
+          prag.add($raisesSpec)
+          hasImplicitRaises = true
+      if tfNoSideEffect in t.flags:
+        addSep(prag)
+        prag.add("noSideEffect")
+      if tfThread in t.flags:
+        addSep(prag)
+        prag.add("gcsafe")
+      var effectsOfStr = ""
+      for i, a in t.paramTypes:
+        let j = paramTypeToNodeIndex(i)
+        if t.n != nil and j < t.n.len and t.n[j].kind == nkSym and t.n[j].sym.kind == skParam and sfEffectsDelayed in t.n[j].sym.flags:
+          addSep(effectsOfStr)
+          effectsOfStr.add(t.n[j].sym.name.s)
+      if effectsOfStr != "":
+        addSep(prag)
+        prag.add("effectsOf: ")
+        prag.add(effectsOfStr)
+      if not hasImplicitRaises and prefer == preferInferredEffects and not isNil(t.owner) and not isNil(t.owner.typ) and not isNil(t.owner.typ.n) and (t.owner.typ.n.len > 0):
+        let effects = t.owner.typ.n[0]
+        if effects.kind == nkEffectList and effects.len == effectListLen:
+          var inferredRaisesStr = ""
+          let effs = effects[exceptionEffects]
+          if not isNil(effs):
+            for eff in items(effs):
+              if not isNil(eff):
+                addSep(inferredRaisesStr)
+                inferredRaisesStr.add($eff.typ)
+          addSep(prag)
+          prag.add("raises: <inferred> [")
+          prag.add(inferredRaisesStr)
+          prag.add("]")
+      if prag.len != 0: result.add("{." & prag & ".}")
+    of tyVarargs:
+      result = typeToStr[t.kind] % typeToString(t.elementType)
+    of tySink:
+      result = "sink " & typeToString(t.skipModifier)
+    of tyOwned:
+      result = "owned " & typeToString(t.elementType)
+    else:
+      result = typeToStr[t.kind]
     result.addTypeFlags(t)
-    return
+  result = typeToString(typ, prefer)
+
+proc firstOrd*(conf: ConfigRef; t: PType): Int128 =
   case t.kind
+  of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyError:
+    result = Zero
+  of tySet, tyVar: result = firstOrd(conf, t.elementType)
+  of tyArray: result = firstOrd(conf, t.indexType)
+  of tyRange:
+    assert(t.n != nil)        # range directly given:
+    assert(t.n.kind == nkRange)
+    result = getOrdValue(t.n[0])
   of tyInt:
-    if not isIntLit(t) or prefer == preferExported:
-      result = typeToStr[t.kind]
-    else:
-      if prefer == preferGenericArg:
-        result = $t.n.intVal
-      else:
-        result = "int literal(" & $t.n.intVal & ")"
-  of tyGenericBody, tyGenericInst, tyGenericInvocation:
-    result = typeToString(t.sons[0]) & '['
-    for i in countup(1, sonsLen(t)-1-ord(t.kind != tyGenericInvocation)):
-      if i > 1: add(result, ", ")
-      add(result, typeToString(t.sons[i], preferGenericArg))
-    add(result, ']')
-  of tyTypeDesc:
-    if t.sons[0].kind == tyNone: result = "typedesc"
-    else: result = "type " & typeToString(t.sons[0])
-  of tyStatic:
-    if prefer == preferGenericArg and t.n != nil:
-      result = t.n.renderTree
+    if conf != nil:
+      case conf.target.intSize
+      of 8: result = toInt128(0x8000000000000000'i64)
+      of 4: result = toInt128(-2147483648)
+      of 2: result = toInt128(-32768)
+      of 1: result = toInt128(-128)
+      else: result = Zero
     else:
-      result = "static[" & (if t.len > 0: typeToString(t.sons[0]) else: "") & "]"
-      if t.n != nil: result.add "(" & renderTree(t.n) & ")"
-  of tyUserTypeClass:
-    if t.sym != nil and t.sym.owner != nil:
-      if t.isResolvedUserTypeClass: return typeToString(t.lastSon)
-      return t.sym.owner.name.s
-    else:
-      result = "<invalid tyUserTypeClass>"
-  of tyBuiltInTypeClass:
-    result = case t.base.kind:
-      of tyVar: "var"
-      of tyRef: "ref"
-      of tyPtr: "ptr"
-      of tySequence: "seq"
-      of tyArray: "array"
-      of tySet: "set"
-      of tyRange: "range"
-      of tyDistinct: "distinct"
-      of tyProc: "proc"
-      of tyObject: "object"
-      of tyTuple: "tuple"
-      of tyOpenArray: "openarray"
-      else: typeToStr[t.base.kind]
-  of tyInferred:
-    let concrete = t.previouslyInferred
-    if concrete != nil: result = typeToString(concrete)
-    else: result = "inferred[" & typeToString(t.base) & "]"
-  of tyUserTypeClassInst:
-    let body = t.base
-    result = body.sym.name.s & "["
-    for i in countup(1, sonsLen(t) - 2):
-      if i > 1: add(result, ", ")
-      add(result, typeToString(t.sons[i]))
-    result.add "]"
-  of tyAnd:
-    result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1])
-  of tyOr:
-    result = typeToString(t.sons[0]) & " or " & typeToString(t.sons[1])
-  of tyNot:
-    result = "not " & typeToString(t.sons[0])
-  of tyExpr:
-    #internalAssert t.len == 0
-    result = "untyped"
-  of tyFromExpr:
-    result = renderTree(t.n)
-  of tyArray:
-    if t.sons[0].kind == tyRange:
-      result = "array[" & rangeToStr(t.sons[0].n) & ", " &
-          typeToString(t.sons[1]) & ']'
+      result = toInt128(0x8000000000000000'i64)
+  of tyInt8: result =  toInt128(-128)
+  of tyInt16: result = toInt128(-32768)
+  of tyInt32: result = toInt128(-2147483648)
+  of tyInt64: result = toInt128(0x8000000000000000'i64)
+  of tyUInt..tyUInt64: result = Zero
+  of tyEnum:
+    # if basetype <> nil then return firstOrd of basetype
+    if t.baseClass != nil:
+      result = firstOrd(conf, t.baseClass)
     else:
-      result = "array[" & typeToString(t.sons[0]) & ", " &
-          typeToString(t.sons[1]) & ']'
-  of tySequence:
-    result = "seq[" & typeToString(t.sons[0]) & ']'
-  of tyOpt:
-    result = "opt[" & typeToString(t.sons[0]) & ']'
+      if t.n.len > 0:
+        assert(t.n[0].kind == nkSym)
+        result = toInt128(t.n[0].sym.position)
+      else:
+        result = Zero
+  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
+     tyStatic, tyInferred, tyLent:
+    result = firstOrd(conf, skipModifier(t))
+  of tyUserTypeClasses:
+    result = firstOrd(conf, last(t))
   of tyOrdinal:
-    result = "ordinal[" & typeToString(t.sons[0]) & ']'
-  of tySet:
-    result = "set[" & typeToString(t.sons[0]) & ']'
-  of tyOpenArray:
-    result = "openarray[" & typeToString(t.sons[0]) & ']'
-  of tyDistinct:
-    result = "distinct " & typeToString(t.sons[0],
-      if prefer == preferModuleInfo: preferModuleInfo else: preferTypeName)
-  of tyTuple:
-    # we iterate over t.sons here, because t.n may be nil
-    if t.n != nil:
-      result = "tuple["
-      assert(sonsLen(t.n) == sonsLen(t))
-      for i in countup(0, sonsLen(t.n) - 1):
-        assert(t.n.sons[i].kind == nkSym)
-        add(result, t.n.sons[i].sym.name.s & ": " & typeToString(t.sons[i]))
-        if i < sonsLen(t.n) - 1: add(result, ", ")
-      add(result, ']')
-    elif sonsLen(t) == 0:
-      result = "tuple[]"
+    if t.hasElementType: result = firstOrd(conf, skipModifier(t))
     else:
-      if prefer == preferTypeName: result = "("
-      else: result = "tuple of ("
-      for i in countup(0, sonsLen(t) - 1):
-        add(result, typeToString(t.sons[i]))
-        if i < sonsLen(t) - 1: add(result, ", ")
-      add(result, ')')
-  of tyPtr, tyRef, tyVar, tyLent:
-    result = typeToStr[t.kind]
-    if t.len >= 2:
-      setLen(result, result.len-1)
-      result.add '['
-      for i in countup(0, sonsLen(t) - 1):
-        add(result, typeToString(t.sons[i]))
-        if i < sonsLen(t) - 1: add(result, ", ")
-      result.add ']'
-    else:
-      result.add typeToString(t.sons[0])
-  of tyRange:
-    result = "range "
-    if t.n != nil and t.n.kind == nkRange:
-      result.add rangeToStr(t.n)
-    if prefer != preferExported:
-      result.add("(" & typeToString(t.sons[0]) & ")")
-  of tyProc:
-    result = if tfIterator in t.flags: "iterator " else: "proc "
-    if tfUnresolved in t.flags: result.add "[*missing parameters*]"
-    result.add "("
-    for i in countup(1, sonsLen(t) - 1):
-      if t.n != nil and i < t.n.len and t.n[i].kind == nkSym:
-        add(result, t.n[i].sym.name.s)
-        add(result, ": ")
-      add(result, typeToString(t.sons[i]))
-      if i < sonsLen(t) - 1: add(result, ", ")
-    add(result, ')')
-    if t.len > 0 and t.sons[0] != nil: add(result, ": " & typeToString(t.sons[0]))
-    var prag = if t.callConv == ccDefault: "" else: CallingConvToStr[t.callConv]
-    if tfNoSideEffect in t.flags:
-      addSep(prag)
-      add(prag, "noSideEffect")
-    if tfThread in t.flags:
-      addSep(prag)
-      add(prag, "gcsafe")
-    if t.lockLevel.ord != UnspecifiedLockLevel.ord:
-      addSep(prag)
-      add(prag, "locks: " & $t.lockLevel)
-    if len(prag) != 0: add(result, "{." & prag & ".}")
-  of tyVarargs:
-    result = typeToStr[t.kind] % typeToString(t.sons[0])
-  of tySink:
-    result = "sink " & typeToString(t.sons[0])
+      result = Zero
+      fatal(conf, unknownLineInfo, "invalid kind for firstOrd(" & $t.kind & ')')
+  of tyUncheckedArray, tyCstring:
+    result = Zero
   else:
-    result = typeToStr[t.kind]
-  result.addTypeFlags(t)
+    result = Zero
+    fatal(conf, unknownLineInfo, "invalid kind for firstOrd(" & $t.kind & ')')
 
-proc firstOrd*(conf: ConfigRef; t: PType): BiggestInt =
+proc firstFloat*(t: PType): BiggestFloat =
   case t.kind
-  of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy:
-    result = 0
-  of tySet, tyVar: result = firstOrd(conf, t.sons[0])
-  of tyArray: result = firstOrd(conf, t.sons[0])
+  of tyFloat..tyFloat128: -Inf
   of tyRange:
     assert(t.n != nil)        # range directly given:
     assert(t.n.kind == nkRange)
-    result = getOrdValue(t.n.sons[0])
+    getFloatValue(t.n[0])
+  of tyVar: firstFloat(t.elementType)
+  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
+     tyStatic, tyInferred:
+    firstFloat(skipModifier(t))
+  of tyUserTypeClasses:
+    firstFloat(last(t))
+  else:
+    internalError(newPartialConfigRef(), "invalid kind for firstFloat(" & $t.kind & ')')
+    NaN
+
+proc targetSizeSignedToKind*(conf: ConfigRef): TTypeKind =
+  case conf.target.intSize
+  of 8: result = tyInt64
+  of 4: result = tyInt32
+  of 2: result = tyInt16
+  else: result = tyNone
+
+proc targetSizeUnsignedToKind*(conf: ConfigRef): TTypeKind =
+  case conf.target.intSize
+  of 8: result = tyUInt64
+  of 4: result = tyUInt32
+  of 2: result = tyUInt16
+  else: result = tyNone
+
+proc normalizeKind*(conf: ConfigRef, k: TTypeKind): TTypeKind =
+  case k
   of tyInt:
-    if conf != nil and conf.target.intSize == 4: result = - (2147483646) - 2
-    else: result = 0x8000000000000000'i64
-  of tyInt8: result = - 128
-  of tyInt16: result = - 32768
-  of tyInt32: result = - 2147483646 - 2
-  of tyInt64: result = 0x8000000000000000'i64
-  of tyUInt..tyUInt64: result = 0
-  of tyEnum:
-    # if basetype <> nil then return firstOrd of basetype
-    if sonsLen(t) > 0 and t.sons[0] != nil:
-      result = firstOrd(conf, t.sons[0])
-    else:
-      assert(t.n.sons[0].kind == nkSym)
-      result = t.n.sons[0].sym.position
-  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred:
-    result = firstOrd(conf, lastSon(t))
-  of tyOrdinal:
-    if t.len > 0: result = firstOrd(conf, lastSon(t))
-    else: internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')')
+    result = conf.targetSizeSignedToKind()
+  of tyUInt:
+    result = conf.targetSizeUnsignedToKind()
   else:
-    internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')')
-    result = 0
+    result = k
 
-proc lastOrd*(conf: ConfigRef; t: PType; fixedUnsigned = false): BiggestInt =
+proc lastOrd*(conf: ConfigRef; t: PType): Int128 =
   case t.kind
-  of tyBool: result = 1
-  of tyChar: result = 255
-  of tySet, tyVar: result = lastOrd(conf, t.sons[0])
-  of tyArray: result = lastOrd(conf, t.sons[0])
+  of tyBool: result = toInt128(1'u)
+  of tyChar: result = toInt128(255'u)
+  of tySet, tyVar: result = lastOrd(conf, t.elementType)
+  of tyArray: result = lastOrd(conf, t.indexType)
   of tyRange:
     assert(t.n != nil)        # range directly given:
     assert(t.n.kind == nkRange)
-    result = getOrdValue(t.n.sons[1])
+    result = getOrdValue(t.n[1])
   of tyInt:
-    if conf != nil and conf.target.intSize == 4: result = 0x7FFFFFFF
-    else: result = 0x7FFFFFFFFFFFFFFF'i64
-  of tyInt8: result = 0x0000007F
-  of tyInt16: result = 0x00007FFF
-  of tyInt32: result = 0x7FFFFFFF
-  of tyInt64: result = 0x7FFFFFFFFFFFFFFF'i64
+    if conf != nil:
+      case conf.target.intSize
+      of 8: result = toInt128(0x7FFFFFFFFFFFFFFF'u64)
+      of 4: result = toInt128(0x7FFFFFFF)
+      of 2: result = toInt128(0x00007FFF)
+      of 1: result = toInt128(0x0000007F)
+      else: result = Zero
+    else: result = toInt128(0x7FFFFFFFFFFFFFFF'u64)
+  of tyInt8: result = toInt128(0x0000007F)
+  of tyInt16: result = toInt128(0x00007FFF)
+  of tyInt32: result = toInt128(0x7FFFFFFF)
+  of tyInt64: result = toInt128(0x7FFFFFFFFFFFFFFF'u64)
   of tyUInt:
-    if conf != nil and conf.target.intSize == 4: result = 0xFFFFFFFF
-    elif fixedUnsigned: result = 0xFFFFFFFFFFFFFFFF'i64
-    else: result = 0x7FFFFFFFFFFFFFFF'i64
-  of tyUInt8: result = 0xFF
-  of tyUInt16: result = 0xFFFF
-  of tyUInt32: result = 0xFFFFFFFF
+    if conf != nil and conf.target.intSize == 4:
+      result = toInt128(0xFFFFFFFF)
+    else:
+      result = toInt128(0xFFFFFFFFFFFFFFFF'u64)
+  of tyUInt8: result = toInt128(0xFF)
+  of tyUInt16: result = toInt128(0xFFFF)
+  of tyUInt32: result = toInt128(0xFFFFFFFF)
   of tyUInt64:
-    if fixedUnsigned: result = 0xFFFFFFFFFFFFFFFF'i64
-    else: result = 0x7FFFFFFFFFFFFFFF'i64
+    result = toInt128(0xFFFFFFFFFFFFFFFF'u64)
   of tyEnum:
-    assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym)
-    result = t.n.sons[sonsLen(t.n) - 1].sym.position
-  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred:
-    result = lastOrd(conf, lastSon(t))
-  of tyProxy: result = 0
+    if t.n.len > 0:
+      assert(t.n[^1].kind == nkSym)
+      result = toInt128(t.n[^1].sym.position)
+    else:
+      result = Zero
+  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
+     tyStatic, tyInferred, tyLent:
+    result = lastOrd(conf, skipModifier(t))
+  of tyUserTypeClasses:
+    result = lastOrd(conf, last(t))
+  of tyError: result = Zero
   of tyOrdinal:
-    if t.len > 0: result = lastOrd(conf, lastSon(t))
-    else: internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')')
+    if t.hasElementType: result = lastOrd(conf, skipModifier(t))
+    else:
+      result = Zero
+      fatal(conf, unknownLineInfo, "invalid kind for lastOrd(" & $t.kind & ')')
+  of tyUncheckedArray:
+    result = Zero
+  else:
+    result = Zero
+    fatal(conf, unknownLineInfo, "invalid kind for lastOrd(" & $t.kind & ')')
+
+proc lastFloat*(t: PType): BiggestFloat =
+  case t.kind
+  of tyFloat..tyFloat128: Inf
+  of tyVar: lastFloat(t.elementType)
+  of tyRange:
+    assert(t.n != nil)        # range directly given:
+    assert(t.n.kind == nkRange)
+    getFloatValue(t.n[1])
+  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
+     tyStatic, tyInferred:
+    lastFloat(skipModifier(t))
+  of tyUserTypeClasses:
+    lastFloat(last(t))
   else:
-    internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')')
-    result = 0
+    internalError(newPartialConfigRef(), "invalid kind for lastFloat(" & $t.kind & ')')
+    NaN
 
-proc lengthOrd*(conf: ConfigRef; t: PType): BiggestInt =
+proc floatRangeCheck*(x: BiggestFloat, t: PType): bool =
   case t.kind
-  of tyInt64, tyInt32, tyInt: result = lastOrd(conf, t)
-  of tyDistinct: result = lengthOrd(conf, t.sons[0])
+  # This needs to be special cased since NaN is never
+  # part of firstFloat(t)..lastFloat(t)
+  of tyFloat..tyFloat128:
+    true
+  of tyRange:
+    x in firstFloat(t)..lastFloat(t)
+  of tyVar:
+    floatRangeCheck(x, t.elementType)
+  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
+     tyStatic, tyInferred:
+    floatRangeCheck(x, skipModifier(t))
+  of tyUserTypeClasses:
+    floatRangeCheck(x, last(t))
+  else:
+    internalError(newPartialConfigRef(), "invalid kind for floatRangeCheck:" & $t.kind)
+    false
+
+proc lengthOrd*(conf: ConfigRef; t: PType): Int128 =
+  if t.skipTypes(tyUserTypeClasses).kind == tyDistinct:
+    result = lengthOrd(conf, t.skipModifier)
   else:
     let last = lastOrd(conf, t)
     let first = firstOrd(conf, t)
-    # XXX use a better overflow check here:
-    if last == high(BiggestInt) and first <= 0:
-      result = last
-    else:
-      result = lastOrd(conf, t) - firstOrd(conf, t) + 1
+    result = last - first + One
 
 # -------------- type equality -----------------------------------------------
 
@@ -694,10 +996,14 @@ type
     ExactConstraints
     ExactGcSafety
     AllowCommonBase
+    PickyCAliases  # be picky about the distinction between 'cint' and 'int32'
+    IgnoreFlags    # used for borrowed functions and methods; ignores the tfVarIsPtr flag
+    PickyBackendAliases # be picky about different aliases
+    IgnoreRangeShallow
 
   TTypeCmpFlags* = set[TTypeCmpFlag]
 
-  TSameTypeClosure = object {.pure.}
+  TSameTypeClosure = object
     cmp: TDistinctCompare
     recCheck: int
     flags: TTypeCmpFlags
@@ -706,12 +1012,11 @@ type
 
 proc initSameTypeClosure: TSameTypeClosure =
   # we do the initialization lazily for performance (avoids memory allocations)
-  discard
+  result = TSameTypeClosure()
 
 proc containsOrIncl(c: var TSameTypeClosure, a, b: PType): bool =
-  result = not isNil(c.s) and c.s.contains((a.id, b.id))
+  result = c.s.len > 0 and c.s.contains((a.id, b.id))
   if not result:
-    if isNil(c.s): c.s = @[]
     c.s.add((a.id, b.id))
 
 proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool
@@ -746,13 +1051,15 @@ proc equalParam(a, b: PSym): TParamsEquality =
       result = paramsEqual
     elif b.ast != nil:
       result = paramsIncompatible
+    else:
+      result = paramsNotEqual
   else:
     result = paramsNotEqual
 
 proc sameConstraints(a, b: PNode): bool =
   if isNil(a) and isNil(b): return true
   if a.len != b.len: return false
-  for i in 1 ..< a.len:
+  for i in 1..<a.len:
     if not exprStructuralEquivalent(a[i].sym.constraint,
                                     b[i].sym.constraint):
       return false
@@ -760,13 +1067,12 @@ proc sameConstraints(a, b: PNode): bool =
 
 proc equalParams(a, b: PNode): TParamsEquality =
   result = paramsEqual
-  var length = sonsLen(a)
-  if length != sonsLen(b):
+  if a.len != b.len:
     result = paramsNotEqual
   else:
-    for i in countup(1, length - 1):
-      var m = a.sons[i].sym
-      var n = b.sons[i].sym
+    for i in 1..<a.len:
+      var m = a[i].sym
+      var n = b[i].sym
       assert((m.kind == skParam) and (n.kind == skParam))
       case equalParam(m, n)
       of paramsNotEqual:
@@ -775,7 +1081,7 @@ proc equalParams(a, b: PNode): TParamsEquality =
         discard
       of paramsIncompatible:
         result = paramsIncompatible
-      if (m.name.id != n.name.id):
+      if m.name.id != n.name.id:
         # BUGFIX
         return paramsNotEqual # paramsIncompatible;
       # continue traversal! If not equal, we can return immediately; else
@@ -791,11 +1097,11 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool =
   # two tuples are equivalent iff the names, types and positions are the same;
   # however, both types may not have any field names (t.n may be nil) which
   # complicates the matter a bit.
-  if sonsLen(a) == sonsLen(b):
+  if sameTupleLengths(a, b):
     result = true
-    for i in countup(0, sonsLen(a) - 1):
-      var x = a.sons[i]
-      var y = b.sons[i]
+    for i, aa, bb in tupleTypePairs(a, b):
+      var x = aa
+      var y = bb
       if IgnoreTupleFields in c.flags:
         x = skipTypes(x, {tyRange, tyGenericInst, tyAlias})
         y = skipTypes(y, {tyRange, tyGenericInst, tyAlias})
@@ -803,17 +1109,19 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool =
       result = sameTypeAux(x, y, c)
       if not result: return
     if a.n != nil and b.n != nil and IgnoreTupleFields notin c.flags:
-      for i in countup(0, sonsLen(a.n) - 1):
+      for i in 0..<a.n.len:
         # check field names:
-        if a.n.sons[i].kind == nkSym and b.n.sons[i].kind == nkSym:
-          var x = a.n.sons[i].sym
-          var y = b.n.sons[i].sym
+        if a.n[i].kind == nkSym and b.n[i].kind == nkSym:
+          var x = a.n[i].sym
+          var y = b.n[i].sym
           result = x.name.id == y.name.id
           if not result: break
         else:
           return false
     elif a.n != b.n and (a.n == nil or b.n == nil) and IgnoreTupleFields notin c.flags:
       result = false
+  else:
+    result = false
 
 template ifFastObjectTypeCheckFailed(a, b: PType, body: untyped) =
   if tfFromGeneric notin a.flags + b.flags:
@@ -833,6 +1141,8 @@ template ifFastObjectTypeCheckFailed(a, b: PType, body: untyped) =
     if tfFromGeneric in a.flags * b.flags and a.sym.id == b.sym.id:
       # ok, we need the expensive structural check
       body
+    else:
+      result = false
 
 proc sameObjectTypes*(a, b: PType): bool =
   # specialized for efficiency (sigmatch uses it)
@@ -866,36 +1176,48 @@ proc sameObjectTree(a, b: PNode, c: var TSameTypeClosure): bool =
       of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
       of nkEmpty, nkNilLit, nkType: result = true
       else:
-        if sonsLen(a) == sonsLen(b):
-          for i in countup(0, sonsLen(a) - 1):
-            if not sameObjectTree(a.sons[i], b.sons[i], c): return
+        if a.len == b.len:
+          for i in 0..<a.len:
+            if not sameObjectTree(a[i], b[i], c): return
           result = true
+        else:
+          result = false
+    else:
+      result = false
+  else:
+    result = false
 
 proc sameObjectStructures(a, b: PType, c: var TSameTypeClosure): bool =
-  # check base types:
-  if sonsLen(a) != sonsLen(b): return
-  for i in countup(0, sonsLen(a) - 1):
-    if not sameTypeOrNilAux(a.sons[i], b.sons[i], c): return
-  if not sameObjectTree(a.n, b.n, c): return
+  if not sameTypeOrNilAux(a.baseClass, b.baseClass, c): return false
+  if not sameObjectTree(a.n, b.n, c): return false
   result = true
 
 proc sameChildrenAux(a, b: PType, c: var TSameTypeClosure): bool =
-  if sonsLen(a) != sonsLen(b): return false
+  if not sameTupleLengths(a, b): return false
+  # XXX This is not tuple specific.
   result = true
-  for i in countup(0, sonsLen(a) - 1):
-    result = sameTypeOrNilAux(a.sons[i], b.sons[i], c)
+  for _, x, y in tupleTypePairs(a, b):
+    result = sameTypeOrNilAux(x, y, c)
     if not result: return
 
 proc isGenericAlias*(t: PType): bool =
-  return t.kind == tyGenericInst and t.lastSon.kind == tyGenericInst
+  return t.kind == tyGenericInst and t.skipModifier.kind == tyGenericInst
+
+proc genericAliasDepth*(t: PType): int =
+  result = 0
+  var it = t
+  while it.isGenericAlias:
+    it = it.skipModifier
+    inc result
 
 proc skipGenericAlias*(t: PType): PType =
-  return if t.isGenericAlias: t.lastSon else: t
+  return if t.isGenericAlias: t.skipModifier else: t
 
 proc sameFlags*(a, b: PType): bool {.inline.} =
   result = eqTypeFlags*a.flags == eqTypeFlags*b.flags
 
 proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
+  result = false
   template cycleCheck() =
     # believe it or not, the direct check for ``containsOrIncl(c, a, b)``
     # increases bootstrapping time from 2.4s to 3.3s on my laptop! So we cheat
@@ -906,65 +1228,103 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
       inc c.recCheck
     else:
       if containsOrIncl(c, a, b): return true
+  template maybeSkipRange(x: set[TTypeKind]): set[TTypeKind] =
+    if IgnoreRangeShallow in c.flags:
+      x + {tyRange}
+    else:
+      x
+  
+  template withoutShallowFlags(body) =
+    let oldFlags = c.flags
+    c.flags.excl IgnoreRangeShallow
+    body
+    c.flags = oldFlags
 
   if x == y: return true
-  var a = skipTypes(x, {tyGenericInst, tyAlias})
-  var b = skipTypes(y, {tyGenericInst, tyAlias})
+  let aliasSkipSet = maybeSkipRange({tyAlias})
+  var a = skipTypes(x, aliasSkipSet)
+  while a.kind == tyUserTypeClass and tfResolved in a.flags:
+    a = skipTypes(a.last, aliasSkipSet)
+  var b = skipTypes(y, aliasSkipSet)
+  while b.kind == tyUserTypeClass and tfResolved in b.flags:
+    b = skipTypes(b.last, aliasSkipSet)
   assert(a != nil)
   assert(b != nil)
-  if a.kind != b.kind:
-    case c.cmp
-    of dcEq: return false
-    of dcEqIgnoreDistinct:
-      while a.kind == tyDistinct: a = a.sons[0]
-      while b.kind == tyDistinct: b = b.sons[0]
-      if a.kind != b.kind: return false
-    of dcEqOrDistinctOf:
-      while a.kind == tyDistinct: a = a.sons[0]
-      if a.kind != b.kind: return false
-
+  case c.cmp
+  of dcEq:
+    if a.kind != b.kind: return false
+  of dcEqIgnoreDistinct:
+    let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst})
+    a = a.skipTypes(distinctSkipSet)
+    b = b.skipTypes(distinctSkipSet)
+    if a.kind != b.kind: return false
+  of dcEqOrDistinctOf:
+    let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst})
+    a = a.skipTypes(distinctSkipSet)
+    if a.kind != b.kind: return false
+
+  #[
+    The following code should not run in the case either side is an generic alias,
+    but it's not presently possible to distinguish the genericinsts from aliases of
+    objects ie `type A[T] = SomeObject`
+  ]#
   # this is required by tunique_type but makes no sense really:
-  if x.kind == tyGenericInst and IgnoreTupleFields notin c.flags:
+  if c.cmp == dcEq and x.kind == tyGenericInst and
+      IgnoreTupleFields notin c.flags and tyDistinct != y.kind:
     let
       lhs = x.skipGenericAlias
       rhs = y.skipGenericAlias
-    if rhs.kind != tyGenericInst or lhs.base != rhs.base:
+    if rhs.kind != tyGenericInst or lhs.base != rhs.base or rhs.kidsLen != lhs.kidsLen:
       return false
-    for i in 1 .. lhs.len - 2:
-      let ff = rhs.sons[i]
-      let aa = lhs.sons[i]
-      if not sameTypeAux(ff, aa, c): return false
+    withoutShallowFlags:
+      for ff, aa in underspecifiedPairs(rhs, lhs, 1, -1):
+        if not sameTypeAux(ff, aa, c): return false
     return true
 
   case a.kind
-  of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
-     tyInt..tyUInt64, tyStmt, tyExpr, tyVoid:
+  of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCstring,
+     tyInt..tyUInt64, tyTyped, tyUntyped, tyVoid:
     result = sameFlags(a, b)
+    if result and {PickyCAliases, ExactTypeDescValues} <= c.flags:
+      # additional requirement for the caching of generics for importc'ed types:
+      # the symbols must be identical too:
+      let symFlagsA = if a.sym != nil: a.sym.flags else: {}
+      let symFlagsB = if b.sym != nil: b.sym.flags else: {}
+      if (symFlagsA+symFlagsB) * {sfImportc, sfExportc} != {}:
+        result = symFlagsA == symFlagsB
+    elif result and PickyBackendAliases in c.flags:
+      let symFlagsA = if a.sym != nil: a.sym.flags else: {}
+      let symFlagsB = if b.sym != nil: b.sym.flags else: {}
+      if (symFlagsA+symFlagsB) * {sfImportc, sfExportc} != {}:
+        result = a.id == b.id
+
   of tyStatic, tyFromExpr:
     result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b)
-    if result and a.len == b.len and a.len == 1:
+    if result and sameTupleLengths(a, b) and a.hasElementType:
       cycleCheck()
-      result = sameTypeAux(a.sons[0], b.sons[0], c)
+      result = sameTypeAux(a.skipModifier, b.skipModifier, c)
   of tyObject:
-    ifFastObjectTypeCheckFailed(a, b):
-      cycleCheck()
-      result = sameObjectStructures(a, b, c) and sameFlags(a, b)
+    withoutShallowFlags:
+      ifFastObjectTypeCheckFailed(a, b):
+        cycleCheck()
+        result = sameObjectStructures(a, b, c) and sameFlags(a, b)
   of tyDistinct:
     cycleCheck()
     if c.cmp == dcEq:
       if sameFlags(a, b):
         ifFastObjectTypeCheckFailed(a, b):
-          result = sameTypeAux(a.sons[0], b.sons[0], c)
+          result = sameTypeAux(a.elementType, b.elementType, c)
     else:
-      result = sameTypeAux(a.sons[0], b.sons[0], c) and sameFlags(a, b)
+      result = sameTypeAux(a.elementType, b.elementType, c) and sameFlags(a, b)
   of tyEnum, tyForward:
     # XXX generic enums do not make much sense, but require structural checking
     result = a.id == b.id and sameFlags(a, b)
   of tyError:
     result = b.kind == tyError
   of tyTuple:
-    cycleCheck()
-    result = sameTuple(a, b, c) and sameFlags(a, b)
+    withoutShallowFlags:
+      cycleCheck()
+      result = sameTuple(a, b, c) and sameFlags(a, b)
   of tyTypeDesc:
     if c.cmp == dcEqIgnoreDistinct: result = false
     elif ExactTypeDescValues in c.flags:
@@ -976,15 +1336,23 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
     result = sameChildrenAux(a, b, c) and sameFlags(a, b)
     if result and {ExactGenericParams, ExactTypeDescValues} * c.flags != {}:
       result = a.sym.position == b.sym.position
-  of tyGenericInvocation, tyGenericBody, tySequence,
-     tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyLent, tySink,
-     tyArray, tyProc, tyVarargs, tyOrdinal, tyTypeClasses, tyOpt:
+  of tyBuiltInTypeClass:
+    result = a.elementType.kind == b.elementType.kind and sameFlags(a.elementType, b.elementType)
+    if result and a.elementType.kind == tyProc and IgnoreCC notin c.flags:
+      let ecc = a.elementType.flags * {tfExplicitCallConv}
+      result = ecc == b.elementType.flags * {tfExplicitCallConv} and
+               (ecc == {} or a.elementType.callConv == b.elementType.callConv)
+  of tyGenericInvocation, tyGenericBody, tySequence, tyOpenArray, tySet, tyRef,
+     tyPtr, tyVar, tyLent, tySink, tyUncheckedArray, tyArray, tyProc, tyVarargs,
+     tyOrdinal, tyCompositeTypeClass, tyUserTypeClass, tyUserTypeClassInst,
+     tyAnd, tyOr, tyNot, tyAnything, tyOwned:
     cycleCheck()
     if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n
-    result = sameChildrenAux(a, b, c)
-    if result:
+    withoutShallowFlags:
+      result = sameChildrenAux(a, b, c)
+    if result and IgnoreFlags notin c.flags:
       if IgnoreTupleFields in c.flags:
-        result = a.flags * {tfVarIsPtr} == b.flags * {tfVarIsPtr}
+        result = a.flags * {tfVarIsPtr, tfIsOutParam} == b.flags * {tfVarIsPtr, tfIsOutParam}
       else:
         result = sameFlags(a, b)
     if result and ExactGcSafety in c.flags:
@@ -994,14 +1362,25 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
                ((ExactConstraints notin c.flags) or sameConstraints(a.n, b.n))
   of tyRange:
     cycleCheck()
-    result = sameTypeOrNilAux(a.sons[0], b.sons[0], c) and
-        sameValue(a.n.sons[0], b.n.sons[0]) and
-        sameValue(a.n.sons[1], b.n.sons[1])
-  of tyGenericInst, tyAlias, tyInferred:
+    result = sameTypeOrNilAux(a.elementType, b.elementType, c)
+    if result and IgnoreRangeShallow notin c.flags:
+      result = sameValue(a.n[0], b.n[0]) and
+        sameValue(a.n[1], b.n[1])
+  of tyAlias, tyInferred, tyIterable:
     cycleCheck()
-    result = sameTypeAux(a.lastSon, b.lastSon, c)
+    result = sameTypeAux(a.skipModifier, b.skipModifier, c)
+  of tyGenericInst:
+    # BUG #23445
+    # The type system must distinguish between `T[int] = object #[empty]#`
+    # and `T[float] = object #[empty]#`!
+    cycleCheck()
+    withoutShallowFlags:
+      for ff, aa in underspecifiedPairs(a, b, 1, -1):
+        if not sameTypeAux(ff, aa, c): return false
+    result = sameTypeAux(a.skipModifier, b.skipModifier, c)
   of tyNone: result = false
-  of tyUnused, tyOptAsRef: result = false
+  of tyConcept:
+    result = exprStructuralEquivalent(a.n, b.n)
 
 proc sameBackendType*(x, y: PType): bool =
   var c = initSameTypeClosure()
@@ -1009,6 +1388,19 @@ proc sameBackendType*(x, y: PType): bool =
   c.cmp = dcEqIgnoreDistinct
   result = sameTypeAux(x, y, c)
 
+proc sameBackendTypeIgnoreRange*(x, y: PType): bool =
+  var c = initSameTypeClosure()
+  c.flags.incl IgnoreTupleFields
+  c.flags.incl IgnoreRangeShallow
+  c.cmp = dcEqIgnoreDistinct
+  result = sameTypeAux(x, y, c)
+
+proc sameBackendTypePickyAliases*(x, y: PType): bool =
+  var c = initSameTypeClosure()
+  c.flags.incl {IgnoreTupleFields, PickyCAliases, PickyBackendAliases}
+  c.cmp = dcEqIgnoreDistinct
+  result = sameTypeAux(x, y, c)
+
 proc compareTypes*(x, y: PType,
                    cmp: TDistinctCompare = dcEq,
                    flags: TTypeCmpFlags = {}): bool =
@@ -1026,25 +1418,26 @@ proc inheritanceDiff*(a, b: PType): int =
   # | returns: +x iff `a` is the x'th direct subclass of `b`
   # | returns: `maxint` iff `a` and `b` are not compatible at all
   if a == b or a.kind == tyError or b.kind == tyError: return 0
-  assert a.kind == tyObject
-  assert b.kind == tyObject
+  assert a.kind in {tyObject} + skipPtrs
+  assert b.kind in {tyObject} + skipPtrs
   var x = a
   result = 0
   while x != nil:
     x = skipTypes(x, skipPtrs)
     if sameObjectTypes(x, b): return
-    x = x.sons[0]
+    x = x.baseClass
     dec(result)
   var y = b
   result = 0
   while y != nil:
     y = skipTypes(y, skipPtrs)
     if sameObjectTypes(y, a): return
-    y = y.sons[0]
+    y = y.baseClass
     inc(result)
   result = high(int)
 
 proc commonSuperclass*(a, b: PType): PType =
+  result = nil
   # quick check: are they the same?
   if sameObjectTypes(a, b): return a
 
@@ -1057,7 +1450,7 @@ proc commonSuperclass*(a, b: PType): PType =
   while x != nil:
     x = skipTypes(x, skipPtrs)
     ancestors.incl(x.id)
-    x = x.sons[0]
+    x = x.baseClass
   var y = b
   while y != nil:
     var t = y # bug #7818, save type before skip
@@ -1066,326 +1459,29 @@ proc commonSuperclass*(a, b: PType): PType =
       # bug #7818, defer the previous skipTypes
       if t.kind != tyGenericInst: t = y
       return t
-    y = y.sons[0]
-
-type
-  TTypeAllowedFlag* = enum
-    taField,
-    taHeap,
-    taConcept
-
-  TTypeAllowedFlags* = set[TTypeAllowedFlag]
+    y = y.baseClass
 
-proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
-                    flags: TTypeAllowedFlags = {}): PType
+proc lacksMTypeField*(typ: PType): bool {.inline.} =
+  (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags
 
-proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind,
-                     flags: TTypeAllowedFlags = {}): PType =
-  if n != nil:
-    result = typeAllowedAux(marker, n.typ, kind, flags)
-    #if not result: debug(n.typ)
-    if result == nil:
-      case n.kind
-      of nkNone..nkNilLit:
-        discard
-      else:
-        if n.kind == nkRecCase and kind in {skProc, skFunc, skConst}:
-          return n[0].typ
-        for i in countup(0, sonsLen(n) - 1):
-          let it = n.sons[i]
-          result = typeAllowedNode(marker, it, kind, flags)
-          if result != nil: break
-
-proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
-                last: TTypeKind): bool =
-  var a = a
-  for k, i in pattern.items:
-    if a.kind != k: return false
-    if i >= a.sonsLen or a.sons[i] == nil: return false
-    a = a.sons[i]
-  result = a.kind == last
-
-proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
-                    flags: TTypeAllowedFlags = {}): PType =
-  assert(kind in {skVar, skLet, skConst, skProc, skFunc, skParam, skResult})
-  # if we have already checked the type, return true, because we stop the
-  # evaluation if something is wrong:
-  result = nil
-  if typ == nil: return
-  if containsOrIncl(marker, typ.id): return
-  var t = skipTypes(typ, abstractInst-{tyTypeDesc})
-  case t.kind
-  of tyVar, tyLent:
-    if kind in {skProc, skFunc, skConst}: return t
-    var t2 = skipTypes(t.sons[0], abstractInst-{tyTypeDesc})
-    case t2.kind
-    of tyVar, tyLent:
-      if taHeap notin flags: result = t2 # ``var var`` is illegal on the heap
-    of tyOpenArray:
-      if kind != skParam: result = t
-      else: result = typeAllowedAux(marker, t2, kind, flags)
-    else:
-      if kind notin {skParam, skResult}: result = t
-      else: result = typeAllowedAux(marker, t2, kind, flags)
-  of tyProc:
-    if kind == skConst and t.callConv == ccClosure: return t
-    for i in countup(1, sonsLen(t) - 1):
-      result = typeAllowedAux(marker, t.sons[i], skParam, flags)
-      if result != nil: break
-    if result.isNil and t.sons[0] != nil:
-      result = typeAllowedAux(marker, t.sons[0], skResult, flags)
-  of tyTypeDesc:
-    # XXX: This is still a horrible idea...
-    result = nil
-  of tyExpr, tyStmt, tyStatic:
-    if kind notin {skParam, skResult}: result = t
-  of tyVoid:
-    if taField notin flags: result = t
-  of tyTypeClasses:
-    if tfGenericTypeParam in t.flags or taConcept in flags: #or taField notin flags:
-      discard
-    elif t.isResolvedUserTypeClass:
-      result = typeAllowedAux(marker, t.lastSon, kind, flags)
-    elif kind notin {skParam, skResult}:
-      result = t
-  of tyGenericBody, tyGenericParam, tyGenericInvocation,
-     tyNone, tyForward, tyFromExpr:
-    result = t
-  of tyNil:
-    if kind != skConst: result = t
-  of tyString, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, tyPointer:
-    result = nil
-  of tyOrdinal:
-    if kind != skParam: result = t
-  of tyGenericInst, tyDistinct, tyAlias, tyInferred:
-    result = typeAllowedAux(marker, lastSon(t), kind, flags)
-  of tyRange:
-    if skipTypes(t.sons[0], abstractInst-{tyTypeDesc}).kind notin
-        {tyChar, tyEnum, tyInt..tyFloat128, tyUInt8..tyUInt32}: result = t
-  of tyOpenArray, tyVarargs, tySink:
-    if kind != skParam: result = t
-    else: result = typeAllowedAux(marker, t.sons[0], skVar, flags)
-  of tySequence, tyOpt:
-    if t.sons[0].kind != tyEmpty:
-      result = typeAllowedAux(marker, t.sons[0], skVar, flags+{taHeap})
-    elif kind in {skVar, skLet}:
-      result = t.sons[0]
-  of tyArray:
-    if t.sons[1].kind != tyEmpty:
-      result = typeAllowedAux(marker, t.sons[1], skVar, flags)
-    elif kind in {skVar, skLet}:
-      result = t.sons[1]
-  of tyRef:
-    if kind == skConst: result = t
-    else: result = typeAllowedAux(marker, t.lastSon, skVar, flags+{taHeap})
-  of tyPtr:
-    result = typeAllowedAux(marker, t.lastSon, skVar, flags+{taHeap})
-  of tySet:
-    for i in countup(0, sonsLen(t) - 1):
-      result = typeAllowedAux(marker, t.sons[i], kind, flags)
-      if result != nil: break
-  of tyObject, tyTuple:
-    if kind in {skProc, skFunc, skConst} and
-        t.kind == tyObject and t.sons[0] != nil: return t
-    let flags = flags+{taField}
-    for i in countup(0, sonsLen(t) - 1):
-      result = typeAllowedAux(marker, t.sons[i], kind, flags)
-      if result != nil: break
-    if result.isNil and t.n != nil:
-      result = typeAllowedNode(marker, t.n, kind, flags)
-  of tyEmpty:
-    if kind in {skVar, skLet}: result = t
-  of tyProxy:
-    # for now same as error node; we say it's a valid type as it should
-    # prevent cascading errors:
-    result = nil
-  of tyUnused, tyOptAsRef: result = t
-
-proc typeAllowed*(t: PType, kind: TSymKind; flags: TTypeAllowedFlags = {}): PType =
-  # returns 'nil' on success and otherwise the part of the type that is
-  # wrong!
-  var marker = initIntSet()
-  result = typeAllowedAux(marker, t, kind, flags)
-
-proc align(address, alignment: BiggestInt): BiggestInt =
-  result = (address + (alignment - 1)) and not (alignment - 1)
-
-const
-  szNonConcreteType* = -3
-  szIllegalRecursion* = -2
-  szUnknownSize* = -1
-
-proc computeSizeAux(conf: ConfigRef; typ: PType, a: var BiggestInt): BiggestInt
-proc computeRecSizeAux(conf: ConfigRef; n: PNode, a, currOffset: var BiggestInt): BiggestInt =
-  var maxAlign, maxSize, b, res: BiggestInt
-  case n.kind
-  of nkRecCase:
-    assert(n.sons[0].kind == nkSym)
-    result = computeRecSizeAux(conf, n.sons[0], a, currOffset)
-    maxSize = 0
-    maxAlign = 1
-    for i in countup(1, sonsLen(n) - 1):
-      case n.sons[i].kind
-      of nkOfBranch, nkElse:
-        res = computeRecSizeAux(conf, lastSon(n.sons[i]), b, currOffset)
-        if res < 0: return res
-        maxSize = max(maxSize, res)
-        maxAlign = max(maxAlign, b)
-      else:
-        return szIllegalRecursion
-    currOffset = align(currOffset, maxAlign) + maxSize
-    result = align(result, maxAlign) + maxSize
-    a = maxAlign
-  of nkRecList:
-    result = 0
-    maxAlign = 1
-    for i in countup(0, sonsLen(n) - 1):
-      res = computeRecSizeAux(conf, n.sons[i], b, currOffset)
-      if res < 0: return res
-      currOffset = align(currOffset, b) + res
-      result = align(result, b) + res
-      if b > maxAlign: maxAlign = b
-    a = maxAlign
-  of nkSym:
-    result = computeSizeAux(conf, n.sym.typ, a)
-    n.sym.offset = int(currOffset)
-  else:
-    a = 1
-    result = szNonConcreteType
-
-proc computeSizeAux(conf: ConfigRef; typ: PType, a: var BiggestInt): BiggestInt =
-  var res, maxAlign, length, currOffset: BiggestInt
-  if typ.size == szIllegalRecursion:
-    # we are already computing the size of the type
-    # --> illegal recursion in type
-    return szIllegalRecursion
-  if typ.size >= 0:
-    # size already computed
-    result = typ.size
-    a = typ.align
-    return
-  typ.size = szIllegalRecursion # mark as being computed
-  case typ.kind
-  of tyInt, tyUInt:
-    result = conf.target.intSize
-    a = result
-  of tyInt8, tyUInt8, tyBool, tyChar:
-    result = 1
-    a = result
-  of tyInt16, tyUInt16:
-    result = 2
-    a = result
-  of tyInt32, tyUInt32, tyFloat32:
-    result = 4
-    a = result
-  of tyInt64, tyUInt64, tyFloat64:
-    result = 8
-    a = result
-  of tyFloat128:
-    result = 16
-    a = result
-  of tyFloat:
-    result = conf.target.floatSize
-    a = result
-  of tyProc:
-    if typ.callConv == ccClosure: result = 2 * conf.target.ptrSize
-    else: result = conf.target.ptrSize
-    a = conf.target.ptrSize
-  of tyString, tyNil:
-    result = conf.target.ptrSize
-    a = result
-  of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray:
-    let base = typ.lastSon
-    if base == typ or (base.kind == tyTuple and base.size==szIllegalRecursion):
-      result = szIllegalRecursion
-    else: result = conf.target.ptrSize
-    a = result
-  of tyArray:
-    let elemSize = computeSizeAux(conf, typ.sons[1], a)
-    if elemSize < 0: return elemSize
-    result = lengthOrd(conf, typ.sons[0]) * elemSize
-  of tyEnum:
-    if firstOrd(conf, typ) < 0:
-      result = 4              # use signed int32
-    else:
-      length = lastOrd(conf, typ)   # BUGFIX: use lastOrd!
-      if length + 1 < `shl`(1, 8): result = 1
-      elif length + 1 < `shl`(1, 16): result = 2
-      elif length + 1 < `shl`(BiggestInt(1), 32): result = 4
-      else: result = 8
-    a = result
-  of tySet:
-    if typ.sons[0].kind == tyGenericParam:
-      result = szUnknownSize
-    else:
-      length = lengthOrd(conf, typ.sons[0])
-      if length <= 8: result = 1
-      elif length <= 16: result = 2
-      elif length <= 32: result = 4
-      elif length <= 64: result = 8
-      elif align(length, 8) mod 8 == 0: result = align(length, 8) div 8
-      else: result = align(length, 8) div 8 + 1
-    a = result
-  of tyRange:
-    result = computeSizeAux(conf, typ.sons[0], a)
-  of tyTuple:
-    result = 0
-    maxAlign = 1
-    for i in countup(0, sonsLen(typ) - 1):
-      res = computeSizeAux(conf, typ.sons[i], a)
-      if res < 0: return res
-      maxAlign = max(maxAlign, a)
-      result = align(result, a) + res
-    result = align(result, maxAlign)
-    a = maxAlign
-  of tyObject:
-    if typ.sons[0] != nil:
-      result = computeSizeAux(conf, typ.sons[0].skipTypes(skipPtrs), a)
-      if result < 0: return
-      maxAlign = a
-    elif isObjectWithTypeFieldPredicate(typ):
-      result = conf.target.intSize
-      maxAlign = result
-    else:
-      result = 0
-      maxAlign = 1
-    currOffset = result
-    result = computeRecSizeAux(conf, typ.n, a, currOffset)
-    if result < 0: return
-    if a < maxAlign: a = maxAlign
-    result = align(result, a)
-  of tyInferred:
-    if typ.len > 1:
-      result = computeSizeAux(conf, typ.lastSon, a)
-  of tyGenericInst, tyDistinct, tyGenericBody, tyAlias:
-    result = computeSizeAux(conf, lastSon(typ), a)
-  of tyTypeClasses:
-    result = if typ.isResolvedUserTypeClass: computeSizeAux(conf, typ.lastSon, a)
-             else: szUnknownSize
-  of tyTypeDesc:
-    result = computeSizeAux(conf, typ.base, a)
-  of tyForward: return szIllegalRecursion
-  of tyStatic:
-    result = if typ.n != nil: computeSizeAux(conf, typ.lastSon, a)
-             else: szUnknownSize
-  else:
-    #internalError("computeSizeAux()")
-    result = szUnknownSize
-  typ.size = result
-  typ.align = int16(a)
+include sizealignoffsetimpl
 
 proc computeSize*(conf: ConfigRef; typ: PType): BiggestInt =
-  var a: BiggestInt = 1
-  result = computeSizeAux(conf, typ, a)
+  computeSizeAlign(conf, typ)
+  result = typ.size
 
 proc getReturnType*(s: PSym): PType =
   # Obtains the return type of a iterator/proc/macro/template
   assert s.kind in skProcKinds
-  result = s.typ.sons[0]
+  result = s.typ.returnType
+
+proc getAlign*(conf: ConfigRef; typ: PType): BiggestInt =
+  computeSizeAlign(conf, typ)
+  result = typ.align
 
 proc getSize*(conf: ConfigRef; typ: PType): BiggestInt =
-  result = computeSize(conf, typ)
-  if result < 0: internalError(conf, "getSize: " & $typ.kind)
+  computeSizeAlign(conf, typ)
+  result = typ.size
 
 proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
   case t.kind
@@ -1403,18 +1499,36 @@ proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
 proc containsGenericType*(t: PType): bool =
   result = iterOverType(t, containsGenericTypeIter, nil)
 
-proc baseOfDistinct*(t: PType): PType =
+proc containsUnresolvedTypeIter(t: PType, closure: RootRef): bool =
+  if tfUnresolved in t.flags: return true
+  case t.kind
+  of tyStatic:
+    return t.n == nil
+  of tyTypeDesc:
+    if t.base.kind == tyNone: return true
+    if containsUnresolvedTypeIter(t.base, closure): return true
+    return false
+  of tyGenericInvocation, tyGenericParam, tyFromExpr, tyAnything:
+    return true
+  else:
+    return false
+
+proc containsUnresolvedType*(t: PType): bool =
+  result = iterOverType(t, containsUnresolvedTypeIter, nil)
+
+proc baseOfDistinct*(t: PType; g: ModuleGraph; idgen: IdGenerator): PType =
   if t.kind == tyDistinct:
-    result = t.sons[0]
+    result = t.elementType
   else:
-    result = copyType(t, t.owner, false)
+    result = copyType(t, idgen, t.owner)
+    copyTypeProps(g, idgen.module, result, t)
     var parent: PType = nil
     var it = result
-    while it.kind in {tyPtr, tyRef}:
+    while it.kind in {tyPtr, tyRef, tyOwned}:
       parent = it
-      it = it.lastSon
+      it = it.elementType
     if it.kind == tyDistinct and parent != nil:
-      parent.sons[0] = it.sons[0]
+      parent[0] = it[0]
 
 proc safeInheritanceDiff*(a, b: PType): int =
   # same as inheritanceDiff but checks for tyError:
@@ -1433,6 +1547,27 @@ proc compatibleEffectsAux(se, re: PNode): bool =
       return false
   result = true
 
+proc isDefectException*(t: PType): bool
+proc compatibleExceptions(se, re: PNode): bool =
+  if re.isNil: return false
+  for r in items(re):
+    block search:
+      if isDefectException(r.typ):
+        break search
+      for s in items(se):
+        if safeInheritanceDiff(r.typ, s.typ) <= 0:
+          break search
+      return false
+  result = true
+
+proc hasIncompatibleEffect(se, re: PNode): bool =
+  result = false
+  if re.isNil: return false
+  for r in items(re):
+    for s in items(se):
+      if safeInheritanceDiff(r.typ, s.typ) != high(int):
+        return true
+
 type
   EffectsCompat* = enum
     efCompat
@@ -1440,52 +1575,74 @@ type
     efRaisesUnknown
     efTagsDiffer
     efTagsUnknown
-    efLockLevelsDiffer
+    efEffectsDelayed
+    efTagsIllegal
 
 proc compatibleEffects*(formal, actual: PType): EffectsCompat =
   # for proc type compatibility checking:
   assert formal.kind == tyProc and actual.kind == tyProc
-  if formal.n.sons[0].kind != nkEffectList or
-     actual.n.sons[0].kind != nkEffectList:
+  #if tfEffectSystemWorkaround in actual.flags:
+  #  return efCompat
+
+  if formal.n[0].kind != nkEffectList or
+     actual.n[0].kind != nkEffectList:
     return efTagsUnknown
 
-  var spec = formal.n.sons[0]
+  var spec = formal.n[0]
   if spec.len != 0:
-    var real = actual.n.sons[0]
+    var real = actual.n[0]
 
-    let se = spec.sons[exceptionEffects]
+    let se = spec[exceptionEffects]
     # if 'se.kind == nkArgList' it is no formal type really, but a
     # computed effect and as such no spec:
     # 'r.msgHandler = if isNil(msgHandler): defaultMsgHandler else: msgHandler'
     if not isNil(se) and se.kind != nkArgList:
       # spec requires some exception or tag, but we don't know anything:
       if real.len == 0: return efRaisesUnknown
-      let res = compatibleEffectsAux(se, real.sons[exceptionEffects])
+      let res = compatibleExceptions(se, real[exceptionEffects])
       if not res: return efRaisesDiffer
 
-    let st = spec.sons[tagEffects]
+    let st = spec[tagEffects]
     if not isNil(st) and st.kind != nkArgList:
       # spec requires some exception or tag, but we don't know anything:
       if real.len == 0: return efTagsUnknown
-      let res = compatibleEffectsAux(st, real.sons[tagEffects])
-      if not res: return efTagsDiffer
-  if formal.lockLevel.ord < 0 or
-      actual.lockLevel.ord <= formal.lockLevel.ord:
-    result = efCompat
-  else:
-    result = efLockLevelsDiffer
+      let res = compatibleEffectsAux(st, real[tagEffects])
+      if not res:
+        #if tfEffectSystemWorkaround notin actual.flags:
+        return efTagsDiffer
+
+    let sn = spec[forbiddenEffects]
+    if not isNil(sn) and sn.kind != nkArgList:
+      if 0 == real.len:
+        return efTagsUnknown
+      elif hasIncompatibleEffect(sn, real[tagEffects]):
+        return efTagsIllegal
+
+  for i in 1 ..< min(formal.n.len, actual.n.len):
+    if formal.n[i].sym.flags * {sfEffectsDelayed} != actual.n[i].sym.flags * {sfEffectsDelayed}:
+      result = efEffectsDelayed
+      break
+
+  result = efCompat
+
 
 proc isCompileTimeOnly*(t: PType): bool {.inline.} =
-  result = t.kind in {tyTypeDesc, tyStatic}
+  result = t.kind in {tyTypeDesc, tyStatic, tyGenericParam}
 
 proc containsCompileTimeOnly*(t: PType): bool =
   if isCompileTimeOnly(t): return true
-  if t.sons != nil:
-    for i in 0 ..< t.sonsLen:
-      if t.sons[i] != nil and isCompileTimeOnly(t.sons[i]):
-        return true
+  for a in t.kids:
+    if a != nil and isCompileTimeOnly(a):
+      return true
   return false
 
+proc safeSkipTypes*(t: PType, kinds: TTypeKinds): PType =
+  ## same as 'skipTypes' but with a simple cycle detector.
+  result = t
+  var seen = initIntSet()
+  while result.kind in kinds and not containsOrIncl(seen, result.id):
+    result = skipModifier(result)
+
 type
   OrdinalType* = enum
     NoneLike, IntLike, FloatLike
@@ -1507,11 +1664,11 @@ proc skipConv*(n: PNode): PNode =
   of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
     # only skip the conversion if it doesn't lose too important information
     # (see bug #1334)
-    if n.sons[0].typ.classify == n.typ.classify:
-      result = n.sons[0]
+    if n[0].typ.classify == n.typ.classify:
+      result = n[0]
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
-    if n.sons[1].typ.classify == n.typ.classify:
-      result = n.sons[1]
+    if n[1].typ.classify == n.typ.classify:
+      result = n[1]
   else: discard
 
 proc skipHidden*(n: PNode): PNode =
@@ -1519,11 +1676,11 @@ proc skipHidden*(n: PNode): PNode =
   while true:
     case result.kind
     of nkHiddenStdConv, nkHiddenSubConv:
-      if result.sons[1].typ.classify == result.typ.classify:
-        result = result.sons[1]
+      if result[1].typ.classify == result.typ.classify:
+        result = result[1]
       else: break
     of nkHiddenDeref, nkHiddenAddr:
-      result = result.sons[0]
+      result = result[0]
     else: break
 
 proc skipConvTakeType*(n: PNode): PNode =
@@ -1532,14 +1689,13 @@ proc skipConvTakeType*(n: PNode): PNode =
 
 proc isEmptyContainer*(t: PType): bool =
   case t.kind
-  of tyExpr, tyNil: result = true
-  of tyArray: result = t.sons[1].kind == tyEmpty
-  of tySet, tySequence, tyOpenArray, tyVarargs:
-    result = t.sons[0].kind == tyEmpty
-  of tyGenericInst, tyAlias: result = isEmptyContainer(t.lastSon)
+  of tyUntyped, tyNil: result = true
+  of tyArray, tySet, tySequence, tyOpenArray, tyVarargs:
+    result = t.elementType.kind == tyEmpty
+  of tyGenericInst, tyAlias, tySink: result = isEmptyContainer(t.skipModifier)
   else: result = false
 
-proc takeType*(formal, arg: PType): PType =
+proc takeType*(formal, arg: PType; g: ModuleGraph; idgen: IdGenerator): PType =
   # param: openArray[string] = []
   # [] is an array constructor of length 0 of type string!
   if arg.kind == tyNil:
@@ -1547,23 +1703,24 @@ proc takeType*(formal, arg: PType): PType =
     result = formal
   elif formal.kind in {tyOpenArray, tyVarargs, tySequence} and
       arg.isEmptyContainer:
-    let a = copyType(arg.skipTypes({tyGenericInst, tyAlias}), arg.owner, keepId=false)
-    a.sons[ord(arg.kind == tyArray)] = formal.sons[0]
+    let a = copyType(arg.skipTypes({tyGenericInst, tyAlias}), idgen, arg.owner)
+    copyTypeProps(g, idgen.module, a, arg)
+    a[ord(arg.kind == tyArray)] = formal[0]
     result = a
   elif formal.kind in {tyTuple, tySet} and arg.kind == formal.kind:
     result = formal
   else:
     result = arg
 
-proc skipHiddenSubConv*(n: PNode): PNode =
+proc skipHiddenSubConv*(n: PNode; g: ModuleGraph; idgen: IdGenerator): PNode =
   if n.kind == nkHiddenSubConv:
     # param: openArray[string] = []
     # [] is an array constructor of length 0 of type string!
     let formal = n.typ
-    result = n.sons[1]
+    result = n[1]
     let arg = result.typ
-    let dest = takeType(formal, arg)
-    if dest == arg and formal.kind != tyExpr:
+    let dest = takeType(formal, arg, g, idgen)
+    if dest == arg and formal.kind != tyUntyped:
       #echo n.info, " came here for ", formal.typeToString
       result = n
     else:
@@ -1572,26 +1729,209 @@ proc skipHiddenSubConv*(n: PNode): PNode =
   else:
     result = n
 
-proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType) =
+proc getProcConvMismatch*(c: ConfigRef, f, a: PType, rel = isNone): (set[ProcConvMismatch], TTypeRelation) =
+  ## Returns a set of the reason of mismatch, and the relation for conversion.
+  result[1] = rel
+  if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
+    # Formal is pure, but actual is not
+    result[0].incl pcmNoSideEffect
+    result[1] = isNone
+
+  if tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {} and
+    optThreadAnalysis in c.globalOptions:
+    # noSideEffect implies ``tfThread``!
+    result[0].incl pcmNotGcSafe
+    result[1] = isNone
+
+  if f.flags * {tfIterator} != a.flags * {tfIterator}:
+    # One of them is an iterator so not convertible
+    result[0].incl pcmNotIterator
+    result[1] = isNone
+
+  if f.callConv != a.callConv:
+    # valid to pass a 'nimcall' thingie to 'closure':
+    if f.callConv == ccClosure and a.callConv == ccNimCall:
+      case result[1]
+      of isInferred: result[1] = isInferredConvertible
+      of isBothMetaConvertible: result[1] = isBothMetaConvertible
+      elif result[1] != isNone: result[1] = isConvertible
+      else: result[0].incl pcmDifferentCallConv
+    else:
+      result[1] = isNone
+      result[0].incl pcmDifferentCallConv
+
+proc addPragmaAndCallConvMismatch*(message: var string, formal, actual: PType, conf: ConfigRef) =
+  assert formal.kind == tyProc and actual.kind == tyProc
+  let (convMismatch, _) = getProcConvMismatch(conf, formal, actual)
+  var
+    gotPragmas = ""
+    expectedPragmas = ""
+  for reason in convMismatch:
+    case reason
+    of pcmDifferentCallConv:
+      message.add "\n  Calling convention mismatch: got '{.$1.}', but expected '{.$2.}'." % [$actual.callConv, $formal.callConv]
+    of pcmNoSideEffect:
+      expectedPragmas.add "noSideEffect, "
+    of pcmNotGcSafe:
+      expectedPragmas.add "gcsafe, "
+    of pcmNotIterator: discard
+
+  if expectedPragmas.len > 0:
+    gotPragmas.setLen(max(0, gotPragmas.len - 2)) # Remove ", "
+    expectedPragmas.setLen(max(0, expectedPragmas.len - 2)) # Remove ", "
+    message.add "\n  Pragma mismatch: got '{.$1.}', but expected '{.$2.}'." % [gotPragmas, expectedPragmas]
+
+proc processPragmaAndCallConvMismatch(msg: var string, formal, actual: PType, conf: ConfigRef) =
+  if formal.kind == tyProc and actual.kind == tyProc:
+    msg.addPragmaAndCallConvMismatch(formal, actual, conf)
+    case compatibleEffects(formal, actual)
+    of efCompat: discard
+    of efRaisesDiffer:
+      msg.add "\n.raise effects differ"
+    of efRaisesUnknown:
+      msg.add "\n.raise effect is 'can raise any'"
+    of efTagsDiffer:
+      msg.add "\n.tag effects differ"
+    of efTagsUnknown:
+      msg.add "\n.tag effect is 'any tag allowed'"
+    of efEffectsDelayed:
+      msg.add "\n.effectsOf annotations differ"
+    of efTagsIllegal:
+      msg.add "\n.notTag catched an illegal effect"
+
+proc typeNameAndDesc*(t: PType): string =
+  result = typeToString(t)
+  let desc = typeToString(t, preferDesc)
+  if result != desc:
+    result.add(" = ")
+    result.add(desc)
+
+proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType, n: PNode) =
   if formal.kind != tyError and actual.kind != tyError:
-    let named = typeToString(formal)
+    let actualStr = typeToString(actual)
+    let formalStr = typeToString(formal)
     let desc = typeToString(formal, preferDesc)
-    let x = if named == desc: named else: named & " = " & desc
-    var msg = "type mismatch: got <" &
-              typeToString(actual) & "> " &
-              "but expected '" & x & "'"
-
-    if formal.kind == tyProc and actual.kind == tyProc:
-      case compatibleEffects(formal, actual)
-      of efCompat: discard
-      of efRaisesDiffer:
-        msg.add "\n.raise effects differ"
-      of efRaisesUnknown:
-        msg.add "\n.raise effect is 'can raise any'"
-      of efTagsDiffer:
-        msg.add "\n.tag effects differ"
-      of efTagsUnknown:
-        msg.add "\n.tag effect is 'any tag allowed'"
-      of efLockLevelsDiffer:
-        msg.add "\nlock levels differ"
+    let x = if formalStr == desc: formalStr else: formalStr & " = " & desc
+    let verbose = actualStr == formalStr or optDeclaredLocs in conf.globalOptions
+    var msg = "type mismatch:"
+    if verbose: msg.add "\n"
+    if conf.isDefined("nimLegacyTypeMismatch"):
+      msg.add  " got <$1>" % actualStr
+    else:
+      msg.add  " got '$1' for '$2'" % [actualStr, n.renderTree]
+    if verbose:
+      msg.addDeclaredLoc(conf, actual)
+      msg.add "\n"
+    msg.add " but expected '$1'" % x
+    if verbose: msg.addDeclaredLoc(conf, formal)
+    var a = formal
+    var b = actual
+    if formal.kind == tyArray and actual.kind == tyArray:
+      a = formal[1]
+      b = actual[1]
+      processPragmaAndCallConvMismatch(msg, a, b, conf)
+    elif formal.kind == tySequence and actual.kind == tySequence:
+      a = formal[0]
+      b = actual[0]
+      processPragmaAndCallConvMismatch(msg, a, b, conf)
+    else:
+      processPragmaAndCallConvMismatch(msg, a, b, conf)
     localError(conf, info, msg)
+
+proc isTupleRecursive(t: PType, cycleDetector: var IntSet): bool =
+  if t == nil:
+    return false
+  if cycleDetector.containsOrIncl(t.id):
+    return true
+  case t.kind
+  of tyTuple:
+    result = false
+    var cycleDetectorCopy: IntSet
+    for a in t.kids:
+      cycleDetectorCopy = cycleDetector
+      if isTupleRecursive(a, cycleDetectorCopy):
+        return true
+  of tyRef, tyPtr, tyVar, tyLent, tySink,
+      tyArray, tyUncheckedArray, tySequence, tyDistinct:
+    return isTupleRecursive(t.elementType, cycleDetector)
+  of tyAlias, tyGenericInst:
+    return isTupleRecursive(t.skipModifier, cycleDetector)
+  else:
+    return false
+
+proc isTupleRecursive*(t: PType): bool =
+  var cycleDetector = initIntSet()
+  isTupleRecursive(t, cycleDetector)
+
+proc isException*(t: PType): bool =
+  # check if `y` is object type and it inherits from Exception
+  assert(t != nil)
+
+  var t = t.skipTypes(abstractInst)
+  while t.kind == tyObject:
+    if t.sym != nil and t.sym.magic == mException: return true
+    if t.baseClass == nil: break
+    t = skipTypes(t.baseClass, abstractPtrs)
+  return false
+
+proc isDefectException*(t: PType): bool =
+  var t = t.skipTypes(abstractPtrs)
+  while t.kind == tyObject:
+    if t.sym != nil and t.sym.owner != nil and
+        sfSystemModule in t.sym.owner.flags and
+        t.sym.name.s == "Defect":
+      return true
+    if t.baseClass == nil: break
+    t = skipTypes(t.baseClass, abstractPtrs)
+  return false
+
+proc isDefectOrCatchableError*(t: PType): bool =
+  var t = t.skipTypes(abstractPtrs)
+  while t.kind == tyObject:
+    if t.sym != nil and t.sym.owner != nil and
+        sfSystemModule in t.sym.owner.flags and
+        (t.sym.name.s == "Defect" or
+        t.sym.name.s == "CatchableError"):
+      return true
+    if t.baseClass == nil: break
+    t = skipTypes(t.baseClass, abstractPtrs)
+  return false
+
+proc isSinkTypeForParam*(t: PType): bool =
+  # a parameter like 'seq[owned T]' must not be used only once, but its
+  # elements must, so we detect this case here:
+  result = t.skipTypes({tyGenericInst, tyAlias}).kind in {tySink, tyOwned}
+  when false:
+    if isSinkType(t):
+      if t.skipTypes({tyGenericInst, tyAlias}).kind in {tyArray, tyVarargs, tyOpenArray, tySequence}:
+        result = false
+      else:
+        result = true
+
+proc lookupFieldAgain*(ty: PType; field: PSym): PSym =
+  result = nil
+  var ty = ty
+  while ty != nil:
+    ty = ty.skipTypes(skipPtrs)
+    assert(ty.kind in {tyTuple, tyObject})
+    result = lookupInRecord(ty.n, field.name)
+    if result != nil: break
+    ty = ty.baseClass
+  if result == nil: result = field
+
+proc isCharArrayPtr*(t: PType; allowPointerToChar: bool): bool =
+  let t = t.skipTypes(abstractInst)
+  if t.kind == tyPtr:
+    let pointsTo = t.elementType.skipTypes(abstractInst)
+    case pointsTo.kind
+    of tyUncheckedArray:
+      result = pointsTo.elementType.kind == tyChar
+    of tyArray:
+      result = pointsTo.elementType.kind == tyChar and firstOrd(nil, pointsTo.indexType) == 0 and
+        skipTypes(pointsTo.indexType, {tyRange}).kind in {tyInt..tyInt64}
+    of tyChar:
+      result = allowPointerToChar
+    else:
+      result = false
+  else:
+    result = false
diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim
index 4d75d5d05..72bcddb05 100644
--- a/compiler/typesrenderer.nim
+++ b/compiler/typesrenderer.nim
@@ -7,10 +7,21 @@
 #    distribution, for details about the copyright.
 #
 
-import renderer, strutils, ast, msgs, types, astalgo
+import renderer, ast, types
+import std/strutils
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 
 const defaultParamSeparator* = ","
 
+template mayNormalize(s: string): string =
+  if toNormalize:
+    s.nimIdentNormalize
+  else:
+    s
+
 proc renderPlainSymbolName*(n: PNode): string =
   ## Returns the first non '*' nkIdent node from the tree.
   ##
@@ -19,7 +30,7 @@ proc renderPlainSymbolName*(n: PNode): string =
   ## for the HTML hyperlinks.
   case n.kind
   of nkPostfix, nkAccQuoted:
-    result = renderPlainSymbolName(n[n.len-1])
+    result = renderPlainSymbolName(n[^1])
   of nkIdent:
     result = n.ident.s
   of nkSym:
@@ -29,86 +40,109 @@ proc renderPlainSymbolName*(n: PNode): string =
   else:
     result = ""
     #internalError(n.info, "renderPlainSymbolName() with " & $n.kind)
-  assert(not result.isNil)
 
-proc renderType(n: PNode): string =
+proc renderType(n: PNode, toNormalize: bool): string =
   ## Returns a string with the node type or the empty string.
+  ## This proc should be kept in sync with `toLangSymbols` from
+  ## ``lib/packages/docutils/dochelpers.nim``.
   case n.kind:
-  of nkIdent: result = n.ident.s
-  of nkSym: result = typeToString(n.sym.typ)
+  of nkIdent: result = mayNormalize(n.ident.s)
+  of nkSym: result = mayNormalize(typeToString(n.sym.typ))
   of nkVarTy:
     if n.len == 1:
-      result = renderType(n[0])
+      result = renderType(n[0], toNormalize)
     else:
       result = "var"
   of nkRefTy:
     if n.len == 1:
-      result = "ref." & renderType(n[0])
+      result = "ref." & renderType(n[0], toNormalize)
     else:
       result = "ref"
   of nkPtrTy:
     if n.len == 1:
-      result = "ptr." & renderType(n[0])
+      result = "ptr." & renderType(n[0], toNormalize)
     else:
       result = "ptr"
   of nkProcTy:
-    assert len(n) != 1
-    if len(n) > 1:
+    assert n.len != 1
+    if n.len > 1 and n[0].kind == nkFormalParams:
       let params = n[0]
-      assert params.kind == nkFormalParams
-      assert len(params) > 0
+      assert params.len > 0
       result = "proc("
-      for i in 1 ..< len(params): result.add(renderType(params[i]) & ',')
-      result[len(result)-1] = ')'
+      for i in 1..<params.len: result.add(renderType(params[i], toNormalize) & ',')
+      result[^1] = ')'
     else:
       result = "proc"
   of nkIdentDefs:
-    assert len(n) >= 3
-    let typePos = len(n) - 2
-    let typeStr = renderType(n[typePos])
+    assert n.len >= 3
+    let typePos = n.len - 2
+    let typeStr = renderType(n[typePos], toNormalize)
     result = typeStr
-    for i in 1 ..< typePos:
-      assert n[i].kind == nkIdent
+    for i in 1..<typePos:
+      assert n[i].kind in {nkSym, nkIdent}
       result.add(',' & typeStr)
   of nkTupleTy:
     result = "tuple["
-    for i in 0 ..< len(n): result.add(renderType(n[i]) & ',')
-    result[len(result)-1] = ']'
+    for i in 0..<n.len: result.add(renderType(n[i], toNormalize) & ',')
+    result[^1] = ']'
   of nkBracketExpr:
-    assert len(n) >= 2
-    result = renderType(n[0]) & '['
-    for i in 1 ..< len(n): result.add(renderType(n[i]) & ',')
-    result[len(result)-1] = ']'
+    assert n.len >= 2
+    result = renderType(n[0], toNormalize) & '['
+    for i in 1..<n.len: result.add(renderType(n[i], toNormalize) & ',')
+    result[^1] = ']'
+  of nkCommand:
+    result = renderType(n[0], toNormalize)
+    for i in 1..<n.len:
+      if i > 1: result.add ", "
+      result.add(renderType(n[i], toNormalize))
   else: result = ""
-  assert(not result.isNil)
 
 
-proc renderParamTypes(found: var seq[string], n: PNode) =
+proc renderParamNames*(n: PNode, toNormalize=false): seq[string] =
+  ## Returns parameter names of routine `n`.
+  result = @[]
+  doAssert n.kind == nkFormalParams
+  case n.kind
+  of nkFormalParams:
+    for i in 1..<n.len:
+      if n[i].kind == nkIdentDefs:
+        # These are parameter names + type + default value node.
+        let typePos = n[i].len - 2
+        for j in 0..<typePos:
+          result.add mayNormalize($n[i][j])
+      else:  # error
+        result.add($n[i])
+  else:  #error
+    result.add $n
+
+
+proc renderParamTypes*(found: var seq[string], n: PNode, toNormalize=false) =
   ## Recursive helper, adds to `found` any types, or keeps diving the AST.
   ##
   ## The normal `doc` generator doesn't include .typ information, so the
-  ## function won't render types for parameters with default values. The `doc2`
+  ## function won't render types for parameters with default values. The `doc`
   ## generator does include the information.
   case n.kind
   of nkFormalParams:
-    for i in 1 ..< len(n): renderParamTypes(found, n[i])
+    for i in 1..<n.len: renderParamTypes(found, n[i], toNormalize)
   of nkIdentDefs:
     # These are parameter names + type + default value node.
-    let typePos = len(n) - 2
+    let typePos = n.len - 2
     assert typePos > 0
-    var typeStr = renderType(n[typePos])
+    var typeStr = renderType(n[typePos], toNormalize)
     if typeStr.len < 1 and n[typePos+1].kind != nkEmpty:
       # Try with the last node, maybe its a default value.
       let typ = n[typePos+1].typ
       if not typ.isNil: typeStr = typeToString(typ, preferExported)
       if typeStr.len < 1: return
-    for i in 0 ..< typePos:
+    for i in 0..<typePos:
       found.add(typeStr)
   else:
     found.add($n)
     #internalError(n.info, "renderParamTypes(found,n) with " & $n.kind)
 
-proc renderParamTypes*(n: PNode, sep = defaultParamSeparator): string =
+proc renderParamTypes*(n: PNode, sep = defaultParamSeparator,
+                       toNormalize=false): string =
   ## Returns the types contained in `n` joined by `sep`.
   ##
   ## This proc expects to be passed as `n` the parameters of any callable. The
@@ -117,6 +151,10 @@ proc renderParamTypes*(n: PNode, sep = defaultParamSeparator): string =
   ## other characters may appear too, like ``[]`` or ``|``.
   result = ""
   var found: seq[string] = @[]
-  renderParamTypes(found, n)
+  renderParamTypes(found, n, toNormalize)
   if found.len > 0:
     result = found.join(sep)
+
+proc renderOutType*(n: PNode, toNormalize=false): string =
+  assert n.kind == nkFormalParams
+  result = renderType(n[0], toNormalize)
diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim
new file mode 100644
index 000000000..1711fea46
--- /dev/null
+++ b/compiler/varpartitions.nim
@@ -0,0 +1,1019 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Partition variables into different graphs. Used for
+## Nim's write tracking, borrow checking and also for the
+## cursor inference.
+## The algorithm is a reinvention / variation of Steensgaard's
+## algorithm.
+## The used data structure is "union find" with path compression.
+
+## We perform two passes over the AST:
+## - Pass one (``computeLiveRanges``): collect livetimes of local
+##   variables and whether they are potentially re-assigned.
+## - Pass two (``traverse``): combine local variables to abstract "graphs".
+##   Strict func checking: Ensure that graphs that are connected to
+##   const parameters are not mutated.
+##   Cursor inference: Ensure that potential cursors are not
+##     borrowed from locations that are connected to a graph
+##     that is mutated during the liveness of the cursor.
+##     (We track all possible mutations of a graph.)
+##
+## See https://nim-lang.github.io/Nim/manual_experimental.html#view-types-algorithm
+## for a high-level description of how borrow checking works.
+
+import ast, types, lineinfos, options, msgs, renderer, typeallowed, modulegraphs
+from trees import getMagic, isNoSideEffectPragma, stupidStmtListExpr
+from isolation_check import canAlias
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+type
+  AbstractTime = distinct int
+
+const
+  MaxTime = AbstractTime high(int)
+  MinTime = AbstractTime(-1)
+
+proc `<=`(a, b: AbstractTime): bool {.borrow.}
+proc `<`(a, b: AbstractTime): bool {.borrow.}
+
+proc inc(x: var AbstractTime; diff = 1) {.borrow.}
+proc dec(x: var AbstractTime; diff = 1) {.borrow.}
+
+proc `$`(x: AbstractTime): string {.borrow.}
+
+type
+  SubgraphFlag = enum
+    isMutated, # graph might be mutated
+    isMutatedDirectly, # graph is mutated directly by a non-var parameter.
+    isMutatedByVarParam, # graph is mutated by a var parameter.
+    connectsConstParam # graph is connected to a non-var parameter.
+
+  VarFlag = enum
+    ownsData,
+    preventCursor,
+    isReassigned,
+    isConditionallyReassigned,
+    viewDoesMutate,
+    viewBorrowsFromConst
+
+  VarIndexKind = enum
+    isEmptyRoot,
+    dependsOn,
+    isRootOf
+
+  Connection = object
+    case kind: VarIndexKind
+    of isEmptyRoot: discard
+    of dependsOn: parent: int
+    of isRootOf: graphIndex: int
+
+  VarIndex = object
+    con: Connection
+    flags: set[VarFlag]
+    sym: PSym
+    reassignedTo: int
+    aliveStart, aliveEnd: AbstractTime # the range for which the variable is alive.
+    borrowsFrom: seq[int] # indexes into Partitions.s
+
+  MutationInfo* = object
+    param: PSym
+    mutatedHere, connectedVia: TLineInfo
+    flags: set[SubgraphFlag]
+    maxMutation, minConnection: AbstractTime
+    mutations: seq[AbstractTime]
+
+  Goal* = enum
+    constParameters,
+    borrowChecking,
+    cursorInference
+
+  Partitions* = object
+    abstractTime: AbstractTime
+    defers: seq[PNode]
+    processDefer: bool
+    s: seq[VarIndex]
+    graphs: seq[MutationInfo]
+    goals: set[Goal]
+    unanalysableMutation: bool
+    inAsgnSource, inConstructor, inNoSideEffectSection: int
+    inConditional, inLoop: int
+    inConvHasDestructor: int
+    owner: PSym
+    g: ModuleGraph
+
+proc mutationAfterConnection(g: MutationInfo): bool {.inline.} =
+  #echo g.maxMutation.int, " ", g.minConnection.int, " ", g.param
+  g.maxMutation > g.minConnection
+
+proc `$`*(config: ConfigRef; g: MutationInfo): string =
+  result = ""
+  if g.flags * {isMutated, connectsConstParam} == {isMutated, connectsConstParam}:
+    result.add "\nan object reachable from '"
+    result.add g.param.name.s
+    result.add "' is potentially mutated"
+    if g.mutatedHere != unknownLineInfo:
+      result.add "\n"
+      result.add config $ g.mutatedHere
+      result.add " the mutation is here"
+    if g.connectedVia != unknownLineInfo:
+      result.add "\n"
+      result.add config $ g.connectedVia
+      result.add " is the statement that connected the mutation to the parameter"
+
+proc hasSideEffect*(c: var Partitions; info: var MutationInfo): bool =
+  for g in mitems c.graphs:
+    if g.flags * {isMutated, connectsConstParam} == {isMutated, connectsConstParam} and
+        (mutationAfterConnection(g) or isMutatedDirectly in g.flags):
+      info = g
+      return true
+  return false
+
+template isConstParam(a): bool = a.kind == skParam and a.typ.kind notin {tyVar, tySink}
+
+proc variableId(c: Partitions; x: PSym): int =
+  for i in 0 ..< c.s.len:
+    if c.s[i].sym == x: return i
+  return -1
+
+proc registerResult(c: var Partitions; n: PNode) =
+  if n.kind == nkSym:
+    c.s.add VarIndex(con: Connection(kind: isEmptyRoot), sym: n.sym, reassignedTo: 0,
+                      aliveStart: MaxTime, aliveEnd: c.abstractTime)
+
+proc registerParam(c: var Partitions; n: PNode) =
+  assert n.kind == nkSym
+  if isConstParam(n.sym):
+    c.s.add VarIndex(con: Connection(kind: isRootOf, graphIndex: c.graphs.len),
+                      sym: n.sym, reassignedTo: 0,
+                      aliveStart: c.abstractTime, aliveEnd: c.abstractTime)
+    c.graphs.add MutationInfo(param: n.sym, mutatedHere: unknownLineInfo,
+                          connectedVia: unknownLineInfo, flags: {connectsConstParam},
+                          maxMutation: MinTime, minConnection: MaxTime,
+                          mutations: @[])
+  else:
+    c.s.add VarIndex(con: Connection(kind: isEmptyRoot), sym: n.sym, reassignedTo: 0,
+                     aliveStart: c.abstractTime, aliveEnd: c.abstractTime)
+
+proc registerVariable(c: var Partitions; n: PNode) =
+  if n.kind == nkSym and variableId(c, n.sym) < 0:
+    c.s.add VarIndex(con: Connection(kind: isEmptyRoot), sym: n.sym, reassignedTo: 0,
+                     aliveStart: c.abstractTime, aliveEnd: c.abstractTime)
+
+proc root(v: var Partitions; start: int): int =
+  result = start
+  var depth = 0
+  while v.s[result].con.kind == dependsOn:
+    result = v.s[result].con.parent
+    inc depth
+  if depth > 0:
+    # path compression:
+    var it = start
+    while v.s[it].con.kind == dependsOn:
+      let next = v.s[it].con.parent
+      v.s[it].con = Connection(kind: dependsOn, parent: result)
+      it = next
+
+proc potentialMutation(v: var Partitions; s: PSym; level: int; info: TLineInfo) =
+  let id = variableId(v, s)
+  if id >= 0:
+    let r = root(v, id)
+    let flags = if s.kind == skParam:
+                  if isConstParam(s):
+                    {isMutated, isMutatedDirectly}
+                  elif s.typ.kind == tyVar and level <= 1:
+                    # varParam[i] = v is different from varParam[i][] = v
+                    {isMutatedByVarParam}
+                  else:
+                    {isMutated}
+                else:
+                  {isMutated}
+
+    case v.s[r].con.kind
+    of isEmptyRoot:
+      v.s[r].con = Connection(kind: isRootOf, graphIndex: v.graphs.len)
+      v.graphs.add MutationInfo(param: if isConstParam(s): s else: nil, mutatedHere: info,
+                            connectedVia: unknownLineInfo, flags: flags,
+                            maxMutation: v.abstractTime, minConnection: MaxTime,
+                            mutations: @[v.abstractTime])
+    of isRootOf:
+      let g = addr v.graphs[v.s[r].con.graphIndex]
+      if g.param == nil and isConstParam(s):
+        g.param = s
+      if v.abstractTime > g.maxMutation:
+        g.mutatedHere = info
+        g.maxMutation = v.abstractTime
+      g.flags.incl flags
+      g.mutations.add v.abstractTime
+    else:
+      assert false, "cannot happen"
+  else:
+    v.unanalysableMutation = true
+
+proc connect(v: var Partitions; a, b: PSym; info: TLineInfo) =
+  let aid = variableId(v, a)
+  if aid < 0:
+    return
+  let bid = variableId(v, b)
+  if bid < 0:
+    return
+
+  let ra = root(v, aid)
+  let rb = root(v, bid)
+  if ra != rb:
+    var param = PSym(nil)
+    if isConstParam(a): param = a
+    elif isConstParam(b): param = b
+
+    let paramFlags =
+      if param != nil:
+        {connectsConstParam}
+      else:
+        {}
+
+    # for now we always make 'rb' the slave and 'ra' the master:
+    var rbFlags: set[SubgraphFlag] = {}
+    var mutatedHere = unknownLineInfo
+    var mut = AbstractTime 0
+    var con = v.abstractTime
+    var gb: ptr MutationInfo = nil
+    if v.s[rb].con.kind == isRootOf:
+      gb = addr v.graphs[v.s[rb].con.graphIndex]
+      if param == nil: param = gb.param
+      mutatedHere = gb.mutatedHere
+      rbFlags = gb.flags
+      mut = gb.maxMutation
+      con = min(con, gb.minConnection)
+
+    v.s[rb].con = Connection(kind: dependsOn, parent: ra)
+    case v.s[ra].con.kind
+    of isEmptyRoot:
+      v.s[ra].con = Connection(kind: isRootOf, graphIndex: v.graphs.len)
+      v.graphs.add MutationInfo(param: param, mutatedHere: mutatedHere,
+                            connectedVia: info, flags: paramFlags + rbFlags,
+                            maxMutation: mut, minConnection: con,
+                            mutations: if gb != nil: gb.mutations else: @[])
+    of isRootOf:
+      var g = addr v.graphs[v.s[ra].con.graphIndex]
+      if g.param == nil: g.param = param
+      if g.mutatedHere == unknownLineInfo: g.mutatedHere = mutatedHere
+      g.minConnection = min(g.minConnection, con)
+      g.connectedVia = info
+      g.flags.incl paramFlags + rbFlags
+      if gb != nil:
+        g.mutations.add gb.mutations
+    else:
+      assert false, "cannot happen"
+
+proc borrowFromConstExpr(n: PNode): bool =
+  case n.kind
+  of nkCharLit..nkNilLit:
+    result = true
+  of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv,
+      nkCast, nkObjUpConv, nkObjDownConv:
+    result = borrowFromConstExpr(n.lastSon)
+  of nkCurly, nkBracket, nkPar, nkTupleConstr, nkObjConstr, nkClosure, nkRange:
+    result = true
+    for i in ord(n.kind == nkObjConstr)..<n.len:
+      if not borrowFromConstExpr(n[i]): return false
+  of nkCallKinds:
+    if getMagic(n) == mArrToSeq:
+      result = true
+      for i in 1..<n.len:
+        if not borrowFromConstExpr(n[i]): return false
+    else:
+      result = false
+  else: result = false
+
+proc pathExpr(node: PNode; owner: PSym): PNode =
+  #[ From the spec:
+
+  - ``source`` itself is a path expression.
+  - Container access like ``e[i]`` is a path expression.
+  - Tuple access ``e[0]`` is a path expression.
+  - Object field access ``e.field`` is a path expression.
+  - ``system.toOpenArray(e, ...)`` is a path expression.
+  - Pointer dereference ``e[]`` is a path expression.
+  - An address ``addr e``, ``unsafeAddr e`` is a path expression.
+  - A type conversion ``T(e)`` is a path expression.
+  - A cast expression ``cast[T](e)`` is a path expression.
+  - ``f(e, ...)`` is a path expression if ``f``'s return type is a view type.
+    Because the view can only have been borrowed from ``e``, we then know
+    that owner of ``f(e, ...)`` is ``e``.
+
+  Returns the owner of the path expression. Returns ``nil``
+  if it is not a valid path expression.
+  ]#
+  var n = node
+  result = nil
+  while true:
+    case n.kind
+    of nkSym:
+      case n.sym.kind
+      of skParam, skTemp, skResult, skForVar:
+        if n.sym.owner == owner: result = n
+      of skVar:
+        if n.sym.owner == owner or sfThread in n.sym.flags: result = n
+      of skLet, skConst:
+        if n.sym.owner == owner or {sfThread, sfGlobal} * n.sym.flags != {}:
+          result = n
+      else:
+        discard
+      break
+    of nkDotExpr, nkDerefExpr, nkBracketExpr, nkHiddenDeref,
+        nkCheckedFieldExpr, nkAddr, nkHiddenAddr:
+      n = n[0]
+    of nkHiddenStdConv, nkHiddenSubConv, nkConv,  nkCast,
+        nkObjUpConv, nkObjDownConv:
+      n = n.lastSon
+    of nkStmtList, nkStmtListExpr:
+      if n.len > 0 and stupidStmtListExpr(n):
+        n = n.lastSon
+      else:
+        break
+    of nkCallKinds:
+      if n.len > 1:
+        if (n.typ != nil and classifyViewType(n.typ) != noView) or getMagic(n) == mSlice:
+          n = n[1]
+        else:
+          break
+      else:
+        break
+    else:
+      break
+  # borrowFromConstExpr(n) is correct here because we need 'node'
+  # stripped off the path suffixes:
+  if result == nil and borrowFromConstExpr(n):
+    result = n
+
+const
+  RootEscapes = 1000 # in 'p(r)' we don't know what p does to our poor root.
+                     # so we assume a high level of indirections
+
+proc allRoots(n: PNode; result: var seq[(PSym, int)]; level: int) =
+  case n.kind
+  of nkSym:
+    if n.sym.kind in {skParam, skVar, skTemp, skLet, skResult, skForVar}:
+      result.add((n.sym, level))
+
+  of nkDerefExpr, nkHiddenDeref:
+    allRoots(n[0], result, level+1)
+  of nkBracketExpr, nkDotExpr, nkCheckedFieldExpr, nkAddr, nkHiddenAddr:
+    allRoots(n[0], result, level)
+
+  of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkConv,
+      nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkCast,
+      nkObjUpConv, nkObjDownConv:
+    if n.len > 0:
+      allRoots(n.lastSon, result, level)
+  of nkCaseStmt, nkObjConstr:
+    for i in 1..<n.len:
+      allRoots(n[i].lastSon, result, level)
+  of nkIfStmt, nkIfExpr:
+    for i in 0..<n.len:
+      allRoots(n[i].lastSon, result, level)
+  of nkBracket, nkTupleConstr, nkPar:
+    for i in 0..<n.len:
+      allRoots(n[i], result, level-1)
+
+  of nkCallKinds:
+    if n.typ != nil and n.typ.kind in {tyVar, tyLent}:
+      if n.len > 1:
+        # XXX We really need the unwritten RFC here and distinguish between
+        #   proc `[]`(x: var Container): var T # resizes the container
+        # and
+        #   proc `[]`(x: Container): var T # only allows for slot mutation
+        allRoots(n[1], result, RootEscapes)
+    else:
+      let m = getMagic(n)
+      case m
+      of mNone:
+        if n[0].typ.isNil: return
+        var typ = n[0].typ
+        if typ != nil:
+          typ = skipTypes(typ, abstractInst)
+          if typ.kind != tyProc: typ = nil
+
+        for i in 1 ..< n.len:
+          let it = n[i]
+          if typ != nil and i < typ.n.len:
+            assert(typ.n[i].kind == nkSym)
+            let paramType = typ.n[i].typ
+            if not paramType.isCompileTimeOnly and not typ.returnType.isEmptyType and
+                canAlias(paramType, typ.returnType):
+              allRoots(it, result, RootEscapes)
+          else:
+            allRoots(it, result, RootEscapes)
+
+      of mSlice:
+        allRoots(n[1], result, level+1)
+      else:
+        discard "harmless operation"
+  else:
+    discard "nothing to do"
+
+proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) =
+  ## Analyse if 'n' is an expression that owns the data, if so mark 'dest'
+  ## with 'ownsData'.
+  case n.kind
+  of nkEmpty, nkCharLit..nkNilLit:
+    # primitive literals including the empty are harmless:
+    discard
+
+  of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast:
+    destMightOwn(c, dest, n[1])
+
+  of nkConv:
+    if hasDestructor(n.typ):
+      inc c.inConvHasDestructor
+      destMightOwn(c, dest, n[1])
+      dec c.inConvHasDestructor
+    else:
+      destMightOwn(c, dest, n[1])
+
+  of nkIfStmt, nkIfExpr:
+    for i in 0..<n.len:
+      inc c.inConditional
+      destMightOwn(c, dest, n[i].lastSon)
+      dec c.inConditional
+
+  of nkCaseStmt:
+    for i in 1..<n.len:
+      inc c.inConditional
+      destMightOwn(c, dest, n[i].lastSon)
+      dec c.inConditional
+
+  of nkStmtList, nkStmtListExpr:
+    if n.len > 0:
+      destMightOwn(c, dest, n[^1])
+
+  of nkClosure:
+    for i in 1..<n.len:
+      destMightOwn(c, dest, n[i])
+    # you must destroy a closure:
+    dest.flags.incl ownsData
+
+  of nkObjConstr:
+    for i in 1..<n.len:
+      destMightOwn(c, dest, n[i])
+    if hasDestructor(n.typ):
+      # you must destroy a ref object:
+      dest.flags.incl ownsData
+
+  of nkCurly, nkBracket, nkPar, nkTupleConstr:
+    inc c.inConstructor
+    for son in n:
+      destMightOwn(c, dest, son)
+    dec c.inConstructor
+    if n.typ.skipTypes(abstractInst).kind == tySequence:
+      # you must destroy a sequence:
+      dest.flags.incl ownsData
+
+  of nkSym:
+    if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar, skParam}:
+      if n.sym.flags * {sfThread, sfGlobal} != {}:
+        # aliasing a global is inherently dangerous:
+        dest.flags.incl ownsData
+      else:
+        # otherwise it's just a dependency, nothing to worry about:
+        connect(c, dest.sym, n.sym, n.info)
+        # but a construct like ``[symbol]`` is dangerous:
+        if c.inConstructor > 0: dest.flags.incl ownsData
+
+  of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr,
+      nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr, nkAddr, nkHiddenAddr:
+    destMightOwn(c, dest, n[0])
+
+  of nkCallKinds:
+    if n.typ != nil:
+      if hasDestructor(n.typ) or c.inConvHasDestructor > 0:
+        # calls do construct, what we construct must be destroyed,
+        # so dest cannot be a cursor:
+        dest.flags.incl ownsData
+      elif n.typ.kind in {tyLent, tyVar} and n.len > 1:
+        # we know the result is derived from the first argument:
+        var roots: seq[(PSym, int)] = @[]
+        allRoots(n[1], roots, RootEscapes)
+        if roots.len == 0 and c.inConditional > 0:
+          # when in a conditional expression,
+          # to ensure that the first argument isn't outlived
+          # by the lvalue, we need find the root, otherwise
+          # it is probably a local temporary
+          # (e.g. a return value from a call),
+          # we should prevent cursorfication
+          dest.flags.incl preventCursor
+        else:
+          for r in roots:
+            connect(c, dest.sym, r[0], n[1].info)
+
+      else:
+        let magic = if n[0].kind == nkSym: n[0].sym.magic else: mNone
+        # this list is subtle, we try to answer the question if after 'dest = f(src)'
+        # there is a connection betwen 'src' and 'dest' so that mutations to 'src'
+        # also reflect 'dest':
+        if magic in {mNone, mMove, mSlice,
+            mAppendStrCh, mAppendStrStr, mAppendSeqElem,
+            mArrToSeq, mOpenArrayToSeq}:
+          for i in 1..<n.len:
+            # we always have to assume a 'select(...)' like mechanism.
+            # But at least we do filter out simple POD types from the
+            # list of dependencies via the 'hasDestructor' check for
+            # the root's symbol.
+            if hasDestructor(n[i].typ.skipTypes({tyVar, tySink, tyLent, tyGenericInst, tyAlias})):
+              destMightOwn(c, dest, n[i])
+
+  else:
+    # something we cannot handle:
+    dest.flags.incl preventCursor
+
+proc noCursor(c: var Partitions, s: PSym) =
+  let vid = variableId(c, s)
+  if vid >= 0:
+    c.s[vid].flags.incl preventCursor
+
+proc pretendOwnsData(c: var Partitions, s: PSym) =
+  let vid = variableId(c, s)
+  if vid >= 0:
+    c.s[vid].flags.incl ownsData
+
+const
+  explainCursors = false
+
+proc isConstSym(s: PSym): bool =
+  result = s.kind in {skConst, skLet} or isConstParam(s)
+
+proc toString(n: PNode): string =
+  if n.kind == nkEmpty: result = "<empty>"
+  else: result = $n
+
+proc borrowFrom(c: var Partitions; dest: PSym; src: PNode) =
+  const
+    url = "see https://nim-lang.github.io/Nim/manual_experimental.html#view-types-algorithm-path-expressions for details"
+
+  let s = pathExpr(src, c.owner)
+  if s == nil:
+    localError(c.g.config, src.info, "cannot borrow from " & src.toString & ", it is not a path expression; " & url)
+  elif s.kind == nkSym:
+    if dest.kind == skResult:
+      if s.sym.kind != skParam or s.sym.position != 0:
+        localError(c.g.config, src.info, "'result' must borrow from the first parameter")
+
+    let vid = variableId(c, dest)
+    if vid >= 0:
+      var sourceIdx = variableId(c, s.sym)
+      if sourceIdx < 0:
+        sourceIdx = c.s.len
+        c.s.add VarIndex(con: Connection(kind: isEmptyRoot), sym: s.sym, reassignedTo: 0,
+                        aliveStart: MinTime, aliveEnd: MaxTime)
+
+      c.s[vid].borrowsFrom.add sourceIdx
+      if isConstSym(s.sym):
+        c.s[vid].flags.incl viewBorrowsFromConst
+  else:
+    let vid = variableId(c, dest)
+    if vid >= 0:
+      c.s[vid].flags.incl viewBorrowsFromConst
+    #discard "a valid borrow location that is a deeply constant expression so we have nothing to track"
+
+
+proc borrowingCall(c: var Partitions; destType: PType; n: PNode; i: int) =
+  let v = pathExpr(n[i], c.owner)
+  if v != nil and v.kind == nkSym:
+    when false:
+      let isView = directViewType(destType) == immutableView
+      if n[0].kind == nkSym and n[0].sym.name.s == "[]=":
+        localError(c.g.config, n[i].info, "attempt to mutate an immutable view")
+
+    for j in i+1..<n.len:
+      if getMagic(n[j]) == mSlice:
+        borrowFrom(c, v.sym, n[j])
+  else:
+    localError(c.g.config, n[i].info, "cannot determine the target of the borrow")
+
+proc borrowingAsgn(c: var Partitions; dest, src: PNode) =
+  proc mutableParameter(n: PNode): bool {.inline.} =
+    result = n.kind == nkSym and n.sym.kind == skParam and n.sym.typ.kind == tyVar
+
+  if dest.kind == nkSym:
+    if directViewType(dest.typ) != noView:
+      borrowFrom(c, dest.sym, src)
+  else:
+    let viewOrigin = pathExpr(dest, c.owner)
+    if viewOrigin != nil and viewOrigin.kind == nkSym:
+      let viewSym = viewOrigin.sym
+      let directView = directViewType(dest[0].typ) # check something like result[first] = toOpenArray(s, first, last-1)
+                                                   # so we don't need to iterate the original type
+      let originSymbolView = directViewType(viewSym.typ) # find the original symbol which preserves the view type
+                                                    #  var foo: var Object = a
+                                                    #  foo.id = 777 # the type of foo is no view, so we need
+                                                    #  to check the original symbol
+      let viewSets = {directView, originSymbolView}
+
+      if viewSets * {mutableView, immutableView} != {}:
+        # we do not borrow, but we use the view to mutate the borrowed
+        # location:
+        let vid = variableId(c, viewSym)
+        if vid >= 0:
+          c.s[vid].flags.incl viewDoesMutate
+      #[of immutableView:
+        if dest.kind == nkBracketExpr and dest[0].kind == nkHiddenDeref and
+            mutableParameter(dest[0][0]):
+          discard "remains a mutable location anyhow"
+        else:
+          localError(c.g.config, dest.info, "attempt to mutate a borrowed location from an immutable view")
+          ]#
+      else:
+        discard "nothing to do"
+
+proc containsPointer(t: PType): bool =
+  proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr}
+  result = types.searchTypeFor(t, wrap)
+
+proc deps(c: var Partitions; dest, src: PNode) =
+  if borrowChecking in c.goals:
+    borrowingAsgn(c, dest, src)
+
+  var targets: seq[(PSym, int)] = @[]
+  var sources: seq[(PSym, int)] = @[]
+  allRoots(dest, targets, 0)
+  allRoots(src, sources, 0)
+
+  let destIsComplex = containsPointer(dest.typ)
+
+  for t in targets:
+    if dest.kind != nkSym and c.inNoSideEffectSection == 0:
+      potentialMutation(c, t[0], t[1], dest.info)
+
+    if destIsComplex:
+      for s in sources:
+        connect(c, t[0], s[0], dest.info)
+
+  if cursorInference in c.goals and src.kind != nkEmpty:
+    let d = pathExpr(dest, c.owner)
+    if d != nil and d.kind == nkSym:
+      let vid = variableId(c, d.sym)
+      if vid >= 0:
+        destMightOwn(c, c.s[vid], src)
+        for source in sources:
+          let s = source[0]
+          if s == d.sym:
+            discard "assignments like: it = it.next are fine"
+          elif {sfGlobal, sfThread} * s.flags != {} or hasDisabledAsgn(c.g, s.typ):
+            # do not borrow from a global variable or from something with a
+            # disabled assignment operator.
+            c.s[vid].flags.incl preventCursor
+            when explainCursors: echo "A not a cursor: ", d.sym, " ", s
+          else:
+            let srcid = variableId(c, s)
+            if srcid >= 0:
+              if s.kind notin {skResult, skParam} and (
+                  c.s[srcid].aliveEnd < c.s[vid].aliveEnd):
+                # you cannot borrow from a local that lives shorter than 'vid':
+                when explainCursors: echo "B not a cursor ", d.sym, " ", c.s[srcid].aliveEnd, " ", c.s[vid].aliveEnd
+                c.s[vid].flags.incl preventCursor
+              elif {isReassigned, preventCursor} * c.s[srcid].flags != {}:
+                # you cannot borrow from something that is re-assigned:
+                when explainCursors: echo "C not a cursor ", d.sym, " ", c.s[srcid].flags, " reassignedTo ", c.s[srcid].reassignedTo
+                c.s[vid].flags.incl preventCursor
+              elif c.s[srcid].reassignedTo != 0 and c.s[srcid].reassignedTo != d.sym.id:
+                when explainCursors: echo "D not a cursor ", d.sym, " reassignedTo ", c.s[srcid].reassignedTo
+                c.s[vid].flags.incl preventCursor
+
+
+proc potentialMutationViaArg(c: var Partitions; n: PNode; callee: PType) =
+  if constParameters in c.goals and tfNoSideEffect in callee.flags:
+    discard "we know there are no hidden mutations through an immutable parameter"
+  elif c.inNoSideEffectSection == 0 and containsPointer(n.typ):
+    var roots: seq[(PSym, int)] = @[]
+    allRoots(n, roots, RootEscapes)
+    for r in roots: potentialMutation(c, r[0], r[1], n.info)
+
+proc traverse(c: var Partitions; n: PNode) =
+  inc c.abstractTime
+  case n.kind
+  of nkLetSection, nkVarSection:
+    for child in n:
+      let last = lastSon(child)
+      traverse(c, last)
+      if child.kind == nkVarTuple and last.kind in {nkPar, nkTupleConstr}:
+        if child.len-2 != last.len: return
+        for i in 0..<child.len-2:
+          #registerVariable(c, child[i])
+          deps(c, child[i], last[i])
+      else:
+        for i in 0..<child.len-2:
+          #registerVariable(c, child[i])
+          deps(c, child[i], last)
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
+    traverse(c, n[0])
+    inc c.inAsgnSource
+    traverse(c, n[1])
+    dec c.inAsgnSource
+    deps(c, n[0], n[1])
+  of nkSym:
+    dec c.abstractTime
+
+  of nodesToIgnoreSet:
+    dec c.abstractTime
+    discard "do not follow the construct"
+  of nkCallKinds:
+    for child in n: traverse(c, child)
+
+    let parameters = n[0].typ
+    let L = if parameters != nil: parameters.signatureLen else: 0
+    let m = getMagic(n)
+
+    if m == mEnsureMove and n[1].kind == nkSym:
+      # we know that it must be moved so it cannot be a cursor
+      noCursor(c, n[1].sym)
+
+    for i in 1..<n.len:
+      let it = n[i]
+      if i < L:
+        let paramType = parameters[i].skipTypes({tyGenericInst, tyAlias})
+        if not paramType.isCompileTimeOnly and paramType.kind in {tyVar, tySink, tyOwned}:
+          var roots: seq[(PSym, int)] = @[]
+          allRoots(it, roots, RootEscapes)
+          if paramType.kind == tyVar:
+            if c.inNoSideEffectSection == 0:
+              for r in roots: potentialMutation(c, r[0], r[1], it.info)
+            for r in roots: noCursor(c, r[0])
+
+            if borrowChecking in c.goals:
+              # a call like 'result.add toOpenArray()' can also be a borrow
+              # operation. We know 'paramType' is a tyVar and we really care if
+              # 'paramType[0]' is still a view type, this is not a typo!
+              if directViewType(paramType[0]) == noView and classifyViewType(paramType[0]) != noView:
+                borrowingCall(c, paramType[0], n, i)
+        elif m == mNone:
+          potentialMutationViaArg(c, n[i], parameters)
+
+  of nkAddr, nkHiddenAddr:
+    traverse(c, n[0])
+    when false:
+      # XXX investigate if this is required, it doesn't look
+      # like it is!
+      var roots: seq[(PSym, int)]
+      allRoots(n[0], roots, RootEscapes)
+      for r in roots:
+        potentialMutation(c, r[0], r[1], it.info)
+
+  of nkTupleConstr, nkBracket:
+    for child in n: traverse(c, child)
+    if c.inAsgnSource > 0:
+      for i in 0..<n.len:
+        if n[i].kind == nkSym:
+          # we assume constructions with cursors are better without
+          # the cursors because it's likely we can move then, see
+          # test arc/topt_no_cursor.nim
+          pretendOwnsData(c, n[i].sym)
+
+  of nkObjConstr:
+    for child in n: traverse(c, child)
+    if c.inAsgnSource > 0:
+      for i in 1..<n.len:
+        let it = n[i].skipColon
+        if it.kind == nkSym:
+          # we assume constructions with cursors are better without
+          # the cursors because it's likely we can move then, see
+          # test arc/topt_no_cursor.nim
+          pretendOwnsData(c, it.sym)
+
+  of nkPragmaBlock:
+    let pragmaList = n[0]
+    var enforceNoSideEffects = 0
+    for i in 0..<pragmaList.len:
+      if isNoSideEffectPragma(pragmaList[i]):
+        enforceNoSideEffects = 1
+        break
+
+    inc c.inNoSideEffectSection, enforceNoSideEffects
+    traverse(c, n.lastSon)
+    dec c.inNoSideEffectSection, enforceNoSideEffects
+  of nkWhileStmt, nkForStmt, nkParForStmt:
+    for child in n: traverse(c, child)
+    # analyse loops twice so that 'abstractTime' suffices to detect cases
+    # like:
+    #   while cond:
+    #     mutate(graph)
+    #     connect(graph, cursorVar)
+    for child in n: traverse(c, child)
+
+    if n.kind == nkWhileStmt:
+      traverse(c, n[0])
+      # variables in while condition has longer alive time than local variables
+      # in the while loop body
+  of nkDefer:
+    if c.processDefer:
+      for child in n: traverse(c, child)
+  else:
+    for child in n: traverse(c, child)
+
+proc markAsReassigned(c: var Partitions; vid: int) {.inline.} =
+  c.s[vid].flags.incl isReassigned
+  if c.inConditional > 0 and c.inLoop > 0:
+    # bug #17033: live ranges with loops and conditionals are too
+    # complex for our current analysis, so we prevent the cursorfication.
+    c.s[vid].flags.incl isConditionallyReassigned
+
+proc computeLiveRanges(c: var Partitions; n: PNode) =
+  # first pass: Compute live ranges for locals.
+  # **Watch out!** We must traverse the tree like 'traverse' does
+  # so that the 'c.abstractTime' is consistent.
+  inc c.abstractTime
+  case n.kind
+  of nkLetSection, nkVarSection:
+    for child in n:
+      let last = lastSon(child)
+      computeLiveRanges(c, last)
+      if child.kind == nkVarTuple and last.kind in {nkPar, nkTupleConstr}:
+        if child.len-2 != last.len: return
+        for i in 0..<child.len-2:
+          registerVariable(c, child[i])
+          #deps(c, child[i], last[i])
+      else:
+        for i in 0..<child.len-2:
+          registerVariable(c, child[i])
+          #deps(c, child[i], last)
+
+        if c.inLoop > 0 and child[0].kind == nkSym: # bug #22787
+          let vid = variableId(c, child[0].sym)
+          if child[^1].kind != nkEmpty:
+            markAsReassigned(c, vid)
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
+    computeLiveRanges(c, n[0])
+    computeLiveRanges(c, n[1])
+    if n[0].kind == nkSym:
+      let vid = variableId(c, n[0].sym)
+      if vid >= 0:
+        if n[1].kind == nkSym and (c.s[vid].reassignedTo == 0 or c.s[vid].reassignedTo == n[1].sym.id):
+          c.s[vid].reassignedTo = n[1].sym.id
+          if c.inConditional > 0 and c.inLoop > 0:
+            # bug #22200: live ranges with loops and conditionals are too
+            # complex for our current analysis, so we prevent the cursorfication.
+            c.s[vid].flags.incl isConditionallyReassigned
+        else:
+          markAsReassigned(c, vid)
+
+  of nkSym:
+    dec c.abstractTime
+    if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar, skParam}:
+      let id = variableId(c, n.sym)
+      if id >= 0:
+        c.s[id].aliveEnd = max(c.s[id].aliveEnd, c.abstractTime)
+        if n.sym.kind == skResult:
+          c.s[id].aliveStart = min(c.s[id].aliveStart, c.abstractTime)
+
+  of nodesToIgnoreSet:
+    dec c.abstractTime
+    discard "do not follow the construct"
+  of nkCallKinds:
+    for child in n: computeLiveRanges(c, child)
+
+    let parameters = n[0].typ
+    let L = if parameters != nil: parameters.signatureLen else: 0
+
+    for i in 1..<n.len:
+      let it = n[i]
+      if it.kind == nkSym and i < L:
+        let paramType = parameters[i].skipTypes({tyGenericInst, tyAlias})
+        if not paramType.isCompileTimeOnly and paramType.kind == tyVar:
+          let vid = variableId(c, it.sym)
+          if vid >= 0:
+            markAsReassigned(c, vid)
+
+  of nkAddr, nkHiddenAddr:
+    computeLiveRanges(c, n[0])
+    if n[0].kind == nkSym:
+      let vid = variableId(c, n[0].sym)
+      if vid >= 0:
+        c.s[vid].flags.incl preventCursor
+
+  of nkPragmaBlock:
+    computeLiveRanges(c, n.lastSon)
+  of nkWhileStmt, nkForStmt, nkParForStmt:
+    for child in n: computeLiveRanges(c, child)
+    # analyse loops twice so that 'abstractTime' suffices to detect cases
+    # like:
+    #   while cond:
+    #     mutate(graph)
+    #     connect(graph, cursorVar)
+    inc c.inLoop
+    for child in n: computeLiveRanges(c, child)
+    dec c.inLoop
+
+    if n.kind == nkWhileStmt:
+      computeLiveRanges(c, n[0])
+      # variables in while condition has longer alive time than local variables
+      # in the while loop body
+  of nkElifBranch, nkElifExpr, nkElse, nkOfBranch:
+    inc c.inConditional
+    for child in n: computeLiveRanges(c, child)
+    dec c.inConditional
+  of nkDefer:
+    if c.processDefer:
+      for child in n: computeLiveRanges(c, child)
+    else:
+      c.defers.add n
+  else:
+    for child in n: computeLiveRanges(c, child)
+
+proc computeGraphPartitions*(s: PSym; n: PNode; g: ModuleGraph; goals: set[Goal]): Partitions =
+  result = Partitions(owner: s, g: g, goals: goals)
+  if s.kind notin {skModule, skMacro}:
+    let params = s.typ.n
+    for i in 1..<params.len:
+      registerParam(result, params[i])
+    if resultPos < s.ast.safeLen:
+      registerResult(result, s.ast[resultPos])
+
+  computeLiveRanges(result, n)
+  result.processDefer = true
+  for i in countdown(len(result.defers)-1, 0):
+    computeLiveRanges(result, result.defers[i])
+  result.processDefer = false
+  # restart the timer for the second pass:
+  result.abstractTime = AbstractTime 0
+  traverse(result, n)
+  result.processDefer = true
+  for i in countdown(len(result.defers)-1, 0):
+    traverse(result, result.defers[i])
+  result.processDefer = false
+
+proc dangerousMutation(g: MutationInfo; v: VarIndex): bool =
+  #echo "range ", v.aliveStart, " .. ", v.aliveEnd, " ", v.sym
+  if {isMutated, isMutatedByVarParam} * g.flags != {}:
+    for m in g.mutations:
+      #echo "mutation ", m
+      if m in v.aliveStart..v.aliveEnd:
+        return true
+  return false
+
+proc cannotBorrow(config: ConfigRef; s: PSym; g: MutationInfo) =
+  var m = "cannot borrow " & s.name.s &
+    "; what it borrows from is potentially mutated"
+
+  if g.mutatedHere != unknownLineInfo:
+    m.add "\n"
+    m.add config $ g.mutatedHere
+    m.add " the mutation is here"
+  if g.connectedVia != unknownLineInfo:
+    m.add "\n"
+    m.add config $ g.connectedVia
+    m.add " is the statement that connected the mutation to the parameter"
+  localError(config, s.info, m)
+
+proc checkBorrowedLocations*(par: var Partitions; body: PNode; config: ConfigRef) =
+  for i in 0 ..< par.s.len:
+    let v = par.s[i].sym
+    if v.kind != skParam and classifyViewType(v.typ) != noView:
+      let rid = root(par, i)
+      if rid >= 0:
+        var constViolation = false
+        for b in par.s[rid].borrowsFrom:
+          let sid = root(par, b)
+          if sid >= 0:
+            if par.s[sid].con.kind == isRootOf and dangerousMutation(par.graphs[par.s[sid].con.graphIndex], par.s[i]):
+              cannotBorrow(config, v, par.graphs[par.s[sid].con.graphIndex])
+            if par.s[sid].sym.kind != skParam and par.s[sid].aliveEnd < par.s[rid].aliveEnd:
+              localError(config, v.info, "'" & v.name.s & "' borrows from location '" & par.s[sid].sym.name.s &
+                "' which does not live long enough")
+            if viewDoesMutate in par.s[rid].flags and isConstSym(par.s[sid].sym):
+              localError(config, v.info, "'" & v.name.s & "' borrows from the immutable location '" &
+                par.s[sid].sym.name.s & "' and attempts to mutate it")
+              constViolation = true
+        if {viewDoesMutate, viewBorrowsFromConst} * par.s[rid].flags == {viewDoesMutate, viewBorrowsFromConst} and
+            not constViolation:
+          # we do not track the constant expressions we allow to borrow from so
+          # we can only produce a more generic error message:
+          localError(config, v.info, "'" & v.name.s &
+              "' borrows from an immutable location and attempts to mutate it")
+
+      #if par.s[rid].con.kind == isRootOf and dangerousMutation(par.graphs[par.s[rid].con.graphIndex], par.s[i]):
+      #  cannotBorrow(config, s, par.graphs[par.s[rid].con.graphIndex])
+
+proc computeCursors*(s: PSym; n: PNode; g: ModuleGraph) =
+  var par = computeGraphPartitions(s, n, g, {cursorInference})
+  for i in 0 ..< par.s.len:
+    let v = addr(par.s[i])
+    if v.flags * {ownsData, preventCursor, isConditionallyReassigned} == {} and
+        v.sym.kind notin {skParam, skResult} and
+        v.sym.flags * {sfThread, sfGlobal} == {} and hasDestructor(v.sym.typ) and
+        v.sym.typ.skipTypes({tyGenericInst, tyAlias}).kind != tyOwned and
+        (getAttachedOp(g, v.sym.typ, attachedAsgn) == nil or
+        sfError notin getAttachedOp(g, v.sym.typ, attachedAsgn).flags):
+      let rid = root(par, i)
+      if par.s[rid].con.kind == isRootOf and dangerousMutation(par.graphs[par.s[rid].con.graphIndex], par.s[i]):
+        discard "cannot cursor into a graph that is mutated"
+      else:
+        v.sym.flags.incl sfCursor
+        when false:
+          echo "this is now a cursor ", v.sym, " ", par.s[rid].flags, " ", g.config $ v.sym.info
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 3e33e8256..161b025a6 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -10,48 +10,27 @@
 ## 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 semmacrosanity
 import
-  strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes,
-  parser, vmdeps, idents, trees, renderer, options, transf, parseutils,
-  vmmarshal, gorgeimpl, lineinfos, tables, btrees, macrocacheimpl
-
+  std/[strutils, tables, parseutils],
+  msgs, vmdef, vmgen, nimsets, types,
+  parser, vmdeps, idents, trees, renderer, options, transf,
+  gorgeimpl, lineinfos, btrees, macrocacheimpl,
+  modulegraphs, sighashes, int128, vmprofiler
+
+when defined(nimPreviewSlimSystem):
+  import std/formatfloat
+import ast except getstr
 from semfold import leValueConv, ordinalValToString
 from evaltempl import evalTemplate
+from magicsys import getSysType
 
-from modulegraphs import ModuleGraph
+const
+  traceCode = defined(nimVMDebug)
 
 when hasFFI:
   import evalffi
 
-type
-  TRegisterKind = enum
-    rkNone, rkNode, rkInt, rkFloat, rkRegisterAddr, rkNodeAddr
-  TFullReg = object   # with a custom mark proc, we could use the same
-                      # data representation as LuaJit (tagged NaNs).
-    case kind: TRegisterKind
-    of rkNone: nil
-    of rkInt: intVal: BiggestInt
-    of rkFloat: floatVal: BiggestFloat
-    of rkNode: node: PNode
-    of rkRegisterAddr: regAddr: ptr TFullReg
-    of rkNodeAddr: nodeAddr: ptr PNode
-
-  PStackFrame* = ref TStackFrame
-  TStackFrame* = object
-    prc: PSym                 # current prc; proc that is evaluated
-    slots: seq[TFullReg]      # parameters passed to the proc + locals;
-                              # parameters come first
-    next: PStackFrame         # for stacking
-    comesFrom: int
-    safePoints: seq[int]      # used for exception handling
-                              # XXX 'break' should perform cleanup actions
-                              # What does the C backend do for it?
 
 proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) =
   if x != nil:
@@ -61,47 +40,62 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) =
       while x != nil:
         inc calls
         x = x.next
-      msgWriteln(c.config, $calls & " calls omitted\n")
+      msgWriteln(c.config, $calls & " calls omitted\n", {msgNoUnitSep})
       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, ')')
+      s.add('(')
+      s.add($line)
+      s.add(", ")
+      s.add($(col + ColOffset))
+      s.add(')')
     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,
-                msg: string, n: PNode = nil) =
-  msgWriteln(c.config, "stack trace: (most recent call last)")
+      for k in 1..max(1, 25-s.len): s.add(' ')
+      s.add(x.prc.name.s)
+    msgWriteln(c.config, s, {msgNoUnitSep})
+
+proc stackTraceImpl(c: PCtx, tos: PStackFrame, pc: int,
+  msg: string, lineInfo: TLineInfo, infoOrigin: InstantiationInfo) {.noinline.} =
+  # noinline to avoid code bloat
+  msgWriteln(c.config, "stack trace: (most recent call last)", {msgNoUnitSep})
   stackTraceAux(c, tos, pc)
-  # XXX test if we want 'globalError' for every mode
-  let lineInfo = if n == nil: c.debug[pc] else: n.info
-  if c.mode == emRepl: globalError(c.config, lineInfo, msg)
-  else: localError(c.config, lineInfo, msg)
+  let action = if c.mode == emRepl: doRaise else: doNothing
+    # XXX test if we want 'globalError' for every mode
+  let lineInfo = if lineInfo == TLineInfo.default: c.debug[pc] else: lineInfo
+  liMessage(c.config, lineInfo, errGenerated, msg, action, infoOrigin)
+
+when not defined(nimHasCallsitePragma):
+  {.pragma: callsite.}
+
+template stackTrace(c: PCtx, tos: PStackFrame, pc: int,
+                    msg: string, lineInfo: TLineInfo = TLineInfo.default) {.callsite.} =
+  stackTraceImpl(c, tos, pc, msg, lineInfo, instantiationInfo(-2, fullPaths = true))
+  return
 
 proc bailOut(c: PCtx; tos: PStackFrame) =
   stackTrace(c, tos, c.exceptionInstr, "unhandled exception: " &
-             c.currentExceptionA.sons[3].skipColon.strVal)
+             c.currentExceptionA[3].skipColon.strVal &
+             " [" & c.currentExceptionA[2].skipColon.strVal & "]")
 
 when not defined(nimComputedGoto):
   {.pragma: computedGoto.}
 
-proc myreset(n: var TFullReg) = reset(n)
+proc ensureKind(n: var TFullReg, k: TRegisterKind) {.inline.} =
+  if n.kind != k:
+    n = TFullReg(kind: k)
 
 template ensureKind(k: untyped) {.dirty.} =
-  if regs[ra].kind != k:
-    myreset(regs[ra])
-    regs[ra].kind = k
+  ensureKind(regs[ra], k)
 
 template decodeB(k: untyped) {.dirty.} =
   let rb = instr.regB
@@ -125,8 +119,44 @@ template decodeBx(k: untyped) {.dirty.} =
   let rbx = instr.regBx - wordExcess
   ensureKind(k)
 
-template move(a, b: untyped) {.dirty.} = system.shallowCopy(a, b)
-# XXX fix minor 'shallowCopy' overloading bug in compiler
+template move(a, b: untyped) {.dirty.} =
+  when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
+    a = move b
+  else:
+    system.shallowCopy(a, b)
+    # XXX fix minor 'shallowCopy' overloading bug in compiler
+
+proc derefPtrToReg(address: BiggestInt, typ: PType, r: var TFullReg, isAssign: bool): bool =
+  # nim bug: `isAssign: static bool` doesn't work, giving odd compiler error
+  template fun(field, typ, rkind) =
+    if isAssign:
+      cast[ptr typ](address)[] = typ(r.field)
+    else:
+      r.ensureKind(rkind)
+      let val = cast[ptr typ](address)[]
+      when typ is SomeInteger | char:
+        r.field = BiggestInt(val)
+      else:
+        r.field = val
+    return true
+
+  ## see also typeinfo.getBiggestInt
+  case typ.kind
+  of tyChar: fun(intVal, char, rkInt)
+  of tyInt: fun(intVal, int, rkInt)
+  of tyInt8: fun(intVal, int8, rkInt)
+  of tyInt16: fun(intVal, int16, rkInt)
+  of tyInt32: fun(intVal, int32, rkInt)
+  of tyInt64: fun(intVal, int64, rkInt)
+  of tyUInt: fun(intVal, uint, rkInt)
+  of tyUInt8: fun(intVal, uint8, rkInt)
+  of tyUInt16: fun(intVal, uint16, rkInt)
+  of tyUInt32: fun(intVal, uint32, rkInt)
+  of tyUInt64: fun(intVal, uint64, rkInt) # note: differs from typeinfo.getBiggestInt
+  of tyFloat: fun(floatVal, float, rkFloat)
+  of tyFloat32: fun(floatVal, float32, rkFloat)
+  of tyFloat64: fun(floatVal, float64, rkFloat)
+  else: return false
 
 proc createStrKeepNode(x: var TFullReg; keepNode=true) =
   if x.node.isNil or not keepNode:
@@ -134,8 +164,7 @@ proc createStrKeepNode(x: var TFullReg; keepNode=true) =
   elif x.node.kind == nkNilLit and keepNode:
     when defined(useNodeIds):
       let id = x.node.id
-    system.reset(x.node[])
-    x.node.kind = nkStrLit
+    x.node[] = TNode(kind: nkStrLit)
     when defined(useNodeIds):
       x.node.id = id
   elif x.node.kind notin {nkStrLit..nkTripleStrLit} or
@@ -144,7 +173,7 @@ proc createStrKeepNode(x: var TFullReg; keepNode=true) =
     x.node = newNode(nkStrLit)
     # It not only hackey, it is also wrong for tgentemplate. The primary
     # cause of bugs like these is that the VM does not properly distinguish
-    # between variable defintions (var foo = e) and variable updates (foo = e).
+    # between variable definitions (var foo = e) and variable updates (foo = e).
 
 include vmhooks
 
@@ -155,9 +184,7 @@ template createSet(x) =
   x.node = newNode(nkCurly)
 
 proc moveConst(x: var TFullReg, y: TFullReg) =
-  if x.kind != y.kind:
-    myreset(x)
-    x.kind = y.kind
+  x.ensureKind(y.kind)
   case x.kind
   of rkNone: discard
   of rkInt: x.intVal = y.intVal
@@ -188,14 +215,12 @@ proc copyValue(src: PNode): PNode =
   of nkIdent: result.ident = src.ident
   of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
   else:
-    newSeq(result.sons, sonsLen(src))
-    for i in countup(0, sonsLen(src) - 1):
-      result.sons[i] = copyValue(src.sons[i])
+    newSeq(result.sons, src.len)
+    for i in 0..<src.len:
+      result[i] = copyValue(src[i])
 
 proc asgnComplex(x: var TFullReg, y: TFullReg) =
-  if x.kind != y.kind:
-    myreset(x)
-    x.kind = y.kind
+  x.ensureKind(y.kind)
   case x.kind
   of rkNone: discard
   of rkInt: x.intVal = y.intVal
@@ -204,38 +229,44 @@ proc asgnComplex(x: var TFullReg, y: TFullReg) =
   of rkRegisterAddr: x.regAddr = y.regAddr
   of rkNodeAddr: x.nodeAddr = y.nodeAddr
 
-proc putIntoNode(n: var PNode; x: TFullReg) =
+proc fastAsgnComplex(x: var TFullReg, y: TFullReg) =
+  x.ensureKind(y.kind)
   case x.kind
   of rkNone: discard
-  of rkInt: n.intVal = x.intVal
+  of rkInt: x.intVal = y.intVal
+  of rkFloat: x.floatVal = y.floatVal
+  of rkNode: x.node = y.node
+  of rkRegisterAddr: x.regAddr = y.regAddr
+  of rkNodeAddr: x.nodeAddr = y.nodeAddr
+
+proc writeField(n: var PNode, x: TFullReg) =
+  case x.kind
+  of rkNone: discard
+  of rkInt:
+    if n.kind == nkNilLit:
+      n[] = TNode(kind: nkIntLit) # ideally, `nkPtrLit`
+    n.intVal = x.intVal
   of rkFloat: n.floatVal = x.floatVal
-  of rkNode:
-    if nfIsRef in x.node.flags:
-      n = x.node
-    else:
-      let destIsRef = nfIsRef in n.flags
-      n[] = x.node[]
-      # Ref-ness must be kept for the destination
-      if destIsRef:
-        n.flags.incl nfIsRef
-  of rkRegisterAddr: putIntoNode(n, x.regAddr[])
-  of rkNodeAddr: n[] = x.nodeAddr[][]
+  of rkNode: n = copyValue(x.node)
+  of rkRegisterAddr: writeField(n, x.regAddr[])
+  of rkNodeAddr: n = x.nodeAddr[]
 
 proc putIntoReg(dest: var TFullReg; n: PNode) =
   case n.kind
   of nkStrLit..nkTripleStrLit:
-    dest.kind = rkNode
-    createStr(dest)
-    dest.node.strVal = n.strVal
-  of nkCharLit..nkUInt64Lit:
-    dest.kind = rkInt
-    dest.intVal = n.intVal
+    dest = TFullReg(kind: rkNode, node: newStrNode(nkStrLit, n.strVal))
+  of nkIntLit: # use `nkPtrLit` once this is added
+    if dest.kind == rkNode: dest.node = n
+    elif n.typ != nil and n.typ.kind in PtrLikeKinds:
+      dest = TFullReg(kind: rkNode, node: n)
+    else:
+      dest = TFullReg(kind: rkInt, intVal: n.intVal)
+  of {nkCharLit..nkUInt64Lit} - {nkIntLit}:
+    dest = TFullReg(kind: rkInt, intVal: n.intVal)
   of nkFloatLit..nkFloat128Lit:
-    dest.kind = rkFloat
-    dest.floatVal = n.floatVal
+    dest = TFullReg(kind: rkFloat, floatVal: n.floatVal)
   else:
-    dest.kind = rkNode
-    dest.node = n
+    dest = TFullReg(kind: rkNode, node: n)
 
 proc regToNode(x: TFullReg): PNode =
   case x.kind
@@ -250,87 +281,121 @@ template getstr(a: untyped): untyped =
   (if a.kind == rkNode: a.node.strVal else: $chr(int(a.intVal)))
 
 proc pushSafePoint(f: PStackFrame; pc: int) =
-  if f.safePoints.isNil: f.safePoints = @[]
   f.safePoints.add(pc)
 
 proc popSafePoint(f: PStackFrame) =
-  # XXX this needs a proper fix!
-  if f.safePoints.len > 0:
-    discard f.safePoints.pop()
-
-proc cleanUpOnException(c: PCtx; tos: PStackFrame):
-                                              tuple[pc: int, f: PStackFrame] =
-  let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs)
-  var f = tos
-  while true:
-    while f.safePoints.isNil or f.safePoints.len == 0:
-      f = f.next
-      if f.isNil: return (-1, nil)
-    var pc2 = f.safePoints[f.safePoints.high]
-
-    var nextExceptOrFinally = -1
-    if c.code[pc2].opcode == opcExcept:
-      nextExceptOrFinally = pc2 + c.code[pc2].regBx - wordExcess
-      inc pc2
-    while c.code[pc2].opcode == opcExcept:
-      let excIndex = c.code[pc2].regBx-wordExcess
-      let exceptType = if excIndex > 0: c.types[excIndex].skipTypes(
-                          abstractPtrs)
-                       else: nil
-      #echo typeToString(exceptType), " ", typeToString(raisedType)
-      if exceptType.isNil or inheritanceDiff(exceptType, raisedType) <= 0:
-        # mark exception as handled but keep it in B for
-        # the getCurrentException() builtin:
-        c.currentExceptionB = c.currentExceptionA
-        c.currentExceptionA = nil
-        # execute the corresponding handler:
-        while c.code[pc2].opcode == opcExcept: inc pc2
-        discard f.safePoints.pop
-        return (pc2, f)
-      inc pc2
-      if c.code[pc2].opcode != opcExcept and nextExceptOrFinally >= 0:
-        # we're at the end of the *except list*, but maybe there is another
-        # *except branch*?
-        pc2 = nextExceptOrFinally+1
-        if c.code[pc2].opcode == opcExcept:
-          nextExceptOrFinally = pc2 + c.code[pc2].regBx - wordExcess
-
-    if nextExceptOrFinally >= 0:
-      pc2 = nextExceptOrFinally
-    if c.code[pc2].opcode == opcFinally:
-      # execute the corresponding handler, but don't quit walking the stack:
-      discard f.safePoints.pop
-      return (pc2+1, f)
-    # not the right one:
-    discard f.safePoints.pop
+  discard f.safePoints.pop()
+
+type
+  ExceptionGoto = enum
+    ExceptionGotoHandler,
+    ExceptionGotoFinally,
+    ExceptionGotoUnhandled
+
+proc findExceptionHandler(c: PCtx, f: PStackFrame, exc: PNode):
+    tuple[why: ExceptionGoto, where: int] =
+  let raisedType = exc.typ.skipTypes(abstractPtrs)
+
+  while f.safePoints.len > 0:
+    var pc = f.safePoints.pop()
+
+    var matched = false
+    var pcEndExcept = pc
+
+    # Scan the chain of exceptions starting at pc.
+    # The structure is the following:
+    # pc - opcExcept, <end of this block>
+    #      - opcExcept, <pattern1>
+    #      - opcExcept, <pattern2>
+    #        ...
+    #      - opcExcept, <patternN>
+    #      - Exception handler body
+    #    - ... more opcExcept blocks may follow
+    #    - ... an optional opcFinally block may follow
+    #
+    # Note that the exception handler body already contains a jump to the
+    # finally block or, if that's not present, to the point where the execution
+    # should continue.
+    # Also note that opcFinally blocks are the last in the chain.
+    while c.code[pc].opcode == opcExcept:
+      # Where this Except block ends
+      pcEndExcept = pc + c.code[pc].regBx - wordExcess
+      inc pc
+
+      # A series of opcExcept follows for each exception type matched
+      while c.code[pc].opcode == opcExcept:
+        let excIndex = c.code[pc].regBx - wordExcess
+        let exceptType =
+          if excIndex > 0: c.types[excIndex].skipTypes(abstractPtrs)
+          else: nil
+
+        # echo typeToString(exceptType), " ", typeToString(raisedType)
+
+        # Determine if the exception type matches the pattern
+        if exceptType.isNil or inheritanceDiff(raisedType, exceptType) <= 0:
+          matched = true
+          break
+
+        inc pc
+
+      # Skip any further ``except`` pattern and find the first instruction of
+      # the handler body
+      while c.code[pc].opcode == opcExcept:
+        inc pc
+
+      if matched:
+        break
+
+      # If no handler in this chain is able to catch this exception we check if
+      # the "parent" chains are able to. If this chain ends with a `finally`
+      # block we must execute it before continuing.
+      pc = pcEndExcept
+
+    # Where the handler body starts
+    let pcBody = pc
+
+    if matched:
+      return (ExceptionGotoHandler, pcBody)
+    elif c.code[pc].opcode == opcFinally:
+      # The +1 here is here because we don't want to execute it since we've
+      # already pop'd this statepoint from the stack.
+      return (ExceptionGotoFinally, pc + 1)
+
+  return (ExceptionGotoUnhandled, 0)
 
 proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int =
-  if f.safePoints.isNil: return -1
-  for s in f.safePoints:
-    var pc = s
+  # Walk up the chain of safepoints and return the PC of the first `finally`
+  # block we find or -1 if no such block is found.
+  # Note that the safepoint is removed once the function returns!
+  result = -1
+
+  # Traverse the stack starting from the end in order to execute the blocks in
+  # the intended order
+  for i in 1..f.safePoints.len:
+    var pc = f.safePoints[^i]
+    # Skip the `except` blocks
     while c.code[pc].opcode == opcExcept:
-      pc = pc + c.code[pc].regBx - wordExcess
+      pc += c.code[pc].regBx - wordExcess
     if c.code[pc].opcode == opcFinally:
-      return pc
-  return -1
+      discard f.safePoints.pop
+      return pc + 1
 
 proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool =
+  result = false
   if desttyp.kind == tyString:
-    if dest.kind != rkNode:
-      myreset(dest)
-      dest.kind = rkNode
+    dest.ensureKind(rkNode)
     dest.node = newNode(nkStrLit)
     let styp = srctyp.skipTypes(abstractRange)
     case styp.kind
     of tyEnum:
       let n = styp.n
       let x = src.intVal.int
-      if x <% n.len and (let f = n.sons[x].sym; f.position == x):
+      if x <% n.len and (let f = n[x].sym; f.position == x):
         dest.node.strVal = if f.ast.isNil: f.name.s else: f.ast.strVal
       else:
         for i in 0..<n.len:
-          if n.sons[i].kind != nkSym: internalError(c.config, "opConv for enum")
-          let f = n.sons[i].sym
+          if n[i].kind != nkSym: internalError(c.config, "opConv for enum")
+          let f = n[i].sym
           if f.position == x:
             dest.node.strVal = if f.ast.isNil: f.name.s else: f.ast.strVal
             return
@@ -345,7 +410,7 @@ proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType):
       dest.node.strVal = $src.floatVal
     of tyString:
       dest.node.strVal = src.node.strVal
-    of tyCString:
+    of tyCstring:
       if src.node.kind == nkBracket:
         # Array of chars
         var strVal = ""
@@ -361,40 +426,57 @@ proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType):
     else:
       internalError(c.config, "cannot convert to string " & desttyp.typeToString)
   else:
-    case skipTypes(desttyp, abstractRange).kind
+    let desttyp = skipTypes(desttyp, abstractVarRange)
+    case desttyp.kind
     of tyInt..tyInt64:
-      if dest.kind != rkInt:
-        myreset(dest); dest.kind = rkInt
+      dest.ensureKind(rkInt)
       case skipTypes(srctyp, abstractRange).kind
       of tyFloat..tyFloat64:
         dest.intVal = int(src.floatVal)
       else:
         dest.intVal = src.intVal
-      if dest.intVal < firstOrd(c.config, desttyp) or dest.intVal > lastOrd(c.config, desttyp):
+      if toInt128(dest.intVal) < firstOrd(c.config, desttyp) or toInt128(dest.intVal) > lastOrd(c.config, desttyp):
         return true
     of tyUInt..tyUInt64:
-      if dest.kind != rkInt:
-        myreset(dest); dest.kind = rkInt
-      case skipTypes(srctyp, abstractRange).kind
+      dest.ensureKind(rkInt)
+      let styp = srctyp.skipTypes(abstractRange) # skip distinct types(dest type could do this too if needed)
+      case styp.kind
       of tyFloat..tyFloat64:
         dest.intVal = int(src.floatVal)
       else:
-        let srcDist = (sizeof(src.intVal) - srctyp.size) * 8
-        let destDist = (sizeof(dest.intVal) - desttyp.size) * 8
-        when system.cpuEndian == bigEndian:
-          dest.intVal = (src.intVal shr srcDist) shl srcDist
-          dest.intVal = (dest.intVal shr destDist) shl destDist
-        else:
-          dest.intVal = (src.intVal shl srcDist) shr srcDist
-          dest.intVal = (dest.intVal shl destDist) shr destDist
+        let destSize = getSize(c.config, desttyp)
+        let destDist = (sizeof(dest.intVal) - destSize) * 8
+        var value = cast[BiggestUInt](src.intVal)
+        when false:
+          # this would make uint64(-5'i8) evaluate to 251
+          # but at runtime, uint64(-5'i8) is 18446744073709551611
+          # so don't do it
+          let srcSize = getSize(c.config, styp)
+          let srcDist = (sizeof(src.intVal) - srcSize) * 8
+          value = (value shl srcDist) shr srcDist
+        value = (value shl destDist) shr destDist
+        dest.intVal = cast[BiggestInt](value)
+    of tyBool:
+      dest.ensureKind(rkInt)
+      dest.intVal =
+        case skipTypes(srctyp, abstractRange).kind
+          of tyFloat..tyFloat64: int(src.floatVal != 0.0)
+          else: int(src.intVal != 0)
     of tyFloat..tyFloat64:
-      if dest.kind != rkFloat:
-        myreset(dest); dest.kind = rkFloat
-      case skipTypes(srctyp, abstractRange).kind
+      dest.ensureKind(rkFloat)
+      let srcKind = skipTypes(srctyp, abstractRange).kind
+      case srcKind
       of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyBool, tyChar:
         dest.floatVal = toBiggestFloat(src.intVal)
+      elif src.kind == rkInt:
+        dest.floatVal = toBiggestFloat(src.intVal)
       else:
         dest.floatVal = src.floatVal
+    of tyObject:
+      if srctyp.skipTypes(abstractVarRange).kind != tyObject:
+        internalError(c.config, "invalid object-to-object conversion")
+      # A object-to-object conversion is essentially a no-op
+      moveConst(dest, src)
     else:
       asgnComplex(dest, src)
 
@@ -406,198 +488,446 @@ proc compile(c: PCtx, s: PSym): int =
 template handleJmpBack() {.dirty.} =
   if c.loopIterations <= 0:
     if allowInfiniteLoops in c.features:
-      c.loopIterations = MaxLoopIterations
+      c.loopIterations = c.config.maxLoopIterationsVM
     else:
-      msgWriteln(c.config, "stack trace: (most recent call last)")
+      msgWriteln(c.config, "stack trace: (most recent call last)", {msgNoUnitSep})
       stackTraceAux(c, tos, pc)
-      globalError(c.config, c.debug[pc], errTooManyIterations)
+      globalError(c.config, c.debug[pc], errTooManyIterations % $c.config.maxLoopIterationsVM)
   dec(c.loopIterations)
 
 proc recSetFlagIsRef(arg: PNode) =
-  arg.flags.incl(nfIsRef)
-  for i in 0 ..< arg.safeLen:
-    arg.sons[i].recSetFlagIsRef
+  if arg.kind notin {nkStrLit..nkTripleStrLit}:
+    arg.flags.incl(nfIsRef)
+  for i in 0..<arg.safeLen:
+    arg[i].recSetFlagIsRef
 
 proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) =
-  # FIXME: this doesn't attempt to solve incomplete
-  # support of tyPtr, tyRef in VM.
   let typ = node.typ.skipTypes(abstractInst+{tyRange}-{tyTypeDesc})
-  let typeEntry = typ.sons[0].skipTypes(abstractInst+{tyRange}-{tyTypeDesc})
-  let typeKind = case typeEntry.kind
-                 of tyUInt..tyUInt64: nkUIntLit
-                 of tyRange, tyEnum, tyBool, tyChar, tyInt..tyInt64: nkIntLit
-                 of tyFloat..tyFloat128: nkFloatLit
-                 of tyString: nkStrLit
-                 of tyObject: nkObjConstr
-                 of tySequence: nkNilLit
-                 of tyProc, tyTuple: nkTupleConstr
-                 else: nkEmpty
-
   let oldLen = node.len
   setLen(node.sons, newLen)
   if oldLen < newLen:
-    # TODO: This is still not correct for tyPtr, tyRef default value
-    for i in oldLen ..< newLen:
-      node.sons[i] = newNodeI(typeKind, info)
+    for i in oldLen..<newLen:
+      node[i] = getNullValue(c, typ.elementType, info, c.config)
 
 const
-  errIndexOutOfBounds = "index out of bounds"
   errNilAccess = "attempt to access a nil address"
   errOverOrUnderflow = "over- or underflow"
   errConstantDivisionByZero = "division by zero"
   errIllegalConvFromXtoY = "illegal conversion from '$1' to '$2'"
   errTooManyIterations = "interpretation requires too many iterations; " &
-    "if you are sure this is not a bug in your code edit " &
-    "compiler/vmdef.MaxLoopIterations and rebuild the compiler"
+    "if you are sure this is not a bug in your code, compile with `--maxLoopIterationsVM:number` (current value: $1)"
   errFieldXNotFound = "node lacks field: "
 
+
+template maybeHandlePtr(node2: PNode, reg: TFullReg, isAssign2: bool): bool =
+  let node = node2 # prevent double evaluation
+  if node.kind == nkNilLit:
+    stackTrace(c, tos, pc, errNilAccess)
+  let typ = node.typ
+  if nfIsPtr in node.flags or (typ != nil and typ.kind == tyPtr):
+    assert node.kind == nkIntLit, $(node.kind)
+    assert typ != nil
+    let typ2 = if typ.kind == tyPtr: typ.elementType else: typ
+    if not derefPtrToReg(node.intVal, typ2, reg, isAssign = isAssign2):
+      # tyObject not supported in this context
+      stackTrace(c, tos, pc, "deref unsupported ptr type: " & $(typeToString(typ), typ.kind))
+    true
+  else:
+    false
+
+template takeAddress(reg, source) =
+  reg.nodeAddr = addr source
+  GC_ref source
+
+proc takeCharAddress(c: PCtx, src: PNode, index: BiggestInt, pc: int): TFullReg =
+  let typ = newType(tyPtr, c.idgen, c.module.owner)
+  typ.add getSysType(c.graph, c.debug[pc], tyChar)
+  var node = newNodeIT(nkIntLit, c.debug[pc], typ) # xxx nkPtrLit
+  node.intVal = cast[int](src.strVal[index].addr)
+  node.flags.incl nfIsPtr
+  TFullReg(kind: rkNode, node: node)
+
+
 proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
+  result = TFullReg(kind: rkNone)
   var pc = start
   var tos = tos
-  var regs: seq[TFullReg] # alias to tos.slots for performance
-  move(regs, tos.slots)
+  # Used to keep track of where the execution is resumed.
+  var savedPC = -1
+  var savedFrame: PStackFrame = nil
+  when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
+    template updateRegsAlias = discard
+    template regs: untyped = tos.slots
+  else:
+    template updateRegsAlias =
+      move(regs, tos.slots)
+    var regs: seq[TFullReg] # alias to tos.slots for performance
+    updateRegsAlias
   #echo "NEW RUN ------------------------"
   while true:
     #{.computedGoto.}
     let instr = c.code[pc]
     let ra = instr.regA
-    #if c.traceActive:
-    when traceCode:
-      echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra, " rb ", instr.regB, " rc ", instr.regC
-    #  message(c.config, c.debug[pc], warnUser, "Trace")
 
+    when traceCode:
+      template regDescr(name, r): string =
+        let kind = if r < regs.len: $regs[r].kind else: ""
+        let ret = name & ": " & $r & " " & $kind
+        alignLeft(ret, 15)
+      echo "PC:$pc $opcode $ra $rb $rc" % [
+        "pc", $pc, "opcode", alignLeft($c.code[pc].opcode, 15),
+        "ra", regDescr("ra", ra), "rb", regDescr("rb", instr.regB),
+        "rc", regDescr("rc", instr.regC)]
+    if c.config.isVmTrace:
+      # unlike nimVMDebug, this doesn't require re-compiling nim and is controlled by user code
+      let info = c.debug[pc]
+      # other useful variables: c.loopIterations
+      echo "$# [$#] $#" % [c.config$info, $instr.opcode, c.config.sourceLine(info)]
+    c.profiler.enter(c, tos)
     case instr.opcode
     of opcEof: return regs[ra]
     of opcRet:
-      # XXX perform any cleanup actions
-      pc = tos.comesFrom
-      tos = tos.next
-      let retVal = regs[0]
-      if tos.isNil:
-        #echo "RET ", retVal.rendertree
-        return retVal
-
-      move(regs, tos.slots)
-      assert c.code[pc].opcode in {opcIndCall, opcIndCallAsgn}
-      if c.code[pc].opcode == opcIndCallAsgn:
-        regs[c.code[pc].regA] = retVal
-        #echo "RET2 ", retVal.rendertree, " ", c.code[pc].regA
+      let newPc = c.cleanUpOnReturn(tos)
+      # Perform any cleanup action before returning
+      if newPc < 0:
+        pc = tos.comesFrom
+        let retVal = regs[0]
+        tos = tos.next
+        if tos.isNil:
+          return retVal
+
+        updateRegsAlias
+        assert c.code[pc].opcode in {opcIndCall, opcIndCallAsgn}
+        if c.code[pc].opcode == opcIndCallAsgn:
+          regs[c.code[pc].regA] = retVal
+      else:
+        savedPC = pc
+        savedFrame = tos
+        # The -1 is needed because at the end of the loop we increment `pc`
+        pc = newPc - 1
     of opcYldYoid: assert false
     of opcYldVal: assert false
     of opcAsgnInt:
       decodeB(rkInt)
-      regs[ra].intVal = regs[rb].intVal
-    of opcAsgnStr:
-      decodeBC(rkNode)
-      createStrKeepNode regs[ra], rc != 0
-      regs[ra].node.strVal = regs[rb].node.strVal
+      if regs[rb].kind == rkInt:
+        regs[ra].intVal = regs[rb].intVal
+      else:
+        stackTrace(c, tos, pc, "opcAsgnInt: got " & $regs[rb].kind)
     of opcAsgnFloat:
       decodeB(rkFloat)
       regs[ra].floatVal = regs[rb].floatVal
+    of opcCastFloatToInt32:
+      let rb = instr.regB
+      ensureKind(rkInt)
+      regs[ra].intVal = cast[int32](float32(regs[rb].floatVal))
+    of opcCastFloatToInt64:
+      let rb = instr.regB
+      ensureKind(rkInt)
+      regs[ra].intVal = cast[int64](regs[rb].floatVal)
+    of opcCastIntToFloat32:
+      let rb = instr.regB
+      ensureKind(rkFloat)
+      regs[ra].floatVal = cast[float32](regs[rb].intVal)
+    of opcCastIntToFloat64:
+      let rb = instr.regB
+      ensureKind(rkFloat)
+      regs[ra].floatVal = cast[float64](regs[rb].intVal)
+
+    of opcCastPtrToInt: # RENAME opcCastPtrOrRefToInt
+      decodeBImm(rkInt)
+      case imm
+      of 1: # PtrLikeKinds
+        case regs[rb].kind
+        of rkNode:
+          regs[ra].intVal = cast[int](regs[rb].node.intVal)
+        of rkNodeAddr:
+          regs[ra].intVal = cast[int](regs[rb].nodeAddr)
+        of rkRegisterAddr:
+          regs[ra].intVal = cast[int](regs[rb].regAddr)
+        of rkInt:
+          regs[ra].intVal = regs[rb].intVal
+        else:
+          stackTrace(c, tos, pc, "opcCastPtrToInt: got " & $regs[rb].kind)
+      of 2: # tyRef
+        regs[ra].intVal = cast[int](regs[rb].node)
+      else: assert false, $imm
+    of opcCastIntToPtr:
+      let rb = instr.regB
+      let typ = regs[ra].node.typ
+      let node2 = newNodeIT(nkIntLit, c.debug[pc], typ)
+      case regs[rb].kind
+      of rkInt: node2.intVal = regs[rb].intVal
+      of rkNode:
+        if regs[rb].node.typ.kind notin PtrLikeKinds:
+          stackTrace(c, tos, pc, "opcCastIntToPtr: regs[rb].node.typ: " & $regs[rb].node.typ.kind)
+        node2.intVal = regs[rb].node.intVal
+      else: stackTrace(c, tos, pc, "opcCastIntToPtr: regs[rb].kind: " & $regs[rb].kind)
+      regs[ra].node = node2
     of opcAsgnComplex:
       asgnComplex(regs[ra], regs[instr.regB])
+    of opcFastAsgnComplex:
+      fastAsgnComplex(regs[ra], regs[instr.regB])
     of opcAsgnRef:
       asgnRef(regs[ra], regs[instr.regB])
-    of opcRegToNode:
-      decodeB(rkNode)
-      putIntoNode(regs[ra].node, regs[rb])
     of opcNodeToReg:
       let ra = instr.regA
       let rb = instr.regB
-      # opcDeref might already have loaded it into a register. XXX Let's hope
+      # opcLdDeref might already have loaded it into a register. XXX Let's hope
       # this is still correct this way:
       if regs[rb].kind != rkNode:
         regs[ra] = regs[rb]
       else:
         assert regs[rb].kind == rkNode
         let nb = regs[rb].node
-        case nb.kind
-        of nkCharLit..nkUInt64Lit:
-          ensureKind(rkInt)
-          regs[ra].intVal = nb.intVal
-        of nkFloatLit..nkFloat64Lit:
-          ensureKind(rkFloat)
-          regs[ra].floatVal = nb.floatVal
+        if nb == nil:
+          stackTrace(c, tos, pc, errNilAccess)
         else:
-          ensureKind(rkNode)
-          regs[ra].node = nb
+          case nb.kind
+          of nkCharLit..nkUInt64Lit:
+            ensureKind(rkInt)
+            regs[ra].intVal = nb.intVal
+          of nkFloatLit..nkFloat64Lit:
+            ensureKind(rkFloat)
+            regs[ra].floatVal = nb.floatVal
+          else:
+            ensureKind(rkNode)
+            regs[ra].node = nb
+    of opcSlice:
+      # A bodge, but this takes in `toOpenArray(rb, rc, rc)` and emits
+      # nkTupleConstr(x, y, z) into the `regs[ra]`. These can later be used for calculating the slice we have taken.
+      decodeBC(rkNode)
+      let
+        collection = regs[ra].node
+        leftInd = regs[rb].intVal
+        rightInd = regs[rc].intVal
+
+      proc rangeCheck(left, right: BiggestInt, safeLen: BiggestInt) =
+        if left < 0:
+          stackTrace(c, tos, pc, formatErrorIndexBound(left, safeLen))
+
+        if right > safeLen:
+          stackTrace(c, tos, pc, formatErrorIndexBound(right, safeLen))
+
+      case collection.kind
+      of nkTupleConstr: # slice of a slice
+        let safeLen = collection[2].intVal - collection[1].intVal
+        rangeCheck(leftInd, rightInd, safeLen)
+        let
+          leftInd = leftInd + collection[1].intVal # Slice is from the start of the old
+          rightInd = rightInd + collection[1].intVal
+
+        regs[ra].node = newTree(
+          nkTupleConstr,
+          collection[0],
+          newIntNode(nkIntLit, BiggestInt leftInd),
+          newIntNode(nkIntLit, BiggestInt rightInd)
+        )
+
+      else:
+        let safeLen = safeArrLen(collection) - 1
+        rangeCheck(leftInd, rightInd, safeLen)
+        regs[ra].node = newTree(
+          nkTupleConstr,
+          collection,
+          newIntNode(nkIntLit, BiggestInt leftInd),
+          newIntNode(nkIntLit, BiggestInt rightInd)
+        )
+
+
     of opcLdArr:
       # a = b[c]
       decodeBC(rkNode)
       if regs[rc].intVal > high(int):
-        stackTrace(c, tos, pc, errIndexOutOfBounds)
+        stackTrace(c, tos, pc, formatErrorIndexBound(regs[rc].intVal, high(int)))
       let idx = regs[rc].intVal.int
       let src = regs[rb].node
-      if src.kind in {nkStrLit..nkTripleStrLit}:
+      case src.kind
+      of nkTupleConstr: # refer to `of opcSlice`
+        let
+          left = src[1].intVal
+          right = src[2].intVal
+          realIndex = left + idx
+        if idx in 0..(right - left):
+          case src[0].kind
+          of nkStrKinds:
+            regs[ra].node =  newIntNode(nkCharLit, ord src[0].strVal[int realIndex])
+          of nkBracket:
+            regs[ra].node = src[0][int realIndex]
+          else:
+            stackTrace(c, tos, pc, "opcLdArr internal error")
+        else:
+          stackTrace(c, tos, pc, formatErrorIndexBound(idx, int right))
+
+      of nkStrLit..nkTripleStrLit:
         if idx <% src.strVal.len:
           regs[ra].node = newNodeI(nkCharLit, c.debug[pc])
           regs[ra].node.intVal = src.strVal[idx].ord
         else:
-          stackTrace(c, tos, pc, errIndexOutOfBounds)
+          stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.strVal.len-1))
       elif src.kind notin {nkEmpty..nkFloat128Lit} and idx <% src.len:
-        regs[ra].node = src.sons[idx]
+        regs[ra].node = src[idx]
       else:
-        stackTrace(c, tos, pc, errIndexOutOfBounds)
+        stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.safeLen-1))
+    of opcLdArrAddr:
+      # a = addr(b[c])
+      decodeBC(rkNodeAddr)
+      if regs[rc].intVal > high(int):
+        stackTrace(c, tos, pc, formatErrorIndexBound(regs[rc].intVal, high(int)))
+      let idx = regs[rc].intVal.int
+      let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
+      case src.kind
+      of nkTupleConstr:
+        let
+          left = src[1].intVal
+          right = src[2].intVal
+          realIndex = left + idx
+        if idx in 0..(right - left): # Refer to `opcSlice`
+          case src[0].kind
+          of nkStrKinds:
+            regs[ra] = takeCharAddress(c, src[0], realIndex, pc)
+          of nkBracket:
+            takeAddress regs[ra], src.sons[0].sons[realIndex]
+          else:
+            stackTrace(c, tos, pc, "opcLdArrAddr internal error")
+        else:
+          stackTrace(c, tos, pc, formatErrorIndexBound(idx, int right))
+      else:
+        if src.kind notin {nkEmpty..nkTripleStrLit} and idx <% src.len:
+          takeAddress regs[ra], src.sons[idx]
+        elif src.kind in nkStrKinds and idx <% src.strVal.len:
+          regs[ra] = takeCharAddress(c, src, idx, pc)
+        else:
+          stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.safeLen-1))
     of opcLdStrIdx:
       decodeBC(rkInt)
       let idx = regs[rc].intVal.int
-      let s = regs[rb].node.strVal
-      if s.isNil:
-        stackTrace(c, tos, pc, errNilAccess)
-      elif idx <=% s.len:
+      let s {.cursor.} = regs[rb].node.strVal
+      if idx <% s.len:
         regs[ra].intVal = s[idx].ord
       else:
-        stackTrace(c, tos, pc, errIndexOutOfBounds)
+        stackTrace(c, tos, pc, formatErrorIndexBound(idx, s.len-1))
+    of opcLdStrIdxAddr:
+      # a = addr(b[c]); similar to opcLdArrAddr
+      decodeBC(rkNode)
+      if regs[rc].intVal > high(int):
+        stackTrace(c, tos, pc, formatErrorIndexBound(regs[rc].intVal, high(int)))
+      let idx = regs[rc].intVal.int
+      let s = regs[rb].node.strVal.addr # or `byaddr`
+      if idx <% s[].len:
+        regs[ra] = takeCharAddress(c, regs[rb].node, idx, pc)
+      else:
+        stackTrace(c, tos, pc, formatErrorIndexBound(idx, s[].len-1))
     of opcWrArr:
       # a[b] = c
       decodeBC(rkNode)
       let idx = regs[rb].intVal.int
+      assert regs[ra].kind == rkNode
       let arr = regs[ra].node
-      if arr.kind in {nkStrLit..nkTripleStrLit}:
+      case arr.kind
+      of nkTupleConstr: # refer to `opcSlice`
+        let
+          src = arr[0]
+          left = arr[1].intVal
+          right = arr[2].intVal
+          realIndex = left + idx
+        if idx in 0..(right - left):
+          case src.kind
+          of nkStrKinds:
+            src.strVal[int(realIndex)] = char(regs[rc].intVal)
+          of nkBracket:
+            if regs[rc].kind == rkInt:
+              src[int(realIndex)] = newIntNode(nkIntLit, regs[rc].intVal)
+            else:
+              assert regs[rc].kind == rkNode
+              src[int(realIndex)] = regs[rc].node
+          else:
+            stackTrace(c, tos, pc, "opcWrArr internal error")
+        else:
+          stackTrace(c, tos, pc, formatErrorIndexBound(idx, int right))
+      of {nkStrLit..nkTripleStrLit}:
         if idx <% arr.strVal.len:
           arr.strVal[idx] = chr(regs[rc].intVal)
         else:
-          stackTrace(c, tos, pc, errIndexOutOfBounds)
+          stackTrace(c, tos, pc, formatErrorIndexBound(idx, arr.strVal.len-1))
       elif idx <% arr.len:
-        putIntoNode(arr.sons[idx], regs[rc])
+        writeField(arr[idx], regs[rc])
       else:
-        stackTrace(c, tos, pc, errIndexOutOfBounds)
+        stackTrace(c, tos, pc, formatErrorIndexBound(idx, arr.safeLen-1))
     of opcLdObj:
       # a = b.c
       decodeBC(rkNode)
-      let src = regs[rb].node
-      if src.kind notin {nkEmpty..nkNilLit}:
-        let n = src.sons[rc + ord(src.kind == nkObjConstr)].skipColon
-        regs[ra].node = n
+      if rb >= regs.len or regs[rb].kind == rkNone or 
+        (regs[rb].kind == rkNode and regs[rb].node == nil) or
+        (regs[rb].kind == rkNodeAddr and regs[rb].nodeAddr[] == nil): 
+        stackTrace(c, tos, pc, errNilAccess)
       else:
+        let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
+        case src.kind
+        of nkEmpty..nkNilLit:
+          # for nkPtrLit, this could be supported in the future, use something like:
+          # derefPtrToReg(src.intVal + offsetof(src.typ, rc), typ_field, regs[ra], isAssign = false)
+          # where we compute the offset in bytes for field rc
+          stackTrace(c, tos, pc, errNilAccess & " " & $("kind", src.kind, "typ", typeToString(src.typ), "rc", rc))
+        of nkObjConstr:
+          let n = src[rc + 1].skipColon
+          regs[ra].node = n
+        of nkTupleConstr:
+          let n = if src.typ != nil and tfTriggersCompileTime in src.typ.flags:
+              src[rc]
+            else:
+              src[rc].skipColon
+          regs[ra].node = n
+        else:
+          let n = src[rc]
+          regs[ra].node = n
+    of opcLdObjAddr:
+      # a = addr(b.c)
+      decodeBC(rkNodeAddr)
+      let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
+      case src.kind
+      of nkEmpty..nkNilLit:
         stackTrace(c, tos, pc, errNilAccess)
+      of nkObjConstr:
+        let n = src.sons[rc + 1]
+        if n.kind == nkExprColonExpr:
+          takeAddress regs[ra], n.sons[1]
+        else:
+          takeAddress regs[ra], src.sons[rc + 1]
+      else:
+        takeAddress regs[ra], src.sons[rc]
     of opcWrObj:
       # a.b = c
       decodeBC(rkNode)
+      assert regs[ra].node != nil
       let shiftedRb = rb + ord(regs[ra].node.kind == nkObjConstr)
       let dest = regs[ra].node
       if dest.kind == nkNilLit:
         stackTrace(c, tos, pc, errNilAccess)
-      elif dest.sons[shiftedRb].kind == nkExprColonExpr:
-        putIntoNode(dest.sons[shiftedRb].sons[1], regs[rc])
+      elif dest[shiftedRb].kind == nkExprColonExpr:
+        writeField(dest[shiftedRb][1], regs[rc])
+        dest[shiftedRb][1].flags.incl nfSkipFieldChecking
       else:
-        putIntoNode(dest.sons[shiftedRb], regs[rc])
+        writeField(dest[shiftedRb], regs[rc])
+        dest[shiftedRb].flags.incl nfSkipFieldChecking
     of opcWrStrIdx:
       decodeBC(rkNode)
       let idx = regs[rb].intVal.int
       if idx <% regs[ra].node.strVal.len:
         regs[ra].node.strVal[idx] = chr(regs[rc].intVal)
       else:
-        stackTrace(c, tos, pc, errIndexOutOfBounds)
+        stackTrace(c, tos, pc, formatErrorIndexBound(idx, regs[ra].node.strVal.len-1))
     of opcAddrReg:
       decodeB(rkRegisterAddr)
       regs[ra].regAddr = addr(regs[rb])
     of opcAddrNode:
       decodeB(rkNodeAddr)
-      if regs[rb].kind == rkNode:
-        regs[ra].nodeAddr = addr(regs[rb].node)
+      case regs[rb].kind
+      of rkNode:
+        takeAddress regs[ra], regs[rb].node
+      of rkNodeAddr: # bug #14339
+        regs[ra].nodeAddr = regs[rb].nodeAddr
       else:
-        stackTrace(c, tos, pc, "limited VM support for 'addr'")
+        stackTrace(c, tos, pc, "limited VM support for 'addr', got kind: " & $regs[rb].kind)
     of opcLdDeref:
       # a = b[]
       let ra = instr.regA
@@ -610,23 +940,35 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         ensureKind(regs[rb].regAddr.kind)
         regs[ra] = regs[rb].regAddr[]
       of rkNode:
-        if regs[rb].node.kind == nkNilLit:
-          stackTrace(c, tos, pc, errNilAccess)
         if regs[rb].node.kind == nkRefTy:
-          regs[ra].node = regs[rb].node.sons[0]
-        else:
+          regs[ra].node = regs[rb].node[0]
+        elif not maybeHandlePtr(regs[rb].node, regs[ra], false):
+          ## e.g.: typ.kind = tyObject
           ensureKind(rkNode)
           regs[ra].node = regs[rb].node
       else:
-        stackTrace(c, tos, pc, errNilAccess)
+        stackTrace(c, tos, pc, errNilAccess & " kind: " & $regs[rb].kind)
     of opcWrDeref:
       # a[] = c; b unused
       let ra = instr.regA
       let rc = instr.regC
       case regs[ra].kind
-      of rkNodeAddr: putIntoNode(regs[ra].nodeAddr[], regs[rc])
+      of rkNodeAddr:
+        let n = regs[rc].regToNode
+        # `var object` parameters are sent as rkNodeAddr. When they are mutated
+        # vmgen generates opcWrDeref, which means that we must dereference
+        # twice.
+        # TODO: This should likely be handled differently in vmgen.
+        let nAddr = regs[ra].nodeAddr
+        if nAddr[] == nil: stackTrace(c, tos, pc, "opcWrDeref internal error") # refs bug #16613
+        if (nfIsRef notin nAddr[].flags and nfIsRef notin n.flags): nAddr[][] = n[]
+        else: nAddr[] = n
       of rkRegisterAddr: regs[ra].regAddr[] = regs[rc]
-      of rkNode: putIntoNode(regs[ra].node, regs[rc])
+      of rkNode:
+         # xxx: also check for nkRefTy as in opcLdDeref?
+        if not maybeHandlePtr(regs[ra].node, regs[rc], true):
+          regs[ra].node[] = regs[rc].regToNode[]
+          regs[ra].node.flags.incl nfIsRef
       else: stackTrace(c, tos, pc, errNilAccess)
     of opcAddInt:
       decodeBC(rkInt)
@@ -673,36 +1015,50 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcLenSeq:
       decodeBImm(rkInt)
       #assert regs[rb].kind == nkBracket
-      let high = (imm and 1) # discard flags
+      let
+        high = (imm and 1) # discard flags
+        node = regs[rb].node
       if (imm and nimNodeFlag) != 0:
         # used by mNLen (NimNode.len)
         regs[ra].intVal = regs[rb].node.safeLen - high
       else:
-        # safeArrLen also return string node len
-        # used when string is passed as openArray in VM
-        regs[ra].intVal = regs[rb].node.safeArrLen - high
+        case node.kind
+        of nkTupleConstr: # refer to `of opcSlice`
+          regs[ra].intVal = node[2].intVal - node[1].intVal + 1 - high
+        else:
+          # safeArrLen also return string node len
+          # used when string is passed as openArray in VM
+          regs[ra].intVal = node.safeArrLen - high
+
     of opcLenStr:
       decodeBImm(rkInt)
       assert regs[rb].kind == rkNode
       regs[ra].intVal = regs[rb].node.strVal.len - imm
+    of opcLenCstring:
+      decodeBImm(rkInt)
+      assert regs[rb].kind == rkNode
+      if regs[rb].node.kind == nkNilLit:
+        regs[ra].intVal = -imm
+      else:
+        regs[ra].intVal = regs[rb].node.strVal.cstring.len - imm
     of opcIncl:
       decodeB(rkNode)
       let b = regs[rb].regToNode
       if not inSet(regs[ra].node, b):
-        addSon(regs[ra].node, copyTree(b))
+        regs[ra].node.add copyTree(b)
     of opcInclRange:
       decodeBC(rkNode)
       var r = newNode(nkRange)
       r.add regs[rb].regToNode
       r.add regs[rc].regToNode
-      addSon(regs[ra].node, r.copyTree)
+      regs[ra].node.add r.copyTree
     of opcExcl:
       decodeB(rkNode)
       var b = newNodeIT(nkCurly, regs[ra].node.info, regs[ra].node.typ)
-      addSon(b, regs[rb].regToNode)
+      b.add regs[rb].regToNode
       var r = diffSets(c.config, regs[ra].node, b)
       discardSons(regs[ra].node)
-      for i in countup(0, sonsLen(r) - 1): addSon(regs[ra].node, r.sons[i])
+      for i in 0..<r.len: regs[ra].node.add r[i]
     of opcCard:
       decodeB(rkInt)
       regs[ra].intVal = nimsets.cardSet(c.config, regs[rb].node)
@@ -742,10 +1098,16 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       regs[ra].floatVal = regs[rb].floatVal / regs[rc].floatVal
     of opcShrInt:
       decodeBC(rkInt)
-      regs[ra].intVal = regs[rb].intVal shr regs[rc].intVal
+      let b = cast[uint64](regs[rb].intVal)
+      let c = cast[uint64](regs[rc].intVal)
+      let a = cast[int64](b shr c)
+      regs[ra].intVal = a
     of opcShlInt:
       decodeBC(rkInt)
       regs[ra].intVal = regs[rb].intVal shl regs[rc].intVal
+    of opcAshrInt:
+      decodeBC(rkInt)
+      regs[ra].intVal = ashr(regs[rb].intVal, regs[rc].intVal)
     of opcBitandInt:
       decodeBC(rkInt)
       regs[ra].intVal = regs[rb].intVal and regs[rc].intVal
@@ -795,30 +1157,61 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       decodeBC(rkInt)
       regs[ra].intVal = ord(regs[rb].intVal <% regs[rc].intVal)
     of opcEqRef:
+      var ret = false
       decodeBC(rkInt)
-      if regs[rb].kind == rkNodeAddr:
-        if regs[rc].kind == rkNodeAddr:
-          regs[ra].intVal = ord(regs[rb].nodeAddr == regs[rc].nodeAddr)
+      template getTyp(n): untyped =
+        n.typ.skipTypes(abstractInst)
+      template skipRegisterAddr(n: TFullReg): TFullReg =
+        var tmp = n
+        while tmp.kind == rkRegisterAddr:
+          tmp = tmp.regAddr[]
+        tmp
+
+      proc ptrEquality(n1: ptr PNode, n2: PNode): bool =
+        ## true if n2.intVal represents a ptr equal to n1
+        let p1 = cast[int](n1)
+        case n2.kind
+        of nkNilLit: return p1 == 0
+        of nkIntLit: # TODO: nkPtrLit
+          # for example, n1.kind == nkFloatLit (ptr float)
+          # the problem is that n1.typ == nil so we can't compare n1.typ and n2.typ
+          # this is the best we can do (pending making sure we assign a valid n1.typ to nodeAddr's)
+          let t2 = n2.getTyp
+          return t2.kind in PtrLikeKinds and n2.intVal == p1
+        else: return false
+
+      let rbReg = skipRegisterAddr(regs[rb])
+      let rcReg = skipRegisterAddr(regs[rc])
+
+      if rbReg.kind == rkNodeAddr:
+        if rcReg.kind == rkNodeAddr:
+          ret = rbReg.nodeAddr == rcReg.nodeAddr
         else:
-          assert regs[rc].kind == rkNode
-          # we know these cannot be equal
-          regs[ra].intVal = ord(false)
-      elif regs[rc].kind == rkNodeAddr:
-        assert regs[rb].kind == rkNode
-        # we know these cannot be equal
-        regs[ra].intVal = ord(false)
+          ret = ptrEquality(rbReg.nodeAddr, rcReg.node)
+      elif rcReg.kind == rkNodeAddr:
+        ret = ptrEquality(rcReg.nodeAddr, rbReg.node)
       else:
-        regs[ra].intVal = ord((regs[rb].node.kind == nkNilLit and
-                              regs[rc].node.kind == nkNilLit) or
-                              regs[rb].node == regs[rc].node)
-    of opcEqNimrodNode:
+        let nb = rbReg.node
+        let nc = rcReg.node
+        if nb.kind != nc.kind: discard
+        elif (nb == nc) or (nb.kind == nkNilLit): ret = true # intentional
+        elif nb.kind in {nkSym, nkTupleConstr, nkClosure} and nb.typ != nil and nb.typ.kind == tyProc and sameConstant(nb, nc):
+          ret = true
+          # this also takes care of procvar's, represented as nkTupleConstr, e.g. (nil, nil)
+        elif nb.kind == nkIntLit and nc.kind == nkIntLit and nb.intVal == nc.intVal: # TODO: nkPtrLit
+          let tb = nb.getTyp
+          let tc = nc.getTyp
+          ret = tb.kind in PtrLikeKinds and tc.kind == tb.kind
+      regs[ra].intVal = ord(ret)
+    of opcEqNimNode:
       decodeBC(rkInt)
       regs[ra].intVal =
         ord(exprStructuralEquivalent(regs[rb].node, regs[rc].node,
                                      strictSymEquality=true))
     of opcSameNodeType:
       decodeBC(rkInt)
-      regs[ra].intVal = ord(regs[rb].node.typ.sameTypeOrNil regs[rc].node.typ)
+      regs[ra].intVal = ord(regs[rb].node.typ.sameTypeOrNil(regs[rc].node.typ, {ExactTypeDescValues, ExactGenericParams}))
+      # The types should exactly match which is why we pass `{ExactTypeDescValues..ExactGcSafety}`.
     of opcXor:
       decodeBC(rkInt)
       regs[ra].intVal = ord(regs[rb].intVal != regs[rc].intVal)
@@ -845,6 +1238,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcEqStr:
       decodeBC(rkInt)
       regs[ra].intVal = ord(regs[rb].node.strVal == regs[rc].node.strVal)
+    of opcEqCString:
+      decodeBC(rkInt)
+      let bNil = regs[rb].node.kind == nkNilLit
+      let cNil = regs[rc].node.kind == nkNilLit
+      regs[ra].intVal = ord((bNil and cNil) or
+        (not bNil and not cNil and regs[rb].node.strVal == regs[rc].node.strVal))
     of opcLeStr:
       decodeBC(rkInt)
       regs[ra].intVal = ord(regs[rb].node.strVal <= regs[rc].node.strVal)
@@ -877,11 +1276,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       createSet(regs[ra])
       move(regs[ra].node.sons,
            nimsets.diffSets(c.config, regs[rb].node, regs[rc].node).sons)
-    of opcSymdiffSet:
-      decodeBC(rkNode)
-      createSet(regs[ra])
-      move(regs[ra].node.sons,
-           nimsets.symdiffSets(c.config, regs[rb].node, regs[rc].node).sons)
     of opcConcatStr:
       decodeBC(rkNode)
       createStr regs[ra]
@@ -890,11 +1284,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         regs[ra].node.strVal.add getstr(regs[i])
     of opcAddStrCh:
       decodeB(rkNode)
-      #createStrKeepNode regs[ra]
       regs[ra].node.strVal.add(regs[rb].intVal.chr)
     of opcAddStrStr:
       decodeB(rkNode)
-      #createStrKeepNode regs[ra]
       regs[ra].node.strVal.add(regs[rb].node.strVal)
     of opcAddSeqElem:
       decodeB(rkNode)
@@ -904,45 +1296,88 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         stackTrace(c, tos, pc, errNilAccess)
     of opcGetImpl:
       decodeB(rkNode)
-      let a = regs[rb].node
+      var a = regs[rb].node
+      if a.kind == nkVarTy: a = a[0]
       if a.kind == nkSym:
         regs[ra].node = if a.sym.ast.isNil: newNode(nkNilLit)
                         else: copyTree(a.sym.ast)
+        regs[ra].node.flags.incl nfIsRef
+      else:
+        stackTrace(c, tos, pc, "node is not a symbol")
+    of opcGetImplTransf:
+      decodeB(rkNode)
+      let a = regs[rb].node
+      if a.kind == nkSym:
+        regs[ra].node =
+          if a.sym.ast.isNil:
+            newNode(nkNilLit)
+          else:
+            let ast = a.sym.ast.shallowCopy
+            for i in 0..<a.sym.ast.len:
+              ast[i] = a.sym.ast[i]
+            ast[bodyPos] = transformBody(c.graph, c.idgen, a.sym, {useCache, force})
+            ast.copyTree()
+    of opcSymOwner:
+      decodeB(rkNode)
+      let a = regs[rb].node
+      if a.kind == nkSym:
+        regs[ra].node = if a.sym.owner.isNil: newNode(nkNilLit)
+                        else: newSymNode(a.sym.skipGenericOwner)
+        regs[ra].node.flags.incl nfIsRef
       else:
         stackTrace(c, tos, pc, "node is not a symbol")
+    of opcSymIsInstantiationOf:
+      decodeBC(rkInt)
+      let a = regs[rb].node
+      let b = regs[rc].node
+      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.instantiatedFrom == b.sym: 1
+          else: 0
+      else:
+        stackTrace(c, tos, pc, "node is not a proc symbol")
     of opcEcho:
       let rb = instr.regB
-      if rb == 1:
-        msgWriteln(c.config, regs[ra].node.strVal, {msgStdout})
+      template fn(s) = msgWriteln(c.config, s, {msgStdout, msgNoUnitSep})
+      if rb == 1: fn(regs[ra].node.strVal)
       else:
         var outp = ""
         for i in ra..ra+rb-1:
           #if regs[i].kind != rkNode: debug regs[i]
           outp.add(regs[i].node.strVal)
-        msgWriteln(c.config, outp, {msgStdout})
+        fn(outp)
     of opcContainsSet:
       decodeBC(rkInt)
       regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode))
-    of opcSubStr:
-      decodeBC(rkNode)
-      inc pc
-      assert c.code[pc].opcode == opcSubStr
-      let rd = c.code[pc].regA
-      createStr regs[ra]
-      regs[ra].node.strVal = substr(regs[rb].node.strVal,
-                                    regs[rc].intVal.int, regs[rd].intVal.int)
     of opcParseFloat:
       decodeBC(rkInt)
-      inc pc
-      assert c.code[pc].opcode == opcParseFloat
-      let rd = c.code[pc].regA
       var rcAddr = addr(regs[rc])
       if rcAddr.kind == rkRegisterAddr: rcAddr = rcAddr.regAddr
       elif regs[rc].kind != rkFloat:
-        myreset(regs[rc])
-        regs[rc].kind = rkFloat
-      regs[ra].intVal = parseBiggestFloat(regs[rb].node.strVal,
-                                          rcAddr.floatVal, regs[rd].intVal.int)
+        regs[rc] = TFullReg(kind: rkFloat)
+
+      let coll = regs[rb].node
+
+      case coll.kind
+      of nkTupleConstr:
+        let
+          data = coll[0]
+          left = coll[1].intVal
+          right = coll[2].intVal
+        case data.kind
+        of nkStrKinds:
+          regs[ra].intVal = parseBiggestFloat(data.strVal.toOpenArray(int left, int right), rcAddr.floatVal)
+        of nkBracket:
+          var s = newStringOfCap(right - left + 1)
+          for i in left..right:
+            s.add char data[int i].intVal
+          regs[ra].intVal = parseBiggestFloat(s, rcAddr.floatVal)
+        else:
+          internalError(c.config, c.debug[pc], "opcParseFloat: Incorrectly created openarray")
+      else:
+        regs[ra].intVal = parseBiggestFloat(regs[rb].node.strVal, rcAddr.floatVal)
+
     of opcRangeChck:
       let rb = instr.regB
       let rc = instr.regC
@@ -956,24 +1391,35 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       let rb = instr.regB
       let rc = instr.regC
       let bb = regs[rb].node
+      if bb.kind == nkNilLit:
+        stackTrace(c, tos, pc, "attempt to call nil closure")
       let isClosure = bb.kind == nkTupleConstr
-      let prc = if not isClosure: bb.sym else: bb.sons[0].sym
+      if isClosure and bb[0].kind == nkNilLit:
+        stackTrace(c, tos, pc, "attempt to call nil closure")
+      let prc = if not isClosure: bb.sym else: bb[0].sym
       if prc.offset < -1:
         # it's a callback:
-        c.callbacks[-prc.offset-2].value(
-          VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[pointer](regs),
-                 currentException: c.currentExceptionB,
-                 currentLineInfo: c.debug[pc]))
-      elif sfImportc in prc.flags:
-        if allowFFI notin c.features:
-          globalError(c.config, c.debug[pc], "VM not allowed to do FFI")
+        c.callbacks[-prc.offset-2](
+          VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[ptr UncheckedArray[TFullReg]](addr regs[0]),
+                 currentException: c.currentExceptionA,
+                 currentLineInfo: c.debug[pc])
+                 )
+      elif importcCond(c, prc):
+        if compiletimeFFI notin c.config.features:
+          globalError(c.config, c.debug[pc], "VM not allowed to do FFI, see `compiletimeFFI`")
         # we pass 'tos.slots' instead of 'regs' so that the compiler can keep
         # 'regs' in a register:
         when hasFFI:
-          let prcValue = c.globals.sons[prc.position-1]
+          if prc.position - 1 < 0:
+            globalError(c.config, c.debug[pc],
+              "VM call invalid: prc.position: " & $prc.position)
+          let prcValue = c.globals[prc.position-1]
           if prcValue.kind == nkEmpty:
             globalError(c.config, c.debug[pc], "cannot run " & prc.name.s)
-          let newValue = callForeignFunction(prcValue, prc.typ, tos.slots,
+          var slots2: TNodeSeq = newSeq[PNode](tos.slots.len)
+          for i in 0..<tos.slots.len:
+            slots2[i] = regToNode(tos.slots[i])
+          let newValue = callForeignFunction(c.config, prcValue, prc.typ, slots2,
                                              rb+1, rc-1, c.debug[pc])
           if newValue.kind != nkEmpty:
             assert instr.opcode == opcIndCallAsgn
@@ -988,15 +1434,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         #echo "new pc ", newPc, " calling: ", prc.name.s
         var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos)
         newSeq(newFrame.slots, prc.offset+ord(isClosure))
-        if not isEmptyType(prc.typ.sons[0]) or prc.kind == skMacro:
-          putIntoReg(newFrame.slots[0], getNullValue(prc.typ.sons[0], prc.info, c.config))
-        for i in 1 .. rc-1:
+        if not isEmptyType(prc.typ.returnType):
+          putIntoReg(newFrame.slots[0], getNullValue(c, prc.typ.returnType, prc.info, c.config))
+        for i in 1..rc-1:
           newFrame.slots[i] = regs[rb+i]
         if isClosure:
-          newFrame.slots[rc].kind = rkNode
-          newFrame.slots[rc].node = regs[rb].node.sons[1]
+          newFrame.slots[rc] = TFullReg(kind: rkNode, node: regs[rb].node[1])
         tos = newFrame
-        move(regs, newFrame.slots)
+        updateRegsAlias
         # -1 for the following 'inc pc'
         pc = newPc-1
       else:
@@ -1007,11 +1452,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
                             c.module
         var macroCall = newNodeI(nkCall, c.debug[pc])
         macroCall.add(newSymNode(prc))
-        for i in 1 .. rc-1:
+        for i in 1..rc-1:
           let node = regs[rb+i].regToNode
           node.info = c.debug[pc]
+          if prc.typ[i].kind notin {tyTyped, tyUntyped}:
+            node.annotateType(prc.typ[i], c.config)
+
           macroCall.add(node)
-        var a = evalTemplate(macroCall, prc, genSymOwner, c.config)
+        var a = evalTemplate(macroCall, prc, genSymOwner, c.config, c.cache, c.templInstCounter, c.idgen)
         if a.kind == nkStmtList and a.len == 1: a = a[0]
         a.recSetFlagIsRef
         ensureKind(rkNode)
@@ -1038,8 +1486,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       # we know the next instruction is a 'fjmp':
       let branch = c.constants[instr.regBx-wordExcess]
       var cond = false
-      for j in countup(0, sonsLen(branch) - 2):
-        if overlap(regs[ra].regToNode, branch.sons[j]):
+      for j in 0..<branch.len - 1:
+        if overlap(regs[ra].regToNode, branch[j]):
           cond = true
           break
       assert c.code[pc+1].opcode == opcFJmp
@@ -1055,48 +1503,72 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       tos.pushSafePoint(pc + rbx)
       assert c.code[pc+rbx].opcode in {opcExcept, opcFinally}
     of opcExcept:
-      # just skip it; it's followed by a jump;
-      # we'll execute in the 'raise' handler
-      let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc'
-      inc pc, rbx
-      while c.code[pc+1].opcode == opcExcept:
-        let rbx = c.code[pc+1].regBx - wordExcess - 1
-        inc pc, rbx
-      #assert c.code[pc+1].opcode in {opcExcept, opcFinally}
-      if c.code[pc+1].opcode != opcFinally:
-        # in an except handler there is no active safe point for the 'try':
-        tos.popSafePoint()
+      # This opcode is never executed, it only holds information for the
+      # exception handling routines.
+      raiseAssert "unreachable"
     of opcFinally:
-      # just skip it; it's followed by the code we need to execute anyway
+      # Pop the last safepoint introduced by a opcTry. This opcode is only
+      # executed _iff_ no exception was raised in the body of the `try`
+      # statement hence the need to pop the safepoint here.
+      doAssert(savedPC < 0)
       tos.popSafePoint()
     of opcFinallyEnd:
-      if c.currentExceptionA != nil:
-        # we are in a cleanup run:
-        let (newPc, newTos) = cleanUpOnException(c, tos)
-        if newPc-1 < 0:
-          bailOut(c, tos)
-          return
-        pc = newPc-1
-        if tos != newTos:
-          tos = newTos
-          move(regs, tos.slots)
+      # The control flow may not resume at the next instruction since we may be
+      # raising an exception or performing a cleanup.
+      if savedPC >= 0:
+        pc = savedPC - 1
+        savedPC = -1
+        if tos != savedFrame:
+          tos = savedFrame
+          updateRegsAlias
     of opcRaise:
-      let raised = regs[ra].node
+      let raised =
+        # Empty `raise` statement - reraise current exception
+        if regs[ra].kind == rkNone:
+          c.currentExceptionA
+        else:
+          regs[ra].node
       c.currentExceptionA = raised
+      # Set the `name` field of the exception
+      var exceptionNameNode = newStrNode(nkStrLit, c.currentExceptionA.typ.sym.name.s)
+      if c.currentExceptionA[2].kind == nkExprColonExpr:
+        exceptionNameNode.typ = c.currentExceptionA[2][1].typ
+        c.currentExceptionA[2][1] = exceptionNameNode
+      else:
+        exceptionNameNode.typ = c.currentExceptionA[2].typ
+        c.currentExceptionA[2] = exceptionNameNode
       c.exceptionInstr = pc
-      let (newPc, newTos) = cleanUpOnException(c, tos)
-      # -1 because of the following 'inc'
-      if newPc-1 < 0:
+
+      var frame = tos
+      var jumpTo = findExceptionHandler(c, frame, raised)
+      while jumpTo.why == ExceptionGotoUnhandled and not frame.next.isNil:
+        frame = frame.next
+        jumpTo = findExceptionHandler(c, frame, raised)
+
+      case jumpTo.why
+      of ExceptionGotoHandler:
+        # Jump to the handler, do nothing when the `finally` block ends.
+        savedPC = -1
+        pc = jumpTo.where - 1
+        if tos != frame:
+          tos = frame
+          updateRegsAlias
+      of ExceptionGotoFinally:
+        # Jump to the `finally` block first then re-jump here to continue the
+        # traversal of the exception chain
+        savedPC = pc
+        savedFrame = tos
+        pc = jumpTo.where - 1
+        if tos != frame:
+          tos = frame
+          updateRegsAlias
+      of ExceptionGotoUnhandled:
+        # Nobody handled this exception, error out.
         bailOut(c, tos)
-        return
-      pc = newPc-1
-      if tos != newTos:
-        tos = newTos
-        move(regs, tos.slots)
     of opcNew:
       ensureKind(rkNode)
       let typ = c.types[instr.regBx - wordExcess]
-      regs[ra].node = getNullValue(typ, c.debug[pc], c.config)
+      regs[ra].node = getNullValue(c, typ, c.debug[pc], c.config)
       regs[ra].node.flags.incl nfIsRef
     of opcNewSeq:
       let typ = c.types[instr.regBx - wordExcess]
@@ -1107,8 +1579,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       regs[ra].node = newNodeI(nkBracket, c.debug[pc])
       regs[ra].node.typ = typ
       newSeq(regs[ra].node.sons, count)
-      for i in 0 ..< count:
-        regs[ra].node.sons[i] = getNullValue(typ.sons[0], c.debug[pc], c.config)
+      for i in 0..<count:
+        regs[ra].node[i] = getNullValue(c, typ.elementType, c.debug[pc], c.config)
     of opcNewStr:
       decodeB(rkNode)
       regs[ra].node = newNodeI(nkStrLit, c.debug[pc])
@@ -1120,10 +1592,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcLdNull:
       ensureKind(rkNode)
       let typ = c.types[instr.regBx - wordExcess]
-      regs[ra].node = getNullValue(typ, c.debug[pc], c.config)
+      regs[ra].node = getNullValue(c, typ, c.debug[pc], c.config)
       # opcLdNull really is the gist of the VM's problems: should it load
       # a fresh null to  regs[ra].node  or to regs[ra].node[]? This really
-      # depends on whether regs[ra] represents the variable itself or wether
+      # depends on whether regs[ra] represents the variable itself or whether
       # it holds the indirection! Due to the way registers are re-used we cannot
       # say for sure here! --> The codegen has to deal with it
       # via 'genAsgnPatch'.
@@ -1138,16 +1610,16 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         regs[ra].intVal = 0
     of opcLdConst:
       let rb = instr.regBx - wordExcess
-      let cnst = c.constants.sons[rb]
+      let cnst = c.constants[rb]
       if fitsRegister(cnst.typ):
-        myreset(regs[ra])
+        reset(regs[ra])
         putIntoReg(regs[ra], cnst)
       else:
         ensureKind(rkNode)
         regs[ra].node = cnst
     of opcAsgnConst:
       let rb = instr.regBx - wordExcess
-      let cnst = c.constants.sons[rb]
+      let cnst = c.constants[rb]
       if fitsRegister(cnst.typ):
         putIntoReg(regs[ra], cnst)
       else:
@@ -1156,21 +1628,49 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcLdGlobal:
       let rb = instr.regBx - wordExcess - 1
       ensureKind(rkNode)
-      regs[ra].node = c.globals.sons[rb]
+      regs[ra].node = c.globals[rb]
+    of opcLdGlobalDerefFFI:
+      let rb = instr.regBx - wordExcess - 1
+      let node = c.globals[rb]
+      let typ = node.typ
+      doAssert node.kind == nkIntLit, $(node.kind)
+      if typ.kind == tyPtr:
+        ensureKind(rkNode)
+        # use nkPtrLit once this is added
+        let node2 = newNodeIT(nkIntLit, c.debug[pc], typ)
+        node2.intVal = cast[ptr int](node.intVal)[]
+        node2.flags.incl nfIsPtr
+        regs[ra].node = node2
+      elif not derefPtrToReg(node.intVal, typ, regs[ra], isAssign = false):
+        stackTrace(c, tos, pc, "opcLdDeref unsupported type: " & $(typeToString(typ), typ.elementType.kind))
+    of opcLdGlobalAddrDerefFFI:
+      let rb = instr.regBx - wordExcess - 1
+      let node = c.globals[rb]
+      let typ = node.typ
+      var node2 = newNodeIT(nkIntLit, node.info, typ)
+      node2.intVal = node.intVal
+      node2.flags.incl nfIsPtr
+      ensureKind(rkNode)
+      regs[ra].node = node2
     of opcLdGlobalAddr:
       let rb = instr.regBx - wordExcess - 1
       ensureKind(rkNodeAddr)
-      regs[ra].nodeAddr = addr(c.globals.sons[rb])
+      regs[ra].nodeAddr = addr(c.globals[rb])
     of opcRepr:
       decodeB(rkNode)
       createStr regs[ra]
-      regs[ra].node.strVal = renderTree(regs[rb].regToNode, {renderNoComments, renderDocComments})
+      regs[ra].node.strVal = renderTree(regs[rb].regToNode, {renderNoComments, renderDocComments, renderNonExportedFields})
     of opcQuit:
       if c.mode in {emRepl, emStaticExpr, emStaticStmt}:
         message(c.config, c.debug[pc], hintQuitCalled)
-        msgQuit(int8(getOrdValue(regs[ra].regToNode)))
+        msgQuit(int8(toInt(getOrdValue(regs[ra].regToNode, onError = toInt128(1)))))
       else:
         return TFullReg(kind: rkNone)
+    of opcInvalidField:
+      let msg = regs[ra].node.strVal
+      let disc = regs[instr.regB].regToNode
+      let msg2 = formatFieldDefect(msg, $disc)
+      stackTrace(c, tos, pc, msg2)
     of opcSetLenStr:
       decodeB(rkNode)
       #createStrKeepNode regs[ra]
@@ -1178,7 +1678,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcOf:
       decodeBC(rkInt)
       let typ = c.types[regs[rc].intVal.int]
-      regs[ra].intVal = ord(inheritanceDiff(regs[rb].node.typ, typ) >= 0)
+      regs[ra].intVal = ord(inheritanceDiff(regs[rb].node.typ, typ) <= 0)
     of opcIs:
       decodeBC(rkInt)
       let t1 = regs[rb].node.typ.skipTypes({tyTypeDesc})
@@ -1192,8 +1692,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))
@@ -1203,47 +1701,83 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNarrowU:
       decodeB(rkInt)
       regs[ra].intVal = regs[ra].intVal and ((1'i64 shl rb)-1)
+    of opcSignExtend:
+      # like opcNarrowS, but no out of range possible
+      decodeB(rkInt)
+      let imm = 64 - rb
+      regs[ra].intVal = ashr(regs[ra].intVal shl imm, imm)
     of opcIsNil:
       decodeB(rkInt)
       let node = regs[rb].node
-      regs[ra].intVal = ord(node.kind == nkNilLit or
-        (node.kind in {nkStrLit..nkTripleStrLit} and node.strVal.isNil))
+      regs[ra].intVal = ord(
+        # Note that `nfIsRef` + `nkNilLit` represents an allocated
+        # reference with the value `nil`, so `isNil` should be false!
+        (node.kind == nkNilLit and nfIsRef notin node.flags) or
+        (not node.typ.isNil and node.typ.kind == tyProc and
+          node.typ.callConv == ccClosure and node.safeLen > 0 and
+          node[0].kind == nkNilLit and node[1].kind == nkNilLit))
     of opcNBindSym:
+      # cannot use this simple check
+      # if dynamicBindSym notin c.config.features:
+
+      # bindSym with static input
       decodeBx(rkNode)
-      regs[ra].node = copyTree(c.constants.sons[rbx])
+      regs[ra].node = copyTree(c.constants[rbx])
+      regs[ra].node.flags.incl nfIsRef
+    of opcNDynBindSym:
+      # experimental bindSym
+      let
+        rb = instr.regB
+        rc = instr.regC
+        idx = int(regs[rb+rc-1].intVal)
+        callback = c.callbacks[idx]
+        args = VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[ptr UncheckedArray[TFullReg]](addr regs[0]),
+                currentException: c.currentExceptionA,
+                currentLineInfo: c.debug[pc])
+      callback(args)
+      regs[ra].node.flags.incl nfIsRef
     of opcNChild:
       decodeBC(rkNode)
       let idx = regs[rc].intVal.int
       let src = regs[rb].node
-      if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len:
-        regs[ra].node = src.sons[idx]
+      if src.kind in {nkEmpty..nkNilLit}:
+        stackTrace(c, tos, pc, "cannot get child of node kind: n" & $src.kind)
+      elif idx >=% src.len:
+        stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.len-1))
       else:
-        stackTrace(c, tos, pc, errIndexOutOfBounds)
+        regs[ra].node = src[idx]
     of opcNSetChild:
       decodeBC(rkNode)
       let idx = regs[rb].intVal.int
       var dest = regs[ra].node
-      if dest.kind notin {nkEmpty..nkNilLit} and idx <% dest.len:
-        dest.sons[idx] = regs[rc].node
+      if nfSem in dest.flags and allowSemcheckedAstModification notin c.config.legacyFeatures:
+        stackTrace(c, tos, pc, "typechecked nodes may not be modified")
+      elif dest.kind in {nkEmpty..nkNilLit}:
+        stackTrace(c, tos, pc, "cannot set child of node kind: n" & $dest.kind)
+      elif idx >=% dest.len:
+        stackTrace(c, tos, pc, formatErrorIndexBound(idx, dest.len-1))
       else:
-        stackTrace(c, tos, pc, errIndexOutOfBounds)
+        dest[idx] = regs[rc].node
     of opcNAdd:
       decodeBC(rkNode)
       var u = regs[rb].node
-      if u.kind notin {nkEmpty..nkNilLit}:
-        u.add(regs[rc].node)
+      if nfSem in u.flags and allowSemcheckedAstModification notin c.config.legacyFeatures:
+        stackTrace(c, tos, pc, "typechecked nodes may not be modified")
+      elif u.kind in {nkEmpty..nkNilLit}:
+        stackTrace(c, tos, pc, "cannot add to node kind: n" & $u.kind)
       else:
-        stackTrace(c, tos, pc, "cannot add to node kind: " & $u.kind)
+        u.add(regs[rc].node)
       regs[ra].node = u
     of opcNAddMultiple:
       decodeBC(rkNode)
       let x = regs[rc].node
       var u = regs[rb].node
-      if u.kind notin {nkEmpty..nkNilLit}:
-        # XXX can be optimized:
-        for i in 0..<x.len: u.add(x.sons[i])
+      if nfSem in u.flags and allowSemcheckedAstModification notin c.config.legacyFeatures:
+        stackTrace(c, tos, pc, "typechecked nodes may not be modified")
+      elif u.kind in {nkEmpty..nkNilLit}:
+        stackTrace(c, tos, pc, "cannot add to node kind: n" & $u.kind)
       else:
-        stackTrace(c, tos, pc, "cannot add to node kind: " & $u.kind)
+        for i in 0..<x.len: u.add(x[i])
       regs[ra].node = u
     of opcNKind:
       decodeB(rkInt)
@@ -1260,9 +1794,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNIntVal:
       decodeB(rkInt)
       let a = regs[rb].node
-      case a.kind
-      of nkCharLit..nkUInt64Lit: regs[ra].intVal = a.intVal
-      else: stackTrace(c, tos, pc, errFieldXNotFound & "intVal")
+      if a.kind in {nkCharLit..nkUInt64Lit}:
+        regs[ra].intVal = a.intVal
+      elif a.kind == nkSym and a.sym.kind == skEnumField:
+        regs[ra].intVal = a.sym.position
+      else:
+        stackTrace(c, tos, pc, errFieldXNotFound & "intVal")
     of opcNFloatVal:
       decodeB(rkFloat)
       let a = regs[rb].node
@@ -1283,15 +1820,23 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         regs[ra].node = copyNode(a)
       else:
         stackTrace(c, tos, pc, errFieldXNotFound & "ident")
+    of opcNodeId:
+      decodeB(rkInt)
+      when defined(useNodeIds):
+        regs[ra].intVal = regs[rb].node.id
+      else:
+        regs[ra].intVal = -1
     of opcNGetType:
       let rb = instr.regB
       let rc = instr.regC
-      case rc:
+      case rc
       of 0:
         # getType opcode:
         ensureKind(rkNode)
         if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
-          regs[ra].node = opMapTypeToAst(c.cache, regs[rb].node.typ, c.debug[pc])
+          regs[ra].node = opMapTypeToAst(c.cache, regs[rb].node.typ, c.debug[pc], c.idgen)
+        elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil:
+          regs[ra].node = opMapTypeToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc], c.idgen)
         else:
           stackTrace(c, tos, pc, "node has no type")
       of 1:
@@ -1299,28 +1844,55 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         ensureKind(rkInt)
         if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
           regs[ra].intVal = ord(regs[rb].node.typ.kind)
+        elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil:
+          regs[ra].intVal = ord(regs[rb].node.sym.typ.kind)
         #else:
         #  stackTrace(c, tos, pc, "node has no type")
       of 2:
         # getTypeInst opcode:
         ensureKind(rkNode)
         if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
-          regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.typ, c.debug[pc])
+          regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.typ, c.debug[pc], c.idgen)
+        elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil:
+          regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc], c.idgen)
         else:
           stackTrace(c, tos, pc, "node has no type")
       else:
         # getTypeImpl opcode:
         ensureKind(rkNode)
         if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
-          regs[ra].node = opMapTypeImplToAst(c.cache, regs[rb].node.typ, c.debug[pc])
+          regs[ra].node = opMapTypeImplToAst(c.cache, regs[rb].node.typ, c.debug[pc], c.idgen)
+        elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil:
+          regs[ra].node = opMapTypeImplToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc], c.idgen)
         else:
           stackTrace(c, tos, pc, "node has no type")
+    of opcNGetSize:
+      decodeBImm(rkInt)
+      let n = regs[rb].node
+      case imm
+      of 0: # size
+        if n.typ == nil:
+          stackTrace(c, tos, pc, "node has no type")
+        else:
+          regs[ra].intVal = getSize(c.config, n.typ)
+      of 1: # align
+        if n.typ == nil:
+          stackTrace(c, tos, pc, "node has no type")
+        else:
+          regs[ra].intVal = getAlign(c.config, n.typ)
+      else: # offset
+        if n.kind != nkSym:
+          stackTrace(c, tos, pc, "node is not a symbol")
+        elif n.sym.kind != skField:
+          stackTrace(c, tos, pc, "symbol is not a field (nskField)")
+        else:
+          regs[ra].intVal = n.sym.offset
     of opcNStrVal:
       decodeB(rkNode)
       createStr regs[ra]
       let a = regs[rb].node
       case a.kind
-      of {nkStrLit..nkTripleStrLit}:
+      of nkStrLit..nkTripleStrLit:
         regs[ra].node.strVal = a.strVal
       of nkCommentStmt:
         regs[ra].node.strVal = a.comment
@@ -1330,58 +1902,73 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         regs[ra].node.strVal = a.sym.name.s
       else:
         stackTrace(c, tos, pc, errFieldXNotFound & "strVal")
+    of opcNSigHash:
+      decodeB(rkNode)
+      createStr regs[ra]
+      if regs[rb].node.kind != nkSym:
+        stackTrace(c, tos, pc, "node is not a symbol")
+      else:
+        regs[ra].node.strVal = $sigHash(regs[rb].node.sym, c.config)
     of opcSlurp:
       decodeB(rkNode)
       createStr regs[ra]
       regs[ra].node.strVal = opSlurp(regs[rb].node.strVal, c.debug[pc],
                                      c.module, c.config)
     of opcGorge:
-      when defined(nimcore):
-        decodeBC(rkNode)
-        inc pc
-        let rd = c.code[pc].regA
-
-        createStr regs[ra]
-        regs[ra].node.strVal = opGorge(regs[rb].node.strVal,
-                                      regs[rc].node.strVal, regs[rd].node.strVal,
-                                      c.debug[pc], c.config)[0]
+      decodeBC(rkNode)
+      inc pc
+      let rd = c.code[pc].regA
+      createStr regs[ra]
+      if defined(nimsuggest) or c.config.cmd == cmdCheck:
+        discard "don't run staticExec for 'nim suggest'"
+        regs[ra].node.strVal = ""
       else:
-        globalError(c.config, c.debug[pc], "VM is not built with 'gorge' support")
-    of opcNError:
+        when defined(nimcore):
+          regs[ra].node.strVal = opGorge(regs[rb].node.strVal,
+                                        regs[rc].node.strVal, regs[rd].node.strVal,
+                                        c.debug[pc], c.config)[0]
+        else:
+          regs[ra].node.strVal = ""
+          globalError(c.config, c.debug[pc], "VM is not built with 'gorge' support")
+    of opcNError, opcNWarning, opcNHint:
       decodeB(rkNode)
       let a = regs[ra].node
       let b = regs[rb].node
-      stackTrace(c, tos, pc, a.strVal, if b.kind == nkNilLit: nil else: b)
-    of opcNWarning:
-      message(c.config, c.debug[pc], warnUser, regs[ra].node.strVal)
-    of opcNHint:
-      message(c.config, c.debug[pc], hintUser, regs[ra].node.strVal)
+      let info = if b.kind == nkNilLit: c.debug[pc] else: b.info
+      if instr.opcode == opcNError:
+        stackTrace(c, tos, pc, a.strVal, info)
+      elif instr.opcode == opcNWarning:
+        message(c.config, info, warnUser, a.strVal)
+      elif instr.opcode == opcNHint:
+        message(c.config, info, hintUser, a.strVal)
     of opcParseExprToAst:
-      decodeB(rkNode)
-      # c.debug[pc].line.int - countLines(regs[rb].strVal) ?
-      var error: string
+      decodeBC(rkNode)
+      var error: string = ""
       let ast = parseString(regs[rb].node.strVal, c.cache, c.config,
-                            toFullPath(c.config, c.debug[pc]), c.debug[pc].line.int,
+                            regs[rc].node.strVal, 0,
                             proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) =
-                              if error.isNil and msg <= errMax:
+                              if error.len == 0 and msg <= errMax:
                                 error = formatMsg(conf, info, msg, arg))
-      if not error.isNil:
+
+      regs[ra].node = newNode(nkEmpty)
+      if error.len > 0:
         c.errorFlag = error
-      elif sonsLen(ast) != 1:
+      elif ast.len != 1:
         c.errorFlag = formatMsg(c.config, c.debug[pc], errGenerated,
           "expected expression, but got multiple statements")
       else:
-        regs[ra].node = ast.sons[0]
+        regs[ra].node = ast[0]
     of opcParseStmtToAst:
-      decodeB(rkNode)
-      var error: string
+      decodeBC(rkNode)
+      var error: string = ""
       let ast = parseString(regs[rb].node.strVal, c.cache, c.config,
-                            toFullPath(c.config, c.debug[pc]), c.debug[pc].line.int,
+                            regs[rc].node.strVal, 0,
                             proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) =
-                              if error.isNil and msg <= errMax:
+                              if error.len == 0 and msg <= errMax:
                                 error = formatMsg(conf, info, msg, arg))
-      if not error.isNil:
+      if error.len > 0:
         c.errorFlag = error
+        regs[ra].node = newNode(nkEmpty)
       else:
         regs[ra].node = ast
     of opcQueryErrorFlag:
@@ -1392,36 +1979,57 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       ensureKind(rkNode)
       if c.callsite != nil: regs[ra].node = c.callsite
       else: stackTrace(c, tos, pc, errFieldXNotFound & "callsite")
-    of opcNGetFile:
-      decodeB(rkNode)
+    of opcNGetLineInfo:
+      decodeBImm(rkNode)
       let n = regs[rb].node
-      regs[ra].node = newStrNode(nkStrLit, toFilename(c.config, n.info))
+      case imm
+      of 0: # getFile
+        regs[ra].node = newStrNode(nkStrLit, toFullPath(c.config, n.info))
+      of 1: # getLine
+        regs[ra].node = newIntNode(nkIntLit, n.info.line.int)
+      of 2: # getColumn
+        regs[ra].node = newIntNode(nkIntLit, n.info.col.int)
+      else:
+        internalAssert c.config, false
       regs[ra].node.info = n.info
       regs[ra].node.typ = n.typ
-    of opcNGetLine:
+    of opcNCopyLineInfo:
       decodeB(rkNode)
-      let n = regs[rb].node
-      regs[ra].node = newIntNode(nkIntLit, n.info.line.int)
-      regs[ra].node.info = n.info
-      regs[ra].node.typ = n.typ
-    of opcNGetColumn:
+      regs[ra].node.info = regs[rb].node.info
+    of opcNSetLineInfoLine:
       decodeB(rkNode)
-      let n = regs[rb].node
-      regs[ra].node = newIntNode(nkIntLit, n.info.col)
-      regs[ra].node.info = n.info
-      regs[ra].node.typ = n.typ
+      regs[ra].node.info.line = regs[rb].intVal.uint16
+    of opcNSetLineInfoColumn:
+      decodeB(rkNode)
+      regs[ra].node.info.col = regs[rb].intVal.int16
+    of opcNSetLineInfoFile:
+      decodeB(rkNode)
+      regs[ra].node.info.fileIndex =
+        fileInfoIdx(c.config, RelativeFile regs[rb].node.strVal)
     of opcEqIdent:
       decodeBC(rkInt)
       # aliases for shorter and easier to understand code below
-      let aNode = regs[rb].node
-      let bNode = regs[rc].node
-      # these are cstring to prevent string copy, and cmpIgnoreStyle from
-      # takes cstring arguments
+      var aNode = regs[rb].node
+      var bNode = regs[rc].node
+      # Skipping both, `nkPostfix` and `nkAccQuoted` for both
+      # arguments.  `nkPostfix` exists only to tag exported symbols
+      # and therefor it can be safely skipped. Nim has no postfix
+      # operator. `nkAccQuoted` is used to quote an identifier that
+      # wouldn't be allowed to use in an unquoted context.
+      if aNode.kind == nkPostfix:
+        aNode = aNode[1]
+      if aNode.kind == nkAccQuoted:
+        aNode = aNode[0]
+      if bNode.kind == nkPostfix:
+        bNode = bNode[1]
+      if bNode.kind == nkAccQuoted:
+        bNode = bNode[0]
+      # These vars are of type `cstring` to prevent unnecessary string copy.
       var aStrVal: cstring = nil
       var bStrVal: cstring = nil
       # extract strVal from argument ``a``
       case aNode.kind
-      of {nkStrLit..nkTripleStrLit}:
+      of nkStrLit..nkTripleStrLit:
         aStrVal = aNode.strVal.cstring
       of nkIdent:
         aStrVal = aNode.ident.s.cstring
@@ -1433,7 +2041,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         discard
       # extract strVal from argument ``b``
       case bNode.kind
-      of {nkStrLit..nkTripleStrLit}:
+      of nkStrLit..nkTripleStrLit:
         bStrVal = bNode.strVal.cstring
       of nkIdent:
         bStrVal = bNode.ident.s.cstring
@@ -1443,10 +2051,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         bStrVal = bNode[0].sym.name.s.cstring
       else:
         discard
-      # set result
       regs[ra].intVal =
         if aStrVal != nil and bStrVal != nil:
-          ord(idents.cmpIgnoreStyle(aStrVal,bStrVal,high(int)) == 0)
+          ord(idents.cmpIgnoreStyle(aStrVal, bStrVal, high(int)) == 0)
         else:
           0
 
@@ -1457,10 +2064,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       else:
         regs[ra].node = newNodeI(nkIdent, c.debug[pc])
         regs[ra].node.ident = getIdent(c.cache, regs[rb].node.strVal)
+        regs[ra].node.flags.incl nfIsRef
     of opcSetType:
+      let typ = c.types[instr.regBx - wordExcess]
       if regs[ra].kind != rkNode:
-        internalError(c.config, c.debug[pc], "cannot set type")
-      regs[ra].node.typ = c.types[instr.regBx - wordExcess]
+        let temp = regToNode(regs[ra])
+        ensureKind(rkNode)
+        regs[ra].node = temp
+        regs[ra].node.info = c.debug[pc]
+      regs[ra].node.typ = typ
     of opcConv:
       let rb = instr.regB
       inc pc
@@ -1480,8 +2092,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       let srctyp = c.types[c.code[pc].regBx - wordExcess]
 
       when hasFFI:
-        let dest = fficast(regs[rb], desttyp)
-        asgnRef(regs[ra], dest)
+        let dest = fficast(c.config, regs[rb].node, desttyp)
+        # todo: check whether this is correct
+        # asgnRef(regs[ra], dest)
+        putIntoReg(regs[ra], dest)
       else:
         globalError(c.config, c.debug[pc], "cannot evaluate cast")
     of opcNSetIntVal:
@@ -1490,6 +2104,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       if dest.kind in {nkCharLit..nkUInt64Lit} and
          regs[rb].kind in {rkInt}:
         dest.intVal = regs[rb].intVal
+      elif dest.kind == nkSym and dest.sym.kind == skEnumField:
+        stackTrace(c, tos, pc, "`intVal` cannot be changed for an enum symbol.")
       else:
         stackTrace(c, tos, pc, errFieldXNotFound & "intVal")
     of opcNSetFloatVal:
@@ -1514,12 +2130,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         dest.ident = regs[rb].node.ident
       else:
         stackTrace(c, tos, pc, errFieldXNotFound & "ident")
-    of opcNSetType:
-      decodeB(rkNode)
-      let b = regs[rb].node
-      internalAssert c.config, b.kind == nkSym and b.sym.kind == skType
-      internalAssert c.config, regs[ra].node != nil
-      regs[ra].node.typ = b.sym.typ
     of opcNSetStrVal:
       decodeB(rkNode)
       var dest = regs[ra].node
@@ -1560,7 +2170,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNDel:
       decodeBC(rkNode)
       let bb = regs[rb].intVal.int
-      for i in countup(0, regs[rc].intVal.int-1):
+      for i in 0..<regs[rc].intVal.int:
         delSon(regs[ra].node, bb)
     of opcGenSym:
       decodeBC(rkNode)
@@ -1569,35 +2179,37 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
                  else: regs[rc].node.strVal
       if k < 0 or k > ord(high(TSymKind)):
         internalError(c.config, c.debug[pc], "request to create symbol of invalid kind")
-      var sym = newSym(k.TSymKind, getIdent(c.cache, name), c.module.owner, c.debug[pc])
+      var sym = newSym(k.TSymKind, getIdent(c.cache, name), c.idgen, c.module.owner, c.debug[pc])
       incl(sym.flags, sfGenSym)
       regs[ra].node = newSymNode(sym)
-
+      regs[ra].node.flags.incl nfIsRef
     of opcNccValue:
       decodeB(rkInt)
-      let destKey = regs[rb].node.strVal
+      let destKey {.cursor.} = regs[rb].node.strVal
       regs[ra].intVal = getOrDefault(c.graph.cacheCounters, destKey)
     of opcNccInc:
       let g = c.graph
-      let destKey = regs[ra].node.strVal
-      let by = regs[instr.regB].intVal
+      declBC()
+      let destKey {.cursor.} = regs[rb].node.strVal
+      let by = regs[rc].intVal
       let v = getOrDefault(g.cacheCounters, destKey)
       g.cacheCounters[destKey] = v+by
       recordInc(c, c.debug[pc], destKey, by)
     of opcNcsAdd:
       let g = c.graph
-      let destKey = regs[ra].node.strVal
-      let val = regs[instr.regB].node
+      declBC()
+      let destKey {.cursor.} = regs[rb].node.strVal
+      let val = regs[rc].node
       if not contains(g.cacheSeqs, destKey):
         g.cacheSeqs[destKey] = newTree(nkStmtList, val)
-        # newNodeI(nkStmtList, c.debug[pc])
       else:
         g.cacheSeqs[destKey].add val
       recordAdd(c, c.debug[pc], destKey, val)
     of opcNcsIncl:
       let g = c.graph
-      let destKey = regs[ra].node.strVal
-      let val = regs[instr.regB].node
+      declBC()
+      let destKey {.cursor.} = regs[rb].node.strVal
+      let val = regs[rc].node
       if not contains(g.cacheSeqs, destKey):
         g.cacheSeqs[destKey] = newTree(nkStmtList, val)
       else:
@@ -1610,22 +2222,22 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNcsLen:
       let g = c.graph
       decodeB(rkInt)
-      let destKey = regs[rb].node.strVal
+      let destKey {.cursor.} = regs[rb].node.strVal
       regs[ra].intVal =
         if contains(g.cacheSeqs, destKey): g.cacheSeqs[destKey].len else: 0
     of opcNcsAt:
       let g = c.graph
       decodeBC(rkNode)
       let idx = regs[rc].intVal
-      let destKey = regs[rb].node.strVal
+      let destKey {.cursor.} = regs[rb].node.strVal
       if contains(g.cacheSeqs, destKey) and idx <% g.cacheSeqs[destKey].len:
         regs[ra].node = g.cacheSeqs[destKey][idx.int]
       else:
-        stackTrace(c, tos, pc, errIndexOutOfBounds)
+        stackTrace(c, tos, pc, formatErrorIndexBound(idx, g.cacheSeqs[destKey].len-1))
     of opcNctPut:
       let g = c.graph
-      let destKey = regs[ra].node.strVal
-      let key = regs[instr.regB].node.strVal
+      let destKey {.cursor.} = regs[ra].node.strVal
+      let key {.cursor.} = regs[instr.regB].node.strVal
       let val = regs[instr.regC].node
       if not contains(g.cacheTables, destKey):
         g.cacheTables[destKey] = initBTree[string, PNode]()
@@ -1637,14 +2249,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNctLen:
       let g = c.graph
       decodeB(rkInt)
-      let destKey = regs[rb].node.strVal
+      let destKey {.cursor.} = regs[rb].node.strVal
       regs[ra].intVal =
         if contains(g.cacheTables, destKey): g.cacheTables[destKey].len else: 0
     of opcNctGet:
       let g = c.graph
       decodeBC(rkNode)
-      let destKey = regs[rb].node.strVal
-      let key = regs[rc].node.strVal
+      let destKey {.cursor.} = regs[rb].node.strVal
+      let key {.cursor.} = regs[rc].node.strVal
       if contains(g.cacheTables, destKey):
         if contains(g.cacheTables[destKey], key):
           regs[ra].node = getOrDefault(g.cacheTables[destKey], key)
@@ -1655,7 +2267,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNctHasNext:
       let g = c.graph
       decodeBC(rkInt)
-      let destKey = regs[rb].node.strVal
+      let destKey {.cursor.} = regs[rb].node.strVal
       regs[ra].intVal =
         if g.cacheTables.contains(destKey):
           ord(btrees.hasNext(g.cacheTables[destKey], regs[rc].intVal.int))
@@ -1664,7 +2276,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNctNext:
       let g = c.graph
       decodeBC(rkNode)
-      let destKey = regs[rb].node.strVal
+      let destKey {.cursor.} = regs[rb].node.strVal
       let index = regs[rc].intVal
       if contains(g.cacheTables, destKey):
         let (k, v, nextIndex) = btrees.next(g.cacheTables[destKey], index.int)
@@ -1679,46 +2291,27 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       decodeB(rkNode)
       var typ = regs[rb].node.typ
       internalAssert c.config, typ != nil
-      while typ.kind == tyTypeDesc and typ.len > 0: typ = typ.sons[0]
+      while typ.kind == tyTypeDesc and typ.hasElementType: typ = typ.skipModifier
       createStr regs[ra]
       regs[ra].node.strVal = typ.typeToString(preferExported)
-    of opcMarshalLoad:
-      let ra = instr.regA
-      let rb = instr.regB
-      inc pc
-      let typ = c.types[c.code[pc].regBx - wordExcess]
-      putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ, c.cache, c.config))
-    of opcMarshalStore:
-      decodeB(rkNode)
-      inc pc
-      let typ = c.types[c.code[pc].regBx - wordExcess]
-      createStrKeepNode(regs[ra])
-      if regs[ra].node.strVal.isNil: regs[ra].node.strVal = newStringOfCap(1000)
-      storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode, c.config)
-    of opcToNarrowInt:
-      decodeBC(rkInt)
-      let mask = (1'i64 shl rc) - 1 # 0xFF
-      let signbit = 1'i64 shl (rc - 1) # 0x80
-      let toggle = mask - signbit # 0x7F
-      # algorithm: -((i8 and 0xFF) xor 0x7F) + 0x7F
-      # mask off higher bits.
-      # uses two's complement to sign-extend integer.
-      # reajust integer into desired range.
-      regs[ra].intVal = -((regs[rb].intVal and mask) xor toggle) + toggle
+
+    c.profiler.leave(c)
 
     inc pc
 
 proc execute(c: PCtx, start: int): PNode =
   var tos = PStackFrame(prc: nil, comesFrom: 0, next: nil)
-  newSeq(tos.slots, c.prc.maxSlots)
+  newSeq(tos.slots, c.prc.regInfo.len)
   result = rawExecute(c, start, tos).regToNode
 
 proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
+  c.loopIterations = c.config.maxLoopIterationsVM
   if sym.kind in routineKinds:
-    if sym.typ.len-1 != args.len:
+    if sym.typ.paramsLen != args.len:
+      result = nil
       localError(c.config, sym.info,
         "NimScript: expected $# arguments, but got $#" % [
-        $(sym.typ.len-1), $args.len])
+        $(sym.typ.paramsLen), $args.len])
     else:
       let start = genProc(c, sym)
 
@@ -1727,19 +2320,20 @@ proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
       newSeq(tos.slots, maxSlots)
 
       # setup parameters:
-      if not isEmptyType(sym.typ.sons[0]) or sym.kind == skMacro:
-        putIntoReg(tos.slots[0], getNullValue(sym.typ.sons[0], sym.info, c.config))
+      if not isEmptyType(sym.typ.returnType) or sym.kind == skMacro:
+        putIntoReg(tos.slots[0], getNullValue(c, sym.typ.returnType, sym.info, c.config))
       # XXX We could perform some type checking here.
-      for i in 1..<sym.typ.len:
-        putIntoReg(tos.slots[i], args[i-1])
+      for i in 0..<sym.typ.paramsLen:
+        putIntoReg(tos.slots[i+1], args[i])
 
       result = rawExecute(c, start, tos).regToNode
   else:
+    result = nil
     localError(c.config, sym.info,
       "NimScript: attempt to call non-routine: " & sym.name.s)
 
 proc evalStmt*(c: PCtx, n: PNode) =
-  let n = transformExpr(c.graph, c.module, n)
+  let n = transformExpr(c.graph, c.idgen, c.module, n)
   let start = genStmt(c, n)
   # execute new instructions; this redundant opcEof check saves us lots
   # of allocations in 'execute':
@@ -1747,36 +2341,42 @@ proc evalStmt*(c: PCtx, n: PNode) =
     discard execute(c, start)
 
 proc evalExpr*(c: PCtx, n: PNode): PNode =
-  let n = transformExpr(c.graph, c.module, n)
+  # deadcode
+  # `nim --eval:"expr"` might've used it at some point for idetools; could
+  # be revived for nimsuggest
+  let n = transformExpr(c.graph, c.idgen, c.module, n)
   let start = genExpr(c, n)
   assert c.code[start].opcode != opcEof
   result = execute(c, start)
 
 proc getGlobalValue*(c: PCtx; s: PSym): PNode =
   internalAssert c.config, s.kind in {skLet, skVar} and sfGlobal in s.flags
-  result = c.globals.sons[s.position-1]
+  result = c.globals[s.position-1]
+
+proc setGlobalValue*(c: PCtx; s: PSym, val: PNode) =
+  ## Does not do type checking so ensure the `val` matches the `s.typ`
+  internalAssert c.config, s.kind in {skLet, skVar} and sfGlobal in s.flags
+  c.globals[s.position-1] = val
 
 include vmops
 
-proc setupGlobalCtx(module: PSym; graph: ModuleGraph) =
+proc setupGlobalCtx*(module: PSym; graph: ModuleGraph; idgen: IdGenerator) =
   if graph.vm.isNil:
-    graph.vm = newCtx(module, graph.cache, graph)
+    graph.vm = newCtx(module, graph.cache, graph, idgen)
     registerAdditionalOps(PCtx graph.vm)
   else:
-    refresh(PCtx graph.vm, module)
+    refresh(PCtx graph.vm, module, idgen)
 
-proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
+proc setupEvalGen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
   #var c = newEvalContext(module, emRepl)
-  #c.features = {allowCast, allowFFI, allowInfiniteLoops}
+  #c.features = {allowCast, allowInfiniteLoops}
   #pushStackFrame(c, newStackFrame())
 
   # XXX produce a new 'globals' environment here:
-  setupGlobalCtx(module, graph)
+  setupGlobalCtx(module, graph, idgen)
   result = PCtx graph.vm
-  when hasFFI:
-    PCtx(graph.vm).features = {allowFFI, allowCast}
 
-proc myProcess(c: PPassContext, n: PNode): PNode =
+proc interpreterCode*(c: PPassContext, n: PNode): PNode =
   let c = PCtx(c)
   # don't eval errornous code:
   if c.oldErrorCount == c.config.errorCounter:
@@ -1786,69 +2386,98 @@ proc myProcess(c: PPassContext, n: PNode): PNode =
     result = n
   c.oldErrorCount = c.config.errorCounter
 
-proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode =
-  myProcess(c, n)
-
-const evalPass* = makePass(myOpen, myProcess, myClose)
-
-proc evalConstExprAux(module: PSym;
+proc evalConstExprAux(module: PSym; idgen: IdGenerator;
                       g: ModuleGraph; prc: PSym, n: PNode,
                       mode: TEvalMode): PNode =
-  let n = transformExpr(g, module, n)
-  setupGlobalCtx(module, g)
+  when defined(nimsuggest):
+    if g.config.expandDone():
+      return n
+  #if g.config.errorCounter > 0: return n
+  let n = transformExpr(g, idgen, module, n)
+  setupGlobalCtx(module, g, idgen)
   var c = PCtx g.vm
   let oldMode = c.mode
-  defer: c.mode = oldMode
   c.mode = mode
   let start = genExpr(c, n, requiresValue = mode!=emStaticStmt)
   if c.code[start].opcode == opcEof: return newNodeI(nkEmpty, n.info)
   assert c.code[start].opcode != opcEof
   when debugEchoCode: c.echoCode start
   var tos = PStackFrame(prc: prc, comesFrom: 0, next: nil)
-  newSeq(tos.slots, c.prc.maxSlots)
-  #for i in 0 ..< c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty)
+  newSeq(tos.slots, c.prc.regInfo.len)
+  #for i in 0..<c.prc.regInfo.len: tos.slots[i] = newNode(nkEmpty)
   result = rawExecute(c, start, tos).regToNode
   if result.info.col < 0: result.info = n.info
+  c.mode = oldMode
+
+proc evalConstExpr*(module: PSym; idgen: IdGenerator; g: ModuleGraph; e: PNode): PNode =
+  result = evalConstExprAux(module, idgen, g, nil, e, emConst)
+
+proc evalStaticExpr*(module: PSym; idgen: IdGenerator; g: ModuleGraph; e: PNode, prc: PSym): PNode =
+  result = evalConstExprAux(module, idgen, g, prc, e, emStaticExpr)
+
+proc evalStaticStmt*(module: PSym; idgen: IdGenerator; g: ModuleGraph; e: PNode, prc: PSym) =
+  discard evalConstExprAux(module, idgen, g, prc, e, emStaticStmt)
+
+proc setupCompileTimeVar*(module: PSym; idgen: IdGenerator; g: ModuleGraph; n: PNode) =
+  discard evalConstExprAux(module, idgen, g, nil, n, emStaticStmt)
 
-proc evalConstExpr*(module: PSym; g: ModuleGraph; e: PNode): PNode =
-  result = evalConstExprAux(module, g, nil, e, emConst)
+proc prepareVMValue(arg: PNode): PNode =
+  ## strip nkExprColonExpr from tuple values recursively. That is how
+  ## they are expected to be stored in the VM.
 
-proc evalStaticExpr*(module: PSym; g: ModuleGraph; e: PNode, prc: PSym): PNode =
-  result = evalConstExprAux(module, g, prc, e, emStaticExpr)
+  # Early abort without copy. No transformation takes place.
+  if arg.kind in nkLiterals:
+    return arg
 
-proc evalStaticStmt*(module: PSym; g: ModuleGraph; e: PNode, prc: PSym) =
-  discard evalConstExprAux(module, g, prc, e, emStaticStmt)
+  if arg.kind == nkExprColonExpr and arg[0].typ != nil and
+     arg[0].typ.sym != nil and arg[0].typ.sym.magic == mPNimrodNode:
+    # Poor mans way of protecting static NimNodes
+    # XXX: Maybe we need a nkNimNode?
+    return arg
 
-proc setupCompileTimeVar*(module: PSym; g: ModuleGraph; n: PNode) =
-  discard evalConstExprAux(module, g, nil, n, emStaticStmt)
+  result = copyNode(arg)
+  if arg.kind == nkTupleConstr:
+    for child in arg:
+      if child.kind == nkExprColonExpr:
+        result.add prepareVMValue(child[1])
+      else:
+        result.add prepareVMValue(child)
+  else:
+    for child in arg:
+      result.add prepareVMValue(child)
 
 proc setupMacroParam(x: PNode, typ: PType): TFullReg =
   case typ.kind
   of tyStatic:
-    putIntoReg(result, x)
-  of tyTypeDesc:
-    putIntoReg(result, x)
+    result = TFullReg(kind: rkNone)
+    putIntoReg(result, prepareVMValue(x))
   else:
-    result.kind = rkNode
     var n = x
-    if n.kind in {nkHiddenSubConv, nkHiddenStdConv}: n = n.sons[1]
-    n = n.canonValue
+    if n.kind in {nkHiddenSubConv, nkHiddenStdConv}: n = n[1]
     n.flags.incl nfIsRef
     n.typ = x.typ
-    result.node = n
+    result = TFullReg(kind: rkNode, node: n)
 
 iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
   let gp = macroSym.ast[genericParamsPos]
-  for i in 0 ..< gp.len:
+  for i in 0..<gp.len:
     let genericParam = gp[i].sym
-    let posInCall = macroSym.typ.len + i
-    yield (genericParam, call[posInCall])
+    let posInCall = macroSym.typ.signatureLen + i
+    if posInCall < call.len:
+      yield (genericParam, call[posInCall])
 
 # to prevent endless recursion in macro instantiation
 const evalMacroLimit = 1000
 
-proc evalMacroCall*(module: PSym; g: ModuleGraph;
+#proc errorNode(idgen: IdGenerator; owner: PSym, n: PNode): PNode =
+#  result = newNodeI(nkEmpty, n.info)
+#  result.typ = newType(tyError, idgen, owner)
+#  result.typ.flags.incl tfCheckedForDestructor
+
+proc evalMacroCall*(module: PSym; idgen: IdGenerator; g: ModuleGraph; templInstCounter: ref int;
                     n, nOrig: PNode, sym: PSym): PNode =
+  #if g.config.errorCounter > 0: return errorNode(idgen, module, n)
+
   # XXX globalError() is ugly here, but I don't know a better solution for now
   inc(g.config.evalMacroCounter)
   if g.config.evalMacroCounter > evalMacroLimit:
@@ -1856,15 +2485,18 @@ proc evalMacroCall*(module: PSym; g: ModuleGraph;
 
   # immediate macros can bypass any type and arity checking so we check the
   # arity here too:
-  if sym.typ.len > n.safeLen and sym.typ.len > 1:
+  let sl = sym.typ.signatureLen
+  if sl > n.safeLen and sl > 1:
     globalError(g.config, n.info, "in call '$#' got $#, but expected $# argument(s)" % [
-        n.renderTree, $(n.safeLen-1), $(sym.typ.len-1)])
+        n.renderTree, $(n.safeLen-1), $(sym.typ.paramsLen)])
 
-  setupGlobalCtx(module, g)
+  setupGlobalCtx(module, g, idgen)
   var c = PCtx g.vm
+  let oldMode = c.mode
+  c.mode = emStaticStmt
   c.comesFromHeuristic.line = 0'u16
-
   c.callsite = nOrig
+  c.templInstCounter = templInstCounter
   let start = genProc(c, sym)
 
   var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil)
@@ -1878,32 +2510,27 @@ proc evalMacroCall*(module: PSym; g: ModuleGraph;
   #InternalAssert tos.slots.len >= L
 
   # return value:
-  tos.slots[0].kind = rkNode
-  tos.slots[0].node = newNodeI(nkEmpty, n.info)
+  tos.slots[0] = TFullReg(kind: rkNode, node: newNodeI(nkEmpty, n.info))
 
   # setup parameters:
-  for i in 1..<sym.typ.len:
-    tos.slots[i] = setupMacroParam(n.sons[i], sym.typ.sons[i])
+  for i, param in paramTypes(sym.typ):
+    tos.slots[i-FirstParamAt+1] = setupMacroParam(n[i-FirstParamAt+1], param)
 
   let gp = sym.ast[genericParamsPos]
-  for i in 0 ..< gp.len:
-    if sfImmediate notin sym.flags:
-      let idx = sym.typ.len + i
-      if idx < n.len:
-        tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ)
-      else:
-        dec(g.config.evalMacroCounter)
-        c.callsite = nil
-        localError(c.config, n.info, "expected " & $gp.len &
-                   " generic parameter(s)")
-    elif gp[i].sym.typ.kind in {tyStatic, tyTypeDesc}:
+  for i in 0..<gp.len:
+    let idx = sym.typ.signatureLen + i
+    if idx < n.len:
+      tos.slots[idx] = setupMacroParam(n[idx], gp[i].sym.typ)
+    else:
       dec(g.config.evalMacroCounter)
       c.callsite = nil
-      globalError(c.config, n.info, "static[T] or typedesc nor supported for .immediate macros")
+      localError(c.config, n.info, "expected " & $gp.len &
+                 " generic parameter(s)")
   # temporary storage:
-  #for i in L ..< maxSlots: tos.slots[i] = newNode(nkEmpty)
+  #for i in L..<maxSlots: tos.slots[i] = newNode(nkEmpty)
   result = rawExecute(c, start, tos).regToNode
   if result.info.line < 0: result.info = n.info
   if cyclicTree(result): globalError(c.config, n.info, "macro produced a cyclic tree")
   dec(g.config.evalMacroCounter)
   c.callsite = nil
+  c.mode = oldMode
diff --git a/compiler/vmconv.nim b/compiler/vmconv.nim
new file mode 100644
index 000000000..45d925df0
--- /dev/null
+++ b/compiler/vmconv.nim
@@ -0,0 +1,57 @@
+import ast except elementType
+import idents, lineinfos, astalgo
+import vmdef
+import std/times
+
+template elementType*(T: typedesc): typedesc =
+  typeof(block:
+    var a: T
+    for ai in a: ai)
+
+proc fromLit*(a: PNode, T: typedesc): auto =
+  ## generic PNode => type
+  ## see also reverse operation `toLit`
+  when T is set:
+    result = default(T)
+    type Ti = elementType(T)
+    for ai in a:
+      result.incl Ti(ai.intVal)
+  else:
+    static: raiseAssert "not yet supported: " & $T # add as needed
+
+proc toLit*[T](a: T): PNode =
+  ## generic type => PNode
+  ## see also reverse operation `fromLit`
+  when T is string: newStrNode(nkStrLit, a)
+  elif T is Ordinal: newIntNode(nkIntLit, a.ord)
+  elif T is (proc): newNode(nkNilLit)
+  elif T is ref:
+    if a == nil: newNode(nkNilLit)
+    else: toLit(a[])
+  elif T is tuple:
+    result = newTree(nkTupleConstr)
+    for ai in fields(a): result.add toLit(ai)
+  elif T is seq:
+    result = newNode(nkBracket)
+    for ai in a:
+      result.add toLit(ai)
+  elif T is object:
+    result = newTree(nkObjConstr)
+    result.add(newNode(nkEmpty))
+    for k, ai in fieldPairs(a):
+      let reti = newNode(nkExprColonExpr)
+      reti.add k.toLit
+      reti.add ai.toLit
+      result.add reti
+  else:
+    static: raiseAssert "not yet supported: " & $T # add as needed
+
+proc toTimeLit*(a: Time, c: PCtx, obj: PNode, info: TLineInfo): PNode =
+  # probably refactor it into `toLit` in the future
+  result = newTree(nkObjConstr)
+  result.add(newNode(nkEmpty)) # can be changed to a symbol according to PType
+  for k, ai in fieldPairs(a):
+    let reti = newNode(nkExprColonExpr)
+    reti.add newSymNode(lookupInRecord(obj, getIdent(c.cache, k)), info)
+    reti.add ai.toLit
+    result.add reti
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index cec61ade5..bdb0aeed1 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -10,20 +10,44 @@
 ## This module contains the type definitions for the new evaluation engine.
 ## An instruction is 1-3 int32s in memory, it is a register based VM.
 
-import ast, passes, msgs, idents, intsets, options, modulegraphs, lineinfos,
-  tables, btrees
+import std/[tables, strutils]
+
+import ast, idents, options, modulegraphs, lineinfos
+
+type TInstrType* = uint64
 
 const
+  regOBits = 8 # Opcode
+  regABits = 16
+  regBBits = 16
+  regCBits = 16
+  regBxBits = 24
+
   byteExcess* = 128 # we use excess-K for immediates
-  wordExcess* = 32768
 
-  MaxLoopIterations* = 1_000_000_000 # max iterations of all loops
+# Calculate register shifts, masks and ranges
 
+const
+  regOShift* = 0.TInstrType
+  regAShift* = (regOShift + regOBits)
+  regBShift* = (regAShift + regABits)
+  regCShift* = (regBShift + regBBits)
+  regBxShift* = (regAShift + regABits)
+
+  regOMask*  = ((1.TInstrType shl regOBits) - 1)
+  regAMask*  = ((1.TInstrType shl regABits) - 1)
+  regBMask*  = ((1.TInstrType shl regBBits) - 1)
+  regCMask*  = ((1.TInstrType shl regCBits) - 1)
+  regBxMask* = ((1.TInstrType shl regBxBits) - 1)
+
+  wordExcess* = 1 shl (regBxBits-1)
+  regBxMin* = -wordExcess+1
+  regBxMax* =  wordExcess-1
 
 type
-  TRegister* = range[0..255]
-  TDest* = range[-1 .. 255]
-  TInstr* = distinct uint32
+  TRegister* = range[0..regAMask.int]
+  TDest* = range[-1..regAMask.int]
+  TInstr* = distinct TInstrType
 
   TOpcode* = enum
     opcEof,         # end of code
@@ -32,16 +56,23 @@ type
     opcYldVal,      # yield with a value
 
     opcAsgnInt,
-    opcAsgnStr,
     opcAsgnFloat,
     opcAsgnRef,
     opcAsgnComplex,
-    opcRegToNode,
+    opcCastIntToFloat32,    # int and float must be of the same byte size
+    opcCastIntToFloat64,    # int and float must be of the same byte size
+    opcCastFloatToInt32,    # int and float must be of the same byte size
+    opcCastFloatToInt64,    # int and float must be of the same byte size
+    opcCastPtrToInt,
+    opcCastIntToPtr,
+    opcFastAsgnComplex,
     opcNodeToReg,
 
     opcLdArr,  # a = b[c]
+    opcLdArrAddr, # a = addr(b[c])
     opcWrArr,  # a[b] = c
     opcLdObj,  # a = b.c
+    opcLdObjAddr, # a = addr(b.c)
     opcWrObj,  # a.b = c
     opcAddrReg,
     opcAddrNode,
@@ -49,6 +80,8 @@ type
     opcWrDeref,
     opcWrStrIdx,
     opcLdStrIdx, # a = b[c]
+    opcLdStrIdxAddr,  # a = addr(b[c])
+    opcSlice, # toOpenArray(collection, left, right)
 
     opcAddInt,
     opcAddImmInt,
@@ -56,21 +89,24 @@ type
     opcSubImmInt,
     opcLenSeq,
     opcLenStr,
+    opcLenCstring,
 
     opcIncl, opcInclRange, opcExcl, opcCard, opcMulInt, opcDivInt, opcModInt,
-    opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat, opcShrInt, opcShlInt,
+    opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat,
+    opcShrInt, opcShlInt, opcAshrInt,
     opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu,
     opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat,
     opcLeFloat, opcLtFloat, opcLeu, opcLtu,
-    opcEqRef, opcEqNimrodNode, opcSameNodeType,
+    opcEqRef, opcEqNimNode, opcSameNodeType,
     opcXor, opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt,
-    opcEqStr, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet,
-    opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr,
+    opcEqStr, opcEqCString, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet,
+    opcMulSet, opcPlusSet, opcMinusSet, opcConcatStr,
     opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
     opcIsNil, opcOf, opcIs,
-    opcSubStr, opcParseFloat, opcConv, opcCast,
-    opcQuit, opcReset,
+    opcParseFloat, opcConv, opcCast,
+    opcQuit, opcInvalidField,
     opcNarrowS, opcNarrowU,
+    opcSignExtend,
 
     opcAddStrCh,
     opcAddStrStr,
@@ -87,13 +123,15 @@ type
     opcNIdent,
     opcNGetType,
     opcNStrVal,
+    opcNSigHash,
+    opcNGetSize,
 
     opcNSetIntVal,
-    opcNSetFloatVal, opcNSetSymbol, opcNSetIdent, opcNSetType, opcNSetStrVal,
+    opcNSetFloatVal, opcNSetSymbol, opcNSetIdent, opcNSetStrVal,
     opcNNewNimNode, opcNCopyNimNode, opcNCopyNimTree, opcNDel, opcGenSym,
 
     opcNccValue, opcNccInc, opcNcsAdd, opcNcsIncl, opcNcsLen, opcNcsAt,
-    opcNctPut, opcNctLen, opcNctGet, opcNctHasNext, opcNctNext,
+    opcNctPut, opcNctLen, opcNctGet, opcNctHasNext, opcNctNext, opcNodeId,
 
     opcSlurp,
     opcGorge,
@@ -103,10 +141,12 @@ type
     opcNError,
     opcNWarning,
     opcNHint,
-    opcNGetLine, opcNGetColumn, opcNGetFile,
+    opcNGetLineInfo, opcNCopyLineInfo, opcNSetLineInfoLine,
+    opcNSetLineInfoColumn, opcNSetLineInfoFile
     opcEqIdent,
     opcStrToIdent,
     opcGetImpl,
+    opcGetImplTransf
 
     opcEcho,
     opcIndCall, # dest = call regStart, n; where regStart = fn, arg1, ...
@@ -135,13 +175,15 @@ type
     opcAsgnConst, # dest = copy(constants[Bx])
     opcLdGlobal,  # dest = globals[Bx]
     opcLdGlobalAddr, # dest = addr(globals[Bx])
+    opcLdGlobalDerefFFI, # dest = globals[Bx][]
+    opcLdGlobalAddrDerefFFI, # globals[Bx][] = ...
 
     opcLdImmInt,  # dest = immediate value
-    opcNBindSym,
+    opcNBindSym, opcNDynBindSym,
     opcSetType,   # dest.typ = types[Bx]
     opcTypeTrait,
-    opcMarshalLoad, opcMarshalStore,
-    opcToNarrowInt
+    opcSymOwner,
+    opcSymIsInstantiationOf
 
   TBlock* = object
     label*: PSym
@@ -158,7 +200,6 @@ type
 
   TSandboxFlag* = enum        ## what the evaluation engine should allow
     allowCast,                ## allow unsafe language feature: 'cast'
-    allowFFI,                 ## allow the FFI
     allowInfiniteLoops        ## allow endless loops
   TSandboxFlags* = set[TSandboxFlag]
 
@@ -176,21 +217,32 @@ type
     slotTempComplex,  # some complex temporary (s.node field is used)
     slotTempPerm      # slot is temporary but permanent (hack)
 
+  TRegisterKind* = enum
+    rkNone, rkNode, rkInt, rkFloat, rkRegisterAddr, rkNodeAddr
+  TFullReg* = object  # with a custom mark proc, we could use the same
+                      # data representation as LuaJit (tagged NaNs).
+    case kind*: TRegisterKind
+    of rkNone: nil
+    of rkInt: intVal*: BiggestInt
+    of rkFloat: floatVal*: BiggestFloat
+    of rkNode: node*: PNode
+    of rkRegisterAddr: regAddr*: ptr TFullReg
+    of rkNodeAddr: nodeAddr*: ptr PNode
+
   PProc* = ref object
     blocks*: seq[TBlock]    # blocks; temp data structure
     sym*: PSym
-    slots*: array[TRegister, tuple[inUse: bool, kind: TSlotKind]]
-    maxSlots*: int
+    regInfo*: seq[tuple[inUse: bool, kind: TSlotKind]]
 
   VmArgs* = object
     ra*, rb*, rc*: Natural
-    slots*: pointer
+    slots*: ptr UncheckedArray[TFullReg]
     currentException*: PNode
     currentLineInfo*: TLineInfo
   VmCallback* = proc (args: VmArgs) {.closure.}
 
   PCtx* = ref TCtx
-  TCtx* = object of passes.TPassContext # code gen context
+  TCtx* = object of TPassContext # code gen context
     code*: seq[TInstr]
     debug*: seq[TLineInfo]  # line info for every instruction; kept separate
                             # to not slow down interpretation
@@ -207,47 +259,77 @@ type
     traceActive*: bool
     loopIterations*: int
     comesFromHeuristic*: TLineInfo # Heuristic for better macro stack traces
-    callbacks*: seq[tuple[key: string, value: VmCallback]]
+    callbacks*: seq[VmCallback]
+    callbackIndex*: Table[string, int]
     errorFlag*: string
     cache*: IdentCache
     config*: ConfigRef
     graph*: ModuleGraph
     oldErrorCount*: int
+    profiler*: Profiler
+    templInstCounter*: ref int # gives every template instantiation a unique ID, needed here for getAst
+    vmstateDiff*: seq[(PSym, PNode)] # we remember the "diff" to global state here (feature for IC)
+    procToCodePos*: Table[int, int]
+
+  PStackFrame* = ref TStackFrame
+  TStackFrame* {.acyclic.} = object
+    prc*: PSym                 # current prc; proc that is evaluated
+    slots*: seq[TFullReg]      # parameters passed to the proc + locals;
+                              # parameters come first
+    next*: PStackFrame         # for stacking
+    comesFrom*: int
+    safePoints*: seq[int]      # used for exception handling
+                              # XXX 'break' should perform cleanup actions
+                              # What does the C backend do for it?
+  Profiler* = object
+    tEnter*: float
+    tos*: PStackFrame
 
   TPosition* = distinct int
 
   PEvalContext* = PCtx
 
-proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph): PCtx =
+proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph; idgen: IdGenerator): PCtx =
   PCtx(code: @[], debug: @[],
     globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
-    prc: PProc(blocks: @[]), module: module, loopIterations: MaxLoopIterations,
-    comesFromHeuristic: unknownLineInfo(), callbacks: @[], errorFlag: "",
-    cache: cache, config: g.config, graph: g)
+    prc: PProc(blocks: @[]), module: module, loopIterations: g.config.maxLoopIterationsVM,
+    comesFromHeuristic: unknownLineInfo, callbacks: @[], callbackIndex: initTable[string, int](), errorFlag: "",
+    cache: cache, config: g.config, graph: g, idgen: idgen)
 
-proc refresh*(c: PCtx, module: PSym) =
+proc refresh*(c: PCtx, module: PSym; idgen: IdGenerator) =
   c.module = module
   c.prc = PProc(blocks: @[])
-  c.loopIterations = MaxLoopIterations
-
-proc registerCallback*(c: PCtx; name: string; callback: VmCallback) =
-  c.callbacks.add((name, callback))
+  c.loopIterations = c.config.maxLoopIterationsVM
+  c.idgen = idgen
+
+proc reverseName(s: string): string =
+  result = newStringOfCap(s.len)
+  let y = s.split('.')
+  for i in 1..y.len:
+    result.add y[^i]
+    if i != y.len:
+      result.add '.'
+
+proc registerCallback*(c: PCtx; name: string; callback: VmCallback): int {.discardable.} =
+  result = c.callbacks.len
+  c.callbacks.add(callback)
+  c.callbackIndex[reverseName(name)] = result
 
 const
   firstABxInstr* = opcTJmp
   largeInstrs* = { # instructions which use 2 int32s instead of 1:
-    opcSubStr, opcConv, opcCast, opcNewSeq, opcOf,
-    opcMarshalLoad, opcMarshalStore}
+    opcConv, opcCast, opcNewSeq, opcOf
+    }
   slotSomeTemp* = slotTempUnknown
   relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack}
 
 # flag is used to signal opcSeqLen if node is NimNode.
 const nimNodeFlag* = 16
 
-template opcode*(x: TInstr): TOpcode = TOpcode(x.uint32 and 0xff'u32)
-template regA*(x: TInstr): TRegister = TRegister(x.uint32 shr 8'u32 and 0xff'u32)
-template regB*(x: TInstr): TRegister = TRegister(x.uint32 shr 16'u32 and 0xff'u32)
-template regC*(x: TInstr): TRegister = TRegister(x.uint32 shr 24'u32)
-template regBx*(x: TInstr): int = (x.uint32 shr 16'u32).int
+template opcode*(x: TInstr): TOpcode = TOpcode(x.TInstrType shr regOShift and regOMask)
+template regA*(x: TInstr): TRegister = TRegister(x.TInstrType shr regAShift and regAMask)
+template regB*(x: TInstr): TRegister = TRegister(x.TInstrType shr regBShift and regBMask)
+template regC*(x: TInstr): TRegister = TRegister(x.TInstrType shr regCShift and regCMask)
+template regBx*(x: TInstr): int = (x.TInstrType shr regBxShift and regBxMask).int
 
 template jmpDiff*(x: TInstr): int = regBx(x) - wordExcess
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index 865ecd36e..294aaaa79 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -7,24 +7,31 @@
 #    distribution, for details about the copyright.
 #
 
-import ast, types, msgs, os, streams, options, idents, lineinfos
+import ast, types, msgs, options, idents, lineinfos
+from pathutils import AbsoluteFile
+
+import std/os
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
 
 proc opSlurp*(file: string, info: TLineInfo, module: PSym; conf: ConfigRef): string =
   try:
     var filename = parentDir(toFullPath(conf, info)) / file
     if not fileExists(filename):
-      filename = findFile(conf, file)
+      filename = findFile(conf, file).string
     result = readFile(filename)
     # we produce a fake include statement for every slurped filename, so that
     # the module dependencies are accurate:
-    appendToModule(module, newNode(nkIncludeStmt, info, @[
-      newStrNode(nkStrLit, filename)]))
+    discard conf.fileInfoIdx(AbsoluteFile filename)
+    appendToModule(module, newTreeI(nkIncludeStmt, info, newStrNode(nkStrLit, filename)))
   except IOError:
     localError(conf, info, "cannot open file: " & file)
     result = ""
 
-proc atomicTypeX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLineInfo): PNode =
-  let sym = newSym(skType, getIdent(cache, name), t.owner, info)
+proc atomicTypeX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLineInfo;
+                 idgen: IdGenerator): PNode =
+  let sym = newSym(skType, getIdent(cache, name), idgen, t.owner, info)
   sym.magic = m
   sym.typ = t
   result = newSymNode(sym)
@@ -34,44 +41,46 @@ proc atomicTypeX(s: PSym; info: TLineInfo): PNode =
   result = newSymNode(s)
   result.info = info
 
-proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
+proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGenerator;
                    inst=false; allowRecursionX=false): PNode
 
 proc mapTypeToBracketX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLineInfo;
+                       idgen: IdGenerator;
                        inst=false): PNode =
   result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
-  result.add atomicTypeX(cache, name, m, t, info)
-  for i in 0 ..< t.len:
-    if t.sons[i] == nil:
-      let void = atomicTypeX(cache, "void", mVoid, t, info)
-      void.typ = newType(tyVoid, t.owner)
-      result.add void
+  result.add atomicTypeX(cache, name, m, t, info, idgen)
+  for a in t.kids:
+    if a == nil:
+      let voidt = atomicTypeX(cache, "void", mVoid, t, info, idgen)
+      voidt.typ = newType(tyVoid, idgen, t.owner)
+      result.add voidt
     else:
-      result.add mapTypeToAstX(cache, t.sons[i], info, inst)
+      result.add mapTypeToAstX(cache, a, info, idgen, inst)
 
-proc objectNode(cache: IdentCache; n: PNode): PNode =
+proc objectNode(cache: IdentCache; n: PNode; idgen: IdGenerator): PNode =
   if n.kind == nkSym:
     result = newNodeI(nkIdentDefs, n.info)
     result.add n  # name
-    result.add mapTypeToAstX(cache, n.sym.typ, n.info, true, false)  # type
+    result.add mapTypeToAstX(cache, n.sym.typ, n.info, idgen, true, false)  # type
     result.add newNodeI(nkEmpty, n.info)  # no assigned value
   else:
     result = copyNode(n)
-    for i in 0 ..< n.safeLen:
-      result.add objectNode(cache, n[i])
+    for i in 0..<n.safeLen:
+      result.add objectNode(cache, n[i], idgen)
 
 proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
+                   idgen: IdGenerator;
                    inst=false; allowRecursionX=false): PNode =
   var allowRecursion = allowRecursionX
-  template atomicType(name, m): untyped = atomicTypeX(cache, name, m, t, info)
+  template atomicType(name, m): untyped = atomicTypeX(cache, name, m, t, info, idgen)
   template atomicType(s): untyped = atomicTypeX(s, info)
-  template mapTypeToAst(t,info): untyped = mapTypeToAstX(cache, t, info, inst)
-  template mapTypeToAstR(t,info): untyped = mapTypeToAstX(cache, t, info, inst, true)
-  template mapTypeToAst(t,i,info): untyped =
-    if i<t.len and t.sons[i]!=nil: mapTypeToAstX(cache, t.sons[i], info, inst)
+  template mapTypeToAst(t, info): untyped = mapTypeToAstX(cache, t, info, idgen, inst)
+  template mapTypeToAstR(t, info): untyped = mapTypeToAstX(cache, t, info, idgen, inst, true)
+  template mapTypeToAst(t, i, info): untyped =
+    if i<t.len and t[i]!=nil: mapTypeToAstX(cache, t[i], info, idgen, inst)
     else: newNodeI(nkEmpty, info)
   template mapTypeToBracket(name, m, t, info): untyped =
-    mapTypeToBracketX(cache, name, m, t, info, inst)
+    mapTypeToBracketX(cache, name, m, t, info, idgen, inst)
   template newNodeX(kind): untyped =
     newNodeIT(kind, if t.n.isNil: info else: t.n.info, t)
   template newIdentDefs(n,t): untyped =
@@ -82,34 +91,35 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
     id
   template newIdentDefs(s): untyped = newIdentDefs(s, s.typ)
 
-  if inst:
-    if t.sym != nil:  # if this node has a symbol
-      if not allowRecursion:  # getTypeInst behavior: return symbol
-        return atomicType(t.sym)
-      #else:  # getTypeImpl behavior: turn off recursion
-      #  allowRecursion = false
+  if inst and not allowRecursion and t.sym != nil:
+    # getTypeInst behavior: return symbol
+    return atomicType(t.sym)
 
   case t.kind
   of tyNone: result = atomicType("none", mNone)
   of tyBool: result = atomicType("bool", mBool)
   of tyChar: result = atomicType("char", mChar)
   of tyNil: result = atomicType("nil", mNil)
-  of tyExpr: result = atomicType("expr", mExpr)
-  of tyStmt: result = atomicType("stmt", mStmt)
+  of tyUntyped: result = atomicType("untyped", mExpr)
+  of tyTyped: result = atomicType("typed", mStmt)
   of tyVoid: result = atomicType("void", mVoid)
   of tyEmpty: result = atomicType("empty", mNone)
+  of tyUncheckedArray:
+    result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
+    result.add atomicType("UncheckedArray", mUncheckedArray)
+    result.add mapTypeToAst(t.elementType, info)
   of tyArray:
     result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
     result.add atomicType("array", mArray)
-    if inst and t.sons[0].kind == tyRange:
+    if inst and t.indexType.kind == tyRange:
       var rng = newNodeX(nkInfix)
       rng.add newIdentNode(getIdent(cache, ".."), info)
-      rng.add t.sons[0].n.sons[0].copyTree
-      rng.add t.sons[0].n.sons[1].copyTree
+      rng.add t.indexType.n[0].copyTree
+      rng.add t.indexType.n[1].copyTree
       result.add rng
     else:
-      result.add mapTypeToAst(t.sons[0], info)
-    result.add mapTypeToAst(t.sons[1], info)
+      result.add mapTypeToAst(t.indexType, info)
+    result.add mapTypeToAst(t.elementType, info)
   of tyTypeDesc:
     if t.base != nil:
       result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
@@ -119,33 +129,37 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
       result = atomicType("typeDesc", mTypeDesc)
   of tyGenericInvocation:
     result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
-    for i in 0 ..< t.len:
-      result.add mapTypeToAst(t.sons[i], info)
+    for a in t.kids:
+      result.add mapTypeToAst(a, info)
   of tyGenericInst:
     if inst:
       if allowRecursion:
-        result = mapTypeToAstR(t.lastSon, info)
+        result = mapTypeToAstR(t.skipModifier, info)
+        # keep original type info for getType calls on the output node:
+        result.typ = t
       else:
         result = newNodeX(nkBracketExpr)
-        #result.add mapTypeToAst(t.lastSon, info)
-        result.add mapTypeToAst(t[0], info)
-        for i in 1 ..< t.len-1:
-          result.add mapTypeToAst(t.sons[i], info)
+        #result.add mapTypeToAst(t.last, info)
+        result.add mapTypeToAst(t.genericHead, info)
+        for _, a in t.genericInstParams:
+          result.add mapTypeToAst(a, info)
     else:
-      result = mapTypeToAstX(cache, t.lastSon, info, inst, allowRecursion)
+      result = mapTypeToAstX(cache, t.skipModifier, info, idgen, inst, allowRecursion)
+      # keep original type info for getType calls on the output node:
+      result.typ = t
   of tyGenericBody:
     if inst:
-      result = mapTypeToAstR(t.lastSon, info)
+      result = mapTypeToAstR(t.typeBodyImpl, info)
     else:
-      result = mapTypeToAst(t.lastSon, info)
+      result = mapTypeToAst(t.typeBodyImpl, info)
   of tyAlias:
-    result = mapTypeToAstX(cache, t.lastSon, info, inst, allowRecursion)
+    result = mapTypeToAstX(cache, t.skipModifier, info, idgen, inst, allowRecursion)
   of tyOrdinal:
-    result = mapTypeToAst(t.lastSon, info)
+    result = mapTypeToAst(t.skipModifier, info)
   of tyDistinct:
     if inst:
       result = newNodeX(nkDistinctTy)
-      result.add mapTypeToAst(t.sons[0], info)
+      result.add mapTypeToAst(t.skipModifier, info)
     else:
       if allowRecursion or t.sym == nil:
         result = mapTypeToBracket("distinct", mDistinct, t, info)
@@ -156,24 +170,28 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
   of tyObject:
     if inst:
       result = newNodeX(nkObjectTy)
-      result.add newNodeI(nkEmpty, info)  # pragmas not reconstructed yet
-      if t.sons[0] == nil: result.add newNodeI(nkEmpty, info)  # handle parent object
-      else:
+      var objectDef = t.sym.ast[2]
+      if objectDef.kind == nkRefTy:
+        objectDef = objectDef[0]
+      result.add objectDef[0].copyTree  # copy object pragmas
+      if t.baseClass == nil:
+        result.add newNodeI(nkEmpty, info)
+      else:  # handle parent object
         var nn = newNodeX(nkOfInherit)
-        nn.add mapTypeToAst(t.sons[0], info)
+        nn.add mapTypeToAst(t.baseClass, info)
         result.add nn
       if t.n.len > 0:
-        result.add objectNode(cache, t.n)
+        result.add objectNode(cache, t.n, idgen)
       else:
         result.add newNodeI(nkEmpty, info)
     else:
       if allowRecursion or t.sym == nil:
         result = newNodeIT(nkObjectTy, if t.n.isNil: info else: t.n.info, t)
         result.add newNodeI(nkEmpty, info)
-        if t.sons[0] == nil:
+        if t.baseClass == nil:
           result.add newNodeI(nkEmpty, info)
         else:
-          result.add mapTypeToAst(t.sons[0], info)
+          result.add mapTypeToAst(t.baseClass, info)
         result.add copyTree(t.n)
       else:
         result = atomicType(t.sym)
@@ -187,7 +205,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
       # only named tuples have a node, unnamed tuples don't
       if t.n.isNil:
         result = newNodeX(nkTupleConstr)
-        for subType in t.sons:
+        for subType in t.kids:
           result.add mapTypeToAst(subType, info)
       else:
         result = newNodeX(nkTupleTy)
@@ -199,48 +217,63 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
   of tyPtr:
     if inst:
       result = newNodeX(nkPtrTy)
-      result.add mapTypeToAst(t.sons[0], info)
+      result.add mapTypeToAst(t.elementType, info)
     else:
       result = mapTypeToBracket("ptr", mPtr, t, info)
   of tyRef:
     if inst:
       result = newNodeX(nkRefTy)
-      result.add mapTypeToAst(t.sons[0], info)
+      result.add mapTypeToAst(t.elementType, info)
     else:
       result = mapTypeToBracket("ref", mRef, t, info)
   of tyVar:
     if inst:
       result = newNodeX(nkVarTy)
-      result.add mapTypeToAst(t.sons[0], info)
+      result.add mapTypeToAst(t.elementType, info)
     else:
       result = mapTypeToBracket("var", mVar, t, info)
   of tyLent: result = mapTypeToBracket("lent", mBuiltinType, t, info)
   of tySink: result = mapTypeToBracket("sink", mBuiltinType, t, info)
   of tySequence: result = mapTypeToBracket("seq", mSeq, t, info)
-  of tyOpt: result = mapTypeToBracket("opt", mOpt, t, info)
   of tyProc:
     if inst:
-      result = newNodeX(nkProcTy)
+      result = newNodeX(if tfIterator in t.flags: nkIteratorTy else: nkProcTy)
       var fp = newNodeX(nkFormalParams)
-      if t.sons[0] == nil:
+      if t.returnType == nil:
         fp.add newNodeI(nkEmpty, info)
       else:
-        fp.add mapTypeToAst(t.sons[0], t.n[0].info)
-      for i in 1..<t.sons.len:
-        fp.add newIdentDefs(t.n[i], t.sons[i])
+        fp.add mapTypeToAst(t.returnType, t.n[0].info)
+      for i in FirstParamAt..<t.kidsLen:
+        fp.add newIdentDefs(t.n[i], t[i])
       result.add fp
-      result.add newNodeI(nkEmpty, info)  # pragmas aren't reconstructed yet
+      var prag =
+        if t.n[0].len > 0:
+          t.n[0][pragmasEffects].copyTree
+        else:
+          newNodeI(nkEmpty, info)
+      if t.callConv != ccClosure or tfExplicitCallConv in t.flags:
+        if prag.kind == nkEmpty: prag = newNodeI(nkPragma, info)
+        prag.add newIdentNode(getIdent(cache, $t.callConv), info)
+      result.add prag
     else:
       result = mapTypeToBracket("proc", mNone, t, info)
   of tyOpenArray: result = mapTypeToBracket("openArray", mOpenArray, t, info)
   of tyRange:
     result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
     result.add atomicType("range", mRange)
-    result.add t.n.sons[0].copyTree
-    result.add t.n.sons[1].copyTree
+    if inst and t.n.len == 2:
+      let rng = newNodeX(nkInfix)
+      rng.add newIdentNode(getIdent(cache, ".."), info)
+      rng.add t.n[0].copyTree
+      rng.add t.n[1].copyTree
+      result.add rng
+    else:
+      result.add t.n[0].copyTree
+      if t.n.len > 1:
+        result.add t.n[1].copyTree
   of tyPointer: result = atomicType("pointer", mPointer)
   of tyString: result = atomicType("string", mString)
-  of tyCString: result = atomicType("cstring", mCString)
+  of tyCstring: result = atomicType("cstring", mCstring)
   of tyInt: result = atomicType("int", mInt)
   of tyInt8: result = atomicType("int8", mInt8)
   of tyInt16: result = atomicType("int16", mInt16)
@@ -250,18 +283,18 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
   of tyFloat32: result = atomicType("float32", mFloat32)
   of tyFloat64: result = atomicType("float64", mFloat64)
   of tyFloat128: result = atomicType("float128", mFloat128)
-  of tyUInt: result = atomicType("uint", mUint)
-  of tyUInt8: result = atomicType("uint8", mUint8)
-  of tyUInt16: result = atomicType("uint16", mUint16)
-  of tyUInt32: result = atomicType("uint32", mUint32)
-  of tyUInt64: result = atomicType("uint64", mUint64)
+  of tyUInt: result = atomicType("uint", mUInt)
+  of tyUInt8: result = atomicType("uint8", mUInt8)
+  of tyUInt16: result = atomicType("uint16", mUInt16)
+  of tyUInt32: result = atomicType("uint32", mUInt32)
+  of tyUInt64: result = atomicType("uint64", mUInt64)
   of tyVarargs: result = mapTypeToBracket("varargs", mVarargs, t, info)
-  of tyProxy: result = atomicType("error", mNone)
+  of tyError: result = atomicType("error", mNone)
   of tyBuiltInTypeClass:
     result = mapTypeToBracket("builtinTypeClass", mNone, t, info)
   of tyUserTypeClass, tyUserTypeClassInst:
     if t.isResolvedUserTypeClass:
-      result = mapTypeToAst(t.lastSon, info)
+      result = mapTypeToAst(t.last, info)
     else:
       result = mapTypeToBracket("concept", mNone, t, info)
       result.add t.n.copyTree
@@ -270,8 +303,9 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
   of tyAnd: result = mapTypeToBracket("and", mAnd, t, info)
   of tyOr: result = mapTypeToBracket("or", mOr, t, info)
   of tyNot: result = mapTypeToBracket("not", mNot, t, info)
+  of tyIterable: result = mapTypeToBracket("iterable", mIterableType, t, info)
   of tyAnything: result = atomicType("anything", mNone)
-  of tyInferred: assert false
+  of tyInferred: result = mapTypeToAstX(cache, t.skipModifier, info, idgen, inst, allowRecursion)
   of tyStatic, tyFromExpr:
     if inst:
       if t.n != nil: result = t.n.copyTree
@@ -281,17 +315,20 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
       result.add atomicType("static", mNone)
       if t.n != nil:
         result.add t.n.copyTree
-  of tyUnused, tyOptAsRef: assert(false, "mapTypeToAstX")
+  of tyOwned: result = mapTypeToBracket("owned", mBuiltinType, t, info)
+  of tyConcept:
+    result = mapTypeToBracket("concept", mNone, t, info)
+    result.add t.n.copyTree
 
-proc opMapTypeToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode =
-  result = mapTypeToAstX(cache, t, info, false, true)
+proc opMapTypeToAst*(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGenerator): PNode =
+  result = mapTypeToAstX(cache, t, info, idgen, inst=false, allowRecursionX=true)
 
 # the "Inst" version includes generic parameters in the resulting type tree
 # and also tries to look like the corresponding Nim type declaration
-proc opMapTypeInstToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode =
-  result = mapTypeToAstX(cache, t, info, true, false)
+proc opMapTypeInstToAst*(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGenerator): PNode =
+  result = mapTypeToAstX(cache, t, info, idgen, inst=true, allowRecursionX=false)
 
 # the "Impl" version includes generic parameters in the resulting type tree
 # and also tries to look like the corresponding Nim type implementation
-proc opMapTypeImplToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode =
-  result = mapTypeToAstX(cache, t, info, true, true)
+proc opMapTypeImplToAst*(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGenerator): PNode =
+  result = mapTypeToAstX(cache, t, info, idgen, inst=true, allowRecursionX=true)
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index d2243376c..0c7a49984 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -15,11 +15,10 @@
 #   this doesn't matter. However it matters for strings and other complex
 #   types that use the 'node' field; the reason is that slots are
 #   re-used in a register based VM. Example:
-#
-# .. code-block:: nim
-#   let s = a & b  # no matter what, create fresh node
-#   s = a & b  # no matter what, keep the node
-#
+#     ```nim
+#     let s = a & b  # no matter what, create fresh node
+#     s = a & b  # no matter what, keep the node
+#     ```
 # Also *stores* into non-temporary memory need to perform deep copies:
 # a.b = x.y
 # We used to generate opcAsgn for the *load* of 'x.y' but this is clearly
@@ -27,23 +26,41 @@
 # solves the opcLdConst vs opcAsgnConst issue. Of course whether we need
 # this copy depends on the involved types.
 
+import std/[tables, intsets, strutils]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 import
-  strutils, ast, astalgo, types, msgs, renderer, vmdef,
-  trees, intsets, magicsys, options, lowerings, lineinfos
-import platform
-from os import splitFile
+  ast, types, msgs, renderer, vmdef, trees,
+  magicsys, options, lowerings, lineinfos, transf, astmsgs
+
+from modulegraphs import getBody
 
+when defined(nimCompilerStacktraceHints):
+  import std/stackframes
+
+const
+  debugEchoCode* = defined(nimVMDebug)
+
+when debugEchoCode:
+  import std/private/asciitables
 when hasFFI:
   import evalffi
 
 type
-  TGenFlag = enum gfAddrOf, gfFieldAccess
+  TGenFlag = enum
+    gfNode # Affects how variables are loaded - always loads as rkNode
+    gfNodeAddr # Affects how variables are loaded - always loads as rkNodeAddr
+    gfIsParam # do not deepcopy parameters, they are immutable
+    gfIsSinkParam # deepcopy sink parameters
   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)
@@ -52,7 +69,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)
@@ -60,48 +79,52 @@ 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,
+    elif opc in relativeJumps + {opcTry}:
+      result.addf("\t$#\tr$#, L$#", opc.toStr, x.regA,
                   i+x.regBx-wordExcess)
+    elif opc in {opcExcept}:
+      let idx = x.regBx-wordExcess
+      result.addf("\t$#\t$#, $#", opc.toStr, x.regA, $idx)
     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,
-        c.types[y.regBx-wordExcess].typeToString)
-      inc i
     else:
-      result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess)
-    result.add("\t#")
+      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 = ""
   codeListing(c, buf, start, last)
   echo buf
 
-proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) =
+proc gABC(ctx: PCtx; n: PNode; opc: TOpcode;
+          a: TRegister = 0, b: TRegister = 0, c: TRegister = 0) =
   ## Takes the registers `b` and `c`, applies the operation `opc` to them, and
   ## stores the result into register `a`
   ## The node is needed for debug information
   assert opc.ord < 255
-  let ins = (opc.uint32 or (a.uint32 shl 8'u32) or
-                           (b.uint32 shl 16'u32) or
-                           (c.uint32 shl 24'u32)).TInstr
+  let ins = (opc.TInstrType or (a.TInstrType shl regAShift) or
+                           (b.TInstrType shl regBShift) or
+                           (c.TInstrType shl regCShift)).TInstr
   when false:
     if ctx.code.len == 43:
       writeStackTrace()
@@ -110,13 +133,13 @@ proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) =
   ctx.debug.add(n.info)
 
 proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) =
-  # Takes the `b` register and the immediate `imm`, appies the operation `opc`,
+  # Takes the `b` register and the immediate `imm`, applies the operation `opc`,
   # and stores the output value into `a`.
   # `imm` is signed and must be within [-128, 127]
   if imm >= -128 and imm <= 127:
-    let ins = (opc.uint32 or (a.uint32 shl 8'u32) or
-                             (b.uint32 shl 16'u32) or
-                             (imm+byteExcess).uint32 shl 24'u32).TInstr
+    let ins = (opc.TInstrType or (a.TInstrType shl regAShift) or
+                             (b.TInstrType shl regBShift) or
+                             (imm+byteExcess).TInstrType shl regCShift).TInstr
     c.code.add(ins)
     c.debug.add(n.info)
   else:
@@ -125,20 +148,20 @@ proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) =
 
 proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) =
   # Applies `opc` to `bx` and stores it into register `a`
-  # `bx` must be signed and in the range [-32768, 32767]
+  # `bx` must be signed and in the range [regBxMin, regBxMax]
   when false:
     if c.code.len == 43:
       writeStackTrace()
       echo "generating ", opc
 
-  if bx >= -32768 and bx <= 32767:
-    let ins = (opc.uint32 or a.uint32 shl 8'u32 or
-              (bx+wordExcess).uint32 shl 16'u32).TInstr
+  if bx >= regBxMin-1 and bx <= regBxMax:
+    let ins = (opc.TInstrType or a.TInstrType shl regAShift or
+              (bx+wordExcess).TInstrType shl regBxShift).TInstr
     c.code.add(ins)
     c.debug.add(n.info)
   else:
     localError(c.config, n.info,
-      "VM: immediate value does not fit into an int16")
+      "VM: immediate value does not fit into regBx")
 
 proc xjmp(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0): TPosition =
   #assert opc in {opcJmp, opcFJmp, opcTJmp}
@@ -151,7 +174,7 @@ proc genLabel(c: PCtx): TPosition =
 
 proc jmpBack(c: PCtx, n: PNode, p = TPosition(0)) =
   let dist = p.int - c.code.len
-  internalAssert(c.config, -0x7fff < dist and dist < 0x7fff)
+  internalAssert(c.config, regBxMin < dist and dist < regBxMax)
   gABx(c, n, opcJmpBack, 0, dist)
 
 proc patch(c: PCtx, p: TPosition) =
@@ -159,17 +182,17 @@ proc patch(c: PCtx, p: TPosition) =
   let p = p.int
   let diff = c.code.len - p
   #c.jumpTargets.incl(c.code.len)
-  internalAssert(c.config, -0x7fff < diff and diff < 0x7fff)
+  internalAssert(c.config, regBxMin < diff and diff < regBxMax)
   let oldInstr = c.code[p]
   # opcode and regA stay the same:
-  c.code[p] = ((oldInstr.uint32 and 0xffff'u32).uint32 or
-               uint32(diff+wordExcess) shl 16'u32).TInstr
+  c.code[p] = ((oldInstr.TInstrType and regBxMask).TInstrType or
+               TInstrType(diff+wordExcess) shl regBxShift).TInstr
 
 proc getSlotKind(t: PType): TSlotKind =
   case t.skipTypes(abstractRange-{tyTypeDesc}).kind
   of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64:
     slotTempInt
-  of tyString, tyCString:
+  of tyString, tyCstring:
     slotTempStr
   of tyFloat..tyFloat128:
     slotTempFloat
@@ -180,56 +203,75 @@ const
   HighRegisterPressure = 40
 
 proc bestEffort(c: PCtx): TLineInfo =
-  (if c.prc == nil: c.module.info else: c.prc.sym.info)
+  if c.prc != nil and c.prc.sym != nil:
+    c.prc.sym.info
+  else:
+    c.module.info
 
-proc getTemp(cc: PCtx; tt: PType): TRegister =
-  let typ = tt.skipTypesOrNil({tyStatic})
+proc getFreeRegister(cc: PCtx; k: TSlotKind; start: int): TRegister =
   let c = cc.prc
   # we prefer the same slot kind here for efficiency. Unfortunately for
   # discardable return types we may not know the desired type. This can happen
   # for e.g. mNAdd[Multiple]:
-  let k = if typ.isNil: slotTempComplex else: typ.getSlotKind
-  for i in 0 .. c.maxSlots-1:
-    if c.slots[i].kind == k and not c.slots[i].inUse:
-      c.slots[i].inUse = true
+  for i in start..c.regInfo.len-1:
+    if c.regInfo[i].kind == k and not c.regInfo[i].inUse:
+      c.regInfo[i].inUse = true
       return TRegister(i)
 
   # if register pressure is high, we re-use more aggressively:
-  if c.maxSlots >= HighRegisterPressure and false:
-    for i in 0 .. c.maxSlots-1:
-      if not c.slots[i].inUse:
-        c.slots[i] = (inUse: true, kind: k)
+  if c.regInfo.len >= high(TRegister):
+    for i in start..c.regInfo.len-1:
+      if not c.regInfo[i].inUse:
+        c.regInfo[i] = (inUse: true, kind: k)
         return TRegister(i)
-  if c.maxSlots >= high(TRegister):
+  if c.regInfo.len >= high(TRegister):
     globalError(cc.config, cc.bestEffort, "VM problem: too many registers required")
-  result = TRegister(c.maxSlots)
-  c.slots[c.maxSlots] = (inUse: true, kind: k)
-  inc c.maxSlots
+  result = TRegister(max(c.regInfo.len, start))
+  c.regInfo.setLen int(result)+1
+  c.regInfo[result] = (inUse: true, kind: k)
+
+proc getTemp(cc: PCtx; tt: PType): TRegister =
+  let typ = tt.skipTypesOrNil({tyStatic})
+  # we prefer the same slot kind here for efficiency. Unfortunately for
+  # discardable return types we may not know the desired type. This can happen
+  # for e.g. mNAdd[Multiple]:
+  let k = if typ.isNil: slotTempComplex else: typ.getSlotKind
+  result = getFreeRegister(cc, k, start = 0)
+  when false:
+    # enable this to find "register" leaks:
+    if result == 4:
+      echo "begin ---------------"
+      writeStackTrace()
+      echo "end ----------------"
 
 proc freeTemp(c: PCtx; r: TRegister) =
   let c = c.prc
-  if c.slots[r].kind in {slotSomeTemp..slotTempComplex}: c.slots[r].inUse = false
+  if r < c.regInfo.len and c.regInfo[r].kind in {slotSomeTemp..slotTempComplex}:
+    # this seems to cause https://github.com/nim-lang/Nim/issues/10647
+    c.regInfo[r].inUse = false
 
 proc getTempRange(cc: PCtx; n: int; kind: TSlotKind): TRegister =
   # if register pressure is high, we re-use more aggressively:
   let c = cc.prc
-  if c.maxSlots >= HighRegisterPressure or c.maxSlots+n >= high(TRegister):
-    for i in 0 .. c.maxSlots-n:
-      if not c.slots[i].inUse:
+  # we could also customize via the following (with proper caching in ConfigRef):
+  # let highRegisterPressure = cc.config.getConfigVar("vm.highRegisterPressure", "40").parseInt
+  if c.regInfo.len >= HighRegisterPressure or c.regInfo.len+n >= high(TRegister):
+    for i in 0..c.regInfo.len-n:
+      if not c.regInfo[i].inUse:
         block search:
-          for j in i+1 .. i+n-1:
-            if c.slots[j].inUse: break search
+          for j in i+1..i+n-1:
+            if c.regInfo[j].inUse: break search
           result = TRegister(i)
-          for k in result .. result+n-1: c.slots[k] = (inUse: true, kind: kind)
+          for k in result..result+n-1: c.regInfo[k] = (inUse: true, kind: kind)
           return
-  if c.maxSlots+n >= high(TRegister):
+  if c.regInfo.len+n >= high(TRegister):
     globalError(cc.config, cc.bestEffort, "VM problem: too many registers required")
-  result = TRegister(c.maxSlots)
-  inc c.maxSlots, n
-  for k in result .. result+n-1: c.slots[k] = (inUse: true, kind: kind)
+  result = TRegister(c.regInfo.len)
+  setLen c.regInfo, c.regInfo.len+n
+  for k in result..result+n-1: c.regInfo[k] = (inUse: true, kind: kind)
 
 proc freeTempRange(c: PCtx; start: TRegister, n: int) =
-  for i in start .. start+n-1: c.freeTemp(TRegister(i))
+  for i in start..start+n-1: c.freeTemp(TRegister(i))
 
 template withTemp(tmp, typ, body: untyped) {.dirty.} =
   var tmp = getTemp(c, typ)
@@ -256,7 +298,9 @@ proc gen(c: PCtx; n: PNode; dest: TRegister; flags: TGenFlags = {}) =
 proc gen(c: PCtx; n: PNode; flags: TGenFlags = {}) =
   var tmp: TDest = -1
   gen(c, n, tmp, flags)
-  #if n.typ.isEmptyType: InternalAssert tmp < 0
+  if tmp >= 0:
+    freeTemp(c, tmp)
+  #if n.typ.isEmptyType: internalAssert tmp < 0
 
 proc genx(c: PCtx; n: PNode; flags: TGenFlags = {}): TRegister =
   var tmp: TDest = -1
@@ -264,6 +308,8 @@ proc genx(c: PCtx; n: PNode; flags: TGenFlags = {}): TRegister =
   #internalAssert c.config, tmp >= 0 # 'nim check' does not like this internalAssert.
   if tmp >= 0:
     result = TRegister(tmp)
+  else:
+    result = 0
 
 proc clearDest(c: PCtx; n: PNode; dest: var TDest) {.inline.} =
   # stmt is different from 'void' in meta programming contexts.
@@ -273,112 +319,139 @@ proc clearDest(c: PCtx; n: PNode; dest: var TDest) {.inline.} =
     dest = -1
 
 proc isNotOpr(n: PNode): bool =
-  n.kind in nkCallKinds and n.sons[0].kind == nkSym and
-    n.sons[0].sym.magic == mNot
-
-proc isTrue(n: PNode): bool =
-  n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
-    n.kind == nkIntLit and n.intVal != 0
+  n.kind in nkCallKinds and n[0].kind == nkSym and
+    n[0].sym.magic == mNot
 
 proc genWhile(c: PCtx; n: PNode) =
-  # L1:
+  # lab1:
   #   cond, tmp
-  #   fjmp tmp, L2
+  #   fjmp tmp, lab2
   #   body
-  #   jmp L1
-  # L2:
-  let L1 = c.genLabel
+  #   jmp lab1
+  # lab2:
+  let lab1 = c.genLabel
   withBlock(nil):
-    if isTrue(n.sons[0]):
-      c.gen(n.sons[1])
-      c.jmpBack(n, L1)
-    elif isNotOpr(n.sons[0]):
-      var tmp = c.genx(n.sons[0].sons[1])
-      let L2 = c.xjmp(n, opcTJmp, tmp)
+    if isTrue(n[0]):
+      c.gen(n[1])
+      c.jmpBack(n, lab1)
+    elif isNotOpr(n[0]):
+      var tmp = c.genx(n[0][1])
+      let lab2 = c.xjmp(n, opcTJmp, tmp)
       c.freeTemp(tmp)
-      c.gen(n.sons[1])
-      c.jmpBack(n, L1)
-      c.patch(L2)
+      c.gen(n[1])
+      c.jmpBack(n, lab1)
+      c.patch(lab2)
     else:
-      var tmp = c.genx(n.sons[0])
-      let L2 = c.xjmp(n, opcFJmp, tmp)
+      var tmp = c.genx(n[0])
+      let lab2 = c.xjmp(n, opcFJmp, tmp)
       c.freeTemp(tmp)
-      c.gen(n.sons[1])
-      c.jmpBack(n, L1)
-      c.patch(L2)
+      c.gen(n[1])
+      c.jmpBack(n, lab1)
+      c.patch(lab2)
 
 proc genBlock(c: PCtx; n: PNode; dest: var TDest) =
-  withBlock(n.sons[0].sym):
-    c.gen(n.sons[1], dest)
+  let oldRegisterCount = c.prc.regInfo.len
+  withBlock(n[0].sym):
+    c.gen(n[1], dest)
+
+  for i in oldRegisterCount..<c.prc.regInfo.len:
+    #if c.prc.regInfo[i].kind in {slotFixedVar, slotFixedLet}:
+    if i != dest:
+      when not defined(release):
+        if c.config.cmd != cmdCheck:
+          if c.prc.regInfo[i].inUse and c.prc.regInfo[i].kind in {slotTempUnknown,
+                                    slotTempInt,
+                                    slotTempFloat,
+                                    slotTempStr,
+                                    slotTempComplex}:
+            raiseAssert "leaking temporary " & $i & " " & $c.prc.regInfo[i].kind
+      c.prc.regInfo[i] = (inUse: false, kind: slotEmpty)
+
   c.clearDest(n, dest)
 
 proc genBreak(c: PCtx; n: PNode) =
-  let L1 = c.xjmp(n, opcJmp)
-  if n.sons[0].kind == nkSym:
-    #echo cast[int](n.sons[0].sym)
+  let lab1 = c.xjmp(n, opcJmp)
+  if n[0].kind == nkSym:
+    #echo cast[int](n[0].sym)
     for i in countdown(c.prc.blocks.len-1, 0):
-      if c.prc.blocks[i].label == n.sons[0].sym:
-        c.prc.blocks[i].fixups.add L1
+      if c.prc.blocks[i].label == n[0].sym:
+        c.prc.blocks[i].fixups.add lab1
         return
     globalError(c.config, n.info, "VM problem: cannot find 'break' target")
   else:
-    c.prc.blocks[c.prc.blocks.high].fixups.add L1
+    c.prc.blocks[c.prc.blocks.high].fixups.add lab1
 
 proc genIf(c: PCtx, n: PNode; dest: var TDest) =
-  #  if (!expr1) goto L1;
+  #  if (!expr1) goto lab1;
   #    thenPart
   #    goto LEnd
-  #  L1:
-  #  if (!expr2) goto L2;
+  #  lab1:
+  #  if (!expr2) goto lab2;
   #    thenPart2
   #    goto LEnd
-  #  L2:
+  #  lab2:
   #    elsePart
   #  Lend:
   if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
   var endings: seq[TPosition] = @[]
-  for i in countup(0, len(n) - 1):
-    var it = n.sons[i]
+  for i in 0..<n.len:
+    var it = n[i]
     if it.len == 2:
-      withTemp(tmp, it.sons[0].typ):
+      withTemp(tmp, it[0].typ):
         var elsePos: TPosition
-        if isNotOpr(it.sons[0]):
-          c.gen(it.sons[0].sons[1], tmp)
-          elsePos = c.xjmp(it.sons[0].sons[1], opcTJmp, tmp) # if true
+        if isNotOpr(it[0]):
+          c.gen(it[0][1], tmp)
+          elsePos = c.xjmp(it[0][1], opcTJmp, tmp) # if true
         else:
-          c.gen(it.sons[0], tmp)
-          elsePos = c.xjmp(it.sons[0], opcFJmp, tmp) # if false
+          c.gen(it[0], tmp)
+          elsePos = c.xjmp(it[0], opcFJmp, tmp) # if false
       c.clearDest(n, dest)
-      c.gen(it.sons[1], dest) # then part
-      if i < sonsLen(n)-1:
-        endings.add(c.xjmp(it.sons[1], opcJmp, 0))
+      if isEmptyType(it[1].typ): # maybe noreturn call, don't touch `dest`
+        c.gen(it[1])
+      else:
+        c.gen(it[1], dest) # then part
+      if i < n.len-1:
+        endings.add(c.xjmp(it[1], opcJmp, 0))
       c.patch(elsePos)
     else:
       c.clearDest(n, dest)
-      c.gen(it.sons[0], dest)
+      if isEmptyType(it[0].typ): # maybe noreturn call, don't touch `dest`
+        c.gen(it[0])
+      else:
+        c.gen(it[0], dest)
   for endPos in endings: c.patch(endPos)
   c.clearDest(n, dest)
 
+proc isTemp(c: PCtx; dest: TDest): bool =
+  result = dest >= 0 and c.prc.regInfo[dest].kind >= slotTempUnknown
+
 proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) =
   #   asgn dest, a
-  #   tjmp|fjmp L1
+  #   tjmp|fjmp lab1
   #   asgn dest, b
-  # L1:
-  if dest < 0: dest = getTemp(c, n.typ)
-  c.gen(n.sons[1], dest)
-  let L1 = c.xjmp(n, opc, dest)
-  c.gen(n.sons[2], dest)
-  c.patch(L1)
-
-proc canonValue*(n: PNode): PNode =
-  result = n
+  # lab1:
+  let copyBack = dest < 0 or not isTemp(c, dest)
+  let tmp = if copyBack:
+              getTemp(c, n.typ)
+            else:
+              TRegister dest
+  c.gen(n[1], tmp)
+  let lab1 = c.xjmp(n, opc, tmp)
+  c.gen(n[2], tmp)
+  c.patch(lab1)
+  if dest < 0:
+    dest = tmp
+  elif copyBack:
+    c.gABC(n, opcAsgnInt, dest, tmp)
+    freeTemp(c, tmp)
 
 proc rawGenLiteral(c: PCtx; n: PNode): int =
   result = c.constants.len
   #assert(n.kind != nkCall)
   n.flags.incl nfAllConst
-  c.constants.add n.canonValue
-  internalAssert c.config, result < 0x7fff
+  n.flags.excl nfIsRef
+  c.constants.add n
+  internalAssert c.config, result < regBxMax
 
 proc sameConstant*(a, b: PNode): bool =
   result = false
@@ -389,19 +462,24 @@ proc sameConstant*(a, b: PNode): bool =
     of nkSym: result = a.sym == b.sym
     of nkIdent: result = a.ident.id == b.ident.id
     of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal
-    of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
+    of nkFloatLit..nkFloat64Lit:
+      result = cast[uint64](a.floatVal) == cast[uint64](b.floatVal)
+      # refs bug #16469
+      # if we wanted to only distinguish 0.0 vs -0.0:
+      # if a.floatVal == 0.0: result = cast[uint64](a.floatVal) == cast[uint64](b.floatVal)
+      # else: result = a.floatVal == b.floatVal
     of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
     of nkType, nkNilLit: result = a.typ == b.typ
     of nkEmpty: result = true
     else:
-      if sonsLen(a) == sonsLen(b):
-        for i in countup(0, sonsLen(a) - 1):
-          if not sameConstant(a.sons[i], b.sons[i]): return
+      if a.len == b.len:
+        for i in 0..<a.len:
+          if not sameConstant(a[i], b[i]): return
         result = true
 
 proc genLiteral(c: PCtx; n: PNode): int =
   # types do not matter here:
-  for i in 0 ..< c.constants.len:
+  for i in 0..<c.constants.len:
     if sameConstant(c.constants[i], n): return i
   result = rawGenLiteral(c, n)
 
@@ -411,14 +489,14 @@ proc unused(c: PCtx; n: PNode; x: TDest) {.inline.} =
     globalError(c.config, n.info, "not unused")
 
 proc genCase(c: PCtx; n: PNode; dest: var TDest) =
-  #  if (!expr1) goto L1;
+  #  if (!expr1) goto lab1;
   #    thenPart
   #    goto LEnd
-  #  L1:
-  #  if (!expr2) goto L2;
+  #  lab1:
+  #  if (!expr2) goto lab2;
   #    thenPart2
   #    goto LEnd
-  #  L2:
+  #  lab2:
   #    elsePart
   #  Lend:
   if not isEmptyType(n.typ):
@@ -426,22 +504,33 @@ proc genCase(c: PCtx; n: PNode; dest: var TDest) =
   else:
     unused(c, n, dest)
   var endings: seq[TPosition] = @[]
-  withTemp(tmp, n.sons[0].typ):
-    c.gen(n.sons[0], tmp)
+  withTemp(tmp, n[0].typ):
+    c.gen(n[0], tmp)
     # branch tmp, codeIdx
     # fjmp   elseLabel
-    for i in 1 ..< n.len:
-      let it = n.sons[i]
+    for i in 1..<n.len:
+      let it = n[i]
       if it.len == 1:
         # else stmt:
-        c.gen(it.sons[0], dest)
+        let body = it[0]
+        if body.kind != nkNilLit or body.typ != nil:
+          # an nkNilLit with nil for typ implies there is no else branch, this
+          # avoids unused related errors as we've already consumed the dest
+          if isEmptyType(body.typ): # maybe noreturn call, don't touch `dest`
+            c.gen(body)
+          else:
+            c.gen(body, dest)
       else:
         let b = rawGenLiteral(c, it)
         c.gABx(it, opcBranch, tmp, b)
-        let elsePos = c.xjmp(it.lastSon, opcFJmp, tmp)
-        c.gen(it.lastSon, dest)
-        if i < sonsLen(n)-1:
-          endings.add(c.xjmp(it.lastSon, opcJmp, 0))
+        let body = it.lastSon
+        let elsePos = c.xjmp(body, opcFJmp, tmp)
+        if isEmptyType(body.typ): # maybe noreturn call, don't touch `dest`
+          c.gen(body)
+        else:
+          c.gen(body, dest)
+        if i < n.len-1:
+          endings.add(c.xjmp(body, opcJmp, 0))
         c.patch(elsePos)
       c.clearDest(n, dest)
   for endPos in endings: c.patch(endPos)
@@ -451,51 +540,61 @@ proc genType(c: PCtx; typ: PType): int =
     if sameType(t, typ): return i
   result = c.types.len
   c.types.add(typ)
-  internalAssert(c.config, result <= 0x7fff)
+  internalAssert(c.config, result <= regBxMax)
 
 proc genTry(c: PCtx; n: PNode; dest: var TDest) =
   if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
   var endings: seq[TPosition] = @[]
-  let elsePos = c.xjmp(n, opcTry, 0)
-  c.gen(n.sons[0], dest)
+  let ehPos = c.xjmp(n, opcTry, 0)
+  if isEmptyType(n[0].typ): # maybe noreturn call, don't touch `dest`
+    c.gen(n[0])
+  else:
+    c.gen(n[0], dest)
   c.clearDest(n, dest)
-  c.patch(elsePos)
-  for i in 1 ..< n.len:
-    let it = n.sons[i]
+  # Add a jump past the exception handling code
+  let jumpToFinally = c.xjmp(n, opcJmp, 0)
+  # This signals where the body ends and where the exception handling begins
+  c.patch(ehPos)
+  for i in 1..<n.len:
+    let it = n[i]
     if it.kind != nkFinally:
-      var blen = len(it)
       # first opcExcept contains the end label of the 'except' block:
       let endExcept = c.xjmp(it, opcExcept, 0)
-      for j in countup(0, blen - 2):
-        assert(it.sons[j].kind == nkType)
-        let typ = it.sons[j].typ.skipTypes(abstractPtrs-{tyTypeDesc})
+      for j in 0..<it.len - 1:
+        assert(it[j].kind == nkType)
+        let typ = it[j].typ.skipTypes(abstractPtrs-{tyTypeDesc})
         c.gABx(it, opcExcept, 0, c.genType(typ))
-      if blen == 1:
+      if it.len == 1:
         # general except section:
         c.gABx(it, opcExcept, 0, 0)
-      c.gen(it.lastSon, dest)
+      let body = it.lastSon
+      if isEmptyType(body.typ): # maybe noreturn call, don't touch `dest`
+        c.gen(body)
+      else:
+        c.gen(body, dest)
       c.clearDest(n, dest)
-      if i < sonsLen(n)-1:
+      if i < n.len:
         endings.add(c.xjmp(it, opcJmp, 0))
       c.patch(endExcept)
-  for endPos in endings: c.patch(endPos)
   let fin = lastSon(n)
   # we always generate an 'opcFinally' as that pops the safepoint
-  # from the stack
+  # from the stack if no exception is raised in the body.
+  c.patch(jumpToFinally)
   c.gABx(fin, opcFinally, 0, 0)
+  for endPos in endings: c.patch(endPos)
   if fin.kind == nkFinally:
-    c.gen(fin.sons[0])
+    c.gen(fin[0])
     c.clearDest(n, dest)
   c.gABx(fin, opcFinallyEnd, 0, 0)
 
 proc genRaise(c: PCtx; n: PNode) =
-  let dest = genx(c, n.sons[0])
+  let dest = genx(c, n[0])
   c.gABC(n, opcRaise, dest)
   c.freeTemp(dest)
 
 proc genReturn(c: PCtx; n: PNode) =
-  if n.sons[0].kind != nkEmpty:
-    gen(c, n.sons[0])
+  if n[0].kind != nkEmpty:
+    gen(c, n[0])
   c.gABC(n, opcRet)
 
 
@@ -504,7 +603,7 @@ proc genLit(c: PCtx; n: PNode; dest: var TDest) =
   # assignments now:
   #var opc = opcLdConst
   if dest < 0: dest = c.getTemp(n.typ)
-  #elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst
+  #elif c.prc.regInfo[dest].kind == slotFixedVar: opc = opcAsgnConst
   let lit = genLiteral(c, n)
   c.gABx(n, opcLdConst, dest, lit)
 
@@ -514,19 +613,25 @@ proc genCall(c: PCtx; n: PNode; dest: var TDest) =
   #if n.typ != nil and n.typ.sym != nil and n.typ.sym.magic == mPNimrodNode:
   #  genLit(c, n, dest)
   #  return
+  # bug #10901: do not produce code for wrong call expressions:
+  if n.len == 0 or n[0].typ.isNil: return
   if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
   let x = c.getTempRange(n.len, slotTempUnknown)
   # varargs need 'opcSetType' for the FFI support:
-  let fntyp = skipTypes(n.sons[0].typ, abstractInst)
+  let fntyp = skipTypes(n[0].typ, abstractInst)
   for i in 0..<n.len:
-    #if i > 0 and i < sonsLen(fntyp):
-    #  let paramType = fntyp.n.sons[i]
-    #  if paramType.typ.isCompileTimeOnly: continue
     var r: TRegister = x+i
-    c.gen(n.sons[i], r)
-    if i >= fntyp.len:
+    if i >= fntyp.signatureLen:
+      c.gen(n[i], r, {gfIsParam})
       internalAssert c.config, tfVarargs in fntyp.flags
-      c.gABx(n, opcSetType, r, c.genType(n.sons[i].typ))
+      c.gABx(n, opcSetType, r, c.genType(n[i].typ))
+    else:
+      if fntyp[i] != nil and fntyp[i].kind == tySink and
+          fntyp[i].skipTypes({tySink}).kind in {tyObject, tyString, tySequence}:
+        c.gen(n[i], r, {gfIsSinkParam})
+      else:
+        c.gen(n[i], r, {gfIsParam})
+
   if dest < 0:
     c.gABC(n, opcIndCall, 0, x, n.len)
   else:
@@ -544,74 +649,95 @@ proc genField(c: PCtx; n: PNode): TRegister =
   if n.kind != nkSym or n.sym.kind != skField:
     globalError(c.config, n.info, "no field symbol")
   let s = n.sym
-  if s.position > high(result):
+  if s.position > high(typeof(result)):
     globalError(c.config, n.info,
         "too large offset! cannot generate code for: " & s.name.s)
   result = s.position
 
 proc genIndex(c: PCtx; n: PNode; arr: PType): TRegister =
   if arr.skipTypes(abstractInst).kind == tyArray and (let x = firstOrd(c.config, arr);
-      x != 0):
+      x != Zero):
     let tmp = c.genx(n)
     # freeing the temporary here means we can produce:  regA = regA - Imm
     c.freeTemp(tmp)
     result = c.getTemp(n.typ)
-    c.gABI(n, opcSubImmInt, result, tmp, x.int)
+    c.gABI(n, opcSubImmInt, result, tmp, toInt(x))
   else:
     result = c.genx(n)
 
+proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags)
+
 proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
   case le.kind
   of nkBracketExpr:
-    let dest = c.genx(le.sons[0], {gfAddrOf, gfFieldAccess})
-    let idx = c.genIndex(le.sons[1], le.sons[0].typ)
-    c.gABC(le, opcWrArr, dest, idx, value)
+    let
+      dest = c.genx(le[0], {gfNode})
+      idx = c.genIndex(le[1], le[0].typ)
+      collTyp = le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc})
+
+    case collTyp.kind
+    of tyString, tyCstring:
+      c.gABC(le, opcWrStrIdx, dest, idx, value)
+    of tyTuple:
+      c.gABC(le, opcWrObj, dest, int le[1].intVal, value)
+    else:
+      c.gABC(le, opcWrArr, dest, idx, value)
+
     c.freeTemp(dest)
     c.freeTemp(idx)
-  of nkDotExpr, nkCheckedFieldExpr:
-    # XXX field checks here
-    let left = if le.kind == nkDotExpr: le else: le.sons[0]
-    let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess})
-    let idx = genField(c, left.sons[1])
-    c.gABC(left, opcWrObj, dest, idx, value)
+  of nkCheckedFieldExpr:
+    var objR: TDest = -1
+    genCheckedObjAccessAux(c, le, objR, {gfNode})
+    let idx = genField(c, le[0][1])
+    c.gABC(le[0], opcWrObj, objR, idx, value)
+    c.freeTemp(objR)
+  of nkDotExpr:
+    let dest = c.genx(le[0], {gfNode})
+    let idx = genField(c, le[1])
+    c.gABC(le, opcWrObj, dest, idx, value)
     c.freeTemp(dest)
   of nkDerefExpr, nkHiddenDeref:
-    let dest = c.genx(le.sons[0], {gfAddrOf})
+    let dest = c.genx(le[0], {gfNode})
     c.gABC(le, opcWrDeref, dest, 0, value)
     c.freeTemp(dest)
   of nkSym:
     if le.sym.isGlobal:
-      let dest = c.genx(le, {gfAddrOf})
+      let dest = c.genx(le, {gfNodeAddr})
       c.gABC(le, opcWrDeref, dest, 0, value)
       c.freeTemp(dest)
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+    if sameBackendType(le.typ, le[1].typ):
+      genAsgnPatch(c, le[1], value)
   else:
     discard
 
 proc genNew(c: PCtx; n: PNode) =
-  let dest = if needsAsgnPatch(n.sons[1]): c.getTemp(n.sons[1].typ)
-             else: c.genx(n.sons[1])
+  let dest = if needsAsgnPatch(n[1]): c.getTemp(n[1].typ)
+             else: c.genx(n[1])
   # we use the ref's base type here as the VM conflates 'ref object'
   # and 'object' since internally we already have a pointer.
   c.gABx(n, opcNew, dest,
-         c.genType(n.sons[1].typ.skipTypes(abstractVar-{tyTypeDesc}).sons[0]))
-  c.genAsgnPatch(n.sons[1], dest)
+         c.genType(n[1].typ.skipTypes(abstractVar-{tyTypeDesc})[0]))
+  c.genAsgnPatch(n[1], dest)
   c.freeTemp(dest)
 
 proc genNewSeq(c: PCtx; n: PNode) =
-  let t = n.sons[1].typ
-  let dest = if needsAsgnPatch(n.sons[1]): c.getTemp(t)
-             else: c.genx(n.sons[1])
-  let tmp = c.genx(n.sons[2])
+  let t = n[1].typ
+  let dest = if needsAsgnPatch(n[1]): c.getTemp(t)
+             else: c.genx(n[1])
+  let tmp = c.genx(n[2])
   c.gABx(n, opcNewSeq, dest, c.genType(t.skipTypes(
                                                   abstractVar-{tyTypeDesc})))
   c.gABx(n, opcNewSeq, tmp, 0)
   c.freeTemp(tmp)
-  c.genAsgnPatch(n.sons[1], dest)
+  c.genAsgnPatch(n[1], dest)
   c.freeTemp(dest)
 
 proc genNewSeqOfCap(c: PCtx; n: PNode; dest: var TDest) =
   let t = n.typ
-  let tmp = c.getTemp(n.sons[1].typ)
+  if dest < 0:
+    dest = c.getTemp(n.typ)
+  let tmp = c.getTemp(n[1].typ)
   c.gABx(n, opcLdNull, dest, c.genType(t))
   c.gABx(n, opcLdImmInt, tmp, 0)
   c.gABx(n, opcNewSeq, dest, c.genType(t.skipTypes(
@@ -620,21 +746,22 @@ proc genNewSeqOfCap(c: PCtx; n: PNode; dest: var TDest) =
   c.freeTemp(tmp)
 
 proc genUnaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
-  let tmp = c.genx(n.sons[1])
+  let tmp = c.genx(n[1])
   if dest < 0: dest = c.getTemp(n.typ)
   c.gABC(n, opc, dest, tmp)
   c.freeTemp(tmp)
 
 proc genUnaryABI(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; imm: BiggestInt=0) =
-  let tmp = c.genx(n.sons[1])
+  let tmp = c.genx(n[1])
   if dest < 0: dest = c.getTemp(n.typ)
   c.gABI(n, opc, dest, tmp, imm)
   c.freeTemp(tmp)
 
+
 proc genBinaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
   let
-    tmp = c.genx(n.sons[1])
-    tmp2 = c.genx(n.sons[2])
+    tmp = c.genx(n[1])
+    tmp2 = c.genx(n[2])
   if dest < 0: dest = c.getTemp(n.typ)
   c.gABC(n, opc, dest, tmp, tmp2)
   c.freeTemp(tmp)
@@ -642,9 +769,9 @@ proc genBinaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
 
 proc genBinaryABCD(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
   let
-    tmp = c.genx(n.sons[1])
-    tmp2 = c.genx(n.sons[2])
-    tmp3 = c.genx(n.sons[3])
+    tmp = c.genx(n[1])
+    tmp2 = c.genx(n[2])
+    tmp3 = c.genx(n[3])
   if dest < 0: dest = c.getTemp(n.typ)
   c.gABC(n, opc, dest, tmp, tmp2)
   c.gABC(n, opc, tmp3)
@@ -652,22 +779,27 @@ proc genBinaryABCD(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
   c.freeTemp(tmp2)
   c.freeTemp(tmp3)
 
+template sizeOfLikeMsg(name): string =
+  "'$1' requires '.importc' types to be '.completeStruct'" % [name]
+
 proc genNarrow(c: PCtx; n: PNode; dest: TDest) =
   let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
   # uint is uint64 in the VM, we we only need to mask the result for
   # other unsigned types:
-  if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and t.size < 8):
-    c.gABC(n, opcNarrowU, dest, TRegister(t.size*8))
-  elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and t.size < 8):
-    c.gABC(n, opcNarrowS, dest, TRegister(t.size*8))
+  let size = getSize(c.config, t)
+  if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8):
+    c.gABC(n, opcNarrowU, dest, TRegister(size*8))
+  elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and size < 8):
+    c.gABC(n, opcNarrowS, dest, TRegister(size*8))
 
 proc genNarrowU(c: PCtx; n: PNode; dest: TDest) =
   let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
   # uint is uint64 in the VM, we we only need to mask the result for
   # other unsigned types:
+  let size = getSize(c.config, t)
   if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32} or
-    (t.kind in {tyUInt, tyInt} and t.size < 8):
-    c.gABC(n, opcNarrowU, dest, TRegister(t.size*8))
+    (t.kind in {tyUInt, tyInt} and size < 8):
+    c.gABC(n, opcNarrowU, dest, TRegister(size*8))
 
 proc genBinaryABCnarrow(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
   genBinaryABC(c, n, dest, opc)
@@ -684,69 +816,87 @@ proc genSetType(c: PCtx; n: PNode; dest: TRegister) =
 
 proc genBinarySet(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
   let
-    tmp = c.genx(n.sons[1])
-    tmp2 = c.genx(n.sons[2])
+    tmp = c.genx(n[1])
+    tmp2 = c.genx(n[2])
   if dest < 0: dest = c.getTemp(n.typ)
-  c.genSetType(n.sons[1], tmp)
-  c.genSetType(n.sons[2], tmp2)
+  c.genSetType(n[1], tmp)
+  c.genSetType(n[2], tmp2)
   c.gABC(n, opc, dest, tmp, tmp2)
   c.freeTemp(tmp)
   c.freeTemp(tmp2)
 
 proc genBinaryStmt(c: PCtx; n: PNode; opc: TOpcode) =
   let
-    dest = c.genx(n.sons[1])
-    tmp = c.genx(n.sons[2])
+    dest = c.genx(n[1])
+    tmp = c.genx(n[2])
   c.gABC(n, opc, dest, tmp, 0)
   c.freeTemp(tmp)
+  c.freeTemp(dest)
 
 proc genBinaryStmtVar(c: PCtx; n: PNode; opc: TOpcode) =
-  var x = n.sons[1]
-  if x.kind in {nkAddr, nkHiddenAddr}: x = x.sons[0]
+  var x = n[1]
+  if x.kind in {nkAddr, nkHiddenAddr}: x = x[0]
   let
     dest = c.genx(x)
-    tmp = c.genx(n.sons[2])
+    tmp = c.genx(n[2])
   c.gABC(n, opc, dest, tmp, 0)
-  #c.genAsgnPatch(n.sons[1], dest)
+  #c.genAsgnPatch(n[1], dest)
   c.freeTemp(tmp)
+  c.freeTemp(dest)
 
 proc genUnaryStmt(c: PCtx; n: PNode; opc: TOpcode) =
-  let tmp = c.genx(n.sons[1])
+  let tmp = c.genx(n[1])
   c.gABC(n, opc, tmp, 0, 0)
   c.freeTemp(tmp)
 
 proc genVarargsABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
   if dest < 0: dest = getTemp(c, n.typ)
   var x = c.getTempRange(n.len-1, slotTempStr)
-  for i in 1..n.len-1:
+  for i in 1..<n.len:
     var r: TRegister = x+i-1
-    c.gen(n.sons[i], r)
+    c.gen(n[i], r)
   c.gABC(n, opc, dest, x, n.len-1)
-  c.freeTempRange(x, n.len)
+  c.freeTempRange(x, n.len-1)
 
 proc isInt8Lit(n: PNode): bool =
   if n.kind in {nkCharLit..nkUInt64Lit}:
     result = n.intVal >= low(int8) and n.intVal <= high(int8)
+  else:
+    result = false
 
 proc isInt16Lit(n: PNode): bool =
   if n.kind in {nkCharLit..nkUInt64Lit}:
     result = n.intVal >= low(int16) and n.intVal <= high(int16)
+  else:
+    result = false
 
 proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
-  if n.sons[2].isInt8Lit:
-    let tmp = c.genx(n.sons[1])
+  if n[2].isInt8Lit:
+    let tmp = c.genx(n[1])
     if dest < 0: dest = c.getTemp(n.typ)
-    c.gABI(n, succ(opc), dest, tmp, n.sons[2].intVal)
+    c.gABI(n, succ(opc), dest, tmp, n[2].intVal)
     c.freeTemp(tmp)
   else:
     genBinaryABC(c, n, dest, opc)
   c.genNarrow(n, dest)
 
-proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
-  if n.typ.kind == arg.typ.kind and arg.typ.kind == tyProc:
-    # don't do anything for lambda lifting conversions:
-    gen(c, arg, dest)
+proc genConv(c: PCtx; n, arg: PNode; dest: var TDest, flags: TGenFlags = {}; opc=opcConv) =
+  let t2 = n.typ.skipTypes({tyDistinct})
+  let targ2 = arg.typ.skipTypes({tyDistinct})
+
+  proc implicitConv(): bool =
+    if sameBackendType(t2, targ2): return true
+    # xxx consider whether to use t2 and targ2 here
+    if n.typ.kind == arg.typ.kind and arg.typ.kind == tyProc:
+      # don't do anything for lambda lifting conversions:
+      result = true
+    else:
+      result = false
+
+  if implicitConv():
+    gen(c, arg, dest, flags)
     return
+
   let tmp = c.genx(arg)
   if dest < 0: dest = c.getTemp(n.typ)
   c.gABC(n, opc, dest, tmp)
@@ -755,54 +905,94 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
   c.freeTemp(tmp)
 
 proc genCard(c: PCtx; n: PNode; dest: var TDest) =
-  let tmp = c.genx(n.sons[1])
+  let tmp = c.genx(n[1])
   if dest < 0: dest = c.getTemp(n.typ)
-  c.genSetType(n.sons[1], tmp)
+  c.genSetType(n[1], tmp)
   c.gABC(n, opcCard, dest, tmp)
   c.freeTemp(tmp)
 
-proc genIntCast(c: PCtx; n: PNode; dest: var TDest) =
-  const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar}
-  var signedIntegers = {tyInt8..tyInt32}
-  var unsignedIntegers = {tyUInt8..tyUInt32, tyChar}
-  let src = n.sons[1].typ.skipTypes(abstractRange)#.kind
-  let dst = n.sons[0].typ.skipTypes(abstractRange)#.kind
-  let src_size = getSize(c.config, src)
-
-  if c.config.target.intSize < 8:
-    signedIntegers.incl(tyInt)
-    unsignedIntegers.incl(tyUInt)
-  if src_size == getSize(c.config, dst) and src.kind in allowedIntegers and
-                                 dst.kind in allowedIntegers:
-    let tmp = c.genx(n.sons[1])
-    var tmp2 = c.getTemp(n.sons[1].typ)
-    let tmp3 = c.getTemp(n.sons[1].typ)
+proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) =
+  template isSigned(typ: PType): bool {.dirty.} =
+    typ.kind == tyEnum and firstOrd(c.config, typ) < 0 or
+    typ.kind in {tyInt..tyInt64}
+  template isUnsigned(typ: PType): bool {.dirty.} =
+    typ.kind == tyEnum and firstOrd(c.config, typ) >= 0 or
+    typ.kind in {tyUInt..tyUInt64, tyChar, tyBool}
+
+  const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar, tyEnum, tyBool}
+
+  let src = n[1].typ.skipTypes(abstractRange)#.kind
+  let dst = n[0].typ.skipTypes(abstractRange)#.kind
+  let srcSize = getSize(c.config, src)
+  let dstSize = getSize(c.config, dst)
+  const unsupportedCastDifferentSize =
+    "VM does not support 'cast' from $1 with size $2 to $3 with size $4 due to different sizes"
+  if src.kind in allowedIntegers and dst.kind in allowedIntegers:
+    let tmp = c.genx(n[1])
     if dest < 0: dest = c.getTemp(n[0].typ)
-    proc mkIntLit(ival: int): int =
-      result = genLiteral(c, newIntTypeNode(nkIntLit, ival, getSysType(c.graph, n.info, tyInt)))
-    if src.kind in unsignedIntegers and dst.kind in signedIntegers:
-      # cast unsigned to signed integer of same size
-      # signedVal = (unsignedVal xor offset) -% offset
-      let offset = 1 shl (src_size * 8 - 1)
-      c.gABx(n, opcLdConst, tmp2, mkIntLit(offset))
-      c.gABC(n, opcBitxorInt, tmp3, tmp, tmp2)
-      c.gABC(n, opcSubInt, dest, tmp3, tmp2)
-    elif src.kind in signedIntegers and dst.kind in unsignedIntegers:
-      # cast signed to unsigned integer of same size
-      # unsignedVal = (offset +% signedVal +% 1) and offset
-      let offset = (1 shl (src_size * 8)) - 1
-      c.gABx(n, opcLdConst, tmp2, mkIntLit(offset))
-      c.gABx(n, opcLdConst, dest, mkIntLit(offset+1))
-      c.gABC(n, opcAddu, tmp3, tmp, dest)
-      c.gABC(n, opcNarrowU, tmp3, TRegister(src_size*8))
-      c.gABC(n, opcBitandInt, dest, tmp3, tmp2)
+    c.gABC(n, opcAsgnInt, dest, tmp)
+    if dstSize != sizeof(BiggestInt): # don't do anything on biggest int types
+      if isSigned(dst): # we need to do sign extensions
+        if dstSize <= srcSize:
+          # Sign extension can be omitted when the size increases.
+          c.gABC(n, opcSignExtend, dest, TRegister(dstSize*8))
+      elif isUnsigned(dst):
+        if isSigned(src) or dstSize < srcSize:
+          # Cast from signed to unsigned always needs narrowing. Cast
+          # from unsigned to unsigned only needs narrowing when target
+          # is smaller than source.
+          c.gABC(n, opcNarrowU, dest, TRegister(dstSize*8))
+    c.freeTemp(tmp)
+  elif src.kind in allowedIntegers and
+      dst.kind in {tyFloat, tyFloat32, tyFloat64}:
+    if srcSize != dstSize:
+      globalError(c.config, n.info, unsupportedCastDifferentSize %
+        [$src.kind, $srcSize, $dst.kind, $dstSize])
+    let tmp = c.genx(n[1])
+    if dest < 0: dest = c.getTemp(n[0].typ)
+    if dst.kind == tyFloat32:
+      c.gABC(n, opcCastIntToFloat32, dest, tmp)
     else:
-      c.gABC(n, opcAsgnInt, dest, tmp)
+      c.gABC(n, opcCastIntToFloat64, dest, tmp)
     c.freeTemp(tmp)
-    c.freeTemp(tmp2)
-    c.freeTemp(tmp3)
+
+  elif src.kind in {tyFloat, tyFloat32, tyFloat64} and
+                           dst.kind in allowedIntegers:
+    if srcSize != dstSize:
+      globalError(c.config, n.info, unsupportedCastDifferentSize %
+        [$src.kind, $srcSize, $dst.kind, $dstSize])
+    let tmp = c.genx(n[1])
+    if dest < 0: dest = c.getTemp(n[0].typ)
+    if src.kind == tyFloat32:
+      c.gABC(n, opcCastFloatToInt32, dest, tmp)
+      if isUnsigned(dst):
+        # integers are sign extended by default.
+        # since there is no opcCastFloatToUInt32, narrowing should do the trick.
+        c.gABC(n, opcNarrowU, dest, TRegister(32))
+    else:
+      c.gABC(n, opcCastFloatToInt64, dest, tmp)
+      # narrowing for 64 bits not needed (no extended sign bits available).
+    c.freeTemp(tmp)
+  elif src.kind in PtrLikeKinds + {tyRef} and dst.kind == tyInt:
+    let tmp = c.genx(n[1])
+    if dest < 0: dest = c.getTemp(n[0].typ)
+    var imm: BiggestInt = if src.kind in PtrLikeKinds: 1 else: 2
+    c.gABI(n, opcCastPtrToInt, dest, tmp, imm)
+    c.freeTemp(tmp)
+  elif src.kind in PtrLikeKinds + {tyInt} and dst.kind in PtrLikeKinds:
+    let tmp = c.genx(n[1])
+    if dest < 0: dest = c.getTemp(n[0].typ)
+    c.gABx(n, opcSetType, dest, c.genType(dst))
+    c.gABC(n, opcCastIntToPtr, dest, tmp)
+    c.freeTemp(tmp)
+  elif src.kind == tyNil and dst.kind in NilableTypes:
+    # supports casting nil literals to NilableTypes in VM
+    # see #16024
+    if dest < 0: dest = c.getTemp(n[0].typ)
+    genLit(c, n[1], dest)
   else:
-    globalError(c.config, n.info, "VM is only allowed to 'cast' between integers of same size")
+    # todo: support cast from tyInt to tyRef
+    globalError(c.config, n.info, "VM does not support 'cast' from " & $src.kind & " to " & $dst.kind)
 
 proc genVoidABC(c: PCtx, n: PNode, dest: TDest, opcode: TOpcode) =
   unused(c, n, dest)
@@ -815,33 +1005,91 @@ proc genVoidABC(c: PCtx, n: PNode, dest: TDest, opcode: TOpcode) =
   c.freeTemp(tmp2)
   c.freeTemp(tmp3)
 
-proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
+proc genBindSym(c: PCtx; n: PNode; dest: var TDest) =
+  # nah, cannot use c.config.features because sempass context
+  # can have local experimental switch
+  # if dynamicBindSym notin c.config.features:
+  if n.len == 2: # hmm, reliable?
+    # bindSym with static input
+    if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkSym}:
+      let idx = c.genLiteral(n[1])
+      if dest < 0: dest = c.getTemp(n.typ)
+      c.gABx(n, opcNBindSym, dest, idx)
+    else:
+      localError(c.config, n.info, "invalid bindSym usage")
+  else:
+    # experimental bindSym
+    if dest < 0: dest = c.getTemp(n.typ)
+    let x = c.getTempRange(n.len, slotTempUnknown)
+
+    # callee symbol
+    var tmp0 = TDest(x)
+    c.genLit(n[0], tmp0)
+
+    # original parameters
+    for i in 1..<n.len-2:
+      var r = TRegister(x+i)
+      c.gen(n[i], r)
+
+    # info node
+    var tmp1 = TDest(x+n.len-2)
+    c.genLit(n[^2], tmp1)
+
+    # payload idx
+    var tmp2 = TDest(x+n.len-1)
+    c.genLit(n[^1], tmp2)
+
+    c.gABC(n, opcNDynBindSym, dest, x, n.len)
+    c.freeTempRange(x, n.len)
+
+proc fitsRegister*(t: PType): bool =
+  assert t != nil
+  t.skipTypes(abstractInst + {tyStatic} - {tyTypeDesc}).kind in {
+    tyRange, tyEnum, tyBool, tyInt..tyUInt64, tyChar}
+
+proc ldNullOpcode(t: PType): TOpcode =
+  assert t != nil
+  if fitsRegister(t): opcLdNullReg else: opcLdNull
+
+proc whichAsgnOpc(n: PNode; requiresCopy = true): TOpcode =
+  case n.typ.skipTypes(abstractRange+{tyOwned}-{tyTypeDesc}).kind
+  of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64:
+    opcAsgnInt
+  of tyFloat..tyFloat128:
+    opcAsgnFloat
+  of tyRef, tyNil, tyVar, tyLent, tyPtr:
+    opcAsgnRef
+  else:
+    (if requiresCopy: opcAsgnComplex else: opcFastAsgnComplex)
+
+proc genMagic(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}, m: TMagic) =
   case m
   of mAnd: c.genAndOr(n, opcFJmp, dest)
   of mOr:  c.genAndOr(n, opcTJmp, dest)
-  of mUnaryLt:
-    let tmp = c.genx(n.sons[1])
-    if dest < 0: dest = c.getTemp(n.typ)
-    c.gABI(n, opcSubImmInt, dest, tmp, 1)
-    c.freeTemp(tmp)
   of mPred, mSubI:
     c.genAddSubInt(n, dest, opcSubInt)
   of mSucc, mAddI:
     c.genAddSubInt(n, dest, opcAddInt)
   of mInc, mDec:
     unused(c, n, dest)
-    let opc = if m == mInc: opcAddInt else: opcSubInt
-    let d = c.genx(n.sons[1])
-    if n.sons[2].isInt8Lit:
-      c.gABI(n, succ(opc), d, d, n.sons[2].intVal)
+    let isUnsigned = n[1].typ.skipTypes(abstractVarRange).kind in {tyUInt..tyUInt64}
+    let opc = if not isUnsigned:
+                if m == mInc: opcAddInt else: opcSubInt
+              else:
+                if m == mInc: opcAddu else: opcSubu
+    let d = c.genx(n[1])
+    if n[2].isInt8Lit and not isUnsigned:
+      c.gABI(n, succ(opc), d, d, n[2].intVal)
     else:
-      let tmp = c.genx(n.sons[2])
+      let tmp = c.genx(n[2])
       c.gABC(n, opc, d, d, tmp)
       c.freeTemp(tmp)
-    c.genNarrow(n.sons[1], d)
-    c.genAsgnPatch(n.sons[1], d)
+    c.genNarrow(n[1], d)
+    c.genAsgnPatch(n[1], d)
     c.freeTemp(d)
-  of mOrd, mChr, mArrToSeq: c.gen(n.sons[1], dest)
+  of mOrd, mChr, mArrToSeq, mUnown: c.gen(n[1], dest)
+  of generatedMagics:
+    genCall(c, n, dest)
   of mNew, mNewFinalize:
     unused(c, n, dest)
     c.genNew(n)
@@ -854,23 +1102,38 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     # XXX buggy
   of mNewStringOfCap:
     # we ignore the 'cap' argument and translate it as 'newString(0)'.
-    # eval n.sons[1] for possible side effects:
-    c.freeTemp(c.genx(n.sons[1]))
-    var tmp = c.getTemp(n.sons[1].typ)
+    # eval n[1] for possible side effects:
+    c.freeTemp(c.genx(n[1]))
+    var tmp = c.getTemp(n[1].typ)
     c.gABx(n, opcLdImmInt, tmp, 0)
     if dest < 0: dest = c.getTemp(n.typ)
     c.gABC(n, opcNewStr, dest, tmp)
     c.freeTemp(tmp)
     # XXX buggy
-  of mLengthOpenArray, mLengthArray, mLengthSeq, mXLenSeq:
+  of mLengthOpenArray, mLengthArray, mLengthSeq:
     genUnaryABI(c, n, dest, opcLenSeq)
-  of mLengthStr, mXLenStr:
-    genUnaryABI(c, n, dest, opcLenStr)
+  of mLengthStr:
+    case n[1].typ.skipTypes(abstractVarRange).kind
+    of tyString: genUnaryABI(c, n, dest, opcLenStr)
+    of tyCstring: genUnaryABI(c, n, dest, opcLenCstring)
+    else: raiseAssert $n[1].typ.kind
+  of mSlice:
+    var
+      d = c.genx(n[1])
+      left = c.genIndex(n[2], n[1].typ)
+      right = c.genIndex(n[3], n[1].typ)
+    if dest < 0: dest = c.getTemp(n.typ)
+    c.gABC(n, opcNodeToReg, dest, d)
+    c.gABC(n, opcSlice, dest, left, right)
+    c.freeTemp(left)
+    c.freeTemp(right)
+    c.freeTemp(d)
+
   of mIncl, mExcl:
     unused(c, n, dest)
-    var d = c.genx(n.sons[1])
-    var tmp = c.genx(n.sons[2])
-    c.genSetType(n.sons[1], d)
+    var d = c.genx(n[1])
+    var tmp = c.genx(n[2])
+    c.genSetType(n[1], d)
     c.gABC(n, if m == mIncl: opcIncl else: opcExcl, d, tmp)
     c.freeTemp(d)
     c.freeTemp(tmp)
@@ -883,26 +1146,28 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mMulF64: genBinaryABC(c, n, dest, opcMulFloat)
   of mDivF64: genBinaryABC(c, n, dest, opcDivFloat)
   of mShrI:
-    # the idea here is to narrow type if needed before executing right shift
-    # inlined modified: genNarrowU(c, n, dest)
-    let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
-    # uint is uint64 in the VM, we we only need to mask the result for
-    # other unsigned types:
-    let tmp = c.genx(n.sons[1])
-    if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32}:
-      c.gABC(n, opcNarrowU, tmp, TRegister(t.size*8))
-
-    # inlined modified: genBinaryABC(c, n, dest, opcShrInt)
-    let tmp2 = c.genx(n.sons[2])
+    # modified: genBinaryABC(c, n, dest, opcShrInt)
+    # narrowU is applied to the left operandthe idea here is to narrow the left operand
+    let tmp = c.genx(n[1])
+    c.genNarrowU(n, tmp)
+    let tmp2 = c.genx(n[2])
     if dest < 0: dest = c.getTemp(n.typ)
     c.gABC(n, opcShrInt, dest, tmp, tmp2)
     c.freeTemp(tmp)
     c.freeTemp(tmp2)
-
-  of mShlI: genBinaryABCnarrowU(c, n, dest, opcShlInt)
-  of mBitandI: genBinaryABCnarrowU(c, n, dest, opcBitandInt)
-  of mBitorI: genBinaryABCnarrowU(c, n, dest, opcBitorInt)
-  of mBitxorI: genBinaryABCnarrowU(c, n, dest, opcBitxorInt)
+  of mShlI:
+    genBinaryABC(c, n, dest, opcShlInt)
+    # genNarrowU modified
+    let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
+    let size = getSize(c.config, t)
+    if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8):
+      c.gABC(n, opcNarrowU, dest, TRegister(size*8))
+    elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and size < 8):
+      c.gABC(n, opcSignExtend, dest, TRegister(size*8))
+  of mAshrI: genBinaryABC(c, n, dest, opcAshrInt)
+  of mBitandI: genBinaryABC(c, n, dest, opcBitandInt)
+  of mBitorI: genBinaryABC(c, n, dest, opcBitorInt)
+  of mBitxorI: genBinaryABC(c, n, dest, opcBitxorInt)
   of mAddU: genBinaryABCnarrowU(c, n, dest, opcAddu)
   of mSubU: genBinaryABCnarrowU(c, n, dest, opcSubu)
   of mMulU: genBinaryABCnarrowU(c, n, dest, opcMulu)
@@ -917,40 +1182,28 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mEqF64: genBinaryABC(c, n, dest, opcEqFloat)
   of mLeF64: genBinaryABC(c, n, dest, opcLeFloat)
   of mLtF64: genBinaryABC(c, n, dest, opcLtFloat)
-  of mLePtr, mLeU, mLeU64: genBinaryABC(c, n, dest, opcLeu)
-  of mLtPtr, mLtU, mLtU64: genBinaryABC(c, n, dest, opcLtu)
-  of mEqProc, mEqRef, mEqUntracedRef:
+  of mLePtr, mLeU: genBinaryABC(c, n, dest, opcLeu)
+  of mLtPtr, mLtU: genBinaryABC(c, n, dest, opcLtu)
+  of mEqProc, mEqRef:
     genBinaryABC(c, n, dest, opcEqRef)
-  of mXor: genBinaryABCnarrowU(c, n, dest, opcXor)
+  of mXor: genBinaryABC(c, n, dest, opcXor)
   of mNot: genUnaryABC(c, n, dest, opcNot)
   of mUnaryMinusI, mUnaryMinusI64:
     genUnaryABC(c, n, dest, opcUnaryMinusInt)
     genNarrow(c, n, dest)
   of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat)
-  of mUnaryPlusI, mUnaryPlusF64: gen(c, n.sons[1], dest)
+  of mUnaryPlusI, mUnaryPlusF64: gen(c, n[1], dest)
   of mBitnotI:
     genUnaryABC(c, n, dest, opcBitnotInt)
-    genNarrowU(c, n, dest)
-  of mToFloat, mToBiggestFloat, mToInt,
-     mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr,
-     mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr:
-    genConv(c, n, n.sons[1], dest)
-  of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
-    #genNarrowU modified
-    let t = skipTypes(n.sons[1].typ, abstractVar-{tyTypeDesc})
-    let tmp = c.genx(n.sons[1])
-    c.gABC(n, opcNarrowU, tmp, TRegister(t.size*8))
-    # assign result to dest register
-    if dest < 0: dest = c.getTemp(n.typ)
-    c.gABC(n, opcAsgnInt, dest, tmp)
-    c.freeTemp(tmp)
-  of mToU8, mToU16, mToU32:
+    #genNarrowU modified, do not narrow signed types
     let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
-    var tmp = c.genx(n.sons[1])
-    if dest < 0: dest = c.getTemp(n.typ)
-    c.gABC(n, opcToNarrowInt, dest, tmp, TRegister(t.size*8))
-    c.freeTemp(tmp)
-  of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr)
+    let size = getSize(c.config, t)
+    if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8):
+      c.gABC(n, opcNarrowU, dest, TRegister(size*8))
+  of mCharToStr, mBoolToStr, mCStrToStr, mStrToStr, mEnumToStr:
+    genConv(c, n, n[1], dest, flags)
+  of mEqStr: genBinaryABC(c, n, dest, opcEqStr)
+  of mEqCString: genBinaryABC(c, n, dest, opcEqCString)
   of mLeStr: genBinaryABC(c, n, dest, opcLeStr)
   of mLtStr: genBinaryABC(c, n, dest, opcLtStr)
   of mEqSet: genBinarySet(c, n, dest, opcEqSet)
@@ -959,102 +1212,73 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mMulSet: genBinarySet(c, n, dest, opcMulSet)
   of mPlusSet: genBinarySet(c, n, dest, opcPlusSet)
   of mMinusSet: genBinarySet(c, n, dest, opcMinusSet)
-  of mSymDiffSet: genBinarySet(c, n, dest, opcSymdiffSet)
   of mConStrStr: genVarargsABC(c, n, dest, opcConcatStr)
   of mInSet: genBinarySet(c, n, dest, opcContainsSet)
   of mRepr: genUnaryABC(c, n, dest, opcRepr)
   of mExit:
     unused(c, n, dest)
-    var tmp = c.genx(n.sons[1])
+    var tmp = c.genx(n[1])
     c.gABC(n, opcQuit, tmp)
     c.freeTemp(tmp)
   of mSetLengthStr, mSetLengthSeq:
     unused(c, n, dest)
-    var d = c.genx(n.sons[1])
-    var tmp = c.genx(n.sons[2])
+    var d = c.genx(n[1])
+    var tmp = c.genx(n[2])
     c.gABC(n, if m == mSetLengthStr: opcSetLenStr else: opcSetLenSeq, d, tmp)
-    c.genAsgnPatch(n.sons[1], d)
+    c.genAsgnPatch(n[1], d)
     c.freeTemp(tmp)
+    c.freeTemp(d)
   of mSwap:
     unused(c, n, dest)
-    c.gen(lowerSwap(c.graph, n, if c.prc == nil: c.module else: c.prc.sym))
+    c.gen(lowerSwap(c.graph, n, c.idgen, if c.prc == nil or c.prc.sym == nil: c.module else: c.prc.sym))
   of mIsNil: genUnaryABC(c, n, dest, opcIsNil)
-  of mCopyStr:
-    if dest < 0: dest = c.getTemp(n.typ)
-    var
-      tmp1 = c.genx(n.sons[1])
-      tmp2 = c.genx(n.sons[2])
-      tmp3 = c.getTemp(n.sons[2].typ)
-    c.gABC(n, opcLenStr, tmp3, tmp1)
-    c.gABC(n, opcSubStr, dest, tmp1, tmp2)
-    c.gABC(n, opcSubStr, tmp3)
-    c.freeTemp(tmp1)
-    c.freeTemp(tmp2)
-    c.freeTemp(tmp3)
-  of mCopyStrLast:
-    if dest < 0: dest = c.getTemp(n.typ)
-    var
-      tmp1 = c.genx(n.sons[1])
-      tmp2 = c.genx(n.sons[2])
-      tmp3 = c.genx(n.sons[3])
-    c.gABC(n, opcSubStr, dest, tmp1, tmp2)
-    c.gABC(n, opcSubStr, tmp3)
-    c.freeTemp(tmp1)
-    c.freeTemp(tmp2)
-    c.freeTemp(tmp3)
   of mParseBiggestFloat:
     if dest < 0: dest = c.getTemp(n.typ)
     var d2: TRegister
     # skip 'nkHiddenAddr':
-    let d2AsNode = n.sons[2].sons[0]
+    let d2AsNode = n[2][0]
     if needsAsgnPatch(d2AsNode):
       d2 = c.getTemp(getSysType(c.graph, n.info, tyFloat))
     else:
       d2 = c.genx(d2AsNode)
     var
-      tmp1 = c.genx(n.sons[1])
-      tmp3 = c.genx(n.sons[3])
+      tmp1 = c.genx(n[1])
     c.gABC(n, opcParseFloat, dest, tmp1, d2)
-    c.gABC(n, opcParseFloat, tmp3)
     c.freeTemp(tmp1)
-    c.freeTemp(tmp3)
     c.genAsgnPatch(d2AsNode, d2)
     c.freeTemp(d2)
-  of mReset:
-    unused(c, n, dest)
-    var d = c.genx(n.sons[1])
-    c.gABC(n, opcReset, d)
+  of mDefault, mZeroDefault:
+    if dest < 0: dest = c.getTemp(n.typ)
+    c.gABx(n, ldNullOpcode(n.typ), dest, c.genType(n.typ))
   of mOf, mIs:
     if dest < 0: dest = c.getTemp(n.typ)
-    var tmp = c.genx(n.sons[1])
+    var tmp = c.genx(n[1])
     var idx = c.getTemp(getSysType(c.graph, n.info, tyInt))
-    var typ = n.sons[2].typ
-    if m == mOf: typ = typ.skipTypes(abstractPtrs-{tyTypeDesc})
+    var typ = n[2].typ
+    if m == mOf: typ = typ.skipTypes(abstractPtrs)
     c.gABx(n, opcLdImmInt, idx, c.genType(typ))
     c.gABC(n, if m == mOf: opcOf else: opcIs, dest, tmp, idx)
     c.freeTemp(tmp)
     c.freeTemp(idx)
-  of mSizeOf:
-    globalError(c.config, n.info, "cannot run in the VM: " & renderTree(n))
   of mHigh:
     if dest < 0: dest = c.getTemp(n.typ)
-    let tmp = c.genx(n.sons[1])
-    case n.sons[1].typ.skipTypes(abstractVar-{tyTypeDesc}).kind:
-    of tyString, tyCString:
-      c.gABI(n, opcLenStr, dest, tmp, 1)
-    else:
-      c.gABI(n, opcLenSeq, dest, tmp, 1)
+    let tmp = c.genx(n[1])
+    case n[1].typ.skipTypes(abstractVar-{tyTypeDesc}).kind:
+    of tyString: c.gABI(n, opcLenStr, dest, tmp, 1)
+    of tyCstring: c.gABI(n, opcLenCstring, dest, tmp, 1)
+    else: c.gABI(n, opcLenSeq, dest, tmp, 1)
     c.freeTemp(tmp)
   of mEcho:
     unused(c, n, dest)
     let n = n[1].skipConv
-    let x = c.getTempRange(n.len, slotTempUnknown)
-    internalAssert c.config, n.kind == nkBracket
-    for i in 0..<n.len:
-      var r: TRegister = x+i
-      c.gen(n.sons[i], r)
-    c.gABC(n, opcEcho, x, n.len)
-    c.freeTempRange(x, n.len)
+    if n.kind == nkBracket:
+      # can happen for nim check, see bug #9609
+      let x = c.getTempRange(n.len, slotTempUnknown)
+      for i in 0..<n.len:
+        var r: TRegister = x+i
+        c.gen(n[i], r)
+      c.gABC(n, opcEcho, x, n.len)
+      c.freeTempRange(x, n.len)
   of mAppendStrCh:
     unused(c, n, dest)
     genBinaryStmtVar(c, n, opcAddStrCh)
@@ -1065,19 +1289,22 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     unused(c, n, dest)
     genBinaryStmtVar(c, n, opcAddSeqElem)
   of mParseExprToAst:
-    genUnaryABC(c, n, dest, opcParseExprToAst)
+    genBinaryABC(c, n, dest, opcParseExprToAst)
   of mParseStmtToAst:
-    genUnaryABC(c, n, dest, opcParseStmtToAst)
+    genBinaryABC(c, n, dest, opcParseStmtToAst)
   of mTypeTrait:
-    let tmp = c.genx(n.sons[1])
+    let tmp = c.genx(n[1])
     if dest < 0: dest = c.getTemp(n.typ)
-    c.gABx(n, opcSetType, tmp, c.genType(n.sons[1].typ))
+    c.gABx(n, opcSetType, tmp, c.genType(n[1].typ))
     c.gABC(n, opcTypeTrait, dest, tmp)
     c.freeTemp(tmp)
   of mSlurp: genUnaryABC(c, n, dest, opcSlurp)
   of mStaticExec: genBinaryABCD(c, n, dest, opcGorge)
   of mNLen: genUnaryABI(c, n, dest, opcLenSeq, nimNodeFlag)
   of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl)
+  of mGetImplTransf: genUnaryABC(c, n, dest, opcGetImplTransf)
+  of mSymOwner: genUnaryABC(c, n, dest, opcSymOwner)
+  of mSymIsInstantiationOf: genBinaryABC(c, n, dest, opcSymIsInstantiationOf)
   of mNChild: genBinaryABC(c, n, dest, opcNChild)
   of mNSetChild: genVoidABC(c, n, dest, opcNSetChild)
   of mNDel: genVoidABC(c, n, dest, opcNDel)
@@ -1103,7 +1330,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mNSymbol: genUnaryABC(c, n, dest, opcNSymbol)
   of mNIdent: genUnaryABC(c, n, dest, opcNIdent)
   of mNGetType:
-    let tmp = c.genx(n.sons[1])
+    let tmp = c.genx(n[1])
     if dest < 0: dest = c.getTemp(n.typ)
     let rc = case n[0].sym.name.s:
       of "getType": 0
@@ -1113,7 +1340,14 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     c.gABC(n, opcNGetType, dest, tmp, rc)
     c.freeTemp(tmp)
     #genUnaryABC(c, n, dest, opcNGetType)
+  of mNSizeOf:
+    let imm = case n[0].sym.name.s:
+      of "getSize": 0
+      of "getAlign": 1
+      else: 2 # "getOffset"
+    c.genUnaryABI(n, dest, opcNGetSize, imm)
   of mNStrVal: genUnaryABC(c, n, dest, opcNStrVal)
+  of mNSigHash: genUnaryABC(c, n , dest, opcNSigHash)
   of mNSetIntVal:
     unused(c, n, dest)
     genBinaryStmt(c, n, opcNSetIntVal)
@@ -1126,42 +1360,45 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mNSetIdent:
     unused(c, n, dest)
     genBinaryStmt(c, n, opcNSetIdent)
-  of mNSetType:
-    unused(c, n, dest)
-    genBinaryStmt(c, n, opcNSetType)
   of mNSetStrVal:
     unused(c, n, dest)
     genBinaryStmt(c, n, opcNSetStrVal)
   of mNNewNimNode: genBinaryABC(c, n, dest, opcNNewNimNode)
   of mNCopyNimNode: genUnaryABC(c, n, dest, opcNCopyNimNode)
   of mNCopyNimTree: genUnaryABC(c, n, dest, opcNCopyNimTree)
-  of mNBindSym:
-    if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkSym}:
-      let idx = c.genLiteral(n[1])
-      if dest < 0: dest = c.getTemp(n.typ)
-      c.gABx(n, opcNBindSym, dest, idx)
-    else:
-      localError(c.config, n.info, "invalid bindSym usage")
+  of mNBindSym: genBindSym(c, n, dest)
   of mStrToIdent: genUnaryABC(c, n, dest, opcStrToIdent)
   of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent)
-  of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode)
+  of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimNode)
   of mSameNodeType: genBinaryABC(c, n, dest, opcSameNodeType)
   of mNLineInfo:
     case n[0].sym.name.s
-    of "getFile":
-      genUnaryABC(c, n, dest, opcNGetFile)
-    of "getLine":
-      genUnaryABC(c, n, dest, opcNGetLine)
-    of "getColumn":
-      genUnaryABC(c, n, dest, opcNGetColumn)
-    else:
-      internalAssert c.config, false
+    of "getFile": genUnaryABI(c, n, dest, opcNGetLineInfo, 0)
+    of "getLine": genUnaryABI(c, n, dest, opcNGetLineInfo, 1)
+    of "getColumn": genUnaryABI(c, n, dest, opcNGetLineInfo, 2)
+    of "copyLineInfo":
+      internalAssert c.config, n.len == 3
+      unused(c, n, dest)
+      genBinaryStmt(c, n, opcNCopyLineInfo)
+    of "setLine":
+      internalAssert c.config, n.len == 3
+      unused(c, n, dest)
+      genBinaryStmt(c, n, opcNSetLineInfoLine)
+    of "setColumn":
+      internalAssert c.config, n.len == 3
+      unused(c, n, dest)
+      genBinaryStmt(c, n, opcNSetLineInfoColumn)
+    of "setFile":
+      internalAssert c.config, n.len == 3
+      unused(c, n, dest)
+      genBinaryStmt(c, n, opcNSetLineInfoFile)
+    else: internalAssert c.config, false
   of mNHint:
     unused(c, n, dest)
-    genUnaryStmt(c, n, opcNHint)
+    genBinaryStmt(c, n, opcNHint)
   of mNWarning:
     unused(c, n, dest)
-    genUnaryStmt(c, n, opcNWarning)
+    genBinaryStmt(c, n, opcNWarning)
   of mNError:
     if n.len <= 1:
       # query error condition:
@@ -1174,13 +1411,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     if dest < 0: dest = c.getTemp(n.typ)
     c.gABC(n, opcCallSite, dest)
   of mNGenSym: genBinaryABC(c, n, dest, opcGenSym)
-  of mMinI, mMaxI, mAbsF64, mMinF64, mMaxF64, mAbsI,
-     mDotDot:
+  of mMinI, mMaxI, mAbsI, mDotDot:
     c.genCall(n, dest)
   of mExpandToAst:
     if n.len != 2:
       globalError(c.config, n.info, "expandToAst requires 1 argument")
-    let arg = n.sons[1]
+    let arg = n[1]
     if arg.kind in nkCallKinds:
       #if arg[0].kind != nkSym or arg[0].sym.kind notin {skTemplate, skMacro}:
       #      "ExpandToAst: expanded symbol is no macro or template"
@@ -1190,150 +1426,131 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
       # produces a value
     else:
       globalError(c.config, n.info, "expandToAst requires a call expression")
+  of mSizeOf:
+    globalError(c.config, n.info, sizeOfLikeMsg("sizeof"))
+  of mAlignOf:
+    globalError(c.config, n.info, sizeOfLikeMsg("alignof"))
+  of mOffsetOf:
+    globalError(c.config, n.info, sizeOfLikeMsg("offsetof"))
   of mRunnableExamples:
     discard "just ignore any call to runnableExamples"
+  of mDestroy, mTrace: discard "ignore calls to the default destructor"
+  of mEnsureMove:
+    gen(c, n[1], dest)
+  of mMove:
+    let arg = n[1]
+    let a = c.genx(arg)
+    if dest < 0: dest = c.getTemp(arg.typ)
+    gABC(c, arg, whichAsgnOpc(arg, requiresCopy=false), dest, a)
+    # XXX use ldNullOpcode() here?
+    # Don't zero out the arg for now #17199
+    # c.gABx(n, opcLdNull, a, c.genType(arg.typ))
+    # c.gABx(n, opcNodeToReg, a, a)
+    # c.genAsgnPatch(arg, a)
+    c.freeTemp(a)
+  of mDup:
+    let arg = n[1]
+    let a = c.genx(arg)
+    if dest < 0: dest = c.getTemp(arg.typ)
+    gABC(c, arg, whichAsgnOpc(arg, requiresCopy=false), dest, a)
+    c.freeTemp(a)
+  of mNodeId:
+    c.genUnaryABC(n, dest, opcNodeId)
   else:
     # mGCref, mGCunref,
     globalError(c.config, n.info, "cannot generate code for: " & $m)
 
-proc genMarshalLoad(c: PCtx, n: PNode, dest: var TDest) =
-  ## Signature: proc to*[T](data: string): T
-  if dest < 0: dest = c.getTemp(n.typ)
-  var tmp = c.genx(n.sons[1])
-  c.gABC(n, opcMarshalLoad, dest, tmp)
-  c.gABx(n, opcMarshalLoad, 0, c.genType(n.typ))
-  c.freeTemp(tmp)
-
-proc genMarshalStore(c: PCtx, n: PNode, dest: var TDest) =
-  ## Signature: proc `$$`*[T](x: T): string
-  if dest < 0: dest = c.getTemp(n.typ)
-  var tmp = c.genx(n.sons[1])
-  c.gABC(n, opcMarshalStore, dest, tmp)
-  c.gABx(n, opcMarshalStore, 0, c.genType(n.sons[1].typ))
-  c.freeTemp(tmp)
-
-const
-  atomicTypes = {tyBool, tyChar,
-    tyExpr, tyStmt, tyTypeDesc, tyStatic,
-    tyEnum,
-    tyOrdinal,
-    tyRange,
-    tyProc,
-    tyPointer, tyOpenArray,
-    tyString, tyCString,
-    tyInt, tyInt8, tyInt16, tyInt32, tyInt64,
-    tyFloat, tyFloat32, tyFloat64, tyFloat128,
-    tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64}
-
-proc fitsRegister*(t: PType): bool =
-  assert t != nil
-  t.skipTypes(abstractInst-{tyTypeDesc}).kind in {
-    tyRange, tyEnum, tyBool, tyInt..tyUInt64, tyChar}
-
 proc unneededIndirection(n: PNode): bool =
-  n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind == tyRef
+  n.typ.skipTypes(abstractInstOwned-{tyTypeDesc}).kind == tyRef
 
-proc canElimAddr(n: PNode): PNode =
-  case n.sons[0].kind
+proc canElimAddr(n: PNode; idgen: IdGenerator): PNode =
+  result = nil
+  case n[0].kind
   of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
-    var m = n.sons[0].sons[0]
+    var m = n[0][0]
     if m.kind in {nkDerefExpr, nkHiddenDeref}:
       # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
-      result = copyNode(n.sons[0])
-      result.add m.sons[0]
+      result = copyNode(n[0])
+      result.add m[0]
+      if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
+        result.typ = n.typ
+      elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
+        result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen)
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
-    var m = n.sons[0].sons[1]
+    var m = n[0][1]
     if m.kind in {nkDerefExpr, nkHiddenDeref}:
       # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
-      result = copyNode(n.sons[0])
-      result.add m.sons[0]
+      result = copyNode(n[0])
+      result.add n[0][0]
+      result.add m[0]
+      if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
+        result.typ = n.typ
+      elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
+        result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen)
   else:
-    if n.sons[0].kind in {nkDerefExpr, nkHiddenDeref}:
+    if n[0].kind in {nkDerefExpr, nkHiddenDeref}:
       # addr ( deref ( x )) --> x
-      result = n.sons[0].sons[0]
+      result = n[0][0]
 
-proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
-                  flags: TGenFlags) =
-  # a nop for certain types
-  let isAddr = opc in {opcAddrNode, opcAddrReg}
-  if isAddr and (let m = canElimAddr(n); m != nil):
+proc genAddr(c: PCtx, n: PNode, dest: var TDest, flags: TGenFlags) =
+  if (let m = canElimAddr(n, c.idgen); m != nil):
     gen(c, m, dest, flags)
     return
 
-  let af = if n[0].kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr}: {gfAddrOf, gfFieldAccess}
-           else: {gfAddrOf}
-  let newflags = if isAddr: flags+af else: flags
-  # consider:
-  # proc foo(f: var ref int) =
-  #   f = new(int)
-  # proc blah() =
-  #   var x: ref int
-  #   foo x
-  #
-  # The type of 'f' is 'var ref int' and of 'x' is 'ref int'. Hence for
-  # nkAddr we must not use 'unneededIndirection', but for deref we use it.
-  if not isAddr and unneededIndirection(n.sons[0]):
-    gen(c, n.sons[0], dest, newflags)
-    if gfAddrOf notin flags and fitsRegister(n.typ):
-      c.gABC(n, opcNodeToReg, dest, dest)
-  elif isAddr and isGlobal(n.sons[0]):
-    gen(c, n.sons[0], dest, flags+af)
+  let newflags = flags-{gfNode}+{gfNodeAddr}
+
+  if isGlobal(n[0]) or n[0].kind in {nkDotExpr, nkCheckedFieldExpr, nkBracketExpr}:
+    # checking for this pattern:  addr(obj.field) / addr(array[i])
+    gen(c, n[0], dest, newflags)
   else:
-    let tmp = c.genx(n.sons[0], newflags)
+    let tmp = c.genx(n[0], newflags)
     if dest < 0: dest = c.getTemp(n.typ)
-    if not isAddr:
-      gABC(c, n, opc, dest, tmp)
-      assert n.typ != nil
-      if gfAddrOf notin flags and fitsRegister(n.typ):
-        c.gABC(n, opcNodeToReg, dest, dest)
-    elif c.prc.slots[tmp].kind >= slotTempUnknown:
+    if c.prc.regInfo[tmp].kind >= slotTempUnknown:
       gABC(c, n, opcAddrNode, dest, tmp)
       # hack ahead; in order to fix bug #1781 we mark the temporary as
       # permanent, so that it's not used for anything else:
-      c.prc.slots[tmp].kind = slotTempPerm
+      c.prc.regInfo[tmp].kind = slotTempPerm
       # XXX this is still a hack
-      #message(n.info, warnUser, "suspicious opcode used")
+      #message(c.congig, n.info, warnUser, "suspicious opcode used")
     else:
       gABC(c, n, opcAddrReg, dest, tmp)
     c.freeTemp(tmp)
 
-proc whichAsgnOpc(n: PNode): TOpcode =
-  case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind
-  of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64:
-    opcAsgnInt
-  of tyString, tyCString:
-    opcAsgnStr
-  of tyFloat..tyFloat128:
-    opcAsgnFloat
-  of tyRef, tyNil, tyVar, tyLent, tyPtr:
-    opcAsgnRef
+proc genDeref(c: PCtx, n: PNode, dest: var TDest, flags: TGenFlags) =
+  if unneededIndirection(n[0]):
+    gen(c, n[0], dest, flags)
+    if {gfNodeAddr, gfNode} * flags == {} and fitsRegister(n.typ):
+      c.gABC(n, opcNodeToReg, dest, dest)
   else:
-    opcAsgnComplex
-
-proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc
+    let tmp = c.genx(n[0], flags)
+    if dest < 0: dest = c.getTemp(n.typ)
+    gABC(c, n, opcLdDeref, dest, tmp)
+    assert n.typ != nil
+    if {gfNodeAddr, gfNode} * flags == {} and fitsRegister(n.typ):
+      c.gABC(n, opcNodeToReg, dest, dest)
+    c.freeTemp(tmp)
 
 proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
   let tmp = c.genx(ri)
   assert dest >= 0
-  gABC(c, ri, whichAsgnOpc(ri), dest, tmp, 1-ord(requiresCopy))
+  gABC(c, ri, whichAsgnOpc(ri, requiresCopy), dest, tmp)
   c.freeTemp(tmp)
 
 proc setSlot(c: PCtx; v: PSym) =
   # XXX generate type initialization here?
   if v.position == 0:
-    if c.prc.maxSlots == 0: c.prc.maxSlots = 1
-    if c.prc.maxSlots >= high(TRegister):
-      globalError(c.config, v.info, "cannot generate code; too many registers required")
-    v.position = c.prc.maxSlots
-    c.prc.slots[v.position] = (inUse: true,
-        kind: if v.kind == skLet: slotFixedLet else: slotFixedVar)
-    inc c.prc.maxSlots
-
-proc cannotEval(c: PCtx; n: PNode) {.noinline.} =
+    v.position = getFreeRegister(c, if v.kind == skLet: slotFixedLet else: slotFixedVar, start = 1)
+
+template cannotEval(c: PCtx; n: PNode) =
+  if c.config.cmd == cmdCheck:
+    localError(c.config, n.info, "cannot evaluate at compile time: " & 
+    n.renderTree)
+    return
   globalError(c.config, n.info, "cannot evaluate at compile time: " &
     n.renderTree)
 
 proc isOwnedBy(a, b: PSym): bool =
+  result = false
   var a = a.owner
   while a != nil and a.kind != skModule:
     if a == b: return true
@@ -1343,28 +1560,37 @@ proc getOwner(c: PCtx): PSym =
   result = c.prc.sym
   if result.isNil: result = c.module
 
+proc importcCondVar*(s: PSym): bool {.inline.} =
+  # see also importcCond
+  if sfImportc in s.flags:
+    result = s.kind in {skVar, skLet, skConst}
+  else:
+    result = false
+
 proc checkCanEval(c: PCtx; n: PNode) =
   # we need to ensure that we don't evaluate 'x' here:
   # proc foo() = var x ...
   let s = n.sym
   if {sfCompileTime, sfGlobal} <= s.flags: return
+  if compiletimeFFI in c.config.features and s.importcCondVar: return
   if s.kind in {skVar, skTemp, skLet, skParam, skResult} and
       not s.isOwnedBy(c.prc.sym) and s.owner != c.module and c.mode != emRepl:
-    cannotEval(c, n)
+    # little hack ahead for bug #12612: assume gensym'ed variables
+    # are in the right scope:
+    if sfGenSym in s.flags and c.prc.sym == nil: discard
+    elif s.kind == skParam and s.typ.kind == tyTypeDesc: discard
+    else: cannotEval(c, n)
   elif s.kind in {skProc, skFunc, skConverter, skMethod,
                   skIterator} and sfForward in s.flags:
     cannotEval(c, n)
 
-proc isTemp(c: PCtx; dest: TDest): bool =
-  result = dest >= 0 and c.prc.slots[dest].kind >= slotTempUnknown
-
 template needsAdditionalCopy(n): untyped =
   not c.isTemp(dest) and not fitsRegister(n.typ)
 
 proc genAdditionalCopy(c: PCtx; n: PNode; opc: TOpcode;
                        dest, idx, value: TRegister) =
   var cc = c.getTemp(n.typ)
-  c.gABC(n, whichAsgnOpc(n), cc, value, 0)
+  c.gABC(n, whichAsgnOpc(n), cc, value)
   c.gABC(n, opc, dest, idx, cc)
   c.freeTemp(cc)
 
@@ -1382,146 +1608,199 @@ proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode;
 proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
   case le.kind
   of nkBracketExpr:
-    let dest = c.genx(le.sons[0], {gfAddrOf, gfFieldAccess})
-    let idx = c.genIndex(le.sons[1], le.sons[0].typ)
-    let tmp = c.genx(ri)
-    if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in {
-        tyString, tyCString}:
+    let
+      dest = c.genx(le[0], {gfNode})
+      idx = c.genIndex(le[1], le[0].typ)
+      tmp = c.genx(ri)
+      collTyp = le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc})
+    case collTyp.kind
+    of tyString, tyCstring:
       c.preventFalseAlias(le, opcWrStrIdx, dest, idx, tmp)
+    of tyTuple:
+      c.preventFalseAlias(le, opcWrObj, dest, int le[1].intVal, tmp)
     else:
       c.preventFalseAlias(le, opcWrArr, dest, idx, tmp)
     c.freeTemp(tmp)
-  of nkDotExpr, nkCheckedFieldExpr:
-    # XXX field checks here
-    let left = if le.kind == nkDotExpr: le else: le.sons[0]
-    let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess})
-    let idx = genField(c, left.sons[1])
+    c.freeTemp(idx)
+    c.freeTemp(dest)
+  of nkCheckedFieldExpr:
+    var objR: TDest = -1
+    genCheckedObjAccessAux(c, le, objR, {gfNode})
+    let idx = genField(c, le[0][1])
     let tmp = c.genx(ri)
-    c.preventFalseAlias(left, opcWrObj, dest, idx, tmp)
+    c.preventFalseAlias(le[0], opcWrObj, objR, idx, tmp)
     c.freeTemp(tmp)
+    # c.freeTemp(idx) # BUGFIX, see nkDotExpr
+    c.freeTemp(objR)
+  of nkDotExpr:
+    let dest = c.genx(le[0], {gfNode})
+    let idx = genField(c, le[1])
+    let tmp = c.genx(ri)
+    c.preventFalseAlias(le, opcWrObj, dest, idx, tmp)
+    # c.freeTemp(idx) # BUGFIX: idx is an immediate (field position), not a register
+    c.freeTemp(tmp)
+    c.freeTemp(dest)
   of nkDerefExpr, nkHiddenDeref:
-    let dest = c.genx(le.sons[0], {gfAddrOf})
+    let dest = c.genx(le[0], {gfNode})
     let tmp = c.genx(ri)
     c.preventFalseAlias(le, opcWrDeref, dest, 0, tmp)
+    c.freeTemp(dest)
     c.freeTemp(tmp)
   of nkSym:
     let s = le.sym
     checkCanEval(c, le)
     if s.isGlobal:
       withTemp(tmp, le.typ):
-        c.gen(le, tmp, {gfAddrOf})
+        c.gen(le, tmp, {gfNodeAddr})
         let val = c.genx(ri)
         c.preventFalseAlias(le, opcWrDeref, tmp, 0, val)
         c.freeTemp(val)
     else:
       if s.kind == skForVar: c.setSlot s
       internalAssert c.config, s.position > 0 or (s.position == 0 and
-                                        s.kind in {skParam,skResult})
+                                        s.kind in {skParam, skResult})
       var dest: TRegister = s.position + ord(s.kind == skParam)
       assert le.typ != nil
       if needsAdditionalCopy(le) and s.kind in {skResult, skVar, skParam}:
         var cc = c.getTemp(le.typ)
         gen(c, ri, cc)
-        c.gABC(le, whichAsgnOpc(le), dest, cc, 1)
+        c.gABC(le, whichAsgnOpc(le), dest, cc)
         c.freeTemp(cc)
       else:
         gen(c, ri, dest)
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+    if sameBackendType(le.typ, le[1].typ):
+      genAsgn(c, le[1], ri, requiresCopy)
   else:
-    let dest = c.genx(le, {gfAddrOf})
+    let dest = c.genx(le, {gfNodeAddr})
     genAsgn(c, dest, ri, requiresCopy)
+    c.freeTemp(dest)
 
 proc genTypeLit(c: PCtx; t: PType; dest: var TDest) =
   var n = newNode(nkType)
   n.typ = t
   genLit(c, n, dest)
 
+proc isEmptyBody(n: PNode): bool =
+  case n.kind
+  of nkStmtList:
+    for i in 0..<n.len:
+      if not isEmptyBody(n[i]): return false
+    result = true
+  else:
+    result = n.kind in {nkCommentStmt, nkEmpty}
+
+proc importcCond*(c: PCtx; s: PSym): bool {.inline.} =
+  ## return true to importc `s`, false to execute its body instead (refs #8405)
+  result = false
+  if sfImportc in s.flags:
+    if s.kind in routineKinds:
+      return isEmptyBody(getBody(c.graph, s))
+
 proc importcSym(c: PCtx; info: TLineInfo; s: PSym) =
   when hasFFI:
-    if allowFFI in c.features:
-      c.globals.add(importcSymbol(s))
+    if compiletimeFFI in c.config.features:
+      c.globals.add(importcSymbol(c.config, s))
       s.position = c.globals.len
     else:
-      localError(c.config, info, "VM is not allowed to 'importc'")
+      localError(c.config, info,
+        "VM is not allowed to 'importc' without --experimental:compiletimeFFI")
   else:
     localError(c.config, info,
-               "cannot 'importc' variable at compile time")
+               "cannot 'importc' variable at compile time; " & s.name.s)
 
-proc getNullValue*(typ: PType, info: TLineInfo; conf: ConfigRef): PNode
+proc getNullValue*(c: PCtx; typ: PType, info: TLineInfo; conf: ConfigRef): PNode
 
 proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
-  c.globals.add(getNullValue(s.typ, n.info, c.config))
+  c.globals.add(getNullValue(c, s.typ, n.info, c.config))
   s.position = c.globals.len
   # This is rather hard to support, due to the laziness of the VM code
   # generator. See tests/compile/tmacro2 for why this is necessary:
   #   var decls{.compileTime.}: seq[NimNode] = @[]
   let dest = c.getTemp(s.typ)
   c.gABx(n, opcLdGlobal, dest, s.position)
-  if s.ast != nil:
-    let tmp = c.genx(s.ast)
+  if s.astdef != nil:
+    let tmp = c.genx(s.astdef)
     c.genAdditionalCopy(n, opcWrDeref, dest, 0, tmp)
     c.freeTemp(dest)
     c.freeTemp(tmp)
 
 proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
+  # gfNodeAddr and gfNode are mutually exclusive
+  assert card(flags * {gfNodeAddr, gfNode}) < 2
   let s = n.sym
   if s.isGlobal:
-    if sfCompileTime in s.flags or c.mode == emRepl:
+    let isImportcVar = importcCondVar(s)
+    if sfCompileTime in s.flags or c.mode == emRepl or isImportcVar:
       discard
     elif s.position == 0:
       cannotEval(c, n)
     if s.position == 0:
-      if sfImportc in s.flags: c.importcSym(n.info, s)
+      if importcCond(c, s) or isImportcVar: c.importcSym(n.info, s)
       else: genGlobalInit(c, n, s)
     if dest < 0: dest = c.getTemp(n.typ)
     assert s.typ != nil
-    if gfAddrOf notin flags and fitsRegister(s.typ):
+
+    if gfNodeAddr in flags:
+      if isImportcVar:
+        c.gABx(n, opcLdGlobalAddrDerefFFI, dest, s.position)
+      else:
+        c.gABx(n, opcLdGlobalAddr, dest, s.position)
+    elif isImportcVar:
+      c.gABx(n, opcLdGlobalDerefFFI, dest, s.position)
+    elif gfIsSinkParam in flags:
+      genAsgn(c, dest, n, requiresCopy = true)
+    elif fitsRegister(s.typ) and gfNode notin flags:
       var cc = c.getTemp(n.typ)
       c.gABx(n, opcLdGlobal, cc, s.position)
       c.gABC(n, opcNodeToReg, dest, cc)
       c.freeTemp(cc)
-    elif {gfAddrOf, gfFieldAccess} * flags == {gfAddrOf}:
-      c.gABx(n, opcLdGlobalAddr, dest, s.position)
     else:
       c.gABx(n, opcLdGlobal, dest, s.position)
   else:
     if s.kind == skForVar and c.mode == emRepl: c.setSlot(s)
     if s.position > 0 or (s.position == 0 and
-                          s.kind in {skParam,skResult}):
+                          s.kind in {skParam, skResult}):
       if dest < 0:
         dest = s.position + ord(s.kind == skParam)
-        internalAssert(c.config, c.prc.slots[dest].kind < slotSomeTemp)
+        internalAssert(c.config, c.prc.regInfo.len > dest and c.prc.regInfo[dest].kind < slotSomeTemp)
       else:
         # we need to generate an assignment:
-        genAsgn(c, dest, n, c.prc.slots[dest].kind >= slotSomeTemp)
+        let requiresCopy = c.prc.regInfo[dest].kind >= slotSomeTemp and
+          gfIsParam notin flags
+        genAsgn(c, dest, n, requiresCopy)
     else:
       # see tests/t99bott for an example that triggers it:
       cannotEval(c, n)
 
 template needsRegLoad(): untyped =
-  gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar, tyLent}))
+  {gfNode, gfNodeAddr} * flags == {} and
+    fitsRegister(n.typ.skipTypes({tyVar, tyLent, tyStatic}))
 
-proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
-                   flags: TGenFlags) =
-  let a = c.genx(n.sons[0], flags)
-  let b = c.genIndex(n.sons[1], n.sons[0].typ)
+proc genArrAccessOpcode(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
+                        flags: TGenFlags) =
+  let a = c.genx(n[0], flags)
+  let b = c.genIndex(n[1], n[0].typ)
   if dest < 0: dest = c.getTemp(n.typ)
-  if needsRegLoad():
+  if opc in {opcLdArrAddr, opcLdStrIdxAddr} and gfNodeAddr in flags:
+    c.gABC(n, opc, dest, a, b)
+  elif needsRegLoad():
     var cc = c.getTemp(n.typ)
     c.gABC(n, opc, cc, a, b)
     c.gABC(n, opcNodeToReg, dest, cc)
     c.freeTemp(cc)
   else:
-    #message(n.info, warnUser, "argh")
+    #message(c.config, n.info, warnUser, "argh")
     #echo "FLAGS ", flags, " ", fitsRegister(n.typ), " ", typeToString(n.typ)
     c.gABC(n, opc, dest, a, b)
   c.freeTemp(a)
   c.freeTemp(b)
 
-proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
-  let a = c.genx(n.sons[0], flags)
-  let b = genField(c, n.sons[1])
+proc genObjAccessAux(c: PCtx; n: PNode; a, b: int, dest: var TDest; flags: TGenFlags) =
   if dest < 0: dest = c.getTemp(n.typ)
-  if needsRegLoad():
+  if {gfNodeAddr} * flags != {}:
+    c.gABC(n, opcLdObjAddr, dest, a, b)
+  elif needsRegLoad():
     var cc = c.getTemp(n.typ)
     c.gABC(n, opcLdObj, cc, a, b)
     c.gABC(n, opcNodeToReg, dest, cc)
@@ -1530,36 +1809,114 @@ proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
     c.gABC(n, opcLdObj, dest, a, b)
   c.freeTemp(a)
 
+proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
+  genObjAccessAux(c, n, c.genx(n[0], flags), genField(c, n[1]), dest, flags)
+
+
+
+proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
+  internalAssert c.config, n.kind == nkCheckedFieldExpr
+  # nkDotExpr to access the requested field
+  let accessExpr = n[0]
+  # nkCall to check if the discriminant is valid
+  var checkExpr = n[1]
+
+  let negCheck = checkExpr[0].sym.magic == mNot
+  if negCheck:
+    checkExpr = checkExpr[^1]
+
+  # Discriminant symbol
+  let disc = checkExpr[2]
+  internalAssert c.config, disc.sym.kind == skField
+
+  # Load the object in `dest`
+  c.gen(accessExpr[0], dest, flags)
+  # Load the discriminant
+  var discVal = c.getTemp(disc.typ)
+  c.gABC(n, opcLdObj, discVal, dest, genField(c, disc))
+  # Check if its value is contained in the supplied set
+  let setLit = c.genx(checkExpr[1])
+  var rs = c.getTemp(getSysType(c.graph, n.info, tyBool))
+  c.gABC(n, opcContainsSet, rs, setLit, discVal)
+  c.freeTemp(setLit)
+  # If the check fails let the user know
+  let lab1 = c.xjmp(n, if negCheck: opcFJmp else: opcTJmp, rs)
+  c.freeTemp(rs)
+  let strType = getSysType(c.graph, n.info, tyString)
+  var msgReg: TDest = c.getTemp(strType)
+  let fieldName = $accessExpr[1]
+  let msg = genFieldDefect(c.config, fieldName, disc.sym)
+  let strLit = newStrNode(msg, accessExpr[1].info)
+  strLit.typ = strType
+  c.genLit(strLit, msgReg)
+  c.gABC(n, opcInvalidField, msgReg, discVal)
+  c.freeTemp(discVal)
+  c.freeTemp(msgReg)
+  c.patch(lab1)
+
 proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
-  # XXX implement field checks!
-  genObjAccess(c, n.sons[0], dest, flags)
+  var objR: TDest = -1
+  genCheckedObjAccessAux(c, n, objR, flags)
+
+  let accessExpr = n[0]
+  # Field symbol
+  var field = accessExpr[1]
+  internalAssert c.config, field.sym.kind == skField
+
+  # Load the content now
+  if dest < 0: dest = c.getTemp(n.typ)
+  let fieldPos = genField(c, field)
+
+  if {gfNodeAddr} * flags != {}:
+    c.gABC(n, opcLdObjAddr, dest, objR, fieldPos)
+  elif needsRegLoad():
+    var cc = c.getTemp(accessExpr.typ)
+    c.gABC(n, opcLdObj, cc, objR, fieldPos)
+    c.gABC(n, opcNodeToReg, dest, cc)
+    c.freeTemp(cc)
+  else:
+    c.gABC(n, opcLdObj, dest, objR, fieldPos)
+
+  c.freeTemp(objR)
 
 proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
-  let arrayType = n.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind
-  if arrayType in {tyString, tyCString}:
-    genArrAccess2(c, n, dest, opcLdStrIdx, {})
-  elif arrayType == tyTypeDesc:
+  let arrayType = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind
+  case arrayType
+  of tyString, tyCstring:
+    let opc = if gfNodeAddr in flags: opcLdStrIdxAddr else: opcLdStrIdx
+    genArrAccessOpcode(c, n, dest, opc, flags)
+  of tyTuple:
+    c.genObjAccessAux(n, c.genx(n[0], flags), int n[1].intVal, dest, flags)
+  of tyTypeDesc:
     c.genTypeLit(n.typ, dest)
   else:
-    genArrAccess2(c, n, dest, opcLdArr, flags)
+    let opc = if gfNodeAddr in flags: opcLdArrAddr else: opcLdArr
+    genArrAccessOpcode(c, n, dest, opc, flags)
 
-proc getNullValueAux(obj: PNode, result: PNode; conf: ConfigRef) =
+proc getNullValueAux(c: PCtx; t: PType; obj: PNode, result: PNode; conf: ConfigRef; currPosition: var int) =
+  if t != nil and t.baseClass != nil:
+    let b = skipTypes(t.baseClass, skipPtrs)
+    getNullValueAux(c, b, b.n, result, conf, currPosition)
   case obj.kind
   of nkRecList:
-    for i in countup(0, sonsLen(obj) - 1): getNullValueAux(obj.sons[i], result, conf)
+    for i in 0..<obj.len: getNullValueAux(c, nil, obj[i], result, conf, currPosition)
   of nkRecCase:
-    getNullValueAux(obj.sons[0], result, conf)
-    for i in countup(1, sonsLen(obj) - 1):
-      getNullValueAux(lastSon(obj.sons[i]), result, conf)
+    getNullValueAux(c, nil, obj[0], result, conf, currPosition)
+    for i in 1..<obj.len:
+      getNullValueAux(c, nil, lastSon(obj[i]), result, conf, currPosition)
   of nkSym:
     let field = newNodeI(nkExprColonExpr, result.info)
     field.add(obj)
-    field.add(getNullValue(obj.sym.typ, result.info, conf))
-    addSon(result, field)
+    let value = getNullValue(c, obj.sym.typ, result.info, conf)
+    value.flags.incl nfSkipFieldChecking
+    field.add(value)
+    result.add field
+    doAssert obj.sym.position == currPosition
+    inc currPosition
   else: globalError(conf, result.info, "cannot create null element for: " & $obj)
 
-proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
-  var t = skipTypes(typ, abstractRange-{tyTypeDesc})
+proc getNullValue(c: PCtx; typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
+  var t = skipTypes(typ, abstractRange+{tyStatic, tyOwned}-{tyTypeDesc})
   case t.kind
   of tyBool, tyEnum, tyChar, tyInt..tyInt64:
     result = newNodeIT(nkIntLit, info, t)
@@ -1567,103 +1924,112 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
     result = newNodeIT(nkUIntLit, info, t)
   of tyFloat..tyFloat128:
     result = newNodeIT(nkFloatLit, info, t)
-  of tyCString, tyString:
+  of tyString:
     result = newNodeIT(nkStrLit, info, t)
     result.strVal = ""
-  of tyVar, tyLent, tyPointer, tyPtr, tyExpr,
-     tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil:
+  of tyCstring, tyVar, tyLent, tyPointer, tyPtr, tyUntyped,
+     tyTyped, tyTypeDesc, tyRef, tyNil:
     result = newNodeIT(nkNilLit, info, t)
   of tyProc:
     if t.callConv != ccClosure:
       result = newNodeIT(nkNilLit, info, t)
     else:
       result = newNodeIT(nkTupleConstr, info, t)
-      result.add(newNodeIT(nkNilLit, info, t))
-      result.add(newNodeIT(nkNilLit, info, t))
+      result.add(newNodeIT(nkNilLit, info, getSysType(c.graph, info, tyPointer)))
+      result.add(newNodeIT(nkNilLit, info, getSysType(c.graph, info, tyPointer)))
   of tyObject:
     result = newNodeIT(nkObjConstr, info, t)
     result.add(newNodeIT(nkEmpty, info, t))
-    # initialize inherited fields:
-    var base = t.sons[0]
-    while base != nil:
-      getNullValueAux(skipTypes(base, skipPtrs).n, result, conf)
-      base = base.sons[0]
-    getNullValueAux(t.n, result, conf)
+    # initialize inherited fields, and all in the correct order:
+    var currPosition = 0
+    getNullValueAux(c, t, t.n, result, conf, currPosition)
   of tyArray:
     result = newNodeIT(nkBracket, info, t)
-    for i in countup(0, int(lengthOrd(conf, t)) - 1):
-      addSon(result, getNullValue(elemType(t), info, conf))
+    for i in 0..<toInt(lengthOrd(conf, t)):
+      result.add getNullValue(c, elemType(t), info, conf)
   of tyTuple:
     result = newNodeIT(nkTupleConstr, info, t)
-    for i in countup(0, sonsLen(t) - 1):
-      addSon(result, getNullValue(t.sons[i], info, conf))
+    for a in t.kids:
+      result.add getNullValue(c, a, info, conf)
   of tySet:
     result = newNodeIT(nkCurly, info, t)
-  of tyOpt:
-    result = newNodeIT(nkNilLit, info, t)
-  of tySequence:
+  of tySequence, tyOpenArray:
     result = newNodeIT(nkBracket, info, t)
   else:
     globalError(conf, info, "cannot create null element for: " & $t.kind)
     result = newNodeI(nkEmpty, info)
 
-proc ldNullOpcode(t: PType): TOpcode =
-  assert t != nil
-  if fitsRegister(t): opcLdNullReg else: opcLdNull
-
 proc genVarSection(c: PCtx; n: PNode) =
   for a in n:
     if a.kind == nkCommentStmt: continue
-    #assert(a.sons[0].kind == nkSym) can happen for transformed vars
+    #assert(a[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])
-      c.gen(lowerTupleUnpacking(c.graph, a, c.getOwner))
-    elif a.sons[0].kind == nkSym:
-      let s = a.sons[0].sym
-      checkCanEval(c, a.sons[0])
+      for i in 0..<a.len-2:
+        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.idgen, c.getOwner))
+    elif a[0].kind == nkSym:
+      let s = a[0].sym
+      checkCanEval(c, a[0])
       if s.isGlobal:
+        let runtimeAccessToCompileTime = c.mode == emRepl and
+              sfCompileTime in s.flags and s.position > 0
         if s.position == 0:
-          if sfImportc in s.flags: c.importcSym(a.info, s)
+          if importcCond(c, s): c.importcSym(a.info, s)
           else:
-            let sa = getNullValue(s.typ, a.info, c.config)
+            let sa = getNullValue(c, s.typ, a.info, c.config)
             #if s.ast.isNil: getNullValue(s.typ, a.info)
-            #else: canonValue(s.ast)
+            #else: s.ast
             assert sa.kind != nkCall
             c.globals.add(sa)
             s.position = c.globals.len
-        if a.sons[2].kind != nkEmpty:
-          let tmp = c.genx(a.sons[0], {gfAddrOf})
-          let val = c.genx(a.sons[2])
-          c.genAdditionalCopy(a.sons[2], opcWrDeref, tmp, 0, val)
+        if runtimeAccessToCompileTime:
+          discard
+        elif a[2].kind != nkEmpty:
+          let tmp = c.genx(a[0], {gfNodeAddr})
+          let val = c.genx(a[2])
+          c.genAdditionalCopy(a[2], opcWrDeref, tmp, 0, val)
+          c.freeTemp(val)
+          c.freeTemp(tmp)
+        elif not importcCondVar(s) and not (s.typ.kind == tyProc and s.typ.callConv == ccClosure) and
+                sfPure notin s.flags: # fixes #10938
+          # there is a pre-existing issue with closure types in VM
+          # if `(var s: proc () = default(proc ()); doAssert s == nil)` works for you;
+          # you might remove the second condition.
+          # the problem is that closure types are tuples in VM, but the types of its children
+          # shouldn't have the same type as closure types.
+          let tmp = c.genx(a[0], {gfNodeAddr})
+          let sa = getNullValue(c, s.typ, a.info, c.config)
+          let val = c.genx(sa)
+          c.genAdditionalCopy(sa, opcWrDeref, tmp, 0, val)
           c.freeTemp(val)
           c.freeTemp(tmp)
       else:
         setSlot(c, s)
-        if a.sons[2].kind == nkEmpty:
+        if a[2].kind == nkEmpty:
           c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ))
         else:
           assert s.typ != nil
           if not fitsRegister(s.typ):
             c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ))
-          let le = a.sons[0]
+          let le = a[0]
           assert le.typ != nil
           if not fitsRegister(le.typ) and s.kind in {skResult, skVar, skParam}:
             var cc = c.getTemp(le.typ)
-            gen(c, a.sons[2], cc)
-            c.gABC(le, whichAsgnOpc(le), s.position.TRegister, cc, 1)
+            gen(c, a[2], cc)
+            c.gABC(le, whichAsgnOpc(le), s.position.TRegister, cc)
             c.freeTemp(cc)
           else:
-            gen(c, a.sons[2], s.position.TRegister)
+            gen(c, a[2], s.position.TRegister)
     else:
-      # assign to a.sons[0]; happens for closures
-      if a.sons[2].kind == nkEmpty:
-        let tmp = genx(c, a.sons[0])
-        c.gABx(a, ldNullOpcode(a[0].typ), tmp, c.genType(a.sons[0].typ))
+      # assign to a[0]; happens for closures
+      if a[2].kind == nkEmpty:
+        let tmp = genx(c, a[0])
+        c.gABx(a, ldNullOpcode(a[0].typ), tmp, c.genType(a[0].typ))
         c.freeTemp(tmp)
       else:
-        genAsgn(c, a.sons[0], a.sons[2], true)
+        genAsgn(c, a[0], a[2], true)
 
 proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) =
   if dest < 0: dest = c.getTemp(n.typ)
@@ -1683,7 +2049,7 @@ proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) =
     c.gABx(n, opcLdNullReg, tmp, c.genType(intType))
     for x in n:
       let a = c.genx(x)
-      c.preventFalseAlias(n, whichAsgnOpc(x, opcWrArr), dest, tmp, a)
+      c.preventFalseAlias(n, opcWrArr, dest, tmp, a)
       c.gABI(n, opcAddImmInt, tmp, tmp, 1)
       c.freeTemp(a)
     c.freeTemp(tmp)
@@ -1693,8 +2059,8 @@ proc genSetConstr(c: PCtx, n: PNode, dest: var TDest) =
   c.gABx(n, opcLdNull, dest, c.genType(n.typ))
   for x in n:
     if x.kind == nkRange:
-      let a = c.genx(x.sons[0])
-      let b = c.genx(x.sons[1])
+      let a = c.genx(x[0])
+      let b = c.genx(x[1])
       c.gABC(n, opcInclRange, dest, a, b)
       c.freeTemp(b)
       c.freeTemp(a)
@@ -1704,18 +2070,20 @@ proc genSetConstr(c: PCtx, n: PNode, dest: var TDest) =
       c.freeTemp(a)
 
 proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) =
+  if tfUnion in n.typ.flags: # bug #22708 # bug #13481
+    globalError(c.config, n.info, "object with '{.union.}' pragmas is not supported by VM")
   if dest < 0: dest = c.getTemp(n.typ)
-  let t = n.typ.skipTypes(abstractRange-{tyTypeDesc})
+  let t = n.typ.skipTypes(abstractRange+{tyOwned}-{tyTypeDesc})
   if t.kind == tyRef:
-    c.gABx(n, opcNew, dest, c.genType(t.sons[0]))
+    c.gABx(n, opcNew, dest, c.genType(t.elementType))
   else:
     c.gABx(n, opcLdNull, dest, c.genType(n.typ))
   for i in 1..<n.len:
-    let it = n.sons[i]
-    if it.kind == nkExprColonExpr and it.sons[0].kind == nkSym:
-      let idx = genField(c, it.sons[0])
-      let tmp = c.genx(it.sons[1])
-      c.preventFalseAlias(it.sons[1], whichAsgnOpc(it.sons[1], opcWrObj),
+    let it = n[i]
+    if it.kind == nkExprColonExpr and it[0].kind == nkSym:
+      let idx = genField(c, it[0])
+      let tmp = c.genx(it[1])
+      c.preventFalseAlias(it[1], opcWrObj,
                           dest, idx, tmp)
       c.freeTemp(tmp)
     else:
@@ -1723,70 +2091,77 @@ proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) =
 
 proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) =
   if dest < 0: dest = c.getTemp(n.typ)
-  c.gABx(n, opcLdNull, dest, c.genType(n.typ))
-  # XXX x = (x.old, 22)  produces wrong code ... stupid self assignments
-  for i in 0..<n.len:
-    let it = n.sons[i]
-    if it.kind == nkExprColonExpr:
-      let idx = genField(c, it.sons[0])
-      let tmp = c.genx(it.sons[1])
-      c.preventFalseAlias(it.sons[1], whichAsgnOpc(it.sons[1], opcWrObj),
-                          dest, idx, tmp)
-      c.freeTemp(tmp)
-    else:
-      let tmp = c.genx(it)
-      c.preventFalseAlias(it, whichAsgnOpc(it, opcWrObj), dest, i.TRegister, tmp)
-      c.freeTemp(tmp)
+  if n.typ.kind != tyTypeDesc:
+    c.gABx(n, opcLdNull, dest, c.genType(n.typ))
+    # XXX x = (x.old, 22)  produces wrong code ... stupid self assignments
+    for i in 0..<n.len:
+      let it = n[i]
+      if it.kind == nkExprColonExpr:
+        let idx = genField(c, it[0])
+        let tmp = c.genx(it[1])
+        c.preventFalseAlias(it[1], opcWrObj,
+                            dest, idx, tmp)
+        c.freeTemp(tmp)
+      else:
+        let tmp = c.genx(it)
+        c.preventFalseAlias(it, opcWrObj, dest, i.TRegister, tmp)
+        c.freeTemp(tmp)
 
 proc genProc*(c: PCtx; s: PSym): int
 
-proc matches(s: PSym; x: string): bool =
-  let y = x.split('.')
+proc toKey(s: PSym): string =
+  result = ""
   var s = s
-  var L = y.len-1
-  while L >= 0:
-    if s == nil or (y[L].cmpIgnoreStyle(s.name.s) != 0 and y[L] != "*"):
-      return false
-    s = s.owner
-    dec L
-  result = true
-
-proc matches(s: PSym; y: varargs[string]): bool =
-  var s = s
-  var L = y.len-1
-  while L >= 0:
-    if s == nil or (y[L].cmpIgnoreStyle(s.name.s) != 0 and y[L] != "*"):
-      return false
-    s = if sfFromGeneric in s.flags: s.owner.owner else: s.owner
-    dec L
-  result = true
+  while s != nil:
+    result.add s.name.s
+    if s.owner != nil:
+      if sfFromGeneric in s.flags:
+        s = s.instantiatedFrom.owner
+      else:
+        s = s.owner
+      result.add "."
+    else:
+      break
 
 proc procIsCallback(c: PCtx; s: PSym): bool =
   if s.offset < -1: return true
-  var i = -2
-  for key, value in items(c.callbacks):
-    if s.matches(key):
-      doAssert s.offset == -1
-      s.offset = i
-      return true
-    dec i
+  let key = toKey(s)
+  if c.callbackIndex.contains(key):
+    let index = c.callbackIndex[key]
+    doAssert s.offset == -1
+    s.offset = -2'i32 - index.int32
+    result = true
+  else:
+    result = false
 
 proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
+  when defined(nimCompilerStacktraceHints):
+    setFrameMsg c.config$n.info & " " & $n.kind & " " & $flags
   case n.kind
   of nkSym:
     let s = n.sym
     checkCanEval(c, n)
     case s.kind
-    of skVar, skForVar, skTemp, skLet, skParam, skResult:
+    of skVar, skForVar, skTemp, skLet, skResult:
       genRdVar(c, n, dest, flags)
+    of skParam:
+      if s.typ.kind == tyTypeDesc:
+        genTypeLit(c, s.typ.skipTypes({tyTypeDesc}), dest)
+      else:
+        genRdVar(c, n, dest, flags)
     of skProc, skFunc, skConverter, skMacro, skTemplate, skMethod, skIterator:
       # 'skTemplate' is only allowed for 'getAst' support:
+      if s.kind == skIterator and s.typ.callConv == TCallingConvention.ccClosure:
+        globalError(c.config, n.info, "Closure iterators are not supported by VM!")
       if procIsCallback(c, s): discard
-      elif sfImportc in s.flags: c.importcSym(n.info, s)
+      elif importcCond(c, s): c.importcSym(n.info, s)
       genLit(c, n, dest)
     of skConst:
-      let constVal = if s.ast != nil: s.ast else: s.typ.n
-      gen(c, constVal, dest)
+      let constVal = if s.astdef != nil: s.astdef else: s.typ.n
+      if dontInlineConstant(n, constVal):
+        genLit(c, constVal, dest)
+      else:
+        gen(c, constVal, dest)
     of skEnumField:
       # we never reach this case - as of the time of this comment,
       # skEnumField is folded to an int in semfold.nim, but this code
@@ -1807,16 +2182,13 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
     else:
       globalError(c.config, n.info, "cannot generate code for: " & s.name.s)
   of nkCallKinds:
-    if n.sons[0].kind == nkSym:
-      let s = n.sons[0].sym
+    if n[0].kind == nkSym:
+      let s = n[0].sym
       if s.magic != mNone:
-        genMagic(c, n, dest, s.magic)
-      elif matches(s, "stdlib", "marshal", "to"):
-        # XXX marshal load&store should not be opcodes, but use the
-        # general callback mechanisms.
-        genMarshalLoad(c, n, dest)
-      elif matches(s, "stdlib", "marshal", "$$"):
-        genMarshalStore(c, n, dest)
+        genMagic(c, n, dest, flags, s.magic)
+      elif s.kind == skMethod:
+        localError(c.config, n.info, "cannot call method " & s.name.s &
+          " at compile time")
       else:
         genCall(c, n, dest)
         clearDest(c, n, dest)
@@ -1831,93 +2203,97 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
       genLit(c, n, dest)
   of nkUIntLit..pred(nkNilLit): genLit(c, n, dest)
   of nkNilLit:
-    if not n.typ.isEmptyType: genLit(c, getNullValue(n.typ, n.info, c.config), dest)
+    if not n.typ.isEmptyType: genLit(c, getNullValue(c, n.typ, n.info, c.config), dest)
     else: unused(c, n, dest)
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     unused(c, n, dest)
-    genAsgn(c, n.sons[0], n.sons[1], n.kind == nkAsgn)
+    genAsgn(c, n[0], n[1], n.kind == nkAsgn)
   of nkDotExpr: genObjAccess(c, n, dest, flags)
   of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest, flags)
   of nkBracketExpr: genArrAccess(c, n, dest, flags)
-  of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcLdDeref, flags)
-  of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddrNode, flags)
+  of nkDerefExpr, nkHiddenDeref: genDeref(c, n, dest, flags)
+  of nkAddr, nkHiddenAddr: genAddr(c, n, dest, flags)
   of nkIfStmt, nkIfExpr: genIf(c, n, dest)
   of nkWhenStmt:
     # This is "when nimvm" node. Chose the first branch.
-    gen(c, n.sons[0].sons[1], dest)
+    gen(c, n[0][1], dest)
   of nkCaseStmt: genCase(c, n, dest)
   of nkWhileStmt:
     unused(c, n, dest)
     genWhile(c, n)
   of nkBlockExpr, nkBlockStmt: genBlock(c, n, dest)
   of nkReturnStmt:
-    unused(c, n, dest)
     genReturn(c, n)
   of nkRaiseStmt:
     genRaise(c, n)
   of nkBreakStmt:
-    unused(c, n, dest)
     genBreak(c, n)
-  of nkTryStmt: genTry(c, n, dest)
+  of nkTryStmt, nkHiddenTryStmt: genTry(c, n, dest)
   of nkStmtList:
     #unused(c, n, dest)
     # XXX Fix this bug properly, lexim triggers it
     for x in n: gen(c, x)
   of nkStmtListExpr:
-    let L = n.len-1
-    for i in 0 ..< L: gen(c, n.sons[i])
-    gen(c, n.sons[L], dest, flags)
+    for i in 0..<n.len-1: gen(c, n[i])
+    gen(c, n[^1], dest, flags)
   of nkPragmaBlock:
     gen(c, n.lastSon, dest, flags)
   of nkDiscardStmt:
     unused(c, n, dest)
-    gen(c, n.sons[0])
+    gen(c, n[0])
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
-    genConv(c, n, n.sons[1], dest)
+    genConv(c, n, n[1], dest, flags)
   of nkObjDownConv:
-    genConv(c, n, n.sons[0], dest)
+    genConv(c, n, n[0], dest, flags)
+  of nkObjUpConv:
+    genConv(c, n, n[0], dest, flags)
   of nkVarSection, nkLetSection:
     unused(c, n, dest)
     genVarSection(c, n)
-  of declarativeDefs, nkMacroDef:
-    unused(c, n, dest)
   of nkLambdaKinds:
-    #let s = n.sons[namePos].sym
+    #let s = n[namePos].sym
     #discard genProc(c, s)
-    genLit(c, newSymNode(n.sons[namePos].sym), dest)
+    genLit(c, newSymNode(n[namePos].sym), dest)
   of nkChckRangeF, nkChckRange64, nkChckRange:
-    let
-      tmp0 = c.genx(n.sons[0])
-      tmp1 = c.genx(n.sons[1])
-      tmp2 = c.genx(n.sons[2])
-    c.gABC(n, opcRangeChck, tmp0, tmp1, tmp2)
-    c.freeTemp(tmp1)
-    c.freeTemp(tmp2)
-    if dest >= 0:
-      gABC(c, n, whichAsgnOpc(n), dest, tmp0, 1)
-      c.freeTemp(tmp0)
+    if skipTypes(n.typ, abstractVar).kind in {tyUInt..tyUInt64}:
+      genConv(c, n, n[0], dest, flags)
     else:
-      dest = tmp0
+      let
+        tmp0 = c.genx(n[0])
+        tmp1 = c.genx(n[1])
+        tmp2 = c.genx(n[2])
+      c.gABC(n, opcRangeChck, tmp0, tmp1, tmp2)
+      c.freeTemp(tmp1)
+      c.freeTemp(tmp2)
+      if dest >= 0:
+        gABC(c, n, whichAsgnOpc(n), dest, tmp0)
+        c.freeTemp(tmp0)
+      else:
+        dest = tmp0
   of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma,
-     nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt:
+     nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt,
+     nkMixinStmt, nkBindStmt, declarativeDefs, nkMacroDef:
     unused(c, n, dest)
   of nkStringToCString, nkCStringToString:
-    gen(c, n.sons[0], dest)
+    gen(c, n[0], dest)
   of nkBracket: genArrayConstr(c, n, dest)
   of nkCurly: genSetConstr(c, n, dest)
   of nkObjConstr: genObjConstr(c, n, dest)
   of nkPar, nkClosure, nkTupleConstr: genTupleConstr(c, n, dest)
   of nkCast:
     if allowCast in c.features:
-      genConv(c, n, n.sons[1], dest, opcCast)
+      genConv(c, n, n[1], dest, flags, opcCast)
     else:
-      genIntCast(c, n, dest)
+      genCastIntFloat(c, n, dest)
   of nkTypeOfExpr:
     genTypeLit(c, n.typ, dest)
   of nkComesFrom:
     discard "XXX to implement for better stack traces"
   else:
-    globalError(c.config, n.info, "cannot generate VM code for " & $n)
+    if n.typ != nil and n.typ.isCompileTimeOnly:
+      genTypeLit(c, n.typ, dest)
+    else:
+      globalError(c.config, n.info, "cannot generate VM code for " & $n)
 
 proc removeLastEof(c: PCtx) =
   let last = c.code.len-1
@@ -1952,29 +2328,29 @@ proc genExpr*(c: PCtx; n: PNode, requiresValue = true): int =
 
 proc genParams(c: PCtx; params: PNode) =
   # res.sym.position is already 0
-  c.prc.slots[0] = (inUse: true, kind: slotFixedVar)
+  setLen(c.prc.regInfo, max(params.len, 1))
+  c.prc.regInfo[0] = (inUse: true, kind: slotFixedVar)
   for i in 1..<params.len:
-    c.prc.slots[i] = (inUse: true, kind: slotFixedLet)
-  c.prc.maxSlots = max(params.len, 1)
+    c.prc.regInfo[i] = (inUse: true, kind: slotFixedLet)
 
 proc finalJumpTarget(c: PCtx; pc, diff: int) =
-  internalAssert(c.config, -0x7fff < diff and diff < 0x7fff)
+  internalAssert(c.config, regBxMin < diff and diff < regBxMax)
   let oldInstr = c.code[pc]
   # opcode and regA stay the same:
-  c.code[pc] = ((oldInstr.uint32 and 0xffff'u32).uint32 or
-                uint32(diff+wordExcess) shl 16'u32).TInstr
+  c.code[pc] = ((oldInstr.TInstrType and ((regOMask shl regOShift) or (regAMask shl regAShift))).TInstrType or
+                TInstrType(diff+wordExcess) shl regBxShift).TInstr
 
 proc genGenericParams(c: PCtx; gp: PNode) =
-  var base = c.prc.maxSlots
+  var base = c.prc.regInfo.len
+  setLen c.prc.regInfo, base + gp.len
   for i in 0..<gp.len:
-    var param = gp.sons[i].sym
+    var param = gp[i].sym
     param.position = base + i # XXX: fix this earlier; make it consistent with templates
-    c.prc.slots[base + i] = (inUse: true, kind: slotFixedLet)
-  c.prc.maxSlots = base + gp.len
+    c.prc.regInfo[base + i] = (inUse: true, kind: slotFixedLet)
 
 proc optimizeJumps(c: PCtx; start: int) =
   const maxIterations = 10
-  for i in start ..< c.code.len:
+  for i in start..<c.code.len:
     let opc = c.code[i].opcode
     case opc
     of opcTJmp, opcFJmp:
@@ -1982,8 +2358,8 @@ proc optimizeJumps(c: PCtx; start: int) =
       var d = i + c.code[i].jmpDiff
       for iters in countdown(maxIterations, 0):
         case c.code[d].opcode
-        of opcJmp, opcJmpBack:
-          d = d + c.code[d].jmpDiff
+        of opcJmp:
+          d += c.code[d].jmpDiff
         of opcTJmp, opcFJmp:
           if c.code[d].regA != reg: break
           # tjmp x, 23
@@ -1991,12 +2367,12 @@ proc optimizeJumps(c: PCtx; start: int) =
           # tjmp x, 12
           # -- we know 'x' is true, and so can jump to 12+13:
           if c.code[d].opcode == opc:
-            d = d + c.code[d].jmpDiff
+            d += c.code[d].jmpDiff
           else:
             # tjmp x, 23
             # fjmp x, 22
             # We know 'x' is true so skip to the next instruction:
-            d = d + 1
+            d += 1
         else: break
       if d != i + c.code[i].jmpDiff:
         c.finalJumpTarget(i, d - i)
@@ -2004,7 +2380,7 @@ proc optimizeJumps(c: PCtx; start: int) =
       var d = i + c.code[i].jmpDiff
       var iters = maxIterations
       while c.code[d].opcode == opcJmp and iters > 0:
-        d = d + c.code[d].jmpDiff
+        d += c.code[d].jmpDiff
         dec iters
       if c.code[d].opcode == opcRet:
         # optimize 'jmp to ret' to 'ret' here
@@ -2014,26 +2390,30 @@ proc optimizeJumps(c: PCtx; start: int) =
     else: discard
 
 proc genProc(c: PCtx; s: PSym): int =
-  var x = s.ast.sons[miscPos]
-  if x.kind == nkEmpty or x[0].kind == nkEmpty:
+  let
+    pos = c.procToCodePos.getOrDefault(s.id)
+    wasNotGenProcBefore = pos == 0
+    noRegistersAllocated = s.offset == -1
+  if wasNotGenProcBefore or noRegistersAllocated:
+    # xxx: the noRegisterAllocated check is required in order to avoid issues
+    #      where nimsuggest can crash due as a macro with pos will be loaded
+    #      but it doesn't have offsets for register allocations see:
+    #      https://github.com/nim-lang/Nim/issues/18385
+    #      Improvements and further use of IC should remove the need for this.
     #if s.name.s == "outterMacro" or s.name.s == "innerProc":
     #  echo "GENERATING CODE FOR ", s.name.s
     let last = c.code.len-1
-    var eofInstr: TInstr
+    var eofInstr: TInstr = default(TInstr)
     if last >= 0 and c.code[last].opcode == opcEof:
       eofInstr = c.code[last]
       c.code.setLen(last)
       c.debug.setLen(last)
     #c.removeLastEof
     result = c.code.len+1 # skip the jump instruction
-    if x.kind == nkEmpty:
-      x = newTree(nkBracket, newIntNode(nkIntLit, result), x)
-    else:
-      x.sons[0] = newIntNode(nkIntLit, result)
-    s.ast.sons[miscPos] = x
+    c.procToCodePos[s.id] = result
     # thanks to the jmp we can add top level statements easily and also nest
     # procs easily:
-    let body = s.getBody
+    let body = transformBody(c.graph, c.idgen, s, if isCompileTimeProc(s): {} else: {useCache})
     let procStart = c.xjmp(body, opcJmp, 0)
     var p = PProc(blocks: @[], sym: s)
     let oldPrc = c.prc
@@ -2042,27 +2422,24 @@ proc genProc(c: PCtx; s: PSym): int =
     genParams(c, s.typ.n)
 
     # allocate additional space for any generically bound parameters
-    if s.kind == skMacro and
-       sfImmediate notin s.flags and
-       s.ast[genericParamsPos].kind != nkEmpty:
+    if s.kind == skMacro and s.isGenericRoutineStrict:
       genGenericParams(c, s.ast[genericParamsPos])
 
     if tfCapturesEnv in s.typ.flags:
-      #let env = s.ast.sons[paramsPos].lastSon.sym
+      #let env = s.ast[paramsPos].lastSon.sym
       #assert env.position == 2
-      c.prc.slots[c.prc.maxSlots] = (inUse: true, kind: slotFixedLet)
-      inc c.prc.maxSlots
+      c.prc.regInfo.add (inUse: true, kind: slotFixedLet)
     gen(c, body)
     # generate final 'return' statement:
     c.gABC(body, opcRet)
     c.patch(procStart)
     c.gABC(body, opcEof, eofInstr.regA)
     c.optimizeJumps(result)
-    s.offset = c.prc.maxSlots
-    #if s.name.s == "calc":
+    s.offset = c.prc.regInfo.len.int32
+    #if s.name.s == "main" or s.name.s == "[]":
     #  echo renderTree(body)
     #  c.echoCode(result)
     c.prc = oldPrc
   else:
-    c.prc.maxSlots = s.offset
-    result = x[0].intVal.int
+    c.prc.regInfo.setLen s.offset
+    result = pos
diff --git a/compiler/vmhooks.nim b/compiler/vmhooks.nim
index 548a3af97..2d7ad63e7 100644
--- a/compiler/vmhooks.nim
+++ b/compiler/vmhooks.nim
@@ -7,13 +7,14 @@
 #    distribution, for details about the copyright.
 #
 
+import pathutils
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 template setX(k, field) {.dirty.} =
-  var s: seq[TFullReg]
-  move(s, cast[seq[TFullReg]](a.slots))
-  if s[a.ra].kind != k:
-    myreset(s[a.ra])
-    s[a.ra].kind = k
-  s[a.ra].field = v
+  a.slots[a.ra].ensureKind(k)
+  a.slots[a.ra].field = v
 
 proc setResult*(a: VmArgs; v: BiggestInt) = setX(rkInt, intVal)
 proc setResult*(a: VmArgs; v: BiggestFloat) = setX(rkFloat, floatVal)
@@ -22,49 +23,55 @@ proc setResult*(a: VmArgs; v: bool) =
   setX(rkInt, intVal)
 
 proc setResult*(a: VmArgs; v: string) =
-  var s: seq[TFullReg]
-  move(s, cast[seq[TFullReg]](a.slots))
-  if s[a.ra].kind != rkNode:
-    myreset(s[a.ra])
-    s[a.ra].kind = rkNode
-  s[a.ra].node = newNode(nkStrLit)
-  s[a.ra].node.strVal = v
+  a.slots[a.ra].ensureKind(rkNode)
+  a.slots[a.ra].node = newNode(nkStrLit)
+  a.slots[a.ra].node.strVal = v
 
 proc setResult*(a: VmArgs; n: PNode) =
-  var s: seq[TFullReg]
-  move(s, cast[seq[TFullReg]](a.slots))
-  if s[a.ra].kind != rkNode:
-    myreset(s[a.ra])
-    s[a.ra].kind = rkNode
-  s[a.ra].node = n
+  a.slots[a.ra].ensureKind(rkNode)
+  a.slots[a.ra].node = n
+
+proc setResult*(a: VmArgs; v: AbsoluteDir) = setResult(a, v.string)
 
 proc setResult*(a: VmArgs; v: seq[string]) =
-  var s: seq[TFullReg]
-  move(s, cast[seq[TFullReg]](a.slots))
-  if s[a.ra].kind != rkNode:
-    myreset(s[a.ra])
-    s[a.ra].kind = rkNode
+  a.slots[a.ra].ensureKind(rkNode)
   var n = newNode(nkBracket)
   for x in v: n.add newStrNode(nkStrLit, x)
-  s[a.ra].node = n
+  a.slots[a.ra].node = n
+
+proc setResult*(a: VmArgs; v: (BiggestInt, BiggestInt)) =
+  a.slots[a.ra].ensureKind(rkNode)
+  var tuplen = newNode(nkTupleConstr)
+  tuplen.add newIntNode(nkIntLit, v[0])
+  tuplen.add newIntNode(nkIntLit, v[1]) 
+  a.slots[a.ra].node = tuplen
 
-template getX(k, field) {.dirty.} =
+template getReg(a, i): untyped =
   doAssert i < a.rc-1
-  let s = cast[seq[TFullReg]](a.slots)
-  doAssert s[i+a.rb+1].kind == k
-  result = s[i+a.rb+1].field
+  a.slots[i+a.rb+1].unsafeAddr
+
+template getX(k, field): untyped {.dirty.} =
+  let p = getReg(a, i)
+  doAssert p.kind == k, $p.kind
+  p.field
+
+proc numArgs*(a: VmArgs): int =
+  result = a.rc-1
 
 proc getInt*(a: VmArgs; i: Natural): BiggestInt = getX(rkInt, intVal)
 proc getBool*(a: VmArgs; i: Natural): bool = getInt(a, i) != 0
 proc getFloat*(a: VmArgs; i: Natural): BiggestFloat = getX(rkFloat, floatVal)
-proc getString*(a: VmArgs; i: Natural): string =
-  doAssert i < a.rc-1
-  let s = cast[seq[TFullReg]](a.slots)
-  doAssert s[i+a.rb+1].kind == rkNode
-  result = s[i+a.rb+1].node.strVal
+proc getNode*(a: VmArgs; i: Natural): PNode = getX(rkNode, node)
+proc getString*(a: VmArgs; i: Natural): string = getX(rkNode, node).strVal
+proc getVar*(a: VmArgs; i: Natural): PNode =
+  let p = getReg(a, i)
+  # depending on whether we come from top-level or proc scope, we need to consider 2 cases
+  case p.kind
+  of rkRegisterAddr: result = p.regAddr.node
+  of rkNodeAddr: result = p.nodeAddr[]
+  else: raiseAssert $p.kind
 
-proc getNode*(a: VmArgs; i: Natural): PNode =
-  doAssert i < a.rc-1
-  let s = cast[seq[TFullReg]](a.slots)
-  doAssert s[i+a.rb+1].kind == rkNode
-  result = s[i+a.rb+1].node
+proc getNodeAddr*(a: VmArgs; i: Natural): PNode =
+  let nodeAddr = getX(rkNodeAddr, nodeAddr)
+  doAssert nodeAddr != nil
+  result = nodeAddr[]
diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim
index eb01b3514..0e67ededa 100644
--- a/compiler/vmmarshal.nim
+++ b/compiler/vmmarshal.nim
@@ -9,56 +9,58 @@
 
 ## Implements marshaling for the VM.
 
-import streams, json, intsets, tables, ast, astalgo, idents, types, msgs,
+import ast, astalgo, idents, types, msgs,
   options, lineinfos
 
+import std/[streams, json, intsets, tables]
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, formatfloat]
+
 proc ptrToInt(x: PNode): int {.inline.} =
   result = cast[int](x) # don't skip alignment
 
 proc getField(n: PNode; position: int): PSym =
   case n.kind
   of nkRecList:
-    for i in countup(0, sonsLen(n) - 1):
-      result = getField(n.sons[i], position)
+    result = nil
+    for i in 0..<n.len:
+      result = getField(n[i], position)
       if result != nil: return
   of nkRecCase:
-    result = getField(n.sons[0], position)
+    result = getField(n[0], position)
     if result != nil: return
-    for i in countup(1, sonsLen(n) - 1):
-      case n.sons[i].kind
+    for i in 1..<n.len:
+      case n[i].kind
       of nkOfBranch, nkElse:
-        result = getField(lastSon(n.sons[i]), position)
+        result = getField(lastSon(n[i]), position)
         if result != nil: return
       else: discard
   of nkSym:
     if n.sym.position == position: result = n.sym
-  else: discard
+    else: result = nil
+  else: result = nil
 
 proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet; conf: ConfigRef)
 
 proc storeObj(s: var string; typ: PType; x: PNode; stored: var IntSet; conf: ConfigRef) =
   assert x.kind == nkObjConstr
   let start = 1
-  for i in countup(start, sonsLen(x) - 1):
+  for i in start..<x.len:
     if i > start: s.add(", ")
-    var it = x.sons[i]
+    var it = x[i]
     if it.kind == nkExprColonExpr:
-      if it.sons[0].kind == nkSym:
-        let field = it.sons[0].sym
+      if it[0].kind == nkSym:
+        let field = it[0].sym
         s.add(escapeJson(field.name.s))
         s.add(": ")
-        storeAny(s, field.typ, it.sons[1], stored, conf)
+        storeAny(s, field.typ, it[1], stored, conf)
     elif typ.n != nil:
       let field = getField(typ.n, i)
       s.add(escapeJson(field.name.s))
       s.add(": ")
       storeAny(s, field.typ, it, stored, conf)
 
-proc skipColon*(n: PNode): PNode =
-  result = n
-  if n.kind == nkExprColonExpr:
-    result = n.sons[1]
-
 proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet;
               conf: ConfigRef) =
   case t.kind
@@ -74,17 +76,17 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet;
     if t.kind == tySequence and a.kind == nkNilLit: s.add("null")
     else:
       s.add("[")
-      for i in 0 .. a.len-1:
+      for i in 0..<a.len:
         if i > 0: s.add(", ")
         storeAny(s, t.elemType, a[i], stored, conf)
       s.add("]")
   of tyTuple:
     s.add("{")
-    for i in 0..<t.len:
+    for i, ti in t.ikids:
       if i > 0: s.add(", ")
       s.add("\"Field" & $i)
       s.add("\": ")
-      storeAny(s, t.sons[i], a[i].skipColon, stored, conf)
+      storeAny(s, ti, a[i].skipColon, stored, conf)
     s.add("}")
   of tyObject:
     s.add("{")
@@ -96,16 +98,17 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet;
       if i > 0: s.add(", ")
       if a[i].kind == nkRange:
         var x = copyNode(a[i][0])
-        storeAny(s, t.lastSon, x, stored, conf)
-        while x.intVal+1 <= a[i][1].intVal:
+        storeAny(s, t.elementType, x, stored, conf)
+        inc x.intVal
+        while x.intVal <= a[i][1].intVal:
           s.add(", ")
-          storeAny(s, t.lastSon, x, stored, conf)
+          storeAny(s, t.elementType, x, stored, conf)
           inc x.intVal
       else:
-        storeAny(s, t.lastSon, a[i], stored, conf)
+        storeAny(s, t.elementType, a[i], stored, conf)
     s.add("]")
   of tyRange, tyGenericInst, tyAlias, tySink:
-    storeAny(s, t.lastSon, a, stored, conf)
+    storeAny(s, t.skipModifier, a, stored, conf)
   of tyEnum:
     # we need a slow linear search because of enums with holes:
     for e in items(t.n):
@@ -124,10 +127,10 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet;
       s.add("[")
       s.add($x.ptrToInt)
       s.add(", ")
-      storeAny(s, t.lastSon, a, stored, conf)
+      storeAny(s, t.elementType, a, stored, conf)
       s.add("]")
-  of tyString, tyCString:
-    if a.kind == nkNilLit or a.strVal.isNil: s.add("null")
+  of tyString, tyCstring:
+    if a.kind == nkNilLit: s.add("null")
     else: s.add(escapeJson(a.strVal))
   of tyInt..tyInt64, tyUInt..tyUInt64: s.add($a.intVal)
   of tyFloat..tyFloat128: s.add($a.floatVal)
@@ -141,9 +144,12 @@ proc storeAny*(s: var string; t: PType; a: PNode; conf: ConfigRef) =
 proc loadAny(p: var JsonParser, t: PType,
              tab: var Table[BiggestInt, PNode];
              cache: IdentCache;
-             conf: ConfigRef): PNode =
+             conf: ConfigRef;
+             idgen: IdGenerator): PNode =
   case t.kind
-  of tyNone: assert false
+  of tyNone:
+    result = nil
+    assert false
   of tyBool:
     case p.kind
     of jsonFalse: result = newIntNode(nkIntLit, 0)
@@ -153,6 +159,7 @@ proc loadAny(p: var JsonParser, t: PType,
   of tyChar:
     if p.kind == jsonString:
       var x = p.str
+      result = nil
       if x.len == 1:
         result = newIntNode(nkIntLit, ord(x[0]))
         next(p)
@@ -161,8 +168,11 @@ proc loadAny(p: var JsonParser, t: PType,
       result = newIntNode(nkIntLit, getInt(p))
       next(p)
       return
+    else:
+      result = nil
     raiseParseErr(p, "string of length 1 expected for a char")
   of tyEnum:
+    result = nil
     if p.kind == jsonString:
       for e in items(t.n):
         if e.sym.name.s == p.str:
@@ -175,7 +185,7 @@ proc loadAny(p: var JsonParser, t: PType,
     next(p)
     result = newNode(nkBracket)
     while p.kind != jsonArrayEnd and p.kind != jsonEof:
-      result.add loadAny(p, t.elemType, tab, cache, conf)
+      result.add loadAny(p, t.elemType, tab, cache, conf, idgen)
     if p.kind == jsonArrayEnd: next(p)
     else: raiseParseErr(p, "']' end of array expected")
   of tySequence:
@@ -187,23 +197,25 @@ proc loadAny(p: var JsonParser, t: PType,
       next(p)
       result = newNode(nkBracket)
       while p.kind != jsonArrayEnd and p.kind != jsonEof:
-        result.add loadAny(p, t.elemType, tab, cache, conf)
+        result.add loadAny(p, t.elemType, tab, cache, conf, idgen)
       if p.kind == jsonArrayEnd: next(p)
       else: raiseParseErr(p, "")
     else:
+      result = nil
       raiseParseErr(p, "'[' expected for a seq")
   of tyTuple:
     if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
     next(p)
     result = newNode(nkTupleConstr)
     var i = 0
+    let tupleLen = t.kidsLen
     while p.kind != jsonObjectEnd and p.kind != jsonEof:
       if p.kind != jsonString:
         raiseParseErr(p, "string expected for a field name")
       next(p)
-      if i >= t.len:
+      if i >= tupleLen:
         raiseParseErr(p, "too many fields to tuple type " & typeToString(t))
-      result.add loadAny(p, t.sons[i], tab, cache, conf)
+      result.add loadAny(p, t[i], tab, cache, conf, idgen)
       inc i
     if p.kind == jsonObjectEnd: next(p)
     else: raiseParseErr(p, "'}' end of object expected")
@@ -221,12 +233,12 @@ proc loadAny(p: var JsonParser, t: PType,
         raiseParseErr(p, "unknown field for object of type " & typeToString(t))
       next(p)
       let pos = field.position + 1
-      if pos >= result.sons.len:
+      if pos >= result.len:
         setLen(result.sons, pos + 1)
       let fieldNode = newNode(nkExprColonExpr)
-      fieldNode.addSon(newSymNode(newSym(skField, ident, nil, unknownLineInfo())))
-      fieldNode.addSon(loadAny(p, field.typ, tab, cache, conf))
-      result.sons[pos] = fieldNode
+      fieldNode.add newSymNode(newSym(skField, ident, idgen, nil, unknownLineInfo))
+      fieldNode.add loadAny(p, field.typ, tab, cache, conf, idgen)
+      result[pos] = fieldNode
     if p.kind == jsonObjectEnd: next(p)
     else: raiseParseErr(p, "'}' end of object expected")
   of tySet:
@@ -234,8 +246,7 @@ proc loadAny(p: var JsonParser, t: PType,
     next(p)
     result = newNode(nkCurly)
     while p.kind != jsonArrayEnd and p.kind != jsonEof:
-      result.add loadAny(p, t.lastSon, tab, cache, conf)
-      next(p)
+      result.add loadAny(p, t.elementType, tab, cache, conf, idgen)
     if p.kind == jsonArrayEnd: next(p)
     else: raiseParseErr(p, "']' end of array expected")
   of tyPtr, tyRef:
@@ -249,17 +260,20 @@ proc loadAny(p: var JsonParser, t: PType,
         raiseParseErr(p, "cannot load object with address " & $p.getInt)
       next(p)
     of jsonArrayStart:
+      result = nil
       next(p)
       if p.kind == jsonInt:
         let idx = p.getInt
         next(p)
-        result = loadAny(p, t.lastSon, tab, cache, conf)
+        result = loadAny(p, t.elementType, tab, cache, conf, idgen)
         tab[idx] = result
       else: raiseParseErr(p, "index for ref type expected")
       if p.kind == jsonArrayEnd: next(p)
       else: raiseParseErr(p, "']' end of ref-address pair expected")
-    else: raiseParseErr(p, "int for pointer type expected")
-  of tyString, tyCString:
+    else:
+      result = nil
+      raiseParseErr(p, "int for pointer type expected")
+  of tyString, tyCstring:
     case p.kind
     of jsonNull:
       result = newNode(nkNilLit)
@@ -267,28 +281,35 @@ proc loadAny(p: var JsonParser, t: PType,
     of jsonString:
       result = newStrNode(nkStrLit, p.str)
       next(p)
-    else: raiseParseErr(p, "string expected")
+    else:
+      result = nil
+      raiseParseErr(p, "string expected")
   of tyInt..tyInt64, tyUInt..tyUInt64:
     if p.kind == jsonInt:
       result = newIntNode(nkIntLit, getInt(p))
       next(p)
       return
+    else:
+      result = nil
     raiseParseErr(p, "int expected")
   of tyFloat..tyFloat128:
     if p.kind == jsonFloat:
       result = newFloatNode(nkFloatLit, getFloat(p))
       next(p)
       return
+    else:
+      result = nil
     raiseParseErr(p, "float expected")
   of tyRange, tyGenericInst, tyAlias, tySink:
-    result = loadAny(p, t.lastSon, tab, cache, conf)
+    result = loadAny(p, t.skipModifier, tab, cache, conf, idgen)
   else:
+    result = nil
     internalError conf, "cannot marshal at compile-time " & t.typeToString
 
-proc loadAny*(s: string; t: PType; cache: IdentCache; conf: ConfigRef): PNode =
+proc loadAny*(s: string; t: PType; cache: IdentCache; conf: ConfigRef; idgen: IdGenerator): PNode =
   var tab = initTable[BiggestInt, PNode]()
-  var p: JsonParser
+  var p: JsonParser = default(JsonParser)
   open(p, newStringStream(s), "unknown file")
   next(p)
-  result = loadAny(p, t, tab, cache, conf)
+  result = loadAny(p, t, tab, cache, conf, idgen)
   close(p)
diff --git a/compiler/vmops.nim b/compiler/vmops.nim
index a7d47d7a3..45194e633 100644
--- a/compiler/vmops.nim
+++ b/compiler/vmops.nim
@@ -7,13 +7,41 @@
 #    distribution, for details about the copyright.
 #
 
-# Unforunately this cannot be a module yet:
+# Unfortunately this cannot be a module yet:
 #import vmdeps, vm
-from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin,
+from std/math import sqrt, ln, log10, log2, exp, round, arccos, arcsin,
   arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc,
-  floor, ceil, fmod
+  floor, ceil, `mod`, cbrt, arcsinh, arccosh, arctanh, erf, erfc, gamma,
+  lgamma, divmod
+from std/sequtils import toSeq
+when declared(math.copySign):
+  # pending bug #18762, avoid renaming math
+  from std/math as math2 import copySign
 
-from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir
+when declared(math.signbit):
+  # ditto
+  from std/math as math3 import signbit
+
+
+from std/envvars import getEnv, existsEnv, delEnv, putEnv, envPairs
+from std/os import getAppFilename
+from std/private/oscommon import dirExists, fileExists
+from std/private/osdirs import walkDir, createDir
+from std/private/ospaths2 import getCurrentDir
+
+from std/times import cpuTime
+from std/hashes import hash
+from std/osproc import nil
+
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
+else:
+  from std/formatfloat import addFloatRoundtrip, addFloatSprintf
+
+
+# There are some useful procs in vmconv.
+import vmconv, vmmarshal
 
 template mathop(op) {.dirty.} =
   registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`)
@@ -21,25 +49,43 @@ template mathop(op) {.dirty.} =
 template osop(op) {.dirty.} =
   registerCallback(c, "stdlib.os." & astToStr(op), `op Wrapper`)
 
-template ospathsop(op) {.dirty.} =
-  registerCallback(c, "stdlib.ospaths." & astToStr(op), `op Wrapper`)
+template oscommonop(op) {.dirty.} =
+  registerCallback(c, "stdlib.oscommon." & astToStr(op), `op Wrapper`)
+
+template osdirsop(op) {.dirty.} =
+  registerCallback(c, "stdlib.osdirs." & astToStr(op), `op Wrapper`)
+
+template envvarsop(op) {.dirty.} =
+  registerCallback(c, "stdlib.envvars." & astToStr(op), `op Wrapper`)
+
+template timesop(op) {.dirty.} =
+  registerCallback(c, "stdlib.times." & astToStr(op), `op Wrapper`)
 
 template systemop(op) {.dirty.} =
   registerCallback(c, "stdlib.system." & astToStr(op), `op Wrapper`)
 
+template ioop(op) {.dirty.} =
+  registerCallback(c, "stdlib.syncio." & astToStr(op), `op Wrapper`)
+
 template macrosop(op) {.dirty.} =
   registerCallback(c, "stdlib.macros." & astToStr(op), `op Wrapper`)
 
-template wrap1f_math(op) {.dirty.} =
+template wrap1fMath(op) {.dirty.} =
   proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+    doAssert a.numArgs == 1
     setResult(a, op(getFloat(a, 0)))
   mathop op
 
-template wrap2f_math(op) {.dirty.} =
+template wrap2fMath(op) {.dirty.} =
   proc `op Wrapper`(a: VmArgs) {.nimcall.} =
     setResult(a, op(getFloat(a, 0), getFloat(a, 1)))
   mathop op
 
+template wrap2iMath(op) {.dirty.} =
+  proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+    setResult(a, op(getInt(a, 0), getInt(a, 1)))
+  mathop op
+
 template wrap0(op, modop) {.dirty.} =
   proc `op Wrapper`(a: VmArgs) {.nimcall.} =
     setResult(a, op())
@@ -55,6 +101,11 @@ template wrap2s(op, modop) {.dirty.} =
     setResult(a, op(getString(a, 0), getString(a, 1)))
   modop op
 
+template wrap2si(op, modop) {.dirty.} =
+  proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+    setResult(a, op(getString(a, 0), getInt(a, 1)))
+  modop op
+
 template wrap1svoid(op, modop) {.dirty.} =
   proc `op Wrapper`(a: VmArgs) {.nimcall.} =
     op(getString(a, 0))
@@ -65,58 +116,302 @@ template wrap2svoid(op, modop) {.dirty.} =
     op(getString(a, 0), getString(a, 1))
   modop op
 
+template wrapDangerous1svoid(op, modop) {.dirty.} =
+  if vmopsDanger notin c.config.features and (defined(nimsuggest) or c.config.cmd == cmdCheck):
+    proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+      discard
+    modop op
+  else:
+    proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+      op(getString(a, 0))
+    modop op
+
+template wrapDangerous2svoid(op, modop) {.dirty.} =
+  if vmopsDanger notin c.config.features and (defined(nimsuggest) or c.config.cmd == cmdCheck):
+    proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+      discard
+    modop op
+  else:
+    proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+      op(getString(a, 0), getString(a, 1))
+    modop op
+
 proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} =
   setResult(a, if a.currentException.isNil: ""
-               else: a.currentException.sons[3].skipColon.strVal)
+               else: a.currentException[3].skipColon.strVal)
+
+proc getCurrentExceptionWrapper(a: VmArgs) {.nimcall.} =
+  setResult(a, a.currentException)
 
 proc staticWalkDirImpl(path: string, relative: bool): PNode =
   result = newNode(nkBracket)
   for k, f in walkDir(path, relative):
-    result.add newTree(nkTupleConstr, newIntNode(nkIntLit, k.ord),
-                              newStrNode(nkStrLit, f))
+    result.add toLit((k, f))
+
+from std / compilesettings import SingleValueSetting, MultipleValueSetting
+
+proc querySettingImpl(conf: ConfigRef, switch: BiggestInt): string =
+  {.push warning[Deprecated]:off.}
+  case SingleValueSetting(switch)
+  of arguments: result = conf.arguments
+  of outFile: result = conf.outFile.string
+  of outDir: result = conf.outDir.string
+  of nimcacheDir: result = conf.getNimcacheDir().string
+  of projectName: result = conf.projectName
+  of projectPath: result = conf.projectPath.string
+  of projectFull: result = conf.projectFull.string
+  of command: result = conf.command
+  of commandLine: result = conf.commandLine
+  of linkOptions: result = conf.linkOptions
+  of compileOptions: result = conf.compileOptions
+  of ccompilerPath: result = conf.cCompilerPath
+  of backend: result = $conf.backend
+  of libPath: result = conf.libpath.string
+  of gc: result = $conf.selectedGC
+  of mm: result = $conf.selectedGC
+  {.pop.}
+
+proc querySettingSeqImpl(conf: ConfigRef, switch: BiggestInt): seq[string] =
+  template copySeq(field: untyped): untyped =
+    result = @[]
+    for i in field: result.add i.string
+
+  case MultipleValueSetting(switch)
+  of nimblePaths: copySeq(conf.nimblePaths)
+  of searchPaths: copySeq(conf.searchPaths)
+  of lazyPaths: copySeq(conf.lazyPaths)
+  of commandArgs: result = conf.commandArgs
+  of cincludes: copySeq(conf.cIncludes)
+  of clibs: copySeq(conf.cLibs)
+
+proc stackTrace2(c: PCtx, msg: string, n: PNode) =
+  stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr, msg, n.info)
+
 
 proc registerAdditionalOps*(c: PCtx) =
+
+  template wrapIterator(fqname: string, iter: untyped) =
+    registerCallback c, fqname, proc(a: VmArgs) =
+      setResult(a, toLit(toSeq(iter)))
+
+
   proc gorgeExWrapper(a: VmArgs) =
-    let (s, e) = opGorge(getString(a, 0), getString(a, 1), getString(a, 2),
+    let ret = opGorge(getString(a, 0), getString(a, 1), getString(a, 2),
                          a.currentLineInfo, c.config)
-    setResult a, newTree(nkTupleConstr, newStrNode(nkStrLit, s), newIntNode(nkIntLit, e))
+    setResult a, ret.toLit
 
   proc getProjectPathWrapper(a: VmArgs) =
-    setResult a, c.config.projectPath
-
-  wrap1f_math(sqrt)
-  wrap1f_math(ln)
-  wrap1f_math(log10)
-  wrap1f_math(log2)
-  wrap1f_math(exp)
-  wrap1f_math(round)
-  wrap1f_math(arccos)
-  wrap1f_math(arcsin)
-  wrap1f_math(arctan)
-  wrap2f_math(arctan2)
-  wrap1f_math(cos)
-  wrap1f_math(cosh)
-  wrap2f_math(hypot)
-  wrap1f_math(sinh)
-  wrap1f_math(sin)
-  wrap1f_math(tan)
-  wrap1f_math(tanh)
-  wrap2f_math(pow)
-  wrap1f_math(trunc)
-  wrap1f_math(floor)
-  wrap1f_math(ceil)
-  wrap2f_math(fmod)
+    setResult a, c.config.projectPath.string
+
+  wrap1fMath(sqrt)
+  wrap1fMath(cbrt)
+  wrap1fMath(ln)
+  wrap1fMath(log10)
+  wrap1fMath(log2)
+  wrap1fMath(exp)
+  wrap1fMath(arccos)
+  wrap1fMath(arcsin)
+  wrap1fMath(arctan)
+  wrap1fMath(arcsinh)
+  wrap1fMath(arccosh)
+  wrap1fMath(arctanh)
+  wrap2fMath(arctan2)
+  wrap1fMath(cos)
+  wrap1fMath(cosh)
+  wrap2fMath(hypot)
+  wrap1fMath(sinh)
+  wrap1fMath(sin)
+  wrap1fMath(tan)
+  wrap1fMath(tanh)
+  wrap2fMath(pow)
+  wrap1fMath(trunc)
+  wrap1fMath(floor)
+  wrap1fMath(ceil)
+  wrap1fMath(erf)
+  wrap1fMath(erfc)
+  wrap1fMath(gamma)
+  wrap1fMath(lgamma)
+  wrap2iMath(divmod)
+
+  when declared(copySign):
+    wrap2fMath(copySign)
+
+  when declared(signbit):
+    wrap1fMath(signbit)
+
+  registerCallback c, "stdlib.math.round", proc (a: VmArgs) {.nimcall.} =
+    let n = a.numArgs
+    case n
+    of 1: setResult(a, round(getFloat(a, 0)))
+    of 2: setResult(a, round(getFloat(a, 0), getInt(a, 1).int))
+    else: raiseAssert $n
+
+  proc `mod Wrapper`(a: VmArgs) {.nimcall.} =
+    setResult(a, `mod`(getFloat(a, 0), getFloat(a, 1)))
+  registerCallback(c, "stdlib.math.mod", `mod Wrapper`)
 
   when defined(nimcore):
-    wrap2s(getEnv, ospathsop)
-    wrap1s(existsEnv, ospathsop)
-    wrap2svoid(putEnv, ospathsop)
-    wrap1s(dirExists, osop)
-    wrap1s(fileExists, osop)
-    wrap2svoid(writeFile, systemop)
-    wrap1s(readFile, systemop)
+    wrap2s(getEnv, envvarsop)
+    wrap1s(existsEnv, envvarsop)
+    wrap2svoid(putEnv, envvarsop)
+    wrap1svoid(delEnv, envvarsop)
+    wrap1s(dirExists, oscommonop)
+    wrap1s(fileExists, oscommonop)
+    wrapDangerous2svoid(writeFile, ioop)
+    wrapDangerous1svoid(createDir, osdirsop)
+    wrap1s(readFile, ioop)
+    wrap2si(readLines, ioop)
     systemop getCurrentExceptionMsg
-    registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
+    systemop getCurrentException
+    registerCallback c, "stdlib.osdirs.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
       setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
-    systemop gorgeEx
+    registerCallback c, "stdlib.staticos.staticDirExists", proc (a: VmArgs) {.nimcall.} =
+      setResult(a, dirExists(getString(a, 0)))
+    registerCallback c, "stdlib.staticos.staticFileExists", proc (a: VmArgs) {.nimcall.} =
+      setResult(a, fileExists(getString(a, 0)))
+    registerCallback c, "stdlib.compilesettings.querySetting", proc (a: VmArgs) =
+      setResult(a, querySettingImpl(c.config, getInt(a, 0)))
+    registerCallback c, "stdlib.compilesettings.querySettingSeq", proc (a: VmArgs) =
+      setResult(a, querySettingSeqImpl(c.config, getInt(a, 0)))
+
+    if defined(nimsuggest) or c.config.cmd == cmdCheck:
+      discard "don't run staticExec for 'nim suggest'"
+    else:
+      systemop gorgeEx
   macrosop getProjectPath
+
+  registerCallback c, "stdlib.os.getCurrentCompilerExe", proc (a: VmArgs) {.nimcall.} =
+    setResult(a, getAppFilename())
+
+  registerCallback c, "stdlib.macros.symBodyHash", proc (a: VmArgs) =
+    let n = getNode(a, 0)
+    if n.kind != nkSym:
+      stackTrace2(c, "symBodyHash() requires a symbol. '$#' is of kind '$#'" % [$n, $n.kind], n)
+    setResult(a, $symBodyDigest(c.graph, n.sym))
+
+  registerCallback c, "stdlib.macros.isExported", proc(a: VmArgs) =
+    let n = getNode(a, 0)
+    if n.kind != nkSym:
+      stackTrace2(c, "isExported() requires a symbol. '$#' is of kind '$#'" % [$n, $n.kind], n)
+    setResult(a, sfExported in n.sym.flags)
+
+  registerCallback c, "stdlib.macrocache.hasKey", proc (a: VmArgs) =
+    let
+      table = getString(a, 0)
+      key = getString(a, 1)
+    setResult(a, table in c.graph.cacheTables and key in c.graph.cacheTables[table])
+
+  registerCallback c, "stdlib.vmutils.vmTrace", proc (a: VmArgs) =
+    c.config.isVmTrace = getBool(a, 0)
+
+  proc hashVmImpl(a: VmArgs) =
+    var res = hashes.hash(a.getString(0), a.getInt(1).int, a.getInt(2).int)
+    if c.config.backend == backendJs:
+      # emulate JS's terrible integers:
+      res = cast[int32](res)
+    setResult(a, res)
+
+  registerCallback c, "stdlib.hashes.hashVmImpl", hashVmImpl
+
+  proc hashVmImplByte(a: VmArgs) =
+    # nkBracket[...]
+    let sPos = a.getInt(1).int
+    let ePos = a.getInt(2).int
+    let arr = a.getNode(0)
+    var bytes = newSeq[byte](arr.len)
+    for i in 0..<arr.len:
+      bytes[i] = byte(arr[i].intVal and 0xff)
+
+    var res = hashes.hash(bytes, sPos, ePos)
+    if c.config.backend == backendJs:
+      # emulate JS's terrible integers:
+      res = cast[int32](res)
+    setResult(a, res)
+
+  registerCallback c, "stdlib.hashes.hashVmImplByte", hashVmImplByte
+  registerCallback c, "stdlib.hashes.hashVmImplChar", hashVmImplByte
+
+  if optBenchmarkVM in c.config.globalOptions or vmopsDanger in c.config.features:
+    wrap0(cpuTime, timesop)
+  else:
+    proc cpuTime(): float = 5.391245e-44  # Randomly chosen
+    wrap0(cpuTime, timesop)
+
+  if vmopsDanger in c.config.features:
+    ## useful procs but these should be opt-in because they may impact
+    ## reproducible builds and users need to understand that this runs at CT.
+    ## Note that `staticExec` can already do equal amount of damage so it's more
+    ## of a semantic issue than a security issue.
+    registerCallback c, "stdlib.ospaths2.getCurrentDir", proc (a: VmArgs) {.nimcall.} =
+      setResult(a, getCurrentDir())
+    registerCallback c, "stdlib.osproc.execCmdEx", proc (a: VmArgs) {.nimcall.} =
+      let options = getNode(a, 1).fromLit(set[osproc.ProcessOption])
+      a.setResult osproc.execCmdEx(getString(a, 0), options).toLit
+    registerCallback c, "stdlib.times.getTimeImpl", proc (a: VmArgs) =
+      let obj = a.getNode(0).typ.n
+      setResult(a, times.getTime().toTimeLit(c, obj, a.currentLineInfo))
+
+  proc getEffectList(c: PCtx; a: VmArgs; effectIndex: int) =
+    let fn = getNode(a, 0)
+    var list = newNodeI(nkBracket, fn.info)
+    if fn.typ != nil and fn.typ.n != nil and fn.typ.n[0].len >= effectListLen and
+        fn.typ.n[0][effectIndex] != nil:
+      for e in fn.typ.n[0][effectIndex]:
+        list.add opMapTypeInstToAst(c.cache, e.typ.skipTypes({tyRef}), e.info, c.idgen)
+    else:
+      list.add newIdentNode(getIdent(c.cache, "UncomputedEffects"), fn.info)
+
+    setResult(a, list)
+
+  registerCallback c, "stdlib.effecttraits.getRaisesListImpl", proc (a: VmArgs) =
+    getEffectList(c, a, exceptionEffects)
+  registerCallback c, "stdlib.effecttraits.getTagsListImpl", proc (a: VmArgs) =
+    getEffectList(c, a, tagEffects)
+  registerCallback c, "stdlib.effecttraits.getForbidsListImpl", proc (a: VmArgs) =
+    getEffectList(c, a, forbiddenEffects)
+
+  registerCallback c, "stdlib.effecttraits.isGcSafeImpl", proc (a: VmArgs) =
+    let fn = getNode(a, 0)
+    setResult(a, fn.typ != nil and tfGcSafe in fn.typ.flags)
+
+  registerCallback c, "stdlib.effecttraits.hasNoSideEffectsImpl", proc (a: VmArgs) =
+    let fn = getNode(a, 0)
+    setResult(a, (fn.typ != nil and tfNoSideEffect in fn.typ.flags) or
+                 (fn.kind == nkSym and fn.sym.kind == skFunc))
+
+  registerCallback c, "stdlib.typetraits.hasClosureImpl", proc (a: VmArgs) =
+    let fn = getNode(a, 0)
+    setResult(a, fn.kind == nkClosure or (fn.typ != nil and fn.typ.callConv == ccClosure))
+
+  registerCallback c, "stdlib.formatfloat.addFloatRoundtrip", proc(a: VmArgs) =
+    let p = a.getVar(0)
+    let x = a.getFloat(1)
+    addFloatRoundtrip(p.strVal, x)
+
+  registerCallback c, "stdlib.formatfloat.addFloatSprintf", proc(a: VmArgs) =
+    let p = a.getVar(0)
+    let x = a.getFloat(1)
+    addFloatSprintf(p.strVal, x)
+
+  registerCallback c, "stdlib.strutils.formatBiggestFloat", proc(a: VmArgs) =
+    setResult(a, formatBiggestFloat(a.getFloat(0), FloatFormatMode(a.getInt(1)),
+                                    a.getInt(2), chr(a.getInt(3))))
+
+  wrapIterator("stdlib.envvars.envPairsImplSeq"): envPairs()
+
+  registerCallback c, "stdlib.marshal.toVM", proc(a: VmArgs) =
+    let typ = a.getNode(0).typ
+    case typ.kind
+    of tyInt..tyInt64, tyUInt..tyUInt64:
+      setResult(a, loadAny(a.getString(1), typ, c.cache, c.config, c.idgen).intVal)
+    of tyFloat..tyFloat128:
+      setResult(a, loadAny(a.getString(1), typ, c.cache, c.config, c.idgen).floatVal)
+    else:
+      setResult(a, loadAny(a.getString(1), typ, c.cache, c.config, c.idgen))
+
+  registerCallback c, "stdlib.marshal.loadVM", proc(a: VmArgs) =
+    let typ = a.getNode(0).typ
+    let p = a.getReg(1)
+    var res: string = ""
+    storeAny(res, typ, regToNode(p[]), c.config)
+    setResult(a, res)
diff --git a/compiler/vmprofiler.nim b/compiler/vmprofiler.nim
new file mode 100644
index 000000000..3f0db84bd
--- /dev/null
+++ b/compiler/vmprofiler.nim
@@ -0,0 +1,45 @@
+
+import options, vmdef, lineinfos, msgs
+
+import std/[times, strutils, tables]
+
+proc enter*(prof: var Profiler, c: PCtx, tos: PStackFrame) {.inline.} =
+  if optProfileVM in c.config.globalOptions:
+    prof.tEnter = cpuTime()
+    prof.tos = tos
+
+proc leaveImpl(prof: var Profiler, c: PCtx) {.noinline.} =
+  let tLeave = cpuTime()
+  var tos = prof.tos
+  var data = c.config.vmProfileData.data
+  while tos != nil:
+    if tos.prc != nil:
+      let li = tos.prc.info
+      if li notin data:
+        data[li] = ProfileInfo()
+      data[li].time += tLeave - prof.tEnter
+      if tos == prof.tos:
+        inc data[li].count
+    tos = tos.next
+
+proc leave*(prof: var Profiler, c: PCtx) {.inline.} =
+  if optProfileVM in c.config.globalOptions:
+    leaveImpl(prof, c)
+
+proc dump*(conf: ConfigRef, pd: ProfileData): string =
+  var data = pd.data
+  result = "\nprof:     µs    #instr  location"
+  for i in 0..<32:
+    var tMax: float
+    var infoMax: ProfileInfo = default(ProfileInfo)
+    var flMax: TLineInfo = default(TLineInfo)
+    for fl, info in data:
+      if info.time > infoMax.time:
+        infoMax = info
+        flMax = fl
+    if infoMax.count == 0:
+      break
+    result.add  "  " & align($int(infoMax.time * 1e6), 10) &
+                       align($int(infoMax.count), 10) & "  " &
+                       conf.toFileLineCol(flMax) & "\n"
+    data.del flMax
diff --git a/compiler/vtables.nim b/compiler/vtables.nim
new file mode 100644
index 000000000..928c64dd5
--- /dev/null
+++ b/compiler/vtables.nim
@@ -0,0 +1,167 @@
+import ast, modulegraphs, magicsys, lineinfos, options, cgmeth, types
+import std/[algorithm, tables, intsets, assertions]
+
+
+
+proc genVTableDispatcher(g: ModuleGraph; methods: seq[PSym]; index: int): PSym =
+#[
+proc dispatch(x: Base, params: ...) =
+  cast[proc bar(x: Base, params: ...)](x.vTable[index])(x, params)
+]#
+  var base = methods[0].ast[dispatcherPos].sym
+  result = base
+  var paramLen = base.typ.signatureLen
+  var body = newNodeI(nkStmtList, base.info)
+
+  var disp = newNodeI(nkIfStmt, base.info)
+
+  var vTableAccess = newNodeIT(nkBracketExpr, base.info, base.typ)
+  let nimGetVTableSym = getCompilerProc(g, "nimGetVTable")
+  let ptrPNimType = nimGetVTableSym.typ.n[1].sym.typ
+
+  var nTyp = base.typ.n[1].sym.typ
+  var dispatchObject = newSymNode(base.typ.n[1].sym)
+  if nTyp.kind == tyObject:
+    dispatchObject = newTree(nkAddr, dispatchObject)
+  else:
+    if g.config.backend != backendCpp: # TODO: maybe handle ptr?
+      if nTyp.kind == tyVar and nTyp.skipTypes({tyVar}).kind != tyObject:
+        dispatchObject = newTree(nkDerefExpr, dispatchObject)
+
+  var getVTableCall = newTree(nkCall,
+    newSymNode(nimGetVTableSym),
+    dispatchObject,
+    newIntNode(nkIntLit, index)
+  )
+  getVTableCall.typ = base.typ
+  var vTableCall = newNodeIT(nkCall, base.info, base.typ.returnType)
+  var castNode = newTree(nkCast,
+        newNodeIT(nkType, base.info, base.typ),
+        getVTableCall)
+
+  castNode.typ = base.typ
+  vTableCall.add castNode
+  for col in 1..<paramLen:
+    let param = base.typ.n[col].sym
+    vTableCall.add newSymNode(param)
+
+  var ret: PNode
+  if base.typ.returnType != nil:
+    var a = newNodeI(nkFastAsgn, base.info)
+    a.add newSymNode(base.ast[resultPos].sym)
+    a.add vTableCall
+    ret = newNodeI(nkReturnStmt, base.info)
+    ret.add a
+  else:
+    ret = vTableCall
+
+  if base.typ.n[1].sym.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
+    let ifBranch = newNodeI(nkElifBranch, base.info)
+    let boolType = getSysType(g, unknownLineInfo, tyBool)
+    var isNil = getSysMagic(g, unknownLineInfo, "isNil", mIsNil)
+    let checkSelf = newNodeIT(nkCall, base.info, boolType)
+    checkSelf.add newSymNode(isNil)
+    checkSelf.add newSymNode(base.typ.n[1].sym)
+    ifBranch.add checkSelf
+    ifBranch.add newTree(nkCall,
+        newSymNode(getCompilerProc(g, "chckNilDisp")), newSymNode(base.typ.n[1].sym))
+    let elseBranch = newTree(nkElifBranch, ret)
+    disp.add ifBranch
+    disp.add elseBranch
+  else:
+    disp = ret
+
+  body.add disp
+  body.flags.incl nfTransf # should not be further transformed
+  result.ast[bodyPos] = body
+
+proc containGenerics(base: PType, s: seq[tuple[depth: int, value: PType]]): bool =
+  result = tfHasMeta in base.flags
+  for i in s:
+    if tfHasMeta in i.value.flags:
+      result = true
+      break
+
+proc collectVTableDispatchers*(g: ModuleGraph) =
+  var itemTable = initTable[ItemId, seq[LazySym]]()
+  var rootTypeSeq = newSeq[PType]()
+  var rootItemIdCount = initCountTable[ItemId]()
+  for bucket in 0..<g.methods.len:
+    var relevantCols = initIntSet()
+    if relevantCol(g.methods[bucket].methods, 1): incl(relevantCols, 1)
+    sortBucket(g.methods[bucket].methods, relevantCols)
+    let base = g.methods[bucket].methods[^1]
+    let baseType = base.typ.firstParamType.skipTypes(skipPtrs-{tyTypeDesc})
+    if baseType.itemId in g.objectTree and not containGenerics(baseType, g.objectTree[baseType.itemId]):
+      let methodIndexLen = g.bucketTable[baseType.itemId]
+      if baseType.itemId notin itemTable: # once is enough
+        rootTypeSeq.add baseType
+        itemTable[baseType.itemId] = newSeq[LazySym](methodIndexLen)
+
+        sort(g.objectTree[baseType.itemId], cmp = proc (x, y: tuple[depth: int, value: PType]): int =
+          if x.depth >= y.depth: 1
+          else: -1
+          )
+
+        for item in g.objectTree[baseType.itemId]:
+          if item.value.itemId notin itemTable:
+            itemTable[item.value.itemId] = newSeq[LazySym](methodIndexLen)
+
+      var mIndex = 0 # here is the correpsonding index
+      if baseType.itemId notin rootItemIdCount:
+        rootItemIdCount[baseType.itemId] = 1
+      else:
+        mIndex = rootItemIdCount[baseType.itemId]
+        rootItemIdCount.inc(baseType.itemId)
+      for idx in 0..<g.methods[bucket].methods.len:
+        let obj = g.methods[bucket].methods[idx].typ.firstParamType.skipTypes(skipPtrs)
+        itemTable[obj.itemId][mIndex] = LazySym(sym: g.methods[bucket].methods[idx])
+      g.addDispatchers genVTableDispatcher(g, g.methods[bucket].methods, mIndex)
+    else: # if the base object doesn't have this method
+      g.addDispatchers genIfDispatcher(g, g.methods[bucket].methods, relevantCols, g.idgen)
+
+proc sortVTableDispatchers*(g: ModuleGraph) =
+  var itemTable = initTable[ItemId, seq[LazySym]]()
+  var rootTypeSeq = newSeq[ItemId]()
+  var rootItemIdCount = initCountTable[ItemId]()
+  for bucket in 0..<g.methods.len:
+    var relevantCols = initIntSet()
+    if relevantCol(g.methods[bucket].methods, 1): incl(relevantCols, 1)
+    sortBucket(g.methods[bucket].methods, relevantCols)
+    let base = g.methods[bucket].methods[^1]
+    let baseType = base.typ.firstParamType.skipTypes(skipPtrs-{tyTypeDesc})
+    if baseType.itemId in g.objectTree and not containGenerics(baseType, g.objectTree[baseType.itemId]):
+      let methodIndexLen = g.bucketTable[baseType.itemId]
+      if baseType.itemId notin itemTable: # once is enough
+        rootTypeSeq.add baseType.itemId
+        itemTable[baseType.itemId] = newSeq[LazySym](methodIndexLen)
+
+        sort(g.objectTree[baseType.itemId], cmp = proc (x, y: tuple[depth: int, value: PType]): int =
+          if x.depth >= y.depth: 1
+          else: -1
+          )
+
+        for item in g.objectTree[baseType.itemId]:
+          if item.value.itemId notin itemTable:
+            itemTable[item.value.itemId] = newSeq[LazySym](methodIndexLen)
+
+      var mIndex = 0 # here is the correpsonding index
+      if baseType.itemId notin rootItemIdCount:
+        rootItemIdCount[baseType.itemId] = 1
+      else:
+        mIndex = rootItemIdCount[baseType.itemId]
+        rootItemIdCount.inc(baseType.itemId)
+      for idx in 0..<g.methods[bucket].methods.len:
+        let obj = g.methods[bucket].methods[idx].typ.firstParamType.skipTypes(skipPtrs)
+        itemTable[obj.itemId][mIndex] = LazySym(sym: g.methods[bucket].methods[idx])
+
+  for baseType in rootTypeSeq:
+    g.setMethodsPerType(baseType, itemTable[baseType])
+    for item in g.objectTree[baseType]:
+      let typ = item.value.skipTypes(skipPtrs)
+      let idx = typ.itemId
+      for mIndex in 0..<itemTable[idx].len:
+        if itemTable[idx][mIndex].sym == nil:
+          let parentIndex = typ.baseClass.skipTypes(skipPtrs).itemId
+          itemTable[idx][mIndex] = itemTable[parentIndex][mIndex]
+      g.setMethodsPerType(idx, itemTable[idx])
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index 91b527e02..39e0b2e25 100644
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -13,78 +13,116 @@
 # does not support strings. Without this the code would
 # be slow and unreadable.
 
-from strutils import cmpIgnoreStyle
-
-# Keywords must be kept sorted and within a range
-
 type
   TSpecialWord* = enum
-    wInvalid,
-
-    wAddr, wAnd, wAs, wAsm,
-    wBind, wBlock, wBreak, wCase, wCast, wConcept, wConst,
-    wContinue, wConverter, wDefer, wDiscard, wDistinct, wDiv, wDo,
-    wElif, wElse, wEnd, wEnum, wExcept, wExport,
-    wFinally, wFor, wFrom, wFunc, wIf, wImport, wIn,
-    wInclude, wInterface, wIs, wIsnot, wIterator, wLet,
-    wMacro, wMethod, wMixin, wMod, wNil,
-    wNot, wNotin, wObject, wOf, wOr, wOut, wProc, wPtr, wRaise, wRef, wReturn,
-    wShl, wShr, wStatic, wTemplate, wTry, wTuple, wType, wUsing, wVar,
-    wWhen, wWhile, wXor, wYield,
-
-    wColon, wColonColon, wEquals, wDot, wDotDot,
-    wStar, wMinus,
-    wMagic, wThread, wFinal, wProfiler, wMemTracker, wObjChecks,
-    wIntDefine, wStrDefine,
-
-    wDestroy,
-
-    wImmediate, wConstructor, wDestructor, wDelegator, wOverride,
-    wImportCpp, wImportObjC,
-    wImportCompilerProc,
-    wImportc, wExportc, wExportNims, wIncompleteStruct, wRequiresInit,
-    wAlign, wNodecl, wPure, wSideeffect, wHeader,
-    wNosideeffect, wGcSafe, wNoreturn, wMerge, wLib, wDynlib,
-    wCompilerproc, wCore, wProcVar, wBase, wUsed,
-    wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef,
-    wLinedir, wStacktrace, wLinetrace, wLink, wCompile,
-    wLinksys, wDeprecated, wVarargs, wCallconv, wBreakpoint, wDebugger,
-    wNimcall, wStdcall, wCdecl, wSafecall, wSyscall, wInline, wNoInline,
-    wFastcall, wClosure, wNoconv, wOn, wOff, wChecks, wRangechecks,
-    wBoundchecks, wOverflowchecks, wNilchecks,
-    wFloatchecks, wNanChecks, wInfChecks, wMoveChecks,
-    wAssertions, wPatterns, wWarnings,
-    wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags,
-    wDeadCodeElimUnused,  # deprecated, dead code elim always happens
-    wSafecode, wPackage, wNoForward, wReorder, wNoRewrite,
-    wPragma,
-    wCompileTime, wNoInit,
-    wPassc, wPassl, wBorrow, wDiscardable,
-    wFieldChecks,
-    wWatchPoint, wSubsChar,
-    wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto,
-    wInjectStmt, wExperimental,
-    wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
-    wAsmNoStackFrame,
-    wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wGuard, wLocks,
-    wPartial, wExplain, wLiftLocals,
-
-    wAuto, wBool, wCatch, wChar, wClass,
-    wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
-    wExplicit, wExtern, wFalse, wFloat, wFriend,
-    wGoto, wInt, wLong, wMutable, wNamespace, wNew, wOperator,
-    wPrivate, wProtected, wPublic, wRegister, wReinterpret_cast,
-    wShort, wSigned, wSizeof, wStatic_cast, wStruct, wSwitch,
-    wThis, wThrow, wTrue, wTypedef, wTypeid, wTypename,
-    wUnion, wPacked, wUnsigned, wVirtual, wVoid, wVolatile, wWchar_t,
-
-    wAlignas, wAlignof, wConstexpr, wDecltype, wNullptr, wNoexcept,
-    wThread_local, wStatic_assert, wChar16_t, wChar32_t,
-
-    wStdIn, wStdOut, wStdErr,
-
-    wInOut, wByCopy, wByRef, wOneWay,
-    wBitsize,
+    wInvalid = "",
+    wAddr = "addr", wAnd = "and", wAs = "as", wAsm = "asm",
+    wBind = "bind", wBlock = "block", wBreak = "break", wCase = "case", wCast = "cast",
+    wConcept = "concept", wConst = "const", wContinue = "continue", wConverter = "converter",
+    wDefer = "defer", wDiscard = "discard", wDistinct = "distinct", wDiv = "div", wDo = "do",
+    wElif = "elif", wElse = "else", wEnd = "end", wEnum = "enum", wExcept = "except",
+    wExport = "export", wFinally = "finally", wFor = "for", wFrom = "from", wFunc = "func",
+    wIf = "if", wImport = "import", wIn = "in", wInclude = "include", wInterface = "interface",
+    wIs = "is", wIsnot = "isnot",  wIterator = "iterator", wLet = "let", wMacro = "macro",
+    wMethod = "method", wMixin = "mixin", wMod = "mod", wNil = "nil", wNot = "not", wNotin = "notin",
+    wObject = "object", wOf = "of", wOr = "or", wOut = "out", wProc = "proc", wPtr = "ptr",
+    wRaise = "raise", wRef = "ref", wReturn = "return", wShl = "shl", wShr = "shr", wStatic = "static",
+    wTemplate = "template", wTry = "try", wTuple = "tuple", wType = "type", wUsing = "using",
+    wVar = "var", wWhen = "when", wWhile = "while", wXor = "xor", wYield = "yield",
+
+    wColon = ":", wColonColon = "::", wEquals = "=", wDot = ".", wDotDot = "..",
+    wStar = "*", wMinus = "-",
+    wUnderscore = "_",
+    wMagic = "magic", wThread = "thread", wFinal = "final", wProfiler = "profiler",
+    wMemTracker = "memtracker", wObjChecks = "objchecks",
+    wIntDefine = "intdefine", wStrDefine = "strdefine", wBoolDefine = "booldefine",
+    wCursor = "cursor", wNoalias = "noalias", wEffectsOf = "effectsOf",
+    wUncheckedAssign = "uncheckedAssign", wRunnableExamples = "runnableExamples",
+
+    wImmediate = "immediate", wConstructor = "constructor", wDestructor = "destructor",
+    wDelegator = "delegator", wOverride = "override", wImportCpp = "importcpp",
+    wCppNonPod = "cppNonPod",
+    wImportObjC = "importobjc", wImportCompilerProc = "importCompilerProc",
+    wImportc = "importc", wImportJs = "importjs", wExportc = "exportc", wExportCpp = "exportcpp",
+    wExportNims = "exportnims",
+    wIncompleteStruct = "incompleteStruct", # deprecated
+    wCompleteStruct = "completeStruct", wRequiresInit = "requiresInit", wAlign = "align",
+    wNodecl = "nodecl", wPure = "pure", wSideEffect = "sideEffect", wHeader = "header",
+    wNoSideEffect = "noSideEffect", wGcSafe = "gcsafe", wNoreturn = "noreturn",
+    wNosinks = "nosinks", wLib = "lib", wDynlib = "dynlib",
+    wCompilerProc = "compilerproc", wCore = "core", wProcVar = "procvar",
+    wBase = "base", wUsed = "used", wFatal = "fatal", wError = "error", wWarning = "warning",
+    wHint = "hint",
+    wWarningAsError = "warningAsError",
+    wHintAsError = "hintAsError",
+    wLine = "line", wPush = "push",
+    wPop = "pop", wDefine = "define", wUndef = "undef", wLineDir = "lineDir",
+    wStackTrace = "stackTrace", wLineTrace = "lineTrace", wLink = "link", wCompile = "compile",
+    wLinksys = "linksys", wDeprecated = "deprecated", wVarargs = "varargs", wCallconv = "callconv",
+    wDebugger = "debugger", wNimcall = "nimcall", wStdcall = "stdcall", wCdecl = "cdecl",
+    wSafecall = "safecall", wSyscall = "syscall", wInline = "inline", wNoInline = "noinline",
+    wFastcall = "fastcall", wThiscall = "thiscall", wClosure = "closure", wNoconv = "noconv",
+    wOn = "on", wOff = "off", wChecks = "checks", wRangeChecks = "rangeChecks",
+    wBoundChecks = "boundChecks", wOverflowChecks = "overflowChecks", wNilChecks = "nilChecks",
+    wFloatChecks = "floatChecks", wNanChecks = "nanChecks", wInfChecks = "infChecks",
+    wStyleChecks = "styleChecks", wStaticBoundchecks = "staticBoundChecks",
+    wNonReloadable = "nonReloadable", wExecuteOnReload = "executeOnReload",
+
+    wAssertions = "assertions", wPatterns = "patterns", wTrMacros = "trmacros",
+    wSinkInference = "sinkInference", wWarnings = "warnings",
+    wHints = "hints", wOptimization = "optimization", wRaises = "raises",
+    wWrites = "writes", wReads = "reads", wSize = "size", wEffects = "effects", wTags = "tags",
+    wForbids = "forbids", wRequires = "requires", wEnsures = "ensures", wInvariant = "invariant",
+    wAssume = "assume", wAssert = "assert",
+    wDeadCodeElimUnused = "deadCodeElim",  # deprecated, dead code elim always happens
+    wSafecode = "safecode", wPackage = "package", wNoForward = "noforward", wReorder = "reorder",
+    wNoRewrite = "norewrite", wNoDestroy = "nodestroy", wPragma = "pragma",
+    wCompileTime = "compileTime", wNoInit = "noinit", wPassc = "passc", wPassl = "passl",
+    wLocalPassc = "localPassC", wBorrow = "borrow", wDiscardable = "discardable",
+    wFieldChecks = "fieldChecks", wSubsChar = "subschar", wAcyclic = "acyclic",
+    wShallow = "shallow", wUnroll = "unroll", wLinearScanEnd = "linearScanEnd",
+    wComputedGoto = "computedGoto", wExperimental = "experimental", wDoctype = "doctype",
+    wWrite = "write", wGensym = "gensym", wInject = "inject", wDirty = "dirty",
+    wInheritable = "inheritable", wThreadVar = "threadvar", wEmit = "emit",
+    wAsmNoStackFrame = "asmNoStackFrame", wAsmSyntax = "asmSyntax", wImplicitStatic = "implicitStatic",
+    wGlobal = "global", wCodegenDecl = "codegenDecl", wUnchecked = "unchecked",
+    wGuard = "guard", wLocks = "locks", wPartial = "partial", wExplain = "explain",
+    wLiftLocals = "liftlocals", wEnforceNoRaises = "enforceNoRaises", wSystemRaisesDefect = "systemRaisesDefect",
+    wRedefine = "redefine", wCallsite = "callsite",
+    wQuirky = "quirky",
+
+    # codegen keywords, but first the ones that are also pragmas:
+    wExtern = "extern", wGoto = "goto", wRegister = "register",
+    wUnion = "union", wPacked = "packed", wVirtual = "virtual",
+    wVolatile = "volatile", wMember = "member",
+    wByCopy = "bycopy", wByRef = "byref",
+
+    # codegen keywords but not pragmas:
+    wAuto = "auto", wBool = "bool", wCatch = "catch", wChar = "char",
+    wClass = "class", wCompl = "compl", wConstCast = "const_cast", wDefault = "default",
+    wDelete = "delete", wDouble = "double", wDynamicCast = "dynamic_cast",
+    wExplicit = "explicit", wFalse = "false", wFloat = "float",
+    wFriend = "friend", wInt = "int", wLong = "long", wMutable = "mutable",
+    wNamespace = "namespace", wNew = "new", wOperator = "operator", wPrivate = "private",
+    wProtected = "protected", wPublic = "public",
+    wReinterpretCast = "reinterpret_cast", wRestrict = "restrict", wShort = "short",
+    wSigned = "signed", wSizeof = "sizeof", wStaticCast = "static_cast", wStruct = "struct",
+    wSwitch = "switch", wThis = "this", wThrow = "throw", wTrue = "true", wTypedef = "typedef",
+    wTypeid = "typeid", wTypeof = "typeof",  wTypename = "typename",
+    wUnsigned = "unsigned", wVoid = "void", 
+
+    wAlignas = "alignas", wAlignof = "alignof", wConstexpr = "constexpr", wDecltype = "decltype",
+    wNullptr = "nullptr", wNoexcept = "noexcept",
+    wThreadLocal = "thread_local", wStaticAssert = "static_assert",
+    wChar16 = "char16_t", wChar32 = "char32_t", wWchar = "wchar_t",
+
+    wStdIn = "stdin", wStdOut = "stdout", wStdErr = "stderr",
+
+    wInOut = "inout", wOneWay = "oneway",
+    # end of codegen keywords
+
+    wBitsize = "bitsize", wImportHidden = "all",
+    wSendable = "sendable"
 
   TSpecialWords* = set[TSpecialWord]
 
@@ -95,89 +133,18 @@ const
   nimKeywordsLow* = ord(wAsm)
   nimKeywordsHigh* = ord(wYield)
 
-  ccgKeywordsLow* = ord(wAuto)
+  ccgKeywordsLow* = ord(wExtern)
   ccgKeywordsHigh* = ord(wOneWay)
 
   cppNimSharedKeywords* = {
     wAsm, wBreak, wCase, wConst, wContinue, wDo, wElse, wEnum, wExport,
     wFor, wIf, wReturn, wStatic, wTemplate, wTry, wWhile, wUsing}
+  
+  nonPragmaWordsLow* = wAuto
+  nonPragmaWordsHigh* = wOneWay
+
 
-  specialWords*: array[low(TSpecialWord)..high(TSpecialWord), string] = ["",
-
-    "addr", "and", "as", "asm",
-    "bind", "block", "break", "case", "cast",
-    "concept", "const", "continue", "converter",
-    "defer", "discard", "distinct", "div", "do",
-    "elif", "else", "end", "enum", "except", "export",
-    "finally", "for", "from", "func", "if",
-    "import", "in", "include", "interface", "is", "isnot", "iterator",
-    "let",
-    "macro", "method", "mixin", "mod", "nil", "not", "notin",
-    "object", "of", "or",
-    "out", "proc", "ptr", "raise", "ref", "return",
-    "shl", "shr", "static",
-    "template", "try", "tuple", "type", "using", "var",
-    "when", "while", "xor",
-    "yield",
-
-    ":", "::", "=", ".", "..",
-    "*", "-",
-    "magic", "thread", "final", "profiler", "memtracker", "objchecks", "intdefine", "strdefine",
-
-    "destroy",
-
-    "immediate", "constructor", "destructor", "delegator", "override",
-    "importcpp", "importobjc",
-    "importcompilerproc", "importc", "exportc", "exportnims",
-    "incompletestruct",
-    "requiresinit", "align", "nodecl", "pure", "sideeffect",
-    "header", "nosideeffect", "gcsafe", "noreturn", "merge", "lib", "dynlib",
-    "compilerproc", "core", "procvar", "base", "used",
-    "fatal", "error", "warning", "hint", "line",
-    "push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace",
-    "link", "compile", "linksys", "deprecated", "varargs",
-    "callconv", "breakpoint", "debugger", "nimcall", "stdcall",
-    "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "closure",
-    "noconv", "on", "off", "checks", "rangechecks", "boundchecks",
-    "overflowchecks", "nilchecks",
-    "floatchecks", "nanchecks", "infchecks", "movechecks",
-
-    "assertions", "patterns", "warnings", "hints",
-    "optimization", "raises", "writes", "reads", "size", "effects", "tags",
-    "deadcodeelim",  # deprecated, dead code elim always happens
-    "safecode", "package", "noforward", "reorder", "norewrite",
-    "pragma",
-    "compiletime", "noinit",
-    "passc", "passl", "borrow", "discardable", "fieldchecks",
-    "watchpoint",
-    "subschar", "acyclic", "shallow", "unroll", "linearscanend",
-    "computedgoto", "injectstmt", "experimental",
-    "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
-    "asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked",
-    "guard", "locks", "partial", "explain", "liftlocals",
-
-    "auto", "bool", "catch", "char", "class",
-    "const_cast", "default", "delete", "double",
-    "dynamic_cast", "explicit", "extern", "false",
-    "float", "friend", "goto", "int", "long", "mutable",
-    "namespace", "new", "operator",
-    "private", "protected", "public", "register", "reinterpret_cast",
-    "short", "signed", "sizeof", "static_cast", "struct", "switch",
-    "this", "throw", "true", "typedef", "typeid",
-    "typename", "union", "packed", "unsigned", "virtual", "void", "volatile",
-    "wchar_t",
-
-    "alignas", "alignof", "constexpr", "decltype", "nullptr", "noexcept",
-    "thread_local", "static_assert", "char16_t", "char32_t",
-
-    "stdin", "stdout", "stderr",
-
-    "inout", "bycopy", "byref", "oneway",
-    "bitsize",
-    ]
-
-proc findStr*(a: openArray[string], s: string): int =
-  for i in countup(low(a), high(a)):
-    if cmpIgnoreStyle(a[i], s) == 0:
-      return i
-  result = - 1
+from std/enumutils import genEnumCaseStmt
+from std/strutils import normalize
+proc findStr*[T: enum](a, b: static[T], s: string, default: T): T =
+  genEnumCaseStmt(T, s, default, ord(a), ord(b), normalize)
diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim
deleted file mode 100644
index 1ea1deb2d..000000000
--- a/compiler/writetracking.nim
+++ /dev/null
@@ -1,277 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module implements the write tracking analysis. Read my block post for
-## a basic description of the algorithm and ideas.
-## The algorithm operates in 2 phases:
-##
-##   * Collecting information about assignments (and pass-by-var calls).
-##   * Computing an aliasing relation based on the assignments. This relation
-##     is then used to compute the 'writes' and 'escapes' effects.
-
-import intsets, idents, ast, astalgo, trees, renderer, msgs, types, options,
-  lineinfos
-
-const
-  debug = false
-
-type
-  AssignToResult = enum
-    asgnNil,   # 'nil' is fine
-    asgnNew,   # 'new(result)'
-    asgnOther  # result = fooBar # not a 'new' --> 'result' might not 'new'
-  NewLocation = enum
-    newNone,
-    newLit,
-    newCall
-  RootInfo = enum
-    rootIsResultOrParam,
-    rootIsHeapAccess,
-    rootIsSym,
-    markAsWrittenTo,
-    markAsEscaping
-
-  Assignment = object # \
-    # Note that the transitive closures MUST be computed in
-    # phase 2 of the algorithm.
-    dest, src: seq[ptr TSym] # we use 'ptr' here to save RC ops and GC cycles
-    destNoTc, srcNoTc: int # length of 'dest', 'src' without the
-                           # transitive closure
-    destInfo: set[RootInfo]
-    info: TLineInfo
-
-  W = object # WriteTrackContext
-    owner: PSym
-    returnsNew: AssignToResult # assignments to 'result'
-    assignments: seq[Assignment] # list of all assignments in this proc
-
-proc allRoots(n: PNode; result: var seq[ptr TSym]; info: var set[RootInfo]) =
-  case n.kind
-  of nkSym:
-    if n.sym.kind in {skParam, skVar, skTemp, skLet, skResult, skForVar}:
-      if n.sym.kind in {skResult, skParam}: incl(info, rootIsResultOrParam)
-      result.add(cast[ptr TSym](n.sym))
-  of nkHiddenDeref, nkDerefExpr:
-    incl(info, rootIsHeapAccess)
-    allRoots(n.sons[0], result, info)
-  of nkDotExpr, nkBracketExpr, nkCheckedFieldExpr,
-      nkHiddenAddr, nkObjUpConv, nkObjDownConv:
-    allRoots(n.sons[0], result, info)
-  of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkConv,
-      nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkOfBranch,
-      nkElifBranch, nkElse, nkExceptBranch, nkFinally, nkCast:
-    allRoots(n.lastSon, result, info)
-  of nkCallKinds:
-    if getMagic(n) == mSlice:
-      allRoots(n.sons[1], result, info)
-    else:
-      # we do significantly better here by using the available escape
-      # information:
-      if n.sons[0].typ.isNil: return
-      var typ = n.sons[0].typ
-      if typ != nil:
-        typ = skipTypes(typ, abstractInst)
-        if typ.kind != tyProc: typ = nil
-        else: assert(sonsLen(typ) == sonsLen(typ.n))
-
-      for i in 1 ..< n.len:
-        let it = n.sons[i]
-        if typ != nil and i < sonsLen(typ):
-          assert(typ.n.sons[i].kind == nkSym)
-          let paramType = typ.n.sons[i]
-          if paramType.typ.isCompileTimeOnly: continue
-          if sfEscapes in paramType.sym.flags or paramType.typ.kind == tyVar:
-            allRoots(it, result, info)
-        else:
-          allRoots(it, result, info)
-  else:
-    for i in 0..<n.safeLen:
-      allRoots(n.sons[i], result, info)
-
-proc addAsgn(a: var Assignment; dest, src: PNode; destInfo: set[RootInfo]) =
-  a.dest = @[]
-  a.src = @[]
-  a.destInfo = destInfo
-  allRoots(dest, a.dest, a.destInfo)
-  if dest.kind == nkSym: incl(a.destInfo, rootIsSym)
-  if src != nil:
-    var dummy: set[RootInfo]
-    allRoots(src, a.src, dummy)
-  a.destNoTc = a.dest.len
-  a.srcNoTc = a.src.len
-  a.info = dest.info
-  #echo "ADDING ", dest.info, " ", a.destInfo
-
-proc srcHasSym(a: Assignment; x: ptr TSym): bool =
-  for i in 0 ..< a.srcNoTc:
-    if a.src[i] == x: return true
-
-proc returnsNewExpr*(n: PNode): NewLocation =
-  case n.kind
-  of nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
-      nkFloatLit..nkFloat64Lit, nkNilLit:
-    result = newLit
-  of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv,
-      nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkOfBranch,
-      nkElifBranch, nkElse, nkExceptBranch, nkFinally, nkCast:
-    result = returnsNewExpr(n.lastSon)
-  of nkCurly, nkBracket, nkPar, nkTupleConstr, nkObjConstr, nkClosure,
-      nkIfExpr, nkIfStmt, nkWhenStmt, nkCaseStmt, nkTryStmt:
-    result = newLit
-    for i in ord(n.kind == nkObjConstr) ..< n.len:
-      let x = returnsNewExpr(n.sons[i])
-      case x
-      of newNone: return newNone
-      of newLit: discard
-      of newCall: result = newCall
-  of nkCallKinds:
-    if n.sons[0].typ != nil and tfReturnsNew in n.sons[0].typ.flags:
-      result = newCall
-  else:
-    result = newNone
-
-proc deps(w: var W; dest, src: PNode; destInfo: set[RootInfo]) =
-  # let x = (localA, localB)
-  # compute 'returnsNew' property:
-  let retNew = if src.isNil: newNone else: returnsNewExpr(src)
-  if dest.kind == nkSym and dest.sym.kind == skResult:
-    if retNew != newNone:
-      if w.returnsNew != asgnOther: w.returnsNew = asgnNew
-    else:
-      w.returnsNew = asgnOther
-  # mark the dependency, but
-  # rule out obviously innocent assignments like 'somebool = true'
-  if dest.kind == nkSym and retNew == newLit: discard
-  else:
-    let L = w.assignments.len
-    w.assignments.setLen(L+1)
-    addAsgn(w.assignments[L], dest, src, destInfo)
-
-proc depsArgs(w: var W; n: PNode) =
-  if n.sons[0].typ.isNil: return
-  var typ = skipTypes(n.sons[0].typ, abstractInst)
-  if typ.kind != tyProc: return
-  # echo n.info, " ", n, " ", w.owner.name.s, " ", typeToString(typ)
-  assert(sonsLen(typ) == sonsLen(typ.n))
-  for i in 1 ..< n.len:
-    let it = n.sons[i]
-    if i < sonsLen(typ):
-      assert(typ.n.sons[i].kind == nkSym)
-      let paramType = typ.n.sons[i]
-      if paramType.typ.isCompileTimeOnly: continue
-      var destInfo: set[RootInfo] = {}
-      if sfWrittenTo in paramType.sym.flags or paramType.typ.kind == tyVar:
-        # p(f(x, y), X, g(h, z))
-        destInfo.incl markAsWrittenTo
-      if sfEscapes in paramType.sym.flags:
-        destInfo.incl markAsEscaping
-      if destInfo != {}:
-        deps(w, it, nil, destInfo)
-
-proc deps(w: var W; n: PNode) =
-  case n.kind
-  of nkLetSection, nkVarSection:
-    for child in n:
-      let last = lastSon(child)
-      if last.kind == nkEmpty: continue
-      if child.kind == nkVarTuple and last.kind in {nkPar, nkTupleConstr}:
-        if child.len-2 != last.len: return
-        for i in 0 .. child.len-3:
-          deps(w, child.sons[i], last.sons[i], {})
-      else:
-        for i in 0 .. child.len-3:
-          deps(w, child.sons[i], last, {})
-  of nkAsgn, nkFastAsgn:
-    deps(w, n.sons[0], n.sons[1], {})
-  else:
-    for i in 0 ..< n.safeLen:
-      deps(w, n.sons[i])
-    if n.kind in nkCallKinds:
-      if getMagic(n) in {mNew, mNewFinalize, mNewSeq}:
-        # may not look like an assignment, but it is:
-        deps(w, n.sons[1], newNodeIT(nkObjConstr, n.info, n.sons[1].typ), {})
-      else:
-        depsArgs(w, n)
-
-proc possibleAliases(w: var W; result: var seq[ptr TSym]) =
-  # this is an expensive fixpoint iteration. We could speed up this analysis
-  # by a smarter data-structure but we wait until profiling shows us it's
-  # expensive. Usually 'w.assignments' is small enough.
-  var alreadySeen = initIntSet()
-  template addNoDup(x) =
-    if not alreadySeen.containsOrIncl(x.id): result.add x
-  for x in result: alreadySeen.incl x.id
-
-  var todo = 0
-  while todo < result.len:
-    let x = result[todo]
-    inc todo
-    for i in 0..<len(w.assignments):
-      let a = addr(w.assignments[i])
-      #if a.srcHasSym(x):
-      #  # y = f(..., x, ...)
-      #  for i in 0 ..< a.destNoTc: addNoDup a.dest[i]
-      if a.destNoTc > 0 and a.dest[0] == x and rootIsSym in a.destInfo:
-        # x = f(..., y, ....)
-        for i in 0 ..< a.srcNoTc: addNoDup a.src[i]
-
-proc markWriteOrEscape(w: var W; conf: ConfigRef) =
-  ## Both 'writes' and 'escapes' effects ultimately only care
-  ## about *parameters*.
-  ## However, due to aliasing, even locals that might not look as parameters
-  ## have to count as parameters if they can alias a parameter:
-  ##
-  ## .. code-block:: nim
-  ##   proc modifies(n: Node) {.writes: [n].} =
-  ##     let x = n
-  ##     x.data = "abc"
-  ##
-  ## We call a symbol *parameter-like* if it is a parameter or can alias a
-  ## parameter.
-  ## Let ``p``, ``q`` be *parameter-like* and ``x``, ``y`` be general
-  ## expressions.
-  ##
-  ## A write then looks like ``p[] = x``.
-  ## An escape looks like ``p[] = q`` or more generally
-  ## like ``p[] = f(q)`` where ``f`` can forward ``q``.
-  for i in 0..<len(w.assignments):
-    let a = addr(w.assignments[i])
-    if a.destInfo != {}:
-      possibleAliases(w, a.dest)
-
-    if {rootIsHeapAccess, markAsWrittenTo} * a.destInfo != {}:
-      for p in a.dest:
-        if p.kind == skParam and p.owner == w.owner:
-          incl(p.flags, sfWrittenTo)
-          if w.owner.kind == skFunc and p.typ.kind != tyVar:
-            localError(conf, a.info, "write access to non-var parameter: " & p.name.s)
-
-    if {rootIsResultOrParam, rootIsHeapAccess, markAsEscaping}*a.destInfo != {}:
-      var destIsParam = false
-      for p in a.dest:
-        if p.kind in {skResult, skParam} and p.owner == w.owner:
-          destIsParam = true
-          break
-      if destIsParam:
-        possibleAliases(w, a.src)
-        for p in a.src:
-          if p.kind == skParam and p.owner == w.owner:
-            incl(p.flags, sfEscapes)
-
-proc trackWrites*(owner: PSym; body: PNode; conf: ConfigRef) =
-  var w: W
-  w.owner = owner
-  w.assignments = @[]
-  # Phase 1: Collect and preprocess any assignments in the proc body:
-  deps(w, body)
-  # Phase 2: Compute the 'writes' and 'escapes' effects:
-  markWriteOrEscape(w, conf)
-  if w.returnsNew != asgnOther and not isEmptyType(owner.typ.sons[0]) and
-      containsGarbageCollectedRef(owner.typ.sons[0]):
-    incl(owner.typ.flags, tfReturnsNew)
diff --git a/config/build_config.txt b/config/build_config.txt
new file mode 100644
index 000000000..66390e695
--- /dev/null
+++ b/config/build_config.txt
@@ -0,0 +1,5 @@
+nim_comment="key-value pairs for windows/posix bootstrapping build scripts"
+nim_csourcesDir=csources_v2
+nim_csourcesUrl=https://github.com/nim-lang/csources_v2.git
+nim_csourcesBranch=master
+nim_csourcesHash=86742fb02c6606ab01a532a0085784effb2e753e
diff --git a/config/config.nims b/config/config.nims
new file mode 100644
index 000000000..b8979e8e3
--- /dev/null
+++ b/config/config.nims
@@ -0,0 +1,23 @@
+# this config.nims also needs to exist to prevent future regressions, see #9990
+
+cppDefine "errno"
+cppDefine "unix"
+
+# mangle the macro names in nimbase.h
+cppDefine "NAN_INFINITY"
+cppDefine "INF"
+cppDefine "NAN"
+
+when defined(nimStrictMode):
+  # xxx add more flags here, and use `-d:nimStrictMode` in more contexts in CI.
+
+  # enable this:
+  # when defined(nimHasWarningAsError):
+  #   switch("warningAsError", "UnusedImport")
+
+  when defined(nimHasHintAsError):
+    # switch("hint", "ConvFromXtoItselfNotNeeded")
+    switch("hintAsError", "ConvFromXtoItselfNotNeeded")
+    # future work: XDeclaredButNotUsed
+
+switch("define", "nimVersion:" & NimVersion) # deadcode
diff --git a/config/nim.cfg b/config/nim.cfg
index e11826587..7c9958139 100644
--- a/config/nim.cfg
+++ b/config/nim.cfg
@@ -2,6 +2,7 @@
 # (c) 2017 Andreas Rumpf
 
 # Feel free to edit the default values as you need.
+# See https://nim-lang.org/docs/nimc.html
 
 # You may set environment variables with
 # @putenv "key" "val"
@@ -13,17 +14,32 @@ cc = gcc
 # additional options always passed to the compiler:
 --parallel_build: "0" # 0 to auto-detect number of processors
 
-hint[LineTooLong]=off
+@if not nimHasNolineTooLong:
+  hint[LineTooLong]=off
+@end
+
 #hint[XDeclaredButNotUsed]=off
 
+threads:on
+
 # Examples of how to setup a cross-compiler:
+# Nim can target architectures and OSes different than the local host
+# Syntax: <arch>.<os>.gcc.exe = "<compiler executable>"
+#         <arch>.<os>.gcc.linkerexe = "<linker executable>"
 
-# Cross-compiling for Raspberry Pi.
-# (This compiler is available in gcc-arm-linux-gnueabihf package on Ubuntu)
+# ARM e.g. Raspberry Pi 2: gcc-arm-linux-gnueabihf package on Debian/Ubuntu
 arm.linux.gcc.exe = "arm-linux-gnueabihf-gcc"
 arm.linux.gcc.linkerexe = "arm-linux-gnueabihf-gcc"
+# ARM64/aarch64 e.g. Raspberry Pi 3: gcc-aarch64-linux-gnu package on Debian/Ubuntu
+arm64.linux.gcc.exe = "aarch64-linux-gnu-gcc"
+arm64.linux.gcc.linkerexe = "aarch64-linux-gnu-gcc"
+# RISC-V: gcc-riscv64-linux-gnu package on Debian/Ubuntu
+riscv32.linux.gcc.exe = "riscv64-linux-gnu-gcc"
+riscv32.linux.gcc.linkerexe = "riscv64-linux-gnu-gcc"
+riscv64.linux.gcc.exe = "riscv64-linux-gnu-gcc"
+riscv64.linux.gcc.linkerexe = "riscv64-linux-gnu-gcc"
 
-# For OpenWRT, you will also need to adjust PATH to point to your toolchain. 
+# For OpenWRT, you will also need to adjust PATH to point to your toolchain.
 mips.linux.gcc.exe = "mips-openwrt-linux-gcc"
 mips.linux.gcc.linkerexe = "mips-openwrt-linux-gcc"
 
@@ -43,16 +59,17 @@ path="$lib/arch"
 path="$lib/core"
 path="$lib/pure"
 
-@if nimbabel:
-  nimblepath="$home/.nimble/pkgs/"
-  @if not windows:
-    nimblepath="/opt/nimble/pkgs/"
-  @else:
-    # TODO:
-  @end
+@if not windows:
+  nimblepath="/opt/nimble/pkgs2/"
+  nimblepath="/opt/nimble/pkgs/"
+@else:
+  # TODO:
 @end
+nimblepath="$home/.nimble/pkgs2/"
+nimblepath="$home/.nimble/pkgs/"
 
-@if release or quick:
+# Syncronize with compiler/commands.specialDefine
+@if danger or quick:
   obj_checks:off
   field_checks:off
   range_checks:off
@@ -63,18 +80,66 @@ path="$lib/pure"
   linetrace:off
   debugger:off
   line_dir:off
-  dead_code_elim:on
-  @if nimHasNilChecks:
-    nilchecks:off
-  @end
 @end
 
-@if release:
+# Syncronize with compiler/commands.specialDefine
+@if release or danger:
+  stacktrace:off
+  excessiveStackTrace:off
+  linetrace:off
+  debugger:off
+  line_dir:off
   opt:speed
+  define:release
+@end
+
+@if false: # not danger: # this does not work yet.
+  clang.options.always %= "${clang.options.always} -fsanitize=null -fsanitize-undefined-trap-on-error"
+  gcc.options.always %= "${gcc.options.always} -fsanitize=null -fsanitize-undefined-trap-on-error"
+@end
+
+# Turn off threads support when compiling for bare-metal targets (--os:any)
+@if any:
+  threads:off
+@end
+
+@if unix and mingw:
+  # Cross compile for Windows from Linux/OSX using MinGW
+  i386.windows.gcc.exe = "i686-w64-mingw32-gcc"
+  i386.windows.gcc.linkerexe = "i686-w64-mingw32-gcc"
+  i386.windows.gcc.cpp.exe = "i686-w64-mingw32-g++"
+  i386.windows.gcc.cpp.linkerexe = "i686-w64-mingw32-g++"
+
+  amd64.windows.gcc.exe = "x86_64-w64-mingw32-gcc"
+  amd64.windows.gcc.linkerexe = "x86_64-w64-mingw32-gcc"
+  amd64.windows.gcc.cpp.exe = "x86_64-w64-mingw32-g++"
+  amd64.windows.gcc.cpp.linkerexe = "x86_64-w64-mingw32-g++"
+
+  @if macosx:
+    i386.windows.gcc.path = "/usr/local/bin"
+    amd64.windows.gcc.path = "/usr/local/bin"
+  @else:
+    i386.windows.gcc.path = "/usr/bin"
+    amd64.windows.gcc.path = "/usr/bin"
+  @end
+
+  os = windows
+
+  gcc.options.linker = ""
+  gcc.cpp.options.linker = ""
 @end
 
 @if unix:
-  @if not bsd or haiku:
+  @if bsd:
+    # BSD got posix_spawn only recently, so we deactivate it for osproc:
+    define:useFork
+  @elif haiku:
+    gcc.options.linker = "-Wl,--as-needed -lnetwork"
+    gcc.cpp.options.linker = "-Wl,--as-needed -lnetwork"
+    clang.options.linker = "-Wl,--as-needed -lnetwork"
+    clang.cpp.options.linker = "-Wl,--as-needed -lnetwork"
+    tcc.options.linker = "-Wl,--as-needed -lnetwork"
+  @elif not genode:
     # -fopenmp
     gcc.options.linker = "-ldl"
     gcc.cpp.options.linker = "-ldl"
@@ -82,20 +147,6 @@ path="$lib/pure"
     clang.cpp.options.linker = "-ldl"
     tcc.options.linker = "-ldl"
   @end
-  @if bsd or haiku:
-    # BSD got posix_spawn only recently, so we deactivate it for osproc:
-    define:useFork
-    # at least NetBSD has problems with thread local storage:
-    tlsEmulation:on
-  @end
-  @if haiku:
-    # -fopenmp
-    gcc.options.linker = "-lroot -lnetwork"
-    gcc.cpp.options.linker = "-lroot -lnetwork"
-    clang.options.linker = "-lroot -lnetwork"
-    clang.cpp.options.linker = "-lroot -lnetwork"
-    tcc.options.linker = "-lroot -lnetwork"
-  @end
 @end
 
 @if android:
@@ -109,6 +160,14 @@ path="$lib/pure"
   @end
 @end
 
+@if nintendoswitch:
+  cc = "switch_gcc"
+  switch_gcc.options.linker = "-g -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE"
+  switch_gcc.cpp.options.linker = "-g -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE"
+  switch_gcc.options.always = "-g -Wall -O2 -ffunction-sections -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE -D__SWITCH__"
+  switch_gcc.cpp.options.always = "-g -Wall -O2 -ffunction-sections -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE -D__SWITCH__ -fno-rtti -fno-exceptions -std=gnu++11"
+@end
+
 # Configuration for the Intel C/C++ compiler:
 @if windows:
   icl.options.speed = "/Ox /arch:SSE2"
@@ -118,22 +177,32 @@ path="$lib/pure"
 # Configuration for the GNU C/C++ compiler:
 @if windows:
   #gcc.path = r"$nim\dist\mingw\bin"
-  @if gcc or tcc:
+  @if gcc:
+    gcc.options.linker %= "-Wl,-Bstatic -lpthread"
+  @end
+  @if tcc:
     tlsEmulation:on
   @end
 @end
 
-@if macosx or freebsd:
-  cc = clang
+gcc.maxerrorsimpl = "-fmax-errors=3"
+
+@if freebsd or netbsd:
+  tlsEmulation:off
+@elif bsd:
   tlsEmulation:on
-  gcc.options.always = "-w"
-  gcc.cpp.options.always = "-w -fpermissive"
+@end
+
+@if macosx or freebsd or openbsd:
+  cc = clang
+  gcc.options.always %= "-w ${gcc.maxerrorsimpl}"
+  gcc.cpp.options.always %= "-w ${gcc.maxerrorsimpl} -fpermissive"
 @elif windows:
-  gcc.options.always = "-w -mno-ms-bitfields"
-  gcc.cpp.options.always = "-w -fpermissive -mno-ms-bitfields"
+  gcc.options.always %= "-w ${gcc.maxerrorsimpl} -mno-ms-bitfields"
+  gcc.cpp.options.always %= "-w ${gcc.maxerrorsimpl} -fpermissive -mno-ms-bitfields"
 @else:
-  gcc.options.always = "-w"
-  gcc.cpp.options.always = "-w -fpermissive"
+  gcc.options.always %= "-w ${gcc.maxerrorsimpl}"
+  gcc.cpp.options.always %= "-w ${gcc.maxerrorsimpl} -fpermissive"
 @end
 
 # Configuration for Objective-C compiler:
@@ -161,6 +230,17 @@ clang.objc.options.linker = "-lobjc -lgnustep-base"
   llvm_gcc.cpp.options.linker = "-Wl,-rpath=.:/usr/local/lib:/usr/pkg/lib:/usr/X11R6/lib"
   clang.options.linker = "-Wl,-rpath=.:/usr/local/lib:/usr/pkg/lib:/usr/X11R6/lib"
   clang.cpp.options.linker = "-Wl,-rpath=.:/usr/local/lib:/usr/pkg/lib:/usr/X11R6/lib"
+
+  cincludes: "/usr/local/include"
+  clibdir: "/usr/local/lib"
+@end
+
+@if freebsd or openbsd:
+  cincludes: "/usr/local/include"
+  clibdir: "/usr/local/lib"
+@elif netbsd:
+  cincludes: "/usr/pkg/include"
+  clibdir: "/usr/pkg/lib"
 @end
 
 # Configuration for the VxWorks
@@ -177,16 +257,19 @@ clang.objc.options.linker = "-lobjc -lgnustep-base"
   gcc.options.linker %= "-L $WIND_BASE/target/lib/usr/lib/ppc/PPC32/common -mrtp -fno-strict-aliasing -D_C99 -D_HAS_C9X -std=c99 -fasm -Wall -Wno-write-strings"
 @end
 
-gcc.options.speed = "-O3 -fno-strict-aliasing"
-gcc.options.size = "-Os"
+# -fno-math-errno is default in OSX, iOS, BSD, Musl, Libm, LLVM, Clang, ICC.
+# See https://itnext.io/why-standard-c-math-functions-are-slow-d10d02554e33
+# and https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Optimize-Options.html#Optimize-Options
+gcc.options.speed = "-O3 -fno-strict-aliasing -fno-ident -fno-math-errno"
+gcc.options.size = "-Os -fno-ident"
 @if windows:
-  gcc.options.debug = "-g3 -O0 -gdwarf-3"
+  gcc.options.debug = "-g3 -Og -gdwarf-3"
 @else:
-  gcc.options.debug = "-g3 -O0"
+  gcc.options.debug = "-g3 -Og"
 @end
-gcc.cpp.options.speed = "-O3 -fno-strict-aliasing"
-gcc.cpp.options.size = "-Os"
-gcc.cpp.options.debug = "-g3 -O0"
+gcc.cpp.options.speed = "-O3 -fno-strict-aliasing -fno-ident -fno-math-errno"
+gcc.cpp.options.size = "-Os -fno-ident"
+gcc.cpp.options.debug = "-g3 -Og"
 #passl = "-pg"
 
 # Configuration for the LLVM GCC compiler:
@@ -197,57 +280,85 @@ llvm_gcc.options.size = "-Os"
 
 # Configuration for the LLVM CLang compiler:
 clang.options.debug = "-g"
-clang.options.always = "-w"
+clang.cpp.options.debug = "-g"
+clang.options.always = "-w -ferror-limit=3"
 clang.options.speed = "-O3"
 clang.options.size = "-Os"
 
+@if windows:
+  clang_cl.cpp.options.always %= "${clang_cl.options.always} /EHsc"
+  @if not release and not safety and not danger:
+    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 and not safety and not danger:
+    clang.options.linker = "-g"
+    clang.cpp.options.linker = "-g"
+  @end
+@end
+
 # Configuration for the Visual C/C++ compiler:
-vcc.exe =     "vccexe.exe"
+# VCCEXE is a tool that invokes the Visual Studio Developer Command Prompt
+# before calling the compiler.
+# Please make sure either Visual Studio or C++ Build SKU is installed when using the vcc compiler backend.
+
+vcc.exe = "vccexe.exe"
 vcc.cpp.exe = "vccexe.exe"
-vcc.linkerexe =     "vccexe.exe"
+vcc.linkerexe = "vccexe.exe"
 vcc.cpp.linkerexe = "vccexe.exe"
 
-# set the options for specific platforms:
-vcc.options.always =      "/nologo"
-@if release:
-  # no debug symbols in release builds
-@else:
-  vcc.options.always %= "${vcc.options.always} /Z7" # Get VCC to output full debug symbols in the obj file
-@end
-vcc.cpp.options.always %=  "${vcc.options.always} /EHsc"
-vcc.options.linker =      "/nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB
-vcc.cpp.options.linker %=  "${vcc.options.linker}"
-@if i386:
-vcc.options.always %=      "--platform:x86 ${vcc.options.always}"
-vcc.cpp.options.always %=  "--platform:x86 ${vcc.cpp.options.always}"
-vcc.options.linker %=      "--platform:x86 ${vcc.options.linker}"
-vcc.cpp.options.linker %=  "--platform:x86 ${vcc.cpp.options.linker}"
-@elif amd64:
-vcc.options.always %=      "--platform:amd64 ${vcc.options.always}"
-vcc.cpp.options.always %=  "--platform:amd64 ${vcc.cpp.options.always}"
-vcc.options.linker %=      "--platform:amd64 ${vcc.options.linker}"
-vcc.cpp.options.linker %=  "--platform:amd64 ${vcc.cpp.options.linker}"
-@elif arm:
-vcc.options.always %=      "--platform:arm ${vcc.options.always}"
-vcc.cpp.options.always %=  "--platform:arm ${vcc.cpp.options.always}"
-vcc.options.linker %=      "--platform:arm ${vcc.options.linker}"
-vcc.cpp.options.linker %=  "--platform:arm ${vcc.cpp.options.linker}"
-@end
-
-vcc.options.debug =     "/Od"
-vcc.cpp.options.debug = "/Od"
-vcc.options.speed =     "/O2"
+vcc.options.always =  "/nologo"
+vcc.cpp.options.always = "/nologo /EHsc"
+vcc.options.debug = "/Zi /FS /Od"
+vcc.cpp.options.debug = "/Zi /FS /Od"
+vcc.options.speed = "/O2"
 vcc.cpp.options.speed = "/O2"
-vcc.options.size =     "/O1"
+vcc.options.size = "/O1"
 vcc.cpp.options.size = "/O1"
 
 # Configuration for the Tiny C Compiler:
 tcc.options.always = "-w"
 
-# Configuration for the Genode toolchain
-amd64.genode.gcc.cpp.exe = "genode-x86-g++"
-amd64.genode.gcc.exe = "genode-x86-gcc"
-amd64.genode.gcc.path = "/usr/local/genode-gcc/bin"
-arm.genode.gcc.cpp.exe = "genode-arm-g++"
-arm.genode.gcc.exe = "genode-arm-gcc"
-arm.genode.gcc.path = "/usr/local/genode-gcc/bin"
+@if arm or arm64:
+  --define:nimEmulateOverflowChecks
+@end
+
+@if lto or lto_incremental:
+  @if lto_incremental:
+   vcc.options.always%= "${vcc.options.always} /GL /Gw /Gy"
+   vcc.cpp.options.always%= "${vcc.cpp.options.always} /GL /Gw /Gy"
+   vcc.options.linker %= "${vcc.options.linker} /link /LTCG:incremental"
+   vcc.cpp.options.linker %= "${vcc.cpp.options.linker} /link /LTCG:incremental"
+   clang_cl.options.always%= "${clang_cl.options.always} -flto=thin"
+   clang_cl.cpp.options.always%= "${clang.cpp.options.always} -flto=thin"
+   clang.options.always%= "${clang.options.always} -flto=thin"
+   clang.cpp.options.always%= "${clang.cpp.options.always} -flto=thin"
+   clang.options.linker %= "${clang.options.linker} -flto=thin"
+   clang.cpp.options.linker %= "${clang.cpp.options.linker} -flto=thin"
+  @else:
+   vcc.options.always%= "${vcc.options.always} /GL"
+   vcc.cpp.options.always%= "${vcc.cpp.options.always} /GL"
+   vcc.options.linker %= "${vcc.options.linker} /link /LTCG"
+   vcc.cpp.options.linker %= "${vcc.cpp.options.linker} /link /LTCG"
+   clang_cl.options.always%= "${clang_cl.options.always} -flto"
+   clang_cl.cpp.options.always%= "${clang.cpp.options.always} -flto"
+   clang.options.always%= "${clang.options.always} -flto"
+   clang.cpp.options.always%= "${clang.cpp.options.always} -flto"
+   clang.options.linker %= "${clang.options.linker} -flto"
+   clang.cpp.options.linker %= "${clang.cpp.options.linker} -flto"
+  @end
+  icl.options.always %= "${icl.options.always} /Qipo"
+  icl.cpp.options.always %= "${icl.cpp.options.always} /Qipo"
+  gcc.options.always %= "${gcc.options.always} -flto=auto"
+  gcc.cpp.options.always %= "${gcc.cpp.options.always} -flto=auto"
+  gcc.options.linker %= "${gcc.options.linker} -flto=auto -Wno-stringop-overflow"  # https://github.com/nim-lang/Nim/issues/21595
+  gcc.cpp.options.linker %= "${gcc.cpp.options.linker} -flto=auto"
+@end
+@if strip:
+  gcc.options.linker %= "${gcc.options.linker} -s"
+  gcc.cpp.options.linker %= "${gcc.cpp.options.linker} -s"
+  clang.options.linker %= "${clang.options.linker} -s"
+  clang.cpp.options.linker %= "${clang.cpp.options.linker} -s"
+@end
diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg
index 11d9987d8..99751f79d 100644
--- a/config/nimdoc.cfg
+++ b/config/nimdoc.cfg
@@ -9,82 +9,121 @@ split.item.toc = "20"
 
 doc.section = """
 <div class="section" id="$sectionID">
-<h1><a class="toc-backref" href="#$sectionID">$sectionTitle</a></h1>
-<dl class="item">
-$content
-</dl></div>
+  <h1><a class="toc-backref" href="#$sectionID">$sectionTitle</a></h1>
+  <dl class="item">
+    $content
+  </dl>
+</div>
 """
 
-doc.section.toc = """
+# Just a single item in the TOC (e.g. imports, exports)
+doc.section.toc_item = """
 <li>
   <a class="reference reference-toplevel" href="#$sectionID" id="$sectionTitleID">$sectionTitle</a>
-  <ul class="simple simple-toc-section">
-    $content
-  </ul>
 </li>
 """
 
+# This is a section (e.g. procs, types) in the TOC which gets turned into a drop down
+doc.section.toc = """
+<li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#$sectionID" id="$sectionTitleID">$sectionTitle</a></summary>
+    <ul class="simple simple-toc-section">
+      $content
+    </ul>
+  </details>
+</li>
+"""
+
+doc.section.toc2 = """
+<ul class="simple nested-toc-section">$plainName
+  $content
+</ul>
+"""
+
 # Chunk of HTML emitted for each entry in the HTML table of contents.
 # Available variables are:
 # * $desc: the actual docstring of the item.
 # * $header: the full version of name, including types, pragmas, tags, etc.
-# * $header_plain: like header but without HTML, for attribute embedding.
+# * $header_plain: like header but without HTML (and without pragmas, tags, etc.),
+#   for attribute embedding.
 # * $itemID: numerical unique entry of the item in the HTML.
 # * $itemSym: short symbolic name of the item for easier hyperlinking.
 # * $itemSymEnc: quoted version for URLs or attributes.
 # * $itemSymOrID: the symbolic name or the ID if that is not unique.
 # * $itemSymOrIDEnc: quoted version for URLs or attributes.
 # * $name: reduced name of the item.
+# * $uniqueName: name with parameters for routine types or $name for others.
 # * $seeSrc: generated HTML from doc.item.seesrc (if some switches are used).
 
 doc.item = """
-<dt id="$itemSym"><a name="$itemSymOrID"></a><pre>$header</pre></dt>
-<dd>
-$desc
-$seeSrc
-</dd>
+<div id="$itemSymOrID">
+  <dt><pre>$header</pre></dt>
+  <dd>
+    $deprecationMsg
+    $desc
+    $seeSrc
+  </dd>
+</div>
+"""
+
+# A wrapper of a few overloaded `doc.item`s with the same basic name
+# * $header_plain - see above
+# * $overloadGroupName - the anchor for this whole group
+# * $content - string containing `doc.item`s themselves
+doc.item2 = """
+<div id="$overloadGroupName">
+  $content
+</div>
 """
 
 # Chunk of HTML emitted for each entry in the HTML table of contents.
 # See doc.item for available substitution variables.
+
+# This is used for TOC items which are not overloadable (e.g. types).
+# `$header_plain` would be too verbose here, so we use $name.
 doc.item.toc = """
-  <li><a class="reference" href="#$itemSymOrID"
-    title="$header_plain">$name<span class="attachedType" style="visibility:hidden">$attype</span></a></li>
+<li><a class="reference" href="#$itemSymOrIDEnc" title="$header_plain">$name</a></li>
+"""
+
+# This is used for TOC items which are grouped by the same name (e.g. procs).
+doc.item.tocTable = """
+<li><a class="reference" href="#$itemSymOrIDEnc" title="$header_plain">$header_plain</a></li>
 """
 
 # HTML rendered for doc.item's seeSrc variable. Note that this will render to
-# the empty string if you don't pass anything through --docSeeSrcURL. Available
+# the empty string if you don't pass anything through --git.url. Available
 # substitutaion variables here are:
+# * $commit: branch/commit to use in source link.
+# * $devel: branch to use in edit link.
 # * $path: relative path to the file being processed.
 # * $line: line of the item in the original source file.
-# * $url: whatever you did pass through the --docSeeSrcUrl switch (which also
+# * $url: whatever you did pass through the --git.url switch (which also
 #   gets variables path/line replaced!)
-doc.item.seesrc = """&nbsp;&nbsp;<a
-href="${url}/tree/${commit}/${path}#L${line}"
-class="link-seesrc" target="_blank">Source</a>
-<a href="${url}/edit/devel/${path}#L${line}" class="link-seesrc" target="_blank" >Edit</a>
+doc.item.seesrc = """
+<a href="${url}/tree/${commit}/${path}#L${line}" class="link-seesrc" target="_blank">Source</a>&nbsp;&nbsp;
+<a href="${url}/edit/${devel}/${path}#L${line}" class="link-seesrc" target="_blank" >Edit</a>&nbsp;&nbsp;
+"""
+
+doc.deprecationmsg = """
+<div class="deprecation-message">
+  <b>$label</b> $message
+</div>
 """
 
 doc.toc = """
 <ul class="simple simple-toc" id="toc-list">
-$content
+  $content
 </ul>
 """
 
-doc.body_toc = """
-<div class="row">
-  <div class="three columns">
-  <div>
-    Search: <input type="text" id="searchInput"
-      onkeyup="search()" />
-  </div>
-  $tableofcontents
-  </div>
-  <div class="nine columns" id="content">
-  <div id="tocRoot"></div>
-  <p class="module-desc">$moduledesc</p>
-  $content
-  </div>
+doc.body_toc_groupsection = """
+<div class="search-groupby">
+  Group by:
+  <select onchange="groupBy(this.value)">
+    <option value="section">Section</option>
+    <option value="type">Type</option>
+  </select>
 </div>
 """
 
@@ -96,1307 +135,130 @@ doc.body_toc = """
 doc.body_toc_group = """
 <div class="row">
   <div class="three columns">
-  <div id="global-links">
-    <ul class="simple">
-      <li>
-        <a href="manual.html">Manual</a>
-      </li>
-      <li>
-        <a href="lib.html">Standard library</a>
-      </li>
-      <li>
-        <a href="theindex.html">Index</a>
-      </li>
-    </ul>
-  </div>
-  <div id="searchInputDiv">
-    Search: <input type="text" id="searchInput"
-      onkeyup="search()" />
-  </div>
-  <div class="search-groupby">
-    Group by:
-    <select onchange="groupBy(this.value)">
-      <option value="section">Section</option>
-      <option value="type">Type</option>
-    </select>
-  </div>
-  $tableofcontents
+    <div class="theme-select-wrapper">
+      <label for="theme-select">Theme:&nbsp;</label>
+      <select id="theme-select" onchange="setTheme(this.value)">
+        <option value="auto">🌗 Match OS</option>
+        <option value="dark">🌑 Dark</option>
+        <option value="light">🌕 Light</option>
+      </select>
+    </div>
+    <div id="global-links">
+      <ul class="simple-boot">
+        <li><a href="manual.html">Manual</a></li>
+        <li><a href="lib.html">Standard library</a></li>
+        <li> <a id="indexLink" href="$theindexhref">Index</a></li>
+        <li><a href="compiler/$theindexhref">Compiler docs</a></li>
+        <li><a href="https://nim-lang.github.io/fusion/theindex.html">Fusion docs</a></li>
+        <li><a href="https://nim-lang.github.io/Nim/">devel</a>, <a href="https://nim-lang.org/documentation.html">stable</a></li>
+      </ul>
+    </div>
+    <div id="searchInputDiv">
+      Search: <input type="search" id="searchInput"
+        oninput="search()" />
+    </div>
+    $body_toc_groupsection
+    $tableofcontents
   </div>
   <div class="nine columns" id="content">
-  <div id="tocRoot"></div>
-  <p class="module-desc">$moduledesc</p>
-  $content
+    $seeSrc
+    <div id="tocRoot"></div>
+    $deprecationMsg
+    <p class="module-desc">$moduledesc</p>
+    $content
   </div>
 </div>
 """
 
 @else
-
+# keep in sink with other `doc.body_toc_group` or better, refactor
 doc.body_toc_group = """
 <div class="row">
   <div class="three columns">
-  <div id="global-links">
-    <ul class="simple">
-    </ul>
-  </div>
-  <div id="searchInputDiv">
-    Search: <input type="text" id="searchInput"
-      onkeyup="search()" />
-  </div>
-  <div>
-    Group by:
-    <select onchange="groupBy(this.value)">
-      <option value="section">Section</option>
-      <option value="type">Type</option>
-    </select>
-  </div>
-  $tableofcontents
+    <div class="theme-select-wrapper">
+      <label for="theme-select">Theme:&nbsp;</label>
+      <select id="theme-select" onchange="setTheme(this.value)">
+        <option value="auto">🌗 Match OS</option>
+        <option value="dark">🌑 Dark</option>
+        <option value="light">🌕 Light</option>
+      </select>
+    </div>
+    <div id="global-links">
+      <ul class="simple">
+        <li><a id="indexLink" href="$theindexhref">Index</a></li>
+      </ul>
+    </div>
+    <div id="searchInputDiv">
+      Search: <input type="search" id="searchInput" oninput="search()"/>
+    </div>
+    <div>
+      Group by:
+      <select onchange="groupBy(this.value)">
+        <option value="section">Section</option>
+        <option value="type">Type</option>
+      </select>
+    </div>
+    $tableofcontents
   </div>
   <div class="nine columns" id="content">
-  <div id="tocRoot"></div>
-  <p class="module-desc">$moduledesc</p>
-  $content
+    $seeSrc
+    <div id="tocRoot"></div>
+    $deprecationMsg
+    <p class="module-desc">$moduledesc</p>
+    $content
   </div>
 </div>
 """
 @end
 
+doc.body_toc %= "${doc.body_toc_group}" # should only be used for boot
+
 doc.body_no_toc = """
 $moduledesc
 $content
 """
 
-doc.listing_start = "<pre class=\"listing\">"
+# $1 - number of listing in document, $2 - language (e.g. langNim), $3 - anchor
+doc.listing_start = "<pre$3 class=\"listing\">"
 doc.listing_end = "</pre>"
 
 # * $analytics: Google analytics location, includes <script> tags
 doc.file = """<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!--  This file is generated by Nim. -->
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
-
-<!-- Favicon -->
-<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<title>$title</title>
 
 <!-- Google fonts -->
 <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
 <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
 
-<!-- CSS -->
-<title>$title</title>
-<style type="text/css" >
-/*
-Stylesheet for use with Docutils/rst2html.
-
-See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
-customize this style sheet.
-
-Modified from Chad Skeeters' rst2html-style
-https://bitbucket.org/cskeeters/rst2html-style/
-
-Modified by Boyd Greenfield
-*/
-/* SCSS variables */
-/* Text weights */
-/* Body colors */
-/* Text colors */
-/* Link colors */
-/* Syntax highlighting colors */
-/* Pct changes */
-/* Mixins */
-/* Body/layout */
-html {
-  font-size: 100%;
-  -webkit-text-size-adjust: 100%;
-  -ms-text-size-adjust: 100%; }
-
-/* Where we want fancier font if available */
-h1, h2, h3, h4, h5, h6, p.module-desc, table.docinfo + blockquote p, table.docinfo blockquote p, h1 + blockquote p {
-  font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif !important; }
-
-h1.title {
-  font-weight: 900; }
-
-body {
-  font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
-  font-weight: 400;
-  font-size: 16px;
-  line-height: 20px;
-  color: #444;
-  letter-spacing: 0.15px;
-  background-color: #FDFBFA; }
-
-/* Skeleton grid */
-.container {
-  position: relative;
-  width: 100%;
-  max-width: 960px;
-  margin: 0 auto;
-  padding: 0 20px;
-  box-sizing: border-box; }
-
-.column,
-.columns {
-  width: 100%;
-  float: left;
-  box-sizing: border-box; }
-
-/* For devices larger than 400px */
-@media (min-width: 400px) {
-  .container {
-    width: 100%;
-    padding: 0; } }
-/* For devices larger than 650px */
-@media (min-width: 650px) {
-  .container {
-    width: 100%; }
-
-  .column,
-  .columns {
-    margin-left: 4%; }
-
-  .column:first-child,
-  .columns:first-child {
-    margin-left: 0; }
-
-  .one.column,
-  .one.columns {
-    width: 4.66666666667%; }
-
-  .two.columns {
-    width: 13.3333333333%; }
-
-  .three.columns {
-    width: 22%; }
-
-  .four.columns {
-    width: 30.6666666667%; }
-
-  .five.columns {
-    width: 39.3333333333%; }
-
-  .six.columns {
-    width: 48%; }
-
-  .seven.columns {
-    width: 56.6666666667%; }
-
-  .eight.columns {
-    width: 65.3333333333%; }
-
-  .nine.columns {
-    width: 74.0%; }
-
-  .ten.columns {
-    width: 82.6666666667%; }
-
-  .eleven.columns {
-    width: 91.3333333333%; }
-
-  .twelve.columns {
-    width: 100%;
-    margin-left: 0; }
-
-  .one-third.column {
-    width: 30.6666666667%; }
-
-  .two-thirds.column {
-    width: 65.3333333333%; } }
-/* Customer Overrides */
-.footer {
-  text-align: center;
-  color: #969696;
-  padding-top: 10%; }
-
-p.module-desc {
-  font-size: 1.1em;
-  color: #666666; }
-
-a.link-seesrc {
-  color: #aec7d2;
-  font-style: italic; }
-
-a.link-seesrc:hover {
-  color: #6c9aae; }
-
-#toc-list {
-  word-wrap: break-word; }
-
-ul.simple-toc {
-  list-style: none; }
-
-ul.simple-toc a.reference-toplevel {
-  font-weight: bold;
-  color: #0077b3; }
-
-ul.simple-toc-section {
-  list-style-type: circle;
-  color: #6c9aae; }
-
-ul.simple-toc-section a.reference {
-  color: #0077b3; }
-
-cite {
-  font-style: italic !important; }
-
-dt > pre {
-  border-color: rgba(0, 0, 0, 0.1);
-  background-color: rgba(255, 255, 255, 0.3);
-  margin: 15px 0px 5px; }
-
-dd > pre {
-  border-color: rgba(0, 0, 0, 0.1);
-  background-color: whitesmoke;
-  margin-top: 8px; }
-
-.item > dd {
-  margin-left: 10px;
-  margin-bottom: 30px; }
-
-/* Nim line-numbered tables */
-.line-nums-table {
-  width: 100%;
-  table-layout: fixed; }
-
-/* Nim search input */
-div#searchInputDiv {
-  margin-bottom: 8px;
-}
-div#searchInputDiv input#searchInput {
-  width: 10em;
-}
-div.search-groupby {
-  margin-bottom: 8px;
-}
-
-table.line-nums-table {
-  border-radius: 4px;
-  border: 1px solid #cccccc;
-  background-color: whitesmoke;
-  border-collapse: separate;
-  margin-top: 15px;
-  margin-bottom: 25px; }
-
-.line-nums-table tbody {
-  border: none; }
-
-.line-nums-table td pre {
-  border: none;
-  background-color: transparent; }
-
-.line-nums-table td.blob-line-nums {
-  width: 28px; }
-
-.line-nums-table td.blob-line-nums pre {
-  color: #b0b0b0;
-  -webkit-filter: opacity(75%);
-  text-align: right;
-  border-color: transparent;
-  background-color: transparent;
-  padding-left: 0px;
-  margin-left: 0px;
-  padding-right: 0px;
-  margin-right: 0px; }
-
-/* Docgen styles */
-/* Links */
-a {
-  color: #0077b3;
-  text-decoration: none; }
-
-a:hover,
-a:focus {
-  color: #00334d;
-  text-decoration: underline; }
-
-a:visited {
-  color: #00334d; }
-
-a:focus {
-  outline: thin dotted #2d2d2d;
-  outline: 5px auto -webkit-focus-ring-color;
-  outline-offset: -2px; }
-
-a:hover,
-a:active {
-  outline: 0; }
-
-sub,
-sup {
-  position: relative;
-  font-size: 75%;
-  line-height: 0;
-  vertical-align: baseline; }
-
-sup {
-  top: -0.5em; }
-
-sub {
-  bottom: -0.25em; }
-
-img {
-  width: auto;
-  height: auto;
-  max-width: 100%;
-  vertical-align: middle;
-  border: 0;
-  -ms-interpolation-mode: bicubic; }
-
-@media print {
-  * {
-    color: black !important;
-    text-shadow: none !important;
-    background: transparent !important;
-    box-shadow: none !important; }
-
-  a,
-  a:visited {
-    text-decoration: underline; }
-
-  a[href]:after {
-    content: " (" attr(href) ")"; }
-
-  abbr[title]:after {
-    content: " (" attr(title) ")"; }
-
-  .ir a:after,
-  a[href^="javascript:"]:after,
-  a[href^="#"]:after {
-    content: ""; }
-
-  pre,
-  blockquote {
-    border: 1px solid #999;
-    page-break-inside: avoid; }
-
-  thead {
-    display: table-header-group; }
-
-  tr,
-  img {
-    page-break-inside: avoid; }
-
-  img {
-    max-width: 100% !important; }
-
-  @page {
-    margin: 0.5cm; }
-
-  h1 {
-    page-break-before: always; }
-
-  h1.title {
-    page-break-before: avoid; }
-
-  p,
-  h2,
-  h3 {
-    orphans: 3;
-    widows: 3; }
-
-  h2,
-  h3 {
-    page-break-after: avoid; } }
-.img-rounded {
-  -webkit-border-radius: 6px;
-  -moz-border-radius: 6px;
-  border-radius: 6px; }
-
-.img-polaroid {
-  padding: 4px;
-  background-color: rgba(252, 248, 244, 0.75);
-  border: 1px solid #ccc;
-  border: 1px solid rgba(0, 0, 0, 0.2);
-  -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-  -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); }
-
-p {
-  margin: 0 0 8px; }
-
-small {
-  font-size: 85%; }
-
-strong {
-  font-weight: 600; }
-
-em {
-  font-style: italic; }
-
-cite {
-  font-style: normal; }
-
-h1,
-h2,
-h3,
-h4,
-h5,
-h6 {
-  font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
-  font-weight: 600;
-  line-height: 20px;
-  color: inherit;
-  text-rendering: optimizelegibility; }
-
-h1 {
-  font-size: 2em;
-  font-weight: 400;
-  padding-bottom: .15em;
-  border-bottom: 1px solid #aaaaaa;
-  margin-top: 1.0em;
-  line-height: 1.2em; }
-
-h1.title {
-  padding-bottom: 1em;
-  border-bottom: 0px;
-  font-size: 2.75em; }
-
-h2 {
-  font-size: 1.5em;
-  margin-top: 1.5em; }
-
-h3 {
-  font-size: 1.3em;
-  font-style: italic;
-  margin-top: 0.75em; }
-
-h4 {
-  font-size: 1.3em;
-  margin-top: 0.5em; }
-
-h5 {
-  font-size: 1.2em;
-  margin-top: 0.25em; }
-
-h6 {
-  font-size: 1.1em; }
-
-ul,
-ol {
-  padding: 0;
-  margin: 0 0 0px 15px; }
-
-ul ul,
-ul ol,
-ol ol,
-ol ul {
-  margin-bottom: 0; }
-
-li {
-  line-height: 20px; }
-
-dl {
-  margin-bottom: 20px; }
-
-dt,
-dd {
-  line-height: 20px; }
-
-dt {
-  font-weight: bold; }
-
-dd {
-  margin-left: 10px;
-  margin-bottom: 26px; }
-
-hr {
-  margin: 20px 0;
-  border: 0;
-  border-top: 1px solid #eeeeee;
-  border-bottom: 1px solid #ffffff; }
-
-abbr[title],
-abbr[data-original-title] {
-  cursor: help;
-  border-bottom: 1px dotted #999999; }
-
-abbr.initialism {
-  font-size: 90%;
-  text-transform: uppercase; }
-
-blockquote {
-  padding: 0 0 0 15px;
-  margin: 0 0 20px;
-  border-left: 5px solid #EFEBE0; }
-
-table.docinfo + blockquote, table.docinfo blockquote, h1 + blockquote {
-  border-left: 5px solid #c9c9c9;
-}
-
-table.docinfo + blockquote p, table.docinfo blockquote p, h1 + blockquote p {
-  margin-bottom: 0;
-  font-size: 15px;
-  font-weight: 200;
-  line-height: 1.5;
-  font-style: italic; }
-
-q:before,
-q:after,
-blockquote:before,
-blockquote:after {
-  content: ""; }
-
-address {
-  display: block;
-  margin-bottom: 20px;
-  font-style: normal;
-  line-height: 20px; }
-
-code,
-pre {
-  font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace;
-  padding: 0 3px 2px;
-  font-weight: 500;
-  font-size: 12px;
-  color: #444444;
-  -webkit-border-radius: 3px;
-  -moz-border-radius: 3px;
-  border-radius: 3px; }
-
-.pre {
-  font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace;
-  font-weight: 600;
-  /*color: #504da6;*/
-}
-
-code {
-  padding: 2px 4px;
-  color: #444444;
-  white-space: nowrap;
-  background-color: white;
-  border: 1px solid #777777; }
-
-pre {
-  display: inline-block;
-  box-sizing: border-box;
-  min-width: calc(100% - 19.5px);
-  padding: 9.5px;
-  margin: 0.25em 10px 10px 10px;
-  font-size: 15px;
-  line-height: 20px;
-  white-space: pre !important;
-  overflow-y: hidden;
-  overflow-x: visible;
-  background-color: rgba(0, 0, 0, 0.01);
-  border: 1px solid #cccccc;
-  -webkit-border-radius: 4px;
-  -moz-border-radius: 4px;
-  border-radius: 4px; }
-
-pre.prettyprint {
-  margin-bottom: 20px; }
-
-pre code {
-  padding: 0;
-  color: inherit;
-  white-space: pre;
-  overflow-x: visible;
-  background-color: transparent;
-  border: 0; }
-
-.pre-scrollable {
-  max-height: 340px;
-  overflow-y: scroll; }
-
-table {
-  max-width: 100%;
-  background-color: transparent;
-  border-collapse: collapse;
-  border-spacing: 0; }
-
-table th, table td {
-  padding: 0px 8px 0px;
-}
-
-.table {
-  width: 100%;
-  margin-bottom: 20px; }
-
-.table th,
-.table td {
-  padding: 8px;
-  line-height: 20px;
-  text-align: left;
-  vertical-align: top;
-  border-top: 1px solid #444444; }
-
-.table th {
-  font-weight: bold; }
-
-.table thead th {
-  vertical-align: bottom; }
-
-.table caption + thead tr:first-child th,
-.table caption + thead tr:first-child td,
-.table colgroup + thead tr:first-child th,
-.table colgroup + thead tr:first-child td,
-.table thead:first-child tr:first-child th,
-.table thead:first-child tr:first-child td {
-  border-top: 0; }
-
-.table tbody + tbody {
-  border-top: 2px solid #444444; }
-
-.table .table {
-  background-color: rgba(252, 248, 244, 0.75); }
-
-.table-condensed th,
-.table-condensed td {
-  padding: 4px 5px; }
-
-.table-bordered {
-  border: 1px solid #444444;
-  border-collapse: separate;
-  *border-collapse: collapse;
-  border-left: 0;
-  -webkit-border-radius: 4px;
-  -moz-border-radius: 4px;
-  border-radius: 4px; }
-
-.table-bordered th,
-.table-bordered td {
-  border-left: 1px solid #444444; }
-
-.table-bordered caption + thead tr:first-child th,
-.table-bordered caption + tbody tr:first-child th,
-.table-bordered caption + tbody tr:first-child td,
-.table-bordered colgroup + thead tr:first-child th,
-.table-bordered colgroup + tbody tr:first-child th,
-.table-bordered colgroup + tbody tr:first-child td,
-.table-bordered thead:first-child tr:first-child th,
-.table-bordered tbody:first-child tr:first-child th,
-.table-bordered tbody:first-child tr:first-child td {
-  border-top: 0; }
-
-.table-bordered thead:first-child tr:first-child > th:first-child,
-.table-bordered tbody:first-child tr:first-child > td:first-child,
-.table-bordered tbody:first-child tr:first-child > th:first-child {
-  -webkit-border-top-left-radius: 4px;
-  border-top-left-radius: 4px;
-  -moz-border-radius-topleft: 4px; }
-
-.table-bordered thead:first-child tr:first-child > th:last-child,
-.table-bordered tbody:first-child tr:first-child > td:last-child,
-.table-bordered tbody:first-child tr:first-child > th:last-child {
-  -webkit-border-top-right-radius: 4px;
-  border-top-right-radius: 4px;
-  -moz-border-radius-topright: 4px; }
-
-.table-bordered thead:last-child tr:last-child > th:first-child,
-.table-bordered tbody:last-child tr:last-child > td:first-child,
-.table-bordered tbody:last-child tr:last-child > th:first-child,
-.table-bordered tfoot:last-child tr:last-child > td:first-child,
-.table-bordered tfoot:last-child tr:last-child > th:first-child {
-  -webkit-border-bottom-left-radius: 4px;
-  border-bottom-left-radius: 4px;
-  -moz-border-radius-bottomleft: 4px; }
-
-.table-bordered thead:last-child tr:last-child > th:last-child,
-.table-bordered tbody:last-child tr:last-child > td:last-child,
-.table-bordered tbody:last-child tr:last-child > th:last-child,
-.table-bordered tfoot:last-child tr:last-child > td:last-child,
-.table-bordered tfoot:last-child tr:last-child > th:last-child {
-  -webkit-border-bottom-right-radius: 4px;
-  border-bottom-right-radius: 4px;
-  -moz-border-radius-bottomright: 4px; }
-
-.table-bordered tfoot + tbody:last-child tr:last-child td:first-child {
-  -webkit-border-bottom-left-radius: 0;
-  border-bottom-left-radius: 0;
-  -moz-border-radius-bottomleft: 0; }
-
-.table-bordered tfoot + tbody:last-child tr:last-child td:last-child {
-  -webkit-border-bottom-right-radius: 0;
-  border-bottom-right-radius: 0;
-  -moz-border-radius-bottomright: 0; }
-
-.table-bordered caption + thead tr:first-child th:first-child,
-.table-bordered caption + tbody tr:first-child td:first-child,
-.table-bordered colgroup + thead tr:first-child th:first-child,
-.table-bordered colgroup + tbody tr:first-child td:first-child {
-  -webkit-border-top-left-radius: 4px;
-  border-top-left-radius: 4px;
-  -moz-border-radius-topleft: 4px; }
-
-.table-bordered caption + thead tr:first-child th:last-child,
-.table-bordered caption + tbody tr:first-child td:last-child,
-.table-bordered colgroup + thead tr:first-child th:last-child,
-.table-bordered colgroup + tbody tr:first-child td:last-child {
-  -webkit-border-top-right-radius: 4px;
-  border-top-right-radius: 4px;
-  -moz-border-radius-topright: 4px; }
-
-table.docutils th {
-  background-color: #e8e8e8; }
-
-table.docutils tr:hover {
-  background-color: whitesmoke; }
-
-.table-striped tbody > tr:nth-child(odd) > td,
-.table-striped tbody > tr:nth-child(odd) > th {
-  background-color: rgba(252, 248, 244, 0.75); }
-
-.table-hover tbody tr:hover > td,
-.table-hover tbody tr:hover > th {
-  background-color: rgba(241, 222, 204, 0.75); }
-
-table td[class*="span"],
-table th[class*="span"],
-.row-fluid table td[class*="span"],
-.row-fluid table th[class*="span"] {
-  display: table-cell;
-  float: none;
-  margin-left: 0; }
-
-.hero-unit {
-  padding: 60px;
-  margin-bottom: 30px;
-  font-size: 18px;
-  font-weight: 200;
-  line-height: 30px;
-  color: inherit;
-  background-color: rgba(230, 197, 164, 0.75);
-  -webkit-border-radius: 6px;
-  -moz-border-radius: 6px;
-  border-radius: 6px; }
-
-.hero-unit h1 {
-  margin-bottom: 0;
-  font-size: 60px;
-  line-height: 1;
-  letter-spacing: -1px;
-  color: inherit; }
-
-.hero-unit li {
-  line-height: 30px; }
-
-/* rst2html default used to remove borders from tables and images */
-.borderless, table.borderless td, table.borderless th {
-  border: 0; }
-
-table.borderless td, table.borderless th {
-  /* Override padding for "table.docutils td" with "! important".
-     The right padding separates the table cells. */
-  padding: 0 0.5em 0 0 !important; }
-
-.first {
-  /* Override more specific margin styles with "! important". */
-  margin-top: 0 !important; }
-
-.last, .with-subtitle {
-  margin-bottom: 0 !important; }
-
-.hidden {
-  display: none; }
-
-a.toc-backref {
-  text-decoration: none;
-  color: #444444; }
-
-blockquote.epigraph {
-  margin: 2em 5em; }
-
-dl.docutils dd {
-  margin-bottom: 0.5em; }
-
-object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
-  overflow: hidden; }
-
-/* Uncomment (and remove this text!) to get bold-faced definition list terms
-dl.docutils dt {
-  font-weight: bold }
-*/
-div.abstract {
-  margin: 2em 5em; }
-
-div.abstract p.topic-title {
-  font-weight: bold;
-  text-align: center; }
-
-div.admonition, div.attention, div.caution, div.danger, div.error,
-div.hint, div.important, div.note, div.tip, div.warning {
-  margin: 2em;
-  border: medium outset;
-  padding: 1em; }
-
-div.note, div.warning {
-  margin: 1.5em 0px;
-  border: none; }
-
-div.note p.admonition-title,
-div.warning p.admonition-title {
-  display: none; }
-
-/* Clearfix
- * http://css-tricks.com/snippets/css/clear-fix/
- */
-div.note:after,
-div.warning:after {
-  content: "";
-  display: table;
-  clear: both; }
-
-div.note p:before,
-div.warning p:before {
-  display: block;
-  float: left;
-  font-size: 4em;
-  line-height: 1em;
-  margin-right: 20px;
-  margin-left: 0em;
-  margin-top: -10px;
-  content: '\0270D';
-  /*handwriting*/ }
-
-div.warning p:before {
-  content: '\026A0';
-  /*warning*/ }
-
-div.admonition p.admonition-title, div.hint p.admonition-title,
-div.important p.admonition-title, div.note p.admonition-title,
-div.tip p.admonition-title {
-  font-weight: bold;
-  font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; }
-
-div.attention p.admonition-title, div.caution p.admonition-title,
-div.danger p.admonition-title, div.error p.admonition-title,
-div.warning p.admonition-title, .code .error {
-  color: #b30000;
-  font-weight: bold;
-  font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; }
-
-/* Uncomment (and remove this text!) to get reduced vertical space in
-   compound paragraphs.
-div.compound .compound-first, div.compound .compound-middle {
-  margin-bottom: 0.5em }
-
-div.compound .compound-last, div.compound .compound-middle {
-  margin-top: 0.5em }
-*/
-div.dedication {
-  margin: 2em 5em;
-  text-align: center;
-  font-style: italic; }
-
-div.dedication p.topic-title {
-  font-weight: bold;
-  font-style: normal; }
-
-div.figure {
-  margin-left: 2em;
-  margin-right: 2em; }
-
-div.footer, div.header {
-  clear: both;
-  font-size: smaller; }
-
-div.line-block {
-  display: block;
-  margin-top: 1em;
-  margin-bottom: 1em; }
-
-div.line-block div.line-block {
-  margin-top: 0;
-  margin-bottom: 0;
-  margin-left: 1.5em; }
-
-div.sidebar {
-  margin: 0 0 0.5em 1em;
-  border: medium outset;
-  padding: 1em;
-  background-color: rgba(252, 248, 244, 0.75);
-  width: 40%;
-  float: right;
-  clear: right; }
-
-div.sidebar p.rubric {
-  font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
-  font-size: medium; }
-
-div.system-messages {
-  margin: 5em; }
-
-div.system-messages h1 {
-  color: #b30000; }
-
-div.system-message {
-  border: medium outset;
-  padding: 1em; }
-
-div.system-message p.system-message-title {
-  color: #b30000;
-  font-weight: bold; }
-
-div.topic {
-  margin: 2em; }
-
-h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
-h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
-  margin-top: 0.4em; }
-
-h1.title {
-  text-align: center; }
-
-h2.subtitle {
-  text-align: center; }
-
-hr.docutils {
-  width: 75%; }
-
-img.align-left, .figure.align-left, object.align-left {
-  clear: left;
-  float: left;
-  margin-right: 1em; }
-
-img.align-right, .figure.align-right, object.align-right {
-  clear: right;
-  float: right;
-  margin-left: 1em; }
-
-img.align-center, .figure.align-center, object.align-center {
-  display: block;
-  margin-left: auto;
-  margin-right: auto; }
-
-.align-left {
-  text-align: left; }
-
-.align-center {
-  clear: both;
-  text-align: center; }
-
-.align-right {
-  text-align: right; }
-
-/* reset inner alignment in figures */
-div.align-right {
-  text-align: inherit; }
-
-/* div.align-center * { */
-/*   text-align: left } */
-
-ul.simple > li {
-  margin-bottom: 0.5em }
-
-ol.simple, ul.simple {
-  margin-bottom: 1em; }
-
-ol.arabic {
-  list-style: decimal; }
-
-ol.loweralpha {
-  list-style: lower-alpha; }
-
-ol.upperalpha {
-  list-style: upper-alpha; }
-
-ol.lowerroman {
-  list-style: lower-roman; }
-
-ol.upperroman {
-  list-style: upper-roman; }
-
-p.attribution {
-  text-align: right;
-  margin-left: 50%; }
-
-p.caption {
-  font-style: italic; }
-
-p.credits {
-  font-style: italic;
-  font-size: smaller; }
-
-p.label {
-  white-space: nowrap; }
-
-p.rubric {
-  font-weight: bold;
-  font-size: larger;
-  color: maroon;
-  text-align: center; }
-
-p.sidebar-title {
-  font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
-  font-weight: bold;
-  font-size: larger; }
-
-p.sidebar-subtitle {
-  font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
-  font-weight: bold; }
-
-p.topic-title {
-  font-weight: bold; }
-
-pre.address {
-  margin-bottom: 0;
-  margin-top: 0;
-  font: inherit; }
-
-pre.literal-block, pre.doctest-block, pre.math, pre.code {
-  margin-left: 2em;
-  margin-right: 2em; }
-
-pre.code .ln {
-  color: grey; }
-
-/* line numbers */
-pre.code, code {
-  background-color: #eeeeee; }
-
-pre.code .comment, code .comment {
-  color: #5c6576; }
-
-pre.code .keyword, code .keyword {
-  color: #3B0D06;
-  font-weight: bold; }
-
-pre.code .literal.string, code .literal.string {
-  color: #0c5404; }
-
-pre.code .name.builtin, code .name.builtin {
-  color: #352b84; }
-
-pre.code .deleted, code .deleted {
-  background-color: #DEB0A1; }
-
-pre.code .inserted, code .inserted {
-  background-color: #A3D289; }
-
-span.classifier {
-  font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
-  font-style: oblique; }
-
-span.classifier-delimiter {
-  font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
-  font-weight: bold; }
-
-span.interpreted {
-  font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; }
-
-span.option {
-  white-space: nowrap; }
-
-span.pre {
-  white-space: pre; }
-
-span.problematic {
-  color: #b30000; }
-
-span.section-subtitle {
-  /* font-size relative to parent (h1..h6 element) */
-  font-size: 80%; }
-
-table.citation {
-  border-left: solid 1px #666666;
-  margin-left: 1px; }
-
-table.docinfo {
-  margin: 0em;
-  margin-top: 2em;
-  margin-bottom: 2em;
-  font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif !important;
-  color: #444444; }
-
-table.docutils {
-  margin-top: 0.5em;
-  margin-bottom: 0.5em; }
-
-table.footnote {
-  border-left: solid 1px #2d2d2d;
-  margin-left: 1px; }
-
-table.docutils td, table.docutils th,
-table.docinfo td, table.docinfo th {
-  padding-left: 0.5em;
-  padding-right: 0.5em;
-  vertical-align: top; }
-
-table.docutils th.field-name, table.docinfo th.docinfo-name {
-  font-weight: 700;
-  text-align: left;
-  white-space: nowrap;
-  padding-left: 0; }
-
-h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
-h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
-  font-size: 100%; }
-
-ul.auto-toc {
-  list-style-type: none; }
-
-span.DecNumber {
-  color: #252dbe; }
-
-span.BinNumber {
-  color: #252dbe; }
-
-span.HexNumber {
-  color: #252dbe; }
-
-span.OctNumber {
-  color: #252dbe; }
-
-span.FloatNumber {
-  color: #252dbe; }
-
-span.Identifier {
-  color: #3b3b3b; }
-
-span.Keyword {
-  font-weight: 600;
-  color: #5e8f60; }
-
-span.StringLit {
-  color: #a4255b; }
-
-span.LongStringLit {
-  color: #a4255b; }
-
-span.CharLit {
-  color: #a4255b; }
-
-span.EscapeSequence {
-  color: black; }
-
-span.Operator {
-  color: black; }
-
-span.Punctuation {
-  color: black; }
-
-span.Comment, span.LongComment {
-  font-style: italic;
-  font-weight: 400;
-  color: #484a86; }
-
-span.RegularExpression {
-  color: darkviolet; }
-
-span.TagStart {
-  color: darkviolet; }
-
-span.TagEnd {
-  color: darkviolet; }
-
-span.Key {
-  color: #252dbe; }
-
-span.Value {
-  color: #252dbe; }
-
-span.RawData {
-  color: #a4255b; }
-
-span.Assembler {
-  color: #252dbe; }
-
-span.Preprocessor {
-  color: #252dbe; }
-
-span.Directive {
-  color: #252dbe; }
-
-span.Command, span.Rule, span.Hyperlink, span.Label, span.Reference,
-span.Other {
-  color: black; }
-
-/* Pop type, const, proc, and iterator defs in nim def blocks */
-dt pre > span.Identifier, dt pre > span.Operator {
-  color: #155da4;
-  font-weight: 700; }
-
-dt pre > span.Identifier ~ span.Identifier, dt pre > span.Operator ~ span.Identifier {
-  color: inherit;
-  font-weight: inherit; }
-
-dt pre > span.Operator ~ span.Identifier {
-  color: inherit;
-  font-weight: inherit; }
-
-/* Nim sprite for the footer (taken from main page favicon) */
-.nim-sprite {
-  display: inline-block;
-  height: 12px;
-  width: 12px;
-  background-position: 0 0;
-  background-size: 12px 12px;
-  -webkit-filter: opacity(50%);
-  background-repeat: no-repeat;
-  background-image: url("data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA==");
-  margin-bottom: -5px; }
-div.pragma {
-  display: none;
-}
-span.pragmabegin {
-  cursor: pointer;
-}
-span.pragmaend {
-  cursor: pointer;
-}
-
-div.search_results {
-  background-color: antiquewhite;
-  margin: 3em;
-  padding: 1em;
-  border: 1px solid #4d4d4d;
-}
-
-div#global-links ul {
-  margin-left: 0;
-  list-style-type: none;
-}
-</style>
-
-<script type="text/javascript" src="../dochack.js"></script>
-
-<script type="text/javascript">
-function togglepragma(d) {
-  if (d.style.display != 'inline')
-    d.style.display = 'inline';
-  else
-    d.style.display = 'none';
-}
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
 
-function main() {
-  var elements = document.getElementsByClassName("pragmabegin");
-  for (var i = 0; i < elements.length; ++i) {
-    var e = elements[i];
-    e.onclick = function(event) {
-      togglepragma(event.target.nextSibling);
-    };
-  }
-  var elements = document.getElementsByClassName("pragmaend");
-  for (var i = 0; i < elements.length; ++i) {
-    var e = elements[i];
-    e.onclick = function(event) {
-      togglepragma(event.target.previousSibling);
-    };
-  }
-}
-</script>
+<!-- CSS -->
+<link rel="stylesheet" type="text/css" href="${nimdoccss}?v=$nimVersion">
 
+<!-- JS -->
+<script type="text/javascript" src="${dochackjs}?v=$nimVersion"></script>
 </head>
-<body onload="main()">
-<div class="document" id="documentId">
-  <div class="container">
-    <h1 class="title">$title</h1>
-    $content
-    <div class="row">
+<body>
+  <div class="document" id="documentId">
+    <div class="container">
+      <h1 class="title">$title</h1>$subtitle
+      $content
       <div class="twelve-columns footer">
         <span class="nim-sprite"></span>
-        <br/>
-        <small>Made with Nim. Generated: $date $time UTC</small>
+        <br>
+        <small style="color: var(--hint);">Made with Nim. Generated: $date $time UTC</small>
       </div>
     </div>
   </div>
-</div>
-$analytics
+  $analytics
 </body>
 </html>
 """
diff --git a/config/nimdoc.tex.cfg b/config/nimdoc.tex.cfg
index 599ede345..4fb1aec90 100644
--- a/config/nimdoc.tex.cfg
+++ b/config/nimdoc.tex.cfg
@@ -3,27 +3,42 @@
 # (c) 2012 Andreas Rumpf
 # Feel free to edit the templates as you need.
 
-split.item.toc = "20"  
+split.item.toc = "20"
 # too long entries in the table of contents wrap around
 # after this number of characters
 
 doc.section = """
-\chapter{$sectionTitle}\label{$sectionID}
-\begin{description}
+\rsthA[$sectionTitle]{$sectionTitle}\label{$sectionID}
+
 $content
-\end{description}
 """
 
 doc.section.toc = ""
 # $sectionID $sectionTitleID $sectionTitle $content
 
 doc.item = """
-\item[\texttt{$header}\label{$itemID}]\mbox{~}\\*
+
+\vspace{1em}
+\phantomsection\addcontentsline{toc}{subsubsection}{$uniqueName}
+\label{$itemSymOrID}\hypertarget{$itemSymOrID}{}
+
+\begin{rstdocitem}
+$header
+\end{rstdocitem}
+
+\begin{addmargin}[0.05\linewidth]{0pt}
 $desc
+\end{addmargin}
+"""
+
+doc.item2 = """
+\phantomsection\addcontentsline{toc}{subsection}{$header_plain}
+\label{$overloadGroupName}\hypertarget{$overloadGroupName}{}
+
+$content
 """
 
 doc.item.toc = ""
-#  \item $name\ref{$itemID}
 
 doc.toc = r"\tableofcontents \newpage"
 
@@ -38,86 +53,33 @@ $moduledesc
 $content
 """
 
+# $1 - number of listing in document, $2 - language (e.g. langNim), $3 - anchor
+doc.listing_start = "\\begin{rstpre}\n"
+doc.listing_end = "\n\\end{rstpre}\n\n"
+
 doc.file = """
-% This file was generated by Nimrod.
+% This file was generated by Nim.
 % Generated: $date $time UTC
-\documentclass[a4paper]{article}
-\usepackage[left=2cm,right=3cm,top=3cm,bottom=3cm]{geometry}
-\usepackage[utf8]{inputenc}
-\usepackage[T1]{fontenc}
-\usepackage{graphicx}
-\usepackage{lmodern}
-\usepackage{fancyvrb, courier}
-\usepackage{tabularx}
-\usepackage{hyperref}
+%
+% Compile it by:   xelatex    (up to 3 times to get labels generated)
+%                  -------
+% For example:
+%   xelatex file.tex
+%   xelatex file.tex
+%   makeindex file
+%   xelatex file.tex
+%
+\documentclass{nimdoc}
 
 \begin{document}
-\title{$title $version}
+\title{$title $version $subtitle}
 \author{$author}
 
-\tolerance 1414 
-\hbadness 1414 
-\emergencystretch 1.5em 
-\hfuzz 0.3pt 
-\widowpenalty=10000 
-\vfuzz \hfuzz 
-\raggedbottom 
-
 \maketitle
 
-\newenvironment{rstpre}{\VerbatimEnvironment\begingroup\begin{Verbatim}[fontsize=\footnotesize , commandchars=\\\{\}]}{\end{Verbatim}\endgroup}
-
-% to pack tabularx into a new environment, special syntax is needed :-(
-\newenvironment{rsttab}[1]{\tabularx{\linewidth}{#1}}{\endtabularx}
-
-\newcommand{\rstsub}[1]{\raisebox{-0.5ex}{\scriptsize{#1}}}
-\newcommand{\rstsup}[1]{\raisebox{0.5ex}{\scriptsize{#1}}}
-
-\newcommand{\rsthA}[1]{\section{#1}}
-\newcommand{\rsthB}[1]{\subsection{#1}}
-\newcommand{\rsthC}[1]{\subsubsection{#1}}
-\newcommand{\rsthD}[1]{\paragraph{#1}}
-\newcommand{\rsthE}[1]{\paragraph{#1}}
-
-\newcommand{\rstovA}[1]{\section*{#1}}
-\newcommand{\rstovB}[1]{\subsection*{#1}}
-\newcommand{\rstovC}[1]{\subsubsection*{#1}}
-\newcommand{\rstovD}[1]{\paragraph*{#1}}
-\newcommand{\rstovE}[1]{\paragraph*{#1}}
-
-% Syntax highlighting:
-\newcommand{\spanDecNumber}[1]{#1}
-\newcommand{\spanBinNumber}[1]{#1}
-\newcommand{\spanHexNumber}[1]{#1}
-\newcommand{\spanOctNumber}[1]{#1}
-\newcommand{\spanFloatNumber}[1]{#1}
-\newcommand{\spanIdentifier}[1]{#1}
-\newcommand{\spanKeyword}[1]{\textbf{#1}}
-\newcommand{\spanStringLit}[1]{#1}
-\newcommand{\spanLongStringLit}[1]{#1}
-\newcommand{\spanCharLit}[1]{#1}
-\newcommand{\spanEscapeSequence}[1]{#1}
-\newcommand{\spanOperator}[1]{#1}
-\newcommand{\spanPunctuation}[1]{#1}
-\newcommand{\spanComment}[1]{\emph{#1}}
-\newcommand{\spanLongComment}[1]{\emph{#1}}
-\newcommand{\spanRegularExpression}[1]{#1}
-\newcommand{\spanTagStart}[1]{#1}
-\newcommand{\spanTagEnd}[1]{#1}
-\newcommand{\spanKey}[1]{#1}
-\newcommand{\spanValue}[1]{#1}
-\newcommand{\spanRawData}[1]{#1}
-\newcommand{\spanAssembler}[1]{#1}
-\newcommand{\spanPreprocessor}[1]{#1}
-\newcommand{\spanDirective}[1]{#1}
-\newcommand{\spanCommand}[1]{#1}
-\newcommand{\spanRule}[1]{#1}
-\newcommand{\spanHyperlink}[1]{#1}
-\newcommand{\spanLabel}[1]{#1}
-\newcommand{\spanReference}[1]{#1}
-\newcommand{\spanOther}[1]{#1}
-\newcommand{\spantok}[1]{\frame{#1}}
-
 $content
+
+\printindex
+
 \end{document}
 """
diff --git a/copying.txt b/copying.txt
index d2bf9a7a7..819330be3 100644
--- a/copying.txt
+++ b/copying.txt
@@ -1,7 +1,7 @@
 =====================================================
 Nim -- a Compiler for Nim. https://nim-lang.org/
 
-Copyright (C) 2006-2018 Andreas Rumpf. All rights reserved.
+Copyright (C) 2006-2024 Andreas Rumpf. All rights reserved.
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/doc/advopt.txt b/doc/advopt.txt
index 685c8127d..e4d11081a 100644
--- a/doc/advopt.txt
+++ b/doc/advopt.txt
@@ -4,93 +4,176 @@ Advanced commands:
   //compileToOC, objc       compile project to Objective C code
   //js                      compile project to Javascript
   //e                       run a Nimscript file
+  //md2html                 convert a Markdown file to HTML
+                            use `--docCmd:skip` to skip compiling snippets
   //rst2html                convert a reStructuredText file to HTML
-  //rst2tex                 convert a reStructuredText file to TeX
+                            use `--docCmd:skip` to skip compiling snippets
+  //md2tex                  convert a Markdown file to LaTeX
+  //rst2tex                 convert a reStructuredText file to LaTeX
+  //doc2tex                 extract the documentation to a LaTeX file
   //jsondoc                 extract the documentation to a json file
   //ctags                   create a tags file
   //buildIndex              build an index for the whole documentation
-  //run                     run the project (with Tiny C backend; buggy!)
   //genDepend               generate a DOT file containing the
                             module dependency graph
   //dump                    dump all defined conditionals and search paths
-  //check                   checks the project for syntax and semantic
+                            see also: --dump.format:json (useful with: `| jq`)
+  //check                   checks the project for syntax and semantics
+                            (can be combined with --defusages)
+
+Runtime checks (see -x):
+  --objChecks:on|off        turn obj conversion checks on|off
+  --fieldChecks:on|off      turn case variant field checks on|off
+  --rangeChecks:on|off      turn range checks on|off
+  --boundChecks:on|off      turn bound checks on|off
+  --overflowChecks:on|off   turn int over-/underflow checks on|off
+  --floatChecks:on|off      turn all floating point (NaN/Inf) checks on|off
+  --nanChecks:on|off        turn NaN checks on|off
+  --infChecks:on|off        turn Inf checks on|off
 
 Advanced options:
+  --defusages:FILE,LINE,COL
+                            find the definition and all usages of a symbol
   -o:FILE, --out:FILE       set the output filename
-  --stdout                  output to stdout
+  --outdir:DIR              set the path where the output file will be written
+  --usenimcache             will use `outdir=$$nimcache`, whichever it resolves
+                            to after all options have been processed
+  --stdout:on|off           output to stdout
   --colors:on|off           turn compiler messages coloring on|off
-  --listFullPaths           list full paths in messages
+  --filenames:abs|canonical|legacyRelProj
+                            customize how filenames are rendered in compiler messages,
+                            defaults to `abs` (absolute)
+  --processing:dots|filenames|off
+                            show files as they're being processed by nim compiler
+  --unitsep:on|off          use the ASCII unit separator (31) between error
+                            messages, useful for IDE-like tooling
+  --declaredLocs:on|off     show declaration locations in messages
+  --spellSuggest:num        show at most `num >= 0` spelling suggestions on typos.
+                            if `num` is not specified (or `auto`), return
+                            an implementation defined set of suggestions.
+  --hints:on|off|list.      `on|off` enables or disables hints.
+                            `list` reports which hints are selected.
+  --hint:X:on|off           turn specific hint X on|off. `hint:X` means `hint:X:on`,
+                            as with similar flags. `all` is the set of all hints
+                            (only `all:off` is supported).
+  --hintAsError:X:on|off    turn specific hint X into an error on|off
   -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
-  --hints:on|off|list       turn all hints on|off or list all available
-  --hint[X]:on|off          turn specific hint X on|off
+                            `on|off` enables or disables warnings.
+                            `list` reports which warnings are selected.
+  --warning:X:on|off        turn specific warning X on|off. `warning:X` means `warning:X:on`,
+                            as with similar flags. `all` is the set of all warning
+                            (only `all:off` is supported).
+  --warningAsError:X:on|off
+                            turn specific warning X into an error on|off
+  --styleCheck:off|hint|error
+                            produce hints or errors for Nim identifiers that
+                            do not adhere to Nim's official style guide
+                            https://nim-lang.org/docs/nep1.html
+  --styleCheck:usages       only enforce consistent spellings of identifiers,
+                            do not enforce the style on declarations
+  --showAllMismatches:on|off
+                            show all mismatching candidates in overloading
+                            resolution
   --lib:PATH                set the system library path
   --import:PATH             add an automatically imported module
+                            see also `patchFile` in nimscript which offers more flexibility.
   --include:PATH            add an automatically included module
   --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'
+                            see also https://nim-lang.org/docs/nimc.html#compiler-usage-generated-c-code-directory
+  -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'),
+                            and a '.deps' file containing the dependencies;
                             implies --compileOnly
-  --genDeps                 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
+  --cc:SYMBOL               specify the C compiler
   --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
+  --project                 document the whole project (doc)
+  --docRoot:path            `nim doc --docRoot:/foo --project --outdir:docs /foo/sub/main.nim`
+                            generates: docs/sub/main.html
+                            if path == @pkg, will use nimble file enclosing dir
+                            if path == @path, will use first matching dir in `--path`
+                            if path == @default (the default and most useful), will use
+                            best match among @pkg,@path.
+                            if these are nonexistent, will use project path
+  -b, --backend:c|cpp|js|objc
+                            sets backend to use with commands like `nim doc` or `nim r`
+  --docCmd:cmd              if `cmd == skip`, skips runnableExamples
+                            else, runs runnableExamples with given options, e.g.:
+                            `--docCmd:"-d:foo --threads:on"`
+  --docSeeSrcUrl:url        activate 'see source' for doc command
                             (see doc.item.seesrc in config/nimdoc.cfg)
+  --docInternal             also generate documentation for non-exported symbols
   --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
-  --taintMode:on|off        turn taint mode on|off
   --implicitStatic:on|off   turn implicit compile time evaluation on|off
-  --patterns:on|off         turn pattern matching on|off
-  --memTracker:on|off       turn memory tracker on|off
+  --trmacros:on|off         turn term rewriting macros on|off
+  --multimethods:on|off     turn multi-methods on|off
   --hotCodeReloading:on|off
                             turn support for hot code reloading on|off
   --excessiveStackTrace:on|off
                             stack traces use full file paths
-  --oldNewlines:on|off      turn on|off the old behaviour of "\n"
-  --laxStrings:on|off       when turned on, accessing the zero terminator in
-                            strings is allowed; only 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
-                            select the GC to use; default is 'refc'
-  --index:on|off            turn index file generation on|off
+  --stackTraceMsgs:on|off   enable user defined stack frame msgs via `setFrameMsg`
+  --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
+  --mm:orc|arc|refc|markAndSweep|boehm|go|none|regions
+                            select which memory management to use; default is 'orc'
+  --exceptions:setjmp|cpp|goto|quirky
+                            select the exception handling implementation
+  --index:on|off|only       docgen: turn index file generation on|off (`only` means
+                            not generate output files like HTML)
+  --noImportdoc:on|off      turn loading documentation ``.idx`` files on|off
   --putenv:key=value        set an environment variable
   --NimblePath:PATH         add a path for Nimble support
   --noNimblePath            deactivate the Nimble path
-  --noCppExceptions         use default exception handling with C++ backend
-  --cppCompileToNamespace   use namespace "Nim" for the generated C++ code
+  --clearNimblePath         empty the list of Nimble package search paths
+  --cppCompileToNamespace:namespace
+                            use the provided namespace for the generated C++ code,
+                            if no namespace is provided "Nim" will be used
+  --nimMainPrefix:prefix    use `{prefix}NimMain` instead of `NimMain` in the produced
+                            C/C++ code
+  --expandMacro:MACRO       dump every generated AST from MACRO
+  --expandArc:PROCNAME      show how PROCNAME looks like after diverse optimizations
+                            before the final backend phase (mostly ARC/ORC specific)
   --excludePath:PATH        exclude a path from the list of search paths
   --dynlibOverride:SYMBOL   marks SYMBOL so that dynlib:SYMBOL
                             has no effect and can be statically linked instead;
                             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
+                            disables the effects of the dynlib pragma
+  --listCmd                 list the compilation commands; can be combined with
+                            `--hint:exec:on` and `--hint:link:on`
+  --asm                     produce assembler code
   --parallelBuild:0|1|...   perform a parallel build
                             value = number of processors (0 for auto-detect)
+  --incremental:on|off      only recompile the changed modules (experimental!)
   --verbosity:0|1|2|3       set Nim's verbosity level (1 is default)
+  --errorMax:N              stop compilation after N errors; 0 means unlimited
+  --maxLoopIterationsVM:N   set max iterations for all VM loops
   --experimental:$1
                             enable experimental language feature
-  -v, --version             show detailed version information
+  --legacy:$2
+                            enable obsolete/legacy language feature
+  --benchmarkVM:on|off      turn benchmarking of VM code with cpuTime() on|off
+  --profileVM:on|off        turn compile time VM profiler on|off
+  --panics:on|off           turn panics into process terminations (default: off)
+  --deepcopy:on|off         enable 'system.deepCopy' for ``--mm:arc|orc``
+  --jsbigint64:on|off       toggle the use of BigInt for 64-bit integers for
+                            the JavaScript backend (default: on)
+  --nimBasePattern:nimbase.h
+                            allows to specify a custom pattern for `nimbase.h`
diff --git a/doc/apis.md b/doc/apis.md
new file mode 100644
index 000000000..f0b8c93e5
--- /dev/null
+++ b/doc/apis.md
@@ -0,0 +1,85 @@
+=================
+API naming design
+=================
+
+.. default-role:: code
+.. include:: rstcommon.rst
+
+The API is designed to be **easy to use** and consistent. Ease of use is
+measured by the number of calls to achieve a concrete high-level action.
+
+
+Naming scheme
+=============
+
+The library uses a simple naming scheme that makes use of common abbreviations
+to keep the names short but meaningful. Since version 0.8.2 many symbols have
+been renamed to fit this scheme. The ultimate goal is that the programmer can
+*guess* a name.
+
+
+===================     ============   ======================================
+English word            To use         Notes
+===================     ============   ======================================
+initialize              initT          `init` is used to create a
+                                       value type `T`
+new                     newP           `new` is used to create a
+                                       reference type `P`
+find                    find           should return the position where
+                                       something was found; for a bool result
+                                       use `contains`
+contains                contains       often short for `find() >= 0`
+append                  add            use `add` instead of `append`
+compare                 cmp            should return an int with the
+                                       `< 0` `== 0` or `> 0` semantics;
+                                       for a bool result use `sameXYZ`
+put                     put, `[]=`     consider overloading `[]=` for put
+get                     get, `[]`      consider overloading `[]` for get;
+                                       consider to not use `get` as a
+                                       prefix: `len` instead of `getLen`
+length                  len            also used for *number of elements*
+size                    size, len      size should refer to a byte size
+capacity                cap
+memory                  mem            implies a low-level operation
+items                   items          default iterator over a collection
+pairs                   pairs          iterator over (key, value) pairs
+delete                  delete, del    del is supposed to be faster than
+                                       delete, because it does not keep
+                                       the order; delete keeps the order
+remove                  delete, del    inconsistent right now
+remove-and-return       pop            `Table`/`TableRef` alias to `take`
+include                 incl
+exclude                 excl
+command                 cmd
+execute                 exec
+environment             env
+variable                var
+value                   value, val     val is preferred, inconsistent right
+                                       now
+executable              exe
+directory               dir
+path                    path           path is the string "/usr/bin" (for
+                                       example), dir is the content of
+                                       "/usr/bin"; inconsistent right now
+extension               ext
+separator               sep
+column                  col, column    col is preferred, inconsistent right
+                                       now
+application             app
+configuration           cfg
+message                 msg
+argument                arg
+object                  obj
+parameter               param
+operator                opr
+procedure               proc
+function                func
+coordinate              coord
+rectangle               rect
+point                   point
+symbol                  sym
+literal                 lit
+string                  str
+identifier              ident
+indentation             indent
+===================     ============   ======================================
diff --git a/doc/apis.txt b/doc/apis.txt
deleted file mode 100644
index 277c1925b..000000000
--- a/doc/apis.txt
+++ /dev/null
@@ -1,81 +0,0 @@
-=================
-API naming design
-=================
-
-The API is designed to be **easy to use** and consistent. Ease of use is
-measured by the number of calls to achieve a concrete high level action.
-
-
-Naming scheme
-=============
-
-The library uses a simple naming scheme that makes use of common abbreviations
-to keep the names short but meaningful. Since version 0.8.2 many symbols have
-been renamed to fit this scheme. The ultimate goal is that the programmer can
-*guess* a name.
-
-
--------------------     ------------   --------------------------------------
-English word            To use         Notes
--------------------     ------------   --------------------------------------
-initialize              initT          ``init`` is used to create a
-                                       value type ``T``
-new                     newP           ``new`` is used to create a
-                                       reference type ``P``
-find                    find           should return the position where
-                                       something was found; for a bool result
-                                       use ``contains``
-contains                contains       often short for ``find() >= 0``
-append                  add            use ``add`` instead of ``append``
-compare                 cmp            should return an int with the
-                                       ``< 0`` ``== 0`` or ``> 0`` semantics;
-                                       for a bool result use ``sameXYZ``
-put                     put, ``[]=``   consider overloading ``[]=`` for put
-get                     get, ``[]``    consider overloading ``[]`` for get;
-                                       consider to not use ``get`` as a
-                                       prefix: ``len`` instead of ``getLen``
-length                  len            also used for *number of elements*
-size                    size, len      size should refer to a byte size
-capacity                cap
-memory                  mem            implies a low-level operation
-items                   items          default iterator over a collection
-pairs                   pairs          iterator over (key, value) pairs
-delete                  delete, del    del is supposed to be faster than
-                                       delete, because it does not keep
-                                       the order; delete keeps the order
-remove                  delete, del    inconsistent right now
-include                 incl
-exclude                 excl
-command                 cmd
-execute                 exec
-environment             env
-variable                var
-value                   value, val     val is preferred, inconsistent right
-                                       now
-executable              exe
-directory               dir
-path                    path           path is the string "/usr/bin" (for
-                                       example), dir is the content of
-                                       "/usr/bin"; inconsistent right now
-extension               ext
-separator               sep
-column                  col, column    col is preferred, inconsistent right
-                                       now
-application             app
-configuration           cfg
-message                 msg
-argument                arg
-object                  obj
-parameter               param
-operator                opr
-procedure               proc
-function                func
-coordinate              coord
-rectangle               rect
-point                   point
-symbol                  sym
-literal                 lit
-string                  str
-identifier              ident
-indentation             indent
--------------------     ------------   --------------------------------------
diff --git a/doc/astspec.txt b/doc/astspec.txt
index 73058cd93..7a7053a2d 100644
--- a/doc/astspec.txt
+++ b/doc/astspec.txt
@@ -6,8 +6,7 @@ The AST consists of nodes (``NimNode``) with a variable number of
 children. Each node has a field named ``kind`` which describes what the node
 contains:
 
-.. code-block:: nim
-
+  ```nim
   type
     NimNodeKind = enum     ## kind of a node; only explanatory
       nnkNone,             ## invalid node kind
@@ -32,6 +31,7 @@ contains:
         strVal: string                 ## the string literal
       else:
         sons: seq[NimNode]             ## the node's sons (or children)
+  ```
 
 For the ``NimNode`` type, the ``[]`` operator has been overloaded:
 ``n[i]`` is ``n``'s ``i``-th child.
@@ -50,9 +50,9 @@ A leaf of the AST often corresponds to a terminal symbol in the concrete
 syntax. Note that the default ``float`` in Nim maps to ``float64`` such that
 the default AST for a float is ``nnkFloat64Lit`` as below.
 
------------------                ---------------------------------------------
+=================                =============================================
 Nim expression                   Corresponding AST
------------------                ---------------------------------------------
+=================                =============================================
 ``42``                           ``nnkIntLit(intVal = 42)``
 ``42'i8``                        ``nnkInt8Lit(intVal = 42)``
 ``42'i16``                       ``nnkInt16Lit(intVal = 42)``
@@ -72,7 +72,7 @@ Nim expression                   Corresponding AST
 ``nil``                          ``nnkNilLit()``
 ``myIdentifier``                 ``nnkIdent(strVal = "myIdentifier")``
 ``myIdentifier``                 after lookup pass: ``nnkSym(strVal = "myIdentifier", ...)``
------------------                ---------------------------------------------
+=================                =============================================
 
 Identifiers are ``nnkIdent`` nodes. After the name lookup pass these nodes
 get transferred into ``nnkSym`` nodes.
@@ -86,17 +86,19 @@ Command call
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   echo "abc", "xyz"
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkCommand(
     nnkIdent("echo"),
     nnkStrLit("abc"),
     nnkStrLit("xyz")
   )
+  ```
 
 
 Call with ``()``
@@ -104,17 +106,19 @@ Call with ``()``
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   echo("abc", "xyz")
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkCall(
     nnkIdent("echo"),
     nnkStrLit("abc"),
     nnkStrLit("xyz")
   )
+  ```
 
 
 Infix operator call
@@ -122,29 +126,32 @@ Infix operator call
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   "abc" & "xyz"
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkInfix(
     nnkIdent("&"),
     nnkStrLit("abc"),
     nnkStrLit("xyz")
   )
+  ```
 
 Note that with multiple infix operators, the command is parsed by operator
 precedence.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   5 + 3 * 4
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkInfix(
     nnkIdent("+"),
     nnkIntLit(5),
@@ -154,20 +161,22 @@ AST:
       nnkIntLit(4)
     )
   )
+  ```
 
 As a side note, if you choose to use infix operators in a prefix form, the AST
 behaves as a
-[parenthetical function call](./macros.html#calls-expressions-call-with) with
+[parenthetical function call](#callsslashexpressions-call-with) with
 ``nnkAccQuoted``, as follows:
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   `+`(3, 4)
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkCall(
     nnkAccQuoted(
       nnkIdent("+")
@@ -175,22 +184,25 @@ AST:
     nnkIntLit(3),
     nnkIntLit(4)
   )
+  ```
 
 Prefix operator call
 --------------------
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   ? "xyz"
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkPrefix(
     nnkIdent("?"),
     nnkStrLit("abc")
   )
+  ```
 
 
 Postfix operator call
@@ -201,16 +213,18 @@ Postfix operator call
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   identifier*
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkPostfix(
     nnkIdent("*"),
     nnkIdent("identifier")
   )
+  ```
 
 
 Call with named arguments
@@ -218,12 +232,13 @@ Call with named arguments
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   writeLine(file=stdout, "hallo")
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkCall(
     nnkIdent("writeLine"),
     nnkExprEqExpr(
@@ -232,39 +247,44 @@ AST:
     ),
     nnkStrLit("hallo")
   )
+  ```
 
 Call with raw string literal
 ----------------------------
 
 This is used, for example, in the ``bindSym`` examples
-[here](http://nim-lang.org/docs/manual.html#macros-bindsym) and with
+[here](manual.html#macros-bindsym) and with
 ``re"some regexp"`` in the regular expression module.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   echo"abc"
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkCallStrLit(
     nnkIdent("echo"),
     nnkRStrLit("hello")
   )
+  ```
 
 Dereference operator ``[]``
 ---------------------------
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   x[]
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkDerefExpr(nnkIdent("x"))
+  ```
 
 
 Addr operator
@@ -272,13 +292,15 @@ Addr operator
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   addr(x)
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkAddr(nnkIdent("x"))
+  ```
 
 
 Cast operator
@@ -286,13 +308,15 @@ Cast operator
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   cast[T](x)
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkCast(nnkIdent("T"), nnkIdent("x"))
+  ```
 
 
 Object access operator ``.``
@@ -300,13 +324,15 @@ Object access operator ``.``
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   x.y
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkDotExpr(nnkIdent("x"), nnkIdent("y"))
+  ```
 
 If you use Nim's flexible calling syntax (as in ``x.len()``), the result is the
 same as above but wrapped in an ``nnkCall``.
@@ -317,31 +343,77 @@ Array access operator ``[]``
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   x[y]
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkBracketExpr(nnkIdent("x"), nnkIdent("y"))
+  ```
 
 
 Parentheses
 -----------
 
-Parentheses for affecting operator precedence or tuple construction
-are built with the ``nnkPar`` node.
+Parentheses for affecting operator precedence use the ``nnkPar`` node.
 
 Concrete syntax:
 
-.. code-block:: nim
-  (1, 2, (3))
+  ```nim
+  (a + b) * c
+  ```
 
 AST:
 
-.. code-block:: nim
-  nnkPar(nnkIntLit(1), nnkIntLit(2), nnkPar(nnkIntLit(3)))
+  ```nim
+  nnkInfix(nnkIdent("*"),
+    nnkPar(
+      nnkInfix(nnkIdent("+"), nnkIdent("a"), nnkIdent("b"))),
+    nnkIdent("c"))
+  ```
+
+Tuple Constructors
+------------------
+
+Nodes for tuple construction are built with the ``nnkTupleConstr`` node.
+
+Concrete syntax:
+
+  ```nim
+  (1, 2, 3)
+  (a: 1, b: 2, c: 3)
+  ()
+  ```
+
+AST:
+
+  ```nim
+  nnkTupleConstr(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3))
+  nnkTupleConstr(
+    nnkExprColonExpr(nnkIdent("a"), nnkIntLit(1)),
+    nnkExprColonExpr(nnkIdent("b"), nnkIntLit(2)),
+    nnkExprColonExpr(nnkIdent("c"), nnkIntLit(3)))
+  nnkTupleConstr()
+  ```
+
+Since the one tuple would be syntactically identical to parentheses
+with an expression in them, the parser expects a trailing comma for
+them. For tuple constructors with field names, this is not necessary.
 
+  ```nim
+  (1,)
+  (a: 1)
+  ```
+
+AST:
+
+  ```nim
+  nnkTupleConstr(nnkIntLit(1))
+  nnkTupleConstr(
+    nnkExprColonExpr(nnkIdent("a"), nnkIntLit(1)))
+  ```
 
 Curly braces
 ------------
@@ -350,28 +422,32 @@ Curly braces are used as the set constructor.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   {1, 2, 3}
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkCurly(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3))
+  ```
 
 When used as a table constructor, the syntax is different.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   {a: 3, b: 5}
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkTableConstr(
     nnkExprColonExpr(nnkIdent("a"), nnkIntLit(3)),
     nnkExprColonExpr(nnkIdent("b"), nnkIntLit(5))
   )
+  ```
 
 
 Brackets
@@ -381,13 +457,15 @@ Brackets are used as the array constructor.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   [1, 2, 3]
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkBracket(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3))
+  ```
 
 
 Ranges
@@ -399,22 +477,24 @@ AST, construction with ``..`` as an infix operator should be used instead.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   1..3
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkInfix(
     nnkIdent(".."),
     nnkIntLit(1),
     nnkIntLit(3)
   )
+  ```
 
 Example code:
 
-.. code-block:: nim
-  macro genRepeatEcho(): stmt =
+  ```nim
+  macro genRepeatEcho() =
     result = newNimNode(nnkStmtList)
 
     var forStmt = newNimNode(nnkForStmt) # generate a for statement
@@ -432,6 +512,7 @@ Example code:
                   # 3
                   # 3
                   # 3
+  ```
 
 
 If expression
@@ -441,17 +522,19 @@ The representation of the ``if`` expression is subtle, but easy to traverse.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   if cond1: expr1 elif cond2: expr2 else: expr3
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkIfExpr(
     nnkElifExpr(cond1, expr1),
     nnkElifExpr(cond2, expr2),
     nnkElseExpr(expr3)
   )
+  ```
 
 Documentation Comments
 ----------------------
@@ -462,19 +545,21 @@ comments are ignored.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   ## This is a comment
   ## This is part of the first comment
   stmt1
   ## Yet another
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkCommentStmt() # only appears once for the first two lines!
   stmt1
   nnkCommentStmt() # another nnkCommentStmt because there is another comment
                    # (separate from the first)
+  ```
 
 Pragmas
 -------
@@ -485,30 +570,33 @@ objects, but the standalone ``emit`` pragma shows the basics with the AST.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   {.emit: "#include <stdio.h>".}
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkPragma(
     nnkExprColonExpr(
       nnkIdent("emit"),
       nnkStrLit("#include <stdio.h>") # the "argument"
     )
   )
+  ```
 
 As many ``nnkIdent`` appear as there are pragmas between ``{..}``. Note that
 the declaration of new pragmas is essentially the same:
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   {.pragma: cdeclRename, cdecl.}
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkPragma(
     nnkExprColonExpr(
       nnkIdent("pragma"), # this is always first when declaring a new pragma
@@ -516,6 +604,7 @@ AST:
     ),
     nnkIdent("cdecl")
   )
+  ```
 
 Statements
 ==========
@@ -528,7 +617,7 @@ there is no ``else`` branch, no ``nnkElse`` child exists.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   if cond1:
     stmt1
   elif cond2:
@@ -537,16 +626,18 @@ Concrete syntax:
     stmt3
   else:
     stmt4
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkIfStmt(
     nnkElifBranch(cond1, stmt1),
     nnkElifBranch(cond2, stmt2),
     nnkElifBranch(cond3, stmt3),
     nnkElse(stmt4)
   )
+  ```
 
 
 When statement
@@ -560,13 +651,15 @@ Assignment
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   x = 42
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkAsgn(nnkIdent("x"), nnkIntLit(42))
+  ```
 
 This is not the syntax for assignment when combined with ``var``, ``let``,
 or ``const``.
@@ -576,15 +669,17 @@ Statement list
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   stmt1
   stmt2
   stmt3
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkStmtList(stmt1, stmt2, stmt3)
+  ```
 
 
 Case statement
@@ -592,7 +687,7 @@ Case statement
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   case expr1
   of expr2, expr3..expr4:
     stmt1
@@ -602,10 +697,11 @@ Concrete syntax:
     stmt3
   else:
     stmt4
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkCaseStmt(
     expr1,
     nnkOfBranch(expr2, nnkRange(expr3, expr4), stmt1),
@@ -613,6 +709,7 @@ AST:
     nnkElifBranch(cond1, stmt3),
     nnkElse(stmt4)
   )
+  ```
 
 The ``nnkElifBranch`` and ``nnkElse`` parts may be missing.
 
@@ -622,14 +719,16 @@ While statement
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   while expr1:
     stmt1
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkWhileStmt(expr1, stmt1)
+  ```
 
 
 For statement
@@ -637,14 +736,16 @@ For statement
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   for ident1, ident2 in expr1:
     stmt1
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkForStmt(ident1, ident2, expr1, stmt1)
+  ```
 
 
 Try statement
@@ -652,7 +753,7 @@ Try statement
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   try:
     stmt1
   except e1, e2:
@@ -663,10 +764,11 @@ Concrete syntax:
     stmt4
   finally:
     stmt5
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkTryStmt(
     stmt1,
     nnkExceptBranch(e1, e2, stmt2),
@@ -674,6 +776,7 @@ AST:
     nnkExceptBranch(stmt4),
     nnkFinally(stmt5)
   )
+  ```
 
 
 Return statement
@@ -681,13 +784,15 @@ Return statement
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   return expr1
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkReturnStmt(expr1)
+  ```
 
 
 Yield statement
@@ -695,8 +800,9 @@ Yield statement
 
 Like ``return``, but with ``nnkYieldStmt`` kind.
 
-.. code-block:: nim
+  ```nim
   nnkYieldStmt(expr1)
+  ```
 
 
 Discard statement
@@ -704,8 +810,9 @@ Discard statement
 
 Like ``return``, but with ``nnkDiscardStmt`` kind.
 
-.. code-block:: nim
+  ```nim
   nnkDiscardStmt(expr1)
+  ```
 
 
 Continue statement
@@ -713,26 +820,30 @@ Continue statement
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   continue
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkContinueStmt()
+  ```
 
 Break statement
 ---------------
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   break otherLocation
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkBreakStmt(nnkIdent("otherLocation"))
+  ```
 
 If ``break`` is used without a jump-to location, ``nnkEmpty`` replaces ``nnkIdent``.
 
@@ -741,13 +852,15 @@ Block statement
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   block name:
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkBlockStmt(nnkIdent("name"), nnkStmtList(...))
+  ```
 
 A ``block`` doesn't need an name, in which case ``nnkEmpty`` is used.
 
@@ -756,18 +869,20 @@ Asm statement
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   asm """
     some asm
   """
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkAsmStmt(
     nnkEmpty(), # for pragmas
     nnkTripleStrLit("some asm"),
   )
+  ```
 
 Import section
 --------------
@@ -777,37 +892,42 @@ on what keywords are present. Let's start with the simplest form.
 
 Concrete syntax:
 
-.. code-block:: nim
-  import math
+  ```nim
+  import std/math
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkImportStmt(nnkIdent("math"))
+  ```
 
 With ``except``, we get ``nnkImportExceptStmt``.
 
 Concrete syntax:
 
-.. code-block:: nim
-  import math except pow
+  ```nim
+  import std/math except pow
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkImportExceptStmt(nnkIdent("math"),nnkIdent("pow"))
+  ```
 
-Note that ``import math as m`` does not use a different node; rather,
+Note that ``import std/math as m`` does not use a different node; rather,
 we use ``nnkImportStmt`` with ``as`` as an infix operator.
 
 Concrete syntax:
 
-.. code-block:: nim
-  import strutils as su
+  ```nim
+  import std/strutils as su
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkImportStmt(
     nnkInfix(
       nnkIdent("as"),
@@ -815,6 +935,7 @@ AST:
       nnkIdent("su")
     )
   )
+  ```
 
 From statement
 --------------
@@ -823,15 +944,17 @@ If we use ``from ... import``, the result is different, too.
 
 Concrete syntax:
 
-.. code-block:: nim
-  from math import pow
+  ```nim
+  from std/math import pow
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkFromStmt(nnkIdent("math"), nnkIdent("pow"))
+  ```
 
-Using ``from math as m import pow`` works identically to the ``as`` modifier
+Using ``from std/math as m import pow`` works identically to the ``as`` modifier
 with the ``import`` statement, but wrapped in ``nnkFromStmt``.
 
 Export statement
@@ -842,26 +965,30 @@ the ``export`` syntax is pretty straightforward.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   export unsigned
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkExportStmt(nnkIdent("unsigned"))
+  ```
 
 Similar to the ``import`` statement, the AST is different for
 ``export ... except``.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   export math except pow # we're going to implement our own exponentiation
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkExportExceptStmt(nnkIdent("math"),nnkIdent("pow"))
+  ```
 
 Include statement
 -----------------
@@ -870,25 +997,28 @@ Like a plain ``import`` statement but with ``nnkIncludeStmt``.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   include blocks
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkIncludeStmt(nnkIdent("blocks"))
+  ```
 
 Var section
 -----------
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   var a = 3
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkVarSection(
     nnkIdentDefs(
       nnkIdent("a"),
@@ -896,13 +1026,14 @@ AST:
       nnkIntLit(3),
     )
   )
+  ```
 
 Note that either the second or third (or both) parameters above must exist,
 as the compiler needs to know the type somehow (which it can infer from
 the given assignment).
 
 This is not the same AST for all uses of ``var``. See
-[Procedure declaration](http://nim-lang.org/docs/macros.html#statements-procedure-declaration)
+[Procedure declaration](macros.html#statements-procedure-declaration)
 for details.
 
 Let section
@@ -913,12 +1044,13 @@ This is equivalent to ``var``, but with ``nnkLetSection`` rather than
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   let a = 3
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkLetSection(
     nnkIdentDefs(
       nnkIdent("a"),
@@ -926,18 +1058,20 @@ AST:
       nnkIntLit(3),
     )
   )
+  ```
 
 Const section
 -------------
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   const a = 3
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkConstSection(
     nnkConstDef( # not nnkConstDefs!
       nnkIdent("a"),
@@ -945,6 +1079,7 @@ AST:
       nnkIntLit(3), # required in a const declaration!
     )
   )
+  ```
 
 Type section
 ------------
@@ -954,12 +1089,13 @@ and ``const``.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   type A = int
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkTypeSection(
     nnkTypeDef(
       nnkIdent("A"),
@@ -967,18 +1103,20 @@ AST:
       nnkIdent("int")
     )
   )
+  ```
 
 Declaring ``distinct`` types is similar, with the last ``nnkIdent`` wrapped
 in ``nnkDistinctTy``.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   type MyInt = distinct int
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   # ...
   nnkTypeDef(
     nnkIdent("MyInt"),
@@ -987,17 +1125,19 @@ AST:
       nnkIdent("int")
     )
   )
+  ```
 
 If a type section uses generic parameters, they are treated here:
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   type A[T] = expr1
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkTypeSection(
     nnkTypeDef(
       nnkIdent("A"),
@@ -1012,19 +1152,21 @@ AST:
       expr1,
     )
   )
+  ```
 
 Note that not all ``nnkTypeDef`` utilize ``nnkIdent`` as their
-their parameter. One of the most common uses of type declarations
+parameter. One of the most common uses of type declarations
 is to work with objects.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   type IO = object of RootObj
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   # ...
   nnkTypeDef(
     nnkIdent("IO"),
@@ -1033,33 +1175,43 @@ AST:
       nnkEmpty(), # no pragmas here
       nnkOfInherit(
         nnkIdent("RootObj") # inherits from RootObj
-      )
+      ),
       nnkEmpty()
     )
   )
+  ```
 
 Nim's object syntax is rich. Let's take a look at an involved example in
 its entirety to see some of the complexities.
 
 Concrete syntax:
 
-.. code-block:: nim
-  type Obj[T] = object {.inheritable.}
+  ```nim
+  type Obj[T] {.inheritable.} = object
     name: string
     case isFat: bool
     of true:
       m: array[100_000, T]
     of false:
       m: array[10, T]
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   # ...
+  nnkPragmaExpr(
+    nnkIdent("Obj"),
+    nnkPragma(nnkIdent("inheritable"))
+  ),
+  nnkGenericParams(
+  nnkIdentDefs(
+    nnkIdent("T"),
+    nnkEmpty(),
+    nnkEmpty())
+  ),
   nnkObjectTy(
-    nnkPragma(
-      nnkIdent("inheritable")
-    ),
+    nnkEmpty(),
     nnkEmpty(),
     nnkRecList( # list of object parameters
       nnkIdentDefs(
@@ -1103,55 +1255,61 @@ AST:
       )
     )
   )
+  ```
 
 
 Using an ``enum`` is similar to using an ``object``.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   type X = enum
     First
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   # ...
   nnkEnumTy(
     nnkEmpty(),
     nnkIdent("First") # you need at least one nnkIdent or the compiler complains
   )
+  ```
 
 The usage of ``concept`` (experimental) is similar to objects.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   type Con = concept x,y,z
     (x & y & z) is string
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   # ...
   nnkTypeClassTy( # note this isn't nnkConceptTy!
-    nnkArglist(
+    nnkArgList(
       # ... idents for x, y, z
     )
     # ...
   )
+  ```
 
 Static types, like ``static[int]``, use ``nnkIdent`` wrapped in
 ``nnkStaticTy``.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   type A[T: static[int]] = object
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   # ... within nnkGenericParams
   nnkIdentDefs(
     nnkIdent("T"),
@@ -1161,13 +1319,14 @@ AST:
     nnkEmpty()
   )
   # ...
+  ```
 
 In general, declaring types mirrors this syntax (i.e., ``nnkStaticTy`` for
 ``static``, etc.). Examples follow (exceptions marked by ``*``):
 
--------------                ---------------------------------------------
+=============                =============================================
 Nim type                     Corresponding AST
--------------                ---------------------------------------------
+=============                =============================================
 ``static``                   ``nnkStaticTy``
 ``tuple``                    ``nnkTupleTy``
 ``var``                      ``nnkVarTy``
@@ -1180,7 +1339,7 @@ Nim type                     Corresponding AST
 ``proc``                     ``nnkProcTy``
 ``iterator``                 ``nnkIteratorTy``
 ``object``                   ``nnkObjectTy``
--------------                ---------------------------------------------
+=============                =============================================
 
 Take special care when declaring types as ``proc``. The behavior is similar
 to ``Procedure declaration``, below, but does not treat ``nnkGenericParams``.
@@ -1188,12 +1347,13 @@ Generic parameters are treated in the type, not the ``proc`` itself.
 
 Concrete syntax:
 
-.. code-block:: nim
-  type MyProc[T] = proc(x: T)
+  ```nim
+  type MyProc[T] = proc(x: T) {.nimcall.}
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   # ...
   nnkTypeDef(
     nnkIdent("MyProc"),
@@ -1203,38 +1363,75 @@ AST:
     nnkProcTy( # behaves like a procedure declaration from here on
       nnkFormalParams(
         # ...
-      )
+      ),
+      nnkPragma(nnkIdent("nimcall"))
     )
   )
+  ```
 
 The same syntax applies to ``iterator`` (with ``nnkIteratorTy``), but
 *does not* apply to ``converter`` or ``template``.
 
+Type class versions of these nodes generally share the same node kind but
+without any child nodes. The ``tuple`` type class is represented by
+``nnkTupleClassTy``, while a ``proc`` or ``iterator`` type class with pragmas
+has an ``nnkEmpty`` node in place of the ``nnkFormalParams`` node of a
+concrete ``proc`` or ``iterator`` type node.
+
+  ```nim
+  type TypeClass = proc {.nimcall.} | ref | tuple
+  ```
+
+AST:
+
+  ```nim
+  nnkTypeDef(
+    nnkIdent("TypeClass"),
+    nnkEmpty(),
+    nnkInfix(
+      nnkIdent("|"),
+      nnkProcTy(
+        nnkEmpty(),
+        nnkPragma(nnkIdent("nimcall"))
+      ),
+      nnkInfix(
+        nnkIdent("|"),
+        nnkRefTy(),
+        nnkTupleClassTy()
+      )
+    )
+  )
+  ```
+
 Mixin statement
 ---------------
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   mixin x
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkMixinStmt(nnkIdent("x"))
+  ```
 
 Bind statement
 --------------
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   bind x
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkBindStmt(nnkIdent("x"))
+  ```
 
 Procedure declaration
 ---------------------
@@ -1244,18 +1441,21 @@ a feel for how procedure calls are broken down.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   proc hello*[T: SomeInteger](x: int = 3, y: float32): int {.inline.} = discard
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkProcDef(
     nnkPostfix(nnkIdent("*"), nnkIdent("hello")), # the exported proc name
     nnkEmpty(), # patterns for term rewriting in templates and macros (not procs)
     nnkGenericParams( # generic type parameters, like with type declaration
       nnkIdentDefs(
-        nnkIdent("T"), nnkIdent("SomeInteger")
+        nnkIdent("T"),
+        nnkIdent("SomeInteger"),
+        nnkEmpty()
       )
     ),
     nnkFormalParams(
@@ -1270,11 +1470,12 @@ AST:
         nnkIdent("float32"),
         nnkEmpty()
       )
-      nnkPragma(nnkIdent("inline")),
-      nnkEmpty(), # reserved slot for future use
-      nnkStmtList(nnkDiscardStmt(nnkEmpty())) # the meat of the proc
-    )
+    ),
+    nnkPragma(nnkIdent("inline")),
+    nnkEmpty(), # reserved slot for future use
+    nnkStmtList(nnkDiscardStmt(nnkEmpty())) # the meat of the proc
   )
+  ```
 
 There is another consideration. Nim has flexible type identification for
 its procs. Even though ``proc(a: int, b: int)`` and ``proc(a, b: int)``
@@ -1282,12 +1483,13 @@ are equivalent in the code, the AST is a little different for the latter.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   proc(a, b: int)
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   # ...AST as above...
   nnkFormalParams(
     nnkEmpty(), # no return here
@@ -1299,24 +1501,27 @@ AST:
     )
   ),
   # ...
+  ```
 
 When a procedure uses the special ``var`` type return variable, the result
 is different from that of a var section.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   proc hello(): var int
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   # ...
   nnkFormalParams(
     nnkVarTy(
       nnkIdent("int")
     )
   )
+  ```
 
 Iterator declaration
 --------------------
@@ -1326,17 +1531,19 @@ replacing ``nnkProcDef``.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   iterator nonsense[T](x: seq[T]): float {.closure.} = ...
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkIteratorDef(
     nnkIdent("nonsense"),
     nnkEmpty(),
     ...
   )
+  ```
 
 Converter declaration
 ---------------------
@@ -1345,34 +1552,37 @@ A converter is similar to a proc.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   converter toBool(x: float): bool
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkConverterDef(
     nnkIdent("toBool"),
     # ...
   )
+  ```
 
 Template declaration
 --------------------
 
 Templates (as well as macros, as we'll see) have a slightly expanded AST when
 compared to procs and iterators. The reason for this is [term-rewriting
-macros](http://nim-lang.org/docs/manual.html#term-rewriting-macros). Notice
+macros](manual.html#term-rewriting-macros). Notice
 the ``nnkEmpty()`` as the second argument to ``nnkProcDef`` and
 ``nnkIteratorDef`` above? That's where the term-rewriting macros go.
 
 Concrete syntax:
 
-.. code-block:: nim
+  ```nim
   template optOpt{expr1}(a: int): int
+  ```
 
 AST:
 
-.. code-block:: nim
+  ```nim
   nnkTemplateDef(
     nnkIdent("optOpt"),
     nnkStmtList( # instead of nnkEmpty()
@@ -1380,6 +1590,7 @@ AST:
     ),
     # follows like a proc or iterator
   )
+  ```
 
 If the template does not have types for its parameters, the type identifiers
 inside ``nnkFormalParams`` just becomes ``nnkEmpty``.
@@ -1390,6 +1601,18 @@ Macro declaration
 Macros behave like templates, but ``nnkTemplateDef`` is replaced with
 ``nnkMacroDef``.
 
+Hidden Standard Conversion
+--------------------------
+
+  ```nim
+  var f: float = 1
+  ```
+
+The type of "f" is ``float`` but the type of "1" is actually ``int``. Inserting 
+``int`` into a ``float`` is a type error. Nim inserts the ``nnkHiddenStdConv``  
+node around the ``nnkIntLit`` node so that the new node has the correct type of 
+``float``. This works for any auto converted nodes and makes the conversion 
+explicit.
 
 Special node kinds
 ==================
diff --git a/doc/backends.md b/doc/backends.md
new file mode 100644
index 000000000..9f0c54835
--- /dev/null
+++ b/doc/backends.md
@@ -0,0 +1,406 @@
+================================
+   Nim Backend Integration
+================================
+
+:Author: Puppet Master
+:Version: |nimversion|
+
+.. default-role:: code
+.. include:: rstcommon.rst
+.. no syntax highlighting here by default:
+
+.. contents::
+
+> "Heresy grows from idleness." -- Unknown.
+
+
+Introduction
+============
+
+The [Nim Compiler User Guide](nimc.html) documents the typical
+compiler invocation, using the `compile`:option:
+or `c`:option: command to transform a
+``.nim`` file into one or more ``.c`` files which are then compiled with the
+platform's C compiler into a static binary. However, there are other commands
+to compile to C++, Objective-C, or JavaScript. This document tries to
+concentrate in a single place all the backend and interfacing options.
+
+The Nim compiler supports mainly two backend families: the C, C++ and
+Objective-C targets and the JavaScript target. [The C like targets](
+#backends-the-c-like-targets) creates source files that can be compiled
+into a library or a final executable. [The JavaScript target](
+#backends-the-javascript-target) can generate a ``.js`` file which you
+reference from an HTML file or create a [standalone Node.js program](
+http://nodejs.org).
+
+On top of generating libraries or standalone applications, Nim offers
+bidirectional interfacing with the backend targets through generic and
+specific pragmas.
+
+
+Backends
+========
+
+The C like targets
+------------------
+
+The commands to compile to either C, C++ or Objective-C are:
+
+//compileToC, cc          compile project with C code generator
+//compileToCpp, cpp       compile project to C++ code
+//compileToOC, objc       compile project to Objective C code
+
+The most significant difference between these commands is that if you look
+into the ``nimcache`` directory you will find ``.c``, ``.cpp`` or ``.m``
+files, other than that all of them will produce a native binary for your
+project. This allows you to take the generated code and place it directly
+into a project using any of these languages. Here are some typical command-
+line invocations:
+
+  ```cmd
+  nim c hallo.nim
+  nim cpp hallo.nim
+  nim objc hallo.nim
+  ```
+
+The compiler commands select the target backend, but if needed you can
+[specify additional switches for cross-compilation](
+nimc.html#crossminuscompilation) to select the target CPU, operative system
+or compiler/linker commands.
+
+
+The JavaScript target
+---------------------
+
+Nim can also generate `JavaScript`:idx: code through the `js`:option: command.
+
+Nim targets JavaScript 1.5 which is supported by any widely used browser.
+Since JavaScript does not have a portable means to include another module,
+Nim just generates a long ``.js`` file.
+
+Features or modules that the JavaScript platform does not support are not
+available. This includes:
+
+* manual memory management (`alloc`, etc.)
+* casting and other unsafe operations (`cast` operator, `zeroMem`, etc.)
+* file management
+* OS-specific operations
+* threading, coroutines
+* some modules of the standard library
+* proper 64-bit integer arithmetic
+
+To compensate, the standard library has modules [catered to the JS backend](
+lib.html#pure-libraries-modules-for-js-backend)
+and more support will come in the future (for instance, Node.js bindings
+to get OS info).
+
+To compile a Nim module into a ``.js`` file use the `js`:option: command; the
+default is a ``.js`` file that is supposed to be referenced in an ``.html``
+file. However, you can also run the code with `nodejs`:idx:
+(http://nodejs.org):
+
+  ```cmd
+  nim js -d:nodejs -r examples/hallo.nim
+  ```
+
+If you experience errors saying that `globalThis` is not defined, be
+sure to run a recent version of Node.js (at least 12.0).
+
+
+Interfacing
+===========
+
+Nim offers bidirectional interfacing with the target backend. This means
+that you can call backend code from Nim and Nim code can be called by
+the backend code. Usually the direction of which calls which depends on your
+software architecture (is Nim your main program or is Nim providing a
+component?).
+
+
+Nim code calling the backend
+----------------------------
+
+Nim code can interface with the backend through the [Foreign function
+interface](manual.html#foreign-function-interface) mainly through the
+[importc pragma](manual.html#foreign-function-interface-importc-pragma).
+The `importc` pragma is the *generic* way of making backend symbols available
+in Nim and is available in all the target backends (JavaScript too). The C++
+or Objective-C backends have their respective [ImportCpp](
+manual.html#implementation-specific-pragmas-importcpp-pragma) and
+[ImportObjC](manual.html#implementation-specific-pragmas-importobjc-pragma)
+pragmas to call methods from classes.
+
+Whenever you use any of these pragmas you need to integrate native code into
+your final binary. In the case of JavaScript this is no problem at all, the
+same HTML file which hosts the generated JavaScript will likely provide other
+JavaScript functions which you are importing with `importc`.
+
+However, for the C like targets you need to link external code either
+statically or dynamically. The preferred way of integrating native code is to
+use dynamic linking because it allows you to compile Nim programs without
+the need for having the related development libraries installed. This is done
+through the [dynlib pragma for import](
+manual.html#foreign-function-interface-dynlib-pragma-for-import), though
+more specific control can be gained using the [dynlib module](dynlib.html).
+
+The [dynlibOverride](nimc.html#dynliboverride) command line switch allows
+to avoid dynamic linking if you need to statically link something instead.
+Nim wrappers designed to statically link source files can use the [compile
+pragma](manual.html#implementation-specific-pragmas-compile-pragma) if
+there are few sources or providing them along the Nim code is easier than using
+a system library. Libraries installed on the host system can be linked in with
+the [PassL pragma](manual.html#implementation-specific-pragmas-passl-pragma).
+
+To wrap native code, take a look at the [c2nim tool](
+https://github.com/nim-lang/c2nim/blob/master/doc/c2nim.rst) which helps
+with the process of scanning and transforming header files into a Nim
+interface.
+
+### C invocation example
+
+Create a ``logic.c`` file with the following content:
+
+  ```c
+  int addTwoIntegers(int a, int b)
+  {
+    return a + b;
+  }
+  ```
+
+Create a ``calculator.nim`` file with the following content:
+
+  ```nim
+  {.compile: "logic.c".}
+  proc addTwoIntegers(a, b: cint): cint {.importc.}
+
+  when isMainModule:
+    echo addTwoIntegers(3, 7)
+  ```
+
+With these two files in place, you can run `nim c -r calculator.nim`:cmd: and
+the Nim compiler will compile the ``logic.c`` file in addition to
+``calculator.nim`` and link both into an executable, which outputs `10` when
+run. Another way to link the C file statically and get the same effect would
+be to remove the line with the `compile` pragma and run the following
+typical Unix commands:
+
+  ```cmd
+  gcc -c logic.c
+  ar rvs mylib.a logic.o
+  nim c --passL:mylib.a -r calculator.nim
+  ```
+
+Just like in this example we pass the path to the ``mylib.a`` library (and we
+could as well pass ``logic.o``) we could be passing switches to link any other
+static C library.
+
+
+### JavaScript invocation example
+
+Create a ``host.html`` file with the following content:
+
+  ```
+  <html><body>
+  <script type="text/javascript">
+  function addTwoIntegers(a, b)
+  {
+    return a + b;
+  }
+  </script>
+  <script type="text/javascript" src="calculator.js"></script>
+  </body></html>
+  ```
+
+Create a ``calculator.nim`` file with the following content (or reuse the one
+from the previous section):
+
+  ```nim
+  proc addTwoIntegers(a, b: int): int {.importc.}
+
+  when isMainModule:
+    echo addTwoIntegers(3, 7)
+  ```
+
+Compile the Nim code to JavaScript with `nim js -o:calculator.js
+calculator.nim`:cmd: and open ``host.html`` in a browser. If the browser supports
+javascript, you should see the value `10` in the browser's console. Use the
+[dom module](dom.html) for specific DOM querying and modification procs
+or take a look at [karax](https://github.com/pragmagic/karax) for how to
+develop browser-based applications.
+
+
+Backend code calling Nim
+------------------------
+
+Backend code can interface with Nim code exposed through the [exportc
+pragma](manual.html#foreign-function-interface-exportc-pragma). The
+`exportc` pragma is the *generic* way of making Nim symbols available to
+the backends. By default, the Nim compiler will mangle all the Nim symbols to
+avoid any name collision, so the most significant thing the `exportc` pragma
+does is maintain the Nim symbol name, or if specified, use an alternative
+symbol for the backend in case the symbol rules don't match.
+
+The JavaScript target doesn't have any further interfacing considerations
+since it also has garbage collection, but the C targets require you to
+initialize Nim's internals, which is done calling a `NimMain` function.
+Also, C code requires you to specify a forward declaration for functions or
+the compiler will assume certain types for the return value and parameters
+which will likely make your program crash at runtime.
+
+The name `NimMain` can be influenced via the `--nimMainPrefix:prefix` switch.
+Use `--nimMainPrefix:MyLib` and the function to call is named `MyLibNimMain`.
+
+When compiling to static or dynamic libraries, they don't call destructors of global variables as normal Nim programs would do. A C API `NimDestroyGlobals` is provided to call these global destructors.
+
+
+### Nim invocation example from C
+
+Create a ``fib.nim`` file with the following content:
+
+  ```nim
+  proc fib(a: cint): cint {.exportc.} =
+    if a <= 2:
+      result = 1
+    else:
+      result = fib(a - 1) + fib(a - 2)
+  ```
+
+Create a ``maths.c`` file with the following content:
+
+  ```c
+  #include <stdio.h>
+
+  int fib(int a);
+  void NimMain();
+
+  int main(void)
+  {
+    NimMain();
+    for (int f = 0; f < 10; f++)
+      printf("Fib of %d is %d\n", f, fib(f));
+    return 0;
+  }
+  ```
+
+Now you can run the following Unix like commands to first generate C sources
+from the Nim code, then link them into a static binary along your main C
+program:
+
+  ```cmd
+  nim c --noMain --noLinking fib.nim
+  gcc -o m -I$HOME/.cache/nim/fib_d -Ipath/to/nim/lib $HOME/.cache/nim/fib_d/*.c maths.c
+  ```
+
+The first command runs the Nim compiler with three special options to avoid
+generating a `main()`:c: function in the generated files and to avoid linking the
+object files into a final binary. All the generated files are placed into the ``nimcache``
+directory. That's why the next command compiles the ``maths.c`` source plus
+all the ``.c`` files from ``nimcache``. In addition to this path, you also
+have to tell the C compiler where to find Nim's ``nimbase.h`` header file.
+
+Instead of depending on the generation of the individual ``.c`` files you can
+also ask the Nim compiler to generate a statically linked library:
+
+  ```cmd
+  nim c --app:staticLib fib.nim
+  gcc -o m -Inimcache -Ipath/to/nim/lib maths.c libfib.nim.a
+  ```
+
+The Nim compiler will handle linking the source files generated in the
+``nimcache`` directory into the ``libfib.nim.a`` static library, which you can
+then link into your C program. Note that these commands are generic and will
+vary for each system. For instance, on Linux systems you will likely need to
+use `-ldl`:option: too to link in required dlopen functionality.
+
+
+### Nim invocation example from JavaScript
+
+Create a ``mhost.html`` file with the following content:
+
+  ```
+  <html><body>
+  <script type="text/javascript" src="fib.js"></script>
+  <script type="text/javascript">
+  alert("Fib for 9 is " + fib(9));
+  </script>
+  </body></html>
+  ```
+
+Create a ``fib.nim`` file with the following content (or reuse the one
+from the previous section):
+
+  ```nim
+  proc fib(a: cint): cint {.exportc.} =
+    if a <= 2:
+      result = 1
+    else:
+      result = fib(a - 1) + fib(a - 2)
+  ```
+
+Compile the Nim code to JavaScript with `nim js -o:fib.js fib.nim`:cmd: and
+open ``mhost.html`` in a browser. If the browser supports javascript, you
+should see an alert box displaying the text ``Fib for 9 is 34``. As mentioned
+earlier, JavaScript doesn't require an initialization call to `NimMain` or
+a similar function and you can call the exported Nim proc directly.
+
+
+Nimcache naming logic
+---------------------
+
+The `nimcache`:idx: directory is generated during compilation and will hold
+either temporary or final files depending on your backend target. The default
+name for the directory depends on the used backend and on your OS but you can
+use the `--nimcache`:option: [compiler switch](
+nimc.html#compiler-usage-commandminusline-switches) to change it.
+
+
+Memory management
+=================
+
+In the previous sections, the `NimMain()` function reared its head. Since
+JavaScript already provides automatic memory management, you can freely pass
+objects between the two languages without problems. In C and derivate languages
+you need to be careful about what you do and how you share memory. The
+previous examples only dealt with simple scalar values, but passing a Nim
+string to C, or reading back a C string in Nim already requires you to be
+aware of who controls what to avoid crashing.
+
+
+Strings and C strings
+---------------------
+
+The manual mentions that [Nim strings are implicitly convertible to
+cstrings](manual.html#types-cstring-type) which makes interaction usually
+painless. Most C functions accepting a Nim string converted to a
+`cstring` will likely not need to keep this string around and by the time
+they return the string won't be needed anymore.
+
+A similar thing happens with C code invoking Nim code which returns a
+`cstring`. Consider the following proc:
+
+  ```nim
+  proc gimme(): cstring {.exportc.} =
+    result = "Hey there C code! " & $rand(100)
+  ```
+
+Since Nim's reference counting mechanism is not aware of the C code, once the
+`gimme` proc has finished it can reclaim the memory of the `cstring`.
+
+
+Custom data types
+-----------------
+
+Just like strings, custom data types that are to be shared between Nim and
+the backend will need careful consideration of who controls who. If you want
+to hand a Nim reference to C code, you will need to use [GC_ref](
+system.html#GC_ref,ref.T) to mark the reference as used, so it does not get
+freed. And for the C backend you will need to expose the [GC_unref](
+system.html#GC_unref,ref.T) proc to clean up this memory when it is not
+required anymore.
+
+Again, if you are wrapping a library which *mallocs* and *frees* data
+structures, you need to expose the appropriate *free* function to Nim so
+you can clean it up. And of course, once cleaned you should avoid accessing it
+from Nim (or C for that matter). Typically C data structures have their own
+`malloc_structure`:c: and `free_structure`:c: specific functions, so wrapping
+these for the Nim side should be enough.
diff --git a/doc/backends.txt b/doc/backends.txt
deleted file mode 100644
index b7f5308ab..000000000
--- a/doc/backends.txt
+++ /dev/null
@@ -1,474 +0,0 @@
-================================
-   Nim Backend Integration
-================================
-
-:Author: Puppet Master
-:Version: |nimversion|
-
-.. contents::
-  "Heresy grows from idleness." -- Unknown.
-
-
-Introduction
-============
-
-The `Nim Compiler User Guide <nimc.html>`_ documents the typical
-compiler invocation, using the ``compile`` or ``c`` command to transform a
-``.nim`` file into one or more ``.c`` files which are then compiled with the
-platform's C compiler into a static binary. However there are other commands
-to compile to C++, Objective-C or JavaScript. This document tries to
-concentrate in a single place all the backend and interfacing options.
-
-The Nim compiler supports mainly two backend families: the C, C++ and
-Objective-C targets and the JavaScript target. `The C like targets
-<#backends-the-c-like-targets>`_ creates source files which can be compiled
-into a library or a final executable. `The JavaScript target
-<#backends-the-javascript-target>`_ can generate a ``.js`` file which you
-reference from an HTML file or create a `standalone nodejs program
-<http://nodejs.org>`_.
-
-On top of generating libraries or standalone applications, Nim offers
-bidirectional interfacing with the backend targets through generic and
-specific pragmas.
-
-
-Backends
-========
-
-The C like targets
-------------------
-
-The commands to compile to either C, C++ or Objective-C are:
-
-  //compileToC, cc          compile project with C code generator
-  //compileToCpp, cpp       compile project to C++ code
-  //compileToOC, objc       compile project to Objective C code
-
-The most significant difference between these commands is that if you look
-into the ``nimcache`` directory you will find ``.c``, ``.cpp`` or ``.m``
-files, other than that all of them will produce a native binary for your
-project.  This allows you to take the generated code and place it directly
-into a project using any of these languages. Here are some typical command
-line invocations::
-
-    $ nim c hallo.nim
-    $ nim cpp hallo.nim
-    $ nim objc hallo.nim
-
-The compiler commands select the target backend, but if needed you can
-`specify additional switches for cross compilation
-<nimc.html#cross-compilation>`_ to select the target CPU, operative system
-or compiler/linker commands.
-
-
-The JavaScript target
----------------------
-
-Nim can also generate `JavaScript`:idx: code through the ``js`` command.
-However, the JavaScript code generator is experimental!
-
-Nim targets JavaScript 1.5 which is supported by any widely used browser.
-Since JavaScript does not have a portable means to include another module,
-Nim just generates a long ``.js`` file.
-
-Features or modules that the JavaScript platform does not support are not
-available. This includes:
-
-* manual memory management (``alloc``, etc.)
-* casting and other unsafe operations (``cast`` operator, ``zeroMem``, etc.)
-* file management
-* most modules of the Standard library
-* proper 64 bit integer arithmetic
-* unsigned integer arithmetic
-
-However, the modules `strutils <strutils.html>`_, `math <math.html>`_, and
-`times <times.html>`_ are available! To access the DOM, use the `dom
-<dom.html>`_ module that is only available for the JavaScript platform.
-
-To compile a Nim module into a ``.js`` file use the ``js`` command; the
-default is a ``.js`` file that is supposed to be referenced in an ``.html``
-file. However, you can also run the code with `nodejs`:idx:, a `software
-platform for easily building fast, scalable network applications
-<http://nodejs.org>`_::
-
-  nim js -d:nodejs -r examples/hallo.nim
-
-
-Interfacing
-===========
-
-Nim offers bidirectional interfacing with the target backend. This means
-that you can call backend code from Nim and Nim code can be called by
-the backend code. Usually the direction of which calls which depends on your
-software architecture (is Nim your main program or is Nim providing a
-component?).
-
-
-Nim code calling the backend
-----------------------------
-
-Nim code can interface with the backend through the `Foreign function
-interface <manual.html#foreign-function-interface>`_ mainly through the
-`importc pragma <manual.html#importc-pragma>`_. The ``importc`` pragma is the
-*generic* way of making backend symbols available in Nim and is available
-in all the target backends (JavaScript too).  The C++ or Objective-C backends
-have their respective `ImportCpp <manual.html#implementation-specific-pragmas-importcpp-pragma>`_ and
-`ImportObjC <manual.html#implementation-specific-pragmas-importobjc-pragma>`_ pragmas to call methods from
-classes.
-
-Whenever you use any of these pragmas you need to integrate native code into
-your final binary. In the case of JavaScript this is no problem at all, the
-same html file which hosts the generated JavaScript will likely provide other
-JavaScript functions which you are importing with ``importc``.
-
-However, for the C like targets you need to link external code either
-statically or dynamically. The preferred way of integrating native code is to
-use dynamic linking because it allows you to compile Nim programs without
-the need for having the related development libraries installed. This is done
-through the `dynlib pragma for import
-<manual.html#dynlib-pragma-for-import>`_, though more specific control can be
-gained using the `dynlib module <dynlib.html>`_.
-
-The `dynlibOverride <nimc.html#dynliboverride>`_ command line switch allows
-to avoid dynamic linking if you need to statically link something instead.
-Nim wrappers designed to statically link source files can use the `compile
-pragma <nimc.html#compile-pragma>`_ if there are few sources or providing
-them along the Nim code is easier than using a system library. Libraries
-installed on the host system can be linked in with the `PassL pragma
-<nimc.html#passl-pragma>`_.
-
-To wrap native code, take a look at the `c2nim tool <c2nim.html>`_ which helps
-with the process of scanning and transforming header files into a Nim
-interface.
-
-C invocation example
-~~~~~~~~~~~~~~~~~~~~
-
-Create a ``logic.c`` file with the following content:
-
-.. code-block:: c
-  int addTwoIntegers(int a, int b)
-  {
-    return a + b;
-  }
-
-Create a ``calculator.nim`` file with the following content:
-
-.. code-block:: nim
-
-  {.compile: "logic.c".}
-  proc addTwoIntegers(a, b: cint): cint {.importc.}
-
-  when isMainModule:
-    echo addTwoIntegers(3, 7)
-
-With these two files in place, you can run ``nim c -r calculator.nim`` and
-the Nim compiler will compile the ``logic.c`` file in addition to
-``calculator.nim`` and link both into an executable, which outputs ``10`` when
-run. Another way to link the C file statically and get the same effect would
-be remove the line with the ``compile`` pragma and run the following typical
-Unix commands::
-
-    $ gcc -c logic.c
-    $ ar rvs mylib.a logic.o
-    $ nim c --passL:mylib.a -r calculator.nim
-
-Just like in this example we pass the path to the ``mylib.a`` library (and we
-could as well pass ``logic.o``) we could be passing switches to link any other
-static C library.
-
-
-JavaScript invocation example
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Create a ``host.html`` file with the following content:
-
-.. code-block::
-
-  <html><body>
-  <script type="text/javascript">
-  function addTwoIntegers(a, b)
-  {
-    return a + b;
-  }
-  </script>
-  <script type="text/javascript" src="calculator.js"></script>
-  </body></html>
-
-Create a ``calculator.nim`` file with the following content (or reuse the one
-from the previous section):
-
-.. code-block:: nim
-
-  proc addTwoIntegers(a, b: int): int {.importc.}
-
-  when isMainModule:
-    echo addTwoIntegers(3, 7)
-
-Compile the Nim code to JavaScript with ``nim js -o:calculator.js
-calculator.nim`` and open ``host.html`` in a browser. If the browser supports
-javascript, you should see the value ``10`` in the browser's console. Use the
-`dom module <dom.html>`_ for specific DOM querying and modification procs
-or take a look at `karax <https://github.com/pragmagic/karax>`_ for how to
-develop browser based applications.
-
-
-Backend code calling Nim
-------------------------
-
-Backend code can interface with Nim code exposed through the `exportc
-pragma <manual.html#exportc-pragma>`_. The ``exportc`` pragma is the *generic*
-way of making Nim symbols available to the backends. By default the Nim
-compiler will mangle all the Nim symbols to avoid any name collision, so
-the most significant thing the ``exportc`` pragma does is maintain the Nim
-symbol name, or if specified, use an alternative symbol for the backend in
-case the symbol rules don't match.
-
-The JavaScript target doesn't have any further interfacing considerations
-since it also has garbage collection, but the C targets require you to
-initialize Nim's internals, which is done calling a ``NimMain`` function.
-Also, C code requires you to specify a forward declaration for functions or
-the compiler will assume certain types for the return value and parameters
-which will likely make your program crash at runtime.
-
-The Nim compiler can generate a C interface header through the ``--header``
-command line switch. The generated header will contain all the exported
-symbols and the ``NimMain`` proc which you need to call before any other
-Nim code.
-
-
-Nim invocation example from C
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Create a ``fib.nim`` file with the following content:
-
-.. code-block:: nim
-
-  proc fib(a: cint): cint {.exportc.} =
-    if a <= 2:
-      result = 1
-    else:
-      result = fib(a - 1) + fib(a - 2)
-
-Create a ``maths.c`` file with the following content:
-
-.. code-block:: c
-
-  #include "fib.h"
-  #include <stdio.h>
-
-  int main(void)
-  {
-    NimMain();
-    for (int f = 0; f < 10; f++)
-      printf("Fib of %d is %d\n", f, fib(f));
-    return 0;
-  }
-
-Now you can run the following Unix like commands to first generate C sources
-form the Nim code, then link them into a static binary along your main C
-program::
-
-  $ nim c --noMain --noLinking --header:fib.h fib.nim
-  $ gcc -o m -Inimcache -Ipath/to/nim/lib nimcache/*.c maths.c
-
-The first command runs the Nim compiler with three special options to avoid
-generating a ``main()`` function in the generated files, avoid linking the
-object files into a final binary, and explicitly generate a header file for C
-integration. All the generated files are placed into the ``nimcache``
-directory. That's why the next command compiles the ``maths.c`` source plus
-all the ``.c`` files form ``nimcache``. In addition to this path, you also
-have to tell the C compiler where to find Nim's ``nimbase.h`` header file.
-
-Instead of depending on the generation of the individual ``.c`` files you can
-also ask the Nim compiler to generate a statically linked library::
-
-  $ nim c --app:staticLib --noMain --header fib.nim
-  $ gcc -o m -Inimcache -Ipath/to/nim/lib libfib.nim.a maths.c
-
-The Nim compiler will handle linking the source files generated in the
-``nimcache`` directory into the ``libfib.nim.a`` static library, which you can
-then link into your C program.  Note that these commands are generic and will
-vary for each system. For instance, on Linux systems you will likely need to
-use ``-ldl`` too to link in required dlopen functionality.
-
-
-Nim invocation example from JavaScript
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Create a ``mhost.html`` file with the following content:
-
-.. code-block::
-
-  <html><body>
-  <script type="text/javascript" src="fib.js"></script>
-  <script type="text/javascript">
-  alert("Fib for 9 is " + fib(9));
-  </script>
-  </body></html>
-
-Create a ``fib.nim`` file with the following content (or reuse the one
-from the previous section):
-
-.. code-block:: nim
-
-  proc fib(a: cint): cint {.exportc.} =
-    if a <= 2:
-      result = 1
-    else:
-      result = fib(a - 1) + fib(a - 2)
-
-Compile the Nim code to JavaScript with ``nim js -o:fib.js fib.nim`` and
-open ``mhost.html`` in a browser. If the browser supports javascript, you
-should see an alert box displaying the text ``Fib for 9 is 34``. As mentioned
-earlier, JavaScript doesn't require an initialisation call to ``NimMain`` or
-similar function and you can call the exported Nim proc directly.
-
-
-Nimcache naming logic
----------------------
-
-The `nimcache`:idx: directory is generated during compilation and will hold
-either temporary or final files depending on your backend target. The default
-name for the directory is ``nimcache`` but you can use the ``--nimcache``
-`compiler switch <nimc.html#command-line-switches>`_ to change it.
-
-Nimcache and C like targets
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The C like backends will place their temporary ``.c``, ``.cpp`` or ``.m`` files
-in the ``nimcache`` directory. The naming of these files follows the pattern
-``nimblePackageName_`` + ``nimSource``:
-
-* Filenames for modules imported from `nimble packages
-  <https://github.com/nim-lang/nimble>`_ will end up with
-  ``nimblePackageName_module.c``. For example, if you import the
-  ``argument_parser`` module from the same name nimble package you
-  will end up with a ``argument_parser_argument_parser.c`` file
-  under ``nimcache``.  The name of the nimble package comes from the
-  ``proj.nimble`` file, the actual contents are not read by the
-  compiler.
-
-* Filenames for non nimble packages (like your project) will be
-  renamed from ``.nim`` to have the extension of your target backend
-  (from now on ``.c`` for these examples), but otherwise nothing
-  else will change. This will quickly break if your project consists
-  of a main ``proj.nim`` file which includes a ``utils/proj.nim``
-  file: both ``proj.nim`` files will generate the same name ``proj.c``
-  output in the ``nimcache`` directory overwriting themselves!
-
-* Filenames for modules found in the standard library will be named
-  ``stdlib_module.c``. Unless you are doing something special, you
-  will end up with at least ``stdlib_system.c``, since the `system
-  module <system.html>`_ is always imported automatically. Same for
-  the `hashes module <hashes.html>`_ which will be named
-  ``stdlib_hashes.c``. The ``stdlib_`` prefix comes from the *fake*
-  ``lib/stdlib.nimble`` file.
-
-To find the name of a nimble package the compiler searches for a ``*.nimble``
-file in the parent directory hierarchy of whatever module you are compiling.
-Even if you are in a subdirectory of your project, a parent ``*.nimble`` file
-will influence the naming of the nimcache name. This means that on Unix systems
-creating the file ``~/foo.nimble`` will automatically prefix all nimcache files
-not part of another package with the string ``foo_``.
-
-
-Nimcache and the Javascript target
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Unless you explicitly use the ``-o:filename.js`` switch as mentioned in the
-previous examples, the compiler will create a ``filename.js`` file in the
-``nimcache`` directory using the name of your input nim file. There are no
-other temporary files generated, the output is always a single self contained
-``.js`` file.
-
-
-Memory management
-=================
-
-In the previous sections the ``NimMain()`` function reared its head. Since
-JavaScript already provides automatic memory management, you can freely pass
-objects between the two language without problems. In C and derivate languages
-you need to be careful about what you do and how you share memory. The
-previous examples only dealt with simple scalar values, but passing a Nim
-string to C, or reading back a C string in Nim already requires you to be
-aware of who controls what to avoid crashing.
-
-
-Strings and C strings
----------------------
-
-The manual mentions that `Nim strings are implicitly convertible to
-cstrings <manual.html#cstring-type>`_ which makes interaction usually
-painless. Most C functions accepting a Nim string converted to a
-``cstring`` will likely not need to keep this string around and by the time
-they return the string won't be needed any more. However, for the rare cases
-where a Nim string has to be preserved and made available to the C backend
-as a ``cstring``, you will need to manually prevent the string data from being
-freed with `GC_ref <system.html#GC_ref>`_ and `GC_unref
-<system.html#GC_unref>`_.
-
-A similar thing happens with C code invoking Nim code which returns a
-``cstring``. Consider the following proc:
-
-.. code-block:: nim
-
-  proc gimme(): cstring {.exportc.} =
-    result = "Hey there C code! " & $random(100)
-
-Since Nim's garbage collector is not aware of the C code, once the
-``gimme`` proc has finished it can reclaim the memory of the ``cstring``.
-However, from a practical standpoint, the C code invoking the ``gimme``
-function directly will be able to use it since Nim's garbage collector has
-not had a chance to run *yet*. This gives you enough time to make a copy for
-the C side of the program, as calling any further Nim procs *might* trigger
-garbage collection making the previously returned string garbage. Or maybe you
-are `yourself triggering the collection <gc.html>`_.
-
-
-Custom data types
------------------
-
-Just like strings, custom data types that are to be shared between Nim and
-the backend will need careful consideration of who controls who. If you want
-to hand a Nim reference to C code, you will need to use `GC_ref
-<system.html#GC_ref>`_ to mark the reference as used, so it does not get
-freed. And for the C backend you will need to expose the `GC_unref
-<system.html#GC_unref>`_ proc to clean up this memory when it is not required
-any more.
-
-Again, if you are wrapping a library which *mallocs* and *frees* data
-structures, you need to expose the appropriate *free* function to Nim so
-you can clean it up. And of course, once cleaned you should avoid accessing it
-from Nim (or C for that matter). Typically C data structures have their own
-``malloc_structure`` and ``free_structure`` specific functions, so wrapping
-these for the Nim side should be enough.
-
-
-Thread coordination
--------------------
-
-When the ``NimMain()`` function is called Nim initializes the garbage
-collector to the current thread, which is usually the main thread of your
-application. If your C code later spawns a different thread and calls Nim
-code, the garbage collector will fail to work properly and you will crash.
-
-As long as you don't use the threadvar emulation Nim uses native thread
-variables, of which you get a fresh version whenever you create a thread. You
-can then attach a GC to this thread via
-
-.. code-block:: nim
-
-  system.setupForeignThreadGc()
-
-It is **not** safe to disable the garbage collector and enable it after the
-call from your background thread even if the code you are calling is short
-lived.
-
-Before the thread exits, you should tear down the thread's GC to prevent memory
-leaks by calling
-
-.. code-block:: nim
-
-  system.tearDownForeignThreadGc()
-
diff --git a/doc/basicopt.txt b/doc/basicopt.txt
index 90c7ba09c..e8133d227 100644
--- a/doc/basicopt.txt
+++ b/doc/basicopt.txt
@@ -1,41 +1,40 @@
-::
 
     nim command [options] [projectfile] [arguments]
 
 Command:
-  //compile, c                compile project with default code generator (C)
-  //doc                       generate the documentation for inputfile
+  //compile, c              compile project with default code generator (C)
+  //r                       compile to $nimcache/projname, run with `arguments`
+                            using backend specified by `--backend` (default: c)
+  //doc                     generate the documentation for inputfile for
+                            backend specified by `--backend` (default: c)
 
 Arguments:
   arguments are passed to the program being run (if --run option is selected)
+
 Options:
   -p, --path:PATH           add path to search paths
   -d, --define:SYMBOL(:VAL)
                             define a conditional symbol
-                            (Optionally: Define the value for that symbol)
+                            (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
   -x, --checks:on|off       turn all runtime checks on|off
-  --objChecks:on|off        turn obj conversion checks on|off
-  --fieldChecks:on|off      turn case variant field checks on|off
-  --rangeChecks:on|off      turn range checks on|off
-  --boundChecks:on|off      turn bound checks on|off
-  --overflowChecks:on|off   turn int over-/underflow checks on|off
   -a, --assertions:on|off   turn assertions on|off
-  --floatChecks:on|off      turn all floating point (NaN/Inf) checks on|off
-  --nanChecks:on|off        turn NaN checks on|off
-  --infChecks:on|off        turn Inf checks on|off
-  --nilChecks:on|off        turn nil checks on|off
   --opt:none|speed|size     optimize not at all or for speed|size
                             Note: use -d:release for a release build!
-  --debugger:native|endb    use native debugger (gdb) | ENDB (experimental)
+  --debugger:native         use native debugger (gdb)
   --app:console|gui|lib|staticlib
                             generate a console app|GUI app|DLL|static library
   -r, --run                 run the compiled program with given arguments
+  --eval:cmd                evaluate nim code directly; e.g.: `nim --eval:"echo 1"`
+                            defaults to `e` (nimscript) but customizable:
+                            `nim r --eval:'for a in stdin.lines: echo a'`
   --fullhelp                show all command line switches
   -h, --help                show this help
+  -v, --version             show detailed version information
 
 Note, single letter options that take an argument require a colon. E.g. -p:PATH.
diff --git a/doc/contributing.md b/doc/contributing.md
new file mode 100644
index 000000000..420c1438e
--- /dev/null
+++ b/doc/contributing.md
@@ -0,0 +1,798 @@
+============
+Contributing
+============
+
+.. default-role:: code
+.. include:: rstcommon.rst
+
+.. contents::
+
+
+Contributing happens via "Pull requests" (PR) on GitHub. Every PR needs to be
+reviewed before it can be merged and the Continuous Integration should be green.
+The title of a PR should contain a brief description. If it fixes an issue,
+in addition to the number of the issue, the title should also contain a description
+of the issue.
+
+The PR has to be approved by two core developers or by Araq.
+
+
+
+Writing tests
+=============
+
+There are 4 types of tests:
+
+1. `runnableExamples` documentation comment tests, ran by `nim doc mymod.nim`:cmd:
+   These end up in documentation and ensure documentation stays in sync with code.
+
+2. separate test files, e.g.: ``tests/stdlib/tos.nim``.
+   In nim repo, `testament`:cmd: (see below) runs all
+   ``$nim/tests/*/t*.nim`` test files;
+   for nimble packages, see https://github.com/nim-lang/nimble#tests.
+
+3. (deprecated) tests in `when isMainModule:` block, ran by `nim r mymod.nim`:cmd:.
+   `nimble test`:cmd: can run those in nimble packages when specified in a
+   `task "test"`.
+
+4. (not preferred) ``.. code-block:: nim`` RST snippets;
+   these should only be used in rst sources,
+   in nim sources `runnableExamples` should now always be preferred to those for
+   several reasons (cleaner syntax, syntax highlights, batched testing, and
+   parameter `rdoccmd` allows customization).
+
+Not all the tests follow the convention here, feel free to change the ones
+that don't. Always leave the code cleaner than you found it.
+
+Stdlib
+------
+
+Each stdlib module (anything under ``lib/``, e.g. ``lib/pure/os.nim``) should
+preferably have a corresponding separate test file, e.g. ``tests/stdlib/tos.nim``.
+The old convention was to add a `when isMainModule:` block in the source file,
+which only gets executed when the tester is building the file.
+
+Each test should be in a separate `block:` statement, such that
+each has its own scope. Use boolean conditions and `doAssert` for the
+testing by itself, don't rely on echo statements or similar; in particular, avoid
+things like `echo "done"`. Don't use `unittest.suite` and `unittest.test`.
+
+Sample test:
+
+  ```nim
+  block: # foo
+    doAssert foo(1) == 10
+
+  block: # bug #1234
+    static: doAssert 1+1 == 2
+
+  block: # bug #1235
+    var seq2D = newSeqWith(4, newSeq[bool](2))
+    seq2D[0][0] = true
+    seq2D[1][0] = true
+    seq2D[0][1] = true
+    doAssert seq2D == @[@[true, true], @[true, false],
+                        @[false, false], @[false, false]]
+    # doAssert with `not` can now be done as follows:
+    doAssert not (1 == 2)
+  ```
+
+Always refer to a GitHub issue using the following exact syntax: ``bug #1234`` as shown
+above, so that it's consistent and easier to search or for tooling. Some browser
+extensions (e.g. https://github.com/sindresorhus/refined-github) will even turn those
+in clickable links when it works.
+
+Rationale for using a separate test file instead of `when isMainModule:` block:
+* allows custom compiler flags or testing options (see details below)
+* faster CI since they can be joined in ``megatest`` (combined into a single test)
+* avoids making the parser do un-necessary work when a source file is merely imported
+* avoids mixing source and test code when reporting line of code statistics or code coverage
+
+Compiler
+--------
+
+The tests for the compiler use a testing tool called `testament`:cmd:. They are all
+located in ``tests/`` (e.g.: ``tests/destructor/tdestructor3.nim``).
+Each test has its own file. All test files are prefixed with `t`. If you want
+to create a file for import into another test only, use the prefix `m`.
+
+At the beginning of every test is the expected behavior of the test.
+Possible keys are:
+
+- `cmd`: A compilation command template e.g. `nim $target --threads:on $options $file`:cmd:
+- `output`: The expected output (stdout + stderr), most likely via `echo`
+- `exitcode`: Exit code of the test (via `exit(number)`)
+- `errormsg`: The expected compiler error message
+- `file`: The file the errormsg was produced at
+- `line`: The line the errormsg was produced at
+
+For a full spec, see here: ``testament/specs.nim``
+
+An example of a test:
+
+  ```nim
+  discard """
+    errormsg: "type mismatch: got (PTest)"
+  """
+
+  type
+    PTest = ref object
+
+  proc test(x: PTest, y: int) = nil
+
+  var buf: PTest
+  buf.test()
+  ```
+
+
+Running tests
+=============
+
+You can run the tests with
+
+  ```cmd
+  ./koch tests
+  ```
+
+which will run a good subset of tests. Some tests may fail. If you
+only want to see the output of failing tests, go for
+
+  ```cmd
+  ./koch tests --failing all
+  ```
+
+You can also run only a single category of tests. A category is a subdirectory
+in the ``tests/`` directory. There are a couple of special categories; for a
+list of these, see ``testament/categories.nim``, at the bottom.
+
+  ```cmd
+  ./koch tests c lib # compiles / runs stdlib modules, including `isMainModule` tests
+  ./koch tests c megatest # runs a set of tests that can be combined into 1
+  ```
+
+To run a single test:
+
+  ```cmd
+  ./koch test run <category>/<name>    # e.g.: tuples/ttuples_issues
+  ./koch test run tests/stdlib/tos.nim # can also provide relative path
+  ```
+
+For reproducible tests (to reproduce an environment more similar to the one
+run by Continuous Integration on GitHub actions/azure pipelines), you may want to disable your
+local configuration (e.g. in ``~/.config/nim/nim.cfg``) which may affect some
+tests; this can also be achieved by using
+`export XDG_CONFIG_HOME=pathtoAlternateConfig`:cmd: before running `./koch`:cmd:
+commands.
+
+
+Comparing tests
+===============
+
+Test failures can be grepped using ``Failure:``.
+
+The tester can compare two test runs. First, you need to create a
+reference test. You'll also need to the commit id, because that's what
+the tester needs to know in order to compare the two.
+
+  ```cmd
+  git checkout devel
+  DEVEL_COMMIT=$(git rev-parse HEAD)
+  ./koch tests
+  ```
+
+Then switch over to your changes and run the tester again.
+
+  ```cmd
+  git checkout your-changes
+  ./koch tests
+  ```
+
+Then you can ask the tester to create a ``testresults.html`` which will
+tell you if any new tests passed/failed.
+
+  ```cmd
+  ./koch tests --print html $DEVEL_COMMIT
+  ```
+
+
+Deprecation
+===========
+
+Backwards compatibility is important. When renaming types, procedures, etc. the old name
+must be marked as deprecated using the `deprecated` pragma:
+
+  ```nim
+  # for routines (proc/template/macro/iterator) and types:
+  proc oldProc(a: int, b: float): bool {.deprecated:
+      "deprecated since v1.2.3; use `newImpl: string -> int` instead".} = discard
+
+  # for (const/var/let/fields) the msg is not yet supported:
+  const Foo {.deprecated.}  = 1
+
+  # for enum types, you can deprecate the type or some elements
+  # (likewise with object types and their fields):
+  type Bar {.deprecated.} = enum bar0, bar1
+  type Barz  = enum baz0, baz1 {.deprecated.}, baz2
+  ```
+
+
+See also [Deprecated](manual.html#pragmas-deprecated-pragma)
+pragma in the manual.
+
+
+Documentation
+=============
+
+When contributing new procs, be sure to add documentation, especially if
+the proc is public. Even private procs benefit from documentation and can be
+viewed using `nim doc --docInternal foo.nim`:cmd:.
+Documentation begins on the line
+following the `proc` definition, and is prefixed by `##` on each line.
+
+Runnable code examples are also encouraged, to show typical behavior with a few
+test cases (typically 1 to 3 `assert` statements, depending on complexity).
+These `runnableExamples` are automatically run by `nim doc mymodule.nim`:cmd:
+as well as `testament`:cmd: and guarantee they stay in sync.
+
+  ```nim
+  proc addBar*(a: string): string =
+    ## Adds "Bar" to `a`.
+    runnableExamples:
+      assert "baz".addBar == "bazBar"
+    result = a & "Bar"
+  ```
+
+See [parentDir](os.html#parentDir,string) example.
+
+The RestructuredText Nim uses has a special syntax for including code snippets
+embedded in documentation; these are not run by `nim doc`:cmd: and therefore are
+not guaranteed to stay in sync, so `runnableExamples` is almost always preferred:
+
+  ````nim
+  proc someProc*(): string =
+    ## Returns "something"
+    ##
+    ##   ```
+    ##   echo someProc() # "something"
+    ##   ```
+    result = "something" # single-hash comments do not produce documentation
+  ````
+
+The \`\`\` followed by a newline and an indentation instructs the
+`nim doc`:cmd: command to produce syntax-highlighted example code with the
+documentation (\`\`\` is sufficient inside a ``.nim`` module, while from
+a ``.md`` one needs to set the language explicitly as \`\`\`nim).
+
+When forward declaration is used, the documentation should be included with the
+first appearance of the proc.
+
+  ```nim
+  proc hello*(): string
+    ## Put documentation here
+  proc nothing() = discard
+  proc hello*(): string =
+    ## ignore this
+    echo "hello"
+  ```
+
+The preferred documentation style is to begin with a capital letter and use
+the third-person singular. That is, between:
+
+  ```nim
+  proc hello*(): string =
+    ## Returns "hello"
+    result = "hello"
+  ```
+
+or
+
+  ```nim
+  proc hello*(): string =
+    ## say hello
+    result = "hello"
+  ```
+
+the first is preferred.
+
+When you specify an *RST role* (highlighting/interpretation marker) do it
+in the postfix form for uniformity, that is after \`text in backticks\`.
+For example an ``:idx:`` role for referencing a topic ("SQLite" in the
+example below) from [Nim Index] can be used in doc comment this way:
+
+  ```nim
+  ## A higher level `SQLite`:idx: database wrapper.
+  ```
+
+.. _`Nim Index`: https://nim-lang.org/docs/theindex.html
+
+Inline monospaced text can be input using \`single backticks\` or
+\`\`double backticks\`\`. The former are syntactically highlighted,
+the latter are not.
+To avoid accidental highlighting follow this rule in ``*.nim`` files:
+
+* Use single backticks for fragments of code in Nim and other
+  programming languages, including identifiers, in ``*.nim`` files.
+
+  For languages other than Nim add a role after final backtick,
+  e.g. for C++ inline highlighting:
+
+      `#include <stdio.h>`:cpp:
+
+  For a currently unsupported language add the `:code:` role,
+  like for SQL in this example:
+
+      `SELECT * FROM <table_name>;`:code:
+
+  Highlight shell commands by ``:cmd:`` role; for command line options use
+  ``:option:`` role, e.g.: \`--docInternal\`:option:.
+
+* Use double backticks:
+
+  * For file names: \`\`os.nim\`\`
+  * For fragments of strings **not** enclosed by `"` and `"` and not
+    related to code, e.g. text of compiler messages
+  * When code ends with a standalone ``\`` (otherwise a combination of
+    ``\`` and a final \` would get escaped)
+
+.. Note:: ``*.rst`` files have ``:literal:`` as their default role.
+  So for them the rule above is only applicable if the ``:nim:`` role
+  is set up manually as the default [^1]:
+
+      .. role:: nim(code)
+         :language: nim
+      .. default-role:: nim
+
+  The first 2 lines are for other RST implementations,
+  including Github one.
+
+  [^1]: this is fulfilled when ``doc/rstcommon.rst`` is included.
+
+Best practices
+==============
+
+Note: these are general guidelines, not hard rules; there are always exceptions.
+Code reviews can just point to a specific section here to save time and
+propagate best practices.
+
+.. _define_needs_prefix:
+New `defined(foo)` symbols need to be prefixed by the nimble package name, or
+by `nim` for symbols in nim sources (e.g. compiler, standard library). This is
+to avoid name conflicts across packages.
+
+  ```nim
+  # if in nim sources
+  when defined(allocStats): discard # bad, can cause conflicts
+  when defined(nimAllocStats): discard # preferred
+  # if in a package `cligen`:
+  when defined(debug): discard # bad, can cause conflicts
+  when defined(cligenDebug): discard # preferred
+  ```
+
+.. _noimplicitbool:
+Take advantage of no implicit bool conversion
+
+  ```nim
+  doAssert isValid() == true
+  doAssert isValid() # preferred
+  ```
+
+.. _design_for_mcs:
+Design with method call syntax chaining in mind
+
+  ```nim
+  proc foo(cond: bool, lines: seq[string]) # bad
+  proc foo(lines: seq[string], cond: bool) # preferred
+  # can be called as: `getLines().foo(false)`
+  ```
+
+.. _avoid_quit:
+Use exceptions (including `assert` / `doAssert`) instead of `quit`
+rationale: https://forum.nim-lang.org/t/4089
+
+  ```nim
+  quit() # bad in almost all cases
+  doAssert() # preferred
+  ```
+
+.. _tests_use_doAssert:
+Use `doAssert` (or `unittest.check`, `unittest.require`), not `assert` in all
+tests, so they'll be enabled even with `--assertions:off`:option:.
+
+  ```nim
+  block: # foo
+    assert foo() # bad
+    doAssert foo() # preferred
+  ```
+
+.. _runnableExamples_use_assert:
+An exception to the above rule is `runnableExamples` and ``code-block`` rst blocks
+intended to be used as `runnableExamples`, which for brevity use `assert`
+instead of `doAssert`. Note that `nim doc -d:danger main`:cmd: won't pass `-d:danger`:option: to the
+`runnableExamples`, but `nim doc --doccmd:-d:danger main`:cmd: would, and so would the
+second example below:
+
+  ```nim
+  runnableExamples:
+    doAssert foo() # bad
+    assert foo() # preferred
+
+  runnableExamples("-d:danger"):
+    doAssert foo() # `assert` would be disabled here, so `doAssert` makes more sense
+  ```
+
+.. _delegate_printing:
+Delegate printing to caller: return `string` instead of calling `echo`
+rationale: it's more flexible (e.g. allows the caller to call custom printing,
+including prepending location info, writing to log files, etc.).
+
+  ```nim
+  proc foo() = echo "bar" # bad
+  proc foo(): string = "bar" # preferred (usually)
+  ```
+
+.. _use_Option:
+(Ongoing debate) Consider using Option instead of return bool + var argument,
+unless stack allocation is needed (e.g. for efficiency).
+
+  ```nim
+  proc foo(a: var Bar): bool
+  proc foo(): Option[Bar]
+  ```
+
+.. _use_doAssert_not_echo:
+Tests (including in testament) should always prefer assertions over `echo`,
+except when that's not possible. It's more precise, easier for readers and
+maintainers to where expected values refer to. See for example
+https://github.com/nim-lang/Nim/pull/9335 and https://forum.nim-lang.org/t/4089
+
+  ```nim
+  echo foo() # adds a line for testament in `output:` block inside `discard`.
+  doAssert foo() == [1, 2] # preferred, except when not possible to do so.
+  ```
+
+
+The `git`:cmd: stuff
+====================
+
+General commit rules
+--------------------
+
+1. Important, critical bugfixes that have a tiny chance of breaking
+   somebody's code should be backported to the latest stable release
+   branch (currently 1.4.x) and maybe also all the way back to the 1.0.x branch.
+   The commit message should contain the tag ``[backport]`` for "backport to the latest
+   stable release" and the tag ``[backport:$VERSION]`` for backporting back to the
+   given $VERSION (and all newer releases).
+
+2. If you introduce changes which affect backward compatibility,
+   make breaking changes, or have PR which is tagged as ``[feature]``,
+   the changes should be mentioned in [the changelog](
+   https://github.com/nim-lang/Nim/blob/devel/changelog.md).
+
+3. All changes introduced by the commit (diff lines) must be related to the
+   subject of the commit.
+
+   If you change something unrelated to the subject parts of the file, because
+   your editor reformatted automatically the code or whatever different reason,
+   this should be excluded from the commit.
+
+   *Tip:* Never commit everything as-is using `git commit -a`:cmd:, but review
+   carefully your changes with `git add -p`:cmd:.
+
+4. Changes should not introduce any trailing whitespace.
+
+   Always check your changes for whitespace errors using `git diff --check`:cmd:
+   or add the following ``pre-commit`` hook:
+
+     ```cmd
+     #!/bin/sh
+     git diff --check --cached || exit $?
+     ```
+5. Describe your commit and use your common sense.
+   Example commit message:
+
+       Fixes #123; refs #124
+
+   indicates that issue ``#123`` is completely fixed (GitHub may automatically
+   close it when the PR is committed), whereas issue ``#124`` is referenced
+   (e.g.: partially fixed) and won't close the issue when committed.
+
+6. PR body (not just PR title) should contain references to fixed/referenced GitHub
+   issues, e.g.: ``fix #123`` or ``refs #123``. This is so that you get proper
+   cross-referencing from linked issue to the PR (GitHub won't make those links
+   with just a PR title, and commit messages aren't always sufficient to ensure
+   that, e.g. can't be changed after a PR is merged).
+
+7. Commits should be always be rebased against devel (so a fast-forward
+   merge can happen)
+
+   e.g.: use `git pull --rebase origin devel`:cmd:. This is to avoid messing up
+   git history.
+   Exceptions should be very rare: when rebase gives too many conflicts, simply
+   squash all commits using the script shown in
+   https://github.com/nim-lang/Nim/pull/9356
+
+8. Do not mix pure formatting changes (e.g. whitespace changes, nimpretty) or
+   automated changes with other code changes: these should be in
+   separate commits (and the merge on GitHub should not squash these into 1).
+
+
+Continuous Integration (CI)
+---------------------------
+
+1. Continuous Integration is by default run on every push in a PR; this clogs
+   the CI pipeline and affects other PR's; if you don't need it (e.g. for WIP or
+   documentation only changes), add ``[skip ci]`` to your commit message title.
+   This convention is supported by our GitHub actions pipelines and our azure pipeline
+   (using custom logic, which should complete in < 1mn) as well as our former other pipelines:
+   [Appveyor](
+   https://www.appveyor.com/docs/how-to/filtering-commits/#skip-directive-in-commit-message)
+   and [Travis](
+   https://docs.travis-ci.com/user/customizing-the-build/#skipping-a-build).
+
+2. Consider enabling CI (azure, GitHub actions and builds.sr.ht) in your own Nim fork, and
+   waiting for CI to be green in that fork (fixing bugs as needed) before
+   opening your PR in the original Nim repo, to reduce CI congestion. Same
+   applies for updates on a PR: you can test commits on a separate private
+   branch before updating the main PR.
+
+Debugging CI failures, flaky tests, etc
+---------------------------------------
+
+1. First check the CI logs and search for `FAIL` to find why CI failed; if the
+   failure seems related to your PR, try to fix the code instead of restarting CI.
+
+2. If CI failure seems unrelated to your PR, it could be caused by a flaky test.
+   File a bug for it if it isn't already reported. A PR push (or opening/closing PR)
+   will re-trigger all CI jobs (even successful ones, which can be wasteful). Instead,
+   request collaboration from the Nim team. The Nim team should
+   follow these instructions to only restart the jobs that failed:
+
+   * Azure: if on your own fork, it's possible from inside azure console
+     (e.g. ``dev.azure.com/username/username/_build/results?buildId=1430&view=results``) via
+     ``rerun failed jobs`` on top.
+     If either on you own fork or in Nim repo, it's possible from inside GitHub UI
+     under checks tab, see https://github.com/timotheecour/Nim/issues/211#issuecomment-629751569
+   * GitHub actions: under "Checks" tab, click "Re-run jobs" in the right.
+   * builds.sr.ht: create a SourceHut account so that you can restart a PR job as illustrated.
+     builds.sr.ht also allows you to ssh to a CI machine which can help a lot for debugging
+     issues, see docs in https://man.sr.ht/builds.sr.ht/build-ssh.md and
+     https://drewdevault.com/2019/08/19/Introducing-shell-access-for-builds.html; see
+     https://man.sr.ht/tutorials/set-up-account-and-git.md to generate and upload ssh keys.
+
+
+Code reviews
+------------
+
+1. Whenever possible, use GitHub's new 'Suggested change' in code reviews, which
+   saves time explaining the change or applying it; see also
+   https://forum.nim-lang.org/t/4317
+
+2. When reviewing large diffs that may involve code moving around, GitHub's interface
+   doesn't help much, as it doesn't highlight moves. Instead, you can use something
+   like this, see visual results [here](
+   https://github.com/nim-lang/Nim/pull/10431#issuecomment-456968196):
+
+     ```cmd
+     git fetch origin pull/10431/head && git checkout FETCH_HEAD
+     git diff --color-moved-ws=allow-indentation-change --color-moved=blocks HEAD^
+     ```
+
+3. In addition, you can view GitHub-like diffs locally to identify what was changed
+   within a code block using `diff-highlight`:cmd: or `diff-so-fancy`:cmd:, e.g.:
+
+        # put this in ~/.gitconfig:
+        [core]
+          pager = "diff-so-fancy | less -R" # or: use: `diff-highlight`
+
+
+
+.. include:: docstyle.md
+
+
+Evolving the stdlib
+===================
+
+As outlined in https://github.com/nim-lang/RFCs/issues/173 there are a couple
+of guidelines about what should go into the stdlib, what should be added and
+what eventually should be removed.
+
+
+What the compiler itself needs must be part of the stdlib
+---------------------------------------------------------
+
+Maybe in the future the compiler itself can depend on Nimble packages but for
+the time being, we strive to have zero dependencies in the compiler as the
+compiler is the root of the bootstrapping process and is also used to build
+Nimble.
+
+
+Vocabulary types must be part of the stdlib
+-------------------------------------------
+
+These are types most packages need to agree on for better interoperability,
+for example `Option[T]`. This rule also covers the existing collections like
+`Table`, `CountTable` etc. "Sorted" containers based on a tree-like data
+structure are still missing and should be added.
+
+Time handling, especially the `Time` type are also covered by this rule.
+
+
+Existing, battle-tested modules stay
+------------------------------------
+
+Reason: There is no benefit in moving them around just to fulfill some design
+fashion as in "Nim's core MUST BE SMALL". If you don't like an existing module,
+don't import it. If a compilation target (e.g. JS) cannot support a module,
+document this limitation.
+
+This covers modules like `os`, `osproc`, `strscans`, `strutils`,
+`strformat`, etc.
+
+
+Syntactic helpers can start as experimental stdlib modules
+----------------------------------------------------------
+
+Reason: Generally speaking as external dependencies they are not exposed
+to enough users so that we can see if the shortcuts provide enough benefit
+or not. Many programmers avoid external dependencies, especially for
+"tiny syntactic improvements". However, this is only true for really good
+syntactic improvements that have the potential to clean up other parts of
+the Nim library substantially. If in doubt, new stdlib modules should start
+as external, successful Nimble packages.
+
+
+
+Other new stdlib modules do not start as stdlib modules
+-------------------------------------------------------
+
+As we strive for higher quality everywhere, it's easier to adopt existing,
+battle-tested modules eventually rather than creating modules from scratch.
+
+
+Little additions are acceptable
+-------------------------------
+
+As long as they are documented and tested well, adding little helpers
+to existing modules is acceptable. For two reasons:
+
+1. It makes Nim easier to learn and use in the long run.
+   ("Why does sequtils lack a `countIt`?
+   Because version 1.0 happens to have lacked it? Silly...")
+2. To encourage contributions. Contributors often start with PRs that
+   add simple things, then they stay and also fix bugs. Nim is an
+   open source project and lives from people's contributions and involvement.
+   Newly introduced issues have to be balanced against motivating new people. We know where
+   to find perfectly designed pieces of software that have no bugs -- these are the systems
+   that nobody uses.
+
+Conventions
+-----------
+1. New stdlib modules should go under ``Nim/lib/std/``. The rationale is to
+   require users to import via `import std/foo` instead of `import foo`,
+   which would cause potential conflicts with nimble packages.
+   Note that this still applies for new modules in existing logical
+   directories, e.g.: use ``lib/std/collections/foo.nim``,
+   not ``lib/pure/collections/foo.nim``.
+
+2. New module names should prefer plural form whenever possible, e.g.:
+   ``std/sums.nim`` instead of ``std/sum.nim``. In particular, this reduces
+   chances of conflicts between module name and the symbols it defines.
+   Furthermore, module names should use `snake_case` and not use capital
+   letters, which cause issues when going from an OS without case
+   sensitivity to an OS with it.
+
+
+Breaking Changes
+================
+
+Introducing breaking changes, no matter how well-intentioned,
+creates long-term problems for the community, in particular those looking to promote
+reusable Nim code in libraries: In the Nim distribution, critical security and bugfixes,
+language changes and community improvements are bundled in a single distribution - it is
+difficult to make partial upgrades with only benign changes. When one library depends on
+a legacy behavior, it can no longer be used together with another library that does not,
+breaking all downstream applications - the standard library is unique in that it sits at
+the root of **all** dependency trees.
+
+There is a big difference between compile-time breaking changes and run-time breaking
+changes.
+
+
+Run-time breaking changes
+-------------------------
+
+Run-time breaking changes are to be avoided at almost all costs: Nim is used for
+mission critical applications which depend on behaviours that
+are not covered by the test suite. As such, it's important that changes to the
+*stable* parts of the standard library are made avoiding changing the existing
+behaviors, even when the test suite continues to pass.
+
+Examples of run-time breaking changes:
+
+- Raising exceptions of a new type, compared to what's currently being raised.
+
+- Adding unconstrained or poorly constrained generic procs or macros
+  ("hash now works for all `ref T`"): This may cause code to behave differently
+  depending only on which modules are imported - common examples include `==` and `hash`.
+
+- Changing behavior of existing functions like:
+
+  * "Nim's path handling procs like `getXDir` now consistently lack the trailing slash"
+  * "Nim's strformat implementation is now more consistent with Python"
+
+Instead, write new code that explicitly announces the feature you think we announced but
+didn't. For example, `strformat` does not say "it's compatible with Python", it
+says "inspired by Python's f-strings". This new code can be submitted to the stdlib
+and the old code can be deprecated or published as a Nimble package.
+
+Sometimes, a run-time breaking change is most desirable: For example, a string
+representation of a floating point number that "roundtrips" is much better than
+a string representation that doesn't. These run-time breaking changes must start in the
+state "opt-in" via a new `-d:nimPreviewX` or command line flag and then should become
+the new default later, in follow-up versions. This way users can track
+regressions more easily. ("git bisect" is not an acceptable alternative, that's for
+Nim compiler developers, not for Nim users.)
+
+Above all else, additive approaches that don't change existing behaviors
+should be preferred.
+
+
+Compile-time breaking changes
+-----------------------------
+
+Compile-time breaking changes are usually easier to handle, but for large code bases
+they can also involve a large amount of work and can hinder the adoption of a new
+Nim release.
+Additive approaches are to be preferred here as well.
+
+Examples of compile-time breaking changes include (but are not limited to):
+
+* Renaming functions and modules, or moving things. Instead of a direct rename,
+  deprecate the old name and introduce a new one.
+* Renaming the parameter names: Thanks to Nim's "named parameter" calling syntax
+  like `f(x = 0, y = 1)` this is a breaking change. Instead, live with the existing
+  parameter names.
+* Adding an enum value to an existing enum. Nim's exhaustive case statements stop
+  compiling after such a change. Instead, consider to introduce new `bool`
+  fields/parameters. This can be impractical though, so we use good judgement
+  and our list of "important packages" to see if it doesn't break too much code
+  out there in practice.
+* Adding a new proc to an existing stdlib module. However, for aesthetic reasons
+  this is often preferred over introducing a new module with just a single proc
+  inside. We use good judgement and our list of "important packages" to see if
+  it doesn't break too much code out there in practice. The new procs need to
+  be annotated with a `.since` annotation.
+
+
+Compiler/language spec bugfixes
+-------------------------------
+
+This can even be applied to compiler "bugfixes": If the compiler should have been
+"pickier" in its handling of `typedesc`, instead of "fixing typedesc handling bugs",
+consider the following solution:
+
+- Spec out how `typedesc` should really work and also spec out the cases where it
+  should not be allowed!
+- Deprecate `typedesc` and name the new metatype something new like `typeArg`.
+- Implement the spec.
+
+
+Non-breaking changes
+--------------------
+
+Examples of changes that are considered non-breaking (or acceptable breaking changes) include:
+
+* Creating a new module.
+* Adding an overload to an already overloaded proc.
+* Adding new default parameters to an existing proc. It is assumed that you do not
+  use Nim's stdlib procs's addresses (that you don't use them as first class entities).
+* Changing the calling convention from `nimcall` to `inline`
+  (but first RFC https://github.com/nim-lang/RFCs/issues/396 needs to be implemented).
+* Changing the behavior from "crashing" into some other, well documented result (including
+  raising a Defect, but not raising an exception that does not inherit from Defect).
+* Adding new fields to an existing object.
+
+Nim's introspection facilities imply that strictly speaking almost every addition can
+break somebody's code. It is impractical to care about these cases, a change that only
+affects introspection is not considered to be a breaking change.
diff --git a/doc/contributing.rst b/doc/contributing.rst
deleted file mode 100644
index ee97f6dc8..000000000
--- a/doc/contributing.rst
+++ /dev/null
@@ -1,226 +0,0 @@
-Writing tests
-=============
-
-Not all the tests follow this scheme, feel free to change the ones
-that don't. Always leave the code cleaner than you found it.
-
-Stdlib
-------
-
-If you change the stdlib (anything under ``lib/``), put a test in the
-file you changed. Add the tests under an ``when isMainModule:``
-condition so they only get executed when the tester is building the
-file. Each test should be in a separate ``block:`` statement, such that
-each has its own scope. Use boolean conditions and ``doAssert`` for the
-testing by itself, don't rely on echo statements or similar.
-
-Sample test:
-
-.. code-block:: nim
-
-  when isMainModule:
-    block: # newSeqWith tests
-      var seq2D = newSeqWith(4, newSeq[bool](2))
-      seq2D[0][0] = true
-      seq2D[1][0] = true
-      seq2D[0][1] = true
-      doAssert seq2D == @[@[true, true], @[true, false],
-                          @[false, false], @[false, false]]
-
-Compiler
---------
-
-The tests for the compiler work differently, they are all located in
-``tests/``. Each test has its own file, which is different from the
-stdlib tests. All test files are prefixed with ``t``. If you want to
-create a file for import into another test only, use the prefix ``m``.
-
-At the beginning of every test is the expected side of the test.
-Possible keys are:
-
-- output: The expected output, most likely via ``echo``
-- exitcode: Exit code of the test (via ``exit(number)``)
-- errormsg: The expected error message
-- file: The file the errormsg
-- line: The line the errormsg was produced at
-
-An example for a test:
-
-.. code-block:: nim
-
-  discard """
-    errormsg: "type mismatch: got (PTest)"
-  """
-
-  type
-    PTest = ref object
-
-  proc test(x: PTest, y: int) = nil
-
-  var buf: PTest
-  buf.test()
-
-Running tests
-=============
-
-You can run the tests with
-
-::
-
-  ./koch tests
-
-which will run a good subset of tests. Some tests may fail. If you
-only want to see the output of failing tests, go for
-
-::
-
-  ./koch tests --failing all
-
-You can also run only a single category of tests. A category is a subdirectory
-in the ``tests`` directory. There are a couple of special categories; for a
-list of these, see ``tests/testament/categories.nim``, at the bottom.
-
-::
-
-  ./koch tests c lib
-
-Comparing tests
-===============
-
-Because some tests fail in the current ``devel`` branch, not every fail
-after your change is necessarily caused by your changes.
-
-The tester can compare two test runs. First, you need to create the
-reference test. You'll also need to the commit id, because that's what
-the tester needs to know in order to compare the two.
-
-::
-
-  git checkout devel
-  DEVEL_COMMIT=$(git rev-parse HEAD)
-  ./koch tests
-
-Then switch over to your changes and run the tester again.
-
-::
-
-  git checkout your-changes
-  ./koch tests
-
-Then you can ask the tester to create a ``testresults.html`` which will
-tell you if any new tests passed/failed.
-
-::
-
-  ./koch tests --print html $DEVEL_COMMIT
-
-
-Deprecation
-===========
-
-Backward compatibility is important, so if you are renaming a proc or
-a type, you can use
-
-
-.. code-block:: nim
-
-  {.deprecated: [oldName: new_name].}
-
-Or you can simply use
-
-.. code-block:: nim
-
-  proc oldProc() {.deprecated.}
-
-to mark a symbol as deprecated. Works for procs/types/vars/consts,
-etc. Note that currently the ``deprecated`` statement does not work well with
-overloading so for routines the latter variant is better.
-
-
-`Deprecated <https://nim-lang.org/docs/manual.html#pragmas-deprecated-pragma>`_
-pragma in the manual.
-
-
-Documentation
-=============
-
-When contributing new procedures, be sure to add documentation, especially if
-the procedure is exported from the module. Documentation begins on the line
-following the ``proc`` definition, and is prefixed by ``##`` on each line.
-
-Code examples are also encouraged. The RestructuredText Nim uses has a special
-syntax for including examples.
-
-.. code-block:: nim
-
-  proc someproc*(): string =
-    ## Return "something"
-    ##
-    ## .. code-block:: nim
-    ##
-    ##  echo someproc() # "something"
-    result = "something" # single-hash comments do not produce documentation
-
-The ``.. code-block:: nim`` followed by a newline and an indentation instructs the
-``nim doc`` and ``nim doc2`` commands to produce syntax-highlighted example code with
-the documentation.
-
-When forward declaration is used, the documentation should be included with the
-first appearance of the proc.
-
-.. code-block:: nim
-
-  proc hello*(): string
-    ## Put documentation here
-  proc nothing() = discard
-  proc hello*(): string =
-    ## Ignore this
-    echo "hello"
-
-The preferred documentation style is to begin with a capital letter and use
-the imperative (command) form. That is, between:
-
-.. code-block:: nim
-
-  proc hello*(): string =
-    # Return "hello"
-    result = "hello"
-or
-
-.. code-block:: nim
-
-  proc hello*(): string =
-    # says hello
-    result = "hello"
-
-the first is preferred.
-
-The Git stuff
-=============
-
-General commit rules
---------------------
-
-1. All changes introduced by the commit (diff lines) must be related to the
-   subject of the commit.
-
-   If you change some other unrelated to the subject parts of the file, because
-   your editor reformatted automatically the code or whatever different reason,
-   this should be excluded from the commit.
-
-   *Tip:* Never commit everything as is using ``git commit -a``, but review
-   carefully your changes with ``git add -p``.
-
-2. Changes should not introduce any trailing whitespace.
-
-   Always check your changes for whitespace errors using ``git diff --check``
-   or add following ``pre-commit`` hook:
-
-   .. code-block:: sh
-
-      #!/bin/sh
-      git diff --check --cached || exit $?
-
-3. Describe your commit and use your common sense.
-
-.. include:: docstyle.rst
diff --git a/doc/destructors.md b/doc/destructors.md
new file mode 100644
index 000000000..e192fd362
--- /dev/null
+++ b/doc/destructors.md
@@ -0,0 +1,801 @@
+==================================
+Nim Destructors and Move Semantics
+==================================
+
+:Authors: Andreas Rumpf
+:Version: |nimversion|
+
+.. default-role:: code
+.. include:: rstcommon.rst
+.. contents::
+
+
+About this document
+===================
+
+This document describes the ARC/ORC Nim runtime which does
+not use classical GC algorithms anymore but is based on destructors and
+move semantics. The advantages are that Nim programs become
+oblivious to the involved heap sizes and programs are easier to write to make
+effective use of multi-core machines. As a nice bonus, files and sockets and
+the like can be written not to require manual `close` calls anymore.
+
+This document aims to be a precise specification about how
+move semantics and destructors work in Nim.
+
+
+Motivating example
+==================
+
+With the language mechanisms described here, a custom seq could be
+written as:
+
+  ```nim test
+  type
+    myseq*[T] = object
+      len, cap: int
+      data: ptr UncheckedArray[T]
+
+  proc `=destroy`*[T](x: myseq[T]) =
+    if x.data != nil:
+      for i in 0..<x.len: `=destroy`(x.data[i])
+      dealloc(x.data)
+
+  proc `=wasMoved`*[T](x: var myseq[T]) =
+    x.data = nil
+
+  proc `=trace`[T](x: var myseq[T]; env: pointer) =
+    # `=trace` allows the cycle collector `--mm:orc`
+    # to understand how to trace the object graph.
+    if x.data != nil:
+      for i in 0..<x.len: `=trace`(x.data[i], env)
+
+  proc `=copy`*[T](a: var myseq[T]; b: myseq[T]) =
+    # do nothing for self-assignments:
+    if a.data == b.data: return
+    `=destroy`(a)
+    `=wasMoved`(a)
+    a.len = b.len
+    a.cap = b.cap
+    if b.data != nil:
+      a.data = cast[typeof(a.data)](alloc(a.cap * sizeof(T)))
+      for i in 0..<a.len:
+        a.data[i] = b.data[i]
+
+  proc `=dup`*[T](a: myseq[T]): myseq[T] {.nodestroy.} =
+    # an optimized version of `=wasMoved(tmp); `=copy(tmp, src)`
+    # usually present if a custom `=copy` hook is overridden
+    result = myseq[T](len: a.len, cap: a.cap, data: nil)
+    if a.data != nil:
+      result.data = cast[typeof(result.data)](alloc(result.cap * sizeof(T)))
+      for i in 0..<result.len:
+        result.data[i] = `=dup`(a.data[i])
+
+  proc `=sink`*[T](a: var myseq[T]; b: myseq[T]) =
+    # move assignment, optional.
+    # Compiler is using `=destroy` and `copyMem` when not provided
+    `=destroy`(a)
+    a.len = b.len
+    a.cap = b.cap
+    a.data = b.data
+
+  proc add*[T](x: var myseq[T]; y: sink T) =
+    if x.len >= x.cap:
+      x.cap = max(x.len + 1, x.cap * 2)
+      x.data = cast[typeof(x.data)](realloc(x.data, x.cap * sizeof(T)))
+    x.data[x.len] = y
+    inc x.len
+
+  proc `[]`*[T](x: myseq[T]; i: Natural): lent T =
+    assert i < x.len
+    x.data[i]
+
+  proc `[]=`*[T](x: var myseq[T]; i: Natural; y: sink T) =
+    assert i < x.len
+    x.data[i] = y
+
+  proc createSeq*[T](elems: varargs[T]): myseq[T] =
+    result = myseq[T](
+      len: elems.len,
+      cap: elems.len,
+      data: cast[typeof(result.data)](alloc(result.cap * sizeof(T))))
+    for i in 0..<result.len: result.data[i] = elems[i]
+
+  proc len*[T](x: myseq[T]): int {.inline.} = x.len
+  ```
+
+
+Lifetime-tracking hooks
+=======================
+
+The memory management for Nim's standard `string` and `seq` types as
+well as other standard collections is performed via so-called
+"Lifetime-tracking hooks", which are particular [type bound operators](
+manual.html#procedures-type-bound-operators).
+
+There are 6 different hooks for each (generic or concrete) object type `T` (`T` can also be a
+`distinct` type) that are called implicitly by the compiler.
+
+(Note: The word "hook" here does not imply any kind of dynamic binding
+or runtime indirections, the implicit calls are statically bound and
+potentially inlined.)
+
+
+`=destroy` hook
+---------------
+
+A `=destroy` hook frees the object's associated memory and releases
+other associated resources. Variables are destroyed via this hook when
+they go out of scope or when the routine they were declared in is about
+to return.
+
+A `=destroy` hook is allowed to have a parameter of a `var T` or `T` type. Taking a `var T` type is deprecated. The prototype of this hook for a type `T` needs to be:
+
+  ```nim
+  proc `=destroy`(x: T)
+  ```
+
+The general pattern in `=destroy` looks like:
+
+  ```nim
+  proc `=destroy`(x: T) =
+    # first check if 'x' was moved to somewhere else:
+    if x.field != nil:
+      freeResource(x.field)
+  ```
+
+A `=destroy` is implicitly annotated with `.raises: []`; a destructor
+should not raise exceptions. For backwards compatibility the compiler
+produces a warning for a `=destroy` that does raise.
+
+A `=destroy` can explicitly list the exceptions it can raise, if any,
+but this of little utility as a raising destructor is implementation defined
+behavior. Later versions of the language specification might cover this case precisely.
+
+
+`=wasMoved` hook
+----------------
+
+A `=wasMoved` hook sets the object to a state that signifies to the destructor there is nothing to destroy.
+
+The prototype of this hook for a type `T` needs to be:
+
+  ```nim
+  proc `=wasMoved`(x: var T)
+  ```
+
+Usually some pointer field inside the object is set to `nil`:
+
+  ```nim
+  proc `=wasMoved`(x: var T) =
+    x.field = nil
+  ```
+
+
+`=sink` hook
+------------
+
+A `=sink` hook moves an object around, the resources are stolen from the source
+and passed to the destination. It is ensured that the source's destructor does
+not free the resources afterward by setting the object to its default value
+(the value the object's state started in). Setting an object `x` back to its
+default value is written as `wasMoved(x)`. When not provided the compiler
+is using a combination of `=destroy` and `copyMem` instead. This is efficient
+hence users rarely need to implement their own `=sink` operator, it is enough to
+provide `=destroy` and `=copy`, the compiler will take care of the rest.
+
+The prototype of this hook for a type `T` needs to be:
+
+  ```nim
+  proc `=sink`(dest: var T; source: T)
+  ```
+
+The general pattern in `=sink` looks like:
+
+  ```nim
+
+  proc `=sink`(dest: var T; source: T) =
+    `=destroy`(dest)
+    wasMoved(dest)
+    dest.field = source.field
+  ```
+
+**Note**: `=sink` does not need to check for self-assignments.
+How self-assignments are handled is explained later in this document.
+
+
+`=copy` hook
+------------
+
+The ordinary assignment in Nim conceptually copies the values. The `=copy` hook
+is called for assignments that couldn't be transformed into `=sink`
+operations.
+
+The prototype of this hook for a type `T` needs to be:
+
+  ```nim
+  proc `=copy`(dest: var T; source: T)
+  ```
+
+The general pattern in `=copy` looks like:
+
+  ```nim
+  proc `=copy`(dest: var T; source: T) =
+    # protect against self-assignments:
+    if dest.field != source.field:
+      `=destroy`(dest)
+      wasMoved(dest)
+      dest.field = duplicateResource(source.field)
+  ```
+
+The `=copy` proc can be marked with the `{.error.}` pragma. Then any assignment
+that otherwise would lead to a copy is prevented at compile-time. This looks like:
+
+  ```nim
+  proc `=copy`(dest: var T; source: T) {.error.}
+  ```
+
+but a custom error message (e.g., `{.error: "custom error".}`) will not be emitted
+by the compiler. Notice that there is no `=` before the `{.error.}` pragma.
+
+
+`=trace` hook
+-------------
+
+A custom **container** type can support Nim's cycle collector `--mm:orc` via
+the `=trace` hook. If the container does not implement `=trace`, cyclic data
+structures which are constructed with the help of the container might leak
+memory or resources, but memory safety is not compromised.
+
+The prototype of this hook for a type `T` needs to be:
+
+  ```nim
+  proc `=trace`(dest: var T; env: pointer)
+  ```
+
+`env` is used by ORC to keep track of its internal state, it should be passed around
+to calls of the built-in `=trace` operation.
+
+Usually there will only be a need for a custom `=trace` when a custom `=destroy` that deallocates
+manually allocated resources is also used, and then only when there is a chance of cyclic
+references from items within the manually allocated resources when it is desired that `--mm:orc`
+is able to break and collect these cyclic referenced resources. Currently however, there is a
+mutual use problem in that whichever of `=destroy`/`=trace` is used first will automatically
+create a version of the other which will then conflict with the creation of the second of the
+pair. The workaround for this problem is to forward declare the second of the "hooks" to
+prevent the automatic creation.
+
+The general pattern in using `=destroy` with `=trace` looks like:
+
+  ```nim
+  type
+    Test[T] = object
+      size: Natural
+      arr: ptr UncheckedArray[T] # raw pointer field
+
+  proc makeTest[T](size: Natural): Test[T] = # custom allocation...
+    Test[T](size: size, arr: cast[ptr UncheckedArray[T]](alloc0(sizeof(T) * size)))
+
+
+  proc `=destroy`[T](dest: Test[T]) =
+    if dest.arr != nil:
+      for i in 0 ..< dest.size: dest.arr[i].`=destroy`
+      dealloc dest.arr
+
+  proc `=trace`[T](dest: var Test[T]; env: pointer) =
+    if dest.arr != nil:
+      # trace the `T`'s which may be cyclic
+      for i in 0 ..< dest.size: `=trace`(dest.arr[i], env)
+
+  # following may be other custom "hooks" as required...
+  ```
+
+**Note**: The `=trace` hooks (which are only used by `--mm:orc`) are currently more experimental and less refined
+than the other hooks.
+
+
+`=dup` hook
+-----------
+
+A `=dup` hook duplicates an object. `=dup(x)` can be regarded as an optimization replacing a `wasMoved(dest); =copy(dest, x)` operation.
+
+The prototype of this hook for a type `T` needs to be:
+
+  ```nim
+  proc `=dup`(x: T): T
+  ```
+
+The general pattern in implementing `=dup` looks like:
+
+  ```nim
+  type
+    Ref[T] = object
+      data: ptr T
+      rc: ptr int
+
+  proc `=dup`[T](x: Ref[T]): Ref[T] =
+    result = x
+    if x.rc != nil:
+      inc x.rc[]
+  ```
+
+Move semantics
+==============
+
+A "move" can be regarded as an optimized copy operation. If the source of the
+copy operation is not used afterward, the copy can be replaced by a move. This
+document uses the notation `lastReadOf(x)` to describe that `x` is not
+used afterward. This property is computed by a static control flow analysis
+but can also be enforced by using `system.move` explicitly.
+
+One can query if the analysis is able to perform a move with `system.ensureMove`.
+`move` enforces a move operation and calls `=wasMoved` whereas `ensureMove` is
+an annotation that implies no runtime operation. An `ensureMove` annotation leads to a static error
+if the compiler cannot prove that a move would be safe.
+
+For example:
+
+  ```nim
+  proc main(normalParam: string; sinkParam: sink string) =
+    var x = "abc"
+    # valid:
+    let valid = ensureMove x
+    # invalid:
+    let invalid = ensureMove normalParam
+    # valid:
+    let alsoValid = ensureMove sinkParam
+  ```
+
+
+Swap
+====
+
+The need to check for self-assignments and also the need to destroy previous
+objects inside `=copy` and `=sink` is a strong indicator to treat
+`system.swap` as a builtin primitive of its own that simply swaps every
+field in the involved objects via `copyMem` or a comparable mechanism.
+In other words, `swap(a, b)` is **not** implemented
+as `let tmp = move(b); b = move(a); a = move(tmp)`.
+
+This has further consequences:
+
+* Objects that contain pointers that point to the same object are not supported
+  by Nim's model. Otherwise swapped objects would end up in an inconsistent state.
+* Seqs can use `realloc` in the implementation.
+
+
+Sink parameters
+===============
+
+To move a variable into a collection usually `sink` parameters are involved.
+A location that is passed to a `sink` parameter should not be used afterward.
+This is ensured by a static analysis over a control flow graph. If it cannot be
+proven to be the last usage of the location, a copy is done instead and this
+copy is then passed to the sink parameter.
+
+A sink parameter
+*may* be consumed once in the proc's body but doesn't have to be consumed at all.
+The reason for this is that signatures
+like `proc put(t: var Table; k: sink Key, v: sink Value)` should be possible
+without any further overloads and `put` might not take ownership of `k` if
+`k` already exists in the table. Sink parameters enable an affine type system,
+not a linear type system.
+
+The employed static analysis is limited and only concerned with local variables;
+however, object and tuple fields are treated as separate entities:
+
+  ```nim
+  proc consume(x: sink Obj) = discard "no implementation"
+
+  proc main =
+    let tup = (Obj(), Obj())
+    consume tup[0]
+    # ok, only tup[0] was consumed, tup[1] is still alive:
+    echo tup[1]
+  ```
+
+Sometimes it is required to explicitly `move` a value into its final position:
+
+  ```nim
+  proc main =
+    var dest, src: array[10, string]
+    # ...
+    for i in 0..high(dest): dest[i] = move(src[i])
+  ```
+
+An implementation is allowed, but not required to implement even more move
+optimizations (and the current implementation does not).
+
+
+Sink parameter inference
+========================
+
+The current implementation can do a limited form of sink parameter
+inference. But it has to be enabled via `--sinkInference:on`:option:, either
+on the command line or via a `push` pragma.
+
+To enable it for a section of code, one can
+use `{.push sinkInference: on.}` ... `{.pop.}`.
+
+The `.nosinks`:idx: pragma can be used to disable this inference
+for a single routine:
+
+  ```nim
+  proc addX(x: T; child: T) {.nosinks.} =
+    x.s.add child
+  ```
+
+The details of the inference algorithm are currently undocumented.
+
+
+Rewrite rules
+=============
+
+**Note**: There are two different allowed implementation strategies:
+
+1. The produced `finally` section can be a single section that is wrapped
+   around the complete routine body.
+2. The produced `finally` section is wrapped around the enclosing scope.
+
+The current implementation follows strategy (2). This means that resources are
+destroyed at the scope exit.
+
+
+    var x: T; stmts
+    ---------------             (destroy-var)
+    var x: T; try stmts
+    finally: `=destroy`(x)
+
+
+    g(f(...))
+    ------------------------    (nested-function-call)
+    g(let tmp;
+    bitwiseCopy tmp, f(...);
+    tmp)
+    finally: `=destroy`(tmp)
+
+
+    x = f(...)
+    ------------------------    (function-sink)
+    `=sink`(x, f(...))
+
+
+    x = lastReadOf z
+    ------------------          (move-optimization)
+    `=sink`(x, z)
+    `=wasMoved`(z)
+
+
+    v = v
+    ------------------   (self-assignment-removal)
+    discard "nop"
+
+
+    x = y
+    ------------------          (copy)
+    `=copy`(x, y)
+
+
+    f_sink(g())
+    -----------------------     (call-to-sink)
+    f_sink(g())
+
+
+    f_sink(notLastReadOf y)
+    --------------------------     (copy-to-sink)
+    (let tmp = `=dup`(y);
+    f_sink(tmp))
+
+
+    f_sink(lastReadOf y)
+    -----------------------     (move-to-sink)
+    f_sink(y)
+    `=wasMoved`(y)
+
+
+Object and array construction
+=============================
+
+Object and array construction is treated as a function call where the
+function has `sink` parameters.
+
+
+Destructor removal
+==================
+
+`=wasMoved(x)` followed by a `=destroy(x)` operation cancel each other
+out. An implementation is encouraged to exploit this in order to improve
+efficiency and code sizes. The current implementation does perform this
+optimization.
+
+
+Self assignments
+================
+
+`=sink` in combination with `=wasMoved` can handle self-assignments but
+it's subtle.
+
+The simple case of `x = x` cannot be turned
+into `=sink(x, x); =wasMoved(x)` because that would lose `x`'s value.
+The solution is that simple self-assignments that consist of
+
+- Symbols: `x = x`
+- Field access: `x.f = x.f`
+- Array, sequence or string access with indices known at compile-time: `x[0] = x[0]`
+
+are transformed into an empty statement that does nothing.
+The compiler is free to optimize further cases.
+
+The complex case looks like a variant of `x = f(x)`, we consider
+`x = select(rand() < 0.5, x, y)` here:
+
+
+  ```nim
+  proc select(cond: bool; a, b: sink string): string =
+    if cond:
+      result = a # moves a into result
+    else:
+      result = b # moves b into result
+
+  proc main =
+    var x = "abc"
+    var y = "xyz"
+    # possible self-assignment:
+    x = select(true, x, y)
+  ```
+
+Is transformed into:
+
+  ```nim
+  proc select(cond: bool; a, b: sink string): string =
+    try:
+      if cond:
+        `=sink`(result, a)
+        `=wasMoved`(a)
+      else:
+        `=sink`(result, b)
+        `=wasMoved`(b)
+    finally:
+      `=destroy`(b)
+      `=destroy`(a)
+
+  proc main =
+    var
+      x: string
+      y: string
+    try:
+      `=sink`(x, "abc")
+      `=sink`(y, "xyz")
+      `=sink`(x, select(true,
+        let blitTmp = x
+        `=wasMoved`(x)
+        blitTmp,
+        let blitTmp = y
+        `=wasMoved`(y)
+        blitTmp))
+      echo [x]
+    finally:
+      `=destroy`(y)
+      `=destroy`(x)
+  ```
+
+As can be manually verified, this transformation is correct for
+self-assignments.
+
+
+Lent type
+=========
+
+`proc p(x: sink T)` means that the proc `p` takes ownership of `x`.
+To eliminate even more creation/copy <-> destruction pairs, a proc's return
+type can be annotated as `lent T`. This is useful for "getter" accessors
+that seek to allow an immutable view into a container.
+
+The `sink` and `lent` annotations allow us to remove most (if not all)
+superfluous copies and destructions.
+
+`lent T` is like `var T` a hidden pointer. It is proven by the compiler
+that the pointer does not outlive its origin. No destructor call is injected
+for expressions of type `lent T` or of type `var T`.
+
+
+  ```nim test
+  type
+    Tree = object
+      kids: seq[Tree]
+
+  proc construct(kids: sink seq[Tree]): Tree =
+    result = Tree(kids: kids)
+    # converted into:
+    `=sink`(result.kids, kids); `=wasMoved`(kids)
+    `=destroy`(kids)
+
+  proc `[]`*(x: Tree; i: int): lent Tree =
+    result = x.kids[i]
+    # borrows from 'x', this is transformed into:
+    # result = addr x.kids[i]
+    # This means 'lent' is like 'var T' a hidden pointer.
+    # Unlike 'var' this hidden pointer cannot be used to mutate the object.
+
+  iterator children*(t: Tree): lent Tree =
+    for x in t.kids: yield x
+
+  proc main =
+    # everything turned into moves:
+    let t = construct(@[construct(@[]), construct(@[])])
+    echo t[0] # accessor does not copy the element!
+  ```
+
+
+The cursor pragma
+=================
+
+Under the `--mm:arc|orc`:option: modes Nim's `ref` type is implemented
+via the same runtime "hooks" and thus via reference counting.
+This means that cyclic structures cannot be freed
+immediately (`--mm:orc`:option: ships with a cycle collector).
+With the `cursor` pragma one can break up cycles declaratively:
+
+  ```nim
+  type
+    Node = ref object
+      left: Node # owning ref
+      right {.cursor.}: Node # non-owning ref
+  ```
+
+But please notice that this is not C++'s weak_ptr, it means the right field is not
+involved in the reference counting, it is a raw pointer without runtime checks.
+
+Automatic reference counting also has the disadvantage that it introduces overhead
+when iterating over linked structures. The `cursor` pragma can also be used
+to avoid this overhead:
+
+  ```nim
+  var it {.cursor.} = listRoot
+  while it != nil:
+    use(it)
+    it = it.next
+  ```
+
+In fact, `cursor` more generally prevents object construction/destruction pairs
+and so can also be useful in other contexts. The alternative solution would be to
+use raw pointers (`ptr`) instead which is more cumbersome and also more dangerous
+for Nim's evolution: Later on, the compiler can try to prove `cursor` pragmas
+to be safe, but for `ptr` the compiler has to remain silent about possible
+problems.
+
+
+Cursor inference / copy elision
+===============================
+
+The current implementation also performs `cursor` inference. Cursor inference is
+a form of copy elision.
+
+To see how and when we can do that, think about this question: In `dest = src` when
+do we really have to *materialize* the full copy? - Only if `dest` or `src` are mutated
+afterward. If `dest` is a local variable that is simple to analyze. And if `src` is a
+location derived from a formal parameter, we also know it is not mutated! In other
+words, we do a compile-time copy-on-write analysis.
+
+This means that "borrowed" views can be written naturally and without explicit pointer
+indirections:
+
+  ```nim
+  proc main(tab: Table[string, string]) =
+    let v = tab["key"] # inferred as cursor because 'tab' is not mutated.
+    # no copy into 'v', no destruction of 'v'.
+    use(v)
+    useItAgain(v)
+  ```
+
+
+Hook lifting
+============
+
+The hooks of a tuple type `(A, B, ...)` are generated by lifting the
+hooks of the involved types `A`, `B`, ... to the tuple type. In
+other words, a copy `x = y` is implemented
+as `x[0] = y[0]; x[1] = y[1]; ...`, likewise for `=sink` and `=destroy`.
+
+Other value-based compound types like `object` and `array` are handled
+correspondingly. For `object` however, the compiler-generated hooks
+can be overridden. This can also be important to use an alternative traversal
+of the involved data structure that is more efficient or in order to avoid
+deep recursions.
+
+
+
+Hook generation
+===============
+
+The ability to override a hook leads to a phase ordering problem:
+
+  ```nim
+  type
+    Foo[T] = object
+
+  proc main =
+    var f: Foo[int]
+    # error: destructor for 'f' called here before
+    # it was seen in this module.
+
+  proc `=destroy`[T](f: Foo[T]) =
+    discard
+  ```
+
+The solution is to define ``proc `=destroy`[T](f: Foo[T])`` before
+it is used. The compiler generates implicit
+hooks for all types in *strategic places* so that an explicitly provided
+hook that comes too "late" can be detected reliably. These *strategic places*
+have been derived from the rewrite rules and are as follows:
+
+- In the construct `let/var x = ...` (var/let binding)
+  hooks are generated for `typeof(x)`.
+- In `x = ...` (assignment) hooks are generated for `typeof(x)`.
+- In `f(...)` (function call) hooks are generated for `typeof(f(...))`.
+- For every sink parameter `x: sink T` the hooks are generated
+  for `typeof(x)`.
+
+
+nodestroy pragma
+================
+
+The experimental `nodestroy`:idx: pragma inhibits hook injections. This can be
+used to specialize the object traversal in order to avoid deep recursions:
+
+
+  ```nim test
+  type Node = ref object
+    x, y: int32
+    left, right: Node
+
+  type Tree = object
+    root: Node
+
+  proc `=destroy`(t: Tree) {.nodestroy.} =
+    # use an explicit stack so that we do not get stack overflows:
+    var s: seq[Node] = @[t.root]
+    while s.len > 0:
+      let x = s.pop
+      if x.left != nil: s.add(x.left)
+      if x.right != nil: s.add(x.right)
+      # free the memory explicitly:
+      `=dispose`(x)
+    # notice how even the destructor for 's' is not called implicitly
+    # anymore thanks to .nodestroy, so we have to call it on our own:
+    `=destroy`(s)
+  ```
+
+
+As can be seen from the example, this solution is hardly sufficient and
+should eventually be replaced by a better solution.
+
+
+Copy on write
+=============
+
+String literals are implemented as "copy on write".
+When assigning a string literal to a variable, a copy of the literal won't be created.
+Instead the variable simply points to the literal.
+The literal is shared between different variables which are pointing to it.
+The copy operation is deferred until the first write.
+
+For example:
+
+  ```nim
+  var x = "abc"  # no copy
+  var y = x      # no copy
+  y[0] = 'h'     # copy
+  ```
+
+The abstraction fails for `addr x` because whether the address is going to be used for mutations is unknown.
+`prepareMutation` needs to be called before the "address of" operation. For example:
+
+  ```nim
+  var x = "abc"
+  var y = x
+
+  prepareMutation(y)
+  moveMem(addr y[0], addr x[0], 3)
+  assert y == "abc"
+  ```
diff --git a/doc/docgen.md b/doc/docgen.md
new file mode 100644
index 000000000..3cc75fc18
--- /dev/null
+++ b/doc/docgen.md
@@ -0,0 +1,891 @@
+===================================
+   Nim DocGen Tools Guide
+===================================
+
+:Author: Erik O'Leary
+:Version: |nimversion|
+
+.. default-role:: code
+.. include:: rstcommon.rst
+.. contents::
+
+.. importdoc:: markdown_rst.md, compiler/docgen.nim
+
+Introduction
+============
+
+This document describes the `documentation generation tools`:idx: built into
+the [Nim compiler](nimc.html), which can generate HTML, Latex and JSON output
+from input ``.nim`` files and projects.
+The output documentation will include the module
+dependencies (`import`), any top-level documentation comments (`##`), and
+exported symbols (`*`), including procedures, types, and variables.
+
+===================   ==============
+command               output format
+===================   ==============
+`nim doc`:cmd:        ``.html`` HTML
+`nim doc2tex`:cmd:    ``.tex`` LaTeX
+`nim jsondoc`:cmd:    ``.json`` JSON
+===================   ==============
+
+Nim can generate HTML and LaTeX from input Markdown and
+RST (reStructuredText) files as well, which is intended for writing
+standalone documents like user's guides and technical specifications.
+See [Nim-flavored Markdown and reStructuredText] document for the description
+of this feature and particularly section [Command line usage] for the full
+list of supported commands.
+
+Quick start
+-----------
+
+Generate HTML documentation for a file:
+
+  ```cmd
+  nim doc <filename>.nim
+  ```
+
+Generate HTML documentation for a whole project:
+
+  ```cmd
+  # delete any htmldocs/*.idx file before starting
+  nim doc --project --index:on --git.url:<url> --git.commit:<tag> --outdir:htmldocs <main_filename>.nim
+  # this will generate html files, a theindex.html index, css and js under `htmldocs`
+  # See also `--docroot` to specify a relative root.
+  # to get search (dochacks.js) to work locally, you need a server otherwise
+  # CORS will prevent opening file:// urls; this works:
+  python3 -m http.server 7029 --directory htmldocs
+  # When --outdir is omitted it defaults to $projectPath/htmldocs,
+  # or `$nimcache/htmldocs` with `--usenimcache` which avoids clobbering your sources;
+  # and likewise without `--project`.
+  # Adding `-r` will open in a browser directly.
+  # Use `--showNonExports` to show non-exported fields of an exported type.
+  ```
+
+Documentation Comments
+----------------------
+
+Any comments which are preceded by a double-hash (`##`), are interpreted as
+documentation.  Comments are parsed as RST (see [reference](
+http://docutils.sourceforge.net/docs/user/rst/quickref.html)), providing
+Nim module authors the ability to easily generate richly formatted
+documentation with only their well-documented code!
+Basic Markdown syntax is also supported inside the doc comments.
+
+Example:
+
+  ```nim
+  type Person* = object
+    ## This type contains a description of a person
+    name: string
+    age: int
+  ```
+
+Outputs:
+
+    Person* = object
+      name: string
+      age: int
+
+  This type contains a description of a person
+
+Field documentation comments can be added to fields like so:
+
+  ```nim
+  var numValues: int ## \
+    ## `numValues` stores the number of values
+  ```
+
+Note that without the `*` following the name of the type, the documentation for
+this type would not be generated. Documentation will only be generated for
+*exported* types/procedures/etc.
+
+It's recommended to always add exactly **one** space after `##` for readability
+of comments — this extra space will be cropped from the parsed comments and
+won't influence RST formatting.
+
+.. note:: Generally, this baseline indentation level inside a documentation
+   comment may not be 1: it can be any since it is determined by the offset
+   of the first non-whitespace character in the comment.
+   After that indentation **must** be consistent on the following lines of
+   the same comment.
+   If you still need to add an additional indentation at the very beginning
+   (for RST block quote syntax) use backslash \\ before it:
+
+     ```nim
+     ## \
+     ##
+     ##    Block quote at the first line.
+     ##
+     ## Paragraph.
+     ```
+
+Structuring output directories
+------------------------------
+
+Basic directory for output is set by `--outdir:OUTDIR`:option: switch,
+by default `OUTDIR` is ``htmldocs`` sub-directory in the directory of
+the processed file.
+
+There are 2 basic options as to how generated HTML output files are stored:
+
+1) complex hierarchy when docgen-compiling with `--project`:option:,
+   which follows directory structure of the project itself.
+   So `nim doc`:cmd: replicates project's directory structure
+   inside `--outdir:OUTDIR`:option: directory.
+   `--project`:option: is well suited for projects that have 1 main module.
+   File name clashes are impossible in this case.
+
+2) flattened structure, where user-provided script goes through all
+   needed input files and calls commands like `nim doc`:cmd:
+   with `--outdir:OUTDIR`:option: switch, thus putting all HTML (and
+   ``.idx``) files into 1 directory.
+
+   .. Important:: Make sure that you don't have files with same base name
+     like ``x.nim`` and ``x.md`` in the same package, otherwise you'll
+     have name conflict for ``x.html``.
+
+   .. Tip:: To structure your output directories and avoid file name
+     clashes you can split your project into
+     different *packages* -- parts of your repository that are
+     docgen-compiled with different `--outdir:OUTDIR`:option: options.
+
+     An example of such strategy is Nim repository itself which has:
+
+     * its stdlib ``.nim`` files from different directories and ``.md``
+       documentation from ``doc/`` directory are all docgen-compiled
+       into `--outdir:web/upload/<version>/`:option: directory
+     * its ``.nim`` files from ``compiler/`` directory are docgen-compiled
+       into `--outdir:web/upload/<version>/compiler/`:option: directory.
+       Interestingly, it's compiled with complex hierarchy using
+       `--project`:option: switch.
+
+     Contents of ``web/upload/<version>`` are then deployed into Nim's
+     Web server.
+
+     This output directory structure allows to work correctly with files like
+     ``compiler/docgen.nim`` (implementation) and ``doc/docgen.md`` (user
+     documentation) in 1 repository.
+
+
+Index files
+-----------
+
+Index (``.idx``) files are used for 2 different purposes:
+
+1. easy cross-referencing between different ``.nim`` and/or ``.md`` / ``.rst``
+   files described in [Nim external referencing]
+2. creating a whole-project index for searching of symbols and keywords,
+   see [Buildindex command].
+
+
+Document Types
+==============
+
+Example of Nim file input
+-------------------------
+
+The following examples will generate documentation for this sample
+*Nim* module, aptly named ``doc/docgen_sample.nim``:
+
+   ```nim file=docgen_sample.nim
+   ```
+
+All the below commands save their output to ``htmldocs`` directory relative to
+the directory of file;
+hence the output for this sample will be in ``doc/htmldocs``.
+
+HTML
+----
+
+The generation of HTML documents is done via the `doc`:option: command. This command
+takes either a single ``.nim`` file, outputting a single ``.html`` file with the same
+base filename, or multiple ``.nim`` files, outputting multiple ``.html`` files and,
+optionally, an index file.
+
+The `doc`:option: command:
+
+  ```cmd
+  nim doc docgen_sample.nim
+  ```
+
+Partial Output:
+
+    ...
+    proc helloWorld(times: int) {.raises: [], tags: [].}
+    ...
+
+The full output can be seen here: [docgen_sample.html](docgen_sample.html).
+It runs after semantic checking and includes pragmas attached implicitly by the
+compiler.
+
+LaTeX
+-----
+
+LaTeX files are intended to be converted to PDF, especially for offline
+reading or making hard copies. (LaTeX output is oftentimes better than
+HTML -> PDF conversion).
+
+The `doc2tex`:option: command:
+
+  ```cmd
+  nim doc2tex docgen_sample.nim
+  cd htmldocs
+  xelatex docgen_sample.tex
+  xelatex docgen_sample.tex
+  # It is usually necessary to run `xelatex` 2 times (or even 3 times for
+  # large documents) to get all labels generated.
+  # That depends on this warning in the end of `xelatex` output:
+  #   LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right.
+  ```
+
+The output is ``docgen_sample.pdf``.
+
+JSON
+----
+
+The generation of JSON documents is done via the `jsondoc`:option: command.
+This command takes in a ``.nim`` file and outputs a ``.json`` file with
+the same base filename.
+Note that this tool is built off of the `doc`:option: command
+(previously `doc2`:option:), and contains the same information.
+
+The `jsondoc`:option: command:
+
+  ```cmd
+  nim jsondoc docgen_sample.nim
+  ```
+
+Output:
+
+    {
+      "orig": "docgen_sample.nim",
+      "nimble": "",
+      "moduleDescription": "This module is a sample",
+      "entries": [
+        {
+          "name": "helloWorld",
+          "type": "skProc",
+          "line": 5,
+          "col": 0,
+          "description": "Takes an integer and outputs as many &quot;hello world!&quot;s",
+          "code": "proc helloWorld(times: int) {.raises: [], tags: [].}"
+        }
+      ]
+    }
+
+Similarly to the old `doc`:option: command, the old `jsondoc`:option: command has been
+renamed to `jsondoc0`:option:.
+
+The `jsondoc0`:option: command:
+
+  ```cmd
+  nim jsondoc0 docgen_sample.nim
+  ```
+
+Output:
+
+    [
+      {
+        "comment": "This module is a sample."
+      },
+      {
+        "name": "helloWorld",
+        "type": "skProc",
+        "description": "Takes an integer and outputs as many &quot;hello world!&quot;s",
+        "code": "proc helloWorld*(times: int)"
+      }
+    ]
+
+Note that the `jsondoc`:option: command outputs its JSON without pretty-printing it,
+while `jsondoc0`:option: outputs pretty-printed JSON.
+
+
+Simple documentation links
+==========================
+
+It's possible to use normal Markdown/RST syntax to *manually*
+reference Nim symbols using HTML anchors, however Nim has an *automatic*
+facility that makes referencing inside ``.nim`` and ``.md/.rst`` files and
+between them easy and seamless.
+The point is that such links will be resolved automatically
+by `nim doc`:cmd: (or `md2html`:option:, or `jsondoc`:option:,
+or `doc2tex`:option:, ...). And, unlike manual links, such automatic
+links **check** that their target exists -- a warning is emitted for
+any broken link, so you avoid broken links in your project.
+
+Nim treats both ``.md/.rst`` files and ``.nim`` modules (their doc comment
+part) as *documents* uniformly.
+Hence all directions of referencing are equally possible having the same syntax:
+
+1. ``.md/rst`` -> itself (internal). See [Markup local referencing].
+2. ``.md/rst`` -> external ``.md/rst``. See [Markup external referencing].
+   To summarize, referencing in `.md`/`.rst` files was already described in
+   [Nim-flavored Markdown and reStructuredText]
+   (particularly it described usage of index files for referencing),
+   while in this document we focus on Nim-specific details.
+3. ``.md/rst`` -> external ``.nim``. See [Nim external referencing].
+4. ``.nim`` -> itself (internal). See [Nim local referencing].
+5. ``.nim`` -> external ``.md/rst``. See [Markup external referencing].
+6. ``.nim`` -> external ``.nim``. See [Nim external referencing].
+
+To put it shortly, local referencing always works out of the box,
+external referencing requires to use ``.. importdoc:: <file>``
+directive to import `file` and to ensure that the corresponding
+``.idx`` file was generated.
+
+Syntax for referencing is basically the same as for normal markup.
+Recall from [Referencing] that our parser supports two equivalent syntaxes
+for referencing, Markdown and RST one.
+So to reference ``proc f`` one should use something like that,
+depending on markup type:
+
+    Markdown                    RST
+
+    Ref. [proc f] or [f]        Ref. `proc f`_ or just f_ for a one-word case
+
+Nim local referencing
+---------------------
+
+You can reference Nim identifiers from Nim documentation comments
+inside their ``.nim`` file (or inside a ``.rst`` file included from
+a ``.nim``).
+This pertains to any exported symbol like `proc`, `const`, `iterator`, etc.
+Link text is either one word or a group of words enclosed by delimiters
+(brackets ``[...]`` for Markdown or backticks `\`...\`_` for RST).
+Link text will be displayed *as is* while *link target* will be set to
+the anchor [^1] of Nim symbol that corresponds to link text.
+
+[^1] anchors' format is described in [HTML anchor generation] section below.
+
+If you have a constant:
+
+  ```Nim
+  const pi* = 3.14
+  ```
+
+then it should be referenced in one of the 2 forms:
+
+A. non-qualified (no symbol kind specification):
+
+       pi_
+
+B. qualified (with symbol kind specification):
+
+       `const pi`_
+
+For routine kinds there are more options. Consider this definition:
+
+  ```Nim
+  proc foo*(a: int, b: float): string
+  ```
+
+Generally following syntax is allowed for referencing `foo`:
+
+*  short (without parameters):
+
+   A. non-qualified:
+
+          foo_
+
+   B. qualified:
+
+          `proc foo`_
+
+*  longer variants (with parameters):
+
+   A. non-qualified:
+
+      1) specifying parameters names:
+
+             `foo(a, b)`_
+
+      2) specifying parameters types:
+
+             `foo(int, float)`_
+
+      3) specifying both names and types:
+
+             `foo(a: int, b: float)`_
+
+      4) output parameter can also be specified if you wish:
+
+             `foo(a: int, b: float): string`_
+
+   B. qualified: all 4 options above are valid.
+      Particularly you can use the full format:
+
+          `proc foo(a: int, b: float): string`_
+
+.. Tip:: Avoid cluttering your text with extraneous information by using
+   one of shorter forms:
+
+       binarySearch_
+       `binarySearch(a, key, cmp)`_
+
+   Brevity is better for reading! If you use a short form and have an
+   ambiguity problem (see below) then just add some additional info.
+
+Symbol kind like `proc` can also be specified in the postfix form:
+
+    `foo proc`_
+    `walkDir(d: string) iterator`_
+
+.. Warning:: An ambiguity in resolving documentation links may arise because of:
+
+   1. clash with other RST anchors
+      * manually setup anchors
+      * automatically set up, e.g. section names
+   2. collision with other Nim symbols:
+
+      * routines with different parameters can exist e.g. for
+        `proc` and `template`. In this case they are split between their
+        corresponding sections in output file. Qualified references are
+        useful in this case -- just disambiguate by referring to these
+        sections explicitly:
+
+            See `foo proc`_ and `foo template`_.
+
+      * because in Nim `proc` and `iterator` belong to different namespaces,
+        so there can be a collision even if parameters are the same.
+        Use `\`proc foo\`_`:literal: or `\`iterator foo\`_`:literal: then.
+
+   Any ambiguity is always reported with Nim compiler warnings and an anchor
+   with higher priority is selected. Manual anchors have highest
+   priority, then go automatic RST anchors; then Nim-generated anchors
+   (while procs have higher priority than other Nim symbol kinds).
+
+Generic parameters can also be used. All in all, this long form will be
+recognized fine:
+
+    `proc binarySearch*[T; K](a: openArray[T], key: K, cmp: proc(T, K)): int`_
+
+**Limitations**:
+
+1. The parameters of a nested routine type can be specified only with types
+   (without parameter names, see form A.2 above).
+   E.g. for this signature:
+
+      ```Nim
+      proc binarySearch*[T, K](a: openArray[T]; key: K;
+                               cmp: proc (x: T; y: K): int {.closure.}): int
+                                          ~~    ~~   ~~~~~
+      ```
+
+   you cannot use names underlined by `~~` so it must be referenced with
+   ``cmp: proc(T, K)``. Hence these forms are valid:
+
+       `binarySearch(a: openArray[T], key: K, cmp: proc(T, K))`_
+       `binarySearch(openArray[T], K, proc(T, K))`_
+       `binarySearch(a, key, cmp)`_
+2. Default values in routine parameters are not recognized, one needs to
+   specify the type and/or name instead. E.g. for referencing `proc f(x = 7)`
+   use one of the mentioned forms:
+
+       `f(int)`_ or `f(x)`_ or `f(x: int)`_.
+3. Generic parameters must be given the same way as in the
+   definition of referenced symbol.
+
+   * their names should be the same
+   * parameters list should be given the same way, e.g. without substitutions
+     between commas (,) and semicolons (;).
+
+.. Note:: A bit special case is operators
+   (as their signature is also defined with `\``):
+
+      ```Nim
+      func `$`(x: MyType): string
+      func `[]`*[T](x: openArray[T]): T
+      ```
+
+   A short form works without additional backticks:
+
+       `$`_
+       `[]`_
+
+   However for fully-qualified reference copy-pasting backticks (`) into other
+   backticks will not work in our RST parser (because we use Markdown-like
+   inline markup rules). You need either to delete backticks or keep
+   them and escape with backslash \\:
+
+       no backticks: `func $`_
+       escaped:  `func \`$\``_
+       no backticks: `func [][T](x: openArray[T]): T`_
+       escaped:  `func \`[]\`[T](x: openArray[T]): T`_
+
+.. Note:: Types that defined as `enum`, or `object`, or `tuple` can also be
+   referenced with those names directly (instead of `type`):
+
+       type CopyFlag = enum
+         ...
+       ## Ref. `CopyFlag enum`_
+
+Nim external referencing
+------------------------
+
+Just like for [Markup external referencing], which saves markup anchors,
+the Nim symbols are also saved in ``.idx`` files, so one needs
+to generate them beforehand, and they should be loaded by
+an ``.. importdoc::`` directive. Arguments to ``.. importdoc::`` is a
+comma-separated list of Nim modules or Markdown/RST documents.
+
+`--index:only`:option: tells Nim to only generate ``.idx`` file and
+do **not** attempt to generate HTML/LaTeX output.
+For ``.nim`` modules there are 2 alternatives to work with ``.idx`` files:
+
+1. using [Project switch] implies generation of ``.idx`` files,
+   however, if ``importdoc`` is called on upper modules as its arguments,
+   their ``.idx`` are not yet created. Thus one should generate **all**
+   required ``.idx`` first:
+     ```cmd
+     nim doc --project --index:only <main>.nim
+     nim doc --project <main>.nim
+     ```
+2. or run `nim doc --index:only <module.nim>`:cmd: command for **all** (used)
+   Nim modules in your project. Then run `nim doc <module.nim>` on them for
+   output HTML generation.
+
+   .. Warning:: A mere `nim doc --index:on`:cmd: may fail on an attempt to do
+      ``importdoc`` from another module (for which ``.idx`` was not yet
+      generated), that's why `--index:only`:option: shall be used instead.
+
+   For ``.md``/``.rst`` markup documents point 2 is the only option.
+
+Then, you can freely use something like this in ``your_module.nim``:
+
+  ```nim
+  ## .. importdoc::  user_manual.md, another_module.nim
+
+  ...
+  ## Ref. [some section from User Manual].
+
+  ...
+  ## Ref. [proc f]
+  ## (assuming you have a proc `f` in ``another_module``).
+  ```
+
+and compile it by `nim doc`:cmd:. Note that link text will
+be automatically prefixed by the module name of symbol,
+so you will see something like "Ref. [another_module: proc f](#)"
+in the generated output.
+
+It's also possible to reference a whole module by prefixing or
+suffixing full canonical module name with "module":
+
+    Ref. [module subdir/name] or [subdir/name module].
+
+Markup documents as a whole can be referenced just by their title
+(or by their file name if the title was not set) without any prefix.
+
+.. Tip:: During development process the stage of ``.idx`` files generation
+  can be done only *once*, after that you use already generated ``.idx``
+  files while working with a document *being developed* (unless you do
+  incompatible changes to *referenced* documents).
+
+.. Hint:: After changing a *referenced* document file one may need
+  to regenerate its corresponding ``.idx`` file to get correct results.
+  Of course, when referencing *internally* inside any given ``.nim`` file,
+  it's not needed, one can even immediately use any freshly added anchor
+  (a document's own ``.idx`` file is not used for resolving its internal links).
+
+If an ``importdoc`` directive fails to find a ``.idx``, then an error
+is emitted.
+
+In case of such compilation failures please note that:
+
+* **all** relative paths, given to ``importdoc``, relate to insides of
+  ``OUTDIR``, and **not** project's directory structure.
+
+* ``importdoc`` searches for ``.idx`` in `--outdir:OUTDIR`:option: directory
+  (``htmldocs`` by default) and **not** around original modules, so:
+
+  .. Tip:: look into ``OUTDIR`` to understand what's going on.
+
+* also keep in mind that ``.html`` and ``.idx`` files should always be
+  output to the same directory, so check this and, if it's not true, check
+  that both runs *with* and *without* `--index:only`:option: have all
+  other options the same.
+
+To summarize, for 2 basic options of [Structuring output directories]
+compilation options are different:
+
+1) complex hierarchy with `--project`:option: switch.
+
+   As the **original** project's directory structure is replicated in
+   `OUTDIR`, all passed paths are related to this structure also.
+
+   E.g. if a module ``path1/module.nim`` does
+   ``.. importdoc:: path2/another.nim`` then docgen tries to load file
+   ``OUTDIR/path1/path2/another.idx``.
+
+   .. Note:: markup documents are just placed into the specified directory
+     `OUTDIR`:option: by default (i.e. they are **not** affected by
+     `--project`:option:), so if you have ``PROJECT/doc/manual.md``
+     document and want to use complex hierarchy (with ``doc/``),
+     compile it with `--docroot`:option:\:
+       ```cmd
+       # 1st stage
+       nim md2html --outdir:OUTDIR --docroot:/absolute/path/to/PROJECT \
+            --index:only PROJECT/doc/manual.md
+       ...
+       # 2nd stage
+       nim md2html --outdir:OUTDIR --docroot:/absolute/path/to/PROJECT \
+                         PROJECT/doc/manual.md
+       ```
+
+     Then the output file will be placed as ``OUTDIR/doc/manual.idx``.
+     So if you have ``PROJECT/path1/module.nim``, then ``manual.md`` can
+     be referenced as ``../doc/manual.md``.
+
+2) flattened structure.
+
+   E.g. if a module ``path1/module.nim`` does
+   ``.. importdoc:: path2/another.nim`` then docgen tries to load
+   ``OUTDIR/path2/another.idx``, so the path ``path1``
+   does not matter and providing ``path2`` can be useful only
+   in the case it contains another package that was placed there
+   using `--outdir:OUTDIR/path2`:option:.
+
+   The links' text will be prefixed as ``another: ...`` in both cases.
+
+   .. Warning:: Again, the same `--outdir:OUTDIR`:option: option should
+     be provided to both `doc --index:only`:option: /
+     `md2html --index:only`:option: and final generation by
+     `doc`:option:/`md2html`:option: inside 1 package.
+
+To temporarily disable ``importdoc``, e.g. if you don't need
+correct link resolution at the moment, use a `--noImportdoc`:option: switch
+(only warnings about unresolved links will be generated for external references).
+
+Related Options
+===============
+
+Project switch
+--------------
+
+  ```cmd
+  nim doc --project filename.nim
+  ```
+
+This will recursively generate documentation of all Nim modules imported
+into the input module that belong to the Nimble package that ``filename.nim``
+belongs to. The index files and the corresponding ``theindex.html`` will
+also be generated.
+
+
+Index switch
+------------
+
+  ```cmd
+  nim doc --index:on filename.nim
+  ```
+
+This will generate an index of all the exported symbols in the input Nim
+module, and put it into a neighboring file with the extension of ``.idx``. The
+index file is line-oriented (newlines have to be escaped). Each line
+represents a tab-separated record of several columns, the first two mandatory,
+the rest optional. See the [Index (idx) file format] section for details.
+
+.. Note:: `--index`:option: switch only affects creation of ``.idx``
+  index files, while user-searchable Index HTML file is created by
+  `buildIndex`:option: command.
+
+Buildindex command
+------------------
+
+Once index files have been generated for one or more modules, the Nim
+compiler command `nim buildIndex directory`:cmd: can be run to go over all the index
+files in the specified directory to generate a [theindex.html](theindex.html)
+file:
+
+  ```cmd
+  nim buildIndex -o:path/to/htmldocs/theindex.html path/to/htmldocs
+  ```
+
+See source switch
+-----------------
+
+  ```cmd
+  nim doc --git.url:<url> filename.nim
+  ```
+
+With the `git.url`:option: switch the *See source* hyperlink will appear below each
+documented item in your source code pointing to the implementation of that
+item on a GitHub repository.
+You can click the link to see the implementation of the item.
+
+The `git.commit`:option: switch overrides the hardcoded `devel` branch in
+``config/nimdoc.cfg``.
+This is useful to link to a different branch e.g. `--git.commit:master`:option:,
+or to a tag e.g. `--git.commit:1.2.3`:option: or a commit.
+
+Source URLs are generated as ``href="${url}/tree/${commit}/${path}#L${line}"``
+by default and thus compatible with GitHub but not with GitLab.
+
+Similarly, `git.devel`:option: switch overrides the hardcoded `devel` branch
+for the `Edit` link which is also useful if you have a different working
+branch than `devel` e.g. `--git.devel:master`:option:.
+
+Edit URLs are generated as ``href="${url}/tree/${devel}/${path}#L${line}"``
+by default.
+
+You can edit ``config/nimdoc.cfg`` and modify the ``doc.item.seesrc`` value
+with a hyperlink to your own code repository.
+
+In the case of Nim's own documentation, the `commit` value is just a commit
+hash to append to a formatted URL to https://github.com/nim-lang/Nim.
+
+
+Other Input Formats
+===================
+
+The *Nim compiler* also has support for RST (reStructuredText) files with
+the `rst2html`:option: and `rst2tex`:option: commands. Documents like this one are
+initially written in a dialect of RST which adds support for Nim source code
+highlighting with the ``.. code-block:: nim`` prefix. ``code-block`` also
+supports highlighting of a few other languages supported by the
+[packages/docutils/highlite module](highlite.html).
+
+See [Markdown and RST markup languages](markdown_rst.html) for
+usage of those commands.
+
+HTML anchor generation
+======================
+
+When you run the `rst2html`:option: command, all sections in the RST document will
+get an anchor you can hyperlink to. Usually, you can guess the anchor lower
+casing the section title and replacing spaces with dashes, and in any case, you
+can get it from the table of contents. But when you run the `doc`:option:
+command to generate API documentation, some symbol get one or two anchors at
+the same time: a numerical identifier, or a plain name plus a complex name.
+
+The numerical identifier is just a random number. The number gets assigned
+according to the section and position of the symbol in the file being processed
+and you should not rely on it being constant: if you add or remove a symbol the
+numbers may shuffle around.
+
+The plain name of a symbol is a simplified version of its fully exported
+signature. Variables or constants have the same plain name symbol as their
+complex name. The plain name for procs, templates, and other callable types
+will be their unquoted value after removing parameters, return types, and
+pragmas. The plain name allows short and nice linking of symbols that works
+unless you have a module with collisions due to overloading.
+
+If you hyperlink a plain name symbol and there are other matches on the same
+HTML file, most browsers will go to the first one. To differentiate the rest,
+you will need to use the complex name. A complex name for a callable type is
+made up of several parts:
+
+  (**plain symbol**)(**.type**),(**first param**)?(**,param type**)\*
+
+The first thing to note is that all callable types have at least a comma, even
+if they don't have any parameters. If there are parameters, they are
+represented by their types and will be comma-separated. To the plain symbol a
+suffix may be added depending on the type of the callable:
+
+==============   ==============
+Callable type    Suffix
+==============   ==============
+`proc`, `func`   *empty string*
+`macro`          ``.m``
+`method`         ``.e``
+`iterator`       ``.i``
+`template`       ``.t``
+`converter`      ``.c``
+==============   ==============
+
+The relationship of type to suffix is made by the proc `complexName` in the
+``compiler/docgen.nim`` file. Here are some examples of complex names for
+symbols in the [system module](system.html).
+
+* `type SomeSignedInt = int | int8 | int16 | int32 | int64` **=>**
+  [#SomeSignedInt](system.html#SomeSignedInt)
+* `var globalRaiseHook: proc (e: ref E_Base): bool {.nimcall.}` **=>**
+  [#globalRaiseHook](system.html#globalRaiseHook)
+* `const NimVersion = "0.0.0"` **=>**
+  [#NimVersion](system.html#NimVersion)
+* `proc getTotalMem(): int {.rtl, raises: [], tags: [].}` **=>**
+  [#getTotalMem](system.html#getTotalMem)
+* `proc len[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.}` **=>**
+  [#len,seq[T]](system.html#len,seq[T])
+* `iterator pairs[T](a: seq[T]): tuple[key: int, val: T] {.inline.}` **=>**
+  [#pairs.i,seq[T]](iterators.html#pairs.i,seq[T])
+* `template newException[](exceptn: typedesc; message: string;
+    parentException: ref Exception = nil): untyped` **=>**
+  [#newException.t,typedesc,string,ref.Exception](
+  system.html#newException.t,typedesc,string,ref.Exception)
+
+
+Index (idx) file format
+=======================
+
+Files with the ``.idx`` extension are generated when you use the [Index
+switch] along with commands to generate
+documentation from source or text files. You can programmatically generate
+indices with the [setIndexTerm()](
+rstgen.html#setIndexTerm,RstGenerator,string,string,string,string,string)
+and `writeIndexFile() <rstgen.html#writeIndexFile,RstGenerator,string>`_ procs.
+The purpose of `idx` files is to hold the interesting symbols and their HTML
+references so they can be later concatenated into a big index file with
+[mergeIndexes()](rstgen.html#mergeIndexes,string).  This section documents
+the file format in detail.
+
+Index files are line-oriented and tab-separated (newline and tab characters
+have to be escaped). Each line represents a record with 6 fields.
+The content of these columns is:
+
+0. Discriminator tag denoting type of the index entry, allowed values are:
+   `markupTitle`
+   : a title for ``.md``/``.rst`` document
+   `nimTitle`
+   : a title of ``.nim`` module
+   `heading`
+   : heading of sections, can be both in Nim and markup files
+   `idx`
+   : terms marked with :idx: role
+   `nim`
+   : a Nim symbol
+   `nimgrp`
+   : a Nim group for overloadable symbols like `proc`s
+1. Mandatory term being indexed. Terms can include quoting according to
+   Nim's rules (e.g. \`^\`).
+2. Base filename plus anchor hyperlink (e.g. ``algorithm.html#*,int,SortOrder``).
+3. Optional human-readable string to display as a hyperlink. If the value is not
+   present or is the empty string, the hyperlink will be rendered
+   using the term. Prefix whitespace indicates that this entry is
+   not for an API symbol but for a TOC entry.
+4. Optional title or description of the hyperlink. Browsers usually display
+   this as a tooltip after hovering a moment over the hyperlink.
+5. A line number of file where the entry was defined.
+
+The index generation tools differentiate between documentation
+generated from ``.nim`` files and documentation generated from ``.md`` or
+``.rst`` files by tag `nimTitle` or `markupTitle` in the 1st line of
+the ``.idx`` file.
+
+.. TODO Normal symbols are added to the index with surrounding whitespaces removed. An
+  exception to this are the table of content (TOC) entries. TOC entries are added to
+  the index file with their third column having as much prefix spaces as their
+  level is in the TOC (at least 1 character). The prefix whitespace helps to
+  filter TOC entries from API or text symbols. This is important because the
+  amount of spaces is used to replicate the hierarchy for document TOCs in the
+  final index, and TOC entries found in ``.nim`` files are discarded.
+
+
+Additional resources
+====================
+
+* [Nim Compiler User Guide](nimc.html#compiler-usage-commandminusline-switches)
+
+* already mentioned documentation for
+  [Markdown and RST markup languages](markdown_rst.html), which also
+  contains the list of implemented features of these markup languages.
+
+* the implementation is in [module compiler/docgen].
+
+The output for HTML and LaTeX comes from the ``config/nimdoc.cfg`` and
+``config/nimdoc.tex.cfg`` configuration files. You can add and modify these
+files to your project to change the look of the docgen output.
+
+You can import the [packages/docutils/rstgen module](rstgen.html) in your
+programs if you want to reuse the compiler's documentation generation procs.
diff --git a/doc/docgen.rst b/doc/docgen.rst
deleted file mode 100644
index e6693e153..000000000
--- a/doc/docgen.rst
+++ /dev/null
@@ -1,378 +0,0 @@
-===================================
-   Nim DocGen Tools Guide
-===================================
-
-:Author: Erik O'Leary
-:Version: |nimversion|
-
-.. contents::
-
-
-Introduction
-============
-
-This document describes the `documentation generation tools`:idx: built into
-the `Nim compiler <nimc.html>`_, which can generate HTML and JSON output
-from input .nim files and projects, as well as HTML and LaTeX from input RST
-(reStructuredText) files. The output documentation will include module
-dependencies (``import``), any top-level documentation comments (##), and
-exported symbols (*), including procedures, types, and variables.
-
-
-Documentation Comments
-----------------------
-
-Any comments which are preceded by a double-hash (##), are interpreted as
-documentation.  Comments are parsed as RST (see `reference
-<http://docutils.sourceforge.net/docs/user/rst/quickref.html>`_), providing
-Nim module authors the ability to easily generate richly formatted
-documentation with only their well-documented code.
-
-Example:
-
-.. code-block:: nim
-  type Person* = object
-    ## This type contains a description of a person
-    name: string
-    age: int
-
-Outputs::
-  Person* = object
-    name: string
-    age: int
-
-This type contains a description of a person
-
-Field documentation comments can be added to fields like so:
-
-.. code-block:: nim
-  var numValues: int ## \
-    ## `numValues` stores the number of values
-
-Note that without the `*` following the name of the type, the documentation for
-this type would not be generated. Documentation will only be generated for
-*exported* types/procedures/etc.
-
-
-Nim file input
------------------
-
-The following examples will generate documentation for the below contrived
-*Nim* module, aptly named 'sample.nim'
-
-sample.nim:
-
-.. code-block:: nim
-  ## This module is a sample.
-
-  import strutils
-
-  proc helloWorld*(times: int) =
-    ## Takes an integer and outputs
-    ## as many "hello world!"s
-
-    for i in 0 .. times-1:
-      echo "hello world!"
-
-  helloWorld(5)
-
-
-Document Types
-==============
-
-
-HTML
-----
-
-Generation of HTML documents is done via the ``doc`` command. This command
-takes either a single .nim file, outputting a single .html file with the same
-base filename, or multiple .nim files, outputting multiple .html files and,
-optionally, an index file.
-
-The ``doc`` command::
-  nim doc sample
-
-Partial Output::
-  ...
-  proc helloWorld(times: int) {.raises: [], tags: [].}
-  ...
-
-The full output can be seen here: `docgen_sample2.html <docgen_sample2.html>`_.
-
-The older version of the ``doc`` command, now renamed ``doc0`` runs before
-semantic checking which means it lacks some of the things ``doc`` will output.
-
-The ``doc0`` command::
-  nim doc0 sample
-
-Partial Output::
-  ...
-  proc helloWorld*(times: int)
-  ...
-
-Output can be viewed in full here: `docgen_sample.html <docgen_sample.html>`_.
-As you can see, the tool has extracted less information than what the ``doc``
-command provides, such as pragmas attached implicitly by the compiler. This type
-of information is not available from looking at the AST (Abstract Syntax Tree)
-prior to semantic checking, which is why ``doc0`` doesn't show it.
-
-
-JSON
-----
-
-Generation of JSON documents is done via the ``jsondoc`` command. This command
-takes in a .nim file, and outputs a .json file with the same base filename. Note
-that this tool is built off of the ``doc`` command (previously ``doc2``), and
-contains the same information.
-
-The ``jsondoc`` command::
-  nim jsondoc sample
-
-Output::
-  {
-    "orig": "docgen_sample.nim",
-    "nimble": "",
-    "entries": [
-      {
-        "name": "helloWorld",
-        "type": "skProc",
-        "line": 5,
-        "col": 0,
-        "description": "Takes an integer and outputs as many &quot;hello world!&quot;s",
-        "code": "proc helloWorld(times: int) {.raises: [], tags: [].}"
-      }
-    ]
-  }
-
-Similarly to the old ``doc`` command the old ``jsondoc`` command has been
-renamed ``jsondoc0``.
- 
-The ``jsondoc0`` command::
-  nim jsondoc0 sample
-
-Output::
-  [
-    {
-      "comment": "This module is a sample."
-    },
-    {
-      "name": "helloWorld",
-      "type": "skProc",
-      "description": "Takes an integer and outputs as many &quot;hello world!&quot;s",
-      "code": "proc helloWorld*(times: int)"
-    }
-  ]
-
-Note that the ``jsondoc`` command outputs it's JSON without pretty-printing it,
-while ``jsondoc0`` outputs pretty-printed JSON.
-
-Related Options
-===============
-
-Project switch
---------------
-
-::
-  nim doc2 --project filename.nim
-
-This will recursively generate documentation of all nim modules imported
-into the input module, including system modules. Be careful with this command,
-as it may end up sprinkling html files all over your filesystem!
-
-
-Index switch
-------------
-
-::
-  nim doc2 --index:on filename.nim
-
-This will generate an index of all the exported symbols in the input Nim
-module, and put it into a neighboring file with the extension of ``.idx``. The
-index file is line oriented (newlines have to be escaped). Each line
-represents a tab separated record of several columns, the first two mandatory,
-the rest optional. See the `Index (idx) file format`_ section for details.
-
-Once index files have been generated for one or more modules, the Nim
-compiler command ``buildIndex directory`` can be run to go over all the index
-files in the specified directory to generate a `theindex.html <theindex.html>`_
-file.
-
-See source switch
------------------
-
-::
-  nim doc2 --docSeeSrcUrl:txt filename.nim
-
-When you pass the ``docSeeSrcUrl`` switch to docgen, after each documented item
-in your source code the hyper link *See source* will appear pointing to the
-implementation of that item on a GitHub repository. You can click the link to
-see the implementation of the item.
-
-If you want to reuse this feature in your own documentation you will have to
-modify ``config/nimdoc.cfg`` to contain a ``doc.item.seesrc`` value with a
-hyper link to your own code repository. As you will see by the comments in that
-file, the value ``txt`` passed on the command line will be used in the HTML
-template along others like ``$path`` and ``$line``.
-
-In the case of Nim's own documentation, the ``txt`` value is just a commit
-hash to append to a formatted URL to https://github.com/Araq/Nim. The
-``tools/nimweb.nim`` helper queries the current git commit hash during doc
-generation, but since you might be working on an unpublished repository, it
-also allows specifying a ``githash`` value in ``web/website.ini`` to force a
-specific commit in the output.
-
-
-Other Input Formats
-===================
-
-The *Nim compiler* also has support for RST (reStructuredText) files with
-the ``rst2html`` and ``rst2tex`` commands. Documents like this one are
-initially written in a dialect of RST which adds support for nim sourcecode
-highlighting with the ``.. code-block:: nim`` prefix. ``code-block`` also
-supports highlighting of C++ and some other c-like languages.
-
-Usage::
-  nim rst2html docgen.txt
-
-Output::
-  You're reading it!
-
-The input can be viewed here `docgen.txt <docgen.txt>`_. The ``rst2tex``
-command is invoked identically to ``rst2html``, but outputs a .tex file instead
-of .html.
-
-
-HTML anchor generation
-======================
-
-When you run the ``rst2html`` command, all sections in the RST document will
-get an anchor you can hyper link to. Usually you can guess the anchor lower
-casing the section title and replacing spaces with dashes, and in any case you
-can get it from the table of contents. But when you run the ``doc`` or ``doc2``
-commands to generate API documentation, some symbol get one or two anchors at
-the same time: a numerical identifier, or a plain name plus a complex name.
-
-The numerical identifier is just a random number. The number gets assigned
-according to the section and position of the symbol in the file being processed
-and you should not rely on it being constant: if you add or remove a symbol the
-numbers may shuffle around.
-
-The plain name of a symbol is a simplified version of its fully exported
-signature. Variables or constants have the same plain name symbol as their
-complex name. The plain name for procs, templates, and other callable types
-will be their unquoted value after removing parameters, return types and
-pragmas. The plain name allows short and nice linking of symbols which works
-unless you have a module with collisions due to overloading.
-
-If you hyper link a plain name symbol and there are other matches on the same
-HTML file, most browsers will go to the first one. To differentiate the rest,
-you will need to use the complex name. A complex name for a callable type is
-made up from several parts:
-
-    (**plain symbol**)(**.type**),(**first param**)?(**,param type**)\*
-
-The first thing to note is that all callable types have at least a comma, even
-if they don't have any parameters. If there are parameters, they are
-represented by their types and will be comma separated. To the plain symbol a
-suffix may be added depending on the type of the callable:
-
--------------   --------------
-Callable type   Suffix
--------------   --------------
-proc            *empty string*
-macro           ``.m``
-method          ``.e``
-iterator        ``.i``
-template        ``.t``
-converter       ``.c``
--------------   --------------
-
-The relationship of type to suffix is made by the proc ``complexName`` in the
-``compiler/docgen.nim`` file. Here are some examples of complex names for
-symbols in the `system module <system.html>`_.
-
-* ``type SignedInt = int | int8 | int16 | int32 | int64`` **=>**
-  `#SignedInt <system.html#SignedInt>`_
-* ``var globalRaiseHook: proc (e: ref E_Base): bool {.nimcall.}`` **=>**
-  `#globalRaiseHook <system.html#globalRaiseHook>`_
-* ``const NimVersion = "0.0.0"`` **=>**
-  `#NimVersion <system.html#NimVersion>`_
-* ``proc getTotalMem(): int {.rtl, raises: [], tags: [].}`` **=>**
-  `#getTotalMem, <system.html#getTotalMem,>`_
-* ``proc len[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.}`` **=>**
-  `#len,seq[T] <system.html#len,seq[T]>`_
-* ``iterator pairs[T](a: seq[T]): tuple[key: int, val: T] {.inline.}`` **=>**
-  `#pairs.i,seq[T] <system.html#pairs.i,seq[T]>`_
-* ``template newException[](exceptn: typedesc; message: string): expr`` **=>**
-  `#newException.t,typedesc,string
-  <system.html#newException.t,typedesc,string>`_
-
-
-Index (idx) file format
-=======================
-
-Files with the ``.idx`` extension are generated when you use the `Index
-switch`_ along with commands to generate documentation from source or text
-files. You can programatically generate indices with the `setIndexTerm()
-<rstgen.html#setIndexTerm>`_ and `writeIndexFile()
-<rstgen.html#writeIndexFile>`_ procs. The purpose of ``idx`` files is to hold
-the interesting symbols and their HTML references so they can be later
-concatenated into a big index file with `mergeIndexes()
-<rstgen.html#mergeIndexes>`_.  This section documents the file format in
-detail.
-
-Index files are line oriented and tab separated (newline and tab characters
-have to be escaped). Each line represents a record with at least two fields,
-but can have up to four (additional columns are ignored). The content of these
-columns is:
-
-1. Mandatory term being indexed. Terms can include quoting according to
-   Nim's rules (eg. \`^\` like in `the actors module
-   <actors.html#^,ptr.TChannel[T]>`_).
-2. Base filename plus anchor hyper link (eg.
-   ``algorithm.html#*,int,SortOrder``).
-3. Optional human readable string to display as hyper link. If the value is not
-   present or is the empty string, the hyper link will be rendered
-   using the term. Prefix whitespace indicates that this entry is
-   not for an API symbol but for a TOC entry.
-4. Optional title or description of the hyper link. Browsers usually display
-   this as a tooltip after hovering a moment over the hyper link.
-
-The index generation tools try to differentiate between documentation
-generated from ``.nim`` files and documentation generated from ``.txt`` or
-``.rst`` files. The former are always closely related to source code and
-consist mainly of API entries. The latter are generic documents meant for
-human reading.
-
-To differentiate both types (documents and APIs), the index generator will add
-to the index of documents an entry with the title of the document. Since the
-title is the topmost element, it will be added with a second field containing
-just the filename without any HTML anchor.  By convention this entry without
-anchor is the *title entry*, and since entries in the index file are added as
-they are scanned, the title entry will be the first line. The title for APIs
-is not present because it can be generated concatenating the name of the file
-to the word **Module**.
-
-Normal symbols are added to the index with surrounding whitespaces removed. An
-exception to this are table of content (TOC) entries. TOC entries are added to
-the index file with their third column having as much prefix spaces as their
-level is in the TOC (at least 1 character). The prefix whitespace helps to
-filter TOC entries from API or text symbols. This is important because the
-amount of spaces is used to replicate the hiearchy for document TOCs in the
-final index, and TOC entries found in ``.nim`` files are discarded.
-
-
-Additional resources
-====================
-
-`Nim Compiler User Guide <nimc.html#command-line-switches>`_
-
-`RST Quick Reference
-<http://docutils.sourceforge.net/docs/user/rst/quickref.html>`_
-
-The output for HTML and LaTeX comes from the ``config/nimdoc.cfg`` and
-``config/nimdoc.tex.cfg`` configuration files. You can add and modify these
-files to your project to change the look of docgen output.
-
-You can import the `packages/docutils/rstgen module <rstgen.html>`_ in your
-programs if you want to reuse the compiler's documentation generation procs.
diff --git a/doc/docgen_sample.nim b/doc/docgen_sample.nim
index 875993187..06b8d7f8e 100644
--- a/doc/docgen_sample.nim
+++ b/doc/docgen_sample.nim
@@ -1,12 +1,12 @@
 ## This module is a sample.
 
-import strutils
+import std/strutils
 
 proc helloWorld*(times: int) =
   ## Takes an integer and outputs
-  ## as many "hello world!"s
+  ## as many indented "hello world!"s
 
   for i in 0 .. times-1:
-    echo "hello world!"
+    echo "hello world!".indent(2) # using indent to avoid `UnusedImport`
 
 helloWorld(5)
diff --git a/doc/docs.md b/doc/docs.md
new file mode 100644
index 000000000..b6ff6d2c7
--- /dev/null
+++ b/doc/docs.md
@@ -0,0 +1,38 @@
+The documentation consists of several documents:
+
+- | [Tutorial (part I)](tut1.html)
+  | The Nim tutorial part one deals with the basics.
+
+- | [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.
+
+- | [Library documentation](lib.html)
+  | This document describes Nim's standard library.
+
+- | [Compiler user guide](nimc.html)
+  | The user guide lists command line arguments, special features of the
+    compiler, etc.
+
+- | [Tools documentation](tools.html)
+  | Description of some tools that come with the standard distribution.
+
+- | [Memory management](mm.html)
+  | Additional documentation about Nim's memory management strategies
+  | and how to operate them in a realtime setting.
+
+- | [Source code filters](filters.html)
+  | The Nim compiler supports source code filters as a simple yet powerful
+    builtin templating system.
+
+- | [Internal documentation](intern.html)
+  | The internal documentation describes how the compiler is implemented. Read
+    this if you want to hack the compiler.
+
+- | [Index](theindex.html)
+  | The generated index. **Index + (Ctrl+F) == Joy**
diff --git a/doc/docs.rst b/doc/docs.rst
deleted file mode 100644
index 4484784ae..000000000
--- a/doc/docs.rst
+++ /dev/null
@@ -1,40 +0,0 @@
-The documentation consists of several documents:
-
-- | `Tutorial (part I) <tut1.html>`_
-  | The Nim tutorial part one deals with the basics.
-
-- | `Tutorial (part II) <tut2.html>`_
-  | The Nim tutorial part two deals with the advanced language constructs.
-
-- | `Language Manual <manual.html>`_
-  | The Nim manual is a draft that will evolve into a proper specification.
-
-- | `Library documentation <lib.html>`_
-  | This document describes Nim's standard library.
-
-- | `Compiler user guide <nimc.html>`_
-  | The user guide lists command line arguments, special features of the
-    compiler, etc.
-
-- | `Tools documentation <tools.html>`_
-  | Description of some tools that come with the standard distribution.
-
-- | `GC <gc.html>`_
-  | Additional documentation about Nim's GC and how to operate it in a
-  | realtime setting.
-
-- | `Source code filters <filters.html>`_
-  | The Nim compiler supports source code filters as a simple yet powerful
-    builtin templating system.
-
-- | `Term rewriting macros <trmacros.html>`_
-  | Term rewriting macros enhance the compilation process with user defined
-    optimizations.
-
-- | `Internal documentation <intern.html>`_
-  | The internal documentation describes how the compiler is implemented. Read
-    this if you want to hack the compiler.
-
-- | `Index <theindex.html>`_
-  | The generated index. **Index + (Ctrl+F) == Joy**
-
diff --git a/doc/docstyle.md b/doc/docstyle.md
new file mode 100644
index 000000000..291a34cf6
--- /dev/null
+++ b/doc/docstyle.md
@@ -0,0 +1,164 @@
+Documentation Style
+===================
+
+General Guidelines
+------------------
+
+* See also [nep1](nep1.html) which should probably be merged here.
+* Authors should document anything that is exported; documentation for private
+  procs can be useful too (visible via `nim doc --docInternal foo.nim`:cmd:).
+* Within documentation, a period (`.`) should follow each sentence (or sentence fragment) in a comment block.
+  The documentation may be limited to one sentence fragment, but if multiple sentences are within the documentation,
+  each sentence after the first should be complete and in present tense.
+* Documentation is parsed as a custom ReStructuredText (RST) with partial markdown support.
+* In nim sources, prefer single backticks to double backticks since it's simpler
+  and `nim doc`:cmd: supports it. Likewise with ``rst`` files: `nim rst2html`:cmd: will render those as monospace, and
+  adding ``.. default-role:: code`` to an ``rst`` file will also make those render as monospace when rendered directly
+  in tools such as github.
+* (debatable) In nim sources, for links, prefer ``[link text](link.html)`` to `\`link text<link.html>\`_`:code:
+  since the syntax is simpler and markdown is more common (likewise, `nim rst2html`:cmd: also supports it in ``rst`` files).
+
+  ```nim
+  proc someproc*(s: string, foo: int) =
+    ## Use single backticks for inline code, e.g.: `s` or `someExpr(true)`.
+    ## Use a backlash to follow with alphanumeric char: `int8`\s are great.
+  ```
+
+
+Module-level documentation
+--------------------------
+
+Documentation of a module is placed at the top of the module itself. Each line of documentation begins with double hashes (`##`).
+Sometimes `##[ multiline docs containing code ]##` is preferable, see ``lib/pure/times.nim``.
+Code samples are encouraged, and should follow the general RST syntax:
+
+  ````Nim
+  ## The `universe` module computes the answer to life, the universe, and everything.
+  ##
+  ##   ```
+  ##   doAssert computeAnswerString() == 42
+  ##   ```
+  ````
+
+
+Within this top-level comment, you can indicate the authorship and copyright of the code, which will be featured in the produced documentation.
+
+  ```Nim
+  ## This is the best module ever. It provides answers to everything!
+  ##
+  ## :Author: Steve McQueen
+  ## :Copyright: 1965
+  ##
+  ```
+
+Leave a space between the last line of top-level documentation and the beginning of Nim code (the imports, etc.).
+
+Procs, Templates, Macros, Converters, and Iterators
+---------------------------------------------------
+
+The documentation of a procedure should begin with a capital letter and should be in present tense. Variables referenced in the documentation should be surrounded by single tick marks:
+
+  ```Nim
+  proc example1*(x: int) =
+    ## Prints the value of `x`.
+    echo x
+  ```
+
+Whenever an example of usage would be helpful to the user, you should include one within the documentation in RST format as below.
+
+  ````Nim
+  proc addThree*(x, y, z: int8): int =
+    ## Adds three `int8` values, treating them as unsigned and
+    ## truncating the result.
+    ##
+    ##   ```
+    ##   # things that aren't suitable for a `runnableExamples` go in code block:
+    ##   echo execCmdEx("git pull")
+    ##   drawOnScreen()
+    ##   ```
+    runnableExamples:
+      # `runnableExamples` is usually preferred to code blocks, when possible.
+      doAssert addThree(3, 125, 6) == -122
+    result = x +% y +% z
+  ````
+
+The command `nim doc`:cmd: will then correctly syntax highlight the Nim code within the documentation.
+
+Types
+-----
+
+Exported types should also be documented. This documentation can also contain code samples, but those are better placed with the functions to which they refer.
+
+  ```Nim
+  type
+    NamedQueue*[T] = object ## Provides a linked data structure with names
+                            ## throughout. It is named for convenience. I'm making
+                            ## this comment long to show how you can, too.
+      name*: string ## The name of the item
+      val*: T ## Its value
+      next*: ref NamedQueue[T] ## The next item in the queue
+  ```
+
+
+You have some flexibility when placing the documentation:
+
+  ```Nim
+  type
+    NamedQueue*[T] = object
+      ## Provides a linked data structure with names
+      ## throughout. It is named for convenience. I'm making
+      ## this comment long to show how you can, too.
+      name*: string ## The name of the item
+      val*: T ## Its value
+      next*: ref NamedQueue[T] ## The next item in the queue
+  ```
+
+Make sure to place the documentation beside or within the object.
+
+  ```Nim
+  type
+    ## Bad: this documentation disappears because it annotates the `type` keyword
+    ## above, not `NamedQueue`.
+    NamedQueue*[T] = object
+      name*: string ## This becomes the main documentation for the object, which
+                    ## is not what we want.
+      val*: T ## Its value
+      next*: ref NamedQueue[T] ## The next item in the queue
+  ```
+
+Var, Let, and Const
+-------------------
+
+When declaring module-wide constants and values, documentation is encouraged. The placement of doc comments is similar to the `type` sections.
+
+  ```Nim
+  const
+    X* = 42 ## An awesome number.
+    SpreadArray* = [
+      [1,2,3],
+      [2,3,1],
+      [3,1,2],
+    ] ## Doc comment for `SpreadArray`.
+  ```
+
+Placement of comments in other areas is usually allowed, but will not become part of the documentation output and should therefore be prefaced by a single hash (`#`).
+
+  ```Nim
+  const
+    BadMathVals* = [
+      3.14, # pi
+      2.72, # e
+      0.58, # gamma
+    ] ## A bunch of badly rounded values.
+  ```
+
+Nim supports Unicode in comments, so the above can be replaced with the following:
+
+  ```Nim
+  const
+    BadMathVals* = [
+      3.14, # π
+      2.72, # e
+      0.58, # γ
+    ] ## A bunch of badly rounded values (including π!).
+  ```
diff --git a/doc/docstyle.rst b/doc/docstyle.rst
deleted file mode 100644
index d789b1df9..000000000
--- a/doc/docstyle.rst
+++ /dev/null
@@ -1,140 +0,0 @@
-Documentation Style
-===================
-
-General Guidelines
-------------------
-
-* Authors should document anything that is exported.
-* Within documentation, a period (`.`) should follow each sentence (or sentence fragment) in a comment block. The documentation may be limited to one sentence fragment, but if multiple sentences are within the documentation, each sentence after the first should be complete and in present tense.
-* Documentation is parsed as ReStructuredText (RST).
-* Inline code should be surrounded by double tick marks ("``````"). If you would like a character to immediately follow inline code (e.g., "``int8``s are great!"), escape the following character with a backslash (``\``). The preceding is typed as ``` ``int8``\s are great!```.
-
-Module-level documentation
---------------------------
-
-Documentation of a module is placed at the top of the module itself. Each line of documentation begins with double hashes (``##``).
-Code samples are encouraged, and should follow the general RST syntax:
-
-.. code-block:: Nim
-
-  ## The ``universe`` module computes the answer to life, the universe, and everything.
-  ##
-  ## .. code-block:: Nim
-  ##  echo computeAnswerString() # "42"
-
-
-Within this top-level comment, you can indicate the authorship and copyright of the code, which will be featured in the produced documentation.
-
-.. code-block:: Nim
-
-  ## This is the best module ever. It provides answers to everything!
-  ##
-  ## :Author: Steve McQueen
-  ## :Copyright: 1965
-  ##
-
-Leave a space between the last line of top-level documentation and the beginning of Nim code (the imports, etc.).
-
-Procs, Templates, Macros, Converters, and Iterators
----------------------------------------------------
-
-The documentation of a procedure should begin with a capital letter and should be in present tense. Variables referenced in the documentation should be surrounded by double tick marks (``````).
-
-.. code-block:: Nim
-
-  proc example1*(x: int) =
-    ## Prints the value of ``x``.
-    echo x
-
-Whenever an example of usage would be helpful to the user, you should include one within the documentation in RST format as below.
-
-.. code-block:: Nim
-
-  proc addThree*(x, y, z: int8): int =
-    ## Adds three ``int8`` values, treating them as unsigned and
-    ## truncating the result.
-    ##
-    ## .. code-block:: nim
-    ##  echo addThree(3, 125, 6) # -122
-    result = x +% y +% z
-
-The commands ``nim doc`` and ``nim doc2`` will then correctly syntax highlight the Nim code within the documentation.
-
-Types
------
-
-Exported types should also be documented. This documentation can also contain code samples, but those are better placed with the functions to which they refer.
-
-.. code-block:: Nim
-
-  type
-    NamedQueue*[T] = object ## Provides a linked data structure with names
-                            ## throughout. It is named for convenience. I'm making
-                            ## this comment long to show how you can, too.
-      name*: string ## The name of the item
-      val*: T ## Its value
-      next*: ref NamedQueue[T] ## The next item in the queue
-
-
-You have some flexibility when placing the documentation:
-
-.. code-block:: Nim
-
-  type
-    NamedQueue*[T] = object
-      ## Provides a linked data structure with names
-      ## throughout. It is named for convenience. I'm making
-      ## this comment long to show how you can, too.
-      name*: string ## The name of the item
-      val*: T ## Its value
-      next*: ref NamedQueue[T] ## The next item in the queue
-
-Make sure to place the documentation beside or within the object.
-
-.. code-block:: Nim
-
-  type
-    ## This documentation disappears because it annotates the ``type`` keyword
-    ## above, not ``NamedQueue``.
-    NamedQueue*[T] = object
-      name*: string ## This becomes the main documentation for the object, which
-                    ## is not what we want.
-      val*: T ## Its value
-      next*: ref NamedQueue[T] ## The next item in the queue
-
-Var, Let, and Const
--------------------
-
-When declaring module-wide constants and values, documentation is encouraged. The placement of doc comments is similar to the ``type`` sections.
-
-.. code-block:: Nim
-
-  const
-    X* = 42 ## An awesome number.
-    SpreadArray* = [
-      [1,2,3],
-      [2,3,1],
-      [3,1,2],
-    ] ## Doc comment for ``SpreadArray``.
-
-Placement of comments in other areas is usually allowed, but will not become part of the documentation output and should therefore be prefaced by a single hash (``#``).
-
-.. code-block:: Nim
-
-  const
-    BadMathVals* = [
-      3.14, # pi
-      2.72, # e
-      0.58, # gamma
-    ] ## A bunch of badly rounded values.
-
-Nim supports Unicode in comments, so the above can be replaced with the following:
-
-.. code-block:: Nim
-
-  const
-    BadMathVals* = [
-      3.14, # π
-      2.72, # e
-      0.58, # γ
-    ] ## A bunch of badly rounded values (including π!).
diff --git a/doc/drnim.md b/doc/drnim.md
new file mode 100644
index 000000000..1dc2b550f
--- /dev/null
+++ b/doc/drnim.md
@@ -0,0 +1,205 @@
+===================================
+   DrNim User Guide
+===================================
+
+:Author: Andreas Rumpf
+:Version: |nimversion|
+
+.. default-role:: code
+.. include:: rstcommon.rst
+.. contents::
+
+
+Introduction
+============
+
+This document describes the usage of the *DrNim* tool. DrNim combines
+the Nim frontend with the [Z3](https://github.com/Z3Prover/z3) proof
+engine, in order to allow verify/validate software written in Nim.
+DrNim's command-line options are the same as the Nim compiler's.
+
+
+DrNim currently only checks the sections of your code that are marked
+via `staticBoundChecks: on`:
+
+  ```nim
+  {.push staticBoundChecks: on.}
+  # <--- code section here ---->
+  {.pop.}
+  ```
+
+DrNim currently only tries to prove array indexing or subrange checks,
+overflow errors are *not* prevented. Overflows will be checked for in
+the future.
+
+Later versions of the **Nim compiler** will **assume** that the checks inside
+the `staticBoundChecks: on` environment have been proven correct and so
+it will **omit** the runtime checks. If you do not want this behavior, use
+instead `{.push staticBoundChecks: defined(nimDrNim).}`. This way the
+Nim compiler remains unaware of the performed proofs but DrNim will prove
+your code.
+
+
+Installation
+============
+
+Run `koch drnim`:cmd:, the executable will afterwards be
+in ``$nim/bin/drnim``.
+
+
+Motivating Example
+==================
+
+The follow example highlights what DrNim can easily do, even
+without additional annotations:
+
+  ```nim
+  {.push staticBoundChecks: on.}
+
+  proc sum(a: openArray[int]): int =
+    for i in 0..a.len:
+      result += a[i]
+
+  {.pop.}
+
+  echo sum([1, 2, 3])
+  ```
+
+This program contains a famous "index out of bounds" bug. DrNim
+detects it and produces the following error message:
+
+    cannot prove: i <= len(a) + -1; counter example: i -> 0 a.len -> 0 [IndexCheck]
+
+In other words for `i == 0` and `a.len == 0` (for example!) there would be
+an index out of bounds error.
+
+
+Pre-, postconditions and invariants
+===================================
+
+DrNim adds 4 additional annotations (pragmas) to Nim:
+
+- `requires`:idx:
+- `ensures`:idx:
+- `invariant`:idx:
+- `assume`:idx:
+
+These pragmas are ignored by the Nim compiler so that they don't have to
+be disabled via `when defined(nimDrNim)`.
+
+
+Invariant
+---------
+
+An `invariant` is a proposition that must be true after every loop
+iteration, it's tied to the loop body it's part of.
+
+
+Requires
+--------
+
+A `requires` annotation describes what the function expects to be true
+before it's called so that it can perform its operation. A `requires`
+annotation is also called a `precondition`:idx:.
+
+
+Ensures
+-------
+
+An `ensures` annotation describes what will be true after the function
+call. An `ensures` annotation is also called a `postcondition`:idx:.
+
+
+Assume
+------
+
+An `assume` annotation describes what DrNim should **assume** to be true
+in this section of the program. It is an unsafe escape mechanism comparable
+to Nim's `cast` statement. Use it only when you really know better
+than DrNim. You should add a comment to a paper that proves the proposition
+you assume.
+
+
+Example: insertionSort
+======================
+
+**Note**: This example does not yet work with DrNim.
+
+  ```nim
+  import std / logic
+
+  proc insertionSort(a: var openArray[int]) {.
+      ensures: forall(i in 1..<a.len, a[i-1] <= a[i]).} =
+
+    for k in 1 ..< a.len:
+      {.invariant: 1 <= k and k <= a.len.}
+      {.invariant: forall(j in 1..<k, i in 0..<j, a[i] <= a[j]).}
+      var t = k
+      while t > 0 and a[t-1] > a[t]:
+        {.invariant: k < a.len.}
+        {.invariant: 0 <= t and t <= k.}
+        {.invariant: forall(j in 1..k, i in 0..<j, j == t or a[i] <= a[j]).}
+        swap a[t], a[t-1]
+        dec t
+  ```
+
+Unfortunately, the invariants required to prove that this code is correct take more
+code than the imperative instructions. However, this effort can be compensated
+by the fact that the result needs very little testing. Be aware though that
+DrNim only proves that after `insertionSort` this condition holds:
+
+    forall(i in 1..<a.len, a[i-1] <= a[i])
+
+
+This is required, but not sufficient to describe that a `sort` operation
+was performed. For example, the same postcondition is true for this proc
+which doesn't sort at all:
+
+  ```nim
+  import std / logic
+
+  proc insertionSort(a: var openArray[int]) {.
+      ensures: forall(i in 1..<a.len, a[i-1] <= a[i]).} =
+    # does not sort, overwrites `a`'s contents!
+    for i in 0..<a.len: a[i] = i
+  ```
+
+
+
+Syntax of propositions
+======================
+
+The basic syntax is `ensures|requires|invariant: <prop>`.
+A `prop` is either a comparison or a compound:
+
+    prop = nim_bool_expression
+         | prop 'and' prop
+         | prop 'or' prop
+         | prop '->' prop # implication
+         | prop '<->' prop
+         | 'not' prop
+         | '(' prop ')' # you can group props via ()
+         | forallProp
+         | existsProp
+
+    forallProp = 'forall' '(' quantifierList ',' prop ')'
+    existsProp = 'exists' '(' quantifierList ',' prop ')'
+
+    quantifierList = quantifier (',' quantifier)*
+    quantifier = <new identifier> 'in' nim_iteration_expression
+
+
+`nim_iteration_expression` here is an ordinary expression of Nim code
+that describes an iteration space, for example `1..4` or `1..<a.len`.
+
+`nim_bool_expression` here is an ordinary expression of Nim code of
+type `bool` like `a == 3` or `23 > a.len`.
+
+The supported subset of Nim code that can be used in these expressions
+is currently underspecified but `let` variables, function parameters
+and `result` (which represents the function's final result) are amenable
+for verification. The expressions must not have any side-effects and must
+terminate.
+
+The operators `forall`, `exists`, `->`, `<->` have to imported
+from `std / logic`.
diff --git a/doc/effects.txt b/doc/effects.txt
index 4ed1d09f1..df624e8b0 100644
--- a/doc/effects.txt
+++ b/doc/effects.txt
@@ -11,13 +11,14 @@ Iff a proc is side effect free and all its argument are evaluable at
 compile time, it can be evaluated by the compiler. However, really
 difficult is the ``newString`` proc: If it is simply wrapped, it
 should not be evaluated at compile time! On other occasions it can
-and should be evaluted:
+and should be evaluated:
 
-.. code-block:: nim
+  ```nim
   proc toUpper(s: string): string =
     result = newString(len(s))
     for i in 0..len(s) - 1:
       result[i] = toUpper(s[i])
+  ```
 
 No, it really can always be evaluated. The code generator should transform
 ``s = "\0\0\0..."`` back into ``s = newString(...)``.
diff --git a/doc/endb.rst b/doc/endb.rst
deleted file mode 100644
index 90bb964ba..000000000
--- a/doc/endb.rst
+++ /dev/null
@@ -1,203 +0,0 @@
-==============================================
-  Embedded Nim Debugger (ENDB) User Guide
-==============================================
-
-:Author: Andreas Rumpf
-:Version: |nimversion|
-
-.. contents::
-
-**WARNING**: ENDB is not maintained anymore! Please help if you're interested
-in this tool.
-
-Nim comes with a platform independent debugger -
-the Embedded Nim Debugger (ENDB). The debugger is
-*embedded* into your executable if it has been
-compiled with the ``--debugger:on`` command line option.
-This also defines the conditional symbol ``ENDB`` for you.
-
-Note: You must not compile your program with the ``--app:gui``
-command line option because then there would be no console
-available for the debugger.
-
-If you start your program the debugger will immediately show
-a prompt on the console. You can now enter a command. The next sections
-deal with the possible commands. As usual in Nim in all commands
-underscores and case do not matter. Optional components of a command
-are listed in brackets ``[...]`` here.
-
-
-General Commands
-================
-
-``h``, ``help``
-    Display a quick reference of the possible commands.
-
-``q``, ``quit``
-    Quit the debugger and the program.
-
-<ENTER>
-    (Without any typed command) repeat the previous debugger command.
-    If there is no previous command, ``step_into`` is assumed.
-
-Executing Commands
-==================
-
-``s``, ``step_into``
-    Single step, stepping into routine calls.
-
-``n``, ``step_over``
-    Single step, without stepping into routine calls.
-
-``f``, ``skip_current``
-    Continue execution until the current routine finishes.
-
-``c``, ``continue``
-    Continue execution until the next breakpoint.
-
-``i``, ``ignore``
-    Continue execution, ignore all breakpoints. This effectively quits
-    the debugger and runs the program until it finishes.
-
-
-Breakpoint Commands
-===================
-
-``b``, ``setbreak`` [fromline [toline]] [file]
-    Set a new breakpoint for the given file
-    and line numbers. If no file is given, the current execution point's
-    filename is used. If the filename has no extension, ``.nim`` is
-    appended for your convenience.
-    If no line numbers are given, the current execution point's
-    line is used. If both ``fromline`` and ``toline`` are given the
-    breakpoint contains a line number range. Some examples if it is still
-    unclear:
-
-    * ``b 12 15 thallo`` creates a breakpoint that
-      will be triggered if the instruction pointer reaches one of the
-      lines 12-15 in the file ``thallo.nim``.
-    * ``b 12 thallo`` creates a breakpoint that
-      will be triggered if the instruction pointer reaches the
-      line 12 in the file ``thallo.nim``.
-    * ``b 12`` creates a breakpoint that
-      will be triggered if the instruction pointer reaches the
-      line 12 in the current file.
-    * ``b`` creates a breakpoint that
-      will be triggered if the instruction pointer reaches the
-      current line in the current file again.
-
-``breakpoints``
-    Display the entire breakpoint list.
-
-``disable`` <identifier>
-    Disable a breakpoint. It remains disabled until you turn it on again
-    with the ``enable`` command.
-
-``enable`` <identifier>
-    Enable a breakpoint.
-
-Often it happens when debugging that you keep retyping the breakpoints again
-and again because they are lost when you restart your program. This is not
-necessary: A special pragma has been defined for this:
-
-
-The ``breakpoint`` pragma
--------------------------
-
-The ``breakpoint`` pragma is syntactically a statement. It can be used
-to mark the *following line* as a breakpoint:
-
-.. code-block:: Nim
-  write("1")
-  {.breakpoint: "before_write_2".}
-  write("2")
-
-The name of the breakpoint here is ``before_write_2``. Of course the
-breakpoint's name is optional - the compiler will generate one for you
-if you leave it out.
-
-Code for the ``breakpoint`` pragma is only generated if the debugger
-is turned on, so you don't need to remove it from your source code after
-debugging.
-
-
-The ``watchpoint`` pragma
--------------------------
-
-The ``watchpoint`` pragma is syntactically a statement. It can be used
-to mark a location as a watchpoint:
-
-.. code-block:: Nim
-  var a: array[0..20, int]
-
-  {.watchpoint: a[3].}
-  for i in 0 .. 20: a[i] = i
-
-ENDB then writes a stack trace whenever the content of the location ``a[3]``
-changes. The current implementation only tracks a hash value of the location's
-contents and so locations that are not word sized may encounter false
-negatives in very rare cases.
-
-Code for the ``watchpoint`` pragma is only generated if the debugger
-is turned on, so you don't need to remove it from your source code after
-debugging.
-
-Due to the primitive implementation watchpoints are even slower than
-breakpoints: After *every* executed Nim code line it is checked whether the
-location changed.
-
-
-Data Display Commands
-=====================
-
-``e``, ``eval`` <exp>
-    Evaluate the expression <exp>. Note that ENDB has no full-blown expression
-    evaluator built-in. So expressions are limited:
-
-    * To display global variables prefix their names with their
-      owning module: ``nim1.globalVar``
-    * To display local variables or parameters just type in
-      their name: ``localVar``. If you want to inspect variables that are not
-      in the current stack frame, use the ``up`` or ``down`` command.
-
-    Unfortunately, only inspecting variables is possible at the moment. Maybe
-    a future version will implement a full-blown Nim expression evaluator,
-    but this is not easy to do and would bloat the debugger's code.
-
-    Since displaying the whole data structures is often not needed and
-    painfully slow, the debugger uses a *maximal display depth* concept for
-    displaying.
-
-    You can alter the maximal display depth with the ``maxdisplay``
-    command.
-
-``maxdisplay`` <natural>
-    Sets the maximal display depth to the given integer value. A value of 0
-    means there is no maximal display depth. Default is 3.
-
-``o``, ``out`` <filename> <exp>
-    Evaluate the expression <exp> and store its string representation into a
-    file named <filename>. If the file does not exist, it will be created,
-    otherwise it will be opened for appending.
-
-``w``, ``where``
-    Display the current execution point.
-
-``u``, ``up``
-    Go up in the call stack.
-
-``d``, ``down``
-    Go down in the call stack.
-
-``stackframe`` [file]
-    Displays the content of the current stack frame in ``stdout`` or
-    appends it to the file, depending on whether a file is given.
-
-``callstack``
-    Display the entire call stack (but not its content).
-
-``l``, ``locals``
-    Display the available local variables in the current stack frame.
-
-``g``, ``globals``
-    Display all the global variables that are available for inspection.
diff --git a/doc/estp.md b/doc/estp.md
new file mode 100644
index 000000000..8a986bdf3
--- /dev/null
+++ b/doc/estp.md
@@ -0,0 +1,206 @@
+===================================================
+  Embedded Stack Trace Profiler (ESTP) User Guide
+===================================================
+
+.. default-role:: code
+.. include:: rstcommon.rst
+
+:Author: Andreas Rumpf
+:Version: |nimversion|
+
+
+Nim comes with a platform independent profiler -
+the Embedded Stack Trace Profiler (ESTP). The profiler
+is *embedded* into your executable. To activate the profiler you need to do:
+
+* compile your program with the `--profiler:on --stackTrace:on`:option: command
+  line options
+* import the `nimprof` module
+* run your program as usual.
+
+You can in fact look at `nimprof`'s source code to see how to implement
+your own profiler.
+
+The setting `--profiler:on`:option: defines the conditional symbol `profiler`.
+You can use `when compileOption("profiler")` to make the switch seamless.
+If `profiler`:option: is `off`:option:, your program runs normally.
+Otherwise your program is profiled.
+
+```nim
+when compileOption("profiler"):
+  import std/nimprof
+```
+
+After your program has finished the profiler will create a
+file ``profile_results.txt`` containing the profiling results.
+
+Since the profiler works by examining stack traces, it's essential that
+the option `--stackTrace:on`:option: is active! Unfortunately this means that a
+profiling build is much slower than a release build.
+
+
+Memory profiler
+===============
+
+You can also use ESTP as a memory profiler to see which stack traces allocate
+the most memory and thus create the most GC pressure. It may also help to
+find memory leaks. To activate the memory profiler you need to do:
+
+* compile your program with the
+  `--profiler:off --stackTrace:on -d:memProfiler`:option:
+  command line options. Yes it's `--profiler:off`:option:.
+* import the `nimprof` module
+* run your program as usual.
+
+Define the symbol `ignoreAllocationSize` so that only the number of
+allocations is counted and the sizes of the memory allocations do not matter.
+
+
+Example results file
+====================
+
+The results file lists stack traces ordered by significance.
+
+The following example file has been generated by profiling the Nim compiler
+itself: It shows that in total 5.4% of the runtime has been spent
+in `crcFromRope` or its children.
+
+In general the stack traces show you immediately where the problem is because
+the trace acts like an explanation; in traditional profilers you can only find
+expensive leaf functions easily but the *reason* why they are invoked
+often remains mysterious.
+
+    total executions of each stack trace:
+    Entry: 0/3391 Calls: 84/4160 = 2.0% [sum: 84; 84/4160 = 2.0%]
+      newCrcFromRopeAux
+      crcFromRope
+      writeRopeIfNotEqual
+      shouldRecompile
+      writeModule
+      myClose
+      closePasses
+      processModule
+      CompileModule
+      CompileProject
+      CommandCompileToC
+      MainCommand
+      HandleCmdLine
+      nim
+    Entry: 1/3391 Calls: 46/4160 = 1.1% [sum: 130; 130/4160 = 3.1%]
+      updateCrc32
+      newCrcFromRopeAux
+      crcFromRope
+      writeRopeIfNotEqual
+      shouldRecompile
+      writeModule
+      myClose
+      closePasses
+      processModule
+      CompileModule
+      CompileProject
+      CommandCompileToC
+      MainCommand
+      HandleCmdLine
+      nim
+    Entry: 2/3391 Calls: 41/4160 = 0.99% [sum: 171; 171/4160 = 4.1%]
+      updateCrc32
+      updateCrc32
+      newCrcFromRopeAux
+      crcFromRope
+      writeRopeIfNotEqual
+      shouldRecompile
+      writeModule
+      myClose
+      closePasses
+      processModule
+      CompileModule
+      CompileProject
+      CommandCompileToC
+      MainCommand
+      HandleCmdLine
+      nim
+    Entry: 3/3391 Calls: 41/4160 = 0.99% [sum: 212; 212/4160 = 5.1%]
+      crcFromFile
+      writeRopeIfNotEqual
+      shouldRecompile
+      writeModule
+      myClose
+      closePasses
+      processModule
+      CompileModule
+      CompileProject
+      CommandCompileToC
+      MainCommand
+      HandleCmdLine
+      nim
+    Entry: 4/3391 Calls: 41/4160 = 0.99% [sum: 253; 253/4160 = 6.1%]
+      updateCrc32
+      crcFromFile
+      writeRopeIfNotEqual
+      shouldRecompile
+      writeModule
+      myClose
+      closePasses
+      processModule
+      CompileModule
+      CompileProject
+      CommandCompileToC
+      MainCommand
+      HandleCmdLine
+      nim
+    Entry: 5/3391 Calls: 32/4160 = 0.77% [sum: 285; 285/4160 = 6.9%]
+      pop
+      newCrcFromRopeAux
+      crcFromRope
+      writeRopeIfNotEqual
+      shouldRecompile
+      writeModule
+      myClose
+      closePasses
+      processModule
+      CompileModule
+      CompileProject
+      CommandCompileToC
+      MainCommand
+      HandleCmdLine
+      nim
+    Entry: 6/3391 Calls: 17/4160 = 0.41% [sum: 302; 302/4160 = 7.3%]
+      doOperation
+      forAllChildrenAux
+      pop
+      newCrcFromRopeAux
+      crcFromRope
+      writeRopeIfNotEqual
+      shouldRecompile
+      writeModule
+      myClose
+      closePasses
+      processModule
+      CompileModule
+      CompileProject
+      CommandCompileToC
+      MainCommand
+      HandleCmdLine
+      ...
+      nim
+    Entry: 7/3391 Calls: 14/4160 = 0.34% [sum: 316; 316/4160 = 7.6%]
+      Contains
+      isAccessible
+      interiorAllocatedPtr
+      gcMark
+      markStackAndRegisters
+      collectCTBody
+      collectCT
+      rawNewObj
+      newObj
+      newNode
+      copyTree
+      matchesAux
+      matches
+      resolveOverloads
+      semOverloadedCall
+      semOverloadedCallAnalyseEffects
+      ...
+      CommandCompileToC
+      MainCommand
+      HandleCmdLine
diff --git a/doc/estp.rst b/doc/estp.rst
deleted file mode 100644
index 805a84eb7..000000000
--- a/doc/estp.rst
+++ /dev/null
@@ -1,195 +0,0 @@
-===================================================
-  Embedded Stack Trace Profiler (ESTP) User Guide
-===================================================
-
-:Author: Andreas Rumpf
-:Version: |nimversion|
-
-
-Nim comes with a platform independent profiler -
-the Embedded Stack Trace Profiler (ESTP). The profiler
-is *embedded* into your executable. To activate the profiler you need to do:
-
-* compile your program with the ``--profiler:on --stackTrace:on`` command
-  line options
-* import the ``nimprof`` module
-* run your program as usual.
-
-You can in fact look at ``nimprof``'s source code to see how to implement
-your own profiler.
-
-The setting ``--profiler:on`` defines the conditional symbol ``profiler``.
-
-After your program has finished the profiler will create a
-file ``profile_results.txt`` containing the profiling results.
-
-Since the profiler works by examining stack traces, it's essential that
-the option ``--stackTrace:on`` is active! Unfortunately this means that a
-profiling build is much slower than a release build.
-
-
-Memory profiler
-===============
-
-You can also use ESTP as a memory profiler to see which stack traces allocate
-the most memory and thus create the most GC pressure. It may also help to
-find memory leaks. To activate the memory profiler you need to do:
-
-* compile your program with the ``--profiler:off --stackTrace:on -d:memProfiler``
-  command line options. Yes it's ``--profiler:off``.
-* import the ``nimprof`` module
-* run your program as usual.
-
-Define the symbol ``ignoreAllocationSize`` so that only the number of
-allocations is counted and the sizes of the memory allocations do not matter.
-
-
-Example results file
-====================
-
-The results file lists stack traces ordered by significance.
-
-The following example file has been generated by profiling the Nim compiler
-itself: It shows that in total 5.4% of the runtime has been spent
-in ``crcFromRope`` or its children.
-
-In general the stack traces show you immediately where the problem is because
-the trace acts like an explanation; in traditional profilers you can only find
-expensive leaf functions easily but the *reason* why they are invoked
-often remains mysterious.
-
-::
-  total executions of each stack trace:
-  Entry: 0/3391 Calls: 84/4160 = 2.0% [sum: 84; 84/4160 = 2.0%]
-    newCrcFromRopeAux
-    crcFromRope
-    writeRopeIfNotEqual
-    shouldRecompile
-    writeModule
-    myClose
-    closePasses
-    processModule
-    CompileModule
-    CompileProject
-    CommandCompileToC
-    MainCommand
-    HandleCmdLine
-    nim
-  Entry: 1/3391 Calls: 46/4160 = 1.1% [sum: 130; 130/4160 = 3.1%]
-    updateCrc32
-    newCrcFromRopeAux
-    crcFromRope
-    writeRopeIfNotEqual
-    shouldRecompile
-    writeModule
-    myClose
-    closePasses
-    processModule
-    CompileModule
-    CompileProject
-    CommandCompileToC
-    MainCommand
-    HandleCmdLine
-    nim
-  Entry: 2/3391 Calls: 41/4160 = 0.99% [sum: 171; 171/4160 = 4.1%]
-    updateCrc32
-    updateCrc32
-    newCrcFromRopeAux
-    crcFromRope
-    writeRopeIfNotEqual
-    shouldRecompile
-    writeModule
-    myClose
-    closePasses
-    processModule
-    CompileModule
-    CompileProject
-    CommandCompileToC
-    MainCommand
-    HandleCmdLine
-    nim
-  Entry: 3/3391 Calls: 41/4160 = 0.99% [sum: 212; 212/4160 = 5.1%]
-    crcFromFile
-    writeRopeIfNotEqual
-    shouldRecompile
-    writeModule
-    myClose
-    closePasses
-    processModule
-    CompileModule
-    CompileProject
-    CommandCompileToC
-    MainCommand
-    HandleCmdLine
-    nim
-  Entry: 4/3391 Calls: 41/4160 = 0.99% [sum: 253; 253/4160 = 6.1%]
-    updateCrc32
-    crcFromFile
-    writeRopeIfNotEqual
-    shouldRecompile
-    writeModule
-    myClose
-    closePasses
-    processModule
-    CompileModule
-    CompileProject
-    CommandCompileToC
-    MainCommand
-    HandleCmdLine
-    nim
-  Entry: 5/3391 Calls: 32/4160 = 0.77% [sum: 285; 285/4160 = 6.9%]
-    pop
-    newCrcFromRopeAux
-    crcFromRope
-    writeRopeIfNotEqual
-    shouldRecompile
-    writeModule
-    myClose
-    closePasses
-    processModule
-    CompileModule
-    CompileProject
-    CommandCompileToC
-    MainCommand
-    HandleCmdLine
-    nim
-  Entry: 6/3391 Calls: 17/4160 = 0.41% [sum: 302; 302/4160 = 7.3%]
-    doOperation
-    forAllChildrenAux
-    pop
-    newCrcFromRopeAux
-    crcFromRope
-    writeRopeIfNotEqual
-    shouldRecompile
-    writeModule
-    myClose
-    closePasses
-    processModule
-    CompileModule
-    CompileProject
-    CommandCompileToC
-    MainCommand
-    HandleCmdLine
-    ...
-    nim
-  Entry: 7/3391 Calls: 14/4160 = 0.34% [sum: 316; 316/4160 = 7.6%]
-    Contains
-    isAccessible
-    interiorAllocatedPtr
-    gcMark
-    markStackAndRegisters
-    collectCTBody
-    collectCT
-    rawNewObj
-    newObj
-    newNode
-    copyTree
-    matchesAux
-    matches
-    resolveOverloads
-    semOverloadedCall
-    semOverloadedCallAnalyseEffects
-    ...
-    CommandCompileToC
-    MainCommand
-    HandleCmdLine
diff --git a/doc/exception_hierarchy_fragment.txt b/doc/exception_hierarchy_fragment.txt
deleted file mode 100644
index a02d9ccef..000000000
--- a/doc/exception_hierarchy_fragment.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-* `Exception <system.html#Exception>`_
-  * `AccessViolationError <system.html#AccessViolationError>`_
-  * `ArithmeticError <system.html#ArithmeticError>`_
-    * `DivByZeroError <system.html#DivByZeroError>`_
-    * `OverflowError <system.html#OverflowError>`_
-  * `AssertionError <system.html#AssertionError>`_
-  * `DeadThreadError <system.html#DeadThreadError>`_
-  * `FloatingPointError <system.html#FloatingPointError>`_
-    * `FloatDivByZeroError <system.html#FloatDivByZeroError>`_
-    * `FloatInexactError <system.html#FloatInexactError>`_
-    * `FloatInvalidOpError <system.html#FloatInvalidOpError>`_
-    * `FloatOverflowError <system.html#FloatOverflowError>`_
-    * `FloatUnderflowError <system.html#FloatUnderflowError>`_
-  * `FieldError <system.html#FieldError>`_
-  * `IndexError <system.html#IndexError>`_
-  * `ObjectAssignmentError <system.html#ObjectAssignmentError>`_
-  * `ObjectConversionError <system.html#ObjectConversionError>`_
-  * `ValueError <system.html#ValueError>`_
-    * `KeyError <system.html#KeyError>`_
-  * `ReraiseError <system.html#ReraiseError>`_
-  * `RangeError <system.html#RangeError>`_
-  * `OutOfMemoryError <system.html#OutOfMemoryError>`_
-  * `ResourceExhaustedError <system.html#ResourceExhaustedError>`_
-  * `StackOverflowError <system.html#StackOverflowError>`_
-  * `SystemError <system.html#SystemError>`_
-    * `IOError <system.html#IOError>`_
-    * `OSError <system.html#OSError>`_
-      * `LibraryError <system.html#LibraryError>`_
diff --git a/doc/filelist.txt b/doc/filelist.txt
index 71379b40d..5522414fe 100644
--- a/doc/filelist.txt
+++ b/doc/filelist.txt
@@ -5,7 +5,7 @@ Short description of Nim's modules
 Module          Description
 ==============  ==========================================================
 nim             main module: parses the command line and calls
-                ``main.MainCommand``
+                `main.MainCommand`
 main            implements the top-level command dispatching
 nimconf         implements the config file reader
 syntaxes        dispatcher for the different parsers and filters
@@ -31,16 +31,14 @@ semstmts        contains the semantic checking phase for statements
 semtypes        contains the semantic checking phase for types
 seminst         instantiation of generic procs and types
 semfold         contains code to deal with constant folding
-semthreads      deep program analysis for threads
-evals           contains an AST interpreter for compile time evaluation
+sempass2        Second semantic checking pass over the AST
+vm              contains an AST interpreter for compile time evaluation
 pragmas         semantic checking of pragmas
 
 idents          implements a general mapping from identifiers to an internal
-                representation (``PIdent``) that is used so that a simple
-                id-comparison suffices to say whether two Nim identifiers
-                are equivalent
-ropes           implements long strings represented as trees for
-                lazy evaluation; used mainly by the code generators
+                representation (`PIdent`) that is used so that a simple
+                id-comparison suffices to establish whether two Nim
+                identifiers are equivalent
 
 transf          transformations on the AST that need to be done before
                 code generation
diff --git a/doc/filters.md b/doc/filters.md
new file mode 100644
index 000000000..9482b0b47
--- /dev/null
+++ b/doc/filters.md
@@ -0,0 +1,218 @@
+===================
+Source Code Filters
+===================
+
+.. include:: rstcommon.rst
+.. default-role:: code
+.. contents::
+
+A `Source Code Filter (SCF)`  transforms the input character stream to an in-memory
+output stream before parsing. A filter can be used to provide templating
+systems or preprocessors.
+
+To use a filter for a source file the `#?` notation is used:
+
+    #? stdtmpl(subsChar = '$', metaChar = '#')
+    #proc generateXML(name, age: string): string =
+    #  result = ""
+    <xml>
+      <name>$name</name>
+      <age>$age</age>
+    </xml>
+
+As the example shows, passing arguments to a filter can be done
+just like an ordinary procedure call with named or positional arguments. The
+available parameters depend on the invoked filter. Before version 0.12.0 of
+the language `#!` was used instead of `#?`.
+
+**Hint:** With `--hint:codeBegin:on`:option: or `--verbosity:2`:option:
+(or higher) while compiling or `nim check`:cmd:, Nim lists the processed code after
+each filter application.
+
+Usage
+=====
+
+First, put your SCF code in a separate file with filters specified in the first line. 
+**Note:** You can name your SCF file with any file extension you want, but the
+conventional extension is `.nimf`
+(it used to be `.tmpl` but that was too generic, for example preventing github to
+recognize it as Nim source file).
+
+If we use `generateXML` code shown above and call the SCF file `xmlGen.nimf`
+In your `main.nim`:
+
+  ```nim
+  include "xmlGen.nimf"
+  
+  echo generateXML("John Smith","42")
+  ```
+
+Pipe operator
+=============
+
+Filters can be combined with the `|` pipe operator:
+
+    #? strip(startswith="<") | stdtmpl
+    #proc generateXML(name, age: string): string =
+    #  result = ""
+    <xml>
+      <name>$name</name>
+      <age>$age</age>
+    </xml>
+
+
+Available filters
+=================
+
+Replace filter
+--------------
+
+The replace filter replaces substrings in each line.
+
+Parameters and their defaults:
+
+* `sub: string = ""`
+  : the substring that is searched for
+
+* `by: string = ""`
+  : the string the substring is replaced with
+
+
+Strip filter
+------------
+
+The strip filter simply removes leading and trailing whitespace from
+each line.
+
+Parameters and their defaults:
+
+* `startswith: string = ""`
+  : strip only the lines that start with *startswith* (ignoring leading
+    whitespace). If empty every line is stripped.
+
+* `leading: bool = true`
+  : strip leading whitespace
+
+* `trailing: bool = true`
+  : strip trailing whitespace
+
+
+StdTmpl filter
+--------------
+
+The stdtmpl filter provides a simple templating engine for Nim. The
+filter uses a line based parser: Lines prefixed with a *meta character*
+(default: `#`) contain Nim code, other lines are verbatim. Because
+indentation-based parsing is not suited for a templating engine, control flow
+statements need `end X` delimiters.
+
+Parameters and their defaults:
+
+* `metaChar: char = '#'`
+  : prefix for a line that contains Nim code
+
+* `subsChar: char = '$'`
+  : prefix for a Nim expression within a template line
+
+* `conc: string = " & "`
+  : the operation for concatenation
+
+* `emit: string = "result.add"`
+  : the operation to emit a string literal
+
+* `toString: string = "$"`
+  : the operation that is applied to each expression
+
+Example:
+
+    #? stdtmpl | standard
+    #proc generateHTMLPage(title, currentTab, content: string,
+    #                      tabs: openArray[string]): string =
+    #  result = ""
+    <head><title>$title</title></head>
+    <body>
+      <div id="menu">
+        <ul>
+      #for tab in items(tabs):
+        #if currentTab == tab:
+        <li><a id="selected"
+        #else:
+        <li><a
+        #end if
+        href="${tab}.html">$tab</a></li>
+      #end for
+        </ul>
+      </div>
+      <div id="content">
+        $content
+        A dollar: $$.
+      </div>
+    </body>
+
+The filter transforms this into:
+
+  ```nim
+  proc generateHTMLPage(title, currentTab, content: string,
+                        tabs: openArray[string]): string =
+    result = ""
+    result.add("<head><title>" & $(title) & "</title></head>\n" &
+      "<body>\n" &
+      "  <div id=\"menu\">\n" &
+      "    <ul>\n")
+    for tab in items(tabs):
+      if currentTab == tab:
+        result.add("    <li><a id=\"selected\" \n")
+      else:
+        result.add("    <li><a\n")
+      #end
+      result.add("    href=\"" & $(tab) & ".html\">" & $(tab) & "</a></li>\n")
+    #end
+    result.add("    </ul>\n" &
+      "  </div>\n" &
+      "  <div id=\"content\">\n" &
+      "    " & $(content) & "\n" &
+      "    A dollar: $.\n" &
+      "  </div>\n" &
+      "</body>\n")
+  ```
+
+
+Each line that does not start with the meta character (ignoring leading
+whitespace) is converted to a string literal that is added to `result`.
+
+The substitution character introduces a Nim expression *e* within the
+string literal. *e* is converted to a string with the *toString* operation
+which defaults to `$`. For strong type checking, set `toString` to the
+empty string. *e* must match this PEG pattern:
+
+    e <- [a-zA-Z\128-\255][a-zA-Z0-9\128-\255_.]* / '{' x '}'
+    x <- '{' x+ '}' / [^}]*
+
+To produce a single substitution character it has to be doubled: `$$`
+produces `$`.
+
+The template engine is quite flexible. It is easy to produce a procedure that
+writes the template code directly to a file:
+
+    #? stdtmpl(emit="f.write") | standard
+    #proc writeHTMLPage(f: File, title, currentTab, content: string,
+    #                   tabs: openArray[string]) =
+    <head><title>$title</title></head>
+    <body>
+      <div id="menu">
+        <ul>
+      #for tab in items(tabs):
+        #if currentTab == tab:
+        <li><a id="selected"
+        #else:
+        <li><a
+        #end if
+        href="${tab}.html" title = "$title - $tab">$tab</a></li>
+      #end for
+        </ul>
+      </div>
+      <div id="content">
+        $content
+        A dollar: $$.
+      </div>
+    </body>
diff --git a/doc/filters.rst b/doc/filters.rst
deleted file mode 100644
index 1937b187c..000000000
--- a/doc/filters.rst
+++ /dev/null
@@ -1,198 +0,0 @@
-===================
-Source Code Filters
-===================
-
-.. contents::
-
-A `Source Code Filter` transforms the input character stream to an in-memory
-output stream before parsing. A filter can be used to provide templating
-systems or preprocessors.
-
-To use a filter for a source file the ``#?`` notation is used::
-
-  #? stdtmpl(subsChar = '$', metaChar = '#')
-  #proc generateXML(name, age: string): string =
-  #  result = ""
-  <xml>
-    <name>$name</name>
-    <age>$age</age>
-  </xml>
-
-As the example shows, passing arguments to a filter can be done
-just like an ordinary procedure call with named or positional arguments. The
-available parameters depend on the invoked filter. Before version 0.12.0 of
-the language ``#!`` was used instead of ``#?``.
-
-**Hint:** With ``--hint[codeBegin]:on```or ``--verbosity:2``
-(or higher) Nim lists the processed code after each filter
-application.
-
-
-Pipe operator
-=============
-
-Filters can be combined with the ``|`` pipe operator::
-
-  #? strip(startswith="<") | stdtmpl
-  #proc generateXML(name, age: string): string =
-  #  result = ""
-  <xml>
-    <name>$name</name>
-    <age>$age</age>
-  </xml>
-
-
-Available filters
-=================
-
-Replace filter
---------------
-
-The replace filter replaces substrings in each line.
-
-Parameters and their defaults:
-
-  ``sub: string = ""``
-    the substring that is searched for
-
-  ``by: string = ""``
-    the string the substring is replaced with
-
-
-Strip filter
-------------
-
-The strip filter simply removes leading and trailing whitespace from
-each line.
-
-Parameters and their defaults:
-
-  ``startswith: string = ""``
-    strip only the lines that start with *startswith* (ignoring leading
-    whitespace). If empty every line is stripped.
-
-  ``leading: bool = true``
-    strip leading whitespace
-
-  ``trailing: bool = true``
-    strip trailing whitespace
-
-
-StdTmpl filter
---------------
-
-The stdtmpl filter provides a simple templating engine for Nim. The
-filter uses a line based parser: Lines prefixed with a *meta character*
-(default: ``#``) contain Nim code, other lines are verbatim. Because
-indentation-based parsing is not suited for a templating engine, control flow
-statements need ``end X`` delimiters.
-
-Parameters and their defaults:
-
-  ``metaChar: char = '#'``
-    prefix for a line that contains Nim code
-
-  ``subsChar: char = '$'``
-    prefix for a Nim expression within a template line
-
-  ``conc: string = " & "``
-    the operation for concatenation
-
-  ``emit: string = "result.add"``
-    the operation to emit a string literal
-
-  ``toString: string = "$"``
-    the operation that is applied to each expression
-
-Example::
-
-  #? stdtmpl | standard
-  #proc generateHTMLPage(title, currentTab, content: string,
-  #                      tabs: openArray[string]): string =
-  #  result = ""
-  <head><title>$title</title></head>
-  <body>
-    <div id="menu">
-      <ul>
-    #for tab in items(tabs):
-      #if currentTab == tab:
-      <li><a id="selected"
-      #else:
-      <li><a
-      #end if
-      href="${tab}.html">$tab</a></li>
-    #end for
-      </ul>
-    </div>
-    <div id="content">
-      $content
-      A dollar: $$.
-    </div>
-  </body>
-
-The filter transforms this into:
-
-.. code-block:: nim
-  proc generateHTMLPage(title, currentTab, content: string,
-                        tabs: openArray[string]): string =
-    result = ""
-    result.add("<head><title>" & $(title) & "</title></head>\n" &
-      "<body>\n" &
-      "  <div id=\"menu\">\n" &
-      "    <ul>\n")
-    for tab in items(tabs):
-      if currentTab == tab:
-        result.add("    <li><a id=\"selected\" \n")
-      else:
-        result.add("    <li><a\n")
-      #end
-      result.add("    href=\"" & $(tab) & ".html\">" & $(tab) & "</a></li>\n")
-    #end
-    result.add("    </ul>\n" &
-      "  </div>\n" &
-      "  <div id=\"content\">\n" &
-      "    " & $(content) & "\n" &
-      "    A dollar: $.\n" &
-      "  </div>\n" &
-      "</body>\n")
-
-
-Each line that does not start with the meta character (ignoring leading
-whitespace) is converted to a string literal that is added to ``result``.
-
-The substitution character introduces a Nim expression *e* within the
-string literal. *e* is converted to a string with the *toString* operation
-which defaults to ``$``. For strong type checking, set ``toString`` to the
-empty string. *e* must match this PEG pattern::
-
-  e <- [a-zA-Z\128-\255][a-zA-Z0-9\128-\255_.]* / '{' x '}'
-  x <- '{' x+ '}' / [^}]*
-
-To produce a single substitution character it has to be doubled: ``$$``
-produces ``$``.
-
-The template engine is quite flexible. It is easy to produce a procedure that
-writes the template code directly to a file::
-
-  #? stdtmpl(emit="f.write") | standard
-  #proc writeHTMLPage(f: File, title, currentTab, content: string,
-  #                   tabs: openArray[string]) =
-  <head><title>$title</title></head>
-  <body>
-    <div id="menu">
-      <ul>
-    #for tab in items(tabs):
-      #if currentTab == tab:
-      <li><a id="selected"
-      #else:
-      <li><a
-      #end if
-      href="${tab}.html" title = "$title - $tab">$tab</a></li>
-    #end for
-      </ul>
-    </div>
-    <div id="content">
-      $content
-      A dollar: $$.
-    </div>
-  </body>
diff --git a/doc/gc.rst b/doc/gc.rst
deleted file mode 100644
index 7ec4e6d36..000000000
--- a/doc/gc.rst
+++ /dev/null
@@ -1,146 +0,0 @@
-==========================
-Nim's Garbage Collector
-==========================
-
-:Author: Andreas Rumpf
-:Version: |nimversion|
-
-..
-
-
-  "The road to hell is paved with good intentions."
-
-
-Introduction
-============
-
-This document describes how the GC works and how to tune it for
-(soft) `realtime systems`:idx:.
-
-The basic algorithm is *Deferred Reference Counting* with cycle detection.
-References on the stack are not counted for better performance (and easier C
-code generation). Cycle detection is currently done by a simple mark&sweep
-GC that has to scan the full (thread local heap). ``--gc:v2`` replaces this
-with an incremental mark and sweep. That it is not production ready yet,
-however.
-
-
-The GC is only triggered in a memory allocation operation. It is not triggered
-by some timer and does not run in a background thread.
-
-To force a full collection call ``GC_fullCollect``. Note that it is generally
-better to let the GC do its work and not enforce a full collection.
-
-
-Cycle collector
-===============
-
-The cycle collector can be en-/disabled independently from the other parts of
-the GC with ``GC_enableMarkAndSweep`` and ``GC_disableMarkAndSweep``.
-
-
-Realtime support
-================
-
-To enable realtime support, the symbol `useRealtimeGC`:idx: needs to be
-defined via ``--define:useRealtimeGC`` (you can put this into your config
-file as well). With this switch the GC supports the following operations:
-
-.. code-block:: nim
-  proc GC_setMaxPause*(maxPauseInUs: int)
-  proc GC_step*(us: int, strongAdvice = false, stackSize = -1)
-
-The unit of the parameters ``maxPauseInUs`` and ``us`` is microseconds.
-
-These two procs are the two modus operandi of the realtime GC:
-
-(1) GC_SetMaxPause Mode
-
-    You can call ``GC_SetMaxPause`` at program startup and then each triggered
-    GC run tries to not take longer than ``maxPause`` time. However, it is
-    possible (and common) that the work is nevertheless not evenly distributed
-    as each call to ``new`` can trigger the GC and thus take  ``maxPause``
-    time.
-
-(2) GC_step Mode
-
-    This allows the GC to perform some work for up to ``us`` time. This is
-    useful to call in a main loop to ensure the GC can do its work. To
-    bind all GC activity to a ``GC_step`` call, deactivate the GC with
-    ``GC_disable`` at program startup. If ``strongAdvice`` is set to ``true``,
-    GC will be forced to perform collection cycle. Otherwise, GC may decide not
-    to do anything, if there is not much garbage to collect.
-    You may also specify the current stack size via ``stackSize`` parameter.
-    It can improve performance, when you know that there are no unique Nim
-    references below certain point on the stack. Make sure the size you specify
-    is greater than the potential worst case size.
-
-These procs provide a "best effort" realtime guarantee; in particular the
-cycle collector is not aware of deadlines yet. Deactivate it to get more
-predictable realtime behaviour. Tests show that a 2ms max pause
-time will be met in almost all cases on modern CPUs (with the cycle collector
-disabled).
-
-
-Time measurement
-----------------
-
-The GC's way of measuring time uses (see ``lib/system/timers.nim`` for the
-implementation):
-
-1) ``QueryPerformanceCounter`` and ``QueryPerformanceFrequency`` on Windows.
-2) ``mach_absolute_time`` on Mac OS X.
-3) ``gettimeofday`` on Posix systems.
-
-As such it supports a resolution of nanoseconds internally; however the API
-uses microseconds for convenience.
-
-
-Define the symbol ``reportMissedDeadlines`` to make the GC output whenever it
-missed a deadline. The reporting will be enhanced and supported by the API in
-later versions of the collector.
-
-
-Tweaking the GC
----------------
-
-The collector checks whether there is still time left for its work after
-every ``workPackage``'th iteration. This is currently set to 100 which means
-that up to 100 objects are traversed and freed before it checks again. Thus
-``workPackage`` affects the timing granularity and may need to be tweaked in
-highly specialized environments or for older hardware.
-
-
-Keeping track of memory
------------------------
-
-If you need to pass around memory allocated by Nim to C, you can use the
-procs ``GC_ref`` and ``GC_unref`` to mark objects as referenced to avoid them
-being freed by the GC. Other useful procs from `system <system.html>`_ you can
-use to keep track of memory are:
-
-* getTotalMem(): returns the amount of total memory managed by the GC.
-* getOccupiedMem(): bytes reserved by the GC and used by objects.
-* getFreeMem(): bytes reserved by the GC and not in use.
-
-In addition to ``GC_ref`` and ``GC_unref`` you can avoid the GC by manually
-allocating memory with procs like ``alloc``, ``allocShared``, or
-``allocCStringArray``. The GC won't try to free them, you need to call their
-respective *dealloc* pairs when you are done with them or they will leak.
-
-
-Heap dump
-=========
-
-The heap dump feature is still in its infancy, but it already proved
-useful for us, so it might be useful for you. To get a heap dump, compile
-with ``-d:nimTypeNames`` and call ``dumpNumberOfInstances`` at a strategic place in your program.
-This produces a list of used types in your program and for every type
-the total amount of object instances for this type as well as the total
-amount of bytes these instances take up. This list is currently unsorted!
-You need to use external shell script hacking to sort it.
-
-The numbers count the number of objects in all GC heaps, they refer to
-all running threads, not only to the current thread. (The current thread
-would be the thread that calls ``dumpNumberOfInstances``.) This might
-change in later versions.
diff --git a/doc/grammar.txt b/doc/grammar.txt
index e06ebd5d9..51b3e0053 100644
--- a/doc/grammar.txt
+++ b/doc/grammar.txt
@@ -1,15 +1,15 @@
 # This file is generated by compiler/parser.nim.
-module = stmt ^* (';' / IND{=})
+module = complexOrSimpleStmt ^* (';' / IND{=})
 comma = ',' COMMENT?
 semicolon = ';' COMMENT?
 colon = ':' COMMENT?
 colcom = ':' COMMENT?
 operator =  OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9
          | 'or' | 'xor' | 'and'
-         | 'is' | 'isnot' | 'in' | 'notin' | 'of'
-         | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..'
+         | 'is' | 'isnot' | 'in' | 'notin' | 'of' | 'as' | 'from'
+         | 'div' | 'mod' | 'shl' | 'shr' | 'not' | '..'
 prefixOperator = operator
-optInd = COMMENT?
+optInd = COMMENT? IND?
 optPar = (IND{>} | IND{=})?
 simpleExpr = arrowExpr (OP0 optInd arrowExpr)* pragma?
 arrowExpr = assignExpr (OP1 optInd assignExpr)*
@@ -22,111 +22,129 @@ ampExpr = plusExpr (OP7 optInd plusExpr)*
 plusExpr = mulExpr (OP8 optInd mulExpr)*
 mulExpr = dollarExpr (OP9 optInd dollarExpr)*
 dollarExpr = primary (OP10 optInd primary)*
+operatorB = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9 |
+            'div' | 'mod' | 'shl' | 'shr' | 'in' | 'notin' |
+            'is' | 'isnot' | 'not' | 'of' | 'as' | 'from' | '..' | 'and' | 'or' | 'xor'
 symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
-       | IDENT | KEYW
-exprColonEqExpr = expr (':'|'=' expr)?
+       | IDENT | 'addr' | 'type' | 'static'
+symbolOrKeyword = symbol | KEYW
+exprColonEqExpr = expr ((':'|'=') expr
+                       / doBlock extraPostExprBlock*)?
+exprEqExpr = expr ('=' expr
+                  / doBlock extraPostExprBlock*)?
 exprList = expr ^+ comma
+optionalExprList = expr ^* comma
 exprColonEqExprList = exprColonEqExpr (comma exprColonEqExpr)* (comma)?
-dotExpr = expr '.' optInd (symbol | '[:' exprList ']')
-explicitGenericInstantiation = '[:' exprList ']' ( '(' exprColonEqExpr ')' )?
-qualifiedIdent = symbol ('.' optInd symbol)?
+qualifiedIdent = symbol ('.' optInd symbolOrKeyword)?
 setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}'
-castExpr = 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')'
+castExpr = 'cast' ('[' optInd typeDesc optPar ']' '(' optInd expr optPar ')') /
 parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
         | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let'
         | 'when' | 'var' | 'mixin'
 par = '(' optInd
-          ( &parKeyw complexOrSimpleStmt ^+ ';'
-          | ';' complexOrSimpleStmt ^+ ';'
+          ( &parKeyw (ifExpr / complexOrSimpleStmt) ^+ ';'
+          | ';' (ifExpr / complexOrSimpleStmt) ^+ ';'
           | pragmaStmt
-          | simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )
+          | simpleExpr ( (doBlock extraPostExprBlock*)
+                       | ('=' expr (';' (ifExpr / complexOrSimpleStmt) ^+ ';' )? )
                        | (':' expr (',' exprColonEqExpr     ^+ ',' )? ) ) )
           optPar ')'
 literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
           | UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT
           | FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT
           | STR_LIT | RSTR_LIT | TRIPLESTR_LIT
-          | CHAR_LIT
+          | CHAR_LIT | CUSTOM_NUMERIC_LIT
           | NIL
 generalizedLit = GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT
 identOrLiteral = generalizedLit | symbol | literal
-               | par | arrayConstr | setOrTableConstr
+               | par | arrayConstr | setOrTableConstr | tupleConstr
                | castExpr
 tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')'
 arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']'
-primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
-      | doBlocks
-      | '.' optInd symbol generalizedLit?
-      | '[' optInd indexExprList optPar ']'
-      | '{' optInd indexExprList optPar '}'
-      | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax
-condExpr = expr colcom expr optInd
-        ('elif' expr colcom expr optInd)*
-         'else' colcom expr
-ifExpr = 'if' condExpr
-whenExpr = 'when' condExpr
-pragma = '{.' optInd (exprColonExpr comma?)* optPar ('.}' | '}')
-identVis = symbol opr?  # postfix position
-identVisDot = symbol '.' optInd symbol opr?
+primarySuffix = '(' (exprColonEqExpr comma?)* ')'
+      | '.' optInd symbolOrKeyword ('[:' exprList ']' ( '(' exprColonEqExpr ')' )?)? generalizedLit?
+      | DOTLIKEOP optInd symbolOrKeyword generalizedLit?
+      | '[' optInd exprColonEqExprList optPar ']'
+      | '{' optInd exprColonEqExprList optPar '}'
+pragma = '{.' optInd (exprColonEqExpr comma?)* optPar ('.}' | '}')
+identVis = symbol OPR?  # postfix position
+identVisDot = symbol '.' optInd symbolOrKeyword OPR?
 identWithPragma = identVis pragma?
 identWithPragmaDot = identVisDot pragma?
 declColonEquals = identWithPragma (comma identWithPragma)* comma?
-                  (':' optInd typeDesc)? ('=' optInd expr)?
-identColonEquals = ident (comma ident)* comma?
-     (':' optInd typeDesc)? ('=' optInd expr)?)
-inlTupleDecl = 'tuple'
-    [' optInd  (identColonEquals (comma/semicolon)?)*  optPar ']'
-extTupleDecl = 'tuple'
-    COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)?
-tupleClass = 'tuple'
+                  (':' optInd typeDescExpr)? ('=' optInd expr)?
+identColonEquals = IDENT (comma IDENT)* comma?
+     (':' optInd typeDescExpr)? ('=' optInd expr)?)
+tupleTypeBracket = '[' optInd (identColonEquals (comma/semicolon)?)* optPar ']'
+tupleType = 'tuple' tupleTypeBracket
+tupleDecl = 'tuple' (tupleTypeBracket /
+    COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)?)
 paramList = '(' declColonEquals ^* (comma/semicolon) ')'
 paramListArrow = paramList? ('->' optInd typeDesc)?
 paramListColon = paramList? (':' optInd typeDesc)?
-doBlock = 'do' paramListArrow pragmas? colcom stmt
-procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)?
-distinct = 'distinct' optInd typeDesc
+doBlock = 'do' paramListArrow pragma? colcom stmt
+routineExpr = ('proc' | 'func' | 'iterator') paramListColon pragma? ('=' COMMENT? stmt)?
+routineType = ('proc' | 'iterator') paramListColon pragma?
+forStmt = 'for' ((varTuple / identWithPragma) ^+ comma) 'in' expr colcom stmt
+forExpr = forStmt
 expr = (blockExpr
       | ifExpr
       | whenExpr
-      | caseExpr
+      | caseStmt
+      | forExpr
       | tryExpr)
       / simpleExpr
-typeKeyw = 'var' | 'out' | 'ref' | 'ptr' | 'shared' | 'tuple'
-         | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
-primary = typeKeyw typeDescK
-        /  prefixOperator* identOrLiteral primarySuffix*
-        / 'static' primary
-        / 'bind' primary
-typeDesc = simpleExpr
-typeDefAux = simpleExpr
-           | 'concept' typeClass
-postExprBlocks = ':' stmt? ( IND{=} doBlock
-                           | IND{=} 'of' exprList ':' stmt
-                           | IND{=} 'elif' expr ':' stmt
-                           | IND{=} 'except' exprList ':' stmt
-                           | IND{=} 'else' ':' stmt )*
-exprStmt = simpleExpr
-         (( '=' optInd expr colonBody? )
-         / ( expr ^+ comma
-             doBlocks
-              / macroColon
-           ))?
+simplePrimary = SIGILLIKEOP? identOrLiteral primarySuffix*
+commandStart = &('`'|IDENT|literal|'cast'|'addr'|'type'|'var'|'out'|
+                 'static'|'enum'|'tuple'|'object'|'proc')
+primary = simplePrimary (commandStart expr (doBlock extraPostExprBlock*)?)?
+        / operatorB primary
+        / routineExpr
+        / rawTypeDesc
+        / prefixOperator primary
+rawTypeDesc = (tupleType | routineType | 'enum' | 'object' |
+                ('var' | 'out' | 'ref' | 'ptr' | 'distinct') typeDesc?)
+                ('not' primary)?
+typeDescExpr = (routineType / simpleExpr) ('not' primary)?
+typeDesc = rawTypeDesc / typeDescExpr
+typeDefValue = ((tupleDecl | enumDecl | objectDecl | conceptDecl |
+                 ('ref' | 'ptr' | 'distinct') (tupleDecl | objectDecl))
+               / (simpleExpr (exprEqExpr ^+ comma postExprBlocks?)?))
+               ('not' primary)?
+extraPostExprBlock = ( IND{=} doBlock
+                     | IND{=} 'of' exprList ':' stmt
+                     | IND{=} 'elif' expr ':' stmt
+                     | IND{=} 'except' optionalExprList ':' stmt
+                     | IND{=} 'finally' ':' stmt
+                     | IND{=} 'else' ':' stmt )
+postExprBlocks = (doBlock / ':' (extraPostExprBlock / stmt)) extraPostExprBlock*
+exprStmt = simpleExpr postExprBlocks?
+         / simplePrimary (exprEqExpr ^+ comma) postExprBlocks?
+         / simpleExpr '=' optInd (expr postExprBlocks?)
 importStmt = 'import' optInd expr
               ((comma expr)*
               / 'except' optInd (expr ^+ comma))
+exportStmt = 'export' optInd expr
+              ((comma expr)*
+              / 'except' optInd (expr ^+ comma))
 includeStmt = 'include' optInd expr ^+ comma
-fromStmt = 'from' moduleName 'import' optInd expr (comma expr)*
+fromStmt = 'from' expr 'import' optInd expr (comma expr)*
 returnStmt = 'return' optInd expr?
 raiseStmt = 'raise' optInd expr?
 yieldStmt = 'yield' optInd expr?
 discardStmt = 'discard' optInd expr?
 breakStmt = 'break' optInd expr?
-continueStmt = 'break' optInd expr?
+continueStmt = 'continue' optInd expr?
 condStmt = expr colcom stmt COMMENT?
            (IND{=} 'elif' expr colcom stmt)*
            (IND{=} 'else' colcom stmt)?
 ifStmt = 'if' condStmt
 whenStmt = 'when' condStmt
+condExpr = expr colcom stmt optInd
+        ('elif' expr colcom stmt optInd)*
+         'else' colcom stmt
+ifExpr = 'if' condExpr
+whenExpr = 'when' condExpr
 whileStmt = 'while' expr colcom stmt
 ofBranch = 'of' exprList colcom stmt
 ofBranches = ofBranch (IND{=} ofBranch)*
@@ -136,18 +154,16 @@ caseStmt = 'case' expr ':'? COMMENT?
             (IND{>} ofBranches DED
             | IND{=} ofBranches)
 tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally')
-           (IND{=}? 'except' exprList colcom stmt)*
+           (IND{=}? 'except' optionalExprList colcom stmt)*
            (IND{=}? 'finally' colcom stmt)?
 tryExpr = 'try' colcom stmt &(optInd 'except'|'finally')
-           (optInd 'except' exprList colcom stmt)*
+           (optInd 'except' optionalExprList colcom stmt)*
            (optInd 'finally' colcom stmt)?
-exceptBlock = 'except' colcom stmt
-forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt
 blockStmt = 'block' symbol? colcom stmt
 blockExpr = 'block' symbol? colcom stmt
 staticStmt = 'static' colcom stmt
 deferStmt = 'defer' colcom stmt
-asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLE_STR_LIT)
+asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
 genericParam = symbol (comma symbol)* (colon expr)? ('=' optInd expr)?
 genericParamList = '[' optInd
   genericParam ^* (comma/semicolon) optPar ']'
@@ -156,9 +172,8 @@ indAndComment = (IND{>} COMMENT)? | COMMENT?
 routine = optInd identVis pattern? genericParamList?
   paramListColon pragma? ('=' COMMENT? stmt)? indAndComment
 commentStmt = COMMENT
-section(p) = COMMENT? p / (IND{>} (p / COMMENT)^+IND{=} DED)
-constant = identWithPragma (colon typeDesc)? '=' optInd expr indAndComment
-enum = 'enum' optInd (symbol optInd ('=' optInd expr COMMENT?)? comma?)+
+section(RULE) = COMMENT? RULE / (IND{>} (RULE / COMMENT)^+IND{=} DED)
+enumDecl = 'enum' optInd (symbol pragma? optInd ('=' optInd expr COMMENT?)? comma?)+
 objectWhen = 'when' expr colcom objectPart COMMENT?
             ('elif' expr colcom objectPart COMMENT?)*
             ('else' colcom objectPart COMMENT?)?
@@ -166,20 +181,22 @@ objectBranch = 'of' exprList colcom objectPart
 objectBranches = objectBranch (IND{=} objectBranch)*
                       (IND{=} 'elif' expr colcom objectPart)*
                       (IND{=} 'else' colcom objectPart)?
-objectCase = 'case' identWithPragma ':' typeDesc ':'? COMMENT?
+objectCase = 'case' declColonEquals ':'? COMMENT?
             (IND{>} objectBranches DED
             | IND{=} objectBranches)
 objectPart = IND{>} objectPart^+IND{=} DED
            / objectWhen / objectCase / 'nil' / 'discard' / declColonEquals
-object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart
-typeClassParam = ('var' | 'out')? symbol
-typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
+objectDecl = 'object' ('of' typeDesc)? COMMENT? objectPart
+conceptParam = ('var' | 'out' | 'ptr' | 'ref' | 'static' | 'type')? symbol
+conceptDecl = 'concept' conceptParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
               &IND{>} stmt
-typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux
+typeDef = identVisDot genericParamList? pragma '=' optInd typeDefValue
             indAndComment?
-varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr
-colonBody = colcom stmt doBlocks?
+varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')' (':' optInd typeDescExpr)?
+varTuple = varTupleLhs '=' optInd expr
+colonBody = colcom stmt postExprBlocks?
 variable = (varTuple / identColonEquals) colonBody? indAndComment
+constant = (varTuple / identWithPragma) (colon typeDesc)? '=' optInd expr indAndComment
 bindStmt = 'bind' optInd qualifiedIdent ^+ comma
 mixinStmt = 'mixin' optInd qualifiedIdent ^+ comma
 pragmaStmt = pragma (':' COMMENT? stmt)?
@@ -191,6 +208,7 @@ complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt
                     | blockStmt | staticStmt | deferStmt | asmStmt
                     | 'proc' routine
                     | 'method' routine
+                    | 'func' routine
                     | 'iterator' routine
                     | 'macro' routine
                     | 'template' routine
diff --git a/doc/hcr.md b/doc/hcr.md
new file mode 100644
index 000000000..285a86282
--- /dev/null
+++ b/doc/hcr.md
@@ -0,0 +1,245 @@
+===================================
+      Hot code reloading
+===================================
+
+.. default-role:: code
+.. include:: rstcommon.rst
+
+The `hotCodeReloading`:idx: option enables special compilation mode where
+changes in the code can be applied automatically to a running program.
+The code reloading happens at the granularity of an individual module.
+When a module is reloaded, any newly added global variables will be
+initialized, but all other top-level code appearing in the module won't
+be re-executed and the state of all existing global variables will be
+preserved.
+
+
+Basic workflow
+==============
+
+Currently, hot code reloading does not work for the main module itself,
+so we have to use a helper module where the major logic we want to change
+during development resides.
+
+In this example, we use SDL2 to create a window and we reload the logic
+code when `F9` is pressed. The important lines are marked with `#***`.
+To install SDL2 you can use `nimble install sdl2`:cmd:.
+
+
+  ```nim
+  # logic.nim
+  import sdl2
+
+  #*** import the hotcodereloading stdlib module ***
+  import std/hotcodereloading
+
+  var runGame*: bool = true
+  var window: WindowPtr
+  var renderer: RendererPtr
+  var evt = sdl2.defaultEvent
+
+  proc init*() =
+    discard sdl2.init(INIT_EVERYTHING)
+    window = createWindow("testing", SDL_WINDOWPOS_UNDEFINED.cint, SDL_WINDOWPOS_UNDEFINED.cint, 640, 480, 0'u32)
+    assert(window != nil, $sdl2.getError())
+    renderer = createRenderer(window, -1, RENDERER_SOFTWARE)
+    assert(renderer != nil, $sdl2.getError())
+
+  proc destroy*() =
+    destroyRenderer(renderer)
+    destroyWindow(window)
+
+  var posX: cint = 1
+  var posY: cint = 0
+  var dX: cint = 1
+  var dY: cint = 1
+
+  proc update*() =
+    while pollEvent(evt):
+      if evt.kind == QuitEvent:
+        runGame = false
+        break
+      if evt.kind == KeyDown:
+        if evt.key.keysym.scancode == SDL_SCANCODE_ESCAPE: runGame = false
+        elif evt.key.keysym.scancode == SDL_SCANCODE_F9:
+          #*** reload this logic.nim module on the F9 keypress ***
+          performCodeReload()
+
+    # draw a bouncing rectangle:
+    posX += dX
+    posY += dY
+
+    if posX >= 640: dX = -2
+    if posX <= 0: dX = +2
+    if posY >= 480: dY = -2
+    if posY <= 0: dY = +2
+
+    discard renderer.setDrawColor(0, 0, 255, 255)
+    discard renderer.clear()
+    discard renderer.setDrawColor(255, 128, 128, 0)
+
+    var rect: Rect = (x: posX - 25, y: posY - 25, w: 50.cint, h: 50.cint)
+    discard renderer.fillRect(rect)
+    delay(16)
+    renderer.present()
+  ```
+
+
+  ```nim
+  # mymain.nim
+  import logic
+
+  proc main() =
+    init()
+    while runGame:
+      update()
+    destroy()
+
+  main()
+  ```
+
+
+Compile this example via:
+
+  ```cmd
+  nim c --hotcodereloading:on mymain.nim
+  ```
+
+Now start the program and KEEP it running!
+
+  ```cmd
+  # Unix:
+  mymain &
+  # or Windows (click on the .exe)
+  mymain.exe
+  # edit
+  ```
+
+For example, change the line:
+
+  ```nim
+  discard renderer.setDrawColor(255, 128, 128, 0)
+  ```
+
+into:
+
+  ```nim
+  discard renderer.setDrawColor(255, 255, 128, 0)
+  ```
+
+(This will change the color of the rectangle.)
+
+Then recompile the project, but do not restart or quit the mymain.exe program!
+
+  ```cmd
+  nim c --hotcodereloading:on mymain.nim
+  ```
+
+Now give the `mymain` SDL window the focus, press F9, and watch the
+updated version of the program.
+
+
+
+Reloading API
+=============
+
+One can use the special event handlers `beforeCodeReload` and
+`afterCodeReload` to reset the state of a particular variable or to force
+the execution of certain statements:
+
+  ```Nim
+  var
+   settings = initTable[string, string]()
+   lastReload: Time
+
+  for k, v in loadSettings():
+    settings[k] = v
+
+  initProgram()
+
+  afterCodeReload:
+    lastReload = now()
+    resetProgramState()
+  ```
+
+On each code reload, Nim will first execute all `beforeCodeReload`:idx:
+handlers registered in the previous version of the program and then all
+`afterCodeReload`:idx: handlers appearing in the newly loaded code. Please note
+that any handlers appearing in modules that weren't reloaded will also be
+executed. To prevent this behavior, one can guard the code with the
+`hasModuleChanged()`:idx: API:
+
+  ```Nim
+  import mydb
+
+  var myCache = initTable[Key, Value]()
+
+  afterCodeReload:
+    if hasModuleChanged(mydb):
+      resetCache(myCache)
+  ```
+
+The hot code reloading is based on dynamic library hot swapping in the native
+targets and direct manipulation of the global namespace in the JavaScript
+target. The Nim compiler does not specify the mechanism for detecting the
+conditions when the code must be reloaded. Instead, the program code is
+expected to call `performCodeReload()`:idx: every time it wishes to reload
+its code.
+
+It's expected that most projects will implement the reloading with a suitable
+build-system triggered IPC notification mechanism, but a polling solution is
+also possible through the provided `hasAnyModuleChanged()`:idx: API.
+
+In order to access `beforeCodeReload`, `afterCodeReload`, `hasModuleChanged`
+or `hasAnyModuleChanged` one must import the `hotcodereloading`:idx: module.
+
+
+Native code targets
+===================
+
+Native projects using the hot code reloading option will be implicitly
+compiled with the `-d:useNimRtl`:option: option and they will depend on both
+the `nimrtl` library and the `nimhcr` library which implements the
+hot code reloading run-time. Both libraries can be found in the `lib`
+folder of Nim and can be compiled into dynamic libraries to satisfy
+runtime demands of the example code above. An example of compiling
+``nimhcr.nim`` and ``nimrtl.nim`` when the source dir of Nim is installed
+with choosenim follows.
+
+  ```console
+  # Unix/MacOS
+  # Make sure you are in the directory containing your .nim files
+  $ cd your-source-directory
+
+  # Compile two required files and set their output directory to current dir
+  $ nim c --outdir:$PWD ~/.choosenim/toolchains/nim-#devel/lib/nimhcr.nim
+  $ nim c --outdir:$PWD ~/.choosenim/toolchains/nim-#devel/lib/nimrtl.nim
+
+  # verify that you have two files named libnimhcr and libnimrtl in your
+  # source directory (.dll for Windows, .so for Unix, .dylib for MacOS)
+  ```
+
+All modules of the project will be compiled to separate dynamic link
+libraries placed in the `nimcache` directory. Please note that during
+the execution of the program, the hot code reloading run-time will load
+only copies of these libraries in order to not interfere with any newly
+issued build commands.
+
+The main module of the program is considered non-reloadable. Please note
+that procs from reloadable modules should not appear in the call stack of
+program while `performCodeReload` is being called. Thus, the main module
+is a suitable place for implementing a program loop capable of calling
+`performCodeReload`.
+
+Please note that reloading won't be possible when any of the type definitions
+in the program has been changed. When closure iterators are used (directly or
+through async code), the reloaded definitions will affect only newly created
+instances. Existing iterator instances will execute their original code to
+completion.
+
+JavaScript target
+=================
+
+Once your code is compiled for hot reloading, a convenient solution for implementing the actual reloading
+in the browser using a framework such as [LiveReload](http://livereload.com/)
+or [BrowserSync](https://browsersync.io/).
diff --git a/doc/idetools.md b/doc/idetools.md
new file mode 100644
index 000000000..0388a76c0
--- /dev/null
+++ b/doc/idetools.md
@@ -0,0 +1,617 @@
+.. default-role:: code
+
+================================
+  Nim IDE Integration Guide
+================================
+
+:Author: Britney Spears
+:Version: |nimversion|
+
+.. contents::
+
+
+> "yes, I'm the creator" -- Araq, 2013-07-26 19:28:32.
+
+Note: this is mostly outdated, see instead [nimsuggest](nimsuggest.html)
+
+Nim differs from many other compilers in that it is really fast,
+and being so fast makes it suited to provide external queries for
+text editors about the source code being written. Through the
+`idetools` command of [the compiler](nimc.html), any IDE
+can query a `.nim` source file and obtain useful information like
+definition of symbols or suggestions for completion.
+
+This document will guide you through the available options. If you
+want to look at practical examples of idetools support you can look
+at the test files found in the [Test suite] or [various editor
+integrations](https://github.com/Araq/Nim/wiki/Editor-Support)
+already available.
+
+
+Idetools invocation
+===================
+
+Specifying the location of the query
+------------------------------------
+
+All of the available idetools commands require you to specify a
+query location through the `--track` or `--trackDirty` switches.
+The general idetools invocations are:
+
+  ```cmd
+  nim idetools --track:FILE,LINE,COL <switches> proj.nim
+  ```
+
+Or:
+
+  ```cmd
+  nim idetools --trackDirty:DIRTY_FILE,FILE,LINE,COL <switches> proj.nim
+  ```
+
+`proj.nim`
+:   This is the main *project* filename. Most of the time you will
+    pass in the same as **FILE**, but for bigger projects this is
+    the file which is used as main entry point for the program, the
+    one which users compile to generate a final binary.
+
+`<switches>`
+:   This would be any of the other idetools available options, like
+    `--def` or `--suggest` explained in the following sections.
+
+`COL`
+:   An integer with the column you are going to query. For the
+    compiler columns start at zero, so the first column will be
+    **0** and the last in an 80 column terminal will be **79**.
+
+`LINE`
+:   An integer with the line you are going to query. For the compiler
+    lines start at **1**.
+
+`FILE`
+:   The file you want to perform the query on. Usually you will
+    pass in the same value as **proj.nim**.
+
+`DIRTY_FILE`
+:   The **FILE** parameter is enough for static analysis, but IDEs
+    tend to have *unsaved buffers* where the user may still be in
+    the middle of typing a line. In such situations the IDE can
+    save the current contents to a temporary file and then use the
+    `--trackDirty` switch.
+
+    Dirty files are likely to contain errors and they are usually
+    compiled partially only to the point needed to service the
+    idetool request. The compiler discriminates them to ensure that
+    **a)** they won't be cached and **b)** they won't invalidate
+    the cached contents of the original module.
+
+    The other reason is that the dirty file can appear anywhere on
+    disk (e.g. in tmpfs), but it must be treated as having a path
+    matching the original module when it comes to usage of relative
+    paths, etc. Queries, however, will refer to the dirty module
+    name in their answers instead of the normal filename.
+
+
+Definitions
+-----------
+
+The `--def` idetools switch performs a query about the definition
+of a specific symbol. If available, idetools will answer with the
+type, source file, line/column information and other accessory data
+if available like a docstring. With this information an IDE can
+provide the typical *Jump to definition* where a user puts the
+cursor on a symbol or uses the mouse to select it and is redirected
+to the place where the symbol is located.
+
+Since Nim is implemented in Nim, one of the nice things of
+this feature is that any user with an IDE supporting it can quickly
+jump around the standard library implementation and see exactly
+what a proc does, learning about the language and seeing real life
+examples of how to write/implement specific features.
+
+Idetools will always answer with a single definition or none if it
+can't find any valid symbol matching the position of the query.
+
+
+Suggestions
+-----------
+
+The `--suggest` idetools switch performs a query about possible
+completion symbols at some point in the file. IDEs can easily provide
+an autocompletion feature where the IDE scans the current file (and
+related ones, if it knows about the language being edited and follows
+includes/imports) and when the user starts typing something a
+completion box with different options appears.
+
+However such features are not context sensitive and work simply on
+string matching, which can be problematic in Nim especially due
+to the case insensitiveness of the language (plus underscores as
+separators!).
+
+The typical usage scenario for this option is to call it after the
+user has typed the dot character for [the object oriented call
+syntax](tut2.html#object-oriented-programming-method-call-syntax).
+Idetools will try to return the suggestions sorted first by scope
+(from innermost to outermost) and then by item name.
+
+
+Invocation context
+------------------
+
+The `--context` idetools switch is very similar to the suggestions
+switch, but instead of being used after the user has typed a dot
+character, this one is meant to be used after the user has typed
+an opening brace to start typing parameters.
+
+
+Symbol usages
+-------------
+
+The `--usages` idetools switch lists all usages of the symbol at
+a position. IDEs can use this to find all the places in the file
+where the symbol is used and offer the user to rename it in all
+places at the same time. Again, a pure string based search and
+replace may catch symbols out of the scope of a function/loop.
+
+For this kind of query the IDE will most likely ignore all the
+type/signature info provided by idetools and concentrate on the
+filename, line and column position of the multiple returned answers.
+
+
+Expression evaluation
+---------------------
+
+This feature is still under development. In the future it will allow
+an IDE to evaluate an expression in the context of the currently
+running/debugged user project.
+
+
+Compiler as a service (CAAS)
+============================
+
+The occasional use of idetools is acceptable for things like
+definitions, where the user puts the cursor on a symbol or double
+clicks it and after a second or two the IDE displays where that
+symbol is defined. Such latencies would be terrible for features
+like symbol suggestion, plus why wait at all if we can avoid it?
+
+The idetools command can be run as a compiler service (CAAS),
+where you first launch the compiler and it will stay online as a
+server, accepting queries in a telnet like fashion. The advantage
+of staying on is that for many queries the compiler can cache the
+results of the compilation, and subsequent queries should be fast
+in the millisecond range, thus being responsive enough for IDEs.
+
+If you want to start the server using stdin/stdout as communication
+you need to type:
+
+  ```cmd
+  nim serve --server.type:stdin proj.nim
+  ```
+
+If you want to start the server using tcp and a port, you need to type:
+
+  ```cmd
+  nim serve --server.type:tcp --server.port:6000 \
+      --server.address:hostname proj.nim
+  ```
+
+In both cases the server will start up and await further commands.
+The syntax of the commands you can now send to the server is
+practically the same as running the nim compiler on the commandline,
+you only need to remove the name of the compiler since you are
+already talking to it. The server will answer with as many lines
+of text it thinks necessary plus an empty line to indicate the end
+of the answer.
+
+You can find examples of client/server communication in the idetools
+tests found in the [Test suite].
+
+
+Parsing idetools output
+=======================
+
+Idetools outputs is always returned on single lines separated by
+tab characters (``\t``). The values of each column are:
+
+1. Three characters indicating the type of returned answer (e.g.
+   def for definition, `sug` for suggestion, etc).
+2. Type of the symbol. This can be `skProc`, `skLet`, and just
+   about any of the enums defined in the module `compiler/ast.nim`.
+3. Full qualified path of the symbol. If you are querying a symbol
+   defined in the `proj.nim` file, this would have the form
+   `proj.symbolName`.
+4. Type/signature. For variables and enums this will contain the
+   type of the symbol, for procs, methods and templates this will
+   contain the full unique signature (e.g. `proc (File)`).
+5. Full path to the file containing the symbol.
+6. Line where the symbol is located in the file. Lines start to
+   count at **1**.
+7. Column where the symbol is located in the file. Columns start
+   to count at **0**.
+8. Docstring for the symbol if available or the empty string. To
+   differentiate the docstring from end of answer in server mode,
+   the docstring is always provided enclosed in double quotes, and
+   if the docstring spans multiple lines, all following lines of the
+   docstring will start with a blank space to align visually with
+   the starting quote.
+
+   Also, you won't find raw ``\n`` characters breaking the one
+   answer per line format. Instead you will need to parse sequences
+   in the form ``\xHH``, where *HH* is a hexadecimal value (e.g.
+   newlines generate the sequence ``\x0A``).
+
+The following sections define the expected output for each kind of
+symbol for which idetools returns valid output.
+
+
+skConst
+-------
+
+| **Third column**: module + \[n scope nesting] + const name.
+| **Fourth column**: the type of the const value.
+| **Docstring**: always the empty string.
+
+  ```nim
+  const SOME_SEQUENCE = @[1, 2]
+  --> col 2: $MODULE.SOME_SEQUENCE
+      col 3: seq[int]
+      col 7: ""
+  ```
+
+
+skEnumField
+-----------
+
+| **Third column**: module + \[n scope nesting] + enum type + enum field name.
+| **Fourth column**: enum type grouping other enum fields.
+| **Docstring**: always the empty string.
+
+  ```nim
+  Open(filename, fmWrite)
+  --> col 2: system.FileMode.fmWrite
+      col 3: FileMode
+      col 7: ""
+  ```
+
+
+skForVar
+--------
+
+| **Third column**: module + \[n scope nesting] + var name.
+| **Fourth column**: type of the var.
+| **Docstring**: always the empty string.
+
+  ```nim
+  proc looper(filename = "tests.nim") =
+    for letter in filename:
+      echo letter
+  --> col 2: $MODULE.looper.letter
+      col 3: char
+      col 7: ""
+  ```
+
+
+skIterator, skClosureIterator
+-----------------------------
+
+The fourth column will be the empty string if the iterator is being
+defined, since at that point in the file the parser hasn't processed
+the full line yet. The signature will be returned complete in
+posterior instances of the iterator.
+
+| **Third column**: module + \[n scope nesting] + iterator name.
+| **Fourth column**: signature of the iterator including return type.
+| **Docstring**: docstring if available.
+
+  ```nim
+  let
+    text = "some text"
+    letters = toSeq(runes(text))
+  --> col 2: unicode.runes
+      col 3: iterator (string): Rune
+      col 7: "iterates over any unicode character of the string `s`."
+  ```
+
+
+skLabel
+-------
+
+| **Third column**: module + \[n scope nesting] + name.
+| **Fourth column**: always the empty string.
+| **Docstring**: always the empty string.
+
+  ```nim
+  proc test(text: string) =
+    var found = -1
+    block loops:
+  --> col 2: $MODULE.test.loops
+      col 3: ""
+      col 7: ""
+  ```
+
+
+skLet
+-----
+
+| **Third column**: module + \[n scope nesting] + let name.
+| **Fourth column**: the type of the let variable.
+| **Docstring**: always the empty string.
+
+  ```nim
+  let
+    text = "some text"
+  --> col 2: $MODULE.text
+      col 3: string
+      col 7: ""
+  ```
+
+
+skMacro
+-------
+
+The fourth column will be the empty string if the macro is being
+defined, since at that point in the file the parser hasn't processed
+the full line yet. The signature will be returned complete in
+posterior instances of the macro.
+
+| **Third column**: module + \[n scope nesting] + macro name.
+| **Fourth column**: signature of the macro including return type.
+| **Docstring**: docstring if available.
+
+  ```nim
+  proc testMacro() =
+    expect(EArithmetic):
+  --> col 2: idetools_api.expect
+      col 3: proc (varargs[expr], stmt): stmt
+      col 7: ""
+  ```
+
+
+skMethod
+--------
+
+The fourth column will be the empty string if the method is being
+defined, since at that point in the file the parser hasn't processed
+the full line yet. The signature will be returned complete in
+posterior instances of the method.
+
+Methods imply [dynamic dispatch](
+tut2.html#object-oriented-programming-dynamic-dispatch) and
+idetools performs a static analysis on the code. For this reason
+idetools may not return the definition of the correct method you
+are querying because it may be impossible to know until the code
+is executed. It will try to return the method which covers the most
+possible cases (i.e. for variations of different classes in a
+hierarchy it will prefer methods using the base class).
+
+While at the language level a method is differentiated from others
+by the parameters and return value, the signature of the method
+returned by idetools returns also the pragmas for the method.
+
+Note that at the moment the word `proc` is returned for the
+signature of the found method instead of the expected `method`.
+This may change in the future.
+
+| **Third column**: module + \[n scope nesting] + method name.
+| **Fourth column**: signature of the method including return type.
+| **Docstring**: docstring if available.
+
+  ```nim
+  method eval(e: PExpr): int = quit "to override!"
+  method eval(e: PLiteral): int = e.x
+  method eval(e: PPlusExpr): int = eval(e.a) + eval(e.b)
+  echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
+  --> col 2: $MODULE.eval
+      col 3: proc (PPlusExpr): int
+      col 7: ""
+  ```
+
+
+skParam
+-------
+
+| **Third column**: module + \[n scope nesting] + param name.
+| **Fourth column**: the type of the parameter.
+| **Docstring**: always the empty string.
+
+  ```nim
+  proc reader(filename = "tests.nim") =
+    let text = readFile(filename)
+  --> col 2: $MODULE.reader.filename
+      col 3: string
+      col 7: ""
+  ```
+
+
+skProc
+------
+
+The fourth column will be the empty string if the proc is being
+defined, since at that point in the file the parser hasn't processed
+the full line yet. The signature will be returned complete in
+posterior instances of the proc.
+
+While at the language level a proc is differentiated from others
+by the parameters and return value, the signature of the proc
+returned by idetools returns also the pragmas for the proc.
+
+| **Third column**: module + \[n scope nesting] + proc name.
+| **Fourth column**: signature of the proc including return type.
+| **Docstring**: docstring if available.
+
+  ```nim
+  open(filename, fmWrite)
+  --> col 2: system.Open
+      col 3: proc (var File, string, FileMode, int): bool
+      col 7:
+  "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."
+  ```
+
+
+skResult
+--------
+
+| **Third column**: module + \[n scope nesting] + result.
+| **Fourth column**: the type of the result.
+| **Docstring**: always the empty string.
+
+  ```nim
+  proc getRandomValue() : int =
+    return 4
+  --> col 2: $MODULE.getRandomValue.result
+      col 3: int
+      col 7: ""
+  ```
+
+
+skTemplate
+----------
+
+The fourth column will be the empty string if the template is being
+defined, since at that point in the file the parser hasn't processed
+the full line yet. The signature will be returned complete in
+posterior instances of the template.
+
+| **Third column**: module + \[n scope nesting] + template name.
+| **Fourth column**: signature of the template including return type.
+| **Docstring**: docstring if available.
+
+  `````nim
+    let
+      text = "some text"
+      letters = toSeq(runes(text))
+    --> col 2: sequtils.toSeq
+        col 3: proc (expr): expr
+        col 7:
+    "Transforms any iterator into a sequence.
+
+     Example:
+
+       ```nim
+       let
+         numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
+         odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
+           if x mod 2 == 1:
+             result = true)
+       assert odd_numbers == @[1, 3, 5, 7, 9]"
+       ```
+  `````
+
+
+skType
+------
+
+| **Third column**: module + \[n scope nesting] + type name.
+| **Fourth column**: the type.
+| **Docstring**: always the empty string.
+
+  ```nim
+  proc writeTempFile() =
+    var output: File
+  --> col 2: system.File
+      col 3: File
+      col 7: ""
+  ```
+
+
+skVar
+-----
+
+| **Third column**: module + \[n scope nesting] + var name.
+| **Fourth column**: the type of the var.
+| **Docstring**: always the empty string.
+
+  ```nim
+  proc writeTempFile() =
+    var output: File
+    output.open("/tmp/somefile", fmWrite)
+    output.write("test")
+  --> col 2: $MODULE.writeTempFile.output
+      col 3: File
+      col 7: ""
+  ```
+
+
+Test suite
+==========
+
+To verify that idetools is working properly there are files in the
+`tests/caas/` directory which provide unit testing. If you find
+odd idetools behaviour and are able to reproduce it, you are welcome
+to report it as a bug and add a test to the suite to avoid future
+regressions.
+
+
+Running the test suite
+----------------------
+
+At the moment idetools support is still in development so the test
+suite is not integrated with the main test suite and you have to
+run it manually. First you have to compile the tester:
+
+  ```cmd
+  $ cd my/nim/checkout/tests
+  $ nim c testament/caasdriver.nim
+  ```
+
+Running the `caasdriver` without parameters will attempt to process
+all the test cases in all three operation modes. If a test succeeds
+nothing will be printed and the process will exit with zero. If any
+test fails, the specific line of the test preceding the failure
+and the failure itself will be dumped to stdout, along with a final
+indicator of the success state and operation mode. You can pass the
+parameter `verbose` to force all output even on successful tests.
+
+The normal operation mode is called `ProcRun` and it involves
+starting a process for each command or query, similar to running
+manually the Nim compiler from the commandline. The `CaasRun`
+mode starts a server process to answer all queries. The `SymbolProcRun`
+mode is used by compiler developers.  This means that running all
+tests involves processing all `*.txt` files three times, which
+can be quite time consuming.
+
+If you don't want to run all the test case files you can pass any
+substring as a parameter to `caasdriver`. Only files matching the
+passed substring will be run. The filtering doesn't use any globbing
+metacharacters, it's a plain match. For example, to run only
+`*-compile*.txt` tests in verbose mode:
+
+  ```cmd
+  ./caasdriver verbose -compile
+  ```
+
+
+Test case file format
+---------------------
+
+All the `tests/caas/*.txt` files encode a session with the compiler:
+
+* The first line indicates the main project file.
+
+* Lines starting with `>` indicate a command to be sent to the
+  compiler and the lines following a command include checks for
+  expected or forbidden output (`!` for forbidden).
+
+* If a line starts with `#` it will be ignored completely, so you
+  can use that for comments.
+
+* Since some cases are specific to either `ProcRun` or `CaasRun`
+  modes, you can prefix a line with the mode and the line will be
+  processed only in that mode.
+
+* The rest of the line is treated as a [regular expression](re.html),
+  so be careful escaping metacharacters like parenthesis.
+
+Before the line is processed as a regular expression, some basic
+variables are searched for and replaced in the tests. The variables
+which will be replaced are:
+
+* **$TESTNIM**: filename specified in the first line of the script.
+* **$MODULE**: like $TESTNIM but without extension, useful for
+  expected output.
+
+When adding a test case to the suite it is a good idea to write a
+few comments about what the test is meant to verify.
diff --git a/doc/idetools.rst b/doc/idetools.rst
deleted file mode 100644
index 2ffe46d4b..000000000
--- a/doc/idetools.rst
+++ /dev/null
@@ -1,588 +0,0 @@
-================================
-  Nim IDE Integration Guide
-================================
-
-:Author: Britney Spears
-:Version: |nimversion|
-
-.. contents::
-
-
-.. raw:: html
-  <blockquote><p>
-  "yes, I'm the creator" -- Araq, 2013-07-26 19:28:32.
-  </p></blockquote>
-
-Nim differs from many other compilers in that it is really fast,
-and being so fast makes it suited to provide external queries for
-text editors about the source code being written. Through the
-``idetools`` command of `the compiler <nimc.html>`_, any IDE
-can query a ``.nim`` source file and obtain useful information like
-definition of symbols or suggestions for completion.
-
-This document will guide you through the available options. If you
-want to look at practical examples of idetools support you can look
-at the test files found in the `Test suite`_ or `various editor
-integrations <https://github.com/Araq/Nim/wiki/Editor-Support>`_
-already available.
-
-
-Idetools invocation
-===================
-
-Specifying the location of the query
-------------------------------------
-
-All of the available idetools commands require you to specify a
-query location through the ``--track`` or ``--trackDirty`` switches.
-The general idetools invocations are::
-
-    nim idetools --track:FILE,LINE,COL <switches> proj.nim
-
-Or::
-
-    nim idetools --trackDirty:DIRTY_FILE,FILE,LINE,COL <switches> proj.nim
-
-``proj.nim``
-    This is the main *project* filename. Most of the time you will
-    pass in the same as **FILE**, but for bigger projects this is
-    the file which is used as main entry point for the program, the
-    one which users compile to generate a final binary.
-
-``<switches>``
-    This would be any of the other idetools available options, like
-    ``--def`` or ``--suggest`` explained in the following sections.
-
-``COL``
-    An integer with the column you are going to query. For the
-    compiler columns start at zero, so the first column will be
-    **0** and the last in an 80 column terminal will be **79**.
-
-``LINE``
-    An integer with the line you are going to query. For the compiler
-    lines start at **1**.
-
-``FILE``
-    The file you want to perform the query on. Usually you will
-    pass in the same value as **proj.nim**.
-
-``DIRTY_FILE``
-    The **FILE** paramater is enough for static analysis, but IDEs
-    tend to have *unsaved buffers* where the user may still be in
-    the middle of typing a line. In such situations the IDE can
-    save the current contents to a temporary file and then use the
-    ``--trackDirty`` switch.
-
-    Dirty files are likely to contain errors and they are usually
-    compiled partially only to the point needed to service the
-    idetool request. The compiler discriminates them to ensure that
-    **a)** they won't be cached and **b)** they won't invalidate
-    the cached contents of the original module.
-
-    The other reason is that the dirty file can appear anywhere on
-    disk (e.g. in tmpfs), but it must be treated as having a path
-    matching the original module when it comes to usage of relative
-    paths, etc. Queries, however, will refer to the dirty module
-    name in their answers instead of the normal filename.
-
-
-Definitions
------------
-
-The ``--def`` idetools switch performs a query about the definition
-of a specific symbol. If available, idetools will answer with the
-type, source file, line/column information and other accessory data
-if available like a docstring. With this information an IDE can
-provide the typical *Jump to definition* where a user puts the
-cursor on a symbol or uses the mouse to select it and is redirected
-to the place where the symbol is located.
-
-Since Nim is implemented in Nim, one of the nice things of
-this feature is that any user with an IDE supporting it can quickly
-jump around the standard library implementation and see exactly
-what a proc does, learning about the language and seeing real life
-examples of how to write/implement specific features.
-
-Idetools will always answer with a single definition or none if it
-can't find any valid symbol matching the position of the query.
-
-
-Suggestions
------------
-
-The ``--suggest`` idetools switch performs a query about possible
-completion symbols at some point in the file. IDEs can easily provide
-an autocompletion feature where the IDE scans the current file (and
-related ones, if it knows about the language being edited and follows
-includes/imports) and when the user starts typing something a
-completion box with different options appears.
-
-However such features are not context sensitive and work simply on
-string matching, which can be problematic in Nim especially due
-to the case insensitiveness of the language (plus underscores as
-separators!).
-
-The typical usage scenario for this option is to call it after the
-user has typed the dot character for `the object oriented call
-syntax <tut2.html#method-call-syntax>`_. Idetools will try to return
-the suggestions sorted first by scope (from innermost to outermost)
-and then by item name.
-
-
-Invocation context
-------------------
-
-The ``--context`` idetools switch is very similar to the suggestions
-switch, but instead of being used after the user has typed a dot
-character, this one is meant to be used after the user has typed
-an opening brace to start typing parameters.
-
-
-Symbol usages
--------------
-
-The ``--usages`` idetools switch lists all usages of the symbol at
-a position. IDEs can use this to find all the places in the file
-where the symbol is used and offer the user to rename it in all
-places at the same time. Again, a pure string based search and
-replace may catch symbols out of the scope of a function/loop.
-
-For this kind of query the IDE will most likely ignore all the
-type/signature info provided by idetools and concentrate on the
-filename, line and column position of the multiple returned answers.
-
-
-Expression evaluation
----------------------
-
-This feature is still under development. In the future it will allow
-an IDE to evaluate an expression in the context of the currently
-running/debugged user project.
-
-
-Compiler as a service (CAAS)
-============================
-
-The occasional use of idetools is acceptable for things like
-definitions, where the user puts the cursor on a symbol or double
-clicks it and after a second or two the IDE displays where that
-symbol is defined. Such latencies would be terrible for features
-like symbol suggestion, plus why wait at all if we can avoid it?
-
-The idetools command can be run as a compiler service (CAAS),
-where you first launch the compiler and it will stay online as a
-server, accepting queries in a telnet like fashion. The advantage
-of staying on is that for many queries the compiler can cache the
-results of the compilation, and subsequent queries should be fast
-in the millisecond range, thus being responsive enough for IDEs.
-
-If you want to start the server using stdin/stdout as communication
-you need to type::
-
-    nim serve --server.type:stdin proj.nim
-
-If you want to start the server using tcp and a port, you need to type::
-
-    nim serve --server.type:tcp --server.port:6000 \
-      --server.address:hostname proj.nim
-
-In both cases the server will start up and await further commands.
-The syntax of the commands you can now send to the server is
-practically the same as running the nim compiler on the commandline,
-you only need to remove the name of the compiler since you are
-already talking to it. The server will answer with as many lines
-of text it thinks necessary plus an empty line to indicate the end
-of the answer.
-
-You can find examples of client/server communication in the idetools
-tests found in the `Test suite`_.
-
-
-Parsing idetools output
-=======================
-
-Idetools outputs is always returned on single lines separated by
-tab characters (``\t``). The values of each column are:
-
-1. Three characters indicating the type of returned answer (e.g.
-   def for definition, ``sug`` for suggestion, etc).
-2. Type of the symbol. This can be ``skProc``, ``skLet``, and just
-   about any of the enums defined in the module ``compiler/ast.nim``.
-3. Full qualitifed path of the symbol. If you are querying a symbol
-   defined in the ``proj.nim`` file, this would have the form
-   ``proj.symbolName``.
-4. Type/signature. For variables and enums this will contain the
-   type of the symbol, for procs, methods and templates this will
-   contain the full unique signature (e.g. ``proc (File)``).
-5. Full path to the file containing the symbol.
-6. Line where the symbol is located in the file. Lines start to
-   count at **1**.
-7. Column where the symbol is located in the file. Columns start
-   to count at **0**.
-8. Docstring for the symbol if available or the empty string. To
-   differentiate the docstring from end of answer in server mode,
-   the docstring is always provided enclosed in double quotes, and
-   if the docstring spans multiple lines, all following lines of the
-   docstring will start with a blank space to align visually with
-   the starting quote.
-
-   Also, you won't find raw ``\n`` characters breaking the one
-   answer per line format. Instead you will need to parse sequences
-   in the form ``\xHH``, where *HH* is a hexadecimal value (e.g.
-   newlines generate the sequence ``\x0A``).
-
-The following sections define the expected output for each kind of
-symbol for which idetools returns valid output.
-
-
-skConst
--------
-
-| **Third column**: module + [n scope nesting] + const name.
-| **Fourth column**: the type of the const value.
-| **Docstring**: always the empty string.
-
-.. code-block:: nim
-    const SOME_SEQUENCE = @[1, 2]
-    --> col 2: $MODULE.SOME_SEQUENCE
-        col 3: seq[int]
-        col 7: ""
-
-
-skEnumField
------------
-
-| **Third column**: module + [n scope nesting] + enum type + enum field name.
-| **Fourth column**: enum type grouping other enum fields.
-| **Docstring**: always the empty string.
-
-.. code-block:: nim
-    Open(filename, fmWrite)
-    --> col 2: system.FileMode.fmWrite
-        col 3: FileMode
-        col 7: ""
-
-
-skForVar
---------
-
-| **Third column**: module + [n scope nesting] + var name.
-| **Fourth column**: type of the var.
-| **Docstring**: always the empty string.
-
-.. code-block:: nim
-    proc looper(filename = "tests.nim") =
-      for letter in filename:
-        echo letter
-    --> col 2: $MODULE.looper.letter
-        col 3: char
-        col 7: ""
-
-
-skIterator, skClosureIterator
------------------------------
-
-The fourth column will be the empty string if the iterator is being
-defined, since at that point in the file the parser hasn't processed
-the full line yet. The signature will be returned complete in
-posterior instances of the iterator.
-
-| **Third column**: module + [n scope nesting] + iterator name.
-| **Fourth column**: signature of the iterator including return type.
-| **Docstring**: docstring if available.
-
-.. code-block:: nim
-    let
-      text = "some text"
-      letters = toSeq(runes(text))
-    --> col 2: unicode.runes
-        col 3: iterator (string): Rune
-        col 7: "iterates over any unicode character of the string `s`."
-
-
-skLabel
--------
-
-| **Third column**: module + [n scope nesting] + name.
-| **Fourth column**: always the empty string.
-| **Docstring**: always the empty string.
-
-.. code-block:: nim
-    proc test(text: string) =
-      var found = -1
-      block loops:
-    --> col 2: $MODULE.test.loops
-        col 3: ""
-        col 7: ""
-
-
-skLet
------
-
-| **Third column**: module + [n scope nesting] + let name.
-| **Fourth column**: the type of the let variable.
-| **Docstring**: always the empty string.
-
-.. code-block:: nim
-    let
-      text = "some text"
-    --> col 2: $MODULE.text
-        col 3: TaintedString
-        col 7: ""
-
-
-skMacro
--------
-
-The fourth column will be the empty string if the macro is being
-defined, since at that point in the file the parser hasn't processed
-the full line yet. The signature will be returned complete in
-posterior instances of the macro.
-
-| **Third column**: module + [n scope nesting] + macro name.
-| **Fourth column**: signature of the macro including return type.
-| **Docstring**: docstring if available.
-
-.. code-block:: nim
-    proc testMacro() =
-      expect(EArithmetic):
-    --> col 2: idetools_api.expect
-        col 3: proc (varargs[expr], stmt): stmt
-        col 7: ""
-
-
-skMethod
---------
-
-The fourth column will be the empty string if the method is being
-defined, since at that point in the file the parser hasn't processed
-the full line yet. The signature will be returned complete in
-posterior instances of the method.
-
-Methods imply `dynamic dispatch <tut2.html#dynamic-dispatch>`_ and
-idetools performs a static analysis on the code. For this reason
-idetools may not return the definition of the correct method you
-are querying because it may be impossible to know until the code
-is executed. It will try to return the method which covers the most
-possible cases (i.e. for variations of different classes in a
-hierarchy it will prefer methods using the base class).
-
-While at the language level a method is differentiated from others
-by the parameters and return value, the signature of the method
-returned by idetools returns also the pragmas for the method.
-
-Note that at the moment the word ``proc`` is returned for the
-signature of the found method instead of the expected ``method``.
-This may change in the future.
-
-| **Third column**: module + [n scope nesting] + method name.
-| **Fourth column**: signature of the method including return type.
-| **Docstring**: docstring if available.
-
-.. code-block:: nim
-    method eval(e: PExpr): int = quit "to override!"
-    method eval(e: PLiteral): int = e.x
-    method eval(e: PPlusExpr): int = eval(e.a) + eval(e.b)
-    echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
-    --> col 2: $MODULE.eval
-        col 3: proc (PPlusExpr): int
-        col 7: ""
-
-
-skParam
--------
-
-| **Third column**: module + [n scope nesting] + param name.
-| **Fourth column**: the type of the parameter.
-| **Docstring**: always the empty string.
-
-.. code-block:: nim
-    proc reader(filename = "tests.nim") =
-      let text = readFile(filename)
-    --> col 2: $MODULE.reader.filename
-        col 3: string
-        col 7: ""
-
-
-skProc
-------
-
-The fourth column will be the empty string if the proc is being
-defined, since at that point in the file the parser hasn't processed
-the full line yet. The signature will be returned complete in
-posterior instances of the proc.
-
-While at the language level a proc is differentiated from others
-by the parameters and return value, the signature of the proc
-returned by idetools returns also the pragmas for the proc.
-
-| **Third column**: module + [n scope nesting] + proc name.
-| **Fourth column**: signature of the proc including return type.
-| **Docstring**: docstring if available.
-
-.. code-block:: nim
-    open(filename, fmWrite)
-    --> col 2: system.Open
-        col 3: proc (var File, string, FileMode, int): bool
-        col 7:
-    "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."
-
-
-skResult
---------
-
-| **Third column**: module + [n scope nesting] + result.
-| **Fourth column**: the type of the result.
-| **Docstring**: always the empty string.
-
-.. code-block:: nim
-    proc getRandomValue() : int =
-      return 4
-    --> col 2: $MODULE.getRandomValue.result
-        col 3: int
-        col 7: ""
-
-
-skTemplate
-----------
-
-The fourth column will be the empty string if the template is being
-defined, since at that point in the file the parser hasn't processed
-the full line yet. The signature will be returned complete in
-posterior instances of the template.
-
-| **Third column**: module + [n scope nesting] + template name.
-| **Fourth column**: signature of the template including return type.
-| **Docstring**: docstring if available.
-
-.. code-block:: nim
-    let
-      text = "some text"
-      letters = toSeq(runes(text))
-    --> col 2: sequtils.toSeq
-        col 3: proc (expr): expr
-        col 7:
-    "Transforms any iterator into a sequence.
-
-     Example:
-
-     .. code-block:: nim
-       let
-         numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
-         odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
-           if x mod 2 == 1:
-             result = true)
-       assert odd_numbers == @[1, 3, 5, 7, 9]"
-
-
-skType
-------
-
-| **Third column**: module + [n scope nesting] + type name.
-| **Fourth column**: the type.
-| **Docstring**: always the empty string.
-
-.. code-block:: nim
-    proc writeTempFile() =
-      var output: File
-    --> col 2: system.File
-        col 3: File
-        col 7: ""
-
-
-skVar
------
-
-| **Third column**: module + [n scope nesting] + var name.
-| **Fourth column**: the type of the var.
-| **Docstring**: always the empty string.
-
-.. code-block:: nim
-    proc writeTempFile() =
-      var output: File
-      output.open("/tmp/somefile", fmWrite)
-      output.write("test")
-    --> col 2: $MODULE.writeTempFile.output
-        col 3: File
-        col 7: ""
-
-
-Test suite
-==========
-
-To verify that idetools is working properly there are files in the
-``tests/caas/`` directory which provide unit testing. If you find
-odd idetools behaviour and are able to reproduce it, you are welcome
-to report it as a bug and add a test to the suite to avoid future
-regressions.
-
-
-Running the test suite
-----------------------
-
-At the moment idetools support is still in development so the test
-suite is not integrated with the main test suite and you have to
-run it manually. First you have to compile the tester::
-
-	$ cd my/nim/checkout/tests
-	$ nim c testament/caasdriver.nim
-
-Running the ``caasdriver`` without parameters will attempt to process
-all the test cases in all three operation modes. If a test succeeds
-nothing will be printed and the process will exit with zero. If any
-test fails, the specific line of the test preceding the failure
-and the failure itself will be dumped to stdout, along with a final
-indicator of the success state and operation mode. You can pass the
-parameter ``verbose`` to force all output even on successful tests.
-
-The normal operation mode is called ``ProcRun`` and it involves
-starting a process for each command or query, similar to running
-manually the Nim compiler from the commandline. The ``CaasRun``
-mode starts a server process to answer all queries. The ``SymbolProcRun``
-mode is used by compiler developers.  This means that running all
-tests involves processing all ``*.txt`` files three times, which
-can be quite time consuming.
-
-If you don't want to run all the test case files you can pass any
-substring as a parameter to ``caasdriver``. Only files matching the
-passed substring will be run. The filtering doesn't use any globbing
-metacharacters, it's a plain match. For example, to run only
-``*-compile*.txt`` tests in verbose mode::
-
-	./caasdriver verbose -compile
-
-
-Test case file format
----------------------
-
-All the ``tests/caas/*.txt`` files encode a session with the compiler:
-
-* The first line indicates the main project file.
-
-* Lines starting with ``>`` indicate a command to be sent to the
-  compiler and the lines following a command include checks for
-  expected or forbidden output (``!`` for forbidden).
-
-* If a line starts with ``#`` it will be ignored completely, so you
-  can use that for comments.
-
-* Since some cases are specific to either ``ProcRun`` or ``CaasRun``
-  modes, you can prefix a line with the mode and the line will be
-  processed only in that mode.
-
-* The rest of the line is treated as a `regular expression <re.html>`_,
-  so be careful escaping metacharacters like parenthesis.
-
-Before the line is processed as a regular expression, some basic
-variables are searched for and replaced in the tests. The variables
-which will be replaced are:
-
-* **$TESTNIM**: filename specified in the first line of the script.
-* **$MODULE**: like $TESTNIM but without extension, useful for
-  expected output.
-
-When adding a test case to the suite it is a good idea to write a
-few comments about what the test is meant to verify.
diff --git a/doc/intern.md b/doc/intern.md
new file mode 100644
index 000000000..6b16bc71f
--- /dev/null
+++ b/doc/intern.md
@@ -0,0 +1,679 @@
+=========================================
+    Internals of the Nim Compiler
+=========================================
+
+
+:Author: Andreas Rumpf
+:Version: |nimversion|
+
+.. default-role:: code
+.. include:: rstcommon.rst
+.. contents::
+
+> "Abstraction is layering ignorance on top of reality." -- Richard Gabriel
+
+
+Directory structure
+===================
+
+The Nim project's directory structure is:
+
+============   ===================================================
+Path           Purpose
+============   ===================================================
+`bin`          generated binary files
+`build`        generated C code for the installation
+`compiler`     the Nim compiler itself; note that this
+               code has been translated from a bootstrapping
+               version written in Pascal, so the code is **not**
+               a poster child of good Nim code
+`config`       configuration files for Nim
+`dist`         additional packages for the distribution
+`doc`          the documentation; it is a bunch of
+               reStructuredText files
+`lib`          the Nim library
+============   ===================================================
+
+
+Bootstrapping the compiler
+==========================
+
+**Note**: Add ``.`` to your PATH so that `koch`:cmd: can be used without the ``./``.
+
+Compiling the compiler is a simple matter of running:
+
+  ```cmd
+  nim c koch.nim
+  koch boot -d:release
+  ```
+
+For a debug version use:
+
+  ```cmd
+  nim c koch.nim
+  koch boot
+  ```
+
+
+And for a debug version compatible with GDB:
+
+  ```cmd
+  nim c koch.nim
+  koch boot --debuginfo --linedir:on
+  ```
+
+The `koch`:cmd: program is Nim's maintenance script. It is a replacement for
+make and shell scripting with the advantage that it is much more portable.
+More information about its options can be found in the [koch](koch.html)
+documentation.
+
+
+Reproducible builds
+-------------------
+
+Set the compilation timestamp with the `SOURCE_DATE_EPOCH` environment variable.
+
+  ```cmd
+  export SOURCE_DATE_EPOCH=$(git log -n 1 --format=%at)
+  koch boot # or `./build_all.sh`
+  ```
+
+
+Debugging the compiler
+======================
+
+
+Bisecting for regressions
+-------------------------
+
+There are often times when there is a bug that is caused by a regression in the
+compiler or stdlib. Bisecting the Nim repo commits is a useful tool to identify
+what commit introduced the regression.
+
+Even if it's not known whether a bug is caused by a regression, bisection can reduce
+debugging time by ruling it out. If the bug is found to be a regression, then you
+focus on the changes introduced by that one specific commit.
+
+`koch temp`:cmd: returns 125 as the exit code in case the compiler
+compilation fails. This exit code tells `git bisect`:cmd: to skip the
+current commit:
+
+  ```cmd
+  git bisect start bad-commit good-commit
+  git bisect run ./koch temp -r c test-source.nim
+  ```
+
+You can also bisect using custom options to build the compiler, for example if
+you don't need a debug version of the compiler (which runs slower), you can replace
+`./koch temp`:cmd: by explicit compilation command, see [Bootstrapping the compiler].
+
+See also:
+
+- Crossplatform C/Cpp/Valgrind/JS Bisect in GitHub: https://github.com/juancarlospaco/nimrun-action#examples
+
+
+Building an instrumented compiler
+---------------------------------
+
+Considering that a useful method of debugging the compiler is inserting debug
+logging, or changing code and then observing the outcome of a testcase, it is
+fastest to build a compiler that is instrumented for debugging from an
+existing release build. `koch temp`:cmd: provides a convenient method of doing
+just that.
+
+By default, running `koch temp`:cmd: will build a lean version of the compiler
+with `-d:debug`:option: enabled. The compiler is written to `bin/nim_temp` by
+default. A lean version of the compiler lacks JS and documentation generation.
+
+`bin/nim_temp` can be directly used to run testcases, or used with testament
+with `testament --nim:bin/nim_temp r tests/category/tsometest`:cmd:.
+
+`koch temp`:cmd: will build the temporary compiler with the `-d:debug`:option:
+enabled. Here are compiler options that are of interest when debugging:
+
+* `-d:debug`:option:\: enables `assert` statements and stacktraces and all
+  runtime checks
+* `--opt:speed`:option:\: build with optimizations enabled
+* `--debugger:native`:option:\: enables `--debuginfo --lineDir:on`:option: for using
+  a native debugger like GDB, LLDB or CDB
+* `-d:nimDebug`:option: cause calls to `quit` to raise an assertion exception
+* `-d:nimDebugUtils`:option:\: enables various debugging utilities;
+  see `compiler/debugutils`
+* `-d:stacktraceMsgs -d:nimCompilerStacktraceHints`:option:\: adds some additional
+  stacktrace hints; see https://github.com/nim-lang/Nim/pull/13351
+* `-u:leanCompiler`:option:\: enable JS and doc generation
+
+Another method to build and run the compiler is directly through `koch`:cmd:\:
+
+  ```cmd
+  koch temp [options] c test.nim
+
+  # (will build with js support)
+  koch temp [options] js test.nim
+
+  # (will build with doc support)
+  koch temp [options] doc test.nim
+  ```
+
+Debug logging
+-------------
+
+"Printf debugging" is still the most appropriate way to debug many problems
+arising in compiler development. The typical usage of breakpoints to debug
+the code is often less practical, because almost all code paths in the
+compiler will be executed hundreds of times before a particular section of the
+tested program is reached where the newly developed code must be activated.
+
+To work around this problem, you'll typically introduce an if statement in the
+compiler code detecting more precisely the conditions where the tested feature
+is being used. One very common way to achieve this is to use the `mdbg` condition,
+which will be true only in contexts, processing expressions and statements from
+the currently compiled main module:
+
+  ```nim
+  # inside some compiler module
+  if mdbg:
+    debug someAstNode
+  ```
+
+Using the `isCompilerDebug`:nim: condition along with inserting some statements
+into the testcase provides more granular logging:
+
+  ```nim
+  # compilermodule.nim
+  if isCompilerDebug():
+    debug someAstNode
+
+  # testcase.nim
+  proc main =
+    {.define(nimCompilerDebug).}
+    let a = 2.5 * 3
+    {.undef(nimCompilerDebug).}
+  ```
+
+Logging can also be scoped to a specific filename as well. This will of course
+match against every module with that name.
+
+  ```nim
+  if `??`(conf, n.info, "module.nim"):
+    debug(n)
+  ```
+
+The above examples also makes use of the `debug`:nim: proc, which is able to
+print a human-readable form of an arbitrary AST tree. Other common ways to print
+information about the internal compiler types include:
+
+  ```nim
+  # pretty print PNode
+
+  # pretty prints the Nim ast
+  echo renderTree(someNode)
+
+  # pretty prints the Nim ast, but annotates symbol IDs
+  echo renderTree(someNode, {renderIds})
+
+  # pretty print ast as JSON
+  debug(someNode)
+
+  # print as YAML
+  echo treeToYaml(config, someNode)
+
+
+  # pretty print PType
+
+  # print type name
+  echo typeToString(someType)
+
+  # pretty print as JSON
+  debug(someType)
+
+  # print as YAML
+  echo typeToYaml(config, someType)
+
+
+  # pretty print PSym
+
+  # print the symbol's name
+  echo symbol.name.s
+
+  # pretty print as JSON
+  debug(symbol)
+
+  # print as YAML
+  echo symToYaml(config, symbol)
+
+
+  # pretty print TLineInfo
+  lineInfoToStr(lineInfo)
+
+
+  # print the structure of any type
+  repr(someVar)
+  ```
+
+Here are some other helpful utilities:
+
+  ```nim
+  # how did execution reach this location?
+  writeStackTrace()
+  ```
+
+These procs may not already be imported by the module you're editing.
+You can import them directly for debugging:
+
+  ```nim
+  from astalgo import debug
+  from types import typeToString
+  from renderer import renderTree
+  from msgs import `??`
+  ```
+
+Native debugging
+----------------
+
+Stepping through the compiler with a native debugger is a very powerful tool to
+both learn and debug it. However, there is still the need to constrain when
+breakpoints are triggered. The same methods as in [Debug logging] can be applied
+here when combined with calls to the debug helpers `enteringDebugSection()`:nim:
+and `exitingDebugSection()`:nim:.
+
+#. Compile the temp compiler with `--debugger:native -d:nimDebugUtils`:option:
+#. Set your desired breakpoints or watchpoints.
+#. Configure your debugger:
+   * GDB: execute `source tools/compiler.gdb` at startup
+   * LLDB execute `command source tools/compiler.lldb` at startup
+#. Use one of the scoping helpers like so:
+
+  ```nim
+  if isCompilerDebug():
+    enteringDebugSection()
+  else:
+    exitingDebugSection()
+  ```
+
+A caveat of this method is that all breakpoints and watchpoints are enabled or
+disabled. Also, due to a bug, only breakpoints can be constrained for LLDB.
+
+The compiler's architecture
+===========================
+
+Nim uses the classic compiler architecture: A lexer/scanner feeds tokens to a
+parser. The parser builds a syntax tree that is used by the code generators.
+This syntax tree is the interface between the parser and the code generator.
+It is essential to understand most of the compiler's code.
+
+Semantic analysis is separated from parsing.
+
+.. include:: filelist.txt
+
+
+The syntax tree
+---------------
+The syntax tree consists of nodes which may have an arbitrary number of
+children. Types and symbols are represented by other nodes, because they
+may contain cycles. The AST changes its shape after semantic checking. This
+is needed to make life easier for the code generators. See the "ast" module
+for the type definitions. The [macros](macros.html) module contains many
+examples how the AST represents each syntactic structure.
+
+
+Runtimes
+========
+
+Nim has two different runtimes, the "old runtime" and the "new runtime". The old
+runtime supports the old GCs (markAndSweep, refc, Boehm), the new runtime supports
+ARC/ORC. The new runtime is active `when defined(nimV2)`.
+
+
+Coding Guidelines
+=================
+
+* We follow Nim's official style guide, see [NEP1](nep1.html).
+* Max line length is 100 characters.
+* Provide spaces around binary operators if that enhances readability.
+* Use a space after a colon, but not before it.
+* (deprecated) Start types with a capital `T`, unless they are
+  pointers/references which start with `P`.
+* Prefer `import package`:nim: over `from package import symbol`:nim:.
+
+See also the [API naming design](apis.html) document.
+
+
+Porting to new platforms
+========================
+
+Porting Nim to a new architecture is pretty easy, since C is the most
+portable programming language (within certain limits) and Nim generates
+C code, porting the code generator is not necessary.
+
+POSIX-compliant systems on conventional hardware are usually pretty easy to
+port: Add the platform to `platform` (if it is not already listed there),
+check that the OS, System modules work and recompile Nim.
+
+The only case where things aren't as easy is when old runtime's garbage
+collectors need some assembler tweaking to work. The default
+implementation uses C's `setjmp`:c: function to store all registers
+on the hardware stack. It may be necessary that the new platform needs to
+replace this generic code by some assembler code.
+
+Files that may need changed for your platform include:
+
+* `compiler/platform.nim`
+  Add os/cpu properties.
+* `lib/system.nim`
+  Add os/cpu to the documentation for `system.hostOS` and `system.hostCPU`.
+* `compiler/options.nim`
+  Add special os/cpu property checks in `isDefined`.
+* `compiler/installer.ini`
+  Add os/cpu to `Project.Platforms` field.
+* `lib/system/platforms.nim`
+  Add os/cpu.
+* `std/private/osseps.nim`
+  Add os specializations.
+* `lib/pure/distros.nim`
+  Add os, package handler.
+* `tools/niminst/makefile.nimf`
+  Add os/cpu compiler/linker flags.
+* `tools/niminst/buildsh.nimf`
+  Add os/cpu compiler/linker flags.
+
+If the `--os` or `--cpu` options aren't passed to the compiler, then Nim will
+determine the current host os, cpu and endianness from `system.cpuEndian`,
+`system.hostOS` and `system.hostCPU`. Those values are derived from
+`compiler/platform.nim`.
+
+In order for the new platform to be bootstrapped from the `csources`, it must:
+
+* have `compiler/platform.nim` updated
+* have `compiler/installer.ini` updated
+* have `tools/niminst/buildsh.nimf` updated
+* have `tools/niminst/makefile.nimf` updated
+* be backported to the Nim version used by the `csources`
+* the new `csources` must be pushed
+* the new `csources` revision must be updated in `config/build_config.txt`
+
+
+Runtime type information
+========================
+
+**Note**: This section describes the "old runtime".
+
+*Runtime type information* (RTTI) is needed for several aspects of the Nim
+programming language:
+
+Garbage collection
+: The old GCs use the RTTI for traversing arbitrary Nim types, but usually
+  only the `marker` field which contains a proc that does the traversal.
+
+Complex assignments
+: Sequences and strings are implemented as
+  pointers to resizable buffers, but Nim requires copying for
+  assignments. Apart from RTTI the compiler also generates copy procedures
+  as a specialization.
+
+We already know the type information as a graph in the compiler.
+Thus, we need to serialize this graph as RTTI for C code generation.
+Look at the file ``lib/system/hti.nim`` for more information.
+
+
+Magics and compilerProcs
+========================
+
+The `system` module contains the part of the RTL which needs support by
+compiler magic. The C code generator generates the C code for it, just like any other
+module. However, calls to some procedures like `addInt` are inserted by
+the generator. Therefore, there is a table (`compilerprocs`)
+with all symbols that are marked as `compilerproc`. `compilerprocs` are
+needed by the code generator. A `magic` proc is not the same as a
+`compilerproc`: A `magic` is a proc that needs compiler magic for its
+semantic checking, a `compilerproc` is a proc that is used by the code
+generator.
+
+
+Code generation for closures
+============================
+
+Code generation for closures is implemented by `lambda lifting`:idx:.
+
+
+Design
+------
+
+A `closure` proc var can call ordinary procs of the default Nim calling
+convention. But not the other way round! A closure is implemented as a
+`tuple[prc, env]`. `env` can be nil implying a call without a closure.
+This means that a call through a closure generates an `if` but the
+interoperability is worth the cost of the `if`. Thunk generation would be
+possible too, but it's slightly more effort to implement.
+
+Tests with GCC on Amd64 showed that it's really beneficial if the
+'environment' pointer is passed as the last argument, not as the first argument.
+
+Proper thunk generation is harder because the proc that is to wrap
+could stem from a complex expression:
+
+  ```nim
+  receivesClosure(returnsDefaultCC[i])
+  ```
+
+A thunk would need to call `returnsDefaultCC[i]` somehow and that would require
+an *additional* closure generation... Ok, not really, but it requires to pass
+the function to call. So we'd end up with 2 indirect calls instead of one.
+Another much more severe problem with this solution is that it's not GC-safe
+to pass a proc pointer around via a generic `ref` type.
+
+
+Example code:
+
+  ```nim
+  proc add(x: int): proc (y: int): int {.closure.} =
+    return proc (y: int): int =
+      return x + y
+
+  var add2 = add(2)
+  echo add2(5) #OUT 7
+  ```
+
+This should produce roughly this code:
+
+  ```nim
+  type
+    Env = ref object
+      x: int # data
+
+  proc anon(y: int, c: Env): int =
+    return y + c.x
+
+  proc add(x: int): tuple[prc, data] =
+    var env: Env
+    new env
+    env.x = x
+    result = (anon, env)
+
+  var add2 = add(2)
+  let tmp = if add2.data == nil: add2.prc(5) else: add2.prc(5, add2.data)
+  echo tmp
+  ```
+
+
+Beware of nesting:
+
+  ```nim
+  proc add(x: int): proc (y: int): proc (z: int): int {.closure.} {.closure.} =
+    return lambda (y: int): proc (z: int): int {.closure.} =
+      return lambda (z: int): int =
+        return x + y + z
+
+  var add24 = add(2)(4)
+  echo add24(5) #OUT 11
+  ```
+
+This should produce roughly this code:
+
+  ```nim
+  type
+    EnvX = ref object
+      x: int # data
+
+    EnvY = ref object
+      y: int
+      ex: EnvX
+
+  proc lambdaZ(z: int, ey: EnvY): int =
+    return ey.ex.x + ey.y + z
+
+  proc lambdaY(y: int, ex: EnvX): tuple[prc, data: EnvY] =
+    var ey: EnvY
+    new ey
+    ey.y = y
+    ey.ex = ex
+    result = (lambdaZ, ey)
+
+  proc add(x: int): tuple[prc, data: EnvX] =
+    var ex: EnvX
+    ex.x = x
+    result = (lambdaY, ex)
+
+  var tmp = add(2)
+  var tmp2 = tmp.fn(4, tmp.data)
+  var add24 = tmp2.fn(4, tmp2.data)
+  echo add24(5)
+  ```
+
+
+We could get rid of nesting environments by always inlining inner anon procs.
+More useful is escape analysis and stack allocation of the environment,
+however.
+
+
+Accumulator
+-----------
+
+  ```nim
+  proc getAccumulator(start: int): proc (): int {.closure} =
+    var i = start
+    return lambda: int =
+      inc i
+      return i
+
+  proc p =
+    var delta = 7
+    proc accumulator(start: int): proc(): int =
+      var x = start-1
+      result = proc (): int =
+        x = x + delta
+        inc delta
+        return x
+
+    var a = accumulator(3)
+    var b = accumulator(4)
+    echo a() + b()
+  ```
+
+
+Internals
+---------
+
+Lambda lifting is implemented as part of the `transf` pass. The `transf`
+pass generates code to set up the environment and to pass it around. However,
+this pass does not change the types! So we have some kind of mismatch here; on
+the one hand the proc expression becomes an explicit tuple, on the other hand
+the tyProc(ccClosure) type is not changed. For C code generation it's also
+important the hidden formal param is `void*`:c: and not something more
+specialized. However, the more specialized env type needs to passed to the
+backend somehow. We deal with this by modifying `s.ast[paramPos]` to contain
+the formal hidden parameter, but not `s.typ`!
+
+
+Notes on type and AST representation
+====================================
+
+To be expanded.
+
+
+Integer literals
+----------------
+
+In Nim, there is a redundant way to specify the type of an
+integer literal. First, it should be unsurprising that every
+node has a node kind. The node of an integer literal can be any of the
+following values:
+
+    nkIntLit, nkInt8Lit, nkInt16Lit, nkInt32Lit, nkInt64Lit,
+    nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit
+
+On top of that, there is also the `typ` field for the type. The
+kind of the `typ` field can be one of the following ones, and it
+should be matching the literal kind:
+
+    tyInt, tyInt8, tyInt16, tyInt32, tyInt64, tyUInt, tyUInt8,
+    tyUInt16, tyUInt32, tyUInt64
+
+Then there is also the integer literal type. This is a specific type
+that is implicitly convertible into the requested type if the
+requested type can hold the value. For this to work, the type needs to
+know the concrete value of the literal. For example an expression
+`321` will be of type `int literal(321)`. This type is implicitly
+convertible to all integer types and ranges that contain the value
+`321`. That would be all builtin integer types except `uint8` and
+`int8` where `321` would be out of range. When this literal type is
+assigned to a new `var` or `let` variable, it's type will be resolved
+to just `int`, not `int literal(321)` unlike constants. A constant
+keeps the full `int literal(321)` type. Here is an example where that
+difference matters.
+
+
+  ```nim
+  proc foo(arg: int8) =
+    echo "def"
+
+  const tmp1 = 123
+  foo(tmp1)  # OK
+
+  let tmp2 = 123
+  foo(tmp2) # Error
+  ```
+
+In a context with multiple overloads, the integer literal kind will
+always prefer the `int` type over all other types. If none of the
+overloads is of type `int`, then there will be an error because of
+ambiguity.
+
+  ```nim
+  proc foo(arg: int) =
+    echo "abc"
+  proc foo(arg: int8) =
+    echo "def"
+  foo(123) # output: abc
+
+  proc bar(arg: int16) =
+    echo "abc"
+  proc bar(arg: int8) =
+    echo "def"
+
+  bar(123) # Error ambiguous call
+  ```
+
+In the compiler these integer literal types are represented with the
+node kind `nkIntLit`, type kind `tyInt` and the member `n` of the type
+pointing back to the integer literal node in the ast containing the
+integer value. These are the properties that hold true for integer
+literal types.
+
+    n.kind == nkIntLit
+    n.typ.kind == tyInt
+    n.typ.n == n
+
+Other literal types, such as `uint literal(123)` that would
+automatically convert to other integer types, but prefers to
+become a `uint` are not part of the Nim language.
+
+In an unchecked AST, the `typ` field is nil. The type checker will set
+the `typ` field accordingly to the node kind. Nodes of kind `nkIntLit`
+will get the integer literal type (e.g. `int literal(123)`). Nodes of
+kind `nkUIntLit` will get type `uint` (kind `tyUint`), etc.
+
+This also means that it is not possible to write a literal in an
+unchecked AST that will after sem checking just be of type `int` and
+not implicitly convertible to other integer types. This only works for
+all integer types that are not `int`.
diff --git a/doc/intern.txt b/doc/intern.txt
deleted file mode 100644
index b253cac42..000000000
--- a/doc/intern.txt
+++ /dev/null
@@ -1,669 +0,0 @@
-=========================================
-    Internals of the Nim Compiler
-=========================================
-
-
-:Author: Andreas Rumpf
-:Version: |nimversion|
-
-.. contents::
-
-  "Abstraction is layering ignorance on top of reality." -- unknown
-
-
-Directory structure
-===================
-
-The Nim project's directory structure is:
-
-============   ==============================================
-Path           Purpose
-============   ==============================================
-``bin``        generated binary files
-``build``      generated C code for the installation
-``compiler``   the Nim compiler itself; note that this
-               code has been translated from a bootstrapping
-               version written in Pascal, so the code is **not**
-               a poster child of good Nim code
-``config``     configuration files for Nim
-``dist``       additional packages for the distribution
-``doc``        the documentation; it is a bunch of
-               reStructuredText files
-``lib``        the Nim library
-``web``        website of Nim; generated by ``nimweb``
-               from the ``*.txt`` and ``*.tmpl`` files
-============   ==============================================
-
-
-Bootstrapping the compiler
-==========================
-
-Compiling the compiler is a simple matter of running::
-
-  nim c koch.nim
-  ./koch boot
-
-For a release version use::
-
-  nim c koch.nim
-  ./koch boot -d:release
-
-And for a debug version compatible with GDB::
-
-  nim c koch.nim
-  ./koch boot --debuginfo --linedir:on
-
-The ``koch`` program is Nim's maintenance script. It is a replacement for
-make and shell scripting with the advantage that it is much more portable.
-More information about its options can be found in the `koch <koch.html>`_
-documentation.
-
-
-Coding Guidelines
-=================
-
-* Use CamelCase, not underscored_identifiers.
-* Indent with two spaces.
-* Max line length is 80 characters.
-* Provide spaces around binary operators if that enhances readability.
-* Use a space after a colon, but not before it.
-* Start types with a capital ``T``, unless they are pointers/references which
-  start with ``P``.
-
-See also the `API naming design <apis.html>`_ document.
-
-
-Porting to new platforms
-========================
-
-Porting Nim to a new architecture is pretty easy, since C is the most
-portable programming language (within certain limits) and Nim generates
-C code, porting the code generator is not necessary.
-
-POSIX-compliant systems on conventional hardware are usually pretty easy to
-port: Add the platform to ``platform`` (if it is not already listed there),
-check that the OS, System modules work and recompile Nim.
-
-The only case where things aren't as easy is when the garbage
-collector needs some assembler tweaking to work. The standard
-version of the GC uses C's ``setjmp`` function to store all registers
-on the hardware stack. It may be necessary that the new platform needs to
-replace this generic code by some assembler code.
-
-
-Runtime type information
-========================
-
-*Runtime type information* (RTTI) is needed for several aspects of the Nim
-programming language:
-
-Garbage collection
-  The most important reason for RTTI. Generating
-  traversal procedures produces bigger code and is likely to be slower on
-  modern hardware as dynamic procedure binding is hard to predict.
-
-Complex assignments
-  Sequences and strings are implemented as
-  pointers to resizeable buffers, but Nim requires copying for
-  assignments. Apart from RTTI the compiler could generate copy procedures
-  for any type that needs one. However, this would make the code bigger and
-  the RTTI is likely already there for the GC.
-
-We already know the type information as a graph in the compiler.
-Thus we need to serialize this graph as RTTI for C code generation.
-Look at the file ``lib/system/hti.nim`` for more information.
-
-
-Debugging the compiler
-======================
-
-You can of course use GDB or Visual Studio to debug the
-compiler (via ``--debuginfo --lineDir:on``). However, there
-are also lots of procs that aid in debugging:
-
-
-.. code-block:: nim
-  # pretty prints the Nim AST
-  echo renderTree(someNode)
-  # outputs some JSON representation
-  debug(someNode)
-  # pretty prints some type
-  echo typeToString(someType)
-  debug(someType)
-  echo symbol.name.s
-  debug(symbol)
-  # pretty prints the Nim ast, but annotates symbol IDs:
-  echo renderTree(someNode, {renderIds})
-  if n.info ?? "temp.nim":
-    # only output when it comes from "temp.nim"
-    echo renderTree(n)
-  if n.info ?? "temp.nim":
-    # why does it process temp.nim here?
-    writeStackTrace()
-
-To create a new compiler for each run, use ``koch temp``::
-
-  ./koch temp c /tmp/test.nim
-
-``koch temp`` creates a debug build of the compiler, which is useful
-to create stacktraces for compiler debugging.
-
-``koch temp`` returns 125 as the exit code in case the compiler
-compilation fails. This exit code tells ``git bisect`` to skip the
-current commit.::
-
-  git bisect start bad-commit good-commit
-  git bisect run ./koch temp -r c test-source.nim
-
-The compiler's architecture
-===========================
-
-Nim uses the classic compiler architecture: A lexer/scanner feds tokens to a
-parser. The parser builds a syntax tree that is used by the code generator.
-This syntax tree is the interface between the parser and the code generator.
-It is essential to understand most of the compiler's code.
-
-In order to compile Nim correctly, type-checking has to be separated from
-parsing. Otherwise generics cannot work.
-
-.. include:: filelist.txt
-
-
-The syntax tree
----------------
-The syntax tree consists of nodes which may have an arbitrary number of
-children. Types and symbols are represented by other nodes, because they
-may contain cycles. The AST changes its shape after semantic checking. This
-is needed to make life easier for the code generators. See the "ast" module
-for the type definitions. The `macros <macros.html>`_ module contains many
-examples how the AST represents each syntactic structure.
-
-
-How the RTL is compiled
-=======================
-
-The ``system`` module contains the part of the RTL which needs support by
-compiler magic (and the stuff that needs to be in it because the spec
-says so). The C code generator generates the C code for it just like any other
-module. However, calls to some procedures like ``addInt`` are inserted by
-the CCG. Therefore the module ``magicsys`` contains a table (``compilerprocs``)
-with all symbols that are marked as ``compilerproc``. ``compilerprocs`` are
-needed by the code generator. A ``magic`` proc is not the same as a
-``compilerproc``: A ``magic`` is a proc that needs compiler magic for its
-semantic checking, a ``compilerproc`` is a proc that is used by the code
-generator.
-
-
-Compilation cache
-=================
-
-The implementation of the compilation cache is tricky: There are lots
-of issues to be solved for the front- and backend.
-
-
-General approach: AST replay
-----------------------------
-
-We store a module's AST of a successful semantic check in a SQLite
-database. There are plenty of features that require a sub sequence
-to be re-applied, for example:
-
-.. code-block:: nim
-  {.compile: "foo.c".} # even if the module is loaded from the DB,
-                       # "foo.c" needs to be compiled/linked.
-
-The solution is to **re-play** the module's top level statements.
-This solves the problem without having to special case the logic
-that fills the internal seqs which are affected by the pragmas.
-
-In fact, this decribes how the AST should be stored in the database,
-as a "shallow" tree. Let's assume we compile module ``m`` with the
-following contents:
-
-.. code-block:: nim
-  import strutils
-
-  var x*: int = 90
-  {.compile: "foo.c".}
-  proc p = echo "p"
-  proc q = echo "q"
-  static:
-    echo "static"
-
-Conceptually this is the AST we store for the module:
-
-.. code-block:: nim
-  import strutils
-
-  var x*
-  {.compile: "foo.c".}
-  proc p
-  proc q
-  static:
-    echo "static"
-
-The symbol's ``ast`` field is loaded lazily, on demand. This is where most
-savings come from, only the shallow outer AST is reconstructed immediately.
-
-It is also important that the replay involves the ``import`` statement so
-that the dependencies are resolved properly.
-
-
-Shared global compiletime state
--------------------------------
-
-Nim allows ``.global, compiletime`` variables that can be filled by macro
-invokations across different modules. This feature breaks modularity in a
-severe way. Plenty of different solutions have been proposed:
-
-- Restrict the types of global compiletime variables to ``Set[T]`` or
-  similar unordered, only-growable collections so that we can track
-  the module's write effects to these variables and reapply the changes
-  in a different order.
-- In every module compilation, reset the variable to its default value.
-- Provide a restrictive API that can load/save the compiletime state to
-  a file.
-
-(These solutions are not mutually exclusive.)
-
-Since we adopt the "replay the top level statements" idea, the natural
-solution to this problem is to emit pseudo top level statements that
-reflect the mutations done to the global variable. However, this is
-MUCH harder than it sounds, for example ``squeaknim`` uses this
-snippet:
-
-.. code-block:: nim
-  apicall.add(") module: '" & dllName & "'>\C" &
-              "\t^self externalCallFailed\C!\C\C")
-  stCode.add(st & "\C\t\"Generated by NimSqueak\"\C\t" & apicall)
-
-We can "replay" ``stCode.add`` only if the values of ``st``
-and ``apicall`` are known. And even then a hash table's ``add`` with its
-hashing mechanism is too hard to replay.
-
-In practice, things are worse still, consider ``someGlobal[i][j].add arg``.
-We only know the root is ``someGlobal`` but the concrete path to the data
-is unknown as is the value that is added. We could compute a "diff" between
-the global states and use that to compute a symbol patchset, but this is
-quite some work, expensive to do at runtime (it would need to run after
-every module has been compiled) and also would break for hash tables.
-
-We need an API that hides the complex aliasing problems by not relying
-on Nim's global variables. The obvious solution is to use string keys
-instead of global variables:
-
-.. code-block:: nim
-
-  proc cachePut*(key: string; value: string)
-  proc cacheGet*(key: string): string
-
-However, the values being strings/json is quite problematic: Many
-lookup tables that are built at compiletime embed *proc vars* and
-types which have no obvious string representation... Seems like
-AST diffing is still the best idea as it will not require to use
-an alien API and works with some existing Nimble packages, at least.
-
-On the other hand, in Nim's future I would like to replace the VM
-by native code. A diff algorithm wouldn't work for that.
-Instead the native code would work with an API like ``put``, ``get``:
-
-.. code-block:: nim
-
-  proc cachePut*(key: string; value: NimNode)
-  proc cacheGet*(key: string): NimNode
-
-The API should embrace the AST diffing notion: See the
-module ``macrocache`` for the final details.
-
-
-
-Methods and type converters
----------------------------
-
-In the following
-sections *global* means *shared between modules* or *property of the whole
-program*.
-
-Nim contains language features that are *global*. The best example for that
-are multi methods: Introducing a new method with the same name and some
-compatible object parameter means that the method's dispatcher needs to take
-the new method into account. So the dispatching logic is only completely known
-after the whole program has been translated!
-
-Other features that are *implicitly* triggered cause problems for modularity
-too. Type converters fall into this category:
-
-.. code-block:: nim
-  # module A
-  converter toBool(x: int): bool =
-    result = x != 0
-
-.. code-block:: nim
-  # module B
-  import A
-
-  if 1:
-    echo "ugly, but should work"
-
-If in the above example module ``B`` is re-compiled, but ``A`` is not then
-``B`` needs to be aware of ``toBool`` even though  ``toBool`` is not referenced
-in ``B`` *explicitly*.
-
-Both the multi method and the type converter problems are solved by the
-AST replay implementation.
-
-
-Generics
-~~~~~~~~
-
-We cache generic instantiations and need to ensure this caching works
-well with the incremental compilation feature. Since the cache is
-attached to the ``PSym`` datastructure, it should work without any
-special logic.
-
-
-Backend issues
---------------
-
-- Init procs must not be "forgotten" to be called.
-- Files must not be "forgotten" to be linked.
-- Method dispatchers are global.
-- DLL loading via ``dlsym`` is global.
-- Emulated thread vars are global.
-
-However the biggest problem is that dead code elimination breaks modularity!
-To see why, consider this scenario: The module ``G`` (for example the huge
-Gtk2 module...) is compiled with dead code elimination turned on. So none
-of ``G``'s procs is generated at all.
-
-Then module ``B`` is compiled that requires ``G.P1``. Ok, no problem,
-``G.P1`` is loaded from the symbol file and ``G.c`` now contains ``G.P1``.
-
-Then module ``A`` (that depends on ``B`` and ``G``) is compiled and ``B``
-and ``G`` are left unchanged. ``A`` requires ``G.P2``.
-
-So now ``G.c`` MUST contain both ``P1`` and ``P2``, but we haven't even
-loaded ``P1`` from the symbol file, nor do we want to because we then quickly
-would restore large parts of the whole program.
-
-Solution
-~~~~~~~~ 
-
-The backend must have some logic so that if the currently processed module
-is from the compilation cache, the ``ast`` field is not accessed. Instead
-the generated C(++) for the symbol's body needs to be cached too and
-inserted back into the produced C file. This approach seems to deal with
-all the outlined problems above.
-
-
-Debugging Nim's memory management
-=================================
-
-The following paragraphs are mostly a reminder for myself. Things to keep
-in mind:
-
-* If an assertion in Nim's memory manager or GC fails, the stack trace
-  keeps allocating memory! Thus a stack overflow may happen, hiding the
-  real issue.
-* What seem to be C code generation problems is often a bug resulting from
-  not producing prototypes, so that some types default to ``cint``. Testing
-  without the ``-w`` option helps!
-
-
-The Garbage Collector
-=====================
-
-Introduction
-------------
-
-I use the term *cell* here to refer to everything that is traced
-(sequences, refs, strings).
-This section describes how the GC works.
-
-The basic algorithm is *Deferrent Reference Counting* with cycle detection.
-References on the stack are not counted for better performance and easier C
-code generation.
-
-Each cell has a header consisting of a RC and a pointer to its type
-descriptor. However the program does not know about these, so they are placed at
-negative offsets. In the GC code the type ``PCell`` denotes a pointer
-decremented by the right offset, so that the header can be accessed easily. It
-is extremely important that ``pointer`` is not confused with a ``PCell``
-as this would lead to a memory corruption.
-
-
-The CellSet data structure
---------------------------
-
-The GC depends on an extremely efficient datastructure for storing a
-set of pointers - this is called a ``TCellSet`` in the source code.
-Inserting, deleting and searching are done in constant time. However,
-modifying a ``TCellSet`` during traversation leads to undefined behaviour.
-
-.. code-block:: Nim
-  type
-    TCellSet # hidden
-
-  proc cellSetInit(s: var TCellSet) # initialize a new set
-  proc cellSetDeinit(s: var TCellSet) # empty the set and free its memory
-  proc incl(s: var TCellSet, elem: PCell) # include an element
-  proc excl(s: var TCellSet, elem: PCell) # exclude an element
-
-  proc `in`(elem: PCell, s: TCellSet): bool # tests membership
-
-  iterator elements(s: TCellSet): (elem: PCell)
-
-
-All the operations have to perform efficiently. Because a Cellset can
-become huge a hash table alone is not suitable for this.
-
-We use a mixture of bitset and hash table for this. The hash table maps *pages*
-to a page descriptor. The page descriptor contains a bit for any possible cell
-address within this page. So including a cell is done as follows:
-
-- Find the page descriptor for the page the cell belongs to.
-- Set the appropriate bit in the page descriptor indicating that the
-  cell points to the start of a memory block.
-
-Removing a cell is analogous - the bit has to be set to zero.
-Single page descriptors are never deleted from the hash table. This is not
-needed as the data structures needs to be rebuilt periodically anyway.
-
-Complete traversal is done in this way::
-
-  for each page descriptor d:
-    for each bit in d:
-      if bit == 1:
-        traverse the pointer belonging to this bit
-
-
-Further complications
----------------------
-
-In Nim the compiler cannot always know if a reference
-is stored on the stack or not. This is caused by var parameters.
-Consider this example:
-
-.. code-block:: Nim
-  proc setRef(r: var ref TNode) =
-    new(r)
-
-  proc usage =
-    var
-      r: ref TNode
-    setRef(r) # here we should not update the reference counts, because
-              # r is on the stack
-    setRef(r.left) # here we should update the refcounts!
-
-We have to decide at runtime whether the reference is on the stack or not.
-The generated code looks roughly like this:
-
-.. code-block:: C
-  void setref(TNode** ref) {
-    unsureAsgnRef(ref, newObj(TNode_TI, sizeof(TNode)))
-  }
-  void usage(void) {
-    setRef(&r)
-    setRef(&r->left)
-  }
-
-Note that for systems with a continuous stack (which most systems have)
-the check whether the ref is on the stack is very cheap (only two
-comparisons).
-
-
-Code generation for closures
-============================
-
-Code generation for closures is implemented by `lambda lifting`:idx:.
-
-Design
-------
-
-A ``closure`` proc var can call ordinary procs of the default Nim calling
-convention. But not the other way round! A closure is implemented as a
-``tuple[prc, env]``. ``env`` can be nil implying a call without a closure.
-This means that a call through a closure generates an ``if`` but the
-interoperability is worth the cost of the ``if``. Thunk generation would be
-possible too, but it's slightly more effort to implement.
-
-Tests with GCC on Amd64 showed that it's really beneficical if the
-'environment' pointer is passed as the last argument, not as the first argument.
-
-Proper thunk generation is harder because the proc that is to wrap
-could stem from a complex expression:
-
-.. code-block:: nim
-  receivesClosure(returnsDefaultCC[i])
-
-A thunk would need to call 'returnsDefaultCC[i]' somehow and that would require
-an *additional* closure generation... Ok, not really, but it requires to pass
-the function to call. So we'd end up with 2 indirect calls instead of one.
-Another much more severe problem which this solution is that it's not GC-safe
-to pass a proc pointer around via a generic ``ref`` type.
-
-
-Example code:
-
-.. code-block:: nim
-  proc add(x: int): proc (y: int): int {.closure.} =
-    return proc (y: int): int =
-      return x + y
-
-  var add2 = add(2)
-  echo add2(5) #OUT 7
-
-This should produce roughly this code:
-
-.. code-block:: nim
-  type
-    PEnv = ref object
-      x: int # data
-
-  proc anon(y: int, c: PEnv): int =
-    return y + c.x
-
-  proc add(x: int): tuple[prc, data] =
-    var env: PEnv
-    new env
-    env.x = x
-    result = (anon, env)
-
-  var add2 = add(2)
-  let tmp = if add2.data == nil: add2.prc(5) else: add2.prc(5, add2.data)
-  echo tmp
-
-
-Beware of nesting:
-
-.. code-block:: nim
-  proc add(x: int): proc (y: int): proc (z: int): int {.closure.} {.closure.} =
-    return lamba (y: int): proc (z: int): int {.closure.} =
-      return lambda (z: int): int =
-        return x + y + z
-
-  var add24 = add(2)(4)
-  echo add24(5) #OUT 11
-
-This should produce roughly this code:
-
-.. code-block:: nim
-  type
-    PEnvX = ref object
-      x: int # data
-
-    PEnvY = ref object
-      y: int
-      ex: PEnvX
-
-  proc lambdaZ(z: int, ey: PEnvY): int =
-    return ey.ex.x + ey.y + z
-
-  proc lambdaY(y: int, ex: PEnvX): tuple[prc, data: PEnvY] =
-    var ey: PEnvY
-    new ey
-    ey.y = y
-    ey.ex = ex
-    result = (lambdaZ, ey)
-
-  proc add(x: int): tuple[prc, data: PEnvX] =
-    var ex: PEnvX
-    ex.x = x
-    result = (labmdaY, ex)
-
-  var tmp = add(2)
-  var tmp2 = tmp.fn(4, tmp.data)
-  var add24 = tmp2.fn(4, tmp2.data)
-  echo add24(5)
-
-
-We could get rid of nesting environments by always inlining inner anon procs.
-More useful is escape analysis and stack allocation of the environment,
-however.
-
-
-Alternative
------------
-
-Process the closure of all inner procs in one pass and accumulate the
-environments. This is however not always possible.
-
-
-Accumulator
------------
-
-.. code-block:: nim
-  proc getAccumulator(start: int): proc (): int {.closure} =
-    var i = start
-    return lambda: int =
-      inc i
-      return i
-
-  proc p =
-    var delta = 7
-    proc accumulator(start: int): proc(): int =
-      var x = start-1
-      result = proc (): int =
-        x = x + delta
-        inc delta
-        return x
-
-    var a = accumulator(3)
-    var b = accumulator(4)
-    echo a() + b()
-
-
-Internals
----------
-
-Lambda lifting is implemented as part of the ``transf`` pass. The ``transf``
-pass generates code to setup the environment and to pass it around. However,
-this pass does not change the types! So we have some kind of mismatch here; on
-the one hand the proc expression becomes an explicit tuple, on the other hand
-the tyProc(ccClosure) type is not changed. For C code generation it's also
-important the hidden formal param is ``void*`` and not something more
-specialized. However the more specialized env type needs to passed to the
-backend somehow. We deal with this by modifying ``s.ast[paramPos]`` to contain
-the formal hidden parameter, but not ``s.typ``!
-
-
diff --git a/doc/koch.md b/doc/koch.md
new file mode 100644
index 000000000..8fa19ce44
--- /dev/null
+++ b/doc/koch.md
@@ -0,0 +1,91 @@
+===============================
+   Nim maintenance script
+===============================
+
+:Version: |nimversion|
+
+.. default-role:: code
+.. include:: rstcommon.rst
+.. contents::
+
+> "A great chef is an artist that I truly respect" -- Robert Stack.
+
+
+Introduction
+============
+
+The `koch`:idx: program is Nim's maintenance script. It is a replacement
+for make and shell scripting with the advantage that it is much more portable.
+The word *koch* means *cook* in German. `koch`:cmd: is used mainly to build the
+Nim compiler, but it can also be used for other tasks. This document
+describes the supported commands and their options.
+
+
+Commands
+========
+
+boot command
+------------
+
+The `boot`:idx: command bootstraps the compiler, and it accepts different
+options:
+
+-d:release    By default a debug version is created, passing this option will
+  force a release build, which is much faster and should be preferred
+  unless you are debugging the compiler.
+-d:nimUseLinenoise     Use the linenoise library for interactive mode
+                       (not needed on Windows).
+-d:leanCompiler        Produce a compiler without JS codegen or
+                       documentation generator in order to use less RAM
+                       for bootstrapping.
+
+After compilation is finished you will hopefully end up with the nim
+compiler in the `bin` directory. You can add Nim's `bin` directory to
+your `$PATH` or use the install command to place it where it will be
+found.
+
+csource command
+---------------
+
+The `csource`:idx: command builds the C sources for installation. It accepts
+the same options as you would pass to the [boot command](
+#commands-boot-command).
+
+temp command
+------------
+
+The temp command builds the Nim compiler but with a different final name
+(`nim_temp`:cmd:), so it doesn't overwrite your normal compiler. You can use
+this command to test different options, the same you would issue for the [boot
+command](#commands-boot-command).
+
+test command
+------------
+
+The `test`:idx: command can also be invoked with the alias `tests`:option:. This
+command will compile and run ``testament/tester.nim``, which is the main
+driver of Nim's test suite. You can pass options to the `test`:option: command,
+they will be forwarded to the tester. See its source code for available
+options.
+
+web command
+-----------
+
+The `web`:idx: command converts the documentation in the `doc` directory
+from rst to HTML. It also repeats the same operation but places the result in
+the ``web/upload`` which can be used to update the website at
+https://nim-lang.org.
+
+By default, the documentation will be built in parallel using the number of
+available CPU cores. If any documentation build sub-commands fail, they will
+be rerun in serial fashion so that meaningful error output can be gathered for
+inspection. The `--parallelBuild:n`:option: switch or configuration option can be
+used to force a specific number of parallel jobs or run everything serially
+from the start (`n == 1`).
+
+pdf command
+-----------
+
+The `pdf`:idx: command builds PDF versions of Nim documentation: Manual,
+Tutorial and a few other documents. To run it one needs to
+[install Latex/xelatex](https://www.latex-project.org/get) first.
diff --git a/doc/koch.rst b/doc/koch.rst
deleted file mode 100644
index 35cf9d8b6..000000000
--- a/doc/koch.rst
+++ /dev/null
@@ -1,82 +0,0 @@
-===============================
-   Nim maintenance script
-===============================
-
-:Version: |nimversion|
-
-.. contents::
-
-.. raw:: html
-  <blockquote><p>
-  "A great chef is an artist that I truly respect" -- Robert Stack.
-  </p></blockquote>
-
-
-Introduction
-============
-
-The `koch`:idx: program is Nim's maintenance script. It is a replacement
-for make and shell scripting with the advantage that it is much more portable.
-The word *koch* means *cook* in German. ``koch`` is used mainly to build the
-Nim compiler, but it can also be used for other tasks. This document
-describes the supported commands and their options.
-
-
-Commands
-========
-
-boot command
-------------
-
-The `boot`:idx: command bootstraps the compiler, and it accepts different
-options:
-
--d:release
-  By default a debug version is created, passing this option will
-  force a release build, which is much faster and should be preferred
-  unless you are debugging the compiler.
--d:useLinenoise
-  Use the linenoise library for interactive mode (not needed on Windows).
-
-After compilation is finished you will hopefully end up with the nim
-compiler in the ``bin`` directory. You can add Nim's ``bin`` directory to
-your ``$PATH`` or use the `install command`_ to place it where it will be
-found.
-
-csource command
----------------
-
-The `csource`:idx: command builds the C sources for installation. It accepts
-the same options as you would pass to the `boot command`_.
-
-temp command
-------------
-
-The temp command builds the Nim compiler but with a different final name
-(``nim_temp``), so it doesn't overwrite your normal compiler. You can use
-this command to test different options, the same you would issue for the `boot
-command`_.
-
-test command
-------------
-
-The `test`:idx: command can also be invoked with the alias ``tests``. This
-command will compile and run ``tests/testament/tester.nim``, which is the main
-driver of Nim's test suite. You can pass options to the ``test`` command,
-they will be forwarded to the tester. See its source code for available
-options.
-
-web command
------------
-
-The `web`:idx: command converts the documentation in the ``doc`` directory
-from rst to HTML. It also repeats the same operation but places the result in
-the ``web/upload`` which can be used to update the website at
-https://nim-lang.org.
-
-By default the documentation will be built in parallel using the number of
-available CPU cores. If any documentation build sub commands fail, they will
-be rerun in serial fashion so that meaninful error output can be gathered for
-inspection. The ``--parallelBuild:n`` switch or configuration option can be
-used to force a specific number of parallel jobs or run everything serially
-from the start (``n == 1``).
diff --git a/doc/lib.md b/doc/lib.md
new file mode 100644
index 000000000..1507bbaac
--- /dev/null
+++ b/doc/lib.md
@@ -0,0 +1,682 @@
+====================
+Nim Standard Library
+====================
+
+:Author: Andreas Rumpf
+:Version: |nimversion|
+
+.. default-role:: code
+.. include:: rstcommon.rst
+.. contents::
+
+Nim's library is divided into *pure libraries*, *impure libraries*, and *wrappers*.
+
+Pure libraries do not depend on any external ``*.dll`` or ``lib*.so`` binary
+while impure libraries do. A wrapper is an impure library that is a very
+low-level interface to a C library.
+
+Read [this document](apis.html) for a quick overview of the API design.
+
+
+Nimble
+======
+
+Nim's standard library only covers the basics, check
+out https://nimble.directory/ for a list of 3rd party packages.
+
+
+Pure libraries
+==============
+
+Automatic imports
+-----------------
+
+* [system](system.html)
+  Basic procs and operators that every program needs. It also provides IO
+  facilities for reading and writing text and binary files. It is imported
+  implicitly by the compiler. Do not import it directly. It relies on compiler
+  magic to work.
+
+Core
+----
+
+* [atomics](atomics.html)
+  Types and operations for atomic operations and lockless algorithms.
+
+* [bitops](bitops.html)
+  Provides a series of low-level methods for bit manipulation.
+
+* [compilesettings](compilesettings.html)
+  Querying the compiler about diverse configuration settings from code.
+
+* [cpuinfo](cpuinfo.html)
+  Procs to determine the number of CPUs / cores.
+
+* [effecttraits](effecttraits.html)
+  Access to the inferred .raises effects
+  for Nim's macro system.
+
+* [endians](endians.html)
+  Helpers that deal with different byte orders.
+
+* [locks](locks.html)
+  Locks and condition variables for Nim.
+
+* [macrocache](macrocache.html)
+  Provides an API for macros to collect compile-time information across modules.
+
+* [macros](macros.html)
+  Contains the AST API and documentation of Nim for writing macros.
+
+* [rlocks](rlocks.html)
+  Reentrant locks for Nim.
+
+* [typeinfo](typeinfo.html)
+  Provides (unsafe) access to Nim's run-time type information.
+
+* [typetraits](typetraits.html)
+  Compile-time reflection procs for working with types.
+
+* [volatile](volatile.html)
+  Code for generating volatile loads and stores,
+  which are useful in embedded and systems programming.
+
+
+Algorithms
+----------
+
+* [algorithm](algorithm.html)
+  Some common generic algorithms like sort or binary search.
+
+* [enumutils](enumutils.html)
+  Additional functionality for the built-in `enum` type.
+
+* [sequtils](sequtils.html)
+  Operations for the built-in `seq` type
+  which were inspired by functional programming languages.
+
+* [setutils](setutils.html)
+  Additional functionality for the built-in `set` type.
+
+
+Collections
+-----------
+
+* [critbits](critbits.html)
+  A *crit bit tree* which is an efficient
+  container for a sorted set of strings, or a sorted mapping of strings.
+
+* [deques](deques.html)
+  Implementation of a double-ended queue.
+  The underlying implementation uses a `seq`.
+
+* [heapqueue](heapqueue.html)
+  Implementation of a binary heap data structure that can be used as a priority queue.
+
+* [intsets](intsets.html)
+  Efficient implementation of a set of ints as a sparse bit set.
+
+* [lists](lists.html)
+  Nim linked list support. Contains singly and doubly linked lists and
+  circular lists ("rings").
+
+* [options](options.html)
+  The option type encapsulates an optional value.
+
+* [packedsets](packedsets.html)
+  Efficient implementation of a set of ordinals as a sparse bit set.
+
+* [ropes](ropes.html)
+  A *rope* data type.
+  Ropes can represent very long strings efficiently;
+  in particular, concatenation is done in O(1) instead of O(n).
+
+* [sets](sets.html)
+  Nim hash set support.
+
+* [strtabs](strtabs.html)
+  The `strtabs` module implements an efficient hash table that is a mapping
+  from strings to strings. Supports a case-sensitive, case-insensitive and
+  style-insensitive modes.
+
+* [tables](tables.html)
+  Nim hash table support. Contains tables, ordered tables, and count tables.
+
+
+String handling
+---------------
+
+* [cstrutils](cstrutils.html)
+  Utilities for `cstring` handling.
+
+* [editdistance](editdistance.html)
+  An algorithm to compute the edit distance between two
+  Unicode strings.
+
+* [encodings](encodings.html)
+  Converts between different character encodings. On UNIX, this uses
+  the `iconv` library, on Windows the Windows API.
+
+* [formatfloat](formatfloat.html)
+  Formatting floats as strings.
+
+* [objectdollar](objectdollar.html)
+  A generic `$` operator to convert objects to strings.
+
+* [punycode](punycode.html)
+  Implements a representation of Unicode with the limited ASCII character subset.
+
+* [strbasics](strbasics.html)
+  Some high performance string operations.
+
+* [strformat](strformat.html)
+  Macro based standard string interpolation/formatting. Inspired by
+  Python's f-strings.\
+  **Note:** if you need templating, consider using Nim
+  [Source Code Filters (SCF)](filters.html).
+
+* [strmisc](strmisc.html)
+  Uncommon string handling operations that do not
+  fit with the commonly used operations in [strutils](strutils.html).
+
+* [strscans](strscans.html)
+  A `scanf` macro for convenient parsing of mini languages.
+
+* [strutils](strutils.html)
+  Common string handling operations like changing
+  case of a string, splitting a string into substrings, searching for
+  substrings, replacing substrings.
+
+* [unicode](unicode.html)
+  Support for handling the Unicode UTF-8 encoding.
+
+* [unidecode](unidecode.html)
+  It provides a single proc that does Unicode to ASCII transliterations.
+  Based on Python's Unidecode module.
+
+* [widestrs](widestrs.html)
+  Nim support for C/C++'s wide strings.
+
+* [wordwrap](wordwrap.html)
+  An algorithm for word-wrapping Unicode strings.
+
+
+Time handling
+-------------
+
+* [monotimes](monotimes.html)
+  The `monotimes` module implements monotonic timestamps.
+
+* [times](times.html)
+  The `times` module contains support for working with time.
+
+
+Generic Operating System Services
+---------------------------------
+
+* [appdirs](appdirs.html)
+  Helpers for determining special directories used by apps.
+
+* [cmdline](cmdline.html)
+  System facilities for reading command line parameters.
+
+* [dirs](dirs.html)
+  Directory handling.
+
+* [distros](distros.html)
+  Basics for OS distribution ("distro") detection
+  and the OS's native package manager.
+  Its primary purpose is to produce output for Nimble packages,
+  but it also contains the widely used **Distribution** enum
+  that is useful for writing platform-specific code.
+  See [packaging](packaging.html) for hints on distributing Nim using OS packages.
+
+* [dynlib](dynlib.html)
+  Accessing symbols from shared libraries.
+
+* [envvars](envvars.html)
+  Environment variable handling.
+
+* [exitprocs](exitprocs.html)
+  Adding hooks to program exit.
+
+* [files](files.html)
+  File handling.
+
+* [memfiles](memfiles.html)
+  Support for memory-mapped files (Posix's `mmap`)
+  on the different operating systems.
+
+* [os](os.html)
+  Basic operating system facilities like retrieving environment variables,
+  reading command line arguments, working with directories, running shell
+  commands, etc.
+
+* [oserrors](oserrors.html)
+  OS error reporting.
+
+* [osproc](osproc.html)
+  Module for process communication beyond `os.execShellCmd`.
+
+* [paths](paths.html)
+  Path handling.
+
+* [reservedmem](reservedmem.html)
+  Utilities for reserving portions of the
+  address space of a program without consuming physical memory.
+
+* [streams](streams.html)
+  A stream interface and two implementations thereof:
+  the `FileStream` and the `StringStream` which implement the stream
+  interface for Nim file objects (`File`) and strings. Other modules
+  may provide other implementations for this standard stream interface.
+
+* [symlinks](symlinks.html)
+  Symlink handling.
+
+* [syncio](syncio.html)
+  Various synchronized I/O operations.
+
+* [terminal](terminal.html)
+  A module to control the terminal output (also called *console*).
+  
+* [tempfiles](tempfiles.html)
+  Some utilities for generating temporary path names and
+  creating temporary files and directories.
+
+
+Math libraries
+--------------
+
+* [complex](complex.html)
+  Complex numbers and relevant mathematical operations.
+
+* [fenv](fenv.html)
+  Floating-point environment. Handling of floating-point rounding and
+  exceptions (overflow, zero-divide, etc.).
+
+* [lenientops](lenientops.html)
+  Binary operators for mixed integer/float expressions for convenience.
+
+* [math](math.html)
+  Mathematical operations like cosine, square root.
+
+* [random](random.html)
+  Fast and tiny random number generator.
+
+* [rationals](rationals.html)
+  Rational numbers and relevant mathematical operations.
+
+* [stats](stats.html)
+  Statistical analysis.
+
+* [sysrand](sysrand.html)
+  Cryptographically secure pseudorandom number generator.
+
+
+Internet Protocols and Support
+------------------------------
+
+* [async](async.html)
+  Exports `asyncmacro` and `asyncfutures` for native backends, and `asyncjs` on the JS backend. 
+
+* [asyncdispatch](asyncdispatch.html)
+  An asynchronous dispatcher for IO operations.
+
+* [asyncfile](asyncfile.html)
+  An asynchronous file reading and writing using `asyncdispatch`.
+
+* [asyncftpclient](asyncftpclient.html)
+  An asynchronous FTP client using the `asyncnet` module.
+
+* [asynchttpserver](asynchttpserver.html)
+  An asynchronous HTTP server using the `asyncnet` module.
+
+* [asyncmacro](asyncmacro.html)
+  `async` and `multisync` macros for `asyncdispatch`.
+
+* [asyncnet](asyncnet.html)
+  Asynchronous sockets based on the `asyncdispatch` module.
+
+* [asyncstreams](asyncstreams.html)
+  `FutureStream` - a future that acts as a queue.
+
+* [cgi](cgi.html)
+  Helpers for CGI applications.
+
+* [cookies](cookies.html)
+  Helper procs for parsing and generating cookies.
+
+* [httpclient](httpclient.html)
+  A simple HTTP client with support for both synchronous
+  and asynchronous retrieval of web pages.
+
+* [mimetypes](mimetypes.html)
+  A mimetypes database.
+
+* [nativesockets](nativesockets.html)
+  A low-level sockets API.
+
+* [net](net.html)
+  A high-level sockets API.
+
+* [selectors](selectors.html)
+  A selector API with backends specific to each OS.
+  Supported OS primitives: `epoll`, `kqueue`, `poll`, and `select` on Windows.
+
+* [smtp](smtp.html)
+  A simple SMTP client with support for both synchronous and asynchronous operation.
+
+* [socketstreams](socketstreams.html)
+  An implementation of the streams interface for sockets.
+
+* [uri](uri.html)
+  Functions for working with URIs and URLs.
+
+
+Threading
+---------
+
+* [isolation](isolation.html)
+  The `Isolated[T]` type for
+  safe construction of isolated subgraphs that can be
+  passed efficiently to different channels and threads.
+
+* [tasks](tasks.html)
+  Basic primitives for creating parallel programs.
+
+* [threadpool](threadpool.html)
+  Implements Nim's [spawn](manual_experimental.html#parallel-amp-spawn).
+
+* [typedthreads](typedthreads.html)
+  Basic Nim thread support.
+
+
+Parsers
+-------
+
+* [htmlparser](htmlparser.html)
+  HTML document parser that creates a XML tree representation.
+
+* [json](json.html)
+  High-performance JSON parser.
+
+* [lexbase](lexbase.html)
+  A low-level module that implements an extremely efficient buffering
+  scheme for lexers and parsers. This is used by the diverse parsing modules.
+
+* [parsecfg](parsecfg.html)
+  The `parsecfg` module implements a high-performance configuration file
+  parser. The configuration file's syntax is similar to the Windows ``.ini``
+  format, but much more powerful, as it is not a line based parser. String
+  literals, raw string literals, and triple quote string literals are supported
+  as in the Nim programming language.
+
+* [parsecsv](parsecsv.html)
+  The `parsecsv` module implements a simple high-performance CSV parser.
+
+* [parsejson](parsejson.html)
+  A JSON parser. It is used and exported by the [json](json.html) module, but can also be used in its own right.
+
+* [parseopt](parseopt.html)
+  The `parseopt` module implements a command line option parser.
+
+* [parsesql](parsesql.html)
+  The `parsesql` module implements a simple high-performance SQL parser.
+
+* [parseutils](parseutils.html)
+  Helpers for parsing tokens, numbers, identifiers, etc.
+
+* [parsexml](parsexml.html)
+  The `parsexml` module implements a simple high performance XML/HTML parser.
+  The only encoding that is supported is UTF-8. The parser has been designed
+  to be somewhat error-correcting, so that even some "wild HTML" found on the
+  web can be parsed with it.
+
+* [pegs](pegs.html)
+  Procedures and operators for handling PEGs.
+
+
+Docutils
+--------
+
+* [packages/docutils/highlite](highlite.html)
+  Source highlighter for programming or markup languages. Currently,
+  only a few languages are supported, other languages may be added.
+  The interface supports one language nested in another.
+
+* [packages/docutils/rst](rst.html)
+  A reStructuredText parser. A large subset
+  is implemented. Some features of the markdown wiki syntax are also supported.
+
+* [packages/docutils/rstast](rstast.html)
+  An AST for the reStructuredText parser.
+
+* [packages/docutils/rstgen](rstgen.html)
+  A generator of HTML/Latex from reStructuredText.
+
+
+XML Processing
+--------------
+
+* [xmltree](xmltree.html)
+  A simple XML tree. More efficient and simpler than the DOM. It also
+  contains a macro for XML/HTML code generation.
+
+* [xmlparser](xmlparser.html)
+  XML document parser that creates a XML tree representation.
+
+
+Generators
+----------
+
+* [genasts](genasts.html)
+  AST generation using captured variables for macros.
+
+* [htmlgen](htmlgen.html)
+  A simple XML and HTML code
+  generator. Each commonly used HTML tag has a corresponding macro
+  that generates a string with its HTML representation.
+
+
+Hashing
+-------
+
+* [base64](base64.html)
+  A Base64 encoder and decoder.
+
+* [hashes](hashes.html)
+  Efficient computations of hash values for diverse Nim types.
+
+* [md5](md5.html)
+  The MD5 checksum algorithm.
+
+* [oids](oids.html)
+  An OID is a global ID that consists of a timestamp,
+  a unique counter, and a random value. This combination should suffice to
+  produce a globally distributed unique ID.
+
+* [sha1](sha1.html)
+  The SHA-1 checksum algorithm.
+
+
+Serialization
+-------------
+
+* [jsonutils](jsonutils.html)
+  Hookable (de)serialization for arbitrary types
+  using JSON.
+
+* [marshal](marshal.html)
+  Contains procs for serialization and deserialization of arbitrary Nim
+  data structures.
+
+
+Miscellaneous
+-------------
+
+* [assertions](assertions.html)
+  Assertion handling.
+
+* [browsers](browsers.html)
+  Procs for opening URLs with the user's default
+  browser.
+
+* [colors](colors.html)
+  Color handling.
+
+* [coro](coro.html)
+  Experimental coroutines in Nim.
+
+* [decls](decls.html)
+  Syntax sugar for some declarations.
+
+* [enumerate](enumerate.html)
+  `enumerate` syntactic sugar based on Nim's macro system.
+
+* [importutils](importutils.html)
+  Utilities related to import and symbol resolution.
+
+* [logging](logging.html)
+  A simple logger.
+
+* [segfaults](segfaults.html)
+  Turns access violations or segfaults into a `NilAccessDefect` exception.
+
+* [sugar](sugar.html)
+  Nice syntactic sugar based on Nim's macro system.
+
+* [unittest](unittest.html)
+  Implements a Unit testing DSL.
+
+* [varints](varints.html)
+  Decode variable-length integers that are compatible with SQLite.
+
+* [with](with.html)
+  The `with` macro for easy function chaining.
+
+* [wrapnils](wrapnils.html)
+  Allows evaluating expressions safely against nil dereferences.
+
+
+Modules for the JavaScript backend
+----------------------------------
+
+* [asyncjs](asyncjs.html)
+  Types and macros for writing asynchronous procedures in JavaScript.
+
+* [dom](dom.html)
+  Declaration of the Document Object Model for the JS backend.
+
+* [jsbigints](jsbigints.html)
+  Arbitrary precision integers.
+
+* [jsconsole](jsconsole.html)
+  Wrapper for the `console` object.
+
+* [jscore](jscore.html)
+  The wrapper of core JavaScript functions. For most purposes, you should be using
+  the `math`, `json`, and `times` stdlib modules instead of this module.
+
+* [jsfetch](jsfetch.html)
+  Wrapper for `fetch`.
+
+* [jsffi](jsffi.html)
+  Types and macros for easier interaction with JavaScript.
+
+* [jsre](jsre.html)
+  Regular Expressions for the JavaScript target.
+
+
+Impure libraries
+================
+
+Regular expressions
+-------------------
+
+* [re](re.html)
+  Procedures and operators for handling regular
+  expressions. The current implementation uses PCRE.
+
+* [nre](nre.html)
+
+  Many help functions for handling regular expressions.
+  The current implementation uses PCRE.
+
+Database support
+----------------
+
+* [db_mysql](db_mysql.html)
+  A higher level MySQL database wrapper. The same interface is implemented
+  for other databases too.
+
+* [db_odbc](db_odbc.html)
+  A higher level ODBC database wrapper. The same interface is implemented
+  for other databases too.
+
+* [db_postgres](db_postgres.html)
+  A higher level PostgreSQL database wrapper. The same interface is implemented
+  for other databases too.
+
+* [db_sqlite](db_sqlite.html)
+  A higher level SQLite database wrapper. The same interface is implemented
+  for other databases too.
+
+
+Generic Operating System Services
+---------------------------------
+
+* [rdstdin](rdstdin.html)
+  Code for reading user input from stdin.
+
+
+Wrappers
+========
+
+The generated HTML for some of these wrappers is so huge that it is
+not contained in the distribution. You can then find them on the website.
+
+
+Windows-specific
+----------------
+
+* [winlean](winlean.html)
+  Wrapper for a small subset of the Win32 API.
+* [registry](registry.html)
+  Windows registry support.
+
+
+UNIX specific
+-------------
+
+* [posix](posix.html)
+  Wrapper for the POSIX standard.
+* [posix_utils](posix_utils.html)
+  Contains helpers for the POSIX standard or specialized for Linux and BSDs.
+
+
+Regular expressions
+-------------------
+
+* [pcre](pcre.html)
+  Wrapper for the PCRE library.
+
+
+Database support
+----------------
+
+* [mysql](mysql.html)
+  Wrapper for the mySQL API.
+* [odbcsql](odbcsql.html)
+  interface to the ODBC driver.
+* [postgres](postgres.html)
+  Wrapper for the PostgreSQL API.
+* [sqlite3](sqlite3.html)
+  Wrapper for the SQLite 3 API.
+
+
+Network Programming and Internet Protocols
+------------------------------------------
+
+* [openssl](openssl.html)
+  Wrapper for OpenSSL.
diff --git a/doc/lib.rst b/doc/lib.rst
deleted file mode 100644
index f0569070d..000000000
--- a/doc/lib.rst
+++ /dev/null
@@ -1,568 +0,0 @@
-====================
-Nim Standard Library
-====================
-
-:Author: Andreas Rumpf
-:Version: |nimversion|
-
-.. contents::
-
-  "The good thing about reinventing the wheel is that you can get a round one."
-
-Though the Nim Standard Library is still evolving, it is already quite
-usable. It is divided into *pure libraries*, *impure libraries* and *wrappers*.
-
-Pure libraries do not depend on any external ``*.dll`` or ``lib*.so`` binary
-while impure libraries do. A wrapper is an impure library that is a very
-low-level interface to a C library.
-
-Read this `document <apis.html>`_ for a quick overview of the API design.
-
-The `bottom <#nimble>`_ of this page includes a list of 3rd party packages
-created by the Nim community. These packages are a useful addition to the
-modules in the standard library.
-
-
-Pure libraries
-==============
-
-Core
-----
-
-* `system <system.html>`_
-  Basic procs and operators that every program needs. It also provides IO
-  facilities for reading and writing text and binary files. It is imported
-  implicitly by the compiler. Do not import it directly. It relies on compiler
-  magic to work.
-
-* `threads <threads.html>`_
-  Nim thread support. **Note**: This is part of the system module. Do not
-  import it explicitly.
-
-* `channels <channels.html>`_
-  Nim message passing support for threads. **Note**: This is part of the
-  system module. Do not import it explicitly.
-
-* `locks <locks.html>`_
-  Locks and condition variables for Nim.
-
-* `rlocks <rlocks.html>`_
-  Reentrant locks for Nim.
-
-* `macros <macros.html>`_
-  Contains the AST API and documentation of Nim for writing macros.
-
-* `typeinfo <typeinfo.html>`_
-  Provides (unsafe) access to Nim's run time type information.
-
-* `typetraits <typetraits.html>`_
-  This module defines compile-time reflection procs for working with types.
-
-* `threadpool <threadpool.html>`_
-  Implements Nim's `spawn <manual.html#spawn>`_.
-
-* `cpuinfo <cpuinfo.html>`_
-  This module implements procs to determine the number of CPUs / cores.
-
-* `lenientops <lenientops.html>`_
-  Provides binary operators for mixed integer/float expressions for convenience.
-
-
-
-Collections and algorithms
---------------------------
-
-* `algorithm <algorithm.html>`_
-  Implements some common generic algorithms like sort or binary search.
-* `tables <tables.html>`_
-  Nim hash table support. Contains tables, ordered tables and count tables.
-* `sets <sets.html>`_
-  Nim hash and bit set support.
-* `lists <lists.html>`_
-  Nim linked list support. Contains singly and doubly linked lists and
-  circular lists ("rings").
-* `deques <deques.html>`_
-  Implementation of a double-ended queue.
-  The underlying implementation uses a ``seq``.
-* `intsets <intsets.html>`_
-  Efficient implementation of a set of ints as a sparse bit set.
-* `critbits <critbits.html>`_
-  This module implements a *crit bit tree* which is an efficient
-  container for a sorted set of strings, or for a sorted mapping of strings.
-* `sequtils <sequtils.html>`_
-  This module implements operations for the built-in seq type
-  which were inspired by functional programming languages.
-* `sharedtables <sharedtables.html>`_
-  Nim shared hash table support. Contains shared tables.
-* `sharedlist <sharedlist.html>`_
-  Nim shared linked list support. Contains shared singly linked list.
-
-
-String handling
----------------
-
-* `strutils <strutils.html>`_
-  This module contains common string handling operations like changing
-  case of a string, splitting a string into substrings, searching for
-  substrings, replacing substrings.
-
-* `strformat <strformat.html>`_
-  Macro based standard string interpolation / formatting. Inpired by
-  Python's ```f``-strings.
-
-* `strmisc <strmisc.html>`_
-  This module contains uncommon string handling operations that do not
-  fit with the commonly used operations in strutils.
-
-* `parseutils <parseutils.html>`_
-  This module contains helpers for parsing tokens, numbers, identifiers, etc.
-
-* `strscans <strscans.html>`_
-  This module contains a ``scanf`` macro for convenient parsing of mini languages.
-
-* `strtabs <strtabs.html>`_
-  The ``strtabs`` module implements an efficient hash table that is a mapping
-  from strings to strings. Supports a case-sensitive, case-insensitive and
-  style-insensitive mode. An efficient string substitution operator ``%``
-  for the string table is also provided.
-
-* `unicode <unicode.html>`_
-  This module provides support to handle the Unicode UTF-8 encoding.
-
-* `encodings <encodings.html>`_
-  Converts between different character encodings. On UNIX, this uses
-  the ``iconv`` library, on Windows the Windows API.
-
-* `pegs <pegs.html>`_
-  This module contains procedures and operators for handling PEGs.
-
-* `ropes <ropes.html>`_
-  This module contains support for a *rope* data type.
-  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.
-
-
-Generic Operating System Services
----------------------------------
-
-* `os <os.html>`_
-  Basic operating system facilities like retrieving environment variables,
-  reading command line arguments, working with directories, running shell
-  commands, etc.
-
-* `osproc <osproc.html>`_
-  Module for process communication beyond ``os.execShellCmd``.
-
-* `times <times.html>`_
-  The ``times`` module contains basic support for working with time.
-
-* `dynlib <dynlib.html>`_
-  This module implements the ability to access symbols from shared libraries.
-
-* `streams <streams.html>`_
-  This module provides a stream interface and two implementations thereof:
-  the `FileStream` and the `StringStream` which implement the stream
-  interface for Nim file objects (`File`) and strings. Other modules
-  may provide other implementations for this standard stream interface.
-
-* `marshal <marshal.html>`_
-  Contains procs for serialization and deseralization of arbitrary Nim
-  data structures.
-
-* `terminal <terminal.html>`_
-  This module contains a few procedures to control the *terminal*
-  (also called *console*). The implementation simply uses ANSI escape
-  sequences and does not depend on any other module.
-
-* `memfiles <memfiles.html>`_
-  This module provides support for memory mapped files (Posix's ``mmap``)
-  on the different operating systems.
-
-* `fsmonitor <fsmonitor.html>`_
-  This module implements the ability to monitor a directory/file for changes
-  using Posix's inotify API.
-
-  **Warning:** This module will likely be moved out to a Nimble package soon.
-
-* `asyncfile <asyncfile.html>`_
-  This module implements asynchronous file reading and writing using
-  ``asyncdispatch``.
-
-* `distros <distros.html>`_
-  This module implements the basics for OS distribution ("distro") detection and the OS's native package manager.
-  Its primary purpose is to produce output for Nimble packages, but it also contains the widely used **Distribution** enum
-  that is useful for writing platform specific code.
-
-
-Math libraries
---------------
-
-* `math <math.html>`_
-  Mathematical operations like cosine, square root.
-
-* `complex <complex.html>`_
-  This module implements complex numbers and their mathematical operations.
-
-* `rationals <rationals.html>`_
-  This module implements rational numbers and their mathematical operations.
-
-* `fenv <fenv.html>`_
-  Floating-point environment. Handling of floating-point rounding and
-  exceptions (overflow, zero-devide, etc.).
-
-* `mersenne <mersenne.html>`_
-  Mersenne twister random number generator.
-
-* `random <random.html>`_
-  Fast and tiny random number generator.
-
-* `stats <stats.html>`_
-  Statistical analysis
-
-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.
-
-* `httpserver <httpserver.html>`_
-  This module implements a simple HTTP server.
-
-* `httpclient <httpclient.html>`_
-  This module implements a simple HTTP client which supports both synchronous
-  and asynchronous retrieval of web pages.
-
-* `smtp <smtp.html>`_
-  This module implement a simple SMTP client.
-
-* `cookies <cookies.html>`_
-  This module contains helper procs for parsing and generating cookies.
-
-* `mimetypes <mimetypes.html>`_
-  This module implements a mimetypes database.
-
-* `uri <uri.html>`_
-  This module provides functions for working with URIs.
-
-* `asyncdispatch <asyncdispatch.html>`_
-  This module implements an asynchronous dispatcher for IO operations.
-
-* `asyncnet <asyncnet.html>`_
-  This module implements asynchronous sockets based on the ``asyncdispatch``
-  module.
-
-* `asynchttpserver <asynchttpserver.html>`_
-  This module implements an asynchronous HTTP server using the ``asyncnet``
-  module.
-
-* `asyncftpclient <asyncftpclient.html>`_
-  This module implements an asynchronous FTP client using the ``asyncnet``
-  module.
-
-* `net <net.html>`_
-  This module implements a high-level sockets API. It will replace the
-  ``sockets`` module in the future.
-
-* `nativesockets <nativesockets.html>`_
-  This module implements a low-level sockets API.
-
-* `selectors <selectors.html>`_
-  This module implements a selector API with backends specific to each OS.
-  Currently epoll on Linux and select on other operating systems.
-
-Parsers
--------
-
-* `parseopt <parseopt.html>`_
-  The ``parseopt`` module implements a command line option parser.
-
-* `parseopt2 <parseopt2.html>`_
-  The ``parseopt2`` module implements a command line option parser. This
-  supports long and short command options with optional values and command line
-  arguments.
-
-* `parsecfg <parsecfg.html>`_
-  The ``parsecfg`` module implements a high performance configuration file
-  parser. The configuration file's syntax is similar to the Windows ``.ini``
-  format, but much more powerful, as it is not a line based parser. String
-  literals, raw string literals and triple quote string literals are supported
-  as in the Nim programming language.
-
-* `parsexml <parsexml.html>`_
-  The ``parsexml`` module implements a simple high performance XML/HTML parser.
-  The only encoding that is supported is UTF-8. The parser has been designed
-  to be somewhat error correcting, so that even some "wild HTML" found on the
-  Web can be parsed with it.
-
-* `parsecsv <parsecsv.html>`_
-  The ``parsecsv`` module implements a simple high performance CSV parser.
-
-* `parsesql <parsesql.html>`_
-  The ``parsesql`` module implements a simple high performance SQL parser.
-
-* `json <json.html>`_
-  High performance JSON parser.
-
-* `lexbase <lexbase.html>`_
-  This is a low level module that implements an extremely efficient buffering
-  scheme for lexers and parsers. This is used by the diverse parsing modules.
-
-* `highlite <highlite.html>`_
-  Source highlighter for programming or markup languages.  Currently
-  only few languages are supported, other languages may be added.
-  The interface supports one language nested in another.
-
-* `rst <rst.html>`_
-  This module implements a reStructuredText parser. A large subset
-  is implemented. Some features of the markdown wiki syntax are
-  also supported.
-
-* `rstast <rstast.html>`_
-  This module implements an AST for the reStructuredText parser.
-
-* `rstgen <rstgen.html>`_
-  This module implements a generator of HTML/Latex from reStructuredText.
-
-* `sexp <sexp.html>`_
-  High performance sexp parser and generator, mainly for communication
-  with emacs.
-
-
-XML Processing
---------------
-
-* `xmldom <xmldom.html>`_
-  This module implements the XML DOM Level 2.
-
-* `xmldomparser <xmldomparser.html>`_
-  This module parses an XML Document into a XML DOM Document representation.
-
-* `xmltree <xmltree.html>`_
-  A simple XML tree. More efficient and simpler than the DOM. It also
-  contains a macro for XML/HTML code generation.
-
-* `xmlparser <xmlparser.html>`_
-  This module parses an XML document and creates its XML tree representation.
-
-* `htmlparser <htmlparser.html>`_
-  This module parses an HTML document and creates its XML tree representation.
-
-* `htmlgen <htmlgen.html>`_
-  This module implements a simple XML and HTML code
-  generator. Each commonly used HTML tag has a corresponding macro
-  that generates a string with its HTML representation.
-
-Cryptography and Hashing
-------------------------
-
-* `hashes <hashes.html>`_
-  This module implements efficient computations of hash values for diverse
-  Nim types.
-
-* `md5 <md5.html>`_
-  This module implements the MD5 checksum algorithm.
-
-* `base64 <base64.html>`_
-  This module implements a base64 encoder and decoder.
-
-* `sha1 <sha1.html>`_
-  This module implements a sha1 encoder and decoder.
-
-
-Multimedia support
-------------------
-
-* `colors <colors.html>`_
-  This module implements color handling for Nim. It is used by
-  the ``graphics`` module.
-
-
-Miscellaneous
--------------
-
-* `events <events.html>`_
-  This module implements an event system that is not dependent on external
-  graphical toolkits.
-
-* `oids <oids.html>`_
-  An OID is a global ID that consists of a timestamp,
-  a unique counter and a random value. This combination should suffice to
-  produce a globally distributed unique ID. This implementation was extracted
-  from the Mongodb interface and it thus binary compatible with a Mongo OID.
-
-* `endians <endians.html>`_
-  This module contains helpers that deal with different byte orders.
-
-* `logging <logging.html>`_
-  This module implements a simple logger.
-
-* `options <options.html>`_
-  Types which encapsulate an optional value.
-
-* `sugar <sugar.html>`_
-  This module implements nice syntactic sugar based on Nim's macro system.
-
-* `coro <coro.html>`_
-  This module implements experimental coroutines in Nim.
-
-* `unittest <unittest.html>`_
-  Implements a Unit testing DSL.
-
-* `segfaults <segfaults.html>`_
-  Turns access violations or segfaults into a ``NilAccessError`` exception.
-
-Modules for JS backend
----------------------------
-
-* `dom <dom.html>`_
-  Declaration of the Document Object Model for the JS backend.
-
-* `jsffi <jsffi.html>`_
-  Types and macros for easier interaction with JavaScript.
-
-* `asyncjs <asyncjs.html>`_
-  Types and macros for writing asynchronous procedures in JavaScript.
-
-* `jscore <jscore.html>`_
-  Wrapper of core JavaScript functions. For most purposes you should be using
-  the ``math``, ``json``, and ``times`` stdlib modules instead of this module.
-
-Deprecated modules
-------------------
-
-* `asyncio <asyncio.html>`_
-  This module implements an asynchronous event loop for sockets.
-  **Deprecated since version 0.11.2:**
-  Use the `asyncnet <asyncnet.html>`_ together with the
-  `asyncdispatch <asyncdispatch.html>`_ module instead.
-
-* `ftpclient <ftpclient.html>`_
-  This module implements an FTP client.
-  **Deprecated since version 0.11.3:**
-  Use the `asyncftpclient <asyncftpclient.html>`_ module instead.
-
-* `sockets <sockets.html>`_
-  This module implements a simple portable type-safe sockets layer.
-  **Deprecated since version 0.11.2:**
-  Use the `net <net.html>`_ or the `rawsockets <rawsockets.html>`_ module
-  instead.
-
-* `rawsockets <rawsockets.html>`_
-  **Deprecated since version 0.11.4:**
-  This module has been renamed to `nativesockets <nativesockets.html>`_.
-
-
-Impure libraries
-================
-
-Regular expressions
--------------------
-
-* `re <re.html>`_
-  This module contains procedures and operators for handling regular
-  expressions. The current implementation uses PCRE.
-
-* `nre <nre.html>`_
-  Another implementation of procedures for using regular expressions. Also uses
-  PCRE.
-
-
-Database support
-----------------
-
-* `db_postgres <db_postgres.html>`_
-  A higher level PostgreSQL database wrapper. The same interface is implemented
-  for other databases too.
-
-* `db_mysql <db_mysql.html>`_
-  A higher level MySQL database wrapper. The same interface is implemented
-  for other databases too.
-
-* `db_sqlite <db_sqlite.html>`_
-  A higher level SQLite database wrapper. The same interface is implemented
-  for other databases too.
-
-
-Other
------
-
-* `ssl <ssl.html>`_
-  This module provides an easy to use sockets-style
-  Nim interface to the OpenSSL library.
-
-
-Wrappers
-========
-
-The generated HTML for some of these wrappers is so huge that it is
-not contained in the distribution. You can then find them on the website.
-
-Windows specific
-----------------
-
-* `winlean <winlean.html>`_
-  Contains a wrapper for a small subset of the Win32 API.
-
-
-UNIX specific
--------------
-
-* `posix <posix.html>`_
-  Contains a wrapper for the POSIX standard.
-
-
-Regular expressions
--------------------
-
-* `pcre <pcre.html>`_
-  Wrapper for the PCRE library.
-
-
-GUI libraries
--------------
-
-* `iup <iup.html>`_
-  Wrapper of the IUP GUI library.
-
-
-Database support
-----------------
-
-* `postgres <postgres.html>`_
-  Contains a wrapper for the PostgreSQL API.
-* `mysql <mysql.html>`_
-  Contains a wrapper for the mySQL API.
-* `sqlite3 <sqlite3.html>`_
-  Contains a wrapper for SQLite 3 API.
-* `odbcsql <odbcsql.html>`_
-  interface to the ODBC driver.
-
-
-Network Programming and Internet Protocols
-------------------------------------------
-
-* `openssl <openssl.html>`_
-  Wrapper for OpenSSL.
-
-
-Nimble
-======
-
-Nimble is a package manager for the Nim programming language.
-For instructions on how to install Nimble packages see
-`its README <https://github.com/nim-lang/nimble#readme>`_.
-
-To see a list of Nimble's packages, check out `https://nimble.directory/ <https://nimble.directory/>`_ or the `packages repos <https://github.com/nim-lang/packages>`_ on GitHub.
diff --git a/doc/manual.md b/doc/manual.md
new file mode 100644
index 000000000..5c36a0a7b
--- /dev/null
+++ b/doc/manual.md
@@ -0,0 +1,9033 @@
+==========
+Nim Manual
+==========
+
+:Authors: Andreas Rumpf, Zahary Karadjov
+:Version: |nimversion|
+
+.. default-role:: code
+.. include:: rstcommon.rst
+.. contents::
+
+
+> "Complexity" seems to be a lot like "energy": you can transfer it from the
+> end-user to one/some of the other players, but the total amount seems to remain
+> pretty much constant for a given task. -- Ran
+
+
+About this document
+===================
+
+**Note**: This document is a draft! Several of Nim's features may need more
+precise wording. This manual is constantly evolving into a proper specification.
+
+**Note**: The experimental features of Nim are
+covered [here](manual_experimental.html).
+
+**Note**: Assignments, moves, and destruction are specified in
+the [destructors](destructors.html) document.
+
+
+This document describes the lexis, the syntax, and the semantics of the Nim language.
+
+To learn how to compile Nim programs and generate documentation see
+the [Compiler User Guide](nimc.html) and the [DocGen Tools Guide](docgen.html).
+
+The language constructs are explained using an extended BNF, in which `(a)*`
+means 0 or more `a`'s, `a+` means 1 or more `a`'s, and `(a)?` means an
+optional *a*. Parentheses may be used to group elements.
+
+`&` is the lookahead operator; `&a` means that an `a` is expected but
+not consumed. It will be consumed in the following rule.
+
+The `|`, `/` symbols are used to mark alternatives and have the lowest
+precedence. `/` is the ordered choice that requires the parser to try the
+alternatives in the given order. `/` is often used to ensure the grammar
+is not ambiguous.
+
+Non-terminals start with a lowercase letter, abstract terminal symbols are in
+UPPERCASE. Verbatim terminal symbols (including keywords) are quoted
+with `'`. An example:
+
+    ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)?
+
+The binary `^*` operator is used as a shorthand for 0 or more occurrences
+separated by its second argument; likewise `^+` means 1 or more
+occurrences: `a ^+ b` is short for `a (b a)*`
+and `a ^* b` is short for `(a (b a)*)?`. Example:
+
+    arrayConstructor = '[' expr ^* ',' ']'
+
+Other parts of Nim, like scoping rules or runtime semantics, are
+described informally.
+
+
+
+
+Definitions
+===========
+
+Nim code specifies a computation that acts on a memory consisting of
+components called `locations`:idx:. A variable is basically a name for a
+location. Each variable and location is of a certain `type`:idx:. The
+variable's type is called `static type`:idx:, the location's type is called
+`dynamic type`:idx:. If the static type is not the same as the dynamic type,
+it is a super-type or subtype of the dynamic type.
+
+An `identifier`:idx: is a symbol declared as a name for a variable, type,
+procedure, etc. The region of the program over which a declaration applies is
+called the `scope`:idx: of the declaration. Scopes can be nested. The meaning
+of an identifier is determined by the smallest enclosing scope in which the
+identifier is declared unless overloading resolution rules suggest otherwise.
+
+An expression specifies a computation that produces a value or location.
+Expressions that produce locations are called `l-values`:idx:. An l-value
+can denote either a location or the value the location contains, depending on
+the context.
+
+A Nim `program`:idx: consists of one or more text `source files`:idx: containing
+Nim code. It is processed by a Nim `compiler`:idx: into an `executable`:idx:.
+The nature of this executable depends on the compiler implementation; it may,
+for example, be a native binary or JavaScript source code.
+
+In a typical Nim program, most of the code is compiled into the executable.
+However, some code may be executed at
+`compile-time`:idx:. This can include constant expressions, macro definitions,
+and Nim procedures used by macro definitions. Most of the Nim language is
+supported at compile-time, but there are some restrictions -- see [Restrictions
+on Compile-Time Execution] for
+details. We use the term `runtime`:idx: to cover both compile-time execution
+and code execution in the executable.
+
+The compiler parses Nim source code into an internal data structure called the
+`abstract syntax tree`:idx: (`AST`:idx:). Then, before executing the code or
+compiling it into the executable, it transforms the AST through
+`semantic analysis`:idx:. This adds semantic information such as expression types,
+identifier meanings, and in some cases expression values. An error detected
+during semantic analysis is called a `static error`:idx:. Errors described in
+this manual are static errors when not otherwise specified.
+
+A `panic`:idx: is an error that the implementation detects
+and reports at runtime. The method for reporting such errors is via
+*raising exceptions* or *dying with a fatal error*. However, the implementation
+provides a means to disable these `runtime checks`:idx:. See the section
+[Pragmas] for details.
+
+Whether a panic results in an exception or in a fatal error is
+implementation specific. Thus, the following program is invalid; even though the
+code purports to catch the `IndexDefect` from an out-of-bounds array access, the
+compiler may instead choose to allow the program to die with a fatal error.
+
+  ```nim
+  var a: array[0..1, char]
+  let i = 5
+  try:
+    a[i] = 'N'
+  except IndexDefect:
+    echo "invalid index"
+  ```
+
+The current implementation allows switching between these different behaviors
+via `--panics:on|off`:option:. When panics are turned on, the program dies with a
+panic, if they are turned off the runtime errors are turned into
+exceptions. The benefit of `--panics:on`:option: is that it produces smaller binary
+code and the compiler has more freedom to optimize the code.
+
+An `unchecked runtime error`:idx: is an error that is not guaranteed to be
+detected and can cause the subsequent behavior of the computation to
+be arbitrary. Unchecked runtime errors cannot occur if only `safe`:idx:
+language features are used and if no runtime checks are disabled.
+
+A `constant expression`:idx: is an expression whose value can be computed during
+a semantic analysis of the code in which it appears. It is never an l-value and
+never has side effects. Constant expressions are not limited to the capabilities
+of semantic analysis, such as constant folding; they can use all Nim language
+features that are supported for compile-time execution. Since constant
+expressions can be used as an input to semantic analysis (such as for defining
+array bounds), this flexibility requires the compiler to interleave semantic
+analysis and compile-time code execution.
+
+It is mostly accurate to picture semantic analysis proceeding top to bottom and
+left to right in the source code, with compile-time code execution interleaved
+when necessary to compute values that are required for subsequent semantic
+analysis. We will see much later in this document that macro invocation not only
+requires this interleaving, but also creates a situation where semantic analysis
+does not entirely proceed top to bottom and left to right.
+
+
+Lexical Analysis
+================
+
+Encoding
+--------
+
+All Nim source files are in the UTF-8 encoding (or its ASCII subset). Other
+encodings are not supported. Any of the standard platform line termination
+sequences can be used - the Unix form using ASCII LF (linefeed), the Windows
+form using the ASCII sequence CR LF (return followed by linefeed), or the old
+Macintosh form using the ASCII CR (return) character. All of these forms can be
+used equally, regardless of the platform.
+
+
+Indentation
+-----------
+
+Nim's standard grammar describes an `indentation sensitive`:idx: language.
+This means that all the control structures are recognized by indentation.
+Indentation consists only of spaces; tabulators are not allowed.
+
+The indentation handling is implemented as follows: The lexer annotates the
+following token with the preceding number of spaces; indentation is not
+a separate token. This trick allows parsing of Nim with only 1 token of
+lookahead.
+
+The parser uses a stack of indentation levels: the stack consists of integers
+counting the spaces. The indentation information is queried at strategic
+places in the parser but ignored otherwise: The pseudo-terminal `IND{>}`
+denotes an indentation that consists of more spaces than the entry at the top
+of the stack; `IND{=}` an indentation that has the same number of spaces. `DED`
+is another pseudo terminal that describes the *action* of popping a value
+from the stack, `IND{>}` then implies to push onto the stack.
+
+With this notation we can now easily define the core of the grammar: A block of
+statements (simplified example):
+
+    ifStmt = 'if' expr ':' stmt
+             (IND{=} 'elif' expr ':' stmt)*
+             (IND{=} 'else' ':' stmt)?
+
+    simpleStmt = ifStmt / ...
+
+    stmt = IND{>} stmt ^+ IND{=} DED  # list of statements
+         / simpleStmt                 # or a simple statement
+
+
+
+Comments
+--------
+
+Comments start anywhere outside a string or character literal with the
+hash character `#`.
+Comments consist of a concatenation of `comment pieces`:idx:. A comment piece
+starts with `#` and runs until the end of the line. The end of line characters
+belong to the piece. If the next line only consists of a comment piece with
+no other tokens between it and the preceding one, it does not start a new
+comment:
+
+
+  ```nim
+  i = 0     # This is a single comment over multiple lines.
+    # The lexer merges these two pieces.
+    # The comment continues here.
+  ```
+
+
+`Documentation comments`:idx: are comments that start with two `##`.
+Documentation comments are tokens; they are only allowed at certain places in
+the input file as they belong to the syntax tree.
+
+
+Multiline comments
+------------------
+
+Starting with version 0.13.0 of the language Nim supports multiline comments.
+They look like:
+
+  ```nim
+  #[Comment here.
+  Multiple lines
+  are not a problem.]#
+  ```
+
+Multiline comments support nesting:
+
+  ```nim
+  #[  #[ Multiline comment in already
+     commented out code. ]#
+  proc p[T](x: T) = discard
+  ]#
+  ```
+
+Multiline documentation comments also exist and support nesting too:
+
+  ```nim
+  proc foo =
+    ##[Long documentation comment
+       here.
+    ]##
+  ```
+
+You can also use the [discard statement](#statements-and-expressions-discard-statement) together with
+[triple quoted string literals](#lexical-analysis-triple-quoted-string-literals) to create multiline comments:
+
+  ```nim
+  discard """ You can have any Nim code text commented
+  out inside this with no indentation restrictions.
+        yes("May I ask a pointless question?") """
+  ```
+
+This was how multiline comments were done before version 0.13.0,
+and it is used to provide specifications to [testament](testament.html#writing-unit-tests) test framework.
+
+
+Identifiers & Keywords
+----------------------
+
+Identifiers in Nim can be any string of letters, digits
+and underscores, with the following restrictions:
+
+* begins with a letter
+* does not end with an underscore `_`
+* two immediate following underscores `__` are not allowed:
+
+  ```
+  letter ::= 'A'..'Z' | 'a'..'z' | '\x80'..'\xff'
+  digit ::= '0'..'9'
+  IDENTIFIER ::= letter ( ['_'] (letter | digit) )*
+  ```
+
+Currently, any Unicode character with an ordinal value > 127 (non-ASCII) is
+classified as a `letter` and may thus be part of an identifier but later
+versions of the language may assign some Unicode characters to belong to the
+operator characters instead.
+
+The following keywords are reserved and cannot be used as identifiers:
+
+  ```nim file="keywords.txt"
+  ```
+
+Some keywords are unused; they are reserved for future developments of the
+language.
+
+
+Identifier equality
+-------------------
+
+Two identifiers are considered equal if the following algorithm returns true:
+
+  ```nim
+  proc sameIdentifier(a, b: string): bool =
+    a[0] == b[0] and
+      a.replace("_", "").toLowerAscii == b.replace("_", "").toLowerAscii
+  ```
+
+That means only the first letters are compared in a case-sensitive manner. Other
+letters are compared case-insensitively within the ASCII range and underscores are ignored.
+
+This rather unorthodox way to do identifier comparisons is called
+`partial case-insensitivity`:idx: and has some advantages over the conventional
+case sensitivity:
+
+It allows programmers to mostly use their own preferred
+spelling style, be it humpStyle or snake_style, and libraries written
+by different programmers cannot use incompatible conventions.
+A Nim-aware editor or IDE can show the identifiers as preferred.
+Another advantage is that it frees the programmer from remembering
+the exact spelling of an identifier. The exception with respect to the first
+letter allows common code like `var foo: Foo` to be parsed unambiguously.
+
+Note that this rule also applies to keywords, meaning that `notin` is
+the same as `notIn` and `not_in` (all-lowercase version (`notin`, `isnot`)
+is the preferred way of writing keywords).
+
+Historically, Nim was a fully `style-insensitive`:idx: language. This meant that
+it was not case-sensitive and underscores were ignored and there was not even a
+distinction between `foo` and `Foo`.
+
+
+Keywords as identifiers
+-----------------------
+
+If a keyword is enclosed in backticks it loses its keyword property and becomes an ordinary identifier.
+
+Examples
+
+  ```nim
+  var `var` = "Hello Stropping"
+  ```
+
+  ```nim
+  type Obj = object
+    `type`: int
+
+  let `object` = Obj(`type`: 9)
+  assert `object` is Obj
+  assert `object`.`type` == 9
+
+  var `var` = 42
+  let `let` = 8
+  assert `var` + `let` == 50
+
+  const `assert` = true
+  assert `assert`
+  ```
+
+
+String literals
+---------------
+
+Terminal symbol in the grammar: `STR_LIT`.
+
+String literals can be delimited by matching double quotes, and can
+contain the following `escape sequences`:idx:\ :
+
+==================         ===================================================
+  Escape sequence          Meaning
+==================         ===================================================
+  ``\p``                   platform specific newline: CRLF on Windows,
+                           LF on Unix
+  ``\r``, ``\c``           `carriage return`:idx:
+  ``\n``, ``\l``           `line feed`:idx: (often called `newline`:idx:)
+  ``\f``                   `form feed`:idx:
+  ``\t``                   `tabulator`:idx:
+  ``\v``                   `vertical tabulator`:idx:
+  ``\\``                   `backslash`:idx:
+  ``\"``                   `quotation mark`:idx:
+  ``\'``                   `apostrophe`:idx:
+  ``\`` '0'..'9'+          `character with decimal value d`:idx:;
+                           all decimal digits directly
+                           following are used for the character
+  ``\a``                   `alert`:idx:
+  ``\b``                   `backspace`:idx:
+  ``\e``                   `escape`:idx: `[ESC]`:idx:
+  ``\x`` HH                `character with hex value HH`:idx:;
+                           exactly two hex digits are allowed
+  ``\u`` HHHH              `unicode codepoint with hex value HHHH`:idx:;
+                           exactly four hex digits are allowed
+  ``\u`` {H+}              `unicode codepoint`:idx:;
+                           all hex digits enclosed in `{}` are used for
+                           the codepoint
+==================         ===================================================
+
+
+Strings in Nim may contain any 8-bit value, even embedded zeros. However,
+some operations may interpret the first binary zero as a terminator.
+
+
+Triple quoted string literals
+-----------------------------
+
+Terminal symbol in the grammar: `TRIPLESTR_LIT`.
+
+String literals can also be delimited by three double quotes `"""` ... `"""`.
+Literals in this form may run for several lines, may contain `"` and do not
+interpret any escape sequences.
+For convenience, when the opening `"""` is followed by a newline (there may
+be whitespace between the opening `"""` and the newline),
+the newline (and the preceding whitespace) is not included in the string. The
+ending of the string literal is defined by the pattern `"""[^"]`, so this:
+
+  ```nim
+  """"long string within quotes""""
+  ```
+
+Produces:
+
+    "long string within quotes"
+
+
+Raw string literals
+-------------------
+
+Terminal symbol in the grammar: `RSTR_LIT`.
+
+There are also raw string literals that are preceded with the
+letter `r` (or `R`) and are delimited by matching double quotes (just
+like ordinary string literals) and do not interpret the escape sequences.
+This is especially convenient for regular expressions or Windows paths:
+
+  ```nim
+  var f = openFile(r"C:\texts\text.txt") # a raw string, so ``\t`` is no tab
+  ```
+
+To produce a single `"` within a raw string literal, it has to be doubled:
+
+  ```nim
+  r"a""b"
+  ```
+
+Produces:
+
+    a"b
+
+`r""""` is not possible with this notation, because the three leading
+quotes introduce a triple quoted string literal. `r"""` is the same
+as `"""` since triple quoted string literals do not interpret escape
+sequences either.
+
+
+Generalized raw string literals
+-------------------------------
+
+Terminal symbols in the grammar: `GENERALIZED_STR_LIT`,
+`GENERALIZED_TRIPLESTR_LIT`.
+
+The construct `identifier"string literal"` (without whitespace between the
+identifier and the opening quotation mark) is a
+generalized raw string literal. It is a shortcut for the construct
+`identifier(r"string literal")`, so it denotes a routine call with a
+raw string literal as its only argument. Generalized raw string literals
+are especially convenient for embedding mini languages directly into Nim
+(for example regular expressions).
+
+The construct `identifier"""string literal"""` exists too. It is a shortcut
+for `identifier("""string literal""")`.
+
+
+Character literals
+------------------
+
+Character literals are enclosed in single quotes `''` and can contain the
+same escape sequences as strings - with one exception: the platform
+dependent `newline`:idx: (``\p``)
+is not allowed as it may be wider than one character (it can be the pair
+CR/LF). Here are the valid `escape sequences`:idx: for character
+literals:
+
+==================         ===================================================
+  Escape sequence          Meaning
+==================         ===================================================
+  ``\r``, ``\c``           `carriage return`:idx:
+  ``\n``, ``\l``           `line feed`:idx:
+  ``\f``                   `form feed`:idx:
+  ``\t``                   `tabulator`:idx:
+  ``\v``                   `vertical tabulator`:idx:
+  ``\\``                   `backslash`:idx:
+  ``\"``                   `quotation mark`:idx:
+  ``\'``                   `apostrophe`:idx:
+  ``\`` '0'..'9'+          `character with decimal value d`:idx:;
+                           all decimal digits directly
+                           following are used for the character
+  ``\a``                   `alert`:idx:
+  ``\b``                   `backspace`:idx:
+  ``\e``                   `escape`:idx: `[ESC]`:idx:
+  ``\x`` HH                `character with hex value HH`:idx:;
+                           exactly two hex digits are allowed
+==================         ===================================================
+
+A character is not a Unicode character but a single byte.
+
+Rationale: It enables the efficient support of `array[char, int]` or
+`set[char]`.
+
+The `Rune` type can represent any Unicode character.
+`Rune` is declared in the [unicode module](unicode.html).
+
+A character literal that does not end in `'` is interpreted as `'` if there
+is a preceding backtick token. There must be no whitespace between the preceding
+backtick token and the character literal. This special case ensures that a declaration
+like ``proc `'customLiteral`(s: string)`` is valid. ``proc `'customLiteral`(s: string)``
+is the same as ``proc `'\''customLiteral`(s: string)``.
+
+See also [custom numeric literals].
+
+
+Numeric literals
+----------------
+
+Numeric literals have the form:
+
+    hexdigit = digit | 'A'..'F' | 'a'..'f'
+    octdigit = '0'..'7'
+    bindigit = '0'..'1'
+    unary_minus = '-' # See the section about unary minus
+    HEX_LIT = unary_minus? '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )*
+    DEC_LIT = unary_minus? digit ( ['_'] digit )*
+    OCT_LIT = unary_minus? '0' 'o' octdigit ( ['_'] octdigit )*
+    BIN_LIT = unary_minus? '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*
+
+    INT_LIT = HEX_LIT
+            | DEC_LIT
+            | OCT_LIT
+            | BIN_LIT
+
+    INT8_LIT = INT_LIT ['\''] ('i' | 'I') '8'
+    INT16_LIT = INT_LIT ['\''] ('i' | 'I') '16'
+    INT32_LIT = INT_LIT ['\''] ('i' | 'I') '32'
+    INT64_LIT = INT_LIT ['\''] ('i' | 'I') '64'
+
+    UINT_LIT = INT_LIT ['\''] ('u' | 'U')
+    UINT8_LIT = INT_LIT ['\''] ('u' | 'U') '8'
+    UINT16_LIT = INT_LIT ['\''] ('u' | 'U') '16'
+    UINT32_LIT = INT_LIT ['\''] ('u' | 'U') '32'
+    UINT64_LIT = INT_LIT ['\''] ('u' | 'U') '64'
+
+    exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )*
+    FLOAT_LIT = unary_minus? digit (['_'] digit)* (('.' digit (['_'] digit)* [exponent]) |exponent)
+    FLOAT32_SUFFIX = ('f' | 'F') ['32']
+    FLOAT32_LIT = HEX_LIT '\'' FLOAT32_SUFFIX
+                | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT32_SUFFIX
+    FLOAT64_SUFFIX = ( ('f' | 'F') '64' ) | 'd' | 'D'
+    FLOAT64_LIT = HEX_LIT '\'' FLOAT64_SUFFIX
+                | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT64_SUFFIX
+
+    CUSTOM_NUMERIC_LIT = (FLOAT_LIT | INT_LIT) '\'' CUSTOM_NUMERIC_SUFFIX
+
+    # CUSTOM_NUMERIC_SUFFIX is any Nim identifier that is not
+    # a pre-defined type suffix.
+
+
+As can be seen in the productions, numeric literals can contain underscores
+for readability. Integer and floating-point literals may be given in decimal (no
+prefix), binary (prefix `0b`), octal (prefix `0o`), and hexadecimal
+(prefix `0x`) notation.
+
+The fact that the unary minus `-` in a number literal like `-1` is considered
+to be part of the literal is a late addition to the language. The rationale is that
+an expression `-128'i8` should be valid and without this special case, this would
+be impossible -- `128` is not a valid `int8` value, only `-128` is.
+
+For the `unary_minus` rule there are further restrictions that are not covered
+in the formal grammar. For `-` to be part of the number literal the immediately
+preceding character has to be in the
+set `{' ', '\t', '\n', '\r', ',', ';', '(', '[', '{'}`. This set was designed to
+cover most cases in a natural manner.
+
+In the following examples, `-1` is a single token:
+
+  ```nim
+  echo -1
+  echo(-1)
+  echo [-1]
+  echo 3,-1
+
+  "abc";-1
+  ```
+
+In the following examples, `-1` is parsed as two separate tokens
+(as `-`:tok: `1`:tok:):
+
+  ```nim
+  echo x-1
+  echo (int)-1
+  echo [a]-1
+  "abc"-1
+  ```
+
+
+The suffix starting with an apostrophe ('\'') is called a
+`type suffix`:idx:. Literals without a type suffix are of an integer type
+unless the literal contains a dot or `E|e` in which case it is of
+type `float`. This integer type is `int` if the literal is in the range
+`low(int32)..high(int32)`, otherwise it is `int64`.
+For notational convenience, the apostrophe of a type suffix
+is optional if it is not ambiguous (only hexadecimal floating-point literals
+with a type suffix can be ambiguous).
+
+
+The pre-defined type suffixes are:
+
+=================    =========================
+  Type Suffix        Resulting type of literal
+=================    =========================
+  `'i8`              int8
+  `'i16`             int16
+  `'i32`             int32
+  `'i64`             int64
+  `'u`               uint
+  `'u8`              uint8
+  `'u16`             uint16
+  `'u32`             uint32
+  `'u64`             uint64
+  `'f`               float32
+  `'d`               float64
+  `'f32`             float32
+  `'f64`             float64
+=================    =========================
+
+Floating-point literals may also be in binary, octal or hexadecimal
+notation:
+`0B0_10001110100_0000101001000111101011101111111011000101001101001001'f64`
+is approximately 1.72826e35 according to the IEEE floating-point standard.
+
+Literals must match the datatype, for example, `333'i8` is an invalid literal.
+Non-base-10 literals are used mainly for flags and bit pattern representations,
+therefore the checking is done on bit width and not on value range.
+Hence: 0b10000000'u8 == 0x80'u8 == 128, but, 0b10000000'i8 == 0x80'i8 == -1
+instead of causing an overflow error.
+
+
+### Custom numeric literals
+
+If the suffix is not predefined, then the suffix is assumed to be a call
+to a proc, template, macro or other callable identifier that is passed the
+string containing the literal. The callable identifier needs to be declared
+with a special ``'`` prefix:
+
+  ```nim
+  import std/strutils
+  type u4 = distinct uint8 # a 4-bit unsigned integer aka "nibble"
+  proc `'u4`(n: string): u4 =
+    # The leading ' is required.
+    result = (parseInt(n) and 0x0F).u4
+
+  var x = 5'u4
+  ```
+
+More formally, a custom numeric literal `123'custom` is transformed
+to r"123".`'custom` in the parsing step. There is no AST node kind that
+corresponds to this transformation. The transformation naturally handles
+the case that additional parameters are passed to the callee:
+
+  ```nim
+  import std/strutils
+  type u4 = distinct uint8 # a 4-bit unsigned integer aka "nibble"
+  proc `'u4`(n: string; moreData: int): u4 =
+    result = (parseInt(n) and 0x0F).u4
+
+  var x = 5'u4(123)
+  ```
+
+Custom numeric literals are covered by the grammar rule named `CUSTOM_NUMERIC_LIT`.
+A custom numeric literal is a single token.
+
+
+Operators
+---------
+
+Nim allows user defined operators. An operator is any combination of the
+following characters:
+
+       =     +     -     *     /     <     >
+       @     $     ~     &     %     |
+       !     ?     ^     .     :     \
+
+(The grammar uses the terminal OPR to refer to operator symbols as
+defined here.)
+
+These keywords are also operators:
+`and or not xor shl shr div mod in notin is isnot of as from`.
+
+`.`:tok:, `=`:tok:, `:`:tok:, `::`:tok: are not available as general operators; they
+are used for other notational purposes.
+
+`*:` is as a special case treated as the two tokens `*`:tok: and `:`:tok:
+(to support `var v*: T`).
+
+The `not` keyword is always a unary operator, `a not b` is parsed
+as `a(not b)`, not as `(a) not (b)`.
+
+Unicode Operators
+-----------------
+
+These Unicode operators are also parsed as operators:
+
+    ∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓   # same priority as * (multiplication)
+    ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔             # same priority as + (addition)
+
+
+Unicode operators can be combined with non-Unicode operator
+symbols. The usual precedence extensions then apply, for example, `⊠=` is an
+assignment like operator just like `*=` is.
+
+No Unicode normalization step is performed.
+
+
+Other tokens
+------------
+
+The following strings denote other tokens:
+
+    `   (    )     {    }     [    ]    ,  ;   [.    .]  {.   .}  (.  .)  [:
+
+
+The `slice`:idx: operator `..`:tok: takes precedence over other tokens that
+contain a dot: `{..}` are the three tokens `{`:tok:, `..`:tok:, `}`:tok:
+and not the two tokens `{.`:tok:, `.}`:tok:.
+
+
+Syntax
+======
+
+This section lists Nim's standard syntax. How the parser handles
+the indentation is already described in the [Lexical Analysis] section.
+
+Nim allows user-definable operators.
+Binary operators have 11 different levels of precedence.
+
+
+
+Associativity
+-------------
+
+Binary operators whose first character is `^` are right-associative, all
+other binary operators are left-associative.
+
+  ```nim
+  proc `^/`(x, y: float): float =
+    # a right-associative division operator
+    result = x / y
+  echo 12 ^/ 4 ^/ 8 # 24.0 (4 / 8 = 0.5, then 12 / 0.5 = 24.0)
+  echo 12  / 4  / 8 # 0.375 (12 / 4 = 3.0, then 3 / 8 = 0.375)
+  ```
+
+Precedence
+----------
+
+Unary operators always bind stronger than any binary
+operator: `$a + b` is `($a) + b` and not `$(a + b)`.
+
+If a unary operator's first character is `@` it is a `sigil-like`:idx:
+operator which binds stronger than a `primarySuffix`: `@x.abc` is parsed
+as `(@x).abc` whereas `$x.abc` is parsed as `$(x.abc)`.
+
+
+For binary operators that are not keywords, the precedence is determined by the
+following rules:
+
+Operators ending in either `->`, `~>` or `=>` are called
+`arrow like`:idx:, and have the lowest precedence of all operators.
+
+If the operator ends with `=` and its first character is none of
+`<`, `>`, `!`, `=`, `~`, `?`, it is an *assignment operator* which
+has the second-lowest precedence.
+
+Otherwise, precedence is determined by the first character.
+
+
+================  =======================================================  ==================  ===============
+Precedence level    Operators                                              First character     Terminal symbol
+================  =======================================================  ==================  ===============
+ 10 (highest)                                                              `$  ^`              OP10
+  9               `*    /    div   mod   shl  shr  %`                      `*  %  \  /`        OP9
+  8               `+    -`                                                 `+  -  ~  |`        OP8
+  7               `&`                                                      `&`                 OP7
+  6               `..`                                                     `.`                 OP6
+  5               `==  <= < >= > !=  in notin is isnot not of as from`     `=  <  >  !`        OP5
+  4               `and`                                                                        OP4
+  3               `or xor`                                                                     OP3
+  2                                                                        `@  :  ?`           OP2
+  1               *assignment operator* (like `+=`, `*=`)                                      OP1
+  0 (lowest)      *arrow like operator* (like `->`, `=>`)                                      OP0
+================  =======================================================  ==================  ===============
+
+
+Whether an operator is used as a prefix operator is also affected by preceding
+whitespace (this parsing change was introduced with version 0.13.0):
+
+  ```nim
+  echo $foo
+  # is parsed as
+  echo($foo)
+  ```
+
+
+Spacing also determines whether `(a, b)` is parsed as an argument list
+of a call or whether it is parsed as a tuple constructor:
+
+  ```nim
+  echo(1, 2) # pass 1 and 2 to echo
+  ```
+
+  ```nim
+  echo (1, 2) # pass the tuple (1, 2) to echo
+  ```
+
+Dot-like operators
+------------------
+
+Terminal symbol in the grammar: `DOTLIKEOP`.
+
+Dot-like operators are operators starting with `.`, but not with `..`, for e.g. `.?`;
+they have the same precedence as `.`, so that `a.?b.c` is parsed as `(a.?b).c` instead of `a.?(b.c)`.
+
+
+Grammar
+-------
+
+The grammar's start symbol is `module`.
+
+.. include:: grammar.txt
+   :literal:
+
+
+
+Order of evaluation
+===================
+
+Order of evaluation is strictly left-to-right, inside-out as it is typical for most others
+imperative programming languages:
+
+  ```nim  test = "nim c $1"
+  var s = ""
+
+  proc p(arg: int): int =
+    s.add $arg
+    result = arg
+
+  discard p(p(1) + p(2))
+
+  doAssert s == "123"
+  ```
+
+
+Assignments are not special, the left-hand-side expression is evaluated before the
+right-hand side:
+
+  ```nim  test = "nim c $1"
+  var v = 0
+  proc getI(): int =
+    result = v
+    inc v
+
+  var a, b: array[0..2, int]
+
+  proc someCopy(a: var int; b: int) = a = b
+
+  a[getI()] = getI()
+
+  doAssert a == [1, 0, 0]
+
+  v = 0
+  someCopy(b[getI()], getI())
+
+  doAssert b == [1, 0, 0]
+  ```
+
+
+Rationale: Consistency with overloaded assignment or assignment-like operations,
+`a = b` can be read as `performSomeCopy(a, b)`.
+
+
+However, the concept of "order of evaluation" is only applicable after the code
+was normalized: The normalization involves template expansions and argument
+reorderings that have been passed to named parameters:
+
+  ```nim  test = "nim c $1"
+  var s = ""
+
+  proc p(): int =
+    s.add "p"
+    result = 5
+
+  proc q(): int =
+    s.add "q"
+    result = 3
+
+  # Evaluation order is 'b' before 'a' due to template
+  # expansion's semantics.
+  template swapArgs(a, b): untyped =
+    b + a
+
+  doAssert swapArgs(p() + q(), q() - p()) == 6
+  doAssert s == "qppq"
+
+  # Evaluation order is not influenced by named parameters:
+  proc construct(first, second: int) =
+    discard
+
+  # 'p' is evaluated before 'q'!
+  construct(second = q(), first = p())
+
+  doAssert s == "qppqpq"
+  ```
+
+
+Rationale: This is far easier to implement than hypothetical alternatives.
+
+
+Constants and Constant Expressions
+==================================
+
+A `constant`:idx: is a symbol that is bound to the value of a constant
+expression. Constant expressions are restricted to depend only on the following
+categories of values and operations, because these are either built into the
+language or declared and evaluated before semantic analysis of the constant
+expression:
+
+* literals
+* built-in operators
+* previously declared constants and compile-time variables
+* previously declared macros and templates
+* previously declared procedures that have no side effects beyond
+  possibly modifying compile-time variables
+
+A constant expression can contain code blocks that may internally use all Nim
+features supported at compile time (as detailed in the next section below).
+Within such a code block, it is possible to declare variables and then later
+read and update them, or declare variables and pass them to procedures that
+modify them. However, the code in such a block must still adhere to the
+restrictions listed above for referencing values and operations outside the
+block.
+
+The ability to access and modify compile-time variables adds flexibility to
+constant expressions that may be surprising to those coming from other
+statically typed languages. For example, the following code echoes the beginning
+of the Fibonacci series **at compile-time**. (This is a demonstration of
+flexibility in defining constants, not a recommended style for solving this
+problem.)
+
+  ```nim  test = "nim c $1"
+  import std/strformat
+
+  var fibN {.compileTime.}: int
+  var fibPrev {.compileTime.}: int
+  var fibPrevPrev {.compileTime.}: int
+
+  proc nextFib(): int =
+    result = if fibN < 2:
+      fibN
+    else:
+      fibPrevPrev + fibPrev
+    inc(fibN)
+    fibPrevPrev = fibPrev
+    fibPrev = result
+
+  const f0 = nextFib()
+  const f1 = nextFib()
+
+  const displayFib = block:
+    const f2 = nextFib()
+    var result = fmt"Fibonacci sequence: {f0}, {f1}, {f2}"
+    for i in 3..12:
+      add(result, fmt", {nextFib()}")
+    result
+
+  static:
+    echo displayFib
+  ```
+
+
+Restrictions on Compile-Time Execution
+======================================
+
+Nim code that will be executed at compile time cannot use the following
+language features:
+
+* methods
+* closure iterators
+* the `cast` operator
+* reference (pointer) types
+* FFI
+
+The use of wrappers that use FFI and/or `cast` is also disallowed. Note that
+these wrappers include the ones in the standard libraries.
+
+Some or all of these restrictions are likely to be lifted over time.
+
+
+Types
+=====
+
+All expressions have a type that is known during semantic analysis. Nim
+is statically typed. One can declare new types, which is in essence defining
+an identifier that can be used to denote this custom type.
+
+These are the major type classes:
+
+* ordinal types (consist of integer, bool, character, enumeration
+  (and subranges thereof) types)
+* floating-point types
+* string type
+* structured types
+* reference (pointer) type
+* procedural type
+* generic type
+
+
+Ordinal types
+-------------
+Ordinal types have the following characteristics:
+
+- Ordinal types are countable and ordered. This property allows the operation
+  of functions such as `inc`, `ord`, and `dec` on ordinal types to
+  be defined.
+- Ordinal types have a smallest possible value, accessible with `low(type)`.
+  Trying to count further down than the smallest value produces a panic or
+  a static error.
+- Ordinal types have a largest possible value, accessible with `high(type)`.
+  Trying to count further up than the largest value produces a panic or
+  a static error.
+
+Integers, bool, characters, and enumeration types (and subranges of these
+types) belong to ordinal types.
+
+A distinct type is an ordinal type if its base type is an ordinal type.
+
+
+Pre-defined integer types
+-------------------------
+These integer types are pre-defined:
+
+`int`
+: the generic signed integer type; its size is platform-dependent and has the
+  same size as a pointer. This type should be used in general. An integer
+  literal that has no type suffix is of this type if it is in the range
+  `low(int32)..high(int32)` otherwise the literal's type is `int64`.
+
+`int`\ XX
+: additional signed integer types of XX bits use this naming scheme
+  (example: int16 is a 16-bit wide integer).
+  The current implementation supports `int8`, `int16`, `int32`, `int64`.
+  Literals of these types have the suffix 'iXX.
+
+`uint`
+: the generic `unsigned integer`:idx: type; its size is platform-dependent and
+  has the same size as a pointer. An integer literal with the type
+  suffix `'u` is of this type.
+
+`uint`\ XX
+: additional unsigned integer types of XX bits use this naming scheme
+  (example: uint16 is a 16-bit wide unsigned integer).
+  The current implementation supports `uint8`, `uint16`, `uint32`,
+  `uint64`. Literals of these types have the suffix 'uXX.
+  Unsigned operations all wrap around; they cannot lead to over- or
+  underflow errors.
+
+
+In addition to the usual arithmetic operators for signed and unsigned integers
+(`+ - *` etc.) there are also operators that formally work on *signed*
+integers but treat their arguments as *unsigned*: They are mostly provided
+for backwards compatibility with older versions of the language that lacked
+unsigned integer types. These unsigned operations for signed integers use
+the `%` suffix as convention:
+
+
+======================   ======================================================
+operation                meaning
+======================   ======================================================
+`a +% b`                 unsigned integer addition
+`a -% b`                 unsigned integer subtraction
+`a *% b`                 unsigned integer multiplication
+`a /% b`                 unsigned integer division
+`a %% b`                 unsigned integer modulo operation
+`a <% b`                 treat `a` and `b` as unsigned and compare
+`a <=% b`                treat `a` and `b` as unsigned and compare
+======================   ======================================================
+
+`Automatic type conversion`:idx: is performed in expressions where different
+kinds of integer types are used: the smaller type is converted to the larger.
+
+A `narrowing type conversion`:idx: converts a larger to a smaller type (for
+example `int32 -> int16`). A `widening type conversion`:idx: converts a
+smaller type to a larger type (for example `int16 -> int32`). In Nim only
+widening type conversions are *implicit*:
+
+  ```nim
+  var myInt16 = 5i16
+  var myInt: int
+  myInt16 + 34     # of type `int16`
+  myInt16 + myInt  # of type `int`
+  myInt16 + 2i32   # of type `int32`
+  ```
+
+However, `int` literals are implicitly convertible to a smaller integer type
+if the literal's value fits this smaller type and such a conversion is less
+expensive than other implicit conversions, so `myInt16 + 34` produces
+an `int16` result.
+
+For further details, see [Convertible relation].
+
+
+Subrange types
+--------------
+A subrange type is a range of values from an ordinal or floating-point type (the base
+type). To define a subrange type, one must specify its limiting values -- the
+lowest and highest value of the type. For example:
+
+  ```nim
+  type
+    Subrange = range[0..5]
+    PositiveFloat = range[0.0..Inf]
+    Positive* = range[1..high(int)] # as defined in `system`
+  ```
+
+
+`Subrange` is a subrange of an integer which can only hold the values 0
+to 5. `PositiveFloat` defines a subrange of all positive floating-point values.
+NaN does not belong to any subrange of floating-point types.
+Assigning any other value to a variable of type `Subrange` is a
+panic (or a static error if it can be determined during
+semantic analysis). Assignments from the base type to one of its subrange types
+(and vice versa) are allowed.
+
+A subrange type has the same size as its base type (`int` in the
+Subrange example).
+
+
+Pre-defined floating-point types
+--------------------------------
+
+The following floating-point types are pre-defined:
+
+`float`
+: the generic floating-point type; its size used to be platform-dependent,
+  but now it is always mapped to `float64`.
+  This type should be used in general.
+
+`float`\ XX
+: an implementation may define additional floating-point types of XX bits using
+  this naming scheme (example: `float64` is a 64-bit wide float). The current
+  implementation supports `float32` and `float64`. Literals of these types
+  have the suffix 'fXX.
+
+
+Automatic type conversion in expressions with different kinds of floating-point
+types is performed: See [Convertible relation] for further details. Arithmetic
+performed on floating-point types follows the IEEE standard. Integer types are
+not converted to floating-point types automatically and vice versa.
+
+The IEEE standard defines five types of floating-point exceptions:
+
+* Invalid: operations with mathematically invalid operands,
+  for example 0.0/0.0, sqrt(-1.0), and log(-37.8).
+* Division by zero: divisor is zero and dividend is a finite nonzero number,
+  for example 1.0/0.0.
+* Overflow: operation produces a result that exceeds the range of the exponent,
+  for example MAXDOUBLE+0.0000000000001e308.
+* Underflow: operation produces a result that is too small to be represented
+  as a normal number, for example, MINDOUBLE * MINDOUBLE.
+* Inexact: operation produces a result that cannot be represented with infinite
+  precision, for example, 2.0 / 3.0, log(1.1) and 0.1 in input.
+
+The IEEE exceptions are either ignored during execution or mapped to the
+Nim exceptions: `FloatInvalidOpDefect`:idx:, `FloatDivByZeroDefect`:idx:,
+`FloatOverflowDefect`:idx:, `FloatUnderflowDefect`:idx:,
+and `FloatInexactDefect`:idx:.
+These exceptions inherit from the `FloatingPointDefect`:idx: base class.
+
+Nim provides the pragmas `nanChecks`:idx: and `infChecks`:idx: to control
+whether the IEEE exceptions are ignored or trap a Nim exception:
+
+  ```nim
+  {.nanChecks: on, infChecks: on.}
+  var a = 1.0
+  var b = 0.0
+  echo b / b # raises FloatInvalidOpDefect
+  echo a / b # raises FloatOverflowDefect
+  ```
+
+In the current implementation `FloatDivByZeroDefect` and `FloatInexactDefect`
+are never raised. `FloatOverflowDefect` is raised instead of
+`FloatDivByZeroDefect`.
+There is also a `floatChecks`:idx: pragma that is a short-cut for the
+combination of `nanChecks` and `infChecks` pragmas. `floatChecks` are
+turned off as default.
+
+The only operations that are affected by the `floatChecks` pragma are
+the `+`, `-`, `*`, `/` operators for floating-point types.
+
+An implementation should always use the maximum precision available to evaluate
+floating-point values during semantic analysis; this means expressions like
+`0.09'f32 + 0.01'f32 == 0.09'f64 + 0.01'f64` that are evaluating during
+constant folding are true.
+
+
+Boolean type
+------------
+The boolean type is named `bool`:idx: in Nim and can be one of the two
+pre-defined values `true` and `false`. Conditions in `while`,
+`if`, `elif`, `when`-statements need to be of type `bool`.
+
+This condition holds:
+
+  ```nim
+  ord(false) == 0 and ord(true) == 1
+  ```
+
+The operators `not, and, or, xor, <, <=, >, >=, !=, ==` are defined
+for the bool type. The `and` and `or` operators perform short-cut
+evaluation. Example:
+
+  ```nim
+  while p != nil and p.name != "xyz":
+    # p.name is not evaluated if p == nil
+    p = p.next
+  ```
+
+
+The size of the bool type is one byte.
+
+
+Character type
+--------------
+The character type is named `char` in Nim. Its size is one byte.
+Thus, it cannot represent a UTF-8 character, but a part of it.
+
+The `Rune` type is used for Unicode characters, it can represent any Unicode
+character. `Rune` is declared in the [unicode module](unicode.html).
+
+
+
+
+Enumeration types
+-----------------
+Enumeration types define a new type whose values consist of the ones
+specified. The values are ordered. Example:
+
+  ```nim
+  type
+    Direction = enum
+      north, east, south, west
+  ```
+
+
+Now the following holds:
+
+  ```nim
+  ord(north) == 0
+  ord(east) == 1
+  ord(south) == 2
+  ord(west) == 3
+
+  # Also allowed:
+  ord(Direction.west) == 3
+  ```
+
+The implied order is: north < east < south < west. The comparison operators can be used
+with enumeration types. Instead of `north` etc., the enum value can also
+be qualified with the enum type that it resides in, `Direction.north`.
+
+For better interfacing to other programming languages, the fields of enum
+types can be assigned an explicit ordinal value. However, the ordinal values
+have to be in ascending order. A field whose ordinal value is not
+explicitly given is assigned the value of the previous field + 1.
+
+An explicit ordered enum can have *holes*:
+
+  ```nim
+  type
+    TokenType = enum
+      a = 2, b = 4, c = 89 # holes are valid
+  ```
+
+However, it is then not ordinal anymore, so it is impossible to use these
+enums as an index type for arrays. The procedures `inc`, `dec`, `succ`
+and `pred` are not available for them either.
+
+
+The compiler supports the built-in stringify operator `$` for enumerations.
+The stringify's result can be controlled by explicitly giving the string
+values to use:
+
+  ```nim
+  type
+    MyEnum = enum
+      valueA = (0, "my value A"),
+      valueB = "value B",
+      valueC = 2,
+      valueD = (3, "abc")
+  ```
+
+As can be seen from the example, it is possible to both specify a field's
+ordinal value and its string value by using a tuple. It is also
+possible to only specify one of them.
+
+An enum can be marked with the `pure` pragma so that its fields are
+added to a special module-specific hidden scope that is only queried
+as the last attempt. Only non-ambiguous symbols are added to this scope.
+But one can always access these via type qualification written
+as `MyEnum.value`:
+
+  ```nim
+  type
+    MyEnum {.pure.} = enum
+      valueA, valueB, valueC, valueD, amb
+
+    OtherEnum {.pure.} = enum
+      valueX, valueY, valueZ, amb
+
+
+  echo valueA # MyEnum.valueA
+  echo amb    # Error: Unclear whether it's MyEnum.amb or OtherEnum.amb
+  echo MyEnum.amb # OK.
+  ```
+
+Enum value names are overloadable, much like routines. If both of the enums
+`T` and `U` have a member named `foo`, then the identifier `foo` corresponds
+to a choice between `T.foo` and `U.foo`. During overload resolution,
+the correct type of `foo` is decided from the context. If the type of `foo` is
+ambiguous, a static error will be produced.
+
+  ```nim  test = "nim c $1"
+
+  type
+    E1 = enum
+      value1,
+      value2
+    E2 = enum
+      value1,
+      value2 = 4
+
+  const
+    Lookuptable = [
+      E1.value1: "1",
+      # no need to qualify value2, known to be E1.value2
+      value2: "2"
+    ]
+
+  proc p(e: E1) =
+    # disambiguation in 'case' statements:
+    case e
+    of value1: echo "A"
+    of value2: echo "B"
+
+  p value2
+  ```
+
+In some cases, ambiguity of enums is resolved depending on the relation
+between the current scope and the scope the enums were defined in.
+
+  ```nim
+  # a.nim
+  type Foo* = enum abc
+
+  # b.nim
+  import a
+  type Bar = enum abc
+  echo abc is Bar # true
+
+  block:
+    type Baz = enum abc
+    echo abc is Baz # true
+  ```
+
+To implement bit fields with enums see [Bit fields].
+
+
+String type
+-----------
+All string literals are of the type `string`. A string in Nim is very
+similar to a sequence of characters. However, strings in Nim are both
+zero-terminated and have a length field. One can retrieve the length with the
+builtin `len` procedure; the length never counts the terminating zero.
+
+The terminating zero cannot be accessed unless the string is converted
+to the `cstring` type first. The terminating zero assures that this
+conversion can be done in O(1) and without any allocations.
+
+The assignment operator for strings always copies the string.
+The `&` operator concatenates strings.
+
+Most native Nim types support conversion to strings with the special `$` proc.
+When calling the `echo` proc, for example, the built-in stringify operation
+for the parameter is called:
+
+  ```nim
+  echo 3 # calls `$` for `int`
+  ```
+
+Whenever a user creates a specialized object, implementation of this procedure
+provides for `string` representation.
+
+  ```nim
+  type
+    Person = object
+      name: string
+      age: int
+
+  proc `$`(p: Person): string = # `$` always returns a string
+    result = p.name & " is " &
+            $p.age & # we *need* the `$` in front of p.age which
+                     # is natively an integer to convert it to
+                     # a string
+            " years old."
+  ```
+
+While `$p.name` can also be used, the `$` operation on a string does
+nothing. Note that we cannot rely on automatic conversion from an `int` to
+a `string` like we can for the `echo` proc.
+
+Strings are compared by their lexicographical order. All comparison operators
+are available. Strings can be indexed like arrays (lower bound is 0). Unlike
+arrays, they can be used in case statements:
+
+  ```nim
+  case paramStr(i)
+  of "-v": incl(options, optVerbose)
+  of "-h", "-?": incl(options, optHelp)
+  else: write(stdout, "invalid command line option!\n")
+  ```
+
+Per convention, all strings are UTF-8 strings, but this is not enforced. For
+example, when reading strings from binary files, they are merely a sequence of
+bytes. The index operation `s[i]` means the i-th *char* of `s`, not the
+i-th *unichar*. The iterator `runes` from the [unicode module](unicode.html)
+can be used for iteration over all Unicode characters.
+
+
+cstring type
+------------
+
+The `cstring` type meaning `compatible string` is the native representation
+of a string for the compilation backend. For the C backend the `cstring` type
+represents a pointer to a zero-terminated char array
+compatible with the type `char*` in ANSI C. Its primary purpose lies in easy
+interfacing with C. The index operation `s[i]` means the i-th *char* of
+`s`; however no bounds checking for `cstring` is performed making the
+index operation unsafe.
+
+A Nim `string` is implicitly convertible
+to `cstring` for convenience. If a Nim string is passed to a C-style
+variadic proc, it is implicitly converted to `cstring` too:
+
+  ```nim
+  proc printf(formatstr: cstring) {.importc: "printf", varargs,
+                                    header: "<stdio.h>".}
+
+  printf("This works %s", "as expected")
+  ```
+
+Even though the conversion is implicit, it is not *safe*: The garbage collector
+does not consider a `cstring` to be a root and may collect the underlying
+memory. For this reason, the implicit conversion will be removed in future
+releases of the Nim compiler. Certain idioms like conversion of a `const` string
+to `cstring` are safe and will remain to be allowed.
+
+A `$` proc is defined for cstrings that returns a string. Thus, to get a nim
+string from a cstring:
+
+  ```nim
+  var str: string = "Hello!"
+  var cstr: cstring = str
+  var newstr: string = $cstr
+  ```
+
+`cstring` literals shouldn't be modified.
+
+  ```nim
+  var x = cstring"literals"
+  x[1] = 'A' # This is wrong!!!
+  ```
+
+If the `cstring` originates from a regular memory (not read-only memory),
+it can be modified:
+
+  ```nim
+  var x = "123456"
+  prepareMutation(x) # call `prepareMutation` before modifying the strings
+  var s: cstring = cstring(x)
+  s[0] = 'u' # This is ok
+  ```
+
+`cstring` values may also be used in case statements like strings.
+
+Structured types
+----------------
+A variable of a structured type can hold multiple values at the same
+time. Structured types can be nested to unlimited levels. Arrays, sequences,
+tuples, objects, and sets belong to the structured types.
+
+Array and sequence types
+------------------------
+Arrays are a homogeneous type, meaning that each element in the array has the
+same type. Arrays always have a fixed length specified as a constant expression
+(except for open arrays). They can be indexed by any ordinal type.
+A parameter `A` may be an *open array*, in which case it is indexed by
+integers from 0 to `len(A)-1`. An array expression may be constructed by the
+array constructor `[]`. The element type of this array expression is
+inferred from the type of the first element. All other elements need to be
+implicitly convertible to this type.
+
+An array type can be defined using the `array[size, T]` syntax, or using
+`array[lo..hi, T]` for arrays that start at an index other than zero.
+
+Sequences are similar to arrays but of dynamic length which may change
+during runtime (like strings). Sequences are implemented as growable arrays,
+allocating pieces of memory as items are added. A sequence `S` is always
+indexed by integers from 0 to `len(S)-1` and its bounds are checked.
+Sequences can be constructed by the array constructor `[]` in conjunction
+with the array to sequence operator `@`. Another way to allocate space for a
+sequence is to call the built-in `newSeq` procedure.
+
+A sequence may be passed to a parameter that is of type *open array*.
+
+Example:
+
+  ```nim
+  type
+    IntArray = array[0..5, int] # an array that is indexed with 0..5
+    IntSeq = seq[int] # a sequence of integers
+  var
+    x: IntArray
+    y: IntSeq
+  x = [1, 2, 3, 4, 5, 6]  # [] is the array constructor
+  y = @[1, 2, 3, 4, 5, 6] # the @ turns the array into a sequence
+
+  let z = [1.0, 2, 3, 4] # the type of z is array[0..3, float]
+  ```
+
+The lower bound of an array or sequence may be received by the built-in proc
+`low()`, the higher bound by `high()`. The length may be
+received by `len()`. `low()` for a sequence or an open array always returns
+0, as this is the first valid index.
+One can append elements to a sequence with the `add()` proc or the `&`
+operator, and remove (and get) the last element of a sequence with the
+`pop()` proc.
+
+The notation `x[i]` can be used to access the i-th element of `x`.
+
+Arrays are always bounds checked (statically or at runtime). These
+checks can be disabled via pragmas or invoking the compiler with the
+`--boundChecks:off`:option: command-line switch.
+
+An array constructor can have explicit indexes for readability:
+
+  ```nim
+  type
+    Values = enum
+      valA, valB, valC
+
+  const
+    lookupTable = [
+      valA: "A",
+      valB: "B",
+      valC: "C"
+    ]
+  ```
+
+If an index is left out, `succ(lastIndex)` is used as the index
+value:
+
+  ```nim
+  type
+    Values = enum
+      valA, valB, valC, valD, valE
+
+  const
+    lookupTable = [
+      valA: "A",
+      "B",
+      valC: "C",
+      "D", "e"
+    ]
+  ```
+
+
+
+Open arrays
+-----------
+
+Often fixed size arrays turn out to be too inflexible; procedures should
+be able to deal with arrays of different sizes. The `openarray`:idx: type
+allows this; it can only be used for parameters. Open arrays are always
+indexed with an `int` starting at position 0. The `len`, `low`
+and `high` operations are available for open arrays too. Any array with
+a compatible base type can be passed to an open array parameter, the index
+type does not matter. In addition to arrays, sequences can also be passed
+to an open array parameter.
+
+The `openarray` type cannot be nested: multidimensional open arrays are not
+supported because this is seldom needed and cannot be done efficiently.
+
+  ```nim
+  proc testOpenArray(x: openArray[int]) = echo repr(x)
+
+  testOpenArray([1,2,3])  # array[]
+  testOpenArray(@[1,2,3]) # seq[]
+  ```
+
+Varargs
+-------
+
+A `varargs` parameter is an open array parameter that additionally
+allows a variable number of arguments to be passed to a procedure. The compiler
+converts the list of arguments to an array implicitly:
+
+  ```nim
+  proc myWriteln(f: File, a: varargs[string]) =
+    for s in items(a):
+      write(f, s)
+    write(f, "\n")
+
+  myWriteln(stdout, "abc", "def", "xyz")
+  # is transformed to:
+  myWriteln(stdout, ["abc", "def", "xyz"])
+  ```
+
+This transformation is only done if the `varargs` parameter is the
+last parameter in the procedure header. It is also possible to perform
+type conversions in this context:
+
+  ```nim
+  proc myWriteln(f: File, a: varargs[string, `$`]) =
+    for s in items(a):
+      write(f, s)
+    write(f, "\n")
+
+  myWriteln(stdout, 123, "abc", 4.0)
+  # is transformed to:
+  myWriteln(stdout, [$123, $"abc", $4.0])
+  ```
+
+In this example `$` is applied to any argument that is passed to the
+parameter `a`. (Note that `$` applied to strings is a nop.)
+
+Note that an explicit array constructor passed to a `varargs` parameter is
+not wrapped in another implicit array construction:
+
+  ```nim
+  proc takeV[T](a: varargs[T]) = discard
+
+  takeV([123, 2, 1]) # takeV's T is "int", not "array of int"
+  ```
+
+
+`varargs[typed]` is treated specially: It matches a variable list of arguments
+of arbitrary type but *always* constructs an implicit array. This is required
+so that the builtin `echo` proc does what is expected:
+
+  ```nim
+  proc echo*(x: varargs[typed, `$`]) {...}
+
+  echo @[1, 2, 3]
+  # prints "@[1, 2, 3]" and not "123"
+  ```
+
+
+Unchecked arrays
+----------------
+The `UncheckedArray[T]` type is a special kind of `array` where its bounds
+are not checked. This is often useful to implement customized flexibly sized
+arrays. Additionally, an unchecked array is translated into a C array of
+undetermined size:
+
+  ```nim
+  type
+    MySeq = object
+      len, cap: int
+      data: UncheckedArray[int]
+  ```
+
+Produces roughly this C code:
+
+  ```C
+  typedef struct {
+    NI len;
+    NI cap;
+    NI data[];
+  } MySeq;
+  ```
+
+The base type of the unchecked array may not contain any GC'ed memory but this
+is currently not checked.
+
+**Future directions**: GC'ed memory should be allowed in unchecked arrays and
+there should be an explicit annotation of how the GC is to determine the
+runtime size of the array.
+
+
+
+Tuples and object types
+-----------------------
+A variable of a tuple or object type is a heterogeneous storage
+container.
+A tuple or object defines various named *fields* of a type. A tuple also
+defines a lexicographic *order* of the fields. Tuples are meant to be
+heterogeneous storage types with few abstractions. The `()` syntax
+can be used to construct tuples. The order of the fields in the constructor
+must match the order of the tuple's definition. Different tuple-types are
+*equivalent* if they specify the same fields of the same type in the same
+order. The *names* of the fields also have to be the same.
+
+  ```nim
+  type
+    Person = tuple[name: string, age: int] # type representing a person:
+                                           # it consists of a name and an age.
+  var person: Person
+  person = (name: "Peter", age: 30)
+  assert person.name == "Peter"
+  # the same, but less readable:
+  person = ("Peter", 30)
+  assert person[0] == "Peter"
+  assert Person is (string, int)
+  assert (string, int) is Person
+  assert Person isnot tuple[other: string, age: int] # `other` is a different identifier
+  ```
+
+A tuple with one unnamed field can be constructed with the parentheses and a
+trailing comma:
+
+  ```nim
+  proc echoUnaryTuple(a: (int,)) =
+    echo a[0]
+
+  echoUnaryTuple (1,)
+  ```
+
+
+In fact, a trailing comma is allowed for every tuple construction.
+
+The implementation aligns the fields for the best access performance. The alignment
+is compatible with the way the C compiler does it.
+
+For consistency  with `object` declarations, tuples in a `type` section
+can also be defined with indentation instead of `[]`:
+
+  ```nim
+  type
+    Person = tuple   # type representing a person
+      name: string   # a person consists of a name
+      age: Natural   # and an age
+  ```
+
+Objects provide many features that tuples do not. Objects provide inheritance
+and the ability to hide fields from other modules. Objects with inheritance
+enabled have information about their type at runtime so that the `of` operator
+can be used to determine the object's type. The `of` operator is similar to
+the `instanceof` operator in Java.
+
+  ```nim
+  type
+    Person = object of RootObj
+      name*: string   # the * means that `name` is accessible from other modules
+      age: int        # no * means that the field is hidden
+
+    Student = ref object of Person # a student is a person
+      id: int                      # with an id field
+
+  var
+    student: Student
+    person: Person
+  assert(student of Student) # is true
+  assert(student of Person) # also true
+  ```
+
+Object fields that should be visible from outside the defining module have to
+be marked by `*`. In contrast to tuples, different object types are
+never *equivalent*, they are nominal types whereas tuples are structural.
+Objects that have no ancestor are implicitly `final` and thus have no hidden
+type information. One can use the `inheritable` pragma to
+introduce new object roots apart from `system.RootObj`.
+
+  ```nim
+  type
+    Person = object # example of a final object
+      name*: string
+      age: int
+
+    Student = ref object of Person # Error: inheritance only works with non-final objects
+      id: int
+  ```
+
+The assignment operator for tuples and objects copies each component.
+The methods to override this copying behavior are described [here][type
+bound operators].
+
+
+Object construction
+-------------------
+
+Objects can also be created with an `object construction expression`:idx: that
+has the syntax `T(fieldA: valueA, fieldB: valueB, ...)` where `T` is
+an `object` type or a `ref object` type:
+
+  ```nim
+  type
+    Student = object
+      name: string
+      age: int
+    PStudent = ref Student
+  var a1 = Student(name: "Anton", age: 5)
+  var a2 = PStudent(name: "Anton", age: 5)
+  # this also works directly:
+  var a3 = (ref Student)(name: "Anton", age: 5)
+  # not all fields need to be mentioned, and they can be mentioned out of order:
+  var a4 = Student(age: 5)
+  ```
+
+Note that, unlike tuples, objects require the field names along with their values.
+For a `ref object` type `system.new` is invoked implicitly.
+
+
+Object variants
+---------------
+Often an object hierarchy is an overkill in certain situations where simple variant
+types are needed. Object variants are tagged unions discriminated via an
+enumerated type used for runtime type flexibility, mirroring the concepts of
+*sum types* and *algebraic data types (ADTs)* as found in other languages.
+
+An example:
+
+  ```nim
+  # This is an example of how an abstract syntax tree could be modelled in Nim
+  type
+    NodeKind = enum  # the different node types
+      nkInt,          # a leaf with an integer value
+      nkFloat,        # a leaf with a float value
+      nkString,       # a leaf with a string value
+      nkAdd,          # an addition
+      nkSub,          # a subtraction
+      nkIf            # an if statement
+    Node = ref NodeObj
+    NodeObj = object
+      case kind: NodeKind  # the `kind` field is the discriminator
+      of nkInt: intVal: int
+      of nkFloat: floatVal: float
+      of nkString: strVal: string
+      of nkAdd, nkSub:
+        leftOp, rightOp: Node
+      of nkIf:
+        condition, thenPart, elsePart: Node
+
+  # create a new case object:
+  var n = Node(kind: nkIf, condition: nil)
+  # accessing n.thenPart is valid because the `nkIf` branch is active:
+  n.thenPart = Node(kind: nkFloat, floatVal: 2.0)
+
+  # the following statement raises an `FieldDefect` exception, because
+  # n.kind's value does not fit and the `nkString` branch is not active:
+  n.strVal = ""
+
+  # invalid: would change the active object branch:
+  n.kind = nkInt
+
+  var x = Node(kind: nkAdd, leftOp: Node(kind: nkInt, intVal: 4),
+                            rightOp: Node(kind: nkInt, intVal: 2))
+  # valid: does not change the active object branch:
+  x.kind = nkSub
+  ```
+
+As can be seen from the example, an advantage to an object hierarchy is that
+no casting between different object types is needed. Yet, access to invalid
+object fields raises an exception.
+
+The syntax of `case` in an object declaration follows closely the syntax of
+the `case` statement: The branches in a `case` section may be indented too.
+
+In the example, the `kind` field is called the `discriminator`:idx:\: For
+safety, its address cannot be taken and assignments to it are restricted: The
+new value must not lead to a change of the active object branch. Also, when the
+fields of a particular branch are specified during object construction, the
+corresponding discriminator value must be specified as a constant expression.
+
+Instead of changing the active object branch, replace the old object in memory
+with a new one completely:
+
+  ```nim
+  var x = Node(kind: nkAdd, leftOp: Node(kind: nkInt, intVal: 4),
+                            rightOp: Node(kind: nkInt, intVal: 2))
+  # change the node's contents:
+  x[] = NodeObj(kind: nkString, strVal: "abc")
+  ```
+
+
+Starting with version 0.20 `system.reset` cannot be used anymore to support
+object branch changes as this never was completely memory safe.
+
+As a special rule, the discriminator kind can also be bounded using a `case`
+statement. If possible values of the discriminator variable in a
+`case` statement branch are a subset of discriminator values for the selected
+object branch, the initialization is considered valid. This analysis only works
+for immutable discriminators of an ordinal type and disregards `elif`
+branches. For discriminator values with a `range` type, the compiler
+checks if the entire range of possible values for the discriminator value is
+valid for the chosen object branch.
+
+A small example:
+
+  ```nim
+  let unknownKind = nkSub
+
+  # invalid: unsafe initialization because the kind field is not statically known:
+  var y = Node(kind: unknownKind, strVal: "y")
+
+  var z = Node()
+  case unknownKind
+  of nkAdd, nkSub:
+    # valid: possible values of this branch are a subset of nkAdd/nkSub object branch:
+    z = Node(kind: unknownKind, leftOp: Node(), rightOp: Node())
+  else:
+    echo "ignoring: ", unknownKind
+
+  # also valid, since unknownKindBounded can only contain the values nkAdd or nkSub
+  let unknownKindBounded = range[nkAdd..nkSub](unknownKind)
+  z = Node(kind: unknownKindBounded, leftOp: Node(), rightOp: Node())
+  ```
+
+
+cast uncheckedAssign
+--------------------
+
+Some restrictions for case objects can be disabled via a `{.cast(uncheckedAssign).}` section:
+
+  ```nim  test="nim c $1"
+  type
+    TokenKind* = enum
+      strLit, intLit
+    Token = object
+      case kind*: TokenKind
+      of strLit:
+        s*: string
+      of intLit:
+        i*: int64
+
+  proc passToVar(x: var TokenKind) = discard
+
+  var t = Token(kind: strLit, s: "abc")
+
+  {.cast(uncheckedAssign).}:
+    # inside the 'cast' section it is allowed to pass 't.kind' to a 'var T' parameter:
+    passToVar(t.kind)
+
+    # inside the 'cast' section it is allowed to set field 's' even though the
+    # constructed 'kind' field has an unknown value:
+    t = Token(kind: t.kind, s: "abc")
+
+    # inside the 'cast' section it is allowed to assign to the 't.kind' field directly:
+    t.kind = intLit
+  ```
+
+Default values for object fields
+--------------------------------
+
+Object fields are allowed to have a constant default value. The type of field can be omitted if a default value is given.
+
+```nim test
+type
+  Foo = object
+    a: int = 2
+    b: float = 3.14
+    c = "I can have a default value"
+
+  Bar = ref object
+    a: int = 2
+    b: float = 3.14
+    c = "I can have a default value"
+```
+
+The explicit initialization uses these defaults which includes an `object` created with an object construction expression or the procedure `default`; a `ref object` created with an object construction expression or the procedure `new`; an array or a tuple with a subtype which has a default created with the procedure `default`.
+
+
+```nim test
+type
+  Foo = object
+    a: int = 2
+    b = 3.0
+  Bar = ref object
+    a: int = 2
+    b = 3.0
+
+block: # created with an object construction expression
+  let x = Foo()
+  assert x.a == 2 and x.b == 3.0
+
+  let y = Bar()
+  assert y.a == 2 and y.b == 3.0
+
+block: # created with an object construction expression
+  let x = default(Foo)
+  assert x.a == 2 and x.b == 3.0
+
+  let y = default(array[1, Foo])
+  assert y[0].a == 2 and y[0].b == 3.0
+
+  let z = default(tuple[x: Foo])
+  assert z.x.a == 2 and z.x.b == 3.0
+
+block: # created with the procedure `new`
+  let y = new Bar
+  assert y.a == 2 and y.b == 3.0
+```
+
+Set type
+--------
+
+.. include:: sets_fragment.txt
+
+Reference and pointer types
+---------------------------
+References (similar to pointers in other programming languages) are a
+way to introduce many-to-one relationships. This means different references can
+point to and modify the same location in memory (also called `aliasing`:idx:).
+
+Nim distinguishes between `traced`:idx: and `untraced`:idx: references.
+Untraced references are also called *pointers*. Traced references point to
+objects of a garbage-collected heap, untraced references point to
+manually allocated objects or objects somewhere else in memory. Thus,
+untraced references are *unsafe*. However, for certain low-level operations
+(accessing the hardware) untraced references are unavoidable.
+
+Traced references are declared with the **ref** keyword, untraced references
+are declared with the **ptr** keyword. In general, a `ptr T` is implicitly
+convertible to the `pointer` type.
+
+An empty subscript `[]` notation can be used to de-refer a reference,
+the `addr` procedure returns the address of an item. An address is always
+an untraced reference.
+Thus, the usage of `addr` is an *unsafe* feature.
+
+The `.` (access a tuple/object field operator)
+and `[]` (array/string/sequence index operator) operators perform implicit
+dereferencing operations for reference types:
+
+  ```nim
+  type
+    Node = ref NodeObj
+    NodeObj = object
+      le, ri: Node
+      data: int
+
+  var
+    n: Node
+  new(n)
+  n.data = 9
+  # no need to write n[].data; in fact n[].data is highly discouraged!
+  ```
+
+In order to simplify structural type checking, recursive tuples are not valid:
+
+  ```nim
+  # invalid recursion
+  type MyTuple = tuple[a: ref MyTuple]
+  ```
+
+Likewise `T = ref T` is an invalid type.
+
+As a syntactical extension, `object` types can be anonymous if
+declared in a type section via the `ref object` or `ptr object` notations.
+This feature is useful if an object should only gain reference semantics:
+
+  ```nim
+  type
+    Node = ref object
+      le, ri: Node
+      data: int
+  ```
+
+
+To allocate a new traced object, the built-in procedure `new` has to be used.
+To deal with untraced memory, the procedures `alloc`, `dealloc` and
+`realloc` can be used. The documentation of the [system](system.html) module
+contains further information.
+
+
+Nil
+---
+
+If a reference points to *nothing*, it has the value `nil`. `nil` is the
+default value for all `ref` and `ptr` types. The `nil` value can also be
+used like any other literal value. For example, it can be used in an assignment
+like `myRef = nil`.
+
+Dereferencing `nil` is an unrecoverable fatal runtime error (and not a panic).
+
+A successful dereferencing operation `p[]` implies that `p` is not nil. This
+can be exploited by the implementation to optimize code like:
+
+  ```nim
+  p[].field = 3
+  if p != nil:
+    # if p were nil, `p[]` would have caused a crash already,
+    # so we know `p` is always not nil here.
+    action()
+  ```
+
+Into:
+
+  ```nim
+  p[].field = 3
+  action()
+  ```
+
+
+*Note*: This is not comparable to C's "undefined behavior" for
+dereferencing NULL pointers.
+
+
+Mixing GC'ed memory with `ptr`
+--------------------------------
+
+Special care has to be taken if an untraced object contains traced objects like
+traced references, strings, or sequences: in order to free everything properly,
+the built-in procedure `reset` has to be called before freeing the untraced
+memory manually:
+
+  ```nim
+  type
+    Data = tuple[x, y: int, s: string]
+
+  # allocate memory for Data on the heap:
+  var d = cast[ptr Data](alloc0(sizeof(Data)))
+
+  # create a new string on the garbage collected heap:
+  d.s = "abc"
+
+  # tell the GC that the string is not needed anymore:
+  reset(d.s)
+
+  # free the memory:
+  dealloc(d)
+  ```
+
+Without the `reset` call the memory allocated for the `d.s` string would
+never be freed. The example also demonstrates two important features for
+low-level programming: the `sizeof` proc returns the size of a type or value
+in bytes. The `cast` operator can circumvent the type system: the compiler
+is forced to treat the result of the `alloc0` call (which returns an untyped
+pointer) as if it would have the type `ptr Data`. Casting should only be
+done if it is unavoidable: it breaks type safety and bugs can lead to
+mysterious crashes.
+
+**Note**: The example only works because the memory is initialized to zero
+(`alloc0` instead of `alloc` does this): `d.s` is thus initialized to
+binary zero which the string assignment can handle. One needs to know low-level
+details like this when mixing garbage-collected data with unmanaged memory.
+
+.. XXX finalizers for traced objects
+
+
+Procedural type
+---------------
+A procedural type is internally a pointer to a procedure. `nil` is
+an allowed value for a variable of a procedural type.
+
+Examples:
+
+  ```nim
+  proc printItem(x: int) = ...
+
+  proc forEach(c: proc (x: int) {.cdecl.}) =
+    ...
+
+  forEach(printItem)  # this will NOT compile because calling conventions differ
+  ```
+
+
+  ```nim
+  type
+    OnMouseMove = proc (x, y: int) {.closure.}
+
+  proc onMouseMove(mouseX, mouseY: int) =
+    # has default calling convention
+    echo "x: ", mouseX, " y: ", mouseY
+
+  proc setOnMouseMove(mouseMoveEvent: OnMouseMove) = discard
+
+  # ok, 'onMouseMove' has the default calling convention, which is compatible
+  # to 'closure':
+  setOnMouseMove(onMouseMove)
+  ```
+
+
+A subtle issue with procedural types is that the calling convention of the
+procedure influences the type compatibility: procedural types are only
+compatible if they have the same calling convention. As a special extension,
+a procedure of the calling convention `nimcall` can be passed to a parameter
+that expects a proc of the calling convention `closure`.
+
+Nim supports these `calling conventions`:idx:\:
+
+`nimcall`:idx:
+:   is the default convention used for a Nim **proc**. It is the
+    same as `fastcall`, but only for C compilers that support `fastcall`.
+
+`closure`:idx:
+:   is the default calling convention for a **procedural type** that lacks
+    any pragma annotations. It indicates that the procedure has a hidden
+    implicit parameter (an *environment*). Proc vars that have the calling
+    convention `closure` take up two machine words: One for the proc pointer
+    and another one for the pointer to implicitly passed environment.
+
+`stdcall`:idx:
+:   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
+    the `__cdecl` keyword.
+
+`safecall`:idx:
+:   This is the safecall convention as specified by Microsoft. The generated C
+    procedure is declared with the `__safecall` keyword. The word *safe*
+    refers to the fact that all hardware registers shall be pushed to the
+    hardware stack.
+
+`inline`:idx:
+:   The inline convention means the caller should not call the procedure,
+    but inline its code directly. Note that Nim does not inline, but leaves
+    this to the C compiler; it generates `__inline` procedures. This is
+    only a hint for the compiler: it may completely ignore it, and
+    it may inline procedures that are not marked as `inline`.
+
+`fastcall`:idx:
+:   Fastcall means different things to different C compilers. One gets whatever
+    the C `__fastcall` means.
+
+`thiscall`:idx:
+:   This is the thiscall calling convention as specified by Microsoft, used on
+    C++ class member functions on the x86 architecture.
+
+`syscall`:idx:
+:   The syscall convention is the same as `__syscall`:c: in C. It is used for
+    interrupts.
+
+`noconv`:idx:
+:   The generated C code will not have any explicit calling convention and thus
+    use the C compiler's default calling convention. This is needed because
+    Nim's default calling convention for procedures is `fastcall` to
+    improve speed.
+
+Most calling conventions exist only for the Windows 32-bit platform.
+
+The default calling convention is `nimcall`, unless it is an inner proc (a
+proc inside of a proc). For an inner proc an analysis is performed whether it
+accesses its environment. If it does so, it has the calling convention
+`closure`, otherwise it has the calling convention `nimcall`.
+
+
+Distinct type
+-------------
+
+A `distinct` type is a new type derived from a `base type`:idx: that is
+incompatible with its base type. In particular, it is an essential property
+of a distinct type that it **does not** imply a subtype relation between it
+and its base type. Explicit type conversions from a distinct type to its
+base type and vice versa are allowed. See also `distinctBase` to get the
+reverse operation.
+
+A distinct type is an ordinal type if its base type is an ordinal type.
+
+
+### Modeling currencies
+
+A distinct type can be used to model different physical `units`:idx: with a
+numerical base type, for example. The following example models currencies.
+
+Different currencies should not be mixed in monetary calculations. Distinct
+types are a perfect tool to model different currencies:
+
+  ```nim
+  type
+    Dollar = distinct int
+    Euro = distinct int
+
+  var
+    d: Dollar
+    e: Euro
+
+  echo d + 12
+  # Error: cannot add a number with no unit and a `Dollar`
+  ```
+
+Unfortunately, `d + 12.Dollar` is not allowed either,
+because `+` is defined for `int` (among others), not for `Dollar`. So
+a `+` for dollars needs to be defined:
+
+  ```nim
+  proc `+` (x, y: Dollar): Dollar =
+    result = Dollar(int(x) + int(y))
+  ```
+
+It does not make sense to multiply a dollar with a dollar, but with a
+number without unit; and the same holds for division:
+
+  ```nim
+  proc `*` (x: Dollar, y: int): Dollar =
+    result = Dollar(int(x) * y)
+
+  proc `*` (x: int, y: Dollar): Dollar =
+    result = Dollar(x * int(y))
+
+  proc `div` ...
+  ```
+
+This quickly gets tedious. The implementations are trivial and the compiler
+should not generate all this code only to optimize it away later - after all
+`+` for dollars should produce the same binary code as `+` for ints.
+The pragma `borrow`:idx: has been designed to solve this problem; in principle,
+it generates the above trivial implementations:
+
+  ```nim
+  proc `*` (x: Dollar, y: int): Dollar {.borrow.}
+  proc `*` (x: int, y: Dollar): Dollar {.borrow.}
+  proc `div` (x: Dollar, y: int): Dollar {.borrow.}
+  ```
+
+The `borrow` pragma makes the compiler use the same implementation as
+the proc that deals with the distinct type's base type, so no code is
+generated.
+
+But it seems all this boilerplate code needs to be repeated for the `Euro`
+currency. This can be solved with [templates].
+
+  ```nim  test = "nim c $1"
+  template additive(typ: typedesc) =
+    proc `+` *(x, y: typ): typ {.borrow.}
+    proc `-` *(x, y: typ): typ {.borrow.}
+
+    # unary operators:
+    proc `+` *(x: typ): typ {.borrow.}
+    proc `-` *(x: typ): typ {.borrow.}
+
+  template multiplicative(typ, base: typedesc) =
+    proc `*` *(x: typ, y: base): typ {.borrow.}
+    proc `*` *(x: base, y: typ): typ {.borrow.}
+    proc `div` *(x: typ, y: base): typ {.borrow.}
+    proc `mod` *(x: typ, y: base): typ {.borrow.}
+
+  template comparable(typ: typedesc) =
+    proc `<` * (x, y: typ): bool {.borrow.}
+    proc `<=` * (x, y: typ): bool {.borrow.}
+    proc `==` * (x, y: typ): bool {.borrow.}
+
+  template defineCurrency(typ, base: untyped) =
+    type
+      typ* = distinct base
+    additive(typ)
+    multiplicative(typ, base)
+    comparable(typ)
+
+  defineCurrency(Dollar, int)
+  defineCurrency(Euro, int)
+  ```
+
+
+The borrow pragma can also be used to annotate the distinct type to allow
+certain builtin operations to be lifted:
+
+  ```nim
+  type
+    Foo = object
+      a, b: int
+      s: string
+
+    Bar {.borrow: `.`.} = distinct Foo
+
+  var bb: ref Bar
+  new bb
+  # field access now valid
+  bb.a = 90
+  bb.s = "abc"
+  ```
+
+Currently, only the dot accessor can be borrowed in this way.
+
+
+### Avoiding SQL injection attacks
+
+An SQL statement that is passed from Nim to an SQL database might be
+modeled as a string. However, using string templates and filling in the
+values is vulnerable to the famous `SQL injection attack`:idx:\:
+
+  ```nim
+  import std/strutils
+
+  proc query(db: DbHandle, statement: string) = ...
+
+  var
+    username: string
+
+  db.query("SELECT FROM users WHERE name = '$1'" % username)
+  # Horrible security hole, but the compiler does not mind!
+  ```
+
+This can be avoided by distinguishing strings that contain SQL from strings
+that don't. Distinct types provide a means to introduce a new string type
+`SQL` that is incompatible with `string`:
+
+  ```nim
+  type
+    SQL = distinct string
+
+  proc query(db: DbHandle, statement: SQL) = ...
+
+  var
+    username: string
+
+  db.query("SELECT FROM users WHERE name = '$1'" % username)
+  # Static error: `query` expects an SQL string!
+  ```
+
+
+It is an essential property of abstract types that they **do not** imply a
+subtype relation between the abstract type and its base type. Explicit type
+conversions from `string` to `SQL` are allowed:
+
+  ```nim
+  import std/[strutils, sequtils]
+
+  proc properQuote(s: string): SQL =
+    # quotes a string properly for an SQL statement
+    return SQL(s)
+
+  proc `%` (frmt: SQL, values: openarray[string]): SQL =
+    # quote each argument:
+    let v = values.mapIt(properQuote(it))
+    # we need a temporary type for the type conversion :-(
+    type StrSeq = seq[string]
+    # call strutils.`%`:
+    result = SQL(string(frmt) % StrSeq(v))
+
+  db.query("SELECT FROM users WHERE name = '$1'".SQL % [username])
+  ```
+
+Now we have compile-time checking against SQL injection attacks. Since
+`"".SQL` is transformed to `SQL("")` no new syntax is needed for nice
+looking `SQL` string literals. The hypothetical `SQL` type actually
+exists in the library as the [SqlQuery type](db_common.html#SqlQuery) of
+modules like [db_sqlite](db_sqlite.html).
+
+
+Auto type
+---------
+
+The `auto` type can only be used for return types and parameters. For return
+types it causes the compiler to infer the type from the routine body:
+
+  ```nim
+  proc returnsInt(): auto = 1984
+  ```
+
+For parameters it currently creates implicitly generic routines:
+
+  ```nim
+  proc foo(a, b: auto) = discard
+  ```
+
+Is the same as:
+
+  ```nim
+  proc foo[T1, T2](a: T1, b: T2) = discard
+  ```
+
+However, later versions of the language might change this to mean "infer the
+parameters' types from the body". Then the above `foo` would be rejected as
+the parameters' types can not be inferred from an empty `discard` statement.
+
+
+Type relations
+==============
+
+The following section defines several relations on types that are needed to
+describe the type checking done by the compiler.
+
+
+Type equality
+-------------
+
+Nim uses structural type equivalence for most types. Only for objects,
+enumerations and distinct types and for generic types name equivalence is used.
+
+
+Subtype relation
+----------------
+
+If object `a` inherits from `b`, `a` is a subtype of `b`.
+
+This subtype relation is extended to the types `var`, `ref`, `ptr`.
+If `A` is a subtype of `B` and `A` and `B` are `object` types then:
+
+- `var A` is a subtype of `var B`
+- `ref A` is a subtype of `ref B`
+- `ptr A` is a subtype of `ptr B`.
+
+**Note**: One of the above pointer-indirections is required for assignment from
+a subtype to its parent type to prevent "object slicing".
+
+
+Convertible relation
+--------------------
+
+A type `a` is **implicitly** convertible to type `b` iff the following
+algorithm returns true:
+
+  ```nim
+  proc isImplicitlyConvertible(a, b: PType): bool =
+    if isSubtype(a, b):
+      return true
+    if isIntLiteral(a):
+      return b in {int8, int16, int32, int64, int, uint, uint8, uint16,
+                   uint32, uint64, float32, float64}
+    case a.kind
+    of int:     result = b in {int32, int64}
+    of int8:    result = b in {int16, int32, int64, int}
+    of int16:   result = b in {int32, int64, int}
+    of int32:   result = b in {int64, int}
+    of uint:    result = b in {uint32, uint64}
+    of uint8:   result = b in {uint16, uint32, uint64}
+    of uint16:  result = b in {uint32, uint64}
+    of uint32:  result = b in {uint64}
+    of float32: result = b in {float64}
+    of float64: result = b in {float32}
+    of seq:
+      result = b == openArray and typeEquals(a.baseType, b.baseType)
+    of array:
+      result = b == openArray and typeEquals(a.baseType, b.baseType)
+      if a.baseType == char and a.indexType.rangeA == 0:
+        result = b == cstring
+    of cstring, ptr:
+      result = b == pointer
+    of string:
+      result = b == cstring
+    of proc:
+      result = typeEquals(a, b) or compatibleParametersAndEffects(a, b)
+  ```
+
+We used the predicate `typeEquals(a, b)` for the "type equality" property
+and the predicate `isSubtype(a, b)` for the "subtype relation".
+`compatibleParametersAndEffects(a, b)` is currently not specified.
+
+Implicit conversions are also performed for Nim's `range` type
+constructor.
+
+Let `a0`, `b0` of type `T`.
+
+Let `A = range[a0..b0]` be the argument's type, `F` the formal
+parameter's type. Then an implicit conversion from `A` to `F`
+exists if `a0 >= low(F) and b0 <= high(F)` and both `T` and `F`
+are signed integers or if both are unsigned integers.
+
+
+A type `a` is **explicitly** convertible to type `b` iff the following
+algorithm returns true:
+
+  ```nim
+  proc isIntegralType(t: PType): bool =
+    result = isOrdinal(t) or t.kind in {float, float32, float64}
+
+  proc isExplicitlyConvertible(a, b: PType): bool =
+    result = false
+    if isImplicitlyConvertible(a, b): return true
+    if typeEquals(a, b): return true
+    if a == distinct and typeEquals(a.baseType, b): return true
+    if b == distinct and typeEquals(b.baseType, a): return true
+    if isIntegralType(a) and isIntegralType(b): return true
+    if isSubtype(a, b) or isSubtype(b, a): return true
+  ```
+
+The convertible relation can be relaxed by a user-defined type
+`converter`:idx:.
+
+  ```nim
+  converter toInt(x: char): int = result = ord(x)
+
+  var
+    x: int
+    chr: char = 'a'
+
+  # implicit conversion magic happens here
+  x = chr
+  echo x # => 97
+  # one can use the explicit form too
+  x = chr.toInt
+  echo x # => 97
+  ```
+
+The type conversion `T(a)` is an L-value if `a` is an L-value and
+`typeEqualsOrDistinct(T, typeof(a))` holds.
+
+
+Assignment compatibility
+------------------------
+
+An expression `b` can be assigned to an expression `a` iff `a` is an
+`l-value` and `isImplicitlyConvertible(b.typ, a.typ)` holds.
+
+
+Overload resolution
+===================
+
+In a call `p(args)` where `p` may refer to more than one
+candidate, it is said to be a symbol choice. Overload resolution will attempt to
+find the best candidate, thus transforming the symbol choice into a resolved symbol.
+The routine `p` that matches best is selected following a series of trials explained below. 
+In order: Catagory matching, Hierarchical Order Comparison, and finally, Complexity Analysis.
+
+If multiple candidates match equally well after all trials have been tested, the ambiguity 
+is reported during semantic analysis.
+
+First Trial: Catagory matching
+--------------------------------
+
+Every arg in `args` needs to match and there are multiple different categories of matches.
+Let `f` be the formal parameter's type and `a` the type of the argument.
+
+1. Exact match: `a` and `f` are of the same type.
+2. Literal match: `a` is an integer literal of value `v`
+   and `f` is a signed or unsigned integer type and `v` is in `f`'s
+   range. Or:  `a` is a floating-point literal of value `v`
+   and `f` is a floating-point type and `v` is in `f`'s
+   range.
+3. Generic match: `f` is a generic type and `a` matches, for
+   instance `a` is `int` and `f` is a generic (constrained) parameter
+   type (like in `[T]` or `[T: int|char]`).
+4. Subrange or subtype match: `a` is a `range[T]` and `T`
+   matches `f` exactly. Or: `a` is a subtype of `f`.
+5. Integral conversion match: `a` is convertible to `f` and `f` and `a`
+   is some integer or floating-point type.
+6. Conversion match: `a` is convertible to `f`, possibly via a user
+   defined `converter`.
+
+Each operand may fall into one of the categories above; the operand's
+highest priority category. The list above is in order or priority.
+If a candidate has more priority matches than all other candidates, it is selected as the
+resolved symbol.
+
+For example, if a candidate with one exact match is compared to a candidate with multiple
+generic matches and zero exact matches, the candidate with an exact match will win.
+
+Below is a pseudocode interpretation of category matching, `count(p, m)` counts the number 
+of matches of the matching category `m` for the routine `p`.
+
+A routine `p` matches better than a routine `q` if the following
+algorithm returns true:
+
+  ```nim
+  for each matching category m in ["exact match", "literal match",
+                                  "generic match", "subtype match",
+                                  "integral match", "conversion match"]:
+    if count(p, m) > count(q, m): return true
+    elif count(p, m) == count(q, m):
+      discard "continue with next category m"
+    else:
+      return false
+  return "ambiguous"
+  ```
+
+Second Trial: Hierarchical Order Comparison
+----------------------------------------------
+
+The hierarchical order of a type is analogous to its relative specificity. Consider the type defined:
+
+```nim
+type A[T] = object
+```
+
+Matching formals for this type include `T`, `object`, `A`, `A[...]` and `A[C]` where `C` is a concrete type, `A[...]`
+is a generic typeclass composition and `T` is an unconstrained generic type variable. This list is in order of 
+specificity with respect to `A` as each subsequent category narrows the set of types that are members of their match set.
+
+In this trail, the formal parameters of candidates are compared in order (1st parameter, 2nd parameter, etc.) to search for
+a candidate that has an unrivaled specificity. If such a formal parameter is found, the candidate it belongs to is chosen 
+as the resolved symbol.
+
+Third Trial: Complexity Analysis
+----------------------------------
+
+A slight clarification: While category matching digests all the formal parameters of a candidate at once (order doesn't matter),
+specificity comparison and complexity analysis operate on each formal parameter at a time. The following
+is the final trial to disambiguate a symbol choice when a pair of formal parameters have the same hierarchical order.
+
+The complexity of a type is essentially its number of modifiers and depth of shape. The definition with the *highest*
+complexity wins. Consider the following types:
+
+```nim
+type
+  A[T] = object
+  B[T, H] = object
+```
+
+Note: The below examples are not exhaustive.
+
+We shall say that:
+
+1. `A[T]` has a higher complexity than `A`
+2. `var A[T]` has a higher complexity than `A[T]`
+3. `A[A[T]]` has a higher complexity than `A[T]`
+4. `B[T, H]` has a higher complexity than `A[T]` (`A` and `B` are not compatible here, but convoluted versions of this exist)
+5. `B[ptr T, H]` has a higher complexity than `B[T, H]`
+
+Some Examples
+---------------
+
+  ```nim
+  proc takesInt(x: int) = echo "int"
+  proc takesInt[T](x: T) = echo "T"
+  proc takesInt(x: int16) = echo "int16"
+
+  takesInt(4) # "int"
+  var x: int32
+  takesInt(x) # "T"
+  var y: int16
+  takesInt(y) # "int16"
+  var z: range[0..4] = 0
+  takesInt(z) # "T"
+  ```
+
+
+If the argument `a` matches both the parameter type `f` of `p`
+and `g` of `q` via a subtyping relation, the inheritance depth is taken
+into account:
+
+  ```nim
+  type
+    A = object of RootObj
+    B = object of A
+    C = object of B
+
+  proc p(obj: A) =
+    echo "A"
+
+  proc p(obj: B) =
+    echo "B"
+
+  var c = C()
+  # not ambiguous, calls 'B', not 'A' since B is a subtype of A
+  # but not vice versa:
+  p(c)
+
+  proc pp(obj: A, obj2: B) = echo "A B"
+  proc pp(obj: B, obj2: A) = echo "B A"
+
+  # but this is ambiguous:
+  pp(c, c)
+  ```
+
+
+Likewise, for generic matches, the most specialized generic type (that still
+matches) is preferred:
+
+  ```nim
+  proc gen[T](x: ref ref T) = echo "ref ref T"
+  proc gen[T](x: ref T) = echo "ref T"
+  proc gen[T](x: T) = echo "T"
+
+  var ri: ref int
+  gen(ri) # "ref T"
+  ```
+
+Type variables match
+----------------------
+
+When overload resolution is considering candidates, the type variable's definition
+is not overlooked as it is used to define the formal parameter's type via variable substitution.
+
+For example:
+```nim
+type A
+proc p[T: A](param: T)
+proc p[T: object](param: T)
+```
+
+These signatures are not ambiguous for a concrete type of `A` even though the formal parameters match ("T" == "T").
+Instead `T` is treated as a variable in that (`T` ?= `T`) depending on the bound type of `T` at the time of
+overload resolution.
+
+
+Overloading based on 'var T'
+--------------------------------------
+
+If the formal parameter `f` is of type `var T`
+in addition to the ordinary type checking,
+the argument is checked to be an `l-value`:idx:.
+`var T` matches better than just `T` then.
+
+  ```nim
+  proc sayHi(x: int): string =
+    # matches a non-var int
+    result = $x
+  proc sayHi(x: var int): string =
+    # matches a var int
+    result = $(x + 10)
+
+  proc sayHello(x: int) =
+    var m = x # a mutable version of x
+    echo sayHi(x) # matches the non-var version of sayHi
+    echo sayHi(m) # matches the var version of sayHi
+
+  sayHello(3) # 3
+              # 13
+  ```
+
+
+Lazy type resolution for untyped
+--------------------------------
+
+**Note**: An `unresolved`:idx: expression is an expression for which no symbol
+lookups and no type checking have been performed.
+
+Since templates and macros that are not declared as `immediate` participate
+in overloading resolution, it's essential to have a way to pass unresolved
+expressions to a template or macro. This is what the meta-type `untyped`
+accomplishes:
+
+  ```nim
+  template rem(x: untyped) = discard
+
+  rem unresolvedExpression(undeclaredIdentifier)
+  ```
+
+A parameter of type `untyped` always matches any argument (as long as there is
+any argument passed to it).
+
+But one has to watch out because other overloads might trigger the
+argument's resolution:
+
+  ```nim
+  template rem(x: untyped) = discard
+  proc rem[T](x: T) = discard
+
+  # undeclared identifier: 'unresolvedExpression'
+  rem unresolvedExpression(undeclaredIdentifier)
+  ```
+
+`untyped` and `varargs[untyped]` are the only metatype that are lazy in this sense, the other
+metatypes `typed` and `typedesc` are not lazy.
+
+
+Varargs matching
+----------------
+
+See [Varargs].
+
+
+iterable
+--------
+
+A called `iterator` yielding type `T` can be passed to a template or macro via
+a parameter typed as `untyped` (for unresolved expressions) or the type class
+`iterable` or `iterable[T]` (after type checking and overload resolution).
+
+  ```nim
+  iterator iota(n: int): int =
+    for i in 0..<n: yield i
+
+  template toSeq2[T](a: iterable[T]): seq[T] =
+    var ret: seq[T]
+    assert a.typeof is T
+    for ai in a: ret.add ai
+    ret
+
+  assert iota(3).toSeq2 == @[0, 1, 2]
+  assert toSeq2(5..7) == @[5, 6, 7]
+  assert not compiles(toSeq2(@[1,2])) # seq[int] is not an iterable
+  assert toSeq2(items(@[1,2])) == @[1, 2] # but items(@[1,2]) is
+  ```
+
+
+Overload disambiguation
+=======================
+
+For routine calls "overload resolution" is performed. There is a weaker form of
+overload resolution called *overload disambiguation* that is performed when an
+overloaded symbol is used in a context where there is additional type information
+available. Let `p` be an overloaded symbol. These contexts are:
+
+- In a function call `q(..., p, ...)` when the corresponding formal parameter
+  of `q` is a `proc` type. If `q` itself is overloaded then the cartesian product
+  of every interpretation of `q` and `p` must be considered.
+- In an object constructor `Obj(..., field: p, ...)` when `field` is a `proc`
+  type. Analogous rules exist for array/set/tuple constructors.
+- In a declaration like `x: T = p` when `T` is a `proc` type.
+
+As usual, ambiguous matches produce a compile-time error.
+
+Named argument overloading
+--------------------------
+
+Routines with the same type signature can be called individually if
+a parameter has different names between them.
+
+  ```Nim
+  proc foo(x: int) =
+    echo "Using x: ", x
+  proc foo(y: int) =
+    echo "Using y: ", y
+
+  foo(x = 2) # Using x: 2
+  foo(y = 2) # Using y: 2
+  ```
+
+Not supplying the parameter name in such cases results in an
+ambiguity error.
+
+
+Statements and expressions
+==========================
+
+Nim uses the common statement/expression paradigm: Statements do not
+produce a value in contrast to expressions. However, some expressions are
+statements.
+
+Statements are separated into `simple statements`:idx: and
+`complex statements`:idx:.
+Simple statements are statements that cannot contain other statements like
+assignments, calls, or the `return` statement; complex statements can
+contain other statements. To avoid the `dangling else problem`:idx:, complex
+statements always have to be indented. The details can be found in the grammar.
+
+
+Statement list expression
+-------------------------
+
+Statements can also occur in an expression context that looks
+like `(stmt1; stmt2; ...; ex)`. This is called
+a statement list expression or `(;)`. The type
+of `(stmt1; stmt2; ...; ex)` is the type of `ex`. All the other statements
+must be of type `void`. (One can use `discard` to produce a `void` type.)
+`(;)` does not introduce a new scope.
+
+
+Discard statement
+-----------------
+
+Example:
+
+  ```nim
+  proc p(x, y: int): int =
+    result = x + y
+
+  discard p(3, 4) # discard the return value of `p`
+  ```
+
+The `discard` statement evaluates its expression for side-effects and
+throws the expression's resulting value away, and should only be used
+when ignoring this value is known not to cause problems.
+
+Ignoring the return value of a procedure without using a discard statement is
+a static error.
+
+The return value can be ignored implicitly if the called proc/iterator has
+been declared with the `discardable`:idx: pragma:
+
+  ```nim
+  proc p(x, y: int): int {.discardable.} =
+    result = x + y
+
+  p(3, 4) # now valid
+  ```
+
+however the discardable pragma does not work on templates as templates substitute the AST in place. For example:
+
+  ```nim
+  {.push discardable .}
+  template example(): string = "https://nim-lang.org"
+  {.pop.}
+
+  example()
+  ```
+
+This template will resolve into "https://nim-lang.org" which is a string literal and since {.discardable.} doesn't apply to literals, the compiler will error.
+
+An empty `discard` statement is often used as a null statement:
+
+  ```nim
+  proc classify(s: string) =
+    case s[0]
+    of SymChars, '_': echo "an identifier"
+    of '0'..'9': echo "a number"
+    else: discard
+  ```
+
+
+Void context
+------------
+
+In a list of statements, every expression except the last one needs to have the
+type `void`. In addition to this rule an assignment to the builtin `result`
+symbol also triggers a mandatory `void` context for the subsequent expressions:
+
+  ```nim
+  proc invalid*(): string =
+    result = "foo"
+    "invalid"  # Error: value of type 'string' has to be discarded
+  ```
+
+  ```nim
+  proc valid*(): string =
+    let x = 317
+    "valid"
+  ```
+
+
+Var statement
+-------------
+
+Var statements declare new local and global variables and
+initialize them. A comma-separated list of variables can be used to specify
+variables of the same type:
+
+  ```nim
+  var
+    a: int = 0
+    x, y, z: int
+  ```
+
+If an initializer is given, the type can be omitted: the variable is then of the
+same type as the initializing expression. Variables are always initialized
+with a default value if there is no initializing expression. The default
+value depends on the type and is always a zero in binary.
+
+============================    ==============================================
+Type                            default value
+============================    ==============================================
+any integer type                0
+any float                       0.0
+char                            '\\0'
+bool                            false
+ref or pointer type             nil
+procedural type                 nil
+sequence                        `@[]`
+string                          `""`
+`tuple[x: A, y: B, ...]`        (zeroDefault(A), zeroDefault(B), ...)
+                                (analogous for objects)
+`array[0..., T]`                `[zeroDefault(T), ...]`
+`range[T]`                      default(T); this may be out of the valid range
+T = enum                        `cast[T](0)`; this may be an invalid value
+============================    ==============================================
+
+
+The implicit initialization can be avoided for optimization reasons with the
+`noinit`:idx: pragma:
+
+  ```nim
+  var
+    a {.noinit.}: array[0..1023, char]
+  ```
+
+If a proc is annotated with the `noinit` pragma, this refers to its implicit
+`result` variable:
+
+  ```nim
+  proc returnUndefinedValue: int {.noinit.} = discard
+  ```
+
+
+The implicit initialization can also be prevented by the `requiresInit`:idx:
+type pragma. The compiler requires an explicit initialization for the object
+and all of its fields. However, it does a `control flow analysis`:idx: to prove
+the variable has been initialized and does not rely on syntactic properties:
+
+  ```nim
+  type
+    MyObject {.requiresInit.} = object
+
+  proc p() =
+    # the following is valid:
+    var x: MyObject
+    if someCondition():
+      x = a()
+    else:
+      x = a()
+    # use x
+  ```
+
+`requiresInit` pragma can also be applied to `distinct` types.
+
+Given the following distinct type definitions:
+
+  ```nim
+  type
+    Foo = object
+      x: string
+
+    DistinctFoo {.requiresInit, borrow: `.`.} = distinct Foo
+    DistinctString {.requiresInit.} = distinct string
+  ```
+
+The following code blocks will fail to compile:
+
+  ```nim
+  var foo: DistinctFoo
+  foo.x = "test"
+  doAssert foo.x == "test"
+  ```
+
+  ```nim
+  var s: DistinctString
+  s = "test"
+  doAssert string(s) == "test"
+  ```
+
+But these will compile successfully:
+
+  ```nim
+  let foo = DistinctFoo(Foo(x: "test"))
+  doAssert foo.x == "test"
+  ```
+
+  ```nim
+  let s = DistinctString("test")
+  doAssert string(s) == "test"
+  ```
+
+Let statement
+-------------
+
+A `let` statement declares new local and global `single assignment`:idx:
+variables and binds a value to them. The syntax is the same as that of the `var`
+statement, except that the keyword `var` is replaced by the keyword `let`.
+Let variables are not l-values and can thus not be passed to `var` parameters
+nor can their address be taken. They cannot be assigned new values.
+
+For let variables, the same pragmas are available as for ordinary variables.
+
+As `let` statements are immutable after creation they need to define a value
+when they are declared. The only exception to this is if the `{.importc.}`
+pragma (or any of the other `importX` pragmas) is applied, in this case the
+value is expected to come from native code, typically a C/C++ `const`.
+
+Special identifier `_` (underscore)
+-----------------------------------
+
+The identifier `_` has a special meaning in declarations.
+Any definition with the name `_` will not be added to scope, meaning the
+definition is evaluated, but cannot be used. As a result the name `_` can be
+indefinitely redefined.
+
+  ```nim
+  let _ = 123
+  echo _ # error
+  let _ = 456 # compiles
+  ```
+
+Tuple unpacking
+---------------
+
+In a `var`, `let` or `const` statement tuple unpacking can be performed.
+The special identifier `_` can be used to ignore some parts of the tuple:
+
+  ```nim
+  proc returnsTuple(): (int, int, int) = (4, 2, 3)
+
+  let (x, _, z) = returnsTuple()
+  ```
+
+This is treated as syntax sugar for roughly the following:
+
+  ```nim
+  let
+    tmpTuple = returnsTuple()
+    x = tmpTuple[0]
+    z = tmpTuple[2]
+  ```
+
+For `var` or `let` statements, if the value expression is a tuple literal,
+each expression is directly expanded into an assignment without the use of
+a temporary variable.
+
+  ```nim
+  let (x, y, z) = (1, 2, 3)
+  # becomes
+  let
+    x = 1
+    y = 2
+    z = 3
+  ```
+
+Tuple unpacking can also be nested:
+
+  ```nim
+  proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3)
+
+  let (x, (_, y), _, z) = returnsNestedTuple()
+  ```
+
+
+Const section
+-------------
+
+A const section declares constants whose values are constant expressions:
+
+  ```nim
+  import std/[strutils]
+  const
+    roundPi = 3.1415
+    constEval = contains("abc", 'b') # computed at compile time!
+  ```
+
+Once declared, a constant's symbol can be used as a constant expression.
+
+The value part of a constant declaration opens a new scope for each constant,
+so no symbols declared in the constant value are accessible outside of it.
+
+  ```nim
+  const foo = (var a = 1; a)
+  const bar = a # error
+  let baz = a # error
+  ```
+
+See [Constants and Constant Expressions] for details.
+
+Static statement/expression
+---------------------------
+
+A static statement/expression explicitly requires compile-time execution.
+Even some code that has side effects is permitted in a static block:
+
+  ```nim
+  static:
+    echo "echo at compile time"
+  ```
+
+`static` can also be used like a routine.
+
+  ```nim
+  proc getNum(a: int): int = a
+
+  # Below calls "echo getNum(123)" at compile time.
+  static:
+    echo getNum(123)
+
+  # Below call evaluates the "getNum(123)" at compile time, but its
+  # result gets used at run time.
+  echo static(getNum(123))
+  ```
+
+There are limitations on what Nim code can be executed at compile time;
+see [Restrictions on Compile-Time Execution] for details.
+It's a static error if the compiler cannot execute the block at compile
+time.
+
+
+If statement
+------------
+
+Example:
+
+  ```nim
+  var name = readLine(stdin)
+
+  if name == "Andreas":
+    echo "What a nice name!"
+  elif name == "":
+    echo "Don't you have a name?"
+  else:
+    echo "Boring name..."
+  ```
+
+The `if` statement is a simple way to make a branch in the control flow:
+The expression after the keyword `if` is evaluated, if it is true
+the corresponding statements after the `:` are executed. Otherwise,
+the expression after the `elif` is evaluated (if there is an
+`elif` branch), if it is true the corresponding statements after
+the `:` are executed. This goes on until the last `elif`. If all
+conditions fail, the `else` part is executed. If there is no `else`
+part, execution continues with the next statement.
+
+In `if` statements, new scopes begin immediately after
+the `if`/`elif`/`else` keywords and ends after the
+corresponding *then* block.
+For visualization purposes the scopes have been enclosed
+in `{|  |}` in the following example:
+
+  ```nim
+  if {| (let m = input =~ re"(\w+)=\w+"; m.isMatch):
+    echo "key ", m[0], " value ", m[1]  |}
+  elif {| (let m = input =~ re""; m.isMatch):
+    echo "new m in this scope"  |}
+  else: {|
+    echo "m not declared here"  |}
+  ```
+
+Case statement
+--------------
+
+Example:
+
+  ```nim
+  let line = readline(stdin)
+  case line
+  of "delete-everything", "restart-computer":
+    echo "permission denied"
+  of "go-for-a-walk":     echo "please yourself"
+  elif line.len == 0:     echo "empty" # optional, must come after `of` branches
+  else:                   echo "unknown command" # ditto
+
+  # indentation of the branches is also allowed; and so is an optional colon
+  # after the selecting expression:
+  case readline(stdin):
+    of "delete-everything", "restart-computer":
+      echo "permission denied"
+    of "go-for-a-walk":     echo "please yourself"
+    else:                   echo "unknown command"
+  ```
+
+
+The `case` statement is similar to the `if` statement, but it represents
+a multi-branch selection. The expression after the keyword `case` is
+evaluated and if its value is in a *slicelist* the corresponding statements
+(after the `of` keyword) are executed. If the value is not in any
+given *slicelist*, trailing `elif` and `else` parts are executed using same
+semantics as for `if` statement, and `elif` is handled just like `else: if`.
+If there are no `else` or `elif` parts and not
+all possible values that `expr` can hold occur in a *slicelist*, a static error occurs.
+This holds only for expressions of ordinal types.
+"All possible values" of `expr` are determined by `expr`'s type.
+To suppress the static error an `else: discard` should be used.
+
+Only ordinal types, floats, strings and cstrings are allowed as values
+in case statements.
+
+For non-ordinal types, it is not possible to list every possible value and so
+these always require an `else` part.
+An exception to this rule is for the `string` type, which currently doesn't
+require a trailing `else` or `elif` branch; it's unspecified whether this will
+keep working in future versions.
+
+Because case statements are checked for exhaustiveness during semantic analysis,
+the value in every `of` branch must be a constant expression.
+This restriction also allows the compiler to generate more performant code.
+
+As a special semantic extension, an expression in an `of` branch of a case
+statement may evaluate to a set or array constructor; the set or array is then
+expanded into a list of its elements:
+
+  ```nim
+  const
+    SymChars: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'}
+
+  proc classify(s: string) =
+    case s[0]
+    of SymChars, '_': echo "an identifier"
+    of '0'..'9': echo "a number"
+    else: echo "other"
+
+  # is equivalent to:
+  proc classify(s: string) =
+    case s[0]
+    of 'a'..'z', 'A'..'Z', '\x80'..'\xFF', '_': echo "an identifier"
+    of '0'..'9': echo "a number"
+    else: echo "other"
+  ```
+
+The `case` statement doesn't produce an l-value, so the following example
+won't work:
+
+  ```nim
+  type
+    Foo = ref object
+      x: seq[string]
+
+  proc get_x(x: Foo): var seq[string] =
+    # doesn't work
+    case true
+    of true:
+      x.x
+    else:
+      x.x
+
+  var foo = Foo(x: @[])
+  foo.get_x().add("asd")
+  ```
+
+This can be fixed by explicitly using `result` or `return`:
+
+  ```nim
+  proc get_x(x: Foo): var seq[string] =
+    case true
+    of true:
+      result = x.x
+    else:
+      result = x.x
+  ```
+
+
+When statement
+--------------
+
+Example:
+
+  ```nim
+  when sizeof(int) == 2:
+    echo "running on a 16 bit system!"
+  elif sizeof(int) == 4:
+    echo "running on a 32 bit system!"
+  elif sizeof(int) == 8:
+    echo "running on a 64 bit system!"
+  else:
+    echo "cannot happen!"
+  ```
+
+The `when` statement is almost identical to the `if` statement with some
+exceptions:
+
+* Each condition (`expr`) has to be a constant expression (of type `bool`).
+* The statements do not open a new scope.
+* The statements that belong to the expression that evaluated to true are
+  translated by the compiler, the other statements are not checked for
+  semantics! However, each condition is checked for semantics.
+
+The `when` statement enables conditional compilation techniques. As
+a special syntactic extension, the `when` construct is also available
+within `object` definitions.
+
+
+When nimvm statement
+--------------------
+
+`nimvm` is a special symbol that may be used as the expression of a
+`when nimvm` statement to differentiate the execution path between
+compile-time and the executable.
+
+Example:
+
+  ```nim
+  proc someProcThatMayRunInCompileTime(): bool =
+    when nimvm:
+      # This branch is taken at compile time.
+      result = true
+    else:
+      # This branch is taken in the executable.
+      result = false
+  const ctValue = someProcThatMayRunInCompileTime()
+  let rtValue = someProcThatMayRunInCompileTime()
+  assert(ctValue == true)
+  assert(rtValue == false)
+  ```
+
+A `when nimvm` statement must meet the following requirements:
+
+* Its expression must always be `nimvm`. More complex expressions are not
+  allowed.
+* It must not contain `elif` branches.
+* It must contain an `else` branch.
+* Code in branches must not affect semantics of the code that follows the
+  `when nimvm` statement. E.g. it must not define symbols that are used in
+  the following code.
+
+Return statement
+----------------
+
+Example:
+
+  ```nim
+  return 40 + 2
+  ```
+
+The `return` statement ends the execution of the current procedure.
+It is only allowed in procedures. If there is an `expr`, this is syntactic
+sugar for:
+
+  ```nim
+  result = expr
+  return result
+  ```
+
+
+`return` without an expression is a short notation for `return result` if
+the proc has a return type. The `result`:idx: variable is always the return
+value of the procedure. It is automatically declared by the compiler. As all
+variables, `result` is initialized to (binary) zero:
+
+  ```nim
+  proc returnZero(): int =
+    # implicitly returns 0
+  ```
+
+
+Yield statement
+---------------
+
+Example:
+
+  ```nim
+  yield (1, 2, 3)
+  ```
+
+The `yield` statement is used instead of the `return` statement in
+iterators. It is only valid in iterators. Execution is returned to the body
+of the for loop that called the iterator. Yield does not end the iteration
+process, but the execution is passed back to the iterator if the next iteration
+starts. See the section about iterators ([Iterators and the for statement])
+for further information.
+
+
+Block statement
+---------------
+
+Example:
+
+  ```nim
+  var found = false
+  block myblock:
+    for i in 0..3:
+      for j in 0..3:
+        if a[j][i] == 7:
+          found = true
+          break myblock # leave the block, in this case both for-loops
+  echo found
+  ```
+
+The block statement is a means to group statements to a (named) `block`.
+Inside the block, the `break` statement is allowed to leave the block
+immediately. A `break` statement can contain a name of a surrounding
+block to specify which block is to be left.
+
+
+Break statement
+---------------
+
+Example:
+
+  ```nim
+  break
+  ```
+
+The `break` statement is used to leave a block immediately. If `symbol`
+is given, it is the name of the enclosing block that is to be left. If it is
+absent, the innermost block is left.
+
+
+While statement
+---------------
+
+Example:
+
+  ```nim
+  echo "Please tell me your password:"
+  var pw = readLine(stdin)
+  while pw != "12345":
+    echo "Wrong password! Next try:"
+    pw = readLine(stdin)
+  ```
+
+
+The `while` statement is executed until the `expr` evaluates to false.
+Endless loops are no error. `while` statements open an `implicit block`
+so that they can be left with a `break` statement.
+
+
+Continue statement
+------------------
+
+A `continue` statement leads to the immediate next iteration of the
+surrounding loop construct. It is only allowed within a loop. A continue
+statement is syntactic sugar for a nested block:
+
+  ```nim
+  while expr1:
+    stmt1
+    continue
+    stmt2
+  ```
+
+Is equivalent to:
+
+  ```nim
+  while expr1:
+    block myBlockName:
+      stmt1
+      break myBlockName
+      stmt2
+  ```
+
+
+Assembler statement
+-------------------
+
+The direct embedding of assembler code into Nim code is supported
+by the unsafe `asm` statement. Identifiers in the assembler code that refer to
+Nim identifiers shall be enclosed in a special character which can be
+specified in the statement's pragmas. The default special character is `'\`'`:
+
+  ```nim
+  {.push stackTrace:off.}
+  proc addInt(a, b: int): int =
+    # a in eax, and b in edx
+    asm """
+        mov eax, `a`
+        add eax, `b`
+        jno theEnd
+        call `raiseOverflow`
+      theEnd:
+    """
+  {.pop.}
+  ```
+
+If the GNU assembler is used, quotes and newlines are inserted automatically:
+
+  ```nim
+  proc addInt(a, b: int): int =
+    asm """
+      addl %%ecx, %%eax
+      jno 1
+      call `raiseOverflow`
+      1:
+      :"=a"(`result`)
+      :"a"(`a`), "c"(`b`)
+    """
+  ```
+
+Instead of:
+
+  ```nim
+  proc addInt(a, b: int): int =
+    asm """
+      "addl %%ecx, %%eax\n"
+      "jno 1\n"
+      "call `raiseOverflow`\n"
+      "1: \n"
+      :"=a"(`result`)
+      :"a"(`a`), "c"(`b`)
+    """
+  ```
+
+Using statement
+---------------
+
+The `using` statement provides syntactic convenience in modules where
+the same parameter names and types are used over and over. Instead of:
+
+  ```nim
+  proc foo(c: Context; n: Node) = ...
+  proc bar(c: Context; n: Node, counter: int) = ...
+  proc baz(c: Context; n: Node) = ...
+  ```
+
+One can tell the compiler about the convention that a parameter of
+name `c` should default to type `Context`, `n` should default to
+`Node` etc.:
+
+  ```nim
+  using
+    c: Context
+    n: Node
+    counter: int
+
+  proc foo(c, n) = ...
+  proc bar(c, n, counter) = ...
+  proc baz(c, n) = ...
+
+  proc mixedMode(c, n; x, y: int) =
+    # 'c' is inferred to be of the type 'Context'
+    # 'n' is inferred to be of the type 'Node'
+    # But 'x' and 'y' are of type 'int'.
+  ```
+
+The `using` section uses the same indentation based grouping syntax as
+a `var` or `let` section.
+
+Note that `using` is not applied for `template` since the untyped template
+parameters default to the type `system.untyped`.
+
+Mixing parameters that should use the `using` declaration with parameters
+that are explicitly typed is possible and requires a semicolon between them.
+
+
+If expression
+-------------
+
+An `if` expression is almost like an if statement, but it is an expression.
+This feature is similar to *ternary operators* in other languages.
+Example:
+
+  ```nim
+  var y = if x > 8: 9 else: 10
+  ```
+
+An `if` expression always results in a value, so the `else` part is
+required. `elif` parts are also allowed.
+
+When expression
+---------------
+
+Just like an `if` expression, but corresponding to the `when` statement.
+
+Case expression
+---------------
+
+The `case` expression is again very similar to the case statement:
+
+  ```nim
+  var favoriteFood = case animal
+    of "dog": "bones"
+    of "cat": "mice"
+    elif animal.endsWith"whale": "plankton"
+    else:
+      echo "I'm not sure what to serve, but everybody loves ice cream"
+      "ice cream"
+  ```
+
+As seen in the above example, the case expression can also introduce side
+effects. When multiple statements are given for a branch, Nim will use
+the last expression as the result value.
+
+Block expression
+----------------
+
+A `block` expression is almost like a block statement, but it is an expression
+that uses the last expression under the block as the value.
+It is similar to the statement list expression, but the statement list expression
+does not open a new block scope.
+
+  ```nim
+  let a = block:
+    var fib = @[0, 1]
+    for i in 0..10:
+      fib.add fib[^1] + fib[^2]
+    fib
+  ```
+
+Table constructor
+-----------------
+
+A table constructor is syntactic sugar for an array constructor:
+
+  ```nim
+  {"key1": "value1", "key2", "key3": "value2"}
+
+  # is the same as:
+  [("key1", "value1"), ("key2", "value2"), ("key3", "value2")]
+  ```
+
+
+The empty table can be written `{:}` (in contrast to the empty set
+which is `{}`) which is thus another way to write the empty array
+constructor `[]`. This slightly unusual way of supporting tables
+has lots of advantages:
+
+* The order of the (key,value)-pairs is preserved, thus it is easy to
+  support ordered dicts with for example `{key: val}.newOrderedTable`.
+* A table literal can be put into a `const` section and the compiler
+  can easily put it into the executable's data section just like it can
+  for arrays and the generated data section requires a minimal amount
+  of memory.
+* Every table implementation is treated equally syntactically.
+* Apart from the minimal syntactic sugar, the language core does not need to
+  know about tables.
+
+
+Type conversions
+----------------
+
+Syntactically a *type conversion* is like a procedure call, but a
+type name replaces the procedure name. A type conversion is always
+safe in the sense that a failure to convert a type to another
+results in an exception (if it cannot be determined statically).
+
+Ordinary procs are often preferred over type conversions in Nim: For instance,
+`$` is the `toString` operator by convention and `toFloat` and `toInt`
+can be used to convert from floating-point to integer or vice versa.
+
+Type conversion can also be used to disambiguate overloaded routines:
+
+  ```nim
+  proc p(x: int) = echo "int"
+  proc p(x: string) = echo "string"
+
+  let procVar = (proc(x: string))(p)
+  procVar("a")
+  ```
+
+Since operations on unsigned numbers wrap around and are unchecked so are
+type conversions to unsigned integers and between unsigned integers. The
+rationale for this is mostly better interoperability with the C Programming
+language when algorithms are ported from C to Nim.
+
+**Note**: Historically the operations
+were unchecked and the conversions were sometimes checked but starting with
+the revision 1.0.4 of this document and the language implementation the
+conversions too are now *always unchecked*.
+
+
+Type casts
+----------
+
+*Type casts* are a crude mechanism to interpret the bit pattern of an expression
+as if it would be of another type. Type casts are only needed for low-level
+programming and are inherently unsafe.
+
+  ```nim
+  cast[int](x)
+  ```
+
+The target type of a cast must be a concrete type, for instance, a target type
+that is a type class (which is non-concrete) would be invalid:
+
+  ```nim
+  type Foo = int or float
+  var x = cast[Foo](1) # Error: cannot cast to a non concrete type: 'Foo'
+  ```
+
+Type casts should not be confused with *type conversions,* as mentioned in the
+prior section. Unlike type conversions, a type cast cannot change the underlying
+bit pattern of the data being cast (aside from that the size of the target type
+may differ from the source type). Casting resembles *type punning* in other
+languages or C++'s `reinterpret_cast`:cpp: and `bit_cast`:cpp: features.
+
+If the size of the target type is larger than the size of the source type,
+the remaining memory is zeroed.
+
+The addr operator
+-----------------
+The `addr` operator returns the address of an l-value. If the type of the
+location is `T`, the `addr` operator result is of the type `ptr T`. An
+address is always an untraced reference. Taking the address of an object that
+resides on the stack is **unsafe**, as the pointer may live longer than the
+object on the stack and can thus reference a non-existing object. One can get
+the address of variables. For easier interoperability with other compiled languages
+such as C, retrieving the address of a `let` variable, a parameter,
+or a `for` loop variable can be accomplished too:
+
+  ```nim
+  let t1 = "Hello"
+  var
+    t2 = t1
+    t3 : pointer = addr(t2)
+  echo repr(addr(t2))
+  # --> ref 0x7fff6b71b670 --> 0x10bb81050"Hello"
+  echo cast[ptr string](t3)[]
+  # --> Hello
+  # The following line also works
+  echo repr(addr(t1))
+  ```
+
+The unsafeAddr operator
+-----------------------
+
+The `unsafeAddr` operator is a deprecated alias for the `addr` operator:
+
+  ```nim
+  let myArray = [1, 2, 3]
+  foreignProcThatTakesAnAddr(unsafeAddr myArray)
+  ```
+
+Procedures
+==========
+
+What most programming languages call `methods`:idx: or `functions`:idx: are
+called `procedures`:idx: in Nim. A procedure
+declaration consists of an identifier, zero or more formal parameters, a return
+value type and a block of code. Formal parameters are declared as a list of
+identifiers separated by either comma or semicolon. A parameter is given a type
+by `: typename`. The type applies to all parameters immediately before it,
+until either the beginning of the parameter list, a semicolon separator, or an
+already typed parameter, is reached. The semicolon can be used to make
+separation of types and subsequent identifiers more distinct.
+
+  ```nim
+  # Using only commas
+  proc foo(a, b: int, c, d: bool): int
+
+  # Using semicolon for visual distinction
+  proc foo(a, b: int; c, d: bool): int
+
+  # Will fail: a is untyped since ';' stops type propagation.
+  proc foo(a; b: int; c, d: bool): int
+  ```
+
+A parameter may be declared with a default value which is used if the caller
+does not provide a value for the argument. The value will be reevaluated
+every time the function is called.
+
+  ```nim
+  # b is optional with 47 as its default value.
+  proc foo(a: int, b: int = 47): int
+  ```
+
+Parameters can be declared mutable and so allow the proc to modify those
+arguments, by using the type modifier `var`.
+
+  ```nim
+  # "returning" a value to the caller through the 2nd argument
+  # Notice that the function uses no actual return value at all (ie void)
+  proc foo(inp: int, outp: var int) =
+    outp = inp + 47
+  ```
+
+If the proc declaration doesn't have a body, it is a `forward`:idx: declaration.
+If the proc returns a value, the procedure body can access an implicitly declared
+variable named `result`:idx: that represents the return value. Procs can be
+overloaded. The overloading resolution algorithm determines which proc is the
+best match for the arguments. Example:
+
+  ```nim
+  proc toLower(c: char): char = # toLower for characters
+    if c in {'A'..'Z'}:
+      result = chr(ord(c) + (ord('a') - ord('A')))
+    else:
+      result = c
+
+  proc toLower(s: string): string = # toLower for strings
+    result = newString(len(s))
+    for i in 0..len(s) - 1:
+      result[i] = toLower(s[i]) # calls toLower for characters; no recursion!
+  ```
+
+Calling a procedure can be done in many ways:
+
+  ```nim
+  proc callme(x, y: int, s: string = "", c: char, b: bool = false) = ...
+
+  # call with positional arguments      # parameter bindings:
+  callme(0, 1, "abc", '\t', true)       # (x=0, y=1, s="abc", c='\t', b=true)
+  # call with named and positional arguments:
+  callme(y=1, x=0, "abd", '\t')         # (x=0, y=1, s="abd", c='\t', b=false)
+  # call with named arguments (order is not relevant):
+  callme(c='\t', y=1, x=0)              # (x=0, y=1, s="", c='\t', b=false)
+  # call as a command statement: no () needed:
+  callme 0, 1, "abc", '\t'              # (x=0, y=1, s="abc", c='\t', b=false)
+  ```
+
+A procedure may call itself recursively.
+
+
+`Operators`:idx: are procedures with a special operator symbol as identifier:
+
+  ```nim
+  proc `$` (x: int): string =
+    # converts an integer to a string; this is a prefix operator.
+    result = intToStr(x)
+  ```
+
+Operators with one parameter are prefix operators, operators with two
+parameters are infix operators. (However, the parser distinguishes these from
+the operator's position within an expression.) There is no way to declare
+postfix operators: all postfix operators are built-in and handled by the
+grammar explicitly.
+
+Any operator can be called like an ordinary proc with the \`opr\`
+notation. (Thus an operator can have more than two parameters):
+
+  ```nim
+  proc `*+` (a, b, c: int): int =
+    # Multiply and add
+    result = a * b + c
+
+  assert `*+`(3, 4, 6) == `+`(`*`(a, b), c)
+  ```
+
+
+Export marker
+-------------
+
+If a declared symbol is marked with an `asterisk`:idx: it is exported from the
+current module:
+
+  ```nim
+  proc exportedEcho*(s: string) = echo s
+  proc `*`*(a: string; b: int): string =
+    result = newStringOfCap(a.len * b)
+    for i in 1..b: result.add a
+
+  var exportedVar*: int
+  const exportedConst* = 78
+  type
+    ExportedType* = object
+      exportedField*: int
+  ```
+
+
+Method call syntax
+------------------
+
+For object-oriented programming, the syntax `obj.methodName(args)` can be used
+instead of `methodName(obj, args)`. The parentheses can be omitted if
+there are no remaining arguments: `obj.len` (instead of `len(obj)`).
+
+This method call syntax is not restricted to objects, it can be used
+to supply any type of first argument for procedures:
+
+  ```nim
+  echo "abc".len # is the same as echo len "abc"
+  echo "abc".toUpper()
+  echo {'a', 'b', 'c'}.card
+  stdout.writeLine("Hallo") # the same as writeLine(stdout, "Hallo")
+  ```
+
+Another way to look at the method call syntax is that it provides the missing
+postfix notation.
+
+The method call syntax conflicts with explicit generic instantiations:
+`p[T](x)` cannot be written as `x.p[T]` because `x.p[T]` is always
+parsed as `(x.p)[T]`.
+
+See also: [Limitations of the method call syntax].
+
+The `[: ]` notation has been designed to mitigate this issue: `x.p[:T]`
+is rewritten by the parser to `p[T](x)`, `x.p[:T](y)` is rewritten to
+`p[T](x, y)`. Note that `[: ]` has no AST representation, the rewrite
+is performed directly in the parsing step.
+
+
+Properties
+----------
+Nim has no need for *get-properties*: Ordinary get-procedures that are called
+with the *method call syntax* achieve the same. But setting a value is
+different; for this, a special setter syntax is needed:
+
+  ```nim
+  # Module asocket
+  type
+    Socket* = ref object of RootObj
+      host: int # cannot be accessed from the outside of the module
+
+  proc `host=`*(s: var Socket, value: int) {.inline.} =
+    ## setter of hostAddr.
+    ## This accesses the 'host' field and is not a recursive call to
+    ## `host=` because the builtin dot access is preferred if it is
+    ## available:
+    s.host = value
+
+  proc host*(s: Socket): int {.inline.} =
+    ## getter of hostAddr
+    ## This accesses the 'host' field and is not a recursive call to
+    ## `host` because the builtin dot access is preferred if it is
+    ## available:
+    s.host
+  ```
+
+  ```nim
+  # module B
+  import asocket
+  var s: Socket
+  new s
+  s.host = 34  # same as `host=`(s, 34)
+  ```
+
+A proc defined as `f=` (with the trailing `=`) is called
+a `setter`:idx:. A setter can be called explicitly via the common
+backticks notation:
+
+  ```nim
+  proc `f=`(x: MyObject; value: string) =
+    discard
+
+  `f=`(myObject, "value")
+  ```
+
+
+`f=` can be called implicitly in the pattern
+`x.f = value` if and only if the type of `x` does not have a field
+named `f` or if `f` is not visible in the current module. These rules
+ensure that object fields and accessors can have the same name. Within the
+module `x.f` is then always interpreted as field access and outside the
+module it is interpreted as an accessor proc call.
+
+
+Command invocation syntax
+-------------------------
+
+Routines can be invoked without the `()` if the call is syntactically
+a statement. This command invocation syntax also works for
+expressions, but then only a single argument may follow. This restriction
+means `echo f 1, f 2` is parsed as `echo(f(1), f(2))` and not as
+`echo(f(1, f(2)))`. The method call syntax may be used to provide one
+more argument in this case:
+
+  ```nim
+  proc optarg(x: int, y: int = 0): int = x + y
+  proc singlearg(x: int): int = 20*x
+
+  echo optarg 1, " ", singlearg 2  # prints "1 40"
+
+  let fail = optarg 1, optarg 8   # Wrong. Too many arguments for a command call
+  let x = optarg(1, optarg 8)  # traditional procedure call with 2 arguments
+  let y = 1.optarg optarg 8    # same thing as above, w/o the parenthesis
+  assert x == y
+  ```
+
+The command invocation syntax also can't have complex expressions as arguments.
+For example: [anonymous procedures], `if`,
+`case` or `try`. Function calls with no arguments still need () to
+distinguish between a call and the function itself as a first-class value.
+
+
+Closures
+--------
+
+Procedures can appear at the top level in a module as well as inside other
+scopes, in which case they are called nested procs. A nested proc can access
+local variables from its enclosing scope and if it does so it becomes a
+closure. Any captured variables are stored in a hidden additional argument
+to the closure (its environment) and they are accessed by reference by both
+the closure and its enclosing scope (i.e. any modifications made to them are
+visible in both places). The closure environment may be allocated on the heap
+or on the stack if the compiler determines that this would be safe.
+
+### Creating closures in loops
+
+Since closures capture local variables by reference it is often not wanted
+behavior inside loop bodies. See [closureScope](
+system.html#closureScope.t,untyped) and [capture](
+sugar.html#capture.m,varargs[typed],untyped) for details on how to change this behavior.
+
+Anonymous procedures
+--------------------
+
+Unnamed procedures can be used as lambda expressions to pass into other
+procedures:
+
+  ```nim
+  var cities = @["Frankfurt", "Tokyo", "New York", "Kyiv"]
+
+  cities.sort(proc (x, y: string): int =
+    cmp(x.len, y.len))
+  ```
+
+
+Procs as expressions can appear both as nested procs and inside top-level
+executable code. The  [sugar](sugar.html) module contains the `=>` macro
+which enables a more succinct syntax for anonymous procedures resembling
+lambdas as they are in languages like JavaScript, C#, etc.
+
+Do notation
+-----------
+
+As a special convenience notation that keeps most elements of a
+regular proc expression, the `do` keyword can be used to pass
+anonymous procedures to routines:
+
+  ```nim
+  var cities = @["Frankfurt", "Tokyo", "New York", "Kyiv"]
+
+  sort(cities) do (x, y: string) -> int:
+    cmp(x.len, y.len)
+
+  # Less parentheses using the method plus command syntax:
+  cities = cities.map do (x: string) -> string:
+    "City of " & x
+  ```
+
+`do` is written after the parentheses enclosing the regular proc parameters.
+The proc expression represented by the `do` block is appended to the routine
+call as the last argument. In calls using the command syntax, the `do` block
+will bind to the immediately preceding expression rather than the command call.
+
+`do` with a parameter list or pragma list corresponds to an anonymous `proc`,
+however `do` without parameters or pragmas is treated as a normal statement
+list. This allows macros to receive both indented statement lists as an
+argument in inline calls, as well as a direct mirror of Nim's routine syntax.
+
+  ```nim
+  # Passing a statement list to an inline macro:
+  macroResults.add quote do:
+    if not `ex`:
+      echo `info`, ": Check failed: ", `expString`
+
+  # Processing a routine definition in a macro:
+  rpc(router, "add") do (a, b: int) -> int:
+    result = a + b
+  ```
+
+Func
+----
+
+The `func` keyword introduces a shortcut for a `noSideEffect`:idx: proc.
+
+  ```nim
+  func binarySearch[T](a: openArray[T]; elem: T): int
+  ```
+
+Is short for:
+
+  ```nim
+  proc binarySearch[T](a: openArray[T]; elem: T): int {.noSideEffect.}
+  ```
+
+
+
+Routines
+--------
+
+A routine is a symbol of kind: `proc`, `func`, `method`, `iterator`, `macro`, `template`, `converter`.
+
+Type bound operators
+--------------------
+
+A type bound operator is a `proc` or `func` whose name starts with `=` but isn't an operator
+(i.e. containing only symbols, such as `==`). These are unrelated to setters
+(see [Properties]), which instead end in `=`.
+A type bound operator declared for a type applies to the type regardless of whether
+the operator is in scope (including if it is private).
+
+  ```nim
+  # foo.nim:
+  var witness* = 0
+  type Foo[T] = object
+  proc initFoo*(T: typedesc): Foo[T] = discard
+  proc `=destroy`[T](x: var Foo[T]) = witness.inc # type bound operator
+
+  # main.nim:
+  import foo
+  block:
+    var a = initFoo(int)
+    doAssert witness == 0
+  doAssert witness == 1
+  block:
+    var a = initFoo(int)
+    doAssert witness == 1
+    `=destroy`(a) # can be called explicitly, even without being in scope
+    doAssert witness == 2
+  # will still be called upon exiting scope
+  doAssert witness == 3
+  ```
+
+Type bound operators are:
+`=destroy`, `=copy`, `=sink`, `=trace`, `=deepcopy`, `=wasMoved`, `=dup`.
+
+These operations can be *overridden* instead of *overloaded*. This means that
+the implementation is automatically lifted to structured types. For instance,
+if the type `T` has an overridden assignment operator `=`, this operator is
+also used for assignments of the type `seq[T]`.
+
+Since these operations are bound to a type, they have to be bound to a
+nominal type for reasons of simplicity of implementation; this means an
+overridden `deepCopy` for `ref T` is really bound to `T` and not to `ref T`.
+This also means that one cannot override `deepCopy` for both `ptr T` and
+`ref T` at the same time, instead a distinct or object helper type has to be
+used for one pointer type.
+
+For more details on some of those procs, see
+[Lifetime-tracking hooks](destructors.html#lifetimeminustracking-hooks).
+
+Nonoverloadable builtins
+------------------------
+
+The following built-in procs cannot be overloaded for reasons of implementation
+simplicity (they require specialized semantic checking):
+
+    declared, defined, definedInScope, compiles, sizeof,
+    is, shallowCopy, getAst, astToStr, spawn, procCall
+
+Thus, they act more like keywords than like ordinary identifiers; unlike a
+keyword however, a redefinition may `shadow`:idx: the definition in
+the [system](system.html) module.
+From this list the following should not be written in dot
+notation `x.f` since `x` cannot be type-checked before it gets passed
+to `f`:
+
+    declared, defined, definedInScope, compiles, getAst, astToStr
+
+
+Var parameters
+--------------
+The type of a parameter may be prefixed with the `var` keyword:
+
+  ```nim
+  proc divmod(a, b: int; res, remainder: var int) =
+    res = a div b
+    remainder = a mod b
+
+  var
+    x, y: int
+
+  divmod(8, 5, x, y) # modifies x and y
+  assert x == 1
+  assert y == 3
+  ```
+
+In the example, `res` and `remainder` are `var parameters`.
+Var parameters can be modified by the procedure and the changes are
+visible to the caller. The argument passed to a var parameter has to be
+an l-value. Var parameters are implemented as hidden pointers. The
+above example is equivalent to:
+
+  ```nim
+  proc divmod(a, b: int; res, remainder: ptr int) =
+    res[] = a div b
+    remainder[] = a mod b
+
+  var
+    x, y: int
+  divmod(8, 5, addr(x), addr(y))
+  assert x == 1
+  assert y == 3
+  ```
+
+In the examples, var parameters or pointers are used to provide two
+return values. This can be done in a cleaner way by returning a tuple:
+
+  ```nim
+  proc divmod(a, b: int): tuple[res, remainder: int] =
+    (a div b, a mod b)
+
+  var t = divmod(8, 5)
+
+  assert t.res == 1
+  assert t.remainder == 3
+  ```
+
+One can use `tuple unpacking`:idx: to access the tuple's fields:
+
+  ```nim
+  var (x, y) = divmod(8, 5) # tuple unpacking
+  assert x == 1
+  assert y == 3
+  ```
+
+
+**Note**: `var` parameters are never necessary for efficient parameter
+passing. Since non-var parameters cannot be modified the compiler is always
+free to pass arguments by reference if it considers it can speed up execution.
+
+
+Var return type
+---------------
+
+A proc, converter, or iterator may return a `var` type which means that the
+returned value is an l-value and can be modified by the caller:
+
+  ```nim
+  var g = 0
+
+  proc writeAccessToG(): var int =
+    result = g
+
+  writeAccessToG() = 6
+  assert g == 6
+  ```
+
+It is a static error if the implicitly introduced pointer could be
+used to access a location beyond its lifetime:
+
+  ```nim
+  proc writeAccessToG(): var int =
+    var g = 0
+    result = g # Error!
+  ```
+
+For iterators, a component of a tuple return type can have a `var` type too:
+
+  ```nim
+  iterator mpairs(a: var seq[string]): tuple[key: int, val: var string] =
+    for i in 0..a.high:
+      yield (i, a[i])
+  ```
+
+In the standard library every name of a routine that returns a `var` type
+starts with the prefix `m` per convention.
+
+
+.. include:: manual/var_t_return.md
+
+### Future directions
+
+Later versions of Nim can be more precise about the borrowing rule with
+a syntax like:
+
+  ```nim
+  proc foo(other: Y; container: var X): var T from container
+  ```
+
+Here `var T from container` explicitly exposes that the
+location is derived from the second parameter (called
+'container' in this case). The syntax `var T from p` specifies a type
+`varTy[T, 2]` which is incompatible with `varTy[T, 1]`.
+
+
+NRVO
+----
+
+**Note**: This section describes the current implementation. This part
+of the language specification will be changed.
+See https://github.com/nim-lang/RFCs/issues/230 for more information.
+
+The return value is represented inside the body of a routine as the special
+`result`:idx: variable. This allows for a mechanism much like C++'s
+"named return value optimization" (`NRVO`:idx:). NRVO means that the stores
+to `result` inside `p` directly affect the destination `dest`
+in `let/var dest = p(args)` (definition of `dest`) and also in `dest = p(args)`
+(assignment to `dest`). This is achieved by rewriting `dest = p(args)`
+to `p'(args, dest)` where `p'` is a variation of `p` that returns `void` and
+receives a hidden mutable parameter representing `result`.
+
+Informally:
+
+  ```nim
+  proc p(): BigT = ...
+
+  var x = p()
+  x = p()
+
+  # is roughly turned into:
+
+  proc p(result: var BigT) = ...
+
+  var x; p(x)
+  p(x)
+  ```
+
+
+Let `T`'s be `p`'s return type. NRVO applies for `T`
+if `sizeof(T) >= N` (where `N` is implementation dependent),
+in other words, it applies for "big" structures.
+
+If `p` can raise an exception, NRVO applies regardless. This can produce
+observable differences in behavior:
+
+  ```nim
+  type
+    BigT = array[16, int]
+
+  proc p(raiseAt: int): BigT =
+    for i in 0..high(result):
+      if i == raiseAt: raise newException(ValueError, "interception")
+      result[i] = i
+
+  proc main =
+    var x: BigT
+    try:
+      x = p(8)
+    except ValueError:
+      doAssert x == [0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0]
+
+  main()
+  ```
+
+
+The compiler can produce a warning in these cases, however this behavior is
+turned off by default. It can be enabled for a section of code via the
+`warning[ObservableStores]` and `push`/`pop` pragmas. Take the above code
+as an example:
+
+  ```nim
+  {.push warning[ObservableStores]: on.}
+  main()
+  {.pop.}
+  ```
+
+Overloading of the subscript operator
+-------------------------------------
+
+The `[]` subscript operator for arrays/openarrays/sequences can be overloaded
+for any type (with some exceptions) by defining a routine with the name `[]`.
+
+  ```nim
+  type Foo = object
+    data: seq[int]
+  
+  proc `[]`(foo: Foo, i: int): int =
+    result = foo.data[i]
+  
+  let foo = Foo(data: @[1, 2, 3])
+  echo foo[1] # 2
+  ```
+
+Assignment to subscripts can also be overloaded by naming a routine `[]=`,
+which has precedence over assigning to the result of `[]`.
+
+  ```nim
+  type Foo = object
+    data: seq[int]
+  
+  proc `[]`(foo: Foo, i: int): int =
+    result = foo.data[i]
+  proc `[]=`(foo: var Foo, i: int, val: int) =
+    foo.data[i] = val
+  
+  var foo = Foo(data: @[1, 2, 3])
+  echo foo[1] # 2
+  foo[1] = 5
+  echo foo.data # @[1, 5, 3]
+  echo foo[1] # 5
+  ```
+
+Overloads of the subscript operator cannot be applied to routine or type
+symbols themselves, as this conflicts with the syntax for instantiating
+generic parameters, i.e. `foo[int](1, 2, 3)` or `Foo[int]`.
+
+
+Methods
+=============
+
+Procedures always use static dispatch. Methods use dynamic
+dispatch. For dynamic dispatch to work on an object it should be a reference
+type.
+
+  ```nim
+  type
+    Expression = ref object of RootObj ## abstract base class for an expression
+    Literal = ref object of Expression
+      x: int
+    PlusExpr = ref object of Expression
+      a, b: Expression
+
+  method eval(e: Expression): int {.base.} =
+    # override this base method
+    raise newException(CatchableError, "Method without implementation override")
+
+  method eval(e: Literal): int = return e.x
+
+  method eval(e: PlusExpr): int =
+    # watch out: relies on dynamic binding
+    result = eval(e.a) + eval(e.b)
+
+  proc newLit(x: int): Literal =
+    new(result)
+    result.x = x
+
+  proc newPlus(a, b: Expression): PlusExpr =
+    new(result)
+    result.a = a
+    result.b = b
+
+  echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
+  ```
+
+In the example the constructors `newLit` and `newPlus` are procs
+because they should use static binding, but `eval` is a method because it
+requires dynamic binding.
+
+As can be seen in the example, base methods have to be annotated with
+the `base`:idx: pragma. The `base` pragma also acts as a reminder for the
+programmer that a base method `m` is used as the foundation to determine all
+the effects that a call to `m` might cause.
+
+
+**Note**: Compile-time execution is not (yet) supported for methods.
+
+**Note**: Starting from Nim 0.20, generic methods are deprecated.
+
+Multi-methods
+--------------
+
+**Note:** Starting from Nim 0.20, to use multi-methods one must explicitly pass
+`--multimethods:on`:option: when compiling.
+
+In a multi-method, all parameters that have an object type are used for the
+dispatching:
+
+  ```nim  test = "nim c --multiMethods:on $1"
+  type
+    Thing = ref object of RootObj
+    Unit = ref object of Thing
+      x: int
+
+  method collide(a, b: Thing) {.base, inline.} =
+    quit "to override!"
+
+  method collide(a: Thing, b: Unit) {.inline.} =
+    echo "1"
+
+  method collide(a: Unit, b: Thing) {.inline.} =
+    echo "2"
+
+  var a, b: Unit
+  new a
+  new b
+  collide(a, b) # output: 2
+  ```
+
+Inhibit dynamic method resolution via procCall
+-----------------------------------------------
+
+Dynamic method resolution can be inhibited via the builtin `system.procCall`:idx:.
+This is somewhat comparable to the `super`:idx: keyword that traditional OOP
+languages offer.
+
+  ```nim  test = "nim c $1"
+  type
+    Thing = ref object of RootObj
+    Unit = ref object of Thing
+      x: int
+
+  method m(a: Thing) {.base.} =
+    echo "base"
+
+  method m(a: Unit) =
+    # Call the base method:
+    procCall m(Thing(a))
+    echo "1"
+  ```
+
+
+Iterators and the for statement
+===============================
+
+The `for`:idx: statement is an abstract mechanism to iterate over the elements
+of a container. It relies on an `iterator`:idx: to do so. Like `while`
+statements, `for` statements open an `implicit block`:idx: so that they
+can be left with a `break` statement.
+
+The `for` loop declares iteration variables - their scope reaches until the
+end of the loop body. The iteration variables' types are inferred by the
+return type of the iterator.
+
+An iterator is similar to a procedure, except that it can be called in the
+context of a `for` loop. Iterators provide a way to specify the iteration over
+an abstract type. The `yield` statement in the called iterator plays a key
+role in the execution of a `for` loop. Whenever a `yield` statement is
+reached, the data is bound to the `for` loop variables and control continues
+in the body of the `for` loop. The iterator's local variables and execution
+state are automatically saved between calls. Example:
+
+  ```nim
+  # this definition exists in the system module
+  iterator items*(a: string): char {.inline.} =
+    var i = 0
+    while i < len(a):
+      yield a[i]
+      inc(i)
+
+  for ch in items("hello world"): # `ch` is an iteration variable
+    echo ch
+  ```
+
+The compiler generates code as if the programmer had written this:
+
+  ```nim
+  var i = 0
+  while i < len(a):
+    var ch = a[i]
+    echo ch
+    inc(i)
+  ```
+
+If the iterator yields a tuple, there can be as many iteration variables
+as there are components in the tuple. The i'th iteration variable's type is
+the type of the i'th component. In other words, implicit tuple unpacking in a
+for loop context is supported.
+
+Implicit items/pairs invocations
+--------------------------------
+
+If the for loop expression `e` does not denote an iterator and the for loop
+has exactly 1 variable, the for loop expression is rewritten to `items(e)`;
+i.e. an `items` iterator is implicitly invoked:
+
+  ```nim
+  for x in [1,2,3]: echo x
+  ```
+
+If the for loop has exactly 2 variables, a `pairs` iterator is implicitly
+invoked.
+
+Symbol lookup of the identifiers `items`/`pairs` is performed after
+the rewriting step, so that all overloads of `items`/`pairs` are taken
+into account.
+
+
+First-class iterators
+---------------------
+
+There are 2 kinds of iterators in Nim: *inline* and *closure* iterators.
+An `inline iterator`:idx: is an iterator that's always inlined by the compiler
+leading to zero overhead for the abstraction, but may result in a heavy
+increase in code size.
+
+Caution: the body of a for loop over an inline iterator is inlined into
+each `yield` statement appearing in the iterator code,
+so ideally the code should be refactored to contain a single yield when possible
+to avoid code bloat.
+
+Inline iterators are second class citizens;
+They can be passed as parameters only to other inlining code facilities like
+templates, macros, and other inline iterators.
+
+In contrast to that, a `closure iterator`:idx: can be passed around more freely:
+
+  ```nim
+  iterator count0(): int {.closure.} =
+    yield 0
+
+  iterator count2(): int {.closure.} =
+    var x = 1
+    yield x
+    inc x
+    yield x
+
+  proc invoke(iter: iterator(): int {.closure.}) =
+    for x in iter(): echo x
+
+  invoke(count0)
+  invoke(count2)
+  ```
+
+Closure iterators and inline iterators have some restrictions:
+
+1. For now, a closure iterator cannot be executed at compile time.
+2. `return` is allowed in a closure iterator but not in an inline iterator
+   (but rarely useful) and ends the iteration.
+3. Inline iterators cannot be recursive.
+4. Neither inline nor closure iterators have the special `result` variable.
+
+Iterators that are neither marked `{.closure.}` nor `{.inline.}` explicitly
+default to being inline, but this may change in future versions of the
+implementation.
+
+The `iterator` type is always of the calling convention `closure`
+implicitly; the following example shows how to use iterators to implement
+a `collaborative tasking`:idx: system:
+
+  ```nim
+  # simple tasking:
+  type
+    Task = iterator (ticker: int)
+
+  iterator a1(ticker: int) {.closure.} =
+    echo "a1: A"
+    yield
+    echo "a1: B"
+    yield
+    echo "a1: C"
+    yield
+    echo "a1: D"
+
+  iterator a2(ticker: int) {.closure.} =
+    echo "a2: A"
+    yield
+    echo "a2: B"
+    yield
+    echo "a2: C"
+
+  proc runTasks(t: varargs[Task]) =
+    var ticker = 0
+    while true:
+      let x = t[ticker mod t.len]
+      if finished(x): break
+      x(ticker)
+      inc ticker
+
+  runTasks(a1, a2)
+  ```
+
+The builtin `system.finished` can be used to determine if an iterator has
+finished its operation; no exception is raised on an attempt to invoke an
+iterator that has already finished its work.
+
+Note that `system.finished` is error-prone to use because it only returns
+`true` one iteration after the iterator has finished:
+
+  ```nim
+  iterator mycount(a, b: int): int {.closure.} =
+    var x = a
+    while x <= b:
+      yield x
+      inc x
+
+  var c = mycount # instantiate the iterator
+  while not finished(c):
+    echo c(1, 3)
+
+  # Produces
+  1
+  2
+  3
+  0
+  ```
+
+Instead, this code has to be used:
+
+  ```nim
+  var c = mycount # instantiate the iterator
+  while true:
+    let value = c(1, 3)
+    if finished(c): break # and discard 'value'!
+    echo value
+  ```
+
+It helps to think that the iterator actually returns a
+pair `(value, done)` and `finished` is used to access the hidden `done`
+field.
+
+
+Closure iterators are *resumable functions* and so one has to provide the
+arguments to every call. To get around this limitation one can capture
+parameters of an outer factory proc:
+
+  ```nim
+  proc mycount(a, b: int): iterator (): int =
+    result = iterator (): int =
+      var x = a
+      while x <= b:
+        yield x
+        inc x
+
+  let foo = mycount(1, 4)
+
+  for f in foo():
+    echo f
+  ```
+
+The call can be made more like an inline iterator with a for loop macro:
+
+  ```nim
+  import std/macros
+  macro toItr(x: ForLoopStmt): untyped =
+    let expr = x[0]
+    let call = x[1][1] # Get foo out of toItr(foo)
+    let body = x[2]
+    result = quote do:
+      block:
+        let itr = `call`
+        for `expr` in itr():
+            `body`
+
+  for f in toItr(mycount(1, 4)): # using early `proc mycount`
+    echo f
+  ```
+
+Because of full backend function call apparatus involvement, closure iterator
+invocation is typically higher cost than inline iterators. Adornment by
+a macro wrapper at the call site like this is a possibly useful reminder.
+
+The factory `proc`, as an ordinary procedure, can be recursive. The
+above macro allows such recursion to look much like a recursive iterator
+would. For example:
+
+  ```nim
+  proc recCountDown(n: int): iterator(): int =
+    result = iterator(): int =
+      if n > 0:
+        yield n
+        for e in toItr(recCountDown(n - 1)):
+          yield e
+
+  for i in toItr(recCountDown(6)): # Emits: 6 5 4 3 2 1
+    echo i
+  ```
+
+
+See also [iterable] for passing iterators to templates and macros.
+
+Converters
+==========
+
+A converter is like an ordinary proc except that it enhances
+the "implicitly convertible" type relation (see [Convertible relation]):
+
+  ```nim
+  # bad style ahead: Nim is not C.
+  converter toBool(x: int): bool = x != 0
+
+  if 4:
+    echo "compiles"
+  ```
+
+
+A converter can also be explicitly invoked for improved readability. Note that
+implicit converter chaining is not supported: If there is a converter from
+type A to type B and from type B to type C, the implicit conversion from A to C
+is not provided.
+
+
+Type sections
+=============
+
+Example:
+
+  ```nim
+  type # example demonstrating mutually recursive types
+    Node = ref object  # an object managed by the garbage collector (ref)
+      le, ri: Node     # left and right subtrees
+      sym: ref Sym     # leaves contain a reference to a Sym
+
+    Sym = object       # a symbol
+      name: string     # the symbol's name
+      line: int        # the line the symbol was declared in
+      code: Node       # the symbol's abstract syntax tree
+  ```
+
+A type section begins with the `type` keyword. It contains multiple
+type definitions. A type definition binds a type to a name. Type definitions
+can be recursive or even mutually recursive. Mutually recursive types are only
+possible within a single `type` section. Nominal types like `objects`
+or `enums` can only be defined in a `type` section.
+
+
+
+Exception handling
+==================
+
+Try statement
+-------------
+
+Example:
+
+  ```nim
+  # read the first two lines of a text file that should contain numbers
+  # and tries to add them
+  var
+    f: File
+  if open(f, "numbers.txt"):
+    try:
+      var a = readLine(f)
+      var b = readLine(f)
+      echo "sum: " & $(parseInt(a) + parseInt(b))
+    except OverflowDefect:
+      echo "overflow!"
+    except ValueError, IOError:
+      echo "catch multiple exceptions!"
+    except CatchableError:
+      echo "Catchable exception!"
+    finally:
+      close(f)
+  ```
+
+
+The statements after the `try` are executed in sequential order unless
+an exception `e` is raised. If the exception type of `e` matches any
+listed in an `except` clause, the corresponding statements are executed.
+The statements following the `except` clauses are called
+`exception handlers`:idx:.
+
+If there is a `finally`:idx: clause, it is always executed after the
+exception handlers.
+
+The exception is *consumed* in an exception handler. However, an
+exception handler may raise another exception. If the exception is not
+handled, it is propagated through the call stack. This means that often
+the rest of the procedure - that is not within a `finally` clause -
+is not executed (if an exception occurs).
+
+
+Try expression
+--------------
+
+Try can also be used as an expression; the type of the `try` branch then
+needs to fit the types of `except` branches, but the type of the `finally`
+branch always has to be `void`:
+
+  ```nim test
+  from std/strutils import parseInt
+
+  let x = try: parseInt("133a")
+          except ValueError: -1
+          finally: echo "hi"
+  ```
+
+
+To prevent confusing code there is a parsing limitation; if the `try`
+follows a `(` it has to be written as a one liner:
+
+  ```nim test
+  from std/strutils import parseInt
+  let x = (try: parseInt("133a") except ValueError: -1)
+  ```
+
+
+Except clauses
+--------------
+
+Within an `except` clause it is possible to access the current exception
+using the following syntax:
+
+  ```nim
+  try:
+    # ...
+  except IOError as e:
+    # Now use "e"
+    echo "I/O error: " & e.msg
+  ```
+
+Alternatively, it is possible to use `getCurrentException` to retrieve the
+exception that has been raised:
+
+  ```nim
+  try:
+    # ...
+  except IOError:
+    let e = getCurrentException()
+    # Now use "e"
+  ```
+
+Note that `getCurrentException` always returns a `ref Exception`
+type. If a variable of the proper type is needed (in the example
+above, `IOError`), one must convert it explicitly:
+
+  ```nim
+  try:
+    # ...
+  except IOError:
+    let e = (ref IOError)(getCurrentException())
+    # "e" is now of the proper type
+  ```
+
+However, this is seldom needed. The most common case is to extract an
+error message from `e`, and for such situations, it is enough to use
+`getCurrentExceptionMsg`:
+
+  ```nim
+  try:
+    # ...
+  except CatchableError:
+    echo getCurrentExceptionMsg()
+  ```
+
+Custom exceptions
+-----------------
+
+It is possible to create custom exceptions. A custom exception is a custom type:
+
+  ```nim
+  type
+    LoadError* = object of Exception
+  ```
+
+Ending the custom exception's name with `Error` is recommended.
+
+Custom exceptions can be raised just like any other exception, e.g.:
+
+  ```nim
+  raise newException(LoadError, "Failed to load data")
+  ```
+
+Defer statement
+---------------
+
+Instead of a `try finally` statement a `defer` statement can be used, which
+avoids lexical nesting and offers more flexibility in terms of scoping as shown
+below.
+
+Any statements following the `defer` will be considered
+to be in an implicit try block in the current block:
+
+  ```nim  test = "nim c $1"
+  proc main =
+    var f = open("numbers.txt", fmWrite)
+    defer: close(f)
+    f.write "abc"
+    f.write "def"
+  ```
+
+Is rewritten to:
+
+  ```nim  test = "nim c $1"
+  proc main =
+    var f = open("numbers.txt")
+    try:
+      f.write "abc"
+      f.write "def"
+    finally:
+      close(f)
+  ```
+
+When `defer` is at the outermost scope of a template/macro, its scope extends
+to the block where the template/macro is called from:
+
+  ```nim  test = "nim c $1"
+  template safeOpenDefer(f, path) =
+    var f = open(path, fmWrite)
+    defer: close(f)
+
+  template safeOpenFinally(f, path, body) =
+    var f = open(path, fmWrite)
+    try: body # without `defer`, `body` must be specified as parameter
+    finally: close(f)
+
+  block:
+    safeOpenDefer(f, "/tmp/z01.txt")
+    f.write "abc"
+  block:
+    safeOpenFinally(f, "/tmp/z01.txt"):
+      f.write "abc" # adds a lexical scope
+  block:
+    var f = open("/tmp/z01.txt", fmWrite)
+    try:
+      f.write "abc" # adds a lexical scope
+    finally: close(f)
+  ```
+
+Top-level `defer` statements are not supported
+since it's unclear what such a statement should refer to.
+
+
+Raise statement
+---------------
+
+Example:
+
+  ```nim
+  raise newException(IOError, "IO failed")
+  ```
+
+Apart from built-in operations like array indexing, memory allocation, etc.
+the `raise` statement is the only way to raise an exception.
+
+.. XXX document this better!
+
+If no exception name is given, the current exception is `re-raised`:idx:. The
+`ReraiseDefect`:idx: exception is raised if there is no exception to
+re-raise. It follows that the `raise` statement *always* raises an
+exception.
+
+
+Exception hierarchy
+-------------------
+
+The exception tree is defined in the [system](system.html) module.
+Every exception inherits from `system.Exception`. Exceptions that indicate
+programming bugs inherit from `system.Defect` (which is a subtype of `Exception`)
+and are strictly speaking not catchable as they can also be mapped to an operation
+that terminates the whole process. If panics are turned into exceptions, these
+exceptions inherit from `Defect`.
+
+Exceptions that indicate any other runtime error that can be caught inherit from
+`system.CatchableError` (which is a subtype of `Exception`).
+
+```
+Exception
+|-- CatchableError
+|   |-- IOError
+|   |   `-- EOFError
+|   |-- OSError
+|   |-- ResourceExhaustedError
+|   `-- ValueError
+|       `-- KeyError
+`-- Defect
+    |-- AccessViolationDefect
+    |-- ArithmeticDefect
+    |   |-- DivByZeroDefect
+    |   `-- OverflowDefect
+    |-- AssertionDefect
+    |-- DeadThreadDefect
+    |-- FieldDefect
+    |-- FloatingPointDefect
+    |   |-- FloatDivByZeroDefect
+    |   |-- FloatInvalidOpDefect
+    |   |-- FloatOverflowDefect
+    |   |-- FloatUnderflowDefect
+    |   `-- InexactDefect
+    |-- IndexDefect
+    |-- NilAccessDefect
+    |-- ObjectAssignmentDefect
+    |-- ObjectConversionDefect
+    |-- OutOfMemoryDefect
+    |-- RangeDefect
+    |-- ReraiseDefect
+    `-- StackOverflowDefect
+```
+
+Imported exceptions
+-------------------
+
+It is possible to raise/catch imported C++ exceptions. Types imported using
+`importcpp` can be raised or caught. Exceptions are raised by value and
+caught by reference. Example:
+
+  ```nim  test = "nim cpp -r $1"
+  type
+    CStdException {.importcpp: "std::exception", header: "<exception>", inheritable.} = object
+      ## does not inherit from `RootObj`, so we use `inheritable` instead
+    CRuntimeError {.requiresInit, importcpp: "std::runtime_error", header: "<stdexcept>".} = object of CStdException
+      ## `CRuntimeError` has no default constructor => `requiresInit`
+  proc what(s: CStdException): cstring {.importcpp: "((char *)#.what())".}
+  proc initRuntimeError(a: cstring): CRuntimeError {.importcpp: "std::runtime_error(@)", constructor.}
+  proc initStdException(): CStdException {.importcpp: "std::exception()", constructor.}
+
+  proc fn() =
+    let a = initRuntimeError("foo")
+    doAssert $a.what == "foo"
+    var b: cstring
+    try: raise initRuntimeError("foo2")
+    except CStdException as e:
+      doAssert e is CStdException
+      b = e.what()
+    doAssert $b == "foo2"
+
+    try: raise initStdException()
+    except CStdException: discard
+
+    try: raise initRuntimeError("foo3")
+    except CRuntimeError as e:
+      b = e.what()
+    except CStdException:
+      doAssert false
+    doAssert $b == "foo3"
+
+  fn()
+  ```
+
+**Note:** `getCurrentException()` and `getCurrentExceptionMsg()` are not available
+for imported exceptions from C++. One needs to use the `except ImportedException as x:` syntax
+and rely on functionality of the `x` object to get exception details.
+
+
+Effect system
+=============
+
+**Note**: The rules for effect tracking changed with the release of version
+1.6 of the Nim compiler.
+
+
+Exception tracking
+------------------
+
+Nim supports exception tracking. The `raises`:idx: pragma can be used
+to explicitly define which exceptions a proc/iterator/method/converter is
+allowed to raise. The compiler verifies this:
+
+  ```nim  test = "nim c $1"
+  proc p(what: bool) {.raises: [IOError, OSError].} =
+    if what: raise newException(IOError, "IO")
+    else: raise newException(OSError, "OS")
+  ```
+
+An empty `raises` list (`raises: []`) means that no exception may be raised:
+
+  ```nim
+  proc p(): bool {.raises: [].} =
+    try:
+      unsafeCall()
+      result = true
+    except CatchableError:
+      result = false
+  ```
+
+
+A `raises` list can also be attached to a proc type. This affects type
+compatibility:
+
+  ```nim  test = "nim c $1"  status = 1
+  type
+    Callback = proc (s: string) {.raises: [IOError].}
+  var
+    c: Callback
+
+  proc p(x: string) =
+    raise newException(OSError, "OS")
+
+  c = p # type error
+  ```
+
+
+For a routine `p`, the compiler uses inference rules to determine the set of
+possibly raised exceptions; the algorithm operates on `p`'s call graph:
+
+1. Every indirect call via some proc type `T` is assumed to
+   raise `system.Exception` (the base type of the exception hierarchy) and
+   thus any exception unless `T` has an explicit `raises` list.
+   However, if the call is of the form `f(...)` where `f` is a parameter of
+   the currently analyzed routine that is marked as `.effectsOf: f`, it is ignored.
+   The call is optimistically assumed to have no effect.
+   Rule 2 compensates for this case.
+2. Every expression `e` of some proc type within a call that is passed to parameter
+   marked as `.effectsOf` of proc `p` is assumed to be called indirectly and thus
+   its raises list is added to `p`'s raises list.
+3. Every call to a proc `q` which has an unknown body (due to a forward
+   declaration) is assumed to
+   raise `system.Exception` unless `q` has an explicit `raises` list.
+   Procs that are `importc`'ed are assumed to have `.raises: []`, unless explicitly
+   declared otherwise.
+4. Every call to a method `m` is assumed to
+   raise `system.Exception` unless `m` has an explicit `raises` list.
+5. For every other call, the analysis can determine an exact `raises` list.
+6. For determining a `raises` list, the `raise` and `try` statements
+   of `p` are taken into consideration.
+
+
+Exceptions inheriting from `system.Defect` are not tracked with
+the `.raises: []` exception tracking mechanism. This is more consistent with the
+built-in operations. The following code is valid:
+
+  ```nim
+  proc mydiv(a, b): int {.raises: [].} =
+    a div b # can raise an DivByZeroDefect
+  ```
+
+And so is:
+
+  ```nim
+  proc mydiv(a, b): int {.raises: [].} =
+    if b == 0: raise newException(DivByZeroDefect, "division by zero")
+    else: result = a div b
+  ```
+
+
+The reason for this is that `DivByZeroDefect` inherits from `Defect` and
+with `--panics:on`:option: Defects become unrecoverable errors.
+(Since version 1.4 of the language.)
+
+
+EffectsOf annotation
+--------------------
+
+Rules 1-2 of the exception tracking inference rules (see the previous section)
+ensure the following works:
+
+  ```nim
+  proc weDontRaiseButMaybeTheCallback(callback: proc()) {.raises: [], effectsOf: callback.} =
+    callback()
+
+  proc doRaise() {.raises: [IOError].} =
+    raise newException(IOError, "IO")
+
+  proc use() {.raises: [].} =
+    # doesn't compile! Can raise IOError!
+    weDontRaiseButMaybeTheCallback(doRaise)
+  ```
+
+As can be seen from the example, a parameter of type `proc (...)` can be
+annotated as `.effectsOf`. Such a parameter allows for effect polymorphism:
+The proc `weDontRaiseButMaybeTheCallback` raises the exceptions
+that `callback` raises.
+
+So in many cases a callback does not cause the compiler to be overly
+conservative in its effect analysis:
+
+  ```nim  test = "nim c $1"  status = 1
+  {.push warningAsError[Effect]: on.}
+
+  import std/algorithm
+
+  type
+    MyInt = distinct int
+
+  var toSort = @[MyInt 1, MyInt 2, MyInt 3]
+
+  proc cmpN(a, b: MyInt): int =
+    cmp(a.int, b.int)
+
+  proc harmless {.raises: [].} =
+    toSort.sort cmpN
+
+  proc cmpE(a, b: MyInt): int {.raises: [Exception].} =
+    cmp(a.int, b.int)
+
+  proc harmful {.raises: [].} =
+    # does not compile, `sort` can now raise Exception
+    toSort.sort cmpE
+  ```
+
+
+
+Tag tracking
+------------
+
+Exception tracking is part of Nim's `effect system`:idx:. Raising an exception
+is an *effect*. Other effects can also be defined. A user defined effect is a
+means to *tag* a routine and to perform checks against this tag:
+
+  ```nim  test = "nim c --warningAsError:Effect:on $1"  status = 1
+  type IO = object ## input/output effect
+  proc readLine(): string {.tags: [IO].} = discard
+
+  proc no_effects_please() {.tags: [].} =
+    # the compiler prevents this:
+    let x = readLine()
+  ```
+
+A tag has to be a type name. A `tags` list - like a `raises` list - can
+also be attached to a proc type. This affects type compatibility.
+
+The inference for tag tracking is analogous to the inference for
+exception tracking.
+
+There is also a way which can be used to forbid certain effects:
+
+  ```nim  test = "nim c --warningAsError:Effect:on $1"  status = 1
+  type IO = object ## input/output effect
+  proc readLine(): string {.tags: [IO].} = discard
+  proc echoLine(): void = discard
+
+  proc no_IO_please() {.forbids: [IO].} =
+    # this is OK because it didn't define any tag:
+    echoLine()
+    # the compiler prevents this:
+    let y = readLine()
+  ```
+
+The `forbids` pragma defines a list of illegal effects - if any statement
+invokes any of those effects, the compilation will fail.
+Procedure types with any disallowed effect are the subtypes of equal
+procedure types without such lists:
+
+  ```nim
+  type MyEffect = object
+  type ProcType1 = proc (i: int): void {.forbids: [MyEffect].}
+  type ProcType2 = proc (i: int): void
+
+  proc caller1(p: ProcType1): void = p(1)
+  proc caller2(p: ProcType2): void = p(1)
+
+  proc effectful(i: int): void {.tags: [MyEffect].} = echo $i
+  proc effectless(i: int): void {.forbids: [MyEffect].} = echo $i
+
+  proc toBeCalled1(i: int): void = effectful(i)
+  proc toBeCalled2(i: int): void = effectless(i)
+
+  ## this will fail because toBeCalled1 uses MyEffect which was forbidden by ProcType1:
+  caller1(toBeCalled1)
+  ## this is OK because both toBeCalled2 and ProcType1 have the same requirements:
+  caller1(toBeCalled2)
+  ## these are OK because ProcType2 doesn't have any effect requirement:
+  caller2(toBeCalled1)
+  caller2(toBeCalled2)
+  ```
+
+`ProcType2` is a subtype of `ProcType1`. Unlike with the `tags` pragma, the parent context - the
+function which calls other functions with forbidden effects - doesn't inherit the forbidden list of effects.
+
+
+Side effects
+------------
+
+The `noSideEffect` pragma is used to mark a proc/iterator that can have only
+side effects through parameters. This means that the proc/iterator only changes locations that are
+reachable from its parameters and the return value only depends on the
+parameters. If none of its parameters have the type `var`, `ref`, `ptr`, `cstring`, or `proc`,
+then no locations are modified.
+
+In other words, a routine has no side effects if it does not access a threadlocal
+or global variable and it does not call any routine that has a side effect.
+
+It is a static error to mark a proc/iterator to have no side effect if the compiler cannot verify this.
+
+As a special semantic rule, the built-in [debugEcho](
+system.html#debugEcho,varargs[typed,]) pretends to be free of side effects
+so that it can be used for debugging routines marked as `noSideEffect`.
+
+`func` is syntactic sugar for a proc with no side effects:
+
+  ```nim
+  func `+` (x, y: int): int
+  ```
+
+
+To override the compiler's side effect analysis a `{.noSideEffect.}`
+`cast` pragma block can be used:
+
+  ```nim
+  func f() =
+    {.cast(noSideEffect).}:
+      echo "test"
+  ```
+
+**Side effects are usually inferred. The inference for side effects is
+analogous to the inference for exception tracking.**
+
+When the compiler cannot infer side effects, as is the case for imported
+functions, one can annotate them with the `sideEffect` pragma.
+
+GC safety effect
+----------------
+
+We call a proc `p` `GC safe`:idx: when it doesn't access any global variable
+that contains GC'ed memory (`string`, `seq`, `ref` or a closure) either
+directly or indirectly through a call to a GC unsafe proc.
+
+**The GC safety property is usually inferred. The inference for GC safety is
+analogous to the inference for exception tracking.**
+
+The `gcsafe`:idx: annotation can be used to mark a proc to be gcsafe,
+otherwise this property is inferred by the compiler. Note that `noSideEffect`
+implies `gcsafe`.
+
+Routines that are imported from C are always assumed to be `gcsafe`.
+
+To override the compiler's gcsafety analysis a `{.cast(gcsafe).}` pragma block can
+be used:
+
+  ```nim
+  var
+    someGlobal: string = "some string here"
+    perThread {.threadvar.}: string
+
+  proc setPerThread() =
+    {.cast(gcsafe).}:
+      deepCopy(perThread, someGlobal)
+  ```
+
+
+See also:
+
+- [Shared heap memory management](mm.html).
+
+
+
+Effects pragma
+--------------
+
+The `effects` pragma has been designed to assist the programmer with the
+effects analysis. It is a statement that makes the compiler output all inferred
+effects up to the `effects`'s position:
+
+  ```nim
+  proc p(what: bool) =
+    if what:
+      raise newException(IOError, "IO")
+      {.effects.}
+    else:
+      raise newException(OSError, "OS")
+  ```
+
+The compiler produces a hint message that `IOError` can be raised. `OSError`
+is not listed as it cannot be raised in the branch the `effects` pragma
+appears in.
+
+
+Generics
+========
+
+Generics are Nim's means to parametrize procs, iterators or types with
+`type parameters`:idx:. Depending on the context, the brackets are used either to
+introduce type parameters or to instantiate a generic proc, iterator, or type.
+
+
+The following example shows how a generic binary tree can be modeled:
+
+  ```nim  test = "nim c $1"
+  type
+    BinaryTree*[T] = ref object # BinaryTree is a generic type with
+                                # generic parameter `T`
+      le, ri: BinaryTree[T]     # left and right subtrees; may be nil
+      data: T                   # the data stored in a node
+
+  proc newNode*[T](data: T): BinaryTree[T] =
+    # constructor for a node
+    result = BinaryTree[T](le: nil, ri: nil, data: data)
+
+  proc add*[T](root: var BinaryTree[T], n: BinaryTree[T]) =
+    # insert a node into the tree
+    if root == nil:
+      root = n
+    else:
+      var it = root
+      while it != nil:
+        # compare the data items; uses the generic `cmp` proc
+        # that works for any type that has a `==` and `<` operator
+        var c = cmp(it.data, n.data)
+        if c < 0:
+          if it.le == nil:
+            it.le = n
+            return
+          it = it.le
+        else:
+          if it.ri == nil:
+            it.ri = n
+            return
+          it = it.ri
+
+  proc add*[T](root: var BinaryTree[T], data: T) =
+    # convenience proc:
+    add(root, newNode(data))
+
+  iterator preorder*[T](root: BinaryTree[T]): T =
+    # Preorder traversal of a binary tree.
+    # This uses an explicit stack (which is more efficient than
+    # a recursive iterator factory).
+    var stack: seq[BinaryTree[T]] = @[root]
+    while stack.len > 0:
+      var n = stack.pop()
+      while n != nil:
+        yield n.data
+        add(stack, n.ri)  # push right subtree onto the stack
+        n = n.le          # and follow the left pointer
+
+  var
+    root: BinaryTree[string] # instantiate a BinaryTree with `string`
+  add(root, newNode("hello")) # instantiates `newNode` and `add`
+  add(root, "world")          # instantiates the second `add` proc
+  for str in preorder(root):
+    stdout.writeLine(str)
+  ```
+
+The `T` is called a `generic type parameter`:idx: or
+a `type variable`:idx:.
+
+
+Generic Procs
+---------------
+
+Let's consider the anatomy of a generic `proc` to agree on defined terminology.
+
+```nim
+p[T: t](arg1: f): y
+```
+
+- `p`: Callee symbol
+- `[...]`: Generic parameters
+- `T: t`: Generic constraint
+- `T`: Type variable
+- `[T: t](arg1: f): y`: Formal signature
+- `arg1: f`: Formal parameter
+- `f`: Formal parameter type
+- `y`: Formal return type
+
+The use of the word "formal" here is to denote the symbols as they are defined by the programmer,
+not as they may be at compile time contextually. Since generics may be instantiated and
+types bound, we have more than one entity to think about when generics are involved.
+
+The usage of a generic will resolve the formally defined expression into an instance of that
+expression bound to only concrete types. This process is called "instantiation".
+
+Brackets at the site of a generic's formal definition specify the "constraints" as in:
+
+```nim
+type Foo[T] = object
+proc p[H;T: Foo[H]](param: T): H
+```
+
+A constraint definition may have more than one symbol defined by separating each definition by
+a `;`. Notice how `T` is composed of `H` and the return  type of `p` is defined as `H`. When this
+generic proc is instantiated `H` will be bound to a concrete type, thus making `T` concrete and
+the return type of `p` will be bound to the same concrete type used to define `H`.
+
+Brackets at the site of usage can be used to supply concrete types to instantiate the generic in the same
+order that the symbols are defined in the constraint. Alternatively, type bindings may be inferred by the compiler
+in some situations, allowing for cleaner code.
+
+
+Is operator
+-----------
+
+The `is` operator is evaluated during semantic analysis to check for type
+equivalence. It is therefore very useful for type specialization within generic
+code:
+
+  ```nim
+  type
+    Table[Key, Value] = object
+      keys: seq[Key]
+      values: seq[Value]
+      when not (Key is string): # empty value for strings used for optimization
+        deletedKeys: seq[bool]
+  ```
+
+
+Type classes
+------------
+
+A type class is a special pseudo-type that can be used to match against
+types in the context of overload resolution or the `is` operator.
+Nim supports the following built-in type classes:
+
+==================   ===================================================
+type class           matches
+==================   ===================================================
+`object`             any object type
+`tuple`              any tuple type
+`enum`               any enumeration
+`proc`               any proc type
+`iterator`           any iterator type
+`ref`                any `ref` type
+`ptr`                any `ptr` type
+`var`                any `var` type
+`distinct`           any distinct type
+`array`              any array type
+`set`                any set type
+`seq`                any seq type
+`auto`               any type
+==================   ===================================================
+
+Furthermore, every generic type automatically creates a type class of the same
+name that will match any instantiation of the generic type.
+
+Type classes can be combined using the standard boolean operators to form
+more complex type classes:
+
+  ```nim
+  # create a type class that will match all tuple and object types
+  type RecordType = (tuple or object)
+
+  proc printFields[T: RecordType](rec: T) =
+    for key, value in fieldPairs(rec):
+      echo key, " = ", value
+  ```
+
+Type constraints on generic parameters can be grouped with `,` and propagation
+stops with `;`, similarly to parameters for macros and templates:
+
+  ```nim
+  proc fn1[T; U, V: SomeFloat]() = discard # T is unconstrained
+  template fn2(t; u, v: SomeFloat) = discard # t is unconstrained
+  ```
+
+Whilst the syntax of type classes appears to resemble that of ADTs/algebraic data
+types in ML-like languages, it should be understood that type classes are static
+constraints to be enforced at type instantiations. Type classes are not really
+types in themselves but are instead a system of providing generic "checks" that
+ultimately *resolve* to some singular type. Type classes do not allow for
+runtime type dynamism, unlike object variants or methods.
+
+As an example, the following would not compile:
+
+  ```nim
+  type TypeClass = int | string
+  var foo: TypeClass = 2 # foo's type is resolved to an int here
+  foo = "this will fail" # error here, because foo is an int
+  ```
+
+Nim allows for type classes and regular types to be specified
+as `type constraints`:idx: of the generic type parameter:
+
+  ```nim
+  proc onlyIntOrString[T: int|string](x, y: T) = discard
+
+  onlyIntOrString(450, 616) # valid
+  onlyIntOrString(5.0, 0.0) # type mismatch
+  onlyIntOrString("xy", 50) # invalid as 'T' cannot be both at the same time
+  ```
+
+`proc` and `iterator` type classes also accept a calling convention pragma
+to restrict the calling convention of the matching `proc` or `iterator` type.
+
+  ```nim
+  proc onlyClosure[T: proc {.closure.}](x: T) = discard
+
+  onlyClosure(proc() = echo "hello") # valid
+  proc foo() {.nimcall.} = discard
+  onlyClosure(foo) # type mismatch
+  ```
+
+
+Implicit generics
+-----------------
+
+A type class can be used directly as the parameter's type.
+
+  ```nim
+  # create a type class that will match all tuple and object types
+  type RecordType = (tuple or object)
+
+  proc printFields(rec: RecordType) =
+    for key, value in fieldPairs(rec):
+      echo key, " = ", value
+  ```
+
+
+Procedures utilizing type classes in such a manner are considered to be
+`implicitly generic`:idx:. They will be instantiated once for each unique
+combination of parameter types used within the program.
+
+By default, during overload resolution, each named type class will bind to
+exactly one concrete type. We call such type classes `bind once`:idx: types.
+Here is an example taken directly from the system module to illustrate this:
+
+  ```nim
+  proc `==`*(x, y: tuple): bool =
+    ## requires `x` and `y` to be of the same tuple type
+    ## generic `==` operator for tuples that is lifted from the components
+    ## of `x` and `y`.
+    result = true
+    for a, b in fields(x, y):
+      if a != b: result = false
+  ```
+
+Alternatively, the `distinct` type modifier can be applied to the type class
+to allow each parameter matching the type class to bind to a different type. Such
+type classes are called `bind many`:idx: types.
+
+Procs written with the implicitly generic style will often need to refer to the
+type parameters of the matched generic type. They can be easily accessed using
+the dot syntax:
+
+  ```nim
+  type Matrix[T, Rows, Columns] = object
+    ...
+
+  proc `[]`(m: Matrix, row, col: int): Matrix.T =
+    m.data[col * high(Matrix.Columns) + row]
+  ```
+
+
+Here are more examples that illustrate implicit generics:
+
+  ```nim
+  proc p(t: Table; k: Table.Key): Table.Value
+
+  # is roughly the same as:
+
+  proc p[Key, Value](t: Table[Key, Value]; k: Key): Value
+  ```
+
+
+  ```nim
+  proc p(a: Table, b: Table)
+
+  # is roughly the same as:
+
+  proc p[Key, Value](a, b: Table[Key, Value])
+  ```
+
+
+  ```nim
+  proc p(a: Table, b: distinct Table)
+
+  # is roughly the same as:
+
+  proc p[Key, Value, KeyB, ValueB](a: Table[Key, Value], b: Table[KeyB, ValueB])
+  ```
+
+
+`typedesc` used as a parameter type also introduces an implicit
+generic. `typedesc` has its own set of rules:
+
+  ```nim
+  proc p(a: typedesc)
+
+  # is roughly the same as:
+
+  proc p[T](a: typedesc[T])
+  ```
+
+
+`typedesc` is a "bind many" type class:
+
+  ```nim
+  proc p(a, b: typedesc)
+
+  # is roughly the same as:
+
+  proc p[T, T2](a: typedesc[T], b: typedesc[T2])
+  ```
+
+
+A parameter of type `typedesc` is itself usable as a type. If it is used
+as a type, it's the underlying type. In other words, one level
+of "typedesc"-ness is stripped off:
+
+  ```nim
+  proc p(a: typedesc; b: a) = discard
+
+  # is roughly the same as:
+  proc p[T](a: typedesc[T]; b: T) = discard
+
+  # hence this is a valid call:
+  p(int, 4)
+  # as parameter 'a' requires a type, but 'b' requires a value.
+  ```
+
+
+Generic inference restrictions
+------------------------------
+
+The types `var T` and `typedesc[T]` cannot be inferred in a generic
+instantiation. The following is not allowed:
+
+  ```nim  test = "nim c $1"  status = 1
+  proc g[T](f: proc(x: T); x: T) =
+    f(x)
+
+  proc c(y: int) = echo y
+  proc v(y: var int) =
+    y += 100
+  var i: int
+
+  # allowed: infers 'T' to be of type 'int'
+  g(c, 42)
+
+  # not valid: 'T' is not inferred to be of type 'var int'
+  g(v, i)
+
+  # also not allowed: explicit instantiation via 'var int'
+  g[var int](v, i)
+  ```
+
+
+
+Symbol lookup in generics
+-------------------------
+
+### Open and Closed symbols
+
+The symbol binding rules in generics are slightly subtle: There are "open" and
+"closed" symbols. A "closed" symbol cannot be re-bound in the instantiation
+context, an "open" symbol can. Per default, overloaded symbols are open
+and every other symbol is closed.
+
+Open symbols are looked up in two different contexts: Both the context
+at definition and the context at instantiation are considered:
+
+  ```nim  test = "nim c $1"
+  type
+    Index = distinct int
+
+  proc `==` (a, b: Index): bool {.borrow.}
+
+  var a = (0, 0.Index)
+  var b = (0, 0.Index)
+
+  echo a == b # works!
+  ```
+
+In the example, the [generic `==` for tuples](system.html#%3D%3D%2CT%2CT_2) (as defined in the system module)
+uses the `==` operators of the tuple's components. However, the `==` for
+the `Index` type is defined *after* the `==` for tuples; yet the example
+compiles as the instantiation takes the currently defined symbols into account
+too.
+
+Mixin statement
+---------------
+
+A symbol can be forced to be open by a `mixin`:idx: declaration:
+
+  ```nim  test = "nim c $1"
+  proc create*[T](): ref T =
+    # there is no overloaded 'init' here, so we need to state that it's an
+    # open symbol explicitly:
+    mixin init
+    new result
+    init result
+  ```
+
+`mixin` statements only make sense in templates and generics.
+
+
+Bind statement
+--------------
+
+The `bind` statement is the counterpart to the `mixin` statement. It
+can be used to explicitly declare identifiers that should be bound early (i.e.
+the identifiers should be looked up in the scope of the template/generic
+definition):
+
+  ```nim
+  # Module A
+  var
+    lastId = 0
+
+  template genId*: untyped =
+    bind lastId
+    inc(lastId)
+    lastId
+  ```
+
+  ```nim
+  # Module B
+  import A
+
+  echo genId()
+  ```
+
+But a `bind` is rarely useful because symbol binding from the definition
+scope is the default.
+
+`bind` statements only make sense in templates and generics.
+
+
+Delegating bind statements
+--------------------------
+
+The following example outlines a problem that can arise when generic
+instantiations cross multiple different modules:
+
+  ```nim
+  # module A
+  proc genericA*[T](x: T) =
+    mixin init
+    init(x)
+  ```
+
+
+  ```nim
+  import C
+
+  # module B
+  proc genericB*[T](x: T) =
+    # Without the `bind init` statement C's init proc is
+    # not available when `genericB` is instantiated:
+    bind init
+    genericA(x)
+  ```
+
+  ```nim
+  # module C
+  type O = object
+  proc init*(x: var O) = discard
+  ```
+
+  ```nim
+  # module main
+  import B, C
+
+  genericB O()
+  ```
+
+In module B has an `init` proc from module C in its scope that is not
+taken into account when `genericB` is instantiated which leads to the
+instantiation of `genericA`. The solution is to `forward`:idx: these
+symbols by a `bind` statement inside `genericB`.
+
+
+Templates
+=========
+
+A template is a simple form of a macro: It is a simple substitution
+mechanism that operates on Nim's abstract syntax trees. It is processed in
+the semantic pass of the compiler.
+
+The syntax to *invoke* a template is the same as calling a procedure.
+
+Example:
+
+  ```nim
+  template `!=` (a, b: untyped): untyped =
+    # this definition exists in the system module
+    not (a == b)
+
+  assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6))
+  ```
+
+The `!=`, `>`, `>=`, `in`, `notin`, `isnot` operators are in fact
+templates:
+
+| `a > b` is transformed into `b < a`.
+| `a in b` is transformed into `contains(b, a)`.
+| `notin` and `isnot` have the obvious meanings.
+
+The "types" of templates can be the symbols `untyped`,
+`typed` or `typedesc`. These are "meta types", they can only be used in certain
+contexts. Regular types can be used too; this implies that `typed` expressions
+are expected.
+
+
+Typed vs untyped parameters
+---------------------------
+
+An `untyped` parameter means that symbol lookups and type resolution is not
+performed before the expression is passed to the template. This means that
+*undeclared* identifiers, for example, can be passed to the template:
+
+  ```nim  test = "nim c $1"
+  template declareInt(x: untyped) =
+    var x: int
+
+  declareInt(x) # valid
+  x = 3
+  ```
+
+
+  ```nim  test = "nim c $1"  status = 1
+  template declareInt(x: typed) =
+    var x: int
+
+  declareInt(x) # invalid, because x has not been declared and so it has no type
+  ```
+
+A template where every parameter is `untyped` is called an `immediate`:idx:
+template. For historical reasons, templates can be explicitly annotated with
+an `immediate` pragma and then these templates do not take part in
+overloading resolution and the parameters' types are *ignored* by the
+compiler. Explicit immediate templates are now deprecated.
+
+**Note**: For historical reasons, `stmt` was an alias for `typed` and
+`expr` was an alias for `untyped`, but they are removed.
+
+
+Passing a code block to a template
+----------------------------------
+
+One can pass a block of statements as the last argument to a template
+following the special `:` syntax:
+
+  ```nim  test = "nim c $1"
+  template withFile(f, fn, mode, actions: untyped): untyped =
+    var f: File
+    if open(f, fn, mode):
+      try:
+        actions
+      finally:
+        close(f)
+    else:
+      quit("cannot open: " & fn)
+
+  withFile(txt, "ttempl3.txt", fmWrite):  # special colon
+    txt.writeLine("line 1")
+    txt.writeLine("line 2")
+  ```
+
+In the example, the two `writeLine` statements are bound to the `actions`
+parameter.
+
+
+Usually, to pass a block of code to a template, the parameter that accepts
+the block needs to be of type `untyped`. Because symbol lookups are then
+delayed until template instantiation time:
+
+  ```nim  test = "nim c $1"  status = 1
+  template t(body: typed) =
+    proc p = echo "hey"
+    block:
+      body
+
+  t:
+    p()  # fails with 'undeclared identifier: p'
+  ```
+
+The above code fails with the error message that `p` is not declared.
+The reason for this is that the `p()` body is type-checked before getting
+passed to the `body` parameter and type checking in Nim implies symbol lookups.
+The same code works with `untyped` as the passed body is not required to be
+type-checked:
+
+  ```nim  test = "nim c $1"
+  template t(body: untyped) =
+    proc p = echo "hey"
+    block:
+      body
+
+  t:
+    p()  # compiles
+  ```
+
+
+Varargs of untyped
+------------------
+
+In addition to the `untyped` meta-type that prevents type checking, there is
+also `varargs[untyped]` so that not even the number of parameters is fixed:
+
+  ```nim  test = "nim c $1"
+  template hideIdentifiers(x: varargs[untyped]) = discard
+
+  hideIdentifiers(undeclared1, undeclared2)
+  ```
+
+However, since a template cannot iterate over varargs, this feature is
+generally much more useful for macros.
+
+
+Symbol binding in templates
+---------------------------
+
+A template is a `hygienic`:idx: macro and so opens a new scope. Most symbols are
+bound from the definition scope of the template:
+
+  ```nim
+  # Module A
+  var
+    lastId = 0
+
+  template genId*: untyped =
+    inc(lastId)
+    lastId
+  ```
+
+  ```nim
+  # Module B
+  import A
+
+  echo genId() # Works as 'lastId' has been bound in 'genId's defining scope
+  ```
+
+As in generics, symbol binding can be influenced via `mixin` or `bind`
+statements.
+
+
+
+Identifier construction
+-----------------------
+
+In templates, identifiers can be constructed with the backticks notation:
+
+  ```nim  test = "nim c $1"
+  template typedef(name: untyped, typ: typedesc) =
+    type
+      `T name`* {.inject.} = typ
+      `P name`* {.inject.} = ref `T name`
+
+  typedef(myint, int)
+  var x: PMyInt
+  ```
+
+In the example, `name` is instantiated with `myint`, so \`T name\` becomes
+`Tmyint`.
+
+
+Lookup rules for template parameters
+------------------------------------
+
+A parameter `p` in a template is even substituted in the expression `x.p`.
+Thus, template arguments can be used as field names and a global symbol can be
+shadowed by the same argument name even when fully qualified:
+
+  ```nim
+  # module 'm'
+
+  type
+    Lev = enum
+      levA, levB
+
+  var abclev = levB
+
+  template tstLev(abclev: Lev) =
+    echo abclev, " ", m.abclev
+
+  tstLev(levA)
+  # produces: 'levA levA'
+  ```
+
+But the global symbol can properly be captured by a `bind` statement:
+
+  ```nim
+  # module 'm'
+
+  type
+    Lev = enum
+      levA, levB
+
+  var abclev = levB
+
+  template tstLev(abclev: Lev) =
+    bind m.abclev
+    echo abclev, " ", m.abclev
+
+  tstLev(levA)
+  # produces: 'levA levB'
+  ```
+
+
+Hygiene in templates
+--------------------
+
+Per default, templates are `hygienic`:idx:\: Local identifiers declared in a
+template cannot be accessed in the instantiation context:
+
+  ```nim  test = "nim c $1"
+  template newException*(exceptn: typedesc, message: string): untyped =
+    var
+      e: ref exceptn  # e is implicitly gensym'ed here
+    new(e)
+    e.msg = message
+    e
+
+  # so this works:
+  let e = "message"
+  raise newException(IoError, e)
+  ```
+
+
+Whether a symbol that is declared in a template is exposed to the instantiation
+scope is controlled by the `inject`:idx: and `gensym`:idx: pragmas:
+`gensym`'ed symbols are not exposed but `inject`'ed symbols are.
+
+The default for symbols of entity `type`, `var`, `let` and `const`
+is `gensym`. For `proc`, `iterator`, `converter`, `template`,
+`macro`, the default is `inject`, but if a `gensym` symbol with the same name
+is defined in the same syntax-level scope, it will be `gensym` by default.
+This can be overriden by marking the routine as `inject`. 
+
+If the name of the entity is passed as a template parameter, it is an `inject`'ed symbol:
+
+  ```nim
+  template withFile(f, fn, mode: untyped, actions: untyped): untyped =
+    block:
+      var f: File  # since 'f' is a template parameter, it's injected implicitly
+      ...
+
+  withFile(txt, "ttempl3.txt", fmWrite):
+    txt.writeLine("line 1")
+    txt.writeLine("line 2")
+  ```
+
+
+The `inject` and `gensym` pragmas are second class annotations; they have
+no semantics outside a template definition and cannot be abstracted over:
+
+  ```nim
+  {.pragma myInject: inject.}
+
+  template t() =
+    var x {.myInject.}: int # does NOT work
+  ```
+
+
+To get rid of hygiene in templates, one can use the `dirty`:idx: pragma for
+a template. `inject` and `gensym` have no effect in `dirty` templates.
+
+`gensym`'ed symbols cannot be used as `field` in the `x.field` syntax.
+Nor can they be used in the `ObjectConstruction(field: value)`
+and `namedParameterCall(field = value)` syntactic constructs.
+
+The reason for this is that code like
+
+  ```nim  test = "nim c $1"
+  type
+    T = object
+      f: int
+
+  template tmp(x: T) =
+    let f = 34
+    echo x.f, T(f: 4)
+  ```
+
+
+should work as expected.
+
+However, this means that the method call syntax is not available for
+`gensym`'ed symbols:
+
+  ```nim  test = "nim c $1"  status = 1
+  template tmp(x) =
+    type
+      T {.gensym.} = int
+
+    echo x.T # invalid: instead use:  'echo T(x)'.
+
+  tmp(12)
+  ```
+
+
+Limitations of the method call syntax
+-------------------------------------
+
+The expression `x` in `x.f` needs to be semantically checked (that means
+symbol lookup and type checking) before it can be decided that it needs to be
+rewritten to `f(x)`. Therefore, the dot syntax has some limitations when it
+is used to invoke templates/macros:
+
+  ```nim  test = "nim c $1"  status = 1
+  template declareVar(name: untyped) =
+    const name {.inject.} = 45
+
+  # Doesn't compile:
+  unknownIdentifier.declareVar
+  ```
+
+
+It is also not possible to use fully qualified identifiers with module
+symbol in method call syntax. The order in which the dot operator
+binds to symbols prohibits this.
+
+  ```nim  test = "nim c $1"  status = 1
+  import std/sequtils
+
+  var myItems = @[1,3,3,7]
+  let N1 = count(myItems, 3) # OK
+  let N2 = sequtils.count(myItems, 3) # fully qualified, OK
+  let N3 = myItems.count(3) # OK
+  let N4 = myItems.sequtils.count(3) # illegal, `myItems.sequtils` can't be resolved
+  ```
+
+This means that when for some reason a procedure needs a
+disambiguation through the module name, the call needs to be
+written in function call syntax.
+
+Macros
+======
+
+A macro is a special function that is executed at compile time.
+Normally, the input for a macro is an abstract syntax
+tree (AST) of the code that is passed to it. The macro can then do
+transformations on it and return the transformed AST. This can be used to
+add custom language features and implement `domain-specific languages`:idx:.
+
+Macro invocation is a case where semantic analysis does **not** entirely proceed
+top to bottom and left to right. Instead, semantic analysis happens at least
+twice:
+
+* Semantic analysis recognizes and resolves the macro invocation.
+* The compiler executes the macro body (which may invoke other procs).
+* It replaces the AST of the macro invocation with the AST returned by the macro.
+* It repeats semantic analysis of that region of the code.
+* If the AST returned by the macro contains other macro invocations,
+  this process iterates.
+
+While macros enable advanced compile-time code transformations, they
+cannot change Nim's syntax.
+
+**Style note:** For code readability, it is best to use the least powerful
+programming construct that remains expressive. So the "check list" is:
+
+(1) Use an ordinary proc/iterator, if possible.
+(2) Else: Use a generic proc/iterator, if possible.
+(3) Else: Use a template, if possible.
+(4) Else: Use a macro.
+
+Debug example
+-------------
+
+The following example implements a powerful `debug` command that accepts a
+variable number of arguments:
+
+  ```nim  test = "nim c $1"
+  # to work with Nim syntax trees, we need an API that is defined in the
+  # `macros` module:
+  import std/macros
+
+  macro debug(args: varargs[untyped]): untyped =
+    # `args` is a collection of `NimNode` values that each contain the
+    # AST for an argument of the macro. A macro always has to
+    # return a `NimNode`. A node of kind `nnkStmtList` is suitable for
+    # this use case.
+    result = nnkStmtList.newTree()
+    # iterate over any argument that is passed to this macro:
+    for n in args:
+      # add a call to the statement list that writes the expression;
+      # `toStrLit` converts an AST to its string representation:
+      result.add newCall("write", newIdentNode("stdout"), newLit(n.repr))
+      # add a call to the statement list that writes ": "
+      result.add newCall("write", newIdentNode("stdout"), newLit(": "))
+      # add a call to the statement list that writes the expressions value:
+      result.add newCall("writeLine", newIdentNode("stdout"), n)
+
+  var
+    a: array[0..10, int]
+    x = "some string"
+  a[0] = 42
+  a[1] = 45
+
+  debug(a[0], a[1], x)
+  ```
+
+The macro call expands to:
+
+  ```nim
+  write(stdout, "a[0]")
+  write(stdout, ": ")
+  writeLine(stdout, a[0])
+
+  write(stdout, "a[1]")
+  write(stdout, ": ")
+  writeLine(stdout, a[1])
+
+  write(stdout, "x")
+  write(stdout, ": ")
+  writeLine(stdout, x)
+  ```
+
+
+Arguments that are passed to a `varargs` parameter are wrapped in an array
+constructor expression. This is why `debug` iterates over all of `args`'s
+children.
+
+
+bindSym
+-------
+
+The above `debug` macro relies on the fact that `write`, `writeLine` and
+`stdout` are declared in the system module and are thus visible in the
+instantiating context. There is a way to use bound identifiers
+(aka `symbols`:idx:) instead of using unbound identifiers. The `bindSym`
+builtin can be used for that:
+
+  ```nim  test = "nim c $1"
+  import std/macros
+
+  macro debug(n: varargs[typed]): untyped =
+    result = newNimNode(nnkStmtList, n)
+    for x in n:
+      # we can bind symbols in scope via 'bindSym':
+      add(result, newCall(bindSym"write", bindSym"stdout", toStrLit(x)))
+      add(result, newCall(bindSym"write", bindSym"stdout", newStrLitNode(": ")))
+      add(result, newCall(bindSym"writeLine", bindSym"stdout", x))
+
+  var
+    a: array[0..10, int]
+    x = "some string"
+  a[0] = 42
+  a[1] = 45
+
+  debug(a[0], a[1], x)
+  ```
+
+The macro call expands to:
+
+  ```nim
+  write(stdout, "a[0]")
+  write(stdout, ": ")
+  writeLine(stdout, a[0])
+
+  write(stdout, "a[1]")
+  write(stdout, ": ")
+  writeLine(stdout, a[1])
+
+  write(stdout, "x")
+  write(stdout, ": ")
+  writeLine(stdout, x)
+  ```
+
+In this version of `debug`, the symbols `write`, `writeLine` and `stdout`
+are already bound and are not looked up again. As the example shows, `bindSym`
+does work with overloaded symbols implicitly.
+
+Note that the symbol names passed to `bindSym` have to be constant. The
+experimental feature `dynamicBindSym` ([experimental manual](
+manual_experimental.html#dynamic-arguments-for-bindsym))
+allows this value to be computed dynamically.
+
+Post-statement blocks
+---------------------
+
+Macros can receive `of`, `elif`, `else`, `except`, `finally` and `do`
+blocks (including their different forms such as `do` with routine parameters)
+as arguments if called in statement form.
+
+  ```nim
+  macro performWithUndo(task, undo: untyped) = ...
+
+  performWithUndo do:
+    # multiple-line block of code
+    # to perform the task
+  do:
+    # code to undo it
+
+  let num = 12
+  # a single colon may be used if there is no initial block
+  match (num mod 3, num mod 5):
+  of (0, 0):
+    echo "FizzBuzz"
+  of (0, _):
+    echo "Fizz"
+  of (_, 0):
+    echo "Buzz"
+  else:
+    echo num
+  ```
+
+
+For loop macro
+--------------
+
+A macro that takes as its only input parameter an expression of the special
+type `system.ForLoopStmt` can rewrite the entirety of a `for` loop:
+
+  ```nim  test = "nim c $1"
+  import std/macros
+
+  macro example(loop: ForLoopStmt) =
+    result = newTree(nnkForStmt)    # Create a new For loop.
+    result.add loop[^3]             # This is "item".
+    result.add loop[^2][^1]         # This is "[1, 2, 3]".
+    result.add newCall(bindSym"echo", loop[0])
+
+  for item in example([1, 2, 3]): discard
+  ```
+
+Expands to:
+
+  ```nim
+  for item in items([1, 2, 3]):
+    echo item
+  ```
+
+Another example:
+
+  ```nim  test = "nim c $1"
+  import std/macros
+
+  macro enumerate(x: ForLoopStmt): untyped =
+    expectKind x, nnkForStmt
+    # check if the starting count is specified:
+    var countStart = if x[^2].len == 2: newLit(0) else: x[^2][1]
+    result = newStmtList()
+    # we strip off the first for loop variable and use it as an integer counter:
+    result.add newVarStmt(x[0], countStart)
+    var body = x[^1]
+    if body.kind != nnkStmtList:
+      body = newTree(nnkStmtList, body)
+    body.add newCall(bindSym"inc", x[0])
+    var newFor = newTree(nnkForStmt)
+    for i in 1..x.len-3:
+      newFor.add x[i]
+    # transform enumerate(X) to 'X'
+    newFor.add x[^2][^1]
+    newFor.add body
+    result.add newFor
+    # now wrap the whole macro in a block to create a new scope
+    result = quote do:
+      block: `result`
+
+  for a, b in enumerate(items([1, 2, 3])):
+    echo a, " ", b
+
+  # without wrapping the macro in a block, we'd need to choose different
+  # names for `a` and `b` here to avoid redefinition errors
+  for a, b in enumerate(10, [1, 2, 3, 5]):
+    echo a, " ", b
+  ```
+
+
+Case statement macros
+---------------------
+
+Macros named `` `case` `` can provide implementations of `case` statements
+for certain types. The following is an example of such an implementation
+for tuples, leveraging the existing equality operator for tuples
+(as provided in `system.==`):
+
+  ```nim  test = "nim c $1"
+  import std/macros
+
+  macro `case`(n: tuple): untyped =
+    result = newTree(nnkIfStmt)
+    let selector = n[0]
+    for i in 1 ..< n.len:
+      let it = n[i]
+      case it.kind
+      of nnkElse, nnkElifBranch, nnkElifExpr, nnkElseExpr:
+        result.add it
+      of nnkOfBranch:
+        for j in 0..it.len-2:
+          let cond = newCall("==", selector, it[j])
+          result.add newTree(nnkElifBranch, cond, it[^1])
+      else:
+        error "custom 'case' for tuple cannot handle this node", it
+
+  case ("foo", 78)
+  of ("foo", 78): echo "yes"
+  of ("bar", 88): echo "no"
+  else: discard
+  ```
+
+`case` macros are subject to overload resolution. The type of the
+`case` statement's selector expression is matched against the type
+of the first argument of the `case` macro. Then the complete `case`
+statement is passed in place of the argument and the macro is evaluated.
+
+In other words, the macro needs to transform the full `case` statement
+but only the statement's selector expression is used to determine which
+macro to call.
+
+
+Special Types
+=============
+
+static\[T]
+----------
+
+As their name suggests, static parameters must be constant expressions:
+
+  ```nim
+  proc precompiledRegex(pattern: static string): RegEx =
+    var res {.global.} = re(pattern)
+    return res
+
+  precompiledRegex("/d+") # Replaces the call with a precompiled
+                          # regex, stored in a global variable
+
+  precompiledRegex(paramStr(1)) # Error, command-line options
+                                # are not constant expressions
+  ```
+
+
+For the purposes of code generation, all static parameters are treated as
+generic parameters - the proc will be compiled separately for each unique
+supplied value (or combination of values).
+
+Static parameters can also appear in the signatures of generic types:
+
+  ```nim
+  type
+    Matrix[M,N: static int; T: Number] = array[0..(M*N - 1), T]
+      # Note how `Number` is just a type constraint here, while
+      # `static int` requires us to supply an int value
+
+    AffineTransform2D[T] = Matrix[3, 3, T]
+    AffineTransform3D[T] = Matrix[4, 4, T]
+
+  var m1: AffineTransform3D[float]  # OK
+  var m2: AffineTransform2D[string] # Error, `string` is not a `Number`
+  ```
+
+Please note that `static T` is just a syntactic convenience for the underlying
+generic type `static[T]`. The type parameter can be omitted to obtain the type
+class of all constant expressions. A more specific type class can be created by
+instantiating `static` with another type class.
+
+One can force an expression to be evaluated at compile time as a constant
+expression by coercing it to a corresponding `static` type:
+
+  ```nim
+  import std/math
+
+  echo static(fac(5)), " ", static[bool](16.isPowerOfTwo)
+  ```
+
+The compiler will report any failure to evaluate the expression or a
+possible type mismatch error.
+
+typedesc\[T]
+------------
+
+In many contexts, Nim treats the names of types as regular
+values. These values exist only during the compilation phase, but since
+all values must have a type, `typedesc` is considered their special type.
+
+`typedesc` acts as a generic type. For instance, the type of the symbol
+`int` is `typedesc[int]`. Just like with regular generic types, when the
+generic parameter is omitted, `typedesc` denotes the type class of all types.
+As a syntactic convenience, one can also use `typedesc` as a modifier.
+
+Procs featuring `typedesc` parameters are considered implicitly generic.
+They will be instantiated for each unique combination of supplied types,
+and within the body of the proc, the name of each parameter will refer to
+the bound concrete type:
+
+  ```nim
+  proc new(T: typedesc): ref T =
+    echo "allocating ", T.name
+    new(result)
+
+  var n = Node.new
+  var tree = new(BinaryTree[int])
+  ```
+
+When multiple type parameters are present, they will bind freely to different
+types. To force a bind-once behavior, one can use an explicit generic parameter:
+
+  ```nim
+  proc acceptOnlyTypePairs[T, U](A, B: typedesc[T]; C, D: typedesc[U])
+  ```
+
+Once bound, type parameters can appear in the rest of the proc signature:
+
+  ```nim  test = "nim c $1"
+  template declareVariableWithType(T: typedesc, value: T) =
+    var x: T = value
+
+  declareVariableWithType int, 42
+  ```
+
+
+Overload resolution can be further influenced by constraining the set
+of types that will match the type parameter. This works in practice by
+attaching attributes to types via templates. The constraint can be a
+concrete type or a type class.
+
+  ```nim  test = "nim c $1"
+  template maxval(T: typedesc[int]): int = high(int)
+  template maxval(T: typedesc[float]): float = Inf
+
+  var i = int.maxval
+  var f = float.maxval
+  when false:
+    var s = string.maxval # error, maxval is not implemented for string
+
+  template isNumber(t: typedesc[object]): string = "Don't think so."
+  template isNumber(t: typedesc[SomeInteger]): string = "Yes!"
+  template isNumber(t: typedesc[SomeFloat]): string = "Maybe, could be NaN."
+
+  echo "is int a number? ", isNumber(int)
+  echo "is float a number? ", isNumber(float)
+  echo "is RootObj a number? ", isNumber(RootObj)
+  ```
+
+Passing `typedesc` is almost identical, just with the difference that
+the macro is not instantiated generically. The type expression is
+simply passed as a `NimNode` to the macro, like everything else.
+
+  ```nim
+  import std/macros
+
+  macro forwardType(arg: typedesc): typedesc =
+    # `arg` is of type `NimNode`
+    let tmp: NimNode = arg
+    result = tmp
+
+  var tmp: forwardType(int)
+  ```
+
+typeof operator
+---------------
+
+**Note**: `typeof(x)` can for historical reasons also be written as
+`type(x)` but `type(x)` is discouraged.
+
+One can obtain the type of a given expression by constructing a `typeof`
+value from it (in many other languages this is known as the `typeof`:idx:
+operator):
+
+  ```nim
+  var x = 0
+  var y: typeof(x) # y has type int
+  ```
+
+
+If `typeof` is used to determine the result type of a proc/iterator/converter
+call `c(X)` (where `X` stands for a possibly empty list of arguments), the
+interpretation, where `c` is an iterator, is preferred over the
+other interpretations, but this behavior can be changed by
+passing `typeOfProc` as the second argument to `typeof`:
+
+  ```nim  test = "nim c $1"
+  iterator split(s: string): string = discard
+  proc split(s: string): seq[string] = discard
+
+  # since an iterator is the preferred interpretation, this has the type `string`:
+  assert typeof("a b c".split) is string
+
+  assert typeof("a b c".split, typeOfProc) is seq[string]
+  ```
+
+
+
+Modules
+=======
+Nim supports splitting a program into pieces by a module concept.
+Each module needs to be in its own file and has its own `namespace`:idx:.
+Modules enable `information hiding`:idx: and `separate compilation`:idx:.
+A module may gain access to the symbols of another module by the `import`:idx:
+statement. `Recursive module dependencies`:idx: are allowed, but are slightly
+subtle. Only top-level symbols that are marked with an asterisk (`*`) are
+exported. A valid module name can only be a valid Nim identifier (and thus its
+filename is ``identifier.nim``).
+
+The algorithm for compiling modules is:
+
+- Compile the whole module as usual, following import statements recursively.
+
+- If there is a cycle, only import the already parsed symbols (that are
+  exported); if an unknown identifier occurs then abort.
+
+This is best illustrated by an example:
+
+  ```nim
+  # Module A
+  type
+    T1* = int  # Module A exports the type `T1`
+  import B     # the compiler starts parsing B
+
+  proc main() =
+    var i = p(3) # works because B has been parsed completely here
+
+  main()
+  ```
+
+
+  ```nim
+  # Module B
+  import A  # A is not parsed here! Only the already known symbols
+            # of A are imported.
+
+  proc p*(x: A.T1): A.T1 =
+    # this works because the compiler has already
+    # added T1 to A's interface symbol table
+    result = x + 1
+  ```
+
+
+Import statement
+----------------
+
+After the `import` keyword, a list of module names can follow or a single
+module name followed by an `except` list to prevent some symbols from being
+imported:
+
+  ```nim  test = "nim c $1"  status = 1
+  import std/strutils except `%`, toUpperAscii
+
+  # doesn't work then:
+  echo "$1" % "abc".toUpperAscii
+  ```
+
+
+It is not checked that the `except` list is really exported from the module.
+This feature allows us to compile against different versions of the module,
+even when one version does not export some of these identifiers.
+
+The `import` statement is only allowed at the top level.
+
+String literals can be used for import/include statements.
+The compiler performs [path substitution](nimc.html#compiler-usage-commandminusline-switches) when used.
+
+Include statement
+-----------------
+
+The `include` statement does something fundamentally different than
+importing a module: it merely includes the contents of a file. The `include`
+statement is useful to split up a large module into several files:
+
+  ```nim
+  include fileA, fileB, fileC
+  ```
+
+The `include` statement can be used outside the top level, as such:
+
+  ```nim
+  # Module A
+  echo "Hello World!"
+  ```
+
+  ```nim
+  # Module B
+  proc main() =
+    include A
+
+  main() # => Hello World!
+  ```
+
+
+Module names in imports
+-----------------------
+
+A module alias can be introduced via the `as` keyword, after which the original module name
+is inaccessible:
+
+  ```nim
+  import std/strutils as su, std/sequtils as qu
+
+  echo su.format("$1", "lalelu")
+  ```
+
+The notations `path/to/module` or `"path/to/module"` can be used to refer to a module
+in subdirectories:
+
+  ```nim
+  import lib/pure/os, "lib/pure/times"
+  ```
+
+Note that the module name is still `strutils` and not `lib/pure/strutils`,
+thus one **cannot** do:
+
+  ```nim
+  import lib/pure/strutils
+  echo lib/pure/strutils.toUpperAscii("abc")
+  ```
+
+Likewise, the following does not make sense as the name is `strutils` already:
+
+  ```nim
+  import lib/pure/strutils as strutils
+  ```
+
+
+Collective imports from a directory
+-----------------------------------
+
+The syntax `import dir / [moduleA, moduleB]` can be used to import multiple modules
+from the same directory.
+
+Path names are syntactically either Nim identifiers or string literals. If the path
+name is not a valid Nim identifier it needs to be a string literal:
+
+  ```nim
+  import "gfx/3d/somemodule" # in quotes because '3d' is not a valid Nim identifier
+  ```
+
+
+Pseudo import/include paths
+---------------------------
+
+A directory can also be a so-called "pseudo directory". They can be used to
+avoid ambiguity when there are multiple modules with the same path.
+
+There are two pseudo directories:
+
+1. `std`: The `std` pseudo directory is the abstract location of Nim's standard
+   library. For example, the syntax `import std / strutils` is used to unambiguously
+   refer to the standard library's `strutils` module.
+2. `pkg`: The `pkg` pseudo directory is used to unambiguously refer to a Nimble
+   package. However, for technical details that lie outside the scope of this document,
+   its semantics are: *Use the search path to look for module name but ignore the standard
+   library locations*. In other words, it is the opposite of `std`.
+
+It is recommended and preferred but not currently enforced that all stdlib module imports include the std/ "pseudo directory" as part of the import name.
+
+From import statement
+---------------------
+
+After the `from` keyword, a module name followed by
+an `import` to list the symbols one likes to use without explicit
+full qualification:
+
+  ```nim  test = "nim c $1"
+  from std/strutils import `%`
+
+  echo "$1" % "abc"
+  # always possible: full qualification:
+  echo strutils.replace("abc", "a", "z")
+  ```
+
+It's also possible to use `from module import nil` if one wants to import
+the module but wants to enforce fully qualified access to every symbol
+in `module`.
+
+
+Export statement
+----------------
+
+An `export` statement can be used for symbol forwarding so that client
+modules don't need to import a module's dependencies:
+
+  ```nim
+  # module B
+  type MyObject* = object
+  ```
+
+  ```nim
+  # module A
+  import B
+  export B.MyObject
+
+  proc `$`*(x: MyObject): string = "my object"
+  ```
+
+
+  ```nim
+  # module C
+  import A
+
+  # B.MyObject has been imported implicitly here:
+  var x: MyObject
+  echo $x
+  ```
+
+When the exported symbol is another module, all of its definitions will
+be forwarded. One can use an `except` list to exclude some of the symbols.
+
+Notice that when exporting, one needs to specify only the module name:
+
+  ```nim
+  import foo/bar/baz
+  export baz
+  ```
+
+
+
+Scope rules
+-----------
+Identifiers are valid from the point of their declaration until the end of
+the block in which the declaration occurred. The range where the identifier
+is known is the scope of the identifier. The exact scope of an
+identifier depends on the way it was declared.
+
+### Block scope
+
+The *scope* of a variable declared in the declaration part of a block
+is valid from the point of declaration until the end of the block. If a
+block contains a second block, in which the identifier is redeclared,
+then inside this block, the second declaration will be valid. Upon
+leaving the inner block, the first declaration is valid again. An
+identifier cannot be redefined in the same block, except if valid for
+procedure or iterator overloading purposes.
+
+
+### Tuple or object scope
+
+The field identifiers inside a tuple or object definition are valid in the
+following places:
+
+* To the end of the tuple/object definition.
+* Field designators of a variable of the given tuple/object type.
+* In all descendant types of the object type.
+
+### Module scope
+
+All identifiers of a module are valid from the point of declaration until
+the end of the module. Identifiers from indirectly dependent modules are *not*
+available. The `system`:idx: module is automatically imported in every module.
+
+If a module imports the same identifier from two different modules, the
+identifier is considered ambiguous, which can be resolved in the following ways:
+
+* Qualifying the identifier as `module.identifier` resolves ambiguity
+  between modules. (See below for the case that the module name itself
+  is ambiguous.)
+* Calling the identifier as a routine makes overload resolution take place,
+  which resolves ambiguity in the case that one overload matches stronger
+  than the others.
+* Using the identifier in a context where the compiler can infer the type
+  of the identifier resolves ambiguity in the case that one definition
+  matches the type stronger than the others.
+
+  ```nim
+  # Module A
+  var x*: string
+  proc foo*(a: string) =
+    echo "A: ", a
+  ```
+
+  ```nim
+  # Module B
+  var x*: int
+  proc foo*(b: int) =
+    echo "B: ", b
+  ```
+
+  ```nim
+  # Module C
+  import A, B
+
+  foo("abc") # A: abc
+  foo(123) # B: 123
+  let inferred: proc (x: string) = foo
+  foo("def") # A: def
+
+  write(stdout, x) # error: x is ambiguous
+  write(stdout, A.x) # no error: qualifier used
+  
+  proc bar(a: int): int = a + 1
+  assert bar(x) == x + 1 # no error: only A.x of type int matches
+
+  var x = 4
+  write(stdout, x) # not ambiguous: uses the module C's x
+  ```
+Modules can share their name, however, when trying to qualify an identifier with the module name the compiler will fail with ambiguous identifier error. One can qualify the identifier by aliasing the module.
+
+
+```nim
+# Module A/C
+proc fb* = echo "fizz"
+```
+
+
+```nim
+# Module B/C
+proc fb* = echo "buzz"
+```
+
+
+```nim
+import A/C
+import B/C
+
+C.fb() # Error: ambiguous identifier: 'C'
+```
+
+
+```nim
+import A/C as fizz
+import B/C
+
+fizz.fb() # Works
+```
+
+
+Packages
+--------
+A collection of modules in a file tree with an ``identifier.nimble`` file in the
+root of the tree is called a Nimble package. A valid package name can only be a
+valid Nim identifier and thus its filename is ``identifier.nimble`` where
+``identifier`` is the desired package name. A module without a ``.nimble`` file
+is assigned the package identifier: `unknown`.
+
+The distinction between packages allows diagnostic compiler messages to be
+scoped to the current project's package vs foreign packages.
+
+
+
+Compiler Messages
+=================
+
+The Nim compiler emits different kinds of messages: `hint`:idx:,
+`warning`:idx:, and `error`:idx: messages. An *error* message is emitted if
+the compiler encounters any static error.
+
+
+
+Pragmas
+=======
+
+Pragmas are Nim's method to give the compiler additional information /
+commands without introducing a massive number of new keywords. Pragmas are
+processed on the fly during semantic checking. Pragmas are enclosed in the
+special `{.` and `.}` curly brackets. Pragmas are also often used as a
+first implementation to play with a language feature before a nicer syntax
+to access the feature becomes available.
+
+
+deprecated pragma
+-----------------
+
+The deprecated pragma is used to mark a symbol as deprecated:
+
+  ```nim
+  proc p() {.deprecated.}
+  var x {.deprecated.}: char
+  ```
+
+This pragma can also take in an optional warning string to relay to developers.
+
+  ```nim
+  proc thing(x: bool) {.deprecated: "use thong instead".}
+  ```
+
+
+
+compileTime pragma
+------------------
+The `compileTime` pragma is used to mark a proc or variable to be used only
+during compile-time execution. No code will be generated for it. Compile-time
+procs are useful as helpers for macros. Since version 0.12.0 of the language, a
+proc that uses `system.NimNode` within its parameter types is implicitly
+declared `compileTime`:
+
+  ```nim
+  proc astHelper(n: NimNode): NimNode =
+    result = n
+  ```
+
+Is the same as:
+
+  ```nim
+  proc astHelper(n: NimNode): NimNode {.compileTime.} =
+    result = n
+  ```
+
+`compileTime` variables are available at runtime too. This simplifies certain
+idioms where variables are filled at compile-time (for example, lookup tables)
+but accessed at runtime:
+
+  ```nim  test = "nim c -r $1"
+  import std/macros
+
+  var nameToProc {.compileTime.}: seq[(string, proc (): string {.nimcall.})]
+
+  macro registerProc(p: untyped): untyped =
+    result = newTree(nnkStmtList, p)
+
+    let procName = p[0]
+    let procNameAsStr = $p[0]
+    result.add quote do:
+      nameToProc.add((`procNameAsStr`, `procName`))
+
+  proc foo: string {.registerProc.} = "foo"
+  proc bar: string {.registerProc.} = "bar"
+  proc baz: string {.registerProc.} = "baz"
+
+  doAssert nameToProc[2][1]() == "baz"
+  ```
+
+
+noreturn pragma
+---------------
+The `noreturn` pragma is used to mark a proc that never returns.
+
+
+acyclic pragma
+--------------
+The `acyclic` pragma can be used for object types to mark them as acyclic
+even though they seem to be cyclic. This is an **optimization** for the garbage
+collector to not consider objects of this type as part of a cycle:
+
+  ```nim
+  type
+    Node = ref NodeObj
+    NodeObj {.acyclic.} = object
+      left, right: Node
+      data: string
+  ```
+
+Or if we directly use a ref object:
+
+  ```nim
+  type
+    Node {.acyclic.} = ref object
+      left, right: Node
+      data: string
+  ```
+
+In the example, a tree structure is declared with the `Node` type. Note that
+the type definition is recursive and the GC has to assume that objects of
+this type may form a cyclic graph. The `acyclic` pragma passes the
+information that this cannot happen to the GC. If the programmer uses the
+`acyclic` pragma for data types that are in reality cyclic, this may result
+in memory leaks, but memory safety is preserved.
+
+
+
+final pragma
+------------
+The `final` pragma can be used for an object type to specify that it
+cannot be inherited from. Note that inheritance is only available for
+objects that inherit from an existing object (via the `object of SuperType`
+syntax) or that have been marked as `inheritable`.
+
+
+shallow pragma
+--------------
+The `shallow` pragma affects the semantics of a type: The compiler is
+allowed to make a shallow copy. This can cause serious semantic issues and
+break memory safety! However, it can speed up assignments considerably,
+because the semantics of Nim require deep copying of sequences and strings.
+This can be expensive, especially if sequences are used to build a tree
+structure:
+
+  ```nim
+  type
+    NodeKind = enum nkLeaf, nkInner
+    Node {.shallow.} = object
+      case kind: NodeKind
+      of nkLeaf:
+        strVal: string
+      of nkInner:
+        children: seq[Node]
+  ```
+
+
+pure pragma
+-----------
+An object type can be marked with the `pure` pragma so that its type field
+which is used for runtime type identification is omitted. This used to be
+necessary for binary compatibility with other compiled languages.
+
+An enum type can be marked as `pure`. Then access of its fields always
+requires full qualification.
+
+
+asmNoStackFrame pragma
+----------------------
+A proc can be marked with the `asmNoStackFrame` pragma to tell the compiler
+it should not generate a stack frame for the proc. There are also no exit
+statements like `return result;` generated and the generated C function is
+declared as `__declspec(naked)`:c: or `__attribute__((naked))`:c: (depending on
+the used C compiler).
+
+**Note**: This pragma should only be used by procs which consist solely of
+assembler statements.
+
+error pragma
+------------
+The `error` pragma is used to make the compiler output an error message
+with the given content. The compilation does not necessarily abort after an error
+though.
+
+The `error` pragma can also be used to
+annotate a symbol (like an iterator or proc). The *usage* of the symbol then
+triggers a static error. This is especially useful to rule out that some
+operation is valid due to overloading and type conversions:
+
+  ```nim
+  ## check that underlying int values are compared and not the pointers:
+  proc `==`(x, y: ptr int): bool {.error.}
+  ```
+
+
+fatal pragma
+------------
+The `fatal` pragma is used to make the compiler output an error message
+with the given content. In contrast to the `error` pragma, the compilation
+is guaranteed to be aborted by this pragma. Example:
+
+  ```nim
+  when not defined(objc):
+    {.fatal: "Compile this program with the objc command!".}
+  ```
+
+warning pragma
+--------------
+The `warning` pragma is used to make the compiler output a warning message
+with the given content. Compilation continues after the warning.
+
+hint pragma
+-----------
+The `hint` pragma is used to make the compiler output a hint message with
+the given content. Compilation continues after the hint.
+
+line pragma
+-----------
+The `line` pragma can be used to affect line information of the annotated
+statement, as seen in stack backtraces:
+
+  ```nim
+  template myassert*(cond: untyped, msg = "") =
+    if not cond:
+      # change run-time line information of the 'raise' statement:
+      {.line: instantiationInfo().}:
+        raise newException(AssertionDefect, msg)
+  ```
+
+If the `line` pragma is used with a parameter, the parameter needs to be a
+`tuple[filename: string, line: int]`. If it is used without a parameter,
+`system.instantiationInfo()` is used.
+
+
+linearScanEnd pragma
+--------------------
+The `linearScanEnd` pragma can be used to tell the compiler how to
+compile a Nim `case`:idx: statement. Syntactically it has to be used as a
+statement:
+
+  ```nim
+  case myInt
+  of 0:
+    echo "most common case"
+  of 1:
+    {.linearScanEnd.}
+    echo "second most common case"
+  of 2: echo "unlikely: use branch table"
+  else: echo "unlikely too: use branch table for ", myInt
+  ```
+
+In the example, the case branches `0` and `1` are much more common than
+the other cases. Therefore, the generated assembler code should test for these
+values first so that the CPU's branch predictor has a good chance to succeed
+(avoiding an expensive CPU pipeline stall). The other cases might be put into a
+jump table for O(1) overhead but at the cost of a (very likely) pipeline
+stall.
+
+The `linearScanEnd` pragma should be put into the last branch that should be
+tested against via linear scanning. If put into the last branch of the
+whole `case` statement, the whole `case` statement uses linear scanning.
+
+
+computedGoto pragma
+-------------------
+The `computedGoto` pragma can be used to tell the compiler how to
+compile a Nim `case`:idx: in a `while true` statement.
+Syntactically it has to be used as a statement inside the loop:
+
+  ```nim
+  type
+    MyEnum = enum
+      enumA, enumB, enumC, enumD, enumE
+
+  proc vm() =
+    var instructions: array[0..100, MyEnum]
+    instructions[2] = enumC
+    instructions[3] = enumD
+    instructions[4] = enumA
+    instructions[5] = enumD
+    instructions[6] = enumC
+    instructions[7] = enumA
+    instructions[8] = enumB
+
+    instructions[12] = enumE
+    var pc = 0
+    while true:
+      {.computedGoto.}
+      let instr = instructions[pc]
+      case instr
+      of enumA:
+        echo "yeah A"
+      of enumC, enumD:
+        echo "yeah CD"
+      of enumB:
+        echo "yeah B"
+      of enumE:
+        break
+      inc(pc)
+
+  vm()
+  ```
+
+As the example shows, `computedGoto` is mostly useful for interpreters. If
+the underlying backend (C compiler) does not support the computed goto
+extension the pragma is simply ignored.
+
+
+immediate pragma
+----------------
+
+The immediate pragma is obsolete. See [Typed vs untyped parameters].
+
+redefine pragma
+---------------
+
+Redefinition of template symbols with the same signature is allowed.
+This can be made explicit with the `redefine` pragma:
+
+```nim
+template foo: int = 1
+echo foo() # 1
+template foo: int {.redefine.} = 2
+echo foo() # 2
+# warning: implicit redefinition of template
+template foo: int = 3
+```
+
+This is mostly intended for macro generated code.
+
+compilation option pragmas
+--------------------------
+The listed pragmas here can be used to override the code generation options
+for a proc/method/converter.
+
+The implementation currently provides the following possible options (various
+others may be added later).
+
+===============  ===============  ============================================
+pragma           allowed values   description
+===============  ===============  ============================================
+checks           on|off           Turns the code generation for all runtime
+                                  checks on or off.
+boundChecks      on|off           Turns the code generation for array bound
+                                  checks on or off.
+overflowChecks   on|off           Turns the code generation for over- or
+                                  underflow checks on or off.
+nilChecks        on|off           Turns the code generation for nil pointer
+                                  checks on or off.
+assertions       on|off           Turns the code generation for assertions
+                                  on or off.
+warnings         on|off           Turns the warning messages of the compiler
+                                  on or off.
+hints            on|off           Turns the hint messages of the compiler
+                                  on or off.
+optimization     none|speed|size  Optimize the code for speed or size, or
+                                  disable optimization.
+patterns         on|off           Turns the term rewriting templates/macros
+                                  on or off.
+callconv         cdecl|...        Specifies the default calling convention for
+                                  all procedures (and procedure types) that
+                                  follow.
+===============  ===============  ============================================
+
+Example:
+
+  ```nim
+  {.checks: off, optimization: speed.}
+  # compile without runtime checks and optimize for speed
+  ```
+
+
+push and pop pragmas
+--------------------
+The `push/pop`:idx: pragmas are very similar to the option directive,
+but are used to override the settings temporarily. Example:
+
+  ```nim
+  {.push checks: off.}
+  # compile this section without runtime checks as it is
+  # speed critical
+  # ... some code ...
+  {.pop.} # restore old settings
+  ```
+
+`push/pop`:idx: can switch on/off some standard library pragmas, example:
+
+  ```nim
+  {.push inline.}
+  proc thisIsInlined(): int = 42
+  func willBeInlined(): float = 42.0
+  {.pop.}
+  proc notInlined(): int = 9
+
+  {.push discardable, boundChecks: off, compileTime, noSideEffect, experimental.}
+  template example(): string = "https://nim-lang.org"
+  {.pop.}
+
+  {.push deprecated, used, stackTrace: off.}
+  proc sample(): bool = true
+  {.pop.}
+  ```
+
+For third party pragmas, it depends on its implementation but uses the same syntax.
+
+
+register pragma
+---------------
+The `register` pragma is for variables only. It declares the variable as
+`register`, giving the compiler a hint that the variable should be placed
+in a hardware register for faster access. C compilers usually ignore this
+though and for good reasons: Often they do a better job without it anyway.
+
+However, in highly specific cases (a dispatch loop of a bytecode interpreter
+for example) it may provide benefits.
+
+
+global pragma
+-------------
+The `global` pragma can be applied to a variable within a proc to instruct
+the compiler to store it in a global location and initialize it once at program
+startup.
+
+  ```nim
+  proc isHexNumber(s: string): bool =
+    var pattern {.global.} = re"[0-9a-fA-F]+"
+    result = s.match(pattern)
+  ```
+
+When used within a generic proc, a separate unique global variable will be
+created for each instantiation of the proc. The order of initialization of
+the created global variables within a module is not defined, but all of them
+will be initialized after any top-level variables in their originating module
+and before any variable in a module that imports it.
+
+Disabling certain messages
+--------------------------
+Nim generates some warnings and hints that may annoy the
+user. A mechanism for disabling certain messages is provided: Each hint
+and warning message is associated with a symbol. This is the message's
+identifier, which can be used to enable or disable the message by putting it
+in brackets following the pragma:
+
+  ```Nim
+  {.hint[XDeclaredButNotUsed]: off.} # Turn off the hint about declared but not used symbols.
+  ```
+
+This is often better than disabling all warnings at once.
+
+
+used pragma
+-----------
+
+Nim produces a warning for symbols that are not exported and not used either.
+The `used` pragma can be attached to a symbol to suppress this warning. This
+is particularly useful when the symbol was generated by a macro:
+
+  ```nim
+  template implementArithOps(T) =
+    proc echoAdd(a, b: T) {.used.} =
+      echo a + b
+    proc echoSub(a, b: T) {.used.} =
+      echo a - b
+
+  # no warning produced for the unused 'echoSub'
+  implementArithOps(int)
+  echoAdd 3, 5
+  ```
+
+`used` can also be used as a top-level statement to mark a module as "used".
+This prevents the "Unused import" warning:
+
+  ```nim
+  # module: debughelper.nim
+  when defined(nimHasUsed):
+    # 'import debughelper' is so useful for debugging
+    # that Nim shouldn't produce a warning for that import,
+    # even if currently unused:
+    {.used.}
+  ```
+
+
+experimental pragma
+-------------------
+
+The `experimental` pragma enables experimental language features. Depending
+on the concrete feature, this means that the feature is either considered
+too unstable for an otherwise stable release or that the future of the feature
+is uncertain (it may be removed at any time). See the
+[experimental manual](manual_experimental.html) for more details.
+
+Example:
+
+  ```nim
+  import std/threadpool
+  {.experimental: "parallel".}
+
+  proc threadedEcho(s: string, i: int) =
+    echo(s, " ", $i)
+
+  proc useParallel() =
+    parallel:
+      for i in 0..4:
+        spawn threadedEcho("echo in parallel", i)
+
+  useParallel()
+  ```
+
+
+As a top-level statement, the experimental pragma enables a feature for the
+rest of the module it's enabled in. This is problematic for macro and generic
+instantiations that cross a module scope. Currently, these usages have to be
+put into a `.push/pop` environment:
+
+  ```nim
+  # client.nim
+  proc useParallel*[T](unused: T) =
+    # use a generic T here to show the problem.
+    {.push experimental: "parallel".}
+    parallel:
+      for i in 0..4:
+        echo "echo in parallel"
+
+    {.pop.}
+  ```
+
+
+  ```nim
+  import client
+  useParallel(1)
+  ```
+
+
+Implementation Specific Pragmas
+===============================
+
+This section describes additional pragmas that the current Nim implementation
+supports but which should not be seen as part of the language specification.
+
+Bitsize pragma
+--------------
+
+The `bitsize` pragma is for object field members. It declares the field as
+a bitfield in C/C++.
+
+  ```Nim
+  type
+    mybitfield = object
+      flag {.bitsize:1.}: cuint
+  ```
+
+generates:
+
+  ```C
+  struct mybitfield {
+    unsigned int flag:1;
+  };
+  ```
+
+
+size pragma
+-----------
+Nim automatically determines the size of an enum.
+But when wrapping a C enum type, it needs to be of a specific size.
+The `size pragma` allows specifying the size of the enum type.
+
+  ```Nim
+  type
+    EventType* {.size: sizeof(uint32).} = enum
+      QuitEvent,
+      AppTerminating,
+      AppLowMemory
+
+  doAssert sizeof(EventType) == sizeof(uint32)
+  ```
+
+The `size pragma` can also specify the size of an `importc` incomplete object type
+so that one can get the size of it at compile time even if it was declared without fields.
+
+  ```Nim
+    type
+      AtomicFlag* {.importc: "atomic_flag", header: "<stdatomic.h>", size: 1.} = object
+
+    static:
+      # if AtomicFlag didn't have the size pragma, this code would result in a compile time error.
+      echo sizeof(AtomicFlag)
+  ```
+
+The `size pragma` accepts only the values 1, 2, 4 or 8.
+
+
+Align pragma
+------------
+
+The `align`:idx: pragma is for variables and object field members. It
+modifies the alignment requirement of the entity being declared. The
+argument must be a constant power of 2. Valid non-zero
+alignments that are weaker than other align pragmas on the same
+declaration are ignored. Alignments that are weaker than the
+alignment requirement of the type are ignored.
+
+  ```Nim
+  type
+    sseType = object
+      sseData {.align(16).}: array[4, float32]
+
+    # every object will be aligned to 128-byte boundary
+    Data = object
+      x: char
+      cacheline {.align(128).}: array[128, char] # over-aligned array of char,
+
+  proc main() =
+    echo "sizeof(Data) = ", sizeof(Data), " (1 byte + 127 bytes padding + 128-byte array)"
+    # output: sizeof(Data) = 256 (1 byte + 127 bytes padding + 128-byte array)
+    echo "alignment of sseType is ", alignof(sseType)
+    # output: alignment of sseType is 16
+    var d {.align(2048).}: Data # this instance of data is aligned even stricter
+
+  main()
+  ```
+
+This pragma has no effect on the JS backend.
+
+
+Noalias pragma
+--------------
+
+Since version 1.4 of the Nim compiler, there is a `.noalias` annotation for variables
+and parameters. It is mapped directly to C/C++'s `restrict`:c: keyword and means that
+the underlying pointer is pointing to a unique location in memory, no other aliases to
+this location exist. It is *unchecked* that this alias restriction is followed. If the
+restriction is violated, the backend optimizer is free to miscompile the code.
+This is an **unsafe** language feature.
+
+Ideally in later versions of the language, the restriction will be enforced at
+compile time. (This is also why the name `noalias` was chosen instead of a more
+verbose name like `unsafeAssumeNoAlias`.)
+
+
+Volatile pragma
+---------------
+The `volatile` pragma is for variables only. It declares the variable as
+`volatile`:c:, whatever that means in C/C++ (its semantics are not well-defined
+in C/C++).
+
+**Note**: This pragma will not exist for the LLVM backend.
+
+
+nodecl pragma
+-------------
+The `nodecl` pragma can be applied to almost any symbol (variable, proc,
+type, etc.) and is sometimes useful for interoperability with C:
+It tells Nim that it should not generate a declaration for the symbol in
+the C code. For example:
+
+  ```Nim
+  var
+    EACCES {.importc, nodecl.}: cint # pretend EACCES was a variable, as
+                                     # Nim does not know its value
+  ```
+
+However, the `header` pragma is often the better alternative.
+
+**Note**: This will not work for the LLVM backend.
+
+
+Header pragma
+-------------
+The `header` pragma is very similar to the `nodecl` pragma: It can be
+applied to almost any symbol and specifies that it should not be declared
+and instead, the generated code should contain an `#include`:c:\:
+
+  ```Nim
+  type
+    PFile {.importc: "FILE*", header: "<stdio.h>".} = distinct pointer
+      # import C's FILE* type; Nim will treat it as a new pointer type
+  ```
+
+The `header` pragma always expects a string constant. The string constant
+contains the header file: As usual for C, a system header file is enclosed
+in angle brackets: `<>`:c:. If no angle brackets are given, Nim
+encloses the header file in `""`:c: in the generated C code.
+
+**Note**: This will not work for the LLVM backend.
+
+
+IncompleteStruct pragma
+-----------------------
+The `incompleteStruct` pragma tells the compiler to not use the
+underlying C `struct`:c: in a `sizeof` expression:
+
+  ```Nim
+  type
+    DIR* {.importc: "DIR", header: "<dirent.h>",
+           pure, incompleteStruct.} = object
+  ```
+
+
+Compile pragma
+--------------
+The `compile` pragma can be used to compile and link a C/C++ source file
+with the project:
+
+This pragma can take three forms. The first is a simple file input:
+  ```Nim
+  {.compile: "myfile.cpp".}
+  ```
+
+The second form is a tuple where the second arg is the output name strutils formatter:
+  ```Nim
+  {.compile: ("file.c", "$1.o").}
+  ```
+
+**Note**: Nim computes a SHA1 checksum and only recompiles the file if it
+has changed. One can use the `-f`:option: command-line option to force
+the recompilation of the file.
+
+Since 1.4 the `compile` pragma is also available with this syntax:
+
+  ```Nim
+  {.compile("myfile.cpp", "--custom flags here").}
+  ```
+
+As can be seen in the example, this new variant allows for custom flags
+that are passed to the C compiler when the file is recompiled.
+
+
+Link pragma
+-----------
+The `link` pragma can be used to link an additional file with the project:
+
+  ```Nim
+  {.link: "myfile.o".}
+  ```
+
+
+passc pragma
+------------
+The `passc` pragma can be used to pass additional parameters to the C
+compiler like one would use the command-line switch `--passc`:option:\:
+
+  ```Nim
+  {.passc: "-Wall -Werror".}
+  ```
+
+Note that one can use `gorge` from the [system module](system.html) to
+embed parameters from an external command that will be executed
+during semantic analysis:
+
+  ```Nim
+  {.passc: gorge("pkg-config --cflags sdl").}
+  ```
+
+
+localPassC pragma
+-----------------
+The `localPassC` pragma can be used to pass additional parameters to the C
+compiler, but only for the C/C++ file that is produced from the Nim module
+the pragma resides in:
+
+  ```Nim
+  # Module A.nim
+  # Produces: A.nim.cpp
+  {.localPassC: "-Wall -Werror".} # Passed when compiling A.nim.cpp
+  ```
+
+
+passl pragma
+------------
+The `passl` pragma can be used to pass additional parameters to the linker
+like one would be using the command-line switch `--passl`:option:\:
+
+  ```Nim
+  {.passl: "-lSDLmain -lSDL".}
+  ```
+
+Note that one can use `gorge` from the [system module](system.html) to
+embed parameters from an external command that will be executed
+during semantic analysis:
+
+  ```Nim
+  {.passl: gorge("pkg-config --libs sdl").}
+  ```
+
+
+Emit pragma
+-----------
+The `emit` pragma can be used to directly affect the output of the
+compiler's code generator. The code is then unportable to other code
+generators/backends. Its usage is highly discouraged! However, it can be
+extremely useful for interfacing with `C++`:idx: or `Objective C`:idx: code.
+
+Example:
+
+  ```Nim
+  {.emit: """
+  static int cvariable = 420;
+  """.}
+
+  {.push stackTrace:off.}
+  proc embedsC() =
+    var nimVar = 89
+    # access Nim symbols within an emit section outside of string literals:
+    {.emit: ["""fprintf(stdout, "%d\n", cvariable + (int)""", nimVar, ");"].}
+  {.pop.}
+
+  embedsC()
+  ```
+
+``nimbase.h`` defines `NIM_EXTERNC`:c: C macro that can be used for
+`extern "C"`:cpp: code to work with both `nim c`:cmd: and `nim cpp`:cmd:, e.g.:
+
+  ```Nim
+  proc foobar() {.importc:"$1".}
+  {.emit: """
+  #include <stdio.h>
+  NIM_EXTERNC
+  void fun(){}
+  """.}
+  ```
+
+.. note:: For backward 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.
+
+For a top-level emit statement, the section where in the generated C/C++ file
+the code should be emitted can be influenced via the prefixes
+`/*TYPESECTION*/`:c: or `/*VARSECTION*/`:c: or `/*INCLUDESECTION*/`:c:\:
+
+  ```Nim
+  {.emit: """/*TYPESECTION*/
+  struct Vector3 {
+  public:
+    Vector3(): x(5) {}
+    Vector3(float x_): x(x_) {}
+    float x;
+  };
+  """.}
+
+  type Vector3 {.importcpp: "Vector3", nodecl} = object
+    x: cfloat
+
+  proc constructVector3(a: cfloat): Vector3 {.importcpp: "Vector3(@)", nodecl}
+  ```
+
+
+ImportCpp pragma
+----------------
+
+**Note**: [c2nim](https://github.com/nim-lang/c2nim/blob/master/doc/c2nim.rst)
+can parse a large subset of C++ and knows
+about the `importcpp` pragma pattern language. It is not necessary
+to know all the details described here.
+
+
+Similar to the [importc pragma] for C, the
+`importcpp` pragma can be used to import `C++`:idx: methods or C++ symbols
+in general. The generated code then uses the C++ method calling
+syntax: `obj->method(arg)`:cpp:. In combination with the `header` and `emit`
+pragmas this allows *sloppy* interfacing with libraries written in C++:
+
+  ```Nim
+  # Horrible example of how to interface with a C++ engine ... ;-)
+
+  {.link: "/usr/lib/libIrrlicht.so".}
+
+  {.emit: """
+  using namespace irr;
+  using namespace core;
+  using namespace scene;
+  using namespace video;
+  using namespace io;
+  using namespace gui;
+  """.}
+
+  const
+    irr = "<irrlicht/irrlicht.h>"
+
+  type
+    IrrlichtDeviceObj {.header: irr,
+                        importcpp: "IrrlichtDevice".} = object
+    IrrlichtDevice = ptr IrrlichtDeviceObj
+
+  proc createDevice(): IrrlichtDevice {.
+    header: irr, importcpp: "createDevice(@)".}
+  proc run(device: IrrlichtDevice): bool {.
+    header: irr, importcpp: "#.run(@)".}
+  ```
+
+The compiler needs to be told to generate C++ (command `cpp`:option:) for
+this to work. The conditional symbol `cpp` is defined when the compiler
+emits C++ code.
+
+### Namespaces
+
+The *sloppy interfacing* example uses `.emit` to produce `using namespace`:cpp:
+declarations. It is usually much better to instead refer to the imported name
+via the `namespace::identifier`:cpp: notation:
+
+  ```nim
+  type
+    IrrlichtDeviceObj {.header: irr,
+                        importcpp: "irr::IrrlichtDevice".} = object
+  ```
+
+
+### Importcpp for enums
+
+When `importcpp` is applied to an enum type the numerical enum values are
+annotated with the C++ enum type, like in this example:
+`((TheCppEnum)(3))`:cpp:.
+(This turned out to be the simplest way to implement it.)
+
+
+### Importcpp for procs
+
+Note that the `importcpp` variant for procs uses a somewhat cryptic pattern
+language for maximum flexibility:
+
+- A hash ``#`` symbol is replaced by the first or next argument.
+- A dot following the hash ``#.`` indicates that the call should use C++'s dot
+  or arrow notation.
+- An at symbol ``@`` is replaced by the remaining arguments,
+  separated by commas.
+
+For example:
+
+  ```nim
+  proc cppMethod(this: CppObj, a, b, c: cint) {.importcpp: "#.CppMethod(@)".}
+  var x: ptr CppObj
+  cppMethod(x[], 1, 2, 3)
+  ```
+
+Produces:
+
+  ```C
+  x->CppMethod(1, 2, 3)
+  ```
+
+As a special rule to keep backward compatibility with older versions of the
+`importcpp` pragma, if there is no special pattern
+character (any of ``# ' @``) at all, C++'s
+dot or arrow notation is assumed, so the above example can also be written as:
+
+  ```nim
+  proc cppMethod(this: CppObj, a, b, c: cint) {.importcpp: "CppMethod".}
+  ```
+
+Note that the pattern language naturally also covers C++'s operator overloading
+capabilities:
+
+  ```nim
+  proc vectorAddition(a, b: Vec3): Vec3 {.importcpp: "# + #".}
+  proc dictLookup(a: Dict, k: Key): Value {.importcpp: "#[#]".}
+  ```
+
+
+- An apostrophe ``'`` followed by an integer ``i`` in the range 0..9
+  is replaced by the i'th parameter *type*. The 0th position is the result
+  type. This can be used to pass types to C++ function templates. Between
+  the ``'`` and the digit, an asterisk can be used to get to the base type
+  of the type. (So it "takes away a star" from the type; `T*`:c: becomes `T`.)
+  Two stars can be used to get to the element type of the element type etc.
+
+For example:
+
+  ```nim
+  type Input {.importcpp: "System::Input".} = object
+  proc getSubsystem*[T](): ptr T {.importcpp: "SystemManager::getSubsystem<'*0>()", nodecl.}
+
+  let x: ptr Input = getSubsystem[Input]()
+  ```
+
+Produces:
+
+  ```C
+  x = SystemManager::getSubsystem<System::Input>()
+  ```
+
+
+- ``#@`` is a special case to support a `cnew` operation. It is required so
+  that the call expression is inlined directly, without going through a
+  temporary location. This is only required to circumvent a limitation of the
+  current code generator.
+
+For example C++'s `new`:cpp: operator can be "imported" like this:
+
+  ```nim
+  proc cnew*[T](x: T): ptr T {.importcpp: "(new '*0#@)", nodecl.}
+
+  # constructor of 'Foo':
+  proc constructFoo(a, b: cint): Foo {.importcpp: "Foo(@)".}
+
+  let x = cnew constructFoo(3, 4)
+  ```
+
+Produces:
+
+  ```C
+  x = new Foo(3, 4)
+  ```
+
+However, depending on the use case `new Foo`:cpp: can also be wrapped like this
+instead:
+
+  ```nim
+  proc newFoo(a, b: cint): ptr Foo {.importcpp: "new Foo(@)".}
+
+  let x = newFoo(3, 4)
+  ```
+
+
+### Wrapping constructors
+
+Sometimes a C++ class has a private copy constructor and so code like
+`Class c = Class(1,2);`:cpp: must not be generated but instead
+`Class c(1,2);`:cpp:.
+For this purpose the Nim proc that wraps a C++ constructor needs to be
+annotated with the `constructor`:idx: pragma. This pragma also helps to generate
+faster C++ code since construction then doesn't invoke the copy constructor:
+
+  ```nim
+  # a better constructor of 'Foo':
+  proc constructFoo(a, b: cint): Foo {.importcpp: "Foo(@)", constructor.}
+  ```
+
+
+### Wrapping destructors
+
+Since Nim generates C++ directly, any destructor is called implicitly by the
+C++ compiler at the scope exits. This means that often one can get away with
+not wrapping the destructor at all! However, when it needs to be invoked
+explicitly, it needs to be wrapped. The pattern language provides
+everything that is required:
+
+  ```nim
+  proc destroyFoo(this: var Foo) {.importcpp: "#.~Foo()".}
+  ```
+
+
+### Importcpp for objects
+
+Generic `importcpp`'ed objects are mapped to C++ templates. This means that
+one can import C++'s templates rather easily without the need for a pattern
+language for object types:
+
+  ```nim  test = "nim cpp $1"
+  type
+    StdMap[K, V] {.importcpp: "std::map", header: "<map>".} = object
+  proc `[]=`[K, V](this: var StdMap[K, V]; key: K; val: V) {.
+    importcpp: "#[#] = #", header: "<map>".}
+
+  var x: StdMap[cint, cdouble]
+  x[6] = 91.4
+  ```
+
+
+Produces:
+
+  ```C
+  std::map<int, double> x;
+  x[6] = 91.4;
+  ```
+
+
+- If more precise control is needed, the apostrophe `'` can be used in the
+  supplied pattern to denote the concrete type parameters of the generic type.
+  See the usage of the apostrophe operator in proc patterns for more details.
+
+    ```nim
+    type
+      VectorIterator[T] {.importcpp: "std::vector<'0>::iterator".} = object
+
+    var x: VectorIterator[cint]
+    ```
+
+  Produces:
+
+    ```C
+
+    std::vector<int>::iterator x;
+    ```
+
+
+ImportJs pragma
+---------------
+
+Similar to the [importcpp pragma] for C++,
+the `importjs` pragma can be used to import Javascript methods or
+symbols in general. The generated code then uses the Javascript method
+calling syntax: ``obj.method(arg)``.
+
+
+ImportObjC pragma
+-----------------
+Similar to the [importc pragma] for C, the `importobjc` pragma can
+be used to import `Objective C`:idx: methods. The generated code then uses the
+Objective C method calling syntax: ``[obj method param1: arg]``.
+In addition with the `header` and `emit` pragmas this
+allows *sloppy* interfacing with libraries written in Objective C:
+
+  ```Nim
+  # horrible example of how to interface with GNUStep ...
+
+  {.passl: "-lobjc".}
+  {.emit: """
+  #include <objc/Object.h>
+  @interface Greeter:Object
+  {
+  }
+
+  - (void)greet:(long)x y:(long)dummy;
+  @end
+
+  #include <stdio.h>
+  @implementation Greeter
+
+  - (void)greet:(long)x y:(long)dummy
+  {
+    printf("Hello, World!\n");
+  }
+  @end
+
+  #include <stdlib.h>
+  """.}
+
+  type
+    Id {.importc: "id", header: "<objc/Object.h>", final.} = distinct int
+
+  proc newGreeter: Id {.importobjc: "Greeter new", nodecl.}
+  proc greet(self: Id, x, y: int) {.importobjc: "greet", nodecl.}
+  proc free(self: Id) {.importobjc: "free", nodecl.}
+
+  var g = newGreeter()
+  g.greet(12, 34)
+  g.free()
+  ```
+
+The compiler needs to be told to generate Objective C (command `objc`:option:) for
+this to work. The conditional symbol ``objc`` is defined when the compiler
+emits Objective C code.
+
+
+CodegenDecl pragma
+------------------
+
+The `codegenDecl` pragma can be used to directly influence Nim's code
+generator. It receives a format string that determines how the variable,
+proc or object type is declared in the generated code.
+
+For variables, $1 in the format string represents the type of the variable,
+$2 is the name of the variable, and each appearance of $# represents $1/$2
+respectively according to its position.
+
+The following Nim code:
+
+  ```nim
+  var
+    a {.codegenDecl: "$# progmem $#".}: int
+  ```
+
+will generate this C code:
+
+  ```c
+  int progmem a
+  ```
+
+For procedures, $1 is the return type of the procedure, $2 is the name of
+the procedure, $3 is the parameter list, and each appearance of $# represents
+$1/$2/$3 respectively according to its position.
+
+The following nim code:
+
+  ```nim
+  proc myinterrupt() {.codegenDecl: "__interrupt $# $#$#".} =
+    echo "realistic interrupt handler"
+  ```
+
+will generate this code:
+
+  ```c
+  __interrupt void myinterrupt()
+  ```
+
+For object types, the $1 represents the name of the object type, $2 is the list of
+fields and $3 is the base type.
+
+```nim
+
+const strTemplate = """
+  struct $1 {
+    $2
+  };
+"""
+type Foo {.codegenDecl:strTemplate.} = object
+  a, b: int
+```
+
+will generate this code:
+
+
+```c
+struct Foo {
+  NI a;
+  NI b;
+};
+```
+
+`cppNonPod` pragma
+------------------
+
+The `cppNonPod` pragma should be used for non-POD `importcpp` types so that they
+work properly (in particular regarding constructor and destructor) for
+`threadvar` variables. This requires `--tlsEmulation:off`:option:.
+
+  ```nim
+  type Foo {.cppNonPod, importcpp, header: "funs.h".} = object
+    x: cint
+  proc main()=
+    var a {.threadvar.}: Foo
+  ```
+
+
+compile-time define pragmas
+---------------------------
+
+The pragmas listed here can be used to optionally accept values from
+the `-d/--define`:option: option at compile time.
+
+The implementation currently provides the following possible options (various
+others may be added later).
+
+=================  ============================================
+pragma             description
+=================  ============================================
+`intdefine`:idx:   Reads in a build-time define as an integer
+`strdefine`:idx:   Reads in a build-time define as a string
+`booldefine`:idx:  Reads in a build-time define as a bool
+=================  ============================================
+
+  ```nim
+  const FooBar {.intdefine.}: int = 5
+  echo FooBar
+  ```
+
+  ```cmd
+  nim c -d:FooBar=42 foobar.nim
+  ```
+
+In the above example, providing the `-d`:option: flag causes the symbol
+`FooBar` to be overwritten at compile-time, printing out 42. If the
+`-d:FooBar=42`:option: were to be omitted, the default value of 5 would be
+used. To see if a value was provided, `defined(FooBar)` can be used.
+
+The syntax `-d:flag`:option: is actually just a shortcut for
+`-d:flag=true`:option:.
+
+These pragmas also accept an optional string argument for qualified
+define names.
+
+  ```nim
+  const FooBar {.intdefine: "package.FooBar".}: int = 5
+  echo FooBar
+  ```
+
+  ```cmd
+  nim c -d:package.FooBar=42 foobar.nim
+  ```
+
+This helps disambiguate define names in different packages.
+
+See also the [generic `define` pragma](manual_experimental.html#generic-nimdefine-pragma)
+for a version of these pragmas that detects the type of the define based on
+the constant value.
+
+User-defined pragmas
+====================
+
+
+pragma pragma
+-------------
+
+The `pragma` pragma can be used to declare user-defined pragmas. This is
+useful because Nim's templates and macros do not affect pragmas.
+User-defined pragmas are in a different module-wide scope than all other symbols.
+They cannot be imported from a module.
+
+Example:
+
+  ```nim
+  when appType == "lib":
+    {.pragma: rtl, exportc, dynlib, cdecl.}
+  else:
+    {.pragma: rtl, importc, dynlib: "client.dll", cdecl.}
+
+  proc p*(a, b: int): int {.rtl.} =
+    result = a + b
+  ```
+
+In the example, a new pragma named `rtl` is introduced that either imports
+a symbol from a dynamic library or exports the symbol for dynamic library
+generation.
+
+
+Custom annotations
+------------------
+It is possible to define custom typed pragmas. Custom pragmas do not affect
+code generation directly, but their presence can be detected by macros.
+Custom pragmas are defined using templates annotated with pragma `pragma`:
+
+  ```nim
+  template dbTable(name: string, table_space: string = "") {.pragma.}
+  template dbKey(name: string = "", primary_key: bool = false) {.pragma.}
+  template dbForeignKey(t: typedesc) {.pragma.}
+  template dbIgnore {.pragma.}
+  ```
+
+
+Consider this stylized example of a possible Object Relation Mapping (ORM)
+implementation:
+
+  ```nim
+  const tblspace {.strdefine.} = "dev" # switch for dev, test and prod environments
+
+  type
+    User {.dbTable("users", tblspace).} = object
+      id {.dbKey(primary_key = true).}: int
+      name {.dbKey"full_name".}: string
+      is_cached {.dbIgnore.}: bool
+      age: int
+
+    UserProfile {.dbTable("profiles", tblspace).} = object
+      id {.dbKey(primary_key = true).}: int
+      user_id {.dbForeignKey: User.}: int
+      read_access: bool
+      write_access: bool
+      admin_access: bool
+  ```
+
+In this example, custom pragmas are used to describe how Nim objects are
+mapped to the schema of the relational database. Custom pragmas can have
+zero or more arguments. In order to pass multiple arguments use one of
+template call syntaxes. All arguments are typed and follow standard
+overload resolution rules for templates. Therefore, it is possible to have
+default values for arguments, pass by name, varargs, etc.
+
+Custom pragmas can be used in all locations where ordinary pragmas can be
+specified. It is possible to annotate procs, templates, type and variable
+definitions, statements, etc.
+
+The macros module includes helpers which can be used to simplify custom pragma
+access `hasCustomPragma`, `getCustomPragmaVal`. Please consult the
+[macros](macros.html) module documentation for details. These macros are not
+magic, everything they do can also be achieved by walking the AST of the object
+representation.
+
+More examples with custom pragmas:
+
+- Better serialization/deserialization control:
+
+    ```nim
+    type MyObj = object
+      a {.dontSerialize.}: int
+      b {.defaultDeserialize: 5.}: int
+      c {.serializationKey: "_c".}: string
+    ```
+
+- Adopting type for gui inspector in a game engine:
+
+    ```nim
+    type MyComponent = object
+      position {.editable, animatable.}: Vector3
+      alpha {.editRange: [0.0..1.0], animatable.}: float32
+    ```
+
+
+Macro pragmas
+-------------
+
+Macros and templates can sometimes be called with the pragma syntax. Cases
+where this is possible include when attached to routine (procs, iterators, etc.)
+declarations or routine type expressions. The compiler will perform the
+following simple syntactic transformations:
+
+  ```nim
+  template command(name: string, def: untyped) = discard
+
+  proc p() {.command("print").} = discard
+  ```
+
+This is translated to:
+
+  ```nim
+  command("print"):
+    proc p() = discard
+  ```
+
+------
+
+  ```nim
+  type
+    AsyncEventHandler = proc (x: Event) {.async.}
+  ```
+
+This is translated to:
+
+  ```nim
+  type
+    AsyncEventHandler = async(proc (x: Event))
+  ```
+
+------
+
+When multiple macro pragmas are applied to the same definition, the first one
+from left to right will be evaluated. This macro can then choose to keep
+the remaining macro pragmas in its output, and those will be evaluated in
+the same way.
+
+There are a few more applications of macro pragmas, such as in type,
+variable and constant declarations, but this behavior is considered to be
+experimental and is documented in the [experimental manual](
+manual_experimental.html#extended-macro-pragmas) instead.
+
+
+Foreign function interface
+==========================
+
+Nim's `FFI`:idx: (foreign function interface) is extensive and only the
+parts that scale to other future backends (like the LLVM/JavaScript backends)
+are documented here.
+
+
+Importc pragma
+--------------
+The `importc` pragma provides a means to import a proc or a variable
+from C. The optional argument is a string containing the C identifier. If
+the argument is missing, the C name is the Nim identifier *exactly as
+spelled*:
+
+  ```nim
+  proc printf(formatstr: cstring) {.header: "<stdio.h>", importc: "printf", varargs.}
+  ```
+
+When `importc` is applied to a `let` statement it can omit its value which
+will then be expected to come from C. This can be used to import a C `const`:c:\:
+
+  ```nim
+  {.emit: "const int cconst = 42;".}
+
+  let cconst {.importc, nodecl.}: cint
+
+  assert cconst == 42
+  ```
+
+Note that this pragma has been abused in the past to also work in the
+JS backend for JS objects and functions. Other backends do provide
+the same feature under the same name. Also, when the target language
+is not set to C, other pragmas are available:
+
+ * [importcpp][importcpp pragma]
+ * [importobjc][importobjc pragma]
+ * [importjs][importjs pragma]
+
+The string literal passed to `importc` can be a format string:
+
+  ```Nim
+  proc p(s: cstring) {.importc: "prefix$1".}
+  ```
+
+In the example, the external name of `p` is set to `prefixp`. Only ``$1``
+is available and a literal dollar sign must be written as ``$$``.
+
+
+Exportc pragma
+--------------
+The `exportc` pragma provides a means to export a type, a variable, or a
+procedure to C. Enums and constants can't be exported. The optional argument
+is a string containing the C identifier. If the argument is missing, the C
+name is the Nim identifier *exactly as spelled*:
+
+  ```Nim
+  proc callme(formatstr: cstring) {.exportc: "callMe", varargs.}
+  ```
+
+Note that this pragma is somewhat of a misnomer: Other backends do provide
+the same feature under the same name.
+
+The string literal passed to `exportc` can be a format string:
+
+  ```Nim
+  proc p(s: string) {.exportc: "prefix$1".} =
+    echo s
+  ```
+
+In the example, the external name of `p` is set to `prefixp`. Only ``$1``
+is available and a literal dollar sign must be written as ``$$``.
+
+If the symbol should also be exported to a dynamic library, the `dynlib`
+pragma should be used in addition to the `exportc` pragma. See
+[Dynlib pragma for export].
+
+
+Extern pragma
+-------------
+Like `exportc` or `importc`, the `extern` pragma affects name
+mangling. The string literal passed to `extern` can be a format string:
+
+  ```Nim
+  proc p(s: string) {.extern: "prefix$1".} =
+    echo s
+  ```
+
+In the example, the external name of `p` is set to `prefixp`. Only ``$1``
+is available and a literal dollar sign must be written as ``$$``.
+
+
+Bycopy pragma
+-------------
+
+The `bycopy` pragma can be applied to an object or tuple type or a proc param. It instructs the compiler to pass the type by value to procs:
+
+  ```nim
+  type
+    Vector {.bycopy.} = object
+      x, y, z: float
+  ```
+
+The Nim compiler automatically determines whether a parameter is passed by value or
+by reference based on the parameter type's size. If a parameter must be passed by value
+or by reference, (such as when interfacing with a C library) use the bycopy or byref pragmas.
+Notice params marked as `byref` takes precedence over types marked as `bycopy`.
+
+Byref pragma
+------------
+
+The `byref` pragma can be applied to an object or tuple type or a proc param.
+When applied to a type it instructs the compiler to pass the type by reference
+(hidden pointer) to procs. When applied to a param it will take precedence, even
+if the the type was marked as `bycopy`. When an `importc` type has a `byref` pragma or
+parameters are marked as `byref` in an `importc` proc, these params translate to pointers.
+When an `importcpp` type has a `byref` pragma, these params translate to
+C++ references `&`.
+
+  ```Nim
+  {.emit: """/*TYPESECTION*/
+  typedef struct {
+    int x;
+  } CStruct;
+  """.}
+
+  {.emit: """
+  #ifdef __cplusplus
+  extern "C"
+  #endif
+  int takesCStruct(CStruct* x) {
+    return x->x;
+  }
+  """.}
+
+  type
+    CStruct {.importc, byref.} = object
+      x: cint
+
+  proc takesCStruct(x: CStruct): cint {.importc.}
+  ```
+
+  or
+
+
+  ```Nim
+  type
+    CStruct {.importc.} = object
+      x: cint
+
+  proc takesCStruct(x {.byref.}: CStruct): cint {.importc.}
+  ```
+
+  ```Nim
+  {.emit: """/*TYPESECTION*/
+  struct CppStruct {
+    int x;
+
+    int takesCppStruct(CppStruct& y) {
+      return x + y.x;
+    }
+  };
+  """.}
+
+  type
+    CppStruct {.importcpp, byref.} = object
+      x: cint
+
+  proc takesCppStruct(x, y: CppStruct): cint {.importcpp.}
+  ```
+
+Varargs pragma
+--------------
+The `varargs` pragma can be applied to procedures only (and procedure
+types). It tells Nim that the proc can take a variable number of parameters
+after the last specified parameter. Nim string values will be converted to C
+strings automatically:
+
+  ```Nim
+  proc printf(formatstr: cstring) {.header: "<stdio.h>", varargs.}
+
+  printf("hallo %s", "world") # "world" will be passed as C string
+  ```
+
+
+Union pragma
+------------
+The `union` pragma can be applied to any `object` type. It means all
+of an object's fields are overlaid in memory. This produces a `union`:c:
+instead of a `struct`:c: in the generated C/C++ code. The object declaration
+then must not use inheritance or any GC'ed memory but this is currently not
+checked.
+
+**Future directions**: GC'ed memory should be allowed in unions and the GC
+should scan unions conservatively.
+
+
+Packed pragma
+-------------
+The `packed` pragma can be applied to any `object` type. It ensures
+that the fields of an object are packed back-to-back in memory. It is useful
+to store packets or messages from/to network or hardware drivers, and for
+interoperability with C. Combining packed pragma with inheritance is not
+defined, and it should not be used with GC'ed memory (ref's).
+
+**Future directions**: Using GC'ed memory in packed pragma will result in
+a static error. Usage with inheritance should be defined and documented.
+
+
+Dynlib pragma for import
+------------------------
+With the `dynlib` pragma, a procedure or a variable can be imported from
+a dynamic library (``.dll`` files for Windows, ``lib*.so`` files for UNIX).
+The non-optional argument has to be the name of the dynamic library:
+
+  ```Nim
+  proc gtk_image_new(): PGtkWidget
+    {.cdecl, dynlib: "libgtk-x11-2.0.so", importc.}
+  ```
+
+In general, importing a dynamic library does not require any special linker
+options or linking with import libraries. This also implies that no *devel*
+packages need to be installed.
+
+The `dynlib` import mechanism supports a versioning scheme:
+
+  ```nim
+  proc Tcl_Eval(interp: pTcl_Interp, script: cstring): int {.cdecl,
+    importc, dynlib: "libtcl(|8.5|8.4|8.3).so.(1|0)".}
+  ```
+
+At runtime, the dynamic library is searched for (in this order):
+
+    libtcl.so.1
+    libtcl.so.0
+    libtcl8.5.so.1
+    libtcl8.5.so.0
+    libtcl8.4.so.1
+    libtcl8.4.so.0
+    libtcl8.3.so.1
+    libtcl8.3.so.0
+
+The `dynlib` pragma supports not only constant strings as an argument but also
+string expressions in general:
+
+  ```nim
+  import std/os
+
+  proc getDllName: string =
+    result = "mylib.dll"
+    if fileExists(result): return
+    result = "mylib2.dll"
+    if fileExists(result): return
+    quit("could not load dynamic library")
+
+  proc myImport(s: cstring) {.cdecl, importc, dynlib: getDllName().}
+  ```
+
+**Note**: Patterns like ``libtcl(|8.5|8.4).so`` are only supported in constant
+strings, because they are precompiled.
+
+**Note**: Passing variables to the `dynlib` pragma will fail at runtime
+because of order of initialization problems.
+
+**Note**: A `dynlib` import can be overridden with
+the `--dynlibOverride:name`:option: command-line option. The
+[Compiler User Guide](nimc.html) contains further information.
+
+
+Dynlib pragma for export
+------------------------
+
+With the `dynlib` pragma, a procedure can also be exported to
+a dynamic library. The pragma then has no argument and has to be used in
+conjunction with the `exportc` pragma:
+
+  ```Nim
+  proc exportme(): int {.cdecl, exportc, dynlib.}
+  ```
+
+This is only useful if the program is compiled as a dynamic library via the
+`--app:lib`:option: command-line option.
+
+
+Threads
+=======
+
+The `--threads:on`:option: command-line switch is enabled by default. The [typedthreads module](typedthreads.html) module then contains several threading primitives. See [spawn](manual_experimental.html#parallel-amp-spawn) for
+further details.
+
+The only ways to create a thread is via `spawn` or `createThread`.
+
+
+Thread pragma
+-------------
+
+A proc that is executed as a new thread of execution should be marked by the
+`thread` pragma for reasons of readability. The compiler checks for
+violations of the `no heap sharing restriction`:idx:\: This restriction implies
+that it is invalid to construct a data structure that consists of memory
+allocated from different (thread-local) heaps.
+
+A thread proc can be passed to `createThread` or `spawn`.
+
+
+
+Threadvar pragma
+----------------
+
+A variable can be marked with the `threadvar` pragma, which makes it a
+`thread-local`:idx: variable; Additionally, this implies all the effects
+of the `global` pragma.
+
+  ```nim
+  var checkpoints* {.threadvar.}: seq[string]
+  ```
+
+Due to implementation restrictions, thread-local variables cannot be
+initialized within the `var` section. (Every thread-local variable needs to
+be replicated at thread creation.)
+
+
+Threads and exceptions
+----------------------
+
+The interaction between threads and exceptions is simple: A *handled* exception
+in one thread cannot affect any other thread. However, an *unhandled* exception
+in one thread terminates the whole *process*.
+
+
+Guards and locks
+================
+
+Nim provides common low level concurrency mechanisms like locks, atomic
+intrinsics or condition variables.
+
+Nim significantly improves on the safety of these features via additional
+pragmas:
+
+1) A `guard`:idx: annotation is introduced to prevent data races.
+2) Every access of a guarded memory location needs to happen in an
+   appropriate `locks`:idx: statement.
+
+
+Guards and locks sections
+-------------------------
+
+### Protecting global variables
+
+Object fields and global variables can be annotated via a `guard` pragma:
+
+  ```nim
+  import std/locks
+
+  var glock: Lock
+  var gdata {.guard: glock.}: int
+  ```
+
+The compiler then ensures that every access of `gdata` is within a `locks`
+section:
+
+  ```nim
+  proc invalid =
+    # invalid: unguarded access:
+    echo gdata
+
+  proc valid =
+    # valid access:
+    {.locks: [glock].}:
+      echo gdata
+  ```
+
+Top level accesses to `gdata` are always allowed so that it can be initialized
+conveniently. It is *assumed* (but not enforced) that every top level statement
+is executed before any concurrent action happens.
+
+The `locks` section deliberately looks ugly because it has no runtime
+semantics and should not be used directly! It should only be used in templates
+that also implement some form of locking at runtime:
+
+  ```nim
+  template lock(a: Lock; body: untyped) =
+    pthread_mutex_lock(a)
+    {.locks: [a].}:
+      try:
+        body
+      finally:
+        pthread_mutex_unlock(a)
+  ```
+
+
+The guard does not need to be of any particular type. It is flexible enough to
+model low level lockfree mechanisms:
+
+  ```nim
+  var dummyLock {.compileTime.}: int
+  var atomicCounter {.guard: dummyLock.}: int
+
+  template atomicRead(x): untyped =
+    {.locks: [dummyLock].}:
+      memoryReadBarrier()
+      x
+
+  echo atomicRead(atomicCounter)
+  ```
+
+
+The `locks` pragma takes a list of lock expressions `locks: [a, b, ...]`
+in order to support *multi lock* statements.
+
+
+### Protecting general locations
+
+The `guard` annotation can also be used to protect fields within an object.
+The guard then needs to be another field within the same object or a
+global variable.
+
+Since objects can reside on the heap or on the stack, this greatly enhances
+the expressiveness of the language:
+
+  ```nim
+  import std/locks
+
+  type
+    ProtectedCounter = object
+      v {.guard: L.}: int
+      L: Lock
+
+  proc incCounters(counters: var openArray[ProtectedCounter]) =
+    for i in 0..counters.high:
+      lock counters[i].L:
+        inc counters[i].v
+  ```
+
+The access to field `x.v` is allowed since its guard `x.L`  is active.
+After template expansion, this amounts to:
+
+  ```nim
+  proc incCounters(counters: var openArray[ProtectedCounter]) =
+    for i in 0..counters.high:
+      pthread_mutex_lock(counters[i].L)
+      {.locks: [counters[i].L].}:
+        try:
+          inc counters[i].v
+        finally:
+          pthread_mutex_unlock(counters[i].L)
+  ```
+
+There is an analysis that checks that `counters[i].L` is the lock that
+corresponds to the protected location `counters[i].v`. This analysis is called
+`path analysis`:idx: because it deals with paths to locations
+like `obj.field[i].fieldB[j]`.
+
+The path analysis is **currently unsound**, but that doesn't make it useless.
+Two paths are considered equivalent if they are syntactically the same.
+
+This means the following compiles (for now) even though it really should not:
+
+  ```nim
+  {.locks: [a[i].L].}:
+    inc i
+    access a[i].v
+  ```
diff --git a/doc/manual.rst b/doc/manual.rst
deleted file mode 100644
index 8e548afdc..000000000
--- a/doc/manual.rst
+++ /dev/null
@@ -1,8217 +0,0 @@
-==========
-Nim Manual
-==========
-
-:Authors: Andreas Rumpf, Zahary Karadjov
-:Version: |nimversion|
-
-.. contents::
-
-
-  "Complexity" seems to be a lot like "energy": you can transfer it from the end
-  user to one/some of the other players, but the total amount seems to remain
-  pretty much constant for a given task. -- Ran
-
-
-About this document
-===================
-
-**Note**: This document is a draft! Several of Nim's features may need more
-precise wording. This manual is constantly evolving until the 1.0 release and is
-not to be considered as the final proper specification.
-
-This document describes the lexis, the syntax, and the semantics of Nim.
-
-The language constructs are explained using an extended BNF, in which ``(a)*``
-means 0 or more ``a``'s, ``a+`` means 1 or more ``a``'s, and ``(a)?`` means an
-optional *a*. Parentheses may be used to group elements.
-
-``&`` is the lookahead operator; ``&a`` means that an ``a`` is expected but
-not consumed. It will be consumed in the following rule.
-
-The ``|``, ``/`` symbols are used to mark alternatives and have the lowest
-precedence. ``/`` is the ordered choice that requires the parser to try the
-alternatives in the given order. ``/`` is often used to ensure the grammar
-is not ambiguous.
-
-Non-terminals start with a lowercase letter, abstract terminal symbols are in
-UPPERCASE. Verbatim terminal symbols (including keywords) are quoted
-with ``'``. An example::
-
-  ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)?
-
-The binary ``^*`` operator is used as a shorthand for 0 or more occurrences
-separated by its second argument; likewise ``^+`` means 1 or more
-occurrences: ``a ^+ b`` is short for ``a (b a)*``
-and ``a ^* b`` is short for ``(a (b a)*)?``. Example::
-
-  arrayConstructor = '[' expr ^* ',' ']'
-
-Other parts of Nim - like scoping rules or runtime semantics are only
-described in the, more easily comprehensible, informal manner for now.
-
-
-
-
-Definitions
-===========
-
-A Nim program specifies a computation that acts on a memory consisting of
-components called `locations`:idx:. A variable is basically a name for a
-location. Each variable and location is of a certain `type`:idx:. The
-variable's type is called `static type`:idx:, the location's type is called
-`dynamic type`:idx:. If the static type is not the same as the dynamic type,
-it is a super-type or subtype of the dynamic type.
-
-An `identifier`:idx: is a symbol declared as a name for a variable, type,
-procedure, etc. The region of the program over which a declaration applies is
-called the `scope`:idx: of the declaration. Scopes can be nested. The meaning
-of an identifier is determined by the smallest enclosing scope in which the
-identifier is declared unless overloading resolution rules suggest otherwise.
-
-An expression specifies a computation that produces a value or location.
-Expressions that produce locations are called `l-values`:idx:. An l-value
-can denote either a location or the value the location contains, depending on
-the context. Expressions whose values can be determined statically are called
-`constant expressions`:idx:; they are never l-values.
-
-A `static error`:idx: is an error that the implementation detects before
-program execution. Unless explicitly classified, an error is a static error.
-
-A `checked runtime error`:idx: is an error that the implementation detects
-and reports at runtime. The method for reporting such errors is via *raising
-exceptions* or *dying with a fatal error*. However, the implementation
-provides a means to disable these runtime checks. See the section pragmas_
-for details.
-
-Whether a checked runtime error results in an exception or in a fatal error at
-runtime is implementation specific. Thus the following program is always
-invalid:
-
-.. code-block:: nim
-  var a: array[0..1, char]
-  let i = 5
-  try:
-    a[i] = 'N'
-  except IndexError:
-    echo "invalid index"
-
-An `unchecked runtime error`:idx: is an error that is not guaranteed to be
-detected, and can cause the subsequent behavior of the computation to
-be arbitrary. Unchecked runtime errors cannot occur if only `safe`:idx:
-language features are used.
-
-
-
-Lexical Analysis
-================
-
-Encoding
---------
-
-All Nim source files are in the UTF-8 encoding (or its ASCII subset). Other
-encodings are not supported. Any of the standard platform line termination
-sequences can be used - the Unix form using ASCII LF (linefeed), the Windows
-form using the ASCII sequence CR LF (return followed by linefeed), or the old
-Macintosh form using the ASCII CR (return) character. All of these forms can be
-used equally, regardless of platform.
-
-
-Indentation
------------
-
-Nim's standard grammar describes an `indentation sensitive`:idx: language.
-This means that all the control structures are recognized by indentation.
-Indentation consists only of spaces; tabulators are not allowed.
-
-The indentation handling is implemented as follows: The lexer annotates the
-following token with the preceding number of spaces; indentation is not
-a separate token. This trick allows parsing of Nim with only 1 token of
-lookahead.
-
-The parser uses a stack of indentation levels: the stack consists of integers
-counting the spaces. The indentation information is queried at strategic
-places in the parser but ignored otherwise: The pseudo terminal ``IND{>}``
-denotes an indentation that consists of more spaces than the entry at the top
-of the stack; ``IND{=}`` an indentation that has the same number of spaces. ``DED``
-is another pseudo terminal that describes the *action* of popping a value
-from the stack, ``IND{>}`` then implies to push onto the stack.
-
-With this notation we can now easily define the core of the grammar: A block of
-statements (simplified example)::
-
-  ifStmt = 'if' expr ':' stmt
-           (IND{=} 'elif' expr ':' stmt)*
-           (IND{=} 'else' ':' stmt)?
-
-  simpleStmt = ifStmt / ...
-
-  stmt = IND{>} stmt ^+ IND{=} DED  # list of statements
-       / simpleStmt                 # or a simple statement
-
-
-
-Comments
---------
-
-Comments start anywhere outside a string or character literal with the
-hash character ``#``.
-Comments consist of a concatenation of `comment pieces`:idx:. A comment piece
-starts with ``#`` and runs until the end of the line. The end of line characters
-belong to the piece. If the next line only consists of a comment piece with
-no other tokens between it and the preceding one, it does not start a new
-comment:
-
-
-.. code-block:: nim
-  i = 0     # This is a single comment over multiple lines.
-    # The scanner merges these two pieces.
-    # The comment continues here.
-
-
-`Documentation comments`:idx: are comments that start with two ``##``.
-Documentation comments are tokens; they are only allowed at certain places in
-the input file as they belong to the syntax tree!
-
-
-Multiline comments
-------------------
-
-Starting with version 0.13.0 of the language Nim supports multiline comments.
-They look like:
-
-.. code-block:: nim
-  #[Comment here.
-  Multiple lines
-  are not a problem.]#
-
-Multiline comments support nesting:
-
-.. code-block:: nim
-  #[  #[ Multiline comment in already
-     commented out code. ]#
-  proc p[T](x: T) = discard
-  ]#
-
-Multiline documentation comments also exist and support nesting too:
-
-.. code-block:: nim
-  proc foo =
-    ##[Long documentation comment
-    here.
-    ]##
-
-
-Identifiers & Keywords
-----------------------
-
-Identifiers in Nim can be any string of letters, digits
-and underscores, beginning with a letter. Two immediate following
-underscores ``__`` are not allowed::
-
-  letter ::= 'A'..'Z' | 'a'..'z' | '\x80'..'\xff'
-  digit ::= '0'..'9'
-  IDENTIFIER ::= letter ( ['_'] (letter | digit) )*
-
-Currently any Unicode character with an ordinal value > 127 (non ASCII) is
-classified as a ``letter`` and may thus be part of an identifier but later
-versions of the language may assign some Unicode characters to belong to the
-operator characters instead.
-
-The following keywords are reserved and cannot be used as identifiers:
-
-.. code-block:: nim
-   :file: keywords.txt
-
-Some keywords are unused; they are reserved for future developments of the
-language.
-
-
-Identifier equality
--------------------
-
-Two identifiers are considered equal if the following algorithm returns true:
-
-.. code-block:: nim
-  proc sameIdentifier(a, b: string): bool =
-    a[0] == b[0] and
-      a.replace("_", "").toLowerAscii == b.replace("_", "").toLowerAscii
-
-That means only the first letters are compared in a case sensitive manner. Other
-letters are compared case insensitively within the ASCII range and underscores are ignored.
-
-This rather unorthodox way to do identifier comparisons is called
-`partial case insensitivity`:idx: and has some advantages over the conventional
-case sensitivity:
-
-It allows programmers to mostly use their own preferred
-spelling style, be it humpStyle or snake_style, and libraries written
-by different programmers cannot use incompatible conventions.
-A Nim-aware editor or IDE can show the identifiers as preferred.
-Another advantage is that it frees the programmer from remembering
-the exact spelling of an identifier. The exception with respect to the first
-letter allows common code like ``var foo: Foo`` to be parsed unambiguously.
-
-Historically, Nim was a fully `style-insensitive`:idx: language. This meant that
-it was not case-sensitive and underscores were ignored and there was no even a
-distinction between ``foo`` and ``Foo``.
-
-
-String literals
----------------
-
-Terminal symbol in the grammar: ``STR_LIT``.
-
-String literals can be delimited by matching double quotes, and can
-contain the following `escape sequences`:idx:\ :
-
-==================         ===================================================
-  Escape sequence          Meaning
-==================         ===================================================
-  ``\p``                   platform specific newline: CRLF on Windows,
-                           LF on Unix
-  ``\r``, ``\c``           `carriage return`:idx:
-  ``\n``, ``\l``           `line feed`:idx: (often called `newline`:idx:)
-  ``\f``                   `form feed`:idx:
-  ``\t``                   `tabulator`:idx:
-  ``\v``                   `vertical tabulator`:idx:
-  ``\\``                   `backslash`:idx:
-  ``\"``                   `quotation mark`:idx:
-  ``\'``                   `apostrophe`:idx:
-  ``\`` '0'..'9'+          `character with decimal value d`:idx:;
-                           all decimal digits directly
-                           following are used for the character
-  ``\a``                   `alert`:idx:
-  ``\b``                   `backspace`:idx:
-  ``\e``                   `escape`:idx: `[ESC]`:idx:
-  ``\x`` HH                `character with hex value HH`:idx:;
-                           exactly two hex digits are allowed
-==================         ===================================================
-
-
-Strings in Nim may contain any 8-bit value, even embedded zeros. However
-some operations may interpret the first binary zero as a terminator.
-
-
-Triple quoted string literals
------------------------------
-
-Terminal symbol in the grammar: ``TRIPLESTR_LIT``.
-
-String literals can also be delimited by three double quotes
-``"""`` ... ``"""``.
-Literals in this form may run for several lines, may contain ``"`` and do not
-interpret any escape sequences.
-For convenience, when the opening ``"""`` is followed by a newline (there may
-be whitespace between the opening ``"""`` and the newline),
-the newline (and the preceding whitespace) is not included in the string. The
-ending of the string literal is defined by the pattern ``"""[^"]``, so this:
-
-.. code-block:: nim
-  """"long string within quotes""""
-
-Produces::
-
-  "long string within quotes"
-
-
-Raw string literals
--------------------
-
-Terminal symbol in the grammar: ``RSTR_LIT``.
-
-There are also raw string literals that are preceded with the
-letter ``r`` (or ``R``) and are delimited by matching double quotes (just
-like ordinary string literals) and do not interpret the escape sequences.
-This is especially convenient for regular expressions or Windows paths:
-
-.. code-block:: nim
-
-  var f = openFile(r"C:\texts\text.txt") # a raw string, so ``\t`` is no tab
-
-To produce a single ``"`` within a raw string literal, it has to be doubled:
-
-.. code-block:: nim
-
-  r"a""b"
-
-Produces::
-
-  a"b
-
-``r""""`` is not possible with this notation, because the three leading
-quotes introduce a triple quoted string literal. ``r"""`` is the same
-as ``"""`` since triple quoted string literals do not interpret escape
-sequences either.
-
-
-Generalized raw string literals
--------------------------------
-
-Terminal symbols in the grammar: ``GENERALIZED_STR_LIT``,
-``GENERALIZED_TRIPLESTR_LIT``.
-
-The construct ``identifier"string literal"`` (without whitespace between the
-identifier and the opening quotation mark) is a
-generalized raw string literal. It is a shortcut for the construct
-``identifier(r"string literal")``, so it denotes a procedure call with a
-raw string literal as its only argument. Generalized raw string literals
-are especially convenient for embedding mini languages directly into Nim
-(for example regular expressions).
-
-The construct ``identifier"""string literal"""`` exists too. It is a shortcut
-for ``identifier("""string literal""")``.
-
-
-Character literals
-------------------
-
-Character literals are enclosed in single quotes ``''`` and can contain the
-same escape sequences as strings - with one exception: the platform
-dependent `newline`:idx: (``\p``)
-is not allowed as it may be wider than one character (often it is the pair
-CR/LF for example).  Here are the valid `escape sequences`:idx: for character
-literals:
-
-==================         ===================================================
-  Escape sequence          Meaning
-==================         ===================================================
-  ``\r``, ``\c``           `carriage return`:idx:
-  ``\n``, ``\l``           `line feed`:idx:
-  ``\f``                   `form feed`:idx:
-  ``\t``                   `tabulator`:idx:
-  ``\v``                   `vertical tabulator`:idx:
-  ``\\``                   `backslash`:idx:
-  ``\"``                   `quotation mark`:idx:
-  ``\'``                   `apostrophe`:idx:
-  ``\`` '0'..'9'+          `character with decimal value d`:idx:;
-                           all decimal digits directly
-                           following are used for the character
-  ``\a``                   `alert`:idx:
-  ``\b``                   `backspace`:idx:
-  ``\e``                   `escape`:idx: `[ESC]`:idx:
-  ``\x`` HH                `character with hex value HH`:idx:;
-                           exactly two hex digits are allowed
-==================         ===================================================
-
-A character is not an Unicode character but a single byte. The reason for this
-is efficiency: for the overwhelming majority of use-cases, the resulting
-programs will still handle UTF-8 properly as UTF-8 was specially designed for
-this. Another reason is that Nim can thus support ``array[char, int]`` or
-``set[char]`` efficiently as many algorithms rely on this feature.  The `Rune`
-type is used for Unicode characters, it can represent any Unicode character.
-``Rune`` is declared in the `unicode module <unicode.html>`_.
-
-
-Numerical constants
--------------------
-
-Numerical constants are of a single type and have the form::
-
-  hexdigit = digit | 'A'..'F' | 'a'..'f'
-  octdigit = '0'..'7'
-  bindigit = '0'..'1'
-  HEX_LIT = '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )*
-  DEC_LIT = digit ( ['_'] digit )*
-  OCT_LIT = '0' ('o' | 'c' | 'C') octdigit ( ['_'] octdigit )*
-  BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*
-
-  INT_LIT = HEX_LIT
-          | DEC_LIT
-          | OCT_LIT
-          | BIN_LIT
-
-  INT8_LIT = INT_LIT ['\''] ('i' | 'I') '8'
-  INT16_LIT = INT_LIT ['\''] ('i' | 'I') '16'
-  INT32_LIT = INT_LIT ['\''] ('i' | 'I') '32'
-  INT64_LIT = INT_LIT ['\''] ('i' | 'I') '64'
-
-  UINT_LIT = INT_LIT ['\''] ('u' | 'U')
-  UINT8_LIT = INT_LIT ['\''] ('u' | 'U') '8'
-  UINT16_LIT = INT_LIT ['\''] ('u' | 'U') '16'
-  UINT32_LIT = INT_LIT ['\''] ('u' | 'U') '32'
-  UINT64_LIT = INT_LIT ['\''] ('u' | 'U') '64'
-
-  exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )*
-  FLOAT_LIT = digit (['_'] digit)* (('.' (['_'] digit)* [exponent]) |exponent)
-  FLOAT32_SUFFIX = ('f' | 'F') ['32']
-  FLOAT32_LIT = HEX_LIT '\'' FLOAT32_SUFFIX
-              | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT32_SUFFIX
-  FLOAT64_SUFFIX = ( ('f' | 'F') '64' ) | 'd' | 'D'
-  FLOAT64_LIT = HEX_LIT '\'' FLOAT64_SUFFIX
-              | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT64_SUFFIX
-
-
-As can be seen in the productions, numerical constants can contain underscores
-for readability. Integer and floating point literals may be given in decimal (no
-prefix), binary (prefix ``0b``), octal (prefix ``0o`` or ``0c``) and hexadecimal
-(prefix ``0x``) notation.
-
-There exists a literal for each numerical type that is
-defined. The suffix starting with an apostrophe ('\'') is called a
-`type suffix`:idx:. Literals without a type suffix are of an integer type,
-unless the literal contains a dot or ``E|e`` in which case it is of
-type ``float``. This integer type is ``int`` if the literal is in the range
-``low(i32)..high(i32)``, otherwise it is ``int64``.
-For notational convenience the apostrophe of a type suffix
-is optional if it is not ambiguous (only hexadecimal floating point literals
-with a type suffix can be ambiguous).
-
-
-The type suffixes are:
-
-=================    =========================
-  Type Suffix        Resulting type of literal
-=================    =========================
-  ``'i8``            int8
-  ``'i16``           int16
-  ``'i32``           int32
-  ``'i64``           int64
-  ``'u``             uint
-  ``'u8``            uint8
-  ``'u16``           uint16
-  ``'u32``           uint32
-  ``'u64``           uint64
-  ``'f``             float32
-  ``'d``             float64
-  ``'f32``           float32
-  ``'f64``           float64
-  ``'f128``          float128
-=================    =========================
-
-Floating point literals may also be in binary, octal or hexadecimal
-notation:
-``0B0_10001110100_0000101001000111101011101111111011000101001101001001'f64``
-is approximately 1.72826e35 according to the IEEE floating point standard.
-
-Literals are bounds checked so that they fit the datatype. Non base-10
-literals are used mainly for flags and bit pattern representations, therefore
-bounds checking is done on bit width, not value range. If the literal fits in
-the bit width of the datatype, it is accepted.
-Hence: 0b10000000'u8 == 0x80'u8 == 128, but, 0b10000000'i8 == 0x80'i8 == -1
-instead of causing an overflow error.
-
-Operators
----------
-
-Nim allows user defined operators. An operator is any combination of the
-following characters::
-
-       =     +     -     *     /     <     >
-       @     $     ~     &     %     |
-       !     ?     ^     .     :     \
-
-These keywords are also operators:
-``and or not xor shl shr div mod in notin is isnot of``.
-
-`=`:tok:, `:`:tok:, `::`:tok: are not available as general operators; they
-are used for other notational purposes.
-
-``*:`` is as a special case treated as the two tokens `*`:tok: and `:`:tok:
-(to support ``var v*: T``).
-
-
-Other tokens
-------------
-
-The following strings denote other tokens::
-
-    `   (    )     {    }     [    ]    ,  ;   [.    .]  {.   .}  (.  .)  [:
-
-
-The `slice`:idx: operator `..`:tok: takes precedence over other tokens that
-contain a dot: `{..}`:tok: are the three tokens `{`:tok:, `..`:tok:, `}`:tok:
-and not the two tokens `{.`:tok:, `.}`:tok:.
-
-
-
-Syntax
-======
-
-This section lists Nim's standard syntax. How the parser handles
-the indentation is already described in the `Lexical Analysis`_ section.
-
-Nim allows user-definable operators.
-Binary operators have 11 different levels of precedence.
-
-
-
-Associativity
--------------
-
-Binary operators whose first character is ``^`` are right-associative, all
-other binary operators are left-associative.
-
-.. code-block:: nim
-  proc `^/`(x, y: float): float =
-    # a right-associative division operator
-    result = x / y
-  echo 12 ^/ 4 ^/ 8 # 24.0 (4 / 8 = 0.5, then 12 / 0.5 = 24.0)
-  echo 12  / 4  / 8 # 0.375 (12 / 4 = 3.0, then 3 / 8 = 0.375)
-
-Precedence
-----------
-
-Unary operators always bind stronger than any binary
-operator: ``$a + b`` is ``($a) + b`` and not ``$(a + b)``.
-
-If an unary operator's first character is ``@`` it is a `sigil-like`:idx:
-operator which binds stronger than a ``primarySuffix``: ``@x.abc`` is parsed
-as ``(@x).abc`` whereas ``$x.abc`` is parsed as ``$(x.abc)``.
-
-
-For binary operators that are not keywords the precedence is determined by the
-following rules:
-
-Operators ending in either ``->``, ``~>`` or ``=>`` are called
-`arrow like`:idx:, and have the lowest precedence of all operators.
-
-If the operator ends with ``=`` and its first character is none of
-``<``, ``>``, ``!``, ``=``, ``~``, ``?``, it is an *assignment operator* which
-has the second lowest precedence.
-
-Otherwise precedence is determined by the first character.
-
-================  ===============================================  ==================  ===============
-Precedence level    Operators                                      First character     Terminal symbol
-================  ===============================================  ==================  ===============
- 10 (highest)                                                      ``$  ^``            OP10
-  9               ``*    /    div   mod   shl  shr  %``            ``*  %  \  /``      OP9
-  8               ``+    -``                                       ``+  -  ~  |``      OP8
-  7               ``&``                                            ``&``               OP7
-  6               ``..``                                           ``.``               OP6
-  5               ``==  <= < >= > !=  in notin is isnot not of``   ``=  <  >  !``      OP5
-  4               ``and``                                                              OP4
-  3               ``or xor``                                                           OP3
-  2                                                                ``@  :  ?``         OP2
-  1               *assignment operator* (like ``+=``, ``*=``)                          OP1
-  0 (lowest)      *arrow like operator* (like ``->``, ``=>``)                          OP0
-================  ===============================================  ==================  ===============
-
-
-Whether an operator is used a prefix operator is also affected by preceding
-whitespace (this parsing change was introduced with version 0.13.0):
-
-.. code-block:: nim
-  echo $foo
-  # is parsed as
-  echo($foo)
-
-
-Spacing also determines whether ``(a, b)`` is parsed as an the argument list
-of a call or whether it is parsed as a tuple constructor:
-
-.. code-block:: nim
-  echo(1, 2) # pass 1 and 2 to echo
-
-.. code-block:: nim
-  echo (1, 2) # pass the tuple (1, 2) to echo
-
-
-Grammar
--------
-
-The grammar's start symbol is ``module``.
-
-.. include:: grammar.txt
-   :literal:
-
-
-
-Types
-=====
-
-All expressions have a type which is known at compile time. Nim
-is statically typed. One can declare new types, which is in essence defining
-an identifier that can be used to denote this custom type.
-
-These are the major type classes:
-
-* ordinal types (consist of integer, bool, character, enumeration
-  (and subranges thereof) types)
-* floating point types
-* string type
-* structured types
-* reference (pointer) type
-* procedural type
-* generic type
-
-
-Ordinal types
--------------
-Ordinal types have the following characteristics:
-
-- Ordinal types are countable and ordered. This property allows
-  the operation of functions as ``inc``, ``ord``, ``dec`` on ordinal types to
-  be defined.
-- Ordinal values have a smallest possible value. Trying to count further
-  down than the smallest value gives a checked runtime or static error.
-- Ordinal values have a largest possible value. Trying to count further
-  than the largest value gives a checked runtime or static error.
-
-Integers, bool, characters and enumeration types (and subranges of these
-types) belong to ordinal types. For reasons of simplicity of implementation
-the types ``uint`` and ``uint64`` are not ordinal types.
-
-
-Pre-defined integer types
--------------------------
-These integer types are pre-defined:
-
-``int``
-  the generic signed integer type; its size is platform dependent and has the
-  same size as a pointer. This type should be used in general. An integer
-  literal that has no type suffix is of this type if it is in the range
-  ``low(int32)..high(int32)`` otherwise the literal's type is ``int64``.
-
-intXX
-  additional signed integer types of XX bits use this naming scheme
-  (example: int16 is a 16 bit wide integer).
-  The current implementation supports ``int8``, ``int16``, ``int32``, ``int64``.
-  Literals of these types have the suffix 'iXX.
-
-``uint``
-  the generic `unsigned integer`:idx: type; its size is platform dependent and
-  has the same size as a pointer. An integer literal with the type
-  suffix ``'u`` is of this type.
-
-uintXX
-  additional signed integer types of XX bits use this naming scheme
-  (example: uint16 is a 16 bit wide unsigned integer).
-  The current implementation supports ``uint8``, ``uint16``, ``uint32``,
-  ``uint64``. Literals of these types have the suffix 'uXX.
-  Unsigned operations all wrap around; they cannot lead to over- or
-  underflow errors.
-
-
-In addition to the usual arithmetic operators for signed and unsigned integers
-(``+ - *`` etc.) there are also operators that formally work on *signed*
-integers but treat their arguments as *unsigned*: They are mostly provided
-for backwards compatibility with older versions of the language that lacked
-unsigned integer types. These unsigned operations for signed integers use
-the ``%`` suffix as convention:
-
-
-======================   ======================================================
-operation                meaning
-======================   ======================================================
-``a +% b``               unsigned integer addition
-``a -% b``               unsigned integer subtraction
-``a *% b``               unsigned integer multiplication
-``a /% b``               unsigned integer division
-``a %% b``               unsigned integer modulo operation
-``a <% b``               treat ``a`` and ``b`` as unsigned and compare
-``a <=% b``              treat ``a`` and ``b`` as unsigned and compare
-``ze(a)``                extends the bits of ``a`` with zeros until it has the
-                         width of the ``int`` type
-``toU8(a)``              treats ``a`` as unsigned and converts it to an
-                         unsigned integer of 8 bits (but still the
-                         ``int8`` type)
-``toU16(a)``             treats ``a`` as unsigned and converts it to an
-                         unsigned integer of 16 bits (but still the
-                         ``int16`` type)
-``toU32(a)``             treats ``a`` as unsigned and converts it to an
-                         unsigned integer of 32 bits (but still the
-                         ``int32`` type)
-======================   ======================================================
-
-`Automatic type conversion`:idx: is performed in expressions where different
-kinds of integer types are used: the smaller type is converted to the larger.
-
-A `narrowing type conversion`:idx: converts a larger to a smaller type (for
-example ``int32 -> int16``. A `widening type conversion`:idx: converts a
-smaller type to a larger type (for example ``int16 -> int32``). In Nim only
-widening type conversions are *implicit*:
-
-.. code-block:: nim
-  var myInt16 = 5i16
-  var myInt: int
-  myInt16 + 34     # of type ``int16``
-  myInt16 + myInt  # of type ``int``
-  myInt16 + 2i32   # of type ``int32``
-
-However, ``int`` literals are implicitly convertible to a smaller integer type
-if the literal's value fits this smaller type and such a conversion is less
-expensive than other implicit conversions, so ``myInt16 + 34`` produces
-an ``int16`` result.
-
-For further details, see `Convertible relation
-<#type-relations-convertible-relation>`_.
-
-
-Subrange types
---------------
-A subrange type is a range of values from an ordinal type (the base
-type). To define a subrange type, one must specify it's limiting values: the
-lowest and highest value of the type:
-
-.. code-block:: nim
-  type
-    Subrange = range[0..5]
-
-
-``Subrange`` is a subrange of an integer which can only hold the values 0
-to 5. Assigning any other value to a variable of type ``Subrange`` is a
-checked runtime error (or static error if it can be statically
-determined). Assignments from the base type to one of its subrange types
-(and vice versa) are allowed.
-
-A subrange type has the same size as its base type (``int`` in the example).
-
-
-Pre-defined floating point types
---------------------------------
-
-The following floating point types are pre-defined:
-
-``float``
-  the generic floating point type; its size is platform dependent
-  (the compiler chooses the processor's fastest floating point type).
-  This type should be used in general.
-
-floatXX
-  an implementation may define additional floating point types of XX bits using
-  this naming scheme (example: float64 is a 64 bit wide float). The current
-  implementation supports ``float32`` and ``float64``. Literals of these types
-  have the suffix 'fXX.
-
-
-Automatic type conversion in expressions with different kinds
-of floating point types is performed: See `Convertible relation`_ for further
-details. Arithmetic performed on floating point types follows the IEEE
-standard. Integer types are not converted to floating point types automatically
-and vice versa.
-
-The IEEE standard defines five types of floating-point exceptions:
-
-* Invalid: operations with mathematically invalid operands,
-  for example 0.0/0.0, sqrt(-1.0), and log(-37.8).
-* Division by zero: divisor is zero and dividend is a finite nonzero number,
-  for example 1.0/0.0.
-* Overflow: operation produces a result that exceeds the range of the exponent,
-  for example MAXDOUBLE+0.0000000000001e308.
-* Underflow: operation produces a result that is too small to be represented
-  as a normal number, for example, MINDOUBLE * MINDOUBLE.
-* Inexact: operation produces a result that cannot be represented with infinite
-  precision, for example, 2.0 / 3.0, log(1.1) and 0.1 in input.
-
-The IEEE exceptions are either ignored at runtime or mapped to the
-Nim exceptions: `FloatInvalidOpError`:idx:, `FloatDivByZeroError`:idx:,
-`FloatOverflowError`:idx:, `FloatUnderflowError`:idx:,
-and `FloatInexactError`:idx:.
-These exceptions inherit from the `FloatingPointError`:idx: base class.
-
-Nim provides the pragmas `nanChecks`:idx: and `infChecks`:idx: to control
-whether the IEEE exceptions are ignored or trap a Nim exception:
-
-.. code-block:: nim
-  {.nanChecks: on, infChecks: on.}
-  var a = 1.0
-  var b = 0.0
-  echo b / b # raises FloatInvalidOpError
-  echo a / b # raises FloatOverflowError
-
-In the current implementation ``FloatDivByZeroError`` and ``FloatInexactError``
-are never raised. ``FloatOverflowError`` is raised instead of
-``FloatDivByZeroError``.
-There is also a `floatChecks`:idx: pragma that is a short-cut for the
-combination of ``nanChecks`` and ``infChecks`` pragmas. ``floatChecks`` are
-turned off as default.
-
-The only operations that are affected by the ``floatChecks`` pragma are
-the ``+``, ``-``, ``*``, ``/`` operators for floating point types.
-
-An implementation should always use the maximum precision available to evaluate
-floating pointer values at compile time; this means expressions like
-``0.09'f32 + 0.01'f32 == 0.09'f64 + 0.01'f64`` are true.
-
-
-Boolean type
-------------
-The boolean type is named `bool`:idx: in Nim and can be one of the two
-pre-defined values ``true`` and ``false``. Conditions in ``while``,
-``if``, ``elif``, ``when``-statements need to be of type ``bool``.
-
-This condition holds::
-
-  ord(false) == 0 and ord(true) == 1
-
-The operators ``not, and, or, xor, <, <=, >, >=, !=, ==`` are defined
-for the bool type. The ``and`` and ``or`` operators perform short-cut
-evaluation. Example:
-
-.. code-block:: nim
-
-  while p != nil and p.name != "xyz":
-    # p.name is not evaluated if p == nil
-    p = p.next
-
-
-The size of the bool type is one byte.
-
-
-Character type
---------------
-The character type is named ``char`` in Nim. Its size is one byte.
-Thus it cannot represent an UTF-8 character, but a part of it.
-The reason for this is efficiency: for the overwhelming majority of use-cases,
-the resulting programs will still handle UTF-8 properly as UTF-8 was specially
-designed for this.
-Another reason is that Nim can support ``array[char, int]`` or
-``set[char]`` efficiently as many algorithms rely on this feature. The
-`Rune` type is used for Unicode characters, it can represent any Unicode
-character. ``Rune`` is declared in the `unicode module <unicode.html>`_.
-
-
-
-
-Enumeration types
------------------
-Enumeration types define a new type whose values consist of the ones
-specified. The values are ordered. Example:
-
-.. code-block:: nim
-
-  type
-    Direction = enum
-      north, east, south, west
-
-
-Now the following holds::
-
-  ord(north) == 0
-  ord(east) == 1
-  ord(south) == 2
-  ord(west) == 3
-
-Thus, north < east < south < west. The comparison operators can be used
-with enumeration types.
-
-For better interfacing to other programming languages, the fields of enum
-types can be assigned an explicit ordinal value. However, the ordinal values
-have to be in ascending order. A field whose ordinal value is not
-explicitly given is assigned the value of the previous field + 1.
-
-An explicit ordered enum can have *holes*:
-
-.. code-block:: nim
-  type
-    TokenType = enum
-      a = 2, b = 4, c = 89 # holes are valid
-
-However, it is then not an ordinal anymore, so it is not possible to use these
-enums as an index type for arrays. The procedures ``inc``, ``dec``, ``succ``
-and ``pred`` are not available for them either.
-
-
-The compiler supports the built-in stringify operator ``$`` for enumerations.
-The stringify's result can be controlled by explicitly giving the string
-values to use:
-
-.. code-block:: nim
-
-  type
-    MyEnum = enum
-      valueA = (0, "my value A"),
-      valueB = "value B",
-      valueC = 2,
-      valueD = (3, "abc")
-
-As can be seen from the example, it is possible to both specify a field's
-ordinal value and its string value by using a tuple. It is also
-possible to only specify one of them.
-
-An enum can be marked with the ``pure`` pragma so that it's fields are not
-added to the current scope, so they always need to be accessed
-via ``MyEnum.value``:
-
-.. code-block:: nim
-
-  type
-    MyEnum {.pure.} = enum
-      valueA, valueB, valueC, valueD
-
-  echo valueA # error: Unknown identifier
-  echo MyEnum.valueA # works
-
-
-String type
------------
-All string literals are of the type ``string``. A string in Nim is very
-similar to a sequence of characters. However, strings in Nim are both
-zero-terminated and have a length field. One can retrieve the length with the
-builtin ``len`` procedure; the length never counts the terminating zero.
-The assignment operator for strings always copies the string.
-The ``&`` operator concatenates strings.
-
-Most native Nim types support conversion to strings with the special ``$`` proc.
-When calling the ``echo`` proc, for example, the built-in stringify operation
-for the parameter is called:
-
-.. code-block:: nim
-
-  echo 3 # calls `$` for `int`
-
-Whenever a user creates a specialized object, implementation of this procedure
-provides for ``string`` representation.
-
-.. code-block:: nim
-  type
-    Person = object
-      name: string
-      age: int
-
-  proc `$`(p: Person): string = # `$` always returns a string
-    result = p.name & " is " &
-            $p.age & # we *need* the `$` in front of p.age which
-                     # is natively an integer to convert it to
-                     # a string
-            " years old."
-
-While ``$p.name`` can also be used, the ``$`` operation on a string does
-nothing. Note that we cannot rely on automatic conversion from an ``int`` to
-a ``string`` like we can for the ``echo`` proc.
-
-Strings are compared by their lexicographical order. All comparison operators
-are available. Strings can be indexed like arrays (lower bound is 0). Unlike
-arrays, they can be used in case statements:
-
-.. code-block:: nim
-
-  case paramStr(i)
-  of "-v": incl(options, optVerbose)
-  of "-h", "-?": incl(options, optHelp)
-  else: write(stdout, "invalid command line option!\n")
-
-Per convention, all strings are UTF-8 strings, but this is not enforced. For
-example, when reading strings from binary files, they are merely a sequence of
-bytes. The index operation ``s[i]`` means the i-th *char* of ``s``, not the
-i-th *unichar*. The iterator ``runes`` from the `unicode module
-<unicode.html>`_ can be used for iteration over all Unicode characters.
-
-
-cstring type
-------------
-
-The ``cstring`` type meaning `compatible string` is the native representation
-of a string for the compilation backend. For the C backend the ``cstring`` type
-represents a pointer to a zero-terminated char array
-compatible to the type ``char*`` in Ansi C. Its primary purpose lies in easy
-interfacing with C. The index operation ``s[i]`` means the i-th *char* of
-``s``; however no bounds checking for ``cstring`` is performed making the
-index operation unsafe.
-
-A Nim ``string`` is implicitly convertible
-to ``cstring`` for convenience. If a Nim string is passed to a C-style
-variadic proc, it is implicitly converted to ``cstring`` too:
-
-.. code-block:: nim
-  proc printf(formatstr: cstring) {.importc: "printf", varargs,
-                                    header: "<stdio.h>".}
-
-  printf("This works %s", "as expected")
-
-Even though the conversion is implicit, it is not *safe*: The garbage collector
-does not consider a ``cstring`` to be a root and may collect the underlying
-memory. However in practice this almost never happens as the GC considers
-stack roots conservatively. One can use the builtin procs ``GC_ref`` and
-``GC_unref`` to keep the string data alive for the rare cases where it does
-not work.
-
-A `$` proc is defined for cstrings that returns a string. Thus to get a nim
-string from a cstring:
-
-.. code-block:: nim
-  var str: string = "Hello!"
-  var cstr: cstring = str
-  var newstr: string = $cstr
-
-Structured types
-----------------
-A variable of a structured type can hold multiple values at the same
-time. Structured types can be nested to unlimited levels. Arrays, sequences,
-tuples, objects and sets belong to the structured types.
-
-Array and sequence types
-------------------------
-Arrays are a homogeneous type, meaning that each element in the array
-has the same type. Arrays always have a fixed length which is specified at
-compile time (except for open arrays). They can be indexed by any ordinal type.
-A parameter ``A`` may be an *open array*, in which case it is indexed by
-integers from 0 to ``len(A)-1``. An array expression may be constructed by the
-array constructor ``[]``. The element type of this array expression is
-inferred from the type of the first element. All other elements need to be
-implicitly convertable to this type.
-
-Sequences are similar to arrays but of dynamic length which may change
-during runtime (like strings). Sequences are implemented as growable arrays,
-allocating pieces of memory as items are added. A sequence ``S`` is always
-indexed by integers from 0 to ``len(S)-1`` and its bounds are checked.
-Sequences can be constructed by the array constructor ``[]`` in conjunction
-with the array to sequence operator ``@``. Another way to allocate space for a
-sequence is to call the built-in ``newSeq`` procedure.
-
-A sequence may be passed to a parameter that is of type *open array*.
-
-Example:
-
-.. code-block:: nim
-
-  type
-    IntArray = array[0..5, int] # an array that is indexed with 0..5
-    IntSeq = seq[int] # a sequence of integers
-  var
-    x: IntArray
-    y: IntSeq
-  x = [1, 2, 3, 4, 5, 6]  # [] is the array constructor
-  y = @[1, 2, 3, 4, 5, 6] # the @ turns the array into a sequence
-
-  let z = [1.0, 2, 3, 4] # the type of z is array[0..3, float]
-
-The lower bound of an array or sequence may be received by the built-in proc
-``low()``, the higher bound by ``high()``. The length may be
-received by ``len()``. ``low()`` for a sequence or an open array always returns
-0, as this is the first valid index.
-One can append elements to a sequence with the ``add()`` proc or the ``&``
-operator, and remove (and get) the last element of a sequence with the
-``pop()`` proc.
-
-The notation ``x[i]`` can be used to access the i-th element of ``x``.
-
-Arrays are always bounds checked (at compile-time or at runtime). These
-checks can be disabled via pragmas or invoking the compiler with the
-``--boundChecks:off`` command line switch.
-
-
-Open arrays
------------
-
-Often fixed size arrays turn out to be too inflexible; procedures should
-be able to deal with arrays of different sizes. The `openarray`:idx: type
-allows this; it can only be used for parameters. Openarrays are always
-indexed with an ``int`` starting at position 0. The ``len``, ``low``
-and ``high`` operations are available for open arrays too. Any array with
-a compatible base type can be passed to an openarray parameter, the index
-type does not matter. In addition to arrays sequences can also be passed
-to an open array parameter.
-
-The openarray type cannot be nested: multidimensional openarrays are not
-supported because this is seldom needed and cannot be done efficiently.
-
-.. code-block:: nim
-  proc testOpenArray(x: openArray[int]) = echo repr(x)
-
-  testOpenArray([1,2,3])  # array[]
-  testOpenArray(@[1,2,3]) # seq[]
-
-Varargs
--------
-
-A ``varargs`` parameter is an openarray parameter that additionally
-allows to pass a variable number of arguments to a procedure. The compiler
-converts the list of arguments to an array implicitly:
-
-.. code-block:: nim
-  proc myWriteln(f: File, a: varargs[string]) =
-    for s in items(a):
-      write(f, s)
-    write(f, "\n")
-
-  myWriteln(stdout, "abc", "def", "xyz")
-  # is transformed to:
-  myWriteln(stdout, ["abc", "def", "xyz"])
-
-This transformation is only done if the varargs parameter is the
-last parameter in the procedure header. It is also possible to perform
-type conversions in this context:
-
-.. code-block:: nim
-  proc myWriteln(f: File, a: varargs[string, `$`]) =
-    for s in items(a):
-      write(f, s)
-    write(f, "\n")
-
-  myWriteln(stdout, 123, "abc", 4.0)
-  # is transformed to:
-  myWriteln(stdout, [$123, $"def", $4.0])
-
-In this example ``$`` is applied to any argument that is passed to the
-parameter ``a``. (Note that ``$`` applied to strings is a nop.)
-
-Note that an explicit array constructor passed to a ``varargs`` parameter is
-not wrapped in another implicit array construction:
-
-.. code-block:: nim
-  proc takeV[T](a: varargs[T]) = discard
-
-  takeV([123, 2, 1]) # takeV's T is "int", not "array of int"
-
-
-``varargs[typed]`` is treated specially: It matches a variable list of arguments
-of arbitrary type but *always* constructs an implicit array. This is required
-so that the builtin ``echo`` proc does what is expected:
-
-.. code-block:: nim
-  proc echo*(x: varargs[typed, `$`]) {...}
-
-  echo @[1, 2, 3]
-  # prints "@[1, 2, 3]" and not "123"
-
-
-Tuples and object types
------------------------
-A variable of a tuple or object type is a heterogeneous storage
-container.
-A tuple or object defines various named *fields* of a type. A tuple also
-defines an *order* of the fields. Tuples are meant for heterogeneous storage
-types with no overhead and few abstraction possibilities. The constructor ``()``
-can be used to construct tuples. The order of the fields in the constructor
-must match the order of the tuple's definition. Different tuple-types are
-*equivalent* if they specify the same fields of the same type in the same
-order. The *names* of the fields also have to be identical.
-
-The assignment operator for tuples copies each component.
-The default assignment operator for objects copies each component. Overloading
-of the assignment operator is described in `type-bound-operations-operator`_.
-
-.. code-block:: nim
-
-  type
-    Person = tuple[name: string, age: int] # type representing a person:
-                                           # a person consists of a name
-                                           # and an age
-  var
-    person: Person
-  person = (name: "Peter", age: 30)
-  # the same, but less readable:
-  person = ("Peter", 30)
-
-A tuple with one unnamed field can be constructed with the parentheses and a
-trailing comma:
-
-.. code-block:: nim
-  proc echoUnaryTuple(a: (int,)) =
-    echo a[0]
-
-  echoUnaryTuple (1,)
-
-
-In fact, a trailing comma is allowed for every tuple construction.
-
-The implementation aligns the fields for best access performance. The alignment
-is compatible with the way the C compiler does it.
-
-For consistency  with ``object`` declarations, tuples in a ``type`` section
-can also be defined with indentation instead of ``[]``:
-
-.. code-block:: nim
-  type
-    Person = tuple   # type representing a person
-      name: string   # a person consists of a name
-      age: natural   # and an age
-
-Objects provide many features that tuples do not. Object provide inheritance
-and information hiding. Objects have access to their type at runtime, so that
-the ``of`` operator can be used to determine the object's type. The ``of`` operator
-is similar to the ``instanceof`` operator in Java.
-
-.. code-block:: nim
-  type
-    Person = object of RootObj
-      name*: string   # the * means that `name` is accessible from other modules
-      age: int        # no * means that the field is hidden
-
-    Student = ref object of Person # a student is a person
-      id: int                      # with an id field
-
-  var
-    student: Student
-    person: Person
-  assert(student of Student) # is true
-  assert(student of Person) # also true
-
-Object fields that should be visible from outside the defining module, have to
-be marked by ``*``. In contrast to tuples, different object types are
-never *equivalent*. Objects that have no ancestor are implicitly ``final``
-and thus have no hidden type field. One can use the ``inheritable`` pragma to
-introduce new object roots apart from ``system.RootObj``.
-
-
-Object construction
--------------------
-
-Objects can also be created with an `object construction expression`:idx: that
-has the syntax ``T(fieldA: valueA, fieldB: valueB, ...)`` where ``T`` is
-an ``object`` type or a ``ref object`` type:
-
-.. code-block:: nim
-  var student = Student(name: "Anton", age: 5, id: 3)
-
-Note that, unlike tuples, objects require the field names along with their values.
-For a ``ref object`` type ``system.new`` is invoked implicitly.
-
-
-Object variants
----------------
-Often an object hierarchy is overkill in certain situations where simple
-variant types are needed.
-
-An example:
-
-.. code-block:: nim
-
-  # This is an example how an abstract syntax tree could be modelled in Nim
-  type
-    NodeKind = enum  # the different node types
-      nkInt,          # a leaf with an integer value
-      nkFloat,        # a leaf with a float value
-      nkString,       # a leaf with a string value
-      nkAdd,          # an addition
-      nkSub,          # a subtraction
-      nkIf            # an if statement
-    Node = ref NodeObj
-    NodeObj = object
-      case kind: NodeKind  # the ``kind`` field is the discriminator
-      of nkInt: intVal: int
-      of nkFloat: floatVal: float
-      of nkString: strVal: string
-      of nkAdd, nkSub:
-        leftOp, rightOp: Node
-      of nkIf:
-        condition, thenPart, elsePart: Node
-
-  # create a new case object:
-  var n = Node(kind: nkIf, condition: nil)
-  # accessing n.thenPart is valid because the ``nkIf`` branch is active:
-  n.thenPart = Node(kind: nkFloat, floatVal: 2.0)
-
-  # the following statement raises an `FieldError` exception, because
-  # n.kind's value does not fit and the ``nkString`` branch is not active:
-  n.strVal = ""
-
-  # invalid: would change the active object branch:
-  n.kind = nkInt
-
-  var x = Node(kind: nkAdd, leftOp: Node(kind: nkInt, intVal: 4),
-                            rightOp: Node(kind: nkInt, intVal: 2))
-  # valid: does not change the active object branch:
-  x.kind = nkSub
-
-As can been seen from the example, an advantage to an object hierarchy is that
-no casting between different object types is needed. Yet, access to invalid
-object fields raises an exception.
-
-The syntax of ``case`` in an object declaration follows closely the syntax of
-the ``case`` statement: The branches in a ``case`` section may be indented too.
-
-In the example the ``kind`` field is called the `discriminator`:idx:\: For
-safety its address cannot be taken and assignments to it are restricted: The
-new value must not lead to a change of the active object branch. For an object
-branch switch ``system.reset`` has to be used. Also, when the fields of a
-particular branch are specified during object construction, the correct value
-for the discriminator must be supplied at compile-time.
-
-Package level objects
----------------------
-
-Every Nim module resides in a (nimble) package. An object type can be attached
-to the package it resides in. If that is done, the type can be referenced from
-other modules as an `incomplete`:idx: object type. This features allows to
-break up recursive type dependencies accross module boundaries. Incomplete
-object types are always passed ``byref`` and can only be used in pointer like
-contexts (``var/ref/ptr IncompleteObject``) in general since the compiler does
-not yet know the size of the object. To complete an incomplete object
-the ``package`` pragma has to be used. ``package`` implies ``byref``.
-
-As long as a type ``T`` is incomplete ``sizeof(T)`` or "runtime type
-information" for ``T`` is not available.
-
-
-Example:
-
-.. code-block:: nim
-
-  # module A (in an arbitrary package)
-  type
-    Pack.SomeObject = object ## declare as incomplete object of package 'Pack'
-    Triple = object
-      a, b, c: ref SomeObject ## pointers to incomplete objects are allowed
-
-  ## Incomplete objects can be used as parameters:
-  proc myproc(x: SomeObject) = discard
-
-
-.. code-block:: nim
-
-  # module B (in package "Pack")
-  type
-    SomeObject* {.package.} = object ## Use 'package' to complete the object
-      s, t: string
-      x, y: int
-
-
-Set type
---------
-
-.. include:: sets_fragment.txt
-
-Reference and pointer types
----------------------------
-References (similar to pointers in other programming languages) are a
-way to introduce many-to-one relationships. This means different references can
-point to and modify the same location in memory (also called `aliasing`:idx:).
-
-Nim distinguishes between `traced`:idx: and `untraced`:idx: references.
-Untraced references are also called *pointers*. Traced references point to
-objects of a garbage collected heap, untraced references point to
-manually allocated objects or to objects somewhere else in memory. Thus
-untraced references are *unsafe*. However for certain low-level operations
-(accessing the hardware) untraced references are unavoidable.
-
-Traced references are declared with the **ref** keyword, untraced references
-are declared with the **ptr** keyword.  In general, a `ptr T` is implicitly
-convertible to the `pointer` type.
-
-An empty subscript ``[]`` notation can be used to derefer a reference,
-the ``addr`` procedure returns the address of an item. An address is always
-an untraced reference.
-Thus the usage of ``addr`` is an *unsafe* feature.
-
-The ``.`` (access a tuple/object field operator)
-and ``[]`` (array/string/sequence index operator) operators perform implicit
-dereferencing operations for reference types:
-
-.. code-block:: nim
-
-  type
-    Node = ref NodeObj
-    NodeObj = object
-      le, ri: Node
-      data: int
-
-  var
-    n: Node
-  new(n)
-  n.data = 9
-  # no need to write n[].data; in fact n[].data is highly discouraged!
-
-Automatic dereferencing is also performed for the first argument of a routine
-call. But currently this feature has to be only enabled
-via ``{.experimental: "implicitDeref".}``:
-
-.. code-block:: nim
-  {.experimental: "implicitDeref".}
-
-  proc depth(x: NodeObj): int = ...
-
-  var
-    n: Node
-  new(n)
-  echo n.depth
-  # no need to write n[].depth either
-
-
-
-In order to simplify structural type checking, recursive tuples are not valid:
-
-.. code-block:: nim
-  # invalid recursion
-  type MyTuple = tuple[a: ref MyTuple]
-
-Likewise ``T = ref T`` is an invalid type.
-
-As a syntactical extension ``object`` types can be anonymous if
-declared in a type section via the ``ref object`` or ``ptr object`` notations.
-This feature is useful if an object should only gain reference semantics:
-
-.. code-block:: nim
-
-  type
-    Node = ref object
-      le, ri: Node
-      data: int
-
-
-To allocate a new traced object, the built-in procedure ``new`` has to be used.
-To deal with untraced memory, the procedures ``alloc``, ``dealloc`` and
-``realloc`` can be used. The documentation of the system module contains
-further information.
-
-If a reference points to *nothing*, it has the value ``nil``.
-
-Special care has to be taken if an untraced object contains traced objects like
-traced references, strings or sequences: in order to free everything properly,
-the built-in procedure ``GCunref`` has to be called before freeing the untraced
-memory manually:
-
-.. code-block:: nim
-  type
-    Data = tuple[x, y: int, s: string]
-
-  # allocate memory for Data on the heap:
-  var d = cast[ptr Data](alloc0(sizeof(Data)))
-
-  # create a new string on the garbage collected heap:
-  d.s = "abc"
-
-  # tell the GC that the string is not needed anymore:
-  GCunref(d.s)
-
-  # free the memory:
-  dealloc(d)
-
-Without the ``GCunref`` call the memory allocated for the ``d.s`` string would
-never be freed. The example also demonstrates two important features for low
-level programming: the ``sizeof`` proc returns the size of a type or value
-in bytes. The ``cast`` operator can circumvent the type system: the compiler
-is forced to treat the result of the ``alloc0`` call (which returns an untyped
-pointer) as if it would have the type ``ptr Data``. Casting should only be
-done if it is unavoidable: it breaks type safety and bugs can lead to
-mysterious crashes.
-
-**Note**: The example only works because the memory is initialized to zero
-(``alloc0`` instead of ``alloc`` does this): ``d.s`` is thus initialized to
-binary zero which the string assignment can handle. One needs to know low level
-details like this when mixing garbage collected data with unmanaged memory.
-
-.. XXX finalizers for traced objects
-
-
-Not nil annotation
-------------------
-
-All types for that ``nil`` is a valid value can be annotated to
-exclude ``nil`` as a valid value with the ``not nil`` annotation:
-
-.. code-block:: nim
-  type
-    PObject = ref TObj not nil
-    TProc = (proc (x, y: int)) not nil
-
-  proc p(x: PObject) =
-    echo "not nil"
-
-  # compiler catches this:
-  p(nil)
-
-  # and also this:
-  var x: PObject
-  p(x)
-
-The compiler ensures that every code path initializes variables which contain
-non nilable pointers. The details of this analysis are still to be specified
-here.
-
-
-Memory regions
---------------
-
-The types ``ref`` and ``ptr`` can get an optional ``region`` annotation.
-A region has to be an object type.
-
-Regions are very useful to separate user space and kernel memory in the
-development of OS kernels:
-
-.. code-block:: nim
-  type
-    Kernel = object
-    Userspace = object
-
-  var a: Kernel ptr Stat
-  var b: Userspace ptr Stat
-
-  # the following does not compile as the pointer types are incompatible:
-  a = b
-
-As the example shows ``ptr`` can also be used as a binary
-operator, ``region ptr T`` is a shortcut for ``ptr[region, T]``.
-
-In order to make generic code easier to write ``ptr T`` is a subtype
-of ``ptr[R, T]`` for any ``R``.
-
-Furthermore the subtype relation of the region object types is lifted to
-the pointer types: If ``A <: B`` then ``ptr[A, T] <: ptr[B, T]``. This can be
-used to model subregions of memory. As a special typing rule ``ptr[R, T]`` is
-not compatible to ``pointer`` to prevent the following from compiling:
-
-.. code-block:: nim
-  # from system
-  proc dealloc(p: pointer)
-
-  # wrap some scripting language
-  type
-    PythonsHeap = object
-    PyObjectHeader = object
-      rc: int
-      typ: pointer
-    PyObject = ptr[PythonsHeap, PyObjectHeader]
-
-  proc createPyObject(): PyObject {.importc: "...".}
-  proc destroyPyObject(x: PyObject) {.importc: "...".}
-
-  var foo = createPyObject()
-  # type error here, how convenient:
-  dealloc(foo)
-
-
-Future directions:
-
-* Memory regions might become available for  ``string`` and ``seq`` too.
-* Builtin regions like ``private``, ``global`` and ``local`` might be
-  useful for an OpenCL target.
-* Builtin "regions" can model ``lent`` and ``unique`` pointers.
-* An assignment operator can be attached to a region so that proper write
-  barriers can be generated. This would imply that the GC can be implemented
-  completely in user-space.
-
-
-Procedural type
----------------
-A procedural type is internally a pointer to a procedure. ``nil`` is
-an allowed value for variables of a procedural type. Nim uses procedural
-types to achieve `functional`:idx: programming techniques.
-
-Examples:
-
-.. code-block:: nim
-
-  proc printItem(x: int) = ...
-
-  proc forEach(c: proc (x: int) {.cdecl.}) =
-    ...
-
-  forEach(printItem)  # this will NOT compile because calling conventions differ
-
-
-.. code-block:: nim
-
-  type
-    OnMouseMove = proc (x, y: int) {.closure.}
-
-  proc onMouseMove(mouseX, mouseY: int) =
-    # has default calling convention
-    echo "x: ", mouseX, " y: ", mouseY
-
-  proc setOnMouseMove(mouseMoveEvent: OnMouseMove) = discard
-
-  # ok, 'onMouseMove' has the default calling convention, which is compatible
-  # to 'closure':
-  setOnMouseMove(onMouseMove)
-
-
-A subtle issue with procedural types is that the calling convention of the
-procedure influences the type compatibility: procedural types are only
-compatible if they have the same calling convention. As a special extension,
-a procedure of the calling convention ``nimcall`` can be passed to a parameter
-that expects a proc of the calling convention ``closure``.
-
-Nim supports these `calling conventions`:idx:\:
-
-`nimcall`:idx:
-    is the default convention used for a Nim **proc**. It is the
-    same as ``fastcall``, but only for C compilers that support ``fastcall``.
-
-`closure`:idx:
-    is the default calling convention for a **procedural type** that lacks
-    any pragma annotations. It indicates that the procedure has a hidden
-    implicit parameter (an *environment*). Proc vars that have the calling
-    convention ``closure`` take up two machine words: One for the proc pointer
-    and another one for the pointer to implicitly passed environment.
-
-`stdcall`:idx:
-    This 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
-    the ``__cdecl`` keyword.
-
-`safecall`:idx:
-    This is the safecall convention as specified by Microsoft. The generated C
-    procedure is declared with the ``__safecall`` keyword. The word *safe*
-    refers to the fact that all hardware registers shall be pushed to the
-    hardware stack.
-
-`inline`:idx:
-    The inline convention means the the caller should not call the procedure,
-    but inline its code directly. Note that Nim does not inline, but leaves
-    this to the C compiler; it generates ``__inline`` procedures. This is
-    only a hint for the compiler: it may completely ignore it and
-    it may inline procedures that are not marked as ``inline``.
-
-`fastcall`:idx:
-    Fastcall means different things to different C compilers. One gets whatever
-    the C ``__fastcall`` means.
-
-`syscall`:idx:
-    The syscall convention is the same as ``__syscall`` in C. It is used for
-    interrupts.
-
-`noconv`:idx:
-    The generated C code will not have any explicit calling convention and thus
-    use the C compiler's default calling convention. This is needed because
-    Nim's default calling convention for procedures is ``fastcall`` to
-    improve speed.
-
-Most calling conventions exist only for the Windows 32-bit platform.
-
-The default calling convention is ``nimcall``, unless it is an inner proc (a
-proc inside of a proc). For an inner proc an analysis is performed whether it
-accesses its environment. If it does so, it has the calling convention
-``closure``, otherwise it has the calling convention ``nimcall``.
-
-
-Distinct type
--------------
-
-A ``distinct`` type is new type derived from a `base type`:idx: that is
-incompatible with its base type. In particular, it is an essential property
-of a distinct type that it **does not** imply a subtype relation between it
-and its base type. Explicit type conversions from a distinct type to its
-base type and vice versa are allowed.
-
-
-Modelling currencies
-~~~~~~~~~~~~~~~~~~~~
-
-A distinct type can be used to model different physical `units`:idx: with a
-numerical base type, for example. The following example models currencies.
-
-Different currencies should not be mixed in monetary calculations. Distinct
-types are a perfect tool to model different currencies:
-
-.. code-block:: nim
-  type
-    Dollar = distinct int
-    Euro = distinct int
-
-  var
-    d: Dollar
-    e: Euro
-
-  echo d + 12
-  # Error: cannot add a number with no unit and a ``Dollar``
-
-Unfortunately, ``d + 12.Dollar`` is not allowed either,
-because ``+`` is defined for ``int`` (among others), not for ``Dollar``. So
-a ``+`` for dollars needs to be defined:
-
-.. code-block::
-  proc `+` (x, y: Dollar): Dollar =
-    result = Dollar(int(x) + int(y))
-
-It does not make sense to multiply a dollar with a dollar, but with a
-number without unit; and the same holds for division:
-
-.. code-block::
-  proc `*` (x: Dollar, y: int): Dollar =
-    result = Dollar(int(x) * y)
-
-  proc `*` (x: int, y: Dollar): Dollar =
-    result = Dollar(x * int(y))
-
-  proc `div` ...
-
-This quickly gets tedious. The implementations are trivial and the compiler
-should not generate all this code only to optimize it away later - after all
-``+`` for dollars should produce the same binary code as ``+`` for ints.
-The pragma `borrow`:idx: has been designed to solve this problem; in principle
-it generates the above trivial implementations:
-
-.. code-block:: nim
-  proc `*` (x: Dollar, y: int): Dollar {.borrow.}
-  proc `*` (x: int, y: Dollar): Dollar {.borrow.}
-  proc `div` (x: Dollar, y: int): Dollar {.borrow.}
-
-The ``borrow`` pragma makes the compiler use the same implementation as
-the proc that deals with the distinct type's base type, so no code is
-generated.
-
-But it seems all this boilerplate code needs to be repeated for the ``Euro``
-currency. This can be solved with templates_.
-
-.. code-block:: nim
-  template additive(typ: typedesc) =
-    proc `+` *(x, y: typ): typ {.borrow.}
-    proc `-` *(x, y: typ): typ {.borrow.}
-
-    # unary operators:
-    proc `+` *(x: typ): typ {.borrow.}
-    proc `-` *(x: typ): typ {.borrow.}
-
-  template multiplicative(typ, base: typedesc) =
-    proc `*` *(x: typ, y: base): typ {.borrow.}
-    proc `*` *(x: base, y: typ): typ {.borrow.}
-    proc `div` *(x: typ, y: base): typ {.borrow.}
-    proc `mod` *(x: typ, y: base): typ {.borrow.}
-
-  template comparable(typ: typedesc) =
-    proc `<` * (x, y: typ): bool {.borrow.}
-    proc `<=` * (x, y: typ): bool {.borrow.}
-    proc `==` * (x, y: typ): bool {.borrow.}
-
-  template defineCurrency(typ, base: untyped) =
-    type
-      typ* = distinct base
-    additive(typ)
-    multiplicative(typ, base)
-    comparable(typ)
-
-  defineCurrency(Dollar, int)
-  defineCurrency(Euro, int)
-
-
-The borrow pragma can also be used to annotate the distinct type to allow
-certain builtin operations to be lifted:
-
-.. code-block:: nim
-  type
-    Foo = object
-      a, b: int
-      s: string
-
-    Bar {.borrow: `.`.} = distinct Foo
-
-  var bb: ref Bar
-  new bb
-  # field access now valid
-  bb.a = 90
-  bb.s = "abc"
-
-Currently only the dot accessor can be borrowed in this way.
-
-
-Avoiding SQL injection attacks
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-An SQL statement that is passed from Nim to an SQL database might be
-modelled as a string. However, using string templates and filling in the
-values is vulnerable to the famous `SQL injection attack`:idx:\:
-
-.. code-block:: nim
-  import strutils
-
-  proc query(db: DbHandle, statement: string) = ...
-
-  var
-    username: string
-
-  db.query("SELECT FROM users WHERE name = '$1'" % username)
-  # Horrible security hole, but the compiler does not mind!
-
-This can be avoided by distinguishing strings that contain SQL from strings
-that don't. Distinct types provide a means to introduce a new string type
-``SQL`` that is incompatible with ``string``:
-
-.. code-block:: nim
-  type
-    SQL = distinct string
-
-  proc query(db: DbHandle, statement: SQL) = ...
-
-  var
-    username: string
-
-  db.query("SELECT FROM users WHERE name = '$1'" % username)
-  # Error at compile time: `query` expects an SQL string!
-
-
-It is an essential property of abstract types that they **do not** imply a
-subtype relation between the abstract type and its base type. Explicit type
-conversions from ``string`` to ``SQL`` are allowed:
-
-.. code-block:: nim
-  import strutils, sequtils
-
-  proc properQuote(s: string): SQL =
-    # quotes a string properly for an SQL statement
-    return SQL(s)
-
-  proc `%` (frmt: SQL, values: openarray[string]): SQL =
-    # quote each argument:
-    let v = values.mapIt(SQL, properQuote(it))
-    # we need a temporary type for the type conversion :-(
-    type StrSeq = seq[string]
-    # call strutils.`%`:
-    result = SQL(string(frmt) % StrSeq(v))
-
-  db.query("SELECT FROM users WHERE name = '$1'".SQL % [username])
-
-Now we have compile-time checking against SQL injection attacks.  Since
-``"".SQL`` is transformed to ``SQL("")`` no new syntax is needed for nice
-looking ``SQL`` string literals. The hypothetical ``SQL`` type actually
-exists in the library as the `TSqlQuery type <db_sqlite.html#TSqlQuery>`_ of
-modules like `db_sqlite <db_sqlite.html>`_.
-
-
-Void type
----------
-
-The ``void`` type denotes the absence of any type. Parameters of
-type ``void`` are treated as non-existent, ``void`` as a return type means that
-the procedure does not return a value:
-
-.. code-block:: nim
-  proc nothing(x, y: void): void =
-    echo "ha"
-
-  nothing() # writes "ha" to stdout
-
-The ``void`` type is particularly useful for generic code:
-
-.. code-block:: nim
-  proc callProc[T](p: proc (x: T), x: T) =
-    when T is void:
-      p()
-    else:
-      p(x)
-
-  proc intProc(x: int) = discard
-  proc emptyProc() = discard
-
-  callProc[int](intProc, 12)
-  callProc[void](emptyProc)
-
-However, a ``void`` type cannot be inferred in generic code:
-
-.. code-block:: nim
-  callProc(emptyProc)
-  # Error: type mismatch: got (proc ())
-  # but expected one of:
-  # callProc(p: proc (T), x: T)
-
-The ``void`` type is only valid for parameters and return types; other symbols
-cannot have the type ``void``.
-
-
-Auto type
----------
-
-The ``auto`` type can only be used for return types and parameters. For return
-types it causes the compiler to infer the type from the routine body:
-
-.. code-block:: nim
-  proc returnsInt(): auto = 1984
-
-For parameters it currently creates implicitly generic routines:
-
-.. code-block:: nim
-  proc foo(a, b: auto) = discard
-
-Is the same as:
-
-.. code-block:: nim
-  proc foo[T1, T2](a: T1, b: T2) = discard
-
-However later versions of the language might change this to mean "infer the
-parameters' types from the body". Then the above ``foo`` would be rejected as
-the parameters' types can not be inferred from an empty ``discard`` statement.
-
-
-Type relations
-==============
-
-The following section defines several relations on types that are needed to
-describe the type checking done by the compiler.
-
-
-Type equality
--------------
-Nim uses structural type equivalence for most types. Only for objects,
-enumerations and distinct types name equivalence is used. The following
-algorithm, *in pseudo-code*, determines type equality:
-
-.. code-block:: nim
-  proc typeEqualsAux(a, b: PType,
-                     s: var HashSet[(PType, PType)]): bool =
-    if (a,b) in s: return true
-    incl(s, (a,b))
-    if a.kind == b.kind:
-      case a.kind
-      of int, intXX, float, floatXX, char, string, cstring, pointer,
-          bool, nil, void:
-        # leaf type: kinds identical; nothing more to check
-        result = true
-      of ref, ptr, var, set, seq, openarray:
-        result = typeEqualsAux(a.baseType, b.baseType, s)
-      of range:
-        result = typeEqualsAux(a.baseType, b.baseType, s) and
-          (a.rangeA == b.rangeA) and (a.rangeB == b.rangeB)
-      of array:
-        result = typeEqualsAux(a.baseType, b.baseType, s) and
-                 typeEqualsAux(a.indexType, b.indexType, s)
-      of tuple:
-        if a.tupleLen == b.tupleLen:
-          for i in 0..a.tupleLen-1:
-            if not typeEqualsAux(a[i], b[i], s): return false
-          result = true
-      of object, enum, distinct:
-        result = a == b
-      of proc:
-        result = typeEqualsAux(a.parameterTuple, b.parameterTuple, s) and
-                 typeEqualsAux(a.resultType, b.resultType, s) and
-                 a.callingConvention == b.callingConvention
-
-  proc typeEquals(a, b: PType): bool =
-    var s: HashSet[(PType, PType)] = {}
-    result = typeEqualsAux(a, b, s)
-
-Since types are graphs which can have cycles, the above algorithm needs an
-auxiliary set ``s`` to detect this case.
-
-
-Type equality modulo type distinction
--------------------------------------
-
-The following algorithm (in pseudo-code) determines whether two types
-are equal with no respect to ``distinct`` types. For brevity the cycle check
-with an auxiliary set ``s`` is omitted:
-
-.. code-block:: nim
-  proc typeEqualsOrDistinct(a, b: PType): bool =
-    if a.kind == b.kind:
-      case a.kind
-      of int, intXX, float, floatXX, char, string, cstring, pointer,
-          bool, nil, void:
-        # leaf type: kinds identical; nothing more to check
-        result = true
-      of ref, ptr, var, set, seq, openarray:
-        result = typeEqualsOrDistinct(a.baseType, b.baseType)
-      of range:
-        result = typeEqualsOrDistinct(a.baseType, b.baseType) and
-          (a.rangeA == b.rangeA) and (a.rangeB == b.rangeB)
-      of array:
-        result = typeEqualsOrDistinct(a.baseType, b.baseType) and
-                 typeEqualsOrDistinct(a.indexType, b.indexType)
-      of tuple:
-        if a.tupleLen == b.tupleLen:
-          for i in 0..a.tupleLen-1:
-            if not typeEqualsOrDistinct(a[i], b[i]): return false
-          result = true
-      of distinct:
-        result = typeEqualsOrDistinct(a.baseType, b.baseType)
-      of object, enum:
-        result = a == b
-      of proc:
-        result = typeEqualsOrDistinct(a.parameterTuple, b.parameterTuple) and
-                 typeEqualsOrDistinct(a.resultType, b.resultType) and
-                 a.callingConvention == b.callingConvention
-    elif a.kind == distinct:
-      result = typeEqualsOrDistinct(a.baseType, b)
-    elif b.kind == distinct:
-      result = typeEqualsOrDistinct(a, b.baseType)
-
-
-Subtype relation
-----------------
-If object ``a`` inherits from ``b``, ``a`` is a subtype of ``b``. This subtype
-relation is extended to the types ``var``, ``ref``, ``ptr``:
-
-.. code-block:: nim
-  proc isSubtype(a, b: PType): bool =
-    if a.kind == b.kind:
-      case a.kind
-      of object:
-        var aa = a.baseType
-        while aa != nil and aa != b: aa = aa.baseType
-        result = aa == b
-      of var, ref, ptr:
-        result = isSubtype(a.baseType, b.baseType)
-
-.. XXX nil is a special value!
-
-
-Covariance
-----------
-
-Covariance in Nim can be introduced only though pointer-like types such
-as ``ptr`` and ``ref``. Sequence, Array and OpenArray types, instantiated
-with pointer-like types will be considered covariant if and only if they
-are also immutable. The introduction of a ``var`` modifier or additional
-``ptr`` or ``ref`` indirections would result in invariant treatment of
-these types.
-
-``proc`` types are currently always invariant, but future versions of Nim
-may relax this rule.
-
-User-defined generic types may also be covariant with respect to some of
-their parameters. By default, all generic params are considered invariant,
-but you may choose the apply the prefix modifier ``in`` to a parameter to
-make it contravariant or ``out`` to make it covariant:
-
-.. code-block:: nim
-  type
-    AnnotatedPtr[out T] =
-      metadata: MyTypeInfo
-      p: ref T
-
-    RingBuffer[out T] =
-      startPos: int
-      data: seq[T]
-
-    Action {.importcpp: "std::function<void ('0)>".} [in T] = object
-
-When the designated generic parameter is used to instantiate a pointer-like
-type as in the case of `AnnotatedPtr` above, the resulting generic type will
-also have pointer-like covariance:
-
-.. code-block:: nim
-  type
-    GuiWidget = object of RootObj
-    Button = object of GuiWidget
-    ComboBox = object of GuiWidget
-
-  var
-    widgetPtr: AnnotatedPtr[GuiWidget]
-    buttonPtr: AnnotatedPtr[Button]
-
-  ...
-
-  proc drawWidget[T](x: AnnotatedPtr[GuiWidget]) = ...
-
-  # you can call procs expecting base types by supplying a derived type
-  drawWidget(buttonPtr)
-
-  # and you can convert more-specific pointer types to more general ones
-  widgetPtr = buttonPtr
-
-Just like with regular pointers, covariance will be enabled only for immutable
-values:
-
-.. code-block:: nim
-  proc makeComboBox[T](x: var AnnotatedPtr[GuiWidget]) =
-    x.p = new(ComboBox)
-
-  makeComboBox(buttonPtr) # Error, AnnotatedPtr[Button] cannot be modified
-                          # to point to a ComboBox
-
-On the other hand, in the `RingBuffer` example above, the designated generic
-param is used to instantiate the non-pointer ``seq`` type, which means that
-the resulting generic type will have covariance that mimics an array or
-sequence (i.e. it will be covariant only when instantiated with ``ptr`` and
-``ref`` types):
-
-.. code-block:: nim
-
-  type
-    Base = object of RootObj
-    Derived = object of Base
-
-  proc consumeBaseValues(b: RingBuffer[Base]) = ...
-
-  var derivedValues: RingBuffer[Derived]
-
-  consumeBaseValues(derivedValues) # Error, Base and Derived values may differ
-                                   # in size
-
-  proc consumeBasePointers(b: RingBuffer[ptr Base]) = ...
-
-  var derivedPointers: RingBuffer[ptr Derived]
-
-  consumeBaseValues(derivedPointers) # This is legal
-
-Please note that Nim will treat the user-defined pointer-like types as
-proper alternatives to the built-in pointer types. That is, types such
-as `seq[AnnotatedPtr[T]]` or `RingBuffer[AnnotatedPtr[T]]` will also be
-considered covariant and you can create new pointer-like types by instantiating
-other user-defined pointer-like types.
-
-The contravariant parameters introduced with the ``in`` modifier are currently
-useful only when interfacing with imported types having such semantics.
-
-
-Convertible relation
---------------------
-A type ``a`` is **implicitly** convertible to type ``b`` iff the following
-algorithm returns true:
-
-.. code-block:: nim
-  # XXX range types?
-  proc isImplicitlyConvertible(a, b: PType): bool =
-    if isSubtype(a, b) or isCovariant(a, b):
-      return true
-    case a.kind
-    of int:     result = b in {int8, int16, int32, int64, uint, uint8, uint16,
-                               uint32, uint64, float, float32, float64}
-    of int8:    result = b in {int16, int32, int64, int}
-    of int16:   result = b in {int32, int64, int}
-    of int32:   result = b in {int64, int}
-    of uint:    result = b in {uint32, uint64}
-    of uint8:   result = b in {uint16, uint32, uint64}
-    of uint16:  result = b in {uint32, uint64}
-    of uint32:  result = b in {uint64}
-    of float:   result = b in {float32, float64}
-    of float32: result = b in {float64, float}
-    of float64: result = b in {float32, float}
-    of seq:
-      result = b == openArray and typeEquals(a.baseType, b.baseType)
-    of array:
-      result = b == openArray and typeEquals(a.baseType, b.baseType)
-      if a.baseType == char and a.indexType.rangeA == 0:
-        result = b = cstring
-    of cstring, ptr:
-      result = b == pointer
-    of string:
-      result = b == cstring
-
-A type ``a`` is **explicitly** convertible to type ``b`` iff the following
-algorithm returns true:
-
-.. code-block:: nim
-  proc isIntegralType(t: PType): bool =
-    result = isOrdinal(t) or t.kind in {float, float32, float64}
-
-  proc isExplicitlyConvertible(a, b: PType): bool =
-    result = false
-    if isImplicitlyConvertible(a, b): return true
-    if typeEqualsOrDistinct(a, b): return true
-    if isIntegralType(a) and isIntegralType(b): return true
-    if isSubtype(a, b) or isSubtype(b, a): return true
-
-The convertible relation can be relaxed by a user-defined type
-`converter`:idx:.
-
-.. code-block:: nim
-  converter toInt(x: char): int = result = ord(x)
-
-  var
-    x: int
-    chr: char = 'a'
-
-  # implicit conversion magic happens here
-  x = chr
-  echo x # => 97
-  # you can use the explicit form too
-  x = chr.toInt
-  echo x # => 97
-
-The type conversion ``T(a)`` is an L-value if ``a`` is an L-value and
-``typeEqualsOrDistinct(T, type(a))`` holds.
-
-
-Assignment compatibility
-------------------------
-
-An expression ``b`` can be assigned to an expression ``a`` iff ``a`` is an
-`l-value` and ``isImplicitlyConvertible(b.typ, a.typ)`` holds.
-
-
-Overloading resolution
-======================
-
-In a call ``p(args)`` the routine ``p`` that matches best is selected. If
-multiple routines match equally well, the ambiguity is reported at compiletime.
-
-Every arg in args needs to match. There are multiple different categories how an
-argument can match. Let ``f`` be the formal parameter's type and ``a`` the type
-of the argument.
-
-1. Exact match: ``a`` and ``f`` are of the same type.
-2. Literal match: ``a`` is an integer literal of value ``v``
-   and ``f`` is a signed or unsigned integer type and ``v`` is in ``f``'s
-   range. Or:  ``a`` is a floating point literal of value ``v``
-   and ``f`` is a floating point type and ``v`` is in ``f``'s
-   range.
-3. Generic match: ``f`` is a generic type and ``a`` matches, for
-   instance ``a`` is ``int`` and ``f`` is a generic (constrained) parameter
-   type (like in ``[T]`` or ``[T: int|char]``.
-4. Subrange or subtype match: ``a`` is a ``range[T]`` and ``T``
-   matches ``f`` exactly. Or: ``a`` is a subtype of ``f``.
-5. Integral conversion match: ``a`` is convertible to ``f`` and ``f`` and ``a``
-   is some integer or floating point type.
-6. Conversion match: ``a`` is convertible to ``f``, possibly via a user
-   defined ``converter``.
-
-These matching categories have a priority: An exact match is better than a
-literal match and that is better than a generic match etc. In the following
-``count(p, m)`` counts the number of matches of the matching category ``m``
-for the routine ``p``.
-
-A routine ``p`` matches better than a routine ``q`` if the following
-algorithm returns true::
-
-  for each matching category m in ["exact match", "literal match",
-                                  "generic match", "subtype match",
-                                  "integral match", "conversion match"]:
-    if count(p, m) > count(q, m): return true
-    elif count(p, m) == count(q, m):
-      discard "continue with next category m"
-    else:
-      return false
-  return "ambiguous"
-
-
-Some examples:
-
-.. code-block:: nim
-  proc takesInt(x: int) = echo "int"
-  proc takesInt[T](x: T) = echo "T"
-  proc takesInt(x: int16) = echo "int16"
-
-  takesInt(4) # "int"
-  var x: int32
-  takesInt(x) # "T"
-  var y: int16
-  takesInt(y) # "int16"
-  var z: range[0..4] = 0
-  takesInt(z) # "T"
-
-
-If this algorithm returns "ambiguous" further disambiguation is performed:
-If the argument ``a`` matches both the parameter type ``f`` of ``p``
-and ``g`` of ``q`` via a subtyping relation, the inheritance depth is taken
-into account:
-
-.. code-block:: nim
-  type
-    A = object of RootObj
-    B = object of A
-    C = object of B
-
-  proc p(obj: A) =
-    echo "A"
-
-  proc p(obj: B) =
-    echo "B"
-
-  var c = C()
-  # not ambiguous, calls 'B', not 'A' since B is a subtype of A
-  # but not vice versa:
-  p(c)
-
-  proc pp(obj: A, obj2: B) = echo "A B"
-  proc pp(obj: B, obj2: A) = echo "B A"
-
-  # but this is ambiguous:
-  pp(c, c)
-
-
-Likewise for generic matches the most specialized generic type (that still
-matches) is preferred:
-
-.. code-block:: nim
-  proc gen[T](x: ref ref T) = echo "ref ref T"
-  proc gen[T](x: ref T) = echo "ref T"
-  proc gen[T](x: T) = echo "T"
-
-  var ri: ref int
-  gen(ri) # "ref T"
-
-
-Overloading based on 'var T'
-----------------------------
-
-If the formal parameter ``f`` is of type ``var T`` in addition to the ordinary
-type checking, the argument is checked to be an `l-value`:idx:. ``var T``
-matches better than just ``T`` then.
-
-.. code-block:: nim
-  proc sayHi(x: int): string =
-    # matches a non-var int
-    result = $x
-  proc sayHi(x: var int): string =
-    # matches a var int
-    result = $(x + 10)
-
-  proc sayHello(x: int) =
-    var m = x # a mutable version of x
-    echo sayHi(x) # matches the non-var version of sayHi
-    echo sayHi(m) # matches the var version of sayHi
-
-  sayHello(3) # 3
-              # 13
-
-Automatic dereferencing
------------------------
-
-If the `experimental mode <#pragmas-experimental-pragma>`_ is active and no other match
-is found, the first argument ``a`` is dereferenced automatically if it's a
-pointer type and overloading resolution is tried with ``a[]`` instead.
-
-Automatic self insertions
--------------------------
-
-Starting with version 0.14 of the language, Nim supports ``field`` as a
-shortcut for ``self.field`` comparable to the `this`:idx: keyword in Java
-or C++. This feature has to be explicitly enabled via a ``{.this: self.}``
-statement pragma. This pragma is active for the rest of the module:
-
-.. code-block:: nim
-  type
-    Parent = object of RootObj
-      parentField: int
-    Child = object of Parent
-      childField: int
-
-  {.this: self.}
-  proc sumFields(self: Child): int =
-    result = parentField + childField
-    # is rewritten to:
-    # result = self.parentField + self.childField
-
-Instead of ``self`` any other identifier can be used too, but
-``{.this: self.}`` will become the default directive for the whole language
-eventually.
-
-In addition to fields, routine applications are also rewritten, but only
-if no other interpretation of the call is possible:
-
-.. code-block:: nim
-  proc test(self: Child) =
-    echo childField, " ", sumFields()
-    # is rewritten to:
-    echo self.childField, " ", sumFields(self)
-    # but NOT rewritten to:
-    echo self, self.childField, " ", sumFields(self)
-
-
-Lazy type resolution for untyped
---------------------------------
-
-**Note**: An `unresolved`:idx: expression is an expression for which no symbol
-lookups and no type checking have been performed.
-
-Since templates and macros that are not declared as ``immediate`` participate
-in overloading resolution it's essential to have a way to pass unresolved
-expressions to a template or macro. This is what the meta-type ``untyped``
-accomplishes:
-
-.. code-block:: nim
-  template rem(x: untyped) = discard
-
-  rem unresolvedExpression(undeclaredIdentifier)
-
-A parameter of type ``untyped`` always matches any argument (as long as there is
-any argument passed to it).
-
-But one has to watch out because other overloads might trigger the
-argument's resolution:
-
-.. code-block:: nim
-  template rem(x: untyped) = discard
-  proc rem[T](x: T) = discard
-
-  # undeclared identifier: 'unresolvedExpression'
-  rem unresolvedExpression(undeclaredIdentifier)
-
-``untyped`` and ``varargs[untyped]`` are the only metatype that are lazy in this sense, the other
-metatypes ``typed`` and ``typedesc`` are not lazy.
-
-
-Varargs matching
-----------------
-
-See `Varargs <#types-varargs>`_.
-
-
-Statements and expressions
-==========================
-
-Nim uses the common statement/expression paradigm: Statements do not
-produce a value in contrast to expressions. However, some expressions are
-statements.
-
-Statements are separated into `simple statements`:idx: and
-`complex statements`:idx:.
-Simple statements are statements that cannot contain other statements like
-assignments, calls or the ``return`` statement; complex statements can
-contain other statements. To avoid the `dangling else problem`:idx:, complex
-statements always have to be indented. The details can be found in the grammar.
-
-
-Statement list expression
--------------------------
-
-Statements can also occur in an expression context that looks
-like ``(stmt1; stmt2; ...; ex)``. This is called
-an statement list expression or ``(;)``. The type
-of ``(stmt1; stmt2; ...; ex)`` is the type of ``ex``. All the other statements
-must be of type ``void``. (One can use ``discard`` to produce a ``void`` type.)
-``(;)`` does not introduce a new scope.
-
-
-Discard statement
------------------
-
-Example:
-
-.. code-block:: nim
-  proc p(x, y: int): int =
-    result = x + y
-
-  discard p(3, 4) # discard the return value of `p`
-
-The ``discard`` statement evaluates its expression for side-effects and
-throws the expression's resulting value away.
-
-Ignoring the return value of a procedure without using a discard statement is
-a static error.
-
-The return value can be ignored implicitly if the called proc/iterator has
-been declared with the `discardable`:idx: pragma:
-
-.. code-block:: nim
-  proc p(x, y: int): int {.discardable.} =
-    result = x + y
-
-  p(3, 4) # now valid
-
-An empty ``discard`` statement is often used as a null statement:
-
-.. code-block:: nim
-  proc classify(s: string) =
-    case s[0]
-    of SymChars, '_': echo "an identifier"
-    of '0'..'9': echo "a number"
-    else: discard
-
-
-Void context
-------------
-
-In a list of statements every expression except the last one needs to have the
-type ``void``. In addition to this rule an assignment to the builtin ``result``
-symbol also triggers a mandatory ``void`` context for the subsequent expressions:
-
-.. code-block:: nim
-  proc invalid*(): string =
-    result = "foo"
-    "invalid"  # Error: value of type 'string' has to be discarded
-
-.. code-block:: nim
-  proc valid*(): string =
-    let x = 317
-    "valid"
-
-
-Var statement
--------------
-
-Var statements declare new local and global variables and
-initialize them. A comma separated list of variables can be used to specify
-variables of the same type:
-
-.. code-block:: nim
-
-  var
-    a: int = 0
-    x, y, z: int
-
-If an initializer is given the type can be omitted: the variable is then of the
-same type as the initializing expression. Variables are always initialized
-with a default value if there is no initializing expression. The default
-value depends on the type and is always a zero in binary.
-
-============================    ==============================================
-Type                            default value
-============================    ==============================================
-any integer type                0
-any float                       0.0
-char                            '\\0'
-bool                            false
-ref or pointer type             nil
-procedural type                 nil
-sequence                        ``@[]``
-string                          ``""``
-tuple[x: A, y: B, ...]          (default(A), default(B), ...)
-                                (analogous for objects)
-array[0..., T]                  [default(T), ...]
-range[T]                        default(T); this may be out of the valid range
-T = enum                        cast[T](0); this may be an invalid value
-============================    ==============================================
-
-
-The implicit initialization can be avoided for optimization reasons with the
-`noinit`:idx: pragma:
-
-.. code-block:: nim
-  var
-    a {.noInit.}: array[0..1023, char]
-
-If a proc is annotated with the ``noinit`` pragma this refers to its implicit
-``result`` variable:
-
-.. code-block:: nim
-  proc returnUndefinedValue: int {.noinit.} = discard
-
-
-The implicit initialization can be also prevented by the `requiresInit`:idx:
-type pragma. The compiler requires an explicit initialization for the object
-and all of its fields. However it does a `control flow analysis`:idx: to prove
-the variable has been initialized and does not rely on syntactic properties:
-
-.. code-block:: nim
-  type
-    MyObject = object {.requiresInit.}
-
-  proc p() =
-    # the following is valid:
-    var x: MyObject
-    if someCondition():
-      x = a()
-    else:
-      x = a()
-    # use x
-
-
-let statement
--------------
-
-A ``let`` statement declares new local and global `single assignment`:idx:
-variables and binds a value to them. The syntax is the same as that of the ``var``
-statement, except that the keyword ``var`` is replaced by the keyword ``let``.
-Let variables are not l-values and can thus not be passed to ``var`` parameters
-nor can their address be taken. They cannot be assigned new values.
-
-For let variables the same pragmas are available as for ordinary variables.
-
-
-Tuple unpacking
----------------
-
-In a ``var`` or ``let`` statement tuple unpacking can be performed. The special
-identifier ``_`` can be used to ignore some parts of the tuple:
-
-.. code-block:: nim
-    proc returnsTuple(): (int, int, int) = (4, 2, 3)
-
-    let (x, _, z) = returnsTuple()
-
-
-
-Const section
--------------
-
-`Constants`:idx: are symbols which are bound to a value. The constant's value
-cannot change. The compiler must be able to evaluate the expression in a
-constant declaration at compile time.
-
-Nim contains a sophisticated compile-time evaluator, so procedures which
-have no side-effect can be used in constant expressions too:
-
-.. code-block:: nim
-  import strutils
-  const
-    constEval = contains("abc", 'b') # computed at compile time!
-
-
-The rules for compile-time computability are:
-
-1. Literals are compile-time computable.
-2. Type conversions are compile-time computable.
-3. Procedure calls of the form ``p(X)`` are compile-time computable if
-   ``p`` is a proc without side-effects (see the `noSideEffect pragma
-   <#pragmas-nosideeffect-pragma>`_ for details) and if ``X`` is a
-   (possibly empty) list of compile-time computable arguments.
-
-
-Constants cannot be of type ``ptr``, ``ref`` or ``var``, nor can
-they contain such a type.
-
-
-Static statement/expression
----------------------------
-
-A static statement/expression can be used to enforce compile
-time evaluation explicitly. Enforced compile time evaluation can even evaluate
-code that has side effects:
-
-.. code-block::
-
-  static:
-    echo "echo at compile time"
-
-It's a static error if the compiler cannot perform the evaluation at compile
-time.
-
-The current implementation poses some restrictions for compile time
-evaluation: Code which contains ``cast`` or makes use of the foreign function
-interface cannot be evaluated at compile time. Later versions of Nim will
-support the FFI at compile time.
-
-
-If statement
-------------
-
-Example:
-
-.. code-block:: nim
-
-  var name = readLine(stdin)
-
-  if name == "Andreas":
-    echo "What a nice name!"
-  elif name == "":
-    echo "Don't you have a name?"
-  else:
-    echo "Boring name..."
-
-The ``if`` statement is a simple way to make a branch in the control flow:
-The expression after the keyword ``if`` is evaluated, if it is true
-the corresponding statements after the ``:`` are executed. Otherwise
-the expression after the ``elif`` is evaluated (if there is an
-``elif`` branch), if it is true the corresponding statements after
-the ``:`` are executed. This goes on until the last ``elif``. If all
-conditions fail, the ``else`` part is executed. If there is no ``else``
-part, execution continues with the next statement.
-
-In ``if`` statements new scopes begin immediately after the ``if``/``elif``/``else`` keywords and ends after the corresponding *then* block.
-For visualization purposes the scopes have been enclosed in ``{|  |}`` in the following example:
-
-.. code-block:: nim
-  if {| (let m = input =~ re"(\w+)=\w+"; m.isMatch):
-    echo "key ", m[0], " value ", m[1]  |}
-  elif {| (let m = input =~ re""; m.isMatch):
-    echo "new m in this scope"  |}
-  else: {|
-    echo "m not declared here"  |}
-
-Case statement
---------------
-
-Example:
-
-.. code-block:: nim
-
-  case readline(stdin)
-  of "delete-everything", "restart-computer":
-    echo "permission denied"
-  of "go-for-a-walk":     echo "please yourself"
-  else:                   echo "unknown command"
-
-  # indentation of the branches is also allowed; and so is an optional colon
-  # after the selecting expression:
-  case readline(stdin):
-    of "delete-everything", "restart-computer":
-      echo "permission denied"
-    of "go-for-a-walk":     echo "please yourself"
-    else:                   echo "unknown command"
-
-
-The ``case`` statement is similar to the if statement, but it represents
-a multi-branch selection. The expression after the keyword ``case`` is
-evaluated and if its value is in a *slicelist* the corresponding statements
-(after the ``of`` keyword) are executed. If the value is not in any
-given *slicelist* the ``else`` part is executed. If there is no ``else``
-part and not all possible values that ``expr`` can hold occur in a
-``slicelist``, a static error occurs. This holds only for expressions of
-ordinal types. "All possible values" of ``expr`` are determined by ``expr``'s
-type. To suppress the static error an ``else`` part with an
-empty ``discard`` statement should be used.
-
-For non ordinal types it is not possible to list every possible value and so
-these always require an ``else`` part.
-
-As case statements perform compile-time exhaustiveness checks, the value in
-every ``of`` branch must be known at compile time. This fact is also exploited
-to generate more performant code.
-
-As a special semantic extension, an expression in an ``of`` branch of a case
-statement may evaluate to a set or array constructor; the set or array is then
-expanded into a list of its elements:
-
-.. code-block:: nim
-  const
-    SymChars: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'}
-
-  proc classify(s: string) =
-    case s[0]
-    of SymChars, '_': echo "an identifier"
-    of '0'..'9': echo "a number"
-    else: echo "other"
-
-  # is equivalent to:
-  proc classify(s: string) =
-    case s[0]
-    of 'a'..'z', 'A'..'Z', '\x80'..'\xFF', '_': echo "an identifier"
-    of '0'..'9': echo "a number"
-    else: echo "other"
-
-
-When statement
---------------
-
-Example:
-
-.. code-block:: nim
-
-  when sizeof(int) == 2:
-    echo "running on a 16 bit system!"
-  elif sizeof(int) == 4:
-    echo "running on a 32 bit system!"
-  elif sizeof(int) == 8:
-    echo "running on a 64 bit system!"
-  else:
-    echo "cannot happen!"
-
-The ``when`` statement is almost identical to the ``if`` statement with some
-exceptions:
-
-* Each condition (``expr``) has to be a constant expression (of type ``bool``).
-* The statements do not open a new scope.
-* The statements that belong to the expression that evaluated to true are
-  translated by the compiler, the other statements are not checked for
-  semantics! However, each condition is checked for semantics.
-
-The ``when`` statement enables conditional compilation techniques. As
-a special syntactic extension, the ``when`` construct is also available
-within ``object`` definitions.
-
-
-When nimvm statement
---------------------
-
-``nimvm`` is a special symbol, that may be used as expression of ``when nimvm``
-statement to differentiate execution path between runtime and compile time.
-
-Example:
-
-.. code-block:: nim
-  proc someProcThatMayRunInCompileTime(): bool =
-    when nimvm:
-      # This code runs in compile time
-      result = true
-    else:
-      # This code runs in runtime
-      result = false
-  const ctValue = someProcThatMayRunInCompileTime()
-  let rtValue = someProcThatMayRunInCompileTime()
-  assert(ctValue == true)
-  assert(rtValue == false)
-
-``when nimvm`` statement must meet the following requirements:
-
-* Its expression must always be ``nimvm``. More complex expressions are not
-  allowed.
-* It must not contain ``elif`` branches.
-* It must contain ``else`` branch.
-* Code in branches must not affect semantics of the code that follows the
-  ``when nimvm`` statement. E.g. it must not define symbols that are used in
-  the following code.
-
-Return statement
-----------------
-
-Example:
-
-.. code-block:: nim
-  return 40+2
-
-The ``return`` statement ends the execution of the current procedure.
-It is only allowed in procedures. If there is an ``expr``, this is syntactic
-sugar for:
-
-.. code-block:: nim
-  result = expr
-  return result
-
-
-``return`` without an expression is a short notation for ``return result`` if
-the proc has a return type. The `result`:idx: variable is always the return
-value of the procedure. It is automatically declared by the compiler. As all
-variables, ``result`` is initialized to (binary) zero:
-
-.. code-block:: nim
-  proc returnZero(): int =
-    # implicitly returns 0
-
-
-Yield statement
----------------
-
-Example:
-
-.. code-block:: nim
-  yield (1, 2, 3)
-
-The ``yield`` statement is used instead of the ``return`` statement in
-iterators. It is only valid in iterators. Execution is returned to the body
-of the for loop that called the iterator. Yield does not end the iteration
-process, but execution is passed back to the iterator if the next iteration
-starts. See the section about iterators (`Iterators and the for statement`_)
-for further information.
-
-
-Block statement
----------------
-
-Example:
-
-.. code-block:: nim
-  var found = false
-  block myblock:
-    for i in 0..3:
-      for j in 0..3:
-        if a[j][i] == 7:
-          found = true
-          break myblock # leave the block, in this case both for-loops
-  echo found
-
-The block statement is a means to group statements to a (named) ``block``.
-Inside the block, the ``break`` statement is allowed to leave the block
-immediately. A ``break`` statement can contain a name of a surrounding
-block to specify which block is to leave.
-
-
-Break statement
----------------
-
-Example:
-
-.. code-block:: nim
-  break
-
-The ``break`` statement is used to leave a block immediately. If ``symbol``
-is given, it is the name of the enclosing block that is to leave. If it is
-absent, the innermost block is left.
-
-
-While statement
----------------
-
-Example:
-
-.. code-block:: nim
-  echo "Please tell me your password:"
-  var pw = readLine(stdin)
-  while pw != "12345":
-    echo "Wrong password! Next try:"
-    pw = readLine(stdin)
-
-
-The ``while`` statement is executed until the ``expr`` evaluates to false.
-Endless loops are no error. ``while`` statements open an `implicit block`,
-so that they can be left with a ``break`` statement.
-
-
-Continue statement
-------------------
-
-A ``continue`` statement leads to the immediate next iteration of the
-surrounding loop construct. It is only allowed within a loop. A continue
-statement is syntactic sugar for a nested block:
-
-.. code-block:: nim
-  while expr1:
-    stmt1
-    continue
-    stmt2
-
-Is equivalent to:
-
-.. code-block:: nim
-  while expr1:
-    block myBlockName:
-      stmt1
-      break myBlockName
-      stmt2
-
-
-Assembler statement
--------------------
-
-The direct embedding of assembler code into Nim code is supported
-by the unsafe ``asm`` statement. Identifiers in the assembler code that refer to
-Nim identifiers shall be enclosed in a special character which can be
-specified in the statement's pragmas. The default special character is ``'`'``:
-
-.. code-block:: nim
-  {.push stackTrace:off.}
-  proc addInt(a, b: int): int =
-    # a in eax, and b in edx
-    asm """
-        mov eax, `a`
-        add eax, `b`
-        jno theEnd
-        call `raiseOverflow`
-      theEnd:
-    """
-  {.pop.}
-
-If the GNU assembler is used, quotes and newlines are inserted automatically:
-
-.. code-block:: nim
-  proc addInt(a, b: int): int =
-    asm """
-      addl %%ecx, %%eax
-      jno 1
-      call `raiseOverflow`
-      1:
-      :"=a"(`result`)
-      :"a"(`a`), "c"(`b`)
-    """
-
-Instead of:
-
-.. code-block:: nim
-  proc addInt(a, b: int): int =
-    asm """
-      "addl %%ecx, %%eax\n"
-      "jno 1\n"
-      "call `raiseOverflow`\n"
-      "1: \n"
-      :"=a"(`result`)
-      :"a"(`a`), "c"(`b`)
-    """
-
-Using statement
----------------
-
-The using statement provides syntactic convenience in modules where
-the same parameter names and types are used over and over. Instead of:
-
-.. code-block:: nim
-  proc foo(c: Context; n: Node) = ...
-  proc bar(c: Context; n: Node, counter: int) = ...
-  proc baz(c: Context; n: Node) = ...
-
-One can tell the compiler about the convention that a parameter of
-name ``c`` should default to type ``Context``, ``n`` should default to
-``Node`` etc.:
-
-.. code-block:: nim
-  using
-    c: Context
-    n: Node
-    counter: int
-
-  proc foo(c, n) = ...
-  proc bar(c, n, counter) = ...
-  proc baz(c, n) = ...
-
-
-The ``using`` section uses the same indentation based grouping syntax as
-a ``var`` or ``let`` section.
-
-Note that ``using`` is not applied for ``template`` since untyped template
-parameters default to the type ``system.untyped``.
-
-
-If expression
--------------
-
-An `if expression` is almost like an if statement, but it is an expression.
-Example:
-
-.. code-block:: nim
-  var y = if x > 8: 9 else: 10
-
-An if expression always results in a value, so the ``else`` part is
-required. ``Elif`` parts are also allowed.
-
-When expression
----------------
-
-Just like an `if expression`, but corresponding to the when statement.
-
-Case expression
----------------
-
-The `case expression` is again very similar to the case statement:
-
-.. code-block:: nim
-  var favoriteFood = case animal
-    of "dog": "bones"
-    of "cat": "mice"
-    elif animal.endsWith"whale": "plankton"
-    else:
-      echo "I'm not sure what to serve, but everybody loves ice cream"
-      "ice cream"
-
-As seen in the above example, the case expression can also introduce side
-effects. When multiple statements are given for a branch, Nim will use
-the last expression as the result value, much like in an `expr` template.
-
-Table constructor
------------------
-
-A table constructor is syntactic sugar for an array constructor:
-
-.. code-block:: nim
-  {"key1": "value1", "key2", "key3": "value2"}
-
-  # is the same as:
-  [("key1", "value1"), ("key2", "value2"), ("key3", "value2")]
-
-
-The empty table can be written ``{:}`` (in contrast to the empty set
-which is ``{}``) which is thus another way to write as the empty array
-constructor ``[]``. This slightly unusual way of supporting tables
-has lots of advantages:
-
-* The order of the (key,value)-pairs is preserved, thus it is easy to
-  support ordered dicts with for example ``{key: val}.newOrderedTable``.
-* A table literal can be put into a ``const`` section and the compiler
-  can easily put it into the executable's data section just like it can
-  for arrays and the generated data section requires a minimal amount
-  of memory.
-* Every table implementation is treated equal syntactically.
-* Apart from the minimal syntactic sugar the language core does not need to
-  know about tables.
-
-
-Type conversions
-----------------
-Syntactically a `type conversion` is like a procedure call, but a
-type name replaces the procedure name. A type conversion is always
-safe in the sense that a failure to convert a type to another
-results in an exception (if it cannot be determined statically).
-
-Ordinary procs are often preferred over type conversions in Nim: For instance,
-``$`` is the ``toString`` operator by convention and ``toFloat`` and ``toInt``
-can be used to convert from floating point to integer or vice versa.
-
-
-Type casts
-----------
-Example:
-
-.. code-block:: nim
-  cast[int](x)
-
-Type casts are a crude mechanism to interpret the bit pattern of
-an expression as if it would be of another type. Type casts are
-only needed for low-level programming and are inherently unsafe.
-
-
-The addr operator
------------------
-The ``addr`` operator returns the address of an l-value. If the type of the
-location is ``T``, the `addr` operator result is of the type ``ptr T``. An
-address is always an untraced reference. Taking the address of an object that
-resides on the stack is **unsafe**, as the pointer may live longer than the
-object on the stack and can thus reference a non-existing object. One can get
-the address of variables, but one can't use it on variables declared through
-``let`` statements:
-
-.. code-block:: nim
-
-  let t1 = "Hello"
-  var
-    t2 = t1
-    t3 : pointer = addr(t2)
-  echo repr(addr(t2))
-  # --> ref 0x7fff6b71b670 --> 0x10bb81050"Hello"
-  echo cast[ptr string](t3)[]
-  # --> Hello
-  # The following line doesn't compile:
-  echo repr(addr(t1))
-  # Error: expression has no address
-
-
-Procedures
-==========
-
-What most programming languages call `methods`:idx: or `functions`:idx: are
-called `procedures`:idx: in Nim. A procedure
-declaration consists of an identifier, zero or more formal parameters, a return
-value type and a block of code. Formal parameters are declared as a list of
-identifiers separated by either comma or semicolon. A parameter is given a type
-by ``: typename``. The type applies to all parameters immediately before it,
-until either the beginning of the parameter list, a semicolon separator or an
-already typed parameter, is reached. The semicolon can be used to make
-separation of types and subsequent identifiers more distinct.
-
-.. code-block:: nim
-  # Using only commas
-  proc foo(a, b: int, c, d: bool): int
-
-  # Using semicolon for visual distinction
-  proc foo(a, b: int; c, d: bool): int
-
-  # Will fail: a is untyped since ';' stops type propagation.
-  proc foo(a; b: int; c, d: bool): int
-
-A parameter may be declared with a default value which is used if the caller
-does not provide a value for the argument.
-
-.. code-block:: nim
-  # b is optional with 47 as its default value
-  proc foo(a: int, b: int = 47): int
-
-Parameters can be declared mutable and so allow the proc to modify those
-arguments, by using the type modifier `var`.
-
-.. code-block:: nim
-  # "returning" a value to the caller through the 2nd argument
-  # Notice that the function uses no actual return value at all (ie void)
-  proc foo(inp: int, outp: var int) =
-    outp = inp + 47
-
-If the proc declaration has no body, it is a `forward`:idx: declaration. If the
-proc returns a value, the procedure body can access an implicitly declared
-variable named `result`:idx: that represents the return value. Procs can be
-overloaded. The overloading resolution algorithm determines which proc is the
-best match for the arguments. Example:
-
-.. code-block:: nim
-
-  proc toLower(c: char): char = # toLower for characters
-    if c in {'A'..'Z'}:
-      result = chr(ord(c) + (ord('a') - ord('A')))
-    else:
-      result = c
-
-  proc toLower(s: string): string = # toLower for strings
-    result = newString(len(s))
-    for i in 0..len(s) - 1:
-      result[i] = toLower(s[i]) # calls toLower for characters; no recursion!
-
-Calling a procedure can be done in many different ways:
-
-.. code-block:: nim
-  proc callme(x, y: int, s: string = "", c: char, b: bool = false) = ...
-
-  # call with positional arguments      # parameter bindings:
-  callme(0, 1, "abc", '\t', true)       # (x=0, y=1, s="abc", c='\t', b=true)
-  # call with named and positional arguments:
-  callme(y=1, x=0, "abd", '\t')         # (x=0, y=1, s="abd", c='\t', b=false)
-  # call with named arguments (order is not relevant):
-  callme(c='\t', y=1, x=0)              # (x=0, y=1, s="", c='\t', b=false)
-  # call as a command statement: no () needed:
-  callme 0, 1, "abc", '\t'              # (x=0, y=1, s="abc", c='\t', b=false)
-
-A procedure may call itself recursively.
-
-
-`Operators`:idx: are procedures with a special operator symbol as identifier:
-
-.. code-block:: nim
-  proc `$` (x: int): string =
-    # converts an integer to a string; this is a prefix operator.
-    result = intToStr(x)
-
-Operators with one parameter are prefix operators, operators with two
-parameters are infix operators. (However, the parser distinguishes these from
-the operator's position within an expression.) There is no way to declare
-postfix operators: all postfix operators are built-in and handled by the
-grammar explicitly.
-
-Any operator can be called like an ordinary proc with the '`opr`'
-notation. (Thus an operator can have more than two parameters):
-
-.. code-block:: nim
-  proc `*+` (a, b, c: int): int =
-    # Multiply and add
-    result = a * b + c
-
-  assert `*+`(3, 4, 6) == `+`(`*`(a, b), c)
-
-
-Export marker
--------------
-
-If a declared symbol is marked with an `asterisk`:idx: it is exported from the
-current module:
-
-.. code-block:: nim
-
-  proc exportedEcho*(s: string) = echo s
-  proc `*`*(a: string; b: int): string =
-    result = newStringOfCap(a.len * b)
-    for i in 1..b: result.add a
-
-  var exportedVar*: int
-  const exportedConst* = 78
-  type
-    ExportedType* = object
-      exportedField*: int
-
-
-Method call syntax
-------------------
-
-For object oriented programming, the syntax ``obj.method(args)`` can be used
-instead of ``method(obj, args)``. The parentheses can be omitted if there are no
-remaining arguments: ``obj.len`` (instead of ``len(obj)``).
-
-This method call syntax is not restricted to objects, it can be used
-to supply any type of first argument for procedures:
-
-.. code-block:: nim
-
-  echo "abc".len # is the same as echo len "abc"
-  echo "abc".toUpper()
-  echo {'a', 'b', 'c'}.card
-  stdout.writeLine("Hallo") # the same as writeLine(stdout, "Hallo")
-
-Another way to look at the method call syntax is that it provides the missing
-postfix notation.
-
-The method call syntax conflicts with explicit generic instantiations:
-``p[T](x)`` cannot be written as ``x.p[T]`` because ``x.p[T]`` is always
-parsed as ``(x.p)[T]``.
-
-See also: `Limitations of the method call syntax
-<#templates-limitations-of-the-method-call-syntax>`_.
-
-The ``[: ]`` notation has been designed to mitigate this issue: ``x.p[:T]``
-is rewritten by the parser to ``p[T](x)``, ``x.p[:T](y)`` is rewritten to
-``p[T](x, y)``. Note that ``[: ]`` has no AST representation, the rewrite
-is performed directly in the parsing step.
-
-
-Properties
-----------
-Nim has no need for *get-properties*: Ordinary get-procedures that are called
-with the *method call syntax* achieve the same. But setting a value is
-different; for this a special setter syntax is needed:
-
-.. code-block:: nim
-  # Module asocket
-  type
-    Socket* = ref object of RootObj
-      host: int # cannot be accessed from the outside of the module
-
-  proc `host=`*(s: var Socket, value: int) {.inline.} =
-    ## setter of hostAddr.
-    ## This accesses the 'host' field and is not a recursive call to
-    ## ``host=`` because the builtin dot access is preferred if it is
-    ## avaliable:
-    s.host = value
-
-  proc host*(s: Socket): int {.inline.} =
-    ## getter of hostAddr
-    ## This accesses the 'host' field and is not a recursive call to
-    ## ``host`` because the builtin dot access is preferred if it is
-    ## avaliable:
-    s.host
-
-.. code-block:: nim
-  # module B
-  import asocket
-  var s: Socket
-  new s
-  s.host = 34  # same as `host=`(s, 34)
-
-
-Command invocation syntax
--------------------------
-
-Routines can be invoked without the ``()`` if the call is syntactically
-a statement. This command invocation syntax also works for
-expressions, but then only a single argument may follow. This restriction
-means ``echo f 1, f 2`` is parsed as ``echo(f(1), f(2))`` and not as
-``echo(f(1, f(2)))``. The method call syntax may be used to provide one
-more argument in this case:
-
-.. code-block:: nim
-  proc optarg(x: int, y: int = 0): int = x + y
-  proc singlearg(x: int): int = 20*x
-
-  echo optarg 1, " ", singlearg 2  # prints "1 40"
-
-  let fail = optarg 1, optarg 8   # Wrong. Too many arguments for a command call
-  let x = optarg(1, optarg 8)  # traditional procedure call with 2 arguments
-  let y = 1.optarg optarg 8    # same thing as above, w/o the parenthesis
-  assert x == y
-
-The command invocation syntax also can't have complex expressions as arguments.
-For example: (`anonymous procs`_), ``if``, ``case`` or ``try``. The (`do
-notation`_) is limited, but usable for a single proc (see the example in the
-corresponding section). Function calls with no arguments still needs () to
-distinguish between a call and the function itself as a first class value.
-
-
-Closures
---------
-
-Procedures can appear at the top level in a module as well as inside other
-scopes, in which case they are called nested procs. A nested proc can access
-local variables from its enclosing scope and if it does so it becomes a
-closure. Any captured variables are stored in a hidden additional argument
-to the closure (its environment) and they are accessed by reference by both
-the closure and its enclosing scope (i.e. any modifications made to them are
-visible in both places). The closure environment may be allocated on the heap
-or on the stack if the compiler determines that this would be safe.
-
-Creating closures in loops
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Since closures capture local variables by reference it is often not wanted
-behavior inside loop bodies. See `closureScope <system.html#closureScope>`_
-for details on how to change this behavior.
-
-Anonymous Procs
----------------
-
-Procs can also be treated as expressions, in which case it's allowed to omit
-the proc's name.
-
-.. code-block:: nim
-  var cities = @["Frankfurt", "Tokyo", "New York", "Kyiv"]
-
-  cities.sort(proc (x,y: string): int =
-      cmp(x.len, y.len))
-
-
-Procs as expressions can appear both as nested procs and inside top level
-executable code.
-
-
-Func
-----
-
-The ``func`` keyword introduces a shortcut for a `noSideEffect`:idx: proc.
-
-.. code-block:: nim
-  func binarySearch[T](a: openArray[T]; elem: T): int
-
-Is short for:
-
-.. code-block:: nim
-  proc binarySearch[T](a: openArray[T]; elem: T): int {.noSideEffect.}
-
-
-Do notation
------------
-
-As a special more convenient notation, proc expressions involved in procedure
-calls can use the ``do`` keyword:
-
-.. code-block:: nim
-  sort(cities) do (x,y: string) -> int:
-    cmp(x.len, y.len)
-
-  # Less parenthesis using the method plus command syntax:
-  cities = cities.map do (x:string) -> string:
-    "City of " & x
-
-  # In macros, the do notation is often used for quasi-quoting
-  macroResults.add quote do:
-    if not `ex`:
-      echo `info`, ": Check failed: ", `expString`
-
-``do`` is written after the parentheses enclosing the regular proc params.
-The proc expression represented by the do block is appended to them.
-In calls using the command syntax, the do block will bind to the immediately
-preceeding expression, transforming it in a call.
-
-``do`` with parentheses is an anonymous ``proc``; however a ``do`` without
-parentheses is just a block of code. The ``do`` notation can be used to
-pass multiple blocks to a macro:
-
-.. code-block:: nim
-  macro performWithUndo(task, undo: untyped) = ...
-
-  performWithUndo do:
-    # multiple-line block of code
-    # to perform the task
-  do:
-    # code to undo it
-
-
-Nonoverloadable builtins
-------------------------
-
-The following builtin procs cannot be overloaded for reasons of implementation
-simplicity (they require specialized semantic checking)::
-
-  declared, defined, definedInScope, compiles, sizeOf,
-  is, shallowCopy, getAst, astToStr, spawn, procCall
-
-Thus they act more like keywords than like ordinary identifiers; unlike a
-keyword however, a redefinition may `shadow`:idx: the definition in
-the ``system`` module. From this list the following should not be written in dot
-notation ``x.f`` since ``x`` cannot be type checked before it gets passed
-to ``f``::
-
-  declared, defined, definedInScope, compiles, getAst, astToStr
-
-
-Var parameters
---------------
-The type of a parameter may be prefixed with the ``var`` keyword:
-
-.. code-block:: nim
-  proc divmod(a, b: int; res, remainder: var int) =
-    res = a div b
-    remainder = a mod b
-
-  var
-    x, y: int
-
-  divmod(8, 5, x, y) # modifies x and y
-  assert x == 1
-  assert y == 3
-
-In the example, ``res`` and ``remainder`` are `var parameters`.
-Var parameters can be modified by the procedure and the changes are
-visible to the caller. The argument passed to a var parameter has to be
-an l-value. Var parameters are implemented as hidden pointers. The
-above example is equivalent to:
-
-.. code-block:: nim
-  proc divmod(a, b: int; res, remainder: ptr int) =
-    res[] = a div b
-    remainder[] = a mod b
-
-  var
-    x, y: int
-  divmod(8, 5, addr(x), addr(y))
-  assert x == 1
-  assert y == 3
-
-In the examples, var parameters or pointers are used to provide two
-return values. This can be done in a cleaner way by returning a tuple:
-
-.. code-block:: nim
-  proc divmod(a, b: int): tuple[res, remainder: int] =
-    (a div b, a mod b)
-
-  var t = divmod(8, 5)
-
-  assert t.res == 1
-  assert t.remainder == 3
-
-One can use `tuple unpacking`:idx: to access the tuple's fields:
-
-.. code-block:: nim
-  var (x, y) = divmod(8, 5) # tuple unpacking
-  assert x == 1
-  assert y == 3
-
-
-**Note**: ``var`` parameters are never necessary for efficient parameter
-passing. Since non-var parameters cannot be modified the compiler is always
-free to pass arguments by reference if it considers it can speed up execution.
-
-
-Var return type
----------------
-
-A proc, converter or iterator may return a ``var`` type which means that the
-returned value is an l-value and can be modified by the caller:
-
-.. code-block:: nim
-  var g = 0
-
-  proc writeAccessToG(): var int =
-    result = g
-
-  writeAccessToG() = 6
-  assert g == 6
-
-It is a compile time error if the implicitly introduced pointer could be
-used to access a location beyond its lifetime:
-
-.. code-block:: nim
-  proc writeAccessToG(): var int =
-    var g = 0
-    result = g # Error!
-
-For iterators, a component of a tuple return type can have a ``var`` type too:
-
-.. code-block:: nim
-  iterator mpairs(a: var seq[string]): tuple[key: int, val: var string] =
-    for i in 0..a.high:
-      yield (i, a[i])
-
-In the standard library every name of a routine that returns a ``var`` type
-starts with the prefix ``m`` per convention.
-
-
-.. include:: manual/var_t_return.rst
-
-Future directions
-~~~~~~~~~~~~~~~~~
-
-Later versions of Nim can be more precise about the borrowing rule with
-a syntax like:
-
-.. code-block:: nim
-  proc foo(other: Y; container: var X): var T from container
-
-Here ``var T from container`` explicitly exposes that the
-location is deviated from the second parameter (called
-'container' in this case). The syntax ``var T from p`` specifies a type
-``varTy[T, 2]`` which is incompatible with ``varTy[T, 1]``.
-
-
-
-Overloading of the subscript operator
--------------------------------------
-
-The ``[]`` subscript operator for arrays/openarrays/sequences can be overloaded.
-
-
-Multi-methods
-=============
-
-Procedures always use static dispatch. Multi-methods use dynamic
-dispatch. For dynamic dispatch to work on an object it should be a reference
-type as well.
-
-.. code-block:: nim
-  type
-    Expression = ref object of RootObj ## abstract base class for an expression
-    Literal = ref object of Expression
-      x: int
-    PlusExpr = ref object of Expression
-      a, b: Expression
-
-  method eval(e: Expression): int {.base.} =
-    # override this base method
-    quit "to override!"
-
-  method eval(e: Literal): int = return e.x
-
-  method eval(e: PlusExpr): int =
-    # watch out: relies on dynamic binding
-    result = eval(e.a) + eval(e.b)
-
-  proc newLit(x: int): Literal =
-    new(result)
-    result.x = x
-
-  proc newPlus(a, b: Expression): PlusExpr =
-    new(result)
-    result.a = a
-    result.b = b
-
-  echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
-
-In the example the constructors ``newLit`` and ``newPlus`` are procs
-because they should use static binding, but ``eval`` is a method because it
-requires dynamic binding.
-
-As can be seen in the example, base methods have to be annotated with
-the `base`:idx: pragma. The ``base`` pragma also acts as a reminder for the
-programmer that a base method ``m`` is used as the foundation to determine all
-the effects that a call to ``m`` might cause.
-
-In a multi-method all parameters that have an object type are used for the
-dispatching:
-
-.. code-block:: nim
-  type
-    Thing = ref object of RootObj
-    Unit = ref object of Thing
-      x: int
-
-  method collide(a, b: Thing) {.base, inline.} =
-    quit "to override!"
-
-  method collide(a: Thing, b: Unit) {.inline.} =
-    echo "1"
-
-  method collide(a: Unit, b: Thing) {.inline.} =
-    echo "2"
-
-  var a, b: Unit
-  new a
-  new b
-  collide(a, b) # output: 2
-
-
-Invocation of a multi-method cannot be ambiguous: collide 2 is preferred over
-collide 1 because the resolution works from left to right.
-In the example ``Unit, Thing`` is preferred over ``Thing, Unit``.
-
-**Performance note**: Nim does not produce a virtual method table, but
-generates dispatch trees. This avoids the expensive indirect branch for method
-calls and enables inlining. However, other optimizations like compile time
-evaluation or dead code elimination do not work with methods.
-
-
-Iterators and the for statement
-===============================
-
-The `for`:idx: statement is an abstract mechanism to iterate over the elements
-of a container. It relies on an `iterator`:idx: to do so. Like ``while``
-statements, ``for`` statements open an `implicit block`:idx:, so that they
-can be left with a ``break`` statement.
-
-The ``for`` loop declares iteration variables - their scope reaches until the
-end of the loop body. The iteration variables' types are inferred by the
-return type of the iterator.
-
-An iterator is similar to a procedure, except that it can be called in the
-context of a ``for`` loop. Iterators provide a way to specify the iteration over
-an abstract type. A key role in the execution of a ``for`` loop plays the
-``yield`` statement in the called iterator. Whenever a ``yield`` statement is
-reached the data is bound to the ``for`` loop variables and control continues
-in the body of the ``for`` loop. The iterator's local variables and execution
-state are automatically saved between calls. Example:
-
-.. code-block:: nim
-  # this definition exists in the system module
-  iterator items*(a: string): char {.inline.} =
-    var i = 0
-    while i < len(a):
-      yield a[i]
-      inc(i)
-
-  for ch in items("hello world"): # `ch` is an iteration variable
-    echo ch
-
-The compiler generates code as if the programmer would have written this:
-
-.. code-block:: nim
-  var i = 0
-  while i < len(a):
-    var ch = a[i]
-    echo ch
-    inc(i)
-
-If the iterator yields a tuple, there can be as many iteration variables
-as there are components in the tuple. The i'th iteration variable's type is
-the type of the i'th component. In other words, implicit tuple unpacking in a
-for loop context is supported.
-
-Implict items/pairs invocations
--------------------------------
-
-If the for loop expression ``e`` does not denote an iterator and the for loop
-has exactly 1 variable, the for loop expression is rewritten to ``items(e)``;
-ie. an ``items`` iterator is implicitly invoked:
-
-.. code-block:: nim
-  for x in [1,2,3]: echo x
-
-If the for loop has exactly 2 variables, a ``pairs`` iterator is implicitly
-invoked.
-
-Symbol lookup of the identifiers ``items``/``pairs`` is performed after
-the rewriting step, so that all overloads of ``items``/``pairs`` are taken
-into account.
-
-
-First class iterators
----------------------
-
-There are 2 kinds of iterators in Nim: *inline* and *closure* iterators.
-An `inline iterator`:idx: is an iterator that's always inlined by the compiler
-leading to zero overhead for the abstraction, but may result in a heavy
-increase in code size. Inline iterators are second class citizens;
-They can be passed as parameters only to other inlining code facilities like
-templates, macros and other inline iterators.
-
-In contrast to that, a `closure iterator`:idx: can be passed around more freely:
-
-.. code-block:: nim
-  iterator count0(): int {.closure.} =
-    yield 0
-
-  iterator count2(): int {.closure.} =
-    var x = 1
-    yield x
-    inc x
-    yield x
-
-  proc invoke(iter: iterator(): int {.closure.}) =
-    for x in iter(): echo x
-
-  invoke(count0)
-  invoke(count2)
-
-Closure iterators have other restrictions than inline iterators:
-
-1. ``yield`` in a closure iterator can not occur in a ``try`` statement.
-2. For now, a closure iterator cannot be evaluated at compile time.
-3. ``return`` is allowed in a closure iterator (but rarely useful) and ends
-   iteration.
-4. Neither inline nor closure iterators can be recursive.
-5. Closure iterators are not supported by the js backend.
-
-Iterators that are neither marked ``{.closure.}`` nor ``{.inline.}`` explicitly
-default to being inline, but this may change in future versions of the
-implementation.
-
-The ``iterator`` type is always of the calling convention ``closure``
-implicitly; the following example shows how to use iterators to implement
-a `collaborative tasking`:idx: system:
-
-.. code-block:: nim
-  # simple tasking:
-  type
-    Task = iterator (ticker: int)
-
-  iterator a1(ticker: int) {.closure.} =
-    echo "a1: A"
-    yield
-    echo "a1: B"
-    yield
-    echo "a1: C"
-    yield
-    echo "a1: D"
-
-  iterator a2(ticker: int) {.closure.} =
-    echo "a2: A"
-    yield
-    echo "a2: B"
-    yield
-    echo "a2: C"
-
-  proc runTasks(t: varargs[Task]) =
-    var ticker = 0
-    while true:
-      let x = t[ticker mod t.len]
-      if finished(x): break
-      x(ticker)
-      inc ticker
-
-  runTasks(a1, a2)
-
-The builtin ``system.finished`` can be used to determine if an iterator has
-finished its operation; no exception is raised on an attempt to invoke an
-iterator that has already finished its work.
-
-Note that ``system.finished`` is error prone to use because it only returns
-``true`` one iteration after the iterator has finished:
-
-.. code-block:: nim
-  iterator mycount(a, b: int): int {.closure.} =
-    var x = a
-    while x <= b:
-      yield x
-      inc x
-
-  var c = mycount # instantiate the iterator
-  while not finished(c):
-    echo c(1, 3)
-
-  # Produces
-  1
-  2
-  3
-  0
-
-Instead this code has to be used:
-
-.. code-block:: nim
-  var c = mycount # instantiate the iterator
-  while true:
-    let value = c(1, 3)
-    if finished(c): break # and discard 'value'!
-    echo value
-
-It helps to think that the iterator actually returns a
-pair ``(value, done)`` and ``finished`` is used to access the hidden ``done``
-field.
-
-
-Closure iterators are *resumable functions* and so one has to provide the
-arguments to every call. To get around this limitation one can capture
-parameters of an outer factory proc:
-
-.. code-block:: nim
-  proc mycount(a, b: int): iterator (): int =
-    result = iterator (): int =
-      var x = a
-      while x <= b:
-        yield x
-        inc x
-
-  let foo = mycount(1, 4)
-
-  for f in foo():
-    echo f
-
-..
-  Implicit return type
-  --------------------
-
-  Since inline iterators must always produce values that will be consumed in
-  a for loop, the compiler will implicitly use the ``auto`` return type if no
-  type is given by the user. In contrast, since closure iterators can be used
-  as a collaborative tasking system, ``void`` is a valid return type for them.
-
-
-Converters
-==========
-
-A converter is like an ordinary proc except that it enhances
-the "implicitly convertible" type relation (see `Convertible relation`_):
-
-.. code-block:: nim
-  # bad style ahead: Nim is not C.
-  converter toBool(x: int): bool = x != 0
-
-  if 4:
-    echo "compiles"
-
-
-A converter can also be explicitly invoked for improved readability. Note that
-implicit converter chaining is not supported: If there is a converter from
-type A to type B and from type B to type C the implicit conversion from A to C
-is not provided.
-
-
-Type sections
-=============
-
-Example:
-
-.. code-block:: nim
-  type # example demonstrating mutually recursive types
-    Node = ref object  # an object managed by the garbage collector (ref)
-      le, ri: Node     # left and right subtrees
-      sym: ref Sym     # leaves contain a reference to a Sym
-
-    Sym = object       # a symbol
-      name: string     # the symbol's name
-      line: int        # the line the symbol was declared in
-      code: Node       # the symbol's abstract syntax tree
-
-A type section begins with the ``type`` keyword. It contains multiple
-type definitions. A type definition binds a type to a name. Type definitions
-can be recursive or even mutually recursive. Mutually recursive types are only
-possible within a single ``type`` section. Nominal types like ``objects``
-or ``enums`` can only be defined in a ``type`` section.
-
-
-
-Exception handling
-==================
-
-Try statement
--------------
-
-Example:
-
-.. code-block:: nim
-  # read the first two lines of a text file that should contain numbers
-  # and tries to add them
-  var
-    f: File
-  if open(f, "numbers.txt"):
-    try:
-      var a = readLine(f)
-      var b = readLine(f)
-      echo "sum: " & $(parseInt(a) + parseInt(b))
-    except OverflowError:
-      echo "overflow!"
-    except ValueError:
-      echo "could not convert string to integer"
-    except IOError:
-      echo "IO error!"
-    except:
-      echo "Unknown exception!"
-    finally:
-      close(f)
-
-
-The statements after the ``try`` are executed in sequential order unless
-an exception ``e`` is raised. If the exception type of ``e`` matches any
-listed in an ``except`` clause the corresponding statements are executed.
-The statements following the ``except`` clauses are called
-`exception handlers`:idx:.
-
-The empty `except`:idx: clause is executed if there is an exception that is
-not listed otherwise. It is similar to an ``else`` clause in ``if`` statements.
-
-If there is a `finally`:idx: clause, it is always executed after the
-exception handlers.
-
-The exception is *consumed* in an exception handler. However, an
-exception handler may raise another exception. If the exception is not
-handled, it is propagated through the call stack. This means that often
-the rest of the procedure - that is not within a ``finally`` clause -
-is not executed (if an exception occurs).
-
-
-Try expression
---------------
-
-Try can also be used as an expression; the type of the ``try`` branch then
-needs to fit the types of ``except`` branches, but the type of the ``finally``
-branch always has to be ``void``:
-
-.. code-block:: nim
-  let x = try: parseInt("133a")
-          except: -1
-          finally: echo "hi"
-
-
-To prevent confusing code there is a parsing limitation; if the ``try``
-follows a ``(`` it has to be written as a one liner:
-
-.. code-block:: nim
-  let x = (try: parseInt("133a") except: -1)
-
-
-Except clauses
---------------
-
-Within an ``except`` clause, it is possible to use
-``getCurrentException`` to retrieve the exception that has been
-raised:
-
-.. code-block:: nim
-  try:
-    # ...
-  except IOError:
-    let e = getCurrentException()
-    # Now use "e"
-
-Note that ``getCurrentException`` always returns a ``ref Exception``
-type. If a variable of the proper type is needed (in the example
-above, ``IOError``), one must convert it explicitly:
-
-.. code-block:: nim
-  try:
-    # ...
-  except IOError:
-    let e = (ref IOError)(getCurrentException())
-    # "e" is now of the proper type
-
-However, this is seldom needed. The most common case is to extract an
-error message from ``e``, and for such situations it is enough to use
-``getCurrentExceptionMsg``:
-
-.. code-block:: nim
-  try:
-    # ...
-  except IOError:
-    echo "I/O error: " & getCurrentExceptionMsg()
-
-
-Defer statement
----------------
-
-Instead of a ``try finally`` statement a ``defer`` statement can be used.
-
-Any statements following the ``defer`` in the current block will be considered
-to be in an implicit try block:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  proc main =
-    var f = open("numbers.txt")
-    defer: close(f)
-    f.write "abc"
-    f.write "def"
-
-Is rewritten to:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  proc main =
-    var f = open("numbers.txt")
-    try:
-      f.write "abc"
-      f.write "def"
-    finally:
-      close(f)
-
-Top level ``defer`` statements are not supported
-since it's unclear what such a statement should refer to.
-
-
-Raise statement
----------------
-
-Example:
-
-.. code-block:: nim
-  raise newEOS("operating system failed")
-
-Apart from built-in operations like array indexing, memory allocation, etc.
-the ``raise`` statement is the only way to raise an exception.
-
-.. XXX document this better!
-
-If no exception name is given, the current exception is `re-raised`:idx:. The
-`ReraiseError`:idx: exception is raised if there is no exception to
-re-raise. It follows that the ``raise`` statement *always* raises an
-exception.
-
-
-Exception hierarchy
--------------------
-
-The exception tree is defined in the `system <system.html>`_ module:
-
-.. include:: exception_hierarchy_fragment.txt
-
-
-Imported exceptions
--------------------
-
-It is possible to raise/catch imported C++ exceptions. Types imported using
-`importcpp` can be raised or caught. Exceptions are raised by value and
-caught by reference. Example:
-
-.. code-block:: nim
-
-  type
-    std_exception {.importcpp: "std::exception", header: "<exception>".} = object
-
-  proc what(s: std_exception): cstring {.importcpp: "((char *)#.what())".}
-
-  try:
-    raise std_exception()
-  except std_exception as ex:
-    echo ex.what()
-
-
-
-Effect system
-=============
-
-Exception tracking
-------------------
-
-Nim supports exception tracking. The `raises`:idx: pragma can be used
-to explicitly define which exceptions a proc/iterator/method/converter is
-allowed to raise. The compiler verifies this:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  proc p(what: bool) {.raises: [IOError, OSError].} =
-    if what: raise newException(IOError, "IO")
-    else: raise newException(OSError, "OS")
-
-An empty ``raises`` list (``raises: []``) means that no exception may be raised:
-
-.. code-block:: nim
-  proc p(): bool {.raises: [].} =
-    try:
-      unsafeCall()
-      result = true
-    except:
-      result = false
-
-
-A ``raises`` list can also be attached to a proc type. This affects type
-compatibility:
-
-.. code-block:: nim
-    :test: "nim c $1"
-    :status: 1
-
-  type
-    Callback = proc (s: string) {.raises: [IOError].}
-  var
-    c: Callback
-
-  proc p(x: string) =
-    raise newException(OSError, "OS")
-
-  c = p # type error
-
-
-For a routine ``p`` the compiler uses inference rules to determine the set of
-possibly raised exceptions; the algorithm operates on ``p``'s call graph:
-
-1. Every indirect call via some proc type ``T`` is assumed to
-   raise ``system.Exception`` (the base type of the exception hierarchy) and
-   thus any exception unless ``T`` has an explicit ``raises`` list.
-   However if the call is of the form ``f(...)`` where ``f`` is a parameter
-   of the currently analysed routine it is ignored. The call is optimistically
-   assumed to have no effect. Rule 2 compensates for this case.
-2. Every expression of some proc type within a call that is not a call
-   itself (and not nil) is assumed to be called indirectly somehow and thus
-   its raises list is added to ``p``'s raises list.
-3. Every call to a proc ``q`` which has an unknown body (due to a forward
-   declaration or an ``importc`` pragma) is assumed to
-   raise ``system.Exception`` unless ``q`` has an explicit ``raises`` list.
-4. Every call to a method ``m`` is assumed to
-   raise ``system.Exception`` unless ``m`` has an explicit ``raises`` list.
-5. For every other call the analysis can determine an exact ``raises`` list.
-6. For determining a ``raises`` list, the ``raise`` and ``try`` statements
-   of ``p`` are taken into consideration.
-
-Rules 1-2 ensure the following works:
-
-.. code-block:: nim
-  proc noRaise(x: proc()) {.raises: [].} =
-    # unknown call that might raise anything, but valid:
-    x()
-
-  proc doRaise() {.raises: [IOError].} =
-    raise newException(IOError, "IO")
-
-  proc use() {.raises: [].} =
-    # doesn't compile! Can raise IOError!
-    noRaise(doRaise)
-
-So in many cases a callback does not cause the compiler to be overly
-conservative in its effect analysis.
-
-
-Tag tracking
-------------
-
-The exception tracking is part of Nim's `effect system`:idx:. Raising an
-exception is an *effect*. Other effects can also be defined. A user defined
-effect is a means to *tag* a routine and to perform checks against this tag:
-
-.. code-block:: nim
-    :test: "nim c $1"
-    :status: 1
-
-  type IO = object ## input/output effect
-  proc readLine(): string {.tags: [IO].} = discard
-
-  proc no_IO_please() {.tags: [].} =
-    # the compiler prevents this:
-    let x = readLine()
-
-A tag has to be a type name. A ``tags`` list - like a ``raises`` list - can
-also be attached to a proc type. This affects type compatibility.
-
-The inference for tag tracking is analogous to the inference for
-exception tracking.
-
-
-Read/Write tracking
--------------------
-
-**Note**: Read/write tracking is not yet implemented!
-
-The inference for read/write tracking is analogous to the inference for
-exception tracking.
-
-
-Effects pragma
---------------
-
-The ``effects`` pragma has been designed to assist the programmer with the
-effects analysis. It is a statement that makes the compiler output all inferred
-effects up to the ``effects``'s position:
-
-.. code-block:: nim
-  proc p(what: bool) =
-    if what:
-      raise newException(IOError, "IO")
-      {.effects.}
-    else:
-      raise newException(OSError, "OS")
-
-The compiler produces a hint message that ``IOError`` can be raised. ``OSError``
-is not listed as it cannot be raised in the branch the ``effects`` pragma
-appears in.
-
-
-Generics
-========
-
-Generics are Nim's means to parametrize procs, iterators or types with
-`type parameters`:idx:. Depending on context, the brackets are used either to
-introduce type parameters or to instantiate a generic proc, iterator or type.
-
-The following example shows a generic binary tree can be modelled:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  type
-    BinaryTree*[T] = ref object # BinaryTree is a generic type with
-                                # generic param ``T``
-      le, ri: BinaryTree[T]     # left and right subtrees; may be nil
-      data: T                   # the data stored in a node
-
-  proc newNode*[T](data: T): BinaryTree[T] =
-    # constructor for a node
-    result = BinaryTree[T](le: nil, ri: nil, data: data)
-
-  proc add*[T](root: var BinaryTree[T], n: BinaryTree[T]) =
-    # insert a node into the tree
-    if root == nil:
-      root = n
-    else:
-      var it = root
-      while it != nil:
-        # compare the data items; uses the generic ``cmp`` proc
-        # that works for any type that has a ``==`` and ``<`` operator
-        var c = cmp(it.data, n.data)
-        if c < 0:
-          if it.le == nil:
-            it.le = n
-            return
-          it = it.le
-        else:
-          if it.ri == nil:
-            it.ri = n
-            return
-          it = it.ri
-
-  proc add*[T](root: var BinaryTree[T], data: T) =
-    # convenience proc:
-    add(root, newNode(data))
-
-  iterator preorder*[T](root: BinaryTree[T]): T =
-    # Preorder traversal of a binary tree.
-    # Since recursive iterators are not yet implemented,
-    # this uses an explicit stack (which is more efficient anyway):
-    var stack: seq[BinaryTree[T]] = @[root]
-    while stack.len > 0:
-      var n = stack.pop()
-      while n != nil:
-        yield n.data
-        add(stack, n.ri)  # push right subtree onto the stack
-        n = n.le          # and follow the left pointer
-
-  var
-    root: BinaryTree[string] # instantiate a BinaryTree with ``string``
-  add(root, newNode("hello")) # instantiates ``newNode`` and ``add``
-  add(root, "world")          # instantiates the second ``add`` proc
-  for str in preorder(root):
-    stdout.writeLine(str)
-
-The ``T`` is called a `generic type parameter`:idx: or
-a `type variable`:idx:.
-
-
-Is operator
------------
-
-The ``is`` operator checks for type equivalence at compile time. It is
-therefore very useful for type specialization within generic code:
-
-.. code-block:: nim
-  type
-    Table[Key, Value] = object
-      keys: seq[Key]
-      values: seq[Value]
-      when not (Key is string): # empty value for strings used for optimization
-        deletedKeys: seq[bool]
-
-
-Type operator
--------------
-
-The ``type`` (in many other languages called `typeof`:idx:) operator can
-be used to get the type of an expression:
-
-.. code-block:: nim
-  var x = 0
-  var y: type(x) # y has type int
-
-If ``type`` is used to determine the result type of a proc/iterator/converter
-call ``c(X)`` (where ``X`` stands for a possibly empty list of arguments), the
-interpretation where ``c`` is an iterator is preferred over the
-other interpretations:
-
-.. code-block:: nim
-  import strutils
-
-  # strutils contains both a ``split`` proc and iterator, but since an
-  # an iterator is the preferred interpretation, `y` has the type ``string``:
-  var y: type("a b c".split)
-
-
-Type Classes
-------------
-
-A type class is a special pseudo-type that can be used to match against
-types in the context of overload resolution or the ``is`` operator.
-Nim supports the following built-in type classes:
-
-==================   ===================================================
-type class           matches
-==================   ===================================================
-``object``           any object type
-``tuple``            any tuple type
-
-``enum``             any enumeration
-``proc``             any proc type
-``ref``              any ``ref`` type
-``ptr``              any ``ptr`` type
-``var``              any ``var`` type
-``distinct``         any distinct type
-``array``            any array type
-``set``              any set type
-``seq``              any seq type
-``auto``             any type
-``any``              distinct auto (see below)
-==================   ===================================================
-
-Furthermore, every generic type automatically creates a type class of the same
-name that will match any instantiation of the generic type.
-
-Type classes can be combined using the standard boolean operators to form
-more complex type classes:
-
-.. code-block:: nim
-  # create a type class that will match all tuple and object types
-  type RecordType = tuple or object
-
-  proc printFields(rec: RecordType) =
-    for key, value in fieldPairs(rec):
-      echo key, " = ", value
-
-Procedures utilizing type classes in such manner are considered to be
-`implicitly generic`:idx:. They will be instantiated once for each unique
-combination of param types used within the program.
-
-Nim also allows for type classes and regular types to be specified
-as `type constraints`:idx: of the generic type parameter:
-
-.. code-block:: nim
-  proc onlyIntOrString[T: int|string](x, y: T) = discard
-
-  onlyIntOrString(450, 616) # valid
-  onlyIntOrString(5.0, 0.0) # type mismatch
-  onlyIntOrString("xy", 50) # invalid as 'T' cannot be both at the same time
-
-By default, during overload resolution each named type class will bind to
-exactly one concrete type. We call such type classes `bind once`:idx: types.
-Here is an example taken directly from the system module to illustrate this:
-
-.. code-block:: nim
-  proc `==`*(x, y: tuple): bool =
-    ## requires `x` and `y` to be of the same tuple type
-    ## generic ``==`` operator for tuples that is lifted from the components
-    ## of `x` and `y`.
-    result = true
-    for a, b in fields(x, y):
-      if a != b: result = false
-
-Alternatively, the ``distinct`` type modifier can be applied to the type class
-to allow each param matching the type class to bind to a different type. Such
-type classes are called `bind many`:idx: types.
-
-Procs written with the implicitly generic style will often need to refer to the
-type parameters of the matched generic type. They can be easily accessed using
-the dot syntax:
-
-.. code-block:: nim
-  type Matrix[T, Rows, Columns] = object
-    ...
-
-  proc `[]`(m: Matrix, row, col: int): Matrix.T =
-    m.data[col * high(Matrix.Columns) + row]
-
-Alternatively, the `type` operator can be used over the proc params for similar
-effect when anonymous or distinct type classes are used.
-
-When a generic type is instantiated with a type class instead of a concrete
-type, this results in another more specific type class:
-
-.. code-block:: nim
-  seq[ref object]  # Any sequence storing references to any object type
-
-  type T1 = auto
-  proc foo(s: seq[T1], e: T1)
-    # seq[T1] is the same as just `seq`, but T1 will be allowed to bind
-    # to a single type, while the signature is being matched
-
-  Matrix[Ordinal] # Any Matrix instantiation using integer values
-
-As seen in the previous example, in such instantiations, it's not necessary to
-supply all type parameters of the generic type, because any missing ones will
-be inferred to have the equivalent of the `any` type class and thus they will
-match anything without discrimination.
-
-
-Concepts
---------
-
-**Note**: Concepts are still in development.
-
-Concepts, also known as "user-defined type classes", are used to specify an
-arbitrary set of requirements that the matched type must satisfy.
-
-Concepts are written in the following form:
-
-.. code-block:: nim
-  type
-    Comparable = concept x, y
-      (x < y) is bool
-
-    Stack[T] = concept s, var v
-      s.pop() is T
-      v.push(T)
-
-      s.len is Ordinal
-
-      for value in s:
-        value is T
-
-The concept is a match if:
-
-a) all of the expressions within the body can be compiled for the tested type
-b) all statically evaluable boolean expressions in the body must be true
-
-The identifiers following the ``concept`` keyword represent instances of the
-currently matched type. You can apply any of the standard type modifiers such
-as ``var``, ``ref``, ``ptr`` and ``static`` to denote a more specific type of
-instance. You can also apply the `type` modifier to create a named instance of
-the type itself:
-
-.. code-block:: nim
-  type
-    MyConcept = concept x, var v, ref r, ptr p, static s, type T
-      ...
-
-Within the concept body, types can appear in positions where ordinary values
-and parameters are expected. This provides a more convenient way to check for
-the presence of callable symbols with specific signatures:
-
-.. code-block:: nim
-  type
-    OutputStream = concept var s
-      s.write(string)
-
-In order to check for symbols accepting ``typedesc`` params, you must prefix
-the type with an explicit ``type`` modifier. The named instance of the type,
-following the ``concept`` keyword is also considered an explicit ``typedesc``
-value that will be matched only as a type.
-
-.. code-block:: nim
-  type
-    # Let's imagine a user-defined casting framework with operators
-    # such as `val.to(string)` and `val.to(JSonValue)`. We can test
-    # for these with the following concept:
-    MyCastables = concept x
-      x.to(type string)
-      x.to(type JSonValue)
-
-    # Let's define a couple of concepts, known from Algebra:
-    AdditiveMonoid* = concept x, y, type T
-      x + y is T
-      T.zero is T # require a proc such as `int.zero` or 'Position.zero'
-
-    AdditiveGroup* = concept x, y, type T
-      x is AdditiveMonoid
-      -x is T
-      x - y is T
-
-Please note that the ``is`` operator allows one to easily verify the precise
-type signatures of the required operations, but since type inference and
-default parameters are still applied in the concept body, it's also possible
-to describe usage protocols that do not reveal implementation details.
-
-Much like generics, concepts are instantiated exactly once for each tested type
-and any static code included within the body is executed only once.
-
-
-Concept diagnostics
--------------------
-
-By default, the compiler will report the matching errors in concepts only when
-no other overload can be selected and a normal compilation error is produced.
-When you need to understand why the compiler is not matching a particular
-concept and, as a result, a wrong overload is selected, you can apply the
-``explain`` pragma to either the concept body or a particular call-site.
-
-.. code-block:: nim
-  type
-    MyConcept {.explain.} = concept ...
-
-  overloadedProc(x, y, z) {.explain.}
-
-This will provide Hints in the compiler output either every time the concept is
-not matched or only on the particular call-site.
-
-
-Generic concepts and type binding rules
----------------------------------------
-
-The concept types can be parametric just like the regular generic types:
-
-.. code-block:: nim
-  ### matrixalgo.nim
-
-  import typetraits
-
-  type
-    AnyMatrix*[R, C: static[int]; T] = concept m, var mvar, type M
-      M.ValueType is T
-      M.Rows == R
-      M.Cols == C
-
-      m[int, int] is T
-      mvar[int, int] = T
-
-      type TransposedType = stripGenericParams(M)[C, R, T]
-
-    AnySquareMatrix*[N: static[int], T] = AnyMatrix[N, N, T]
-
-    AnyTransform3D* = AnyMatrix[4, 4, float]
-
-  proc transposed*(m: AnyMatrix): m.TransposedType =
-    for r in 0 ..< m.R:
-      for c in 0 ..< m.C:
-        result[r, c] = m[c, r]
-
-  proc determinant*(m: AnySquareMatrix): int =
-    ...
-
-  proc setPerspectiveProjection*(m: AnyTransform3D) =
-    ...
-
-  --------------
-  ### matrix.nim
-
-  type
-    Matrix*[M, N: static[int]; T] = object
-      data: array[M*N, T]
-
-  proc `[]`*(M: Matrix; m, n: int): M.T =
-    M.data[m * M.N + n]
-
-  proc `[]=`*(M: var Matrix; m, n: int; v: M.T) =
-    M.data[m * M.N + n] = v
-
-  # Adapt the Matrix type to the concept's requirements
-  template Rows*(M: type Matrix): expr = M.M
-  template Cols*(M: type Matrix): expr = M.N
-  template ValueType*(M: type Matrix): typedesc = M.T
-
-  -------------
-  ### usage.nim
-
-  import matrix, matrixalgo
-
-  var
-    m: Matrix[3, 3, int]
-    projectionMatrix: Matrix[4, 4, float]
-
-  echo m.transposed.determinant
-  setPerspectiveProjection projectionMatrix
-
-When the concept type is matched against a concrete type, the unbound type
-parameters are inferred from the body of the concept in a way that closely
-resembles the way generic parameters of callable symbols are inferred on
-call sites.
-
-Unbound types can appear both as params to calls such as `s.push(T)` and
-on the right-hand side of the ``is`` operator in cases such as `x.pop is T`
-and `x.data is seq[T]`.
-
-Unbound static params will be inferred from expressions involving the `==`
-operator and also when types dependent on them are being matched:
-
-.. code-block:: nim
-  type
-    MatrixReducer[M, N: static[int]; T] = concept x
-      x.reduce(SquareMatrix[N, T]) is array[M, int]
-
-The Nim compiler includes a simple linear equation solver, allowing it to
-infer static params in some situations where integer arithmetic is involved.
-
-Just like in regular type classes, Nim discriminates between ``bind once``
-and ``bind many`` types when matching the concept. You can add the ``distinct``
-modifier to any of the otherwise inferable types to get a type that will be
-matched without permanently inferring it. This may be useful when you need
-to match several procs accepting the same wide class of types:
-
-.. code-block:: nim
-  type
-    Enumerable[T] = concept e
-      for v in e:
-        v is T
-
-  type
-    MyConcept = concept o
-      # this could be inferred to a type such as Enumerable[int]
-      o.foo is distinct Enumerable
-
-      # this could be inferred to a different type such as Enumerable[float]
-      o.bar is distinct Enumerable
-
-      # it's also possible to give an alias name to a `bind many` type class
-      type Enum = distinct Enumerable
-      o.baz is Enum
-
-On the other hand, using ``bind once`` types allows you to test for equivalent
-types used in multiple signatures, without actually requiring any concrete
-types, thus allowing you to encode implementation-defined types:
-
-.. code-block:: nim
-  type
-    MyConcept = concept x
-      type T1 = auto
-      x.foo(T1)
-      x.bar(T1) # both procs must accept the same type
-
-      type T2 = seq[SomeNumber]
-      x.alpha(T2)
-      x.omega(T2) # both procs must accept the same type
-                  # and it must be a numeric sequence
-
-As seen in the previous examples, you can refer to generic concepts such as
-`Enumerable[T]` just by their short name. Much like the regular generic types,
-the concept will be automatically instantiated with the bind once auto type
-in the place of each missing generic param.
-
-Please note that generic concepts such as `Enumerable[T]` can be matched
-against concrete types such as `string`. Nim doesn't require the concept
-type to have the same number of parameters as the type being matched.
-If you wish to express a requirement towards the generic parameters of
-the matched type, you can use a type mapping operator such as `genericHead`
-or `stripGenericParams` within the body of the concept to obtain the
-uninstantiated version of the type, which you can then try to instantiate
-in any required way. For example, here is how one might define the classic
-`Functor` concept from Haskell and then demonstrate that Nim's `Option[T]`
-type is an instance of it:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  import future, typetraits
-
-  type
-    Functor[A] = concept f
-      type MatchedGenericType = genericHead(f.type)
-        # `f` will be a value of a type such as `Option[T]`
-        # `MatchedGenericType` will become the `Option` type
-
-      f.val is A
-        # The Functor should provide a way to obtain
-        # a value stored inside it
-
-      type T = auto
-      map(f, A -> T) is MatchedGenericType[T]
-        # And it should provide a way to map one instance of
-        # the Functor to a instance of a different type, given
-        # a suitable `map` operation for the enclosed values
-
-  import options
-  echo Option[int] is Functor # prints true
-
-
-Concept derived values
-----------------------
-
-All top level constants or types appearing within the concept body are
-accessible through the dot operator in procs where the concept was successfully
-matched to a concrete type:
-
-.. code-block:: nim
-  type
-    DateTime = concept t1, t2, type T
-      const Min = T.MinDate
-      T.Now is T
-
-      t1 < t2 is bool
-
-      type TimeSpan = type(t1 - t2)
-      TimeSpan * int is TimeSpan
-      TimeSpan + TimeSpan is TimeSpan
-
-      t1 + TimeSpan is T
-
-  proc eventsJitter(events: Enumerable[DateTime]): float =
-    var
-      # this variable will have the inferred TimeSpan type for
-      # the concrete Date-like value the proc was called with:
-      averageInterval: DateTime.TimeSpan
-
-      deviation: float
-    ...
-
-
-Concept refinement
-------------------
-
-When the matched type within a concept is directly tested against a different
-concept, we say that the outer concept is a refinement of the inner concept and
-thus it is more-specific. When both concepts are matched in a call during
-overload resolution, Nim will assign a higher precedence to the most specific
-one. As an alternative way of defining concept refinements, you can use the
-object inheritance syntax involving the ``of`` keyword:
-
-.. code-block:: nim
-  type
-    Graph = concept g, type G of EqualyComparable, Copyable
-      type
-        VertexType = G.VertexType
-        EdgeType = G.EdgeType
-
-      VertexType is Copyable
-      EdgeType is Copyable
-
-      var
-        v: VertexType
-        e: EdgeType
-
-    IncidendeGraph = concept of Graph
-      # symbols such as variables and types from the refined
-      # concept are automatically in scope:
-
-      g.source(e) is VertexType
-      g.target(e) is VertexType
-
-      g.outgoingEdges(v) is Enumerable[EdgeType]
-
-    BidirectionalGraph = concept g, type G
-      # The following will also turn the concept into a refinement when it
-      # comes to overload resolution, but it doesn't provide the convenient
-      # symbol inheritance
-      g is IncidendeGraph
-
-      g.incomingEdges(G.VertexType) is Enumerable[G.EdgeType]
-
-  proc f(g: IncidendeGraph)
-  proc f(g: BidirectionalGraph) # this one will be preferred if we pass a type
-                                # matching the BidirectionalGraph concept
-
-..
-  Converter type classes
-  ----------------------
-
-  Concepts can also be used to convert a whole range of types to a single type or
-  a small set of simpler types. This is achieved with a `return` statement within
-  the concept body:
-
-  .. code-block:: nim
-    type
-      Stringable = concept x
-        $x is string
-        return $x
-
-      StringRefValue[CharType] = object
-        base: ptr CharType
-        len: int
-
-      StringRef = concept x
-        # the following would be an overloaded proc for cstring, string, seq and
-        # other user-defined types, returning either a StringRefValue[char] or
-        # StringRefValue[wchar]
-        return makeStringRefValue(x)
-
-    # the varargs param will here be converted to an array of StringRefValues
-    # the proc will have only two instantiations for the two character types
-    proc log(format: static[string], varargs[StringRef])
-
-    # this proc will allow char and wchar values to be mixed in
-    # the same call at the cost of additional instantiations
-    # the varargs param will be converted to a tuple
-    proc log(format: static[string], varargs[distinct StringRef])
-
-
-..
-  VTable types
-  ------------
-
-  Concepts allow Nim to define a great number of algorithms, using only
-  static polymorphism and without erasing any type information or sacrificing
-  any execution speed. But when polymorphic collections of objects are required,
-  the user must use one of the provided type erasure techniques - either common
-  base types or VTable types.
-
-  VTable types are represented as "fat pointers" storing a reference to an
-  object together with a reference to a table of procs implementing a set of
-  required operations (the so called vtable).
-
-  In contrast to other programming languages, the vtable in Nim is stored
-  externally to the object, allowing you to create multiple different vtable
-  views for the same object. Thus, the polymorphism in Nim is unbounded -
-  any type can implement an unlimited number of protocols or interfaces not
-  originally envisioned by the type's author.
-
-  Any concept type can be turned into a VTable type by using the ``vtref``
-  or the ``vtptr`` compiler magics. Under the hood, these magics generate
-  a converter type class, which converts the regular instances of the matching
-  types to the corresponding VTable type.
-
-  .. code-block:: nim
-    type
-      IntEnumerable = vtref Enumerable[int]
-
-      MyObject = object
-        enumerables: seq[IntEnumerable]
-        streams: seq[OutputStream.vtref]
-
-    proc addEnumerable(o: var MyObject, e: IntEnumerable) =
-      o.enumerables.add e
-
-    proc addStream(o: var MyObject, e: OutputStream.vtref) =
-      o.streams.add e
-
-  The procs that will be included in the vtable are derived from the concept
-  body and include all proc calls for which all param types were specified as
-  concrete types. All such calls should include exactly one param of the type
-  matched against the concept (not necessarily in the first position), which
-  will be considered the value bound to the vtable.
-
-  Overloads will be created for all captured procs, accepting the vtable type
-  in the position of the captured underlying object.
-
-  Under these rules, it's possible to obtain a vtable type for a concept with
-  unbound type parameters or one instantiated with metatypes (type classes),
-  but it will include a smaller number of captured procs. A completely empty
-  vtable will be reported as an error.
-
-  The ``vtref`` magic produces types which can be bound to ``ref`` types and
-  the ``vtptr`` magic produced types bound to ``ptr`` types.
-
-
-Symbol lookup in generics
--------------------------
-
-Open and Closed symbols
-~~~~~~~~~~~~~~~~~~~~~~~
-
-The symbol binding rules in generics are slightly subtle: There are "open" and
-"closed" symbols. A "closed" symbol cannot be re-bound in the instantiation
-context, an "open" symbol can. Per default overloaded symbols are open
-and every other symbol is closed.
-
-Open symbols are looked up in two different contexts: Both the context
-at definition and the context at instantiation are considered:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  type
-    Index = distinct int
-
-  proc `==` (a, b: Index): bool {.borrow.}
-
-  var a = (0, 0.Index)
-  var b = (0, 0.Index)
-
-  echo a == b # works!
-
-In the example the generic ``==`` for tuples (as defined in the system module)
-uses the ``==`` operators of the tuple's components. However, the ``==`` for
-the ``Index`` type is defined *after* the ``==`` for tuples; yet the example
-compiles as the instantiation takes the currently defined symbols into account
-too.
-
-Mixin statement
----------------
-
-A symbol can be forced to be open by a `mixin`:idx: declaration:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  proc create*[T](): ref T =
-    # there is no overloaded 'init' here, so we need to state that it's an
-    # open symbol explicitly:
-    mixin init
-    new result
-    init result
-
-
-Bind statement
---------------
-
-The ``bind`` statement is the counterpart to the ``mixin`` statement. It
-can be used to explicitly declare identifiers that should be bound early (i.e.
-the identifiers should be looked up in the scope of the template/generic
-definition):
-
-.. code-block:: nim
-  # Module A
-  var
-    lastId = 0
-
-  template genId*: untyped =
-    bind lastId
-    inc(lastId)
-    lastId
-
-.. code-block:: nim
-  # Module B
-  import A
-
-  echo genId()
-
-But a ``bind`` is rarely useful because symbol binding from the definition
-scope is the default.
-
-
-
-Templates
-=========
-
-A template is a simple form of a macro: It is a simple substitution
-mechanism that operates on Nim's abstract syntax trees. It is processed in
-the semantic pass of the compiler.
-
-The syntax to *invoke* a template is the same as calling a procedure.
-
-Example:
-
-.. code-block:: nim
-  template `!=` (a, b: untyped): untyped =
-    # this definition exists in the System module
-    not (a == b)
-
-  assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6))
-
-The ``!=``, ``>``, ``>=``, ``in``, ``notin``, ``isnot`` operators are in fact
-templates:
-
-| ``a > b`` is transformed into ``b < a``.
-| ``a in b`` is transformed into ``contains(b, a)``.
-| ``notin`` and ``isnot`` have the obvious meanings.
-
-The "types" of templates can be the symbols ``untyped``,
-``typed`` or ``typedesc`` (stands for *type
-description*). These are "meta types", they can only be used in certain
-contexts. Real types can be used too; this implies that ``typed`` expressions
-are expected.
-
-
-Typed vs untyped parameters
----------------------------
-
-An ``untyped`` parameter means that symbol lookups and type resolution is not
-performed before the expression is passed to the template. This means that for
-example *undeclared* identifiers can be passed to the template:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  template declareInt(x: untyped) =
-    var x: int
-
-  declareInt(x) # valid
-  x = 3
-
-
-.. code-block:: nim
-    :test: "nim c $1"
-    :status: 1
-
-  template declareInt(x: typed) =
-    var x: int
-
-  declareInt(x) # invalid, because x has not been declared and so has no type
-
-A template where every parameter is ``untyped`` is called an `immediate`:idx:
-template. For historical reasons templates can be explicitly annotated with
-an ``immediate`` pragma and then these templates do not take part in
-overloading resolution and the parameters' types are *ignored* by the
-compiler. Explicit immediate templates are now deprecated.
-
-**Note**: For historical reasons ``stmt`` is an alias for ``typed`` and
-``expr`` an alias for ``untyped``, but new code should use the newer,
-clearer names.
-
-
-Passing a code block to a template
-----------------------------------
-
-You can pass a block of statements as a last parameter to a template via a
-special ``:`` syntax:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  template withFile(f, fn, mode, actions: untyped): untyped =
-    var f: File
-    if open(f, fn, mode):
-      try:
-        actions
-      finally:
-        close(f)
-    else:
-      quit("cannot open: " & fn)
-
-  withFile(txt, "ttempl3.txt", fmWrite):
-    txt.writeLine("line 1")
-    txt.writeLine("line 2")
-
-In the example the two ``writeLine`` statements are bound to the ``actions``
-parameter.
-
-
-Usually to pass a block of code to a template the parameter that accepts
-the block needs to be of type ``untyped``. Because symbol lookups are then
-delayed until template instantiation time:
-
-.. code-block:: nim
-    :test: "nim c $1"
-    :status: 1
-
-  template t(body: typed) =
-    block:
-      body
-
-  t:
-    var i = 1
-    echo i
-
-  t:
-    var i = 2  # fails with 'attempt to redeclare i'
-    echo i
-
-The above code fails with the mysterious error message that ``i`` has already
-been declared. The reason for this is that the ``var i = ...`` bodies need to
-be type-checked before they are passed to the ``body`` parameter and type
-checking in Nim implies symbol lookups. For the symbol lookups to succeed
-``i`` needs to be added to the current (i.e. outer) scope. After type checking
-these additions to the symbol table are not rolled back (for better or worse).
-The same code works with ``untyped`` as the passed body is not required to be
-type-checked:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  template t(body: untyped) =
-    block:
-      body
-
-  t:
-    var i = 1
-    echo i
-
-  t:
-    var i = 2  # compiles
-    echo i
-
-
-Varargs of untyped
-------------------
-
-In addition to the ``untyped`` meta-type that prevents type checking there is
-also ``varargs[untyped]`` so that not even the number of parameters is fixed:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  template hideIdentifiers(x: varargs[untyped]) = discard
-
-  hideIdentifiers(undeclared1, undeclared2)
-
-However, since a template cannot iterate over varargs, this feature is
-generally much more useful for macros.
-
-**Note**: For historical reasons ``varargs[expr]`` is not equivalent
-to ``varargs[untyped]``.
-
-
-Symbol binding in templates
----------------------------
-
-A template is a `hygienic`:idx: macro and so opens a new scope. Most symbols are
-bound from the definition scope of the template:
-
-.. code-block:: nim
-  # Module A
-  var
-    lastId = 0
-
-  template genId*: untyped =
-    inc(lastId)
-    lastId
-
-.. code-block:: nim
-  # Module B
-  import A
-
-  echo genId() # Works as 'lastId' has been bound in 'genId's defining scope
-
-As in generics symbol binding can be influenced via ``mixin`` or ``bind``
-statements.
-
-
-
-Identifier construction
------------------------
-
-In templates identifiers can be constructed with the backticks notation:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  template typedef(name: untyped, typ: typedesc) =
-    type
-      `T name`* {.inject.} = typ
-      `P name`* {.inject.} = ref `T name`
-
-  typedef(myint, int)
-  var x: PMyInt
-
-In the example ``name`` is instantiated with ``myint``, so \`T name\` becomes
-``Tmyint``.
-
-
-Lookup rules for template parameters
-------------------------------------
-
-A parameter ``p`` in a template is even substituted in the expression ``x.p``.
-Thus template arguments can be used as field names and a global symbol can be
-shadowed by the same argument name even when fully qualified:
-
-.. code-block:: nim
-  # module 'm'
-
-  type
-    Lev = enum
-      levA, levB
-
-  var abclev = levB
-
-  template tstLev(abclev: Lev) =
-    echo abclev, " ", m.abclev
-
-  tstLev(levA)
-  # produces: 'levA levA'
-
-But the global symbol can properly be captured by a ``bind`` statement:
-
-.. code-block:: nim
-  # module 'm'
-
-  type
-    Lev = enum
-      levA, levB
-
-  var abclev = levB
-
-  template tstLev(abclev: Lev) =
-    bind m.abclev
-    echo abclev, " ", m.abclev
-
-  tstLev(levA)
-  # produces: 'levA levB'
-
-
-Hygiene in templates
---------------------
-
-Per default templates are `hygienic`:idx:\: Local identifiers declared in a
-template cannot be accessed in the instantiation context:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  template newException*(exceptn: typedesc, message: string): untyped =
-    var
-      e: ref exceptn  # e is implicitly gensym'ed here
-    new(e)
-    e.msg = message
-    e
-
-  # so this works:
-  let e = "message"
-  raise newException(EIO, e)
-
-
-Whether a symbol that is declared in a template is exposed to the instantiation
-scope is controlled by the `inject`:idx: and `gensym`:idx: pragmas: gensym'ed
-symbols are not exposed but inject'ed are.
-
-The default for symbols of entity ``type``, ``var``, ``let`` and ``const``
-is ``gensym`` and for ``proc``, ``iterator``, ``converter``, ``template``,
-``macro`` is ``inject``. However, if the name of the entity is passed as a
-template parameter, it is an inject'ed symbol:
-
-.. code-block:: nim
-  template withFile(f, fn, mode: untyped, actions: untyped): untyped =
-    block:
-      var f: File  # since 'f' is a template param, it's injected implicitly
-      ...
-
-  withFile(txt, "ttempl3.txt", fmWrite):
-    txt.writeLine("line 1")
-    txt.writeLine("line 2")
-
-
-The ``inject`` and ``gensym`` pragmas are second class annotations; they have
-no semantics outside of a template definition and cannot be abstracted over:
-
-.. code-block:: nim
-  {.pragma myInject: inject.}
-
-  template t() =
-    var x {.myInject.}: int # does NOT work
-
-
-To get rid of hygiene in templates, one can use the `dirty`:idx: pragma for
-a template. ``inject`` and ``gensym`` have no effect in ``dirty`` templates.
-
-
-
-Limitations of the method call syntax
--------------------------------------
-
-The expression ``x`` in ``x.f`` needs to be semantically checked (that means
-symbol lookup and type checking) before it can be decided that it needs to be
-rewritten to ``f(x)``. Therefore the dot syntax has some limitations when it
-is used to invoke templates/macros:
-
-.. code-block:: nim
-    :test: "nim c $1"
-    :status: 1
-
-  template declareVar(name: untyped) =
-    const name {.inject.} = 45
-
-  # Doesn't compile:
-  unknownIdentifier.declareVar
-
-
-Another common example is this:
-
-.. code-block:: nim
-    :test: "nim c $1"
-    :status: 1
-
-  from sequtils import toSeq
-
-  iterator something: string =
-    yield "Hello"
-    yield "World"
-
-  var info = something().toSeq
-
-The problem here is that the compiler already decided that ``something()`` as
-an iterator is not callable in this context before ``toSeq`` gets its
-chance to convert it into a sequence.
-
-
-Macros
-======
-
-A macro is a special function that is executed at compile-time.
-Normally the input for a macro is an abstract syntax
-tree (AST) of the code that is passed to it. The macro can then do
-transformations on it and return the transformed AST. The
-transformed AST is then passed to the compiler as if the macro
-invocation would have been replaced by its result in the source
-code. This can be used to implement `domain specific
-languages`:idx:.
-
-While macros enable advanced compile-time code transformations, they
-cannot change Nim's syntax. However, this is no real restriction because
-Nim's syntax is flexible enough anyway.
-
-To write macros, one needs to know how the Nim concrete syntax is converted
-to an AST.
-
-There are two ways to invoke a macro:
-(1) invoking a macro like a procedure call (`expression macros`)
-(2) invoking a macro with the special ``macrostmt`` syntax (`statement macros`)
-
-
-Expression Macros
------------------
-
-The following example implements a powerful ``debug`` command that accepts a
-variable number of arguments:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  # to work with Nim syntax trees, we need an API that is defined in the
-  # ``macros`` module:
-  import macros
-
-  macro debug(args: varargs[untyped]): untyped =
-    # `args` is a collection of `NimNode` values that each contain the
-    # AST for an argument of the macro. A macro always has to
-    # return a `NimNode`. A node of kind `nnkStmtList` is suitable for
-    # this use case.
-    result = nnkStmtList.newTree()
-    # iterate over any argument that is passed to this macro:
-    for n in args:
-      # add a call to the statement list that writes the expression;
-      # `toStrLit` converts an AST to its string representation:
-      result.add newCall("write", newIdentNode("stdout"), newLit(n.repr))
-      # add a call to the statement list that writes ": "
-      result.add newCall("write", newIdentNode("stdout"), newLit(": "))
-      # add a call to the statement list that writes the expressions value:
-      result.add newCall("writeLine", newIdentNode("stdout"), n)
-
-  var
-    a: array[0..10, int]
-    x = "some string"
-  a[0] = 42
-  a[1] = 45
-
-  debug(a[0], a[1], x)
-
-The macro call expands to:
-
-.. code-block:: nim
-  write(stdout, "a[0]")
-  write(stdout, ": ")
-  writeLine(stdout, a[0])
-
-  write(stdout, "a[1]")
-  write(stdout, ": ")
-  writeLine(stdout, a[1])
-
-  write(stdout, "x")
-  write(stdout, ": ")
-  writeLine(stdout, x)
-
-
-Arguments that are passed to a ``varargs`` parameter are wrapped in an array
-constructor expression. This is why ``debug`` iterates over all of ``n``'s
-children.
-
-
-BindSym
--------
-
-The above ``debug`` macro relies on the fact that ``write``, ``writeLine`` and
-``stdout`` are declared in the system module and thus visible in the
-instantiating context. There is a way to use bound identifiers
-(aka `symbols`:idx:) instead of using unbound identifiers. The ``bindSym``
-builtin can be used for that:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  import macros
-
-  macro debug(n: varargs[typed]): untyped =
-    result = newNimNode(nnkStmtList, n)
-    for x in n:
-      # we can bind symbols in scope via 'bindSym':
-      add(result, newCall(bindSym"write", bindSym"stdout", toStrLit(x)))
-      add(result, newCall(bindSym"write", bindSym"stdout", newStrLitNode(": ")))
-      add(result, newCall(bindSym"writeLine", bindSym"stdout", x))
-
-  var
-    a: array[0..10, int]
-    x = "some string"
-  a[0] = 42
-  a[1] = 45
-
-  debug(a[0], a[1], x)
-
-The macro call expands to:
-
-.. code-block:: nim
-  write(stdout, "a[0]")
-  write(stdout, ": ")
-  writeLine(stdout, a[0])
-
-  write(stdout, "a[1]")
-  write(stdout, ": ")
-  writeLine(stdout, a[1])
-
-  write(stdout, "x")
-  write(stdout, ": ")
-  writeLine(stdout, x)
-
-However, the symbols ``write``, ``writeLine`` and ``stdout`` are already bound
-and are not looked up again. As the example shows, ``bindSym`` does work with
-overloaded symbols implicitly.
-
-
-Statement Macros
-----------------
-
-Statement macros are defined just as expression macros. However, they are
-invoked by an expression following a colon.
-
-The following example outlines a macro that generates a lexical analyzer from
-regular expressions:
-
-.. code-block:: nim
-  import macros
-
-  macro case_token(n: untyped): untyped =
-    # creates a lexical analyzer from regular expressions
-    # ... (implementation is an exercise for the reader :-)
-    discard
-
-  case_token: # this colon tells the parser it is a macro statement
-  of r"[A-Za-z_]+[A-Za-z_0-9]*":
-    return tkIdentifier
-  of r"0-9+":
-    return tkInteger
-  of r"[\+\-\*\?]+":
-    return tkOperator
-  else:
-    return tkUnknown
-
-
-**Style note**: For code readability, it is the best idea to use the least
-powerful programming construct that still suffices. So the "check list" is:
-
-(1) Use an ordinary proc/iterator, if possible.
-(2) Else: Use a generic proc/iterator, if possible.
-(3) Else: Use a template, if possible.
-(4) Else: Use a macro.
-
-
-Macros as pragmas
------------------
-
-Whole routines (procs, iterators etc.) can also be passed to a template or
-a macro via the pragma notation:
-
-.. code-block:: nim
-  template m(s: untyped) = discard
-
-  proc p() {.m.} = discard
-
-This is a simple syntactic transformation into:
-
-.. code-block:: nim
-  template m(s: untyped) = discard
-
-  m:
-    proc p() = discard
-
-
-For loop macros
----------------
-
-A macro that takes as its only input parameter an expression of the special
-type ``system.ForLoopStmt`` can rewrite the entirety of a ``for`` loop:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  import macros
-
-  macro enumerate(x: ForLoopStmt): untyped =
-    expectKind x, nnkForStmt
-    # we strip off the first for loop variable and use
-    # it as an integer counter:
-    result = newStmtList()
-    result.add newVarStmt(x[0], newLit(0))
-    var body = x[^1]
-    if body.kind != nnkStmtList:
-      body = newTree(nnkStmtList, body)
-    body.add newCall(bindSym"inc", x[0])
-    var newFor = newTree(nnkForStmt)
-    for i in 1..x.len-3:
-      newFor.add x[i]
-    # transform enumerate(X) to 'X'
-    newFor.add x[^2][1]
-    newFor.add body
-    result.add newFor
-
-  for a, b in enumerate(items([1, 2, 3])):
-    echo a, " ", b
-
-  for a2, b2 in enumerate([1, 2, 3, 5]):
-    echo a2, " ", b2
-
-
-Special Types
-=============
-
-static[T]
----------
-
-**Note**: static[T] is still in development.
-
-As their name suggests, static parameters must be known at compile-time:
-
-.. code-block:: nim
-
-  proc precompiledRegex(pattern: static[string]): RegEx =
-    var res {.global.} = re(pattern)
-    return res
-
-  precompiledRegex("/d+") # Replaces the call with a precompiled
-                          # regex, stored in a global variable
-
-  precompiledRegex(paramStr(1)) # Error, command-line options
-                                # are not known at compile-time
-
-
-For the purposes of code generation, all static params are treated as
-generic params - the proc will be compiled separately for each unique
-supplied value (or combination of values).
-
-Static params can also appear in the signatures of generic types:
-
-.. code-block:: nim
-
-  type
-    Matrix[M,N: static[int]; T: Number] = array[0..(M*N - 1), T]
-      # Note how `Number` is just a type constraint here, while
-      # `static[int]` requires us to supply a compile-time int value
-
-    AffineTransform2D[T] = Matrix[3, 3, T]
-    AffineTransform3D[T] = Matrix[4, 4, T]
-
-  var m1: AffineTransform3D[float]  # OK
-  var m2: AffineTransform2D[string] # Error, `string` is not a `Number`
-
-
-typedesc
---------
-
-`typedesc` is a special type allowing one to treat types as compile-time values
-(i.e. if types are compile-time values and all values have a type, then
-typedesc must be their type).
-
-When used as a regular proc param, typedesc acts as a type class. The proc
-will be instantiated for each unique type parameter and one can refer to the
-instantiation type using the param name:
-
-.. code-block:: nim
-
-  proc new(T: typedesc): ref T =
-    echo "allocating ", T.name
-    new(result)
-
-  var n = Node.new
-  var tree = new(BinaryTree[int])
-
-When multiple typedesc params are present, they will bind freely to different
-types. To force a bind-once behavior
-one can use an explicit ``typedesc[T]`` generic param:
-
-.. code-block:: nim
-  proc acceptOnlyTypePairs[T, U](A, B: typedesc[T]; C, D: typedesc[U])
-
-Once bound, typedesc params can appear in the rest of the proc signature:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  template declareVariableWithType(T: typedesc, value: T) =
-    var x: T = value
-
-  declareVariableWithType int, 42
-
-
-Overload resolution can be further influenced by constraining the set of
-types that will match the typedesc param:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  template maxval(T: typedesc[int]): int = high(int)
-  template maxval(T: typedesc[float]): float = Inf
-
-  var i = int.maxval
-  var f = float.maxval
-  when false:
-    var s = string.maxval # error, maxval is not implemented for string
-
-The constraint can be a concrete type or a type class.
-
-
-
-
-Special Operators
-=================
-
-dot operators
--------------
-
-**Note**: Dot operators are still experimental and so need to be enabled
-via ``{.experimental: "dotOperators".}``.
-
-Nim offers a special family of dot operators that can be used to
-intercept and rewrite proc call and field access attempts, referring
-to previously undeclared symbol names. They can be used to provide a
-fluent interface to objects lying outside the static confines of the
-type system such as values from dynamic scripting languages
-or dynamic file formats such as JSON or XML.
-
-When Nim encounters an expression that cannot be resolved by the
-standard overload resolution rules, the current scope will be searched
-for a dot operator that can be matched against a re-written form of
-the expression, where the unknown field or proc name is passed to
-an ``untyped`` parameter:
-
-.. code-block:: nim
-  a.b # becomes `.`(a, b)
-  a.b(c, d) # becomes `.`(a, b, c, d)
-
-The matched dot operators can be symbols of any callable kind (procs,
-templates and macros), depending on the desired effect:
-
-.. code-block:: nim
-  template `.` (js: PJsonNode, field: untyped): JSON = js[astToStr(field)]
-
-  var js = parseJson("{ x: 1, y: 2}")
-  echo js.x # outputs 1
-  echo js.y # outputs 2
-
-The following dot operators are available:
-
-operator `.`
-------------
-This operator will be matched against both field accesses and method calls.
-
-operator `.()`
----------------
-This operator will be matched exclusively against method calls. It has higher
-precedence than the `.` operator and this allows one to handle expressions like
-`x.y` and `x.y()` differently if one is interfacing with a scripting language
-for example.
-
-operator `.=`
--------------
-This operator will be matched against assignments to missing fields.
-
-.. code-block:: nim
-  a.b = c # becomes `.=`(a, b, c)
-
-
-
-
-Type bound operations
-=====================
-
-There are 3 operations that are bound to a type:
-
-1. Assignment
-2. Destruction
-3. Deep copying for communication between threads
-
-These operations can be *overridden* instead of *overloaded*. This means the
-implementation is automatically lifted to structured types. For instance if type
-``T`` has an overridden assignment operator ``=`` this operator is also used
-for assignments of the type ``seq[T]``. Since these operations are bound to a
-type they have to be bound to a nominal type for reasons of simplicity of
-implementation: This means an overridden ``deepCopy`` for ``ref T`` is really
-bound to ``T`` and not to ``ref T``. This also means that one cannot override
-``deepCopy`` for both ``ptr T`` and ``ref T`` at the same time; instead a
-helper distinct or object type has to be used for one pointer type.
-
-
-operator `=`
-------------
-
-This operator is the assignment operator. Note that in the contexts
-``result = expr``, ``parameter = defaultValue`` or for
-parameter passing no assignment is performed. For a type ``T`` that has an
-overloaded assignment operator ``var v = T()`` is rewritten
-to ``var v: T; v = T()``; in other words ``var`` and ``let`` contexts do count
-as assignments.
-
-The assignment operator needs to be attached to an object or distinct
-type ``T``. Its signature has to be ``(var T, T)``. Example:
-
-.. code-block:: nim
-  type
-    Concrete = object
-      a, b: string
-
-  proc `=`(d: var Concrete; src: Concrete) =
-    shallowCopy(d.a, src.a)
-    shallowCopy(d.b, src.b)
-    echo "Concrete '=' called"
-
-  var x, y: array[0..2, Concrete]
-  var cA, cB: Concrete
-
-  var cATup, cBTup: tuple[x: int, ha: Concrete]
-
-  x = y
-  cA = cB
-  cATup = cBTup
-
-
-
-destructors
------------
-
-A destructor must have a single parameter with a concrete type (the name of a
-generic type is allowed too). The name of the destructor has to be ``=destroy``.
-
-``=destroy(v)`` will be automatically invoked for every local stack
-variable ``v`` that goes out of scope.
-
-If a structured type features a field with destructable type and
-the user has not provided an explicit implementation, a destructor for the
-structured type will be automatically generated. Calls to any base class
-destructors in both user-defined and generated destructors will be inserted.
-
-A destructor is attached to the type it destructs; expressions of this type
-can then only be used in *destructible contexts* and as parameters:
-
-.. code-block:: nim
-  type
-    MyObj = object
-      x, y: int
-      p: pointer
-
-  proc `=destroy`(o: var MyObj) =
-    if o.p != nil: dealloc o.p
-
-  proc open: MyObj =
-    result = MyObj(x: 1, y: 2, p: alloc(3))
-
-  proc work(o: MyObj) =
-    echo o.x
-    # No destructor invoked here for 'o' as 'o' is a parameter.
-
-  proc main() =
-    # destructor automatically invoked at the end of the scope:
-    var x = open()
-    # valid: pass 'x' to some other proc:
-    work(x)
-
-    # Error: usage of a type with a destructor in a non destructible context
-    echo open()
-
-A destructible context is currently only the following:
-
-1. The ``expr`` in ``var x = expr``.
-2. The ``expr`` in ``let x = expr``.
-3. The ``expr`` in ``return expr``.
-4. The ``expr`` in ``result = expr`` where ``result`` is the special symbol
-   introduced by the compiler.
-
-These rules ensure that the construction is tied to a variable and can easily
-be destructed at its scope exit. Later versions of the language will improve
-the support of destructors.
-
-Be aware that destructors are not called for objects allocated with ``new``.
-This may change in future versions of language, but for now the `finalizer`:idx:
-parameter to ``new`` has to be used.
-
-**Note**: Destructors are still experimental and the spec might change
-significantly in order to incorporate an escape analysis.
-
-
-deepCopy
---------
-
-``=deepCopy`` is a builtin that is invoked whenever data is passed to
-a ``spawn``'ed proc to ensure memory safety. The programmer can override its
-behaviour for a specific ``ref`` or ``ptr`` type ``T``. (Later versions of the
-language may weaken this restriction.)
-
-The signature has to be:
-
-.. code-block:: nim
-  proc `=deepCopy`(x: T): T
-
-This mechanism will be used by most data structures that support shared memory
-like channels to implement thread safe automatic memory management.
-
-The builtin ``deepCopy`` can even clone closures and their environments. See
-the documentation of `spawn`_ for details.
-
-
-Term rewriting macros
-=====================
-
-Term rewriting macros are macros or templates that have not only
-a *name* but also a *pattern* that is searched for after the semantic checking
-phase of the compiler: This means they provide an easy way to enhance the
-compilation pipeline with user defined optimizations:
-
-.. code-block:: nim
-  template optMul{`*`(a, 2)}(a: int): int = a+a
-
-  let x = 3
-  echo x * 2
-
-The compiler now rewrites ``x * 2`` as ``x + x``. The code inside the
-curlies is the pattern to match against. The operators ``*``,  ``**``,
-``|``, ``~`` have a special meaning in patterns if they are written in infix
-notation, so to match verbatim against ``*`` the ordinary function call syntax
-needs to be used.
-
-
-Unfortunately optimizations are hard to get right and even the tiny example
-is **wrong**:
-
-.. code-block:: nim
-  template optMul{`*`(a, 2)}(a: int): int = a+a
-
-  proc f(): int =
-    echo "side effect!"
-    result = 55
-
-  echo f() * 2
-
-We cannot duplicate 'a' if it denotes an expression that has a side effect!
-Fortunately Nim supports side effect analysis:
-
-.. code-block:: nim
-  template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a+a
-
-  proc f(): int =
-    echo "side effect!"
-    result = 55
-
-  echo f() * 2 # not optimized ;-)
-
-You can make one overload matching with a constraint and one without, and the
-one with a constraint will have precedence, and so you can handle both cases
-differently.
-
-So what about ``2 * a``? We should tell the compiler ``*`` is commutative. We
-cannot really do that however as the following code only swaps arguments
-blindly:
-
-.. code-block:: nim
-  template mulIsCommutative{`*`(a, b)}(a, b: int): int = b*a
-
-What optimizers really need to do is a *canonicalization*:
-
-.. code-block:: nim
-  template canonMul{`*`(a, b)}(a: int{lit}, b: int): int = b*a
-
-The ``int{lit}`` parameter pattern matches against an expression of
-type ``int``, but only if it's a literal.
-
-
-
-Parameter constraints
----------------------
-
-The `parameter constraint`:idx: expression can use the operators ``|`` (or),
-``&`` (and) and ``~`` (not) and the following predicates:
-
-===================      =====================================================
-Predicate                Meaning
-===================      =====================================================
-``atom``                 The matching node has no children.
-``lit``                  The matching node is a literal like "abc", 12.
-``sym``                  The matching node must be a symbol (a bound
-                         identifier).
-``ident``                The matching node must be an identifier (an unbound
-                         identifier).
-``call``                 The matching AST must be a call/apply expression.
-``lvalue``               The matching AST must be an lvalue.
-``sideeffect``           The matching AST must have a side effect.
-``nosideeffect``         The matching AST must have no side effect.
-``param``                A symbol which is a parameter.
-``genericparam``         A symbol which is a generic parameter.
-``module``               A symbol which is a module.
-``type``                 A symbol which is a type.
-``var``                  A symbol which is a variable.
-``let``                  A symbol which is a ``let`` variable.
-``const``                A symbol which is a constant.
-``result``               The special ``result`` variable.
-``proc``                 A symbol which is a proc.
-``method``               A symbol which is a method.
-``iterator``             A symbol which is an iterator.
-``converter``            A symbol which is a converter.
-``macro``                A symbol which is a macro.
-``template``             A symbol which is a template.
-``field``                A symbol which is a field in a tuple or an object.
-``enumfield``            A symbol which is a field in an enumeration.
-``forvar``               A for loop variable.
-``label``                A label (used in ``block`` statements).
-``nk*``                  The matching AST must have the specified kind.
-                         (Example: ``nkIfStmt`` denotes an ``if`` statement.)
-``alias``                States that the marked parameter needs to alias
-                         with *some* other parameter.
-``noalias``              States that *every* other parameter must not alias
-                         with the marked parameter.
-===================      =====================================================
-
-Predicates that share their name with a keyword have to be escaped with
-backticks: `` `const` ``.
-The ``alias`` and ``noalias`` predicates refer not only to the matching AST,
-but also to every other bound parameter; syntactically they need to occur after
-the ordinary AST predicates:
-
-.. code-block:: nim
-  template ex{a = b + c}(a: int{noalias}, b, c: int) =
-    # this transformation is only valid if 'b' and 'c' do not alias 'a':
-    a = b
-    inc a, c
-
-
-Pattern operators
------------------
-
-The operators ``*``,  ``**``, ``|``, ``~`` have a special meaning in patterns
-if they are written in infix notation.
-
-
-The ``|`` operator
-~~~~~~~~~~~~~~~~~~
-
-The ``|`` operator if used as infix operator creates an ordered choice:
-
-.. code-block:: nim
-  template t{0|1}(): untyped = 3
-  let a = 1
-  # outputs 3:
-  echo a
-
-The matching is performed after the compiler performed some optimizations like
-constant folding, so the following does not work:
-
-.. code-block:: nim
-  template t{0|1}(): untyped = 3
-  # outputs 1:
-  echo 1
-
-The reason is that the compiler already transformed the 1 into "1" for
-the ``echo`` statement. However, a term rewriting macro should not change the
-semantics anyway. In fact they can be deactivated with the ``--patterns:off``
-command line option or temporarily with the ``patterns`` pragma.
-
-
-The ``{}`` operator
-~~~~~~~~~~~~~~~~~~~
-
-A pattern expression can be bound to a pattern parameter via the ``expr{param}``
-notation:
-
-.. code-block:: nim
-  template t{(0|1|2){x}}(x: untyped): untyped = x+1
-  let a = 1
-  # outputs 2:
-  echo a
-
-
-The ``~`` operator
-~~~~~~~~~~~~~~~~~~
-
-The ``~`` operator is the **not** operator in patterns:
-
-.. code-block:: nim
-  template t{x = (~x){y} and (~x){z}}(x, y, z: bool) =
-    x = y
-    if x: x = z
-
-  var
-    a = false
-    b = true
-    c = false
-  a = b and c
-  echo a
-
-
-The ``*`` operator
-~~~~~~~~~~~~~~~~~~
-
-The ``*`` operator can *flatten* a nested binary expression like ``a & b & c``
-to ``&(a, b, c)``:
-
-.. code-block:: nim
-  var
-    calls = 0
-
-  proc `&&`(s: varargs[string]): string =
-    result = s[0]
-    for i in 1..len(s)-1: result.add s[i]
-    inc calls
-
-  template optConc{ `&&` * a }(a: string): untyped = &&a
-
-  let space = " "
-  echo "my" && (space & "awe" && "some " ) && "concat"
-
-  # check that it's been optimized properly:
-  doAssert calls == 1
-
-
-The second operator of `*` must be a parameter; it is used to gather all the
-arguments. The expression ``"my" && (space & "awe" && "some " ) && "concat"``
-is passed to ``optConc`` in ``a`` as a special list (of kind ``nkArgList``)
-which is flattened into a call expression; thus the invocation of ``optConc``
-produces:
-
-.. code-block:: nim
-   `&&`("my", space & "awe", "some ", "concat")
-
-
-The ``**`` operator
-~~~~~~~~~~~~~~~~~~~
-
-The ``**`` is much like the ``*`` operator, except that it gathers not only
-all the arguments, but also the matched operators in reverse polish notation:
-
-.. code-block:: nim
-  import macros
-
-  type
-    Matrix = object
-      dummy: int
-
-  proc `*`(a, b: Matrix): Matrix = discard
-  proc `+`(a, b: Matrix): Matrix = discard
-  proc `-`(a, b: Matrix): Matrix = discard
-  proc `$`(a: Matrix): string = result = $a.dummy
-  proc mat21(): Matrix =
-    result.dummy = 21
-
-  macro optM{ (`+`|`-`|`*`) ** a }(a: Matrix): untyped =
-    echo treeRepr(a)
-    result = newCall(bindSym"mat21")
-
-  var x, y, z: Matrix
-
-  echo x + y * z - x
-
-This passes the expression ``x + y * z - x`` to the ``optM`` macro as
-an ``nnkArgList`` node containing::
-
-  Arglist
-    Sym "x"
-    Sym "y"
-    Sym "z"
-    Sym "*"
-    Sym "+"
-    Sym "x"
-    Sym "-"
-
-(Which is the reverse polish notation of ``x + y * z - x``.)
-
-
-Parameters
-----------
-
-Parameters in a pattern are type checked in the matching process. If a
-parameter is of the type ``varargs`` it is treated specially and it can match
-0 or more arguments in the AST to be matched against:
-
-.. code-block:: nim
-  template optWrite{
-    write(f, x)
-    ((write|writeLine){w})(f, y)
-  }(x, y: varargs[untyped], f: File, w: untyped) =
-    w(f, x, y)
-
-
-
-Example: Partial evaluation
----------------------------
-
-The following example shows how some simple partial evaluation can be
-implemented with term rewriting:
-
-.. code-block:: nim
-  proc p(x, y: int; cond: bool): int =
-    result = if cond: x + y else: x - y
-
-  template optP1{p(x, y, true)}(x, y: untyped): untyped = x + y
-  template optP2{p(x, y, false)}(x, y: untyped): untyped = x - y
-
-
-Example: Hoisting
------------------
-
-The following example shows how some form of hoisting can be implemented:
-
-.. code-block:: nim
-  import pegs
-
-  template optPeg{peg(pattern)}(pattern: string{lit}): Peg =
-    var gl {.global, gensym.} = peg(pattern)
-    gl
-
-  for i in 0 .. 3:
-    echo match("(a b c)", peg"'(' @ ')'")
-    echo match("W_HI_Le", peg"\y 'while'")
-
-The ``optPeg`` template optimizes the case of a peg constructor with a string
-literal, so that the pattern will only be parsed once at program startup and
-stored in a global ``gl`` which is then re-used. This optimization is called
-hoisting because it is comparable to classical loop hoisting.
-
-
-AST based overloading
-=====================
-
-Parameter constraints can also be used for ordinary routine parameters; these
-constraints affect ordinary overloading resolution then:
-
-.. code-block:: nim
-  proc optLit(a: string{lit|`const`}) =
-    echo "string literal"
-  proc optLit(a: string) =
-    echo "no string literal"
-
-  const
-    constant = "abc"
-
-  var
-    variable = "xyz"
-
-  optLit("literal")
-  optLit(constant)
-  optLit(variable)
-
-However, the constraints ``alias`` and ``noalias`` are not available in
-ordinary routines.
-
-
-Move optimization
------------------
-
-The ``call`` constraint is particularly useful to implement a move
-optimization for types that have copying semantics:
-
-.. code-block:: nim
-  proc `[]=`*(t: var Table, key: string, val: string) =
-    ## puts a (key, value)-pair into `t`. The semantics of string require
-    ## a copy here:
-    let idx = findInsertionPosition(key)
-    t[idx].key = key
-    t[idx].val = val
-
-  proc `[]=`*(t: var Table, key: string{call}, val: string{call}) =
-    ## puts a (key, value)-pair into `t`. Optimized version that knows that
-    ## the strings are unique and thus don't need to be copied:
-    let idx = findInsertionPosition(key)
-    shallowCopy t[idx].key, key
-    shallowCopy t[idx].val, val
-
-  var t: Table
-  # overloading resolution ensures that the optimized []= is called here:
-  t[f()] = g()
-
-
-
-Modules
-=======
-Nim supports splitting a program into pieces by a module concept.
-Each module needs to be in its own file and has its own `namespace`:idx:.
-Modules enable `information hiding`:idx: and `separate compilation`:idx:.
-A module may gain access to symbols of another module by the `import`:idx:
-statement. `Recursive module dependencies`:idx: are allowed, but slightly
-subtle. Only top-level symbols that are marked with an asterisk (``*``) are
-exported. A valid module name can only be a valid Nim identifier (and thus its
-filename is ``identifier.nim``).
-
-The algorithm for compiling modules is:
-
-- compile the whole module as usual, following import statements recursively
-
-- if there is a cycle only import the already parsed symbols (that are
-  exported); if an unknown identifier occurs then abort
-
-This is best illustrated by an example:
-
-.. code-block:: nim
-  # Module A
-  type
-    T1* = int  # Module A exports the type ``T1``
-  import B     # the compiler starts parsing B
-
-  proc main() =
-    var i = p(3) # works because B has been parsed completely here
-
-  main()
-
-
-.. code-block:: nim
-  # Module B
-  import A  # A is not parsed here! Only the already known symbols
-            # of A are imported.
-
-  proc p*(x: A.T1): A.T1 =
-    # this works because the compiler has already
-    # added T1 to A's interface symbol table
-    result = x + 1
-
-
-Import statement
-~~~~~~~~~~~~~~~~
-
-After the ``import`` statement a list of module names can follow or a single
-module name followed by an ``except`` list to prevent some symbols to be
-imported:
-
-.. code-block:: nim
-    :test: "nim c $1"
-    :status: 1
-
-  import strutils except `%`, toUpper
-
-  # doesn't work then:
-  echo "$1" % "abc".toUpper
-
-
-It is not checked that the ``except`` list is really exported from the module.
-This feature allows to compile against an older version of the module that
-does not export these identifiers.
-
-
-Include statement
-~~~~~~~~~~~~~~~~~
-The ``include`` statement does something fundamentally different than
-importing a module: it merely includes the contents of a file. The ``include``
-statement is useful to split up a large module into several files:
-
-.. code-block:: nim
-  include fileA, fileB, fileC
-
-
-
-Module names in imports
-~~~~~~~~~~~~~~~~~~~~~~~
-
-A module alias can be introduced via the ``as`` keyword:
-
-.. code-block:: nim
-  import strutils as su, sequtils as qu
-
-  echo su.format("$1", "lalelu")
-
-The original module name is then not accessible. The
-notations ``path/to/module`` or ``path.to.module`` or ``"path/to/module"``
-can be used to refer to a module in subdirectories:
-
-.. code-block:: nim
-  import lib.pure.strutils, lib/pure/os, "lib/pure/times"
-
-Note that the module name is still ``strutils`` and not ``lib.pure.strutils``
-and so one **cannot** do:
-
-.. code-block:: nim
-  import lib.pure.strutils
-  echo lib.pure.strutils
-
-Likewise the following does not make sense as the name is ``strutils`` already:
-
-.. code-block:: nim
-  import lib.pure.strutils as strutils
-
-
-Collective imports from a directory
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The syntax ``import dir / [moduleA, moduleB]`` can be used to import multiple modules
-from the same directory.
-
-Path names are syntactically either Nim identifiers or string literals. If the path
-name is not a valid Nim identifier it needs to be a string literal:
-
-.. code-block:: nim
-  import "gfx/3d/somemodule" # in quotes because '3d' is not a valid Nim identifier
-
-
-Pseudo import/include paths
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-A directory can also be a so called "pseudo directory".
-
-There are two pseudo directories:
-
-1. ``std``: The ``std`` pseudo directory is the abstract location of Nim's standard
-  library. For example, the syntax ``import std / strutils`` is used to unambiguously
-  refer to the standard library's ``strutils`` module.
-2. ``pkg``: The ``pkg`` pseudo directory is used to unambiguously refer to a Nimble
-  package. However, for technical details that lie outside of the scope of this document
-  its semantics are: *Use the search path to look for module name but ignore the standard
-  library locations*. In other words, it is the opposite of ``std``.
-
-
-From import statement
-~~~~~~~~~~~~~~~~~~~~~
-
-After the ``from`` statement a module name follows followed by
-an ``import`` to list the symbols one likes to use without explicit
-full qualification:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  from strutils import `%`
-
-  echo "$1" % "abc"
-  # always possible: full qualification:
-  echo strutils.replace("abc", "a", "z")
-
-It's also possible to use ``from module import nil`` if one wants to import
-the module but wants to enforce fully qualified access to every symbol
-in ``module``.
-
-
-Export statement
-~~~~~~~~~~~~~~~~
-
-An ``export`` statement can be used for symbol forwarding so that client
-modules don't need to import a module's dependencies:
-
-.. code-block:: nim
-  # module B
-  type MyObject* = object
-
-.. code-block:: nim
-  # module A
-  import B
-  export B.MyObject
-
-  proc `$`*(x: MyObject): string = "my object"
-
-
-.. code-block:: nim
-  # module C
-  import A
-
-  # B.MyObject has been imported implicitly here:
-  var x: MyObject
-  echo $x
-
-When the exported symbol is another module, all of its definitions will
-be forwarded. You can use an ``except`` list to exclude some of the symbols.
-
-
-Scope rules
------------
-Identifiers are valid from the point of their declaration until the end of
-the block in which the declaration occurred. The range where the identifier
-is known is the scope of the identifier. The exact scope of an
-identifier depends on the way it was declared.
-
-Block scope
-~~~~~~~~~~~
-The *scope* of a variable declared in the declaration part of a block
-is valid from the point of declaration until the end of the block. If a
-block contains a second block, in which the identifier is redeclared,
-then inside this block, the second declaration will be valid. Upon
-leaving the inner block, the first declaration is valid again. An
-identifier cannot be redefined in the same block, except if valid for
-procedure or iterator overloading purposes.
-
-
-Tuple or object scope
-~~~~~~~~~~~~~~~~~~~~~
-The field identifiers inside a tuple or object definition are valid in the
-following places:
-
-* To the end of the tuple/object definition.
-* Field designators of a variable of the given tuple/object type.
-* In all descendant types of the object type.
-
-Module scope
-~~~~~~~~~~~~
-All identifiers of a module are valid from the point of declaration until
-the end of the module. Identifiers from indirectly dependent modules are *not*
-available. The `system`:idx: module is automatically imported in every module.
-
-If a module imports an identifier by two different modules, each occurrence of
-the identifier has to be qualified, unless it is an overloaded procedure or
-iterator in which case the overloading resolution takes place:
-
-.. code-block:: nim
-  # Module A
-  var x*: string
-
-.. code-block:: nim
-  # Module B
-  var x*: int
-
-.. code-block:: nim
-  # Module C
-  import A, B
-  write(stdout, x) # error: x is ambiguous
-  write(stdout, A.x) # no error: qualifier used
-
-  var x = 4
-  write(stdout, x) # not ambiguous: uses the module C's x
-
-
-Compiler Messages
-=================
-
-The Nim compiler emits different kinds of messages: `hint`:idx:,
-`warning`:idx:, and `error`:idx: messages. An *error* message is emitted if
-the compiler encounters any static error.
-
-
-
-Pragmas
-=======
-
-Pragmas are Nim's method to give the compiler additional information /
-commands without introducing a massive number of new keywords. Pragmas are
-processed on the fly during semantic checking. Pragmas are enclosed in the
-special ``{.`` and ``.}`` curly brackets. Pragmas are also often used as a
-first implementation to play with a language feature before a nicer syntax
-to access the feature becomes available.
-
-
-deprecated pragma
------------------
-
-The deprecated pragma is used to mark a symbol as deprecated:
-
-.. code-block:: nim
-  proc p() {.deprecated.}
-  var x {.deprecated.}: char
-
-It can also be used as a statement, in that case it takes a list of *renamings*.
-
-.. code-block:: nim
-  type
-    File = object
-    Stream = ref object
-  {.deprecated: [TFile: File, PStream: Stream].}
-
-
-noSideEffect pragma
--------------------
-The ``noSideEffect`` pragma is used to mark a proc/iterator to have no side
-effects. This means that the proc/iterator only changes locations that are
-reachable from its parameters and the return value only depends on the
-arguments. If none of its parameters have the type ``var T``
-or ``ref T`` or ``ptr T`` this means no locations are modified. It is a static
-error to mark a proc/iterator to have no side effect if the compiler cannot
-verify this.
-
-As a special semantic rule, the built-in `debugEcho <system.html#debugEcho>`_
-pretends to be free of side effects, so that it can be used for debugging
-routines marked as ``noSideEffect``.
-
-``func`` is syntactic sugar for a proc with no side effects:
-
-.. code-block:: nim
-  func `+` (x, y: int): int
-
-
-compileTime pragma
-------------------
-The ``compileTime`` pragma is used to mark a proc or variable to be used at
-compile time only. No code will be generated for it. Compile time procs are
-useful as helpers for macros. Since version 0.12.0 of the language, a proc
-that uses ``system.NimNode`` within its parameter types is implicitly declared
-``compileTime``:
-
-.. code-block:: nim
-  proc astHelper(n: NimNode): NimNode =
-    result = n
-
-Is the same as:
-
-.. code-block:: nim
-  proc astHelper(n: NimNode): NimNode {.compileTime.} =
-    result = n
-
-
-noReturn pragma
----------------
-The ``noreturn`` pragma is used to mark a proc that never returns.
-
-
-acyclic pragma
---------------
-The ``acyclic`` pragma can be used for object types to mark them as acyclic
-even though they seem to be cyclic. This is an **optimization** for the garbage
-collector to not consider objects of this type as part of a cycle:
-
-.. code-block:: nim
-  type
-    Node = ref NodeObj
-    NodeObj {.acyclic.} = object
-      left, right: Node
-      data: string
-
-Or if we directly use a ref object:
-
-.. code-block:: nim
-  type
-    Node = ref object {.acyclic.}
-      left, right: Node
-      data: string
-
-In the example a tree structure is declared with the ``Node`` type. Note that
-the type definition is recursive and the GC has to assume that objects of
-this type may form a cyclic graph. The ``acyclic`` pragma passes the
-information that this cannot happen to the GC. If the programmer uses the
-``acyclic`` pragma for data types that are in reality cyclic, the GC may leak
-memory, but nothing worse happens.
-
-**Future directions**: The ``acyclic`` pragma may become a property of a
-``ref`` type:
-
-.. code-block:: nim
-  type
-    Node = acyclic ref NodeObj
-    NodeObj = object
-      left, right: Node
-      data: string
-
-
-final pragma
-------------
-The ``final`` pragma can be used for an object type to specify that it
-cannot be inherited from. Note that inheritance is only available for
-objects that inherit from an existing object (via the ``object of SuperType``
-syntax) or that have been marked as ``inheritable``.
-
-
-shallow pragma
---------------
-The ``shallow`` pragma affects the semantics of a type: The compiler is
-allowed to make a shallow copy. This can cause serious semantic issues and
-break memory safety! However, it can speed up assignments considerably,
-because the semantics of Nim require deep copying of sequences and strings.
-This can be expensive, especially if sequences are used to build a tree
-structure:
-
-.. code-block:: nim
-  type
-    NodeKind = enum nkLeaf, nkInner
-    Node {.shallow.} = object
-      case kind: NodeKind
-      of nkLeaf:
-        strVal: string
-      of nkInner:
-        children: seq[Node]
-
-
-pure pragma
------------
-An object type can be marked with the ``pure`` pragma so that its type
-field which is used for runtime type identification is omitted. This used to be
-necessary for binary compatibility with other compiled languages.
-
-An enum type can be marked as ``pure``. Then access of its fields always
-requires full qualification.
-
-
-asmNoStackFrame pragma
-----------------------
-A proc can be marked with the ``asmNoStackFrame`` pragma to tell the compiler
-it should not generate a stack frame for the proc. There are also no exit
-statements like ``return result;`` generated and the generated C function is
-declared as ``__declspec(naked)`` or ``__attribute__((naked))`` (depending on
-the used C compiler).
-
-**Note**: This pragma should only be used by procs which consist solely of
-assembler statements.
-
-error pragma
-------------
-The ``error`` pragma is used to make the compiler output an error message
-with the given content. Compilation does not necessarily abort after an error
-though.
-
-The ``error`` pragma can also be used to
-annotate a symbol (like an iterator or proc). The *usage* of the symbol then
-triggers a compile-time error. This is especially useful to rule out that some
-operation is valid due to overloading and type conversions:
-
-.. code-block:: nim
-  ## check that underlying int values are compared and not the pointers:
-  proc `==`(x, y: ptr int): bool {.error.}
-
-
-fatal pragma
-------------
-The ``fatal`` pragma is used to make the compiler output an error message
-with the given content. In contrast to the ``error`` pragma, compilation
-is guaranteed to be aborted by this pragma. Example:
-
-.. code-block:: nim
-  when not defined(objc):
-    {.fatal: "Compile this program with the objc command!".}
-
-warning pragma
---------------
-The ``warning`` pragma is used to make the compiler output a warning message
-with the given content. Compilation continues after the warning.
-
-hint pragma
------------
-The ``hint`` pragma is used to make the compiler output a hint message with
-the given content. Compilation continues after the hint.
-
-line pragma
------------
-The ``line`` pragma can be used to affect line information of the annotated
-statement as seen in stack backtraces:
-
-.. code-block:: nim
-
-  template myassert*(cond: untyped, msg = "") =
-    if not cond:
-      # change run-time line information of the 'raise' statement:
-      {.line: InstantiationInfo().}:
-        raise newException(EAssertionFailed, msg)
-
-If the ``line`` pragma is used with a parameter, the parameter needs be a
-``tuple[filename: string, line: int]``. If it is used without a parameter,
-``system.InstantiationInfo()`` is used.
-
-
-linearScanEnd pragma
---------------------
-The ``linearScanEnd`` pragma can be used to tell the compiler how to
-compile a Nim `case`:idx: statement. Syntactically it has to be used as a
-statement:
-
-.. code-block:: nim
-  case myInt
-  of 0:
-    echo "most common case"
-  of 1:
-    {.linearScanEnd.}
-    echo "second most common case"
-  of 2: echo "unlikely: use branch table"
-  else: echo "unlikely too: use branch table for ", myInt
-
-In the example, the case branches ``0`` and ``1`` are much more common than
-the other cases. Therefore the generated assembler code should test for these
-values first, so that the CPU's branch predictor has a good chance to succeed
-(avoiding an expensive CPU pipeline stall). The other cases might be put into a
-jump table for O(1) overhead, but at the cost of a (very likely) pipeline
-stall.
-
-The ``linearScanEnd`` pragma should be put into the last branch that should be
-tested against via linear scanning. If put into the last branch of the
-whole ``case`` statement, the whole ``case`` statement uses linear scanning.
-
-
-computedGoto pragma
--------------------
-The ``computedGoto`` pragma can be used to tell the compiler how to
-compile a Nim `case`:idx: in a ``while true`` statement.
-Syntactically it has to be used as a statement inside the loop:
-
-.. code-block:: nim
-
-  type
-    MyEnum = enum
-      enumA, enumB, enumC, enumD, enumE
-
-  proc vm() =
-    var instructions: array[0..100, MyEnum]
-    instructions[2] = enumC
-    instructions[3] = enumD
-    instructions[4] = enumA
-    instructions[5] = enumD
-    instructions[6] = enumC
-    instructions[7] = enumA
-    instructions[8] = enumB
-
-    instructions[12] = enumE
-    var pc = 0
-    while true:
-      {.computedGoto.}
-      let instr = instructions[pc]
-      case instr
-      of enumA:
-        echo "yeah A"
-      of enumC, enumD:
-        echo "yeah CD"
-      of enumB:
-        echo "yeah B"
-      of enumE:
-        break
-      inc(pc)
-
-  vm()
-
-As the example shows ``computedGoto`` is mostly useful for interpreters. If
-the underlying backend (C compiler) does not support the computed goto
-extension the pragma is simply ignored.
-
-
-unroll pragma
--------------
-The ``unroll`` pragma can be used to tell the compiler that it should unroll
-a `for`:idx: or `while`:idx: loop for runtime efficiency:
-
-.. code-block:: nim
-  proc searchChar(s: string, c: char): int =
-    for i in 0 .. s.high:
-      {.unroll: 4.}
-      if s[i] == c: return i
-    result = -1
-
-In the above example, the search loop is unrolled by a factor 4. The unroll
-factor can be left out too; the compiler then chooses an appropriate unroll
-factor.
-
-**Note**: Currently the compiler recognizes but ignores this pragma.
-
-
-immediate pragma
-----------------
-
-The immediate pragma is obsolete. See `Typed vs untyped parameters`_.
-
-
-compilation option pragmas
---------------------------
-The listed pragmas here can be used to override the code generation options
-for a proc/method/converter.
-
-The implementation currently provides the following possible options (various
-others may be added later).
-
-===============  ===============  ============================================
-pragma           allowed values   description
-===============  ===============  ============================================
-checks           on|off           Turns the code generation for all runtime
-                                  checks on or off.
-boundChecks      on|off           Turns the code generation for array bound
-                                  checks on or off.
-overflowChecks   on|off           Turns the code generation for over- or
-                                  underflow checks on or off.
-nilChecks        on|off           Turns the code generation for nil pointer
-                                  checks on or off.
-assertions       on|off           Turns the code generation for assertions
-                                  on or off.
-warnings         on|off           Turns the warning messages of the compiler
-                                  on or off.
-hints            on|off           Turns the hint messages of the compiler
-                                  on or off.
-optimization     none|speed|size  Optimize the code for speed or size, or
-                                  disable optimization.
-patterns         on|off           Turns the term rewriting templates/macros
-                                  on or off.
-callconv         cdecl|...        Specifies the default calling convention for
-                                  all procedures (and procedure types) that
-                                  follow.
-===============  ===============  ============================================
-
-Example:
-
-.. code-block:: nim
-  {.checks: off, optimization: speed.}
-  # compile without runtime checks and optimize for speed
-
-
-push and pop pragmas
---------------------
-The `push/pop`:idx: pragmas are very similar to the option directive,
-but are used to override the settings temporarily. Example:
-
-.. code-block:: nim
-  {.push checks: off.}
-  # compile this section without runtime checks as it is
-  # speed critical
-  # ... some code ...
-  {.pop.} # restore old settings
-
-
-register pragma
----------------
-The ``register`` pragma is for variables only. It declares the variable as
-``register``, giving the compiler a hint that the variable should be placed
-in a hardware register for faster access. C compilers usually ignore this
-though and for good reasons: Often they do a better job without it anyway.
-
-In highly specific cases (a dispatch loop of a bytecode interpreter for
-example) it may provide benefits, though.
-
-
-global pragma
--------------
-The ``global`` pragma can be applied to a variable within a proc to instruct
-the compiler to store it in a global location and initialize it once at program
-startup.
-
-.. code-block:: nim
-  proc isHexNumber(s: string): bool =
-    var pattern {.global.} = re"[0-9a-fA-F]+"
-    result = s.match(pattern)
-
-When used within a generic proc, a separate unique global variable will be
-created for each instantiation of the proc. The order of initialization of
-the created global variables within a module is not defined, but all of them
-will be initialized after any top-level variables in their originating module
-and before any variable in a module that imports it.
-
-
-..
-  NoForward pragma
-  ----------------
-  The ``noforward`` pragma can be used to turn on and off a special compilation
-  mode that to large extent eliminates the need for forward declarations. In this
-  mode, the proc definitions may appear out of order and the compiler will postpone
-  their semantic analysis and compilation until it actually needs to generate code
-  using the definitions. In this regard, this mode is similar to the modus operandi
-  of dynamic scripting languages, where the function calls are not resolved until
-  the code is executed. Here is the detailed algorithm taken by the compiler:
-
-  1. When a callable symbol is first encountered, the compiler will only note the
-  symbol callable name and it will add it to the appropriate overload set in the
-  current scope. At this step, it won't try to resolve any of the type expressions
-  used in the signature of the symbol (so they can refer to other not yet defined
-  symbols).
-
-  2. When a top level call is encountered (usually at the very end of the module),
-  the compiler will try to determine the actual types of all of the symbols in the
-  matching overload set. This is a potentially recursive process as the signatures
-  of the symbols may include other call expressions, whose types will be resolved
-  at this point too.
-
-  3. Finally, after the best overload is picked, the compiler will start
-  compiling the body of the respective symbol. This in turn will lead the
-  compiler to discover more call expressions that need to be resolved and steps
-  2 and 3 will be repeated as necessary.
-
-  Please note that if a callable symbol is never used in this scenario, its body
-  will never be compiled. This is the default behavior leading to best compilation
-  times, but if exhaustive compilation of all definitions is required, using
-  ``nim check`` provides this option as well.
-
-  Example:
-
-  .. code-block:: nim
-
-    {.noforward: on.}
-
-    proc foo(x: int) =
-      bar x
-
-    proc bar(x: int) =
-      echo x
-
-    foo(10)
-
-
-pragma pragma
--------------
-
-The ``pragma`` pragma can be used to declare user defined pragmas. This is
-useful because Nim's templates and macros do not affect pragmas. User
-defined pragmas are in a different module-wide scope than all other symbols.
-They cannot be imported from a module.
-
-Example:
-
-.. code-block:: nim
-  when appType == "lib":
-    {.pragma: rtl, exportc, dynlib, cdecl.}
-  else:
-    {.pragma: rtl, importc, dynlib: "client.dll", cdecl.}
-
-  proc p*(a, b: int): int {.rtl.} =
-    result = a+b
-
-In the example a new pragma named ``rtl`` is introduced that either imports
-a symbol from a dynamic library or exports the symbol for dynamic library
-generation.
-
-
-Disabling certain messages
---------------------------
-Nim generates some warnings and hints ("line too long") that may annoy the
-user. A mechanism for disabling certain messages is provided: Each hint
-and warning message contains a symbol in brackets. This is the message's
-identifier that can be used to enable or disable it:
-
-.. code-block:: Nim
-  {.hint[LineTooLong]: off.} # turn off the hint about too long lines
-
-This is often better than disabling all warnings at once.
-
-
-used pragma
------------
-
-Nim produces a warning for symbols that are not exported and not used either.
-The ``used`` pragma can be attached to a symbol to suppress this warning. This
-is particularly useful when the symbol was generated by a macro:
-
-.. code-block:: nim
-  template implementArithOps(T) =
-    proc echoAdd(a, b: T) {.used.} =
-      echo a + b
-    proc echoSub(a, b: T) {.used.} =
-      echo a - b
-
-  # no warning produced for the unused 'echoSub'
-  implementArithOps(int)
-  echoAdd 3, 5
-
-
-
-experimental pragma
--------------------
-
-The ``experimental`` pragma enables experimental language features. Depending
-on the concrete feature this means that the feature is either considered
-too unstable for an otherwise stable release or that the future of the feature
-is uncertain (it may be removed any time).
-
-Example:
-
-.. code-block:: nim
-  {.experimental: "parallel".}
-
-  proc useUsing(bar, foo) =
-    parallel:
-      for i in 0..4:
-        echo "echo in parallel"
-
-
-Implementation Specific Pragmas
-===============================
-
-This section describes additional pragmas that the current Nim implementation
-supports but which should not be seen as part of the language specification.
-
-Bitsize pragma
---------------
-
-The ``bitsize`` pragma is for object field members. It declares the field as
-a bitfield in C/C++.
-
-.. code-block:: Nim
-  type
-    mybitfield = object
-      flag {.bitsize:1.}: cuint
-
-generates:
-
-.. code-block:: C
-  struct mybitfield {
-    unsigned int flag:1;
-  };
-
-
-Volatile pragma
----------------
-The ``volatile`` pragma is for variables only. It declares the variable as
-``volatile``, whatever that means in C/C++ (its semantics are not well defined
-in C/C++).
-
-**Note**: This pragma will not exist for the LLVM backend.
-
-
-NoDecl pragma
--------------
-The ``noDecl`` pragma can be applied to almost any symbol (variable, proc,
-type, etc.) and is sometimes useful for interoperability with C:
-It tells Nim that it should not generate a declaration for the symbol in
-the C code. For example:
-
-.. code-block:: Nim
-  var
-    EACCES {.importc, noDecl.}: cint # pretend EACCES was a variable, as
-                                     # Nim does not know its value
-
-However, the ``header`` pragma is often the better alternative.
-
-**Note**: This will not work for the LLVM backend.
-
-
-Header pragma
--------------
-The ``header`` pragma is very similar to the ``noDecl`` pragma: It can be
-applied to almost any symbol and specifies that it should not be declared
-and instead the generated code should contain an ``#include``:
-
-.. code-block:: Nim
-  type
-    PFile {.importc: "FILE*", header: "<stdio.h>".} = distinct pointer
-      # import C's FILE* type; Nim will treat it as a new pointer type
-
-The ``header`` pragma always expects a string constant. The string contant
-contains the header file: As usual for C, a system header file is enclosed
-in angle brackets: ``<>``. If no angle brackets are given, Nim
-encloses the header file in ``""`` in the generated C code.
-
-**Note**: This will not work for the LLVM backend.
-
-
-IncompleteStruct pragma
------------------------
-The ``incompleteStruct`` pragma tells the compiler to not use the
-underlying C ``struct`` in a ``sizeof`` expression:
-
-.. code-block:: Nim
-  type
-    DIR* {.importc: "DIR", header: "<dirent.h>",
-           pure, incompleteStruct.} = object
-
-
-Compile pragma
---------------
-The ``compile`` pragma can be used to compile and link a C/C++ source file
-with the project:
-
-.. code-block:: Nim
-  {.compile: "myfile.cpp".}
-
-**Note**: Nim computes a SHA1 checksum and only recompiles the file if it
-has changed. You can use the ``-f`` command line option to force recompilation
-of the file.
-
-
-Link pragma
------------
-The ``link`` pragma can be used to link an additional file with the project:
-
-.. code-block:: Nim
-  {.link: "myfile.o".}
-
-
-PassC pragma
-------------
-The ``passC`` pragma can be used to pass additional parameters to the C
-compiler like you would using the commandline switch ``--passC``:
-
-.. code-block:: Nim
-  {.passC: "-Wall -Werror".}
-
-Note that you can use ``gorge`` from the `system module <system.html>`_ to
-embed parameters from an external command at compile time:
-
-.. code-block:: Nim
-  {.passC: gorge("pkg-config --cflags sdl").}
-
-PassL pragma
-------------
-The ``passL`` pragma can be used to pass additional parameters to the linker
-like you would using the commandline switch ``--passL``:
-
-.. code-block:: Nim
-  {.passL: "-lSDLmain -lSDL".}
-
-Note that you can use ``gorge`` from the `system module <system.html>`_ to
-embed parameters from an external command at compile time:
-
-.. code-block:: Nim
-  {.passL: gorge("pkg-config --libs sdl").}
-
-
-Emit pragma
------------
-The ``emit`` pragma can be used to directly affect the output of the
-compiler's code generator. So it makes your code unportable to other code
-generators/backends. Its usage is highly discouraged! However, it can be
-extremely useful for interfacing with `C++`:idx: or `Objective C`:idx: code.
-
-Example:
-
-.. code-block:: Nim
-  {.emit: """
-  static int cvariable = 420;
-  """.}
-
-  {.push stackTrace:off.}
-  proc embedsC() =
-    var nimVar = 89
-    # access Nim symbols within an emit section outside of string literals:
-    {.emit: ["""fprintf(stdout, "%d\n", cvariable + (int)""", nimVar, ");"].}
-  {.pop.}
-
-  embedsC()
-
-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.
-
-For a toplevel emit statement the section where in the generated C/C++ file
-the code should be emitted can be influenced via the
-prefixes ``/*TYPESECTION*/`` or ``/*VARSECTION*/`` or ``/*INCLUDESECTION*/``:
-
-.. code-block:: Nim
-  {.emit: """/*TYPESECTION*/
-  struct Vector3 {
-  public:
-    Vector3(): x(5) {}
-    Vector3(float x_): x(x_) {}
-    float x;
-  };
-  """.}
-
-  type Vector3 {.importcpp: "Vector3", nodecl} = object
-    x: cfloat
-
-  proc constructVector3(a: cfloat): Vector3 {.importcpp: "Vector3(@)", nodecl}
-
-
-ImportCpp pragma
-----------------
-
-**Note**: `c2nim <c2nim.html>`_ can parse a large subset of C++ and knows
-about the ``importcpp`` pragma pattern language. It is not necessary
-to know all the details described here.
-
-
-Similar to the `importc pragma for C
-<#foreign-function-interface-importc-pragma>`_, the
-``importcpp`` pragma can be used to import `C++`:idx: methods or C++ symbols
-in general. The generated code then uses the C++ method calling
-syntax: ``obj->method(arg)``.  In combination with the ``header`` and ``emit``
-pragmas this allows *sloppy* interfacing with libraries written in C++:
-
-.. code-block:: Nim
-  # Horrible example of how to interface with a C++ engine ... ;-)
-
-  {.link: "/usr/lib/libIrrlicht.so".}
-
-  {.emit: """
-  using namespace irr;
-  using namespace core;
-  using namespace scene;
-  using namespace video;
-  using namespace io;
-  using namespace gui;
-  """.}
-
-  const
-    irr = "<irrlicht/irrlicht.h>"
-
-  type
-    IrrlichtDeviceObj {.header: irr,
-                        importcpp: "IrrlichtDevice".} = object
-    IrrlichtDevice = ptr IrrlichtDeviceObj
-
-  proc createDevice(): IrrlichtDevice {.
-    header: irr, importcpp: "createDevice(@)".}
-  proc run(device: IrrlichtDevice): bool {.
-    header: irr, importcpp: "#.run(@)".}
-
-The compiler needs to be told to generate C++ (command ``cpp``) for
-this to work. The conditional symbol ``cpp`` is defined when the compiler
-emits C++ code.
-
-
-Namespaces
-~~~~~~~~~~
-
-The *sloppy interfacing* example uses ``.emit`` to produce ``using namespace``
-declarations. It is usually much better to instead refer to the imported name
-via the ``namespace::identifier`` notation:
-
-.. code-block:: nim
-  type
-    IrrlichtDeviceObj {.header: irr,
-                        importcpp: "irr::IrrlichtDevice".} = object
-
-
-Importcpp for enums
-~~~~~~~~~~~~~~~~~~~
-
-When ``importcpp`` is applied to an enum type the numerical enum values are
-annotated with the C++ enum type, like in this example: ``((TheCppEnum)(3))``.
-(This turned out to be the simplest way to implement it.)
-
-
-Importcpp for procs
-~~~~~~~~~~~~~~~~~~~
-
-Note that the ``importcpp`` variant for procs uses a somewhat cryptic pattern
-language for maximum flexibility:
-
-- A hash ``#`` symbol is replaced by the first or next argument.
-- A dot following the hash ``#.`` indicates that the call should use C++'s dot
-  or arrow notation.
-- An at symbol ``@`` is replaced by the remaining arguments, separated by
-  commas.
-
-For example:
-
-.. code-block:: nim
-  proc cppMethod(this: CppObj, a, b, c: cint) {.importcpp: "#.CppMethod(@)".}
-  var x: ptr CppObj
-  cppMethod(x[], 1, 2, 3)
-
-Produces:
-
-.. code-block:: C
-  x->CppMethod(1, 2, 3)
-
-As a special rule to keep backwards compatibility with older versions of the
-``importcpp`` pragma, if there is no special pattern
-character (any of ``# ' @``) at all, C++'s
-dot or arrow notation is assumed, so the above example can also be written as:
-
-.. code-block:: nim
-  proc cppMethod(this: CppObj, a, b, c: cint) {.importcpp: "CppMethod".}
-
-Note that the pattern language naturally also covers C++'s operator overloading
-capabilities:
-
-.. code-block:: nim
-  proc vectorAddition(a, b: Vec3): Vec3 {.importcpp: "# + #".}
-  proc dictLookup(a: Dict, k: Key): Value {.importcpp: "#[#]".}
-
-
-- An apostrophe ``'`` followed by an integer ``i`` in the range 0..9
-  is replaced by the i'th parameter *type*. The 0th position is the result
-  type. This can be used to pass types to C++ function templates. Between
-  the ``'`` and the digit an asterisk can be used to get to the base type
-  of the type. (So it "takes away a star" from the type; ``T*`` becomes ``T``.)
-  Two stars can be used to get to the element type of the element type etc.
-
-For example:
-
-.. code-block:: nim
-
-  type Input {.importcpp: "System::Input".} = object
-  proc getSubsystem*[T](): ptr T {.importcpp: "SystemManager::getSubsystem<'*0>()", nodecl.}
-
-  let x: ptr Input = getSubsystem[Input]()
-
-Produces:
-
-.. code-block:: C
-  x = SystemManager::getSubsystem<System::Input>()
-
-
-- ``#@`` is a special case to support a ``cnew`` operation. It is required so
-  that the call expression is inlined directly, without going through a
-  temporary location. This is only required to circumvent a limitation of the
-  current code generator.
-
-For example C++'s ``new`` operator can be "imported" like this:
-
-.. code-block:: nim
-  proc cnew*[T](x: T): ptr T {.importcpp: "(new '*0#@)", nodecl.}
-
-  # constructor of 'Foo':
-  proc constructFoo(a, b: cint): Foo {.importcpp: "Foo(@)".}
-
-  let x = cnew constructFoo(3, 4)
-
-Produces:
-
-.. code-block:: C
-  x = new Foo(3, 4)
-
-However, depending on the use case ``new Foo`` can also be wrapped like this
-instead:
-
-.. code-block:: nim
-  proc newFoo(a, b: cint): ptr Foo {.importcpp: "new Foo(@)".}
-
-  let x = newFoo(3, 4)
-
-
-Wrapping constructors
-~~~~~~~~~~~~~~~~~~~~~
-
-Sometimes a C++ class has a private copy constructor and so code like
-``Class c = Class(1,2);`` must not be generated but instead ``Class c(1,2);``.
-For this purpose the Nim proc that wraps a C++ constructor needs to be
-annotated with the `constructor`:idx: pragma. This pragma also helps to generate
-faster C++ code since construction then doesn't invoke the copy constructor:
-
-.. code-block:: nim
-  # a better constructor of 'Foo':
-  proc constructFoo(a, b: cint): Foo {.importcpp: "Foo(@)", constructor.}
-
-
-Wrapping destructors
-~~~~~~~~~~~~~~~~~~~~
-
-Since Nim generates C++ directly, any destructor is called implicitly by the
-C++ compiler at the scope exits. This means that often one can get away with
-not wrapping the destructor at all! However when it needs to be invoked
-explicitly, it needs to be wrapped. The pattern language provides
-everything that is required:
-
-.. code-block:: nim
-  proc destroyFoo(this: var Foo) {.importcpp: "#.~Foo()".}
-
-
-Importcpp for objects
-~~~~~~~~~~~~~~~~~~~~~
-
-Generic ``importcpp``'ed objects are mapped to C++ templates. This means that
-you can import C++'s templates rather easily without the need for a pattern
-language for object types:
-
-.. code-block:: nim
-  type
-    StdMap {.importcpp: "std::map", header: "<map>".} [K, V] = object
-  proc `[]=`[K, V](this: var StdMap[K, V]; key: K; val: V) {.
-    importcpp: "#[#] = #", header: "<map>".}
-
-  var x: StdMap[cint, cdouble]
-  x[6] = 91.4
-
-
-Produces:
-
-.. code-block:: C
-  std::map<int, double> x;
-  x[6] = 91.4;
-
-
-- If more precise control is needed, the apostrophe ``'`` can be used in the
-  supplied pattern to denote the concrete type parameters of the generic type.
-  See the usage of the apostrophe operator in proc patterns for more details.
-
-.. code-block:: nim
-
-  type
-    VectorIterator {.importcpp: "std::vector<'0>::iterator".} [T] = object
-
-  var x: VectorIterator[cint]
-
-
-Produces:
-
-.. code-block:: C
-
-  std::vector<int>::iterator x;
-
-
-ImportObjC pragma
------------------
-Similar to the `importc pragma for C
-<#foreign-function-interface-importc-pragma>`_, the ``importobjc`` pragma can
-be used to import `Objective C`:idx: methods.  The generated code then uses the
-Objective C method calling syntax: ``[obj method param1: arg]``.
-In addition with the ``header`` and ``emit`` pragmas this
-allows *sloppy* interfacing with libraries written in Objective C:
-
-.. code-block:: Nim
-  # horrible example of how to interface with GNUStep ...
-
-  {.passL: "-lobjc".}
-  {.emit: """
-  #include <objc/Object.h>
-  @interface Greeter:Object
-  {
-  }
-
-  - (void)greet:(long)x y:(long)dummy;
-  @end
-
-  #include <stdio.h>
-  @implementation Greeter
-
-  - (void)greet:(long)x y:(long)dummy
-  {
-    printf("Hello, World!\n");
-  }
-  @end
-
-  #include <stdlib.h>
-  """.}
-
-  type
-    Id {.importc: "id", header: "<objc/Object.h>", final.} = distinct int
-
-  proc newGreeter: Id {.importobjc: "Greeter new", nodecl.}
-  proc greet(self: Id, x, y: int) {.importobjc: "greet", nodecl.}
-  proc free(self: Id) {.importobjc: "free", nodecl.}
-
-  var g = newGreeter()
-  g.greet(12, 34)
-  g.free()
-
-The compiler needs to be told to generate Objective C (command ``objc``) for
-this to work. The conditional symbol ``objc`` is defined when the compiler
-emits Objective C code.
-
-
-CodegenDecl pragma
-------------------
-
-The ``codegenDecl`` pragma can be used to directly influence Nim's code
-generator. It receives a format string that determines how the variable
-or proc is declared in the generated code.
-
-For variables $1 in the format string represents the type of the variable
-and $2 is the name of the variable.
-
-The following Nim code:
-
-.. code-block:: nim
-  var
-    a {.codegenDecl: "$# progmem $#".}: int
-
-will generate this C code:
-
-.. code-block:: c
-  int progmem a
-
-For procedures $1 is the return type of the procedure, $2 is the name of
-the procedure and $3 is the parameter list.
-
-The following nim code:
-
-.. code-block:: nim
-  proc myinterrupt() {.codegenDecl: "__interrupt $# $#$#".} =
-    echo "realistic interrupt handler"
-
-will generate this code:
-
-.. code-block:: c
-  __interrupt void myinterrupt()
-
-
-InjectStmt pragma
------------------
-
-The ``injectStmt`` pragma can be used to inject a statement before every
-other statement in the current module. It is only supposed to be used for
-debugging:
-
-.. code-block:: nim
-  {.injectStmt: gcInvariants().}
-
-  # ... complex code here that produces crashes ...
-
-compile time define pragmas
----------------------------
-
-The pragmas listed here can be used to optionally accept values from
-the -d/--define option at compile time.
-
-The implementation currently provides the following possible options (various
-others may be added later).
-
-=================  ============================================
-pragma             description
-=================  ============================================
-`intdefine`:idx:   Reads in a build-time define as an integer
-`strdefine`:idx:   Reads in a build-time define as a string
-=================  ============================================
-
-.. code-block:: nim
-   const FooBar {.intdefine.}: int = 5
-   echo FooBar
-
-::
-   nim c -d:FooBar=42 foobar.c
-
-In the above example, providing the -d flag causes the symbol
-``FooBar`` to be overwritten at compile time, printing out 42. If the
-``-d:FooBar=42`` were to be omitted, the default value of 5 would be
-used.
-
-
-Custom annotations
-------------------
-It is possible to define custom typed pragmas. Custom pragmas do not effect
-code generation directly, but their presence can be detected by macros.
-Custom pragmas are defined using templates annotated with pragma ``pragma``:
-
-.. code-block:: nim
-  template dbTable(name: string, table_space: string = "") {.pragma.}
-  template dbKey(name: string = "", primary_key: bool = false) {.pragma.}
-  template dbForeignKey(t: typedesc) {.pragma.}
-  template dbIgnore {.pragma.}
-
-
-Consider stylized example of possible Object Relation Mapping (ORM) implementation:
-
-.. code-block:: nim
-  const tblspace {.strdefine.} = "dev" # switch for dev, test and prod environments
-
-  type
-    User {.dbTable("users", tblspace).} = object
-      id {.dbKey(primary_key = true).}: int
-      name {.dbKey"full_name".}: string
-      is_cached {.dbIgnore.}: bool
-      age: int
-
-    UserProfile {.dbTable("profiles", tblspace).} = object
-      id {.dbKey(primary_key = true).}: int
-      user_id {.dbForeignKey: User.}: int
-      read_access: bool
-      write_access: bool
-      admin_acess: bool
-
-In this example custom pragmas are used to describe how Nim objects are
-mapped to the schema of the relational database. Custom pragmas can have
-zero or more arguments. In order to pass multiple arguments use one of
-template call syntaxes. All arguments are typed and follow standard
-overload resolution rules for templates. Therefore, it is possible to have
-default values for arguments, pass by name, varargs, etc.
-
-Custom pragmas can be used in all locations where ordinary pragmas can be
-specified. It is possible to annotate procs, templates, type and variable
-definitions, statements, etc.
-
-Macros module includes helpers which can be used to simplify custom pragma
-access `hasCustomPragma`, `getCustomPragmaVal`. Please consult macros module
-documentation for details. These macros are no magic, they don't do anything
-you cannot do yourself by walking AST object representation.
-
-More examples with custom pragmas:
-  - Better serialization/deserialization control:
-
-  .. code-block:: nim
-    type MyObj = object
-      a {.dontSerialize.}: int
-      b {.defaultDeserialize: 5.}: int
-      c {.serializationKey: "_c".}: string
-
-  - Adopting type for gui inspector in a game engine:
-
-  .. code-block:: nim
-    type MyComponent = object
-      position {.editable, animatable.}: Vector3
-      alpha {.editRange: [0.0..1.0], animatable.}: float32
-
-
-
-
-Foreign function interface
-==========================
-
-Nim's `FFI`:idx: (foreign function interface) is extensive and only the
-parts that scale to other future backends (like the LLVM/JavaScript backends)
-are documented here.
-
-
-Importc pragma
---------------
-The ``importc`` pragma provides a means to import a proc or a variable
-from C. The optional argument is a string containing the C identifier. If
-the argument is missing, the C name is the Nim identifier *exactly as
-spelled*:
-
-.. code-block::
-  proc printf(formatstr: cstring) {.header: "<stdio.h>", importc: "printf", varargs.}
-
-Note that this pragma is somewhat of a misnomer: Other backends do provide
-the same feature under the same name. Also, if one is interfacing with C++
-the `ImportCpp pragma <manual.html#implementation-specific-pragmas-importcpp-pragma>`_ and
-interfacing with Objective-C the `ImportObjC pragma
-<manual.html#implementation-specific-pragmas-importobjc-pragma>`_ can be used.
-
-The string literal passed to ``importc`` can be a format string:
-
-.. code-block:: Nim
-  proc p(s: cstring) {.importc: "prefix$1".}
-
-In the example the external name of ``p`` is set to ``prefixp``. Only ``$1``
-is available and a literal dollar sign must be written as ``$$``.
-
-
-Exportc pragma
---------------
-The ``exportc`` pragma provides a means to export a type, a variable, or a
-procedure to C. Enums and constants can't be exported. The optional argument
-is a string containing the C identifier.  If the argument is missing, the C
-name is the Nim identifier *exactly as spelled*:
-
-.. code-block:: Nim
-  proc callme(formatstr: cstring) {.exportc: "callMe", varargs.}
-
-Note that this pragma is somewhat of a misnomer: Other backends do provide
-the same feature under the same name.
-
-The string literal passed to ``exportc`` can be a format string:
-
-.. code-block:: Nim
-  proc p(s: string) {.exportc: "prefix$1".} =
-    echo s
-
-In the example the external name of ``p`` is set to ``prefixp``. Only ``$1``
-is available and a literal dollar sign must be written as ``$$``.
-
-
-
-Extern pragma
--------------
-Like ``exportc`` or ``importc``, the ``extern`` pragma affects name
-mangling. The string literal passed to ``extern`` can be a format string:
-
-.. code-block:: Nim
-  proc p(s: string) {.extern: "prefix$1".} =
-    echo s
-
-In the example the external name of ``p`` is set to ``prefixp``. Only ``$1``
-is available and a literal dollar sign must be written as ``$$``.
-
-
-
-Bycopy pragma
--------------
-
-The ``bycopy`` pragma can be applied to an object or tuple type and
-instructs the compiler to pass the type by value to procs:
-
-.. code-block:: nim
-  type
-    Vector {.bycopy.} = object
-      x, y, z: float
-
-
-Byref pragma
-------------
-
-The ``byref`` pragma can be applied to an object or tuple type and instructs
-the compiler to pass the type by reference (hidden pointer) to procs.
-
-
-Varargs pragma
---------------
-The ``varargs`` pragma can be applied to procedures only (and procedure
-types). It tells Nim that the proc can take a variable number of parameters
-after the last specified parameter. Nim string values will be converted to C
-strings automatically:
-
-.. code-block:: Nim
-  proc printf(formatstr: cstring) {.nodecl, varargs.}
-
-  printf("hallo %s", "world") # "world" will be passed as C string
-
-
-Union pragma
-------------
-The ``union`` pragma can be applied to any ``object`` type. It means all
-of the object's fields are overlaid in memory. This produces a ``union``
-instead of a ``struct`` in the generated C/C++ code. The object declaration
-then must not use inheritance or any GC'ed memory but this is currently not
-checked.
-
-**Future directions**: GC'ed memory should be allowed in unions and the GC
-should scan unions conservatively.
-
-Packed pragma
--------------
-The ``packed`` pragma can be applied to any ``object`` type. It ensures
-that the fields of an object are packed back-to-back in memory. It is useful
-to store packets or messages from/to network or hardware drivers, and for
-interoperability with C. Combining packed pragma with inheritance is not
-defined, and it should not be used with GC'ed memory (ref's).
-
-**Future directions**: Using GC'ed memory in packed pragma will result in
-compile-time error. Usage with inheritance should be defined and documented.
-
-Unchecked pragma
-----------------
-The ``unchecked`` pragma can be used to mark a named array as ``unchecked``
-meaning its bounds are not checked. This is often useful to
-implement customized flexibly sized arrays. Additionally an unchecked array is
-translated into a C array of undetermined size:
-
-.. code-block:: nim
-  type
-    ArrayPart{.unchecked.} = array[0, int]
-    MySeq = object
-      len, cap: int
-      data: ArrayPart
-
-Produces roughly this C code:
-
-.. code-block:: C
-  typedef struct {
-    NI len;
-    NI cap;
-    NI data[];
-  } MySeq;
-
-The base type of the unchecked array may not contain any GC'ed memory but this
-is currently not checked.
-
-**Future directions**: GC'ed memory should be allowed in unchecked arrays and
-there should be an explicit annotation of how the GC is to determine the
-runtime size of the array.
-
-
-Dynlib pragma for import
-------------------------
-With the ``dynlib`` pragma a procedure or a variable can be imported from
-a dynamic library (``.dll`` files for Windows, ``lib*.so`` files for UNIX).
-The non-optional argument has to be the name of the dynamic library:
-
-.. code-block:: Nim
-  proc gtk_image_new(): PGtkWidget
-    {.cdecl, dynlib: "libgtk-x11-2.0.so", importc.}
-
-In general, importing a dynamic library does not require any special linker
-options or linking with import libraries. This also implies that no *devel*
-packages need to be installed.
-
-The ``dynlib`` import mechanism supports a versioning scheme:
-
-.. code-block:: nim
-  proc Tcl_Eval(interp: pTcl_Interp, script: cstring): int {.cdecl,
-    importc, dynlib: "libtcl(|8.5|8.4|8.3).so.(1|0)".}
-
-At runtime the dynamic library is searched for (in this order)::
-
-  libtcl.so.1
-  libtcl.so.0
-  libtcl8.5.so.1
-  libtcl8.5.so.0
-  libtcl8.4.so.1
-  libtcl8.4.so.0
-  libtcl8.3.so.1
-  libtcl8.3.so.0
-
-The ``dynlib`` pragma supports not only constant strings as argument but also
-string expressions in general:
-
-.. code-block:: nim
-  import os
-
-  proc getDllName: string =
-    result = "mylib.dll"
-    if existsFile(result): return
-    result = "mylib2.dll"
-    if existsFile(result): return
-    quit("could not load dynamic library")
-
-  proc myImport(s: cstring) {.cdecl, importc, dynlib: getDllName().}
-
-**Note**: Patterns like ``libtcl(|8.5|8.4).so`` are only supported in constant
-strings, because they are precompiled.
-
-**Note**: Passing variables to the ``dynlib`` pragma will fail at runtime
-because of order of initialization problems.
-
-**Note**: A ``dynlib`` import can be overridden with
-the ``--dynlibOverride:name`` command line option. The Compiler User Guide
-contains further information.
-
-
-Dynlib pragma for export
-------------------------
-
-With the ``dynlib`` pragma a procedure can also be exported to
-a dynamic library. The pragma then has no argument and has to be used in
-conjunction with the ``exportc`` pragma:
-
-.. code-block:: Nim
-  proc exportme(): int {.cdecl, exportc, dynlib.}
-
-This is only useful if the program is compiled as a dynamic library via the
-``--app:lib`` command line option. This pragma only has an effect for the code
-generation on the Windows target, so when this pragma is forgotten and the dynamic
-library is only tested on Mac and/or Linux, there won't be an error. On Windows
-this pragma adds ``__declspec(dllexport)`` to the function declaration.
-
-
-
-Threads
-=======
-
-To enable thread support the ``--threads:on`` command line switch needs to
-be used. The ``system`` module then contains several threading primitives.
-See the `threads <threads.html>`_ and `channels <channels.html>`_ modules
-for the low level thread API. There are also high level parallelism constructs
-available. See `spawn <#parallel-spawn>`_ for further details.
-
-Nim's memory model for threads is quite different than that of other common
-programming languages (C, Pascal, Java): Each thread has its own (garbage
-collected) heap and sharing of memory is restricted to global variables. This
-helps to prevent race conditions. GC efficiency is improved quite a lot,
-because the GC never has to stop other threads and see what they reference.
-Memory allocation requires no lock at all! This design easily scales to massive
-multicore processors that are becoming the norm.
-
-
-Thread pragma
--------------
-
-A proc that is executed as a new thread of execution should be marked by the
-``thread`` pragma for reasons of readability. The compiler checks for
-violations of the `no heap sharing restriction`:idx:\: This restriction implies
-that it is invalid to construct a data structure that consists of memory
-allocated from different (thread local) heaps.
-
-A thread proc is passed to ``createThread`` or ``spawn`` and invoked
-indirectly; so the ``thread`` pragma implies ``procvar``.
-
-
-GC safety
----------
-
-We call a proc ``p`` `GC safe`:idx: when it doesn't access any global variable
-that contains GC'ed memory (``string``, ``seq``, ``ref`` or a closure) either
-directly or indirectly through a call to a GC unsafe proc.
-
-The `gcsafe`:idx: annotation can be used to mark a proc to be gcsafe,
-otherwise this property is inferred by the compiler. Note that ``noSideEffect``
-implies ``gcsafe``. The only way to create a thread is via ``spawn`` or
-``createThread``. ``spawn`` is usually the preferable method. Either way
-the invoked proc must not use ``var`` parameters nor must any of its parameters
-contain a ``ref`` or ``closure`` type. This enforces
-the *no heap sharing restriction*.
-
-Routines that are imported from C are always assumed to be ``gcsafe``.
-To disable the GC-safety checking the ``--threadAnalysis:off`` command line
-switch can be used. This is a temporary workaround to ease the porting effort
-from old code to the new threading model.
-
-To override the compiler's gcsafety analysis a ``{.gcsafe.}`` pragma block can
-be used:
-
-.. code-block:: nim
-
-  var
-    someGlobal: string = "some string here"
-    perThread {.threadvar.}: string
-
-  proc setPerThread() =
-    {.gcsafe.}:
-      deepCopy(perThread, someGlobal)
-
-
-Future directions:
-
-- A shared GC'ed heap might be provided.
-
-
-Threadvar pragma
-----------------
-
-A variable can be marked with the ``threadvar`` pragma, which makes it a
-`thread-local`:idx: variable; Additionally, this implies all the effects
-of the ``global`` pragma.
-
-.. code-block:: nim
-  var checkpoints* {.threadvar.}: seq[string]
-
-Due to implementation restrictions thread local variables cannot be
-initialized within the ``var`` section. (Every thread local variable needs to
-be replicated at thread creation.)
-
-
-Threads and exceptions
-----------------------
-
-The interaction between threads and exceptions is simple: A *handled* exception
-in one thread cannot affect any other thread. However, an *unhandled* exception
-in one thread terminates the whole *process*!
-
-
-
-Parallel & Spawn
-================
-
-Nim has two flavors of parallelism:
-1) `Structured`:idx: parallelism via the ``parallel`` statement.
-2) `Unstructured`:idx: parallelism via the standalone ``spawn`` statement.
-
-Nim has a builtin thread pool that can be used for CPU intensive tasks. For
-IO intensive tasks the ``async`` and ``await`` features should be
-used instead. Both parallel and spawn need the `threadpool <threadpool.html>`_
-module to work.
-
-Somewhat confusingly, ``spawn`` is also used in the ``parallel`` statement
-with slightly different semantics. ``spawn`` always takes a call expression of
-the form ``f(a, ...)``. Let ``T`` be ``f``'s return type. If ``T`` is ``void``
-then ``spawn``'s return type is also ``void`` otherwise it is ``FlowVar[T]``.
-
-Within a ``parallel`` section sometimes the ``FlowVar[T]`` is eliminated
-to ``T``. This happens when ``T`` does not contain any GC'ed memory.
-The compiler can ensure the location in ``location = spawn f(...)`` is not
-read prematurely within a ``parallel`` section and so there is no need for
-the overhead of an indirection via ``FlowVar[T]`` to ensure correctness.
-
-**Note**: Currently exceptions are not propagated between ``spawn``'ed tasks!
-
-
-Spawn statement
----------------
-
-`spawn`:idx: can be used to pass a task to the thread pool:
-
-.. code-block:: nim
-  import threadpool
-
-  proc processLine(line: string) =
-    discard "do some heavy lifting here"
-
-  for x in lines("myinput.txt"):
-    spawn processLine(x)
-  sync()
-
-For reasons of type safety and implementation simplicity the expression
-that ``spawn`` takes is restricted:
-
-* It must be a call expression ``f(a, ...)``.
-* ``f`` must be ``gcsafe``.
-* ``f`` must not have the calling convention ``closure``.
-* ``f``'s parameters may not be of type ``var``.
-  This means one has to use raw ``ptr``'s for data passing reminding the
-  programmer to be careful.
-* ``ref`` parameters are deeply copied which is a subtle semantic change and
-  can cause performance problems but ensures memory safety. This deep copy
-  is performed via ``system.deepCopy`` and so can be overridden.
-* For *safe* data exchange between ``f`` and the caller a global ``TChannel``
-  needs to be used. However, since spawn can return a result, often no further
-  communication is required.
-
-
-``spawn`` executes the passed expression on the thread pool and returns
-a `data flow variable`:idx: ``FlowVar[T]`` that can be read from. The reading
-with the ``^`` operator is **blocking**. However, one can use ``awaitAny`` to
-wait on multiple flow variables at the same time:
-
-.. code-block:: nim
-  import threadpool, ...
-
-  # wait until 2 out of 3 servers received the update:
-  proc main =
-    var responses = newSeq[FlowVarBase](3)
-    for i in 0..2:
-      responses[i] = spawn tellServer(Update, "key", "value")
-    var index = awaitAny(responses)
-    assert index >= 0
-    responses.del(index)
-    discard awaitAny(responses)
-
-Data flow variables ensure that no data races
-are possible. Due to technical limitations not every type ``T`` is possible in
-a data flow variable: ``T`` has to be of the type ``ref``, ``string``, ``seq``
-or of a type that doesn't contain a type that is garbage collected. This
-restriction is not hard to work-around in practice.
-
-
-
-Parallel statement
-------------------
-
-Example:
-
-.. code-block:: nim
-    :test: "nim c --threads:on $1"
-
-  # Compute PI in an inefficient way
-  import strutils, math, threadpool
-  {.experimental: "parallel".}
-
-  proc term(k: float): float = 4 * math.pow(-1, k) / (2*k + 1)
-
-  proc pi(n: int): float =
-    var ch = newSeq[float](n+1)
-    parallel:
-      for k in 0..ch.high:
-        ch[k] = spawn term(float(k))
-    for k in 0..ch.high:
-      result += ch[k]
-
-  echo formatFloat(pi(5000))
-
-
-The parallel statement is the preferred mechanism to introduce parallelism
-in a Nim program. A subset of the Nim language is valid within a
-``parallel`` section. This subset is checked to be free of data races at
-compile time. A sophisticated `disjoint checker`:idx: ensures that no data
-races are possible even though shared memory is extensively supported!
-
-The subset is in fact the full language with the following
-restrictions / changes:
-
-* ``spawn`` within a ``parallel`` section has special semantics.
-* Every location of the form ``a[i]`` and ``a[i..j]`` and ``dest`` where
-  ``dest`` is part of the pattern ``dest = spawn f(...)`` has to be
-  provably disjoint. This is called the *disjoint check*.
-* Every other complex location ``loc`` that is used in a spawned
-  proc (``spawn f(loc)``) has to be immutable for the duration of
-  the ``parallel`` section. This is called the *immutability check*. Currently
-  it is not specified what exactly "complex location" means. We need to make
-  this an optimization!
-* Every array access has to be provably within bounds. This is called
-  the *bounds check*.
-* Slices are optimized so that no copy is performed. This optimization is not
-  yet performed for ordinary slices outside of a ``parallel`` section.
-
-
-Guards and locks
-================
-
-Apart from ``spawn`` and ``parallel`` Nim also provides all the common low level
-concurrency mechanisms like locks, atomic intrinsics or condition variables.
-
-Nim significantly improves on the safety of these features via additional
-pragmas:
-
-1) A `guard`:idx: annotation is introduced to prevent data races.
-2) Every access of a guarded memory location needs to happen in an
-   appropriate `locks`:idx: statement.
-3) Locks and routines can be annotated with `lock levels`:idx: to prevent
-   deadlocks at compile time.
-
-
-Guards and the locks section
-----------------------------
-
-Protecting global variables
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Object fields and global variables can be annotated via a ``guard`` pragma:
-
-.. code-block:: nim
-  var glock: TLock
-  var gdata {.guard: glock.}: int
-
-The compiler then ensures that every access of ``gdata`` is within a ``locks``
-section:
-
-.. code-block:: nim
-  proc invalid =
-    # invalid: unguarded access:
-    echo gdata
-
-  proc valid =
-    # valid access:
-    {.locks: [glock].}:
-      echo gdata
-
-Top level accesses to ``gdata`` are always allowed so that it can be initialized
-conveniently. It is *assumed* (but not enforced) that every top level statement
-is executed before any concurrent action happens.
-
-The ``locks`` section deliberately looks ugly because it has no runtime
-semantics and should not be used directly! It should only be used in templates
-that also implement some form of locking at runtime:
-
-.. code-block:: nim
-  template lock(a: TLock; body: untyped) =
-    pthread_mutex_lock(a)
-    {.locks: [a].}:
-      try:
-        body
-      finally:
-        pthread_mutex_unlock(a)
-
-
-The guard does not need to be of any particular type. It is flexible enough to
-model low level lockfree mechanisms:
-
-.. code-block:: nim
-  var dummyLock {.compileTime.}: int
-  var atomicCounter {.guard: dummyLock.}: int
-
-  template atomicRead(x): untyped =
-    {.locks: [dummyLock].}:
-      memoryReadBarrier()
-      x
-
-  echo atomicRead(atomicCounter)
-
-
-The ``locks`` pragma takes a list of lock expressions ``locks: [a, b, ...]``
-in order to support *multi lock* statements. Why these are essential is
-explained in the `lock levels <#guards-and-locks-lock-levels>`_ section.
-
-
-Protecting general locations
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The ``guard`` annotation can also be used to protect fields within an object.
-The guard then needs to be another field within the same object or a
-global variable.
-
-Since objects can reside on the heap or on the stack this greatly enhances the
-expressivity of the language:
-
-.. code-block:: nim
-  type
-    ProtectedCounter = object
-      v {.guard: L.}: int
-      L: TLock
-
-  proc incCounters(counters: var openArray[ProtectedCounter]) =
-    for i in 0..counters.high:
-      lock counters[i].L:
-        inc counters[i].v
-
-The access to field ``x.v`` is allowed since its guard ``x.L``  is active.
-After template expansion, this amounts to:
-
-.. code-block:: nim
-  proc incCounters(counters: var openArray[ProtectedCounter]) =
-    for i in 0..counters.high:
-      pthread_mutex_lock(counters[i].L)
-      {.locks: [counters[i].L].}:
-        try:
-          inc counters[i].v
-        finally:
-          pthread_mutex_unlock(counters[i].L)
-
-There is an analysis that checks that ``counters[i].L`` is the lock that
-corresponds to the protected location ``counters[i].v``. This analysis is called
-`path analysis`:idx: because it deals with paths to locations
-like ``obj.field[i].fieldB[j]``.
-
-The path analysis is **currently unsound**, but that doesn't make it useless.
-Two paths are considered equivalent if they are syntactically the same.
-
-This means the following compiles (for now) even though it really should not:
-
-.. code-block:: nim
-  {.locks: [a[i].L].}:
-    inc i
-    access a[i].v
-
-
-
-Lock levels
------------
-
-Lock levels are used to enforce a global locking order in order to prevent
-deadlocks at compile-time. A lock level is an constant integer in the range
-0..1_000. Lock level 0 means that no lock is acquired at all.
-
-If a section of code holds a lock of level ``M`` than it can also acquire any
-lock of level ``N < M``. Another lock of level ``M`` cannot be acquired. Locks
-of the same level can only be acquired *at the same time* within a
-single ``locks`` section:
-
-.. code-block:: nim
-  var a, b: TLock[2]
-  var x: TLock[1]
-  # invalid locking order: TLock[1] cannot be acquired before TLock[2]:
-  {.locks: [x].}:
-    {.locks: [a].}:
-      ...
-  # valid locking order: TLock[2] acquired before TLock[1]:
-  {.locks: [a].}:
-    {.locks: [x].}:
-      ...
-
-  # invalid locking order: TLock[2] acquired before TLock[2]:
-  {.locks: [a].}:
-    {.locks: [b].}:
-      ...
-
-  # valid locking order, locks of the same level acquired at the same time:
-  {.locks: [a, b].}:
-    ...
-
-
-Here is how a typical multilock statement can be implemented in Nim. Note how
-the runtime check is required to ensure a global ordering for two locks ``a``
-and ``b`` of the same lock level:
-
-.. code-block:: nim
-  template multilock(a, b: ptr TLock; body: untyped) =
-    if cast[ByteAddress](a) < cast[ByteAddress](b):
-      pthread_mutex_lock(a)
-      pthread_mutex_lock(b)
-    else:
-      pthread_mutex_lock(b)
-      pthread_mutex_lock(a)
-    {.locks: [a, b].}:
-      try:
-        body
-      finally:
-        pthread_mutex_unlock(a)
-        pthread_mutex_unlock(b)
-
-
-Whole routines can also be annotated with a ``locks`` pragma that takes a lock
-level. This then means that the routine may acquire locks of up to this level.
-This is essential so that procs can be called within a ``locks`` section:
-
-.. code-block:: nim
-  proc p() {.locks: 3.} = discard
-
-  var a: TLock[4]
-  {.locks: [a].}:
-    # p's locklevel (3) is strictly less than a's (4) so the call is allowed:
-    p()
-
-
-As usual ``locks`` is an inferred effect and there is a subtype
-relation: ``proc () {.locks: N.}`` is a subtype of ``proc () {.locks: M.}``
-iff (M <= N).
-
-The ``locks`` pragma can also take the special value ``"unknown"``. This
-is useful in the context of dynamic method dispatching. In the following
-example, the compiler can infer a lock level of 0 for the ``base`` case.
-However, one of the overloaded methods calls a procvar which is
-potentially locking. Thus, the lock level of calling ``g.testMethod``
-cannot be inferred statically, leading to compiler warnings. By using
-``{.locks: "unknown".}``, the base method can be marked explicitly as
-having unknown lock level as well:
-
-.. code-block:: nim
-  type SomeBase* = ref object of RootObj
-  type SomeDerived* = ref object of SomeBase
-    memberProc*: proc ()
-
-  method testMethod(g: SomeBase) {.base, locks: "unknown".} = discard
-  method testMethod(g: SomeDerived) =
-    if g.memberProc != nil:
-      g.memberProc()
-
-
-Taint mode
-==========
-
-The Nim compiler and most parts of the standard library support
-a taint mode. Input strings are declared with the `TaintedString`:idx:
-string type declared in the ``system`` module.
-
-If the taint mode is turned on (via the ``--taintMode:on`` command line
-option) it is a distinct string type which helps to detect input
-validation errors:
-
-.. code-block:: nim
-  echo "your name: "
-  var name: TaintedString = stdin.readline
-  # it is safe here to output the name without any input validation, so
-  # we simply convert `name` to string to make the compiler happy:
-  echo "hi, ", name.string
-
-If the taint mode is turned off, ``TaintedString`` is simply an alias for
-``string``.
diff --git a/doc/manual/var_t_return.md b/doc/manual/var_t_return.md
new file mode 100644
index 000000000..15d908c74
--- /dev/null
+++ b/doc/manual/var_t_return.md
@@ -0,0 +1,24 @@
+.. default-role:: code
+.. include:: ../rstcommon.rst
+
+Memory safety for returning by `var T` is ensured by a simple borrowing
+rule: If `result` does not refer to a location pointing to the heap
+(that is in `result = X` the `X` involves a `ptr` or `ref` access)
+then it has to be derived from the routine's first parameter:
+
+  ```nim
+  proc forward[T](x: var T): var T =
+    result = x # ok, derived from the first parameter.
+
+  proc p(param: var int): var int =
+    var x: int
+    # we know 'forward' provides a view into the location derived from
+    # its first argument 'x'.
+    result = forward(x) # Error: location is derived from `x`
+                        # which is not p's first parameter and lives
+                        # on the stack.
+  ```
+
+In other words, the lifetime of what `result` points to is attached to the
+lifetime of the first parameter and that is enough knowledge to verify
+memory safety at the call site.
diff --git a/doc/manual/var_t_return.rst b/doc/manual/var_t_return.rst
deleted file mode 100644
index b9ff1d892..000000000
--- a/doc/manual/var_t_return.rst
+++ /dev/null
@@ -1,20 +0,0 @@
-Memory safety for returning by ``var T`` is ensured by a simple borrowing
-rule: If ``result`` does not refer to a location pointing to the heap
-(that is in ``result = X`` the ``X`` involves a ``ptr`` or ``ref`` access)
-then it has to be deviated by the routine's first parameter:
-
-.. code-block:: nim
-  proc forward[T](x: var T): var T =
-    result = x # ok, deviated from the first parameter.
-
-  proc p(param: var int): var int =
-    var x: int
-    # we know 'forward' provides a view into the location deviated by
-    # its first argument 'x'.
-    result = forward(x) # Error: location is derived from ``x``
-                        # which is not p's first parameter and lives
-                        # on the stack.
-
-In other words, the lifetime of what ``result`` points to is attached to the
-lifetime of the first parameter and that is enough knowledge to verify
-memory safety at the callsite.
diff --git a/doc/manual_experimental.md b/doc/manual_experimental.md
new file mode 100644
index 000000000..da51d59ad
--- /dev/null
+++ b/doc/manual_experimental.md
@@ -0,0 +1,2669 @@
+=========================
+Nim Experimental Features
+=========================
+
+:Authors: Andreas Rumpf
+:Version: |nimversion|
+
+.. default-role:: code
+.. include:: rstcommon.rst
+.. contents::
+
+
+About this document
+===================
+
+This document describes features of Nim that are to be considered experimental.
+Some of these are not covered by the `.experimental` pragma or
+`--experimental`:option: switch because they are already behind a special syntax and
+one may want to use Nim libraries using these features without using them
+oneself.
+
+.. note:: Unless otherwise indicated, these features are not to be removed,
+  but refined and overhauled.
+
+
+Void type
+=========
+
+The `void` type denotes the absence of any value, i.e. it is the type that contains no values. Consequently, no value can be provided for parameters of
+type `void`, and no value can be returned from a function with return type `void`:
+
+  ```nim
+  proc nothing(x, y: void): void =
+    echo "ha"
+
+  nothing() # writes "ha" to stdout
+  ```
+
+The `void` type is particularly useful for generic code:
+
+  ```nim
+  proc callProc[T](p: proc (x: T), x: T) =
+    when T is void:
+      p()
+    else:
+      p(x)
+
+  proc intProc(x: int) = discard
+  proc emptyProc() = discard
+
+  callProc[int](intProc, 12)
+  callProc[void](emptyProc)
+  ```
+
+However, a `void` type cannot be inferred in generic code:
+
+  ```nim
+  callProc(emptyProc)
+  # Error: type mismatch: got (proc ())
+  # but expected one of:
+  # callProc(p: proc (T), x: T)
+  ```
+
+The `void` type is only valid for parameters and return types; other symbols
+cannot have the type `void`.
+
+Generic `define` pragma
+=======================
+
+Aside the [typed define pragmas for constants](manual.html#implementation-specific-pragmas-compileminustime-define-pragmas),
+there is a generic `{.define.}` pragma that interprets the value of the define
+based on the type of the constant value.
+
+  ```nim
+  const foo {.define: "package.foo".} = 123
+  const bar {.define: "package.bar".} = false
+  ```
+
+  ```cmd
+  nim c -d:package.foo=456 -d:package.bar foobar.nim
+  ```
+
+The following types are supported:
+
+* `string` and `cstring`
+* Signed and unsigned integer types
+* `bool`
+* Enums
+
+Top-down type inference
+=======================
+
+In expressions such as:
+
+```nim
+let a: T = ex
+```
+
+Normally, the compiler type checks the expression `ex` by itself, then
+attempts to statically convert the type-checked expression to the given type
+`T` as much as it can, while making sure it matches the type. The extent of
+this process is limited however due to the expression usually having
+an assumed type that might clash with the given type.
+
+With top-down type inference, the expression is type checked with the
+extra knowledge that it is supposed to be of type `T`. For example,
+the following code is does not compile with the former method, but
+compiles with top-down type inference:
+
+```nim
+let foo: (float, uint8, cstring) = (1, 2, "abc")
+```
+
+The tuple expression has an expected type of `(float, uint8, cstring)`.
+Since it is a tuple literal, we can use this information to assume the types
+of its elements. The expected types for the expressions `1`, `2` and `"abc"`
+are respectively `float`, `uint8`, and `cstring`; and these expressions can be
+statically converted to these types.
+
+Without this information, the type of the tuple expression would have been
+assumed to be `(int, int, string)`. Thus the type of the tuple expression
+would not match the type of the variable, and an error would be given.
+
+The extent of this varies, but there are some notable special cases.
+
+
+Inferred generic parameters
+---------------------------
+
+In expressions making use of generic procs or templates, the expected
+(unbound) types are often able to be inferred based on context.
+This feature has to be enabled via `{.experimental: "inferGenericTypes".}`
+
+  ```nim  test = "nim c $1"
+  {.experimental: "inferGenericTypes".}
+
+  import std/options
+
+  var x = newSeq[int](1)
+  # Do some work on 'x'...
+
+  # Works!
+  # 'x' is 'seq[int]' so 'newSeq[int]' is implied
+  x = newSeq(10)
+
+  # Works!
+  # 'T' of 'none' is bound to the 'T' of 'noneProducer', passing it along.
+  # Effectively 'none.T = noneProducer.T'
+  proc noneProducer[T](): Option[T] = none()
+  let myNone = noneProducer[int]()
+
+  # Also works
+  # 'myOtherNone' binds its 'T' to 'float' and 'noneProducer' inherits it
+  # noneProducer.T = myOtherNone.T
+  let myOtherNone: Option[float] = noneProducer()
+
+  # Works as well
+  # none.T = myOtherOtherNone.T
+  let myOtherOtherNone: Option[int] = none()
+  ```
+
+This is achieved by reducing the types on the lhs and rhs until the *lhs* is left with only types such as `T`.
+While lhs and rhs are reduced together, this does *not* mean that the *rhs* will also only be left
+with a flat type `Z`, it may be of the form `MyType[Z]`.
+
+After the types have been reduced, the types `T` are bound to the types that are left on the rhs.
+
+If bindings *cannot be inferred*, compilation will fail and manual specification is required.
+
+An example for *failing inference* can be found when passing a generic expression
+to a function/template call:
+
+  ```nim  test = "nim c $1"  status = 1
+  {.experimental: "inferGenericTypes".}
+
+  proc myProc[T](a, b: T) = discard
+
+  # Fails! Unable to infer that 'T' is supposed to be 'int'
+  myProc(newSeq[int](), newSeq(1))
+
+  # Works! Manual specification of 'T' as 'int' necessary
+  myProc(newSeq[int](), newSeq[int](1))
+  ```
+
+Combination of generic inference with the `auto` type is also unsupported:
+
+  ```nim  test = "nim c $1"  status = 1
+  {.experimental: "inferGenericTypes".}
+
+  proc produceValue[T]: auto = default(T)
+  let a: int = produceValue() # 'auto' cannot be inferred here
+  ```
+
+**Note**: The described inference does not permit the creation of overrides based on
+the return type of a procedure. It is a mapping mechanism that does not attempt to 
+perform deeper inference, nor does it modify what is a valid override.
+
+  ```nim  test = "nim c $1"  status = 1
+  # Doesn't affect the following code, it is invalid either way
+  {.experimental: "inferGenericTypes".}
+
+  proc a: int = 0
+  proc a: float = 1.0 # Fails! Invalid code and not recommended
+  ```
+
+
+Sequence literals
+-----------------
+
+Top-down type inference applies to sequence literals.
+
+```nim
+let x: seq[seq[float]] = @[@[1, 2, 3], @[4, 5, 6]]
+```
+
+This behavior is tied to the `@` overloads in the `system` module,
+so overloading `@` can disable this behavior. This can be circumvented by
+specifying the `` system.`@` `` overload.
+
+```nim
+proc `@`(x: string): string = "@" & x
+
+# does not compile:
+let x: seq[float] = @[1, 2, 3]
+# compiles:
+let x: seq[float] = system.`@`([1, 2, 3])
+```
+
+
+Package level objects
+=====================
+
+Every Nim module resides in a (nimble) package. An object type can be attached
+to the package it resides in. If that is done, the type can be referenced from
+other modules as an `incomplete`:idx: object type. This feature allows to
+break up recursive type dependencies across module boundaries. Incomplete
+object types are always passed `byref` and can only be used in pointer like
+contexts (`var/ref/ptr IncompleteObject`) in general, since the compiler does
+not yet know the size of the object. To complete an incomplete object,
+the `package` pragma has to be used. `package` implies `byref`.
+
+As long as a type `T` is incomplete, no runtime type information for `T` is
+available.
+
+
+Example:
+
+  ```nim
+  # module A (in an arbitrary package)
+  type
+    Pack.SomeObject = object # declare as incomplete object of package 'Pack'
+    Triple = object
+      a, b, c: ref SomeObject # pointers to incomplete objects are allowed
+
+  # Incomplete objects can be used as parameters:
+  proc myproc(x: SomeObject) = discard
+  ```
+
+
+  ```nim
+  # module B (in package "Pack")
+  type
+    SomeObject* {.package.} = object # Use 'package' to complete the object
+      s, t: string
+      x, y: int
+  ```
+
+This feature will likely be superseded in the future by support for
+recursive module dependencies.
+
+
+Importing private symbols
+=========================
+
+In some situations, it may be useful to import all symbols (public or private)
+from a module. The syntax `import foo {.all.}` can be used to import all
+symbols from the module `foo`. Note that importing private symbols is
+generally not recommended.
+
+See also the experimental [importutils](importutils.html) module.
+
+
+Code reordering
+===============
+
+The code reordering feature can implicitly rearrange procedure, template, and
+macro definitions along with variable declarations and initializations at the top
+level scope so that, to a large extent, a programmer should not have to worry
+about ordering definitions correctly or be forced to use forward declarations to
+preface definitions inside a module.
+
+..
+   NOTE: The following was documentation for the code reordering precursor,
+   which was {.noForward.}.
+
+   In this mode, procedure definitions may appear out of order and the compiler
+   will postpone their semantic analysis and compilation until it actually needs
+   to generate code using the definitions. In this regard, this mode is similar
+   to the modus operandi of dynamic scripting languages, where the function
+   calls are not resolved until the code is executed. Here is the detailed
+   algorithm taken by the compiler:
+
+   1. When a callable symbol is first encountered, the compiler will only note
+   the symbol callable name and it will add it to the appropriate overload set
+   in the current scope. At this step, it won't try to resolve any of the type
+   expressions used in the signature of the symbol (so they can refer to other
+   not yet defined symbols).
+
+   2. When a top level call is encountered (usually at the very end of the
+   module), the compiler will try to determine the actual types of all of the
+   symbols in the matching overload set. This is a potentially recursive process
+   as the signatures of the symbols may include other call expressions, whose
+   types will be resolved at this point too.
+
+   3. Finally, after the best overload is picked, the compiler will start
+   compiling the body of the respective symbol. This in turn will lead the
+   compiler to discover more call expressions that need to be resolved and steps
+   2 and 3 will be repeated as necessary.
+
+   Please note that if a callable symbol is never used in this scenario, its
+   body will never be compiled. This is the default behavior leading to best
+   compilation times, but if exhaustive compilation of all definitions is
+   required, using `nim check` provides this option as well.
+
+Example:
+
+  ```nim
+  {.experimental: "codeReordering".}
+
+  proc foo(x: int) =
+    bar(x)
+
+  proc bar(x: int) =
+    echo(x)
+
+  foo(10)
+  ```
+
+Variables can also be reordered as well. Variables that are *initialized* (i.e.
+variables that have their declaration and assignment combined in a single
+statement) can have their entire initialization statement reordered. Be wary of
+what code is executed at the top level:
+
+  ```nim
+  {.experimental: "codeReordering".}
+
+  proc a() =
+    echo(foo)
+
+  var foo = 5
+
+  a() # outputs: "5"
+  ```
+
+..
+   TODO: Let's table this for now. This is an *experimental feature* and so the
+   specific manner in which `declared` operates with it can be decided in
+   eventuality, because right now it works a bit weirdly.
+
+   The values of expressions involving `declared` are decided *before* the
+   code reordering process, and not after. As an example, the output of this
+   code is the same as it would be with code reordering disabled.
+
+     ```nim
+     {.experimental: "codeReordering".}
+
+     proc x() =
+       echo(declared(foo))
+
+     var foo = 4
+
+     x() # "false"
+     ```
+
+It is important to note that reordering *only* works for symbols at top level
+scope. Therefore, the following will *fail to compile:*
+
+  ```nim
+  {.experimental: "codeReordering".}
+
+  proc a() =
+    b()
+    proc b() =
+      echo("Hello!")
+
+  a()
+  ```
+
+This feature will likely be replaced with a better solution to remove
+the need for forward declarations.
+
+Special Operators
+=================
+
+dot operators
+-------------
+
+.. note:: Dot operators are still experimental and so need to be enabled
+  via `{.experimental: "dotOperators".}`.
+
+Nim offers a special family of dot operators that can be used to
+intercept and rewrite proc call and field access attempts, referring
+to previously undeclared symbol names. They can be used to provide a
+fluent interface to objects lying outside the static confines of the
+type system such as values from dynamic scripting languages
+or dynamic file formats such as JSON or XML.
+
+When Nim encounters an expression that cannot be resolved by the
+standard overload resolution rules, the current scope will be searched
+for a dot operator that can be matched against a re-written form of
+the expression, where the unknown field or proc name is passed to
+an `untyped` parameter:
+
+  ```nim
+  a.b # becomes `.`(a, b)
+  a.b(c, d) # becomes `.`(a, b, c, d)
+  ```
+
+The matched dot operators can be symbols of any callable kind (procs,
+templates and macros), depending on the desired effect:
+
+  ```nim
+  template `.`(js: PJsonNode, field: untyped): JSON = js[astToStr(field)]
+
+  var js = parseJson("{ x: 1, y: 2}")
+  echo js.x # outputs 1
+  echo js.y # outputs 2
+  ```
+
+The following dot operators are available:
+
+operator `.`
+------------
+This operator will be matched against both field accesses and method calls.
+
+operator `.()`
+---------------
+This operator will be matched exclusively against method calls. It has higher
+precedence than the `.` operator and this allows one to handle expressions like
+`x.y` and `x.y()` differently if one is interfacing with a scripting language
+for example.
+
+operator `.=`
+-------------
+This operator will be matched against assignments to missing fields.
+
+  ```nim
+  a.b = c # becomes `.=`(a, b, c)
+  ```
+
+Call operator
+-------------
+The call operator, `()`, matches all kinds of unresolved calls and takes
+precedence over dot operators, however it does not match missing overloads
+for existing routines. The experimental `callOperator` switch must be enabled
+to use this operator.
+
+  ```nim
+  {.experimental: "callOperator".}
+
+  template `()`(a: int, b: float): untyped = $(a, b)
+
+  block:
+    let a = 1.0
+    let b = 2
+    doAssert b(a) == `()`(b, a)
+    doAssert a.b == `()`(b, a)
+
+  block:
+    let a = 1.0
+    proc b(): int = 2
+    doAssert not compiles(b(a))
+    doAssert not compiles(a.b) # `()` not called
+
+  block:
+    let a = 1.0
+    proc b(x: float): int = int(x + 1)
+    let c = 3.0
+
+    doAssert not compiles(a.b(c)) # gives a type mismatch error same as b(a, c)
+    doAssert (a.b)(c) == `()`(a.b, c)
+  ```
+
+
+Extended macro pragmas
+======================
+
+Macro pragmas as described in [the manual](manual.html#userminusdefined-pragmas-macro-pragmas)
+can also be applied to type, variable and constant declarations.
+
+For types:
+
+  ```nim
+  type
+    MyObject {.schema: "schema.protobuf".} = object
+  ```
+
+This is translated to a call to the `schema` macro with a `nnkTypeDef`
+AST node capturing the left-hand side, remaining pragmas and the right-hand
+side of the definition. The macro can return either a type section or
+another `nnkTypeDef` node, both of which will replace the original row
+in the type section.
+
+In the future, this `nnkTypeDef` argument may be replaced with a unary
+type section node containing the type definition, or some other node that may
+be more convenient to work with. The ability to return nodes other than type
+definitions may also be supported, however currently this is not convenient
+when dealing with mutual type recursion. For now, macros can return an unused
+type definition where the right-hand node is of kind `nnkStmtListType`.
+Declarations in this node will be attached to the same scope as
+the parent scope of the type section.
+
+------
+
+For variables and constants, it is largely the same, except a unary node with
+the same kind as the section containing a single definition is passed to macros,
+and macros can return any expression.
+
+  ```nim
+  var
+    a = ...
+    b {.importc, foo, nodecl.} = ...
+    c = ...
+  ```
+
+Assuming `foo` is a macro or a template, this is roughly equivalent to:
+
+  ```nim
+  var a = ...
+  foo:
+    var b {.importc, nodecl.} = ...
+  var c = ...
+  ```
+
+
+Symbols as template/macro calls (alias syntax)
+==============================================
+
+Templates and macros that have no generic parameters and no required arguments
+can be called as lone symbols, i.e. without parentheses. This is useful for
+repeated uses of complex expressions that cannot conveniently be represented
+as runtime values.
+
+  ```nim
+  type Foo = object
+    bar: int
+
+  var foo = Foo(bar: 10)
+  template bar: int = foo.bar
+  assert bar == 10
+  bar = 15
+  assert bar == 15
+  ```
+
+
+Not nil annotation
+==================
+
+**Note:** This is an experimental feature. It can be enabled with
+`{.experimental: "notnil".}`.
+
+All types for which `nil` is a valid value can be annotated with the
+`not nil` annotation to exclude `nil` as a valid value. Note that only local
+symbols are checked.
+
+  ```nim
+  {.experimental: "notnil".}
+
+  type
+    TObj = object
+    PObject = ref TObj not nil
+    TProc = (proc (x, y: int)) not nil
+
+  proc p(x: PObject) =
+    echo "not nil"
+
+  # compiler catches this:
+  p(nil)
+
+  # and also this:
+  proc foo =
+    var x: PObject
+    p(x)
+
+  foo()
+  ```
+
+The compiler ensures that every code path initializes variables which contain
+non-nilable pointers. The details of this analysis are still to be specified
+here.
+
+.. include:: manual_experimental_strictnotnil.md
+
+
+Aliasing restrictions in parameter passing
+==========================================
+
+.. note:: The aliasing restrictions are currently not enforced by the
+  implementation and need to be fleshed out further.
+
+"Aliasing" here means that the underlying storage locations overlap in memory
+at runtime. An "output parameter" is a parameter of type `var T`,
+an input parameter is any parameter that is not of type `var`.
+
+1. Two output parameters should never be aliased.
+2. An input and an output parameter should not be aliased.
+3. An output parameter should never be aliased with a global or thread local
+   variable referenced by the called proc.
+4. An input parameter should not be aliased with a global or thread local
+   variable updated by the called proc.
+
+One problem with rules 3 and 4 is that they affect specific global or thread
+local variables, but Nim's effect tracking only tracks "uses no global variable"
+via `.noSideEffect`. The rules 3 and 4 can also be approximated by a different rule:
+
+5. A global or thread local variable (or a location derived from such a location)
+   can only passed to a parameter of a `.noSideEffect` proc.
+
+
+Strict funcs
+============
+
+Since version 1.4, a stricter definition of "side effect" is available.
+In addition to the existing rule that a side effect is calling a function
+with side effects, the following rule is also enforced:
+
+A store to the heap via a `ref` or `ptr` indirection is not allowed.
+
+For example:
+
+  ```nim
+  {.experimental: "strictFuncs".}
+
+  type
+    Node = ref object
+      le, ri: Node
+      data: string
+
+  func len(n: Node): int =
+    # valid: len does not have side effects
+    var it = n
+    while it != nil:
+      inc result
+      it = it.ri
+
+  func mut(n: Node) =
+    var it = n
+    while it != nil:
+      it.data = "yeah" # forbidden mutation
+      it = it.ri
+
+  ```
+
+
+View types
+==========
+
+.. tip::  `--experimental:views`:option: is more effective
+  with `--experimental:strictFuncs`:option:.
+
+A view type is a type that is or contains one of the following types:
+
+- `lent T` (view into `T`)
+- `openArray[T]` (pair of (pointer to array of `T`, size))
+
+For example:
+
+  ```nim
+  type
+    View1 = openArray[byte]
+    View2 = lent string
+    View3 = Table[openArray[char], int]
+  ```
+
+
+Exceptions to this rule are types constructed via `ptr` or `proc`.
+For example, the following types are **not** view types:
+
+  ```nim
+  type
+    NotView1 = proc (x: openArray[int])
+    NotView2 = ptr openArray[char]
+    NotView3 = ptr array[4, lent int]
+  ```
+
+
+The mutability aspect of a view type is not part of the type but part
+of the locations it's derived from. More on this later.
+
+A *view* is a symbol (a let, var, const, etc.) that has a view type.
+
+Since version 1.4, Nim allows view types to be used as local variables.
+This feature needs to be enabled via `{.experimental: "views".}`.
+
+A local variable of a view type *borrows* from the locations and
+it is statically enforced that the view does not outlive the location
+it was borrowed from.
+
+For example:
+
+  ```nim
+  {.experimental: "views".}
+
+  proc take(a: openArray[int]) =
+    echo a.len
+
+  proc main(s: seq[int]) =
+    var x: openArray[int] = s # 'x' is a view into 's'
+    # it is checked that 'x' does not outlive 's' and
+    # that 's' is not mutated.
+    for i in 0 .. high(x):
+      echo x[i]
+    take(x)
+
+    take(x.toOpenArray(0, 1)) # slicing remains possible
+    let y = x  # create a view from a view
+    take y
+    # it is checked that 'y' does not outlive 'x' and
+    # that 'x' is not mutated as long as 'y' lives.
+
+
+  main(@[11, 22, 33])
+  ```
+
+
+A local variable of a view type can borrow from a location
+derived from a parameter, another local variable, a global `const` or `let`
+symbol or a thread-local `var` or `let`.
+
+Let `p` the proc that is analysed for the correctness of the borrow operation.
+
+Let `source` be one of:
+
+- A formal parameter of `p`. Note that this does not cover parameters of
+  inner procs.
+- The `result` symbol of `p`.
+- A local `var` or `let` or `const` of `p`. Note that this does
+  not cover locals of inner procs.
+- A thread-local `var` or `let`.
+- A global `let` or `const`.
+- A constant array/seq/object/tuple constructor.
+
+
+Path expressions
+----------------
+
+A location derived from `source` is then defined as a path expression that
+has `source` as the owner. A path expression `e` is defined recursively:
+
+- `source` itself is a path expression.
+- Container access like `e[i]` is a path expression.
+- Tuple access `e[0]` is a path expression.
+- Object field access `e.field` is a path expression.
+- `system.toOpenArray(e, ...)` is a path expression.
+- Pointer dereference `e[]` is a path expression.
+- An address `addr e` is a path expression.
+- A type conversion `T(e)` is a path expression.
+- A cast expression `cast[T](e)` is a path expression.
+- `f(e, ...)` is a path expression if `f`'s return type is a view type.
+  Because the view can only have been borrowed from `e`, we then know
+  that the owner of `f(e, ...)` is `e`.
+
+
+If a view type is used as a return type, the location must borrow from a location
+that is derived from the first parameter that is passed to the proc.
+See [the manual](manual.html#procedures-var-return-type)
+for details about how this is done for `var T`.
+
+A mutable view can borrow from a mutable location, an immutable view can borrow
+from both a mutable or an immutable location.
+
+If a view borrows from a mutable location, the view can be used to update the
+location. Otherwise it cannot be used for mutations.
+
+The *duration* of a borrow is the span of commands beginning from the assignment
+to the view and ending with the last usage of the view.
+
+For the duration of the borrow operation, no mutations to the borrowed locations
+may be performed except via the view that borrowed from the
+location. The borrowed location is said to be *sealed* during the borrow.
+
+  ```nim
+  {.experimental: "views".}
+
+  type
+    Obj = object
+      field: string
+
+  proc dangerous(s: var seq[Obj]) =
+    let v: lent Obj = s[0] # seal 's'
+    s.setLen 0  # prevented at compile-time because 's' is sealed.
+    echo v.field
+  ```
+
+
+The scope of the view does not matter:
+
+  ```nim
+  proc valid(s: var seq[Obj]) =
+    let v: lent Obj = s[0]  # begin of borrow
+    echo v.field            # end of borrow
+    s.setLen 0  # valid because 'v' isn't used afterwards
+  ```
+
+
+The analysis requires as much precision about mutations as is reasonably obtainable,
+so it is more effective with the experimental [strict funcs]
+feature. In other words `--experimental:views`:option: works better
+with `--experimental:strictFuncs`:option:.
+
+The analysis is currently control flow insensitive:
+
+  ```nim
+  proc invalid(s: var seq[Obj]) =
+    let v: lent Obj = s[0]
+    if false:
+      s.setLen 0
+    echo v.field
+  ```
+
+In this example, the compiler assumes that `s.setLen 0` invalidates the
+borrow operation of `v` even though a human being can easily see that it
+will never do that at runtime.
+
+
+Start of a borrow
+-----------------
+
+A borrow starts with one of the following:
+
+- The assignment of a non-view-type to a view-type.
+- The assignment of a location that is derived from a local parameter
+  to a view-type.
+
+
+End of a borrow
+---------------
+
+A borrow operation ends with the last usage of the view variable.
+
+
+Reborrows
+---------
+
+A view `v` can borrow from multiple different locations. However, the borrow
+is always the full span of `v`'s lifetime and every location that is borrowed
+from is sealed during `v`'s lifetime.
+
+
+Algorithm
+---------
+
+The following section is an outline of the algorithm that the current implementation
+uses. The algorithm performs two traversals over the AST of the procedure or global
+section of code that uses a view variable. No fixpoint iterations are performed, the
+complexity of the analysis is O(N) where N is the number of nodes of the AST.
+
+The first pass over the AST computes the lifetime of each local variable based on
+a notion of an "abstract time", in the implementation it's a simple integer that is
+incremented for every visited node.
+
+In the second pass, information about the underlying object "graphs" is computed.
+Let `v` be a parameter or a local variable. Let `G(v)` be the graph
+that `v` belongs to. A graph is defined by the set of variables that belong
+to the graph. Initially for all `v`: `G(v) = {v}`. Every variable can only
+be part of a single graph.
+
+Assignments like `a = b` "connect" two variables, both variables end up in the
+same graph `{a, b} = G(a) = G(b)`. Unfortunately, the pattern to look for is
+much more complex than that and can involve multiple assignment targets
+and sources:
+
+    f(x, y) = g(a, b)
+
+connects `x` and `y` to `a` and `b`: `G(x) = G(y) = G(a) = G(b) = {x, y, a, b}`.
+A type based alias analysis rules out some of these combinations, for example
+a `string` value cannot possibly be connected to a `seq[int]`.
+
+A pattern like `v[] = value` or `v.field = value` marks `G(v)` as mutated.
+After the second pass a set of disjoint graphs was computed.
+
+For strict functions it is then enforced that there is no graph that is both mutated
+and has an element that is an immutable parameter (that is a parameter that is not
+of type `var T`).
+
+For borrow checking, a different set of checks is performed. Let `v` be the view
+and `b` the location that is borrowed from.
+
+- The lifetime of `v` must not exceed `b`'s lifetime. Note: The lifetime of
+  a parameter is the complete proc body.
+- If `v` is used for a mutation, `b` must be a mutable location too.
+- During `v`'s lifetime, `G(b)` can only be modified by `v` (and only if
+  `v` is a mutable view).
+- If `v` is `result` then `b` has to be a location derived from the first
+  formal parameter or from a constant location.
+- A view cannot be used for a read or a write access before it was assigned to.
+
+
+Concepts
+========
+
+Concepts, also known as "user-defined type classes", are used to specify an
+arbitrary set of requirements that the matched type must satisfy.
+
+Concepts are written in the following form:
+
+  ```nim
+  type
+    Comparable = concept x, y
+      (x < y) is bool
+
+    Stack[T] = concept s, var v
+      s.pop() is T
+      v.push(T)
+
+      s.len is Ordinal
+
+      for value in s:
+        value is T
+  ```
+
+The concept matches if:
+
+a) all expressions within the body can be compiled for the tested type
+b) all statically evaluable boolean expressions in the body are true
+c) all type modifiers specified match their respective definitions
+
+The identifiers following the `concept` keyword represent instances of the
+currently matched type. You can apply any of the standard type modifiers such
+as `var`, `ref`, `ptr` and `static` to denote a more specific type of
+instance. You can also apply the `type` modifier to create a named instance of
+the type itself:
+
+  ```nim
+  type
+    MyConcept = concept x, var v, ref r, ptr p, static s, type T
+      ...
+  ```
+
+Within the concept body, types can appear in positions where ordinary values
+and parameters are expected. This provides a more convenient way to check for
+the presence of callable symbols with specific signatures:
+
+  ```nim
+  type
+    OutputStream = concept var s
+      s.write(string)
+  ```
+
+In order to check for symbols accepting `type` params, you must prefix
+the type with the explicit `type` modifier. The named instance of the
+type, following the `concept` keyword is also considered to have the
+explicit modifier and will be matched only as a type.
+
+  ```nim
+  type
+    # Let's imagine a user-defined casting framework with operators
+    # such as `val.to(string)` and `val.to(JSonValue)`. We can test
+    # for these with the following concept:
+    MyCastables = concept x
+      x.to(type string)
+      x.to(type JSonValue)
+
+    # Let's define a couple of concepts, known from Algebra:
+    AdditiveMonoid* = concept x, y, type T
+      x + y is T
+      T.zero is T # require a proc such as `int.zero` or 'Position.zero'
+
+    AdditiveGroup* = concept x, y, type T
+      x is AdditiveMonoid
+      -x is T
+      x - y is T
+  ```
+
+Please note that the `is` operator allows one to easily verify the precise
+type signatures of the required operations, but since type inference and
+default parameters are still applied in the concept body, it's also possible
+to describe usage protocols that do not reveal implementation details.
+
+Much like generics, concepts are instantiated exactly once for each tested type
+and any static code included within the body is executed only once.
+
+
+Concept diagnostics
+-------------------
+
+By default, the compiler will report the matching errors in concepts only when
+no other overload can be selected and a normal compilation error is produced.
+When you need to understand why the compiler is not matching a particular
+concept and, as a result, a wrong overload is selected, you can apply the
+`explain` pragma to either the concept body or a particular call-site.
+
+  ```nim
+  type
+    MyConcept {.explain.} = concept ...
+
+  overloadedProc(x, y, z) {.explain.}
+  ```
+
+This will provide Hints in the compiler output either every time the concept is
+not matched or only on the particular call-site.
+
+
+Generic concepts and type binding rules
+---------------------------------------
+
+The concept types can be parametric just like the regular generic types:
+
+  ```nim
+  ### matrixalgo.nim
+
+  import std/typetraits
+
+  type
+    AnyMatrix*[R, C: static int; T] = concept m, var mvar, type M
+      M.ValueType is T
+      M.Rows == R
+      M.Cols == C
+
+      m[int, int] is T
+      mvar[int, int] = T
+
+      type TransposedType = stripGenericParams(M)[C, R, T]
+
+    AnySquareMatrix*[N: static int, T] = AnyMatrix[N, N, T]
+
+    AnyTransform3D* = AnyMatrix[4, 4, float]
+
+  proc transposed*(m: AnyMatrix): m.TransposedType =
+    for r in 0 ..< m.R:
+      for c in 0 ..< m.C:
+        result[r, c] = m[c, r]
+
+  proc determinant*(m: AnySquareMatrix): int =
+    ...
+
+  proc setPerspectiveProjection*(m: AnyTransform3D) =
+    ...
+
+  --------------
+  ### matrix.nim
+
+  type
+    Matrix*[M, N: static int; T] = object
+      data: array[M*N, T]
+
+  proc `[]`*(M: Matrix; m, n: int): M.T =
+    M.data[m * M.N + n]
+
+  proc `[]=`*(M: var Matrix; m, n: int; v: M.T) =
+    M.data[m * M.N + n] = v
+
+  # Adapt the Matrix type to the concept's requirements
+  template Rows*(M: typedesc[Matrix]): int = M.M
+  template Cols*(M: typedesc[Matrix]): int = M.N
+  template ValueType*(M: typedesc[Matrix]): typedesc = M.T
+
+  -------------
+  ### usage.nim
+
+  import matrix, matrixalgo
+
+  var
+    m: Matrix[3, 3, int]
+    projectionMatrix: Matrix[4, 4, float]
+
+  echo m.transposed.determinant
+  setPerspectiveProjection projectionMatrix
+  ```
+
+When the concept type is matched against a concrete type, the unbound type
+parameters are inferred from the body of the concept in a way that closely
+resembles the way generic parameters of callable symbols are inferred on
+call sites.
+
+Unbound types can appear both as params to calls such as `s.push(T)` and
+on the right-hand side of the `is` operator in cases such as `x.pop is T`
+and `x.data is seq[T]`.
+
+Unbound static params will be inferred from expressions involving the `==`
+operator and also when types dependent on them are being matched:
+
+  ```nim
+  type
+    MatrixReducer[M, N: static int; T] = concept x
+      x.reduce(SquareMatrix[N, T]) is array[M, int]
+  ```
+
+The Nim compiler includes a simple linear equation solver, allowing it to
+infer static params in some situations where integer arithmetic is involved.
+
+Just like in regular type classes, Nim discriminates between `bind once`
+and `bind many` types when matching the concept. You can add the `distinct`
+modifier to any of the otherwise inferable types to get a type that will be
+matched without permanently inferring it. This may be useful when you need
+to match several procs accepting the same wide class of types:
+
+  ```nim
+  type
+    Enumerable[T] = concept e
+      for v in e:
+        v is T
+
+  type
+    MyConcept = concept o
+      # this could be inferred to a type such as Enumerable[int]
+      o.foo is distinct Enumerable
+
+      # this could be inferred to a different type such as Enumerable[float]
+      o.bar is distinct Enumerable
+
+      # it's also possible to give an alias name to a `bind many` type class
+      type Enum = distinct Enumerable
+      o.baz is Enum
+  ```
+
+On the other hand, using `bind once` types allows you to test for equivalent
+types used in multiple signatures, without actually requiring any concrete
+types, thus allowing you to encode implementation-defined types:
+
+  ```nim
+  type
+    MyConcept = concept x
+      type T1 = auto
+      x.foo(T1)
+      x.bar(T1) # both procs must accept the same type
+
+      type T2 = seq[SomeNumber]
+      x.alpha(T2)
+      x.omega(T2) # both procs must accept the same type
+                  # and it must be a numeric sequence
+  ```
+
+As seen in the previous examples, you can refer to generic concepts such as
+`Enumerable[T]` just by their short name. Much like the regular generic types,
+the concept will be automatically instantiated with the bind once auto type
+in the place of each missing generic param.
+
+Please note that generic concepts such as `Enumerable[T]` can be matched
+against concrete types such as `string`. Nim doesn't require the concept
+type to have the same number of parameters as the type being matched.
+If you wish to express a requirement towards the generic parameters of
+the matched type, you can use a type mapping operator such as `genericHead`
+or `stripGenericParams` within the body of the concept to obtain the
+uninstantiated version of the type, which you can then try to instantiate
+in any required way. For example, here is how one might define the classic
+`Functor` concept from Haskell and then demonstrate that Nim's `Option[T]`
+type is an instance of it:
+
+  ```nim  test = "nim c $1"
+  import std/[sugar, typetraits]
+
+  type
+    Functor[A] = concept f
+      type MatchedGenericType = genericHead(typeof(f))
+        # `f` will be a value of a type such as `Option[T]`
+        # `MatchedGenericType` will become the `Option` type
+
+      f.val is A
+        # The Functor should provide a way to obtain
+        # a value stored inside it
+
+      type T = auto
+      map(f, A -> T) is MatchedGenericType[T]
+        # And it should provide a way to map one instance of
+        # the Functor to a instance of a different type, given
+        # a suitable `map` operation for the enclosed values
+
+  import std/options
+  echo Option[int] is Functor # prints true
+  ```
+
+
+Concept derived values
+----------------------
+
+All top level constants or types appearing within the concept body are
+accessible through the dot operator in procs where the concept was successfully
+matched to a concrete type:
+
+  ```nim
+  type
+    DateTime = concept t1, t2, type T
+      const Min = T.MinDate
+      T.Now is T
+
+      t1 < t2 is bool
+
+      type TimeSpan = typeof(t1 - t2)
+      TimeSpan * int is TimeSpan
+      TimeSpan + TimeSpan is TimeSpan
+
+      t1 + TimeSpan is T
+
+  proc eventsJitter(events: Enumerable[DateTime]): float =
+    var
+      # this variable will have the inferred TimeSpan type for
+      # the concrete Date-like value the proc was called with:
+      averageInterval: DateTime.TimeSpan
+
+      deviation: float
+    ...
+  ```
+
+
+Concept refinement
+------------------
+
+When the matched type within a concept is directly tested against a different
+concept, we say that the outer concept is a refinement of the inner concept and
+thus it is more-specific. When both concepts are matched in a call during
+overload resolution, Nim will assign a higher precedence to the most specific
+one. As an alternative way of defining concept refinements, you can use the
+object inheritance syntax involving the `of` keyword:
+
+  ```nim
+  type
+    Graph = concept g, type G of EquallyComparable, Copyable
+      type
+        VertexType = G.VertexType
+        EdgeType = G.EdgeType
+
+      VertexType is Copyable
+      EdgeType is Copyable
+
+      var
+        v: VertexType
+        e: EdgeType
+
+    IncidendeGraph = concept of Graph
+      # symbols such as variables and types from the refined
+      # concept are automatically in scope:
+
+      g.source(e) is VertexType
+      g.target(e) is VertexType
+
+      g.outgoingEdges(v) is Enumerable[EdgeType]
+
+    BidirectionalGraph = concept g, type G
+      # The following will also turn the concept into a refinement when it
+      # comes to overload resolution, but it doesn't provide the convenient
+      # symbol inheritance
+      g is IncidendeGraph
+
+      g.incomingEdges(G.VertexType) is Enumerable[G.EdgeType]
+
+  proc f(g: IncidendeGraph)
+  proc f(g: BidirectionalGraph) # this one will be preferred if we pass a type
+                                # matching the BidirectionalGraph concept
+  ```
+
+..
+  Converter type classes
+  ----------------------
+
+  Concepts can also be used to convert a whole range of types to a single type or
+  a small set of simpler types. This is achieved with a `return` statement within
+  the concept body:
+
+    ```nim
+    type
+      Stringable = concept x
+        $x is string
+        return $x
+
+      StringRefValue[CharType] = object
+        base: ptr CharType
+        len: int
+
+      StringRef = concept x
+        # the following would be an overloaded proc for cstring, string, seq and
+        # other user-defined types, returning either a StringRefValue[char] or
+        # StringRefValue[wchar]
+        return makeStringRefValue(x)
+
+    # the varargs param will here be converted to an array of StringRefValues
+    # the proc will have only two instantiations for the two character types
+    proc log(format: static string, varargs[StringRef])
+
+    # this proc will allow char and wchar values to be mixed in
+    # the same call at the cost of additional instantiations
+    # the varargs param will be converted to a tuple
+    proc log(format: static string, varargs[distinct StringRef])
+    ```
+
+
+..
+  VTable types
+  ------------
+
+  Concepts allow Nim to define a great number of algorithms, using only
+  static polymorphism and without erasing any type information or sacrificing
+  any execution speed. But when polymorphic collections of objects are required,
+  the user must use one of the provided type erasure techniques - either common
+  base types or VTable types.
+
+  VTable types are represented as "fat pointers" storing a reference to an
+  object together with a reference to a table of procs implementing a set of
+  required operations (the so called vtable).
+
+  In contrast to other programming languages, the vtable in Nim is stored
+  externally to the object, allowing you to create multiple different vtable
+  views for the same object. Thus, the polymorphism in Nim is unbounded -
+  any type can implement an unlimited number of protocols or interfaces not
+  originally envisioned by the type's author.
+
+  Any concept type can be turned into a VTable type by using the `vtref`
+  or the `vtptr` compiler magics. Under the hood, these magics generate
+  a converter type class, which converts the regular instances of the matching
+  types to the corresponding VTable type.
+
+    ```nim
+    type
+      IntEnumerable = vtref Enumerable[int]
+
+      MyObject = object
+        enumerables: seq[IntEnumerable]
+        streams: seq[OutputStream.vtref]
+
+    proc addEnumerable(o: var MyObject, e: IntEnumerable) =
+      o.enumerables.add e
+
+    proc addStream(o: var MyObject, e: OutputStream.vtref) =
+      o.streams.add e
+    ```
+
+  The procs that will be included in the vtable are derived from the concept
+  body and include all proc calls for which all param types were specified as
+  concrete types. All such calls should include exactly one param of the type
+  matched against the concept (not necessarily in the first position), which
+  will be considered the value bound to the vtable.
+
+  Overloads will be created for all captured procs, accepting the vtable type
+  in the position of the captured underlying object.
+
+  Under these rules, it's possible to obtain a vtable type for a concept with
+  unbound type parameters or one instantiated with metatypes (type classes),
+  but it will include a smaller number of captured procs. A completely empty
+  vtable will be reported as an error.
+
+  The `vtref` magic produces types which can be bound to `ref` types and
+  the `vtptr` magic produced types bound to `ptr` types.
+
+
+..
+  deepCopy
+  --------
+  `=deepCopy` is a builtin that is invoked whenever data is passed to
+  a `spawn`'ed proc to ensure memory safety. The programmer can override its
+  behaviour for a specific `ref` or `ptr` type `T`. (Later versions of the
+  language may weaken this restriction.)
+
+  The signature has to be:
+
+    ```nim
+    proc `=deepCopy`(x: T): T
+    ```
+
+  This mechanism will be used by most data structures that support shared memory,
+  like channels, to implement thread safe automatic memory management.
+
+  The builtin `deepCopy` can even clone closures and their environments. See
+  the documentation of [spawn][spawn statement] for details.
+
+
+Dynamic arguments for bindSym
+=============================
+
+This experimental feature allows the symbol name argument of `macros.bindSym`
+to be computed dynamically.
+
+  ```nim
+  {.experimental: "dynamicBindSym".}
+
+  import std/macros
+
+  macro callOp(opName, arg1, arg2): untyped =
+    result = newCall(bindSym($opName), arg1, arg2)
+
+  echo callOp("+", 1, 2)
+  echo callOp("-", 5, 4)
+  ```
+
+
+Term rewriting macros
+=====================
+
+Term rewriting macros are macros or templates that have not only
+a *name* but also a *pattern* that is searched for after the semantic checking
+phase of the compiler: This means they provide an easy way to enhance the
+compilation pipeline with user defined optimizations:
+
+  ```nim
+  template optMul{`*`(a, 2)}(a: int): int = a + a
+
+  let x = 3
+  echo x * 2
+  ```
+
+The compiler now rewrites `x * 2` as `x + x`. The code inside the
+curly brackets is the pattern to match against. The operators `*`,  `**`,
+`|`, `~` have a special meaning in patterns if they are written in infix
+notation, so to match verbatim against `*` the ordinary function call syntax
+needs to be used.
+
+Term rewriting macros are applied recursively, up to a limit. This means that
+if the result of a term rewriting macro is eligible for another rewriting,
+the compiler will try to perform it, and so on, until no more optimizations
+are applicable. To avoid putting the compiler into an infinite loop, there is
+a hard limit on how many times a single term rewriting macro can be applied.
+Once this limit has been passed, the term rewriting macro will be ignored.
+
+Unfortunately optimizations are hard to get right and even this tiny example
+is **wrong**:
+
+  ```nim
+  template optMul{`*`(a, 2)}(a: int): int = a + a
+
+  proc f(): int =
+    echo "side effect!"
+    result = 55
+
+  echo f() * 2
+  ```
+
+We cannot duplicate 'a' if it denotes an expression that has a side effect!
+Fortunately Nim supports side effect analysis:
+
+  ```nim
+  template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a + a
+
+  proc f(): int =
+    echo "side effect!"
+    result = 55
+
+  echo f() * 2 # not optimized ;-)
+  ```
+
+You can make one overload matching with a constraint and one without, and the
+one with a constraint will have precedence, and so you can handle both cases
+differently.
+
+So what about `2 * a`? We should tell the compiler `*` is commutative. We
+cannot really do that however as the following code only swaps arguments
+blindly:
+
+  ```nim
+  template mulIsCommutative{`*`(a, b)}(a, b: int): int = b * a
+  ```
+
+What optimizers really need to do is a *canonicalization*:
+
+  ```nim
+  template canonMul{`*`(a, b)}(a: int{lit}, b: int): int = b * a
+  ```
+
+The `int{lit}` parameter pattern matches against an expression of
+type `int`, but only if it's a literal.
+
+
+
+Parameter constraints
+---------------------
+
+The `parameter constraint`:idx: expression can use the operators `|` (or),
+`&` (and) and `~` (not) and the following predicates:
+
+===================      =====================================================
+Predicate                Meaning
+===================      =====================================================
+`atom`                   The matching node has no children.
+`lit`                    The matching node is a literal like `"abc"`, `12`.
+`sym`                    The matching node must be a symbol (a bound
+                         identifier).
+`ident`                  The matching node must be an identifier (an unbound
+                         identifier).
+`call`                   The matching AST must be a call/apply expression.
+`lvalue`                 The matching AST must be an lvalue.
+`sideeffect`             The matching AST must have a side effect.
+`nosideeffect`           The matching AST must have no side effect.
+`param`                  A symbol which is a parameter.
+`genericparam`           A symbol which is a generic parameter.
+`module`                 A symbol which is a module.
+`type`                   A symbol which is a type.
+`var`                    A symbol which is a variable.
+`let`                    A symbol which is a `let` variable.
+`const`                  A symbol which is a constant.
+`result`                 The special `result` variable.
+`proc`                   A symbol which is a proc.
+`method`                 A symbol which is a method.
+`iterator`               A symbol which is an iterator.
+`converter`              A symbol which is a converter.
+`macro`                  A symbol which is a macro.
+`template`               A symbol which is a template.
+`field`                  A symbol which is a field in a tuple or an object.
+`enumfield`              A symbol which is a field in an enumeration.
+`forvar`                 A for loop variable.
+`label`                  A label (used in `block` statements).
+`nk*`                    The matching AST must have the specified kind.
+                         (Example: `nkIfStmt` denotes an `if` statement.)
+`alias`                  States that the marked parameter needs to alias
+                         with *some* other parameter.
+`noalias`                States that *every* other parameter must not alias
+                         with the marked parameter.
+===================      =====================================================
+
+Predicates that share their name with a keyword have to be escaped with
+backticks.
+The `alias` and `noalias` predicates refer not only to the matching AST,
+but also to every other bound parameter; syntactically they need to occur after
+the ordinary AST predicates:
+
+  ```nim
+  template ex{a = b + c}(a: int{noalias}, b, c: int) =
+    # this transformation is only valid if 'b' and 'c' do not alias 'a':
+    a = b
+    inc a, c
+  ```
+
+Another example:
+
+  ```nim
+  proc somefunc(s: string)                 = assert s == "variable"
+  proc somefunc(s: string{nkStrLit})       = assert s == "literal"
+  proc somefunc(s: string{nkRStrLit})      = assert s == r"raw"
+  proc somefunc(s: string{nkTripleStrLit}) = assert s == """triple"""
+  proc somefunc(s: static[string])         = assert s == "constant"
+
+  # Use parameter constraints to provide overloads based on both the input parameter type and form.
+  var variable = "variable"
+  somefunc(variable)
+  const constant = "constant"
+  somefunc(constant)
+  somefunc("literal")
+  somefunc(r"raw")
+  somefunc("""triple""")
+  ```
+
+
+Pattern operators
+-----------------
+
+The operators `*`,  `**`, `|`, `~` have a special meaning in patterns
+if they are written in infix notation.
+
+
+### The `|` operator
+
+The `|` operator if used as infix operator creates an ordered choice:
+
+  ```nim
+  template t{0|1}(): untyped = 3
+  let a = 1
+  # outputs 3:
+  echo a
+  ```
+
+The matching is performed after the compiler performed some optimizations like
+constant folding, so the following does not work:
+
+  ```nim
+  template t{0|1}(): untyped = 3
+  # outputs 1:
+  echo 1
+  ```
+
+The reason is that the compiler already transformed the 1 into "1" for
+the `echo` statement. However, a term rewriting macro should not change the
+semantics anyway. In fact, they can be deactivated with the `--patterns:off`:option:
+command line option or temporarily with the `patterns` pragma.
+
+
+### The `{}` operator
+
+A pattern expression can be bound to a pattern parameter via the `expr{param}`
+notation:
+
+  ```nim
+  template t{(0|1|2){x}}(x: untyped): untyped = x + 1
+  let a = 1
+  # outputs 2:
+  echo a
+  ```
+
+
+### The `~` operator
+
+The `~` operator is the 'not' operator in patterns:
+
+  ```nim
+  template t{x = (~x){y} and (~x){z}}(x, y, z: bool) =
+    x = y
+    if x: x = z
+
+  var
+    a = false
+    b = true
+    c = false
+  a = b and c
+  echo a
+  ```
+
+
+### The `*` operator
+
+The `*` operator can *flatten* a nested binary expression like `a & b & c`
+to `&(a, b, c)`:
+
+  ```nim
+  var
+    calls = 0
+
+  proc `&&`(s: varargs[string]): string =
+    result = s[0]
+    for i in 1..len(s)-1: result.add s[i]
+    inc calls
+
+  template optConc{ `&&` * a }(a: string): untyped = &&a
+
+  let space = " "
+  echo "my" && (space & "awe" && "some " ) && "concat"
+
+  # check that it's been optimized properly:
+  doAssert calls == 1
+  ```
+
+
+The second operator of `*` must be a parameter; it is used to gather all the
+arguments. The expression `"my" && (space & "awe" && "some " ) && "concat"`
+is passed to `optConc` in `a` as a special list (of kind `nkArgList`)
+which is flattened into a call expression; thus the invocation of `optConc`
+produces:
+
+  ```nim
+  `&&`("my", space & "awe", "some ", "concat")
+  ```
+
+
+### The `**` operator
+
+The `**` is much like the `*` operator, except that it gathers not only
+all the arguments, but also the matched operators in reverse polish notation:
+
+  ```nim
+  import std/macros
+
+  type
+    Matrix = object
+      dummy: int
+
+  proc `*`(a, b: Matrix): Matrix = discard
+  proc `+`(a, b: Matrix): Matrix = discard
+  proc `-`(a, b: Matrix): Matrix = discard
+  proc `$`(a: Matrix): string = result = $a.dummy
+  proc mat21(): Matrix =
+    result.dummy = 21
+
+  macro optM{ (`+`|`-`|`*`) ** a }(a: Matrix): untyped =
+    echo treeRepr(a)
+    result = newCall(bindSym"mat21")
+
+  var x, y, z: Matrix
+
+  echo x + y * z - x
+  ```
+
+This passes the expression `x + y * z - x` to the `optM` macro as
+an `nnkArgList` node containing:
+
+    Arglist
+      Sym "x"
+      Sym "y"
+      Sym "z"
+      Sym "*"
+      Sym "+"
+      Sym "x"
+      Sym "-"
+
+(This is the reverse polish notation of `x + y * z - x`.)
+
+
+Parameters
+----------
+
+Parameters in a pattern are type checked in the matching process. If a
+parameter is of the type `varargs`, it is treated specially and can match
+0 or more arguments in the AST to be matched against:
+
+  ```nim
+  template optWrite{
+    write(f, x)
+    ((write|writeLine){w})(f, y)
+  }(x, y: varargs[untyped], f: File, w: untyped) =
+    w(f, x, y)
+  ```
+
+
+noRewrite pragma
+----------------
+
+Term rewriting macros and templates are currently greedy and
+they will rewrite as long as there is a match.
+There was no way to ensure some rewrite happens only once,
+e.g. when rewriting term to same term plus extra content.
+
+`noRewrite` pragma can actually prevent further rewriting on marked code,
+e.g. with given example `echo("ab")` will be rewritten just once:
+
+  ```nim
+  template pwnEcho{echo(x)}(x: untyped) =
+    {.noRewrite.}: echo("pwned!")
+
+  echo "ab"
+  ```
+
+`noRewrite` pragma can be useful to control term-rewriting macros recursion.
+
+
+
+Example: Partial evaluation
+---------------------------
+
+The following example shows how some simple partial evaluation can be
+implemented with term rewriting:
+
+  ```nim
+  proc p(x, y: int; cond: bool): int =
+    result = if cond: x + y else: x - y
+
+  template optP1{p(x, y, true)}(x, y: untyped): untyped = x + y
+  template optP2{p(x, y, false)}(x, y: untyped): untyped = x - y
+  ```
+
+
+Example: Hoisting
+-----------------
+
+The following example shows how some form of hoisting can be implemented:
+
+  ```nim
+  import std/pegs
+
+  template optPeg{peg(pattern)}(pattern: string{lit}): Peg =
+    var gl {.global, gensym.} = peg(pattern)
+    gl
+
+  for i in 0 .. 3:
+    echo match("(a b c)", peg"'(' @ ')'")
+    echo match("W_HI_Le", peg"\y 'while'")
+  ```
+
+The `optPeg` template optimizes the case of a peg constructor with a string
+literal, so that the pattern will only be parsed once at program startup and
+stored in a global `gl` which is then re-used. This optimization is called
+hoisting because it is comparable to classical loop hoisting.
+
+
+AST based overloading
+=====================
+
+Parameter constraints can also be used for ordinary routine parameters; these
+constraints then affect ordinary overloading resolution:
+
+  ```nim
+  proc optLit(a: string{lit|`const`}) =
+    echo "string literal"
+  proc optLit(a: string) =
+    echo "no string literal"
+
+  const
+    constant = "abc"
+
+  var
+    variable = "xyz"
+
+  optLit("literal")
+  optLit(constant)
+  optLit(variable)
+  ```
+
+However, the constraints `alias` and `noalias` are not available in
+ordinary routines.
+
+
+Parallel & Spawn
+================
+
+Nim has two flavors of parallelism:
+1) `Structured`:idx: parallelism via the `parallel` statement.
+2) `Unstructured`:idx: parallelism via the standalone `spawn` statement.
+
+Nim has a builtin thread pool that can be used for CPU intensive tasks. For
+IO intensive tasks the `async` and `await` features should be
+used instead. Both parallel and spawn need the [threadpool](threadpool.html)
+module to work.
+
+Somewhat confusingly, `spawn` is also used in the `parallel` statement
+with slightly different semantics. `spawn` always takes a call expression of
+the form `f(a, ...)`. Let `T` be `f`'s return type. If `T` is `void`,
+then `spawn`'s return type is also `void`, otherwise it is `FlowVar[T]`.
+
+Within a `parallel` section, the `FlowVar[T]` is sometimes eliminated
+to `T`. This happens when `T` does not contain any GC'ed memory.
+The compiler can ensure the location in `location = spawn f(...)` is not
+read prematurely within a `parallel` section and so there is no need for
+the overhead of an indirection via `FlowVar[T]` to ensure correctness.
+
+.. note:: Currently exceptions are not propagated between `spawn`'ed tasks!
+
+This feature is likely to be removed in the future as external packages
+can have better solutions.
+
+
+Spawn statement
+---------------
+
+The `spawn`:idx: statement can be used to pass a task to the thread pool:
+
+  ```nim
+  import std/threadpool
+
+  proc processLine(line: string) =
+    discard "do some heavy lifting here"
+
+  for x in lines("myinput.txt"):
+    spawn processLine(x)
+  sync()
+  ```
+
+For reasons of type safety and implementation simplicity the expression
+that `spawn` takes is restricted:
+
+* It must be a call expression `f(a, ...)`.
+* `f` must be `gcsafe`.
+* `f` must not have the calling convention `closure`.
+* `f`'s parameters may not be of type `var`.
+  This means one has to use raw `ptr`'s for data passing reminding the
+  programmer to be careful.
+* `ref` parameters are deeply copied, which is a subtle semantic change and
+  can cause performance problems, but ensures memory safety. This deep copy
+  is performed via `system.deepCopy`, so it can be overridden.
+* For *safe* data exchange between `f` and the caller, a global `Channel`
+  needs to be used. However, since spawn can return a result, often no further
+  communication is required.
+
+
+`spawn` executes the passed expression on the thread pool and returns
+a `data flow variable`:idx: `FlowVar[T]` that can be read from. The reading
+with the `^` operator is **blocking**. However, one can use `blockUntilAny` to
+wait on multiple flow variables at the same time:
+
+  ```nim
+  import std/threadpool, ...
+
+  # wait until 2 out of 3 servers received the update:
+  proc main =
+    var responses = newSeq[FlowVarBase](3)
+    for i in 0..2:
+      responses[i] = spawn tellServer(Update, "key", "value")
+    var index = blockUntilAny(responses)
+    assert index >= 0
+    responses.del(index)
+    discard blockUntilAny(responses)
+  ```
+
+Data flow variables ensure that no data races are possible. Due to
+technical limitations, not every type `T` can be used in
+a data flow variable: `T` has to be a `ref`, `string`, `seq`
+or of a type that doesn't contain any GC'd type. This
+restriction is not hard to work-around in practice.
+
+
+
+Parallel statement
+------------------
+
+Example:
+
+  ```nim  test = "nim c --threads:on $1"
+  # Compute pi in an inefficient way
+  import std/[strutils, math, threadpool]
+  {.experimental: "parallel".}
+
+  proc term(k: float): float = 4 * math.pow(-1, k) / (2*k + 1)
+
+  proc pi(n: int): float =
+    var ch = newSeq[float](n + 1)
+    parallel:
+      for k in 0..ch.high:
+        ch[k] = spawn term(float(k))
+    for k in 0..ch.high:
+      result += ch[k]
+
+  echo formatFloat(pi(5000))
+  ```
+
+
+The parallel statement is the preferred mechanism to introduce parallelism in a
+Nim program. Only a subset of the Nim language is valid within a `parallel`
+section. This subset is checked during semantic analysis to be free of data
+races. A sophisticated `disjoint checker`:idx: ensures that no data races are
+possible, even though shared memory is extensively supported!
+
+The subset is in fact the full language with the following
+restrictions / changes:
+
+* `spawn` within a `parallel` section has special semantics.
+* Every location of the form `a[i]`, `a[i..j]` and `dest` where
+  `dest` is part of the pattern `dest = spawn f(...)` has to be
+  provably disjoint. This is called the *disjoint check*.
+* Every other complex location `loc` that is used in a spawned
+  proc (`spawn f(loc)`) has to be immutable for the duration of
+  the `parallel` section. This is called the *immutability check*. Currently
+  it is not specified what exactly "complex location" means. We need to make
+  this an optimization!
+* Every array access has to be provably within bounds. This is called
+  the *bounds check*.
+* Slices are optimized so that no copy is performed. This optimization is not
+  yet performed for ordinary slices outside of a `parallel` section.
+
+
+Strict definitions and `out` parameters
+=======================================
+
+With `experimental: "strictDefs"` *every* local variable must be initialized explicitly before it can be used:
+
+  ```nim
+  {.experimental: "strictDefs".}
+
+  proc test =
+    var s: seq[string]
+    s.add "abc" # invalid!
+
+  ```
+
+Needs to be written as:
+
+  ```nim
+  {.experimental: "strictDefs".}
+
+  proc test =
+    var s: seq[string] = @[]
+    s.add "abc" # valid!
+
+  ```
+
+A control flow analysis is performed in order to prove that a variable has been written to
+before it is used. Thus the following is valid:
+
+  ```nim
+  {.experimental: "strictDefs".}
+
+  proc test(cond: bool) =
+    var s: seq[string]
+    if cond:
+      s = @["y"]
+    else:
+      s = @[]
+    s.add "abc" # valid!
+  ```
+
+In this example every path does set `s` to a value before it is used.
+
+  ```nim
+  {.experimental: "strictDefs".}
+
+  proc test(cond: bool) =
+    let s: seq[string]
+    if cond:
+      s = @["y"]
+    else:
+      s = @[]
+  ```
+
+With `experimental: "strictDefs"`, `let` statements are allowed to not have an initial value, but every path should set `s` to a value before it is used.
+
+
+`out` parameters
+----------------
+
+An `out` parameter is like a `var` parameter but it must be written to before it can be used:
+
+  ```nim
+  proc myopen(f: out File; name: string): bool =
+    f = default(File)
+    result = open(f, name)
+  ```
+
+While it is usually the better style to use the return type in order to return results API and ABI
+considerations might make this infeasible. Like for `var T` Nim maps `out T` to a hidden pointer.
+For example POSIX's `stat` routine can be wrapped as:
+
+  ```nim
+  proc stat*(a1: cstring, a2: out Stat): cint {.importc, header: "<sys/stat.h>".}
+  ```
+
+When the implementation of a routine with output parameters is analysed, the compiler
+checks that every path before the (implicit or explicit) return does set every output
+parameter:
+
+  ```nim
+  proc p(x: out int; y: out string; cond: bool) =
+    x = 4
+    if cond:
+      y = "abc"
+    # error: not every path initializes 'y'
+  ```
+
+
+Out parameters and exception handling
+-------------------------------------
+
+The analysis should take exceptions into account (but currently does not):
+
+  ```nim
+  proc p(x: out int; y: out string; cond: bool) =
+    x = canRaise(45)
+    y = "abc" # <-- error: not every path initializes 'y'
+  ```
+
+Once the implementation takes exceptions into account it is easy enough to
+use `outParam = default(typeof(outParam))` in the beginning of the proc body.
+
+Out parameters and inheritance
+------------------------------
+
+It is not valid to pass an lvalue of a supertype to an `out T` parameter:
+
+  ```nim
+  type
+    Superclass = object of RootObj
+      a: int
+    Subclass = object of Superclass
+      s: string
+
+  proc init(x: out Superclass) =
+    x = Superclass(a: 8)
+
+  var v: Subclass
+  init v
+  use v.s # the 's' field was never initialized!
+  ```
+
+However, in the future this could be allowed and provide a better way to write object
+constructors that take inheritance into account.
+
+
+**Note**: The implementation of "strict definitions" and "out parameters" is experimental but the concept
+is solid and it is expected that eventually this mode becomes the default in later versions.
+
+
+Strict case objects
+===================
+
+With `experimental: "strictCaseObjects"` *every* field access is checked to be valid at compile-time.
+The field is within a `case` section of an `object`.
+
+  ```nim
+  {.experimental: "strictCaseObjects".}
+
+  type
+    Foo = object
+      case b: bool
+      of false:
+        s: string
+      of true:
+        x: int
+
+  var x = Foo(b: true, x: 4)
+  case x.b
+  of true:
+    echo x.x # valid
+  of false:
+    echo "no"
+
+  case x.b
+  of false:
+    echo x.x # error: field access outside of valid case branch: x.x
+  of true:
+    echo "no"
+
+  ```
+
+**Note**: The implementation of "strict case objects" is experimental but the concept
+is solid and it is expected that eventually this mode becomes the default in later versions.
+
+
+Quirky routines
+===============
+
+The default code generation strategy of exceptions under the ARC/ORC model is the so called
+`--exceptions:goto` implementation. This implementation inserts a check after every call that
+can potentially raise an exception. A typical instruction sequence for this on
+for a x86 64 bit machine looks like:
+
+  ```
+  cmp DWORD PTR [rbx], 0
+  je  .L1
+  ```
+
+This is a memory fetch followed by jump. (An ideal implementation would
+use the carry flag and a single instruction like ``jc .L1``.)
+
+This overhead might not be desired and depending on the semantics of the routine may not be required
+either.
+So it can be disabled via a `.quirky` annotation:
+
+  ```nim
+  proc wontRaise(x: int) {.quirky.} =
+    if x != 0:
+      # because of `quirky` this will continue even if `write` raised an IO exception:
+      write x
+      wontRaise(x-1)
+
+  wontRaise 10
+
+  ```
+
+If the used exception model is not `--exceptions:goto` then the `quirky` pragma has no effect and is
+ignored.
+
+The `quirky` pragma can also be be pushed in order to affect a group of routines and whether
+the compiler supports the pragma can be checked with `defined(nimHasQuirky)`:
+
+  ```nim
+  when defined(nimHasQuirky):
+    {.push quirky: on.}
+
+  proc doRaise() = raise newException(ValueError, "")
+
+  proc f(): string = "abc"
+
+  proc q(cond: bool) =
+    if cond:
+      doRaise()
+    echo f()
+
+  q(true)
+
+  when defined(nimHasQuirky):
+    {.pop.}
+  ```
+
+**Warning**: The `quirky` pragma only affects code generation, no check for validity is performed!
+
+
+Threading under ARC/ORC
+=======================
+
+ARC/ORC supports a shared heap out of the box. This means that messages can be sent between
+threads without copies. However, without copying the data there is an inherent danger of
+data races. Data races are prevented at compile-time if it is enforced that
+only **isolated** subgraphs can be sent around.
+
+
+Isolation
+---------
+
+The standard library module `isolation.nim` provides a generic type `Isolated[T]` that
+captures the important notion that nothing else can reference the graph that is wrapped
+inside `Isolated[T]`. It is what a channel implementation should use in order to enforce
+the freedom of data races:
+
+  ```nim
+  proc send*[T](c: var Channel[T]; msg: sink Isolated[T])
+  proc recv*[T](c: var Channel[T]): T
+    ## Note: Returns T, not Isolated[T] for convenience.
+
+  proc recvIso*[T](c: var Channel[T]): Isolated[T]
+    ## remembers the data is Isolated[T].
+  ```
+
+In order to create an `Isolated` graph one has to use either `isolate` or `unsafeIsolate`.
+`unsafeIsolate` is as its name says unsafe and no checking is performed. It should be considered
+to be as dangerous as a `cast` operation.
+
+
+Construction must ensure that the invariant holds, namely that the wrapped `T`
+is free of external aliases into it. `isolate` ensures this invariant. It is
+inspired by Pony's `recover` construct:
+
+  ```nim
+  func isolate(x: sink T): Isolated[T] {.magic: "Isolate".}
+  ```
+
+
+As you can see, this is a new builtin because the check it performs on `x` is non-trivial:
+
+If `T` does not contain a `ref` or `closure` type, it is isolated. Else the syntactic
+structure of `x` is analyzed:
+
+- Literals like `nil`, `4`, `"abc"` are isolated.
+- A local variable or a routine parameter is isolated if either of these conditions is true:
+  1. Its type is annotated with the `.sendable` pragma. Note `Isolated[T]` is annotated as
+     `.sendable`.
+  2. Its type contains the potentially dangerous `ref` and `proc {.closure}` types
+     only in places that are protected via a `.sendable` container.
+
+- An array constructor `[x...]` is isolated if every element `x` is isolated.
+- An object constructor `Obj(x...)` is isolated if every element `x` is isolated.
+- An `if` or `case` expression is isolated if all possible values the expression
+  may return are isolated.
+- A type conversion `C(x)` is isolated if `x` is isolated. Analogous for `cast`
+  expressions.
+- A function call `f(x...)` is isolated if `f` is `.noSideEffect` and for every argument `x`:
+  - `x` is isolated **or**
+  - `f`'s return type cannot *alias* `x`'s type. This is checked via a form of alias analysis as explained in the next paragraph.
+
+
+
+Alias analysis
+--------------
+
+We start with an important, simple case that must be valid: Sending the result
+of `parseJson` to a channel. Since the signature
+is `func parseJson(input: string): JsonNode` it is easy to see that JsonNode
+can never simply be a view into `input` which is a `string`.
+
+A different case is the identity function `id`, `send id(myJsonGraph)` must be
+invalid because we do not know how many aliases into `myJsonGraph` exist
+elsewhere.
+
+In general type `A` can alias type `T` if:
+
+- `A` and `T` are the same types.
+- `A` is a distinct type derived from `T`.
+- `A` is a field inside `T` if `T` is a final object type.
+- `T` is an inheritable object type. (An inherited type could always contain
+  a `field: A`).
+- `T` is a closure type. Reason: `T`'s environment can contain a field of
+  type `A`.
+- `A` is the element type of `T` if `T` is an array, sequence or pointer type.
+
+
+
+
+Sendable pragma
+---------------
+
+A container type can be marked as `.sendable`. `.sendable` declares that the type
+encapsulates a `ref` type effectively so that a variable of this container type
+can be used in an `isolate` context:
+
+  ```nim
+  type
+    Isolated*[T] {.sendable.} = object ## Isolated data can only be moved, not copied.
+      value: T
+
+  proc `=copy`*[T](dest: var Isolated[T]; src: Isolated[T]) {.error.}
+
+  proc `=sink`*[T](dest: var Isolated[T]; src: Isolated[T]) {.inline.} =
+    # delegate to value's sink operation
+    `=sink`(dest.value, src.value)
+
+  proc `=destroy`*[T](dest: var Isolated[T]) {.inline.} =
+    # delegate to value's destroy operation
+    `=destroy`(dest.value)
+  ```
+
+The `.sendable` pragma itself is an experimenal, unchecked, unsafe annotation. It is
+currently only used by `Isolated[T]`.
+
+Virtual pragma
+==============
+
+`virtual` is designed to extend or create virtual functions when targeting the cpp backend. When a proc is marked with virtual, it forward declares the proc header within the type's body.
+
+Here's an example of how to use the virtual pragma:
+
+```nim
+proc newCpp*[T](): ptr T {.importcpp: "new '*0()".}
+type
+  Foo = object of RootObj
+  FooPtr = ptr Foo
+  Boo = object of Foo
+  BooPtr = ptr Boo
+
+proc salute(self: FooPtr) {.virtual.} =
+  echo "hello foo"
+
+proc salute(self: BooPtr) {.virtual.} =
+  echo "hello boo"
+
+let foo = newCpp[Foo]()
+let boo = newCpp[Boo]()
+let booAsFoo = cast[FooPtr](newCpp[Boo]())
+
+foo.salute() # prints hello foo
+boo.salute() # prints hello boo
+booAsFoo.salute() # prints hello boo
+```
+In this example, the `salute` function is virtual in both Foo and Boo types. This allows for polymorphism.
+
+The virtual pragma also supports a special syntax to express Cpp constraints. Here's how it works:
+
+`$1` refers to the function name
+`'idx` refers to the type of the argument at the position idx. Where idx = 1 is the `this` argument.
+`#idx` refers to the argument name.
+
+The return type can be referred to as `-> '0`, but this is optional and often not needed.
+
+ ```nim
+ {.emit:"""/*TYPESECTION*/
+#include <iostream>
+  class CppPrinter {
+  public:
+
+    virtual void printConst(char* message) const {
+        std::cout << "Const Message: " << message << std::endl;
+    }
+    virtual void printConstRef(char* message, const int& flag) const {
+        std::cout << "Const Ref Message: " << message << std::endl;
+    }
+};
+""".}
+
+type
+  CppPrinter {.importcpp, inheritable.} = object
+  NimPrinter {.exportc.} = object of CppPrinter
+
+proc printConst(self: CppPrinter; message:cstring) {.importcpp.}
+CppPrinter().printConst(message)
+
+# override is optional.
+proc printConst(self: NimPrinter; message: cstring) {.virtual: "$1('2 #2) const override".} =
+  echo "NimPrinter: " & $message
+
+proc printConstRef(self: NimPrinter; message: cstring; flag:int32) {.virtual: "$1('2 #2, const '3& #3 ) const override".} =
+  echo "NimPrinterConstRef: " & $message
+
+NimPrinter().printConst(message)
+var val: int32 = 10
+NimPrinter().printConstRef(message, val)
+
+```
+
+Constructor pragma
+==================
+
+The `constructor` pragma can be used in two ways: in conjunction with `importcpp` to import a C++ constructor, and to declare constructors that operate similarly to `virtual`.
+
+Consider:
+
+```nim
+type Foo* = object
+  x: int32
+
+proc makeFoo(x: int32): Foo {.constructor.} =
+  result.x = x
+```
+
+It forward declares the constructor in the type definition. When the constructor has parameters, it also generates a default constructor. One can avoid this behaviour by using `noDecl` in a default constructor.
+
+Like `virtual`, `constructor` also supports a syntax that allows to express C++ constraints.
+
+For example:
+
+```nim
+{.emit:"""/*TYPESECTION*/
+struct CppClass {
+  int x;
+  int y;
+  CppClass(int inX, int inY) {
+    this->x = inX;
+    this->y = inY;
+  }
+  //CppClass() = default;
+};
+""".}
+
+type
+  CppClass* {.importcpp, inheritable.} = object
+    x: int32
+    y: int32
+  NimClass* = object of CppClass
+
+proc makeNimClass(x: int32): NimClass {.constructor:"NimClass('1 #1) : CppClass(0, #1)".} =
+  result.x = x
+
+# Optional: define the default constructor explicitly
+proc makeCppClass(): NimClass {.constructor: "NimClass() : CppClass(0, 0)".} =
+  result.x = 1
+```
+
+In the example above `CppClass` has a deleted default constructor. Notice how by using the constructor syntax, one can call the appropriate constructor.
+
+Notice when calling a constructor in the section of a global variable initialization, it will be called before `NimMain` meaning Nim is not fully initialized.
+
+Constructor Initializer
+=======================
+
+By default Nim initializes `importcpp` types with `{}`. This can be problematic when importing
+types with a deleted default constructor. In order to avoid this, one can specify default values for a constructor by specifying default values for the proc params in the `constructor` proc.
+
+For example:
+
+```nim
+
+{.emit: """/*TYPESECTION*/
+struct CppStruct {
+  CppStruct(int x, char* y): x(x), y(y){}
+  int x;
+  char* y;
+};
+""".}
+type
+  CppStruct {.importcpp, inheritable.} = object
+
+proc makeCppStruct(a: cint = 5, b:cstring = "hello"): CppStruct {.importcpp: "CppStruct(@)", constructor.}
+
+(proc (s: CppStruct) = echo "hello")(makeCppStruct()) 
+# If one removes a default value from the constructor and passes it to the call explicitly, the C++ compiler will complain.
+
+```
+Skip initializers in fields members
+===================================
+
+By using `noInit` in a type or field declaration, the compiler will skip the initializer. By doing so one can explicitly initialize those values in the constructor of the type owner.
+
+For example:
+
+```nim
+
+{.emit: """/*TYPESECTION*/
+  struct Foo {
+    Foo(int a){};
+  };
+  struct Boo {
+    Boo(int a){};
+  };
+
+  """.}
+
+type 
+  Foo {.importcpp.} = object
+  Boo {.importcpp, noInit.} = object
+  Test {.exportc.} = object
+    foo {.noInit.}: Foo
+    boo: Boo
+
+proc makeTest(): Test {.constructor: "Test() : foo(10), boo(1)".} = 
+  discard
+
+proc main() = 
+  var t = makeTest()
+
+main()
+
+```
+
+Will produce: 
+
+```cpp
+
+struct Test {
+	Foo foo; 
+	Boo boo;
+  N_LIB_PRIVATE N_NOCONV(, Test)(void);
+};
+
+```
+
+Notice that without `noInit` it would produce `Foo foo {}` and `Boo boo {}`
+
+
+Member pragma
+=============
+
+Like the `constructor` and `virtual` pragmas, the `member` pragma can be used to attach a procedure to a C++ type. It's more flexible than the `virtual` pragma in the sense that it accepts not only names but also operators and destructors.
+
+For example:
+
+```nim
+proc print(s: cstring) {.importcpp: "printf(@)", header: "<stdio.h>".}
+
+type
+  Doo {.exportc.} = object
+    test: int
+
+proc memberProc(f: Doo) {.member.} = 
+  echo $f.test
+
+proc destructor(f: Doo) {.member: "~'1()", used.} = 
+  print "destructing\n"
+
+proc `==`(self, other: Doo): bool {.member: "operator==('2 const & #2) const -> '0".} = 
+  self.test == other.test
+
+let doo = Doo(test: 2)
+doo.memberProc()
+echo doo == Doo(test: 1)
+
+```
+
+Will print:
+```
+2
+false
+destructing
+destructing
+```
+
+Notice how the C++ destructor is called automatically. Also notice the double implementation of `==` as an operator in Nim but also in C++. This is useful if you need the type to match some C++ `concept` or `trait` when interoping. 
+
+A side effect of being able to declare C++ operators, is that you can now also create a
+C++ functor to have seamless interop with C++ lambdas (syntactic sugar for functors).
+
+For example:
+
+```nim
+type
+  NimFunctor = object
+    discard
+proc invoke(f: NimFunctor; n: int) {.member: "operator ()('2 #2)".} = 
+  echo "FunctorSupport!"
+
+{.experimental: "callOperator".}
+proc `()`(f: NimFunctor; n:int) {.importcpp: "#(@)" .} 
+NimFunctor()(1)
+```
+Notice we use the overload of `()` to have the same semantics in Nim, but on the `importcpp` we import the functor as a function. 
+This allows to easy interop with functions that accepts for example a `const` operator in its signature. 
+
+
+Injected symbols in generic procs and templates
+===============================================
+
+With the experimental option `openSym`, captured symbols in generic routine and
+template bodies may be replaced by symbols injected locally by templates/macros
+at instantiation time. `bind` may be used to keep the captured symbols over the
+injected ones regardless of enabling the options, but other methods like
+renaming the captured symbols should be used instead so that the code is not
+affected by context changes.
+
+Since this change may affect runtime behavior, the experimental switch
+`openSym` needs to be enabled; and a warning is given in the case where an
+injected symbol would replace a captured symbol not bound by `bind` and
+the experimental switch isn't enabled.
+
+```nim
+const value = "captured"
+template foo(x: int, body: untyped): untyped =
+  let value {.inject.} = "injected"
+  body
+
+proc old[T](): string =
+  foo(123):
+    return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
+echo old[int]() # "captured"
+
+template oldTempl(): string =
+  block:
+    foo(123):
+      value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
+echo oldTempl() # "captured"
+
+{.experimental: "openSym".}
+
+proc bar[T](): string =
+  foo(123):
+    return value
+assert bar[int]() == "injected" # previously it would be "captured"
+
+proc baz[T](): string =
+  bind value
+  foo(123):
+    return value
+assert baz[int]() == "captured"
+
+template barTempl(): string =
+  block:
+    foo(123):
+      value
+assert barTempl() == "injected" # previously it would be "captured"
+
+template bazTempl(): string =
+  bind value
+  block:
+    foo(123):
+      value
+assert bazTempl() == "captured"
+```
+
+This option also generates a new node kind `nnkOpenSym` which contains
+exactly 1 `nnkSym` node. In the future this might be merged with a slightly
+modified `nnkOpenSymChoice` node but macros that want to support the
+experimental feature should still handle `nnkOpenSym`, as the node kind would
+simply not be generated as opposed to being removed.
+
+Another experimental switch `genericsOpenSym` exists that enables this behavior
+at instantiation time, meaning templates etc can enable it specifically when
+they are being called. However this does not generate `nnkOpenSym` nodes
+(unless the other switch is enabled) and so doesn't reflect the regular
+behavior of the switch.
+
+```nim
+const value = "captured"
+template foo(x: int, body: untyped): untyped =
+  let value {.inject.} = "injected"
+  {.push experimental: "genericsOpenSym".}
+  body
+  {.pop.}
+
+proc bar[T](): string =
+  foo(123):
+    return value
+echo bar[int]() # "injected"
+
+template barTempl(): string =
+  block:
+    var res: string
+    foo(123):
+      res = value
+    res
+assert barTempl() == "injected"
+```
+
+
+VTable for methods
+==================
+
+Methods now support implementations based on a VTable by using `--experimental:vtables`. Note that the option needs to enabled
+globally. The virtual method table is stored in the type info of
+an object, which is an array of function pointers.
+
+```nim
+method foo(x: Base, ...) {.base.}
+method foo(x: Derived, ...) {.base.}
+```
+
+It roughly generates a dispatcher like
+
+```nim
+proc foo_dispatch(x: Base, ...) =
+  x.typeinfo.vtable[method_index](x, ...) # method_index is the index of the sorted order of a method
+```
+
+Methods are required to be in the same module where their type has been defined.
+
+```nim
+# types.nim
+type
+  Base* = ref object
+```
+
+```nim
+import types
+
+method foo(x: Base) {.base.} = discard
+```
+
+It gives an error: method `foo` can be defined only in the same module with its type (Base).
+
+
+asmSyntax pragma
+================
+
+The `asmSyntax` pragma is used to specify target inline assembler syntax in an `asm` statement.
+
+It prevents compiling code with different of the target CC inline asm syntax, i.e. it will not allow gcc inline asm code to be compiled with vcc.
+
+```nim
+proc nothing() =
+  asm {.asmSyntax: "gcc".}"""
+    nop
+  """
+```
+
+The current C(C++) backend implementation cannot generate code for gcc and for vcc at the same time. For example, `{.asmSyntax: "vcc".}` with the ICC compiler will not generate code with intel asm syntax, even though ICC can use both gcc-like and vcc-like asm.
diff --git a/doc/manual_experimental_strictnotnil.md b/doc/manual_experimental_strictnotnil.md
new file mode 100644
index 000000000..a6fa6cda8
--- /dev/null
+++ b/doc/manual_experimental_strictnotnil.md
@@ -0,0 +1,255 @@
+Strict not nil checking
+=========================
+
+.. default-role:: code
+.. include:: rstcommon.rst
+
+**Note:** This feature is experimental, you need to enable it with
+
+  ```nim
+  {.experimental: "strictNotNil".}
+  ```
+
+or 
+
+  ```cmd
+  nim c --experimental:strictNotNil <program>
+  ```
+
+In the second case it would check builtin and imported modules as well.
+
+It checks the nilability of ref-like types and makes dereferencing safer based on flow typing and `not nil` annotations.
+
+Its implementation is different than the `notnil` one: defined under `strictNotNil`. Keep in mind the difference in option names, be careful with distinguishing them.
+
+We check several kinds of types for nilability:
+
+- ref types
+- pointer types
+- proc types
+- cstrings
+
+nil
+-------
+
+The default kind of nilability types is the nilable kind: they can have the value `nil`.
+If you have a non-nilable type `T`, you can use `T nil` to get a nilable type for it.
+
+
+not nil
+--------
+
+You can annotate a type where nil isn't a valid value with `not nil`.
+
+  ```nim
+    type
+      NilableObject = ref object
+        a: int
+      Object = NilableObject not nil
+
+      Proc = (proc (x, y: int))
+    
+    proc p(x: Object) =
+      echo x.a # ensured to dereference without an error
+    # compiler catches this:
+    p(nil)
+    # and also this:
+    var x: NilableObject
+    if x.isNil:
+      p(x)
+    else:
+      p(x) # ok
+  ```
+
+
+
+If a type can include `nil` as a valid value, dereferencing values of the type
+is checked by the compiler: if a value which might be nil is dereferenced, this
+produces a warning by default, you can turn this into an error using
+the compiler options `--warningAsError:strictNotNil`:option:.
+
+If a type is nilable, you should dereference its values only after a `isNil` or equivalent check.
+
+local turn on/off
+---------------------
+
+You can still turn off nil checking on function/module level by using a `{.strictNotNil: off.}` pragma.
+
+..
+  Note: test that/TODO for code/manual.
+
+nilability state
+-----------------
+
+Currently, a nilable value can be `Safe`, `MaybeNil` or `Nil` : we use internally `Parent` and `Unreachable` but this is an implementation detail(a parent layer has the actual nilability).
+
+- `Safe` means it shouldn't be nil at that point: e.g. after assignment to
+  a non-nil value or `not a.isNil` check
+- `MaybeNil` means it might be nil, but it might not be nil: e.g. an argument,
+  a call argument or a value after an `if` and `else`.
+- `Nil` means it should be nil at that point; e.g. after an assignment to
+  `nil` or a `.isNil` check.
+- `Unreachable` means it shouldn't be possible to access this in this branch:
+  so we do generate a warning as well.
+
+We show an error for each dereference (`[]`, `.field`, `[index]` `()` etc.) which is of a tracked expression which is
+in `MaybeNil` or `Nil` state.
+
+
+type nilability
+----------------
+
+Types are either nilable or non-nilable.
+When you pass a param or a default value, we use the type : for nilable types we return `MaybeNil`
+and for non-nilable `Safe`.
+
+..
+  TODO: fix the manual here. (This is not great, as default values for non-nilables and nilables are usually actually `nil` , so we should think a bit more about this section.)
+
+params rules
+------------
+
+Param's nilability is detected based on type nilability. We use the type of the argument to detect the nilability.
+
+
+assignment rules
+-----------------
+
+Let's say we have `left = right`.
+
+When we assign, we pass the right's nilability to the left's expression. There should be special handling of aliasing and compound expressions which we specify in their sections. (Assignment is a possible alias `move` or `move out`).
+
+call args rules
+-----------------
+
+When we call with arguments, we have two cases when we might change the nilability.
+
+  ```nim
+  callByVar(a)
+  ```
+
+Here `callByVar` can re-assign `a`, so this might change `a`'s nilability, so we change it to `MaybeNil`.
+This is also a possible aliasing `move out` (moving out of a current alias set).
+
+  ```nim
+  call(a)
+  ```
+
+Here `call` can change a field or element of `a`, so if we have a dependant expression of `a` : e.g. `a.field`. Dependants become `MaybeNil`.
+
+
+branches rules
+---------------
+
+Branches are the reason we do nil checking like this: with flow checking. 
+Sources of branching are `if`, `while`, `for`, `and`, `or`, `case`, `try` and combinations with `return`, `break`, `continue` and `raise`
+
+We create a new layer/"scope" for each branch where we map expressions to nilability. This happens when we "fork": usually on the beginning of a construct.
+When branches "join" we usually unify their expression maps or/and nilabilities.
+
+Merging usually merges maps and alias sets: nilabilities are merged like this:
+
+  ```nim
+  template union(l: Nilability, r: Nilability): Nilability =
+    ## unify two states
+    if l == r:
+      l
+    else:
+      MaybeNil
+  ```
+
+Special handling is for `.isNil` and `== nil`, also for `not`, `and` and `or`.
+
+`not` reverses the nilability, `and` is similar to "forking" : the right expression is checked in the layer resulting from the left one and `or` is similar to "merging": the right and left expression should be both checked in the original layer.
+
+`isNil`, `== nil` make expressions `Nil`. If there is a `not` or `!= nil`, they make them `Safe`.
+We also reverse the nilability in the opposite branch: e.g. `else`.
+
+compound expressions: field, index expressions
+-----------------------------------------------
+
+We want to track also field(dot) and index(bracket) expressions.
+
+We track some of those compound expressions which might be nilable as dependants of their bases: `a.field` is changed if `a` is moved (re-assigned), 
+similarly `a[index]` is dependent on `a` and `a.field.field` on `a.field`.
+
+When we move the base, we update dependants to `MaybeNil`. Otherwise, we usually start with type nilability.
+
+When we call args, we update the nilability of their dependants to `MaybeNil` as the calls usually can change them.
+We might need to check for `strictFuncs` pure funcs and not do that then.
+
+For field expressions `a.field`, we calculate an integer value based on a hash of the tree and just accept equivalent trees as equivalent expressions.
+
+For item expression `a[index]`, we also calculate an integer value based on a hash of the tree and accept equivalent trees as equivalent expressions: for static values only.
+For now, we support only constant indices: we don't track expression with no-const indices. For those we just report a warning even if they are safe for now: one can use a local variable to workaround. For loops this might be annoying: so one should be able to turn off locally the warning using the `{.warning[StrictNotNil]:off.}`.
+
+For bracket expressions, in the future we might count `a[<any>]` as the same general expression.
+This means we should the index but otherwise handle it the same for assign (maybe "aliasing" all the non-static elements) and differentiate only for static: e.g. `a[0]` and `a[1]`.
+
+element tracking
+-----------------
+
+When we assign an object construction, we should track the fields as well: 
+
+
+  ```nim
+  var a = Nilable(field: Nilable()) # a : Safe, a.field: Safe
+  ```
+
+Usually we just track the result of an expression: probably this should apply for elements in other cases as well.
+Also related to tracking initialization of expressions/fields.
+
+unstructured control flow rules
+-------------------------------
+
+Unstructured control flow keywords as `return`, `break`, `continue`, `raise` mean that we jump from a branch out.
+This means that if there is code after the finishing of the branch, it would be run if one hasn't hit the direct parent branch of those: so it is similar to an `else`. In those cases we should use the reverse nilabilities for the local to the condition expressions. E.g.
+
+  ```nim
+  for a in c:
+    if not a.isNil:
+      b()
+      break
+    code # here a: Nil , because if not, we would have breaked
+  ```
+
+
+aliasing
+------------
+
+We support alias detection for local expressions.
+
+We track sets of aliased expressions. We start with all nilable local expressions in separate sets.
+Assignments and other changes to nilability can move / move out expressions of sets.
+
+`move`: Moving `left` to `right` means we remove `left` from its current set and unify it with the `right`'s set.
+This means it stops being aliased with its previous aliases.
+
+  ```nim
+  var left = b
+  left = right # moving left to right
+  ```
+
+`move out`: Moving out `left` might remove it from the current set and ensure that it's in its own set as a single element.
+e.g.
+
+
+  ```nim
+  var left = b
+  left = nil # moving out
+  ```
+
+..
+  initialization of non nilable and nilable values
+  -------------------------------------------------
+
+  TODO
+
+warnings and errors
+---------------------
+
+We show an error for each dereference (`[]`, `.field`, `[index]` `()` etc.) which is of a tracked expression which is
+in `MaybeNil` or `Nil` state.
+
+We might also show a history of the transitions and the reasons for them that might change the nilability of the expression.
+
diff --git a/doc/markdown_rst.md b/doc/markdown_rst.md
new file mode 100644
index 000000000..c7977f75a
--- /dev/null
+++ b/doc/markdown_rst.md
@@ -0,0 +1,349 @@
+==========================================
+Nim-flavored Markdown and reStructuredText
+==========================================
+
+:Author: Andrey Makarov
+:Version: |nimversion|
+
+.. default-role:: code
+.. include:: rstcommon.rst
+.. contents::
+
+.. importdoc:: docgen.md
+
+Both `Markdown`:idx: (md) and `reStructuredText`:idx: (RST) are markup
+languages whose goal is to typeset texts with complex structure,
+formatting and references using simple plaintext representation.
+
+Command line usage
+==================
+
+Usage (to convert Markdown into HTML):
+
+  ```cmd
+  nim md2html markdown_rst.md
+  ```
+
+Output:
+
+    You're reading it!
+
+The `md2tex`:option: command is invoked identically to `md2html`:option:,
+but outputs a ``.tex`` file instead of ``.html``.
+
+These tools embedded into Nim compiler; the compiler can output
+the result to HTML [^html] or Latex [^latex].
+
+[^html]: commands `nim doc`:cmd: for ``*.nim`` files and
+   `nim rst2html`:cmd: for ``*.rst`` files
+
+[^latex]: commands `nim doc2tex`:cmd: for ``*.nim`` and
+   `nim rst2tex`:cmd: for ``*.rst``.
+
+Full list of supported commands:
+
+===================   ======================   ============   ==============
+command               runs on...               input format   output format
+===================   ======================   ============   ==============
+`nim md2html`:cmd:    standalone md files      ``.md``        ``.html`` HTML
+`nim md2tex`:cmd:     same                     same           ``.tex`` LaTeX
+`nim rst2html`:cmd:   standalone rst files     ``.rst``       ``.html`` HTML
+`nim rst2tex`:cmd:    same                     same           ``.tex`` LaTeX
+`nim doc`:cmd:        documentation comments   ``.nim``       ``.html`` HTML
+`nim doc2tex`:cmd:    same                     same           ``.tex`` LaTeX
+`nim jsondoc`:cmd:    same                     same           ``.json`` JSON
+===================   ======================   ============   ==============
+
+
+Basic markup
+============
+
+If you are new to Markdown/RST please consider reading the following:
+
+1) [Markdown Basic Syntax]
+2) a long specification of Markdown: [CommonMark Spec]
+3) a short [quick introduction] to RST
+4) an [RST reference]: a comprehensive cheatsheet for RST
+5) a more formal 50-page [RST specification].
+
+Features
+--------
+
+A large subset is implemented with some [limitations] and
+[additional Nim-specific features].
+
+Supported common RST/Markdown features:
+
+* body elements
+  + sections
+  + transitions
+  + paragraphs
+  + bullet lists using \+, \*, \-
+  + enumerated lists using arabic numerals or alphabet
+    characters:  1. ... 2. ... *or* a. ... b. ... *or* A. ... B. ...
+  + footnotes (including manually numbered, auto-numbered, auto-numbered
+    with label, and auto-symbol footnotes) and citations
+  + field lists
+  + option lists
+  + quoted literal blocks
+  + line blocks
+  + simple tables
+  + directives (see official documentation in [RST directives list]):
+    - ``image``, ``figure`` for including images and videos
+    - ``code``
+    - ``contents`` (table of contents), ``container``, ``raw``
+    - ``include``
+    - admonitions: "attention", "caution", "danger", "error", "hint",
+      "important", "note", "tip", "warning", "admonition"
+    - substitution definitions: `replace` and `image`
+  + comments
+* inline markup
+  + *emphasis*, **strong emphasis**,
+    ``inline literals``, hyperlink references (including embedded URI),
+    substitution references, standalone hyperlinks,
+    internal links (inline and outline)
+  + \`interpreted text\` with roles ``:literal:``, ``:strong:``,
+    ``emphasis``, ``:sub:``/``:subscript:``, ``:sup:``/``:superscript:``
+    (see [RST roles list] for description).
+  + inline internal targets
+
+RST mode only features
+----------------------
+
++ RST syntax for definition lists (that is additional indentation after
+  a definition line)
++ indented literal blocks starting from ``::``
+
+Markdown-specific features
+--------------------------
+
+* Markdown tables
+* Markdown code blocks. For them the same additional arguments as for RST
+  code blocks can be provided (e.g. `test` or `number-lines`) but with
+  a one-line syntax like this:
+
+      ```nim test number-lines=10
+      echo "ok"
+      ```
+* Markdown links ``[...](...)``
+* Pandoc syntax for automatic links ``[...]``, see [Referencing] for description
+* Pandoc syntax for footnotes, including ``[^10]`` (manually numbered)
+  and ``[^someName]`` (auto-numbered with a label)
+* Markdown literal blocks indented by 4 or more spaces
+* Markdown headlines
+* Markdown block quotes
+* Markdown syntax for definition lists
+* using ``1`` as auto-enumerator in enumerated lists like RST ``#``
+  (auto-enumerator ``1`` can not be used with ``#`` in the same list)
+
+Additional Nim-specific features
+--------------------------------
+
+* referencing to definitions in external files, see
+  [Markup external referencing] section
+* directives: ``code-block`` [^Sphinx], ``title``,
+  ``index`` [^Sphinx]
+* predefined roles
+  - ``:nim:`` (default), ``:c:`` (C programming language),
+    ``:python:``, ``:yaml:``, ``:java:``, ``:cpp:`` (C++), ``:csharp`` (C#).
+    That is every language that [highlite](highlite.html) supports.
+    They turn on appropriate syntax highlighting in inline code.
+
+    .. Note:: default role for Nim files is ``:nim:``,
+              for ``*.rst`` it's currently ``:literal:``.
+
+  - generic command line highlighting roles:
+    - ``:cmd:`` for commands and common shells syntax
+    - ``:console:`` the same  for interactive sessions
+      (commands should be prepended by ``$``)
+    - ``:program:`` for executable names [^Sphinx]
+      (one can just use ``:cmd:`` on single word)
+    - ``:option:`` for command line options [^Sphinx]
+  - ``:tok:``, a role for highlighting of programming language tokens
+* ***triple emphasis*** (bold and italic) using \*\*\*
+* ``:idx:`` role for \`interpreted text\` to include the link to this
+  text into an index (example: [Nim index]).
+* double slash `//` in option lists serves as a prefix for any option that
+  starts from a word (without any leading symbols like `-`, `--`, `/`):
+
+      //compile   compile the project
+      //doc       generate documentation
+
+  Here the dummy `//` will disappear, while options `compile`:option:
+  and `doc`:option: will be left in the final document.
+* emoji / smiley symbols
+
+[^Sphinx]: similar but different from the directives of
+   Python [Sphinx directives] and [Sphinx roles] extensions
+
+.. Note:: By default Nim has ``roSupportMarkdown`` and
+   ``roSupportRawDirective`` turned **on**.
+
+.. warning:: Using Nim-specific features can cause other Markdown and
+  RST implementations to fail on your document.
+
+Referencing
+===========
+
+To be able to copy and share links Nim generates anchors for all
+main document elements:
+
+* headlines (including document title)
+* footnotes
+* explicitly set anchors: RST internal cross-references and
+  inline internal targets
+* Nim symbols (external referencing), see [Nim DocGen Tools Guide] for details.
+
+But direct use of those anchors have 2 problems:
+
+1. the anchors are usually mangled (e.g. spaces substituted to minus
+   signs, etc).
+2. manual usage of anchors is not checked, so it's easy to get broken
+   links inside your project if e.g. spelling has changed for a heading
+   or you use a wrong relative path to your document.
+
+That's why Nim implementation has syntax for using
+*original* labels for referencing.
+Such referencing can be either local/internal or external:
+
+* Local referencing (inside any given file) is defined by
+  RST standard or Pandoc Markdown User guide.
+* External (cross-document) referencing is a Nim-specific feature,
+  though it's not really different from local referencing by its syntax.
+
+Markup local referencing
+------------------------
+
+There are 2 syntax option available for referencing to objects
+inside any given file, e.g. for headlines:
+
+    Markdown                  RST
+
+    Some headline             Some headline
+    =============             =============
+
+    Ref. [Some headline]      Ref. `Some headline`_
+
+
+Markup external referencing
+---------------------------
+
+The syntax is the same as for local referencing, but the anchors are
+saved in ``.idx`` files, so one needs to generate them beforehand,
+and they should be loaded by an `.. importdoc::` directive.
+E.g. if we want to reference section "Some headline" in ``file1.md``
+from ``file2.md``, then ``file2.md`` may look like:
+
+```
+.. importdoc:: file1.md
+
+Ref. [Some headline]
+```
+
+```cmd
+nim md2html --index:only file1.md  # creates ``htmldocs/file1.idx``
+nim md2html file2.md               # creates ``htmldocs/file2.html``
+```
+
+To allow cross-references between any files in any order (especially, if
+circular references are present), it's strongly recommended
+to make a run for creating all the indexes first:
+
+```cmd
+nim md2html --index:only file1.md  # creates ``htmldocs/file1.idx``
+nim md2html --index:only file2.md  # creates ``htmldocs/file2.idx``
+nim md2html file1.md               # creates ``htmldocs/file1.html``
+nim md2html file2.md               # creates ``htmldocs/file2.html``
+```
+
+and then one can freely reference any objects as if these 2 documents
+are actually 1 file.
+
+Other
+=====
+
+Idiosyncrasies
+--------------
+
+Currently we do **not** aim at 100% Markdown or RST compatibility in inline
+markup recognition rules because that would provide very little user value.
+This parser has 2 modes for inline markup:
+
+1) Markdown-like mode which is enabled by `roPreferMarkdown` option
+   (turned **on** by default).
+
+   .. Note:: RST features like directives are still turned **on**
+
+2) Compatibility mode which is RST rules.
+
+.. Note:: in both modes the parser interpretes text between single
+     backticks (code) identically:
+     backslash does not escape; the only exception: ``\`` folowed by `
+     does escape so that we can always input a single backtick ` in
+     inline code. However that makes impossible to input code with
+     ``\`` at the end in *single* backticks, one must use *double*
+     backticks:
+
+         `\`   -- WRONG
+         ``\`` -- GOOD
+         So single backticks can always be input: `\`` will turn to ` code
+
+.. Attention::
+   We don't support some obviously poor design choices of Markdown (or RST).
+
+   - no support for the rule of 2 spaces causing a line break in Markdown
+     (use RST "line blocks" syntax for making line breaks)
+
+   - interpretation of Markdown block quotes is also slightly different,
+     e.g. case
+
+         >>> foo
+         > bar
+         >>baz
+
+     is a single 3rd-level quote `foo bar baz` in original Markdown, while
+     in Nim we naturally see it as 3rd-level quote `foo` + 1st level `bar` +
+     2nd level `baz`:
+
+     >>> foo
+     > bar
+     >>baz
+
+Limitations
+-----------
+
+* no Unicode support in character width calculations
+* body elements
+  - no roman numerals in enumerated lists
+  - no doctest blocks
+  - no grid tables
+  - some directives are missing (check official [RST directives list]):
+    ``parsed-literal``, ``sidebar``, ``topic``, ``math``, ``rubric``,
+    ``epigraph``, ``highlights``, ``pull-quote``, ``compound``,
+    ``table``, ``csv-table``, ``list-table``, ``section-numbering``,
+    ``header``, ``footer``, ``meta``, ``class``
+    - no ``role`` directives and no custom interpreted text roles
+    - some standard roles are not supported (check [RST roles list])
+    - no generic admonition support
+* inline markup
+  - no simple-inline-markup
+  - no embedded aliases
+
+Additional resources
+--------------------
+
+* See [Nim DocGen Tools Guide](docgen.html) for the details about
+  `nim doc`:cmd: command and idiosyncrasies of documentation markup in
+  ``.nim`` files and Nim programming language projects.
+* See also documentation for [rst module](rst.html) -- Nim RST/Markdown parser.
+
+.. _Markdown Basic Syntax: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax
+.. _CommonMark Spec: https://spec.commonmark.org/0.30
+.. _quick introduction: https://docutils.sourceforge.io/docs/user/rst/quickstart.html
+.. _RST reference: https://docutils.sourceforge.io/docs/user/rst/quickref.html
+.. _RST specification: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html
+.. _RST directives list: https://docutils.sourceforge.io/docs/ref/rst/directives.html
+.. _RST roles list: https://docutils.sourceforge.io/docs/ref/rst/roles.html
+.. _Nim index: https://nim-lang.org/docs/theindex.html
+.. _Sphinx directives: https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html
+.. _Sphinx roles: https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html
diff --git a/doc/mm.md b/doc/mm.md
new file mode 100644
index 000000000..5e0d2f3b9
--- /dev/null
+++ b/doc/mm.md
@@ -0,0 +1,95 @@
+=======================
+Nim's Memory Management
+=======================
+
+.. default-role:: code
+.. include:: rstcommon.rst
+
+:Author: Andreas Rumpf
+:Version: |nimversion|
+
+..
+
+
+> "The road to hell is paved with good intentions."
+
+
+Multi-paradigm Memory Management Strategies
+===========================================
+
+.. default-role:: option
+
+Nim offers multiple different memory management strategies.
+To choose the memory management strategy use the `--mm:` switch.
+
+ .. hint:: **The recommended switch for newly written Nim code is `--mm:orc`.**
+
+
+ARC/ORC
+-------
+
+ORC is the default memory management strategy. It is a memory
+management mode primarily based on reference counting. Reference cycles are
+handled by a cycle collection mechanism based on "trial deletion".
+Since algorithms based on "tracing" are not used, the runtime behavior is oblivious to the involved heap and stack sizes.
+
+The reference counting operations (= "RC ops") do not use atomic instructions and do not have to --
+instead entire subgraphs are *moved* between threads. The Nim compiler also aggressively
+optimizes away RC ops and exploits [move semantics](destructors.html#move-semantics).
+
+Nim performs a fair share of optimizations for ARC/ORC; you can inspect what it did
+to your time critical function via `--expandArc:functionName`. Likewise, you can inspect the whole module via `--expandArc:fileName`.
+
+`--mm:arc` uses the same mechanism as `--mm:orc`, but it leaves out the cycle collector.
+Both ARC and ORC offer deterministic performance for `hard realtime`:idx: systems, but
+ARC can be easier to reason about for people coming from Ada/C++/C -- roughly speaking
+the memory for a variable is freed when it goes "out of scope".
+
+We generally advise you to use the `acyclic` annotation in order to optimize away the
+cycle collector's overhead
+but `--mm:orc` also produces more machine code than `--mm:arc`, so if you're on a target
+where code size matters and you know that your code does not produce cycles, you can
+use `--mm:arc`. Notice that the default `async`:idx: implementation produces cycles
+and leaks memory with `--mm:arc`, in other words, for `async` you need to use `--mm:orc`.
+
+
+
+Other MM modes
+--------------
+
+.. note:: The `refc` GC is incremental, thread-local and not "stop-the-world".
+
+--mm:refc    It's a deferred reference counting based garbage collector
+  with a simple Mark&Sweep backup GC in order to collect cycles.
+  Heaps are thread-local. [This document](refc.html) contains further information.
+--mm:markAndSweep  Simple Mark-And-Sweep based garbage collector.
+  Heaps are thread-local.
+--mm:boehm    Boehm based garbage collector, it offers a shared heap.
+--mm:go    Go's garbage collector, useful for interoperability with Go.
+  Offers a shared heap.
+
+--mm:none    No memory management strategy nor a garbage collector. Allocated memory is
+  simply never freed. You should use `--mm:arc` instead.
+
+Here is a comparison of the different memory management modes:
+
+================== ======== ================= ============== ====== =================== ===================
+Memory Management  Heap     Reference Cycles  Stop-The-World Atomic Valgrind compatible Command line switch
+================== ======== ================= ============== ====== =================== ===================
+ORC                Shared   Cycle Collector   No             No     Yes                 `--mm:orc`
+ARC                Shared   Leak              No             No     Yes                 `--mm:arc`
+Atomic ARC         Shared   Leak              No             Yes    Yes                 `--mm:atomicArc`
+RefC               Local    Cycle Collector   No             No     No                  `--mm:refc`
+Mark & Sweep       Local    Cycle Collector   No             No     No                  `--mm:markAndSweep`
+Boehm              Shared   Cycle Collector   Yes            No     No                  `--mm:boehm`
+Go                 Shared   Cycle Collector   Yes            No     No                  `--mm:go`
+None               Manual   Manual            Manual         Manual Manual              `--mm:none`
+================== ======== ================= ============== ====== =================== ===================
+
+.. default-role:: code
+.. include:: rstcommon.rst
+
+JavaScript's garbage collector is used for the [JavaScript and NodeJS](
+backends.html#backends-the-javascript-target) compilation targets.
+The [NimScript](nims.html) target uses the memory management strategy built into
+the Nim compiler.
diff --git a/doc/mytest.cfg b/doc/mytest.cfg
index be1118c46..db058b8f3 100644
--- a/doc/mytest.cfg
+++ b/doc/mytest.cfg
@@ -3,6 +3,8 @@
 
 [Common]
 cc=gcc     # '=' and ':' are the same
+--foo="bar"   # '--cc' and 'cc' are the same, 'bar' and '"bar"' are the same (except for '#')
+macrosym: "#"  # Note that '#' is interpreted as a comment without the quotation
 --verbose
 
 [Windows]
diff --git a/doc/nep1.md b/doc/nep1.md
new file mode 100644
index 000000000..3d2a0cef3
--- /dev/null
+++ b/doc/nep1.md
@@ -0,0 +1,335 @@
+============================
+Standard Library Style Guide
+============================
+
+:Author: Clay Sweetser, Dominik Picheta
+:Version: |nimversion|
+
+.. default-role:: code
+.. include:: rstcommon.rst
+.. contents::
+
+
+Introduction
+============
+Although Nim supports a variety of code and formatting styles, it is
+nevertheless beneficial that certain community efforts, such as the standard
+library, should follow a consistent set of style guidelines when suitable.
+This enhancement proposal aims to list a series of guidelines that the standard
+library should follow.
+
+Note that there can be exceptions to these rules. Nim being as flexible as it
+is, there will be parts of this style guide that don't make sense in certain
+contexts. Furthermore, just as
+[Python's style guide](http://legacy.python.org/dev/peps/pep-0008/) changes
+over time, this style guide will too.
+
+These rules will only be enforced for contributions to the Nim
+codebase and official projects, such as the Nim compiler, the standard library,
+and the various official tools such as C2Nim.
+
+----------------
+Style Guidelines
+----------------
+
+Spacing and Whitespace Conventions
+-----------------------------------
+
+- Lines should be no longer than 80 characters. Limiting the amount of
+  information present on each line makes for more readable code - the reader
+  has smaller chunks to process.
+
+- Two spaces should be used for indentation of blocks; tabstops are not allowed
+  (the compiler enforces this). Using spaces means that the appearance of code
+  is more consistent across editors. Unlike spaces, tabstop width varies across
+  editors, and not all editors provide means of changing this width.
+
+- Although use of whitespace for stylistic reasons other than the ones endorsed
+  by this guide are allowed, careful thought should be put into such practices.
+  Not all editors support automatic alignment of code sections, and re-aligning
+  long sections of code by hand can quickly become tedious.
+
+    ```nim
+    # This is bad, as the next time someone comes
+    # to edit this code block, they
+    # must re-align all the assignments again:
+    type
+      WordBool*    = int16
+      CalType*     = int
+      ... # 5 lines later
+      CalId*       = int
+      LongLong*    = int64
+      LongLongPtr* = ptr LongLong
+    ```
+
+
+Naming Conventions
+------------------
+
+- Type identifiers should be in PascalCase. All other identifiers should be in
+  camelCase with the exception of constants which **may** use PascalCase but
+  are not required to.
+
+    ```nim
+    # Constants can start with either a lower case or upper case letter.
+    const aConstant = 42
+    const FooBar = 4.2
+
+    var aVariable = "Meep" # Variables must start with a lowercase letter.
+
+    # Types must start with an uppercase letter.
+    type
+      FooBar = object
+    ```
+
+  For constants coming from a C/C++ wrapper, ALL_UPPERCASE are allowed, but ugly.
+  (Why shout CONSTANT? Constants do no harm, variables do!)
+
+- When naming types that come in value, pointer, and reference varieties, use a
+  regular name for the variety that is to be used the most, and add a "Obj",
+  "Ref", or "Ptr" suffix for the other varieties. If there is no single variety
+  that will be used the most, add the suffixes to the pointer variants only. The
+  same applies to C/C++ wrappers.
+
+    ```nim
+    type
+      Handle = object # Will be used most often
+        fd: int64
+      HandleRef = ref Handle # Will be used less often
+    ```
+
+- Exception and Error types should have the "Error" or "Defect" suffix.
+
+    ```nim
+    type
+      ValueError = object of CatchableError
+      AssertionDefect = object of Defect
+      Foo = object of Exception # bad style, try to inherit CatchableError or Defect
+    ```
+
+- Unless marked with the `{.pure.}` pragma, members of enums should have an
+  identifying prefix, such as an abbreviation of the enum's name.
+
+    ```nim
+    type
+      PathComponent = enum
+        pcDir
+        pcLinkToDir
+        pcFile
+        pcLinkToFile
+    ```
+
+- Non-pure enum values should use camelCase whereas pure enum values should use
+  PascalCase.
+
+    ```nim
+    type
+      PathComponent {.pure.} = enum
+        Dir
+        LinkToDir
+        File
+        LinkToFile
+    ```
+
+- In the age of HTTP, HTML, FTP, TCP, IP, UTF, WWW it is foolish to pretend
+  these are somewhat special words requiring all uppercase. Instead treat them
+  as what they are: Real words. So it's `parseUrl` rather than
+  `parseURL`, `checkHttpHeader` instead of `checkHTTPHeader` etc.
+
+- Operations like `mitems` or `mpairs` (or the now deprecated `mget`)
+  that allow a *mutating view* into some data structure should start with an `m`.
+- When both in-place mutation and 'returns transformed copy' are available the latter
+  is a past participle of the former:
+
+  - reverse and reversed in algorithm
+  - sort and sorted
+  - rotate and rotated
+
+- When the 'returns transformed copy' version already exists like `strutils.replace`
+  an in-place version should get an ``-In`` suffix (`replaceIn` for this example).
+
+
+- Use `subjectVerb`, not `verbSubject`, e.g.: `fileExists`, not `existsFile`.
+
+The stdlib API is designed to be **easy to use** and consistent. Ease of use is
+measured by the number of calls to achieve a concrete high level action. The
+ultimate goal is that the programmer can *guess* a name.
+
+The library uses a simple naming scheme that makes use of common abbreviations
+to keep the names short but meaningful.
+
+
+===================     ============   ======================================
+English word            To use         Notes
+===================     ============   ======================================
+initialize              initFoo        initializes a value type `Foo`
+new                     newFoo         initializes a reference type `Foo`
+                                       via `new` or a value type `Foo`
+                                       with reference semantics.
+this or self            self           for method like procs, e.g.:
+                                       `proc fun(self: Foo, a: int)`
+                                       rationale: `self` is more unique in English
+                                       than `this`, and `foo` would not be DRY.
+find                    find           should return the position where
+                                       something was found; for a bool result
+                                       use `contains`
+contains                contains       often short for `find() >= 0`
+append                  add            use `add` instead of `append`
+compare                 cmp            should return an int with the
+                                       `< 0` `== 0` or `> 0` semantics;
+                                       for a bool result use `sameXYZ`
+put                     put, `[]=`     consider overloading `[]=` for put
+get                     get, `[]`      consider overloading `[]` for get;
+                                       consider to not use `get` as a
+                                       prefix: `len` instead of `getLen`
+length                  len            also used for *number of elements*
+size                    size, len      size should refer to a byte size
+capacity                cap
+memory                  mem            implies a low-level operation
+items                   items          default iterator over a collection
+pairs                   pairs          iterator over (key, value) pairs
+delete                  delete, del    del is supposed to be faster than
+                                       delete, because it does not keep
+                                       the order; delete keeps the order
+remove                  delete, del    inconsistent right now
+include                 incl
+exclude                 excl
+command                 cmd
+execute                 exec
+environment             env
+variable                var
+value                   value, val     val is preferred, inconsistent right
+                                       now
+executable              exe
+directory               dir
+path                    path           path is the string "/usr/bin" (for
+                                       example), dir is the content of
+                                       "/usr/bin"; inconsistent right now
+extension               ext
+separator               sep
+column                  col, column    col is preferred, inconsistent right
+                                       now
+application             app
+configuration           cfg
+message                 msg
+argument                arg
+object                  obj
+parameter               param
+operator                opr
+procedure               proc
+function                func
+coordinate              coord
+rectangle               rect
+point                   point
+symbol                  sym
+literal                 lit
+string                  str
+identifier              ident
+indentation             indent
+===================     ============   ======================================
+
+
+Coding Conventions
+------------------
+
+- The `return` statement should ideally be used when its control-flow properties
+  are required. Use a procedure's implicit `result` variable whenever possible.
+  This improves readability.
+
+    ```nim
+    proc repeat(text: string, x: int): string =
+      result = ""
+
+      for i in 0..x:
+        result.add($i)
+    ```
+
+- Use a proc when possible, only using the more powerful facilities of macros,
+  templates, iterators, and converters when necessary.
+
+- Use the `let` statement (not the `var` statement) when declaring variables that
+  do not change within their scope. Using the `let` statement ensures that
+  variables remain immutable, and gives those who read the code a better idea
+  of the code's purpose.
+
+
+Conventions for multi-line statements and expressions
+-----------------------------------------------------
+
+- Tuples which are longer than one line should indent their parameters.
+
+    ```nim
+    type
+      LongTupleA = tuple[
+        wordyTupleMemberOne: int, wordyTupleMemberTwo: string,
+        wordyTupleMemberThree: float]
+    ```
+
+- Similarly, any procedure and procedure type declarations that are longer
+  than one line should do the same thing. Double indent may be used to
+  distinguish them from the body that follows - this applies to all constructs
+  with a body (if, while, etc).
+
+    ```nim
+    type
+      EventCallback = proc(
+        timeReceived: Time, errorCode: int, event: Event,
+        output: var string)
+
+    proc lotsOfArguments(
+        argOne: string, argTwo: int, argThree: float,
+        argFour: proc(), argFive: bool, argSix: int
+    ): GenericType[int, string] {.heyLookALongPragma.} =
+      discard
+    ```
+
+- Multi-line procedure calls should continue indented (like multi-line procedure
+  declarations).
+
+    ```nim
+    startProcess(
+      nimExecutable, currentDirectory, compilerArguments
+      environment, processOptions)
+    ```
+
+Previous versions of this guide advocated vertical alignment along the opening
+brace / parenthesis - both styles are permissible with a preference for the
+current style in new code.
+
+Miscellaneous
+-------------
+
+- Use `a..b` instead of `a .. b`, except when `b` contains an operator, for example `a .. -3`.
+  Likewise with `a..<b`, `a..^b` and other operators starting with `..`.
+
+- Use `std` prefix for standard library modules, namely use `std/os` for single module and
+  use `std/[os, sysrand, posix]` for multiple modules.
+
+- Prefer multiline triple quote literals to start with a newline; it's semantically identical
+  (it's a feature of triple quote literals) but clearer because it aligns with the next line:
+
+  use this:
+
+    ```nim
+    let a = """
+    foo
+    bar
+    """
+    ```
+
+  instead of:
+
+    ```nim
+    let a = """foo
+    bar
+    """
+    ```
+
+- A getter API for a private field `foo` should preferably be named `foo`, not `getFoo`.
+  A getter-like API should preferably be named `getFoo`, not `foo` if:
+    * the API has side effects
+    * or the cost is not `O(1)`
+  For in between cases, there is no clear guideline.
+
+- Likewise with a setter API, replacing `foo` with `foo=` and `getFoo` with `setFoo`
+  in the above text.
diff --git a/doc/nep1.rst b/doc/nep1.rst
deleted file mode 100644
index c4d445681..000000000
--- a/doc/nep1.rst
+++ /dev/null
@@ -1,201 +0,0 @@
-==============================================
-Nim Enhancement Proposal #1 - Standard Library Style Guide
-==============================================
-:Author: Clay Sweetser, Dominik Picheta
-:Version: |nimversion|
-
-.. contents::
-
-
-Introduction
-============
-Although Nim supports a variety of code and formatting styles, it is
-nevertheless beneficial that certain community efforts, such as the standard
-library, should follow a consistent set of style guidelines when suitable.
-This enhancement proposal aims to list a series of guidelines that the standard
-library should follow.
-
-Note that there can be exceptions to these rules. Nim being as flexible as it
-is, there will be parts of this style guide that don't make sense in certain
-contexts. Furthermore, just as
-`Python's style guide<http://legacy.python.org/dev/peps/pep-0008/>`_ changes
-over time, this style guide will too.
-
-These rules will only be enforced for contributions to the Nim
-codebase and official projects, such as the Nim compiler, the standard library,
-and the various official tools such as C2Nim.
-
-----------------
-Style Guidelines
-----------------
-
-Spacing and Whitespace Conventions
------------------------------------
-
-- Lines should be no longer than 80 characters. Limiting the amount of
-  information present on each line makes for more readable code - the reader
-  has smaller chunks to process.
-
-- Two spaces should be used for indentation of blocks; tabstops are not allowed
-  (the compiler enforces this). Using spaces means that the appearance of code
-  is more consistent across editors. Unlike spaces, tabstop width varies across
-  editors, and not all editors provide means of changing this width.
-
-- Although use of whitespace for stylistic reasons other than the ones endorsed
-  by this guide are allowed, careful thought should be put into such practices.
-  Not all editors support automatic alignment of code sections, and re-aligning
-  long sections of code by hand can quickly become tedious.
-
-  .. code-block:: nim
-    # This is bad, as the next time someone comes
-    # to edit this code block, they
-    # must re-align all the assignments again:
-    type
-      WordBool*    = int16
-      CalType*     = int
-      ... # 5 lines later
-      CalId*       = int
-      LongLong*    = int64
-      LongLongPtr* = ptr LongLong
-
-
-Naming Conventions
--------------------------
-
-Note: While the rules outlined below are the *current* naming conventions,
-these conventions have not always been in place. Previously, the naming
-conventions for identifiers followed the Pascal tradition of prefixes which
-indicated the base type of the identifier - PFoo for pointer and reference
-types, TFoo for value types, EFoo for exceptions, etc. Though this has since
-changed, there are many places in the standard library which still use this
-convention. Such style remains in place purely for legacy reasons, and will be
-changed in the future.
-
-- Type identifiers should be in PascalCase. All other identifiers should be in
-  camelCase with the exception of constants which **may** use PascalCase but
-  are not required to.
-
-  .. code-block:: nim
-    # Constants can start with either a lower case or upper case letter.
-    const aConstant = 42
-    const FooBar = 4.2
-
-    var aVariable = "Meep" # Variables must start with a lowercase letter.
-
-    # Types must start with an uppercase letter.
-    type
-      FooBar = object
-
-  For constants coming from a C/C++ wrapper, ALL_UPPERCASE are allowed, but ugly.
-  (Why shout CONSTANT? Constants do no harm, variables do!)
-
-- When naming types that come in value, pointer, and reference varieties, use a
-  regular name for the variety that is to be used the most, and add a "Obj",
-  "Ref", or "Ptr" suffix for the other varieties. If there is no single variety
-  that will be used the most, add the suffixes to the pointer variants only. The
-  same applies to C/C++ wrappers.
-
-  .. code-block:: nim
-    type
-      Handle = object # Will be used most often
-        fd: int64
-      HandleRef = ref Handle # Will be used less often
-
-- Exception and Error types should have the "Error" suffix.
-
-  .. code-block:: nim
-    type
-      UnluckyError = object of Exception
-
-- Unless marked with the `{.pure.}` pragma, members of enums should have an
-  identifying prefix, such as an abbreviation of the enum's name.
-
-  .. code-block:: nim
-    type
-      PathComponent = enum
-        pcDir
-        pcLinkToDir
-        pcFile
-        pcLinkToFile
-
-- Non-pure enum values should use camelCase whereas pure enum values should use
-  PascalCase.
-
-  .. code-block:: nim
-    type
-      PathComponent {.pure.} = enum
-        Dir
-        LinkToDir
-        File
-        LinkToFile
-
-- In the age of HTTP, HTML, FTP, TCP, IP, UTF, WWW it is foolish to pretend
-  these are somewhat special words requiring all uppercase. Instead treat them
-  as what they are: Real words. So it's ``parseUrl`` rather than
-  ``parseURL``, ``checkHttpHeader`` instead of ``checkHTTPHeader`` etc.
-
-- Operations like ``mitems`` or ``mpairs`` (or the now deprecated ``mget``)
-  that allow a *mutating view* into some data structure should start with an ``m``.
-- When both in-place mutation and 'returns transformed copy' are available the latter
-  is a past participle of the former:
-
-  - reverse and reversed in algorithm
-  - sort and sorted
-  - rotate and rotated
-
-- When the 'returns transformed copy' version already exists like ``strutils.replace``
-  an in-place version should get an ``-In`` suffix (``replaceIn`` for this example).
-
-
-Coding Conventions
-------------------
-
-- The 'return' statement should ideally be used when its control-flow properties
-  are required. Use a procedure's implicit 'result' variable whenever possible.
-  This improves readability.
-
-  .. code-block:: nim
-    proc repeat(text: string, x: int): string =
-      result = ""
-
-      for i in 0 .. x:
-        result.add($i)
-
-- Use a proc when possible, only using the more powerful facilities of macros,
-  templates, iterators, and converters when necessary.
-
-- Use the ``let`` statement (not the ``var`` statement) when declaring variables that
-  do not change within their scope. Using the ``let`` statement ensures that
-  variables remain immutable, and gives those who read the code a better idea
-  of the code's purpose.
-
-
-Conventions for multi-line statements and expressions
------------------------------------------------------
-
-- Tuples which are longer than one line should indent their parameters to
-  align with the parameters above it.
-
-  .. code-block:: nim
-    type
-      LongTupleA = tuple[wordyTupleMemberOne: int, wordyTupleMemberTwo: string,
-                         wordyTupleMemberThree: float]
-
-- Similarly, any procedure and procedure type declarations that are longer
-  than one line should do the same thing.
-
-  .. code-block:: nim
-    type
-      EventCallback = proc (timeReceived: Time, errorCode: int, event: Event,
-                            output: var string)
-
-    proc lotsOfArguments(argOne: string, argTwo: int, argThree: float
-                         argFour: proc(), argFive: bool): int
-                        {.heyLookALongPragma.} =
-
-- Multi-line procedure calls should continue on the same column as the opening
-  parenthesis (like multi-line procedure declarations).
-
-  .. code-block:: nim
-    startProcess(nimExecutable, currentDirectory, compilerArguments
-                 environment, processOptions)
diff --git a/doc/nimc.md b/doc/nimc.md
new file mode 100644
index 000000000..38558454b
--- /dev/null
+++ b/doc/nimc.md
@@ -0,0 +1,844 @@
+===================================
+   Nim Compiler User Guide
+===================================
+
+:Author: Andreas Rumpf
+:Version: |nimversion|
+
+.. default-role:: code
+.. include:: rstcommon.rst
+.. contents::
+
+..
+
+> "Look at you, hacker. A pathetic creature of meat and bone, panting and
+> sweating as you run through my corridors. How can you challenge a perfect,
+> immortal machine?"
+
+
+Introduction
+============
+
+This document describes the usage of the *Nim compiler*
+on the different supported platforms. It is not a definition of the Nim
+programming language (which is covered in the [manual](manual.html)).
+
+Nim is free software; it is licensed under the
+[MIT License](http://www.opensource.org/licenses/mit-license.php).
+
+
+Compiler Usage
+==============
+
+Command-line switches
+---------------------
+All options that take a `PATH` or `DIR` argument are subject to path substitution:
+
+- `$nim`: The global nim prefix path
+- `$lib`: The stdlib path
+- `$home` and `~`: The user's home path
+- `$config`: The directory of the module currently being compiled
+- `$projectname`: The project file's name without file extension
+- `$projectpath` and `$projectdir`: The project file's path
+- `$nimcache`: The nimcache path
+
+
+Basic command-line switches are:
+
+.. no syntax highlighting in the below included files at the moment
+.. default-role:: code
+
+Usage:
+
+.. include:: basicopt.txt
+
+----
+
+Advanced command-line switches are:
+
+.. include:: advopt.txt
+
+
+.. include:: rstcommon.rst
+
+List of warnings
+----------------
+
+Each warning can be activated individually with `--warning:NAME:on|off`:option: or
+in a `push` pragma with `{.warning[NAME]:on|off.}`.
+
+==========================       ============================================
+Name                             Description
+==========================       ============================================
+CannotOpenFile                   Some file not essential for the compiler's
+                                 working could not be opened.
+OctalEscape                      The code contains an unsupported octal
+                                 sequence.
+Deprecated                       The code uses a deprecated symbol.
+ConfigDeprecated                 The project makes use of a deprecated config
+                                 file.
+SmallLshouldNotBeUsed            The letter 'l' should not be used as an
+                                 identifier.
+EachIdentIsTuple                 The code contains a confusing `var`
+                                 declaration.
+CStringConv                      Warn about dangerous implicit conversions
+                                 to `cstring`.
+EnumConv                         Warn about conversions from enum to enum.
+AnyEnumConv                      Warn about any conversions to an enum type.
+HoleEnumConv                     Warn about conversion to an enum with
+                                 holes. These conversions are unsafe.
+ResultUsed                       Warn about the usage of the
+                                 built-in `result` variable.
+User                             Some user-defined warning.
+==========================       ============================================
+
+
+List of hints
+-------------
+
+Each hint can be activated individually with `--hint:NAME:on|off`:option: or in a
+`push` pragma with `{.hint[NAME]:on|off.}`.
+
+==========================       ============================================
+Name                             Description
+==========================       ============================================
+CC                               Shows when the C compiler is called.
+CodeBegin
+CodeEnd
+CondTrue
+Conf                             A config file was loaded.
+ConvToBaseNotNeeded
+ConvFromXtoItselfNotNeeded
+Dependency
+Exec                             Program is executed.
+ExprAlwaysX
+ExtendedContext
+GCStats                          Dumps statistics about the Garbage Collector.
+GlobalVar                        Shows global variables declarations.
+Link                             Linking phase.
+Name
+Path                             Search paths modifications.
+Pattern
+Performance
+Processing                       Artifact being compiled.
+QuitCalled
+Source                           The source line that triggered a diagnostic
+                                 message.
+StackTrace
+Success, SuccessX                Successful compilation of a library or a binary.
+User
+UserRaw
+XDeclaredButNotUsed              Unused symbols in the code.
+==========================       ============================================
+
+
+Verbosity levels
+----------------
+
+=====  ============================================
+Level  Description
+=====  ============================================
+0      Minimal output level for the compiler.
+1      Displays compilation of all the compiled files, including those imported
+       by other modules or through the [compile pragma](
+       manual.html#implementation-specific-pragmas-compile-pragma).
+       This is the default level.
+2      Displays compilation statistics, enumerates the dynamic
+       libraries that will be loaded by the final binary, and dumps to
+       standard output the result of applying [a filter to the source code](
+       filters.html) if any filter was used during compilation.
+3      In addition to the previous levels dumps a debug stack trace
+       for compiler developers.
+=====  ============================================
+
+
+Compile-time symbols
+--------------------
+
+Through the `-d:x`:option: or `--define:x`:option: switch you can define compile-time
+symbols for conditional compilation. The defined switches can be checked in
+source code with the [when statement](
+manual.html#statements-and-expressions-when-statement) and
+[defined proc](system.html#defined,untyped). The typical use of this switch is
+to enable builds in release mode (`-d:release`:option:) where optimizations are
+enabled for better performance. Another common use is the `-d:ssl`:option: switch to
+activate SSL sockets.
+
+Additionally, you may pass a value along with the symbol: `-d:x=y`:option:
+which may be used in conjunction with the [compile-time define
+pragmas](manual.html#implementation-specific-pragmas-compileminustime-define-pragmas)
+to override symbols during build time.
+
+Compile-time symbols are completely **case insensitive** and underscores are
+ignored too. `--define:FOO`:option: and `--define:foo`:option: are identical.
+
+Compile-time symbols starting with the `nim` prefix are reserved for the
+implementation and should not be used elsewhere.
+
+==========================       ============================================
+Name                             Description
+==========================       ============================================
+nimStdSetjmp                     Use the standard `setjmp()/longjmp()` library
+                                 functions for setjmp-based exceptions. This is
+                                 the default on most platforms.
+nimSigSetjmp                     Use `sigsetjmp()/siglongjmp()` for setjmp-based exceptions.
+nimRawSetjmp                     Use `_setjmp()/_longjmp()` on POSIX and `_setjmp()/longjmp()`
+                                 on Windows, for setjmp-based exceptions. It's the default on
+                                 BSDs and BSD-like platforms, where it's significantly faster
+                                 than the standard functions.
+nimBuiltinSetjmp                 Use `__builtin_setjmp()/__builtin_longjmp()` for setjmp-based
+                                 exceptions. This will not work if an exception is being thrown
+                                 and caught inside the same procedure. Useful for benchmarking.
+==========================       ============================================
+
+
+Configuration files
+-------------------
+
+**Note:** The *project file name* is the name of the ``.nim`` file that is
+passed as a command-line argument to the compiler.
+
+
+The `nim`:cmd: executable processes configuration files in the following
+directories (in this order; later files overwrite previous settings):
+
+1) ``$nim/config/nim.cfg``, ``/etc/nim/nim.cfg`` (UNIX) or
+   ``<Nim's installation directory>\config\nim.cfg`` (Windows).
+   This file can be skipped with the `--skipCfg`:option: command line option.
+2) If environment variable `XDG_CONFIG_HOME` is defined,
+   ``$XDG_CONFIG_HOME/nim/nim.cfg`` or ``~/.config/nim/nim.cfg`` (POSIX) or
+   ``%APPDATA%/nim/nim.cfg`` (Windows).
+   This file can be skipped with the `--skipUserCfg`:option: command line
+   option.
+3) ``$parentDir/nim.cfg`` where ``$parentDir`` stands for any parent
+   directory of the project file's path.
+   These files can be skipped with the `--skipParentCfg`:option:
+   command-line option.
+4) ``$projectDir/nim.cfg`` where ``$projectDir`` stands for the project
+   file's path.
+   This file can be skipped with the `--skipProjCfg`:option:
+   command-line option.
+5) A project can also have a project-specific configuration file named
+   ``$project.nim.cfg`` that resides in the same directory as ``$project.nim``.
+   This file can be skipped with the `--skipProjCfg`:option:
+   command-line option.
+
+
+Command-line settings have priority over configuration file settings.
+
+The default build of a project is a `debug build`:idx:. To compile a
+`release build`:idx: define the `release` symbol:
+
+  ```cmd
+  nim c -d:release myproject.nim
+  ```
+
+To compile a `dangerous release build`:idx: define the `danger` symbol:
+
+  ```cmd
+  nim c -d:danger myproject.nim
+  ```
+
+
+Search path handling
+--------------------
+
+Nim has the concept of a global search path (PATH) that is queried to
+determine where to find imported modules or include files. If multiple files are
+found an ambiguity error is produced.
+
+`nim dump`:cmd: shows the contents of the PATH.
+
+However before the PATH is used the current directory is checked for the
+file's existence. So if PATH contains ``$lib`` and ``$lib/bar`` and the
+directory structure looks like this:
+
+    $lib/x.nim
+    $lib/bar/x.nim
+    foo/x.nim
+    foo/main.nim
+    other.nim
+
+And `main` imports `x`, `foo/x` is imported. If `other` imports `x`
+then both ``$lib/x.nim`` and ``$lib/bar/x.nim`` match but ``$lib/x.nim`` is used
+as it is the first match.
+
+
+Generated C code directory
+--------------------------
+The generated files that Nim produces all go into a subdirectory called
+``nimcache``. Its full path is
+
+- ``$XDG_CACHE_HOME/nim/$projectname(_r|_d)`` or ``~/.cache/nim/$projectname(_r|_d)``
+  on Posix
+- ``$HOME\nimcache\$projectname(_r|_d)`` on Windows.
+
+The `_r` suffix is used for release builds, `_d` is for debug builds.
+
+This makes it easy to delete all generated files.
+
+The `--nimcache`:option:
+[compiler switch][command-line switches] can be used to
+to change the ``nimcache`` directory.
+
+However, the generated C code is not platform-independent. C code generated for
+Linux does not compile on Windows, for instance. The comment on top of the
+C file lists the OS, CPU, and CC the file has been compiled for.
+
+
+Compiler Selection
+==================
+
+To change the compiler from the default compiler (at the command line):
+
+  ```cmd
+  nim c --cc:llvm_gcc --compile_only myfile.nim
+  ```
+
+This uses the configuration defined in ``config\nim.cfg`` for `llvm_gcc`:cmd:.
+
+If nimcache already contains compiled code from a different compiler for the same project,
+add the `-f`:option: flag to force all files to be recompiled.
+
+The default compiler is defined at the top of ``config\nim.cfg``.
+Changing this setting affects the compiler used by `koch`:cmd: to (re)build Nim.
+
+To use the `CC` environment variable, use `nim c --cc:env myfile.nim`:cmd:.
+To use the `CXX` environment variable, use `nim cpp --cc:env myfile.nim`:cmd:.
+`--cc:env`:option: is available since Nim version 1.4.
+
+
+Cross-compilation
+=================
+
+To cross compile, use for example:
+
+  ```cmd
+  nim c --cpu:i386 --os:linux --compileOnly --genScript myproject.nim
+  ```
+
+Then move the C code and the compile script `compile_myproject.sh`:cmd: to your
+Linux i386 machine and run the script.
+
+Another way is to make Nim invoke a cross compiler toolchain:
+
+  ```cmd
+  nim c --cpu:arm --os:linux myproject.nim
+  ```
+
+For cross compilation, the compiler invokes a C compiler named like
+`$cpu.$os.$cc` (for example `arm.linux.gcc`) with options defined in
+`$cpu.$os.$cc.options.always`. The configuration system is used to provide
+meaningful defaults. For example, for Linux on a 32-bit ARM CPU, your
+configuration file should contain something like:
+
+    arm.linux.gcc.path = "/usr/bin"
+    arm.linux.gcc.exe = "arm-linux-gcc"
+    arm.linux.gcc.linkerexe = "arm-linux-gcc"
+    arm.linux.gcc.options.always = "-w -fmax-errors=3"
+
+Cross-compilation for Windows
+=============================
+
+To cross-compile for Windows from Linux or macOS using the MinGW-w64 toolchain:
+
+  ```cmd
+  nim c -d:mingw myproject.nim
+  # `nim r` also works, running the binary via `wine` or `wine64`:
+  nim r -d:mingw --eval:'import os; echo "a" / "b"'
+  ```
+
+Use `--cpu:i386`:option: or `--cpu:amd64`:option: to switch the CPU architecture.
+
+The MinGW-w64 toolchain can be installed as follows:
+
+  ```cmd
+  apt install mingw-w64   # Ubuntu
+  yum install mingw32-gcc
+  yum install mingw64-gcc # CentOS - requires EPEL
+  brew install mingw-w64  # OSX
+  ```
+
+
+Cross-compilation for Android
+=============================
+
+There are two ways to compile for Android: terminal programs (Termux) and with
+the NDK (Android Native Development Kit).
+
+The first one is to treat Android as a simple Linux and use
+[Termux](https://wiki.termux.com) to connect and run the Nim compiler
+directly on android as if it was Linux. These programs are console-only
+programs that can't be distributed in the Play Store.
+
+Use regular `nim c`:cmd: inside termux to make Android terminal programs.
+
+Normal Android apps are written in Java, to use Nim inside an Android app
+you need a small Java stub that calls out to a native library written in
+Nim using the [NDK](https://developer.android.com/ndk). You can also use
+[native-activity](https://developer.android.com/ndk/samples/sample_na)
+to have the Java stub be auto-generated for you.
+
+Use `nim c -c --cpu:arm --os:android -d:androidNDK --noMain:on`:cmd: to
+generate the C source files you need to include in your Android Studio
+project. Add the generated C files to CMake build script in your Android
+project. Then do the final compile with Android Studio which uses Gradle
+to call CMake to compile the project.
+
+Because Nim is part of a library it can't have its own C-style `main()`:c:
+so you would need to define your own `android_main`:c: and init the Java
+environment, or use a library like SDL2 or GLFM to do it. After the Android
+stuff is done, it's very important to call `NimMain()`:c: in order to
+initialize Nim's garbage collector and to run the top level statements
+of your program.
+
+  ```Nim
+  proc NimMain() {.importc.}
+  proc glfmMain*(display: ptr GLFMDisplay) {.exportc.} =
+    NimMain() # initialize garbage collector memory, types and stack
+  ```
+
+
+The name `NimMain` can be influenced via the `--nimMainPrefix:prefix` switch.
+Use `--nimMainPrefix:MyLib` and the function to call is named `MyLibNimMain`.
+
+
+Cross-compilation for iOS
+=========================
+
+To cross-compile for iOS you need to be on a macOS computer and use XCode.
+Normal languages for iOS development are Swift and Objective C. Both of these
+use LLVM and can be compiled into object files linked together with C, C++
+or Objective C code produced by Nim.
+
+Use `nim c -c --os:ios --noMain:on`:cmd: to generate C files and include them in
+your XCode project. Then you can use XCode to compile, link, package and
+sign everything.
+
+Because Nim is part of a library it can't have its own C-style `main()`:c: so you
+would need to define `main` that calls `autoreleasepool` and
+`UIApplicationMain` to do it, or use a library like SDL2 or GLFM. After
+the iOS setup is done, it's very important to call `NimMain()`:c: to
+initialize Nim's garbage collector and to run the top-level statements
+of your program.
+
+  ```Nim
+  proc NimMain() {.importc.}
+  proc glfmMain*(display: ptr GLFMDisplay) {.exportc.} =
+    NimMain() # initialize garbage collector memory, types and stack
+  ```
+
+Note: XCode's "make clean" gets confused about the generated nim.c files,
+so you need to clean those files manually to do a clean build.
+
+The name `NimMain` can be influenced via the `--nimMainPrefix:prefix` switch.
+Use `--nimMainPrefix:MyLib` and the function to call is named `MyLibNimMain`.
+
+
+Cross-compilation for Nintendo Switch
+=====================================
+
+Simply add `--os:nintendoswitch`:option:
+to your usual `nim c`:cmd: or `nim cpp`:cmd: command and set the `passC`:option:
+and `passL`:option: command line switches to something like:
+
+  ```cmd
+  nim c ... --d:nimAllocPagesViaMalloc --mm:orc --passC="-I$DEVKITPRO/libnx/include" ...
+  --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx"
+  ```
+
+or setup a ``nim.cfg`` file like so:
+
+    #nim.cfg
+    --mm:orc
+    --d:nimAllocPagesViaMalloc
+    --define:nimInheritHandles
+    --passC="-I$DEVKITPRO/libnx/include"
+    --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx"
+
+The devkitPro setup must be the same as the default with their new installer
+[here for Mac/Linux](https://github.com/devkitPro/pacman/releases) or
+[here for Windows](https://github.com/devkitPro/installer/releases).
+
+For example, with the above-mentioned config:
+
+  ```cmd
+  nim c --os:nintendoswitch switchhomebrew.nim
+  ```
+
+This will generate a file called ``switchhomebrew.elf`` which can then be turned into
+an nro file with the `elf2nro`:cmd: tool in the devkitPro release. Examples can be found at
+[the nim-libnx github repo](https://github.com/jyapayne/nim-libnx.git).
+
+There are a few things that don't work because the devkitPro libraries don't support them.
+They are:
+
+1. Waiting for a subprocess to finish. A subprocess can be started, but right
+   now it can't be waited on, which sort of makes subprocesses a bit hard to use
+2. Dynamic calls. Switch OS (Horizon) doesn't support dynamic libraries, so dlopen/dlclose are not available.
+3. mqueue. Sadly there are no mqueue headers.
+4. ucontext. No headers for these either. No coroutines for now :(
+5. nl_types. No headers for this.
+6. As mmap is not supported, the nimAllocPagesViaMalloc option has to be used.
+
+GPU Compilation
+===============
+
+Compiling for GPU computation can be achieved with `--cc:nvcc` for CUDA with nvcc, or with `--cc:hipcc` for AMD GPUs with HIP. Both compilers require building for C++ with `nim cpp`.
+
+Here's a very simple CUDA kernel example using emit, which can be compiled with `nim cpp --cc:nvcc --define:"useMalloc" hello_kernel.nim` assuming you have the CUDA toolkit installed.
+
+```nim
+{.emit: """
+__global__ void add(int a, int b) {
+  int c;
+  c = a + b;
+}
+""".}
+
+proc main() =
+  {.emit: """
+  add<<<1,1>>>(2,7);
+  """.}
+
+main()
+```
+
+DLL generation
+==============
+
+**Note**: The same rules apply to `lib*.so` shared object files on UNIX. For better
+readability only the DLL version is described here.
+
+Nim supports the generation of DLLs. However, there must be only one
+instance of the GC per process/address space. This instance is contained in
+``nimrtl.dll``. This means that every generated Nim DLL depends
+on ``nimrtl.dll``. To generate the "nimrtl.dll" file, use the command:
+
+  ```cmd
+  nim c -d:release lib/nimrtl.nim
+  ```
+
+To link against ``nimrtl.dll`` use the command:
+
+  ```cmd
+  nim c -d:useNimRtl myprog.nim
+  ```
+
+
+Additional compilation switches
+===============================
+
+The standard library supports a growing number of `useX` conditional defines
+affecting how some features are implemented. This section tries to give a
+complete list.
+
+======================   =========================================================
+Define                   Effect
+======================   =========================================================
+`release`                Turns on the optimizer.
+                         More aggressive optimizations are possible, e.g.:
+                         `--passC:-ffast-math`:option: (but see issue #10305)
+`danger`                 Turns off all runtime checks and turns on the optimizer.
+`useFork`                Makes `osproc` use `fork`:c: instead of `posix_spawn`:c:.
+`useNimRtl`              Compile and link against ``nimrtl.dll``.
+`useMalloc`              Makes Nim use C's `malloc`:idx: instead of Nim's
+                         own memory manager, albeit prefixing each allocation with
+                         its size to support clearing memory on reallocation.
+                         This only works with `--mm:none`:option:,
+                         `--mm:arc`:option: and `--mm:orc`:option:.
+`useRealtimeGC`          Enables support of Nim's GC for *soft* realtime
+                         systems. See the documentation of the [refc](refc.html)
+                         for further information.
+`logGC`                  Enable GC logging to stdout.
+`nodejs`                 The JS target is actually ``node.js``.
+`ssl`                    Enables OpenSSL support for the sockets module.
+`memProfiler`            Enables memory profiling for the native GC.
+`uClibc`                 Use uClibc instead of libc. (Relevant for Unix-like OSes)
+`checkAbi`               When using types from C headers, add checks that compare
+                         what's in the Nim file with what's in the C header.
+                         This may become enabled by default in the future.
+`tempDir`                This symbol takes a string as its value, like
+                         `--define:tempDir:/some/temp/path`:option: to override
+                         the temporary directory returned by `os.getTempDir()`.
+                         The value **should** end with a directory separator
+                         character. (Relevant for the Android platform)
+`useShPath`              This symbol takes a string as its value, like
+                         `--define:useShPath:/opt/sh/bin/sh`:option: to override
+                         the path for the `sh`:cmd: binary, in cases where it is
+                         not located in the default location ``/bin/sh``.
+`noSignalHandler`        Disable the crash handler from ``system.nim``.
+`globalSymbols`          Load all `{.dynlib.}` libraries with the `RTLD_GLOBAL`:c:
+                         flag on Posix systems to resolve symbols in subsequently
+                         loaded libraries.
+`lto`                    Enable link-time optimization in the backend compiler and
+                         linker.
+`lto_incremental`        Enable link-time optimization and additionally enable
+                         incremental linking for compilers that support it.
+                         Currently only clang and vcc.
+`strip`                  Strip debug symbols added by the backend compiler from
+                         the executable.
+======================   =========================================================
+
+
+
+Additional Features
+===================
+
+This section describes Nim's additional features that are not listed in the
+Nim manual. Some of the features here only make sense for the C code
+generator and are subject to change.
+
+
+LineDir option
+--------------
+The `--lineDir`:option: option can be turned on or off. If turned on the
+generated C code contains `#line`:c: directives. This may be helpful for
+debugging with GDB.
+
+
+StackTrace option
+-----------------
+If the `--stackTrace`:option: option is turned on, the generated C contains code to
+ensure that proper stack traces are given if the program crashes or some uncaught exception is raised.
+
+
+LineTrace option
+----------------
+The `--lineTrace`:option: option implies the `stackTrace`:option: option.
+If turned on,
+the generated C contains code to ensure that proper stack traces with line
+number information are given if the program crashes or an uncaught exception
+is raised.
+
+
+DynlibOverride
+==============
+
+By default Nim's `dynlib` pragma causes the compiler to generate
+`GetProcAddress`:cpp: (or their Unix counterparts)
+calls to bind to a DLL. With the `dynlibOverride`:option: command line switch this
+can be prevented and then via `--passL`:option: the static library can be linked
+against. For instance, to link statically against Lua this command might work
+on Linux:
+
+  ```cmd
+  nim c --dynlibOverride:lua --passL:liblua.lib program.nim
+  ```
+
+
+Backend language options
+========================
+
+The typical compiler usage involves using the `compile`:option: or `c`:option:
+command to transform a ``.nim`` file into one or more ``.c`` files which are then
+compiled with the platform's C compiler into a static binary. However, there
+are other commands to compile to C++, Objective-C, or JavaScript. More details
+can be read in the [Nim Backend Integration document](backends.html).
+
+
+Nim documentation tools
+=======================
+
+Nim provides the `doc`:idx: command to generate HTML
+documentation from ``.nim`` source files. Only exported symbols will appear in
+the output. For more details see [the docgen documentation](docgen.html).
+
+Nim idetools integration
+========================
+
+Nim provides language integration with external IDEs through the
+idetools command. See the documentation of [idetools](idetools.html)
+for further information.
+
+..
+  Nim interactive mode
+  ====================
+
+  The Nim compiler supports an interactive mode. This is also known as
+  a `REPL`:idx: (*read eval print loop*). If Nim has been built with the
+  `-d:nimUseLinenoise` switch, it uses the GNU readline library for terminal
+  input management. To start Nim in interactive mode use the command
+  `nim secret`. To quit use the `quit()` command. To determine whether an input
+  line is an incomplete statement to be continued these rules are used:
+
+  1. The line ends with ``[-+*/\\<>!\?\|%&$@~,;:=#^]\s*$`` (operator symbol followed by optional whitespace).
+  2. The line starts with a space (indentation).
+  3. The line is within a triple quoted string literal. However, the detection
+     does not work if the line contains more than one `"""`.
+
+
+Nim for embedded systems
+========================
+
+While the default Nim configuration is targeted for optimal performance on
+modern PC hardware and operating systems with ample memory, it is very well
+possible to run Nim code and a good part of the Nim standard libraries on small
+embedded microprocessors with only a few kilobytes of memory.
+
+A good start is to use the `any` operating target together with the
+`malloc` memory allocator and the `arc` garbage collector. For example:
+
+  ```cmd
+  nim c --os:any --mm:arc -d:useMalloc [...] x.nim
+  ```
+
+- `--mm:arc`:option: will enable the reference counting memory management instead
+  of the default garbage collector. This enables Nim to use heap memory which
+  is required for strings and seqs, for example.
+
+- The `--os:any`:option: target makes sure Nim does not depend on any specific
+  operating system primitives. Your platform should support only some basic
+  ANSI C library `stdlib` and `stdio` functions which should be available
+  on almost any platform.
+
+- The `-d:useMalloc`:option: option configures Nim to use only the standard C memory
+  manage primitives `malloc()`:c:, `free()`:c:, `realloc()`:c:.
+
+If your platform does not provide these functions it should be trivial to
+provide an implementation for them and link these to your program.
+
+For targets with very restricted memory, it might be beneficial to pass some
+additional flags to both the Nim compiler and the C compiler and/or linker
+to optimize the build for size. For example, the following flags can be used
+when targeting a gcc compiler:
+
+`--opt:size -d:lto -d:strip`:option:
+
+The `--opt:size`:option: flag instructs Nim to optimize code generation for small
+size (with the help of the C compiler), the `-d:lto`:option: flags enable link-time
+optimization in the compiler and linker, the `-d:strip`:option: strips debug symbols.
+
+Check the [Cross-compilation] section for instructions on how to compile the
+program for your target.
+
+
+nimAllocPagesViaMalloc
+----------------------
+
+Nim's default allocator is based on TLSF, this algorithm was designed for embedded
+devices. This allocator gets blocks/pages of memory via a currently undocumented
+`osalloc` API which usually uses POSIX's `mmap` call. On many environments `mmap`
+is not available but C's `malloc` is. You can use the `nimAllocPagesViaMalloc`
+define to use `malloc` instead of `mmap`. `nimAllocPagesViaMalloc` is currently
+only supported with `--mm:arc` or `--mm:orc`. (Since version 1.6)
+
+nimPage256 / nimPage512 / nimPage1k
+===================================
+
+Adjust the page size for Nim's GC allocator. This enables using
+`nimAllocPagesViaMalloc` on devices with less RAM. The default
+page size requires too much RAM to work.
+
+Recommended settings:
+
+- < 32 kB of RAM use `nimPage256`
+
+- < 512 kB of RAM use `nimPage512`
+
+- < 2 MB of RAM use `nimPage1k`
+
+Initial testing hasn't shown much difference between 512B or 1kB page sizes
+in terms of performance or latency. Using `nimPages256` will limit the
+total amount of allocatable RAM.
+
+nimMemAlignTiny
+===============
+
+Sets `MemAlign` to `4` bytes which reduces the memory alignment
+to better match some embedded devices.
+
+Thread stack size
+=================
+
+Nim's thread API provides a simple wrapper around more advanced
+RTOS task features. Customizing the stack size and stack guard size can
+be done by setting `-d:nimThreadStackSize=16384` or `-d:nimThreadStackGuard=32`.
+
+Currently only Zephyr, NuttX and FreeRTOS support these configurations.
+
+Nim for realtime systems
+========================
+
+See the `--mm:arc` or `--mm:orc` memory management settings in
+[MM](mm.html) for further information.
+
+
+Signal handling in Nim
+======================
+
+The Nim programming language has no concept of Posix's signal handling
+mechanisms. However, the standard library offers some rudimentary support
+for signal handling, in particular, segmentation faults are turned into
+fatal errors that produce a stack trace. This can be disabled with the
+`-d:noSignalHandler`:option: switch.
+
+
+Optimizing for Nim
+==================
+
+Nim has no separate optimizer, but the C code that is produced is very
+efficient. Most C compilers have excellent optimizers, so usually it is
+not needed to optimize one's code. Nim has been designed to encourage
+efficient code: The most readable code in Nim is often the most efficient
+too.
+
+However, sometimes one has to optimize. Do it in the following order:
+
+1. switch off the embedded debugger (it is **slow**!)
+2. turn on the optimizer and turn off runtime checks
+3. profile your code to find where the bottlenecks are
+4. try to find a better algorithm
+5. do low-level optimizations
+
+This section can only help you with the last item.
+
+
+Optimizing string handling
+--------------------------
+
+String assignments are sometimes expensive in Nim: They are required to
+copy the whole string. However, the compiler is often smart enough to not copy
+strings. Due to the argument passing semantics, strings are never copied when
+passed to subroutines. The compiler does not copy strings that are a result of
+a procedure call, because the callee returns a new string anyway.
+Thus it is efficient to do:
+
+  ```Nim
+  var s = procA() # assignment will not copy the string; procA allocates a new
+                  # string already
+  ```
+
+However, it is not efficient to do:
+
+  ```Nim
+  var s = varA    # assignment has to copy the whole string into a new buffer!
+  ```
+
+For `let` symbols a copy is not always necessary:
+
+  ```Nim
+  let s = varA    # may only copy a pointer if it safe to do so
+  ```
+
+The compiler optimizes string case statements: A hashing scheme is used for them
+if several different string constants are used. So code like this is reasonably
+efficient:
+
+  ```Nim
+  case normalize(k.key)
+  of "name": c.name = v
+  of "displayname": c.displayName = v
+  of "version": c.version = v
+  of "os": c.oses = split(v, {';'})
+  of "cpu": c.cpus = split(v, {';'})
+  of "authors": c.authors = split(v, {';'})
+  of "description": c.description = v
+  of "app":
+    case normalize(v)
+    of "console": c.app = appConsole
+    of "gui": c.app = appGUI
+    else: quit(errorStr(p, "expected: console or gui"))
+  of "license": c.license = UnixToNativePath(k.value)
+  else: quit(errorStr(p, "unknown variable: " & k.key))
+  ```
diff --git a/doc/nimc.rst b/doc/nimc.rst
deleted file mode 100644
index 29dbdea42..000000000
--- a/doc/nimc.rst
+++ /dev/null
@@ -1,543 +0,0 @@
-===================================
-   Nim Compiler User Guide
-===================================
-
-:Author: Andreas Rumpf
-:Version: |nimversion|
-
-.. contents::
-
-  "Look at you, hacker. A pathetic creature of meat and bone, panting and
-  sweating as you run through my corridors. How can you challenge a perfect,
-  immortal machine?"
-
-
-Introduction
-============
-
-This document describes the usage of the *Nim compiler*
-on the different supported platforms. It is not a definition of the Nim
-programming language (therefore is the `manual <manual.html>`_).
-
-Nim is free software; it is licensed under the
-`MIT License <http://www.opensource.org/licenses/mit-license.php>`_.
-
-
-Compiler Usage
-==============
-
-Command line switches
----------------------
-Basic command line switches are:
-
-Usage:
-
-.. include:: basicopt.txt
-
-----
-
-Advanced command line switches are:
-
-.. include:: advopt.txt
-
-
-
-List of warnings
-----------------
-
-Each warning can be activated individually with ``--warning[NAME]:on|off`` or
-in a ``push`` pragma.
-
-==========================       ============================================
-Name                             Description
-==========================       ============================================
-CannotOpenFile                   Some file not essential for the compiler's
-                                 working could not be opened.
-OctalEscape                      The code contains an unsupported octal
-                                 sequence.
-Deprecated                       The code uses a deprecated symbol.
-ConfigDeprecated                 The project makes use of a deprecated config
-                                 file.
-SmallLshouldNotBeUsed            The letter 'l' should not be used as an
-                                 identifier.
-EachIdentIsTuple                 The code contains a confusing ``var``
-                                 declaration.
-ShadowIdent                      A local variable shadows another local
-                                 variable of an outer scope.
-User                             Some user defined warning.
-==========================       ============================================
-
-
-Verbosity levels
-----------------
-
-=====  ============================================
-Level  Description
-=====  ============================================
-0      Minimal output level for the compiler.
-1      Displays compilation of all the compiled files, including those imported
-       by other modules or through the `compile pragma<#compile-pragma>`_.
-       This is the default level.
-2      Displays compilation statistics, enumerates the dynamic
-       libraries that will be loaded by the final binary and dumps to
-       standard output the result of applying `a filter to the source code
-       <filters.html>`_ if any filter was used during compilation.
-3      In addition to the previous levels dumps a debug stack trace
-       for compiler developers.
-=====  ============================================
-
-
-Compile time symbols
---------------------
-
-Through the ``-d:x`` or ``--define:x`` switch you can define compile time
-symbols for conditional compilation. The defined switches can be checked in
-source code with the `when statement <manual.html#when-statement>`_ and
-`defined proc <system.html#defined>`_. The typical use of this switch is to
-enable builds in release mode (``-d:release``) where certain safety checks are
-omitted for better performance. Another common use is the ``-d:ssl`` switch to
-activate `SSL sockets <sockets.html>`_.
-
-Additionally, you may pass a value along with the symbol: ``-d:x=y``
-which may be used in conjunction with the `compile time define
-pragmas<manual.html#implementation-specific-pragmas-compile-time-define-pragmas>`_
-to override symbols during build time.
-
-
-Configuration files
--------------------
-
-**Note:** The *project file name* is the name of the ``.nim`` file that is
-passed as a command line argument to the compiler.
-
-
-The ``nim`` executable processes configuration files in the following
-directories (in this order; later files overwrite previous settings):
-
-1) ``$nim/config/nim.cfg``, ``/etc/nim.cfg`` (UNIX) or ``%NIMROD%/config/nim.cfg`` (Windows). This file can be skipped with the ``--skipCfg`` command line option.
-2) ``$HOME/.config/nim.cfg`` (POSIX) or  ``%APPDATA%/nim.cfg`` (Windows). This file can be skipped with the ``--skipUserCfg`` command line option.
-3) ``$parentDir/nim.cfg`` where ``$parentDir`` stands for any parent  directory of the project file's path. These files can be skipped with the ``--skipParentCfg`` command line option.
-4) ``$projectDir/nim.cfg`` where ``$projectDir`` stands for the project  file's path. This file can be skipped with the ``--skipProjCfg`` command line option.
-5) A project can also have a project specific configuration file named ``$project.nim.cfg`` that resides in the same directory as ``$project.nim``. This file can be skipped with the ``--skipProjCfg`` command line option.
-
-
-Command line settings have priority over configuration file settings.
-
-The default build of a project is a `debug build`:idx:. To compile a
-`release build`:idx: define the ``release`` symbol::
-
-  nim c -d:release myproject.nim
-
-
-Search path handling
---------------------
-
-Nim has the concept of a global search path (PATH) that is queried to
-determine where to find imported modules or include files. If multiple files are
-found an ambiguity error is produced.
-
-``nim dump`` shows the contents of the PATH.
-
-However before the PATH is used the current directory is checked for the
-file's existence. So if PATH contains ``$lib`` and ``$lib/bar`` and the
-directory structure looks like this::
-
-  $lib/x.nim
-  $lib/bar/x.nim
-  foo/x.nim
-  foo/main.nim
-  other.nim
-
-And ``main`` imports ``x``, ``foo/x`` is imported. If ``other`` imports ``x``
-then both ``$lib/x.nim`` and ``$lib/bar/x.nim`` match and so the compiler
-should reject it. Currently however this check is not implemented and instead
-the first matching file is used.
-
-
-Generated C code directory
---------------------------
-The generated files that Nim produces all go into a subdirectory called
-``nimcache`` in your project directory. This makes it easy to delete all
-generated files. Files generated in this directory follow a naming logic which
-you can read about in the `Nim Backend Integration document
-<backends.html#nimcache-naming-logic>`_.
-
-However, the generated C code is not platform independent. C code generated for
-Linux does not compile on Windows, for instance. The comment on top of the
-C file lists the OS, CPU and CC the file has been compiled for.
-
-
-Compilation cache
-=================
-
-**Warning**: The compilation cache is still highly experimental!
-
-The ``nimcache`` directory may also contain so called `rod`:idx:
-or `symbol files`:idx:. These files are pre-compiled modules that are used by
-the compiler to perform `incremental compilation`:idx:. This means that only
-modules that have changed since the last compilation (or the modules depending
-on them etc.) are re-compiled. However, per default no symbol files are
-generated; use the ``--symbolFiles:on`` command line switch to activate them.
-
-Unfortunately due to technical reasons the ``--symbolFiles:on`` needs
-to *aggregate* some generated C code. This means that the resulting executable
-might contain some cruft even with dead code elimination. So
-the final release build should be done with ``--symbolFiles:off``.
-
-Due to the aggregation of C code it is also recommended that each project
-resides in its own directory so that the generated ``nimcache`` directory
-is not shared between different projects.
-
-
-Compiler Selection
-==================
-
-To change the compiler from the default compiler (at the command line)::
-
-  nim c --cc:llvm_gcc --compile_only myfile.nim
-
-This uses the configuration defined in ``config\nim.cfg`` for ``lvm_gcc``.
-
-If nimcache already contains compiled code from a different compiler for the same project,
-add the ``-f`` flag to force all files to be recompiled.
-
-The default compiler is defined at the top of ``config\nim.cfg``.  Changing this setting
-affects the compiler used by ``koch`` to (re)build Nim.
-
-
-Cross compilation
-=================
-
-To cross compile, use for example::
-
-  nim c --cpu:i386 --os:linux --compileOnly --genScript myproject.nim
-
-Then move the C code and the compile script ``compile_myproject.sh`` to your
-Linux i386 machine and run the script.
-
-Another way is to make Nim invoke a cross compiler toolchain::
-
-  nim c --cpu:arm --os:linux myproject.nim
-
-For cross compilation, the compiler invokes a C compiler named
-like ``$cpu.$os.$cc`` (for example arm.linux.gcc) and the configuration
-system is used to provide meaningful defaults. For example for ``ARM`` your
-configuration file should contain something like::
-
-  arm.linux.gcc.path = "/usr/bin"
-  arm.linux.gcc.exe = "arm-linux-gcc"
-  arm.linux.gcc.linkerexe = "arm-linux-gcc"
-
-
-DLL generation
-==============
-
-Nim supports the generation of DLLs. However, there must be only one
-instance of the GC per process/address space. This instance is contained in
-``nimrtl.dll``. This means that every generated Nim DLL depends
-on ``nimrtl.dll``. To generate the "nimrtl.dll" file, use the command::
-
-  nim c -d:release lib/nimrtl.nim
-
-To link against ``nimrtl.dll`` use the command::
-
-  nim c -d:useNimRtl myprog.nim
-
-**Note**: Currently the creation of ``nimrtl.dll`` with thread support has
-never been tested and is unlikely to work!
-
-
-Additional compilation switches
-===============================
-
-The standard library supports a growing number of ``useX`` conditional defines
-affecting how some features are implemented. This section tries to give a
-complete list.
-
-==================   =========================================================
-Define               Effect
-==================   =========================================================
-``release``          Turns off runtime checks and turns on the optimizer.
-``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
-                     its size to support clearing memory on reallocation.
-                     This only works with ``gc:none``.
-``useRealtimeGC``    Enables support of Nim's GC for *soft* realtime
-                     systems. See the documentation of the `gc <gc.html>`_
-                     for further information.
-``nodejs``           The JS target is actually ``node.js``.
-``ssl``              Enables OpenSSL support for the sockets module.
-``memProfiler``      Enables memory profiling for the native GC.
-``uClibc``           Use uClibc instead of libc. (Relevant for Unix-like OSes)
-``checkAbi``         When using types from C headers, add checks that compare
-                     what's in the Nim file with what's in the C header
-                     (requires a C compiler with _Static_assert support, like
-                     any C11 compiler)
-``tempDir``          This symbol takes a string as its value, like
-                     ``--define:tempDir:/some/temp/path`` to override the
-                     temporary directory returned by ``os.getTempDir()``.
-                     The value **should** end with a directory separator
-                     character. (Relevant for the Android platform)
-``useShPath``        This symbol takes a string as its value, like
-                     ``--define:useShPath:/opt/sh/bin/sh`` to override the
-                     path for the ``sh`` binary, in cases where it is not
-                     located in the default location ``/bin/sh``
-==================   =========================================================
-
-
-
-Additional Features
-===================
-
-This section describes Nim's additional features that are not listed in the
-Nim manual. Some of the features here only make sense for the C code
-generator and are subject to change.
-
-
-LineDir option
---------------
-The ``lineDir`` option can be turned on or off. If turned on the
-generated C code contains ``#line`` directives. This may be helpful for
-debugging with GDB.
-
-
-StackTrace option
------------------
-If the ``stackTrace`` option is turned on, the generated C contains code to
-ensure that proper stack traces are given if the program crashes or an
-uncaught exception is raised.
-
-
-LineTrace option
-----------------
-The ``lineTrace`` option implies the ``stackTrace`` option. If turned on,
-the generated C contains code to ensure that proper stack traces with line
-number information are given if the program crashes or an uncaught exception
-is raised.
-
-Debugger option
----------------
-The ``debugger`` option enables or disables the *Embedded Nim Debugger*.
-See the documentation of endb_ for further information.
-
-Hot code reloading
-------------------
-**Note:** At the moment hot code reloading is supported only in
-JavaScript projects.
-
-The `hotCodeReloading`:idx: option enables special compilation mode where changes in
-the code can be applied automatically to a running program. The code reloading
-happens at the granularity of an individual module. When a module is reloaded,
-Nim will preserve the state of all global variables which are initialized with
-a standard variable declaration in the code. All other top level code will be
-executed repeatedly on each reload. If you want to prevent this behavior, you
-can guard a block of code with the ``once`` construct:
-
-.. code-block:: Nim
-  var settings = initTable[string, string]()
-
-  once:
-    myInit()
-
-    for k, v in loadSettings():
-      settings[k] = v
-
-If you want to reset the state of a global variable on each reload, just
-re-assign a value anywhere within the top-level code:
-
-.. code-block:: Nim
-  var lastReload: Time
-
-  lastReload = now()
-  resetProgramState()
-
-**Known limitations:** In the JavaScript target, global variables using the
-``codegenDecl`` pragma will be re-initialized on each reload. Please guard the
-initialization with a `once` block to work-around this.
-
-**Usage in JavaScript projects:**
-
-Once your code is compiled for hot reloading, you can use a framework such
-as `LiveReload <http://livereload.com/>` or `BrowserSync <https://browsersync.io/>`
-to implement the actual reloading behavior in your project.
-
-Breakpoint pragma
------------------
-The *breakpoint* pragma was specially added for the sake of debugging with
-ENDB. See the documentation of `endb <endb.html>`_ for further information.
-
-
-DynlibOverride
-==============
-
-By default Nim's ``dynlib`` pragma causes the compiler to generate
-``GetProcAddress`` (or their Unix counterparts)
-calls to bind to a DLL. With the ``dynlibOverride`` command line switch this
-can be prevented and then via ``--passL`` the static library can be linked
-against. For instance, to link statically against Lua this command might work
-on Linux::
-
-  nim c --dynlibOverride:lua --passL:liblua.lib program.nim
-
-
-Backend language options
-========================
-
-The typical compiler usage involves using the ``compile`` or ``c`` command to
-transform a ``.nim`` file into one or more ``.c`` files which are then
-compiled with the platform's C compiler into a static binary. However there
-are other commands to compile to C++, Objective-C or Javascript. More details
-can be read in the `Nim Backend Integration document <backends.html>`_.
-
-
-Nim documentation tools
-=======================
-
-Nim provides the `doc`:idx: and `doc2`:idx: commands to generate HTML
-documentation from ``.nim`` source files. Only exported symbols will appear in
-the output. For more details `see the docgen documentation <docgen.html>`_.
-
-Nim idetools integration
-========================
-
-Nim provides language integration with external IDEs through the
-idetools command. See the documentation of `idetools <idetools.html>`_
-for further information.
-
-..
-  Nim interactive mode
-  ====================
-
-  The Nim compiler supports an interactive mode. This is also known as
-  a `REPL`:idx: (*read eval print loop*). If Nim has been built with the
-  ``-d:useGnuReadline`` switch, it uses the GNU readline library for terminal
-  input management. To start Nim in interactive mode use the command
-  ``nim secret``. To quit use the ``quit()`` command. To determine whether an input
-  line is an incomplete statement to be continued these rules are used:
-
-  1. The line ends with ``[-+*/\\<>!\?\|%&$@~,;:=#^]\s*$`` (operator symbol followed by optional whitespace).
-  2. The line starts with a space (indentation).
-  3. The line is within a triple quoted string literal. However, the detection
-     does not work if the line contains more than one ``"""``.
-
-
-Nim for embedded systems
-========================
-
-The standard library can be avoided to a point where C code generation
-for 16bit micro controllers is feasible. Use the `standalone`:idx: target
-(``--os:standalone``) for a bare bones standard library that lacks any
-OS features.
-
-To make the compiler output code for a 16bit target use the ``--cpu:avr``
-target.
-
-For example, to generate code for an `AVR`:idx: processor use this command::
-
-  nim c --cpu:avr --os:standalone --genScript x.nim
-
-For the ``standalone`` target one needs to provide
-a file ``panicoverride.nim``.
-See ``tests/manyloc/standalone/panicoverride.nim`` for an example
-implementation.  Additionally, users should specify the
-amount of heap space to use with the ``-d:StandaloneHeapSize=<size>``
-command line switch.  Note that the total heap size will be
-``<size> * sizeof(float64)``.
-
-
-Nim for realtime systems
-========================
-
-See the documentation of Nim's soft realtime `GC <gc.html>`_ for further
-information.
-
-
-Debugging with Nim
-==================
-
-Nim comes with its own *Embedded Nim Debugger*. See
-the documentation of endb_ for further information.
-
-
-Optimizing for Nim
-==================
-
-Nim has no separate optimizer, but the C code that is produced is very
-efficient. Most C compilers have excellent optimizers, so usually it is
-not needed to optimize one's code. Nim has been designed to encourage
-efficient code: The most readable code in Nim is often the most efficient
-too.
-
-However, sometimes one has to optimize. Do it in the following order:
-
-1. switch off the embedded debugger (it is **slow**!)
-2. turn on the optimizer and turn off runtime checks
-3. profile your code to find where the bottlenecks are
-4. try to find a better algorithm
-5. do low-level optimizations
-
-This section can only help you with the last item.
-
-
-Optimizing string handling
---------------------------
-
-String assignments are sometimes expensive in Nim: They are required to
-copy the whole string. However, the compiler is often smart enough to not copy
-strings. Due to the argument passing semantics, strings are never copied when
-passed to subroutines. The compiler does not copy strings that are a result from
-a procedure call, because the callee returns a new string anyway.
-Thus it is efficient to do:
-
-.. code-block:: Nim
-  var s = procA() # assignment will not copy the string; procA allocates a new
-                  # string already
-
-However it is not efficient to do:
-
-.. code-block:: Nim
-  var s = varA    # assignment has to copy the whole string into a new buffer!
-
-For ``let`` symbols a copy is not always necessary:
-
-.. code-block:: Nim
-  let s = varA    # may only copy a pointer if it safe to do so
-
-
-If you know what you're doing, you can also mark single string (or sequence)
-objects as `shallow`:idx:\:
-
-.. code-block:: Nim
-  var s = "abc"
-  shallow(s) # mark 's' as shallow string
-  var x = s  # now might not copy the string!
-
-Usage of ``shallow`` is always safe once you know the string won't be modified
-anymore, similar to Ruby's `freeze`:idx:.
-
-
-The compiler optimizes string case statements: A hashing scheme is used for them
-if several different string constants are used. So code like this is reasonably
-efficient:
-
-.. code-block:: Nim
-  case normalize(k.key)
-  of "name": c.name = v
-  of "displayname": c.displayName = v
-  of "version": c.version = v
-  of "os": c.oses = split(v, {';'})
-  of "cpu": c.cpus = split(v, {';'})
-  of "authors": c.authors = split(v, {';'})
-  of "description": c.description = v
-  of "app":
-    case normalize(v)
-    of "console": c.app = appConsole
-    of "gui": c.app = appGUI
-    else: quit(errorStr(p, "expected: console or gui"))
-  of "license": c.license = UnixToNativePath(k.value)
-  else: quit(errorStr(p, "unknown variable: " & k.key))
diff --git a/doc/nimdoc.cls b/doc/nimdoc.cls
new file mode 100644
index 000000000..37039f130
--- /dev/null
+++ b/doc/nimdoc.cls
@@ -0,0 +1,196 @@
+\ProvidesClass{nimdoc}[2022/04/17, 2018/01/01 LaTeX2e nonstandard class]
+
+\LoadClass[a4paper,11pt]{article}
+
+\usepackage[a4paper,xetex,left=3cm,right=3cm,top=1.5cm,bottom=2cm]{geometry}
+
+%   for 2-sided printing with larger inner "binding" margin
+%\usepackage[a4paper,xetex,twoside,left=4cm,right=2cm,top=1.5cm,bottom=2cm]{geometry}
+%   for e-readers with 1.77:1 aspect ratio (e.g. 1920x1080)
+%\usepackage[xetex,paperheight=27.6cm,paperwidth=15.5cm,left=3mm,right=3mm,top=3mm,bottom=3mm]{geometry}
+%   for e-readers with 1.45:1 aspect ratio (e.g. 1200x825)
+%\usepackage[xetex,paperheight=22.5cm,paperwidth=15.5cm,left=3mm,right=3mm,top=3mm,bottom=3mm]{geometry}
+%   for e-readers with 1.33:1 aspect ratio (e.g. 1872x1404)
+%\usepackage[xetex,paperheight=20.7cm,paperwidth=15.5cm,left=3mm,right=3mm,top=3mm,bottom=3mm]{geometry}
+
+\usepackage{fontspec}
+% logic to select default font with some fall-back fonts.
+\IfFontExistsTF{Times New Roman}{%
+  \setmainfont{Times New Roman}  % the default font
+  \typeout{========================================= nim: using Times New Roman}
+}{
+  \IfFontExistsTF{FreeSerif}{%
+    \setmainfont{FreeSerif}  % fallback #1 - official GNU font, resembles Times
+    \typeout{========================================= nim: using FreeSerif}
+  }{
+    \IfFontExistsTF{DejaVuSerif}{%
+      \setmainfont{DejaVuSerif}  % fallback #2 - very widespread free font
+      \typeout{========================================= nim: using DejaVuSerif}
+    }{
+      \typeout{!!!!!!!!!!!!!!!!!!! Fonts not found !!!!!!!!!!!!!!!!!!!!!!!}
+    }
+  }
+}
+
+% default monospace font for code:
+\usepackage{GoMono}
+\usepackage{relsize}
+% make this monospace font 2 steps smaller to hold 80-character line
+\newcommand{\rstverbblockfont}{\smaller[2]}
+\newcommand{\rstverbinlinefont}{\smaller}
+
+\usepackage{parskip}  % paragraphs delimited by vertical space, no indent
+\usepackage{graphicx}
+
+\usepackage{makeidx}
+\newcommand{\nimindexterm}[2]{#2\index{#2}\label{#1}}
+\makeindex
+
+\usepackage{dingbat} % for \carriagereturn, etc
+\usepackage{fvextra}  % for code blocks (works better than original fancyvrb)
+\fvset{
+  breaklines,
+  breakafter={=}:|\_\{\}[](){,}.;+-*/'",
+  breaksymbolleft=\color{red}{\ensuremath{\hookrightarrow}},
+  breaksymbolright=\color{red}{\small\carriagereturn}
+}
+\fvinlineset{%
+   breaklines,
+   breakafter={=}:|\_\{\}[](){,}.;+-*/'",
+     % that does not work at all when we underline inline code by ulem :-(
+   commandchars=\\\{\}
+}
+
+\usepackage{scrextend}  % for the `addmargin` environment
+
+\usepackage[table]{xcolor}
+\usepackage[urlbordercolor=blue,linkbordercolor=cyan,
+            pdfborderstyle={/S/U/W 1}]{hyperref}
+\usepackage{enumitem}  % for option list, enumList, and rstfootnote
+
+\usepackage[most]{tcolorbox}  % boxes around admonitions, code blocks, doc.item
+
+\newtcolorbox{rstadmonition}[1][]{blanker, breakable,
+     left=3mm, right=0mm, top=1mm, bottom=1mm,
+     before upper=\indent, parbox=false, #1}
+
+\newtcolorbox{rstquote}[1][]{blanker, breakable,
+     left=3mm, right=3mm, top=1mm, bottom=1mm,
+     parbox=false,
+     borderline west={0.3em}{0pt}{lightgray},
+     borderline north={0.05em}{0pt}{lightgray},
+     borderline east={0.05em}{0pt}{lightgray},
+     borderline south={0.05em}{0pt}{lightgray}}
+
+\definecolor{rstframecolor}{rgb}{0.85, 0.8, 0.6}
+
+\usepackage{booktabs}
+\belowrulesep=0ex
+\aboverulesep=0ex
+\renewcommand{\arraystretch}{1.1}
+
+\newtcolorbox{rstprebox}[1][]{blanker, breakable,
+     left=3mm, right=3mm, top=1mm, bottom=1mm,
+     borderline ={0.1em}{0pt}{rstframecolor},
+     before upper=\indent, parbox=false, #1}
+
+\newenvironment{rstpre}{%
+\VerbatimEnvironment\begingroup\begin{rstprebox}%
+\begin{Verbatim}[fontsize=\rstverbblockfont , commandchars=\\\{\}]}%
+{\end{Verbatim}\end{rstprebox}\endgroup}
+
+\newtcolorbox{rstdocitembox}[1][]{blanker, breakable,
+     left=3mm, right=3mm, top=1mm, bottom=1mm,
+     borderline ={1pt}{0pt}{cyan},
+     before upper=\indent, parbox=false, #1}
+
+% Inline code formatting: grey underline,
+% use \Verb from fvextras e.g. to display -- correctly as double -
+\usepackage[normalem]{ulem}
+\newcommand\rstuline{\bgroup\markoverwith{\textcolor{rstframecolor}{\rule[-0.8ex]{2pt}{1.0pt}}}\ULon}
+
+\newcommand{\rstcode}[1]{%
+{\rstverbinlinefont\Verb{\rstuline{#1}}}%
+}
+
+\newcommand{\rstcodeitem}[1]{\Verb{#1}}
+
+\newenvironment{rstdocitem}{%
+\VerbatimEnvironment\begingroup\begin{rstdocitembox}%
+\begin{Verbatim}[fontsize=\rstverbblockfont , commandchars=\\\{\}]}%
+{\end{Verbatim}\end{rstdocitembox}\endgroup}
+
+
+\newenvironment{rstfootnote}{\begin{description}[labelindent=1em,leftmargin=1em,labelwidth=2.6em]}{\end{description}}
+\ifdim\linewidth<30em
+  \def\rstoptleftmargin{0.4\linewidth}
+  \def\rstoptlabelwidth{0.35\linewidth}
+\else
+  \def\rstoptleftmargin{12em}
+  \def\rstoptlabelwidth{10.5em}
+\fi
+\newenvironment{rstoptlist}{%
+\begin{description}[font=\sffamily\bfseries,style=nextline,leftmargin=\rstoptleftmargin,labelwidth=\rstoptlabelwidth]}{\end{description}}
+
+\usepackage{multirow}
+\usepackage{tabulary}  % tables with adjustable cell width and no overflow
+% make tabulary prevent overflows (https://tex.stackexchange.com/a/195088)
+\tymin=60pt
+\tymax=\maxdimen
+% to pack tabulary into a new environment, special syntax is needed :-(
+\newenvironment{rsttab}[1]{\tabulary{\linewidth}{#1}}{\endtabulary}
+
+\newcommand{\rstsub}[1]{\raisebox{-0.5ex}{\scriptsize{#1}}}
+\newcommand{\rstsup}[1]{\raisebox{0.5ex}{\scriptsize{#1}}}
+
+\newcommand{\rsthA}[2][]{\section[#1]{#2}}
+\newcommand{\rsthB}[2][]{\subsection[#1]{#2}}
+\newcommand{\rsthC}[2][]{\subsubsection[#1]{#2}}
+\newcommand{\rsthD}[2][]{\paragraph[#1]{#2}}
+\newcommand{\rsthE}[2][]{\paragraph[#1]{#2}}
+
+\newcommand{\rstovA}[2][]{\section*[#1]{#2}}
+\newcommand{\rstovB}[2][]{\subsection*[#1]{#2}}
+\newcommand{\rstovC}[2][]{\subsubsection*[#1]{#2}}
+\newcommand{\rstovD}[2][]{\paragraph*[#1]{#2}}
+\newcommand{\rstovE}[2][]{\paragraph*[#1]{#2}}
+
+% Syntax highlighting:
+\newcommand{\spanDecNumber}[1]{\textbf{\textcolor{darkgray}{#1}}}
+\newcommand{\spanBinNumber}[1]{\textbf{\textcolor{darkgray}{#1}}}
+\newcommand{\spanHexNumber}[1]{\textbf{\textcolor{darkgray}{#1}}}
+\newcommand{\spanOctNumber}[1]{\textbf{\textcolor{darkgray}{#1}}}
+\newcommand{\spanFloatNumber}[1]{\textbf{\textcolor{darkgray}{#1}}}
+\newcommand{\spanIdentifier}[1]{#1}
+\newcommand{\spanKeyword}[1]{\textbf{#1}}
+\newcommand{\spanStringLit}[1]{\textbf{\textcolor{darkgray}{#1}}}
+\newcommand{\spanLongStringLit}[1]{\textbf{\textcolor{darkgray}{#1}}}
+\newcommand{\spanCharLit}[1]{#1}
+\newcommand{\spanEscapeSequence}[1]{#1}
+\newcommand{\spanOperator}[1]{\textbf{#1}}
+\newcommand{\spanPunctuation}[1]{#1}
+\newcommand{\spanComment}[1]{\emph{#1}}
+\newcommand{\spanLongComment}[1]{\emph{#1}}
+\newcommand{\spanRegularExpression}[1]{#1}
+\newcommand{\spanTagStart}[1]{#1}
+\newcommand{\spanTagEnd}[1]{#1}
+\newcommand{\spanKey}[1]{#1}
+\newcommand{\spanValue}[1]{#1}
+\newcommand{\spanRawData}[1]{\textbf{\textcolor{darkgray}{#1}}}
+\newcommand{\spanAssembler}[1]{#1}
+\newcommand{\spanPreprocessor}[1]{#1}
+\newcommand{\spanDirective}[1]{#1}
+\newcommand{\spanCommand}[1]{#1}
+\newcommand{\spanRule}[1]{#1}
+\newcommand{\spanHyperlink}[1]{#1}
+\newcommand{\spanLabel}[1]{#1}
+\newcommand{\spanReference}[1]{#1}
+\newcommand{\spanOther}[1]{#1}
+\newcommand{\spantok}[1]{\fbox{#1}}
+\newcommand{\spanPrompt}[1]{\textcolor{red}{\textbf{#1}}}
+\newcommand{\spanProgramOutput}[1]{\textcolor{darkgray}{\textbf{#1}}}
+\newcommand{\spanprogram}[1]{\textbf{\underline{#1}}}
+\newcommand{\spanoption}[1]{\textbf{\textcolor{darkgray}{#1}}}
+
+% Never allow text overflow to margin:
+\setlength\emergencystretch{\hsize}\hbadness=10000
diff --git a/doc/nimdoc.css b/doc/nimdoc.css
index a002b6be1..0c399e4c1 100644
--- a/doc/nimdoc.css
+++ b/doc/nimdoc.css
@@ -1,305 +1,1036 @@
 /*

-NOTE - THIS IS PROBABLY NOT THE CSS FILE YOU WANT

-The CSS text used by Nim's documentation tools (such as 'koch web',

-'rst2html', etc) is contained in 'config\nimdoc.cfg'

-*/

-/*

-:Author: David Goodger

-:Contact: goodger@python.org

-:Date: $Date: 2006-05-21 22:44:42 +0200 (Sun, 21 May 2006) $

-:Revision: $Revision: 4564 $

-:Copyright: This stylesheet has been placed in the public domain.

-

-Default cascading style sheet for the HTML output of Docutils.

+Stylesheet for use with Docutils/rst2html.

 

 See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to

 customize this style sheet.

-*/

 

-/*

-   Modified for the Nimrod Documenation by

-   Andreas Rumpf

+Modified from Chad Skeeters' rst2html-style

+https://bitbucket.org/cskeeters/rst2html-style/

+

+Modified by Boyd Greenfield and narimiran

 */

 

+:root {

+  --primary-background: #fff;

+  --secondary-background: ghostwhite;

+  --third-background: #e8e8e8;

+  --info-background: #50c050;

+  --warning-background: #c0a000;

+  --error-background: #e04040;

+  --border: #dde;

+  --text: #222;

+  --anchor: #07b;

+  --anchor-focus: #607c9f;

+  --input-focus: #1fa0eb;

+  --strong: #3c3c3c;

+  --hint: #9A9A9A;

+  --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN4AAAA9CAYAAADCt9ebAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjAzOjQ4KzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMjoyODo0MSswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMjoyODo0MSswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozMzM0ZjAxYS0yMDExLWE1NGQtOTVjNy1iOTgxMDFlMDFhMmEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MzMzNGYwMWEtMjAxMS1hNTRkLTk1YzctYjk4MTAxZTAxYTJhIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6MzMzNGYwMWEtMjAxMS1hNTRkLTk1YzctYjk4MTAxZTAxYTJhIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDozMzM0ZjAxYS0yMDExLWE1NGQtOTVjNy1iOTgxMDFlMDFhMmEiIHN0RXZ0OndoZW49IjIwMTktMTItMDNUMDE6MDM6NDgrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4wIChXaW5kb3dzKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4PsixkAAAJ5klEQVR4nO2dfbBUZR3HP3vvxVD0zo0ACXxBuQMoQjJ1DfMl0NIhNcuSZqQhfGt6UWtK06xJexkrmywVRTQlHCIdtclC0zBJvYIvvEUgZpc3XyC7RVbKlQu1/fHdbc+uu2fPOfs85+y55/nMnBl2z+5zfnc5v/M8z+8119XVRYroAG4HfgvMT1YUR4MMAa4HLkhakCRoSVqAELwLeBY4C7gF+D6QS1QiR1ROAJ4Dzk9akKQwoXhtwL4GxvHjU8AKoNPz3leAu4HBFq+bAyZZHD9rDAK+BywDDklYlkQxoXhfAtYAEw2MVckQYBHwU6or99nA08BBFq49GngUeBIYaWH8rNEJdAOXA60Jy5I4jSreSOBKYDzwBPCJhiUqcSjwe2BWnc9NLnxuvMFrnwqsAqYBBwBfNzh2FpmNfs9jkhakWcg1aFxZiH5UL3cDnwf+Xue7BwFjgFHAOwuv24tyob3cO0LIshP4EbCn8Pq/wKvA9sLxMvCvOmPsA1yDZnHv/nEv2mM+F0IeR4m8z7lM7tMbUbzj0CxX7YfbAXwaWFJ4PRrNIu9FS9KJyEIZN68CG4DnkRJtLBw7gHHAYuDdNb77EDAjBhkHIk7xKoiqeK3IwjilzuceQJvoZjdQ/AMZaeoZiWYgBXSEwyleBW0Rv3cR9ZUO4LSI48fN2wN+bi5wJNBvUZaBSCaVy48oxpVhwDdMC5ISxpJRh6/DLGEUrxXt29YBQ+2IkwquR76ofZIWxJFegireNLSnm48skFmmDfmiVgJHJyuKI620ADOpbWEcDPwYOZKD7OmyxCTkXL+wzueOiEEWR8poQb60V4A7kLm/yFjgKeALuM1xLfYDbkX+zEGe98cAX0Oui6viF8vR7OS6urragW2UZr21wK+Aiwlu7XPoN3sYOAd4H6WH1SnA0qSEcjQnRT/e1bgnsw16kGPez4/lyCBF48oNwL+TFGSAsgCndI4qFBVvJ0owdZhjL3CnxfHzBo8+YBMyol0CHBijrKbHS/LoA7Yio9sPgJNr/QHekLGR6MffL+KP4SjnHmQxtoXNmbQP+CHyV75hYDzTIWNpWkU8iR5mq71vVsZqXgtcFqNQ/wG2IOtfD8oi6AX+Ujj+isKz8sBrnu+1okyGdmD/wnEgcDClTIdRyJRvI1cvCMciq7At4rj5eoCPAusbHCfLigda/VyKgi+AtyreMGAzykGzQQ/wO+BxSlkCuy1dq8hw5OieUjimYT+x9bHCdWwS1823Ez1EXmhgjKwrXpHzkduuanbCtzGX+NkPPAj8GincNkPjNkIO5dadUjiOB95m+BonopQpm8R58/0JJbHWy2eshVM8sRvdbyurKV4Hmoka2WA/iwwLP6d+QmzSdKC92GzK/W9R+Q3woQbHCELcN991wJcjftcpXolngKm18vFmoVonYcgDv0Qz5pqGREuOTuA8lPYUZbndh0LJNpkUqgZx33xvomim7RG+6xSvnOm1gqQXoyiMoKxFs8VZpFfpQHvQK4HDUPnAsBa9bxGP0tUjF+IYCkxFew+/G3owdq20pgjzt3uPRscs/o43IaOhH2f4ZaAPRyZQP6vgbuCbyGext87F0sgIZFI/N8BnlwBnolovcWAjq/uzwM0+55cBJ0UYN84ZL+rfbnLMM4FfUDv7Z1XlCe8FetETbleNL7+CZrnvMjCVDuTOOA84Hf+96ga0PC8qXY50FQsuMg+41+d8p885R4n7gdt8zo+qvDkmUF4fZQXwEbS+99KDMhlWkw0eALqQglXyDDCdcovf+4lv5jPNXJ9zWc/FDMMdPudGVCreRlTWwVtWbynwYVQQCFSp61Q042WJLUjB1nneuw8tvXo97x1Lugvg+j1Mo9boySLVHtJFWqsthx5GlbSGeN5bigrHdqPl52Zj4qWLXvTQWY4KOX2ccgPMBLRcuy9+0YzhguXN4GuYq2Zc2R/NZg+hfYt3/9ZCepdQthmB4vIWIYOTbWyWzGt2Y0izG1fqjlltxnsdpbPMRMmd3lqTTumqMw7FZY5G5mSHw5dalreiRWYGWjbZ7gYUlFa0xOtIWA4vk1E6zWEoI+FvyYrjSAO1FG8DCmQGKd+DJFsGogWVVFiP/GWbga9Svg9NgtPQvnd04fUNCcriSBF+vqZ5nn9PQ+Xs4q401oI6EP0R+BkyXoAeAtcgBfwidnvkVaMVFTO6n1JoWTfqiONw1MVP8e6l3GVwOPJZXW5VItGGiuduAu5CZdOrMQJ1CHqpIFccS+LxaD/3Hcr7vF0Xw7UdAwQ/xduLGkJ6aUMhVAuwU006B3wM+ZLmozJ5QRhWkGs9yjKw1fhwDsq8eE/F+y+i1CeHIxD1wppupXrA5xyUOjQHMzU3cyjTeS2aaaN2Fzoc1bhch3xspuqBTkDulQVUz1q4mYEbNuewQD3FexGFS1VjOLoRHwOOinj9HAooXY2CSidHHKeSI5GFcRWNdSxqR7VH1iHHeTV24R+X53C8hSCBvPPqnD8B+AOygn6OYAm0ORSGthLl8B0d4DtRmIKsoMsJF1U/Hi1dt6DusIN8PrsIlUdwOAITpDFlC6q3MTbgmHm011qGepOvQSXPipyOCujW6rxqk0dRWYsVFe8PRSn5JxWOoEvdfOGzfnF5tnCRK+bGi33MoB1hL0U5d1H5J5oVD6A5mp8sQS6KSWh5e0jEcR4BPmhKqJA4xTM3XuxjBlW8DuRacDU3y0myNbNTPHPjxT5m0GTN15A/zVFiI+HKYzgc/ydMlrRfgmQWuYn0F91xJEQYxVuDnMcOrQAWJi2EI72ErQviwqLEQpQ+5XBEIqzi3YWLwF+BMiMcjshEqYR1Gdk1KmxBsaR9SQviSDdRFK8fxVU+YliWZmcbcq7vSFoQR/qJWvuxD0WgLDYoSzPzAqowtjVhORwDhEaKru4GPoliGgcyy4Hj0DLT4TBCo9WO88jQ8Bns97lLghvRTOfqqDiMYqrM+HyUYdBtaLykeRmlK12C9rQOh1FM1vd/HqUIzaT5e+LVoh/VxByHShs6HFaw0VjjHhTxP5d0LT+fRnu5q3HuAodlbHW02Q5cDByM+sw1642cRylCx6PeZiuTFScUFxK+f19QovaRS+t4tsasxhvABbZbSfUCV6CM7qtQl6Fm4E1U22UqcAYqvZ42fgJMxH6vdYc5nkBlSW6Pq4fbS6hb6jg0u9yGug7FyS5U1+UcVBbwbFSuMM1sQ1bXK4A9CcviqM0e9H80HdUxCpwIa4McygA/GfgAcCJqmGKKXUixupEv7nHsLc2agWNQ0d9OzC+PHNHIo1XeLCoe8kkqXiUtwKFoWXoEKqk3BpWLaC8cXsV8HT1J+tFTZKvn+DMqFZi1knvtyKg1O2lBHADcCVxEedNSAP4HJcsr0NNWHVUAAAAASUVORK5CYII=");

+

+  --keyword: #5e8f60;

+  --identifier: #222;

+  --comment: #484a86;

+  --operator: #155da4;

+  --punctuation: black;

+  --other: black;

+  --escapeSequence: #c4891b;

+  --number: #252dbe;

+  --literal: #a4255b;

+  --program: #6060c0;

+  --option: #508000;

+  --raw-data: #a4255b;

+

+  --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: black' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E");

+  --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: black' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E");

+  --clipboard-image: var(--clipboard-image-normal)

+}

+

+[data-theme="dark"] {

+  --primary-background: #171921;

+  --secondary-background: #1e202a;

+  --third-background: #2b2e3b;

+  --info-background: #008000;

+  --warning-background: #807000;

+  --error-background: #c03000;

+  --border: #0e1014;

+  --text: #fff;

+  --anchor: #8be9fd;

+  --anchor-focus: #8be9fd;

+  --input-focus: #8be9fd;

+  --strong: #bd93f9;

+  --hint: #7A7C85;

+  --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARMAAABMCAYAAABOBlMuAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjE4OjIyKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQyNDQtYTExZi0yMjc5YmY4NDNhYTAiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ZWRlYjM1NzAtYjZmYy1kMjQ0LWExMWYtMjI3OWJmODQzYWEwIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6ZWRlYjM1NzAtYjZmYy1kMjQ0LWExMWYtMjI3OWJmODQzYWEwIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQyNDQtYTExZi0yMjc5YmY4NDNhYTAiIHN0RXZ0OndoZW49IjIwMTktMTItMDNUMDE6MTg6MjIrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4wIChXaW5kb3dzKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4JZNR8AAAfG0lEQVR4nO2deViTZ7r/7yxkJaxJ2MK+GCBAMCwS1kgUFQSKK4XWWqsz1jpjp3b0tDP1V+eqU391fqfT/mpPPd20drTFDS0KFEVWJSGAEgLIZpAICBJACIRs549Rj1WILAkBfD/XlevySp68z/0S3+/7vPdzLyidTgcLkU2bd+z39/f/q1gshsrKSoJELFCa2iaEuU9K6kb+8uXxv54/fzE8L/eswNT2zCfQpjbAGKS8lPFKSEjIXiaTCSEhIeDj4xNnapsQ5j6rktZGp6UlfxIdzQVzCplmanvmG1hTG2BIAtlc26CgoDfT0tL2e3l5AQCAjY0NkMnk/a9s2k6rrKw8UV8n1JjYTIQ5RlAw14KzmL3xze1vfJyUuMJaq9UCFovFm9qu+YbBxcSPFUYkk8l2Q0NDsvo6ocrQx5+I8Ih4bz6f/0l8fHyKlZXV4/dRKBQwmcwwMpn8A4FAoPgHhH9bV1sxa488wZxoaycnJ/a9e/duCa5fkc3WvAiTI4Ib77p+XdqHG9anbfLy8gAAgLGxMdBpF+bjvzExqJj4scKI0dHRnwQHB++orq7+AgDeMuTxJ2Jl4rqU9PT0EwEBAUQCgTDuGAaDAampqYepVKpHUHDk325Ulw0a266YuFW+Gzdu/MDPz29jfn7+XgA4aOw5ESZP6kvpCXv3vnM8NiaSamVl+fj9BepGNDoGFRN7e/slcXFxO1xcXMDJyWnH7j//H/fi4uJdgutXmgw5z5O8smn7X9euXbvf29sbMBjMhONQKBRYWVlBbGzsbjMzM3JoOG+/sKKwy1h2rd/4elpGRsYuLy+vaDweD2w2Oy1h5ZrCvEunEaeeiVnMiabyl/F2/+X9P+8JDPQHHA5napMWBAYTk6DgSNuEhIS9DAYDAP7tq1i6dOkqOp3OWbNu0wens44emeoxA9lcWwKBYEMkEm2JRKIdHo+3QKFQWJ1Op8ZgMER3d/dVq1evTnFycpr0MSkUCsTExGzH4/Gk1LTME/39/TI0Go1FoVCg1WrVY2NjipGRkcGRkRH5dPwrEZHLXMPCwjJSUlIy3dzcfB+97+rqGhYSEpIOAIiYmBguN3zL77dt3uPh4W5qUxYUBhMTb2/vjeHh4cvR6P/dILK0tITIyEg7BweHr363/Z3Ampqaf1Zcu/zMKiVsyVJvMplsRyKR7IhEor2FhYUbhUJhJCYm2pFIJB6JRAIymQx4PB7QaDRoNBowMzMDJycnwOOn7icjEokQGxu7icFgbLp///7jFY1WqwWlUgkjIyOgUCgO7Ni5Rz48PCwfHh7uGRkZeaBQKOSjo6ODCoVCXlNVKn/6uCsT13FXrVr1emho6BYKhfLMnP7+/omrU9LPX8g+UThloxEMxqJFXjxESAyPQcSEExrLWLNmzW57e/txP/fw8ABHR8cdDAaDt3xF2ru9vb03sVgs0cbGxs/FxWVZUlISj0aj+dna2oKtrS1M5PcwJCgUCry8vODRrs84vPfoH6OjoyCXy6Gvr+/R6+CWrX9s7evrk/b19bWr1Wqli4sLZ8OGDe95eXmxUSjUuAd0cHDwjoqK2sYKXFIhvnldYYTTQpgU4/8+jyASCYDGoCd+ZkYYF8OICYezl8PhuOkbQyAQIDo62s/NzS2np6cHbGxsgEajAYFAAAwGA1gsFia6CE0NgUAABwcHsLe3B61WC2q1eo9WqwWNRgNKpRLUajUQiUSgUCh6zwGHwwGTydzo5+eXBQBnZu8MEJ5keHhYPqyYWMtHR0ZBpVIhYj9FUDONgOUvT12+du3avMDAQJjssdRqNWCxCyrEZdLodDoQi8Ulx44de628NL/V1Pa8iERE8l2dHB2CJvpcq9Nqbt1qKURWj1Njxld0ZGTkAW9v70kLCQC8sEIC8O/HKx8fn2gmk8kHgCk7pRFmzrWyAikASE1tx0Jj2uH0EZHL/N7YtuvT4OBgzmz4OBYSeDweIiMjt2S++vtMP1YYEmmJsCCY8mNOIJtr6+zsHBcZGXmIw+G4mZubG8m0hU9HRwcUFxe/KxQKTyDRsQjznSmJCS9+dVRERMTfQ0NDo2xtbfUGiSFMjtHRUaitrc3Jzc09kHvxVLmp7UFAmC6oZQkvrZLL5RJhReHtiQb5scKIXC7371FRUX90dnYGIpE4JR8Jgn40Gg20t7fXFxYWfnr9+vWjz8sdYi+Osh4vzgUBwZSgtu94V+fs7Hx7YGCgra6u7khLS0u2RCwYeTQgKmYFh8fj/f/g4OAldnZ2prR1wdPd3Q1CofBQSUnJkdLi3N8E93FCY6k+Pj48FxcXjlar1ZSWlh65VvYr4kREmDNg79+/D3FxcW5OTk5uXl5evNbW1tL0jK3ZXV1d1ykUintycvInoaGhdkj+gvGxs7MDPp+/m0AgWMQvS/lyeHhYTqPRPJycnIJSU1NZ3t7eW2g0Gly/fv2oWq1Gij0hzClQ/gHhpLS0tEM8Hm/7I8Ho7++HlpYWsLa2Bg8PDxOb+OKhUCigqakJ7t+/D25ubuDu7g4oFAp0Oh08ePAAvv7666TTWUdzTG0nAsKTYMU3ryuSU18+4+bmFrZo0SIOAICVlRUsXrx4zkakLnRIJBI8CgJ8MtdJp9NBZ2enqL29XWRC8xAQxgUNAHD+3L8KGhoaCp78ABES04JCoX4jJAAAAwMDUFtbe96YpRMQEKbL41DU5ubmko6Ojj2PSgggzD36+/vrb9y4cX425zzw93/8EBjon2is44+NjSkePBjqGRwc7G5v7xBV19w8U5B/3qgrr9+/uWtXUuKKD/TZ9MXh/066/OuFmunO8dGBQ98HBbGSp/t9U6LRaDXK0dHBoeFhuVzeL22/0yFqamopufjLqRJ933ssJi0tLSXV1dWHGAzGbuObOzs8ubqa71vZKpUKOjo6blwpOF8zm/Mu5cVkLlkSaswprAHAaVihgK7O7oSGxltvfXLon3nXK4RHT2cdN4pfKDCAlZyUuMJan02nTmczAaBmunPw4qI3cbnh0/36XICq0+lgcPABp7OrK629vUP5z8++LLh2XXD05L++yxrvC4/F5EZ12WBS8saLS5Ys2U2lUufUY45SqQSlUgkqlQrUavXj19jYGGg0GtBoNKDT6UCn05VotVq1TqfToFAojFar1eh0Og0Wi8XhcDgeGo1+/PhgZmYGOBwOsFgsmJmZ/eY1F+nt7YXa2trs2Z73wdCQBgCMHp1IJpHA09MdPD3dLRIS+OtKisvWvbP7vf2lZdePVFwzbHTwyMiI3hidkZFRUKvUYzOZ48HQkBIA5nWqBAqFAktLC7C0tADmIh88Pz4uMSyUk7hn776DV4tKPn/6d/lNxp1MJqsRCASf8vn8XdMpOjRTVCoVjI2NgUqlAq1WCyMjI9DX1wf379+Hvr6+/Q8ePOgdGRmRKxSKx0WLFAqFXKlUKnQ6nUar1arHq47mxwrD4/F4Eg6HI2GxWDwej7cgkUjWFAqFam5uTjU3N6eRyeQPLSwswNraGqysrIBAIDwWFywW+zja11Qi29LSclIikeSZZPJZBovBAI8XA8HBQR9kZZ3lR8cmvFZSlGe00p8IkwONRkNERBj4+i7a4+XpHv307/IbMakWlciXJbx0nMPh7Jqo0JGh0el0MDo6Cl1dXSCVSkEmk7177969W319fe1DQ0M9KpVKoVarlWq1WjndNhUPG3ApAWDcOxLTLwSDwWAOotFoDBaLxRMIBAsrKysne3t7Xzqd7k2n0/c4OzsDlUoFHA4364IyMDAATU1NxdWikhcq6tXKyhJezljPJZKI2eERS5cZeoWCMD2srCwhPX0tVzk2djiCG//GtfLLUoBxShB0dHTU3Lx580sLC4vtJBLJKMZoNBqQSqUglUqPdnR01PT09DT19/fLHjx40DM0NNQ72933GiSVGgB4JFQK+LfoSAGgnL04yppEIh2xtLS0t7GxcaFSqR7Ozs4fMRgMcHR0nJX8pJs3b54Ui8UXjT7RHIRMIkFK8irfwcEHPwQELUmqvYHUGJkLmJubw8YNa/i9vfffY/px3myQiDTPiEl9nVDDX576jaenZ7SnpyfLUJNrNBqQyWRw+/bt4x0dHTdkMlltV1dXw/XygjkdEv4wB0YOAK0AUM70C8HQ6fSzdDrdm0qlejg6OrLc3Ny2MBiMadWjfR4PHjyAmzdvZs/1v5MxoVAokJK8iicWS95k+nH+s0EiQhqpzQGoVFtYk5a87ba0XQAA34xbpagg/5zoT7s/OGNnZ8eaaYkBuVwOnZ2d5VKpVNTS0lLS2NhYWFVZ3Dujg5qQh6uY+ocvCAiKIPn4+Jz19PSMdnV15VCpVL6Dg4NBViw6nQ5EItHRpqamqzM+2DzHzo4O69amftLQeKsAZrDLgmBY/PyYsCIhfs+SiKUFE5Y8EwqFx11cXDihoaFTjjFAoVAwPDwMHR0dourq6jNCofDHhZqUVnvjmgIAcgAgJyg40mLRokX8kJCQjT4+PussLS1n1JPl7t27UFxcfHguB6mNjY2B7G4naNRTWyygUCjAYDGAx+PB0sICSCSi3vFYLBbCwjjA8vddBQtATKb7d3saBwc7IJPJBpsHjUGDGRYLJBIJLK0sAfucmyIGg4FFi3y8AwNZtycUk5KiS02vvf7WWQaDkejg4DApQwAeh3xDaWnpPoFAcPxFqnP6sEvgGf+A8Bx3d/cvIyIiNi1evHjT8wpNj8fAwACUlZW9P9dD5+/ckcFbf9gd2dcnn9LNAovF4inmZHtXNxdOdBR3+/JlS33pdP29wolEInA4weuiYxOy5vvuTkeHDHb+8c8xvb33Z3R9/N+Df+uIjYk02DwkEsna2trS1d/fNyGeF7uTyw1/7g3R3t4O2OxA/TVghULhcQqFQk1JSfmYSNR/5wD4d6EfgUBwvLS09IhUKhW9qAV5H9YjKQwJi6uvrKw8ERoamhkSEpKp7w7yJEqlEiQSyZmysrJv53qjdaVSCZdyTk+3qFMrAJRHRPLPN95qeifj5fU7mYt8JhyMRqMhMJDFdnF25gDAvBYTpXIMWlpay2fq/8m5mDcIABYGnEcGAGI/VlhBZWX1yZdSkz55OX0dV5+7w9bGGvz8mPrFpK62QskJjf2GTqd7x8bGbpnID4BCoUAmk0lLSkqOiESik2UleS/MakQflYKrXQDQxY1a3tTe3i6KiIjY5OXlxX7e9+rr6wsuXbr0t4ffn9OgMWjghMZQRcLp+8GulRVI/QPC37Wxtnal0ajJtjY2E451ZjiBra31vE9lR2PQQKFQaAAwo98Yi8Xq9fpPd56HO6rlvKWJv/PwcK+JilyCmajWMw6HAzs7+rMFpQOCIn6zHywSFvXm5eUdFAqFZ9Rq9bgHa2trq79w4cK+zz49cAARkmcpL81v/a/Dhz49d+7c3qqqqjyVSjXuOJ1OBxKJpDw3N/fA5V+zax6978cKw/sHhM/raMrnUVdboSy4fPWQSFSjd5yFBQWIRNKEd2IEw1J4JUd88WL+R51d3XrHWVDMnxUTa2tr1zXrNiUGsrmPf7DS4tymCxcu7Kuurs55+kKQSqVN586d23vs+8NHDXUCC5Wzp3/Iy8rKeruysvLM2Nhvo7VVKhXU1tYWnj17du/T7UOdnZ2D7OzsfGGB09raVi4S1RzXl0eFw+EAj8chYjKLVFffyOrq1C8mJBLpWTFRKBRyDofzC4vFWvXk+1ev/CLOzs7eKxAIslQqFeh0Oujp6enKzs7em/XTd7OayTqfKb56sT4rK+sPAoHg5KO/o0KhAKFQmHXy5MkdF3/5+TeZmctXpIXZ29v7zqVcKWNRX1epuXu3U/y8pEw0GmndOZt0dnXVDw0P6/W5oNHoZ30mQ0NDPb29vfvj4+Pf3rR5B/7od188XnEUXr4gDgmL+0NfX5/U19d3d3l5+YGfTnyDtLmcIhXXLsu4UcvfR6PRGGtra9eysrIjYrE45+kt4Fheou/69es/unnz5vm7d+/Wmsre2WRkZGTQ1DYg/JYGiUiTm1ugBAC9IfHPiEmDpFITE7fqJI/H27lmzZpDq5LWtz55t6wUXO3ihMYerK+vz2tpaUFaM0yT8tL81ujYle+TSCTrvEunBU9/voTLd92wYcPHVCqV39XVdXCu7+oYCp1O90Kc50Jk3I5+xVcv1jc3N5d4enpSMzIyvkpK3sh78nORsKg3++yPBS/q1q+hKCm61DSekERGJ3ikp6d/ERsbm1xVVXWwtbX1hRFtFAqFPMLMUyZsDyoQCI7LZDKIiIjwzczM/GpV0vro2TTsRSUqZoX3+vXrP1u9enXi0NAQiESirIdRtggIc5oJ40zq6uryGhoa8ry8vBJCQ0O9USjU94mrN7yWc+EnvaXb5gJMvxCMp6cnl0Kh2Le1tZVXXLs8L1LXefGrWRkZGZ/x+XyeUqkEkUh0vqenZ14HZyG8OEwoJjdrygd37NxTEBkZmWBtbQ3BwcEeKBTq+/UbX3/355Pfzlmn66qk9dGbN29+k8PhbCSRSNDZ2Snb9ae/HCkpKTksEhbN2QTD5NSX+Vu3bj0cHBzsjcFg4O7du1BWVvbNwxB9BIQ5j94I2Fu3bhXW19cDl8sFLBYLHA7Hg0wmf/e77e84ffXlPz6fLSMnQ2paZkJ4eHjmtm3b+B4eHvZkMhlQKBTY29s72dvbfxgUFJT8x7ffP1NRUfHjXErnZ/qFYKKjo7dt3rz5g8DAQPtH/XHa2tpqGhsbC55/BASEuYFeMblz505NTU3NgfDw8PcwGAygUCjw9fW1IJPJn/1130Hv0tLSI4WXL4hny9inYS+Osvbz80tgMpn8jIwMPovFch2vpoiDgwM4ODhwfH19OYsWLeJv3/Hu+cbGxquzXZz5aZYlvMRJT0/fFhkZue3JZmfd3d0gEolOIr4ShPmEXjFpkFRqXlrzSnFnZ+d7Tk5OjzNfXVxcICMjY6ezszNnVdL6vU8HWhmbgKAIkrOzMyc1NTXz0YU4maAuOp0OK1as4EVFRfGEQqHg1dfePHzr1q2rs71S8WOF4f38/BLS09M/iIyM5DxdxLq5uVlcVVU1bgVwBIS5il4xAQCQyWRigUBwJikpKe3JVGQcDgdLly7l2tranti0ecf7IpEoy9hbxX6sMDydTvdevXr1ltjY2F3u7u6AxT73FJ7B3Nwc4uLiwthsdphQKCzZkL7l0/r6+oKbNeVG90+EhMXZL1++fFtycvKHrq6uz4igUqmE5ubmEiTHCWG+8dwrUXD9imz9xtd/jIuLS7N5KpsTjUZDUFCQE4PB+F4oFGYmJW888Mv5k4UTHGpGxC9LYaenp78VEhKyxdHRESgUyoyOh0KhwNraGuLi4qIDAgKi6+rqyjekb/mHMSN6N6RvSdu+ffseNpsdZm09ftuW+vp6EIvFSB9hhHnHpG7rUqm0orW1tdXS0tLj6TIEaDQaaDQaxMfH811dXTl/3Xfw+JUrVz411J01cfWG6IiIiC07d+5McHNzs7ewMGyOFw6HAwcHB6BSqVx3d/fwz7/4rkAgEBwXCoUnHpZonDGrU9J5MTEx27du3Zrm4uKC0beaqq6u/ry+vj7XEPMiIMwmkxKTimuXZe/u+fCkp6fnexPdUfF4PPj7+1szGIydLi4unF1/+kvenTt3RG1tbRXTqfma8lIG39/fP/HVV19NZrFYHpMpzjQTzMzMwNPTE+Pp6Zng6emZ4Ofnl5CesfV8bW1tznQe3/wDwvFeXl7Rvr6+Ca+88kpaUFCQh74GXzqdDrq7u6GpqankRQmdR1hYTNrhUFVVlcXj8d6ysrKy0OfstLS0hPj4eC6Xy+U2NzeDRCI5/sa2XeX37t1rGhwc7BoYGJBN1P+FFbiE5OzszGaxWImvvvrqpoCAAKfp+ERmCpPJBCaTmcnhcDJLS0u/TE59+YxUKhXoi/lg+oVgrKysGJaWlna2trYeaWlpXDabvTMgIGDSfp2KiorzbW1tL0zoPMLCYtJX6uVfs2u++PKowMPDgz+ZIslEIhECAgKAxWJlajSazJ6eHmhra4PW1tZvtmz9o6Czs7O+r6+vfWxsbFir1WosLCzsV6xYkcnj8d7z9vaelmPV0Hh5eYGnp+f2mJiY7UVFRZ/HL0v5tru7+5ZGo1FisVg8Docj4fF4CxsbG1c+nx/m7e39sYeHB7i4uIC5ufmU6r4ODQ1BZWXlifkSrYuA8DRTumIrKytPent78728vCb9HRQKBVgsFhwcHIBOpwObzd4yNja2RaVSwdDQEHR1dcHo6CjQaDRwdXWdsWPV0KBQKPDw8AA7O7udERERO2tra2FgYACoVCo4OTkBjUYDMpkMeDz+8WuqaLVaaGxsbL19+/YzSX8ICPOFqYrJidDQ0AwvLy/e80c/CwaDARKJBI86BdJoNHB3dwe1Wj0nViL6IJPJwGQywdnZGZRKJRAIBDBUx8OBgQEoLS39BtkORpjPTJg1PB61N64pmpqarvb39xvUiLkuJE9CJpPBxsbGYEICANDZ2SlHgtQQ5jtTEhMAgLq6ulyJRFJvDGNeREZGRkAikRSUFuci2cEI85opi0l+7hmBWCzOeV6dToTJcfv27cHr168jxbgR5j1TFhMAgObm5hKZDNl0MAQtLS3Xzpw6hkS8Isx7piUmUqlUIBAIJuyjgzA5Ojs7QSKRINGuCAuCaYmJsKKw68qVK59KJJIu5HFneiiVSigqKjouEolOmtoWBARDMC0xAQC4+MvPJadOnXq3ra1N8yL0dDEkOp0OSktLy/Pz8w8+3d4CAWG+Mm0xAQA4fuy/jl+8ePGju3fvGsqeBY9Wq4XKysrWU6dOvX31yi8mKyyFgGBoZiQmAAD/79D+fadPn96PCMrz0el0UFVV1frtt9+mj9fiAgFhPjNjMQEAyMvLO3Ds2LE/tLS0INmuerh27Vr9999//xoiJAgLEYOEntbVVigB4PNNm3cMpqSkfMRms50McdyFgkqlgqKiovJTp069nZ97BhEShAWJQePYj373xdF1GzbLFQrFx6Ghob766ne8KNy7dw+KiopO5ubmfmTK4tsICMbG4EkxWT99d35l4rre/v7+D0NCQvh0Ot3QU8wL1Go1SKVSTX5+/sH8/PyDSP8bhIWOUTLsLuVklQcFR65pbGzcvnLlyvfc3NwsCASCMaaac+h0OhgaGoLq6uqaCxcu/OV01tGcTw7uM7VZCAhGx2jpug/vxAd58atzoqKitq1cuXKnvb29saabE+h0Oqiurpbm5eUdrK6uPlspuDrvY0hmO4YIhUIBGq1/X2CmNqFQKL3/79HomZ/z82xEowyy9zFr80zGDqPn/hdeviBmL47ad+fOnRsRERGbQkNDo62srIw97azT2dkJxcXFx0tKSo7Mdh8hY4LD4TDPH2U4MFjMc6tLmZmZzaj+Aw6H0/t9PB4PGCxmRudNJBL0ngeZTAI0Gj3jv+1szfM88Hic8cUEAKCmqlQOAN/ELU2qkEgkySwWK3HRokVcBoMxG9MbDZ1OB83NzdDU1FRQW1t7XiAQHJ+ovu18pbr6Rg6L5ZtoM0EhcUPT0tJW8tWRb0vQqIkvgKqqmhnVfrl2TfANXo+gjKlUio4OWc1M5sjOzjnQUH8rbqLPu3t6moaGhmfc+3q25tGHUqmECoEIUKbIrVkcEkONiIh4jcvlvu7s7OxLo9GmVe7QVCgUCujq6oKGhoaCioqKo9XV1WeM3YDMVPDik1gpyas+XrVyeaKXl8czjyANjbcgI/MNmkg49Q4ECPOH3NyC4RUr+M8IcHt7B1y9WlKRl3/5kElKnD1sfXEoJCzueEBAQGJYWFgGk8nk2djYAIFAgLm4pTw6Ogqjo6Mgl8vhxo0b50tLS4/U19fnLvS2FIWXfxEDQNLmLW9ueW1TxtchHDaQyWRTm4VgYkZHR6G+vhF+/NfP+y5e+vVjiVgwZpKVydOwF0dZW1lZOTGZTD6bzU4LCAiIptPp8HTDL1MwOjoKLS0tUFdXd1IsFudIpdKKgYGB7tloJTrX4MUnsVJTEj9etzY10dHRAQAAGm81wcsZW5CVyQInL69gNCGBjwcAGBx8ANnncypOnTr3H9nn/reD55wovvrQpyIHAHFUzIocGo3mQaPRfBwdHVlubm7bXF1dgcFgABqNNvruglwuh7t374JMJoOOjo7P79y5I+ru7m7q7e1tXQi7MzOh8PIv4pCw2DdaWtte37Au7aPIyCWAxWABjUbPif9HCMbjURtKiaQBfvr5zH9evlJ0uLQ4r/nJMXNiZTIRrMAlJAcHB18HBweWo6Mjy8rKajeJRAJLS0uwtLQECwsLoFAogMfjAYvFgpmZ2XNXMyqVCoaHh2FoaAiGh4cfvwYGBqCvrw+6u7vfvnfvXlNvb29rT09Pq0QsUM7S6c4rNqS/lrZ5U+YPRBKR9M7u9xwqBUUvtNAudH766XSLE8PR49ixE78/8tVnX403Zk7fUR46NUUAIPIPCMdTKJTdNjY2QKPRgE6nA51OB1tbWyCRSIDD4YBAIAAejwcCgfDYUajVakGlUoFarQadTvfY79HX1wf9/f0gl8tBLpfDvXv3HvXw+dxQPYYXMj+d+P7Mmzv+5OHr6/OJWq1GBHeB09TcUiKuq/coKS3/eqIx/wPkiIXC3w6YjAAAAABJRU5ErkJggg==");

+

+  --keyword: #ff79c6;

+  --identifier: #f8f8f2;

+  --comment: #6272a4;

+  --operator: #ff79c6;

+  --punctuation: #f8f8f2;

+  --other: #f8f8f2;

+  --escapeSequence: #bd93f9;

+  --number: #bd93f9;

+  --literal: #f1fa8c;

+  --program: #9090c0;

+  --option: #90b010;

+  --raw-data: #8be9fd;

+

+  --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E");

+  --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E");

+  --clipboard-image: var(--clipboard-image-normal);

+}

+

+@media (prefers-color-scheme: dark) {

+  [data-theme="auto"] {

+    --primary-background: #171921;

+    --secondary-background: #1e202a;

+    --third-background: #2b2e3b;

+    --info-background: #008000;

+    --warning-background: #807000;

+    --error-background: #c03000;

+    --border: #0e1014;

+    --text: #fff;

+    --anchor: #8be9fd;

+    --anchor-focus: #8be9fd;

+    --input-focus: #8be9fd;

+    --strong: #bd93f9;

+    --hint: #7A7C85;

+    --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARMAAABMCAYAAABOBlMuAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjE4OjIyKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQyNDQtYTExZi0yMjc5YmY4NDNhYTAiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ZWRlYjM1NzAtYjZmYy1kMjQ0LWExMWYtMjI3OWJmODQzYWEwIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6ZWRlYjM1NzAtYjZmYy1kMjQ0LWExMWYtMjI3OWJmODQzYWEwIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQyNDQtYTExZi0yMjc5YmY4NDNhYTAiIHN0RXZ0OndoZW49IjIwMTktMTItMDNUMDE6MTg6MjIrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4wIChXaW5kb3dzKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4JZNR8AAAfG0lEQVR4nO2deViTZ7r/7yxkJaxJ2MK+GCBAMCwS1kgUFQSKK4XWWqsz1jpjp3b0tDP1V+eqU391fqfT/mpPPd20drTFDS0KFEVWJSGAEgLIZpAICBJACIRs549Rj1WILAkBfD/XlevySp68z/0S3+/7vPdzLyidTgcLkU2bd+z39/f/q1gshsrKSoJELFCa2iaEuU9K6kb+8uXxv54/fzE8L/eswNT2zCfQpjbAGKS8lPFKSEjIXiaTCSEhIeDj4xNnapsQ5j6rktZGp6UlfxIdzQVzCplmanvmG1hTG2BIAtlc26CgoDfT0tL2e3l5AQCAjY0NkMnk/a9s2k6rrKw8UV8n1JjYTIQ5RlAw14KzmL3xze1vfJyUuMJaq9UCFovFm9qu+YbBxcSPFUYkk8l2Q0NDsvo6ocrQx5+I8Ih4bz6f/0l8fHyKlZXV4/dRKBQwmcwwMpn8A4FAoPgHhH9bV1sxa488wZxoaycnJ/a9e/duCa5fkc3WvAiTI4Ib77p+XdqHG9anbfLy8gAAgLGxMdBpF+bjvzExqJj4scKI0dHRnwQHB++orq7+AgDeMuTxJ2Jl4rqU9PT0EwEBAUQCgTDuGAaDAampqYepVKpHUHDk325Ulw0a266YuFW+Gzdu/MDPz29jfn7+XgA4aOw5ESZP6kvpCXv3vnM8NiaSamVl+fj9BepGNDoGFRN7e/slcXFxO1xcXMDJyWnH7j//H/fi4uJdgutXmgw5z5O8smn7X9euXbvf29sbMBjMhONQKBRYWVlBbGzsbjMzM3JoOG+/sKKwy1h2rd/4elpGRsYuLy+vaDweD2w2Oy1h5ZrCvEunEaeeiVnMiabyl/F2/+X9P+8JDPQHHA5napMWBAYTk6DgSNuEhIS9DAYDAP7tq1i6dOkqOp3OWbNu0wens44emeoxA9lcWwKBYEMkEm2JRKIdHo+3QKFQWJ1Op8ZgMER3d/dVq1evTnFycpr0MSkUCsTExGzH4/Gk1LTME/39/TI0Go1FoVCg1WrVY2NjipGRkcGRkRH5dPwrEZHLXMPCwjJSUlIy3dzcfB+97+rqGhYSEpIOAIiYmBguN3zL77dt3uPh4W5qUxYUBhMTb2/vjeHh4cvR6P/dILK0tITIyEg7BweHr363/Z3Ampqaf1Zcu/zMKiVsyVJvMplsRyKR7IhEor2FhYUbhUJhJCYm2pFIJB6JRAIymQx4PB7QaDRoNBowMzMDJycnwOOn7icjEokQGxu7icFgbLp///7jFY1WqwWlUgkjIyOgUCgO7Ni5Rz48PCwfHh7uGRkZeaBQKOSjo6ODCoVCXlNVKn/6uCsT13FXrVr1emho6BYKhfLMnP7+/omrU9LPX8g+UThloxEMxqJFXjxESAyPQcSEExrLWLNmzW57e/txP/fw8ABHR8cdDAaDt3xF2ru9vb03sVgs0cbGxs/FxWVZUlISj0aj+dna2oKtrS1M5PcwJCgUCry8vODRrs84vPfoH6OjoyCXy6Gvr+/R6+CWrX9s7evrk/b19bWr1Wqli4sLZ8OGDe95eXmxUSjUuAd0cHDwjoqK2sYKXFIhvnldYYTTQpgU4/8+jyASCYDGoCd+ZkYYF8OICYezl8PhuOkbQyAQIDo62s/NzS2np6cHbGxsgEajAYFAAAwGA1gsFia6CE0NgUAABwcHsLe3B61WC2q1eo9WqwWNRgNKpRLUajUQiUSgUCh6zwGHwwGTydzo5+eXBQBnZu8MEJ5keHhYPqyYWMtHR0ZBpVIhYj9FUDONgOUvT12+du3avMDAQJjssdRqNWCxCyrEZdLodDoQi8Ulx44de628NL/V1Pa8iERE8l2dHB2CJvpcq9Nqbt1qKURWj1Njxld0ZGTkAW9v70kLCQC8sEIC8O/HKx8fn2gmk8kHgCk7pRFmzrWyAikASE1tx0Jj2uH0EZHL/N7YtuvT4OBgzmz4OBYSeDweIiMjt2S++vtMP1YYEmmJsCCY8mNOIJtr6+zsHBcZGXmIw+G4mZubG8m0hU9HRwcUFxe/KxQKTyDRsQjznSmJCS9+dVRERMTfQ0NDo2xtbfUGiSFMjtHRUaitrc3Jzc09kHvxVLmp7UFAmC6oZQkvrZLL5RJhReHtiQb5scKIXC7371FRUX90dnYGIpE4JR8Jgn40Gg20t7fXFxYWfnr9+vWjz8sdYi+Osh4vzgUBwZSgtu94V+fs7Hx7YGCgra6u7khLS0u2RCwYeTQgKmYFh8fj/f/g4OAldnZ2prR1wdPd3Q1CofBQSUnJkdLi3N8E93FCY6k+Pj48FxcXjlar1ZSWlh65VvYr4kREmDNg79+/D3FxcW5OTk5uXl5evNbW1tL0jK3ZXV1d1ykUintycvInoaGhdkj+gvGxs7MDPp+/m0AgWMQvS/lyeHhYTqPRPJycnIJSU1NZ3t7eW2g0Gly/fv2oWq1Gij0hzClQ/gHhpLS0tEM8Hm/7I8Ho7++HlpYWsLa2Bg8PDxOb+OKhUCigqakJ7t+/D25ubuDu7g4oFAp0Oh08ePAAvv7666TTWUdzTG0nAsKTYMU3ryuSU18+4+bmFrZo0SIOAICVlRUsXrx4zkakLnRIJBI8CgJ8MtdJp9NBZ2enqL29XWRC8xAQxgUNAHD+3L8KGhoaCp78ABES04JCoX4jJAAAAwMDUFtbe96YpRMQEKbL41DU5ubmko6Ojj2PSgggzD36+/vrb9y4cX425zzw93/8EBjon2is44+NjSkePBjqGRwc7G5v7xBV19w8U5B/3qgrr9+/uWtXUuKKD/TZ9MXh/066/OuFmunO8dGBQ98HBbGSp/t9U6LRaDXK0dHBoeFhuVzeL22/0yFqamopufjLqRJ933ssJi0tLSXV1dWHGAzGbuObOzs8ubqa71vZKpUKOjo6blwpOF8zm/Mu5cVkLlkSaswprAHAaVihgK7O7oSGxltvfXLon3nXK4RHT2cdN4pfKDCAlZyUuMJan02nTmczAaBmunPw4qI3cbnh0/36XICq0+lgcPABp7OrK629vUP5z8++LLh2XXD05L++yxrvC4/F5EZ12WBS8saLS5Ys2U2lUufUY45SqQSlUgkqlQrUavXj19jYGGg0GtBoNKDT6UCn05VotVq1TqfToFAojFar1eh0Og0Wi8XhcDgeGo1+/PhgZmYGOBwOsFgsmJmZ/eY1F+nt7YXa2trs2Z73wdCQBgCMHp1IJpHA09MdPD3dLRIS+OtKisvWvbP7vf2lZdePVFwzbHTwyMiI3hidkZFRUKvUYzOZ48HQkBIA5nWqBAqFAktLC7C0tADmIh88Pz4uMSyUk7hn776DV4tKPn/6d/lNxp1MJqsRCASf8vn8XdMpOjRTVCoVjI2NgUqlAq1WCyMjI9DX1wf379+Hvr6+/Q8ePOgdGRmRKxSKx0WLFAqFXKlUKnQ6nUar1arHq47mxwrD4/F4Eg6HI2GxWDwej7cgkUjWFAqFam5uTjU3N6eRyeQPLSwswNraGqysrIBAIDwWFywW+zja11Qi29LSclIikeSZZPJZBovBAI8XA8HBQR9kZZ3lR8cmvFZSlGe00p8IkwONRkNERBj4+i7a4+XpHv307/IbMakWlciXJbx0nMPh7Jqo0JGh0el0MDo6Cl1dXSCVSkEmk7177969W319fe1DQ0M9KpVKoVarlWq1WjndNhUPG3ApAWDcOxLTLwSDwWAOotFoDBaLxRMIBAsrKysne3t7Xzqd7k2n0/c4OzsDlUoFHA4364IyMDAATU1NxdWikhcq6tXKyhJezljPJZKI2eERS5cZeoWCMD2srCwhPX0tVzk2djiCG//GtfLLUoBxShB0dHTU3Lx580sLC4vtJBLJKMZoNBqQSqUglUqPdnR01PT09DT19/fLHjx40DM0NNQ72933GiSVGgB4JFQK+LfoSAGgnL04yppEIh2xtLS0t7GxcaFSqR7Ozs4fMRgMcHR0nJX8pJs3b54Ui8UXjT7RHIRMIkFK8irfwcEHPwQELUmqvYHUGJkLmJubw8YNa/i9vfffY/px3myQiDTPiEl9nVDDX576jaenZ7SnpyfLUJNrNBqQyWRw+/bt4x0dHTdkMlltV1dXw/XygjkdEv4wB0YOAK0AUM70C8HQ6fSzdDrdm0qlejg6OrLc3Ny2MBiMadWjfR4PHjyAmzdvZs/1v5MxoVAokJK8iicWS95k+nH+s0EiQhqpzQGoVFtYk5a87ba0XQAA34xbpagg/5zoT7s/OGNnZ8eaaYkBuVwOnZ2d5VKpVNTS0lLS2NhYWFVZ3Dujg5qQh6uY+ocvCAiKIPn4+Jz19PSMdnV15VCpVL6Dg4NBViw6nQ5EItHRpqamqzM+2DzHzo4O69amftLQeKsAZrDLgmBY/PyYsCIhfs+SiKUFE5Y8EwqFx11cXDihoaFTjjFAoVAwPDwMHR0dourq6jNCofDHhZqUVnvjmgIAcgAgJyg40mLRokX8kJCQjT4+PussLS1n1JPl7t27UFxcfHguB6mNjY2B7G4naNRTWyygUCjAYDGAx+PB0sICSCSi3vFYLBbCwjjA8vddBQtATKb7d3saBwc7IJPJBpsHjUGDGRYLJBIJLK0sAfucmyIGg4FFi3y8AwNZtycUk5KiS02vvf7WWQaDkejg4DApQwAeh3xDaWnpPoFAcPxFqnP6sEvgGf+A8Bx3d/cvIyIiNi1evHjT8wpNj8fAwACUlZW9P9dD5+/ckcFbf9gd2dcnn9LNAovF4inmZHtXNxdOdBR3+/JlS33pdP29wolEInA4weuiYxOy5vvuTkeHDHb+8c8xvb33Z3R9/N+Df+uIjYk02DwkEsna2trS1d/fNyGeF7uTyw1/7g3R3t4O2OxA/TVghULhcQqFQk1JSfmYSNR/5wD4d6EfgUBwvLS09IhUKhW9qAV5H9YjKQwJi6uvrKw8ERoamhkSEpKp7w7yJEqlEiQSyZmysrJv53qjdaVSCZdyTk+3qFMrAJRHRPLPN95qeifj5fU7mYt8JhyMRqMhMJDFdnF25gDAvBYTpXIMWlpay2fq/8m5mDcIABYGnEcGAGI/VlhBZWX1yZdSkz55OX0dV5+7w9bGGvz8mPrFpK62QskJjf2GTqd7x8bGbpnID4BCoUAmk0lLSkqOiESik2UleS/MakQflYKrXQDQxY1a3tTe3i6KiIjY5OXlxX7e9+rr6wsuXbr0t4ffn9OgMWjghMZQRcLp+8GulRVI/QPC37Wxtnal0ajJtjY2E451ZjiBra31vE9lR2PQQKFQaAAwo98Yi8Xq9fpPd56HO6rlvKWJv/PwcK+JilyCmajWMw6HAzs7+rMFpQOCIn6zHywSFvXm5eUdFAqFZ9Rq9bgHa2trq79w4cK+zz49cAARkmcpL81v/a/Dhz49d+7c3qqqqjyVSjXuOJ1OBxKJpDw3N/fA5V+zax6978cKw/sHhM/raMrnUVdboSy4fPWQSFSjd5yFBQWIRNKEd2IEw1J4JUd88WL+R51d3XrHWVDMnxUTa2tr1zXrNiUGsrmPf7DS4tymCxcu7Kuurs55+kKQSqVN586d23vs+8NHDXUCC5Wzp3/Iy8rKeruysvLM2Nhvo7VVKhXU1tYWnj17du/T7UOdnZ2D7OzsfGGB09raVi4S1RzXl0eFw+EAj8chYjKLVFffyOrq1C8mJBLpWTFRKBRyDofzC4vFWvXk+1ev/CLOzs7eKxAIslQqFeh0Oujp6enKzs7em/XTd7OayTqfKb56sT4rK+sPAoHg5KO/o0KhAKFQmHXy5MkdF3/5+TeZmctXpIXZ29v7zqVcKWNRX1epuXu3U/y8pEw0GmndOZt0dnXVDw0P6/W5oNHoZ30mQ0NDPb29vfvj4+Pf3rR5B/7od188XnEUXr4gDgmL+0NfX5/U19d3d3l5+YGfTnyDtLmcIhXXLsu4UcvfR6PRGGtra9eysrIjYrE45+kt4Fheou/69es/unnz5vm7d+/Wmsre2WRkZGTQ1DYg/JYGiUiTm1ugBAC9IfHPiEmDpFITE7fqJI/H27lmzZpDq5LWtz55t6wUXO3ihMYerK+vz2tpaUFaM0yT8tL81ujYle+TSCTrvEunBU9/voTLd92wYcPHVCqV39XVdXCu7+oYCp1O90Kc50Jk3I5+xVcv1jc3N5d4enpSMzIyvkpK3sh78nORsKg3++yPBS/q1q+hKCm61DSekERGJ3ikp6d/ERsbm1xVVXWwtbX1hRFtFAqFPMLMUyZsDyoQCI7LZDKIiIjwzczM/GpV0vro2TTsRSUqZoX3+vXrP1u9enXi0NAQiESirIdRtggIc5oJ40zq6uryGhoa8ry8vBJCQ0O9USjU94mrN7yWc+EnvaXb5gJMvxCMp6cnl0Kh2Le1tZVXXLs8L1LXefGrWRkZGZ/x+XyeUqkEkUh0vqenZ14HZyG8OEwoJjdrygd37NxTEBkZmWBtbQ3BwcEeKBTq+/UbX3/355Pfzlmn66qk9dGbN29+k8PhbCSRSNDZ2Snb9ae/HCkpKTksEhbN2QTD5NSX+Vu3bj0cHBzsjcFg4O7du1BWVvbNwxB9BIQ5j94I2Fu3bhXW19cDl8sFLBYLHA7Hg0wmf/e77e84ffXlPz6fLSMnQ2paZkJ4eHjmtm3b+B4eHvZkMhlQKBTY29s72dvbfxgUFJT8x7ffP1NRUfHjXErnZ/qFYKKjo7dt3rz5g8DAQPtH/XHa2tpqGhsbC55/BASEuYFeMblz505NTU3NgfDw8PcwGAygUCjw9fW1IJPJn/1130Hv0tLSI4WXL4hny9inYS+Osvbz80tgMpn8jIwMPovFch2vpoiDgwM4ODhwfH19OYsWLeJv3/Hu+cbGxquzXZz5aZYlvMRJT0/fFhkZue3JZmfd3d0gEolOIr4ShPmEXjFpkFRqXlrzSnFnZ+d7Tk5OjzNfXVxcICMjY6ezszNnVdL6vU8HWhmbgKAIkrOzMyc1NTXz0YU4maAuOp0OK1as4EVFRfGEQqHg1dfePHzr1q2rs71S8WOF4f38/BLS09M/iIyM5DxdxLq5uVlcVVU1bgVwBIS5il4xAQCQyWRigUBwJikpKe3JVGQcDgdLly7l2tranti0ecf7IpEoy9hbxX6sMDydTvdevXr1ltjY2F3u7u6AxT73FJ7B3Nwc4uLiwthsdphQKCzZkL7l0/r6+oKbNeVG90+EhMXZL1++fFtycvKHrq6uz4igUqmE5ubmEiTHCWG+8dwrUXD9imz9xtd/jIuLS7N5KpsTjUZDUFCQE4PB+F4oFGYmJW888Mv5k4UTHGpGxC9LYaenp78VEhKyxdHRESgUyoyOh0KhwNraGuLi4qIDAgKi6+rqyjekb/mHMSN6N6RvSdu+ffseNpsdZm09ftuW+vp6EIvFSB9hhHnHpG7rUqm0orW1tdXS0tLj6TIEaDQaaDQaxMfH811dXTl/3Xfw+JUrVz411J01cfWG6IiIiC07d+5McHNzs7ewMGyOFw6HAwcHB6BSqVx3d/fwz7/4rkAgEBwXCoUnHpZonDGrU9J5MTEx27du3Zrm4uKC0beaqq6u/ry+vj7XEPMiIMwmkxKTimuXZe/u+fCkp6fnexPdUfF4PPj7+1szGIydLi4unF1/+kvenTt3RG1tbRXTqfma8lIG39/fP/HVV19NZrFYHpMpzjQTzMzMwNPTE+Pp6Zng6emZ4Ofnl5CesfV8bW1tznQe3/wDwvFeXl7Rvr6+Ca+88kpaUFCQh74GXzqdDrq7u6GpqankRQmdR1hYTNrhUFVVlcXj8d6ysrKy0OfstLS0hPj4eC6Xy+U2NzeDRCI5/sa2XeX37t1rGhwc7BoYGJBN1P+FFbiE5OzszGaxWImvvvrqpoCAAKfp+ERmCpPJBCaTmcnhcDJLS0u/TE59+YxUKhXoi/lg+oVgrKysGJaWlna2trYeaWlpXDabvTMgIGDSfp2KiorzbW1tL0zoPMLCYtJX6uVfs2u++PKowMPDgz+ZIslEIhECAgKAxWJlajSazJ6eHmhra4PW1tZvtmz9o6Czs7O+r6+vfWxsbFir1WosLCzsV6xYkcnj8d7z9vaelmPV0Hh5eYGnp+f2mJiY7UVFRZ/HL0v5tru7+5ZGo1FisVg8Docj4fF4CxsbG1c+nx/m7e39sYeHB7i4uIC5ufmU6r4ODQ1BZWXlifkSrYuA8DRTumIrKytPent78728vCb9HRQKBVgsFhwcHIBOpwObzd4yNja2RaVSwdDQEHR1dcHo6CjQaDRwdXWdsWPV0KBQKPDw8AA7O7udERERO2tra2FgYACoVCo4OTkBjUYDMpkMeDz+8WuqaLVaaGxsbL19+/YzSX8ICPOFqYrJidDQ0AwvLy/e80c/CwaDARKJBI86BdJoNHB3dwe1Wj0nViL6IJPJwGQywdnZGZRKJRAIBDBUx8OBgQEoLS39BtkORpjPTJg1PB61N64pmpqarvb39xvUiLkuJE9CJpPBxsbGYEICANDZ2SlHgtQQ5jtTEhMAgLq6ulyJRFJvDGNeREZGRkAikRSUFuci2cEI85opi0l+7hmBWCzOeV6dToTJcfv27cHr168jxbgR5j1TFhMAgObm5hKZDNl0MAQtLS3Xzpw6hkS8Isx7piUmUqlUIBAIJuyjgzA5Ojs7QSKRINGuCAuCaYmJsKKw68qVK59KJJIu5HFneiiVSigqKjouEolOmtoWBARDMC0xAQC4+MvPJadOnXq3ra1N8yL0dDEkOp0OSktLy/Pz8w8+3d4CAWG+Mm0xAQA4fuy/jl+8ePGju3fvGsqeBY9Wq4XKysrWU6dOvX31yi8mKyyFgGBoZiQmAAD/79D+fadPn96PCMrz0el0UFVV1frtt9+mj9fiAgFhPjNjMQEAyMvLO3Ds2LE/tLS0INmuerh27Vr9999//xoiJAgLEYOEntbVVigB4PNNm3cMpqSkfMRms50McdyFgkqlgqKiovJTp069nZ97BhEShAWJQePYj373xdF1GzbLFQrFx6Ghob766ne8KNy7dw+KiopO5ubmfmTK4tsICMbG4EkxWT99d35l4rre/v7+D0NCQvh0Ot3QU8wL1Go1SKVSTX5+/sH8/PyDSP8bhIWOUTLsLuVklQcFR65pbGzcvnLlyvfc3NwsCASCMaaac+h0OhgaGoLq6uqaCxcu/OV01tGcTw7uM7VZCAhGx2jpug/vxAd58atzoqKitq1cuXKnvb29saabE+h0Oqiurpbm5eUdrK6uPlspuDrvY0hmO4YIhUIBGq1/X2CmNqFQKL3/79HomZ/z82xEowyy9zFr80zGDqPn/hdeviBmL47ad+fOnRsRERGbQkNDo62srIw97azT2dkJxcXFx0tKSo7Mdh8hY4LD4TDPH2U4MFjMc6tLmZmZzaj+Aw6H0/t9PB4PGCxmRudNJBL0ngeZTAI0Gj3jv+1szfM88Hic8cUEAKCmqlQOAN/ELU2qkEgkySwWK3HRokVcBoMxG9MbDZ1OB83NzdDU1FRQW1t7XiAQHJ+ovu18pbr6Rg6L5ZtoM0EhcUPT0tJW8tWRb0vQqIkvgKqqmhnVfrl2TfANXo+gjKlUio4OWc1M5sjOzjnQUH8rbqLPu3t6moaGhmfc+3q25tGHUqmECoEIUKbIrVkcEkONiIh4jcvlvu7s7OxLo9GmVe7QVCgUCujq6oKGhoaCioqKo9XV1WeM3YDMVPDik1gpyas+XrVyeaKXl8czjyANjbcgI/MNmkg49Q4ECPOH3NyC4RUr+M8IcHt7B1y9WlKRl3/5kElKnD1sfXEoJCzueEBAQGJYWFgGk8nk2djYAIFAgLm4pTw6Ogqjo6Mgl8vhxo0b50tLS4/U19fnLvS2FIWXfxEDQNLmLW9ueW1TxtchHDaQyWRTm4VgYkZHR6G+vhF+/NfP+y5e+vVjiVgwZpKVydOwF0dZW1lZOTGZTD6bzU4LCAiIptPp8HTDL1MwOjoKLS0tUFdXd1IsFudIpdKKgYGB7tloJTrX4MUnsVJTEj9etzY10dHRAQAAGm81wcsZW5CVyQInL69gNCGBjwcAGBx8ANnncypOnTr3H9nn/reD55wovvrQpyIHAHFUzIocGo3mQaPRfBwdHVlubm7bXF1dgcFgABqNNvruglwuh7t374JMJoOOjo7P79y5I+ru7m7q7e1tXQi7MzOh8PIv4pCw2DdaWtte37Au7aPIyCWAxWABjUbPif9HCMbjURtKiaQBfvr5zH9evlJ0uLQ4r/nJMXNiZTIRrMAlJAcHB18HBweWo6Mjy8rKajeJRAJLS0uwtLQECwsLoFAogMfjAYvFgpmZ2XNXMyqVCoaHh2FoaAiGh4cfvwYGBqCvrw+6u7vfvnfvXlNvb29rT09Pq0QsUM7S6c4rNqS/lrZ5U+YPRBKR9M7u9xwqBUUvtNAudH766XSLE8PR49ixE78/8tVnX403Zk7fUR46NUUAIPIPCMdTKJTdNjY2QKPRgE6nA51OB1tbWyCRSIDD4YBAIAAejwcCgfDYUajVakGlUoFarQadTvfY79HX1wf9/f0gl8tBLpfDvXv3HvXw+dxQPYYXMj+d+P7Mmzv+5OHr6/OJWq1GBHeB09TcUiKuq/coKS3/eqIx/wPkiIXC3w6YjAAAAABJRU5ErkJggg==");

+

+    --keyword: #ff79c6;

+    --identifier: #f8f8f2;

+    --comment: #6272a4;

+    --operator: #ff79c6;

+    --punctuation: #f8f8f2;

+    --other: #f8f8f2;

+    --escapeSequence: #bd93f9;

+    --number: #bd93f9;

+    --literal: #f1fa8c;

+    --program: #9090c0;

+    --option: #90b010;

+    --raw-data: #8be9fd;

+

+    --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E");

+    --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E");

+    --clipboard-image: var(--clipboard-image-normal);

+  }

+}

+

+.theme-select-wrapper {

+  display: flex;

+  align-items: center;

+}

+

+html {

+  font-size: 100%;

+  -webkit-text-size-adjust: 100%;

+  -ms-text-size-adjust: 100%; }

+

 body {

-  color: black;

-  background: white;

+  font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;

+  font-weight: 400;

+  font-size: 1.125em;

+  line-height: 1.5;

+  color: var(--text);

+  background-color: var(--primary-background); }

+

+/* Skeleton grid */

+.container {

+  position: relative;

+  width: 100%;

+  max-width: 1050px;

+  margin: 0 auto;

+  padding: 0;

+  box-sizing: border-box; }

+

+.column, .columns {

+  width: 100%;

+  float: left;

+  box-sizing: border-box;

+  margin-left: 1%; }

+

+@media print {

+  #global-links, .link-seesrc, .theme-switch-wrapper, #searchInputDiv, .search-groupby {

+    display:none;

+  }

+  .columns {

+    width:100% !important;

+  }

 }

 

-/* used to remove borders from tables and images */

-.borderless, table.borderless td, table.borderless th {

-  border: 0 }

+.column:first-child, .columns:first-child {

+  margin-left: 0; }

 

-table.borderless td, table.borderless th {

-  /* Override padding for "table.docutils td" with "! important".

-     The right padding separates the table cells. */

-  padding: 0 0.5em 0 0 ! important }

+.container .row {

+  display: flex; }

 

-.first {

-  /* Override more specific margin styles with "! important". */

-  margin-top: 0 ! important }

+.three.columns {

+  width: 25.0%;

+  height: 100vh;

+  position: sticky;

+  top: 0px;

+  overflow-y: auto;

+  padding: 2px;

+}

 

-.last, .with-subtitle {

-  margin-bottom: 0 ! important }

+.nine.columns {

+  width: 75.0%;

+  padding-left: 1.5em; }

+

+.twelve.columns {

+  width: 100%;

+  margin-left: 0; }

+

+@media screen and (max-width: 860px) {

+  .three.columns {

+    display: none;

+  }

+  .nine.columns {

+    width: 98.0%;

+  }

+  body {

+    font-size: 1em;

+    line-height: 1.35;

+  }

+}

+

+cite {

+  font-style: italic !important; }

 

-.hidden {

-  display: none }

+

+/* Nim search input */

+div#searchInputDiv {

+  margin-bottom: 1em;

+}

+input#searchInput {

+  width: 80%;

+}

+

+/*

+ * Some custom formatting for input forms.

+ * This also fixes input form colors on Firefox with a dark system theme on Linux.

+ */

+input {

+  -moz-appearance: none;

+  background-color: var(--secondary-background);

+  color: var(--text);

+  border: 1px solid var(--border);

+  font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;

+  font-size: 0.9em;

+  padding: 6px;

+}

+

+input:focus {

+  border: 1px solid var(--input-focus);

+  box-shadow: 0 0 3px var(--input-focus);

+}

+

+select {

+  -moz-appearance: none;

+  background-color: var(--secondary-background);

+  color: var(--text);

+  border: 1px solid var(--border);

+  font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;

+  font-size: 0.9em;

+  padding: 6px;

+}

+

+select:focus {

+  border: 1px solid var(--input-focus);

+  box-shadow: 0 0 3px var(--input-focus);

+}

+

+/* Docgen styles */

+

+:target {

+  border: 2px solid #B5651D;

+  border-style: dotted;

+}

+

+/* Links */

+a {

+  color: var(--anchor);

+  text-decoration: none;

+}

+

+a span.Identifier {

+  text-decoration: underline;

+  text-decoration-color: #aab;

+}

+

+a.reference-toplevel {

+  font-weight: bold;

+}

+

+a.nimdoc {

+  word-spacing: 0.3em;

+}

 

 a.toc-backref {

-  text-decoration: none ;

-  color: black }

+  text-decoration: none;

+  color: var(--text);

+}

 

-blockquote.epigraph {

-  margin: 2em 5em ; }

+a.link-seesrc {

+  color: #607c9f;

+  font-size: 0.9em;

+  font-style: italic;

+}

 

-dl.docutils dd {

-  margin-bottom: 0.5em }

+a:hover, a:focus {

+  color: var(--anchor-focus);

+  text-decoration: underline;

+}

 

-/* Uncomment (and remove this text!) to get bold-faced definition list terms

-dl.docutils dt {

-  font-weight: bold }

-*/

+a:hover span.Identifier {

+  color: var(--anchor);

+}

 

-div.abstract {

-  margin: 2em 5em }

-

-div.abstract p.topic-title {

-  font-weight: bold ;

-  text-align: center }

-

-div.admonition, div.attention, div.caution, div.danger, div.error,

-div.hint, div.important, div.note, div.tip, div.warning {

-  margin: 2em ;

-  border: medium outset ;

-  padding: 1em }

-

-div.admonition p.admonition-title, div.hint p.admonition-title,

-div.important p.admonition-title, div.note p.admonition-title,

-div.tip p.admonition-title {

-  font-weight: bold ;

-  font-family: sans-serif }

-

-div.attention p.admonition-title, div.caution p.admonition-title,

-div.danger p.admonition-title, div.error p.admonition-title,

-div.warning p.admonition-title {

-  color: red ;

-  font-weight: bold ;

-  font-family: sans-serif }

-

-/* Uncomment (and remove this text!) to get reduced vertical space in

-   compound paragraphs.

-div.compound .compound-first, div.compound .compound-middle {

-  margin-bottom: 0.5em }

-

-div.compound .compound-last, div.compound .compound-middle {

-  margin-top: 0.5em }

-*/

 

-div.dedication {

-  margin: 2em 5em ;

-  text-align: center ;

-  font-style: italic }

+sub, sup {

+  position: relative;

+  font-size: 75%;

+  line-height: 0;

+  vertical-align: baseline; }

 

-div.dedication p.topic-title {

-  font-weight: bold ;

-  font-style: normal }

+sup {

+  top: -0.5em; }

 

-div.figure {

-  margin-left: 2em ;

-  margin-right: 2em }

+sub {

+  bottom: -0.25em; }

 

-div.footer, div.header {

-  clear: both;

-  font-size: smaller }

+img {

+  width: auto;

+  height: auto;

+  max-width: 100%;

+  vertical-align: middle;

+  border: 0;

+  -ms-interpolation-mode: bicubic; }

 

-div.line-block {

-  display: block ;

-  margin-top: 1em ;

-  margin-bottom: 1em }

+@media print {

+  * {

+    color: black !important;

+    text-shadow: none !important;

+    background: transparent !important;

+    box-shadow: none !important; }

 

-div.line-block div.line-block {

-  margin-top: 0 ;

-  margin-bottom: 0 ;

-  margin-left: 1.5em }

+  a, a:visited {

+    text-decoration: underline; }

 

-div.sidebar {

-  margin-left: 1em ;

-  border: medium outset ;

-  padding: 1em ;

-  background-color: #ffffee ;

-  width: 40% ;

-  float: right ;

-  clear: right }

+  a[href]:after {

+    content: " (" attr(href) ")"; }

 

-div.sidebar p.rubric {

-  font-family: sans-serif ;

-  font-size: medium }

+  abbr[title]:after {

+    content: " (" attr(title) ")"; }

 

-div.system-messages {

-  margin: 5em }

+  .ir a:after,

+  a[href^="javascript:"]:after,

+  a[href^="#"]:after {

+    content: ""; }

 

-div.system-messages h1 {

-  color: red }

+  pre, blockquote {

+    border: 1px solid #999;

+    page-break-inside: avoid; }

 

-div.system-message {

-  border: medium outset ;

-  padding: 1em }

+  thead {

+    display: table-header-group; }

 

-div.system-message p.system-message-title {

-  color: red ;

-  font-weight: bold }

+  tr, img {

+    page-break-inside: avoid; }

 

-div.topic {

-  margin: 2em;

+  img {

+    max-width: 100% !important; }

+

+  @page {

+    margin: 0.5cm; }

+

+  h1 {

+    page-break-before: always; }

+

+  h1.title {

+    page-break-before: avoid; }

+

+  p, h2, h3 {

+    orphans: 3;

+    widows: 3; }

+

+  h2, h3 {

+    page-break-after: avoid; }

 }

 

-h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,

-h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {

-  margin-top: 0.4em }

 

-h1.title { text-align: center }

-h2.subtitle { text-align: center }

-hr.docutils { width: 75% }

-img.align-left { clear: left }

-img.align-right { clear: right }

+p {

+  margin-top: 0.5em;

+  margin-bottom: 0.5em; }

+

+small {

+  font-size: 85%; }

+

+strong {

+  font-weight: 600;

+  font-size: 0.95em;

+  color: var(--strong); }

+

+em {

+  font-style: italic; }

+

+h1 {

+  font-size: 1.8em;

+  font-weight: 400;

+  padding-bottom: .25em;

+  border-bottom: 6px solid var(--third-background);

+  margin-top: 2.5em;

+  margin-bottom: 1em;

+  line-height: 1.2em; }

+

+h1.title {

+  padding-bottom: 1em;

+  border-bottom: 0px;

+  font-size: 2.5em;

+  text-align: center;

+  font-weight: 900;

+  margin-top: 0.75em;

+  margin-bottom: 0em; }

+

+h2 {

+  font-size: 1.3em;

+  margin-top: 2em; }

+

+h2.subtitle {

+  margin-top: 0em;

+  text-align: center; }

+

+h3 {

+  font-size: 1.125em;

+  font-style: italic;

+  margin-top: 1.5em; }

+

+h4 {

+  font-size: 1.125em;

+  margin-top: 1em; }

+

+h5 {

+  font-size: 1.125em;

+  margin-top: 0.75em; }

+

+h6 {

+  font-size: 1.1em; }

+

+

+ul, ol {

+  padding: 0;

+  margin-top: 0.5em;

+  margin-left: 0.75em; }

+

+ul ul, ul ol, ol ol, ol ul {

+  margin-bottom: 0;

+  margin-left: 1.25em; }

+

+ul.simple > li {

+  list-style-type: circle; }

+

+ul.simple-boot li {

+  list-style-type: none;

+  margin-left: 0em;

+  margin-bottom: 0.5em; }

+

+ol.simple > li, ul.simple > li {

+  margin-bottom: 0.2em;

+  margin-left: 0.4em }

+

+ul.simple.simple-toc > li {

+  margin-top: 1em; }

+

+ul.simple-toc {

+  list-style: none;

+  font-size: 0.9em;

+  margin-left: -0.3em;

+  margin-top: 1em; }

+

+ul.simple-toc > li {

+  list-style-type: none; }

+

+ul.simple-toc-section {

+  list-style-type: circle;

+  margin-left: 0.8em;

+  color: #6c9aae; }

+

+ul.nested-toc-section {

+  list-style-type: circle;

+  margin-left: -0.75em;

+  color: var(--text); }

+

+ul.nested-toc-section > li {

+  margin-left: 1.25em; }

 

-ol.simple, ul.simple {

-  margin-bottom: 1em }

 

 ol.arabic {

-  list-style: decimal }

+  list-style: decimal; }

 

 ol.loweralpha {

-  list-style: lower-alpha }

+  list-style: lower-alpha; }

 

 ol.upperalpha {

-  list-style: upper-alpha }

+  list-style: upper-alpha; }

 

 ol.lowerroman {

-  list-style: lower-roman }

+  list-style: lower-roman; }

 

 ol.upperroman {

-  list-style: upper-roman }

+  list-style: upper-roman; }

+

+ul.auto-toc {

+  list-style-type: none; }

+

+

+dl {

+  margin-bottom: 1.5em; }

+

+dt {

+  margin-bottom: -0.5em;

+  margin-left: 0.0em; }

+

+dd {

+  margin-left: 2.0em;

+  margin-bottom: 3.0em;

+  margin-top: 0.5em; }

+

+

+hr {

+  margin: 2em 0;

+  border: 0;

+  border-top: 1px solid #aaa; }

+

+hr.footnote {

+  width: 25%;

+  border-top: 0.15em solid #999;

+  margin-bottom: 0.15em;

+  margin-top: 0.15em;

+}

+div.footnote-group {

+  margin-left: 1em;

+}

+div.footnote-label {

+  display: inline-block;

+  min-width: 1.7em;

+}

+

+div.option-list {

+  border: 0.1em solid var(--border);

+}

+div.option-list-item {

+  padding-left: 12em;

+  padding-right: 0;

+  padding-bottom: 0.3em;

+  padding-top: 0.3em;

+}

+div.odd {

+  background-color: var(--secondary-background);

+}

+div.option-list-label {

+  margin-left: -11.5em;

+  margin-right: 0em;

+  min-width: 11.5em;

+  display: inline-block;

+  vertical-align: top;

+}

+div.option-list-description {

+  width: calc(100% - 1em);

+  padding-left: 1em;

+  padding-right: 0;

+  display: inline-block;

+}

+

+blockquote {

+  font-size: 0.9em;

+  font-style: italic;

+  padding-left: 0.5em;

+  margin-left: 0;

+  border-left: 5px solid #bbc;

+}

+

+blockquote.markdown-quote {

+  font-size: 0.9rem;  /* use rem to avoid recursion */

+  font-style: normal;

+}

+

+.pre, span.tok {

+  font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace;

+  font-weight: 500;

+  font-size: 0.85em;

+  color: var(--text);

+  background-color: var(--third-background);

+  padding-left: 3px;

+  padding-right: 3px;

+  border-radius: 4px;

+}

+

+span.tok {

+  border: 1px solid #808080;

+  padding-bottom: 0.1em;

+  margin-right: 0.2em;

+}

+

+.copyToClipBoard {

+  position: relative;

+}

+

+pre {

+  font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace;

+  color: var(--text);

+  font-weight: 500;

+  display: inline-block;

+  box-sizing: border-box;

+  min-width: 100%;

+  padding: 0.5em;

+  margin-top: 0.5em;

+  margin-bottom: 0.5em;

+  font-size: 0.85em;

+  white-space: pre !important;

+  overflow-y: hidden;

+  overflow-x: visible;

+  background-color: var(--secondary-background);

+  border: 1px solid var(--border);

+  -webkit-border-radius: 6px;

+  -moz-border-radius: 6px;

+  border-radius: 6px;

+}

+

+.copyToClipBoardBtn {

+  visibility: hidden;

+  position: absolute;

+  width: 24px;

+  border-radius: 4px;

+  background-image: var(--clipboard-image);

+  right: 5px;

+  top: 13px;

+  background-color: var(--secondary-background);

+  padding: 11px;

+  border: 0;

+}

+

+.copyToClipBoard:hover .copyToClipBoardBtn {

+  visibility: visible;

+}

+

+.pre-scrollable {

+  max-height: 340px;

+  overflow-y: scroll; }

+

+

+/* Nim line-numbered tables */

+.line-nums-table {

+  width: 100%;

+  table-layout: fixed; }

+

+table.line-nums-table {

+  border-radius: 4px;

+  border: 1px solid var(--border);

+  background-color: var(--secondary-background);

+  border-collapse: separate;

+  margin-top: 15px;

+  margin-bottom: 25px; }

+

+.line-nums-table tbody {

+  border: none; }

+

+.line-nums-table td pre {

+  border: none;

+  background-color: transparent; }

+

+.line-nums-table td.blob-line-nums {

+  width: 28px; }

+

+.line-nums-table td.blob-line-nums pre {

+  color: #b0b0b0;

+  -webkit-filter: opacity(75%);

+  filter: opacity(75%);

+  text-align: right;

+  border-color: transparent;

+  background-color: transparent;

+  padding-left: 0px;

+  margin-left: 0px;

+  padding-right: 0px;

+  margin-right: 0px; }

+

+

+table {

+  max-width: 100%;

+  background-color: transparent;

+  margin-top: 0.5em;

+  margin-bottom: 1.5em;

+  border-collapse: collapse;

+  border-color: var(--third-background);

+  border-spacing: 0;

+}

+

+table:not(.line-nums-table) {

+  font-size: 0.9em;

+}

+

+table th, table td {

+  padding: 0px 0.5em 0px;

+  border-color: var(--third-background);

+}

+

+table th {

+  background-color: var(--third-background);

+  border-color: var(--third-background);

+  font-weight: bold; }

+

+table th.docinfo-name {

+  background-color: transparent;

+  text-align: right;

+}

+

+table:not(.line-nums-table) tr:hover {

+  background-color: var(--third-background); }

+

+

+/* rst2html default used to remove borders from tables and images */

+.borderless, table.borderless td, table.borderless th {

+  border: 0; }

+

+table.borderless td, table.borderless th {

+  /* Override padding for "table.docutils td" with "! important".

+     The right padding separates the table cells. */

+  padding: 0 0.5em 0 0 !important; }

+

+.admonition {

+  padding: 0.3em;

+  background-color: var(--secondary-background);

+  border-left: 0.4em solid #7f7f84;

+  margin-bottom: 0.5em;

+  -webkit-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2);

+      -moz-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2);

+          box-shadow: 0 5px 8px -6px rgba(0,0,0,.2);

+}

+.admonition-info {

+  border-color: var(--info-background);

+}

+.admonition-info-text {

+  color: var(--info-background);

+}

+.admonition-warning {

+  border-color: var(--warning-background);

+}

+.admonition-warning-text {

+  color: var(--warning-background);

+}

+.admonition-error {

+  border-color: var(--error-background);

+}

+.admonition-error-text {

+  color: var(--error-background);

+}

+

+.first {

+  /* Override more specific margin styles with "! important". */

+  margin-top: 0 !important; }

+

+.last, .with-subtitle {

+  margin-bottom: 0 !important; }

+

+.hidden {

+  display: none; }

+

+blockquote.epigraph {

+  margin: 2em 5em; }

+

+dl.docutils dd {

+  margin-bottom: 0.5em; }

+

+object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {

+  overflow: hidden; }

+

+

+div.figure {

+  margin-left: 2em;

+  margin-right: 2em; }

+

+div.footer, div.header {

+  clear: both;

+  text-align: center;

+  color: #666;

+  font-size: smaller; }

+

+div.footer {

+  padding-top: 5em; }

+

+div.line-block {

+  display: block;

+  margin-top: 1em;

+  margin-bottom: 1em; }

+

+div.line-block div.line-block {

+  margin-top: 0;

+  margin-bottom: 0;

+  margin-left: 1.5em; }

+

+div.topic {

+  margin: 2em; }

+

+div.search_results {

+  background-color: var(--third-background);

+  margin: 3vh 5vw;

+  padding: 1em;

+  border: 1px solid #4d4d4d;

+  position: fixed;

+  top: 10px;

+  isolation: isolate;

+  max-width: calc(100vw - 6em);

+  z-index: 1;

+  max-height: calc(100vh - 6em);

+  overflow-y: scroll;}

+

+div#global-links ul {

+  margin-left: 0;

+  list-style-type: none; }

+

+div#global-links > simple-boot {

+  margin-left: 3em; }

+

+hr.docutils {

+  width: 75%; }

+

+img.align-left, .figure.align-left, object.align-left {

+  clear: left;

+  float: left;

+  margin-right: 1em; }

+

+img.align-right, .figure.align-right, object.align-right {

+  clear: right;

+  float: right;

+  margin-left: 1em; }

+

+img.align-center, .figure.align-center, object.align-center {

+  display: block;

+  margin-left: auto;

+  margin-right: auto; }

+

+.align-left {

+  text-align: left; }

+

+.align-center {

+  clear: both;

+  text-align: center; }

+

+.align-right {

+  text-align: right; }

+

+/* reset inner alignment in figures */

+div.align-right {

+  text-align: inherit; }

 

 p.attribution {

-  text-align: right ;

-  margin-left: 50% }

+  text-align: right;

+  margin-left: 50%; }

 

 p.caption {

-  font-style: italic }

+  font-style: italic; }

 

 p.credits {

-  font-style: italic ;

-  font-size: smaller }

+  font-style: italic;

+  font-size: smaller; }

 

 p.label {

-  white-space: nowrap }

+  white-space: nowrap; }

 

 p.rubric {

-  font-weight: bold ;

-  font-size: larger ;

-  color: maroon ;

-  text-align: center }

-

-p.sidebar-title {

-  font-family: sans-serif ;

-  font-weight: bold ;

-  font-size: larger }

-

-p.sidebar-subtitle {

-  font-family: sans-serif ;

-  font-weight: bold }

+  font-weight: bold;

+  font-size: larger;

+  color: maroon;

+  text-align: center; }

 

 p.topic-title {

-  font-weight: bold }

+  font-weight: bold; }

 

 pre.address {

-  margin-bottom: 0 ;

-  margin-top: 0 ;

-  font-family: serif ;

-  font-size: 100% }

+  margin-bottom: 0;

+  margin-top: 0;

+  font: inherit; }

 

-pre, span.pre {

-  background-color:#F9F9F9;

-  border:1px dotted #2F6FAB;

-  color:black;

-}

+pre.literal-block, pre.doctest-block, pre.math, pre.code {

+  margin-left: 2em;

+  margin-right: 2em; }

 

-pre {padding:1em;}

+pre.code .ln {

+  color: grey; }

 

-pre.literal-block, pre.doctest-block {

-  margin-left: 2em ;

-  margin-right: 2em }

+/* line numbers */

+pre.code, code {

+  background-color: #eeeeee; }

 

-span.classifier {

-  font-family: sans-serif ;

-  font-style: oblique }

+pre.code .comment, code .comment {

+  color: #5c6576; }

 

-span.classifier-delimiter {

-  font-family: sans-serif ;

-  font-weight: bold }

+pre.code .keyword, code .keyword {

+  color: #3B0D06;

+  font-weight: bold; }

 

-span.interpreted {

-  font-family: sans-serif }

+pre.code .literal.string, code .literal.string {

+  color: #0c5404; }

 

-span.option {

-  white-space: nowrap }

+pre.code .name.builtin, code .name.builtin {

+  color: #352b84; }

+

+pre.code .deleted, code .deleted {

+  background-color: #DEB0A1; }

+

+pre.code .inserted, code .inserted {

+  background-color: #A3D289; }

 

-span.pre { white-space: pre }

+span.classifier {

+  font-style: oblique; }

+

+span.classifier-delimiter {

+  font-weight: bold; }

 

 span.problematic {

-  color: red }

+  color: #b30000; }

 

 span.section-subtitle {

   /* font-size relative to parent (h1..h6 element) */

-  font-size: 80% }

+  font-size: 80%; }

 

-table.citation {

-  border-left: solid 1px gray;

-  margin-left: 1px }

+span.DecNumber {

+  color: var(--number); }

 

-table.docinfo {

-  margin: 2em 4em }

+span.BinNumber {

+  color: var(--number); }

 

-table.docutils {

-  margin-top: 0.5em ;

-  margin-bottom: 0.5em }

+span.HexNumber {

+  color: var(--number); }

 

-table.footnote {

-  border-left: solid 1px black;

-  margin-left: 1px }

+span.OctNumber {

+  color: var(--number); }

 

-table.docutils td, table.docutils th,

-table.docinfo td, table.docinfo th {

-  padding-left: 0.5em ;

-  padding-right: 0.5em ;

-  vertical-align: top }

+span.FloatNumber {

+  color: var(--number); }

 

-table.docutils th.field-name, table.docinfo th.docinfo-name {

-  font-weight: bold ;

-  text-align: left ;

-  white-space: nowrap ;

-  padding-left: 0 }

+span.Identifier {

+  color: var(--identifier); }

 

-h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,

-h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {

-  font-size: 100% }

+span.Keyword {

+  font-weight: 600;

+  color: var(--keyword); }

 

-ul.auto-toc {

-  list-style-type: none }

+span.StringLit {

+  color: var(--literal); }

 

-a.reference {

-  color: #E00000;

-  font-weight:bold;

-}

+span.LongStringLit {

+  color: var(--literal); }

 

-a.reference:hover {

-  color: #E00000;

-  background-color: #ffff00;

-  display: margin;

-  font-weight:bold;

-}

+span.CharLit {

+  color: var(--literal); }

 

-div.topic ul {

-  list-style-type: none;

-}

+span.EscapeSequence {

+  color: var(--escapeSequence); }

+

+span.Operator {

+  color: var(--operator); }

+

+span.Punctuation {

+  color: var(--punctuation); }

+

+span.Comment, span.LongComment {

+  font-style: italic;

+  font-weight: 400;

+  color: var(--comment); }

+

+span.RegularExpression {

+  color: darkviolet; }

+

+span.TagStart {

+  color: darkviolet; }

+

+span.TagEnd {

+  color: darkviolet; }

+

+span.Key {

+  color: #252dbe; }

+

+span.Value {

+  color: #252dbe; }

+

+span.RawData {

+  color: var(--raw-data); }

+

+span.Assembler {

+  color: #252dbe; }

+

+span.Preprocessor {

+  color: #252dbe; }

+

+span.Directive {

+  color: #252dbe; }

+

+span.option {

+  font-weight: bold;

+  font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace;

+  color: var(--option); }

+

+span.Prompt {

+  font-weight: bold;

+  color: red; }

+

+span.ProgramOutput {

+  font-weight: bold;

+  color: #808080; }

+

+span.program {

+  font-weight: bold;

+  color: var(--program);

+  text-decoration: underline;

+  text-decoration-color: var(--hint);

+  text-decoration-thickness: 0.05em;

+  text-underline-offset: 0.15em; }

+

+span.Command, span.Rule, span.Hyperlink,

+span.Label, span.Reference, span.Other {

+  color: var(--other); }

+

+/* Pop type, const, proc, and iterator defs in nim def blocks */

+dt pre > span.Identifier, dt pre > span.Operator {

+  color: var(--identifier);

+  font-weight: 700; }

+

+dt pre > span.Keyword ~ span.Identifier, dt pre > span.Identifier ~ span.Identifier,

+dt pre > span.Operator ~ span.Identifier, dt pre > span.Other ~ span.Identifier {

+  color: var(--identifier);

+  font-weight: inherit; }

+

+/* Nim sprite for the footer (taken from main page favicon) */

+.nim-sprite {

+  display: inline-block;

+  width: 51px;

+  height: 14px;

+  background-position: 0 0;

+  background-size: 51px 14px;

+  -webkit-filter: opacity(50%);

+  filter: opacity(50%);

+  background-repeat: no-repeat;

+  background-image: var(--nim-sprite-base64);

+  margin-bottom: 5px; }

+

+span.pragmadots {

+  /* Position: relative frees us up to make the dots

+  look really nice without fucking up the layout and

+  causing bulging in the parent container */

+  position: relative;

+  /* 1px down looks slightly nicer */

+  top: 1px;

+  padding: 2px;

+  background-color: var(--third-background);

+  border-radius: 4px;

+  margin: 0 2px;

+  cursor: pointer;

+  font-size: 0.8em; }

+

+span.pragmadots:hover {

+  background-color: var(--hint); }

+

+span.pragmawrap {

+  display: none; }

+

+span.attachedType {

+  display: none;

+  visibility: hidden; }

diff --git a/doc/nimfix.rst b/doc/nimfix.rst
deleted file mode 100644
index 62064fe69..000000000
--- a/doc/nimfix.rst
+++ /dev/null
@@ -1,56 +0,0 @@
-=====================
-  Nimfix User Guide
-=====================
-
-:Author: Andreas Rumpf
-:Version: |nimversion|
-
-**WARNING**: Nimfix is currently beta-quality.
-
-Nimfix is a tool to help you upgrade from Nimrod (<= version 0.9.6) to
-Nim (=> version 0.10.0).
-
-It performs 3 different actions:
-
-1. It makes your code case consistent.
-2. It renames every symbol that has a deprecation rule. So if a module has a
-   rule ``{.deprecated: [TFoo: Foo].}`` then ``TFoo`` is replaced by ``Foo``.
-3. It can also check that your identifiers adhere to the official style guide
-   and optionally modify them to do so (via ``--styleCheck:auto``).
-
-Note that ``nimfix`` defaults to **overwrite** your code unless you
-use ``--overwriteFiles:off``! But hey, if you do not use a version control
-system by this day and age, your project is already in big trouble.
-
-
-Installation
-------------
-
-Nimfix is part of the compiler distribution. Compile via::
-
-  nim c compiler/nimfix/nimfix.nim
-  mv compiler/nimfix/nimfix bin
-
-Or on windows::
-
-  nim c compiler\nimfix\nimfix.nim
-  move compiler\nimfix\nimfix.exe bin
-
-Usage
------
-
-Usage:
-  nimfix [options] projectfile.nim
-
-Options:
-
-  --overwriteFiles:on|off       overwrite the original nim files. DEFAULT is ON!
-  --wholeProject                overwrite every processed file.
-  --checkExtern:on|off          style check also extern names
-  --styleCheck:on|off|auto      performs style checking for identifiers
-                                and suggests an alternative spelling;
-                                'auto' corrects the spelling.
-
-In addition, all command line options of Nim are supported.
-
-
diff --git a/doc/nimgrep.md b/doc/nimgrep.md
new file mode 100644
index 000000000..63f760051
--- /dev/null
+++ b/doc/nimgrep.md
@@ -0,0 +1,128 @@
+=========================
+  nimgrep User's manual
+=========================
+
+:Author: Andreas Rumpf
+:Version: 1.6.0
+
+.. default-role:: option
+.. contents::
+
+Nimgrep is a command line tool for search and replace tasks. It can search for
+regex or peg patterns and can search whole directories at once. User
+confirmation for every single replace operation can be requested.
+
+Nimgrep has particularly good support for Nim's
+eccentric *style insensitivity* (see option `-y` below).
+Apart from that it is a generic text manipulation tool.
+
+
+Installation
+============
+
+Compile nimgrep with the command:
+
+  ```cmd
+  nim c -d:release tools/nimgrep.nim
+  ```
+
+And copy the executable somewhere in your ``$PATH``.
+
+
+Command line switches
+=====================
+
+.. include:: nimgrep_cmdline.txt
+
+Path filter options
+-------------------
+
+Let us assume we have file `dirA/dirB/dirC/file.nim`.
+Filesystem path options will match for these parts of the path:
+
+| option              | matches for                        |
+| :------------------ | :--------------------------------  |
+| `--[not]extensions` | ``nim``                            |
+| `--[not]filename`   | ``file.nim``                       |
+| `--[not]dirname`    | ``dirA`` and ``dirB`` and ``dirC`` |
+| `--[not]dirpath`    | ``dirA/dirB/dirC``                 |
+
+Combining multiple filter options together and negating them
+------------------------------------------------------------
+
+Options for filtering can be provided multiple times so they form a list,
+which works as:
+* positive filters
+  `--filename`, `--dirname`, `--dirpath`, `--inContext`,
+  `--inFile` accept files/matches if *any* pattern from the list is hit
+* negative filters
+  `--notfilename`, `--notdirname`, `--notdirpath`, `--notinContext`,
+  `--notinFile` accept files/matches if *no* pattern from the list is hit.
+
+In other words the same filtering option repeated many times means logical OR.
+
+.. Important::
+  Different filtering options are related by logical AND: they all must
+  be true for a match to be accepted.
+  E.g. `--filename:F --dirname:D1 --notdirname:D2` means
+  `filename(F) AND dirname(D1) AND (NOT dirname(D2))`.
+
+So negative filtering patterns are effectively related by logical OR also:
+`(NOT PAT1) AND (NOT PAT2) == NOT (PAT1 OR PAT2)`:literal: in pseudo-code.
+
+That means you can always use only 1 such an option with logical OR, e.g.
+`--notdirname:PAT1 --notdirname:PAT2` is fully equivalent to
+`--notdirname:'PAT1|PAT2'`.
+
+.. Note::
+   If you want logical AND on patterns you should compose 1 appropriate pattern,
+   possibly combined with multi-line mode `(?s)`:literal:.
+   E.g. to require that multi-line context of matches has occurrences of
+   **both** PAT1 and PAT2 use positive lookaheads (`(?=PAT)`:literal:):
+     ```cmd
+     nimgrep --inContext:'(?s)(?=.*PAT1)(?=.*PAT2)'
+     ```
+
+Meaning of `^`:literal: and `$`:literal:
+========================================
+
+`nimgrep`:cmd: PCRE engine is run in a single-line mode so
+`^`:literal: matches the beginning of whole input *file* and
+`$`:literal: matches the end of *file* (or whole input *string* for
+options like `--filename`).
+
+Add the `(?m)`:literal: modifier to the beginning of your pattern for
+`^`:literal: and `$`:literal: to match the beginnings and ends of *lines*.
+
+Examples
+========
+
+All examples below use default PCRE Regex patterns:
+
++ To search recursively in Nim files using style-insensitive identifiers:
+
+    ```cmd
+    nimgrep --recursive --ext:'nim|nims' --ignoreStyle
+    # short: -r --ext:'nim|nims' -y
+    ```
+
+  .. Note:: we used `'` quotes to avoid special treatment of `|` symbol
+    for shells like Bash
+
++ To exclude version control directories (Git, Mercurial=hg, Subversion=svn)
+  from the search:
+    ```cmd
+    nimgrep --notdirname:'^\.git$' --notdirname:'^\.hg$' --notdirname:'^\.svn$'
+    # short: --ndi:'^\.git$' --ndi:'^\.hg$' --ndi:'^\.svn$'
+    ```
++ To search only in paths containing the `tests`:literal: sub-directory
+  recursively:
+    ```cmd
+    nimgrep --recursive --dirname:'^tests$'
+    # short: -r --di:'^tests$'
+    # or using --dirpath:
+    nimgrep --recursive --dirpath:'(^|/)tests($|/)'
+    # short: -r --pa:'(^|/)tests($|/)'
+    ```
++ Nimgrep can search multi-line, e.g. to find files containing `import`:literal:
+  and then `strutils`:literal: use pattern `'import(.|\n)*?strutils'`:literal:.
diff --git a/doc/nimgrep.rst b/doc/nimgrep.rst
deleted file mode 100644
index 791ead162..000000000
--- a/doc/nimgrep.rst
+++ /dev/null
@@ -1,50 +0,0 @@
-=========================
-  nimgrep User's manual
-=========================
-
-:Author: Andreas Rumpf
-:Version: 0.9
-
-
-Nimgrep is a command line tool for search&replace tasks. It can search for
-regex or peg patterns and can search whole directories at once. User
-confirmation for every single replace operation can be requested.
-
-Nimgrep has particularly good support for Nim's
-eccentric *style insensitivity*. Apart from that it is a generic text
-manipulation tool.
-
-
-Installation
-============
-
-Compile nimgrep with the command::
-
-  nim c -d:release tools/nimgrep.nim
-
-And copy the executable somewhere in your ``$PATH``.
-
-
-Command line switches
-=====================
-
-Usage:
-  nimgrep [options] [pattern] [replacement] (file/directory)*
-Options:
-  --find, -f          find the pattern (default)
-  --replace, -r       replace the pattern
-  --peg               pattern is a peg
-  --re                pattern is a regular expression (default); extended
-                      syntax for the regular expression is always turned on
-  --recursive         process directories recursively
-  --confirm           confirm each occurrence/replacement; there is a chance
-                      to abort any time without touching the file
-  --stdin             read pattern from stdin (to avoid the shell's confusing
-                      quoting rules)
-  --word, -w          the match should have word boundaries (buggy for pegs!)
-  --ignoreCase, -i    be case insensitive
-  --ignoreStyle, -y   be style insensitive
-  --ext:EX1|EX2|...   only search the files with the given extension(s)
-  --verbose           be verbose: list every processed file
-  --help, -h          shows this help
-  --version, -v       shows the version
diff --git a/doc/nimgrep_cmdline.txt b/doc/nimgrep_cmdline.txt
new file mode 100644
index 000000000..6f6887bc4
--- /dev/null
+++ b/doc/nimgrep_cmdline.txt
@@ -0,0 +1,136 @@
+
+Usage:
+
+* To search:
+
+      nimgrep [options] PATTERN [(FILE/DIRECTORY)*/-]
+
+* To replace:
+
+      nimgrep [options] PATTERN --replace REPLACEMENT (FILE/DIRECTORY)*/-
+
+* To list file names:
+
+      nimgrep [options] --filenames [PATTERN] [(FILE/DIRECTORY)*]
+
+Positional arguments, from left to right:
+1) PATTERN is either Regex (default) or Peg if `--peg` is specified.
+   PATTERN and REPLACEMENT should be skipped when `--stdin` is specified.
+2) REPLACEMENT supports `$1`, `$#` notations for captured groups in PATTERN.
+
+   .. DANGER:: `--replace` mode **DOES NOT** ask confirmation
+      unless `--confirm` is specified!
+
+3) Final arguments are a list of paths (FILE/DIRECTORY) or a standalone
+   minus `-` or not specified (empty):
+
+   * empty, current directory `.` is assumed (not with `--replace`)
+
+     .. Note:: so when no FILE/DIRECTORY/`-` is specified nimgrep
+       does **not** read the pipe, but searches files in the current
+       dir instead!
+   * `-`, read buffer once from stdin: pipe or terminal input;
+     in `--replace` mode the result is directed to stdout;
+     it's not compatible with `--stdin`, `--filenames`, or `--confirm`
+
+
+   For any given DIRECTORY nimgrep searches only its immediate files without
+   traversing subdirectories unless `--recursive` is specified.
+
+In replacement mode we require all 3 positional arguments to avoid damaging.
+
+Options:
+* Mode of operation:
+  --find, -f          find the PATTERN (default)
+  --replace, -!       replace the PATTERN to REPLACEMENT, rewriting the files
+  --confirm           confirm each occurrence/replacement; there is a chance
+                      to abort any time without touching the file
+  --filenames         just list filenames. Provide a PATTERN to find it in
+                      the filenames (not in the contents of a file) or run
+                      with empty pattern to just list all files:
+
+                          nimgrep --filenames               # In current dir
+                          nimgrep --filenames "" DIRECTORY
+                           # Note empty pattern "", lists all files in DIRECTORY
+* Interprete patterns:
+  --peg               PATTERN and PAT are Peg
+  --re                PATTERN and PAT are regular expressions (default)
+  --rex, -x           use the "extended" syntax for the regular expression
+                      so that whitespace is not significant
+  --word, -w          matches should have word boundaries (buggy for pegs!)
+  --ignoreCase, -i    be case-insensitive in PATTERN and PAT
+  --ignoreStyle, -y   be style insensitive in PATTERN and PAT
+  .. Note:: PATTERN and patterns PAT (see below in other options) are all either
+     Regex or Peg simultaneously and options `--rex`, `--word`, `--ignoreCase`,
+     and `--ignoreStyle` are applied to all of them.
+
+* File system walk:
+  --recursive, -r      process directories recursively
+  --follow             follow all symlinks when processing recursively
+  --sortTime, -s[:asc|desc]
+                       order files by the last modification time (default: off):
+                       ascending (recent files go last) or descending
+
+* Filter files (based on filesystem paths):
+
+  .. Hint:: Instead of `not` you can type just `n` for negative options below.
+
+  --ex[tensions]:EX1|EX2|...
+                       only search the files with the given extension(s),
+                       empty one (`--ex`) means files with missing extension
+  --notex[tensions]:EX1|EX2|...
+                       exclude files having given extension(s), use empty one to
+                       skip files with no extension (like some binary files are)
+  --fi[lename]:PAT     search only files whose name matches pattern PAT
+  --notfi[lename]:PAT  skip files whose name matches pattern PAT
+  --di[rname]:PAT      select files that in their path have a directory name
+                       that matches pattern PAT
+  --notdi[rname]:PAT   do not descend into directories whose name (not path)
+                       matches pattern PAT
+  --dirp[ath]:PAT      select only files whose whole relative directory path
+                       matches pattern PAT
+  --notdirp[ath]:PAT   skip files whose whole relative directory path
+                       matches pattern PAT
+
+* Filter files (based on file contents):
+  --inF[ile]:PAT      select files containing a (not displayed) match of PAT
+  --notinF[ile]:PAT   skip files containing a match of PAT
+  --bin:on|off|only   process binary files? (detected by \0 in first 1K bytes)
+                      (default: on - binary and text files treated the same way)
+  --text, -t          process only text files, the same as `--bin:off`
+
+* Filter matches:
+  --inC[ontext]:PAT   select only matches containing a match of PAT in their
+                      surrounding context (multiline with `-c`, `-a`, `-b`)
+  --notinC[ontext]:PAT
+                      skip matches not containing a match of PAT
+                      in their surrounding context
+
+* Represent results:
+  --nocolor           output will be given without any colors
+  --color[:on]        force color even if output is redirected (default: auto)
+  --colorTheme:THEME  select color THEME from `simple` (default),
+                      `bnw` (black and white), `ack`, or `gnu` (GNU grep)
+  --count             only print counts of matches for files that matched
+  --context:N, -c:N   print N lines of leading context before every match and
+                      N lines of trailing context after it (default N: 0)
+  --afterContext:N, -a:N
+                      print N lines of trailing context after every match
+  --beforeContext:N, -b:N
+                      print N lines of leading context before every match
+  --group, -g         group matches by file
+  --newLine, -l       display every matching line starting from a new line
+  --cols[:N]          limit max displayed columns/width of output lines from
+                      files by N characters, cropping overflows (default: off)
+  --cols:auto, -%     calculate columns from terminal width for every line
+  --onlyAscii, -@     display only printable ASCII Latin characters 0x20-0x7E
+                      substitutions: 0 -> ^@, 1 -> ^A, ... 0x1F -> ^_,
+                                     0x7F -> '7F, ..., 0xFF -> 'FF
+
+* Miscellaneous:
+  --threads:N, -j:N   speed up search by N additional workers (default: 0, off)
+  --stdin             read PATTERN from stdin (to avoid the shell's confusing
+                      quoting rules) and, if `--replace` given, REPLACEMENT
+  --verbose           be verbose: list every processed file
+  --help, -h          shows this help
+  --version, -v       shows the version
diff --git a/doc/niminst.md b/doc/niminst.md
new file mode 100644
index 000000000..cc399c57a
--- /dev/null
+++ b/doc/niminst.md
@@ -0,0 +1,197 @@
+=========================
+  niminst User's manual
+=========================
+
+:Author: Andreas Rumpf
+:Version: |nimversion|
+
+.. default-role:: code
+.. include:: rstcommon.rst
+.. contents::
+
+Introduction
+============
+
+niminst is a tool to generate an installer for a Nim program. Currently
+it can create an installer for Windows
+via [Inno Setup](http://www.jrsoftware.org/isinfo.php) as well as
+installation/deinstallation scripts for UNIX. Later versions will support
+Linux' package management systems.
+
+niminst works by reading a configuration file that contains all the
+information that it needs to generate an installer for the different operating
+systems.
+
+
+Configuration file
+==================
+
+niminst uses the Nim [parsecfg](parsecfg.html) module to parse the
+configuration file. Here's an example of how the syntax looks like:
+
+.. include:: mytest.cfg
+     :literal:
+
+The value of a key-value pair can reference user-defined variables via
+the `$variable` notation: They can be defined in the command line with the
+`--var:name=value`:option: switch. This is useful to not hard-coding the
+program's version number into the configuration file, for instance.
+
+It follows a description of each possible section and how it affects the
+generated installers.
+
+
+Project section
+---------------
+The project section gathers general information about your project. It must
+contain the following key-value pairs:
+
+====================   =======================================================
+Key                    description
+====================   =======================================================
+`Name`                 the project's name; this needs to be a single word
+`DisplayName`          the project's long name; this can contain spaces. If
+                       not specified, this is the same as `Name`.
+`Version`              the project's version
+`OS`                   the OSes to generate C code for; for example:
+                       `"windows;linux;macosx"`
+`CPU`                  the CPUs to generate C code for; for example:
+                       `"i386;amd64;powerpc"`
+`Authors`              the project's authors
+`Description`          the project's description
+`App`                  the application's type: "Console" or "GUI". If
+                       "Console", niminst generates a special batch file
+                       for Windows to open up the command-line shell.
+`License`              the filename of the application's license
+====================   =======================================================
+
+
+`files` key
+-------------
+
+Many sections support the `files` key. Listed filenames
+can be separated by semicolon or the `files` key can be repeated. Wildcards
+in filenames are supported. If it is a directory name, all files in the
+directory are used:
+
+    [Config]
+    Files: "configDir"
+    Files: "otherconfig/*.conf;otherconfig/*.cfg"
+
+
+Config section
+--------------
+
+The `config` section currently only supports the `files` key. Listed files
+will be installed into the OS's configuration directory.
+
+
+Documentation section
+---------------------
+
+The `documentation` section supports the `files` key.
+Listed files will be installed into the OS's native documentation directory
+(which might be ``$appdir/doc``).
+
+There is a `start` key which determines whether the Windows installer
+generates a link to e.g. the ``index.html`` of your documentation.
+
+
+Other section
+-------------
+
+The `other` section currently only supports the `files` key.
+Listed files will be installed into the application installation directory
+(`$appdir`).
+
+
+Lib section
+-----------
+
+The `lib` section currently only supports the `files` key.
+Listed files will be installed into the OS's native library directory
+(which might be `$appdir/lib`).
+
+
+Windows section
+---------------
+
+The `windows` section supports the `files` key for Windows-specific files.
+Listed files will be installed into the application installation directory
+(`$appdir`).
+
+Other possible options are:
+
+====================   =======================================================
+Key                    description
+====================   =======================================================
+`BinPath`              paths to add to the Windows `%PATH%` environment
+                       variable. Example: ``BinPath: r"bin;dist\mingw\bin"``
+`InnoSetup`            boolean flag whether an Inno Setup installer should be
+                       generated for Windows. Example: `InnoSetup: "Yes"`
+====================   =======================================================
+
+
+UnixBin section
+---------------
+
+The `UnixBin` section currently only supports the `files` key.
+Listed files will be installed into the OS's native bin directory
+(e.g. ``/usr/local/bin``). The exact location depends on the
+installation path the user specifies when running the `install.sh` script.
+
+
+Unix section
+------------
+
+Possible options are:
+
+====================   =======================================================
+Key                    description
+====================   =======================================================
+`InstallScript`        boolean flag whether an installation shell script
+                       should be generated. Example: `InstallScript: "Yes"`
+`UninstallScript`      boolean flag whether a de-installation shell script
+                       should be generated.
+                       Example: `UninstallScript: "Yes"`
+====================   =======================================================
+
+
+InnoSetup section
+-----------------
+
+Possible options are:
+
+====================   =======================================================
+Key                    description
+====================   =======================================================
+`path`                 Path to Inno Setup.
+                       Example: ``path = r"c:\inno setup 5\iscc.exe"``
+`flags`                Flags to pass to Inno Setup.
+                       Example: `flags = "/Q"`
+====================   =======================================================
+
+
+C_Compiler section
+------------------
+
+Possible options are:
+
+====================   =======================================================
+Key                    description
+====================   =======================================================
+`path`                 Path to the C compiler.
+`flags`                Flags to pass to the C Compiler.
+                       Example: `flags = "-w"`
+====================   =======================================================
+
+
+Real-world example
+==================
+
+The installers for the Nim compiler itself are generated by niminst. Have a
+look at its configuration file:
+
+.. include:: ../compiler/installer.ini
+     :literal:
+
diff --git a/doc/niminst.rst b/doc/niminst.rst
deleted file mode 100644
index bf5cb0f50..000000000
--- a/doc/niminst.rst
+++ /dev/null
@@ -1,195 +0,0 @@
-=========================
-  niminst User's manual
-=========================
-
-:Author: Andreas Rumpf
-:Version: |nimversion|
-
-.. contents::
-
-Introduction
-============
-
-niminst is a tool to generate an installer for a Nim program. Currently
-it can create an installer for Windows
-via `Inno Setup <http://www.jrsoftware.org/isinfo.php>`_ as well as
-installation/deinstallation scripts for UNIX. Later versions will support
-Linux' package management systems.
-
-niminst works by reading a configuration file that contains all the
-information that it needs to generate an installer for the different operating
-systems.
-
-
-Configuration file
-==================
-
-niminst uses the Nim `parsecfg <parsecfg.html>`_ module to parse the
-configuration file. Here's an example of how the syntax looks like:
-
-.. include:: mytest.cfg
-     :literal:
-
-The value of a key-value pair can reference user-defined variables via
-the ``$variable`` notation: They can be defined in the command line with the
-``--var:name=value`` switch. This is useful to not hard-coding the
-program's version number into the configuration file, for instance.
-
-It follows a description of each possible section and how it affects the
-generated installers.
-
-
-Project section
----------------
-The project section gathers general information about your project. It must
-contain the following key-value pairs:
-
-====================   =======================================================
-Key                    description
-====================   =======================================================
-``Name``               the project's name; this needs to be a single word
-``DisplayName``        the project's long name; this can contain spaces. If
-                       not specified, this is the same as ``Name``.
-``Version``            the project's version
-``OS``                 the OSes to generate C code for; for example:
-                       ``"windows;linux;macosx"``
-``CPU``                the CPUs to generate C code for; for example:
-                       ``"i386;amd64;powerpc"``
-``Authors``            the project's authors
-``Description``        the project's description
-``App``                the application's type: "Console" or "GUI". If
-                       "Console", niminst generates a special batch file
-                       for Windows to open up the command line shell.
-``License``            the filename of the application's license
-====================   =======================================================
-
-
-``files`` key
--------------
-
-Many sections support the ``files`` key. Listed filenames
-can be separated by semicolon or the ``files`` key can be repeated. Wildcards
-in filenames are supported. If it is a directory name, all files in the
-directory are used::
-
-  [Config]
-  Files: "configDir"
-  Files: "otherconfig/*.conf;otherconfig/*.cfg"
-
-
-Config section
---------------
-
-The ``config`` section currently only supports the ``files`` key. Listed files
-will be installed into the OS's configuration directory.
-
-
-Documentation section
----------------------
-
-The ``documentation`` section supports the ``files`` key.
-Listed files will be installed into the OS's native documentation directory
-(which might be ``$appdir/doc``).
-
-There is a ``start`` key which determines whether the Windows installer
-generates a link to e.g. the ``index.html`` of your documentation.
-
-
-Other section
--------------
-
-The ``other`` section currently only supports the ``files`` key.
-Listed files will be installed into the application installation directory
-(``$appdir``).
-
-
-Lib section
------------
-
-The ``lib`` section currently only supports the ``files`` key.
-Listed files will be installed into the OS's native library directory
-(which might be ``$appdir/lib``).
-
-
-Windows section
----------------
-
-The ``windows`` section supports the ``files`` key for Windows specific files.
-Listed files will be installed into the application installation directory
-(``$appdir``).
-
-Other possible options are:
-
-====================   =======================================================
-Key                    description
-====================   =======================================================
-``BinPath``            paths to add to the Windows ``%PATH%`` environment
-                       variable. Example: ``BinPath: r"bin;dist\mingw\bin"``
-``InnoSetup``          boolean flag whether an Inno Setup installer should be
-                       generated for Windows. Example: ``InnoSetup: "Yes"``
-====================   =======================================================
-
-
-UnixBin section
----------------
-
-The ``UnixBin`` section currently only supports the ``files`` key.
-Listed files will be installed into the OS's native bin directory
-(e.g. ``/usr/local/bin``). The exact location depends on the
-installation path the user specifies when running the ``install.sh`` script.
-
-
-Unix section
-------------
-
-Possible options are:
-
-====================   =======================================================
-Key                    description
-====================   =======================================================
-``InstallScript``      boolean flag whether an installation shell script
-                       should be generated. Example: ``InstallScript: "Yes"``
-``UninstallScript``    boolean flag whether a deinstallation shell script
-                       should be generated.
-                       Example: ``UninstallScript: "Yes"``
-====================   =======================================================
-
-
-InnoSetup section
------------------
-
-Possible options are:
-
-====================   =======================================================
-Key                    description
-====================   =======================================================
-``path``               Path to Inno Setup.
-                       Example: ``path = r"c:\inno setup 5\iscc.exe"``
-``flags``              Flags to pass to Inno Setup.
-                       Example: ``flags = "/Q"``
-====================   =======================================================
-
-
-C_Compiler section
-------------------
-
-Possible options are:
-
-====================   =======================================================
-Key                    description
-====================   =======================================================
-``path``               Path to the C compiler.
-``flags``              Flags to pass to the C Compiler.
-                       Example: ``flags = "-w"``
-====================   =======================================================
-
-
-Real world example
-==================
-
-The installers for the Nim compiler itself are generated by niminst. Have a
-look at its configuration file:
-
-.. include:: ../compiler/installer.ini
-     :literal:
-
diff --git a/doc/nims.md b/doc/nims.md
new file mode 100644
index 000000000..987cc2096
--- /dev/null
+++ b/doc/nims.md
@@ -0,0 +1,352 @@
+================================
+          NimScript
+================================
+
+.. default-role:: code
+.. include:: rstcommon.rst
+
+Strictly speaking, `NimScript` is the subset of Nim that can be evaluated
+by Nim's builtin virtual machine (VM). This VM is used for Nim's compiletime
+function evaluation features.
+
+The `nim`:cmd: executable processes the ``.nims`` configuration files in
+the following directories (in this order; later files overwrite
+previous settings):
+
+1) If environment variable `XDG_CONFIG_HOME` is defined,
+   ``$XDG_CONFIG_HOME/nim/config.nims`` or
+   ``~/.config/nim/config.nims`` (POSIX) or
+   ``%APPDATA%/nim/config.nims`` (Windows). This file can be skipped
+   with the `--skipUserCfg`:option: command line option.
+2) ``$parentDir/config.nims`` where ``$parentDir`` stands for any
+   parent directory of the project file's path. These files can be
+   skipped with the `--skipParentCfg`:option: command line option.
+3) ``$projectDir/config.nims`` where ``$projectDir`` stands for the
+   project's path. This file can be skipped with the `--skipProjCfg`:option:
+   command line option.
+4) A project can also have a project specific configuration file named
+   ``$project.nims`` that resides in the same directory as
+   ``$project.nim``. This file can be skipped with the same
+   `--skipProjCfg`:option: command line option.
+
+For available procs and implementation details see [nimscript](nimscript.html).
+
+
+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.
+
+* `random.randomize()` requires an `int64` explicitly passed as argument, you *must* pass a Seed integer.
+
+
+Standard library modules
+========================
+
+At least the following standard library modules are available:
+
+* [algorithm](algorithm.html)
+* [base64](base64.html)
+* [bitops](bitops.html)
+* [chains](chains.html)
+* [colors](colors.html)
+* [complex](complex.html)
+* [distros](distros.html)
+* [std/editdistance](editdistance.html)
+* [htmlgen](htmlgen.html)
+* [htmlparser](htmlparser.html)
+* [httpcore](httpcore.html)
+* [json](json.html)
+* [lenientops](lenientops.html)
+* [macros](macros.html)
+* [math](math.html)
+* [options](options.html)
+* [os](os.html)
+* [parsecfg](parsecfg.html)
+* [parsecsv](parsecsv.html)
+* [parsejson](parsejson.html)
+* [parsesql](parsesql.html)
+* [parseutils](parseutils.html)
+* [punycode](punycode.html)
+* [random](random.html)
+* [ropes](ropes.html)
+* [std/setutils](setutils.html)
+* [stats](stats.html)
+* [strformat](strformat.html)
+* [strmisc](strmisc.html)
+* [strscans](strscans.html)
+* [strtabs](strtabs.html)
+* [strutils](strutils.html)
+* [sugar](sugar.html)
+* [unicode](unicode.html)
+* [unidecode](unidecode.html)
+* [uri](uri.html)
+* [std/wordwrap](wordwrap.html)
+* [xmlparser](xmlparser.html)
+
+In addition to the standard Nim syntax ([system](system.html) module),
+NimScripts support the procs and templates defined in the
+[nimscript](nimscript.html) module too.
+
+See also:
+* [Check the tests for more information about modules compatible with NimScript](
+  https://github.com/nim-lang/Nim/blob/devel/tests/test_nimscript.nims)
+
+
+NimScript as a configuration file
+=================================
+
+A command-line switch `--FOO`:option: is written as `switch("FOO")` in
+NimScript. Similarly, command-line `--FOO:VAL`:option: translates to
+`switch("FOO", "VAL")`.
+
+Here are few examples of using the `switch` proc:
+
+  ```nim
+  # command-line: --opt:size
+  switch("opt", "size")
+  # command-line: --define:release or -d:release
+  switch("define", "release")
+  # command-line: --forceBuild
+  switch("forceBuild")
+  # command-line: --hint[Conf]:off or --hint:Conf:off
+  switch("hint", "[Conf]:off")
+  ```
+
+NimScripts also support `--`:option: templates for convenience, which look
+like command-line switches written as-is in the NimScript file. So the
+above example can be rewritten as:
+
+  ```nim
+  --opt:size
+  --define:release
+  --forceBuild
+  ```
+
+**Note**: In general, the *define* switches can also be set in
+NimScripts using `switch` or `--`, as shown in above examples. Few
+`define` switches such as `-d:strip`:option:, `-d:lto`:option: and
+`-d:lto_incremental`:option: cannot be set in NimScripts.
+
+
+NimScript as a build tool
+=========================
+
+The `task` template that the `system` module defines allows a NimScript
+file to be used as a build tool. The following example defines a
+task `build` that is an alias for the `c`:option: command:
+
+  ```nim
+  task build, "builds an example":
+    setCommand "c"
+  ```
+
+
+In fact, as a convention the following tasks should be available:
+
+=========     ===================================================
+Task          Description
+=========     ===================================================
+`help`        List all the available NimScript tasks along with their docstrings.
+`build`       Build the project with the required
+              backend (`c`:option:, `cpp`:option: or `js`:option:).
+`tests`       Runs the tests belonging to the project.
+`bench`       Runs benchmarks belonging to the project.
+=========     ===================================================
+
+
+Look at the module [distros](distros.html) for some support of the
+OS's native package managers.
+
+
+Nimble integration
+==================
+
+See the [Nimble readme](https://github.com/nim-lang/nimble#readme)
+for more information.
+
+
+Standalone NimScript
+====================
+
+NimScript can also be used directly as a portable replacement for Bash and
+Batch files. Use `nim myscript.nims`:cmd: to run ``myscript.nims``. For example,
+installation of Nimble could be accomplished with this simple script:
+
+  ```nim
+  mode = ScriptMode.Verbose
+
+  var id = 0
+  while dirExists("nimble" & $id):
+    inc id
+
+  exec "git clone https://github.com/nim-lang/nimble.git nimble" & $id
+
+  withDir "nimble" & $id & "/src":
+    exec "nim c nimble"
+
+  mvFile "nimble" & $id & "/src/nimble".toExe, "bin/nimble".toExe
+  ```
+
+On Unix, you can also use the shebang `#!/usr/bin/env nim`, as long as your filename
+ends with ``.nims``:
+
+  ```nim
+  #!/usr/bin/env nim
+  mode = ScriptMode.Silent
+
+  echo "hello world"
+  ```
+
+Use `#!/usr/bin/env -S nim e --hints:off` to disable hints and relax the file extension constraint.
+
+
+Benefits
+========
+
+Cross-Platform
+--------------
+
+It is a cross-platform scripting language that can run where Nim can run,
+e.g. you can not run Batch or PowerShell on Linux or Mac,
+the Bash for Linux might not run on Mac,
+there are no unit tests tools for Batch, etc.
+
+NimScript can detect on which platform, operating system,
+architecture, and even which Linux distribution is running on,
+allowing the same script to support a lot of systems.
+
+See the following (incomplete) example:
+
+  ```nim
+  import std/distros
+
+  # Architectures.
+  if defined(amd64):
+    echo "Architecture is x86 64Bits"
+  elif defined(i386):
+    echo "Architecture is x86 32Bits"
+  elif defined(arm):
+    echo "Architecture is ARM"
+
+  # Operating Systems.
+  if defined(linux):
+    echo "Operating System is GNU Linux"
+  elif defined(windows):
+    echo "Operating System is Microsoft Windows"
+  elif defined(macosx):
+    echo "Operating System is Apple OS X"
+
+  # Distros.
+  if detectOs(Ubuntu):
+    echo "Distro is Ubuntu"
+  elif detectOs(ArchLinux):
+    echo "Distro is ArchLinux"
+  elif detectOs(Debian):
+    echo "Distro is Debian"
+  ```
+
+
+Uniform Syntax
+--------------
+
+The syntax, style, and rest of the ecosystem is the same as for compiled Nim,
+that means there is nothing new to learn, no context switch for developers.
+
+
+Powerful Metaprogramming
+------------------------
+
+NimScript can use Nim's templates, macros, types, concepts, effect tracking system, and more,
+you can create modules that work on compiled Nim and also on interpreted NimScript.
+
+`func` will still check for side effects, `debugEcho` also works as expected,
+making it ideal for functional scripting metaprogramming.
+
+This is an example of a third party module that uses macros and templates to
+translate text strings on unmodified NimScript:
+
+  ```nim
+  import nimterlingua
+  nimterlingua("translations.cfg")
+  echo "cat"  # Run with -d:RU becomes "kot", -d:ES becomes "gato", ...
+  ```
+
+translations.cfg
+
+  ```none
+  [cat]
+  ES = gato
+  IT = gatto
+  RU = kot
+  FR = chat
+  ```
+
+
+* [Nimterlingua](https://nimble.directory/pkg/nimterlingua)
+
+
+Graceful Fallback
+-----------------
+
+Some features of compiled Nim may not work on NimScript,
+but often a graceful and seamless fallback degradation is used.
+
+See the following NimScript:
+
+  ```nim
+  if likely(true):
+    discard
+  elif unlikely(false):
+    discard
+
+  proc foo() {.compiletime.} = echo NimVersion
+
+  static:
+    echo CompileDate
+  ```
+
+
+`likely()`, `unlikely()`, `static:` and `{.compiletime.}`
+will produce no code at all when run on NimScript,
+but still no error nor warning is produced and the code just works.
+
+Evolving Scripting language
+---------------------------
+
+NimScript evolves together with Nim,
+[occasionally new features might become available on NimScript](
+https://github.com/nim-lang/Nim/pulls?q=nimscript+is%3Amerged),
+adapted from compiled Nim or added as new features on both.
+
+Scripting Language with a Package Manager
+-----------------------------------------
+
+You can create your own modules to be compatible with NimScript,
+and check [Nimble](https://nimble.directory)
+to search for third party modules that may work on NimScript.
+
+DevOps Scripting
+----------------
+
+You can use NimScript to deploy to production, run tests, build projects, do benchmarks,
+generate documentation, and all kinds of DevOps/SysAdmin specific tasks.
+
+* [An example of a third party NimScript that can be used as a project-agnostic
+  tool.](https://github.com/kaushalmodi/nim_config#list-available-tasks)
diff --git a/doc/nims.rst b/doc/nims.rst
deleted file mode 100644
index d4ef0055f..000000000
--- a/doc/nims.rst
+++ /dev/null
@@ -1,119 +0,0 @@
-================================
-          NimScript
-================================
-
-Strictly speaking, ``NimScript`` is the subset of Nim that can be evaluated
-by Nim's builtin virtual machine (VM). This VM is used for Nim's compiletime
-function evaluation features, but also replaces Nim's existing configuration
-system.
-
-So instead of a ``myproject.nim.cfg`` configuration file, you can use
-a ``myproject.nims`` file that simply contains Nim code controlling the
-compilation process. For a directory wide configuration, use ``config.nims``
-instead of ``nim.cfg``.
-
-The VM cannot deal with ``importc``, the FFI is not available, so there are not
-many stdlib modules that you can use with Nim's VM. However, at least the
-following modules are available:
-
-* `strutils <strutils.html>`_
-* `ospaths <ospaths.html>`_
-* `math <math.html>`_
-* `distros <distros.html>`_
-
-The `system <system.html>`_ module in NimScript mode additionally supports
-these operations: `nimscript <nimscript.html>`_.
-
-
-NimScript as a configuration file
-=================================
-
-What is ``x.y.key = "value"`` in the configuration file
-becomes ``switch("x.y.key", "value")``. ``--option`` is ``switch("option")``.
-The ``system`` module also exports 2 ``--`` templates for convenience:
-
-.. code-block:: nim
-  --forceBuild
-  # is the same as:
-  switch("forceBuild")
-
-
-NimScript as a build tool
-=========================
-
-The ``task`` template that the ``system`` module defines allows a NimScript
-file to be used as a build tool. The following example defines a
-task ``build`` that is an alias for the ``c`` command:
-
-.. code-block:: nim
-  task build, "builds an example":
-    setCommand "c"
-
-
-In fact, as a convention the following tasks should be available:
-
-=========     ===================================================
-Task          Description
-=========     ===================================================
-``build``     Build the project with the required
-              backend (``c``, ``cpp`` or ``js``).
-``tests``     Runs the tests belonging to the project.
-``bench``     Runs benchmarks belonging to the project.
-=========     ===================================================
-
-
-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.
-
-
-Nimble integration
-==================
-
-See the `Nimble readme <https://github.com/nim-lang/nimble#readme>`_
-for more information.
-
-
-
-
-Standalone NimScript
-====================
-
-NimScript can also be used directly as a portable replacement for Bash and
-Batch files. Use ``nim e myscript.nims`` to run ``myscript.nims``. For example,
-installation of Nimble is done with this simple script:
-
-.. code-block:: nim
-
-  mode = ScriptMode.Verbose
-
-  var id = 0
-  while dirExists("nimble" & $id):
-    inc id
-
-  exec "git clone https://github.com/nim-lang/nimble.git nimble" & $id
-
-  withDir "nimble" & $id & "/src":
-    exec "nim c nimble"
-
-  mvFile "nimble" & $id & "/src/nimble".toExe, "bin/nimble".toExe
-
-You can also use the shebang ``#!/usr/bin/env nim``, as long as your filename
-ends with ``.nims``:
-
-.. code-block:: nim
-
-  #!/usr/bin/env nim
-  mode = ScriptMode.Silent
-
-  echo "hello world"
diff --git a/doc/nimsuggest.md b/doc/nimsuggest.md
new file mode 100644
index 000000000..3d076a6f5
--- /dev/null
+++ b/doc/nimsuggest.md
@@ -0,0 +1,176 @@
+================================
+  Nim IDE Integration Guide
+================================
+
+:Author: Unknown
+:Version: |nimversion|
+
+.. default-role:: code
+.. include:: rstcommon.rst
+.. contents::
+
+
+Nim differs from many other compilers in that it is really fast,
+and being so fast makes it suited to provide external queries for
+text editors about the source code being written. Through the
+`nimsuggest`:cmd: tool, any IDE
+can query a ``.nim`` source file and obtain useful information like
+definition of symbols or suggestions for completion.
+
+This document will guide you through the available options. If you
+want to look at practical examples of nimsuggest support you can look
+at the
+[various editor integrations](https://github.com/Araq/Nim/wiki/Editor-Support)
+already available.
+
+
+Installation
+============
+
+Nimsuggest is part of Nim's core. Build it via:
+
+  ```cmd
+  koch nimsuggest
+  ```
+
+
+Nimsuggest invocation
+=====================
+
+Run it via `nimsuggest --stdin --debug myproject.nim`:cmd:. Nimsuggest is a
+server that takes queries that are related to `myproject`. There is some
+support so that you can throw random ``.nim`` files which are not part
+of `myproject` at Nimsuggest too, but usually the query refer to modules/files
+that are part of `myproject`.
+
+`--stdin`:option: means that Nimsuggest reads the query from `stdin`. This is great
+for testing things out and playing with it but for an editor communication
+via sockets is more reasonable so that is the default. It listens to port 6000
+by default.
+
+Nimsuggest is basically a frontend for the nim compiler so `--path`:option: flags and
+[config files](nimc.html#compiler-usage-configuration-files)
+can be used to specify additional dependencies like 
+`nimsuggest --stdin --debug --path:"dependencies" myproject.nim`:cmd:.
+
+
+Specifying the location of the query
+------------------------------------
+
+Nimsuggest then waits for queries to process. A query consists of a
+cryptic 3 letter "command" `def` or `con` or `sug` or `use` followed by
+a location. A query location consists of:
+
+
+``file.nim``
+:   This is the name of the module or include file the query refers to.
+
+``dirtyfile.nim``
+:   This is optional.
+
+    The `file` parameter is enough for static analysis, but IDEs
+    tend to have *unsaved buffers* where the user may still be in
+    the middle of typing a line. In such situations the IDE can
+    save the current contents to a temporary file and then use the
+    ``dirtyfile.nim`` option to tell Nimsuggest that ``foobar.nim`` should
+    be taken from ``temporary/foobar.nim``.
+
+
+``line``
+:   An integer with the line you are going to query. For the compiler
+    lines start at **1**.
+
+``col``
+:   An integer with the column you are going to query. For the
+    compiler columns start at **0**.
+
+
+Definitions
+-----------
+
+The `def` Nimsuggest command performs a query about the definition
+of a specific symbol. If available, Nimsuggest will answer with the
+type, source file, line/column information and other accessory data
+if available like a docstring. With this information an IDE can
+provide the typical *Jump to definition* where a user puts the
+cursor on a symbol or uses the mouse to select it and is redirected
+to the place where the symbol is located.
+
+Since Nim is implemented in Nim, one of the nice things of
+this feature is that any user with an IDE supporting it can quickly
+jump around the standard library implementation and see exactly
+what a proc does, learning about the language and seeing real life
+examples of how to write/implement specific features.
+
+Nimsuggest will always answer with a single definition or none if it
+can't find any valid symbol matching the position of the query.
+
+
+Suggestions
+-----------
+
+The `sug` Nimsuggest command performs a query about possible
+completion symbols at some point in the file.
+
+The typical usage scenario for this option is to call it after the
+user has typed the dot character for [the object oriented call
+syntax](tut2.html#object-oriented-programming-method-call-syntax).
+Nimsuggest will try to return the suggestions sorted first by scope
+(from innermost to outermost) and then by item name.
+
+
+Invocation context
+------------------
+
+The `con` Nimsuggest command is very similar to the suggestions
+command, but instead of being used after the user has typed a dot
+character, this one is meant to be used after the user has typed
+an opening brace to start typing parameters.
+
+
+Symbol usages
+-------------
+
+The `use` Nimsuggest command lists all usages of the symbol at
+a position. IDEs can use this to find all the places in the file
+where the symbol is used and offer the user to rename it in all
+places at the same time.
+
+For this kind of query the IDE will most likely ignore all the
+type/signature info provided by Nimsuggest and concentrate on the
+filename, line and column position of the multiple returned answers.
+
+
+
+Parsing nimsuggest output
+=========================
+
+Nimsuggest output is always returned on single lines separated by
+tab characters (``\t``). The values of each column are:
+
+1. Three characters indicating the type of returned answer (e.g.
+   `def` for definition, `sug` for suggestion, etc).
+2. Type of the symbol. This can be `skProc`, `skLet`, and just
+   about any of the enums defined in the module ``compiler/ast.nim``.
+3. Fully qualified path of the symbol. If you are querying a symbol
+   defined in the ``proj.nim`` file, this would have the form
+   `proj.symbolName`.
+4. Type/signature. For variables and enums this will contain the
+   type of the symbol, for procs, methods and templates this will
+   contain the full unique signature (e.g. `proc (File)`).
+5. Full path to the file containing the symbol.
+6. Line where the symbol is located in the file. Lines start to
+   count at **1**.
+7. Column where the symbol is located in the file. Columns start
+   to count at **0**.
+8. Docstring for the symbol if available or the empty string. To
+   differentiate the docstring from end of answer,
+   the docstring is always provided enclosed in double quotes, and
+   if the docstring spans multiple lines, all following lines of the
+   docstring will start with a blank space to align visually with
+   the starting quote.
+
+   Also, you won't find raw ``\n`` characters breaking the one
+   answer per line format. Instead you will need to parse sequences
+   in the form ``\xHH``, where *HH* is a hexadecimal value (e.g.
+   newlines generate the sequence ``\x0A``).
diff --git a/doc/nimsuggest.rst b/doc/nimsuggest.rst
deleted file mode 100644
index c5c0c7518..000000000
--- a/doc/nimsuggest.rst
+++ /dev/null
@@ -1,167 +0,0 @@
-================================
-  Nim IDE Integration Guide
-================================
-
-:Author: Unknown
-:Version: |nimversion|
-
-.. contents::
-
-
-Nim differs from many other compilers in that it is really fast,
-and being so fast makes it suited to provide external queries for
-text editors about the source code being written. Through the
-``nimsuggest`` tool, any IDE
-can query a ``.nim`` source file and obtain useful information like
-definition of symbols or suggestions for completion.
-
-This document will guide you through the available options. If you
-want to look at practical examples of nimsuggest support you can look
-at the
-`various editor integrations <https://github.com/Araq/Nim/wiki/Editor-Support>`_
-already available.
-
-
-Installation
-============
-
-Nimsuggest is part of Nim's core. Build it via::
-
-  koch nimsuggest
-
-
-Nimsuggest invocation
-=====================
-
-Run it via ``nimsuggest --stdin --debug --v2 myproject.nim``. Nimsuggest is a
-server that takes queries that are related to ``myproject``. There is some
-support so that you can throw random ``.nim`` files which are not part
-of ``myproject`` at Nimsuggest too, but usually the query refer to modules/files
-that are part of ``myproject``.
-
-``--stdin`` means that Nimsuggest reads the query from ``stdin``. This is great
-for testing things out and playing with it but for an editor communication
-via sockets is more reasonable so that is the default. It listens to port 6000
-by default.
-
-
-Specifying the location of the query
-------------------------------------
-
-Nimsuggest than waits for queries to process. A query consists of a
-cryptic 3 letter "command" ``def`` or ``con`` or ``sug`` or ``use`` followed by
-a location. A query location consists of:
-
-
-``file.nim``
-    This is the name of the module or include file the query refers to.
-
-``dirtyfile.nim``
-    This is optional.
-
-    The ``file`` paramater is enough for static analysis, but IDEs
-    tend to have *unsaved buffers* where the user may still be in
-    the middle of typing a line. In such situations the IDE can
-    save the current contents to a temporary file and then use the
-    ``dirtyfile.nim`` option to tell Nimsuggest that ``foobar.nim`` should
-    be taken from ``temporary/foobar.nim``.
-
-
-``line``
-    An integer with the line you are going to query. For the compiler
-    lines start at **1**.
-
-``col``
-    An integer with the column you are going to query. For the
-    compiler columns start at **0**.
-
-
-Definitions
------------
-
-The ``def`` Nimsuggest command performs a query about the definition
-of a specific symbol. If available, Nimsuggest will answer with the
-type, source file, line/column information and other accessory data
-if available like a docstring. With this information an IDE can
-provide the typical *Jump to definition* where a user puts the
-cursor on a symbol or uses the mouse to select it and is redirected
-to the place where the symbol is located.
-
-Since Nim is implemented in Nim, one of the nice things of
-this feature is that any user with an IDE supporting it can quickly
-jump around the standard library implementation and see exactly
-what a proc does, learning about the language and seeing real life
-examples of how to write/implement specific features.
-
-Nimsuggest will always answer with a single definition or none if it
-can't find any valid symbol matching the position of the query.
-
-
-Suggestions
------------
-
-The ``sug`` Nimsuggest command performs a query about possible
-completion symbols at some point in the file.
-
-The typical usage scenario for this option is to call it after the
-user has typed the dot character for `the object oriented call
-syntax <tut2.html#method-call-syntax>`_. Nimsuggest will try to return
-the suggestions sorted first by scope (from innermost to outermost)
-and then by item name.
-
-
-Invocation context
-------------------
-
-The ``con`` Nimsuggest command is very similar to the suggestions
-command, but instead of being used after the user has typed a dot
-character, this one is meant to be used after the user has typed
-an opening brace to start typing parameters.
-
-
-Symbol usages
--------------
-
-The ``use`` Nimsuggest command lists all usages of the symbol at
-a position. IDEs can use this to find all the places in the file
-where the symbol is used and offer the user to rename it in all
-places at the same time.
-
-For this kind of query the IDE will most likely ignore all the
-type/signature info provided by Nimsuggest and concentrate on the
-filename, line and column position of the multiple returned answers.
-
-
-
-Parsing nimsuggest output
-=========================
-
-Nimsuggest output is always returned on single lines separated by
-tab characters (``\t``). The values of each column are:
-
-1. Three characters indicating the type of returned answer (e.g.
-   ``def`` for definition, ``sug`` for suggestion, etc).
-2. Type of the symbol. This can be ``skProc``, ``skLet``, and just
-   about any of the enums defined in the module ``compiler/ast.nim``.
-3. Full qualitifed path of the symbol. If you are querying a symbol
-   defined in the ``proj.nim`` file, this would have the form
-   ``proj.symbolName``.
-4. Type/signature. For variables and enums this will contain the
-   type of the symbol, for procs, methods and templates this will
-   contain the full unique signature (e.g. ``proc (File)``).
-5. Full path to the file containing the symbol.
-6. Line where the symbol is located in the file. Lines start to
-   count at **1**.
-7. Column where the symbol is located in the file. Columns start
-   to count at **0**.
-8. Docstring for the symbol if available or the empty string. To
-   differentiate the docstring from end of answer,
-   the docstring is always provided enclosed in double quotes, and
-   if the docstring spans multiple lines, all following lines of the
-   docstring will start with a blank space to align visually with
-   the starting quote.
-
-   Also, you won't find raw ``\n`` characters breaking the one
-   answer per line format. Instead you will need to parse sequences
-   in the form ``\xHH``, where *HH* is a hexadecimal value (e.g.
-   newlines generate the sequence ``\x0A``).
diff --git a/doc/overview.md b/doc/overview.md
new file mode 100644
index 000000000..b21eb1e68
--- /dev/null
+++ b/doc/overview.md
@@ -0,0 +1,9 @@
+=============================
+Nim Documentation Overview
+=============================
+
+:Author: Andreas Rumpf
+:Version: |nimversion|
+
+.. include:: docs.md
+
diff --git a/doc/overview.rst b/doc/overview.rst
deleted file mode 100644
index e01520d7c..000000000
--- a/doc/overview.rst
+++ /dev/null
@@ -1,9 +0,0 @@
-=============================
-Nim Documentation Overview
-=============================
-
-:Author: Andreas Rumpf
-:Version: |nimversion|
-
-.. include:: docs.rst
-
diff --git a/doc/packaging.md b/doc/packaging.md
new file mode 100644
index 000000000..b742bef28
--- /dev/null
+++ b/doc/packaging.md
@@ -0,0 +1,80 @@
+=============
+Packaging Nim
+=============
+
+This page provide hints on distributing Nim using OS packages.
+
+See [distros](distros.html) for tools to detect Linux distribution at runtime.
+
+See [here](intern.html#bootstrapping-the-compiler-reproducible-builds) for how to
+compile reproducible builds.
+
+Supported architectures
+-----------------------
+
+Nim runs on a wide variety of platforms. Support on amd64 and i386 is tested regularly, while less popular platforms are tested by the community.
+
+- amd64
+- arm64 (aka aarch64)
+- armel
+- armhf
+- i386
+- m68k
+- mips64el
+- mipsel
+- powerpc
+- ppc64
+- ppc64el (aka ppc64le)
+- riscv64
+
+The following platforms are seldomly tested:
+
+- alpha
+- hppa
+- ia64
+- mips
+- s390x
+- sparc64
+
+Packaging for Linux
+-------------------
+
+See https://github.com/nim-lang/Nim/labels/Installation for installation-related bugs.
+
+Build Nim from the released tarball at https://nim-lang.org/install_unix.html
+It is different from the GitHub sources as it contains Nimble, C sources & other tools.
+
+The Debian package ships bash and ksh completion and manpages that can be reused.
+
+Hints on the build process:
+
+  ```cmd
+  # build from C sources and then using koch
+  make -j   # supports parallel build
+  # alternatively: ./build.sh --os $os_type --cpu $cpu_arch
+  ./bin/nim c -d:release koch
+  ./koch boot -d:release
+
+  # optionally generate docs into doc/html
+  ./koch docs
+
+  ./koch tools
+
+  # extract files to be really installed
+  ./install.sh <tempdir>
+
+  # also include the tools
+  for fn in nimble nimsuggest nimgrep; do cp ./bin/$fn <tempdir>/nim/bin/; done
+  ```
+
+What to install:
+
+- The expected stdlib location is `/usr/lib/nim/lib`, previously it was just `/usr/lib/nim`
+- `nimdoc.css` and `nimdoc.cls` from the `doc` folder should go into `/usr/lib/nim/doc/`
+- `tools/debug/nim-gdb.py` should go into `/usr/lib/nim/tools/`
+- `tools/dochack/dochack.js` should be installed to `/usr/lib/nim/tools/dochack/`
+- Global configuration files under `/etc/nim`
+- Optionally: manpages, documentation, shell completion
+- When installing documentation, .idx files are not required
+- The "compiler" directory contains compiler sources and should not be part of the compiler binary package
+
diff --git a/doc/pegdocs.txt b/doc/pegdocs.txt
index 4c557aed8..0a8fd8187 100644
--- a/doc/pegdocs.txt
+++ b/doc/pegdocs.txt
@@ -13,12 +13,12 @@ notation           meaning
 ``A / ... / Z``    Ordered choice: Apply expressions `A`, ..., `Z`, in this
                    order, to the text ahead, until one of them succeeds and
                    possibly consumes some text. Indicate success if one of
-                   expressions succeeded. Otherwise do not consume any text
+                   expressions succeeded. Otherwise, do not consume any text
                    and indicate failure.
 ``A ... Z``        Sequence: Apply expressions `A`, ..., `Z`, in this order,
                    to consume consecutive portions of the text ahead, as long
                    as they succeed. Indicate success if all succeeded.
-                   Otherwise do not consume any text and indicate failure.
+                   Otherwise, do not consume any text and indicate failure.
                    The sequence's precedence is higher than that of ordered
                    choice: ``A B / C`` means ``(A B) / Z`` and
                    not ``A (B / Z)``.
@@ -27,7 +27,10 @@ notation           meaning
 ``{E}``            Capture: Apply expression `E` and store the substring
                    that matched `E` into a *capture* that can be accessed
                    after the matching process.
-``$i``             Back reference to the ``i``th capture. ``i`` counts from 1.
+``{}``             Empty capture: Delete the last capture. No character
+                   is consumed.
+``$i``             Back reference to the ``i``th capture. ``i`` counts forwards
+                   from 1 or backwards (last capture to first) from ^1.
 ``$``              Anchor: Matches at the end of the input. No character
                    is consumed. Same as ``!.``.
 ``^``              Anchor: Matches at the start of the input. No character
@@ -41,20 +44,20 @@ notation           meaning
 ``E+``             One or more: Apply expression `E` repeatedly to match
                    the text ahead, as long as it succeeds. Consume the matched
                    text (if any) and indicate success if there was at least
-                   one match. Otherwise indicate failure.
+                   one match. Otherwise, indicate failure.
 ``E*``             Zero or more: Apply expression `E` repeatedly to match
                    the text ahead, as long as it succeeds. Consume the matched
                    text (if any). Always indicate success.
 ``E?``             Zero or one: If expression `E` matches the text ahead,
                    consume it. Always indicate success.
 ``[s]``            Character class: If the character ahead appears in the
-                   string `s`, consume it and indicate success. Otherwise
+                   string `s`, consume it and indicate success. Otherwise,
                    indicate failure.
 ``[a-b]``          Character range: If the character ahead is one from the
                    range `a` through `b`, consume it and indicate success.
-                   Otherwise indicate failure.
+                   Otherwise, indicate failure.
 ``'s'``            String: If the text ahead is the string `s`, consume it
-                   and indicate success. Otherwise indicate failure.
+                   and indicate success. Otherwise, indicate failure.
 ``i's'``           String match ignoring case.
 ``y's'``           String match ignoring style.
 ``v's'``           Verbatim string match: Use this to override a global
@@ -63,15 +66,15 @@ notation           meaning
 ``y$j``            String match ignoring style for back reference.
 ``v$j``            Verbatim string match for back reference.
 ``.``              Any character: If there is a character ahead, consume it
-                   and indicate success. Otherwise (that is, at the end of
+                   and indicate success. Otherwise, (that is, at the end of
                    input) indicate failure.
-``_``              Any Unicode character: If there is an UTF-8 character
-                   ahead, consume it and indicate success. Otherwise indicate
+``_``              Any Unicode character: If there is a UTF-8 character
+                   ahead, consume it and indicate success. Otherwise, indicate
                    failure.
 ``@E``             Search: Shorthand for ``(!E .)* E``. (Search loop for the
                    pattern `E`.)
 ``{@} E``          Captured Search: Shorthand for ``{(!E .)*} E``. (Search
-                   loop for the pattern `E`.) Everything until and exluding
+                   loop for the pattern `E`.) Everything until and excluding
                    `E` is captured.
 ``@@ E``           Same as ``{@} E``.
 ``A <- E``         Rule: Bind the expression `E` to the *nonterminal symbol*
@@ -79,7 +82,7 @@ notation           meaning
                    matching engine.**
 ``\identifier``    Built-in macro for a longer expression.
 ``\ddd``           Character with decimal code *ddd*.
-``\"``, etc        Literal ``"``, etc.
+``\"``, etc.       Literal ``"``, etc.
 ===============    ============================================================
 
 
@@ -128,51 +131,53 @@ notation           meaning
 Supported PEG grammar
 ---------------------
 
-The PEG parser implements this grammar (written in PEG syntax)::
+The PEG parser implements this grammar (written in PEG syntax):
 
-  # Example grammar of PEG in PEG syntax.
-  # Comments start with '#'.
-  # First symbol is the start symbol.
+    # Example grammar of PEG in PEG syntax.
+    # Comments start with '#'.
+    # First symbol is the start symbol.
 
-  grammar <- rule* / expr
+    grammar <- rule* / expr
 
-  identifier <- [A-Za-z][A-Za-z0-9_]*
-  charsetchar <- "\\" . / [^\]]
-  charset <- "[" "^"? (charsetchar ("-" charsetchar)?)+ "]"
-  stringlit <- identifier? ("\"" ("\\" . / [^"])* "\"" /
-                            "'" ("\\" . / [^'])* "'")
-  builtin <- "\\" identifier / [^\13\10]
+    identifier <- [A-Za-z][A-Za-z0-9_]*
+    charsetchar <- "\\" . / [^\]]
+    charset <- "[" "^"? (charsetchar ("-" charsetchar)?)+ "]"
+    stringlit <- identifier? ("\"" ("\\" . / [^"])* "\"" /
+                              "'" ("\\" . / [^'])* "'")
+    builtin <- "\\" identifier / [^\13\10]
 
-  comment <- '#' @ \n
-  ig <- (\s / comment)* # things to ignore
+    comment <- '#' @ \n
+    ig <- (\s / comment)* # things to ignore
 
-  rule <- identifier \s* "<-" expr ig
-  identNoArrow <- identifier !(\s* "<-")
-  prefixOpr <- ig '&' / ig '!' / ig '@' / ig '{@}' / ig '@@'
-  literal <- ig identifier? '$' [0-9]+ / '$' / '^' /
-             ig identNoArrow /
-             ig charset /
-             ig stringlit /
-             ig builtin /
-             ig '.' /
-             ig '_' /
-             (ig "(" expr ig ")")
-  postfixOpr <- ig '?' / ig '*' / ig '+'
-  primary <- prefixOpr* (literal postfixOpr*)
+    rule <- identifier \s* "<-" expr ig
+    identNoArrow <- identifier !(\s* "<-")
+    prefixOpr <- ig '&' / ig '!' / ig '@' / ig '{@}' / ig '@@'
+    literal <- ig identifier? '$' '^'? [0-9]+ / '$' / '^' /
+               ig identNoArrow /
+               ig charset /
+               ig stringlit /
+               ig builtin /
+               ig '.' /
+               ig '_' /
+               (ig "(" expr ig ")") /
+               (ig "{" expr? ig "}")
+    postfixOpr <- ig '?' / ig '*' / ig '+'
+    primary <- prefixOpr* (literal postfixOpr*)
 
-  # Concatenation has higher priority than choice:
-  # ``a b / c`` means ``(a b) / c``
+    # Concatenation has higher priority than choice:
+    # ``a b / c`` means ``(a b) / c``
 
-  seqExpr <- primary+
-  expr <- seqExpr (ig "/" expr)*
+    seqExpr <- primary+
+    expr <- seqExpr (ig "/" expr)*
 
 
 **Note**: As a special syntactic extension if the whole PEG is only a single
 expression, identifiers are not interpreted as non-terminals, but are
 interpreted as verbatim string:
 
-.. code-block:: nim
+  ```nim
   abc =~ peg"abc" # is true
+  ```
 
 So it is not necessary to write ``peg" 'abc' "`` in the above example.
 
@@ -182,22 +187,25 @@ Examples
 
 Check if `s` matches Nim's "while" keyword:
 
-.. code-block:: nim
+  ```nim
   s =~ peg" y'while'"
+  ```
 
 Exchange (key, val)-pairs:
 
-.. code-block:: nim
+  ```nim
   "key: val; key2: val2".replacef(peg"{\ident} \s* ':' \s* {\ident}", "$2: $1")
+  ```
 
 Determine the ``#include``'ed files of a C file:
 
-.. code-block:: nim
+  ```nim
   for line in lines("myfile.c"):
     if line =~ peg"""s <- ws '#include' ws '"' {[^"]+} '"' ws
                      comment <- '/*' @ '*/' / '//' .*
                      ws <- (comment / \s+)* """:
       echo matches[0]
+  ```
 
 PEG vs regular expression
 -------------------------
diff --git a/doc/readme.txt b/doc/readme.txt
index 6f4cece87..1157752b9 100644
--- a/doc/readme.txt
+++ b/doc/readme.txt
@@ -1,7 +1,7 @@
 ============================
-Nim's documenation system
+Nim's documentation system
 ============================
 
 This folder contains Nim's documentation. The documentation
-is written in a format called *reStructuredText*, a markup language that reads
+is written in a format called *Markdown*, a markup language that reads
 like ASCII and can be converted to HTML automatically!
diff --git a/doc/refc.md b/doc/refc.md
new file mode 100644
index 000000000..4023748e6
--- /dev/null
+++ b/doc/refc.md
@@ -0,0 +1,156 @@
+Tweaking the refc GC
+====================
+
+Cycle collector
+---------------
+
+The cycle collector can be en-/disabled independently from the other parts of
+the garbage collector with `GC_enableMarkAndSweep` and `GC_disableMarkAndSweep`.
+
+
+Soft real-time support
+----------------------
+
+To enable real-time support, the symbol `useRealtimeGC`:idx: needs to be
+defined via `--define:useRealtimeGC`:option: (you can put this into your config
+file as well).
+With this switch the garbage collector supports the following operations:
+
+  ```nim
+  proc GC_setMaxPause*(maxPauseInUs: int)
+  proc GC_step*(us: int, strongAdvice = false, stackSize = -1)
+  ```
+
+The unit of the parameters `maxPauseInUs` and `us` is microseconds.
+
+These two procs are the two modus operandi of the real-time garbage collector:
+
+(1) GC_SetMaxPause Mode
+
+    You can call `GC_SetMaxPause` at program startup and then each triggered
+    garbage collector run tries to not take longer than `maxPause` time. However, it is
+    possible (and common) that the work is nevertheless not evenly distributed
+    as each call to `new` can trigger the garbage collector and thus take  `maxPause`
+    time.
+
+(2) GC_step Mode
+
+    This allows the garbage collector to perform some work for up to `us` time.
+    This is useful to call in the main loop to ensure the garbage collector can do its work.
+    To bind all garbage collector activity to a `GC_step` call,
+    deactivate the garbage collector with `GC_disable` at program startup.
+    If `strongAdvice` is set to `true`,
+    then the garbage collector will be forced to perform the collection cycle.
+    Otherwise, the garbage collector may decide not to do anything,
+    if there is not much garbage to collect.
+    You may also specify the current stack size via `stackSize` parameter.
+    It can improve performance when you know that there are no unique Nim references
+    below a certain point on the stack. Make sure the size you specify is greater
+    than the potential worst-case size.
+
+    It can improve performance when you know that there are no unique Nim
+    references below a certain point on the stack. Make sure the size you specify
+    is greater than the potential worst-case size.
+
+These procs provide a "best effort" real-time guarantee; in particular the
+cycle collector is not aware of deadlines. Deactivate it to get more
+predictable real-time behaviour. Tests show that a 1ms max pause
+time will be met in almost all cases on modern CPUs (with the cycle collector
+disabled).
+
+
+Time measurement with garbage collectors
+----------------------------------------
+
+The garbage collectors' way of measuring time uses
+(see ``lib/system/timers.nim`` for the implementation):
+
+1) `QueryPerformanceCounter` and `QueryPerformanceFrequency` on Windows.
+2) `mach_absolute_time` on Mac OS X.
+3) `gettimeofday` on Posix systems.
+
+As such it supports a resolution of nanoseconds internally; however, the API
+uses microseconds for convenience.
+
+Define the symbol `reportMissedDeadlines` to make the
+garbage collector output whenever it missed a deadline.
+The reporting will be enhanced and supported by the API in later versions of the collector.
+
+
+Tweaking the garbage collector
+------------------------------
+
+The collector checks whether there is still time left for its work after
+every `workPackage`'th iteration. This is currently set to 100 which means
+that up to 100 objects are traversed and freed before it checks again. Thus
+`workPackage` affects the timing granularity and may need to be tweaked in
+highly specialized environments or for older hardware.
+
+
+Thread coordination
+-------------------
+
+When the `NimMain()` function is called Nim initializes the garbage
+collector to the current thread, which is usually the main thread of your
+application. If your C code later spawns a different thread and calls Nim
+code, the garbage collector will fail to work properly and you will crash.
+
+As long as you don't use the threadvar emulation Nim uses native thread
+variables, of which you get a fresh version whenever you create a thread. You
+can then attach a GC to this thread via
+
+  ```nim
+  system.setupForeignThreadGc()
+  ```
+
+It is **not** safe to disable the garbage collector and enable it after the
+call from your background thread even if the code you are calling is short
+lived.
+
+Before the thread exits, you should tear down the thread's GC to prevent memory
+leaks by calling
+
+  ```nim
+  system.tearDownForeignThreadGc()
+  ```
+
+
+Keeping track of memory
+=======================
+
+If you need to pass around memory allocated by Nim to C, you can use the
+procs `GC_ref` and `GC_unref` to mark objects as referenced to avoid them
+being freed by the garbage collector.
+Other useful procs from [system](system.html) you can use to keep track of memory are:
+
+* `getTotalMem()` Returns the amount of total memory managed by the garbage collector.
+* `getOccupiedMem()` Bytes reserved by the garbage collector and used by objects.
+* `getFreeMem()` Bytes reserved by the garbage collector and not in use.
+* `GC_getStatistics()` Garbage collector statistics as a human-readable string.
+
+These numbers are usually only for the running thread, not for the whole heap,
+with the exception of `--mm:boehm`:option: and `--mm:go`:option:.
+
+In addition to `GC_ref` and `GC_unref` you can avoid the garbage collector by manually
+allocating memory with procs like `alloc`, `alloc0`, `allocShared`, `allocShared0` or `allocCStringArray`.
+The garbage collector won't try to free them, you need to call their respective *dealloc* pairs
+(`dealloc`, `deallocShared`, `deallocCStringArray`, etc)
+when you are done with them or they will leak.
+
+
+
+Heap dump
+=========
+
+The heap dump feature is still in its infancy, but it already proved
+useful for us, so it might be useful for you. To get a heap dump, compile
+with `-d:nimTypeNames`:option: and call `dumpNumberOfInstances`
+at a strategic place in your program.
+This produces a list of the used types in your program and for every type
+the total amount of object instances for this type as well as the total
+amount of bytes these instances take up.
+
+The numbers count the number of objects in all garbage collector heaps, they refer to
+all running threads, not only to the current thread. (The current thread
+would be the thread that calls `dumpNumberOfInstances`.) This might
+change in later versions.
diff --git a/doc/regexprs.txt b/doc/regexprs.txt
index 5c6d37e89..fa7f9d24a 100644
--- a/doc/regexprs.txt
+++ b/doc/regexprs.txt
@@ -47,16 +47,18 @@ full documentation of Perl's regular expressions.
 Because the backslash ``\`` is a meta character both in the Nim
 programming language and in regular expressions, it is strongly
 recommended that one uses the *raw* strings of Nim, so that
-backslashes are interpreted by the regular expression engine::
+backslashes are interpreted by the regular expression engine:
 
+```nim
   r"\S"  # matches any character that is not whitespace
+```
 
 A regular expression is a pattern that is matched against a subject string
 from left to right. Most characters stand for themselves in a pattern, and
 match the corresponding characters in the subject. As a trivial example,
-the pattern::
+the pattern:
 
-  The quick brown fox
+    The quick brown fox
 
 matches a portion of a subject string that is identical to itself.
 The power of regular expressions comes from the ability to include
@@ -80,13 +82,13 @@ meta character     meaning
 ``|``              start of alternative branch
 ``(``              start subpattern
 ``)``              end subpattern
-``?``              extends the meaning of ``(``
-                   also 0 or 1 quantifier
-                   also quantifier minimizer
-``*``              0 or more quantifier
-``+``              1 or more quantifier
-                   also "possessive quantifier"
 ``{``              start min/max quantifier
+``?``              extends the meaning of ``(``
+                   | also 0 or 1 quantifier (equal to ``{0,1}``)
+                   | also quantifier minimizer
+``*``              0 or more quantifier (equal to ``{0,}``)
+``+``              1 or more quantifier (equal to ``{1,}``)
+                   | also "possessive quantifier"
 ==============     ============================================================
 
 
@@ -128,7 +130,7 @@ in patterns in a visible manner. There is no restriction on the appearance of
 non-printing characters, apart from the binary zero that terminates a pattern,
 but when a pattern is being prepared by text editing, it is usually easier to
 use one of the following escape sequences than the binary character it
-represents::
+represents:
 
 ==============     ============================================================
 character          meaning
@@ -146,7 +148,7 @@ character          meaning
 After ``\x``, from zero to two hexadecimal digits are read (letters can be in
 upper or lower case). In UTF-8 mode, any number of hexadecimal digits may
 appear between ``\x{`` and ``}``, but the value of the character code must be
-less than 2**31 (that is, the maximum hexadecimal value is 7FFFFFFF). If
+less than 2^31 (that is, the maximum hexadecimal value is 7FFFFFFF). If
 characters other than hexadecimal digits appear between ``\x{`` and ``}``, or
 if there is no terminating ``}``, this form of escape is not recognized.
 Instead, the initial ``\x`` will be interpreted as a basic hexadecimal escape,
@@ -224,7 +226,7 @@ current matching point is at the end of the subject string, all of them fail,
 since there is no character to match.
 
 For compatibility with Perl, ``\s`` does not match the VT character (code 11).
-This makes it different from the the POSIX "space" class. The ``\s`` characters
+This makes it different from the POSIX "space" class. The ``\s`` characters
 are HT (9), LF (10), FF (12), CR (13), and space (32).
 
 A "word" character is an underscore or any character less than 256 that is
@@ -244,7 +246,7 @@ The fourth use of backslash is for certain `simple assertions`:idx:. An
 assertion specifies a condition that has to be met at a particular point in
 a match, without consuming any characters from the subject string. The use of
 subpatterns for more complicated assertions is described below. The
-backslashed assertions are::
+backslashed assertions are:
 
 ==============     ============================================================
 assertion          meaning
diff --git a/doc/rstcommon.rst b/doc/rstcommon.rst
new file mode 100644
index 000000000..e9cc615db
--- /dev/null
+++ b/doc/rstcommon.rst
@@ -0,0 +1,51 @@
+..
+  Usage of this file:
+     Add this in the beginning of *.rst file:
+
+         .. default-role:: code
+         .. include:: rstcommon.rst
+
+     It's the current trick for brevity and compatibility with both Github and
+     rst2html.py, considering that Github cannot highlight Nim in
+     RST files anyway and it does not include files.
+     This way interpreted text is displayed with monospaced font in Github
+     and it's displayed an Nim code in both rst2html.py
+     (note ".. default-role:: Nim" above) and `nim rst2html`.
+
+     For files that are user manual and consist of stuff like cmdline
+     option description, use 'code' as a **real** default role:
+
+     .. include:: rstcommon.rst
+     .. default-role:: code
+
+.. define language roles explicitly (for compatibility with rst2html.py):
+
+.. role:: nim(code)
+   :language: nim
+
+.. default-role:: nim
+
+.. role:: c(code)
+   :language: c
+
+.. role:: cpp(code)
+   :language: cpp
+
+.. role:: yaml(code)
+   :language: yaml
+
+.. role:: python(code)
+   :language: python
+
+.. role:: java(code)
+   :language: java
+
+.. role:: csharp(code)
+   :language: csharp
+
+.. role:: cmd(code)
+
+.. role:: program(code)
+
+.. role:: option(code)
+
diff --git a/doc/sets_fragment.txt b/doc/sets_fragment.txt
index 5c341b7c8..35b1fc023 100644
--- a/doc/sets_fragment.txt
+++ b/doc/sets_fragment.txt
@@ -1,24 +1,35 @@
 The set type models the mathematical notion of a set. The set's basetype can
 only be an ordinal type of a certain size, namely:
 
-  * ``int8``-``int16``
-  * ``uint8``/``byte``-``uint16``
-  * ``char``
-  * ``enum``
+* `int8`-`int16`
+* `uint8`/`byte`-`uint16`
+* `char`
+* `enum`
+* Ordinal subrange types, i.e. `range[-10..10]`
 
-or equivalent. The reason is that sets are implemented as high
-performance bit vectors. Attempting to declare a set with a larger type will
-result in an error:
+or equivalent. When constructing a set with signed integer literals, the set's
+base type is defined to be in the range `0 .. DefaultSetElements-1` where
+`DefaultSetElements` is currently always 2^8. The maximum range length for the
+base type of a set is `MaxSetElements` which is currently always 2^16. Types
+with a bigger range length are coerced into the range `0 .. MaxSetElements-1`.
 
-.. code-block:: nim
+The reason is that sets are implemented as high performance bit vectors.
+Attempting to declare a set with a larger type will result in an error:
 
-  var s: set[int64] # Error: set is too large
+```nim
+  var s: set[int64] # Error: set is too large; use `std/sets` for ordinal types
+                    # with more than 2^16 elements
+```
 
-Sets can be constructed via the set constructor: ``{}`` is the empty set. The
+
+**Note:** Nim also offers [hash sets](sets.html) (which you need to import
+with `import std/sets`), which have no such restrictions.
+
+Sets can be constructed via the set constructor: `{}` is the empty set. The
 empty set is type compatible with any concrete set type. The constructor
 can also be used to include elements (and ranges of elements):
 
-.. code-block:: nim
+  ```nim
   type
     CharSet = set[char]
   var
@@ -26,26 +37,66 @@ can also be used to include elements (and ranges of elements):
   x = {'a'..'z', '0'..'9'} # This constructs a set that contains the
                            # letters from 'a' to 'z' and the digits
                            # from '0' to '9'
+  ```
+
+The module [`std/setutils`](setutils.html) provides a way to initialize a set from an iterable:
+
+```nim
+import std/setutils
+
+let uniqueChars = myString.toSet
+```
 
 These operations are supported by sets:
 
 ==================    ========================================================
 operation             meaning
 ==================    ========================================================
-``A + B``             union of two sets
-``A * B``             intersection of two sets
-``A - B``             difference of two sets (A without B's elements)
-``A == B``            set equality
-``A <= B``            subset relation (A is subset of B or equal to B)
-``A < B``             strong subset relation (A is a real subset of B)
-``e in A``            set membership (A contains element e)
-``e notin A``         A does not contain element e
-``contains(A, e)``    A contains element e
-``card(A)``           the cardinality of A (number of elements in A)
-``incl(A, elem)``     same as ``A = A + {elem}``
-``excl(A, elem)``     same as ``A = A - {elem}``
+`A + B`               union of two sets
+`A * B`               intersection of two sets
+`A - B`               difference of two sets (A without B's elements)
+`A == B`              set equality
+`A <= B`              subset relation (A is subset of B or equal to B)
+`A < B`               strict subset relation (A is a proper subset of B)
+`e in A`              set membership (A contains element e)
+`e notin A`           A does not contain element e
+`contains(A, e)`      A contains element e
+`card(A)`             the cardinality of A (number of elements in A)
+`incl(A, elem)`       same as `A = A + {elem}`
+`excl(A, elem)`       same as `A = A - {elem}`
 ==================    ========================================================
 
-Sets are often used to define a type for the *flags* of a procedure. This is
-a much cleaner (and type safe) solution than just defining integer
-constants that should be ``or``'ed together.
+### Bit fields
+
+Sets are often used to define a type for the *flags* of a procedure.
+This is a cleaner (and type safe) solution than defining integer
+constants that have to be `or`'ed together.
+
+Enum, sets and casting can be used together as in:
+
+  ```nim
+  type
+    MyFlag* {.size: sizeof(cint).} = enum
+      A
+      B
+      C
+      D
+    MyFlags = set[MyFlag]
+
+  proc toNum(f: MyFlags): int = cast[cint](f)
+  proc toFlags(v: int): MyFlags = cast[MyFlags](v)
+
+  assert toNum({}) == 0
+  assert toNum({A}) == 1
+  assert toNum({D}) == 8
+  assert toNum({A, C}) == 5
+  assert toFlags(0) == {}
+  assert toFlags(7) == {A, B, C}
+  ```
+
+Note how the set turns enum values into powers of 2.
+
+If using enums and sets with C, use distinct cint.
+
+For interoperability with C see also the
+[bitsize pragma](manual.html#implementation-specific-pragmas-bitsize-pragma).
diff --git a/doc/spawn.txt b/doc/spawn.txt
index 522c94464..ed25ad5fd 100644
--- a/doc/spawn.txt
+++ b/doc/spawn.txt
@@ -6,7 +6,7 @@ Nim has two flavors of parallelism:
 1) `Structured`:idx parallelism via the ``parallel`` statement.
 2) `Unstructured`:idx: parallelism via the standalone ``spawn`` statement.
 
-Both need the `threadpool <threadpool.html>`_ module to work.
+Both need the [threadpool](threadpool.html) module to work.
 
 Somewhat confusingly, ``spawn`` is also used in the ``parallel`` statement
 with slightly different semantics. ``spawn`` always takes a call expression of
@@ -25,21 +25,22 @@ Spawn statement
 A standalone ``spawn`` statement is a simple construct. It executes
 the passed expression on the thread pool and returns a `data flow variable`:idx:
 ``FlowVar[T]`` that can be read from. The reading with the ``^`` operator is
-**blocking**. However, one can use ``awaitAny`` to wait on multiple flow
+**blocking**. However, one can use ``blockUntilAny`` to wait on multiple flow
 variables at the same time:
 
-.. code-block:: nim
-  import threadpool, ...
+  ```nim
+  import std/threadpool, ...
 
   # wait until 2 out of 3 servers received the update:
   proc main =
     var responses = newSeq[FlowVarBase](3)
     for i in 0..2:
       responses[i] = spawn tellServer(Update, "key", "value")
-    var index = awaitAny(responses)
+    var index = blockUntilAny(responses)
     assert index >= 0
     responses.del(index)
-    discard awaitAny(responses)
+    discard blockUntilAny(responses)
+  ```
 
 Data flow variables ensure that no data races
 are possible. Due to technical limitations not every type ``T`` is possible in
@@ -54,9 +55,9 @@ Parallel statement
 
 Example:
 
-.. code-block:: nim
+  ```nim
   # Compute PI in an inefficient way
-  import strutils, math, threadpool
+  import std/[strutils, math, threadpool]
 
   proc term(k: float): float = 4 * math.pow(-1, k) / (2*k + 1)
 
@@ -69,6 +70,7 @@ Example:
       result += ch[k]
 
   echo formatFloat(pi(5000))
+  ```
 
 
 The parallel statement is the preferred mechanism to introduce parallelism
diff --git a/doc/subexes.txt b/doc/subexes.txt
index 54304f033..1bfd60213 100644
--- a/doc/subexes.txt
+++ b/doc/subexes.txt
@@ -14,7 +14,7 @@ Notation                meaning
 =====================   =====================================================
 ``$#``                  use first or next argument
 ``$name``               use named argument, you can wrap the named argument
-                        in curly braces (eg. ``${name}``) to separate it from
+                        in curly braces (e.g. ``${name}``) to separate it from
                         the next characters.
 ``$$``                  produces a single ``$``
 ``$1``                  use first argument
@@ -47,8 +47,7 @@ Notation                meaning
 Examples
 ========
 
-.. code-block:: nim
-
+  ```nim
   subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)"
 
   subex"$1 $[files|file|files]{1} copied" % ["1"] == "1 file copied"
@@ -57,5 +56,5 @@ Examples
 
   subex("type\n  TEnum = enum\n    $', '40c'\n    '{..}") % [
     "fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"]
-
+  ```
 
diff --git a/doc/testament.md b/doc/testament.md
new file mode 100644
index 000000000..0ff3591ac
--- /dev/null
+++ b/doc/testament.md
@@ -0,0 +1,390 @@
+===========
+ Testament
+===========
+
+.. default-role:: code
+.. include:: rstcommon.rst
+.. contents::
+
+Testament is an advanced automatic unittests runner for Nim tests, is used for the development of Nim itself,
+offers process isolation for your tests, it can generate statistics about test cases,
+supports multiple targets (C, C++, ObjectiveC, JavaScript, etc.),
+simulated [Dry-Runs](https://en.wikipedia.org/wiki/Dry_run_(testing)),
+has logging, can generate HTML reports, skip tests from a file, and more,
+so can be useful to run your tests, even the most complex ones.
+
+
+Test files location
+===================
+
+By default, Testament looks for test files on ``"./tests/category/*.nim"``.
+You can overwrite this pattern glob using `pattern <glob>`:option:.
+The default working directory path can be changed using
+`--directory:"folder/subfolder/"`:option:.
+
+Testament uses the `nim`:cmd: compiler on `PATH`.
+You can change that using `--nim:"folder/subfolder/nim"`:option:.
+Running JavaScript tests with `--targets:"js"`:option: requires
+a working NodeJS on `PATH`.
+
+Commands
+========
+
+p|pat|pattern <glob>        run all the tests matching the given pattern
+all                         run all tests inside of category folders
+c|cat|category <category>   run all the tests of a certain category
+r|run <test>                run single test file
+html                        generate testresults.html from the database
+
+
+Options
+=======
+
+--print                   print results to the console
+--verbose                 print commands (compiling and running tests)
+--simulate                see what tests would be run but don't run them (for debugging)
+--failing                 only show failing/ignored tests
+--targets:"c cpp js objc" run tests for specified targets (default: c)
+--nim:path                use a particular nim executable (default: $PATH/nim)
+--directory:dir           Change to directory dir before reading the tests or doing anything else.
+--colors:on|off           Turn messages coloring on|off.
+--backendLogging:on|off   Disable or enable backend logging. By default turned on.
+--megatest:on|off         Enable or disable megatest. Default is on.
+--valgrind:on|off         Enable or disable valgrind support. Default is on.
+--skipFrom:file           Read tests to skip from `file` - one test per line, # comments ignored
+
+
+Running a single test
+=====================
+
+This is a minimal example to understand the basics,
+not very useful for production, but easy to understand:
+
+  ```console
+  $ mkdir -p tests/category
+  $ echo "assert 42 == 42" > tests/category/test0.nim
+  $ testament run tests/category/test0.nim
+  PASS: tests/category/test0.nim c                           ( 0.2 sec)
+  $ testament r tests/category/test0
+  PASS: tests/category/test0.nim C                           ( 0.2 sec)
+  ```
+
+
+Running all tests from a directory
+==================================
+
+ This will run all tests in the top level tests/ directory. NOTE: these
+ tests are skipped by `testament all`.
+ 
+  ```console
+  $ testament pattern "tests/*.nim"
+  ```
+
+To search for tests deeper in a directory, use
+
+  ```console
+  $ testament pattern "tests/**/*.nim"    # one level deeper
+  $ testament pattern "tests/**/**/*.nim" # two levels deeper
+  ```
+
+HTML Reports
+============
+
+Generate HTML Reports ``testresults.html`` from unittests,
+you have to run at least 1 test *before* generating a report:
+
+  ```console
+  $ testament html
+  ```
+
+
+Writing Unit tests
+==================
+
+Example "template" **to edit** and write a Testament unittest:
+
+  ```nim
+  discard """
+
+    # What actions to expect completion on.
+    # Options:
+    #   "compile": expect successful compilation
+    #   "run": expect successful compilation and execution
+    #   "reject": expect failed compilation. The "reject" action can catch
+    #             {.error.} pragmas but not {.fatal.} pragmas because
+    #             {.error.} calls are expected to originate from the test-file, 
+    #             and can explicitly be specified using the "file", "line" and
+    #             "column" options.
+    #             {.fatal.} pragmas guarantee that compilation will be aborted.
+    action: "run"
+    
+    # For testing failed compilations you can specify the expected origin of the 
+    # compilation error.
+    # With the "file", "line" and "column" options you can define the file, 
+    # line and column that a compilation-error should have originated from.
+    # Use only with action: "reject" as it expects a failed compilation.
+    # Requires errormsg or msg to be defined.
+    # file: ""
+    # line: ""
+    # column: ""
+
+    # The exit code that the test is expected to return. Typically, the default
+    # value of 0 is fine. Note that if the test will be run by valgrind, then
+    # the test will exit with either a code of 0 on success or 1 on failure.
+    exitcode: 0
+
+    # Provide an `output` string to assert that the test prints to standard out
+    # exactly the expected string. Provide an `outputsub` string to assert that
+    # the string given here is a substring of the standard out output of the
+    # test (the output includes both the compiler and test execution output).
+    output: ""
+    outputsub: ""
+
+    # Whether to sort the compiler output lines before comparing them to the 
+    # expected output.
+    sortoutput: true
+
+    # Provide a `nimout` string to assert that the compiler during compilation
+    # prints the defined lines to the standard out.
+    # The lines must match in order, but there may be more lines that appear 
+    # before, after, or in between them. 
+    nimout: '''
+  a very long,
+  multi-line
+  string'''
+
+    # This is the Standard Input the test should take, if any.
+    input: ""
+
+    # Error message the test should print, if any.
+    errormsg: ""
+
+    # Can be run in batch mode, or not.
+    batchable: true
+
+    # Can be run Joined with other tests to run all together, or not.
+    joinable: true
+
+    # On Linux 64-bit machines, whether to use Valgrind to check for bad memory
+    # accesses or memory leaks. On other architectures, the test will be run
+    # as-is, without Valgrind.
+    # Options:
+    #   true: run the test with Valgrind
+    #   false: run the without Valgrind
+    #   "leaks": run the test with Valgrind, but do not check for memory leaks
+    valgrind: false   # Can use Valgrind to check for memory leaks, or not (Linux 64Bit only).
+
+    # Checks that the specified piece of C-code is within the generated C-code.
+    ccodecheck: "'Assert error message'"
+
+    # Command the test should use to run. If left out or an empty string is
+    # provided, the command is taken to be:
+    # "nim $target --hints:on -d:testing --nimblePath:build/deps/pkgs $options $file"
+    # Subject to variable interpolation.
+    cmd: "nim c -r $file"
+
+    # Maximum generated temporary intermediate code file size for the test.
+    maxcodesize: 666
+
+    # Timeout seconds to run the test. Fractional values are supported.
+    timeout: 1.5
+
+    # Targets to run the test into (c, cpp, objc, js). Defaults to c.
+    targets: "c js"
+
+    # flags with which to run the test, delimited by `;`
+    matrix: "; -d:release; -d:caseFoo -d:release"
+
+    # Conditions that will skip this test. Use of multiple "disabled" clauses
+    # is permitted.
+    disabled: "bsd"   # Can disable OSes...
+    disabled: "win"
+    disabled: "32bit" # ...or architectures
+    disabled: "i386"
+    disabled: "azure" # ...or pipeline runners
+    disabled: true    # ...or can disable the test entirely
+
+  """
+  assert true
+  assert 42 == 42, "Assert error message"
+  ```
+
+
+* As you can see the "Spec" is just a `discard """ """`.
+* Spec has sane defaults, so you don't need to provide them all, any simple assert will work just fine.
+* This is not the full spec of Testament, check [the Testament Spec on GitHub,
+  see parseSpec()](https://github.com/nim-lang/Nim/blob/devel/testament/specs.nim#L317).
+* Nim itself uses Testament, so [there are plenty of test examples](
+  https://github.com/nim-lang/Nim/tree/devel/tests).
+* Has some built-in CI compatibility, like Azure Pipelines, etc.
+
+
+Inline hints, warnings and errors (notes)
+-----------------------------------------
+
+Testing the line, column, kind and message of hints, warnings and errors can
+be written inline like so:
+  ```nim
+  {.warning: "warning!!"} #[tt.Warning
+           ^ warning!! [User] ]#
+  ```
+
+The opening `#[tt.` marks the message line.
+The `^` marks the message column.
+
+Inline messages can be combined with `nimout` when `nimoutFull` is false (default).
+This allows testing for expected messages from other modules:
+
+  ```nim
+  discard """
+    nimout: "config.nims(1, 1) Hint: some hint message [User]"
+  """
+  {.warning: "warning!!"} #[tt.Warning
+           ^ warning!! [User] ]#
+  ```
+
+Multiple messages for a line can be checked by delimiting messages with ';':
+
+  ```nim
+  discard """
+    matrix: "--errorMax:0 --styleCheck:error"
+  """
+
+  proc generic_proc*[T](a_a: int) = #[tt.Error
+       ^ 'generic_proc' should be: 'genericProc'; tt.Error
+                        ^ 'a_a' should be: 'aA' ]#
+    discard
+  ```
+
+Use `--errorMax:0` in `matrix`, or `cmd: "nim check $file"` when testing
+for multiple 'Error' messages.
+
+Output message variable interpolation
+-------------------------------------
+
+`errormsg`, `nimout`, and inline messages are subject to these variable interpolations:
+
+* `${/}` - platform's directory separator
+* `$file` - the filename (without directory) of the test
+
+All other `$` characters need escaped as `$$`.
+
+Cmd variable interpolation
+--------------------------
+
+The `cmd` option is subject to these variable interpolations:
+
+* `$target` - the compilation target, e.g. `c`.
+* `$options` - the options for the compiler.
+* `$file` - the file path of the test.
+* `$filedir` - the directory of the test file.
+
+.. code-block:: nim
+
+  discard """
+    cmd: "nim $target --nimblePath:./nimbleDir/simplePkgs $options $file"
+  """
+
+All other `$` characters need escaped as `$$`.
+
+
+Unit test Examples
+==================
+
+Expected to fail:
+
+  ```nim
+  discard """
+    errormsg: "undeclared identifier: 'not_defined'"
+  """
+  assert not_defined == "not_defined", "not_defined is not defined"
+  ```
+
+Expected to fail with error thrown from another file:
+```nim
+# test.nim
+discard """
+  action: "reject"
+  errorMsg: "I break"
+  file: "breakPragma.nim"
+"""
+import ./breakPragma
+
+proc x() {.justDo.} = discard
+
+# breakPragma.nim
+import std/macros
+
+macro justDo*(procDef: typed): untyped =
+  error("I break")
+  return procDef
+```
+
+Expecting generated C to contain a given piece of code:
+
+  ```nim
+  discard """
+    # Checks that the string "Assert error message" is in the generated 
+    # C code.
+    ccodecheck: "'Assert error message'"
+  """
+  assert 42 == 42, "Assert error message"
+  ```
+
+
+Non-Zero exit code:
+
+  ```nim
+  discard """
+    exitcode: 1
+  """
+  quit "Non-Zero exit code", 1
+  ```
+
+Standard output checking:
+
+  ```nim
+  discard """
+
+    output: '''
+  0
+  1
+  2
+  3
+  4
+  5
+  '''
+
+  """
+  for i in 0..5: echo i
+  ```
+
+JavaScript tests:
+
+  ```nim
+  discard """
+    targets: "js"
+  """
+  when defined(js):
+    import std/jsconsole
+    console.log("My Frontend Project")
+  ```
+
+Compile-time tests:
+
+  ```nim
+  discard """
+    action: "compile"
+  """
+  static: assert 9 == 9, "Compile time assert"
+  ```
+
+Tests without Spec:
+
+  ```nim
+  assert 1 == 1
+  ```
+
+
+See also:
+* [Unittest](unittest.html)
diff --git a/doc/tools.md b/doc/tools.md
new file mode 100644
index 000000000..baf7ce386
--- /dev/null
+++ b/doc/tools.md
@@ -0,0 +1,45 @@
+========================
+Tools available with Nim
+========================
+
+.. default-role:: code
+.. include:: rstcommon.rst
+
+The standard distribution ships with the following tools:
+
+- | [atlas](atlas.html)
+  | `atlas`:cmd: is a simple package cloner tool. It manages an isolated workspace that
+    contains projects and dependencies.
+
+- | [Hot code reloading](hcr.html)
+  | The "Hot code reloading" feature is built into the compiler but has its own
+    document explaining how it works.
+
+- | [Documentation generator](docgen.html)
+  | The builtin document generator `nim doc`:cmd: generates HTML documentation
+    from ``.nim`` source files.
+
+- | [Nimsuggest for IDE support](nimsuggest.html)
+  | Through the `nimsuggest`:cmd: tool, any IDE can query a ``.nim`` source file
+    and obtain useful information like the definition of symbols or suggestions for
+    completion.
+
+- | [C2nim](https://github.com/nim-lang/c2nim/blob/master/doc/c2nim.rst)
+  | C to Nim source converter. Translates C header files to Nim.
+
+- | [niminst](niminst.html)
+  | niminst is a tool to generate an installer for a Nim program.
+
+- | [nimgrep](nimgrep.html)
+  | Nim search and replace utility.
+
+- | nimpretty
+  | `nimpretty`:cmd: is a Nim source code beautifier,
+    to format code according to the official style guide.
+
+- | [testament](testament.html)
+  | `testament`:cmd: is an advanced automatic *unittests runner* for Nim tests,
+    is used for the development of Nim itself, offers process isolation for your tests,
+    it can generate statistics about test cases, supports multiple targets (C, JS, etc),
+    has logging, can generate HTML reports, skip tests from a file, and more,
+    so can be useful to run your tests, even the most complex ones.
diff --git a/doc/tools.txt b/doc/tools.txt
deleted file mode 100644
index 070deb806..000000000
--- a/doc/tools.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-========================
-Tools available with Nim
-========================
-
-The standard distribution ships with the following tools:
-
-- | `Documentation generator <docgen.html>`_
-  | The builtin document generator ``nim doc2`` generates HTML documentation
-    from ``.nim`` source files.
-
-- | `Nimsuggest for IDE support <nimsuggest.html>`_
-  | Through the ``nimsuggest`` tool, any IDE can query a ``.nim`` source file
-    and obtain useful information like definition of symbols or suggestions for
-    completion.
-
-- | `Nim Installation Generator <niminst.html>`_
-  | How to generate a nice installer for your Nim program.
-
-- | `C2nim <c2nim.html>`_
-  | C to Nim source converter. Translates C header files to Nim.
-
-- | `nimgrep <nimgrep.html>`_
-  | Nim search and replace utility.
-
-- | `endb <endb.html>`_
-  | Nim's slow platform independent embedded debugger.
-
-- | `estp <estp.html>`_
-  | Nim's slow platform independent embedded stack trace profiler.
diff --git a/doc/tut1.md b/doc/tut1.md
new file mode 100644
index 000000000..2e83effa3
--- /dev/null
+++ b/doc/tut1.md
@@ -0,0 +1,1952 @@
+=====================
+Nim Tutorial (Part I)
+=====================
+
+:Author: Andreas Rumpf
+:Version: |nimversion|
+
+.. default-role:: code
+.. include:: rstcommon.rst
+.. contents::
+
+Introduction
+============
+
+>  "Der Mensch ist doch ein Augentier -- Schöne Dinge wünsch' ich mir."
+
+
+This document is a tutorial for the programming language *Nim*.
+
+This tutorial assumes that you are familiar with basic programming concepts
+like variables, types, or statements.
+
+Here are several other resources for learning Nim:
+
+* [Nim Basics tutorial](https://narimiran.github.io/nim-basics/) - a gentle 
+  introduction of the concepts mentioned above
+* [Learn Nim in 5 minutes](https://learnxinyminutes.com/docs/nim/) - quick,
+  five-minute introduction to Nim
+* [The Nim manual](manual.html) - many more examples of the advanced language features
+
+All code examples in this tutorial, as well as the ones found in the rest of
+Nim's documentation, follow the [Nim style guide](nep1.html).
+
+
+The first program
+=================
+
+We start the tour with a modified "hello world" program:
+
+  ```Nim  test = "nim c $1"
+  # This is a comment
+  echo "What's your name? "
+  var name: string = readLine(stdin)
+  echo "Hi, ", name, "!"
+  ```
+
+
+Save this code to the file "greetings.nim". Now compile and run it:
+  ```cmd
+  nim compile --run greetings.nim
+  ```
+
+With the ``--run`` [switch](nimc.html#compiler-usage-commandminusline-switches) Nim
+executes the file automatically after compilation. You can give your program
+command-line arguments by appending them after the filename:
+  ```cmd
+  nim compile --run greetings.nim arg1 arg2
+  ```
+
+Commonly used commands and switches have abbreviations, so you can also use:
+  ```cmd
+  nim c -r greetings.nim
+  ```
+
+This is a **debug version**.
+To compile a release version use:
+  ```cmd
+  nim c -d:release greetings.nim
+  ```
+
+By default, the Nim compiler generates a large number of runtime checks
+aiming for your debugging pleasure. With ``-d:release`` some checks are
+[turned off and optimizations are turned on](
+nimc.html#compiler-usage-compileminustime-symbols).
+
+For benchmarking or production code, use the ``-d:release`` switch.
+For comparing the performance with unsafe languages like C, use the ``-d:danger`` switch
+in order to get meaningful, comparable results. Otherwise, Nim might be handicapped
+by checks that are **not even available** for C.
+
+Though it should be pretty obvious what the program does, I will explain the
+syntax: statements which are not indented are executed when the program
+starts. Indentation is Nim's way of grouping statements. Indentation is
+done with spaces only, tabulators are not allowed.
+
+String literals are enclosed in double-quotes. The `var` statement declares
+a new variable named `name` of type `string` with the value that is
+returned by the [readLine](syncio.html#readLine,File) procedure. Since the
+compiler knows that [readLine](syncio.html#readLine,File) returns a string,
+you can leave out the type in the declaration (this is called `local type
+inference`:idx:). So this will work too:
+
+  ```Nim  test = "nim c $1"
+  var name = readLine(stdin)
+  ```
+
+Note that this is basically the only form of type inference that exists in
+Nim: it is a good compromise between brevity and readability.
+
+The "hello world" program contains several identifiers that are already known
+to the compiler: `echo`, [readLine](syncio.html#readLine,File), etc.
+These built-ins are declared in the [system](system.html) module which is implicitly
+imported by any other module.
+
+
+Lexical elements
+================
+
+Let us look at Nim's lexical elements in more detail: like other
+programming languages Nim consists of (string) literals, identifiers,
+keywords, comments, operators, and other punctuation marks.
+
+
+String and character literals
+-----------------------------
+
+String literals are enclosed in double-quotes; character literals in single
+quotes. Special characters are escaped with ``\``: ``\n`` means newline, ``\t``
+means tabulator, etc. There are also *raw* string literals:
+
+  ```Nim
+  r"C:\program files\nim"
+  ```
+
+In raw literals, the backslash is not an escape character.
+
+The third and last way to write string literals is *long-string literals*.
+They are written with three quotes: `""" ... """`; they can span over
+multiple lines and the ``\`` is not an escape character either. They are very
+useful for embedding HTML code templates for example.
+
+
+Comments
+--------
+
+Comments start anywhere outside a string or character literal with the
+hash character `#`. Documentation comments start with `##`:
+
+  ```nim  test = "nim c $1"
+  # A comment.
+
+  var myVariable: int ## a documentation comment
+  ```
+
+
+Documentation comments are tokens; they are only allowed at certain places in
+the input file as they belong to the syntax tree! This feature enables simpler
+documentation generators.
+
+Multiline comments are started with `#[` and terminated with `]#`.  Multiline
+comments can also be nested.
+
+  ```nim  test = "nim c $1"
+  #[
+  You can have any Nim code text commented
+  out inside this with no indentation restrictions.
+        yes("May I ask a pointless question?")
+    #[
+       Note: these can be nested!!
+    ]#
+  ]#
+  ```
+
+
+Numbers
+-------
+
+Numerical literals are written as in most other languages. As a special twist,
+underscores are allowed for better readability: `1_000_000` (one million).
+A number that contains a dot (or 'e' or 'E') is a floating-point literal:
+`1.0e9` (one billion). Hexadecimal literals are prefixed with `0x`,
+binary literals with `0b` and octal literals with `0o`. A leading zero
+alone does not produce an octal.
+
+
+The var statement
+=================
+The var statement declares a new local or global variable:
+
+  ```nim
+  var x, y: int # declares x and y to have the type `int`
+  ```
+
+Indentation can be used after the `var` keyword to list a whole section of
+variables:
+
+  ```nim  test = "nim c $1"
+  var
+    x, y: int
+    # a comment can occur here too
+    a, b, c: string
+  ```
+
+
+Constants
+=========
+
+Constants are symbols which are bound to a value. The constant's value
+cannot change. The compiler must be able to evaluate the expression in a
+constant declaration at compile time:
+
+  ```nim  test = "nim c $1"
+  const x = "abc" # the constant x contains the string "abc"
+  ```
+
+Indentation can be used after the `const` keyword to list a whole section of
+constants:
+
+  ```nim  test = "nim c $1"
+  const
+    x = 1
+    # a comment can occur here too
+    y = 2
+    z = y + 5 # computations are possible
+  ```
+
+
+The let statement
+=================
+The `let` statement works like the `var` statement but the declared
+symbols are *single assignment* variables: After the initialization their
+value cannot change:
+
+  ```nim
+  let x = "abc" # introduces a new variable `x` and binds a value to it
+  x = "xyz"     # Illegal: assignment to `x`
+  ```
+
+The difference between `let` and `const` is: `let` introduces a variable
+that can not be re-assigned, `const` means "enforce compile time evaluation
+and put it into a data section":
+
+  ```nim
+  const input = readLine(stdin) # Error: constant expression expected
+  ```
+
+  ```nim  test = "nim c $1"
+  let input = readLine(stdin)   # works
+  ```
+
+
+The assignment statement
+========================
+
+The assignment statement assigns a new value to a variable or more generally
+to a storage location:
+
+  ```nim
+  var x = "abc" # introduces a new variable `x` and assigns a value to it
+  x = "xyz"     # assigns a new value to `x`
+  ```
+
+`=` is the *assignment operator*. The assignment operator can be
+overloaded. You can declare multiple variables with a single assignment
+statement and all the variables will have the same value:
+
+  ```nim  test = "nim c $1"
+  var x, y = 3  # assigns 3 to the variables `x` and `y`
+  echo "x ", x  # outputs "x 3"
+  echo "y ", y  # outputs "y 3"
+  x = 42        # changes `x` to 42 without changing `y`
+  echo "x ", x  # outputs "x 42"
+  echo "y ", y  # outputs "y 3"
+  ```
+
+
+Control flow statements
+=======================
+
+The greetings program consists of 3 statements that are executed sequentially.
+Only the most primitive programs can get away with that: branching and looping
+are needed too.
+
+
+If statement
+------------
+
+The if statement is one way to branch the control flow:
+
+  ```nim  test = "nim c $1"
+  let name = readLine(stdin)
+  if name == "":
+    echo "Poor soul, you lost your name?"
+  elif name == "name":
+    echo "Very funny, your name is name."
+  else:
+    echo "Hi, ", name, "!"
+  ```
+
+There can be zero or more `elif` parts, and the `else` part is optional.
+The keyword `elif` is short for `else if`, and is useful to avoid
+excessive indentation. (The `""` is the empty string. It contains no
+characters.)
+
+
+Case statement
+--------------
+
+Another way to branch is provided by the case statement. A case statement allows
+for multiple branches:
+
+  ```nim  test = "nim c $1"
+  let name = readLine(stdin)
+  case name
+  of "":
+    echo "Poor soul, you lost your name?"
+  of "name":
+    echo "Very funny, your name is name."
+  of "Dave", "Frank":
+    echo "Cool name!"
+  else:
+    echo "Hi, ", name, "!"
+  ```
+
+As it can be seen, for an `of` branch a comma-separated list of values is also
+allowed.
+
+The case statement can deal with integers, other ordinal types, and strings.
+(What an ordinal type is will be explained soon.)
+For integers or other ordinal types value ranges are also possible:
+
+  ```nim
+  # this statement will be explained later:
+  from std/strutils import parseInt
+
+  echo "A number please: "
+  let n = parseInt(readLine(stdin))
+  case n
+  of 0..2, 4..7: echo "The number is in the set: {0, 1, 2, 4, 5, 6, 7}"
+  of 3, 8: echo "The number is 3 or 8"
+  ```
+
+However, the above code **does not compile**: the reason is that you have to cover
+every value that `n` may contain, but the code only handles the values
+`0..8`. Since it is not very practical to list every other possible integer
+(though it is possible thanks to the range notation), we fix this by telling
+the compiler that for every other value nothing should be done:
+
+  ```nim
+  ...
+  case n
+  of 0..2, 4..7: echo "The number is in the set: {0, 1, 2, 4, 5, 6, 7}"
+  of 3, 8: echo "The number is 3 or 8"
+  else: discard
+  ```
+
+The empty [discard statement] is a *do
+nothing* statement. The compiler knows that a case statement with an else part
+cannot fail and thus the error disappears. Note that it is impossible to cover
+all possible string values: that is why string cases always need an `else`
+branch.
+
+In general, the case statement is used for subrange types or enumerations where
+it is of great help that the compiler checks that you covered any possible
+value.
+
+
+While statement
+---------------
+
+The while statement is a simple looping construct:
+
+  ```nim  test = "nim c $1"
+  echo "What's your name? "
+  var name = readLine(stdin)
+  while name == "":
+    echo "Please tell me your name: "
+    name = readLine(stdin) # no `var`, because we do not declare a new variable here
+  ```
+
+The example uses a while loop to keep asking the users for their name, as long
+as the user types in nothing (only presses RETURN).
+
+
+For statement
+-------------
+
+The `for` statement is a construct to loop over any element an *iterator*
+provides. The example uses the built-in [countup](
+system.html#countup.i,T,T,Positive) iterator:
+
+  ```nim  test = "nim c $1"
+  echo "Counting to ten: "
+  for i in countup(1, 10):
+    echo i
+  # --> Outputs 1 2 3 4 5 6 7 8 9 10 on different lines
+  ```
+
+The variable `i` is implicitly declared by the
+`for` loop and has the type `int`, because that is what [countup](
+system.html#countup.i,T,T,Positive) returns. `i` runs through the values
+1, 2, .., 10. Each value is `echo`-ed. This code does the same:
+
+  ```nim
+  echo "Counting to 10: "
+  var i = 1
+  while i <= 10:
+    echo i
+    inc i # increment i by 1
+  # --> Outputs 1 2 3 4 5 6 7 8 9 10 on different lines
+  ```
+
+Since counting up occurs so often in programs, Nim also has a [..](
+system.html#...i,T,T) iterator that does the same:
+
+  ```nim
+  for i in 1 .. 10:
+    ...
+  ```
+
+Counting down can be achieved as easily (but is less often needed):
+
+  ```nim
+  echo "Counting down from 10 to 1: "
+  for i in countdown(10, 1):
+    echo i
+  # --> Outputs 10 9 8 7 6 5 4 3 2 1 on different lines
+  ```
+
+Zero-indexed counting has two shortcuts `..<` and `.. ^1`
+([backward index operator](system.html#^.t%2Cint)) to simplify
+counting to one less than the higher index:
+
+  ```nim
+  for i in 0 ..< 10:
+    ...  # the same as 0 .. 9
+  ```
+
+or
+
+  ```nim
+  var s = "some string"
+  for i in 0 ..< s.len:
+    ...
+  ```
+
+or
+
+  ```nim
+  var s = "some string"
+  for idx, c in s[0 .. ^1]:
+    ... # ^1 is the last element, ^2 would be one before it, and so on
+  ```
+
+Other useful iterators for collections (like arrays and sequences) are
+* `items` and `mitems`, which provides immutable and mutable elements respectively, and
+* `pairs` and `mpairs` which provides the element and an index number (immutable and mutable respectively)
+
+  ```nim  test = "nim c $1"
+  for index, item in ["a","b"].pairs:
+    echo item, " at index ", index
+  # => a at index 0
+  # => b at index 1
+  ```
+
+Scopes and the block statement
+------------------------------
+
+Control flow statements have a feature not covered yet: they open a
+new scope. This means that in the following example, `x` is not accessible
+outside the loop:
+
+  ```nim  test = "nim c $1"  status = 1
+  while false:
+    var x = "hi"
+  echo x # does not work
+  ```
+
+A while (for) statement introduces an implicit block. Identifiers
+are only visible within the block they have been declared. The `block`
+statement can be used to open a new block explicitly:
+
+  ```nim  test = "nim c $1"  status = 1
+  block myblock:
+    var x = "hi"
+  echo x # does not work either
+  ```
+
+The block's *label* (`myblock` in the example) is optional.
+
+
+Break statement
+---------------
+
+A block can be left prematurely with a `break` statement. The break statement
+can leave a `while`, `for`, or a `block` statement. It leaves the
+innermost construct, unless a label of a block is given:
+
+  ```nim  test = "nim c $1"
+  block myblock:
+    echo "entering block"
+    while true:
+      echo "looping"
+      break # leaves the loop, but not the block
+    echo "still in block"
+  echo "outside the block"
+
+  block myblock2:
+    echo "entering block"
+    while true:
+      echo "looping"
+      break myblock2 # leaves the block (and the loop)
+    echo "still in block" # it won't be printed
+  echo "outside the block"
+  ```
+
+
+Continue statement
+------------------
+
+Like in many other programming languages, a `continue` statement starts
+the next iteration immediately:
+
+  ```nim  test = "nim c $1"
+  for i in 1 .. 5:
+    if i <= 3: continue
+    echo i # will only print 4 and 5
+  ```
+
+
+When statement
+--------------
+
+Example:
+
+  ```nim  test = "nim c $1"
+  when system.hostOS == "windows":
+    echo "running on Windows!"
+  elif system.hostOS == "linux":
+    echo "running on Linux!"
+  elif system.hostOS == "macosx":
+    echo "running on Mac OS X!"
+  else:
+    echo "unknown operating system"
+  ```
+
+The `when` statement is almost identical to the `if` statement, but with these
+differences:
+
+* Each condition must be a constant expression since it is evaluated by the
+  compiler.
+* The statements within a branch do not open a new scope.
+* The compiler checks the semantics and produces code *only* for the statements
+  that belong to the first condition that evaluates to `true`.
+
+The `when` statement is useful for writing platform-specific code, similar to
+the `#ifdef`:c: construct in the C programming language.
+
+
+Statements and indentation
+==========================
+
+Now that we covered the basic control flow statements, let's return to Nim
+indentation rules.
+
+In Nim, there is a distinction between *simple statements* and *complex
+statements*. *Simple statements* cannot contain other statements:
+Assignment, procedure calls, or the `return` statement are all simple
+statements. *Complex statements* like `if`, `when`, `for`, `while` can
+contain other statements. To avoid ambiguities, complex statements must always
+be indented, but single simple statements do not:
+
+  ```nim
+  # no indentation needed for single-assignment statement:
+  if x: x = false
+
+  # indentation needed for nested if statement:
+  if x:
+    if y:
+      y = false
+    else:
+      y = true
+
+  # indentation needed, because two statements follow the condition:
+  if x:
+    x = false
+    y = false
+  ```
+
+
+*Expressions* are parts of a statement that usually result in a value. The
+condition in an if statement is an example of an expression. Expressions can
+contain indentation at certain places for better readability:
+
+  ```nim
+  if thisIsaLongCondition() and
+      thisIsAnotherLongCondition(1,
+         2, 3, 4):
+    x = true
+  ```
+
+As a rule of thumb, indentation within expressions is allowed after operators,
+an open parenthesis and after commas.
+
+With parenthesis and semicolons `(;)` you can use statements where only
+an expression is allowed:
+
+  ```nim  test = "nim c $1"
+  # computes fac(4) at compile time:
+  const fac4 = (var x = 1; for i in 1..4: x *= i; x)
+  ```
+
+
+Procedures
+==========
+
+To define new commands like [echo](system.html#echo,varargs[typed,])
+and [readLine](syncio.html#readLine,File) in the examples, the concept of a
+*procedure* is needed. You might be used to them being called *methods* or
+*functions* in other languages, but Nim
+[differentiates these concepts](tut1.html#procedures-funcs-and-methods). In
+Nim, new procedures are defined with the `proc` keyword:
+
+  ```nim  test = "nim c $1"
+  proc yes(question: string): bool =
+    echo question, " (y/n)"
+    while true:
+      case readLine(stdin)
+      of "y", "Y", "yes", "Yes": return true
+      of "n", "N", "no", "No": return false
+      else: echo "Please be clear: yes or no"
+
+  if yes("Should I delete all your important files?"):
+    echo "I'm sorry Dave, I'm afraid I can't do that."
+  else:
+    echo "I think you know what the problem is just as well as I do."
+  ```
+
+This example shows a procedure named `yes` that asks the user a `question`
+and returns true if they answered "yes" (or something similar) and returns
+false if they answered "no" (or something similar). A `return` statement
+leaves the procedure (and therefore the while loop) immediately. The
+`(question: string): bool` syntax describes that the procedure expects a
+parameter named `question` of type `string` and returns a value of type
+`bool`. The `bool` type is built-in: the only valid values for `bool` are
+`true` and `false`.
+The conditions in if or while statements must be of type `bool`.
+
+Some terminology: in the example `question` is called a (formal) *parameter*,
+`"Should I..."` is called an *argument* that is passed to this parameter.
+
+
+Result variable
+---------------
+
+A procedure that returns a value has an implicit `result` variable declared
+that represents the return value. A `return` statement with no expression is
+shorthand for `return result`. The `result` value is always returned
+automatically at the end of a procedure if there is no `return` statement at
+the exit.
+
+  ```nim  test = "nim c $1"
+  proc sumTillNegative(x: varargs[int]): int =
+    for i in x:
+      if i < 0:
+        return
+      result = result + i
+
+  echo sumTillNegative() # echoes 0
+  echo sumTillNegative(3, 4, 5) # echoes 12
+  echo sumTillNegative(3, 4 , -1 , 6) # echoes 7
+  ```
+
+The `result` variable is already implicitly declared at the start of the
+function, so declaring it again with 'var result', for example, would shadow it
+with a normal variable of the same name. The result variable is also already
+initialized with the type's default value. Note that referential data types will
+be `nil` at the start of the procedure, and thus may require manual
+initialization.
+
+A procedure that does not have any `return` statement and does not use the
+special `result` variable returns the value of its last expression. For example,
+this procedure
+
+  ```nim  test = "nim c $1"
+  proc helloWorld(): string =
+    "Hello, World!"
+  ```
+
+returns the string "Hello, World!".
+
+Parameters
+----------
+
+Parameters are immutable in the procedure body. By default, their value cannot be
+changed because this allows the compiler to implement parameter passing in the
+most efficient way. If a mutable variable is needed inside the procedure, it has
+to be declared with `var` in the procedure body. Shadowing the parameter name
+is possible, and actually an idiom:
+
+  ```nim  test = "nim c $1"
+  proc printSeq(s: seq, nprinted: int = -1) =
+    var nprinted = if nprinted == -1: s.len else: min(nprinted, s.len)
+    for i in 0 ..< nprinted:
+      echo s[i]
+  ```
+
+If the procedure needs to modify the argument for the
+caller, a `var` parameter can be used:
+
+  ```nim  test = "nim c $1"
+  proc divmod(a, b: int; res, remainder: var int) =
+    res = a div b        # integer division
+    remainder = a mod b  # integer modulo operation
+
+  var
+    x, y: int
+  divmod(8, 5, x, y) # modifies x and y
+  echo x
+  echo y
+  ```
+
+In the example, `res` and `remainder` are `var parameters`.
+Var parameters can be modified by the procedure and the changes are
+visible to the caller. Note that the above example would better make use of
+a tuple as a return value instead of using var parameters.
+
+
+Discard statement
+-----------------
+
+To call a procedure that returns a value just for its side effects and ignoring
+its return value, a `discard` statement **must** be used. Nim does not
+allow silently throwing away a return value:
+
+  ```nim
+  discard yes("May I ask a pointless question?")
+  ```
+
+
+The return value can be ignored implicitly if the called proc/iterator has
+been declared with the `discardable` pragma:
+
+  ```nim  test = "nim c $1"
+  proc p(x, y: int): int {.discardable.} =
+    return x + y
+
+  p(3, 4) # now valid
+  ```
+
+
+Named arguments
+---------------
+
+Often a procedure has many parameters and it is not clear in which order the
+parameters appear. This is especially true for procedures that construct a
+complex data type. Therefore, the arguments to a procedure can be named, so
+that it is clear which argument belongs to which parameter:
+
+  ```nim
+  proc createWindow(x, y, width, height: int; title: string;
+                    show: bool): Window =
+     ...
+
+  var w = createWindow(show = true, title = "My Application",
+                       x = 0, y = 0, height = 600, width = 800)
+  ```
+
+Now that we use named arguments to call `createWindow` the argument order
+does not matter anymore. Mixing named arguments with ordered arguments is
+also possible, but not very readable:
+
+  ```nim
+  var w = createWindow(0, 0, title = "My Application",
+                       height = 600, width = 800, true)
+  ```
+
+The compiler checks that each parameter receives exactly one argument.
+
+
+Default values
+--------------
+
+To make the `createWindow` proc easier to use it should provide `default
+values`; these are values that are used as arguments if the caller does not
+specify them:
+
+  ```nim
+  proc createWindow(x = 0, y = 0, width = 500, height = 700,
+                    title = "unknown",
+                    show = true): Window =
+     ...
+
+  var w = createWindow(title = "My Application", height = 600, width = 800)
+  ```
+
+Now the call to `createWindow` only needs to set the values that differ
+from the defaults.
+
+Note that type inference works for parameters with default values; there is
+no need to write `title: string = "unknown"`, for example.
+
+
+Overloaded procedures
+---------------------
+
+Nim provides the ability to overload procedures similar to C++:
+
+  ```nim
+  proc toString(x: int): string =
+    result =
+      if x < 0: "negative"
+      elif x > 0: "positive"
+      else: "zero"
+
+  proc toString(x: bool): string =
+    result =
+      if x: "yep"
+      else: "nope"
+
+  assert toString(13) == "positive" # calls the toString(x: int) proc
+  assert toString(true) == "yep"    # calls the toString(x: bool) proc
+  ```
+
+(Note that `toString` is usually the [$](dollars.html) operator in
+Nim.) The compiler chooses the most appropriate proc for the `toString`
+calls. How this overloading resolution algorithm works exactly is not
+discussed here -- see the manual for details. Ambiguous calls are reported as errors.
+
+
+Operators
+---------
+
+The Nim standard library makes heavy use of overloading - one reason for this is that
+each operator like `+` is just an overloaded proc. The parser lets you
+use operators in *infix notation* (`a + b`) or *prefix notation* (`+ a`).
+An infix operator always receives two arguments, a prefix operator always one.
+(Postfix operators are not possible, because this would be ambiguous: does
+`a @ @ b` mean `(a) @ (@b)` or `(a@) @ (b)`? It always means
+`(a) @ (@b)`, because there are no postfix operators in Nim.)
+
+Apart from a few built-in keyword operators such as `and`, `or`, `not`,
+operators always consist of these characters:
+`+  -  *  \  /  <  >  =  @  $  ~  &  %  !  ?  ^  .  |`
+
+User-defined operators are allowed. Nothing stops you from defining your own
+`@!?+~` operator, but doing so may reduce readability.
+
+The operator's precedence is determined by its first character. The details
+can be [found in the manual](manual.html#syntax-precedence).
+
+To define a new operator enclose the operator in backticks "`":
+
+  ```nim
+  proc `$` (x: myDataType): string = ...
+  # now the $ operator also works with myDataType, overloading resolution
+  # ensures that $ works for built-in types just like before
+  ```
+
+The "`" notation can also be used to call an operator just like any other
+procedure:
+
+  ```nim  test = "nim c $1"
+  if `==`( `+`(3, 4), 7): echo "true"
+  ```
+
+
+Forward declarations
+--------------------
+
+Every variable, procedure, etc. needs to be declared before it can be used.
+(The reason for this is that it is non-trivial to avoid this need in a
+language that supports metaprogramming as extensively as Nim does.)
+However, this cannot be done for mutually recursive procedures:
+
+  ```nim
+  # forward declaration:
+  proc even(n: int): bool
+  ```
+
+  ```nim
+  proc odd(n: int): bool =
+    assert(n >= 0) # makes sure we don't run into negative recursion
+    if n == 0: false
+    else:
+      n == 1 or even(n-1)
+
+  proc even(n: int): bool =
+    assert(n >= 0) # makes sure we don't run into negative recursion
+    if n == 1: false
+    else:
+      n == 0 or odd(n-1)
+  ```
+
+Here `odd` depends on `even` and vice versa. Thus `even` needs to be
+introduced to the compiler before it is completely defined. The syntax for
+such a forward declaration is simple: just omit the `=` and the
+procedure's body. The `assert` just adds border conditions, and will be
+covered later in [Modules] section.
+
+Later versions of the language will weaken the requirements for forward
+declarations.
+
+The example also shows that a proc's body can consist of a single expression
+whose value is then returned implicitly.
+
+
+Funcs and methods
+-----------------
+
+As mentioned in the introduction, Nim differentiates between procedures,
+functions, and methods, defined by the `proc`, `func`, and `method` keywords
+respectively. In some ways, Nim is a bit more pedantic in its definitions than
+other languages.
+
+Functions are closer to the concept of a pure mathematical
+function, which might be familiar to you if you've ever done functional
+programming. Essentially they are procedures with additional limitations set on
+them: they can't access global state (except `const`) and can't produce
+side-effects. The `func` keyword is basically an alias for `proc` tagged
+with `{.noSideEffects.}`. Functions can still change their mutable arguments
+however, which are those marked as `var`, along with any `ref` objects.
+
+Unlike procedures, methods are dynamically dispatched. This sounds a bit
+complicated, but it is a concept closely related to inheritance and object-oriented
+programming. If you overload a procedure (two procedures with the same name but
+of different types or with different sets of arguments are said to be overloaded), the procedure to use is determined
+at compile-time. Methods, on the other hand, depend on objects that inherit from
+the `RootObj`. This is something that is covered in much greater depth in
+the [second part of the tutorial](tut2.html#object-oriented-programming-dynamic-dispatch).
+
+
+Iterators
+=========
+
+Let's return to the simple counting example:
+
+  ```nim  test = "nim c $1"
+  echo "Counting to ten: "
+  for i in countup(1, 10):
+    echo i
+  ```
+
+Can a [countup](system.html#countup.i,T,T,Positive) proc be written that
+supports this loop? Let's try:
+
+  ```nim
+  proc countup(a, b: int): int =
+    var res = a
+    while res <= b:
+      return res
+      inc(res)
+  ```
+
+However, this does not work. The problem is that the procedure should not
+only `return`, but return and **continue** after an iteration has
+finished. This *return and continue* is called a `yield` statement. Now
+the only thing left to do is to replace the `proc` keyword by `iterator`
+and here it is -- our first iterator:
+
+  ```nim  test = "nim c $1"
+  iterator countup(a, b: int): int =
+    var res = a
+    while res <= b:
+      yield res
+      inc(res)
+  ```
+
+Iterators look very similar to procedures, but there are several
+important differences:
+
+* Iterators can only be called from for loops.
+* Iterators cannot contain a `return` statement (and procs cannot contain a
+  `yield` statement).
+* Iterators have no implicit `result` variable.
+* Iterators do not support recursion.
+* Iterators cannot be forward declared, because the compiler must be able to inline an iterator.
+  (This restriction will be gone in a future version of the compiler.)
+
+However, you can also use a closure iterator to get a different set of
+restrictions. See [first-class iterators](
+manual.html#iterators-and-the-for-statement-firstminusclass-iterators)
+for details. Iterators can have the same name and parameters as a proc since
+essentially they have their own namespaces. Therefore, it is common to
+wrap iterators in procs of the same name which accumulate the result of the
+iterator and return it as a sequence, like `split` from the [strutils module](
+strutils.html).
+
+
+Basic types
+===========
+
+This section deals with the basic built-in types and the operations
+that are available for them in detail.
+
+Booleans
+--------
+
+Nim's boolean type is called `bool` and consists of the two
+pre-defined values `true` and `false`. Conditions in `while`,
+`if`, `elif`, and `when` statements must be of type bool.
+
+The operators `not, and, or, xor, <, <=, >, >=, !=, ==` are defined
+for the bool type. The `and` and `or` operators perform short-circuit
+evaluation. For example:
+
+  ```nim
+  while p != nil and p.name != "xyz":
+    # p.name is not evaluated if p == nil
+    p = p.next
+  ```
+
+
+Characters
+----------
+
+The *character type* is called `char`. Its size is always one byte, so
+it cannot represent most UTF-8 characters, but it *can* represent one of the bytes
+that makes up a multibyte UTF-8 character.
+The reason for this is efficiency: for the overwhelming majority of use-cases,
+the resulting programs will still handle UTF-8 properly as UTF-8 was especially
+designed for this.
+Character literals are enclosed in single quotes.
+
+Chars can be compared with the `==`, `<`, `<=`, `>`, `>=` operators.
+The `$` operator converts a `char` to a `string`. Chars cannot be mixed
+with integers; to get the ordinal value of a `char` use the `ord` proc.
+Converting from an integer to a `char` is done with the `chr` proc.
+
+
+Strings
+-------
+
+String variables are **mutable**, so appending to a string
+is possible, and quite efficient. Strings in Nim are both zero-terminated and have a
+length field. A string's length can be retrieved with the builtin `len`
+procedure; the length never counts the terminating zero. Accessing the
+terminating zero is an error, it only exists so that a Nim string can be converted
+to a `cstring` without doing a copy.
+
+The assignment operator for strings copies the string. You can use the `&`
+operator to concatenate strings and `add` to append to a string.
+
+Strings are compared using their lexicographical order. All the comparison operators
+are supported. By convention, all strings are UTF-8 encoded, but this is not
+enforced. For example, when reading strings from binary files, they are merely
+a sequence of bytes. The index operation `s[i]` means the i-th *char* of
+`s`, not the i-th *unichar*.
+
+A string variable is initialized with the empty string `""`.
+
+
+Integers
+--------
+
+Nim has these integer types built-in:
+`int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64`.
+
+The default integer type is `int`. Integer literals can have a *type suffix*
+to specify a non-default integer type:
+
+
+  ```nim  test = "nim c $1"
+  let
+    x = 0     # x is of type `int`
+    y = 0'i8  # y is of type `int8`
+    z = 0'i32 # z is of type `int32`
+    u = 0'u   # u is of type `uint`
+  ```
+
+Most often integers are used for counting objects that reside in memory, so
+`int` has the same size as a pointer.
+
+The common operators `+ - * div mod  <  <=  ==  !=  >  >=` are defined for
+integers. The `and or xor not` operators are also defined for integers and
+provide *bitwise* operations. Left bit shifting is done with the `shl`, right
+shifting with the `shr` operator. Bit shifting operators always treat their
+arguments as *unsigned*. For `arithmetic bit shifts`:idx: ordinary
+multiplication or division can be used.
+
+Unsigned operations all wrap around; they cannot lead to over- or under-flow
+errors.
+
+Lossless `Automatic type conversion`:idx: is performed in expressions where different
+kinds of integer types are used. However, if the type conversion
+would cause loss of information, the `RangeDefect`:idx: is raised (if the error
+cannot be detected at compile time).
+
+
+Floats
+------
+
+Nim has these floating-point types built-in: `float float32 float64`.
+
+The default float type is `float`. In the current implementation,
+`float` is always 64-bits.
+
+Float literals can have a *type suffix* to specify a non-default float
+type:
+
+  ```nim  test = "nim c $1"
+  var
+    x = 0.0      # x is of type `float`
+    y = 0.0'f32  # y is of type `float32`
+    z = 0.0'f64  # z is of type `float64`
+  ```
+
+The common operators `+ - * /  <  <=  ==  !=  >  >=` are defined for
+floats and follow the IEEE-754 standard.
+
+Automatic type conversion in expressions with different kinds of floating-point types is performed: the smaller type is converted to the larger. Integer
+types are **not** converted to floating-point types automatically, nor vice
+versa. Use the [toInt](system.html#toInt,float) and
+[toFloat](system.html#toFloat,int) procs for these conversions.
+
+
+Type Conversion
+---------------
+
+Conversion between numerical types is performed by using the
+type as a function:
+
+  ```nim  test = "nim c $1"
+  var
+    x: int32 = 1.int32   # same as calling int32(1)
+    y: int8  = int8('a') # 'a' == 97'i8
+    z: float = 2.5       # int(2.5) rounds down to 2
+    sum: int = int(x) + int(y) + int(z) # sum == 100
+  ```
+
+
+Internal type representation
+============================
+
+As mentioned earlier, the built-in [$](dollars.html) (stringify) operator
+turns any basic type into a string, which you can then print to the console
+using the `echo` proc. However, advanced types, and your own custom types,
+won't work with the `$` operator until you define it for them.
+Sometimes you just want to debug the current value of a complex type without
+having to write its `$` operator.  You can use then the [repr](
+system.html#repr,T) proc which works with any type and even complex data
+graphs with cycles. The following example shows that even for basic types
+there is a difference between the `$` and `repr` outputs:
+
+  ```nim  test = "nim c $1"
+  var
+    myBool = true
+    myCharacter = 'n'
+    myString = "nim"
+    myInteger = 42
+    myFloat = 3.14
+  echo myBool, ":", repr(myBool)
+  # --> true:true
+  echo myCharacter, ":", repr(myCharacter)
+  # --> n:'n'
+  echo myString, ":", repr(myString)
+  # --> nim:0x10fa8c050"nim"
+  echo myInteger, ":", repr(myInteger)
+  # --> 42:42
+  echo myFloat, ":", repr(myFloat)
+  # --> 3.14:3.14
+  ```
+
+
+Advanced types
+==============
+
+In Nim new types can be defined within a `type` statement:
+
+  ```nim  test = "nim c $1"
+  type
+    biggestInt = int64      # biggest integer type that is available
+    biggestFloat = float64  # biggest float type that is available
+  ```
+
+Enumeration and object types may only be defined within a
+`type` statement.
+
+
+Enumerations
+------------
+
+A variable of an enumeration type can only be assigned one of the enumeration's specified values.
+These values are a set of ordered symbols. Each symbol is mapped
+to an integer value internally. The first symbol is represented
+at runtime by 0, the second by 1, and so on. For example:
+
+  ```nim  test = "nim c $1"
+  type
+    Direction = enum
+      north, east, south, west
+
+  var x = south     # `x` is of type `Direction`; its value is `south`
+  echo x            # prints "south"
+  ```
+
+All the comparison operators can be used with enumeration types.
+
+An enumeration's symbol can be qualified to avoid ambiguities:
+`Direction.south`.
+
+The `$` operator can convert any enumeration value to its name, and the `ord`
+proc can convert it to its underlying integer value.
+
+For better interfacing to other programming languages, the symbols of enum
+types can be assigned an explicit ordinal value. However, the ordinal values
+must be in ascending order.
+
+
+Ordinal types
+-------------
+
+Enumerations, integer types, `char` and `bool` (and
+subranges) are called ordinal types. Ordinal types have quite
+a few special operations:
+
+
+=================     ========================================================
+Operation             Comment
+=================     ========================================================
+`ord(x)`              returns the integer value that is used to
+                      represent `x`'s value
+`inc(x)`              increments `x` by one
+`inc(x, n)`           increments `x` by `n`; `n` is an integer
+`dec(x)`              decrements `x` by one
+`dec(x, n)`           decrements `x` by `n`; `n` is an integer
+`succ(x)`             returns the successor of `x`
+`succ(x, n)`          returns the `n`'th successor of `x`
+`pred(x)`             returns the predecessor of `x`
+`pred(x, n)`          returns the `n`'th predecessor of `x`
+=================     ========================================================
+
+
+The [inc](system.html#inc,T,int), [dec](system.html#dec,T,int), [succ](
+system.html#succ,T,int) and [pred](system.html#pred,T,int) operations can
+fail by raising an `RangeDefect` or `OverflowDefect`. (If the code has been
+compiled with the proper runtime checks turned on.)
+
+
+Subranges
+---------
+
+A subrange type is a range of values from an integer or enumeration type
+(the base type). Example:
+
+  ```nim  test = "nim c $1"
+  type
+    MySubrange = range[0..5]
+  ```
+
+
+`MySubrange` is a subrange of `int` which can only hold the values 0
+to 5. Assigning any other value to a variable of type `MySubrange` is a
+compile-time or runtime error. Assignments from the base type to one of its
+subrange types (and vice versa) are allowed.
+
+The `system` module defines the important [Natural](system.html#Natural)
+type as `range[0..high(int)]` ([high](system.html#high,typedesc[T]) returns
+the maximal value). Other programming languages may suggest the use of unsigned
+integers for natural numbers. This is often **unwise**: you don't want unsigned
+arithmetic (which wraps around) just because the numbers cannot be negative.
+Nim's `Natural` type helps to avoid this common programming error.
+
+
+Sets
+----
+
+.. include:: sets_fragment.txt
+
+Arrays
+------
+
+An array is a simple fixed-length container. Each element in
+an array has the same type. The array's index type can be any ordinal type.
+
+Arrays can be constructed using `[]`:
+
+  ```nim  test = "nim c $1"
+  type
+    IntArray = array[0..5, int] # an array that is indexed with 0..5
+  var
+    x: IntArray
+  x = [1, 2, 3, 4, 5, 6]
+  for i in low(x) .. high(x):
+    echo x[i]
+  ```
+
+The notation `x[i]` is used to access the i-th element of `x`.
+Array access is always bounds checked (at compile-time or at runtime). These
+checks can be disabled via pragmas or invoking the compiler with the
+``--bound_checks:off`` command line switch.
+
+Arrays are value types, like any other Nim type. The assignment operator
+copies the whole array contents.
+
+The built-in [len](system.html#len,TOpenArray) proc returns the array's
+length. [low(a)](system.html#low,openArray[T]) returns the lowest valid index
+for the array `a` and [high(a)](system.html#high,openArray[T]) the highest
+valid index.
+
+  ```nim  test = "nim c $1"
+  type
+    Direction = enum
+      north, east, south, west
+    BlinkLights = enum
+      off, on, slowBlink, mediumBlink, fastBlink
+    LevelSetting = array[north..west, BlinkLights]
+  var
+    level: LevelSetting
+  level[north] = on
+  level[south] = slowBlink
+  level[east] = fastBlink
+  echo level        # --> [on, fastBlink, slowBlink, off]
+  echo low(level)   # --> north
+  echo len(level)   # --> 4
+  echo high(level)  # --> west
+  ```
+
+The syntax for nested arrays (multidimensional) in other languages is a matter
+of appending more brackets because usually each dimension is restricted to the
+same index type as the others. In Nim you can have different dimensions with
+different index types, so the nesting syntax is slightly different. Building on
+the previous example where a level is defined as an array of enums indexed by
+yet another enum, we can add the following lines to add a light tower type
+subdivided into height levels accessed through their integer index:
+
+  ```nim
+  type
+    LightTower = array[1..10, LevelSetting]
+  var
+    tower: LightTower
+  tower[1][north] = slowBlink
+  tower[1][east] = mediumBlink
+  echo len(tower)     # --> 10
+  echo len(tower[1])  # --> 4
+  echo tower          # --> [[slowBlink, mediumBlink, ...more output..
+  # The following lines don't compile due to type mismatch errors
+  #tower[north][east] = on
+  #tower[0][1] = on
+  ```
+
+Note how the built-in `len` proc returns only the array's first dimension
+length.  Another way of defining the `LightTower` to better illustrate its
+nested nature would be to omit the previous definition of the `LevelSetting`
+type and instead write it embedded directly as the type of the first dimension:
+
+  ```nim
+  type
+    LightTower = array[1..10, array[north..west, BlinkLights]]
+  ```
+
+It is quite common to have arrays start at zero, so there's a shortcut syntax
+to specify a range from zero to the specified index minus one:
+
+  ```nim  test = "nim c $1"
+  type
+    IntArray = array[0..5, int] # an array that is indexed with 0..5
+    QuickArray = array[6, int]  # an array that is indexed with 0..5
+  var
+    x: IntArray
+    y: QuickArray
+  x = [1, 2, 3, 4, 5, 6]
+  y = x
+  for i in low(x) .. high(x):
+    echo x[i], y[i]
+  ```
+
+
+Sequences
+---------
+
+Sequences are similar to arrays but of dynamic length which may change
+during runtime (like strings). Since sequences are resizable they are always
+allocated on the heap and garbage collected.
+
+Sequences are always indexed with an `int` starting at position 0.  The [len](
+system.html#len,seq[T]), [low](system.html#low,openArray[T]) and [high](
+system.html#high,openArray[T]) operations are available for sequences too.
+The notation `x[i]` can be used to access the i-th element of `x`.
+
+Sequences can be constructed by the array constructor `[]` in conjunction
+with the array to sequence operator `@`. Another way to allocate space for
+a sequence is to call the built-in [newSeq](system.html#newSeq) procedure.
+
+A sequence may be passed to an openarray parameter.
+
+Example:
+
+  ```nim  test = "nim c $1"
+  var
+    x: seq[int] # a reference to a sequence of integers
+  x = @[1, 2, 3, 4, 5, 6] # the @ turns the array into a sequence allocated on the heap
+  ```
+
+Sequence variables are initialized with `@[]`.
+
+The `for` statement can be used with one or two variables when used with a
+sequence. When you use the one variable form, the variable will hold the value
+provided by the sequence. The `for` statement is looping over the results
+from the [items()](iterators.html#items.i,seq[T]) iterator from the [system](
+system.html) module.  But if you use the two-variable form, the first
+variable will hold the index position and the second variable will hold the
+value. Here the `for` statement is looping over the results from the
+[pairs()](iterators.html#pairs.i,seq[T]) iterator from the [system](
+system.html) module.  Examples:
+
+  ```nim  test = "nim c $1"
+  for value in @[3, 4, 5]:
+    echo value
+  # --> 3
+  # --> 4
+  # --> 5
+
+  for i, value in @[3, 4, 5]:
+    echo "index: ", $i, ", value:", $value
+  # --> index: 0, value:3
+  # --> index: 1, value:4
+  # --> index: 2, value:5
+  ```
+
+
+Open arrays
+-----------
+
+**Note**: Openarrays can only be used for parameters.
+
+Often fixed-size arrays turn out to be too inflexible; procedures should be
+able to deal with arrays of different sizes. The `openarray`:idx: type allows
+this. Openarrays are always indexed with an `int` starting at position 0.
+The [len](system.html#len,TOpenArray), [low](system.html#low,openArray[T])
+and [high](system.html#high,openArray[T]) operations are available for open
+arrays too.  Any array with a compatible base type can be passed to an
+openarray parameter, the index type does not matter.
+
+  ```nim  test = "nim c $1"
+  var
+    fruits:   seq[string]       # reference to a sequence of strings that is initialized with '@[]'
+    capitals: array[3, string]  # array of strings with a fixed size
+
+  capitals = ["New York", "London", "Berlin"]   # array 'capitals' allows assignment of only three elements
+  fruits.add("Banana")          # sequence 'fruits' is dynamically expandable during runtime
+  fruits.add("Mango")
+
+  proc openArraySize(oa: openArray[string]): int =
+    oa.len
+
+  assert openArraySize(fruits) == 2     # procedure accepts a sequence as parameter
+  assert openArraySize(capitals) == 3   # but also an array type
+  ```
+
+The openarray type cannot be nested: multidimensional openarrays are not
+supported because this is seldom needed and cannot be done efficiently.
+
+
+Varargs
+-------
+
+A `varargs` parameter is like an openarray parameter. However, it is
+also a means to implement passing a variable number of
+arguments to a procedure. The compiler converts the list of arguments
+to an array automatically:
+
+  ```nim  test = "nim c $1"
+  proc myWriteln(f: File, a: varargs[string]) =
+    for s in items(a):
+      write(f, s)
+    write(f, "\n")
+
+  myWriteln(stdout, "abc", "def", "xyz")
+  # is transformed by the compiler to:
+  myWriteln(stdout, ["abc", "def", "xyz"])
+  ```
+
+This transformation is only done if the varargs parameter is the
+last parameter in the procedure header. It is also possible to perform
+type conversions in this context:
+
+  ```nim  test = "nim c $1"
+  proc myWriteln(f: File, a: varargs[string, `$`]) =
+    for s in items(a):
+      write(f, s)
+    write(f, "\n")
+
+  myWriteln(stdout, 123, "abc", 4.0)
+  # is transformed by the compiler to:
+  myWriteln(stdout, [$123, $"abc", $4.0])
+  ```
+
+In this example [$](dollars.html) is applied to any argument that is passed
+to the parameter `a`. Note that [$](dollars.html) applied to strings is a
+nop.
+
+
+Slices
+------
+
+Slices look similar to subranges types in syntax but are used in a different
+context. A slice is just an object of type Slice which contains two bounds,
+`a` and `b`. By itself a slice is not very useful, but other collection types
+define operators which accept Slice objects to define ranges.
+
+  ```nim  test = "nim c $1"
+  var
+    a = "Nim is a programming language"
+    b = "Slices are useless."
+
+  echo a[7 .. 12] # --> 'a prog'
+  b[11 .. ^2] = "useful"
+  echo b # --> 'Slices are useful.'
+  ```
+
+In the previous example slices are used to modify a part of a string. The
+slice's bounds can hold any value supported by
+their type, but it is the proc using the slice object which defines what values
+are accepted.
+
+To understand the different ways of specifying the indices of
+strings, arrays, sequences, etc., it must be remembered that Nim uses
+zero-based indices.
+
+So the string `b` is of length 19, and two different ways of specifying the
+indices are
+
+  ```nim
+  "Slices are useless."
+   |          |     |
+   0         11    17   using indices
+  ^19        ^8    ^2   using ^ syntax
+  ```
+
+where `b[0 .. ^1]` is equivalent to `b[0 .. b.len-1]` and `b[0 ..< b.len]`, and it
+can be seen that the `^1` provides a shorthand way of specifying the `b.len-1`. See
+the [backwards index operator](system.html#^.t%2Cint).
+
+In the above example, because the string ends in a period, to get the portion of the
+string that is "useless" and replace it with "useful".
+
+`b[11 .. ^2]` is the portion "useless", and `b[11 .. ^2] = "useful"` replaces the
+"useless" portion with "useful", giving the result "Slices are useful."
+
+Note 1: alternate ways of writing this are `b[^8 .. ^2] = "useful"` or
+as `b[11 .. b.len-2] = "useful"` or as `b[11 ..< b.len-1] = "useful"`.
+
+Note 2: As the `^` template returns a [distinct int](manual.html#types-distinct-type)
+of type `BackwardsIndex`, we can have a `lastIndex` constant defined as `const lastIndex = ^1`,
+and later used as `b[0 .. lastIndex]`.
+
+Objects
+-------
+
+The default type to pack different values together in a single
+structure with a name is the object type. An object is a value type,
+which means that when an object is assigned to a new variable all its
+components are copied as well.
+
+Each object type `Foo` has a constructor `Foo(field: value, ...)`
+where all of its fields can be initialized. Unspecified fields will
+get their default value.
+
+  ```nim
+  type
+    Person = object
+      name: string
+      age: int
+
+  var person1 = Person(name: "Peter", age: 30)
+
+  echo person1.name # "Peter"
+  echo person1.age  # 30
+
+  var person2 = person1 # copy of person 1
+
+  person2.age += 14
+
+  echo person1.age # 30
+  echo person2.age # 44
+
+
+  # the order may be changed
+  let person3 = Person(age: 12, name: "Quentin")
+
+  # not every member needs to be specified
+  let person4 = Person(age: 3)
+  # unspecified members will be initialized with their default
+  # values. In this case it is the empty string.
+  doAssert person4.name == ""
+  ```
+
+
+Object fields that should be visible from outside the defining module have to
+be marked with `*`.
+
+  ```nim  test = "nim c $1"
+  type
+    Person* = object # the type is visible from other modules
+      name*: string  # the field of this type is visible from other modules
+      age*: int
+  ```
+
+Tuples
+------
+
+Tuples are very much like what you have seen so far from objects. They
+are value types where the assignment operator copies each component.
+Unlike object types though, tuple types are structurally typed,
+meaning different tuple-types are *equivalent* if they specify fields of
+the same type and of the same name in the same order.
+
+The constructor `()` can be used to construct tuples. The order of the
+fields in the constructor must match the order in the tuple's
+definition. But unlike objects, a name for the tuple type may not be
+used here.
+
+
+Like the object type the notation `t.field` is used to access a
+tuple's field. Another notation that is not available for objects is
+`t[i]` to access the `i`'th field. Here `i` must be a constant
+integer.
+
+  ```nim  test = "nim c $1"
+  type
+    # type representing a person:
+    # A person consists of a name and an age.
+    Person = tuple
+      name: string
+      age: int
+
+    # Alternative syntax for an equivalent type.
+    PersonX = tuple[name: string, age: int]
+
+    # anonymous field syntax
+    PersonY = (string, int)
+
+  var
+    person: Person
+    personX: PersonX
+    personY: PersonY
+
+  person = (name: "Peter", age: 30)
+  # Person and PersonX are equivalent
+  personX = person
+
+  # Create a tuple with anonymous fields:
+  personY = ("Peter", 30)
+
+  # A tuple with anonymous fields is compatible with a tuple that has
+  # field names.
+  person = personY
+  personY = person
+
+  # Usually used for short tuple initialization syntax
+  person = ("Peter", 30)
+
+  echo person.name # "Peter"
+  echo person.age  # 30
+
+  echo person[0] # "Peter"
+  echo person[1] # 30
+
+  # You don't need to declare tuples in a separate type section.
+  var building: tuple[street: string, number: int]
+  building = ("Rue del Percebe", 13)
+  echo building.street
+
+  # The following line does not compile, they are different tuples!
+  #person = building
+  # --> Error: type mismatch: got (tuple[street: string, number: int])
+  #     but expected 'Person'
+  ```
+
+Even though you don't need to declare a type for a tuple to use it, tuples
+created with different field names will be considered different objects despite
+having the same field types.
+
+Tuples can be *unpacked* during variable assignment. This can
+be handy to assign directly the fields of the tuples to individually named
+variables. An example of this is the [splitFile](os.html#splitFile,string)
+proc from the [os module](os.html) which returns the directory, name, and
+extension of a path at the same time. For tuple unpacking to work you must
+use parentheses around the values you want to assign the unpacking to,
+otherwise, you will be assigning the same value to all the individual
+variables! For example:
+
+  ```nim  test = "nim c $1"
+  import std/os
+
+  let
+    path = "usr/local/nimc.html"
+    (dir, name, ext) = splitFile(path)
+    baddir, badname, badext = splitFile(path)
+  echo dir      # outputs "usr/local"
+  echo name     # outputs "nimc"
+  echo ext      # outputs ".html"
+  # All the following output the same line:
+  # "(dir: usr/local, name: nimc, ext: .html)"
+  echo baddir
+  echo badname
+  echo badext
+  ```
+
+Tuple unpacking is also supported in for-loops:
+
+  ```nim  test = "nim c $1"
+  let a = [(10, 'a'), (20, 'b'), (30, 'c')]
+
+  for (x, c) in a:
+    echo x
+  # This will output: 10; 20; 30
+
+  # Accessing the index is also possible:
+  for i, (x, c) in a:
+    echo i, c
+  # This will output: 0a; 1b; 2c
+  ```
+
+Fields of tuples are always public, they don't need to be explicitly
+marked to be exported, unlike for example fields in an object type.
+
+
+Reference and pointer types
+---------------------------
+
+References (similar to pointers in other programming languages) are a
+way to introduce many-to-one relationships. This means different references can
+point to and modify the same location in memory.
+
+Nim distinguishes between `traced`:idx: and `untraced`:idx: references.
+Untraced references are also called *pointers*. Traced references point to
+objects in a garbage-collected heap, untraced references point to
+manually allocated objects or objects elsewhere in memory. Thus,
+untraced references are *unsafe*. However, for certain low-level operations
+(e.g. accessing the hardware), untraced references are necessary.
+
+Traced references are declared with the **ref** keyword; untraced references
+are declared with the **ptr** keyword.
+
+The empty `[]` subscript notation can be used to *de-refer* a reference,
+meaning to retrieve the item the reference points to. The `.` (access a
+tuple/object field operator) and `[]` (array/string/sequence index operator)
+operators perform implicit dereferencing operations for reference types:
+
+  ```nim  test = "nim c $1"
+  type
+    Node = ref object
+      le, ri: Node
+      data: int
+
+  var n = Node(data: 9)
+  echo n.data
+  # no need to write n[].data; in fact n[].data is highly discouraged!
+  ```
+
+To allocate a new traced object, the built-in procedure `new` can be used:
+
+  ```nim
+  var n: Node
+  new(n)
+  ```
+
+To deal with untraced memory, the procedures `alloc`, `dealloc` and
+`realloc` can be used. The [system](system.html)
+module's documentation contains further details.
+
+If a reference points to *nothing*, it has the value `nil`.
+
+
+Procedural type
+---------------
+
+A procedural type is a (somewhat abstract) pointer to a procedure.
+`nil` is an allowed value for a variable of a procedural type.
+Nim uses procedural types to achieve `functional`:idx: programming
+techniques.
+
+Example:
+
+  ```nim  test = "nim c $1"
+  proc greet(name: string): string =
+    "Hello, " & name & "!"
+
+  proc bye(name: string): string =
+    "Goodbye, " & name & "."
+
+  proc communicate(greeting: proc (x: string): string, name: string) =
+    echo greeting(name)
+
+  communicate(greet, "John")
+  communicate(bye, "Mary")
+  ```
+
+A subtle issue with procedural types is that the calling convention of the
+procedure influences the type compatibility: procedural types are only compatible
+if they have the same calling convention. The different calling conventions are
+listed in the [manual](manual.html#types-procedural-type).
+
+Distinct type
+-------------
+
+A Distinct type allows for the creation of a new type that "does not imply a
+subtype relationship between it and its base type".
+You must **explicitly** define all behavior for the distinct type.
+To help with this, both the distinct type and its base type can cast from one
+type to the other.
+Examples are provided in the [manual](manual.html#types-distinct-type).
+
+Modules
+=======
+
+Nim supports splitting a program into pieces with a *module* concept.
+Each module is in its own file. Modules enable `information hiding`:idx: and
+`separate compilation`:idx:. A module may gain access to the symbols of another
+module by using the `import`:idx: statement. Only top-level symbols that are marked
+with an asterisk (`*`) are exported:
+
+  ```nim
+  # Module A
+  var
+    x*, y: int
+
+  proc `*` *(a, b: seq[int]): seq[int] =
+    # allocate a new sequence:
+    newSeq(result, len(a))
+    # multiply two int sequences:
+    for i in 0 ..< len(a): result[i] = a[i] * b[i]
+
+  when isMainModule:
+    # test the new `*` operator for sequences:
+    assert(@[1, 2, 3] * @[1, 2, 3] == @[1, 4, 9])
+  ```
+
+The above module exports `x` and `*`, but not `y`.
+
+A module's top-level statements are executed at the start of the program.
+This can be used to initialize complex data structures for example.
+
+Each module has a special magic constant `isMainModule` that is true if the
+module is compiled as the main file. This is very useful to embed tests within
+the module as shown by the above example.
+
+A symbol of a module *can* be *qualified* with the `module.symbol` syntax. And if
+a symbol is ambiguous, it *must* be qualified. A symbol is ambiguous
+if it is defined in two (or more) different modules and both modules are
+imported by a third one:
+
+  ```nim
+  # Module A
+  var x*: string
+  ```
+
+  ```nim
+  # Module B
+  var x*: int
+  ```
+
+  ```nim
+  # Module C
+  import A, B
+  write(stdout, x) # error: x is ambiguous
+  write(stdout, A.x) # okay: qualifier used
+
+  var x = 4
+  write(stdout, x) # not ambiguous: uses the module C's x
+  ```
+
+
+But this rule does not apply to procedures or iterators. Here the overloading
+rules apply:
+
+  ```nim
+  # Module A
+  proc x*(a: int): string = $a
+  ```
+
+  ```nim
+  # Module B
+  proc x*(a: string): string = $a
+  ```
+
+  ```nim
+  # Module C
+  import A, B
+  write(stdout, x(3))   # no error: A.x is called
+  write(stdout, x(""))  # no error: B.x is called
+
+  proc x*(a: int): string = discard
+  write(stdout, x(3))   # ambiguous: which `x` is to call?
+  ```
+
+
+Excluding symbols
+-----------------
+
+The normal `import` statement will bring in all exported symbols.
+These can be limited by naming symbols that should be excluded using
+the `except` qualifier.
+
+  ```nim
+  import mymodule except y
+  ```
+
+
+From statement
+--------------
+
+We have already seen the simple `import` statement that just imports all
+exported symbols. An alternative that only imports listed symbols is the
+`from import` statement:
+
+  ```nim
+  from mymodule import x, y, z
+  ```
+
+The `from` statement can also force namespace qualification on
+symbols, thereby making symbols available, but needing to be qualified
+in order to be used.
+
+  ```nim
+  from mymodule import x, y, z
+
+  x()           # use x without any qualification
+  ```
+
+  ```nim
+  from mymodule import nil
+
+  mymodule.x()  # must qualify x with the module name as prefix
+
+  x()           # using x here without qualification is a compile error
+  ```
+
+Since module names are generally long to be descriptive, you can also
+define a shorter alias to use when qualifying symbols.
+
+  ```nim
+  from mymodule as m import nil
+
+  m.x()         # m is aliasing mymodule
+  ```
+
+
+Include statement
+-----------------
+
+The `include` statement does something fundamentally different than
+importing a module: it merely includes the contents of a file. The `include`
+statement is useful to split up a large module into several files:
+
+  ```nim
+  include fileA, fileB, fileC
+  ```
+
+
+
+Part 2
+======
+
+So, now that we are done with the basics, let's see what Nim offers apart
+from a nice syntax for procedural programming: [Part II](tut2.html)
+
+
+.. _strutils: strutils.html
+.. _system: system.html
diff --git a/doc/tut1.rst b/doc/tut1.rst
deleted file mode 100644
index aa6114cf7..000000000
--- a/doc/tut1.rst
+++ /dev/null
@@ -1,1750 +0,0 @@
-=====================
-Nim Tutorial (Part I)
-=====================
-
-:Author: Andreas Rumpf
-:Version: |nimversion|
-
-.. contents::
-
-Introduction
-============
-
-.. raw:: html
-  <blockquote><p>
-  "Der Mensch ist doch ein Augentier -- sch&ouml;ne Dinge w&uuml;nsch ich mir."
-  </p></blockquote>
-
-
-This document is a tutorial for the programming language *Nim*.
-This tutorial assumes that you are familiar with basic programming concepts
-like variables, types or statements but is kept very basic. The `manual
-<manual.html>`_ contains many more examples of the advanced language features.
-All code examples in this tutorial, as well as the ones found in the rest of
-Nim's documentation, follow the `Nim style guide <nep1.html>`_.
-
-
-The first program
-=================
-
-We start the tour with a modified "hello world" program:
-
-.. code-block:: Nim
-    :test: "nim c $1"
-  # This is a comment
-  echo "What's your name? "
-  var name: string = readLine(stdin)
-  echo "Hi, ", name, "!"
-
-
-Save this code to the file "greetings.nim". Now compile and run it::
-
-  nim compile --run greetings.nim
-
-With the ``--run`` `switch <nimc.html#compiler-usage-command-line-switches>`_ Nim
-executes the file automatically after compilation. You can give your program
-command line arguments by appending them after the filename::
-
-  nim compile --run greetings.nim arg1 arg2
-
-Commonly used commands and switches have abbreviations, so you can also use::
-
-  nim c -r greetings.nim
-
-To compile a release version use::
-
-  nim c -d:release greetings.nim
-
-By default the Nim compiler generates a large amount of runtime checks
-aiming for your debugging pleasure. With ``-d:release`` these checks are
-`turned off and optimizations are turned on
-<nimc.html#compiler-usage-compile-time-symbols>`_.
-
-Though it should be pretty obvious what the program does, I will explain the
-syntax: statements which are not indented are executed when the program
-starts. Indentation is Nim's way of grouping statements. Indentation is
-done with spaces only, tabulators are not allowed.
-
-String literals are enclosed in double quotes. The ``var`` statement declares
-a new variable named ``name`` of type ``string`` with the value that is
-returned by the `readLine <system.html#readLine,File>`_ procedure. Since the
-compiler knows that `readLine <system.html#readLine,File>`_ returns a string,
-you can leave out the type in the declaration (this is called `local type
-inference`:idx:). So this will work too:
-
-.. code-block:: Nim
-    :test: "nim c $1"
-  var name = readLine(stdin)
-
-Note that this is basically the only form of type inference that exists in
-Nim: it is a good compromise between brevity and readability.
-
-The "hello world" program contains several identifiers that are already known
-to the compiler: ``echo``, `readLine <system.html#readLine,File>`_, etc.
-These built-ins are declared in the system_ module which is implicitly
-imported by any other module.
-
-
-Lexical elements
-================
-
-Let us look at Nim's lexical elements in more detail: like other
-programming languages Nim consists of (string) literals, identifiers,
-keywords, comments, operators, and other punctuation marks.
-
-
-String and character literals
------------------------------
-
-String literals are enclosed in double quotes; character literals in single
-quotes. Special characters are escaped with ``\``: ``\n`` means newline, ``\t``
-means tabulator, etc. There are also *raw* string literals:
-
-.. code-block:: Nim
-  r"C:\program files\nim"
-
-In raw literals the backslash is not an escape character.
-
-The third and last way to write string literals are *long string literals*.
-They are written with three quotes: ``""" ... """``; they can span over
-multiple lines and the ``\`` is not an escape character either. They are very
-useful for embedding HTML code templates for example.
-
-
-Comments
---------
-
-Comments start anywhere outside a string or character literal with the
-hash character ``#``. Documentation comments start with ``##``:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  # A comment.
-
-  var myVariable: int ## a documentation comment
-
-
-Documentation comments are tokens; they are only allowed at certain places in
-the input file as they belong to the syntax tree! This feature enables simpler
-documentation generators.
-
-Multiline comments are started with ``#[`` and terminated with ``]#``.  Multiline
-comments can also be nested.
-
-.. code-block:: nim
-    :test: "nim c $1"
-  #[
-  You can have any Nim code text commented
-  out inside this with no indentation restrictions.
-        yes("May I ask a pointless question?")
-    #[
-       Note: these can be nested!!
-    ]#
-  ]#
-
-You can also use the `discard statement <#procedures-discard-statement>`_ together with *long string
-literals* to create block comments:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  discard """ You can have any Nim code text commented
-  out inside this with no indentation restrictions.
-        yes("May I ask a pointless question?") """
-
-
-Numbers
--------
-
-Numerical literals are written as in most other languages. As a special twist,
-underscores are allowed for better readability: ``1_000_000`` (one million).
-A number that contains a dot (or 'e' or 'E') is a floating point literal:
-``1.0e9`` (one billion). Hexadecimal literals are prefixed with ``0x``,
-binary literals with ``0b`` and octal literals with ``0o``. A leading zero
-alone does not produce an octal.
-
-
-The var statement
-=================
-The var statement declares a new local or global variable:
-
-.. code-block::
-  var x, y: int # declares x and y to have the type ``int``
-
-Indentation can be used after the ``var`` keyword to list a whole section of
-variables:
-
-.. code-block::
-    :test: "nim c $1"
-  var
-    x, y: int
-    # a comment can occur here too
-    a, b, c: string
-
-
-The assignment statement
-========================
-
-The assignment statement assigns a new value to a variable or more generally
-to a storage location:
-
-.. code-block::
-  var x = "abc" # introduces a new variable `x` and assigns a value to it
-  x = "xyz"     # assigns a new value to `x`
-
-``=`` is the *assignment operator*. The assignment operator can be
-overloaded. You can declare multiple variables with a single assignment
-statement and all the variables will have the same value:
-
-.. code-block::
-    :test: "nim c $1"
-  var x, y = 3  # assigns 3 to the variables `x` and `y`
-  echo "x ", x  # outputs "x 3"
-  echo "y ", y  # outputs "y 3"
-  x = 42        # changes `x` to 42 without changing `y`
-  echo "x ", x  # outputs "x 42"
-  echo "y ", y  # outputs "y 3"
-
-Note that declaring multiple variables with a single assignment which calls a
-procedure can have unexpected results: the compiler will *unroll* the
-assignments and end up calling the procedure several times. If the result of
-the procedure depends on side effects, your variables may end up having
-different values! For safety use side-effect free procedures if making multiple
-assignments.
-
-
-Constants
-=========
-
-Constants are symbols which are bound to a value. The constant's value
-cannot change. The compiler must be able to evaluate the expression in a
-constant declaration at compile time:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  const x = "abc" # the constant x contains the string "abc"
-
-Indentation can be used after the ``const`` keyword to list a whole section of
-constants:
-
-.. code-block::
-    :test: "nim c $1"
-  const
-    x = 1
-    # a comment can occur here too
-    y = 2
-    z = y + 5 # computations are possible
-
-
-The let statement
-=================
-The ``let`` statement works like the ``var`` statement but the declared
-symbols are *single assignment* variables: After the initialization their
-value cannot change:
-
-.. code-block::
-  let x = "abc" # introduces a new variable `x` and binds a value to it
-  x = "xyz"     # Illegal: assignment to `x`
-
-The difference between ``let`` and ``const`` is: ``let`` introduces a variable
-that can not be re-assigned, ``const`` means "enforce compile time evaluation
-and put it into a data section":
-
-.. code-block::
-  const input = readLine(stdin) # Error: constant expression expected
-
-.. code-block::
-    :test: "nim c $1"
-  let input = readLine(stdin)   # works
-
-
-Control flow statements
-=======================
-
-The greetings program consists of 3 statements that are executed sequentially.
-Only the most primitive programs can get away with that: branching and looping
-are needed too.
-
-
-If statement
-------------
-
-The if statement is one way to branch the control flow:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  let name = readLine(stdin)
-  if name == "":
-    echo "Poor soul, you lost your name?"
-  elif name == "name":
-    echo "Very funny, your name is name."
-  else:
-    echo "Hi, ", name, "!"
-
-There can be zero or more ``elif`` parts, and the ``else`` part is optional.
-The keyword ``elif`` is short for ``else if``, and is useful to avoid
-excessive indentation. (The ``""`` is the empty string. It contains no
-characters.)
-
-
-Case statement
---------------
-
-Another way to branch is provided by the case statement. A case statement is
-a multi-branch:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  let name = readLine(stdin)
-  case name
-  of "":
-    echo "Poor soul, you lost your name?"
-  of "name":
-    echo "Very funny, your name is name."
-  of "Dave", "Frank":
-    echo "Cool name!"
-  else:
-    echo "Hi, ", name, "!"
-
-As it can be seen, for an ``of`` branch a comma separated list of values is also
-allowed.
-
-The case statement can deal with integers, other ordinal types and strings.
-(What an ordinal type is will be explained soon.)
-For integers or other ordinal types value ranges are also possible:
-
-.. code-block:: nim
-  # this statement will be explained later:
-  from strutils import parseInt
-
-  echo "A number please: "
-  let n = parseInt(readLine(stdin))
-  case n
-  of 0..2, 4..7: echo "The number is in the set: {0, 1, 2, 4, 5, 6, 7}"
-  of 3, 8: echo "The number is 3 or 8"
-
-However, the above code does not compile: the reason is that you have to cover
-every value that ``n`` may contain, but the code only handles the values
-``0..8``. Since it is not very practical to list every other possible integer
-(though it is possible thanks to the range notation), we fix this by telling
-the compiler that for every other value nothing should be done:
-
-.. code-block:: nim
-  ...
-  case n
-  of 0..2, 4..7: echo "The number is in the set: {0, 1, 2, 4, 5, 6, 7}"
-  of 3, 8: echo "The number is 3 or 8"
-  else: discard
-
-The empty `discard statement`_ is a *do nothing* statement. The compiler knows
-that a case statement with an else part cannot fail and thus the error
-disappears. Note that it is impossible to cover all possible string values:
-that is why string cases always need an ``else`` branch.
-
-In general the case statement is used for subrange types or enumerations where
-it is of great help that the compiler checks that you covered any possible
-value.
-
-
-While statement
----------------
-
-The while statement is a simple looping construct:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  echo "What's your name? "
-  var name = readLine(stdin)
-  while name == "":
-    echo "Please tell me your name: "
-    name = readLine(stdin)
-    # no ``var``, because we do not declare a new variable here
-
-The example uses a while loop to keep asking the users for their name, as long
-as the user types in nothing (only presses RETURN).
-
-
-For statement
--------------
-
-The ``for`` statement is a construct to loop over any element an *iterator*
-provides. The example uses the built-in `countup <system.html#countup>`_
-iterator:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  echo "Counting to ten: "
-  for i in countup(1, 10):
-    echo i
-  # --> Outputs 1 2 3 4 5 6 7 8 9 10 on different lines
-
-The variable ``i`` is implicitly declared by the
-``for`` loop and has the type ``int``, because that is what `countup
-<system.html#countup>`_ returns. ``i`` runs through the values 1, 2, .., 10.
-Each value is ``echo``-ed. This code does the same:
-
-.. code-block:: nim
-  echo "Counting to 10: "
-  var i = 1
-  while i <= 10:
-    echo i
-    inc(i) # increment i by 1
-  # --> Outputs 1 2 3 4 5 6 7 8 9 10 on different lines
-
-Counting down can be achieved as easily (but is less often needed):
-
-.. code-block:: nim
-  echo "Counting down from 10 to 1: "
-  for i in countdown(10, 1):
-    echo i
-  # --> Outputs 10 9 8 7 6 5 4 3 2 1 on different lines
-
-Since counting up occurs so often in programs, Nim also has a `..
-<system.html#...i,S,T>`_ iterator that does the same:
-
-.. code-block:: nim
-  for i in 1..10:
-    ...
-
-Zero-indexed counting have two shortcuts ``..<`` and ``..^`` to simplify counting to one less than the higher index:
-
-.. code-block:: nim
-  for i in 0..<10:
-    ...  # 0..9
-
-or
-
-.. code-block:: nim
-  var s = "some string"
-  for i in 0..<s.len:
-    ...
-
-Other useful iterators for collections (like arrays and sequences) are
-* ``items`` and ``mitems``, which provides immutable and mutable elements respectively, and
-* ``pairs`` and ``mpairs`` which provides the element and an index number (immutable and mutable respectively)
-
-.. code-block:: nim
-    :test: "nim c $1"
-  for index, item in ["a","b"].pairs:
-    echo item, " at index ", index
-  # => a at index 0
-  # => b at index 1
-
-Scopes and the block statement
-------------------------------
-Control flow statements have a feature not covered yet: they open a
-new scope. This means that in the following example, ``x`` is not accessible
-outside the loop:
-
-.. code-block:: nim
-    :test: "nim c $1"
-    :status: 1
-  while false:
-    var x = "hi"
-  echo x # does not work
-
-A while (for) statement introduces an implicit block. Identifiers
-are only visible within the block they have been declared. The ``block``
-statement can be used to open a new block explicitly:
-
-.. code-block:: nim
-    :test: "nim c $1"
-    :status: 1
-  block myblock:
-    var x = "hi"
-  echo x # does not work either
-
-The block's *label* (``myblock`` in the example) is optional.
-
-
-Break statement
----------------
-A block can be left prematurely with a ``break`` statement. The break statement
-can leave a ``while``, ``for``, or a ``block`` statement. It leaves the
-innermost construct, unless a label of a block is given:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  block myblock:
-    echo "entering block"
-    while true:
-      echo "looping"
-      break # leaves the loop, but not the block
-    echo "still in block"
-
-  block myblock2:
-    echo "entering block"
-    while true:
-      echo "looping"
-      break myblock2 # leaves the block (and the loop)
-    echo "still in block"
-
-
-Continue statement
-------------------
-Like in many other programming languages, a ``continue`` statement starts
-the next iteration immediately:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  while true:
-    let x = readLine(stdin)
-    if x == "": continue
-    echo x
-
-
-When statement
---------------
-
-Example:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  when system.hostOS == "windows":
-    echo "running on Windows!"
-  elif system.hostOS == "linux":
-    echo "running on Linux!"
-  elif system.hostOS == "macosx":
-    echo "running on Mac OS X!"
-  else:
-    echo "unknown operating system"
-
-The ``when`` statement is almost identical to the ``if`` statement, but with these
-differences:
-
-* Each condition must be a constant expression since it is evaluated by the
-  compiler.
-* The statements within a branch do not open a new scope.
-* The compiler checks the semantics and produces code *only* for the statements
-  that belong to the first condition that evaluates to ``true``.
-
-The ``when`` statement is useful for writing platform specific code, similar to
-the ``#ifdef`` construct in the C programming language.
-
-
-Statements and indentation
-==========================
-
-Now that we covered the basic control flow statements, let's return to Nim
-indentation rules.
-
-In Nim there is a distinction between *simple statements* and *complex
-statements*. *Simple statements* cannot contain other statements:
-Assignment, procedure calls or the ``return`` statement belong to the simple
-statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can
-contain other statements. To avoid ambiguities, complex statements must always
-be indented, but single simple statements do not:
-
-.. code-block:: nim
-  # no indentation needed for single assignment statement:
-  if x: x = false
-
-  # indentation needed for nested if statement:
-  if x:
-    if y:
-      y = false
-    else:
-      y = true
-
-  # indentation needed, because two statements follow the condition:
-  if x:
-    x = false
-    y = false
-
-
-*Expressions* are parts of a statement which usually result in a value. The
-condition in an if statement is an example for an expression. Expressions can
-contain indentation at certain places for better readability:
-
-.. code-block:: nim
-
-  if thisIsaLongCondition() and
-      thisIsAnotherLongCondition(1,
-         2, 3, 4):
-    x = true
-
-As a rule of thumb, indentation within expressions is allowed after operators,
-an open parenthesis and after commas.
-
-With parenthesis and semicolons ``(;)`` you can use statements where only
-an expression is allowed:
-
-.. code-block:: nim
-  # computes fac(4) at compile time:
-    :test: "nim c $1"
-  const fac4 = (var x = 1; for i in 1..4: x *= i; x)
-
-
-Procedures
-==========
-
-To define new commands like `echo <system.html#echo>`_ and `readLine
-<system.html#readLine,File>`_ in the examples, the concept of a `procedure`
-is needed. (Some languages call them *methods* or *functions*.) In Nim new
-procedures are defined with the ``proc`` keyword:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  proc yes(question: string): bool =
-    echo question, " (y/n)"
-    while true:
-      case readLine(stdin)
-      of "y", "Y", "yes", "Yes": return true
-      of "n", "N", "no", "No": return false
-      else: echo "Please be clear: yes or no"
-
-  if yes("Should I delete all your important files?"):
-    echo "I'm sorry Dave, I'm afraid I can't do that."
-  else:
-    echo "I think you know what the problem is just as well as I do."
-
-This example shows a procedure named ``yes`` that asks the user a ``question``
-and returns true if they answered "yes" (or something similar) and returns
-false if they answered "no" (or something similar). A ``return`` statement
-leaves the procedure (and therefore the while loop) immediately. The
-``(question: string): bool`` syntax describes that the procedure expects a
-parameter named ``question`` of type ``string`` and returns a value of type
-``bool``. The ``bool`` type is built-in: the only valid values for ``bool`` are
-``true`` and ``false``.
-The conditions in if or while statements must be of type ``bool``.
-
-Some terminology: in the example ``question`` is called a (formal) *parameter*,
-``"Should I..."`` is called an *argument* that is passed to this parameter.
-
-
-Result variable
----------------
-A procedure that returns a value has an implicit ``result`` variable declared
-that represents the return value. A ``return`` statement with no expression is a
-shorthand for ``return result``. The ``result`` value is always returned
-automatically at the end of a procedure if there is no ``return`` statement at
-the exit.
-
-.. code-block:: nim
-    :test: "nim c $1"
-  proc sumTillNegative(x: varargs[int]): int =
-    for i in x:
-      if i < 0:
-        return
-      result = result + i
-
-  echo sumTillNegative() # echos 0
-  echo sumTillNegative(3, 4, 5) # echos 12
-  echo sumTillNegative(3, 4 , -1 , 6) # echos 7
-
-The ``result`` variable is already implicitly declared at the start of the
-function, so declaring it again with 'var result', for example, would shadow it
-with a normal variable of the same name. The result variable is also already
-initialised with the type's default value. Note that referential data types will
-be ``nil`` at the start of the procedure, and thus may require manual
-initialisation.
-
-
-Parameters
-----------
-Parameters are immutable in the procedure body. By default, their value cannot be
-changed because this allows the compiler to implement parameter passing in the
-most efficient way. If a mutable variable is needed inside the procedure, it has
-to be declared with ``var`` in the procedure body. Shadowing the parameter name
-is possible, and actually an idiom:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  proc printSeq(s: seq, nprinted: int = -1) =
-    var nprinted = if nprinted == -1: s.len else: min(nprinted, s.len)
-    for i in 0 .. <nprinted:
-      echo s[i]
-
-If the procedure needs to modify the argument for the
-caller, a ``var`` parameter can be used:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  proc divmod(a, b: int; res, remainder: var int) =
-    res = a div b        # integer division
-    remainder = a mod b  # integer modulo operation
-
-  var
-    x, y: int
-  divmod(8, 5, x, y) # modifies x and y
-  echo x
-  echo y
-
-In the example, ``res`` and ``remainder`` are `var parameters`.
-Var parameters can be modified by the procedure and the changes are
-visible to the caller. Note that the above example would better make use of
-a tuple as a return value instead of using var parameters.
-
-
-Discard statement
------------------
-To call a procedure that returns a value just for its side effects and ignoring
-its return value, a ``discard`` statement **must** be used. Nim does not
-allow silently throwing away a return value:
-
-.. code-block:: nim
-  discard yes("May I ask a pointless question?")
-
-
-The return value can be ignored implicitly if the called proc/iterator has
-been declared with the ``discardable`` pragma:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  proc p(x, y: int): int {.discardable.} =
-    return x + y
-
-  p(3, 4) # now valid
-
-The ``discard`` statement can also be used to create block comments as
-described in the `Comments`_ section.
-
-
-Named arguments
----------------
-
-Often a procedure has many parameters and it is not clear in which order the
-parameters appear. This is especially true for procedures that construct a
-complex data type. Therefore the arguments to a procedure can be named, so
-that it is clear which argument belongs to which parameter:
-
-.. code-block:: nim
-  proc createWindow(x, y, width, height: int; title: string;
-                    show: bool): Window =
-     ...
-
-  var w = createWindow(show = true, title = "My Application",
-                       x = 0, y = 0, height = 600, width = 800)
-
-Now that we use named arguments to call ``createWindow`` the argument order
-does not matter anymore. Mixing named arguments with ordered arguments is
-also possible, but not very readable:
-
-.. code-block:: nim
-  var w = createWindow(0, 0, title = "My Application",
-                       height = 600, width = 800, true)
-
-The compiler checks that each parameter receives exactly one argument.
-
-
-Default values
---------------
-To make the ``createWindow`` proc easier to use it should provide `default
-values`; these are values that are used as arguments if the caller does not
-specify them:
-
-.. code-block:: nim
-  proc createWindow(x = 0, y = 0, width = 500, height = 700,
-                    title = "unknown",
-                    show = true): Window =
-     ...
-
-  var w = createWindow(title = "My Application", height = 600, width = 800)
-
-Now the call to ``createWindow`` only needs to set the values that differ
-from the defaults.
-
-Note that type inference works for parameters with default values; there is
-no need to write ``title: string = "unknown"``, for example.
-
-
-Overloaded procedures
----------------------
-Nim provides the ability to overload procedures similar to C++:
-
-.. code-block:: nim
-  proc toString(x: int): string = ...
-  proc toString(x: bool): string =
-    if x: result = "true"
-    else: result = "false"
-
-  echo toString(13)   # calls the toString(x: int) proc
-  echo toString(true) # calls the toString(x: bool) proc
-
-(Note that ``toString`` is usually the `$ <system.html#$>`_ operator in
-Nim.) The compiler chooses the most appropriate proc for the ``toString``
-calls. How this overloading resolution algorithm works exactly is not
-discussed here (it will be specified in the manual soon).  However, it does
-not lead to nasty surprises and is based on a quite simple unification
-algorithm. Ambiguous calls are reported as errors.
-
-
-Operators
----------
-The Nim library makes heavy use of overloading - one reason for this is that
-each operator like ``+`` is just an overloaded proc. The parser lets you
-use operators in `infix notation` (``a + b``) or `prefix notation` (``+ a``).
-An infix operator always receives two arguments, a prefix operator always one.
-(Postfix operators are not possible, because this would be ambiguous: does
-``a @ @ b`` mean ``(a) @ (@b)`` or ``(a@) @ (b)``? It always means
-``(a) @ (@b)``, because there are no postfix operators in Nim.)
-
-Apart from a few built-in keyword operators such as ``and``, ``or``, ``not``,
-operators always consist of these characters:
-``+  -  *  \  /  <  >  =  @  $  ~  &  %  !  ?  ^  .  |``
-
-User defined operators are allowed. Nothing stops you from defining your own
-``@!?+~`` operator, but doing so may reduce readability.
-
-The operator's precedence is determined by its first character. The details
-can be found in the manual.
-
-To define a new operator enclose the operator in backticks "``":
-
-.. code-block:: nim
-  proc `$` (x: myDataType): string = ...
-  # now the $ operator also works with myDataType, overloading resolution
-  # ensures that $ works for built-in types just like before
-
-The "``" notation can also be used to call an operator just like any other
-procedure:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  if `==`( `+`(3, 4), 7): echo "True"
-
-
-Forward declarations
---------------------
-
-Every variable, procedure, etc. needs to be declared before it can be used.
-(The reason for this is that it is non-trivial to avoid this need in a
-language that supports meta programming as extensively as Nim does.)
-However, this cannot be done for mutually recursive procedures:
-
-.. code-block:: nim
-  # forward declaration:
-  proc even(n: int): bool
-
-.. code-block:: nim
-  proc odd(n: int): bool =
-    assert(n >= 0) # makes sure we don't run into negative recursion
-    if n == 0: false
-    else:
-      n == 1 or even(n-1)
-
-  proc even(n: int): bool =
-    assert(n >= 0) # makes sure we don't run into negative recursion
-    if n == 1: false
-    else:
-      n == 0 or odd(n-1)
-
-Here ``odd`` depends on ``even`` and vice versa. Thus ``even`` needs to be
-introduced to the compiler before it is completely defined. The syntax for
-such a forward declaration is simple: just omit the ``=`` and the
-procedure's body. The ``assert`` just adds border conditions, and will be
-covered later in `Modules`_ section.
-
-Later versions of the language will weaken the requirements for forward
-declarations.
-
-The example also shows that a proc's body can consist of a single expression
-whose value is then returned implicitly.
-
-
-Iterators
-=========
-
-Let's return to the simple counting example:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  echo "Counting to ten: "
-  for i in countup(1, 10):
-    echo i
-
-Can a `countup <system.html#countup>`_ proc be written that supports this
-loop? Lets try:
-
-.. code-block:: nim
-  proc countup(a, b: int): int =
-    var res = a
-    while res <= b:
-      return res
-      inc(res)
-
-However, this does not work. The problem is that the procedure should not
-only ``return``, but return and **continue** after an iteration has
-finished. This *return and continue* is called a `yield` statement. Now
-the only thing left to do is to replace the ``proc`` keyword by ``iterator``
-and here it is - our first iterator:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  iterator countup(a, b: int): int =
-    var res = a
-    while res <= b:
-      yield res
-      inc(res)
-
-Iterators look very similar to procedures, but there are several
-important differences:
-
-* Iterators can only be called from for loops.
-* Iterators cannot contain a ``return`` statement (and procs cannot contain a
-  ``yield`` statement).
-* Iterators have no implicit ``result`` variable.
-* Iterators do not support recursion.
-* Iterators cannot be forward declared, because the compiler must be able
-  to inline an iterator. (This restriction will be gone in a
-  future version of the compiler.)
-
-However, you can also use a ``closure`` iterator to get a different set of
-restrictions. See `first class iterators <manual.html#first-class-iterators>`_
-for details. Iterators can have the same name and parameters as a proc, since
-essentially they have their own namespaces. Therefore it is common practice to
-wrap iterators in procs of the same name which accumulate the result of the
-iterator and return it as a sequence, like ``split`` from the `strutils module
-<strutils.html>`_.
-
-
-Basic types
-===========
-
-This section deals with the basic built-in types and the operations
-that are available for them in detail.
-
-Booleans
---------
-
-Nim's boolean type is called ``bool`` and consists of the two
-pre-defined values ``true`` and ``false``. Conditions in while,
-if, elif, and when statements must be of type bool.
-
-The operators ``not, and, or, xor, <, <=, >, >=, !=, ==`` are defined
-for the bool type. The ``and`` and ``or`` operators perform short-circuit
-evaluation. For example:
-
-.. code-block:: nim
-
-  while p != nil and p.name != "xyz":
-    # p.name is not evaluated if p == nil
-    p = p.next
-
-
-Characters
-----------
-The `character type` is called ``char``. Its size is always one byte, so
-it cannot represent most UTF-8 characters; but it *can* represent one of the bytes
-that makes up a multi-byte UTF-8 character.
-The reason for this is efficiency: for the overwhelming majority of use-cases,
-the resulting programs will still handle UTF-8 properly as UTF-8 was specially
-designed for this.
-Character literals are enclosed in single quotes.
-
-Chars can be compared with the ``==``, ``<``, ``<=``, ``>``, ``>=`` operators.
-The ``$`` operator converts a ``char`` to a ``string``. Chars cannot be mixed
-with integers; to get the ordinal value of a ``char`` use the ``ord`` proc.
-Converting from an integer to a ``char`` is done with the ``chr`` proc.
-
-
-Strings
--------
-String variables are **mutable**, so appending to a string
-is possible, and quite efficient. Strings in Nim are both zero-terminated and have a
-length field. A string's length can be retrieved with the builtin ``len``
-procedure; the length never counts the terminating zero. Accessing the
-terminating zero is an error, it only exists so that a Nim string can be converted
-to a ``cstring`` without doing a copy.
-
-The assignment operator for strings copies the string. You can use the ``&``
-operator to concatenate strings and ``add`` to append to a string.
-
-Strings are compared using their lexicographical order. All the comparison operators
-are supported. By convention, all strings are UTF-8 encoded, but this is not
-enforced. For example, when reading strings from binary files, they are merely
-a sequence of bytes. The index operation ``s[i]`` means the i-th *char* of
-``s``, not the i-th *unichar*.
-
-A string variable is initialized with the empty string ``""``.
-
-
-Integers
---------
-Nim has these integer types built-in:
-``int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64``.
-
-The default integer type is ``int``. Integer literals can have a *type suffix*
-to specify a non-default integer type:
-
-
-.. code-block:: nim
-    :test: "nim c $1"
-  let
-    x = 0     # x is of type ``int``
-    y = 0'i8  # y is of type ``int8``
-    z = 0'i64 # z is of type ``int64``
-    u = 0'u   # u is of type ``uint``
-
-Most often integers are used for counting objects that reside in memory, so
-``int`` has the same size as a pointer.
-
-The common operators ``+ - * div mod  <  <=  ==  !=  >  >=`` are defined for
-integers. The ``and or xor not`` operators are also defined for integers, and
-provide *bitwise* operations. Left bit shifting is done with the ``shl``, right
-shifting with the ``shr`` operator. Bit shifting operators always treat their
-arguments as *unsigned*. For `arithmetic bit shifts`:idx: ordinary
-multiplication or division can be used.
-
-Unsigned operations all wrap around; they cannot lead to over- or under-flow
-errors.
-
-Lossless `Automatic type conversion`:idx: is performed in expressions where different
-kinds of integer types are used. However, if the type conversion
-would cause loss of information, the `EOutOfRange`:idx: exception is raised (if the error
-cannot be detected at compile time).
-
-
-Floats
-------
-Nim has these floating point types built-in: ``float float32 float64``.
-
-The default float type is ``float``. In the current implementation,
-``float`` is always 64-bits.
-
-Float literals can have a *type suffix* to specify a non-default float
-type:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  var
-    x = 0.0      # x is of type ``float``
-    y = 0.0'f32  # y is of type ``float32``
-    z = 0.0'f64  # z is of type ``float64``
-
-The common operators ``+ - * /  <  <=  ==  !=  >  >=`` are defined for
-floats and follow the IEEE-754 standard.
-
-Automatic type conversion in expressions with different kinds of floating
-point types is performed: the smaller type is converted to the larger. Integer
-types are **not** converted to floating point types automatically, nor vice
-versa. Use the `toInt <system.html#toInt>`_ and `toFloat <system.html#toFloat>`_
-procs for these conversions.
-
-
-Type Conversion
----------------
-Conversion between numerical types is performed by using the
-type as a function:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  var
-    x: int32 = 1.int32   # same as calling int32(1)
-    y: int8  = int8('a') # 'a' == 97'i8
-    z: float = 2.5       # int(2.5) rounds down to 2
-    sum: int = int(x) + int(y) + int(z) # sum == 100
-
-
-Internal type representation
-============================
-
-As mentioned earlier, the built-in `$ <system.html#$>`_ (stringify) operator
-turns any basic type into a string, which you can then print to the console
-using the ``echo`` proc. However, advanced types, and your own custom types,
-won't work with the ``$`` operator until you define it for them.
-Sometimes you just want to debug the current value of a complex type without
-having to write its ``$`` operator.  You can use then the `repr
-<system.html#repr>`_ proc which works with any type and even complex data
-graphs with cycles. The following example shows that even for basic types
-there is a difference between the ``$`` and ``repr`` outputs:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  var
-    myBool = true
-    myCharacter = 'n'
-    myString = "nim"
-    myInteger = 42
-    myFloat = 3.14
-  echo myBool, ":", repr(myBool)
-  # --> true:true
-  echo myCharacter, ":", repr(myCharacter)
-  # --> n:'n'
-  echo myString, ":", repr(myString)
-  # --> nim:0x10fa8c050"nim"
-  echo myInteger, ":", repr(myInteger)
-  # --> 42:42
-  echo myFloat, ":", repr(myFloat)
-  # --> 3.1400000000000001e+00:3.1400000000000001e+00
-
-
-Advanced types
-==============
-
-In Nim new types can be defined within a ``type`` statement:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  type
-    biggestInt = int64      # biggest integer type that is available
-    biggestFloat = float64  # biggest float type that is available
-
-Enumeration and object types may only be defined within a
-``type`` statement.
-
-
-Enumerations
-------------
-A variable of an enumeration type can only be assigned one of the enumeration's specified values.
-These values are a set of ordered symbols. Each symbol is mapped
-to an integer value internally. The first symbol is represented
-at runtime by 0, the second by 1 and so on. For example:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  type
-    Direction = enum
-      north, east, south, west
-
-  var x = south     # `x` is of type `Direction`; its value is `south`
-  echo x            # writes "south" to `stdout`
-
-All the comparison operators can be used with enumeration types.
-
-An enumeration's symbol can be qualified to avoid ambiguities:
-``Direction.south``.
-
-The ``$`` operator can convert any enumeration value to its name, and the ``ord``
-proc can convert it to its underlying integer value.
-
-For better interfacing to other programming languages, the symbols of enum
-types can be assigned an explicit ordinal value. However, the ordinal values
-must be in ascending order. A symbol whose ordinal value is not
-explicitly given is assigned the value of the previous symbol + 1.
-
-An explicit ordered enum can have *holes*:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  type
-    MyEnum = enum
-      a = 2, b = 4, c = 89
-
-
-Ordinal types
--------------
-Enumerations without holes, integer types, ``char`` and ``bool`` (and
-subranges) are called ordinal types. Ordinal types have quite
-a few special operations:
-
------------------     --------------------------------------------------------
-Operation             Comment
------------------     --------------------------------------------------------
-``ord(x)``            returns the integer value that is used to
-                      represent `x`'s value
-``inc(x)``            increments `x` by one
-``inc(x, n)``         increments `x` by `n`; `n` is an integer
-``dec(x)``            decrements `x` by one
-``dec(x, n)``         decrements `x` by `n`; `n` is an integer
-``succ(x)``           returns the successor of `x`
-``succ(x, n)``        returns the `n`'th successor of `x`
-``pred(x)``           returns the predecessor of `x`
-``pred(x, n)``        returns the `n`'th predecessor of `x`
------------------     --------------------------------------------------------
-
-The `inc <system.html#inc>`_, `dec <system.html#dec>`_, `succ
-<system.html#succ>`_ and `pred <system.html#pred>`_ operations can fail by
-raising an `EOutOfRange` or `EOverflow` exception. (If the code has been
-compiled with the proper runtime checks turned on.)
-
-
-Subranges
----------
-A subrange type is a range of values from an integer or enumeration type
-(the base type). Example:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  type
-    MySubrange = range[0..5]
-
-
-``MySubrange`` is a subrange of ``int`` which can only hold the values 0
-to 5. Assigning any other value to a variable of type ``MySubrange`` is a
-compile-time or runtime error. Assignments from the base type to one of its
-subrange types (and vice versa) are allowed.
-
-The ``system`` module defines the important `Natural <system.html#Natural>`_
-type as ``range[0..high(int)]`` (`high <system.html#high>`_ returns the
-maximal value). Other programming languages may suggest the use of unsigned
-integers for natural numbers. This is often **unwise**: you don't want unsigned
-arithmetic (which wraps around) just because the numbers cannot be negative.
-Nim's ``Natural`` type helps to avoid this common programming error.
-
-
-Sets
-----
-
-.. include:: sets_fragment.txt
-
-Arrays
-------
-An array is a simple fixed length container. Each element in
-an array has the same type. The array's index type can be any ordinal type.
-
-Arrays can be constructed using ``[]``:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  type
-    IntArray = array[0..5, int] # an array that is indexed with 0..5
-  var
-    x: IntArray
-  x = [1, 2, 3, 4, 5, 6]
-  for i in low(x)..high(x):
-    echo x[i]
-
-The notation ``x[i]`` is used to access the i-th element of ``x``.
-Array access is always bounds checked (at compile-time or at runtime). These
-checks can be disabled via pragmas or invoking the compiler with the
-``--bound_checks:off`` command line switch.
-
-Arrays are value types, like any other Nim type. The assignment operator
-copies the whole array contents.
-
-The built-in `len <system.html#len,TOpenArray>`_ proc returns the array's
-length. `low(a) <system.html#low>`_ returns the lowest valid index for the
-array `a` and `high(a) <system.html#high>`_ the highest valid index.
-
-.. code-block:: nim
-    :test: "nim c $1"
-  type
-    Direction = enum
-      north, east, south, west
-    BlinkLights = enum
-      off, on, slowBlink, mediumBlink, fastBlink
-    LevelSetting = array[north..west, BlinkLights]
-  var
-    level: LevelSetting
-  level[north] = on
-  level[south] = slowBlink
-  level[east] = fastBlink
-  echo repr(level)  # --> [on, fastBlink, slowBlink, off]
-  echo low(level)   # --> north
-  echo len(level)   # --> 4
-  echo high(level)  # --> west
-
-The syntax for nested arrays (multidimensional) in other languages is a matter
-of appending more brackets because usually each dimension is restricted to the
-same index type as the others. In Nim you can have different dimensions with
-different index types, so the nesting syntax is slightly different. Building on
-the previous example where a level is defined as an array of enums indexed by
-yet another enum, we can add the following lines to add a light tower type
-subdivided in height levels accessed through their integer index:
-
-.. code-block:: nim
-  type
-    LightTower = array[1..10, LevelSetting]
-  var
-    tower: LightTower
-  tower[1][north] = slowBlink
-  tower[1][east] = mediumBlink
-  echo len(tower)     # --> 10
-  echo len(tower[1])  # --> 4
-  echo repr(tower)    # --> [[slowBlink, mediumBlink, ...more output..
-  # The following lines don't compile due to type mismatch errors
-  #tower[north][east] = on
-  #tower[0][1] = on
-
-Note how the built-in ``len`` proc returns only the array's first dimension
-length.  Another way of defining the ``LightTower`` to better illustrate its
-nested nature would be to omit the previous definition of the ``LevelSetting``
-type and instead write it embedded directly as the type of the first dimension:
-
-.. code-block:: nim
-  type
-    LightTower = array[1..10, array[north..west, BlinkLights]]
-
-It is quite common to have arrays start at zero, so there's a shortcut syntax
-to specify a range from zero to the specified index minus one:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  type
-    IntArray = array[0..5, int] # an array that is indexed with 0..5
-    QuickArray = array[6, int]  # an array that is indexed with 0..5
-  var
-    x: IntArray
-    y: QuickArray
-  x = [1, 2, 3, 4, 5, 6]
-  y = x
-  for i in low(x)..high(x):
-    echo x[i], y[i]
-
-
-Sequences
----------
-Sequences are similar to arrays but of dynamic length which may change
-during runtime (like strings). Since sequences are resizable they are always
-allocated on the heap and garbage collected.
-
-Sequences are always indexed with an ``int`` starting at position 0.  The `len
-<system.html#len,seq[T]>`_, `low <system.html#low>`_ and `high
-<system.html#high>`_ operations are available for sequences too.  The notation
-``x[i]`` can be used to access the i-th element of ``x``.
-
-Sequences can be constructed by the array constructor ``[]`` in conjunction
-with the array to sequence operator ``@``. Another way to allocate space for
-a sequence is to call the built-in `newSeq <system.html#newSeq>`_ procedure.
-
-A sequence may be passed to an openarray parameter.
-
-Example:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  var
-    x: seq[int] # a reference to a sequence of integers
-  x = @[1, 2, 3, 4, 5, 6] # the @ turns the array into a sequence allocated on the heap
-
-Sequence variables are initialized with ``@[]``.
-
-The ``for`` statement can be used with one or two variables when used with a
-sequence. When you use the one variable form, the variable will hold the value
-provided by the sequence. The ``for`` statement is looping over the results
-from the `items() <system.html#items.i,seq[T]>`_ iterator from the `system
-<system.html>`_ module.  But if you use the two variable form, the first
-variable will hold the index position and the second variable will hold the
-value. Here the ``for`` statement is looping over the results from the
-`pairs() <system.html#pairs.i,seq[T]>`_ iterator from the `system
-<system.html>`_ module.  Examples:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  for value in @[3, 4, 5]:
-    echo value
-  # --> 3
-  # --> 4
-  # --> 5
-
-  for i, value in @[3, 4, 5]:
-    echo "index: ", $i, ", value:", $value
-  # --> index: 0, value:3
-  # --> index: 1, value:4
-  # --> index: 2, value:5
-
-
-Open arrays
------------
-**Note**: Openarrays can only be used for parameters.
-
-Often fixed size arrays turn out to be too inflexible; procedures should be
-able to deal with arrays of different sizes. The `openarray`:idx: type allows
-this. Openarrays are always indexed with an ``int`` starting at position 0.
-The `len <system.html#len,TOpenArray>`_, `low <system.html#low>`_ and `high
-<system.html#high>`_ operations are available for open arrays too.  Any array
-with a compatible base type can be passed to an openarray parameter, the index
-type does not matter.
-
-.. code-block:: nim
-    :test: "nim c $1"
-  var
-    fruits:   seq[string]       # reference to a sequence of strings that is initialized with '@[]'
-    capitals: array[3, string]  # array of strings with a fixed size
-
-  capitals = ["New York", "London", "Berlin"]   # array 'capitals' allows assignment of only three elements
-  fruits.add("Banana")          # sequence 'fruits' is dynamically expandable during runtime
-  fruits.add("Mango")
-
-  proc openArraySize(oa: openArray[string]): int =
-    oa.len
-
-  assert openArraySize(fruits) == 2     # procedure accepts a sequence as parameter
-  assert openArraySize(capitals) == 3   # but also an array type
-
-The openarray type cannot be nested: multidimensional openarrays are not
-supported because this is seldom needed and cannot be done efficiently.
-
-
-Varargs
--------
-
-A ``varargs`` parameter is like an openarray parameter. However, it is
-also a means to implement passing a variable number of
-arguments to a procedure. The compiler converts the list of arguments
-to an array automatically:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  proc myWriteln(f: File, a: varargs[string]) =
-    for s in items(a):
-      write(f, s)
-    write(f, "\n")
-
-  myWriteln(stdout, "abc", "def", "xyz")
-  # is transformed by the compiler to:
-  myWriteln(stdout, ["abc", "def", "xyz"])
-
-This transformation is only done if the varargs parameter is the
-last parameter in the procedure header. It is also possible to perform
-type conversions in this context:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  proc myWriteln(f: File, a: varargs[string, `$`]) =
-    for s in items(a):
-      write(f, s)
-    write(f, "\n")
-
-  myWriteln(stdout, 123, "abc", 4.0)
-  # is transformed by the compiler to:
-  myWriteln(stdout, [$123, $"abc", $4.0])
-
-In this example `$ <system.html#$>`_ is applied to any argument that is passed
-to the parameter ``a``. Note that `$ <system.html#$>`_ applied to strings is a
-nop.
-
-
-Slices
-------
-
-Slices look similar to subranges types in syntax but are used in a different
-context. A slice is just an object of type Slice which contains two bounds,
-`a` and `b`. By itself a slice is not very useful, but other collection types
-define operators which accept Slice objects to define ranges.
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  var
-    a = "Nim is a progamming language"
-    b = "Slices are useless."
-
-  echo a[7..12] # --> 'a prog'
-  b[11..^2] = "useful"
-  echo b # --> 'Slices are useful.'
-
-In the previous example slices are used to modify a part of a string. The
-slice's bounds can hold any value supported by
-their type, but it is the proc using the slice object which defines what values
-are accepted.
-
-To understand some of the different ways of specifying the indices of
-strings, arrays, sequences, etc., it must be remembered that Nim uses
-zero-based indices.
-
-So the string ``b`` is of length 19, and two different ways of specifying the
-indices are
-
-.. code-block:: nim
-
-  "Slices are useless."
-   |          |     |
-   0         11    17   using indices
-  ^19        ^8    ^2   using ^ syntax
-
-where ``b[0..^1]`` is equivalent to ``b[0..b.len-1]`` and ``b[0..<b.len]``, and it
-can be seen that the ``^1`` provides a short-hand way of specifying the ``b.len-1``.
-
-In the above example, because the string ends in a period, to get the portion of the
-string that is "useless" and replace it with "useful".
-
-``b[11..^2]`` is the portion "useless", and ``b[11..^2] = "useful"`` replaces the
-"useless" portion with "useful", giving the result "Slices are useful."
-
-Note: alternate ways of writing this are ``b[^8..^2] = "useful"`` or
-as ``b[11..b.len-2] = "useful"`` or as ``b[11..<b.len-1] = "useful"``.
-
-Tuples
-------
-
-A tuple type defines various named *fields* and an *order* of the fields.
-The constructor ``()`` can be used to construct tuples. The order of the
-fields in the constructor must match the order in the tuple's definition.
-Different tuple-types are *equivalent* if they specify fields of
-the same type and of the same name in the same order.
-
-The assignment operator for tuples copies each component. The notation
-``t.field`` is used to access a tuple's field. Another notation is
-``t[i]`` to access the ``i``'th field. Here ``i`` must be a constant
-integer.
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  type
-    Person = tuple[name: string, age: int] # type representing a person:
-                                           # a person consists of a name
-                                           # and an age
-  var
-    person: Person
-  person = (name: "Peter", age: 30)
-  # the same, but less readable:
-  person = ("Peter", 30)
-
-  echo person.name # "Peter"
-  echo person.age  # 30
-
-  echo person[0] # "Peter"
-  echo person[1] # 30
-
-  # You don't need to declare tuples in a separate type section.
-  var building: tuple[street: string, number: int]
-  building = ("Rue del Percebe", 13)
-  echo building.street
-
-  # The following line does not compile, they are different tuples!
-  #person = building
-  # --> Error: type mismatch: got (tuple[street: string, number: int])
-  #     but expected 'Person'
-
-  # The following works because the field names and types are the same.
-  var teacher: tuple[name: string, age: int] = ("Mark", 42)
-  person = teacher
-
-Even though you don't need to declare a type for a tuple to use it, tuples
-created with different field names will be considered different objects despite
-having the same field types.
-
-Tuples can be *unpacked* during variable assignment (and only then!). This can
-be handy to assign directly the fields of the tuples to individually named
-variables. An example of this is the `splitFile <os.html#splitFile>`_ proc
-from the `os module <os.html>`_ which returns the directory, name and
-extension of a path at the same time. For tuple unpacking to work you must
-use parentheses around the values you want to assign the unpacking to,
-otherwise you will be assigning the same value to all the individual
-variables! For example:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  import os
-
-  let
-    path = "usr/local/nimc.html"
-    (dir, name, ext) = splitFile(path)
-    baddir, badname, badext = splitFile(path)
-  echo dir      # outputs `usr/local`
-  echo name     # outputs `nimc`
-  echo ext      # outputs `.html`
-  # All the following output the same line:
-  # `(dir: usr/local, name: nimc, ext: .html)`
-  echo baddir
-  echo badname
-  echo badext
-
-
-Reference and pointer types
----------------------------
-References (similar to pointers in other programming languages) are a
-way to introduce many-to-one relationships. This means different references can
-point to and modify the same location in memory.
-
-Nim distinguishes between `traced`:idx: and `untraced`:idx: references.
-Untraced references are also called *pointers*. Traced references point to
-objects in a garbage collected heap, untraced references point to
-manually allocated objects or to objects elsewhere in memory. Thus
-untraced references are *unsafe*. However for certain low-level operations
-(e.g., accessing the hardware), untraced references are necessary.
-
-Traced references are declared with the **ref** keyword; untraced references
-are declared with the **ptr** keyword.
-
-The empty ``[]`` subscript notation can be used to *derefer* a reference,
-meaning to retrieve the item the reference points to. The ``.`` (access a
-tuple/object field operator) and ``[]`` (array/string/sequence index operator)
-operators perform implicit dereferencing operations for reference types:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  type
-    Node = ref object
-      le, ri: Node
-      data: int
-  var
-    n: Node
-  new(n)
-  n.data = 9
-  # no need to write n[].data; in fact n[].data is highly discouraged!
-
-To allocate a new traced object, the built-in procedure ``new`` must be used.
-To deal with untraced memory, the procedures ``alloc``, ``dealloc`` and
-``realloc`` can be used. The `system <system.html>`_
-module's documentation contains further details.
-
-If a reference points to *nothing*, it has the value ``nil``.
-
-
-Procedural type
----------------
-A procedural type is a (somewhat abstract) pointer to a procedure.
-``nil`` is an allowed value for a variable of a procedural type.
-Nim uses procedural types to achieve `functional`:idx: programming
-techniques.
-
-Example:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  proc echoItem(x: int) = echo x
-
-  proc forEach(action: proc (x: int)) =
-    const
-      data = [2, 3, 5, 7, 11]
-    for d in items(data):
-      action(d)
-
-  forEach(echoItem)
-
-A subtle issue with procedural types is that the calling convention of the
-procedure influences the type compatibility: procedural types are only compatible
-if they have the same calling convention. The different calling conventions are
-listed in the `manual <manual.html#types-procedural-type>`_.
-
-Distinct type
--------------
-A Distinct type allows for the creation of new type that "does not imply a
-subtype relationship between it and its base type".
-You must **explicitly** define all behaviour for the distinct type.
-To help with this, both the distinct type and its base type can cast from one
-type to the other.
-Examples are provided in the `manual <manual.html#types-distinct-type>`_.
-
-Modules
-=======
-Nim supports splitting a program into pieces with a module concept.
-Each module is in its own file. Modules enable `information hiding`:idx: and
-`separate compilation`:idx:. A module may gain access to the symbols of another
-module by using the `import`:idx: statement. Only top-level symbols that are marked
-with an asterisk (``*``) are exported:
-
-.. code-block:: nim
-  # Module A
-  var
-    x*, y: int
-
-  proc `*` *(a, b: seq[int]): seq[int] =
-    # allocate a new sequence:
-    newSeq(result, len(a))
-    # multiply two int sequences:
-    for i in 0..len(a)-1: result[i] = a[i] * b[i]
-
-  when isMainModule:
-    # test the new ``*`` operator for sequences:
-    assert(@[1, 2, 3] * @[1, 2, 3] == @[1, 4, 9])
-
-The above module exports ``x`` and ``*``, but not ``y``.
-
-A module's top-level statements are executed at the start of the program.
-This can be used to initialize complex data structures for example.
-
-Each module has a special magic constant ``isMainModule`` that is true if the
-module is compiled as the main file. This is very useful to embed tests within
-the module as shown by the above example.
-
-A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. And if
-a symbol is ambiguous, it *must* be qualified. A symbol is ambiguous
-if it is defined in two (or more) different modules and both modules are
-imported by a third one:
-
-.. code-block:: nim
-  # Module A
-  var x*: string
-
-.. code-block:: nim
-  # Module B
-  var x*: int
-
-.. code-block:: nim
-  # Module C
-  import A, B
-  write(stdout, x) # error: x is ambiguous
-  write(stdout, A.x) # okay: qualifier used
-
-  var x = 4
-  write(stdout, x) # not ambiguous: uses the module C's x
-
-
-But this rule does not apply to procedures or iterators. Here the overloading
-rules apply:
-
-.. code-block:: nim
-  # Module A
-  proc x*(a: int): string = $a
-
-.. code-block:: nim
-  # Module B
-  proc x*(a: string): string = $a
-
-.. code-block:: nim
-  # Module C
-  import A, B
-  write(stdout, x(3))   # no error: A.x is called
-  write(stdout, x(""))  # no error: B.x is called
-
-  proc x*(a: int): string = discard
-  write(stdout, x(3))   # ambiguous: which `x` is to call?
-
-
-Excluding symbols
------------------
-
-The normal ``import`` statement will bring in all exported symbols.
-These can be limited by naming symbols which should be excluded with
-the ``except`` qualifier.
-
-.. code-block:: nim
-  import mymodule except y
-
-
-From statement
---------------
-
-We have already seen the simple ``import`` statement that just imports all
-exported symbols. An alternative that only imports listed symbols is the
-``from import`` statement:
-
-.. code-block:: nim
-  from mymodule import x, y, z
-
-The ``from`` statement can also force namespace qualification on
-symbols, thereby making symbols available, but needing to be qualified
-to be used.
-
-.. code-block:: nim
-  from mymodule import x, y, z
-
-  x()           # use x without any qualification
-
-.. code-block:: nim
-  from mymodule import nil
-
-  mymodule.x()  # must qualify x with the module name as prefix
-
-  x()           # using x here without qualification is a compile error
-
-Since module names are generally long to be descriptive, you can also
-define a shorter alias to use when qualifying symbols.
-
-.. code-block:: nim
-  from mymodule as m import nil
-
-  m.x()         # m is aliasing mymodule
-
-
-Include statement
------------------
-The ``include`` statement does something fundamentally different than
-importing a module: it merely includes the contents of a file. The ``include``
-statement is useful to split up a large module into several files:
-
-.. code-block:: nim
-  include fileA, fileB, fileC
-
-
-
-Part 2
-======
-
-So, now that we are done with the basics, let's see what Nim offers apart
-from a nice syntax for procedural programming: `Part II <tut2.html>`_
-
-
-.. _strutils: strutils.html
-.. _system: system.html
diff --git a/doc/tut2.md b/doc/tut2.md
new file mode 100644
index 000000000..1b59288d5
--- /dev/null
+++ b/doc/tut2.md
@@ -0,0 +1,697 @@
+======================
+Nim Tutorial (Part II)
+======================
+
+:Author: Andreas Rumpf
+:Version: |nimversion|
+
+.. default-role:: code
+.. include:: rstcommon.rst
+.. contents::
+
+
+Introduction
+============
+
+> "Repetition renders the ridiculous reasonable." -- Norman Wildberger
+
+This document is a tutorial for the advanced constructs of the *Nim*
+programming language. **Note that this document is somewhat obsolete as the**
+[manual](manual.html) **contains many more examples of the advanced language
+features.**
+
+
+Pragmas
+=======
+
+Pragmas are Nim's method to give the compiler additional information/
+commands without introducing a massive number of new keywords. Pragmas are
+enclosed in the special `{.` and `.}` curly dot brackets. This tutorial
+does not cover pragmas. See the [manual](manual.html#pragmas) or [user guide](
+nimc.html#additional-features) for a description of the available
+pragmas.
+
+
+Object Oriented Programming
+===========================
+
+While Nim's support for object oriented programming (OOP) is minimalistic,
+powerful OOP techniques can be used. OOP is seen as *one* way to design a
+program, not *the only* way. Often a procedural approach leads to simpler
+and more efficient code. In particular, preferring composition over inheritance
+is often the better design.
+
+
+Inheritance
+-----------
+
+Inheritance in Nim is entirely optional. To enable inheritance with
+runtime type information the object needs to inherit from
+`RootObj`.  This can be done directly, or indirectly by
+inheriting from an object that inherits from `RootObj`.  Usually
+types with inheritance are also marked as `ref` types even though
+this isn't strictly enforced. To check at runtime if an object is of a certain
+type, the `of` operator can be used.
+
+  ```nim  test = "nim c $1"
+  type
+    Person = ref object of RootObj
+      name*: string  # the * means that `name` is accessible from other modules
+      age: int       # no * means that the field is hidden from other modules
+
+    Student = ref object of Person # Student inherits from Person
+      id: int                      # with an id field
+
+  var
+    student: Student
+    person: Person
+  assert(student of Student) # is true
+  # object construction:
+  student = Student(name: "Anton", age: 5, id: 2)
+  echo student[]
+  ```
+
+Inheritance is done with the `object of` syntax. Multiple inheritance is
+currently not supported. If an object type has no suitable ancestor, `RootObj`
+can be used as its ancestor, but this is only a convention. Objects that have
+no ancestor are implicitly `final`. You can use the `inheritable` pragma
+to introduce new object roots apart from `system.RootObj`. (This is used
+in the GTK wrapper for instance.)
+
+Ref objects should be used whenever inheritance is used. It isn't strictly
+necessary, but with non-ref objects, assignments such as `let person: Person =
+Student(id: 123)` will truncate subclass fields.
+
+**Note**: Composition (*has-a* relation) is often preferable to inheritance
+(*is-a* relation) for simple code reuse. Since objects are value types in
+Nim, composition is as efficient as inheritance.
+
+
+Mutually recursive types
+------------------------
+
+Objects, tuples and references can model quite complex data structures which
+depend on each other; they are *mutually recursive*. In Nim
+these types can only be declared within a single type section. (Anything else
+would require arbitrary symbol lookahead which slows down compilation.)
+
+Example:
+
+  ```nim  test = "nim c $1"
+  type
+    Node = ref object  # a reference to an object with the following field:
+      le, ri: Node     # left and right subtrees
+      sym: ref Sym     # leaves contain a reference to a Sym
+
+    Sym = object       # a symbol
+      name: string     # the symbol's name
+      line: int        # the line the symbol was declared in
+      code: Node       # the symbol's abstract syntax tree
+  ```
+
+
+Type conversions
+----------------
+Nim distinguishes between `type casts`:idx: and `type conversions`:idx:.
+Casts are done with the `cast` operator and force the compiler to
+interpret a bit pattern to be of another type.
+
+Type conversions are a much more polite way to convert a type into another:
+They preserve the abstract *value*, not necessarily the *bit-pattern*. If a
+type conversion is not possible, the compiler complains or an exception is
+raised.
+
+The syntax for type conversions is `destination_type(expression_to_convert)`
+(like an ordinary call):
+
+  ```nim
+  proc getID(x: Person): int =
+    Student(x).id
+  ```
+
+The `InvalidObjectConversionDefect` exception is raised if `x` is not a
+`Student`.
+
+
+Object variants
+---------------
+Often an object hierarchy is overkill in certain situations where simple
+variant types are needed.
+
+An example:
+
+  ```nim  test = "nim c $1"
+  # This is an example how an abstract syntax tree could be modelled in Nim
+  type
+    NodeKind = enum  # the different node types
+      nkInt,          # a leaf with an integer value
+      nkFloat,        # a leaf with a float value
+      nkString,       # a leaf with a string value
+      nkAdd,          # an addition
+      nkSub,          # a subtraction
+      nkIf            # an if statement
+    Node = ref object
+      case kind: NodeKind  # the `kind` field is the discriminator
+      of nkInt: intVal: int
+      of nkFloat: floatVal: float
+      of nkString: strVal: string
+      of nkAdd, nkSub:
+        leftOp, rightOp: Node
+      of nkIf:
+        condition, thenPart, elsePart: Node
+
+  var n = Node(kind: nkFloat, floatVal: 1.0)
+  # the following statement raises an `FieldDefect` exception, because
+  # n.kind's value does not fit:
+  n.strVal = ""
+  ```
+
+As can be seen from the example, an advantage to an object hierarchy is that
+no conversion between different object types is needed. Yet, access to invalid
+object fields raises an exception.
+
+
+Method call syntax
+------------------
+
+There is a syntactic sugar for calling routines:
+The syntax `obj.methodName(args)` can be used
+instead of `methodName(obj, args)`.
+If there are no remaining arguments, the parentheses can be omitted:
+`obj.len` (instead of `len(obj)`).
+
+This method call syntax is not restricted to objects, it can be used
+for any type:
+
+  ```nim  test = "nim c $1"
+  import std/strutils
+
+  echo "abc".len # is the same as echo len("abc")
+  echo "abc".toUpperAscii()
+  echo({'a', 'b', 'c'}.card)
+  stdout.writeLine("Hallo") # the same as writeLine(stdout, "Hallo")
+  ```
+
+(Another way to look at the method call syntax is that it provides the missing
+postfix notation.)
+
+So "pure object oriented" code is easy to write:
+
+  ```nim  test = "nim c $1"
+  import std/[strutils, sequtils]
+
+  stdout.writeLine("Give a list of numbers (separated by spaces): ")
+  stdout.write(stdin.readLine.splitWhitespace.map(parseInt).max.`$`)
+  stdout.writeLine(" is the maximum!")
+  ```
+
+
+Properties
+----------
+As the above example shows, Nim has no need for *get-properties*:
+Ordinary get-procedures that are called with the *method call syntax* achieve
+the same. But setting a value is different; for this a special setter syntax
+is needed:
+
+  ```nim  test = "nim c $1"
+  type
+    Socket* = ref object of RootObj
+      h: int # cannot be accessed from the outside of the module due to missing star
+
+  proc `host=`*(s: var Socket, value: int) {.inline.} =
+    ## setter of host address
+    s.h = value
+
+  proc host*(s: Socket): int {.inline.} =
+    ## getter of host address
+    s.h
+
+  var s: Socket
+  new s
+  s.host = 34  # same as `host=`(s, 34)
+  ```
+
+(The example also shows `inline` procedures.)
+
+
+The `[]` array access operator can be overloaded to provide
+`array properties`:idx:\ :
+
+  ```nim  test = "nim c $1"
+  type
+    Vector* = object
+      x, y, z: float
+
+  proc `[]=`* (v: var Vector, i: int, value: float) =
+    # setter
+    case i
+    of 0: v.x = value
+    of 1: v.y = value
+    of 2: v.z = value
+    else: assert(false)
+
+  proc `[]`* (v: Vector, i: int): float =
+    # getter
+    case i
+    of 0: result = v.x
+    of 1: result = v.y
+    of 2: result = v.z
+    else: assert(false)
+  ```
+
+The example is silly, since a vector is better modelled by a tuple which
+already provides `v[]` access.
+
+
+Dynamic dispatch
+----------------
+
+Procedures always use static dispatch. For dynamic dispatch replace the
+`proc` keyword by `method`:
+
+  ```nim  test = "nim c $1"
+  type
+    Expression = ref object of RootObj ## abstract base class for an expression
+    Literal = ref object of Expression
+      x: int
+    PlusExpr = ref object of Expression
+      a, b: Expression
+
+  # watch out: 'eval' relies on dynamic binding
+  method eval(e: Expression): int {.base.} =
+    # override this base method
+    quit "to override!"
+
+  method eval(e: Literal): int = e.x
+  method eval(e: PlusExpr): int = eval(e.a) + eval(e.b)
+
+  proc newLit(x: int): Literal = Literal(x: x)
+  proc newPlus(a, b: Expression): PlusExpr = PlusExpr(a: a, b: b)
+
+  echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
+  ```
+
+Note that in the example the constructors `newLit` and `newPlus` are procs
+because it makes more sense for them to use static binding, but `eval` is a
+method because it requires dynamic binding.
+
+**Note:** Starting from Nim 0.20, to use multi-methods one must explicitly pass
+``--multimethods:on`` when compiling.
+
+In a multi-method all parameters that have an object type are used for the
+dispatching:
+
+  ```nim  test = "nim c --multiMethods:on $1"
+  type
+    Thing = ref object of RootObj
+    Unit = ref object of Thing
+      x: int
+
+  method collide(a, b: Thing) {.inline.} =
+    quit "to override!"
+
+  method collide(a: Thing, b: Unit) {.inline.} =
+    echo "1"
+
+  method collide(a: Unit, b: Thing) {.inline.} =
+    echo "2"
+
+  var a, b: Unit
+  new a
+  new b
+  collide(a, b) # output: 2
+  ```
+
+
+As the example demonstrates, invocation of a multi-method cannot be ambiguous:
+Collide 2 is preferred over collide 1 because the resolution works from left to
+right. Thus `Unit, Thing` is preferred over `Thing, Unit`.
+
+**Performance note**: Nim does not produce a virtual method table, but
+generates dispatch trees. This avoids the expensive indirect branch for method
+calls and enables inlining. However, other optimizations like compile time
+evaluation or dead code elimination do not work with methods.
+
+
+Exceptions
+==========
+
+In Nim exceptions are objects. By convention, exception types are
+suffixed with 'Error'. The [system](system.html) module defines an
+exception hierarchy that you might want to stick to. Exceptions derive from
+`system.Exception`, which provides the common interface.
+
+Exceptions have to be allocated on the heap because their lifetime is unknown.
+The compiler will prevent you from raising an exception created on the stack.
+All raised exceptions should at least specify the reason for being raised in
+the `msg` field.
+
+A convention is that exceptions should be raised in *exceptional* cases,
+they should not be used as an alternative method of control flow.
+
+Raise statement
+---------------
+Raising an exception is done with the `raise` statement:
+
+  ```nim  test = "nim c $1"
+  var
+    e: ref OSError
+  new(e)
+  e.msg = "the request to the OS failed"
+  raise e
+  ```
+
+If the `raise` keyword is not followed by an expression, the last exception
+is *re-raised*. For the purpose of avoiding repeating this common code pattern,
+the template `newException` in the `system` module can be used:
+
+  ```nim
+  raise newException(OSError, "the request to the OS failed")
+  ```
+
+
+Try statement
+-------------
+
+The `try` statement handles exceptions:
+
+  ```nim  test = "nim c $1"
+  from std/strutils import parseInt
+
+  # read the first two lines of a text file that should contain numbers
+  # and tries to add them
+  var
+    f: File
+  if open(f, "numbers.txt"):
+    try:
+      let a = readLine(f)
+      let b = readLine(f)
+      echo "sum: ", parseInt(a) + parseInt(b)
+    except OverflowDefect:
+      echo "overflow!"
+    except ValueError:
+      echo "could not convert string to integer"
+    except IOError:
+      echo "IO error!"
+    except CatchableError:
+      echo "Unknown exception!"
+      # reraise the unknown exception:
+      raise
+    finally:
+      close(f)
+  ```
+
+The statements after the `try` are executed unless an exception is
+raised. Then the appropriate `except` part is executed.
+
+The empty `except` part is executed if there is an exception that is
+not explicitly listed. It is similar to an `else` part in `if`
+statements.
+
+If there is a `finally` part, it is always executed after the
+exception handlers.
+
+The exception is *consumed* in an `except` part. If an exception is not
+handled, it is propagated through the call stack. This means that often
+the rest of the procedure - that is not within a `finally` clause -
+is not executed (if an exception occurs).
+
+If you need to *access* the actual exception object or message inside an
+`except` branch you can use the [getCurrentException()](
+system.html#getCurrentException) and [getCurrentExceptionMsg()](
+system.html#getCurrentExceptionMsg) procs from the [system](system.html)
+module. Example:
+
+  ```nim
+  try:
+    doSomethingHere()
+  except CatchableError:
+    let
+      e = getCurrentException()
+      msg = getCurrentExceptionMsg()
+    echo "Got exception ", repr(e), " with message ", msg
+  ```
+
+
+Annotating procs with raised exceptions
+---------------------------------------
+
+Through the use of the optional `{.raises.}` pragma you can specify that a
+proc is meant to raise a specific set of exceptions, or none at all. If the
+`{.raises.}` pragma is used, the compiler will verify that this is true. For
+instance, if you specify that a proc raises `IOError`, and at some point it
+(or one of the procs it calls) starts raising a new exception the compiler will
+prevent that proc from compiling. Usage example:
+
+  ```nim
+  proc complexProc() {.raises: [IOError, ArithmeticDefect].} =
+    ...
+
+  proc simpleProc() {.raises: [].} =
+    ...
+  ```
+
+Once you have code like this in place, if the list of raised exception changes
+the compiler will stop with an error specifying the line of the proc which
+stopped validating the pragma and the raised exception not being caught, along
+with the file and line where the uncaught exception is being raised, which may
+help you locate the offending code which has changed.
+
+If you want to add the `{.raises.}` pragma to existing code, the compiler can
+also help you. You can add the `{.effects.}` pragma statement to your proc and
+the compiler will output all inferred effects up to that point (exception
+tracking is part of Nim's effect system). Another more roundabout way to
+find out the list of exceptions raised by a proc is to use the Nim ``doc``
+command which generates documentation for a whole module and decorates all
+procs with the list of raised exceptions. You can read more about Nim's
+[effect system and related pragmas in the manual](manual.html#effect-system).
+
+
+Generics
+========
+
+Generics are Nim's means to parametrize procs, iterators or types
+with `type parameters`:idx:. Generic parameters are written within square
+brackets, for example `Foo[T]`. They are most useful for efficient type safe
+containers:
+
+  ```nim  test = "nim c $1"
+  type
+    BinaryTree*[T] = ref object # BinaryTree is a generic type with
+                                # generic param `T`
+      le, ri: BinaryTree[T]     # left and right subtrees; may be nil
+      data: T                   # the data stored in a node
+
+  proc newNode*[T](data: T): BinaryTree[T] =
+    # constructor for a node
+    new(result)
+    result.data = data
+
+  proc add*[T](root: var BinaryTree[T], n: BinaryTree[T]) =
+    # insert a node into the tree
+    if root == nil:
+      root = n
+    else:
+      var it = root
+      while it != nil:
+        # compare the data items; uses the generic `cmp` proc
+        # that works for any type that has a `==` and `<` operator
+        var c = cmp(it.data, n.data)
+        if c < 0:
+          if it.le == nil:
+            it.le = n
+            return
+          it = it.le
+        else:
+          if it.ri == nil:
+            it.ri = n
+            return
+          it = it.ri
+
+  proc add*[T](root: var BinaryTree[T], data: T) =
+    # convenience proc:
+    add(root, newNode(data))
+
+  iterator preorder*[T](root: BinaryTree[T]): T =
+    # Preorder traversal of a binary tree.
+    # This uses an explicit stack (which is more efficient than
+    # a recursive iterator factory).
+    var stack: seq[BinaryTree[T]] = @[root]
+    while stack.len > 0:
+      var n = stack.pop()
+      while n != nil:
+        yield n.data
+        add(stack, n.ri)  # push right subtree onto the stack
+        n = n.le          # and follow the left pointer
+
+  var
+    root: BinaryTree[string] # instantiate a BinaryTree with `string`
+  add(root, newNode("hello")) # instantiates `newNode` and `add`
+  add(root, "world")          # instantiates the second `add` proc
+  for str in preorder(root):
+    stdout.writeLine(str)
+  ```
+
+The example shows a generic binary tree. Depending on context, the brackets are
+used either to introduce type parameters or to instantiate a generic proc,
+iterator or type. As the example shows, generics work with overloading: the
+best match of `add` is used. The built-in `add` procedure for sequences
+is not hidden and is used in the `preorder` iterator.
+
+There is a special `[:T]` syntax when using generics with the method call syntax:
+
+  ```nim  test = "nim c $1"
+  proc foo[T](i: T) =
+    discard
+
+  var i: int
+
+  # i.foo[int]() # Error: expression 'foo(i)' has no type (or is ambiguous)
+
+  i.foo[:int]() # Success
+  ```
+
+
+Templates
+=========
+
+Templates are a simple substitution mechanism that operates on Nim's
+abstract syntax trees. Templates are processed in the semantic pass of the
+compiler. They integrate well with the rest of the language and share none
+of C's preprocessor macros flaws.
+
+To *invoke* a template, call it like a procedure.
+
+Example:
+
+  ```nim
+  template `!=` (a, b: untyped): untyped =
+    # this definition exists in the System module
+    not (a == b)
+
+  assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6))
+  ```
+
+The `!=`, `>`, `>=`, `in`, `notin`, `isnot` operators are in fact
+templates: this has the benefit that if you overload the `==` operator,
+the `!=` operator is available automatically and does the right thing. (Except
+for IEEE floating point numbers - NaN breaks basic boolean logic.)
+
+`a > b` is transformed into `b < a`.
+`a in b` is transformed into `contains(b, a)`.
+`notin` and `isnot` have the obvious meanings.
+
+Templates are especially useful for lazy evaluation purposes. Consider a
+simple proc for logging:
+
+  ```nim  test = "nim c $1"
+  const
+    debug = true
+
+  proc log(msg: string) {.inline.} =
+    if debug: stdout.writeLine(msg)
+
+  var
+    x = 4
+  log("x has the value: " & $x)
+  ```
+
+This code has a shortcoming: if `debug` is set to false someday, the quite
+expensive `$` and `&` operations are still performed! (The argument
+evaluation for procedures is *eager*).
+
+Turning the `log` proc into a template solves this problem:
+
+  ```nim  test = "nim c $1"
+  const
+    debug = true
+
+  template log(msg: string) =
+    if debug: stdout.writeLine(msg)
+
+  var
+    x = 4
+  log("x has the value: " & $x)
+  ```
+
+The parameters' types can be ordinary types or the meta types `untyped`,
+`typed`, or `type`. `type` suggests that only a type symbol may be given
+as an argument, and `untyped` means symbol lookups and type resolution is not
+performed before the expression is passed to the template.
+
+If the template has no explicit return type,
+`void` is used for consistency with procs and methods.
+
+To pass a block of statements to a template, use `untyped` for the last parameter:
+
+  ```nim  test = "nim c $1"
+  template withFile(f: untyped, filename: string, mode: FileMode,
+                    body: untyped) =
+    let fn = filename
+    var f: File
+    if open(f, fn, mode):
+      try:
+        body
+      finally:
+        close(f)
+    else:
+      quit("cannot open: " & fn)
+
+  withFile(txt, "ttempl3.txt", fmWrite):
+    txt.writeLine("line 1")
+    txt.writeLine("line 2")
+  ```
+
+In the example the two `writeLine` statements are bound to the `body`
+parameter. The `withFile` template contains boilerplate code and helps to
+avoid a common bug: to forget to close the file. Note how the
+`let fn = filename` statement ensures that `filename` is evaluated only
+once.
+
+Example: Lifting Procs
+----------------------
+
+  `````nim  test = "nim c $1"
+  import std/math
+
+  template liftScalarProc(fname) =
+    ## Lift a proc taking one scalar parameter and returning a
+    ## scalar value (eg `proc sssss[T](x: T): float`),
+    ## to provide templated procs that can handle a single
+    ## parameter of seq[T] or nested seq[seq[]] or the same type
+    ##
+    ##   ```Nim
+    ##   liftScalarProc(abs)
+    ##   # now abs(@[@[1,-2], @[-2,-3]]) == @[@[1,2], @[2,3]]
+    ##   ```
+    proc fname[T](x: openarray[T]): auto =
+      var temp: T
+      type outType = typeof(fname(temp))
+      result = newSeq[outType](x.len)
+      for i in 0..<x.len:
+        result[i] = fname(x[i])
+
+  liftScalarProc(sqrt)   # make sqrt() work for sequences
+  echo sqrt(@[4.0, 16.0, 25.0, 36.0])   # => @[2.0, 4.0, 5.0, 6.0]
+  `````
+
+Compilation to JavaScript
+=========================
+
+Nim code can be compiled to JavaScript. However in order to write
+JavaScript-compatible code you should remember the following:
+- `addr` and `ptr` have slightly different semantic meaning in JavaScript.
+  It is recommended to avoid those if you're not sure how they are translated
+  to JavaScript.
+- `cast[T](x)` in JavaScript is translated to `(x)`, except for casting
+  between signed/unsigned ints, in which case it behaves as static cast in
+  C language.
+- `cstring` in JavaScript means JavaScript string. It is a good practice to
+  use `cstring` only when it is semantically appropriate. E.g. don't use
+  `cstring` as a binary data buffer.
+
+
+Part 3
+======
+
+The next part is entirely about metaprogramming via macros: [Part III](tut3.html).
diff --git a/doc/tut2.rst b/doc/tut2.rst
deleted file mode 100644
index 91cb52834..000000000
--- a/doc/tut2.rst
+++ /dev/null
@@ -1,1085 +0,0 @@
-======================
-Nim Tutorial (Part II)
-======================
-
-:Author: Andreas Rumpf
-:Version: |nimversion|
-
-.. contents::
-
-
-Introduction
-============
-
-  "Repetition renders the ridiculous reasonable." -- Norman Wildberger
-
-
-This document is a tutorial for the advanced constructs of the *Nim*
-programming language. **Note that this document is somewhat obsolete as the**
-`manual <manual.html>`_ **contains many more examples of the advanced language
-features.**
-
-
-Pragmas
-=======
-
-Pragmas are Nim's method to give the compiler additional information/
-commands without introducing a massive number of new keywords. Pragmas are
-enclosed in the special ``{.`` and ``.}`` curly dot brackets. This tutorial
-does not cover pragmas. See the `manual <manual.html#pragmas>`_ or `user guide
-<nimc.html#additional-features>`_ for a description of the available
-pragmas.
-
-
-Object Oriented Programming
-===========================
-
-While Nim's support for object oriented programming (OOP) is minimalistic,
-powerful OOP techniques can be used. OOP is seen as *one* way to design a
-program, not *the only* way. Often a procedural approach leads to simpler
-and more efficient code. In particular, preferring composition over inheritance
-is often the better design.
-
-
-Objects
--------
-
-Like tuples, objects are a means to pack different values together in a
-structured way. However, objects provide many features that tuples do not:
-They provide inheritance and information hiding. Because objects encapsulate
-data, the ``T()`` object constructor should only be used internally and the
-programmer should provide a proc to initialize the object (this is called
-a *constructor*).
-
-Objects have access to their type at runtime. There is an
-``of`` operator that can be used to check the object's type:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  type
-    Person = ref object of RootObj
-      name*: string  # the * means that `name` is accessible from other modules
-      age: int       # no * means that the field is hidden from other modules
-
-    Student = ref object of Person # Student inherits from Person
-      id: int                      # with an id field
-
-  var
-    student: Student
-    person: Person
-  assert(student of Student) # is true
-  # object construction:
-  student = Student(name: "Anton", age: 5, id: 2)
-  echo student[]
-
-Object fields that should be visible from outside the defining module have to
-be marked by ``*``. In contrast to tuples, different object types are
-never *equivalent*. New object types can only be defined within a type
-section.
-
-Inheritance is done with the ``object of`` syntax. Multiple inheritance is
-currently not supported. If an object type has no suitable ancestor, ``RootObj``
-can be used as its ancestor, but this is only a convention. Objects that have
-no ancestor are implicitly ``final``. You can use the ``inheritable`` pragma
-to introduce new object roots apart from ``system.RootObj``. (This is used
-in the GTK wrapper for instance.)
-
-Ref objects should be used whenever inheritance is used. It isn't strictly
-necessary, but with non-ref objects assignments such as ``let person: Person =
-Student(id: 123)`` will truncate subclass fields.
-
-**Note**: Composition (*has-a* relation) is often preferable to inheritance
-(*is-a* relation) for simple code reuse. Since objects are value types in
-Nim, composition is as efficient as inheritance.
-
-
-Mutually recursive types
-------------------------
-
-Objects, tuples and references can model quite complex data structures which
-depend on each other; they are *mutually recursive*. In Nim
-these types can only be declared within a single type section. (Anything else
-would require arbitrary symbol lookahead which slows down compilation.)
-
-Example:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  type
-    Node = ref object  # a reference to an object with the following field:
-      le, ri: Node     # left and right subtrees
-      sym: ref Sym     # leaves contain a reference to a Sym
-
-    Sym = object       # a symbol
-      name: string     # the symbol's name
-      line: int        # the line the symbol was declared in
-      code: Node       # the symbol's abstract syntax tree
-
-
-Type conversions
-----------------
-Nim distinguishes between `type casts`:idx: and `type conversions`:idx:.
-Casts are done with the ``cast`` operator and force the compiler to
-interpret a bit pattern to be of another type.
-
-Type conversions are a much more polite way to convert a type into another:
-They preserve the abstract *value*, not necessarily the *bit-pattern*. If a
-type conversion is not possible, the compiler complains or an exception is
-raised.
-
-The syntax for type conversions is ``destination_type(expression_to_convert)``
-(like an ordinary call):
-
-.. code-block:: nim
-  proc getID(x: Person): int =
-    Student(x).id
-
-The ``InvalidObjectConversionError`` exception is raised if ``x`` is not a
-``Student``.
-
-
-Object variants
----------------
-Often an object hierarchy is overkill in certain situations where simple
-variant types are needed.
-
-An example:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  # This is an example how an abstract syntax tree could be modelled in Nim
-  type
-    NodeKind = enum  # the different node types
-      nkInt,          # a leaf with an integer value
-      nkFloat,        # a leaf with a float value
-      nkString,       # a leaf with a string value
-      nkAdd,          # an addition
-      nkSub,          # a subtraction
-      nkIf            # an if statement
-    Node = ref object
-      case kind: NodeKind  # the ``kind`` field is the discriminator
-      of nkInt: intVal: int
-      of nkFloat: floatVal: float
-      of nkString: strVal: string
-      of nkAdd, nkSub:
-        leftOp, rightOp: Node
-      of nkIf:
-        condition, thenPart, elsePart: Node
-
-  var n = Node(kind: nkFloat, floatVal: 1.0)
-  # the following statement raises an `FieldError` exception, because
-  # n.kind's value does not fit:
-  n.strVal = ""
-
-As can been seen from the example, an advantage to an object hierarchy is that
-no conversion between different object types is needed. Yet, access to invalid
-object fields raises an exception.
-
-
-Methods
--------
-In ordinary object oriented languages, procedures (also called *methods*) are
-bound to a class. This has disadvantages:
-
-* Adding a method to a class the programmer has no control over is
-  impossible or needs ugly workarounds.
-* Often it is unclear where the method should belong to: is
-  ``join`` a string method or an array method?
-
-Nim avoids these problems by not assigning methods to a class. All methods
-in Nim are multi-methods. As we will see later, multi-methods are
-distinguished from procs only for dynamic binding purposes.
-
-
-Method call syntax
-------------------
-
-There is a syntactic sugar for calling routines:
-The syntax ``obj.method(args)`` can be used instead of ``method(obj, args)``.
-If there are no remaining arguments, the parentheses can be omitted:
-``obj.len`` (instead of ``len(obj)``).
-
-This method call syntax is not restricted to objects, it can be used
-for any type:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  import strutils
-
-  echo "abc".len # is the same as echo len("abc")
-  echo "abc".toUpperAscii()
-  echo({'a', 'b', 'c'}.card)
-  stdout.writeLine("Hallo") # the same as writeLine(stdout, "Hallo")
-
-(Another way to look at the method call syntax is that it provides the missing
-postfix notation.)
-
-So "pure object oriented" code is easy to write:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  import strutils, sequtils
-
-  stdout.writeLine("Give a list of numbers (separated by spaces): ")
-  stdout.write(stdin.readLine.splitWhitespace.map(parseInt).max.`$`)
-  stdout.writeLine(" is the maximum!")
-
-
-Properties
-----------
-As the above example shows, Nim has no need for *get-properties*:
-Ordinary get-procedures that are called with the *method call syntax* achieve
-the same. But setting a value is different; for this a special setter syntax
-is needed:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  type
-    Socket* = ref object of RootObj
-      h: int # cannot be accessed from the outside of the module due to missing star
-
-  proc `host=`*(s: var Socket, value: int) {.inline.} =
-    ## setter of host address
-    s.h = value
-
-  proc host*(s: Socket): int {.inline.} =
-    ## getter of host address
-    s.h
-
-  var s: Socket
-  new s
-  s.host = 34  # same as `host=`(s, 34)
-
-(The example also shows ``inline`` procedures.)
-
-
-The ``[]`` array access operator can be overloaded to provide
-`array properties`:idx:\ :
-
-.. code-block:: nim
-    :test: "nim c $1"
-  type
-    Vector* = object
-      x, y, z: float
-
-  proc `[]=`* (v: var Vector, i: int, value: float) =
-    # setter
-    case i
-    of 0: v.x = value
-    of 1: v.y = value
-    of 2: v.z = value
-    else: assert(false)
-
-  proc `[]`* (v: Vector, i: int): float =
-    # getter
-    case i
-    of 0: result = v.x
-    of 1: result = v.y
-    of 2: result = v.z
-    else: assert(false)
-
-The example is silly, since a vector is better modelled by a tuple which
-already provides ``v[]`` access.
-
-
-Dynamic dispatch
-----------------
-
-Procedures always use static dispatch. For dynamic dispatch replace the
-``proc`` keyword by ``method``:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  type
-    Expression = ref object of RootObj ## abstract base class for an expression
-    Literal = ref object of Expression
-      x: int
-    PlusExpr = ref object of Expression
-      a, b: Expression
-
-  # watch out: 'eval' relies on dynamic binding
-  method eval(e: Expression): int =
-    # override this base method
-    quit "to override!"
-
-  method eval(e: Literal): int = e.x
-  method eval(e: PlusExpr): int = eval(e.a) + eval(e.b)
-
-  proc newLit(x: int): Literal = Literal(x: x)
-  proc newPlus(a, b: Expression): PlusExpr = PlusExpr(a: a, b: b)
-
-  echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
-
-Note that in the example the constructors ``newLit`` and ``newPlus`` are procs
-because it makes more sense for them to use static binding, but ``eval`` is a
-method because it requires dynamic binding.
-
-In a multi-method all parameters that have an object type are used for the
-dispatching:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  type
-    Thing = ref object of RootObj
-    Unit = ref object of Thing
-      x: int
-
-  method collide(a, b: Thing) {.inline.} =
-    quit "to override!"
-
-  method collide(a: Thing, b: Unit) {.inline.} =
-    echo "1"
-
-  method collide(a: Unit, b: Thing) {.inline.} =
-    echo "2"
-
-  var a, b: Unit
-  new a
-  new b
-  collide(a, b) # output: 2
-
-
-As the example demonstrates, invocation of a multi-method cannot be ambiguous:
-Collide 2 is preferred over collide 1 because the resolution works from left to
-right. Thus ``Unit, Thing`` is preferred over ``Thing, Unit``.
-
-**Performance note**: Nim does not produce a virtual method table, but
-generates dispatch trees. This avoids the expensive indirect branch for method
-calls and enables inlining. However, other optimizations like compile time
-evaluation or dead code elimination do not work with methods.
-
-
-Exceptions
-==========
-
-In Nim exceptions are objects. By convention, exception types are
-suffixed with 'Error'. The `system <system.html>`_ module defines an
-exception hierarchy that you might want to stick to. Exceptions derive from
-``system.Exception``, which provides the common interface.
-
-Exceptions have to be allocated on the heap because their lifetime is unknown.
-The compiler will prevent you from raising an exception created on the stack.
-All raised exceptions should at least specify the reason for being raised in
-the ``msg`` field.
-
-A convention is that exceptions should be raised in *exceptional* cases:
-For example, if a file cannot be opened, this should not raise an
-exception since this is quite common (the file may not exist).
-
-
-Raise statement
----------------
-Raising an exception is done with the ``raise`` statement:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  var
-    e: ref OSError
-  new(e)
-  e.msg = "the request to the OS failed"
-  raise e
-
-If the ``raise`` keyword is not followed by an expression, the last exception
-is *re-raised*. For the purpose of avoiding repeating this common code pattern,
-the template ``newException`` in the ``system`` module can be used:
-
-.. code-block:: nim
-  raise newException(OSError, "the request to the OS failed")
-
-
-Try statement
--------------
-
-The ``try`` statement handles exceptions:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  from strutils import parseInt
-
-  # read the first two lines of a text file that should contain numbers
-  # and tries to add them
-  var
-    f: File
-  if open(f, "numbers.txt"):
-    try:
-      let a = readLine(f)
-      let b = readLine(f)
-      echo "sum: ", parseInt(a) + parseInt(b)
-    except OverflowError:
-      echo "overflow!"
-    except ValueError:
-      echo "could not convert string to integer"
-    except IOError:
-      echo "IO error!"
-    except:
-      echo "Unknown exception!"
-      # reraise the unknown exception:
-      raise
-    finally:
-      close(f)
-
-The statements after the ``try`` are executed unless an exception is
-raised. Then the appropriate ``except`` part is executed.
-
-The empty ``except`` part is executed if there is an exception that is
-not explicitly listed. It is similar to an ``else`` part in ``if``
-statements.
-
-If there is a ``finally`` part, it is always executed after the
-exception handlers.
-
-The exception is *consumed* in an ``except`` part. If an exception is not
-handled, it is propagated through the call stack. This means that often
-the rest of the procedure - that is not within a ``finally`` clause -
-is not executed (if an exception occurs).
-
-If you need to *access* the actual exception object or message inside an
-``except`` branch you can use the `getCurrentException()
-<system.html#getCurrentException>`_ and `getCurrentExceptionMsg()
-<system.html#getCurrentExceptionMsg>`_ procs from the `system <system.html>`_
-module. Example:
-
-.. code-block:: nim
-  try:
-    doSomethingHere()
-  except:
-    let
-      e = getCurrentException()
-      msg = getCurrentExceptionMsg()
-    echo "Got exception ", repr(e), " with message ", msg
-
-
-Annotating procs with raised exceptions
----------------------------------------
-
-Through the use of the optional ``{.raises.}`` pragma you can specify that a
-proc is meant to raise a specific set of exceptions, or none at all. If the
-``{.raises.}`` pragma is used, the compiler will verify that this is true. For
-instance, if you specify that a proc raises ``IOError``, and at some point it
-(or one of the procs it calls) starts raising a new exception the compiler will
-prevent that proc from compiling. Usage example:
-
-.. code-block:: nim
-  proc complexProc() {.raises: [IOError, ArithmeticError].} =
-    ...
-
-  proc simpleProc() {.raises: [].} =
-    ...
-
-Once you have code like this in place, if the list of raised exception changes
-the compiler will stop with an error specifying the line of the proc which
-stopped validating the pragma and the raised exception not being caught, along
-with the file and line where the uncaught exception is being raised, which may
-help you locate the offending code which has changed.
-
-If you want to add the ``{.raises.}`` pragma to existing code, the compiler can
-also help you. You can add the ``{.effects.}`` pragma statement to your proc and
-the compiler will output all inferred effects up to that point (exception
-tracking is part of Nim's effect system). Another more roundabout way to
-find out the list of exceptions raised by a proc is to use the Nim ``doc2``
-command which generates documentation for a whole module and decorates all
-procs with the list of raised exceptions. You can read more about Nim's
-`effect system and related pragmas in the manual <manual.html#effect-system>`_.
-
-
-Generics
-========
-
-Generics are Nim's means to parametrize procs, iterators or types
-with `type parameters`:idx:. They are most useful for efficient type safe
-containers:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  type
-    BinaryTree*[T] = ref object # BinaryTree is a generic type with
-                                # generic param ``T``
-      le, ri: BinaryTree[T]     # left and right subtrees; may be nil
-      data: T                   # the data stored in a node
-
-  proc newNode*[T](data: T): BinaryTree[T] =
-    # constructor for a node
-    new(result)
-    result.data = data
-
-  proc add*[T](root: var BinaryTree[T], n: BinaryTree[T]) =
-    # insert a node into the tree
-    if root == nil:
-      root = n
-    else:
-      var it = root
-      while it != nil:
-        # compare the data items; uses the generic ``cmp`` proc
-        # that works for any type that has a ``==`` and ``<`` operator
-        var c = cmp(it.data, n.data)
-        if c < 0:
-          if it.le == nil:
-            it.le = n
-            return
-          it = it.le
-        else:
-          if it.ri == nil:
-            it.ri = n
-            return
-          it = it.ri
-
-  proc add*[T](root: var BinaryTree[T], data: T) =
-    # convenience proc:
-    add(root, newNode(data))
-
-  iterator preorder*[T](root: BinaryTree[T]): T =
-    # Preorder traversal of a binary tree.
-    # Since recursive iterators are not yet implemented,
-    # this uses an explicit stack (which is more efficient anyway):
-    var stack: seq[BinaryTree[T]] = @[root]
-    while stack.len > 0:
-      var n = stack.pop()
-      while n != nil:
-        yield n.data
-        add(stack, n.ri)  # push right subtree onto the stack
-        n = n.le          # and follow the left pointer
-
-  var
-    root: BinaryTree[string] # instantiate a BinaryTree with ``string``
-  add(root, newNode("hello")) # instantiates ``newNode`` and ``add``
-  add(root, "world")          # instantiates the second ``add`` proc
-  for str in preorder(root):
-    stdout.writeLine(str)
-
-The example shows a generic binary tree. Depending on context, the brackets are
-used either to introduce type parameters or to instantiate a generic proc,
-iterator or type. As the example shows, generics work with overloading: the
-best match of ``add`` is used. The built-in ``add`` procedure for sequences
-is not hidden and is used in the ``preorder`` iterator.
-
-
-Templates
-=========
-
-Templates are a simple substitution mechanism that operates on Nim's
-abstract syntax trees. Templates are processed in the semantic pass of the
-compiler. They integrate well with the rest of the language and share none
-of C's preprocessor macros flaws.
-
-To *invoke* a template, call it like a procedure.
-
-Example:
-
-.. code-block:: nim
-  template `!=` (a, b: untyped): untyped =
-    # this definition exists in the System module
-    not (a == b)
-
-  assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6))
-
-The ``!=``, ``>``, ``>=``, ``in``, ``notin``, ``isnot`` operators are in fact
-templates: this has the benefit that if you overload the ``==`` operator,
-the ``!=`` operator is available automatically and does the right thing. (Except
-for IEEE floating point numbers - NaN breaks basic boolean logic.)
-
-``a > b`` is transformed into ``b < a``.
-``a in b`` is transformed into ``contains(b, a)``.
-``notin`` and ``isnot`` have the obvious meanings.
-
-Templates are especially useful for lazy evaluation purposes. Consider a
-simple proc for logging:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  const
-    debug = true
-
-  proc log(msg: string) {.inline.} =
-    if debug: stdout.writeLine(msg)
-
-  var
-    x = 4
-  log("x has the value: " & $x)
-
-This code has a shortcoming: if ``debug`` is set to false someday, the quite
-expensive ``$`` and ``&`` operations are still performed! (The argument
-evaluation for procedures is *eager*).
-
-Turning the ``log`` proc into a template solves this problem:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  const
-    debug = true
-
-  template log(msg: string) =
-    if debug: stdout.writeLine(msg)
-
-  var
-    x = 4
-  log("x has the value: " & $x)
-
-The parameters' types can be ordinary types or the meta types ``untyped``,
-``typed``, or ``typedesc``.
-``typedesc`` stands for *type description*, and ``untyped`` means symbol lookups and
-type resolution is not performed before the expression is passed to the template.
-
-If the template has no explicit return type,
-``void`` is used for consistency with procs and methods.
-
-To pass a block of statements to a template, use 'untyped' for the last parameter:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  template withFile(f: untyped, filename: string, mode: FileMode,
-                    body: untyped): typed =
-    let fn = filename
-    var f: File
-    if open(f, fn, mode):
-      try:
-        body
-      finally:
-        close(f)
-    else:
-      quit("cannot open: " & fn)
-
-  withFile(txt, "ttempl3.txt", fmWrite):
-    txt.writeLine("line 1")
-    txt.writeLine("line 2")
-
-In the example the two ``writeLine`` statements are bound to the ``body``
-parameter. The ``withFile`` template contains boilerplate code and helps to
-avoid a common bug: to forget to close the file. Note how the
-``let fn = filename`` statement ensures that ``filename`` is evaluated only
-once.
-
-Macros
-======
-
-Macros enable advanced compile-time code transformations, but they cannot
-change Nim's syntax. However, this is no real restriction because Nim's
-syntax is flexible enough anyway. Macros have to be implemented in pure Nim
-code if the `foreign function interface (FFI)
-<manual.html#foreign-function-interface>`_ is not enabled in the compiler, but
-other than that restriction (which at some point in the future will go away)
-you can write any kind of Nim code and the compiler will run it at compile
-time.
-
-There are two ways to write a macro, either *generating* Nim source code and
-letting the compiler parse it, or creating manually an abstract syntax tree
-(AST) which you feed to the compiler. In order to build the AST one needs to
-know how the Nim concrete syntax is converted to an abstract syntax tree
-(AST). The AST is documented in the `macros <macros.html>`_ module.
-
-Once your macro is finished, there are two ways to invoke it:
-(1) invoking a macro like a procedure call (expression macros)
-(2) invoking a macro with the special ``macrostmt``
-    syntax (statement macros)
-
-
-Expression Macros
------------------
-
-The following example implements a powerful ``debug`` command that accepts a
-variable number of arguments:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  # to work with Nim syntax trees, we need an API that is defined in the
-  # ``macros`` module:
-  import macros
-
-  macro debug(n: varargs[untyped]): typed =
-    # `n` is a Nim AST that contains a list of expressions;
-    # this macro returns a list of statements (n is passed for proper line
-    # information):
-    result = newNimNode(nnkStmtList, n)
-    # iterate over any argument that is passed to this macro:
-    for x in n:
-      # add a call to the statement list that writes the expression;
-      # `toStrLit` converts an AST to its string representation:
-      result.add(newCall("write", newIdentNode("stdout"), toStrLit(x)))
-      # add a call to the statement list that writes ": "
-      result.add(newCall("write", newIdentNode("stdout"), newStrLitNode(": ")))
-      # add a call to the statement list that writes the expressions value:
-      result.add(newCall("writeLine", newIdentNode("stdout"), x))
-
-  var
-    a: array[0..10, int]
-    x = "some string"
-  a[0] = 42
-  a[1] = 45
-
-  debug(a[0], a[1], x)
-
-The macro call expands to:
-
-.. code-block:: nim
-  write(stdout, "a[0]")
-  write(stdout, ": ")
-  writeLine(stdout, a[0])
-
-  write(stdout, "a[1]")
-  write(stdout, ": ")
-  writeLine(stdout, a[1])
-
-  write(stdout, "x")
-  write(stdout, ": ")
-  writeLine(stdout, x)
-
-
-
-Statement Macros
-----------------
-
-Statement macros are defined just as expression macros. However, they are
-invoked by an expression following a colon.
-
-The following example outlines a macro that generates a lexical analyzer from
-regular expressions:
-
-.. code-block:: nim
-
-  macro case_token(n: varargs[untyped]): typed =
-    # creates a lexical analyzer from regular expressions
-    # ... (implementation is an exercise for the reader :-)
-    discard
-
-  case_token: # this colon tells the parser it is a macro statement
-  of r"[A-Za-z_]+[A-Za-z_0-9]*":
-    return tkIdentifier
-  of r"0-9+":
-    return tkInteger
-  of r"[\+\-\*\?]+":
-    return tkOperator
-  else:
-    return tkUnknown
-
-
-Building your first macro
--------------------------
-
-To give a footstart to writing macros we will show now how to turn your typical
-dynamic code into something that compiles statically. For the exercise we will
-use the following snippet of code as the starting point:
-
-.. code-block:: nim
-    :test: "nim c $1"
-
-  import strutils, tables
-
-  proc readCfgAtRuntime(cfgFilename: string): Table[string, string] =
-    let
-      inputString = readFile(cfgFilename)
-    var
-      source = ""
-
-    result = initTable[string, string]()
-    for line in inputString.splitLines:
-      # Ignore empty lines
-      if line.len < 1: continue
-      var chunks = split(line, ',')
-      if chunks.len != 2:
-        quit("Input needs comma split values, got: " & line)
-      result[chunks[0]] = chunks[1]
-
-    if result.len < 1: quit("Input file empty!")
-
-  let info = readCfgAtRuntime("data.cfg")
-
-  when isMainModule:
-    echo info["licenseOwner"]
-    echo info["licenseKey"]
-    echo info["version"]
-
-Presumably this snippet of code could be used in a commercial software, reading
-a configuration file to display information about the person who bought the
-software. This external file would be generated by an online web shopping cart
-to be included along the program containing the license information::
-
-  version,1.1
-  licenseOwner,Hyori Lee
-  licenseKey,M1Tl3PjBWO2CC48m
-
-The ``readCfgAtRuntime`` proc will open the given filename and return a
-``Table`` from the `tables module <tables.html>`_. The parsing of the file is
-done (without much care for handling invalid data or corner cases) using the
-`splitLines proc from the strutils module <strutils.html#splitLines>`_. There
-are many things which can fail; mind the purpose is explaining how to make
-this run at compile time, not how to properly implement a DRM scheme.
-
-The reimplementation of this code as a compile time proc will allow us to get
-rid of the ``data.cfg`` file we would need to distribute along the binary, plus
-if the information is really constant, it doesn't make from a logical point of
-view to have it *mutable* in a global variable, it would be better if it was a
-constant. Finally, and likely the most valuable feature, we can implement some
-verification at compile time. You could think of this as a *better unit
-testing*, since it is impossible to obtain a binary unless everything is
-correct, preventing you to ship to users a broken program which won't start
-because a small critical file is missing or its contents changed by mistake to
-something invalid.
-
-
-Generating source code
-++++++++++++++++++++++
-
-Our first attempt will start by modifying the program to generate a compile
-time string with the *generated source code*, which we then pass to the
-``parseStmt`` proc from the `macros module <macros.html>`_. Here is the
-modified source code implementing the macro:
-
-.. code-block:: nim
-   :number-lines:
-
-  import macros, strutils
-
-  macro readCfgAndBuildSource(cfgFilename: string): typed =
-    let
-      inputString = slurp(cfgFilename.strVal)
-    var
-      source = ""
-
-    for line in inputString.splitLines:
-      # Ignore empty lines
-      if line.len < 1: continue
-      var chunks = split(line, ',')
-      if chunks.len != 2:
-        error("Input needs comma split values, got: " & line)
-      source &= "const cfg" & chunks[0] & "= \"" & chunks[1] & "\"\n"
-
-    if source.len < 1: error("Input file empty!")
-    result = parseStmt(source)
-
-  readCfgAndBuildSource("data.cfg")
-
-  when isMainModule:
-    echo cfglicenseOwner
-    echo cfglicenseKey
-    echo cfgversion
-
-The good news is not much has changed! First, we need to change the handling
-of the input parameter (line 3). In the dynamic version the
-``readCfgAtRuntime`` proc receives a string parameter. However, in the macro
-version it is also declared as string, but this is the *outside* interface of
-the macro.  When the macro is run, it actually gets a ``PNimNode`` object
-instead of a string, and we have to call the `strVal proc
-<macros.html#strVal>`_ (line 5) from the `macros module <macros.html>`_ to
-obtain the string being passed in to the macro.
-
-Second, we cannot use the `readFile proc <system.html#readFile>`_ from the
-`system module <system.html>`_ due to FFI restriction at compile time. If we
-try to use this proc, or any other which depends on FFI, the compiler will
-error with the message ``cannot evaluate`` and a dump of the macro's source
-code, along with a stack trace where the compiler reached before bailing out.
-We can get around this limitation by using the `slurp proc
-<system.html#slurp>`_ from the `system module <system.html>`_, which was
-precisely made for compilation time (just like `gorge <system.html#gorge>`_
-which executes an external program and captures its output).
-
-The interesting thing is that our macro does not return a runtime `Table
-<tables.html#Table>`_ object. Instead, it builds up Nim source code into
-the ``source`` variable.  For each line of the configuration file a ``const``
-variable will be generated (line 15).  To avoid conflicts we prefix these
-variables with ``cfg``. In essence, what the compiler is doing is replacing
-the line calling the macro with the following snippet of code:
-
-.. code-block:: nim
-  const cfgversion = "1.1"
-  const cfglicenseOwner = "Hyori Lee"
-  const cfglicenseKey = "M1Tl3PjBWO2CC48m"
-
-You can verify this yourself adding the line ``echo source`` somewhere at the
-end of the macro and compiling the program. Another difference is that instead
-of calling the usual `quit proc <system.html#quit>`_ to abort (which we could
-still call) this version calls the `error proc <macros.html#error>`_ (line
-14). The ``error`` proc has the same behavior as ``quit`` but will dump also
-the source and file line information where the error happened, making it
-easier for the programmer to find where compilation failed. In this situation
-it would point to the line invoking the macro, but **not** the line of
-``data.cfg`` we are processing, that's something the macro itself would need
-to control.
-
-
-Generating AST by hand
-++++++++++++++++++++++
-
-To generate an AST we would need to intimately know the structures used by the
-Nim compiler exposed in the `macros module <macros.html>`_, which at first
-look seems a daunting task. But we can use as helper shortcut the `dumpTree
-macro <macros.html#dumpTree>`_, which is used as a statement macro instead of
-an expression macro.  Since we know that we want to generate a bunch of
-``const`` symbols we can create the following source file and compile it to
-see what the compiler *expects* from us:
-
-.. code-block:: nim
-    :test: "nim c $1"
-  import macros
-
-  dumpTree:
-    const cfgversion: string = "1.1"
-    const cfglicenseOwner = "Hyori Lee"
-    const cfglicenseKey = "M1Tl3PjBWO2CC48m"
-
-During compilation of the source code we should see the following lines in the
-output (again, since this is a macro, compilation is enough, you don't have to
-run any binary)::
-
-  StmtList
-    ConstSection
-      ConstDef
-        Ident !"cfgversion"
-        Ident !"string"
-        StrLit 1.1
-    ConstSection
-      ConstDef
-        Ident !"cfglicenseOwner"
-        Empty
-        StrLit Hyori Lee
-    ConstSection
-      ConstDef
-        Ident !"cfglicenseKey"
-        Empty
-        StrLit M1Tl3PjBWO2CC48m
-
-With this output we have a better idea of what kind of input the compiler
-expects. We need to generate a list of statements. For each constant the source
-code generates a ``ConstSection`` and a ``ConstDef``. If we were to move all
-the constants to a single ``const`` block we would see only a single
-``ConstSection`` with three children.
-
-Maybe you didn't notice, but in the ``dumpTree`` example the first constant
-explicitly specifies the type of the constant.  That's why in the tree output
-the two last constants have their second child ``Empty`` but the first has a
-string identifier. So basically a ``const`` definition is made up from an
-identifier, optionally a type (can be an *empty* node) and the value. Armed
-with this knowledge, let's look at the finished version of the AST building
-macro:
-
-.. code-block:: nim
-   :number-lines:
-
-  import macros, strutils
-
-  macro readCfgAndBuildAST(cfgFilename: string): typed =
-    let
-      inputString = slurp(cfgFilename.strVal)
-
-    result = newNimNode(nnkStmtList)
-    for line in inputString.splitLines:
-      # Ignore empty lines
-      if line.len < 1: continue
-      var chunks = split(line, ',')
-      if chunks.len != 2:
-        error("Input needs comma split values, got: " & line)
-      var
-        section = newNimNode(nnkConstSection)
-        constDef = newNimNode(nnkConstDef)
-      constDef.add(newIdentNode("cfg" & chunks[0]))
-      constDef.add(newEmptyNode())
-      constDef.add(newStrLitNode(chunks[1]))
-      section.add(constDef)
-      result.add(section)
-
-    if result.len < 1: error("Input file empty!")
-
-  readCfgAndBuildAST("data.cfg")
-
-  when isMainModule:
-    echo cfglicenseOwner
-    echo cfglicenseKey
-    echo cfgversion
-
-Since we are building on the previous example generating source code, we will
-only mention the differences to it. Instead of creating a temporary ``string``
-variable and writing into it source code as if it were written *by hand*, we
-use the ``result`` variable directly and create a statement list node
-(``nnkStmtList``) which will hold our children (line 7).
-
-For each input line we have to create a constant definition (``nnkConstDef``)
-and wrap it inside a constant section (``nnkConstSection``). Once these
-variables are created, we fill them hierarchichally (line 17) like the
-previous AST dump tree showed: the constant definition is a child of the
-section definition, and the constant definition has an identifier node, an
-empty node (we let the compiler figure out the type), and a string literal
-with the value.
-
-A last tip when writing a macro: if you are not sure the AST you are building
-looks ok, you may be tempted to use the ``dumpTree`` macro. But you can't use
-it *inside* the macro you are writting/debugging. Instead ``echo`` the string
-generated by `treeRepr <macros.html#treeRepr>`_. If at the end of the this
-example you add ``echo treeRepr(result)`` you should get the same output as
-using the ``dumpTree`` macro, but of course you can call that at any point of
-the macro where you might be having troubles.
-
-Example Templates and Macros
-============================
-
-Lifting Procs
-+++++++++++++
-
-.. code-block:: nim
-    :test: "nim c $1"
-  import math
-
-  template liftScalarProc(fname) =
-    ## Lift a proc taking one scalar parameter and returning a
-    ## scalar value (eg ``proc sssss[T](x: T): float``),
-    ## to provide templated procs that can handle a single
-    ## parameter of seq[T] or nested seq[seq[]] or the same type
-    ##
-    ## .. code-block:: Nim
-    ##  liftScalarProc(abs)
-    ##  # now abs(@[@[1,-2], @[-2,-3]]) == @[@[1,2], @[2,3]]
-    proc fname[T](x: openarray[T]): auto =
-      var temp: T
-      type outType = type(fname(temp))
-      result = newSeq[outType](x.len)
-      for i in 0..<x.len:
-        result[i] = fname(x[i])
-
-  liftScalarProc(sqrt)   # make sqrt() work for sequences
-  echo sqrt(@[4.0, 16.0, 25.0, 36.0])   # => @[2.0, 4.0, 5.0, 6.0]
-
-Identifier Mangling
-+++++++++++++++++++
-
-.. code-block:: nim
-  proc echoHW() =
-    echo "Hello world"
-  proc echoHW0() =
-    echo "Hello world 0"
-  proc echoHW1() =
-    echo "Hello world 1"
-
-  template joinSymbols(a, b: untyped): untyped =
-    `a b`()
-
-  joinSymbols(echo, HW)
-
-  macro str2Call(s1, s2): typed =
-    result = newNimNode(nnkStmtList)
-    for i in 0..1:
-      # combines s1, s2 and an integer into an proc identifier
-      # that is called in a statement list
-      result.add(newCall(!($s1 & $s2 & $i)))
-
-  str2Call("echo", "HW")
-
-  # Output:
-  #   Hello world
-  #   Hello world 0
-  #   Hello world 1
-
-Compilation to JavaScript
-=========================
-
-Nim code can be compiled to JavaScript. However in order to write
-JavaScript-compatible code you should remember the following:
-- ``addr`` and ``ptr`` have slightly different semantic meaning in JavaScript.
-  It is recommended to avoid those if you're not sure how they are translated
-  to JavaScript.
-- ``cast[T](x)`` in JavaScript is translated to ``(x)``, except for casting
-  between signed/unsigned ints, in which case it behaves as static cast in
-  C language.
-- ``cstring`` in JavaScript means JavaScript string. It is a good practice to
-  use ``cstring`` only when it is semantically appropriate. E.g. don't use
-  ``cstring`` as a binary data buffer.
diff --git a/doc/tut3.md b/doc/tut3.md
new file mode 100644
index 000000000..3a55d4790
--- /dev/null
+++ b/doc/tut3.md
@@ -0,0 +1,417 @@
+=======================
+Nim Tutorial (Part III)
+=======================
+
+:Author: Arne Döring
+:Version: |nimversion|
+
+.. default-role:: code
+.. include:: rstcommon.rst
+.. contents::
+
+
+Introduction
+============
+
+>  "With Great Power Comes Great Responsibility." -- Spider Man's Uncle
+
+This document is a tutorial about Nim's macro system.
+A macro is a function that is executed at compile-time and transforms
+a Nim syntax tree into a different tree.
+
+Examples of things that can be implemented in macros:
+
+* An assert macro that prints both sides of a comparison operator, if
+  the assertion fails. `myAssert(a == b)` is converted to
+  `if a != b: quit($a " != " $b)`
+
+* A debug macro that prints the value and the name of the symbol.
+  `myDebugEcho(a)` is converted to `echo "a: ", a`
+
+* Symbolic differentiation of an expression.
+  `diff(a*pow(x,3) + b*pow(x,2) + c*x + d, x)` is converted to
+  `3*a*pow(x,2) + 2*b*x + c`
+
+
+Macro Arguments
+---------------
+
+The types of macro arguments have two faces. One face is used for
+the overload resolution and the other face is used within the macro
+body. For example, if `macro foo(arg: int)` is called in an
+expression `foo(x)`, `x` has to be of a type compatible to int, but
+*within* the macro's body `arg` has the type `NimNode`, not `int`!
+Why it is done this way will become obvious later, when we have seen
+concrete examples.
+
+There are two ways to pass arguments to a macro, an argument can be
+either `typed` or `untyped`.
+
+
+Untyped Arguments
+-----------------
+
+Untyped macro arguments are passed to the macro before they are
+semantically checked. This means the syntax tree that is passed down
+to the macro does not need to make sense for Nim yet, the only
+limitation is that it needs to be parsable. Usually, the macro does
+not check the argument either but uses it in the transformation's
+result somehow. The result of a macro expansion is always checked
+by the compiler, so apart from weird error messages, nothing bad
+can happen.
+
+The downside for an `untyped` argument is that these do not play
+well with Nim's overloading resolution.
+
+The upside for untyped arguments is that the syntax tree is
+quite predictable and less complex compared to its `typed`
+counterpart.
+
+
+Typed Arguments
+---------------
+
+For typed arguments, the semantic checker runs on the argument and
+does transformations on it, before it is passed to the macro. Here
+identifier nodes are resolved as symbols, implicit type
+conversions are visible in the tree as calls, templates are
+expanded, and probably most importantly, nodes have type information.
+Typed arguments can have the type `typed` in the arguments list.
+But all other types, such as `int`, `float` or `MyObjectType`
+are typed arguments as well, and they are passed to the macro as a
+syntax tree.
+
+
+Static Arguments
+----------------
+
+Static arguments are a way to pass values as values and not as syntax
+tree nodes to a macro. For example for `macro foo(arg: static[int])`
+in the expression `foo(x)`, `x` needs to be an integer constant,
+but in the macro body `arg` is just like a normal parameter of type
+`int`.
+
+  ```nim
+  import std/macros
+
+  macro myMacro(arg: static[int]): untyped =
+    echo arg # just an int (7), not `NimNode`
+
+  myMacro(1 + 2 * 3)
+  ```
+
+
+Code Blocks as Arguments
+------------------------
+
+It is possible to pass the last argument of a call expression in a
+separate code block with indentation. For example, the following code
+example is a valid (but not a recommended) way to call `echo`:
+
+  ```nim
+  echo "Hello ":
+    let a = "Wor"
+    let b = "ld!"
+    a & b
+  ```
+
+For macros this way of calling is very useful; syntax trees of arbitrary
+complexity can be passed to macros with this notation.
+
+
+The Syntax Tree
+---------------
+
+In order to build a Nim syntax tree one needs to know how Nim source
+code is represented as a syntax tree, and how such a tree needs to
+look like so that the Nim compiler will understand it. The nodes of the
+Nim syntax tree are documented in the [macros](macros.html) module.
+But a more interactive way to explore the Nim
+syntax tree is with `macros.treeRepr`, it converts a syntax tree
+into a multi-line string for printing on the console. It can be used
+to explore how the argument expressions are represented in tree form
+and for debug printing of generated syntax tree. `dumpTree` is a
+predefined macro that just prints its argument in a tree representation,
+but does nothing else. Here is an example of such a tree representation:
+
+  ```nim
+  dumpTree:
+    var mt: MyType = MyType(a:123.456, b:"abcdef")
+
+  # output:
+  #   StmtList
+  #     VarSection
+  #       IdentDefs
+  #         Ident "mt"
+  #         Ident "MyType"
+  #         ObjConstr
+  #           Ident "MyType"
+  #           ExprColonExpr
+  #             Ident "a"
+  #             FloatLit 123.456
+  #           ExprColonExpr
+  #             Ident "b"
+  #             StrLit "abcdef"
+  ```
+
+
+Custom Semantic Checking
+------------------------
+
+The first thing that a macro should do with its arguments is to check
+if the argument is in the correct form. Not every type of wrong input
+needs to be caught here, but anything that could cause a crash during
+macro evaluation should be caught and create a nice error message.
+`macros.expectKind` and `macros.expectLen` are a good start. If
+the checks need to be more complex, arbitrary error messages can
+be created with the `macros.error` proc.
+
+  ```nim
+  macro myAssert(arg: untyped): untyped =
+    arg.expectKind nnkInfix
+  ```
+
+
+Generating Code
+---------------
+
+There are two ways to generate the code. Either by creating the syntax
+tree with expressions that contain a lot of calls to `newTree` and
+`newLit`, or with `quote do:` expressions. The first option offers
+the best low-level control for the syntax tree generation, but the
+second option is much less verbose. If you choose to create the syntax
+tree with calls to `newTree` and `newLit` the macro
+`macros.dumpAstGen` can help you with the verbosity.
+
+`quote do:` allows you to write the code that you want to generate literally.
+Backticks are used to insert code from `NimNode` symbols into the
+generated expression.
+
+  ```nim  test = "nim c $1"
+  import std/macros
+  macro a(i) = quote do:
+    let `i` = 0
+
+  a b
+  doAssert b == 0
+  ```
+
+A custom prefix operator can be defined whenever backticks are needed.
+
+  ```nim  test = "nim c $1"
+  import std/macros
+  macro a(i) = quote("@") do:
+    assert @i == 0
+
+  let b = 0
+  a b
+  ```
+
+The injected symbol needs accent quoted when it resolves to a symbol.
+
+  ```nim  test = "nim c $1"
+  import std/macros
+  macro a(i) = quote("@") do:
+    let `@i` = 0
+
+  a b
+  doAssert b == 0
+  ```
+
+Make sure to inject only symbols of type `NimNode` into the generated syntax
+tree. You can use `newLit` to convert arbitrary values into
+expressions trees of type `NimNode` so that it is safe to inject
+them into the tree.
+
+
+  ```nim  test = "nim c $1"
+  import std/macros
+
+  type
+    MyType = object
+      a: float
+      b: string
+
+  macro myMacro(arg: untyped): untyped =
+    var mt: MyType = MyType(a:123.456, b:"abcdef")
+
+    # ...
+
+    let mtLit = newLit(mt)
+
+    result = quote do:
+      echo `arg`
+      echo `mtLit`
+
+  myMacro("Hallo")
+  ```
+
+The call to `myMacro` will generate the following code:
+
+  ```nim
+  echo "Hallo"
+  echo MyType(a: 123.456'f64, b: "abcdef")
+  ```
+
+
+Building Your First Macro
+-------------------------
+
+To give a starting point to writing macros we will show now how to
+implement the `myAssert` macro mentioned earlier. The first thing to
+do is to build a simple example of the macro usage, and then just
+print the argument. This way it is possible to get an idea of what a
+correct argument should look like.
+
+  ```nim  test = "nim c $1"
+  import std/macros
+
+  macro myAssert(arg: untyped): untyped =
+    echo arg.treeRepr
+
+  let a = 1
+  let b = 2
+
+  myAssert(a != b)
+  ```
+
+  ```
+  Infix
+    Ident "!="
+    Ident "a"
+    Ident "b"
+  ```
+
+
+From the output, it is possible to see that the argument is an infix
+operator (node kind is "Infix"), as well as that the two operands are
+at index 1 and 2. With this information, the actual macro can be
+written.
+
+  ```nim  test = "nim c $1"
+  import std/macros
+
+  macro myAssert(arg: untyped): untyped =
+    # all node kind identifiers are prefixed with "nnk"
+    arg.expectKind nnkInfix
+    arg.expectLen 3
+    # operator as string literal
+    let op  = newLit(" " & arg[0].repr & " ")
+    let lhs = arg[1]
+    let rhs = arg[2]
+
+    result = quote do:
+      if not `arg`:
+        raise newException(AssertionDefect,$`lhs` & `op` & $`rhs`)
+
+  let a = 1
+  let b = 2
+
+  myAssert(a != b)
+  myAssert(a == b)
+  ```
+
+
+This is the code that will be generated. To debug what the macro
+actually generated, the statement `echo result.repr` can be used, in
+the last line of the macro. It is also the statement that has been
+used to get this output.
+
+  ```nim
+  if not (a != b):
+    raise newException(AssertionDefect, $a & " != " & $b)
+  ```
+
+
+Going further
+-------------
+
+It is possible to create more complex macros by combining different
+`NimNode` symbols with `quote do:` expressions. For example, you may
+use `newStmtList` to build your macro iteratively, and `ident` in cases
+in which you wish to create an identifier from a string, as shown below.
+
+  ```nim
+  import std/macros
+
+  macro createProcedures() =
+    result = newStmtList()
+
+    for i in 0..<10:
+      let name = ident("myProc" & $i)
+      let content = newLit("I am procedure number #" & $i)
+
+      result.add quote do:
+        proc `name`() =
+          echo `content`
+
+  createProcedures()
+  myProc7()
+  ```
+
+The call to `myProc7` will echo `I am procedure number #7`.
+
+
+With Power Comes Responsibility
+-------------------------------
+
+Macros are very powerful. A piece of good advice is to use them as little as
+possible, but as much as necessary. Macros can change the semantics of
+expressions, making the code incomprehensible for anybody who does not
+know exactly what the macro does with it. So whenever a macro is not
+necessary and the same logic can be implemented using templates or
+generics, it is probably better not to use a macro. And when a macro
+is used for something, the macro should better have a well-written
+documentation. For all the people who claim to write only perfectly
+self-explanatory code: when it comes to macros, the implementation is
+not enough for documentation.
+
+Limitations
+-----------
+
+Since macros are evaluated in the compiler in the NimVM, macros share
+all the limitations of the NimVM. They have to be implemented in pure Nim
+code. Macros can start external processes on the shell, but they
+cannot call C functions except those that are built in the
+compiler.
+
+
+More Examples
+=============
+
+This tutorial can only cover the basics of the macro system. There are
+macros out there that could be an inspiration for you of what is
+possible with it.
+
+
+Strformat
+---------
+
+In the Nim standard library, the `strformat` library provides a
+macro that parses a string literal at compile time. Parsing a string
+in a macro like here is generally not recommended. The parsed AST
+cannot have type information, and parsing implemented on the VM is
+generally not very fast. Working on AST nodes is almost always the
+recommended way. But still `strformat` is a good example for a
+practical use case for a macro that is slightly more complex than the
+`assert` macro.
+
+[Strformat](https://github.com/nim-lang/Nim/blob/devel/lib/pure/strformat.nim)
+
+Ast Pattern Matching
+--------------------
+
+Ast Pattern Matching is a macro library to aid in writing complex
+macros. This can be seen as a good example of how to repurpose the
+Nim syntax tree with new semantics.
+
+[Ast Pattern Matching](https://github.com/nim-lang/ast-pattern-matching)
+
+OpenGL Sandbox
+--------------
+
+This project has a working Nim to GLSL compiler written entirely in
+macros. It scans recursively through all used function symbols to
+compile them so that cross library functions can be executed on the GPU.
+
+[OpenGL Sandbox](https://github.com/krux02/opengl-sandbox)
diff --git a/drnim/drnim.nim b/drnim/drnim.nim
new file mode 100644
index 000000000..eb0d89aa2
--- /dev/null
+++ b/drnim/drnim.nim
@@ -0,0 +1,1272 @@
+#
+#
+#            Doctor Nim
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+#[
+
+- introduce Phi nodes to complete the SSA representation
+- the analysis has to take 'break', 'continue' and 'raises' into account
+- We need to map arrays to Z3 and test for something like 'forall(i, (i in 3..4) -> (a[i] > 3))'
+- We need teach DrNim what 'inc', 'dec' and 'swap' mean, for example
+  'x in n..m; inc x' implies 'x in n+1..m+1'
+
+]#
+
+import std / [
+  parseopt, strutils, os, tables, times, intsets, hashes
+]
+
+import ".." / compiler / [
+  ast, astalgo, types, renderer,
+  commands, options, msgs,
+  platform, trees, wordrecg, guards,
+  idents, lineinfos, cmdlinehelper, modulegraphs, condsyms,
+  pathutils, passes, passaux, sem, modules
+]
+
+import z3 / z3_api
+
+when not defined(windows):
+  # on UNIX we use static linking because UNIX's lib*.so system is broken
+  # beyond repair and the neckbeards don't understand software development.
+  {.passL: "dist/z3/build/libz3.a".}
+
+const
+  HelpMessage = "DrNim Version $1 [$2: $3]\n" &
+      "Compiled at $4\n" &
+      "Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n"
+
+const
+  Usage = """
+drnim [options] [projectfile]
+
+Options: Same options that the Nim compiler supports. Plus:
+
+--assumeUnique   Assume unique `ref` pointers. This makes the analysis unsound
+                 but more useful for wild Nim code such as the Nim compiler
+                 itself.
+"""
+
+proc getCommandLineDesc(conf: ConfigRef): string =
+  result = (HelpMessage % [system.NimVersion, platform.OS[conf.target.hostOS].name,
+                           CPU[conf.target.hostCPU].name, CompileDate]) &
+                           Usage
+
+proc helpOnError(conf: ConfigRef) =
+  msgWriteln(conf, getCommandLineDesc(conf), {msgStdout})
+  msgQuit(0)
+
+type
+  CannotMapToZ3Error = object of ValueError
+  Z3Exception = object of ValueError
+  VersionScope = distinct int
+  DrnimContext = ref object
+    z3: Z3_context
+    graph: ModuleGraph
+    idgen: IdGenerator
+    facts: seq[(PNode, VersionScope)]
+    varVersions: seq[int] # this maps variable IDs to their current version.
+    varSyms: seq[PSym] # mirrors 'varVersions'
+    o: Operators
+    hasUnstructedCf: int
+    currOptions: TOptions
+    owner: PSym
+    mangler: seq[PSym]
+    opImplies: PSym
+
+  DrCon = object
+    graph: ModuleGraph
+    idgen: IdGenerator
+    mapping: Table[string, Z3_ast]
+    canonParameterNames: bool
+    assumeUniqueness: bool
+    up: DrnimContext
+
+var
+  assumeUniqueness: bool
+
+proc echoFacts(c: DrnimContext) =
+  echo "FACTS:"
+  for i in 0 ..< c.facts.len:
+    let f = c.facts[i]
+    echo f[0], " version ", int(f[1])
+
+proc isLoc(m: PNode; assumeUniqueness: bool): bool =
+  # We can reason about "locations" and map them to Z3 constants.
+  # For code that is full of "ref" (e.g. the Nim compiler itself) that
+  # is too limiting
+  proc isLet(n: PNode): bool =
+    if n.kind == nkSym:
+      if n.sym.kind in {skLet, skTemp, skForVar}:
+        result = true
+      elif n.sym.kind == skParam and skipTypes(n.sym.typ,
+                                               abstractInst).kind != tyVar:
+        result = true
+
+  var n = m
+  while true:
+    case n.kind
+    of nkDotExpr, nkCheckedFieldExpr, nkObjUpConv, nkObjDownConv, nkHiddenDeref:
+      n = n[0]
+    of nkDerefExpr:
+      n = n[0]
+      if not assumeUniqueness: return false
+    of nkBracketExpr:
+      if isConstExpr(n[1]) or isLet(n[1]) or isConstExpr(n[1].skipConv):
+        n = n[0]
+      else: return
+    of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+      n = n[1]
+    else:
+      break
+  if n.kind == nkSym:
+    case n.sym.kind
+    of skLet, skTemp, skForVar, skParam:
+      result = true
+    #of skParam:
+    #  result = skipTypes(n.sym.typ, abstractInst).kind != tyVar
+    of skResult, skVar:
+      result = {sfAddrTaken} * n.sym.flags == {}
+    else:
+      discard
+
+proc currentVarVersion(c: DrnimContext; s: PSym; begin: VersionScope): int =
+  # we need to take into account both en- and disabled var bindings here,
+  # hence the 'abs' call:
+  result = 0
+  for i in countdown(int(begin)-1, 0):
+    if abs(c.varVersions[i]) == s.id: inc result
+
+proc previousVarVersion(c: DrnimContext; s: PSym; begin: VersionScope): int =
+  # we need to ignore currently disabled var bindings here,
+  # hence no 'abs' call here.
+  result = -1
+  for i in countdown(int(begin)-1, 0):
+    if c.varVersions[i] == s.id: inc result
+
+proc disamb(c: DrnimContext; s: PSym): int =
+  # we group by 's.name.s' to compute the stable name ID.
+  result = 0
+  for i in 0 ..< c.mangler.len:
+    if s == c.mangler[i]: return result
+    if s.name.s == c.mangler[i].name.s: inc result
+  c.mangler.add s
+
+proc stableName(result: var string; c: DrnimContext; n: PNode; version: VersionScope;
+                isOld: bool) =
+  # we can map full Nim expressions like 'f(a, b, c)' to Z3 variables.
+  # We must be careful to select a unique, stable name for these expressions
+  # based on structural equality. 'stableName' helps us with this problem.
+  # In the future we will also use this string for the caching mechanism.
+  case n.kind
+  of nkEmpty, nkNilLit, nkType: discard
+  of nkIdent:
+    result.add n.ident.s
+  of nkSym:
+    result.add n.sym.name.s
+    if n.sym.magic == mNone:
+      let d = disamb(c, n.sym)
+      if d != 0:
+        result.add "`scope="
+        result.addInt d
+      let v = if isOld: c.previousVarVersion(n.sym, version)
+              else: c.currentVarVersion(n.sym, version)
+      if v > 0:
+        result.add '`'
+        result.addInt v
+    else:
+      result.add "`magic="
+      result.addInt ord(n.sym.magic)
+  of nkBindStmt:
+    # we use 'bind x 3' to use the 3rd version of variable 'x'. This
+    # is easier than using 'old' which is position relative.
+    assert n.len == 2
+    assert n[0].kind == nkSym
+    assert n[1].kind == nkIntLit
+    let s = n[0].sym
+    let v = int(n[1].intVal)
+    result.add s.name.s
+    let d = disamb(c, s)
+    if d != 0:
+      result.add "`scope="
+      result.addInt d
+    if v > 0:
+      result.add '`'
+      result.addInt v
+  of nkCharLit..nkUInt64Lit:
+    result.addInt n.intVal
+  of nkFloatLit..nkFloat64Lit:
+    result.addFloat n.floatVal
+  of nkStrLit..nkTripleStrLit:
+    result.add strutils.escape n.strVal
+  of nkDotExpr:
+    stableName(result, c, n[0], version, isOld)
+    result.add '.'
+    stableName(result, c, n[1], version, isOld)
+  of nkBracketExpr:
+    stableName(result, c, n[0], version, isOld)
+    result.add '['
+    stableName(result, c, n[1], version, isOld)
+    result.add ']'
+  of nkCallKinds:
+    if n.len == 2:
+      stableName(result, c, n[1], version, isOld)
+      result.add '.'
+      case getMagic(n)
+      of mLengthArray, mLengthOpenArray, mLengthSeq, mLengthStr:
+        result.add "len"
+      of mHigh:
+        result.add "high"
+      of mLow:
+        result.add "low"
+      else:
+        stableName(result, c, n[0], version, isOld)
+    elif n.kind == nkInfix and n.len == 3:
+      result.add '('
+      stableName(result, c, n[1], version, isOld)
+      result.add ' '
+      stableName(result, c, n[0], version, isOld)
+      result.add ' '
+      stableName(result, c, n[2], version, isOld)
+      result.add ')'
+    else:
+      stableName(result, c, n[0], version, isOld)
+      result.add '('
+      for i in 1..<n.len:
+        if i > 1: result.add ", "
+        stableName(result, c, n[i], version, isOld)
+      result.add ')'
+  else:
+    result.add $n.kind
+    result.add '('
+    for i in 0..<n.len:
+      if i > 0: result.add ", "
+      stableName(result, c, n[i], version, isOld)
+    result.add ')'
+
+proc stableName(c: DrnimContext; n: PNode; version: VersionScope;
+                isOld = false): string =
+  stableName(result, c, n, version, isOld)
+
+template allScopes(c): untyped = VersionScope(c.varVersions.len)
+template currentScope(c): untyped = VersionScope(c.varVersions.len)
+
+proc notImplemented(msg: string) {.noinline.} =
+  when defined(debug):
+    writeStackTrace()
+    echo msg
+  raise newException(CannotMapToZ3Error, "; cannot map to Z3: " & msg)
+
+proc notImplemented(n: PNode) {.noinline.} =
+  when defined(debug):
+    writeStackTrace()
+  raise newException(CannotMapToZ3Error, "; cannot map to Z3: " & $n)
+
+proc notImplemented(t: PType) {.noinline.} =
+  when defined(debug):
+    writeStackTrace()
+  raise newException(CannotMapToZ3Error, "; cannot map to Z3: " & typeToString t)
+
+proc translateEnsures(e, x: PNode): PNode =
+  if e.kind == nkSym and e.sym.kind == skResult:
+    result = x
+  else:
+    result = shallowCopy(e)
+    for i in 0 ..< safeLen(e):
+      result[i] = translateEnsures(e[i], x)
+
+proc typeToZ3(c: DrCon; t: PType): Z3_sort =
+  template ctx: untyped = c.up.z3
+  case t.skipTypes(abstractInst+{tyVar}).kind
+  of tyEnum, tyInt..tyInt64:
+    result = Z3_mk_int_sort(ctx)
+  of tyBool:
+    result = Z3_mk_bool_sort(ctx)
+  of tyFloat..tyFloat128:
+    result = Z3_mk_fpa_sort_double(ctx)
+  of tyChar, tyUInt..tyUInt64:
+    result = Z3_mk_bv_sort(ctx, 64)
+    #cuint(getSize(c.graph.config, t) * 8))
+  else:
+    notImplemented(t)
+
+template binary(op, a, b): untyped =
+  var arr = [a, b]
+  op(ctx, cuint(2), addr(arr[0]))
+
+proc nodeToZ3(c: var DrCon; n: PNode; scope: VersionScope; vars: var seq[PNode]): Z3_ast
+
+proc nodeToDomain(c: var DrCon; n, q: PNode; opAnd: PSym): PNode =
+  assert n.kind == nkInfix
+  let opLe = createMagic(c.graph, c.idgen, "<=", mLeI)
+  case $n[0]
+  of "..":
+    result = buildCall(opAnd, buildCall(opLe, n[1], q), buildCall(opLe, q, n[2]))
+  of "..<":
+    let opLt = createMagic(c.graph, c.idgen, "<", mLtI)
+    result = buildCall(opAnd, buildCall(opLe, n[1], q), buildCall(opLt, q, n[2]))
+  else:
+    notImplemented(n)
+
+template quantorToZ3(fn) {.dirty.} =
+  template ctx: untyped = c.up.z3
+
+  var bound = newSeq[Z3_app](n.len-2)
+  let opAnd = createMagic(c.graph, c.idgen, "and", mAnd)
+  var known: PNode
+  for i in 1..n.len-2:
+    let it = n[i]
+    doAssert it.kind == nkInfix
+    let v = it[1].sym
+    let name = Z3_mk_string_symbol(ctx, v.name.s)
+    let vz3 = Z3_mk_const(ctx, name, typeToZ3(c, v.typ))
+    c.mapping[stableName(c.up, it[1], allScopes(c.up))] = vz3
+    bound[i-1] = Z3_to_app(ctx, vz3)
+    let domain = nodeToDomain(c, it[2], it[1], opAnd)
+    if known == nil:
+      known = domain
+    else:
+      known = buildCall(opAnd, known, domain)
+
+  var dummy: seq[PNode]
+  assert known != nil
+  let x = nodeToZ3(c, buildCall(createMagic(c.graph, c.idgen, "->", mImplies),
+                   known, n[^1]), scope, dummy)
+  result = fn(ctx, 0, bound.len.cuint, addr(bound[0]), 0, nil, x)
+
+proc forallToZ3(c: var DrCon; n: PNode; scope: VersionScope): Z3_ast = quantorToZ3(Z3_mk_forall_const)
+proc existsToZ3(c: var DrCon; n: PNode; scope: VersionScope): Z3_ast = quantorToZ3(Z3_mk_exists_const)
+
+proc paramName(c: DrnimContext; n: PNode): string =
+  case n.sym.kind
+  of skParam: result = "arg" & $n.sym.position
+  of skResult: result = "result"
+  else: result = stableName(c, n, allScopes(c))
+
+proc nodeToZ3(c: var DrCon; n: PNode; scope: VersionScope; vars: var seq[PNode]): Z3_ast =
+  template ctx: untyped = c.up.z3
+  template rec(n): untyped = nodeToZ3(c, n, scope, vars)
+  case n.kind
+  of nkSym:
+    let key = if c.canonParameterNames: paramName(c.up, n) else: stableName(c.up, n, scope)
+    result = c.mapping.getOrDefault(key)
+    if pointer(result) == nil:
+      let name = Z3_mk_string_symbol(ctx, key)
+      result = Z3_mk_const(ctx, name, typeToZ3(c, n.sym.typ))
+      c.mapping[key] = result
+      vars.add n
+  of nkCharLit..nkUInt64Lit:
+    if n.typ != nil and n.typ.skipTypes(abstractInst).kind in {tyInt..tyInt64}:
+      # optimized for the common case
+      result = Z3_mk_int64(ctx, clonglong(n.intval), Z3_mk_int_sort(ctx))
+    elif n.typ != nil and n.typ.kind == tyBool:
+      result = if n.intval != 0: Z3_mk_true(ctx) else: Z3_mk_false(ctx)
+    elif n.typ != nil and isUnsigned(n.typ):
+      result = Z3_mk_unsigned_int64(ctx, cast[uint64](n.intVal), typeToZ3(c, n.typ))
+    else:
+      let zt = if n.typ == nil: Z3_mk_int_sort(ctx) else: typeToZ3(c, n.typ)
+      result = Z3_mk_numeral(ctx, $getOrdValue(n), zt)
+  of nkFloatLit..nkFloat64Lit:
+    result = Z3_mk_fpa_numeral_double(ctx, n.floatVal, Z3_mk_fpa_sort_double(ctx))
+  of nkCallKinds:
+    assert n.len > 0
+    let operator = getMagic(n)
+    case operator
+    of mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
+        mEqStr, mEqSet, mEqCString:
+      result = Z3_mk_eq(ctx, rec n[1], rec n[2])
+    of mLeI, mLeEnum, mLeCh, mLeB, mLePtr, mLeStr:
+      result = Z3_mk_le(ctx, rec n[1], rec n[2])
+    of mLtI, mLtEnum, mLtCh, mLtB, mLtPtr, mLtStr:
+      result = Z3_mk_lt(ctx, rec n[1], rec n[2])
+    of mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq:
+      # len(x) needs the same logic as 'x' itself
+      if isLoc(n[1], c.assumeUniqueness):
+        let key = stableName(c.up, n, scope)
+        result = c.mapping.getOrDefault(key)
+        if pointer(result) == nil:
+          let name = Z3_mk_string_symbol(ctx, key)
+          result = Z3_mk_const(ctx, name, Z3_mk_int_sort(ctx))
+          c.mapping[key] = result
+          vars.add n
+      else:
+        notImplemented(n)
+    of mHigh:
+      let addOpr = createMagic(c.graph, c.idgen, "+", mAddI)
+      let lenOpr = createMagic(c.graph, c.idgen, "len", mLengthOpenArray)
+      let asLenExpr = addOpr.buildCall(lenOpr.buildCall(n[1]), nkIntLit.newIntNode(-1))
+      result = rec asLenExpr
+    of mLow:
+      result = rec lowBound(c.graph.config, n[1])
+    of mAddI, mSucc:
+      result = binary(Z3_mk_add, rec n[1], rec n[2])
+    of mSubI, mPred:
+      result = binary(Z3_mk_sub, rec n[1], rec n[2])
+    of mMulI:
+      result = binary(Z3_mk_mul, rec n[1], rec n[2])
+    of mDivI:
+      result = Z3_mk_div(ctx, rec n[1], rec n[2])
+    of mModI:
+      result = Z3_mk_mod(ctx, rec n[1], rec n[2])
+    of mMaxI:
+      # max(a, b) <=> ite(a < b, b, a)
+      result = Z3_mk_ite(ctx, Z3_mk_lt(ctx, rec n[1], rec n[2]),
+        rec n[2], rec n[1])
+    of mMinI:
+      # min(a, b) <=> ite(a < b, a, b)
+      result = Z3_mk_ite(ctx, Z3_mk_lt(ctx, rec n[1], rec n[2]),
+        rec n[1], rec n[2])
+    of mLeU:
+      result = Z3_mk_bvule(ctx, rec n[1], rec n[2])
+    of mLtU:
+      result = Z3_mk_bvult(ctx, rec n[1], rec n[2])
+    of mAnd:
+      # 'a and b' <=> ite(a, b, false)
+      result = Z3_mk_ite(ctx, rec n[1], rec n[2], Z3_mk_false(ctx))
+      #result = binary(Z3_mk_and, rec n[1], rec n[2])
+    of mOr:
+      result = Z3_mk_ite(ctx, rec n[1], Z3_mk_true(ctx), rec n[2])
+      #result = binary(Z3_mk_or, rec n[1], rec n[2])
+    of mXor:
+      result = Z3_mk_xor(ctx, rec n[1], rec n[2])
+    of mNot:
+      result = Z3_mk_not(ctx, rec n[1])
+    of mImplies:
+      result = Z3_mk_implies(ctx, rec n[1], rec n[2])
+    of mIff:
+      result = Z3_mk_iff(ctx, rec n[1], rec n[2])
+    of mForall:
+      result = forallToZ3(c, n, scope)
+    of mExists:
+      result = existsToZ3(c, n, scope)
+    of mLeF64:
+      result = Z3_mk_fpa_leq(ctx, rec n[1], rec n[2])
+    of mLtF64:
+      result = Z3_mk_fpa_lt(ctx, rec n[1], rec n[2])
+    of mAddF64:
+      result = Z3_mk_fpa_add(ctx, Z3_mk_fpa_round_nearest_ties_to_even(ctx), rec n[1], rec n[2])
+    of mSubF64:
+      result = Z3_mk_fpa_sub(ctx, Z3_mk_fpa_round_nearest_ties_to_even(ctx), rec n[1], rec n[2])
+    of mMulF64:
+      result = Z3_mk_fpa_mul(ctx, Z3_mk_fpa_round_nearest_ties_to_even(ctx), rec n[1], rec n[2])
+    of mDivF64:
+      result = Z3_mk_fpa_div(ctx, Z3_mk_fpa_round_nearest_ties_to_even(ctx), rec n[1], rec n[2])
+    of mShrI:
+      # XXX handle conversions from int to uint here somehow
+      result = Z3_mk_bvlshr(ctx, rec n[1], rec n[2])
+    of mAshrI:
+      result = Z3_mk_bvashr(ctx, rec n[1], rec n[2])
+    of mShlI:
+      result = Z3_mk_bvshl(ctx, rec n[1], rec n[2])
+    of mBitandI:
+      result = Z3_mk_bvand(ctx, rec n[1], rec n[2])
+    of mBitorI:
+      result = Z3_mk_bvor(ctx, rec n[1], rec n[2])
+    of mBitxorI:
+      result = Z3_mk_bvxor(ctx, rec n[1], rec n[2])
+    of mOrd, mChr:
+      result = rec n[1]
+    of mOld:
+      let key = if c.canonParameterNames: (paramName(c.up, n[1]) & ".old")
+                else: stableName(c.up, n[1], scope, isOld = true)
+      result = c.mapping.getOrDefault(key)
+      if pointer(result) == nil:
+        let name = Z3_mk_string_symbol(ctx, key)
+        result = Z3_mk_const(ctx, name, typeToZ3(c, n[1].typ))
+        c.mapping[key] = result
+        # XXX change the logic in `addRangeInfo` for this
+        #vars.add n
+
+    else:
+      # sempass2 adds some 'fact' like 'x = f(a, b)' (see addAsgnFact)
+      # 'f(a, b)' can have an .ensures annotation and we need to make use
+      # of this information.
+      # we need to map 'f(a, b)' to a Z3 variable of this name
+      let op = n[0].typ
+      if op != nil and op.n != nil and op.n.len > 0 and op.n[0].kind == nkEffectList and
+          ensuresEffects < op.n[0].len:
+        let ensures = op.n[0][ensuresEffects]
+        if ensures != nil and ensures.kind != nkEmpty:
+          let key = stableName(c.up, n, scope)
+          result = c.mapping.getOrDefault(key)
+          if pointer(result) == nil:
+            let name = Z3_mk_string_symbol(ctx, key)
+            result = Z3_mk_const(ctx, name, typeToZ3(c, n.typ))
+            c.mapping[key] = result
+            vars.add n
+
+      if pointer(result) == nil:
+        notImplemented(n)
+  of nkStmtListExpr, nkPar:
+    var isTrivial = true
+    for i in 0..n.len-2:
+      isTrivial = isTrivial and n[i].kind in {nkEmpty, nkCommentStmt}
+    if isTrivial:
+      result = rec n[^1]
+    else:
+      notImplemented(n)
+  of nkHiddenDeref:
+    result = rec n[0]
+  else:
+    if isLoc(n, c.assumeUniqueness):
+      let key = stableName(c.up, n, scope)
+      result = c.mapping.getOrDefault(key)
+      if pointer(result) == nil:
+        let name = Z3_mk_string_symbol(ctx, key)
+        result = Z3_mk_const(ctx, name, typeToZ3(c, n.typ))
+        c.mapping[key] = result
+        vars.add n
+    else:
+      notImplemented(n)
+
+proc addRangeInfo(c: var DrCon, n: PNode; scope: VersionScope, res: var seq[Z3_ast]) =
+  var cmpOp = mLeI
+  if n.typ != nil:
+    cmpOp =
+      case n.typ.skipTypes(abstractInst).kind
+      of tyFloat..tyFloat128: mLeF64
+      of tyChar, tyUInt..tyUInt64: mLeU
+      else: mLeI
+
+  var lowBound, highBound: PNode
+  if n.kind == nkSym:
+    let v = n.sym
+    let t = v.typ.skipTypes(abstractInst - {tyRange})
+
+    case t.kind
+    of tyRange:
+      lowBound = t.n[0]
+      highBound = t.n[1]
+    of tyFloat..tyFloat128:
+      # no range information for non-range'd floats
+      return
+    of tyUInt..tyUInt64, tyChar:
+      lowBound = newIntNode(nkUInt64Lit, firstOrd(nil, v.typ))
+      lowBound.typ = v.typ
+      highBound = newIntNode(nkUInt64Lit, lastOrd(nil, v.typ))
+      highBound.typ = v.typ
+    of tyInt..tyInt64, tyEnum:
+      lowBound = newIntNode(nkInt64Lit, firstOrd(nil, v.typ))
+      highBound = newIntNode(nkInt64Lit, lastOrd(nil, v.typ))
+    else:
+      # no range information available:
+      return
+  elif n.kind in nkCallKinds and n.len == 2 and n[0].kind == nkSym and
+      n[0].sym.magic in {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq}:
+    # we know it's a 'len(x)' expression and we seek to teach
+    # Z3 that the result is >= 0 and <= high(int).
+    doAssert n.kind in nkCallKinds
+    doAssert n[0].kind == nkSym
+    doAssert n.len == 2
+
+    lowBound = newIntNode(nkInt64Lit, 0)
+    if n.typ != nil:
+      highBound = newIntNode(nkInt64Lit, lastOrd(nil, n.typ))
+    else:
+      highBound = newIntNode(nkInt64Lit, high(int64))
+  else:
+    let op = n[0].typ
+    if op != nil and op.n != nil and op.n.len > 0 and op.n[0].kind == nkEffectList and
+        ensuresEffects < op.n[0].len:
+      let ensures = op.n[0][ensuresEffects]
+      if ensures != nil and ensures.kind != nkEmpty:
+        var dummy: seq[PNode]
+        res.add nodeToZ3(c, translateEnsures(ensures, n), scope, dummy)
+    return
+
+  let x = newTree(nkInfix, newSymNode createMagic(c.graph, c.idgen, "<=", cmpOp), lowBound, n)
+  let y = newTree(nkInfix, newSymNode createMagic(c.graph, c.idgen, "<=", cmpOp), n, highBound)
+
+  var dummy: seq[PNode]
+  res.add nodeToZ3(c, x, scope, dummy)
+  res.add nodeToZ3(c, y, scope, dummy)
+
+proc on_err(ctx: Z3_context, e: Z3_error_code) {.nimcall.} =
+  #writeStackTrace()
+  let msg = $Z3_get_error_msg(ctx, e)
+  raise newException(Z3Exception, msg)
+
+proc forall(ctx: Z3_context; vars: seq[Z3_ast]; assumption, body: Z3_ast): Z3_ast =
+  let x = Z3_mk_implies(ctx, assumption, body)
+  if vars.len > 0:
+    var bound: seq[Z3_app]
+    for v in vars: bound.add Z3_to_app(ctx, v)
+    result = Z3_mk_forall_const(ctx, 0, bound.len.cuint, addr(bound[0]), 0, nil, x)
+  else:
+    result = x
+
+proc conj(ctx: Z3_context; conds: seq[Z3_ast]): Z3_ast =
+  if conds.len > 0:
+    result = Z3_mk_and(ctx, cuint(conds.len), unsafeAddr conds[0])
+  else:
+    result = Z3_mk_true(ctx)
+
+proc setupZ3(): Z3_context =
+  let cfg = Z3_mk_config()
+  when false:
+    Z3_set_param_value(cfg, "timeout", "1000")
+  Z3_set_param_value(cfg, "model", "true")
+  result = Z3_mk_context(cfg)
+  Z3_del_config(cfg)
+  Z3_set_error_handler(result, on_err)
+
+proc proofEngineAux(c: var DrCon; assumptions: seq[(PNode, VersionScope)];
+                    toProve: (PNode, VersionScope)): (bool, string) =
+  c.mapping = initTable[string, Z3_ast]()
+  try:
+
+    #[
+    For example, let's have these facts:
+
+      i < 10
+      i > 0
+
+    Question:
+
+      i + 3 < 13
+
+    What we need to produce:
+
+    forall(i, (i < 10) & (i > 0) -> (i + 3 < 13))
+
+    ]#
+
+    var collectedVars: seq[PNode]
+
+    template ctx(): untyped = c.up.z3
+
+    let solver = Z3_mk_solver(ctx)
+    var lhs: seq[Z3_ast]
+    for assumption in items(assumptions):
+      try:
+        let za = nodeToZ3(c, assumption[0], assumption[1], collectedVars)
+        #Z3_solver_assert ctx, solver, za
+        lhs.add za
+      except CannotMapToZ3Error:
+        discard "ignore a fact we cannot map to Z3"
+
+    let z3toProve = nodeToZ3(c, toProve[0], toProve[1], collectedVars)
+    for v in collectedVars:
+      addRangeInfo(c, v, toProve[1], lhs)
+
+    # to make Z3 produce nice counterexamples, we try to prove the
+    # negation of our conjecture and see if it's Z3_L_FALSE
+    let fa = Z3_mk_not(ctx, Z3_mk_implies(ctx, conj(ctx, lhs), z3toProve))
+
+    #Z3_mk_not(ctx, forall(ctx, collectedVars, conj(ctx, lhs), z3toProve))
+
+    when defined(dz3):
+      echo "toProve: ", Z3_ast_to_string(ctx, fa), " ", c.graph.config $ toProve[0].info, " ", int(toProve[1])
+    Z3_solver_assert ctx, solver, fa
+
+    let z3res = Z3_solver_check(ctx, solver)
+    result[0] = z3res == Z3_L_FALSE
+    result[1] = ""
+    if not result[0]:
+      let counterex = strip($Z3_model_to_string(ctx, Z3_solver_get_model(ctx, solver)))
+      if counterex.len > 0:
+        result[1].add "; counter example: " & counterex
+  except ValueError:
+    result[0] = false
+    result[1] = getCurrentExceptionMsg()
+
+proc proofEngine(ctx: DrnimContext; assumptions: seq[(PNode, VersionScope)];
+                 toProve: (PNode, VersionScope)): (bool, string) =
+  var c: DrCon
+  c.graph = ctx.graph
+  c.idgen = ctx.idgen
+  c.assumeUniqueness = assumeUniqueness
+  c.up = ctx
+  result = proofEngineAux(c, assumptions, toProve)
+
+proc skipAddr(n: PNode): PNode {.inline.} =
+  (if n.kind == nkHiddenAddr: n[0] else: n)
+
+proc translateReq(r, call: PNode): PNode =
+  if r.kind == nkSym and r.sym.kind == skParam:
+    if r.sym.position+1 < call.len:
+      result = call[r.sym.position+1].skipAddr
+    else:
+      notImplemented("no argument given for formal parameter: " & r.sym.name.s)
+  else:
+    result = shallowCopy(r)
+    for i in 0 ..< safeLen(r):
+      result[i] = translateReq(r[i], call)
+
+proc requirementsCheck(ctx: DrnimContext; assumptions: seq[(PNode, VersionScope)];
+                      call, requirement: PNode): (bool, string) =
+  try:
+    let r = translateReq(requirement, call)
+    result = proofEngine(ctx, assumptions, (r, ctx.currentScope))
+  except ValueError:
+    result[0] = false
+    result[1] = getCurrentExceptionMsg()
+
+proc compatibleProps(graph: ModuleGraph; formal, actual: PType): bool {.nimcall.} =
+  #[
+  Thoughts on subtyping rules for 'proc' types:
+
+    proc a(y: int) {.requires: y > 0.}  # a is 'weaker' than F
+    # 'requires' must be weaker (or equal)
+    # 'ensures'  must be stronger (or equal)
+
+    # a 'is weaker than' b iff  b -> a
+    # a 'is stronger than' b iff a -> b
+    # --> We can use Z3 to compute whether 'var x: T = q' is valid
+
+    type
+      F = proc (y: int) {.requires: y > 5.}
+
+    var
+      x: F = a # valid?
+  ]#
+  proc isEmpty(n: PNode): bool {.inline.} = n == nil or n.safeLen == 0
+
+  result = true
+  if formal.n != nil and formal.n.len > 0 and formal.n[0].kind == nkEffectList and
+      ensuresEffects < formal.n[0].len:
+
+    let frequires = formal.n[0][requiresEffects]
+    let fensures = formal.n[0][ensuresEffects]
+
+    if actual.n != nil and actual.n.len > 0 and actual.n[0].kind == nkEffectList and
+        ensuresEffects < actual.n[0].len:
+      let arequires = actual.n[0][requiresEffects]
+      let aensures = actual.n[0][ensuresEffects]
+
+      var c: DrCon
+      c.graph = graph
+      c.idgen = graph.idgen
+      c.canonParameterNames = true
+      try:
+        c.up = DrnimContext(z3: setupZ3(), o: initOperators(graph), graph: graph, owner: nil,
+          opImplies: createMagic(graph, c.idgen, "->", mImplies))
+        template zero: untyped = VersionScope(0)
+        if not frequires.isEmpty:
+          result = not arequires.isEmpty and proofEngineAux(c, @[(frequires, zero)], (arequires, zero))[0]
+
+        if result:
+          if not fensures.isEmpty:
+            result = not aensures.isEmpty and proofEngineAux(c, @[(aensures, zero)], (fensures, zero))[0]
+      finally:
+        Z3_del_context(c.up.z3)
+    else:
+      # formal has requirements but 'actual' has none, so make it
+      # incompatible. XXX What if the requirement only mentions that
+      # we already know from the type system?
+      result = frequires.isEmpty and fensures.isEmpty
+
+template config(c: typed): untyped = c.graph.config
+
+proc addFact(c: DrnimContext; n: PNode) =
+  let v = c.currentScope
+  if n.kind in nkCallKinds and n[0].kind == nkSym and n[0].sym.magic in {mOr, mAnd}:
+    c.addFact(n[1])
+  c.facts.add((n, v))
+
+proc neg(c: DrnimContext; n: PNode): PNode =
+  result = newNodeI(nkCall, n.info, 2)
+  result[0] = newSymNode(c.o.opNot)
+  result[1] = n
+
+proc addFactNeg(c: DrnimContext; n: PNode) =
+  addFact(c, neg(c, n))
+
+proc combineFacts(c: DrnimContext; a, b: PNode): PNode =
+  if a == nil:
+    result = b
+  else:
+    result = buildCall(c.o.opAnd, a, b)
+
+proc prove(c: DrnimContext; prop: PNode): bool =
+  let (success, m) = proofEngine(c, c.facts, (prop, c.currentScope))
+  if not success:
+    message(c.config, prop.info, warnStaticIndexCheck, "cannot prove: " & $prop & m)
+  result = success
+
+proc traversePragmaStmt(c: DrnimContext, n: PNode) =
+  for it in n:
+    if it.kind == nkExprColonExpr:
+      let pragma = whichPragma(it)
+      if pragma == wAssume:
+        addFact(c, it[1])
+      elif pragma == wInvariant or pragma == wAssert:
+        if prove(c, it[1]):
+          addFact(c, it[1])
+        else:
+          echoFacts(c)
+
+proc requiresCheck(c: DrnimContext, call: PNode; op: PType) =
+  assert op.n[0].kind == nkEffectList
+  if requiresEffects < op.n[0].len:
+    let requires = op.n[0][requiresEffects]
+    if requires != nil and requires.kind != nkEmpty:
+      # we need to map the call arguments to the formal parameters used inside
+      # 'requires':
+      let (success, m) = requirementsCheck(c, c.facts, call, requires)
+      if not success:
+        message(c.config, call.info, warnStaticIndexCheck, "cannot prove: " & $requires & m)
+
+proc freshVersion(c: DrnimContext; arg: PNode) =
+  let v = getRoot(arg)
+  if v != nil:
+    c.varVersions.add v.id
+    c.varSyms.add v
+
+proc translateEnsuresFromCall(c: DrnimContext, e, call: PNode): PNode =
+  if e.kind in nkCallKinds and e[0].kind == nkSym and e[0].sym.magic == mOld:
+    assert e[1].kind == nkSym and e[1].sym.kind == skParam
+    let param = e[1].sym
+    let arg = call[param.position+1].skipAddr
+    result = buildCall(e[0].sym, arg)
+  elif e.kind == nkSym and e.sym.kind == skParam:
+    let param = e.sym
+    let arg = call[param.position+1].skipAddr
+    result = arg
+  else:
+    result = shallowCopy(e)
+    for i in 0 ..< safeLen(e): result[i] = translateEnsuresFromCall(c, e[i], call)
+
+proc collectEnsuredFacts(c: DrnimContext, call: PNode; op: PType) =
+  assert op.n[0].kind == nkEffectList
+  for i in 1 ..< min(call.len, op.len):
+    if op[i].kind == tyVar:
+      freshVersion(c, call[i].skipAddr)
+
+  if ensuresEffects < op.n[0].len:
+    let ensures = op.n[0][ensuresEffects]
+    if ensures != nil and ensures.kind != nkEmpty:
+      addFact(c, translateEnsuresFromCall(c, ensures, call))
+
+proc checkLe(c: DrnimContext, a, b: PNode) =
+  var cmpOp = mLeI
+  if a.typ != nil:
+    case a.typ.skipTypes(abstractInst).kind
+    of tyFloat..tyFloat128: cmpOp = mLeF64
+    of tyChar, tyUInt..tyUInt64: cmpOp = mLeU
+    else: discard
+
+  let cmp = newTree(nkInfix, newSymNode createMagic(c.graph, c.idgen, "<=", cmpOp), a, b)
+  cmp.info = a.info
+  discard prove(c, cmp)
+
+proc checkBounds(c: DrnimContext; arr, idx: PNode) =
+  checkLe(c, lowBound(c.config, arr), idx)
+  checkLe(c, idx, highBound(c.config, arr, c.o))
+
+proc checkRange(c: DrnimContext; value: PNode; typ: PType) =
+  let t = typ.skipTypes(abstractInst - {tyRange})
+  if t.kind == tyRange:
+    let lowBound = copyTree(t.n[0])
+    lowBound.info = value.info
+    let highBound = copyTree(t.n[1])
+    highBound.info = value.info
+    checkLe(c, lowBound, value)
+    checkLe(c, value, highBound)
+
+proc addAsgnFact*(c: DrnimContext, key, value: PNode) =
+  var fact = newNodeI(nkCall, key.info, 3)
+  fact[0] = newSymNode(c.o.opEq)
+  fact[1] = key
+  fact[2] = value
+  c.facts.add((fact, c.currentScope))
+
+proc traverse(c: DrnimContext; n: PNode)
+
+proc traverseTryStmt(c: DrnimContext; n: PNode) =
+  traverse(c, n[0])
+  let oldFacts = c.facts.len
+  for i in 1 ..< n.len:
+    traverse(c, n[i].lastSon)
+  setLen(c.facts, oldFacts)
+
+proc traverseCase(c: DrnimContext; n: PNode) =
+  traverse(c, n[0])
+  let oldFacts = c.facts.len
+  for i in 1 ..< n.len:
+    traverse(c, n[i].lastSon)
+  # XXX make this as smart as 'if elif'
+  setLen(c.facts, oldFacts)
+
+proc disableVarVersions(c: DrnimContext; until: int) =
+  for i in until..<c.varVersions.len:
+    c.varVersions[i] = - abs(c.varVersions[i])
+
+proc varOfVersion(c: DrnimContext; x: PSym; scope: int): PNode =
+  let version = currentVarVersion(c, x, VersionScope(scope))
+  result = newTree(nkBindStmt, newSymNode(x), newIntNode(nkIntLit, version))
+
+proc traverseIf(c: DrnimContext; n: PNode) =
+  #[ Consider this example::
+
+    var x = y   # x'0
+    if a:
+      inc x     # x'1 == x'0 + 1
+    elif b:
+      inc x, 2  # x'2 == x'0 + 2
+
+  afterwards we know this is fact::
+
+    x'3 = Phi(x'0, x'1, x'2)
+
+  So a Phi node from SSA representation is an 'or' formula like::
+
+    x'3 == x'1 or x'3 == x'2 or x'3 == x'0
+
+  However, this loses some information. The formula that doesn't
+  lose information is::
+
+    (a -> (x'3 == x'1)) and
+    ((not a and b) -> (x'3 == x'2)) and
+    ((not a and not b) -> (x'3 == x'0))
+
+  (Where ``->`` is the logical implication.)
+
+  In addition to the Phi information we also know the 'facts'
+  computed by the branches, for example::
+
+    if a:
+      factA
+    elif b:
+      factB
+    else:
+      factC
+
+    (a -> factA) and
+    ((not a and b) -> factB) and
+    ((not a and not b) -> factC)
+
+  We can combine these two aspects by producing the following facts
+  after each branch::
+
+    var x = y   # x'0
+    if a:
+      inc x     # x'1 == x'0 + 1
+      # also:     x'1 == x'final
+    elif b:
+      inc x, 2  # x'2 == x'0 + 2
+      # also:     x'2 == x'final
+    else:
+      # also:     x'0 == x'final
+
+  ]#
+  let oldFacts = c.facts.len
+  let oldVars = c.varVersions.len
+  var newFacts: seq[PNode]
+  var branches = newSeq[(PNode, int)](n.len) # (cond, newVars) pairs
+  template condVersion(): untyped = VersionScope(oldVars)
+
+  for i in 0..<n.len:
+    let branch = n[i]
+    setLen(c.facts, oldFacts)
+
+    var cond = PNode(nil)
+    for j in 0..i-1:
+      addFactNeg(c, n[j][0])
+      cond = combineFacts(c, cond, neg(c, n[j][0]))
+    if branch.len > 1:
+      addFact(c, branch[0])
+      cond = combineFacts(c, cond, branch[0])
+
+    for i in 0..<branch.len:
+      traverse(c, branch[i])
+
+    assert cond != nil
+    branches[i] = (cond, c.varVersions.len)
+
+    var newInfo = PNode(nil)
+    for f in oldFacts..<c.facts.len:
+      newInfo = combineFacts(c, newInfo, c.facts[f][0])
+    if newInfo != nil:
+      newFacts.add buildCall(c.opImplies, cond, newInfo)
+
+    disableVarVersions(c, oldVars)
+
+  setLen(c.facts, oldFacts)
+  for f in newFacts: c.facts.add((f, condVersion))
+  # build the 'Phi' information:
+  let varsWithoutFinals = c.varVersions.len
+  var mutatedVars = initIntSet()
+  for i in oldVars ..< varsWithoutFinals:
+    let vv = c.varVersions[i]
+    if not mutatedVars.containsOrIncl(vv):
+      c.varVersions.add vv
+      c.varSyms.add c.varSyms[i]
+
+  var prevIdx = oldVars
+  for i in 0 ..< branches.len:
+    for v in prevIdx .. branches[i][1] - 1:
+      c.facts.add((buildCall(c.opImplies, branches[i][0],
+        buildCall(c.o.opEq, varOfVersion(c, c.varSyms[v], branches[i][1]), newSymNode(c.varSyms[v]))),
+        condVersion))
+    prevIdx = branches[i][1]
+
+proc traverseBlock(c: DrnimContext; n: PNode) =
+  traverse(c, n)
+
+proc addFactLe(c: DrnimContext; a, b: PNode) =
+  c.addFact c.o.opLe.buildCall(a, b)
+
+proc addFactLt(c: DrnimContext; a, b: PNode) =
+  c.addFact c.o.opLt.buildCall(a, b)
+
+proc ensuresCheck(c: DrnimContext; owner: PSym) =
+  if owner.typ != nil and owner.typ.kind == tyProc and owner.typ.n != nil:
+    let n = owner.typ.n
+    if n.len > 0 and n[0].kind == nkEffectList and ensuresEffects < n[0].len:
+      let ensures = n[0][ensuresEffects]
+      if ensures != nil and ensures.kind != nkEmpty:
+        discard prove(c, ensures)
+
+proc traverseAsgn(c: DrnimContext; n: PNode) =
+  traverse(c, n[0])
+  traverse(c, n[1])
+
+  proc replaceByOldParams(fact, le: PNode): PNode =
+    if guards.sameTree(fact, le):
+      result = newNodeIT(nkCall, fact.info, fact.typ)
+      result.add newSymNode createMagic(c.graph, c.idgen, "old", mOld)
+      result.add fact
+    else:
+      result = shallowCopy(fact)
+      for i in 0 ..< safeLen(fact):
+        result[i] = replaceByOldParams(fact[i], le)
+
+  freshVersion(c, n[0])
+  addAsgnFact(c, n[0], replaceByOldParams(n[1], n[0]))
+  when defined(debug):
+    echoFacts(c)
+
+proc traverse(c: DrnimContext; n: PNode) =
+  case n.kind
+  of nkEmpty..nkNilLit:
+    discard "nothing to do"
+  of nkRaiseStmt, nkBreakStmt, nkContinueStmt:
+    inc c.hasUnstructedCf
+    for i in 0..<n.safeLen:
+      traverse(c, n[i])
+  of nkReturnStmt:
+    for i in 0 ..< n.safeLen:
+      traverse(c, n[i])
+    ensuresCheck(c, c.owner)
+  of nkCallKinds:
+    # p's effects are ours too:
+    var a = n[0]
+    let op = a.typ
+    if op != nil and op.kind == tyProc and op.n[0].kind == nkEffectList:
+      requiresCheck(c, n, op)
+      collectEnsuredFacts(c, n, op)
+    if a.kind == nkSym:
+      case a.sym.magic
+      of mNew, mNewFinalize, mNewSeq:
+        # may not look like an assignment, but it is:
+        let arg = n[1]
+        freshVersion(c, arg)
+        traverse(c, arg)
+        let x = newNodeIT(nkObjConstr, arg.info, arg.typ)
+        x.add arg
+        addAsgnFact(c, arg, x)
+      of mArrGet, mArrPut:
+        #if optStaticBoundsCheck in c.currOptions: checkBounds(c, n[1], n[2])
+        discard
+      else:
+        discard
+
+    for i in 0..<n.safeLen:
+      traverse(c, n[i])
+  of nkDotExpr:
+    #guardDotAccess(c, n)
+    for i in 0..<n.len: traverse(c, n[i])
+  of nkCheckedFieldExpr:
+    traverse(c, n[0])
+    #checkFieldAccess(c.facts, n, c.config)
+  of nkTryStmt: traverseTryStmt(c, n)
+  of nkPragma: traversePragmaStmt(c, n)
+  of nkAsgn, nkFastAsgn: traverseAsgn(c, n)
+  of nkVarSection, nkLetSection:
+    for child in n:
+      let last = lastSon(child)
+      if last.kind != nkEmpty: traverse(c, last)
+      if child.kind == nkIdentDefs and last.kind != nkEmpty:
+        for i in 0..<child.len-2:
+          addAsgnFact(c, child[i], last)
+      elif child.kind == nkVarTuple and last.kind != nkEmpty:
+        for i in 0..<child.len-1:
+          if child[i].kind == nkEmpty or
+              child[i].kind == nkSym and child[i].sym.name.s == "_":
+            discard "anon variable"
+          elif last.kind in {nkPar, nkTupleConstr}:
+            addAsgnFact(c, child[i], last[i])
+  of nkConstSection:
+    for child in n:
+      let last = lastSon(child)
+      traverse(c, last)
+  of nkCaseStmt: traverseCase(c, n)
+  of nkWhen, nkIfStmt, nkIfExpr: traverseIf(c, n)
+  of nkBlockStmt, nkBlockExpr: traverseBlock(c, n[1])
+  of nkWhileStmt:
+    # 'while true' loop?
+    if isTrue(n[0]):
+      traverseBlock(c, n[1])
+    else:
+      let oldFacts = c.facts.len
+      addFact(c, n[0])
+      traverse(c, n[0])
+      traverse(c, n[1])
+      setLen(c.facts, oldFacts)
+  of nkForStmt, nkParForStmt:
+    # we are very conservative here and assume the loop is never executed:
+    let oldFacts = c.facts.len
+    let iterCall = n[n.len-2]
+    if optStaticBoundsCheck in c.currOptions and iterCall.kind in nkCallKinds:
+      let op = iterCall[0]
+      if op.kind == nkSym and fromSystem(op.sym):
+        let iterVar = n[0]
+        case op.sym.name.s
+        of "..", "countup", "countdown":
+          let lower = iterCall[1]
+          let upper = iterCall[2]
+          # for i in 0..n   means  0 <= i and i <= n. Countdown is
+          # the same since only the iteration direction changes.
+          addFactLe(c, lower, iterVar)
+          addFactLe(c, iterVar, upper)
+        of "..<":
+          let lower = iterCall[1]
+          let upper = iterCall[2]
+          addFactLe(c, lower, iterVar)
+          addFactLt(c, iterVar, upper)
+        else: discard
+
+    for i in 0..<n.len-2:
+      let it = n[i]
+      traverse(c, it)
+    let loopBody = n[^1]
+    traverse(c, iterCall)
+    traverse(c, loopBody)
+    setLen(c.facts, oldFacts)
+  of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
+      nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
+    discard
+  of nkCast:
+    if n.len == 2:
+      traverse(c, n[1])
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+    if n.len == 2:
+      traverse(c, n[1])
+      if optStaticBoundsCheck in c.currOptions:
+        checkRange(c, n[1], n.typ)
+  of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
+    if n.len == 1:
+      traverse(c, n[0])
+      if optStaticBoundsCheck in c.currOptions:
+        checkRange(c, n[0], n.typ)
+  of nkBracketExpr:
+    if optStaticBoundsCheck in c.currOptions and n.len == 2:
+      if n[0].typ != nil and skipTypes(n[0].typ, abstractVar).kind != tyTuple:
+        checkBounds(c, n[0], n[1])
+    for i in 0 ..< n.len: traverse(c, n[i])
+  else:
+    for i in 0 ..< n.len: traverse(c, n[i])
+
+proc strongSemCheck(graph: ModuleGraph; owner: PSym; n: PNode) =
+  var c = DrnimContext()
+  c.currOptions = graph.config.options + owner.options
+  if optStaticBoundsCheck in c.currOptions:
+    c.z3 = setupZ3()
+    c.o = initOperators(graph)
+    c.graph = graph
+    c.idgen = graph.idgen
+    c.owner = owner
+    c.opImplies = createMagic(c.graph, c.idgen, "->", mImplies)
+    try:
+      traverse(c, n)
+      ensuresCheck(c, owner)
+    finally:
+      Z3_del_context(c.z3)
+
+
+proc mainCommand(graph: ModuleGraph) =
+  let conf = graph.config
+  conf.lastCmdTime = epochTime()
+
+  graph.strongSemCheck = strongSemCheck
+  graph.compatibleProps = compatibleProps
+
+  graph.config.setErrorMaxHighMaybe
+  defineSymbol(graph.config.symbols, "nimcheck")
+  defineSymbol(graph.config.symbols, "nimDrNim")
+
+  registerPass graph, verbosePass
+  registerPass graph, semPass
+  compileProject(graph)
+  if conf.errorCounter == 0:
+    genSuccessX(graph.config)
+
+proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
+  var p = parseopt.initOptParser(cmd)
+  var argsCount = 1
+
+  config.commandLine.setLen 0
+  config.setCmd cmdCheck
+  while true:
+    parseopt.next(p)
+    case p.kind
+    of cmdEnd: break
+    of cmdLongOption, cmdShortOption:
+      config.commandLine.add " "
+      config.commandLine.addCmdPrefix p.kind
+      config.commandLine.add p.key.quoteShell # quoteShell to be future proof
+      if p.val.len > 0:
+        config.commandLine.add ':'
+        config.commandLine.add p.val.quoteShell
+
+      if p.key == " ":
+        p.key = "-"
+        if processArgument(pass, p, argsCount, config): break
+      else:
+        case p.key.normalize
+        of "assumeunique":
+          assumeUniqueness = true
+        else:
+          processSwitch(pass, p, config)
+    of cmdArgument:
+      config.commandLine.add " "
+      config.commandLine.add p.key.quoteShell
+      if processArgument(pass, p, argsCount, config): break
+  if pass == passCmd2:
+    if {optRun, optWasNimscript} * config.globalOptions == {} and
+        config.arguments.len > 0 and config.cmd notin {cmdTcc, cmdNimscript}:
+      rawMessage(config, errGenerated, errArgsNeedRunOption)
+
+proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
+  incl conf.options, optStaticBoundsCheck
+  let self = NimProg(
+    supportsStdinFile: true,
+    processCmdLine: processCmdLine
+  )
+  self.initDefinesProg(conf, "drnim")
+  if paramCount() == 0:
+    helpOnError(conf)
+    return
+
+  self.processCmdLineAndProjectPath(conf)
+  var graph = newModuleGraph(cache, conf)
+  if not self.loadConfigsAndProcessCmdLine(cache, conf, graph): return
+  mainCommand(graph)
+  if conf.hasHint(hintGCStats): echo(GC_getStatistics())
+
+when compileOption("gc", "refc"):
+  # the new correct mark&sweep collector is too slow :-/
+  GC_disableMarkAndSweep()
+
+when not defined(selftest):
+  let conf = newConfigRef()
+  handleCmdLine(newIdentCache(), conf)
+  when declared(GC_setMaxPause):
+    echo GC_getStatistics()
+  msgQuit(int8(conf.errorCounter > 0))
diff --git a/drnim/nim.cfg b/drnim/nim.cfg
new file mode 100644
index 000000000..58c1725d9
--- /dev/null
+++ b/drnim/nim.cfg
@@ -0,0 +1,18 @@
+# Special configuration file for the Nim project
+
+hint[XDeclaredButNotUsed]:off
+
+define:booting
+define:nimcore
+define:drnim
+
+@if windows:
+  cincludes: "$lib/wrappers/libffi/common"
+@end
+
+define:useStdoutAsStdmsg
+
+--path: "../../nimz3/src"
+--path: "../dist/nimz3/src"
+
+#define:useNodeIds
diff --git a/drnim/tests/config.nims b/drnim/tests/config.nims
new file mode 100644
index 000000000..346b0b4e6
--- /dev/null
+++ b/drnim/tests/config.nims
@@ -0,0 +1,10 @@
+switch("path", "$nim/testament/lib") # so we can `import stdtest/foo` in this dir
+
+## prevent common user config settings to interfere with testament expectations
+## Indifidual tests can override this if needed to test for these options.
+switch("colors", "off")
+switch("filenames", "canonical")
+switch("excessiveStackTrace", "off")
+
+# we only want to check the marked parts in the tests:
+switch("staticBoundChecks", "off")
diff --git a/drnim/tests/tbasic_array_index.nim b/drnim/tests/tbasic_array_index.nim
new file mode 100644
index 000000000..8ae019e78
--- /dev/null
+++ b/drnim/tests/tbasic_array_index.nim
@@ -0,0 +1,51 @@
+discard """
+  nimout: '''tbasic_array_index.nim(26, 17) Warning: cannot prove: 0 <= len(a) - 4; counter example: a.len -> 0 [IndexCheck]
+tbasic_array_index.nim(32, 5) Warning: cannot prove: 4.0 <= 1.0 [IndexCheck]
+tbasic_array_index.nim(38, 36) Warning: cannot prove: a <= 10'u32; counter example: a -> #x000000000000000b
+'''
+  cmd: "drnim $file"
+  action: "compile"
+"""
+
+{.push staticBoundChecks: defined(nimDrNim).}
+
+proc takeNat(n: Natural) =
+  discard
+
+proc p(a, b: openArray[int]) =
+  if a.len > 0:
+    echo a[0]
+
+  for i in 0..a.len-8:
+    #{.invariant: i < a.len.}
+    echo a[i]
+
+  for i in 0..min(a.len, b.len)-1:
+    echo a[i], " ", b[i]
+
+  takeNat(a.len - 4)
+
+proc r(x: range[0.0..1.0]) = echo x
+
+proc sum() =
+  r 1.0
+  r 4.0
+
+proc ru(x: range[1u32..10u32]) = echo x
+
+proc xu(a: uint) =
+  if a >= 4u:
+    let chunk = range[1u32..10u32](a)
+    ru chunk
+
+proc parse(s: string) =
+  var i = 0
+
+  while i < s.len and s[i] != 'a':
+    inc i
+
+parse("abc")
+
+{.pop.}
+
+p([1, 2, 3], [4, 5])
diff --git a/drnim/tests/tensures.nim b/drnim/tests/tensures.nim
new file mode 100644
index 000000000..f4e8f84e6
--- /dev/null
+++ b/drnim/tests/tensures.nim
@@ -0,0 +1,74 @@
+discard """
+  nimout: '''tensures.nim(18, 10) Warning: BEGIN [User]
+tensures.nim(27, 5) Warning: cannot prove:
+0 < n [IndexCheck]
+tensures.nim(47, 17) Warning: cannot prove: a < 4; counter example: y -> 2
+a`2 -> 4
+a`1 -> 3
+a -> 2 [IndexCheck]
+tensures.nim(69, 17) Warning: cannot prove: a < 4; counter example: y -> 2
+a`1 -> 4
+a -> 2 [IndexCheck]
+tensures.nim(73, 10) Warning: END [User]'''
+  cmd: "drnim $file"
+  action: "compile"
+"""
+import std/logic
+{.push staticBoundChecks: defined(nimDrNim).}
+{.warning: "BEGIN".}
+
+proc fac(n: int) {.requires: n > 0.} =
+  discard
+
+proc g(): int {.ensures: result > 0.} =
+  result = 10
+
+fac 7 # fine
+fac -45 # wrong
+
+fac g() # fine
+
+proc main =
+  var x = g()
+  fac x
+
+main()
+
+proc myinc(x: var int) {.ensures: x == old(x)+1.} =
+  inc x
+  {.assume: old(x)+1 == x.}
+
+proc mainB(y: int) =
+  var a = y
+  if a < 3:
+    myinc a
+    {.assert: a < 4.}
+    myinc a
+    {.assert: a < 4.} # now this is wrong!
+
+mainB(3)
+
+proc a(yy, z: int) {.requires: (yy - z) > 6.} = discard
+# 'requires' must be weaker (or equal)
+# 'ensures'  must be stronger (or equal)
+
+# a 'is weaker than' b iff  b -> a
+# a 'is stronger than' b iff a -> b
+# --> We can use Z3 to compute whether 'var x: T = q' is valid
+
+type
+  F = proc (yy, z3: int) {.requires: z3 < 5 and z3 > -5 and yy > 10.}
+
+var
+  x: F = a # valid?
+
+proc testAsgn(y: int) =
+  var a = y
+  if a < 3:
+    a = a + 2
+    {.assert: a < 4.}
+
+testAsgn(3)
+
+{.warning: "END".}
+{.pop.}
diff --git a/drnim/tests/tphi.nim b/drnim/tests/tphi.nim
new file mode 100644
index 000000000..7ff49f4dc
--- /dev/null
+++ b/drnim/tests/tphi.nim
@@ -0,0 +1,23 @@
+discard """
+  nimout: '''tphi.nim(9, 10) Warning: BEGIN [User]
+tphi.nim(22, 10) Warning: END [User]'''
+  cmd: "drnim $file"
+  action: "compile"
+"""
+import std/logic
+{.push staticBoundChecks: defined(nimDrNim).}
+{.warning: "BEGIN".}
+
+proc testAsgn(y: int) =
+  var a = y
+  if a > 0:
+    if a > 3:
+      a = a + 2
+    else:
+      a = a + 1
+    {.assert: a > 1.}
+
+testAsgn(3)
+
+{.warning: "END".}
+{.pop.}
diff --git a/drnim/tests/tsetlen_invalidates.nim b/drnim/tests/tsetlen_invalidates.nim
new file mode 100644
index 000000000..f3e595744
--- /dev/null
+++ b/drnim/tests/tsetlen_invalidates.nim
@@ -0,0 +1,26 @@
+discard """
+  nimout: '''tsetlen_invalidates.nim(12, 10) Warning: BEGIN [User]
+tsetlen_invalidates.nim(18, 12) Warning: cannot prove: 0 <= len(a) + -1; counter example: a`1.len -> 0
+a.len -> 1 [IndexCheck]
+tsetlen_invalidates.nim(26, 10) Warning: END [User]
+'''
+  cmd: "drnim $file"
+  action: "compile"
+"""
+
+{.push staticBoundChecks: defined(nimDrNim).}
+{.warning: "BEGIN".}
+
+proc p() =
+  var a = newSeq[int](3)
+  if a.len > 0:
+    a.setLen 0
+    echo a[0]
+
+  if a.len > 0:
+    echo a[0]
+
+{.pop.}
+
+p()
+{.warning: "END".}
diff --git a/examples/allany.nim b/examples/allany.nim
deleted file mode 100644
index 8a5ab81f0..000000000
--- a/examples/allany.nim
+++ /dev/null
@@ -1,24 +0,0 @@
-# All and any
-
-template all(container, cond: untyped): bool =
-  var result = true
-  for it in items(container):
-    if not cond(it):
-      result = false
-      break
-  result
-
-template any(container, cond: untyped): bool =
-  var result = false
-  for it in items(container):
-    if cond(it):
-      result = true
-      break
-  result
-
-if all("mystring", {'a'..'z'}.contains) and any("myohmy", 'y'.`==`):
-  echo "works"
-else:
-  echo "does not work"
-
-
diff --git a/examples/c++iface/irrlichtex.nim b/examples/c++iface/irrlichtex.nim
deleted file mode 100644
index 373a78ce7..000000000
--- a/examples/c++iface/irrlichtex.nim
+++ /dev/null
@@ -1,114 +0,0 @@
-# Horrible example of how to interface with a C++ engine ... ;-)
-
-{.link: "/usr/lib/libIrrlicht.so".}
-
-{.emit: """
-using namespace irr;
-using namespace core;
-using namespace scene;
-using namespace video;
-using namespace io;
-using namespace gui;
-""".}
-
-const
-  irr = "<irrlicht/irrlicht.h>"
-
-type
-  TDimension2d {.final, header: irr, importc: "dimension2d".} = object
-  Tvector3df {.final, header: irr, importc: "vector3df".} = object
-  TColor {.final, header: irr, importc: "SColor".} = object
-
-  TIrrlichtDevice {.final, header: irr, importc: "IrrlichtDevice".} = object
-  TIVideoDriver {.final, header: irr, importc: "IVideoDriver".} = object
-  TISceneManager {.final, header: irr, importc: "ISceneManager".} = object
-  TIGUIEnvironment {.final, header: irr, importc: "IGUIEnvironment".} = object
-  TIAnimatedMesh {.final, header: irr, importc: "IAnimatedMesh".} = object
-  TIAnimatedMeshSceneNode {.final, header: irr,
-    importc: "IAnimatedMeshSceneNode".} = object
-  TITexture {.final, header: irr, importc: "ITexture".} = object
-
-  PIrrlichtDevice = ptr TIrrlichtDevice
-  PIVideoDriver = ptr TIVideoDriver
-  PISceneManager = ptr TISceneManager
-  PIGUIEnvironment = ptr TIGUIEnvironment
-  PIAnimatedMesh = ptr TIAnimatedMesh
-  PIAnimatedMeshSceneNode = ptr TIAnimatedMeshSceneNode
-  PITexture = ptr TITexture
-
-proc dimension2d(x, y: cint): TDimension2d {.
-  header: irr, importc: "dimension2d<u32>".}
-proc vector3df(x,y,z: cint): Tvector3df {.
-  header: irr, importc: "vector3df".}
-proc SColor(r,g,b,a: cint): TColor {.
-  header: irr, importc: "SColor".}
-
-proc createDevice(): PIrrlichtDevice {.
-  header: irr, importc: "createDevice".}
-proc run(device: PIrrlichtDevice): bool {.
-  header: irr, importcpp: "run".}
-
-proc getVideoDriver(dev: PIrrlichtDevice): PIVideoDriver {.
-  header: irr, importcpp: "getVideoDriver".}
-proc getSceneManager(dev: PIrrlichtDevice): PISceneManager {.
-  header: irr, importcpp: "getSceneManager".}
-proc getGUIEnvironment(dev: PIrrlichtDevice): PIGUIEnvironment {.
-  header: irr, importcpp: "getGUIEnvironment".}
-
-proc getMesh(smgr: PISceneManager, path: cstring): PIAnimatedMesh {.
-  header: irr, importcpp: "getMesh".}
-
-proc drawAll(smgr: PISceneManager) {.
-  header: irr, importcpp: "drawAll".}
-proc drawAll(guienv: PIGUIEnvironment) {.
-  header: irr, importcpp: "drawAll".}
-
-proc drop(dev: PIrrlichtDevice) {.
-  header: irr, importcpp: "drop".}
-
-proc getTexture(driver: PIVideoDriver, path: cstring): PITexture {.
-  header: irr, importcpp: "getTexture".}
-proc endScene(driver: PIVideoDriver) {.
-  header: irr, importcpp: "endScene".}
-proc beginScene(driver: PIVideoDriver, a, b: bool, c: TColor) {.
-  header: irr, importcpp: "beginScene".}
-
-proc addAnimatedMeshSceneNode(
-  smgr: PISceneManager, mesh: PIAnimatedMesh): PIAnimatedMeshSceneNode {.
-  header: irr, importcpp: "addAnimatedMeshSceneNode".}
-
-proc setMaterialTexture(n: PIAnimatedMeshSceneNode, x: cint, t: PITexture) {.
-  header: irr, importcpp: "setMaterialTexture".}
-proc addCameraSceneNode(smgr: PISceneManager, x: cint, a, b: TVector3df) {.
-  header: irr, importcpp: "addCameraSceneNode".}
-
-
-var device = createDevice()
-if device == nil: quit "device is nil"
-
-var driver = device.getVideoDriver()
-var smgr = device.getSceneManager()
-var guienv = device.getGUIEnvironment()
-
-var mesh = smgr.getMesh("/home/andreas/download/irrlicht-1.7.2/media/sydney.md2")
-if mesh == nil:
-  device.drop()
-  quit "no mesh!"
-
-var node = smgr.addAnimatedMeshSceneNode(mesh)
-
-if node != nil:
-  #node->setMaterialFlag(EMF_LIGHTING, false)
-  #node->setMD2Animation(scene::EMAT_STAND)
-  node.setMaterialTexture(0,
-    driver.getTexture(
-      "/home/andreas/download/irrlicht-1.7.2/media/media/sydney.bmp"))
-
-smgr.addCameraSceneNode(0, vector3df(0,30,-40), vector3df(0,5,0))
-while device.run():
-  driver.beginScene(true, true, SColor(255,100,101,140))
-  smgr.drawAll()
-  guienv.drawAll()
-  driver.endScene()
-device.drop()
-
diff --git a/examples/cgi/cgi_server.py b/examples/cgi/cgi_server.py
deleted file mode 100644
index 1907515e8..000000000
--- a/examples/cgi/cgi_server.py
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env python
-import BaseHTTPServer
-import CGIHTTPServer
-
-server = BaseHTTPServer.HTTPServer
-handler = CGIHTTPServer.CGIHTTPRequestHandler
-server_address = ('localhost', 8008)
-handler.cgi_directories = ['/']
-
-httpd = server(server_address, handler)
-httpd.serve_forever()
diff --git a/examples/cgi/cgi_stacktrace.nim b/examples/cgi/cgi_stacktrace.nim
deleted file mode 100644
index e9f2f567c..000000000
--- a/examples/cgi/cgi_stacktrace.nim
+++ /dev/null
@@ -1,5 +0,0 @@
-import cgi
-cgi.setStackTraceStdout()
-
-var a: string = nil
-a.add "foobar"
diff --git a/examples/cgi/example.nim b/examples/cgi/example.nim
deleted file mode 100644
index 761197cdb..000000000
--- a/examples/cgi/example.nim
+++ /dev/null
@@ -1,7 +0,0 @@
-import cgi
-
-write(stdout, "Content-type: text/html\n\n")
-write(stdout, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n")
-write(stdout, "<html><head><title>Test</title></head><body>\n")
-write(stdout, "Hello!")
-writeLine(stdout, "</body></html>")
diff --git a/examples/cgiex.nim b/examples/cgiex.nim
deleted file mode 100644
index fb55a731a..000000000
--- a/examples/cgiex.nim
+++ /dev/null
@@ -1,12 +0,0 @@
-# Test/show CGI module
-import strtabs, cgi
-
-var myData = readData()
-validateData(myData, "name", "password")
-writeContentType()
-
-write(stdout, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n")
-write(stdout, "<html><head><title>Test</title></head><body>\n")
-writeLine(stdout, "name: " & myData["name"])
-writeLine(stdout, "password: " & myData["password"])
-writeLine(stdout, "</body></html>")
diff --git a/examples/cross_calculator/.gitignore b/examples/cross_calculator/.gitignore
deleted file mode 100644
index e521bf338..000000000
--- a/examples/cross_calculator/.gitignore
+++ /dev/null
@@ -1,12 +0,0 @@
-# Android specific absolute paths.
-android/bin/
-android/gen/
-android/jni/backend-jni.h
-android/libs/
-android/local.properties
-android/obj/
-android/tags
-# iOS specific absolute paths
-ios/resources/ui/*.m
-ios/tags
-
diff --git a/examples/cross_calculator/android/AndroidManifest.xml b/examples/cross_calculator/android/AndroidManifest.xml
deleted file mode 100644
index 05b96fb50..000000000
--- a/examples/cross_calculator/android/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-		package="com.github.nimrod.crosscalculator"
-		android:versionCode="1"
-		android:versionName="1.0">
-
-	<uses-sdk android:minSdkVersion="3" />
-
-	<application android:label="@string/app_name" android:debuggable="true">
-		<activity android:name=".CrossCalculator"
-			android:label="@string/app_name">
-			<intent-filter>
-				<action android:name="android.intent.action.MAIN" />
-				<category android:name="android.intent.category.LAUNCHER" />
-			</intent-filter>
-		</activity>
-	</application>
-</manifest>
diff --git a/examples/cross_calculator/android/build.xml b/examples/cross_calculator/android/build.xml
deleted file mode 100644
index d7c88432a..000000000
--- a/examples/cross_calculator/android/build.xml
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project name="CrossCalculator" default="help">
-
-    <!-- The local.properties file is created and updated by the 'android' tool.
-         It contains the path to the SDK. It should *NOT* be checked into
-         Version Control Systems. -->
-    <property file="local.properties" />
-
-    <!-- The ant.properties file can be created by you. It is only edited by the
-         'android' tool to add properties to it.
-         This is the place to change some Ant specific build properties.
-         Here are some properties you may want to change/update:
-
-         source.dir
-             The name of the source directory. Default is 'src'.
-         out.dir
-             The name of the output directory. Default is 'bin'.
-
-         For other overridable properties, look at the beginning of the rules
-         files in the SDK, at tools/ant/build.xml
-
-         Properties related to the SDK location or the project target should
-         be updated using the 'android' tool with the 'update' action.
-
-         This file is an integral part of the build system for your
-         application and should be checked into Version Control Systems.
-
-         -->
-    <property file="ant.properties" />
-
-    <!-- if sdk.dir was not set from one of the property file, then
-         get it from the ANDROID_HOME env var.
-         This must be done before we load project.properties since
-         the proguard config can use sdk.dir -->
-    <property environment="env" />
-    <condition property="sdk.dir" value="${env.ANDROID_HOME}">
-        <isset property="env.ANDROID_HOME" />
-    </condition>
-
-    <!-- The project.properties file is created and updated by the 'android'
-         tool, as well as ADT.
-
-         This contains project specific properties such as project target, and library
-         dependencies. Lower level build properties are stored in ant.properties
-         (or in .classpath for Eclipse projects).
-
-         This file is an integral part of the build system for your
-         application and should be checked into Version Control Systems. -->
-    <loadproperties srcFile="project.properties" />
-
-    <!-- quick check on sdk.dir -->
-    <fail
-            message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
-            unless="sdk.dir"
-    />
-
-    <!--
-        Import per project custom build rules if present at the root of the project.
-        This is the place to put custom intermediary targets such as:
-            -pre-build
-            -pre-compile
-            -post-compile (This is typically used for code obfuscation.
-                           Compiled code location: ${out.classes.absolute.dir}
-                           If this is not done in place, override ${out.dex.input.absolute.dir})
-            -post-package
-            -post-build
-            -pre-clean
-    -->
-    <import file="custom_rules.xml" optional="true" />
-
-    <!-- Import the actual build file.
-
-         To customize existing targets, there are two options:
-         - Customize only one target:
-             - copy/paste the target into this file, *before* the
-               <import> task.
-             - customize it to your needs.
-         - Customize the whole content of build.xml
-             - copy/paste the content of the rules files (minus the top node)
-               into this file, replacing the <import> task.
-             - customize to your needs.
-
-         ***********************
-         ****** IMPORTANT ******
-         ***********************
-         In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
-         in order to avoid having your file be overridden by tools such as "android update project"
-    -->
-    <!-- version-tag: 1 -->
-    <import file="${sdk.dir}/tools/ant/build.xml" />
-
-</project>
diff --git a/examples/cross_calculator/android/custom_rules.xml b/examples/cross_calculator/android/custom_rules.xml
deleted file mode 100644
index 91783a50e..000000000
--- a/examples/cross_calculator/android/custom_rules.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project name="custom_rules" default="debug">
-	<target name="nim">
-		<exec executable="scripts/nimbuild.sh" failonerror="true">
-		</exec>
-	</target>
-	<target name="jni">
-		<exec executable="scripts/jnibuild.sh" failonerror="true">
-		</exec>
-	</target>
-	<target name="ndk">
-		<exec executable="ndk-build" failonerror="true">
-		</exec>
-	</target>
-	<target name="-post-compile" depends="nim, jni, ndk">
-	</target>
-</project>
diff --git a/examples/cross_calculator/android/jni/Android.mk b/examples/cross_calculator/android/jni/Android.mk
deleted file mode 100644
index c1a0feac3..000000000
--- a/examples/cross_calculator/android/jni/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2009 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := nimrod
-LOCAL_SRC_FILES := nimcache/backend.c nimcache/system.c
-LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/nimcache
-
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE    := backend-jni
-LOCAL_SRC_FILES := backend-jni.c
-LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
-LOCAL_STATIC_LIBRARIES := nimrod
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/examples/cross_calculator/android/jni/backend-jni.c b/examples/cross_calculator/android/jni/backend-jni.c
deleted file mode 100644
index 3d65458ec..000000000
--- a/examples/cross_calculator/android/jni/backend-jni.c
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-#include <android/log.h>
-#include <string.h>
-#include "backend-jni.h"
-#include "backend.h"
-
-#define TAG		"backend-jni.c"
-
-jint JNICALL Java_com_github_nimrod_crosscalculator_CrossCalculator_myAdd
-	(JNIEnv *env, jobject thiz, jint a, jint b)
-{
-	char buf[256];
-	const jint ret = myAdd(a, b);
-	// Using logging from inside the native bridge to log-debug.
-	sprintf(buf, "a %d + b %d = ret %d", a, b, ret);
-	__android_log_write(ANDROID_LOG_DEBUG, TAG, buf);
-	return ret;
-}
-
-void JNICALL Java_com_github_nimrod_crosscalculator_CrossCalculator_initNimMain
-	(JNIEnv *env, jclass thiz)
-{
-	NimMain();
-	__android_log_write(ANDROID_LOG_DEBUG, TAG, "Nimrod initialised");
-}
-
-// vim:tabstop=2 shiftwidth=2 syntax=c
diff --git a/examples/cross_calculator/android/project.properties b/examples/cross_calculator/android/project.properties
deleted file mode 100644
index 9fb894d9b..000000000
--- a/examples/cross_calculator/android/project.properties
+++ /dev/null
@@ -1,14 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-3
diff --git a/examples/cross_calculator/android/readme.txt b/examples/cross_calculator/android/readme.txt
deleted file mode 100644
index 96bd403ca..000000000
--- a/examples/cross_calculator/android/readme.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-In this directory you will find the Android platform cross-calculator sample.
-
-Due to the nature of Android being java and Nim generating C code, the build
-process is slightly more complex because jni code has to be written to bridge
-both languages. In a distant future it may be possible for Nim to generate
-the whole jni bridge, but for the moment this is manual work.
-
-For the jni bridge to work first the java code is compiled with the Nim code
-just declared as a native method which will be resolved at runtime. The scripts
-nimbuild.sh and jnibuild.sh are in charge of building the Nim code and
-generating the jni bridge from the java code respectively. Finally, the
-ndk-build command from the android ndk tools has to be run to build the binary
-library which will be installed along the final apk.
-
-All these steps are wrapped in the ant build script through the customization
-of the -post-compile rule. If you have the android ndk tools installed and you
-modify scripts/nimbuild.sh to point to the directory where you have Nim
-installed on your system, you can simply run "ant debug" to build everything.
-
-Once the apk is built you can install it on your device or emulator with the
-command "adb install bin/CrossCalculator-debug.apk".
-
-This example runs against the Android level 3 API, meaning devices from
-Android 1.5 and above should be able to run the generated binary.
diff --git a/examples/cross_calculator/android/res/layout/cross_calculator.xml b/examples/cross_calculator/android/res/layout/cross_calculator.xml
deleted file mode 100644
index 11531334c..000000000
--- a/examples/cross_calculator/android/res/layout/cross_calculator.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/RelativeLayout1"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
-    android:orientation="vertical" >
-
-    <TextView
-        android:id="@+id/title"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentTop="true"
-        android:layout_centerHorizontal="true"
-        android:text="Crossplatform Nimrod calculator"
-        android:textSize="20dip" >
-
-    </TextView>
-
-    <TextView
-        android:id="@+id/value_a"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:layout_below="@+id/title"
-        android:text="Value A: " >
-    </TextView>
-
-    <EditText
-        android:id="@+id/edit_text_a"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentRight="true"
-        android:layout_below="@+id/title"
-        android:ems="10"
-        android:inputType="number" />
-
-    <TextView
-        android:id="@+id/value_b"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:layout_below="@+id/edit_text_a"
-        android:text="Value B: " >
-    </TextView>
-
-    <EditText
-        android:id="@+id/edit_text_b"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentRight="true"
-        android:layout_below="@+id/edit_text_a"
-        android:ems="10"
-        android:inputType="number" />
-
-    <Button
-        android:id="@+id/add_button"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:layout_alignParentLeft="true"
-        android:layout_below="@+id/edit_text_b"
-        android:scrollbarAlwaysDrawVerticalTrack="false"
-        android:selectAllOnFocus="false"
-        android:text="Add!"
-        android:visibility="visible" />
-
-    <TextView
-        android:id="@+id/result_text"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:layout_alignParentLeft="true"
-        android:layout_below="@+id/add_button" />
-
-</RelativeLayout>
diff --git a/examples/cross_calculator/android/res/values/strings.xml b/examples/cross_calculator/android/res/values/strings.xml
deleted file mode 100644
index 05cd3ac93..000000000
--- a/examples/cross_calculator/android/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <string name="app_name">CrossCalculator</string>
-</resources>
diff --git a/examples/cross_calculator/android/scripts/jnibuild.sh b/examples/cross_calculator/android/scripts/jnibuild.sh
deleted file mode 100644
index 8b61f20f7..000000000
--- a/examples/cross_calculator/android/scripts/jnibuild.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/sh
-
-# Force errors to fail script.
-set -e
-
-# If we are running from inside the scripts subdir, get out.
-if [ ! -d src ]
-then
-	cd ..
-fi
-
-# Ok, are we out now?
-if [ -d src ]
-then
-	javah -classpath bin/classes \
-		-o jni/backend-jni.h \
-		com.github.nimrod.crosscalculator.CrossCalculator
-else
-	echo "Uh oh, bin/classes directory not found?"
-	echo "Try compiling your java code, or opening in eclipse."
-	exit 1
-fi
diff --git a/examples/cross_calculator/android/scripts/nimbuild.sh b/examples/cross_calculator/android/scripts/nimbuild.sh
deleted file mode 100644
index 97130b8dd..000000000
--- a/examples/cross_calculator/android/scripts/nimbuild.sh
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/bin/sh
-
-# Set this to the local or full path of your nimrod compiler
-PATH_TO_NIMROD=~/project/nimrod/bin/nimrod
-# Set this to the location of the nimbase.h file so
-# the script can update it if it changes.
-PATH_TO_NIMBASE=~/project/nimrod/lib/nimbase.h
-
-# Force errors to fail script.
-set -e
-
-# If we are running from inside the scripts subdir, get out.
-if [ ! -d src ]
-then
-	cd ..
-fi
-
-DEST_NIMBASE=jni/nimcache/nimbase.h
-
-# Ok, are we out now?
-if [ -d src ]
-then
-	$PATH_TO_NIMROD c --noMain  --app:lib \
-		--nimcache:jni/nimcache --cpu:arm --os:linux \
-		--compileOnly --header ../nimrod_backend/*.nim
-	if [ "${PATH_TO_NIMBASE}" -nt "${DEST_NIMBASE}" ]
-	then
-		echo "Updating nimbase.h"
-		cp "${PATH_TO_NIMBASE}" "${DEST_NIMBASE}"
-	fi
-else
-	echo "Uh oh, src directory not found?"
-	exit 1
-fi
diff --git a/examples/cross_calculator/android/scripts/tags.sh b/examples/cross_calculator/android/scripts/tags.sh
deleted file mode 100644
index 95507064f..000000000
--- a/examples/cross_calculator/android/scripts/tags.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/sh
-
-if [ ! -d src ]
-then
-	cd ..
-fi
-
-if [ -d src ]
-then
-	~/bin/objctags -R \
-		jni \
-		src
-fi
diff --git a/examples/cross_calculator/android/src/com/github/nimrod/crosscalculator/CrossCalculator.java b/examples/cross_calculator/android/src/com/github/nimrod/crosscalculator/CrossCalculator.java
deleted file mode 100644
index df2eed5ea..000000000
--- a/examples/cross_calculator/android/src/com/github/nimrod/crosscalculator/CrossCalculator.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.github.nimrod.crosscalculator;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.TextView;
-import android.widget.Toast;
-
-public class CrossCalculator extends Activity
-{
-	private static final String TAG = "CrossCalculator";
-	private TextView result_text;
-	private EditText edit_text_a, edit_text_b;
-	/** Called when the activity is first created. */
-	@Override
-	public void onCreate(Bundle savedInstanceState)
-	{
-		super.onCreate(savedInstanceState);
-		setContentView(R.layout.cross_calculator);
-
-		final Button button = (Button)findViewById(R.id.add_button);
-		button.setOnClickListener(new View.OnClickListener() {
-			public void onClick(View v) { addButtonClicked(); } });
-
-		result_text = (TextView)findViewById(R.id.result_text);
-		edit_text_a = (EditText)findViewById(R.id.edit_text_a);
-		edit_text_b = (EditText)findViewById(R.id.edit_text_b);
-	}
-
-	/** Handles clicks on the addition button.
-	 * Reads the values form the input fields and performs the calculation.
-	 */
-	private void addButtonClicked()
-	{
-		int a = 0, b = 0;
-		String errors = "";
-		final String a_text = edit_text_a.getText().toString();
-		final String b_text = edit_text_b.getText().toString();
-		try {
-			a = Integer.valueOf(a_text, 10);
-		} catch (NumberFormatException e) {
-			errors += "Can't parse a value '" + a_text + "'. ";
-		}
-		try {
-			b = Integer.valueOf(b_text, 10);
-		} catch (NumberFormatException e) {
-			errors += "Can't parse b value '" + b_text + "'";
-		}
-		final int c = myAdd(a, b);
-		result_text.setText("myAdd(" + a + ", " + b + ") = " + c);
-
-		if (errors.length() > 0) {
-			Log.e(TAG, errors);
-			Toast.makeText(this, errors, Toast.LENGTH_SHORT).show();
-		}
-	}
-
-	/* A native method that is implemented by the
-	 * 'backend-jni' native library, which is packaged
-	 * with this application. Adds to integers.
-	 */
-	public native int myAdd(int a, int b);
-
-	/* A native method used to initialise Nimrod.
-	 */
-	static public native void initNimMain();
-
-	/* this is used to load the 'backend-jni' library on application
-	 * startup. The library has already been unpacked into
-	 * /data/data/com.github.nimrod.backendjni/lib/libbackend-jni.so at
-	 * installation time by the package manager.
-	 */
-	static {
-		System.loadLibrary("backend-jni");
-		initNimMain();
-	}
-}
diff --git a/examples/cross_calculator/ios/cross-calculator.xcodeproj/project.pbxproj b/examples/cross_calculator/ios/cross-calculator.xcodeproj/project.pbxproj
deleted file mode 100644
index 71cebc18a..000000000
--- a/examples/cross_calculator/ios/cross-calculator.xcodeproj/project.pbxproj
+++ /dev/null
@@ -1,337 +0,0 @@
-// !$*UTF8*$!
-{
-	archiveVersion = 1;
-	classes = {
-	};
-	objectVersion = 46;
-	objects = {
-
-/* Begin PBXBuildFile section */
-		D531422A15BC8611005EFF20 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D531422915BC8611005EFF20 /* UIKit.framework */; };
-		D531422C15BC8611005EFF20 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D531422B15BC8611005EFF20 /* Foundation.framework */; };
-		D531422E15BC8611005EFF20 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D531422D15BC8611005EFF20 /* CoreGraphics.framework */; };
-		D531424D15BC87B6005EFF20 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D531424A15BC87B6005EFF20 /* AppDelegate.m */; };
-		D531424E15BC87B6005EFF20 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D531424B15BC87B6005EFF20 /* main.m */; };
-		D531427215BC94B1005EFF20 /* backend.m in Sources */ = {isa = PBXBuildFile; fileRef = D531426F15BC94B1005EFF20 /* backend.m */; };
-		D531427415BC94B1005EFF20 /* stdlib_system.m in Sources */ = {isa = PBXBuildFile; fileRef = D531427115BC94B1005EFF20 /* stdlib_system.m */; };
-		D5B6F94815FA8D4C0084A85B /* NRViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D5B6F94615FA8D4C0084A85B /* NRViewController.m */; };
-/* End PBXBuildFile section */
-
-/* Begin PBXFileReference section */
-		D531422515BC8611005EFF20 /* cross-calculator.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "cross-calculator.app"; sourceTree = BUILT_PRODUCTS_DIR; };
-		D531422915BC8611005EFF20 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
-		D531422B15BC8611005EFF20 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
-		D531422D15BC8611005EFF20 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
-		D531424715BC87A5005EFF20 /* cross-calculator-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "cross-calculator-Info.plist"; path = "resources/plist/cross-calculator-Info.plist"; sourceTree = "<group>"; };
-		D531424915BC87B6005EFF20 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = src/AppDelegate.h; sourceTree = "<group>"; };
-		D531424A15BC87B6005EFF20 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = src/AppDelegate.m; sourceTree = "<group>"; };
-		D531424B15BC87B6005EFF20 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = src/main.m; sourceTree = "<group>"; };
-		D531424C15BC87B6005EFF20 /* cross-calculator-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "cross-calculator-Prefix.pch"; path = "src/cross-calculator-Prefix.pch"; sourceTree = "<group>"; };
-		D531426715BC91EF005EFF20 /* tags.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; name = tags.sh; path = scripts/tags.sh; sourceTree = "<group>"; };
-		D531426815BC91EF005EFF20 /* xcode_prebuild.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; name = xcode_prebuild.sh; path = scripts/xcode_prebuild.sh; sourceTree = "<group>"; };
-		D531426F15BC94B1005EFF20 /* backend.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = backend.m; path = build/nimcache/backend.m; sourceTree = "<group>"; };
-		D531427115BC94B1005EFF20 /* stdlib_system.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = stdlib_system.m; path = build/nimcache/stdlib_system.m; sourceTree = "<group>"; };
-		D592E19015C7120F005258EA /* backend.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = backend.h; path = build/nimcache/backend.h; sourceTree = "<group>"; };
-		D592E19115C71415005258EA /* nimbase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nimbase.h; path = build/nimcache/nimbase.h; sourceTree = "<group>"; };
-		D5B6F94515FA8D4C0084A85B /* NRViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NRViewController.h; path = src/NRViewController.h; sourceTree = "<group>"; };
-		D5B6F94615FA8D4C0084A85B /* NRViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NRViewController.m; path = src/NRViewController.m; sourceTree = "<group>"; };
-		D5B6F96315FB448D0084A85B /* NRViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = NRViewController.xib; path = resources/ui/NRViewController.xib; sourceTree = "<group>"; };
-/* End PBXFileReference section */
-
-/* Begin PBXFrameworksBuildPhase section */
-		D531422215BC8610005EFF20 /* Frameworks */ = {
-			isa = PBXFrameworksBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				D531422A15BC8611005EFF20 /* UIKit.framework in Frameworks */,
-				D531422C15BC8611005EFF20 /* Foundation.framework in Frameworks */,
-				D531422E15BC8611005EFF20 /* CoreGraphics.framework in Frameworks */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-/* End PBXFrameworksBuildPhase section */
-
-/* Begin PBXGroup section */
-		D531421A15BC8610005EFF20 = {
-			isa = PBXGroup;
-			children = (
-				D531426A15BC93D5005EFF20 /* build */,
-				D531424515BC874E005EFF20 /* resources */,
-				D531426615BC91E1005EFF20 /* scripts */,
-				D531424815BC87AD005EFF20 /* src */,
-				D531422815BC8611005EFF20 /* Frameworks */,
-				D531422615BC8611005EFF20 /* Products */,
-			);
-			sourceTree = "<group>";
-		};
-		D531422615BC8611005EFF20 /* Products */ = {
-			isa = PBXGroup;
-			children = (
-				D531422515BC8611005EFF20 /* cross-calculator.app */,
-			);
-			name = Products;
-			sourceTree = "<group>";
-		};
-		D531422815BC8611005EFF20 /* Frameworks */ = {
-			isa = PBXGroup;
-			children = (
-				D531422915BC8611005EFF20 /* UIKit.framework */,
-				D531422B15BC8611005EFF20 /* Foundation.framework */,
-				D531422D15BC8611005EFF20 /* CoreGraphics.framework */,
-			);
-			name = Frameworks;
-			sourceTree = "<group>";
-		};
-		D531424515BC874E005EFF20 /* resources */ = {
-			isa = PBXGroup;
-			children = (
-				D531424615BC8756005EFF20 /* plist */,
-				D5B6F96115FB447C0084A85B /* ui */,
-			);
-			name = resources;
-			sourceTree = "<group>";
-		};
-		D531424615BC8756005EFF20 /* plist */ = {
-			isa = PBXGroup;
-			children = (
-				D531424715BC87A5005EFF20 /* cross-calculator-Info.plist */,
-			);
-			name = plist;
-			sourceTree = "<group>";
-		};
-		D531424815BC87AD005EFF20 /* src */ = {
-			isa = PBXGroup;
-			children = (
-				D531424915BC87B6005EFF20 /* AppDelegate.h */,
-				D531424A15BC87B6005EFF20 /* AppDelegate.m */,
-				D531424C15BC87B6005EFF20 /* cross-calculator-Prefix.pch */,
-				D531424B15BC87B6005EFF20 /* main.m */,
-				D5B6F94515FA8D4C0084A85B /* NRViewController.h */,
-				D5B6F94615FA8D4C0084A85B /* NRViewController.m */,
-			);
-			name = src;
-			sourceTree = "<group>";
-		};
-		D531426615BC91E1005EFF20 /* scripts */ = {
-			isa = PBXGroup;
-			children = (
-				D531426715BC91EF005EFF20 /* tags.sh */,
-				D531426815BC91EF005EFF20 /* xcode_prebuild.sh */,
-			);
-			name = scripts;
-			sourceTree = "<group>";
-		};
-		D531426A15BC93D5005EFF20 /* build */ = {
-			isa = PBXGroup;
-			children = (
-				D531426E15BC94A6005EFF20 /* nimrod */,
-			);
-			name = build;
-			sourceTree = "<group>";
-		};
-		D531426E15BC94A6005EFF20 /* nimrod */ = {
-			isa = PBXGroup;
-			children = (
-				D592E19015C7120F005258EA /* backend.h */,
-				D531426F15BC94B1005EFF20 /* backend.m */,
-				D592E19115C71415005258EA /* nimbase.h */,
-				D531427115BC94B1005EFF20 /* stdlib_system.m */,
-			);
-			name = nimrod;
-			sourceTree = "<group>";
-		};
-		D5B6F96115FB447C0084A85B /* ui */ = {
-			isa = PBXGroup;
-			children = (
-				D5B6F96315FB448D0084A85B /* NRViewController.xib */,
-			);
-			name = ui;
-			sourceTree = "<group>";
-		};
-/* End PBXGroup section */
-
-/* Begin PBXNativeTarget section */
-		D531422415BC8610005EFF20 /* cross-calculator */ = {
-			isa = PBXNativeTarget;
-			buildConfigurationList = D531423D15BC8611005EFF20 /* Build configuration list for PBXNativeTarget "cross-calculator" */;
-			buildPhases = (
-				D531426915BC926C005EFF20 /* ShellScript */,
-				D531422115BC8610005EFF20 /* Sources */,
-				D531422215BC8610005EFF20 /* Frameworks */,
-				D531422315BC8610005EFF20 /* Resources */,
-			);
-			buildRules = (
-			);
-			dependencies = (
-			);
-			name = "cross-calculator";
-			productName = "cross-calculator";
-			productReference = D531422515BC8611005EFF20 /* cross-calculator.app */;
-			productType = "com.apple.product-type.application";
-		};
-/* End PBXNativeTarget section */
-
-/* Begin PBXProject section */
-		D531421C15BC8610005EFF20 /* Project object */ = {
-			isa = PBXProject;
-			attributes = {
-				LastUpgradeCheck = 0420;
-				ORGANIZATIONNAME = "Electric Hands Software";
-			};
-			buildConfigurationList = D531421F15BC8610005EFF20 /* Build configuration list for PBXProject "cross-calculator" */;
-			compatibilityVersion = "Xcode 3.2";
-			developmentRegion = English;
-			hasScannedForEncodings = 0;
-			knownRegions = (
-				en,
-			);
-			mainGroup = D531421A15BC8610005EFF20;
-			productRefGroup = D531422615BC8611005EFF20 /* Products */;
-			projectDirPath = "";
-			projectRoot = "";
-			targets = (
-				D531422415BC8610005EFF20 /* cross-calculator */,
-			);
-		};
-/* End PBXProject section */
-
-/* Begin PBXResourcesBuildPhase section */
-		D531422315BC8610005EFF20 /* Resources */ = {
-			isa = PBXResourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-/* End PBXResourcesBuildPhase section */
-
-/* Begin PBXShellScriptBuildPhase section */
-		D531426915BC926C005EFF20 /* ShellScript */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputPaths = (
-			);
-			outputPaths = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = scripts/xcode_prebuild.sh;
-		};
-/* End PBXShellScriptBuildPhase section */
-
-/* Begin PBXSourcesBuildPhase section */
-		D531422115BC8610005EFF20 /* Sources */ = {
-			isa = PBXSourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				D531424D15BC87B6005EFF20 /* AppDelegate.m in Sources */,
-				D531424E15BC87B6005EFF20 /* main.m in Sources */,
-				D531427215BC94B1005EFF20 /* backend.m in Sources */,
-				D531427415BC94B1005EFF20 /* stdlib_system.m in Sources */,
-				D5B6F94815FA8D4C0084A85B /* NRViewController.m in Sources */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-/* End PBXSourcesBuildPhase section */
-
-/* Begin XCBuildConfiguration section */
-		D531423B15BC8611005EFF20 /* Debug */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				ALWAYS_SEARCH_USER_PATHS = NO;
-				ARCHS = (
-					armv7,
-					armv6,
-				);
-				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
-				COPY_PHASE_STRIP = NO;
-				GCC_C_LANGUAGE_STANDARD = gnu99;
-				GCC_DYNAMIC_NO_PIC = NO;
-				GCC_OPTIMIZATION_LEVEL = 0;
-				GCC_PREPROCESSOR_DEFINITIONS = (
-					"DEBUG=1",
-					"$(inherited)",
-				);
-				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
-				GCC_VERSION = "";
-				GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
-				GCC_WARN_ABOUT_RETURN_TYPE = YES;
-				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 4.3;
-				SDKROOT = iphoneos;
-				TARGETED_DEVICE_FAMILY = "1,2";
-			};
-			name = Debug;
-		};
-		D531423C15BC8611005EFF20 /* Release */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				ALWAYS_SEARCH_USER_PATHS = NO;
-				ARCHS = (
-					armv7,
-					armv6,
-				);
-				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
-				COPY_PHASE_STRIP = YES;
-				GCC_C_LANGUAGE_STANDARD = gnu99;
-				GCC_VERSION = "";
-				GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
-				GCC_WARN_ABOUT_RETURN_TYPE = YES;
-				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 4.3;
-				OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
-				SDKROOT = iphoneos;
-				TARGETED_DEVICE_FAMILY = "1,2";
-				VALIDATE_PRODUCT = YES;
-			};
-			name = Release;
-		};
-		D531423E15BC8611005EFF20 /* Debug */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				GCC_PRECOMPILE_PREFIX_HEADER = YES;
-				GCC_PREFIX_HEADER = "src/cross-calculator-Prefix.pch";
-				INFOPLIST_FILE = "resources/plist/cross-calculator-Info.plist";
-				PRODUCT_NAME = "$(TARGET_NAME)";
-				WRAPPER_EXTENSION = app;
-			};
-			name = Debug;
-		};
-		D531423F15BC8611005EFF20 /* Release */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				GCC_PRECOMPILE_PREFIX_HEADER = YES;
-				GCC_PREFIX_HEADER = "src/cross-calculator-Prefix.pch";
-				INFOPLIST_FILE = "resources/plist/cross-calculator-Info.plist";
-				PRODUCT_NAME = "$(TARGET_NAME)";
-				WRAPPER_EXTENSION = app;
-			};
-			name = Release;
-		};
-/* End XCBuildConfiguration section */
-
-/* Begin XCConfigurationList section */
-		D531421F15BC8610005EFF20 /* Build configuration list for PBXProject "cross-calculator" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				D531423B15BC8611005EFF20 /* Debug */,
-				D531423C15BC8611005EFF20 /* Release */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
-		D531423D15BC8611005EFF20 /* Build configuration list for PBXNativeTarget "cross-calculator" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				D531423E15BC8611005EFF20 /* Debug */,
-				D531423F15BC8611005EFF20 /* Release */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
-/* End XCConfigurationList section */
-	};
-	rootObject = D531421C15BC8610005EFF20 /* Project object */;
-}
diff --git a/examples/cross_calculator/ios/readme.txt b/examples/cross_calculator/ios/readme.txt
deleted file mode 100644
index 3ea03a367..000000000
--- a/examples/cross_calculator/ios/readme.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-In this directory you will find the iOS platform cross-calculator sample.
-
-The iOS version of the code builds a view controller in charge of displaying
-the interface to the user. The Nim backend code is compiled into C code and
-put into build/nimrod as a pre-build phase of the project.
-
-When the calculate button is used the view controller calls the Nim code to
-delegate the logic of the operation and puts the result in a label for display.
-All interface error checks are implemented in the view controller.
-
-This version of the iOS project is known to work with Xcode 4.2 and Xcode
-4.4.1. The final binary can be deployed on iOS 3.x to 5.x supporting all iOS
-platforms and versions available at the moment.
diff --git a/examples/cross_calculator/ios/resources/plist/cross-calculator-Info.plist b/examples/cross_calculator/ios/resources/plist/cross-calculator-Info.plist
deleted file mode 100644
index 758f20e38..000000000
--- a/examples/cross_calculator/ios/resources/plist/cross-calculator-Info.plist
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>CFBundleDevelopmentRegion</key>
-	<string>en</string>
-	<key>CFBundleDisplayName</key>
-	<string>${PRODUCT_NAME}</string>
-	<key>CFBundleExecutable</key>
-	<string>${EXECUTABLE_NAME}</string>
-	<key>CFBundleIconFiles</key>
-	<array/>
-	<key>CFBundleIdentifier</key>
-	<string>com.github.nimrod.${PRODUCT_NAME:rfc1034identifier}</string>
-	<key>CFBundleInfoDictionaryVersion</key>
-	<string>6.0</string>
-	<key>CFBundleName</key>
-	<string>${PRODUCT_NAME}</string>
-	<key>CFBundlePackageType</key>
-	<string>APPL</string>
-	<key>CFBundleShortVersionString</key>
-	<string>1.0</string>
-	<key>CFBundleSignature</key>
-	<string>????</string>
-	<key>CFBundleVersion</key>
-	<string>1.0</string>
-	<key>UIApplicationExitsOnSuspend</key>
-	<true/>
-</dict>
-</plist>
diff --git a/examples/cross_calculator/ios/resources/ui/NRViewController.xib b/examples/cross_calculator/ios/resources/ui/NRViewController.xib
deleted file mode 100644
index 2118b5044..000000000
--- a/examples/cross_calculator/ios/resources/ui/NRViewController.xib
+++ /dev/null
@@ -1,479 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="8.00">
-	<data>
-		<int key="IBDocument.SystemTarget">0</int>
-		<string key="IBDocument.SystemVersion">11E53</string>
-		<string key="IBDocument.InterfaceBuilderVersion">2549</string>
-		<string key="IBDocument.AppKitVersion">1138.47</string>
-		<string key="IBDocument.HIToolboxVersion">569.00</string>
-		<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
-			<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-			<string key="NS.object.0">1498</string>
-		</object>
-		<array key="IBDocument.IntegratedClassDependencies">
-			<string>IBProxyObject</string>
-			<string>IBUIButton</string>
-			<string>IBUILabel</string>
-			<string>IBUITextField</string>
-			<string>IBUIView</string>
-		</array>
-		<array key="IBDocument.PluginDependencies">
-			<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-		</array>
-		<object class="NSMutableDictionary" key="IBDocument.Metadata">
-			<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
-			<integer value="1" key="NS.object.0"/>
-		</object>
-		<array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
-			<object class="IBProxyObject" id="372490531">
-				<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
-				<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-			</object>
-			<object class="IBProxyObject" id="975951072">
-				<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
-				<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-			</object>
-			<object class="IBUIView" id="191373211">
-				<reference key="NSNextResponder"/>
-				<int key="NSvFlags">274</int>
-				<array class="NSMutableArray" key="NSSubviews">
-					<object class="IBUIButton" id="467453084">
-						<reference key="NSNextResponder" ref="191373211"/>
-						<int key="NSvFlags">292</int>
-						<string key="NSFrame">{{0, -10}, {320, 480}}</string>
-						<reference key="NSSuperview" ref="191373211"/>
-						<reference key="NSWindow"/>
-						<string key="NSReuseIdentifierKey">_NS:9</string>
-						<bool key="IBUIOpaque">NO</bool>
-						<int key="IBUITag">1</int>
-						<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-						<int key="IBUIContentHorizontalAlignment">0</int>
-						<int key="IBUIContentVerticalAlignment">0</int>
-						<object class="NSColor" key="IBUIHighlightedTitleColor" id="95215378">
-							<int key="NSColorSpace">3</int>
-							<bytes key="NSWhite">MQA</bytes>
-						</object>
-						<object class="NSColor" key="IBUINormalTitleColor">
-							<int key="NSColorSpace">1</int>
-							<bytes key="NSRGB">MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA</bytes>
-						</object>
-						<object class="NSColor" key="IBUINormalTitleShadowColor" id="1056499111">
-							<int key="NSColorSpace">3</int>
-							<bytes key="NSWhite">MC41AA</bytes>
-						</object>
-						<object class="IBUIFontDescription" key="IBUIFontDescription" id="686052398">
-							<int key="type">2</int>
-							<double key="pointSize">15</double>
-						</object>
-						<object class="NSFont" key="IBUIFont" id="594372787">
-							<string key="NSName">Helvetica-Bold</string>
-							<double key="NSSize">15</double>
-							<int key="NSfFlags">16</int>
-						</object>
-					</object>
-					<object class="IBUILabel" id="353054360">
-						<reference key="NSNextResponder" ref="191373211"/>
-						<int key="NSvFlags">306</int>
-						<string key="NSFrameSize">{320, 34}</string>
-						<reference key="NSSuperview" ref="191373211"/>
-						<reference key="NSWindow"/>
-						<reference key="NSNextKeyView" ref="525225214"/>
-						<string key="NSReuseIdentifierKey">_NS:9</string>
-						<bool key="IBUIOpaque">NO</bool>
-						<bool key="IBUIClipsSubviews">YES</bool>
-						<int key="IBUIContentMode">7</int>
-						<int key="IBUITag">2</int>
-						<bool key="IBUIUserInteractionEnabled">NO</bool>
-						<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-						<string key="IBUIText">Nim Crossplatform Calculator</string>
-						<object class="NSColor" key="IBUITextColor" id="128895179">
-							<int key="NSColorSpace">1</int>
-							<bytes key="NSRGB">MCAwIDAAA</bytes>
-						</object>
-						<nil key="IBUIHighlightedColor"/>
-						<int key="IBUIBaselineAdjustment">0</int>
-						<float key="IBUIMinimumFontSize">10</float>
-						<int key="IBUITextAlignment">1</int>
-						<object class="IBUIFontDescription" key="IBUIFontDescription">
-							<int key="type">2</int>
-							<double key="pointSize">18</double>
-						</object>
-						<object class="NSFont" key="IBUIFont">
-							<string key="NSName">Helvetica-Bold</string>
-							<double key="NSSize">18</double>
-							<int key="NSfFlags">16</int>
-						</object>
-					</object>
-					<object class="IBUILabel" id="525225214">
-						<reference key="NSNextResponder" ref="191373211"/>
-						<int key="NSvFlags">294</int>
-						<string key="NSFrame">{{20, 42}, {165, 31}}</string>
-						<reference key="NSSuperview" ref="191373211"/>
-						<reference key="NSWindow"/>
-						<reference key="NSNextKeyView" ref="1040444341"/>
-						<string key="NSReuseIdentifierKey">_NS:9</string>
-						<bool key="IBUIOpaque">NO</bool>
-						<bool key="IBUIClipsSubviews">YES</bool>
-						<int key="IBUIContentMode">7</int>
-						<int key="IBUITag">3</int>
-						<bool key="IBUIUserInteractionEnabled">NO</bool>
-						<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-						<string key="IBUIText">Value A:</string>
-						<reference key="IBUITextColor" ref="128895179"/>
-						<nil key="IBUIHighlightedColor"/>
-						<int key="IBUIBaselineAdjustment">0</int>
-						<float key="IBUIMinimumFontSize">10</float>
-						<object class="IBUIFontDescription" key="IBUIFontDescription" id="768572949">
-							<int key="type">1</int>
-							<double key="pointSize">17</double>
-						</object>
-						<object class="NSFont" key="IBUIFont" id="972319481">
-							<string key="NSName">Helvetica</string>
-							<double key="NSSize">17</double>
-							<int key="NSfFlags">16</int>
-						</object>
-					</object>
-					<object class="IBUILabel" id="904781109">
-						<reference key="NSNextResponder" ref="191373211"/>
-						<int key="NSvFlags">294</int>
-						<string key="NSFrame">{{20, 81}, {165, 31}}</string>
-						<reference key="NSSuperview" ref="191373211"/>
-						<reference key="NSWindow"/>
-						<reference key="NSNextKeyView" ref="1041721572"/>
-						<string key="NSReuseIdentifierKey">_NS:9</string>
-						<bool key="IBUIOpaque">NO</bool>
-						<bool key="IBUIClipsSubviews">YES</bool>
-						<int key="IBUIContentMode">7</int>
-						<int key="IBUITag">4</int>
-						<bool key="IBUIUserInteractionEnabled">NO</bool>
-						<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-						<string key="IBUIText">Value B:</string>
-						<reference key="IBUITextColor" ref="128895179"/>
-						<nil key="IBUIHighlightedColor"/>
-						<int key="IBUIBaselineAdjustment">0</int>
-						<float key="IBUIMinimumFontSize">10</float>
-						<reference key="IBUIFontDescription" ref="768572949"/>
-						<reference key="IBUIFont" ref="972319481"/>
-					</object>
-					<object class="IBUIButton" id="557594991">
-						<reference key="NSNextResponder" ref="191373211"/>
-						<int key="NSvFlags">291</int>
-						<string key="NSFrame">{{193, 124}, {107, 37}}</string>
-						<reference key="NSSuperview" ref="191373211"/>
-						<reference key="NSWindow"/>
-						<reference key="NSNextKeyView"/>
-						<string key="NSReuseIdentifierKey">_NS:9</string>
-						<bool key="IBUIOpaque">NO</bool>
-						<int key="IBUITag">5</int>
-						<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-						<int key="IBUIContentHorizontalAlignment">0</int>
-						<int key="IBUIContentVerticalAlignment">0</int>
-						<int key="IBUIButtonType">1</int>
-						<string key="IBUINormalTitle">Add!</string>
-						<reference key="IBUIHighlightedTitleColor" ref="95215378"/>
-						<object class="NSColor" key="IBUINormalTitleColor">
-							<int key="NSColorSpace">1</int>
-							<bytes key="NSRGB">MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA</bytes>
-						</object>
-						<reference key="IBUINormalTitleShadowColor" ref="1056499111"/>
-						<reference key="IBUIFontDescription" ref="686052398"/>
-						<reference key="IBUIFont" ref="594372787"/>
-					</object>
-					<object class="IBUILabel" id="360864196">
-						<reference key="NSNextResponder" ref="191373211"/>
-						<int key="NSvFlags">292</int>
-						<string key="NSFrame">{{20, 124}, {60, 37}}</string>
-						<reference key="NSSuperview" ref="191373211"/>
-						<reference key="NSWindow"/>
-						<reference key="NSNextKeyView" ref="521073831"/>
-						<string key="NSReuseIdentifierKey">_NS:9</string>
-						<bool key="IBUIOpaque">NO</bool>
-						<bool key="IBUIClipsSubviews">YES</bool>
-						<int key="IBUIContentMode">7</int>
-						<int key="IBUITag">6</int>
-						<bool key="IBUIUserInteractionEnabled">NO</bool>
-						<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-						<string key="IBUIText">Result:</string>
-						<reference key="IBUITextColor" ref="128895179"/>
-						<nil key="IBUIHighlightedColor"/>
-						<int key="IBUIBaselineAdjustment">0</int>
-						<float key="IBUIMinimumFontSize">10</float>
-						<reference key="IBUIFontDescription" ref="768572949"/>
-						<reference key="IBUIFont" ref="972319481"/>
-					</object>
-					<object class="IBUILabel" id="521073831">
-						<reference key="NSNextResponder" ref="191373211"/>
-						<int key="NSvFlags">294</int>
-						<string key="NSFrame">{{88, 124}, {97, 37}}</string>
-						<reference key="NSSuperview" ref="191373211"/>
-						<reference key="NSWindow"/>
-						<reference key="NSNextKeyView" ref="557594991"/>
-						<string key="NSReuseIdentifierKey">_NS:9</string>
-						<bool key="IBUIOpaque">NO</bool>
-						<bool key="IBUIClipsSubviews">YES</bool>
-						<int key="IBUIContentMode">7</int>
-						<int key="IBUITag">7</int>
-						<bool key="IBUIUserInteractionEnabled">NO</bool>
-						<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-						<string key="IBUIText"/>
-						<reference key="IBUITextColor" ref="128895179"/>
-						<nil key="IBUIHighlightedColor"/>
-						<int key="IBUIBaselineAdjustment">0</int>
-						<float key="IBUIMinimumFontSize">10</float>
-						<reference key="IBUIFontDescription" ref="768572949"/>
-						<reference key="IBUIFont" ref="972319481"/>
-					</object>
-					<object class="IBUITextField" id="1040444341">
-						<reference key="NSNextResponder" ref="191373211"/>
-						<int key="NSvFlags">291</int>
-						<string key="NSFrame">{{193, 42}, {107, 31}}</string>
-						<reference key="NSSuperview" ref="191373211"/>
-						<reference key="NSWindow"/>
-						<reference key="NSNextKeyView" ref="904781109"/>
-						<string key="NSReuseIdentifierKey">_NS:9</string>
-						<bool key="IBUIOpaque">NO</bool>
-						<bool key="IBUIClipsSubviews">YES</bool>
-						<int key="IBUITag">8</int>
-						<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-						<int key="IBUIContentVerticalAlignment">0</int>
-						<string key="IBUIText"/>
-						<int key="IBUIBorderStyle">3</int>
-						<string key="IBUIPlaceholder">Integer</string>
-						<object class="NSColor" key="IBUITextColor">
-							<int key="NSColorSpace">3</int>
-							<bytes key="NSWhite">MAA</bytes>
-							<object class="NSColorSpace" key="NSCustomColorSpace" id="433120901">
-								<int key="NSID">2</int>
-							</object>
-						</object>
-						<int key="IBUITextAlignment">1</int>
-						<bool key="IBUIAdjustsFontSizeToFit">YES</bool>
-						<float key="IBUIMinimumFontSize">17</float>
-						<object class="IBUITextInputTraits" key="IBUITextInputTraits">
-							<int key="IBUIKeyboardType">4</int>
-							<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-						</object>
-						<int key="IBUIClearButtonMode">1</int>
-						<object class="IBUIFontDescription" key="IBUIFontDescription" id="836805198">
-							<int key="type">1</int>
-							<double key="pointSize">14</double>
-						</object>
-						<object class="NSFont" key="IBUIFont" id="700927782">
-							<string key="NSName">Helvetica</string>
-							<double key="NSSize">14</double>
-							<int key="NSfFlags">16</int>
-						</object>
-					</object>
-					<object class="IBUITextField" id="1041721572">
-						<reference key="NSNextResponder" ref="191373211"/>
-						<int key="NSvFlags">291</int>
-						<string key="NSFrame">{{193, 81}, {107, 31}}</string>
-						<reference key="NSSuperview" ref="191373211"/>
-						<reference key="NSWindow"/>
-						<reference key="NSNextKeyView" ref="360864196"/>
-						<string key="NSReuseIdentifierKey">_NS:9</string>
-						<bool key="IBUIOpaque">NO</bool>
-						<bool key="IBUIClipsSubviews">YES</bool>
-						<int key="IBUITag">9</int>
-						<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-						<int key="IBUIContentVerticalAlignment">0</int>
-						<string key="IBUIText"/>
-						<int key="IBUIBorderStyle">3</int>
-						<string key="IBUIPlaceholder">Integer</string>
-						<object class="NSColor" key="IBUITextColor">
-							<int key="NSColorSpace">3</int>
-							<bytes key="NSWhite">MAA</bytes>
-							<reference key="NSCustomColorSpace" ref="433120901"/>
-						</object>
-						<int key="IBUITextAlignment">1</int>
-						<bool key="IBUIAdjustsFontSizeToFit">YES</bool>
-						<float key="IBUIMinimumFontSize">17</float>
-						<object class="IBUITextInputTraits" key="IBUITextInputTraits">
-							<int key="IBUIKeyboardType">4</int>
-							<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-						</object>
-						<int key="IBUIClearButtonMode">1</int>
-						<reference key="IBUIFontDescription" ref="836805198"/>
-						<reference key="IBUIFont" ref="700927782"/>
-					</object>
-				</array>
-				<string key="NSFrame">{{0, 20}, {320, 460}}</string>
-				<reference key="NSSuperview"/>
-				<reference key="NSWindow"/>
-				<reference key="NSNextKeyView" ref="353054360"/>
-				<object class="NSColor" key="IBUIBackgroundColor">
-					<int key="NSColorSpace">3</int>
-					<bytes key="NSWhite">MQA</bytes>
-					<reference key="NSCustomColorSpace" ref="433120901"/>
-				</object>
-				<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
-				<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-			</object>
-		</array>
-		<object class="IBObjectContainer" key="IBDocument.Objects">
-			<array class="NSMutableArray" key="connectionRecords"/>
-			<object class="IBMutableOrderedSet" key="objectRecords">
-				<array key="orderedObjects">
-					<object class="IBObjectRecord">
-						<int key="objectID">0</int>
-						<array key="object" id="0"/>
-						<reference key="children" ref="1000"/>
-						<nil key="parent"/>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">1</int>
-						<reference key="object" ref="191373211"/>
-						<array class="NSMutableArray" key="children">
-							<reference ref="353054360"/>
-							<reference ref="525225214"/>
-							<reference ref="904781109"/>
-							<reference ref="557594991"/>
-							<reference ref="360864196"/>
-							<reference ref="521073831"/>
-							<reference ref="1040444341"/>
-							<reference ref="1041721572"/>
-							<reference ref="467453084"/>
-						</array>
-						<reference key="parent" ref="0"/>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">-1</int>
-						<reference key="object" ref="372490531"/>
-						<reference key="parent" ref="0"/>
-						<string key="objectName">File's Owner</string>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">-2</int>
-						<reference key="object" ref="975951072"/>
-						<reference key="parent" ref="0"/>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">14</int>
-						<reference key="object" ref="1041721572"/>
-						<reference key="parent" ref="191373211"/>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">13</int>
-						<reference key="object" ref="1040444341"/>
-						<reference key="parent" ref="191373211"/>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">12</int>
-						<reference key="object" ref="521073831"/>
-						<reference key="parent" ref="191373211"/>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">11</int>
-						<reference key="object" ref="360864196"/>
-						<reference key="parent" ref="191373211"/>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">10</int>
-						<reference key="object" ref="557594991"/>
-						<reference key="parent" ref="191373211"/>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">8</int>
-						<reference key="object" ref="904781109"/>
-						<reference key="parent" ref="191373211"/>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">7</int>
-						<reference key="object" ref="525225214"/>
-						<reference key="parent" ref="191373211"/>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">4</int>
-						<reference key="object" ref="353054360"/>
-						<reference key="parent" ref="191373211"/>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">22</int>
-						<reference key="object" ref="467453084"/>
-						<reference key="parent" ref="191373211"/>
-					</object>
-				</array>
-			</object>
-			<dictionary class="NSMutableDictionary" key="flattenedProperties">
-				<string key="-1.CustomClassName">NRViewController</string>
-				<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-				<string key="-2.CustomClassName">UIResponder</string>
-				<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-				<string key="1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-				<string key="10.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-				<string key="11.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-				<string key="12.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-				<string key="13.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-				<string key="14.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-				<string key="22.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-				<string key="4.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-				<string key="7.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-				<string key="8.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-			</dictionary>
-			<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
-			<nil key="activeLocalization"/>
-			<dictionary class="NSMutableDictionary" key="localizations"/>
-			<nil key="sourceID"/>
-			<int key="maxID">22</int>
-		</object>
-		<object class="IBClassDescriber" key="IBDocument.Classes">
-			<array class="NSMutableArray" key="referencedPartialClassDescriptions">
-				<object class="IBPartialClassDescription">
-					<string key="className">NRViewController</string>
-					<string key="superclassName">UIViewController</string>
-					<dictionary class="NSMutableDictionary" key="actions">
-						<string key="backgroundTouched">id</string>
-						<string key="calculateButtonTouched">id</string>
-					</dictionary>
-					<dictionary class="NSMutableDictionary" key="actionInfosByName">
-						<object class="IBActionInfo" key="backgroundTouched">
-							<string key="name">backgroundTouched</string>
-							<string key="candidateClassName">id</string>
-						</object>
-						<object class="IBActionInfo" key="calculateButtonTouched">
-							<string key="name">calculateButtonTouched</string>
-							<string key="candidateClassName">id</string>
-						</object>
-					</dictionary>
-					<dictionary class="NSMutableDictionary" key="outlets">
-						<string key="aText">UITextField</string>
-						<string key="bText">UITextField</string>
-						<string key="calculateButton">UIButton</string>
-						<string key="resultLabel">UILabel</string>
-					</dictionary>
-					<dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
-						<object class="IBToOneOutletInfo" key="aText">
-							<string key="name">aText</string>
-							<string key="candidateClassName">UITextField</string>
-						</object>
-						<object class="IBToOneOutletInfo" key="bText">
-							<string key="name">bText</string>
-							<string key="candidateClassName">UITextField</string>
-						</object>
-						<object class="IBToOneOutletInfo" key="calculateButton">
-							<string key="name">calculateButton</string>
-							<string key="candidateClassName">UIButton</string>
-						</object>
-						<object class="IBToOneOutletInfo" key="resultLabel">
-							<string key="name">resultLabel</string>
-							<string key="candidateClassName">UILabel</string>
-						</object>
-					</dictionary>
-					<object class="IBClassDescriptionSource" key="sourceIdentifier">
-						<string key="majorKey">IBProjectSource</string>
-						<string key="minorKey">./Classes/NRViewController.h</string>
-					</object>
-				</object>
-			</array>
-		</object>
-		<int key="IBDocument.localizationMode">0</int>
-		<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
-		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
-			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string>
-			<real value="0.0" key="NS.object.0"/>
-		</object>
-		<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
-		<int key="IBDocument.defaultPropertyAccessControl">3</int>
-		<string key="IBCocoaTouchPluginVersion">1498</string>
-	</data>
-</archive>
diff --git a/examples/cross_calculator/ios/scripts/tags.sh b/examples/cross_calculator/ios/scripts/tags.sh
deleted file mode 100644
index 111e7a1c0..000000000
--- a/examples/cross_calculator/ios/scripts/tags.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/sh
-
-if [ ! -d src ]
-then
-	cd ..
-fi
-
-if [ -d src ]
-then
-	~/bin/objctags -R \
-		build/nimcache \
-		src
-fi
diff --git a/examples/cross_calculator/ios/scripts/xcode_prebuild.sh b/examples/cross_calculator/ios/scripts/xcode_prebuild.sh
deleted file mode 100644
index 90bafd74e..000000000
--- a/examples/cross_calculator/ios/scripts/xcode_prebuild.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/bin/sh
-
-# Set this to the full path of your nimrod compiler
-# since Xcode doesn't inherit your user environment.
-PATH_TO_NIMROD=~/project/nimrod/bin/nimrod
-# Set this to the location of the nimbase.h file so
-# the script can update it if it changes.
-PATH_TO_NIMBASE=~/project/nimrod/lib/nimbase.h
-
-# Force errors to fail script.
-set -e
-
-# If we are running from inside the scripts subdir, get out.
-if [ ! -d src ]
-then
-	cd ..
-fi
-
-DEST_NIMBASE=build/nimcache/nimbase.h
-
-# Ok, are we out now?
-if [ -d src ]
-then
-	$PATH_TO_NIMROD objc --noMain  --app:lib \
-		--nimcache:./build/nimcache --compileOnly \
-		--header --cpu:i386 ../nimrod_backend/backend.nim
-	if [ "${PATH_TO_NIMBASE}" -nt "${DEST_NIMBASE}" ]
-	then
-		echo "Updating nimbase.h"
-		cp "${PATH_TO_NIMBASE}" "${DEST_NIMBASE}"
-	fi
-else
-	echo "Uh oh, src directory not found?"
-	exit 1
-fi
diff --git a/examples/cross_calculator/ios/src/AppDelegate.h b/examples/cross_calculator/ios/src/AppDelegate.h
deleted file mode 100644
index a5a8b3852..000000000
--- a/examples/cross_calculator/ios/src/AppDelegate.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#import <UIKit/UIKit.h>
-
-@interface AppDelegate : UIResponder <UIApplicationDelegate>
-
-@property (strong, nonatomic) UIWindow *window;
-
-@end
diff --git a/examples/cross_calculator/ios/src/AppDelegate.m b/examples/cross_calculator/ios/src/AppDelegate.m
deleted file mode 100644
index 53e7f6188..000000000
--- a/examples/cross_calculator/ios/src/AppDelegate.m
+++ /dev/null
@@ -1,39 +0,0 @@
-#import "AppDelegate.h"
-
-#import "NRViewController.h"
-
-
-@interface AppDelegate ()
-@property (nonatomic, retain) NRViewController *viewController;
-@end
-
-
-@implementation AppDelegate
-
-@synthesize viewController = _viewController;
-@synthesize window = _window;
-
-- (BOOL)application:(UIApplication *)application
-	didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
-{
-	self.window = [[[UIWindow alloc]
-		initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
-
-	self.viewController = [[NRViewController new] autorelease];
-	if ([self.window respondsToSelector:@selector(setRootViewController:)])
-		self.window.rootViewController = self.viewController;
-	else
-		[self.window addSubview:self.viewController.view];
-	[self.window makeKeyAndVisible];
-
-	return YES;
-}
-
-- (void)dealloc
-{
-	[_window release];
-	[_viewController release];
-	[super dealloc];
-}
-
-@end
diff --git a/examples/cross_calculator/ios/src/NRViewController.h b/examples/cross_calculator/ios/src/NRViewController.h
deleted file mode 100644
index 36ba37758..000000000
--- a/examples/cross_calculator/ios/src/NRViewController.h
+++ /dev/null
@@ -1,11 +0,0 @@
-@interface NRViewController : UIViewController
-
-@property (nonatomic, retain) IBOutlet UIButton *calculateButton;
-@property (nonatomic, retain) IBOutlet UITextField *aText;
-@property (nonatomic, retain) IBOutlet UITextField *bText;
-@property (nonatomic, retain) IBOutlet UILabel *resultLabel;
-
-- (IBAction)calculateButtonTouched;
-- (IBAction)backgroundTouched;
-
-@end
\ No newline at end of file
diff --git a/examples/cross_calculator/ios/src/NRViewController.m b/examples/cross_calculator/ios/src/NRViewController.m
deleted file mode 100644
index f629bfc09..000000000
--- a/examples/cross_calculator/ios/src/NRViewController.m
+++ /dev/null
@@ -1,210 +0,0 @@
-#import "NRViewController.h"
-
-#import "backend.h"
-
-
-@implementation NRViewController
-
-@synthesize aText = _aText;
-@synthesize bText = _bText;
-@synthesize calculateButton = _calculateButton;
-@synthesize resultLabel = _resultLabel;
-
-- (void)dealloc
-{
-	[_aText release];
-	[_bText release];
-	[_calculateButton release];
-	[_resultLabel release];
-	[super dealloc];
-}
-
-- (void)viewDidUnload
-{
-	self.calculateButton = nil;
-	self.aText = nil;
-	self.bText = nil;
-	self.resultLabel = nil;
-	[super viewDidUnload];
-}
-
-- (BOOL)shouldAutorotateToInterfaceOrientation:
-	(UIInterfaceOrientation)interfaceOrientation
-{
-	return YES;
-}
-
-/// User wants to calculate the inputs. Well, do it!
-- (IBAction)calculateButtonTouched
-{
-	// Dismiss all keyboards.
-	[self backgroundTouched];
-
-	// Call Nim code, store the result and display it.
-	const int a = [self.aText.text intValue];
-	const int b = [self.bText.text intValue];
-	const int c = myAdd(a, b);
-	self.resultLabel.text = [NSString stringWithFormat:@"%d + %d = %d",
-		a, b, c];
-}
-
-/// If the user touches the background, dismiss any visible keyboard.
-- (IBAction)backgroundTouched
-{
-	[self.aText resignFirstResponder];
-	[self.bText resignFirstResponder];
-}
-
-/** Custom loadView method for backwards compatibility.
- * Unfortunately I've been unable to coerce Xcode 4.4 to generate nib files
- * which are compatible with my trusty iOS 3.0 ipod touch so in order to be
- * fully compatible for all devices we have to build the interface manually in
- * code rather than through the keyed archivers provided by the interface
- * builder.
- *
- * Rather than recreating the user interface manually in code the tool nib2obj
- * was used on the xib file and slightly modified to fit the original property
- * names. Which means here is a lot of garbage you would never write in real
- * life. Please ignore the following "wall of code" for the purposes of
- * learning Nim, this is all just because Apple can't be bothered to
- * maintain backwards compatibility properly.
- */
-- (void)loadView
-{
-	[super loadView];
-
-	self.calculateButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
-	self.calculateButton.autoresizesSubviews = YES;
-	self.calculateButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin;
-	self.calculateButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
-	self.calculateButton.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
-	self.calculateButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
-	self.calculateButton.frame = CGRectMake(193.0, 124.0, 107.0, 37.0);
-	self.calculateButton.tag = 5;
-	[self.calculateButton setTitle:@"Add!" forState:UIControlStateNormal];
-	[self.calculateButton addTarget:self
-		action:@selector(calculateButtonTouched)
-		forControlEvents:UIControlEventTouchUpInside];
-
-	UILabel *label11 = [[UILabel alloc] initWithFrame:CGRectMake(20.0, 124.0, 60.0, 37.0)];
-	label11.adjustsFontSizeToFitWidth = YES;
-	label11.autoresizesSubviews = YES;
-	label11.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
-	label11.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
-	label11.frame = CGRectMake(20.0, 124.0, 60.0, 37.0);
-	label11.tag = 6;
-	label11.text = @"Result:";
-
-	UILabel *label4 = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 34.0)];
-	label4.adjustsFontSizeToFitWidth = YES;
-	label4.autoresizesSubviews = YES;
-	label4.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
-	label4.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
-	label4.frame = CGRectMake(0.0, 0.0, 320.0, 34.0);
-	label4.tag = 2;
-	label4.text = @"Nim Crossplatform Calculator";
-	label4.textAlignment = UITextAlignmentCenter;
-
-	UIButton *background_button = [UIButton buttonWithType:UIButtonTypeCustom];
-	background_button.autoresizesSubviews = YES;
-	background_button.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
-	background_button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
-	background_button.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
-	background_button.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
-	background_button.frame = CGRectMake(0.0, -10.0, 320.0, 480.0);
-	background_button.tag = 1;
-	[background_button addTarget:self action:@selector(backgroundTouched)
-		forControlEvents:UIControlEventTouchDown];
-
-	self.resultLabel = [[[UILabel alloc] initWithFrame:CGRectMake(88.0, 124.0, 97.0, 37.0)] autorelease];
-	self.resultLabel.adjustsFontSizeToFitWidth = YES;
-	self.resultLabel.autoresizesSubviews = YES;
-	self.resultLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
-	self.resultLabel.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
-	self.resultLabel.frame = CGRectMake(88.0, 124.0, 97.0, 37.0);
-	self.resultLabel.tag = 7;
-	self.resultLabel.text = @"";
-
-	self.aText = [[[UITextField alloc] initWithFrame:CGRectMake(193.0, 42.0, 107.0, 31.0)] autorelease];
-	self.aText.adjustsFontSizeToFitWidth = YES;
-	self.aText.autocapitalizationType = UITextAutocapitalizationTypeNone;
-	self.aText.autocorrectionType = UITextAutocorrectionTypeDefault;
-	self.aText.autoresizesSubviews = YES;
-	self.aText.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin;
-	self.aText.borderStyle = UITextBorderStyleRoundedRect;
-	self.aText.clearButtonMode = UITextFieldViewModeWhileEditing;
-	self.aText.clearsOnBeginEditing = NO;
-	self.aText.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
-	self.aText.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
-	self.aText.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
-	self.aText.enablesReturnKeyAutomatically = NO;
-	self.aText.frame = CGRectMake(193.0, 42.0, 107.0, 31.0);
-	self.aText.keyboardAppearance = UIKeyboardAppearanceDefault;
-	self.aText.keyboardType = UIKeyboardTypeNumberPad;
-	self.aText.placeholder = @"Integer";
-	self.aText.returnKeyType = UIReturnKeyDefault;
-	self.aText.tag = 8;
-	self.aText.text = @"";
-	self.aText.textAlignment = UITextAlignmentCenter;
-
-	UILabel *label7 = [[UILabel alloc] initWithFrame:CGRectMake(20.0, 42.0, 165.0, 31.0)];
-	label7.adjustsFontSizeToFitWidth = YES;
-	label7.autoresizesSubviews = YES;
-	label7.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
-	label7.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
-	label7.frame = CGRectMake(20.0, 42.0, 165.0, 31.0);
-	label7.tag = 3;
-	label7.text = @"Value A:";
-
-	UILabel *label8 = [[UILabel alloc] initWithFrame:CGRectMake(20.0, 81.0, 165.0, 31.0)];
-	label8.adjustsFontSizeToFitWidth = YES;
-	label8.autoresizesSubviews = YES;
-	label8.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
-	label8.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
-	label8.frame = CGRectMake(20.0, 81.0, 165.0, 31.0);
-	label8.tag = 4;
-	label8.text = @"Value B:";
-
-	self.bText = [[[UITextField alloc]
-		initWithFrame:CGRectMake(193.0, 81.0, 107.0, 31.0)] autorelease];
-	self.bText.adjustsFontSizeToFitWidth = YES;
-	self.bText.autocapitalizationType = UITextAutocapitalizationTypeNone;
-	self.bText.autocorrectionType = UITextAutocorrectionTypeDefault;
-	self.bText.autoresizesSubviews = YES;
-	self.bText.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin;
-	self.bText.borderStyle = UITextBorderStyleRoundedRect;
-	self.bText.clearButtonMode = UITextFieldViewModeWhileEditing;
-	self.bText.clearsOnBeginEditing = NO;
-	self.bText.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
-	self.bText.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
-	self.bText.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
-	self.bText.enablesReturnKeyAutomatically = NO;
-	self.bText.frame = CGRectMake(193.0, 81.0, 107.0, 31.0);
-	self.bText.keyboardAppearance = UIKeyboardAppearanceDefault;
-	self.bText.keyboardType = UIKeyboardTypeNumberPad;
-	self.bText.placeholder = @"Integer";
-	self.bText.returnKeyType = UIReturnKeyDefault;
-	self.bText.tag = 9;
-	self.bText.text = @"";
-	self.bText.textAlignment = UITextAlignmentCenter;
-
-	self.view.frame = CGRectMake(0.0, 20.0, 320.0, 460.0);
-	self.view.autoresizesSubviews = YES;
-	self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
-	self.view.backgroundColor = [UIColor colorWithWhite:1.000 alpha:1.000];
-	self.view.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
-	self.view.frame = CGRectMake(0.0, 20.0, 320.0, 460.0);
-	self.view.tag = 0;
-
-	[self.view addSubview:background_button];
-	[self.view addSubview:label4];
-	[self.view addSubview:label7];
-	[self.view addSubview:label8];
-	[self.view addSubview:self.calculateButton];
-	[self.view addSubview:label11];
-	[self.view addSubview:self.resultLabel];
-	[self.view addSubview:self.aText];
-	[self.view addSubview:self.bText];
-}
-
-@end
diff --git a/examples/cross_calculator/ios/src/cross-calculator-Prefix.pch b/examples/cross_calculator/ios/src/cross-calculator-Prefix.pch
deleted file mode 100644
index 2f331ed43..000000000
--- a/examples/cross_calculator/ios/src/cross-calculator-Prefix.pch
+++ /dev/null
@@ -1,10 +0,0 @@
-#import <Availability.h>
-
-#ifndef __IPHONE_3_0
-#warning "This project uses features only available in iOS SDK 3.0 and later."
-#endif
-
-#ifdef __OBJC__
-	#import <UIKit/UIKit.h>
-	#import <Foundation/Foundation.h>
-#endif
diff --git a/examples/cross_calculator/ios/src/main.m b/examples/cross_calculator/ios/src/main.m
deleted file mode 100644
index 7866684fe..000000000
--- a/examples/cross_calculator/ios/src/main.m
+++ /dev/null
@@ -1,13 +0,0 @@
-#import <UIKit/UIKit.h>
-
-#import "AppDelegate.h"
-#import "backend.h"
-
-int main(int argc, char *argv[])
-{
-	@autoreleasepool {
-		NimMain();
-		return UIApplicationMain(argc, argv, nil,
-			NSStringFromClass([AppDelegate class]));
-	}
-}
diff --git a/examples/cross_calculator/lazarus/nimlaz.lpi b/examples/cross_calculator/lazarus/nimlaz.lpi
deleted file mode 100644
index 3b9abd129..000000000
--- a/examples/cross_calculator/lazarus/nimlaz.lpi
+++ /dev/null
@@ -1,140 +0,0 @@
-<?xml version="1.0"?>
-<CONFIG>
-  <ProjectOptions>
-    <Version Value="7"/>
-    <General>
-      <Flags>
-        <LRSInOutputDirectory Value="False"/>
-      </Flags>
-      <MainUnit Value="0"/>
-      <TargetFileExt Value=".exe"/>
-      <UseXPManifest Value="True"/>
-      <ActiveEditorIndexAtStart Value="1"/>
-    </General>
-    <VersionInfo>
-      <ProjectVersion Value=""/>
-      <Language Value=""/>
-      <CharSet Value=""/>
-    </VersionInfo>
-    <PublishOptions>
-      <Version Value="2"/>
-      <IgnoreBinaries Value="False"/>
-      <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
-      <ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/>
-    </PublishOptions>
-    <RunParams>
-      <local>
-        <FormatVersion Value="1"/>
-        <LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
-      </local>
-    </RunParams>
-    <RequiredPackages Count="1">
-      <Item1>
-        <PackageName Value="LCL"/>
-      </Item1>
-    </RequiredPackages>
-    <Units Count="2">
-      <Unit0>
-        <Filename Value="nimlaz.lpr"/>
-        <IsPartOfProject Value="True"/>
-        <UnitName Value="nimlaz"/>
-        <CursorPos X="17" Y="21"/>
-        <TopLine Value="1"/>
-        <EditorIndex Value="1"/>
-        <UsageCount Value="21"/>
-        <Loaded Value="True"/>
-      </Unit0>
-      <Unit1>
-        <Filename Value="unit1.pas"/>
-        <IsPartOfProject Value="True"/>
-        <ComponentName Value="Form1"/>
-        <ResourceBaseClass Value="Form"/>
-        <UnitName Value="Unit1"/>
-        <CursorPos X="26" Y="27"/>
-        <TopLine Value="2"/>
-        <EditorIndex Value="0"/>
-        <UsageCount Value="21"/>
-        <Loaded Value="True"/>
-      </Unit1>
-    </Units>
-    <JumpHistory Count="12" HistoryIndex="11">
-      <Position1>
-        <Filename Value="unit1.pas"/>
-        <Caret Line="27" Column="1" TopLine="1"/>
-      </Position1>
-      <Position2>
-        <Filename Value="unit1.pas"/>
-        <Caret Line="15" Column="3" TopLine="1"/>
-      </Position2>
-      <Position3>
-        <Filename Value="unit1.pas"/>
-        <Caret Line="17" Column="26" TopLine="1"/>
-      </Position3>
-      <Position4>
-        <Filename Value="unit1.pas"/>
-        <Caret Line="16" Column="18" TopLine="1"/>
-      </Position4>
-      <Position5>
-        <Filename Value="unit1.pas"/>
-        <Caret Line="20" Column="43" TopLine="2"/>
-      </Position5>
-      <Position6>
-        <Filename Value="unit1.pas"/>
-        <Caret Line="21" Column="48" TopLine="16"/>
-      </Position6>
-      <Position7>
-        <Filename Value="nimlaz.lpr"/>
-        <Caret Line="1" Column="1" TopLine="1"/>
-      </Position7>
-      <Position8>
-        <Filename Value="unit1.pas"/>
-        <Caret Line="52" Column="17" TopLine="9"/>
-      </Position8>
-      <Position9>
-        <Filename Value="unit1.pas"/>
-        <Caret Line="51" Column="12" TopLine="9"/>
-      </Position9>
-      <Position10>
-        <Filename Value="nimlaz.lpr"/>
-        <Caret Line="21" Column="3" TopLine="1"/>
-      </Position10>
-      <Position11>
-        <Filename Value="nimlaz.lpr"/>
-        <Caret Line="20" Column="1" TopLine="1"/>
-      </Position11>
-      <Position12>
-        <Filename Value="nimlaz.lpr"/>
-        <Caret Line="21" Column="7" TopLine="1"/>
-      </Position12>
-    </JumpHistory>
-  </ProjectOptions>
-  <CompilerOptions>
-    <Version Value="8"/>
-    <SearchPaths>
-      <IncludeFiles Value="$(ProjOutDir)/"/>
-    </SearchPaths>
-    <Linking>
-      <Options>
-        <Win32>
-          <GraphicApplication Value="True"/>
-        </Win32>
-      </Options>
-    </Linking>
-    <Other>
-      <CompilerPath Value="$(CompPath)"/>
-    </Other>
-  </CompilerOptions>
-  <Debugging>
-    <Exceptions Count="3">
-      <Item1>
-        <Name Value="EAbort"/>
-      </Item1>
-      <Item2>
-        <Name Value="ECodetoolError"/>
-      </Item2>
-      <Item3>
-        <Name Value="EFOpenError"/>
-      </Item3>
-    </Exceptions>
-  </Debugging>
-</CONFIG>
diff --git a/examples/cross_calculator/lazarus/nimlaz.lpr b/examples/cross_calculator/lazarus/nimlaz.lpr
deleted file mode 100644
index 4457209d1..000000000
--- a/examples/cross_calculator/lazarus/nimlaz.lpr
+++ /dev/null
@@ -1,21 +0,0 @@
-program nimlaz;

-

-{$mode objfpc}{$H+}

-

-uses

-  {$IFDEF UNIX}{$IFDEF UseCThreads}

-  cthreads,

-  {$ENDIF}{$ENDIF}

-  Interfaces, // this includes the LCL widgetset

-  Forms

-  { you can add units after this }, Unit1, LResources;

-

-{$IFDEF WINDOWS}{$R nimlaz.rc}{$ENDIF}

-

-begin

-  {$I nimlaz.lrs}

-  Application.Initialize;

-  Application.CreateForm(TForm1, Form1);

-  Application.Run;

-end.

-

diff --git a/examples/cross_calculator/lazarus/nimlaz.lrs b/examples/cross_calculator/lazarus/nimlaz.lrs
deleted file mode 100644
index 234df82bd..000000000
--- a/examples/cross_calculator/lazarus/nimlaz.lrs
+++ /dev/null
@@ -1,5222 +0,0 @@
-LazarusResources.Add('MAINICON','ICO',[

-  #0#0#1#0#6#0#0#0#0#0#1#0' '#0#226#145#0#0'f'#0#0#0#128#128#0#0#1#0' '#0'('#8#1

-  +#0'H'#146#0#0'@@'#0#0#1#0' '#0'(B'#0#0'p'#154#1#0'00'#0#0#1#0' '#0#168'%'#0#0

-  +#152#220#1#0'  '#0#0#1#0' '#0#168#16#0#0'@'#2#2#0#16#16#0#0#1#0' '#0'h'#4#0#0

-  +#232#18#2#0#137'PNG'#13#10#26#10#0#0#0#13'IHDR'#0#0#1#0#0#0#1#0#8#6#0#0#0'\r'

-  +#168'f'#0#0#145#169'IDATx'#218#236']'#5#128#28#245#213''#235'n'#231'w9I.N'

-  +#136#144#4#139#17#220'['#220#221#138';!'#184'|'#180#148#2#197'Kq'#13'R('#20

-  +'(V'#180#197#3#9'!'#144#144#16#187#156#219#202#173#251'~'#239#253'gfo'#246'r'

-  +'~'#187';{'#242#131#201#236#173#204#204#202#251#253#159'?'#25#140'a'#12'c'#24

-  +#181#144'I}'#1'c'#24#195#24#164#195#24#1#140'a'#12#163#24'c'#4'0'#134'1'#140

-  +'b'#140#17#192#24#198'0'#138'1F'#0'c'#24#195'('#198#24#1#140'"'#140#155'}2}'

-  +#223#6#220'L'#184#25'q'#11#227#230#161#173#225#167#21#17#169#175'o'#12#217

-  +#199#24#1#12'c'#160'@'#155'q7'#21#183'i'#184'M'#198#173#8'8'#225'N'#217'd2'

-  +#153'p'#155#132#191#167#239'<'#132#155''''#145'H0B'#224'7'#175#232'v'#7'n'

-  +#219'p'#219#128#219'F'#220'j'#145'4'#18'R'#6'c'#24#26#198#8' '#199#129'B.'

-  +#199#221'x'#224#132'|'#170#176#161'P'#211#223'%='#189'N'#214#195'7+'#235#230

-  +'+O@'#207'r'#156#232#249#161#0#146#197'&'#232'$'#132#228#134#196#224#145#250

-  +'s'#27'C'#255'0F'#0'9'#6#20'x'#18#234#189'q'#219#7#183'=P'#208'ie'#215#136

-  +#159'#'#22'nA'#160'e'#194#157#226#199'DO'#20'nwG'#12'b!O'#240'$R'#238#236

-  +#230'q'#209#157']I'#2#159#211#136#187#181#184'}'#198'o'#171#145#20'bR'#182

-  +'c'#216#17'c'#4' 1P'#224#11'p'#183#20'8'#161#223#27#5'u'#186#240'X'#167'L'

-  +#239'('#228't'#155#219#0#228#244#12#185#140'{'#158#140'{'#157#140#191'!'#235

-  +#250'|'#232'<'#166' '#196#130#252#146'ps'#27'pRM'#255#203#184#191#197#143#209

-  +#235#196#247'AbGb'#232'B'#10#29#248#248#255#160#147#16'~'#26'3'#31'r'#3'c'#4

-  +#144'e'#160#192'[p'#183#23#240#171'<'#10#229'L'#224#191#7#177#192#203'8)'#6

-  +#185'\'#158#20'h'#185#156#187'_.'#227#4#158#30#163#219#10'z'#142#188#203#227

-  +#252#30'D'#247#201#21#10#220'+AF{P@\'#22#135'D,'#134#194#26#131'8'#238#227

-  +#241#24'/'#216'qn'#31'O'#0#222#194#251#241#223'8@'#28#255#137#199#185#191#227

-  +#137#4#187#157#224#239'#'#2#136''''#186''''#134'nH'#193#129#247''#14'<! '#25

-  +#172#147#250'{'#25#173#24'#'#128','#0#133#158#28'p'#199#224'v'#26#10#227'b'

-  +#220'+'#232#254#174#2'/'#19#11#171#188'S'#176'i'#175'P'#200#153#192'''o'#227

-  +#166'R'#169'A'#173'1'#128'Z'#173#3#149'V'#15'J'#165#26#31'S'#226'cJ~'#143#130

-  +'.W'#224'm'#5'w'#155'i'#10#144#212#20'8'#1#5'v'#131#9'6'#9'q,'#202#136' '#22

-  +#231'H!'#22#141#178#219#177'H'#4'"'#17'?'#132#131#1#8#135'|'#16#9#7'!F'#143

-  +'#'#25#196'bqF'#10#220'>'#193#238#139#11'['#146'(R'#9#161#139#217'A&'#195#10

-  +#220#158'G2'#248'E'#234#239'k4a'#140#0'2'#4#20'z%'#238#246#7'N'#232''#143'{'

-  +#29#221'/'#172#230#226#21'^,'#240'$'#172'$'#224'J'#165#2#148#180'W)A'#163#214

-  +'3'#1#215#144#176'ku'#160#209#242'B'#175'V'#129#10#159#175'T'#202#217's'#233

-  +'u'#10#185'p'#172'.'#26#129#130'3'#21#184#251#216#149'p'#23#202'Vn'#224'Vo'

-  +#129#4#226#156#128#198'x'#1#142#197'8'#161#142'E'#19#16'E!'#143'D'#227#16'Eb'

-  +#8'!'#9#132#131'~n'#11's'#251#16'#'#135'0>/'#134#207#225'6'#129#16#24'A'#136

-  +#8#129#206#215'U;'#192#251#215#224#238#5#220'^B2h'#150#250'{'#28#233#24'#'

-  +#128'4'#3#5'.'#238'N'#197#237'D'#20#190'b'#186'O,'#244#130#170#174#144#209

-  +'*.c'#2#175#228#133'^'#173'T'#162'P'#171'Ao'#178#130'^o'#3#157#209#10'Z'#29

-  +#10#187'J'#129#171'=nL'#208#233#182#156#221'V)'#21#201'=#'#11'%G'#0'D$r9g'

-  +#247#211#185#146'&'#3#179#8'd)'#206'AnE'#134#20'5'#30'e'#149#187#29#231'6"'

-  +#128'h'#156#132'9'#158'$'#128'H'#132#246'1'#238#182'h'#31#198'}8'#24#130#128

-  +#175#3#252'^'''#248'}N'#8#5'}'#236'1'#129#16#162#177#174#132#16'g'#218#1#153

-  +#24#220'5'#177']'#12#175#225'c'#224#200#224'M$'#3#191#212#223#237'H'#196#24#1

-  +#164#1'('#244#21#184';'#25#183'SQ'#184'v'#162#251#186#21'z~'#133#231'Vv'#18

-  +'^'#20'x'#149#10#244'('#232'z'#163#13#12#6#220#27#204#184#226#227#170#175'Q'

-  +#130'V'#173'`'#194'O'#171'U'#188#192#171':'#133'^'#197#142#195#173#254't'#31

-  +'g&p'#171''#210#148#160#235#224#29#132#130#250#159#18#9#224#205#0#178#245

-  +#133#21#153#217#251#252#223'$'#155'$'#160#209'8'#183#250'w'#146#0#10'2'#145#0

-  +#222#14'G8'#2#160'=m'#161'H'#20#205#132#24#4#195#220#237'@ '#0'~'#143#139#145

-  +#1#145'B'#136#204#8#188#159'#'#131#24'gJ'#224'qbq'#193#223#176#131#153#224'E'

-  +'2'#248'''pd@>'#131#184#212#223#249'H'#193#24#1#12#1'('#248#243'pw='#10#219

-  +#17#192'-'#174#12'rN'#210'8'#149'\&O'#170#244'*'#21#9#178#146#9#188#193'd'

-  +#195'}'#30'nf'#208'jT'#160#19#132#30'7'#29#19'~%G'#4'<'#9#168#149#220#202'O'

-  +#171'<'#169#253#10#165#12#148'ra'#213#167's'#164#10'>]'#2'wA'#188#192#11#209

-  +#129'D*'#1'$U'#239#164'g'#159#255#139#183#215#227#188'3P'#16#206'(o'#14#16#9

-  +#196#4'2Hj'#5#164#1#160#208#147#224#135#163'l'#31#8'E!('#222'P'#240#253'>'#31

-  +#4#188'.'#240'!'#25#248'<'#14'4'#27#130#248#218'(#'#5'F'#4#140#12'x"'#232#226

-  +'3'#192#191')'#25#233'/'#184'='#131'D'#16#146#250'70'#220'1F'#0#131#0#10#254

-  +'"'#220#221#128#194'v'#16#253#157'\'#237'y'#207'<S'#195#153#208'+'#217'J'#205

-  ,#132#30'Wv'#163#181#24#204#184#233#13':'#20'x'#21'h'#181'x'#191'H'#232'5'#188

-  +#224#179#219'$'#252'*A'#221'W$Wx'#177#176#11#231'JQ'#239#229'"'#129#23#9'{JN'

-  +#128#232#189'$xg '#187#157#232'b'#143#11#130#199#251#8#4'3!'#198#251#10#216

-  +'>'#198#147'BLl'#30#136#136#0#133'>'#16'N%'#1#250';'#16#196'-'#20#129'` '#2

-  +#30#183#3'<'#174'f'#240'x'#218'Q;'#8'32 '#13'C'#236'd'#20'L'#4#17#17#144#227

-  +#240'^'#220#30'C"'#240'I'#253#155#24#174#24'#'#128#1#0#5#159#156'z7'#162'0-'

-  +#161#191#185#24'<-'#181#220'j'#159'j'#203#163'0'#235't`'#182#20#131#201'V'#2

-  +'F'#147#25#244'Z'#21#24'p'#163#189'^G'#130#174'b'#194#175#21'V|'#141#130#9'='

-  +#17#6#19#252#164#131#143#183#229#153#195#16'D6='''#224#178#174#241#254'.'#215

-  +'-'#147#13#236'k'#22''''#1#137's'#4#128#251#159'#'#2#193'THtF'#16#146#132'@'

-  +#26'B$'#158'4'#11'B'#145'N'#141' '#200#147'A '#24#1'?'#146#128#31'I'#192#239

-  +#167#219#184#5#130#224'v'#181#129#219#217#194'i'#6','#242#16'c'#14#199#164

-  +#137#16#139#167'h'#5'x'#187#29'/'#233'~'#220#30'F"'#232#144#250'72'#220'0F'#0

-  +'}'#128'/'#160#249#29'p+'#254#174't'#31#23'^'#239#12#209'1!Ur'#234#189'V'#171

-  +#1#147#165#136#173#244'&K'#30'/'#236'j0'#232'T'#201'M'#199#147#128'V'#195#169

-  +#248#156#208'+83A'#201#31'O.'#178#231'{'#17'v'#254'&'#183#239'A'#208#7'"'#255

-  +'='#165#254'v%'#133'D'#167#154#176#3')p^'#255#4#243#27#196'D'#142'C'#210#10

-  +#194#188'_'#128#153#7'A'#142#8'|'#129'0n'#17#182#249'i'#143'Z'#128#23#205#4

-  +#15#18#1#145#129#223#239#193#215'E'#153'f'#192#162#11#188#19#177#139#175#128

-  +#146#141#30#193#253'}H'#4#237'R'#255'n'#134#11#198#8#160#7#240'9'#248#199#1

-  +'g'#227'S'#178#206#14#130#175#18#236'z\'#189#13'&+X'#243#199#161#224#23#162

-  +#144#235'P'#232'U`'#228#5#158#8#192#168#231#132'^'''#8'>'#9#189'Z'#145't'#230

-  +'q'#26#132'8'#254'/J'#254#17'.'#170#15'u>['#232#202#17']'#201#128#249#14#4#13

-  +'A'#228'?`'#209#4'2'#17'b'#228','#140'32 '#159#0#167#13#136#8#0'5'#2'/'#146

-  +#130#159#255#219#227'qA'#135#179#25':'#28#141'hR'#132'92Hj'#5';'#16#129#31

-  +#207#255#24#238#239'A"h'#148#224#227#25'V'#24'#'#128'n'#128#194#191#27#238#30

-  +'EA'#163#144'^'#138#224'+yU_'#141'6'#188#6'7'#163'9'#15'l'#133#149'`'#177#230

-  +#163#176#227'J'#143#130'n'#212#171#217'mN'#232#213'('#244'h'#235'k95_'#205'<'

-  +#250#188#7'_'#217#25#183#239'N'#173'g'#231#22'.j8|S]'#202#7'RR'#135'E'#230'B'

-  +','#193#231#20#240#161'Cf"'#136'|'#3'$'#244'^A+'#240#163'6 '#236'}~p'#180#213

-  +'!'#17'4'#224#243#130#140#8':#'#9#169'D'#128#231#14#224'%'#220#137#219'_'#198

-  +#156#133'=c8'#252#172#178#6#20#252'<'#220#221#137#194'w.'#240'2'#152'"'#248

-  +#188#154#175'A'#219#221'd)`'#130'oE5'#223'hP3'#161'7'#25'4L'#232#13#188#202

-  +#159#180#241#153#224#139#156'y'#188'z/'#8#190'8?'#165#208'g'#128#182'{'#174

-  +#161''''#167'b2'#225#136'O2'#226#180#2'!'#140'H>'#130'X'#167#143#0#137#192'C'

-  +#194#207#8' '#12'n'#220'{'#188'~p'#182#215#131#203'^'#15'A'#144#249#10'z!'#2

-  +#170'X'#188#24'I'#224'?R'#30#185#136#225#253#11'K'#19'x;'#255','#220#238'B'

-  +#161#203#23#156'{2'#133#160#234's+'#183#22'W|'#147#181#8#242#138'+'#193'l'

-  +#182#129#137#9'='''#248'&#'#238'Q'#240'uL'#229'W'#177#24#190#134'9'#243'RU|q'

-  +'r'#14#8#222'z'#254':d#'#248#235#216#161'z0'#25'f'#228'3'#17#197'&'#2'i'#5'Q'

-  +'^+ '#18#8'F'#152#22#224#245#133#192'C$'#224'#R'#8#129#215#27#0#167#189#1#156

-  +#168#21'P'#174'A('#140'D@9'#9'T'#215#192';'#11#227#157#213#141#175#225#238#10

-  +'$'#130#6#169'?'#139'\'#194#200#253#197#245#19'('#252#187#224#238'o('#152'{'

-  +#136#195'y,'#227#142'6'#18'|T'#227'-'#214#18#176#21'U'#160#224'['#153#208#155

-  +'I'#232'y'#225'g'#26#128#150#19'|q'#248'N'#156#158'+'#8'>'#147#249#212'z'#222

-  +#209#9'Q'#232'1'#233'7'#224#137#128#204#132#8#159'tDQ'#0#166#17#132#200'9'

-  +#136#155#143#211#8#220'H'#6'D'#4#28')'#4#145#8#26#145#8'j'#193#239#247'!qt'

-  +#18#1#167'm'#196#5'm'#128#26#156#220#138#219#3'H'#4'Q'#169'?'#130'\'#192'h'

-  +#253#249#9'Uyw'#160'0^'#128'{'#133#160#238#179'"'#27#222#185'G'#9':'#228#205

-  +'/('#169'F'#193'71a'#239'I'#240#5#199#30'K'#203'U'#202'S'#236'zN'#232#187#177

-  +#235#199#176'C'#30'BB'#172#25#176#236'CN+'#8'G'#163#157'D'#192#251#4'8m'#128

-  +'#'#2#143'@'#4'm'#13#224'h'#169'A'#205'!'#192#136' '#194#155#6']'#204#2'*8'

-  +#186#16'I'#224#11#169#223#191#212#24#149#191'E'#20#254'Spw'#15#229#234'''Km'

-  +#21'|J-+'#190'A;'#222'hB'#193#159#4#182#252'"&'#244'f#'''#252'f^'#240#13#188

-  +'W_'#219'e'#197'O&'#231#240'R'#223#25#162#147#250']'#231'>D'#193#4'n/'#242#21

-  +'$S'#144'{'#210#8#188'!'#232' '#18#160'}'#135#23#218#155#183#130#203#217#4

-  +#193'`'#152#249#21#136#8#226#172#246' %'#153#232'y'#220']'#141'D'#208'&'#245

-  +'{'#151#10#163#234'g'#137#130'O'#141'0'#31'G'#193'?'#145#254#22'Vh'#5#191#234

-  +#171'U*'#180#225'5'#204#185'WP2'#30','#6#29'XL'#26#176#24'9'#2#160'U'#159#9

-  +#190'F'#136#225'+9'#167#30#139#219#11'y'#255'\'#213']'#231'''<'#170'>'#226

-  +#244'A'#228'@L@gM'#2#249#9'"'#2#17#136#162#6#130'Y'#208#225#9'22'#160#205#225

-  +'h'#135#246#166'MH'#10#29#156'6'#192#242#8#226#156''#160'S'#27#160'P'#225

-  +#137'H'#2#255#147#250'-K'#129'Q'#243#235'D'#225#159#141#187''#160#144'N'#17

-  +'V}'#5#159'i'#199#169#251'h'#215'['#11#160#176't2'#218#251'f&'#244'V'#163#22

-  ,#204'D'#0#6'-S'#251'){'#143#4#159#203#244#227#146'u'#196'e'#183'];'#248#140

-  +'a'#232#16#151#11#11#230'A,'#193#167#30'G'#184#228'"'#230','#196#205#195#155

-  +#5#29#222' '#18'A'#136#237#221'H'#8#237#173#181#224'l'#217#14#254'`'#16'B'

-  +#225'0'#159'f'#28'Oj'#24#192'U'#30#222#130#251';G['#161#209#168#248#165#162

-  +#240#159#135#187#7'PH'#181'2'#190#6'_H'#217#165#144#158#222'`'#196#21'"'#228

-  +#229#151#178#149#158'V}+['#249'9'#193'7'#232#213'l'#213#167#172'='#242#234's'

-  +#9';\'#201#173'Pp'#195'>'#204'Q'#241'iJ'#131#206'DDn'#229'N'#166#31#147'F'#16

-  +#229#170#16')'#143#128#210#138#189#188'I'#224#18#17'A'#135#219#11'mM'#155#161

-  +#195#209#130'&'#4#146#0'%'#19#9#209#130'Nm'#224'#<'#193')H'#2#173'R'#191#223

-  +'laD'#255'd'#249'N<'#143#137'U~'#193#214#167'D'#30#29#174#250'yE'#168#238#23

-  +'O'#0#179'Y'#199#4'>'#185#242#27'9[_H'#217'e'#169#186#138#212#226#155#222#26

-  +'m'#142'!3'#224#139#21#147#14#195'd>A'#148'K7'#230'2'#11'9"'#224#28#132'H'#4

-  +'n$'#0#242#13#144'Y`o'#133#246#230'M'#224#243'x:'#181#129'h\'#28')h'#194'3'

-  +#156#140'$'#240#153#212#239'5'#27#24#177'?]^'#229''#141#186#234#138'='#252

-  +#172#180'VEN>#'#148#148#239#4'V[>'#191#226'k'#25#1#144#202'O'#241'}'#178#245

-  +'uj~'#213#23'j'#237'E^}'#246#225#141#216'O/'#247#145'$'#2#232#236'AH)'#199'Q'

-  +#190#0#137#10#142#168#208#200#231#227#136#128#132#223#229#9'2'#31'A'#135''''

-  +#0#173#13#191#177#208'a'#144'O-'#142'&C'#134#236#200#184'K'#220#142#251#255

-  +#27#233'&'#193#136#252#9#163#240#255#1'w'#247#139'U~'#174#147#14#197#244'U`'

-  +#177#21'Cq'#249'4'#176#153#13'('#252'Z&'#252#164#242's'#153'|\'#234'.'#173

-  +#250'\5'#30#223'aG&'#235'6'#23''#12#210'"'#145'd'#1#224#27#152#242'D'#16#137

-  +#179#162'#"'#2#31#239'$'#228#132'?'#4'N'#158#8#236'm'#13#208#214#248#27'j'#11

-  +'A'#150'D$D'#10'D&'#193#167#192'i'#3'#'#182'5'#217#136#250'%'#243'*?y'#249'O'

-  +#160#191#197'*'#191'FM'#130#173#129#252#210'j(,'#169'bj'#190#213#162#5#155'I'

-  +#155#12#241#137#227#249'B'#18'Ogn>$k'#236#199#144'['#160#214#229#157#185#4

-  +#188'Y'#192#218#152'qY'#133#212#153'('#192#215#23'0'#223#0#145#128';'#128#166

-  +#1#238#157'Nh'#169'_'#15'^O'#7#231#27#216#209'$h'#1#206'/'#240#177#212#239'3'

-  +#19#24'1'#191'f'#20'~'#26#139#245#30#10#255#188#174'I=\\'#159'T'#254#25'`'

-  +#203#207'c+'#190#205#164#3#171'Y'#203#135#247#212#172'XGH'#221'e'#13'7'#146

-  +#234#190'h'#229#151#250'M'#142#161'G'#136'|'#132#220','#131'8'#223#201'('#193

-  +'7)'#9'sNB'#210#6#220#188'9@'#190#1#167#155#246#254#164'I'#16#224#27#146'DY'

-  +#155#244'$'#9'P'#214#224#217'H'#2#207'K'#253'>'#211#141#17#241#155'F'#225#159

-  +#128#187#255#160#240'O'#18#132#159'b'#243#228#229'gi'#188#182'"T'#249#167#163

-  +#192#163#202#143'B'#159#135#155#133'_'#249#141'|}'#190#154'o'#192'A'#141':'

-  +#229'2Q'#231#220#17#241#9#141'B'#240#13'L'#200#128#23'Z'#153#9#185#3'T['#224

-  +#241#134#185'('#1'j'#1#14#212#6#220#168#21#180#183#212'C['#243'&'#214#152#132

-  +#202#142#133#156#1#222'/'#128'<'#144'X'#134'$p'#143#212'o-'#157#24#246'?o'

-  +#222#217#247#1#10''#137#216#222#167#226#29#157'V'#203#210'x'#11'H'#229''';'

-  +#223#204#169#252't'#155'e'#243#233':='#252#201#10'='#161':O'#234'76'#134#161

-  +'#'#193#165#26#11'-'#200#163#188#147#144#186#19#177#188#1'_8'#169#13'8'#153

-  +'F'#16'@'#147#160#3'Z'#234#214'1'#147#128#146#135'('#5'YD'#2't'#172#187'qw'

-  +#237'H'#153'l4'#172''#231'('#252'4a'#231'-'#20'~'#139' '#252','#163#143'T~'

-  +#131#1#138'+f@^^A'#167#224#155#185'0'#31#9'?+'#213#229#227#250'B'#231#29#232

-  +'R'#142';'#134#145#1'q'#163#211'd'#164' '#202#165#19#11#225'Br'#14':'#220#28

-  +#9#208#214'Z'#191#1'\'#246'&'#212#24#194','#170#16''''#191#0'$M'#130#231#240

-  +#128#231#140#132#130#162'a'#251'sG'#225'?'#10'w/'#161#240'k'#132#240#28#169

-  +#252'j'#170#213'7'#153#160#164'r6'#216'lff'#235#219#204#218#164#189'/'#180

-  +#228'R'#243#173#183#200#209''''#212#225#143#9#255#200#133#144'H'#148'lJ'#130

-  +'&A'#24#237#252#16'_n'#204#162#4'nN'#27' '#147#128#162#4'-'#13#155#192#209'Z'

-  +#203#234#9'('#162' '#174'%@'#18#248'7'#30#238'8$'#129#128#212#239'm('#24#150

-  +'?y>'#204'G%'#188'r'#193#211'/8'#251#168#15'_i'#213'L'#176'YM'#144'G'#206'>'

-  +#139'.'#25#226#211#179'&'#29#202'd'#223'='#161'D'#151#251' '#134#229'G1'#134

-  +#1'B<'#216'Th^J+'#188'@'#2'd'#18#144'c'#208#217#17'`'#251#182#230#237#208#214

-  +#180#9#130#129'03'#7#186'D'#8#190#194'C'#30#142'$'#224#148#250'}'#13#22#195

-  +#238'W'#143#194'3'#10#238'mt['#16'~5_'#193'g'#177#22'@i'#229'L'#176'Z'#245

-  +'L'#240#137#0#200#233#199#132#159'o'#201'%'#30#160#193'>'#128#177'e'#212'A<'

-  +#2']'#156'<D~'#1#175#136#4#28#29#1#22')hk'#173'G'#147'`#'#4#130'!|^'#164'+'#9

-  +'Pi'#241'A'#195#181#209#200#176#250#245#163#240#255#5#5#246#26#186#205#166

-  +#226'*'#185#250'{*'#228#177#229#149'@Q'#229'N'#144'g&'#149'_'#199'b'#252'V'

-  +#163'P'#196#163#226'b'#251#226':}'#217#152#240#143'f'#8'$'#16#231'{'#21#198

-  +#248#150'd'#20'%`$'#224#225'5'#1#182#5#160#189#189#5'Z'#235#214#131#207#31

-  +#224#194#132#148'4'#212'I'#2'5x'#168'E'#195#145#4#134#141#4#160#240'_'#5'\'

-  +#13'R'#248#201'{'#207#132#191#160#2#138#203#167#160#170#143#171#190'E'#155

-  +#20'~'#202#234'#'#225#23#242#248#147'^~'#24#157#246#190#215#235#134#143#222

-  ,''#19#190#253#234#19#216'^'#179#25#206'8'#247'J8'#232#208'c'#164#190',I'#209

-  +#217#180#148#139#18'DDEEIs'#192#205#153#3'v'#135#29'Zj'#6#159#207#151'lH'

-  +#218'E'#19'X2'#220#204#129'a!'#6'|'#3#143#231'ed'#244'w'#17#254#252#226'j(*'

-  +#171'f'#161#189'<f'#239'kYn'#191#144#210'+.'#226#1#217#232#246#242#31#184'x'

-  +#10#174't'#169#142#235'Ysv'#131'?'#223#255'<'#168#213'j'#169'/O2p='#10';['

-  +#146'Q'#203'r'#150'/ J'#26'b&'#1#31'&l'#222#190#150#145'i7$@>'#129#253#135

-  +#147'c0'#231#197#1#133#159#198'o'#189#141#178#175#18#219#252':'#173#26#10#203

-  +#166'@Aq%'#243#240''''#133#159'<'#253'$'#252#201#10'>'#174'tW6'#22#219#135

-  +#223#29'0'#11#2#254#29#167'hi'#181':'#184#231#161#151'`'#234#244'YR_'#162'd'

-  +#224'9 '#233#23#136'D'#184#209'f'#228#28'd'#21#133#148''''#208#17'd'#161'B'

-  +#167#203#141'$'#240#19'x<'#29#221#145#192';x'#168'#'#145#4'bR'#191#167#254' '

-  +#167'e'#130#239#207#255')'#10#191'A,'#252#180#242#23#148'N'#130#162#210#241

-  +'I'#225#23'r'#250#5#225#231'Fi'#203'E='#249'r'#250#173'f'#5''''#31#181#16#218

-  +#219#186#175'kQ('#148'p'#239'#'#175#192#244#25#187'H}'#153#146'AhP'#10#2#9'D'

-  +#185#225'%b'#18'pv'#8#155#27#154'j'#214#128#215#227'fm'#201#187'8'#6#159'F'#2

-  +'8['#234#247#211#31#228#172'T'#160#240'O'#193#221'W('#184#5'B'#146#143' '#252

-  +#249'EU'#156#205#207#132#159#203#235'gi'#189'z.'#179#143#28#131#138#148#148

-  +'^'#24#157'F'#23','#191#252#20#248'i'#245#183'=>N$p'#215#131'/'#194#140#153

-  +#243#165#190'T'#233#144#28'f'#2#201#129#167'D'#2#212''#144#230#18#8'$@'#230

-  +#128#195#233'B'#18#248#17'|>2:'#192'&'#21'q'#14#198'?#'#9'\'''#245#219#233

-  +#11'9)'#21'('#252#165#184#251#26#133''#188'8'#195#143'9'#252#10#199'AI'#197

-  +'N,'#185''''#143#247#246#179#236'>'#157#154#13#215'd'#194'/j'#211#149#179'oR'

-  +#2#216#219'Z'#224#180#227#150#224#15#180#231#18'w'#185'B'#1'w'#222#251'<'#204

-  +#156#179#155#212#151'+'#9#196#195'P'#133#206'CD'#2'!VC'#16'e'#221#135#133'""'

-  +#202#21' '#199' i'#2#1#170#31#160#241#230#204#28'H'#8'$@s'#8#238#151#250'='

-  +#245#134#156#147#13#190']'#247#255'Pxg%'#133#31''#148#26#190#168#167#140#226

-  +#252#164#242'S'#156#223',R'#251#213'JP'#170#184'>}c!'#190#158'q'#255']'#215

-  +#194''''#31#190#217#235's'#228'r'#5#252#249#129#21'h'#14#204#149#250'r%'#133

-  +#16'*'#20'r'#5'h'#132#25#149#21#139#205#1#210#4#236#237#173#208#178#253'g'#8

-  +#132#130#140'('#132#218#129#4'7R'#249#148#134#159'^zI'#234#247#210#19'rJJP'

-  +#248#201#21#253#17#141#223#22#170#250#148'|'#134#159#217#146#7'%'#19'fC'#158

-  +#201#0'6'#171#150#197#250#217#202#143#194#175#213'(S'#187#246#228#212#187#202

-  +'-'#196#227'Q8'#254#176'y'#16#194#31'ko'#208'hu'#240#220'k_'#130'^o'#148#250

-  +#146'%'#133#216'1('#152#3#204''''#224#237'L'#27'&'#231' '#151','#180#129#235

-  +'0'#20'I!'#129#8#30#225'0$'#129#156#28'M'#150'S'#162#130#4#240#16#10#255#197

-  +#226#146'^'#141#138'r'#251'-P:a'#23'\'#249#141'l'#213'g'#194'o'#226'V~-'#133

-  +#250'D'#243#246'r'#234#13#229'('#222#254#231#179#240#244#223#255#220#231#243

-  +#202'+'#171#225#225#167#222#147#250'r%Grr'#17#175#9#4#187#144#128#144''''#208

-  +#218'T'#3#246#166#205#157'$'#208#217'O'#192#145'H'#196'vi\'#251'J'#173#212

-  +#239#165'+rF^'#248#226#158''#138#19'}'#168'A'#7'5'#242'('#173#154#11'y63W'

-  +#213#199#215#242#147#195#143'*'#250#132#129#28#178#177#149'@8'#251#132'%'

-  +#224't'#244'='#15'c'#191#131#143#133#11#175#184']'#234#203#149#28'B1'#17'9'

-  +#249'"|'#187'1.O '#204'J'#137#137#4':P'#19'hi'#220#4#246#214#237#16#162#198

-  +'"'#169'ME'#190#142#4#28'K[{?"'#245'{'#17'#''D'#134'o'#232#241'#'#149#245#10

-  +#225'>'#18'~'#157'^'#15#165#227'w'#1#155#205#154't'#248'Y'#153#218#175#225

-  +#235#248';'#213'~!'#183''#12#253#195'/?}'#7#183',;'#163'_'#207#189#234#134

-  +#251'`'#193#146#131#164#190'd'#201#145#28'Y'#22#227#204#1#210#4'|B'#155#177

-  +'dw'#161' 4'#213#173#131#14#190#148'85<'#24#191#27'M'#129'k!e'#144#186#180

-  +#144'\jx'#187#255'K'#20#254']'#187#134#251#138'+w'#134#130#130#210'd'#172#191

-  +#179'}'#151#138#235#207#207#183#232#22#6'}'#140'a`8'#239#228#165#168#5#244

-  +#221#2#159#194#131#15'='#253#1#20#22#149'I}'#201#146#131'K'#27#230'5'#1#190

-  +#185#8'M.'#166#218#1#18'~'#135''''#192'Z'#140'5m'#253#17'<ng'#215#200'@'#130

-  +#247#7#188#15'9B'#2#146'K'#13#18#192'}('#252#151#167'8'#253'P'#181'O'#198#250

-  +'M'#156#205'/.'#233'U'#11's'#248'd|'#168'O'#226'wq'#238#137#139'Y'#7#25'6/@'

-  +#174#128'3'#254#176#28'U'#231#227#164#254'h'#251#196'#'#247'\'#7#255#251#244

-  +#237'~='#215'j+'#128'G_'#248#140#17#244#168'G'#2'R'#134#146'P'#218#176#143'u'

-  +#29#230':'#14'S'#21#161#147'r'#4#182#173'b'#237#197#186'8'#5#219#19#241#216

-  +'.'#141'?'#191'R/'#245#219' H*:('#252#191#199#221#191'R'#236'~'#230#241#183

-  +'A'#217#248#185#184#242's'#225'>'#202#242'3'#25#185#149#159#230#241'q'#133'='

-  +'\'#168'Oj'#6#251#237#215#31#225#230'kNM'#185#143'H'#224#222#199#222#134#210

-  +#210'*'#137#175#174'wl\'#183#26'n'#185#246#180'~?'#238#174'{'#193#178'['#30

-  +#145#250#178's'#2#180'|s-'#200#169#187#16#154#3'An"'#17#235','#212#17#20#149

-  +#17#175'Ov'#27#22#249#3#190#12'y['#246'n'#223#242#177#228#29#133'$'#147#31#20

-  +'~'#146#14#178#251'm'#226'4_='#218#253'e'#19#230#161#221'oa'#130#207'9'#253

-  ,'h'#229'W'#179#22'^\I'#175'<9r[j'#220'y'#243'y'#240#243#143#223#236'pqi'#5

-  +#220#247'xnz'#208#219'['#155#224#213#23#30#128'5?|'#1'>'#175'{@'#175'='#243

-  +#130#27'`'#255'CN'#144#250'-H'#14'![0'#193#135#7'I'#192'i"'#145#208'^'#140'5'

-  +#20'Am'#160#185#238'Wp'#182#215'C8'#196'u'#26'&'#194#224#15'pg'#253'O+n'#0

-  +#137'M'#1'ID'#8#133'_'#5'\'#178#207#30#226'L?'#157'F'#3'E'#21';'#163#250'_'

-  +#146'l'#229'%T'#246'Q3'#15#214#194'K)'#227#166#239#230#130#244'#'#206'8z.D#;'

-  +':v'#149'J'#21'<'#251#198'j'#169'//'#137'p8'#12#239#188#254#4#252#247#227'7'

-  +#193#209#222'2'#232#227#200#240#187':'#253#188#235'`'#191'1'#18'H'#166#13#11

-  +#227#203'Y'#162#144'P7'#144#236'%'#224'GS`5x'#221#174#29#252#1'h1'#30'R'#183

-  +'f'#197#135' !'#9'HE'#0'w'#161#240'/'#163#219#10#161#139#175'F'#9#249#133#227

-  +#161#168'|2o'#247'wv'#239'e'#225#190'd'''#159#220'q'#248#213'l^'#15'7]'#221

-  +#179' <'#241#202#183#160#213#234'%'#189#198'`'#208#15''#185#245#15#176'y'

-  +#227#207#189#166#0#15#20#147#167#205#134#229#183'?'#1'j'#181'V'#210#247'''5'

-  +#18#162'a'#165'\'#142#0'?{'#128'H'#128#138#134'<\'#205'@'#179#216#31#208'i'

-  +#10#180'i'#212#138#217'['#191''#190#25'$"'#129#172'K'#18#10#255#28#220#253

-  +#128#4#160#16#199#251#141'f'#206#238#183#176#6#158#188#221'o'#224'j'#250#233

-  +'q*'#238#145#201'd9'#21#239''#255'_'#207#193'+'#207#253#181#199#199#175#189

-  +#237'q'#216'i'#214#238#146']'#223#166#13'?'#193#221'('#252#161'Pf'#202#211

-  +#213#26'-\t'#245#221'0g'#254#18#201#222#163#212'H$'''#18#9#21#132#220'\'#194

-  +'dk1'#26'C'#198'g'#10#182'5n`'#13'F'#187#148#15#191#220#240#211#10#234'w'#193

-  +#15'8'#203'.'#178'*J('#252't'#190#175#197#170#191'`'#247#151#140'G'#187#223

-  +'jN'#246#239#167'Q]'#212#193'W'#173#22'M'#229#205#21#201#231#241#183'{'#151

-  +#193#247'_'#247#156#225'y'#197#13#15#195#172#185#139'$'#185#182#230#198#237

-  +'p'#253#165'G'#166'u'#213#239#9#187'.'#216#31'.'#188#234'nI'#222'g'#174'@('

-  +#30'bN'#193#8#231#15#160'Qd'#212'i'#216#201#166#19#147'?`='#184#218#27'v('#26

-  +#146#203#18#251#213#173'y'#137#166#17'g'#157#4#178'M'#0#231#2'7'#187#143#169

-  +#254#148#234#171'U'#171'Q'#237#223#9#237#254'q,'#212'G'#4'@'#19'z'#141#186'N'

-  +#187'_'#220#202'+'#151'p'#203'U'#199'A}'#237#166#30#31'?'#245#188#27'a'#233

-  +#254'GKrm'#203'/:'#12'W'#157#236#181#168'3'#154#173'p'#205#205#143'Cy'#213'd'

-  +'I'#222'o.@p'#10'F'#226#220'(2'#214'iX(!&'#18#240#248#161'q'#235#15#224#247

-  +'y'#216'0R'#145')'#176#161#208#28#223'e'#205#23'/'#135'`'#164#18#0#10''#1

-  +#238'6'#162#240#231#145',+'#168#194#15#5#220'l-'#130#210#170#217#220#136'nQ'

-  +#129#15#169#254#201#210#222#28#136#245'w'#135#171#207';'#0':\'#237'=>>{'#222

-  +#18#184#248#218#236'W'#131#174#250#246#19#248#251'_'#175#201#250'y'#169#148

-  +#248#162'k'#254#138'Z'#207#226#172#159';W@>'#129'x'#12#152#199#159':'#10#249

-  +#168#183#160#159's'#10#18#17#216#219'['#160#165'v-'#155':'#20#21'B'#131'@?'

-  +#239#196#245'{L'#14#255#229#181#215'^'#139'C'#22'I '#155#4#240'4'#10#255#153

-  +'B'#194#15'e'#242'i'#181#26'('#171#222#21'l'#22'+X-'#26#150#227'o'#210#11#241

-  +'~E'#206'W'#247']y'#206#190#224#245#184'z|'#220'`'#180#192'}O}'#154#245#235

-  +'z'#230'o'#183#192'7'#255#253#183'$'#159#9'iw'''#156#185#12#246'>0'#247#19

-  +#161'2'#1'nE'#231'z'#11'R'#166' '#181#26'gIB'#194'@R'#26'8R'#251#11#184#28'M'

-  +']K'#135'}'#10'Yd'#230'YG'#236#180#253#214'[o'#205#154')'#144#21#209'B'#225

-  +'_'#136#187'/'#196'M=)'#225''''#191'x'#2#20#150'M'#228#236'~'#190#159#31#27

-  +#209#205''''#251#200#20'|'#154'o'#142#142#228#190#242#156#165'L'#157#235#13

-  +#203'n'#22#170''''#207#204#234'u'#253#241#186#147#160#174'f'#163#148#31#13

-  +#236'{'#200#201'p'#236#169'WJz'#13'R!'#217'e'#152'2'#5'Q'#19#160#162'!'#174

-  +#155#16'_='#232'rC'#211#214#239'!'#16#12'tI'#16#138#191'5'#217#214'x'#204#231

-  +#159'.h'#1#25''''#129#140'K'#22#10#191#18'w'#171'Q'#246'g'#138#29''#6#163#9

-  +'W'#255#221'P'#232'u'#220#234'o'#20'B~'#156#221#175#148'Q?'#191#172'\'#226

-  +#160'q'#229'YKX'#152#173'7L'#156':'#27#174#186#229')v'#155#190#232#141#235

-  +#190#135'_~'#252#2'j6'#255#2#173'-u'#16#14#5#161#176#184#28'f'#238#178#24#150

-  +#236','#216#242#139#135'|]w\{<4'#214'm'#145#250#227#129'='#150#28#6#167#157

-  +''#171#212#151'!'#13'(*'#144#224#187#9#133'R'#203#135'iko'#222#14#237'M'#155

-  +' '#28#230'M'#1#222'!'#168#144#197#143#216'}r'#244#223#217'2'#5#178'A'#0#201

-  +'~'#254#130#227#143'V'#255#226#138'Y'#144'_X'#194#170#251#204#204#235#175'f3'

-  +#251'4'#194#164'^'#5#191#242#231#174#252#195#21'g.'#198'/0'#216#231#243'*''L'

-  +#135#246#214#6'^['#232#253';'#173#154'8'#3'.^'#246' '#232#141#230'A_'#215#223

-  +#239#189#18'~^'#253#133#212#31#15#195#137'g]'#7#139#246'=J'#234#203#200':'

-  +#196#249#1#194#172#1#242#7'P4'#192#229#14#129#27'I'#160'q'#219#15#204#132#236

-  +#146#27#176#173#196#26#155'S]'#20#243'e'#131#4'2*^('#252#229#184#251#21#133

-  +#223'(8'#254'h'#245'7'#219#138#161#164'r&'#179#249#201#243'o'#22#169#254#148

-  +#240#195#138'j'#134#193#140#238'+'#206'\'#4#209'H8'#237#199#165#234#187#227

-  +#207#188#22#246'\'#250#251'A'#189#254#173#151#31#130#143#223'}A'#234#143#135

-  +#129'4'#190#229'Z'#1#165#229#19#165#190#148#172'#9'#131#144'R'#133#249#209

-  ,'cn'#26'I'#206#180#128#16'8'#236'm'#208'R'#187#134#213#10#136#27#138#202'e'

-  +#137'?'#21')6'#222#186'j'#213'*j-'#158'QS '#211#4#240','#238'N'#231#194'x\'

-  +#165#159'N'#167#129#210#9#187#162#218'o'#6'3o'#247#155'x'#213'_'#165'Rt'#198

-  +#251's\'#248#9'W'#158#177'p'#135'A'#27#233#2'}'#6'W'#160#233'@'#26#193'@'#225

-  +#180#183#192#173#151#255'N'#234#143''''#9#157#222#8'w<'#244'>(G'#227#240#145

-  +#132#184#167' '#167#5#176#198#162#158#16#155'@'#220'Z'#255'+t8'#26#152'C0'#22

-  +#139#9#164#17#210'(";W'#154'[k>'#255#252's'#129#4'2'#130#140#137#25'_'#236

-  +#179#25''#200#202'd'#147#15'\'#225#243'J&Aa'#201#4'&'#248#22'>'#219#143#188

-  +#254'B'#194#143#208#214'K6'#28'4'#128'3'#22#160#234#150#185#249#15#148'F'#252

-  +#199'G>'#4#165'j'#224#130#243#199'k'#143#131#214#166#237'R~<)'#168#24'?'#13

-  +#174#190#253'9'#169'/#'#171'H'#206#31#20'G'#5#130'\'#150' E'#5#152'&'#224#246

-  +'A'#211#182#149#16#240#7#146#25#130'd6'#160#22#240#216#30#147'#'#151#226#203

-  +'c'#153'4'#5'2I'#0''#195#221#5#194#234'O'#171#187#222'`'#198#213'>'#174#252

-  +#186#164#234'O1'#13#175#250#11#173#188's5'#236#215#21'W"'#1'd:'#211'n'#206

-  +'n'#251#194#233#23#253'q'#192#175'['#245#205#135#240#226#223'o'#145#234#163

-  +#233#22'{'#29'x'#2#28'q'#210#229'R_FV'#145'L'#21'&_'#0#223'T4'#16#226#18#132

-  +'H'#3' "ho'#169#5'{'#211#198#174'a'#193#160'Y'#23#157#174#143'ln'#200#164')'

-  +#144#17'Q'#227#251#250'oCa'#214#8#171#191#150#194'~'#227'v'#130#130#194'q,'

-  +#233#135'R}'#169#198#159#10'}'#146#173#189#134'Y['#175#171#207'^'#136'_lf'#9

-  +'@'#173#209#193#157''#31'\.'#193'u'#231#239#3#225#12#213#1#12#22#231'^y'#31

-  +'L'#155#185#135#212#151#145'u'#136'G'#145'S'#130#16#171#21#224'K'#135'I'#19

-  +' -'#192#231#245'$'#147#131#226#156'/'#224#129#137#214#198#229#133#133#133

-  +#145'Li'#1#153'"'#128'{pw'#149#216#246'7'#24'q'#245#175#222#21'W}m'#146#0'('

-  +#215'_'#163'Q'#138'r'#253'3'#253'5'#164#23#203#206'Y'#196#190#172'L'#131#132

-  +'f'#234#206#3'/*'#250#199'3'#130#149'_H'#147#16#212#19#242#10'J'#225#250#191

-  +#252'S'#234#203#200':'#132#4'!'#161'V'#128#229#6#8#166'@W-'#128#250#8#178'(B'

-  +#194'k'#209#248#166#203'|'#155'Z'#143'='#246#216'h&'#18#132#210'.r('#252#249

-  +#184#219'.'#158#231'G*~~'#233'4(('#174'`'#182#191'9'#153#238#171'J'#233#231

-  +'?'#220#176#252#188#197#25#245#1#8#152#188#211#174'H'#2#15#12#248'u~'#159#27

-  +'n'#187#252#224#164'-'#154'+'#184#244#198#167#161'|'#252'4'#169'/#'#235'`i'

-  +#194#162#178'a'#214'U'#152#143#10'Px'#176'i'#203'w'#224#247#251#152#22' D'#4

-  +#20#178#248'_'#198#233'jn'#213'h4'#145'L8'#4'3A'#0#255#135#187#27#133#184'?'

-  +#173#254'z'#189#1#202'&'#238#193#4#159'<'#255#20#243#167#176#31#149#249#178

-  +'b'#31#190#189#207'p'#226#128#166#186#205'p'#255'm'#167'g'#229'\&K>'#220'xo'

-  +#255'z'#247'u'#197#227#247'\'#10'[6'#172#202#230'G'#211'''*'#171'w'#134#139

-  +#174'L'#234#203'H'#162'v'#235'z'#248#224#141#191#179'U'#248#202#219'^'#4'm'

-  +#6#134#161#236'P6LZ'#0's'#8#134'Xkq'#242#7#216'['#182#131#189'y'#19#203#11#16

-  +'E'#4':'#10#13#222#25#165#150'p'#187#209'h'#140#166#219#20'H'#171#200#161#240

-  +'S'#246#10#173#254#214'd'#193#15#10'y^'#233#20#200'/'#170'd'#4'`'#225'+'#253

-  +#132#213'_.'#204#241'K'#251'G'#158'Y<'#245#192#149#176'i'#221#202#172#156#139

-  +#138'l'#254#244#232''#7#245'Z'#183#171#13#238#188#246#168#156#210#2'hq'#184

-  +#225#158'w'#192'h'#178'Jz'#29'?'#175#250#12#222'z'#233#175#224#245'8'#147#247

-  +'Yl'#133'p'#237#157#175#225'oR'#153#246#243#9'#'#200#147#29#132#216#188#193#8

-  +'x<H'#0'>'#242#5'P'#181#224'w'#16#164#136'@'#172'S'#11'P'#202#227#255'7'#181

-  +#208'~'#151#221'n'#15#167#219'!'#152'n'#2#184#30'w'#20#175#254#172#183''

-  +#245#238#184#234#235'p'#245'Ws'#157'}'#249'b'#159'd'#216'o8-'#253#192#133'tn'

-  +#190'x'#31#136'g('#7#160';\'#247#151#183#152'&0'#24'<Cd'#181'>;d'#213'_'#204

-  +#156#191#15#156'x'#174#180#3'G'#222'|'#225'.'#248#254#203'wv'#184#191'x\5\v'

-  +#243#243#25'9'#167'0'#130'<'#22#19#194#130#145'd'#243#16#210#4#218#155#183

-  +#130#179'uk'#215'r'#225#246#170'<'#255',u'#194#233'L'#183'C0m'#146#135#194'O'

-  +#189#175'jP'#152#11#133#156'J'#249#205'+'#158#4#249#197#227'q'#245'Ws'#131

-  +'<uj~'#168#7#151#244#3#195'p'#144#231#23#31#190#4#31#190#249'hV'#207#249#251

-  +'S'#174#129']'#23#13'.'#185#199#231'q'#193#159#151#253'.'#167#180#0#202'v'

-  +#188#229#161#143'2'#178#210#246#23'_}'#252'*'#188#255#250#195#221'>6'#209

-  +#225'p'#196')'#203'2r^'#241#152'1'#234'#'#232#167#228' >*@Z@'#211#150'o!'#24

-  +#10'B4'#210'Y#'#160'R'#196'o'#24#167#171'}'#200#229'r'#133#210#233#16'L'''#1

-  +'\'#134#187#251#133#22#223'JV'#238#171#195#213''#15'0'#25#185')'#190#228#249

-  +#215#137'W'#255'af'#247#11#248#203'uG'#130#167#195#158#213's'#206#156#191'/'

-  +#28'w'#214#224#227#250#143#254#249'\h'#172#149#182'B'#176'+'#14':'#234'"X'

-  +#184#223#241#146#157''#235#198#213#168#29'u'#159#151' '#147#201#225#218#187

-  +#254#5#6'cf'#204#148#148#185#2'4v<'#16#6#143#151'#'#129#246#166#205#224'j'

-  +#171'I'#169#17#192''#154'&'#216#218#230'DC'#30'O:'#29#130#233'$'#128#159#196

-  +'#'#189')'#231#223'VT'#13#249'%'#213','#219#143'y'#254'u'#157#147'|'#5#219''

-  +#184#161#177#246'7x'#236#174's'#179'~'#222#130#226'J'#184#228#230#193#231#247

-  ,'?'#250#167#179#160#185'A'#250#10'A1'#170#167#205#131#211'/'#249#235#208#15

-  +'4H'#4#253'^'#184#243#154'C{|'#188'j'#210'l8'#235#138#7'3rn'#161'N'#128#204#0

-  +'J'#14#242#5#195#172'd'#152#204#0#183#199#11#205#219'V'#162#22#16'Ji'#29#166

-  +'SFO'#221'i\'#224'-$'#128#208#140#25'3'#210#162#5#164'E'#2#199#205':q'#14#200

-  +#228'?'#166#14#248'PC'#217#196'='#193'h'#212'%'#195'~]mv'#1#195'L'#5'x'#246

-  +#129#203#160'f'#211#154#172#159'W'#161'T'#193'M'#247'<'#232#215'?p'#203#9

-  +#224#180'7e'#253#186'{'#131#209#156#15'W'#255#233#13'I'#175#225#214'K'#150'v'

-  +#186#232#187#193'y'#203#158#128#178#202')i='#167'`'#138'u'#230#5#196'Y#Qj'#28

-  +'Bu'#2#20#26'lk'#218#8#238#246':'#22'-'#16#210#131#21#242#196#191''''#231#181

-  +#156#145#151#151#231'okkK'#139#22#144#30#2#152'}'#242#189#184#187'R'#156#248

-  +'c'#206'+'#133#162#242#25'l'#245#167#141'l'#255#174#5'?'#195'L'#246#25#238

-  +#184'|'#191#140#21#0#245#133#195'O'#186#6#230#238'y'#232#160'^{'#207#245'G0_'

-  +'@.'#129'4'#197#155#30#200'~'#199'$1'#250#250'>+&'#236#12'g]'#249#240#0#142

-  +#216'?'#8#26'@gD'#160#179#135' +'#22'r9'#160#173'v'#245#14'EB'#165'F'#215#156

-  +'Bs'#188#209'h4'#6#211#161#5#12'Y'#4#11'&'#238#167#212#24#139#235'P'#160'K'

-  +#196#206#191#194#242'Y`'#205'+J'#18#0'7'#211'O'#148#243#159#246#143'4'#243

-  +#168#223#182#14#158#185#255'b'#201#206'o+('#131#139'oZ1'#168#215#222'u'#237

-  +#161#16#238#163'y'#137#20#184#225#222#143'@'#174#148#206#17'x'#247#242#195'!'

-  +#24#240#246#248'8'#249#2#150#223#243'>.j'#233#175'd'#20#198#139#209#198':'#9

-  +#135'"\'#15'A$'#0'2'#7#154'k'#190'g'#201'\bg'#160'F'#17#185'~'#130#205#254'x'

-  +#186#180#128'!'#203#225#184#217''''#29#130#135'yW'#172#254#235'tz('#157#184#7

-  +#152#244#26#214#229#135'e'#253#241#171'?'#169#255#220#7#155#246#207'3'#227'x'

-  +'g'#197#159'a'#237#247#255#25#250#129#134#128'E'#7#158#10'{'#29'|'#230#128'_'

-  +#247#231#171#15'D{2'#167'F'#211'3\q'#199#27#160#207#144#163#173'?x'#232#182

-  +#19#192#237#236'}B'#242#194#253'N'#130#165#135#157#147#145#243''''#135#138

-  +#196'83'#128#178#3'='#188')`o'#169#1'g'#203#230'd'#219'0'#190'Jp'#213#228#188

-  +#230#223#163#6#224'N'#135'/`'#168'b(C'#245#255'%'#220#159' '#174#250#179#20

-  +'TAA'#233'd'#182#242#147#240#179#156'a'#180#215'0'#140#251#11'x'#152'~,'#174

-  +#190#199'ig'#18#244#217#157'q'#249'#PZ9'#176'T'#218';'#175#220'/+3'#2#6#138

-  +#11#174''#1'l'#133#227'$;'#255'S'#247#156#7'-'#13#155'{}'#142#193'd'#131#203

-  +'n'#207'L'#253#2#203#11#192#175'%'#154#156'''@y'#1'\'#207#0#143#199#3#205#219

-  +#190#235'j'#6'$'#242#180#158#189#139#140#190'_'#210#161#5#12'I'#18'Kw>'#214

-  +'"W'#168#155#240'G'#169#19'w'#252')'#170#154#7#22#139#141#9#191#208#231'O'

-  +#205#175#254#178'a'#24#247#23'p'#215'5'#251'g'#188#250#175'?'#208'h'#13'p'

-  +#217'mo'#12'Hu'#206#149'k'#239#138#11'oz'#5#204#214'B'#201#206#255#226#195

-  +#151#161'i'#247'K'#159#207';'#227#138'G'#161#164'<'#189#206'@'#1#226'J'#193#0

-  +#211#2#194'L'#11' 3'#160#165'n'#13#248#220#246#20'3@'#173#136'>2)'#223#241

-  +''''#157'N'#231#25#170#22'0'#20'I'#148#141#155'u'#210'Y('#205'O'#138'c'#255

-  +':'#189#25'J&'#236#202'B~F'#3#183#250'k'#133#138'?'#129#0'2'#242'1f'#30'w]'

-  +#189#159#212#151#144'DE'#245',8'#233#194#254#135#208#238'^v@V*'#23#7#2#250

-  +#221','#187#251#163#172#159#215#209'V'#7#171#190#252#23'4'#213'n'#128#182#230

-  +'m'#253'j'#235'V5i'#23'8'#225#252#244'O?'#18'$'#150#141#26#143'u:'#3'}'#228

-  +#12'D"p'#182'7'#128#189#241#215#20'3@&K'#212'M'#201'k'#222#27'??'#215'P'#181

-  +#128#161#17#192#236#147'?'#199#253#18'.'#166#207'M'#248#181#22'q'#153'$'#248

-  +#194'`O'#173#134'k'#244#169#16#226#254#195'P'#3#8#6#189#240#208'MGH}'#25')'

-  +#216#231'w'#23#194#188#197#253'k'#184'y'#239#181#7'f'#165'rq '#160'^'#7#151

-  +#221#241#206#208#15#212#7#162#209'0'#252#188#242#3#216#176#230'S'#166#238'G'

-  +#250#209#200#181'+'#228'r'#5'\q'#231#251#236'w'#158'v'#240#209#0#193#25#200

-  +'B'#130#188'/'#192#231#11'@'#243#214'o'#184#190#129#157'f'#0'X'#181#129'S'

-  +#202#173#190'OQ'#3#240'644'#4#15'?'#252#240#216'`'#180#128'AKb'#217#204#227

-  +#170'er'#21#181#252#146#137#213#255#226#234#221#209#246'7'#166#172#254#164

-  +#254#211#7''''#31#198#234'['#211'Vx'#238#190'?H}'#25') '#15#245'YW?'#133'6t'

-  +'y'#159#207#253#235#242#131'r'#142#0#242#138'*'#217#245'g'#18#159#189#243'wX'

-  +#245#5#229#26#12'='#13'z'#241'Ag'#194#238#251#156#148#145#235'$'#225#23#18

-  +#131#184'"!./'#128#234#4#218#234#215#131#167#163#169'Kjp'#236#141')'#5#142

-  +#171'U*'#149#203#135',1'#216#26#129#193'J'#163#172'|'#246#201'7'#226#153'n'

-  +#23'{'#255#141#230#2'('#172#152#205#173#254'z'#21#232#209#12#160'6'#223'*'

-  +#149#144#246';<'#133#159#176'm'#195'w'#240#230#179'7I}'#25';'#128'<'#232#231

-  +#223#248#143'>'#159'w'#255#13#135'd'#181'x'#169'?'#216#251#240#11'`'#151#133

-  +'Gf'#236#248#219'6'#174#132'7'#159#185'1m'#199#179#228#149#194#217#203'2'#211

-  +#215'P'#152'+'#200':'#8'G'#185#225#162'>'#161'i'#136#179#21#218#235#215#166

-  +'$'#5#201' '#209'19'#191'u'#145'\'#150'h'#195#197#215'+'#170#20#28#144#157'7'

-  +'h'#2'@'#245#159#234'S'#23#139'+'#255#242'J'#167#131'-'#191#12#244'zU2'#244

-  +#199#138'~'#134'q'#222#191#128#159#191''#31'>~#'#251's'#254#250#131'I;/'#130

-  ,#195'O'#238#157#156#30#188#241'P'#201#18#152#186#3#149'8_v'#251#191'I'#183

-  +#206#200#241#253'^'#23'<y'#215')i'#14'}'#202#224#162'['#222#0#181'V'#159#145

-  +'k'#238#172#15#232','#21'&G'#160'/'#16'ff@('#20'L1'#3#242#245#254'S'#138#12

-  +#30#26#0#209#129#154'@p0'#253#2#6'#'#146#178#162#169#135#234'UZ'#171#3#133'_'

-  +'-'#206#253'/'#174'^'#128#170#191'.'#153#246'+'#228#253''''#157'9>'#232#163

-  +'7|'#247#217#10#248#230#163#204#148#136#166#3#135#156'p=L'#153#181'W'#143#143

-  +'?q'#231#137#224#243'8'#164#190#204'$'#170'&'#207#131'#'#207#252'S'#198#142

-  +#255#244#221#167#129#219#217#146#246#227#238#182#247#137#176'`'#255'3'#210'~'

-  +'\'#241' '#145#168#168'y('#133#4'I'#19'ho\'#7#30'gsJm'#128'^'#21'y'#180#210

-  +'b'#191#31'I'#193#129'&'#128'o0'#206#192'A'#17'@'#197#156#147#247#195#235#252

-  +#143'X'#253#215#234#200#251'?'#159#9#190#1'5'#0#193#249#199#133#254#134'o'

-  +#234#175#128#207#223'y'#4#214'~'#155'y'#135#213'`'#161'Ti'#224#194'['#223#234

-  +#241#241#247'^'#186#3'6'#175#251'R'#234#203#228#175'U'#13#231',%c+'#233#251

-  +#175#222#9#155#214#14#174#129'J'#223#215#174#129#243'o~3#'#206'@ae'#167'h@'

-  +#152#31'$B'#137'AD'#0'N{=8'#155'6'#166#180#11'S'#202'c'#223'O'#202'k;'#7#175

-  +#165'='#18#137#184'='#30'Oh'#160#13'C'#6'E'#0'h'#255#255#9#143#190'\'#28#254

-  +'3'#219'*'#209#4#152#204#236''#218'h'#188#183'Z'#24#243'5'#12#171#254#186

-  +#226#235#15#159#130#213'_'#190'.'#245'e'#244#138#5#7#156#9's'#23'w?'#149'w'

-  +#203#250#175#224#253#151#239#144#250#18#25#14';'#229'6'#24'?u'#183#140#28';'

-  +#232'w'#195#147'>'#161#215#2#159#161'b'#202#172#189#225#128'c3'#215'+'#128

-  +#181#12#19#204#0#158#0'|^/'#180#212'|'#151','#17#230#252#0#16#156'\'#208#182

-  +#31#138'Y'#227'`'#157#129#3#149'L'#217#173#183#222'*{'#226#205'M_'#225#237'='

-  +#196#197'?y'#227'v'#6#171#173#152#23'~n'#245#231#188#255#178'a'#217#240#179

-  +'+'#182#172#255#18'>x%s*k:'#160#213#153#224#236#235'^'#237#254'A'#252#209#252

-  +#237#182#195'%o'#10'2e'#246#222#176#255#209#215'd'#236#248#159#188'q/lX'#243

-  +'IF'#223#3'-|''_'#246'$s'#10#166#27'I3 '#206#153#1','''#0#9#128#136#128#8' '

-  +#24#240#165#248#1#10#244#254'K'#11#13#158#143'Q'#11#176'SH'#16#181#128#240'@'

-  +#204#128#1#19#192#148#221'N2'#251'B'#178'v'#154#248#211'i'#255#171#160#164'z'

-  +'O'#208#235'u'#172#232#135#156'Z>'#245'W>'#12#219'}w'#7'ZY'#158#185#235'D'

-  +#169'/'#163'O'#236'{'#212#213'L'#200#186#195#11#247#158#14'^w'#187'd'#215'V4'

-  +'n2'#28'}^f'#29#169'O'#220'q'#20'D#'#161#140#191#23'K'#254'88'#233#210#199

-  +#211'~'#220#174#237#195')'#26'@$'#16' ?@'#211#6#240'8'#27#187#250#1'^'#172'0'

-  +#183#223#135'ZA'#171'N'#167's'#15#180'Jp'#160#162')/'#159'}'#226'!'#9#144#191

-  +'#'#216#244#20#255#215#27#172'PX9'#151#9'?'#249#0#152#250#175'$'#239#191#208

-  +#244'cd'#144#192'cl'#5#205#173'l'#186#174'(.'#159#6'G'#158'so'#183#143#173

-  +#252#228'9X'#253'E'#223'!'#195'L '#191'x'#2#28'{A'#250#203'j'#197#248'u'#213

-  +#7#240#223'w'#30#202#218'{Zt'#240#249#176#243#238#135#167#245#152'|'#247#31

-  +#212#0#184'nA'#194'hq6^'#220#209#4#142#166'_'#187#250#1#214'M'#206'o?'#31#9

-  +#160#9#205#0'g('#20#242#15#196#12#24#136'X2I.'#159's'#242#221'x'#222'+'#197

-  +#14'@s~%'#235#253'G'#194#175'G'#225#167'a'#31'D'#0'r'#133'l'#216#135#255#196

-  +'x'#225#222'S'#193#239'u'#14#253'@'#25#132'Zk'#128'3'#175'}'#181#199#199#223

-  +'x'#252'rhk'#218'<'#128'#'#14#29#214#130'r8'#254'"'#154#20#151#153#144#159

-  +#128#151#30'8'#27'<'#174#244'{'#254'{'#2#245'5<c'#249#171#168#233'j'#210'v'

-  +#204'd'#159#0#22#13'H0'#155#223#31#136#242'EB~h'#221#246'M'#151'|'#0#136'T'

-  +#219'Z'#143'R'#200#162#219#200#25'H9'#1#225'p8'#132'f'#0#17'@'#159#171#213

-  +#128#8#224#216'c'#143#149''#253#155#154#218#203#206#21#226#255#10'T'#243#243

-  +#199#205#6#147#165#128#9'?'#179#255#169#235#15'S'#255#249#158#255'#'#4#159

-  +#191'u'#31'lZ+m'#3#139#254#224#204#235'^'#239#241'GIi'#177'+'#238';'#13#194

-  +'A_V'#174#165'l'#252'L8'#244'Tr>fV'#248#219#26'7'#193#191#158#186'2+'#239'I'

-  +#140#25#187#30#6#11#14'Jo'#134'hgRPg'#159#0#193#20'h'#175#253#129#245#8#16'u'

-  +#12#134'|'#157#247#230'b'#147#255'}'#148#201#22'$'#128#14'2'#3'P'#3#136'B?'

-  +#162#1#3#17'O'#249#132'9G'#230#133#19#250'f'#188#173'H'#150#255#170')'#254

-  +#191#16#237'~'#174#233#7#197#254'5|'#242#15#215#245'w'#228'0'#128#179'm;'#188

-  +#241#152't'#13'A'#250#139#197#135']'#10'S'#230#236#223#235#251'x'#243#241'K3'

-  +'n'#206#204'^x'#28#204#223#251#212#172#188#231#183#159#190#10'I'#224#183#172

-  +#156'K'#12#149'Z'#11#167'-{-'#189#7'Mp9'#1#228#8#140'D'#184'h'#128#16#18't'

-  +#182'lBS'#160'N<D'#20#244#170#240'['#149#22#199#189#177'X'#172'I'#163#209'8'

-  +#6'b'#6#244'W:'#153#250'_'#185#203#201#191#199#243#190'!'#216#255#180#194#235

-  +#141'yPP1'#135'9'#254#184#216#191#146#13#251'd'#171#191#156#31#243'='#130#240

-  +#194'='#199'C$'#156'['#3'7'#187'b'#225#193#23#193#212']'#14#236#245'9['#215

-  +#253#23#190'|'#239#225#140'8'#204#228#168#26#239's'#212'r'#168#156#156#153'P'

-  +'_W'#248#189#14'x'#229'Aj'#146'"M'#132'c'#209#161'H'#184#179#211'[)'#26#231

-  +#11#132#136#0#132'>'#1'D'#2'd'#226#216#27#215#165'8'#2'U'#242#216'&4'#3'.'

-  +#198#219#13']'#204#128'>'#163#1#253'&'#0'R'#255#191#217#164#186')'#145#144

-  ,#221'"'#216#255'4'#247#207#156'?'#30#172'E'#19#152#240#147#243#143'&'#1#177

-  +#220''#249#200#178#255#5'|'#251#225'c'#240#235#170#247#164#190#140'^ '#195

-  +#21#233#31'h'#154#245#175#133#213#170#207'_'#128'u+'#223#198#31'T'#223'%'#177

-  +'}'#158#25'5'#194#9#211#23#194'B'#212'@2'#209'B'#171'''|'#242#250#157'P'#251

-  +#219#183'Y;_W'#152'l%p'#204#5#233#27'u&D'#2#132#226' V'#27#16#138'23 '#16#8

-  +'B'#203#182#175#240#251#138''''#29#129'2Y'#194'7'#173#160#245'd4'#11#234'('

-  +#26'`6'#155';'#182'l'#217#18'Z'#183'n'#29#153#1#189#170'y'#253'&'#128#165'K'

-  +#151'*6'#187#198#189#128#231';A'#236#0#180#149'L'#7#147#181#132#9'?'#167#254

-  +'wv'#254'aC?F'#152#6#16#143'Ga'#5'j'#1#180#207'E'#232#12'68'#238#146'g'#7#250

-  +#174#224#251'O'#158#133#223#214'|0@'#141'@'#198#178#250'4:3'#20#148'N'#130#5

-  +#7'_'#12'jm'#250#231#234#245'z'#229#236#251'8N'#210'JG'#146#135#211#174#253

-  +'WZ'#143#201'R'#131'y?'#0#155#29#128#4'@'#27#17'A'#235#182#239' '#28#246#167

-  +#248#1#198'['#237#231'j'#20#225#245'J'#165#178#9'W'#167'N'#167#11#244#167'6'

-  +#160#191#210')'#159'7o'#158#162'9:'#141'hv'#174#152#0#10'*'#230#130#201'le'

-  +#194#175#213't'#134#255#134's'#235#175#190#240#213'{'#15#194#230#28'u'#6#142

-  +#199#21'x'#175#223#15'>'#209#134#242#29#234'6}'#7'M'#219#215#178'N4'#241'X'

-  +#132'U'#17#146#160#145#160'['#242#203#153#176#23'W'#236#4#230'<'#233'Zy'#9

-  +#248#254#147#167'a'#253#247#131#27#156#154'N,='#234'Z'#168#154#178'g'#218#142

-  +#215'u'#132#24'5'#10#9#242'Z@{'#253'Oh'#246#216'S'#8#160#216#232#249#147'M'

-  +#235#251#24#239'k'#196#191#237#161'P'#200#215#159#1'"'#253#145'Pf'#255#31'|'

-  +#240#193#170#181#141'ymx'#219'$'#142#0#20'MX'#8#6#157#134'['#253#133#226#31

-  +#249#200'I'#0#234#14'$'#12#175'=tF'#214'<'#233#253#6'~'#224'G'#254#225'Q0Z'

-  +#138#165#190#146#172#225#229#191#158#136'Z'#203#192#27'|'#164#27#21#147'wC'

-  +#18#184'.m'#199'K'#13#7'v'#18#0#141#21'w4'#253#6'^WC'#151#6'!'#254#23#139#244

-  +#174#151#209#254#175#139'F'#163'mj'#181#218#211#159#18#225'~'#17#0#217#255

-  +#171'6'#199'+Cq'#237#214#164#3#16#237'='#181'F'#143#4#176#27#203#250'c'#171

-  +#191'Z'#193#186#2')'#21'#'''#249#167''''#216'['#182#192#7'/,'#203'h'#206#249

-  +'@Q9e'#15'X'#252#251#204#228#168#231'"6'#253#248#1#172#252'8'#253#217'x'#131

-  +#129'Vo'#129#163'/z&'#173#199#20#210#130#201#214''''#2#16#162#1#29#246':'#232

-  +'h'#221#156#18#9'0'#168'C'#159'UZ\'#15#163'|'#214#14'$'#28#216#31#17#149#163

-  +#253'/'#175'q'#151#238#23#137#201#223'O'#137#0#24#242'!'#191'b'#22#155#3'@'

-  +#177'F'#0#138#206#8#192'H'#199#154#255#189#0#235'W'#190')'#245'e0'#144#231

-  +#253#152'K^'#204#170#243'Mj'#188#249#232#217#16#240#229'Fb'#150#12''#240''''

-  +'^'#153#222'b1a'#148'x'#140#207#7' '#2#8#134'#'#224#235#176#131#189#241#231

-  +#148'H'#128'F'#25#253#181#218'f'#191#3#229's;'#254#221#20#12#6#157'J'#165#178

-  +#207#226#160'~'#17#192#140#25'3'#148'n'#213#172#11#226#9#249#253'b'#2'0'#217

-  +#202#193'V<'#25'U'#255'N'#2'H'#142#252#30#225#26#128#128#207'^'#191#29#154'j'

-  +#178'?*L'#12#242#190#239'}'#204'MPR5['#234#143'#kh'#220#182#26'>'#255'gnT7'

-  +#10'8'#225#242'W@'#158'&'#2'f'#170'='#223'#@'#240#3#176'~'#129#148#16#228#247

-  +'B'#235#246#149')'#4#160#148#199#237#147#242'Z'#175'E'#185#164#9#221#13#129

-  +'@'#192#238#247#251'}&'#147#169#215#226#160#190'D'#148#169#255'^'#175'W'#249

-  +'K'#147#237#190'xBv'#129#152#0#172#197'S'#192#130'$'#160#161#8#128'J!'#234

-  +#254#3#204#30#29#13#4'@'#248#238#195'G`'#235'/'#210'8'#5#233#251'Xr'#228#245

-  +'P6a'#174#212#31'CV'#241#239#167'/'#6#143'3'#183'f'#29#238'{'#252#237'l'#28

-  +'^'#186#144#236#15' '#26'%N&@'#24'5'#129#230'-_B4'#18'I'#18#0'">5'#191#229'r'

-  +#20#203#173#248'w]8'#28#166'Va'#30'Q'#143#128'n'#253#0#253'"'#128#182#182'6'

-  +#213'fW'#217';'#137#132'l?q'#17'P'#222#184#153'`4'#23#178#213#159#171#254#235

-  +','#0#26'%'#178#159#196'/'#223#252#3#183'W'#135'~'#160#1#128':'#213'.8'#236

-  +'*('#159#180#187#212'o?'#171'p'#182'l'#133#15'Wd'#174#164'x'#176#152#179#228

-  +'t'#152'6'#255'wi;'#30'_'#23#196'|'#0#177'(?D'#20'M'#0'r'#6#182'm'#255#1#194

-  +'A/'#171#9#16#136#162#210#226#252#139'A'#29#254#142#252#0#161'P'#168#197'`0t'

-  +#212#214#214#6'{'#203#7#232#147#0'('#254#143'j'#132#250#199'Z'#243'z<O'#149

-  +'8'#9#168'h'#252#238#160#211#25#184#8#0#159#255'O'#17#0'N'#1#24'm'#20#0#176

-  +'m'#221#167#240#253'G'#203'J'#205#189#181'p<'#170#253#183#128'Zk'#150#250'm'

-  +'g'#29#31#189#180#12#28'-'#185'5'#234#156'0q'#214#1'0'#223#244#213#5#8#161

-  +'@'#202#7#136#196#249'p`'#8#9' '#24#3'{'#227'/'#16#240#180#165#16'@'#161#222

-  +#243'b'#129#193#255'~<'#30#175#161'|'#0'4'#1'\('#187#254#222#242#1#250#146'R'

-  +#22#255'Oh'#138#205'->'#27#205#196#146'wv'#1'RB'#209#132'E'#160#165#236'?u'

-  +#151#177#223#195#184#253#247'PA'#171#211#234'O'#30#3'G3'#253'@'#211'O'#4'*'

-  +#181#30'v^t'#18'L'#154's'#176#212'oU'#18#248#221'm'#240#238#147#231'K}'#25

-  +#221'b'#210#156#131'`'#151'}'#206'M'#235'1'#217'b'#194#143#14#139#242'&'#0

-  +#249#1'\'#246'm'#224'm'#175'I!'#0#139'6'#240'q'#153#201'M'#163#250#182#225

-  +#223#141#184'''?'#128#191#188#188'<<h'#2' '#7'`'#212'0c'#170'7'#164'Z'#203'^'

-  +#192#19#128'ZoB{g>g'#255#167'$'#0#201'G'#141#237#223#27#130'~'#23#172#254#244

-  ,'Ih'#216#250'=2'#248#208#179#212#244'hj'#237#188#231#9'0~'#250'^C>'#214'p'

-  +#198''#223#248'?h'#169#253'I'#234#203#232#22'S'#230#30#206#204#128'tB'#136#4

-  +'P80'#18#163'\'#0#218'"'#172'1'#136#171'ecJ2'#16#170#255'?V'#152#29#143#201

-  +#229#242'mx='#154#233#237'}u'#9#234'MTY'#251#175#239#190#251'N'#245'['#171

-  +'q~0'#170#254'R'#198#229#246'2'#2#208#26#242#160#160'|6'#18#0'7'#248'S'#171

-  +#230#186#255#202'y'#239#255#232#243#2't'#15#250#130'~'#254#250'%'#168#221#240

-  +'?'#22#178#234'o'#5#30'y'#246#13#150'"('#169#154#3#147#231#28#2'f'#155#244'Y'

-  +'wR#'#26#14#194#27#143#158'"y['#179#158'0}'#215#163'`'#214#194#147#211'v<!'

-  +#10'@{65'#136#252#0'|.'#128#183#163#13#28#141'?'#167#16#128'N'#25#222'Peu<'

-  +#138'/'#221#138'[m'#28#129#189#18#128#224#0#172#243#20#239#19#140'*'#223#21

-  +#212'z'#5#254'8'#181#166#2#200#31#183'3'#174#254'|'#251'o'#214#0#20#197'^'#14

-  +#163#214#7#208#31#184#218'j'#160#254#183'o'#160#195'^'#11#177'H'#8'b'#177'0+'

-  +#196'Qi'#12'`'#180#150#128'%'#191#10'l'#197#213#144'_'#154#153'A'#148#195#25

-  +'k'#191'x'#1'6'#254#240#214#208#15#148'!'#204#216#243'x'#216'i'#143'c'#211'z'

-  +#204#206#148'`.'#31' '#20#226#250#2#248'='#14#176'7'#172'I!'#0#173'2'#178'm'

-  +#188#213#254#8#222#166'H@-n-'#145'H'#164'#'#136#24'4'#1#212#215#215#171#155

-  +'C'#149#191#11#199#148#175#164#16#128#185#24''#164#211'A'#175'U'#163#22#192

-  +#141#5#147#141#144#6#160'c'#200'M'#188#251#228'y'#16#240#218#165#190#140#30

-  +#177#243#162'S`'#218#252#244'O:'#18'R'#130')'#245#151'B'#129#212#23#192#239

-  +#237#128#246#186'U)'#4#160'QD'#27''''#216#218#169#239#218'Vr'#4#162#188'6'

-  +#163'9'#224#178#217'l'#129#158#250#4#246'J'#0'B'#4#224#151#6#195#201#225#152

-  +#226'q'#193#4' '#2#208#163'Jj+'#158#194#170#0#169#11#144'P'#1#152#201#16' '

-  +#133'='#26#182#172'D'#27'p-L'#156'y '#20#150'O'#207#208#153#198#144'k'#8#162

-  +#249#244#246#227#233'u'#176#165#27#187',='#11'&'#239'rHZ'#143#201#133#2#249#6

-  +'!T'#19'@'#131'C'#169','#216#235#131#182#218'oS'#8'@'#165#136#181'O'#180#181

-  +'QSDJ'#217#175#193'}#e'#4#246#22#9#232'MVY'#4#0'_'#172#217#238'.'#249#3#18

-  +#192'=b'#31#128'1'#175#10#172'E'#19'Y'#27'p'#174#7#160#156#133#6#217'A3'#192

-  +#0#31'<{)s|$/'#28'O2k'#241#169'0e^'#250#226#174'c'#200']'#172#254#228'q'#216

-  +#178#246'?R_F'#175'8'#240#180#251#192#156'_'#145#246#227'2'#1#231'K'#131#169

-  +#24#136'5'#7#241#7#161#181#230#171#20#2'P'#202#227#238#201#249'm'#15#2#231#3

-  +'HF'#2'Ps'#240#245#148#18#220''''#1'h'#17#245#254#138#171#163'q'#197#205#236

-  +#5'|'#20#192'T0'#17#172#5'U'#160#215#171'Y'#18#144'0'#0'4SC@'#222'|'#228#148

-  +'n;'#241#236'y'#232'UP1eAF'#206'9'#134#220#193#219#143#159#141'Z'#128'K'#234

-  +#203#232#17'r'#133#10#142#185#244#149#140#28';Y'#21#136'{6+ '#16'fm'#194'['

-  +#182#254'/'#133#0#20#178'x`J'#1'G'#0#248#247'6'#148#213#250#190#230#5#244'$'

-  +#173','#2#176'n'#221':'#165#211#233#212'm'#178#23#220#138#4'p9{'#128''''#0's'

-  +#209'dd'#187'r'#212#0#144#0'P'#11'P%K'#128'3'#147#2#252#250#3#199'w;'#221#150

-  +#28'g'#135#156#245'HF>'#248'1'#228#14#254#249#192#9'h'#3#167's'#208'gzA+'#255

-  +'A'#167#167''#230'A'#178',8'#193'E'#148#136#0'hX('#245#5'h'#221#250#5#27#248

-  +'*<G&KD'#167#230#183'<'#128#183'k'#20#10#5'K'#9#22'J'#131'{j'#17#214'#'#1#8

-  +'5'#0#200#28#250':_'#197#221#209#184#252'l'#246'@'#146#0#166#130#165'`'#28#24

-  +'H'#3#208#168'X'#31#0'Y2'#4#152'~'#188'v'#255'1='#134''#14':'#237#254#140

-  +#168'^c'#200#29#252#3#191#255'\*'#189#238#138')s'#15#131'9{'#157#153#145'c'

-  +#11'~'#0#214#29'('#18'e'#3'C'#3#193'0'#180'm'#251#154'uy'#22#8#128'0'#173#160

-  +#249#1#178#255'qc'#161'@'#220'Z'#187#204#13'L'#137#4#244'J'#0#20#2'D'#245'A'

-  +#191#161'5'#239'Q$'#0'6tN '#0'K'#201'N'#172#21#152#201#200'5'#3#161#230' '#10

-  +#25#215#7' '#19#12#240#218'_'#143#134#158'2'#235#138'*g'#194'^'#199#220#182

-  +#195#253'[~'#250#0#218#27'~'#5#183#163#30#2#30';'#132'C\'#3#15#210#26#202#170

-  +'w'#133#201's'#15#5#157'1?#_'#218#24#210#7#250#145#191#249#224#9'R_F'#175'8'

-  +#240#244#7'2'#183#8#145#6' '#228#2'Db'#224#245#134'9'#2#168#249#14#162#145'@'

-  +#10#1'L'#206'k}\'#169'H'#208#224#7'r'#4'nW*'#149#205'*'#149#170#163#161#161

-  +'!8`'#2#160#16' '#18#128'a'#171#171#244#249'XB'#206'rO'#5#2#176#150#206'DA*'

-  +#4#147'A'#195'G'#1'd,y%SQ@'#166#2#246#208#184#146#206'{'#228#197#207#131'R'

-  +#205'M'#155#165't'#209'O^'#185#158#9'}_Pi'#244'P2~.'#236'~'#200#229#25#153

-  +#248':'#134#161#195#217#188#25'>Z'#145#187#141'N'#20'*'#13#28'}'#233#203#25

-  +';'#190#160#248'PYp'#8'5'#0#31#18#128'?D3'#2'VA$'#232'N!'#128#234'<'#251#11

-  +#26'Et='#222#220#138#191#231#237'h'#2#244#26#10#236#145#0'('#4'XXX'#168'B3'

-  +#192#248'K'#147#237#159#241#132'l1{@ '#128'q'#179#193'h'#202#7#147#137''''#0

-  +#26#3#206#183#1#207#4#9#188#243#216#185#224#239'E'#160#199#207#216#27'v?'#248

-  +#18#230')^'#245#241#19#3'N'#191'U'#170#180#176#199'!'#151#193#184#201#163#171

-  +#178'n8'#128'l'#223#215#239';6g3'#0#231#238's6'#211'&3'#5'!'#29#152'"'#1#212

-  +#25#200#235#11#177'\'#0'J'#4#10#251#157')'#4'0'#222#234#248#135'N'#21'YK&'#0

-  +#229#2#224#234'O'#205'A\'#212'$t'#247#221'w'#143#12#136#0'('#7' '#20#10#25''

-  +'m-x'#27#9#128'IF'''#1#204#5#163#217#10'f'#163#22#180'H'#0#212#9'H'#166#200

-  ,'\'#15#128#143'W,'#7'{'#227#198#30#31'''/l'#193#184'i'#208'Z'#251#243#144#206

-  +'SX1'#3#150#28'u#j'#19#218#204#188#145'1'#12#10#239'>q'#1'x]'#205'R_'#198#14

-  +#160#26#141#195#255#144#217#182'd'#9'^d'#217#192#208'p'#20'<^'#158#0#234#215

-  +'B8`O!'#128'J'#139#227#13#189'*L'#197#18#201'\'#0#212#226#157'H'#6#221#230#2

-  +#244'J'#0'j'#181'Z'#19#139#197#140#155#28'E'#175#196#226#242#165#236#1#222

-  +#203'o+'#157#131'&@>'#243#1'P$@'#145't'#2'f'#134#1'V~'#240' l'#203'R'#211#13

-  +#5#146#201#220#253#206#131#234#153#251#15#253'`cH'#11#214'|'#254'LN'#166#1#31

-  +'p'#234'=`+'#158#148#209's'#8#233#192'q'#158#0#220'D'#0#254'0'#18#192#26'4'#1

-  +'\)'#4'0'#193#230'x]'#171#140#172#3'>'#27#144#186#4#247'6-'#168'O'#2#8#135

-  +#195#166'm'#29#165'OF'#227'r'#166#227#8#4'`)'#153#9'f['#17#24'y'#2#160'f '

-  +#178#12'N'#2'j'#174'Y'#3#255'}'#237#214#140'~'#208']a+'#158#8'K'#143#187'-'

-  +#235#189#238#199#176'#'#130#254#14'x'#251'og'#230#212't'#230#146#9#187#192'^'

-  +#199#220#146#241#243#8'EAqf'#2'D'#192#235#14#129#151#8#160'n'#21'DB'#30#238

-  +'9<'#1'L'#180#181#189#162'R'#196'6Q='#128#208#30#12'I'#192#209'S2PO'#210#154

-  +#204#2'D'#152'6'#180#218#238#143#198#21#201'('#0#17#128#185#136#250#194#151

-  +#128#201#164#3#131'^'#197#17'@'#134#235#0'^'#187#247'('#180#131#178#251#3#144

-  +#203#149'0{'#233#25'0y'#222'aY='#239#24'v'#196#166#213#239#194#143#159'<!'

-  +#245'e0'#208#140#132'C'#207#251'{'#210#241#156#13#196#248'T`'#143''''#8'>$'#0

-  +'J'#5#142#133#131')'#190#145')'#249'-+'#208#26#175#1'>'#25#8#247#13'('#252

-  +#164#1#248#186#235#11#208''''#1#168'T*'#243'f{'#254#31#195'1'#197#25#236#5'<'

-  +#1#152#10#166#176'D '#179'Y'#139#4#160'a'#4#192'f'#1'f'#144#4#200#14#244'8'

-  +#26#135'~'#160'A'#192'RP'#9'{'#31''#27'h'#141'y'#146#156''#12#28'>}'#233#6

-  +'h'#173#251'E'#210'k'#208#155#242#225#16#18#254',u`f&@'#28'X'#243#15'J'#0'r'

-  +#187#3#204#7#208'F'#169#192#209#136#152#0#18#211#11'[^'#196#191#235'I'#3'P('

-  +#20#219'"'#145'H'#3#222#223'>h'#2'@5'#194'R'#227'*'#188'>'#20'S^'#200'^'#192

-  +#11#184')"'#152#11#171#192'l'#210#128#209#160#3#165#138'k'#6#154#201'>'#0

-  +#171'>~'#156#173#2'R'#129'Z?'#239#188#240'$'#152#177'gzK>'#135#27#220#246'zh'

-  +#216#252#29#11#183#6'|.'#8#5#220#172'V_'#165#214#129'Zg'#2#173#222#12'Fk)TN['

-  +#12'zsAZ'#207'MCY'#222'J'#186#134#160#244#190#14'9'#231'a'#166#25'f'#11#156

-  +#15#144'/'#7#14'F'#160#163'#'#192#178#1#219#182#254#143'/'#20#226#228'Y.KDP'

-  +#3'x'#149'R'#128#129#171#5'`'#233#192#184#136#183#187'\.ow'#147#130'z%'#0#170

-  +#3#192#23'['#234#189#197#151#6'"'#202'd'#23'F"'#1#131#181#10#172'%'#19#193'd'

-  +#208#130#201#172'I'#154#0#178#12#134#210'i '#194#7'OH?'#158#219'd+'#133#165

-  +''''#252#31#232'-'#133'R_J'#198'A'#130']'#187#225'Kh'#218#242#3'ks'#22#244

-  +#216#7'4'#135#143#134#148#26#172#197'P1u'#1#236#180#240#184#180#9#206#215'o'

-  +#221#13'u'#191'~'#153#213#207#194'Z4'#30#14'<'#235'>'#188#149#253'|'#17#166#1

-  +#160#249#27'@'#193'w'#185#209#4#240#5#161'}'#235#23#220'c<'#1'('#229#241#224

-  +#164#188#214''#226'M'#154#18'L'#234#255#214#190':'#3#245'J'#0#227#198#141

-  +#211#6#131'Ak'#141#211'r'#182'/'#172'Nz;H'#208#245#214'r'#176'P='#0#154#0'&'

-  +#147'6'#217#15' '#211'x'#253#158'c'#210'2'#201'v'#168#160#228#163'Is'#15#129

-  +#185#251#159''''#245#165#164#21'm'#245#235#161'n'#195'W'#208'^'#183#142#173

-  +#178#209'4'#142'B'#167#207#172#160'b'''#152#179#247#153#144'W:y'#200#199#219

-  +#188#250'='#248#241#227'''3>'#168#149#186'/O'#158'8'#204#217#231#172#140#158

-  +#167'W$'#184#17'a'#1#180#253']'#164#1'x}`'#223#254#13#255#16''''#207'*E'#204

-  +';)'#175#157#6'%'#146#218#207'4'#0#154#24'L#'#195#245'z'#189#167#191#4#192#10

-  +#129#208'VPVVVjc'#177#152'e'#187#211'|'#162'7'#172#185'+'#249#4'j'#9'f*'#5'['

-  +#201'4\'#253#181','#23'@'#165'Qr'#149#128#221#229'j'#164#145#23#254#243#236

-  +#149',3,W@'#17#130'=w5'#148'T'#15#207#190#252'q$'#211#205'?~'#0'5'#235'>'

-  +#135#142#182#154'n'#11#174'2'#129#252#178#169#176#232#152#27#217'H'#173'!]?'

-  +#10#255#143#31'?'#1'['#250#152#13'2M+'#240'w^>y'#15#216#253#176'+'#178#155

-  +#23#210#141#12'%d'#192'f'#3#4#252'!'#232'p'#5#192#235'q'#131#163'ne'#138#3'P'

-  +#163#140'vT'#219#236'4'#187#190#129#132#159'6'#165'RY'#135'2'#220'6h'#2#136

-  +'D"'#214#6#183#249'w'#238#144#238'!'#241#147#180#166'b'#176#149#206'`y'#0'&'

-  +#139#142'M'#7'b'#29#129'2'#252#217#172#251#250#31#240#243'_'#200#222#151#209

-  +'O'#20#148'O'#135'%'#199#222'<,B'#134'^\'#217'7'#174#252#23#218#241#223#131

-  +#223#211'.Y'#145#13#249'T'#166#237'~$'#139#178#12#29'qX'#243#201'3Hd'#255'e'

-  +#205'C'#134#2#133#146#146#202#166#163#224'_'#206#18'}r'#1'T'#13#24#141#198'P'

-  +#245#15#129#219#229#7#175#219#1#206#134#31'S'#8'@'#167#140#216#171#172#246

-  +#143#128#215#0#168'"'#16#247#140#0#240#182#183#187#138#192'~'#17'@'#179#199

-  +#184#175'3hxZ'#252'$'#141#161#0#242#198#237#12'F'#163#14#205#0#29'k'#14#154

-  +#169'^'#0'b'#208'D'#222'7'#239#203#205#194#16#25#170#138';-8'#22'v^'#156#190

-  +#198#144#233'BG{'#29#252#244#233#211#208#142'*~$'#228#151#250'rR`-'#154#0#251

-  +#159'y'#218'j1(g`'#195'7'#175'C'#253#198#175#193#215'A'#3#173#251'&8r`'#22

-  +'V'#238#12'Sw;'#2#138#170'fI'#253#145#236#0'z'#7#17'j'#7#230#9#162#9#224#199

-  ,#247#213#14#206#198#212#238#200'zU'#184#165#210#226#248#12':}'#0#228#4#172#29

-  +#18#1#144#9#208#234#209#237#209#230'7'#254'#'#229#3#211'Z!'#191'b'#14#24'P'

-  +#253#183#152#245#172''''#0'u'#4#202'F3'#208''#221'w'#18#251#146's'#21'Z'#163

-  +#13#22#163'z'#155'?n'#154#212#151#2#174#214#26#248#238#223#247#129#179')w'

-  +#204#166#238'@'#159#217'!'#231#254#13#212#250#244#14':!'#19#193#217#188#21':'

-  +'Z'#183#129#27'I'#208#227'jfu"F['#25'X'#11'+Y'#178#151#165#184':'#183#11#193

-  +'X'#166#31#215#11#192#235#13#176'('#128#199#209#12#238#150'u)O3'#170'C'#141

-  +#21#22#215''#249'N@d'#2'l'#29#148#9'@'#155#216#9#232#244#171'f6z'#172')'#241

-  +'7'#133'J'#7#5'U'#187#131#193#160#1'3'#154#0':'#150#11' '#239#182#31'X'#186

-  +')'#225#203''#222#153'u'#15#240'`0e'#215#195'a'#222#129#210#12#177#8#251#221

-  +#240#217#203'7'#131#163'i'#147#212#31'C'#191'A'#5'Y'#135#156#255'(k'#135'>'

-  +#154#209#157#190'B'#9'pa>'#9#168#3'M'#0'w'#251'v'#240#218'S'#167'#Y'#180#193

-  +#154'2S'#7'y'#6#27#248'$ F'#0#209'ht@N@BJ'#24'P'#169'R'#149'nh-'#250#2'_'#165

-  +'I>'#3#5#189'p'#194'"$'#0#29'X'#172'z'#208#27'5'#172'5x'#159'y'#0'i`'#131#230

-  +'mk'#224#211#23#174#207#202#151'1T'#152#11'*'#224#128#179#238#205#170'o`'#235

-  +#154#255#192#202'w'#31#206#154'C/'#157#160'<'#130#163#174'x!mSv'#135#5#250

-  +#180'P'#184#28#128'`0'#2#30'w'#0#220#168#1#184'Z6@'#160'#5)'#174#208#224']W'

-  +#160#247#253'D'#137'@d'#2#240#142#192#186'A'#135#1#137#0#240#133#164#147#21

-  +'ov'#20#191#30'K'#200'''$'#159#129#4#144'W1'#15'L'#22#27'j'#0#6'V'#19#160#226

-  +'C'#129']'#223'O&'#140#130'W'#238'8l@'#241'h)A'#177#240#189'N'#184'%+'#145

-  +#130'O'#158'_'#14'-5'#185'99'#167#191#176#20#141#135'C'#207'T'#234#203#144#4

-  +'='#5#209'('#2#16#12#132'Q'#248#253#168#5#4#192'Q'#207#149#2#139'Qnv'#174'D3'

-  +'`'#19'%'#255#8#26#128#144#8#132#154#128'o'#192#4' '#164#2#227#193#138#183'8'

-  +#10#30#12#199#20#139#196#151'E]'#129','#249'e`'#178#234#192'd'#212#129'ZC~'

-  +#128#236#204#5'x'#239#209#11#192#217#178'-+'#231'J'#23#166#237'q$'#204';(}'

-  +#195'#S'#128'6'#226'{'#191'h'#216'}&=a'#194#236'}a'#193#145#185'7'#1'X'#10

-  +'P'#18#16'E'#0'('#4#200#17'@'#16#218#183'}'#3#177'h0'#229'y'#19#243#218'>U+'

-  +#226#164#254#215#163#224'3'' j'#2#245#129'@'#192#142'f'#128'w'#208#181#0#248

-  +'wQ'#141#211'v'#173'?'#162'Jq'#191#27#242#171#217#20#27#179'Y'#159#12#5#210

-  +'x'#176#158'k'#12#211#247#161#172#253#244'yX'#251#249#138#236''#27'C'#196'n'

-  +#135']'#12'Sv;<'#189#7'E'#251#240#237#135#207'cN'#174#145#3#25#28'~'#201'c`)'

-  +#172#146#250'B'#178#131'^'#204#0'j'#6#18#165'n'#192'>'#206#254#247'z'#252#208

-  +#182#245#139#148#16#160'\'#150#136'N'#201'o'#249#8#5#159#194#30'uB'#30#0'nD'

-  +#8#246#129#214#2'$'#203#129#241#128'fd'#143#194'F'#143#229#20'wH'#151'B'#201

-  +'ZS'#9#228#143#155#1'&'#179#14#204'V=h'#132#178#224',('#1'>W'#11#188'q'#239

-  +#169#153'?Q'#154'AYeG]'#189#2't'#166#244#21#21#189#247#247'K'#192#222#176'q'

-  +#232#7#202'1'#152#242#199#193#17#151'?#'#245'eH'#10'a&'#0#245#1#240'y'#3','#7

-  +#192#237'p'#160#9#240'Cj'#18#144'"'#234#169#206#179''#129#247#181#161#204

-  +#210'J'#192'f'#3#12#182#28'8'#165#31#0#218#15#133#142#128'ai'#139#215#248#160

-  +#248'Ij'#141#5#10#198#207#3'#O'#0','#18#160'R'#176#142'A'#137','#12#7'}'#233

-  +#214'Cr"-x'#160'`?'#236'+'#158'K'#203#177'H'#19#250#9#183#145#138'='#143#184

-  +#10'&'#205#31#157#163#208'9$ '#30#163#8#0#239#0't'#250#193#213#214#0#238#214

-  +#245')'#4#128#182'K'#133#197#245#3#222'l%'#199#31#240#229#192#168#193#147

-  +#167'pp'#13'A'#168'%'#152#223#239'7!'#131#20'D'#19#170#201'['#157#133'o'#136

-  +'_'#163'P*'#161#176'z1K'#6#162'H'#128#206#168'ERP'#246'/'#23' '#13#252#240

-  +#206#131#231#177#24#239'p'#196#188#131#207#131#25#139#143#27#210'1'#236#13

-  +#191#193'{'#187'(g{'#229#165#3'r'#133#18#142#185#246#21#208#26#173'R_J'#250

-  +#209#143#175#141'u'#2'B'#251#159#28#128#140#0'P'#3'p6o'#1#175'c[J'#6'g'#158

-  +#206#191#173#200#224#166#196#128'VJ'#254#17'z'#2#146#6'0'#232#150'`BO@T'#31

-  +#10#240'GV'#190#201'Q'#242'J<!'#179'%'#159'$'#151'AA'#213#2#180#255'M'#168#1

-  +#24#152'&'#160'V+'#185#17'a'#252#140#192'LF'#4#214'|'#252#28#219#134'#J'''

-  +#205#133#3#207#185'gH'#199'x'#227#238'S'#193'mo'#144#250#173'd'#28#230#194#10

-  +'8'#234#170#225#249'='#247#7'='#201'H'#130#31#6#18'#'#2#240'q'#14'@'#234#5'`'

-  +#175#255#5#130#238#212#254#136#165'&'#247'z'#171'6'#176#25#229#148#8'`;'#240

-  +'-'#193'P{oB-'#222'IMA'#7'D'#0#212#22#28#247#234#182#182'6'#3#238#11'P'#149

-  +#24#183#201#158#255'P$'#166'HN'#228#164#149#222'6n'#14#152#243#138':'#9#128

-  +#250#3#138':'#4'g'#18#164#254#191'x'#227#193'9'#213'&'#170#191'0'#229#151#193

-  +#209#203#6#239#196'l'#173'Y'#7#239'=*}it'#182'0c'#241#177#176#235'a'#23'J}'

-  +#25'YE'#130#239#4#28'A'#251#159#28#128#180#250#251#144#0#218'j~'#128'p05'#19

-  +#182#202'j'#255'^'#167#140#144#186'O'#204#192#8#128#136#0'I'#160'iPm'#193#133

-  +#193' '#10#133#194#128#7#200#195#139#25'W'#227#202#187')'#16'Q'#237#147'|'#18

-  +'M'#8'*'#156#2#150#162'*'#230#3'0RJ'#176'^'#205#252#0'T'#232#145#141#128#224

-  +';'#15#254#1#218#235#135#159#3#140#230#17#156#242''#239#13#250#245'o'#222'{'

-  +'&'#184'FH'#200#175''#144#193'.'#7#156#9's'#246';M'#234#11#201#26#136#0'b'

-  ,#188#253#239#247'p'#234#191#207#19#132#214#173#255#195#197'/5'#201'kj~'#203

-  +#231'2Y'#194#137'2'#217#132#175'c'#4#128#219'v'#148#223'f'#220':jkk'#131'('

-  +#211#3'#'#0#26#13#134#194#175#15#4#2'y'#184'/'#173'w['#206#243#132#180#157

-  +#174'w'#234#11'`.'#131#188'q'#211#209#12#208#179#141#252#0'*'#181#146'o'#29

-  +'&'#154#18'$'#214's'#210#200#12#219''#249#2'>}'#246'F)'#191#167'A'#129'j'

-  +#227#207#184#251#179'A'#189'6'#28#244#195#138#27'G'#167'c'#172#176'r''8'#248

-  +#194#7'Xr'#213#136'@'#143#250'?O'#0#188#253'O+'#191#199#229'C'#18'p'#130#189

-  +'v%'#211#12#4'('#229#177#224#228#252#246#175#240#249#2#1#212#0#231#4#164#209

-  +'`-h'#198#187#131#136#1'M'#6#18#15#7#141'F'#163'6'#180'%J'#28'~'#221#190'->'

-  +#243#29#226''''#170#212'z('#172#222#19#140'&'#29#152'mD'#0'\B'#144#208'&<'#27

-  +'j'#192#11#215#31#8#145'P'#250#26'Wd'#3#244#3'>'#253#174#143#7#245#218'_'#254

-  +#251'*'#172'|{'#244#14'DUi'#13#176#223#153'd~'#148#145#138#4'_'#0'D'#241#255

-  +#160'?'#4#30#180#255#189#184'9[j'#192#221#250'['#138#227#215#160#14#219'+'

-  +#204#142#213'('#252#14#138#251#211'<'#0'>'#19#144'*'#1'['#7'3'#28#148' G'#155

-  +#129'U'#4#250'|>'#139'F'#163')'#142#199'a'#194'F{'#225#243#137#132','#217#29

-  +#129#170#168#24#1'X,'#204#15'`'#160#210'`'#230#7'P$'#29#129';'#156'-'#205'N'

-  +#235'_'#191'z'#19#190#249#231'_%'#249#162#6#11's'#193'88'#230#250#193#141#147

-  +#254#247#131#23'@k'#141#180#141'1s'#1'TA8m'#193#239'a'#206#1#167'g'#181'G_Z'

-  +#209#131#134'L+<'#133#255#200#254#15#160#253'/'#16'@{'#221'Z'#8#186'[S'#8#160

-  +#208#224#221'R'#160#247'Q'#185'g;'#17'@'#235#0#196#167#239#14';d'#3#226'V'

-  +#185#197#158#255#167'PL1#'#249'$'#20'rK'#241'N`-'#174#0#147'U'#143#4'`'#0#173

-  +'A'#13'*%'#154#1#138#204#205#10#236#138'Wn;'#10'|'#206#214#236#156','#13#152

-  +'0go'#216#231#140#255#27#212'k'#159'_~'#0'D'#130#185'U'#211'/%'#168#15'C'#233

-  +#164']'#160'z'#151'}'#161'j'#230#226'a'#31'2dFz'#156'S'#255'#A'#222#254#167

-  +#30#0#184'o'#222#252#5#196'#'#225#20#2#24'o'#181#175#210#169#162'-x'#179#29#6

-  +#144#5#200'>'#187#222'>W'#241't d'#146'B4'#5'*'#154#188#182#179#221'!'#237

-  +#209#201'''Q'#131'PK)'#228'W'#206'dQ'#0#163#197#0':'#147#150#21#6#201#149#217

-  +#233#15'@h'#217#250#19#252#251#129#225#227'%^t'#194'r'#152#186#231#224'R'#130

-  +#159#185'b'#201#176')'#132#146#2#148';'#160'3'#231#225#162'4'#30'J'#170'g'

-  +#195#196'y'#251#131#9'5'#174'a'#131#4'7'#9#184#211#254#199#213#159'e'#0#182

-  +#131#189#230#7'6"L'#144'c'#133'<'#30#153#146#223#246'5'#10#186#7'e'#141'V@'

-  +#178#251#5#2'h'#196#197#219#217'S'#18#16#161'W'#2#16'&'#4#163#253'o'#212#233

-  +'t'#249#241'x'#188#188'#'#168']'#220#232#177#220#156'|'#18#10#184'R'#163#131

-  +#226#137#11#153#31#192'h3'#128#158#18#130#180#252#184#176#12'N'#12#238#138

-  +#183#239#251#3#180'n'#27#218'l'#192'l'#225#212'?'#0#154'A6'#191'x'#242#210

-  +#133#144'v;j'#132#131'HAo)'#128#252#242')P>mw'#152#186#224#240#156'4'#27#216

-  +#234#159#224#212#255'('#170#255'd'#255#147#240'3'#251#191'y+'#218#255#155'Xn'

-  +#128#0#163':'#212'^aq'#145'='#216#129#178#216#140#143#17#1#176#8#0'.'#220#212

-  +';'#189#199#193#160#132'>'#9'@'#28#10#196#251#202#240#160#213#155#28'%'#143

-  +#196#19'2'#29'{'#18'k'#5'.'#135#194#9'{'#128#201'f'#227#205#0#174'.@AZ'#0#171

-  +#14#236'>)'#168#219#147#14#225#195#11#184#29#240#210#141#135#231'|^@~'#197'T'

-  +'8r'#217#179#131'z-E'#0#158#191'f_'#169#223#194#176#135'J'#171#135#165#167

-  +#222#2'U'#179#150'd'#252'\'#253#253#221#211#243'h'#177#140#199'x'#245'?'#20

-  +#129#128'7'#136#194#239#227#226#255#219''#132#128#167'-%'#2'Pd'#240'l'#201

-  +#211#249#182#163#28#186#144'8'#154#132'$ '#218#227#202#223'b0'#24'X'#8'p'#221

-  +#186'u'#20'7'#140'ww'#222#30#175#137'"'#1#223'}'#247#157'*'#16#8#232#208#20

-  +#176#161#9'P'#138'D0~'#155'3'#255#250'`T9C8'#4#249#1#172#165#211'Q'#229#170#2

-  +#163'@'#0'z'#13#168#132'h@'#182'T'#0#196#202'='#12'k?z1k'#231#27'('#232#179

-  +'8'#254#246'7'#192#152'W:'#168#215#7#189'Nx'#241#218#209#25#2#204#4#138#170

-  +'g'#194#129#23#220';hm,'#221'`'#222'Z'#253#163'Q'#8#163#250'O'#246#191#151

-  +'%'#0#249#161#5#237#255'h'#23#251#191#218#214#190'J'#173#136'R'#248#207#129

-  +'6R'#8#144#239#7'H'#154'@k$'#18'q{<'#158'Pw!@B'#175#4'@'#155#16#9#160#230

-  +#160#212#23#0#239#171'j'#246'ZNv'#5'uG'#8'O$'#2#208'['#202' '#175'b&'#18#0

-  +#154#1'f'#3'W'#23'@'#141'B'#217#200'0'#25'd'#205#14'@'#188#255#224'%'#208#176

-  +'ae'#214#206'7'#16'L'#156#191'?'#236'}'#214#29'C:'#198#147#23#238'.'#245#219

-  +#24'Q '#243'`'#223's'#239#204#138'6'#208#27'HB'#168#244'7'#30'E'#2#8#161#250

-  +#31#8#129#223#205#169#255'n{+'#180'o_'#197'Z'#131'u'#14#2#137#133'&'#229#181

-  +'}'#143#194#239#195'?'#237'|'#8#144#217#255#168#181#215#247#214#12'T|'#206'^'

-  +'?'#27#161'3'#144'^'#175'7'#1#31#9#240#132#212'{'#214'uX'#151''''#15'"''?'

-  +#128#22#138'''.b'#171'?e'#4#234'L|8'#144#175#13'`'#166'B'#22'?'#204'Wo>'#10

-  +#220'm'#245'Y<c'#223#160#216#255#25#247'}'#142#164'84'#219#243#169#139#23#12

-  +#203'v_'#185#12#234#251''#250#189#159#14#249#187#25','#18#252'?d'#190#10#222

-  +''#10#255#249'X'#7'`?8'#154'6'#131#167'u+'#155#15'('#20#1#153'5'#193#214'2'

-  +#147'k'#3#202#150#151'/'#3#174#23'"'#0#168#177'7'#4#131#193'^#'#0#132#190'd2'

-  ,'%'#18#160'T*'#11'('#18#128''''#154#176#177#189#232#238'xBf`OB'#225'&!'#167

-  +'&'#161#198#252'<F'#0'z'#150#22#140'f'#128#150#239#22#204'r'#2#178'G'#1#209

-  +#144#31'V,?'#20#213'(o'#214#206#217#23#22#157#180#28#166'/9z'#200#199'y'#238

-  +#138#189's'#234'}'#141#20#140#159#179#20#246'?'#255'niN'#158#224#230#255#177

-  +#226#159'p'#20'B'#168#254#7#188#1#240#241#249#255#237#181#171#248#177'l'#157

-  +'2\bto'#182'j'#253#212#253#199'ME@'#192'G'#0'('#17'H'#165'R5'#225#177'z'#141

-  +#0#16#250'$'#0'!'#18#160#209'h'#12#168'N'#144'#p'#28#158'`|'#141'3'#239#202

-  +'@T'#181'3'#247','#25'('#228'r0'#21'V'#131#173'l2'#232'id'#184#5#9#192#160'e'

-  +'Z'#128#156'z'#4'p'#163#131#179'i'#9#160#6#208#0#175#221'|tN'#172#150#187#31

-  +'s'#25#204': ='#13'L'#158#191'b_'#8#249'r'#183'-'#250'p'#198#239#151'?'#195

-  +#252#2'Y'#5#231#250#231#212'J'#254#9'E '#228''''#245'?'#0'~'#150#0#228#134

-  +#214'-_A'#12''#199'b'#7#224#196#188#182#31'T'#242#152#155'w'#0#178'" >'#3'p'

-  +';'#202#27#253#221#209'S'#17#144#128'~'#17#0#213#4'x<'#30#180#2#244'Vr'#4'"'

-  +#17'T5{'#244'G8'#2#198'dA;'#9#184'Zg'#132#162#137'{2'#2' '#13'@'#199#178#2'5'

-  +#172'6@'#208#2#178#146#27',B'#243#230'5'#240#254'}'#23'B4'#18#202#234'y'#197

-  +#216#237#168'K`'#246'Ag'#164#237'x/\'#181#31#174#6'C'#155'~3'#134#238'A'#157

-  +#154'N'#185#247#163#172#159#151#171#252#227#212#255'p0'#12'Ao'#144#9'?'#173

-  +#254'T'#244#229'j'#222#200#156#131#130#253#175'VD'#253#19#243#236'?'#226#223

-  +#148#17#230'@'#18'`'#14'@'#222#254#167#134' }:'#0#9#253#145'F9'#146#128#18'I'

-  +#128#210''#205'h'#10#20'#'#9'T'#198#19#138')'#155#157#133#183''''#18'2V'#149

-  +'A'#194'M'#13'B'#10'*'#231#161#25'P'#8#6#19#18#0#211#2#136#0'T'#160#16'*'#4

-  +'3<B'#188';'#132#253'^x'#235#174'3'#192#217#152#253#230'!'#187#29'y1'#204'9$'

-  +#189'C%_'#188#250#0#252'q'#180'g'#253#189#140#22#28'}'#211'K'#144'_'#153#165

-  +#161'.'#178#4'_'#247#207#173#254#156#250#31#130#128''''#192#28#128#20#5'h'

-  +#223#246'='#254#237'J'#177#255#11#244#190#218#2#189#135'B~^'#20#254'v'#222

-  +#254#23'j'#0#26#250'J'#1'N'#158#190#31#151#200#28#129#249#249#249'j'#161'='

-  +#24'%'#4#145#31#160#182'#'#239'|D'#205#244'%'#230#228#195'U'#222#148'W'#9'y'

-  +#21#211'Q'#11#224#252#0',+PK%'#194#157#206'@.'#190#144']'#18' |'#243#242#221

-  +#240#203#199'/C6'#146'h'#200#169#180#224#164'e0u'#241#145'i?'#246'KW'#31#4'>'

-  +'gK'#198#223#195'h'#197#180'%G'#193#226#211'o'#202#206#201#184#170#31#142#0

-  +#248#216''#208#23'd'#130#207#212''#167#19#9#224';'#212#12'"b'#251'?1)'#191

-  +'}'#21#170#255#228#8'"'#251#159#26#129#214'R'#248#15#23'h'#234#1#208#136#194

-  +#239#232#203#254''''#244'G'#10#153'#'#16#15#166'"?'#0#10'p>'#158#168#140#252

-  +#0#206#128'vi'#179#215#194#21'h'#147'#PFf'#128#14#205#128#5#140#0'H'#3#224

-  +#162#1#252#244'`j'#24'*'#151'u'#27#17#200#240'P'#225'$'#26']'#9#31'>|%D'#130

-  +#190'L|'#157'L'#203#153#186#232#8'Xr'#234#13'd'#23'e'#228#28'O'#253'a7'#246

-  +#131#24'Cf`*('#131#19#239'zw'#200#199#233#235'7-<'#206#21#254#196#208'LE'#2

-  +' '#231#31#18'@'#160#131'['#253#157'M'#155#192'M'#222#255'h4'#169#254#235'U'

-  +#225#142'J'#139'c'#29#202'Q'#0#239's'#1#215#4#164'V'#176#255'qk'#17#154#128

-  +#160#240'G'#249'S'#13#158#0#186#250#1#144'eJ'#240'~'#234#215'\'#189#201'Q'

-  +#180'<'#22#151'S'#136#144#17#0'e'#255#217#202'g'#130#185#176#140'#'#1'3o'#6

-  +'h'#133#204#192#206'nA;'#148'Bw'#185#154'L'#182#186#219#248#191'7'#224#199'w'

-  +#159#2'O['#250'Zj'#149'M'#155#15#251'^xOF'#139'Q'#234#214'~'#9#31#220'?z:'#1

-  +'I'#1'J_?'#231#137#31#134'D'#224#221')'#184']'#207','#251'/'#193'y'#254#185

-  +#213'?'#138#230'j'#136'y'#255'I'#253#15'P'#243#143'-'#223'@'#200#239'I'#137

-  +#255#151#24#221'[l'#186#0#165#249#250#241'>'#150#0'$'#148#0#227#177#234'PKo'

-  +#235#143#253'/\C'#192#252#0#168#5'h'#220'n'#183#5#217#165#8'OXIf@'#131#219

-  +'r'#130';'#164#221#141#29#140#198#131'+'#20#172'8('#175'j&o'#6#232#144#0't'

-  +#160#210#169#147'Z'#128#16#17#200#5'8'#27#182#194#183'/'#255#5#26'~]'#201#134

-  +'F'#14#4'T'#133'f-'#29#15#19#230#239#15';'#239'2h'#12#153#207'&{'#239#238'?'

-  +'@'#195#250'o'#165#250#184'F'#13#246#189#240'n'#168#222#245#128#140#158'#!'

-  +#168#255#148#249#23#137'B4'#200#169#255'd'#255#7#220'T'#252#211#10#142#237

-  +#171'!'#134#143#197#249#231#202'e'#137#216#228#252#182#213'2'#136#7#248#248

-  +'?9'#131'X'#252#31#229#170#6'o7'#160#156#182'['#173'V__'#246'?'#161#223#4#128

-  +'f'#128'\'#156#15#128#251'rd'#155#9#193#152'v'#238'v'#151#237'\v02'#3'P'#184

-  +#149#26'5'#20#146#25'`6'#161#9#192#145#128'Z'#199#229#4#144'/@&'#242#5#236

-  +#176#234#15#225'"'#135#2'R'#177#234'~'#254#10#26#214'}'#11'm'#219#214'1'#205

-  +#128'&'#203#138#175#130'VvKq'#5's'#16#149'M'#159#15'e;e?#'#239#233'sw'#131'h'

-  +'X'#186#136#198'hA'#213'.K'#225#192#203#31#24#212'k'#251'j'#132#155'L'#250

-  +#137'sC?c'#164#254#211#234#31#8'A'#136#188#255'l'#245'G'#245#191'a=x'#237#245

-  +')'#225'?'#179'&'#216'^fr'#209#196'W'#26#11#228#22'*'#0#137#0#132#30#128#129

-  +'@'#192#137#230'z'#159#246'w'#215#214#227#243#132'|'#0#178#1#168'0('#26#141

-  +#150'Q8'#16#31#155#176#197#145'a8'#166','#226#252#0'\4'#192'R6'#13#204'EU,$'

-  +'H~'#0#202#9' -@'#201#198#135')'#144#4#184#196#160#28'Q'#4#134#5'>z'#248'*'

-  ,#216#246'}'#246'CT'#163#17#182#242'Ip'#236#31#223#200#216#241')'#233''''#17#3

-  +'V'#214'M+<e'#254#133'|!'#8'2'#245#31'5'#0#175#31#218#182'|'#9#225'`0%'#252

-  +'Wnv'#254'j'#210#132#169#243#15'i'#0#20#11'n'#166#30#128#148#2#204#183#3'o'

-  +#161#30#128'F'#163'1'#216#151#253'O'#232'7'#1#8#133'Ah[h'#145'e'#168'.'#160

-  +'D0'#3'Z}'#166'C'#28#1#195'^'#236#137#188#25'@'#241#212#252#170#249','#10'@Z'

-  +#0#17#128#218#160'a'#218#1#27#30'"'#20#9#245#196#0#217#152'0:'#140#176#253

-  +#199#207#225'?'#247']"'#245'e'#140#26#232','#5'p'#202'C'#3#236#217#216#31#245

-  +'5'#1#201#142'?$'#216#204#249#199#135#254'h'#245''''#2#8#184#131#224'io@'#13

-  +#224#231#20#245'_)'#143#135'Q'#253#167#216'?M'#195'a'#249#255#184#177#22'`'

-  +#212#2#156#230#0#226#194#220'F-'#192#236'v{'#184'/'#251#191#187#203#235#13')'

-  +#29#130'h\'#24'2M9^'#204#248'h\9m'#139#163#224#194#4#249#1'y3@'#161'V1'#2'0'

-  +#216#242'QuF'#13#128'i'#1'd'#6#16#9'(QK'#16#21#9#141#169#1#189'"'#26#244#195

-  +#11#23'-'#193#31'Jp'#232#7#27'C'#191'@'#181'-g>'#249'C'#250#15#156#224#226

-  +#254'@'#158#255'8'#231#249#143#6#195','#243#143#169#255#168#250#19#9#216'kV'

-  +#163#25'`gY'#172#130#250'o'#211#249#27'K'#140#30'R'#247#131'|'#250'/'#27#3'F'

-  +#241'R'#255#169#1#8#154#19#142#190#242#255#197#24#136#228'%'#195#129'V'#171

-  +#213#24#12#6#169'Sp'#25#17#0#238#171'j'#156'yg'#4#162#170'*'#193#12#160#162

-  +#10#163#173#4'l'#21#179#144#0#180'<'#9'hy_'#128#154'9'#3#169'T'#24'dBX'#176

-  +#135#14#194#144'$'#205'~^e'#250#191'3)A~'#136#215#151#31#1#174#198#209#212#2

-  +'\zP'#133#224'9'#207#241'c'#214#135#26#141#226#139#253#19#252#129#216#234#207

-  +'l'#170#250#139#176#208#31'9'#255'H'#240#131'H'#0'>G'#27'8jWC$'#28'I'#241

-  +#254'O'#176#182#175#213#170'b'#30'R'#255#241'O'#23'5'#0#1#222#254''''#239'?'

-  +#223#16#164#163#183#6' '#221']Z'#191#223#134#16#14#164#254#0#248#183#21'U'#13

-  +#150#21'H='#2':'#130#186#5#141#30'3+'#17'f'#177'~'#185#2'Tj5'#20'T'#239#10'z'

-  +#139#141'E'#2#136#0'4d'#10#176'f!*'#166#5'0_'#128#140#159'(<'#194#132'w'#168

-  +#160#130#159#215#151#253#14''#16'cI?'#217#134'B'#165#129#179#158']'#157#222

-  +#131#242#131'>'#133#142'?'#204#246'G'#2#8'3'#225#231#9#0#247#142#186#159#192

-  +#239'la'#142'iA'#253#215#169'"'#238#241'V'#199#175#188#250'O'#9'@'#164#254

-  +#179#240#31'%'#255#144#250'O'#225'?\'#160'='#253#9#255#9#24#16#1#208'&'#152#1

-  +#8#19#153#1#148#21'H'#4#128#143'Umu'#22#156#25#142')'#11#133'.A'#20#247'7'

-  +#228#149#129'm'#220#206'I-@C$@CDY'#179#16'!/'#128'F'#137'e?E8'#151'AQ'#136'7'

-  +#174';r'#172#234'O"'#168'tF8'#227#201#244#246#148#16#135#253'('#238'O'#158

-  +#255'H'#16'U"'#0#15'n>'#170#254#179#163#250#191#10#205#189'0'#243#15#8#234

-  +''#185#217#185#193#168#14#145#211#143#169#255#192#13#1#173#23#17'@'#147#160

-  +#254#163'lF'#250#10#255#9#24#168#196'13'#160#176#176'PE#'#195#168':'#144#6

-  +#134#224#133'T'#145')'#224#10#234#23'4{'#205#7#2#175#210'S'#21' '#211#2#198

-  +#239#14':'#139#5'4F'#29's'#10#170#13'|'#207'@'#181#138'=G!'#231'g'#9#202#248

-  +'K'#234'O'#154#224'`'#223#193'0@'#235#166#159#224#223#183#159'6,'''#31#143#20

-  +'h'#205'yp'#234'c_u'#255'`_b'#213#205#239#151#21#251#240#163#190'H'#176#227

-  +#201#213#191#211#249'G'#171#191#179#254'g\'#253#155'8'#231#31#175#254'k'#149

-  +#17#239'x'#171'}='#10'{'#132#138''#200#251'O'#237#191#240#200#181'|'#2#16

-  +#169#255#164'&v'#168'T'#170'`w3'#0#251'{'#169'}>_0'#3'('#26#128'['#168'K'#16

-  +#158#188#130#8#0#168'm'#184#179#240#244'HLa'#21'j'#3#148#168#234#27#243#202

-  +#193':n:'#168'Q'#11#160#193'!D'#4'j'#22#18'$'#18'P'#240'aA9?V|'#16'W5'#130

-  +#176#229#235#247#224#243'G'#174'a'#182#223#24#164#3#133#1#143#190#251#157#244

-  +#28#140#15#252'S."'#202#175#254'h'#223'G'#130'a^'#253#15#176#172#191'@'#135

-  +#19'W'#255#239#185#213'?'#130'$'#193'1'#7#140'3w'#252'f'#214#4')'#244#23'F'

-  +#185#242#240#201'?'#228#253#167#240'_'#13#202'"'#205#4'l'#31#136#247'_'#192

-  +'`D-'#165'8'#8#184#193#161#204#25'H'#154#128'#`X'#208#234'3-'#21'F'#131#145

-  +'3P'#169#209'@'#225#132#221'@k'#178'0'#19#128#242#2'Th'#6#8'Z'#0#171#20'Lv'

-  +#16#150'%'#157'&'#253'm$:R'#176#254'?+'#224#235'g'#239#200'l'#14#244#24#250

-  +#133#233#251#159#8#11#207#186'eP'#175'M'#249#221#242'!?'#230#253#143'q'#5'?'

-  +#156#237#31#134#8#31#250#163#212'_'#218';'#27#214#129#223#209#8#209'H$'#25

-  +#251#215'('#162#254#9#182'v'#234#250'K1}'#10#3'Q'#247#223#22'~'#213#175#193

-  +#191'k)'#249#167#175#246#223#189']'#235#128#223#159#208'-'#24'm'#13'='#178

-  +#143#141#204#0#188#191#146'i'#1'2Y'#229#22'{'#193'I'#145#184#194#196'r'#2#228

-  +#10'P'#170#149'`'#200#175#2'k'#233'T\'#253#181'H'#2#168#5#232#185#196' V)H'

-  +#179#4'),'#168#232#140#10#176#19#245#148'" zk'#178#228'?'#195#27#171'^{'#8'V'

-  +#191#254#176#212#151'1'#6#30#135#223#186#2'J'#166#207#239#246#177#148#223'_/'

-  +#191#189#4#223#232'#E'#245#167#213'?'#24'a5'#255#17'?'#231#252#11#177#216''

-  +#7#180'o['#9'Q$'#6'z'#14#181#6#163#215#149#153':'#182'X'#180'A'#154#248#19

-  +#225'{'#255#145'&@+>'#9'>'#133#255#234'q'#223#170#211#233#220'='#13#0#237#13

-  +#131'"'#0'J'#10'B'#150'a'#205'B'#3#129#128#153'J'#132'c'#177'X'#5'e'#6'rZ'

-  +#128#17#181#0#227#158#130'3'#144'T|'#138#255#231'O'#216#3'W'#19'G'#2#212'4T'

-  ,#207#151#10#147'CP'#221#233#16#148#241#166#128'l'#7#233#30#204#202#152#251

-  +#236#240#213#147#183#192#175#31#189','#245'e'#140#129#7#253#6#207'~'#249#215

-  +#129#190'Jt;'#145#12']'''#189#254'd'#207'Gx'#199#31#173#254#254'P'#210#251'O'

-  +'{W'#227#175#224'u'#212#177'~'#0#148#31'@i'#194'jE4Pmk'#251#5#229'!'#138#199

-  +#160#252'o'#15'nT'#250'['#207''''#255'lG'#217'#2p'#244#167#246#191#175#171#30

-  +#208#187'%g'#160#201'dR'#163#218'a'#196#191'Yj0'#229#3#224#237'*|'#175#228#11

-  +'8>'#154'P'#232#153'3'#16'Ww'#188'P0'#20#146#22'0'#133'9'#1'Y8'#144'6=_)H)'

-  +#194'h'#6'P'#18#145'L('#22#202#206'lQIa'#175#217#0'o.'#251#157#212#151'1'#6

-  +#17'J'#166#205#135#195'niH'#199#232'n'#245#143#161#240#179#184'0'#196'9'

-  +#255'|A'#206#1#232'qC{'#205'w'#168#25#132'x'#207'?'#183#250#151#24#221#219

-  +#172'Z'#155#200#249#151','#253'%'#225'GY'#169'C'#185'k'#182'Z'#173'.'#148

-  +#195#192'@'#156''#2#6'M'#0#130'3'#16#153'G'#231'r'#185#200#233'W'#140#23'T'

-  +#1'\'#153'pe'#155#207#184#208#30'0'#206'Kj'#1'H'#2'*'#141#22#242'(='#216'bE'

-  +#2#208'$'#137#128#180#3#133'P($T'#11#138#251#6#140'`'#22'x'#251#250'c'#161'u'

-  +#211#26#169'/c'#12'"'#28'q'#215#155'PP'#189#243#160'_'#207#249#252'D'#141'>H'

-  +#168'#'#188#227'/'#192#173#254'L'#248'}AF'#4#174'z'#180#253#157'h'#251'S'#211

-  +#15'z.'#190'T%'#139#134'&'#230#183#255','#227'l'#255#16#18#0#139#253#163'l4'

-  +#144#240#227'mr'#0#178#206'?]b'#255#253'V'#255#9#131'&'#0#232#146#19#128#23

-  +'H'#163#195'h'#0'[%nU1'#210#2#28#133#199#198'P'#180#217#160'p'#210#2'P'#192

-  +'u'#150'"'#176'U'#204'fu'#1'I'#2'`'#14'A$'#1'V.'#204#153#2#192'L'#7#254#242

-  +'Fh'#170#176#223#217#10'/'#159#183'p'#204#233#151'C0'#20#148#194#9''#255'b'

-  +#208#175#151#241#169#190#204#8#136'w'#198#252#185#130#31'A'#245#199#205#31'`'

-  +#197'?'#1#23'e'#253#173#225'<'#255#209#206#213#191#216#232#169#177'i}'#173

-  +#252#234'O'#153''#212#5#150'B}'#245'T'#246'KN@Z'#253#145#0#156'~DSw'#184

-  +#222'!|V'#201#10'A'#179#217#172'#g '#222#199'B'#130#184#175#162'=j'#1#11#218

-  +#253#134#185#204#169'Ge'#194'D'#2'j'#13'X'#199#205#0'}~)S'#255#213#6#29#211#6

-  +'Tz'#13#31#22#228'{'#6'$'#253#1#248#142'd'#217'm)'#158'-|'#244#231's'#161#246

-  +#135'O'#165#190#140'1'#136'p'#240#205#207'C'#217#172#133#131'|5'#159#236#203

-  +#219#253'B'#177'O'#156#217#254#188#234'O'#4#224#193#149'?'#192#217#254#142

-  +#237'?@'#200#211#193'&'#1'1'#2#192#255'T'#242'hp'#162#205#142#182'"'#202#135

-  +#254#216#234'/'#196#254#129#235#250'['#143'2'#215'N'#206'?'#170#252#235#173

-  +#243'oo'#24#18#1#144'3p'#221#186'u'#172'QH '#16'0'#161#6'P'#128#23'Im'#195

-  +#153#22#128'W2n'#155#179#224#247#225#184#202'"h'#1#148#2#172#209#27#208#20

-  +#216#13#212#148#19' '#242#5'(u'#184'Qr'#16#159#27' h'#2'2'#161#157'xOq'#193

-  +#158#222'E'#142'/'#172#207#158#176#19#174#12'c'#181#253#185#130#202#249#251

-  +#192#254#215'='#193#253'1'#136#223#148'X'#248'i'#229#135'8'#151#238'K'#142'='

-  +#177#227'/B'#234'?'#222#246#180#214#128#167'e'#243#14#171''#185#197#181#209

-  +#168#10'R'#184#143#250#190#177#208#31'n'#173'(_'#245'|'#209#15'%'#0'5S'#230

-  +#31'j'#224#254#193'8'#255#4#12'uYMf'#6':'#28#14#189'R'#169#180#225'E'#22#227

-  +'F'#137'AL'#11#240#133'5'#211#235#220#182'}'#4'-@'#193#180#0'5'#24#11''''#128

-  +#185'x"'#175#5'p$'#192#180#0#161'u'#24#223'>'#172#147#4#248#203#29'A'#138#192

-  +'sD'#0'c'#21'~9'#1#26#22'z'#242#243#171'Q'#251'T'#15#238#0#188#25#215#185#242

-  +#243#170''#152's'#252#145#131#143#179#253#3#16#246'"'#17'x=,'#233''''#18#12

-  +'B'#140#250#253'E'#185#196#31#20'|G'#133#197#181#5#15#21#19'V'#161#237#23

-  +#169#253#192#245#252'k'#224#203'~'#221'}'#245#253#239#11'C&'#0'A'#11#160#182

-  +#225#168#9#144'/'#160#0#137#128#249#2#144#8'H'#19'(k'#240#216#246#246#132#181

-  +#149'r'#150#29#168'`5'#0#10#10#11'V'#206#3#141#201#10#26'#g'#10#168#146#17#1

-  +'5'#243#7#176'b!'#218#152'#'#145'+'#27#30'I'#166#192#138#211'v'#129#144#215

-  +'%'#245'e'#140'zP'#225#218#161'w'#188#10'E'#211#230#13#234#245#9#174#212#143

-  +#247#250#199#217'l'#191#4#223#230'+'#30#140'$W'#138#251'3'#199#31#222'v5'

-  +#252#2'AWK'#202#234'/C'#218#168'F'#213'_)'#143#134'zZ'#253#5#207'?n'#142#254

-  +#182#253#234#245#189#167#227#243#19#215#7'h4'#26#214'4'#148'O'#15#166#188#128

-  +#242'h\^'#177#213'Yxp'#2#215'uA'#11'P'#168#212#160#181#20#176'ra'#181#174'S'

-  +#3'P'#11'Z'#0#229#6#8'Q'#1#190#155'0'#136#202#134'G'#130'_'#240#213's'#22#130

-  +#175#189'Q'#234#203#24#213#160#197'e'#191#27#158#132#10'T'#255#7#140#132'x'

-  +#166#31#231#244'#Afv?'#173#252#225#8'K'#250#137#6'B'#172#221'W'#132#247#250

-  +'S'#165#159#139#154'}'#160#240#211' '#16'a'#245'/'#212'{'#234#10#244'>'#10

-  +#243'u]'#253'i'#236'7'#181#253#222#142#130#223'@UH'#8#238#129#230#253'w'#251

-  +#254#211#241#25'v'#213#2#240#194#10#132#136#128#160#5'8'#2#198'9'#173'~'#211

-  +'l'#210#2'@'#161'`'#14'A'#5#154#2#150#178#157'@'#159'W'#202#146#130#152#240

-  +#147#22#160#227'H'#128#181#15'S'#241#237#196'EY'#130'2y'#186'.]Z'#252#235#210

-  +#3#193'Y'#187'Q'#234#203#24#189#192#223#210#146#203#255#10#19#247#26#194#236

-  +#6#22#231#7#214#224#143#229#250#243'N'#191'X'#132#19#254#8#175#250'G|'#156#6

-  +#16#246#243#142'?'#159#155#21#4#9'q'#149'<'#26#168#206'k_'#143#199#139#241

-  ,#171'?y'#254'Y'#213#31#173#254#184#175'U('#20#181#180#250'k'#181'Z'#7#146#192

-  +#144'W'#246#17#164#235#163#236'N'#11' _'#0'p'#17#1#26'$R'#180#213#153#191''

-  +'8'#166#178#10#217#129'$'#220'j'#189#145#229#6#168#13'z.)'#136'O'#17'V'#178

-  +#220#0#222#20'`'#141'D'#249#14'Bra'#196#24'$'#213#128#225'J'#5#31#222'r24'

-  +#174#249'R'#234#203#24#149' '#13't'#191#27#159#134#178'9'#139#7#252'Za'#213

-  +#231#179'}x'#187#159#19'~Z'#209'i'#245#143#133'"'#201#176#31#229#252'GI'#248

-  +'q'#239'i'#217#10#222#214'm'#140' '#152#237'O9'#255#248'_'#165#217#177#193

-  +#160#14'S'#166#31#9'4y'#134#133#145#223'Md'#251#147#240#163'L'#213#167's'#245

-  +''''#164#141#0#186#243#5#0'7H'#180#146#15#13#150#249'#'#234#9#181#238#252'%l'

-  +#29'Wt'#154#2#134#252'r0'#151'N'#229'j'#3#152#6' r'#8'R'#247' '#149#146'+'#24

-  +#18#154#137#178','#193#225#239#24'l\'#243#5#252#231#230#147#165#190#140'Q'#7

-  +'CA'#25#28'~'#223#187'h'#130#230#15#252#197#157#195'y:'#139'|'#226#156#205'O'

-  +#182'?'#173#234'L'#245#167#132#31'a'#245#199'-'#26#8#254'{_'#22'kYv'#158#181

-  +#246'x'#230's'#238'XsWwW'#187#219'v'#187#227')'#241#0'v'#136#193#145#8#194'F'

-  +#8#5#148#4')'#145#176'P'#132#132#20#241#18'!9'#188#0'/H'#188'!'#224#1'x'#2#9

-  +#17#144#128'XHH$`x'#128#7#144#172'$'#216#198#221#158#186'k'#174'[w:'#243#176

-  +#7#254#239'_'#255'Z{'#237'}'#207#173#186#213']w'#170#190'K'#218'w'#15#247#12

-  +#251#236#189#191#239#159#255#165#166#253'='#181#243#222'wT2'#155#209'k'#23'\'

-  +#25#8#159'A;'#158'n'#223#232#238#161#229#19#192#159#152#184'?:'#254#162#230

-  +'_'#233'Yn/'#145#254#0#255#7'*'#27'}'#158#208')E'#4#136#173'V'#137#165'.'

-  +#139#22#128#174'A'#215'Q:|o'#208#251'b'#214'x'#153'+'#255'B"'#129' d'#144

-  +#247#174#146')'#176'zUH@'#8#160#225#248#3#184'V@'#155#2'l'#14#248#158'T'#15

-  +#150'I'#158#23'?'#201#243#206'x'#28#144#198#191#254#181'O'#169#217#197'D'

-  +#159'''3'#232'ay'#245#203'_S'#191#240#219#255#248'H/_'#250','#229#198#225'/'

-  +#137'>'#22#252#186#208''''#227'2_m'#247'c'#129#218#191#24'C'#19#152'p'#155

-  +#175#249#184#207#182#191'Q'#253'=z'#247'k'#171'['#28'x)'#138'}JY'#180'_<'

-  +#255'h'#251'u'#239'yK'#254']'#207#243#242'.'#203#11#160#31'q'#21'~'#0#163#5

-  +'dyp'#229'G{'#27#191#152#229'~'#13#0#246'E'#11#136#234#13#181#242#210'gT'#173

-  +#211#213'&'#128#144#0#182#131'Z'#173'0'#5#196#31'P'#174#28'<'#191'Z'#192'w'

-  +#254#229'?P'#244#187#23'U'#128#199'=z/}D'#253#153'o'#254#11#213#189'~'#235

-  +#253''#8'W'#248#228',ssW'#242'['#187#159'l'#250#217'\KV'#253#167'"'#253#231

-  +'j'#255#222#247#213'd'#239'>{'#253'a&(N'#249'e'#199#223'{'#235#205#17'z'#251

-  +'C'#250#163#3#12#247#251#163#5'Y'#128','#253#197#7#240'p>'#159#239':'#158#255

-  +#15','#253'1'#158'7ll'#227'Pd'#7#146#22#128'9'#178'.!'#18' Q'#1#172'/'#239'O'

-  +'ko'#220#31#174'~'#206#19#135#160'1'#5#226#246#138'Z}'#233#211'l'#2'@'#250'G'

-  +'-q'#8'J'#243#16'?'#150'Ta3'#211#176';'#229#184#167'='#186#207#214'A'#244#244

-  +'G'#150#204#213#191#250'K'#175'?'#243#172'D'#23#227'h'#163#181'y]'#253#220'_'

-  +#251#166'z'#229#231#223'g'#193#149#17#252'z'#30#175#178#211#15#246#187#145

-  +#252'H'#248#129#228'g'#2#152#217#176#31'r'#255'G'#219'w'#200#246''#135'$'

-  +#255#140'I"Ou'#185'o=\'#244'_'#233'm'#191#157#231#252#137'\'#239'o'#26'~@'

-  +#250#211#254'm4'#251#132#244#199#12#192#181'Z'#13'>'#130#217'Q'#27'~'#30'e<w'

-  +#2'0'#165#194'd'#10'`'#18#145'6i'#2'0'#180#174#144#9'pC'#180#0'D'#7'6'#239#15

-  +#186#159#221#159'7_e5'#222#15'X'#186'C'#213'o'#174#222'P'#157'+o'#20'Z@C'#155

-  +#2'Q'#3#25#130#226#15'0'#165#195#156'#P'#20#14#217'_t'#206'b'#132#255#229'w~'

-  +'U'#221#255#206#255'8'#237#211'xq'#6#221#255#205#143'~V}'#254'7'#255#158'Z'

-  +#227'S'#31#236#179'rG'#245'W'#134#0#164#179'O'#166#139'|'#24#252#28#242'+'

-  +#164'?l~'#238#246#219#223'U'#251'w'#255#144#247'a'#247's'#216#143#136#195#247

-  +#211#197#173#149#199#223#13#253#12#30#255#212#244#250#151'v_'#8#5'b'#210'J'

-  +#164#253#222#165'}h'#3'{'#200#249''#150'~G'#186'T'#199'q'#249'M'#195#144'$'

-  +'I'#26'q'#28'w'#137#197'6'#209'5'#8#254#0'D'#4#232#7']'#165#179'_w'#227#203

-  +#179'$^'#209'*'#189'N'#19'F'#135#160#238#213#143#171#230#218#21#142#4#128#4

-  +#160#1#128#8'8*'#0#240#219'.B'#1#147#7#28#131'6G'#192'w'#136' '#247#158#173

-  +#155#0#212#187#252#228#201'c'#255#246#15#213#239#253#141'_'#184'('#10#250#128

-  +#195'''-'#242#229'/}M}'#238'7'#255#174#170'u'#215#138''#28#225#190'.}N<'#29

-  +#232#231#2#31'%'#157'}8'#214#159#234'L?'#168#252#0#255'L'#171#254#139#169#145

-  +#254#180#158#234#148#223#221#219#223#161#253'>'#251#6'x'#138'/'#237#248#203

-  +'otw'#223'i'#199'3'#132#249#172#227#143#176#129#164#159'-H|'#241#252#179#227

-  +#143'0'#180'3'#164#177#183#183'7{'#214#134#31'O'#27#199'B'#0'J*'#5#209'6'#140

-  +#206#187#213'h4`'#10'\VZ'#250#223#148#245#165'E'#26'\!'#18#248'R'#166#194#136

-  +'I'#128#147'~"'#235#15#136#219#29#173#1#136'C'#16#25#130'a='#226#181'o'#138

-  +#134'l'#142#128#152#3'b'#18#148#148#128'e'#191#242#164#177#246#148'+'#253'?'

-  +#255#225'o'#169#31#253#193#191'='#225#147'z1'#6#188#249#31#251#139']'#253

-  +#204'_'#254#155#207'>'#163#239#147#158#13#145#250#182#194#207#218#252#162#250

-  +'/t'#184#143#19'z&s'#209#0'D'#253#167#237'>'#236#254#253#251#252#154'<Ix'#14

-  ,'@'#152#14'k'#141#209#189'K'#205#193'=Q'#253'M'#216#15#234#253#142'8'#254'P'

-  +#241#7#231#31'&'#250#216'B'#193#207'h4'#154'<k'#187#175#247#251#243#159#203

-  +#231#154'~'#1#180']CX'#144'~'#216':'#28#130#240#3#208#177#155#178#189'1X4_'

-  +#185'7'#232'}'#6#0'F'#251'0'#246#7#144#132#143#218#171'j'#229#198#167'8"'#192

-  +#154#0#231#6#196#188#246'k'#210'@'#132'5'#6#9#15'J~'#128#206#24#244#139'l'

-  +#193'\'#251#7'Nk'#210#209#163#14'<\'#191#251'W>'#174#230#163#254'i'#159#202

-  +#185#25'+/T}'#246#27'G]'#255#220'W'#223#215#251#151#206'>g5'#254#188'P'#251

-  +#179'\l~'#29#235#207#165#188'W'#199#251#181#218#159#10#248#19'&'#130#153#26

-  +'o'#223'S'#253#135'?`'#211' [,'#172#215#191#17#206#251'/'#147#221'/'#223#4

-  +#213#31'&'#192#152#22#132#130#224#12#180#170'?-'#152#231#15#14#193#145#211

-  +#236#243#185'I'#251#155#143'iX'#135' &'#20'%'#192'cZ'#241'Mt'#14#130'/'#0

-  +#166#0#189#230#10'-k'#15'G'#189#183'v'''#205#151#141'w_;'#250'b'#213'\'#135

-  +'?'#224'u'#21#213'$1'#168#17'["'#240'M[q'#19#30#180'='#5'i'#9#138#217#134'r'

-  +#201#21'8'#15'n'#129'{'#255#231#191#169#255#250';'#191'z'#218#167'q'#166'Gs'

-  +#253#138'z'#233#203'_S?'#243'+'#191#165#234#171#155#207#245#179'M#'#15'//'#8

-  +#0#21'}'#153#16#0#219#253#146#230#11#149#30'E> '#0#150#254#0#255'Tk'#1#179

-  +#193#158#218#187#253#135#244#191#194#238'W'#176#251#189'd'#241#234#202#246

-  +#247'B'#178#255#149#168#254#180'L'#137#4'0'#203'/:'#253#222#147'f'#159'w$'#1

-  +'h'#219'8'#254#158'W'#216#175':'#142#149#0#224#16#252#214#183#190#197'MC'#136

-  +#201'Z'#244#195'`'#152']'#22'-'#0'$pM!J'#160#188#149'w'#247#214#190'0Kk=eH'#0

-  +#210#157'H'#160's'#249'5'#186#233'/IRP'#205'j'#1#8#13#194#28#240'b'#157'#'

-  +#160#195#131#18'"4'#25#131#158''''#10#192#249#9#19#254#254#223#254'e'#245#240

-  +'";'#176'4'#26'kW'#212#205#159#255#154'z'#235#24'@_'#26#206#196#157'6'#209'G'

-  +'&'#241#204#165#161'g.'#177'~V'#253#167#218#238'O'#5#252')'#183#249#30#178

-  +#211'o1'#30'j'#233#159'&'#186'4'#152#237#254#189'w'#218#209#180'oTz>9'#230

-  +'Ok4'#250#228'f'#31' '#0#244#249#203#178#12#190#128#253#227'p'#252#185#227

-  +#184'aa'#29#130#244#163'j'#164#1'tMn'#0'H@'#136#0#29#133#215#147'<'#218#248

-  +#233#222#250#23'3'#130#189'q'#10#194#31#0'{'#191's'#245#227#170#209#187','

-  +#192#143#181'?'#192'5'#7'b]>\'#20#14'A'#11#208'D'#144#151#10#136#156#159#251

-  +#164'_~'#18#254#129'C'#190#31's'#1#254#222#175'A'#141#30#221'9'#129#147'8'

-  +#187#163#177'vYK'#250'_'#251'['#31#12#244'O{'#194#141#202#159#231#142#237'o'

-  +#192#159#217#226#158'\'#178#252'8'#140'7[X'#192'C'#242#167'Sc'#255#143#9#252

-  +''#164'f'#163'}Q'#253#19#235'7Xo'#142#238'm6'#251#144#240#244'Hz'#166#216'g'

-  +#12#144'#'#227'O'#21#210#255'.'#225#4#181#254#187#180' '#230'?'#251#250#215

-  +#191#158'>O'#199#223#179'\'#158#231#241#249#236#16#188'v'#237'ZD?'#166'I?'

-  +#142'M'#1':~]'#242#2#224#16#132'V'#176#218#159'7n'#222#31#174'|'#138#253#1

-  +#220#16'$'#212#243#6#196#177#234'^{K'#213'z'#235'L'#0'69'#168')'#249#1'5m'#10

-  +#232'D'#161'P'#207'1`'#252#2#158'8'#6#13#1'x:'#195#235','#155#4#217'|'#170

-  +#254#227'o|^M'#182#31#156#246#169#156#232#208#160#255#243#234#19#4'zl'#31#247

-  +'0A'#23#207#216#251#210#196#147#255'!'#192'5Y~'#185#128#31#146'?'#19#181'!$'

-  +#144'Ng'#236#245#239#223#253#174#154#13'w4'#248#209#223#15#170''#134'x?'#236

-  +#254#157#183#229#27'!'#253#145#245'7'#161#239#178'1I'#246'A'#143'?T'#254'm#'

-  +#227#143#204#229#217'q8'#254#220'q'#18'0`-'#128#214#193#189'{'#247'0'#181'x'

-  +#11#141'C'#148#6#189'!'#1#248#5'6i{e{'#210'}'#227#241#164#253#154'.'#24#242

-  +#25#208' '#129#176#214'P'#221#171'oq'#255#128'@'#204#129#192#248#3#140'S'#208

-  +'!'#1#27#29'p'#27#140'B#p'#253#1'v6'#226#167'\'#134#147'L)'#150's'#193#148

-  +#224#223#250#245#159'S'#211#189#173#147#251#238#19#30'A\S'#221#151'^W'#215

-  +#190#240#139#234#245#191#240#13#213'XN'#160'?'#202#253't<'#253'&'#191'7wm'

-  +#254#212'H'#254#2#252#25#131'_'''#251'X'#181#31#14#192#217'L'#245#31'|_M'#247

-  +#183#216'<p'#237#254'0XL'#9#252'?'#8#189'tQI'#248#225'R_'#153#225#23#141'>'

-  +#239#186#170''#179#217#156#16'^'#22#199#225#248'+]'#138#227#248#208'e'#223

-  +'cL'#1#218#174#199'q'#220'Y,'#22'k'#196'vp'#2#26#18#192#246#6#189#180#247'`'

-  +#216'{s'#214#188'a'#253#1'a'#164'g'#24#170#183'T'#239#198'[*nuu'#211#16'h'#2

-  +#13#153'h'#148#246#217#31'P3'#230'@A'#2'z'#242'Q_'#151#17'{^'#209'r</'#251#6

-  +#242#202#229#240'N'#185#167'XBv'#228''#250#198#159'T'#227#199#247'O'#245'<'

-  +#158#215#128'F'#215#190#246#138#186#242#179'Z}'#228'k'#191#161'z/'#244#216

-  +#190#235'i'#247#210#230#243#155'F'#30#166#176'G'#156'}'#214#219#15#135'_R'

-  +#128#159#157'~'#211'"'#236#135#253#193#131#183#9#252#247#11#143'?'#252#4'Y'

-  +#170'B/'#153#223'$'#240#199'~2sC~2'#193#7#167#251'*'#173#250#223#145'\'#255

-  +'GF'#245''''#201#143#196#160#244'8'#165#191#190'.''3'#216#20#248#202'W'#190

-  +#226#147#25#16#175#172#172'4'#8#252']'#250#161#27'2'#185'('#204#0#16#1'G'#5#8

-  +#176#157#187#131#149'O'#14#231#141'K'#202')'#29#230#28#129'f['#245#174''#146

-  +#236#255#150#245#9'0'#17#196#146#31' '#154#128'I'#27'6'#181#3#158#211'_'#176

-  +#152's@'#251#8#220'^'#131'O'#157#233#229#20#198'w'#254#233'7'#213';'#255#254

-  ,#159')'#253#12#157#159#1#226'mn^W'#151'>'#253'e'#245#234'/'#253'U'#181#249

-  +#214#23#143#241#203#158#208#198'/'#175#188#206'H{'''#179'O'#247#242'3'#224

-  +#207'X'#234'g'#21#201#207#241'~'#2'}'#6'{_'#136#0#165#189#147#157#247#24#252

-  +#186#181#151#158#212#211#207#210#228#229#149#237#31#212#130#197#196#181#251

-  +#149#14#249#193#238#223#18#213#31's'#252'q'#200#143#182'w'#8#15#195#231'Y'

-  +#236's'#132#203'vb'#195#166#9'_'#191'~'#29#154'@'#11#181#2#240#7#208#15#191

-  +'*aA'#204'1'#8'=p5'#247#252#206#157#254#234#167#198'I}'#213#23#167#160#23#232

-  +','#192#184#217'c'#18#8#26'uM'#0'l'#18'D'#146')'#136#16#161'4'#18'A'#152#208

-  +#180#21#243#29#18'0'#221#133'<!'#0#229#2#255'9D'#12#142'a6'#227#225#253#159

-  +#168#255#254#219#191#172#134#247'~z'#130#183#236#217#6#8#186#190#178#169#214

-  +'>'#254#179#234#213'?'#251'+'#234#218#159#248#165#163#191#249#176'k'#246'<'

-  +#158'PG'#218'kG'#191#201#231#207'm#'#143#194#230'/'#210'{y'#153#27#169#191

-  +#208#192#135#148#135#250'O'#132'0'#217#190#163'F[?"'#146#152'Ks'#15']'#223

-  +#143'?7'#187'{o7'#194#217#208#177#251#231#244'l'#163#197#23#18'='#182'%'#215

-  +#159#193'O8'#128#25#176#141#132#159#157#157#157#169'x'#253#13#248'_'#24#2#224

-  +#239's'#19#132#8#248#173'$I'#216#31'@'#11'B'#130#208#4#216#31'@'#235#149'\'

-  +#249#157'w'#247#214'?='#205#162#14#146#132#152#4#184'7'#0#145'@gMu'#174#189

-  +'%'#229#194#162#1#152#252#0'h'#1#146'-X8'#6#203#154'@a'#18#248#197#228'#N1'

-  +#209'Y'#237'='#248#255#254#205'?R?'#248'w'#255'DMw'#30#158#234'y'#4#245#166

-  +'jn\S+'#175'}'#130'$'#252#159'R7'#190#244#231'T}'#253#202'i_'#158#202#200#203

-  +#210'_Rz='#227#236#147'r^'#6#191#163#246'['#240#139#202#15#224#179'z'#207'*'

-  +#191#246#1#204'v'#31#168#193#195#183#233'usn'#238#193#239'Cyo'#154#229'W'#187

-  +'{?'#236#196#147#190#168'l'#214#238#23#167#223'.'#236'~ZX'#245#135#211#143'0'

-  +#240#152#222#187'O'#166#241#4#9'?'#199#233#245#175#142#211'x'#202'}'#152#2

-  +#180#6#9'X'#0#217'?'#151#29'S'#0#26#1#252#1#221'$'#243#187#239#246#215'?'

-  +#179#200#163#134'o'#204#1#216#247'$'#233#153#4#174'|'#156'H'#160'nA'#207'f'

-  +#128'h'#4'L'#2#145'&'#1#21#233#168#2#147'@h'#8'@r'#5'|'#129';'#214'2)'#225'Y'

-  +#159#166'|'#240#222#219#234#143#255#249#223'W'#15#254#247#31#208'C99'#190'/'

-  +#162'k'#17#183'{'#170'u'#245'e'#181#250#209#207#168'+'#159#251#170#186#250

-  +#249#175#210#245#175#159#246'%x'#250'03L'#219#130#30'c'#231#23#253#251#140

-  +#167#223'H~V'#225#231'I'#1'x'#6#255#156#155'{'#178'&@'#255#155#238#222'U'#163

-  +'G'#210#206'{Q'#168#253' '#146#203#173'}L'#231#181#131'/q'#156'~H'#245'E'#188

-  +''#15#133'='#180#141'F'#144'P'#251#225#244'{D'#207#245#222'I'#218#253#238'8'

-  +#141'G|'#169'?@f'#22'b'#167#160#210'Z'#0#182#215#233#1#236#204#147'`'#229#189

-  +#254#250#167#19#21#198'<w`'#16'J'#235#240'HE'#173#158#234'^}'#147#204#129#166

-  +#206#12't4'#128#192#201#22#132#230#224#217#254#130'E'#132#128#9'AL'#129'R'

-  +#171'1[S'#228'-'#255#5#238'8'#229#26#158'{'#255#235'?'#171';'#223#254#15'j'

-  +#239'G'#255'W'#141#31#222'V'#139#241#224#169#14#11#238#197#16#197','#201#163

-  +'v'#151'C'#172#245#181#203#170'y'#233#6#131#189's'#253'#'#170#251#202'Gy'#251

-  +#204#141#167'\'#255#220#252#205#197#225'o[w)V'#249'mr'#15'@+'#9'>'#28#234'#p'

-  +#231'R'#217#151'I'#184#15#142'>'#14#251#137#31'`'#188#253#30'-'#239'jR'#224

-  +'i'#188'S'#142#22#0#252#27#205#193#237#245#198#240#145#11'~z'#182#140#221#15

-  +#240'o'#153'J?z'#230#239#201'4'#223#232#240#131#6' '#199#150#237#247','#151

-  +#242#196#190#215#245#7#208#197'h'#18#248'{'#164#10'm'#18#192#175'Hx'#144#179

-  +#4'iY'#3#9#204#210'x'#237#246#254#250'''3'#207#15'Yr#<'#24'j'#18#8#27'm'#213

-  +'!'#18#136#154#29#2'~h'#181#0#228#8'x'#236#20',H'#128#181#129'J'#152#208#212

-  +#17#24#18'`'#231' t'#148#220#153#144#164'`'#132#234#147#246#244'+y'#10#4'1'

-  +#219'{'#172#230#253#29'5'#31#236#210'zO-'#134#251'\9'#217'}'#249#13#213#185

-  +#249#250#217#149#224'G'#189#142'K'#239'C'#17#222's[u'#219#16#159#11#254#170

-  +#212#151#220#254'L'#154'z'#148#9'@K'#255#209#214#143#213'd'#239#30#189'n'#206

-  +#145#1'~'#159#228#248#175#214'F'#15#174#180#251'w'#243#2#252#200#244#131#221

-  +#15#240#195#238',!'#191'{R'#223#15#27'n{:'#157#14#136#0#160#194'%''e'#247#31

-  +#245'r'#31#251'wW'#243#3#200#28'@3'#209'Md'#10'*'#237#16#196#26'$'#176'J m'

-  +#141#23#181#205#187#253#213#183#136#4#2'm'#207#11#9'p'#4#160#201#230'@D'#234

-  +#170#213#4#156#181#23#11#17#24'M'#128'L'#2#21'j'#191#130'2s'#17#26's'#192'M'

-  +#30'2UEn'#163#145'3l'#26'|'#232'F^'#10#226#8#224'M'#21#159#210#158'}'#137#239

-  +#187'i'#189#218#230#215#128#207#13#248#173#228'_0'#200#217#241'7'#211#161#190

-  +#225#163#183#213#172#255'H'#146'|'#140#218#175'k'#251#187#209'd'#235'zw'#239

-  +'=m]d'#153'x'#252#145#236#3#240'#'#151'['#233'y'#253#24#252#216'F'#178#207

-  +'I'#198#251#15#27#167#253'('#151'R'#133'I'#11'h#'#25#136#180#128'K'#208#4'h'

-  +#31'Z'#0'H'#0#164#176'B@lM'#147'x'#237'N'#245#19#169#23#198#218#179'/>'#1

-  +#174#12#172#17#9'|L'#197#157'uM'#10#198#15'`'#192'/m'#198'=!'#2'_z'#10'('#19

-  +'%'#16#231#160'2'#230#128#239#21#4'`'#174#214#139'>]'#241'y'#25'.'#234']sGl|'

-  +'O'#202'wm'#3#143#170#179#207'8'#250#0'f'#212#243'/'#28#240#27#231#31#175#167

-  ,'j'#240#240#7#164'ImK'#140'?'#209#196#145'j'#240#175#213'F'#247'/'#183#145

-  +#226'k'#193#159'I'#154'/g'#250')]'#226#251'H'#230#245#131#228#199#164#30#143

-  +'1'#171#207'|>'#31#31'w'#170#239'Q.'#227'i'#223'F'#207'8'#5#209'@'#4']'#132

-  +#208'P'#212#9#15'"O'#224#138#201#20#4#9'$y'#220#189#189#191#250#214'BE'#245

-  +#18#9#136's'#176's'#249#13#21'w/'#177'/'#192'3'#181#2#177#244#16#176'&A'#164

-  +#253#2'b'#14#168#208'/'#146#134#140'i '#13'F'#172'F`'#175#152#163#10#156#159

-  +':'#163#23'f'#148#146'xl'#171'.}'#140#193#159'i'#201'oc'#251#169'n'#226#1#240

-  +'+'''#204#151'K'#190'>'#171#244'3'#241#250#211#177'\B'#201't'#162#6#247#191

-  +#175#18#228#246'''s'#157#21#232#128''#163'1'#184#189#217#28'>'#210#138'F'

-  +#150';'#146#31'*={'#252#149#238#237#7#240#223#151'p'#223'cH'#254#197'b1'#186

-  +'v'#237#26#156#131''''#234#244#171#142#179#240#236'Z'#18#232't:1'#177'b'#157

-  +'.T'#27#149#131#232'$'#4#2'0$ '#173#198'A'#2#205'L'#133#237#219#253#181'7gY'

-  +#220#214'}'#0#196#174#143't)qk'#243#150'j'#172'^'#215#146#222#1#189#217'f-'

-  +#160#166#147#139'8Dh'#219#142#7#133's0'#240#10#141#192'I rs'#137#205'f~'#224

-  +'''='#225#10'?'#211#173'>'#175']'#130#142#248'h='#245#250','#191#178#185#211

-  +#151#223#5#191#246#238#235#248#190'J'#181#202#175#4#176#156#155#159':'#246

-  +#254'\'#131'?'#183'j'#191'd'#242#17#25'$'#147#161#234#223#255#158'Ji]H~'#157

-  +#225#167#210','#191#220#217#255#201'j<'#217#21#193'o'#193#175#138#190'~'#166

-  +#190#255#1#164#191#132#254#30#163#190#159#198#168#209'h'#204#143';'#207#255

-  +'9'#222#165#227'?'#15'S:'#140#162#161#241'x'#140#134#162#29#169#25#184'd4'#1

-  +#165#251#7#160#177'H'#143#174'X'#147#16#218#188#211'_'#251#216'8'#173#173'x2'

-  +#239#160#206#254#211#18#191#190'rU'#181#214'oiM'#192#250#1'B'#157',$d'#224'I'

-  +'%'#161#138#138#190#2#166#152'Hq'#132#192#244#23#240#203#145#2#167#243'P.'

-  +#161#195#234#21#245#202''#158'q'#228'O'#220'='#179#195'{'#234#129#167#255

-  +#238'|'#201#207#21#144#27#129#175#14#11#237'9'#133'<*-b'#251#197#162#129#207

-  +#210''#190#144'p'#223'B'#180#129#5#255'o>'#216'a'#155'?'#157#142'u'''#31'Z'

-  +#148#128#223'#'#241''#181#187#247#163'n8'#27','#3#191#210#225'>'#128#31'-'

-  +#188#31#144'&'#203#14'?'#228#248'#'#220'G'#175#27#161#200'G'#157#146#211#239

-  +#131#222#157'c='#23#248#3'~'#252#227#31#251'h%F'#154'@'#195#228#8'`V!'#164#12

-  +#131#8'h'#141#164'!4'#26#237#210'Uk'#17#240#234#247#6'k'#175#15#23#245#13'c'

-  +#14'0'#17'DP'#241#145':'#220'Sm2'#9'PG'#224'K?'#193#2#252#142'6'#192#254#0'c'

-  +#14#232'm'#155'8Ti9f'#195#134#158#201'(tr'#207#171#253#7#204#173'=KW'#250','

-  +#143#202#245#202#157#166#156'%'#208#27#162#200#164'eW'#150#29'H'#231'Ui'#1'z'

-  +#227#237'O%'#212#151#207#231#142#195'O|'#0'D'#2#147#221#219'j'#178#253#158

-  +#222'w$?>'#223#167#15#184#222#219'{'#167#21#204#199'K'#192'ob'#253'h'#234#137

-  +'4_+'#249#233#153'}L'#160#223#165#231#23'5'#0' '#137'3'#1'~'#231'2'#159#153

-  +#193'$'#240#189#239'}/@x'#16'$@Z@W'#26#137#184#154#0'"'#3#235'('#28#162#27

-  +#129#18#227#248#254#160'wko'#222#188#234'['#245']k'#2#200#26#244#227#26#153#4

-  +#31'Q'#181#206#166#150#248#174#244#23#223#128#206#17'0'#249#2#129#205#30'TN'

-  +#6#161#237'@'#236#251#5#1'8m'#201#221#26#3#235#164#170#22#27#157#181'+~F'#134

-  +'Wr'#228#185#155#249#1#240'k'''#159'H'#251#180#144#250#165't^'#168#251#137

-  +#238#215#175#9'@<'#254#226#237'7'#26#0#19#0#166#233#154'N'#213#136#164#254'|'

-  +#180#171#227#251'IR'#2''#160#146#217'K'#221#221#31#214#131#197'T'#192'o'#28

-  +'~F'#242's'#129#15'r'#252#209#220#3#146#31#26#0#212#254'('#138'v'#7#131#193

-  +#136#180#218#19'M'#243'='#210'u?'#237#19'XvN&<'#184#181#181#21#211'Ek'#208#5

-  +'4$'#128'lA'#180#24#135')'#192'$@'#251']'#248#4#232'X'#237#209#168#243#210

-  +#246#180'}SO8bH@2'#0#137#8#234#189#171#170#185#241'*'#131#222's'#181#129'H'

-  +#147#129'''-'#199#149'q'#14'F'#21'm'#192'u'#16#154'z'#2'&'#3'eg)'#202#29'2p'

-  +#181#255#220'!'#133#15'f'#26#188'(C'#210'u\'#137#159#23#181#249#202'sT}'#199

-  +#201'W'#168#249'"'#245#197#214#231'2'#222#180'p'#242'1'#232#165#148#215'z'

-  +#252#141#234'/'#170'>'#219#254#201'B-'#250#187'j'#184#245#14#29#27#219#200

-  +#128#174#229#151#137';'#189#249#228#165#238#222#15#185#170#143#235#8'l'#168

-  +#143'S|'#149'd'#249')'#237#224'C'#184#15'R'#255#1#212'~'#178#249'w'#233#249

-  +#29'6'#155#205')i'#183#201'iz'#252#151#141#179#250#4#150'H'#160'Ac2'#153#160

-  +#145#8#155#3'B'#2' '#3#209#4'T'#151#128#136#190#131#181#253'Yc'#243#225#168

-  +#251'Z'#166#194'PKn'#169'!'#8'C1'#9#186#164#13#188#193#213#132#0'>'#28#129

-  +#158'D'#4#220#16#161#155'9'#168#156'Z'#2#229#18#129#231#23#179#22#251#198'I'

-  +#232'j'#3'E'#30'a'#238#148#25'z'#213#146#195#3#153'mg'#245#182#188#223#187

-  +#249#4''#134#233#191#167#156#198#28'|'#188#2'zP'#168#3'~'#211#173#135'U}'#9

-  +#239')'#167'O?'#188#245#249#28#251#11'+'#249's)'#238#201'%'#207'?'''#240#143

-  +'wn'#171')-'#153'y]*R_f'#238'iG'#147#237#171#173#253#219#129#151'&:'#151'(3e'

-  +#189#240#246'3'#248#149#206#242'CO?'#246#248#195#230'G'#168#15#224#167'eH'

-  +#166#236#20#177#254#179#6'~}'#205#207#238#176#133'C'#4#254#136'.b'#147'L'#130

-  +'.H@:'#10']6'#225'A%$'#128#16'!H`'#158#133#237#187#131#222#235#179#172#214

-  ,#230'y'#3#140'o'#192#132#10#163#186'j_zM'#197#221#205#2#248'f1'#4#16'W'#142#7

-  +#186#211#144#178#243#19#6'em@'#26#143#148'#'#6#158#149#244#172#29','#137#30

-  +#152#249#11#142'zG'#206#204#147#227#222#168#167#189' ?'#252#128'i'#197#229'y'

-  +#14#248'3'''#150#175#28#27#223'Q'#247'y'#31#182#190#27#222'K'#11#137#159#27

-  +#208''':'#181'7/'#145'@'#194#245#19'#'#146#250#243#225#142'h'#11':'#188#167

-  +#138#30'~'#217#165'F'#255#246'Zc'#244#152'S{%'#212#167'tz/O'#223'-s'#248'A'

-  +#237'7'#137'>'#15#207#19#248#143't'#239'N'#251#252#12#9#244#251#253#184#213

-  +'j5'#232#226#162#197#248#154#132#4#225#16#188#12#173'@i'#18#232'1'#9'('#175

-  +#158#7'~'#252'p'#208#189#185'?o]-'#146'|t'#184#15'&'#129'oL'#130#245'W'#8#236

-  +#177#205#9#240'L'#5'a'#172'5'#6#171#9'p'#10'qPT'#21#210':'#15#220')'#202#140

-  +'V'#224#21'D'#160#138#237'"JP'#172'K%'#200#206'f^9'#236'='#235#173'z'#158'w'

-  +#245#153#31#215#188#152'>'#219'='#149#188#188#145';/'#178#245#249'n'#18#143

-  +#177#243'MG^'#145#250'*u$'#191'c'#235#27'/?'#219#237#12#244'E'#161#254#207#11

-  +'R'#192#255#23#240#242'?'#254'!i'#1#19#177#247'u|'#223'|n'#228'/&'#215#218'{'

-  +'?i'#132's'#174#229#23'G'#4#236'}'#147#222';'#145'P'#31#171#253'J'#194'}'#178

-  +'F'#169#239#222'y'#0''#233#254#156#209#193#231'W%'#1'd'#12'"Y'#136'.'#178'!'

-  +#1#152#5#151'0'#247#128#210'y'#2'm'#186#212'u'#146#184#241'p'#222'X{0^'#185

-  +#149#145#236#215'@-'#155#4'H!'#6#9#196#157#13#201#7#144#136#128#1#191')$'#10

-  +'+$ '#11#147#138'h'#6#156'N,'#145#130'\'#162#6'V'#27'x'#26#17'8'#221#137'rG;'

-  +#240#202'^'#196''''#220#177#147#184#149#249#225#135'+'#167'i'#140'{'#175#244

-  +'6'#167'<'#23#0'WO'#0'~^'#150#248#202#22#239'8N>c'#239#187'v'#191#149#252#139

-  +#18#240'Y'#19#152'N'#213'd'#251#167'j6'#220'b'#130'@'#179#15'+'#241'3=__'''

-  +#154'<'#190#210#234#223'!'#149'?'#205'm'#147'@]'#210#235'd'#248#245#165#178

-  +#15#222#254'G'#146#215#255'H4'#129'}8'#252'666'#206'<'#248#237#253'9'#227#195

-  +#146#128#18#159#128#146'2b"'#130#21'h'#2#146'%h'#178#5'9Y'#136'$r'#155#192

-  +#211#160';'#23'''Y'#212#184';'#236#189'6Kk'#29'S'#0'd'#27#140#8#184#163#214

-  +#26#19'APo'#21#17#1#167#138#208's'#251#10'<'#137#8#220#148'b'#153#194#220#134

-  +#14#205#196'%'#165')'#204#202'k7'#132'h'#230'4'#176#135#14#132#20#143'n:<'

-  +#151'q'#152'*o'#156'v'#246#144#145#236#230''#249#146#216#189'~'#157#14#227

-  +'I'#230#158'!'#2'k'#227'/Q'#247'S'#145#248#2'|'#181#208#149'|J'#236'w'#215

-  +#222'7'#206'?E'#251#147#254'=5'#221#189#173#19''#210'%*?}'#209#229#198#224

-  +#189#149#198'h'#199#203'MI'#143#14#243')'#1'?'#173#199#180#223#151#154'~4k'

-  +#132#196'GE'#31#182#183'I('#161#145'''{'#251#201'\=s'#14#191'e'#227'<'#16#128

-  +'=OC'#2#251#251#251#17']'#228':]x'#174#29#136#162'h'#157#246'a'#6'\'#18''''

-  +#225#134#212#14'th'#27'}'#4'b'#210#0#162'G'#195#206'K{U'#147' '#212#201'C'

-  +#138#171#11'#U'#235']S'#245#181#27#156'M'#200#14'@'#199#4#0#1'(''Dh'#136#192

-  +#148#24'+'#153#181'X'#249#229#198'#'#182#21'Y%'#179'P'#231#15'-'''#3#207#209

-  +#255#139#222#4#166#151#161'{UN'#136#4#170#143#176#19#182'+m'#229#5'Y'#149'Cx'

-  +'N'#165#158#27#198'3N='''#150'oA'#159#154'm'#0'>#b'#0#160'u'#17#143'Z'#20#13

-  +';'#173#199'?IJ>'#0#132#0#147#209#158#154#236#252'D-&'#131'B'#213#135#169#128

-  +#207'pU'#254#238#238'O'#234'^2'#245't'#229'@'#213#211'?'#147#22#222#168#234

-  +#219'q'#193#15'O'#24#134#232#235#223'_,'#22#227#25#141#243#2'~'#140#243'B'#0

-  +#246'\'#221'<'#1'8'#252#232#194#183#232'p'#143'.'#254#186'8'#7'A'#4#156','

-  +#132'V'#227'$'#129';$u'#27#180'_#'#240'E'#131'i}'#237#193#164#247'j'#150#235

-  +#249#7#180#147'0`'#21#222#244#16#132'Y'#208'X{'#133#139#138#188#208#137#6#200

-  +#220#3#134#8'4q'#4#162#29#136'&`{'#13'8]'#137#131#138#163'P'#8#161'hF"'#19

-  +#152#28'H76'#161#195#162','#185#240#15'T='#3'G'#200'1'#248#160'%'#203'y^yi'

-  +#25#240'z'#229'4'#218'TE|'#159'%'#189#153'l'#195#190#230#160#154#207#246'~'

-  +#154#149#218'r+'#150#250#153#6#173#0'_'#137'='#175#164#131#143'2'#4#144'h'

-  +#239':''u'#231']5'#239'?'#210#173#186'L'#26'p'#146#10#209'h'#149#31#149'|'

-  +#151'['#253#187'P'#249'9'#188#143#254'}'#160'!m'#239#219#24#191#1#191#210'e'

-  +#189#15'e'#198#222#199'R'#207'? '#1'4>K'#25'~G'#29#231#137#0#236'9'#155#180

-  +'a'#218#142#26#141'F'#13#17#2#164#7#27#231#160'8'#5'/'#217#218#1#29'&'#132's'

-  +#176#6#147' Sa'#252'h'#220'~'#169'?onz@'#166#137#20#216'f!Z'#226#195',h'#192

-  +', B'#176'R'#223#1#188'Bd '#22'B'#16'M@9'#25#132#214'I'#24'>A+8'#204'GP'#154

-  +#242#188#186'v:'#26'['#13#193#161#130#165#190#130'CB'#143'O'#200#187#183#160

-  +'.%'#224#23#26'H.'#234'})Q'#199'%'#0#215#147'/'#158'}'#163#226#219'p'#158'q'

-  +#240'Y'#208#167'b'#231';'#158'}l'#11#192#181#202#175#215':'#175#223'8'#2'%vO'

-  +#235#217#222'}5'#222#131#186'?'#211#199#229'3y'#157#167'"'#245#147#201#149

-  +#230#254#237'f<'#27'z'#26#247'8'#15#146#250'%{'#223'&'#248#208#178'C'#210#254

-  +'1'#9#153'G'#210#213'g'#155#128#191'K'#160#31#160#149#23#13#152#8#231#10#252

-  +'K'#158#138's3,'#9#208#197#15'k4'#200#12'h"B'#16#4#193#170#227#23#128'F'#0#18

-  +'X'#5'A B &A'#148#19#196''''#139#176#253'p'#178#242#202','#141'ZE'#235#240

-  +#162'('#200#147'9'#10'k'#221'+'#170#182'rM'#5'h'#162#17#154'y'#8#3#199#28'pM'

-  ,#3#9#17#134'RK`'#10#140'l'#14'A Z'#129#201'$t'#218#146'U'#137#160'j*'#148#202

-  +#146'+'#21#137#21'G'#162';'#242''''#146'A'#165#216'f'#217'c'#235'Jw'#222#168

-  +#172'e;7'#137'<'#153#27#190'3'#137'<'#142#164'wc'#249#2'|'#29#203'/'#192#175

-  +#137#192#9#241#217't^'#13'r'#237#3'H'#10''' -'#139#225#14#219#249#9#171#251

-  +#186'I'#167#146'L>'#179#246#232#207'j}t'#189'1x'#228#177#180#207'3'#147#214

-  +#171'$'#190'O'#203'\j'#249#135#226#233#135's'#15#210#30#249#252'['#178#191'O'

-  +#207#220#160#223#239'O'#187#221#238#156#8' =o'#224#175'>'#9#231'm'#148'J'#137

-  +'I'#19#136'%u'#152#157#131't'#140#181#1#144#0#250#11#210'z'#141#253#2#202'k#'

-  +'}'#152#182'czDc rw'#218#186#180'=m_'#207'H'#164#235#226#31#237'$'#132'fP'

-  +#168#249#145#170#181'/'#169#6#17#129#31'7'#180#244'7j'#191'k'#14#24'@'#224

-  +#164#17#187#166#129#201'!p'#29#133#198'<'#176#4#224'W'#18#139#180'G'#192#246

-  +'.'#180'='#11#141'iP'#152#3'n'#194'Q'#217';'#239#29'XU"s'#202'Az'#217#181#144

-  +#187#161'='#137#215#231'y'#197#214#151'B'#29#163#226'gy'#145#167'oSu'#139')'

-  +#183'\'#201'_H'#253#20'M5'#197'VO-'#9#184'a'#190#220#170#251#198'!H'#18''

-  +#184#173#166'{wU:'#27#22'f'#129#11'~'#241')'#180#162#217'.I'#253';'#152#156

-  +#211'C/`}n'#172#242'+'#221#187#207#228#244#143#140#167#159#182#183#197#219

-  +#191'Ej'#254'6'#9#24'h'#2'}'#147#215#239#244#241'3'#192'?7'#224'w'#30#135's;'

-  +','#9#16#1#4#8#19#18#27#195'9'#216#154#205'f=h'#3#244''#244#26'D'#171'1h'#2

-  +#240#11#160'r'#144#211#135#17'% '#192'D'#244#17'A'#146#249#241#163'I'#247#230

-  +'`'#214'X'#247'l'#241'O'#1'^?0 '#142#184#166#0#206'B'#244#211'3'#166#0#167#13

-  +'['#208#23#161'A'#27'!'#136#180#244#207#131#224'@wb'#235'#p*'#15#11'"P:'#164

-  +'h'#211#139#165']'#153'*L'#4#151#24#150'^"'#175'r'#197#170#26#192'!'#14#190

-  +#18#15#184#245#246#216#204#242#146#189'o5'#0'7W'#223#128#221'z'#244#141#202

-  +'/'#246'~'#166#167#219'V'#226#228#179#128#183#29'{'#202#224#183#197'='#236

-  +#253'_'#168#217'`K'#205#246#239#17#240#199#146#16#148'X'#147#1#251'J:'#246'D'

-  +'^2'#187#212#26#220'n'#215#166'}'#248#28'<'#143'U~V'#252#233#146#177#163'Oz'

-  +#247'Me'#178'Nk'#239'C'#237#151#153'{9'#190#15#149#159#20#206'1b'#252#245'z'

-  +#29#164'a'#234#249#237'U:O'#227#188#19#128#249#13'H'#24#194#154'#'#4'p'#14#18

-  +#1'4E'#27'X5'#190#1'!'#1'6'#9'h'#191#7#191#0'm'#195'A'#8'm'#128#222#231#251

-  +#163'$'#238'>'#28'uo.'#178#168#233#154#5#202'I+V'#226''''#136#219#27#170#222

-  +#187#174#130'F'#235#160#212'7'#170#191'C'#4#134'L'#148'K'#2#2'z'#229'U'#157

-  +#133#14')x'#166#10'Q9~'#130'J7cq$'#30#152#241#232#176#154#3'[m'#231#188#196

-  +'m'#180'a6'#189'B+'#240#158#2'zW'#213#247'*'#222'|'#29#215#207#172'Df'#7'`R'

-  +#168#251#158#1'}'#226'H~'#163#13'd'#134#8'2'#142#223#207#6#143#24#248#217'bb'

-  +#237'~'#227#216#203'\u'#159#132#252'J}'#252'p'#179'9x'#224#209#142#199#165

-  +#130#224#29#218'f%'#198':'#250'f'#210#187#207't'#238#133#167#223#130#31#19'v'

-  +#160#137#7#254#143#254'}'#163#209'h^'#241#244'+u'#14#193''#200#147'q.'#135

-  +#141#16#160#156#248#214#173'['#225#214#214'V'#141#14#213#137#173#17'*'#236

-  +#145#250#134'b'#162'u''u'#24#251#200'#hC'#27#160#237#26#251#6'</'#204'r'#143

-  +#204#130#246#229#221'Y'#243'J'#154#135#145'1'#11#236':'#8#172#175#0'N'#191

-  +#184#181#166#234'+D'#4#245'N'#17#1'p{'#10#28#0#190#16#131#235#24'tk'#11'l'

-  +#152'R'#230'3t'#29#134'Nv!k'#7'jIU'#162'\'#17#175#148'\T'#190#213#182#143'A'

-  +#245'B'#26#233'_r'#240')I'#218#145'}3}'#182#19#194#179#224#183#251#6#240'N"O'

-  +#213#203'/'#241'}'#227#236'c'#144#27#147'@r'#251#141'6'#144'%s'#246#232'O'#25

-  +#248'3'#171#254'g'#198#163#239'F'#17'h'#187'I'#234#254#229'F'#255'^'#28#192

-  +'3O'#146#158#5'>'#199#245'3'#201#231#183'R'#159#22#19#226'c'#149#31#160#199#2

-  +#149'_'#8#1#29'|'#198#244#182')'#217#253' '#140#132#180#206#236'<'#132#249

-  +#142#4#156#23'h'#176's'#240#219#223#254'6'#251#5#232#166'E'#4#234':'#28#132

-  +'259'#155#4'XD'#27'0$'#208#165#183#146'>'#175#26#4#167#24'f'#1#129'='#160#199

-  +'%'#220#157#183'6'#247'&'#173#203#137#10#226#3'D'#224#23#246'>4'#130#176#209

-  +'!'#173'`'#147#8'a]'#249'a,m'#198#140#3#240'0'#167#224'!'#5'F'#146'3'#224#149

-  +'|'#3#166#23'A1'#169#137'%'#2#153#224#196'LuV'#10#18#10'7'#148#156#132#14#3

-  +'xF'#210'Wd'#153#145#246#7#28'{N'#252#222#149#252#182'B'#207#237#200'c'#192

-  +'/'#132#160'\'#167#159#149#242#142#211#207#233#224#131#255'-'#166#251'j1'#220

-  +#162'e'#155'I w'#205#128','#147#150'_'#236#220#195'~'#222#138'g'#187#27#141

-  +#193#131'Z'#184#152#150#212'}'#199#214#151#182']'#166'c/l'#249'}i'#226#193

-  +#158'~Z?Fl'#159#164#252'>='''#3'2/'#199#244','#205'{'#189#30#192'n'#237#253

-  +#165#128'9'#237#19'8'#166#223'T2'#9'h N'#211'diO'#128#167#155'j'#137'@'#204#3

-  +#236#19#9#168#14#1#174'A'#251'u:'#30#25#179#128'@'#21#236#205'Z'#27#187#227

-  +#230#229#133#10#235#218'F'#247#203#211#141#17#1'('#223#183'ZA'#173#177#170'"'

-  +'"'#131#176#185#162#163#4#230#181#142'3'#208'H~'#255'0-'#192#152#8'&'#147#208

-  +#248#7#248#23'V2'#12#221#28#130#220#137#12#8#27#152#196#220#131#209#130'Jz'

-  +#174#167#172'j/'#255'V'#226#245'+Iyc'#227#27'{'#223'4'#227#176'Z'#128#1#186

-  +#172#165#192#166't'#220'M'#237'u'#215'('#203#157#13#31'3'#240'S'#132#242'2'''

-  ,']W'#166#220#206'e'#209#192'O'#243'vm'#182#189#217#24'>'#140'|H|'#28#23#191

-  +'>'#171#251#220'%'#208#205#232#227#240#30#242#249'i'#189'/'#237#187#182#205

-  +'"'#206'?'#28#199'k'#166'F'#229''''#205'2;'#207#246#254'a`y'#17'G'#201'$@'

-  +#168#176#213'j'#197#2'lT'#12'v'#197#25#184'J'#140#143#232#0#22#248#5'D'#27'P'

-  +'-!'#2#152#5'a'#206'd'#128#174#225#190#191'7k'#174#237#142'[W'#230'y'#216#176

-  +#206':'#3'|'#207'H'#242#162#2#17#154'@'#212#222' '#173'`C'#5#141#142#227#252

-  +#11#184#231#160#231'8'#2#139#168'@u'#10'3'#207#2#222'F'#5#2#209#4'\'#237#192

-  +#151#144#161#149#252'U'#7'`%'#159#192#12#183#149'6'#239'+'#235#28#176#213'zF'

-  +#229'O'#171#234'~&fA'#230'T'#234#21#206'?~'#189#209#0'*a'#191#18#9','#230'j6'

-  +#218'f'#208''''#211'A'#161'!'#152'L@'#199'l0}'#0#16#210#235#198#147#199#27#4

-  +'|x'#246'M'#190#241#19#212'}k'#235#211#210#151#238'='#12'~'#186#207#144#254

-  +#216#222#15#130#128#19'{h'#153#145#244'_ '#196#247#162#168#252'K'#129#242#2

-  +#15'k'#18' J'#128#178'b'#186#185'54'#25'!R@'#227'Q'#152#5'+ '#1#133'Y'#137'5'

-  +#9'@'#27#232#209#210#17#179#160#238'{~Lk'#16'A'#8'"'#240#9'}'#251#179#250#234

-  +#206#164'ue'#150'!'#135#192#128#177#144#218#5#176#3'K'#10'a'#212'Tq'#7'Z'#193

-  +#154#10'j'#141'2'#184#157#210'bo'#9'!'#184#9'C'#133#25#224#21'~'#2'''R`o'#173

-  +'u'#11'H'#180#192'M$'#146'W'#185'~'#128#210#172#185#178'*'#194'}'#174#244#207

-  +#156#233#180#225#240#203'd'#178#205#138#183#191'*'#245#179#188#18#1'H'#212'b'

-  +#178#175#230'P'#241#199#187#156#216#147';6'#189#251'~K '#244'}>1B'#183'6f'

-  +#224#7#200#15'6'#192#231#138#221'2'#240#149#6'?'#128#143'N'#189'#'#201#229

-  +#223#23#240#195#185#135#25'y'#25#248#240#240'+M'#14#211'N'#167'3G'#3#143'7'

-  +#223'|'#243#133'R'#249#15#0#228#180'O'#224#132'~#'#155#4#208#6'h;Z]]'#141#160

-  +#13#200#140'D0'#11'z'#162#17#172#233#244'aM'#4#180'&'#18'Pm'#186#235#220'l'#4

-  +'&'#129'o'#136' '#131'F'#224'y'#195#164#209#219#153'4/O'#211'Z'#7#200#180#192

-  +'tH'#160'4'#231#128#152#0#232'I'#16#212'z*jt'#201#168#232#210'~'#173#210'[`I'

-  +#231'!G'#213'/'#171#254'x'#173#18#159#128#241#234'{'#182#152#168#8#4'x'#149

-  +#181'*7'#222'PE'#218#190#178#13'8'#157#146#221#204#13#241#21#234#190'w'#192

-  +#23'P'#201#233'7'#145#0#2'v2'#27#170#20'v'#253#164'O'#219#131'"'#219#207'x'

-  +#239#197#137#231#153#254'~v;'#231'p^'#167'6'#217'^'#175#143#182'|'#143'sy'

-  +#213'2'#224';'#237#185#173#147'O'#233#9':'#160#214#239#25#240#3#248'$'#12#246

-  +#200#222#135#3#16'z'#254#132'l'#253#217#139#230#232'{'#26'8>,'#227#128'6P'

-  +#171#213'bz^0G'#22#166'*G'#159#129#30#28#133#240#17#28#212#6'T'#211#152#5#4

-  +#180#18#17'(.'#25#11#163#253'yc}0k'#172#205#210#168'Y'#0#180#172#198#27#245

-  +'>'#247#202#196#0'B'#8#27'+*'#170'wT'#0'B'#128#19#209'!'#0#27#14#148#162#162

-  +'jc'#210#146#9#224'4"'#177'w'#185#218#140'D'#194'~n'#233'n)'#12#232'j'#2#198

-  +#9#232#168#251'n'#184'/'#207'+I?yA'#0'H'#206'I'#166'}'#150#244#201'tH'#255'J'

-  +#10''' '#155#20'i)4'#200'Q'#0'K&'#184#184'Y'#138#4#158#149#218'x'#167#25#209

-  +#135'i'#204#231#154#160#24#248#0'<'#235#252'b'#231'/*'#234#254'H'#226#250#198

-  +#214'G'#136'oW'#188#251'}'#186#215'C'#168#251#176#245'www'#23'd'#231'sl'#255

-  +#19#159#248'D'#254#162#131#223'yB>4'#163#164#13#192'7'#176#178#178#18#17#25

-  +#208'f'#204'}'#6'h'#1#216#225'(\1'#218#128#210#26#2#250#18'"'#164'h'#136#0'Y'

-  +#132'1a.'#160'g=dg!'#20'r'#210#11#166'i'#220#216#155'5'#215'G'#179#218'Z'#162

-  +#130#200'S'#5#25#20#146#220#169#7#240#141#243#207#28#15#148#31'7Y;'#8#226#22

-  +'}B'#131#9#130#29#141#202'5'#3#156#181#141#18#136#170#159#171#194'$'#200#171

-  +#209#1'U'#16#130#155#244#227'6'#222'4'#7#149#201#4#212'$'#192' u'#18'}'#12

-  +#232#177'dp'#188#211#146'/'#198'l'#199'c'#201#200'4/'#1#190#186'm'#181#137

-  +#204#250#20'`'#195'7'#195'y'#191'S'#159#236't'#163#233#158#199'I'#5'b'#143

-  +#232#188#131'Ll|l'#1#244#236#224's'#128#143#134#29'#'#153#153#135#195'{'#198

-  +#222'7'#14'>'#216#249't'#175#199't'#159#167'('#224'[b'#235';W'#224#197#29#31

-  +'6'#2#176#191#217'h'#3#155#155#155#254't:'#13'1S1'#252#3'0'#11#232#223'-'#178

-  +#7#225#12'D6'#225#138'8'#7'W'#196'y'#168#157#132#158#215#164#199#177'A'#159

-  +'V;@'#4#30#224#166'Q:'#152#215#187#253'Yc}'#148#196'+'#185#22#233'V'#133'7)'

-  +#191'E'#135#225#162#243#176'K'#8#185#152#0#164'x'#16#25#212#153#28#130#176

-  +#206')'#201'>'#214'd>'#148#10#139'J'#191#244'`'#219#242'"G'#160#236#237#247

-  +'Jf@^$'#7#25'3'#1#248'K'#209'H'#19'@'#159'p'#197']'#150#152#245#212#206#198

-  +#163#242#195#0#15#252'f%'#147#1#239'1'#251#181'`1&'#21''#167'GK'#232#193'!'

-  +#160'L'#237'0'#147'B'#154'iu'#223#177#241#171#18#223#216#249#3#1'?'#146'w'#0

-  +'~^'#144#194'K'#210#30#197';'#172#238'w'#187#221#217#214#214'V'#226'x'#248'_'

-  +'X['#255#176#241'a$'#128#210'o_f'#22#208#225#26#217#130#28'6$'#2#128'F'#208

-  +'s'#23#209#6#208'k'#160'm'#136#128#128#140'(CL'#251#16#211'Rc'#204#157#238'|'

-  +#180'*O'#9#206#251#179#198#234'h^'#235'M'#211#168#157#230#162#25#24#199#158

-  +'r'#10#131'J'#132'`'#156''#190'>ig'#187#152#199#16']'#144#27'*'#8'"q:'#134

-  +'tX"'#17#158#164'"'#243#233#232#181'/Z'#134#150#190#137#216#218'R)'#151'9'

-  +#139's'#28#146#28#18#158'_'#207#225'7'#7#196#182#196#183#240#1#184#0'W'#165

-  ,#130' 1#8T'#167#242#154#191#24#213#163#249#176'G*>Zn'#23#17#8#145#246'8'#3#2

-  +'>x'#18#206'}''o'#223#134#244#164'K'#207#184#2'|'#187#208'=d'#224'#'#172#135

-  +'L>"'#2#246#238'#'#149#151#8' '#251#176'I}w|'#152#9#192#140#3'f'#1'B'#244'p'

-  +#196#198'QH'#255'o'#9#216#187#244' '#245#184#150#128#136'@4'#2'v'#20'*'#204

-  +'T'#164#188':4'#2')4'#10#173'V'#224'#'#29'E'#251#10#176#13#208#18#9#212#137

-  +#12#186#147'E'#220#157'$q'#139#224#16#26'S'#161#232''''#232';'#205'D+'#246

-  +#190#152#2#150','#148#155'*l~'#150#252'@.'#30':$$'#232#216#255#182'EW'#169'1'

-  +#191#201#250'S'#165'\'#0'['#221#167#138#148'`'#6'u'#166'T'#158'/'#7'<'#214

-  +#181' '#25#215#131#249#176#25#207#251#237'h6'#242#149'8'#243'<61'#140'moA'

-  +#175't'#202'.'#24#133#213'|Y'#230#198#179'oT}'#19#211#151#5#158#254'>'#128'O'

-  +'D>'#140#162'h'#12#137'O'#199#167't'#23't<'#249'0'#170#251#203#198#5#1#20

-  +#195's'#205#130';w'#238#4#198'?'#128'9'#7#232'!B'#205'@'#203#241#19#244'$'

-  +#140#200'k'#248#7#20'"'#6'9W'#26#214#161#209#18#216#17'm@BQ'#24#248#30#200' '

-  +#160'c>'#180'^F'#165'/'#229'<'#244'wL$@'#132#208#153'.'#162#206'4'#141#219

-  +#153#14#8#138#237'^T'#4#22'e'#194#170#168#10'T'#254'R'#240#219#191'U'#207''

-  +#241#207'J'#246'_'#197#7'`Gn'#205#0#3'z'#183#145'g'#225'G(^g'#0#31#251#201

-  +#164#22#206#135#173'h>h'#147#164#15#252'4)^c'#153#197#128'^Y'#21#255#160#180

-  +'/'#169#250#144#248'p'#224')'#1#187#164#242#150#164#189#1'~'#171#213#154#141

-  +'F#'#246#236#147#169#151'~X'#213#253'e'#227#130#0#202#227#128''#192#16#1'=H'

-  +#28':'#164's2'#17#173'['#244#0#162#216#168'+'#26#129#171#13#176#143#0#175

-  +#229#228'#'#223#135'Ya'#200' 8@'#6#128#174#158'`'#132'a'#142'|<"'#132#246'x'

-  +#17#183#146'4'#168#207#179#176#190'H'#131'Z'#170#160#219'k'#251#221'8'#245

-  +#140'K'#191'T'#13#184'$'#239#223#141#8#228#203'~t5'#20'h'#134'['#251#239#185

-  +#210#221#253#191#6'n'#228#167#211#208#207'fQ'#144#204#234#225'bL'#18'~'#16'.'

-  +#3#188#233#18'"'#182'C'#174''#145#168#248'E'#214#30'-'#11'g'#218'-'#171#234

-  +'+'#157#190#203#224#167'{'#224#18#192#144'#'#179'I'#194#210#254#2#248'O'#31

-  +#23#4#176'|,%'#2'Z'#135#244'@'#177'F'#0'`'#211#186'I*f'#139#236'J'#214#10#132

-  +#16#12#9'X"@'#19#18#218#6'y'#212'@'#6' '#2#218#14#133#12'|'#218#151#194#2#206

-  +#177#169#16#130#178#128'N'#179' '#154'fQm'#158#16')'#164'!'#19'C'#146#250#181

-  +'E'#22#214'r'#195#2'y'#17#247#207#221#219#235#29#248'y2'#242#165#155#252#170

-  +'R'#11'0'#253'7'#240#178'E'#236#167#179#200'O'#166'Q'#152#206#226' '#153#214

-  +'BZ'#252'd^'#10#31#28#4#188#212#222#235#20']Ro'#178#220#168#19#185'JM'''#30

-  +'U'#150#246'('#207#157'J?>'#11'|'#168#251't'#157#135#0#191#0#30'j>&'#221#132

-  +'I0E'#6#159#249#172'%'#192'_'#242'K?'#188#227#130#0#158'<'#150#18#1'|'#4#237

-  +'6'#201#183#217#140';'#20#19#1#160#203#144#169'5h'#201#186'm'#246#141#198#0

-  +#243'@i'#13#2#4#2'2`"'#144#133#181#2'!'#4#175'B'#8#162#229#235'F'#0'E*'#191

-  +#201#243#247#2'"'#129'('#205#252' '#205#149#159#229'~'#0#19'"'#203#184#160')'

-  +#160#181#143#207'&'#158#241#233'8'#142#249'x'#15' '#233'{y'#202#139#162'Wy'

-  +#10#219#153'g'#142'y'#244#137'~'#158#6#4'N'#218'N#?K0C'#14''#181#237#249#167

-  +#247#204'1}Z'#185#20#11'j'#213#222#145#240#12'B'#237#197#183'q{+'#237'Mi'#174

-  +#210#192#159'HW'#158#145'*'#128#15#169#143#184'='#239#147#186#143#10#189'1'

-  +#173'!'#241#209#140'snl'#252'G'#143#30'e'#23#192''#250#184' '#128#163#13'K'

-  +#4#223#253#238'w=4%'#5#25#208#161#144#164'N'#212'l6'#1'd'#214#10#232#129'lxz'

-  +#174'B'#180'('#3#240#153#4'h'#223#144'C'#211#209#10'jB'#6#136#30'DJG'#16't'

-  +#20'A'#19#2#242#10#8#139#220#2'Dr'#130#217'y^'#164#249#225#143#239#219'm'#140

-  +#220'sN'#250#224#207#240#14#252'#?'#176#225#202#240#138#242' '#249#0'z;/'#210

-  +#133'9'#25#152#147'!3'#237'P'#212#21'x'#158#199#217#3'.'#224#233'X"'#221'vy'

-  +#150#29#167'@'#7#224#7#152'a'#223#143' '#229#165' '#135#215't|'#4#208'C'#210

-  +#3#244#240#230'c!'#2'^'#192#171'O'#199#211#11#224'?'#219#184' '#128'g'#27'%"'

-  +'@'#212#224#210#165'K>I'#164#144#30'D&'#3#180''''#4#168#145'a'#8'2'#192#2'2P'

-  +#152#202#220'Y'#132#8#140'i'#192#230#1'k'#5#30';'#14']B`2'#160'c'#1'b'#8#162

-  +#29'0'#17#152#22' '#24'y'#145#223#167'r'#183#11'`'#165#17#192#129'y'#9#151

-  +#254'J'#227#225'We'#253'_)'#227#164#211'h'#215'L$'#130']'#25#231#157#150#242

-  +#136#238#231'6Vo='#248#6#240'R'#142'k'#194'x'#0#255'd'#137#212#231'E'#18'vX'

-  +#189#23#21''#14#208'c'#166']'#196#241'WVV2x'#245#137#148#243#11#224'?'#219

-  +#184' '#128#247'7l^-'#194#135#244#16'z'#198'<'#160#135#18#128'E'#143'B6'#17

-  +#224'/'#160'}'#16'B'#131#164#22#8#129'5'#0#172'}'#157'Q'#216#144'F'#165'L'#4

-  +'R'#177#200#239#177'D'#160#29#136#161#199'A}'#144#129'v '#210#182#246#31'(]'

-  +#151' '#219#170#152'}'#132#165's'#209'4'#200#156#244'!, '#160'Vy^v'#3'j'#183

-  +#164'W'#184#253#10'U>7'#157't'#217#129''''#222'{'#165#165#188#5#190'S'#207

-  +'^|'#165#165'=/p'#236#161#209#6#253'~'#168#243#19#172'%'#166'?!'#160'c'#31

-  +#196'0C=>'#169#247' '#142#132#142'!y'#7#128#207#156'8'#190#156#253#5#232#159

-  +'e\'#16#192#7#27#150#8#240#0#186'Z'#1'I'#168'`gg'#7'R;'#132#137#0#243#20'd'#0

-  +'p'#139#218'_'#23#240'#'#215#192#128#159'5'#2'G30D`4'#3#215'g`'#9'Ai'#13#1'$'

-  ,#16'pI?'#231#28's'#242#156'''`'#247#12#17'('#19';8D'#19'p'#192#207#0'/'#8'AK'

-  +'~'#165'{'#230#27#213'>'#173#0#222#196#233#141#138'o'#156'y'#0#191#241#228'O'

-  +#5#220'v'#27#199#1'~'#243''#168#245'x}'#20'Es'#0#31#210#30#234'='#17'jz'#227

-  +#198#141#20#160#135#180'w'#242#245#249'|O'#251'a8'#143#227#130#0#158#223'8'

-  +#160#21#12#6#3#175#211#233#4#180#248#244#0#179#153#0'I'#14'S'#129#30#234#152

-  +#30'n'#164#31'C'#202#215#232'X'#141#142#25'S'#192#152#5#150#4#232#189#186'eY'

-  +#161#21#132'UBP'#154#20'x'#246'$!'#4#156#147'/'#224#247'DK'#176#219#149#243

-  +#182#170#188'2'#210#221#181#225's''>/'#210']z'#234#25#208'/'#28#208'W'#213'|'

-  +'+'#245'E'#221#231#181#144#194#12'*=IwH'#249#5'@O@_'#200'g&'#0'=T|c'#219#11

-  +#232#205'y'#186#235#139#241'>'#198#5#1#28#207'Xj"'#208#218#7#25'@3'#160#7#157

-  +'M'#5','#4'nL'#142#233#206'"'#151#16'|'#157'?`4'#6#248#22'P'#189#24#11#17'D'

-  +#134#16'@*'#244#186'RD'#1'D@'#175'E'#196#194#19'2`"'#192#182#28#243#212#146

-  +'x`'#201#142'/'#0#159#27#21'_'#21'j=7'#212#196'B'#223#195#192#167#207#157#211

-  +#246#1#2#192'B'#191'k'#230#2#158#200#14#210#29#239'Y@'#194#131'H'#232'xj$'

-  +#189#1'=]'#175#188#162#226#187#235#139#241#1#199#5#1#28#239#176#215#215'8'#14

-  +#141'f'#0'3aoo'#143#9'!@i'#218'`'#16#192'l'#160#151#134' '#3#172'A'#8' '#7':'

-  +#206'k'#128#158'^'#202'k9'#30#154#227'J'#252#4#202'1'#13#180'I'#192'f'#128'o'

-  +'H'#0#231#148'!.X'#144'@q'#178#5#208'sQ'#237'-'#240#177#166#247'X'#201#175

-  +#196#161''''#4#176'0'#251#134#0'd!<'''#6#228#144#236#176#223#23'Fk'#0#224#137

-  +#3#210#181#181#181#148'~?'#212#250#140'L'#168#28#160#191'p'#232#157#204#184

-  +' '#128#147#27#165'D\'#233'Y'#168'@'#8#208#12'n'#222#188#233'-#'#4#12#2'M '

-  +#160#14#5#212'au'#219#172#149'&'#14#214#0'0'#0'v'#1'<k'#0#206#218#19#141'@r'

-  +#141'<'#211'T'#131'%?'#182#245#219#249#205#25'}fN'#167'bl'#254'$/'#231#230

-  +''''#6#212#244'r6'#11'2]'#166#155#212'j5k*'#0#240#147#201'$3'#128'o4'#26#185

-  +#145#242#142'M'#143#211#185#0#253#9#141#11#2'8'#189'q$B'#216#216#216#128#198

-  +#128'jE'#159#192#227#19#160'|'#146#164'L'#18'D'#16#216'f'#130#192#2#174#152

-  +#207#231#188'&'#172#225#0#131#29'=3'#176#157'qk'#252#220#163#247'yf'#187'tB'

-  +#4'~'#240#3'Im'#128#157'5'#1'l'#227#24#246'IZ'#231'd'#186#0#199'<y&i*P'#217

-  +#193#17')'#22#218#207#232#251#179'V'#171#5'5>k6'#155#217't:'#205#232#156#243

-  +'e'#18'~'#9#224#171#219#23#227#152#199#5#1#156#141'Q'#186#15#198'\'#192#182

-  +'1'#25#8'H'#30'H'#129'H'#192'#py'#134#24#8#148#30#200#129#128#232#25'r'#160

-  +'}'#143'@j'#23':'#206#128#199'6>'#19#219#4'V'#187#141#181#207#253'L'#148'"'#0

-  +#27#192#243'>}/o'#155#133#190'?'#7#208#137'Dr'#128#156#190'+'#235#247#251'9'

-  +#129#222#2#29#146#189#221'n'#231#6#236#248#156#11#192#159#205'qA'#0'gsT'#239

-  +#139''''#192'Q.1`'#13'r'#184'u'#235#150'7'#28#14'-9'#224'8'#8#2'k'#179'O'#192

-  +#230'5'#8#3#235#245#245'u'#187'm'#6#128#140'5Iy'#11'J'#2';o'#3#220'X?~'#252

-  +'8'#199'6@'#142#253'e@'#199#250#16#176'/'#219#191#24#167'8.'#8#224'|'#141'e'

-  +#247#235#0'9`'#24#130'0'#3'D'#177#236#3#161'Y`]'#175#215#15#0#211#128#218#29

-  +#6#224#24#0'9'#214#135#0#253#176'c'#23#227#12#141#11#2'x1'#198#179#220#199

-  +#163#190#246#168#224#189#0#249'9'#30#23#4'p1.'#198#135'x'#252'p'#251'ut'#3

-  +#215#244'"'#0#0#0#0'IEND'#174'B`'#130'('#0#0#0#128#0#0#0#0#1#0#0#1#0' '#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1#0#0#0#1#0#0#0#1#0#0#0#1

-  +#0#0#0#1#0#0#0#1#0#0#0#1#0#0#0#1#0#0#0#1#0#0#0#1#0#0#0#1#0#0#0#1#0#0#0#1#0#0

-  +#0#1#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1#0#0#0#1#0#0#0#2#128#128#128#2'UUU'#3

-  +'@@@'#4'333'#5'III'#7'@@@'#8'999'#9'999'#9'MMM'#10'FFF'#11'FFF'#11'FFF'#11'M'

-  +'MM'#10'999'#9'@@@'#8'@@@'#8'UUU'#6'333'#5'UUU'#3#128#128#128#2#0#0#0#2#0#0#0

-  +#1#0#0#0#1#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  ,#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1#0#0#0#1#128#128#128#2'UUU'#3'UUU'

-  +#6'@@@'#8'FFF'#11'III'#14'<<<'#17'III'#21'EEE'#26'DDD'#30'DDD"EEE%AAA''DDD)A'

-  +'AA+AAA+AAA+DDD)AAA''GGG$FFF!DDD'#30'==='#25'@@@'#20'@@@'#16';;;'#13'FFF'#11

-  +'@@@'#8'333'#5'UUU'#3#128#128#128#2#0#0#0#1#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#1#0#0#0#2'UUU'#3'+++'#6'999'#9'NNN'#13'CCC'#19'BBB'#27'GGG$'

-  +'DDD-CCC5DDD<DDDDBBBMCCCTDDDZEEE]DDDaBBBdDDDfCCCgDDDfBBBdBBB`EEE]BBBYDDDSDDD'

-  +'KEEECAAA;AAA3AAA+DDD"GGG'#25'GGG'#18';;;'#13'@@@'#8'333'#5'UUU'#3#0#0#0#2#0

-  +#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#1#0#0#0#2'UUU'#3'UUU'#6'@@@'#12'GGG'#18'BBB'#27'CCC&BBB2DDD@EEENCCC\DDDiDDD'

-  +'tCCC~DDD'#136'DDD'#143'CCC'#149'DCC'#153'DCC'#155'EED'#158'DDC'#160'EED'#162

-  +'EED'#162'EDD'#161'DCC'#160'EDD'#157'CCC'#155'CCC'#152'CCC'#148'BBB'#142'CCC'

-  +#134'DDD|CCCrDDDfEEEYDDDKCCC=EEE0GGG$EEE'#26'<<<'#17'MMM'#10'UUU'#6'UUU'#3#0

-  +#0#0#1#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'UUU'#3'+++'#6'FFF'#11'GGG'#18

-  +'FFF'#29'FFF,DDD<AAANCCC_DDDpCCC'#129'DDD'#142'EED'#154'DDC'#163'DDC'#171'DD'

-  +'C'#178'DDC'#183'EDB'#188'EDB'#190'EDB'#192'FFE'#192'FFE'#192'EED'#193'EED'

-  +#194'EED'#194'EED'#193'EEB'#193'FEC'#192'FEC'#192'DDC'#190'DDC'#189'DDC'#187

-  +'DDC'#182'EED'#177'EDD'#169'DDD'#162'CCC'#152'CCC'#140'CCC~DDDmCCC\DDDKBBB:D'

-  +'DD)BBB'#27'@@@'#16'UUU'#9'333'#5#128#128#128#2#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'UUU'#3'III'#7'DDD'#15

-  +'EEE'#26'DDD)EEE;DDDODDDfBBB{CCC'#141'DDC'#156'DDC'#168'DDC'#178'EDB'#187'EE'

-  +'D'#192'FEC'#195'FFC'#198'HFD'#200'HFD'#201'GGC'#202'HFC'#203'IGD'#203'IGD'

-  +#203'JHC'#203'IHD'#204'IHD'#204'IHD'#204'IHC'#204'IGD'#204'IGD'#203'IGD'#203

-  +'GEC'#203'GEC'#203'FFC'#202'GFD'#201'FEC'#199'FFE'#196'EDB'#194'DDC'#190'EED'

-  ,#185'EDD'#177'DDD'#166'DDD'#153'DDD'#139'DDDxDDDbDDDKFFF7CCC&CCC'#23';;;'#13

-  +'UUU'#6'UUU'#3#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'UUU'#3'@@'

-  +'@'#8'@@@'#16'BBB'#31'BBB2BBBIBBBaCCCyDDD'#142'DDC'#160'DDC'#175'FEC'#186'FF'

-  +'E'#192'GFD'#197'HFD'#200'HFC'#202'IGD'#203'JID'#204'OF>'#211'SC6'#219'W@0'

-  +#227'[>+'#235']<('#238'^<'''#240'_<%'#243'`<#'#245'a;"'#247'a:!'#248'a;"'#246

-  +'`<$'#245'_<%'#242'^='''#240'\=)'#237'Z=,'#233'V@1'#225'RD8'#217'NG?'#210'JH'

-  +'D'#204'IHC'#204'HHD'#204'GEC'#203'FEC'#202'EED'#198'EED'#195'DDC'#190'DDC'

-  +#183'DDD'#172'DDD'#157'DDD'#138'DDDtCCC\DDDDCCC.@@@'#28'DDD'#15'III'#7'UUU'#3

-  +#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#1'UUU'#3'@@@'#8'KKK'#17'HHH CCC5CCCPDDDlCCC'#134'DDC'#156

-  +'EED'#172'FEC'#185'GFD'#193'GFC'#198'HFC'#202'IGD'#203'PE;'#213'VA0'#227']<('

-  +#239'a;#'#246'd9'#30#254'g:'#29#255'g:'#30#255'i<'#30#255'j='#31#255'j>'#30

-  +#255'k>'#30#255'l?'#31#255'l@'#31#255'mA'#31#255'nA'#31#255'm@'#31#255'l@'#31

-  +#255'l?'#31#255'k>'#30#255'j>'#30#255'j='#31#255'i<'#30#255'g:'#30#255'f:'#29

-  +#255'd9'#31#252'`<#'#245'[=*'#236'UA3'#223'MF?'#210'IHC'#204'HFD'#204'FFC'

-  +#203'FFE'#200'DDC'#197'EED'#191'DCC'#182'DDD'#170'CCC'#152'CCC'#129'DDDfDDDK'

-  +'DDD1FFF'#29'@@@'#16'III'#7'UUU'#3#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#1'UUU'#3'III'#7'@@@'#16'@@@ FFF7DDDRCCCoCCC'#140'DDC'

-  +#163'EED'#180'EED'#191'FFC'#198'IGD'#201'ME?'#208'UA2'#224'\<('#238'b9 '#250

-  +'f:'#29#255'h;'#30#255'j>'#30#255'm@'#31#255'oB '#255'qD '#255'sF '#255'uH!'

-  +#255'|L#'#255#128'O$'#255#133'Q%'#255#136'S&'#255#139'V'''#255#143'X('#255

-  +#145'Y)'#255#142'W('#255#139'U'''#255#135'S&'#255#131'Q%'#255#128'O$'#255'yK'

-  +'"'#255'tG!'#255'rE '#255'pD '#255'nA '#255'l@'#31#255'j='#31#255'g;'#30#255

-  +'e9'#29#255'a;!'#247'Z=+'#234'RB5'#221'JGC'#207'HFD'#204'FEC'#203'FFE'#200'E'

-  +'ED'#196'DDC'#189'DDD'#177'CCC'#159'DDD'#135'CCCjBBBMBBB2FFF'#29'III'#14'UUU'

-  +#6#128#128#128#2#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#2'333'#5';;;'#13'BBB'

-  +#27'AAA3BBBQDDDqDDD'#142'EED'#165'EDB'#183'FEC'#194'HFD'#200'IFB'#205'SA3'

-  +#223'^;&'#242'e8'#29#255'g;'#30#255'j>'#31#255'mA'#31#255'pD!'#255'tG!'#255

-  +'~M#'#255#141'W('#255#151']*'#255#160'c-'#255#169'i0'#255#178'n1'#255#181'q2'

-  +#255#182's3'#255#183't3'#255#184't3'#255#184'v3'#255#185'v4'#255#186'w3'#255

-  +#185'v3'#255#184'u3'#255#184't3'#255#183't3'#255#182'r3'#255#181'q2'#255#175

-  +'l1'#255#166'h/'#255#158'a,'#255#149'\*'#255#138'U('#255'zK"'#255'sF '#255'p'

-  +'C '#255'l@'#31#255'i='#31#255'g;'#30#255'c8'#31#252'[<*'#237'RC8'#218'IGD'

-  +#205'HFD'#204'EED'#201'DDC'#198'EED'#191'DDD'#179'CCC'#161'DDD'#136'CCCkEEEJ'

-  +'DDD-@@@'#24'FFF'#11'@@@'#4#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  ,#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'UUU'#3'999'#9'FFF'#22'CCC*DDDGDDD'

-  +'iCCC'#138'EED'#165'FEC'#184'FEC'#194'HHD'#200'QC9'#216'[<*'#237'c8'#31#253

-  +'g;'#30#255'j>'#31#255'nA '#255'sF!'#255'|M#'#255#141'W('#255#158'a,'#255#173

-  +'k1'#255#181'r3'#255#185'v4'#255#187'y4'#255#189'{4'#255#191'~5'#255#193#129

-  +'5'#255#195#131'5'#255#196#132'6'#255#197#134'6'#255#198#134'6'#255#199#136

-  +'6'#255#200#136'6'#255#200#137'6'#255#199#136'6'#255#198#135'6'#255#198#134

-  +'6'#255#197#133'5'#255#196#132'6'#255#195#131'5'#255#193#128'5'#255#191'~4'

-  +#255#188'z4'#255#186'x4'#255#184'u3'#255#181'q2'#255#168'i/'#255#153'_+'#255

-  +#137'T'''#255'yJ"'#255'rE '#255'm@'#31#255'i<'#31#255'f:'#30#255'b9!'#249'X>'

-  +'.'#232'MD>'#212'HFD'#204'EED'#202'EED'#198'EED'#192'CCC'#180'CCC'#160'DDD'

-  +#132'DDDbCCCACCC&CCC'#19'@@@'#8'UUU'#3#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'333'#5'III'#14'>>>!DDD<DDD^CCC'#129'EED'#158

-  +'DDC'#180'FEC'#194'IGD'#200'RB7'#219'^9%'#244'e8'#29#255'i<'#31#255'mA '#255

-  +'rE!'#255'|L#'#255#149'[+'#255#170'j1'#255#181'r3'#255#185'v4'#255#188'z4'

-  +#255#192'6'#255#196#132'6'#255#199#136'6'#255#202#138'7'#255#204#142'7'#255

-  +#206#145'7'#255#209#147'8'#255#211#150'8'#255#212#152'9'#255#213#152'9'#255

-  +#213#153'9'#255#214#154'9'#255#215#155':'#255#215#155'9'#255#215#155':'#255

-  +#214#154'9'#255#213#153'9'#255#212#152'9'#255#211#151'9'#255#210#149'9'#255

-  +#208#147'8'#255#206#143'8'#255#203#141'8'#255#201#138'7'#255#198#135'7'#255

-  +#195#131'5'#255#191'~5'#255#188'y4'#255#184'u3'#255#180'q3'#255#165'f/'#255

-  +#142'W('#255'xJ"'#255'pD!'#255'l?'#31#255'h;'#30#255'd7'#30#255'Z<)'#239'NC<'

-  +#214'HFD'#204'EED'#202'DDC'#198'EDD'#191'DDD'#176'DDD'#153'CCCzCCCXBBB6FFF'

-  +#29'@@@'#12'@@@'#4#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1#128#128#128#2

-  +'@@@'#8'@@@'#20'FFF,AAANBBBtDCC'#149'DDC'#174'EED'#191'GEC'#199'R@5'#220'_9#'

-  +#245'e9'#30#255'i='#31#255'nB!'#255'wI#'#255#142'W)'#255#165'f/'#255#181'q3'

-  +#255#186'x4'#255#191'~5'#255#195#131'6'#255#199#136'7'#255#203#140'8'#255#207

-  +#145'9'#255#211#150'9'#255#214#154':'#255#216#157':'#255#219#160';'#255#221

-  +#162';'#255#223#164';'#255#225#167'<'#255#226#168'<'#255#226#169'='#255#227

-  +#170'<'#255#228#171'<'#255#228#171'='#255#228#172'='#255#228#171'='#255#227

-  +#170'<'#255#227#169'<'#255#226#169'='#255#226#168'<'#255#224#167';'#255#222

-  +#164';'#255#220#162';'#255#218#159':'#255#216#157':'#255#213#154':'#255#210

-  +#149'9'#255#206#144'8'#255#202#139'8'#255#198#134'7'#255#194#129'7'#255#189

-  +'|6'#255#184'v4'#255#178'n2'#255#159'b.'#255#135'T'''#255'tF!'#255'mA '#255

-  +'h;'#31#255'd8'#30#255'\;('#240'OC;'#216'GGD'#204'EDD'#201'DDC'#197'DDD'#187

-  +'DDD'#170'DDD'#143'BBBlDDDGAAA''KKK'#17'UUU'#6#0#0#0#2#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'U'

-  +'UU'#3'FFF'#11'BBB'#27'FFF7EEE]DDD'#132'DDC'#164'FEE'#186'GGD'#197'OA8'#216

-  +'_8#'#246'e9'#30#255'j= '#255'oB!'#255'zK$'#255#151'^,'#255#176'n4'#255#184

-  +'v5'#255#189'|6'#255#194#131'7'#255#200#137'8'#255#205#144':'#255#210#149';'

-  +#255#214#154';'#255#217#158';'#255#221#163'<'#255#225#167'='#255#227#171'='

-  +#255#229#173'='#255#231#175'>'#255#232#177'>'#255#234#179'?'#255#236#181'>'

-  +#255#236#182'?'#255#237#183'?'#255#237#183'?'#255#238#183'@'#255#238#184'?'

-  +#255#238#185'?'#255#238#184'?'#255#238#183'@'#255#237#182'?'#255#237#183'?'

-  +#255#236#182'?'#255#236#181'>'#255#234#179'>'#255#232#176'>'#255#230#175'>'

-  +#255#229#172'='#255#227#169'='#255#224#167'='#255#220#161'<'#255#217#157'<'

-  +#255#213#153';'#255#209#148':'#255#204#142'9'#255#198#135'8'#255#193#129'7'

-  +#255#188'{6'#255#183't5'#255#171'k2'#255#142'W)'#255'tH"'#255'nA '#255'h<'#31

-  +#255'd7'#30#255'[:('#240'KD?'#211'GFD'#204'DDC'#201'EDD'#195'CCC'#182'CCC'

-  +#159'CCC}EEEUBBB2@@@'#24'999'#9'UUU'#3#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  ,#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'333'#5'III'#14'BBB#EEECCCCkEEE'

-  +#145'DDC'#174'FFE'#192'KE@'#206'Z;)'#238'c8'#30#255'i<'#31#255'oB!'#255'|M%'

-  +#255#153'_-'#255#178'o4'#255#186'x6'#255#192#128'7'#255#198#135'9'#255#204

-  +#142':'#255#209#149';'#255#214#155'='#255#219#161'>'#255#223#167'>'#255#227

-  +#170'>'#255#230#174'?'#255#232#177'@'#255#235#181'A'#255#237#183'A'#255#238

-  +#185'A'#255#240#187'A'#255#241#188'A'#255#242#189'B'#255#243#190'A'#255#244

-  +#191'B'#255#244#192'B'#255#244#191'B'#255#244#192'B'#255#244#192'B'#255#245

-  +#193'A'#255#244#192'B'#255#244#192'B'#255#244#191'B'#255#244#192'B'#255#244

-  +#191'B'#255#243#190'A'#255#242#189'B'#255#240#187'A'#255#239#186'A'#255#238

-  +#184'@'#255#237#183'A'#255#235#179'@'#255#232#176'?'#255#229#173'?'#255#226

-  +#169'?'#255#223#165'>'#255#218#160'='#255#213#153'<'#255#208#147';'#255#202

-  +#140':'#255#196#133'9'#255#190'~7'#255#184'v6'#255#173'l3'#255#145'Y+'#255'u'

-  +'H$'#255'm@ '#255'g;'#31#255'b7'#31#253'U>/'#230'IEC'#206'FFE'#202'EED'#199

-  +'CCC'#189'DDD'#170'DDD'#139'BBBdCCC=BBB'#31'@@@'#12'UUU'#3#0#0#0#1#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'UUU'#6'KKK'#17'DDD)EEENDDDxDC'

-  +'C'#157'DDC'#182'GGC'#197'T=1'#225'a7 '#252'g;'#31#255'l@ '#255'wI$'#255#154

-  +'^.'#255#179'o6'#255#186'x7'#255#193#128'9'#255#200#137':'#255#207#146'<'#255

-  +#212#153'>'#255#217#159'>'#255#222#166'?'#255#227#170'@'#255#231#175'A'#255

-  +#234#180'B'#255#236#182'B'#255#238#185'B'#255#240#187'B'#255#242#190'D'#255

-  +#243#191'C'#255#244#192'D'#255#245#193'C'#255#246#194'D'#255#247#194'D'#255

-  +#247#196'E'#255#247#196'D'#255#247#196'D'#255#248#197'D'#255#248#197'D'#255

-  +#248#197'D'#255#248#197'D'#255#248#197'D'#255#248#197'D'#255#248#196'D'#255

-  +#247#196'D'#255#247#196'D'#255#247#195'E'#255#246#195'D'#255#245#193'D'#255

-  +#245#193'C'#255#244#192'D'#255#243#191'C'#255#242#189'C'#255#240#186'C'#255

-  +#238#184'C'#255#236#182'B'#255#233#179'B'#255#230#174'A'#255#226#169'@'#255

-  +#221#164'?'#255#216#157'>'#255#211#151'='#255#205#143'<'#255#198#135':'#255

-  +#191'8'#255#184'w7'#255#174'l4'#255#144'X+'#255'rE"'#255'j> '#255'e9'#30#255

-  +'_8"'#247'O@8'#219'GFD'#204'DDC'#201'CCC'#194'CCC'#178'DDD'#151'DDDqDDDGGGG$'

-  +'III'#14'@@@'#4#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#2'III'#7'@@@'#20

-  +'CCC.DDDVCCC'#130'EED'#165'DDC'#189'LB='#207']7$'#244'd7'#31#255'j> '#255'qE'

-  +'"'#255#140'W+'#255#174'l5'#255#186'x8'#255#193#129':'#255#200#138'<'#255#207

-  +#146'>'#255#213#155'?'#255#220#163'@'#255#225#169'A'#255#229#174'B'#255#233

-  +#179'D'#255#236#182'D'#255#239#185'D'#255#241#189'E'#255#242#190'E'#255#244

-  +#192'E'#255#245#194'E'#255#246#195'F'#255#247#195'F'#255#247#196'G'#255#247

-  +#197'F'#255#248#197'F'#255#248#197'F'#255#249#198'F'#255#249#198'G'#255#249

-  +#198'G'#255#249#198'G'#255#249#198'G'#255#249#198'G'#255#249#198'G'#255#249

-  +#198'G'#255#249#198'G'#255#249#198'G'#255#249#198'G'#255#249#198'G'#255#249

-  +#198'F'#255#248#197'F'#255#248#197'F'#255#247#196'F'#255#247#196'F'#255#247

-  +#195'F'#255#246#194'F'#255#244#193'F'#255#243#191'E'#255#242#190'E'#255#240

-  +#188'E'#255#238#185'D'#255#235#181'D'#255#232#178'C'#255#228#172'C'#255#223

-  +#167'B'#255#218#161'@'#255#212#153'?'#255#205#144'='#255#198#135';'#255#191

-  +'9'#255#183'u7'#255#167'g3'#255#130'P('#255'oB"'#255'h< '#255'c7'#30#255'X:'

-  +'*'#237'HFB'#207'EEE'#202'DCC'#197'DDD'#184'CCC'#160'CCCzEEENFFF(@@@'#16'333'

-  +#5#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#2'III'#7'FFF'#22'BBB2CCC\DDD'#136'DDC'#171

-  +'EED'#192'Q?5'#219'a6'#31#252'f:'#31#255'mA!'#255'N'''#255#164'f3'#255#182

-  +'u8'#255#190':'#255#199#138'='#255#207#147'?'#255#213#155'A'#255#220#163'B'

-  +#255#225#170'D'#255#230#177'E'#255#234#181'E'#255#237#184'F'#255#240#188'G'

-  +#255#242#189'G'#255#243#192'G'#255#244#193'H'#255#245#194'H'#255#246#195'H'

-  ,#255#247#196'H'#255#247#197'H'#255#247#197'H'#255#248#198'H'#255#248#198'H'

-  +#255#248#197'H'#255#248#198'H'#255#248#198'I'#255#249#198'I'#255#249#198'I'

-  +#255#249#198'I'#255#249#198'I'#255#249#198'I'#255#249#198'I'#255#249#198'I'

-  +#255#249#198'I'#255#249#198'I'#255#249#198'I'#255#249#198'I'#255#248#198'I'

-  +#255#248#198'H'#255#248#198'H'#255#248#198'H'#255#248#198'H'#255#247#197'H'

-  +#255#247#196'I'#255#247#195'H'#255#246#195'H'#255#245#195'H'#255#244#193'H'

-  +#255#243#191'G'#255#241#189'G'#255#239#186'G'#255#236#183'F'#255#233#180'F'

-  +#255#229#175'D'#255#224#168'D'#255#218#161'B'#255#211#153'@'#255#205#144'?'

-  +#255#196#134'='#255#188'{:'#255#180'r8'#255#155'_/'#255'wJ$'#255'j? '#255'e8'

-  +#30#255']8#'#246'LB<'#213'FEE'#203'EED'#199'DDD'#188'DDD'#165'DDD'#128'DDDSF'

-  +'FF,GGG'#18'+++'#6#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#2'III'#7'CCC'#23'BBB6DDDaCCC'#141'DDC'#174

-  +'GDB'#196'V:+'#233'c7'#30#255'h; '#255'pC#'#255#146'Z-'#255#179'p7'#255#187

-  +'{;'#255#196#133'='#255#204#144'?'#255#212#154'B'#255#219#163'E'#255#225#170

-  +'E'#255#230#176'G'#255#234#181'H'#255#238#185'I'#255#240#189'I'#255#242#190

-  +'J'#255#243#192'J'#255#244#193'J'#255#245#195'J'#255#246#195'J'#255#246#196

-  +'J'#255#247#196'K'#255#247#196'K'#255#247#197'K'#255#247#197'K'#255#247#197

-  +'K'#255#247#197'J'#255#247#197'J'#255#247#198'J'#255#247#198'J'#255#247#198

-  +'J'#255#247#198'J'#255#247#198'J'#255#247#198'J'#255#247#198'J'#255#247#198

-  +'J'#255#247#198'J'#255#247#198'J'#255#247#198'J'#255#247#198'J'#255#247#198

-  +'J'#255#247#198'J'#255#247#197'J'#255#247#197'J'#255#247#197'J'#255#247#197

-  +'K'#255#247#197'K'#255#247#197'K'#255#247#196'K'#255#246#196'K'#255#246#196

-  +'J'#255#246#195'J'#255#245#195'J'#255#244#194'J'#255#243#191'I'#255#241#189

-  +'I'#255#239#188'I'#255#236#184'H'#255#233#180'H'#255#228#174'G'#255#223#168

-  +'E'#255#217#160'C'#255#210#151'B'#255#202#141'?'#255#193#130'<'#255#185'x:'

-  +#255#174'm6'#255#134'S*'#255'mA"'#255'f:'#31#255'a6'#30#253'R>4'#224'FFE'#203

-  +'EED'#200'CCC'#190'DDD'#169'CCC'#133'CCCXAAA/CCC'#19'+++'#6#0#0#0#1#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'III'#7'CCC'#23'BB'

-  +'B6CCCcCCC'#144'EED'#177'JC>'#202'[6"'#244'c8'#31#255'j?!'#255'xI&'#255#162

-  +'d2'#255#183'u:'#255#192#129'='#255#200#140'@'#255#209#150'B'#255#217#160'E'

-  +#255#223#169'G'#255#229#176'H'#255#233#181'J'#255#236#185'J'#255#239#188'K'

-  +#255#241#190'K'#255#242#192'K'#255#243#193'L'#255#244#194'L'#255#245#195'L'

-  +#255#245#195'L'#255#245#195'L'#255#246#196'L'#255#246#196'L'#255#246#196'L'

-  +#255#246#196'L'#255#246#196'M'#255#246#196'M'#255#246#197'M'#255#246#197'M'

-  +#255#246#197'M'#255#246#197'M'#255#246#197'M'#255#246#197'M'#255#246#197'M'

-  +#255#246#197'M'#255#246#197'M'#255#246#197'M'#255#246#197'M'#255#246#197'M'

-  +#255#246#197'M'#255#246#197'M'#255#246#197'M'#255#246#197'M'#255#246#197'M'

-  +#255#246#197'M'#255#246#196'M'#255#246#196'M'#255#246#196'M'#255#246#196'L'

-  +#255#246#196'L'#255#246#196'L'#255#246#196'L'#255#245#195'L'#255#245#196'L'

-  +#255#244#195'M'#255#244#194'L'#255#243#193'L'#255#242#192'L'#255#241#190'K'

-  +#255#238#188'K'#255#236#184'J'#255#232#179'I'#255#228#173'H'#255#222#166'F'

-  +#255#215#158'E'#255#207#148'B'#255#198#137'?'#255#190'~<'#255#180'r9'#255#150

-  +']/'#255'qE$'#255'h< '#255'b6'#30#255'W9*'#235'GED'#204'DDC'#201'DDD'#192'CC'

-  +'C'#172'CCC'#137'DDDZAAA/GGG'#18'333'#5#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#1'+++'#6'III'#21'CCC5DDDbCCC'#145'DDC'#178'LB;'#206'^6 '

-  +#249'd8'#31#255'l@!'#255#131'Q*'#255#172'k7'#255#186'z;'#255#196#133'?'#255

-  +#205#146'C'#255#213#156'E'#255#220#165'G'#255#227#173'I'#255#232#180'K'#255

-  +#236#184'L'#255#238#187'L'#255#240#189'N'#255#241#191'N'#255#242#192'N'#255

-  +#243#193'N'#255#244#194'N'#255#244#194'N'#255#244#194'O'#255#244#195'O'#255

-  +#244#195'O'#255#244#195'O'#255#244#195'O'#255#244#195'O'#255#244#195'O'#255

-  +#244#195'O'#255#244#195'O'#255#244#195'O'#255#244#195'O'#255#244#195'O'#255

-  +#244#195'O'#255#244#195'O'#255#244#195'O'#255#244#195'O'#255#244#195'O'#255

-  +#244#195'O'#255#244#195'O'#255#244#195'O'#255#244#195'O'#255#244#195'O'#255

-  +#244#195'O'#255#244#195'O'#255#244#195'O'#255#244#195'O'#255#244#195'O'#255

-  +#244#195'O'#255#244#195'O'#255#244#195'O'#255#244#195'O'#255#244#195'O'#255

-  ,#244#195'O'#255#244#195'O'#255#244#195'O'#255#244#195'O'#255#244#194'O'#255

-  +#244#195'N'#255#244#194'N'#255#243#193'N'#255#242#192'M'#255#241#190'N'#255

-  +#240#189'M'#255#237#186'M'#255#235#182'L'#255#230#178'K'#255#225#171'I'#255

-  +#219#163'G'#255#211#153'D'#255#202#143'B'#255#192#130'>'#255#183'u:'#255#164

-  +'f4'#255'yK&'#255'i=!'#255'c7'#30#255'Z8%'#241'GEC'#205'DDC'#201'CCC'#193'DD'

-  +'D'#173'DDD'#136'BBBYDDD-KKK'#17'333'#5#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#1'333'#5'CCC'#19'BBB2CCC_DDD'#143'DDC'#178'M@8'#211'_5'#31#251'e9'#31

-  +#255'lA"'#255#144'Y/'#255#179'r9'#255#188'}='#255#198#138'B'#255#208#149'D'

-  +#255#217#161'H'#255#223#170'K'#255#229#177'L'#255#234#181'M'#255#236#186'N'

-  +#255#239#188'O'#255#240#190'P'#255#241#191'P'#255#242#192'P'#255#242#192'P'

-  +#255#242#193'P'#255#243#193'P'#255#243#194'P'#255#243#194'P'#255#243#194'P'

-  +#255#243#194'P'#255#243#194'P'#255#243#194'P'#255#243#194'P'#255#243#194'P'

-  +#255#243#194'P'#255#243#194'P'#255#243#194'P'#255#243#194'P'#255#243#194'P'

-  +#255#243#194'P'#255#243#194'P'#255#243#194'P'#255#243#194'P'#255#243#194'P'

-  +#255#243#194'P'#255#243#194'P'#255#243#194'P'#255#243#194'P'#255#243#194'P'

-  +#255#243#194'P'#255#243#194'P'#255#243#194'P'#255#243#194'P'#255#243#194'P'

-  +#255#243#194'P'#255#243#194'P'#255#243#194'P'#255#243#194'P'#255#243#194'P'

-  +#255#243#194'P'#255#243#194'P'#255#243#194'P'#255#243#194'P'#255#243#194'P'

-  +#255#243#194'P'#255#243#193'P'#255#242#193'P'#255#242#192'O'#255#242#192'P'

-  +#255#241#192'P'#255#240#190'P'#255#238#188'O'#255#236#185'N'#255#232#180'M'

-  +#255#228#175'L'#255#222#167'I'#255#215#159'H'#255#205#147'D'#255#196#134'@'

-  +#255#186'y<'#255#173'm7'#255#130'O*'#255'j?!'#255'c8'#31#255'[7#'#245'HDA'

-  +#208'DDC'#201'CCC'#193'CCC'#172'CCC'#134'AAAVAAA+@@@'#16'@@@'#4#0#0#0#1#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#1'@@@'#4'<<<'#17'CCC.CCC[CCC'#140'EED'#176'O>5'#213'_5'#30#253

-  +'e9'#31#255'nB#'#255#151'\1'#255#181's:'#255#191#129'@'#255#201#142'C'#255

-  +#211#154'G'#255#219#164'K'#255#226#173'M'#255#231#179'O'#255#234#184'P'#255

-  +#237#187'Q'#255#239#189'Q'#255#240#190'R'#255#240#191'Q'#255#241#192'R'#255

-  +#241#192'R'#255#241#192'R'#255#242#192'R'#255#242#192'R'#255#242#193'R'#255

-  +#242#193'R'#255#242#193'R'#255#242#193'R'#255#242#193'R'#255#242#193'R'#255

-  +#242#193'R'#255#242#193'R'#255#242#193'R'#255#242#193'R'#255#242#193'R'#255

-  +#242#193'R'#255#242#193'R'#255#242#193'R'#255#242#193'R'#255#242#193'R'#255

-  +#242#193'R'#255#242#193'R'#255#242#193'R'#255#242#193'R'#255#242#193'R'#255

-  +#242#193'R'#255#242#193'R'#255#242#193'R'#255#242#193'R'#255#242#193'R'#255

-  +#242#193'R'#255#242#193'R'#255#242#193'R'#255#242#193'R'#255#242#193'R'#255

-  +#242#193'R'#255#242#193'R'#255#242#193'R'#255#242#193'R'#255#242#193'R'#255

-  +#242#193'R'#255#242#193'R'#255#242#193'R'#255#242#192'R'#255#242#192'R'#255

-  +#241#192'R'#255#241#192'R'#255#241#192'R'#255#240#191'Q'#255#240#190'R'#255

-  +#238#189'Q'#255#236#186'P'#255#234#182'O'#255#230#177'N'#255#224#171'L'#255

-  +#217#162'J'#255#208#150'F'#255#198#138'C'#255#188'}>'#255#177'o:'#255#136'S,'

-  +#255'k?"'#255'c8'#31#255'\6!'#248'JC?'#211'DCC'#201'CCC'#193'CCC'#171'BBB'

-  +#131'DDDRAAA''777'#14'UUU'#3#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'UUU'#3';;;'#13'AAA''CCCTBBB'#135'DD'

-  +'C'#174'N=5'#213'`4'#29#254'e9'#31#255'oC$'#255#156'`3'#255#181'u<'#255#193

-  +#131'A'#255#204#146'E'#255#213#157'I'#255#221#167'M'#255#227#175'O'#255#232

-  +#180'Q'#255#235#184'R'#255#237#187'S'#255#238#189'S'#255#239#190'S'#255#240

-  +#191'T'#255#240#191'T'#255#240#191'T'#255#240#191'T'#255#240#191'T'#255#240

-  +#191'T'#255#240#191'T'#255#240#191'T'#255#240#191'T'#255#240#191'T'#255#240

-  +#191'T'#255#240#191'T'#255#240#191'T'#255#240#191'T'#255#240#191'T'#255#240

-  +#191'T'#255#240#191'T'#255#240#191'T'#255#240#191'T'#255#240#191'T'#255#240

-  +#191'T'#255#240#191'T'#255#240#191'T'#255#234#187'R'#255#202#161'G'#255#232

-  +#185'R'#255#240#191'T'#255#240#191'T'#255#240#191'T'#255#240#191'T'#255#240

-  +#191'T'#255#240#191'T'#255#240#191'T'#255#240#191'T'#255#240#191'T'#255#240

-  +#191'T'#255#240#191'T'#255#240#191'T'#255#240#191'T'#255#240#191'T'#255#240

-  +#191'T'#255#240#191'T'#255#240#191'T'#255#240#191'T'#255#240#191'T'#255#240

-  +#191'T'#255#240#191'T'#255#240#191'T'#255#240#191'T'#255#240#191'T'#255#240

-  +#191'T'#255#240#191'T'#255#240#191'T'#255#239#189'S'#255#238#188'R'#255#236

-  ,#186'R'#255#234#184'Q'#255#231#179'Q'#255#226#173'O'#255#219#165'L'#255#211

-  +#154'H'#255#201#142'E'#255#190#128'@'#255#179'q:'#255#140'V.'#255'l@"'#255'c'

-  +'8'#31#255'\4'#31#250'JC>'#211'CCC'#201'DDD'#192'CCC'#168'CCC~DDDKFFF!MMM'#10

-  +#128#128#128#2#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#128#128#128#2'333'#10'@@@ EEEJCCC~EDD'#169'L?8'#208'^4'#30#253'd8'

-  +' '#255'pD%'#255#159'b4'#255#182'v='#255#194#132'C'#255#205#147'H'#255#215

-  +#160'L'#255#222#169'O'#255#228#176'Q'#255#232#181'S'#255#235#185'T'#255#236

-  +#187'T'#255#237#188'U'#255#238#189'U'#255#238#190'U'#255#238#190'U'#255#238

-  +#191'U'#255#239#190'U'#255#239#190'U'#255#239#190'U'#255#239#190'U'#255#239

-  +#190'U'#255#239#190'U'#255#239#190'U'#255#239#190'U'#255#239#190'U'#255#239

-  +#190'U'#255#239#190'U'#255#239#190'U'#255#239#190'U'#255#239#190'U'#255#239

-  +#190'U'#255#239#190'U'#255#181#144'@'#255';/'#21#255#21#16#7#255#9#7#3#255#2

-  +#2#1#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#1#255#7#6#3#255#17#14#6#255#31#25

-  +#11#255'2'''#18#255'SB'#29#255#157'}8'#255#233#186'S'#255#239#190'U'#255#239

-  +#190'U'#255#239#190'U'#255#239#190'U'#255#239#190'U'#255#239#190'U'#255#239

-  +#190'U'#255#239#190'U'#255#239#190'U'#255#239#190'U'#255#239#190'U'#255#239

-  +#190'U'#255#239#190'U'#255#239#190'U'#255#239#190'U'#255#239#190'U'#255#239

-  +#190'U'#255#238#190'U'#255#238#190'U'#255#238#190'U'#255#238#189'U'#255#237

-  +#188'U'#255#236#186'T'#255#234#184'T'#255#231#180'S'#255#226#175'Q'#255#220

-  +#166'N'#255#212#156'K'#255#202#143'F'#255#190#129'A'#255#179'r<'#255#145'Y0'

-  +#255'k?"'#255'c7'#31#255'[4 '#248'IC@'#209'DDD'#200'CCC'#190'CCC'#163'DDDtCC'

-  +'CAEEE'#26'III'#7#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#1'III'#7'==='#25'AAA?CCCsBBB'#162'K?;'#202'^4'#30#252'c8 '#255'oB'

-  +'%'#255#160'b5'#255#183'v>'#255#194#134'D'#255#205#148'J'#255#215#161'M'#255

-  +#223#171'Q'#255#228#177'T'#255#232#182'U'#255#234#185'V'#255#236#187'V'#255

-  +#236#187'V'#255#237#188'W'#255#237#189'W'#255#237#188'W'#255#237#188'W'#255

-  +#237#188'W'#255#237#188'W'#255#237#188'W'#255#237#188'W'#255#237#188'W'#255

-  +#237#188'W'#255#237#188'W'#255#237#188'W'#255#237#188'W'#255#237#188'W'#255

-  +#237#188'W'#255#237#188'W'#255#237#188'W'#255#237#188'W'#255#237#188'W'#255

-  +#159'~:'#255#16#13#6#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#1#0#0#255#16#13#6#255'9-'#21#255'y`,'#255#208#165'M'

-  +#255#237#188'W'#255#237#188'W'#255#237#188'W'#255#237#188'W'#255#237#188'W'

-  +#255#237#188'W'#255#237#188'W'#255#237#188'W'#255#237#188'W'#255#237#188'W'

-  +#255#237#188'W'#255#237#188'W'#255#237#188'W'#255#237#188'W'#255#237#188'W'

-  +#255#237#188'W'#255#237#189'W'#255#237#188'W'#255#236#187'V'#255#235#186'V'

-  +#255#234#184'U'#255#231#180'U'#255#227#175'R'#255#221#169'Q'#255#213#157'L'

-  +#255#202#144'H'#255#191#129'C'#255#180's='#255#145'X0'#255'j>#'#255'b6'#31

-  +#255'Z5#'#245'GDB'#206'DDD'#200'CCC'#186'DDD'#154'EEEhBBB6@@@'#20'333'#5#0#0

-  +#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'@@@'#4'GGG'#18'AAA3DDDf'

-  +'CCC'#152'IA>'#195'\4 '#250'c7'#31#255'm@#'#255#156'_5'#255#182'v?'#255#194

-  +#134'E'#255#206#148'K'#255#215#161'O'#255#223#171'S'#255#228#178'V'#255#232

-  +#182'V'#255#233#185'X'#255#234#186'X'#255#235#187'X'#255#236#187'X'#255#236

-  +#188'X'#255#236#188'X'#255#236#188'X'#255#236#188'X'#255#236#188'X'#255#236

-  +#188'X'#255#236#188'X'#255#236#188'X'#255#236#188'X'#255#236#188'X'#255#236

-  +#188'X'#255#236#188'X'#255#236#188'X'#255#236#188'X'#255#236#188'X'#255#236

-  +#188'X'#255#236#188'X'#255#236#188'X'#255'9-'#21#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#6#5#2#255'$'#29#14#255'ZH"'#255

-  +#169#135'@'#255#234#186'X'#255#236#188'X'#255#236#188'X'#255#236#188'X'#255

-  +#236#188'X'#255#236#188'X'#255#236#188'X'#255#236#188'X'#255#236#188'X'#255

-  +#236#188'X'#255#236#188'X'#255#236#188'X'#255#236#188'X'#255#236#187'X'#255

-  +#235#187'Y'#255#235#186'X'#255#234#186'X'#255#233#184'W'#255#231#180'W'#255

-  +#227#176'T'#255#221#169'R'#255#213#158'N'#255#203#145'J'#255#192#130'D'#255

-  +#179's='#255#139'U/'#255'i="'#255'a5'#30#255'X7%'#242'FDC'#204'CCC'#198'DDD'

-  +#180'CCC'#144'CCC[AAA+III'#14'UUU'#3#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  ,#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#128#128#128#2'@@@'#12'AAA''CCCWCCC'#140'GB?'#185'Z4!'#247'a6'#30#255'j?"'

-  +#255#151']2'#255#181't>'#255#194#133'F'#255#206#149'M'#255#215#161'Q'#255#223

-  +#170'T'#255#228#178'W'#255#231#181'Y'#255#232#183'Y'#255#233#185'Z'#255#234

-  +#185'Y'#255#234#186'Y'#255#234#186'Y'#255#234#186'Z'#255#234#186'Z'#255#234

-  +#186'Z'#255#234#186'Z'#255#234#186'Z'#255#234#186'Z'#255#234#186'Z'#255#234

-  +#186'Z'#255#234#186'Z'#255#234#186'Z'#255#234#186'Z'#255#234#186'Z'#255#234

-  +#186'Z'#255#234#186'Z'#255#234#186'Z'#255#234#186'Z'#255#234#186'Z'#255'C5'

-  +#26#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#1#255#20#16#8#255'K;'#29

-  +#255#189#150'I'#255#234#186'Z'#255#234#186'Z'#255#234#186'Z'#255#234#186'Z'

-  +#255#234#186'Z'#255#234#186'Z'#255#234#186'Z'#255#234#186'Z'#255#234#186'Z'

-  +#255#234#186'Z'#255#234#186'Y'#255#234#186'Y'#255#234#185'Y'#255#233#185'Z'

-  +#255#232#183'Y'#255#230#181'X'#255#226#176'W'#255#221#169'T'#255#213#159'O'

-  +#255#203#145'K'#255#190#129'D'#255#178'p='#255#134'Q-'#255'g;"'#255'`4'#29

-  +#255'U7('#238'CCC'#202'DDD'#195'DDD'#173'BBB'#131'CCCL@@@ 999'#9#0#0#0#2#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#1'III'#7'BBB'#27'CCCEDDD|DDD'#170'V6%'#238'`5'#30

-  +#255'h<"'#255#145'Y1'#255#179's?'#255#192#132'F'#255#205#148'M'#255#215#161

-  +'R'#255#222#171'U'#255#227#176'X'#255#230#181'Z'#255#231#183'Z'#255#232#183

-  +'['#255#232#184'['#255#233#185'['#255#233#185'['#255#233#185'['#255#233#185

-  +'['#255#233#185'['#255#233#185'['#255#233#185'['#255#233#185'['#255#233#185

-  +'['#255#233#185'['#255#233#185'['#255#233#185'['#255#233#185'['#255#233#185

-  +'['#255#233#185'['#255#233#185'['#255#233#185'['#255#233#185'['#255#233#185

-  +'['#255'd1'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#13#11#5#255'N>'#31#255#193#153'L'#255#233

-  +#185'['#255#233#185'['#255#233#185'['#255#233#185'['#255#233#185'['#255#233

-  +#185'['#255#233#185'['#255#233#185'['#255#233#185'['#255#233#184'['#255#232

-  +#184'['#255#232#183'Z'#255#231#182'Z'#255#229#180'Y'#255#226#175'X'#255#220

-  +#168'U'#255#213#158'R'#255#201#144'K'#255#189'D'#255#175'n<'#255#128'N+'#255

-  +'e9 '#255'_3'#29#255'Q;1'#227'CCC'#201'CCC'#191'CCC'#163'CCCrAAA;FFF'#22'333'

-  +#5#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#1'@@@'#4'<<<'#17'AAA3EEEhCCC'#156'R:/'#220'_3'#29

-  +#255'e: '#255#134'Q-'#255#178'p>'#255#190#130'F'#255#203#145'M'#255#213#161

-  +'S'#255#221#170'W'#255#226#176'Z'#255#229#180'Z'#255#230#182'\'#255#231#182

-  +'\'#255#231#183'\'#255#231#183'\'#255#231#183'\'#255#231#183'\'#255#231#183

-  +'\'#255#231#183'\'#255#231#183'\'#255#231#183'\'#255#231#183'\'#255#231#183

-  +'\'#255#231#183'\'#255#231#183'\'#255#231#183'\'#255#231#183'\'#255#231#183

-  +'\'#255#231#183'\'#255#231#183'\'#255#231#183'\'#255#231#183'\'#255#231#183

-  +'\'#255#180#142'G'#255#1#1#1#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#15#12#6#255'VE"'#255#215#171'V'#255#231#183'\'#255#231#183'\'#255#231

-  +#183'\'#255#231#183'\'#255#231#183'\'#255#231#183'\'#255#231#183'\'#255#231

-  +#183'\'#255#231#183'\'#255#231#182'\'#255#230#181'['#255#228#178'['#255#225

-  +#175'Y'#255#219#168'W'#255#211#156'Q'#255#200#142'K'#255#187'|D'#255#172'k<'

-  +#255'wF('#255'c7 '#255'^1'#30#254'K@:'#214'DDD'#199'CCC'#184'CCC'#148'AAA^GG'

-  +'G+NNN'#13'UUU'#3#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#2'MMM'#10'@@@$DDDSDDD'#139'L?8'#197'^1'#29#254

-  +'c7 '#255'wG)'#255#173'l<'#255#188'E'#255#201#144'N'#255#212#158'T'#255#220

-  +#169'X'#255#225#175'['#255#228#179'\'#255#229#180'\'#255#230#181']'#255#230

-  +#181']'#255#230#182']'#255#230#182']'#255#230#182']'#255#230#182']'#255#230

-  +#182']'#255#230#182']'#255#230#182']'#255#230#182']'#255#230#182']'#255#230

-  +#182']'#255#230#182']'#255#230#182']'#255#230#182']'#255#230#182']'#255#230

-  ,#182']'#255#230#182']'#255#230#182']'#255#230#182']'#255#230#182']'#255#221

-  +#175'Y'#255#10#8#4#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#1#1#1#255'4)'#21#255#184#146'J'#255#230#182']'#255

-  +#230#182']'#255#230#182']'#255#230#182']'#255#230#182']'#255#230#182']'#255

-  +#230#182']'#255#230#181']'#255#229#182']'#255#228#180']'#255#227#178'\'#255

-  +#224#174'['#255#218#166'W'#255#209#155'R'#255#198#139'K'#255#184'zC'#255#162

-  +'d8'#255'l@$'#255'a5'#30#255'Z3 '#249'FBA'#206'DDD'#196'CCC'#174'CCC'#129'BB'

-  +'BIFFF'#29'@@@'#8#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#1'333'#5'FFF'#22'BBB>CCCvFA?'#174'Z3'#31#248'`5'#30

-  +#255'l@$'#255#163'd9'#255#184'{D'#255#199#142'N'#255#210#156'T'#255#218#167

-  +'Y'#255#223#174'\'#255#226#177'^'#255#228#179'^'#255#228#179'^'#255#228#180

-  +'_'#255#228#180'_'#255#228#180'_'#255#228#180'_'#255#228#180'_'#255#228#180

-  +'_'#255#228#180'_'#255#228#180'_'#255#228#180'_'#255#228#180'_'#255#228#180

-  +'_'#255#228#180'_'#255#228#180'_'#255#228#180'_'#255#228#180'_'#255#228#180

-  +'_'#255#228#180'_'#255#228#180'_'#255#228#180'_'#255#228#180'_'#255#228#180

-  +'_'#255'bM)'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#27#21#11#255#140

-  +'o;'#255#228#180'_'#255#228#180'_'#255#228#180'_'#255#228#180'_'#255#228#180

-  +'_'#255#228#180'_'#255#228#180'_'#255#228#179'^'#255#227#179'^'#255#226#176

-  +']'#255#222#172'['#255#216#164'X'#255#207#152'R'#255#195#137'K'#255#181'tA'

-  +#255#149'Z3'#255'g<"'#255'_3'#29#255'V6'''#239'CCC'#201'CCC'#190'CCC'#159'CC'

-  +'CkCCC5GGG'#18'@@@'#4#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#2'@@@'#12'CCC*DDD^DDD'#150'U6('#230'^2'#28#255'f:!'#255

-  +#149'Z3'#255#181'uB'#255#195#137'K'#255#208#154'T'#255#216#165'Y'#255#221#172

-  +']'#255#225#176'^'#255#226#178'_'#255#227#178'_'#255#227#178'_'#255#227#179

-  +'_'#255#227#179'_'#255#227#179'_'#255#227#179'_'#255#227#179'_'#255#227#179

-  +'_'#255#227#179'_'#255#227#179'_'#255#227#179'_'#255#227#179'_'#255#227#179

-  +'_'#255#227#179'_'#255#227#179'_'#255#227#179'_'#255#227#179'_'#255#227#179

-  +'_'#255#227#179'_'#255#227#179'_'#255#227#179'_'#255#227#179'_'#255#227#179

-  +'_'#255#13#10#6#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255'&'#30#16#255#208#164'W'#255#227#179'_'#255#227#179'_'#255#227#179'_'

-  +#255#227#179'_'#255#227#178'_'#255#227#178'_'#255#226#179'`'#255#226#177'_'

-  +#255#224#176'^'#255#220#170'\'#255#214#162'X'#255#205#149'R'#255#191#131'I'

-  +#255#177'p?'#255#131'O.'#255'c7 '#255']1'#29#255'N;3'#222'DDD'#199'DDD'#181

-  +'CCC'#140'DDDSDDD"999'#9#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#1'+++'#6'@@@'#24'EEECCCC~K=7'#193']0'#28#255'b6'#31#255#129'M'

-  +','#255#177'o@'#255#191#131'J'#255#204#149'S'#255#214#163'Z'#255#220#171']'

-  +#255#223#174'_'#255#225#176'`'#255#225#177'`'#255#226#177'`'#255#226#177'`'

-  +#255#226#177'`'#255#226#177'`'#255#226#177'`'#255#226#177'`'#255#226#177'`'

-  +#255#226#177'`'#255#226#177'`'#255#226#177'`'#255#226#177'`'#255#226#177'`'

-  +#255#226#177'`'#255#226#177'`'#255#226#177'`'#255#226#177'`'#255#226#177'`'

-  +#255#226#177'`'#255#226#177'`'#255#226#177'`'#255#226#177'`'#255#226#177'`'

-  +#255#161'~D'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  ,#255#0#0#0#255#0#0#0#255#4#3#2#255#134'i9'#255#226#177'`'#255#226#177'`'#255

-  +#226#177'`'#255#226#177'`'#255#226#177'`'#255#225#178'`'#255#225#177'`'#255

-  +#224#176'_'#255#222#174'^'#255#219#169']'#255#212#159'X'#255#200#144'Q'#255

-  +#187'~G'#255#170'j<'#255'qB%'#255'`5'#30#255'Z2'#31#250'FBA'#205'CCC'#193'DD'

-  +'D'#166'CCCsCCC9CCC'#19'@@@'#4#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#2'@@@'#12'AAA+BBBaDBB'#155'Y3!'#244'_3'#29#255'l?$'#255#167'f;'

-  +#255#186'}H'#255#200#144'R'#255#211#158'Y'#255#218#168']'#255#221#172'`'#255

-  +#223#174'a'#255#223#176'a'#255#224#176'b'#255#224#176'b'#255#224#176'b'#255

-  +#224#176'b'#255#224#176'b'#255#224#176'b'#255#224#176'b'#255#224#176'b'#255

-  +#224#176'b'#255#224#176'b'#255#224#176'b'#255#224#176'b'#255#224#176'b'#255

-  +#224#176'b'#255#224#176'b'#255#224#176'b'#255#224#176'b'#255#224#176'b'#255

-  +#224#176'b'#255#224#176'b'#255#224#176'b'#255#224#176'b'#255#224#176'b'#255

-  +'hR.'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'qY2'#255#224#176'b'#255#224#176'b'#255

-  +#224#176'b'#255#224#176'b'#255#224#176'b'#255#224#176'b'#255#223#176'a'#255

-  +#223#174'`'#255#221#172'_'#255#217#166']'#255#208#155'W'#255#196#139'O'#255

-  +#182'wE'#255#152'\5'#255'e9!'#255'^2'#29#255'S7*'#233'DDD'#200'CCC'#183'DDD'

-  +#143'EEEUDDD"UUU'#9#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'333'#5

-  +'@@@'#24'DDDDCCC~P8-'#211']0'#29#255'c8!'#255#144'V2'#255#180'uD'#255#196#138

-  +'P'#255#207#155'X'#255#215#165']'#255#220#171'`'#255#221#173'b'#255#222#174

-  +'a'#255#222#175'b'#255#222#175'b'#255#222#175'b'#255#222#175'b'#255#222#175

-  +'b'#255#222#175'b'#255#222#175'b'#255#222#175'b'#255#222#175'b'#255#222#175

-  +'b'#255#222#175'b'#255#222#175'b'#255#222#175'b'#255#222#175'b'#255#222#175

-  +'b'#255#222#175'b'#255#222#175'b'#255#222#175'b'#255#222#175'b'#255#222#175

-  +'b'#255#222#175'b'#255#222#175'b'#255#222#175'b'#255#222#175'b'#255'VD&'#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#1#1#0#255#166#132'J'#255#222#175'b'#255#222#175

-  +'b'#255#222#175'b'#255#222#175'b'#255#222#175'b'#255#222#175'b'#255#222#174

-  +'a'#255#221#173'a'#255#219#170'`'#255#213#163'\'#255#205#150'V'#255#192#132

-  +'M'#255#176'oA'#255'}K,'#255'a5'#31#255'\0'#28#254'I?;'#212'CCC'#194'DDD'#166

-  +'CCCrDDD8GGG'#18'@@@'#4#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#2'FFF'#11'D'

-  +'DD)BBB`G@<'#164'[1'#29#252'_3'#29#255'uD)'#255#174'l?'#255#189#130'L'#255

-  +#204#150'V'#255#213#162'^'#255#217#168'`'#255#220#172'b'#255#221#173'b'#255

-  +#221#173'c'#255#221#173'c'#255#221#173'c'#255#221#173'c'#255#221#173'c'#255

-  +#221#173'c'#255#221#173'c'#255#221#173'c'#255#221#173'c'#255#221#173'c'#255

-  +#221#173'c'#255#221#173'c'#255#221#173'c'#255#221#173'c'#255#221#173'c'#255

-  +#221#173'c'#255#221#173'c'#255#221#173'c'#255#221#173'c'#255#221#173'c'#255

-  +#221#173'c'#255#221#173'c'#255#221#173'c'#255#221#173'c'#255#221#173'c'#255

-  +'gQ.'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#8#6#4#255#207#163']'#255#221

-  +#173'c'#255#221#173'c'#255#221#173'c'#255#221#173'c'#255#221#173'c'#255#221

-  +#173'c'#255#221#172'b'#255#219#171'b'#255#217#167'`'#255#211#159'['#255#200

-  +#145'T'#255#185'{H'#255#164'd;'#255'h<#'#255'^1'#29#255'X3#'#244'DDD'#200'CC'

-  +'C'#183'DDD'#142'DDDSFFF!@@@'#8#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'@@@'#4'II'

-  +'I'#21'EEE?DDD|S5'''#222'\1'#28#255'c8!'#255#158'_8'#255#183'yH'#255#198#142

-  ,'S'#255#209#158'\'#255#215#166'`'#255#218#170'b'#255#219#171'c'#255#219#171

-  +'d'#255#219#172'd'#255#219#172'd'#255#219#172'd'#255#219#172'd'#255#219#172

-  +'d'#255#219#172'd'#255#219#172'd'#255#219#172'd'#255#219#172'd'#255#219#172

-  +'d'#255#219#172'd'#255#219#172'd'#255#219#172'd'#255#219#172'd'#255#219#172

-  +'d'#255#219#172'd'#255#219#172'd'#255#219#172'd'#255#219#172'd'#255#219#172

-  +'d'#255#219#172'd'#255#219#172'd'#255#219#172'd'#255#219#172'd'#255#219#172

-  +'d'#255#213#168'b'#255#3#3#1#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'M<#'#255

-  +#219#172'd'#255#219#172'd'#255#219#172'd'#255#219#172'd'#255#219#172'd'#255

-  +#219#172'd'#255#219#171'd'#255#219#170'c'#255#217#168'b'#255#214#164'`'#255

-  +#207#153'Z'#255#194#136'P'#255#178'rD'#255#139'R1'#255'a4'#31#255'\0'#28#255

-  +'L<5'#219'CCC'#193'DDD'#164'CCCoCCC5@@@'#16'UUU'#3#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#1'@@@'#8'BBB#CCCWHA='#158'\0'#28#253'_3'#29#255'~I+'#255#175'nB'#255#191#133

-  +'O'#255#205#151'Z'#255#213#163'a'#255#216#167'c'#255#217#169'c'#255#218#170

-  +'d'#255#218#170'd'#255#218#170'd'#255#218#170'd'#255#218#170'd'#255#218#170

-  +'d'#255#218#170'd'#255#218#170'd'#255#218#170'd'#255#218#170'd'#255#218#170

-  +'d'#255#218#170'd'#255#218#170'd'#255#218#170'd'#255#218#170'd'#255#218#170

-  +'d'#255#218#170'd'#255#218#170'd'#255#218#170'd'#255#218#170'd'#255#218#170

-  +'d'#255#218#170'd'#255#218#170'd'#255#218#170'd'#255#218#170'd'#255#218#170

-  +'d'#255#218#170'd'#255#218#170'd'#255'YF)'#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#15#12#7#255#218#170'd'#255#218#170'd'#255#218#170'd'#255#218#170'd'

-  +#255#218#170'd'#255#218#170'd'#255#218#170'd'#255#218#169'c'#255#217#168'c'

-  +#255#215#166'b'#255#211#160'_'#255#201#147'W'#255#187'L'#255#169'h>'#255'l='

-  +'$'#255'^1'#29#255'W3"'#244'DDD'#199'DDD'#180'DDD'#136'DDDKBBB'#27'UUU'#6#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0'UUU'#3'DDD'#15'EEE4BBBpS6)'#215'\1'#28#255'b7!'#255#158'_9'

-  +#255#184'{J'#255#199#144'W'#255#209#158'_'#255#214#165'c'#255#216#168'd'#255

-  +#217#168'd'#255#217#168'd'#255#217#168'd'#255#217#168'd'#255#217#168'd'#255

-  +#217#168'd'#255#217#168'd'#255#217#168'd'#255#217#168'd'#255#217#168'd'#255

-  +#217#168'd'#255#217#168'd'#255#217#168'd'#255#217#168'd'#255#217#168'd'#255

-  +#217#168'd'#255#217#168'd'#255#217#168'd'#255#217#168'd'#255#217#168'd'#255

-  +#217#168'd'#255#217#168'd'#255#217#168'd'#255#217#168'd'#255#217#168'd'#255

-  +#217#168'd'#255#217#168'd'#255#217#168'd'#255#217#168'd'#255#217#168'd'#255

-  +'YD)'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#189#147'W'#255#217#168'd'

-  +#255#217#168'd'#255#217#168'd'#255#217#168'd'#255#217#168'd'#255#217#168'd'

-  +#255#217#168'd'#255#216#168'd'#255#215#167'c'#255#213#164'a'#255#207#154']'

-  +#255#195#138'S'#255#179'tF'#255#139'R1'#255'`5'#30#255'\0'#28#255'K=7'#216'D'

-  +'DD'#191'DDD'#157'CCCcAAA+FFF'#11#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'+++'#6'EEE'#26'BBBIE?='

-  +#144'Z0'#28#252'^2'#29#255'zF*'#255#174'mB'#255#192#135'R'#255#205#152'\'#255

-  +#211#161'b'#255#214#165'd'#255#215#166'd'#255#215#166'd'#255#215#166'd'#255

-  +#215#166'd'#255#215#166'd'#255#215#166'd'#255#215#166'd'#255#215#166'd'#255

-  +#215#166'd'#255#215#166'd'#255#215#166'd'#255#215#166'd'#255#215#166'd'#255

-  +#215#166'd'#255#215#166'd'#255#215#166'd'#255#215#166'd'#255#215#166'd'#255

-  +#215#166'd'#255#215#166'd'#255#215#166'd'#255#215#166'd'#255#215#166'd'#255

-  +#215#166'd'#255#215#166'd'#255#215#166'd'#255#215#166'd'#255#215#166'd'#255

-  ,#215#166'd'#255#215#166'd'#255#215#166'd'#255#215#166'd'#255'jR1'#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255'fN/'#255#215#166'd'#255#215#166'd'#255#215#166'd'

-  +#255#215#166'd'#255#215#166'd'#255#215#166'd'#255#215#166'd'#255#215#166'd'

-  +#255#215#167'e'#255#214#164'c'#255#210#159'a'#255#202#148'Z'#255#187#128'N'

-  +#255#168'f='#255'i;#'#255'\1'#28#255'V3#'#242'CCC'#197'DDD'#173'BBB{FFF>@@@'

-  +#20'@@@'#4#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#1'MMM'#10'FFF(CCC_R6*'#206'\0'#28#255'`5'#31#255#154'\8'

-  +#255#182'yJ'#255#199#144'Y'#255#208#156'`'#255#212#163'd'#255#213#165'd'#255

-  +#214#165'e'#255#214#165'e'#255#214#165'e'#255#214#165'e'#255#214#165'e'#255

-  +#214#165'e'#255#214#165'e'#255#214#165'e'#255#214#165'e'#255#214#165'e'#255

-  +#214#165'e'#255#214#165'e'#255#214#165'e'#255#214#165'e'#255#214#165'e'#255

-  +#214#165'e'#255#214#165'e'#255#214#165'e'#255#214#165'e'#255#214#165'e'#255

-  +#214#165'e'#255#214#165'e'#255#214#165'e'#255#214#165'e'#255#214#165'e'#255

-  +#214#165'e'#255#214#165'e'#255#214#165'e'#255#214#165'e'#255#214#165'e'#255

-  +#214#165'e'#255#214#165'e'#255#214#165'e'#255#135'h@'#255#2#1#1#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'b'

-  +'L/'#255#214#165'e'#255#214#165'e'#255#214#165'e'#255#214#165'e'#255#214#165

-  +'e'#255#214#165'e'#255#214#165'e'#255#214#165'e'#255#213#165'e'#255#213#164

-  +'d'#255#211#161'c'#255#206#154'^'#255#194#139'U'#255#177'sF'#255#135'P/'#255

-  +'^3'#29#255'[/'#27#255'J>9'#213'DDD'#185'CCC'#144'DDDSHHH III'#7#0#0#0#1#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'UUU'#3

-  +'<<<'#17'DDD8DBAyZ0'#29#248'\1'#28#255'uB'''#255#172'lA'#255#190#132'Q'#255

-  +#203#151'^'#255#209#160'c'#255#211#162'e'#255#212#163'e'#255#212#163'e'#255

-  +#212#163'e'#255#212#163'e'#255#212#163'e'#255#212#163'e'#255#212#163'e'#255

-  +#212#163'e'#255#212#163'e'#255#212#163'e'#255#212#163'e'#255#212#163'e'#255

-  +#212#163'e'#255#212#163'e'#255#212#163'e'#255#212#163'e'#255#212#163'e'#255

-  +#212#163'e'#255#212#163'e'#255#212#163'e'#255#212#163'e'#255#212#163'e'#255

-  +#212#163'e'#255#212#163'e'#255#212#163'e'#255#212#163'e'#255#212#163'e'#255

-  +#212#163'e'#255#212#163'e'#255#212#163'e'#255#212#163'e'#255#212#163'e'#255

-  +#212#163'e'#255#212#163'e'#255#212#163'e'#255#156'xK'#255#1#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'jQ2'#255#212

-  +#163'e'#255#212#163'e'#255#212#163'e'#255#212#163'e'#255#212#163'e'#255#212

-  +#163'e'#255#212#163'e'#255#212#163'e'#255#212#163'e'#255#212#162'e'#255#211

-  +#162'd'#255#208#158'b'#255#200#147'['#255#186'~M'#255#165'd<'#255'd8!'#255'\'

-  +'0'#28#255'T5'''#237'CCC'#193'DDD'#162'CCCjCCC.@@@'#12#0#0#0#2#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'333'#5'GGG'#25'BB'

-  +'BIN:1'#174'[/'#27#255'^2'#29#255#145'T3'#255#180'vI'#255#196#142'Y'#255#206

-  +#155'a'#255#209#161'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211

-  +#162'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211

-  +#162'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211

-  +#162'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211

-  +#162'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211

-  +#162'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211

-  +#162'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211

-  +#162'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255'O=&'#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  ,#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'sX7'#255#211

-  +#162'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211

-  +#162'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#210

-  +#161'f'#255#209#159'd'#255#204#153'`'#255#192#135'U'#255#175'pE'#255'~H+'#255

-  +']0'#29#255'Z0'#28#253'FBA'#201'DDD'#176'CCC~FFF>CCC'#19'@@@'#4#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'999'#9'EEE%CCC[V3'

-  +'$'#222'\0'#27#255'f7!'#255#167'd>'#255#187#128'Q'#255#200#148'^'#255#207#156

-  +'d'#255#209#159'e'#255#209#160'e'#255#209#160'e'#255#209#160'e'#255#209#160

-  +'e'#255#209#160'e'#255#209#160'e'#255#209#160'e'#255#209#160'e'#255#209#160

-  +'e'#255#209#160'e'#255#209#160'e'#255#209#160'e'#255#209#160'e'#255#209#160

-  +'e'#255#209#160'e'#255#209#160'e'#255#151'tI'#255'2&'#25#255#27#21#13#255#15

-  +#12#7#255#7#5#3#255#7#5#3#255' '#25#16#255'P='''#255#151'tI'#255#209#160'e'

-  +#255#209#160'e'#255#209#160'e'#255#209#160'e'#255#209#160'e'#255#209#160'e'

-  +#255#209#160'e'#255#209#160'e'#255#209#160'e'#255#209#160'e'#255#209#160'e'

-  +#255#209#160'e'#255#209#160'e'#255#6#5#3#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#146'pF'#255#209#160'e'#255#209#160'e'#255

-  +#209#160'e'#255#209#160'e'#255#209#160'e'#255#209#160'e'#255#209#160'e'#255

-  +#209#160'e'#255#209#160'e'#255#209#160'e'#255#209#160'e'#255#209#159'e'#255

-  +#206#155'c'#255#198#143'['#255#182'zM'#255#151'Y6'#255'^3'#29#255'[/'#27#255

-  +'N:1'#222'DDD'#185'DDD'#142'DDDOIII'#28'+++'#6#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#2';;;'#13'DDD1F@=x[/'#28#253'\1'#28

-  +#255'~G+'#255#174'oE'#255#193#137'X'#255#203#151'b'#255#207#156'e'#255#207

-  +#158'f'#255#208#158'f'#255#208#158'f'#255#208#158'f'#255#208#158'f'#255#208

-  +#158'f'#255#208#158'f'#255#208#158'f'#255#208#158'f'#255#208#158'f'#255#208

-  +#158'f'#255#208#158'f'#255#208#158'f'#255#208#158'f'#255#208#158'f'#255'x[;'

-  +#255#15#12#8#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#4#3#2#255'bJ0'#255#208#158'f'#255#208#158'f'

-  +#255#208#158'f'#255#208#158'f'#255#208#158'f'#255#208#158'f'#255#208#158'f'

-  +#255#208#158'f'#255#208#158'f'#255#208#158'f'#255#208#158'f'#255'3'''#25#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#2#2#1#255#208#158'f'

-  +#255#208#158'f'#255#208#158'f'#255#208#158'f'#255#208#158'f'#255#208#158'f'

-  +#255#208#158'f'#255#208#158'f'#255#208#158'f'#255#208#158'f'#255#208#158'f'

-  +#255#208#158'f'#255#207#158'e'#255#206#156'd'#255#201#149'_'#255#189#131'T'

-  +#255#169'h@'#255'j:#'#255'\0'#28#255'V3#'#242'DDD'#191'CCC'#156'BBB`AAA''333'

-  +#10#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'@@@'#4'C'

-  +'CC'#19'EEE?Q7+'#179'[/'#27#255'^1'#29#255#152'X6'#255#181'yM'#255#197#144']'

-  +#255#204#153'c'#255#206#155'f'#255#206#156'e'#255#206#156'e'#255#206#156'e'

-  +#255#206#156'e'#255#206#156'e'#255#206#156'e'#255#206#156'e'#255#206#156'e'

-  +#255#206#156'e'#255#206#156'e'#255#206#156'e'#255#206#156'e'#255#206#156'e'

-  +#255#202#154'c'#255'6)'#27#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#14#10#7#255#178#135'W'#255#206#156'e'#255#206#156'e'#255#206#156'e'

-  +#255#206#156'e'#255#206#156'e'#255#206#156'e'#255#206#156'e'#255#206#156'e'

-  +#255#206#156'e'#255'WB+'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#29#22#14#255#206#156'e'#255#206#156'e'#255#206#156'e'#255#206#156'e'

-  +#255#206#156'e'#255#206#156'e'#255#206#156'e'#255#206#156'e'#255#206#156'e'

-  +#255#206#156'e'#255#206#156'e'#255#206#156'e'#255#206#156'e'#255#205#156'e'

-  ,#255#203#151'c'#255#194#139'Z'#255#176'qH'#255#131'K-'#255'\1'#28#255'[/'#27

-  +#255'HA='#204'DDD'#169'DDDqEEE4III'#14#128#128#128#2#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'+++'#6'BBB'#27'EEENW2!'#228'[/'#27#255'i9!'

-  +#255#168'e?'#255#187#130'T'#255#200#147'`'#255#203#153'e'#255#205#154'e'#255

-  +#205#154'e'#255#205#154'e'#255#205#154'e'#255#205#154'e'#255#205#154'e'#255

-  +#205#154'e'#255#205#154'e'#255#205#154'e'#255#205#154'e'#255#205#154'e'#255

-  +#205#154'e'#255#205#154'e'#255#199#150'a'#255#19#14#9#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#7#6#4#255#178#133'W'

-  +#255#205#154'e'#255#205#154'e'#255#205#154'e'#255#205#154'e'#255#205#154'e'

-  +#255#205#154'e'#255#205#154'e'#255#205#154'e'#255'S>)'#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#128'a?'#255#205#154'e'#255#205#154'e'

-  +#255#205#154'e'#255#205#154'e'#255#205#154'e'#255#205#154'e'#255#205#154'e'

-  +#255#205#154'e'#255#205#154'e'#255#205#154'e'#255#205#154'e'#255#205#154'e'

-  +#255#205#154'e'#255#204#154'e'#255#203#152'd'#255#197#145'^'#255#182'zO'#255

-  +#157'Z8'#255']1'#29#255'[/'#27#255'P9.'#226'CCC'#179'CCC'#129'CCCA@@@'#20'@@'

-  +'@'#4#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'999'#9'EEE%EA?'

-  +'b[/'#27#253'\0'#27#255'F*'#255#173'nG'#255#191#136'Z'#255#200#149'c'#255

-  +#202#152'e'#255#202#152'e'#255#202#153'e'#255#202#153'e'#255#202#153'e'#255

-  +#202#153'e'#255#202#153'e'#255#202#153'e'#255#202#153'e'#255#202#153'e'#255

-  +#202#153'e'#255#202#153'e'#255#202#153'e'#255#202#153'e'#255#31#24#16#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#11#8#6#255#200#151'e'#255#202#153'e'#255#202#153'e'#255#202

-  +#153'e'#255#202#153'e'#255#202#153'e'#255#202#153'e'#255#202#153'e'#255'O;'''

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255''''#29#19#255#202#153'e'#255

-  +#202#153'e'#255#202#153'e'#255#202#153'e'#255#202#153'e'#255#202#153'e'#255

-  +#202#153'e'#255#202#153'e'#255#202#153'e'#255#202#153'e'#255#202#153'e'#255

-  +#202#153'e'#255#202#153'e'#255#202#153'e'#255#202#152'e'#255#202#151'd'#255

-  +#199#147'b'#255#188#130'U'#255#168'e@'#255'k:"'#255'[/'#27#255'V3$'#241'CCC'

-  +#186'BBB'#142'EEENBBB'#27'+++'#6#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#2'@@@'#12'DDD-O:0'#146'[/'#27#255'\0'#28#255#145'R2'#255#179'wM'

-  +#255#194#141'^'#255#200#148'c'#255#201#150'e'#255#201#150'e'#255#201#150'e'

-  +#255#201#150'e'#255#201#150'e'#255#201#150'e'#255#201#150'e'#255#201#150'e'

-  +#255#201#150'e'#255#201#150'e'#255#201#150'e'#255#201#150'e'#255#201#150'e'

-  +#255';,'#30#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'S>*'#255#201#150'e'

-  +#255#201#150'e'#255#201#150'e'#255#201#150'e'#255#201#150'e'#255#201#150'e'

-  +#255#201#150'e'#255'K8&'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#29#22#14#255

-  +#184#138']'#255#201#150'e'#255#201#150'e'#255#201#150'e'#255#201#150'e'#255

-  +#201#150'e'#255#201#150'e'#255#201#150'e'#255#201#150'e'#255#201#150'e'#255

-  +#201#150'e'#255#201#150'e'#255#201#150'e'#255#201#150'e'#255#201#150'e'#255

-  +#201#150'e'#255#200#149'd'#255#199#148'c'#255#191#136'['#255#173'mG'#255'}D('

-  +#255'\0'#27#255'Z0'#28#253'DBB'#192'CCC'#152'DDDZBBB#@@@'#8#0#0#0#1#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#128#128#128#2'III'#14'CCC5T5'''#190'[/'#27#255

-  +'_2'#29#255#162'^:'#255#184'}S'#255#195#142'`'#255#198#147'd'#255#199#148'd'

-  +#255#199#148'd'#255#199#148'd'#255#199#148'd'#255#199#148'd'#255#199#148'd'

-  +#255#199#148'd'#255#199#148'd'#255#199#148'd'#255#199#148'd'#255#199#148'd'

-  ,#255#199#148'd'#255#147'mJ'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#3#2#1#255#195#144'b'#255#199#148'd'#255#199#148'd'#255#199#148'd'#255

-  +#199#148'd'#255#199#148'd'#255#199#148'd'#255'H6$'#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#3#2#1#255#23#17#12

-  +#255'qT8'#255#199#148'd'#255#199#148'd'#255#199#148'd'#255#199#148'd'#255#199

-  +#148'd'#255#199#148'd'#255#199#148'd'#255#199#148'd'#255#199#148'd'#255#199

-  +#148'd'#255#199#148'd'#255#199#148'd'#255#199#148'd'#255#199#148'd'#255#199

-  +#148'd'#255#199#148'd'#255#199#148'd'#255#199#148'd'#255#198#146'c'#255#193

-  +#140'^'#255#178'uN'#255#143'O1'#255'\0'#28#255'[/'#27#255'J=7'#208'CCC'#161

-  +'DDDfAAA+FFF'#11#0#0#0#2#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'UUU'#3'<<<'

-  +#17'CCC=X1 '#227'[/'#27#255'm:"'#255#167'eA'#255#187#130'X'#255#195#143'a'

-  +#255#196#144'c'#255#197#145'c'#255#197#145'c'#255#197#145'c'#255#197#145'c'

-  +#255#197#145'c'#255#197#145'c'#255#197#145'c'#255#197#145'c'#255#197#145'c'

-  +#255#197#145'c'#255#197#145'c'#255#197#145'c'#255#19#14#10#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#135'cD'#255#197#145'c'#255#197

-  +#145'c'#255#197#145'c'#255#197#145'c'#255#197#145'c'#255#197#145'c'#255'I5$'

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#19#14#10#255'4&'#26#255'cI2'#255

-  +#161'vP'#255#197#145'c'#255#197#145'c'#255#197#145'c'#255#197#145'c'#255#197

-  +#145'c'#255#197#145'c'#255#197#145'c'#255#197#145'c'#255#197#145'c'#255#197

-  +#145'c'#255#197#145'c'#255#197#145'c'#255#197#145'c'#255#197#145'c'#255#197

-  +#145'c'#255#197#145'c'#255#197#145'c'#255#197#145'c'#255#197#145'c'#255#197

-  +#145'c'#255#197#145'b'#255#196#144'c'#255#194#140'`'#255#182'|S'#255#159'\:'

-  +#255']2'#28#255'[/'#27#255'Q8-'#226'DDD'#168'DDDqAAA3NNN'#13#128#128#128#2#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'@@@'#4':::'#22'G@<N[/'#27#253'[/'#27

-  +#255'~D('#255#172'lF'#255#189#133'['#255#194#141'a'#255#194#142'b'#255#195

-  +#142'b'#255#195#142'b'#255#195#142'b'#255#195#142'b'#255#195#142'b'#255#195

-  +#142'b'#255#195#142'b'#255#195#142'b'#255#195#142'b'#255#195#142'b'#255#195

-  +#142'b'#255'oQ8'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255'N9'''#255#195#142'b'#255#195#142'b'#255#195#142'b'#255#195#142

-  +'b'#255#195#142'b'#255#195#142'b'#255#145'jI'#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#0#0#255'7('#28

-  +#255#166'yS'#255#195#142'b'#255#195#142'b'#255#195#142'b'#255#195#142'b'#255

-  +#195#142'b'#255#195#142'b'#255#195#142'b'#255#195#142'b'#255#195#142'b'#255

-  +#195#142'b'#255#195#142'b'#255#195#142'b'#255#195#142'b'#255#195#142'b'#255

-  +#195#142'b'#255#195#142'b'#255#195#142'b'#255#195#142'b'#255#195#142'b'#255

-  +#195#142'b'#255#195#142'b'#255#195#142'b'#255#195#142'b'#255#195#142'b'#255

-  +#195#142'b'#255#194#142'a'#255#193#140'`'#255#185#128'X'#255#166'c?'#255'k8!'

-  +#255'[/'#27#255'V3#'#241'DDD'#175'DDD{BBB:@@@'#16#128#128#128#2#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#1'333'#5'EEE'#26'Q:/z[/'#27#255'\0'#27#255#142'M/'

-  +#255#176'rM'#255#189#135']'#255#192#139'a'#255#192#139'`'#255#192#139'`'#255

-  +#192#139'`'#255#192#139'`'#255#192#139'`'#255#192#139'`'#255#192#139'`'#255

-  +#192#139'`'#255#192#139'`'#255#192#139'`'#255#192#139'`'#255#192#139'`'#255

-  +#26#19#13#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255'D1"'#255#192#139'`'#255#192#139'`'#255#192#139'`'#255#192#139'`'#255#192

-  +#139'`'#255#192#139'`'#255#192#139'`'#255#11#8#6#255#0#0#0#255#0#0#0#255#0#0

-  ,#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#8#6#4#255#143'hH'#255#192#139'`'#255

-  +#192#139'`'#255#192#139'`'#255#192#139'`'#255#192#139'`'#255#192#139'`'#255

-  +#192#139'`'#255#192#139'`'#255#192#139'`'#255#192#139'`'#255#192#139'`'#255

-  +#192#139'`'#255#192#139'`'#255#192#139'`'#255#192#139'`'#255#192#139'`'#255

-  +#192#139'`'#255#192#139'`'#255#192#139'`'#255#192#139'`'#255#192#139'`'#255

-  +#192#139'`'#255#192#139'`'#255#192#139'`'#255#192#139'`'#255#192#139'`'#255

-  +#192#139'`'#255#192#139'`'#255#192#138'a'#255#187#131'Z'#255#170'iE'#255'{B'

-  +''''#255'[/'#27#255'Z0'#28#253'DDD'#182'DDD'#132'CCCACCC'#19'UUU'#3#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'III'#7'BBB'#31'S5('#154'[/'#27#255'\0'#28#255

-  +#153'W5'#255#179'wQ'#255#189#133'^'#255#190#136'_'#255#190#136'_'#255#190#136

-  +'_'#255#190#136'_'#255#190#136'_'#255#190#136'_'#255#190#136'_'#255#190#136

-  +'_'#255#190#136'_'#255#190#136'_'#255#190#136'_'#255#190#136'_'#255#184#132

-  +']'#255#1#1#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255'dH2'#255#190#136'_'#255#190#136'_'#255#190#136'_'#255#190#136'_'#255

-  +#190#136'_'#255#190#136'_'#255#190#136'_'#255'G3#'#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#2#1#1#255#165'wS'#255#190#136'_'#255#190

-  +#136'_'#255#190#136'_'#255#190#136'_'#255#190#136'_'#255#190#136'_'#255#190

-  +#136'_'#255#190#136'_'#255#190#136'_'#255#190#136'_'#255#190#136'_'#255#190

-  +#136'_'#255#190#136'_'#255#190#136'_'#255#190#136'_'#255#190#136'_'#255#190

-  +#136'_'#255#190#136'_'#255#190#136'_'#255#190#136'_'#255#190#136'_'#255#190

-  +#136'_'#255#190#136'_'#255#190#136'_'#255#190#136'_'#255#190#136'_'#255#190

-  +#136'_'#255#190#136'_'#255#190#136'_'#255#190#136'_'#255#187#131'\'#255#173

-  +'nJ'#255#134'H,'#255'[/'#27#255'[/'#27#255'HA='#194'DDD'#139'CCCHCCC'#23'@@@'

-  +#4#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'@@@'#8'DDD"V4$'#182'[/'#27#255'`2'

-  +#29#255#160'\9'#255#181'zT'#255#187#132']'#255#188#133'^'#255#188#134'^'#255

-  +#188#134'^'#255#188#134'^'#255#188#134'^'#255#188#134'^'#255#188#134'^'#255

-  +#188#134'^'#255#188#134'^'#255#188#134'^'#255#188#134'^'#255#188#134'^'#255

-  +'hK4'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#2#1#1#255#163'uR'#255#188#134'^'#255#188#134'^'#255#188#134'^'#255#188

-  +#134'^'#255#188#134'^'#255#188#134'^'#255#188#134'^'#255#180#128'Z'#255#13#9

-  +#6#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'cF1'#255#188#134'^'#255#188

-  +#134'^'#255#188#134'^'#255#188#134'^'#255#188#134'^'#255#188#134'^'#255#188

-  +#134'^'#255#188#134'^'#255#188#134'^'#255#188#134'^'#255#188#134'^'#255#188

-  +#134'^'#255#188#134'^'#255#188#134'^'#255#188#134'^'#255#188#134'^'#255#188

-  +#134'^'#255#188#134'^'#255#188#134'^'#255#188#134'^'#255#188#134'^'#255#188

-  +#134'^'#255#188#134'^'#255#188#134'^'#255#188#134'^'#255#188#134'^'#255#188

-  +#134'^'#255#188#134'^'#255#188#134'^'#255#188#134'^'#255#188#133'^'#255#186

-  +#131'\'#255#176'sO'#255#144'O/'#255'[/'#27#255'[/'#27#255'L=6'#206'DDD'#143

-  +'BBBMEEE'#26'333'#5#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'999'#9'EEE%X2 '

-  +#209'[/'#27#255'j8 '#255#163'_='#255#181'{V'#255#186#130'\'#255#186#130'\'

-  +#255#186#130'\'#255#186#130'\'#255#186#130'\'#255#186#130'\'#255#186#130'\'

-  +#255#186#130'\'#255#186#130'\'#255#186#130'\'#255#186#130'\'#255#186#130'\'

-  +#255#186#130'\'#255'#'#25#17#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255'_B/'#255#186#130'\'#255#186#130'\'#255#186#130'\'

-  +#255#186#130'\'#255#186#130'\'#255#186#130'\'#255#186#130'\'#255#186#130'\'

-  +#255#186#130'\'#255#163'rQ'#255#5#3#2#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'$'#26#18#255

-  ,#186#130'\'#255#186#130'\'#255#186#130'\'#255#186#130'\'#255#186#130'\'#255

-  +#186#130'\'#255#186#130'\'#255#186#130'\'#255#186#130'\'#255#186#130'\'#255

-  +#186#130'\'#255#186#130'\'#255#186#130'\'#255#186#130'\'#255#186#130'\'#255

-  +#186#130'\'#255#186#130'\'#255#186#130'\'#255#186#130'\'#255#186#130'\'#255

-  +#186#130'\'#255#186#130'\'#255#186#130'\'#255#186#130'\'#255#186#130'\'#255

-  +#186#130'\'#255#186#130'\'#255#186#130'\'#255#186#130'\'#255#186#130'\'#255

-  +#186#130'\'#255#186#130'\'#255#185#129'\'#255#177'vR'#255#155'V5'#255'\0'#28

-  +#255'[/'#27#255'P9/'#217'DDD'#146'BBBQ@@@'#28'UUU'#6#0#0#0#1#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#1'MMM'#10'@@@(Z0'#29#234'[/'#27#255't<$'#255#165'b@'#255#181

-  +'{V'#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'

-  +#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255

-  +#184'Z'#255#4#3#2#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#1

-  +#255'Q8('#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#184

-  +'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#142'bE'

-  +#255#3#2#2#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#6#4#3#255#174'yV'#255#184'Z'#255#184'Z'#255#184'Z'#255

-  +#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#184

-  +'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'

-  +#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255

-  +#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#184

-  +'Z'#255#184'Z'#255#184'Z'#255#184'Z'#255#183'~Z'#255#178'wT'#255#159'Y8'

-  +#255'b3'#30#255'[/'#27#255'S6)'#228'CCC'#149'CCCTBBB'#31'III'#7#0#0#0#1#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#1'FFF'#11'F@>/[/'#27#254'[/'#27#255'}A&'#255#167'gD'

-  +#255#180'zW'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255

-  +#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181

-  +'|Y'#255#136']C'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#19#13#9

-  +#255#151'gJ'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255

-  +#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181

-  +'|Y'#255#181'|Y'#255#171'vU'#255'1"'#24#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#1#0#0#255'}V>'#255#181'|Y'#255#181'|Y'#255#181'|Y'

-  +#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255

-  +#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181

-  +'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'

-  +#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255

-  +#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#181'|Y'#255#178

-  +'wT'#255#161'];'#255'k8 '#255'[/'#27#255'V3#'#238'DDD'#151'CCCWFFF!@@@'#8#0#0

-  +#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#2'FFF'#11'K:3?[/'#27#255'[/'#27#255#131'F'

-  +'*'#255#169'iH'#255#179'xV'#255#180'zW'#255#180'{W'#255#180'{W'#255#180'{W'

-  +#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'#255

-  +#180'{W'#255#180'{W'#255'<)'#29#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#1#255'O'

-  +'6&'#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'

-  +#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'#255

-  +#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'#255#142'aE'#255#27

-  +#19#13#255#2#1#1#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#8#5#4#255#135']A'#255#180'{W'#255#180

-  +'{W'#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'

-  +#255#180'{W'#255'pL6'#255'+'#29#20#255'('#28#19#255'*'#28#20#255'+'#29#21#255

-  +'-'#30#21#255'fF2'#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{'

-  +'W'#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'

-  +#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'#255#180'{W'#255

-  +#180'{W'#255#180'zX'#255#178'vU'#255#163'_>'#255'r<"'#255'[/'#27#255'X2!'#243

-  +'CCC'#152'BBBYDDD"@@@'#8#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#2'FFF'#11'N8.'

-  +'J[/'#27#255'[/'#27#255#135'H,'#255#170'kI'#255#178'wV'#255#179'yX'#255#179

-  ,'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'

-  +#255#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255#16#11#8#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#19#13#10#255#150'fJ'#255#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'

-  +#255#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255

-  +#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255#179

-  +'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255'~V>'#255'=)'#30#255

-  +#19#13#9#255#1#1#1#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'$'#24#17

-  +#255#172'uT'#255#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255

-  +#179'yX'#255#179'yX'#255#158'kN'#255'=)'#30#255#6#4#3#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#6#4#3#255'?+'#31#255#164

-  +'oP'#255#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'

-  +#255#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255

-  +#179'yX'#255#179'yX'#255#179'yX'#255#179'yX'#255#177'uU'#255#164'b@'#255'v?%'

-  +#255'[/'#27#255'Y1'#31#246'CCC'#152'BBBYDDD"@@@'#8#0#0#0#1#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#1'FFF'#11'Q7+U[/'#27#255'\0'#28#255#139'J-'#255#171'lL'#255#177

-  +'wW'#255#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'

-  +#255#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255#172'sT'#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#1#1#1#255'O5&'#255#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255

-  +#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255#178

-  +'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'

-  +#255#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255

-  +#178'wV'#255#178'wV'#255#178'wV'#255#176'wV'#255'vO9'#255'7%'#27#255#16#10#8

-  +#255#1#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#11#8#6#255

-  +'9&'#28#255#137'\C'#255#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255#178

-  +'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255'M3%'#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#1#0#0#255'4"'#25#255#178'wV'#255#178'wV'#255#178'wV'#255

-  +#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255#178

-  +'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255#178'wV'#255#176'tT'

-  +#255#165'cC'#255'yA'''#255'[/'#27#255'Z0'#29#250'DDD'#150'CCCWFFF!III'#7#0#0

-  +#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'MMM'#10'R4''^[/'#27#255'\0'#28#255#142

-  +'M.'#255#171'nN'#255#177'uV'#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'

-  +#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255

-  +#177'wX'#255'gF3'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#23#16#12#255#152'fK'#255#177'wX'#255#177'wX'#255#177'wX'

-  +#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255

-  +#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255#177

-  +'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'

-  +#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255

-  +#177'wX'#255#177'wX'#255#177'wX'#255#173'uV'#255#136'\D'#255#133'YB'#255#131

-  +'XA'#255#128'V@'#255#153'gL'#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'

-  +#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255

-  +'S8)'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255'"'#23#17#255#175'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'

-  +#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255#177'wX'#255

-  +#177'wX'#255#177'wX'#255#177'wX'#255#175'tU'#255#166'eE'#255'}B)'#255'\0'#28

-  +#255'[/'#28#253'CCC'#148'CCCTBBB'#31'III'#7#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#1'999'#9'V4%h[/'#27#255'\0'#28#255#145'O0'#255#171'mN'#255#175'sU'#255

-  +#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175

-  +'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255'*'#29#21#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#2#1#1#255'X;,'#255#175'uW'

-  +#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255

-  +#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175

-  +'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'

-  +#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255

-  +#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175

-  ,'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'

-  +#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255

-  +#175'uW'#255#175'uW'#255#175'uW'#255'^?/'#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'$'#24#18#255

-  +#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175

-  +'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'#255#175'uW'

-  +#255#175'sT'#255#166'eE'#255#128'D*'#255'\0'#28#255'[/'#27#255'ECB'#149'CCCP'

-  +'@@@'#28'UUU'#6#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'@@@'#8'W3#q[/'#27#255

-  +'\1'#29#255#150'Q1'#255#171'mN'#255#174'sU'#255#175'uV'#255#175'uV'#255#175

-  +'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'

-  +#255#175'uV'#255#175'uV'#255#15#10#8#255#0#0#0#255#0#0#0#255#0#0#0#255#4#3#2

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#29#19#14#255#156'iM'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'

-  +#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255

-  +#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175

-  +'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'

-  +#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255

-  +#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175

-  +'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'

-  +#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255

-  +#175'uV'#255#11#7#6#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'sM9'#255#175'uV'#255#175

-  +'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'#255#175'uV'

-  +#255#175'uV'#255#175'uV'#255#175'uV'#255#175'tV'#255#174'rS'#255#167'fG'#255

-  +#131'G,'#255'\0'#28#255'[/'#27#255'GA?'#150'CCCLGGG'#25'333'#5#0#0#0#1#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#1'III'#7'W3#k[/'#27#255'\1'#29#255#149'P2'#255#169

-  +'lM'#255#174'qT'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'

-  +#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255

-  +#9#6#4#255#0#0#0#255#0#0#0#255#0#0#0#255#136'ZC'#255'T8*'#255#1#1#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#7#5#3#255'bA0'#255#175'tV'#255#175'tV'#255

-  +#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175

-  +'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'

-  +#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255

-  +#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175

-  +'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'

-  +#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255

-  +#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175

-  +'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#165'nR'#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#10#7#5#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255

-  +#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175'tV'#255#175

-  +'tV'#255#174'sU'#255#173'qR'#255#166'eG'#255#131'F,'#255'\0'#28#255'[/'#27

-  +#255'EA?'#145'DDDGFFF'#22'@@@'#4#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'333'

-  +#5'W3#\[/'#27#255'\1'#29#255#146'P3'#255#168'jM'#255#173'pT'#255#173'rU'#255

-  +#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173

-  +'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#4#3#2#255#0#0#0#255#0#0#0#255#21

-  +#14#10#255#173'rU'#255#173'rU'#255#152'dK'#255'dB1'#255'A+ '#255'D-!'#255#141

-  +']E'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'

-  +#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255

-  +#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173

-  +'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'

-  +#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255

-  +#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173

-  +'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'

-  +#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255

-  +#173'rU'#255#173'rU'#255#169'pS'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#139'\D'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'

-  +#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'rU'#255#173'qT'#255

-  +#172'oR'#255#165'cF'#255#128'E+'#255'\0'#28#255'[/'#27#255'CBA'#132'DDD@CCC'

-  ,#19'UUU'#3#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'@@@'#4'U2"L[/'#27#255'\1'

-  +#29#255#143'N3'#255#168'hK'#255#172'oT'#255#173'qU'#255#173'rV'#255#173'rV'

-  +#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255

-  +#173'rV'#255#173'rV'#255#1#1#1#255#0#0#0#255#0#0#0#255'nI7'#255#173'rV'#255

-  +#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173

-  +'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'

-  +#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255

-  +#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173

-  +'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'

-  +#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255

-  +#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173

-  +'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'

-  +#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255

-  +#173'rV'#255#173'rV'#255#2#1#1#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'H'

-  +'0$'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'

-  +#255#173'rV'#255#173'rV'#255#173'rV'#255#173'rV'#255#173'qU'#255#171'nQ'#255

-  +#165'dF'#255'~D+'#255'\1'#29#255'Z0'#28#252'CCCzCCC9@@@'#16#128#128#128#2#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'UUU'#3'T2#=[/'#27#255'\1'#29#255#140'N1'

-  +#255#166'gI'#255#171'oR'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255

-  +#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173

-  +'qU'#255#1#1#1#255#27#18#13#255'a@0'#255#173'qU'#255#173'qU'#255#173'qU'#255

-  +#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173

-  +'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'

-  +#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255

-  +#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173

-  +'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'

-  +#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255

-  +#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173

-  +'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'

-  +#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255

-  +#173'qU'#255'$'#24#18#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#31#20#15#255

-  +#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#173

-  +'qU'#255#173'qU'#255#173'qU'#255#173'qU'#255#172'pT'#255#169'lP'#255#164'aD'

-  +#255'{C*'#255'\1'#29#255'Y0'#30#247'CCCoBBB2;;;'#13#0#0#0#2#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#128#128#128#2'V6''-[/'#27#255'\1'#29#255#137'L0'#255

-  +#165'fH'#255#170'mQ'#255#172'pT'#255#172'pU'#255#172'pU'#255#172'pU'#255#172

-  +'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'

-  +#255#166'lS'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255

-  +#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172

-  +'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'

-  +#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255

-  +#168'nS'#255#134'WB'#255'b@0'#255'tK9'#255#166'lS'#255#172'pU'#255#172'pU'

-  +#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255

-  +#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172

-  +'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'

-  +#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255

-  +#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172

-  +'pU'#255#129'T@'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#22#14#11#255#172

-  +'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'#255#172'pU'

-  +#255#172'pU'#255#172'pU'#255#172'pU'#255#172'oT'#255#170'mP'#255#163'`C'#255

-  +'xB*'#255'\1'#29#255'Y1'#31#241'BBBdCCC*MMM'#10#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#2'T7*'#29'[/'#27#255'\1'#29#255#134'J0'#255#165'dG'

-  +#255#169'lQ'#255#171'pU'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255

-  +#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172

-  +'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'

-  +#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255

-  +#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172

-  +'qW'#255#172'qW'#255#172'qW'#255#135'YD'#255'$'#24#18#255#3#2#2#255#0#0#0#255

-  ,#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#8#5#4#255#30#20#16#255'xO='#255#172

-  +'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'

-  +#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255

-  +#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172

-  +'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'

-  +#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255

-  +#14#9#7#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#20#13#10#255#172'qW'#255#172'qW'#255

-  +#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172'qW'#255#172

-  +'qW'#255#172'qW'#255#171'oT'#255#169'jM'#255#162'_A'#255's>'''#255'\1'#29#255

-  +'X2 '#234'CCCXDDD"@@@'#8#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#1'C><'#9'[/'#27#247'\1'#29#255'}F-'#255#164'bE'#255#169'lO'#255#171'pT'

-  +#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255

-  +#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172

-  +'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'

-  +#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255

-  +#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#154'eM'#255#22

-  +#15#11#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#25#17#13#255#136'ZE'#255#172'qV'

-  +#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255

-  +#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172

-  +'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'

-  +#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255

-  +#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255'U7*'#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#17#11#9#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'

-  +#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#172'qV'#255#171'oT'#255

-  +#168'jM'#255#161'^@'#255'k;%'#255'\0'#28#255'V3#'#220'CCCLEEE'#26'333'#5#0#0

-  +#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'+++'#6'[/'#28#214'\1'#29

-  +#255't@)'#255#162'`B'#255#168'jO'#255#172'pV'#255#173'rX'#255#173'rX'#255#173

-  +'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'

-  +#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255

-  +#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173

-  +'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'

-  +#255#173'rX'#255#144'_I'#255#6#4#3#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255'Z;-'#255#173'rX'#255#173'rX'#255#173'rX'#255#173

-  +'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'

-  +#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255

-  +#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173

-  +'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'

-  +#255#171'pV'#255#3#2#1#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'!'#22#17#255#173'rX'#255#173'rX'

-  +#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255#173'rX'#255

-  +#173'rX'#255#172'qX'#255#171'oU'#255#167'hM'#255#160'[>'#255'b5!'#255'\0'#28

-  +#255'S5'''#196'EEE?CCC'#19'@@@'#4#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0'UUU'#3'Z0'#29#176'\1'#29#255'k:%'#255#162'^A'#255#169'jN'#255

-  +#173'qV'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174

-  +'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'

-  +#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255

-  +#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174

-  +'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#151'dM'#255#4#2#2#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#1#255

-  +#136'YF'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174

-  +'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'

-  +#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255

-  +#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174

-  +'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255'5#'#27#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  ,#0#0#255'G.$'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255

-  +#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#172'pV'#255#167

-  +'gK'#255#153'X:'#255'^3'#31#255'\0'#28#255'Q7+'#166'BBB2777'#14#0#0#0#2#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#2'Z0'#29#137'\0'#28

-  +#255'a5!'#255#160']>'#255#168'jN'#255#173'sY'#255#175'v\'#255#175'v\'#255#175

-  +'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'

-  +#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255

-  +#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175

-  +'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'

-  +#255#29#20#15#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#8#5#4#255#169'rX'#255#175'v\'#255#175'v\'

-  +#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255

-  +#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175

-  +'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'

-  +#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255

-  +#175'v\'#255#156'jR'#255#1#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'yQ?'#255#175'v\'#255#175'v\'#255

-  +#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175'v\'#255#175

-  +'v\'#255#174'u['#255#172'qV'#255#166'gJ'#255#143'S6'#255'^2'#31#255'[/'#27

-  +#255'O;2'#130'CCC&999'#9#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#1'Z0'#29'a\0'#28#255'^3'#31#255#153'W;'#255#168'jN'#255

-  +#174'tZ'#255#176'x_'#255#176'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255#176

-  +'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255#176'y_'

-  +#255#176'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255

-  +#176'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255#176

-  +'y_'#255#176'y_'#255#176'y_'#255'oL='#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255']@2'#255#176'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255#176

-  +'y_'#255'Z=1'#255')'#28#22#255#26#18#14#255#14#10#8#255#10#7#5#255#23#16#12

-  +#255'+'#30#23#255'E0&'#255#138'_J'#255#176'y_'#255#176'y_'#255#176'y_'#255

-  +#176'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255#176

-  +'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255'#'#24#19#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#172'w]'#255#176'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255

-  +#176'y_'#255#176'y_'#255#176'y_'#255#176'y_'#255#175'w^'#255#172'rW'#255#165

-  +'fI'#255#134'K2'#255']1'#30#255'[/'#27#255'H?:ZBBB'#27'+++'#6#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'X/'#29'7[/'#27#255

-  +'^2'#31#255#141'P5'#255#167'hK'#255#174'sY'#255#177'x_'#255#177'za'#255#177

-  +'za'#255#177'za'#255#177'za'#255#177'za'#255#177'za'#255#177'za'#255#177'za'

-  +#255#177'za'#255#177'za'#255#177'za'#255#177'za'#255#177'za'#255#177'za'#255

-  +#177'za'#255#177'za'#255#177'za'#255#177'za'#255#177'za'#255#177'za'#255#177

-  +'za'#255#177'za'#255#177'za'#255#177'za'#255#177'za'#255#177'za'#255#9#6#5

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'%'#25#20#255#177'za'#255#177'za'

-  +#255#177'za'#255#177'za'#255'bD6'#255#8#6#4#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'>+"'#255#175'za'

-  +#255#177'za'#255#177'za'#255#177'za'#255#177'za'#255#177'za'#255#177'za'#255

-  +#177'za'#255#177'za'#255#177'za'#255#177'za'#255#177'za'#255#177'za'#255#136

-  +'^K'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#16#11#9#255#177'za'#255#177'za'#255#177'za'#255#177'za'#255#177'za'

-  +#255#177'za'#255#177'za'#255#177'za'#255#177'za'#255#177'za'#255#176'x^'#255

-  +#172'qV'#255#164'cF'#255'{E,'#255']1'#30#255'Z0'#29#245'DDD<GGG'#18'UUU'#3#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'X?4'#7

-  +'[/'#27#243']1'#30#255'}G/'#255#165'eH'#255#174'sY'#255#178'zb'#255#179'}d'

-  +#255#179'}d'#255#179'}d'#255#179'}d'#255#179'}d'#255#179'}d'#255#179'}d'#255

-  +#179'}d'#255#179'}d'#255#179'}d'#255#179'}d'#255#179'}d'#255#179'}d'#255#179

-  +'}d'#255#179'}d'#255#179'}d'#255#179'}d'#255#179'}d'#255#179'}d'#255#179'}d'

-  +#255#179'}d'#255#179'}d'#255#179'}d'#255#179'}d'#255#179'}d'#255#158'oX'#255

-  ,#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'&'#26#21#255#179'}d'#255#179

-  +'}d'#255#179'}d'#255'kK;'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#12

-  +#8#6#255#137'_L'#255#179'}d'#255#179'}d'#255#179'}d'#255#179'}d'#255#179'}d'

-  +#255#179'}d'#255#179'}d'#255#179'}d'#255#179'}d'#255#179'}d'#255#179'}d'#255

-  +#179'}d'#255#20#14#11#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255'vRB'#255#179'}d'#255#179'}d'#255#179'}d'#255#179'}d'#255#179

-  +'}d'#255#179'}d'#255#179'}d'#255#179'}d'#255#179'}d'#255#179'}d'#255#177'y`'

-  +#255#172'pU'#255#163'aC'#255'i;&'#255'\1'#29#255'W2!'#206'FFF,@@@'#12#0#0#0#2

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#1'[/'#27#184'\1'#29#255'l<'''#255#164'bE'#255#173'rX'#255#179'|c'#255#180

-  +'g'#255#181'g'#255#181'g'#255#181'g'#255#181'g'#255#181'g'#255#181'g'

-  +#255#181'g'#255#181'g'#255#181'g'#255#181'g'#255#181'g'#255#181'g'#255

-  +#181'g'#255#181'g'#255#181'g'#255#181'g'#255#181'g'#255#181'g'#255#181

-  +'g'#255#181'g'#255#181'g'#255#181'g'#255#181'g'#255#181'g'#255'_C6'#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'Q9.'#255#181'g'#255#181'g'

-  +#255#152'kW'#255#2#1#1#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#3#2#2#255#151'jV'#255#181'g'#255#181'g'#255#181'g'#255#181'g'#255

-  +#181'g'#255#181'g'#255#181'g'#255#181'g'#255#181'g'#255#181'g'#255#181

-  +'g'#255'sPA'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#23#16

-  +#13#255#181'g'#255#181'g'#255#181'g'#255#181'g'#255#181'g'#255#181'g'

-  +#255#181'g'#255#181'g'#255#181'g'#255#181'g'#255#180'g'#255#177'za'#255

-  +#171'nS'#255#156'[>'#255'`5!'#255'\0'#28#255'T5'''#155'DDD'#30'III'#7#0#0#0#1

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0'Z/'#28'w\0'#28#255'`5!'#255#157'\?'#255#172'pU'#255#179'}d'#255#181#129

-  +'i'#255#182#129'j'#255#182#129'j'#255#182#129'j'#255#182#129'j'#255#182#129

-  +'j'#255#182#129'j'#255#182#129'j'#255#182#129'j'#255#182#129'j'#255#182#129

-  +'j'#255#182#129'j'#255#182#129'j'#255#182#129'j'#255#182#129'j'#255#182#129

-  +'j'#255#182#129'j'#255#182#129'j'#255#182#129'j'#255#182#129'j'#255#182#129

-  +'j'#255#182#129'j'#255#182#129'j'#255#182#129'j'#255#182#129'j'#255'.!'#27

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#2#1#1#255#166'va'#255#182#129'j'

-  +#255#182#129'j'#255'A.&'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#9#6#5#255#170'yd'#255#182#129'j'#255#182#129'j'

-  +#255#182#129'j'#255#182#129'j'#255#182#129'j'#255#182#129'j'#255#182#129'j'

-  +#255#182#129'j'#255#182#129'j'#255#182#129'j'#255#182#129'j'#255#8#6#5#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#135'`O'#255#182#129'j'#255#182

-  +#129'j'#255#182#129'j'#255#182#129'j'#255#182#129'j'#255#182#129'j'#255#182

-  +#129'j'#255#182#129'j'#255#182#129'j'#255#182#129'j'#255#181#128'h'#255#177

-  +'za'#255#169'kP'#255#139'Q7'#255'^3'#31#255'\0'#28#255'N9/\GGG'#18'UUU'#3#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0'X.'#26'4\0'#28#255'^3'#31#255#141'S8'#255#170'mQ'#255#179'|c'#255

-  +#182#130'k'#255#184#132'm'#255#184#132'm'#255#184#132'm'#255#184#132'm'#255

-  +#184#132'm'#255#184#132'm'#255#184#132'm'#255#184#132'm'#255#184#132'm'#255

-  +#184#132'm'#255#184#132'm'#255#184#132'm'#255#184#132'm'#255#184#132'm'#255

-  +#184#132'm'#255#184#132'm'#255#184#132'm'#255#184#132'm'#255#184#132'm'#255

-  +#184#132'm'#255#184#132'm'#255#184#132'm'#255#184#132'm'#255#184#132'm'#255

-  +#20#15#12#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'N8.'#255#184#132'm'#255

-  +#184#132'm'#255#184#132'm'#255#22#16#13#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#21#15#13#255#182#130'm'

-  +#255#184#132'm'#255#184#132'm'#255#184#132'm'#255#184#132'm'#255#184#132'm'

-  ,#255#184#132'm'#255#184#132'm'#255#184#132'm'#255#184#132'm'#255#184#132'm'

-  +#255'N8.'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#23#17#13#255#184#132'm'#255

-  +#184#132'm'#255#184#132'm'#255#184#132'm'#255#184#132'm'#255#184#132'm'#255

-  +#184#132'm'#255#184#132'm'#255#184#132'm'#255#184#132'm'#255#183#131'l'#255

-  +#182#129'j'#255#177'x_'#255#167'hK'#255'zG/'#255']1'#30#255'Z0'#28#241'@@@(M'

-  +'MM'#10#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0'='#31#18#3'[/'#27#238']1'#30#255'{G/'#255#167'hL'

-  +#255#177'za'#255#183#131'l'#255#185#134'p'#255#185#134'p'#255#185#134'p'#255

-  +#185#134'p'#255#185#134'p'#255#185#134'p'#255#185#134'p'#255#185#134'p'#255

-  +#185#134'p'#255#185#134'p'#255#185#134'p'#255#185#134'p'#255#185#134'p'#255

-  +#185#134'p'#255#185#134'p'#255#185#134'p'#255#185#134'p'#255#185#134'p'#255

-  +#185#134'p'#255#185#134'p'#255#185#134'p'#255#185#134'p'#255#185#134'p'#255

-  +#185#134'p'#255#26#19#16#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#12#9#7#255#185#134

-  +'p'#255#185#134'p'#255#185#134'p'#255#185#134'p'#255#3#2#2#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255'U>4'#255#185#134'p'#255#185#134'p'#255#185#134'p'#255#185#134'p'#255#185

-  +#134'p'#255#185#134'p'#255#185#134'p'#255#185#134'p'#255#185#134'p'#255#185

-  +#134'p'#255#185#134'p'#255#25#18#15#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'xWI'#255#185

-  +#134'p'#255#185#134'p'#255#185#134'p'#255#185#134'p'#255#185#134'p'#255#185

-  +#134'p'#255#185#134'p'#255#185#134'p'#255#185#134'p'#255#185#134'p'#255#184

-  +#134'o'#255#182#129'j'#255#175'v\'#255#165'dG'#255'h;&'#255'\1'#29#255'X2 '

-  +#193'==='#25'+++'#6#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27#170'\1'#29#255'f9%'#255

-  +#164'bG'#255#175'v]'#255#183#131'l'#255#186#135'r'#255#186#137's'#255#186#137

-  +'s'#255#186#137's'#255#186#137's'#255#186#137's'#255#186#137's'#255#186#137

-  +'s'#255#186#137's'#255#186#137's'#255#186#137's'#255#186#137's'#255#186#137

-  +'s'#255#186#137's'#255#186#137's'#255#186#137's'#255#186#137's'#255#186#137

-  +'s'#255#186#137's'#255#186#137's'#255#186#137's'#255#186#137's'#255#186#137

-  +'s'#255#186#137's'#255'$'#27#23#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#129'_P'#255

-  +#186#137's'#255#186#137's'#255#186#137's'#255#186#137's'#255#1#1#1#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255' '#23#19#255#186#137's'#255#186#137's'#255#186#137's'#255#186

-  +#137's'#255#186#137's'#255#186#137's'#255#186#137's'#255#186#137's'#255#186

-  +#137's'#255#186#137's'#255#186#137's'#255#172'j'#255#7#5#4#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#12#9#7#255

-  +#186#137's'#255#186#137's'#255#186#137's'#255#186#137's'#255#186#137's'#255

-  +#186#137's'#255#186#137's'#255#186#137's'#255#186#137's'#255#186#137's'#255

-  +#186#137's'#255#185#135'q'#255#181#129'i'#255#173'qW'#255#149'X='#255'`5!'

-  +#255'\0'#28#255'V4%333'#15#0#0#0#3#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'Z/'#27'O\0'#28

-  +#255'_4 '#255#144'U:'#255#172'qV'#255#182#130'k'#255#187#137't'#255#188#139

-  +'u'#255#188#140'v'#255#188#140'v'#255#188#140'v'#255#188#140'v'#255#188#140

-  +'v'#255#188#140'v'#255#188#140'v'#255#188#140'v'#255#188#140'v'#255#188#140

-  +'v'#255#188#140'v'#255#188#140'v'#255#188#140'v'#255#188#140'v'#255#188#140

-  +'v'#255#188#140'v'#255#188#140'v'#255#188#140'v'#255#188#140'v'#255#188#140

-  +'v'#255#188#140'v'#255#188#140'v'#255'1$'#31#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255':+$'#255

-  +#188#140'v'#255#188#140'v'#255#188#140'v'#255#188#140'v'#255#188#140'v'#255#1

-  +#1#1#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#4#3#3#255#188#140'v'#255#188#140'v'#255#188#140

-  +'v'#255#188#140'v'#255#188#140'v'#255#188#140'v'#255#188#140'v'#255#188#140

-  +'v'#255#188#140'v'#255#188#140'v'#255#188#140'v'#255#188#140'v'#255#188#140

-  +'v'#255'}]N'#255'6("'#255#14#11#9#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255'S>5'#255#188#140'v'#255#188#140'v'#255#188#140'v'#255#188#140'v'

-  ,#255#188#140'v'#255#188#140'v'#255#188#140'v'#255#188#140'v'#255#188#140'v'

-  +#255#188#140'v'#255#188#139'u'#255#186#136'r'#255#180'~f'#255#169'kP'#255'|H'

-  +'1'#255'^2'#31#255'[/'#27#249'L=6+III'#7#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +'[/'#27#5'[/'#27#235']1'#30#255'wD/'#255#169'jO'#255#180'g'#255#187#137'u'

-  +#255#189#141'x'#255#189#142'y'#255#189#142'y'#255#189#142'y'#255#189#142'y'

-  +#255#189#142'y'#255#189#142'y'#255#189#142'y'#255#189#142'y'#255#189#142'y'

-  +#255#189#142'y'#255#189#142'y'#255#189#142'y'#255#189#142'y'#255#189#142'y'

-  +#255#189#142'y'#255#189#142'y'#255#189#142'y'#255#189#142'y'#255#189#142'y'

-  +#255#189#142'y'#255#189#142'y'#255#189#142'y'#255'J7/'#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#16#12#11

-  +#255#187#140'y'#255#189#142'y'#255#189#142'y'#255#189#142'y'#255#189#142'y'

-  +#255#189#142'y'#255#1#1#1#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#181#136'u'#255#189

-  +#142'y'#255#189#142'y'#255#189#142'y'#255#189#142'y'#255#189#142'y'#255#189

-  +#142'y'#255#189#142'y'#255#189#142'y'#255#189#142'y'#255#189#142'y'#255#189

-  +#142'y'#255#189#142'y'#255#189#142'y'#255#189#142'y'#255#189#142'y'#255#11#8

-  +#7#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#1#255#181#136's'#255#189#142'y'#255

-  +#189#142'y'#255#189#142'y'#255#189#142'y'#255#189#142'y'#255#189#142'y'#255

-  +#189#142'y'#255#189#142'y'#255#189#142'y'#255#189#142'y'#255#189#141'x'#255

-  +#186#136'q'#255#177'za'#255#164'dG'#255'f:&'#255'\1'#29#255'Y1'#31#186'@@@'

-  +#16'UUU'#3#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'Z/'#27#147'\1'#29#255

-  +'b8$'#255#160'bF'#255#177'y`'#255#186#137's'#255#190#143'z'#255#191#145'|'

-  +#255#191#145'|'#255#191#145'|'#255#191#145'|'#255#191#145'|'#255#191#145'|'

-  +#255#191#145'|'#255#191#145'|'#255#191#145'|'#255#191#145'|'#255#191#145'|'

-  +#255#191#145'|'#255#191#145'|'#255#191#145'|'#255#191#145'|'#255#191#145'|'

-  +#255#191#145'|'#255#191#145'|'#255#191#145'|'#255#191#145'|'#255#191#145'|'

-  +#255#191#145'|'#255#162'{i'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#1#1#1#255#162'{i'#255#191#145'|'#255#191#145'|'

-  +#255#191#145'|'#255#191#145'|'#255#191#145'|'#255#191#145'|'#255#2#1#1#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#1#1#1#255#191#145'|'#255#191#145'|'#255#191#145'|'#255#191

-  +#145'|'#255#191#145'|'#255#191#145'|'#255#191#145'|'#255#191#145'|'#255#191

-  +#145'|'#255#191#145'|'#255#191#145'|'#255#191#145'|'#255#191#145'|'#255#191

-  +#145'|'#255#191#145'|'#255#191#145'|'#255#5#4#3#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255'&'#29#25#255#191#145'|'#255#191#145'|'#255#191#145'|'#255#191#145'|'#255

-  +#191#145'|'#255#191#145'|'#255#191#145'|'#255#191#145'|'#255#191#145'|'#255

-  +#191#145'|'#255#191#145'|'#255#189#142'y'#255#184#134'o'#255#174'sY'#255#143

-  +'U;'#255'`5!'#255'\0'#28#255'U3$`@@@'#8#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0'Z.'#27'5\0'#28#255'_4 '#255#137'Q8'#255#173'rX'#255#184#134'o'

-  +#255#190#144'{'#255#192#147''#255#192#147''#255#192#147''#255#192#147''

-  +#255#192#147''#255#192#147''#255#192#147''#255#192#147''#255#192#147''

-  +#255#192#147''#255#192#147''#255#192#147''#255#192#147''#255#192#147''

-  +#255#192#147''#255#192#147''#255#192#147''#255#192#147''#255#192#147''

-  +#255#192#147''#255#192#147''#255#192#147''#255#192#147''#255#14#11#9#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255' '#24#21#255

-  +#192#147''#255#192#147''#255#192#147''#255#192#147''#255#192#147''#255

-  +#192#147''#255#192#147''#255#4#3#3#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#8#6#5#255#192#147

-  +''#255#192#147''#255#192#147''#255#192#147''#255#192#147''#255#192#147

-  +''#255#192#147''#255#192#147''#255#192#147''#255#192#147''#255#192#147

-  +''#255#192#147''#255#192#147''#255#192#147''#255#192#147''#255#192#147

-  +''#255#1#1#0#255#0#0#0#255#0#0#0#255#0#0#0#255#137'iZ'#255#192#147''#255

-  +#192#147''#255#192#147''#255#192#147''#255#192#147''#255#192#147''#255

-  +#192#147''#255#192#147''#255#192#147''#255#192#147''#255#192#146'~'#255

-  +#189#142'y'#255#182#129'j'#255#169'lP'#255'uD.'#255'^2'#31#255'[/'#28#237'NF'

-  ,'B'#21'UUU'#3#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'

-  +#27#211']1'#30#255'l>)'#255#167'iM'#255#181'g'#255#190#143'z'#255#193#148

-  +#128#255#194#149#130#255#194#149#130#255#194#149#130#255#194#149#130#255#194

-  +#149#130#255#194#149#130#255#194#149#130#255#194#149#130#255#194#149#130#255

-  +#194#149#130#255#194#149#130#255#194#149#130#255#194#149#130#255#194#149#130

-  +#255#194#149#130#255#194#149#130#255#194#149#130#255#194#149#130#255#194#149

-  +#130#255#194#149#130#255#194#149#130#255#194#149#130#255'G60'#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'ZE<'#255#194#149#130

-  +#255#194#149#130#255#194#149#130#255#194#149#130#255#194#149#130#255#194#149

-  +#130#255#194#149#130#255#7#6#5#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#23#17#15#255#194#149

-  +#130#255#194#149#130#255#194#149#130#255#194#149#130#255#194#149#130#255#194

-  +#149#130#255#194#149#130#255#194#149#130#255#194#149#130#255#194#149#130#255

-  +#194#149#130#255#194#149#130#255#194#149#130#255#194#149#130#255#194#149#130

-  +#255#179#137'x'#255#0#0#0#255#0#0#0#255#0#0#0#255'-#'#30#255#194#149#130#255

-  +#194#149#130#255#194#149#130#255#194#149#130#255#194#149#130#255#194#149#130

-  +#255#194#149#130#255#194#149#130#255#194#149#130#255#194#149#130#255#194#149

-  +#130#255#192#147''#255#188#140'v'#255#177'za'#255#156'_D'#255'a6#'#255'\1'

-  +#29#255'Y1'#31#153'@@@'#8#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0'Z/'#27']\0'#28#255'`5!'#255#143'V='#255#175'v]'#255#187#138

-  +'u'#255#193#149#129#255#195#152#132#255#195#152#133#255#195#152#133#255#195

-  +#152#133#255#195#152#133#255#195#152#133#255#195#152#133#255#195#152#133#255

-  +#195#152#133#255#195#152#133#255#195#152#133#255#195#152#133#255#195#152#133

-  +#255#195#152#133#255#195#152#133#255#195#152#133#255#195#152#133#255#195#152

-  +#133#255#195#152#133#255#195#152#133#255#195#152#133#255#195#152#133#255#170

-  +#133't'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#172#133'u'#255#195#152#133#255#195#152#133#255#195#152#133#255#195#152#133

-  +#255#195#152#133#255#195#152#133#255#146'rd'#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255'_JA'#255#195#152#133#255#195#152#133#255#195#152#133#255#195#152#133#255

-  +#195#152#133#255#195#152#133#255#195#152#133#255#195#152#133#255#195#152#133

-  +#255#195#152#133#255#195#152#133#255#195#152#133#255#195#152#133#255#195#152

-  +#133#255#195#152#133#255#140'm`'#255#0#0#0#255#0#0#0#255#6#5#4#255#186#145''

-  +#255#195#152#133#255#195#152#133#255#195#152#133#255#195#152#133#255#195#152

-  +#133#255#195#152#133#255#195#152#133#255#195#152#133#255#195#152#133#255#195

-  +#152#133#255#195#152#132#255#192#147''#255#185#134'p'#255#172'pU'#255'|I2'

-  +#255'^3'#31#255'\0'#28#250'R5()UUU'#3#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27#3'[/'#27#221']1'#30#255'oA,'#255#169'lP'

-  +#255#183#131'l'#255#192#147''#255#196#154#135#255#197#155#136#255#197#155

-  +#136#255#197#155#136#255#197#155#136#255#197#155#136#255#197#155#136#255#197

-  +#155#136#255#197#155#136#255#197#155#136#255#197#155#136#255#197#155#136#255

-  +#197#155#136#255#197#155#136#255#197#155#136#255#197#155#136#255#197#155#136

-  +#255#197#155#136#255#197#155#136#255#197#155#136#255#197#155#136#255#197#155

-  +#136#255#197#155#136#255#26#20#18#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#8#6#5#255#197#155#136#255#197#155#136#255#197#155#136#255#197#155#136

-  +#255#197#155#136#255#197#155#136#255#197#155#136#255#28#22#19#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#4#3#3#255#193#153#134#255#197#155#136#255#197#155#136#255#197#155

-  +#136#255#197#155#136#255#197#155#136#255#197#155#136#255#197#155#136#255#197

-  +#155#136#255#197#155#136#255#197#155#136#255#197#155#136#255#197#155#136#255

-  +#197#155#136#255#197#155#136#255#197#155#136#255#182#143'~'#255'=0*'#255#7#5

-  +#5#255#130'fY'#255#197#155#136#255#197#155#136#255#197#155#136#255#197#155

-  +#136#255#197#155#136#255#197#155#136#255#197#155#136#255#197#155#136#255#197

-  +#155#136#255#197#155#136#255#196#154#136#255#195#152#133#255#190#144'{'#255

-  +#180'~f'#255#159'bG'#255'c8%'#255'\1'#29#255'Z0'#30#163'III'#7#0#0#0#1#0#0#0

-  ,#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27

-  +'g\0'#28#255'a6#'#255#147'Y@'#255#177'za'#255#190#143'z'#255#196#154#135#255

-  +#198#157#139#255#198#157#139#255#198#157#139#255#198#157#139#255#198#157#139

-  +#255#198#157#139#255#198#157#139#255#198#157#139#255#198#157#139#255#198#157

-  +#139#255#198#157#139#255#198#157#139#255#198#157#139#255#198#157#139#255#198

-  +#157#139#255#198#157#139#255#198#157#139#255#198#157#139#255#198#157#139#255

-  +#198#157#139#255#198#157#139#255#198#157#139#255'<0+'#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255'*!'#29#255#198#157#139#255#198#157#139#255#198#157

-  +#139#255#198#157#139#255#198#157#139#255#198#157#139#255#164#130's'#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255'?2,'#255#198#157#139#255#198#157#139#255#198#157#139

-  +#255#198#157#139#255#198#157#139#255#198#157#139#255#198#157#139#255#198#157

-  +#139#255#198#157#139#255#198#157#139#255#198#157#139#255#198#157#139#255#198

-  +#157#139#255#198#157#139#255#198#157#139#255#198#157#139#255#198#157#139#255

-  +#198#157#139#255#198#157#139#255#198#157#139#255#198#157#139#255#198#157#139

-  +#255#198#157#139#255#198#157#139#255#198#157#139#255#198#157#139#255#198#157

-  +#139#255#198#157#139#255#198#157#139#255#198#157#139#255#198#156#138#255#195

-  +#152#132#255#187#138't'#255#173'sY'#255'L5'#255'^3'#31#255'\0'#28#252'U3$,'

-  +#128#128#128#2#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0'[/'#27#7'[/'#27#229'^2'#31#255'qB.'#255#171'nS'#255

-  +#185#134'p'#255#194#151#131#255#198#158#140#255#200#160#142#255#200#160#142

-  +#255#200#160#142#255#200#160#142#255#200#160#142#255#200#160#142#255#200#160

-  +#142#255#200#160#142#255#200#160#142#255#200#160#142#255#200#160#142#255#200

-  +#160#142#255#200#160#142#255#200#160#142#255#200#160#142#255#200#160#142#255

-  +#200#160#142#255#200#160#142#255#200#160#142#255#200#160#142#255#200#160#142

-  +#255'9.)'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'hSJ'#255#200#160#142

-  +#255#200#160#142#255#200#160#142#255#200#160#142#255#200#160#142#255#200#160

-  +#142#255'6+'''#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#6#5#5#255#184#148#131#255#200#160#142

-  +#255#200#160#142#255#200#160#142#255#200#160#142#255#200#160#142#255#200#160

-  +#142#255#200#160#142#255#200#160#142#255#200#160#142#255#200#160#142#255#200

-  +#160#142#255#200#160#142#255#200#160#142#255#200#160#142#255#200#160#142#255

-  +#200#160#142#255#200#160#142#255#200#160#142#255#200#160#142#255#200#160#142

-  +#255#200#160#142#255#200#160#142#255#200#160#142#255#200#160#142#255#200#160

-  +#142#255#200#160#142#255#200#160#142#255#200#160#142#255#200#160#142#255#199

-  +#159#141#255#198#157#139#255#192#147''#255#181'g'#255#161'dI'#255'd:&'#255

-  +'\1'#29#255'Z0'#29#171'@@@'#4#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27'q\1'#29#255'a6#'

-  +#255#143'W?'#255#177'za'#255#191#144'|'#255#198#157#139#255#200#161#144#255

-  +#201#162#145#255#201#163#145#255#201#163#145#255#201#163#145#255#201#163#145

-  +#255#201#163#145#255#201#163#145#255#201#163#145#255#201#163#145#255#201#163

-  +#145#255#201#163#145#255#201#163#145#255#201#163#145#255#201#163#145#255#201

-  +#163#145#255#201#163#145#255#201#163#145#255#201#163#145#255#201#163#145#255

-  +#201#163#145#255'=2,'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'!'#27#24#255#197

-  +#161#143#255#201#163#145#255#201#163#145#255#201#163#145#255#201#163#145#255

-  +#201#163#145#255#197#161#143#255#3#2#2#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#1#255#149'zl'#255#201

-  +#163#145#255#201#163#145#255#201#163#145#255#201#163#145#255#201#163#145#255

-  +#201#163#145#255#201#163#145#255#201#163#145#255#201#163#145#255#201#163#145

-  +#255#201#163#145#255#201#163#145#255#201#163#145#255#201#163#145#255#201#163

-  +#145#255#201#163#145#255#201#163#145#255#201#163#145#255#201#163#145#255#201

-  +#163#145#255#201#163#145#255#201#163#145#255#201#163#145#255#201#163#145#255

-  +#201#163#145#255#201#163#145#255#201#163#145#255#201#163#145#255#201#163#145

-  +#255#201#162#145#255#200#161#143#255#197#155#136#255#187#138'u'#255#173'rX'

-  +#255'|I3'#255'_4 '#255'\0'#28#253'W1 2'#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  ,#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27

-  +#6'\0'#28#216'^2'#31#255'i=)'#255#165'jP'#255#184#133'n'#255#196#152#134#255

-  +#201#162#145#255#202#164#148#255#202#165#148#255#202#165#148#255#202#165#148

-  +#255#202#165#148#255#202#165#148#255#202#165#148#255#202#165#148#255#202#165

-  +#148#255#202#165#148#255#202#165#148#255#202#165#148#255#202#165#148#255#202

-  +#165#148#255#202#165#148#255#202#165#148#255#202#165#148#255#202#165#148#255

-  +#202#165#148#255#202#165#148#255'E82'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#2#2#2#255#1#1#1#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'.&"'#255#202

-  +#165#148#255#202#165#148#255#202#165#148#255#202#165#148#255#202#165#148#255

-  +#202#165#148#255#202#165#148#255#146'wk'#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'eRI'#255#202

-  +#165#148#255#202#165#148#255#202#165#148#255#202#165#148#255#202#165#148#255

-  +#202#165#148#255#202#165#148#255#202#165#148#255#202#165#148#255#202#165#148

-  +#255#202#165#148#255#202#165#148#255#202#165#148#255#202#165#148#255#202#165

-  +#148#255#202#165#148#255#202#165#148#255#202#165#148#255#202#165#148#255#202

-  +#165#148#255#202#165#148#255#202#165#148#255#202#165#148#255#202#165#148#255

-  +#202#165#148#255#202#165#148#255#202#165#148#255#202#165#148#255#202#165#148

-  +#255#202#165#148#255#202#164#147#255#200#160#143#255#193#148#128#255#180'~f'

-  +#255#151'^D'#255'a7$'#255'\1'#29#255'Z0'#28#156'UUU'#3#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0'[/'#27'D\0'#28#255'`5!'#255#128'N8'#255#175'v]'#255#190

-  +#143'{'#255#200#159#142#255#203#165#150#255#204#167#151#255#204#167#151#255

-  +#204#167#151#255#204#167#151#255#204#167#151#255#204#167#151#255#204#167#151

-  +#255#204#167#151#255#204#167#151#255#204#167#151#255#204#167#151#255#204#167

-  +#151#255#204#167#151#255#204#167#151#255#204#167#151#255#204#167#151#255#204

-  +#167#151#255#204#167#151#255#204#167#151#255'N?9'#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255'vaW'#255#204#167#151#255#160#131'w'#255'cRI'#255'WG@'#255

-  +#168#137'|'#255#204#167#151#255#204#167#151#255#204#167#151#255#204#167#151

-  +#255#204#167#151#255#204#167#151#255#204#167#151#255#204#167#151#255#145'wk'

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255'G:4'#255#204#167#151#255#204#167#151#255#204#167#151#255#204

-  +#167#151#255#204#167#151#255#204#167#151#255#204#167#151#255#204#167#151#255

-  +#204#167#151#255#204#167#151#255#204#167#151#255#204#167#151#255#204#167#151

-  +#255#204#167#151#255#204#167#151#255#204#167#151#255#204#167#151#255#204#167

-  +#151#255#204#167#151#255#204#167#151#255#204#167#151#255#204#167#151#255#204

-  +#167#151#255#204#167#151#255#204#167#151#255#204#167#151#255#204#167#151#255

-  +#204#167#151#255#204#167#151#255#204#167#151#255#204#167#150#255#202#165#148

-  +#255#198#155#138#255#186#137's'#255#171'nU'#255'oB.'#255'^2'#31#255'\0'#28

-  +#235'V2"'#21#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27

-  +#169'\1'#29#255'c9&'#255#155'aH'#255#182#128'j'#255#195#152#133#255#202#164

-  +#148#255#205#169#154#255#206#169#155#255#206#169#155#255#206#169#155#255#206

-  +#169#155#255#206#169#155#255#206#169#155#255#206#169#155#255#206#169#155#255

-  +#206#169#155#255#206#169#155#255#206#169#155#255#206#169#155#255#206#169#155

-  +#255#206#169#155#255#206#169#155#255#206#169#155#255#206#169#155#255#206#169

-  +#155#255'VGA'#255#0#0#0#255#0#0#0#255#0#0#0#255#5#4#4#255#206#169#155#255#206

-  +#169#155#255#206#169#155#255#206#169#155#255#206#169#155#255#206#169#155#255

-  +#206#169#155#255#206#169#155#255#206#169#155#255#206#169#155#255#206#169#155

-  +#255#206#169#155#255#206#169#155#255#206#169#155#255#152'}r'#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#2#1#1#255'vaY'#255#206

-  +#169#155#255#206#169#155#255#206#169#155#255#206#169#155#255#206#169#155#255

-  +#206#169#155#255#206#169#155#255#206#169#155#255#206#169#155#255#206#169#155

-  +#255#206#169#155#255#206#169#155#255#206#169#155#255#206#169#155#255#206#169

-  +#155#255#206#169#155#255#206#169#155#255#206#169#155#255#206#169#155#255#206

-  +#169#155#255#206#169#155#255#206#169#155#255#206#169#155#255#206#169#155#255

-  +#206#169#155#255#206#169#155#255#206#169#155#255#206#169#155#255#206#169#155

-  +#255#206#169#155#255#206#169#155#255#205#167#152#255#201#162#145#255#192#147

-  +''#255#177'za'#255#138'T='#255'`6"'#255'\1'#29#255'[0'#29'e'#0#0#0#1#0#0#0#0

-  ,#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27#27'\0'#28#243'^3'#31

-  +#255'pC/'#255#171'oU'#255#188#138'u'#255#199#158#141#255#205#169#154#255#207

-  +#171#157#255#207#172#158#255#207#172#158#255#207#172#158#255#207#172#158#255

-  +#207#172#158#255#207#172#158#255#207#172#158#255#207#172#158#255#207#172#158

-  +#255#207#172#158#255#207#172#158#255#207#172#158#255#207#172#158#255#207#172

-  +#158#255#207#172#158#255#207#172#158#255#207#172#158#255'~i`'#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#22#18#16#255#207#172#158#255#207#172#158#255#207#172#158

-  +#255#207#172#158#255#207#172#158#255#207#172#158#255#207#172#158#255#207#172

-  +#158#255#207#172#158#255#207#172#158#255#207#172#158#255#207#172#158#255#207

-  +#172#158#255#207#172#158#255#207#172#158#255'/''$'#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#9#7#7#255#164#136'~'#255#207#172#158#255#207#172#158

-  +#255#207#172#158#255#207#172#158#255#207#172#158#255#207#172#158#255#207#172

-  +#158#255#207#172#158#255#207#172#158#255#207#172#158#255#207#172#158#255#207

-  +#172#158#255#207#172#158#255#207#172#158#255#207#172#158#255#207#172#158#255

-  +#207#172#158#255#207#172#158#255#207#172#158#255#207#172#158#255#207#172#158

-  +#255#207#172#158#255#207#172#158#255#207#172#158#255#207#172#158#255#207#172

-  +#158#255#207#172#158#255#207#172#158#255#207#172#158#255#207#172#158#255#207

-  +#172#158#255#207#171#157#255#204#167#151#255#196#153#136#255#183#131'l'#255

-  +#160'fK'#255'f;('#255']1'#30#255'[/'#27#199'te^'#3#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27'q\0'#28#255'`6"'#255

-  +#130'P9'#255#177'x`'#255#192#146''#255#203#164#149#255#208#173#159#255#209

-  +#175#161#255#209#175#161#255#209#175#161#255#209#175#161#255#209#175#161#255

-  +#209#175#161#255#209#175#161#255#209#175#161#255#209#175#161#255#209#175#161

-  +#255#209#175#161#255#209#175#161#255#209#175#161#255#209#175#161#255#209#175

-  +#161#255#209#175#161#255#209#175#161#255#199#167#153#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255'.&#'#255#209#175#161#255#209#175#161#255#209#175#161#255#209#175

-  +#161#255#209#175#161#255#209#175#161#255#209#175#161#255#209#175#161#255#209

-  +#175#161#255#209#175#161#255#209#175#161#255#209#175#161#255#209#175#161#255

-  +#209#175#161#255#209#175#161#255#209#175#161#255'*$!'#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255'"'#28#26#255#197#165#153#255#209#175#161#255#209#175#161#255#209#175#161

-  +#255#209#175#161#255#209#175#161#255#209#175#161#255#209#175#161#255#209#175

-  +#161#255#209#175#161#255#209#175#161#255#209#175#161#255#209#175#161#255#209

-  +#175#161#255#209#175#161#255#209#175#161#255#209#175#161#255#209#175#161#255

-  +#209#175#161#255#209#175#161#255#209#175#161#255#209#175#161#255#209#175#161

-  +#255#209#175#161#255#209#175#161#255#209#175#161#255#209#175#161#255#209#175

-  +#161#255#209#175#161#255#209#175#161#255#209#175#161#255#209#175#161#255#209

-  +#174#160#255#207#171#157#255#200#160#144#255#188#139'w'#255#171'pV'#255'rD0'

-  +#255'^3'#31#255'\0'#28#250'\2'#30'1'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27#1'[/'#27#184']1'#30#255'c9&'

-  +#255#148'^E'#255#182#128'j'#255#197#154#136#255#206#170#155#255#210#176#163

-  +#255#211#178#164#255#211#178#165#255#211#178#165#255#211#178#165#255#211#178

-  +#165#255#211#178#165#255#211#178#165#255#211#178#165#255#211#178#165#255#211

-  +#178#165#255#211#178#165#255#211#178#165#255#211#178#165#255#211#178#165#255

-  +#211#178#165#255#211#178#165#255#211#178#165#255#12#10#9#255#0#0#0#255#0#0#0

-  +#255'MA<'#255#211#178#165#255#211#178#165#255#211#178#165#255#211#178#165#255

-  +#211#178#165#255#211#178#165#255#211#178#165#255#211#178#165#255#211#178#165

-  +#255#211#178#165#255#211#178#165#255#211#178#165#255#211#178#165#255#211#178

-  +#165#255#211#178#165#255#211#178#165#255#211#178#165#255#178#150#139#255'D95'

-  +#255#9#7#7#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'V'

-  +'IC'#255#211#178#165#255#211#178#165#255#211#178#165#255#211#178#165#255#211

-  +#178#165#255#211#178#165#255#211#178#165#255#211#178#165#255#211#178#165#255

-  +#211#178#165#255#211#178#165#255#211#178#165#255#211#178#165#255#211#178#165

-  +#255#211#178#165#255#211#178#165#255#211#178#165#255#211#178#165#255#211#178

-  +#165#255#211#178#165#255#211#178#165#255#211#178#165#255#211#178#165#255#211

-  +#178#165#255#211#178#165#255#211#178#165#255#211#178#165#255#211#178#165#255

-  +#211#178#165#255#211#178#165#255#211#178#165#255#211#178#165#255#211#178#164

-  ,#255#209#176#162#255#204#167#151#255#193#148#129#255#177'x`'#255#129'P9'#255

-  +'`6"'#255'\0'#28#255'[0'#28't'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27#21'\0'#28#229'^2'

-  +#31#255'i?+'#255#163'jP'#255#186#136's'#255#200#160#144#255#209#174#160#255

-  +#212#180#167#255#213#181#169#255#213#181#169#255#213#181#169#255#213#181#169

-  +#255#213#181#169#255#213#181#169#255#213#181#169#255#213#181#169#255#213#181

-  +#169#255#213#181#169#255#213#181#169#255#213#181#169#255#213#181#169#255#213

-  +#181#169#255#213#181#169#255#213#181#169#255'.''%'#255#0#0#0#255',%#'#255#200

-  +#170#159#255#213#181#169#255#213#181#169#255#213#181#169#255#213#181#169#255

-  +#213#181#169#255#213#181#169#255#213#181#169#255#213#181#169#255#213#181#169

-  +#255#213#181#169#255#213#181#169#255#213#181#169#255#213#181#169#255#213#181

-  +#169#255#213#181#169#255#213#181#169#255#213#181#169#255#213#181#169#255#213

-  +#181#169#255#139'vo'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'.'

-  +'''$'#255#213#181#169#255#213#181#169#255#213#181#169#255#213#181#169#255#213

-  +#181#169#255#213#181#169#255#213#181#169#255#213#181#169#255#213#181#169#255

-  +#213#181#169#255#213#181#169#255#213#181#169#255#213#181#169#255#213#181#169

-  +#255#213#181#169#255#213#181#169#255#213#181#169#255#213#181#169#255#213#181

-  +#169#255#213#181#169#255#213#181#169#255#213#181#169#255#213#181#169#255#213

-  +#181#169#255#213#181#169#255#213#181#169#255#213#181#169#255#213#181#169#255

-  +#213#181#169#255#213#181#169#255#213#181#169#255#213#181#169#255#212#180#168

-  +#255#211#178#165#255#207#171#157#255#197#155#137#255#182#128'j'#255#148']E'

-  +#255'c9&'#255']1'#30#255'[/'#27#179#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27

-  +'?\0'#28#252'_4 '#255'tF1'#255#172'sY'#255#190#142'z'#255#203#165#150#255#211

-  +#178#164#255#213#183#170#255#214#184#172#255#214#184#172#255#214#184#172#255

-  +#214#184#172#255#214#184#172#255#214#184#172#255#214#184#172#255#214#184#172

-  +#255#214#184#172#255#214#184#172#255#214#184#172#255#214#184#172#255#214#184

-  +#172#255#214#184#172#255#214#184#172#255'ng'#255'{ib'#255#214#184#172#255

-  +#214#184#172#255#214#184#172#255#214#184#172#255#214#184#172#255#214#184#172

-  +#255#214#184#172#255#214#184#172#255#214#184#172#255#214#184#172#255#214#184

-  +#172#255#214#184#172#255#214#184#172#255#214#184#172#255#214#184#172#255#214

-  +#184#172#255#214#184#172#255#214#184#172#255#214#184#172#255#214#184#172#255

-  +#214#184#172#255' '#28#26#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#14#12

-  +#11#255#209#180#168#255#214#184#172#255#214#184#172#255#214#184#172#255#214

-  +#184#172#255#214#184#172#255#214#184#172#255#214#184#172#255#214#184#172#255

-  +#214#184#172#255#214#184#172#255#214#184#172#255#214#184#172#255#214#184#172

-  +#255#214#184#172#255#214#184#172#255#214#184#172#255#214#184#172#255#214#184

-  +#172#255#214#184#172#255#214#184#172#255#214#184#172#255#214#184#172#255#214

-  +#184#172#255#214#184#172#255#214#184#172#255#214#184#172#255#214#184#172#255

-  +#214#184#172#255#214#184#172#255#214#184#172#255#214#184#172#255#214#184#172

-  +#255#213#182#169#255#209#176#161#255#200#161#143#255#186#135'q'#255#161'gO'

-  +#255'h=*'#255'^2'#31#255'\0'#28#226'W-'#26#19#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0'[/'#27'}\1'#29#255'`6"'#255'|M8'#255#175'x^'#255#192#147''#255

-  +#205#170#154#255#213#181#169#255#215#185#173#255#216#187#175#255#216#187#175

-  +#255#216#187#175#255#216#187#175#255#216#187#175#255#216#187#175#255#216#187

-  +#175#255#216#187#175#255#216#187#175#255#216#187#175#255#216#187#175#255#216

-  +#187#175#255#216#187#175#255#216#187#175#255#216#187#175#255#216#187#175#255

-  +#216#187#175#255#216#187#175#255#216#187#175#255#216#187#175#255#216#187#175

-  +#255#216#187#175#255#216#187#175#255#216#187#175#255#216#187#175#255#216#187

-  +#175#255#216#187#175#255#216#187#175#255#216#187#175#255#216#187#175#255#216

-  +#187#175#255#216#187#175#255#216#187#175#255#216#187#175#255#216#187#175#255

-  +#216#187#175#255#187#162#151#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#2#2

-  +#2#255#180#156#146#255#216#187#175#255#216#187#175#255#216#187#175#255#216

-  +#187#175#255#216#187#175#255#216#187#175#255#216#187#175#255#216#187#175#255

-  +#216#187#175#255#216#187#175#255#216#187#175#255#216#187#175#255#216#187#175

-  +#255#216#187#175#255#216#187#175#255#216#187#175#255#216#187#175#255#216#187

-  +#175#255#216#187#175#255#216#187#175#255#216#187#175#255#216#187#175#255#216

-  ,#187#175#255#216#187#175#255#216#187#175#255#216#187#175#255#216#187#175#255

-  +#216#187#175#255#216#187#175#255#216#187#175#255#216#187#175#255#216#187#175

-  +#255#215#185#173#255#211#179#166#255#202#165#148#255#188#139'w'#255#167'mU'

-  +#255'nC/'#255'^3'#31#255'\0'#28#251'Z.'#27';'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0'[/'#27#2'[/'#27#170'\1'#29#255'a7$'#255#130'Q<'#255#178

-  +'{c'#255#195#151#132#255#208#173#159#255#215#185#173#255#217#189#178#255#217

-  +#189#178#255#217#189#178#255#217#189#178#255#217#189#178#255#217#189#178#255

-  +#217#189#178#255#217#189#178#255#217#189#178#255#217#189#178#255#217#189#178

-  +#255#217#189#178#255#217#189#178#255#217#189#178#255#217#189#178#255#217#189

-  +#178#255#217#189#178#255#217#189#178#255#217#189#178#255#217#189#178#255#217

-  +#189#178#255#217#189#178#255#217#189#178#255#217#189#178#255#217#189#178#255

-  +#217#189#178#255#217#189#178#255#217#189#178#255#217#189#178#255#217#189#178

-  +#255#217#189#178#255#217#189#178#255#217#189#178#255#217#189#178#255#217#189

-  +#178#255#217#189#178#255'.(&'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#128

-  +'oi'#255#217#189#178#255#217#189#178#255#217#189#178#255#217#189#178#255#217

-  +#189#178#255#217#189#178#255#217#189#178#255#217#189#178#255#217#189#178#255

-  +#217#189#178#255#217#189#178#255#217#189#178#255#217#189#178#255#217#189#178

-  +#255#217#189#178#255#217#189#178#255#217#189#178#255#217#189#178#255#217#189

-  +#178#255#217#189#178#255#217#189#178#255#217#189#178#255#217#189#178#255#217

-  +#189#178#255#217#189#178#255#217#189#178#255#217#189#178#255#217#189#178#255

-  +#217#189#178#255#217#189#178#255#217#189#178#255#217#189#178#255#217#188#177

-  +#255#213#183#170#255#205#168#153#255#191#144'|'#255#170'rZ'#255'sF2'#255'`5!'

-  +#255'\0'#28#255'Z/'#27'g'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0'[/'#27#6'[/'#27#188']1'#30#255'c9&'#255#136'V?'#255#180'~g'

-  +#255#197#154#136#255#209#176#162#255#216#187#176#255#219#192#181#255#219#192

-  +#182#255#219#193#183#255#219#193#183#255#219#193#183#255#219#193#183#255#219

-  +#193#183#255#219#193#183#255#219#193#183#255#219#193#183#255#219#193#183#255

-  +#219#193#183#255#219#193#183#255#219#193#183#255#219#193#183#255#219#193#183

-  +#255#219#193#183#255#219#193#183#255#219#193#183#255#219#193#183#255#219#193

-  +#183#255#219#193#183#255#219#193#183#255#219#193#183#255#219#193#183#255#219

-  +#193#183#255#219#193#183#255#219#193#183#255#219#193#183#255#219#193#183#255

-  +#219#193#183#255#219#193#183#255#219#193#183#255#219#193#183#255#158#138#131

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#1#255#131'sm'#255#219#193#183#255#219

-  +#193#183#255#219#193#183#255#219#193#183#255#219#193#183#255#219#193#183#255

-  +#219#193#183#255#219#193#183#255#219#193#183#255#219#193#183#255#219#193#183

-  +#255#219#193#183#255#219#193#183#255#219#193#183#255#219#193#183#255#219#193

-  +#183#255#219#193#183#255#219#193#183#255#219#193#183#255#219#193#183#255#219

-  +#193#183#255#219#193#183#255#219#193#183#255#219#193#183#255#219#193#183#255

-  +#219#193#183#255#219#193#183#255#219#193#183#255#219#193#183#255#219#193#183

-  +#255#219#193#183#255#219#192#182#255#218#191#180#255#215#185#173#255#207#172

-  +#156#255#193#147#128#255#174'v^'#255'wJ6'#255'`6"'#255'\1'#29#255'[/'#27'~'#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0'[/'#27#12'[/'#27#203']1'#30#255'c:'''#255#141'YC'#255#181#128'h'#255

-  +#198#155#138#255#210#177#164#255#217#190#178#255#220#194#184#255#221#195#185

-  +#255#221#196#186#255#221#196#186#255#221#196#186#255#221#196#186#255#221#196

-  +#186#255#221#196#186#255#221#196#186#255#221#196#186#255#221#196#186#255#221

-  +#196#186#255#221#196#186#255#221#196#186#255#221#196#186#255#221#196#186#255

-  +#221#196#186#255#221#196#186#255#221#196#186#255#221#196#186#255#221#196#186

-  +#255#221#196#186#255#221#196#186#255#221#196#186#255#221#196#186#255#221#196

-  +#186#255#221#196#186#255#221#196#186#255#221#196#186#255#221#196#186#255#221

-  +#196#186#255#221#196#186#255#221#196#186#255're_'#255#0#0#0#255#0#0#0#255#4#4

-  +#4#255#160#142#134#255#221#196#186#255#221#196#186#255#221#196#186#255#221

-  +#196#186#255#221#196#186#255#221#196#186#255#221#196#186#255#221#196#186#255

-  +#221#196#186#255#221#196#186#255#221#196#186#255#221#196#186#255#221#196#186

-  +#255#221#196#186#255#221#196#186#255#221#196#186#255#221#196#186#255#221#196

-  ,#186#255#221#196#186#255#221#196#186#255#221#196#186#255#221#196#186#255#221

-  +#196#186#255#221#196#186#255#221#196#186#255#221#196#186#255#221#196#186#255

-  +#221#196#186#255#221#196#186#255#221#196#186#255#221#196#186#255#221#195#185

-  +#255#219#193#183#255#216#187#175#255#207#172#158#255#194#148#130#255#176'x_'

-  +#255'{M8'#255'a6#'#255'\1'#29#255'[/'#27#148#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27

-  +#21'\0'#28#217']1'#30#255'c:'''#255#138'XB'#255#181'h'#255#197#156#137#255

-  +#211#178#164#255#218#191#180#255#221#196#187#255#222#198#188#255#223#198#189

-  +#255#223#198#189#255#223#198#189#255#223#198#189#255#223#198#189#255#223#198

-  +#189#255#223#198#189#255#223#198#189#255#223#198#189#255#223#198#189#255#223

-  +#198#189#255#223#198#189#255#223#198#189#255#223#198#189#255#223#198#189#255

-  +#223#198#189#255#223#198#189#255#223#198#189#255#223#198#189#255#223#198#189

-  +#255#223#198#189#255#223#198#189#255#223#198#189#255#223#198#189#255#223#198

-  +#189#255#223#198#189#255#223#198#189#255#223#198#189#255#223#198#189#255#223

-  +#198#189#255#221#196#187#255#6#6#5#255#10#9#8#255#188#167#159#255#223#198#189

-  +#255#223#198#189#255#223#198#189#255#223#198#189#255#223#198#189#255#223#198

-  +#189#255#223#198#189#255#223#198#189#255#223#198#189#255#223#198#189#255#223

-  +#198#189#255#223#198#189#255#223#198#189#255#223#198#189#255#223#198#189#255

-  +#223#198#189#255#223#198#189#255#223#198#189#255#223#198#189#255#223#198#189

-  +#255#223#198#189#255#223#198#189#255#223#198#189#255#223#198#189#255#223#198

-  +#189#255#223#198#189#255#223#198#189#255#223#198#189#255#223#198#189#255#223

-  +#198#189#255#223#198#189#255#222#198#188#255#221#196#186#255#217#188#177#255

-  +#208#173#159#255#193#149#129#255#174'w^'#255'zM8'#255'a7$'#255'\1'#29#255'[/'

-  +#27#170'[/'#27#2#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27' \0'#28#223']1'#30

-  +#255'c:'''#255#132'T?'#255#180'~g'#255#197#155#136#255#211#178#164#255#219

-  +#193#181#255#223#198#189#255#224#201#192#255#224#201#192#255#224#201#192#255

-  +#224#201#192#255#224#201#192#255#224#201#192#255#224#201#192#255#224#201#192

-  +#255#224#201#192#255#224#201#192#255#224#201#192#255#224#201#192#255#224#201

-  +#192#255#224#201#192#255#224#201#192#255#224#201#192#255#224#201#192#255#224

-  +#201#192#255#224#201#192#255#224#201#192#255#224#201#192#255#224#201#192#255

-  +#224#201#192#255#224#201#192#255#224#201#192#255#224#201#192#255#224#201#192

-  +#255#224#201#192#255#224#201#192#255#224#201#192#255#224#201#192#255#134'xr'

-  +#255#205#184#176#255#224#201#192#255#224#201#192#255#224#201#192#255#224#201

-  +#192#255#224#201#192#255#224#201#192#255#224#201#192#255#224#201#192#255#224

-  +#201#192#255#224#201#192#255#224#201#192#255#224#201#192#255#224#201#192#255

-  +#224#201#192#255#224#201#192#255#224#201#192#255#224#201#192#255#224#201#192

-  +#255#224#201#192#255#224#201#192#255#224#201#192#255#224#201#192#255#224#201

-  +#192#255#224#201#192#255#224#201#192#255#224#201#192#255#224#201#192#255#224

-  +#201#192#255#224#201#192#255#224#201#192#255#224#201#192#255#223#200#191#255

-  +#222#197#187#255#217#189#178#255#207#172#158#255#192#147''#255#171'u\'#255

-  +'vI6'#255'a6#'#255'\1'#29#255'[/'#27#179'[/'#27#6#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0'[/'#27#25'\0'#28#209']1'#30#255'c9&'#255#128'Q='#255#177

-  +'{c'#255#195#151#133#255#209#176#162#255#219#192#181#255#223#200#191#255#225

-  +#203#194#255#226#204#196#255#226#204#196#255#226#204#196#255#226#204#196#255

-  +#226#204#196#255#226#204#196#255#226#204#196#255#226#204#196#255#226#204#196

-  +#255#226#204#196#255#226#204#196#255#226#204#196#255#226#204#196#255#226#204

-  +#196#255#226#204#196#255#226#204#196#255#226#204#196#255#226#204#196#255#226

-  +#204#196#255#226#204#196#255#226#204#196#255#226#204#196#255#226#204#196#255

-  +#226#204#196#255#226#204#196#255#226#204#196#255#226#204#196#255#226#204#196

-  +#255#226#204#196#255#226#204#196#255#226#204#196#255#226#204#196#255#226#204

-  +#196#255#226#204#196#255#226#204#196#255#226#204#196#255#226#204#196#255#226

-  +#204#196#255#226#204#196#255#226#204#196#255#226#204#196#255#226#204#196#255

-  +#226#204#196#255#226#204#196#255#226#204#196#255#226#204#196#255#226#204#196

-  ,#255#226#204#196#255#226#204#196#255#226#204#196#255#226#204#196#255#226#204

-  +#196#255#226#204#196#255#226#204#196#255#226#204#196#255#226#204#196#255#226

-  +#204#196#255#226#204#196#255#226#204#196#255#226#204#196#255#226#203#195#255

-  +#225#202#193#255#222#198#188#255#217#188#177#255#206#169#155#255#191#144'|'

-  +#255#168'qY'#255'rG3'#255'`6"'#255'\1'#29#255'[/'#27#159'[/'#27#4#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27#15'\0'#28#193']1'

-  +#30#255'b8%'#255'yM8'#255#170'u\'#255#192#146'~'#255#207#171#156#255#217#189

-  +#178#255#223#200#191#255#226#205#197#255#227#206#198#255#228#207#199#255#228

-  +#207#199#255#228#207#199#255#228#207#199#255#228#207#199#255#228#207#199#255

-  +#228#207#199#255#228#207#199#255#228#207#199#255#228#207#199#255#228#207#199

-  +#255#228#207#199#255#228#207#199#255#228#207#199#255#228#207#199#255#228#207

-  +#199#255#228#207#199#255#228#207#199#255#228#207#199#255#228#207#199#255#228

-  +#207#199#255#228#207#199#255#228#207#199#255#228#207#199#255#228#207#199#255

-  +#228#207#199#255#228#207#199#255#228#207#199#255#228#207#199#255#228#207#199

-  +#255#228#207#199#255#228#207#199#255#228#207#199#255#228#207#199#255#228#207

-  +#199#255#228#207#199#255#228#207#199#255#228#207#199#255#228#207#199#255#228

-  +#207#199#255#228#207#199#255#228#207#199#255#228#207#199#255#228#207#199#255

-  +#228#207#199#255#228#207#199#255#228#207#199#255#228#207#199#255#228#207#199

-  +#255#228#207#199#255#228#207#199#255#228#207#199#255#228#207#199#255#228#207

-  +#199#255#228#207#199#255#228#207#199#255#228#207#199#255#227#206#198#255#226

-  +#204#196#255#222#198#188#255#215#185#173#255#202#165#148#255#187#137'u'#255

-  +#159'jR'#255'nD0'#255'`6"'#255'\1'#29#255'[/'#27#137#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27#8'[/'

-  +#27#175'\1'#29#255'`6"'#255'nD0'#255#158'iR'#255#187#137'u'#255#202#165#148

-  +#255#215#186#174#255#223#199#190#255#227#205#198#255#229#208#201#255#229#209

-  +#202#255#229#210#202#255#229#210#202#255#229#210#202#255#229#210#202#255#229

-  +#210#202#255#229#210#202#255#229#210#202#255#229#210#202#255#229#210#202#255

-  +#229#210#202#255#229#210#202#255#229#210#202#255#229#210#202#255#229#210#202

-  +#255#229#210#202#255#229#210#202#255#229#210#202#255#229#210#202#255#229#210

-  +#202#255#229#210#202#255#229#210#202#255#229#210#202#255#229#210#202#255#229

-  +#210#202#255#229#210#202#255#229#210#202#255#229#210#202#255#229#210#202#255

-  +#229#210#202#255#229#210#202#255#229#210#202#255#229#210#202#255#229#210#202

-  +#255#229#210#202#255#229#210#202#255#229#210#202#255#229#210#202#255#229#210

-  +#202#255#229#210#202#255#229#210#202#255#229#210#202#255#229#210#202#255#229

-  +#210#202#255#229#210#202#255#229#210#202#255#229#210#202#255#229#210#202#255

-  +#229#210#202#255#229#210#202#255#229#210#202#255#229#210#202#255#229#210#202

-  +#255#229#210#202#255#229#209#202#255#228#207#201#255#226#204#197#255#221#196

-  +#187#255#212#180#168#255#198#157#140#255#183#130'l'#255#143'^G'#255'g>+'#255

-  +'_4 '#255'\1'#29#254'[/'#27'q'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27#3'[/'#27

-  +#132'\1'#29#254'_4 '#255'g>+'#255#140'[F'#255#181#129'j'#255#198#155#138#255

-  +#211#178#165#255#221#195#185#255#227#204#197#255#230#209#203#255#230#212#205

-  +#255#231#212#206#255#231#212#206#255#231#212#206#255#231#212#206#255#231#212

-  +#206#255#231#212#206#255#231#212#206#255#231#212#206#255#231#212#206#255#231

-  +#212#206#255#231#212#206#255#231#212#206#255#231#212#206#255#231#212#206#255

-  +#231#212#206#255#231#212#206#255#231#212#206#255#231#212#206#255#231#212#206

-  +#255#231#212#206#255#231#212#206#255#231#212#206#255#231#212#206#255#231#212

-  +#206#255#231#212#206#255#231#212#206#255#231#212#206#255#231#212#206#255#231

-  +#212#206#255#231#212#206#255#231#212#206#255#231#212#206#255#231#212#206#255

-  +#231#212#206#255#231#212#206#255#231#212#206#255#231#212#206#255#231#212#206

-  +#255#231#212#206#255#231#212#206#255#231#212#206#255#231#212#206#255#231#212

-  +#206#255#231#212#206#255#231#212#206#255#231#212#206#255#231#212#206#255#231

-  ,#212#206#255#231#212#206#255#231#212#206#255#231#211#206#255#230#211#205#255

-  +#229#209#201#255#225#203#194#255#219#191#181#255#208#173#159#255#193#149#129

-  +#255#173'w`'#255'~Q='#255'c:'''#255'^2'#31#255'\0'#28#238'[/'#27'O'#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27'F\0'#28#234'^2'#31#255'c:'

-  +''''#255'zN:'#255#168's['#255#190#143'{'#255#205#167#152#255#217#187#177#255

-  +#225#201#193#255#229#209#202#255#232#213#207#255#232#214#209#255#233#215#210

-  +#255#233#215#210#255#233#215#210#255#233#215#210#255#233#215#210#255#233#215

-  +#210#255#233#215#210#255#233#215#210#255#233#215#210#255#233#215#210#255#233

-  +#215#210#255#233#215#210#255#233#215#210#255#233#215#210#255#233#215#210#255

-  +#233#215#210#255#233#215#210#255#233#215#210#255#233#215#210#255#233#215#210

-  +#255#233#215#210#255#233#215#210#255#233#215#210#255#233#215#210#255#233#215

-  +#210#255#233#215#210#255#233#215#210#255#233#215#210#255#233#215#210#255#233

-  +#215#210#255#233#215#210#255#233#215#210#255#233#215#210#255#233#215#210#255

-  +#233#215#210#255#233#215#210#255#233#215#210#255#233#215#210#255#233#215#210

-  +#255#233#215#210#255#233#215#210#255#233#215#210#255#233#215#210#255#233#215

-  +#210#255#233#215#210#255#233#215#210#255#232#215#209#255#232#214#208#255#231

-  +#212#206#255#228#207#201#255#223#198#189#255#213#182#170#255#200#160#144#255

-  +#186#136'r'#255#158'iR'#255'pE2'#255'a7$'#255']1'#30#255'\0'#28#201'[/'#27#31

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27

-  +#26'\0'#28#193']1'#30#255'`6"'#255'iA.'#255#142']H'#255#179#128'h'#255#196

-  +#153#135#255#210#176#163#255#220#194#184#255#227#205#198#255#231#212#206#255

-  +#233#216#211#255#234#217#212#255#234#218#213#255#234#218#213#255#234#218#213

-  +#255#234#218#213#255#234#218#213#255#234#218#213#255#234#218#213#255#234#218

-  +#213#255#234#218#213#255#234#218#213#255#234#218#213#255#234#218#213#255#234

-  +#218#213#255#234#218#213#255#234#218#213#255#234#218#213#255#234#218#213#255

-  +#234#218#213#255#234#218#213#255#234#218#213#255#234#218#213#255#234#218#213

-  +#255#234#218#213#255#234#218#213#255#234#218#213#255#234#218#213#255#234#218

-  +#213#255#234#218#213#255#234#218#213#255#234#218#213#255#234#218#213#255#234

-  +#218#213#255#234#218#213#255#234#218#213#255#234#218#213#255#234#218#213#255

-  +#234#218#213#255#234#218#213#255#234#218#213#255#234#218#213#255#234#218#213

-  +#255#234#218#213#255#234#217#212#255#234#216#211#255#233#215#210#255#230#211

-  +#205#255#226#203#195#255#218#190#179#255#206#170#155#255#192#146''#255#172

-  +'v`'#255#129'S?'#255'e<)'#255'_4 '#255'\1'#29#255'[/'#27#143'[/'#27#5#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +'[/'#27#3'[/'#27'\0'#28#248'^3'#31#255'c:'''#255'sI6'#255#158'kT'#255#186

-  +#136's'#255#200#160#143#255#213#180#169#255#222#197#187#255#229#208#201#255

-  +#232#214#209#255#234#218#213#255#235#219#215#255#236#220#215#255#236#220#216

-  +#255#236#220#216#255#236#220#216#255#236#220#216#255#236#220#216#255#236#220

-  +#216#255#236#220#216#255#236#220#216#255#236#220#216#255#236#220#216#255#236

-  +#220#216#255#236#220#216#255#236#220#216#255#236#220#216#255#236#220#216#255

-  +#236#220#216#255#236#220#216#255#236#220#216#255#236#220#216#255#236#220#216

-  +#255#236#220#216#255#236#220#216#255#236#220#216#255#236#220#216#255#236#220

-  +#216#255#236#220#216#255#236#220#216#255#236#220#216#255#236#220#216#255#236

-  +#220#216#255#236#220#216#255#236#220#216#255#236#220#216#255#236#220#216#255

-  +#236#220#216#255#236#220#216#255#236#220#216#255#236#220#215#255#235#219#215

-  +#255#234#217#212#255#232#213#207#255#227#205#198#255#219#193#183#255#209#176

-  +#162#255#196#153#136#255#181#129'l'#255#145'`K'#255'lB0'#255'a7$'#255']1'#30

-  +#255'\0'#28#231'[/'#27'O'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  ,#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27'$\0'

-  +#28#185'\1'#29#255'`5!'#255'f=*'#255#128'S>'#255#166'r['#255#188#139'v'#255

-  +#201#161#145#255#213#181#169#255#223#197#189#255#229#209#202#255#233#216#210

-  +#255#235#219#215#255#236#221#217#255#237#222#219#255#238#223#220#255#238#223

-  +#220#255#238#223#220#255#238#223#220#255#238#223#220#255#238#223#220#255#238

-  +#223#220#255#238#223#220#255#238#223#220#255#238#223#220#255#238#223#220#255

-  +#238#223#220#255#238#223#220#255#238#223#220#255#238#223#220#255#238#223#220

-  +#255#238#223#220#255#238#223#220#255#238#223#220#255#238#223#220#255#238#223

-  +#220#255#238#223#220#255#238#223#220#255#238#223#220#255#238#223#220#255#238

-  +#223#220#255#238#223#220#255#238#223#220#255#238#223#220#255#238#223#220#255

-  +#238#223#220#255#238#223#220#255#237#222#219#255#237#222#218#255#236#221#217

-  +#255#235#218#214#255#232#214#208#255#228#206#199#255#220#193#184#255#210#176

-  +#163#255#197#155#137#255#184#134'o'#255#155'iR'#255'uI7'#255'c:'''#255'^3'#31

-  +#255'\1'#29#252'[/'#27#144'[/'#27#14#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27'U\0'#28#228']1'#30#255'`6"'#255'g>,'#255'R?'

-  +#255#164'qZ'#255#188#139'v'#255#200#159#144#255#211#179#166#255#220#194#184

-  +#255#228#206#199#255#232#214#209#255#235#219#215#255#237#222#219#255#238#225

-  +#221#255#239#225#222#255#239#225#222#255#239#226#223#255#239#226#223#255#239

-  +#226#223#255#239#226#223#255#239#226#223#255#239#226#223#255#239#226#223#255

-  +#239#226#223#255#239#226#223#255#239#226#223#255#239#226#223#255#239#226#223

-  +#255#239#226#223#255#239#226#223#255#239#226#223#255#239#226#223#255#239#226

-  +#223#255#239#226#223#255#239#226#223#255#239#226#223#255#239#226#223#255#239

-  +#226#223#255#239#226#223#255#239#226#223#255#239#226#223#255#239#225#222#255

-  +#238#225#221#255#238#224#220#255#237#222#218#255#235#218#214#255#231#212#206

-  +#255#226#203#195#255#218#191#180#255#209#174#160#255#197#154#136#255#184#132

-  +'o'#255#154'hR'#255'uK8'#255'e;('#255'_4 '#255'\1'#29#255'\0'#28#197'[/'#27

-  +'/'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0'[/'#27#12'[/'#27#134'\0'#28#242'^2'#31#255'a5#'#255'g>+'#255

-  +'|Q='#255#160'lV'#255#184#133'p'#255#196#152#134#255#207#170#156#255#216#187

-  +#175#255#224#200#192#255#230#210#203#255#234#216#211#255#236#220#216#255#238

-  +#224#220#255#239#226#223#255#240#227#225#255#240#228#225#255#240#228#225#255

-  +#241#229#226#255#241#229#226#255#241#229#226#255#241#229#226#255#241#229#227

-  +#255#241#229#227#255#241#229#227#255#241#229#227#255#241#229#227#255#241#229

-  +#227#255#241#229#227#255#241#229#227#255#241#229#227#255#241#229#226#255#241

-  +#229#226#255#241#229#226#255#241#229#226#255#240#228#225#255#240#228#225#255

-  +#240#227#224#255#239#225#222#255#238#223#220#255#236#220#215#255#233#215#210

-  +#255#228#207#201#255#222#196#187#255#213#183#170#255#204#166#150#255#192#147

-  +''#255#179'i'#255#150'fO'#255'tI7'#255'e;('#255'`5!'#255']1'#30#255'\0'#28

-  +#223'[/'#27'`[/'#27#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27#17'[/'#27'\'

-  +'0'#28#238']1'#30#255'`6"'#255'e<)'#255'rI6'#255#142'_J'#255#170'wa'#255#188

-  +#140'w'#255#199#158#141#255#208#173#159#255#216#186#175#255#222#196#187#255

-  ,#227#206#198#255#232#213#208#255#235#219#214#255#237#222#218#255#238#224#220

-  +#255#239#226#223#255#240#227#225#255#241#229#226#255#242#230#228#255#242#230

-  +#228#255#242#231#229#255#242#231#229#255#242#231#229#255#242#231#229#255#242

-  +#231#229#255#242#231#229#255#242#231#229#255#242#230#228#255#241#229#227#255

-  +#241#229#226#255#240#227#225#255#239#225#222#255#238#224#220#255#236#221#217

-  +#255#234#218#213#255#231#211#206#255#226#204#196#255#220#194#184#255#214#183

-  +#171#255#206#169#155#255#196#153#135#255#185#135'r'#255#162'q['#255#134'XD'

-  +#255'lC1'#255'c:'''#255'_4 '#255']0'#30#255'\0'#28#217'[/'#27'`[/'#27#4#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27#13'[/'

-  +#27'w\0'#28#233']1'#30#255'_4 '#255'b8%'#255'h?-'#255'zN;'#255#149'dP'#255

-  +#170'xa'#255#187#137't'#255#194#150#131#255#202#164#148#255#210#176#163#255

-  +#216#187#175#255#220#194#184#255#224#200#192#255#228#207#199#255#231#212#206

-  +#255#234#217#211#255#236#220#215#255#236#222#217#255#237#222#219#255#238#223

-  +#220#255#238#223#220#255#238#224#220#255#238#223#220#255#237#223#219#255#237

-  +#223#218#255#236#221#217#255#235#219#215#255#233#216#210#255#230#211#204#255

-  +#227#204#197#255#223#199#190#255#219#192#183#255#215#185#173#255#208#173#159

-  +#255#200#160#144#255#192#146''#255#183#133'o'#255#164's\'#255#142']J'#255'r'

-  +'I7'#255'f=*'#255'a6#'#255'^3'#31#255'\1'#29#255'\0'#28#210'[/'#27'X[/'#27#2

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27#5'[/'#27'P\0'#28#173'\1'#29#249']1'#30#255'`'

-  +'5!'#255'c9&'#255'g>,'#255'qH6'#255#133'YE'#255#153'iT'#255#172'yd'#255#186

-  +#136'q'#255#191#144'}'#255#196#153#136#255#202#162#146#255#207#171#156#255

-  +#211#178#165#255#214#183#171#255#216#186#175#255#217#188#177#255#218#190#179

-  +#255#219#191#182#255#219#192#182#255#219#192#181#255#218#189#179#255#217#187

-  +#177#255#215#185#173#255#213#182#169#255#210#176#163#255#205#169#154#255#200

-  +#160#144#255#195#151#133#255#190#142'z'#255#183#132'o'#255#167'u_'#255#148'e'

-  +'P'#255'S@'#255'mE2'#255'f=*'#255'b7%'#255'_4 '#255']1'#30#255'\0'#28#235'['

-  +'/'#27#147'[/'#27'5'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0'[/'#27#18'[/'#27'i\0'#28#199'\1'#29#254']1'#30#255'_4 '#255

-  +'a6#'#255'd9('#255'g>,'#255'oF3'#255'}Q>'#255#138'[G'#255#149'fQ'#255#160'oZ'

-  +#255#170'xa'#255#176'}h'#255#180#128'l'#255#183#133'p'#255#186#136's'#255#188

-  +#138'u'#255#188#139'v'#255#187#138'u'#255#186#135'r'#255#183#132'n'#255#178

-  +#128'j'#255#174'|e'#255#168'v`'#255#158'mW'#255#146'bN'#255#133'YE'#255'yM;'

-  +#255'mC1'#255'g>+'#255'c9&'#255'`6"'#255'^3'#31#255']1'#30#255'\0'#28#248'\0'

-  +#28#173'[/'#27'O[/'#27#5#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  ,#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27#20'[/'#27

-  +'V\0'#28#153'\0'#28#220'\1'#29#255']1'#30#255'^3'#31#255'`5!'#255'a7$'#255'c'

-  +'9&'#255'e;('#255'f=*'#255'g>,'#255'i@.'#255'jB0'#255'mE2'#255'qH5'#255'tJ7'

-  +#255'qG5'#255'lC1'#255'jA0'#255'h?-'#255'g>+'#255'e<)'#255'd:('#255'c9&'#255

-  +'a6#'#255'`5!'#255'^2'#31#255']1'#30#255'\1'#29#252'\0'#28#201'[/'#27#134'[/'

-  +#27'C[/'#27#8#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27#31'[/'#27'_[/'#27#138'\0'#28#176'\0'#28

-  +#213'\0'#28#248'\1'#29#255'\1'#29#255'\1'#29#255']1'#30#255'^2'#31#255'^2'#31

-  +#255'^3'#31#255'^3'#31#255'^3'#31#255'^2'#31#255']1'#30#255']1'#30#255'\1'#29

-  +#255'\1'#29#255'\1'#29#255'\0'#28#241'\0'#28#203'\0'#28#165'[/'#27'[/'#27'N'

-  +'[/'#27#15#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0'[/'#27#2'[/'#27'![/'#27'E[/'#27'U[/'#27'a[/'#27'm[/'

-  +#27'y[/'#27#134'\0'#29#141'[/'#27#130'[/'#27'v[/'#27'j[/'#27'^[/'#27'Q[/'#27

-  +'=[/'#27#23#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  ,#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#255#255#255#255#255#255#255#128#0

-  +#255#255#255#255#255#255#255#255#255#255#255#255#255#224#0#0#3#255#255#255

-  +#255#255#255#255#255#255#255#255#254#0#0#0#0'?'#255#255#255#255#255#255#255

-  +#255#255#255#240#0#0#0#0#7#255#255#255#255#255#255#255#255#255#255#128#0#0#0

-  +#0#0#255#255#255#255#255#255#255#255#255#254#0#0#0#0#0#0'?'#255#255#255#255

-  +#255#255#255#255#248#0#0#0#0#0#0#15#255#255#255#255#255#255#255#255#224#0#0#0

-  +#0#0#0#3#255#255#255#255#255#255#255#255#128#0#0#0#0#0#0#0#255#255#255#255

-  +#255#255#255#254#0#0#0#0#0#0#0#0'?'#255#255#255#255#255#255#252#0#0#0#0#0#0#0

-  +#0#31#255#255#255#255#255#255#240#0#0#0#0#0#0#0#0#7#255#255#255#255#255#255

-  +#224#0#0#0#0#0#0#0#0#3#255#255#255#255#255#255#128#0#0#0#0#0#0#0#0#1#255#255

-  +#255#255#255#255#0#0#0#0#0#0#0#0#0#0''#255#255#255#255#254#0#0#0#0#0#0#0#0#0

-  +#0'?'#255#255#255#255#252#0#0#0#0#0#0#0#0#0#0#31#255#255#255#255#248#0#0#0#0

-  +#0#0#0#0#0#0#15#255#255#255#255#240#0#0#0#0#0#0#0#0#0#0#7#255#255#255#255#224

-  +#0#0#0#0#0#0#0#0#0#0#3#255#255#255#255#192#0#0#0#0#0#0#0#0#0#0#1#255#255#255

-  +#255#128#0#0#0#0#0#0#0#0#0#0#0#255#255#255#255#0#0#0#0#0#0#0#0#0#0#0#0''#255

-  +#255#254#0#0#0#0#0#0#0#0#0#0#0#0''#255#255#254#0#0#0#0#0#0#0#0#0#0#0#0'?'

-  +#255#255#252#0#0#0#0#0#0#0#0#0#0#0#0#31#255#255#248#0#0#0#0#0#0#0#0#0#0#0#0

-  +#15#255#255#240#0#0#0#0#0#0#0#0#0#0#0#0#7#255#255#240#0#0#0#0#0#0#0#0#0#0#0#0

-  +#7#255#255#224#0#0#0#0#0#0#0#0#0#0#0#0#3#255#255#192#0#0#0#0#0#0#0#0#0#0#0#0

-  +#3#255#255#192#0#0#0#0#0#0#0#0#0#0#0#0#1#255#255#128#0#0#0#0#0#0#0#0#0#0#0#0

-  +#1#255#255#128#0#0#0#0#0#0#0#0#0#0#0#0#0#255#255#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#255#255#0#0#0#0#0#0#0#0#0#0#0#0#0#0''#254#0#0#0#0#0#0#0#0#0#0#0#0#0#0''

-  +#254#0#0#0#0#0#0#0#0#0#0#0#0#0#0'?'#254#0#0#0#0#0#0#0#0#0#0#0#0#0#0'?'#252#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0'?'#252#0#0#0#0#0#0#0#0#0#0#0#0#0#0#31#252#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#31#248#0#0#0#0#0#0#0#0#0#0#0#0#0#0#15#248#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#15#248#0#0#0#0#0#0#0#0#0#0#0#0#0#0#15#240#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#7#240#0#0#0#0#0#0#0#0#0#0#0#0#0#0#7#240#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#7#224#0#0#0#0#0#0#0#0#0#0#0#0#0#0#7#224#0#0#0#0#0#0#0#0#0#0#0#0#0#0#3#224#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#3#224#0#0#0#0#0#0#0#0#0#0#0#0#0#0#3#224#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#3#224#0#0#0#0#0#0#0#0#0#0#0#0#0#0#3#192#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#3#192#0#0#0#0#0#0#0#0#0#0#0#0#0#0#3#192#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#1#192#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1#192#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1#192

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1#192#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1#192#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#1#192#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1#192#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#1#192#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1#192#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#1#192#0#0#0#0#0#0#0#0#0#0#0#0#0#0#3#192#0#0#0#0#0#0#0#0#0#0#0#0#0#0#3

-  +#224#0#0#0#0#0#0#0#0#0#0#0#0#0#0#3#224#0#0#0#0#0#0#0#0#0#0#0#0#0#0#3#224#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#3#224#0#0#0#0#0#0#0#0#0#0#0#0#0#0#3#224#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#3#240#0#0#0#0#0#0#0#0#0#0#0#0#0#0#7#240#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#7#240#0#0#0#0#0#0#0#0#0#0#0#0#0#0#7#240#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#15#248#0#0#0#0#0#0#0#0#0#0#0#0#0#0#15#248#0#0#0#0#0#0#0#0#0#0#0#0#0#0#15#248

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#15#252#0#0#0#0#0#0#0#0#0#0#0#0#0#0#31#252#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#31#252#0#0#0#0#0#0#0#0#0#0#0#0#0#0'?'#254#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0'?'#254#0#0#0#0#0#0#0#0#0#0#0#0#0#0'?'#254#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0''#255#0#0#0#0#0#0#0#0#0#0#0#0#0#0''#255#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#255#255#128#0#0#0#0#0#0#0#0#0#0#0#0#0#255#255#128#0#0#0#0#0#0#0#0#0#0#0

-  +#0#1#255#255#128#0#0#0#0#0#0#0#0#0#0#0#0#1#255#255#192#0#0#0#0#0#0#0#0#0#0#0

-  +#0#3#255#255#192#0#0#0#0#0#0#0#0#0#0#0#0#3#255#255#224#0#0#0#0#0#0#0#0#0#0#0

-  +#0#7#255#255#224#0#0#0#0#0#0#0#0#0#0#0#0#15#255#255#240#0#0#0#0#0#0#0#0#0#0#0

-  ,#0#15#255#255#248#0#0#0#0#0#0#0#0#0#0#0#0#31#255#255#248#0#0#0#0#0#0#0#0#0#0

-  +#0#0'?'#255#255#252#0#0#0#0#0#0#0#0#0#0#0#0''#255#255#252#0#0#0#0#0#0#0#0#0

-  +#0#0#0#255#255#255#254#0#0#0#0#0#0#0#0#0#0#0#0#255#255#255#255#0#0#0#0#0#0#0

-  +#0#0#0#0#1#255#255#255#255#128#0#0#0#0#0#0#0#0#0#0#3#255#255#255#255#128#0#0

-  +#0#0#0#0#0#0#0#0#7#255#255#255#255#192#0#0#0#0#0#0#0#0#0#0#15#255#255#255#255

-  +#224#0#0#0#0#0#0#0#0#0#0#31#255#255#255#255#240#0#0#0#0#0#0#0#0#0#0#31#255

-  +#255#255#255#248#0#0#0#0#0#0#0#0#0#0'?'#255#255#255#255#252#0#0#0#0#0#0#0#0#0

-  +#0''#255#255#255#255#254#0#0#0#0#0#0#0#0#0#1#255#255#255#255#255#255#0#0#0#0

-  +#0#0#0#0#0#3#255#255#255#255#255#255#128#0#0#0#0#0#0#0#0#7#255#255#255#255

-  +#255#255#224#0#0#0#0#0#0#0#0#15#255#255#255#255#255#255#240#0#0#0#0#0#0#0#0

-  +#31#255#255#255#255#255#255#248#0#0#0#0#0#0#0#0''#255#255#255#255#255#255

-  +#254#0#0#0#0#0#0#0#0#255#255#255#255#255#255#255#255#128#0#0#0#0#0#0#3#255

-  +#255#255#255#255#255#255#255#192#0#0#0#0#0#0#7#255#255#255#255#255#255#255

-  +#255#240#0#0#0#0#0#0#31#255#255#255#255#255#255#255#255#252#0#0#0#0#0#0''

-  +#255#255#255#255#255#255#255#255#255#0#0#0#0#0#3#255#255#255#255#255#255#255

-  +#255#255#255#224#0#0#0#0#15#255#255#255#255#255#255#255#255#255#255#252#0#0#0

-  +#0''#255#255#255#255#255#255#255#255#255#255#255#192#0#0#7#255#255#255#255

-  +#255#255#255#255#255#255#255#255#254#0#1#255#255#255#255#255#255#255#255#255

-  +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255

-  +#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255#255

-  +#255#255#255#255#255#255#255#255'('#0#0#0'@'#0#0#0#128#0#0#0#1#0' '#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'@@@'#4'III'#7'III'#7'33'

-  +'3'#5#128#128#128#2#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'UUU'#6'DDD'#15

-  +'FFF'#22'EEE'#26'DDD'#30'III#GGG/DDD<DDD@CCC5FFF(BBB'#31'BBB'#27'@@@'#24'GGG'

-  +#18'999'#9#128#128#128#2#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#128#128#128

-  +#2'999'#9'GGG'#18'DDD'#30'CCC9FFF_DDDEDD'#146'DDC'#159'EED'#170'DDC'#175'DD'

-  +'C'#178'DDC'#179'EED'#177'EED'#173'DCC'#164'EDD'#151'BBB'#136'CCCoCCCHCCC&CC'

-  +'C'#23';;;'#13'@@@'#4#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#128#128#128#2'777'#14'DDD"EEEFCCCoEDD'#150'DDC'#175'FFC'

-  +#187'GGC'#195'IFC'#200'LD<'#208'OB:'#215'RA6'#221'WA0'#226'TA3'#223'PB9'#218

-  +'MD<'#212'JD@'#206'IGD'#200'HGD'#197'EED'#191'DDC'#180'CCC'#163'DDD'#128'DDD'

-  +'VGGG/III'#21'+++'#6#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#128#128#128

-  +#2'FFF'#11'BBB#CCCXEED'#149'EEB'#179'GFC'#191'MC<'#208'W?.'#228'd=%'#246'i?!'

-  +#252'j?'#30#255'l?'#31#255'nA'#31#255'sF!'#253'zL%'#253'wG#'#253'oB!'#254'm@'

-  +#31#255'k>'#30#255'j>'#31#253'g>#'#249'[>+'#235'PB8'#218'HEC'#203'FEC'#195'E'

-  +'ED'#184'DDD'#166'DDDqEEE4@@@'#20'@@@'#4#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'999'#9'BBB'#31

-  +'CCCPEDD'#143'FFE'#182'NB:'#211'Z?-'#231'g>#'#249'l?'#31#255'zK$'#253#150']*'

-  +#255#165'h.'#255#172'm0'#255#180's2'#255#186'x4'#255#189'{4'#255#192'5'#255

-  +#190'|4'#255#187'x4'#255#182'u3'#255#175'p1'#255#167'j/'#255#156'b+'#255#131

-  +'R&'#254'oB '#255'i>!'#253'_>)'#238'S@6'#222'FEB'#203'DDC'#190'DDD'#164'CCCk'

-  +'CCC.PPP'#16'UUU'#3#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  ,#0#0#0#128#128#128#2'CCC'#19'BBBBDDD'#136'EED'#179'LA9'#209'`=&'#243'k?'#31

-  +#255'N&'#254#154'`,'#255#176'q1'#255#191'~5'#255#200#138'7'#255#207#145'8'

-  +#255#210#149'9'#255#213#153'9'#255#217#157':'#255#219#160';'#255#221#162';'

-  +#255#220#162':'#255#217#158':'#255#215#155':'#255#211#151'9'#255#208#146'8'

-  +#255#203#141'8'#255#194#129'6'#255#183'v4'#255#163'f.'#255#137'U('#254'qC"'

-  +#254'f<!'#250'T?4'#224'GDB'#202'CCB'#188'DDD'#158'BBB`>>>!+++'#6#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'@@@'#4'BBB'#27'CCC[CCB'#165'LD='#203']<'

-  +'('#238'j> '#255#135'R('#254#176'p3'#255#193#128'7'#255#202#139'9'#255#211

-  +#151';'#255#220#162'<'#255#227#170'>'#255#232#176'?'#255#234#180'?'#255#237

-  +#182'?'#255#239#185'@'#255#241#187'@'#255#242#188'A'#255#241#187'@'#255#240

-  +#185'A'#255#238#183'@'#255#235#180'@'#255#233#178'?'#255#229#173'>'#255#222

-  +#165'='#255#215#156'<'#255#205#144':'#255#196#133'8'#255#185'x5'#255#152'^-'

-  +#255'oC"'#254'd<#'#247'QA7'#220'FFE'#200'CCC'#180'CCC}AAA/UUU'#9#0#0#0#1#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#1'III'#7'CCC&DDDpECC'#180'X<,'#232'i=!'#254#128'O('#254

-  +#170'l3'#255#194#130':'#255#210#150'>'#255#220#163'?'#255#227#171'A'#255#234

-  +#180'B'#255#240#187'D'#255#242#190'D'#255#244#193'E'#255#246#194'D'#255#247

-  +#195'E'#255#248#197'E'#255#249#197'E'#255#249#198'E'#255#249#197'E'#255#248

-  +#196'E'#255#247#195'E'#255#246#195'E'#255#245#194'D'#255#243#191'D'#255#240

-  +#188'C'#255#236#183'C'#255#230#174'B'#255#223#165'@'#255#215#156'?'#255#200

-  +#137';'#255#182'u6'#255#146'[,'#255'm@!'#254'`:%'#244'KA;'#211'CCC'#189'CCC'

-  +#144'BBB>PPP'#16#128#128#128#2#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'999'#9'@@@4CCC'#133'HD@'#192'_9$'#244

-  +'rE%'#254#164'h3'#255#191';'#255#206#146'?'#255#219#163'C'#255#232#178'F'

-  +#255#238#185'G'#255#241#189'H'#255#244#193'H'#255#246#195'I'#255#247#196'I'

-  +#255#247#197'J'#255#247#197'I'#255#247#198'I'#255#248#198'I'#255#248#198'I'

-  +#255#248#197'I'#255#248#198'I'#255#248#198'I'#255#248#198'I'#255#247#197'I'

-  +#255#247#197'I'#255#247#196'I'#255#246#196'I'#255#245#194'H'#255#242#190'H'

-  +#255#239#187'H'#255#235#182'G'#255#224#168'D'#255#211#153'A'#255#196#134'='

-  +#255#177'q7'#255#133'Q)'#254'e: '#253'N?7'#218'DDC'#195'CCC'#160'FFFP@@@'#20

-  +#0#0#0#2#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +'III'#7'FFF3CCC'#145'LB;'#204'd:"'#250'|L('#254#180's9'#255#202#143'A'#255

-  +#216#159'F'#255#228#174'I'#255#236#184'K'#255#241#191'L'#255#243#193'M'#255

-  +#244#194'M'#255#244#195'M'#255#245#196'M'#255#245#195'M'#255#245#195'M'#255

-  +#245#195'M'#255#245#195'M'#255#245#195'M'#255#245#195'M'#255#245#195'M'#255

-  +#245#195'M'#255#245#195'M'#255#245#195'M'#255#245#195'M'#255#245#195'M'#255

-  +#245#195'M'#255#245#196'M'#255#245#195'M'#255#244#194'M'#255#243#194'M'#255

-  +#242#192'M'#255#238#186'L'#255#232#179'J'#255#220#165'F'#255#208#150'C'#255

-  +#190'<'#255#147'[.'#255'h< '#255'S=1'#228'EED'#199'DDD'#168'BBBUGGG'#18#0#0

-  +#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'@@@'#4'FFF(EEE'#134

-  +'P?5'#212'e:!'#254#137'U,'#254#186'z='#255#208#150'E'#255#224#171'L'#255#233

-  +#181'O'#255#238#187'P'#255#240#191'P'#255#242#192'Q'#255#242#193'Q'#255#242

-  +#193'Q'#255#242#193'P'#255#242#193'Q'#255#242#193'Q'#255#242#193'Q'#255#242

-  +#193'Q'#255#242#193'Q'#255#242#193'Q'#255#242#193'Q'#255#242#193'Q'#255#242

-  +#193'Q'#255#242#193'Q'#255#242#193'Q'#255#242#193'Q'#255#242#193'Q'#255#242

-  +#193'Q'#255#242#193'Q'#255#242#193'Q'#255#242#193'Q'#255#242#193'Q'#255#242

-  +#193'Q'#255#241#192'Q'#255#239#189'P'#255#234#184'O'#255#228#175'M'#255#215

-  +#159'I'#255#194#132'@'#255#160'd3'#255'k?"'#254'X:*'#236'FDC'#201'CCC'#164'A'

-  +'AAG;;;'#13#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'UUU'#3'BBB'#31'CCCvO='

-  +'3'#211'f:'#31#255#150']2'#255#190#128'A'#255#211#155'I'#255#226#175'Q'#255

-  +#234#185'S'#255#237#188'T'#255#238#190'T'#255#239#191'U'#255#239#191'U'#255

-  +#239#191'U'#255#239#191'U'#255#239#191'U'#255#239#191'U'#255#239#191'U'#255

-  +#239#191'U'#255#235#189'S'#255'|d,'#255'N>'#28#255'4*'#19#255'=1'#22#255'WE'

-  +#31#255'w_*'#255#173#139'='#255#239#191'U'#255#239#191'U'#255#239#191'U'#255

-  +#239#191'U'#255#239#191'U'#255#239#191'U'#255#239#191'U'#255#239#191'U'#255

-  +#239#191'U'#255#239#190'T'#255#238#189'U'#255#236#187'S'#255#230#179'R'#255

-  +#217#162'L'#255#198#138'D'#255#170'l8'#255'pC$'#254'Z8'''#240'DBB'#197'CCC'

-  ,#153'FFF:@@@'#8#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#2'FFF'#22'BBBeJ?9'#195'b8 '

-  +#254#150']2'#255#194#133'E'#255#213#158'N'#255#226#176'T'#255#233#185'W'#255

-  +#236#187'W'#255#236#188'X'#255#236#188'X'#255#236#188'X'#255#236#188'X'#255

-  +#236#188'X'#255#236#188'X'#255#236#188'X'#255#236#188'X'#255#236#188'X'#255

-  +'cO%'#255#4#3#1#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#1#1#0#255#18#14#7#255'A4'#24#255#134'k2'#255#218#174'R'#255

-  +#236#188'X'#255#236#188'X'#255#236#188'X'#255#236#188'X'#255#236#188'X'#255

-  +#236#188'X'#255#236#187'W'#255#234#186'W'#255#229#180'U'#255#218#165'P'#255

-  +#201#143'H'#255#173'o<'#255'k>#'#254'S;-'#231'CCC'#193'CCC'#141'DDD-fff'#5#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0'@@@'#12'DDDKFA='#179'`7 '#251#137'S-'#255#192#131'E'#255

-  +#214#161'Q'#255#226#176'W'#255#231#183'Y'#255#233#185'['#255#233#186'['#255

-  +#234#185'Z'#255#234#185'Z'#255#234#185'Z'#255#234#185'Z'#255#234#185'Z'#255

-  +#234#185'Z'#255#234#185'Z'#255#234#185'Z'#255'L<'#29#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#8#7#3#255'P?'#31#255#203#161'N'

-  +#255#234#185'Z'#255#234#185'Z'#255#234#185'Z'#255#234#185'Z'#255#233#186'['

-  +#255#232#183'Z'#255#228#178'Y'#255#219#167'T'#255#201#143'K'#255#163'g8'#255

-  +'f: '#255'O=4'#222'DDD'#188'CCCzBBB'#27#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'UUU'#3'GGG$D@@'#147'\6#'

-  +#245'{I*'#254#185'{C'#255#211#157'R'#255#225#175'Z'#255#229#181'\'#255#230

-  +#183']'#255#230#183']'#255#230#183']'#255#230#183']'#255#230#183']'#255#230

-  +#183']'#255#230#183']'#255#230#183']'#255#230#183']'#255#230#183']'#255#138

-  +'m8'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#24#20#10#255'x_0'#255#226#181'['#255

-  +#230#183']'#255#230#183']'#255#230#183']'#255#230#181'\'#255#227#177'['#255

-  +#217#165'V'#255#196#136'J'#255#151']4'#255'b8 '#254'K@9'#214'CCC'#175'AAAJ..'

-  +'.'#11#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0'@@@'#16'CCC\V6'''#228'nA&'#254#178'tA'#255#205#151'R'#255#221#172

-  +'\'#255#226#179'_'#255#228#180'_'#255#228#180'_'#255#228#180'_'#255#228#180

-  +'_'#255#228#180'_'#255#228#180'_'#255#228#180'_'#255#228#180'_'#255#228#180

-  +'_'#255#228#180'_'#255#228#180'_'#255#17#13#7#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#2#1#1#255'H9'#30#255#224#178']'#255#228#180'_'#255

-  +#228#180'_'#255#227#179'^'#255#224#175']'#255#213#160'W'#255#190#129'H'#255

-  +#137'T/'#255'^4 '#252'FB?'#201'CCC'#140'DDD"'#0#0#0#3#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'@@@'#4'FFF,J=7'#175'`4'#30#255#165

-  +'g;'#255#200#144'Q'#255#217#166'\'#255#223#176'`'#255#225#177'`'#255#225#177

-  +'a'#255#225#177'a'#255#225#177'a'#255#225#177'a'#255#225#177'a'#255#225#177

-  +'a'#255#225#177'a'#255#225#177'a'#255#225#177'a'#255#225#177'a'#255#218#171

-  +'^'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#11#9#5#255#196#154'U'#255#225#177'a'#255#225#177'`'#255#224#177'a'

-  +#255#220#171'^'#255#208#154'W'#255#184'{F'#255'nA%'#254'T8*'#233'DDD'#180'CC'

-  +'CX333'#15#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'C'

-  +'CC'#19'CCCjY5#'#239'~J,'#254#191#132'M'#255#213#162']'#255#220#171'a'#255

-  +#221#173'c'#255#222#174'b'#255#222#174'b'#255#222#174'b'#255#222#174'b'#255

-  +#222#174'b'#255#222#174'b'#255#222#174'b'#255#222#174'b'#255#222#174'b'#255

-  +#222#174'b'#255#222#174'b'#255#154'yC'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#15#11#6#255#218#172'`'

-  +#255#222#174'b'#255#222#174'b'#255#221#173'b'#255#217#167'_'#255#202#146'T'

-  +#255#156'a9'#255'^4'#30#254'G@='#206'DDD'#151'>>>)@@@'#4#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0'UUU'#3'AAA+L;4'#188'b6 '#254#170'l@'#255#206#152

-  +'Z'#255#217#168'b'#255#219#171'd'#255#219#171'd'#255#219#171'd'#255#219#171

-  +'d'#255#219#171'd'#255#219#171'd'#255#219#171'd'#255#219#171'd'#255#219#171

-  +'d'#255#219#171'd'#255#219#171'd'#255#219#171'd'#255#219#171'd'#255#219#171

-  +'d'#255#26#20#12#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  ,#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255'oW3'#255#219#171'd'#255#219#171'd'#255#219

-  +#171'd'#255#218#169'c'#255#212#161'_'#255#187'L'#255'uD)'#254'W6('#238'CCC'

-  +#182'CCC[III'#14#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'@@@'#12'EBB'

-  +'S[3'#31#244#134'O/'#255#192#134'Q'#255#212#162'b'#255#215#167'd'#255#216#168

-  +'d'#255#216#168'd'#255#216#168'd'#255#216#168'd'#255#216#168'd'#255#216#168

-  +'d'#255#216#168'd'#255#216#168'd'#255#216#168'd'#255#216#168'd'#255#216#168

-  +'d'#255#216#168'd'#255#216#168'd'#255#216#168'd'#255#198#154'\'#255#5#4#2#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255'!'#25#15#255#216#168'd'#255#216#168'd'#255#216#168'd'#255#216#168'd'#255

-  +#214#166'd'#255#202#147'Y'#255#161'e='#255'^2'#29#255'J?:'#208'CCC'#137'EEE'

-  +#26#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'CCC'#23'O9/'#171'`3'#31

-  +#254#174'pD'#255#203#150']'#255#212#163'e'#255#213#164'e'#255#213#164'e'#255

-  +#213#164'e'#255#213#164'e'#255#213#164'e'#255#213#164'e'#255#213#164'e'#255

-  +#213#164'e'#255#213#164'e'#255#213#164'e'#255#213#164'e'#255#213#164'e'#255

-  +#213#164'e'#255#213#164'e'#255#213#164'e'#255#213#164'e'#255#173#133'R'#255#3

-  +#2#1#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#22#17#11#255#213#164'e'#255#213#164'e'#255#213#164'e'#255#213#164'e'#255

-  +#213#164'f'#255#209#158'a'#255#187#128'O'#255'yD)'#254'W4$'#241'BBB'#169'DDD'

-  +'1@@@'#4#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#2'FFF(W4$'#229'{E)'#255#191

-  +#134'T'#255#207#157'c'#255#210#160'f'#255#210#160'f'#255#210#160'f'#255#210

-  +#160'f'#255#210#160'f'#255#210#160'f'#255#210#160'f'#255#210#160'f'#255#152

-  +'tJ'#255'[E,'#255'K9$'#255'{]<'#255#196#149'_'#255#210#160'f'#255#210#160'f'

-  +#255#210#160'f'#255#210#160'f'#255#210#160'f'#255#159'yM'#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'"'#26#17#255#210#160

-  +'f'#255#210#160'f'#255#210#160'f'#255#210#160'f'#255#210#160'f'#255#209#160

-  +'e'#255#199#146']'#255#156'_;'#255'\1'#28#254'FBA'#188'FFFXNNN'#13#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0'FFF'#11'F@=T\/'#28#253#151'\9'#255#200#147'_'#255#207

-  +#156'e'#255#207#156'e'#255#207#156'e'#255#207#156'e'#255#207#156'e'#255#207

-  +#156'e'#255#207#156'e'#255#178#135'W'#255'$'#27#17#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255'N:&'#255#207#156'e'#255#207#156'e'#255#207#156

-  +'e'#255#207#156'e'#255#207#156'e'#255#3#2#1#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255'uX9'#255#207#156'e'#255#207#156'e'#255#207#156

-  +'e'#255#207#156'e'#255#207#156'e'#255#207#157'e'#255#204#153'b'#255#180'wK'

-  +#255'a4'#30#254'M;3'#216'DDD'#132'==='#25#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'CC'

-  +'C'#19'N9.'#159'^2'#30#254#176'rI'#255#202#150'c'#255#203#153'e'#255#203#153

-  +'f'#255#203#153'f'#255#203#153'f'#255#203#153'f'#255#203#153'f'#255#163'zQ'

-  +#255#1#1#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#29#22#15#255#203#153'f'#255#203#153'f'#255#203#153'f'#255#203#153'f'

-  +#255#14#10#7#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#4#3#2#255

-  +#203#153'f'#255#203#153'f'#255#203#153'f'#255#203#153'f'#255#203#153'f'#255

-  +#203#153'f'#255#203#153'f'#255#203#152'e'#255#190#134'X'#255'yC)'#254'V5&'

-  +#239'DDD'#165'FFF('#128#128#128#2#0#0#0#0#0#0#0#0#0#0#0#0'GGG'#25'V4%'#212's'

-  +'@&'#254#186#128'U'#255#199#148'd'#255#200#148'd'#255#200#148'd'#255#200#148

-  +'d'#255#200#148'd'#255#200#148'd'#255#198#146'd'#255#12#9#6#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#138'fE'#255#200#148'd'#255#200#148'd'#255#200#148'd'#255#15#11#7#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#1#0#0#255'&'#28#19#255#145'kH'#255#200#148'd'#255

-  +#200#148'd'#255#200#148'd'#255#200#148'd'#255#200#148'd'#255#200#148'd'#255

-  +#200#148'd'#255#200#148'd'#255#194#141'^'#255#146'W6'#255'[0'#28#254'EBB'#178

-  +'CCC9UUU'#6#0#0#0#0#0#0#0#0#0#0#0#0'JAA'#31'\1'#29#249#140'P2'#255#190#135'\'

-  +#255#196#143'c'#255#196#143'c'#255#196#143'c'#255#196#143'c'#255#196#143'c'

-  ,#255#196#143'c'#255'>-'#31#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'&'#28#19#255#196#143

-  +'c'#255#196#143'c'#255#196#143'c'#255#25#18#12#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#2#2#1#255'*'#31#21#255'kM6'

-  +#255#187#137'_'#255#196#143'c'#255#196#143'c'#255#196#143'c'#255#196#143'c'

-  +#255#196#143'c'#255#196#143'c'#255#196#143'c'#255#196#143'c'#255#196#143'c'

-  +#255#196#143'c'#255#194#141'b'#255#170'kF'#255'\0'#28#254'L<5'#203'CCCH333'

-  +#10#0#0#0#0#0#0#0#0#0#0#0#1'K3+@[/'#27#255#160'`>'#255#190#136'_'#255#191#137

-  +'`'#255#191#137'`'#255#191#137'`'#255#191#137'`'#255#191#137'`'#255#189#135

-  +'`'#255#1#1#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#30#21#15#255#191#137'`'#255#191#137

-  +'`'#255#191#137'`'#255'aF1'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#5#3#2#255#165'vS'#255#191#137'`'#255#191#137'`'#255#191

-  +#137'`'#255#191#137'`'#255#191#137'`'#255#191#137'`'#255#191#137'`'#255#191

-  +#137'`'#255#191#137'`'#255#191#137'`'#255#191#137'`'#255#191#137'`'#255#191

-  +#137'`'#255#191#136'`'#255#178'vQ'#255'h7 '#253'Q8,'#220'DDDV;;;'#13#0#0#0#0

-  +#0#0#0#0'UUU'#3'R4''^[/'#27#255#169'hF'#255#187#131']'#255#187#132'^'#255#187

-  +#132'^'#255#187#132'^'#255#187#132'^'#255#187#132'^'#255'uS;'#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255'{W>'#255#187#132'^'#255#187#132'^'#255#187#132'^'

-  +#255#185#130'^'#255#19#14#10#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#1#1#1#255#158'oO'#255#187#132'^'#255#187#132'^'#255#187#132'^'#255#187#132

-  +'^'#255#187#132'^'#255#187#132'^'#255#187#132'^'#255#187#132'^'#255#187#132

-  +'^'#255#187#132'^'#255#187#132'^'#255#187#132'^'#255#187#132'^'#255#187#132

-  +'^'#255#187#132'^'#255#181'{V'#255'r<"'#255'T6'''#228'BBBd@@@'#16#0#0#0#0#0#0

-  +#0#0'UUU'#6'T5''}_2'#30#253#171'kI'#255#183'}Z'#255#183'~Z'#255#183'~Z'#255

-  +#183'~Z'#255#183'~Z'#255#183'~Z'#255'. '#23#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#0#255'S9)'

-  +#255#183'~Z'#255#183'~Z'#255#183'~Z'#255#183'~Z'#255#183'~Z'#255#173'xV'#255

-  +#31#21#15#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'bC0'#255#183'~Z'#255#183'~Z'

-  +#255#183'~Z'#255#183'~Z'#255#183'~Z'#255#183'~Z'#255#183'~Z'#255#183'~Z'#255

-  +#183'~Z'#255#183'~Z'#255#183'~Z'#255#183'~Z'#255#183'~Z'#255#183'~Z'#255#183

-  +'~Z'#255#183'~Z'#255#181'zV'#255'zA%'#255'X4$'#237'DDDqCCC'#19#0#0#0#0#0#0#0

-  +#0'@@@'#8'U4%'#149'f5'#31#252#172'mK'#255#180'yX'#255#180'yX'#255#180'yX'#255

-  +#180'yX'#255#180'yX'#255#180'yX'#255#6#4#3#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#19#13#9#255#153'gK'#255

-  +#180'yX'#255#180'yX'#255#180'yX'#255#180'yX'#255#180'yX'#255#180'yX'#255#180

-  +'yX'#255#180'yX'#255'{S<'#255'+'#29#21#255#7#4#3#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#1#255'gF3'#255#180'yX'#255#180'yX'

-  +#255#180'yX'#255#139']D'#255'+'#29#21#255#8#6#4#255#8#6#4#255#16#11#8#255'W:'

-  +'*'#255#178'wV'#255#180'yX'#255#180'yX'#255#180'yX'#255#180'yX'#255#180'yX'

-  +#255#180'yX'#255#180'yX'#255#179'yV'#255#131'F+'#255'[3!'#245'CCCyIII'#21#0#0

-  +#0#0#0#0#0#0'UUU'#6'X3"'#165'n:"'#253#172'pO'#255#177'wW'#255#177'wW'#255#177

-  +'wW'#255#177'wW'#255#177'wW'#255#173'uU'#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#0#0#255'P6'''#255#177'wW'#255#177'w'

-  +'W'#255#177'wW'#255#177'wW'#255#177'wW'#255#177'wW'#255#177'wW'#255#177'wW'

-  +#255#177'wW'#255#177'wW'#255#177'wW'#255#177'wW'#255#177'wW'#255#158'jM'#255

-  +'W;+'#255''''#26#19#255'"'#23#17#255'%'#25#18#255'W:+'#255#167'qS'#255#177'w'

-  +'W'#255#177'wW'#255#177'wW'#255'@+'#31#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#3#2#1#255#149'dI'#255#177'wW'#255#177'wW'#255#177

-  +'wW'#255#177'wW'#255#177'wW'#255#177'wW'#255#177'vV'#255#139'M0'#255'[1'#30

-  +#250'DDDpCCC'#19#0#0#0#0#0#0#0#0'UUU'#3'Y1'#31#182'v>%'#255#172'oQ'#255#175

-  +'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255'}S?'#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#26#17#13#255#150'cK'#255#175

-  +'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'

-  +#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255

-  +#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#175

-  +'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255'dC2'#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#9#6#4#255#169'pU'

-  +#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#174'sU'#255

-  ,#145'S6'#255']0'#28#254'DBBd@@@'#16#0#0#0#0#0#0#0#0#0#0#0#0'Z0'#30#175'v=%'

-  +#255#171'nP'#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255

-  +'L2&'#255#0#0#0#255'/'#31#23#255'}R>'#255' '#21#16#255'!'#22#17#255#139'[E'

-  +#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255

-  +#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255#174

-  +'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'

-  +#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255

-  +'?*'#31#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255'6#'#27#255#174'rV'#255#174'rV'#255#174'rV'#255#174

-  +'rV'#255#174'rV'#255#173'qT'#255#145'S7'#255'\1'#29#254'AAAV;;;'#13#0#0#0#0#0

-  +#0#0#0#0#0#0#0'Z1'#30#150'n;$'#252#169'jM'#255#172'qU'#255#172'qU'#255#172'q'

-  +'U'#255#172'qU'#255#172'qU'#255#149'bI'#255'@* '#255#170'qU'#255#172'qU'#255

-  +#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172

-  +'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'

-  +#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255

-  +#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172

-  +'qU'#255#172'qU'#255#172'qU'#255'G/#'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#24#16#12#255#172'qU'

-  +#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#171'oS'#255#138'N3'#255

-  +'\2'#31#249'DDDG999'#9#0#0#0#0#0#0#0#0#0#0#0#0'X1!|g7#'#250#167'gI'#255#172

-  +'pT'#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'

-  +#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255

-  +#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255'P5('#255')'#27#20#255#27#18

-  +#13#255'5#'#26#255'pJ8'#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255

-  +#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172

-  +'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#162'jO'#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#9#6#4#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'#255#172'qU'

-  +#255#170'mR'#255#131'I.'#255'[4"'#241'FFF7UUU'#6#0#0#0#0#0#0#0#0#0#0#0#0'Y1!'

-  +'`b5 '#251#164'cG'#255#172'pU'#255#173'rW'#255#173'rW'#255#173'rW'#255#173'r'

-  +'W'#255#173'rW'#255#173'rW'#255#173'rW'#255#173'rW'#255#173'rW'#255#173'rW'

-  +#255#173'rW'#255#173'rW'#255#173'rW'#255#173'rW'#255'}S?'#255#9#6#5#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#16#11#8#255#152'dM'#255#173'rW'

-  +#255#173'rW'#255#173'rW'#255#173'rW'#255#173'rW'#255#173'rW'#255#173'rW'#255

-  +#173'rW'#255#173'rW'#255#173'rW'#255#173'rW'#255#173'rW'#255#173'rW'#255#173

-  +'rW'#255'$'#24#18#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#4#3#2#255#173'rW'#255#173'rW'#255#173'rW'#255#173'r'

-  +'W'#255#173'rW'#255#170'mQ'#255'|C*'#255'W4$'#229'CCC&'#0#0#0#2#0#0#0#0#0#0#0

-  +#0#0#0#0#0'X5!A^2'#31#254#162'bE'#255#173'rV'#255#175'tY'#255#175'tY'#255#175

-  +'tY'#255#175'tY'#255#175'tY'#255#175'tY'#255#175'tY'#255#175'tY'#255#175'tY'

-  +#255#175'tY'#255#175'tY'#255#175'tY'#255#175'tY'#255'{Q?'#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#14#9#7#255

-  +#173'tY'#255#175'tY'#255#175'tY'#255#175'tY'#255#175'tY'#255#175'tY'#255#175

-  +'tY'#255#175'tY'#255#175'tY'#255#175'tY'#255#175'tY'#255#175'tY'#255#175'tY'

-  +#255#175'tY'#255#132'XC'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#21#14#11#255#175'tY'#255#175'tY'#255#175

-  +'tY'#255#175'tY'#255#174'tY'#255#170'mP'#255't?('#254'U5&'#203'@@@'#24#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0'X0'#24' ^2'#31#255#156'\A'#255#175'v\'#255#177'y`'

-  +#255#177'y`'#255#177'y`'#255#177'y`'#255#177'y`'#255#177'y`'#255#177'y`'#255

-  +#177'y`'#255#177'y`'#255#177'y`'#255#177'y`'#255#177'y`'#255#177'y`'#255#11#8

-  +#6#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#136']J'#255#177'y`'#255#177'y`'#255'7%'#30#255#9#6#5#255

-  +#13#9#7#255#16#11#9#255#26#18#14#255#139'_K'#255#177'y`'#255#177'y`'#255#177

-  +'y`'#255#177'y`'#255#177'y`'#255#177'y`'#255#18#12#9#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'<)!'#255#177'y`'#255#177

-  +'y`'#255#177'y`'#255#177'y`'#255#176'x^'#255#169'kP'#255'j<&'#253'U4%'#168';'

-  +';;'#13#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'^2'#29#234#136'O4'#255#176'x'

-  +'^'#255#180'~f'#255#180'~f'#255#180'~f'#255#180'~f'#255#180'~f'#255#180'~f'

-  +#255#180'~f'#255#180'~f'#255#180'~f'#255#180'~f'#255#180'~f'#255#180'~f'#255

-  +#146'gS'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#139'bO'#255#170'x`'#255#20#14#11#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255']A4'#255#180'~f'#255

-  +#180'~f'#255#180'~f'#255#180'~f'#255#180'~f'#255'qO@'#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#0#255#165't^'#255#180'~f'#255

-  ,#180'~f'#255#180'~f'#255#180'~f'#255#178'zb'#255#162'dH'#255'_3'#31#254'R6)c'

-  +'@@@'#4#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#29#153'r?)'#252#174'sZ'

-  +#255#182#129'j'#255#183#131'l'#255#183#131'l'#255#183#131'l'#255#183#131'l'

-  +#255#183#131'l'#255#183#131'l'#255#183#131'l'#255#183#131'l'#255#183#131'l'

-  +#255#183#131'l'#255#183#131'l'#255'ZA5'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#5#3#3#255#179'j'

-  +#255'uTE'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255'{XI'#255#183#131'l'#255#183#131'l'#255#183#131'l'#255#183

-  +#131'l'#255#183#131'l'#255#20#15#12#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255':)"'#255#183#131'l'#255#183#131'l'#255#183#131'l'#255#183#131

-  +'l'#255#183#131'l'#255#179'|c'#255#140'R9'#255'\2'#31#248'@@6'#28#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#29'E`5 '#252#166'iO'#255#183#131'l'

-  +#255#186#136'q'#255#186#136'q'#255#186#136'q'#255#186#136'q'#255#186#136'q'

-  +#255#186#136'q'#255#186#136'q'#255#186#136'q'#255#186#136'q'#255#186#136'q'

-  +#255#186#136'q'#255'\D8'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'oQC'#255#186#136'q'#255'X@6'#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#12#9#7#255#186#136'q'#255#186#136'q'#255#186#136'q'#255#186#136'q'#255

-  +#186#136'q'#255#148'lZ'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#169'|g'#255#186#136'q'#255#186#136'q'#255#186#136'q'#255#186#136'q'#255#185

-  +#134'q'#255#177'y`'#255'tB+'#254'W2!'#194'III'#14#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0'@@'#0#4'^3'#30#245#144'W='#255#184#134'o'#255#189#141'x'

-  +#255#189#141'x'#255#189#141'x'#255#189#141'x'#255#189#141'x'#255#189#141'x'

-  +#255#189#141'x'#255#189#141'x'#255#189#141'x'#255#189#141'x'#255#189#141'x'

-  +#255'tWJ'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255'('#30#26#255#189#141'x'#255#189#141'x'#255'?/('#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#187#139'v'#255#189#141'x'#255#189#141'x'#255#189#141'x'#255#189#141

-  +'x'#255#189#141'x'#255#172#128'm'#255'_G<'#255#1#0#0#255#0#0#0#255'-"'#28#255

-  +#189#141'x'#255#189#141'x'#255#189#141'x'#255#189#141'x'#255#189#141'x'#255

-  +#186#137's'#255#171'oU'#255'a6"'#253'T5$jUUU'#3#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0'\/'#27#170'vD,'#252#181#128'h'#255#190#144'{'#255

-  +#192#146'~'#255#192#146'~'#255#192#146'~'#255#192#146'~'#255#192#146'~'#255

-  +#192#146'~'#255#192#146'~'#255#192#146'~'#255#192#146'~'#255#192#146'~'#255

-  +#186#142'z'#255#1#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#2#1#1#255#186#142'z'#255#192#146'~'#255#192#146'~'#255'=.('#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#192#146'~'#255#192#146'~'#255#192#146'~'#255#192#146'~'#255#192

-  +#146'~'#255#192#146'~'#255#192#146'~'#255#192#146'~'#255#0#0#0#255#0#0#0#255

-  +#167'n'#255#192#146'~'#255#192#146'~'#255#192#146'~'#255#192#146'~'#255#191

-  +#145'}'#255#186#137's'#255#149'[A'#255'^1'#31#250'F:.'#22#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'\.'#28'H`5 '#252#164'jP'#255#190

-  +#143'z'#255#194#151#131#255#194#151#131#255#194#151#131#255#194#151#131#255

-  +#194#151#131#255#194#151#131#255#194#151#131#255#194#151#131#255#194#151#131

-  +#255#194#151#131#255#194#151#131#255#27#21#18#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255' '#25#21#255#194#151#131#255#194#151#131

-  +#255#194#151#131#255'7*%'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#12#9#8#255#194#151#131#255#194#151#131#255

-  +#194#151#131#255#194#151#131#255#194#151#131#255#194#151#131#255#194#151#131

-  +#255#190#149#129#255#0#0#0#255'0% '#255#194#151#131#255#194#151#131#255#194

-  +#151#131#255#194#151#131#255#194#151#131#255#192#147''#255#180'~f'#255'q@+'

-  +#252'Z0'#29#180#0#0#0#2#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0']0'#28#199'{H2'#252#184#134'o'#255#196#153#134#255#197#156

-  +#137#255#197#156#137#255#197#156#137#255#197#156#137#255#197#156#137#255#197

-  +#156#137#255#197#156#137#255#197#156#137#255#197#156#137#255#197#156#137#255

-  +'G81'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'WE='

-  +#255#197#156#137#255#197#156#137#255#191#152#133#255#1#1#1#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#135'k^'

-  +#255#197#156#137#255#197#156#137#255#197#156#137#255#197#156#137#255#197#156

-  +#137#255#197#156#137#255#197#156#137#255#197#156#137#255#16#12#11#255#174#138

-  +'y'#255#197#156#137#255#197#156#137#255#197#156#137#255#197#156#137#255#197

-  +#155#136#255#191#145'|'#255#151'_F'#255'^3'#31#253'T2!-'#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[0'#29'5_4 '#254

-  +#161'hP'#255#195#152#132#255#200#161#143#255#200#161#144#255#200#161#144#255

-  ,#200#161#144#255#200#161#144#255#200#161#144#255#200#161#144#255#200#161#144

-  +#255#200#161#144#255#200#161#144#255'xaW'#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#1#1#1#255#171#138'{'#255#200#161#144#255#200#161#144#255

-  +'f\'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255')!'#30#255#200#161#144#255#200#161#144#255#200#161#144#255#200

-  +#161#144#255#200#161#144#255#200#161#144#255#200#161#144#255#200#161#144#255

-  +#200#161#144#255#153'{n'#255#200#161#144#255#200#161#144#255#200#161#144#255

-  +#200#161#144#255#200#161#144#255#198#156#138#255#181#128'h'#255'l>*'#252'Z1'

-  +#30#159#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0'^0'#27#173'uE/'#249#186#136'r'#255#201#162#145#255

-  +#203#165#150#255#203#165#150#255#203#165#150#255#203#165#150#255#203#165#150

-  +#255#203#165#150#255#203#165#150#255#203#165#150#255#203#165#150#255#163#132

-  +'x'#255#0#0#0#255#0#0#0#255#26#21#19#255',$!'#255'-%!'#255#156'~s'#255#203

-  +#165#150#255#203#165#150#255#203#165#150#255'@4/'#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#4#3#3#255#186#151#138#255#203

-  +#165#150#255#203#165#150#255#203#165#150#255#203#165#150#255#203#165#150#255

-  +#203#165#150#255#203#165#150#255#203#165#150#255#203#165#150#255#203#165#150

-  +#255#203#165#150#255#203#165#150#255#203#165#150#255#203#165#150#255#203#164

-  +#149#255#195#152#132#255#146'\D'#255'_3'#31#247'R3'#30#25#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +'Z-'#30'"^4 '#252#155'cK'#255#196#153#136#255#206#169#155#255#207#171#156#255

-  +#207#171#156#255#207#171#156#255#207#171#156#255#207#171#156#255#207#171#156

-  +#255#207#171#156#255#207#171#156#255#203#167#152#255#0#0#0#255#0#0#0#255#193

-  +#159#145#255#207#171#156#255#207#171#156#255#207#171#156#255#207#171#156#255

-  +#207#171#156#255#207#171#156#255'E94'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#15#12#11#255#182#150#137#255#207#171#156#255#207#171

-  +#156#255#207#171#156#255#207#171#156#255#207#171#156#255#207#171#156#255#207

-  +#171#156#255#207#171#156#255#207#171#156#255#207#171#156#255#207#171#156#255

-  +#207#171#156#255#207#171#156#255#207#171#156#255#206#170#155#255#201#162#145

-  +#255#178'}f'#255'h<('#250'[0'#28#129#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0']1'

-  +#29#130'f:&'#250#171'u^'#255#202#164#148#255#210#176#163#255#210#177#164#255

-  +#210#177#164#255#210#177#164#255#210#177#164#255#210#177#164#255#210#177#164

-  +#255#210#177#164#255#210#177#164#255#2#1#1#255#27#22#21#255#210#177#164#255

-  +#210#177#164#255#210#177#164#255#210#177#164#255#210#177#164#255#210#177#164

-  +#255#210#177#164#255#210#177#164#255'I=8'#255#6#5#4#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255'''!'#31#255#206#175#162#255#210#177#164#255#210#177#164#255#210#177

-  +#164#255#210#177#164#255#210#177#164#255#210#177#164#255#210#177#164#255#210

-  +#177#164#255#210#177#164#255#210#177#164#255#210#177#164#255#210#177#164#255

-  +#210#177#164#255#210#177#164#255#210#176#163#255#206#170#155#255#188#139'w'

-  +#255'zI4'#252'^2'#29#222']/'#23#11#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#1'^1'#29#181'oA-'#249#182#132'n'#255#208#173#159#255#213#182#169#255#213#183

-  +#170#255#213#183#170#255#213#183#170#255#213#183#170#255#213#183#170#255#213

-  +#183#170#255#213#183#170#255#12#10#9#255#159#136''#255#213#183#170#255#213

-  +#183#170#255#213#183#170#255#213#183#170#255#213#183#170#255#213#183#170#255

-  +#213#183#170#255#213#183#170#255#213#183#170#255#139'xo'#255#0#0#0#255#0#0#0

-  +#255'% '#30#255#213#183#170#255#213#183#170#255#213#183#170#255#213#183#170

-  +#255#213#183#170#255#213#183#170#255#213#183#170#255#213#183#170#255#213#183

-  +#170#255#213#183#170#255#213#183#170#255#213#183#170#255#213#183#170#255#213

-  +#183#170#255#213#183#170#255#213#183#170#255#211#178#165#255#196#152#134#255

-  +#136'T?'#254'`1 '#243'U+'#28'$'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0'U+'#21#12']2'#30#218'zK5'#251#192#146''#255#212#180#168#255#216#187

-  +#176#255#217#188#177#255#217#188#177#255#217#188#177#255#217#188#177#255#217

-  +#188#177#255#217#188#177#255'o`Z'#255#217#188#177#255#217#188#177#255#217#188

-  +#177#255#217#188#177#255#217#188#177#255#217#188#177#255#217#188#177#255#217

-  +#188#177#255#217#188#177#255#217#188#177#255#27#24#22#255#0#0#0#255#9#8#7#255

-  +#209#180#171#255#217#188#177#255#217#188#177#255#217#188#177#255#217#188#177

-  +#255#217#188#177#255#217#188#177#255#217#188#177#255#217#188#177#255#217#188

-  +#177#255#217#188#177#255#217#188#177#255#217#188#177#255#217#188#177#255#217

-  +#188#177#255#217#188#177#255#215#185#173#255#202#163#147#255#152'cM'#255'`5 '

-  +#252'X.'#27'B'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  ,#0#0#0'].'#23'!_3 '#241#135'V@'#254#197#155#137#255#215#185#173#255#219#193

-  +#183#255#220#194#184#255#220#194#184#255#220#194#184#255#220#194#184#255#220

-  +#194#184#255#220#194#184#255#220#194#184#255#220#194#184#255#220#194#184#255

-  +#220#194#184#255#220#194#184#255#220#194#184#255#220#194#184#255#220#194#184

-  +#255#220#194#184#255#220#194#184#255#4#4#4#255#13#11#11#255#197#173#164#255

-  +#220#194#184#255#220#194#184#255#220#194#184#255#220#194#184#255#220#194#184

-  +#255#220#194#184#255#220#194#184#255#220#194#184#255#220#194#184#255#220#194

-  +#184#255#220#194#184#255#220#194#184#255#220#194#184#255#220#194#184#255#220

-  +#194#183#255#218#190#179#255#205#169#154#255#165'pY'#255'b8$'#252'[/'#27'h'#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0'Y1'#28'?_3!'#249#132'T?'#252#193#148#130#255#215#186#174#255#223#198

-  +#189#255#223#200#191#255#223#200#191#255#223#200#191#255#223#200#191#255#223

-  +#200#191#255#223#200#191#255#223#200#191#255#223#200#191#255#223#200#191#255

-  +#223#200#191#255#223#200#191#255#223#200#191#255#223#200#191#255#223#200#191

-  +#255#223#200#191#255#199#179#171#255#215#192#183#255#223#200#191#255#223#200

-  +#191#255#223#200#191#255#223#200#191#255#223#200#191#255#223#200#191#255#223

-  +#200#191#255#223#200#191#255#223#200#191#255#223#200#191#255#223#200#191#255

-  +#223#200#191#255#223#200#191#255#223#200#191#255#223#199#190#255#219#192#183

-  +#255#203#165#150#255#158'kS'#255'c9%'#251']0'#30#147#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +'Z-'#25'3_3'#30#234'wI5'#250#185#138'v'#255#215#186#174#255#225#203#194#255

-  +#227#205#197#255#227#205#197#255#227#205#197#255#227#205#197#255#227#205#197

-  +#255#227#205#197#255#227#205#197#255#227#205#197#255#227#205#197#255#227#205

-  +#197#255#227#205#197#255#227#205#197#255#227#205#197#255#227#205#197#255#227

-  +#205#197#255#227#205#197#255#227#205#197#255#227#205#197#255#227#205#197#255

-  +#227#205#197#255#227#205#197#255#227#205#197#255#227#205#197#255#227#205#197

-  +#255#227#205#197#255#227#205#197#255#227#205#197#255#227#205#197#255#227#205

-  +#197#255#226#204#196#255#220#194#183#255#200#159#143#255#144'_H'#254'a6"'#253

-  +'\1'#30'y'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'\3'#31#25'^2'#29#207'nB.'

-  +#249#175'~i'#255#213#182#169#255#225#202#193#255#230#209#203#255#230#211#205

-  +#255#230#211#205#255#230#211#205#255#230#211#205#255#230#211#205#255#230#211

-  +#205#255#230#211#205#255#230#211#205#255#230#211#205#255#230#211#205#255#230

-  +#211#205#255#230#211#205#255#230#211#205#255#230#211#205#255#230#211#205#255

-  +#230#211#205#255#230#211#205#255#230#211#205#255#230#211#205#255#230#211#205

-  +#255#230#211#205#255#230#211#205#255#230#211#205#255#230#211#205#255#230#211

-  +#205#255#230#210#204#255#227#205#198#255#219#191#181#255#193#149#131#255#131

-  +'R='#251'_4 '#247'Z0'#26'O'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0'`  '#8']0'#28#169'e:'''#250#148'aL'#255#196#153#136#255#220#193#183

-  +#255#229#209#202#255#233#216#210#255#234#216#211#255#234#216#211#255#234#216

-  +#211#255#234#216#211#255#234#216#211#255#234#216#211#255#234#216#211#255#234

-  +#216#211#255#234#216#211#255#234#216#211#255#234#216#211#255#234#216#211#255

-  +#234#216#211#255#234#216#211#255#234#216#211#255#234#216#211#255#234#216#211

-  +#255#234#216#211#255#234#216#211#255#234#216#211#255#234#216#211#255#233#216

-  +#211#255#231#212#206#255#224#200#192#255#207#171#156#255#168'va'#255'rE1'#249

-  +'^3'#31#230'Y,'#28'.'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0'^1'#29'h`3'#31#237'nA/'#249#158'mW'#255#203#165#150

-  +#255#224#201#192#255#232#213#208#255#235#219#215#255#237#222#218#255#237#222

-  +#218#255#237#222#218#255#237#222#218#255#237#222#218#255#237#222#218#255#237

-  +#222#218#255#237#222#218#255#237#222#218#255#237#222#218#255#237#222#218#255

-  +#237#222#218#255#237#222#218#255#237#222#218#255#237#222#218#255#237#222#218

-  +#255#237#222#218#255#236#220#216#255#233#216#211#255#228#206#200#255#213#182

-  +#170#255#177#131'n'#255'}M:'#250'a6"'#253']1'#29#155'U1'#24#21#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0'].'#23#11']1'#29'~`5!'#246'tF5'#249#166'u`'#255#202#164#148#255#221

-  ,#195#186#255#230#210#203#255#234#218#213#255#238#224#220#255#240#227#224#255

-  +#240#227#225#255#240#227#225#255#240#227#225#255#240#227#225#255#240#227#225

-  +#255#240#227#225#255#240#227#225#255#240#227#225#255#240#227#225#255#240#227

-  +#225#255#239#225#222#255#236#220#215#255#232#213#207#255#225#203#194#255#210

-  +#176#164#255#183#138'u'#255#133'UA'#252'c9&'#254'^2'#30#178'\.'#26''''#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'U1'#24#21']0'#30#147'`7"'#251

-  +'jA-'#249#133'UA'#253#169'zf'#255#201#162#146#255#220#194#184#255#230#211#205

-  +#255#233#215#210#255#234#217#212#255#236#220#215#255#237#222#218#255#238#223

-  +#220#255#237#222#219#255#236#220#216#255#234#219#213#255#233#216#211#255#231

-  +#213#206#255#225#203#194#255#209#174#160#255#181#136'u'#255#145'`L'#255'sG4'

-  +#249'c9&'#255'_2'#30#198'].'#28'7'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'U+'#28#18'\.'#27'^_1'#30#179'c7$'

-  +#248'h?,'#251'Q;'#251#148'dN'#255#163't_'#255#175#129'n'#255#186#143'~'#255

-  +#198#157#141#255#204#167#151#255#201#161#145#255#191#149#132#255#179#135'u'

-  +#255#167'ye'#255#153'iT'#255#136'WC'#254'pE2'#249'c9&'#255'_3'#31#212'Z/'#27

-  +'|].'#29','#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'U9'#28#9'\.'#26'N'

-  +'^0'#29#157'`4 '#203'c6#'#230'c7%'#249'd:('#255'f=*'#255'i?-'#252'g>+'#255'e'

-  +';('#255'd9&'#253'b7$'#237'a5!'#214'_1'#30#180'\/'#26'lX,'#26#29#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'`0 '#16'[1'#24'*].'#27'B]1'#29'4\3'#31#25'U'

-  +'U'#0#3#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#255#255

-  +#255#248#15#255#255#255#255#255#254#0#0''#255#255#255#255#240#0#0#7#255#255

-  +#255#255#192#0#0#1#255#255#255#255#0#0#0#0#255#255#255#252#0#0#0#0'?'#255#255

-  +#248#0#0#0#0#31#255#255#240#0#0#0#0#7#255#255#192#0#0#0#0#3#255#255#128#0#0#0

-  +#0#1#255#255#128#0#0#0#0#0#255#255#0#0#0#0#0#0''#254#0#0#0#0#0#0''#252#0#0

-  +#0#0#0#0'?'#252#0#0#0#0#0#0#31#248#0#0#0#0#0#0#31#248#0#0#0#0#0#0#15#240#0#0

-  +#0#0#0#0#15#240#0#0#0#0#0#0#7#224#0#0#0#0#0#0#7#224#0#0#0#0#0#0#7#224#0#0#0#0

-  +#0#0#3#192#0#0#0#0#0#0#3#192#0#0#0#0#0#0#3#192#0#0#0#0#0#0#1#192#0#0#0#0#0#0

-  +#1#192#0#0#0#0#0#0#1#128#0#0#0#0#0#0#1#128#0#0#0#0#0#0#1#128#0#0#0#0#0#0#1

-  +#128#0#0#0#0#0#0#1#128#0#0#0#0#0#0#1#128#0#0#0#0#0#0#1#192#0#0#0#0#0#0#1#192

-  +#0#0#0#0#0#0#1#192#0#0#0#0#0#0#1#192#0#0#0#0#0#0#1#192#0#0#0#0#0#0#3#192#0#0

-  +#0#0#0#0#3#224#0#0#0#0#0#0#3#224#0#0#0#0#0#0#7#224#0#0#0#0#0#0#7#224#0#0#0#0

-  +#0#0#7#240#0#0#0#0#0#0#15#240#0#0#0#0#0#0#15#248#0#0#0#0#0#0#31#248#0#0#0#0#0

-  +#0#31#252#0#0#0#0#0#0'?'#252#0#0#0#0#0#0''#254#0#0#0#0#0#0''#254#0#0#0#0#0

-  +#0#255#255#0#0#0#0#0#1#255#255#128#0#0#0#0#3#255#255#192#0#0#0#0#7#255#255

-  +#224#0#0#0#0#15#255#255#240#0#0#0#0#31#255#255#248#0#0#0#0'?'#255#255#254#0#0

-  +#0#0''#255#255#255#0#0#0#1#255#255#255#255#192#0#0#7#255#255#255#255#240#0#0

-  +#31#255#255#255#255#254#0#0#255#255#255#255#255#255#248#31#255#255#255#255

-  +#255#255#255#255#255#255#255'('#0#0#0'0'#0#0#0'`'#0#0#0#1#0' '#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  ,#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'UUU'#3'333'#5'UUU'#6'III'#7'@@@'#8'@@@'

-  +#8'III'#7'UUU'#6'333'#5'UUU'#3#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#1'@@@'#4'@@@'#8'@@@'#16'DDD1BBBMCCC_FCCrDBB'#129'FDD'#133'CCCvCCCcDDDRD'

-  +'DD<FFF'#22'333'#10'333'#5#0#0#0#2#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'III'#7'FFF'#22'CCCEDAA|CCB'#173'FEC'

-  +#194'IGC'#198'KE?'#204'QD:'#211'TC4'#218'SD6'#216'OD<'#209'JD@'#201'HGD'#197

-  +'FEC'#195'DDC'#184'DDD'#139'EEEUDDD"999'#9#0#0#0#2#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#1'+++'#6'FFF'#22'BBBaDDC'#175'JEA'#198'TB5'#218'a>('#240

-  +'l@!'#252'qE!'#253'yJ$'#252#129'R&'#252#138'X)'#253#134'T('#253'}N%'#252'uG#'

-  +#252'oC"'#253'h=#'#248'Z>.'#230'OC;'#211'FEC'#198'DDC'#187'BBB|CCC&999'#9#0#0

-  +#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#2'<<<'#17'CCCWEED'#170'OB9'#211'd=&'#244'qE"'#253

-  +#137'V)'#253#164'h.'#255#186'y3'#255#194#130'5'#255#198#135'6'#255#203#140'7'

-  +#255#207#145'8'#255#205#143'7'#255#201#137'7'#255#196#132'6'#255#192'5'#255

-  +#177'r1'#255#152'_+'#254'{L%'#252'k?"'#251'[>-'#233'IEA'#203'EDD'#184'DDDq<<'

-  +'<'#30'@@@'#4#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0'@@@'#4'BBB#EDD'#146'KB<'#205'a='''#242'yK&'#252#166'i/'#255#192

-  +#128'6'#255#205#143'9'#255#217#159'<'#255#226#169'>'#255#230#173'>'#255#232

-  +#176'>'#255#235#180'?'#255#237#182'@'#255#236#181'?'#255#234#178'>'#255#231

-  +#175'>'#255#228#171'>'#255#223#165'<'#255#211#150':'#255#199#136'8'#255#182

-  +'v5'#255#146'\,'#253'k@"'#252'W?1'#227'GFE'#199'CCC'#171'???=@@@'#8#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'III'#7'DDD8ECB'#170'[>,'

-  +#234'sF$'#252#160'e0'#255#196#133';'#255#216#158'@'#255#227#170'A'#255#234

-  +#180'C'#255#241#189'D'#255#245#193'E'#255#246#195'F'#255#247#195'F'#255#247

-  +#196'E'#255#248#197'F'#255#248#197'E'#255#247#195'F'#255#247#194'F'#255#246

-  +#194'E'#255#244#192'E'#255#238#184'C'#255#230#176'C'#255#223#166'A'#255#207

-  +#147'='#255#182'w7'#255#138'V+'#253'h>#'#250'NA9'#215'CCC'#186'@@@[;;;'#13#0

-  +#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'@@@'#8'EEENGC?'#189'b;%'#245#139

-  +'W-'#253#190';'#255#212#153'B'#255#227#172'F'#255#239#186'I'#255#242#191'J'

-  +#255#244#193'K'#255#246#195'K'#255#247#196'K'#255#247#196'K'#255#247#196'L'

-  +#255#247#196'L'#255#247#196'L'#255#247#196'L'#255#247#196'L'#255#247#196'K'

-  +#255#247#196'K'#255#246#196'K'#255#245#195'J'#255#243#193'J'#255#242#190'J'

-  +#255#234#180'H'#255#221#165'E'#255#202#142'@'#255#173'o5'#255'oC$'#252'T?3'

-  +#225'CCC'#193'AAAy@@@'#16#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'+++'#6'DDD@K@<'#198'g<"'

-  +#250#154'a2'#254#200#141'B'#255#222#168'J'#255#234#183'N'#255#239#189'P'#255

-  +#242#192'P'#255#242#193'P'#255#243#193'P'#255#243#193'P'#255#243#193'P'#255

-  +#243#193'P'#255#243#193'P'#255#243#193'P'#255#243#193'P'#255#243#193'P'#255

-  +#243#193'P'#255#243#193'P'#255#243#193'P'#255#243#193'P'#255#243#193'P'#255

-  +#242#193'P'#255#242#193'P'#255#241#191'P'#255#238#187'N'#255#229#176'L'#255

-  +#214#158'G'#255#183'y;'#255'yJ)'#252'[=,'#236'CCB'#195'EEEoFFF'#11#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0'UUU'#3'CCC.J?;'#190'g=#'#252#167'k7'#255#205#147'H'#255#226#173'P'#255#235

-  +#186'T'#255#238#189'T'#255#239#190'U'#255#239#190'U'#255#239#190'U'#255#239

-  +#190'U'#255#239#190'U'#255#239#190'U'#255#191#152'D'#255'A4'#23#255'>1'#22

-  +#255'D6'#24#255'H9'#26#255'RA'#29#255#146's4'#255#228#182'Q'#255#239#190'U'

-  +#255#239#190'U'#255#239#190'U'#255#239#190'U'#255#239#190'U'#255#238#190'U'

-  +#255#237#187'T'#255#232#182'S'#255#217#162'M'#255#191#129'B'#255#132'R-'#253

-  +'[9('#240'DDD'#191'CCCW@@@'#8#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'>>>'#29'F@='#169'd9#'#250#165'i8'#255#208

-  +#151'L'#255#226#174'U'#255#233#184'Y'#255#235#186'Y'#255#235#187'Y'#255#235

-  +#187'Y'#255#235#187'Y'#255#235#187'Y'#255#235#187'Y'#255#235#187'Y'#255'<0'

-  +#23#255#1#1#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#0#255

-  ,#0#0#0#255#6#5#2#255'*!'#16#255'w^-'#255#227#181'W'#255#235#187'Y'#255#235

-  +#187'Y'#255#235#186'Y'#255#234#186'Y'#255#231#181'X'#255#218#164'Q'#255#194

-  +#133'E'#255'{J*'#252'U=/'#230'CCC'#186'EEE?@@@'#4#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'333'#10'DAAw_9%'#244#150'^4'#254#204#148

-  +'N'#255#225#175'Y'#255#230#181'\'#255#231#183'\'#255#231#183'\'#255#231#183

-  +'\'#255#231#183'\'#255#231#183'\'#255#231#183'\'#255#231#183'\'#255'hR*'#255

-  +#1#1#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#0#255#1#1#0#255'*!'#17#255#164#130'B'

-  +#255#231#183'\'#255#231#183'\'#255#231#182']'#255#228#179'['#255#217#165'V'

-  +#255#185'|C'#255'nA'''#252'N>5'#219'CCC'#163'@@@'#20#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#128#128#128#2'@@@,W8*'#227#132'P/'#253#196

-  +#139'M'#255#220#170'['#255#226#179'_'#255#227#179'_'#255#227#179'_'#255#227

-  +#179'_'#255#227#179'_'#255#227#179'_'#255#227#179'_'#255#227#179'_'#255#227

-  +#179'_'#255#4#3#2#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#1#1#0#255#7#6#3#255#178#141'J'#255#227#179'_'#255#227#179'_'#255#225

-  +#176'^'#255#211#158'U'#255#174'q?'#255'c9"'#252'GA>'#203'BBB]III'#7#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'FFF'#11'H>:'#150'e:#'#252#186'~H'#255

-  +#214#164'\'#255#222#174'a'#255#223#175'b'#255#223#175'b'#255#223#175'b'#255

-  +#223#175'b'#255#223#175'b'#255#223#175'b'#255#223#175'b'#255#223#175'b'#255

-  +#188#148'S'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#1#255#154'yC'#255#223#175'b'#255#223

-  +#175'a'#255#220#171'`'#255#204#150'U'#255#145'Y5'#254'Y8('#238'CCC'#175'FFF'

-  +#29#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'UUU'#3'AAA3Z7'''#236#148'\6'#255

-  +#207#154'Z'#255#218#170'b'#255#219#171'd'#255#219#171'd'#255#219#171'd'#255

-  +#219#171'd'#255#219#171'd'#255#219#171'd'#255#219#171'd'#255#219#171'd'#255

-  +#219#171'd'#255#211#165'`'#255#6#5#3#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#2#2#1#255#219#171

-  +'d'#255#219#171'd'#255#219#171'd'#255#215#166'`'#255#189#129'M'#255'h=$'#252

-  +'H>;'#207'EEEhIII'#7#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'III'#7'K<5'#137'h;$'#252

-  +#188#130'O'#255#213#164'b'#255#215#167'd'#255#215#167'd'#255#215#167'd'#255

-  +#215#167'd'#255#215#167'd'#255#215#167'd'#255#215#167'd'#255#215#167'd'#255

-  +#215#167'd'#255#215#167'd'#255#215#167'd'#255#168#130'N'#255#1#1#1#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#200#155']'#255#215#167'd'#255#215#167'd'#255#215#166'd'#255

-  +#205#153']'#255#153'`:'#255'[6$'#243'CCC'#164';;;'#13#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0'777'#14'Z5%'#229#150'[8'#255#203#150'^'#255#211#162'e'#255#211#162

-  +'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211#162

-  +'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211#162'e'#255#211#162

-  +'e'#255#211#162'e'#255#142'mD'#255#1#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#0#255#145'pF'#255#211#162'e'

-  +#255#211#162'e'#255#211#162'e'#255#210#159'd'#255#189#131'R'#255'f9#'#252'I@'

-  +';'#202'===.'#128#128#128#2#0#0#0#0#0#0#0#0#128#128#128#2'D??4]3'#30#252#181

-  +'yM'#255#206#155'd'#255#207#157'f'#255#207#157'f'#255#207#157'f'#255#207#157

-  +'f'#255#205#155'f'#255'S?('#255#1#1#1#255#1#0#0#255#3#2#1#255#15#11#7#255#169

-  +#128'S'#255#207#157'f'#255#207#157'f'#255#207#157'f'#255#16#12#8#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#0#0

-  +#255#186#141'\'#255#207#157'f'#255#207#157'f'#255#207#157'f'#255#207#157'e'

-  +#255#201#148'`'#255#135'O0'#254'R9-'#226'BBBeIII'#7#0#0#0#0#0#0#0#0'UUU'#6'O'

-  +'8/'#141'o>'''#251#193#139'['#255#203#151'e'#255#203#152'e'#255#203#152'e'

-  +#255#203#152'e'#255#203#152'e'#255#23#17#11#255#1#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#1#1#1#255#153'rL'#255#203#152'e'#255#203#152'e'#255',!'

-  +#22#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#0#0

-  +#255' '#24#16#255#203#152'e'#255#203#152'e'#255#203#152'e'#255#203#152'e'#255

-  +#203#152'e'#255#202#150'd'#255#165'jC'#255'\3!'#247'DDD'#151'MMM'#10#0#0#0#0

-  +#0#0#0#0'@@@'#8'Z6%'#206#142'T5'#255#195#142'a'#255#198#145'c'#255#198#145'c'

-  +#255#198#145'c'#255#198#145'c'#255'W@,'#255#1#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255'%'#27#19#255#198#145'c'#255#198#145'c'

-  ,#255'U>+'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#0#0#255#1#1#1#255#27#20#13

-  +#255'vW;'#255#198#145'c'#255#198#145'c'#255#198#145'c'#255#198#145'c'#255#198

-  +#145'c'#255#198#145'c'#255#198#145'c'#255#184'}S'#255'`4 '#252'FA>'#183'@@@'

-  +#12#0#0#0#0#0#0#0#0'999'#9']2'#30#243#166'gD'#255#192#138'`'#255#192#139'a'

-  +#255#192#139'a'#255#192#139'a'#255#190#137'a'#255#1#1#1#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#8#6#4#255#192#139'a'

-  +#255#192#139'a'#255#129']B'#255#1#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#0#0#255'Q;)'#255#192

-  +#139'a'#255#192#139'a'#255#192#139'a'#255#192#139'a'#255#192#139'a'#255#192

-  +#139'a'#255#192#139'a'#255#192#139'a'#255#192#139'a'#255#192#139'a'#255#188

-  +#132'['#255'u@'''#252'M;3'#212'999'#18#0#0#0#0#0#0#0#0'FFF'#11'^1'#29#250#174

-  +'oK'#255#186#131'\'#255#186#131'\'#255#186#131'\'#255#186#131'\'#255'vS:'#255

-  +#1#1#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#0#0

-  +#255'S:)'#255#186#131'\'#255#186#131'\'#255#184#129'\'#255'('#28#20#255#1#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255'Q9('#255#186#131'\'#255#186#131'\'#255#186#131'\'#255#186#131'\'#255

-  +#186#131'\'#255#186#131'\'#255#186#131'\'#255#186#131'\'#255#186#131'\'#255

-  +#186#131'\'#255#186#131'\'#255#186#130'['#255#131'I-'#255'Q9-'#221'FFF!'#0#0

-  +#0#1#0#0#0#0'M33'#20'_2'#30#251#173'pN'#255#180'{X'#255#180'{X'#255#180'{X'

-  +#255#180'{X'#255'-'#30#22#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#2#1#1#255'dE1'#255#180'{X'#255#180'{X'#255#180'{X'#255#180'{X'

-  +#255#180'{X'#255'O6&'#255#2#1#1#255#1#1#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255'G0#'#255#180'{X'#255#180'{X'#255#178'{X'#255'hH3'#255'U:)'

-  +#255'mK5'#255#180'{X'#255#180'{X'#255#180'{X'#255#180'{X'#255#180'{X'#255#180

-  +'{X'#255#180'{X'#255#139'P3'#255'U7)'#231'BBB2UUU'#3#0#0#0#0'T1&,c5 '#249#174

-  +'rQ'#255#178'wW'#255#178'wW'#255#178'wW'#255#178'wW'#255#8#5#4#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#1#0#0#255#27#18#13#255#164'nQ'#255#178'wW'#255

-  +#178'wW'#255#178'wW'#255#178'wW'#255#178'wW'#255#178'wW'#255#178'wW'#255#178

-  +'wW'#255#142'_F'#255'H0#'#255#20#13#10#255#25#16#12#255'9'''#28#255'lI5'#255

-  +#178'wW'#255#178'wW'#255'kG4'#255#1#1#1#255#1#1#0#255#1#0#0#255#1#0#0#255#3#2

-  +#1#255'zQ<'#255#178'wW'#255#178'wW'#255#178'wW'#255#178'wW'#255#178'wW'#255

-  +#149'W9'#255'Z5%'#238'DDD-'#128#128#128#2#0#0#0#0'Y3 Bj8#'#248#173'qS'#255

-  +#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#1#0#0#255#1#1#1#255#0#0#0

-  +#255#0#0#0#255#5#3#3#255'cB2'#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'

-  +#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255

-  +#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#175

-  +'tW'#255#173'rU'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#2#1#1#255#152'eL'#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255

-  +#155'[>'#255'\4"'#244'@@@'#28#0#0#0#1#0#0#0#0'X0!3g7"'#246#172'oR'#255#174'r'

-  +'V'#255#174'rV'#255#174'rV'#255#166'lR'#255#7#5#3#255#131'VA'#255#155'fM'#255

-  +'oI7'#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'

-  +#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255

-  +#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255#174'rV'#255#174

-  +'rV'#255#153'dL'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255'<'''#29#255#174'rV'#255#174'rV'#255#174'rV'#255#173'qU'#255

-  +#151'Y='#255']6%'#239'333'#15#0#0#0#0#0#0#0#0'O1'''#25'b3'#31#247#168'jN'#255

-  +#172'oU'#255#172'oU'#255#172'oU'#255#172'oU'#255#172'oU'#255#172'oU'#255#172

-  +'oU'#255#172'oU'#255#172'oU'#255#172'oU'#255#172'oU'#255#172'oU'#255#136'XD'

-  +#255'U7*'#255'^=/'#255'kE5'#255#170'oU'#255#172'oU'#255#172'oU'#255#172'oU'

-  +#255#172'oU'#255#172'oU'#255#172'oU'#255#172'oU'#255#172'oU'#255#172'oU'#255

-  +#172'oU'#255#172'oU'#255#5#3#2#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255'!'#21#16#255#172'oU'#255#172'oU'#255#172'oU'#255#172'oT'

-  +#255#143'R7'#255'Y6('#225'@@@'#12#0#0#0#0#0#0#0#0'333'#5'_2'#31#248#167'hL'

-  +#255#172'qW'#255#172'qX'#255#172'qX'#255#172'qX'#255#172'qX'#255#172'qX'#255

-  +#172'qX'#255#172'qX'#255#172'qX'#255#172'qX'#255#172'qX'#255':&'#30#255#1#0#0

-  +#255#1#1#0#255#1#1#1#255#1#1#0#255#4#2#2#255#133'WD'#255#172'qX'#255#172'qX'

-  +#255#172'qX'#255#172'qX'#255#172'qX'#255#172'qX'#255#172'qX'#255#172'qX'#255

-  +#172'qX'#255#172'qX'#255'E.$'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#30#20#15#255#172'qX'#255#172'qX'#255#172'qX'#255#172'pV'#255

-  +#134'K2'#255'V8)'#199'999'#9#0#0#0#0#0#0#0#0#0#0#0#1'_3'#30#239#164'fJ'#255

-  +#175'w]'#255#175'w^'#255#175'w^'#255#175'w^'#255#175'w^'#255#175'w^'#255#175

-  +'w^'#255#175'w^'#255#175'w^'#255#175'w^'#255'hF7'#255#1#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#7#5#4#255#175'w^'#255#175'w^'#255'{TB'

-  ,#255'X;/'#255']?2'#255'VD'#255#175'w^'#255#175'w^'#255#175'w^'#255#175'w^'

-  +#255#165'qY'#255#1#1#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#1#255

-  +'fF7'#255#175'w^'#255#175'w^'#255#175'w^'#255#175'v\'#255'{D-'#253'S5('#154

-  +'333'#5#0#0#0#0#0#0#0#0#0#0#0#0'a3'#30#200#149'Z?'#255#179'|c'#255#179'}e'

-  +#255#179'}e'#255#179'}e'#255#179'}e'#255#179'}e'#255#179'}e'#255#179'}e'#255

-  +#179'}e'#255#179'}e'#255#19#13#11#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255#2#1#1#255#179'}e'#255'E0'''#255#1#0#0#255#1#1#0#255#1#1

-  +#0#255#0#0#0#255'R:/'#255#179'}e'#255#179'}e'#255#179'}e'#255#179'}e'#255'.!'

-  +#26#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#1#255#177'}e'#255#179'}e'

-  +#255#179'}e'#255#179'}e'#255#175'v\'#255'g8$'#251'W7*O'#0#0#0#1#0#0#0#0#0#0#0

-  +#0#0#0#0#0'[0'#29'lyD-'#249#181#128'i'#255#183#131'l'#255#183#131'l'#255#183

-  +#131'l'#255#183#131'l'#255#183#131'l'#255#183#131'l'#255#183#131'l'#255#183

-  +#131'l'#255#183#131'l'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#0#0#0#255#0#0#0#255'"'#25#20#255#183#131'l'#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255'B/'''#255#183#131'l'#255#183#131'l'#255

-  +#183#131'l'#255#169'yd'#255#1#1#1#255#0#0#0#255#0#0#0#255#0#0#0#255'#'#25#21

-  +#255#183#131'l'#255#183#131'l'#255#183#131'l'#255#183#131'l'#255#167'lQ'#255

-  +'^3'#31#248'@@@'#12#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'Y1'#30#26'b5 '#244#178'z'

-  +'c'#255#187#138't'#255#187#138'u'#255#187#138'u'#255#187#138'u'#255#187#138

-  +'u'#255#187#138'u'#255#187#138'u'#255#187#138'u'#255#187#138'u'#255#2#2#1#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#0#255#170'~j'#255#177

-  +#130'o'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#4#3#3

-  +#255#187#138'u'#255#187#138'u'#255#187#138'u'#255#187#138'u'#255'jNB'#255#11

-  +#8#7#255#0#0#0#255#0#0#0#255#152'p`'#255#187#138'u'#255#187#138'u'#255#187

-  +#138'u'#255#186#136'r'#255#144'W?'#255']6#'#193'333'#5#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0'`4'#31#217#160'gN'#255#190#143'{'#255#191#145'}'#255#191

-  +#145'}'#255#191#145'}'#255#191#145'}'#255#191#145'}'#255#191#145'}'#255#191

-  +#145'}'#255#191#145'}'#255#19#14#12#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255'7*$'#255#191#145'}'#255#164'|k'#255#1#1#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#2#2#1#255#191#145'}'#255#191#145'}'#255

-  +#191#145'}'#255#191#145'}'#255#191#145'}'#255#191#145'}'#255#0#0#0#255#30#23

-  +#20#255#191#145'}'#255#191#145'}'#255#191#145'}'#255#191#145'}'#255#187#138

-  +'t'#255'p>('#249'Y2#U'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27

-  +'huB+'#244#190#142'z'#255#195#152#132#255#195#152#132#255#195#152#132#255#195

-  +#152#132#255#195#152#132#255#195#152#132#255#195#152#132#255#195#152#132#255

-  +'A3,'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#0#0#255#183#142'|'#255

-  +#195#152#132#255#138'k]'#255#1#1#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#9#7#6#255#195#152#132#255#195#152#132#255#195#152#132#255#195

-  +#152#132#255#195#152#132#255#195#152#132#255#1#1#1#255#160'}m'#255#195#152

-  +#132#255#195#152#132#255#195#152#132#255#194#150#131#255#167'pW'#255'`4 '#240

-  +'M33'#10#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'@@'#0#4'`3'#30#229

-  +#164'nU'#255#197#156#137#255#199#159#141#255#199#159#141#255#199#159#141#255

-  +#199#159#141#255#199#159#141#255#199#159#141#255#199#159#141#255'x`U'#255#1#1

-  +#1#255#0#0#0#255#0#0#0#255#0#0#0#255#18#14#13#255#199#159#141#255#199#159#141

-  +#255' '#26#23#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#0#255

-  +#139'oc'#255#199#159#141#255#199#159#141#255#199#159#141#255#199#159#141#255

-  +#199#159#141#255#199#159#141#255'^KC'#255#199#159#141#255#199#159#141#255#199

-  +#159#141#255#199#159#141#255#192#147''#255'rB-'#249'\1 k'#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'\.'#28'Sn>('#243#194#149#131

-  +#255#202#165#148#255#203#165#149#255#203#165#149#255#203#165#149#255#203#165

-  +#149#255#203#165#149#255#203#165#149#255#169#138'|'#255#0#0#0#255#1#1#1#255

-  +#16#13#11#255#23#19#17#255#171#139'~'#255#203#165#149#255#203#165#149#255#3#3

-  +#2#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#0#0#255'*"'#31#255#203#165

-  +#149#255#203#165#149#255#203#165#149#255#203#165#149#255#203#165#149#255#203

-  +#165#149#255#203#165#149#255#203#165#149#255#203#165#149#255#203#165#149#255

-  +#203#165#149#255#201#162#145#255#163'mU'#255'_3'#31#227'U'#0#0#3#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'a3'#30#213#153

-  +'eM'#254#204#167#151#255#207#172#158#255#207#172#158#255#207#172#158#255#207

-  +#172#158#255#207#172#158#255#207#172#158#255#205#170#156#255#0#0#0#255'<1.'

-  +#255#207#172#158#255#207#172#158#255#207#172#158#255#207#172#158#255#207#172

-  +#158#255#20#16#15#255#1#0#0#255#0#0#0#255#0#0#0#255#1#0#0#255'?40'#255#207

-  +#172#158#255#207#172#158#255#207#172#158#255#207#172#158#255#207#172#158#255

-  +#207#172#158#255#207#172#158#255#207#172#158#255#207#172#158#255#207#172#158

-  +#255#207#172#158#255#207#171#156#255#192#147''#255'k<'''#244'Z.'#29'L'#0#0#0

-  ,#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'^'

-  +'/'#27'&b5!'#240#174'{f'#255#209#176#162#255#212#180#167#255#212#180#167#255

-  +#212#180#167#255#212#180#167#255#212#180#167#255#212#180#167#255#6#5#5#255

-  +#179#151#140#255#212#180#167#255#212#180#167#255#212#180#167#255#212#180#167

-  +#255#212#180#167#255#212#180#167#255'gXR'#255#0#0#0#255#0#0#0#255'gXR'#255

-  +#212#180#167#255#212#180#167#255#212#180#167#255#212#180#167#255#212#180#167

-  +#255#212#180#167#255#212#180#167#255#212#180#167#255#212#180#167#255#212#180

-  +#167#255#212#180#167#255#211#179#166#255#202#162#146#255'M7'#248'`2'#31#169

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0'[/'#26'Fg:#'#242#190#145''#255#214#184#172#255#216#187

-  +#176#255#216#187#176#255#216#187#176#255#216#187#176#255#216#187#176#255'k]X'

-  +#255#216#187#176#255#216#187#176#255#216#187#176#255#216#187#176#255#216#187

-  +#176#255#216#187#176#255#216#187#176#255'E<8'#255#0#0#0#255'>63'#255#216#187

-  +#176#255#216#187#176#255#216#187#176#255#216#187#176#255#216#187#176#255#216

-  +#187#176#255#216#187#176#255#216#187#176#255#216#187#176#255#216#187#176#255

-  +#216#187#176#255#216#187#175#255#210#176#163#255#147'_H'#252'`4'#31#210'U++'

-  +#6#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'\0'#29'trB-'#242#198#157#140#255#218#191#180

-  +#255#221#195#185#255#221#195#185#255#221#195#185#255#221#195#185#255#221#195

-  +#185#255#221#195#185#255#221#195#185#255#221#195#185#255#221#195#185#255#221

-  +#195#185#255#221#195#185#255#221#195#185#255',''%'#255'920'#255#221#195#185

-  +#255#221#195#185#255#221#195#185#255#221#195#185#255#221#195#185#255#221#195

-  +#185#255#221#195#185#255#221#195#185#255#221#195#185#255#221#195#185#255#221

-  +#195#185#255#220#194#184#255#213#183#170#255#162'oW'#255'b5 '#232'].'#23#22#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'_1'#30#138'l>)'#242#187#143'|'#255

-  +#221#196#186#255#225#202#193#255#225#203#194#255#225#203#194#255#225#203#194

-  +#255#225#203#194#255#225#203#194#255#225#203#194#255#225#203#194#255#225#203

-  +#194#255#225#203#194#255#225#203#194#255#225#203#194#255#225#203#194#255#225

-  +#203#194#255#225#203#194#255#225#203#194#255#225#203#194#255#225#203#194#255

-  +#225#203#194#255#225#203#194#255#225#203#194#255#225#203#194#255#225#203#194

-  +#255#224#201#192#255#212#180#168#255#149'aK'#252'a5!'#230']2'#25')'#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#28'\d9$'#241#172'|h'

-  +#255#222#197#187#255#228#206#200#255#230#209#203#255#230#209#203#255#230#209

-  +#203#255#230#209#203#255#230#209#203#255#230#209#203#255#230#209#203#255#230

-  +#209#203#255#230#209#203#255#230#209#203#255#230#209#203#255#230#209#203#255

-  +#230#209#203#255#230#209#203#255#230#209#203#255#230#209#203#255#230#209#203

-  +#255#230#209#203#255#230#209#203#255#229#209#202#255#226#204#197#255#208#173

-  +#159#255#131'R;'#245'a3'#31#207'Y3'#26#20#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'].'#28'7a5!'#236#137'WB'#248#201#163

-  +#148#255#229#210#202#255#233#215#210#255#234#217#212#255#234#217#212#255#234

-  +#217#212#255#234#217#212#255#234#217#212#255#234#217#212#255#234#217#212#255

-  +#234#217#212#255#234#217#212#255#234#217#212#255#234#217#212#255#234#217#212

-  +#255#234#217#212#255#234#217#212#255#234#217#212#255#234#216#211#255#231#212

-  +#206#255#221#195#185#255#174#128'm'#255'm>*'#242'`1'#29#163'f33'#5#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0'b'''#20#13'`2'#29#140'd8$'#241#151'hR'#251#211#179#167#255#233#215#210

-  +#255#236#220#215#255#237#222#219#255#238#225#221#255#238#225#221#255#238#225

-  +#221#255#238#225#221#255#238#225#221#255#238#225#221#255#238#225#221#255#238

-  +#225#221#255#238#225#221#255#238#223#220#255#236#221#217#255#234#219#213#255

-  +#227#205#198#255#188#146#128#255'wH2'#242'b5 '#219'].'#27'B'#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'X1'#29#26'a3'#30#168'f:$'#242#134'T?'#246

-  +#179#136'v'#255#214#183#172#255#235#218#214#255#238#225#221#255#239#226#223

-  +#255#240#227#224#255#240#227#225#255#240#227#225#255#239#226#223#255#239#225

-  +#222#255#238#224#220#255#226#204#196#255#199#161#145#255#158'o['#254'rD-'#239

-  +'b4"'#232'^2'#30'\'#0#0#0#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0'`+ '#24']/'#30'fd6#'#197'c8#'#243'vE0'#240#140'ZF'#247

-  ,#156'mY'#255#171'm'#255#184#143''#255#178#136'v'#255#164'wd'#255#148'dP'

-  +#252#130'Q;'#243'l<('#239'b6"'#234'b5!'#151'Z-'#29'>UU'#0#3#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0'].'#23#11'Z/'#29'G^4 fa4!'#130'd6!'#166'd7#'#194'd7"'#182

-  +'c6"'#150'_3'#31's\0'#29'YY/'#30'+'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#255#255#224#3#255#255#255#255#255#254

-  +#0#0''#255#255#255#255#248#0#0#31#255#255#255#255#224#0#0#7#255#255#255#255

-  +#192#0#0#3#255#255#255#255#128#0#0#1#255#255#255#255#0#0#0#0''#255#255#254#0

-  +#0#0#0''#255#255#252#0#0#0#0'?'#255#255#248#0#0#0#0#31#255#255#240#0#0#0#0

-  +#15#255#255#240#0#0#0#0#15#255#255#224#0#0#0#0#7#255#255#224#0#0#0#0#3#255

-  +#255#192#0#0#0#0#3#255#255#192#0#0#0#0#3#255#255#192#0#0#0#0#1#255#255#128#0

-  +#0#0#0#1#255#255#128#0#0#0#0#1#255#255#128#0#0#0#0#1#255#255#128#0#0#0#0#1

-  +#255#255#128#0#0#0#0#0#255#255#128#0#0#0#0#0#255#255#128#0#0#0#0#0#255#255

-  +#128#0#0#0#0#0#255#255#128#0#0#0#0#1#255#255#128#0#0#0#0#1#255#255#128#0#0#0

-  +#0#1#255#255#128#0#0#0#0#1#255#255#192#0#0#0#0#1#255#255#192#0#0#0#0#3#255

-  +#255#192#0#0#0#0#3#255#255#224#0#0#0#0#7#255#255#224#0#0#0#0#7#255#255#224#0

-  +#0#0#0#15#255#255#240#0#0#0#0#15#255#255#248#0#0#0#0#31#255#255#248#0#0#0#0

-  +'?'#255#255#252#0#0#0#0'?'#255#255#254#0#0#0#0''#255#255#255#0#0#0#0#255#255

-  +#255#255#128#0#0#1#255#255#255#255#192#0#0#3#255#255#255#255#224#0#0#15#255

-  +#255#255#255#248#0#0#31#255#255#255#255#254#0#0''#255#255#255#255#255#192#7

-  +#255#255#255#255#255#255#255#255#255#255#255#255'('#0#0#0' '#0#0#0'@'#0#0#0#1

-  +#0' '#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#2'CCC'#19'FFF'#22'UUU'#3#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0'@@@'#12'EAAGDDB'#133'DDC'#181'HC?'#205'LA;'#213'JB='#212'FC@'#204'E'

-  +'ED'#185'DDC'#145'DDDSFFF'#22#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0'III'#14'EEBoLA;'#208'eE-'#235'xK('#246#131'R)'#250#139

-  +'W*'#251#149'_,'#252#145'\+'#252#136'V*'#250#128'Q('#249'rI)'#244'\D2'#227'F'

-  +'B?'#207'CCC'#134'DDD'#30#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'EEENMA9'

-  +#206'oF)'#245#147']-'#252#187'}4'#255#211#150'9'#255#224#167';'#255#230#174

-  +'='#255#234#179'>'#255#233#178'>'#255#228#171'='#255#221#163'<'#255#205#143

-  +'8'#255#175'r2'#255#131'R*'#250'eC,'#238'FB?'#205'CCCoUUU'#3#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'@@@'

-  +#4'GEA|dB+'#239#142'[.'#252#198#137'<'#255#227#172'C'#255#236#183'E'#255#242

-  +#190'F'#255#246#195'G'#255#248#198'G'#255#249#199'H'#255#249#198'H'#255#247

-  +#196'G'#255#245#193'G'#255#240#188'F'#255#234#180'D'#255#220#163'A'#255#183

-  +'y8'#255'}O*'#250'WA4'#225'CCC'#152'<<<'#17#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#1'G@='#148'lD*'#245#175'u8'#254

-  +#221#166'I'#255#234#182'M'#255#242#193'P'#255#243#194'O'#255#244#195'O'#255

-  +#244#195'P'#255#244#195'P'#255#244#195'P'#255#244#195'P'#255#244#195'P'#255

-  +#244#195'P'#255#243#194'O'#255#243#194'O'#255#240#190'O'#255#230#179'L'#255

-  +#212#155'F'#255#148'^1'#252'`A.'#234'CCC'#171'@@@'#12#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'FA>vmB)'#247#189#130'A'#255#224#172

-  +'P'#255#236#187'U'#255#238#189'W'#255#238#190'V'#255#238#190'V'#255#238#190

-  +'V'#255'fQ%'#255#17#14#6#255#17#13#6#255#16#13#6#255',#'#16#255#155'|8'#255

-  +#238#190'V'#255#238#190'V'#255#238#189'V'#255#238#189'W'#255#233#184'U'#255

-  +#217#163'M'#255#164'l8'#254'_>+'#238'DDD'#147#128#128#128#2#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'FDD=e?*'#243#183'}B'#255#223#171'W'#255#232

-  +#183'['#255#232#183'\'#255#232#183'\'#255#232#183'\'#255#232#183'\'#255'>1'

-  +#25#255#1#1#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#1#255#5#4#2#255

-  +#22#17#9#255'pX,'#255#218#171'V'#255#232#183'\'#255#230#182'['#255#217#164'T'

-  +#255#152'a7'#252'V=1'#228'DDDb'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#128

-  ,#128#128#2'Z<-'#215#163'k='#254#217#166'['#255#226#178'`'#255#226#179'a'#255

-  +#226#179'a'#255#226#179'a'#255#226#179'a'#255#226#179'a'#255#5#4#2#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#3#3#1#255',#'#19#255#226#179'a'#255#225#177'_'#255#208#154'T'#255

-  +#129'O1'#251'H?;'#205'@@@'#20#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'J@<[vF,'#249

-  +#206#153'Y'#255#220#172'b'#255#220#172'c'#255#220#172'c'#255#220#172'c'#255

-  +#220#172'c'#255#220#172'c'#255#205#160']'#255#4#3#2#255#0#0#0#255#0#0#0#255#0

-  +#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#1#0#0#255'.$'#21#255#220#172'c'#255#218#170'a'#255#190#132'N'#255'c=)'

-  +#242'CCCz'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'\9('#218#177'vG'#255#213#164'c'

-  +#255#214#166'e'#255#214#166'e'#255#214#166'e'#255#214#166'e'#255#214#166'e'

-  +#255#214#166'e'#255#214#166'e'#255'v\8'#255#2#2#1#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255

-  +#11#9#5#255#214#166'e'#255#214#166'e'#255#209#159'a'#255#139'Y6'#252'K>7'#206

-  +'333'#5#0#0#0#0#0#0#0#0'?;9'#19'mA+'#247#201#148'^'#255#209#159'e'#255#209

-  +#159'e'#255#209#159'e'#255#146'oF'#255#18#14#9#255#21#16#10#255'O<&'#255#209

-  +#159'e'#255#209#159'e'#255'$'#27#17#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#11#8#5#255

-  +#209#159'e'#255#209#159'e'#255#208#158'e'#255#185#128'Q'#255'^9)'#240'BBB6'#0

-  +#0#0#0#0#0#0#0'P8-q'#144'Z:'#252#201#150'd'#255#202#151'd'#255#202#151'd'#255

-  +'uW:'#255#2#1#1#255#0#0#0#255#0#0#0#255#1#0#0#255'-!'#22#255#202#151'd'#255

-  +'ZC-'#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#1#1#0#255#5#4#3#255'gM3'#255#202#151'd'#255#202#151'd'#255#202

-  +#151'd'#255#197#145'`'#255'l@*'#248'CCBo'#0#0#0#0#0#0#0#0'^7$'#194#171'pK'

-  +#255#194#140'a'#255#194#140'a'#255#194#140'a'#255#6#4#3#255#0#0#0#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#7#5#4#255#194#140'a'#255#139'eE'#255#1#1#1#255#0#0#0

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#13#9#6#255'wV<'#255

-  +#192#138'a'#255#194#140'a'#255#194#140'a'#255#194#140'a'#255#194#140'a'#255

-  +#193#140'a'#255#137'T7'#252'K=6'#158#0#0#0#0#0#0#0#0'a6#'#220#177'uQ'#255#185

-  +#129'['#255#185#129'['#255'Y>'#255#1#1#0#255#0#0#0#255#0#0#0#255#0#0#0#255#2

-  +#1#1#255'8'''#27#255#185#129'['#255#185#129'['#255'+'#30#21#255#2#1#1#255#0#0

-  +#0#255#0#0#0#255#0#0#0#255#0#0#0#255#13#9#6#255#179'}Y'#255#185#129'['#255

-  +#185#129'['#255#185#129'['#255#185#129'['#255#185#129'['#255#185#129'['#255

-  +#185#129'['#255#152'^>'#255'P:1'#190#0#0#0#0#0#0#0#0'c7#'#227#175'sS'#255#178

-  +'xW'#255#178'xW'#255','#30#21#255#0#0#0#255#0#0#0#255#0#0#0#255#5#4#3#255'uO'

-  +':'#255#178'xW'#255#178'xW'#255#178'xW'#255#178'xW'#255'xP:'#255'('#26#19#255

-  +#10#7#5#255#10#7#5#255#28#19#13#255#168'rS'#255#163'nO'#255#25#17#12#255#6#4

-  +#3#255#17#11#8#255'bB/'#255#178'xW'#255#178'xW'#255#178'xW'#255#158'aB'#255

-  +'V9,'#210#0#0#0#0#0#0#0#0'f7"'#227#173'sT'#255#175'tW'#255#175'tW'#255#15#10

-  +#7#255#13#8#6#255#5#4#3#255'+'#29#21#255#169'pU'#255#175'tW'#255#175'tW'#255

-  +#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#175'tW'#255#175

-  +'tW'#255#175'tW'#255#175'tW'#255#17#11#8#255#0#0#0#255#0#0#0#255#0#0#0#255#2

-  +#2#1#255#136'ZD'#255#175'tW'#255#175'tW'#255#161'dG'#255'[8('#211#0#0#0#0#0#0

-  +#0#0'e6"'#218#171'nR'#255#173'qV'#255#173'qV'#255#136'YC'#255#173'qV'#255#173

-  +'qV'#255#173'qV'#255#173'qV'#255#173'qV'#255'sK9'#255'uM:'#255#159'gN'#255

-  +#173'qV'#255#173'qV'#255#173'qV'#255#173'qV'#255#173'qV'#255#173'qV'#255#173

-  +'qV'#255'5"'#27#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255'H/$'#255#173'qV'

-  +#255#173'qV'#255#153'^D'#255'Y8*'#188#0#0#0#0#0#0#0#0'c5 '#203#170'kQ'#255

-  +#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#164

-  +'mU'#255#11#7#6#255#1#1#1#255#1#1#1#255#5#3#2#255'TA'#255#174'sY'#255#174's'

-  +'Y'#255#174'sY'#255#174'sY'#255#174'sY'#255#174'sY'#255#128'UA'#255#2#1#1#255

-  +#0#0#0#255#0#0#0#255#0#0#0#255'J1&'#255#174'sY'#255#174'sY'#255#147'X?'#255

-  +'S8,'#140#0#0#0#0#0#0#0#0'd4'#30#153#162'gM'#255#178'{c'#255#178'{c'#255#178

-  +'{c'#255#178'{c'#255#178'{c'#255#178'{c'#255'A-$'#255#0#0#0#255#0#0#0#255#0#0

-  +#0#255#0#0#0#255#20#14#11#255'xSB'#255#6#4#3#255#5#4#3#255#18#12#10#255#170

-  +'u_'#255#178'{c'#255#178'{c'#255#9#6#5#255#0#0#0#255#0#0#0#255#3#2#2#255#166

-  +'s]'#255#178'{c'#255#178'{c'#255#131'P8'#252'S3$>'#0#0#0#0#0#0#0#0'[/'#27'3'

-  +#137'T='#247#184#134'o'#255#184#134'o'#255#184#134'o'#255#184#134'o'#255#184

-  +#134'o'#255#184#134'o'#255#14#10#9#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#1

-  +#255'\C7'#255#14#10#8#255#0#0#0#255#0#0#0#255#0#0#0#255#14#10#8#255#184#134

-  +'o'#255#184#134'o'#255'xWI'#255#2#1#1#255#0#0#0#255#18#14#11#255#184#134'o'

-  +#255#184#134'o'#255#183#131'l'#255'h:&'#240#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +'n<%'#218#189#140'x'#255#190#143'z'#255#190#143'z'#255#190#143'z'#255#190#143

-  +'z'#255#190#143'z'#255',!'#28#255#0#0#0#255#0#0#0#255#0#0#0#255#9#7#6#255#190

-  ,#143'z'#255#9#7#6#255#0#0#0#255#0#0#0#255#0#0#0#255#5#4#3#255#190#143'z'#255

-  +#190#143'z'#255#190#143'z'#255#159'xe'#255#0#0#0#255#140'jZ'#255#190#143'z'

-  +#255#190#143'z'#255#175'yc'#255'b6"'#193#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'd4'

-  +#30#151#167's\'#254#196#154#135#255#196#154#135#255#196#154#135#255#196#154

-  +#135#255#196#154#135#255'qYN'#255#0#0#0#255#0#0#0#255#1#1#1#255'v]R'#255#194

-  +#152#133#255#5#4#4#255#0#0#0#255#0#0#0#255#0#0#0#255#10#8#7#255#196#154#135

-  +#255#196#154#135#255#196#154#135#255#196#154#135#255'"'#27#24#255#196#154#135

-  +#255#196#154#135#255#196#154#135#255#131'R<'#248'^1'#28'1'#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0'[/'#27#14'o>('#224#198#157#140#255#202#163#146#255#202#163#146

-  +#255#202#163#146#255#202#163#146#255#170#138'{'#255#0#0#0#255#8#6#6#255#26#21

-  +#19#255#202#163#146#255#143'th'#255#1#1#1#255#0#0#0#255#0#0#0#255#2#2#2#255

-  +#143'sg'#255#202#163#146#255#202#163#146#255#202#163#146#255#202#163#146#255

-  +#191#155#138#255#202#163#146#255#202#163#146#255#183#135'r'#255'd5 '#193#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'd3'#30'{'#156'jS'#248#209#174

-  +#160#255#209#174#160#255#209#174#160#255#209#174#160#255#209#174#160#255#2#2

-  +#2#255#205#172#158#255#209#174#160#255#209#174#160#255#169#140#129#255#11#9#9

-  +#255#1#1#0#255#4#3#3#255#132'oe'#255#209#174#160#255#209#174#160#255#209#174

-  +#160#255#209#174#160#255#209#174#160#255#209#174#160#255#209#174#160#255#205

-  +#167#152#255'yG3'#235'[/'#27'"'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0'd4'#30#181#179#134'r'#254#215#185#173#255#215#185#173#255#215#185

-  +#173#255#215#185#173#255'm^X'#255#215#185#173#255#215#185#173#255#215#185#173

-  +#255#215#185#173#255#133'sk'#255#0#0#0#255#156#134'}'#255#215#185#173#255#215

-  +#185#173#255#215#185#173#255#215#185#173#255#215#185#173#255#215#185#173#255

-  +#215#185#173#255#214#183#172#255#143'^I'#243'b3'#29'`'#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27#8'f6 '#205#189#148#131#254

-  +#222#197#187#255#222#197#187#255#222#197#187#255#222#197#187#255#222#197#187

-  +#255#222#197#187#255#222#197#187#255#222#197#187#255'qk'#255#134'wq'#255#222

-  +#197#187#255#222#197#187#255#222#197#187#255#222#197#187#255#222#197#187#255

-  +#222#197#187#255#222#197#187#255#220#193#183#255#159'o['#247'd3'#30#147#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'['

-  +'/'#27#15'c3'#30#193#176#132'q'#248#228#206#199#255#228#208#201#255#228#208

-  +#201#255#228#208#201#255#228#208#201#255#228#208#201#255#228#208#201#255#228

-  +#208#201#255#228#208#201#255#228#208#201#255#228#208#201#255#228#208#201#255

-  +#228#208#201#255#228#208#201#255#228#208#201#255#221#196#186#255#141']I'#237

-  +'c3'#29#129#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27#2'c3'#29#150#135'XD'#231#208#176#164

-  +#255#235#219#215#255#235#219#215#255#235#219#215#255#235#219#215#255#235#219

-  +#215#255#235#219#215#255#235#219#215#255#235#219#215#255#235#219#215#255#235

-  +#219#215#255#235#219#215#255#232#214#209#255#189#149#133#253'qA+'#220'a2'#29

-  +'O'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27'"e4'#30#185#144'cO'#235

-  +#195#158#144#254#230#209#203#255#242#231#229#255#242#231#229#255#242#231#229

-  +#255#242#231#229#255#242#231#229#255#241#230#226#255#220#193#184#255#180#141

-  +'|'#251'zK6'#226'd3'#30#142'[/'#27#10#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27#28'b3'#29'{g5'#30#198'xH2'#218#137']J'

-  +#229#156'r`'#236#151'mZ'#234#132'VB'#227'q?)'#213'f4'#30#182'_1'#28'W[/'#27

-  +#11#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27#5'[/'#27#30'[/'#27#22'[/'

-  +#27#1#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#255#252'?'#255#255#192#3#255#255#0#0

-  +#255#254#0#0'?'#248#0#0#31#240#0#0#15#240#0#0#7#224#0#0#7#192#0#0#3#192#0#0#3

-  +#192#0#0#1#128#0#0#1#128#0#0#1#128#0#0#1#128#0#0#1#128#0#0#1#128#0#0#1#128#0

-  +#0#1#128#0#0#1#128#0#0#1#128#0#0#3#192#0#0#3#192#0#0#3#192#0#0#7#224#0#0#7

-  +#240#0#0#15#240#0#0#31#248#0#0'?'#252#0#0''#255#0#0#255#255#192#3#255#255

-  +#252'?'#255'('#0#0#0#16#0#0#0' '#0#0#0#1#0' '#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0';;;'#13'ICB7M=4zL'

-  +'>6xFBA;@@@'#16#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0'III'#7'W;,'#171'nC('#245#145'`+'#250#181'2'#253#175'z1'#253#139'\*'#250

-  +'h?&'#244'P=3'#159'@@@'#12#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'K921d;'''

-  +#235#191#137'7'#254#249#198'E'#255#251#199'F'#255#251#200'F'#255#251#200'F'

-  +#255#251#199'F'#255#246#194'E'#255#173'y3'#253'\9)'#229'E>;2'#0#0#0#0#0#0#0#0

-  +#0#0#0#0'>2,'#7'nC'''#238#223#171'N'#255#239#191'U'#255#240#192'U'#255#164

-  ,#131':'#255'@3'#23#255'J;'#26#255'~e-'#255#196#157'E'#255#239#191'U'#255#212

-  +#159'J'#255'a<('#231'MMM'#10#0#0#0#0#0#0#0#0'[7('#183#195#145'N'#254#228#180

-  +'_'#255#228#180'_'#255#228#180'_'#255'</'#25#255#0#0#0#255#0#0#0#255#0#0#0

-  +#255#4#3#1#255'9-'#24#255#205#163'U'#255#175'~E'#253'Q:0'#168#0#0#0#0'[/'#27

-  +#10#143'd@'#248#219#174'l'#255#219#173'l'#255#159'~N'#255#206#163'f'#255'qY7'

-  +#255#0#0#0#255#0#0#0#255#0#0#0#255#0#0#0#255#1#1#0#255#152'xK'#255#218#172'k'

-  +#255'zQ5'#246'UUU'#3'W4$H'#178#134'_'#254#213#169'x'#255'YF2'#255#0#0#0#255

-  +'L<+'#255#200#158'p'#255#8#6#4#255#0#0#0#255#0#0#0#255#18#15#10#255'pY?'#255

-  +#213#169'x'#255#213#169'x'#255#157'tR'#251'M=74^6$'#154#193#151'x'#255#179

-  +#144's'#255#6#5#4#255#27#21#17#255#155'|c'#255#206#165#132#255#130'hS'#255'='

-  +'1'''#255':/%'#255#170#137'm'#255#141'q['#255#166#133'j'#255#206#165#132#255

-  +#178#135'l'#253'Q8,{c5 '#167#195#154#133#255#163#130'q'#255#153'zj'#255#192

-  +#152#132#255#199#159#138#255#200#159#138#255#201#160#139#255#201#160#139#255

-  +#201#160#139#255#154'zj'#255#1#1#1#255#29#23#20#255#197#157#136#255#181#139

-  +'v'#255'X5%|_1'#28'V'#176#132'r'#254#199#159#141#255#199#159#141#255#184#147

-  +#130#255'#'#28#25#255#27#22#19#255#167#133'v'#255'fZ'#255#172#137'z'#255#197

-  +#157#139#255' '#26#23#255#15#12#11#255#187#150#133#255#156'tb'#251'W1 0[/'#27

-  +#30#160'zk'#246#210#176#163#255#210#176#163#255#154#129'x'#255#0#0#0#255' '

-  +#27#25#255#152#128'v'#255#4#3#3#255'>40'#255#210#176#163#255#148'|s'#255'RD?'

-  +#255#210#176#163#255#140'fW'#243'[/'#27#2#0#0#0#0'tI6'#209#216#188#178#255

-  +#221#195#185#255#213#188#178#255'$'#31#30#255#131'sn'#255'sf`'#255#0#0#0#255

-  +'eYU'#255#221#195#185#255#221#195#185#255#204#180#170#255#207#178#166#255'k>'

-  +'+'#182#0#0#0#0#0#0#0#0'[/'#27#31#156'xi'#240#232#213#207#255#232#213#207#255

-  +#180#165#161#255#232#213#207#255#182#167#162#255'ICA'#255#226#207#201#255#232

-  +#213#207#255#232#213#207#255#231#213#206#255#134'_O'#233'[/'#27#12#0#0#0#0#0

-  +#0#0#0#0#0#0#0'd3'#30'e'#171#139'~'#240#239#226#224#255#242#231#229#255#242

-  +#231#229#255#240#229#227#255#242#231#229#255#242#231#229#255#242#231#229#255

-  +#235#221#216#255#153'uh'#236'a2'#29'?'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0

-  +#0'\0'#27'*'#131'\I'#215#212#191#183#252#239#229#226#255#253#250#252#255#253

-  +#249#250#255#236#223#220#255#204#180#172#250'yM;'#196'[/'#27#23#0#0#0#0#0#0#0

-  +#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'[/'#27#3'[/'#27'>c3'#29#131'uI4'

-  +#190'oA-'#183'b2'#29'v[/'#27'4'#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#248

-  +#31#172'A'#224#7#172'A'#192#3#172'A'#128#1#172'A'#128#1#172'A'#0#0#172'A'#0#0

-  +#172'A'#0#0#172'A'#0#0#172'A'#0#0#172'A'#0#0#172'A'#128#1#172'A'#128#1#172'A'

-  +#192#3#172'A'#224#7#172'A'#240#31#172'A'

-]);

-

diff --git a/examples/cross_calculator/lazarus/nimlaz.rc b/examples/cross_calculator/lazarus/nimlaz.rc
deleted file mode 100644
index d66bb817c..000000000
--- a/examples/cross_calculator/lazarus/nimlaz.rc
+++ /dev/null
@@ -1,6 +0,0 @@
-#define RT_MANIFEST  24

-#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1

-#define ISOLATIONAWARE_MANIFEST_RESOURCE_ID 2

-#define ISOLATIONAWARE_NOSTATICIMPORT_MANIFEST_RESOURCE_ID 3

-

-CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "nimlaz.manifest"
diff --git a/examples/cross_calculator/lazarus/readme.txt b/examples/cross_calculator/lazarus/readme.txt
deleted file mode 100644
index c704d53fd..000000000
--- a/examples/cross_calculator/lazarus/readme.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This example demonstrates how to use Nim with Lazarus. The GUI is generated
-with Lazarus, while the "backend" is written in Nim. To compile the example,
-use this command:
-
-  nim c --app:gui --no_main --no_linking backend.nim
-
-Open the ``nimlaz.lpi`` file in Lazarus and run the program.
-
diff --git a/examples/cross_calculator/lazarus/unit1.lfm b/examples/cross_calculator/lazarus/unit1.lfm
deleted file mode 100644
index bf60ff715..000000000
--- a/examples/cross_calculator/lazarus/unit1.lfm
+++ /dev/null
@@ -1,46 +0,0 @@
-object Form1: TForm1
-  Left = 553
-  Height = 111
-  Top = 464
-  Width = 448
-  ActiveControl = SpinEdit1
-  Caption = 'Sum'
-  ClientHeight = 111
-  ClientWidth = 448
-  OnCreate = FormCreate
-  LCLVersion = '0.9.28.2'
-  object Label1: TLabel
-    Left = 8
-    Height = 18
-    Top = 72
-    Width = 34
-    Caption = 'Sum:'
-    ParentColor = False
-  end
-  object SpinEdit1: TSpinEdit
-    Left = 8
-    Height = 27
-    Top = 8
-    Width = 436
-    Anchors = [akTop, akLeft, akRight]
-    OnChange = SpinEdit1Change
-    TabOrder = 0
-  end
-  object SpinEdit2: TSpinEdit
-    Left = 8
-    Height = 27
-    Top = 40
-    Width = 436
-    Anchors = [akTop, akLeft, akRight]
-    OnChange = SpinEdit1Change
-    TabOrder = 1
-  end
-  object Edit1: TEdit
-    Left = 48
-    Height = 27
-    Top = 72
-    Width = 396
-    Anchors = [akTop, akLeft, akRight]
-    TabOrder = 2
-  end
-end
diff --git a/examples/cross_calculator/lazarus/unit1.pas b/examples/cross_calculator/lazarus/unit1.pas
deleted file mode 100644
index 6091a61d3..000000000
--- a/examples/cross_calculator/lazarus/unit1.pas
+++ /dev/null
@@ -1,58 +0,0 @@
-unit Unit1; 
-
-{$mode objfpc}{$H+}
-
-interface
-
-uses
-  Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
-  Spin, StdCtrls;
-
-type
-
-  { TForm1 }
-
-  TForm1 = class(TForm)
-    Edit1: TEdit;
-    Label1: TLabel;
-    SpinEdit1: TSpinEdit;
-    SpinEdit2: TSpinEdit;
-    procedure FormCreate(Sender: TObject);
-    procedure SpinEdit1Change(Sender: TObject);
-  private
-    { private declarations }
-  public
-    { public declarations }
-  end; 
-
-var
-  Form1: TForm1; 
-
-implementation
-
-{ TForm1 }
-
-{$link nimcache/lib/system.o}
-{$link nimcache/backend.o}
-{$link nimcache/nim__dat.o}
-{$linklib c}
-
-procedure NimMain; cdecl; external;
-function myAdd(x, y: longint): longint; cdecl; external;
-
-procedure TForm1.FormCreate(Sender: TObject);
-begin
-  // we initialize the Nim data structures here:
-  NimMain();
-end;
-
-procedure TForm1.SpinEdit1Change(Sender: TObject);
-begin
-  Edit1.text := IntToStr(myAdd(SpinEdit1.Value, SpinEdit2.Value));
-end;
-
-initialization
-  {$I unit1.lrs}
-
-end.
-
diff --git a/examples/cross_calculator/nim_backend/backend.nim b/examples/cross_calculator/nim_backend/backend.nim
deleted file mode 100644
index c8684581c..000000000
--- a/examples/cross_calculator/nim_backend/backend.nim
+++ /dev/null
@@ -1,5 +0,0 @@
-# Backend for the different user interfaces.
-
-proc myAdd*(x, y: int): int {.cdecl, exportc.} =
-  result = x + y
-
diff --git a/examples/cross_calculator/nim_commandline/nim.cfg b/examples/cross_calculator/nim_commandline/nim.cfg
deleted file mode 100644
index 6f0cb4a01..000000000
--- a/examples/cross_calculator/nim_commandline/nim.cfg
+++ /dev/null
@@ -1,4 +0,0 @@
-# Nim configuration file.
-# The file is used only to add the path of the backend to the compiler options.
-
-path="../nim_backend"
diff --git a/examples/cross_calculator/nim_commandline/nimcalculator.nim b/examples/cross_calculator/nim_commandline/nimcalculator.nim
deleted file mode 100644
index 3f7674dbb..000000000
--- a/examples/cross_calculator/nim_commandline/nimcalculator.nim
+++ /dev/null
@@ -1,109 +0,0 @@
-# Implements a command line interface against the backend.
-
-import backend, parseopt, strutils
-
-const
-  USAGE = """nimcalculator - Nim cross platform calculator
-  (beta version, only integer addition is supported!)
-
-Usage:
-  nimcalculator [options] [-a=value -b=value]
-Options:
-  -a=value    sets the integer value of the a parameter
-  -b=value    sets the integer value of the b parameter
-  -h, --help  shows this help
-
-If no options are used, an interactive mode is entered.
-"""
-
-type
-  TCommand = enum       # The possible types of operation
-    cmdParams,          # Two valid parameters were provided
-    cmdInteractive      # No parameters were provided, run interactive mode
-
-  TParamConfig = object of RootObj
-    action: TCommand      # store the type of operation
-    paramA, paramB: int   # possibly store the valid parameters
-
-
-proc parseCmdLine(): TParamConfig =
-  ## Parses the commandline.
-  ##
-  ## Returns a TParamConfig structure filled with the proper values or directly
-  ## calls quit() with the appropriate error message.
-  var
-    hasA = false
-    hasB = false
-    p = initOptParser()
-    key, val: TaintedString
-
-  result.action = cmdInteractive # By default presume interactive mode.
-  try:
-    while true:
-      next p
-      key = p.key
-      val = p.val
-
-      case p.kind
-      of cmdArgument:
-        stdout.write USAGE
-        quit "Erroneous argument detected: " & key, 1
-      of cmdLongOption, cmdShortOption:
-        case key.normalize
-        of "help", "h":
-          stdout.write USAGE
-          quit 0
-        of "a":
-          result.paramA = val.parseInt
-          hasA = true
-        of "b":
-          result.paramB = val.parseInt
-          hasB = true
-        else:
-          stdout.write USAGE
-          quit "Unexpected option: " & key, 2
-      of cmdEnd: break
-  except ValueError:
-    stdout.write USAGE
-    quit "Invalid value " & val &  " for parameter " & key, 3
-
-  if hasA and hasB:
-    result.action = cmdParams
-  elif hasA or hasB:
-    stdout.write USAGE
-    quit "Error: provide both A and B to operate in param mode", 4
-
-
-proc parseUserInput(question: string): int =
-  ## Parses a line of user input, showing question to the user first.
-  ##
-  ## If the user input is an empty line quit() is called. Returns the value
-  ## parsed as an integer.
-  while true:
-    echo question
-    let input = stdin.readLine
-    try:
-      result = input.parseInt
-      break
-    except ValueError:
-      if input.len < 1: quit "Blank line detected, quitting.", 0
-      echo "Sorry, `$1' doesn't seem to be a valid integer" % input
-
-proc interactiveMode() =
-  ## Asks the user for two integer values, adds them and exits.
-  let
-    paramA = parseUserInput("Enter the first parameter (blank to exit):")
-    paramB = parseUserInput("Enter the second parameter (blank to exit):")
-  echo "Calculating... $1 + $2 = $3" % [$paramA, $paramB,
-    $backend.myAdd(paramA, paramB)]
-
-
-when isMainModule:
-  ## Main entry point.
-  let opt = parseCmdLine()
-  if cmdParams == opt.action:
-    echo "Param mode: $1 + $2 = $3" % [$opt.paramA, $opt.paramB,
-      $backend.myAdd(opt.paramA, opt.paramB)]
-  else:
-    echo "Entering interactive addition mode"
-    interactiveMode()
diff --git a/examples/cross_calculator/nim_commandline/readme.txt b/examples/cross_calculator/nim_commandline/readme.txt
deleted file mode 100644
index f95bd962e..000000000
--- a/examples/cross_calculator/nim_commandline/readme.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-In this directory you will find the nim commandline version of the
-cross-calculator sample.
-
-The commandline interface can be used non interactively through switches, or
-interactively when running the command without parameters.
-
-Compilation is fairly easy despite having the source split in different
-directories. Thanks to the nim.cfg file, which adds the ../nim_backend
-directory as a search path, you can compile and run the example just fine from
-the command line with 'nim c -r nimcalculator.nim'.
diff --git a/examples/cross_calculator/readme.txt b/examples/cross_calculator/readme.txt
deleted file mode 100644
index 72e4130eb..000000000
--- a/examples/cross_calculator/readme.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-The cross platform calculator illustrates how to use Nim to create a backend
-called by different native user interfaces.
-
-Since the purpose of the example is to show how the cross platform code
-interacts with Nim the actual backend code is just a simple addition proc.
-By keeping your program logic in Nim you can easily reuse it in different
-platforms.
-
-To avoid duplication of code, the backend code lies in a separate directory and
-each platform compiles it with a different custom build process, usually
-generating C code in a temporary build directory.
-
-For a more elaborate and useful example see the cross_todo example.
diff --git a/examples/cross_todo/nim_backend/backend.nim b/examples/cross_todo/nim_backend/backend.nim
deleted file mode 100644
index 513fe304f..000000000
--- a/examples/cross_todo/nim_backend/backend.nim
+++ /dev/null
@@ -1,195 +0,0 @@
-# Backend for a simple todo program with sqlite persistence.
-#
-# Most procs dealing with a DbConn object may raise an EDb exception.
-
-import db_sqlite, parseutils, strutils, times
-
-type
-  Todo* = object
-    ## A todo object holding the information serialized to the database.
-    id: int64                 ## Unique identifier of the object in the
-                              ## database, use the getId() accessor to read it.
-    text*: string             ## Description of the task to do.
-    priority*: int            ## The priority can be any user defined integer.
-    isDone*: bool             ## Done todos are still kept marked.
-    modificationDate: Time    ## The modification time can't be modified from
-                              ## outside of this module, use the
-                              ## getModificationDate accessor.
-
-  PagedParams* = object
-    ## Contains parameters for a query, initialize default values with
-    ## initDefaults().
-    pageSize*: int64          ## Lines per returned query page, -1 for
-                              ## unlimited.
-    priorityAscending*: bool  ## Sort results by ascending priority.
-    dateAscending*: bool      ## Sort results by ascending modification date.
-    showUnchecked*: bool      ## Get unchecked objects.
-    showChecked*: bool        ## Get checked objects.
-
-# - General procs
-
-proc initDefaults*(params: var PagedParams) =
-  ## Sets sane defaults for a PagedParams object.
-  ##
-  ## Note that you should always provide a non zero pageSize, either a specific
-  ## positive value or negative for unbounded query results.
-  params.pageSize = high(int64)
-  params.priorityAscending = false
-  params.dateAscending = false
-  params.showUnchecked = true
-  params.showChecked = false
-
-proc openDatabase*(path: string): DbConn =
-  ## Creates or opens the sqlite3 database.
-  ##
-  ## Pass the path to the sqlite database, if the database doesn't exist it
-  ## will be created. The proc may raise a EDB exception
-  let
-    conn = db_sqlite.open(path, "user", "pass", "db")
-    query = sql"""CREATE TABLE IF NOT EXISTS Todos (
-      id INTEGER PRIMARY KEY,
-      priority INTEGER NOT NULL,
-      is_done BOOLEAN NOT NULL,
-      desc TEXT NOT NULL,
-      modification_date INTEGER NOT NULL,
-      CONSTRAINT Todos UNIQUE (id))"""
-  db_sqlite.exec(conn, query)
-  result = conn
-
-# - Procs related to Todo objects
-
-proc initFromDB(id: int64; text: string; priority: int, isDone: bool;
-               modificationDate: Time): Todo =
-  ## Returns an initialized Todo object created from database parameters.
-  ##
-  ## The proc assumes all values are right. Note this proc is NOT exported.
-  assert(id >= 0, "Identity identifiers should not be negative")
-  result.id = id
-  result.text = text
-  result.priority = priority
-  result.isDone = isDone
-  result.modificationDate = modificationDate
-
-proc getId*(todo: Todo): int64 =
-  ## Accessor returning the value of the private id property.
-  return todo.id
-
-proc getModificationDate*(todo: Todo): Time =
-  ## Returns the last modification date of a Todo entry.
-  return todo.modificationDate
-
-proc update*(todo: var Todo; conn: DbConn): bool =
-  ## Checks the database for the object and refreshes its variables.
-  ##
-  ## Use this method if you (or another entity) have modified the database and
-  ## want to update the object you have with whatever the database has stored.
-  ## Returns true if the update succeeded, or false if the object was not found
-  ## in the database any more, in which case you should probably get rid of the
-  ## Todo object.
-  assert(todo.id >= 0, "The identifier of the todo entry can't be negative")
-  let query = sql"""SELECT desc, priority, is_done, modification_date
-    FROM Todos WHERE id = ?"""
-  try:
-    let rows = conn.getAllRows(query, $todo.id)
-    if len(rows) < 1:
-      return
-    assert(1 == len(rows), "Woah, didn't expect so many rows")
-    todo.text = rows[0][0]
-    todo.priority = rows[0][1].parseInt
-    todo.isDone = rows[0][2].parseBool
-    todo.modificationDate = Time(rows[0][3].parseInt)
-    result = true
-  except:
-    echo("Something went wrong selecting for id " & $todo.id)
-
-proc save*(todo: var Todo; conn: DbConn): bool =
-  ## Saves the current state of text, priority and isDone to the database.
-  ##
-  ## Returns true if the database object was updated (in which case the
-  ## modification date will have changed). The proc can return false if the
-  ## object wasn't found, for instance, in which case you should drop that
-  ## object anyway and create a new one with addTodo(). Also EDb can be raised.
-  assert(todo.id >= 0, "The identifier of the todo entry can't be negative")
-  let
-    currentDate = getTime()
-    query = sql"""UPDATE Todos
-      SET desc = ?, priority = ?, is_done = ?, modification_date = ?
-      WHERE id = ?"""
-    rowsUpdated = conn.execAffectedRows(query, $todo.text,
-      $todo.priority, $todo.isDone, $int(currentDate), $todo.id)
-  if 1 == rowsUpdated:
-    todo.modificationDate = currentDate
-    result = true
-
-# - Procs dealing directly with the database
-
-proc addTodo*(conn: DbConn; priority: int; text: string): Todo =
-  ## Inserts a new todo into the database.
-  ##
-  ## Returns the generated todo object. If there is an error EDb will be raised.
-  let
-    currentDate = getTime()
-    query = sql"""INSERT INTO Todos
-      (priority, is_done, desc, modification_date)
-      VALUES (?, 'false', ?, ?)"""
-    todoId = conn.insertId(query, priority, text, $int(currentDate))
-  result = initFromDB(todoId, text, priority, false, currentDate)
-
-proc deleteTodo*(conn: DbConn; todoId: int64): int64 {.discardable.} =
-  ## Deletes the specified todo identifier.
-  ##
-  ## Returns the number of rows which were affected (1 or 0)
-  let query = sql"""DELETE FROM Todos WHERE id = ?"""
-  result = conn.execAffectedRows(query, $todoId)
-
-proc getNumEntries*(conn: DbConn): int =
-  ## Returns the number of entries in the Todos table.
-  ##
-  ## If the function succeeds, returns the zero or positive value, if something
-  ## goes wrong a negative value is returned.
-  let query = sql"""SELECT COUNT(id) FROM Todos"""
-  try:
-    let row = conn.getRow(query)
-    result = row[0].parseInt
-  except:
-    echo("Something went wrong retrieving number of Todos entries")
-    result = -1
-
-proc getPagedTodos*(conn: DbConn; params: PagedParams; page = 0'i64): seq[Todo] =
-  ## Returns the todo entries for a specific page.
-  ##
-  ## Pages are calculated based on the params.pageSize parameter, which can be
-  ## set to a negative value to specify no limit at all.  The query will be
-  ## affected by the PagedParams, which should have sane values (call
-  ## initDefaults).
-  assert(page >= 0, "You should request a page zero or bigger than zero")
-  result = @[]
-  # Well, if you don't want to see anything, there's no point in asking the db.
-  if not params.showUnchecked and not params.showChecked: return
-  let
-    order_by = [
-      if params.priorityAscending: "ASC" else: "DESC",
-      if params.dateAscending: "ASC" else: "DESC"]
-    query = sql("""SELECT id, desc, priority, is_done, modification_date
-      FROM Todos
-      WHERE is_done = ? OR is_done = ?
-      ORDER BY priority $1, modification_date $2, id DESC
-      LIMIT ? * ?,?""" % order_by)
-    args = @[$params.showChecked, $(not params.showUnchecked),
-      $params.pageSize, $page, $params.pageSize]
-  #echo("Query " & string(query))
-  #echo("args: " & args.join(", "))
-  var newId: BiggestInt
-  for row in conn.fastRows(query, args):
-    let numChars = row[0].parseBiggestInt(newId)
-    assert(numChars > 0, "Huh, couldn't parse identifier from database?")
-    result.add(initFromDB(int64(newId), row[1], row[2].parseInt,
-        row[3].parseBool, Time(row[4].parseInt)))
-
-proc getTodo*(conn: DbConn; todoId: int64): ref Todo =
-  ## Returns a reference to a Todo or nil if the todo could not be found.
-  var tempTodo: Todo
-  tempTodo.id = todoId
-  if tempTodo.update(conn):
-    new(result)
-    result[] = tempTodo
diff --git a/examples/cross_todo/nim_backend/readme.txt b/examples/cross_todo/nim_backend/readme.txt
deleted file mode 100644
index 4b31408e3..000000000
--- a/examples/cross_todo/nim_backend/readme.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-This directory contains the nim backend code for the todo cross platform
-example.
-
-Unlike the cross platform calculator example, this backend features more code,
-using an sqlite database for storage. Also a basic test module is provided, not
-to be included with the final program but to test the exported functionality.
-The test is not embedded directly in the backend.nim file to avoid being able
-to access internal data types and procs not exported and replicate the
-environment of client code.
-
-In a bigger project with several people you could run `nim doc backend.nim`
-(or use the doc2 command for a whole project) and provide the generated html
-documentation to another programer for her to implement an interface without
-having to look at the source code.
diff --git a/examples/cross_todo/nim_backend/testbackend.nim b/examples/cross_todo/nim_backend/testbackend.nim
deleted file mode 100644
index 4a71d5f2c..000000000
--- a/examples/cross_todo/nim_backend/testbackend.nim
+++ /dev/null
@@ -1,74 +0,0 @@
-# Tests the backend code.
-
-import backend, db_sqlite, strutils, times
-
-proc showPagedResults(conn: DbConn; params: PagedParams) =
-  ## Shows the contents of the database in pages of specified size.
-  ##
-  ## Hmm... I guess this is more of a debug proc which should be moved outside,
-  ## or to a commandline interface (hint).
-  var
-    page = 0'i64
-    rows = conn.getPagedTodos(params)
-  while rows.len > 0:
-    echo("page " & $page)
-    for row in rows:
-      echo("row id:$1, text:$2, priority:$3, done:$4, date:$5" % [$row.getId,
-        $row.text, $row.priority, $row.isDone,
-        $row.getModificationDate])
-    # Query the database for the next page or quit.
-    if params.pageSize > 0:
-      page = page + 1
-      rows = conn.getPagedTodos(params, page)
-    else:
-      break
-
-proc dumTest() =
-  let conn = openDatabase("todo.sqlite3")
-  try:
-    let numTodos = conn.getNumEntries
-    echo("Current database contains " & $numTodos & " todo items.")
-    if numTodos < 10:
-      # Fill some dummy rows if there are not many entries yet.
-      discard conn.addTodo(3, "Filler1")
-      discard conn.addTodo(4, "Filler2")
-    var todo = conn.addTodo(2, "Testing")
-    echo("New todo added with id " & $todo.getId)
-    # Try changing it and updating the database.
-    var clonedTodo = conn.getTodo(todo.getId)[]
-    assert(clonedTodo.text == todo.text, "Should be equal")
-    todo.text = "Updated!"
-    todo.priority = 7
-    todo.isDone = true
-    if todo.save(conn):
-      echo("Updated priority $1, done $2" % [$todo.priority, $todo.isDone])
-    else:
-      assert(false, "Uh oh, I wasn't expecting that!")
-    # Verify our cloned copy is different but can be updated.
-    assert(clonedTodo.text != todo.text, "Should be different")
-    discard clonedTodo.update(conn)
-    assert(clonedTodo.text == todo.text, "Should be equal")
-    var params: PagedParams
-    params.initDefaults
-    conn.showPagedResults(params)
-    conn.deleteTodo(todo.getId)
-    echo("Deleted rows for id 3? ")
-    let res = conn.deleteTodo(todo.getId)
-    echo("Deleted rows for id 3? " & $res)
-    if todo.update(conn):
-      echo("Later priority $1, done $2" % [$todo.priority, $todo.isDone])
-    else:
-      echo("Can't update object $1 from db!" % $todo.getId)
-    # Try to list content in a different way.
-    params.pageSize = 5
-    params.priorityAscending = true
-    params.dateAscending = true
-    params.showChecked = true
-    conn.showPagedResults(params)
-  finally:
-    conn.close
-    echo("Database closed")
-
-# Code that will be run only on the commandline.
-when isMainModule:
-  dumTest()
diff --git a/examples/cross_todo/nim_commandline/nim.cfg b/examples/cross_todo/nim_commandline/nim.cfg
deleted file mode 100644
index 6f0cb4a01..000000000
--- a/examples/cross_todo/nim_commandline/nim.cfg
+++ /dev/null
@@ -1,4 +0,0 @@
-# Nim configuration file.
-# The file is used only to add the path of the backend to the compiler options.
-
-path="../nim_backend"
diff --git a/examples/cross_todo/nim_commandline/nimtodo.nim b/examples/cross_todo/nim_commandline/nimtodo.nim
deleted file mode 100644
index be5b3407b..000000000
--- a/examples/cross_todo/nim_commandline/nimtodo.nim
+++ /dev/null
@@ -1,297 +0,0 @@
-# Implements a command line interface against the backend.
-
-import backend, db_sqlite, os, parseopt, parseutils, strutils, times
-
-const
-  USAGE = """nimtodo - Nim cross platform todo manager
-
-Usage:
-  nimtodo [command] [list options]
-
-Commands:
-  -a=int text Adds a todo entry with the specified priority and text.
-  -c=int      Marks the specified todo entry as done.
-  -u=int      Marks the specified todo entry as not done.
-  -d=int|all  Deletes a single entry from the database, or all entries.
-  -g          Generates some rows with values for testing.
-  -l          Lists the contents of the database.
-  -h, --help  shows this help
-
-List options (optional):
-  -p=+|-      Sorts list by ascending|descending priority. Default:descending.
-  -m=+|-      Sorts list by ascending|descending date. Default:descending.
-  -t          Show checked entries. By default they are not shown.
-  -z          Hide unchecked entries. By default they are shown.
-
-Examples:
-  nimtodo -a=4 Water the plants
-  nimtodo -c:87
-  nimtodo -d:2
-  nimtodo -d:all
-  nimtodo -l -p=+ -m=- -t
-
-"""
-
-type
-  Command = enum     # The possible types of commands
-    cmdAdd            # The user wants to add a new todo entry.
-    cmdCheck          # User wants to check a todo entry.
-    cmdUncheck        # User wants to uncheck a todo entry.
-    cmdDelete         # User wants to delete a single todo entry.
-    cmdNuke           # User wants to purge all database entries.
-    cmdGenerate       # Add random rows to the database, for testing.
-    cmdList           # User wants to list contents.
-
-  ParamConfig = object
-    # Structure containing the parsed options from the commandline.
-    command: Command         # Store the type of operation
-    addPriority: int          # Only valid with cmdAdd, stores priority.
-    addText: seq[string]      # Only valid with cmdAdd, stores todo text.
-    todoId: int64             # The todo id for operations like check or delete.
-    listParams: PagedParams  # Uses the backend structure directly for params.
-
-proc initDefaults(params: var ParamConfig) =
-  ## Initialises defaults value in the structure.
-  ##
-  ## Most importantly we want to have an empty list for addText.
-  params.listParams.initDefaults
-  params.addText = @[]
-
-proc abort(message: string, value: int) =
-  # Simple wrapper to abort also displaying the help to the user.
-  stdout.write(USAGE)
-  quit(message, value)
-
-template parseTodoIdAndSetCommand(newCommand: Command): untyped =
-  ## Helper to parse a big todo identifier into todoId and set command.
-  try:
-    let numChars = val.parseBiggestInt(newId)
-    if numChars < 1: raise newException(ValueError, "Empty string?")
-    result.command = newCommand
-    result.todoId = newId
-  except OverflowError:
-    raise newException(ValueError, "Value $1 too big" % val)
-
-template verifySingleCommand(actions: typed): typed =
-  ## Helper to make sure only one command has been specified so far.
-  if specifiedCommand:
-    abort("Only one command can be specified at a time! (extra:$1)" % [key], 2)
-  else:
-    actions
-    specifiedCommand = true
-
-proc parsePlusMinus(val: string, debugText: string): bool =
-  ## Helper to process a plus or minus character from the commandline.
-  ##
-  ## Pass the string to parse and the type of parameter for debug errors.
-  ## The processed parameter will be returned as true for a '+' and false for a
-  ## '-'. The proc aborts with a debug message if the passed parameter doesn't
-  ## contain one of those values.
-  case val
-  of "+":
-    return true
-  of "-":
-    return false
-  else:
-    abort("$1 parameter should be + or - but was '$2'." % [debugText, val], 4)
-
-proc parseCmdLine(): ParamConfig =
-  ## Parses the commandline.
-  ##
-  ## Returns a ParamConfig structure filled with the proper values or directly
-  ## calls quit() with the appropriate error message.
-  var
-    specifiedCommand = false
-    usesListParams = false
-    p = initOptParser()
-    key, val: TaintedString
-    newId: BiggestInt
-  result.initDefaults
-  try:
-    while true:
-      next(p)
-      key = p.key
-      val = p.val
-      case p.kind
-      of cmdArgument:
-        if specifiedCommand and cmdAdd == result.command:
-          result.addText.add(key)
-        else:
-          abort("Argument ($1) detected without add command." % [key], 1)
-      of cmdLongOption, cmdShortOption:
-        case normalize(key)
-        of "help", "h":
-          stdout.write(USAGE)
-          quit(0)
-        of "a":
-          verifySingleCommand:
-            result.command = cmdAdd
-            result.addPriority = val.parseInt
-        of "c":
-          verifySingleCommand:
-            parseTodoIdAndSetCommand(cmdCheck)
-        of "u":
-          verifySingleCommand:
-            parseTodoIdAndSetCommand cmdUncheck
-        of "d":
-          verifySingleCommand:
-            if "all" == val:
-              result.command = cmdNuke
-            else:
-              parseTodoIdAndSetCommand cmdDelete
-        of "g":
-          verifySingleCommand:
-            if val.len > 0:
-              abort("Unexpected value '$1' for switch l." % [val], 3)
-            result.command = cmdGenerate
-        of "l":
-          verifySingleCommand:
-            if val.len > 0:
-              abort("Unexpected value '$1' for switch l." % [val], 3)
-            result.command = cmdList
-        of "p":
-          usesListParams = true
-          result.listParams.priorityAscending = parsePlusMinus(val, "Priority")
-        of "m":
-          usesListParams = true
-          result.listParams.dateAscending = parsePlusMinus(val, "Date")
-        of "t":
-          usesListParams = true
-          if val.len > 0:
-            abort("Unexpected value '$1' for switch t." % [val], 5)
-          result.listParams.showChecked = true
-        of "z":
-          usesListParams = true
-          if val.len > 0:
-            abort("Unexpected value '$1' for switch z." % [val], 5)
-          result.listParams.showUnchecked = false
-        else:
-          abort("Unexpected option '$1'." % [key], 6)
-      of cmdEnd:
-        break
-  except ValueError:
-    abort("Invalid integer value '$1' for parameter '$2'." % [val, key], 7)
-  if not specifiedCommand:
-    abort("Didn't specify any command.", 8)
-  if cmdAdd == result.command and result.addText.len < 1:
-    abort("Used the add command, but provided no text/description.", 9)
-  if usesListParams and cmdList != result.command:
-    abort("Used list options, but didn't specify the list command.", 10)
-
-proc generateDatabaseRows(conn: DbConn) =
-  ## Adds some rows to the database ignoring errors.
-  discard conn.addTodo(1, "Watch another random youtube video")
-  discard conn.addTodo(2, "Train some starcraft moves for the league")
-  discard conn.addTodo(3, "Spread the word about Nim")
-  discard conn.addTodo(4, "Give fruit superavit to neighbours")
-  var todo = conn.addTodo(4, "Send tax form through snail mail")
-  todo.isDone = true
-  discard todo.save(conn)
-  discard conn.addTodo(1, "Download new anime to watch")
-  todo = conn.addTodo(2, "Build train model from scraps")
-  todo.isDone = true
-  discard todo.save(conn)
-  discard conn.addTodo(5, "Buy latest Britney Spears album")
-  discard conn.addTodo(6, "Learn a functional programming language")
-  echo("Generated some entries, they were added to your database.")
-
-proc listDatabaseContents(conn: DbConn; listParams: PagedParams) =
-  ## Dumps the database contents formatted to the standard output.
-  ##
-  ## Pass the list/filter parameters parsed from the commandline.
-  var params = listParams
-  params.pageSize = -1
-  let todos = conn.getPagedTodos(params)
-  if todos.len < 1:
-    echo("Database empty")
-    return
-  echo("Todo id, is done, priority, last modification date, text:")
-  # First detect how long should be our columns for formatting.
-  var cols: array[0..2, int]
-  for todo in todos:
-    cols[0] = max(cols[0], ($todo.getId).len)
-    cols[1] = max(cols[1], ($todo.priority).len)
-    cols[2] = max(cols[2], ($todo.getModificationDate).len)
-  # Now dump all the rows using the calculated alignment sizes.
-  for todo in todos:
-    echo("$1 $2 $3, $4, $5" % [
-      ($todo.getId).align(cols[0]),
-      if todo.isDone: "[X]" else: "[-]",
-      ($todo.priority).align(cols[1]),
-      ($todo.getModificationDate).align(cols[2]),
-      todo.text])
-
-proc deleteOneTodo(conn: DbConn; todoId: int64) =
-  ## Deletes a single todo entry from the database.
-  let numDeleted = conn.deleteTodo(todoId)
-  if numDeleted > 0:
-    echo("Deleted todo id " & $todoId)
-  else:
-    quit("Couldn't delete todo id " & $todoId, 11)
-
-proc deleteAllTodos(conn: DbConn) =
-  ## Deletes all the contents from the database.
-  ##
-  ## Note that it would be more optimal to issue a direct DELETE sql statement
-  ## on the database, but for the sake of the example we will restrict
-  ## ourselfves to the API exported by backend.
-  var
-    counter: int64
-    params: PagedParams
-  params.initDefaults
-  params.pageSize = -1
-  params.showUnchecked = true
-  params.showChecked = true
-  let todos = conn.getPagedTodos(params)
-  for todo in todos:
-    if conn.deleteTodo(todo.getId) > 0:
-      counter += 1
-    else:
-      quit("Couldn't delete todo id " & $todo.getId, 12)
-  echo("Deleted $1 todo entries from database." % $counter)
-
-proc setTodoCheck(conn: DbConn; todoId: int64; value: bool) =
-  ## Changes the check state of a todo entry to the specified value.
-  let
-    newState = if value: "checked" else: "unchecked"
-    todo = conn.getTodo(todoId)
-  if todo == nil:
-    quit("Can't modify todo id $1, its not in the database." % $todoId, 13)
-  if todo[].isDone == value:
-    echo("Todo id $1 was already set to $2." % [$todoId, newState])
-    return
-  todo[].isDone = value
-  if todo[].save(conn):
-    echo("Todo id $1 set to $2." % [$todoId, newState])
-  else:
-    quit("Error updating todo id $1 to $2." % [$todoId, newState])
-
-proc addTodo(conn: DbConn; priority: int; tokens: seq[string]) =
-  ## Adds to the database a todo with the specified priority.
-  ##
-  ## The tokens are joined as a single string using the space character. The
-  ## created id will be displayed to the user.
-  let todo = conn.addTodo(priority, tokens.join(" "))
-  echo("Created todo entry with id:$1 for priority $2 and text '$3'." % [
-    $todo.getId, $todo.priority, todo.text])
-
-when isMainModule:
-  ## Main entry point.
-  let
-    opt = parseCmdLine()
-    dbPath = getConfigDir() / "nimtodo.sqlite3"
-  if not dbPath.existsFile:
-    createDir(getConfigDir())
-    echo("No database found at $1, it will be created for you." % dbPath)
-  let conn = openDatabase(dbPath)
-  try:
-    case opt.command
-    of cmdAdd: addTodo(conn, opt.addPriority, opt.addText)
-    of cmdCheck: setTodoCheck(conn, opt.todoId, true)
-    of cmdUncheck: setTodoCheck(conn, opt.todoId, false)
-    of cmdDelete: deleteOneTodo(conn, opt.todoId)
-    of cmdNuke: deleteAllTodos(conn)
-    of cmdGenerate: generateDatabaseRows(conn)
-    of cmdList: listDatabaseContents(conn, opt.listParams)
-  finally:
-    conn.close
diff --git a/examples/cross_todo/nim_commandline/readme.txt b/examples/cross_todo/nim_commandline/readme.txt
deleted file mode 100644
index 7d68bbc8b..000000000
--- a/examples/cross_todo/nim_commandline/readme.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This directory contains the Nim commandline version of the todo cross
-platform example.
-
-The commandline interface can be used only through switches, running the binary
-once will spit out the basic help. The commands you can use are the typical on
-such an application: add, check/uncheck and delete (further could be added,
-like modification at expense of parsing/option complexity). The list command is
-the only one which dumps the contents of the database. The output can be
-filtered and sorted through additional parameters.
-
-When you run the program for the first time the todo database will be generated
-in your user's data directory. To cope with an empty database, a special
-generation switch can be used to fill the database with some basic todo entries
-you can play with.
-
-Compilation is fairly easy despite having the source split in different
-directories. Thanks to the nim.cfg file, which adds the ../Nim_backend
-directory as a search path, you can compile and run the example just fine from
-the command line with 'nim c -r nimtodo.nim'.
diff --git a/examples/cross_todo/readme.txt b/examples/cross_todo/readme.txt
deleted file mode 100644
index 44e8c47aa..000000000
--- a/examples/cross_todo/readme.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This cross platform todo illustrates how to use Nim to create a backend
-called by different native user interfaces.
-
-This example builds on the knowledge learned from the cross_calculator example.
-Check it out first to learn how to set up Nim on different platforms.
diff --git a/examples/debugging.nim b/examples/debugging.nim
deleted file mode 100644
index 89cdd3b2a..000000000
--- a/examples/debugging.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-# Simple program to test the debugger
-# compile with --debugger:on
-
-proc someComp(x, y: int): int =
-  let a = x+y
-  if a > 7:
-    let b = a*90
-    {.breakpoint.}
-    result = b
-  {.breakpoint.}
-
-proc pp() =
-  var aa = 45
-  var bb = "abcdef"
-  echo someComp(23, 45)
-
-pp()
diff --git a/examples/filterex.nim b/examples/filterex.nim
deleted file mode 100644
index 083945254..000000000
--- a/examples/filterex.nim
+++ /dev/null
@@ -1,23 +0,0 @@
-#? stdtmpl | standard
-#proc generateHTMLPage(title, currentTab, content: string,
-#                      tabs: openArray[string]): string =
-#  result = ""
-<head><title>$title</title></head>
-<body>
-  <div id="menu">
-    <ul>
-  #for tab in items(tabs):
-    #if currentTab == tab:
-    <li><a id="selected"
-    #else:
-    <li><a
-    #end if
-    href="${tab}.html" title = "$title - $tab">$tab</a></li>
-  #end for
-    </ul>
-  </div>
-  <div id="content">
-    $content
-    A dollar: $$.
-  </div>
-</body>
diff --git a/examples/fizzbuzz.nim b/examples/fizzbuzz.nim
deleted file mode 100644
index 4b203512c..000000000
--- a/examples/fizzbuzz.nim
+++ /dev/null
@@ -1,14 +0,0 @@
-# Fizz Buzz program
-
-const f = "Fizz"
-const b = "Buzz"
-for i in 1..100:
-  if i mod 15 == 0:
-    echo f, b
-  elif i mod 5 == 0:
-    echo b
-  elif i mod 3 == 0:
-    echo f
-  else:
-    echo i
-
diff --git a/examples/hallo.nim b/examples/hallo.nim
deleted file mode 100644
index d94da8c1f..000000000
--- a/examples/hallo.nim
+++ /dev/null
@@ -1,3 +0,0 @@
-# Hello world program
-
-echo "Hello World"
diff --git a/examples/htmlrefs.nim b/examples/htmlrefs.nim
deleted file mode 100644
index 394932773..000000000
--- a/examples/htmlrefs.nim
+++ /dev/null
@@ -1,57 +0,0 @@
-# Example program to show the new parsexml module
-# This program reads an HTML file and writes all its used links to stdout.
-# Errors and whitespace are ignored.
-
-import os, streams, parsexml, strutils
-
-proc `=?=` (a, b: string): bool =
-  # little trick: define our own comparator that ignores case
-  return cmpIgnoreCase(a, b) == 0
-
-if paramCount() < 1:
-  quit("Usage: htmlrefs filename[.html]")
-
-var links = 0 # count the number of links
-var filename = addFileExt(paramStr(1), "html")
-var s = newFileStream(filename, fmRead)
-if s == nil: quit("cannot open the file " & filename)
-var x: XmlParser
-open(x, s, filename)
-next(x) # get first event
-block mainLoop:
-  while true:
-    case x.kind
-    of xmlElementOpen:
-      # the <a href = "xyz"> tag we are interested in always has an attribute,
-      # thus we search for ``xmlElementOpen`` and not for ``xmlElementStart``
-      if x.elementName =?= "a":
-        x.next()
-        if x.kind == xmlAttribute:
-          if x.attrKey =?= "href":
-            var link = x.attrValue
-            inc(links)
-            # skip until we have an ``xmlElementClose`` event
-            while true:
-              x.next()
-              case x.kind
-              of xmlEof: break mainLoop
-              of xmlElementClose: break
-              else: discard
-            x.next() # skip ``xmlElementClose``
-            # now we have the description for the ``a`` element
-            var desc = ""
-            while x.kind == xmlCharData:
-              desc.add(x.charData)
-              x.next()
-            echo(desc & ": " & link)
-      else:
-        x.next()
-    of xmlEof: break # end of file reached
-    of xmlError:
-      echo(errorMsg(x))
-      x.next()
-    else: x.next() # skip other events
-
-echo($links & " link(s) found!")
-x.close()
-
diff --git a/examples/htmltitle.nim b/examples/htmltitle.nim
deleted file mode 100644
index 96bfc7d91..000000000
--- a/examples/htmltitle.nim
+++ /dev/null
@@ -1,36 +0,0 @@
-# Example program to show the parsexml module
-# This program reads an HTML file and writes its title to stdout.
-# Errors and whitespace are ignored.
-
-import os, streams, parsexml, strutils
-
-if paramCount() < 1:
-  quit("Usage: htmltitle filename[.html]")
-
-var filename = addFileExt(paramStr(1), "html")
-var s = newFileStream(filename, fmRead)
-if s == nil: quit("cannot open the file " & filename)
-var x: XmlParser
-open(x, s, filename)
-while true:
-  x.next()
-  case x.kind
-  of xmlElementStart:
-    if cmpIgnoreCase(x.elementName, "title") == 0:
-      var title = ""
-      x.next()  # skip "<title>"
-      while x.kind == xmlCharData:
-        title.add(x.charData)
-        x.next()
-      if x.kind == xmlElementEnd and cmpIgnoreCase(x.elementName, "title") == 0:
-        echo("Title: " & title)
-        quit(0) # Success!
-      else:
-        echo(x.errorMsgExpected("/title"))
-
-  of xmlEof: break # end of file reached
-  else: discard # ignore other events
-
-x.close()
-quit("Could not determine title!")
-
diff --git a/examples/httpserver2.nim b/examples/httpserver2.nim
deleted file mode 100644
index 1843ff967..000000000
--- a/examples/httpserver2.nim
+++ /dev/null
@@ -1,247 +0,0 @@
-import strutils, os, osproc, strtabs, streams, sockets
-
-const
-  wwwNL* = "\r\L"
-  ServerSig = "Server: httpserver.nim/1.0.0" & wwwNL
-
-type
-  TRequestMethod = enum reqGet, reqPost
-  TServer* = object       ## contains the current server state
-    s: Socket
-    job: seq[TJob]
-  TJob* = object
-    client: Socket
-    process: Process
-
-# --------------- output messages --------------------------------------------
-
-proc sendTextContentType(client: Socket) =
-  send(client, "Content-type: text/html" & wwwNL)
-  send(client, wwwNL)
-
-proc badRequest(client: Socket) =
-  # Inform the client that a request it has made has a problem.
-  send(client, "HTTP/1.0 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)
-
-
-proc cannotExec(client: Socket) =
-  send(client, "HTTP/1.0 500 Internal Server Error" & wwwNL)
-  sendTextContentType(client)
-  send(client, "<P>Error prohibited CGI execution.</p>" & wwwNL)
-
-
-proc headers(client: Socket, filename: string) =
-  # XXX could use filename to determine file type
-  send(client, "HTTP/1.0 200 OK" & wwwNL)
-  send(client, ServerSig)
-  sendTextContentType(client)
-
-proc notFound(client: Socket, path: string) =
-  send(client, "HTTP/1.0 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 <b>" & path & "</b>" & wwwNL)
-  send(client, "is unavailable or nonexistent.</p>" & wwwNL)
-  send(client, "</body></html>" & wwwNL)
-
-
-proc unimplemented(client: Socket) =
-  send(client, "HTTP/1.0 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 ---------------------------------------------
-
-proc discardHeaders(client: Socket) = skip(client)
-
-proc serveFile(client: Socket, filename: string) =
-  discardHeaders(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:
-          let err = osLastError()
-          dealloc(buf)
-          close(f)
-          raiseOSError(err)
-      if bytesread != bufSize: break
-    dealloc(buf)
-    close(f)
-    client.close()
-  else:
-    notFound(client, filename)
-
-# ------------------ CGI execution -------------------------------------------
-
-proc executeCgi(server: var TServer, client: Socket, path, query: string,
-                meth: TRequestMethod) =
-  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 = ""
-    var dataAvail = true
-    while dataAvail:
-      dataAvail = recvLine(client, buf)
-      if buf.len == 0:
-        break
-      var L = toLowerAscii(buf)
-      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)
-
-  var job: TJob
-  job.process = process
-  job.client = client
-  server.job.add(job)
-
-  if meth == reqPost:
-    # get from client and post to CGI program:
-    var buf = alloc(contentLength)
-    if recv(client, buf, contentLength) != contentLength:
-      let err = osLastError()
-      dealloc(buf)
-      raiseOSError(err)
-    var inp = process.inputStream
-    inp.writeData(buf, contentLength)
-    dealloc(buf)
-
-proc animate(server: var TServer) =
-  # checks list of jobs, removes finished ones (pretty sloppy by seq copying)
-  var active_jobs: seq[TJob] = @[]
-  for i in 0..server.job.len-1:
-    var job = server.job[i]
-    if running(job.process):
-      active_jobs.add(job)
-    else:
-      # read process output stream and send it to client
-      var outp = job.process.outputStream
-      while true:
-        var line = outp.readstr(1024)
-        if line.len == 0:
-          break
-        else:
-          try:
-            send(job.client, line)
-          except:
-            echo("send failed, client diconnected")
-      close(job.client)
-
-  server.job = active_jobs
-
-# --------------- Server Setup -----------------------------------------------
-
-proc acceptRequest(server: var TServer, client: Socket) =
-  var cgi = false
-  var query = ""
-  var buf = ""
-  discard recvLine(client, buf)
-  var path = ""
-  var data = buf.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
-  path = "." & path
-
-  echo("accept: " & path)
-
-  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, path)
-    client.close()
-  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(server, client, path, query, meth)
-
-when isMainModule:
-  var port = 80
-
-  var server: TServer
-  server.job = @[]
-  server.s = socket(AF_INET)
-  if server.s == invalidSocket: raiseOSError(osLastError())
-  server.s.bindAddr(port=Port(port))
-  listen(server.s)
-  echo("server up on port " & $port)
-
-  while true:
-    # check for new new connection & handle it
-    var list: seq[Socket] = @[server.s]
-    if select(list, 10) > 0:
-      var client: Socket
-      new(client)
-      accept(server.s, client)
-      try:
-        acceptRequest(server, client)
-      except:
-        echo("failed to accept client request")
-
-    # pooling events
-    animate(server)
-    # some slack for CPU
-    sleep(10)
-  server.s.close()
diff --git a/examples/keyval.nim b/examples/keyval.nim
deleted file mode 100644
index a594c0fa8..000000000
--- a/examples/keyval.nim
+++ /dev/null
@@ -1,8 +0,0 @@
-# Filter key=value pairs from "myfile.txt"
-import re
-
-for x in lines("myfile.txt"):
-  if x =~ re"(\w+)=(.*)":
-    echo "Key: ", matches[0], " Value: ", matches[1]
-
-
diff --git a/examples/keyval2.nim b/examples/keyval2.nim
deleted file mode 100644
index 2a5643276..000000000
--- a/examples/keyval2.nim
+++ /dev/null
@@ -1,7 +0,0 @@
-# Filter key=value pairs from "myfile.txt"
-import pegs
-
-for x in lines("myfile.txt"):
-  if x =~ peg"{\ident} \s* '=' \s* {.*}":
-    echo "Key: ", matches[0],
-         " Value: ", matches[1]
diff --git a/examples/maximum.nim b/examples/maximum.nim
deleted file mode 100644
index 6552a8144..000000000
--- a/examples/maximum.nim
+++ /dev/null
@@ -1,6 +0,0 @@
-# Test high level features
-
-import strutils, sequtils
-
-echo "Give a list of numbers (separated by spaces): "
-stdin.readLine.split.map(parseInt).max.`$`.echo(" is the maximum!")
diff --git a/examples/myfile.txt b/examples/myfile.txt
deleted file mode 100644
index fb7cda984..000000000
--- a/examples/myfile.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-kladsfa
-
-asdflksadlfasf
-
-
-adsfljksadfl
-
-
-key=/usr/bin/value
-key2=/ha/ha
-
diff --git a/examples/objciface/gnustepex.nim b/examples/objciface/gnustepex.nim
deleted file mode 100644
index d961d3087..000000000
--- a/examples/objciface/gnustepex.nim
+++ /dev/null
@@ -1,40 +0,0 @@
-# horrible example of how to interface with GNUStep ...
-
-{.passL: "-lobjc".}
-{.emit: """
-
-#include <objc/Object.h>
-
-@interface Greeter:Object
-{
-}
-
-- (void)greet:(long)x y:(long)dummy;
-
-@end
-
-#include <stdio.h>
-
-@implementation Greeter
-
-- (void)greet:(long)x y:(long)dummy
-{
-	printf("Hello, World!\n");
-}
-
-@end
-
-#include <stdlib.h>
-""".}
-
-type
-  TId {.importc: "id", header: "<objc/Object.h>", final.} = distinct int
-
-proc newGreeter: TId {.importobjc: "Greeter new", nodecl.}
-proc greet(self: TId, x, y: int) {.importobjc: "greet", nodecl.}
-proc free(self: TId) {.importobjc: "free", nodecl.}
-
-var g = newGreeter()
-g.greet(12, 34)
-g.free()
-
diff --git a/examples/parsecfgex.nim b/examples/parsecfgex.nim
deleted file mode 100644
index 0fa03ffb5..000000000
--- a/examples/parsecfgex.nim
+++ /dev/null
@@ -1,25 +0,0 @@
-
-import
-  os, parsecfg, strutils, streams
-
-var f = newFileStream(paramStr(1), fmRead)
-if f != nil:
-  var p: CfgParser
-  open(p, f, paramStr(1))
-  while true:
-    var e = next(p)
-    case e.kind
-    of cfgEof:
-      echo("EOF!")
-      break
-    of cfgSectionStart:   ## a ``[section]`` has been parsed
-      echo("new section: " & e.section)
-    of cfgKeyValuePair:
-      echo("key-value-pair: " & e.key & ": " & e.value)
-    of cfgOption:
-      echo("command: " & e.key & ": " & e.value)
-    of cfgError:
-      echo(e.msg)
-  close(p)
-else:
-  echo("cannot open: " & paramStr(1))
diff --git a/examples/readme.txt b/examples/readme.txt
deleted file mode 100644
index 686271660..000000000
--- a/examples/readme.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-In this directory you will find several examples for how to use the Nim
-library.
diff --git a/examples/ssl/extradata.nim b/examples/ssl/extradata.nim
deleted file mode 100644
index 1e3b89b02..000000000
--- a/examples/ssl/extradata.nim
+++ /dev/null
@@ -1,26 +0,0 @@
-# Stores extra data inside the SSL context.
-import net
-
-let ctx = newContext()
-
-# Our unique index for storing foos
-let fooIndex = ctx.getExtraDataIndex()
-# And another unique index for storing foos
-let barIndex = ctx.getExtraDataIndex()
-echo "got indexes ", fooIndex, " ", barIndex
-
-try:
-  discard ctx.getExtraData(fooIndex)
-  assert false
-except IndexError:
-  echo("Success")
-
-type
-  FooRef = ref object of RootRef
-    foo: int
-
-let foo = FooRef(foo: 5)
-ctx.setExtraData(fooIndex, foo)
-doAssert ctx.getExtraData(fooIndex).FooRef == foo
-
-ctx.destroyContext()
diff --git a/examples/ssl/pskclient.nim b/examples/ssl/pskclient.nim
deleted file mode 100644
index c83f27fbc..000000000
--- a/examples/ssl/pskclient.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-# Create connection encrypted using preshared key (TLS-PSK).
-import net
-
-static: assert defined(ssl)
-
-let sock = newSocket()
-sock.connect("localhost", Port(8800))
-
-proc clientFunc(identityHint: string): tuple[identity: string, psk: string] =
-  echo "identity hint ", identityHint.repr
-  return ("foo", "psk-of-foo")
-
-let context = newContext(cipherList="PSK-AES256-CBC-SHA")
-context.clientGetPskFunc = clientFunc
-context.wrapConnectedSocket(sock, handshakeAsClient)
-context.destroyContext()
diff --git a/examples/ssl/pskserver.nim b/examples/ssl/pskserver.nim
deleted file mode 100644
index 859eaa875..000000000
--- a/examples/ssl/pskserver.nim
+++ /dev/null
@@ -1,20 +0,0 @@
-# Accept connection encrypted using preshared key (TLS-PSK).
-import net
-
-static: assert defined(ssl)
-
-let sock = newSocket()
-sock.bindAddr(Port(8800))
-sock.listen()
-
-let context = newContext(cipherList="PSK-AES256-CBC-SHA")
-context.pskIdentityHint = "hello"
-context.serverGetPskFunc = proc(identity: string): string = "psk-of-" & identity
-
-while true:
-  var client = new(Socket)
-  sock.accept(client)
-  sock.setSockOpt(OptReuseAddr, true)
-  echo "accepted connection"
-  context.wrapConnectedSocket(client, handshakeAsServer)
-  echo "got connection with identity ", client.getPskIdentity()
diff --git a/examples/statcsv.nim b/examples/statcsv.nim
deleted file mode 100644
index 983cd555f..000000000
--- a/examples/statcsv.nim
+++ /dev/null
@@ -1,60 +0,0 @@
-# Example program to show the parsecsv module
-# This program reads a CSV file and computes sum, mean, minimum, maximum and
-# the standard deviation of its columns.
-# The CSV file can have a header which is then used for the output.
-
-import os, streams, parsecsv, strutils, math, stats
-
-if paramCount() < 1:
-  quit("Usage: statcsv filename[.csv]")
-
-var filename = addFileExt(paramStr(1), "csv")
-var s = newFileStream(filename, fmRead)
-if s == nil: quit("cannot open the file " & filename)
-
-var
-  x: CsvParser
-  header: seq[string]
-  res: seq[RunningStat]
-open(x, s, filename, separator=';', skipInitialSpace = true)
-while readRow(x):
-  if processedRows(x) == 1:
-    newSeq(res, x.row.len) # allocate space for the result
-    if validIdentifier(x.row[0]):
-      # header line:
-      header = x.row
-    else:
-      newSeq(header, x.row.len)
-      for i in 0..x.row.len-1: header[i] = "Col " & $(i+1)
-  else:
-    # data line:
-    for i in 0..x.row.len-1:
-      push(res[i], parseFloat(x.row[i]))
-x.close()
-
-# Write results:
-for i in 0..header.len-1:
-  stdout.write("\t")
-  stdout.write(header[i])
-stdout.write("\nSum")
-for i in 0..header.len-1:
-  stdout.write("\t")
-  stdout.write(res[i].sum)
-stdout.write("\nMean")
-for i in 0..header.len-1:
-  stdout.write("\t")
-  stdout.write(res[i].mean)
-stdout.write("\nMin")
-for i in 0..header.len-1:
-  stdout.write("\t")
-  stdout.write(res[i].min)
-stdout.write("\nMax")
-for i in 0..header.len-1:
-  stdout.write("\t")
-  stdout.write(res[i].max)
-stdout.write("\nStdDev")
-for i in 0..header.len-1:
-  stdout.write("\t")
-  stdout.write(res[i].standardDeviation)
-stdout.write("\n")
-
diff --git a/examples/talk/dsl.nim b/examples/talk/dsl.nim
deleted file mode 100644
index 2dde51790..000000000
--- a/examples/talk/dsl.nim
+++ /dev/null
@@ -1,33 +0,0 @@
-
-import strutils
-
-template html(name, matter: untyped) =
-  proc name(): string =
-    result = "<html>"
-    matter
-    result.add("</html>")
-
-template nestedTag(tag: untyped) =
-  template tag(matter: typed) =
-    result.add("<" & astToStr(tag) & ">")
-    matter
-    result.add("</" & astToStr(tag) & ">")
-
-template simpleTag(tag: untyped) =
-  template tag(matter: untyped) =
-    result.add("<$1>$2</$1>" % [astToStr(tag), matter])
-
-nestedTag body
-nestedTag head
-nestedTag ul
-simpleTag title
-simpleTag li
-
-html mainPage:
-  head:
-    title "now look at this"
-  body:
-    ul:
-      li "Nim is quite capable"
-
-echo mainPage()
diff --git a/examples/talk/formatoptimizer.nim b/examples/talk/formatoptimizer.nim
deleted file mode 100644
index 104214e19..000000000
--- a/examples/talk/formatoptimizer.nim
+++ /dev/null
@@ -1,55 +0,0 @@
-## This is the example that optimizes a modified "hello world"
-
-import macros
-
-proc invalidFormatString() =
-  echo "invalidFormatString"
-
-template formatImpl(handleChar: untyped) =
-  var i = 0
-  while i < f.len:
-    if f[i] == '$':
-      case f[i+1]
-      of '1'..'9':
-        var j = 0
-        i += 1
-        while f[i] in {'0'..'9'}:
-          j = j * 10 + ord(f[i]) - ord('0')
-          i += 1
-        result.add(a[j-1])
-      else:
-        invalidFormatString()
-    else:
-      result.add(handleChar(f[i]))
-      i += 1
-
-proc `%`*(f: string, a: openArray[string]): string =
-  template identity(x: untyped): untyped = x
-  result = ""
-  formatImpl(identity)
-
-macro optFormat{`%`(f, a)}(f: string{lit}, a: openArray[string]): untyped =
-  result = newNimNode(nnkBracket)
-  #newCall("&")
-  let f = f.strVal
-  formatImpl(newLit)
-  result = nestList(!"&", result)
-
-template optAdd1{x = y; add(x, z)}(x, y, z: string) =
-  x = y & z
-
-#template optAdd2{x.add(y); x.add(z)}(x, y, z: string) =
-#  x.add(y & z)
-
-proc `/&` [T: object](x: T): string =
-  result = "("
-  for name, value in fieldPairs(x):
-    result.add("$1: $2\n" % [name, $value])
-  result.add(")")
-
-type
-  MyObject = object
-    a, b: int
-    s: string
-let obj = MyObject(a: 3, b: 4, s: "abc")
-echo(/&obj)
diff --git a/examples/talk/hoisting.nim b/examples/talk/hoisting.nim
deleted file mode 100644
index 54e00884f..000000000
--- a/examples/talk/hoisting.nim
+++ /dev/null
@@ -1,23 +0,0 @@
-type
-  Regex = distinct string
-
-const maxSubpatterns = 10
-
-proc re(x: string): Regex =
-  result = Regex(x)
-
-proc match(s: string, pattern: Regex, captures: var openArray[string]): bool =
-  true
-
-template optRe{re(x)}(x: string{lit}): Regex =
-  var g {.global.} = re(x)
-  g
-
-template `=~`(s: string, pattern: Regex): bool =
-  when not declaredInScope(matches):
-    var matches {.inject.}: array[maxSubPatterns, string]
-  match(s, pattern, matches)
-
-for line in lines("input.txt"):
-  if line =~ re"(\w+)=(\w+)":
-    echo "key-value pair; key: ", matches[0], " value: ", matches[1]
diff --git a/examples/talk/lazyeval.nim b/examples/talk/lazyeval.nim
deleted file mode 100644
index 77d963834..000000000
--- a/examples/talk/lazyeval.nim
+++ /dev/null
@@ -1,12 +0,0 @@
-
-const
-  debug = true
-
-template log(msg: string) =
-  if debug:
-    echo msg
-var
-  x = 1
-  y = 2
-
-log("x: " & $x & ", y: " & $y)
diff --git a/examples/talk/quasiquote.nim b/examples/talk/quasiquote.nim
deleted file mode 100644
index b3c7bb971..000000000
--- a/examples/talk/quasiquote.nim
+++ /dev/null
@@ -1,11 +0,0 @@
-
-import macros
-
-macro check(ex: untyped): typed =
-  var info = ex.lineinfo
-  var expString = ex.toStrLit
-  result = quote do:
-    if not `ex`:
-      echo `info`, ": Check failed: ", `expString`
-
-check 1 < 2
diff --git a/examples/talk/tags.nim b/examples/talk/tags.nim
deleted file mode 100644
index 8bf3450c9..000000000
--- a/examples/talk/tags.nim
+++ /dev/null
@@ -1,9 +0,0 @@
-
-template htmlTag(tag: untyped) =
-  proc tag(): string = "<" & astToStr(tag) & ">"
-
-htmlTag(br)
-htmlTag(html)
-
-echo br()
-echo html()
\ No newline at end of file
diff --git a/examples/transff.nim b/examples/transff.nim
deleted file mode 100644
index 32d17e52c..000000000
--- a/examples/transff.nim
+++ /dev/null
@@ -1,8 +0,0 @@
-# Shows how to transform a file
-
-import pegs
-
-transformFile("infile.txt", "outfile.txt",
-  [(peg"""S <- {typedesc} \s* {\ident} \s* ','
-         typedesc <- \ident '*'* """, r"$2: $1")])
-
diff --git a/examples/tunit.nim b/examples/tunit.nim
deleted file mode 100644
index d7b1fcbbd..000000000
--- a/examples/tunit.nim
+++ /dev/null
@@ -1,47 +0,0 @@
-import
-  unittest, macros
-
-var
-    a = 1
-    b = 22
-    c = 1
-    d = 3
-
-suite "my suite":
-  setup:
-    echo "suite setup"
-    var testVar = "from setup"
-
-  teardown:
-    echo "suite teardown"
-
-  test "first suite test":
-    testVar = "modified"
-    echo "test var: " & testVar
-    check a > b
-
-  test "second suite test":
-    echo "test var: " & testVar
-
-proc foo: bool =
-  echo "running foo"
-  return true
-
-proc err =
-  raise newException(EArithmetic, "some exception")
-
-test "final test":
-  echo "inside suite-less test"
-
-  check:
-    a == c
-    foo()
-    d > 10
-
-test "arithmetic failure":
-  expect(EArithmetic):
-    err()
-
-  expect(EArithmetic, ESystem):
-    discard foo()
-
diff --git a/examples/unix_socket/client.nim b/examples/unix_socket/client.nim
deleted file mode 100644
index f4283d64d..000000000
--- a/examples/unix_socket/client.nim
+++ /dev/null
@@ -1,6 +0,0 @@
-import net
-
-let sock = newSocket(AF_UNIX, SOCK_STREAM, IPPROTO_IP)
-
-sock.connectUnix("sock")
-sock.send("hello\n")
diff --git a/examples/unix_socket/server.nim b/examples/unix_socket/server.nim
deleted file mode 100644
index e798bbb48..000000000
--- a/examples/unix_socket/server.nim
+++ /dev/null
@@ -1,14 +0,0 @@
-import net
-
-let sock = newSocket(AF_UNIX, SOCK_STREAM, IPPROTO_IP)
-sock.bindUnix("sock")
-sock.listen()
-
-while true:
-  var client = new(Socket)
-  sock.accept(client)
-  var output = ""
-  output.setLen 32
-  client.readLine(output)
-  echo "got ", output
-  client.close()
diff --git a/install.txt b/install.txt
deleted file mode 100644
index 6ab562b07..000000000
--- a/install.txt
+++ /dev/null
@@ -1,88 +0,0 @@
-Installation
-============
-
-Installation on Linux/UNIX
---------------------------
-
-The GNU C Compiler is fully supported, other compilers may work. The C compiler
-should be in your ``$PATH`` (most likely the case). Note that some few Linux
-distributions do not ship with a GCC compiler preinstalled - then you have to
-install it.
-
-Install Nim by downloading the appropriate ``.tar.xz`` file and extracting it
-to a directory of your choice. The Nim Compiler will stay in this
-directory (unless you copy it somewhere else). The compiler does not need
-write access to its directory, so copying the nim folder to ``/opt``
-works.
-
-Then run the following command::
-
-  sh build.sh
-
-Unlike other software, Nim does not distribute its files over the whole file
-hierarchy. This has the advantage that you can deinstall it by just deleting
-its folder. The disadvantage is that you have to add it to your ``PATH``
-manually. An alternative is to create a symbolic link in ``/usr/bin``::
-
-  [sudo] ln -s $your_install_dir/bin/nim  /usr/bin/nim
-
-There are also ``install.sh`` and ``deinstall.sh`` scripts for distributing
-the files over the UNIX hierarchy. However, updating your Nim installation
-is more cumbersome then.
-
-To complete the installation you should also build Nim's tools like
-``nimsuggest``, ``nimble`` or ``nimgrep`` via::
-
-  nim c koch
-  koch tools
-
-Note that these tools should also end up in your ``PATH`` so adding
-``$your_install_dir/bin/nim`` to your ``PATH`` is preferred over the symlink
-solution.
-
-
-Installation on the Macintosh
------------------------------
-
-Only MacOS X is supported.
-Since MacOS X is UNIX based too, it works like the installation on Linux.
-However, for unknown reasons the symbolic link method does not work on MacOS X.
-You need to install Apple's developer's tools for the GNU Compiler Collection
-or clang.
-
-
-Installation on Windows
------------------------
-
-Install Nim by downloading and unzipping the ``nim_$version.zip`` file.
-Run ``finish.exe`` to detect and setup your MingW environment.
-
-Currently, the following C compilers are supported under Windows:
-
-- | Microsoft's Visual C++
-  | http://msdn.microsoft.com/visualc
-  | (You need the SDK too - but not the full one: Only
-    the win32api header files and import libraries are essential.)
-- | Gnu C Compiler (the mingw version; the cygwin version has not been tested!)
-  | http://www.mingw.org/download.shtml
-- | LLVM with Clang or GNU C/C++ frontend
-  | http://llvm.org/releases/download.html
-
-However, most testing is done with GCC.
-
-Bootstrapping from GitHub
--------------------------
-
-Take a look at the readme file on github `here <https://github.com/nim-lang/Nim#readme>`_
-for instructions.
-
-
-Installation of Nimble
-----------------------
-
-Nimble is Nim's package manager. For the source based installations where you
-added Nim's ``bin`` directory to your ``$PATH`` the easiest way of installing
-Nimble is via::
-
-  nim c koch
-  koch nimble
diff --git a/install_nimble.nims b/install_nimble.nims
deleted file mode 100644
index 29d89bec8..000000000
--- a/install_nimble.nims
+++ /dev/null
@@ -1,6 +0,0 @@
-
-mode = ScriptMode.Verbose
-
-echo "This script is deprecated. Use 'koch nimble' instead."
-
-exec "./koch nimble"
diff --git a/install_tools.nims b/install_tools.nims
deleted file mode 100644
index b0307196c..000000000
--- a/install_tools.nims
+++ /dev/null
@@ -1,6 +0,0 @@
-
-mode = ScriptMode.Verbose
-
-echo "This script is deprecated. Use 'koch tools' instead."
-
-exec "./koch tools"
diff --git a/koch.nim b/koch.nim
index 97e1da776..77bc2299f 100644
--- a/koch.nim
+++ b/koch.nim
@@ -1,14 +1,28 @@
 #
 #
 #         Maintenance program for Nim
-#        (c) Copyright 2017 Andreas Rumpf
+#        (c) Copyright 2024 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 #
-#    See doc/koch.txt for documentation.
+#    See doc/koch.md for documentation.
 #
 
+const
+  # examples of possible values for repos: Head, ea82b54
+  NimbleStableCommit = "4fb6f8e6c33963f6f510fe82d09ad2a61b5e4265" # 0.16.1
+  AtlasStableCommit = "5faec3e9a33afe99a7d22377dd1b45a5391f5504"
+  ChecksumsStableCommit = "bd9bf4eaea124bf8d01e08f92ac1b14c6879d8d3"
+  SatStableCommit = "faf1617f44d7632ee9601ebc13887644925dcc01"
+
+  # examples of possible values for fusion: #head, #ea82b54, 1.2.3
+  FusionStableHash = "#372ee4313827ef9f2ea388840f7d6b46c2b1b014"
+  HeadHash = "#head"
+when not defined(windows):
+  const
+    Z3StableCommit = "65de3f748a6812eecd7db7c478d5fc54424d368b" # the version of Z3 that DrNim uses
+
 when defined(gcc) and defined(windows):
   when defined(x86):
     {.link: "icons/koch.res".}
@@ -16,12 +30,20 @@ when defined(gcc) and defined(windows):
     {.link: "icons/koch_icon.o".}
 
 when defined(amd64) and defined(windows) and defined(vcc):
-  {.link: "icons/koch-amd64-windows-vcc.res" .}
+  {.link: "icons/koch-amd64-windows-vcc.res".}
 when defined(i386) and defined(windows) and defined(vcc):
-  {.link: "icons/koch-i386-windows-vcc.res" .}
+  {.link: "icons/koch-i386-windows-vcc.res".}
+
+import std/[os, strutils, parseopt, osproc]
+  # Using `std/os` instead of `os` to fail early if config isn't set up properly.
+  # If this fails with: `Error: cannot open file: std/os`, see
+  # https://github.com/nim-lang/Nim/pull/14291 for explanation + how to fix.
 
-import
-  os, strutils, parseopt, osproc, streams
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
+
+import tools / kochdocs
+import tools / deps
 
 const VersionAsString = system.NimVersion
 
@@ -30,7 +52,7 @@ const
 +-----------------------------------------------------------------+
 |         Maintenance program for Nim                             |
 |             Version $1|
-|             (c) 2017 Andreas Rumpf                              |
+|             (c) 2024 Andreas Rumpf                              |
 +-----------------------------------------------------------------+
 Build time: $2, $3
 
@@ -38,39 +60,59 @@ Usage:
   koch [options] command [options for command]
 Options:
   --help, -h               shows this help and quits
+  --latest                 bundle the installers with bleeding edge versions of
+                           external components.
+  --stable                 bundle the installers with stable versions of
+                           external components (default).
+  --nim:path               use specified path for nim binary
+  --localdocs[:path]       only build local documentations. If a path is not
+                           specified (or empty), the default is used.
+  --skipIntegrityCheck     skips integrity check when booting the compiler
 Possible Commands:
   boot [options]           bootstraps with given command line options
   distrohelper [bindir]    helper for distro packagers
   tools                    builds Nim related tools
+  toolsNoExternal          builds Nim related tools (except external tools,
+                           e.g. nimble)
+                           doesn't require network connectivity
   nimble                   builds the Nimble tool
+  atlas                    builds the Atlas tool
+  checksums                installs the checksums dependency
+  fusion                   installs fusion via Nimble
+
 Boot options:
   -d:release               produce a release version of the compiler
-  -d:useLinenoise          use the linenoise library for interactive mode
-                           (not needed on Windows)
+  -d:nimUseLinenoise       use the linenoise library for interactive mode
+                           `nim secret` (not needed on Windows)
+  -d:leanCompiler          produce a compiler without JS codegen or
+                           documentation generator in order to use less RAM
+                           for bootstrapping
+  -d:nimHasLibFFI          adds FFI support for allowing compile-time VM to
+                           interface with native functions (experimental,
+                           requires prior `koch installdeps libffi`)
 
 Commands for core developers:
-  web [options]            generates the website and the full documentation
-  website [options]        generates only the website
-  csource -d:release       builds the C sources for installation
+  runCI                    runs continuous integration (CI), e.g. from Github Actions
+  docs [options]           generates the full documentation
+  csource -d:danger        builds the C sources for installation
   pdf                      builds the PDF documentation
   zip                      builds the installation zip package
   xz                       builds the installation tar.xz package
   testinstall              test tar.xz package; Unix only!
-  tests [options]          run the testsuite
+  installdeps [options]    installs external dependency (e.g. tinyc) to dist/
+  tests [options]          run the testsuite (run a subset of tests by
+                           specifying a category, e.g. `tests cat async`)
   temp options             creates a temporary compiler for testing
-  winrelease               creates a Windows release
-  pushcsource              push generated C sources to its repo
-Web options:
-  --googleAnalytics:UA-... add the given google analytics code to the docs. To
-                           build the official docs, use UA-48159761-1
 """
 
-const gaCode = " --googleAnalytics:UA-48159761-1"
+let kochExe* = when isMainModule: os.getAppFilename() # always correct when koch is main program, even if `koch` exe renamed e.g.: `nim c -o:koch_debug koch.nim`
+               else: getAppDir() / "koch".exe # works for winrelease
 
-proc exe(f: string): string =
-  result = addFileExt(f, ExeExt)
-  when defined(windows):
-    result = result.replace('/','\\')
+proc kochExec*(cmd: string) =
+  exec kochExe.quoteShell & " " & cmd
+
+proc kochExecFold*(desc, cmd: string) =
+  execFold(desc, kochExe.quoteShell & " " & cmd)
 
 template withDir(dir, body) =
   let old = getCurrentDir()
@@ -78,78 +120,17 @@ template withDir(dir, body) =
     setCurrentDir(dir)
     body
   finally:
-    setCurrentdir(old)
-
-proc findNim(): string =
-  var nim = "nim".exe
-  result = "bin" / nim
-  if existsFile(result): return
-  for dir in split(getEnv("PATH"), PathSep):
-    if existsFile(dir / nim): return dir / nim
-  # assume there is a symlink to the exe or something:
-  return nim
-
-proc exec(cmd: string, errorcode: int = QuitFailure, additionalPath = "") =
-  let prevPath = getEnv("PATH")
-  if additionalPath.len > 0:
-    var absolute = additionalPATH
-    if not absolute.isAbsolute:
-      absolute = getCurrentDir() / absolute
-    echo("Adding to $PATH: ", absolute)
-    putEnv("PATH", (if prevPath.len > 0: prevPath & PathSep else: "") & absolute)
-  echo(cmd)
-  if execShellCmd(cmd) != 0: quit("FAILURE", errorcode)
-  putEnv("PATH", prevPath)
-
-proc nimexec(cmd: string) =
-  exec findNim() & " " & cmd
+    setCurrentDir(old)
 
-proc execCleanPath(cmd: string,
-                   additionalPath = ""; errorcode: int = QuitFailure) =
-  # simulate a poor man's virtual environment
-  let prevPath = getEnv("PATH")
-  when defined(windows):
-    let CleanPath = r"$1\system32;$1;$1\System32\Wbem" % getEnv"SYSTEMROOT"
-  else:
-    const CleanPath = r"/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin"
-  putEnv("PATH", CleanPath & PathSep & additionalPath)
-  echo(cmd)
-  if execShellCmd(cmd) != 0: quit("FAILURE", errorcode)
-  putEnv("PATH", prevPath)
-
-proc testUnixInstall() =
-  let oldCurrentDir = getCurrentDir()
-  try:
-    let destDir = getTempDir()
-    copyFile("build/nim-$1.tar.xz" % VersionAsString,
-             destDir / "nim-$1.tar.xz" % VersionAsString)
-    setCurrentDir(destDir)
-    execCleanPath("tar -xJf nim-$1.tar.xz" % VersionAsString)
-    setCurrentDir("nim-$1" % VersionAsString)
-    execCleanPath("sh build.sh")
-    # first test: try if './bin/nim --version' outputs something sane:
-    let output = execProcess("./bin/nim --version").splitLines
-    if output.len > 0 and output[0].contains(VersionAsString):
-      echo "Version check: success"
-      execCleanPath("./bin/nim c koch.nim")
-      execCleanPath("./koch boot -d:release", destDir / "bin")
-      # check the docs build:
-      execCleanPath("./koch web", destDir / "bin")
-      # check nimble builds:
-      execCleanPath("./koch tools")
-      # check the tests work:
-      execCleanPath("./koch tests", destDir / "bin")
-    else:
-      echo "Version check: failure"
-  finally:
-    setCurrentDir oldCurrentDir
+let origDir = getCurrentDir()
+setCurrentDir(getAppDir())
 
 proc tryExec(cmd: string): bool =
   echo(cmd)
   result = execShellCmd(cmd) == 0
 
 proc safeRemove(filename: string) =
-  if existsFile(filename): removeFile(filename)
+  if fileExists(filename): removeFile(filename)
 
 proc overwriteFile(source, dest: string) =
   safeRemove(dest)
@@ -158,92 +139,98 @@ proc overwriteFile(source, dest: string) =
 proc copyExe(source, dest: string) =
   safeRemove(dest)
   copyFile(dest=dest, source=source)
-  inclFilePermissions(dest, {fpUserExec})
+  inclFilePermissions(dest, {fpUserExec, fpGroupExec, fpOthersExec})
 
 const
   compileNimInst = "tools/niminst/niminst"
+  distDir = "dist"
 
 proc csource(args: string) =
   nimexec(("cc $1 -r $3 --var:version=$2 --var:mingw=none csource " &
            "--main:compiler/nim.nim compiler/installer.ini $1") %
        [args, VersionAsString, compileNimInst])
 
-proc bundleNimbleSrc() =
-  ## bunldeNimbleSrc() bundles a specific Nimble commit with the tarball. We
-  ## always bundle the latest official release.
-  if not dirExists("dist/nimble/.git"):
-    exec("git clone https://github.com/nim-lang/nimble.git dist/nimble")
-  withDir("dist/nimble"):
-    exec("git checkout -f stable")
-    exec("git pull")
-
-proc bundleNimbleExe() =
-  bundleNimbleSrc()
-  # now compile Nimble and copy it to $nim/bin for the installer.ini
-  # to pick it up:
-  nimexec("c -d:release dist/nimble/src/nimble.nim")
-  copyExe("dist/nimble/src/nimble".exe, "bin/nimble".exe)
-
-proc buildNimble(latest: bool) =
-  # old installations created nim/nimblepkg/*.nim files. We remove these
-  # here so that it cannot cause problems (nimble bug #306):
-  if dirExists("bin/nimblepkg"):
-    removeDir("bin/nimblepkg")
-  # if koch is used for a tar.xz, build the dist/nimble we shipped
-  # with the tarball:
-  var installDir = "dist/nimble"
-  if not latest and dirExists(installDir) and not dirExists("dist/nimble/.git"):
-    discard "don't do the git dance"
+proc bundleC2nim(args: string) =
+  cloneDependency(distDir, "https://github.com/nim-lang/c2nim.git")
+  nimCompile("dist/c2nim/c2nim",
+             options = "--noNimblePath --path:. " & args)
+
+proc bundleNimbleExe(latest: bool, args: string) =
+  let commit = if latest: "HEAD" else: NimbleStableCommit
+  cloneDependency(distDir, "https://github.com/nim-lang/nimble.git",
+                  commit = commit, allowBundled = true)
+  cloneDependency(distDir / "nimble" / distDir, "https://github.com/nim-lang/checksums.git",
+                commit = ChecksumsStableCommit, allowBundled = true) # or copy it from dist?
+  cloneDependency(distDir / "nimble" / distDir, "https://github.com/nim-lang/sat.git",
+                commit = SatStableCommit, allowBundled = true)
+  # installer.ini expects it under $nim/bin
+  nimCompile("dist/nimble/src/nimble.nim",
+             options = "-d:release -d:nimNimbleBootstrap --noNimblePath " & args)
+
+proc bundleAtlasExe(latest: bool, args: string) =
+  let commit = if latest: "HEAD" else: AtlasStableCommit
+  cloneDependency(distDir, "https://github.com/nim-lang/atlas.git",
+                  commit = commit, allowBundled = true)
+  cloneDependency(distDir / "atlas" / distDir, "https://github.com/nim-lang/sat.git",
+                commit = SatStableCommit, allowBundled = true)
+  # installer.ini expects it under $nim/bin
+  nimCompile("dist/atlas/src/atlas.nim",
+             options = "-d:release --noNimblePath -d:nimAtlasBootstrap " & args)
+
+proc bundleNimsuggest(args: string) =
+  nimCompileFold("Compile nimsuggest", "nimsuggest/nimsuggest.nim",
+                 options = "-d:danger " & args)
+
+proc buildVccTool(args: string) =
+  let input = "tools/vccexe/vccexe.nim"
+  if contains(args, "--cc:vcc"):
+    nimCompileFold("Compile Vcc", input, "build", options = args)
+    let fileName = input.splitFile.name
+    moveFile(exe("build" / fileName), exe("bin" / fileName))
   else:
-    if not dirExists("dist/nimble/.git"):
-      if dirExists(installDir):
-        var id = 0
-        while dirExists("dist/nimble" & $id):
-          inc id
-        installDir = "dist/nimble" & $id
-      exec("git clone https://github.com/nim-lang/nimble.git " & installDir)
-    withDir(installDir):
-      if latest:
-        exec("git checkout -f master")
-      else:
-        exec("git checkout -f stable")
-      exec("git pull")
-  nimexec("c --noNimblePath -p:compiler -d:release " & installDir / "src/nimble.nim")
-  copyExe(installDir / "src/nimble".exe, "bin/nimble".exe)
-
-proc bundleNimsuggest(buildExe: bool) =
-  if buildExe:
-    nimexec("c --noNimblePath -d:release -p:compiler nimsuggest/nimsuggest.nim")
-    copyExe("nimsuggest/nimsuggest".exe, "bin/nimsuggest".exe)
-    removeFile("nimsuggest/nimsuggest".exe)
-
-proc buildVccTool() =
-  nimexec("c -o:bin/vccexe.exe tools/vccenv/vccexe")
-
-proc bundleWinTools() =
-  nimexec("c tools/finish.nim")
-  copyExe("tools/finish".exe, "finish".exe)
-  removeFile("tools/finish".exe)
-  buildVccTool()
-  nimexec("c -o:bin/nimgrab.exe -d:ssl tools/nimgrab.nim")
-  nimexec("c -o:bin/nimgrep.exe tools/nimgrep.nim")
+    nimCompileFold("Compile Vcc", input, options = args)
+
+proc bundleNimpretty(args: string) =
+  nimCompileFold("Compile nimpretty", "nimpretty/nimpretty.nim",
+                 options = "-d:release " & args)
+
+proc bundleWinTools(args: string) =
+  nimCompile("tools/finish.nim", outputDir = "", options = args)
+
+  buildVccTool(args)
+  nimCompile("tools/nimgrab.nim", options = "-d:ssl " & args)
+  nimCompile("tools/nimgrep.nim", options = args)
+  nimCompile("testament/testament.nim", options = args)
   when false:
     # not yet a tool worth including
-    nimexec(r"c --cc:vcc --app:gui -o:bin\downloader.exe -d:ssl --noNimblePath " &
-            r"--path:..\ui tools\downloader.nim")
-
-proc zip(args: string) =
-  bundleNimbleExe()
-  bundleNimsuggest(true)
-  bundleWinTools()
+    nimCompile(r"tools\downloader.nim",
+               options = r"--cc:vcc --app:gui -d:ssl --noNimblePath --path:..\ui " & args)
+
+proc bundleChecksums(latest: bool) =
+  let commit = if latest: "HEAD" else: ChecksumsStableCommit
+  cloneDependency(distDir, "https://github.com/nim-lang/checksums.git", commit, allowBundled = true)
+
+proc zip(latest: bool; args: string) =
+  bundleChecksums(latest)
+  bundleNimbleExe(latest, args)
+  bundleAtlasExe(latest, args)
+  bundleNimsuggest(args)
+  bundleNimpretty(args)
+  bundleWinTools(args)
   nimexec("cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" %
        [VersionAsString, compileNimInst])
   exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim zip compiler/installer.ini" %
        ["tools/niminst/niminst".exe, VersionAsString])
 
-proc xz(args: string) =
-  bundleNimbleSrc()
-  bundleNimsuggest(false)
+proc ensureCleanGit() =
+  let (outp, status) = osproc.execCmdEx("git diff")
+  if outp.len != 0:
+    quit "Not a clean git repository; 'git diff' not empty!"
+  if status != 0:
+    quit "Not a clean git repository; 'git diff' returned non-zero!"
+
+proc xz(latest: bool; args: string) =
+  ensureCleanGit()
   nimexec("cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" %
        [VersionAsString, compileNimInst])
   exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim xz compiler/installer.ini" %
@@ -253,21 +240,36 @@ proc buildTool(toolname, args: string) =
   nimexec("cc $# $#" % [args, toolname])
   copyFile(dest="bin" / splitFile(toolname).name.exe, source=toolname.exe)
 
-proc buildTools(latest: bool) =
-  nimexec "c --noNimblePath -p:compiler -d:release -o:" & ("bin/nimsuggest".exe) &
-      " nimsuggest/nimsuggest.nim"
-
-  nimexec "c -d:release -o:" & ("bin/nimgrep".exe) & " tools/nimgrep.nim"
-  when defined(windows): buildVccTool()
-
-  nimexec "c -o:" & ("bin/nimpretty".exe) & " nimpretty/nimpretty.nim"
-
-  buildNimble(latest)
-
-proc nsis(args: string) =
-  bundleNimbleExe()
-  bundleNimsuggest(true)
-  bundleWinTools()
+proc buildTools(args: string = "") =
+  bundleNimsuggest(args)
+  nimCompileFold("Compile nimgrep", "tools/nimgrep.nim",
+                 options = "-d:release " & args)
+  when defined(windows): buildVccTool(args)
+  bundleNimpretty(args)
+  nimCompileFold("Compile testament", "testament/testament.nim", options = "-d:release " & args)
+
+  # pre-packages a debug version of nim which can help in many cases investigate issuses
+  # withouth having to rebuild compiler.
+  # `-d:nimDebugUtils` only makes sense when temporarily editing/debugging compiler
+  # `-d:debug` should be changed to a flag that doesn't require re-compiling nim
+  # `--opt:speed` is a sensible default even for a debug build, it doesn't affect nim stacktraces
+  nimCompileFold("Compile nim_dbg", "compiler/nim.nim", options =
+      "--opt:speed --stacktrace -d:debug --stacktraceMsgs -d:nimCompilerStacktraceHints " & args,
+      outputName = "nim_dbg")
+
+proc testTools(args: string = "") =
+  nimCompileFold("Compile nimgrep", "tools/nimgrep.nim",
+                 options = "-d:release " & args)
+  when defined(windows): buildVccTool(args)
+  bundleNimpretty(args)
+  nimCompileFold("Compile testament", "testament/testament.nim", options = "-d:release " & args)
+
+proc nsis(latest: bool; args: string) =
+  bundleChecksums(latest)
+  bundleNimbleExe(latest, args)
+  bundleAtlasExe(latest, args)
+  bundleNimsuggest(args)
+  bundleWinTools(args)
   # make sure we have generated the niminst executables:
   buildTool("tools/niminst/niminst", args)
   #buildTool("tools/nimgrep", args)
@@ -285,39 +287,39 @@ proc install(args: string) =
   geninstall()
   exec("sh ./install.sh $#" % args)
 
-proc web(args: string) =
-  nimexec("js tools/dochack/dochack.nim")
-  nimexec("cc -r tools/nimweb.nim $# web/website.ini --putenv:nimversion=$#" %
-       [args, VersionAsString])
-
-proc website(args: string) =
-  nimexec("cc -r tools/nimweb.nim $# --website web/website.ini --putenv:nimversion=$#" %
-       [args, VersionAsString])
-
-proc pdf(args="") =
-  exec("$# cc -r tools/nimweb.nim $# --pdf web/website.ini --putenv:nimversion=$#" %
-       [findNim(), args, VersionAsString], additionalPATH=findNim().splitFile.dir)
+proc installDeps(dep: string, commit = "") =
+  # the hashes/urls are version controlled here, so can be changed seamlessly
+  # and tied to a nim release (mimicking git submodules)
+  var commit = commit
+  case dep
+  of "tinyc":
+    if commit.len == 0: commit = "916cc2f94818a8a382dd8d4b8420978816c1dfb3"
+    cloneDependency(distDir, "https://github.com/timotheecour/nim-tinyc-archive", commit)
+  of "libffi":
+    # technically a nimble package, however to play nicely with --noNimblePath,
+    # let's just clone it wholesale:
+    if commit.len == 0: commit = "bb2bdaf1a29a4bff6fbd8ae4695877cbb3ec783e"
+    cloneDependency(distDir, "https://github.com/Araq/libffi", commit)
+  else: doAssert false, "unsupported: " & dep
+  # xxx: also add linenoise, niminst etc, refs https://github.com/nim-lang/RFCs/issues/206
 
 # -------------- boot ---------------------------------------------------------
 
 proc findStartNim: string =
   # we try several things before giving up:
+  # * nimExe
   # * bin/nim
   # * $PATH/nim
   # If these fail, we try to build nim with the "build.(sh|bat)" script.
-  var nim = "nim".exe
-  result = "bin" / nim
-  if existsFile(result): return
-  for dir in split(getEnv("PATH"), PathSep):
-    if existsFile(dir / nim): return dir / nim
-
-  when defined(Posix):
+  let (nim, ok) = findNimImpl()
+  if ok: return nim
+  when defined(posix):
     const buildScript = "build.sh"
-    if existsFile(buildScript):
+    if fileExists(buildScript):
       if tryExec("./" & buildScript): return "bin" / nim
   else:
     const buildScript = "build.bat"
-    if existsFile(buildScript):
+    if fileExists(buildScript):
       if tryExec(buildScript): return "bin" / nim
 
   echo("Found no nim compiler and every attempt to build one failed!")
@@ -326,26 +328,69 @@ proc findStartNim: string =
 proc thVersion(i: int): string =
   result = ("compiler" / "nim" & $i).exe
 
-proc boot(args: string) =
+template doUseCpp(): bool = getEnv("NIM_COMPILE_TO_CPP", "false") == "true"
+
+proc boot(args: string, skipIntegrityCheck: bool) =
+  ## bootstrapping is a process that involves 3 steps:
+  ## 1. use csourcesAny to produce nim1.exe. This nim1.exe is buggy but
+  ## rock solid for building a Nim compiler. It shouldn't be used for anything else.
+  ## 2. use nim1.exe to produce nim2.exe. nim2.exe is the one you really need.
+  ## 3. We use nim2.exe to build nim3.exe. nim3.exe is equal to nim2.exe except for timestamps.
+  ## This step ensures a minimum amount of quality. We know that nim2.exe can be used
+  ## for Nim compiler development.
   var output = "compiler" / "nim".exe
   var finalDest = "bin" / "nim".exe
   # default to use the 'c' command:
-  let bootOptions = if args.len == 0 or args.startsWith("-"): "c" else: ""
-  let smartNimcache = (if "release" in args: "nimcache/r_" else: "nimcache/d_") &
-                      hostOs & "_" & hostCpu
+  let useCpp = doUseCpp()
+  let smartNimcache = (if "release" in args or "danger" in args: "nimcache/r_" else: "nimcache/d_") &
+                      hostOS & "_" & hostCPU
+
+  bundleChecksums(false)
+
+  let usingLibFFI = "nimHasLibFFI" in args
+  if usingLibFFI and not dirExists("dist/libffi"):
+    installDeps("libffi")
 
-  copyExe(findStartNim(), 0.thVersion)
-  for i in 0..2:
+  let nimStart = findStartNim().quoteShell()
+  let times = 2 - ord(skipIntegrityCheck)
+  for i in 0..times:
+    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 = ""
+    var nimi = i.thVersion
+    if i == 0:
+      nimi = nimStart
+      extraOption.add " --skipUserCfg --skipParentCfg -d:nimKochBootstrap"
+
+      # --noNimblePath precludes nimble packages as dependencies to the compiler,
+      # so libffi is not "installed as a nimble package"
+      if usingLibFFI: extraOption.add " --path:./dist"
+        # The configs are skipped for bootstrap
+        # (1st iteration) to prevent newer flags from breaking bootstrap phase.
+      let ret = execCmdEx(nimStart & " --version")
+      doAssert ret.exitCode == 0
+      let version = ret.output.splitLines[0]
+      if version.startsWith "Nim Compiler Version 0.20.0":
+        extraOption.add " --lib:lib" # see https://github.com/nim-lang/Nim/pull/14291
+
+    # in order to use less memory, we split the build into two steps:
+    # --compileOnly produces a $project.json file and does not run GCC/Clang.
+    # jsonbuild then uses the $project.json file to build the Nim binary.
+    exec "$# $# $# --nimcache:$# $# --noNimblePath --compileOnly compiler" / "nim.nim" %
+      [nimi, bootOptions, extraOption, smartNimcache, args]
+    exec "$# jsonscript --noNimblePath --nimcache:$# $# compiler" / "nim.nim" %
+      [nimi, smartNimcache, args]
+
     if sameFileContent(output, i.thVersion):
       copyExe(output, finalDest)
       echo "executables are equal: SUCCESS!"
       return
     copyExe(output, (i+1).thVersion)
   copyExe(output, finalDest)
-  when not defined(windows): echo "[Warning] executables are still not equal"
+  when not defined(windows):
+    if not skipIntegrityCheck:
+      echo "[Warning] executables are still not equal"
 
 # -------------- clean --------------------------------------------------------
 
@@ -408,9 +453,10 @@ proc winReleaseArch(arch: string) =
   withMingw r"..\mingw" & arch & r"\bin":
     # 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 zip -d:release"
+    inFold "winrelease koch":
+      nimexec "c --cpu:$# koch" % cpu
+    kochExecFold("winrelease boot", "boot -d:release --cpu:$#" % cpu)
+    kochExecFold("winrelease zip", "zip -d:release")
     overwriteFile r"build\nim-$#.zip" % VersionAsString,
              r"web\upload\download\nim-$#_x$#.zip" % [VersionAsString, arch]
 
@@ -419,16 +465,19 @@ proc winRelease*() =
   # anymore!
   # Build -docs file:
   when true:
-    web(gaCode)
+    inFold "winrelease buildDocs":
+      buildDocs(gaCode)
     withDir "web/upload/" & VersionAsString:
-      exec "7z a -tzip docs-$#.zip *.html" % VersionAsString
+      inFold "winrelease zipdocs":
+        exec "7z a -tzip docs-$#.zip *.html" % VersionAsString
     overwriteFile "web/upload/$1/docs-$1.zip" % VersionAsString,
                   "web/upload/download/docs-$1.zip" % VersionAsString
   when true:
-    csource("-d:release")
-  when true:
+    inFold "winrelease csource":
+      csource("-d:danger")
+  when sizeof(pointer) == 4:
     winReleaseArch "32"
-  when true:
+  when sizeof(pointer) == 8:
     winReleaseArch "64"
 
 # -------------- tests --------------------------------------------------------
@@ -436,16 +485,11 @@ proc winRelease*() =
 template `|`(a, b): string = (if a.len > 0: a else: b)
 
 proc tests(args: string) =
-  # we compile the tester with taintMode:on to have a basic
-  # taint mode test :-)
-  nimexec "cc --taintMode:on --opt:speed tests/testament/tester"
-  # Since tests take a long time (on my machine), and we want to defy Murhpys
-  # law - lets make sure the compiler really is freshly compiled!
-  nimexec "c --lib:lib -d:release --opt:speed compiler/nim.nim"
-  let tester = quoteShell(getCurrentDir() / "tests/testament/tester".exe)
-  let success = tryExec tester & " " & (args|"all")
-  if not existsEnv("TRAVIS") and not existsEnv("APPVEYOR"):
-    exec tester & " html"
+  nimexec "cc --opt:speed testament/testament"
+  var testCmd = quoteShell(getCurrentDir() / "testament/testament".exe)
+  testCmd.add " " & quoteShell("--nim:" & findNim())
+  testCmd.add " " & (args|"all")
+  let success = tryExec testCmd
   if not success:
     quit("tests failed", QuitFailure)
 
@@ -464,14 +508,21 @@ proc temp(args: string) =
       result[1].add " " & quoteShell(args[i])
       inc i
 
-  var output = "compiler" / "nim".exe
-  var finalDest = "bin" / "nim_temp".exe
-  # 125 is the magic number to tell git bisect to skip the current
-  # commit.
-  let (bootArgs, programArgs) = splitArgs(args)
-  let nimexec = findNim()
-  exec(nimexec & " c -d:debug " & bootArgs & " compiler" / "nim", 125)
+  bundleChecksums(false)
+
+  let d = getAppDir()
+  let output = d / "compiler" / "nim".exe
+  let finalDest = d / "bin" / "nim_temp".exe
+  # 125 is the magic number to tell git bisect to skip the current commit.
+  var (bootArgs, programArgs) = splitArgs(args)
+  if "doc" notin programArgs and
+      "threads" notin programArgs and
+      "js" notin programArgs and "rst2html" notin programArgs:
+    bootArgs = " -d:leanCompiler" & bootArgs
+  let nimexec = findNim().quoteShell()
+  exec(nimexec & " c -d:debug --debugger:native -d:nimBetterRun " & bootArgs & " " & (d / "compiler" / "nim"), 125)
   copyExe(output, finalDest)
+  setCurrentDir(origDir)
   if programArgs.len > 0: exec(finalDest & " " & programArgs)
 
 proc xtemp(cmd: string) =
@@ -485,28 +536,142 @@ proc xtemp(cmd: string) =
   finally:
     copyExe(d / "bin" / "nim_backup".exe, d / "bin" / "nim".exe)
 
-proc pushCsources() =
-  if not dirExists("../csources/.git"):
-    quit "[Error] no csources git repository found"
-  csource("-d:release")
-  let cwd = getCurrentDir()
+proc icTest(args: string) =
+  temp("")
+  let inp = os.parseCmdLine(args)[0]
+  let content = readFile(inp)
+  let nimExe = getAppDir() / "bin" / "nim_temp".exe
+  var i = 0
+  for fragment in content.split("#!EDIT!#"):
+    let file = inp.replace(".nim", "_temp.nim")
+    writeFile(file, fragment)
+    var cmd = nimExe & " cpp --ic:on -d:nimIcIntegrityChecks --listcmd "
+    if i == 0:
+      cmd.add "-f "
+    cmd.add quoteShell(file)
+    exec(cmd)
+    inc i
+
+proc buildDrNim(args: string) =
+  if not dirExists("dist/nimz3"):
+    exec("git clone https://github.com/zevv/nimz3.git dist/nimz3")
+  when defined(windows):
+    if not dirExists("dist/dlls"):
+      exec("git clone -q https://github.com/nim-lang/dlls.git dist/dlls")
+    copyExe("dist/dlls/libz3.dll", "bin/libz3.dll")
+    execFold("build drnim", "nim c -o:$1 $2 drnim/drnim" % ["bin/drnim".exe, args])
+  else:
+    if not dirExists("dist/z3"):
+      exec("git clone -q https://github.com/Z3Prover/z3.git dist/z3")
+      withDir("dist/z3"):
+        exec("git fetch")
+        exec("git checkout " & Z3StableCommit)
+        createDir("build")
+        withDir("build"):
+          exec("""cmake -DZ3_BUILD_LIBZ3_SHARED=FALSE -G "Unix Makefiles" ../""")
+          exec("make -j4")
+    execFold("build drnim", "nim cpp --dynlibOverride=libz3 -o:$1 $2 drnim/drnim" % ["bin/drnim".exe, args])
+  # always run the tests for now:
+  exec("testament/testament".exe & " --nim:" & "drnim".exe & " pat drnim/tests")
+
+
+proc hostInfo(): string =
+  "hostOS: $1, hostCPU: $2, int: $3, float: $4, cpuEndian: $5, cwd: $6" %
+    [hostOS, hostCPU, $int.sizeof, $float.sizeof, $cpuEndian, getCurrentDir()]
+
+proc runCI(cmd: string) =
+  doAssert cmd.len == 0, cmd # avoid silently ignoring
+  echo "runCI: ", cmd
+  echo hostInfo()
+  # boot without -d:nimHasLibFFI to make sure this still works
+  # `--lib:lib` is needed for bootstrap on openbsd, for reasons described in
+  # https://github.com/nim-lang/Nim/pull/14291 (`getAppFilename` bugsfor older nim on openbsd).
+  kochExecFold("Boot Nim ORC", "boot -d:release -d:nimStrictMode --lib:lib")
+
+  when false: # debugging: when you need to run only 1 test in CI, use something like this:
+    execFold("debugging test", "nim r tests/stdlib/tosproc.nim")
+    doAssert false, "debugging only"
+
+  ## build nimble early on to enable remainder to depend on it if needed
+  kochExecFold("Build Nimble", "nimble")
+
+  execFold("Install smtp", "nimble install smtp -y")
+
+  let batchParam = "--batch:$1" % "NIM_TESTAMENT_BATCH".getEnv("_")
+  if getEnv("NIM_TEST_PACKAGES", "0") == "1":
+    nimCompileFold("Compile testament", "testament/testament.nim", options = "-d:release")
+    execFold("Test selected Nimble packages", "testament $# pcat nimble-packages" % batchParam)
+  else:
+    testTools()
+
+    for a in "zip opengl sdl1 jester@#head".split:
+      let buildDeps = "build"/"deps" # xxx factor pending https://github.com/timotheecour/Nim/issues/616
+      # if this gives `Additional info: "build/deps" [OSError]`, make sure nimble is >= v0.12.0,
+      # otherwise `absolutePath` is needed, refs https://github.com/nim-lang/nimble/issues/901
+      execFold("", "nimble install -y --nimbleDir:$# $#" % [buildDeps.quoteShell, a])
+
+    ## run tests
+    execFold("Test nimscript", "nim e tests/test_nimscript.nims")
+    when defined(windows):
+      execFold("Compile tester", "nim c --usenimcache -d:nimCoroutines --os:genode -d:posix --compileOnly testament/testament")
+
+    # main bottleneck here
+    # xxx: even though this is the main bottleneck, we could speedup the rest via batching with `--batch`.
+    # BUG: with initOptParser, `--batch:'' all` interprets `all` as the argument of --batch, pending bug #14343
+    execFold("Run tester", "nim c -r --putenv:NIM_TESTAMENT_REMOTE_NETWORKING:1 -d:nimStrictMode testament/testament $# all -d:nimCoroutines" % batchParam)
+
+    block: # nimHasLibFFI:
+      when defined(posix): # windows can be handled in future PR's
+        installDeps("libffi")
+        const nimFFI = "bin/nim.ctffi"
+        # no need to bootstrap with koch boot (would be slower)
+        let backend = if doUseCpp(): "cpp" else: "c"
+        execFold("build with -d:nimHasLibFFI", "nim $1 -d:release --noNimblePath -d:nimHasLibFFI --path:./dist -o:$2 compiler/nim.nim" % [backend, nimFFI])
+        execFold("test with -d:nimHasLibFFI", "$1 $2 -r testament/testament --nim:$1 r tests/misc/trunner.nim -d:nimTrunnerFfi" % [nimFFI, backend])
+
+    execFold("Run nimdoc tests", "nim r nimdoc/tester")
+    execFold("Run rst2html tests", "nim r nimdoc/rsttester")
+    execFold("Run nimpretty tests", "nim r nimpretty/tester.nim")
+    when defined(posix):
+      # refs #18385, build with -d:release instead of -d:danger for testing
+      # We could also skip building nimsuggest in buildTools, or build it with -d:release
+      # in bundleNimsuggest depending on some environment variable when we are in CI. One advantage
+      # of rebuilding is this won't affect bin/nimsuggest when running runCI locally
+      execFold("build nimsuggest_testing", "nim c -o:bin/nimsuggest_testing -d:release nimsuggest/nimsuggest")
+      execFold("Run nimsuggest tests", "nim r nimsuggest/tester")
+
+    kochExecFold("Testing booting in refc", "boot -d:release --mm:refc -d:nimStrictMode --lib:lib")
+
+
+proc testUnixInstall(cmdLineRest: string) =
+  csource("-d:danger" & cmdLineRest)
+  xz(false, cmdLineRest)
+  let oldCurrentDir = getCurrentDir()
   try:
-    copyDir("build/c_code", "../csources/c_code")
-    copyFile("build/build.sh", "../csources/build.sh")
-    copyFile("build/build.bat", "../csources/build.bat")
-    copyFile("build/build64.bat", "../csources/build64.bat")
-    copyFile("build/makefile", "../csources/makefile")
-
-    setCurrentDir("../csources")
-    for kind, path in walkDir("c_code"):
-      if kind == pcDir:
-        exec("git add " & path / "*.c")
-    exec("git commit -am \"updated csources to version " & NimVersion & "\"")
-    exec("git push origin master")
-    exec("git tag -am \"Version $1\" v$1" % NimVersion)
-    exec("git push origin v$1" % NimVersion)
+    let destDir = getTempDir()
+    copyFile("build/nim-$1.tar.xz" % VersionAsString,
+             destDir / "nim-$1.tar.xz" % VersionAsString)
+    setCurrentDir(destDir)
+    execCleanPath("tar -xJf nim-$1.tar.xz" % VersionAsString)
+    setCurrentDir("nim-$1" % VersionAsString)
+    execCleanPath("sh build.sh")
+    # first test: try if './bin/nim --version' outputs something sane:
+    let output = execProcess("./bin/nim --version").splitLines
+    if output.len > 0 and output[0].contains(VersionAsString):
+      echo "Version check: success"
+      execCleanPath("./bin/nim c koch.nim")
+      execCleanPath("./koch boot -d:release", destDir / "bin")
+      # check the docs build:
+      execCleanPath("./koch docs", destDir / "bin")
+      # check nimble builds:
+      execCleanPath("./koch tools")
+      # check the tests work:
+      putEnv("NIM_EXE_NOT_IN_PATH", "NOT_IN_PATH")
+      execCleanPath("./koch tests --nim:bin/nim cat megatest", destDir / "bin")
+    else:
+      echo "Version check: failure"
   finally:
-    setCurrentDir(cwd)
+    setCurrentDir oldCurrentDir
 
 proc valgrind(cmd: string) =
   # somewhat hacky: '=' sign means "pass to valgrind" else "pass to Nim"
@@ -530,44 +695,86 @@ proc valgrind(cmd: string) =
   let supp = getAppDir() / "tools" / "nimgrind.supp"
   exec("valgrind --suppressions=" & supp & valcmd)
 
-proc showHelp() =
+proc showHelp(success: bool) =
   quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)),
-                   CompileDate, CompileTime], QuitSuccess)
+                   CompileDate, CompileTime], if success: QuitSuccess else: QuitFailure)
+
+proc branchDone() =
+  let thisBranch = execProcess("git symbolic-ref --short HEAD").strip()
+  if thisBranch != "devel" and thisBranch != "":
+    exec("git checkout devel")
+    exec("git branch -D " & thisBranch)
+    exec("git pull --rebase")
 
 when isMainModule:
   var op = initOptParser()
-  op.next()
-  case op.kind
-  of cmdLongOption, cmdShortOption: showHelp()
-  of cmdArgument:
-    case normalize(op.key)
-    of "boot": boot(op.cmdLineRest)
-    of "clean": clean(op.cmdLineRest)
-    of "web": web(op.cmdLineRest)
-    of "doc", "docs": web("--onlyDocs " & op.cmdLineRest)
-    of "json2": web("--json2 " & op.cmdLineRest)
-    of "website": website(op.cmdLineRest & gaCode)
-    of "web0":
-      # undocumented command for Araq-the-merciful:
-      web(op.cmdLineRest & gaCode)
-    of "pdf": pdf()
-    of "csource", "csources": csource(op.cmdLineRest)
-    of "zip": zip(op.cmdLineRest)
-    of "xz": xz(op.cmdLineRest)
-    of "nsis": nsis(op.cmdLineRest)
-    of "geninstall": geninstall(op.cmdLineRest)
-    of "distrohelper": geninstall()
-    of "install": install(op.cmdLineRest)
-    of "testinstall": testUnixInstall()
-    of "test", "tests": tests(op.cmdLineRest)
-    of "temp": temp(op.cmdLineRest)
-    of "xtemp": xtemp(op.cmdLineRest)
-    #of "winrelease": winRelease()
-    of "wintools": bundleWinTools()
-    of "nimble": buildNimble(existsDir(".git"))
-    of "nimsuggest": bundleNimsuggest(buildExe=true)
-    of "tools": buildTools(existsDir(".git"))
-    of "pushcsource", "pushcsources": pushCsources()
-    of "valgrind": valgrind(op.cmdLineRest)
-    else: showHelp()
-  of cmdEnd: showHelp()
+  var
+    latest = false
+    localDocsOnly = false
+    localDocsOut = ""
+    skipIntegrityCheck = false
+  while true:
+    op.next()
+    case op.kind
+    of cmdLongOption, cmdShortOption:
+      case normalize(op.key)
+      of "help", "h": showHelp(success = true)
+      of "latest": latest = true
+      of "stable": latest = false
+      of "nim": nimExe = op.val.absolutePath # absolute so still works with changeDir
+      of "localdocs":
+        localDocsOnly = true
+        if op.val.len > 0:
+          localDocsOut = op.val.absolutePath
+      of "skipintegritycheck":
+        skipIntegrityCheck = true
+      else: showHelp(success = false)
+    of cmdArgument:
+      case normalize(op.key)
+      of "boot": boot(op.cmdLineRest, skipIntegrityCheck)
+      of "clean": clean(op.cmdLineRest)
+      of "doc", "docs": buildDocs(op.cmdLineRest & " --d:nimPreviewSlimSystem " & paCode, localDocsOnly, localDocsOut)
+      of "doc0", "docs0":
+        # undocumented command for Araq-the-merciful:
+        buildDocs(op.cmdLineRest & gaCode)
+      of "pdf": buildPdfDoc(op.cmdLineRest, "doc/pdf")
+      of "csource", "csources": csource(op.cmdLineRest)
+      of "zip": zip(latest, op.cmdLineRest)
+      of "xz": xz(latest, op.cmdLineRest)
+      of "nsis": nsis(latest, op.cmdLineRest)
+      of "geninstall": geninstall(op.cmdLineRest)
+      of "distrohelper": geninstall()
+      of "install": install(op.cmdLineRest)
+      of "testinstall": testUnixInstall(op.cmdLineRest)
+      of "installdeps": installDeps(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(op.cmdLineRest)
+      of "nimble": bundleNimbleExe(latest, op.cmdLineRest)
+      of "atlas": bundleAtlasExe(latest, op.cmdLineRest)
+      of "nimsuggest": bundleNimsuggest(op.cmdLineRest)
+      # toolsNoNimble is kept for backward compatibility with build scripts
+      of "toolsnonimble", "toolsnoexternal":
+        buildTools(op.cmdLineRest)
+      of "tools":
+        buildTools(op.cmdLineRest)
+        bundleNimbleExe(latest, op.cmdLineRest)
+        bundleAtlasExe(latest, op.cmdLineRest)
+      of "checksums":
+        bundleChecksums(latest)
+      of "pushcsource":
+        quit "use this instead: https://github.com/nim-lang/csources_v1/blob/master/push_c_code.nim"
+      of "valgrind": valgrind(op.cmdLineRest)
+      of "c2nim": bundleC2nim(op.cmdLineRest)
+      of "drnim": buildDrNim(op.cmdLineRest)
+      of "fusion":
+        let suffix = if latest: HeadHash else: FusionStableHash
+        exec("nimble install -y fusion@$#" % suffix)
+      of "ic": icTest(op.cmdLineRest)
+      of "branchdone": branchDone()
+      else: showHelp(success = false)
+      break
+    of cmdEnd:
+      showHelp(success = false)
diff --git a/koch.nim.cfg b/koch.nim.cfg
index 1d7acf579..c624b9577 100644
--- a/koch.nim.cfg
+++ b/koch.nim.cfg
@@ -1 +1,7 @@
--d:booting
\ No newline at end of file
+-d:booting
+
+@if openbsd:
+  # because openbsd getAppFilename was broken in v0.20.0
+  # workaround see https://github.com/nim-lang/Nim/pull/14291
+  --lib:lib
+@end
diff --git a/lib/core/allocators.nim b/lib/core/allocators.nim
deleted file mode 100644
index 62f5e9756..000000000
--- a/lib/core/allocators.nim
+++ /dev/null
@@ -1,35 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2017 Nim contributors
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-type
-  Allocator* = ptr object {.inheritable.}
-    alloc*: proc (a: Allocator; size: int; alignment: int = 8): pointer {.nimcall.}
-    dealloc*: proc (a: Allocator; p: pointer; size: int) {.nimcall.}
-    realloc*: proc (a: Allocator; p: pointer; oldSize, newSize: int): pointer {.nimcall.}
-
-var
-  currentAllocator {.threadvar.}: Allocator
-
-proc getCurrentAllocator*(): Allocator =
-  result = currentAllocator
-
-proc setCurrentAllocator*(a: Allocator) =
-  currentAllocator = a
-
-proc alloc*(size: int; alignment: int = 8): pointer =
-  let a = getCurrentAllocator()
-  result = a.alloc(a, size, alignment)
-
-proc dealloc*(p: pointer; size: int) =
-  let a = getCurrentAllocator()
-  a.dealloc(a, p, size)
-
-proc realloc*(p: pointer; oldSize, newSize: int): pointer =
-  let a = getCurrentAllocator()
-  result = a.realloc(a, p, oldSize, newSize)
diff --git a/lib/core/hotcodereloading.nim b/lib/core/hotcodereloading.nim
new file mode 100644
index 000000000..3a876885c
--- /dev/null
+++ b/lib/core/hotcodereloading.nim
@@ -0,0 +1,41 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2019 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Unstable API.
+
+when defined(hotcodereloading):
+  import
+    std/macros
+
+  template beforeCodeReload*(body: untyped) =
+    hcrAddEventHandler(true, proc = body) {.executeOnReload.}
+
+  template afterCodeReload*(body: untyped) =
+    hcrAddEventHandler(false, proc = body) {.executeOnReload.}
+
+  macro hasModuleChanged*(module: typed): untyped =
+    if module.kind != nnkSym or module.symKind != nskModule:
+      error "hasModuleChanged expects a module symbol", module
+    return newCall(bindSym"hcrHasModuleChanged", newLit(module.signatureHash))
+
+  proc hasAnyModuleChanged*(): bool = hcrReloadNeeded()
+
+  when not defined(js):
+    template performCodeReload* =
+      when isMainModule:
+        {.warning: "Code residing in the main module will not be changed from calling a code-reload".}
+      hcrPerformCodeReload()
+  else:
+    template performCodeReload* = discard
+else:
+  template beforeCodeReload*(body: untyped) = discard
+  template afterCodeReload*(body: untyped) = discard
+  template hasModuleChanged*(module: typed): bool = false
+  proc hasAnyModuleChanged*(): bool = false
+  template performCodeReload*() = discard
diff --git a/lib/core/locks.nim b/lib/core/locks.nim
index f9add0037..523727479 100644
--- a/lib/core/locks.nim
+++ b/lib/core/locks.nim
@@ -9,64 +9,82 @@
 
 ## This module contains Nim's support for locks and condition vars.
 
-const insideRLocksModule = false
-include "system/syslocks"
+#[
+for js, for now we treat locks as noop's to avoid pushing `when defined(js)`
+in client code that uses locks.
+]#
+
+when not compileOption("threads") and not defined(nimdoc):
+  when false: # fix #12330
+    {.error: "Locks requires --threads:on option.".}
+
+import std/private/syslocks
 
 type
   Lock* = SysLock ## Nim lock; whether this is re-entrant
                   ## or not is unspecified!
   Cond* = SysCond ## Nim condition variable
 
-{.deprecated: [TLock: Lock, TCond: Cond].}
-
 {.push stackTrace: off.}
 
+
+proc `$`*(lock: Lock): string =
+  # workaround bug #14873
+  result = "()"
+
 proc initLock*(lock: var Lock) {.inline.} =
   ## Initializes the given lock.
-  initSysLock(lock)
+  when not defined(js):
+    initSysLock(lock)
 
-proc deinitLock*(lock: var Lock) {.inline.} =
+proc deinitLock*(lock: Lock) {.inline.} =
   ## Frees the resources associated with the lock.
   deinitSys(lock)
 
-proc tryAcquire*(lock: var Lock): bool =
+proc tryAcquire*(lock: var Lock): bool {.inline.} =
   ## Tries to acquire the given lock. Returns `true` on success.
   result = tryAcquireSys(lock)
 
-proc acquire*(lock: var Lock) =
+proc acquire*(lock: var Lock) {.inline.} =
   ## Acquires the given lock.
-  acquireSys(lock)
+  when not defined(js):
+    acquireSys(lock)
 
-proc release*(lock: var Lock) =
+proc release*(lock: var Lock) {.inline.} =
   ## Releases the given lock.
-  releaseSys(lock)
+  when not defined(js):
+    releaseSys(lock)
 
 
 proc initCond*(cond: var Cond) {.inline.} =
   ## Initializes the given condition variable.
   initSysCond(cond)
 
-proc deinitCond*(cond: var Cond) {.inline.} =
-  ## Frees the resources associated with the lock.
+proc deinitCond*(cond: Cond) {.inline.} =
+  ## Frees the resources associated with the condition variable.
   deinitSysCond(cond)
 
 proc wait*(cond: var Cond, lock: var Lock) {.inline.} =
-  ## waits on the condition variable `cond`.
+  ## Waits on the condition variable `cond`.
   waitSysCond(cond, lock)
 
 proc signal*(cond: var Cond) {.inline.} =
-  ## sends a signal to the condition variable `cond`.
+  ## Sends a signal to the condition variable `cond`.
   signalSysCond(cond)
 
+proc broadcast*(cond: var Cond) {.inline.} =
+  ## Unblocks all threads currently blocked on the
+  ## specified condition variable `cond`.
+  broadcastSysCond(cond)
+
 template withLock*(a: Lock, body: untyped) =
   ## Acquires the given lock, executes the statements in body and
   ## releases the lock after the statements finish executing.
-  mixin acquire, release
-  a.acquire()
+  acquire(a)
   {.locks: [a].}:
     try:
       body
     finally:
-      a.release()
+      release(a)
 
 {.pop.}
diff --git a/lib/core/macrocache.nim b/lib/core/macrocache.nim
index bd48b5bd4..39999fa11 100644
--- a/lib/core/macrocache.nim
+++ b/lib/core/macrocache.nim
@@ -7,39 +7,238 @@
 #    distribution, for details about the copyright.
 #
 
-## This module provides an API for macros that need to collect compile
-## time information across module boundaries in global variables.
-## Starting with version 0.19 of Nim this is not directly supported anymore
-## as it breaks incremental compilations.
-## Instead the API here needs to be used. See XXX (wikipedia page) for a
-## theoretical foundation behind this.
+## This module provides an API for macros to collect compile-time information
+## across module boundaries. It should be used instead of global `{.compileTime.}`
+## variables as those break incremental compilation.
+##
+## The main feature of this module is that if you create `CacheTable`s or
+## any other `Cache` types with the same name in different modules, their
+## content will be shared, meaning that you can fill a `CacheTable` in
+## one module, and iterate over its contents in another.
+
+runnableExamples:
+  import std/macros
+
+  const mcTable = CacheTable"myTable"
+  const mcSeq = CacheSeq"mySeq"
+  const mcCounter = CacheCounter"myCounter"
+
+  static:
+    # add new key "val" with the value `myval`
+    let myval = newLit("hello ic")
+    mcTable["val"] = myval
+    assert mcTable["val"].kind == nnkStrLit
+
+  # Can access the same cache from different static contexts
+  # All the information is retained
+  static:
+    # get value from `mcTable` and add it to `mcSeq`
+    mcSeq.add(mcTable["val"])
+    assert mcSeq.len == 1
+
+  static:
+    assert mcSeq[0].strVal == "hello ic"
+
+    # increase `mcCounter` by 3
+    mcCounter.inc(3)
+    assert mcCounter.value == 3
+
 
 type
   CacheSeq* = distinct string
+    ## Compile-time sequence of `NimNode`s.
   CacheTable* = distinct string
+    ## Compile-time table of key-value pairs.
+    ##
+    ## Keys are `string`s and values are `NimNode`s.
   CacheCounter* = distinct string
+    ## Compile-time counter, uses `int` for storing the count.
+
+proc value*(c: CacheCounter): int {.magic: "NccValue".} =
+  ## Returns the value of a counter `c`.
+  runnableExamples:
+    static:
+      let counter = CacheCounter"valTest"
+      # default value is 0
+      assert counter.value == 0
+
+      inc counter
+      assert counter.value == 1
+
+proc inc*(c: CacheCounter; by = 1) {.magic: "NccInc".} =
+  ## Increments the counter `c` with the value `by`.
+  runnableExamples:
+    static:
+      let counter = CacheCounter"incTest"
+      inc counter
+      inc counter, 5
+
+      assert counter.value == 6
+
+proc add*(s: CacheSeq; value: NimNode) {.magic: "NcsAdd".} =
+  ## Adds `value` to `s`.
+  runnableExamples:
+    import std/macros
+    const mySeq = CacheSeq"addTest"
+
+    static:
+      mySeq.add(newLit(5))
+      mySeq.add(newLit("hello ic"))
+
+      assert mySeq.len == 2
+      assert mySeq[1].strVal == "hello ic"
+
+proc incl*(s: CacheSeq; value: NimNode) {.magic: "NcsIncl".} =
+  ## Adds `value` to `s`.
+  ##
+  ## .. hint:: This doesn't do anything if `value` is already in `s`.
+  runnableExamples:
+    import std/macros
+    const mySeq = CacheSeq"inclTest"
+
+    static:
+      mySeq.incl(newLit(5))
+      mySeq.incl(newLit(5))
+
+      # still one element
+      assert mySeq.len == 1
+
+proc len*(s: CacheSeq): int {.magic: "NcsLen".} =
+  ## Returns the length of `s`.
+  runnableExamples:
+    import std/macros
 
-proc value*(c: CacheCounter): int {.magic: "NccValue".}
-proc inc*(c: CacheCounter; by = 1) {.magic: "NccInc".}
+    const mySeq = CacheSeq"lenTest"
+    static:
+      let val = newLit("helper")
+      mySeq.add(val)
+      assert mySeq.len == 1
 
-proc add*(s: CacheSeq; value: NimNode) {.magic: "NcsAdd".}
-proc incl*(s: CacheSeq; value: NimNode) {.magic: "NcsIncl".}
-proc len*(s: CacheSeq): int {.magic: "NcsLen".}
-proc `[]`*(s: CacheSeq; i: int): NimNode {.magic: "NcsAt".}
+      mySeq.add(val)
+      assert mySeq.len == 2
+
+proc `[]`*(s: CacheSeq; i: int): NimNode {.magic: "NcsAt".} =
+  ## Returns the `i`th value from `s`.
+  runnableExamples:
+    import std/macros
+
+    const mySeq = CacheSeq"subTest"
+    static:
+      mySeq.add(newLit(42))
+      assert mySeq[0].intVal == 42
+
+proc `[]`*(s: CacheSeq; i: BackwardsIndex): NimNode =
+  ## Returns the `i`th last value from `s`.
+  runnableExamples:
+    import std/macros
+
+    const mySeq = CacheSeq"backTest"
+    static:
+      mySeq &= newLit(42)
+      mySeq &= newLit(7)
+      assert mySeq[^1].intVal == 7  # Last item
+      assert mySeq[^2].intVal == 42 # Second last item
+  s[s.len - int(i)]
 
 iterator items*(s: CacheSeq): NimNode =
+  ## Iterates over each item in `s`.
+  runnableExamples:
+    import std/macros
+    const myseq = CacheSeq"itemsTest"
+
+    static:
+      myseq.add(newLit(5))
+      myseq.add(newLit(42))
+
+      for val in myseq:
+        # check that all values in `myseq` are int literals
+        assert val.kind == nnkIntLit
+
   for i in 0 ..< len(s): yield s[i]
 
-proc `[]=`*(t: CacheTable; key: string, value: NimNode) {.magic: "NctPut".}
-  ## 'key' has to be unique!
+proc `[]=`*(t: CacheTable; key: string, value: NimNode) {.magic: "NctPut".} =
+  ## Inserts a `(key, value)` pair into `t`.
+  ##
+  ## .. warning:: `key` has to be unique! Assigning `value` to a `key` that is already
+  ##   in the table will result in a compiler error.
+  runnableExamples:
+    import std/macros
+
+    const mcTable = CacheTable"subTest"
+    static:
+      # assign newLit(5) to the key "value"
+      mcTable["value"] = newLit(5)
+
+      # check that we can get the value back
+      assert mcTable["value"].kind == nnkIntLit
 
-proc len*(t: CacheTable): int {.magic: "NctLen".}
-proc `[]`*(t: CacheTable; key: string): NimNode {.magic: "NctGet".}
+proc len*(t: CacheTable): int {.magic: "NctLen".} =
+  ## Returns the number of elements in `t`.
+  runnableExamples:
+    import std/macros
+
+    const dataTable = CacheTable"lenTest"
+    static:
+      dataTable["key"] = newLit(5)
+      assert dataTable.len == 1
+
+proc `[]`*(t: CacheTable; key: string): NimNode {.magic: "NctGet".} =
+  ## Retrieves the `NimNode` value at `t[key]`.
+  runnableExamples:
+    import std/macros
+
+    const mcTable = CacheTable"subTest"
+    static:
+      mcTable["toAdd"] = newStmtList()
+
+      # get the NimNode back
+      assert mcTable["toAdd"].kind == nnkStmtList
+
+proc hasKey*(t: CacheTable; key: string): bool =
+  ## Returns true if `key` is in the table `t`.
+  ##
+  ## See also:
+  ## * [contains proc][contains(CacheTable, string)] for use with the `in` operator
+  runnableExamples:
+    import std/macros
+    const mcTable = CacheTable"hasKeyEx"
+    static:
+      assert not mcTable.hasKey("foo")
+      mcTable["foo"] = newEmptyNode()
+      # Will now be true since we inserted a value
+      assert mcTable.hasKey("foo")
+  discard "Implemented in vmops"
+
+proc contains*(t: CacheTable; key: string): bool {.inline.} =
+  ## Alias of [hasKey][hasKey(CacheTable, string)] for use with the `in` operator.
+  runnableExamples:
+    import std/macros
+    const mcTable = CacheTable"containsEx"
+    static:
+      mcTable["foo"] = newEmptyNode()
+      # Will be true since we gave it a value before
+      assert "foo" in mcTable
+  t.hasKey(key)
 
 proc hasNext(t: CacheTable; iter: int): bool {.magic: "NctHasNext".}
 proc next(t: CacheTable; iter: int): (string, NimNode, int) {.magic: "NctNext".}
 
 iterator pairs*(t: CacheTable): (string, NimNode) =
+  ## Iterates over all `(key, value)` pairs in `t`.
+  runnableExamples:
+    import std/macros
+    const mytabl = CacheTable"values"
+
+    static:
+      mytabl["intVal"] = newLit(5)
+      mytabl["otherVal"] = newLit(6)
+      for key, val in mytabl:
+        # make sure that we actually get the same keys
+        assert key in ["intVal", "otherVal"]
+
+        # all vals are int literals
+        assert val.kind == nnkIntLit
+
   var h = 0
   while hasNext(t, h):
     let (a, b, h2) = next(t, h)
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 1f251b73e..7646b165c 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -8,12 +8,23 @@
 #
 
 include "system/inclrtl"
+import std/private/since
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, formatfloat]
+
 
 ## This module contains the interface to the compiler's abstract syntax
 ## tree (`AST`:idx:). Macros operate on this tree.
+##
+## See also:
+## * `macros tutorial <tut3.html>`_
+## * `macros section in Nim manual <manual.html#macros>`_
 
 ## .. include:: ../../doc/astspec.txt
 
+## .. importdoc:: system.nim
+
 # If you look for the implementation of the magic symbol
 # ``{.magic: "Foo".}``, search for `mFoo` and `opcFoo`.
 
@@ -66,21 +77,24 @@ type
     nnkTupleTy, nnkTupleClassTy, nnkTypeClassTy, nnkStaticTy,
     nnkRecList, nnkRecCase, nnkRecWhen,
     nnkRefTy, nnkPtrTy, nnkVarTy,
-    nnkConstTy, nnkMutableTy,
+    nnkConstTy, nnkOutTy,
     nnkDistinctTy,
     nnkProcTy,
     nnkIteratorTy,         # iterator type
-    nnkSharedTy,           # 'shared T'
+    nnkSinkAsgn,
     nnkEnumTy,
     nnkEnumFieldDef,
-    nnkArglist, nnkPattern
-    nnkReturnToken,
+    nnkArgList, nnkPattern
+    nnkHiddenTryStmt,
     nnkClosure,
     nnkGotoState,
     nnkState,
     nnkBreakState,
     nnkFuncDef,
-    nnkTupleConstr
+    nnkTupleConstr,
+    nnkError,  ## erroneous AST node
+    nnkModuleRef, nnkReplayAction, nnkNilRodNode ## internal IC nodes
+    nnkOpenSym
 
   NimNodeKinds* = set[NimNodeKind]
   NimTypeKind* = enum  # some types are no longer used, see ast.nim
@@ -97,11 +111,11 @@ type
     ntyUInt, ntyUInt8, ntyUInt16, ntyUInt32, ntyUInt64,
     ntyUnused0, ntyUnused1, ntyUnused2,
     ntyVarargs,
-    ntyUnused,
+    ntyUncheckedArray,
     ntyError,
     ntyBuiltinTypeClass, ntyUserTypeClass, ntyUserTypeClassInst,
     ntyCompositeTypeClass, ntyInferred, ntyAnd, ntyOr, ntyNot,
-    ntyAnything, ntyStatic, ntyFromExpr, ntyOpt, ntyVoid
+    ntyAnything, ntyStatic, ntyFromExpr, ntyOptDeprecated, ntyVoid
 
   TNimTypeKinds* {.deprecated.} = set[NimTypeKind]
   NimSymKind* = enum
@@ -115,129 +129,174 @@ type
 
   TNimSymKinds* {.deprecated.} = set[NimSymKind]
 
+const
+  nnkMutableTy* {.deprecated.} = nnkOutTy
+  nnkSharedTy* {.deprecated.} = nnkSinkAsgn
+
 type
-  NimIdent* = object of RootObj
-    ## represents a Nim identifier in the AST. **Note**: This is only
+  NimIdent* {.deprecated.} = object of RootObj
+    ## Represents a Nim identifier in the AST. **Note**: This is only
     ## rarely useful, for identifier construction from a string
-    ## use ``ident"abc"``.
+    ## use `ident"abc"`.
 
   NimSymObj = object # hidden
   NimSym* {.deprecated.} = ref NimSymObj
-    ## represents a Nim *symbol* in the compiler; a *symbol* is a looked-up
+    ## Represents a Nim *symbol* in the compiler; a *symbol* is a looked-up
     ## *ident*.
 
 
 const
   nnkLiterals* = {nnkCharLit..nnkNilLit}
+  # see matching set CallNodes below
   nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
-                   nnkCallStrLit}
+                   nnkCallStrLit, nnkHiddenCallConv}
   nnkPragmaCallKinds = {nnkExprColonExpr, nnkCall, nnkCallStrLit}
 
-proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.}
-  ## constructs an identifier from the string `s`
-  ## **Deprecated since version 0.18.0**: Use ``ident`` or ``newIdentNode`` instead.
+{.push warnings: off.}
 
-proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.}
-  ## constructs an identifier from the string `s`
-  ## **Deprecated since version 0.18.1**; Use ``ident`` or ``newIdentNode`` instead.
+proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated:
+  "Deprecated since version 0.18.0: Use 'ident' or 'newIdentNode' instead.".}
+  ## Constructs an identifier from the string `s`.
 
-proc `==`*(a, b: NimIdent): bool {.magic: "EqIdent", noSideEffect, deprecated.}
-  ## compares two Nim identifiers
-  ## **Deprecated since version 0.18.1**; Use ``==`` on ``NimNode`` instead.
+proc `==`*(a, b: NimIdent): bool {.magic: "EqIdent", noSideEffect, deprecated:
+  "Deprecated since version 0.18.1; Use '==' on 'NimNode' instead.".}
+  ## Compares two Nim identifiers.
 
 proc `==`*(a, b: NimNode): bool {.magic: "EqNimrodNode", noSideEffect.}
-  ## compares two Nim nodes
+  ## Compare two Nim nodes. Return true if nodes are structurally
+  ## equivalent. This means two independently created nodes can be equal.
 
-proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect, deprecated.}
-  ## compares two Nim symbols
-  ## **Deprecated since version 0.18.1**; Use ```==`(NimNode,NimNode)`` instead.
+proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect, deprecated:
+  "Deprecated since version 0.18.1; Use '==(NimNode, NimNode)' instead.".}
+  ## Compares two Nim symbols.
 
+{.pop.}
 
 proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} =
-  ## compares two Nim nodes' types. Return true if the types are the same,
-  ## eg. true when comparing alias with original type.
+  ## Compares two Nim nodes' types. Return true if the types are the same,
+  ## e.g. true when comparing alias with original type.
   discard
 
 proc len*(n: NimNode): int {.magic: "NLen", noSideEffect.}
-  ## returns the number of children of `n`.
+  ## Returns the number of children of `n`.
 
 proc `[]`*(n: NimNode, i: int): NimNode {.magic: "NChild", noSideEffect.}
-  ## get `n`'s `i`'th child.
+  ## Get `n`'s `i`'th child.
 
 proc `[]`*(n: NimNode, i: BackwardsIndex): NimNode = n[n.len - i.int]
-  ## get `n`'s `i`'th child.
+  ## Get `n`'s `i`'th child.
+
+template `^^`(n: NimNode, i: untyped): untyped =
+  (when i is BackwardsIndex: n.len - int(i) else: int(i))
+
+proc `[]`*[T, U: Ordinal](n: NimNode, x: HSlice[T, U]): seq[NimNode] =
+  ## Slice operation for NimNode.
+  ## Returns a seq of child of `n` who inclusive range `[n[x.a], n[x.b]]`.
+  let xa = n ^^ x.a
+  let L = (n ^^ x.b) - xa + 1
+  result = newSeq[NimNode](L)
+  for i in 0..<L:
+    result[i] = n[i + xa]
 
 proc `[]=`*(n: NimNode, i: int, child: NimNode) {.magic: "NSetChild",
   noSideEffect.}
-  ## set `n`'s `i`'th child to `child`.
+  ## Set `n`'s `i`'th child to `child`.
 
 proc `[]=`*(n: NimNode, i: BackwardsIndex, child: NimNode) =
-  ## set `n`'s `i`'th child to `child`.
+  ## Set `n`'s `i`'th child to `child`.
   n[n.len - i.int] = child
 
+template `or`*(x, y: NimNode): NimNode =
+  ## Evaluate `x` and when it is not an empty node, return
+  ## it. Otherwise evaluate to `y`. Can be used to chain several
+  ## expressions to get the first expression that is not empty.
+  ##   ```nim
+  ##   let node = mightBeEmpty() or mightAlsoBeEmpty() or fallbackNode
+  ##   ```
+
+  let arg = x
+  if arg != nil and arg.kind != nnkEmpty:
+    arg
+  else:
+    y
+
 proc add*(father, child: NimNode): NimNode {.magic: "NAdd", discardable,
-  noSideEffect, locks: 0.}
+  noSideEffect.}
   ## Adds the `child` to the `father` node. Returns the
   ## father node so that calls can be nested.
 
 proc add*(father: NimNode, children: varargs[NimNode]): NimNode {.
-  magic: "NAddMultiple", discardable, noSideEffect, locks: 0.}
+  magic: "NAddMultiple", discardable, noSideEffect.}
   ## Adds each child of `children` to the `father` node.
   ## Returns the `father` node so that calls can be nested.
 
 proc del*(father: NimNode, idx = 0, n = 1) {.magic: "NDel", noSideEffect.}
-  ## deletes `n` children of `father` starting at index `idx`.
+  ## Deletes `n` children of `father` starting at index `idx`.
 
 proc kind*(n: NimNode): NimNodeKind {.magic: "NKind", noSideEffect.}
-  ## returns the `kind` of the node `n`.
+  ## Returns the `kind` of the node `n`.
 
 proc intVal*(n: NimNode): BiggestInt {.magic: "NIntVal", noSideEffect.}
+  ## Returns an integer value from any integer literal or enum field symbol.
 
 proc floatVal*(n: NimNode): BiggestFloat {.magic: "NFloatVal", noSideEffect.}
+  ## Returns a float from any floating point literal.
 
-proc ident*(n: NimNode): NimIdent {.magic: "NIdent", noSideEffect, deprecated.} =
-  ## **Deprecated since version 0.18.1**; All functionality is defined on ``NimNode``.
-
-proc symbol*(n: NimNode): NimSym {.magic: "NSymbol", noSideEffect, deprecated.}
-  ## **Deprecated since version 0.18.1**; All functionality is defined on ``NimNode``.
 
-proc getImpl*(s: NimSym): NimNode {.magic: "GetImpl", noSideEffect, deprecated: "use `getImpl: NimNode -> NimNode` instead".}
-
-when defined(nimSymKind):
-  proc symKind*(symbol: NimNode): NimSymKind {.magic: "NSymKind", noSideEffect.}
-  proc getImpl*(symbol: NimNode): NimNode {.magic: "GetImpl", noSideEffect.}
-  proc strVal*(n: NimNode): string  {.magic: "NStrVal", noSideEffect.}
-    ## retrieve the implementation of `symbol`. `symbol` can be a
-    ## routine or a const.
-
-  proc `$`*(i: NimIdent): string {.magic: "NStrVal", noSideEffect, deprecated.}
-    ## converts a Nim identifier to a string
-    ## **Deprecated since version 0.18.1**; Use ``strVal`` instead.
+proc symKind*(symbol: NimNode): NimSymKind {.magic: "NSymKind", noSideEffect.}
+proc getImpl*(symbol: NimNode): NimNode {.magic: "GetImpl", noSideEffect.}
+  ## Returns a copy of the declaration of a symbol or `nil`.
+proc strVal*(n: NimNode): string  {.magic: "NStrVal", noSideEffect.}
+  ## Returns the string value of an identifier, symbol, comment, or string literal.
+  ##
+  ## See also:
+  ## * `strVal= proc<#strVal=,NimNode,string>`_ for setting the string value.
 
-  proc `$`*(s: NimSym): string {.magic: "NStrVal", noSideEffect, deprecated.}
-    ## converts a Nim symbol to a string
-    ## **Deprecated since version 0.18.1**; Use ``strVal`` instead.
+{.push warnings: off.} # silence `deprecated`
 
-else: # bootstrapping substitute
-  proc getImpl*(symbol: NimNode): NimNode =
-    symbol.symbol.getImpl
+proc ident*(n: NimNode): NimIdent {.magic: "NIdent", noSideEffect, deprecated:
+  "Deprecated since version 0.18.1; All functionality is defined on 'NimNode'.".}
 
-  proc strValOld(n: NimNode): string  {.magic: "NStrVal", noSideEffect.}
+proc symbol*(n: NimNode): NimSym {.magic: "NSymbol", noSideEffect, deprecated:
+  "Deprecated since version 0.18.1; All functionality is defined on 'NimNode'.".}
 
-  proc `$`*(s: NimSym): string {.magic: "IdentToStr", noSideEffect.}
+proc getImpl*(s: NimSym): NimNode {.magic: "GetImpl", noSideEffect, deprecated: "use `getImpl: NimNode -> NimNode` instead".}
 
-  proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.}
+proc `$`*(i: NimIdent): string {.magic: "NStrVal", noSideEffect, deprecated:
+  "Deprecated since version 0.18.1; Use 'strVal' instead.".}
+  ## Converts a Nim identifier to a string.
+
+proc `$`*(s: NimSym): string {.magic: "NStrVal", noSideEffect, deprecated:
+  "Deprecated since version 0.18.1; Use 'strVal' instead.".}
+  ## Converts a Nim symbol to a string.
+
+{.pop.}
+
+when (NimMajor, NimMinor, NimPatch) >= (1, 3, 5) or defined(nimSymImplTransform):
+  proc getImplTransformed*(symbol: NimNode): NimNode {.magic: "GetImplTransf", noSideEffect.}
+    ## For a typed proc returns the AST after transformation pass; this is useful
+    ## for debugging how the compiler transforms code (e.g.: `defer`, `for`) but
+    ## note that code transformations are implementation dependent and subject to change.
+    ## See an example in `tests/macros/tmacros_various.nim`.
+
+proc owner*(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect, deprecated.}
+  ## Accepts a node of kind `nnkSym` and returns its owner's symbol.
+  ## The meaning of 'owner' depends on `sym`'s `NimSymKind` and declaration
+  ## context. For top level declarations this is an `nskModule` symbol,
+  ## for proc local variables an `nskProc` symbol, for enum/object fields an
+  ## `nskType` symbol, etc. For symbols without an owner, `nil` is returned.
+  ##
+  ## See also:
+  ## * `symKind proc<#symKind,NimNode>`_ to get the kind of a symbol
+  ## * `getImpl proc<#getImpl,NimNode>`_ to get the declaration of a symbol
 
-  proc strVal*(n: NimNode): string =
-    if n.kind == nnkIdent:
-      $n.ident
-    elif n.kind == nnkSym:
-      $n.symbol
-    else:
-      n.strValOld
+proc isInstantiationOf*(instanceProcSym, genProcSym: NimNode): bool {.magic: "SymIsInstantiationOf", noSideEffect.}
+  ## Checks if a proc symbol is an instance of the generic proc symbol.
+  ## 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
+  ## 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
   ## API can be used to traverse types. Recursive types are flattened for you
   ## so there is no danger of infinite recursions during traversal. To
@@ -245,14 +304,11 @@ proc getType*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
   ## kind of type it is, call `typeKind` on getType's result.
 
 proc getType*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
-  ## Returns the Nim type node for given type. This can be used to turn macro
-  ## typedesc parameter into proper NimNode representing type, since typedesc
-  ## are an exception in macro calls - they are not mapped implicitly to
-  ## NimNode like any other arguments.
+  ## Version of `getType` which takes a `typedesc`.
 
 proc typeKind*(n: NimNode): NimTypeKind {.magic: "NGetType", noSideEffect.}
   ## Returns the type kind of the node 'n' that should represent a type, that
-  ## means the node should have been obtained via ``getType``.
+  ## means the node should have been obtained via `getType`.
 
 proc getTypeInst*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} =
   ## Returns the `type`:idx: of a node in a form matching the way the
@@ -273,12 +329,12 @@ proc getTypeInst*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} =
     doAssert(dumpTypeInst(c) == "Vec[4, float32]")
 
 proc getTypeInst*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
-  ## Version of ``getTypeInst`` which takes a ``typedesc``.
+  ## Version of `getTypeInst` which takes a `typedesc`.
 
 proc getTypeImpl*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} =
   ## Returns the `type`:idx: of a node in a form matching the implementation
-  ## of the type.  Any intermediate aliases are expanded to arrive at the final
-  ## type implementation.  You can instead use ``getImpl`` on a symbol if you
+  ## of the type. Any intermediate aliases are expanded to arrive at the final
+  ## type implementation. You can instead use `getImpl` on a symbol if you
   ## want to find the intermediate aliases.
   runnableExamples:
     type
@@ -293,119 +349,176 @@ proc getTypeImpl*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} =
       newLit(x.getTypeImpl.repr)
     let t = """
 object
-  arr: array[0 .. 3, float32]
-"""
+  arr: array[0 .. 3, float32]"""
     doAssert(dumpTypeImpl(a) == t)
     doAssert(dumpTypeImpl(b) == t)
     doAssert(dumpTypeImpl(c) == t)
 
+proc signatureHash*(n: NimNode): string {.magic: "NSigHash", noSideEffect.}
+  ## Returns a stable identifier derived from the signature of a symbol.
+  ## The signature combines many factors such as the type of the symbol,
+  ## the owning module of the symbol and others. The same identifier is
+  ## used in the back-end to produce the mangled symbol name.
+
+proc symBodyHash*(s: NimNode): string {.noSideEffect.} =
+  ## Returns a stable digest for symbols derived not only from type signature
+  ## and owning module, but also implementation body. All procs/variables used in
+  ## the implementation of this symbol are hashed recursively as well, including
+  ## magics from system module.
+  discard
+
 proc getTypeImpl*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
-  ## Version of ``getTypeImpl`` which takes a ``typedesc``.
+  ## Version of `getTypeImpl` which takes a `typedesc`.
 
 proc `intVal=`*(n: NimNode, val: BiggestInt) {.magic: "NSetIntVal", noSideEffect.}
 proc `floatVal=`*(n: NimNode, val: BiggestFloat) {.magic: "NSetFloatVal", noSideEffect.}
 
-proc `symbol=`*(n: NimNode, val: NimSym) {.magic: "NSetSymbol", noSideEffect, deprecated.}
-  ## **Deprecated since version 0.18.1**; Generate a new ``NimNode`` with ``genSym`` instead.
+{.push warnings: off.}
+
+proc `symbol=`*(n: NimNode, val: NimSym) {.magic: "NSetSymbol", noSideEffect, deprecated:
+  "Deprecated since version 0.18.1; Generate a new 'NimNode' with 'genSym' instead.".}
 
-proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect, deprecated.}
-  ## **Deprecated since version 0.18.1**; Generate a new ``NimNode`` with ``ident(string)`` instead.
+proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect, deprecated:
+  "Deprecated since version 0.18.1; Generate a new 'NimNode' with 'ident(string)' instead.".}
 
-#proc `typ=`*(n: NimNode, typ: typedesc) {.magic: "NSetType".}
-# this is not sound! Unfortunately forbidding 'typ=' is not enough, as you
-# can easily do:
-#   let bracket = semCheck([1, 2])
-#   let fake = semCheck(2.0)
-#   bracket[0] = fake  # constructs a mixed array with ints and floats!
+{.pop.}
 
 proc `strVal=`*(n: NimNode, val: string) {.magic: "NSetStrVal", noSideEffect.}
+  ## Sets the string value of a string literal or comment.
+  ## Setting `strVal` is disallowed for `nnkIdent` and `nnkSym` nodes; a new node
+  ## must be created using `ident` or `bindSym` instead.
+  ##
+  ## See also:
+  ## * `strVal proc<#strVal,NimNode>`_ for getting the string value.
+  ## * `ident proc<#ident,string>`_ for creating an identifier.
+  ## * `bindSym proc<#bindSym%2C%2CBindSymRule>`_ for binding a symbol.
 
 proc newNimNode*(kind: NimNodeKind,
                  lineInfoFrom: NimNode = nil): NimNode
   {.magic: "NNewNimNode", noSideEffect.}
   ## Creates a new AST node of the specified kind.
   ##
-  ## The ``lineInfoFrom`` parameter is used for line information when the
+  ## The `lineInfoFrom` parameter is used for line information when the
   ## produced code crashes. You should ensure that it is set to a node that
   ## you are transforming.
 
-proc copyNimNode*(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect.}
-proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.}
+proc copyNimNode*(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect.} =
+  ## Creates a new AST node by copying the node `n`. Note that unlike `copyNimTree`,
+  ## child nodes of `n` are not copied.
+  runnableExamples:
+    macro foo(x: typed) =
+      var s = copyNimNode(x)
+      doAssert s.len == 0
+      doAssert s.kind == nnkStmtList
+
+    foo:
+      let x = 12
+      echo x
+
+proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.} =
+  ## Creates a new AST node by recursively copying the node `n`. Note that
+  ## unlike `copyNimNode`, this copies `n`, the children of `n`, etc.
+  runnableExamples:
+    macro foo(x: typed) =
+      var s = copyNimTree(x)
+      doAssert s.len == 2
+      doAssert s.kind == nnkStmtList
+
+    foo:
+      let x = 12
+      echo x
+
+when defined(nimHasNoReturnError):
+  {.pragma: errorNoReturn, noreturn.}
+else:
+  {.pragma: errorNoReturn.}
 
-proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign.}
-  ## writes an error message at compile time
+proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign, errorNoReturn.}
+  ## 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) {.magic: "NWarning", benign.}
-  ## writes a warning message at compile time
+proc warning*(msg: string, n: NimNode = nil) {.magic: "NWarning", benign.}
+  ## Writes a warning message at compile time.
 
-proc hint*(msg: string) {.magic: "NHint", benign.}
-  ## writes a hint message at compile time
+proc hint*(msg: string, n: NimNode = nil) {.magic: "NHint", benign.}
+  ## Writes a hint message at compile time.
 
-proc newStrLitNode*(s: string): NimNode {.compileTime, noSideEffect.} =
-  ## creates a string literal node from `s`
+proc newStrLitNode*(s: string): NimNode {.noSideEffect.} =
+  ## Creates a string literal node from `s`.
   result = newNimNode(nnkStrLit)
   result.strVal = s
 
-proc newCommentStmtNode*(s: string): NimNode {.compileTime, noSideEffect.} =
-  ## creates a comment statement node
+proc newCommentStmtNode*(s: string): NimNode {.noSideEffect.} =
+  ## Creates a comment statement node.
   result = newNimNode(nnkCommentStmt)
   result.strVal = s
 
-proc newIntLitNode*(i: BiggestInt): NimNode {.compileTime.} =
-  ## creates a int literal node from `i`
+proc newIntLitNode*(i: BiggestInt): NimNode =
+  ## Creates an int literal node from `i`.
   result = newNimNode(nnkIntLit)
   result.intVal = i
 
-proc newFloatLitNode*(f: BiggestFloat): NimNode {.compileTime.} =
-  ## creates a float literal node from `f`
+proc newFloatLitNode*(f: BiggestFloat): NimNode =
+  ## Creates a float literal node from `f`.
   result = newNimNode(nnkFloatLit)
   result.floatVal = f
 
-proc newIdentNode*(i: NimIdent): NimNode {.compileTime.} =
-  ## creates an identifier node from `i`
+{.push warnings: off.}
+
+proc newIdentNode*(i: NimIdent): NimNode {.deprecated: "use ident(string)".} =
+  ## Creates an identifier node from `i`.
   result = newNimNode(nnkIdent)
   result.ident = i
 
+{.pop.}
+
 proc newIdentNode*(i: string): NimNode {.magic: "StrToIdent", noSideEffect.}
-  ## creates an identifier node from `i`. It is simply an alias for
-  ## ``ident(string)``. Use that, it's shorter.
+  ## Creates an identifier node from `i`. It is simply an alias for
+  ## `ident(string)`. Use that, it's shorter.
+
+proc ident*(name: string): NimNode {.magic: "StrToIdent", noSideEffect.}
+  ## Create a new ident node from a string.
 
 type
-  BindSymRule* = enum    ## specifies how ``bindSym`` behaves
+  BindSymRule* = enum    ## Specifies how `bindSym` behaves. The difference
+                         ## between open and closed symbols can be found in
+                         ## `<manual.html#symbol-lookup-in-generics-open-and-closed-symbols>`_
     brClosed,            ## only the symbols in current scope are bound
-    brOpen,              ## open wrt overloaded symbols, but may be a single
+    brOpen,              ## open for overloaded symbols, but may be a single
                          ## symbol if not ambiguous (the rules match that of
                          ## binding in generics)
     brForceOpen          ## same as brOpen, but it will always be open even
                          ## if not ambiguous (this cannot be achieved with
                          ## any other means in the language currently)
 
-{.deprecated: [TBindSymRule: BindSymRule].}
-
-proc bindSym*(ident: static[string], rule: BindSymRule = brClosed): NimNode {.
+proc bindSym*(ident: string | NimNode, rule: BindSymRule = brClosed): NimNode {.
               magic: "NBindSym", noSideEffect.}
-  ## creates a node that binds `ident` to a symbol node. The bound symbol
+  ## Creates a node that binds `ident` to a symbol node. The bound symbol
   ## may be an overloaded symbol.
-  ## If ``rule == brClosed`` either an ``nkClosedSymChoice`` tree is
-  ## returned or ``nkSym`` if the symbol is not ambiguous.
-  ## If ``rule == brOpen`` either an ``nkOpenSymChoice`` tree is
-  ## returned or ``nkSym`` if the symbol is not ambiguous.
-  ## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is
+  ## if `ident` is a NimNode, it must have `nnkIdent` kind.
+  ## If `rule == brClosed` either an `nnkClosedSymChoice` tree is
+  ## returned or `nnkSym` if the symbol is not ambiguous.
+  ## If `rule == brOpen` either an `nnkOpenSymChoice` tree is
+  ## returned or `nnkSym` if the symbol is not ambiguous.
+  ## If `rule == brForceOpen` always an `nnkOpenSymChoice` tree is
   ## returned even if the symbol is not ambiguous.
+  ##
+  ## See the `manual <manual.html#macros-bindsym>`_ for more details.
 
 proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {.
   magic: "NGenSym", noSideEffect.}
-  ## generates a fresh symbol that is guaranteed to be unique. The symbol
+  ## Generates a fresh symbol that is guaranteed to be unique. The symbol
   ## needs to occur in a declaration context.
 
-proc callsite*(): NimNode {.magic: "NCallSite", benign,
-  deprecated: "use varargs[untyped] in the macro prototype instead".}
-  ## returns the AST of the invocation expression that invoked this macro.
-  ## **Deprecated since version 0.18.1**.
+proc callsite*(): NimNode {.magic: "NCallSite", benign, deprecated:
+  "Deprecated since v0.18.1; use `varargs[untyped]` in the macro prototype instead".}
+  ## Returns the AST of the invocation expression that invoked this macro.
+  # see https://github.com/nim-lang/RFCs/issues/387 as candidate replacement.
 
-proc toStrLit*(n: NimNode): NimNode {.compileTime.} =
-  ## converts the AST `n` to the concrete Nim code and wraps that
-  ## in a string literal node
+proc toStrLit*(n: NimNode): NimNode =
+  ## Converts the AST `n` to the concrete Nim code and wraps that
+  ## in a string literal node.
   return newStrLitNode(repr(n))
 
 type
@@ -413,264 +526,375 @@ type
     filename*: string
     line*,column*: int
 
-proc `$`*(arg: Lineinfo): string =
+proc `$`*(arg: LineInfo): string =
+  ## Return a string representation in the form `filepath(line, column)`.
+  # BUG: without `result = `, gives compile error
   result = arg.filename & "(" & $arg.line & ", " & $arg.column & ")"
 
 #proc lineinfo*(n: NimNode): LineInfo {.magic: "NLineInfo", noSideEffect.}
-  ## returns the position the node appears in the original source file
-  ## in the form filename(line, col)
+#  ## returns the position the node appears in the original source file
+#  ## in the form filename(line, col)
 
 proc getLine(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
 proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
 proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.}
 
-proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} =
-  result.filename = n.getFile
-  result.line = n.getLine
-  result.column = n.getColumn
+proc copyLineInfo*(arg: NimNode, info: NimNode) {.magic: "NLineInfo", noSideEffect.}
+  ## Copy lineinfo from `info`.
+
+proc setLine(arg: NimNode, line: uint16) {.magic: "NLineInfo", noSideEffect.}
+proc setColumn(arg: NimNode, column: int16) {.magic: "NLineInfo", noSideEffect.}
+proc setFile(arg: NimNode, file: string) {.magic: "NLineInfo", noSideEffect.}
+
+proc setLineInfo*(arg: NimNode, file: string, line: int, column: int) =
+  ## Sets the line info on the NimNode. The file needs to exists, but can be a
+  ## relative path. If you want to attach line info to a block using `quote`
+  ## you'll need to add the line information after the quote block.
+  arg.setFile(file)
+  arg.setLine(line.uint16)
+  arg.setColumn(column.int16)
 
-proc lineInfo*(arg: NimNode): string {.compileTime.} =
+proc setLineInfo*(arg: NimNode, lineInfo: LineInfo) =
+  ## See `setLineInfo proc<#setLineInfo,NimNode,string,int,int>`_
+  setLineInfo(arg, lineInfo.filename, lineInfo.line, lineInfo.column)
+
+proc lineInfoObj*(n: NimNode): LineInfo =
+  ## Returns `LineInfo` of `n`, using absolute path for `filename`.
+  result = LineInfo(filename: n.getFile, line: n.getLine, column: n.getColumn)
+
+proc lineInfo*(arg: NimNode): string =
+  ## Return line info in the form `filepath(line, column)`.
   $arg.lineInfoObj
 
-proc internalParseExpr(s: string): NimNode {.
+proc internalParseExpr(s, filename: string): NimNode {.
   magic: "ParseExprToAst", noSideEffect.}
 
-proc internalParseStmt(s: string): NimNode {.
+proc internalParseStmt(s, filename: string): NimNode {.
   magic: "ParseStmtToAst", noSideEffect.}
 
 proc internalErrorFlag*(): string {.magic: "NError", noSideEffect.}
   ## Some builtins set an error flag. This is then turned into a proper
   ## exception. **Note**: Ordinary application code should not call this.
 
-proc parseExpr*(s: string): NimNode {.noSideEffect, compileTime.} =
+proc parseExpr*(s: string; filename: string = ""): NimNode {.noSideEffect.} =
   ## Compiles the passed string to its AST representation.
-  ## Expects a single expression. Raises ``ValueError`` for parsing errors.
-  result = internalParseExpr(s)
+  ## Expects a single expression. Raises `ValueError` for parsing errors.
+  ## A filename can be given for more informative errors.
+  result = internalParseExpr(s, filename)
   let x = internalErrorFlag()
   if x.len > 0: raise newException(ValueError, x)
 
-proc parseStmt*(s: string): NimNode {.noSideEffect, compileTime.} =
+proc parseStmt*(s: string; filename: string = ""): NimNode {.noSideEffect.} =
   ## Compiles the passed string to its AST representation.
-  ## Expects one or more statements. Raises ``ValueError`` for parsing errors.
-  result = internalParseStmt(s)
+  ## Expects one or more statements. Raises `ValueError` for parsing errors.
+  ## A filename can be given for more informative errors.
+  result = internalParseStmt(s, filename)
   let x = internalErrorFlag()
   if x.len > 0: raise newException(ValueError, x)
 
 proc getAst*(macroOrTemplate: untyped): NimNode {.magic: "ExpandToAst", noSideEffect.}
   ## Obtains the AST nodes returned from a macro or template invocation.
+  ## See also `genasts.genAst`.
   ## Example:
-  ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   macro FooMacro() =
   ##     var ast = getAst(BarTemplate())
+  ##   ```
 
-proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.}
+proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.} =
   ## Quasi-quoting operator.
   ## Accepts an expression or a block and returns the AST that represents it.
   ## Within the quoted AST, you are able to interpolate NimNode expressions
   ## from the surrounding scope. If no operator is given, quoting is done using
   ## backticks. Otherwise, the given operator must be used as a prefix operator
   ## for any interpolated expression. The original meaning of the interpolation
-  ## operator may be obtained by escaping it (by prefixing it with itself):
-  ## e.g. `@` is escaped as `@@`, `@@` is escaped as `@@@` and so on.
-  ##
-  ## Example:
+  ## operator may be obtained by escaping it (by prefixing it with itself) when used
+  ## as a unary operator:
+  ## e.g. `@` is escaped as `@@`, `&%` is escaped as `&%&%` and so on; see examples.
   ##
-  ## .. code-block:: nim
+  ## A custom operator interpolation needs accent quoted (``) whenever it resolves
+  ## to a symbol.
   ##
-  ##   macro check(ex: untyped): typed =
-  ##     # this is a simplified version of the check macro from the
-  ##     # unittest module.
-  ##
-  ##     # If there is a failed check, we want to make it easy for
-  ##     # the user to jump to the faulty line in the code, so we
-  ##     # get the line info here:
-  ##     var info = ex.lineinfo
-  ##
-  ##     # We will also display the code string of the failed check:
-  ##     var expString = ex.toStrLit
-  ##
-  ##     # Finally we compose the code to implement the check:
-  ##     result = quote do:
-  ##       if not `ex`:
-  ##         echo `info` & ": Check failed: " & `expString`
+  ## See also `genasts <genasts.html>`_ which avoids some issues with `quote`.
+  runnableExamples:
+    macro check(ex: untyped) =
+      # this is a simplified version of the check macro from the
+      # unittest module.
 
-proc expectKind*(n: NimNode, k: NimNodeKind) {.compileTime.} =
-  ## checks that `n` is of kind `k`. If this is not the case,
+      # If there is a failed check, we want to make it easy for
+      # the user to jump to the faulty line in the code, so we
+      # get the line info here:
+      var info = ex.lineinfo
+
+      # We will also display the code string of the failed check:
+      var expString = ex.toStrLit
+
+      # Finally we compose the code to implement the check:
+      result = quote do:
+        if not `ex`:
+          echo `info` & ": Check failed: " & `expString`
+    check 1 + 1 == 2
+
+  runnableExamples:
+    # example showing how to define a symbol that requires backtick without
+    # quoting it.
+    var destroyCalled = false
+    macro bar() =
+      let s = newTree(nnkAccQuoted, ident"=destroy")
+      # let s = ident"`=destroy`" # this would not work
+      result = quote do:
+        type Foo = object
+        # proc `=destroy`(a: var Foo) = destroyCalled = true # this would not work
+        proc `s`(a: var Foo) = destroyCalled = true
+        block:
+          let a = Foo()
+    bar()
+    doAssert destroyCalled
+
+  runnableExamples:
+    # custom `op`
+    var destroyCalled = false
+    macro bar(ident) =
+      var x = 1.5
+      result = quote("@") do:
+        type Foo = object
+        let `@ident` = 0 # custom op interpolated symbols need quoted (``)
+        proc `=destroy`(a: var Foo) =
+          doAssert @x == 1.5
+          doAssert compiles(@x == 1.5)
+          let b1 = @[1,2]
+          let b2 = @@[1,2]
+          doAssert $b1 == "[1, 2]"
+          doAssert $b2 == "@[1, 2]"
+          destroyCalled = true
+        block:
+          let a = Foo()
+    bar(someident)
+    doAssert destroyCalled
+
+    proc `&%`(x: int): int = 1
+    proc `&%`(x, y: int): int = 2
+
+    macro bar2() =
+      var x = 3
+      result = quote("&%") do:
+        var y = &%x # quoting operator
+        doAssert &%&%y == 1 # unary operator => need to escape
+        doAssert y &% y == 2 # binary operator => no need to escape
+        doAssert y == 3
+    bar2()
+
+proc expectKind*(n: NimNode, k: NimNodeKind) =
+  ## Checks that `n` is of kind `k`. If this is not the case,
   ## compilation aborts with an error message. This is useful for writing
   ## macros that check the AST that is passed to them.
   if n.kind != k: error("Expected a node of kind " & $k & ", got " & $n.kind, n)
 
-proc expectMinLen*(n: NimNode, min: int) {.compileTime.} =
-  ## checks that `n` has at least `min` children. If this is not the case,
+proc expectMinLen*(n: NimNode, min: int) =
+  ## Checks that `n` has at least `min` children. If this is not the case,
   ## compilation aborts with an error message. This is useful for writing
   ## macros that check its number of arguments.
-  if n.len < min: error("macro expects a node with " & $min & " children", n)
+  if n.len < min: error("Expected a node with at least " & $min & " children, got " & $n.len, n)
 
-proc expectLen*(n: NimNode, len: int) {.compileTime.} =
-  ## checks that `n` has exactly `len` children. If this is not the case,
+proc expectLen*(n: NimNode, len: int) =
+  ## Checks that `n` has exactly `len` children. If this is not the case,
   ## compilation aborts with an error message. This is useful for writing
   ## macros that check its number of arguments.
-  if n.len != len: error("macro expects a node with " & $len & " children", n)
+  if n.len != len: error("Expected a node with " & $len & " children, got " & $n.len, n)
 
-proc expectLen*(n: NimNode, min, max: int) {.compileTime.} =
-  ## checks that `n` has a number of children in the range ``min..max``.
+proc expectLen*(n: NimNode, min, max: int) =
+  ## Checks that `n` has a number of children in the range `min..max`.
   ## If this is not the case, compilation aborts with an error message.
   ## This is useful for writing macros that check its number of arguments.
   if n.len < min or n.len > max:
-    error("macro expects a node with " & $min & ".." & $max " children", n)
+    error("Expected a node with " & $min & ".." & $max & " children, got " & $n.len, n)
 
 proc newTree*(kind: NimNodeKind,
-              children: varargs[NimNode]): NimNode {.compileTime.} =
-  ## produces a new node with children.
+              children: varargs[NimNode]): NimNode =
+  ## Produces a new node with children.
   result = newNimNode(kind)
   result.add(children)
 
-proc newCall*(theProc: NimNode,
-              args: varargs[NimNode]): NimNode {.compileTime.} =
-  ## produces a new call node. `theProc` is the proc that is called with
-  ## the arguments ``args[0..]``.
+proc newCall*(theProc: NimNode, args: varargs[NimNode]): NimNode =
+  ## Produces a new call node. `theProc` is the proc that is called with
+  ## the arguments `args[0..]`.
   result = newNimNode(nnkCall)
   result.add(theProc)
   result.add(args)
 
-proc newCall*(theProc: NimIdent,
-              args: varargs[NimNode]): NimNode {.compileTime, deprecated.} =
-  ## produces a new call node. `theProc` is the proc that is called with
-  ## the arguments ``args[0..]``.
-  ## **Deprecated since version 0.18.1**; Use ``newCall(string, ...)``,
-  ## or ``newCall(NimNode, ...)`` instead.
+{.push warnings: off.}
+
+proc newCall*(theProc: NimIdent, args: varargs[NimNode]): NimNode {.deprecated:
+  "Deprecated since v0.18.1; use 'newCall(string, ...)' or 'newCall(NimNode, ...)' instead".} =
+  ## Produces a new call node. `theProc` is the proc that is called with
+  ## the arguments `args[0..]`.
   result = newNimNode(nnkCall)
   result.add(newIdentNode(theProc))
   result.add(args)
 
+{.pop.}
+
 proc newCall*(theProc: string,
-              args: varargs[NimNode]): NimNode {.compileTime.} =
-  ## produces a new call node. `theProc` is the proc that is called with
-  ## the arguments ``args[0..]``.
+              args: varargs[NimNode]): NimNode =
+  ## Produces a new call node. `theProc` is the proc that is called with
+  ## the arguments `args[0..]`.
   result = newNimNode(nnkCall)
   result.add(newIdentNode(theProc))
   result.add(args)
 
-proc newLit*(c: char): NimNode {.compileTime.} =
-  ## produces a new character literal node.
+proc newLit*(c: char): NimNode =
+  ## Produces a new character literal node.
   result = newNimNode(nnkCharLit)
   result.intVal = ord(c)
 
-proc newLit*(i: int): NimNode {.compileTime.} =
-  ## produces a new integer literal node.
+proc newLit*(i: int): NimNode =
+  ## Produces a new integer literal node.
   result = newNimNode(nnkIntLit)
   result.intVal = i
 
-proc newLit*(i: int8): NimNode {.compileTime.} =
-  ## produces a new integer literal node.
+proc newLit*(i: int8): NimNode =
+  ## Produces a new integer literal node.
   result = newNimNode(nnkInt8Lit)
   result.intVal = i
 
-proc newLit*(i: int16): NimNode {.compileTime.} =
-  ## produces a new integer literal node.
+proc newLit*(i: int16): NimNode =
+  ## Produces a new integer literal node.
   result = newNimNode(nnkInt16Lit)
   result.intVal = i
 
-proc newLit*(i: int32): NimNode {.compileTime.} =
-  ## produces a new integer literal node.
+proc newLit*(i: int32): NimNode =
+  ## Produces a new integer literal node.
   result = newNimNode(nnkInt32Lit)
   result.intVal = i
 
-proc newLit*(i: int64): NimNode {.compileTime.} =
-  ## produces a new integer literal node.
+proc newLit*(i: int64): NimNode =
+  ## Produces a new integer literal node.
   result = newNimNode(nnkInt64Lit)
   result.intVal = i
 
-proc newLit*(i: uint): NimNode {.compileTime.} =
-  ## produces a new unsigned integer literal node.
+proc newLit*(i: uint): NimNode =
+  ## Produces a new unsigned integer literal node.
   result = newNimNode(nnkUIntLit)
   result.intVal = BiggestInt(i)
 
-proc newLit*(i: uint8): NimNode {.compileTime.} =
-  ## produces a new unsigned integer literal node.
+proc newLit*(i: uint8): NimNode =
+  ## Produces a new unsigned integer literal node.
   result = newNimNode(nnkUInt8Lit)
   result.intVal = BiggestInt(i)
 
-proc newLit*(i: uint16): NimNode {.compileTime.} =
-  ## produces a new unsigned integer literal node.
+proc newLit*(i: uint16): NimNode =
+  ## Produces a new unsigned integer literal node.
   result = newNimNode(nnkUInt16Lit)
   result.intVal = BiggestInt(i)
 
-proc newLit*(i: uint32): NimNode {.compileTime.} =
-  ## produces a new unsigned integer literal node.
+proc newLit*(i: uint32): NimNode =
+  ## Produces a new unsigned integer literal node.
   result = newNimNode(nnkUInt32Lit)
   result.intVal = BiggestInt(i)
 
-proc newLit*(i: uint64): NimNode {.compileTime.} =
-  ## produces a new unsigned integer literal node.
+proc newLit*(i: uint64): NimNode =
+  ## Produces a new unsigned integer literal node.
   result = newNimNode(nnkUInt64Lit)
   result.intVal = BiggestInt(i)
 
-proc newLit*(b: bool): NimNode {.compileTime.} =
-  ## produces a new boolean literal node.
+proc newLit*(b: bool): NimNode =
+  ## Produces a new boolean literal node.
   result = if b: bindSym"true" else: bindSym"false"
 
+proc newLit*(s: string): NimNode =
+  ## Produces a new string literal node.
+  result = newNimNode(nnkStrLit)
+  result.strVal = s
+
 when false:
   # the float type is not really a distinct type as described in https://github.com/nim-lang/Nim/issues/5875
-  proc newLit*(f: float): NimNode {.compileTime.} =
-    ## produces a new float literal node.
+  proc newLit*(f: float): NimNode =
+    ## Produces a new float literal node.
     result = newNimNode(nnkFloatLit)
     result.floatVal = f
 
-proc newLit*(f: float32): NimNode {.compileTime.} =
-  ## produces a new float literal node.
+proc newLit*(f: float32): NimNode =
+  ## Produces a new float literal node.
   result = newNimNode(nnkFloat32Lit)
   result.floatVal = f
 
-proc newLit*(f: float64): NimNode {.compileTime.} =
-  ## produces a new float literal node.
+proc newLit*(f: float64): NimNode =
+  ## Produces a new float literal node.
   result = newNimNode(nnkFloat64Lit)
   result.floatVal = f
 
-when compiles(float128):
-  proc newLit*(f: float128): NimNode {.compileTime.} =
-    ## produces a new float literal node.
+when declared(float128):
+  proc newLit*(f: float128): NimNode =
+    ## Produces a new float literal node.
     result = newNimNode(nnkFloat128Lit)
     result.floatVal = f
 
-proc newLit*(arg: object): NimNode {.compileTime.} =
-  result = nnkObjConstr.newTree(arg.type.getTypeInst[1])
+proc newLit*(arg: enum): NimNode =
+  result = newCall(
+    arg.typeof.getTypeInst[1],
+    newLit(int(arg))
+  )
+
+proc newLit*[N,T](arg: array[N,T]): NimNode
+proc newLit*[T](arg: seq[T]): NimNode
+proc newLit*[T](s: set[T]): NimNode
+proc newLit*[T: tuple](arg: T): NimNode
+
+proc newLit*(arg: object): NimNode =
+  result = nnkObjConstr.newTree(arg.typeof.getTypeInst[1])
   for a, b in arg.fieldPairs:
     result.add nnkExprColonExpr.newTree( newIdentNode(a), newLit(b) )
 
-proc newLit*[N,T](arg: array[N,T]): NimNode {.compileTime.} =
+proc newLit*(arg: ref object): NimNode =
+  ## produces a new ref type literal node.
+  result = nnkObjConstr.newTree(arg.typeof.getTypeInst[1])
+  for a, b in fieldPairs(arg[]):
+    result.add nnkExprColonExpr.newTree(newIdentNode(a), newLit(b))
+
+proc newLit*[N,T](arg: array[N,T]): NimNode =
   result = nnkBracket.newTree
   for x in arg:
     result.add newLit(x)
 
-proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} =
-  var bracket = nnkBracket.newTree
+proc newLit*[T](arg: seq[T]): NimNode =
+  let bracket = nnkBracket.newTree
   for x in arg:
     bracket.add newLit(x)
-
-  result = nnkCall.newTree(
-    nnkBracketExpr.newTree(
-      nnkAccQuoted.newTree( bindSym"@" ),
-      getTypeInst( bindSym"T" )
-    ),
+  result = nnkPrefix.newTree(
+    bindSym"@",
     bracket
   )
+  if arg.len == 0:
+    # add type cast for empty seq
+    var typ = getTypeInst(typeof(arg))[1]
+    result = newCall(typ,result)
+
+proc newLit*[T](s: set[T]): NimNode =
+  result = nnkCurly.newTree
+  for x in s:
+    result.add newLit(x)
+  if result.len == 0:
+    # add type cast for empty set
+    var typ = getTypeInst(typeof(s))[1]
+    result = newCall(typ,result)
+
+proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".}
+  ## See `typetraits.isNamedTuple`
+
+proc newLit*[T: tuple](arg: T): NimNode =
+  ## use -d:nimHasWorkaround14720 to restore behavior prior to PR, forcing
+  ## a named tuple even when `arg` is unnamed.
+  result = nnkTupleConstr.newTree
+  when defined(nimHasWorkaround14720) or isNamedTuple(T):
+    for a, b in arg.fieldPairs:
+      result.add nnkExprColonExpr.newTree(newIdentNode(a), newLit(b))
+  else:
+    for b in arg.fields:
+      result.add newLit(b)
 
-proc newLit*(arg: tuple): NimNode {.compileTime.} =
-  result = nnkPar.newTree
-  for a,b in arg.fieldPairs:
-    result.add nnkExprColonExpr.newTree(newIdentNode(a), newLit(b))
-
-proc newLit*(s: string): NimNode {.compileTime.} =
-  ## produces a new string literal node.
-  result = newNimNode(nnkStrLit)
-  result.strVal = s
-
-proc nestList*(op: NimNode; pack: NimNode): NimNode {.compileTime.} =
-  ## nests the list `pack` into a tree of call expressions:
-  ## ``[a, b, c]`` is transformed into ``op(a, op(c, d))``.
+proc nestList*(op: NimNode; pack: NimNode): NimNode =
+  ## Nests the list `pack` into a tree of call expressions:
+  ## `[a, b, c]` is transformed into `op(a, op(c, d))`.
   ## This is also known as fold expression.
   if pack.len < 1:
     error("`nestList` expects a node with at least 1 child")
@@ -678,87 +902,104 @@ proc nestList*(op: NimNode; pack: NimNode): NimNode {.compileTime.} =
   for i in countdown(pack.len - 2, 0):
     result = newCall(op, pack[i], result)
 
-proc nestList*(op: NimNode; pack: NimNode; init: NimNode): NimNode {.compileTime.} =
-  ## nests the list `pack` into a tree of call expressions:
-  ## ``[a, b, c]`` is transformed into ``op(a, op(c, d))``.
+proc nestList*(op: NimNode; pack: NimNode; init: NimNode): NimNode =
+  ## Nests the list `pack` into a tree of call expressions:
+  ## `[a, b, c]` is transformed into `op(a, op(c, d))`.
   ## This is also known as fold expression.
   result = init
   for i in countdown(pack.len - 1, 0):
     result = newCall(op, pack[i], result)
 
-proc nestList*(theProc: NimIdent, x: NimNode): NimNode {.compileTime, deprecated.} =
-  ## **Deprecated since version 0.18.1**; Use one of ``nestList(NimNode, ...)`` instead.
-  var L = x.len
-  result = newCall(theProc, x[L-2], x[L-1])
-  for i in countdown(L-3, 0):
-    result = newCall(theProc, x[i], result)
+proc eqIdent*(a: string; b: string): bool {.magic: "EqIdent", noSideEffect.}
+  ## Style insensitive comparison.
 
-proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
-  ## Convert the AST `n` to a human-readable tree-like string.
-  ##
-  ## See also `repr`, `lispRepr`, and `astGenRepr`.
+proc eqIdent*(a: NimNode; b: string): bool {.magic: "EqIdent", noSideEffect.}
+  ## Style insensitive comparison.  `a` can be an identifier or a
+  ## symbol. `a` may be wrapped in an export marker
+  ## (`nnkPostfix`) or quoted with backticks (`nnkAccQuoted`),
+  ## these nodes will be unwrapped.
 
-  proc traverse(res: var string, level: int, n: NimNode) {.benign.} =
-    for i in 0..level-1: res.add "  "
-    res.add(($n.kind).substr(3))
+proc eqIdent*(a: string; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
+  ## Style insensitive comparison.  `b` can be an identifier or a
+  ## symbol. `b` may be wrapped in an export marker
+  ## (`nnkPostfix`) or quoted with backticks (`nnkAccQuoted`),
+  ## these nodes will be unwrapped.
 
-    case n.kind
-    of nnkEmpty, nnkNilLit: discard # same as nil node in this representation
-    of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)
-    of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)
-    of nnkStrLit..nnkTripleStrLit, nnkIdent, nnkSym:
-      res.add(" " & $n.strVal.newLit.repr)
-    of nnkNone: assert false
-    else:
-      for j in 0..n.len-1:
-        res.add "\n"
-        traverse(res, level + 1, n[j])
+proc eqIdent*(a: NimNode; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
+  ## Style insensitive comparison.  `a` and `b` can be an
+  ## identifier or a symbol. Both may be wrapped in an export marker
+  ## (`nnkPostfix`) or quoted with backticks (`nnkAccQuoted`),
+  ## these nodes will be unwrapped.
 
-  result = ""
-  traverse(result, 0, n)
+const collapseSymChoice = not defined(nimLegacyMacrosCollapseSymChoice)
 
-proc lispRepr*(n: NimNode): string {.compileTime, benign.} =
-  ## Convert the AST `n` to a human-readable lisp-like string,
-  ##
-  ## See also `repr`, `treeRepr`, and `astGenRepr`.
+proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indented = false) {.benign.} =
+  if level > 0:
+    if indented:
+      res.add("\n")
+      for i in 0 .. level-1:
+        if isLisp:
+          res.add(" ")          # dumpLisp indentation
+        else:
+          res.add("  ")         # dumpTree indentation
+    else:
+      res.add(" ")
 
-  result = ($n.kind).substr(3)
-  add(result, "(")
+  if isLisp:
+    res.add("(")
+  res.add(($n.kind).substr(3))
 
   case n.kind
-  of nnkEmpty, nnkNilLit: discard # same as nil node in this representation
-  of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
-  of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
-  of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkident, nnkSym:
-    add(result, n.strVal.newLit.repr)
-  of nnkNone: assert false
-  else:
+  of nnkEmpty, nnkNilLit:
+    discard # same as nil node in this representation
+  of nnkCharLit .. nnkInt64Lit:
+    res.add(" " & $n.intVal)
+  of nnkUIntLit .. nnkUInt64Lit:
+    res.add(" " & $cast[uint64](n.intVal))
+  of nnkFloatLit .. nnkFloat64Lit:
+    res.add(" " & $n.floatVal)
+  of nnkStrLit .. nnkTripleStrLit, nnkCommentStmt, nnkIdent, nnkSym:
+    res.add(" " & $n.strVal.newLit.repr)
+  of nnkNone:
+    assert false
+  elif n.kind in {nnkOpenSymChoice, nnkClosedSymChoice} and collapseSymChoice:
+    res.add(" " & $n.len)
     if n.len > 0:
-      add(result, lispRepr(n[0]))
-      for j in 1..n.len-1:
-        add(result, ", ")
-        add(result, lispRepr(n[j]))
+      var allSameSymName = true
+      for i in 0..<n.len:
+        if n[i].kind != nnkSym or not eqIdent(n[i], n[0]):
+          allSameSymName = false
+          break
+      if allSameSymName:
+        res.add(" " & $n[0].strVal.newLit.repr)
+      else:
+        for j in 0 ..< n.len:
+          n[j].treeTraverse(res, level+1, isLisp, indented)
+  else:
+    for j in 0 ..< n.len:
+      n[j].treeTraverse(res, level+1, isLisp, indented)
 
-  add(result, ")")
+  if isLisp:
+    res.add(")")
 
-proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
-  ## Convert the AST `n` to the code required to generate that AST. So for example
-  ##
-  ## .. code-block:: nim
-  ##   astGenRepr:
-  ##     echo "Hello world"
+proc treeRepr*(n: NimNode): string {.benign.} =
+  ## Convert the AST `n` to a human-readable tree-like string.
   ##
-  ## Would output:
+  ## See also `repr`, `lispRepr`_, and `astGenRepr`_.
+  result = ""
+  n.treeTraverse(result, isLisp = false, indented = true)
+
+proc lispRepr*(n: NimNode; indented = false): string {.benign.} =
+  ## Convert the AST `n` to a human-readable lisp-like string.
   ##
-  ## .. code-block:: nim
-  ##   nnkStmtList.newTree(
-  ##     nnkCommand.newTree(
-  ##       newIdentNode("echo"),
-  ##       newLit("Hello world")
-  ##     )
-  ##   )
+  ## See also `repr`, `treeRepr`_, and `astGenRepr`_.
+  result = ""
+  n.treeTraverse(result, isLisp = true, indented = indented)
+
+proc astGenRepr*(n: NimNode): string {.benign.} =
+  ## Convert the AST `n` to the code required to generate that AST.
   ##
-  ## See also `repr`, `treeRepr`, and `lispRepr`.
+  ## See also `repr`_, `treeRepr`_, and `lispRepr`_.
 
   const
     NodeKinds = {nnkEmpty, nnkIdent, nnkSym, nnkNone, nnkCommentStmt}
@@ -783,6 +1024,10 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
     of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkIdent, nnkSym:
       res.add(n.strVal.newLit.repr)
     of nnkNone: assert false
+    elif n.kind in {nnkOpenSymChoice, nnkClosedSymChoice} and collapseSymChoice:
+      res.add(", # unrepresentable symbols: " & $n.len)
+      if n.len > 0:
+        res.add(" " & n[0].strVal.newLit.repr)
     else:
       res.add(".newTree(")
       for j in 0..<n.len:
@@ -803,180 +1048,234 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
 
 macro dumpTree*(s: untyped): untyped = echo s.treeRepr
   ## Accepts a block of nim code and prints the parsed abstract syntax
-  ## tree using the `treeRepr` function. Printing is done *at compile time*.
+  ## tree using the `treeRepr` proc. Printing is done *at compile time*.
   ##
   ## You can use this as a tool to explore the Nim's abstract syntax
   ## tree and to discover what kind of nodes must be created to represent
   ## a certain expression/statement.
+  ##
+  ## For example:
+  ##   ```nim
+  ##   dumpTree:
+  ##     echo "Hello, World!"
+  ##   ```
+  ##
+  ## Outputs:
+  ##   ```
+  ##   StmtList
+  ##     Command
+  ##       Ident "echo"
+  ##       StrLit "Hello, World!"
+  ##   ```
+  ##
+  ## Also see `dumpAstGen` and `dumpLisp`.
 
-macro dumpLisp*(s: untyped): untyped = echo s.lispRepr
+macro dumpLisp*(s: untyped): untyped = echo s.lispRepr(indented = true)
   ## Accepts a block of nim code and prints the parsed abstract syntax
-  ## tree using the `lispRepr` function. Printing is done *at compile time*.
+  ## tree using the `lispRepr` proc. Printing is done *at compile time*.
   ##
-  ## See `dumpTree`.
+  ## You can use this as a tool to explore the Nim's abstract syntax
+  ## tree and to discover what kind of nodes must be created to represent
+  ## a certain expression/statement.
+  ##
+  ## For example:
+  ##   ```nim
+  ##   dumpLisp:
+  ##     echo "Hello, World!"
+  ##   ```
+  ##
+  ## Outputs:
+  ##   ```
+  ##   (StmtList
+  ##    (Command
+  ##     (Ident "echo")
+  ##     (StrLit "Hello, World!")))
+  ##   ```
+  ##
+  ## Also see `dumpAstGen` and `dumpTree`.
 
 macro dumpAstGen*(s: untyped): untyped = echo s.astGenRepr
   ## Accepts a block of nim code and prints the parsed abstract syntax
-  ## tree using the `astGenRepr` function. Printing is done *at compile time*.
+  ## tree using the `astGenRepr` proc. Printing is done *at compile time*.
   ##
   ## You can use this as a tool to write macros quicker by writing example
   ## outputs and then copying the snippets into the macro for modification.
   ##
-  ## See `dumpTree`.
-
-macro dumpTreeImm*(s: untyped): untyped {.deprecated.} = echo s.treeRepr
-  ## Deprecated. Use `dumpTree` instead.
-
-macro dumpLispImm*(s: untyped): untyped {.deprecated.} = echo s.lispRepr
-  ## Deprecated. Use `dumpLisp` instead.
+  ## For example:
+  ##   ```nim
+  ##   dumpAstGen:
+  ##     echo "Hello, World!"
+  ##   ```
+  ##
+  ## Outputs:
+  ##   ```
+  ##   nnkStmtList.newTree(
+  ##     nnkCommand.newTree(
+  ##       newIdentNode("echo"),
+  ##       newLit("Hello, World!")
+  ##     )
+  ##   )
+  ##   ```
+  ##
+  ## Also see `dumpTree` and `dumpLisp`.
 
-proc newEmptyNode*(): NimNode {.compileTime, noSideEffect.} =
-  ## Create a new empty node
+proc newEmptyNode*(): NimNode {.noSideEffect.} =
+  ## Create a new empty node.
   result = newNimNode(nnkEmpty)
 
-proc newStmtList*(stmts: varargs[NimNode]): NimNode {.compileTime.}=
-  ## Create a new statement list
+proc newStmtList*(stmts: varargs[NimNode]): NimNode =
+  ## Create a new statement list.
   result = newNimNode(nnkStmtList).add(stmts)
 
-proc newPar*(exprs: varargs[NimNode]): NimNode {.compileTime.}=
-  ## Create a new parentheses-enclosed expression
+proc newPar*(exprs: NimNode): NimNode =
+  ## Create a new parentheses-enclosed expression.
+  newNimNode(nnkPar).add(exprs)
+
+proc newPar*(exprs: varargs[NimNode]): NimNode {.deprecated:
+        "don't use newPar/nnkPar to construct tuple expressions; use nnkTupleConstr instead".} =
+  ## Create a new parentheses-enclosed expression.
   newNimNode(nnkPar).add(exprs)
 
-proc newBlockStmt*(label, body: NimNode): NimNode {.compileTime.} =
-  ## Create a new block statement with label
+proc newBlockStmt*(label, body: NimNode): NimNode =
+  ## Create a new block statement with label.
   return newNimNode(nnkBlockStmt).add(label, body)
 
-proc newBlockStmt*(body: NimNode): NimNode {.compiletime.} =
-  ## Create a new block: stmt
+proc newBlockStmt*(body: NimNode): NimNode =
+  ## Create a new block: stmt.
   return newNimNode(nnkBlockStmt).add(newEmptyNode(), body)
 
-proc newVarStmt*(name, value: NimNode): NimNode {.compiletime.} =
-  ## Create a new var stmt
+proc newVarStmt*(name, value: NimNode): NimNode =
+  ## Create a new var stmt.
   return newNimNode(nnkVarSection).add(
     newNimNode(nnkIdentDefs).add(name, newNimNode(nnkEmpty), value))
 
-proc newLetStmt*(name, value: NimNode): NimNode {.compiletime.} =
-  ## Create a new let stmt
+proc newLetStmt*(name, value: NimNode): NimNode =
+  ## Create a new let stmt.
   return newNimNode(nnkLetSection).add(
     newNimNode(nnkIdentDefs).add(name, newNimNode(nnkEmpty), value))
 
-proc newConstStmt*(name, value: NimNode): NimNode {.compileTime.} =
-  ## Create a new const stmt
+proc newConstStmt*(name, value: NimNode): NimNode =
+  ## Create a new const stmt.
   newNimNode(nnkConstSection).add(
     newNimNode(nnkConstDef).add(name, newNimNode(nnkEmpty), value))
 
-proc newAssignment*(lhs, rhs: NimNode): NimNode {.compileTime.} =
+proc newAssignment*(lhs, rhs: NimNode): NimNode =
   return newNimNode(nnkAsgn).add(lhs, rhs)
 
-proc newDotExpr*(a, b: NimNode): NimNode {.compileTime.} =
-  ## Create new dot expression
-  ## a.dot(b) ->  `a.b`
+proc newDotExpr*(a, b: NimNode): NimNode =
+  ## Create new dot expression.
+  ## a.dot(b) -> `a.b`
   return newNimNode(nnkDotExpr).add(a, b)
 
-proc newColonExpr*(a, b: NimNode): NimNode {.compileTime.} =
-  ## Create new colon expression
-  ## newColonExpr(a, b) ->  `a: b`
+proc newColonExpr*(a, b: NimNode): NimNode =
+  ## Create new colon expression.
+  ## newColonExpr(a, b) -> `a: b`
   newNimNode(nnkExprColonExpr).add(a, b)
 
 proc newIdentDefs*(name, kind: NimNode;
-                   default = newEmptyNode()): NimNode {.compileTime.} =
-  ## Creates a new ``nnkIdentDefs`` node of a specific kind and value.
+                   default = newEmptyNode()): NimNode =
+  ## Creates a new `nnkIdentDefs` node of a specific kind and value.
   ##
-  ## ``nnkIdentDefs`` need to have at least three children, but they can have
+  ## `nnkIdentDefs` need to have at least three children, but they can have
   ## more: first comes a list of identifiers followed by a type and value
   ## nodes. This helper proc creates a three node subtree, the first subnode
-  ## being a single identifier name. Both the ``kind`` node and ``default``
-  ## (value) nodes may be empty depending on where the ``nnkIdentDefs``
-  ## appears: tuple or object definitions will have an empty ``default`` node,
-  ## ``let`` or ``var`` blocks may have an empty ``kind`` node if the
+  ## being a single identifier name. Both the `kind` node and `default`
+  ## (value) nodes may be empty depending on where the `nnkIdentDefs`
+  ## appears: tuple or object definitions will have an empty `default` node,
+  ## `let` or `var` blocks may have an empty `kind` node if the
   ## identifier is being assigned a value. Example:
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   var varSection = newNimNode(nnkVarSection).add(
   ##     newIdentDefs(ident("a"), ident("string")),
   ##     newIdentDefs(ident("b"), newEmptyNode(), newLit(3)))
   ##   # --> var
   ##   #       a: string
   ##   #       b = 3
+  ##   ```
   ##
   ## If you need to create multiple identifiers you need to use the lower level
-  ## ``newNimNode``:
-  ##
-  ## .. code-block:: nim
-  ##
+  ## `newNimNode`:
+  ##   ```nim
   ##   result = newNimNode(nnkIdentDefs).add(
   ##     ident("a"), ident("b"), ident("c"), ident("string"),
   ##       newStrLitNode("Hello"))
+  ##   ```
   newNimNode(nnkIdentDefs).add(name, kind, default)
 
-proc newNilLit*(): NimNode {.compileTime.} =
-  ## New nil literal shortcut
+proc newNilLit*(): NimNode =
+  ## New nil literal shortcut.
   result = newNimNode(nnkNilLit)
 
-proc last*(node: NimNode): NimNode {.compileTime.} = node[node.len-1]
-  ## Return the last item in nodes children. Same as `node[^1]`
+proc last*(node: NimNode): NimNode = node[node.len-1]
+  ## Return the last item in nodes children. Same as `node[^1]`.
 
 
 const
   RoutineNodes* = {nnkProcDef, nnkFuncDef, nnkMethodDef, nnkDo, nnkLambda,
-                   nnkIteratorDef, nnkTemplateDef, nnkConverterDef}
+                   nnkIteratorDef, nnkTemplateDef, nnkConverterDef, nnkMacroDef}
   AtomicNodes* = {nnkNone..nnkNilLit}
-  CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
-    nnkCallStrLit, nnkHiddenCallConv}
-
-proc expectKind*(n: NimNode; k: set[NimNodeKind]) {.compileTime.} =
-  assert n.kind in k, "Expected one of " & $k & ", got " & $n.kind
+  # see matching set nnkCallKinds above
+  CallNodes* = nnkCallKinds
 
-proc newProc*(name = newEmptyNode(); params: openArray[NimNode] = [newEmptyNode()];
-    body: NimNode = newStmtList(), procType = nnkProcDef): NimNode {.compileTime.} =
-  ## shortcut for creating a new proc
+proc expectKind*(n: NimNode; k: set[NimNodeKind]) =
+  ## Checks that `n` is of kind `k`. If this is not the case,
+  ## compilation aborts with an error message. This is useful for writing
+  ## macros that check the AST that is passed to them.
+  if n.kind notin k: error("Expected one of " & $k & ", got " & $n.kind, n)
+
+proc newProc*(name = newEmptyNode();
+              params: openArray[NimNode] = [newEmptyNode()];
+              body: NimNode = newStmtList();
+              procType = nnkProcDef;
+              pragmas: NimNode = newEmptyNode()): NimNode =
+  ## Shortcut for creating a new proc.
   ##
-  ## The ``params`` array must start with the return type of the proc,
+  ## The `params` array must start with the return type of the proc,
   ## followed by a list of IdentDefs which specify the params.
-  assert procType in RoutineNodes
+  if procType notin RoutineNodes:
+    error("Expected one of " & $RoutineNodes & ", got " & $procType)
+  pragmas.expectKind({nnkEmpty, nnkPragma})
   result = newNimNode(procType).add(
     name,
     newEmptyNode(),
     newEmptyNode(),
-    newNimNode(nnkFormalParams).add(params), ##params
-    newEmptyNode(),  ## pragmas
+    newNimNode(nnkFormalParams).add(params),
+    pragmas,
     newEmptyNode(),
     body)
 
-proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]):
-                NimNode {.compiletime.} =
-  ## Constructor for ``if`` statements.
-  ##
-  ## .. code-block:: nim
-  ##
-  ##    newIfStmt(
-  ##      (Ident, StmtList),
-  ##      ...
-  ##    )
-  ##
+proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]): NimNode =
+  ## Constructor for `if` statements.
+  ##   ```nim
+  ##   newIfStmt(
+  ##     (Ident, StmtList),
+  ##     ...
+  ##   )
+  ##   ```
   result = newNimNode(nnkIfStmt)
+  if len(branches) < 1:
+    error("If statement must have at least one branch")
   for i in branches:
-    result.add(newNimNode(nnkElifBranch).add(i.cond, i.body))
+    result.add(newTree(nnkElifBranch, i.cond, i.body))
 
 proc newEnum*(name: NimNode, fields: openArray[NimNode],
-              public, pure: bool): NimNode {.compileTime.} =
+              public, pure: bool): NimNode =
 
   ## Creates a new enum. `name` must be an ident. Fields are allowed to be
-  ## either idents or EnumFieldDef
-  ##
-  ## .. code-block:: nim
-  ##
-  ##    newEnum(
-  ##      name    = ident("Colors"),
-  ##      fields  = [ident("Blue"), ident("Red")],
-  ##      public  = true, pure = false)
-  ##
-  ##    # type Colors* = Blue Red
+  ## either idents or EnumFieldDef:
+  ##   ```nim
+  ##   newEnum(
+  ##     name    = ident("Colors"),
+  ##     fields  = [ident("Blue"), ident("Red")],
+  ##     public  = true, pure = false)
   ##
+  ##   # type Colors* = Blue Red
+  ##   ```
 
   expectKind name, nnkIdent
-  doAssert len(fields) > 0, "Enum must contain at least one field"
+  if len(fields) < 1:
+    error("Enum must contain at least one field")
   for field in fields:
     expectKind field, {nnkIdent, nnkEnumFieldDef}
 
@@ -1002,55 +1301,76 @@ proc newEnum*(name: NimNode, fields: openArray[NimNode],
 
   return typeSect
 
-proc copyChildrenTo*(src, dest: NimNode) {.compileTime.}=
-  ## Copy all children from `src` to `dest`
+proc copyChildrenTo*(src, dest: NimNode) =
+  ## Copy all children from `src` to `dest`.
   for i in 0 ..< src.len:
     dest.add src[i].copyNimTree
 
 template expectRoutine(node: NimNode) =
   expectKind(node, RoutineNodes)
 
-proc name*(someProc: NimNode): NimNode {.compileTime.} =
+proc name*(someProc: NimNode): NimNode =
   someProc.expectRoutine
   result = someProc[0]
   if result.kind == nnkPostfix:
-    result = result[1]
-proc `name=`*(someProc: NimNode; val: NimNode) {.compileTime.} =
-  someProc.expectRoutine
-  someProc[0] = val
+    if result[1].kind == nnkAccQuoted:
+      result = result[1][0]
+    else:
+      result = result[1]
+  elif result.kind == nnkAccQuoted:
+    result = result[0]
 
-proc params*(someProc: NimNode): NimNode {.compileTime.} =
-  someProc.expectRoutine
-  result = someProc[3]
-proc `params=`* (someProc: NimNode; params: NimNode) {.compileTime.}=
+proc `name=`*(someProc: NimNode; val: NimNode) =
   someProc.expectRoutine
-  assert params.kind == nnkFormalParams
-  someProc[3] = params
+  if someProc[0].kind == nnkPostfix:
+    someProc[0][1] = val
+  else: someProc[0] = val
 
-proc pragma*(someProc: NimNode): NimNode {.compileTime.} =
-  ## Get the pragma of a proc type
-  ## These will be expanded
-  someProc.expectRoutine
-  result = someProc[4]
-proc `pragma=`*(someProc: NimNode; val: NimNode){.compileTime.}=
-  ## Set the pragma of a proc type
-  someProc.expectRoutine
-  assert val.kind in {nnkEmpty, nnkPragma}
-  someProc[4] = val
+proc params*(someProc: NimNode): NimNode =
+  if someProc.kind == nnkProcTy:
+    someProc[0]
+  else:
+    someProc.expectRoutine
+    someProc[3]
 
-proc addPragma*(someProc, pragma: NimNode) {.compileTime.} =
-  ## Adds pragma to routine definition
-  someProc.expectRoutine
+proc `params=`* (someProc: NimNode; params: NimNode) =
+  expectKind(params, nnkFormalParams)
+  if someProc.kind == nnkProcTy:
+    someProc[0] = params
+  else:
+    someProc.expectRoutine
+    someProc[3] = params
+
+proc pragma*(someProc: NimNode): NimNode =
+  ## Get the pragma of a proc type.
+  ## These will be expanded.
+  if someProc.kind == nnkProcTy:
+    result = someProc[1]
+  else:
+    someProc.expectRoutine
+    result = someProc[4]
+proc `pragma=`*(someProc: NimNode; val: NimNode) =
+  ## Set the pragma of a proc type.
+  expectKind(val, {nnkEmpty, nnkPragma})
+  if someProc.kind == nnkProcTy:
+    someProc[1] = val
+  else:
+    someProc.expectRoutine
+    someProc[4] = val
+
+proc addPragma*(someProc, pragma: NimNode) =
+  ## Adds pragma to routine definition.
+  someProc.expectKind(RoutineNodes + {nnkProcTy})
   var pragmaNode = someProc.pragma
   if pragmaNode.isNil or pragmaNode.kind == nnkEmpty:
     pragmaNode = newNimNode(nnkPragma)
     someProc.pragma = pragmaNode
   pragmaNode.add(pragma)
 
-template badNodeKind(k, f) =
-  assert false, "Invalid node kind " & $k & " for macros.`" & $f & "`"
+template badNodeKind(n, f) =
+  error("Invalid node kind " & $n.kind & " for macros.`" & $f & "`", n)
 
-proc body*(someProc: NimNode): NimNode {.compileTime.} =
+proc body*(someProc: NimNode): NimNode =
   case someProc.kind:
   of RoutineNodes:
     return someProc[6]
@@ -1059,9 +1379,9 @@ proc body*(someProc: NimNode): NimNode {.compileTime.} =
   of nnkForStmt:
     return someProc.last
   else:
-    badNodeKind someProc.kind, "body"
+    badNodeKind someProc, "body"
 
-proc `body=`*(someProc: NimNode, val: NimNode) {.compileTime.} =
+proc `body=`*(someProc: NimNode, val: NimNode) =
   case someProc.kind
   of RoutineNodes:
     someProc[6] = val
@@ -1070,48 +1390,55 @@ proc `body=`*(someProc: NimNode, val: NimNode) {.compileTime.} =
   of nnkForStmt:
     someProc[len(someProc)-1] = val
   else:
-    badNodeKind someProc.kind, "body="
+    badNodeKind someProc, "body="
 
-proc basename*(a: NimNode): NimNode {.compiletime, benign.}
+proc basename*(a: NimNode): NimNode =
+  ## Pull an identifier from prefix/postfix expressions.
+  case a.kind
+  of nnkIdent: result = a
+  of nnkPostfix, nnkPrefix: result = a[1]
+  of nnkPragmaExpr: result = basename(a[0])
+  else:
+    error("Do not know how to get basename of (" & treeRepr(a) & ")\n" &
+      repr(a), a)
 
-proc `$`*(node: NimNode): string {.compileTime.} =
-  ## Get the string of an identifier node
+proc `$`*(node: NimNode): string =
+  ## Get the string of an identifier node.
   case node.kind
   of nnkPostfix:
     result = node.basename.strVal & "*"
   of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkSym, nnkIdent:
     result = node.strVal
-  of nnkOpenSymChoice, nnkClosedSymChoice:
+  of nnkOpenSymChoice, nnkClosedSymChoice, nnkOpenSym:
     result = $node[0]
   of nnkAccQuoted:
-    result = $node[0]
+    result = ""
+    for i in 0 ..< node.len:
+      result.add(repr(node[i]))
   else:
-    badNodeKind node.kind, "$"
-
-proc ident*(name: string): NimNode {.magic: "StrToIdent", noSideEffect.}
-  ## Create a new ident node from a string
+    badNodeKind node, "$"
 
 iterator items*(n: NimNode): NimNode {.inline.} =
-  ## Iterates over the children of the NimNode ``n``.
+  ## Iterates over the children of the NimNode `n`.
   for i in 0 ..< n.len:
     yield n[i]
 
 iterator pairs*(n: NimNode): (int, NimNode) {.inline.} =
-  ## Iterates over the children of the NimNode ``n`` and its indices.
+  ## Iterates over the children of the NimNode `n` and its indices.
   for i in 0 ..< n.len:
     yield (i, n[i])
 
 iterator children*(n: NimNode): NimNode {.inline.} =
-  ## Iterates over the children of the NimNode ``n``.
+  ## Iterates over the children of the NimNode `n`.
   for i in 0 ..< n.len:
     yield n[i]
 
 template findChild*(n: NimNode; cond: untyped): NimNode {.dirty.} =
   ## Find the first child node matching condition (or nil).
-  ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   var res = findChild(n, it.kind == nnkPostfix and
-  ##                          it.basename.ident == toNimIdent"foo")
+  ##                          it.basename.ident == ident"foo")
+  ##   ```
   block:
     var res: NimNode
     for it in n.children:
@@ -1120,127 +1447,75 @@ template findChild*(n: NimNode; cond: untyped): NimNode {.dirty.} =
         break
     res
 
-proc insert*(a: NimNode; pos: int; b: NimNode) {.compileTime.} =
-  ## Insert node B into A at pos
+proc insert*(a: NimNode; pos: int; b: NimNode) =
+  ## Insert node `b` into node `a` at `pos`.
   if len(a)-1 < pos:
-    ## add some empty nodes first
+    # add some empty nodes first
     for i in len(a)-1..pos-2:
       a.add newEmptyNode()
     a.add b
   else:
-    ## push the last item onto the list again
-    ## and shift each item down to pos up one
+    # push the last item onto the list again
+    # and shift each item down to pos up one
     a.add(a[a.len-1])
     for i in countdown(len(a) - 3, pos):
       a[i + 1] = a[i]
     a[pos] = b
 
-proc basename*(a: NimNode): NimNode =
-  ## Pull an identifier from prefix/postfix expressions
-  case a.kind
-  of nnkIdent: return a
-  of nnkPostfix, nnkPrefix: return a[1]
-  else:
-    quit "Do not know how to get basename of (" & treeRepr(a) & ")\n" & repr(a)
-
-proc `basename=`*(a: NimNode; val: string) {.compileTime.}=
+proc `basename=`*(a: NimNode; val: string) =
   case a.kind
-  of nnkIdent: macros.`ident=`(a, toNimIdent val)
-  of nnkPostfix, nnkPrefix: a[1] = ident(val)
+  of nnkIdent:
+    a.strVal = val
+  of nnkPostfix, nnkPrefix:
+    a[1] = ident(val)
+  of nnkPragmaExpr: `basename=`(a[0], val)
   else:
-    quit "Do not know how to get basename of (" & treeRepr(a) & ")\n" & repr(a)
+    error("Do not know how to get basename of (" & treeRepr(a) & ")\n" &
+      repr(a), a)
 
-proc postfix*(node: NimNode; op: string): NimNode {.compileTime.} =
+proc postfix*(node: NimNode; op: string): NimNode =
   newNimNode(nnkPostfix).add(ident(op), node)
 
-proc prefix*(node: NimNode; op: string): NimNode {.compileTime.} =
+proc prefix*(node: NimNode; op: string): NimNode =
   newNimNode(nnkPrefix).add(ident(op), node)
 
 proc infix*(a: NimNode; op: string;
-            b: NimNode): NimNode {.compileTime.} =
+            b: NimNode): NimNode =
   newNimNode(nnkInfix).add(ident(op), a, b)
 
-proc unpackPostfix*(node: NimNode): tuple[node: NimNode; op: string] {.
-  compileTime.} =
+proc unpackPostfix*(node: NimNode): tuple[node: NimNode; op: string] =
   node.expectKind nnkPostfix
   result = (node[1], $node[0])
 
-proc unpackPrefix*(node: NimNode): tuple[node: NimNode; op: string] {.
-  compileTime.} =
+proc unpackPrefix*(node: NimNode): tuple[node: NimNode; op: string] =
   node.expectKind nnkPrefix
   result = (node[1], $node[0])
 
-proc unpackInfix*(node: NimNode): tuple[left: NimNode; op: string;
-                                        right: NimNode] {.compileTime.} =
-  assert node.kind == nnkInfix
+proc unpackInfix*(node: NimNode): tuple[left: NimNode; op: string; right: NimNode] =
+  expectKind(node, nnkInfix)
   result = (node[1], $node[0], node[2])
 
-proc copy*(node: NimNode): NimNode {.compileTime.} =
-  ## An alias for copyNimTree().
+proc copy*(node: NimNode): NimNode =
+  ## An alias for `copyNimTree<#copyNimTree,NimNode>`_.
   return node.copyNimTree()
 
-when defined(nimVmEqIdent):
-  proc eqIdent*(a: string; b: string): bool {.magic: "EqIdent", noSideEffect.}
-    ## Style insensitive comparison.
-
-  proc eqIdent*(a: NimNode; b: string): bool {.magic: "EqIdent", noSideEffect.}
-    ## Style insensitive comparison.
-    ## ``a`` can be an identifier or a symbol.
-
-  proc eqIdent*(a: string; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
-    ## Style insensitive comparison.
-    ## ``b`` can be an identifier or a symbol.
-
-  proc eqIdent*(a: NimNode; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
-    ## Style insensitive comparison.
-    ## ``a`` and ``b`` can be an identifier or a symbol.
-
-else:
-  # this procedure is optimized for native code, it should not be compiled to nimVM bytecode.
-  proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} =
-    proc toLower(c: char): char {.inline.} =
-      if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A')))
-      else: result = c
-    var i = 0
-    var j = 0
-    # first char is case sensitive
-    if a[0] != b[0]: return 1
-    while true:
-      while a[i] == '_': inc(i)
-      while b[j] == '_': inc(j) # BUGFIX: typo
-      var aa = toLower(a[i])
-      var bb = toLower(b[j])
-      result = ord(aa) - ord(bb)
-      if result != 0 or aa == '\0': break
-      inc(i)
-      inc(j)
-
-
-  proc eqIdent*(a, b: string): bool = cmpIgnoreStyle(a, b) == 0
-    ## Check if two idents are identical.
-
-  proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} =
-    ## Check if node is some identifier node (``nnkIdent``, ``nnkSym``, etc.)
-    ## is the same as ``s``. Note that this is the preferred way to check! Most
-    ## other ways like ``node.ident`` are much more error-prone, unfortunately.
-    case node.kind
-    of nnkSym, nnkIdent:
-      result = eqIdent(node.strVal, s)
-    of nnkOpenSymChoice, nnkClosedSymChoice:
-      result = eqIdent($node[0], s)
-    else:
-      result = false
-
-proc hasArgOfName*(params: NimNode; name: string): bool {.compiletime.}=
-  ## Search nnkFormalParams for an argument.
-  assert params.kind == nnkFormalParams
-  for i in 1 ..< params.len:
-    template node: untyped = params[i]
-    if name.eqIdent( $ node[0]):
-      return true
-
-proc addIdentIfAbsent*(dest: NimNode, ident: string) {.compiletime.} =
-  ## Add ident to dest if it is not present. This is intended for use
+proc expectIdent*(n: NimNode, name: string) {.since: (1,1).} =
+  ## Check that `eqIdent(n,name)` holds true. If this is not the
+  ## case, compilation aborts with an error message. This is useful
+  ## for writing macros that check the AST that is passed to them.
+  if not eqIdent(n, name):
+    error("Expected identifier to be `" & name & "` here", n)
+
+proc hasArgOfName*(params: NimNode; name: string): bool =
+  ## Search `nnkFormalParams` for an argument.
+  expectKind(params, nnkFormalParams)
+  for i in 1..<params.len:
+    for j in 0..<params[i].len-2:
+      if name.eqIdent($params[i][j]):
+        return true
+
+proc addIdentIfAbsent*(dest: NimNode, ident: string) =
+  ## Add `ident` to `dest` if it is not present. This is intended for use
   ## with pragmas.
   for node in dest.children:
     case node.kind
@@ -1251,10 +1526,15 @@ proc addIdentIfAbsent*(dest: NimNode, ident: string) {.compiletime.} =
     else: discard
   dest.add(ident(ident))
 
-proc boolVal*(n: NimNode): bool {.compileTime, noSideEffect.} =
+proc boolVal*(n: NimNode): bool {.noSideEffect.} =
   if n.kind == nnkIntLit: n.intVal != 0
   else: n == bindSym"true" # hacky solution for now
 
+proc nodeID*(n: NimNode): int {.magic: "NodeId".}
+  ## Returns the id of `n`, when the compiler has been compiled
+  ## with the flag `-d:useNodeids`, otherwise returns `-1`. This
+  ## proc is for the purpose to debug the compiler only.
+
 macro expandMacros*(body: typed): untyped =
   ## Expands one level of macro - useful for debugging.
   ## Can be used to inspect what happens when a macro call is expanded,
@@ -1262,31 +1542,53 @@ macro expandMacros*(body: typed): untyped =
   ##
   ## For instance,
   ##
-  ## .. code-block:: nim
-  ##   import future, macros
+  ##   ```nim
+  ##   import std/[sugar, macros]
   ##
   ##   let
   ##     x = 10
   ##     y = 20
   ##   expandMacros:
   ##     dump(x + y)
+  ##   ```
   ##
   ## will actually dump `x + y`, but at the same time will print at
-  ## compile time the expansion of the ``dump`` macro, which in this
-  ## case is ``debugEcho ["x + y", " = ", x + y]``.
-  template inner(x: untyped): untyped = x
+  ## compile time the expansion of the `dump` macro, which in this
+  ## case is `debugEcho ["x + y", " = ", x + y]`.
+  echo body.toStrLit
+  result = body
 
-  result = getAst(inner(body))
-  echo result.toStrLit
+proc extractTypeImpl(n: NimNode): NimNode =
+  ## attempts to extract the type definition of the given symbol
+  case n.kind
+  of nnkSym: # can extract an impl
+    result = n.getImpl.extractTypeImpl()
+  of nnkObjectTy, nnkRefTy, nnkPtrTy: result = n
+  of nnkBracketExpr:
+    if n.typeKind == ntyTypeDesc:
+      result = n[1].extractTypeImpl()
+    else:
+      doAssert n.typeKind == ntyGenericInst
+      result = n[0].getImpl()
+  of nnkTypeDef:
+    result = n[2]
+  else: error("Invalid node to retrieve type implementation of: " & $n.kind)
 
 proc customPragmaNode(n: NimNode): NimNode =
-  expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkCheckedFieldExpr})
+  expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkType, nnkCheckedFieldExpr})
   let
     typ = n.getTypeInst()
 
-  if typ.typeKind == ntyTypeDesc:
-    let impl = typ[1].getImpl()
-    if impl[0].kind == nnkPragmaExpr:
+  if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy:
+    return typ[1][1]
+  elif typ.typeKind == ntyTypeDesc:
+    let impl = getImpl(
+      if kind(typ[1]) == nnkBracketExpr: typ[1][0]
+      else: typ[1]
+    )
+    if impl.kind == nnkNilLit:
+      return impl
+    elif impl[0].kind == nnkPragmaExpr:
       return impl[0][1]
     else:
       return impl[0] # handle types which don't have macro at all
@@ -1295,39 +1597,55 @@ proc customPragmaNode(n: NimNode): NimNode =
     let impl = n.getImpl()
     if impl.kind in RoutineNodes:
       return impl.pragma
+    elif impl.kind in {nnkIdentDefs, nnkConstDef} and impl[0].kind == nnkPragmaExpr:
+      return impl[0][1]
     else:
-      return typ.getImpl()[0][1]
+      let timpl = getImpl(if typ.kind == nnkBracketExpr: typ[0] else: typ)
+      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])
-    var typDef = getImpl(getTypeInst(if n.kind == nnkCheckedFieldExpr or n[0].kind == nnkHiddenDeref: n[0][0] else: n[0]))
+    let name = $(if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1])
+    var typInst = getTypeInst(if n.kind == nnkCheckedFieldExpr or n[0].kind == nnkHiddenDeref: n[0][0] else: n[0])
+    while typInst.kind in {nnkVarTy, nnkBracketExpr}: typInst = typInst[0]
+    var typDef = getImpl(typInst)
     while typDef != nil:
       typDef.expectKind(nnkTypeDef)
-      typDef[2].expectKind({nnkRefTy, nnkPtrTy, nnkObjectTy})
-      let isRef = typDef[2].kind in {nnkRefTy, nnkPtrTy}
-      if isRef and typDef[2][0].kind in {nnkSym, nnkBracketExpr}: # defines ref type for another object(e.g. X = ref X)
-        typDef = getImpl(typDef[2][0])
+      let typ = typDef[2].extractTypeImpl()
+      if typ.kind notin {nnkRefTy, nnkPtrTy, nnkObjectTy}: break
+      let isRef = typ.kind in {nnkRefTy, nnkPtrTy}
+      if isRef and typ[0].kind in {nnkSym, nnkBracketExpr}: # defines ref type for another object(e.g. X = ref X)
+        typDef = getImpl(typ[0])
       else: # object definition, maybe an object directly defined as a ref type
         let
-          obj = (if isRef: typDef[2][0] else: typDef[2])
+          obj = (if isRef: typ[0] else: typ)
         var identDefsStack = newSeq[NimNode](obj[2].len)
         for i in 0..<identDefsStack.len: identDefsStack[i] = obj[2][i]
         while identDefsStack.len > 0:
           var identDefs = identDefsStack.pop()
-          if identDefs.kind == nnkRecCase:
-            identDefsStack.add(identDefs[0])
-            for i in 1..<identDefs.len:
-              if identDefs[i][1].kind == nnkIdentDefs:
-                identDefsStack.add(identDefs[i][1])
-              else: # nnkRecList
-                for j in 0..<identDefs[i][1].len:
-                  identDefsStack.add(identDefs[i][1][j])
 
+          case identDefs.kind
+          of nnkRecList:
+            for child in identDefs.children:
+              identDefsStack.add(child)
+          of nnkRecCase:
+            # Add condition definition
+            identDefsStack.add(identDefs[0])
+            # Add branches
+            for i in 1 ..< identDefs.len:
+              identDefsStack.add(identDefs[i].last)
           else:
             for i in 0 .. identDefs.len - 3:
-              if identDefs[i].kind == nnkPragmaExpr and
-                identDefs[i][0].kind == nnkIdent and $identDefs[i][0] == $name:
-                return identDefs[i][1]
+              let varNode = identDefs[i]
+              if varNode.kind == nnkPragmaExpr:
+                var varName = varNode[0]
+                if varName.kind == nnkPostfix:
+                  # This is a public field. We are skipping the postfix *
+                  varName = varName[1]
+                if eqIdent($varName, name):
+                  return varNode[1]
 
         if obj[1].kind == nnkOfInherit: # explore the parent object
           typDef = getImpl(obj[1][0])
@@ -1338,9 +1656,9 @@ macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped =
   ## Expands to `true` if expression `n` which is expected to be `nnkDotExpr`
   ## (if checking a field), a proc or a type has custom pragma `cp`.
   ##
-  ## See also `getCustomPragmaVal`.
+  ## See also `getCustomPragmaVal`_.
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   template myAttr() {.pragma.}
   ##   type
   ##     MyObj = object
@@ -1351,6 +1669,7 @@ macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped =
   ##   var o: MyObj
   ##   assert(o.myField.hasCustomPragma(myAttr))
   ##   assert(myProc.hasCustomPragma(myAttr))
+  ##   ```
   let pragmaNode = customPragmaNode(n)
   for p in pragmaNode:
     if (p.kind == nnkSym and p == cp) or
@@ -1362,9 +1681,9 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped =
   ## Expands to value of custom pragma `cp` of expression `n` which is expected
   ## to be `nnkDotExpr`, a proc or a type.
   ##
-  ## See also `hasCustomPragma`
+  ## See also `hasCustomPragma`_.
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   template serializationKey(key: string) {.pragma.}
   ##   type
   ##     MyObj {.serializationKey: "mo".} = object
@@ -1373,33 +1692,121 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped =
   ##   assert(o.myField.getCustomPragmaVal(serializationKey) == "mf")
   ##   assert(o.getCustomPragmaVal(serializationKey) == "mo")
   ##   assert(MyObj.getCustomPragmaVal(serializationKey) == "mo")
+  ##   ```
+  result = nil
   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,
-
-
-when not defined(booting):
-  template emit*(e: static[string]): untyped {.deprecated.} =
-    ## accepts a single string argument and treats it as nim code
-    ## that should be inserted verbatim in the program
-    ## Example:
-    ##
-    ## .. code-block:: nim
-    ##   emit("echo " & '"' & "hello world".toUpper & '"')
-    ##
-    ## Deprecated since version 0.15 since it's so rarely useful.
-    macro payload: untyped {.gensym.} =
-      result = parseStmt(e)
-    payload()
+      if p.len == 2 or (p.len == 3 and p[1].kind == nnkSym and p[1].symKind == nskType):
+        result = p[1]
+      else:
+        let def = p[0].getImpl[3]
+        result = newTree(nnkPar)
+        for i in 1 ..< def.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,
 
 macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped =
+  ## Calls `callee` with `args` unpacked as individual arguments.
+  ## This is useful in 2 cases:
+  ## * when forwarding `varargs[T]` for some typed `T`
+  ## * when forwarding `varargs[untyped]` when `args` can potentially be empty,
+  ##   due to a compiler limitation
+  runnableExamples:
+    template call1(fun: typed; args: varargs[untyped]): untyped =
+      unpackVarargs(fun, args)
+      # when varargsLen(args) > 0: fun(args) else: fun() # this would also work
+    template call2(fun: typed; args: varargs[typed]): untyped =
+      unpackVarargs(fun, args)
+    proc fn1(a = 0, b = 1) = discard (a, b)
+    call1(fn1, 10, 11)
+    call1(fn1) # `args` is empty in this case
+    if false: call2(echo, 10, 11) # would print 1011
   result = newCall(callee)
   for i in 0 ..< args.len:
     result.add args[i]
 
 proc getProjectPath*(): string = discard
-  ## Returns the path to the currently compiling project
-
+  ## Returns the path to the currently compiling project.
+  ##
+  ## This is not to be confused with `system.currentSourcePath <system.html#currentSourcePath.t>`_
+  ## which returns the path of the source file containing that template
+  ## call.
+  ##
+  ## For example, assume a `dir1/foo.nim` that imports a `dir2/bar.nim`,
+  ## have the `bar.nim` print out both `getProjectPath` and
+  ## `currentSourcePath` outputs.
+  ##
+  ## Now when `foo.nim` is compiled, the `getProjectPath` from
+  ## `bar.nim` will return the `dir1/` path, while the `currentSourcePath`
+  ## will return the path to the `bar.nim` source file.
+  ##
+  ## Now when `bar.nim` is compiled directly, the `getProjectPath`
+  ## will now return the `dir2/` path, and the `currentSourcePath`
+  ## will still return the same path, the path to the `bar.nim` source
+  ## file.
+  ##
+  ## The path returned by this proc is set at compile time.
+  ##
+  ## See also:
+  ## * `getCurrentDir proc <os.html#getCurrentDir>`_
+
+proc getSize*(arg: NimNode): int {.magic: "NSizeOf", noSideEffect.} =
+  ## Returns the same result as `system.sizeof` if the size is
+  ## known by the Nim compiler. Returns a negative value if the Nim
+  ## compiler does not know the size.
+proc getAlign*(arg: NimNode): int {.magic: "NSizeOf", noSideEffect.} =
+  ## Returns the same result as `system.alignof` if the alignment
+  ## is known by the Nim compiler. It works on `NimNode` for use
+  ## in macro context. Returns a negative value if the Nim compiler
+  ## does not know the alignment.
+proc getOffset*(arg: NimNode): int {.magic: "NSizeOf", noSideEffect.} =
+  ## Returns the same result as `system.offsetof` if the offset is
+  ## known by the Nim compiler. It expects a resolved symbol node
+  ## from a field of a type. Therefore it only requires one argument
+  ## instead of two. Returns a negative value if the Nim compiler
+  ## does not know the offset.
+
+proc isExported*(n: NimNode): bool {.noSideEffect.} =
+  ## Returns whether the symbol is exported or not.
+
+proc extractDocCommentsAndRunnables*(n: NimNode): NimNode =
+  ## returns a `nnkStmtList` containing the top-level doc comments and
+  ## runnableExamples in `a`, stopping at the first child that is neither.
+  ## Example:
+  ##
+  ##   ```nim
+  ##   import std/macros
+  ##   macro transf(a): untyped =
+  ##     result = quote do:
+  ##       proc fun2*() = discard
+  ##     let header = extractDocCommentsAndRunnables(a.body)
+  ##     # correct usage: rest is appended
+  ##     result.body = header
+  ##     result.body.add quote do: discard # just an example
+  ##     # incorrect usage: nesting inside a nnkStmtList:
+  ##     # result.body = quote do: (`header`; discard)
+  ##
+  ##   proc fun*() {.transf.} =
+  ##     ## first comment
+  ##     runnableExamples: discard
+  ##     runnableExamples: discard
+  ##     ## last comment
+  ##     discard # first statement after doc comments + runnableExamples
+  ##     ## not docgen'd
+  ##   ```
+
+  result = newStmtList()
+  for ni in n:
+    case ni.kind
+    of nnkCommentStmt:
+      result.add ni
+    of nnkCall, nnkCommand:
+      if ni[0].kind == nnkIdent and ni[0].eqIdent "runnableExamples":
+        result.add ni
+      else: break
+    else: break
diff --git a/lib/core/refs.nim b/lib/core/refs.nim
deleted file mode 100644
index e1575b68c..000000000
--- a/lib/core/refs.nim
+++ /dev/null
@@ -1,97 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2017 Nim contributors
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Default ref implementation used by Nim's core.
-
-# We cannot use the allocator interface here as we require a heap walker to
-# exist. Thus we import 'alloc' directly here to get our own heap that is
-# all under the GC's control and can use the ``allObjects`` iterator which
-# is crucial for the "sweep" phase.
-import typelayouts, alloc
-
-type
-  TracingGc = ptr object of Allocator
-    visit*: proc (fieldAddr: ptr pointer; a: Allocator) {.nimcall.}
-
-  GcColor = enum
-    white = 0, black = 1, grey = 2 ## to flip the meaning of white/black
-                                   ## perform (1 - col)
-
-  GcHeader = object
-    t: ptr TypeLayout
-    color: GcColor
-  Cell = ptr GcHeader
-
-  GcFrame {.core.} = object
-    prev: ptr GcFrame
-    marker: proc (self: GcFrame; a: Allocator)
-
-  Phase = enum
-    None, Marking, Sweeping
-
-  GcHeap = object
-    r: MemRegion
-    phase: Phase
-    currBlack, currWhite: GcColor
-    greyStack: seq[Cell]
-
-var
-  gch {.threadvar.}: GcHeap
-
-proc `=trace`[T](a: ref T) =
-  if not marked(a):
-    mark(a)
-    `=trace`(a[])
-
-template usrToCell(p: pointer): Cell =
-
-template cellToUsr(cell: Cell): pointer =
-  cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(GcHeader)))
-
-template usrToCell(usr: pointer): Cell =
-  cast[Cell](cast[ByteAddress](usr)-%ByteAddress(sizeof(GcHeader)))
-
-template markGrey(x: Cell) =
-  if x.color == gch.currWhite and phase == Marking:
-    x.color = grey
-    add(gch.greyStack, x)
-
-proc `=`[T](dest: var ref T; src: ref T) =
-  ## full write barrier implementation.
-  if src != nil:
-    let s = usrToCell(src)
-    markGrey(s)
-  system.`=`(dest, src)
-
-proc linkGcFrame(f: ptr GcFrame) {.core.}
-proc unlinkGcFrame() {.core.}
-
-proc setGcFrame(f: ptr GcFrame) {.core.}
-
-proc registerGlobal(p: pointer; t: ptr TypeLayout) {.core.}
-proc unregisterGlobal(p: pointer; t: ptr TypeLayout) {.core.}
-
-proc registerThreadvar(p: pointer; t: ptr TypeLayout) {.core.}
-proc unregisterThreadvar(p: pointer; t: ptr TypeLayout) {.core.}
-
-proc newImpl(t: ptr TypeLayout): pointer =
-  let r = cast[Cell](rawAlloc(t.size + sizeof(GcHeader)))
-  r.typ = t
-  result = r +! sizeof(GcHeader)
-
-template new*[T](x: var ref T) =
-  x = newImpl(getTypeLayout(x))
-
-
-when false:
-  # implement these if your GC requires them:
-  proc writeBarrierLocal() {.core.}
-  proc writeBarrierGlobal() {.core.}
-
-  proc writeBarrierGeneric() {.core.}
diff --git a/lib/core/rlocks.nim b/lib/core/rlocks.nim
index 4710d6cf1..8cb0cef05 100644
--- a/lib/core/rlocks.nim
+++ b/lib/core/rlocks.nim
@@ -9,8 +9,14 @@
 
 ## This module contains Nim's support for reentrant locks.
 
-const insideRLocksModule = true
-include "system/syslocks"
+
+when not compileOption("threads") and not defined(nimdoc):
+  when false:
+    # make rlocks modlue consistent with locks module,
+    # so they can replace each other seamlessly.
+    {.error: "Rlocks requires --threads:on option.".}
+
+import std/private/syslocks
 
 type
   RLock* = SysLock ## Nim lock, re-entrant
@@ -20,32 +26,32 @@ proc initRLock*(lock: var RLock) {.inline.} =
   when defined(posix):
     var a: SysLockAttr
     initSysLockAttr(a)
-    setSysLockType(a, SysLockType_Reentrant())
+    setSysLockType(a, SysLockType_Reentrant)
     initSysLock(lock, a.addr)
   else:
     initSysLock(lock)
 
-proc deinitRLock*(lock: var RLock) {.inline.} =
+proc deinitRLock*(lock: RLock) {.inline.} =
   ## Frees the resources associated with the lock.
   deinitSys(lock)
 
-proc tryAcquire*(lock: var RLock): bool =
+proc tryAcquire*(lock: var RLock): bool {.inline.} =
   ## Tries to acquire the given lock. Returns `true` on success.
   result = tryAcquireSys(lock)
 
-proc acquire*(lock: var RLock) =
+proc acquire*(lock: var RLock) {.inline.} =
   ## Acquires the given lock.
   acquireSys(lock)
 
-proc release*(lock: var RLock) =
+proc release*(lock: var RLock) {.inline.} =
   ## Releases the given lock.
   releaseSys(lock)
 
-template withRLock*(lock: var RLock, code: untyped): untyped =
+template withRLock*(lock: RLock, code: untyped) =
   ## Acquires the given lock and then executes the code.
-  block:
-    acquire(lock)
-    defer:
-      release(lock)
-    {.locks: [lock].}:
+  acquire(lock)
+  {.locks: [lock].}:
+    try:
       code
+    finally:
+      release(lock)
diff --git a/lib/core/seqs.nim b/lib/core/seqs.nim
deleted file mode 100644
index 02c192851..000000000
--- a/lib/core/seqs.nim
+++ /dev/null
@@ -1,139 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2017 Nim contributors
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-import allocators, typetraits
-
-## Default seq implementation used by Nim's core.
-type
-  seq*[T] = object
-    len, cap: int
-    data: ptr UncheckedArray[T]
-
-const nimSeqVersion {.core.} = 2
-
-template frees(s) = dealloc(s.data, s.cap * sizeof(T))
-
-# XXX make code memory safe for overflows in '*'
-
-when defined(nimHasTrace):
-  proc `=trace`[T](s: seq[T]; a: Allocator) =
-    for i in 0 ..< s.len: `=trace`(s.data[i], a)
-
-proc `=destroy`[T](x: var seq[T]) =
-  if x.data != nil:
-    when not supportsCopyMem(T):
-      for i in 0..<x.len: `=destroy`(x[i])
-    frees(x)
-    x.data = nil
-    x.len = 0
-    x.cap = 0
-
-proc `=`[T](a: var seq[T]; b: seq[T]) =
-  if a.data == b.data: return
-  if a.data != nil:
-    frees(a)
-    a.data = nil
-  a.len = b.len
-  a.cap = b.cap
-  if b.data != nil:
-    a.data = cast[type(a.data)](alloc(a.cap * sizeof(T)))
-    when supportsCopyMem(T):
-      copyMem(a.data, b.data, a.cap * sizeof(T))
-    else:
-      for i in 0..<a.len:
-        a.data[i] = b.data[i]
-
-proc `=sink`[T](a: var seq[T]; b: seq[T]) =
-  if a.data != nil and a.data != b.data:
-    frees(a)
-  a.len = b.len
-  a.cap = b.cap
-  a.data = b.data
-
-proc resize[T](s: var seq[T]) =
-  let old = s.cap
-  if old == 0: s.cap = 8
-  else: s.cap = (s.cap * 3) shr 1
-  s.data = cast[type(s.data)](realloc(s.data, old * sizeof(T), s.cap * sizeof(T)))
-
-proc reserveSlot[T](x: var seq[T]): ptr T =
-  if x.len >= x.cap: resize(x)
-  result = addr(x.data[x.len])
-  inc x.len
-
-template add*[T](x: var seq[T]; y: T) =
-  reserveSlot(x)[] = y
-
-proc shrink*[T](x: var seq[T]; newLen: int) =
-  assert newLen <= x.len
-  assert newLen >= 0
-  when not supportsCopyMem(T):
-    for i in countdown(x.len - 1, newLen - 1):
-      `=destroy`(x.data[i])
-  x.len = newLen
-
-proc grow*[T](x: var seq[T]; newLen: int; value: T) =
-  if newLen <= x.len: return
-  assert newLen >= 0
-  if x.cap == 0: x.cap = newLen
-  else: x.cap = max(newLen, (x.cap * 3) shr 1)
-  x.data = cast[type(x.data)](realloc(x.data, x.cap * sizeof(T)))
-  for i in x.len..<newLen:
-    x.data[i] = value
-  x.len = newLen
-
-template default[T](t: typedesc[T]): T =
-  var v: T
-  v
-
-proc setLen*[T](x: var seq[T]; newLen: int) {.deprecated.} =
-  if newlen < x.len: shrink(x, newLen)
-  else: grow(x, newLen, default(T))
-
-template `[]`*[T](x: seq[T]; i: Natural): T =
-  assert i < x.len
-  x.data[i]
-
-template `[]=`*[T](x: seq[T]; i: Natural; y: T) =
-  assert i < x.len
-  x.data[i] = y
-
-proc `@`*[T](elems: openArray[T]): seq[T] =
-  result.cap = elems.len
-  result.len = elems.len
-  result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
-  when supportsCopyMem(T):
-    copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T))
-  else:
-    for i in 0..<result.len:
-      result.data[i] = elems[i]
-
-proc len*[T](x: seq[T]): int {.inline.} = x.len
-
-proc `$`*[T](x: seq[T]): string =
-  result = "@["
-  var firstElement = true
-  for i in 0..<x.len:
-    let
-      value = x.data[i]
-    if firstElement:
-      firstElement = false
-    else:
-      result.add(", ")
-
-    when compiles(value.isNil):
-      # this branch should not be necessary
-      if value.isNil:
-        result.add "nil"
-      else:
-        result.addQuoted(value)
-    else:
-      result.addQuoted(value)
-
-  result.add("]")
diff --git a/lib/core/strs.nim b/lib/core/strs.nim
deleted file mode 100644
index ff38aef1d..000000000
--- a/lib/core/strs.nim
+++ /dev/null
@@ -1,110 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2017 Nim contributors
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Default string implementation used by Nim's core.
-
-import allocators
-
-type
-  string {.core, exportc: "NimStringV2".} = object
-    len, cap: int
-    data: ptr UncheckedArray[char]
-
-const nimStrVersion {.core.} = 2
-
-template frees(s) = dealloc(s.data, s.cap + 1)
-
-proc `=destroy`(s: var string) =
-  if s.data != nil:
-    frees(s)
-    s.data = nil
-    s.len = 0
-    s.cap = 0
-
-proc `=sink`(a: var string, b: string) =
-  # we hope this is optimized away for not yet alive objects:
-  if a.data != nil and a.data != b.data:
-    frees(a)
-  a.len = b.len
-  a.cap = b.cap
-  a.data = b.data
-
-proc `=`(a: var string; b: string) =
-  if a.data != nil and a.data != b.data:
-    frees(a)
-    a.data = nil
-  a.len = b.len
-  a.cap = b.cap
-  if b.data != nil:
-    a.data = cast[type(a.data)](alloc(a.cap + 1))
-    copyMem(a.data, b.data, a.cap+1)
-
-proc resize(s: var string) =
-  let old = s.cap
-  if old == 0: s.cap = 8
-  else: s.cap = (s.cap * 3) shr 1
-  s.data = cast[type(s.data)](realloc(s.data, old + 1, s.cap + 1))
-
-proc add*(s: var string; c: char) =
-  if s.len >= s.cap: resize(s)
-  s.data[s.len] = c
-  s.data[s.len+1] = '\0'
-  inc s.len
-
-proc ensure(s: var string; newLen: int) =
-  let old = s.cap
-  if newLen >= old:
-    s.cap = max((old * 3) shr 1, newLen)
-    if s.cap > 0:
-      s.data = cast[type(s.data)](realloc(s.data, old + 1, s.cap + 1))
-
-proc add*(s: var string; y: string) =
-  if y.len != 0:
-    let newLen = s.len + y.len
-    ensure(s, newLen)
-    copyMem(addr s.data[len], y.data, y.data.len + 1)
-    s.len = newLen
-
-proc len*(s: string): int {.inline.} = s.len
-
-proc newString*(len: int): string =
-  result.len = len
-  result.cap = len
-  if len > 0:
-    result.data = alloc0(len+1)
-
-converter toCString(x: string): cstring {.core, inline.} =
-  if x.len == 0: cstring"" else: cast[cstring](x.data)
-
-proc newStringOfCap*(cap: int): string =
-  result.len = 0
-  result.cap = cap
-  if cap > 0:
-    result.data = alloc(cap+1)
-
-proc `&`*(a, b: string): string =
-  let sum = a.len + b.len
-  result = newStringOfCap(sum)
-  result.len = sum
-  copyMem(addr result.data[0], a.data, a.len)
-  copyMem(addr result.data[a.len], b.data, b.len)
-  if sum > 0:
-    result.data[sum] = '\0'
-
-proc concat(x: openArray[string]): string {.core.} =
-  ## used be the code generator to optimize 'x & y & z ...'
-  var sum = 0
-  for i in 0 ..< x.len: inc(sum, x[i].len)
-  result = newStringOfCap(sum)
-  sum = 0
-  for i in 0 ..< x.len:
-    let L = x[i].len
-    copyMem(addr result.data[sum], x[i].data, L)
-    inc(sum, L)
-
diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim
index 16580b318..f2fee91c4 100644
--- a/lib/core/typeinfo.nim
+++ b/lib/core/typeinfo.nim
@@ -8,12 +8,30 @@
 #
 
 ## This module implements an interface to Nim's `runtime type information`:idx:
-## (`RTTI`:idx:).
-## Note that even though ``Any`` and its operations hide the nasty low level
-## details from its clients, it remains inherently unsafe!
+## (`RTTI`:idx:). See the `marshal <marshal.html>`_ module for an example of
+## what this allows you to do.
 ##
-## See the `marshal <marshal.html>`_ module for what this module allows you
-## to do.
+## .. note:: Even though `Any` and its operations hide the nasty low level
+##   details from its users, it remains inherently unsafe! Also, Nim's
+##   runtime type information will evolve and may eventually be deprecated.
+##   As an alternative approach to programmatically understanding and
+##   manipulating types, consider using the `macros <macros.html>`_ module to
+##   work with the types' AST representation at compile time. See for example
+##   the `getTypeImpl proc <macros.html#getTypeImpl,NimNode>`_. As an alternative
+##   approach to storing arbitrary types at runtime, consider using generics.
+
+runnableExamples:
+  var x: Any
+
+  var i = 42
+  x = i.toAny
+  assert x.kind == akInt
+  assert x.getInt == 42
+
+  var s = @[1, 2, 3]
+  x = s.toAny
+  assert x.kind == akSequence
+  assert x.len == 3
 
 {.push hints: off.}
 
@@ -22,45 +40,50 @@ include "system/hti.nim"
 
 {.pop.}
 
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
 type
-  AnyKind* = enum      ## what kind of ``any`` it is
-    akNone = 0,         ## invalid any
-    akBool = 1,         ## any represents a ``bool``
-    akChar = 2,         ## any represents a ``char``
-    akEnum = 14,        ## any represents an enum
-    akArray = 16,       ## any represents an array
-    akObject = 17,      ## any represents an object
-    akTuple = 18,       ## any represents a tuple
-    akSet = 19,         ## any represents a set
-    akRange = 20,       ## any represents a range
-    akPtr = 21,         ## any represents a ptr
-    akRef = 22,         ## any represents a ref
-    akSequence = 24,    ## any represents a sequence
-    akProc = 25,        ## any represents a proc
-    akPointer = 26,     ## any represents a pointer
-    akString = 28,      ## any represents a string
-    akCString = 29,     ## any represents a cstring
-    akInt = 31,         ## any represents an int
-    akInt8 = 32,        ## any represents an int8
-    akInt16 = 33,       ## any represents an int16
-    akInt32 = 34,       ## any represents an int32
-    akInt64 = 35,       ## any represents an int64
-    akFloat = 36,       ## any represents a float
-    akFloat32 = 37,     ## any represents a float32
-    akFloat64 = 38,     ## any represents a float64
-    akFloat128 = 39,    ## any represents a float128
-    akUInt = 40,        ## any represents an unsigned int
-    akUInt8 = 41,       ## any represents an unsigned int8
-    akUInt16 = 42,      ## any represents an unsigned in16
-    akUInt32 = 43,      ## any represents an unsigned int32
-    akUInt64 = 44,      ## any represents an unsigned int64
+  AnyKind* = enum       ## The kind of `Any`.
+    akNone = 0,         ## invalid
+    akBool = 1,         ## bool
+    akChar = 2,         ## char
+    akEnum = 14,        ## enum
+    akArray = 16,       ## array
+    akObject = 17,      ## object
+    akTuple = 18,       ## tuple
+    akSet = 19,         ## set
+    akRange = 20,       ## range
+    akPtr = 21,         ## ptr
+    akRef = 22,         ## ref
+    akSequence = 24,    ## sequence
+    akProc = 25,        ## proc
+    akPointer = 26,     ## pointer
+    akString = 28,      ## string
+    akCString = 29,     ## cstring
+    akInt = 31,         ## int
+    akInt8 = 32,        ## int8
+    akInt16 = 33,       ## int16
+    akInt32 = 34,       ## int32
+    akInt64 = 35,       ## int64
+    akFloat = 36,       ## float
+    akFloat32 = 37,     ## float32
+    akFloat64 = 38,     ## float64
+    akFloat128 = 39,    ## float128
+    akUInt = 40,        ## uint
+    akUInt8 = 41,       ## uint8
+    akUInt16 = 42,      ## uin16
+    akUInt32 = 43,      ## uint32
+    akUInt64 = 44,      ## uint64
 #    akOpt = 44+18       ## the builtin 'opt' type.
 
-  Any* = object          ## can represent any nim value; NOTE: the wrapped
-                          ## value can be modified with its wrapper! This means
-                          ## that ``Any`` keeps a non-traced pointer to its
-                          ## wrapped value and **must not** live longer than
-                          ## its wrapped value.
+  Any* = object
+    ## A type that can represent any nim value.
+    ##
+    ## .. danger:: The wrapped value can be modified with its wrapper! This means
+    ##   that `Any` keeps a non-traced pointer to its wrapped value and
+    ##   **must not** live longer than its wrapped value.
     value: pointer
     when defined(js):
       rawType: PNimType
@@ -68,13 +91,25 @@ type
       rawTypePtr: pointer
 
   ppointer = ptr pointer
-  pbyteArray = ptr array[0xffff, int8]
+  pbyteArray = ptr array[0xffff, uint8]
+
+when not defined(gcDestructors):
+  type
+    TGenericSeq {.importc.} = object
+      len, space: int
+      when defined(gogc):
+        elemSize: int
+    PGenSeq = ptr TGenericSeq
+
+  when defined(gogc):
+    const GenericSeqSize = 3 * sizeof(int)
+  else:
+    const GenericSeqSize = 2 * sizeof(int)
 
-  TGenericSeq {.importc.} = object
-    len, space: int
-    when defined(gogc):
-      elemSize: int
-  PGenSeq = ptr TGenericSeq
+else:
+  include system/seqs_v2_reimpl
+
+from std/private/strimpl import cmpNimIdentifier
 
 when not defined(js):
   template rawType(x: Any): PNimType =
@@ -83,40 +118,42 @@ when not defined(js):
   template `rawType=`(x: var Any, p: PNimType) =
     x.rawTypePtr = cast[pointer](p)
 
-{.deprecated: [TAny: Any, TAnyKind: AnyKind].}
+proc genericAssign(dest, src: pointer, mt: PNimType) {.importCompilerProc.}
 
-when defined(gogc):
-  const GenericSeqSize = (3 * sizeof(int))
+when not defined(gcDestructors):
+  proc genericShallowAssign(dest, src: pointer, mt: PNimType) {.importCompilerProc.}
+  proc incrSeq(seq: PGenSeq, elemSize, elemAlign: int): PGenSeq {.importCompilerProc.}
+  proc newObj(typ: PNimType, size: int): pointer {.importCompilerProc.}
+  proc newSeq(typ: PNimType, len: int): pointer {.importCompilerProc.}
+  proc objectInit(dest: pointer, typ: PNimType) {.importCompilerProc.}
 else:
-  const GenericSeqSize = (2 * sizeof(int))
-
-proc genericAssign(dest, src: pointer, mt: PNimType) {.importCompilerProc.}
-proc genericShallowAssign(dest, src: pointer, mt: PNimType) {.
-  importCompilerProc.}
-proc incrSeq(seq: PGenSeq, elemSize: int): PGenSeq {.importCompilerProc.}
-proc newObj(typ: PNimType, size: int): pointer {.importCompilerProc.}
-proc newSeq(typ: PNimType, len: int): pointer {.importCompilerProc.}
-proc objectInit(dest: pointer, typ: PNimType) {.importCompilerProc.}
+  proc nimNewObj(size, align: int): pointer {.importCompilerProc.}
+  proc newSeqPayload(cap, elemSize, elemAlign: int): pointer {.importCompilerProc.}
+  proc prepareSeqAddUninit(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {.
+    importCompilerProc.}
+  proc zeroNewElements(len: int; p: pointer; addlen, elemSize, elemAlign: int) {.
+    importCompilerProc.}
 
-template `+!!`(a, b): untyped = cast[pointer](cast[ByteAddress](a) + b)
+template `+!!`(a, b): untyped = cast[pointer](cast[int](a) + b)
 
 proc getDiscriminant(aa: pointer, n: ptr TNimNode): int =
   assert(n.kind == nkCase)
   var d: int
-  var a = cast[ByteAddress](aa)
+  let a = cast[int](aa)
   case n.typ.size
-  of 1: d = ze(cast[ptr int8](a +% n.offset)[])
-  of 2: d = ze(cast[ptr int16](a +% n.offset)[])
-  of 4: d = int(cast[ptr int32](a +% n.offset)[])
+  of 1: d = int(cast[ptr uint8](a +% n.offset)[])
+  of 2: d = int(cast[ptr uint16](a +% n.offset)[])
+  of 4: d = int(cast[ptr uint32](a +% n.offset)[])
+  of 8: d = int(cast[ptr uint64](a +% n.offset)[])
   else: assert(false)
   return d
 
 proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode =
-  var discr = getDiscriminant(aa, n)
+  let discr = getDiscriminant(aa, n)
   if discr <% n.len:
     result = n.sons[discr]
     if result == nil: result = n.sons[n.len]
-    # n.sons[n.len] contains the ``else`` part (but may be nil)
+    # n.sons[n.len] contains the `else` part (but may be nil)
   else:
     result = n.sons[n.len]
 
@@ -124,143 +161,188 @@ proc newAny(value: pointer, rawType: PNimType): Any {.inline.} =
   result.value = value
   result.rawType = rawType
 
-when declared(system.VarSlot):
-  proc toAny*(x: VarSlot): Any {.inline.} =
-    ## constructs a ``Any`` object from a variable slot ``x``.
-    ## This captures `x`'s address, so `x` can be modified with its
-    ## ``Any`` wrapper! The client needs to ensure that the wrapper
-    ## **does not** live longer than `x`!
-    ## This is provided for easier reflection capabilities of a debugger.
-    result.value = x.address
-    result.rawType = x.typ
-
 proc toAny*[T](x: var T): Any {.inline.} =
-  ## constructs a ``Any`` object from `x`. This captures `x`'s address, so
-  ## `x` can be modified with its ``Any`` wrapper! The client needs to ensure
+  ## Constructs an `Any` object from `x`. This captures `x`'s address, so
+  ## `x` can be modified with its `Any` wrapper! The caller needs to ensure
   ## that the wrapper **does not** live longer than `x`!
   newAny(addr(x), cast[PNimType](getTypeInfo(x)))
 
 proc kind*(x: Any): AnyKind {.inline.} =
-  ## get the type kind
+  ## Gets the type kind.
   result = AnyKind(ord(x.rawType.kind))
 
 proc size*(x: Any): int {.inline.} =
-  ## returns the size of `x`'s type.
+  ## Returns the size of `x`'s type.
   result = x.rawType.size
 
 proc baseTypeKind*(x: Any): AnyKind {.inline.} =
-  ## get the base type's kind; ``akNone`` is returned if `x` has no base type.
+  ## Gets the base type's kind. If `x` has no base type, `akNone` is returned.
   if x.rawType.base != nil:
     result = AnyKind(ord(x.rawType.base.kind))
 
 proc baseTypeSize*(x: Any): int {.inline.} =
-  ## returns the size of `x`'s basetype.
+  ## Returns the size of `x`'s base type. If `x` has no base type, 0 is returned.
   if x.rawType.base != nil:
     result = x.rawType.base.size
 
 proc invokeNew*(x: Any) =
-  ## performs ``new(x)``. `x` needs to represent a ``ref``.
+  ## Performs `new(x)`. `x` needs to represent a `ref`.
   assert x.rawType.kind == tyRef
-  var z = newObj(x.rawType, x.rawType.base.size)
-  genericAssign(x.value, addr(z), x.rawType)
+  when defined(gcDestructors):
+    cast[ppointer](x.value)[] = nimNewObj(x.rawType.base.size, x.rawType.base.align)
+  else:
+    var z = newObj(x.rawType, x.rawType.base.size)
+    genericAssign(x.value, addr(z), x.rawType)
 
 proc invokeNewSeq*(x: Any, len: int) =
-  ## performs ``newSeq(x, len)``. `x` needs to represent a ``seq``.
+  ## Performs `newSeq(x, len)`. `x` needs to represent a `seq`.
   assert x.rawType.kind == tySequence
-  var z = newSeq(x.rawType, len)
-  genericShallowAssign(x.value, addr(z), x.rawType)
+  when defined(gcDestructors):
+    var s = cast[ptr NimSeqV2Reimpl](x.value)
+    s.len = len
+    let elem = x.rawType.base
+    s.p = cast[ptr NimSeqPayloadReimpl](newSeqPayload(len, elem.size, elem.align))
+  else:
+    var z = newSeq(x.rawType, len)
+    genericShallowAssign(x.value, addr(z), x.rawType)
 
 proc extendSeq*(x: Any) =
-  ## performs ``setLen(x, x.len+1)``. `x` needs to represent a ``seq``.
+  ## Performs `setLen(x, x.len+1)`. `x` needs to represent a `seq`.
   assert x.rawType.kind == tySequence
-  var y = cast[ptr PGenSeq](x.value)[]
-  var z = incrSeq(y, x.rawType.base.size)
-  # 'incrSeq' already freed the memory for us and copied over the RC!
-  # So we simply copy the raw pointer into 'x.value':
-  cast[ppointer](x.value)[] = z
-  #genericShallowAssign(x.value, addr(z), x.rawType)
+  when defined(gcDestructors):
+    var s = cast[ptr NimSeqV2Reimpl](x.value)
+    let elem = x.rawType.base
+    if s.p == nil or s.p.cap < s.len+1:
+      s.p = cast[ptr NimSeqPayloadReimpl](prepareSeqAddUninit(s.len, s.p, 1, elem.size, elem.align))
+    zeroNewElements(s.len, s.p, 1, elem.size, elem.align)
+    inc s.len
+  else:
+    var y = cast[ptr PGenSeq](x.value)[]
+    var z = incrSeq(y, x.rawType.base.size, x.rawType.base.align)
+    # 'incrSeq' already freed the memory for us and copied over the RC!
+    # So we simply copy the raw pointer into 'x.value':
+    cast[ppointer](x.value)[] = z
+    #genericShallowAssign(x.value, addr(z), x.rawType)
 
 proc setObjectRuntimeType*(x: Any) =
-  ## this needs to be called to set `x`'s runtime object type field.
+  ## This needs to be called to set `x`'s runtime object type field.
   assert x.rawType.kind == tyObject
-  objectInit(x.value, x.rawType)
+  when defined(gcDestructors):
+    cast[ppointer](x.value)[] = x.rawType.typeInfoV2
+  else:
+    objectInit(x.value, x.rawType)
 
 proc skipRange(x: PNimType): PNimType {.inline.} =
   result = x
   if result.kind == tyRange: result = result.base
 
+proc align(address, alignment: int): int =
+  result = (address + (alignment - 1)) and not (alignment - 1)
+
 proc `[]`*(x: Any, i: int): Any =
-  ## accessor for an any `x` that represents an array or a sequence.
+  ## Accessor for an any `x` that represents an array or a sequence.
   case x.rawType.kind
   of tyArray:
-    var bs = x.rawType.base.size
+    let bs = x.rawType.base.size
     if i >=% x.rawType.size div bs:
-      raise newException(IndexError, "index out of bounds")
+      raise newException(IndexDefect, formatErrorIndexBound(i, x.rawType.size div bs))
     return newAny(x.value +!! i*bs, x.rawType.base)
   of tySequence:
-    var s = cast[ppointer](x.value)[]
-    if s == nil: raise newException(ValueError, "sequence is nil")
-    var bs = x.rawType.base.size
-    if i >=% cast[PGenSeq](s).len:
-      raise newException(IndexError, "index out of bounds")
-    return newAny(s +!! (GenericSeqSize+i*bs), x.rawType.base)
+    when defined(gcDestructors):
+      var s = cast[ptr NimSeqV2Reimpl](x.value)
+      if i >=% s.len:
+        raise newException(IndexDefect, formatErrorIndexBound(i, s.len-1))
+      let bs = x.rawType.base.size
+      let ba = x.rawType.base.align
+      let headerSize = align(sizeof(int), ba)
+      return newAny(s.p +!! (headerSize+i*bs), x.rawType.base)
+    else:
+      var s = cast[ppointer](x.value)[]
+      if s == nil: raise newException(ValueError, "sequence is nil")
+      let bs = x.rawType.base.size
+      if i >=% cast[PGenSeq](s).len:
+        raise newException(IndexDefect, formatErrorIndexBound(i, cast[PGenSeq](s).len-1))
+      return newAny(s +!! (align(GenericSeqSize, x.rawType.base.align)+i*bs), x.rawType.base)
   else: assert false
 
 proc `[]=`*(x: Any, i: int, y: Any) =
-  ## accessor for an any `x` that represents an array or a sequence.
+  ## Accessor for an any `x` that represents an array or a sequence.
   case x.rawType.kind
   of tyArray:
     var bs = x.rawType.base.size
     if i >=% x.rawType.size div bs:
-      raise newException(IndexError, "index out of bounds")
+      raise newException(IndexDefect, formatErrorIndexBound(i, x.rawType.size div bs))
     assert y.rawType == x.rawType.base
     genericAssign(x.value +!! i*bs, y.value, y.rawType)
   of tySequence:
-    var s = cast[ppointer](x.value)[]
-    if s == nil: raise newException(ValueError, "sequence is nil")
-    var bs = x.rawType.base.size
-    if i >=% cast[PGenSeq](s).len:
-      raise newException(IndexError, "index out of bounds")
-    assert y.rawType == x.rawType.base
-    genericAssign(s +!! (GenericSeqSize+i*bs), y.value, y.rawType)
+    when defined(gcDestructors):
+      var s = cast[ptr NimSeqV2Reimpl](x.value)
+      if i >=% s.len:
+        raise newException(IndexDefect, formatErrorIndexBound(i, s.len-1))
+      let bs = x.rawType.base.size
+      let ba = x.rawType.base.align
+      let headerSize = align(sizeof(int), ba)
+      assert y.rawType == x.rawType.base
+      genericAssign(s.p +!! (headerSize+i*bs), y.value, y.rawType)
+    else:
+      var s = cast[ppointer](x.value)[]
+      if s == nil: raise newException(ValueError, "sequence is nil")
+      var bs = x.rawType.base.size
+      if i >=% cast[PGenSeq](s).len:
+        raise newException(IndexDefect, formatErrorIndexBound(i, cast[PGenSeq](s).len-1))
+      assert y.rawType == x.rawType.base
+      genericAssign(s +!! (align(GenericSeqSize, x.rawType.base.align)+i*bs), y.value, y.rawType)
   else: assert false
 
 proc len*(x: Any): int =
-  ## len for an any `x` that represents an array or a sequence.
+  ## `len` for an any `x` that represents an array or a sequence.
   case x.rawType.kind
-  of tyArray: result = x.rawType.size div x.rawType.base.size
-  of tySequence: result = cast[PGenSeq](cast[ppointer](x.value)[]).len
+  of tyArray:
+    result = x.rawType.size div x.rawType.base.size
+  of tySequence:
+    when defined(gcDestructors):
+      result = cast[ptr NimSeqV2Reimpl](x.value).len
+    else:
+      let pgenSeq = cast[PGenSeq](cast[ppointer](x.value)[])
+      if isNil(pgenSeq):
+        result = 0
+      else:
+        result = pgenSeq.len
   else: assert false
 
 
 proc base*(x: Any): Any =
-  ## returns base Any (useful for inherited object types).
+  ## Returns the base type of `x` (useful for inherited object types).
   result.rawType = x.rawType.base
   result.value = x.value
 
 
 proc isNil*(x: Any): bool =
-  ## `isNil` for an any `x` that represents a sequence, string, cstring,
-  ## proc or some pointer type.
-  assert x.rawType.kind in {tyString, tyCString, tyRef, tyPtr, tyPointer,
-                            tySequence, tyProc}
+  ## `isNil` for an `x` that represents a cstring, proc or
+  ## some pointer type.
+  assert x.rawType.kind in {tyCstring, tyRef, tyPtr, tyPointer, tyProc}
   result = isNil(cast[ppointer](x.value)[])
 
+const pointerLike =
+  when defined(gcDestructors): {tyCstring, tyRef, tyPtr, tyPointer, tyProc}
+  else: {tyString, tyCstring, tyRef, tyPtr, tyPointer, tySequence, tyProc}
+
 proc getPointer*(x: Any): pointer =
-  ## retrieve the pointer value out of `x`. ``x`` needs to be of kind
-  ## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``,
-  ## ``akPointer``, ``akSequence``.
-  assert x.rawType.kind in {tyString, tyCString, tyRef, tyPtr, tyPointer,
-                            tySequence, tyProc}
+  ## Retrieves the pointer value out of `x`. `x` needs to be of kind
+  ## `akString`, `akCString`, `akProc`, `akRef`, `akPtr`,
+  ## `akPointer` or `akSequence`.
+  assert x.rawType.kind in pointerLike
   result = cast[ppointer](x.value)[]
 
 proc setPointer*(x: Any, y: pointer) =
-  ## sets the pointer value of `x`. ``x`` needs to be of kind
-  ## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``,
-  ## ``akPointer``, ``akSequence``.
-  assert x.rawType.kind in {tyString, tyCString, tyRef, tyPtr, tyPointer,
-                            tySequence, tyProc}
-  cast[ppointer](x.value)[] = y
+  ## Sets the pointer value of `x`. `x` needs to be of kind
+  ## `akString`, `akCString`, `akProc`, `akRef`, `akPtr`,
+  ## `akPointer` or `akSequence`.
+  assert x.rawType.kind in pointerLike
+  if y != nil and x.rawType.kind != tyPointer:
+    genericAssign(x.value, y, x.rawType)
+  else:
+    cast[ppointer](x.value)[] = y
 
 proc fieldsAux(p: pointer, n: ptr TNimNode,
                ret: var seq[tuple[name: cstring, any: Any]]) =
@@ -277,15 +359,15 @@ proc fieldsAux(p: pointer, n: ptr TNimNode,
     if m != nil: fieldsAux(p, m, ret)
 
 iterator fields*(x: Any): tuple[name: string, any: Any] =
-  ## iterates over every active field of the any `x` that represents an object
+  ## Iterates over every active field of `x`. `x` needs to represent an object
   ## or a tuple.
   assert x.rawType.kind in {tyTuple, tyObject}
-  var p = x.value
+  let p = x.value
   var t = x.rawType
   # XXX BUG: does not work yet, however is questionable anyway
   when false:
     if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[]
-  var ret: seq[tuple[name: cstring, any: Any]] = @[]
+  var ret: seq[tuple[name: cstring, any: Any]]
   if t.kind == tyObject:
     while true:
       fieldsAux(p, t.node, ret)
@@ -296,48 +378,31 @@ iterator fields*(x: Any): tuple[name: string, any: Any] =
   for name, any in items(ret):
     yield ($name, any)
 
-proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} =
-  proc toLower(c: char): char {.inline.} =
-    if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A')))
-    else: result = c
-  var i = 0
-  var j = 0
-  while true:
-    while a[i] == '_': inc(i)
-    while b[j] == '_': inc(j) # BUGFIX: typo
-    var aa = toLower(a[i])
-    var bb = toLower(b[j])
-    result = ord(aa) - ord(bb)
-    if result != 0 or aa == '\0': break
-    inc(i)
-    inc(j)
-
-proc getFieldNode(p: pointer, n: ptr TNimNode,
-                  name: cstring): ptr TNimNode =
+proc getFieldNode(p: pointer, n: ptr TNimNode, name: cstring): ptr TNimNode =
   case n.kind
   of nkNone: assert(false)
   of nkSlot:
-    if cmpIgnoreStyle(n.name, name) == 0:
+    if cmpNimIdentifier(n.name, name) == 0:
       result = n
   of nkList:
     for i in 0..n.len-1:
       result = getFieldNode(p, n.sons[i], name)
       if result != nil: break
   of nkCase:
-    if cmpIgnoreStyle(n.name, name) == 0:
+    if cmpNimIdentifier(n.name, name) == 0:
       result = n
     else:
-      var m = selectBranch(p, n)
+      let m = selectBranch(p, n)
       if m != nil: result = getFieldNode(p, m, name)
 
 proc `[]=`*(x: Any, fieldName: string, value: Any) =
-  ## sets a field of `x`; `x` represents an object or a tuple.
+  ## Sets a field of `x`. `x` needs to represent an object or a tuple.
   var t = x.rawType
   # XXX BUG: does not work yet, however is questionable anyway
   when false:
     if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[]
   assert x.rawType.kind in {tyTuple, tyObject}
-  var n = getFieldNode(x.value, t.node, fieldName)
+  let n = getFieldNode(x.value, t.node, fieldName)
   if n != nil:
     assert n.typ == value.rawType
     genericAssign(x.value +!! n.offset, value.value, value.rawType)
@@ -345,13 +410,13 @@ proc `[]=`*(x: Any, fieldName: string, value: Any) =
     raise newException(ValueError, "invalid field name: " & fieldName)
 
 proc `[]`*(x: Any, fieldName: string): Any =
-  ## gets a field of `x`; `x` represents an object or a tuple.
+  ## Gets a field of `x`. `x` needs to represent an object or a tuple.
   var t = x.rawType
   # XXX BUG: does not work yet, however is questionable anyway
   when false:
     if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[]
   assert x.rawType.kind in {tyTuple, tyObject}
-  var n = getFieldNode(x.value, t.node, fieldName)
+  let n = getFieldNode(x.value, t.node, fieldName)
   if n != nil:
     result.value = x.value +!! n.offset
     result.rawType = n.typ
@@ -361,47 +426,47 @@ proc `[]`*(x: Any, fieldName: string): Any =
     raise newException(ValueError, "invalid field name: " & fieldName)
 
 proc `[]`*(x: Any): Any =
-  ## dereference operation for the any `x` that represents a ptr or a ref.
+  ## Dereference operator for `Any`. `x` needs to represent a ptr or a ref.
   assert x.rawType.kind in {tyRef, tyPtr}
   result.value = cast[ppointer](x.value)[]
   result.rawType = x.rawType.base
 
 proc `[]=`*(x, y: Any) =
-  ## dereference operation for the any `x` that represents a ptr or a ref.
+  ## Dereference operator for `Any`. `x` needs to represent a ptr or a ref.
   assert x.rawType.kind in {tyRef, tyPtr}
   assert y.rawType == x.rawType.base
   genericAssign(cast[ppointer](x.value)[], y.value, y.rawType)
 
 proc getInt*(x: Any): int =
-  ## retrieve the int value out of `x`. `x` needs to represent an int.
+  ## Retrieves the `int` value out of `x`. `x` needs to represent an `int`.
   assert skipRange(x.rawType).kind == tyInt
   result = cast[ptr int](x.value)[]
 
 proc getInt8*(x: Any): int8 =
-  ## retrieve the int8 value out of `x`. `x` needs to represent an int8.
+  ## Retrieves the `int8` value out of `x`. `x` needs to represent an `int8`.
   assert skipRange(x.rawType).kind == tyInt8
   result = cast[ptr int8](x.value)[]
 
 proc getInt16*(x: Any): int16 =
-  ## retrieve the int16 value out of `x`. `x` needs to represent an int16.
+  ## Retrieves the `int16` value out of `x`. `x` needs to represent an `int16`.
   assert skipRange(x.rawType).kind == tyInt16
   result = cast[ptr int16](x.value)[]
 
 proc getInt32*(x: Any): int32 =
-  ## retrieve the int32 value out of `x`. `x` needs to represent an int32.
+  ## Retrieves the `int32` value out of `x`. `x` needs to represent an `int32`.
   assert skipRange(x.rawType).kind == tyInt32
   result = cast[ptr int32](x.value)[]
 
 proc getInt64*(x: Any): int64 =
-  ## retrieve the int64 value out of `x`. `x` needs to represent an int64.
+  ## Retrieves the `int64` value out of `x`. `x` needs to represent an `int64`.
   assert skipRange(x.rawType).kind == tyInt64
   result = cast[ptr int64](x.value)[]
 
 proc getBiggestInt*(x: Any): BiggestInt =
-  ## retrieve the integer value out of `x`. `x` needs to represent
+  ## Retrieves the integer value out of `x`. `x` needs to represent
   ## some integer, a bool, a char, an enum or a small enough bit set.
-  ## The value might be sign-extended to ``BiggestInt``.
-  var t = skipRange(x.rawType)
+  ## The value might be sign-extended to `BiggestInt`.
+  let t = skipRange(x.rawType)
   case t.kind
   of tyInt: result = BiggestInt(cast[ptr int](x.value)[])
   of tyInt8: result = BiggestInt(cast[ptr int8](x.value)[])
@@ -412,8 +477,8 @@ proc getBiggestInt*(x: Any): BiggestInt =
   of tyChar: result = BiggestInt(cast[ptr char](x.value)[])
   of tyEnum, tySet:
     case t.size
-    of 1: result = ze64(cast[ptr int8](x.value)[])
-    of 2: result = ze64(cast[ptr int16](x.value)[])
+    of 1: result = int64(cast[ptr uint8](x.value)[])
+    of 2: result = int64(cast[ptr uint16](x.value)[])
     of 4: result = BiggestInt(cast[ptr int32](x.value)[])
     of 8: result = BiggestInt(cast[ptr int64](x.value)[])
     else: assert false
@@ -424,9 +489,9 @@ proc getBiggestInt*(x: Any): BiggestInt =
   else: assert false
 
 proc setBiggestInt*(x: Any, y: BiggestInt) =
-  ## sets the integer value of `x`. `x` needs to represent
+  ## Sets the integer value of `x`. `x` needs to represent
   ## some integer, a bool, a char, an enum or a small enough bit set.
-  var t = skipRange(x.rawType)
+  let t = skipRange(x.rawType)
   case t.kind
   of tyInt: cast[ptr int](x.value)[] = int(y)
   of tyInt8: cast[ptr int8](x.value)[] = int8(y)
@@ -437,8 +502,8 @@ proc setBiggestInt*(x: Any, y: BiggestInt) =
   of tyChar: cast[ptr char](x.value)[] = chr(y.int)
   of tyEnum, tySet:
     case t.size
-    of 1: cast[ptr int8](x.value)[] = toU8(y.int)
-    of 2: cast[ptr int16](x.value)[] = toU16(y.int)
+    of 1: cast[ptr uint8](x.value)[] = uint8(y.int)
+    of 2: cast[ptr uint16](x.value)[] = uint16(y.int)
     of 4: cast[ptr int32](x.value)[] = int32(y)
     of 8: cast[ptr int64](x.value)[] = y
     else: assert false
@@ -449,38 +514,34 @@ proc setBiggestInt*(x: Any, y: BiggestInt) =
   else: assert false
 
 proc getUInt*(x: Any): uint =
-  ## retrieve the uint value out of `x`, `x` needs to represent an uint.
+  ## Retrieves the `uint` value out of `x`. `x` needs to represent a `uint`.
   assert skipRange(x.rawType).kind == tyUInt
   result = cast[ptr uint](x.value)[]
 
 proc getUInt8*(x: Any): uint8 =
-  ## retrieve the uint8 value out of `x`, `x` needs to represent an
-  ## uint8.
+  ## Retrieves the `uint8` value out of `x`. `x` needs to represent a `uint8`.
   assert skipRange(x.rawType).kind == tyUInt8
   result = cast[ptr uint8](x.value)[]
 
 proc getUInt16*(x: Any): uint16 =
-  ## retrieve the uint16 value out of `x`, `x` needs to represent an
-  ## uint16.
+  ## Retrieves the `uint16` value out of `x`. `x` needs to represent a `uint16`.
   assert skipRange(x.rawType).kind == tyUInt16
   result = cast[ptr uint16](x.value)[]
 
 proc getUInt32*(x: Any): uint32 =
-  ## retrieve the uint32 value out of `x`, `x` needs to represent an
-  ## uint32.
+  ## Retrieves the `uint32` value out of `x`. `x` needs to represent a `uint32`.
   assert skipRange(x.rawType).kind == tyUInt32
   result = cast[ptr uint32](x.value)[]
 
 proc getUInt64*(x: Any): uint64 =
-  ## retrieve the uint64 value out of `x`, `x` needs to represent an
-  ## uint64.
+  ## Retrieves the `uint64` value out of `x`. `x` needs to represent a `uint64`.
   assert skipRange(x.rawType).kind == tyUInt64
   result = cast[ptr uint64](x.value)[]
 
 proc getBiggestUint*(x: Any): uint64 =
-  ## retrieve the unsigned integer value out of `x`. `x` needs to
+  ## Retrieves the unsigned integer value out of `x`. `x` needs to
   ## represent an unsigned integer.
-  var t = skipRange(x.rawType)
+  let t = skipRange(x.rawType)
   case t.kind
   of tyUInt: result = uint64(cast[ptr uint](x.value)[])
   of tyUInt8: result = uint64(cast[ptr uint8](x.value)[])
@@ -490,9 +551,9 @@ proc getBiggestUint*(x: Any): uint64 =
   else: assert false
 
 proc setBiggestUint*(x: Any; y: uint64) =
-  ## sets the unsigned integer value of `c`. `c` needs to represent an
+  ## Sets the unsigned integer value of `x`. `x` needs to represent an
   ## unsigned integer.
-  var t = skipRange(x.rawType)
+  let t = skipRange(x.rawType)
   case t.kind:
   of tyUInt: cast[ptr uint](x.value)[] = uint(y)
   of tyUInt8: cast[ptr uint8](x.value)[] = uint8(y)
@@ -502,33 +563,33 @@ proc setBiggestUint*(x: Any; y: uint64) =
   else: assert false
 
 proc getChar*(x: Any): char =
-  ## retrieve the char value out of `x`. `x` needs to represent a char.
-  var t = skipRange(x.rawType)
+  ## Retrieves the `char` value out of `x`. `x` needs to represent a `char`.
+  let t = skipRange(x.rawType)
   assert t.kind == tyChar
   result = cast[ptr char](x.value)[]
 
 proc getBool*(x: Any): bool =
-  ## retrieve the bool value out of `x`. `x` needs to represent a bool.
-  var t = skipRange(x.rawType)
+  ## Retrieves the `bool` value out of `x`. `x` needs to represent a `bool`.
+  let t = skipRange(x.rawType)
   assert t.kind == tyBool
   result = cast[ptr bool](x.value)[]
 
 proc skipRange*(x: Any): Any =
-  ## skips the range information of `x`.
+  ## Skips the range information of `x`.
   assert x.rawType.kind == tyRange
   result.rawType = x.rawType.base
   result.value = x.value
 
 proc getEnumOrdinal*(x: Any, name: string): int =
-  ## gets the enum field ordinal from `name`. `x` needs to represent an enum
+  ## Gets the enum field ordinal from `name`. `x` needs to represent an enum
   ## but is only used to access the type information. In case of an error
-  ## ``low(int)`` is returned.
-  var typ = skipRange(x.rawType)
+  ## `low(int)` is returned.
+  let typ = skipRange(x.rawType)
   assert typ.kind == tyEnum
-  var n = typ.node
-  var s = n.sons
+  let n = typ.node
+  let s = n.sons
   for i in 0 .. n.len-1:
-    if cmpIgnoreStyle($s[i].name, name) == 0:
+    if cmpNimIdentifier($s[i].name, name) == 0:
       if ntfEnumHole notin typ.flags:
         return i
       else:
@@ -536,45 +597,45 @@ proc getEnumOrdinal*(x: Any, name: string): int =
   result = low(int)
 
 proc getEnumField*(x: Any, ordinalValue: int): string =
-  ## gets the enum field name as a string. `x` needs to represent an enum
+  ## Gets the enum field name as a string. `x` needs to represent an enum
   ## but is only used to access the type information. The field name of
   ## `ordinalValue` is returned.
-  var typ = skipRange(x.rawType)
+  let typ = skipRange(x.rawType)
   assert typ.kind == tyEnum
-  var e = ordinalValue
+  let e = ordinalValue
   if ntfEnumHole notin typ.flags:
     if e <% typ.node.len:
       return $typ.node.sons[e].name
   else:
     # ugh we need a slow linear search:
-    var n = typ.node
-    var s = n.sons
+    let n = typ.node
+    let s = n.sons
     for i in 0 .. n.len-1:
       if s[i].offset == e: return $s[i].name
   result = $e
 
 proc getEnumField*(x: Any): string =
-  ## gets the enum field name as a string. `x` needs to represent an enum.
+  ## Gets the enum field name as a string. `x` needs to represent an enum.
   result = getEnumField(x, getBiggestInt(x).int)
 
 proc getFloat*(x: Any): float =
-  ## retrieve the float value out of `x`. `x` needs to represent an float.
+  ## Retrieves the `float` value out of `x`. `x` needs to represent a `float`.
   assert skipRange(x.rawType).kind == tyFloat
   result = cast[ptr float](x.value)[]
 
 proc getFloat32*(x: Any): float32 =
-  ## retrieve the float32 value out of `x`. `x` needs to represent an float32.
+  ## Retrieves the `float32` value out of `x`. `x` needs to represent a `float32`.
   assert skipRange(x.rawType).kind == tyFloat32
   result = cast[ptr float32](x.value)[]
 
 proc getFloat64*(x: Any): float64 =
-  ## retrieve the float64 value out of `x`. `x` needs to represent an float64.
+  ## Retrieves the `float64` value out of `x`. `x` needs to represent a `float64`.
   assert skipRange(x.rawType).kind == tyFloat64
   result = cast[ptr float64](x.value)[]
 
 proc getBiggestFloat*(x: Any): BiggestFloat =
-  ## retrieve the float value out of `x`. `x` needs to represent
-  ## some float. The value is extended to ``BiggestFloat``.
+  ## Retrieves the float value out of `x`. `x` needs to represent
+  ## some float. The value is extended to `BiggestFloat`.
   case skipRange(x.rawType).kind
   of tyFloat: result = BiggestFloat(cast[ptr float](x.value)[])
   of tyFloat32: result = BiggestFloat(cast[ptr float32](x.value)[])
@@ -582,7 +643,7 @@ proc getBiggestFloat*(x: Any): BiggestFloat =
   else: assert false
 
 proc setBiggestFloat*(x: Any, y: BiggestFloat) =
-  ## sets the float value of `x`. `x` needs to represent
+  ## Sets the float value of `x`. `x` needs to represent
   ## some float.
   case skipRange(x.rawType).kind
   of tyFloat: cast[ptr float](x.value)[] = y
@@ -591,56 +652,59 @@ proc setBiggestFloat*(x: Any, y: BiggestFloat) =
   else: assert false
 
 proc getString*(x: Any): string =
-  ## retrieve the string value out of `x`. `x` needs to represent a string.
+  ## Retrieves the `string` value out of `x`. `x` needs to represent a `string`.
   assert x.rawType.kind == tyString
-  if not isNil(cast[ptr pointer](x.value)[]):
+  when defined(gcDestructors):
     result = cast[ptr string](x.value)[]
+  else:
+    if not isNil(cast[ptr pointer](x.value)[]):
+      result = cast[ptr string](x.value)[]
 
 proc setString*(x: Any, y: string) =
-  ## sets the string value of `x`. `x` needs to represent a string.
+  ## Sets the `string` value of `x`. `x` needs to represent a `string`.
   assert x.rawType.kind == tyString
-  cast[ptr string](x.value)[] = y
+  cast[ptr string](x.value)[] = y # also correct for gcDestructors
 
 proc getCString*(x: Any): cstring =
-  ## retrieve the cstring value out of `x`. `x` needs to represent a cstring.
-  assert x.rawType.kind == tyCString
+  ## Retrieves the `cstring` value out of `x`. `x` needs to represent a `cstring`.
+  assert x.rawType.kind == tyCstring
   result = cast[ptr cstring](x.value)[]
 
 proc assign*(x, y: Any) =
-  ## copies the value of `y` to `x`. The assignment operator for ``Any``
+  ## Copies the value of `y` to `x`. The assignment operator for `Any`
   ## does NOT do this; it performs a shallow copy instead!
   assert y.rawType == x.rawType
   genericAssign(x.value, y.value, y.rawType)
 
 iterator elements*(x: Any): int =
-  ## iterates over every element of `x` that represents a Nim bitset.
+  ## Iterates over every element of `x`. `x` needs to represent a `set`.
   assert x.rawType.kind == tySet
-  var typ = x.rawType
-  var p = x.value
+  let typ = x.rawType
+  let p = x.value
   # "typ.slots.len" field is for sets the "first" field
   var u: int64
   case typ.size
-  of 1: u = ze64(cast[ptr int8](p)[])
-  of 2: u = ze64(cast[ptr int16](p)[])
-  of 4: u = ze64(cast[ptr int32](p)[])
+  of 1: u = int64(cast[ptr uint8](p)[])
+  of 2: u = int64(cast[ptr uint16](p)[])
+  of 4: u = int64(cast[ptr uint32](p)[])
   of 8: u = cast[ptr int64](p)[]
   else:
-    var a = cast[pbyteArray](p)
+    let a = cast[pbyteArray](p)
     for i in 0 .. typ.size*8-1:
-      if (ze(a[i div 8]) and (1 shl (i mod 8))) != 0:
-        yield i+typ.node.len
+      if (int(a[i div 8]) and (1 shl (i mod 8))) != 0:
+        yield i + typ.node.len
   if typ.size <= 8:
     for i in 0..sizeof(int64)*8-1:
       if (u and (1'i64 shl int64(i))) != 0'i64:
-        yield i+typ.node.len
+        yield i + typ.node.len
 
 proc inclSetElement*(x: Any, elem: int) =
-  ## includes an element `elem` in `x`. `x` needs to represent a Nim bitset.
+  ## Includes an element `elem` in `x`. `x` needs to represent a Nim bitset.
   assert x.rawType.kind == tySet
-  var typ = x.rawType
-  var p = x.value
+  let typ = x.rawType
+  let p = x.value
   # "typ.slots.len" field is for sets the "first" field
-  var e = elem - typ.node.len
+  let e = elem - typ.node.len
   case typ.size
   of 1:
     var a = cast[ptr int8](p)
@@ -656,65 +720,4 @@ proc inclSetElement*(x: Any, elem: int) =
     a[] = a[] or (1'i64 shl e)
   else:
     var a = cast[pbyteArray](p)
-    a[e shr 3] = toU8(a[e shr 3] or (1 shl (e and 7)))
-
-when isMainModule:
-  type
-    TE = enum
-      blah, blah2
-
-    TestObj = object
-      test, asd: int
-      case test2: TE
-      of blah:
-        help: string
-      else:
-        nil
-
-  var test = @[0,1,2,3,4]
-  var x = toAny(test)
-  var y = 78
-  x[4] = toAny(y)
-  assert cast[ptr int](x[2].value)[] == 2
-
-  var test2: tuple[name: string, s: int] = ("test", 56)
-  var x2 = toAny(test2)
-  var i = 0
-  for n, a in fields(x2):
-    case i
-    of 0: assert n == "name" and $a.kind == "akString"
-    of 1: assert n == "s" and $a.kind == "akInt"
-    else: assert false
-    inc i
-
-  var test3: TestObj
-  test3.test = 42
-  test3.test2 = blah2
-  var x3 = toAny(test3)
-  i = 0
-  for n, a in fields(x3):
-    case i
-    of 0: assert n == "test" and $a.kind == "akInt"
-    of 1: assert n == "asd" and $a.kind == "akInt"
-    of 2: assert n == "test2" and $a.kind == "akEnum"
-    else: assert false
-    inc i
-
-  var test4: ref string
-  new(test4)
-  test4[] = "test"
-  var x4 = toAny(test4)
-  assert($x4[].kind() == "akString")
-
-  block:
-    # gimme a new scope dammit
-    var myarr: array[0..4, array[0..4, string]] = [
-      ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
-      ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
-      ["test", "1", "2", "3", "4"]]
-    var m = toAny(myArr)
-    for i in 0 .. m.len-1:
-      for j in 0 .. m[i].len-1:
-        echo getString(m[i][j])
-
-
+    a[e shr 3] = a[e shr 3] or uint8(1 shl (e and 7))
diff --git a/lib/core/typelayouts.nim b/lib/core/typelayouts.nim
deleted file mode 100644
index 445ce77c4..000000000
--- a/lib/core/typelayouts.nim
+++ /dev/null
@@ -1,19 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2017 Nim contributors
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-type
-  TypeLayout* = object
-    size*, alignment*: int
-    destructor*: proc (self: pointer; a: Allocator) {.nimcall.}
-    trace*: proc (self: pointer; a: Allocator) {.nimcall.}
-    when false:
-      construct*: proc (self: pointer; a: Allocator) {.nimcall.}
-      copy*, deepcopy*, sink*: proc (self, other: pointer; a: Allocator) {.nimcall.}
-
-proc getTypeLayout(t: typedesc): ptr TypeLayout {.magic: "getTypeLayout".}
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 17321cc0e..000000000
--- a/lib/deprecated/pure/actors.nim
+++ /dev/null
@@ -1,241 +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
-{.deprecated: [TTask: Task, TActor: 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]
-{.deprecated: [TActorPool: ActorPool].}
-
-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 = nil
-
-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/asyncio.nim b/lib/deprecated/pure/asyncio.nim
deleted file mode 100644
index 34cabefb0..000000000
--- a/lib/deprecated/pure/asyncio.nim
+++ /dev/null
@@ -1,716 +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.
-#
-
-include "system/inclrtl"
-
-import sockets, os
-
-##
-## **Warning:** This module is deprecated since version 0.10.2.
-## Use the brand new `asyncdispatch <asyncdispatch.html>`_ module together
-## with the `asyncnet <asyncnet.html>`_ module.
-
-## This module implements an asynchronous event loop together with asynchronous
-## sockets which use this event loop.
-## It is akin to Python's asyncore module. Many modules that use sockets
-## have an implementation for this module, those modules should all have a
-## ``register`` function which you should use to add the desired objects to a
-## dispatcher which you created so
-## that you can receive the events associated with that module's object.
-##
-## Once everything is registered in a dispatcher, you need to call the ``poll``
-## function in a while loop.
-##
-## **Note:** Most modules have tasks which need to be ran regularly, this is
-## why you should not call ``poll`` with a infinite timeout, or even a
-## very long one. In most cases the default timeout is fine.
-##
-## **Note:** This module currently only supports select(), this is limited by
-## FD_SETSIZE, which is usually 1024. So you may only be able to use 1024
-## sockets at a time.
-##
-## Most (if not all) modules that use asyncio provide a userArg which is passed
-## on with the events. The type that you set userArg to must be inheriting from
-## ``RootObj``!
-##
-## **Note:** If you want to provide async ability to your module please do not
-## use the ``Delegate`` object, instead use ``AsyncSocket``. It is possible
-## that in the future this type's fields will not be exported therefore breaking
-## your code.
-##
-## **Warning:** The API of this module is unstable, and therefore is subject
-## to change.
-##
-## Asynchronous sockets
-## ====================
-##
-## For most purposes you do not need to worry about the ``Delegate`` type. The
-## ``AsyncSocket`` is what you are after. It's a reference to
-## the ``AsyncSocketObj`` object. This object defines events which you should
-## overwrite by your own procedures.
-##
-## For server sockets the only event you need to worry about is the ``handleAccept``
-## event, in your handleAccept proc you should call ``accept`` on the server
-## socket which will give you the client which is connecting. You should then
-## set any events that you want to use on that client and add it to your dispatcher
-## using the ``register`` procedure.
-##
-## An example ``handleAccept`` follows:
-##
-## .. code-block:: nim
-##
-##    var disp = newDispatcher()
-##    ...
-##    proc handleAccept(s: AsyncSocket) =
-##      echo("Accepted client.")
-##      var client: AsyncSocket
-##      new(client)
-##      s.accept(client)
-##      client.handleRead = ...
-##      disp.register(client)
-##    ...
-##
-## For client sockets you should only be interested in the ``handleRead`` and
-## ``handleConnect`` events. The former gets called whenever the socket has
-## received messages and can be read from and the latter gets called whenever
-## the socket has established a connection to a server socket; from that point
-## it can be safely written to.
-##
-## Getting a blocking client from an AsyncSocket
-## =============================================
-##
-## If you need a asynchronous server socket but you wish to process the clients
-## synchronously then you can use the ``getSocket`` converter to get
-## a ``Socket`` from the ``AsyncSocket`` object, this can then be combined
-## with ``accept`` like so:
-##
-## .. code-block:: nim
-##
-##    proc handleAccept(s: AsyncSocket) =
-##      var client: Socket
-##      getSocket(s).accept(client)
-
-{.deprecated.}
-
-when defined(windows):
-  from winlean import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet,
-    FD_ISSET, select
-else:
-  from posix import TimeVal, Time, Suseconds, SocketHandle, FD_SET, FD_ZERO,
-    TFdSet, FD_ISSET, select
-
-type
-  DelegateObj* = object
-    fd*: SocketHandle
-    deleVal*: RootRef
-
-    handleRead*: proc (h: RootRef) {.nimcall, gcsafe.}
-    handleWrite*: proc (h: RootRef) {.nimcall, gcsafe.}
-    handleError*: proc (h: RootRef) {.nimcall, gcsafe.}
-    hasDataBuffered*: proc (h: RootRef): bool {.nimcall, gcsafe.}
-
-    open*: bool
-    task*: proc (h: RootRef) {.nimcall, gcsafe.}
-    mode*: FileMode
-
-  Delegate* = ref DelegateObj
-
-  Dispatcher* = ref DispatcherObj
-  DispatcherObj = object
-    delegates: seq[Delegate]
-
-  AsyncSocket* = ref AsyncSocketObj
-  AsyncSocketObj* = object of RootObj
-    socket: Socket
-    info: SocketStatus
-
-    handleRead*: proc (s: AsyncSocket) {.closure, gcsafe.}
-    handleWrite: proc (s: AsyncSocket) {.closure, gcsafe.}
-    handleConnect*: proc (s:  AsyncSocket) {.closure, gcsafe.}
-
-    handleAccept*: proc (s:  AsyncSocket) {.closure, gcsafe.}
-
-    handleTask*: proc (s: AsyncSocket) {.closure, gcsafe.}
-
-    lineBuffer: TaintedString ## Temporary storage for ``readLine``
-    sendBuffer: string ## Temporary storage for ``send``
-    sslNeedAccept: bool
-    proto: Protocol
-    deleg: Delegate
-
-  SocketStatus* = enum
-    SockIdle, SockConnecting, SockConnected, SockListening, SockClosed,
-    SockUDPBound
-
-{.deprecated: [TDelegate: DelegateObj, PDelegate: Delegate,
-  TInfo: SocketStatus, PAsyncSocket: AsyncSocket, TAsyncSocket: AsyncSocketObj,
-  TDispatcher: DispatcherObj, PDispatcher: Dispatcher,
-  ].}
-
-
-proc newDelegate*(): Delegate =
-  ## Creates a new delegate.
-  new(result)
-  result.handleRead = (proc (h: RootRef) = discard)
-  result.handleWrite = (proc (h: RootRef) = discard)
-  result.handleError = (proc (h: RootRef) = discard)
-  result.hasDataBuffered = (proc (h: RootRef): bool = return false)
-  result.task = (proc (h: RootRef) = discard)
-  result.mode = fmRead
-
-proc newAsyncSocket(): AsyncSocket =
-  new(result)
-  result.info = SockIdle
-
-  result.handleRead = (proc (s: AsyncSocket) = discard)
-  result.handleWrite = nil
-  result.handleConnect = (proc (s: AsyncSocket) = discard)
-  result.handleAccept = (proc (s: AsyncSocket) = discard)
-  result.handleTask = (proc (s: AsyncSocket) = discard)
-
-  result.lineBuffer = "".TaintedString
-  result.sendBuffer = ""
-
-proc asyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
-                  protocol: Protocol = IPPROTO_TCP,
-                  buffered = true): AsyncSocket =
-  ## Initialises an AsyncSocket object. If a socket cannot be initialised
-  ## EOS is raised.
-  result = newAsyncSocket()
-  result.socket = socket(domain, typ, protocol, buffered)
-  result.proto = protocol
-  if result.socket == invalidSocket: raiseOSError(osLastError())
-  result.socket.setBlocking(false)
-
-proc toAsyncSocket*(sock: Socket, state: SocketStatus = SockConnected): AsyncSocket =
-  ## Wraps an already initialized ``Socket`` into a AsyncSocket.
-  ## This is useful if you want to use an already connected Socket as an
-  ## asynchronous AsyncSocket in asyncio's event loop.
-  ##
-  ## ``state`` may be overriden, i.e. if ``sock`` is not connected it should be
-  ## adjusted properly. By default it will be assumed that the socket is
-  ## connected. Please note this is only applicable to TCP client sockets, if
-  ## ``sock`` is a different type of socket ``state`` needs to be adjusted!!!
-  ##
-  ## ================  ================================================================
-  ## Value             Meaning
-  ## ================  ================================================================
-  ##  SockIdle          Socket has only just been initialised, not connected or closed.
-  ##  SockConnected     Socket is connected to a server.
-  ##  SockConnecting    Socket is in the process of connecting to a server.
-  ##  SockListening     Socket is a server socket and is listening for connections.
-  ##  SockClosed        Socket has been closed.
-  ##  SockUDPBound      Socket is a UDP socket which is listening for data.
-  ## ================  ================================================================
-  ##
-  ## **Warning**: If ``state`` is set incorrectly the resulting ``AsyncSocket``
-  ## object may not work properly.
-  ##
-  ## **Note**: This will set ``sock`` to be non-blocking.
-  result = newAsyncSocket()
-  result.socket = sock
-  result.proto = if state == SockUDPBound: IPPROTO_UDP else: IPPROTO_TCP
-  result.socket.setBlocking(false)
-  result.info = state
-
-proc asyncSockHandleRead(h: RootRef) =
-  when defined(ssl):
-    if AsyncSocket(h).socket.isSSL and not
-         AsyncSocket(h).socket.gotHandshake:
-      return
-
-  if AsyncSocket(h).info != SockListening:
-    if AsyncSocket(h).info != SockConnecting:
-      AsyncSocket(h).handleRead(AsyncSocket(h))
-  else:
-    AsyncSocket(h).handleAccept(AsyncSocket(h))
-
-proc close*(sock: AsyncSocket) {.gcsafe.}
-proc asyncSockHandleWrite(h: RootRef) =
-  when defined(ssl):
-    if AsyncSocket(h).socket.isSSL and not
-         AsyncSocket(h).socket.gotHandshake:
-      return
-
-  if AsyncSocket(h).info == SockConnecting:
-    AsyncSocket(h).handleConnect(AsyncSocket(h))
-    AsyncSocket(h).info = SockConnected
-    # Stop receiving write events if there is no handleWrite event.
-    if AsyncSocket(h).handleWrite == nil:
-      AsyncSocket(h).deleg.mode = fmRead
-    else:
-      AsyncSocket(h).deleg.mode = fmReadWrite
-  else:
-    if AsyncSocket(h).sendBuffer != "":
-      let sock = AsyncSocket(h)
-      try:
-        let bytesSent = sock.socket.sendAsync(sock.sendBuffer)
-        if bytesSent == 0:
-          # Apparently the socket cannot be written to. Even though select
-          # just told us that it can be... This used to be an assert. Just
-          # do nothing instead.
-          discard
-        elif bytesSent != sock.sendBuffer.len:
-          sock.sendBuffer = sock.sendBuffer[bytesSent .. ^1]
-        elif bytesSent == sock.sendBuffer.len:
-          sock.sendBuffer = ""
-
-        if AsyncSocket(h).handleWrite != nil:
-          AsyncSocket(h).handleWrite(AsyncSocket(h))
-      except OSError:
-        # Most likely the socket closed before the full buffer could be sent to it.
-        sock.close() # TODO: Provide a handleError for users?
-    else:
-      if AsyncSocket(h).handleWrite != nil:
-        AsyncSocket(h).handleWrite(AsyncSocket(h))
-      else:
-        AsyncSocket(h).deleg.mode = fmRead
-
-when defined(ssl):
-  proc asyncSockDoHandshake(h: PObject) {.gcsafe.} =
-    if AsyncSocket(h).socket.isSSL and not
-         AsyncSocket(h).socket.gotHandshake:
-      if AsyncSocket(h).sslNeedAccept:
-        var d = ""
-        let ret = AsyncSocket(h).socket.acceptAddrSSL(AsyncSocket(h).socket, d)
-        assert ret != AcceptNoClient
-        if ret == AcceptSuccess:
-          AsyncSocket(h).info = SockConnected
-      else:
-        # handshake will set socket's ``sslNoHandshake`` field.
-        discard AsyncSocket(h).socket.handshake()
-
-
-proc asyncSockTask(h: RootRef) =
-  when defined(ssl):
-    h.asyncSockDoHandshake()
-
-  AsyncSocket(h).handleTask(AsyncSocket(h))
-
-proc toDelegate(sock: AsyncSocket): Delegate =
-  result = newDelegate()
-  result.deleVal = sock
-  result.fd = getFD(sock.socket)
-  # We need this to get write events, just to know when the socket connects.
-  result.mode = fmReadWrite
-  result.handleRead = asyncSockHandleRead
-  result.handleWrite = asyncSockHandleWrite
-  result.task = asyncSockTask
-  # TODO: Errors?
-  #result.handleError = (proc (h: PObject) = assert(false))
-
-  result.hasDataBuffered =
-    proc (h: RootRef): bool {.nimcall.} =
-      return AsyncSocket(h).socket.hasDataBuffered()
-
-  sock.deleg = result
-  if sock.info notin {SockIdle, SockClosed}:
-    sock.deleg.open = true
-  else:
-    sock.deleg.open = false
-
-proc connect*(sock: AsyncSocket, name: string, port = Port(0),
-                   af: Domain = AF_INET) =
-  ## Begins connecting ``sock`` to ``name``:``port``.
-  sock.socket.connectAsync(name, port, af)
-  sock.info = SockConnecting
-  if sock.deleg != nil:
-    sock.deleg.open = true
-
-proc close*(sock: AsyncSocket) =
-  ## Closes ``sock``. Terminates any current connections.
-  sock.socket.close()
-  sock.info = SockClosed
-  if sock.deleg != nil:
-    sock.deleg.open = false
-
-proc bindAddr*(sock: AsyncSocket, port = Port(0), address = "") =
-  ## Equivalent to ``sockets.bindAddr``.
-  sock.socket.bindAddr(port, address)
-  if sock.proto == IPPROTO_UDP:
-    sock.info = SockUDPBound
-    if sock.deleg != nil:
-      sock.deleg.open = true
-
-proc listen*(sock: AsyncSocket) =
-  ## Equivalent to ``sockets.listen``.
-  sock.socket.listen()
-  sock.info = SockListening
-  if sock.deleg != nil:
-    sock.deleg.open = true
-
-proc acceptAddr*(server: AsyncSocket, client: var AsyncSocket,
-                 address: var string) =
-  ## Equivalent to ``sockets.acceptAddr``. This procedure should be called in
-  ## a ``handleAccept`` event handler **only** once.
-  ##
-  ## **Note**: ``client`` needs to be initialised.
-  assert(client != nil)
-  client = newAsyncSocket()
-  var c: Socket
-  new(c)
-  when defined(ssl):
-    if server.socket.isSSL:
-      var ret = server.socket.acceptAddrSSL(c, address)
-      # The following shouldn't happen because when this function is called
-      # it is guaranteed that there is a client waiting.
-      # (This should be called in handleAccept)
-      assert(ret != AcceptNoClient)
-      if ret == AcceptNoHandshake:
-        client.sslNeedAccept = true
-      else:
-        client.sslNeedAccept = false
-        client.info = SockConnected
-    else:
-      server.socket.acceptAddr(c, address)
-      client.sslNeedAccept = false
-      client.info = SockConnected
-  else:
-    server.socket.acceptAddr(c, address)
-    client.sslNeedAccept = false
-    client.info = SockConnected
-
-  if c == invalidSocket: raiseSocketError(server.socket)
-  c.setBlocking(false) # TODO: Needs to be tested.
-
-  # deleg.open is set in ``toDelegate``.
-
-  client.socket = c
-  client.lineBuffer = "".TaintedString
-  client.sendBuffer = ""
-  client.info = SockConnected
-
-proc accept*(server: AsyncSocket, client: var AsyncSocket) =
-  ## Equivalent to ``sockets.accept``.
-  var dummyAddr = ""
-  server.acceptAddr(client, dummyAddr)
-
-proc acceptAddr*(server: AsyncSocket): tuple[sock: AsyncSocket,
-                                              address: string] {.deprecated.} =
-  ## Equivalent to ``sockets.acceptAddr``.
-  ##
-  ## **Deprecated since version 0.9.0:** Please use the function above.
-  var client = newAsyncSocket()
-  var address: string = ""
-  acceptAddr(server, client, address)
-  return (client, address)
-
-proc accept*(server: AsyncSocket): AsyncSocket {.deprecated.} =
-  ## Equivalent to ``sockets.accept``.
-  ##
-  ## **Deprecated since version 0.9.0:** Please use the function above.
-  new(result)
-  var address = ""
-  server.acceptAddr(result, address)
-
-proc newDispatcher*(): Dispatcher =
-  new(result)
-  result.delegates = @[]
-
-proc register*(d: Dispatcher, deleg: Delegate) =
-  ## Registers delegate ``deleg`` with dispatcher ``d``.
-  d.delegates.add(deleg)
-
-proc register*(d: Dispatcher, sock: AsyncSocket): Delegate {.discardable.} =
-  ## Registers async socket ``sock`` with dispatcher ``d``.
-  result = sock.toDelegate()
-  d.register(result)
-
-proc unregister*(d: Dispatcher, deleg: Delegate) =
-  ## Unregisters deleg ``deleg`` from dispatcher ``d``.
-  for i in 0..len(d.delegates)-1:
-    if d.delegates[i] == deleg:
-      d.delegates.del(i)
-      return
-  raise newException(IndexError, "Could not find delegate.")
-
-proc isWriteable*(s: AsyncSocket): bool =
-  ## Determines whether socket ``s`` is ready to be written to.
-  var writeSock = @[s.socket]
-  return selectWrite(writeSock, 1) != 0 and s.socket notin writeSock
-
-converter getSocket*(s: AsyncSocket): Socket =
-  return s.socket
-
-proc isConnected*(s: AsyncSocket): bool =
-  ## Determines whether ``s`` is connected.
-  return s.info == SockConnected
-proc isListening*(s: AsyncSocket): bool =
-  ## Determines whether ``s`` is listening for incoming connections.
-  return s.info == SockListening
-proc isConnecting*(s: AsyncSocket): bool =
-  ## Determines whether ``s`` is connecting.
-  return s.info == SockConnecting
-proc isClosed*(s: AsyncSocket): bool =
-  ## Determines whether ``s`` has been closed.
-  return s.info == SockClosed
-proc isSendDataBuffered*(s: AsyncSocket): bool =
-  ## Determines whether ``s`` has data waiting to be sent, i.e. whether this
-  ## socket's sendBuffer contains data.
-  return s.sendBuffer.len != 0
-
-proc setHandleWrite*(s: AsyncSocket,
-    handleWrite: proc (s: AsyncSocket) {.closure, gcsafe.}) =
-  ## Setter for the ``handleWrite`` event.
-  ##
-  ## To remove this event you should use the ``delHandleWrite`` function.
-  ## It is advised to use that function instead of just setting the event to
-  ## ``proc (s: AsyncSocket) = nil`` as that would mean that that function
-  ## would be called constantly.
-  s.deleg.mode = fmReadWrite
-  s.handleWrite = handleWrite
-
-proc delHandleWrite*(s: AsyncSocket) =
-  ## Removes the ``handleWrite`` event handler on ``s``.
-  s.handleWrite = nil
-
-{.push warning[deprecated]: off.}
-proc recvLine*(s: AsyncSocket, line: var TaintedString): bool {.deprecated.} =
-  ## Behaves similar to ``sockets.recvLine``, however it handles non-blocking
-  ## sockets properly. This function guarantees that ``line`` is a full line,
-  ## if this function can only retrieve some data; it will save this data and
-  ## add it to the result when a full line is retrieved.
-  ##
-  ## Unlike ``sockets.recvLine`` this function will raise an EOS or ESSL
-  ## exception if an error occurs.
-  ##
-  ## **Deprecated since version 0.9.2**: This function has been deprecated in
-  ## favour of readLine.
-  setLen(line.string, 0)
-  var dataReceived = "".TaintedString
-  var ret = s.socket.recvLineAsync(dataReceived)
-  case ret
-  of RecvFullLine:
-    if s.lineBuffer.len > 0:
-      string(line).add(s.lineBuffer.string)
-      setLen(s.lineBuffer.string, 0)
-    string(line).add(dataReceived.string)
-    if string(line) == "":
-      line = "\c\L".TaintedString
-    result = true
-  of RecvPartialLine:
-    string(s.lineBuffer).add(dataReceived.string)
-    result = false
-  of RecvDisconnected:
-    result = true
-  of RecvFail:
-    s.raiseSocketError(async = true)
-    result = false
-{.pop.}
-
-proc readLine*(s: AsyncSocket, line: var TaintedString): bool =
-  ## Behaves similar to ``sockets.readLine``, however it handles non-blocking
-  ## sockets properly. This function guarantees that ``line`` is a full line,
-  ## if this function can only retrieve some data; it will save this data and
-  ## add it to the result when a full line is retrieved, when this happens
-  ## False will be returned. True will only be returned if a full line has been
-  ## retrieved or the socket has been disconnected in which case ``line`` will
-  ## be set to "".
-  ##
-  ## This function will raise an EOS exception when a socket error occurs.
-  setLen(line.string, 0)
-  var dataReceived = "".TaintedString
-  var ret = s.socket.readLineAsync(dataReceived)
-  case ret
-  of ReadFullLine:
-    if s.lineBuffer.len > 0:
-      string(line).add(s.lineBuffer.string)
-      setLen(s.lineBuffer.string, 0)
-    string(line).add(dataReceived.string)
-    if string(line) == "":
-      line = "\c\L".TaintedString
-    result = true
-  of ReadPartialLine:
-    string(s.lineBuffer).add(dataReceived.string)
-    result = false
-  of ReadNone:
-    result = false
-  of ReadDisconnected:
-    result = true
-
-proc send*(sock: AsyncSocket, data: string) =
-  ## Sends ``data`` to socket ``sock``. This is basically a nicer implementation
-  ## of ``sockets.sendAsync``.
-  ##
-  ## If ``data`` cannot be sent immediately it will be buffered and sent
-  ## when ``sock`` becomes writeable (during the ``handleWrite`` event).
-  ## It's possible that only a part of ``data`` will be sent immediately, while
-  ## the rest of it will be buffered and sent later.
-  if sock.sendBuffer.len != 0:
-    sock.sendBuffer.add(data)
-    return
-  let bytesSent = sock.socket.sendAsync(data)
-  assert bytesSent >= 0
-  if bytesSent == 0:
-    sock.sendBuffer.add(data)
-    sock.deleg.mode = fmReadWrite
-  elif bytesSent != data.len:
-    sock.sendBuffer.add(data[bytesSent .. ^1])
-    sock.deleg.mode = fmReadWrite
-
-proc timeValFromMilliseconds(timeout = 500): Timeval =
-  if timeout != -1:
-    var seconds = timeout div 1000
-    when defined(posix):
-      result.tv_sec = seconds.Time
-      result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds
-    else:
-      result.tv_sec = seconds.int32
-      result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
-
-proc createFdSet(fd: var TFdSet, s: seq[Delegate], m: var int) =
-  FD_ZERO(fd)
-  for i in items(s):
-    m = max(m, int(i.fd))
-    FD_SET(i.fd, fd)
-
-proc pruneSocketSet(s: var seq[Delegate], fd: var TFdSet) =
-  var i = 0
-  var L = s.len
-  while i < L:
-    if FD_ISSET(s[i].fd, fd) != 0'i32:
-      s[i] = s[L-1]
-      dec(L)
-    else:
-      inc(i)
-  setLen(s, L)
-
-proc select(readfds, writefds, exceptfds: var seq[Delegate],
-             timeout = 500): int =
-  var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
-
-  var rd, wr, ex: TFdSet
-  var m = 0
-  createFdSet(rd, readfds, m)
-  createFdSet(wr, writefds, m)
-  createFdSet(ex, exceptfds, m)
-
-  if timeout != -1:
-    result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), addr(tv)))
-  else:
-    result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), nil))
-
-  pruneSocketSet(readfds, (rd))
-  pruneSocketSet(writefds, (wr))
-  pruneSocketSet(exceptfds, (ex))
-
-proc poll*(d: Dispatcher, timeout: int = 500): bool =
-  ## This function checks for events on all the delegates in the `PDispatcher`.
-  ## It then proceeds to call the correct event handler.
-  ##
-  ## This function returns ``True`` if there are file descriptors that are still
-  ## open, otherwise ``False``. File descriptors that have been
-  ## closed are immediately removed from the dispatcher automatically.
-  ##
-  ## **Note:** Each delegate has a task associated with it. This gets called
-  ## after each select() call, if you set timeout to ``-1`` the tasks will
-  ## only be executed after one or more file descriptors becomes readable or
-  ## writeable.
-  result = true
-  var readDg, writeDg, errorDg: seq[Delegate] = @[]
-  var len = d.delegates.len
-  var dc = 0
-
-  while dc < len:
-    let deleg = d.delegates[dc]
-    if (deleg.mode != fmWrite or deleg.mode != fmAppend) and deleg.open:
-      readDg.add(deleg)
-    if (deleg.mode != fmRead) and deleg.open:
-      writeDg.add(deleg)
-    if deleg.open:
-      errorDg.add(deleg)
-      inc dc
-    else:
-      # File/socket has been closed. Remove it from dispatcher.
-      d.delegates[dc] = d.delegates[len-1]
-      dec len
-
-  d.delegates.setLen(len)
-
-  var hasDataBufferedCount = 0
-  for d in d.delegates:
-    if d.hasDataBuffered(d.deleVal):
-      hasDataBufferedCount.inc()
-      d.handleRead(d.deleVal)
-  if hasDataBufferedCount > 0: return true
-
-  if readDg.len() == 0 and writeDg.len() == 0:
-    ## TODO: Perhaps this shouldn't return if errorDg has something?
-    return false
-
-  if select(readDg, writeDg, errorDg, timeout) != 0:
-    for i in 0..len(d.delegates)-1:
-      if i > len(d.delegates)-1: break # One delegate might've been removed.
-      let deleg = d.delegates[i]
-      if not deleg.open: continue # This delegate might've been closed.
-      if (deleg.mode != fmWrite or deleg.mode != fmAppend) and
-          deleg notin readDg:
-        deleg.handleRead(deleg.deleVal)
-      if (deleg.mode != fmRead) and deleg notin writeDg:
-        deleg.handleWrite(deleg.deleVal)
-      if deleg notin errorDg:
-        deleg.handleError(deleg.deleVal)
-
-  # Execute tasks
-  for i in items(d.delegates):
-    i.task(i.deleVal)
-
-proc len*(disp: Dispatcher): int =
-  ## Retrieves the amount of delegates in ``disp``.
-  return disp.delegates.len
-
-when not defined(testing) and isMainModule:
-
-  proc testConnect(s: AsyncSocket, no: int) =
-    echo("Connected! " & $no)
-
-  proc testRead(s: AsyncSocket, no: int) =
-    echo("Reading! " & $no)
-    var data = ""
-    if not s.readLine(data): return
-    if data == "":
-      echo("Closing connection. " & $no)
-      s.close()
-    echo(data)
-    echo("Finished reading! " & $no)
-
-  proc testAccept(s: AsyncSocket, disp: Dispatcher, no: int) =
-    echo("Accepting client! " & $no)
-    var client: AsyncSocket
-    new(client)
-    var address = ""
-    s.acceptAddr(client, address)
-    echo("Accepted ", address)
-    client.handleRead =
-      proc (s: AsyncSocket) =
-        testRead(s, 2)
-    disp.register(client)
-
-  proc main =
-    var d = newDispatcher()
-
-    var s = asyncSocket()
-    s.connect("amber.tenthbit.net", Port(6667))
-    s.handleConnect =
-      proc (s: AsyncSocket) =
-        testConnect(s, 1)
-    s.handleRead =
-      proc (s: AsyncSocket) =
-        testRead(s, 1)
-    d.register(s)
-
-    var server = asyncSocket()
-    server.handleAccept =
-      proc (s: AsyncSocket) =
-        testAccept(s, d, 78)
-    server.bindAddr(Port(5555))
-    server.listen()
-    d.register(server)
-
-    while d.poll(-1): discard
-  main()
diff --git a/lib/deprecated/pure/ftpclient.nim b/lib/deprecated/pure/ftpclient.nim
deleted file mode 100644
index 7645258b6..000000000
--- a/lib/deprecated/pure/ftpclient.nim
+++ /dev/null
@@ -1,675 +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.
-#
-
-include "system/inclrtl"
-
-import sockets, strutils, parseutils, times, os, asyncio
-
-from asyncnet import nil
-from nativesockets import nil
-from asyncdispatch import Future
-## **Note**: This module is deprecated since version 0.11.3.
-## You should use the async version of this module
-## `asyncftpclient <asyncftpclient.html>`_.
-##
-## ----
-##
-## This module **partially** implements an FTP client as specified
-## by `RFC 959 <http://tools.ietf.org/html/rfc959>`_.
-##
-## This module provides both a synchronous and asynchronous implementation.
-## The asynchronous implementation requires you to use the ``asyncFTPClient``
-## function. You are then required to register the ``AsyncFTPClient`` with a
-## asyncio dispatcher using the ``register`` function. Take a look at the
-## asyncio module documentation for more information.
-##
-## **Note**: The asynchronous implementation is only asynchronous for long
-## file transfers, calls to functions which use the command socket will block.
-##
-## Here is some example usage of this module:
-##
-## .. code-block:: Nim
-##    var ftp = ftpClient("example.org", user = "user", pass = "pass")
-##    ftp.connect()
-##    ftp.retrFile("file.ext", "file.ext")
-##
-## **Warning:** The API of this module is unstable, and therefore is subject
-## to change.
-
-{.deprecated.}
-
-type
-  FtpBase*[SockType] = ref FtpBaseObj[SockType]
-  FtpBaseObj*[SockType] = object
-    csock*: SockType
-    dsock*: SockType
-    when SockType is asyncio.AsyncSocket:
-      handleEvent*: proc (ftp: AsyncFTPClient, ev: FTPEvent){.closure,gcsafe.}
-      disp: Dispatcher
-      asyncDSockID: Delegate
-    user*, pass*: string
-    address*: string
-    when SockType is asyncnet.AsyncSocket:
-      port*: nativesockets.Port
-    else:
-      port*: Port
-
-    jobInProgress*: bool
-    job*: FTPJob[SockType]
-
-    dsockConnected*: bool
-
-  FTPJobType* = enum
-    JRetrText, JRetr, JStore
-
-  FtpJob[T] = ref FtpJobObj[T]
-  FTPJobObj[T] = object
-    prc: proc (ftp: FTPBase[T], async: bool): bool {.nimcall, gcsafe.}
-    case typ*: FTPJobType
-    of JRetrText:
-      lines: string
-    of JRetr, JStore:
-      file: File
-      filename: string
-      total: BiggestInt # In bytes.
-      progress: BiggestInt # In bytes.
-      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
-
-  AsyncFtpClient* = ref AsyncFtpClientObj ## Async alternative to TFTPClient.
-  AsyncFtpClientObj* = FtpBaseObj[asyncio.AsyncSocket]
-
-  FTPEventType* = enum
-    EvTransferProgress, EvLines, EvRetr, EvStore
-
-  FTPEvent* = object ## Event
-    filename*: string
-    case typ*: FTPEventType
-    of EvLines:
-      lines*: string ## Lines that have been transferred.
-    of EvRetr, EvStore: ## Retr/Store operation finished.
-      nil
-    of EvTransferProgress:
-      bytesTotal*: BiggestInt     ## Bytes total.
-      bytesFinished*: BiggestInt  ## Bytes transferred.
-      speed*: BiggestInt          ## Speed in bytes/s
-      currentJob*: FTPJobType     ## The current job being performed.
-
-  ReplyError* = object of IOError
-  FTPError* = object of IOError
-
-{.deprecated: [
-  TFTPClient: FTPClientObj, TFTPJob: FTPJob, PAsyncFTPClient: AsyncFTPClient,
-  TAsyncFTPClient: AsyncFTPClientObj, TFTPEvent: FTPEvent,
-  EInvalidReply: ReplyError, EFTP: FTPError
-].}
-
-const multiLineLimit = 10000
-
-proc ftpClient*(address: string, port = Port(21),
-                user, pass = ""): FtpClient =
-  ## Create a ``FtpClient`` object.
-  new(result)
-  result.user = user
-  result.pass = pass
-  result.address = address
-  result.port = port
-
-  result.dsockConnected = false
-  result.csock = socket()
-  if result.csock == invalidSocket: raiseOSError(osLastError())
-
-template blockingOperation(sock: Socket, body: untyped) =
-  body
-
-template blockingOperation(sock: asyncio.AsyncSocket, body: untyped) =
-  sock.setBlocking(true)
-  body
-  sock.setBlocking(false)
-
-proc expectReply[T](ftp: FtpBase[T]): TaintedString =
-  result = TaintedString""
-  blockingOperation(ftp.csock):
-    when T is Socket:
-      ftp.csock.readLine(result)
-    else:
-      discard ftp.csock.readLine(result)
-    var count = 0
-    while result[3] == '-':
-      ## Multi-line reply.
-      var line = TaintedString""
-      when T is Socket:
-        ftp.csock.readLine(line)
-      else:
-        discard ftp.csock.readLine(line)
-      result.add("\n" & line)
-      count.inc()
-      if count >= multiLineLimit:
-        raise newException(ReplyError, "Reached maximum multi-line reply count.")
-
-proc send*[T](ftp: FtpBase[T], m: string): TaintedString =
-  ## Send a message to the server, and wait for a primary reply.
-  ## ``\c\L`` is added for you.
-  ##
-  ## **Note:** The server may return multiple lines of coded replies.
-  blockingOperation(ftp.csock):
-    ftp.csock.send(m & "\c\L")
-  return ftp.expectReply()
-
-proc assertReply(received: TaintedString, expected: string) =
-  if not received.string.startsWith(expected):
-    raise newException(ReplyError,
-                       "Expected reply '$1' got: $2" % [
-                       expected, received.string])
-
-proc assertReply(received: TaintedString, expected: varargs[string]) =
-  for i in items(expected):
-    if received.string.startsWith(i): return
-  raise newException(ReplyError,
-                     "Expected reply '$1' got: $2" %
-                     [expected.join("' or '"), received.string])
-
-proc createJob[T](ftp: FtpBase[T],
-               prc: proc (ftp: FtpBase[T], async: bool): bool {.
-                          nimcall,gcsafe.},
-               cmd: FTPJobType) =
-  if ftp.jobInProgress:
-    raise newException(FTPError, "Unable to do two jobs at once.")
-  ftp.jobInProgress = true
-  new(ftp.job)
-  ftp.job.prc = prc
-  ftp.job.typ = cmd
-  case cmd
-  of JRetrText:
-    ftp.job.lines = ""
-  of JRetr, JStore:
-    ftp.job.toStore = ""
-
-proc deleteJob[T](ftp: FtpBase[T]) =
-  assert ftp.jobInProgress
-  ftp.jobInProgress = false
-  case ftp.job.typ
-  of JRetrText:
-    ftp.job.lines = ""
-  of JRetr, JStore:
-    ftp.job.file.close()
-  ftp.dsock.close()
-
-proc handleTask(s: AsyncSocket, ftp: AsyncFTPClient) =
-  if ftp.jobInProgress:
-    if ftp.job.typ in {JRetr, JStore}:
-      if epochTime() - ftp.job.lastProgressReport >= 1.0:
-        var r: FTPEvent
-        ftp.job.lastProgressReport = epochTime()
-        r.typ = EvTransferProgress
-        r.bytesTotal = ftp.job.total
-        r.bytesFinished = ftp.job.progress
-        r.speed = ftp.job.oneSecond
-        r.filename = ftp.job.filename
-        r.currentJob = ftp.job.typ
-        ftp.job.oneSecond = 0
-        ftp.handleEvent(ftp, r)
-
-proc handleWrite(s: AsyncSocket, ftp: AsyncFTPClient) =
-  if ftp.jobInProgress:
-    if ftp.job.typ == JStore:
-      assert (not ftp.job.prc(ftp, true))
-
-proc handleConnect(s: AsyncSocket, ftp: AsyncFTPClient) =
-  ftp.dsockConnected = true
-  assert(ftp.jobInProgress)
-  if ftp.job.typ == JStore:
-    s.setHandleWrite(proc (s: AsyncSocket) = handleWrite(s, ftp))
-  else:
-    s.delHandleWrite()
-
-proc handleRead(s: AsyncSocket, ftp: AsyncFTPClient) =
-  assert ftp.jobInProgress
-  assert ftp.job.typ != JStore
-  # This can never return true, because it shouldn't check for code
-  # 226 from csock.
-  assert(not ftp.job.prc(ftp, true))
-
-proc pasv[T](ftp: FtpBase[T]) =
-  ## Negotiate a data connection.
-  when T is Socket:
-    ftp.dsock = socket()
-    if ftp.dsock == invalidSocket: raiseOSError(osLastError())
-  elif T is AsyncSocket:
-    ftp.dsock = asyncSocket()
-    ftp.dsock.handleRead =
-      proc (s: AsyncSocket) =
-        handleRead(s, ftp)
-    ftp.dsock.handleConnect =
-      proc (s: AsyncSocket) =
-        handleConnect(s, ftp)
-    ftp.dsock.handleTask =
-      proc (s: AsyncSocket) =
-        handleTask(s, ftp)
-    ftp.disp.register(ftp.dsock)
-  else:
-    {.fatal: "Incorrect socket instantiation".}
-
-  var pasvMsg = ftp.send("PASV").string.strip.TaintedString
-  assertReply(pasvMsg, "227")
-  var betweenParens = captureBetween(pasvMsg.string, '(', ')')
-  var nums = betweenParens.split(',')
-  var ip = nums[0.. ^3]
-  var port = nums[^2.. ^1]
-  var properPort = port[0].parseInt()*256+port[1].parseInt()
-  ftp.dsock.connect(ip.join("."), Port(properPort.toU16))
-  when T is AsyncSocket:
-    ftp.dsockConnected = false
-  else:
-    ftp.dsockConnected = true
-
-proc normalizePathSep(path: string): string =
-  return replace(path, '\\', '/')
-
-proc connect*[T](ftp: FtpBase[T]) =
-  ## Connect to the FTP server specified by ``ftp``.
-  when T is AsyncSocket:
-    blockingOperation(ftp.csock):
-      ftp.csock.connect(ftp.address, ftp.port)
-  elif T is Socket:
-    ftp.csock.connect(ftp.address, ftp.port)
-  else:
-    {.fatal: "Incorrect socket instantiation".}
-
-  var reply = ftp.expectReply()
-  if reply.startsWith("120"):
-    # 120 Service ready in nnn minutes.
-    # We wait until we receive 220.
-    reply = ftp.expectReply()
-
-  # Handle 220 messages from the server
-  assertReply ftp.expectReply(), "220"
-
-  if ftp.user != "":
-    assertReply(ftp.send("USER " & ftp.user), "230", "331")
-
-  if ftp.pass != "":
-    assertReply ftp.send("PASS " & ftp.pass), "230"
-
-proc pwd*[T](ftp: FtpBase[T]): string =
-  ## Returns the current working directory.
-  var wd = ftp.send("PWD")
-  assertReply wd, "257"
-  return wd.string.captureBetween('"') # "
-
-proc cd*[T](ftp: FtpBase[T], dir: string) =
-  ## Changes the current directory on the remote FTP server to ``dir``.
-  assertReply ftp.send("CWD " & dir.normalizePathSep), "250"
-
-proc cdup*[T](ftp: FtpBase[T]) =
-  ## Changes the current directory to the parent of the current directory.
-  assertReply ftp.send("CDUP"), "200"
-
-proc getLines[T](ftp: FtpBase[T], async: bool = false): bool =
-  ## Downloads text data in ASCII mode
-  ## Returns true if the download is complete.
-  ## It doesn't if `async` is true, because it doesn't check for 226 then.
-  if ftp.dsockConnected:
-    var r = TaintedString""
-    when T is AsyncSocket:
-      if ftp.asyncDSock.readLine(r):
-        if r.string == "":
-          ftp.dsockConnected = false
-        else:
-          ftp.job.lines.add(r.string & "\n")
-    elif T is Socket:
-      assert(not async)
-      ftp.dsock.readLine(r)
-      if r.string == "":
-        ftp.dsockConnected = false
-      else:
-        ftp.job.lines.add(r.string & "\n")
-    else:
-      {.fatal: "Incorrect socket instantiation".}
-
-  if not async:
-    var readSocks: seq[Socket] = @[ftp.csock]
-    # This is only needed here. Asyncio gets this socket...
-    blockingOperation(ftp.csock):
-      if readSocks.select(1) != 0 and ftp.csock in readSocks:
-        assertReply ftp.expectReply(), "226"
-        return true
-
-proc listDirs*[T](ftp: FtpBase[T], dir: string = "",
-               async = false): seq[string] =
-  ## Returns a list of filenames in the given directory. If ``dir`` is "",
-  ## the current directory is used. If ``async`` is true, this
-  ## function will return immediately and it will be your job to
-  ## use asyncio's ``poll`` to progress this operation.
-
-  ftp.createJob(getLines[T], JRetrText)
-  ftp.pasv()
-
-  assertReply ftp.send("NLST " & dir.normalizePathSep), ["125", "150"]
-
-  if not async:
-    while not ftp.job.prc(ftp, false): discard
-    result = splitLines(ftp.job.lines)
-    ftp.deleteJob()
-  else: return @[]
-
-proc fileExists*(ftp: FtpClient, file: string): bool {.deprecated.} =
-  ## **Deprecated since version 0.9.0:** Please use ``existsFile``.
-  ##
-  ## Determines whether ``file`` exists.
-  ##
-  ## Warning: This function may block. Especially on directories with many
-  ## files, because a full list of file names must be retrieved.
-  var files = ftp.listDirs()
-  for f in items(files):
-    if f.normalizePathSep == file.normalizePathSep: return true
-
-proc existsFile*(ftp: FtpClient, file: string): bool =
-  ## Determines whether ``file`` exists.
-  ##
-  ## Warning: This function may block. Especially on directories with many
-  ## files, because a full list of file names must be retrieved.
-  var files = ftp.listDirs()
-  for f in items(files):
-    if f.normalizePathSep == file.normalizePathSep: return true
-
-proc createDir*[T](ftp: FtpBase[T], dir: string, recursive: bool = false) =
-  ## Creates a directory ``dir``. If ``recursive`` is true, the topmost
-  ## subdirectory of ``dir`` will be created first, following the secondmost...
-  ## etc. this allows you to give a full path as the ``dir`` without worrying
-  ## about subdirectories not existing.
-  if not recursive:
-    assertReply ftp.send("MKD " & dir.normalizePathSep), "257"
-  else:
-    var reply = TaintedString""
-    var previousDirs = ""
-    for p in split(dir, {os.DirSep, os.AltSep}):
-      if p != "":
-        previousDirs.add(p)
-        reply = ftp.send("MKD " & previousDirs)
-        previousDirs.add('/')
-    assertReply reply, "257"
-
-proc chmod*[T](ftp: FtpBase[T], path: string,
-            permissions: set[FilePermission]) =
-  ## Changes permission of ``path`` to ``permissions``.
-  var userOctal = 0
-  var groupOctal = 0
-  var otherOctal = 0
-  for i in items(permissions):
-    case i
-    of fpUserExec: userOctal.inc(1)
-    of fpUserWrite: userOctal.inc(2)
-    of fpUserRead: userOctal.inc(4)
-    of fpGroupExec: groupOctal.inc(1)
-    of fpGroupWrite: groupOctal.inc(2)
-    of fpGroupRead: groupOctal.inc(4)
-    of fpOthersExec: otherOctal.inc(1)
-    of fpOthersWrite: otherOctal.inc(2)
-    of fpOthersRead: otherOctal.inc(4)
-
-  var perm = $userOctal & $groupOctal & $otherOctal
-  assertReply ftp.send("SITE CHMOD " & perm &
-                       " " & path.normalizePathSep), "200"
-
-proc list*[T](ftp: FtpBase[T], dir: string = "", async = false): string =
-  ## Lists all files in ``dir``. If ``dir`` is ``""``, uses the current
-  ## working directory. If ``async`` is true, this function will return
-  ## immediately and it will be your job to call asyncio's
-  ## ``poll`` to progress this operation.
-  ftp.createJob(getLines[T], JRetrText)
-  ftp.pasv()
-
-  assertReply(ftp.send("LIST" & " " & dir.normalizePathSep), ["125", "150"])
-
-  if not async:
-    while not ftp.job.prc(ftp, false): discard
-    result = ftp.job.lines
-    ftp.deleteJob()
-  else:
-    return ""
-
-proc retrText*[T](ftp: FtpBase[T], file: string, async = false): string =
-  ## Retrieves ``file``. File must be ASCII text.
-  ## If ``async`` is true, this function will return immediately and
-  ## it will be your job to call asyncio's ``poll`` to progress this operation.
-  ftp.createJob(getLines[T], JRetrText)
-  ftp.pasv()
-  assertReply ftp.send("RETR " & file.normalizePathSep), ["125", "150"]
-
-  if not async:
-    while not ftp.job.prc(ftp, false): discard
-    result = ftp.job.lines
-    ftp.deleteJob()
-  else:
-    return ""
-
-proc getFile[T](ftp: FtpBase[T], async = false): bool =
-  if ftp.dsockConnected:
-    var r = "".TaintedString
-    var bytesRead = 0
-    var returned = false
-    if async:
-      when T is Socket:
-        raise newException(FTPError, "FTPClient must be async.")
-      else:
-        bytesRead = ftp.dsock.recvAsync(r, BufferSize)
-        returned = bytesRead != -1
-    else:
-      bytesRead = ftp.dsock.recv(r, BufferSize)
-      returned = true
-    let r2 = r.string
-    if r2 != "":
-      ftp.job.progress.inc(r2.len)
-      ftp.job.oneSecond.inc(r2.len)
-      ftp.job.file.write(r2)
-    elif returned and r2 == "":
-      ftp.dsockConnected = false
-
-  when T is Socket:
-    if not async:
-      var readSocks: seq[Socket] = @[ftp.csock]
-      blockingOperation(ftp.csock):
-        if readSocks.select(1) != 0 and ftp.csock in readSocks:
-          assertReply ftp.expectReply(), "226"
-          return true
-
-proc retrFile*[T](ftp: FtpBase[T], file, dest: string, async = false) =
-  ## Downloads ``file`` and saves it to ``dest``. Usage of this function
-  ## asynchronously is recommended to view the progress of the download.
-  ## The ``EvRetr`` event is passed to the specified ``handleEvent`` function
-  ## when the download is finished, and the ``filename`` field will be equal
-  ## to ``file``.
-  ftp.createJob(getFile[T], JRetr)
-  ftp.job.file = open(dest, mode = fmWrite)
-  ftp.pasv()
-  var reply = ftp.send("RETR " & file.normalizePathSep)
-  assertReply reply, ["125", "150"]
-  if {'(', ')'} notin reply.string:
-    raise newException(ReplyError, "Reply has no file size.")
-  var fileSize: BiggestInt
-  if reply.string.captureBetween('(', ')').parseBiggestInt(fileSize) == 0:
-    raise newException(ReplyError, "Reply has no file size.")
-
-  ftp.job.total = fileSize
-  ftp.job.lastProgressReport = epochTime()
-  ftp.job.filename = file.normalizePathSep
-
-  if not async:
-    while not ftp.job.prc(ftp, false): discard
-    ftp.deleteJob()
-
-proc doUpload[T](ftp: FtpBase[T], async = false): bool =
-  if ftp.dsockConnected:
-    if ftp.job.toStore.len() > 0:
-      assert(async)
-      let bytesSent = ftp.dsock.sendAsync(ftp.job.toStore)
-      if bytesSent == ftp.job.toStore.len:
-        ftp.job.toStore = ""
-      elif bytesSent != ftp.job.toStore.len and bytesSent != 0:
-        ftp.job.toStore = ftp.job.toStore[bytesSent .. ^1]
-      ftp.job.progress.inc(bytesSent)
-      ftp.job.oneSecond.inc(bytesSent)
-    else:
-      var s = newStringOfCap(4000)
-      var len = ftp.job.file.readBuffer(addr(s[0]), 4000)
-      setLen(s, len)
-      if len == 0:
-        # File finished uploading.
-        ftp.dsock.close()
-        ftp.dsockConnected = false
-
-        if not async:
-          assertReply ftp.expectReply(), "226"
-          return true
-        return false
-
-      if not async:
-        ftp.dsock.send(s)
-      else:
-        let bytesSent = ftp.dsock.sendAsync(s)
-        if bytesSent == 0:
-          ftp.job.toStore.add(s)
-        elif bytesSent != s.len:
-          ftp.job.toStore.add(s[bytesSent .. ^1])
-        len = bytesSent
-
-      ftp.job.progress.inc(len)
-      ftp.job.oneSecond.inc(len)
-
-proc store*[T](ftp: FtpBase[T], file, dest: string, async = false) =
-  ## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this
-  ## function asynchronously is recommended to view the progress of
-  ## the download.
-  ## The ``EvStore`` event is passed to the specified ``handleEvent`` function
-  ## when the upload is finished, and the ``filename`` field will be
-  ## equal to ``file``.
-  ftp.createJob(doUpload[T], JStore)
-  ftp.job.file = open(file)
-  ftp.job.total = ftp.job.file.getFileSize()
-  ftp.job.lastProgressReport = epochTime()
-  ftp.job.filename = file
-  ftp.pasv()
-
-  assertReply ftp.send("STOR " & dest.normalizePathSep), ["125", "150"]
-
-  if not async:
-    while not ftp.job.prc(ftp, false): discard
-    ftp.deleteJob()
-
-proc close*[T](ftp: FtpBase[T]) =
-  ## Terminates the connection to the server.
-  assertReply ftp.send("QUIT"), "221"
-  if ftp.jobInProgress: ftp.deleteJob()
-  ftp.csock.close()
-  ftp.dsock.close()
-
-proc csockHandleRead(s: AsyncSocket, ftp: AsyncFTPClient) =
-  if ftp.jobInProgress:
-    assertReply ftp.expectReply(), "226" # Make sure the transfer completed.
-    var r: FTPEvent
-    case ftp.job.typ
-    of JRetrText:
-      r.typ = EvLines
-      r.lines = ftp.job.lines
-    of JRetr:
-      r.typ = EvRetr
-      r.filename = ftp.job.filename
-      if ftp.job.progress != ftp.job.total:
-        raise newException(FTPError, "Didn't download full file.")
-    of JStore:
-      r.typ = EvStore
-      r.filename = ftp.job.filename
-      if ftp.job.progress != ftp.job.total:
-        raise newException(FTPError, "Didn't upload full file.")
-    ftp.deleteJob()
-
-    ftp.handleEvent(ftp, r)
-
-proc asyncFTPClient*(address: string, port = Port(21),
-                     user, pass = "",
-    handleEvent: proc (ftp: AsyncFTPClient, ev: FTPEvent) {.closure,gcsafe.} =
-      (proc (ftp: AsyncFTPClient, ev: FTPEvent) = discard)): AsyncFTPClient =
-  ## Create a ``AsyncFTPClient`` object.
-  ##
-  ## Use this if you want to use asyncio's dispatcher.
-  var dres: AsyncFtpClient
-  new(dres)
-  dres.user = user
-  dres.pass = pass
-  dres.address = address
-  dres.port = port
-  dres.dsockConnected = false
-  dres.handleEvent = handleEvent
-  dres.csock = asyncSocket()
-  dres.csock.handleRead =
-    proc (s: AsyncSocket) =
-      csockHandleRead(s, dres)
-  result = dres
-
-proc register*(d: Dispatcher, ftp: AsyncFTPClient): Delegate {.discardable.} =
-  ## Registers ``ftp`` with dispatcher ``d``.
-  ftp.disp = d
-  return ftp.disp.register(ftp.csock)
-
-when not defined(testing) and isMainModule:
-  proc main =
-    var d = newDispatcher()
-    let hev =
-      proc (ftp: AsyncFTPClient, event: FTPEvent) =
-        case event.typ
-        of EvStore:
-          echo("Upload finished!")
-          ftp.retrFile("payload.jpg", "payload2.jpg", async = true)
-        of EvTransferProgress:
-          var time: int64 = -1
-          if event.speed != 0:
-            time = (event.bytesTotal - event.bytesFinished) div event.speed
-          echo(event.currentJob)
-          echo(event.speed div 1000, " kb/s. - ",
-               event.bytesFinished, "/", event.bytesTotal,
-               " - ", time, " seconds")
-          echo(d.len)
-        of EvRetr:
-          echo("Download finished!")
-          ftp.close()
-          echo d.len
-        else: assert(false)
-    var ftp = asyncFTPClient("example.com", user = "foo", pass = "bar", handleEvent = hev)
-
-    d.register(ftp)
-    d.len.echo()
-    ftp.connect()
-    echo "connected"
-    ftp.store("payload.jpg", "payload.jpg", async = true)
-    d.len.echo()
-    echo "uploading..."
-    while true:
-      if not d.poll(): break
-  main()
-
-when not defined(testing) and isMainModule:
-  var ftp = ftpClient("example.com", user = "foo", pass = "bar")
-  ftp.connect()
-  echo ftp.pwd()
-  echo ftp.list()
-  echo("uploading")
-  ftp.store("payload.jpg", "payload.jpg", async = false)
-
-  echo("Upload complete")
-  ftp.retrFile("payload.jpg", "payload2.jpg", async = false)
-
-  echo("Download complete")
-  sleep(5000)
-  ftp.close()
-  sleep(200)
diff --git a/lib/deprecated/pure/future.nim b/lib/deprecated/pure/future.nim
new file mode 100644
index 000000000..0e06161f2
--- /dev/null
+++ b/lib/deprecated/pure/future.nim
@@ -0,0 +1,6 @@
+## This module is a deprecated alias for the `sugar` module. Deprecated since 0.19.0.
+
+{.deprecated: "Use the new 'sugar' module instead".}
+
+import std/sugar
+export sugar
diff --git a/lib/deprecated/pure/mersenne.nim b/lib/deprecated/pure/mersenne.nim
new file mode 100644
index 000000000..37c5085b1
--- /dev/null
+++ b/lib/deprecated/pure/mersenne.nim
@@ -0,0 +1,51 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Nim Contributors
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+## The [Mersenne Twister](https://en.wikipedia.org/wiki/Mersenne_Twister)
+## random number generator.
+## .. note:: The procs in this module work at compile-time.
+
+{.deprecated: "use `std/random` instead".}
+
+runnableExamples:
+  var rand = newMersenneTwister(uint32.high)  ## must be "var"
+  doAssert rand.getNum() != rand.getNum()  ## pseudorandom number
+## See also
+## ========
+## * `random module<random.html>`_ for Nim's standard random number generator
+type
+  MersenneTwister* = object
+    ## The Mersenne Twister.
+    mt: array[0..623, uint32]
+    index: int
+
+proc newMersenneTwister*(seed: uint32): MersenneTwister =
+  ## Creates a new `MersenneTwister` with seed `seed`.
+  result.index = 0
+  result.mt[0] = seed
+  for i in 1'u32 .. 623'u32:
+    result.mt[i] = (0x6c078965'u32 * (result.mt[i-1] xor
+                                      (result.mt[i-1] shr 30'u32)) + i)
+
+proc generateNumbers(m: var MersenneTwister) =
+
+  for i in 0..623:
+    var y = (m.mt[i] and 0x80000000'u32) +
+            (m.mt[(i+1) mod 624] and 0x7fffffff'u32)
+    m.mt[i] = m.mt[(i+397) mod 624] xor uint32(y shr 1'u32)
+    if (y mod 2'u32) != 0:
+      m.mt[i] = m.mt[i] xor 0x9908b0df'u32
+
+proc getNum*(m: var MersenneTwister): uint32 =
+  ## Returns the next pseudorandom `uint32`.
+  if m.index == 0:
+    generateNumbers(m)
+  result = m.mt[m.index]
+  m.index = (m.index + 1) mod m.mt.len
+  result = result xor (result shr 11'u32)
+  result = result xor ((result shl 7'u32) and 0x9d2c5680'u32)
+  result = result xor ((result shl 15'u32) and 0xefc60000'u32)
+  result = result xor (result shr 18'u32)
diff --git a/lib/deprecated/pure/ospaths.nim b/lib/deprecated/pure/ospaths.nim
new file mode 100644
index 000000000..43fcb17cc
--- /dev/null
+++ b/lib/deprecated/pure/ospaths.nim
@@ -0,0 +1,22 @@
+#
+#
+#            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 is deprecated since 0.20.0, `import std/os` instead.
+
+{.deprecated: "use `std/os` instead".}
+
+import std/os
+export ReadEnvEffect, WriteEnvEffect, ReadDirEffect, WriteDirEffect, OSErrorCode,
+  doslikeFileSystem, CurDir, ParDir, DirSep, AltSep, PathSep, FileSystemCaseSensitive,
+  ExeExt, ScriptExt, DynlibFormat, ExtSep, joinPath, `/`, splitPath, parentDir,
+  tailDir, isRootDir, parentDirs, `/../`, searchExtPos, splitFile, extractFilename,
+  lastPathPart, changeFileExt, addFileExt, cmpPaths, isAbsolute, unixToNativePath,
+  `==`, `$`, osErrorMsg, raiseOSError, osLastError, getEnv, existsEnv, putEnv,
+  getHomeDir, getConfigDir, getTempDir, expandTilde, quoteShellWindows,
+  quoteShellPosix, quoteShell, quoteShellCommand
diff --git a/lib/deprecated/pure/oswalkdir.nim b/lib/deprecated/pure/oswalkdir.nim
new file mode 100644
index 000000000..57a2cb81d
--- /dev/null
+++ b/lib/deprecated/pure/oswalkdir.nim
@@ -0,0 +1,13 @@
+#
+#
+#            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 is deprecated, `import std/os` instead.
+{.deprecated: "import 'std/os' instead".}
+import std/os
+export PathComponent, walkDir, walkDirRec
diff --git a/lib/deprecated/pure/parseurl.nim b/lib/deprecated/pure/parseurl.nim
deleted file mode 100644
index 6d58e8a73..000000000
--- a/lib/deprecated/pure/parseurl.nim
+++ /dev/null
@@ -1,114 +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]
-
-{.deprecated: [TUrl: Url].}
-
-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/deprecated/pure/sockets.nim b/lib/deprecated/pure/sockets.nim
deleted file mode 100644
index 05aebef76..000000000
--- a/lib/deprecated/pure/sockets.nim
+++ /dev/null
@@ -1,1759 +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.
-#
-
-## **Warning:** Since version 0.10.2 this module is deprecated.
-## Use the `net <net.html>`_ or the
-## `nativesockets <nativesockets.html>`_ module instead.
-##
-## This module implements portable sockets, it supports a mix of different types
-## of sockets. Sockets are buffered by default meaning that data will be
-## received in ``BufferSize`` (4000) sized chunks, buffering
-## behaviour can be disabled by setting the ``buffered`` parameter when calling
-## the ``socket`` function to `false`. Be aware that some functions may not yet
-## support buffered sockets (mainly the recvFrom function).
-##
-## Most procedures raise OSError on error, but some may return ``-1`` or a
-## boolean ``false``.
-##
-## SSL is supported through the OpenSSL library. This support can be activated
-## by compiling with the ``-d:ssl`` switch. When an SSL socket is used it will
-## raise ESSL exceptions when SSL errors occur.
-##
-## Asynchronous sockets are supported, however a better alternative is to use
-## the `asyncio <asyncio.html>`_ module.
-
-{.deprecated.}
-
-include "system/inclrtl"
-
-{.deadCodeElim: on.}  # dce option deprecated
-
-when hostOS == "solaris":
-  {.passl: "-lsocket -lnsl".}
-
-import os, parseutils
-from times import epochTime
-
-when defined(ssl):
-  import openssl
-else:
-  type SSLAcceptResult = int
-
-when defined(Windows):
-  import winlean
-else:
-  import posix
-
-# Note: The enumerations are mapped to Window's constants.
-
-when defined(ssl):
-
-  type
-    SSLError* = object of Exception
-
-    SSLCVerifyMode* = enum
-      CVerifyNone, CVerifyPeer
-
-    SSLProtVersion* = enum
-      protSSLv2, protSSLv3, protTLSv1, protSSLv23
-
-    SSLContext* = distinct SSLCTX
-
-    SSLAcceptResult* = enum
-      AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
-
-  {.deprecated: [ESSL: SSLError, TSSLCVerifyMode: SSLCVerifyMode,
-     TSSLProtVersion: SSLProtVersion, PSSLContext: SSLContext,
-     TSSLAcceptResult: SSLAcceptResult].}
-
-const
-  BufferSize*: int = 4000 ## size of a buffered socket's buffer
-
-type
-  SocketImpl = object ## socket type
-    fd: SocketHandle
-    case isBuffered: bool # determines whether this socket is buffered.
-    of true:
-      buffer: array[0..BufferSize, char]
-      currPos: int # current index in buffer
-      bufLen: int # current length of buffer
-    of false: nil
-    when defined(ssl):
-      case isSsl: bool
-      of true:
-        sslHandle: SSLPtr
-        sslContext: SSLContext
-        sslNoHandshake: bool # True if needs handshake.
-        sslHasPeekChar: bool
-        sslPeekChar: char
-      of false: nil
-    nonblocking: bool
-
-  Socket* = ref SocketImpl
-
-  Port* = distinct uint16  ## port type
-
-  Domain* = enum    ## domain, which specifies the protocol family of the
-                    ## created socket. Other domains than those that are listed
-                    ## here are unsupported.
-    AF_UNIX,        ## for local socket (using a file). Unsupported on Windows.
-    AF_INET = 2,    ## for network protocol IPv4 or
-    AF_INET6 = 23   ## for network protocol IPv6.
-
-  SockType* = enum     ## second argument to `socket` proc
-    SOCK_STREAM = 1,   ## reliable stream-oriented service or Stream Sockets
-    SOCK_DGRAM = 2,    ## datagram service or Datagram Sockets
-    SOCK_RAW = 3,      ## raw protocols atop the network layer.
-    SOCK_SEQPACKET = 5 ## reliable sequenced packet service
-
-  Protocol* = enum      ## third argument to `socket` proc
-    IPPROTO_TCP = 6,    ## Transmission control protocol.
-    IPPROTO_UDP = 17,   ## User datagram protocol.
-    IPPROTO_IP,         ## Internet protocol. Unsupported on Windows.
-    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.
-
-  Servent* = object ## information about a service
-    name*: string
-    aliases*: seq[string]
-    port*: Port
-    proto*: string
-
-  Hostent* = object ## information about a given host
-    name*: string
-    aliases*: seq[string]
-    addrtype*: Domain
-    length*: int
-    addrList*: seq[string]
-
-  SOBool* = enum ## Boolean socket options.
-    OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive,
-    OptOOBInline, OptReuseAddr
-
-  RecvLineResult* = enum ## result for recvLineAsync
-    RecvFullLine, RecvPartialLine, RecvDisconnected, RecvFail
-
-  ReadLineResult* = enum ## result for readLineAsync
-    ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone
-
-  TimeoutError* = object of Exception
-
-{.deprecated: [TSocket: Socket, TType: SockType, TPort: Port, TDomain: Domain,
-    TProtocol: Protocol, TServent: Servent, THostent: Hostent,
-    TSOBool: SOBool, TRecvLineResult: RecvLineResult,
-    TReadLineResult: ReadLineResult, ETimeout: TimeoutError,
-    TSocketImpl: SocketImpl].}
-
-when defined(booting):
-  let invalidSocket*: Socket = nil ## invalid socket
-else:
-  const invalidSocket*: Socket = nil ## invalid socket
-
-when defined(windows):
-  let
-    osInvalidSocket = winlean.INVALID_SOCKET
-else:
-  let
-    osInvalidSocket = posix.INVALID_SOCKET
-
-proc newTSocket(fd: SocketHandle, isBuff: bool): Socket =
-  if fd == osInvalidSocket:
-    return nil
-  new(result)
-  result.fd = fd
-  result.isBuffered = isBuff
-  if isBuff:
-    result.currPos = 0
-  result.nonblocking = false
-
-proc `==`*(a, b: Port): bool {.borrow.}
-  ## ``==`` for ports.
-
-proc `$`*(p: Port): string {.borrow.}
-  ## returns the port number as a string
-
-proc ntohl*(x: int32): int32 =
-  ## 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.
-  when cpuEndian == bigEndian: result = x
-  else: result = (x shr 24'i32) or
-                 (x shr 8'i32 and 0xff00'i32) or
-                 (x shl 8'i32 and 0xff0000'i32) or
-                 (x shl 24'i32)
-
-proc ntohs*(x: int16): int16 =
-  ## 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.
-  when cpuEndian == bigEndian: result = x
-  else: result = (x shr 8'i16) or (x shl 8'i16)
-
-proc htonl*(x: int32): int32 =
-  ## 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.
-  result = sockets.ntohl(x)
-
-proc htons*(x: int16): int16 =
-  ## Converts 16-bit positive 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.
-  result = sockets.ntohs(x)
-
-template ntohl(x: uint32): uint32 =
-  cast[uint32](sockets.ntohl(cast[int32](x)))
-
-template ntohs(x: uint16): uint16 =
-  cast[uint16](sockets.ntohs(cast[int16](x)))
-
-template htonl(x: uint32): uint32 =
-  sockets.ntohl(x)
-
-template htons(x: uint16): uint16 =
-  sockets.ntohs(x)
-
-when defined(Posix):
-  proc toInt(domain: Domain): TSa_Family =
-    case domain
-    of AF_UNIX:        result = posix.AF_UNIX
-    of AF_INET:        result = posix.AF_INET
-    of AF_INET6:       result = posix.AF_INET6
-    else: discard
-
-  proc toInt(typ: SockType): cint =
-    case typ
-    of SOCK_STREAM:    result = posix.SOCK_STREAM
-    of SOCK_DGRAM:     result = posix.SOCK_DGRAM
-    of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
-    of SOCK_RAW:       result = posix.SOCK_RAW
-    else: discard
-
-  proc toInt(p: Protocol): cint =
-    case p
-    of IPPROTO_TCP:    result = posix.IPPROTO_TCP
-    of IPPROTO_UDP:    result = posix.IPPROTO_UDP
-    of IPPROTO_IP:     result = posix.IPPROTO_IP
-    of IPPROTO_IPV6:   result = posix.IPPROTO_IPV6
-    of IPPROTO_RAW:    result = posix.IPPROTO_RAW
-    of IPPROTO_ICMP:   result = posix.IPPROTO_ICMP
-    else: discard
-
-else:
-  proc toInt(domain: Domain): cint =
-    result = toU16(ord(domain))
-
-  proc toInt(typ: SockType): cint =
-    result = cint(ord(typ))
-
-  proc toInt(p: Protocol): cint =
-    result = cint(ord(p))
-
-proc socket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
-             protocol: Protocol = IPPROTO_TCP, buffered = true): Socket =
-  ## Creates a new socket; returns `InvalidSocket` if an error occurs.
-
-  # TODO: Perhaps this should just raise EOS when an error occurs.
-  when defined(Windows):
-    result = newTSocket(winlean.socket(cint(domain), cint(typ), cint(protocol)), buffered)
-  else:
-    result = newTSocket(posix.socket(toInt(domain), toInt(typ), toInt(protocol)), buffered)
-
-when defined(ssl):
-  CRYPTO_malloc_init()
-  SslLibraryInit()
-  SslLoadErrorStrings()
-  ErrLoadBioStrings()
-  OpenSSL_add_all_algorithms()
-
-  proc raiseSSLError(s = "") =
-    if s != "":
-      raise newException(SSLError, s)
-    let err = ErrPeekLastError()
-    if err == 0:
-      raise newException(SSLError, "No error reported.")
-    if err == -1:
-      raiseOSError(osLastError())
-    var errStr = ErrErrorString(err, nil)
-    raise newException(SSLError, $errStr)
-
-  # http://simplestcodings.blogspot.co.uk/2010/08/secure-server-client-using-openssl-in-c.html
-  proc loadCertificates(ctx: SSL_CTX, certFile, keyFile: string) =
-    if certFile != "" and not existsFile(certFile):
-      raise newException(system.IOError, "Certificate file could not be found: " & certFile)
-    if keyFile != "" and not existsFile(keyFile):
-      raise newException(system.IOError, "Key file could not be found: " & keyFile)
-
-    if certFile != "":
-      var ret = SSLCTXUseCertificateChainFile(ctx, certFile)
-      if ret != 1:
-        raiseSslError()
-
-    # TODO: Password? www.rtfm.com/openssl-examples/part1.pdf
-    if keyFile != "":
-      if SSL_CTX_use_PrivateKey_file(ctx, keyFile,
-                                     SSL_FILETYPE_PEM) != 1:
-        raiseSslError()
-
-      if SSL_CTX_check_private_key(ctx) != 1:
-        raiseSslError("Verification of private key file failed.")
-
-  proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer,
-                   certFile = "", keyFile = ""): SSLContext =
-    ## Creates an SSL context.
-    ##
-    ## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1 are
-    ## are available with the addition of ``ProtSSLv23`` which allows for
-    ## compatibility with all of them.
-    ##
-    ## There are currently only two options for verify mode;
-    ## one is ``CVerifyNone`` and with it certificates will not be verified
-    ## the other is ``CVerifyPeer`` and certificates will be verified for
-    ## it, ``CVerifyPeer`` is the safest choice.
-    ##
-    ## The last two parameters specify the certificate file path and the key file
-    ## path, a server socket will most likely not work without these.
-    ## Certificates can be generated using the following command:
-    ## ``openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem``.
-    var newCTX: SSL_CTX
-    case protVersion
-    of protSSLv23:
-      newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support.
-    of protSSLv2:
-      raiseSslError("SSLv2 is no longer secure and has been deprecated, use protSSLv3")
-    of protSSLv3:
-      newCTX = SSL_CTX_new(SSLv3_method())
-    of protTLSv1:
-      newCTX = SSL_CTX_new(TLSv1_method())
-
-    if newCTX.SSLCTXSetCipherList("ALL") != 1:
-      raiseSslError()
-    case verifyMode
-    of CVerifyPeer:
-      newCTX.SSLCTXSetVerify(SSLVerifyPeer, nil)
-    of CVerifyNone:
-      newCTX.SSLCTXSetVerify(SSLVerifyNone, nil)
-    if newCTX == nil:
-      raiseSslError()
-
-    discard newCTX.SSLCTXSetMode(SSL_MODE_AUTO_RETRY)
-    newCTX.loadCertificates(certFile, keyFile)
-    return SSLContext(newCTX)
-
-  proc wrapSocket*(ctx: SSLContext, socket: Socket) =
-    ## Wraps a socket in an SSL context. This function effectively turns
-    ## ``socket`` into an SSL socket.
-    ##
-    ## **Disclaimer**: This code is not well tested, may be very unsafe and
-    ## prone to security vulnerabilities.
-
-    socket.isSSL = true
-    socket.sslContext = ctx
-    socket.sslHandle = SSLNew(SSLCTX(socket.sslContext))
-    socket.sslNoHandshake = false
-    socket.sslHasPeekChar = false
-    if socket.sslHandle == nil:
-      raiseSslError()
-
-    if SSLSetFd(socket.sslHandle, socket.fd) != 1:
-      raiseSslError()
-
-proc raiseSocketError*(socket: Socket, err: int = -1, async = false) =
-  ## Raises proper errors based on return values of ``recv`` functions.
-  ##
-  ## If ``async`` is ``True`` no error will be thrown in the case when the
-  ## error was caused by no data being available to be read.
-  ##
-  ## If ``err`` is not lower than 0 no exception will be raised.
-  when defined(ssl):
-    if socket.isSSL:
-      if err <= 0:
-        var ret = SSLGetError(socket.sslHandle, err.cint)
-        case ret
-        of SSL_ERROR_ZERO_RETURN:
-          raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
-        of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
-          if async:
-            return
-          else: raiseSslError("Not enough data on socket.")
-        of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ:
-          if async:
-            return
-          else: raiseSslError("Not enough data on socket.")
-        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")
-
-  if err == -1 and not (when defined(ssl): socket.isSSL else: false):
-    let lastError = osLastError()
-    if async:
-      when defined(windows):
-        if lastError.int32 == WSAEWOULDBLOCK:
-          return
-        else: raiseOSError(lastError)
-      else:
-        if lastError.int32 == EAGAIN or lastError.int32 == EWOULDBLOCK:
-          return
-        else: raiseOSError(lastError)
-    else: raiseOSError(lastError)
-
-proc listen*(socket: Socket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
-  ## Marks ``socket`` as accepting connections.
-  ## ``Backlog`` specifies the maximum length of the
-  ## queue of pending connections.
-  if listen(socket.fd, cint(backlog)) < 0'i32: raiseOSError(osLastError())
-
-proc invalidIp4(s: string) {.noreturn, noinline.} =
-  raise newException(ValueError, "invalid ip4 address: " & s)
-
-proc parseIp4*(s: string): BiggestInt =
-  ## parses an IP version 4 in dotted decimal form like "a.b.c.d".
-  ##
-  ## This is equivalent to `inet_ntoa`:idx:.
-  ##
-  ## Raises EInvalidValue in case of an error.
-  var a, b, c, d: int
-  var i = 0
-  var j = parseInt(s, a, i)
-  if j <= 0: invalidIp4(s)
-  inc(i, j)
-  if s[i] == '.': inc(i)
-  else: invalidIp4(s)
-  j = parseInt(s, b, i)
-  if j <= 0: invalidIp4(s)
-  inc(i, j)
-  if s[i] == '.': inc(i)
-  else: invalidIp4(s)
-  j = parseInt(s, c, i)
-  if j <= 0: invalidIp4(s)
-  inc(i, j)
-  if s[i] == '.': inc(i)
-  else: invalidIp4(s)
-  j = parseInt(s, d, i)
-  if j <= 0: invalidIp4(s)
-  inc(i, j)
-  if s[i] != '\0': invalidIp4(s)
-  result = BiggestInt(a shl 24 or b shl 16 or c shl 8 or d)
-
-template gaiNim(a, p, h, list: untyped): untyped =
-  var gaiResult = getaddrinfo(a, $p, addr(h), list)
-  if gaiResult != 0'i32:
-    when defined(windows):
-      raiseOSError(osLastError())
-    else:
-      raiseOSError(osLastError(), $gai_strerror(gaiResult))
-
-proc bindAddr*(socket: Socket, port = Port(0), address = "") {.
-  tags: [ReadIOEffect].} =
-  ## binds an address/port number to a socket.
-  ## Use address string in dotted decimal form like "a.b.c.d"
-  ## or leave "" for any address.
-
-  if address == "":
-    var name: Sockaddr_in
-    when defined(Windows):
-      name.sin_family = int16(ord(AF_INET))
-    else:
-      name.sin_family = posix.AF_INET
-    name.sin_port = sockets.htons(uint16(port))
-    name.sin_addr.s_addr = sockets.htonl(INADDR_ANY)
-    if bindSocket(socket.fd, cast[ptr SockAddr](addr(name)),
-                  sizeof(name).SockLen) < 0'i32:
-      raiseOSError(osLastError())
-  else:
-    var hints: AddrInfo
-    var aiList: ptr AddrInfo = nil
-    hints.ai_family = toInt(AF_INET)
-    hints.ai_socktype = toInt(SOCK_STREAM)
-    hints.ai_protocol = toInt(IPPROTO_TCP)
-    gaiNim(address, port, hints, aiList)
-    if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32:
-      raiseOSError(osLastError())
-
-proc getSockName*(socket: Socket): Port =
-  ## returns the socket's associated port number.
-  var name: Sockaddr_in
-  when defined(Windows):
-    name.sin_family = int16(ord(AF_INET))
-  else:
-    name.sin_family = posix.AF_INET
-  #name.sin_port = htons(cint16(port))
-  #name.sin_addr.s_addr = htonl(INADDR_ANY)
-  var namelen = sizeof(name).SockLen
-  if getsockname(socket.fd, cast[ptr SockAddr](addr(name)),
-                 addr(namelen)) == -1'i32:
-    raiseOSError(osLastError())
-  result = Port(sockets.ntohs(name.sin_port))
-
-template acceptAddrPlain(noClientRet, successRet: SSLAcceptResult or int,
-                         sslImplementation: untyped): untyped =
-  assert(client != nil)
-  var sockAddress: Sockaddr_in
-  var addrLen = sizeof(sockAddress).SockLen
-  var sock = accept(server.fd, cast[ptr SockAddr](addr(sockAddress)),
-                    addr(addrLen))
-
-  if sock == osInvalidSocket:
-    let err = osLastError()
-    when defined(windows):
-      if err.int32 == WSAEINPROGRESS:
-        client = invalidSocket
-        address = ""
-        when noClientRet.int == -1:
-          return
-        else:
-          return noClientRet
-      else: raiseOSError(err)
-    else:
-      if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
-        client = invalidSocket
-        address = ""
-        when noClientRet.int == -1:
-          return
-        else:
-          return noClientRet
-      else: raiseOSError(err)
-  else:
-    client.fd = sock
-    client.isBuffered = server.isBuffered
-    sslImplementation
-    # Client socket is set above.
-    address = $inet_ntoa(sockAddress.sin_addr)
-    when successRet.int == -1:
-      return
-    else:
-      return successRet
-
-proc acceptAddr*(server: Socket, client: var Socket, address: var string) {.
-  tags: [ReadIOEffect].} =
-  ## Blocks until a connection is being made from a client. When a connection
-  ## is made sets ``client`` to the client socket and ``address`` to the address
-  ## of the connecting client.
-  ## If ``server`` is non-blocking then this function returns immediately, and
-  ## if there are no connections queued the returned socket will be
-  ## ``InvalidSocket``.
-  ## This function will raise EOS if an error occurs.
-  ##
-  ## The resulting client will inherit any properties of the server socket. For
-  ## example: whether the socket is buffered or not.
-  ##
-  ## **Note**: ``client`` must be initialised (with ``new``), this function
-  ## makes no effort to initialise the ``client`` variable.
-  ##
-  ## **Warning:** When using SSL with non-blocking sockets, it is best to use
-  ## the acceptAddrSSL procedure as this procedure will most likely block.
-  acceptAddrPlain(SSLAcceptResult(-1), SSLAcceptResult(-1)):
-    when defined(ssl):
-      if server.isSSL:
-        # We must wrap the client sock in a ssl context.
-
-        server.sslContext.wrapSocket(client)
-        let ret = SSLAccept(client.sslHandle)
-        while ret <= 0:
-          let err = SSLGetError(client.sslHandle, ret)
-          if err != SSL_ERROR_WANT_ACCEPT:
-            case err
-            of SSL_ERROR_ZERO_RETURN:
-              raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
-            of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE,
-               SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
-              raiseSslError("acceptAddrSSL should be used for non-blocking SSL sockets.")
-            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")
-
-proc setBlocking*(s: Socket, blocking: bool) {.tags: [], gcsafe.}
-  ## Sets blocking mode on socket
-
-when defined(ssl):
-  proc acceptAddrSSL*(server: Socket, client: var Socket,
-                      address: var string): SSLAcceptResult {.
-                      tags: [ReadIOEffect].} =
-    ## This procedure should only be used for non-blocking **SSL** sockets.
-    ## It will immediately return with one of the following values:
-    ##
-    ## ``AcceptSuccess`` will be returned when a client has been successfully
-    ## accepted and the handshake has been successfully performed between
-    ## ``server`` and the newly connected client.
-    ##
-    ## ``AcceptNoHandshake`` will be returned when a client has been accepted
-    ## but no handshake could be performed. This can happen when the client
-    ## connects but does not yet initiate a handshake. In this case
-    ## ``acceptAddrSSL`` should be called again with the same parameters.
-    ##
-    ## ``AcceptNoClient`` will be returned when no client is currently attempting
-    ## to connect.
-    template doHandshake(): untyped =
-      when defined(ssl):
-        if server.isSSL:
-          client.setBlocking(false)
-          # We must wrap the client sock in a ssl context.
-
-          if not client.isSSL or client.sslHandle == nil:
-            server.sslContext.wrapSocket(client)
-          let ret = SSLAccept(client.sslHandle)
-          while ret <= 0:
-            let err = SSLGetError(client.sslHandle, ret)
-            if err != SSL_ERROR_WANT_ACCEPT:
-              case err
-              of SSL_ERROR_ZERO_RETURN:
-                raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
-              of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE,
-                 SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
-                client.sslNoHandshake = true
-                return AcceptNoHandshake
-              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")
-          client.sslNoHandshake = false
-
-    if client.isSSL and client.sslNoHandshake:
-      doHandshake()
-      return AcceptSuccess
-    else:
-      acceptAddrPlain(AcceptNoClient, AcceptSuccess):
-        doHandshake()
-
-proc accept*(server: Socket, client: var Socket) {.tags: [ReadIOEffect].} =
-  ## Equivalent to ``acceptAddr`` but doesn't return the address, only the
-  ## socket.
-  ##
-  ## **Note**: ``client`` must be initialised (with ``new``), this function
-  ## makes no effort to initialise the ``client`` variable.
-
-  var addrDummy = ""
-  acceptAddr(server, client, addrDummy)
-
-proc acceptAddr*(server: Socket): tuple[client: Socket, address: string] {.
-  deprecated, tags: [ReadIOEffect].} =
-  ## Slightly different version of ``acceptAddr``.
-  ##
-  ## **Deprecated since version 0.9.0:** Please use the function above.
-  var client: Socket
-  new(client)
-  var address = ""
-  acceptAddr(server, client, address)
-  return (client, address)
-
-proc accept*(server: Socket): Socket {.deprecated, tags: [ReadIOEffect].} =
-  ## **Deprecated since version 0.9.0:** Please use the function above.
-  new(result)
-  var address = ""
-  acceptAddr(server, result, address)
-
-proc close*(socket: Socket) =
-  ## closes a socket.
-  when defined(windows):
-    discard winlean.closesocket(socket.fd)
-  else:
-    discard posix.close(socket.fd)
-  # TODO: These values should not be discarded. An EOS should be raised.
-  # http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
-  when defined(ssl):
-    if socket.isSSL:
-      discard SSLShutdown(socket.sslHandle)
-      SSLFree(socket.sslHandle)
-      socket.sslHandle = nil
-
-proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} =
-  ## Searches the database from the beginning and finds the first entry for
-  ## which the service name specified by ``name`` matches the s_name member
-  ## and the protocol name specified by ``proto`` matches the s_proto member.
-  ##
-  ## On posix this will search through the ``/etc/services`` file.
-  when defined(Windows):
-    var s = winlean.getservbyname(name, proto)
-  else:
-    var s = posix.getservbyname(name, proto)
-  if s == nil: raiseOSError(osLastError(), "Service not found.")
-  result.name = $s.s_name
-  result.aliases = cstringArrayToSeq(s.s_aliases)
-  result.port = Port(s.s_port)
-  result.proto = $s.s_proto
-
-proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} =
-  ## Searches the database from the beginning and finds the first entry for
-  ## which the port specified by ``port`` matches the s_port member and the
-  ## protocol name specified by ``proto`` matches the s_proto member.
-  ##
-  ## On posix this will search through the ``/etc/services`` file.
-  when defined(Windows):
-    var s = winlean.getservbyport(ze(int16(port)).cint, proto)
-  else:
-    var s = posix.getservbyport(ze(int16(port)).cint, proto)
-  if s == nil: raiseOSError(osLastError(), "Service not found.")
-  result.name = $s.s_name
-  result.aliases = cstringArrayToSeq(s.s_aliases)
-  result.port = Port(s.s_port)
-  result.proto = $s.s_proto
-
-proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} =
-  ## This function will lookup the hostname of an IP Address.
-  var myaddr: InAddr
-  myaddr.s_addr = inet_addr(ip)
-
-  when defined(windows):
-    var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
-                                  cint(sockets.AF_INET))
-    if s == nil: raiseOSError(osLastError())
-  else:
-    var s =
-      when defined(android4):
-        posix.gethostbyaddr(cast[cstring](addr(myaddr)), sizeof(myaddr).cint,
-                            cint(posix.AF_INET))
-      else:
-        posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen,
-                            cint(posix.AF_INET))
-    if s == nil:
-      raiseOSError(osLastError(), $hstrerror(h_errno))
-
-  result.name = $s.h_name
-  result.aliases = cstringArrayToSeq(s.h_aliases)
-  when defined(windows):
-    result.addrtype = Domain(s.h_addrtype)
-  else:
-    if s.h_addrtype == posix.AF_INET:
-      result.addrtype = AF_INET
-    elif s.h_addrtype == posix.AF_INET6:
-      result.addrtype = AF_INET6
-    else:
-      raiseOSError(osLastError(), "unknown h_addrtype")
-  result.addrList = cstringArrayToSeq(s.h_addr_list)
-  result.length = int(s.h_length)
-
-proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
-  ## This function will lookup the IP address of a hostname.
-  when defined(Windows):
-    var s = winlean.gethostbyname(name)
-  else:
-    var s = posix.gethostbyname(name)
-  if s == nil: raiseOSError(osLastError())
-  result.name = $s.h_name
-  result.aliases = cstringArrayToSeq(s.h_aliases)
-  when defined(windows):
-    result.addrtype = Domain(s.h_addrtype)
-  else:
-    if s.h_addrtype == posix.AF_INET:
-      result.addrtype = AF_INET
-    elif s.h_addrtype == posix.AF_INET6:
-      result.addrtype = AF_INET6
-    else:
-      raiseOSError(osLastError(), "unknown h_addrtype")
-  result.addrList = cstringArrayToSeq(s.h_addr_list)
-  result.length = int(s.h_length)
-
-proc getSockOptInt*(socket: Socket, level, optname: int): int {.
-  tags: [ReadIOEffect].} =
-  ## getsockopt for integer options.
-  var res: cint
-  var size = sizeof(res).SockLen
-  if getsockopt(socket.fd, cint(level), cint(optname),
-                addr(res), addr(size)) < 0'i32:
-    raiseOSError(osLastError())
-  result = int(res)
-
-proc setSockOptInt*(socket: Socket, level, optname, optval: int) {.
-  tags: [WriteIOEffect].} =
-  ## setsockopt for integer options.
-  var value = cint(optval)
-  if setsockopt(socket.fd, cint(level), cint(optname), addr(value),
-                sizeof(value).SockLen) < 0'i32:
-    raiseOSError(osLastError())
-
-proc toCInt(opt: SOBool): cint =
-  case opt
-  of OptAcceptConn: SO_ACCEPTCONN
-  of OptBroadcast: SO_BROADCAST
-  of OptDebug: SO_DEBUG
-  of OptDontRoute: SO_DONTROUTE
-  of OptKeepAlive: SO_KEEPALIVE
-  of OptOOBInline: SO_OOBINLINE
-  of OptReuseAddr: SO_REUSEADDR
-
-proc getSockOpt*(socket: Socket, opt: SOBool, level = SOL_SOCKET): bool {.
-  tags: [ReadIOEffect].} =
-  ## Retrieves option ``opt`` as a boolean value.
-  var res: cint
-  var size = sizeof(res).SockLen
-  if getsockopt(socket.fd, cint(level), toCInt(opt),
-                addr(res), addr(size)) < 0'i32:
-    raiseOSError(osLastError())
-  result = res != 0
-
-proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {.
-  tags: [WriteIOEffect].} =
-  ## Sets option ``opt`` to a boolean value specified by ``value``.
-  var valuei = cint(if value: 1 else: 0)
-  if setsockopt(socket.fd, cint(level), toCInt(opt), addr(valuei),
-                sizeof(valuei).SockLen) < 0'i32:
-    raiseOSError(osLastError())
-
-proc connect*(socket: Socket, address: string, port = Port(0),
-              af: Domain = AF_INET) {.tags: [ReadIOEffect].} =
-  ## Connects socket to ``address``:``port``. ``Address`` can be an IP address or a
-  ## host name. If ``address`` is a host name, this function will try each IP
-  ## of that host name. ``htons`` is already performed on ``port`` so you must
-  ## not do it.
-  ##
-  ## If ``socket`` is an SSL socket a handshake will be automatically performed.
-  var hints: AddrInfo
-  var aiList: ptr AddrInfo = nil
-  hints.ai_family = toInt(af)
-  hints.ai_socktype = toInt(SOCK_STREAM)
-  hints.ai_protocol = toInt(IPPROTO_TCP)
-  gaiNim(address, port, hints, aiList)
-  # try all possibilities:
-  var success = false
-  var lastError: OSErrorCode
-  var it = aiList
-  while it != nil:
-    if connect(socket.fd, it.ai_addr, it.ai_addrlen.SockLen) == 0'i32:
-      success = true
-      break
-    else: lastError = osLastError()
-    it = it.ai_next
-
-  freeaddrinfo(aiList)
-  if not success: raiseOSError(lastError)
-
-  when defined(ssl):
-    if socket.isSSL:
-      let ret = SSLConnect(socket.sslHandle)
-      if ret <= 0:
-        let err = SSLGetError(socket.sslHandle, ret)
-        case err
-        of SSL_ERROR_ZERO_RETURN:
-          raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
-        of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_CONNECT,
-           SSL_ERROR_WANT_ACCEPT:
-          raiseSslError("The operation did not complete. Perhaps you should use connectAsync?")
-        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")
-
-  when false:
-    var s: TSockAddrIn
-    s.sin_addr.s_addr = inet_addr(address)
-    s.sin_port = sockets.htons(uint16(port))
-    when defined(windows):
-      s.sin_family = toU16(ord(af))
-    else:
-      case af
-      of AF_UNIX: s.sin_family = posix.AF_UNIX
-      of AF_INET: s.sin_family = posix.AF_INET
-      of AF_INET6: s.sin_family = posix.AF_INET6
-      else: nil
-    if connect(socket.fd, cast[ptr TSockAddr](addr(s)), sizeof(s).cint) < 0'i32:
-      OSError()
-
-proc connectAsync*(socket: Socket, name: string, port = Port(0),
-                     af: Domain = AF_INET) {.tags: [ReadIOEffect].} =
-  ## A variant of ``connect`` for non-blocking sockets.
-  ##
-  ## This procedure will immediately return, it will not block until a connection
-  ## is made. It is up to the caller to make sure the connection has been established
-  ## by checking (using ``select``) whether the socket is writeable.
-  ##
-  ## **Note**: For SSL sockets, the ``handshake`` procedure must be called
-  ## whenever the socket successfully connects to a server.
-  var hints: AddrInfo
-  var aiList: ptr AddrInfo = nil
-  hints.ai_family = toInt(af)
-  hints.ai_socktype = toInt(SOCK_STREAM)
-  hints.ai_protocol = toInt(IPPROTO_TCP)
-  gaiNim(name, port, hints, aiList)
-  # try all possibilities:
-  var success = false
-  var lastError: OSErrorCode
-  var it = aiList
-  while it != nil:
-    var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.SockLen)
-    if ret == 0'i32:
-      success = true
-      break
-    else:
-      lastError = osLastError()
-      when defined(windows):
-        # Windows EINTR doesn't behave same as POSIX.
-        if lastError.int32 == WSAEWOULDBLOCK:
-          success = true
-          break
-      else:
-        if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
-          success = true
-          break
-
-    it = it.ai_next
-
-  freeaddrinfo(aiList)
-  if not success: raiseOSError(lastError)
-  when defined(ssl):
-    if socket.isSSL:
-      socket.sslNoHandshake = true
-
-when defined(ssl):
-  proc handshake*(socket: Socket): bool {.tags: [ReadIOEffect, WriteIOEffect].} =
-    ## 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 ESSL error is raised on any other errors.
-    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.
-    ##
-    ## Throws ESSL if ``socket`` is not an SSL socket.
-    if socket.isSSL:
-      return not socket.sslNoHandshake
-    else:
-      raiseSslError("Socket is not an SSL socket.")
-
-proc timeValFromMilliseconds(timeout = 500): Timeval =
-  if timeout != -1:
-    var seconds = timeout div 1000
-    when defined(posix):
-      result.tv_sec = seconds.Time
-      result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds
-    else:
-      result.tv_sec = seconds.int32
-      result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
-
-proc createFdSet(fd: var TFdSet, s: seq[Socket], m: var int) =
-  FD_ZERO(fd)
-  for i in items(s):
-    m = max(m, int(i.fd))
-    FD_SET(i.fd, fd)
-
-proc pruneSocketSet(s: var seq[Socket], fd: var TFdSet) =
-  var i = 0
-  var L = s.len
-  while i < L:
-    if FD_ISSET(s[i].fd, fd) == 0'i32:
-      # not set.
-      s[i] = s[L-1]
-      dec(L)
-    else:
-      inc(i)
-  setLen(s, L)
-
-proc hasDataBuffered*(s: Socket): bool =
-  ## Determines whether a socket has data buffered.
-  result = false
-  if s.isBuffered:
-    result = s.bufLen > 0 and s.currPos != s.bufLen
-
-  when defined(ssl):
-    if s.isSSL and not result:
-      result = s.sslHasPeekChar
-
-proc checkBuffer(readfds: var seq[Socket]): int =
-  ## Checks the buffer of each socket in ``readfds`` to see whether there is data.
-  ## Removes the sockets from ``readfds`` and returns the count of removed sockets.
-  var res: seq[Socket] = @[]
-  result = 0
-  for s in readfds:
-    if hasDataBuffered(s):
-      inc(result)
-      res.add(s)
-  if result > 0:
-    readfds = res
-
-proc select*(readfds, writefds, exceptfds: var seq[Socket],
-             timeout = 500): int {.tags: [ReadIOEffect].} =
-  ## Traditional select function. This function will return the number of
-  ## sockets that are ready to be read from, written to, or which have errors.
-  ## If there are none; 0 is returned.
-  ## ``Timeout`` is in milliseconds and -1 can be specified for no timeout.
-  ##
-  ## Sockets which are **not** ready for reading, writing or which don't have
-  ## errors waiting on them are removed from the ``readfds``, ``writefds``,
-  ## ``exceptfds`` sequences respectively.
-  let buffersFilled = checkBuffer(readfds)
-  if buffersFilled > 0:
-    return buffersFilled
-
-  var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
-
-  var rd, wr, ex: TFdSet
-  var m = 0
-  createFdSet((rd), readfds, m)
-  createFdSet((wr), writefds, m)
-  createFdSet((ex), exceptfds, m)
-
-  if timeout != -1:
-    result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), addr(tv)))
-  else:
-    result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), nil))
-
-  pruneSocketSet(readfds, (rd))
-  pruneSocketSet(writefds, (wr))
-  pruneSocketSet(exceptfds, (ex))
-
-proc select*(readfds, writefds: var seq[Socket],
-             timeout = 500): int {.tags: [ReadIOEffect].} =
-  ## Variant of select with only a read and write list.
-  let buffersFilled = checkBuffer(readfds)
-  if buffersFilled > 0:
-    return buffersFilled
-  var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
-
-  var rd, wr: TFdSet
-  var m = 0
-  createFdSet((rd), readfds, m)
-  createFdSet((wr), writefds, m)
-
-  if timeout != -1:
-    result = int(select(cint(m+1), addr(rd), addr(wr), nil, addr(tv)))
-  else:
-    result = int(select(cint(m+1), addr(rd), addr(wr), nil, nil))
-
-  pruneSocketSet(readfds, (rd))
-  pruneSocketSet(writefds, (wr))
-
-proc selectWrite*(writefds: var seq[Socket],
-                  timeout = 500): int {.tags: [ReadIOEffect].} =
-  ## When a socket in ``writefds`` is ready to be written to then a non-zero
-  ## value will be returned specifying the count of the sockets which can be
-  ## written to. The sockets which **cannot** be written to will also be removed
-  ## from ``writefds``.
-  ##
-  ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for
-  ## an unlimited time.
-  var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
-
-  var wr: TFdSet
-  var m = 0
-  createFdSet((wr), writefds, m)
-
-  if timeout != -1:
-    result = int(select(cint(m+1), nil, addr(wr), nil, addr(tv)))
-  else:
-    result = int(select(cint(m+1), nil, addr(wr), nil, nil))
-
-  pruneSocketSet(writefds, (wr))
-
-proc select*(readfds: var seq[Socket], timeout = 500): int =
-  ## variant of select with a read list only
-  let buffersFilled = checkBuffer(readfds)
-  if buffersFilled > 0:
-    return buffersFilled
-  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 readIntoBuf(socket: Socket, flags: int32): int =
-  result = 0
-  when defined(ssl):
-    if socket.isSSL:
-      result = SSLRead(socket.sslHandle, addr(socket.buffer), int(socket.buffer.high))
-    else:
-      result = recv(socket.fd, addr(socket.buffer), cint(socket.buffer.high), flags)
-  else:
-    result = recv(socket.fd, addr(socket.buffer), cint(socket.buffer.high), flags)
-  if result <= 0:
-    socket.bufLen = 0
-    socket.currPos = 0
-    return result
-  socket.bufLen = result
-  socket.currPos = 0
-
-template retRead(flags, readBytes: int) {.dirty.} =
-  let res = socket.readIntoBuf(flags.int32)
-  if res <= 0:
-    if readBytes > 0:
-      return readBytes
-    else:
-      return res
-
-proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ReadIOEffect].} =
-  ## Receives data from a socket.
-  ##
-  ## **Note**: This is a low-level function, you may be interested in the higher
-  ## level versions of this function which are also named ``recv``.
-  if size == 0: return
-  if socket.isBuffered:
-    if socket.bufLen == 0:
-      retRead(0'i32, 0)
-
-    var read = 0
-    while read < size:
-      if socket.currPos >= socket.bufLen:
-        retRead(0'i32, read)
-
-      let chunk = min(socket.bufLen-socket.currPos, size-read)
-      var d = cast[cstring](data)
-      copyMem(addr(d[read]), addr(socket.buffer[socket.currPos]), chunk)
-      read.inc(chunk)
-      socket.currPos.inc(chunk)
-
-    result = read
-  else:
-    when defined(ssl):
-      if socket.isSSL:
-        if socket.sslHasPeekChar:
-          copyMem(data, addr(socket.sslPeekChar), 1)
-          socket.sslHasPeekChar = false
-          if size-1 > 0:
-            var d = cast[cstring](data)
-            result = SSLRead(socket.sslHandle, addr(d[1]), size-1) + 1
-          else:
-            result = 1
-        else:
-          result = SSLRead(socket.sslHandle, data, size)
-      else:
-        result = recv(socket.fd, data, size.cint, 0'i32)
-    else:
-      result = recv(socket.fd, data, size.cint, 0'i32)
-
-proc waitFor(socket: Socket, waited: var float, timeout, size: int,
-             funcName: string): int {.tags: [TimeEffect].} =
-  ## determines the amount of characters that can be read. Result will never
-  ## be larger than ``size``. For unbuffered sockets this will be ``1``.
-  ## For buffered sockets it can be as big as ``BufferSize``.
-  ##
-  ## If this function does not determine that there is data on the socket
-  ## within ``timeout`` ms, an ETimeout error will be raised.
-  result = 1
-  if size <= 0: assert false
-  if timeout == -1: return size
-  if socket.isBuffered and socket.bufLen != 0 and socket.bufLen != socket.currPos:
-    result = socket.bufLen - socket.currPos
-    result = min(result, size)
-  else:
-    if timeout - int(waited * 1000.0) < 1:
-      raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
-
-    when defined(ssl):
-      if socket.isSSL:
-        if socket.hasDataBuffered:
-          # sslPeekChar is present.
-          return 1
-        let sslPending = SSLPending(socket.sslHandle)
-        if sslPending != 0:
-          return sslPending
-
-    var s = @[socket]
-    var startTime = epochTime()
-    let selRet = select(s, timeout - int(waited * 1000.0))
-    if selRet < 0: raiseOSError(osLastError())
-    if selRet != 1:
-      raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
-    waited += (epochTime() - startTime)
-
-proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {.
-  tags: [ReadIOEffect, TimeEffect].} =
-  ## overload with a ``timeout`` parameter in milliseconds.
-  var waited = 0.0 # number of seconds already waited
-
-  var read = 0
-  while read < size:
-    let avail = waitFor(socket, waited, timeout, size-read, "recv")
-    var d = cast[cstring](data)
-    result = recv(socket, addr(d[read]), avail)
-    if result == 0: break
-    if result < 0:
-      return result
-    inc(read, result)
-
-  result = read
-
-proc recv*(socket: Socket, data: var string, size: int, timeout = -1): int =
-  ## Higher-level version of ``recv``.
-  ##
-  ## When 0 is returned the socket's connection has been closed.
-  ##
-  ## This function will throw an EOS exception when an error occurs. A value
-  ## lower than 0 is never returned.
-  ##
-  ## A timeout may be specified in milliseconds, if enough data is not received
-  ## within the time specified an ETimeout exception will be raised.
-  ##
-  ## **Note**: ``data`` must be initialised.
-  data.setLen(size)
-  result = recv(socket, cstring(data), size, timeout)
-  if result < 0:
-    data.setLen(0)
-    socket.raiseSocketError(result)
-  data.setLen(result)
-
-proc recvAsync*(socket: Socket, data: var string, size: int): int =
-  ## Async version of ``recv``.
-  ##
-  ## When socket is non-blocking and no data is available on the socket,
-  ## ``-1`` will be returned and ``data`` will be ``""``.
-  ##
-  ## **Note**: ``data`` must be initialised.
-  data.setLen(size)
-  result = recv(socket, cstring(data), size)
-  if result < 0:
-    data.setLen(0)
-    socket.raiseSocketError(async = true)
-    result = -1
-  data.setLen(result)
-
-proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} =
-  if socket.isBuffered:
-    result = 1
-    if socket.bufLen == 0 or socket.currPos > socket.bufLen-1:
-      var res = socket.readIntoBuf(0'i32)
-      if res <= 0:
-        result = res
-
-    c = socket.buffer[socket.currPos]
-  else:
-    when defined(ssl):
-      if socket.isSSL:
-        if not socket.sslHasPeekChar:
-          result = SSLRead(socket.sslHandle, addr(socket.sslPeekChar), 1)
-          socket.sslHasPeekChar = true
-
-        c = socket.sslPeekChar
-        return
-    result = recv(socket.fd, addr(c), 1, MSG_PEEK)
-
-proc recvLine*(socket: Socket, line: var TaintedString, timeout = -1): bool {.
-  tags: [ReadIOEffect, TimeEffect], deprecated.} =
-  ## Receive a line of data from ``socket``.
-  ##
-  ## If a full line is received ``\r\L`` is not
-  ## added to ``line``, however if solely ``\r\L`` is received then ``line``
-  ## will be set to it.
-  ##
-  ## ``True`` is returned if data is available. ``False`` suggests an
-  ## error, EOS exceptions are not raised and ``False`` is simply returned
-  ## instead.
-  ##
-  ## If the socket is disconnected, ``line`` will be set to ``""`` and ``True``
-  ## will be returned.
-  ##
-  ## A timeout can be specified in milliseconds, if data is not received within
-  ## the specified time an ETimeout exception will be raised.
-  ##
-  ## **Deprecated since version 0.9.2**: This function has been deprecated in
-  ## favour of readLine.
-
-  template addNLIfEmpty(): untyped =
-    if line.len == 0:
-      line.add("\c\L")
-
-  var waited = 0.0
-
-  setLen(line.string, 0)
-  while true:
-    var c: char
-    discard waitFor(socket, waited, timeout, 1, "recvLine")
-    var n = recv(socket, addr(c), 1)
-    if n < 0: return
-    elif n == 0: return true
-    if c == '\r':
-      discard waitFor(socket, waited, timeout, 1, "recvLine")
-      n = peekChar(socket, c)
-      if n > 0 and c == '\L':
-        discard recv(socket, addr(c), 1)
-      elif n <= 0: return false
-      addNLIfEmpty()
-      return true
-    elif c == '\L':
-      addNLIfEmpty()
-      return true
-    add(line.string, c)
-
-proc readLine*(socket: Socket, line: var TaintedString, timeout = -1) {.
-  tags: [ReadIOEffect, TimeEffect].} =
-  ## Reads a line of data from ``socket``.
-  ##
-  ## If a full line is read ``\r\L`` is not
-  ## added to ``line``, however if solely ``\r\L`` is read then ``line``
-  ## will be set to it.
-  ##
-  ## If the socket is disconnected, ``line`` will be set to ``""``.
-  ##
-  ## An EOS exception will be raised in the case of a socket error.
-  ##
-  ## A timeout can be specified in milliseconds, if data is not received within
-  ## the specified time an ETimeout exception will be raised.
-
-  template addNLIfEmpty(): untyped =
-    if line.len == 0:
-      line.add("\c\L")
-
-  var waited = 0.0
-
-  setLen(line.string, 0)
-  while true:
-    var c: char
-    discard waitFor(socket, waited, timeout, 1, "readLine")
-    var n = recv(socket, addr(c), 1)
-    if n < 0: socket.raiseSocketError()
-    elif n == 0: return
-    if c == '\r':
-      discard waitFor(socket, waited, timeout, 1, "readLine")
-      n = peekChar(socket, c)
-      if n > 0 and c == '\L':
-        discard recv(socket, addr(c), 1)
-      elif n <= 0: socket.raiseSocketError()
-      addNLIfEmpty()
-      return
-    elif c == '\L':
-      addNLIfEmpty()
-      return
-    add(line.string, c)
-
-proc recvLineAsync*(socket: Socket,
-  line: var TaintedString): RecvLineResult {.tags: [ReadIOEffect], deprecated.} =
-  ## Similar to ``recvLine`` but designed for non-blocking sockets.
-  ##
-  ## The values of the returned enum should be pretty self explanatory:
-  ##
-  ##   * If a full line has been retrieved; ``RecvFullLine`` is returned.
-  ##   * If some data has been retrieved; ``RecvPartialLine`` is returned.
-  ##   * If the socket has been disconnected; ``RecvDisconnected`` is returned.
-  ##   * If call to ``recv`` failed; ``RecvFail`` is returned.
-  ##
-  ## **Deprecated since version 0.9.2**: This function has been deprecated in
-  ## favour of readLineAsync.
-
-  setLen(line.string, 0)
-  while true:
-    var c: char
-    var n = recv(socket, addr(c), 1)
-    if n < 0:
-      return (if line.len == 0: RecvFail else: RecvPartialLine)
-    elif n == 0:
-      return (if line.len == 0: RecvDisconnected else: RecvPartialLine)
-    if c == '\r':
-      n = peekChar(socket, c)
-      if n > 0 and c == '\L':
-        discard recv(socket, addr(c), 1)
-      elif n <= 0:
-        return (if line.len == 0: RecvFail else: RecvPartialLine)
-      return RecvFullLine
-    elif c == '\L': return RecvFullLine
-    add(line.string, c)
-
-proc readLineAsync*(socket: Socket,
-  line: var TaintedString): ReadLineResult {.tags: [ReadIOEffect].} =
-  ## Similar to ``recvLine`` but designed for non-blocking sockets.
-  ##
-  ## The values of the returned enum should be pretty self explanatory:
-  ##
-  ##   * If a full line has been retrieved; ``ReadFullLine`` is returned.
-  ##   * If some data has been retrieved; ``ReadPartialLine`` is returned.
-  ##   * If the socket has been disconnected; ``ReadDisconnected`` is returned.
-  ##   * If no data could be retrieved; ``ReadNone`` is returned.
-  ##   * If call to ``recv`` failed; **an EOS exception is raised.**
-  setLen(line.string, 0)
-
-  template errorOrNone =
-    socket.raiseSocketError(async = true)
-    return ReadNone
-
-  while true:
-    var c: char
-    var n = recv(socket, addr(c), 1)
-    #echo(n)
-    if n < 0:
-      if line.len == 0: errorOrNone else: return ReadPartialLine
-    elif n == 0:
-      return (if line.len == 0: ReadDisconnected else: ReadPartialLine)
-    if c == '\r':
-      n = peekChar(socket, c)
-      if n > 0 and c == '\L':
-        discard recv(socket, addr(c), 1)
-      elif n <= 0:
-        if line.len == 0: errorOrNone else: return ReadPartialLine
-      return ReadFullLine
-    elif c == '\L': return ReadFullLine
-    add(line.string, c)
-
-proc recv*(socket: Socket): TaintedString {.tags: [ReadIOEffect], deprecated.} =
-  ## receives all the available data from the socket.
-  ## Socket errors will result in an ``EOS`` error.
-  ## If socket is not a connectionless socket and socket is not connected
-  ## ``""`` will be returned.
-  ##
-  ## **Deprecated since version 0.9.2**: This function is not safe for use.
-  const bufSize = 4000
-  result = newStringOfCap(bufSize).TaintedString
-  var pos = 0
-  while true:
-    var bytesRead = recv(socket, addr(string(result)[pos]), bufSize-1)
-    if bytesRead == -1: raiseOSError(osLastError())
-    setLen(result.string, pos + bytesRead)
-    if bytesRead != bufSize-1: break
-    # increase capacity:
-    setLen(result.string, result.string.len + bufSize)
-    inc(pos, bytesRead)
-  when false:
-    var buf = newString(bufSize)
-    result = TaintedString""
-    while true:
-      var bytesRead = recv(socket, cstring(buf), bufSize-1)
-      # Error
-      if bytesRead == -1: OSError(osLastError())
-
-      buf[bytesRead] = '\0' # might not be necessary
-      setLen(buf, bytesRead)
-      add(result.string, buf)
-      if bytesRead != bufSize-1: break
-
-{.push warning[deprecated]: off.}
-proc recvTimeout*(socket: Socket, timeout: int): TaintedString {.
-  tags: [ReadIOEffect], deprecated.} =
-  ## overloaded variant to support a ``timeout`` parameter, the ``timeout``
-  ## parameter specifies the amount of milliseconds to wait for data on the
-  ## socket.
-  ##
-  ## **Deprecated since version 0.9.2**: This function is not safe for use.
-  if socket.bufLen == 0:
-    var s = @[socket]
-    if s.select(timeout) != 1:
-      raise newException(TimeoutError, "Call to recv() timed out.")
-
-  return socket.recv
-{.pop.}
-
-proc recvAsync*(socket: Socket, s: var TaintedString): bool {.
-  tags: [ReadIOEffect], deprecated.} =
-  ## receives all the data from a non-blocking socket. If socket is non-blocking
-  ## and there are no messages available, `False` will be returned.
-  ## Other socket errors will result in an ``EOS`` error.
-  ## If socket is not a connectionless socket and socket is not connected
-  ## ``s`` will be set to ``""``.
-  ##
-  ## **Deprecated since version 0.9.2**: This function is not safe for use.
-  const bufSize = 1000
-  # ensure bufSize capacity:
-  setLen(s.string, bufSize)
-  setLen(s.string, 0)
-  var pos = 0
-  while true:
-    var bytesRead = recv(socket, addr(string(s)[pos]), bufSize-1)
-    when defined(ssl):
-      if socket.isSSL:
-        if bytesRead <= 0:
-          var ret = SSLGetError(socket.sslHandle, bytesRead.cint)
-          case ret
-          of SSL_ERROR_ZERO_RETURN:
-            raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
-          of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
-            raiseSslError("Unexpected error occurred.") # This should just not happen.
-          of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ:
-            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")
-
-    if bytesRead == -1 and not (when defined(ssl): socket.isSSL else: false):
-      let err = osLastError()
-      when defined(windows):
-        if err.int32 == WSAEWOULDBLOCK:
-          return false
-        else: raiseOSError(err)
-      else:
-        if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
-          return false
-        else: raiseOSError(err)
-
-    setLen(s.string, pos + bytesRead)
-    if bytesRead != bufSize-1: break
-    # increase capacity:
-    setLen(s.string, s.string.len + bufSize)
-    inc(pos, bytesRead)
-  result = true
-
-proc recvFrom*(socket: Socket, data: var string, length: int,
-               address: var string, port: var Port, flags = 0'i32): int {.
-               tags: [ReadIOEffect].} =
-  ## Receives data from ``socket``. This function should normally be used with
-  ## connection-less sockets (UDP sockets).
-  ##
-  ## If an error occurs the return value will be ``-1``. Otherwise the return
-  ## value will be the length of data received.
-  ##
-  ## **Warning:** This function does not yet have a buffered implementation,
-  ## so when ``socket`` is buffered the non-buffered implementation will be
-  ## used. Therefore if ``socket`` contains something in its buffer this
-  ## function will make no effort to return it.
-
-  # TODO: Buffered sockets
-  data.setLen(length)
-  var sockAddress: Sockaddr_in
-  var addrLen = sizeof(sockAddress).SockLen
-  result = recvfrom(socket.fd, cstring(data), length.cint, flags.cint,
-                    cast[ptr SockAddr](addr(sockAddress)), addr(addrLen))
-
-  if result != -1:
-    data.setLen(result)
-    address = $inet_ntoa(sockAddress.sin_addr)
-    port = ntohs(sockAddress.sin_port).Port
-
-proc recvFromAsync*(socket: Socket, data: var string, length: int,
-                    address: var string, port: var Port,
-                    flags = 0'i32): bool {.tags: [ReadIOEffect].} =
-  ## Variant of ``recvFrom`` for non-blocking sockets. Unlike ``recvFrom``,
-  ## this function will raise an EOS error whenever a socket error occurs.
-  ##
-  ## If there is no data to be read from the socket ``False`` will be returned.
-  result = true
-  var callRes = recvFrom(socket, data, length, address, port, flags)
-  if callRes < 0:
-    let err = osLastError()
-    when defined(windows):
-      if err.int32 == WSAEWOULDBLOCK:
-        return false
-      else: raiseOSError(err)
-    else:
-      if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
-        return false
-      else: raiseOSError(err)
-
-proc skip*(socket: Socket) {.tags: [ReadIOEffect], deprecated.} =
-  ## skips all the data that is pending for the socket
-  ##
-  ## **Deprecated since version 0.9.2**: This function is not safe for use.
-  const bufSize = 1000
-  var buf = alloc(bufSize)
-  while recv(socket, buf, bufSize) == bufSize: discard
-  dealloc(buf)
-
-proc skip*(socket: Socket, size: int, timeout = -1) =
-  ## Skips ``size`` amount of bytes.
-  ##
-  ## An optional timeout can be specified in milliseconds, if skipping the
-  ## bytes takes longer than specified an ETimeout exception will be raised.
-  ##
-  ## Returns the number of skipped bytes.
-  var waited = 0.0
-  var dummy = alloc(size)
-  var bytesSkipped = 0
-  while bytesSkipped != size:
-    let avail = waitFor(socket, waited, timeout, size-bytesSkipped, "skip")
-    bytesSkipped += recv(socket, dummy, avail)
-  dealloc(dummy)
-
-proc send*(socket: Socket, data: pointer, size: int): int {.
-  tags: [WriteIOEffect].} =
-  ## sends data to a socket.
-  when defined(ssl):
-    if socket.isSSL:
-      return SSLWrite(socket.sslHandle, cast[cstring](data), size)
-
-  when defined(windows) or defined(macosx):
-    result = send(socket.fd, data, size.cint, 0'i32)
-  else:
-    when defined(solaris):
-      const MSG_NOSIGNAL = 0
-    result = send(socket.fd, data, size, int32(MSG_NOSIGNAL))
-
-proc send*(socket: Socket, data: string) {.tags: [WriteIOEffect].} =
-  ## sends data to a socket.
-  if socket.nonblocking:
-    raise newException(ValueError, "This function cannot be used on non-blocking sockets.")
-  let sent = send(socket, cstring(data), data.len)
-  if sent < 0:
-    when defined(ssl):
-      if socket.isSSL:
-        raiseSslError()
-
-    raiseOSError(osLastError())
-
-  if sent != data.len:
-    raiseOSError(osLastError(), "Could not send all data.")
-
-proc sendAsync*(socket: Socket, data: string): int {.tags: [WriteIOEffect].} =
-  ## sends data to a non-blocking socket.
-  ## Returns ``0`` if no data could be sent, if data has been sent
-  ## returns the amount of bytes of ``data`` that was successfully sent. This
-  ## number may not always be the length of ``data`` but typically is.
-  ##
-  ## An EOS (or ESSL if socket is an SSL socket) exception is raised if an error
-  ## occurs.
-  result = send(socket, cstring(data), data.len)
-  when defined(ssl):
-    if socket.isSSL:
-      if result <= 0:
-          let ret = SSLGetError(socket.sslHandle, result.cint)
-          case ret
-          of SSL_ERROR_ZERO_RETURN:
-            raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
-          of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
-            raiseSslError("Unexpected error occurred.") # This should just not happen.
-          of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ:
-            return 0
-          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")
-      else:
-        return
-  if result == -1:
-    let err = osLastError()
-    when defined(windows):
-      if err.int32 == WSAEINPROGRESS:
-        return 0
-      else: raiseOSError(err)
-    else:
-      if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
-        return 0
-      else: raiseOSError(err)
-
-
-proc trySend*(socket: Socket, data: string): bool {.tags: [WriteIOEffect].} =
-  ## safe alternative to ``send``. Does not raise an EOS when an error occurs,
-  ## and instead returns ``false`` on failure.
-  result = send(socket, cstring(data), data.len) == data.len
-
-proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
-             size: int, af: Domain = AF_INET, flags = 0'i32): int {.
-             tags: [WriteIOEffect].} =
-  ## low-level sendTo proc. This proc sends ``data`` to the specified ``address``,
-  ## which may be an IP address or a hostname, if a hostname is specified
-  ## this function will try each IP of that hostname.
-  ##
-  ## **Note:** This proc is not available for SSL sockets.
-  var hints: AddrInfo
-  var aiList: ptr AddrInfo = nil
-  hints.ai_family = toInt(af)
-  hints.ai_socktype = toInt(SOCK_STREAM)
-  hints.ai_protocol = toInt(IPPROTO_TCP)
-  gaiNim(address, port, hints, aiList)
-
-  # try all possibilities:
-  var success = false
-  var it = aiList
-  while it != nil:
-    result = sendto(socket.fd, data, size.cint, flags.cint, it.ai_addr,
-                    it.ai_addrlen.SockLen)
-    if result != -1'i32:
-      success = true
-      break
-    it = it.ai_next
-
-  freeaddrinfo(aiList)
-
-proc sendTo*(socket: Socket, address: string, port: Port,
-             data: string): int {.tags: [WriteIOEffect].} =
-  ## Friendlier version of the low-level ``sendTo``.
-  result = socket.sendTo(address, port, cstring(data), data.len)
-
-when defined(Windows):
-  const
-    IOCPARM_MASK = 127
-    IOC_IN = int(-2147483648)
-    FIONBIO = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or
-                             (102 shl 8) or 126
-
-  proc ioctlsocket(s: SocketHandle, cmd: clong,
-                   argptr: ptr clong): cint {.
-                   stdcall, importc:"ioctlsocket", dynlib: "ws2_32.dll".}
-
-proc setBlocking(s: Socket, blocking: bool) =
-  when defined(Windows):
-    var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
-    if ioctlsocket(s.fd, FIONBIO, addr(mode)) == -1:
-      raiseOSError(osLastError())
-  else: # BSD sockets
-    var x: int = fcntl(s.fd, F_GETFL, 0)
-    if x == -1:
-      raiseOSError(osLastError())
-    else:
-      var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
-      if fcntl(s.fd, F_SETFL, mode) == -1:
-        raiseOSError(osLastError())
-  s.nonblocking = not blocking
-
-discard """ proc setReuseAddr*(s: Socket) =
-  var blah: int = 1
-  var mode = SO_REUSEADDR
-  if setsockopt(s.fd, SOL_SOCKET, mode, addr blah, TSOcklen(sizeof(int))) == -1:
-    raiseOSError(osLastError()) """
-
-proc connect*(socket: Socket, address: string, port = Port(0), timeout: int,
-             af: Domain = AF_INET) {.tags: [ReadIOEffect, WriteIOEffect].} =
-  ## Connects to server as specified by ``address`` on port specified by ``port``.
-  ##
-  ## The ``timeout`` paremeter specifies the time in milliseconds to allow for
-  ## the connection to the server to be made.
-  let originalStatus = not socket.nonblocking
-  socket.setBlocking(false)
-
-  socket.connectAsync(address, port, af)
-  var s: seq[Socket] = @[socket]
-  if selectWrite(s, timeout) != 1:
-    raise newException(TimeoutError, "Call to 'connect' timed out.")
-  else:
-    when defined(ssl):
-      if socket.isSSL:
-        socket.setBlocking(true)
-        doAssert socket.handshake()
-  socket.setBlocking(originalStatus)
-
-proc isSSL*(socket: Socket): bool = return socket.isSSL
-  ## Determines whether ``socket`` is a SSL socket.
-
-proc getFD*(socket: Socket): SocketHandle = return socket.fd
-  ## Returns the socket's file descriptor
-
-proc isBlocking*(socket: Socket): bool = not socket.nonblocking
-  ## Determines whether ``socket`` is blocking.
-
-when defined(Windows):
-  var wsa: WSAData
-  if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError())
-
-
diff --git a/lib/deprecated/pure/sums.nim b/lib/deprecated/pure/sums.nim
new file mode 100644
index 000000000..a6ce1b85d
--- /dev/null
+++ b/lib/deprecated/pure/sums.nim
@@ -0,0 +1,80 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2019 b3liever
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+
+## Accurate summation functions.
+
+{.deprecated: "use the nimble package `sums` instead.".}
+
+runnableExamples:
+  import std/math
+
+  template `~=`(x, y: float): bool = abs(x - y) < 1e-4
+
+  let
+    n = 1_000_000
+    first = 1e10
+    small = 0.1
+  var data = @[first]
+  for _ in 1 .. n:
+    data.add(small)
+
+  let result = first + small * n.float
+
+  doAssert abs(sum(data) - result) > 0.3
+  doAssert sumKbn(data) ~= result
+  doAssert sumPairs(data) ~= result
+
+## See also
+## ========
+## * `math module <math.html>`_ for a standard `sum proc <math.html#sum,openArray[T]>`_
+
+func sumKbn*[T](x: openArray[T]): T =
+  ## Kahan-Babuška-Neumaier summation: O(1) error growth, at the expense
+  ## of a considerable increase in computational cost.
+  ##
+  ## See:
+  ## * https://en.wikipedia.org/wiki/Kahan_summation_algorithm#Further_enhancements
+  if len(x) == 0: return
+  var sum = x[0]
+  var c = T(0)
+  for i in 1 ..< len(x):
+    let xi = x[i]
+    let t = sum + xi
+    if abs(sum) >= abs(xi):
+      c += (sum - t) + xi
+    else:
+      c += (xi - t) + sum
+    sum = t
+  result = sum + c
+
+func sumPairwise[T](x: openArray[T], i0, n: int): T =
+  if n < 128:
+    result = x[i0]
+    for i in i0 + 1 ..< i0 + n:
+      result += x[i]
+  else:
+    let n2 = n div 2
+    result = sumPairwise(x, i0, n2) + sumPairwise(x, i0 + n2, n - n2)
+
+func sumPairs*[T](x: openArray[T]): T =
+  ## Pairwise (cascade) summation of `x[i0:i0+n-1]`, with O(log n) error growth
+  ## (vs O(n) for a simple loop) with negligible performance cost if
+  ## the base case is large enough.
+  ##
+  ## See, e.g.:
+  ## * https://en.wikipedia.org/wiki/Pairwise_summation
+  ## * Higham, Nicholas J. (1993), "The accuracy of floating point
+  ##   summation", SIAM Journal on Scientific Computing 14 (4): 783–799.
+  ##
+  ## In fact, the root-mean-square error growth, assuming random roundoff
+  ## errors, is only O(sqrt(log n)), which is nearly indistinguishable from O(1)
+  ## in practice. See:
+  ## * Manfred Tasche and Hansmartin Zeuner, Handbook of
+  ##   Analytic-Computational Methods in Applied Mathematics (2000).
+  let n = len(x)
+  if n == 0: T(0) else: sumPairwise(x, 0, n)
diff --git a/lib/deps.txt b/lib/deps.txt
new file mode 100644
index 000000000..2a44b5fe2
--- /dev/null
+++ b/lib/deps.txt
@@ -0,0 +1,14 @@
+# Dragonbox.nim and Schubfach.nim
+
+"Dragonbox" is Nim's "float64 to string" algorithm.
+"Schubfach" is Nim's "float32 to string" algorithm. These are based on
+
+https://github.com/abolz/Drachennest/blob/master/src/dragonbox.cc
+https://github.com/abolz/Drachennest/blob/master/src/schubfach_32.cc
+
+commit e6714a39ad331b4489d0b6aaf3968635bff4eb5e
+
+The `.cc` files were translated by c2nim via `--cpp --keepBodies --nep1`
+and then modified to remove the unsafe code.
+
+We used c2nim as of commit f0469c909d9e2e28d59687e394bf5ac862f561b6.
diff --git a/lib/experimental/diff.nim b/lib/experimental/diff.nim
new file mode 100644
index 000000000..669e9f613
--- /dev/null
+++ b/lib/experimental/diff.nim
@@ -0,0 +1,339 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2018 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements an algorithm to compute the
+## `diff`:idx: between two sequences of lines.
+##
+## - To learn more see `Diff on Wikipedia. <http://wikipedia.org/wiki/Diff>`_
+
+runnableExamples:
+  assert diffInt(
+    [0, 1, 2, 3, 4, 5, 6, 7, 8],
+    [-1, 1, 2, 3, 4, 5, 666, 7, 42]) ==
+    @[Item(startA: 0, startB: 0, deletedA: 1, insertedB: 1),
+      Item(startA: 6, startB: 6, deletedA: 1, insertedB: 1),
+      Item(startA: 8, startB: 8, deletedA: 1, insertedB: 1)]
+
+runnableExamples:
+  # 2 samples of text (from "The Call of Cthulhu" by Lovecraft)
+  let txt0 = """
+abc
+def ghi
+jkl2"""
+  let txt1 = """
+bacx
+abc
+def ghi
+jkl"""
+  assert diffText(txt0, txt1) ==
+    @[Item(startA: 0, startB: 0, deletedA: 0, insertedB: 1),
+      Item(startA: 2, startB: 3, deletedA: 1, insertedB: 1)]
+
+# code owner: Arne Döring
+#
+# This is based on C# code written by Matthias Hertel, http://www.mathertel.de
+#
+# This Class implements the Difference Algorithm published in
+# "An O(ND) Difference Algorithm and its Variations" by Eugene Myers
+# Algorithmica Vol. 1 No. 2, 1986, p 251.
+
+import std/[tables, strutils]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+type
+  Item* = object    ## An Item in the list of differences.
+    startA*: int    ## Start Line number in Data A.
+    startB*: int    ## Start Line number in Data B.
+    deletedA*: int  ## Number of changes in Data A.
+    insertedB*: int ## Number of changes in Data B.
+
+  DiffData = object ## Data on one input file being compared.
+    data: seq[int] ## Buffer of numbers that will be compared.
+    modified: seq[bool] ## Array of booleans that flag for modified
+                        ## data. This is the result of the diff.
+                        ## This means deletedA in the first Data or
+                        ## inserted in the second Data.
+
+  Smsrd = object
+    x, y: int
+
+# template to avoid a seq copy. Required until `sink` parameters are ready.
+template newDiffData(initData: seq[int]; L: int): DiffData =
+  DiffData(
+    data: initData,
+    modified: newSeq[bool](L + 2)
+  )
+
+proc len(d: DiffData): int {.inline.} = d.data.len
+
+proc diffCodes(aText: string; h: var Table[string, int]): DiffData =
+  ## This function converts all textlines of the text into unique numbers for every unique textline
+  ## so further work can work only with simple numbers.
+  ## `aText` the input text
+  ## `h` This extern initialized hashtable is used for storing all ever used textlines.
+  ## `trimSpace` ignore leading and trailing space characters
+  ## Returns a array of integers.
+  var lastUsedCode = h.len
+  result.data = newSeq[int]()
+  for s in aText.splitLines:
+    if h.contains s:
+      result.data.add h[s]
+    else:
+      inc lastUsedCode
+      h[s] = lastUsedCode
+      result.data.add lastUsedCode
+  result.modified = newSeq[bool](result.data.len + 2)
+
+proc optimize(data: var DiffData) =
+  ## If a sequence of modified lines starts with a line that contains the same content
+  ## as the line that appends the changes, the difference sequence is modified so that the
+  ## appended line and not the starting line is marked as modified.
+  ## This leads to more readable diff sequences when comparing text files.
+  var startPos = 0
+  while startPos < data.len:
+    while startPos < data.len and not data.modified[startPos]:
+      inc startPos
+    var endPos = startPos
+    while endPos < data.len and data.modified[endPos]:
+      inc endPos
+
+    if endPos < data.len and data.data[startPos] == data.data[endPos]:
+      data.modified[startPos] = false
+      data.modified[endPos] = true
+    else:
+      startPos = endPos
+
+proc sms(dataA: var DiffData; lowerA, upperA: int; dataB: DiffData; lowerB, upperB: int;
+         downVector, upVector: var openArray[int]): Smsrd =
+  ## This is the algorithm to find the Shortest Middle Snake (sms).
+  ## `dataA` sequence A
+  ## `lowerA` lower bound of the actual range in dataA
+  ## `upperA` upper bound of the actual range in dataA (exclusive)
+  ## `dataB` sequence B
+  ## `lowerB` lower bound of the actual range in dataB
+  ## `upperB` upper bound of the actual range in dataB (exclusive)
+  ## `downVector` a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons.
+  ## `upVector` a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons.
+  ## Returns a MiddleSnakeData record containing x,y and u,v.
+
+  let max = dataA.len + dataB.len + 1
+
+  let downK = lowerA - lowerB # the k-line to start the forward search
+  let upK = upperA - upperB # the k-line to start the reverse search
+
+  let delta = (upperA - lowerA) - (upperB - lowerB)
+  let oddDelta = (delta and 1) != 0
+
+  # The vectors in the publication accepts negative indexes. the vectors implemented here are 0-based
+  # and are access using a specific offset: upOffset upVector and downOffset for downVector
+  let downOffset = max - downK
+  let upOffset = max - upK
+
+  let maxD = ((upperA - lowerA + upperB - lowerB) div 2) + 1
+
+  downVector[downOffset + downK + 1] = lowerA
+  upVector[upOffset + upK - 1] = upperA
+
+  for D in 0 .. maxD:
+    # Extend the forward path.
+    for k in countup(downK - D, downK + D, 2):
+      # find the only or better starting point
+      var x: int
+      if k == downK - D:
+        x = downVector[downOffset + k + 1] # down
+      else:
+        x = downVector[downOffset + k - 1] + 1 # a step to the right
+        if k < downK + D and downVector[downOffset + k + 1] >= x:
+          x = downVector[downOffset + k + 1] # down
+
+      var y = x - k
+
+      # find the end of the furthest reaching forward D-path in diagonal k.
+      while x < upperA and y < upperB and dataA.data[x] == dataB.data[y]:
+        inc x
+        inc y
+
+      downVector[downOffset + k] = x
+
+      # overlap ?
+      if oddDelta and upK - D < k and k < upK + D:
+        if upVector[upOffset + k] <= downVector[downOffset + k]:
+          return Smsrd(x: downVector[downOffset + k],
+                       y: downVector[downOffset + k] - k)
+
+    # Extend the reverse path.
+    for k in countup(upK - D, upK + D, 2):
+      # find the only or better starting point
+      var x: int
+      if k == upK + D:
+        x = upVector[upOffset + k - 1] # up
+      else:
+        x = upVector[upOffset + k + 1] - 1 # left
+        if k > upK - D and upVector[upOffset + k - 1] < x:
+          x = upVector[upOffset + k - 1] # up
+
+      var y = x - k
+      while x > lowerA and y > lowerB and dataA.data[x - 1] == dataB.data[y - 1]:
+        dec x
+        dec y
+
+      upVector[upOffset + k] = x
+
+      # overlap ?
+      if not oddDelta and downK-D <= k and k <= downK+D:
+        if upVector[upOffset + k] <= downVector[downOffset + k]:
+          return Smsrd(x: downVector[downOffset + k],
+                       y: downVector[downOffset + k] - k)
+
+  assert false, "the algorithm should never come here."
+
+proc lcs(dataA: var DiffData; lowerA, upperA: int; dataB: var DiffData; lowerB, upperB: int;
+         downVector, upVector: var openArray[int]) =
+  ## This is the divide-and-conquer implementation of the longes common-subsequence (lcs)
+  ## algorithm.
+  ## The published algorithm passes recursively parts of the A and B sequences.
+  ## To avoid copying these arrays the lower and upper bounds are passed while the sequences stay constant.
+  ## `dataA` sequence A
+  ## `lowerA` lower bound of the actual range in dataA
+  ## `upperA` upper bound of the actual range in dataA (exclusive)
+  ## `dataB` sequence B
+  ## `lowerB` lower bound of the actual range in dataB
+  ## `upperB` upper bound of the actual range in dataB (exclusive)
+  ## `downVector` a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons.
+  ## `upVector` a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons.
+
+  # make mutable copy:
+  var lowerA = lowerA
+  var lowerB = lowerB
+  var upperA = upperA
+  var upperB = upperB
+
+  # Fast walkthrough equal lines at the start
+  while lowerA < upperA and lowerB < upperB and dataA.data[lowerA] == dataB.data[lowerB]:
+    inc lowerA
+    inc lowerB
+
+  # Fast walkthrough equal lines at the end
+  while lowerA < upperA and lowerB < upperB and dataA.data[upperA - 1] == dataB.data[upperB - 1]:
+    dec upperA
+    dec upperB
+
+  if lowerA == upperA:
+    # mark as inserted lines.
+    while lowerB < upperB:
+      dataB.modified[lowerB] = true
+      inc lowerB
+
+  elif lowerB == upperB:
+    # mark as deleted lines.
+    while lowerA < upperA:
+      dataA.modified[lowerA] = true
+      inc lowerA
+
+  else:
+    # Find the middle snake and length of an optimal path for A and B
+    let smsrd = sms(dataA, lowerA, upperA, dataB, lowerB, upperB, downVector, upVector)
+    # Debug.Write(2, "MiddleSnakeData", String.Format("{0},{1}", smsrd.x, smsrd.y))
+
+    # The path is from LowerX to (x,y) and (x,y) to UpperX
+    lcs(dataA, lowerA, smsrd.x, dataB, lowerB, smsrd.y, downVector, upVector)
+    lcs(dataA, smsrd.x, upperA, dataB, smsrd.y, upperB, downVector, upVector)  # 2002.09.20: no need for 2 points
+
+proc createDiffs(dataA, dataB: DiffData): seq[Item] =
+  ## Scan the tables of which lines are inserted and deleted,
+  ## producing an edit script in forward order.
+  var startA = 0
+  var startB = 0
+  var lineA = 0
+  var lineB = 0
+  while lineA < dataA.len or lineB < dataB.len:
+    if lineA < dataA.len and not dataA.modified[lineA] and
+       lineB < dataB.len and not dataB.modified[lineB]:
+      # equal lines
+      inc lineA
+      inc lineB
+    else:
+      # maybe deleted and/or inserted lines
+      startA = lineA
+      startB = lineB
+
+      while lineA < dataA.len and (lineB >= dataB.len or dataA.modified[lineA]):
+        inc lineA
+
+      while lineB < dataB.len and (lineA >= dataA.len or dataB.modified[lineB]):
+        inc lineB
+
+      if (startA < lineA) or (startB < lineB):
+        result.add Item(startA: startA,
+                        startB: startB,
+                        deletedA: lineA - startA,
+                        insertedB: lineB - startB)
+
+
+proc diffInt*(arrayA, arrayB: openArray[int]): seq[Item] =
+  ## Find the difference in 2 arrays of integers.
+  ##
+  ## `arrayA` A-version of the numbers (usually the old one)
+  ##
+  ## `arrayB` B-version of the numbers (usually the new one)
+  ##
+  ## Returns a sequence of Items that describe the differences.
+
+  # The A-Version of the data (original data) to be compared.
+  var dataA = newDiffData(@arrayA, arrayA.len)
+
+  # The B-Version of the data (modified data) to be compared.
+  var dataB = newDiffData(@arrayB, arrayB.len)
+
+  let max = dataA.len + dataB.len + 1
+  # vector for the (0,0) to (x,y) search
+  var downVector = newSeq[int](2 * max + 2)
+  # vector for the (u,v) to (N,M) search
+  var upVector = newSeq[int](2 * max + 2)
+
+  lcs(dataA, 0, dataA.len, dataB, 0, dataB.len, downVector, upVector)
+  result = createDiffs(dataA, dataB)
+
+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 duplicates in there, and generating a
+  ## new number each time a new textline is inserted.
+  ##
+  ## `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.
+  # See also `gitutils.diffStrings`.
+  # prepare the input-text and convert to comparable numbers.
+  var h = initTable[string, int]()  # TextA.len + TextB.len  <- probably wrong initial size
+  # The A-Version of the data (original data) to be compared.
+  var dataA = diffCodes(textA, h)
+
+  # The B-Version of the data (modified data) to be compared.
+  var dataB = diffCodes(textB, h)
+
+  h.clear # free up hashtable memory (maybe)
+
+  let max = dataA.len + dataB.len + 1
+  # vector for the (0,0) to (x,y) search
+  var downVector = newSeq[int](2 * max + 2)
+  # vector for the (u,v) to (N,M) search
+  var upVector = newSeq[int](2 * max + 2)
+
+  lcs(dataA, 0, dataA.len, dataB, 0, dataB.len, downVector, upVector)
+
+  optimize(dataA)
+  optimize(dataB)
+  result = createDiffs(dataA, dataB)
diff --git a/lib/genode/alloc.nim b/lib/genode/alloc.nim
index 52dc1c32c..24fb9954e 100644
--- a/lib/genode/alloc.nim
+++ b/lib/genode/alloc.nim
@@ -15,20 +15,20 @@ when not defined(genode):
   {.error: "Genode only module".}
 
 when not declared(GenodeEnv):
-  include genode/env
+  import genode/env
 
-type DataspaceCapability {.
-  importcpp: "Genode::Dataspace_capability", pure.} = object
+type RamDataspaceCapability {.
+  importcpp: "Genode::Ram_dataspace_capability", pure.} = object
 
 type
   Map = object
     attachment: pointer
     size: int
-    ds: DataspaceCapability
+    ds: RamDataspaceCapability
 
   SlabMeta = object
     next: ptr MapSlab
-    ds: DataspaceCapability
+    ds: RamDataspaceCapability
 
   MapSlab = object
     meta: SlabMeta
@@ -45,11 +45,11 @@ proc capsAvail(env: GenodeEnv): int {.
   ## Return the number of available capabilities.
   ## Each dataspace allocation consumes a capability.
 
-proc allocDataspace(env: GenodeEnv; size: int): DataspaceCapability {.
+proc allocDataspace(env: GenodeEnv; size: int): RamDataspaceCapability {.
   importcpp: "#->pd().alloc(@)".}
   ## Allocate a dataspace and its capability.
 
-proc attachDataspace(env: GenodeEnv; ds: DataspaceCapability): pointer {.
+proc attachDataspace(env: GenodeEnv; ds: RamDataspaceCapability): pointer {.
   importcpp: "#->rm().attach(@)".}
   ## Attach a dataspace into the component address-space.
 
@@ -57,7 +57,7 @@ proc detachAddress(env: GenodeEnv; p: pointer) {.
   importcpp: "#->rm().detach(@)".}
   ## Detach a dataspace from the component address-space.
 
-proc freeDataspace(env: GenodeEnv; ds: DataspaceCapability) {.
+proc freeDataspace(env: GenodeEnv; ds: RamDataspaceCapability) {.
   importcpp: "#->pd().free(@)".}
   ## Free a dataspace.
 
@@ -70,7 +70,7 @@ proc newMapSlab(): ptr MapSlab =
 
 iterator items(s: ptr MapSlab): ptr Map =
   let mapCount = (SlabBackendSize - sizeof(SlabMeta)) div sizeof(Map)
-  for i in 0 .. <mapCount:
+  for i in 0 ..< mapCount:
     yield s.maps[i].addr
 
 var slabs: ptr MapSlab
@@ -111,7 +111,7 @@ proc osDeallocPages(p: pointer; size: int) =
       if m.attachment == p:
         if m.size != size:
           echo "cannot partially detach dataspace"
-          quit -1
+          rawQuit -1
         runtimeEnv.detachAddress m.attachment
         runtimeEnv.freeDataspace m.ds
         m[] = Map()
diff --git a/lib/genode/constructibles.nim b/lib/genode/constructibles.nim
new file mode 100644
index 000000000..3a4a646e0
--- /dev/null
+++ b/lib/genode/constructibles.nim
@@ -0,0 +1,21 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2022 Emery Hemingway
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+type Constructible*[T] {.
+  importcpp: "Genode::Constructible",
+  header: "<util/reconstructible.h>", byref, pure.} = object
+
+proc construct*[T](x: Constructible[T]) {.importcpp.}
+  ## Construct a constructible C++ object.
+
+proc destruct*[T](x: Constructible[T]) {.importcpp.}
+  ## Destruct a constructible C++ object.
+
+proc constructed*[T](x: Constructible[T]): bool {.importcpp.}
+  ## Test if an object is constructed.
diff --git a/lib/genode/entrypoints.nim b/lib/genode/entrypoints.nim
new file mode 100644
index 000000000..0bf5e0e0e
--- /dev/null
+++ b/lib/genode/entrypoints.nim
@@ -0,0 +1,22 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2022 Emery Hemingway
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## See `Genode Foundations - Entrypoint <https://genode.org/documentation/genode-foundations/21.05/functional_specification/Entrypoint.html>`
+## for a description of Entrypoints.
+
+type
+  EntrypointObj {.
+    importcpp: "Genode::Entrypoint",
+    header: "<base/entrypoint.h>",
+    pure.} = object
+  Entrypoint* = ptr EntrypointObj
+    ## Opaque Entrypoint object.
+
+proc ep*(env: GenodeEnv): Entrypoint {.importcpp: "(&#->ep())".}
+  ## Access the entrypoint associated with `env`.
diff --git a/lib/genode/env.nim b/lib/genode/env.nim
index 2b180d1b3..babe2a8a0 100644
--- a/lib/genode/env.nim
+++ b/lib/genode/env.nim
@@ -11,19 +11,19 @@
 # This file contains the minimum required definitions
 # for interacting with the initial Genode environment.
 # It is reserved for use only within the standard
-# library. See ``componentConstructHook`` in the system
+# library. See `componentConstructHook` in the system
 # module for accessing the Genode environment after the
 # standard library has finished initializating.
 #
 
 when not defined(genode):
-  {.error: "Genode only include".}
+  {.error: "Genode only module".}
 
 type
-  GenodeEnvObj {.importcpp: "Genode::Env", header: "<base/env.h>", pure.} = object
-  GenodeEnvPtr = ptr GenodeEnvObj
+  GenodeEnvObj* {.importcpp: "Genode::Env", header: "<base/env.h>", pure.} = object
+  GenodeEnvPtr* = ptr GenodeEnvObj
 
-const runtimeEnvSym = "nim_runtime_env"
+const runtimeEnvSym* = "nim_runtime_env"
 
 when not defined(nimscript):
-  var runtimeEnv {.importcpp: runtimeEnvSym.}: GenodeEnvPtr
+  var runtimeEnv* {.importcpp: runtimeEnvSym.}: GenodeEnvPtr
diff --git a/lib/genode/signals.nim b/lib/genode/signals.nim
new file mode 100644
index 000000000..7d1875730
--- /dev/null
+++ b/lib/genode/signals.nim
@@ -0,0 +1,77 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2022 Emery Hemingway
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## See `Genode Foundations - Asynchronous notifications <https://genode.org/documentation/genode-foundations/21.05/architecture/Inter-component_communication.html#Asynchronous_notifications>`
+## for a description of Genode signals.
+
+when not defined(genode) or defined(nimdoc):
+  {.error: "Genode only module".}
+
+import ./entrypoints, ./constructibles
+
+export ep # Entrypoint accessor on GenodeEnv
+
+type
+  SignalContextCapability* {.
+    importcpp: "Genode::Signal_context_capability",
+    header: "<base/signal.h>", pure.} = object
+    ## Capability to an asynchronous signal context.
+
+proc isValid*(cap: SignalContextCapability): bool {.importcpp: "#.valid()".}
+  ## Call the Genode core to check if this `SignalContextCapability` is valid.
+  # TODO: RpcEffect
+
+type
+  HandlerProc = proc () {.closure, gcsafe.}
+
+  SignalHandlerBase {.
+    importcpp: "Nim::SignalHandler",
+    header: "genode_cpp/signals.h",
+    pure.} = object
+
+  SignalHandlerCpp = Constructible[SignalHandlerBase]
+
+  SignalHandlerObj = object
+    cpp: SignalHandlerCpp
+    cb: HandlerProc
+      ## Signal handling procedure called during dispatch.
+
+  SignalHandler* = ref SignalHandlerObj
+    ## Nim object enclosing a Genode signal handler.
+
+proc construct(cpp: SignalHandlerCpp; ep: Entrypoint; sh: SignalHandler) {.importcpp.}
+
+proc cap(cpp: SignalHandlerCpp): SignalContextCapability {.importcpp: "#->cap()".}
+
+proc newSignalHandler*(ep: Entrypoint; cb: HandlerProc): SignalHandler =
+  ## Create a new signal handler. A label is recommended for
+  ## debugging purposes. A signal handler will not be garbage
+  ## collected until after it has been dissolved.
+  result = SignalHandler(cb: cb)
+  result.cpp.construct(ep, result)
+  GCref result
+
+proc dissolve*(sig: SignalHandler) =
+  ## Dissolve signal dispatcher from entrypoint.
+  # TODO: =destroy?
+  destruct sig.cpp
+  sig.cb = nil # lose the callback
+  GCunref sig
+
+proc cap*(sig: SignalHandler): SignalContextCapability =
+  ## Signal context capability. Can be delegated to external components.
+  sig.cpp.cap
+
+proc submit*(cap: SignalContextCapability) {.
+    importcpp: "Genode::Signal_transmitter(#).submit()".}
+  ## Submit a signal to a context capability.
+
+proc nimHandleSignal(p: pointer) {.exportc.} =
+  ## C symbol invoked by entrypoint during signal dispatch.
+  cast[SignalHandler](p).cb()
diff --git a/lib/genode_cpp/signals.h b/lib/genode_cpp/signals.h
new file mode 100644
index 000000000..fa3975d38
--- /dev/null
+++ b/lib/genode_cpp/signals.h
@@ -0,0 +1,39 @@
+/*
+ *
+ *           Nim's Runtime Library
+ *       (c) Copyright 2022 Emery Hemingway
+ *
+ *   See the file "copying.txt", included in this
+ *   distribution, for details about the copyright.
+ *
+ */
+
+#ifndef _NIM_SIGNALS_H_
+#define _NIM_SIGNALS_H_
+
+#include <libc/component.h>
+#include <base/signal.h>
+#include <util/reconstructible.h>
+
+// Symbol for calling back into Nim
+extern "C" void nimHandleSignal(void *arg);
+
+namespace Nim { struct SignalHandler; }
+
+struct Nim::SignalHandler
+{
+	// Pointer to the Nim handler object.
+	void *arg;
+
+	void handle_signal() {
+		Libc::with_libc([this] () { nimHandleSignal(arg); }); }
+
+	Genode::Signal_handler<SignalHandler> handler;
+
+	SignalHandler(Genode::Entrypoint *ep, void *arg)
+	: arg(arg), handler(*ep, *this, &SignalHandler::handle_signal) { }
+
+	Genode::Signal_context_capability cap() { return handler; }
+};
+
+#endif
diff --git a/lib/genode_cpp/syslocks.h b/lib/genode_cpp/syslocks.h
index 8ba39abc2..b5d5ae694 100644
--- a/lib/genode_cpp/syslocks.h
+++ b/lib/genode_cpp/syslocks.h
@@ -13,7 +13,7 @@
 
 /* Genode includes */
 #include <base/semaphore.h>
-#include <base/lock.h>
+#include <base/mutex.h>
 
 namespace Nim {
 	struct SysLock;
@@ -22,15 +22,14 @@ namespace Nim {
 
 struct Nim::SysLock
 {
-	Genode::Lock _lock_a, _lock_b;
+	Genode::Mutex _mutex_a, _mutex_b;
 	bool         _locked;
 
 	void acquireSys()
 	{
-		_lock_a.lock();
+		Genode::Mutex::Guard guard(_mutex_a);
 		_locked = true;
-		_lock_a.unlock();
-		_lock_b.lock();
+		_mutex_b.acquire();
 	}
 
 	bool tryAcquireSys()
@@ -38,23 +37,22 @@ struct Nim::SysLock
 		if (_locked)
 			return false;
 
-		_lock_a.lock();
+		Genode::Mutex::Guard guard(_mutex_a);
+
 		if (_locked) {
-			_lock_a.unlock();
 			return false;
 		} else {
 			_locked = true;
-			_lock_b.lock();
-			_lock_a.unlock();
+			_mutex_b.acquire();
 			return true;
 		}
 	}
 
 	void releaseSys()
 	{
+		Genode::Mutex::Guard guard(_mutex_a);
 		_locked = false;
-		_lock_a.unlock();
-		_lock_b.unlock();
+		_mutex_b.release();
 	}
 };
 
@@ -73,6 +71,11 @@ struct Nim::SysCond
 	{
 		_semaphore.up();
 	}
+
+	void broadcastSysCond()
+	{
+		_semaphore.up();
+	}
 };
 
 #endif
diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim
deleted file mode 100644
index ca0e29d11..000000000
--- a/lib/impure/db_mysql.nim
+++ /dev/null
@@ -1,419 +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.
-#
-
-## A higher level `mySQL`:idx: database wrapper. The same interface is
-## implemented for other databases too.
-##
-## See also: `db_odbc <db_odbc.html>`_, `db_sqlite <db_sqlite.html>`_,
-## `db_postgres <db_postgres.html>`_.
-##
-## Parameter substitution
-## ----------------------
-##
-## All ``db_*`` modules support the same form of parameter substitution.
-## That is, using the ``?`` (question mark) to signify the place where a
-## value should be placed. For example:
-##
-## .. code-block:: Nim
-##     sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
-##
-##
-## Examples
-## --------
-##
-## Opening a connection to a database
-## ==================================
-##
-## .. code-block:: Nim
-##     import db_mysql
-##     let db = open("localhost", "user", "password", "dbname")
-##     db.close()
-##
-## Creating a table
-## ================
-##
-## .. code-block:: Nim
-##      db.exec(sql"DROP TABLE IF EXISTS myTable")
-##      db.exec(sql("""CREATE TABLE myTable (
-##                       id integer,
-##                       name varchar(50) not null)"""))
-##
-## Inserting data
-## ==============
-##
-## .. code-block:: Nim
-##     db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
-##             "Dominik")
-##
-## Larger example
-## ==============
-##
-## .. code-block:: Nim
-##
-##  import db_mysql, math
-##
-##  let theDb = open("localhost", "nim", "nim", "test")
-##
-##  theDb.exec(sql"Drop table if exists myTestTbl")
-##  theDb.exec(sql("create table myTestTbl (" &
-##      " Id    INT(11)     NOT NULL AUTO_INCREMENT PRIMARY KEY, " &
-##      " Name  VARCHAR(50) NOT NULL, " &
-##      " i     INT(11), " &
-##      " f     DECIMAL(18,10))"))
-##
-##  theDb.exec(sql"START TRANSACTION")
-##  for i in 1..1000:
-##    theDb.exec(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
-##          "Item#" & $i, i, sqrt(i.float))
-##  theDb.exec(sql"COMMIT")
-##
-##  for x in theDb.fastRows(sql"select * from myTestTbl"):
-##    echo x
-##
-##  let id = theDb.tryInsertId(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
-##          "Item#1001", 1001, sqrt(1001.0))
-##  echo "Inserted item: ", theDb.getValue(sql"SELECT name FROM myTestTbl WHERE id=?", id)
-##
-##  theDb.close()
-
-
-import strutils, mysql
-
-import db_common
-export db_common
-
-type
-  DbConn* = distinct PMySQL ## encapsulates a database connection
-  Row* = seq[string]   ## a row of a dataset. NULL database values will be
-                       ## converted to nil.
-  InstantRow* = object ## a handle that can be used to get a row's
-                       ## column text on demand
-    row: cstringArray
-    len: int
-{.deprecated: [TRow: Row, TDbConn: DbConn].}
-
-proc dbError*(db: DbConn) {.noreturn.} =
-  ## raises a DbError exception.
-  var e: ref DbError
-  new(e)
-  e.msg = $mysql.error(PMySQL db)
-  raise e
-
-when false:
-  proc dbQueryOpt*(db: DbConn, query: string, args: varargs[string, `$`]) =
-    var stmt = mysql_stmt_init(db)
-    if stmt == nil: dbError(db)
-    if mysql_stmt_prepare(stmt, query, len(query)) != 0:
-      dbError(db)
-    var
-      binding: seq[MYSQL_BIND]
-    discard mysql_stmt_close(stmt)
-
-proc dbQuote*(s: string): string =
-  ## DB quotes the string.
-  result = "'"
-  for c in items(s):
-    if c == '\'': add(result, "''")
-    else: add(result, c)
-  add(result, '\'')
-
-proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
-  result = ""
-  var a = 0
-  for c in items(string(formatstr)):
-    if c == '?':
-      if args[a].isNil:
-        add(result, "NULL")
-      else:
-        add(result, dbQuote(args[a]))
-      inc(a)
-    else:
-      add(result, c)
-
-proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
-  tags: [ReadDbEffect, WriteDbEffect].} =
-  ## tries to execute the query and returns true if successful, false otherwise.
-  var q = dbFormat(query, args)
-  return mysql.realQuery(PMySQL db, q, q.len) == 0'i32
-
-proc rawExec(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) =
-  var q = dbFormat(query, args)
-  if mysql.realQuery(PMySQL db, q, q.len) != 0'i32: dbError(db)
-
-proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
-  tags: [ReadDbEffect, WriteDbEffect].} =
-  ## executes the query and raises EDB if not successful.
-  var q = dbFormat(query, args)
-  if mysql.realQuery(PMySQL db, q, q.len) != 0'i32: dbError(db)
-
-proc newRow(L: int): Row =
-  newSeq(result, L)
-  for i in 0..L-1: result[i] = ""
-
-proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
-  if row != nil:
-    while mysql.fetchRow(sqlres) != nil: discard
-  mysql.freeResult(sqlres)
-
-iterator fastRows*(db: DbConn, query: SqlQuery,
-                   args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## executes the query and iterates over the result dataset.
-  ##
-  ## This is very fast, but potentially dangerous.  Use this iterator only
-  ## if you require **ALL** the rows.
-  ##
-  ## Breaking the fastRows() iterator during a loop will cause the next
-  ## database query to raise an [EDb] exception ``Commands out of sync``.
-  rawExec(db, query, args)
-  var sqlres = mysql.useResult(PMySQL db)
-  if sqlres != nil:
-    var
-      L = int(mysql.numFields(sqlres))
-      row: cstringArray
-      result: Row
-      backup: Row
-    newSeq(result, L)
-    while true:
-      row = mysql.fetchRow(sqlres)
-      if row == nil: break
-      for i in 0..L-1:
-        if row[i] == nil:
-          if backup == nil:
-            newSeq(backup, L)
-          if backup[i] == nil and result[i] != nil:
-            shallowCopy(backup[i], result[i])
-          result[i] = nil
-        else:
-          if result[i] == nil:
-            if backup != nil:
-              if backup[i] == nil:
-                backup[i] = ""
-              shallowCopy(result[i], backup[i])
-              setLen(result[i], 0)
-            else:
-              result[i] = ""
-          else:
-            setLen(result[i], 0)
-          add(result[i], row[i])
-      yield result
-    properFreeResult(sqlres, row)
-
-iterator instantRows*(db: DbConn, query: SqlQuery,
-                      args: varargs[string, `$`]): InstantRow
-                      {.tags: [ReadDbEffect].} =
-  ## 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.
-  rawExec(db, query, args)
-  var sqlres = mysql.useResult(PMySQL db)
-  if sqlres != nil:
-    let L = int(mysql.numFields(sqlres))
-    var row: cstringArray
-    while true:
-      row = mysql.fetchRow(sqlres)
-      if row == nil: break
-      yield InstantRow(row: row, len: L)
-    properFreeResult(sqlres, row)
-
-proc setTypeName(t: var DbType; f: PFIELD) =
-  shallowCopy(t.name, $f.name)
-  t.maxReprLen = Natural(f.max_length)
-  if (NOT_NULL_FLAG and f.flags) != 0: t.notNull = true
-  case f.ftype
-  of TYPE_DECIMAL:
-    t.kind = dbDecimal
-  of TYPE_TINY:
-    t.kind = dbInt
-    t.size = 1
-  of TYPE_SHORT:
-    t.kind = dbInt
-    t.size = 2
-  of TYPE_LONG:
-    t.kind = dbInt
-    t.size = 4
-  of TYPE_FLOAT:
-    t.kind = dbFloat
-    t.size = 4
-  of TYPE_DOUBLE:
-    t.kind = dbFloat
-    t.size = 8
-  of TYPE_NULL:
-    t.kind = dbNull
-  of TYPE_TIMESTAMP:
-    t.kind = dbTimestamp
-  of TYPE_LONGLONG:
-    t.kind = dbInt
-    t.size = 8
-  of TYPE_INT24:
-    t.kind = dbInt
-    t.size = 3
-  of TYPE_DATE:
-    t.kind = dbDate
-  of TYPE_TIME:
-    t.kind = dbTime
-  of TYPE_DATETIME:
-    t.kind = dbDatetime
-  of TYPE_YEAR:
-    t.kind = dbDate
-  of TYPE_NEWDATE:
-    t.kind = dbDate
-  of TYPE_VARCHAR, TYPE_VAR_STRING, TYPE_STRING:
-    t.kind = dbVarchar
-  of TYPE_BIT:
-    t.kind = dbBit
-  of TYPE_NEWDECIMAL:
-    t.kind = dbDecimal
-  of TYPE_ENUM: t.kind = dbEnum
-  of TYPE_SET: t.kind = dbSet
-  of TYPE_TINY_BLOB, TYPE_MEDIUM_BLOB, TYPE_LONG_BLOB,
-     TYPE_BLOB: t.kind = dbBlob
-  of TYPE_GEOMETRY:
-    t.kind = dbGeometry
-
-proc setColumnInfo(columns: var DbColumns; res: PRES; L: int) =
-  setLen(columns, L)
-  for i in 0..<L:
-    let fp = mysql.fetch_field_direct(res, cint(i))
-    setTypeName(columns[i].typ, fp)
-    columns[i].name = $fp.name
-    columns[i].tableName = $fp.table
-    columns[i].primaryKey = (fp.flags and PRI_KEY_FLAG) != 0
-    #columns[i].foreignKey = there is no such thing in mysql
-
-iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery;
-                      args: varargs[string, `$`]): InstantRow =
-  ## 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.
-  rawExec(db, query, args)
-  var sqlres = mysql.useResult(PMySQL db)
-  if sqlres != nil:
-    let L = int(mysql.numFields(sqlres))
-    setColumnInfo(columns, sqlres, L)
-    var row: cstringArray
-    while true:
-      row = mysql.fetchRow(sqlres)
-      if row == nil: break
-      yield InstantRow(row: row, len: L)
-    properFreeResult(sqlres, row)
-
-
-proc `[]`*(row: InstantRow, col: int): string {.inline.} =
-  ## Returns text for given column of the row.
-  $row.row[col]
-
-proc len*(row: InstantRow): int {.inline.} =
-  ## Returns number of columns in the row.
-  row.len
-
-proc getRow*(db: DbConn, query: SqlQuery,
-             args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## Retrieves a single row. If the query doesn't return any rows, this proc
-  ## will return a Row with empty strings for each column.
-  rawExec(db, query, args)
-  var sqlres = mysql.useResult(PMySQL db)
-  if sqlres != nil:
-    var L = int(mysql.numFields(sqlres))
-    result = newRow(L)
-    var row = mysql.fetchRow(sqlres)
-    if row != nil:
-      for i in 0..L-1:
-        setLen(result[i], 0)
-        if row[i] == nil:
-          result[i] = nil
-        else:
-          add(result[i], row[i])
-    properFreeResult(sqlres, row)
-
-proc getAllRows*(db: DbConn, query: SqlQuery,
-                 args: varargs[string, `$`]): seq[Row] {.tags: [ReadDbEffect].} =
-  ## executes the query and returns the whole result dataset.
-  result = @[]
-  rawExec(db, query, args)
-  var sqlres = mysql.useResult(PMySQL db)
-  if sqlres != nil:
-    var L = int(mysql.numFields(sqlres))
-    var row: cstringArray
-    var j = 0
-    while true:
-      row = mysql.fetchRow(sqlres)
-      if row == nil: break
-      setLen(result, j+1)
-      newSeq(result[j], L)
-      for i in 0..L-1:
-        if row[i] == nil:
-          result[j][i] = nil
-        else:
-          result[j][i] = $row[i]
-      inc(j)
-    mysql.freeResult(sqlres)
-
-iterator rows*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## same as `fastRows`, but slower and safe.
-  for r in items(getAllRows(db, query, args)): yield r
-
-proc getValue*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): string {.tags: [ReadDbEffect].} =
-  ## executes the query and returns the first column of the first row of the
-  ## result dataset. Returns "" if the dataset contains no rows or the database
-  ## value is NULL.
-  result = getRow(db, query, args)[0]
-
-proc tryInsertId*(db: DbConn, query: SqlQuery,
-                  args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
-  ## executes the query (typically "INSERT") and returns the
-  ## generated ID for the row or -1 in case of an error.
-  var q = dbFormat(query, args)
-  if mysql.realQuery(PMySQL db, q, q.len) != 0'i32:
-    result = -1'i64
-  else:
-    result = mysql.insertId(PMySQL db)
-
-proc insertId*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
-  ## executes the query (typically "INSERT") and returns the
-  ## generated ID for the row.
-  result = tryInsertID(db, query, args)
-  if result < 0: dbError(db)
-
-proc execAffectedRows*(db: DbConn, query: SqlQuery,
-                       args: varargs[string, `$`]): int64 {.
-                       tags: [ReadDbEffect, WriteDbEffect].} =
-  ## runs the query (typically "UPDATE") and returns the
-  ## number of affected rows
-  rawExec(db, query, args)
-  result = mysql.affectedRows(PMySQL db)
-
-proc close*(db: DbConn) {.tags: [DbEffect].} =
-  ## closes the database connection.
-  if PMySQL(db) != nil: mysql.close(PMySQL db)
-
-proc open*(connection, user, password, database: string): DbConn {.
-  tags: [DbEffect].} =
-  ## opens a database connection. Raises `EDb` if the connection could not
-  ## be established.
-  var res = mysql.init(nil)
-  if res == nil: dbError("could not open database connection")
-  let
-    colonPos = connection.find(':')
-    host = if colonPos < 0: connection
-           else: substr(connection, 0, colonPos-1)
-    port: int32 = if colonPos < 0: 0'i32
-                  else: substr(connection, colonPos+1).parseInt.int32
-  if mysql.realConnect(res, host, user, password, database,
-                       port, nil, 0) == nil:
-    var errmsg = $mysql.error(res)
-    mysql.close(res)
-    dbError(errmsg)
-  result = DbConn(res)
-
-proc setEncoding*(connection: DbConn, encoding: string): bool {.
-  tags: [DbEffect].} =
-  ## sets the encoding of a database connection, returns true for
-  ## success, false for failure.
-  result = mysql.set_character_set(PMySQL connection, encoding) == 0
diff --git a/lib/impure/db_odbc.nim b/lib/impure/db_odbc.nim
deleted file mode 100644
index d6343acc7..000000000
--- a/lib/impure/db_odbc.nim
+++ /dev/null
@@ -1,546 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2015 Nim Contributors
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## A higher level `ODBC` database wrapper.
-##
-## This is the same interface that is implemented for other databases.
-##
-## This has NOT yet been (extensively) tested against ODBC drivers for
-## Teradata, Oracle, Sybase, MSSqlvSvr, et. al.  databases.
-##
-## Currently all queries are ANSI calls, not Unicode.
-##
-## See also: `db_postgres <db_postgres.html>`_, `db_sqlite <db_sqlite.html>`_,
-## `db_mysql <db_mysql.html>`_.
-##
-## Parameter substitution
-## ----------------------
-##
-## All ``db_*`` modules support the same form of parameter substitution.
-## That is, using the ``?`` (question mark) to signify the place where a
-## value should be placed. For example:
-##
-## .. code-block:: Nim
-##     sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
-##
-##
-## Examples
-## --------
-##
-## Opening a connection to a database
-## ==================================
-##
-## .. code-block:: Nim
-##     import db_odbc
-##     var db = open("localhost", "user", "password", "dbname")
-##     db.close()
-##
-## Creating a table
-## ================
-##
-## .. code-block:: Nim
-##      db.exec(sql"DROP TABLE IF EXISTS myTable")
-##      db.exec(sql("""CREATE TABLE myTable (
-##                       id integer,
-##                       name varchar(50) not null)"""))
-##
-## Inserting data
-## ==============
-##
-## .. code-block:: Nim
-##     db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
-##             "Andreas")
-##
-## Large example
-## =============
-##
-## .. code-block:: Nim
-##
-##  import db_odbc, math
-##
-##  var theDb = open("localhost", "nim", "nim", "test")
-##
-##  theDb.exec(sql"Drop table if exists myTestTbl")
-##  theDb.exec(sql("create table myTestTbl (" &
-##      " Id    INT(11)     NOT NULL AUTO_INCREMENT PRIMARY KEY, " &
-##      " Name  VARCHAR(50) NOT NULL, " &
-##      " i     INT(11), " &
-##      " f     DECIMAL(18,10))"))
-##
-##  theDb.exec(sql"START TRANSACTION")
-##  for i in 1..1000:
-##    theDb.exec(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
-##          "Item#" & $i, i, sqrt(i.float))
-##  theDb.exec(sql"COMMIT")
-##
-##  for x in theDb.fastRows(sql"select * from myTestTbl"):
-##    echo x
-##
-##  let id = theDb.tryInsertId(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
-##          "Item#1001", 1001, sqrt(1001.0))
-##  echo "Inserted item: ", theDb.getValue(sql"SELECT name FROM myTestTbl WHERE id=?", id)
-##
-##  theDb.close()
-
-import strutils, odbcsql
-import db_common
-export db_common
-
-type
-  OdbcConnTyp = tuple[hDb: SqlHDBC, env: SqlHEnv, stmt: SqlHStmt]
-  DbConn* = OdbcConnTyp    ## encapsulates a database connection
-  Row* = seq[string]   ## a row of a dataset. NULL database values will be
-                       ## converted to nil.
-  InstantRow* = tuple[row: seq[string], len: int]  ## a handle that can be
-                                                    ## used to get a row's
-                                                    ## column text on demand
-
-{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
-
-var
-  buf: array[0..4096, char]
-
-proc properFreeResult(hType: int, sqlres: var SqlHandle) {.
-          tags: [WriteDbEffect], raises: [].} =
-  try:
-    discard SQLFreeHandle(hType.TSqlSmallInt, sqlres)
-    sqlres = nil
-  except: discard
-
-proc getErrInfo(db: var DbConn): tuple[res: int, ss, ne, msg: string] {.
-          tags: [ReadDbEffect], raises: [].} =
-  ## Returns ODBC error information
-  var
-    sqlState: array[0..512, char]
-    nativeErr: array[0..512, char]
-    errMsg: array[0..512, char]
-    retSz: TSqlSmallInt = 0
-    res: TSqlSmallInt = 0
-  try:
-    sqlState[0] = '\0'
-    nativeErr[0] = '\0'
-    errMsg[0] = '\0'
-    res = SQLErr(db.env, db.hDb, db.stmt,
-              cast[PSQLCHAR](sqlState.addr),
-              cast[PSQLCHAR](nativeErr.addr),
-              cast[PSQLCHAR](errMsg.addr),
-              511.TSqlSmallInt, retSz.addr.PSQLSMALLINT)
-  except:
-    discard
-  return (res.int, $sqlState, $nativeErr, $errMsg)
-
-proc dbError*(db: var DbConn) {.
-          tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} =
-  ## Raises an `[DbError]` exception with ODBC error information
-  var
-    e: ref DbError
-    ss, ne, msg: string = ""
-    isAnError = false
-    res: int = 0
-    prevSs = ""
-  while true:
-    prevSs = ss
-    (res, ss, ne, msg) = db.getErrInfo()
-    if prevSs == ss:
-      break
-    # sqlState of 00000 is not an error
-    elif ss == "00000":
-      break
-    elif ss == "01000":
-      echo "\nWarning: ", ss, " ", msg
-      continue
-    else:
-      isAnError = true
-      echo "\nError: ", ss, " ", msg
-  if isAnError:
-    new(e)
-    e.msg = "ODBC Error"
-    if db.stmt != nil:
-      properFreeResult(SQL_HANDLE_STMT, db.stmt)
-    properFreeResult(SQL_HANDLE_DBC, db.hDb)
-    properFreeResult(SQL_HANDLE_ENV, db.env)
-    raise e
-
-proc sqlCheck(db: var DbConn, resVal: TSqlSmallInt) {.raises: [DbError]} =
-  ## Wrapper that raises [EDb] if ``resVal`` is neither SQL_SUCCESS or SQL_NO_DATA
-  if resVal notIn [SQL_SUCCESS, SQL_NO_DATA]: dbError(db)
-
-proc sqlGetDBMS(db: var DbConn): string {.
-        tags: [ReadDbEffect, WriteDbEffect], raises: [] .} =
-  ## Returns the ODBC SQL_DBMS_NAME string
-  const
-    SQL_DBMS_NAME = 17.SqlUSmallInt
-  var
-    sz: TSqlSmallInt = 0
-  buf[0] = '\0'
-  try:
-    db.sqlCheck(SQLGetInfo(db.hDb, SQL_DBMS_NAME, cast[SqlPointer](buf.addr),
-                        4095.TSqlSmallInt, sz.addr))
-  except: discard
-  return $buf.cstring
-
-proc dbQuote*(s: string): string {.noSideEffect.} =
-  ## DB quotes the string.
-  result = "'"
-  for c in items(s):
-    if c == '\'': add(result, "''")
-    else: add(result, c)
-  add(result, '\'')
-
-proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string {.
-                  noSideEffect.} =
-  ## Replace any ``?`` placeholders with `args`,
-  ## and quotes the arguments
-  result = ""
-  var a = 0
-  for c in items(string(formatstr)):
-    if c == '?':
-      if args[a] == nil:
-        add(result, "NULL")
-      else:
-        add(result, dbQuote(args[a]))
-      inc(a)
-    else:
-      add(result, c)
-
-proc prepareFetch(db: var DbConn, query: SqlQuery,
-                args: varargs[string, `$`]): TSqlSmallInt {.
-                tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  # Prepare a statement, execute it and fetch the data to the driver
-  # ready for retrieval of the data
-  # Used internally by iterators and retrieval procs
-  # requires calling
-  #      properFreeResult(SQL_HANDLE_STMT, db.stmt)
-  # when finished
-  db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt))
-  var q = dbFormat(query, args)
-  db.sqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
-  db.sqlCheck(SQLExecute(db.stmt))
-  result = SQLFetch(db.stmt)
-  db.sqlCheck(result)
-
-proc prepareFetchDirect(db: var DbConn, query: SqlQuery,
-                args: varargs[string, `$`]) {.
-                tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  # Prepare a statement, execute it and fetch the data to the driver
-  # ready for retrieval of the data
-  # Used internally by iterators and retrieval procs
-  # requires calling
-  #      properFreeResult(SQL_HANDLE_STMT, db.stmt)
-  # when finished
-  db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt))
-  var q = dbFormat(query, args)
-  db.sqlCheck(SQLExecDirect(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
-  db.sqlCheck(SQLFetch(db.stmt))
-
-proc tryExec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
-  tags: [ReadDbEffect, WriteDbEffect], raises: [].} =
-  ## Tries to execute the query and returns true if successful, false otherwise.
-  var
-    res:TSqlSmallInt = -1
-  try:
-    db.prepareFetchDirect(query, args)
-    var
-      rCnt = -1
-    res = SQLRowCount(db.stmt, rCnt)
-    properFreeResult(SQL_HANDLE_STMT, db.stmt)
-    if res != SQL_SUCCESS: dbError(db)
-  except: discard
-  return res == SQL_SUCCESS
-
-proc rawExec(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
-            tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  db.prepareFetchDirect(query, args)
-
-proc exec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
-            tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  ## Executes the query and raises EDB if not successful.
-  db.prepareFetchDirect(query, args)
-  properFreeResult(SQL_HANDLE_STMT, db.stmt)
-
-proc newRow(L: int): Row {.noSideEFfect.} =
-  newSeq(result, L)
-  for i in 0..L-1: result[i] = ""
-
-iterator fastRows*(db: var DbConn, query: SqlQuery,
-                   args: varargs[string, `$`]): Row {.
-                tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  ## Executes the query and iterates over the result dataset.
-  ##
-  ## This is very fast, but potentially dangerous.  Use this iterator only
-  ## if you require **ALL** the rows.
-  ##
-  ## Breaking the fastRows() iterator during a loop may cause a driver error
-  ## for subsequenct queries
-  ##
-  ## Rows are retrieved from the server at each iteration.
-  var
-    rowRes: Row
-    sz: TSqlSmallInt = 0
-    cCnt: TSqlSmallInt = 0.TSqlSmallInt
-    res: TSqlSmallInt = 0.TSqlSmallInt
-    tempcCnt: TSqlSmallInt # temporary cCnt,Fix the field values to be null when the release schema is compiled.
-    # tempcCnt,A field to store the number of temporary variables, for unknown reasons,
-    # after performing a sqlgetdata function and circulating variables cCnt value will be changed to 0,
-    # so the values of the temporary variable to store the cCnt.
-    # After every cycle and specified to cCnt. To ensure the traversal of all fields.
-  res = db.prepareFetch(query, args)
-  if res == SQL_NO_DATA:
-    discard
-  elif res == SQL_SUCCESS:
-    res = SQLNumResultCols(db.stmt, cCnt)
-    rowRes = newRow(cCnt)
-    rowRes.setLen(max(cCnt,0))
-    tempcCnt = cCnt
-    while res == SQL_SUCCESS:
-      for colId in 1..cCnt:
-        buf[0] = '\0'
-        db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
-                                 cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr))
-        rowRes[colId-1] = $buf.cstring
-        cCnt = tempcCnt
-      yield rowRes
-      res = SQLFetch(db.stmt)
-  properFreeResult(SQL_HANDLE_STMT, db.stmt)
-  db.sqlCheck(res)
-
-iterator instantRows*(db: var DbConn, query: SqlQuery,
-                      args: varargs[string, `$`]): InstantRow
-                {.tags: [ReadDbEffect, WriteDbEffect].} =
-  ## 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 interator body.
-  var
-    rowRes: Row = @[]
-    sz: TSqlSmallInt = 0
-    cCnt: TSqlSmallInt = 0.TSqlSmallInt
-    res: TSqlSmallInt = 0.TSqlSmallInt
-    tempcCnt: TSqlSmallInt # temporary cCnt,Fix the field values to be null when the release schema is compiled.
-    # tempcCnt,A field to store the number of temporary variables, for unknown reasons,
-    # after performing a sqlgetdata function and circulating variables cCnt value will be changed to 0,
-    # so the values of the temporary variable to store the cCnt.
-    # After every cycle and specified to cCnt. To ensure the traversal of all fields.
-  res = db.prepareFetch(query, args)
-  if res == SQL_NO_DATA:
-    discard
-  elif res == SQL_SUCCESS:
-    res = SQLNumResultCols(db.stmt, cCnt)
-    rowRes = newRow(cCnt)
-    rowRes.setLen(max(cCnt,0))
-    tempcCnt = cCnt
-    while res == SQL_SUCCESS:
-      for colId in 1..cCnt:
-        buf[0] = '\0'
-        db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
-                                 cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr))
-        rowRes[colId-1] = $buf.cstring
-        cCnt = tempcCnt
-      yield (row: rowRes, len: cCnt.int)
-      res = SQLFetch(db.stmt)
-  properFreeResult(SQL_HANDLE_STMT, db.stmt)
-  db.sqlCheck(res)
-
-proc `[]`*(row: InstantRow, col: int): string {.inline.} =
-  ## Returns text for given column of the row
-  row.row[col]
-
-proc len*(row: InstantRow): int {.inline.} =
-  ## Returns number of columns in the row
-  row.len
-
-proc getRow*(db: var DbConn, query: SqlQuery,
-             args: varargs[string, `$`]): Row {.
-          tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  ## Retrieves a single row. If the query doesn't return any rows, this proc
-  ## will return a Row with empty strings for each column.
-  var
-    rowRes: Row
-    sz: TSqlSmallInt = 0.TSqlSmallInt
-    cCnt: TSqlSmallInt = 0.TSqlSmallInt
-    res: TSqlSmallInt = 0.TSqlSmallInt
-    tempcCnt: TSqlSmallInt # temporary cCnt,Fix the field values to be null when the release schema is compiled.
-    ## tempcCnt,A field to store the number of temporary variables, for unknown reasons,
-    ## after performing a sqlgetdata function and circulating variables cCnt value will be changed to 0,
-    ## so the values of the temporary variable to store the cCnt.
-    ## After every cycle and specified to cCnt. To ensure the traversal of all fields.
-  res = db.prepareFetch(query, args)
-  if res == SQL_NO_DATA:
-    result = @[]
-  elif res == SQL_SUCCESS:
-    res = SQLNumResultCols(db.stmt, cCnt)
-    rowRes = newRow(cCnt)
-    rowRes.setLen(max(cCnt,0))
-    tempcCnt = cCnt
-    for colId in 1..cCnt:
-      buf[0] = '\0'
-      db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
-                               cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr))
-      rowRes[colId-1] = $buf.cstring
-      cCnt = tempcCnt
-    res = SQLFetch(db.stmt)
-    result = rowRes
-  properFreeResult(SQL_HANDLE_STMT, db.stmt)
-  db.sqlCheck(res)
-
-proc getAllRows*(db: var DbConn, query: SqlQuery,
-                 args: varargs[string, `$`]): seq[Row] {.
-           tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} =
-  ## Executes the query and returns the whole result dataset.
-  var
-    rows: seq[Row] = @[]
-    rowRes: Row
-    sz: TSqlSmallInt = 0
-    cCnt: TSqlSmallInt = 0.TSqlSmallInt
-    res: TSqlSmallInt = 0.TSqlSmallInt
-    tempcCnt: TSqlSmallInt # temporary cCnt,Fix the field values to be null when the release schema is compiled.
-    ## tempcCnt,A field to store the number of temporary variables, for unknown reasons,
-    ## after performing a sqlgetdata function and circulating variables cCnt value will be changed to 0,
-    ## so the values of the temporary variable to store the cCnt.
-    ## After every cycle and specified to cCnt. To ensure the traversal of all fields.
-  res = db.prepareFetch(query, args)
-  if res == SQL_NO_DATA:
-    result = @[]
-  elif res == SQL_SUCCESS:
-    res = SQLNumResultCols(db.stmt, cCnt)
-    rowRes = newRow(cCnt)
-    rowRes.setLen(max(cCnt,0))
-    tempcCnt = cCnt
-    while res == SQL_SUCCESS:
-      for colId in 1..cCnt:
-        buf[0] = '\0'
-        db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
-                                 cast[cstring](buf.addr), 4095.TSqlSmallInt, sz.addr))
-        rowRes[colId-1] = $buf.cstring
-        cCnt = tempcCnt
-      rows.add(rowRes)
-      res = SQLFetch(db.stmt)
-    result = rows
-  properFreeResult(SQL_HANDLE_STMT, db.stmt)
-  db.sqlCheck(res)
-
-iterator rows*(db: var DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): Row {.
-         tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  ## Same as `fastRows`, but slower and safe.
-  ##
-  ## This retrieves ALL rows into memory before
-  ## iterating through the rows.
-  ## Large dataset queries will impact on memory usage.
-  for r in items(getAllRows(db, query, args)): yield r
-
-proc getValue*(db: var DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): string {.
-           tags: [ReadDbEffect, WriteDbEffect], raises: [].} =
-  ## Executes the query and returns the first column of the first row of the
-  ## result dataset. Returns "" if the dataset contains no rows or the database
-  ## value is NULL.
-  result = ""
-  try:
-    result = getRow(db, query, args)[0]
-  except: discard
-
-proc tryInsertId*(db: var DbConn, query: SqlQuery,
-                  args: varargs[string, `$`]): int64 {.
-            tags: [ReadDbEffect, WriteDbEffect], raises: [].} =
-  ## Executes the query (typically "INSERT") and returns the
-  ## generated ID for the row or -1 in case of an error.
-  if not tryExec(db, query, args):
-    result = -1'i64
-  else:
-    result = -1'i64
-    try:
-      case sqlGetDBMS(db).toLower():
-      of "postgresql":
-        result = getValue(db, sql"SELECT LASTVAL();", []).parseInt
-      of "mysql":
-        result = getValue(db, sql"SELECT LAST_INSERT_ID();", []).parseInt
-      of "sqlite":
-        result = getValue(db, sql"SELECT LAST_INSERT_ROWID();", []).parseInt
-      of "microsoft sql server":
-        result = getValue(db, sql"SELECT SCOPE_IDENTITY();", []).parseInt
-      of "oracle":
-        result = getValue(db, sql"SELECT id.currval FROM DUAL;", []).parseInt
-      else: result = -1'i64
-    except: discard
-
-proc insertId*(db: var DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): int64 {.
-         tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  ## Executes the query (typically "INSERT") and returns the
-  ## generated ID for the row.
-  result = tryInsertID(db, query, args)
-  if result < 0: dbError(db)
-
-proc execAffectedRows*(db: var DbConn, query: SqlQuery,
-                       args: varargs[string, `$`]): int64 {.
-             tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  ## Runs the query (typically "UPDATE") and returns the
-  ## number of affected rows
-  result = -1
-  db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt.SqlHandle))
-  var q = dbFormat(query, args)
-  db.sqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
-  rawExec(db, query, args)
-  var rCnt = -1
-  db.sqlCheck(SQLRowCount(db.hDb, rCnt))
-  properFreeResult(SQL_HANDLE_STMT, db.stmt)
-  result = rCnt
-
-proc close*(db: var DbConn) {.
-      tags: [WriteDbEffect], raises: [].} =
-  ## Closes the database connection.
-  if db.hDb != nil:
-    try:
-      var res = SQLDisconnect(db.hDb)
-      if db.stmt != nil:
-        res = SQLFreeHandle(SQL_HANDLE_STMT, db.stmt)
-      res = SQLFreeHandle(SQL_HANDLE_DBC, db.hDb)
-      res = SQLFreeHandle(SQL_HANDLE_ENV, db.env)
-      db = (hDb: nil, env: nil, stmt: nil)
-    except:
-      discard
-
-proc open*(connection, user, password, database: string): DbConn {.
-  tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  ## Opens a database connection.
-  ##
-  ## Raises `EDb` if the connection could not be established.
-  ##
-  ## Currently the database parameter is ignored,
-  ## but included to match ``open()`` in the other db_xxxxx library modules.
-  var
-    val: TSqlInteger = SQL_OV_ODBC3
-    resLen = 0
-  result = (hDb: nil, env: nil, stmt: nil)
-  # allocate environment handle
-  var res = SQLAllocHandle(SQL_HANDLE_ENV, result.env, result.env)
-  if res != SQL_SUCCESS: dbError("Error: unable to initialise ODBC environment.")
-  res = SQLSetEnvAttr(result.env,
-                      SQL_ATTR_ODBC_VERSION.TSqlInteger,
-                      val, resLen.TSqlInteger)
-  if res != SQL_SUCCESS: dbError("Error: unable to set ODBC driver version.")
-  # allocate hDb handle
-  res = SQLAllocHandle(SQL_HANDLE_DBC, result.env, result.hDb)
-  if res != SQL_SUCCESS: dbError("Error: unable to allocate connection handle.")
-
-  # Connect: connection = dsn str,
-  res = SQLConnect(result.hDb,
-                  connection.PSQLCHAR , connection.len.TSqlSmallInt,
-                  user.PSQLCHAR, user.len.TSqlSmallInt,
-                  password.PSQLCHAR, password.len.TSqlSmallInt)
-  if res != SQL_SUCCESS:
-    result.dbError()
-
-proc setEncoding*(connection: DbConn, encoding: string): bool {.
-  tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  ## Currently not implemented for ODBC.
-  ##
-  ## Sets the encoding of a database connection, returns true for
-  ## success, false for failure.
-  ##result = set_character_set(connection, encoding) == 0
-  dbError("setEncoding() is currently not implemented by the db_odbc module")
diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim
deleted file mode 100644
index 1459f0d7e..000000000
--- a/lib/impure/db_postgres.nim
+++ /dev/null
@@ -1,538 +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.
-#
-
-## A higher level `PostgreSQL`:idx: database wrapper. This interface
-## is implemented for other databases also.
-##
-## See also: `db_odbc <db_odbc.html>`_, `db_sqlite <db_sqlite.html>`_,
-## `db_mysql <db_mysql.html>`_.
-##
-## Parameter substitution
-## ----------------------
-##
-## All ``db_*`` modules support the same form of parameter substitution.
-## That is, using the ``?`` (question mark) to signify the place where a
-## value should be placed. For example:
-##
-## .. code-block:: Nim
-##     sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
-##
-## **Note**: There are two approaches to parameter substitution support by
-## this module.
-##
-## 1.  ``SqlQuery`` using ``?, ?, ?, ...`` (same as all the ``db_*`` modules)
-##
-## 2. ``SqlPrepared`` using ``$1, $2, $3, ...``
-##
-## .. code-block:: Nim
-##   prepare(db, "myExampleInsert",
-##           sql"""INSERT INTO myTable
-##                 (colA, colB, colC)
-##                 VALUES ($1, $2, $3)""",
-##           3)
-##
-## Examples
-## --------
-##
-## Opening a connection to a database
-## ==================================
-##
-## .. code-block:: Nim
-##     import db_postgres
-##     let db = open("localhost", "user", "password", "dbname")
-##     db.close()
-##
-## Creating a table
-## ================
-##
-## .. code-block:: Nim
-##      db.exec(sql"DROP TABLE IF EXISTS myTable")
-##      db.exec(sql("""CREATE TABLE myTable (
-##                       id integer,
-##                       name varchar(50) not null)"""))
-##
-## Inserting data
-## ==============
-##
-## .. code-block:: Nim
-##     db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
-##             "Dominik")
-import strutils, postgres
-
-import db_common
-export db_common
-
-type
-  DbConn* = PPGconn    ## encapsulates a database connection
-  Row* = seq[string]   ## a row of a dataset. NULL database values will be
-                       ## converted to nil.
-  InstantRow* = object ## a handle that can be
-    res: PPGresult     ## used to get a row's
-    line: int          ## column text on demand
-  SqlPrepared* = distinct string ## a identifier for the prepared queries
-{.deprecated: [TRow: Row, TDbConn: DbConn, TSqlPrepared: SqlPrepared].}
-
-proc dbError*(db: DbConn) {.noreturn.} =
-  ## raises a DbError exception.
-  var e: ref DbError
-  new(e)
-  e.msg = $pqErrorMessage(db)
-  raise e
-
-proc dbQuote*(s: string): string =
-  ## DB quotes the string.
-  result = "'"
-  for c in items(s):
-    if c == '\'': add(result, "''")
-    else: add(result, c)
-  add(result, '\'')
-
-proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
-  result = ""
-  var a = 0
-  if args.len > 0 and not string(formatstr).contains("?"):
-    dbError("""parameter substitution expects "?" """)
-  if args.len == 0:
-    return string(formatstr)
-  else:
-    for c in items(string(formatstr)):
-      if c == '?':
-        if args[a] == nil:
-          add(result, "NULL")
-        else:
-          add(result, dbQuote(args[a]))
-        inc(a)
-      else:
-        add(result, c)
-
-proc tryExec*(db: DbConn, query: SqlQuery,
-              args: varargs[string, `$`]): bool {.tags: [ReadDbEffect, WriteDbEffect].} =
-  ## tries to execute the query and returns true if successful, false otherwise.
-  var res = pqexecParams(db, dbFormat(query, args), 0, nil, nil,
-                        nil, nil, 0)
-  result = pqresultStatus(res) == PGRES_COMMAND_OK
-  pqclear(res)
-
-proc tryExec*(db: DbConn, stmtName: SqlPrepared,
-              args: varargs[string, `$`]): bool {.tags: [
-              ReadDbEffect, WriteDbEffect].} =
-  ## tries to execute the query and returns true if successful, false otherwise.
-  var arr = allocCStringArray(args)
-  var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
-                           nil, nil, 0)
-  deallocCStringArray(arr)
-  result = pqresultStatus(res) == PGRES_COMMAND_OK
-  pqclear(res)
-
-proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
-  tags: [ReadDbEffect, WriteDbEffect].} =
-  ## executes the query and raises EDB if not successful.
-  var res = pqexecParams(db, dbFormat(query, args), 0, nil, nil,
-                        nil, nil, 0)
-  if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
-  pqclear(res)
-
-proc exec*(db: DbConn, stmtName: SqlPrepared,
-          args: varargs[string]) {.tags: [ReadDbEffect, WriteDbEffect].} =
-  var arr = allocCStringArray(args)
-  var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
-                           nil, nil, 0)
-  deallocCStringArray(arr)
-  if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
-  pqclear(res)
-
-proc newRow(L: int): Row =
-  newSeq(result, L)
-  for i in 0..L-1: result[i] = ""
-
-proc setupQuery(db: DbConn, query: SqlQuery,
-                args: varargs[string]): PPGresult =
-  result = pqexec(db, dbFormat(query, args))
-  if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db)
-
-proc setupQuery(db: DbConn, stmtName: SqlPrepared,
-                 args: varargs[string]): PPGresult =
-  var arr = allocCStringArray(args)
-  result = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
-                          nil, nil, 0)
-  deallocCStringArray(arr)
-  if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db)
-
-proc prepare*(db: DbConn; stmtName: string, query: SqlQuery;
-              nParams: int): SqlPrepared =
-  ## Creates a new ``SqlPrepared`` statement. Parameter substitution is done
-  ## via ``$1``, ``$2``, ``$3``, etc.
-  if nParams > 0 and not string(query).contains("$1"):
-    dbError("parameter substitution expects \"$1\"")
-  var res = pqprepare(db, stmtName, query.string, int32(nParams), nil)
-  if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
-  return SqlPrepared(stmtName)
-
-proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
-  for col in 0'i32..cols-1:
-    setLen(r[col], 0)
-    let x = pqgetvalue(res, line, col)
-    if x.isNil:
-      r[col] = nil
-    else:
-      add(r[col], x)
-
-iterator fastRows*(db: DbConn, query: SqlQuery,
-                   args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## executes the query and iterates over the result dataset. This is very
-  ## fast, but potenially dangerous: If the for-loop-body executes another
-  ## query, the results can be undefined. For Postgres it is safe though.
-  var res = setupQuery(db, query, args)
-  var L = pqnfields(res)
-  var result = newRow(L)
-  for i in 0'i32..pqntuples(res)-1:
-    setRow(res, result, i, L)
-    yield result
-  pqclear(res)
-
-iterator fastRows*(db: DbConn, stmtName: SqlPrepared,
-                   args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## executes the prepared query and iterates over the result dataset.
-  var res = setupQuery(db, stmtName, args)
-  var L = pqNfields(res)
-  var result = newRow(L)
-  for i in 0'i32..pqNtuples(res)-1:
-    setRow(res, result, i, L)
-    yield result
-  pqClear(res)
-
-iterator instantRows*(db: DbConn, query: SqlQuery,
-                      args: varargs[string, `$`]): InstantRow
-                      {.tags: [ReadDbEffect].} =
-  ## same as fastRows but returns a handle that can be used to get column text
-  ## on demand using []. Returned handle is valid only within iterator body.
-  var res = setupQuery(db, query, args)
-  for i in 0'i32..pqNtuples(res)-1:
-    yield InstantRow(res: res, line: i)
-  pqClear(res)
-
-iterator instantRows*(db: DbConn, stmtName: SqlPrepared,
-                      args: varargs[string, `$`]): InstantRow
-                      {.tags: [ReadDbEffect].} =
-  ## same as fastRows but returns a handle that can be used to get column text
-  ## on demand using []. Returned handle is valid only within iterator body.
-  var res = setupQuery(db, stmtName, args)
-  for i in 0'i32..pqNtuples(res)-1:
-    yield InstantRow(res: res, line: i)
-  pqClear(res)
-
-proc getColumnType(res: PPGresult, col: int) : DbType =
-  ## returns DbType for given column in the row
-  ## defined in pg_type.h file in the postgres source code
-  ## Wire representation for types: http://www.npgsql.org/dev/types.html
-  var oid = pqftype(res, int32(col))
-  ## The integer returned is the internal OID number of the type
-  case oid
-  of 16: return DbType(kind: DbTypeKind.dbBool, name: "bool")
-  of 17: return DbType(kind: DbTypeKind.dbBlob, name: "bytea")
-
-  of 21:   return DbType(kind: DbTypeKind.dbInt, name: "int2", size: 2)
-  of 23:   return DbType(kind: DbTypeKind.dbInt, name: "int4", size: 4)
-  of 20:   return DbType(kind: DbTypeKind.dbInt, name: "int8", size: 8)
-  of 1560: return DbType(kind: DbTypeKind.dbBit, name: "bit")
-  of 1562: return DbType(kind: DbTypeKind.dbInt, name: "varbit")
-
-  of 18:   return DbType(kind: DbTypeKind.dbFixedChar, name: "char")
-  of 19:   return DbType(kind: DbTypeKind.dbFixedChar, name: "name")
-  of 1042: return DbType(kind: DbTypeKind.dbFixedChar, name: "bpchar")
-
-  of 25:   return DbType(kind: DbTypeKind.dbVarchar, name: "text")
-  of 1043: return DbType(kind: DbTypeKind.dbVarChar, name: "varchar")
-  of 2275: return DbType(kind: DbTypeKind.dbVarchar, name: "cstring")
-
-  of 700: return DbType(kind: DbTypeKind.dbFloat, name: "float4")
-  of 701: return DbType(kind: DbTypeKind.dbFloat, name: "float8")
-
-  of 790:  return DbType(kind: DbTypeKind.dbDecimal, name: "money")
-  of 1700: return DbType(kind: DbTypeKind.dbDecimal, name: "numeric")
-
-  of 704:  return DbType(kind: DbTypeKind.dbTimeInterval, name: "tinterval")
-  of 702:  return DbType(kind: DbTypeKind.dbTimestamp, name: "abstime")
-  of 703:  return DbType(kind: DbTypeKind.dbTimeInterval, name: "reltime")
-  of 1082: return DbType(kind: DbTypeKind.dbDate, name: "date")
-  of 1083: return DbType(kind: DbTypeKind.dbTime, name: "time")
-  of 1114: return DbType(kind: DbTypeKind.dbTimestamp, name: "timestamp")
-  of 1184: return DbType(kind: DbTypeKind.dbTimestamp, name: "timestamptz")
-  of 1186: return DbType(kind: DbTypeKind.dbTimeInterval, name: "interval")
-  of 1266: return DbType(kind: DbTypeKind.dbTime, name: "timetz")
-
-  of 114:  return DbType(kind: DbTypeKind.dbJson, name: "json")
-  of 142:  return DbType(kind: DbTypeKind.dbXml, name: "xml")
-  of 3802: return DbType(kind: DbTypeKind.dbJson, name: "jsonb")
-
-  of 600: return DbType(kind: DbTypeKind.dbPoint, name: "point")
-  of 601: return DbType(kind: DbTypeKind.dbLseg, name: "lseg")
-  of 602: return DbType(kind: DbTypeKind.dbPath, name: "path")
-  of 603: return DbType(kind: DbTypeKind.dbBox, name: "box")
-  of 604: return DbType(kind: DbTypeKind.dbPolygon, name: "polygon")
-  of 628: return DbType(kind: DbTypeKind.dbLine, name: "line")
-  of 718: return DbType(kind: DbTypeKind.dbCircle, name: "circle")
-
-  of 650: return DbType(kind: DbTypeKind.dbInet, name: "cidr")
-  of 829: return DbType(kind: DbTypeKind.dbMacAddress, name: "macaddr")
-  of 869: return DbType(kind: DbTypeKind.dbInet, name: "inet")
-
-  of 2950: return DbType(kind: DbTypeKind.dbVarchar, name: "uuid")
-  of 3614: return DbType(kind: DbTypeKind.dbVarchar, name: "tsvector")
-  of 3615: return DbType(kind: DbTypeKind.dbVarchar, name: "tsquery")
-  of 2970: return DbType(kind: DbTypeKind.dbVarchar, name: "txid_snapshot")
-
-  of 27:   return DbType(kind: DbTypeKind.dbComposite, name: "tid")
-  of 1790: return DbType(kind: DbTypeKind.dbComposite, name: "refcursor")
-  of 2249: return DbType(kind: DbTypeKind.dbComposite, name: "record")
-  of 3904: return DbType(kind: DbTypeKind.dbComposite, name: "int4range")
-  of 3906: return DbType(kind: DbTypeKind.dbComposite, name: "numrange")
-  of 3908: return DbType(kind: DbTypeKind.dbComposite, name: "tsrange")
-  of 3910: return DbType(kind: DbTypeKind.dbComposite, name: "tstzrange")
-  of 3912: return DbType(kind: DbTypeKind.dbComposite, name: "daterange")
-  of 3926: return DbType(kind: DbTypeKind.dbComposite, name: "int8range")
-
-  of 22:   return DbType(kind: DbTypeKind.dbArray, name: "int2vector")
-  of 30:   return DbType(kind: DbTypeKind.dbArray, name: "oidvector")
-  of 143:  return DbType(kind: DbTypeKind.dbArray, name: "xml[]")
-  of 199:  return DbType(kind: DbTypeKind.dbArray, name: "json[]")
-  of 629:  return DbType(kind: DbTypeKind.dbArray, name: "line[]")
-  of 651:  return DbType(kind: DbTypeKind.dbArray, name: "cidr[]")
-  of 719:  return DbType(kind: DbTypeKind.dbArray, name: "circle[]")
-  of 791:  return DbType(kind: DbTypeKind.dbArray, name: "money[]")
-  of 1000: return DbType(kind: DbTypeKind.dbArray, name: "bool[]")
-  of 1001: return DbType(kind: DbTypeKind.dbArray, name: "bytea[]")
-  of 1002: return DbType(kind: DbTypeKind.dbArray, name: "char[]")
-  of 1003: return DbType(kind: DbTypeKind.dbArray, name: "name[]")
-  of 1005: return DbType(kind: DbTypeKind.dbArray, name: "int2[]")
-  of 1006: return DbType(kind: DbTypeKind.dbArray, name: "int2vector[]")
-  of 1007: return DbType(kind: DbTypeKind.dbArray, name: "int4[]")
-  of 1008: return DbType(kind: DbTypeKind.dbArray, name: "regproc[]")
-  of 1009: return DbType(kind: DbTypeKind.dbArray, name: "text[]")
-  of 1028: return DbType(kind: DbTypeKind.dbArray, name: "oid[]")
-  of 1010: return DbType(kind: DbTypeKind.dbArray, name: "tid[]")
-  of 1011: return DbType(kind: DbTypeKind.dbArray, name: "xid[]")
-  of 1012: return DbType(kind: DbTypeKind.dbArray, name: "cid[]")
-  of 1013: return DbType(kind: DbTypeKind.dbArray, name: "oidvector[]")
-  of 1014: return DbType(kind: DbTypeKind.dbArray, name: "bpchar[]")
-  of 1015: return DbType(kind: DbTypeKind.dbArray, name: "varchar[]")
-  of 1016: return DbType(kind: DbTypeKind.dbArray, name: "int8[]")
-  of 1017: return DbType(kind: DbTypeKind.dbArray, name: "point[]")
-  of 1018: return DbType(kind: DbTypeKind.dbArray, name: "lseg[]")
-  of 1019: return DbType(kind: DbTypeKind.dbArray, name: "path[]")
-  of 1020: return DbType(kind: DbTypeKind.dbArray, name: "box[]")
-  of 1021: return DbType(kind: DbTypeKind.dbArray, name: "float4[]")
-  of 1022: return DbType(kind: DbTypeKind.dbArray, name: "float8[]")
-  of 1023: return DbType(kind: DbTypeKind.dbArray, name: "abstime[]")
-  of 1024: return DbType(kind: DbTypeKind.dbArray, name: "reltime[]")
-  of 1025: return DbType(kind: DbTypeKind.dbArray, name: "tinterval[]")
-  of 1027: return DbType(kind: DbTypeKind.dbArray, name: "polygon[]")
-  of 1040: return DbType(kind: DbTypeKind.dbArray, name: "macaddr[]")
-  of 1041: return DbType(kind: DbTypeKind.dbArray, name: "inet[]")
-  of 1263: return DbType(kind: DbTypeKind.dbArray, name: "cstring[]")
-  of 1115: return DbType(kind: DbTypeKind.dbArray, name: "timestamp[]")
-  of 1182: return DbType(kind: DbTypeKind.dbArray, name: "date[]")
-  of 1183: return DbType(kind: DbTypeKind.dbArray, name: "time[]")
-  of 1185: return DbType(kind: DbTypeKind.dbArray, name: "timestamptz[]")
-  of 1187: return DbType(kind: DbTypeKind.dbArray, name: "interval[]")
-  of 1231: return DbType(kind: DbTypeKind.dbArray, name: "numeric[]")
-  of 1270: return DbType(kind: DbTypeKind.dbArray, name: "timetz[]")
-  of 1561: return DbType(kind: DbTypeKind.dbArray, name: "bit[]")
-  of 1563: return DbType(kind: DbTypeKind.dbArray, name: "varbit[]")
-  of 2201: return DbType(kind: DbTypeKind.dbArray, name: "refcursor[]")
-  of 2951: return DbType(kind: DbTypeKind.dbArray, name: "uuid[]")
-  of 3643: return DbType(kind: DbTypeKind.dbArray, name: "tsvector[]")
-  of 3645: return DbType(kind: DbTypeKind.dbArray, name: "tsquery[]")
-  of 3807: return DbType(kind: DbTypeKind.dbArray, name: "jsonb[]")
-  of 2949: return DbType(kind: DbTypeKind.dbArray, name: "txid_snapshot[]")
-  of 3905: return DbType(kind: DbTypeKind.dbArray, name: "int4range[]")
-  of 3907: return DbType(kind: DbTypeKind.dbArray, name: "numrange[]")
-  of 3909: return DbType(kind: DbTypeKind.dbArray, name: "tsrange[]")
-  of 3911: return DbType(kind: DbTypeKind.dbArray, name: "tstzrange[]")
-  of 3913: return DbType(kind: DbTypeKind.dbArray, name: "daterange[]")
-  of 3927: return DbType(kind: DbTypeKind.dbArray, name: "int8range[]")
-  of 2287: return DbType(kind: DbTypeKind.dbArray, name: "record[]")
-
-  of 705:  return DbType(kind: DbTypeKind.dbUnknown, name: "unknown")
-  else: return DbType(kind: DbTypeKind.dbUnknown, name: $oid) ## Query the system table pg_type to determine exactly which type is referenced.
-
-proc setColumnInfo(columns: var DbColumns; res: PPGresult; L: int32) =
-  setLen(columns, L)
-  for i in 0'i32..<L:
-    columns[i].name = $pqfname(res, i)
-    columns[i].typ = getColumnType(res, i)
-    columns[i].tableName = $(pqftable(res, i)) ## Returns the OID of the table from which the given column was fetched.
-                                               ## Query the system table pg_class to determine exactly which table is referenced.
-    #columns[i].primaryKey = libpq does not have a function for that
-    #columns[i].foreignKey = libpq does not have a function for that
-
-iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery;
-                      args: varargs[string, `$`]): InstantRow
-                      {.tags: [ReadDbEffect].} =
-  var res = setupQuery(db, query, args)
-  setColumnInfo(columns, res, pqnfields(res))
-  for i in 0'i32..<pqntuples(res):
-    yield InstantRow(res: res, line: i)
-  pqClear(res)
-
-proc `[]`*(row: InstantRow; col: int): string {.inline.} =
-  ## returns text for given column of the row
-  $pqgetvalue(row.res, int32(row.line), int32(col))
-
-proc len*(row: InstantRow): int {.inline.} =
-  ## returns number of columns in the row
-  int(pqNfields(row.res))
-
-proc getRow*(db: DbConn, query: SqlQuery,
-             args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## retrieves a single row. If the query doesn't return any rows, this proc
-  ## will return a Row with empty strings for each column.
-  var res = setupQuery(db, query, args)
-  var L = pqnfields(res)
-  result = newRow(L)
-  setRow(res, result, 0, L)
-  pqclear(res)
-
-proc getRow*(db: DbConn, stmtName: SqlPrepared,
-             args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  var res = setupQuery(db, stmtName, args)
-  var L = pqNfields(res)
-  result = newRow(L)
-  setRow(res, result, 0, L)
-  pqClear(res)
-
-proc getAllRows*(db: DbConn, query: SqlQuery,
-                 args: varargs[string, `$`]): seq[Row] {.
-                 tags: [ReadDbEffect].} =
-  ## executes the query and returns the whole result dataset.
-  result = @[]
-  for r in fastRows(db, query, args):
-    result.add(r)
-
-proc getAllRows*(db: DbConn, stmtName: SqlPrepared,
-                 args: varargs[string, `$`]): seq[Row] {.tags:
-                 [ReadDbEffect].} =
-  ## executes the prepared query and returns the whole result dataset.
-  result = @[]
-  for r in fastRows(db, stmtName, args):
-    result.add(r)
-
-iterator rows*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## same as `fastRows`, but slower and safe.
-  for r in items(getAllRows(db, query, args)): yield r
-
-iterator rows*(db: DbConn, stmtName: SqlPrepared,
-               args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## same as `fastRows`, but slower and safe.
-  for r in items(getAllRows(db, stmtName, args)): yield r
-
-proc getValue*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): string {.
-               tags: [ReadDbEffect].} =
-  ## executes the query and returns the first column of the first row of the
-  ## result dataset. Returns "" if the dataset contains no rows or the database
-  ## value is NULL.
-  var x = pqgetvalue(setupQuery(db, query, args), 0, 0)
-  result = if isNil(x): "" else: $x
-
-proc getValue*(db: DbConn, stmtName: SqlPrepared,
-               args: varargs[string, `$`]): string {.
-               tags: [ReadDbEffect].} =
-  ## executes the query and returns the first column of the first row of the
-  ## result dataset. Returns "" if the dataset contains no rows or the database
-  ## value is NULL.
-  var x = pqgetvalue(setupQuery(db, stmtName, args), 0, 0)
-  result = if isNil(x): "" else: $x
-
-proc tryInsertID*(db: DbConn, query: SqlQuery,
-                  args: varargs[string, `$`]): int64 {.
-                  tags: [WriteDbEffect].}=
-  ## executes the query (typically "INSERT") and returns the
-  ## generated ID for the row or -1 in case of an error. For Postgre this adds
-  ## ``RETURNING id`` to the query, so it only works if your primary key is
-  ## named ``id``.
-  var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING id"),
-    args), 0, 0)
-  if not isNil(x):
-    result = parseBiggestInt($x)
-  else:
-    result = -1
-
-proc insertID*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): int64 {.
-               tags: [WriteDbEffect].} =
-  ## executes the query (typically "INSERT") and returns the
-  ## generated ID for the row. For Postgre this adds
-  ## ``RETURNING id`` to the query, so it only works if your primary key is
-  ## named ``id``.
-  result = tryInsertID(db, query, args)
-  if result < 0: dbError(db)
-
-proc execAffectedRows*(db: DbConn, query: SqlQuery,
-                       args: varargs[string, `$`]): int64 {.tags: [
-                       ReadDbEffect, WriteDbEffect].} =
-  ## executes the query (typically "UPDATE") and returns the
-  ## number of affected rows.
-  var q = dbFormat(query, args)
-  var res = pqExec(db, q)
-  if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
-  result = parseBiggestInt($pqcmdTuples(res))
-  pqclear(res)
-
-proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared,
-                       args: varargs[string, `$`]): int64 {.tags: [
-                       ReadDbEffect, WriteDbEffect].} =
-  ## executes the query (typically "UPDATE") and returns the
-  ## number of affected rows.
-  var arr = allocCStringArray(args)
-  var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
-                           nil, nil, 0)
-  deallocCStringArray(arr)
-  if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
-  result = parseBiggestInt($pqcmdTuples(res))
-  pqclear(res)
-
-proc close*(db: DbConn) {.tags: [DbEffect].} =
-  ## closes the database connection.
-  if db != nil: pqfinish(db)
-
-proc open*(connection, user, password, database: string): DbConn {.
-  tags: [DbEffect].} =
-  ## opens a database connection. Raises `EDb` if the connection could not
-  ## be established.
-  ##
-  ## Clients can also use Postgres keyword/value connection strings to
-  ## connect.
-  ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##      con = open("", "", "", "host=localhost port=5432 dbname=mydb")
-  ##
-  ## See http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
-  ## for more information.
-  let
-    colonPos = connection.find(':')
-    host = if colonPos < 0: connection
-           else: substr(connection, 0, colonPos-1)
-    port = if colonPos < 0: ""
-           else: substr(connection, colonPos+1)
-  result = pqsetdbLogin(host, port, nil, nil, database, user, password)
-  if pqStatus(result) != CONNECTION_OK: dbError(result) # result = nil
-
-proc setEncoding*(connection: DbConn, encoding: string): bool {.
-  tags: [DbEffect].} =
-  ## sets the encoding of a database connection, returns true for
-  ## success, false for failure.
-  return pqsetClientEncoding(connection, encoding) == 0
-
-
-# Tests are in ../../tests/untestable/tpostgres.
diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim
deleted file mode 100644
index fd25b2b94..000000000
--- a/lib/impure/db_sqlite.nim
+++ /dev/null
@@ -1,338 +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.
-#
-
-## A higher level `SQLite`:idx: database wrapper. This interface
-## is implemented for other databases too.
-##
-## See also: `db_odbc <db_odbc.html>`_, `db_postgres <db_postgres.html>`_,
-## `db_mysql <db_mysql.html>`_.
-##
-## Parameter substitution
-## ----------------------
-##
-## All ``db_*`` modules support the same form of parameter substitution.
-## That is, using the ``?`` (question mark) to signify the place where a
-## value should be placed. For example:
-##
-## .. code-block:: Nim
-##     sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
-##
-## Examples
-## --------
-##
-## Opening a connection to a database
-## ==================================
-##
-## .. code-block:: Nim
-##     import db_sqlite
-##     let db = open("mytest.db", nil, nil, nil)  # user, password, database name can be nil
-##     db.close()
-##
-## Creating a table
-## ================
-##
-## .. code-block:: Nim
-##      db.exec(sql"DROP TABLE IF EXISTS myTable")
-##      db.exec(sql("""CREATE TABLE myTable (
-##                       id integer,
-##                       name varchar(50) not null)"""))
-##
-## Inserting data
-## ==============
-##
-## .. code-block:: Nim
-##     db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
-##             "Jack")
-##
-## Larger example
-## ==============
-##
-## .. code-block:: nim
-##
-##  import db_sqlite, math
-##
-##  let theDb = open("mytest.db", "", "", "")
-##
-##  theDb.exec(sql"Drop table if exists myTestTbl")
-##  theDb.exec(sql("""create table myTestTbl (
-##       Id    INTEGER PRIMARY KEY,
-##       Name  VARCHAR(50) NOT NULL,
-##       i     INT(11),
-##       f     DECIMAL(18,10))"""))
-##
-##  theDb.exec(sql"BEGIN")
-##  for i in 1..1000:
-##    theDb.exec(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
-##          "Item#" & $i, i, sqrt(i.float))
-##  theDb.exec(sql"COMMIT")
-##
-##  for x in theDb.fastRows(sql"select * from myTestTbl"):
-##    echo x
-##
-##  let id = theDb.tryInsertId(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
-##        "Item#1001", 1001, sqrt(1001.0))
-##  echo "Inserted item: ", theDb.getValue(sql"SELECT name FROM myTestTbl WHERE id=?", id)
-##
-##  theDb.close()
-
-{.deadCodeElim: on.}  # dce option deprecated
-
-import strutils, sqlite3
-
-import db_common
-export db_common
-
-type
-  DbConn* = PSqlite3  ## encapsulates a database connection
-  Row* = seq[string]  ## a row of a dataset. NULL database values will be
-                       ## converted to nil.
-  InstantRow* = Pstmt  ## a handle that can be used to get a row's column
-                       ## text on demand
-{.deprecated: [TRow: Row, TDbConn: DbConn].}
-
-proc dbError*(db: DbConn) {.noreturn.} =
-  ## raises a DbError exception.
-  var e: ref DbError
-  new(e)
-  e.msg = $sqlite3.errmsg(db)
-  raise e
-
-proc dbQuote*(s: string): string =
-  ## DB quotes the string.
-  if s.isNil: return "NULL"
-  result = "'"
-  for c in items(s):
-    if c == '\'': add(result, "''")
-    else: add(result, c)
-  add(result, '\'')
-
-proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
-  result = ""
-  var a = 0
-  for c in items(string(formatstr)):
-    if c == '?':
-      add(result, dbQuote(args[a]))
-      inc(a)
-    else:
-      add(result, c)
-
-proc tryExec*(db: DbConn, query: SqlQuery,
-              args: varargs[string, `$`]): bool {.
-              tags: [ReadDbEffect, WriteDbEffect].} =
-  ## tries to execute the query and returns true if successful, false otherwise.
-  assert(not db.isNil, "Database not connected.")
-  var q = dbFormat(query, args)
-  var stmt: sqlite3.Pstmt
-  if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK:
-    let x = step(stmt)
-    if x in {SQLITE_DONE, SQLITE_ROW}:
-      result = finalize(stmt) == SQLITE_OK
-
-proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`])  {.
-  tags: [ReadDbEffect, WriteDbEffect].} =
-  ## executes the query and raises DbError if not successful.
-  if not tryExec(db, query, args): dbError(db)
-
-proc newRow(L: int): Row =
-  newSeq(result, L)
-  for i in 0..L-1: result[i] = ""
-
-proc setupQuery(db: DbConn, query: SqlQuery,
-                args: varargs[string]): Pstmt =
-  assert(not db.isNil, "Database not connected.")
-  var q = dbFormat(query, args)
-  if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db)
-
-proc setRow(stmt: Pstmt, r: var Row, cols: cint) =
-  for col in 0'i32..cols-1:
-    setLen(r[col], column_bytes(stmt, col)) # set capacity
-    setLen(r[col], 0)
-    let x = column_text(stmt, col)
-    if not isNil(x): add(r[col], x)
-
-iterator fastRows*(db: DbConn, query: SqlQuery,
-                   args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## Executes the query and iterates over the result dataset.
-  ##
-  ## This is very fast, but potentially dangerous.  Use this iterator only
-  ## if you require **ALL** the rows.
-  ##
-  ## Breaking the fastRows() iterator during a loop will cause the next
-  ## database query to raise a DbError exception ``unable to close due to ...``.
-  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)
-
-iterator instantRows*(db: DbConn, query: SqlQuery,
-                      args: varargs[string, `$`]): InstantRow
-                      {.tags: [ReadDbEffect].} =
-  ## 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)
-
-proc toTypeKind(t: var DbType; x: int32) =
-  case x
-  of SQLITE_INTEGER:
-    t.kind = dbInt
-    t.size = 8
-  of SQLITE_FLOAT:
-    t.kind = dbFloat
-    t.size = 8
-  of SQLITE_BLOB: t.kind = dbBlob
-  of SQLITE_NULL: t.kind = dbNull
-  of SQLITE_TEXT: t.kind = dbVarchar
-  else: t.kind = dbUnknown
-
-proc setColumns(columns: var DbColumns; x: PStmt) =
-  let L = column_count(x)
-  setLen(columns, L)
-  for i in 0'i32 ..< L:
-    columns[i].name = $column_name(x, i)
-    columns[i].typ.name = $column_decltype(x, i)
-    toTypeKind(columns[i].typ, column_type(x, i))
-    columns[i].tableName = $column_table_name(x, i)
-
-iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery,
-                      args: varargs[string, `$`]): InstantRow
-                      {.tags: [ReadDbEffect].} =
-  ## 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)
-  setColumns(columns, stmt)
-  while step(stmt) == SQLITE_ROW:
-    yield stmt
-  if finalize(stmt) != SQLITE_OK: dbError(db)
-
-proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
-  ## returns text for given column of the row
-  $column_text(row, col)
-
-proc len*(row: InstantRow): int32 {.inline.} =
-  ## returns number of columns in the row
-  column_count(row)
-
-proc getRow*(db: DbConn, query: SqlQuery,
-             args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## retrieves a single row. If the query doesn't return any rows, this proc
-  ## will return a Row with empty strings for each column.
-  var stmt = setupQuery(db, query, args)
-  var L = (column_count(stmt))
-  result = newRow(L)
-  if step(stmt) == SQLITE_ROW:
-    setRow(stmt, result, L)
-  if finalize(stmt) != SQLITE_OK: dbError(db)
-
-proc getAllRows*(db: DbConn, query: SqlQuery,
-                 args: varargs[string, `$`]): seq[Row] {.tags: [ReadDbEffect].} =
-  ## executes the query and returns the whole result dataset.
-  result = @[]
-  for r in fastRows(db, query, args):
-    result.add(r)
-
-iterator rows*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## same as `FastRows`, but slower and safe.
-  for r in fastRows(db, query, args): yield r
-
-proc getValue*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): string {.tags: [ReadDbEffect].} =
-  ## executes the query and returns the first column of the first row of the
-  ## result dataset. Returns "" if the dataset contains no rows or the database
-  ## value is NULL.
-  var stmt = setupQuery(db, query, args)
-  if step(stmt) == SQLITE_ROW:
-    let cb = column_bytes(stmt, 0)
-    if cb == 0:
-      result = ""
-    else:
-      result = newStringOfCap(cb)
-      add(result, column_text(stmt, 0))
-  else:
-    result = ""
-  if finalize(stmt) != SQLITE_OK: dbError(db)
-
-proc tryInsertID*(db: DbConn, query: SqlQuery,
-                  args: varargs[string, `$`]): int64
-                  {.tags: [WriteDbEffect], raises: [].} =
-  ## executes the query (typically "INSERT") and returns the
-  ## generated ID for the row or -1 in case of an error.
-  assert(not db.isNil, "Database not connected.")
-  var q = dbFormat(query, args)
-  var stmt: sqlite3.Pstmt
-  result = -1
-  if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK:
-    if step(stmt) == SQLITE_DONE:
-      result = last_insert_rowid(db)
-    if finalize(stmt) != SQLITE_OK:
-      result = -1
-
-proc insertID*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
-  ## executes the query (typically "INSERT") and returns the
-  ## generated ID for the row. For Postgre this adds
-  ## ``RETURNING id`` to the query, so it only works if your primary key is
-  ## named ``id``.
-  result = tryInsertID(db, query, args)
-  if result < 0: dbError(db)
-
-proc execAffectedRows*(db: DbConn, query: SqlQuery,
-                       args: varargs[string, `$`]): int64 {.
-                       tags: [ReadDbEffect, WriteDbEffect].} =
-  ## executes the query (typically "UPDATE") and returns the
-  ## number of affected rows.
-  exec(db, query, args)
-  result = changes(db)
-
-proc close*(db: DbConn) {.tags: [DbEffect].} =
-  ## closes the database connection.
-  if sqlite3.close(db) != SQLITE_OK: dbError(db)
-
-proc open*(connection, user, password, database: string): DbConn {.
-  tags: [DbEffect].} =
-  ## opens a database connection. Raises `EDb` if the connection could not
-  ## be established. Only the ``connection`` parameter is used for ``sqlite``.
-  var db: DbConn
-  if sqlite3.open(connection, db) == SQLITE_OK:
-    result = db
-  else:
-    dbError(db)
-
-proc setEncoding*(connection: DbConn, encoding: string): bool {.
-  tags: [DbEffect].} =
-  ## sets the encoding of a database connection, returns true for
-  ## success, false for failure.
-  ##
-  ## Note that the encoding cannot be changed once it's been set.
-  ## According to SQLite3 documentation, any attempt to change
-  ## the encoding after the database is created will be silently
-  ## ignored.
-  exec(connection, sql"PRAGMA encoding = ?", [encoding])
-  result = connection.getValue(sql"PRAGMA encoding") == encoding
-
-when not defined(testing) and isMainModule:
-  var db = open("db.sql", "", "", "")
-  exec(db, sql"create table tbl1(one varchar(10), two smallint)", [])
-  exec(db, sql"insert into tbl1 values('hello!',10)", [])
-  exec(db, sql"insert into tbl1 values('goodbye', 20)", [])
-  #db.query("create table tbl1(one varchar(10), two smallint)")
-  #db.query("insert into tbl1 values('hello!',10)")
-  #db.query("insert into tbl1 values('goodbye', 20)")
-  for r in db.rows(sql"select * from tbl1", []):
-    echo(r[0], r[1])
-  for r in db.instantRows(sql"select * from tbl1", []):
-    echo(r[0], r[1])
-
-  db_sqlite.close(db)
diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim
index 6058128dd..39d238055 100644
--- a/lib/impure/nre.nim
+++ b/lib/impure/nre.nim
@@ -6,32 +6,31 @@
 #    distribution, for details about the copyright.
 #
 
-
-from pcre import nil
-import nre.private.util
-import tables
-from strutils import `%`
-from math import ceil
-import options
-from unicode import runeLenAt
-
-export options
-
+when defined(js):
+  {.error: "This library needs to be compiled with a c-like backend, and depends on PCRE; See jsre for JS backend.".}
 
 ## What is NRE?
 ## ============
 ##
 ## A regular expression library for Nim using PCRE to do the hard work.
 ##
-## **Note**: If you love ``sequtils.toSeq`` we have bad news for you. This
-## library doesn't work with it due to documented compiler limitations. As
-## a workaround, use this:
-##
-## .. code-block:: nim
-##
-##    import nre except toSeq
-##
+## For documentation on how to write patterns, there exists `the official PCRE
+## pattern documentation
+## <https://www.pcre.org/original/doc/html/pcrepattern.html>`_. You can also
+## search the internet for a wide variety of third-party documentation and
+## tools.
 ##
+## .. warning:: If you love `sequtils.toSeq` we have bad news for you. This
+##   library doesn't work with it due to documented compiler limitations. As
+##   a workaround, use this:
+runnableExamples:
+  # either `import std/nre except toSeq` or fully qualify `sequtils.toSeq`:
+  import std/sequtils
+  iterator iota(n: int): int =
+    for i in 0..<n: yield i
+  assert sequtils.toSeq(iota(3)) == @[0, 1, 2]
+## .. note:: There are also alternative nimble packages such as [tinyre](https://github.com/khchen/tinyre)
+##   and [regex](https://github.com/nitely/nim-regex).
 ## Licencing
 ## ---------
 ##
@@ -39,47 +38,58 @@ export options
 ## this module.
 ##
 ## .. _`some additional terms`: http://pcre.sourceforge.net/license.txt
-##
-## Example
-## -------
-##
-## .. code-block:: nim
-##
-##     import nre
-##
-##     let vowels = re"[aeoui]"
-##
-##     for match in "moigagoo".findIter(vowels):
-##       echo match.matchBounds
-##     # (a: 1, b: 1)
-##     # (a: 2, b: 2)
-##     # (a: 4, b: 4)
-##     # (a: 6, b: 6)
-##     # (a: 7, b: 7)
-##
-##     let firstVowel = "foo".find(vowels)
-##     let hasVowel = firstVowel.isSome()
-##     if hasVowel:
-##       let matchBounds = firstVowel.get().captureBounds[-1]
-##       echo "first vowel @", matchBounds.get().a
-##       # first vowel @1
+runnableExamples:
+  import std/sugar
+  let vowels = re"[aeoui]"
+  let bounds = collect:
+    for match in "moiga".findIter(vowels): match.matchBounds
+  assert bounds == @[1 .. 1, 2 .. 2, 4 .. 4]
+  from std/sequtils import toSeq
+  let s = sequtils.toSeq("moiga".findIter(vowels))
+    # fully qualified to avoid confusion with nre.toSeq
+  assert s.len == 3
+
+  let firstVowel = "foo".find(vowels)
+  let hasVowel = firstVowel.isSome()
+  assert hasVowel
+  let matchBounds = firstVowel.get().captureBounds[-1]
+  assert matchBounds.a == 1
+
+  # as with module `re`, unless specified otherwise, `start` parameter in each
+  # proc indicates where the scan starts, but outputs are relative to the start
+  # of the input string, not to `start`:
+  assert find("uxabc", re"(?<=x|y)ab", start = 1).get.captures[-1] == "ab"
+  assert find("uxabc", re"ab", start = 3).isNone
+
+from std/pcre import nil
+import nre/private/util
+import std/tables
+from std/strutils import `%`
+import std/options
+from std/unicode import runeLenAt
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
+export options
 
-# Type definitions {{{
 type
   Regex* = ref object
     ## Represents the pattern that things are matched against, constructed with
-    ## ``re(string)``. Examples: ``re"foo"``, ``re(r"(*ANYCRLF)(?x)foo #
-    ## comment".``
+    ## `re(string)`. Examples: `re"foo"`, `re(r"(*ANYCRLF)(?x)foo #
+    ## comment".`
     ##
-    ## ``pattern: string``
-    ##     the string that was used to create the pattern.
+    ## `pattern: string`
+    ## :   the string that was used to create the pattern. For details on how
+    ##     to write a pattern, please see `the official PCRE pattern
+    ##     documentation.
+    ##     <https://www.pcre.org/original/doc/html/pcrepattern.html>`_
     ##
-    ## ``captureCount: int``
-    ##     the number of captures that the pattern has.
+    ## `captureCount: int`
+    ## :   the number of captures that the pattern has.
     ##
-    ## ``captureNameId: Table[string, int]``
-    ##     a table from the capture names to their numeric id.
+    ## `captureNameId: Table[string, int]`
+    ## :   a table from the capture names to their numeric id.
     ##
     ##
     ## Options
@@ -88,30 +98,30 @@ type
     ## The following options may appear anywhere in the pattern, and they affect
     ## the rest of it.
     ##
-    ## -  ``(?i)`` - case insensitive
-    ## -  ``(?m)`` - multi-line: ``^`` and ``$`` match the beginning and end of
+    ## -  `(?i)` - case insensitive
+    ## -  `(?m)` - multi-line: `^` and `$` match the beginning and end of
     ##    lines, not of the subject string
-    ## -  ``(?s)`` - ``.`` also matches newline (*dotall*)
-    ## -  ``(?U)`` - expressions are not greedy by default. ``?`` can be added
+    ## -  `(?s)` - `.` also matches newline (*dotall*)
+    ## -  `(?U)` - expressions are not greedy by default. `?` can be added
     ##    to a qualifier to make it greedy
-    ## -  ``(?x)`` - whitespace and comments (``#``) are ignored (*extended*)
-    ## -  ``(?X)`` - character escapes without special meaning (``\w`` vs.
-    ##    ``\a``) are errors (*extra*)
+    ## -  `(?x)` - whitespace and comments (`#`) are ignored (*extended*)
+    ## -  `(?X)` - character escapes without special meaning (`\w` vs.
+    ##    `\a`) are errors (*extra*)
     ##
     ## One or a combination of these options may appear only at the beginning
     ## of the pattern:
     ##
-    ## -  ``(*UTF8)`` - treat both the pattern and subject as UTF-8
-    ## -  ``(*UCP)`` - Unicode character properties; ``\w`` matches ``я``
-    ## -  ``(*U)`` - a combination of the two options above
-    ## -  ``(*FIRSTLINE*)`` - fails if there is not a match on the first line
-    ## -  ``(*NO_AUTO_CAPTURE)`` - turn off auto-capture for groups;
-    ##    ``(?<name>...)`` can be used to capture
-    ## -  ``(*CR)`` - newlines are separated by ``\r``
-    ## -  ``(*LF)`` - newlines are separated by ``\n`` (UNIX default)
-    ## -  ``(*CRLF)`` - newlines are separated by ``\r\n`` (Windows default)
-    ## -  ``(*ANYCRLF)`` - newlines are separated by any of the above
-    ## -  ``(*ANY)`` - newlines are separated by any of the above and Unicode
+    ## -  `(*UTF8)` - treat both the pattern and subject as UTF-8
+    ## -  `(*UCP)` - Unicode character properties; `\w` matches `я`
+    ## -  `(*U)` - a combination of the two options above
+    ## -  `(*FIRSTLINE*)` - fails if there is not a match on the first line
+    ## -  `(*NO_AUTO_CAPTURE)` - turn off auto-capture for groups;
+    ##    `(?<name>...)` can be used to capture
+    ## -  `(*CR)` - newlines are separated by `\r`
+    ## -  `(*LF)` - newlines are separated by `\n` (UNIX default)
+    ## -  `(*CRLF)` - newlines are separated by `\r\n` (Windows default)
+    ## -  `(*ANYCRLF)` - newlines are separated by any of the above
+    ## -  `(*ANY)` - newlines are separated by any of the above and Unicode
     ##    newlines:
     ##
     ##     single characters VT (vertical tab, U+000B), FF (form feed, U+000C),
@@ -120,8 +130,8 @@ type
     ##     are recognized only in UTF-8 mode.
     ##     —  man pcre
     ##
-    ## -  ``(*JAVASCRIPT_COMPAT)`` - JavaScript compatibility
-    ## -  ``(*NO_STUDY)`` - turn off studying; study is enabled by default
+    ## -  `(*JAVASCRIPT_COMPAT)` - JavaScript compatibility
+    ## -  `(*NO_STUDY)` - turn off studying; study is enabled by default
     ##
     ## For more details on the leading option groups, see the `Option
     ## Setting <http://man7.org/linux/man-pages/man3/pcresyntax.3.html#OPTION_SETTING>`_
@@ -129,7 +139,13 @@ type
     ## Convention <http://man7.org/linux/man-pages/man3/pcresyntax.3.html#NEWLINE_CONVENTION>`_
     ## sections of the `PCRE syntax
     ## manual <http://man7.org/linux/man-pages/man3/pcresyntax.3.html>`_.
-    pattern*: string  ## not nil
+    ##
+    ## Some of these options are not part of PCRE and are converted by nre
+    ## into PCRE flags. These include `NEVER_UTF`, `ANCHORED`,
+    ## `DOLLAR_ENDONLY`, `FIRSTLINE`, `NO_AUTO_CAPTURE`,
+    ## `JAVASCRIPT_COMPAT`, `U`, `NO_STUDY`. In other PCRE wrappers, you
+    ## will need to pass these as separate flags to PCRE.
+    pattern*: string
     pcreObj: ptr pcre.Pcre  ## not nil
     pcreExtra: ptr pcre.ExtraData  ## nil
 
@@ -139,49 +155,40 @@ type
     ## Usually seen as Option[RegexMatch], it represents the result of an
     ## execution. On failure, it is none, on success, it is some.
     ##
-    ## ``pattern: Regex``
-    ##     the pattern that is being matched
+    ## `pattern: Regex`
+    ## :   the pattern that is being matched
     ##
-    ## ``str: string``
-    ##     the string that was matched against
+    ## `str: string`
+    ## :   the string that was matched against
     ##
-    ## ``captures[]: string``
-    ##     the string value of whatever was captured at that id. If the value
-    ##     is invalid, then behavior is undefined. If the id is ``-1``, then
+    ## `captures[]: string`
+    ## :   the string value of whatever was captured at that id. If the value
+    ##     is invalid, then behavior is undefined. If the id is `-1`, then
     ##     the whole match is returned. If the given capture was not matched,
-    ##     ``nil`` is returned.
+    ##     `nil` is returned. See examples for `match`.
     ##
-    ##     -  ``"abc".match(re"(\w)").captures[0] == "a"``
-    ##     -  ``"abc".match(re"(?<letter>\w)").captures["letter"] == "a"``
-    ##     -  ``"abc".match(re"(\w)\w").captures[-1] == "ab"``
+    ## `captureBounds[]: HSlice[int, int]`
+    ## :   gets the bounds of the given capture according to the same rules as
+    ##     the above. If the capture is not filled, then `None` is returned.
+    ##     The bounds are both inclusive.  See examples for `match`.
     ##
-    ## ``captureBounds[]: Option[HSlice[int, int]]``
-    ##     gets the bounds of the given capture according to the same rules as
-    ##     the above. If the capture is not filled, then ``None`` is returned.
-    ##     The bounds are both inclusive.
+    ## `match: string`
+    ## :   the full text of the match.
     ##
-    ##     -  ``"abc".match(re"(\w)").captureBounds[0] == 0 .. 0``
-    ##     -  ``"abc".match(re"").captureBounds[-1] == 0 .. -1``
-    ##     -  ``"abc".match(re"abc").captureBounds[-1] == 0 .. 2``
+    ## `matchBounds: HSlice[int, int]`
+    ## :   the bounds of the match, as in `captureBounds[]`
     ##
-    ## ``match: string``
-    ##     the full text of the match.
+    ## `(captureBounds|captures).toTable`
+    ## :   returns a table with each named capture as a key.
     ##
-    ## ``matchBounds: HSlice[int, int]``
-    ##     the bounds of the match, as in ``captureBounds[]``
+    ## `(captureBounds|captures).toSeq`
+    ## :   returns all the captures by their number.
     ##
-    ## ``(captureBounds|captures).toTable``
-    ##     returns a table with each named capture as a key.
-    ##
-    ## ``(captureBounds|captures).toSeq``
-    ##     returns all the captures by their number.
-    ##
-    ## ``$: string``
-    ##     same as ``match``
+    ## `$: string`
+    ## :   same as `match`
     pattern*: Regex  ## The regex doing the matching.
                      ## Not nil.
     str*: string  ## The string that was matched against.
-                  ## Not nil.
     pcreMatchBounds: seq[HSlice[cint, cint]] ## First item is the bounds of the match
                                             ## Other items are the captures
                                             ## `a` is inclusive start, `b` is exclusive end
@@ -189,7 +196,7 @@ type
   Captures* = distinct RegexMatch
   CaptureBounds* = distinct RegexMatch
 
-  RegexError* = ref object of Exception
+  RegexError* = ref object of CatchableError
 
   RegexInternalError* = ref object of RegexError
     ## Internal error in the module, this probably means that there is a bug
@@ -205,19 +212,71 @@ type
     pattern*: string  ## the pattern that caused the problem
 
   StudyError* = ref object of RegexError
-    ## Thrown when studying the regular expression failes
+    ## Thrown when studying the regular expression fails
     ## for whatever reason. The message contains the error
     ## code.
-# }}}
+
+proc destroyRegex(pattern: Regex) =
+  `=destroy`(pattern.pattern)
+  pcre.free_substring(cast[cstring](pattern.pcreObj))
+  if pattern.pcreExtra != nil:
+    pcre.free_study(pattern.pcreExtra)
+  `=destroy`(pattern.captureNameToId)
 
 proc getinfo[T](pattern: Regex, opt: cint): T =
   let retcode = pcre.fullinfo(pattern.pcreObj, pattern.pcreExtra, opt, addr result)
 
   if retcode < 0:
     # XXX Error message that doesn't expose implementation details
-    raise newException(FieldError, "Invalid getinfo for $1, errno $2" % [$opt, $retcode])
+    raise newException(FieldDefect, "Invalid getinfo for $1, errno $2" % [$opt, $retcode])
+
+proc getNameToNumberTable(pattern: Regex): Table[string, int] =
+  let entryCount = getinfo[cint](pattern, pcre.INFO_NAMECOUNT)
+  let entrySize = getinfo[cint](pattern, pcre.INFO_NAMEENTRYSIZE)
+  let table = cast[ptr UncheckedArray[uint8]](
+                getinfo[int](pattern, pcre.INFO_NAMETABLE))
+
+  result = initTable[string, int]()
+
+  for i in 0 ..< entryCount:
+    let pos = i * entrySize
+    let num = (int(table[pos]) shl 8) or int(table[pos + 1]) - 1
+    var name = ""
+
+    var idx = 2
+    while table[pos + idx] != 0:
+      name.add(char(table[pos + idx]))
+      idx += 1
+
+    result[name] = num
+
+proc initRegex(pattern: string, flags: int, study = true): Regex =
+  new(result, destroyRegex)
+  result.pattern = pattern
+
+  var errorMsg: cstring
+  var errOffset: cint
+
+  result.pcreObj = pcre.compile(cstring(pattern),
+                                # better hope int is at least 4 bytes..
+                                cint(flags), addr errorMsg,
+                                addr errOffset, nil)
+  if result.pcreObj == nil:
+    # failed to compile
+    raise SyntaxError(msg: $errorMsg, pos: errOffset, pattern: pattern)
+
+  if study:
+    var options: cint = 0
+    var hasJit: cint
+    if pcre.config(pcre.CONFIG_JIT, addr hasJit) == 0:
+      if hasJit == 1'i32:
+        options = pcre.STUDY_JIT_COMPILE
+    result.pcreExtra = pcre.study(result.pcreObj, options, addr errorMsg)
+    if errorMsg != nil:
+      raise StudyError(msg: $errorMsg)
+
+  result.captureNameToId = result.getNameToNumberTable()
 
-# Regex accessors {{{
 proc captureCount*(pattern: Regex): int =
   return getinfo[cint](pattern, pcre.INFO_CAPTURECOUNT)
 
@@ -229,7 +288,7 @@ proc matchesCrLf(pattern: Regex): bool =
   let newlineFlags = flags and (pcre.NEWLINE_CRLF or
                                 pcre.NEWLINE_ANY or
                                 pcre.NEWLINE_ANYCRLF)
-  if newLineFlags > 0u32:
+  if newlineFlags > 0u32:
     return true
 
   # get flags from build config
@@ -244,82 +303,106 @@ proc matchesCrLf(pattern: Regex): bool =
   of -2: return true
   of -1: return true
   else: return false
-# }}}
 
-# Capture accessors {{{
-proc captureBounds*(pattern: RegexMatch): CaptureBounds = return CaptureBounds(pattern)
 
-proc captures*(pattern: RegexMatch): Captures = return Captures(pattern)
+func captureBounds*(pattern: RegexMatch): CaptureBounds = return CaptureBounds(pattern)
+
+func captures*(pattern: RegexMatch): Captures = return Captures(pattern)
 
-proc `[]`*(pattern: CaptureBounds, i: int): Option[HSlice[int, int]] =
+func contains*(pattern: CaptureBounds, i: int): bool =
   let pattern = RegexMatch(pattern)
-  if pattern.pcreMatchBounds[i + 1].a != -1:
-    let bounds = pattern.pcreMatchBounds[i + 1]
-    return some(int(bounds.a) .. int(bounds.b-1))
-  else:
-    return none(HSlice[int, int])
+  pattern.pcreMatchBounds[i + 1].a != -1
+
+func contains*(pattern: Captures, i: int): bool =
+  i in CaptureBounds(pattern)
 
-proc `[]`*(pattern: Captures, i: int): string =
+func `[]`*(pattern: CaptureBounds, i: int): HSlice[int, int] =
+  let pattern = RegexMatch(pattern)
+  if not (i in pattern.captureBounds):
+    raise newException(IndexDefect, "Group '" & $i & "' was not captured")
+
+  let bounds = pattern.pcreMatchBounds[i + 1]
+  int(bounds.a)..int(bounds.b-1)
+
+func `[]`*(pattern: Captures, i: int): string =
   let pattern = RegexMatch(pattern)
   let bounds = pattern.captureBounds[i]
 
-  if bounds.isSome:
-    let bounds = bounds.get
-    return pattern.str.substr(bounds.a, bounds.b)
-  else:
-    return nil
+  pattern.str.substr(bounds.a, bounds.b)
 
-proc match*(pattern: RegexMatch): string =
+func match*(pattern: RegexMatch): string =
   return pattern.captures[-1]
 
-proc matchBounds*(pattern: RegexMatch): HSlice[int, int] =
-  return pattern.captureBounds[-1].get
+func matchBounds*(pattern: RegexMatch): HSlice[int, int] =
+  return pattern.captureBounds[-1]
 
-proc `[]`*(pattern: CaptureBounds, name: string): Option[HSlice[int, int]] =
+func contains*(pattern: CaptureBounds, name: string): bool =
   let pattern = RegexMatch(pattern)
-  return pattern.captureBounds[pattern.pattern.captureNameToId.fget(name)]
+  let nameToId = pattern.pattern.captureNameToId
+  if not (name in nameToId):
+      return false
+  nameToId[name] in pattern.captureBounds
 
-proc `[]`*(pattern: Captures, name: string): string =
+func contains*(pattern: Captures, name: string): bool =
+  name in CaptureBounds(pattern)
+
+func checkNamedCaptured(pattern: RegexMatch, name: string) =
+  if not (name in pattern.captureBounds):
+    raise newException(KeyError, "Group '" & name & "' was not captured")
+
+func `[]`*(pattern: CaptureBounds, name: string): HSlice[int, int] =
   let pattern = RegexMatch(pattern)
-  return pattern.captures[pattern.pattern.captureNameToId.fget(name)]
+  checkNamedCaptured(pattern, name)
+  {.noSideEffect.}:
+    result = pattern.captureBounds[pattern.pattern.captureNameToId[name]]
 
-template toTableImpl(cond: untyped) {.dirty.} =
+func `[]`*(pattern: Captures, name: string): string =
+  let pattern = RegexMatch(pattern)
+  checkNamedCaptured(pattern, name)
+  {.noSideEffect.}:
+    result = pattern.captures[pattern.pattern.captureNameToId[name]]
+
+template toTableImpl() {.dirty.} =
   for key in RegexMatch(pattern).pattern.captureNameId.keys:
-    let nextVal = pattern[key]
-    if cond:
-      result[key] = default
-    else:
-      result[key] = nextVal
+    if key in pattern:
+      result[key] = pattern[key]
 
-proc toTable*(pattern: Captures, default: string = nil): Table[string, string] =
+func toTable*(pattern: Captures): Table[string, string] =
   result = initTable[string, string]()
-  toTableImpl(nextVal == nil)
+  toTableImpl()
 
-proc toTable*(pattern: CaptureBounds, default = none(HSlice[int, int])):
-    Table[string, Option[HSlice[int, int]]] =
-  result = initTable[string, Option[HSlice[int, int]]]()
-  toTableImpl(nextVal.isNone)
+func toTable*(pattern: CaptureBounds): Table[string, HSlice[int, int]] =
+  result = initTable[string, HSlice[int, int]]()
+  toTableImpl()
 
-template itemsImpl(cond: untyped) {.dirty.} =
+template itemsImpl() {.dirty.} =
   for i in 0 ..< RegexMatch(pattern).pattern.captureCount:
-    let nextVal = pattern[i]
     # done in this roundabout way to avoid multiple yields (potential code
     # bloat)
-    let nextYieldVal = if cond: default else: nextVal
-    yield nextYieldVal
+    let nextYieldVal = if i in pattern:
+      some(pattern[i])
+    else:
+      default
 
+    yield nextYieldVal
 
-iterator items*(pattern: CaptureBounds, default = none(HSlice[int, int])): Option[HSlice[int, int]] =
-  itemsImpl(nextVal.isNone)
+iterator items*(pattern: CaptureBounds,
+                default = none(HSlice[int, int])): Option[HSlice[int, int]] =
+  itemsImpl()
 
-iterator items*(pattern: Captures, default: string = nil): string =
-  itemsImpl(nextVal == nil)
+iterator items*(pattern: Captures,
+                default: Option[string] = none(string)): Option[string] =
+  itemsImpl()
 
-proc toSeq*(pattern: CaptureBounds, default = none(HSlice[int, int])): seq[Option[HSlice[int, int]]] =
-  accumulateResult(pattern.items(default))
+proc toSeq*(pattern: CaptureBounds,
+            default = none(HSlice[int, int])): seq[Option[HSlice[int, int]]] =
+  result = @[]
+  for it in pattern.items(default): result.add it
 
-proc toSeq*(pattern: Captures, default: string = nil): seq[string] =
-  accumulateResult(pattern.items(default))
+proc toSeq*(pattern: Captures,
+            default: Option[string] = none(string)): seq[Option[string]] =
+  result = @[]
+  for it in pattern.items(default): result.add it
 
 proc `$`*(pattern: RegexMatch): string =
   return pattern.captures[-1]
@@ -335,10 +418,7 @@ proc `==`*(a, b: Regex): bool =
 proc `==`*(a, b: RegexMatch): bool =
   return a.pattern == b.pattern and
          a.str == b.str
-# }}}
 
-# Creation & Destruction {{{
-# PCRE Options {{{
 const PcreOptions = {
   "NEVER_UTF": pcre.NEVER_UTF,
   "ANCHORED": pcre.ANCHORED,
@@ -394,75 +474,19 @@ proc extractOptions(pattern: string): tuple[pattern: string, flags: int, study:
 
   result.pattern.add pattern[optionStart .. pattern.high]
 
-# }}}
-
-proc destroyRegex(pattern: Regex) =
-  pcre.free_substring(cast[cstring](pattern.pcreObj))
-  pattern.pcreObj = nil
-  if pattern.pcreExtra != nil:
-    pcre.free_study(pattern.pcreExtra)
-
-proc getNameToNumberTable(pattern: Regex): Table[string, int] =
-  let entryCount = getinfo[cint](pattern, pcre.INFO_NAMECOUNT)
-  let entrySize = getinfo[cint](pattern, pcre.INFO_NAMEENTRYSIZE)
-  let table = cast[ptr UncheckedArray[uint8]](
-                getinfo[int](pattern, pcre.INFO_NAMETABLE))
-
-  result = initTable[string, int]()
-
-  for i in 0 ..< entryCount:
-    let pos = i * entrySize
-    let num = (int(table[pos]) shl 8) or int(table[pos + 1]) - 1
-    var name = ""
-
-    var idx = 2
-    while table[pos + idx] != 0:
-      name.add(char(table[pos + idx]))
-      idx += 1
-
-    result[name] = num
-
-proc initRegex(pattern: string, flags: int, study = true): Regex =
-  new(result, destroyRegex)
-  result.pattern = pattern
-
-  var errorMsg: cstring
-  var errOffset: cint
-
-  result.pcreObj = pcre.compile(cstring(pattern),
-                                # better hope int is at least 4 bytes..
-                                cint(flags), addr errorMsg,
-                                addr errOffset, nil)
-  if result.pcreObj == nil:
-    # failed to compile
-    raise SyntaxError(msg: $errorMsg, pos: errOffset, pattern: pattern)
-
-  if study:
-    var options: cint = 0
-    var hasJit: cint
-    if pcre.config(pcre.CONFIG_JIT, addr hasJit) == 0:
-      if hasJit == 1'i32:
-        options = pcre.STUDY_JIT_COMPILE
-    result.pcreExtra = pcre.study(result.pcreObj, options, addr errorMsg)
-    if errorMsg != nil:
-      raise StudyError(msg: $errorMsg)
-
-  result.captureNameToId = result.getNameToNumberTable()
-
 proc re*(pattern: string): Regex =
   let (pattern, flags, study) = extractOptions(pattern)
   initRegex(pattern, flags, study)
-# }}}
 
-# Operations {{{
 proc matchImpl(str: string, pattern: Regex, start, endpos: int, flags: int): Option[RegexMatch] =
-  var myResult = RegexMatch(pattern : pattern, str : str)
+  var myResult = RegexMatch(pattern: pattern, str: str)
   # See PCRE man pages.
   # 2x capture count to make room for start-end pairs
   # 1x capture count as slack space for PCRE
   let vecsize = (pattern.captureCount() + 1) * 3
   # div 2 because each element is 2 cints long
-  myResult.pcreMatchBounds = newSeq[HSlice[cint, cint]](ceil(vecsize / 2).int)
+  # plus 1 because we need the ceiling, not the floor
+  myResult.pcreMatchBounds = newSeq[HSlice[cint, cint]]((vecsize + 1) div 2)
   myResult.pcreMatchBounds.setLen(vecsize div 3)
 
   let strlen = if endpos == int.high: str.len else: endpos+1
@@ -483,33 +507,46 @@ proc matchImpl(str: string, pattern: Regex, start, endpos: int, flags: int): Opt
     of pcre.ERROR_NOMATCH:
       return none(RegexMatch)
     of pcre.ERROR_NULL:
-      raise newException(AccessViolationError, "Expected non-null parameters")
+      raise newException(AccessViolationDefect, "Expected non-null parameters")
     of pcre.ERROR_BADOPTION:
-      raise RegexInternalError(msg : "Unknown pattern flag. Either a bug or " &
+      raise RegexInternalError(msg: "Unknown pattern flag. Either a bug or " &
         "outdated PCRE.")
     of pcre.ERROR_BADUTF8, pcre.ERROR_SHORTUTF8, pcre.ERROR_BADUTF8_OFFSET:
-      raise InvalidUnicodeError(msg : "Invalid unicode byte sequence",
-        pos : myResult.pcreMatchBounds[0].a)
+      raise InvalidUnicodeError(msg: "Invalid unicode byte sequence",
+        pos: myResult.pcreMatchBounds[0].a)
     else:
-      raise RegexInternalError(msg : "Unknown internal error: " & $execRet)
+      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
-  ## string. This means that ``"foo".match(re"f") == true``, but
-  ## ``"foo".match(re"o") == false``.
+  ## Like `find(...)<#find,string,Regex,int>`_, but anchored to the start of the
+  ## string.
+  runnableExamples:
+    assert "foo".match(re"f").isSome
+    assert "foo".match(re"o").isNone
+
+    assert "abc".match(re"(\w)").get.captures[0] == "a"
+    assert "abc".match(re"(?<letter>\w)").get.captures["letter"] == "a"
+    assert "abc".match(re"(\w)\w").get.captures[-1] == "ab"
+
+    assert "abc".match(re"(\w)").get.captureBounds[0] == 0 .. 0
+    assert 0 in "abc".match(re"(\w)").get.captureBounds
+    assert "abc".match(re"").get.captureBounds[-1] == 0 .. -1
+    assert "abc".match(re"abc").get.captureBounds[-1] == 0 .. 2
   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
-  ## non-overlapping match. ``"2222".find(re"22")`` is ``"22", "22"``, not
-  ## ``"22", "22", "22"``.
-  ##
-  ## Arguments are the same as ```find(...)`` <#proc-find>`_
+  ## Works the same as `find(...)<#find,string,Regex,int>`_, but finds every
+  ## non-overlapping match:
+  runnableExamples:
+    import std/sugar
+    assert collect(for a in "2222".findIter(re"22"): a.match) == @["22", "22"]
+     # not @["22", "22", "22"]
+  ## Arguments are the same as `find(...)<#find,string,Regex,int>`_
   ##
   ## Variants:
   ##
-  ## -  ``proc findAll(...)`` returns a ``seq[string]``
-  # see pcredemo for explanation
+  ## -  `proc findAll(...)` returns a `seq[string]`
+  # see pcredemo for explanation => https://www.pcre.org/original/doc/html/pcredemo.html
   let matchesCrLf = pattern.matchesCrLf()
   let unicode = uint32(getinfo[culong](pattern, pcre.INFO_OPTIONS) and
     pcre.UTF8) > 0u32
@@ -530,7 +567,7 @@ iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): R
       # either the end of the input or the string
       # cannot be split here - we also need to bail
       # if we've never matched and we've already tried to...
-      if offset >= strlen or neverMatched:
+      if flags == 0 or offset >= strlen or neverMatched: # All matches found
         break
 
       if matchesCrLf and offset < (str.len - 1) and
@@ -546,19 +583,18 @@ iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): R
     else:
       neverMatched = false
       offset = match.get.matchBounds.b + 1
-
       yield match.get
 
 proc find*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] =
   ## Finds the given pattern in the string between the end and start
   ## positions.
   ##
-  ## ``start``
-  ##     The start point at which to start matching. ``|abc`` is ``0``;
-  ##     ``a|bc`` is ``1``
+  ## `start`
+  ## :   The start point at which to start matching. `|abc` is `0`;
+  ##     `a|bc` is `1`
   ##
-  ## ``endpos``
-  ##     The maximum index for a match; ``int.high`` means the end of the
+  ## `endpos`
+  ## :   The maximum index for a match; `int.high` means the end of the
   ##     string, otherwise it’s an inclusive upper bound.
   return str.matchImpl(pattern, start, endpos, 0)
 
@@ -570,29 +606,33 @@ proc findAll*(str: string, pattern: Regex, start = 0, endpos = int.high): seq[st
 proc contains*(str: string, pattern: Regex, start = 0, endpos = int.high): bool =
   ## Determine if the string contains the given pattern between the end and
   ## start positions:
-  ## -  "abc".contains(re"bc") == true
-  ## -  "abc".contains(re"cd") == false
-  ## -  "abc".contains(re"a", start = 1) == false
-  ##
-  ## Same as ``isSome(str.find(pattern, start, endpos))``.
+  ## This function is equivalent to `isSome(str.find(pattern, start, endpos))`.
+  runnableExamples:
+    assert "abc".contains(re"bc")
+    assert not "abc".contains(re"cd")
+    assert not "abc".contains(re"a", start = 1)
+
   return isSome(str.find(pattern, start, endpos))
 
 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:
-  ##
-  ## -  If the match is zero-width, then the string is still split:
-  ##    ``"123".split(r"") == @["1", "2", "3"]``.
-  ##
-  ## -  If the pattern has a capture in it, it is added after the string
-  ##    split: ``"12".split(re"(\d)") == @["", "1", "", "2", ""]``.
+  ## rules that Perl and Javascript use.
   ##
-  ## -  If ``maxsplit != -1``, then the string will only be split
-  ##    ``maxsplit - 1`` times. This means that there will be ``maxsplit``
-  ##    strings in the output seq.
-  ##    ``"1.2.3".split(re"\.", maxsplit = 2) == @["1", "2.3"]``
+  ## `start` behaves the same as in `find(...)<#find,string,Regex,int>`_.
   ##
-  ## ``start`` behaves the same as in ```find(...)`` <#proc-find>`_.
+  runnableExamples:
+    # -  If the match is zero-width, then the string is still split:
+    assert "123".split(re"") == @["1", "2", "3"]
+
+    # -  If the pattern has a capture in it, it is added after the string
+    #    split:
+    assert "12".split(re"(\d)") == @["", "1", "", "2", ""]
+
+    # -  If `maxsplit != -1`, then the string will only be split
+    #    `maxsplit - 1` times. This means that there will be `maxsplit`
+    #    strings in the output seq.
+    assert "1.2.3".split(re"\.", maxsplit = 2) == @["1", "2.3"]
+
   result = @[]
   var lastIdx = start
   var splits = 0
@@ -620,7 +660,8 @@ proc split*(str: string, pattern: Regex, maxSplit = -1, start = 0): seq[string]
 
     for cap in match.captures:
       # if there are captures, include them in the result
-      result.add(cap)
+      if cap.isSome:
+        result.add(cap.get)
 
     if splits == maxSplit - 1:
       break
@@ -654,27 +695,27 @@ template replaceImpl(str: string, pattern: Regex,
 
 proc replace*(str: string, pattern: Regex,
               subproc: proc (match: RegexMatch): string): string =
-  ## Replaces each match of Regex in the string with ``sub``, which should
-  ## never be or return ``nil``.
+  ## Replaces each match of Regex in the string with `subproc`, which should
+  ## never be or return `nil`.
   ##
-  ## If ``sub`` is a ``proc (RegexMatch): string``, then it is executed with
+  ## If `subproc` is a `proc (RegexMatch): string`, then it is executed with
   ## each match and the return value is the replacement value.
   ##
-  ## If ``sub`` is a ``proc (string): string``, then it is executed with the
-  ## full text of the match and and the return value is the replacement
-  ## value.
+  ## If `subproc` is a `proc (string): string`, then it is executed with the
+  ## full text of the match and the return value is the replacement value.
   ##
-  ## If ``sub`` is a string, the syntax is as follows:
+  ## If `subproc` is a string, the syntax is as follows:
   ##
-  ## -  ``$$`` - literal ``$``
-  ## -  ``$123`` - capture number ``123``
-  ## -  ``$foo`` - named capture ``foo``
-  ## -  ``${foo}`` - same as above
-  ## -  ``$1$#`` - first and second captures
-  ## -  ``$#`` - first capture
-  ## -  ``$0`` - full match
+  ## -  `$$` - literal `$`
+  ## -  `$123` - capture number `123`
+  ## -  `$foo` - named capture `foo`
+  ## -  `${foo}` - same as above
+  ## -  `$1$#` - first and second captures
+  ## -  `$#` - first capture
+  ## -  `$0` - full match
   ##
-  ## If a given capture is missing, a ``ValueError`` exception is thrown.
+  ## If a given capture is missing, `IndexDefect` thrown for un-named captures
+  ## and `KeyError` for named captures.
   replaceImpl(str, pattern, subproc(match))
 
 proc replace*(str: string, pattern: Regex,
@@ -686,10 +727,25 @@ proc replace*(str: string, pattern: Regex, sub: string): string =
   replaceImpl(str, pattern,
     formatStr(sub, match.captures[name], match.captures[id - 1]))
 
-# }}}
-
-let SpecialCharMatcher = re"([\\+*?[^\]$(){}=!<>|:-])"
-proc escapeRe*(str: string): string =
-  ## Escapes the string so it doesn’t match any special characters.
-  ## Incompatible with the Extra flag (``X``).
-  str.replace(SpecialCharMatcher, "\\$1")
+proc escapeRe*(str: string): string {.gcsafe.} =
+  ## Escapes the string so it doesn't match any special characters.
+  ## Incompatible with the Extra flag (`X`).
+  ##
+  ## Escaped char: `\ + * ? [ ^ ] $ ( ) { } = ! < > | : -`
+  runnableExamples:
+    assert escapeRe("fly+wind") == "fly\\+wind"
+    assert escapeRe("!") == "\\!"
+    assert escapeRe("nim*") == "nim\\*"
+
+  #([\\+*?[^\]$(){}=!<>|:-])
+  const SpecialCharMatcher = {'\\', '+', '*', '?', '[', '^', ']', '$', '(',
+                              ')', '{', '}', '=', '!', '<', '>', '|', ':',
+                              '-'}
+
+  for c in items(str):
+    case c
+    of SpecialCharMatcher:
+      result.add("\\")
+      result.add(c)
+    else:
+      result.add(c)
diff --git a/lib/impure/nre/private/util.nim b/lib/impure/nre/private/util.nim
index 12d2506ea..ed8420776 100644
--- a/lib/impure/nre/private/util.nim
+++ b/lib/impure/nre/private/util.nim
@@ -1,21 +1,9 @@
 ## INTERNAL FILE FOR USE ONLY BY nre.nim.
-import tables
-
-proc fget*[K, V](self: Table[K, V], key: K): V =
-  if self.hasKey(key):
-    return self[key]
-  else:
-    raise newException(KeyError, "Key does not exist in table: " & $key)
+import std/tables
 
 const Ident = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
 const StartIdent = Ident - {'0'..'9'}
 
-proc checkNil(arg: string): string =
-  if arg == nil:
-    raise newException(ValueError, "Cannot use nil capture")
-  else:
-    return arg
-
 template formatStr*(howExpr, namegetter, idgetter): untyped =
   let how = howExpr
   var val = newStringOfCap(how.len)
@@ -32,7 +20,7 @@ template formatStr*(howExpr, namegetter, idgetter): untyped =
         i += 2
       elif how[i + 1] == '#':
         var id {.inject.} = lastNum
-        val.add(checkNil(idgetter))
+        val.add(idgetter)
         lastNum += 1
         i += 2
       elif how[i + 1] in {'0'..'9'}:
@@ -41,7 +29,7 @@ template formatStr*(howExpr, namegetter, idgetter): untyped =
         while i < how.len and how[i] in {'0'..'9'}:
           id += (id * 10) + (ord(how[i]) - ord('0'))
           i += 1
-        val.add(checkNil(idgetter))
+        val.add(idgetter)
         lastNum = id + 1
       elif how[i + 1] in StartIdent:
         i += 1
@@ -49,7 +37,7 @@ template formatStr*(howExpr, namegetter, idgetter): untyped =
         while i < how.len and how[i] in Ident:
           name.add(how[i])
           i += 1
-        val.add(checkNil(namegetter))
+        val.add(namegetter)
       elif how[i + 1] == '{':
         i += 2
         var name {.inject.} = ""
@@ -57,7 +45,7 @@ template formatStr*(howExpr, namegetter, idgetter): untyped =
           name.add(how[i])
           i += 1
         i += 1
-        val.add(checkNil(namegetter))
+        val.add(namegetter)
       else:
-        raise newException(Exception, "Syntax error in format string at " & $i)
+        raise newException(ValueError, "Syntax error in format string at " & $i)
   val
diff --git a/lib/impure/osinfo_posix.nim b/lib/impure/osinfo_posix.nim
deleted file mode 100644
index 0362fca12..000000000
--- a/lib/impure/osinfo_posix.nim
+++ /dev/null
@@ -1,10 +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.
-#
-
-{.error: "This module has been moved to the 'osinfo' nimble package.".}
diff --git a/lib/impure/osinfo_win.nim b/lib/impure/osinfo_win.nim
deleted file mode 100644
index 0362fca12..000000000
--- a/lib/impure/osinfo_win.nim
+++ /dev/null
@@ -1,10 +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.
-#
-
-{.error: "This module has been moved to the 'osinfo' nimble package.".}
diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim
index 54bab82f0..f4fc26380 100644
--- a/lib/impure/rdstdin.nim
+++ b/lib/impure/rdstdin.nim
@@ -9,92 +9,66 @@
 
 ## This module contains code for reading from `stdin`:idx:. On UNIX the
 ## linenoise library is wrapped and set up to provide default key bindings
-## (e.g. you can navigate with the arrow keys). On Windows ``system.readLine``
+## (e.g. you can navigate with the arrow keys). On Windows `system.readLine`
 ## is used. This suffices because Windows' console already provides the
 ## wanted functionality.
 
-{.deadCodeElim: on.}  # dce option deprecated
+runnableExamples("-r:off"):
+  echo readLineFromStdin("Is Nim awesome? (Y/n): ")
+  var line: string
+  while true:
+    let ok = readLineFromStdin("How are you? ", line)
+    if not ok: break # ctrl-C or ctrl-D will cause a break
+    if line.len > 0: echo line
+  echo "exiting"
 
-when defined(Windows):
-  proc readLineFromStdin*(prompt: string): TaintedString {.
+
+when defined(windows):
+  when defined(nimPreviewSlimSystem):
+    import std/syncio
+
+  proc readLineFromStdin*(prompt: string): string {.
                           tags: [ReadIOEffect, WriteIOEffect].} =
     ## Reads a line from stdin.
     stdout.write(prompt)
+    stdout.flushFile()
     result = readLine(stdin)
 
-  proc readLineFromStdin*(prompt: string, line: var TaintedString): bool {.
+  proc readLineFromStdin*(prompt: string, line: var string): bool {.
                           tags: [ReadIOEffect, WriteIOEffect].} =
     ## Reads a `line` from stdin. `line` must not be
-    ## ``nil``! May throw an IO exception.
-    ## A line of text may be delimited by ``CR``, ``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.
+    ## `nil`! May throw an IO exception.
+    ## A line of text may be delimited by `CR`, `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.
     stdout.write(prompt)
     result = readLine(stdin, line)
 
-  import winlean
-
-  const
-    VK_SHIFT* = 16
-    VK_CONTROL* = 17
-    VK_MENU* = 18
-    KEY_EVENT* = 1
-
-  type
-    KEY_EVENT_RECORD = object
-      bKeyDown: WinBool
-      wRepeatCount: uint16
-      wVirtualKeyCode: uint16
-      wVirtualScanCode: uint16
-      unicodeChar: uint16
-      dwControlKeyState: uint32
-    INPUT_RECORD = object
-      eventType*: int16
-      reserved*: int16
-      event*: KEY_EVENT_RECORD
-      safetyBuffer: array[0..5, DWORD]
-
-  proc readConsoleInputW*(hConsoleInput: HANDLE, lpBuffer: var INPUTRECORD,
-                          nLength: uint32,
-                          lpNumberOfEventsRead: var uint32): WINBOOL{.
-      stdcall, dynlib: "kernel32", importc: "ReadConsoleInputW".}
-
-  proc getch(): uint16 =
-    let hStdin = getStdHandle(STD_INPUT_HANDLE)
-    var
-      irInputRecord: INPUT_RECORD
-      dwEventsRead: uint32
+elif defined(genode):
+  proc readLineFromStdin*(prompt: string): string {.
+                          tags: [ReadIOEffect, WriteIOEffect].} =
+    stdin.readLine()
 
-    while readConsoleInputW(hStdin, irInputRecord, 1, dwEventsRead) != 0:
-      if irInputRecord.eventType == KEY_EVENT and
-          irInputRecord.event.wVirtualKeyCode notin {VK_SHIFT, VK_MENU, VK_CONTROL}:
-         result = irInputRecord.event.unicodeChar
-         discard readConsoleInputW(hStdin, irInputRecord, 1, dwEventsRead)
-         return result
+  proc readLineFromStdin*(prompt: string, line: var string): bool {.
+                          tags: [ReadIOEffect, WriteIOEffect].} =
+    stdin.readLine(line)
 
 else:
-  import linenoise, termios
+  import std/linenoise
 
-  proc readLineFromStdin*(prompt: string): TaintedString {.
+  proc readLineFromStdin*(prompt: string, line: var string): bool {.
                           tags: [ReadIOEffect, WriteIOEffect].} =
     var buffer = linenoise.readLine(prompt)
     if isNil(buffer):
-      raise newException(IOError, "Linenoise returned nil")
-    result = TaintedString($buffer)
-    if result.string.len > 0:
-      historyAdd(buffer)
-    linenoise.free(buffer)
-
-  proc readLineFromStdin*(prompt: string, line: var TaintedString): bool {.
-                          tags: [ReadIOEffect, WriteIOEffect].} =
-    var buffer = linenoise.readLine(prompt)
-    if isNil(buffer):
-      line.string.setLen(0)
+      line.setLen(0)
       return false
-    line = TaintedString($buffer)
-    if line.string.len > 0:
+    line = $buffer
+    if line.len > 0:
       historyAdd(buffer)
     linenoise.free(buffer)
     result = true
 
+  proc readLineFromStdin*(prompt: string): string {.inline.} =
+    if not readLineFromStdin(prompt, result):
+      raise newException(IOError, "Linenoise returned nil")
diff --git a/lib/impure/re.nim b/lib/impure/re.nim
index 201c490f3..053c6ab55 100644
--- a/lib/impure/re.nim
+++ b/lib/impure/re.nim
@@ -7,6 +7,9 @@
 #    distribution, for details about the copyright.
 #
 
+when defined(js):
+  {.error: "This library needs to be compiled with a c-like backend, and depends on PCRE; See jsre for JS backend.".}
+
 ## Regular expression support for Nim.
 ##
 ## This module is implemented by providing a wrapper around the
@@ -14,27 +17,43 @@
 ## C library. This means that your application will depend on the PCRE
 ## library's licence when using this module, which should not be a problem
 ## though.
+##
+## .. note:: There are also alternative nimble packages such as [tinyre](https://github.com/khchen/tinyre)
+##   and [regex](https://github.com/nitely/nim-regex).
+##
 ## PCRE's licence follows:
 ##
 ## .. include:: ../../doc/regexprs.txt
 ##
 
+runnableExamples:
+  ## Unless specified otherwise, `start` parameter in each proc indicates
+  ## where the scan starts, but outputs are relative to the start of the input
+  ## string, not to `start`:
+  doAssert find("uxabc", re"(?<=x|y)ab", start = 1) == 2 # lookbehind assertion
+  doAssert find("uxabc", re"ab", start = 3) == -1 # we're past `start` => not found
+  doAssert not match("xabc", re"^abc$", start = 1)
+    # can't match start of string since we're starting at 1
+
 import
-  pcre, strutils, rtarrays
+  std/[pcre, strutils, rtarrays]
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
 
 const
   MaxSubpatterns* = 20
     ## defines the maximum number of subpatterns that can be captured.
-    ## This limit still exists for ``replacef`` and ``parallelReplace``.
+    ## This limit still exists for `replacef` and `parallelReplace`.
 
 type
   RegexFlag* = enum     ## options for regular expressions
-    reIgnoreCase = 0,    ## do caseless matching
-    reMultiLine = 1,     ## ``^`` and ``$`` match newlines within data
-    reDotAll = 2,        ## ``.`` matches anything including NL
-    reExtended = 3,      ## ignore whitespace and ``#`` comments
-    reStudy = 4          ## study the expression (may be omitted if the
-                         ## expression will be used only once)
+    reIgnoreCase = 0,   ## do caseless matching
+    reMultiLine = 1,    ## `^` and `$` match newlines within data
+    reDotAll = 2,       ## `.` matches anything including NL
+    reExtended = 3,     ## ignore whitespace and `#` comments
+    reStudy = 4         ## study the expression (may be omitted if the
+                        ## expression will be used only once)
 
   RegexDesc = object
     h: ptr Pcre
@@ -45,6 +64,18 @@ type
   RegexError* = object of ValueError
     ## is raised if the pattern is no valid regular expression.
 
+when defined(gcDestructors):
+  when defined(nimAllowNonVarDestructor):
+    proc `=destroy`(x: RegexDesc) =
+      pcre.free_substring(cast[cstring](x.h))
+      if not isNil(x.e):
+        pcre.free_study(x.e)
+  else:
+    proc `=destroy`(x: var RegexDesc) =
+      pcre.free_substring(cast[cstring](x.h))
+      if not isNil(x.e):
+        pcre.free_study(x.e)
+
 proc raiseInvalidRegex(msg: string) {.noinline, noreturn.} =
   var e: ref RegexError
   new(e)
@@ -61,19 +92,26 @@ proc rawCompile(pattern: string, flags: cint): ptr Pcre =
 
 proc finalizeRegEx(x: Regex) =
   # XXX This is a hack, but PCRE does not export its "free" function properly.
-  # Sigh. The hack relies on PCRE's implementation (see ``pcre_get.c``).
+  # Sigh. The hack relies on PCRE's implementation (see `pcre_get.c`).
   # Fortunately the implementation is unlikely to change.
   pcre.free_substring(cast[cstring](x.h))
   if not isNil(x.e):
-    pcre.free_substring(cast[cstring](x.e))
+    pcre.free_study(x.e)
 
 proc re*(s: string, flags = {reStudy}): Regex =
   ## Constructor of regular expressions.
   ##
   ## Note that Nim's
-  ## extended raw string literals support the syntax ``re"[abc]"`` as
-  ## a short form for ``re(r"[abc]")``.
-  new(result, finalizeRegEx)
+  ## extended raw string literals support the syntax `re"[abc]"` as
+  ## a short form for `re(r"[abc]")`. Also note that since this
+  ## compiles the regular expression, which is expensive, you should
+  ## avoid putting it directly in the arguments of the functions like
+  ## the examples show below if you plan to use it a lot of times, as
+  ## this will hurt performance immensely. (e.g. outside a loop, ...)
+  when defined(gcDestructors):
+    result = Regex()
+  else:
+    new(result, finalizeRegEx)
   result.h = rawCompile(s, cast[cint](flags - {reStudy}))
   if reStudy in flags:
     var msg: cstring = ""
@@ -97,7 +135,7 @@ proc bufSubstr(b: cstring, sPos, ePos: int): string {.inline.} =
   ## Don't assume cstring is '\0' terminated
   let sz = ePos - sPos
   result = newString(sz+1)
-  copyMem(addr(result[0]), unsafeaddr(b[sPos]), sz)
+  copyMem(addr(result[0]), unsafeAddr(b[sPos]), sz)
   result.setLen(sz)
 
 proc matchOrFind(buf: cstring, pattern: Regex, matches: var openArray[string],
@@ -113,16 +151,23 @@ proc matchOrFind(buf: cstring, pattern: Regex, matches: var openArray[string],
     var b = rawMatches[i * 2 + 1]
     if a >= 0'i32:
       matches[i-1] = bufSubstr(buf, int(a), int(b))
-    else: matches[i-1] = nil
+    else: matches[i-1] = ""
   return rawMatches[1] - rawMatches[0]
 
+const MaxReBufSize* = high(cint)
+  ## Maximum PCRE (API 1) buffer start/size equal to `high(cint)`, which even
+  ## for 64-bit systems can be either 2`31`:sup:-1 or 2`63`:sup:-1.
+
 proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[string],
                  start = 0, bufSize: int): tuple[first, last: int] =
-  ## returns the starting position and end position of ``pattern`` in ``buf``
-  ## (where ``buf`` has length ``bufSize`` and is not necessarily ``'\0'`` terminated),
+  ## returns the starting position and end position of `pattern` in `buf`
+  ## (where `buf` has length `bufSize` and is not necessarily `'\0'` terminated),
   ## and the captured
-  ## substrings in the array ``matches``. If it does not match, nothing
-  ## is written into ``matches`` and ``(-1,0)`` is returned.
+  ## substrings in the array `matches`. If it does not match, nothing
+  ## is written into `matches` and `(-1,0)` is returned.
+  ##
+  ## Note: The memory for `matches` needs to be allocated before this function is
+  ## called, otherwise it will just remain empty.
   var
     rtarray = initRtArray[cint]((matches.len+1)*3)
     rawMatches = rtarray.getRawData
@@ -133,25 +178,36 @@ proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[string],
     var a = rawMatches[i * 2]
     var b = rawMatches[i * 2 + 1]
     if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b))
-    else: matches[i-1] = nil
+    else: matches[i-1] = ""
   return (rawMatches[0].int, rawMatches[1].int - 1)
 
 proc findBounds*(s: string, pattern: Regex, matches: var openArray[string],
                  start = 0): tuple[first, last: int] {.inline.} =
-  ## returns the starting position and end position of ``pattern`` in ``s``
-  ## and the captured substrings in the array ``matches``.
+  ## returns the starting position and end position of `pattern` in `s`
+  ## and the captured substrings in the array `matches`.
   ## If it does not match, nothing
-  ## is written into ``matches`` and ``(-1,0)`` is returned.
-  result = findBounds(cstring(s), pattern, matches, start, s.len)
+  ## is written into `matches` and `(-1,0)` is returned.
+  ##
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
+  runnableExamples:
+    var matches = newSeq[string](1)
+    let (first, last) = findBounds("Hello World", re"(W\w+)", matches)
+    doAssert first == 6
+    doAssert last == 10
+    doAssert matches[0] == "World"
+  result = findBounds(cstring(s), pattern, matches,
+      min(start, MaxReBufSize), min(s.len, MaxReBufSize))
 
 proc findBounds*(buf: cstring, pattern: Regex,
                  matches: var openArray[tuple[first, last: int]],
-                 start = 0, bufSize = 0): tuple[first, last: int] =
-  ## returns the starting position and end position of ``pattern`` in ``buf``
-  ## (where ``buf`` has length ``bufSize`` and is not necessarily ``'\0'`` terminated),
-  ## and the captured substrings in the array ``matches``.
-  ## If it does not match, nothing is written into ``matches`` and
-  ## ``(-1,0)`` is returned.
+                 start = 0, bufSize: int): tuple[first, last: int] =
+  ## returns the starting position and end position of `pattern` in `buf`
+  ## (where `buf` has length `bufSize` and is not necessarily `'\0'` terminated),
+  ## and the captured substrings in the array `matches`.
+  ## If it does not match, nothing is written into `matches` and
+  ## `(-1,0)` is returned.
+  ##
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
   var
     rtarray = initRtArray[cint]((matches.len+1)*3)
     rawMatches = rtarray.getRawData
@@ -168,17 +224,38 @@ proc findBounds*(buf: cstring, pattern: Regex,
 proc findBounds*(s: string, pattern: Regex,
                  matches: var openArray[tuple[first, last: int]],
                  start = 0): tuple[first, last: int] {.inline.} =
-  ## returns the starting position and end position of ``pattern`` in ``s``
-  ## and the captured substrings in the array ``matches``.
-  ## If it does not match, nothing is written into ``matches`` and
-  ## ``(-1,0)`` is returned.
-  result = findBounds(cstring(s), pattern, matches, start, s.len)
+  ## returns the starting position and end position of `pattern` in `s`
+  ## and the captured substrings in the array `matches`.
+  ## If it does not match, nothing is written into `matches` and
+  ## `(-1,0)` is returned.
+  ##
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
+  runnableExamples:
+    var matches = newSeq[tuple[first, last: int]](1)
+    let (first, last) = findBounds("Hello World", re"(\w+)", matches)
+    doAssert first == 0
+    doAssert last == 4
+    doAssert matches[0] == (0, 4)
+  result = findBounds(cstring(s), pattern, matches,
+      min(start, MaxReBufSize), min(s.len, MaxReBufSize))
+
+proc findBoundsImpl(buf: cstring, pattern: Regex,
+                    start = 0, bufSize = 0, flags = 0): tuple[first, last: int] =
+  var rtarray = initRtArray[cint](3)
+  let rawMatches = rtarray.getRawData
+  let res = pcre.exec(pattern.h, pattern.e, buf, bufSize.cint, start.cint, flags.int32,
+                cast[ptr cint](rawMatches), 3)
+
+  if res < 0'i32:
+    result = (-1, 0)
+  else:
+    result = (int(rawMatches[0]), int(rawMatches[1]-1))
 
 proc findBounds*(buf: cstring, pattern: Regex,
                  start = 0, bufSize: int): tuple[first, last: int] =
-  ## returns the ``first`` and ``last`` position of ``pattern`` in ``buf``,
-  ## where ``buf`` has length ``bufSize`` (not necessarily ``'\0'`` terminated).
-  ## If it does not match, ``(-1,0)`` is returned.
+  ## returns the `first` and `last` position of `pattern` in `buf`,
+  ## where `buf` has length `bufSize` (not necessarily `'\0'` terminated).
+  ## If it does not match, `(-1,0)` is returned.
   var
     rtarray = initRtArray[cint](3)
     rawMatches = rtarray.getRawData
@@ -189,16 +266,14 @@ proc findBounds*(buf: cstring, pattern: Regex,
 
 proc findBounds*(s: string, pattern: Regex,
                  start = 0): tuple[first, last: int] {.inline.} =
-  ## returns the ``first`` and ``last`` position of ``pattern`` in ``s``.
-  ## If it does not match, ``(-1,0)`` is returned.
+  ## returns the `first` and `last` position of `pattern` in `s`.
+  ## If it does not match, `(-1,0)` is returned.
   ##
   ## Note: there is a speed improvement if the matches do not need to be captured.
-  ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   assert findBounds("01234abc89", re"abc") == (5,7)
-  result = findBounds(cstring(s), pattern, start, s.len)
+  runnableExamples:
+    assert findBounds("01234abc89", re"abc") == (5,7)
+  result = findBounds(cstring(s), pattern,
+      min(start, MaxReBufSize), min(s.len, MaxReBufSize))
 
 proc matchOrFind(buf: cstring, pattern: Regex, start, bufSize: int, flags: cint): cint =
   var
@@ -211,72 +286,77 @@ proc matchOrFind(buf: cstring, pattern: Regex, start, bufSize: int, flags: cint)
 
 proc matchLen*(s: string, pattern: Regex, matches: var openArray[string],
               start = 0): int {.inline.} =
-  ## the same as ``match``, but it returns the length of the match,
-  ## if there is no match, ``-1`` is returned. Note that a match length
+  ## the same as `match`, but it returns the length of the match,
+  ## if there is no match, `-1` is returned. Note that a match length
   ## of zero can happen.
+  ##
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
   result = matchOrFind(cstring(s), pattern, matches, start.cint, s.len.cint, pcre.ANCHORED)
 
 proc matchLen*(buf: cstring, pattern: Regex, matches: var openArray[string],
               start = 0, bufSize: int): int {.inline.} =
-  ## the same as ``match``, but it returns the length of the match,
-  ## if there is no match, ``-1`` is returned. Note that a match length
+  ## the same as `match`, but it returns the length of the match,
+  ## if there is no match, `-1` is returned. Note that a match length
   ## of zero can happen.
+  ##
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
   return matchOrFind(buf, pattern, matches, start.cint, bufSize.cint, pcre.ANCHORED)
 
 proc matchLen*(s: string, pattern: Regex, start = 0): int {.inline.} =
-  ## the same as ``match``, but it returns the length of the match,
-  ## if there is no match, ``-1`` is returned. Note that a match length
+  ## the same as `match`, but it returns the length of the match,
+  ## if there is no match, `-1` is returned. Note that a match length
   ## of zero can happen.
   ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   echo matchLen("abcdefg", re"cde", 2)  # =>  3
-  ##   echo matchLen("abcdefg", re"abcde")   # =>  5
-  ##   echo matchLen("abcdefg", re"cde")     # => -1
+  runnableExamples:
+    doAssert matchLen("abcdefg", re"cde", 2) == 3
+    doAssert matchLen("abcdefg", re"abcde") == 5
+    doAssert matchLen("abcdefg", re"cde") == -1
   result = matchOrFind(cstring(s), pattern, start.cint, s.len.cint, pcre.ANCHORED)
 
 proc matchLen*(buf: cstring, pattern: Regex, start = 0, bufSize: int): int {.inline.} =
-  ## the same as ``match``, but it returns the length of the match,
-  ## if there is no match, ``-1`` is returned. Note that a match length
+  ## the same as `match`, but it returns the length of the match,
+  ## if there is no match, `-1` is returned. Note that a match length
   ## of zero can happen.
   result = matchOrFind(buf, pattern, start.cint, bufSize, pcre.ANCHORED)
 
 proc match*(s: string, pattern: Regex, start = 0): bool {.inline.} =
-  ## returns ``true`` if ``s[start..]`` matches the ``pattern``.
+  ## returns `true` if `s[start..]` matches the `pattern`.
   result = matchLen(cstring(s), pattern, start, s.len) != -1
 
 proc match*(s: string, pattern: Regex, matches: var openArray[string],
            start = 0): bool {.inline.} =
-  ## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
-  ## the captured substrings in the array ``matches``. If it does not
-  ## match, nothing is written into ``matches`` and ``false`` is
+  ## returns `true` if `s[start..]` matches the `pattern` and
+  ## the captured substrings in the array `matches`. If it does not
+  ## match, nothing is written into `matches` and `false` is
   ## returned.
   ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   var matches: array[2, string]
-  ##   if match("abcdefg", re"c(d)ef(g)", matches, 2):
-  ##     for s in matches:
-  ##       echo s       # => d g
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
+  runnableExamples:
+    import std/sequtils
+    var matches: array[2, string]
+    if match("abcdefg", re"c(d)ef(g)", matches, 2):
+      doAssert toSeq(matches) == @["d", "g"]
   result = matchLen(cstring(s), pattern, matches, start, s.len) != -1
 
 proc match*(buf: cstring, pattern: Regex, matches: var openArray[string],
            start = 0, bufSize: int): bool {.inline.} =
-  ## returns ``true`` if ``buf[start..<bufSize]`` matches the ``pattern`` and
-  ## the captured substrings in the array ``matches``. If it does not
-  ## match, nothing is written into ``matches`` and ``false`` is
+  ## returns `true` if `buf[start..<bufSize]` matches the `pattern` and
+  ## the captured substrings in the array `matches`. If it does not
+  ## match, nothing is written into `matches` and `false` is
   ## returned.
-  ## ``buf`` has length ``bufSize`` (not necessarily ``'\0'`` terminated).
+  ## `buf` has length `bufSize` (not necessarily `'\0'` terminated).
+  ##
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
   result = matchLen(buf, pattern, matches, start, bufSize) != -1
 
 proc find*(buf: cstring, pattern: Regex, matches: var openArray[string],
-           start = 0, bufSize = 0): int =
-  ## returns the starting position of ``pattern`` in ``buf`` and the captured
-  ## substrings in the array ``matches``. If it does not match, nothing
-  ## is written into ``matches`` and ``-1`` is returned.
-  ## ``buf`` has length ``bufSize`` (not necessarily ``'\0'`` terminated).
+           start = 0, bufSize: int): int =
+  ## returns the starting position of `pattern` in `buf` and the captured
+  ## substrings in the array `matches`. If it does not match, nothing
+  ## is written into `matches` and `-1` is returned.
+  ## `buf` has length `bufSize` (not necessarily `'\0'` terminated).
+  ##
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
   var
     rtarray = initRtArray[cint]((matches.len+1)*3)
     rawMatches = rtarray.getRawData
@@ -287,20 +367,22 @@ proc find*(buf: cstring, pattern: Regex, matches: var openArray[string],
     var a = rawMatches[i * 2]
     var b = rawMatches[i * 2 + 1]
     if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b))
-    else: matches[i-1] = nil
+    else: matches[i-1] = ""
   return rawMatches[0]
 
 proc find*(s: string, pattern: Regex, matches: var openArray[string],
            start = 0): int {.inline.} =
-  ## returns the starting position of ``pattern`` in ``s`` and the captured
-  ## substrings in the array ``matches``. If it does not match, nothing
-  ## is written into ``matches`` and ``-1`` is returned.
+  ## returns the starting position of `pattern` in `s` and the captured
+  ## substrings in the array `matches`. If it does not match, nothing
+  ## is written into `matches` and `-1` is returned.
+  ##
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
   result = find(cstring(s), pattern, matches, start, s.len)
 
 proc find*(buf: cstring, pattern: Regex, start = 0, bufSize: int): int =
-  ## returns the starting position of ``pattern`` in ``buf``,
-  ## where ``buf`` has length ``bufSize`` (not necessarily ``'\0'`` terminated).
-  ## If it does not match, ``-1`` is returned.
+  ## returns the starting position of `pattern` in `buf`,
+  ## where `buf` has length `bufSize` (not necessarily `'\0'` terminated).
+  ## If it does not match, `-1` is returned.
   var
     rtarray = initRtArray[cint](3)
     rawMatches = rtarray.getRawData
@@ -310,15 +392,16 @@ proc find*(buf: cstring, pattern: Regex, start = 0, bufSize: int): int =
   return rawMatches[0]
 
 proc find*(s: string, pattern: Regex, start = 0): int {.inline.} =
-  ## returns the starting position of ``pattern`` in ``s``. If it does not
-  ## match, ``-1`` is returned.
-  ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##  echo find("abcdefg", re"cde")  # => 2
-  ##  echo find("abcdefg", re"abc")  # => 0
-  ##  echo find("abcdefg", re"zz")  # => -1
+  ## returns the starting position of `pattern` in `s`. If it does not
+  ## match, `-1` is returned. We start the scan at `start`.
+  runnableExamples:
+    doAssert find("abcdefg", re"cde") == 2
+    doAssert find("abcdefg", re"abc") == 0
+    doAssert find("abcdefg", re"zz") == -1 # not found
+    doAssert find("abcdefg", re"cde", start = 2) == 2 # still 2
+    doAssert find("abcdefg", re"cde", start = 3) == -1 # we're past the start position
+    doAssert find("xabc", re"(?<=x|y)abc", start = 1) == 1
+      # lookbehind assertion `(?<=x|y)` can look behind `start`
   result = find(cstring(s), pattern, start, s.len)
 
 iterator findAll*(s: string, pattern: Regex, start = 0): string =
@@ -341,7 +424,7 @@ iterator findAll*(s: string, pattern: Regex, start = 0): string =
     i = b
 
 iterator findAll*(buf: cstring, pattern: Regex, start = 0, bufSize: int): string =
-  ## Yields all matching `substrings` of ``s`` that match ``pattern``.
+  ## Yields all matching `substrings` of `s` that match `pattern`.
   ##
   ## Note that since this is an iterator you should not modify the string you
   ## are iterating over: bad things could happen.
@@ -362,31 +445,25 @@ iterator findAll*(buf: cstring, pattern: Regex, start = 0, bufSize: int): string
     i = b
 
 proc findAll*(s: string, pattern: Regex, start = 0): seq[string] {.inline.} =
-  ## returns all matching `substrings` of ``s`` that match ``pattern``.
-  ## If it does not match, @[] is returned.
-  accumulateResult(findAll(s, pattern, start))
-
-when not defined(nimhygiene):
-  {.pragma: inject.}
+  ## returns all matching `substrings` of `s` that match `pattern`.
+  ## If it does not match, `@[]` is returned.
+  result = @[]
+  for x in findAll(s, pattern, start): result.add x
 
 template `=~` *(s: string, pattern: Regex): untyped =
-  ## This calls ``match`` with an implicit declared ``matches`` array that
-  ## can be used in the scope of the ``=~`` call:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   if line =~ re"\s*(\w+)\s*\=\s*(\w+)":
-  ##     # matches a key=value pair:
-  ##     echo("Key: ", matches[0])
-  ##     echo("Value: ", matches[1])
-  ##   elif line =~ re"\s*(\#.*)":
-  ##     # matches a comment
-  ##     # note that the implicit ``matches`` array is different from the
-  ##     # ``matches`` array of the first branch
-  ##     echo("comment: ", matches[0])
-  ##   else:
-  ##     echo("syntax error")
-  ##
+  ## This calls `match` with an implicit declared `matches` array that
+  ## can be used in the scope of the `=~` call:
+  runnableExamples:
+    proc parse(line: string): string =
+      if line =~ re"\s*(\w+)\s*\=\s*(\w+)": # matches a key=value pair:
+        result = $(matches[0], matches[1])
+      elif line =~ re"\s*(\#.*)": # matches a comment
+        # note that the implicit `matches` array is different from 1st branch
+        result = $(matches[0],)
+      else: raiseAssert "unreachable"
+      doAssert not declared(matches)
+    doAssert parse("NAME = LENA") == """("NAME", "LENA")"""
+    doAssert parse("   # comment ... ") == """("# comment ... ",)"""
   bind MaxSubpatterns
   when not declaredInScope(matches):
     var matches {.inject.}: array[MaxSubpatterns, string]
@@ -395,12 +472,14 @@ template `=~` *(s: string, pattern: Regex): untyped =
 # ------------------------- more string handling ------------------------------
 
 proc contains*(s: string, pattern: Regex, start = 0): bool {.inline.} =
-  ## same as ``find(s, pattern, start) >= 0``
+  ## same as `find(s, pattern, start) >= 0`
   return find(s, pattern, start) >= 0
 
 proc contains*(s: string, pattern: Regex, matches: var openArray[string],
               start = 0): bool {.inline.} =
-  ## same as ``find(s, pattern, matches, start) >= 0``
+  ## same as `find(s, pattern, matches, start) >= 0`
+  ##
+  ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty.
   return find(s, pattern, matches, start) >= 0
 
 proc startsWith*(s: string, prefix: Regex): bool {.inline.} =
@@ -408,64 +487,52 @@ proc startsWith*(s: string, prefix: Regex): bool {.inline.} =
   result = matchLen(s, prefix) >= 0
 
 proc endsWith*(s: string, suffix: Regex): bool {.inline.} =
-  ## returns true if `s` ends with the pattern `prefix`
+  ## returns true if `s` ends with the pattern `suffix`
   for i in 0 .. s.len-1:
     if matchLen(s, suffix, i) == s.len - i: return true
 
 proc replace*(s: string, sub: Regex, by = ""): string =
-  ## Replaces ``sub`` in ``s`` by the string ``by``. Captures cannot be
-  ## accessed in ``by``.
-  ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   "var1=key; var2=key2".replace(re"(\w+)=(\w+)")
-  ##
-  ## Results in:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   "; "
+  ## Replaces `sub` in `s` by the string `by`. Captures cannot be
+  ## accessed in `by`.
+  runnableExamples:
+    doAssert "var1=key; var2=key2".replace(re"(\w+)=(\w+)") == "; "
+    doAssert "var1=key; var2=key2".replace(re"(\w+)=(\w+)", "?") == "?; ?"
   result = ""
   var prev = 0
-  while true:
-    var match = findBounds(s, sub, prev)
+  var flags = int32(0)
+  while prev < s.len:
+    var match = findBoundsImpl(s.cstring, sub, prev, s.len, flags)
+    flags = 0
     if match.first < 0: break
     add(result, substr(s, prev, match.first-1))
     add(result, by)
+    if match.first > match.last:
+      # 0-len match
+      flags = pcre.NOTEMPTY_ATSTART
     prev = match.last + 1
   add(result, substr(s, prev))
 
 proc replacef*(s: string, sub: Regex, by: string): string =
-  ## Replaces ``sub`` in ``s`` by the string ``by``. Captures can be accessed in ``by``
-  ## with the notation ``$i`` and ``$#`` (see strutils.\`%\`).
-  ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   "var1=key; var2=key2".replacef(re"(\w+)=(\w+)", "$1<-$2$2")
-  ##
-  ## Results in:
-  ##
-  ## .. code-block:: nim
-  ##
-  ## "var1<-keykey; val2<-key2key2"
+  ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
+  ## with the notation `$i` and `$#` (see strutils.\`%\`).
+  runnableExamples:
+    doAssert "var1=key; var2=key2".replacef(re"(\w+)=(\w+)", "$1<-$2$2") ==
+      "var1<-keykey; var2<-key2key2"
   result = ""
   var caps: array[MaxSubpatterns, string]
   var prev = 0
-  while true:
+  while prev < s.len:
     var match = findBounds(s, sub, caps, prev)
     if match.first < 0: break
-    assert result != nil
-    assert s != nil
     add(result, substr(s, prev, match.first-1))
     addf(result, by, caps)
+    if match.last + 1 == prev: break
     prev = match.last + 1
   add(result, substr(s, prev))
 
 proc multiReplace*(s: string, subs: openArray[
                    tuple[pattern: Regex, repl: string]]): string =
-  ## Returns a modified copy of ``s`` with the substitutions in ``subs``
+  ## Returns a modified copy of `s` with the substitutions in `subs`
   ## applied in parallel.
   result = ""
   var i = 0
@@ -483,58 +550,41 @@ proc multiReplace*(s: string, subs: openArray[
   # copy the rest:
   add(result, substr(s, i))
 
-proc parallelReplace*(s: string, subs: openArray[
-                      tuple[pattern: Regex, repl: string]]): string {.deprecated.} =
-  ## Returns a modified copy of ``s`` with the substitutions in ``subs``
-  ## applied in parallel.
-  ## **Deprecated since version 0.18.0**: Use ``multiReplace`` instead.
-  result = multiReplace(s, subs)
-
 proc transformFile*(infile, outfile: string,
                     subs: openArray[tuple[pattern: Regex, repl: string]]) =
-  ## reads in the file ``infile``, performs a parallel replacement (calls
-  ## ``parallelReplace``) and writes back to ``outfile``. Raises ``IOError`` if an
+  ## reads in the file `infile`, performs a parallel replacement (calls
+  ## `parallelReplace`) and writes back to `outfile`. Raises `IOError` if an
   ## error occurs. This is supposed to be used for quick scripting.
-  var x = readFile(infile).string
+  var x = readFile(infile)
   writeFile(outfile, x.multiReplace(subs))
 
 iterator split*(s: string, sep: Regex; maxsplit = -1): string =
-  ## Splits the string ``s`` into substrings.
-  ##
-  ## Substrings are separated by the regular expression ``sep``
-  ## (and the portion matched by ``sep`` is not returned).
-  ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   for word in split("00232this02939is39an22example111", re"\d+"):
-  ##     writeLine(stdout, word)
-  ##
-  ## Results in:
-  ##
-  ## .. code-block:: nim
-  ##   ""
-  ##   "this"
-  ##   "is"
-  ##   "an"
-  ##   "example"
-  ##   ""
-  ##
+  ## Splits the string `s` into substrings.
+  ##
+  ## Substrings are separated by the regular expression `sep`
+  ## (and the portion matched by `sep` is not returned).
+  runnableExamples:
+    import std/sequtils
+    doAssert toSeq(split("00232this02939is39an22example111", re"\d+")) ==
+      @["", "this", "is", "an", "example", ""]
   var last = 0
   var splits = maxsplit
-  var x: int
+  var x = -1
+  if len(s) == 0:
+    last = 1
+  if matchLen(s, sep, 0) == 0:
+    x = 0
   while last <= len(s):
     var first = last
     var sepLen = 1
+    if x == 0:
+      inc(last)
     while last < len(s):
       x = matchLen(s, sep, last)
       if x >= 0:
         sepLen = x
         break
       inc(last)
-    if x == 0:
-      if last >= len(s): break
-      inc last
     if splits == 0: last = len(s)
     yield substr(s, first, last-1)
     if splits == 0: break
@@ -542,13 +592,14 @@ iterator split*(s: string, sep: Regex; maxsplit = -1): string =
     inc(last, sepLen)
 
 proc split*(s: string, sep: Regex, maxsplit = -1): seq[string] {.inline.} =
-  ## Splits the string ``s`` into a seq of substrings.
+  ## Splits the string `s` into a seq of substrings.
   ##
-  ## The portion matched by ``sep`` is not returned.
-  accumulateResult(split(s, sep))
+  ## The portion matched by `sep` is not returned.
+  result = @[]
+  for x in split(s, sep, maxsplit): result.add x
 
 proc escapeRe*(s: string): string =
-  ## escapes ``s`` so that it is matched verbatim when used as a regular
+  ## escapes `s` so that it is matched verbatim when used as a regular
   ## expression.
   result = ""
   for c in items(s):
@@ -558,121 +609,3 @@ proc escapeRe*(s: string): string =
     else:
       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}))
-
-  doAssert "0158787".match(re"\d+")
-  doAssert "ABC 0232".match(re"\w+\s+\d+")
-  doAssert "ABC".match(rex"\d+ | \w+")
-
-  {.push warnings:off.}
-  doAssert matchLen("key", re(reIdentifier)) == 3
-  {.pop.}
-
-  var pattern = re"[a-z0-9]+\s*=\s*[a-z0-9]+"
-  doAssert matchLen("key1=  cal9", pattern) == 11
-
-  doAssert find("_____abc_______", re"abc") == 5
-  doAssert findBounds("_____abc_______", re"abc") == (5,7)
-
-  var matches: array[6, string]
-  if match("abcdefg", re"c(d)ef(g)", matches, 2):
-    doAssert matches[0] == "d"
-    doAssert matches[1] == "g"
-  else:
-    doAssert false
-
-  if "abc" =~ re"(a)bcxyz|(\w+)":
-    doAssert matches[1] == "abc"
-  else:
-    doAssert false
-
-  if "abc" =~ re"(cba)?.*":
-    doAssert matches[0] == nil
-  else: doAssert false
-
-  if "abc" =~ re"().*":
-    doAssert matches[0] == ""
-  else: doAssert false
-
-  doAssert "var1=key; var2=key2".endsWith(re"\w+=\w+")
-  doAssert("var1=key; var2=key2".replacef(re"(\w+)=(\w+)", "$1<-$2$2") ==
-         "var1<-keykey; var2<-key2key2")
-  doAssert("var1=key; var2=key2".replace(re"(\w+)=(\w+)", "$1<-$2$2") ==
-         "$1<-$2$2; $1<-$2$2")
-
-  var accum: seq[string] = @[]
-  for word in split("00232this02939is39an22example111", re"\d+"):
-    accum.add(word)
-  doAssert(accum == @["", "this", "is", "an", "example", ""])
-
-  accum = @[]
-  for word in split("AAA :   : BBB", re"\s*:\s*"):
-    accum.add(word)
-  doAssert(accum == @["AAA", "", "BBB"])
-
-  doAssert(split("abc", re"") == @["a", "b", "c"])
-  doAssert(split("", re"") == @[])
-
-  doAssert(split("a;b;c", re";") == @["a", "b", "c"])
-  doAssert(split(";a;b;c", re";") == @["", "a", "b", "c"])
-  doAssert(split(";a;b;c;", re";") == @["", "a", "b", "c", ""])
-  doAssert(split("a;b;c;", re";") == @["a", "b", "c", ""])
-
-  for x in findAll("abcdef", re"^{.}", 3):
-    doAssert x == "d"
-  accum = @[]
-  for x in findAll("abcdef", re".", 3):
-    accum.add(x)
-  doAssert(accum == @["d", "e", "f"])
-
-  doAssert("XYZ".find(re"^\d*") == 0)
-  doAssert("XYZ".match(re"^\d*") == true)
-
-  block:
-    var matches: array[16, string]
-    if match("abcdefghijklmnop", re"(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)(m)(n)(o)(p)", matches):
-      for i in 0..matches.high:
-        doAssert matches[i] == $chr(i + 'a'.ord)
-    else:
-      doAssert false
-
-  block:   # Buffer based RE
-    var cs: cstring = "_____abc_______"
-    doAssert(cs.find(re"abc", bufSize=15) == 5)
-    doAssert(cs.matchLen(re"_*abc", bufSize=15) == 8)
-    doAssert(cs.matchLen(re"abc", start=5, bufSize=15) == 3)
-    doAssert(cs.matchLen(re"abc", start=5, bufSize=7) == -1)
-    doAssert(cs.matchLen(re"abc_*", start=5, bufSize=10) == 5)
-    var accum: seq[string] = @[]
-    for x in cs.findAll(re"[a-z]", start=3, bufSize=15):
-      accum.add($x)
-    doAssert(accum == @["a","b","c"])
-
diff --git a/lib/impure/ssl.nim b/lib/impure/ssl.nim
deleted file mode 100644
index 5b0e899f6..000000000
--- a/lib/impure/ssl.nim
+++ /dev/null
@@ -1,100 +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
-{.deprecated: [TSecureSocket: SecureSocket].}
-
-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/asyncjs.nim b/lib/js/asyncjs.nim
index 894102ca0..9b043f3e5 100644
--- a/lib/js/asyncjs.nim
+++ b/lib/js/asyncjs.nim
@@ -11,65 +11,73 @@
 ## and libraries, writing async procedures in Nim and converting callback-based code
 ## to promises.
 ##
-## A Nim procedure is asynchronous when it includes the ``{.async.}`` pragma. It
-## should always have a ``Future[T]`` return type or not have a return type at all.
-## A ``Future[void]`` return type is assumed by default.
+## A Nim procedure is asynchronous when it includes the `{.async.}` pragma. It
+## should always have a `Future[T]` return type or not have a return type at all.
+## A `Future[void]` return type is assumed by default.
 ##
-## This is roughly equivalent to the ``async`` keyword in JavaScript code.
+## This is roughly equivalent to the `async` keyword in JavaScript code.
 ##
-## .. code-block:: nim
-##  proc loadGame(name: string): Future[Game] {.async.} =
-##    # code
+##   ```nim
+##   proc loadGame(name: string): Future[Game] {.async.} =
+##     # code
+##   ```
 ##
 ## should be equivalent to
 ##
-## .. code-block:: javascript
+##   ```javascript
 ##   async function loadGame(name) {
 ##     // code
 ##   }
+##   ```
 ##
-## A call to an asynchronous procedure usually needs ``await`` to wait for
-## the completion of the ``Future``.
+## A call to an asynchronous procedure usually needs `await` to wait for
+## the completion of the `Future`.
 ##
-## .. code-block:: nim
+##   ```nim
 ##   var game = await loadGame(name)
+##   ```
 ##
 ## Often, you might work with callback-based API-s. You can wrap them with
-## asynchronous procedures using promises and ``newPromise``:
+## asynchronous procedures using promises and `newPromise`:
 ##
-## .. code-block:: nim
+##   ```nim
 ##   proc loadGame(name: string): Future[Game] =
 ##     var promise = newPromise() do (resolve: proc(response: Game)):
 ##       cbBasedLoadGame(name) do (game: Game):
 ##         resolve(game)
 ##     return promise
+##   ```
 ##
-## Forward definitions work properly, you just need to always add the ``{.async.}`` pragma:
+## Forward definitions work properly, you just need to always add the `{.async.}` pragma:
 ##
-## .. code-block:: nim
+##   ```nim
 ##   proc loadGame(name: string): Future[Game] {.async.}
+##   ```
 ##
 ## JavaScript compatibility
-## ~~~~~~~~~~~~~~~~~~~~~~~~~
+## ========================
 ##
 ## Nim currently generates `async/await` JavaScript code which is supported in modern
 ## EcmaScript and most modern versions of browsers, Node.js and Electron.
 ## If you need to use this module with older versions of JavaScript, you can
 ## use a tool that backports the resulting JavaScript code, as babel.
 
-import jsffi
-import macros
+# xxx code: javascript above gives `LanguageXNotSupported` warning.
 
-when not defined(js) and not defined(nimdoc) and not defined(nimsuggest):
+when not defined(js) and not defined(nimsuggest):
   {.fatal: "Module asyncjs is designed to be used with the JavaScript backend.".}
 
+import std/jsffi
+import std/macros
+import std/private/since
+
 type
   Future*[T] = ref object
     future*: T
   ## Wraps the return type of an asynchronous procedure.
 
-  PromiseJs* {.importcpp: "Promise".} = ref object
-  ## A JavaScript Promise
+  PromiseJs* {.importjs: "Promise".} = ref object
+  ## A JavaScript Promise.
 
 
 proc replaceReturn(node: var NimNode) =
@@ -82,6 +90,8 @@ proc replaceReturn(node: var NimNode) =
       node[z] = nnkReturnStmt.newTree(value)
     elif son.kind == nnkAsgn and son[0].kind == nnkIdent and $son[0] == "result":
       node[z] = nnkAsgn.newTree(son[0], nnkCall.newTree(jsResolve, son[1]))
+    elif son.kind in RoutineNodes:
+      discard
     else:
       replaceReturn(son)
     inc z
@@ -92,7 +102,18 @@ proc isFutureVoid(node: NimNode): bool =
            node[1].kind == nnkIdent and $node[1] == "void"
 
 proc generateJsasync(arg: NimNode): NimNode =
-  assert arg.kind == nnkProcDef
+  if arg.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo, nnkProcTy}:
+      error("Cannot transform this node kind into an async proc." &
+            " proc/method definition or lambda node expected.")
+
+  # Transform type X = proc (): something {.async.}
+  # into      type X = proc (): Future[something]
+  if arg.kind == nnkProcTy:
+    result = arg
+    if arg[0][0].kind == nnkEmpty:
+      result[0][0] = quote do: Future[void]
+    return result
+
   result = arg
   var isVoid = false
   let jsResolve = ident("jsResolve")
@@ -108,16 +129,17 @@ proc generateJsasync(arg: NimNode): NimNode =
 
   if len(code) > 0:
     var awaitFunction = quote:
-      proc await[T](f: Future[T]): T {.importcpp: "(await #)".}
+      proc await[T](f: Future[T]): T {.importjs: "(await #)", used.}
     result.body.add(awaitFunction)
 
     var resolve: NimNode
     if isVoid:
       resolve = quote:
-        var `jsResolve` {.importcpp: "undefined".}: Future[void]
+        var `jsResolve` {.importjs: "undefined".}: Future[void]
     else:
       resolve = quote:
-        proc jsResolve[T](a: T): Future[T] {.importcpp: "#".}
+        proc jsResolve[T](a: T): Future[T] {.importjs: "#", used.}
+        proc jsResolve[T](a: Future[T]): Future[T] {.importjs: "#", used.}
     result.body.add(resolve)
   else:
     result.body = newEmptyNode()
@@ -136,13 +158,112 @@ proc generateJsasync(arg: NimNode): NimNode =
 
 macro async*(arg: untyped): untyped =
   ## Macro which converts normal procedures into
-  ## javascript-compatible async procedures
-  generateJsasync(arg)
+  ## javascript-compatible async procedures.
+  if arg.kind == nnkStmtList:
+    result = newStmtList()
+    for oneProc in arg:
+      result.add generateJsasync(oneProc)
+  else:
+    result = generateJsasync(arg)
 
-proc newPromise*[T](handler: proc(resolve: proc(response: T))): Future[T] {.importcpp: "(new Promise(#))".}
+proc newPromise*[T](handler: proc(resolve: proc(response: T))): Future[T] {.importjs: "(new Promise(#))".}
   ## A helper for wrapping callback-based functions
-  ## into promises and async procedures
+  ## into promises and async procedures.
 
-proc newPromise*(handler: proc(resolve: proc())): Future[void] {.importcpp: "(new Promise(#))".}
+proc newPromise*(handler: proc(resolve: proc())): Future[void] {.importjs: "(new Promise(#))".}
   ## A helper for wrapping callback-based functions
-  ## into promises and async procedures
+  ## into promises and async procedures.
+
+template maybeFuture(T): untyped =
+  # avoids `Future[Future[T]]`
+  when T is Future: T
+  else: Future[T]
+
+
+since (1, 5, 1):
+  #[
+  TODO:
+  * map `Promise.all()`
+  * proc toString*(a: Error): cstring {.importjs: "#.toString()".}
+
+  Note:
+  We probably can't have a `waitFor` in js in browser (single threaded), but maybe it would be possible
+  in in nodejs, see https://nodejs.org/api/child_process.html#child_process_child_process_execsync_command_options
+  and https://stackoverflow.com/questions/61377358/javascript-wait-for-async-call-to-finish-before-returning-from-function-witho
+  ]#
+
+  type Error*  {.importjs: "Error".} = ref object of JsRoot
+    ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
+    message*: cstring
+    name*: cstring
+
+  type OnReject* = proc(reason: Error)
+
+  proc then*[T](future: Future[T], onSuccess: proc, onReject: OnReject = nil): auto =
+    ## See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
+    ## Returns a `Future` from the return type of `onSuccess(T.default)`.
+    runnableExamples("-r:off"):
+      from std/sugar import `=>`
+
+      proc fn(n: int): Future[int] {.async.} =
+        if n >= 7: raise newException(ValueError, "foobar: " & $n)
+        else: result = n * 2
+
+      proc asyncFact(n: int): Future[int] {.async.} =
+        if n > 0: result = n * await asyncFact(n-1)
+        else: result = 1
+
+      proc main() {.async.} =
+        block: # then
+          assert asyncFact(3).await == 3*2
+          assert asyncFact(3).then(asyncFact).await == 6*5*4*3*2
+          let x1 = await fn(3)
+          assert x1 == 3 * 2
+          let x2 = await fn(4)
+            .then((a: int) => a.float)
+            .then((a: float) => $a)
+          assert x2 == "8.0"
+
+        block: # then with `onReject` callback
+          var witness = 1
+          await fn(6).then((a: int) => (witness = 2), (r: Error) => (witness = 3))
+          assert witness == 2
+          await fn(7).then((a: int) => (witness = 2), (r: Error) => (witness = 3))
+          assert witness == 3
+
+    template impl(call): untyped =
+      # see D20210421T014713
+      when typeof(block: call) is void:
+        var ret: Future[void]
+      else:
+        var ret = default(maybeFuture(typeof(call)))
+      typeof(ret)
+    when T is void:
+      type A = impl(onSuccess())
+    else:
+      type A = impl(onSuccess(default(T)))
+    var ret: A
+    {.emit: "`ret` = `future`.then(`onSuccess`, `onReject`);".}
+    return ret
+
+  proc catch*[T](future: Future[T], onReject: OnReject): Future[void] =
+    ## See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch
+    runnableExamples("-r:off"):
+      from std/sugar import `=>`
+      from std/strutils import contains
+
+      proc fn(n: int): Future[int] {.async.} =
+        if n >= 7: raise newException(ValueError, "foobar: " & $n)
+        else: result = n * 2
+
+      proc main() {.async.} =
+        var reason: Error
+        await fn(6).catch((r: Error) => (reason = r)) # note: `()` are needed, `=> reason = r` would not work
+        assert reason == nil
+        await fn(7).catch((r: Error) => (reason = r))
+        assert reason != nil
+        assert  "foobar: 7" in $reason.message
+
+      discard main()
+
+    {.emit: "`result` = `future`.catch(`onReject`);".}
diff --git a/lib/js/dom.nim b/lib/js/dom.nim
index fd81fdf3f..be2a34db1 100644
--- a/lib/js/dom.nim
+++ b/lib/js/dom.nim
@@ -8,37 +8,158 @@
 #
 
 ## Declaration of the Document Object Model for the `JavaScript backend
-## <backends.html#the-javascript-target>`_.
+## <backends.html#backends-the-javascript-target>`_.
+##
+##
+## Document Ready
+## --------------
+##
+## * Basic example of a document ready:
+runnableExamples"-b:js -r:off":
+  proc example(e: Event) = echo "Document is ready"
+  document.addEventListener("DOMContentLoaded", example)  # You can also use "load" event.
+## * This example runs 5 seconds after the document ready:
+runnableExamples"-b:js -r:off":
+  proc example() = echo "5 seconds after document ready"
+  proc domReady(e: Event) = discard setTimeout(example, 5_000) # Document is ready.
+  document.addEventListener("DOMContentLoaded", domReady)
+## Document onUnload
+## -----------------
+##
+## * Simple example of how to implement code that runs when the page unloads:
+runnableExamples"-b:js -r:off":
+  proc example(e: Event) = echo "Document is unloaded"
+  document.addEventListener("unload", example)  # You can also use "beforeunload".
+## Document Autorefresh
+## --------------------
+##
+## * Minimal example of a document autorefresh:
+runnableExamples"-b:js -r:off":
+  proc example() = window.location.reload()
+  discard setTimeout(example, 5_000)
+## - For more examples, see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
 
-when not defined(js) and not defined(Nimdoc):
+
+import std/private/since
+when not defined(js):
   {.error: "This module only works on the JavaScript platform".}
 
+const
+  DomApiVersion* = 3 ## the version of DOM API we try to follow. No guarantees though.
+
 type
-  EventTarget* = ref EventTargetObj
-  EventTargetObj {.importc.} = object of RootObj
-    onabort*: proc (event: Event) {.nimcall.}
-    onblur*: proc (event: Event) {.nimcall.}
-    onchange*: proc (event: Event) {.nimcall.}
-    onclick*: proc (event: Event) {.nimcall.}
-    ondblclick*: proc (event: Event) {.nimcall.}
-    onerror*: proc (event: Event) {.nimcall.}
-    onfocus*: proc (event: Event) {.nimcall.}
-    onkeydown*: proc (event: Event) {.nimcall.}
-    onkeypress*: proc (event: Event) {.nimcall.}
-    onkeyup*: proc (event: Event) {.nimcall.}
-    onload*: proc (event: Event) {.nimcall.}
-    onmousedown*: proc (event: Event) {.nimcall.}
-    onmousemove*: proc (event: Event) {.nimcall.}
-    onmouseout*: proc (event: Event) {.nimcall.}
-    onmouseover*: proc (event: Event) {.nimcall.}
-    onmouseup*: proc (event: Event) {.nimcall.}
-    onreset*: proc (event: Event) {.nimcall.}
-    onselect*: proc (event: Event) {.nimcall.}
-    onsubmit*: proc (event: Event) {.nimcall.}
-    onunload*: proc (event: Event) {.nimcall.}
-
-  Window* = ref WindowObj
-  WindowObj {.importc.} = object of EventTargetObj
+  EventTarget* {.importc.} = ref object of RootObj
+    onabort*: proc (event: Event) {.closure.}
+    onblur*: proc (event: Event) {.closure.}
+    onchange*: proc (event: Event) {.closure.}
+    onclick*: proc (event: Event) {.closure.}
+    ondblclick*: proc (event: Event) {.closure.}
+    onerror*: proc (event: Event) {.closure.}
+    onfocus*: proc (event: Event) {.closure.}
+    onkeydown*: proc (event: Event) {.closure.}
+    onkeypress*: proc (event: Event) {.closure.}
+    onkeyup*: proc (event: Event) {.closure.}
+    onload*: proc (event: Event) {.closure.}
+    onmousedown*: proc (event: Event) {.closure.}
+    onmousemove*: proc (event: Event) {.closure.}
+    onmouseout*: proc (event: Event) {.closure.}
+    onmouseover*: proc (event: Event) {.closure.}
+    onmouseup*: proc (event: Event) {.closure.}
+    onreset*: proc (event: Event) {.closure.}
+    onselect*: proc (event: Event) {.closure.}
+    onstorage*: proc (event: Event) {.closure.}
+    onsubmit*: proc (event: Event) {.closure.}
+    onunload*: proc (event: Event) {.closure.}
+    onloadstart*: proc (event: Event) {.closure.}
+    onprogress*: proc (event: Event) {.closure.}
+    onloadend*: proc (event: Event) {.closure.}
+
+  DomEvent* {.pure.} = enum
+    ## see `docs<https://developer.mozilla.org/en-US/docs/Web/Events>`_
+    Abort = "abort",
+    BeforeInput = "beforeinput",
+    Blur = "blur",
+    Click = "click",
+    CompositionEnd = "compositionend",
+    CompositionStart = "compositionstart",
+    CompositionUpdate = "compositionupdate",
+    DblClick = "dblclick",
+    Error = "error",
+    Focus = "focus",
+    FocusIn = "focusin",
+    FocusOut = "focusout",
+    Input = "input",
+    KeyDown = "keydown",
+    KeyPress = "keypress",
+    KeyUp = "keyup",
+    Load = "load",
+    MouseDown = "mousedown",
+    MouseEnter = "mouseenter",
+    MouseLeave = "mouseleave",
+    MouseMove = "mousemove",
+    MouseOut = "mouseout",
+    MouseOver = "mouseover",
+    MouseUp = "mouseup",
+    Resize = "resize",
+    Scroll = "scroll",
+    Select = "select",
+    Storage = "storage",
+    Unload = "unload",
+    Wheel = "wheel"
+
+  PerformanceMemory* {.importc.} = ref object
+    jsHeapSizeLimit*: float
+    totalJSHeapSize*: float
+    usedJSHeapSize*: float
+
+  PerformanceTiming* {.importc.} = ref object
+    connectStart*: float
+    domComplete*: float
+    domContentLoadedEventEnd*: float
+    domContentLoadedEventStart*: float
+    domInteractive*: float
+    domLoading*: float
+    domainLookupEnd*: float
+    domainLookupStart*: float
+    fetchStart*: float
+    loadEventEnd*: float
+    loadEventStart*: float
+    navigationStart*: float
+    redirectEnd*: float
+    redirectStart*: float
+    requestStart*: float
+    responseEnd*: float
+    responseStart*: float
+    secureConnectionStart*: float
+    unloadEventEnd*: float
+    unloadEventStart*: float
+
+  Performance* {.importc.} = ref object
+    memory*: PerformanceMemory
+    timing*: PerformanceTiming
+
+  Range* {.importc.} = ref object
+    ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/Range>`_
+    collapsed*: bool
+    commonAncestorContainer*: Node
+    endContainer*: Node
+    endOffset*: int
+    startContainer*: Node
+    startOffset*: int
+
+  Selection* {.importc.} = ref object
+    ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/Selection>`_
+    anchorNode*: Node
+    anchorOffset*: int
+    focusNode*: Node
+    focusOffset*: int
+    isCollapsed*: bool
+    rangeCount*: int
+    `type`*: cstring
+
+  Storage* {.importc.} = ref object
+
+  Window* {.importc.} = ref object of EventTarget
     document*: Document
     event*: Event
     history*: History
@@ -47,28 +168,29 @@ type
     defaultStatus*: cstring
     devicePixelRatio*: float
     innerHeight*, innerWidth*: int
-    locationbar*: ref TLocationBar
-    menubar*: ref TMenuBar
+    locationbar*: ref LocationBar
+    menubar*: ref MenuBar
     name*: cstring
     outerHeight*, outerWidth*: int
     pageXOffset*, pageYOffset*: int
-    personalbar*: ref TPersonalBar
-    scrollbars*: ref TScrollBars
     scrollX*: float
     scrollY*: float
-    statusbar*: ref TStatusBar
+    personalbar*: ref PersonalBar
+    scrollbars*: ref ScrollBars
+    statusbar*: ref StatusBar
     status*: cstring
-    toolbar*: ref TToolBar
-    frames*: seq[TFrame]
+    toolbar*: ref ToolBar
+    frames*: seq[Frame]
     screen*: Screen
     performance*: Performance
     onpopstate*: proc (event: Event)
+    localStorage*: Storage
+    sessionStorage*: Storage
+    parent*: Window
 
-  Frame* = ref FrameObj
-  FrameObj {.importc.} = object of WindowObj
+  Frame* {.importc.} = ref object of Window
 
-  ClassList* = ref ClassListObj
-  ClassListObj {.importc.} = object of RootObj
+  ClassList* {.importc.} = ref object of RootObj
 
   NodeType* = enum
     ElementNode = 1,
@@ -84,8 +206,7 @@ type
     DocumentFragmentNode,
     NotationNode
 
-  Node* = ref NodeObj
-  NodeObj {.importc.} = object of EventTargetObj
+  Node* {.importc.} = ref object of EventTarget
     attributes*: seq[Node]
     childNodes*: seq[Node]
     children*: seq[Node]
@@ -97,12 +218,21 @@ type
     nodeType*: NodeType
     nodeValue*: cstring
     parentNode*: Node
+    content*: Node
     previousSibling*: Node
+    ownerDocument*: Document
     innerHTML*: cstring
+    outerHTML*: cstring
+    innerText*: cstring
+    textContent*: cstring
     style*: Style
+    baseURI*: cstring
+    parentElement*: Element
+    isConnected*: bool
 
-  Document* = ref DocumentObj
-  DocumentObj {.importc.} = object of NodeObj
+  Document* {.importc.} = ref object of Node
+    activeElement*: Element
+    documentElement*: Element
     alinkColor*: cstring
     bgColor*: cstring
     body*: Element
@@ -111,11 +241,13 @@ type
     defaultCharset*: cstring
     fgColor*: cstring
     head*: Element
+    hidden*: bool
     lastModified*: cstring
     linkColor*: cstring
     referrer*: cstring
     title*: cstring
     URL*: cstring
+    visibilityState*: cstring
     vlinkColor*: cstring
     anchors*: seq[AnchorElement]
     forms*: seq[FormElement]
@@ -123,10 +255,11 @@ type
     applets*: seq[Element]
     embeds*: seq[EmbedElement]
     links*: seq[LinkElement]
+    fonts*: FontFaceSet
 
-  Element* = ref ElementObj
-  ElementObj {.importc.} = object of NodeObj
-    classList*: Classlist
+  Element* {.importc.} = ref object of Node
+    className*: cstring
+    classList*: ClassList
     checked*: bool
     defaultChecked*: bool
     defaultValue*: cstring
@@ -135,10 +268,8 @@ type
     name*: cstring
     readOnly*: bool
     options*: seq[OptionElement]
+    selectedOptions*: seq[OptionElement]
     clientWidth*, clientHeight*: int
-
-  # https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
-  HtmlElement* = ref object of Element
     contentEditable*: cstring
     isContentEditable*: bool
     dir*: cstring
@@ -147,15 +278,92 @@ type
     offsetLeft*: int
     offsetTop*: int
 
-  LinkElement* = ref LinkObj
-  LinkObj {.importc.} = object of ElementObj
+  ValidityState* {.importc.} = ref object ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/ValidityState>`_
+    badInput*: bool
+    customError*: bool
+    patternMismatch*: bool
+    rangeOverflow*: bool
+    rangeUnderflow*: bool
+    stepMismatch*: bool
+    tooLong*: bool
+    tooShort*: bool
+    typeMismatch*: bool
+    valid*: bool
+    valueMissing*: bool
+
+  Blob* {.importc.} = ref object of RootObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/Blob>`_
+    size*: int
+    `type`*: cstring
+
+  File* {.importc.} = ref object of Blob ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/File>`_
+    lastModified*: int
+    name*: cstring
+
+  TextAreaElement* {.importc.} = ref object of Element ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/HTMLTextAreaElement>`_
+    value*: cstring
+    selectionStart*, selectionEnd*: int
+    selectionDirection*: cstring
+    rows*, cols*: int
+
+  InputElement* {.importc.} = ref object of Element ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement>`_
+    # Properties related to the parent form
+    formAction*: cstring
+    formEncType*: cstring
+    formMethod*: cstring
+    formNoValidate*: bool
+    formTarget*: cstring
+
+    # Properties that apply to any type of input element that is not hidden
+    `type`*: cstring
+    autofocus*: bool
+    required*: bool
+    value*: cstring
+    validity*: ValidityState
+    validationMessage*: cstring
+    willValidate*: bool
+
+    # Properties that apply only to elements of type "checkbox" or "radio"
+    indeterminate*: bool
+
+    # Properties that apply only to elements of type "image"
+    alt*: cstring
+    height*: cstring
+    src*: cstring
+    width*: cstring
+
+    # Properties that apply only to elements of type "file"
+    accept*: cstring
+    files*: seq[Blob]
+
+    # Properties that apply only to text/number-containing or elements
+    autocomplete*: cstring
+    maxLength*: int
+    size*: int
+    pattern*: cstring
+    placeholder*: cstring
+    min*: cstring
+    max*: cstring
+    selectionStart*: int
+    selectionEnd*: int
+    selectionDirection*: cstring
+
+    # Properties not yet categorized
+    dirName*: cstring
+    accessKey*: cstring
+    list*: Element
+    multiple*: bool
+    labels*: seq[Element]
+    step*: cstring
+    valueAsDate*: cstring
+    valueAsNumber*: float
+
+  LinkElement* {.importc.} = ref object of Element
     target*: cstring
     text*: cstring
     x*: int
     y*: int
 
-  EmbedElement* = ref EmbedObj
-  EmbedObj {.importc.} = object of ElementObj
+  EmbedElement* {.importc.} = ref object of Element
     height*: int
     hspace*: int
     src*: cstring
@@ -163,35 +371,30 @@ type
     `type`*: cstring
     vspace*: int
 
-  AnchorElement* = ref AnchorObj
-  AnchorObj {.importc.} = object of ElementObj
+  AnchorElement* {.importc.} = ref object of Element
     text*: cstring
     x*, y*: int
 
-  OptionElement* = ref OptionObj
-  OptionObj {.importc.} = object of ElementObj
+  OptionElement* {.importc.} = ref object of Element
     defaultSelected*: bool
     selected*: bool
     selectedIndex*: int
     text*: cstring
     value*: cstring
 
-  TextAreaElement* = ref object of ElementObj
-    value*: cstring
-    selectionStart*, selectionEnd*: int
-    selectionDirection*: cstring
-    rows*, cols*: int
-
-  FormElement* = ref FormObj
-  FormObj {.importc.} = object of ElementObj
+  FormElement* {.importc.} = ref object of Element ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement>`_
+    acceptCharset*: cstring
     action*: cstring
+    autocomplete*: cstring
+    elements*: seq[Element]
     encoding*: cstring
+    enctype*: cstring
+    length*: int
     `method`*: cstring
+    noValidate*: bool
     target*: cstring
-    elements*: seq[Element]
 
-  ImageElement* = ref ImageObj
-  ImageObj {.importc.} = object of ElementObj
+  ImageElement* {.importc.} = ref object of Element
     border*: int
     complete*: bool
     height*: int
@@ -201,148 +404,832 @@ type
     vspace*: int
     width*: int
 
-  Style = ref StyleObj
-  StyleObj {.importc.} = object of RootObj
+  Style* {.importc.} = ref object of RootObj
+    alignContent*: cstring
+    alignItems*: cstring
+    alignSelf*: cstring
+    all*: cstring
+    animation*: cstring
+    animationDelay*: cstring
+    animationDirection*: cstring
+    animationDuration*: cstring
+    animationFillMode*: cstring
+    animationIterationCount*: cstring
+    animationName*: cstring
+    animationPlayState*: cstring
+    animationTimingFunction*: cstring
+    backdropFilter*: cstring
+    backfaceVisibility*: cstring
     background*: cstring
     backgroundAttachment*: cstring
+    backgroundBlendMode*: cstring
+    backgroundClip*: cstring
     backgroundColor*: cstring
     backgroundImage*: cstring
+    backgroundOrigin*: cstring
     backgroundPosition*: cstring
     backgroundRepeat*: cstring
+    backgroundSize*: cstring
+    blockSize*: cstring
     border*: cstring
+    borderBlock*: cstring
+    borderBlockColor*: cstring
+    borderBlockEnd*: cstring
+    borderBlockEndColor*: cstring
+    borderBlockEndStyle*: cstring
+    borderBlockEndWidth*: cstring
+    borderBlockStart*: cstring
+    borderBlockStartColor*: cstring
+    borderBlockStartStyle*: cstring
+    borderBlockStartWidth*: cstring
+    borderBlockStyle*: cstring
+    borderBlockWidth*: cstring
     borderBottom*: cstring
     borderBottomColor*: cstring
+    borderBottomLeftRadius*: cstring
+    borderBottomRightRadius*: cstring
     borderBottomStyle*: cstring
     borderBottomWidth*: cstring
+    borderCollapse*: cstring
     borderColor*: cstring
+    borderEndEndRadius*: cstring
+    borderEndStartRadius*: cstring
+    borderImage*: cstring
+    borderImageOutset*: cstring
+    borderImageRepeat*: cstring
+    borderImageSlice*: cstring
+    borderImageSource*: cstring
+    borderImageWidth*: cstring
+    borderInline*: cstring
+    borderInlineColor*: cstring
+    borderInlineEnd*: cstring
+    borderInlineEndColor*: cstring
+    borderInlineEndStyle*: cstring
+    borderInlineEndWidth*: cstring
+    borderInlineStart*: cstring
+    borderInlineStartColor*: cstring
+    borderInlineStartStyle*: cstring
+    borderInlineStartWidth*: cstring
+    borderInlineStyle*: cstring
+    borderInlineWidth*: cstring
     borderLeft*: cstring
     borderLeftColor*: cstring
     borderLeftStyle*: cstring
     borderLeftWidth*: cstring
+    borderRadius*: cstring
     borderRight*: cstring
     borderRightColor*: cstring
     borderRightStyle*: cstring
     borderRightWidth*: cstring
+    borderSpacing*: cstring
+    borderStartEndRadius*: cstring
+    borderStartStartRadius*: cstring
     borderStyle*: cstring
     borderTop*: cstring
     borderTopColor*: cstring
+    borderTopLeftRadius*: cstring
+    borderTopRightRadius*: cstring
     borderTopStyle*: cstring
     borderTopWidth*: cstring
     borderWidth*: cstring
     bottom*: cstring
+    boxDecorationBreak*: cstring
+    boxShadow*: cstring
+    boxSizing*: cstring
+    breakAfter*: cstring
+    breakBefore*: cstring
+    breakInside*: cstring
     captionSide*: cstring
+    caretColor*: cstring
     clear*: cstring
     clip*: cstring
+    clipPath*: cstring
     color*: cstring
+    colorAdjust*: cstring
+    columnCount*: cstring
+    columnFill*: cstring
+    columnGap*: cstring
+    columnRule*: cstring
+    columnRuleColor*: cstring
+    columnRuleStyle*: cstring
+    columnRuleWidth*: cstring
+    columnSpan*: cstring
+    columnWidth*: cstring
+    columns*: cstring
+    contain*: cstring
+    content*: cstring
+    counterIncrement*: cstring
+    counterReset*: cstring
+    counterSet*: cstring
     cursor*: cstring
     direction*: cstring
     display*: cstring
     emptyCells*: cstring
+    filter*: cstring
+    flex*: cstring
+    flexBasis*: cstring
+    flexDirection*: cstring
+    flexFlow*: cstring
+    flexGrow*: cstring
+    flexShrink*: cstring
+    flexWrap*: cstring
     cssFloat*: cstring
     font*: cstring
     fontFamily*: cstring
+    fontFeatureSettings*: cstring
+    fontKerning*: cstring
+    fontLanguageOverride*: cstring
+    fontOpticalSizing*: cstring
     fontSize*: cstring
+    fontSizeAdjust*: cstring
     fontStretch*: cstring
     fontStyle*: cstring
+    fontSynthesis*: cstring
     fontVariant*: cstring
+    fontVariantAlternates*: cstring
+    fontVariantCaps*: cstring
+    fontVariantEastAsian*: cstring
+    fontVariantLigatures*: cstring
+    fontVariantNumeric*: cstring
+    fontVariantPosition*: cstring
+    fontVariationSettings*: cstring
     fontWeight*: cstring
+    gap*: cstring
+    grid*: cstring
+    gridArea*: cstring
+    gridAutoColumns*: cstring
+    gridAutoFlow*: cstring
+    gridAutoRows*: cstring
+    gridColumn*: cstring
+    gridColumnEnd*: cstring
+    gridColumnStart*: cstring
+    gridRow*: cstring
+    gridRowEnd*: cstring
+    gridRowStart*: cstring
+    gridTemplate*: cstring
+    gridTemplateAreas*: cstring
+    gridTemplateColumns*: cstring
+    gridTemplateRows*: cstring
+    hangingPunctuation*: cstring
     height*: cstring
+    hyphens*: cstring
+    imageOrientation*: cstring
+    imageRendering*: cstring
+    inlineSize*: cstring
+    inset*: cstring
+    insetBlock*: cstring
+    insetBlockEnd*: cstring
+    insetBlockStart*: cstring
+    insetInline*: cstring
+    insetInlineEnd*: cstring
+    insetInlineStart*: cstring
+    isolation*: cstring
+    justifyContent*: cstring
+    justifyItems*: cstring
+    justifySelf*: cstring
     left*: cstring
     letterSpacing*: cstring
+    lineBreak*: cstring
     lineHeight*: cstring
     listStyle*: cstring
     listStyleImage*: cstring
     listStylePosition*: cstring
     listStyleType*: cstring
     margin*: cstring
+    marginBlock*: cstring
+    marginBlockEnd*: cstring
+    marginBlockStart*: cstring
     marginBottom*: cstring
+    marginInline*: cstring
+    marginInlineEnd*: cstring
+    marginInlineStart*: cstring
     marginLeft*: cstring
     marginRight*: cstring
     marginTop*: cstring
+    mask*: cstring
+    maskBorder*: cstring
+    maskBorderMode*: cstring
+    maskBorderOutset*: cstring
+    maskBorderRepeat*: cstring
+    maskBorderSlice*: cstring
+    maskBorderSource*: cstring
+    maskBorderWidth*: cstring
+    maskClip*: cstring
+    maskComposite*: cstring
+    maskImage*: cstring
+    maskMode*: cstring
+    maskOrigin*: cstring
+    maskPosition*: cstring
+    maskRepeat*: cstring
+    maskSize*: cstring
+    maskType*: cstring
+    maxBlockSize*: cstring
     maxHeight*: cstring
+    maxInlineSize*: cstring
     maxWidth*: cstring
+    minBlockSize*: cstring
     minHeight*: cstring
+    minInlineSize*: cstring
     minWidth*: cstring
+    mixBlendMode*: cstring
+    objectFit*: cstring
+    objectPosition*: cstring
+    offset*: cstring
+    offsetAnchor*: cstring
+    offsetDistance*: cstring
+    offsetPath*: cstring
+    offsetRotate*: cstring
+    opacity*: cstring
+    order*: cstring
+    orphans*: cstring
+    outline*: cstring
+    outlineColor*: cstring
+    outlineOffset*: cstring
+    outlineStyle*: cstring
+    outlineWidth*: cstring
     overflow*: cstring
+    overflowAnchor*: cstring
+    overflowBlock*: cstring
+    overflowInline*: cstring
+    overflowWrap*: cstring
     overflowX*: cstring
     overflowY*: cstring
+    overscrollBehavior*: cstring
+    overscrollBehaviorBlock*: cstring
+    overscrollBehaviorInline*: cstring
+    overscrollBehaviorX*: cstring
+    overscrollBehaviorY*: cstring
     padding*: cstring
+    paddingBlock*: cstring
+    paddingBlockEnd*: cstring
+    paddingBlockStart*: cstring
     paddingBottom*: cstring
+    paddingInline*: cstring
+    paddingInlineEnd*: cstring
+    paddingInlineStart*: cstring
     paddingLeft*: cstring
     paddingRight*: cstring
     paddingTop*: cstring
     pageBreakAfter*: cstring
     pageBreakBefore*: cstring
+    pageBreakInside*: cstring
+    paintOrder*: cstring
+    perspective*: cstring
+    perspectiveOrigin*: cstring
+    placeContent*: cstring
+    placeItems*: cstring
+    placeSelf*: cstring
+    pointerEvents*: cstring
     position*: cstring
+    quotes*: cstring
+    resize*: cstring
     right*: cstring
+    rotate*: cstring
+    rowGap*: cstring
+    scale*: cstring
+    scrollBehavior*: cstring
+    scrollMargin*: cstring
+    scrollMarginBlock*: cstring
+    scrollMarginBlockEnd*: cstring
+    scrollMarginBlockStart*: cstring
+    scrollMarginBottom*: cstring
+    scrollMarginInline*: cstring
+    scrollMarginInlineEnd*: cstring
+    scrollMarginInlineStart*: cstring
+    scrollMarginLeft*: cstring
+    scrollMarginRight*: cstring
+    scrollMarginTop*: cstring
+    scrollPadding*: cstring
+    scrollPaddingBlock*: cstring
+    scrollPaddingBlockEnd*: cstring
+    scrollPaddingBlockStart*: cstring
+    scrollPaddingBottom*: cstring
+    scrollPaddingInline*: cstring
+    scrollPaddingInlineEnd*: cstring
+    scrollPaddingInlineStart*: cstring
+    scrollPaddingLeft*: cstring
+    scrollPaddingRight*: cstring
+    scrollPaddingTop*: cstring
+    scrollSnapAlign*: cstring
+    scrollSnapStop*: cstring
+    scrollSnapType*: cstring
     scrollbar3dLightColor*: cstring
     scrollbarArrowColor*: cstring
     scrollbarBaseColor*: cstring
+    scrollbarColor*: cstring
     scrollbarDarkshadowColor*: cstring
     scrollbarFaceColor*: cstring
     scrollbarHighlightColor*: cstring
     scrollbarShadowColor*: cstring
     scrollbarTrackColor*: cstring
+    scrollbarWidth*: cstring
+    shapeImageThreshold*: cstring
+    shapeMargin*: cstring
+    shapeOutside*: cstring
+    tabSize*: cstring
     tableLayout*: cstring
     textAlign*: cstring
+    textAlignLast*: cstring
+    textCombineUpright*: cstring
     textDecoration*: cstring
+    textDecorationColor*: cstring
+    textDecorationLine*: cstring
+    textDecorationSkipInk*: cstring
+    textDecorationStyle*: cstring
+    textDecorationThickness*: cstring
+    textEmphasis*: cstring
+    textEmphasisColor*: cstring
+    textEmphasisPosition*: cstring
+    textEmphasisStyle*: cstring
     textIndent*: cstring
+    textJustify*: cstring
+    textOrientation*: cstring
+    textOverflow*: cstring
+    textRendering*: cstring
+    textShadow*: cstring
     textTransform*: cstring
+    textUnderlineOffset*: cstring
+    textUnderlinePosition*: cstring
     top*: cstring
+    touchAction*: cstring
+    transform*: cstring
+    transformBox*: cstring
+    transformOrigin*: cstring
+    transformStyle*: cstring
+    transition*: cstring
+    transitionDelay*: cstring
+    transitionDuration*: cstring
+    transitionProperty*: cstring
+    transitionTimingFunction*: cstring
+    translate*: cstring
+    unicodeBidi*: cstring
     verticalAlign*: cstring
     visibility*: cstring
+    whiteSpace*: cstring
+    widows*: cstring
     width*: cstring
+    willChange*: cstring
+    wordBreak*: cstring
     wordSpacing*: cstring
-    zIndex*: int
+    writingMode*: cstring
+    zIndex*: cstring
 
-  # TODO: A lot of the fields in Event belong to a more specific type of event.
-  # TODO: Should we clean this up?
-  Event* = ref EventObj
-  EventObj {.importc.} = object of RootObj
+  EventPhase* = enum
+    None = 0,
+    CapturingPhase,
+    AtTarget,
+    BubblingPhase
+
+  Event* {.importc.} = ref object of RootObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/Event>`_
+    bubbles*: bool
+    cancelBubble*: bool
+    cancelable*: bool
+    composed*: bool
+    currentTarget*: Node
+    defaultPrevented*: bool
+    eventPhase*: int
     target*: Node
-    altKey*, ctrlKey*, shiftKey*: bool
+    `type`*: cstring
+    isTrusted*: bool
+
+  UIEvent* {.importc.} = ref object of Event ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/UIEvent>`_
+    detail*: int64
+    view*: Window
+
+  KeyboardEvent* {.importc.} = ref object of UIEvent ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent>`_
+    altKey*, ctrlKey*, metaKey*, shiftKey*: bool
+    code*: cstring
+    isComposing*: bool
+    key*: cstring
+    keyCode*: int
+    location*: int
+
+  KeyboardEventKey* {.pure.} = enum ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values>`_
+    # Modifier keys
+    Alt,
+    AltGraph,
+    CapsLock,
+    Control,
+    Fn,
+    FnLock,
+    Hyper,
+    Meta,
+    NumLock,
+    ScrollLock,
+    Shift,
+    Super,
+    Symbol,
+    SymbolLock,
+
+    # Whitespace keys
+    ArrowDown,
+    ArrowLeft,
+    ArrowRight,
+    ArrowUp,
+    End,
+    Home,
+    PageDown,
+    PageUp,
+
+    # Editing keys
+    Backspace,
+    Clear,
+    Copy,
+    CrSel,
+    Cut,
+    Delete,
+    EraseEof,
+    ExSel,
+    Insert,
+    Paste,
+    Redo,
+    Undo,
+
+    # UI keys
+    Accept,
+    Again,
+    Attn,
+    Cancel,
+    ContextMenu,
+    Escape,
+    Execute,
+    Find,
+    Finish,
+    Help,
+    Pause,
+    Play,
+    Props,
+    Select,
+    ZoomIn,
+    ZoomOut,
+
+    # Device keys
+    BrigtnessDown,
+    BrigtnessUp,
+    Eject,
+    LogOff,
+    Power,
+    PowerOff,
+    PrintScreen,
+    Hibernate,
+    Standby,
+    WakeUp,
+
+    # Common IME keys
+    AllCandidates,
+    Alphanumeric,
+    CodeInput,
+    Compose,
+    Convert,
+    Dead,
+    FinalMode,
+    GroupFirst,
+    GroupLast,
+    GroupNext,
+    GroupPrevious,
+    ModeChange,
+    NextCandidate,
+    NonConvert,
+    PreviousCandidate,
+    Process,
+    SingleCandidate,
+
+    # Korean keyboards only
+    HangulMode,
+    HanjaMode,
+    JunjaMode,
+
+    # Japanese keyboards only
+    Eisu,
+    Hankaku,
+    Hiragana,
+    HiraganaKatakana,
+    KanaMode,
+    KanjiMode,
+    Katakana,
+    Romaji,
+    Zenkaku,
+    ZenkakuHanaku,
+
+    # Function keys
+    F1,
+    F2,
+    F3,
+    F4,
+    F5,
+    F6,
+    F7,
+    F8,
+    F9,
+    F10,
+    F11,
+    F12,
+    F13,
+    F14,
+    F15,
+    F16,
+    F17,
+    F18,
+    F19,
+    F20,
+    Soft1,
+    Soft2,
+    Soft3,
+    Soft4,
+
+    # Phone keys
+    AppSwitch,
+    Call,
+    Camera,
+    CameraFocus,
+    EndCall,
+    GoBack,
+    GoHome,
+    HeadsetHook,
+    LastNumberRedial,
+    Notification,
+    MannerMode,
+    VoiceDial,
+
+    # Multimedia keys
+    ChannelDown,
+    ChannelUp,
+    MediaFastForward,
+    MediaPause,
+    MediaPlay,
+    MediaPlayPause,
+    MediaRecord,
+    MediaRewind,
+    MediaStop,
+    MediaTrackNext,
+    MediaTrackPrevious,
+
+    # Audio control keys
+    AudioBalanceLeft,
+    AudioBalanceRight,
+    AudioBassDown,
+    AudioBassBoostDown,
+    AudioBassBoostToggle,
+    AudioBassBoostUp,
+    AudioBassUp,
+    AudioFaderFront,
+    AudioFaderRear,
+    AudioSurroundModeNext,
+    AudioTrebleDown,
+    AudioTrebleUp,
+    AudioVolumeDown,
+    AUdioVolumeMute,
+    AudioVolumeUp,
+    MicrophoneToggle,
+    MicrophoneVolumeDown,
+    MicrophoneVolumeMute,
+    MicrophoneVolumeUp,
+
+    # TV control keys
+    TV,
+    TV3DMode,
+    TVAntennaCable,
+    TVAudioDescription,
+    TVAudioDescriptionMixDown,
+    TVAudioDescriptionMixUp,
+    TVContentsMenu,
+    TVDataService,
+    TVInput,
+    TVInputComponent1,
+    TVInputComponent2,
+    TVInputComposite1,
+    TVInputComposite2,
+    TVInputHDMI1,
+    TVInputHDMI2,
+    TVInputHDMI3,
+    TVInputHDMI4,
+    TVInputVGA1,
+    TVMediaContext,
+    TVNetwork,
+    TVNumberEntry,
+    TVPower,
+    TVRadioService,
+    TVSatellite,
+    TVSatelliteBS,
+    TVSatelliteCS,
+    TVSatelliteToggle,
+    TVTerrestrialAnalog,
+    TVTerrestrialDigital,
+    TVTimer,
+
+    # Media controller keys
+    AVRInput,
+    AVRPower,
+    ColorF0Red,
+    ColorF1Green,
+    ColorF2Yellow,
+    ColorF3Blue,
+    ColorF4Grey,
+    ColorF5Brown,
+    ClosedCaptionToggle,
+    Dimmer,
+    DisplaySwap,
+    DVR,
+    Exit,
+    FavoriteClear0,
+    FavoriteClear1,
+    FavoriteClear2,
+    FavoriteClear3,
+    FavoriteRecall0,
+    FavoriteRecall1,
+    FavoriteRecall2,
+    FavoriteRecall3,
+    FavoriteStore0,
+    FavoriteStore1,
+    FavoriteStore2,
+    FavoriteStore3,
+    Guide,
+    GuideNextDay,
+    GuidePreviousDay,
+    Info,
+    InstantReplay,
+    Link,
+    ListProgram,
+    LiveContent,
+    Lock,
+    MediaApps,
+    MediaAudioTrack,
+    MediaLast,
+    MediaSkipBackward,
+    MediaSkipForward,
+    MediaStepBackward,
+    MediaStepForward,
+    MediaTopMenu,
+    NavigateIn,
+    NavigateNext,
+    NavigateOut,
+    NavigatePrevious,
+    NextFavoriteChannel,
+    NextUserProfile,
+    OnDemand,
+    Pairing,
+    PinPDown,
+    PinPMove,
+    PinPUp,
+    PlaySpeedDown,
+    PlaySpeedReset,
+    PlaySpeedUp,
+    RandomToggle,
+    RcLowBattery,
+    RecordSpeedNext,
+    RfBypass,
+    ScanChannelsToggle,
+    ScreenModeNext,
+    Settings,
+    SplitScreenToggle,
+    STBInput,
+    STBPower,
+    Subtitle,
+    Teletext,
+    VideoModeNext,
+    Wink,
+    ZoomToggle,
+
+    # Speech recognition keys
+    SpeechCorrectionList,
+    SpeechInputToggle,
+
+    # Document keys
+    Close,
+    New,
+    Open,
+    Print,
+    Save,
+    SpellCheck,
+    MailForward,
+    MailReply,
+    MailSend,
+
+    # Application selector keys
+    LaunchCalculator,
+    LaunchCalendar,
+    LaunchContacts,
+    LaunchMail,
+    LaunchMediaPlayer,
+    LaunchMusicPlayer,
+    LaunchMyComputer,
+    LaunchPhone,
+    LaunchScreenSaver,
+    LaunchSpreadsheet,
+    LaunchWebBrowser,
+    LaunchWebCam,
+    LaunchWordProcessor,
+    LaunchApplication1,
+    LaunchApplication2,
+    LaunchApplication3,
+    LaunchApplication4,
+    LaunchApplication5,
+    LaunchApplication6,
+    LaunchApplication7,
+    LaunchApplication8,
+    LaunchApplication9,
+    LaunchApplication10,
+    LaunchApplication11,
+    LaunchApplication12,
+    LaunchApplication13,
+    LaunchApplication14,
+    LaunchApplication15,
+    LaunchApplication16,
+
+    # Browser control keys
+    BrowserBack,
+    BrowserFavorites,
+    BrowserForward,
+    BrowserHome,
+    BrowserRefresh,
+    BrowserSearch,
+    BrowserStop,
+
+    # Numeric keypad keys
+    Key11,
+    Key12,
+    Separator
+
+  MouseButtons* = enum
+    NoButton = 0,
+    PrimaryButton = 1,
+    SecondaryButton = 2,
+    AuxilaryButton = 4,
+    FourthButton = 8,
+    FifthButton = 16
+
+  MouseEvent* {.importc.} = ref object of UIEvent ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent>`_
+    altKey*, ctrlKey*, metaKey*, shiftKey*: bool
     button*: int
+    buttons*: int
     clientX*, clientY*: int
-    keyCode*: int
-    layerX*, layerY*: int
-    modifiers*: int
-    ALT_MASK*, CONTROL_MASK*, SHIFT_MASK*, META_MASK*: int
+    movementX*, movementY*: int
     offsetX*, offsetY*: int
     pageX*, pageY*: int
+    relatedTarget*: EventTarget
+    #region*: cstring
     screenX*, screenY*: int
-    which*: int
-    `type`*: cstring
     x*, y*: int
-    ABORT*: int
-    BLUR*: int
-    CHANGE*: int
-    CLICK*: int
-    DBLCLICK*: int
-    DRAGDROP*: int
-    ERROR*: int
-    FOCUS*: int
-    KEYDOWN*: int
-    KEYPRESS*: int
-    KEYUP*: int
-    LOAD*: int
-    MOUSEDOWN*: int
-    MOUSEMOVE*: int
-    MOUSEOUT*: int
-    MOUSEOVER*: int
-    MOUSEUP*: int
-    MOVE*: int
-    RESET*: int
-    RESIZE*: int
-    SELECT*: int
-    SUBMIT*: int
-    UNLOAD*: int
+
+  DataTransferItemKind* {.pure.} = enum
+    File = "file",
+    String = "string"
+
+  DataTransferItem* {.importc.} = ref object of RootObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem>`_
+    kind*: cstring
+    `type`*: cstring
+
+  DataTransfer* {.importc.} = ref object of RootObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer>`_
+    dropEffect*: cstring
+    effectAllowed*: cstring
+    files*: seq[Element]
+    items*: seq[DataTransferItem]
+    types*: seq[cstring]
+
+  DataTransferDropEffect* {.pure.} = enum
+    None = "none",
+    Copy = "copy",
+    Link = "link",
+    Move = "move"
+
+  DataTransferEffectAllowed* {.pure.} = enum
+    None = "none",
+    Copy = "copy",
+    CopyLink = "copyLink",
+    CopyMove = "copyMove",
+    Link = "link",
+    LinkMove = "linkMove",
+    Move = "move",
+    All = "all",
+    Uninitialized = "uninitialized"
+
+  DragEventTypes* = enum
+    Drag = "drag",
+    DragEnd = "dragend",
+    DragEnter = "dragenter",
+    DragExit = "dragexit",
+    DragLeave = "dragleave",
+    DragOver = "dragover",
+    DragStart = "dragstart",
+    Drop = "drop"
+
+  DragEvent* {.importc.} = object of MouseEvent
+    ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/DragEvent>`_
+    dataTransfer*: DataTransfer
+
+  ClipboardEvent* {.importc.} = object of Event
+    ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent>`_
+    clipboardData*: DataTransfer
+
+  StorageEvent* {.importc.} = ref object of Event ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/StorageEvent>`_
+    key*: cstring
+    newValue*, oldValue*: cstring
+    storageArea*: Storage
+    url*: cstring
 
   TouchList* {.importc.} = ref object of RootObj
     length*: int
 
-  TouchEvent* {.importc.} = ref object of Event
-    changedTouches*, targetTouches*, touches*: TouchList
-
   Touch* {.importc.} = ref object of RootObj
     identifier*: int
     screenX*, screenY*, clientX*, clientY*, pageX*, pageY*: int
@@ -351,8 +1238,10 @@ type
     rotationAngle*: int
     force*: float
 
-  Location* = ref LocationObj
-  LocationObj {.importc.} = object of RootObj
+  TouchEvent* {.importc.} = ref object of UIEvent
+    changedTouches*, targetTouches*, touches*: seq[Touch]
+
+  Location* {.importc.} = ref object of RootObj
     hash*: cstring
     host*: cstring
     hostname*: cstring
@@ -361,43 +1250,50 @@ type
     port*: cstring
     protocol*: cstring
     search*: cstring
+    origin*: cstring
 
-  History* = ref HistoryObj
-  HistoryObj {.importc.} = object of RootObj
+  History* {.importc.} = ref object of RootObj
     length*: int
 
-  Navigator* = ref NavigatorObj
-  NavigatorObj {.importc.} = object of RootObj
+  Navigator* {.importc.} = ref object of RootObj
     appCodeName*: cstring
     appName*: cstring
     appVersion*: cstring
+    buildID*: cstring        ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/buildID
     cookieEnabled*: bool
+    deviceMemory*: float     ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/deviceMemory
+    doNotTrack*: cstring     ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/doNotTrack
     language*: cstring
+    languages*: seq[cstring] ## https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/languages
+    maxTouchPoints*: cint    ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/maxTouchPoints
+    onLine*: bool            ## https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine
+    oscpu*: cstring          ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/oscpu
     platform*: cstring
     userAgent*: cstring
-    mimeTypes*: seq[ref TMimeType]
+    vendor*: cstring         ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/vendor
+    webdriver*: bool         ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/webdriver
+    mimeTypes*: seq[ref MimeType]
 
-  TPlugin* {.importc.} = object of RootObj
+  Plugin* {.importc.} = object of RootObj
     description*: cstring
     filename*: cstring
     name*: cstring
 
-  TMimeType* {.importc.} = object of RootObj
+  MimeType* {.importc.} = object of RootObj
     description*: cstring
-    enabledPlugin*: ref TPlugin
+    enabledPlugin*: ref Plugin
     suffixes*: seq[cstring]
     `type`*: cstring
 
-  TLocationBar* {.importc.} = object of RootObj
+  LocationBar* {.importc.} = object of RootObj
     visible*: bool
-  TMenuBar* = TLocationBar
-  TPersonalBar* = TLocationBar
-  TScrollBars* = TLocationBar
-  TToolBar* = TLocationBar
-  TStatusBar* = TLocationBar
-
-  Screen = ref ScreenObj
-  ScreenObj {.importc.} = object of RootObj
+  MenuBar* = LocationBar
+  PersonalBar* = LocationBar
+  ScrollBars* = LocationBar
+  ToolBar* = LocationBar
+  StatusBar* = LocationBar
+
+  Screen* {.importc.} = ref object of RootObj
     availHeight*: int
     availWidth*: int
     colorDepth*: int
@@ -405,71 +1301,214 @@ type
     pixelDepth*: int
     width*: int
 
-  TTimeOut* {.importc.} = object of RootObj
-  TInterval* {.importc.} = object of RootObj
+  TimeOut* {.importc.} = ref object of RootObj
+  Interval* {.importc.} = ref object of RootObj
 
   AddEventListenerOptions* = object
     capture*: bool
     once*: bool
     passive*: bool
 
-  BoundingRect* {.importc.} = ref object
-    top*, bottom*, left*, right*, x*, y*, width*, height*: float
+  FontFaceSetReady* {.importc.} = ref object
+    ## see: `docs<https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/ready>`_
+    then*: proc(cb: proc())
 
-  PerformanceMemory* {.importc.} = ref object 
-    jsHeapSizeLimit*: float
-    totalJSHeapSize*: float
-    usedJSHeapSize*: float
+  FontFaceSet* {.importc.} = ref object
+    ## see: `docs<https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet>`_
+    ready*: FontFaceSetReady
+    onloadingdone*: proc(event: Event)
 
-  PerformanceTiming* {.importc.} = ref object 
-    connectStart*: float
-    domComplete*: float
-    domContentLoadedEventEnd*: float
-    domContentLoadedEventStart*: float
-    domInteractive*: float
-    domLoading*: float
-    domainLookupEnd*: float
-    domainLookupStart*: float
-    fetchStart*: float
-    loadEventEnd*: float
-    loadEventStart*: float
-    navigationStart*: float
-    redirectEnd*: float
-    redirectStart*: float
-    requestStart*: float
-    responseEnd*: float
-    responseStart*: float
-    secureConnectionStart*: float
-    unloadEventEnd*: float
-    unloadEventStart*: float
+  ScrollIntoViewOptions* = object
+    behavior*: cstring
+    `block`*: cstring
+    inline*: cstring
 
-  Performance* {.importc.} = ref object
-    memory*: PerformanceMemory
-    timing*: PerformanceTiming
+  MediaQueryList* {.importc.} = ref object of EventTarget
+    matches*: bool
+    media*: cstring
+
+since (1, 3):
+  type
+    DomParser* = ref object
+      ## DOM Parser object (defined on browser only, may not be on NodeJS).
+      ## * https://developer.mozilla.org/en-US/docs/Web/API/DOMParser
+      ##
+      ##   ```nim
+      ##   let prsr = newDomParser()
+      ##   discard prsr.parseFromString("<html><marquee>Hello World</marquee></html>".cstring, "text/html".cstring)
+      ##   ```
+
+    DomException* {.importc.} = ref object
+      ## The DOMException interface represents an abnormal event (called an exception)
+      ## which occurs as a result of calling a method or accessing a property of a web API.
+      ## Each exception has a name, which is a short "CamelCase" style string identifying
+      ## the error or abnormal condition.
+      ## https://developer.mozilla.org/en-US/docs/Web/API/DOMException
+
+    FileReader* {.importc.} = ref object of EventTarget
+      ## The FileReader object lets web applications asynchronously read the contents of files
+      ## (or raw data buffers) stored on the user's computer, using File or Blob objects to specify
+      ## the file or data to read.
+      ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader
+
+    FileReaderState* = distinct range[0'u16..2'u16]
+    RootNodeOptions* = object of RootObj
+      composed*: bool
+    DocumentOrShadowRoot* {.importc.} = object of RootObj
+      activeElement*: Element
+      # styleSheets*: StyleSheetList
+    ShadowRoot* {.importc.} = ref object of DocumentOrShadowRoot
+      delegatesFocus*: bool
+      host*: Element
+      innerHTML*: cstring
+      mode*: cstring # "open" or "closed"
+    ShadowRootInit* = object of RootObj
+      mode*: cstring
+      delegatesFocus*: bool
+
+    HTMLSlotElement* {.importc.} = ref object of RootObj
+      name*: cstring
+    SlotOptions* = object of RootObj
+      flatten*: bool
+
+  const
+    fileReaderEmpty* = 0.FileReaderState
+    fileReaderLoading* = 1.FileReaderState
+    fileReaderDone* = 2.FileReaderState
+
+proc id*(n: Node): cstring {.importcpp: "#.id", nodecl.}
+proc `id=`*(n: Node; x: cstring) {.importcpp: "#.id = #", nodecl.}
+proc class*(n: Node): cstring {.importcpp: "#.className", nodecl.}
+proc `class=`*(n: Node; v: cstring) {.importcpp: "#.className = #", nodecl.}
+
+proc value*(n: Node): cstring {.importcpp: "#.value", nodecl.}
+proc `value=`*(n: Node; v: cstring) {.importcpp: "#.value = #", nodecl.}
+
+proc checked*(n: Node): bool {.importcpp: "#.checked", nodecl.}
+proc `checked=`*(n: Node; v: bool) {.importcpp: "#.checked = #", nodecl.}
+
+proc `disabled=`*(n: Node; v: bool) {.importcpp: "#.disabled = #", nodecl.}
+
+when defined(nodejs):
+  # we provide a dummy DOM for nodejs for testing purposes
+  proc len*(x: Node): int = x.childNodes.len
+  proc `[]`*(x: Node; idx: int): Element =
+    assert idx >= 0 and idx < x.childNodes.len
+    result = cast[Element](x.childNodes[idx])
+
+  var document* = Document(nodeType: DocumentNode)
+  document.ownerDocument = document
+
+  proc getElem(x: Element; id: cstring): Element =
+    if x.id == id: return x
+    for i in 0..<x.len:
+      result = getElem(x[i], id)
+      if result != nil: return result
+
+  proc getElementById*(doc: Document; id: cstring): Element =
+    getElem(doc.body, id)
+  proc getElementById*(id: cstring): Element = document.getElementById(id)
+
+  proc appendChild*(parent, n: Node) =
+    n.parentNode = parent
+    n.ownerDocument = parent.ownerDocument
+    parent.childNodes.add n
+
+  proc replaceChild*(parent, newNode, oldNode: Node) =
+    newNode.parentNode = parent
+    oldNode.parentNode = nil
+    var i = 0
+    while i < parent.len:
+      if Node(parent[i]) == oldNode:
+        parent.childNodes[i] = newNode
+        return
+      inc i
+    raiseAssert "old node not in node list"
+
+  proc removeChild*(parent, child: Node) =
+    child.parentNode = nil
+    var i = 0
+    while i < parent.len:
+      if Node(parent[i]) == child:
+        parent.childNodes.delete(i)
+        return
+      inc i
+    raiseAssert "old node not in node list"
+
+  proc insertBefore*(parent, newNode, before: Node) =
+    appendChild(parent, newNode)
+    var i = 0
+    while i < parent.len-1:
+      if Node(parent[i]) == before:
+        for j in countdown(parent.len-1, i-1):
+          parent.childNodes[j] = parent.childNodes[j-1]
+        parent.childNodes[i-1] = newNode
+        return
+      inc i
+    #raiseAssert "before not in node list"
+
+  proc createElement*(d: Document, identifier: cstring): Element =
+    new(result)
+    result.nodeName = identifier
+    result.nodeType = NodeType.ElementNode
+
+  proc createTextNode*(d: Document, identifier: cstring): Node =
+    new(result)
+    result.nodeName = "#text"
+    result.nodeValue = identifier
+    result.nodeType = NodeType.TextNode
+
+  proc createComment*(d: Document, data: cstring): Node =
+    new(result)
+    result.nodeName = "#comment"
+    result.nodeValue = data
+    result.nodeType = NodeType.CommentNode
+
+else:
+  proc len*(x: Node): int {.importcpp: "#.childNodes.length".}
+  proc `[]`*(x: Node; idx: int): Element {.importcpp: "#.childNodes[#]".}
+  proc getElementById*(id: cstring): Element {.importc: "document.getElementById", nodecl.}
+  proc appendChild*(n, child: Node) {.importcpp.}
+  proc removeChild*(n, child: Node) {.importcpp.}
+  proc remove*(child: Node) {.importcpp.}
+  proc replaceChild*(n, newNode, oldNode: Node) {.importcpp.}
+  proc insertBefore*(n, newNode, before: Node) {.importcpp.}
+  proc getElementById*(d: Document, id: cstring): Element {.importcpp.}
+  proc createElement*(d: Document, identifier: cstring): Element {.importcpp.}
+  proc createElementNS*(d: Document, namespaceURI, qualifiedIdentifier: cstring): Element {.importcpp.}
+  proc createTextNode*(d: Document, identifier: cstring): Node {.importcpp.}
+  proc createComment*(d: Document, data: cstring): Node {.importcpp.}
+
+proc setTimeout*(action: proc(); ms: int): TimeOut {.importc, nodecl.}
+proc clearTimeout*(t: TimeOut) {.importc, nodecl.}
+proc setInterval*(action: proc(); ms: int): Interval {.importc, nodecl.}
+proc clearInterval*(i: Interval) {.importc, nodecl.}
 
 {.push importcpp.}
 
 # EventTarget "methods"
 proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
 proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), options: AddEventListenerOptions)
-proc removeEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
 proc dispatchEvent*(et: EventTarget, ev: Event)
+proc removeEventListener*(et: EventTarget; ev: cstring; cb: proc(ev: Event))
 
 # Window "methods"
 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 clearInterval*(w: Window, interval: Interval)
+proc clearTimeout*(w: Window, timeout: TimeOut)
 proc close*(w: Window)
 proc confirm*(w: Window, msg: cstring): bool
 proc disableExternalCapture*(w: Window)
 proc enableExternalCapture*(w: Window)
 proc find*(w: Window, text: cstring, caseSensitive = false,
-           backwards = false)
+           backwards = false): bool
 proc focus*(w: Window)
 proc forward*(w: Window)
+proc getComputedStyle*(w: Window, e: Node, pe: Node = nil): Style
+  ## .. warning:: The returned Style may or may not be read-only at run-time in the browser. getComputedStyle is performance costly.
+
 proc handleEvent*(w: Window, e: Event)
 proc home*(w: Window)
 proc moveBy*(w: Window, x, y: int)
@@ -478,53 +1517,69 @@ 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)
 proc scrollBy*(w: Window, x, y: int)
 proc scrollTo*(w: Window, x, y: int)
-proc setInterval*(w: Window, code: cstring, pause: int): ref TInterval
-proc setInterval*(w: Window, function: proc (), pause: int): ref TInterval
-proc setTimeout*(w: Window, code: cstring, pause: int): ref TTimeOut
-proc setTimeout*(w: Window, function: proc (), pause: int): ref TInterval
+proc setInterval*(w: Window, code: cstring, pause: int): Interval
+proc setInterval*(w: Window, function: proc (), pause: int): Interval
+proc setTimeout*(w: Window, code: cstring, pause: int): TimeOut
+proc setTimeout*(w: Window, function: proc (), pause: int): Interval
 proc stop*(w: Window)
 proc requestAnimationFrame*(w: Window, function: proc (time: float)): int
 proc cancelAnimationFrame*(w: Window, id: int)
+proc matchMedia*(w: Window, mediaQueryString: cstring): MediaQueryList
 
 # Node "methods"
-proc appendChild*(n, child: Node)
 proc appendData*(n: Node, data: cstring)
 proc cloneNode*(n: Node, copyContent: bool): Node
 proc deleteData*(n: Node, start, len: int)
+proc focus*(e: Node)
 proc getAttribute*(n: Node, attr: cstring): cstring
 proc getAttributeNode*(n: Node, attr: cstring): Node
-proc getBoundingClientRect*(e: Node): BoundingRect
+proc hasAttribute*(n: Node, attr: cstring): bool
 proc hasChildNodes*(n: Node): bool
-proc insertBefore*(n, newNode, before: Node)
+proc normalize*(n: Node)
 proc insertData*(n: Node, position: int, data: cstring)
 proc removeAttribute*(n: Node, attr: cstring)
 proc removeAttributeNode*(n, attr: Node)
-proc removeChild*(n, child: Node)
-proc replaceChild*(n, newNode, oldNode: Node)
 proc replaceData*(n: Node, start, len: int, text: cstring)
-proc scrollIntoView*(n: Node, alignToTop: bool=true)
+proc scrollIntoView*(n: Node)
+proc scrollIntoView*(n: Node, options: ScrollIntoViewOptions)
 proc setAttribute*(n: Node, name, value: cstring)
 proc setAttributeNode*(n: Node, attr: Node)
+proc querySelector*(n: Node, selectors: cstring): Element
+proc querySelectorAll*(n: Node, selectors: cstring): seq[Element]
+proc compareDocumentPosition*(n: Node, otherNode:Node): int
+proc lookupPrefix*(n: Node): cstring
+proc lookupNamespaceURI*(n: Node): cstring
+proc isDefaultNamespace*(n: Node): bool
+proc contains*(n: Node): bool
+proc isEqualNode*(n: Node): bool
+proc isSameNode*(n: Node): bool
+
+since (1, 3):
+  proc getRootNode*(n: Node,options: RootNodeOptions): Node
+
+  # DocumentOrShadowRoot
+  proc getSelection*(n: DocumentOrShadowRoot): Selection
+  proc elementFromPoint*(n: DocumentOrShadowRoot; x, y: float): Element
+
+  # shadow dom
+  proc attachShadow*(n: Element): ShadowRoot
+  proc assignedNodes*(n: HTMLSlotElement; options: SlotOptions): seq[Node]
+  proc assignedElements*(n: HTMLSlotElement; options: SlotOptions): seq[Element]
 
 # 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
-proc getElementById*(d: Document, id: cstring): Element
 proc getElementsByName*(d: Document, name: cstring): seq[Element]
 proc getElementsByTagName*(d: Document, name: cstring): seq[Element]
 proc getElementsByClassName*(d: Document, name: cstring): seq[Element]
-proc getSelection*(d: Document): cstring
+proc insertNode*(range: Range, node: Node)
+proc getSelection*(d: Document): Selection
 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)
@@ -543,6 +1598,8 @@ proc getElementsByClassName*(e: Element, name: cstring): seq[Element]
 # FormElement "methods"
 proc reset*(f: FormElement)
 proc submit*(f: FormElement)
+proc checkValidity*(e: FormElement): bool
+proc reportValidity*(e: FormElement): bool
 
 # EmbedElement "methods"
 proc play*(e: EmbedElement)
@@ -560,36 +1617,86 @@ proc pushState*[T](h: History, stateObject: T, title, url: cstring)
 
 # Navigator "methods"
 proc javaEnabled*(h: Navigator): bool
+since (1, 3):
+  proc canShare*(self: Navigator; data: cstring): bool           ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/canShare
+  proc sendBeacon*(self: Navigator; url, data: cstring): bool    ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon
+  proc vibrate*(self: Navigator; pattern: cint): bool            ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/vibrate
+  proc vibrate*(self: Navigator; pattern: openArray[cint]): bool ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/vibrate
+  proc registerProtocolHandler*(self: Navigator; scheme, url, title: cstring) ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler
 
 # ClassList "methods"
 proc add*(c: ClassList, class: cstring)
 proc remove*(c: ClassList, class: cstring)
-proc contains*(c: ClassList, class: cstring):bool
+proc contains*(c: ClassList, class: cstring): bool
 proc toggle*(c: ClassList, class: cstring)
 
 # Style "methods"
-proc getAttribute*(s: Style, attr: cstring, caseSensitive=false): cstring
-proc removeAttribute*(s: Style, attr: cstring, caseSensitive=false)
-proc setAttribute*(s: Style, attr, value: cstring, caseSensitive=false)
+proc getPropertyValue*(s: Style, property: cstring): cstring
+proc removeProperty*(s: Style, property: cstring)
+proc setProperty*(s: Style, property, value: cstring, priority = "")
+proc getPropertyPriority*(s: Style, property: cstring): cstring
 
 # Event "methods"
 proc preventDefault*(ev: Event)
+proc stopImmediatePropagation*(ev: Event)
+proc stopPropagation*(ev: Event)
+
+# KeyboardEvent "methods"
+proc getModifierState*(ev: KeyboardEvent, keyArg: cstring): bool
+
+# MouseEvent "methods"
+proc getModifierState*(ev: MouseEvent, keyArg: cstring): bool
 
 # TouchEvent "methods"
 proc identifiedTouch*(list: TouchList): Touch
 proc item*(list: TouchList, i: int): Touch
 
+# DataTransfer "methods"
+proc clearData*(dt: DataTransfer, format: cstring)
+proc getData*(dt: DataTransfer, format: cstring): cstring
+proc setData*(dt: DataTransfer, format: cstring, data: cstring)
+proc setDragImage*(dt: DataTransfer, img: Element, xOffset: int, yOffset: int)
+
+# DataTransferItem "methods"
+proc getAsFile*(dti: DataTransferItem): File
+
+# InputElement "methods"
+proc setSelectionRange*(e: InputElement, selectionStart: int, selectionEnd: int, selectionDirection: cstring = "none")
+proc setRangeText*(e: InputElement, replacement: cstring, startindex: int = 0, endindex: int = 0, selectionMode: cstring = "preserve")
+proc setCustomValidity*(e: InputElement, error: cstring)
+proc checkValidity*(e: InputElement): bool
+
+# Blob "methods"
+proc slice*(e: Blob, startindex: int = 0, endindex: int = e.size, contentType: cstring = "")
+
 # Performance "methods"
 proc now*(p: Performance): float
 
+# Selection "methods"
+proc removeAllRanges*(s: Selection)
+proc deleteFromDocument*(s: Selection)
+proc getRangeAt*(s: Selection, index: int): Range
+converter toString*(s: Selection): cstring
+proc `$`*(s: Selection): string = $(s.toString())
+
+# Storage "methods"
+proc getItem*(s: Storage, key: cstring): cstring
+proc setItem*(s: Storage, key, value: cstring)
+proc clear*(s: Storage)
+proc removeItem*(s: Storage, key: cstring)
+
 {.pop.}
 
+proc setAttr*(n: Node; key, val: cstring) {.importcpp: "#.setAttribute(@)".}
+
 var
   window* {.importc, nodecl.}: Window
-  document* {.importc, nodecl.}: Document
   navigator* {.importc, nodecl.}: Navigator
   screen* {.importc, nodecl.}: Screen
 
+when not defined(nodejs):
+  var document* {.importc, nodecl.}: Document
+
 proc decodeURI*(uri: cstring): cstring {.importc, nodecl.}
 proc encodeURI*(uri: cstring): cstring {.importc, nodecl.}
 
@@ -600,30 +1707,135 @@ proc decodeURIComponent*(uri: cstring): cstring {.importc, nodecl.}
 proc encodeURIComponent*(uri: cstring): cstring {.importc, nodecl.}
 proc isFinite*(x: BiggestFloat): bool {.importc, nodecl.}
 proc isNaN*(x: BiggestFloat): bool {.importc, nodecl.}
-proc parseFloat*(s: cstring): BiggestFloat {.importc, nodecl.}
-proc parseInt*(s: cstring): int {.importc, nodecl.}
-proc parseInt*(s: cstring, radix: int):int {.importc, nodecl.}
+  ## see also `math.isNaN`.
 
 proc newEvent*(name: cstring): Event {.importcpp: "new Event(@)", constructor.}
 
+proc getElementsByClass*(n: Node; name: cstring): seq[Node] {.
+  importcpp: "#.getElementsByClassName(#)", nodecl.}
+
+
 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
+  BoundingRect* {.importc.} = object
+    top*, bottom*, left*, right*, x*, y*, width*, height*: float
+
+proc getBoundingClientRect*(e: Node): BoundingRect {.
+  importcpp: "getBoundingClientRect", nodecl.}
+proc clientHeight*(): int {.
+  importcpp: "(window.innerHeight || document.documentElement.clientHeight)@", nodecl.}
+proc clientWidth*(): int {.
+  importcpp: "(window.innerWidth || document.documentElement.clientWidth)@", nodecl.}
+
+proc inViewport*(el: Node): bool =
+  let rect = el.getBoundingClientRect()
+  result = rect.top >= 0 and rect.left >= 0 and
+           rect.bottom <= clientHeight().float and
+           rect.right <= clientWidth().float
+
+proc scrollTop*(e: Node): int {.importcpp: "#.scrollTop", nodecl.}
+proc `scrollTop=`*(e: Node, value: int) {.importcpp: "#.scrollTop = #", nodecl.}
+proc scrollLeft*(e: Node): int {.importcpp: "#.scrollLeft", nodecl.}
+proc scrollHeight*(e: Node): int {.importcpp: "#.scrollHeight", nodecl.}
+proc scrollWidth*(e: Node): int {.importcpp: "#.scrollWidth", nodecl.}
+proc offsetHeight*(e: Node): int {.importcpp: "#.offsetHeight", nodecl.}
+proc offsetWidth*(e: Node): int {.importcpp: "#.offsetWidth", nodecl.}
+proc offsetTop*(e: Node): int {.importcpp: "#.offsetTop", nodecl.}
+proc offsetLeft*(e: Node): int {.importcpp: "#.offsetLeft", nodecl.}
+
+since (1, 3):
+  func newDomParser*(): DomParser {.importcpp: "new DOMParser()".}
+    ## DOM Parser constructor.
+  func parseFromString*(this: DomParser; str: cstring; mimeType: cstring): Document {.importcpp.}
+    ## Parse from string to `Document`.
+
+  proc newDomException*(): DomException {.importcpp: "new DomException()", constructor.}
+    ## DOM Exception constructor
+  proc message*(ex: DomException): cstring {.importcpp: "#.message", nodecl.}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/DOMException/message
+  proc name*(ex: DomException): cstring  {.importcpp: "#.name", nodecl.}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/DOMException/name
+
+  proc newFileReader*(): FileReader {.importcpp: "new FileReader()", constructor.}
+    ## File Reader constructor
+  proc error*(f: FileReader): DomException {.importcpp: "#.error", nodecl.}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/error
+  proc readyState*(f: FileReader): FileReaderState {.importcpp: "#.readyState", nodecl.}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readyState
+  proc resultAsString*(f: FileReader): cstring {.importcpp: "#.result", nodecl.}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/result
+  proc abort*(f: FileReader) {.importcpp: "#.abort()".}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/abort
+  proc readAsBinaryString*(f: FileReader, b: Blob) {.importcpp: "#.readAsBinaryString(#)".}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsBinaryString
+  proc readAsDataURL*(f: FileReader, b: Blob) {.importcpp: "#.readAsDataURL(#)".}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL
+  proc readAsText*(f: FileReader, b: Blob|File, encoding = cstring"UTF-8") {.importcpp: "#.readAsText(#, #)".}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsText
+
+since (1, 5):
+  proc elementsFromPoint*(n: DocumentOrShadowRoot; x, y: float): seq[Element] {.importcpp.}
+
+
+since (1, 7):
+
+  proc insertAdjacentText*(self: Node; position, data: cstring) {.importjs: "#.$1(#, #)".}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentText
+
+  proc insertAdjacentElement*(self: Node; position: cstring; element: Node) {.importjs: "#.$1(#, #)".}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentElement
+
+  proc insertAdjacentHTML*(self: Node; position, html: cstring) {.importjs: "#.$1(#, #)".}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML
+
+  proc after*(self: Node; element: Node): Node {.importjs: "#.$1(@)", varargs.}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Element/after
+
+  proc before*(self: Node; element: Node): Node {.importjs: "#.$1(@)", varargs.}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Element/before
+
+  proc append*(self: Node; element: Node): Node {.importjs: "#.$1(@)", varargs.}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Element/append
+
+  proc closest*(self: Node; cssSelector: cstring): Node {.importjs: "#.$1(#)".}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
+
+  proc hasAttributeNS*(self: Node; namespace, localName: cstring): bool {.importjs: "(#.$1(#, #) || false)".}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Element/hasAttributeNS
+
+  proc removeAttributeNS*(self: Node; namespace, attributeName: cstring) {.importjs: "#.$1(#, #)".}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttributeNS
+
+  proc hasPointerCapture*(self: Node; pointerId: SomeNumber): bool {.importjs: "(#.$1(#) || false)".}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Element/hasPointerCapture
+
+  proc releasePointerCapture*(self: Node; pointerId: SomeNumber) {.importjs: "#.$1(#)".}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Element/releasePointerCapture
+
+  proc requestPointerLock*(self: Node) {.importjs: "#.$1()".}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Element/requestPointerLock
+
+  proc replaceChildren*(self: Node; replacements: Node) {.importjs: "#.$1(@)", varargs.}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Element/replaceChildren
+
+  proc replaceWith*(self: Node; replacements: Node) {.importjs: "#.$1(@)", varargs.}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Element/replaceWith
+
+  proc scrollIntoViewIfNeeded*(self: Node; centerIfNeeded: bool) {.importjs: "#.$1(#)".}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoViewIfNeeded
+
+  proc setHTML*(self: Node; html: cstring) {.importjs: "#.$1(#)".}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Element/setHTML
+
+  proc toggleAttribute*(self: Node; name: cstring; force = false): bool {.importjs: "(#.$1(#, #) || false)".}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Element/toggleAttribute
+
+  proc matches*(self: Node; cssSelector: cstring): bool {.importjs: "(#.$1(#) || false)".}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Element/matches
+
+
+since (2, 1):
+  type VisualViewport* {.importc.} = ref object of EventTarget
+    offsetLeft*, offsetTop*, pageLeft*, pageTop*, width*, height*, scale*: float
+    onResize*, onScroll*: proc (event: Event) {.closure.}
+
+  func visualViewport*(self: Window): VisualViewport {.importjs: "#.$1", nodecl.}
diff --git a/lib/js/jsconsole.nim b/lib/js/jsconsole.nim
index d9ced95f0..e74127334 100644
--- a/lib/js/jsconsole.nim
+++ b/lib/js/jsconsole.nim
@@ -8,37 +8,118 @@
 #
 
 ## Wrapper for the `console` object for the `JavaScript backend
-## <backends.html#the-javascript-target>`_.
+## <backends.html#backends-the-javascript-target>`_.
+##
+## Styled Messages
+## ===============
+##
+## CSS-styled messages in the browser are useful for debugging purposes.
+## To use them, prefix the message with one or more `%c`,
+## and provide the CSS style as the last argument.
+## The amount of `%c`'s must match the amount of CSS-styled strings.
+##
+runnableExamples("-r:off"):
+  console.log "%c My Debug Message", "color: red" # Notice the "%c"
+  console.log "%c My Debug %c Message", "color: red", "font-size: 2em"
 
-when not defined(js) and not defined(Nimdoc):
+import std/private/since, std/private/miscdollars  # toLocation
+
+when not defined(js):
   {.error: "This module only works on the JavaScript platform".}
 
-import macros
+type Console* = ref object of JsRoot
+
+proc log*(console: Console) {.importcpp, varargs.}
+  ## https://developer.mozilla.org/docs/Web/API/Console/log
+
+proc debug*(console: Console) {.importcpp, varargs.}
+  ## https://developer.mozilla.org/docs/Web/API/Console/debug
+
+proc info*(console: Console) {.importcpp, varargs.}
+  ## https://developer.mozilla.org/docs/Web/API/Console/info
+
+proc error*(console: Console) {.importcpp, varargs.}
+  ## https://developer.mozilla.org/docs/Web/API/Console/error
+
+template exception*(console: Console, args: varargs[untyped]) =
+  ## Alias for `console.error()`.
+  error(console, args)
+
+proc trace*(console: Console) {.importcpp, varargs.}
+  ## https://developer.mozilla.org/docs/Web/API/Console/trace
+
+proc warn*(console: Console) {.importcpp, varargs.}
+  ## https://developer.mozilla.org/docs/Web/API/Console/warn
+
+proc clear*(console: Console) {.importcpp, varargs.}
+  ## https://developer.mozilla.org/docs/Web/API/Console/clear
+
+proc count*(console: Console, label = "".cstring) {.importcpp.}
+  ## https://developer.mozilla.org/docs/Web/API/Console/count
+
+proc countReset*(console: Console, label = "".cstring) {.importcpp.}
+  ## https://developer.mozilla.org/docs/Web/API/Console/countReset
+
+proc group*(console: Console, label = "".cstring) {.importcpp.}
+  ## https://developer.mozilla.org/docs/Web/API/Console/group
+
+proc groupCollapsed*(console: Console, label = "".cstring) {.importcpp.}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/Console/groupCollapsed
+
+proc groupEnd*(console: Console) {.importcpp.}
+  ## https://developer.mozilla.org/docs/Web/API/Console/groupEnd
+
+proc time*(console: Console, label = "".cstring) {.importcpp.}
+  ## https://developer.mozilla.org/docs/Web/API/Console/time
+
+proc timeEnd*(console: Console, label = "".cstring) {.importcpp.}
+  ## https://developer.mozilla.org/docs/Web/API/Console/timeEnd
+
+proc timeLog*(console: Console, label = "".cstring) {.importcpp.}
+  ## https://developer.mozilla.org/docs/Web/API/Console/timeLog
+
+proc table*(console: Console) {.importcpp, varargs.}
+  ## https://developer.mozilla.org/docs/Web/API/Console/table
 
-type Console* {.importc.} = ref object of RootObj
+since (1, 5):
+  type InstantiationInfo = tuple[filename: string, line: int, column: int]
 
-proc convertToConsoleLoggable*[T](v: T): RootRef {.importcpp: "#".}
-template convertToConsoleLoggable*(v: string): RootRef = cast[RootRef](cstring(v))
+  func getMsg(info: InstantiationInfo; msg: string): string =
+    var temp = ""
+    temp.toLocation(info.filename, info.line, info.column + 1)
+    result.addQuoted("[jsAssert] " & temp)
+    result.add ','
+    result.addQuoted(msg)
 
-proc logImpl(console: Console) {.importcpp: "log", varargs.}
-proc debugImpl(console: Console) {.importcpp: "debug", varargs.}
-proc infoImpl(console: Console) {.importcpp: "info", varargs.}
-proc errorImpl(console: Console) {.importcpp: "error", varargs.}
+  template jsAssert*(console: Console; assertion) =
+    ## JavaScript `console.assert`, for NodeJS this prints to stderr,
+    ## assert failure just prints to console and do not quit the program,
+    ## this is not meant to be better or even equal than normal assertions,
+    ## is just for when you need faster performance *and* assertions,
+    ## otherwise use the normal assertions for better user experience.
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Console/assert
+    runnableExamples:
+      console.jsAssert(42 == 42) # OK
+      console.jsAssert(42 != 42) # Fail, prints "Assertion failed" and continues
+      console.jsAssert('`' == '\n' and '\t' == '\0') # Message correctly formatted
+      assert 42 == 42  # Normal assertions keep working
 
-proc makeConsoleCall(console: NimNode, procName: NimNode, args: NimNode): NimNode =
-  result = newCall(procName, console)
-  for c in args: result.add(c)
+    const
+      loc = instantiationInfo(fullPaths = compileOption("excessiveStackTrace"))
+      msg = getMsg(loc, astToStr(assertion)).cstring
+    {.line: loc.}:
+      {.emit: ["console.assert(", assertion, ", ", msg, ");"].}
 
-macro log*(console: Console, args: varargs[RootRef, convertToConsoleLoggable]): untyped =
-  makeConsoleCall(console, bindSym "logImpl", args)
+  func dir*(console: Console; obj: auto) {.importcpp.}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Console/dir
 
-macro debug*(console: Console, args: varargs[RootRef, convertToConsoleLoggable]): untyped =
-  makeConsoleCall(console, bindSym "debugImpl", args)
+  func dirxml*(console: Console; obj: auto) {.importcpp.}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Console/dirxml
 
-macro info*(console: Console, args: varargs[RootRef, convertToConsoleLoggable]): untyped =
-  makeConsoleCall(console, bindSym "infoImpl", args)
+  func timeStamp*(console: Console; label: cstring) {.importcpp.}
+    ## https://developer.mozilla.org/en-US/docs/Web/API/Console/timeStamp
+    ##
+    ## ..warning:: non-standard
 
-macro error*(console: Console, args: varargs[RootRef, convertToConsoleLoggable]): untyped =
-  makeConsoleCall(console, bindSym "errorImpl", args)
 
-var console* {.importc, nodecl.}: Console
\ No newline at end of file
+var console* {.importc, nodecl.}: Console
diff --git a/lib/js/jscore.nim b/lib/js/jscore.nim
index 91f3ff8bb..be353875c 100644
--- a/lib/js/jscore.nim
+++ b/lib/js/jscore.nim
@@ -1,11 +1,21 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2018 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
 ## This module wraps core JavaScript functions.
 ##
 ## Unless your application has very
 ## specific requirements and solely targets JavaScript, you should be using
-## the relevant functions in the ``math``, ``json``, and ``times`` stdlib
+## the relevant functions in the `math`, `json`, and `times` stdlib
 ## modules instead.
+import std/private/[since, jsutils]
 
-when not defined(js) and not defined(Nimdoc):
+when not defined(js):
   {.error: "This module only works on the JavaScript platform".}
 
 type
@@ -19,49 +29,47 @@ var
   Date* {.importc, nodecl.}: DateLib
   JSON* {.importc, nodecl.}: JsonLib
 
-{.push importcpp.}
-
 # Math library
-proc abs*(m: MathLib, a: SomeNumber): SomeNumber
-proc acos*(m: MathLib, a: SomeNumber): float
-proc acosh*(m: MathLib, a: SomeNumber): float
-proc asin*(m: MathLib, a: SomeNumber): float
-proc asinh*(m: MathLib, a: SomeNumber): float
-proc atan*(m: MathLib, a: SomeNumber): float
-proc atan2*(m: MathLib, a: SomeNumber): float
-proc atanh*(m: MathLib, a: SomeNumber): float
-proc cbrt*(m: MathLib, f: SomeFloat): SomeFloat
-proc ceil*(m: MathLib, f: SomeFloat): SomeFloat
-proc clz32*(m: MathLib, f: SomeInteger): int
-proc cos*(m: MathLib, a: SomeNumber): float
-proc cosh*(m: MathLib, a: SomeNumber): float
-proc exp*(m: MathLib, a: SomeNumber): float
-proc expm1*(m: MathLib, a: SomeNumber): float
-proc floor*(m: MathLib, f: SomeFloat): int
-proc fround*(m: MathLib, f: SomeFloat): float32
-proc hypot*(m: MathLib, args: varargs[distinct SomeNumber]): float
-proc imul*(m: MathLib, a, b: int32): int32
-proc log*(m: MathLib, a: SomeNumber): float
-proc log10*(m: MathLib, a: SomeNumber): float
-proc log1p*(m: MathLib, a: SomeNumber): float
-proc log2*(m: MathLib, a: SomeNumber): float
-proc max*(m: MathLib, a, b: SomeNumber): SomeNumber
-proc min*[T: SomeNumber | JsRoot](m: MathLib, a, b: T): T
-proc pow*(m: MathLib, a, b: distinct SomeNumber): float
-proc random*(m: MathLib): float
-proc round*(m: MathLib, f: SomeFloat): int
-proc sign*(m: MathLib, f: SomeNumber): int
-proc sin*(m: MathLib, a: SomeNumber): float
-proc sinh*(m: MathLib, a: SomeNumber): float
-proc sqrt*(m: MathLib, f: SomeFloat): SomeFloat
-proc tan*(m: MathLib, a: SomeNumber): float
-proc tanh*(m: MathLib, a: SomeNumber): float
-proc trunc*(m: MathLib, f: SomeFloat): int
+proc abs*(m: MathLib, a: SomeNumber): SomeNumber {.importcpp.}
+proc acos*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc acosh*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc asin*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc asinh*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc atan*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc atan2*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc atanh*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc cbrt*(m: MathLib, f: SomeFloat): SomeFloat {.importcpp.}
+proc ceil*(m: MathLib, f: SomeFloat): SomeFloat {.importcpp.}
+proc clz32*(m: MathLib, f: SomeInteger): int {.importcpp.}
+proc cos*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc cosh*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc exp*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc expm1*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc floor*(m: MathLib, f: SomeFloat): int {.importcpp.}
+proc fround*(m: MathLib, f: SomeFloat): float32 {.importcpp.}
+proc hypot*(m: MathLib, args: varargs[distinct SomeNumber]): float {.importcpp.}
+proc imul*(m: MathLib, a, b: int32): int32 {.importcpp.}
+proc log*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc log10*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc log1p*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc log2*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc max*(m: MathLib, a, b: SomeNumber): SomeNumber {.importcpp.}
+proc min*[T: SomeNumber | JsRoot](m: MathLib, a, b: T): T {.importcpp.}
+proc pow*(m: MathLib, a, b: distinct SomeNumber): float {.importcpp.}
+proc random*(m: MathLib): float {.importcpp.}
+proc round*(m: MathLib, f: SomeFloat): int {.importcpp.}
+proc sign*(m: MathLib, f: SomeNumber): int {.importcpp.}
+proc sin*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc sinh*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc sqrt*(m: MathLib, f: SomeFloat): SomeFloat {.importcpp.}
+proc tan*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc tanh*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc trunc*(m: MathLib, f: SomeFloat): int {.importcpp.}
 
 # Date library
-proc now*(d: DateLib): int
-proc UTC*(d: DateLib): int
-proc parse*(d: DateLib, s: cstring): int
+proc now*(d: DateLib): int {.importcpp.}
+proc UTC*(d: DateLib): int {.importcpp.}
+proc parse*(d: DateLib, s: cstring): int {.importcpp.}
 
 proc newDate*(): DateTime {.
   importcpp: "new Date()".}
@@ -69,23 +77,77 @@ proc newDate*(): DateTime {.
 proc newDate*(date: int|string): DateTime {.
   importcpp: "new Date(#)".}
 
+whenJsNoBigInt64:
+  proc newDate*(date: int64): DateTime {.
+    importcpp: "new Date(#)".}
+do:
+  proc newDate*(date: int64): DateTime {.
+    importcpp: "new Date(Number(#))".}
+
 proc newDate*(year, month, day, hours, minutes,
              seconds, milliseconds: int): DateTime {.
   importcpp: "new Date(#,#,#,#,#,#,#)".}
 
-proc getDay*(d: DateTime): int
-proc getFullYear*(d: DateTime): int
-proc getHours*(d: DateTime): int
-proc getMilliseconds*(d: DateTime): int
-proc getMinutes*(d: DateTime): int
-proc getMonth*(d: DateTime): int
-proc getSeconds*(d: DateTime): int
-proc getYear*(d: DateTime): int
-proc getTime*(d: DateTime): int
-proc toString*(d: DateTime): cstring
+proc getDay*(d: DateTime): int {.importcpp.}
+proc getFullYear*(d: DateTime): int {.importcpp.}
+proc getHours*(d: DateTime): int {.importcpp.}
+proc getMilliseconds*(d: DateTime): int {.importcpp.}
+proc getMinutes*(d: DateTime): int {.importcpp.}
+proc getMonth*(d: DateTime): int {.importcpp.}
+proc getSeconds*(d: DateTime): int {.importcpp.}
+proc getTime*(d: DateTime): int {.importcpp.}
+proc getTimezoneOffset*(d: DateTime): int {.importcpp.}
+proc getUTCDate*(d: DateTime): int {.importcpp.}
+proc getUTCDay*(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 getYear*(d: DateTime): int {.importcpp.}
+
+proc setFullYear*(d: DateTime, year: int) {.importcpp.}
+
+func toDateString*(d: DateTime): cstring {.importcpp.}
+func toISOString*(d: DateTime): cstring {.importcpp.}
+func toJSON*(d: DateTime): cstring {.importcpp.}
+proc toString*(d: DateTime): cstring {.importcpp.}
+func toTimeString*(d: DateTime): cstring {.importcpp.}
+func toUTCString*(d: DateTime): cstring {.importcpp.}
 
 #JSON library
-proc stringify*(l: JsonLib, s: JsRoot): cstring
-proc parse*(l: JsonLib, s: cstring): JsRoot
+proc stringify*(l: JsonLib, s: JsRoot): cstring {.importcpp.}
+proc parse*(l: JsonLib, s: cstring): JsRoot {.importcpp.}
+
+
+since (1, 5):
+  func debugger*() {.importjs: "debugger@".}
+    ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger
+
+  func copyWithin*[T](self: openArray[T]; target: int): seq[T] {.importjs: "#.copyWithin(#)".}
+  func copyWithin*[T](self: openArray[T]; target, start: int): seq[T] {.importjs: "#.copyWithin(#, #)".}
+  func copyWithin*[T](self: openArray[T]; target, start, ends: int): seq[T] {.importjs: "#.copyWithin(#, #, #)".} =
+    ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin
+    ## `copyWithin` uses shallow copy.
+    runnableExamples:
+      assert ['a', 'b', 'c', 'd', 'e'].copyWithin(0, 3, 4) == @['d', 'b', 'c', 'd', 'e']
+      assert ['a', 'b', 'c', 'd', 'e'].copyWithin(1, 3) == @['a', 'd', 'e', 'd', 'e']
+      assert [1, 2, 3, 4, 5].copyWithin(-2) == @[1, 2, 3, 1, 2]
+      assert [1, 2, 3, 4, 5].copyWithin(0, 3) == @[4, 5, 3, 4, 5]
+      assert [1, 2, 3, 4, 5].copyWithin(0, 3, 4) == @[4, 2, 3, 4, 5]
+      assert [1, 2, 3, 4, 5].copyWithin(-2, -3, -1) == @[1, 2, 3, 3, 4]
+
+
+since (1, 7):
+  func shift*[T](self: seq[T]): T {.importjs: "#.$1()".} =
+    ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
+    runnableExamples:
+      var arrai = @[1, 2, 3]
+      assert arrai.shift() == 1
+      assert arrai == @[2, 3]
 
-{.pop.}
+  func queueMicrotask*(function: proc) {.importjs: "$1(#)".} =
+    ## * https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask
+    ## * https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide
+    runnableExamples"-r:off": queueMicrotask(proc() = echo "Microtask")
diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim
index 6e48db6c7..d50d58ae5 100644
--- a/lib/js/jsffi.nim
+++ b/lib/js/jsffi.nim
@@ -8,43 +8,42 @@
 #
 
 ## This Module implements types and macros to facilitate the wrapping of, and
-## interaction with JavaScript libraries. Using the provided types ``JsObject``
-## and ``JsAssoc`` together with the provided macros allows for smoother
+## interaction with JavaScript libraries. Using the provided types `JsObject`
+## and `JsAssoc` together with the provided macros allows for smoother
 ## interfacing with JavaScript, allowing for example quick and easy imports of
 ## JavaScript variables:
-##
-## .. code-block:: nim
-##
-##  # Here, we are using jQuery for just a few calls and do not want to wrap the
-##  # whole library:
-##
-##  # import the document object and the console
-##  var document {. importc, nodecl .}: JsObject
-##  var console {. importc, nodecl .}: JsObject
-##  # import the "$" function
-##  proc jq(selector: JsObject): JsObject {. importcpp: "$(#)" .}
-##
-##  # Use jQuery to make the following code run, after the document is ready.
-##  # This uses an experimental ``.()`` operator for ``JsObject``, to emit
-##  # JavaScript calls, when no corresponding proc exists for ``JsObject``.
-##  proc main =
-##    jq(document).ready(proc() =
-##      console.log("Hello JavaScript!")
-##    )
-##
-
-when not defined(js) and not defined(nimdoc) and not defined(nimsuggest):
+
+runnableExamples:
+  # Here, we are using jQuery for just a few calls and do not want to wrap the
+  # whole library:
+
+  # import the document object and the console
+  var document {.importc, nodecl.}: JsObject
+  var console {.importc, nodecl.}: JsObject
+  # import the "$" function
+  proc jq(selector: JsObject): JsObject {.importjs: "$$(#)".}
+
+  # Use jQuery to make the following code run, after the document is ready.
+  # This uses an experimental `.()` operator for `JsObject`, to emit
+  # JavaScript calls, when no corresponding proc exists for `JsObject`.
+  proc main =
+    jq(document).ready(proc() =
+      console.log("Hello JavaScript!")
+    )
+
+
+when not defined(js) and not defined(nimsuggest):
   {.fatal: "Module jsFFI is designed to be used with the JavaScript backend.".}
 
-import macros, tables
+import std/[macros, tables]
 
 const
   setImpl = "#[#] = #"
   getImpl = "#[#]"
 
 var
-  mangledNames {. compileTime .} = initTable[string, string]()
-  nameCounter {. compileTime .} = 0
+  mangledNames {.compileTime.} = initTable[string, string]()
+  nameCounter {.compileTime.} = 0
 
 proc validJsName(name: string): bool =
   result = true
@@ -65,63 +64,93 @@ proc validJsName(name: string): bool =
     if chr notin {'A'..'Z','a'..'z','_','$','0'..'9'}:
       return false
 
-template mangleJsName(name: cstring): cstring =
+template mangleJsName(name: string): string =
   inc nameCounter
   "mangledName" & $nameCounter
 
+# only values that can be mapped 1 to 1 with cstring should be keys: they have an injective function with cstring
+
+proc toJsKey*[T: SomeInteger](text: cstring, t: type T): T {.importjs: "parseInt(#)".}
+
+proc toJsKey*[T: enum](text: cstring, t: type T): T =
+  T(text.toJsKey(int))
+
+proc toJsKey*(text: cstring, t: type cstring): cstring =
+  text
+
+proc toJsKey*[T: SomeFloat](text: cstring, t: type T): T {.importjs: "parseFloat(#)".}
+
 type
+  JsKey* = concept a, type T
+    cstring.toJsKey(T) is T
+
   JsObject* = ref object of JsRoot
     ## Dynamically typed wrapper around a JavaScript object.
-  JsAssoc*[K, V] = ref object of JsRoot
+  JsAssoc*[K: JsKey, V] = ref object of JsRoot
     ## Statically typed wrapper around a JavaScript object.
 
-  NotString = concept c
-    c isnot string
   js* = JsObject
 
 var
   jsArguments* {.importc: "arguments", nodecl}: JsObject
-    ## JavaScript's arguments pseudo-variable
+    ## JavaScript's arguments pseudo-variable.
   jsNull* {.importc: "null", nodecl.}: JsObject
-    ## JavaScript's null literal
+    ## JavaScript's null literal.
   jsUndefined* {.importc: "undefined", nodecl.}: JsObject
-    ## JavaScript's undefined literal
+    ## JavaScript's undefined literal.
   jsDirname* {.importc: "__dirname", nodecl.}: cstring
-    ## JavaScript's __dirname pseudo-variable
+    ## JavaScript's __dirname pseudo-variable.
   jsFilename* {.importc: "__filename", nodecl.}: cstring
-    ## JavaScript's __filename pseudo-variable
+    ## JavaScript's __filename pseudo-variable.
+
+proc isNull*[T](x: T): bool {.noSideEffect, importjs: "(# === null)".}
+  ## Checks if a value is exactly null.
+
+proc isUndefined*[T](x: T): bool {.noSideEffect, importjs: "(# === undefined)".}
+  ## Checks if a value is exactly undefined.
+
+# Exceptions
+type
+  JsError* {.importc: "Error".} = object of JsRoot
+    message*: cstring
+  JsEvalError* {.importc: "EvalError".} = object of JsError
+  JsRangeError* {.importc: "RangeError".} = object of JsError
+  JsReferenceError* {.importc: "ReferenceError".} = object of JsError
+  JsSyntaxError* {.importc: "SyntaxError".} = object of JsError
+  JsTypeError* {.importc: "TypeError".} = object of JsError
+  JsURIError* {.importc: "URIError".} = object of JsError
 
 # New
-proc newJsObject*: JsObject {. importcpp: "{@}" .}
-  ## Creates a new empty JsObject
+proc newJsObject*: JsObject {.importjs: "{@}".}
+  ## Creates a new empty JsObject.
 
-proc newJsAssoc*[K, V]: JsAssoc[K, V] {. importcpp: "{@}" .}
+proc newJsAssoc*[K: JsKey, V]: JsAssoc[K, V] {.importjs: "{@}".}
   ## Creates a new empty JsAssoc with key type `K` and value type `V`.
 
 # Checks
 proc hasOwnProperty*(x: JsObject, prop: cstring): bool
-  {. importcpp: "#.hasOwnProperty(#)" .}
+  {.importjs: "#.hasOwnProperty(#)".}
   ## Checks, whether `x` has a property of name `prop`.
 
-proc jsTypeOf*(x: JsObject): cstring {. importcpp: "typeof(#)" .}
+proc jsTypeOf*(x: JsObject): cstring {.importjs: "typeof(#)".}
   ## Returns the name of the JsObject's JavaScript type as a cstring.
 
-proc jsNew*(x: auto): JsObject {.importcpp: "(new #)".}
+proc jsNew*(x: auto): JsObject {.importjs: "(new #)".}
   ## Turns a regular function call into an invocation of the
-  ## JavaScript's `new` operator
+  ## JavaScript's `new` operator.
 
-proc jsDelete*(x: auto): JsObject {.importcpp: "(delete #)".}
-  ## JavaScript's `delete` operator
+proc jsDelete*(x: auto): JsObject {.importjs: "(delete #)".}
+  ## JavaScript's `delete` operator.
 
 proc require*(module: cstring): JsObject {.importc.}
-  ## JavaScript's `require` function
+  ## JavaScript's `require` function.
 
 # Conversion to and from JsObject
-proc to*(x: JsObject, T: typedesc): T {. importcpp: "(#)" .}
+proc to*(x: JsObject, T: typedesc): T {.importjs: "(#)".}
   ## Converts a JsObject `x` to type `T`.
 
-proc toJs*[T](val: T): JsObject {. importcpp: "(#)" .}
-  ## Converts a value of any type to type JsObject
+proc toJs*[T](val: T): JsObject {.importjs: "(#)".}
+  ## Converts a value of any type to type JsObject.
 
 template toJs*(s: string): JsObject = cstring(s).toJs
 
@@ -131,107 +160,107 @@ macro jsFromAst*(n: untyped): untyped =
     result = newProc(procType = nnkDo, body = result)
   return quote: toJs(`result`)
 
-proc `&`*(a, b: cstring): cstring {.importcpp: "(# + #)".}
-  ## Concatenation operator for JavaScript strings
-
-proc `+`  *(x, y: JsObject): JsObject {. importcpp: "(# + #)" .}
-proc `-`  *(x, y: JsObject): JsObject {. importcpp: "(# - #)" .}
-proc `*`  *(x, y: JsObject): JsObject {. importcpp: "(# * #)" .}
-proc `/`  *(x, y: JsObject): JsObject {. importcpp: "(# / #)" .}
-proc `%`  *(x, y: JsObject): JsObject {. importcpp: "(# % #)" .}
-proc `+=` *(x, y: JsObject): JsObject {. importcpp: "(# += #)", discardable .}
-proc `-=` *(x, y: JsObject): JsObject {. importcpp: "(# -= #)", discardable .}
-proc `*=` *(x, y: JsObject): JsObject {. importcpp: "(# *= #)", discardable .}
-proc `/=` *(x, y: JsObject): JsObject {. importcpp: "(# /= #)", discardable .}
-proc `%=` *(x, y: JsObject): JsObject {. importcpp: "(# %= #)", discardable .}
-proc `++` *(x: JsObject): JsObject    {. importcpp: "(++#)" .}
-proc `--` *(x: JsObject): JsObject    {. importcpp: "(--#)" .}
-proc `>`  *(x, y: JsObject): JsObject {. importcpp: "(# > #)" .}
-proc `<`  *(x, y: JsObject): JsObject {. importcpp: "(# < #)" .}
-proc `>=` *(x, y: JsObject): JsObject {. importcpp: "(# >= #)" .}
-proc `<=` *(x, y: JsObject): JsObject {. importcpp: "(# <= #)" .}
-proc `and`*(x, y: JsObject): JsObject {. importcpp: "(# && #)" .}
-proc `or` *(x, y: JsObject): JsObject {. importcpp: "(# || #)" .}
-proc `not`*(x: JsObject): JsObject    {. importcpp: "(!#)" .}
-proc `in` *(x, y: JsObject): JsObject {. importcpp: "(# in #)" .}
-
-proc `[]`*(obj: JsObject, field: cstring): JsObject {. importcpp: getImpl .}
-  ## Return the value of a property of name `field` from a JsObject `obj`.
-
-proc `[]`*(obj: JsObject, field: int): JsObject {. importcpp: getImpl .}
-  ## Return the value of a property of name `field` from a JsObject `obj`.
-
-proc `[]=`*[T](obj: JsObject, field: cstring, val: T) {. importcpp: setImpl .}
-  ## Set the value of a property of name `field` in a JsObject `obj` to `v`.
-
-proc `[]=`*[T](obj: JsObject, field: int, val: T) {. importcpp: setImpl .}
-  ## Set the value of a property of name `field` in a JsObject `obj` to `v`.
-
-proc `[]`*[K: NotString, V](obj: JsAssoc[K, V], field: K): V
-  {. importcpp: getImpl .}
-  ## Return the value of a property of name `field` from a JsAssoc `obj`.
-
-proc `[]`*[V](obj: JsAssoc[string, V], field: cstring): V
-  {. importcpp: getImpl .}
-  ## Return the value of a property of name `field` from a JsAssoc `obj`.
-
-proc `[]=`*[K: NotString, V](obj: JsAssoc[K, V], field: K, val: V)
-  {. importcpp: setImpl .}
-  ## Set the value of a property of name `field` in a JsAssoc `obj` to `v`.
-
-proc `[]=`*[V](obj: JsAssoc[string, V], field: cstring, val: V)
-  {. importcpp: setImpl .}
-  ## Set the value of a property of name `field` in a JsAssoc `obj` to `v`.
-
-proc `==`*(x, y: JsRoot): bool {. importcpp: "(# === #)" .}
-  ## Compare two JsObjects or JsAssocs. Be careful though, as this is comparison
+proc `&`*(a, b: cstring): cstring {.importjs: "(# + #)".}
+  ## Concatenation operator for JavaScript strings.
+
+proc `+`*(x, y: JsObject): JsObject {.importjs: "(# + #)".}
+proc `-`*(x, y: JsObject): JsObject {.importjs: "(# - #)".}
+proc `*`*(x, y: JsObject): JsObject {.importjs: "(# * #)".}
+proc `/`*(x, y: JsObject): JsObject {.importjs: "(# / #)".}
+proc `%`*(x, y: JsObject): JsObject {.importjs: "(# % #)".}
+proc `+=`*(x, y: JsObject): JsObject {.importjs: "(# += #)", discardable.}
+proc `-=`*(x, y: JsObject): JsObject {.importjs: "(# -= #)", discardable.}
+proc `*=`*(x, y: JsObject): JsObject {.importjs: "(# *= #)", discardable.}
+proc `/=`*(x, y: JsObject): JsObject {.importjs: "(# /= #)", discardable.}
+proc `%=`*(x, y: JsObject): JsObject {.importjs: "(# %= #)", discardable.}
+proc `++`*(x:    JsObject): JsObject {.importjs: "(++#)".}
+proc `--`*(x:    JsObject): JsObject {.importjs: "(--#)".}
+proc `>`*(x, y: JsObject): JsObject {.importjs: "(# > #)".}
+proc `<`*(x, y: JsObject): JsObject {.importjs: "(# < #)".}
+proc `>=`*(x, y: JsObject): JsObject {.importjs: "(# >= #)".}
+proc `<=`*(x, y: JsObject): JsObject {.importjs: "(# <= #)".}
+proc `**`*(x, y: JsObject): JsObject {.importjs: "((#) ** #)".}
+  # (#) needed, refs https://github.com/nim-lang/Nim/pull/16409#issuecomment-760550812
+proc `and`*(x, y: JsObject): JsObject {.importjs: "(# && #)".}
+proc `or`*(x, y: JsObject): JsObject {.importjs: "(# || #)".}
+proc `not`*(x:    JsObject): JsObject {.importjs: "(!#)".}
+proc `in`*(x, y: JsObject): JsObject {.importjs: "(# in #)".}
+
+proc `[]`*(obj: JsObject, field: cstring): JsObject {.importjs: getImpl.}
+  ## Returns the value of a property of name `field` from a JsObject `obj`.
+
+proc `[]`*(obj: JsObject, field: int): JsObject {.importjs: getImpl.}
+  ## Returns the value of a property of name `field` from a JsObject `obj`.
+
+proc `[]=`*[T](obj: JsObject, field: cstring, val: T) {.importjs: setImpl.}
+  ## Sets the value of a property of name `field` in a JsObject `obj` to `v`.
+
+proc `[]=`*[T](obj: JsObject, field: int, val: T) {.importjs: setImpl.}
+  ## Sets the value of a property of name `field` in a JsObject `obj` to `v`.
+
+proc `[]`*[K: JsKey, V](obj: JsAssoc[K, V], field: K): V
+  {.importjs: getImpl.}
+  ## Returns the value of a property of name `field` from a JsAssoc `obj`.
+
+proc `[]=`*[K: JsKey, V](obj: JsAssoc[K, V], field: K, val: V)
+  {.importjs: setImpl.}
+  ## Sets the value of a property of name `field` in a JsAssoc `obj` to `v`.
+
+proc `[]`*[V](obj: JsAssoc[cstring, V], field: string): V =
+  obj[cstring(field)]
+
+proc `[]=`*[V](obj: JsAssoc[cstring, V], field: string, val: V) =
+  obj[cstring(field)] = val
+
+proc `==`*(x, y: JsRoot): bool {.importjs: "(# === #)".}
+  ## Compares two JsObjects or JsAssocs. Be careful though, as this is comparison
   ## like in JavaScript, so if your JsObjects are in fact JavaScript Objects,
   ## and not strings or numbers, this is a *comparison of references*.
 
-{. experimental .}
+{.experimental.}
 macro `.`*(obj: JsObject, field: untyped): JsObject =
   ## Experimental dot accessor (get) for type JsObject.
   ## Returns the value of a property of name `field` from a JsObject `x`.
-  ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##  let obj = newJsObject()
-  ##  obj.a = 20
-  ##  console.log(obj.a) # puts 20 onto the console.
+  runnableExamples:
+    let obj = newJsObject()
+    obj.a = 20
+    assert obj.a.to(int) == 20
   if validJsName($field):
     let importString = "#." & $field
+    let helperName = genSym(nskProc, "helper")
     result = quote do:
-      proc helper(o: JsObject): JsObject
-        {. importcpp: `importString`, gensym .}
-      helper(`obj`)
+      proc `helperName`(o: JsObject): JsObject
+        {.importjs: `importString`.}
+      `helperName`(`obj`)
   else:
     if not mangledNames.hasKey($field):
-      mangledNames[$field] = $mangleJsName($field)
+      mangledNames[$field] = mangleJsName($field)
     let importString = "#." & mangledNames[$field]
+    let helperName = genSym(nskProc, "helper")
     result = quote do:
-      proc helper(o: JsObject): JsObject
-        {. importcpp: `importString`, gensym .}
-      helper(`obj`)
+      proc `helperName`(o: JsObject): JsObject
+        {.importjs: `importString`.}
+      `helperName`(`obj`)
 
 macro `.=`*(obj: JsObject, field, value: untyped): untyped =
   ## Experimental dot accessor (set) for type JsObject.
   ## Sets the value of a property of name `field` in a JsObject `x` to `value`.
   if validJsName($field):
     let importString = "#." & $field & " = #"
+    let helperName = genSym(nskProc, "helper")
     result = quote do:
-      proc helper(o: JsObject, v: auto)
-        {. importcpp: `importString`, gensym .}
-      helper(`obj`, `value`)
+      proc `helperName`(o: JsObject, v: auto)
+        {.importjs: `importString`.}
+      `helperName`(`obj`, `value`)
   else:
     if not mangledNames.hasKey($field):
-      mangledNames[$field] = $mangleJsName($field)
+      mangledNames[$field] = mangleJsName($field)
     let importString = "#." & mangledNames[$field] & " = #"
+    let helperName = genSym(nskProc, "helper")
     result = quote do:
-      proc helper(o: JsObject, v: auto)
-        {. importcpp: `importString`, gensym .}
-      helper(`obj`, `value`)
+      proc `helperName`(o: JsObject, v: auto)
+        {.importjs: `importString`.}
+      `helperName`(`obj`, `value`)
 
 macro `.()`*(obj: JsObject,
              field: untyped,
@@ -243,32 +272,32 @@ macro `.()`*(obj: JsObject,
   ## so be careful when using this.)
   ##
   ## Example:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##  # Let's get back to the console example:
-  ##  var console {. importc, nodecl .}: JsObject
-  ##  let res = console.log("I return undefined!")
-  ##  console.log(res) # This prints undefined, as console.log always returns
-  ##                   # undefined. Thus one has to be careful, when using
-  ##                   # JsObject calls.
+  ##   ```nim
+  ##   # Let's get back to the console example:
+  ##   var console {.importc, nodecl.}: JsObject
+  ##   let res = console.log("I return undefined!")
+  ##   console.log(res) # This prints undefined, as console.log always returns
+  ##                    # undefined. Thus one has to be careful, when using
+  ##                    # JsObject calls.
+  ##   ```
   var importString: string
   if validJsName($field):
     importString = "#." & $field & "(@)"
   else:
     if not mangledNames.hasKey($field):
-      mangledNames[$field] = $mangleJsName($field)
+      mangledNames[$field] = mangleJsName($field)
     importString = "#." & mangledNames[$field] & "(@)"
-  result = quote:
-    proc helper(o: JsObject): JsObject
-      {. importcpp: `importString`, gensym, discardable .}
-    helper(`obj`)
+  let helperName = genSym(nskProc, "helper")
+  result = quote do:
+    proc `helperName`(o: JsObject): JsObject
+      {.importjs: `importString`, discardable.}
+    `helperName`(`obj`)
   for idx in 0 ..< args.len:
-    let paramName = newIdentNode(!("param" & $idx))
-    result[0][3].add newIdentDefs(paramName, newIdentNode(!"JsObject"))
+    let paramName = newIdentNode("param" & $idx)
+    result[0][3].add newIdentDefs(paramName, newIdentNode("JsObject"))
     result[1].add args[idx].copyNimTree
 
-macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V],
+macro `.`*[K: cstring, V](obj: JsAssoc[K, V],
                                    field: untyped): V =
   ## Experimental dot accessor (get) for type JsAssoc.
   ## Returns the value of a property of name `field` from a JsObject `x`.
@@ -277,14 +306,15 @@ macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V],
     importString = "#." & $field
   else:
     if not mangledNames.hasKey($field):
-      mangledNames[$field] = $mangleJsName($field)
+      mangledNames[$field] = mangleJsName($field)
     importString = "#." & mangledNames[$field]
+  let helperName = genSym(nskProc, "helper")
   result = quote do:
-    proc helper(o: type(`obj`)): `obj`.V
-      {. importcpp: `importString`, gensym .}
-    helper(`obj`)
+    proc `helperName`(o: type(`obj`)): `obj`.V
+      {.importjs: `importString`.}
+    `helperName`(`obj`)
 
-macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V],
+macro `.=`*[K: cstring, V](obj: JsAssoc[K, V],
                                     field: untyped,
                                     value: V): untyped =
   ## Experimental dot accessor (set) for type JsAssoc.
@@ -294,14 +324,15 @@ macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V],
     importString = "#." & $field & " = #"
   else:
     if not mangledNames.hasKey($field):
-      mangledNames[$field] = $mangleJsName($field)
+      mangledNames[$field] = mangleJsName($field)
     importString = "#." & mangledNames[$field] & " = #"
+  let helperName = genSym(nskProc, "helper")
   result = quote do:
-    proc helper(o: type(`obj`), v: `obj`.V)
-      {. importcpp: `importString`, gensym .}
-    helper(`obj`, `value`)
+    proc `helperName`(o: type(`obj`), v: `obj`.V)
+      {.importjs: `importString`.}
+    `helperName`(`obj`, `value`)
 
-macro `.()`*[K: string | cstring, V: proc](obj: JsAssoc[K, V],
+macro `.()`*[K: cstring, V: proc](obj: JsAssoc[K, V],
                                            field: untyped,
                                            args: varargs[untyped]): auto =
   ## Experimental "method call" operator for type JsAssoc.
@@ -317,14 +348,14 @@ macro `.()`*[K: string | cstring, V: proc](obj: JsAssoc[K, V],
 # Iterators:
 
 iterator pairs*(obj: JsObject): (cstring, JsObject) =
-  ## Yields tuples of type ``(cstring, JsObject)``, with the first entry
+  ## Yields tuples of type `(cstring, JsObject)`, with the first entry
   ## being the `name` of a fields in the JsObject and the second being its
   ## value wrapped into a JsObject.
   var k: cstring
   var v: JsObject
   {.emit: "for (var `k` in `obj`) {".}
-  {.emit: "  if (!`obj`.hasOwnProperty(`k`)) continue;".}
-  {.emit: "  `v`=`obj`[`k`];".}
+  {.emit: "  if (!`obj`.hasOwnProperty(`k`)) { continue; }".}
+  {.emit: "  `v` = `obj`[`k`];".}
   yield (k, v)
   {.emit: "}".}
 
@@ -332,8 +363,8 @@ iterator items*(obj: JsObject): JsObject =
   ## Yields the `values` of each field in a JsObject, wrapped into a JsObject.
   var v: JsObject
   {.emit: "for (var k in `obj`) {".}
-  {.emit: "  if (!`obj`.hasOwnProperty(k)) continue;".}
-  {.emit: "  `v`=`obj`[k];".}
+  {.emit: "  if (!`obj`.hasOwnProperty(k)) { continue; }".}
+  {.emit: "  `v` = `obj`[k];".}
   yield v
   {.emit: "}".}
 
@@ -341,75 +372,62 @@ iterator keys*(obj: JsObject): cstring =
   ## Yields the `names` of each field in a JsObject.
   var k: cstring
   {.emit: "for (var `k` in `obj`) {".}
-  {.emit: "  if (!`obj`.hasOwnProperty(`k`)) continue;".}
+  {.emit: "  if (!`obj`.hasOwnProperty(`k`)) { continue; }".}
   yield k
   {.emit: "}".}
 
-iterator pairs*[K, V](assoc: JsAssoc[K, V]): (K,V) =
-  ## Yields tuples of type ``(K, V)``, with the first entry
+iterator pairs*[K: JsKey, V](assoc: JsAssoc[K, V]): (K,V) =
+  ## Yields tuples of type `(K, V)`, with the first entry
   ## being a `key` in the JsAssoc and the second being its corresponding value.
-  when K is string:
-    var k: cstring
-  else:
-    var k: K
+  var k: cstring
   var v: V
   {.emit: "for (var `k` in `assoc`) {".}
-  {.emit: "  if (!`assoc`.hasOwnProperty(`k`)) continue;".}
-  {.emit: "  `v`=`assoc`[`k`];".}
-  when K is string:
-    yield ($k, v)
-  else:
-    yield (k, v)
+  {.emit: "  if (!`assoc`.hasOwnProperty(`k`)) { continue; }".}
+  {.emit: "  `v` = `assoc`[`k`];".}
+  yield (k.toJsKey(K), v)
   {.emit: "}".}
 
-iterator items*[K,V](assoc: JSAssoc[K,V]): V =
+iterator items*[K, V](assoc: JsAssoc[K, V]): V =
   ## Yields the `values` in a JsAssoc.
   var v: V
   {.emit: "for (var k in `assoc`) {".}
-  {.emit: "  if (!`assoc`.hasOwnProperty(k)) continue;".}
-  {.emit: "  `v`=`assoc`[k];".}
+  {.emit: "  if (!`assoc`.hasOwnProperty(k)) { continue; }".}
+  {.emit: "  `v` = `assoc`[k];".}
   yield v
   {.emit: "}".}
 
-iterator keys*[K,V](assoc: JSAssoc[K,V]): K =
+iterator keys*[K: JsKey, V](assoc: JsAssoc[K, V]): K =
   ## Yields the `keys` in a JsAssoc.
-  when K is string:
-    var k: cstring
-  else:
-    var k: K
+  var k: cstring
   {.emit: "for (var `k` in `assoc`) {".}
-  {.emit: "  if (!`assoc`.hasOwnProperty(`k`)) continue;".}
-  when K is string:
-    yield $k
-  else:
-    yield k
+  {.emit: "  if (!`assoc`.hasOwnProperty(`k`)) { continue; }".}
+  yield k.toJsKey(K)
   {.emit: "}".}
 
 # Literal generation
 
 macro `{}`*(typ: typedesc, xs: varargs[untyped]): auto =
-  ## Takes a ``typedesc`` as its first argument, and a series of expressions of
-  ## type ``key: value``, and returns a value of the specified type with each
-  ## field ``key`` set to ``value``, as specified in the arguments of ``{}``.
+  ## Takes a `typedesc` as its first argument, and a series of expressions of
+  ## type `key: value`, and returns a value of the specified type with each
+  ## field `key` set to `value`, as specified in the arguments of `{}`.
   ##
   ## Example:
   ##
-  ## .. code-block:: nim
-  ##
-  ##  # Let's say we have a type with a ton of fields, where some fields do not
-  ##  # need to be set, and we do not want those fields to be set to ``nil``:
-  ##  type
-  ##    ExtremelyHugeType = ref object
-  ##      a, b, c, d, e, f, g: int
-  ##      h, i, j, k, l: cstring
-  ##      # And even more fields ...
+  ##   ```nim
+  ##   # Let's say we have a type with a ton of fields, where some fields do not
+  ##   # need to be set, and we do not want those fields to be set to `nil`:
+  ##   type
+  ##     ExtremelyHugeType = ref object
+  ##       a, b, c, d, e, f, g: int
+  ##       h, i, j, k, l: cstring
+  ##       # And even more fields ...
   ##
-  ##  let obj = ExtremelyHugeType{ a: 1, k: "foo".cstring, d: 42 }
+  ##   let obj = ExtremelyHugeType{ a: 1, k: "foo".cstring, d: 42 }
   ##
-  ##  # This generates roughly the same JavaScript as:
-  ##  {. emit: "var obj = {a: 1, k: "foo", d: 42};" .}
-  ##
-  let a = !"a"
+  ##   # This generates roughly the same JavaScript as:
+  ##   {.emit: "var obj = {a: 1, k: "foo", d: 42};".}
+  ##   ```
+  let a = ident"a"
   var body = quote do:
     var `a` {.noinit.}: `typ`
     {.emit: "`a` = {};".}
@@ -442,47 +460,56 @@ macro `{}`*(typ: typedesc, xs: varargs[untyped]): auto =
 # Macro to build a lambda using JavaScript's `this`
 # from a proc, `this` being the first argument.
 
-macro bindMethod*(procedure: typed): auto =
+proc replaceSyms(n: NimNode): NimNode =
+  if n.kind == nnkSym:
+    result = newIdentNode($n)
+  else:
+    result = n
+    for i in 0..<n.len:
+      result[i] = replaceSyms(n[i])
+
+macro bindMethod*(procedure: typed): auto {.deprecated: "Don't use it with closures".} =
   ## Takes the name of a procedure and wraps it into a lambda missing the first
-  ## argument, which passes the JavaScript builtin ``this`` as the first
+  ## argument, which passes the JavaScript builtin `this` as the first
   ## argument to the procedure. Returns the resulting lambda.
   ##
   ## Example:
   ##
   ## We want to generate roughly this JavaScript:
+  ##   ```js
+  ##   var obj = {a: 10};
+  ##   obj.someMethod = function() {
+  ##     return this.a + 42;
+  ##   };
+  ##   ```
   ##
-  ## .. code-block:: js
-  ##  var obj = {a: 10};
-  ##  obj.someMethod = function() {
-  ##    return this.a + 42;
-  ##  };
-  ##
-  ## We can achieve this using the ``bindMethod`` macro:
+  ## We can achieve this using the `bindMethod` macro:
   ##
-  ## .. code-block:: nim
-  ##  let obj = JsObject{ a: 10 }
-  ##  proc someMethodImpl(that: JsObject): int =
-  ##    that.a.to(int) + 42
-  ##  obj.someMethod = bindMethod someMethodImpl
+  ##   ```nim
+  ##   let obj = JsObject{ a: 10 }
+  ##   proc someMethodImpl(that: JsObject): int =
+  ##     that.a.to(int) + 42
+  ##   obj.someMethod = bindMethod someMethodImpl
   ##
-  ##  # Alternatively:
-  ##  obj.someMethod = bindMethod
-  ##    proc(that: JsObject): int = that.a.to(int) + 42
+  ##   # Alternatively:
+  ##   obj.someMethod = bindMethod
+  ##     proc(that: JsObject): int = that.a.to(int) + 42
+  ##   ```
   if not (procedure.kind == nnkSym or procedure.kind == nnkLambda):
     error("Argument has to be a proc or a symbol corresponding to a proc.")
   var
     rawProc = if procedure.kind == nnkSym:
-        getImpl(procedure.symbol)
+        getImpl(procedure)
       else:
         procedure
-    args = rawProc[3]
+    args = rawProc[3].copyNimTree.replaceSyms
     thisType = args[1][1]
     params = newNimNode(nnkFormalParams).add(args[0])
     body = newNimNode(nnkLambda)
     this = newIdentNode("this")
     # construct the `this` parameter:
     thisQuote = quote do:
-      var `this` {. nodecl, importc .} : `thisType`
+      var `this` {.nodecl, importc: "this".}: `thisType`
     call = newNimNode(nnkCall).add(rawProc[0], thisQuote[0][0][0])
   # construct the procedure call inside the method
   if args.len > 2:
diff --git a/lib/js/jsre.nim b/lib/js/jsre.nim
new file mode 100644
index 000000000..2d931eb20
--- /dev/null
+++ b/lib/js/jsre.nim
@@ -0,0 +1,97 @@
+## Regular Expressions for the JavaScript target.
+## * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
+when not defined(js):
+  {.error: "This module only works on the JavaScript platform".}
+
+type RegExp* = ref object of JsRoot
+  ## Regular Expressions for JavaScript target.
+  ## See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
+  flags*: cstring        ## cstring that contains the flags of the RegExp object.
+  dotAll*: bool          ## Whether `.` matches newlines or not.
+  global*: bool          ## Whether to test against all possible matches in a string, or only against the first.
+  ignoreCase*: bool      ## Whether to ignore case while attempting a match in a string.
+  multiline*: bool       ## Whether to search in strings across multiple lines.
+  source*: cstring       ## The text of the pattern.
+  sticky*: bool          ## Whether the search is sticky.
+  unicode*: bool         ## Whether Unicode features are enabled.
+  lastIndex*: cint       ## Index at which to start the next match (read/write property).
+  input*: cstring        ## Read-only and modified on successful match.
+  lastMatch*: cstring    ## Ditto.
+  lastParen*: cstring    ## Ditto.
+  leftContext*: cstring  ## Ditto.
+  rightContext*: cstring ## Ditto.
+  hasIndices*: bool      ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices
+
+
+func newRegExp*(pattern: cstring; flags: cstring): RegExp {.importjs: "new RegExp(@)".}
+  ## Creates a new RegExp object.
+
+func newRegExp*(pattern: cstring): RegExp {.importjs: "new RegExp(@)".}
+
+func compile*(self: RegExp; pattern: cstring; flags: cstring) {.importjs: "#.compile(@)".}
+  ## Recompiles a regular expression during execution of a script.
+
+func replace*(pattern: cstring; self: RegExp; replacement: cstring): cstring {.importjs: "#.replace(#, #)".}
+  ## Returns a new string with some or all matches of a pattern replaced by given replacement
+
+func replace*(pattern: cstring, self: RegExp, cb: proc (args: varargs[cstring]): cstring): cstring {.importcpp.}
+  ## Returns a new string with some or all matches of a pattern replaced by given callback function
+
+func split*(pattern: cstring; self: RegExp): seq[cstring] {.importjs: "(#.split(#) || [])".}
+  ## Divides a string into an ordered list of substrings and returns the array
+
+func match*(pattern: cstring; self: RegExp): seq[cstring] {.importjs: "(#.match(#) || [])".}
+  ## Returns an array of matches of a RegExp against given string
+
+func exec*(self: RegExp; pattern: cstring): seq[cstring] {.importjs: "(#.exec(#) || [])".}
+  ## Executes a search for a match in its string parameter.
+
+func toCstring*(self: RegExp): cstring {.importjs: "#.toString()".}
+  ## Returns a string representing the RegExp object.
+
+func `$`*(self: RegExp): string = $toCstring(self)
+
+func contains*(pattern: cstring; self: RegExp): bool =
+  ## Tests for a substring match in its string parameter.
+  runnableExamples:
+    let jsregex: RegExp = newRegExp(r"bc$", r"i")
+    assert jsregex in r"abc"
+    assert jsregex notin r"abcd"
+    assert "xabc".contains jsregex
+  {.emit: "`result` = `self`.test(`pattern`);".}
+
+func startsWith*(pattern: cstring; self: RegExp): bool =
+  ## Tests if string starts with given RegExp
+  runnableExamples:
+    let jsregex: RegExp = newRegExp(r"abc", r"i")
+    assert "abcd".startsWith jsregex
+  pattern.contains(newRegExp(("^" & $(self.source)).cstring, self.flags))
+
+func endsWith*(pattern: cstring; self: RegExp): bool =
+  ## Tests if string ends with given RegExp
+  runnableExamples:
+    let jsregex: RegExp = newRegExp(r"bcd", r"i")
+    assert "abcd".endsWith jsregex
+  pattern.contains(newRegExp(($(self.source) & "$").cstring, self.flags))
+
+
+runnableExamples:
+  let jsregex: RegExp = newRegExp(r"\s+", r"i")
+  jsregex.compile(r"\w+", r"i")
+  assert "nim javascript".contains jsregex
+  assert jsregex.exec(r"nim javascript") == @["nim".cstring]
+  assert jsregex.toCstring() == r"/\w+/i"
+  jsregex.compile(r"[0-9]", r"i")
+  assert "0123456789abcd".contains jsregex
+  assert $jsregex == "/[0-9]/i"
+  jsregex.compile(r"abc", r"i")
+  assert "abcd".startsWith jsregex
+  assert "dabc".endsWith jsregex
+  jsregex.compile(r"\d", r"i")
+  assert "do1ne".split(jsregex) == @["do".cstring, "ne".cstring]
+  jsregex.compile(r"[lw]", r"i")
+  assert "hello world".replace(jsregex,"X") == "heXlo world"
+  jsregex.compile(r"([a-z])\1*", r"g")
+  assert "abbcccdddd".replace(jsregex, proc (m: varargs[cstring]): cstring = ($m[0] & $(m.len)).cstring) == "a1b2c3d4"
+  let digitsRegex: RegExp = newRegExp(r"\d")
+  assert "foo".match(digitsRegex) == @[]
diff --git a/lib/nimbase.h b/lib/nimbase.h
index 6dc742910..cf0c8002b 100644
--- a/lib/nimbase.h
+++ b/lib/nimbase.h
@@ -10,13 +10,12 @@
 /* compiler symbols:
 __BORLANDC__
 _MSC_VER
-__WATCOMC__
-__LCC__
 __GNUC__
-__DMC__
-__POCC__
 __TINYC__
 __clang__
+__AVR__
+__arm__
+__EMSCRIPTEN__
 */
 
 
@@ -74,7 +73,8 @@ __clang__
 #endif
 /* ------------------------------------------------------------------------- */
 
-#if defined(__GNUC__)
+#if defined(__GNUC__) && !defined(__ZEPHYR__)
+/* Zephyr does some magic in it's headers that override the GCC stdlib. This breaks that. */
 #  define _GNU_SOURCE 1
 #endif
 
@@ -87,11 +87,8 @@ __clang__
 #endif
 
 /* calling convention mess ----------------------------------------------- */
-#if defined(__GNUC__) || defined(__LCC__) || defined(__POCC__) \
-                      || defined(__TINYC__)
+#if defined(__GNUC__) || defined(__TINYC__)
   /* these should support C99's inline */
-  /* the test for __POCC__ has to come before the test for _MSC_VER,
-     because PellesC defines _MSC_VER too. This is brain-dead. */
 #  define N_INLINE(rettype, name) inline rettype name
 #elif defined(__BORLANDC__) || defined(_MSC_VER)
 /* Borland's compiler is really STRANGE here; note that the __fastcall
@@ -99,19 +96,13 @@ __clang__
    the return type, so we do not handle this mess in the code generator
    but rather here. */
 #  define N_INLINE(rettype, name) __inline rettype name
-#elif defined(__DMC__)
-#  define N_INLINE(rettype, name) inline rettype name
-#elif defined(__WATCOMC__)
-#  define N_INLINE(rettype, name) __inline rettype name
 #else /* others are less picky: */
 #  define N_INLINE(rettype, name) rettype __inline name
 #endif
 
-#if defined(__POCC__)
-#  define NIM_CONST /* PCC is really picky with const modifiers */
-#  undef _MSC_VER /* Yeah, right PCC defines _MSC_VER even if it is
-                     not that compatible. Well done. */
-#elif defined(__cplusplus)
+#define N_INLINE_PTR(rettype, name) rettype (*name)
+
+#if defined(__cplusplus)
 #  define NIM_CONST /* C++ is picky with const modifiers */
 #else
 #  define NIM_CONST  const
@@ -121,28 +112,36 @@ __clang__
   NIM_THREADVAR declaration based on
   http://stackoverflow.com/questions/18298280/how-to-declare-a-variable-as-thread-local-portably
 */
-#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
+#if defined _WIN32
+#  if defined _MSC_VER || defined __BORLANDC__
+#    define NIM_THREADVAR __declspec(thread)
+#  else
+#    define NIM_THREADVAR __thread
+#  endif
+#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
 #  define NIM_THREADVAR _Thread_local
 #elif defined _WIN32 && ( \
        defined _MSC_VER || \
        defined __ICL || \
-       defined __DMC__ || \
        defined __BORLANDC__ )
 #  define NIM_THREADVAR __declspec(thread)
+#elif defined(__TINYC__) || defined(__GENODE__)
+#  define NIM_THREADVAR
 /* note that ICC (linux) and Clang are covered by __GNUC__ */
 #elif defined __GNUC__ || \
        defined __SUNPRO_C || \
        defined __xlC__
 #  define NIM_THREADVAR __thread
-#elif defined __TINYC__
-#  define NIM_THREADVAR
 #else
 #  error "Cannot define NIM_THREADVAR"
 #endif
 
+#if defined(__cplusplus)
+  #define NIM_THREAD_LOCAL thread_local
+#endif
+
 /* --------------- how int64 constants should be declared: ----------- */
-#if defined(__GNUC__) || defined(__LCC__) || \
-    defined(__POCC__) || defined(__DMC__) || defined(_MSC_VER)
+#if defined(__GNUC__) || defined(_MSC_VER)
 #  define IL64(x) x##LL
 #else /* works only without LL */
 #  define IL64(x) ((NI64)x)
@@ -156,7 +155,13 @@ __clang__
 #  define NIM_CAST(type, ptr) ((type)(ptr))
 #endif
 
+
 /* ------------------------------------------------------------------- */
+#ifdef  __cplusplus
+#  define NIM_EXTERNC extern "C"
+#else
+#  define NIM_EXTERNC
+#endif
 
 #if defined(WIN32) || defined(_WIN32) /* only Windows has this mess... */
 #  define N_LIB_PRIVATE
@@ -164,18 +169,22 @@ __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_THISCALL(rettype, name) rettype __thiscall 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_THISCALL_PTR(rettype, name) rettype (__thiscall *name)
+#  define N_SAFECALL_PTR(rettype, name) rettype (__stdcall *name)
 
-#  ifdef __cplusplus
-#    define N_LIB_EXPORT  extern "C" __declspec(dllexport)
+#  ifdef __EMSCRIPTEN__
+#    define N_LIB_EXPORT  NIM_EXTERNC __declspec(dllexport) __attribute__((used))
+#    define N_LIB_EXPORT_VAR  __declspec(dllexport) __attribute__((used))
 #  else
-#    define N_LIB_EXPORT  extern __declspec(dllexport)
+#    define N_LIB_EXPORT  NIM_EXTERNC __declspec(dllexport)
+#    define N_LIB_EXPORT_VAR  __declspec(dllexport)
 #  endif
 #  define N_LIB_IMPORT  extern __declspec(dllimport)
 #else
@@ -205,10 +214,12 @@ __clang__
 #    define N_FASTCALL_PTR(rettype, name) rettype (*name)
 #    define N_SAFECALL_PTR(rettype, name) rettype (*name)
 #  endif
-#  ifdef __cplusplus
-#    define N_LIB_EXPORT  extern "C"
+#  ifdef __EMSCRIPTEN__
+#    define N_LIB_EXPORT NIM_EXTERNC __attribute__((visibility("default"), used))
+#    define N_LIB_EXPORT_VAR  __attribute__((visibility("default"), used))
 #  else
-#    define N_LIB_EXPORT  extern
+#    define N_LIB_EXPORT NIM_EXTERNC __attribute__((visibility("default")))
+#    define N_LIB_EXPORT_VAR  __attribute__((visibility("default")))
 #  endif
 #  define N_LIB_IMPORT  extern
 #endif
@@ -218,7 +229,7 @@ __clang__
 #define N_NOCONV_PTR(rettype, name) rettype (*name)
 
 #if defined(__GNUC__) || defined(__ICC__)
-#  define N_NOINLINE(rettype, name) rettype __attribute__((noinline)) name
+#  define N_NOINLINE(rettype, name) rettype __attribute__((__noinline__)) name
 #elif defined(_MSC_VER)
 #  define N_NOINLINE(rettype, name) __declspec(noinline) rettype name
 #else
@@ -227,8 +238,7 @@ __clang__
 
 #define N_NOINLINE_PTR(rettype, name) rettype (*name)
 
-#if defined(__BORLANDC__) || defined(__WATCOMC__) || \
-    defined(__POCC__) || defined(_MSC_VER) || defined(WIN32) || defined(_WIN32)
+#if defined(__BORLANDC__) || defined(_MSC_VER) || defined(WIN32) || defined(_WIN32)
 /* these compilers have a fastcall so use it: */
 #  ifdef __TINYC__
 #    define N_NIMCALL(rettype, name) rettype __attribute((__fastcall)) name
@@ -255,78 +265,169 @@ __clang__
 #include <limits.h>
 #include <stddef.h>
 
+// define NIM_STATIC_ASSERT
+// example use case: CT sizeof for importc types verification
+// where we have {.completeStruct.} (or lack of {.incompleteStruct.})
+#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L)
+#define NIM_STATIC_ASSERT(x, msg) _Static_assert((x), msg)
+#elif defined(__cplusplus)
+#define NIM_STATIC_ASSERT(x, msg) static_assert((x), msg)
+#else
+#define _NIM_STATIC_ASSERT_FINAL(x, append_name) typedef int NIM_STATIC_ASSERT_AUX ## append_name[(x) ? 1 : -1];
+#define _NIM_STATIC_ASSERT_STAGE_3(x, line)      _NIM_STATIC_ASSERT_FINAL(x, _AT_LINE_##line)
+#define _NIM_STATIC_ASSERT_STAGE_2(x, line)      _NIM_STATIC_ASSERT_STAGE_3(x, line)
+#define NIM_STATIC_ASSERT(x, msg)                _NIM_STATIC_ASSERT_STAGE_2(x,__LINE__)
+// On failure, your C compiler will say something like:
+//   "error: 'NIM_STATIC_ASSERT_AUX_AT_LINE_XXX' declared as an array with a negative size"
+// Adding the line number helps to avoid redefinitions which are not allowed in
+// old GCC versions, however the order of evaluation for __LINE__ is a little tricky,
+// hence all the helper macros. See https://stackoverflow.com/a/3385694 for more info.
+#endif
+
 /* C99 compiler? */
-#if (defined(__STD_VERSION__) && (__STD_VERSION__ >= 199901))
+#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901))
 #  define HAVE_STDINT_H
 #endif
 
-#if defined(__LCC__) || defined(__DMC__) || defined(__POCC__)
+/* Known compiler with stdint.h that doesn't fit the general pattern? */
+#if defined(__AVR__) || (defined(__cplusplus) && (__cplusplus < 201103))
 #  define HAVE_STDINT_H
 #endif
 
+#if (!defined(HAVE_STDINT_H) && defined(__cplusplus) && (__cplusplus >= 201103))
+#  define HAVE_CSTDINT
+#endif
+
+
 /* wrap all Nim typedefs into namespace Nim */
 #ifdef USE_NIM_NAMESPACE
-namespace Nim {
+#ifdef HAVE_CSTDINT
+#include <cstdint>
+#else
+#include <stdint.h>
+#endif
+namespace USE_NIM_NAMESPACE {
+#endif
+
+// preexisting check, seems paranoid, maybe remove
+#if defined(NIM_TRUE) || defined(NIM_FALSE) || defined(NIM_BOOL)
+#error "nim reserved preprocessor macros clash"
 #endif
 
 /* bool types (C++ has it): */
 #ifdef __cplusplus
-#  ifndef NIM_TRUE
-#    define NIM_TRUE true
-#  endif
-#  ifndef NIM_FALSE
-#    define NIM_FALSE false
-#  endif
-#  define NIM_BOOL bool
-#  define NIM_NIL 0
+#define NIM_BOOL bool
+#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901)
+// see #13798: to avoid conflicts for code emitting `#include <stdbool.h>`
+#define NIM_BOOL _Bool
 #else
-#  ifdef bool
-#    define NIM_BOOL bool
+typedef unsigned char NIM_BOOL; // best effort
+#endif
+
+NIM_STATIC_ASSERT(sizeof(NIM_BOOL) == 1, ""); // check whether really needed
+NIM_STATIC_ASSERT(CHAR_BIT == 8, "");
+  // fail fast for (rare) environments where this doesn't hold, as some implicit
+  // assumptions would need revisiting (e.g. `uint8` or https://github.com/nim-lang/Nim/pull/18505)
+
+#define NIM_TRUE true
+#define NIM_FALSE false
+
+#ifdef __cplusplus
+#  if __cplusplus >= 201103L
+#    /* nullptr is more type safe (less implicit conversions than 0) */
+#    define NIM_NIL nullptr
 #  else
-  typedef unsigned char NIM_BOOL;
-#  endif
-#  ifndef NIM_TRUE
-#    define NIM_TRUE ((NIM_BOOL) 1)
-#  endif
-#  ifndef NIM_FALSE
-#    define NIM_FALSE ((NIM_BOOL) 0)
+#    // both `((void*)0)` and `NULL` would cause codegen to emit
+#    // error: assigning to 'Foo *' from incompatible type 'void *'
+#    // but codegen could be fixed if need. See also potential caveat regarding
+#    // NULL.
+#    // However, `0` causes other issues, see #13798
+#    define NIM_NIL 0
 #  endif
+#else
+#  include <stdbool.h>
 #  define NIM_NIL ((void*)0) /* C's NULL is fucked up in some C compilers, so
                               the generated code does not rely on it anymore */
 #endif
 
-#if defined(__BORLANDC__) || defined(__DMC__) \
-   || defined(__WATCOMC__) || defined(_MSC_VER)
+#if defined(__BORLANDC__) || defined(_MSC_VER)
 typedef signed char NI8;
 typedef signed short int NI16;
 typedef signed int NI32;
+typedef __int64 NI64;
 /* XXX: Float128? */
 typedef unsigned char NU8;
 typedef unsigned short int NU16;
-typedef unsigned __int64 NU64;
-typedef __int64 NI64;
 typedef unsigned int NU32;
+typedef unsigned __int64 NU64;
 #elif defined(HAVE_STDINT_H)
+#ifndef USE_NIM_NAMESPACE
 #  include <stdint.h>
+#endif
 typedef int8_t NI8;
 typedef int16_t NI16;
 typedef int32_t NI32;
 typedef int64_t NI64;
-typedef uint64_t NU64;
 typedef uint8_t NU8;
 typedef uint16_t NU16;
 typedef uint32_t NU32;
+typedef uint64_t NU64;
+#elif defined(HAVE_CSTDINT)
+#ifndef USE_NIM_NAMESPACE
+#  include <cstdint>
+#endif
+typedef std::int8_t NI8;
+typedef std::int16_t NI16;
+typedef std::int32_t NI32;
+typedef std::int64_t NI64;
+typedef std::uint8_t NU8;
+typedef std::uint16_t NU16;
+typedef std::uint32_t NU32;
+typedef std::uint64_t NU64;
+#else
+/* Unknown compiler/version, do our best */
+#ifdef __INT8_TYPE__
+typedef __INT8_TYPE__ NI8;
 #else
 typedef signed char NI8;
+#endif
+#ifdef __INT16_TYPE__
+typedef __INT16_TYPE__ NI16;
+#else
 typedef signed short int NI16;
+#endif
+#ifdef __INT32_TYPE__
+typedef __INT32_TYPE__ NI32;
+#else
 typedef signed int NI32;
+#endif
+#ifdef __INT64_TYPE__
+typedef __INT64_TYPE__ NI64;
+#else
+typedef long long int NI64;
+#endif
 /* XXX: Float128? */
+#ifdef __UINT8_TYPE__
+typedef __UINT8_TYPE__ NU8;
+#else
 typedef unsigned char NU8;
+#endif
+#ifdef __UINT16_TYPE__
+typedef __UINT16_TYPE__ NU16;
+#else
 typedef unsigned short int NU16;
-typedef unsigned long long int NU64;
-typedef long long int NI64;
+#endif
+#ifdef __UINT32_TYPE__
+typedef __UINT32_TYPE__ NU32;
+#else
 typedef unsigned int NU32;
 #endif
+#ifdef __UINT64_TYPE__
+typedef __UINT64_TYPE__ NU64;
+#else
+typedef unsigned long long int NU64;
+#endif
+#endif
 
 #ifdef NIM_INTBITS
 #  if NIM_INTBITS == 64
@@ -346,7 +447,12 @@ typedef NU8 NU;
 #  endif
 #endif
 
+// for now there isn't an easy way for C code to reach the program result
+// when hot code reloading is ON - users will have to:
+// load the nimhcr.dll, get the hcrGetGlobal proc from there and use it
+#ifndef NIM_HOT_CODE_RELOADING
 extern NI nim_program_result;
+#endif
 
 typedef float NF32;
 typedef double NF64;
@@ -361,18 +467,6 @@ typedef char* NCSTRING;
 #  define NIM_IMAN 0
 #endif
 
-static N_INLINE(NI, float64ToInt32)(double x) {
-  /* nowadays no hack necessary anymore */
-  return x >= 0 ? (NI)(x+0.5) : (NI)(x-0.5);
-}
-
-static N_INLINE(NI32, float32ToInt32)(float x) {
-  /* nowadays no hack necessary anymore */
-  return x >= 0 ? (NI32)(x+0.5) : (NI32)(x-0.5);
-}
-
-#define float64ToInt64(x) ((NI64) (x))
-
 #define NIM_STRLIT_FLAG ((NU)(1) << ((NIM_INTBITS) - 2)) /* This has to be the same as system.strlitFlag! */
 
 #define STRING_LITERAL(name, str, length) \
@@ -381,10 +475,10 @@ static N_INLINE(NI32, float32ToInt32)(float x) {
      NIM_CHAR data[(length) + 1];          \
   } name = {{length, (NI) ((NU)length | NIM_STRLIT_FLAG)}, str}
 
-typedef struct TStringDesc* string;
-
 /* declared size of a sequence/variable length array: */
-#if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER)
+#if defined(__cplusplus) && defined(__clang__)
+#  define SEQ_DECL_SIZE 1
+#elif defined(__GNUC__) || defined(_MSC_VER)
 #  define SEQ_DECL_SIZE /* empty is correct! */
 #else
 #  define SEQ_DECL_SIZE 1000000
@@ -393,7 +487,6 @@ typedef struct TStringDesc* string;
 #define ALLOC_0(size)  calloc(1, size)
 #define DL_ALLOC_0(size) dlcalloc(1, size)
 
-#define GenericSeqSize sizeof(TGenericSeq)
 #define paramCount() cmdCount
 
 // NAN definition copied from math.h included in the Windows SDK version 10.0.14393.0
@@ -426,45 +519,22 @@ struct TFrame_ {
   NCSTRING filename;
   NI16 len;
   NI16 calldepth;
+  NI frameMsgLen;
 };
 
-#ifdef NIM_NEW_MANGLING_RULES
-  #define nimfr_(proc, file) \
-    TFrame FR_; \
-    FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; nimFrame(&FR_);
-
-  #define nimfrs_(proc, file, slots, length) \
-    struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; VarSlot s[slots];} FR_; \
-    FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = length; nimFrame((TFrame*)&FR_);
-
-  #define nimln_(n, file) \
-    FR_.line = n; FR_.filename = file;
-#else
-  #define nimfr(proc, file) \
-    TFrame FR; \
-    FR.procname = proc; FR.filename = file; FR.line = 0; FR.len = 0; nimFrame(&FR);
-
-  #define nimfrs(proc, file, slots, length) \
-    struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; VarSlot s[slots];} FR; \
-    FR.procname = proc; FR.filename = file; FR.line = 0; FR.len = length; nimFrame((TFrame*)&FR);
-
-  #define nimln(n, file) \
-    FR.line = n; FR.filename = file;
-#endif
-
 #define NIM_POSIX_INIT  __attribute__((constructor))
 
 #ifdef __GNUC__
-#  define likely(x) __builtin_expect(x, 1)
-#  define unlikely(x) __builtin_expect(x, 0)
+#  define NIM_LIKELY(x) __builtin_expect(x, 1)
+#  define NIM_UNLIKELY(x) __builtin_expect(x, 0)
 /* We need the following for the posix wrapper. In particular it will give us
    POSIX_SPAWN_USEVFORK: */
 #  ifndef _GNU_SOURCE
 #    define _GNU_SOURCE
 #  endif
 #else
-#  define likely(x) (x)
-#  define unlikely(x) (x)
+#  define NIM_LIKELY(x) (x)
+#  define NIM_UNLIKELY(x) (x)
 #endif
 
 #if 0 // defined(__GNUC__) || defined(__clang__)
@@ -476,19 +546,19 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); }
 #  define GC_GUARD
 #endif
 
-/* Test to see if Nim and the C compiler agree on the size of a pointer.
-   On disagreement, your C compiler will say something like:
-   "error: 'Nim_and_C_compiler_disagree_on_target_architecture' declared as an array with a negative size" */
-typedef int Nim_and_C_compiler_disagree_on_target_architecture[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];
+// Test to see if Nim and the C compiler agree on the size of a pointer.
+NIM_STATIC_ASSERT(sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8, "Pointer size mismatch between Nim and C/C++ backend. You probably need to setup the backend compiler for target CPU.");
 
 #ifdef USE_NIM_NAMESPACE
 }
 #endif
 
-#ifdef  __cplusplus
-#  define NIM_EXTERNC extern "C"
+#if defined(_MSC_VER)
+#  define NIM_ALIGN(x)  __declspec(align(x))
+#  define NIM_ALIGNOF(x) __alignof(x)
 #else
-#  define NIM_EXTERNC
+#  define NIM_ALIGN(x)  __attribute__((aligned(x)))
+#  define NIM_ALIGNOF(x) __alignof__(x)
 #endif
 
 /* ---------------- platform specific includes ----------------------- */
@@ -502,8 +572,38 @@ typedef int Nim_and_C_compiler_disagree_on_target_architecture[sizeof(NI) == siz
 #  include <sys/types.h>
 #endif
 
-/* Compile with -d:checkAbi and a sufficiently C11:ish compiler to enable */
-#define NIM_CHECK_SIZE(typ, sz) \
-  _Static_assert(sizeof(typ) == sz, "Nim & C disagree on type size")
+/* these exist to make the codegen logic simpler */
+#define nimModInt(a, b, res) (((*res) = (a) % (b)), 0)
+#define nimModInt64(a, b, res) (((*res) = (a) % (b)), 0)
+
+#if (!defined(_MSC_VER) || defined(__clang__)) && !defined(NIM_EmulateOverflowChecks)
+  /* these exist because we cannot have .compilerProcs that are importc'ed
+    by a different name */
+
+  #define nimAddInt64(a, b, res) __builtin_saddll_overflow(a, b, (long long int*)res)
+  #define nimSubInt64(a, b, res) __builtin_ssubll_overflow(a, b, (long long int*)res)
+  #define nimMulInt64(a, b, res) __builtin_smulll_overflow(a, b, (long long int*)res)
+
+  #if NIM_INTBITS == 32
+    #if defined(__arm__) && defined(__GNUC__)
+      /* arm-none-eabi-gcc targets defines int32_t as long int */
+      #define nimAddInt(a, b, res) __builtin_saddl_overflow(a, b, res)
+      #define nimSubInt(a, b, res) __builtin_ssubl_overflow(a, b, res)
+      #define nimMulInt(a, b, res) __builtin_smull_overflow(a, b, res)
+    #else
+      #define nimAddInt(a, b, res) __builtin_sadd_overflow(a, b, res)
+      #define nimSubInt(a, b, res) __builtin_ssub_overflow(a, b, res)
+      #define nimMulInt(a, b, res) __builtin_smul_overflow(a, b, res)
+    #endif
+  #else
+    /* map it to the 'long long' variant */
+    #define nimAddInt(a, b, res) __builtin_saddll_overflow(a, b, (long long int*)res)
+    #define nimSubInt(a, b, res) __builtin_ssubll_overflow(a, b, (long long int*)res)
+    #define nimMulInt(a, b, res) __builtin_smulll_overflow(a, b, (long long int*)res)
+  #endif
+#endif
+
+#define NIM_NOALIAS __restrict
+/* __restrict is said to work for all the C(++) compilers out there that we support */
 
 #endif /* NIMBASE_H */
diff --git a/lib/nimhcr.nim b/lib/nimhcr.nim
new file mode 100644
index 000000000..e87bb2413
--- /dev/null
+++ b/lib/nimhcr.nim
@@ -0,0 +1,671 @@
+discard """
+batchable: false
+"""
+
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2018 Nim Contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# This is the Nim hot code reloading run-time for the native targets.
+#
+# This minimal dynamic library is not subject to reloading when the
+# `hotCodeReloading` build mode is enabled. It's responsible for providing
+# a permanent memory location for all globals and procs within a program
+# and orchestrating the reloading. For globals, this is easily achieved
+# by storing them on the heap. For procs, we produce on the fly simple
+# trampolines that can be dynamically overwritten to jump to a different
+# target. In the host program, all globals and procs are first registered
+# here with `hcrRegisterGlobal` and `hcrRegisterProc` and then the
+# returned permanent locations are used in every reference to these symbols
+# onwards.
+#
+# Detailed description:
+#
+# When code is compiled with the hotCodeReloading option for native targets
+# a couple of things happen for all modules in a project:
+# - the useNimRtl option is forced (including when building the HCR runtime too)
+# - all modules of a target get built into separate shared libraries
+#   - the smallest granularity of reloads is modules
+#   - for each .c (or .cpp) in the corresponding nimcache folder of the project
+#     a shared object is built with the name of the source file + DLL extension
+#   - only the main module produces whatever the original project type intends
+#     (again in nimcache) and is then copied to its original destination
+#   - linking is done in parallel - just like compilation
+# - function calls to functions from the same project go through function pointers:
+#   - with a few exceptions - see the nonReloadable pragma
+#   - the forward declarations of the original functions become function
+#     pointers as static globals with the same names
+#   - the original function definitions get suffixed with <name>_actual
+#   - the function pointers get initialized with the address of the corresponding
+#     function in the DatInit of their module through a call to either hcrRegisterProc
+#     or hcrGetProc. When being registered, the <name>_actual address is passed to
+#     hcrRegisterProc and a permanent location is returned and assigned to the pointer.
+#     This way the implementation (<name>_actual) can change but the address for it
+#     will be the same - this works by just updating a jump instruction (trampoline).
+#     For functions from other modules hcrGetProc is used (after they are registered).
+# - globals are initialized only once and their state is preserved
+#   - including locals with the {.global.} pragma
+#   - their definitions are changed into pointer definitions which are initialized
+#     in the DatInit() of their module with calls to hcrRegisterGlobal (supplying the
+#     size of the type that this HCR runtime should allocate) and a bool is returned
+#     which when true triggers the initialization code for the global (only once).
+#     Globals from other modules: a global pointer coupled with a hcrGetGlobal call.
+#   - globals which have already been initialized cannot have their values changed
+#     by changing their initialization - use a handler or some other mechanism
+#   - new globals can be introduced when reloading
+# - top-level code (global scope) is executed only once - at the first module load
+# - the runtime knows every symbol's module owner (globals and procs)
+# - both the RTL and HCR shared libraries need to be near the program for execution
+#   - same folder, in the PATH or LD_LIBRARY_PATH env var, etc (depending on OS)
+# - the main module is responsible for initializing the HCR runtime
+#   - the main module loads the RTL and HCR shared objects
+#   - after that a call to hcrInit() is done in the main module which triggers
+#     the loading of all modules the main one imports, and doing that for the
+#     dependencies of each module recursively. Basically a DFS traversal.
+#   - then initialization takes place with several passes over all modules:
+#     - HcrInit - initializes the pointers for HCR procs such as hcrRegisterProc
+#     - HcrCreateTypeInfos - creates globals which will be referenced in the next pass
+#     - DatInit - usual dat init + register/get procs and get globals
+#     - Init - it does the following multiplexed operations:
+#       - register globals (if already registered - then just retrieve pointer)
+#       - execute top level scope (only if loaded for the first time)
+#   - when modules are loaded the originally built shared libraries get copied in
+#     the same folder and the copies are loaded instead of the original files
+#   - a module import tree is built in the runtime (and maintained when reloading)
+# - hcrPerformCodeReload
+#   - named `performCodeReload`, requires the hotcodereloading module
+#   - explicitly called by the user - the current active callstack shouldn't contain
+#     any functions which are defined in modules that will be reloaded (or crash!).
+#     The reason is that old dynamic libraries get unloaded.
+#     Example:
+#       if A is the main module and it imports B, then only B is reloadable and only
+#       if when calling hcrPerformCodeReload there is no function defined in B in the
+#       current active callstack at the point of the call (it has to be done from A)
+#   - for reloading to take place the user has to have rebuilt parts of the application
+#     without changes affecting the main module in any way - it shouldn't be rebuilt.
+#   - to determine what needs to be reloaded the runtime starts traversing the import
+#     tree from the root and checks the timestamps of the loaded shared objects
+#   - modules that are no longer referenced are unloaded and cleaned up properly
+#   - symbols (procs/globals) that have been removed in the code are also cleaned up
+#     - so changing the init of a global does nothing, but removing it, reloading,
+#       and then re-introducing it with a new initializer works
+#   - new modules can be imported, and imports can also be reodereded/removed
+#   - hcrReloadNeeded() can be used to determine if any module needs reloading
+#     - named `hasAnyModuleChanged`, requires the hotcodereloading module
+# - code in the beforeCodeReload/afterCodeReload handlers is executed on each reload
+#   - require the hotcodereloading module
+#   - such handlers can be added and removed
+#   - before each reload all "beforeCodeReload" handlers are executed and after
+#     that all handlers (including "after") from the particular module are deleted
+#   - the order of execution is the same as the order of top-level code execution.
+#     Example: if A imports B which imports C, then all handlers in C will be executed
+#     first (from top to bottom) followed by all from B and lastly all from A
+#   - after the reload all "after" handlers are executed the same way as "before"
+#   - the handlers for a reloaded module are always removed when reloading and then
+#     registered when the top-level scope is executed (thanks to `executeOnReload`)
+#
+# TODO next:
+#
+# - implement the before/after handlers and hasModuleChanged for the javascript target
+# - ARM support for the trampolines
+# - investigate:
+#   - soon the system module might be importing other modules - the init order...?
+#     (revert https://github.com/nim-lang/Nim/pull/11971 when working on this)
+#   - rethink the closure iterators
+#     - ability to keep old versions of dynamic libraries alive
+#       - because of async server code
+#       - perhaps with refcounting of .dlls for unfinished closures
+#   - linking with static libs
+#     - all shared objects for each module will (probably) have to link to them
+#       - state in static libs gets duplicated
+#       - linking is slow and therefore iteration time suffers
+#         - have just a single .dll for all .nim files and bulk reload?
+#   - think about the compile/link/passc/passl/emit/injectStmt pragmas
+#     - if a passc pragma is introduced (either written or dragged in by a new
+#       import) the whole command line for compilation changes - for example:
+#         winlean.nim: {.passc: "-DWIN32_LEAN_AND_MEAN".}
+#   - play with plugins/dlls/lfIndirect/lfDynamicLib/lfExportLib - shouldn't add an extra '*'
+#   - everything thread-local related
+# - tests
+#   - add a new travis build matrix entry which builds everything with HCR enabled
+#     - currently building with useNimRtl is problematic - lots of problems...
+#     - how to supply the nimrtl/nimhcr shared objects to all test binaries...?
+#     - think about building to C++ instead of only to C - added type safety
+#   - run tests through valgrind and the sanitizers!
+#
+# TODO - nice to have cool stuff:
+#
+# - separate handling of global state for much faster reloading and manipulation
+#   - imagine sliders in an IDE for tweaking variables
+#   - perhaps using shared memory
+# - multi-dll projects - how everything can be reloaded..?
+#   - a single HCR instance shared across multiple .dlls
+#   - instead of having to call hcrPerformCodeReload from a function in each dll
+#     - which currently renders the main module of each dll not reloadable
+# - ability to check with the current callstack if a reload is "legal"
+#   - if it is in any function which is in a module about to be reloaded ==> error
+# - pragma annotations for files - to be excluded from dll shenanigans
+#   - for such file-global pragmas look at codeReordering or injectStmt
+#   - how would the initialization order be kept? messy...
+# - C code calling stable exportc interface of nim code (for bindings)
+#   - generate proxy functions with the stable names
+#     - in a non-reloadable part (the main binary) that call the function pointers
+#     - parameter passing/forwarding - how? use the same trampoline jumping?
+#     - extracting the dependencies for these stubs/proxies will be hard...
+# - changing memory layout of types - detecting this..?
+#   - implement with registerType() call to HCR runtime...?
+#     - and checking if a previously registered type matches
+#   - issue an error
+#     - or let the user handle this by transferring the state properly
+#       - perhaps in the before/afterCodeReload handlers
+# - implement executeOnReload for global vars too - not just statements (and document!)
+# - cleanup at shutdown - freeing all globals
+# - fallback mechanism if the program crashes (the program should detect crashes
+#   by itself using SEH/signals on Windows/Unix) - should be able to revert to
+#   previous versions of the .dlls by calling some function from HCR
+# - improve runtime performance - possibilities
+#   - implement a way for multiple .nim files to be bundled into the same dll
+#     and have all calls within that domain to use the "_actual" versions of
+#     procs so there are no indirections (or the ability to just bundle everything
+#     except for a few unreloadable modules into a single mega reloadable dll)
+#   - try to load the .dlls at specific addresses of memory (close to each other)
+#     allocated with execution flags - check this: https://github.com/fancycode/MemoryModule
+#
+# TODO - unimportant:
+#
+# - have a "bad call" trampoline that all no-longer-present functions are routed to call there
+#     - so the user gets some error msg if he calls a dangling pointer instead of a crash
+# - before/afterCodeReload and hasModuleChanged should be accessible only where appropriate
+# - nim_program_result is inaccessible in HCR mode from external C code (see nimbase.h)
+# - proper .json build file - but the format is different... multiple link commands...
+# - avoid registering globals on each loop when using an iterator in global scope
+#
+# TODO - REPL:
+# - proper way (as proposed by Zahary):
+#   - parse the input code and put everything in global scope except for
+#     statements with side effects only - those go in afterCodeReload blocks
+# - my very hacky idea: just append to a closure iterator the new statements
+#   followed by a yield statement. So far I can think of 2 problems:
+#   - import and some other code cannot be written inside of a proc -
+#     has to be parsed and extracted in the outer scope
+#   - when new variables are created they are actually locals to the closure
+#     so the struct for the closure state grows in memory, but it has already
+#     been allocated when the closure was created with the previous smaller size.
+#     That would lead to working with memory outside of the initially allocated
+#     block. Perhaps something can be done about this - some way of re-allocating
+#     the state and transferring the old...
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+when not defined(js) and (defined(hotcodereloading) or
+                          defined(createNimHcr) or
+                          defined(testNimHcr)):
+  const
+    dllExt = when defined(windows): "dll"
+             elif defined(macosx): "dylib"
+             else: "so"
+  type
+    HcrProcGetter* = proc (libHandle: pointer, procName: cstring): pointer {.nimcall.}
+    HcrGcMarkerProc = proc () {.nimcall, raises: [].}
+    HcrModuleInitializer* = proc () {.nimcall.}
+
+when defined(createNimHcr):
+  when system.appType != "lib":
+    {.error: "This file has to be compiled as a library!".}
+
+  import std/[os, tables, sets, times, strutils, reservedmem, dynlib]
+
+  template trace(args: varargs[untyped]) =
+    when defined(testNimHcr) or defined(traceHcr):
+      echo args
+
+  proc sanitize(arg: Time): string =
+    when defined(testNimHcr): return "<time>"
+    else: return $arg
+
+  proc sanitize(arg: string|cstring): string =
+    when defined(testNimHcr): return ($arg).splitFile.name.splitFile.name
+    else: return $arg
+
+  {.pragma: nimhcr, compilerproc, exportc, dynlib.}
+
+  # XXX these types are CPU specific and need ARM etc support
+  type
+    ShortJumpInstruction {.packed.} = object
+      opcode: byte
+      offset: int32
+
+    LongJumpInstruction {.packed.} = object
+      opcode1: byte
+      opcode2: byte
+      offset: int32
+      absoluteAddr: pointer
+
+  proc writeJump(jumpTableEntry: ptr LongJumpInstruction, targetFn: pointer) =
+    let
+      jumpFrom = jumpTableEntry.shift(sizeof(ShortJumpInstruction))
+      jumpDistance = distance(jumpFrom, targetFn)
+
+    if abs(jumpDistance) < 0x7fff0000:
+      let shortJump = cast[ptr ShortJumpInstruction](jumpTableEntry)
+      shortJump.opcode = 0xE9 # relative jump
+      shortJump.offset = int32(jumpDistance)
+    else:
+      jumpTableEntry.opcode1 = 0xff # indirect absolute jump
+      jumpTableEntry.opcode2 = 0x25
+      when hostCPU == "i386":
+        # on x86 we write the absolute address of the following pointer
+        jumpTableEntry.offset = cast[int32](addr jumpTableEntry.absoluteAddr)
+      else:
+        # on x64, we use a relative address for the same location
+        jumpTableEntry.offset = 0
+      jumpTableEntry.absoluteAddr = targetFn
+
+  if hostCPU == "arm":
+    const jumpSize = 8
+  elif hostCPU == "arm64":
+    const jumpSize = 16
+
+  const defaultJumpTableSize = case hostCPU
+                               of "i386": 50
+                               of "amd64": 500
+                               else: 50
+
+  let jumpTableSizeStr = getEnv("HOT_CODE_RELOADING_JUMP_TABLE_SIZE")
+  let jumpTableSize = if jumpTableSizeStr.len > 0: parseInt(jumpTableSizeStr)
+                      else: defaultJumpTableSize
+
+  # TODO: perhaps keep track of free slots due to removed procs using a free list
+  var jumpTable = ReservedMemSeq[LongJumpInstruction].init(
+    memStart = cast[pointer](0x10000000),
+    maxLen = jumpTableSize * 1024 * 1024 div sizeof(LongJumpInstruction),
+    accessFlags = memExecReadWrite)
+
+  type
+    ProcSym = object
+      jump: ptr LongJumpInstruction
+      gen: int
+
+    GlobalVarSym = object
+      p: pointer
+      markerProc: HcrGcMarkerProc
+      gen: int
+
+    ModuleDesc = object
+      procs: Table[string, ProcSym]
+      globals: Table[string, GlobalVarSym]
+      imports: seq[string]
+      handle: LibHandle
+      hash: string
+      gen: int
+      lastModification: Time
+      handlers: seq[tuple[isBefore: bool, cb: proc () {.nimcall.}]]
+
+  proc newModuleDesc(): ModuleDesc =
+    result.procs = initTable[string, ProcSym]()
+    result.globals = initTable[string, GlobalVarSym]()
+    result.handle = nil
+    result.gen = -1
+    result.lastModification = low(Time)
+
+  # the global state necessary for traversing and reloading the module import tree
+  var modules = initTable[string, ModuleDesc]()
+  var root: string
+  var system: string
+  var mainDatInit: HcrModuleInitializer
+  var generation = 0
+
+  # necessary for queries such as "has module X changed" - contains all but the main module
+  var hashToModuleMap = initTable[string, string]()
+
+  # necessary for registering handlers and keeping them up-to-date
+  var currentModule: string
+
+  # supplied from the main module - used by others to initialize pointers to this runtime
+  var hcrDynlibHandle: pointer
+  var getProcAddr: HcrProcGetter
+
+  proc hcrRegisterProc*(module: cstring, name: cstring, fn: pointer): pointer {.nimhcr.} =
+    trace "  register proc: ", module.sanitize, " ", name
+    # Please note: We must allocate a local copy of the strings, because the supplied
+    # `cstring` will reside in the data segment of a DLL that will be later unloaded.
+    let name = $name
+    let module = $module
+
+    var jumpTableEntryAddr: ptr LongJumpInstruction
+
+    modules[module].procs.withValue(name, p):
+      trace "    update proc: ", name
+      jumpTableEntryAddr = p.jump
+      p.gen = generation
+    do:
+      let len = jumpTable.len
+      jumpTable.setLen(len + 1)
+      jumpTableEntryAddr = addr jumpTable[len]
+      modules[module].procs[name] = ProcSym(jump: jumpTableEntryAddr, gen: generation)
+
+    writeJump jumpTableEntryAddr, fn
+    return jumpTableEntryAddr
+
+  proc hcrGetProc*(module: cstring, name: cstring): pointer {.nimhcr.} =
+    trace "  get proc: ", module.sanitize, " ", name
+    return modules[$module].procs.getOrDefault($name, ProcSym()).jump
+
+  proc hcrRegisterGlobal*(module: cstring,
+                          name: cstring,
+                          size: Natural,
+                          gcMarker: HcrGcMarkerProc,
+                          outPtr: ptr pointer): bool {.nimhcr.} =
+    trace "  register global: ", module.sanitize, " ", name
+    # Please note: We must allocate local copies of the strings, because the supplied
+    # `cstring` will reside in the data segment of a DLL that will be later unloaded.
+    # Also using a ptr pointer instead of a var pointer (an output parameter)
+    # because for the C++ backend var parameters use references and in this use case
+    # it is not possible to cast an int* (for example) to a void* and then pass it
+    # to void*& since the casting yields an rvalue and references bind only to lvalues.
+    let name = $name
+    let module = $module
+
+    modules[module].globals.withValue(name, global):
+      trace "    update global: ", name
+      outPtr[] = global.p
+      global.gen = generation
+      global.markerProc = gcMarker
+      return false
+    do:
+      outPtr[] = alloc0(size)
+      modules[module].globals[name] = GlobalVarSym(p: outPtr[],
+                                                   gen: generation,
+                                                   markerProc: gcMarker)
+      return true
+
+  proc hcrGetGlobal*(module: cstring, name: cstring): pointer {.nimhcr.} =
+    trace "  get global: ", module.sanitize, " ", name
+    return modules[$module].globals[$name].p
+
+  proc getListOfModules(cstringArray: ptr pointer): seq[string] =
+    var curr = cast[ptr cstring](cstringArray)
+    while len(curr[]) > 0:
+      result.add($curr[])
+      curr = cast[ptr cstring](cast[int64](curr) + sizeof(ptr cstring))
+
+  template cleanup(collection, body) =
+    var toDelete: seq[string]
+    for name, data in collection.pairs:
+      if data.gen < generation:
+        toDelete.add(name)
+        trace "HCR Cleaning ", astToStr(collection), " :: ", name, " ", data.gen
+    for name {.inject.} in toDelete:
+      body
+
+  proc cleanupGlobal(module: string, name: string) =
+    var g: GlobalVarSym
+    if modules[module].globals.take(name, g):
+      dealloc g.p
+
+  proc cleanupSymbols(module: string) =
+    cleanup modules[module].globals:
+      cleanupGlobal(module, name)
+
+    cleanup modules[module].procs:
+      modules[module].procs.del(name)
+
+  proc unloadDll(name: string) =
+    if modules[name].handle != nil:
+      unloadLib(modules[name].handle)
+
+  proc loadDll(name: cstring) {.nimhcr.} =
+    let name = $name
+    trace "HCR LOADING: ", name.sanitize
+    if modules.contains(name):
+      unloadDll(name)
+    else:
+      modules[name] = newModuleDesc()
+
+    let copiedName = name & ".copy." & dllExt
+    copyFileWithPermissions(name, copiedName)
+
+    let lib = loadLib(copiedName)
+    assert lib != nil
+    modules[name].handle = lib
+    modules[name].gen = generation
+    modules[name].lastModification = getLastModificationTime(name)
+
+    # update the list of imports by the module
+    let getImportsProc = cast[proc (): ptr pointer {.nimcall.}](
+      checkedSymAddr(lib, "HcrGetImportedModules"))
+    modules[name].imports = getListOfModules(getImportsProc())
+    # get the hash of the module
+    let getHashProc = cast[proc (): cstring {.nimcall.}](
+      checkedSymAddr(lib, "HcrGetSigHash"))
+    modules[name].hash = $getHashProc()
+    hashToModuleMap[modules[name].hash] = name
+
+    # Remove handlers for this module if reloading - they will be re-registered.
+    # In order for them to be re-registered we need to de-register all globals
+    # that trigger the registering of handlers through calls to hcrAddEventHandler
+    modules[name].handlers.setLen(0)
+
+  proc initHcrData(name: cstring) {.nimhcr.} =
+    trace "HCR Hcr init: ", name.sanitize
+    cast[proc (h: pointer, gpa: HcrProcGetter) {.nimcall.}](
+      checkedSymAddr(modules[$name].handle, "HcrInit000"))(hcrDynlibHandle, getProcAddr)
+
+  proc initTypeInfoGlobals(name: cstring) {.nimhcr.} =
+    trace "HCR TypeInfo globals init: ", name.sanitize
+    cast[HcrModuleInitializer](checkedSymAddr(modules[$name].handle, "HcrCreateTypeInfos"))()
+
+  proc initPointerData(name: cstring) {.nimhcr.} =
+    trace "HCR Dat init: ", name.sanitize
+    cast[HcrModuleInitializer](checkedSymAddr(modules[$name].handle, "DatInit000"))()
+
+  proc initGlobalScope(name: cstring) {.nimhcr.} =
+    trace "HCR Init000: ", name.sanitize
+    # set the currently inited module - necessary for registering the before/after HCR handlers
+    currentModule = $name
+    cast[HcrModuleInitializer](checkedSymAddr(modules[$name].handle, "Init000"))()
+
+  var modulesToInit: seq[string] = @[]
+  var allModulesOrderedByDFS: seq[string] = @[]
+
+  proc recursiveDiscovery(dlls: seq[string]) =
+    for curr in dlls:
+      if modules.contains(curr):
+        # skip updating modules that have already been updated to the latest generation
+        if modules[curr].gen >= generation:
+          trace "HCR SKIP: ", curr.sanitize, " gen is already: ", modules[curr].gen
+          continue
+        # skip updating an unmodified module but continue traversing its dependencies
+        if modules[curr].lastModification >= getLastModificationTime(curr):
+          trace "HCR SKIP (not modified): ", curr.sanitize, " ", modules[curr].lastModification.sanitize
+          # update generation so module doesn't get collected
+          modules[curr].gen = generation
+          # recurse to imported modules - they might be changed
+          recursiveDiscovery(modules[curr].imports)
+          allModulesOrderedByDFS.add(curr)
+          continue
+      loadDll(curr.cstring)
+      # first load all dependencies of the current module and init it after that
+      recursiveDiscovery(modules[curr].imports)
+
+      allModulesOrderedByDFS.add(curr)
+      modulesToInit.add(curr)
+
+  proc initModules() =
+    # first init the pointers to hcr functions and also do the registering of typeinfo globals
+    for curr in modulesToInit:
+      initHcrData(curr.cstring)
+      initTypeInfoGlobals(curr.cstring)
+    # for now system always gets fully inited before any other module (including when reloading)
+    initPointerData(system.cstring)
+    initGlobalScope(system.cstring)
+    # proceed with the DatInit calls - for all modules - including the main one!
+    for curr in allModulesOrderedByDFS:
+      if curr != system:
+        initPointerData(curr.cstring)
+    mainDatInit()
+    # execute top-level code (in global scope)
+    for curr in modulesToInit:
+      if curr != system:
+        initGlobalScope(curr.cstring)
+    # cleanup old symbols which are gone now
+    for curr in modulesToInit:
+      cleanupSymbols(curr)
+
+  proc hcrInit*(moduleList: ptr pointer, main, sys: cstring,
+                datInit: HcrModuleInitializer, handle: pointer, gpa: HcrProcGetter) {.nimhcr.} =
+    trace "HCR INITING: ", main.sanitize, " gen: ", generation
+    # initialize globals
+    root = $main
+    system = $sys
+    mainDatInit = datInit
+    hcrDynlibHandle = handle
+    getProcAddr = gpa
+    # the root is already added and we need it because symbols from it will also be registered in the HCR system
+    modules[root].imports = getListOfModules(moduleList)
+    modules[root].gen = high(int) # something huge so it doesn't get collected
+    # recursively initialize all modules
+    recursiveDiscovery(modules[root].imports)
+    initModules()
+    # the next module to be inited will be the root
+    currentModule = root
+
+  proc hcrHasModuleChanged*(moduleHash: string): bool {.nimhcr.} =
+    let module = hashToModuleMap[moduleHash]
+    return modules[module].lastModification < getLastModificationTime(module)
+
+  proc hcrReloadNeeded*(): bool {.nimhcr.} =
+    for hash, _ in hashToModuleMap:
+      if hcrHasModuleChanged(hash):
+        return true
+    return false
+
+  proc hcrPerformCodeReload*() {.nimhcr.} =
+    if not hcrReloadNeeded():
+      trace "HCR - no changes"
+      return
+
+    # We disable the GC during the reload, because the reloading procedures
+    # will replace type info objects and GC marker procs. This seems to create
+    # problems when the GC is executed while the reload is underway.
+    # Future versions of NIMHCR won't use the GC, because all globals and the
+    # metadata needed to access them will be placed in shared memory, so they
+    # can be manipulated from external programs without reloading.
+    when declared(GC_disable):
+      GC_disable()
+      defer: GC_enable()
+    elif declared(GC_disableOrc):
+      GC_disableOrc()
+      defer: GC_enableOrc()
+
+    inc(generation)
+    trace "HCR RELOADING: ", generation
+
+    var traversedHandlerModules = initHashSet[string]()
+
+    proc recursiveExecuteHandlers(isBefore: bool, module: string) =
+      # do not process an already traversed module
+      if traversedHandlerModules.containsOrIncl(module): return
+      traversedHandlerModules.incl module
+      # first recurse to do a DFS traversal
+      for curr in modules[module].imports:
+        recursiveExecuteHandlers(isBefore, curr)
+      # and then execute the handlers - from leaf modules all the way up to the root module
+      for curr in modules[module].handlers:
+        if curr.isBefore == isBefore:
+         curr.cb()
+
+    # first execute the before reload handlers
+    traversedHandlerModules.clear()
+    recursiveExecuteHandlers(true, root)
+
+    # do the reloading
+    modulesToInit = @[]
+    allModulesOrderedByDFS = @[]
+    recursiveDiscovery(modules[root].imports)
+    initModules()
+
+    # execute the after reload handlers
+    traversedHandlerModules.clear()
+    recursiveExecuteHandlers(false, root)
+
+    # collecting no longer referenced modules - based on their generation
+    cleanup modules:
+      cleanupSymbols(name)
+      unloadDll(name)
+      hashToModuleMap.del(modules[name].hash)
+      modules.del(name)
+
+  proc hcrAddEventHandler*(isBefore: bool, cb: proc () {.nimcall.}) {.nimhcr.} =
+    modules[currentModule].handlers.add(
+      (isBefore: isBefore, cb: cb))
+
+  proc hcrAddModule*(module: cstring) {.nimhcr.} =
+    if not modules.contains($module):
+      modules[$module] = newModuleDesc()
+
+  proc hcrGeneration*(): int {.nimhcr.} =
+    generation
+
+  proc hcrMarkGlobals*() {.compilerproc, exportc, dynlib, nimcall, gcsafe.} =
+    # This is gcsafe, because it will be registered
+    # only in the GC of the main thread.
+    {.gcsafe.}:
+      for _, module in modules:
+        for _, global in module.globals:
+          if global.markerProc != nil:
+            global.markerProc()
+
+elif defined(hotcodereloading) or defined(testNimHcr):
+  when not defined(js):
+    const
+      nimhcrLibname = when defined(windows): "nimhcr." & dllExt
+                      elif defined(macosx): "libnimhcr." & dllExt
+                      else: "libnimhcr." & dllExt
+
+    {.pragma: nimhcr, compilerproc, importc, dynlib: nimhcrLibname.}
+
+    proc hcrRegisterProc*(module: cstring, name: cstring, fn: pointer): pointer {.nimhcr.}
+
+    proc hcrGetProc*(module: cstring, name: cstring): pointer {.nimhcr.}
+
+    proc hcrRegisterGlobal*(module: cstring, name: cstring, size: Natural,
+                            gcMarker: HcrGcMarkerProc, outPtr: ptr pointer): bool {.nimhcr.}
+    proc hcrGetGlobal*(module: cstring, name: cstring): pointer {.nimhcr.}
+
+    proc hcrInit*(moduleList: ptr pointer,
+                  main, sys: cstring,
+                  datInit: HcrModuleInitializer,
+                  handle: pointer,
+                  gpa: HcrProcGetter) {.nimhcr.}
+
+    proc hcrAddModule*(module: cstring) {.nimhcr.}
+
+    proc hcrHasModuleChanged*(moduleHash: string): bool {.nimhcr.}
+
+    proc hcrReloadNeeded*(): bool {.nimhcr.}
+
+    proc hcrPerformCodeReload*() {.nimhcr.}
+
+    proc hcrAddEventHandler*(isBefore: bool, cb: proc () {.nimcall.}) {.nimhcr.}
+
+    proc hcrMarkGlobals*() {.raises: [], nimhcr, nimcall, gcsafe.}
+
+    when declared(nimRegisterGlobalMarker):
+      nimRegisterGlobalMarker(cast[GlobalMarkerProc](hcrMarkGlobals))
+
+  else:
+    proc hcrHasModuleChanged*(moduleHash: string): bool =
+      # TODO
+      false
+
+    proc hcrAddEventHandler*(isBefore: bool, cb: proc () {.nimcall.}) =
+      # TODO
+      discard
+
diff --git a/lib/nimhcr.nim.cfg b/lib/nimhcr.nim.cfg
new file mode 100644
index 000000000..282bec27c
--- /dev/null
+++ b/lib/nimhcr.nim.cfg
@@ -0,0 +1,5 @@
+--app:lib
+--threads:on
+-d:useNimRtl
+-d:createNimHcr
+
diff --git a/lib/nimrtl.nim b/lib/nimrtl.nim
index 4e4cf7e0e..a2fb6ce60 100644
--- a/lib/nimrtl.nim
+++ b/lib/nimrtl.nim
@@ -1,3 +1,7 @@
+discard """
+batchable: false
+"""
+
 #
 #
 #            Nim's Runtime Library
@@ -8,7 +12,7 @@
 #
 
 ## Main file to generate a DLL from the standard library.
-## The default Nimrtl does not only contain the ``system`` module, but these
+## The default Nimrtl does not only contain the `system` module, but these
 ## too:
 ##
 ## * parseutils
@@ -22,6 +26,7 @@
 ## * unicode
 ## * pegs
 ## * ropes
+## * cstrutils
 ##
 
 when system.appType != "lib":
@@ -31,6 +36,5 @@ when not defined(createNimRtl):
   {.error: "This file has to be compiled with '-d:createNimRtl'".}
 
 import
-  parseutils, strutils, parseopt, parsecfg, strtabs, unicode, pegs, ropes,
-  os, osproc, times
-
+  std/[parseutils, strutils, parseopt, parsecfg, strtabs, unicode, pegs, ropes,
+  os, osproc, times, cstrutils]
diff --git a/lib/nimrtl.nim.cfg b/lib/nimrtl.nim.cfg
index b60de183a..a5cec596f 100644
--- a/lib/nimrtl.nim.cfg
+++ b/lib/nimrtl.nim.cfg
@@ -1,5 +1,6 @@
 # The RTL.dll needs to be compiled with these options!
 
 --app:lib
+--threads:on
 --define:createNimRtl
 
diff --git a/lib/packages/docutils/dochelpers.nim b/lib/packages/docutils/dochelpers.nim
new file mode 100644
index 000000000..0a41d85b5
--- /dev/null
+++ b/lib/packages/docutils/dochelpers.nim
@@ -0,0 +1,298 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2021 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Integration helpers between ``docgen.nim`` and ``rst.nim``.
+##
+## Function `toLangSymbol(linkText)`_ produces a signature `docLink` of
+## `type LangSymbol`_ in ``rst.nim``, while `match(generated, docLink)`_
+## matches it with `generated`, produced from `PNode` by ``docgen.rst``.
+
+import rstast
+import std/strutils
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
+
+
+type
+  LangSymbol* = object       ## symbol signature in Nim
+    symKind*: string           ## "proc", "const", "type", etc
+    symTypeKind*: string       ## ""|enum|object|tuple -
+                               ## valid only when `symKind == "type"`
+    name*: string              ## plain symbol name without any parameters
+    generics*: string          ## generic parameters (without brackets)
+    isGroup*: bool             ## is LangSymbol a group with overloads?
+    # the following fields are valid iff `isGroup` == false
+    # (always false when parsed by `toLangSymbol` because link like foo_
+    # can point to just a single symbol foo, e.g. proc).
+    parametersProvided*: bool  ## to disambiguate `proc f`_ and `proc f()`_
+    parameters*: seq[tuple[name: string, `type`: string]]
+                               ## name-type seq, e.g. for proc
+    outType*: string           ## result type, e.g. for proc
+
+proc `$`*(s: LangSymbol): string =  # for debug
+  ("(symkind=$1, symTypeKind=$2, name=$3, generics=$4, isGroup=$5, " &
+   "parametersProvided=$6, parameters=$7, outType=$8)") % [
+      s.symKind, s.symTypeKind , s.name, s.generics, $s.isGroup,
+      $s.parametersProvided, $s.parameters, s.outType]
+
+func nimIdentBackticksNormalize*(s: string): string =
+  ## Normalizes the string `s` as a Nim identifier.
+  ##
+  ## Unlike `nimIdentNormalize` removes spaces and backticks.
+  ##
+  ## .. Warning:: No checking (e.g. that identifiers cannot start from
+  ##    digits or '_', or that number of backticks is even) is performed.
+  runnableExamples:
+    doAssert nimIdentBackticksNormalize("Foo_bar") == "Foobar"
+    doAssert nimIdentBackticksNormalize("FoO BAr") == "Foobar"
+    doAssert nimIdentBackticksNormalize("`Foo BAR`") == "Foobar"
+    doAssert nimIdentBackticksNormalize("` Foo BAR `") == "Foobar"
+    # not a valid identifier:
+    doAssert nimIdentBackticksNormalize("`_x_y`") == "_xy"
+  result = newString(s.len)
+  var firstChar = true
+  var j = 0
+  for i in 0..len(s) - 1:
+    if s[i] in {'A'..'Z'}:
+      if not firstChar:  # to lowercase
+        result[j] = chr(ord(s[i]) + (ord('a') - ord('A')))
+      else:
+        result[j] = s[i]
+        firstChar = false
+      inc j
+    elif s[i] notin {'_', ' ', '`'}:
+      result[j] = s[i]
+      inc j
+      firstChar = false
+    elif s[i] == '_' and firstChar:
+      result[j] = '_'
+      inc j
+      firstChar = false
+    else: discard  # just omit '`' or ' '
+  if j != s.len: setLen(result, j)
+
+proc langSymbolGroup*(kind: string, name: string): LangSymbol =
+  if kind notin ["proc", "func", "macro", "method", "iterator",
+                 "template", "converter"]:
+    raise newException(ValueError, "unknown symbol kind $1" % [kind])
+  result = LangSymbol(symKind: kind, name: name, isGroup: true)
+
+proc toLangSymbol*(linkText: PRstNode): LangSymbol =
+  ## Parses `linkText` into a more structured form using a state machine.
+  ##
+  ## This proc is designed to allow link syntax with operators even
+  ## without escaped backticks inside:
+  ##   
+  ##     `proc *`_
+  ##     `proc []`_
+  ##
+  ## This proc should be kept in sync with the `renderTypes` proc from
+  ## ``compiler/typesrenderer.nim``.
+  template fail(msg: string) =
+    raise newException(ValueError, msg)
+  if linkText.kind notin {rnRstRef, rnInner}:
+    fail("toLangSymbol: wrong input kind " & $linkText.kind)
+
+  const NimDefs = ["proc", "func", "macro", "method", "iterator",
+                   "template", "converter", "const", "type", "var",
+                   "enum", "object", "tuple", "module"]
+  template resolveSymKind(x: string) =
+    if x in ["enum", "object", "tuple"]:
+      result.symKind = "type"
+      result.symTypeKind = x
+    else:
+      result.symKind = x
+  type
+    State = enum
+      inBeginning
+      afterSymKind
+      beforeSymbolName  # auxiliary state to catch situations like `proc []`_ after space
+      atSymbolName
+      afterSymbolName
+      genericsPar
+      parameterName
+      parameterType
+      outType
+  var state = inBeginning
+  var curIdent = ""
+  template flushIdent() =
+    if curIdent != "":
+      case state
+      of inBeginning:  fail("incorrect state inBeginning")
+      of afterSymKind:  resolveSymKind curIdent
+      of beforeSymbolName:  fail("incorrect state beforeSymbolName")
+      of atSymbolName: result.name = curIdent.nimIdentBackticksNormalize
+      of afterSymbolName: fail("incorrect state afterSymbolName")
+      of genericsPar: result.generics = curIdent
+      of parameterName: result.parameters.add (curIdent, "")
+      of parameterType:
+        for a in countdown(result.parameters.len - 1, 0):
+          if result.parameters[a].`type` == "":
+            result.parameters[a].`type` = curIdent
+      of outType: result.outType = curIdent
+      curIdent = ""
+  var parens = 0
+  let L = linkText.sons.len
+  template s(i: int): string = linkText.sons[i].text
+  var i = 0
+  template nextState =
+    case s(i)
+    of " ":
+      if state == afterSymKind:
+        flushIdent
+        state = beforeSymbolName
+    of "`":
+      curIdent.add "`"
+      inc i
+      while i < L:  # add contents between ` ` as a whole
+        curIdent.add s(i)
+        if s(i) == "`":
+          break
+        inc i
+      curIdent = curIdent.nimIdentBackticksNormalize
+      if state in {inBeginning, afterSymKind, beforeSymbolName}:
+        state = atSymbolName
+        flushIdent
+        state = afterSymbolName
+    of "[":
+      if state notin {inBeginning, afterSymKind, beforeSymbolName}:
+        inc parens
+      if state in {inBeginning, afterSymKind, beforeSymbolName}:
+        state = atSymbolName
+        curIdent.add s(i)
+      elif state in {atSymbolName, afterSymbolName} and parens == 1:
+        flushIdent
+        state = genericsPar
+        curIdent.add s(i)
+      else: curIdent.add s(i)
+    of "]":
+      if state notin {inBeginning, afterSymKind, beforeSymbolName, atSymbolName}:
+        dec parens
+      if state == genericsPar and parens == 0:
+        curIdent.add s(i)
+        flushIdent
+      else: curIdent.add s(i)
+    of "(":
+      inc parens
+      if state in {inBeginning, afterSymKind, beforeSymbolName}:
+        result.parametersProvided = true
+        state = atSymbolName
+        flushIdent
+        state = parameterName
+      elif state in {atSymbolName, afterSymbolName, genericsPar} and parens == 1:
+        result.parametersProvided = true
+        flushIdent
+        state = parameterName
+      else: curIdent.add s(i)
+    of ")":
+      dec parens
+      if state in {parameterName, parameterType} and parens == 0:
+        flushIdent
+        state = outType
+      else: curIdent.add s(i)
+    of "{":  # remove pragmas
+      while i < L:
+        if s(i) == "}":
+          break
+        inc i
+    of ",", ";":
+      if state in {parameterName, parameterType} and parens == 1:
+        flushIdent
+        state = parameterName
+      else: curIdent.add s(i)
+    of "*":  # skip export symbol
+      if state == atSymbolName:
+        flushIdent
+        state = afterSymbolName
+      elif state == afterSymbolName:
+        discard
+      else: curIdent.add "*"
+    of ":":
+      if state == outType: discard
+      elif state == parameterName:
+        flushIdent
+        state = parameterType
+      else: curIdent.add ":"
+    else:
+      let isPostfixSymKind = i > 0 and i == L - 1 and
+          result.symKind == "" and s(i) in NimDefs
+      if isPostfixSymKind:  # for links like `foo proc`_
+        resolveSymKind s(i)
+      else:
+        case state
+        of inBeginning:
+          if s(i) in NimDefs:
+            state = afterSymKind
+          else:
+            state = atSymbolName
+          curIdent.add s(i)
+        of afterSymKind, beforeSymbolName:
+          state = atSymbolName
+          curIdent.add s(i)
+        of parameterType:
+          case s(i)
+          of "ref": curIdent.add "ref."
+          of "ptr": curIdent.add "ptr."
+          of "var": discard
+          else: curIdent.add s(i).nimIdentBackticksNormalize
+        of atSymbolName:
+          curIdent.add s(i)
+        else:
+          curIdent.add s(i).nimIdentBackticksNormalize
+  while i < L:
+    nextState
+    inc i
+  if state == afterSymKind:  # treat `type`_ as link to symbol `type`
+    state = atSymbolName
+  flushIdent
+  result.isGroup = false
+
+proc match*(generated: LangSymbol, docLink: LangSymbol): bool =
+  ## Returns true if `generated` can be a target for `docLink`.
+  ## If `generated` is an overload group then only `symKind` and `name`
+  ## are compared for success.
+  result = true
+  if docLink.symKind != "":
+    if generated.symKind == "proc":
+      result = docLink.symKind in ["proc", "func"]
+    else:
+      result = generated.symKind == docLink.symKind
+      if result and docLink.symKind == "type" and docLink.symTypeKind != "":
+        result = generated.symTypeKind == docLink.symTypeKind
+    if not result: return
+  result = generated.name == docLink.name
+  if not result: return
+  if generated.isGroup:
+    # if `()` were added then it's not a reference to the whole group:
+    return not docLink.parametersProvided
+  if docLink.generics != "":
+    result = generated.generics == docLink.generics
+    if not result: return
+  if docLink.outType != "":
+    result = generated.outType == docLink.outType
+    if not result: return
+  if docLink.parametersProvided:
+    result = generated.parameters.len == docLink.parameters.len
+    if not result: return
+    var onlyType = false
+    for i in 0 ..< generated.parameters.len:
+      let g = generated.parameters[i]
+      let d = docLink.parameters[i]
+      if i == 0:
+        if g.`type` == d.name:
+          onlyType = true  # only types, not names, are provided in `docLink`
+      if onlyType:
+        result = g.`type` == d.name
+      else:
+        if d.`type` != "":
+          result = g.`type` == d.`type`
+          if not result: return
+        result = g.name == d.name
+      if not result: return
diff --git a/lib/packages/docutils/docutils.nimble b/lib/packages/docutils/docutils.nimble
deleted file mode 100644
index e32cc6bdb..000000000
--- a/lib/packages/docutils/docutils.nimble
+++ /dev/null
@@ -1,5 +0,0 @@
-name          = "docutils"
-version       = "0.10.0"
-author        = "Andreas Rumpf"
-description   = "Nim's reStructuredText processor."
-license       = "MIT"
diff --git a/lib/packages/docutils/docutils.nimble.old b/lib/packages/docutils/docutils.nimble.old
new file mode 100644
index 000000000..f97c3bdde
--- /dev/null
+++ b/lib/packages/docutils/docutils.nimble.old
@@ -0,0 +1,7 @@
+# xxx disabled this as this isn't really a nimble package and it affects logic
+# used to compute canonical imports, refs https://github.com/nim-lang/Nim/pull/16999#issuecomment-805442914
+
+version       = "0.10.0"
+author        = "Andreas Rumpf"
+description   = "Nim's reStructuredText processor."
+license       = "MIT"
diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim
index fbd2d7eca..f8376f46c 100644
--- a/lib/packages/docutils/highlite.nim
+++ b/lib/packages/docutils/highlite.nim
@@ -10,12 +10,64 @@
 ## Source highlighter for programming or markup languages.
 ## Currently only few languages are supported, other languages may be added.
 ## The interface supports one language nested in another.
+##
+## You can use this to build your own syntax highlighting, check this example:
+##
+##   ```Nim
+##   let code = """for x in $int.high: echo x.ord mod 2 == 0"""
+##   var toknizr: GeneralTokenizer
+##   initGeneralTokenizer(toknizr, code)
+##   while true:
+##     getNextToken(toknizr, langNim)
+##     case toknizr.kind
+##     of gtEof: break  # End Of File (or string)
+##     of gtWhitespace:
+##       echo gtWhitespace # Maybe you want "visible" whitespaces?.
+##       echo substr(code, toknizr.start, toknizr.length + toknizr.start - 1)
+##     of gtOperator:
+##       echo gtOperator # Maybe you want Operators to use a specific color?.
+##       echo substr(code, toknizr.start, toknizr.length + toknizr.start - 1)
+##     # of gtSomeSymbol: syntaxHighlight("Comic Sans", "bold", "99px", "pink")
+##     else:
+##       echo toknizr.kind # All the kinds of tokens can be processed here.
+##       echo substr(code, toknizr.start, toknizr.length + toknizr.start - 1)
+##   ```
+##
+## The proc `getSourceLanguage` can get the language `enum` from a string:
+##   ```Nim
+##   for l in ["C", "c++", "jAvA", "Nim", "c#"]: echo getSourceLanguage(l)
+##   ```
+##
+## There is also a `Cmd` pseudo-language supported, which is a simple generic
+## shell/cmdline tokenizer (UNIX shell/Powershell/Windows Command):
+## no escaping, no programming language constructs besides variable definition
+## at the beginning of line. It supports these operators:
+##   ```Cmd
+##   &  &&  |  ||  (  )  ''  ""  ;  # for comments
+##   ```
+##
+## Instead of escaping always use quotes like here
+## `nimgrep --ext:'nim|nims' file.name`:cmd: shows how to input ``|``.
+## Any argument that contains ``.`` or ``/`` or ``\`` will be treated
+## as a file or directory.
+##
+## In addition to `Cmd` there is also `Console` language for
+## displaying interactive sessions.
+## Lines with a command should start with ``$``, other lines are considered
+## as program output.
 
 import
-  strutils
-from algorithm import binarySearch
+  std/strutils
+from std/algorithm import binarySearch
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
+
 
 type
+  SourceLanguage* = enum
+    langNone, langNim, langCpp, langCsharp, langC, langJava,
+    langYaml, langPython, langCmd, langConsole
   TokenClass* = enum
     gtEof, gtNone, gtWhitespace, gtDecNumber, gtBinNumber, gtHexNumber,
     gtOctNumber, gtFloatNumber, gtIdentifier, gtKeyword, gtStringLit,
@@ -23,28 +75,31 @@ type
     gtOperator, gtPunctuation, gtComment, gtLongComment, gtRegularExpression,
     gtTagStart, gtTagEnd, gtKey, gtValue, gtRawData, gtAssembler,
     gtPreprocessor, gtDirective, gtCommand, gtRule, gtHyperlink, gtLabel,
-    gtReference, gtOther
+    gtReference, gtPrompt, gtProgramOutput, gtProgram, gtOption, gtOther
   GeneralTokenizer* = object of RootObj
     kind*: TokenClass
     start*, length*: int
     buf: cstring
     pos: int
     state: TokenClass
-
-  SourceLanguage* = enum
-    langNone, langNim, langCpp, langCsharp, langC, langJava,
-    langYaml
+    lang: SourceLanguage
 
 const
   sourceLanguageToStr*: array[SourceLanguage, string] = ["none",
-    "Nim", "C++", "C#", "C", "Java", "Yaml"]
+    "Nim", "C++", "C#", "C", "Java", "Yaml", "Python", "Cmd", "Console"]
+  sourceLanguageToAlpha*: array[SourceLanguage, string] = ["none",
+    "Nim", "cpp", "csharp", "C", "Java", "Yaml", "Python", "Cmd", "Console"]
+    ## list of languages spelled with alpabetic characters
   tokenClassToStr*: array[TokenClass, string] = ["Eof", "None", "Whitespace",
     "DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber",
     "Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit",
     "EscapeSequence", "Operator", "Punctuation", "Comment", "LongComment",
     "RegularExpression", "TagStart", "TagEnd", "Key", "Value", "RawData",
     "Assembler", "Preprocessor", "Directive", "Command", "Rule", "Hyperlink",
-    "Label", "Reference", "Other"]
+    "Label", "Reference", "Prompt", "ProgramOutput",
+    # start from lower-case if there is a corresponding RST role (see rst.nim)
+    "program", "option",
+    "Other"]
 
   # The following list comes from doc/keywords.txt, make sure it is
   # synchronized with this array by running the module itself as a test case.
@@ -61,9 +116,11 @@ const
     "xor", "yield"]
 
 proc getSourceLanguage*(name: string): SourceLanguage =
-  for i in countup(succ(low(SourceLanguage)), high(SourceLanguage)):
+  for i in succ(low(SourceLanguage)) .. high(SourceLanguage):
     if cmpIgnoreStyle(name, sourceLanguageToStr[i]) == 0:
       return i
+    if cmpIgnoreStyle(name, sourceLanguageToAlpha[i]) == 0:
+      return i
   result = langNone
 
 proc initGeneralTokenizer*(g: var GeneralTokenizer, buf: cstring) =
@@ -72,9 +129,8 @@ proc initGeneralTokenizer*(g: var GeneralTokenizer, buf: cstring) =
   g.start = 0
   g.length = 0
   g.state = low(TokenClass)
-  var pos = 0                     # skip initial whitespace:
-  while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
-  g.pos = pos
+  g.lang = low(SourceLanguage)
+  g.pos = 0
 
 proc initGeneralTokenizer*(g: var GeneralTokenizer, buf: string) =
   initGeneralTokenizer(g, cstring(buf))
@@ -132,7 +188,10 @@ const
   OpChars  = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.',
               '|', '=', '%', '&', '$', '@', '~', ':'}
 
-proc nimNextToken(g: var GeneralTokenizer) =
+proc isKeyword(x: openArray[string], y: string): int =
+  binarySearch(x, y)
+
+proc nimNextToken(g: var GeneralTokenizer, keywords: openArray[string] = @[]) =
   const
     hexChars = {'0'..'9', 'A'..'F', 'a'..'f', '_'}
     octChars = {'0'..'7', '_'}
@@ -141,36 +200,38 @@ proc nimNextToken(g: var GeneralTokenizer) =
   var pos = g.pos
   g.start = g.pos
   if g.state == gtStringLit:
-    g.kind = gtStringLit
-    while true:
+    if g.buf[pos] == '\\':
+      g.kind = gtEscapeSequence
+      inc(pos)
       case g.buf[pos]
-      of '\\':
-        g.kind = gtEscapeSequence
+      of 'x', 'X':
         inc(pos)
+        if g.buf[pos] in hexChars: inc(pos)
+        if g.buf[pos] in hexChars: inc(pos)
+      of '0'..'9':
+        while g.buf[pos] in {'0'..'9'}: inc(pos)
+      of '\0':
+        g.state = gtNone
+      else: inc(pos)
+    else:
+      g.kind = gtStringLit
+      while true:
         case g.buf[pos]
-        of 'x', 'X':
+        of '\\':
+          break
+        of '\0', '\r', '\n':
+          g.state = gtNone
+          break
+        of '\"':
           inc(pos)
-          if g.buf[pos] in hexChars: inc(pos)
-          if g.buf[pos] in hexChars: inc(pos)
-        of '0'..'9':
-          while g.buf[pos] in {'0'..'9'}: inc(pos)
-        of '\0':
           g.state = gtNone
+          break
         else: inc(pos)
-        break
-      of '\0', '\x0D', '\x0A':
-        g.state = gtNone
-        break
-      of '\"':
-        inc(pos)
-        g.state = gtNone
-        break
-      else: inc(pos)
   else:
     case g.buf[pos]
-    of ' ', '\x09'..'\x0D':
+    of ' ', '\t'..'\r':
       g.kind = gtWhitespace
-      while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
+      while g.buf[pos] in {' ', '\t'..'\r'}: inc(pos)
     of '#':
       g.kind = gtComment
       inc(pos)
@@ -178,7 +239,7 @@ proc nimNextToken(g: var GeneralTokenizer) =
       if g.buf[pos] == '#':
         inc(pos)
         isDoc = true
-      if g.buf[pos] == '[':
+      if g.buf[pos] == '[' and g.lang == langNim:
         g.kind = gtLongComment
         var nesting = 0
         while true:
@@ -207,7 +268,7 @@ proc nimNextToken(g: var GeneralTokenizer) =
           else:
             inc pos
       else:
-        while g.buf[pos] notin {'\0', '\x0A', '\x0D'}: inc(pos)
+        while g.buf[pos] notin {'\0', '\n', '\r'}: inc(pos)
     of 'a'..'z', 'A'..'Z', '_', '\x80'..'\xFF':
       var id = ""
       while g.buf[pos] in SymChars + {'_'}:
@@ -231,12 +292,15 @@ proc nimNextToken(g: var GeneralTokenizer) =
         else:
           g.kind = gtRawData
           inc(pos)
-          while not (g.buf[pos] in {'\0', '\x0A', '\x0D'}):
+          while not (g.buf[pos] in {'\0', '\n', '\r'}):
             if g.buf[pos] == '"' and g.buf[pos+1] != '"': break
             inc(pos)
           if g.buf[pos] == '\"': inc(pos)
       else:
-        g.kind = nimGetKeyword(id)
+        if g.lang == langNim:
+          g.kind = nimGetKeyword(id)
+        elif isKeyword(keywords, id) >= 0:
+          g.kind = gtKeyword
     of '0':
       inc(pos)
       case g.buf[pos]
@@ -260,17 +324,18 @@ proc nimNextToken(g: var GeneralTokenizer) =
       pos = nimNumber(g, pos)
     of '\'':
       inc(pos)
-      g.kind = gtCharLit
-      while true:
-        case g.buf[pos]
-        of '\0', '\x0D', '\x0A':
-          break
-        of '\'':
-          inc(pos)
-          break
-        of '\\':
-          inc(pos, 2)
-        else: inc(pos)
+      if g.kind != gtPunctuation:
+        g.kind = gtCharLit
+        while true:
+          case g.buf[pos]
+          of '\0', '\r', '\n':
+            break
+          of '\'':
+            inc(pos)
+            break
+          of '\\':
+            inc(pos, 2)
+          else: inc(pos)
     of '\"':
       inc(pos)
       if (g.buf[pos] == '\"') and (g.buf[pos + 1] == '\"'):
@@ -291,7 +356,7 @@ proc nimNextToken(g: var GeneralTokenizer) =
         g.kind = gtStringLit
         while true:
           case g.buf[pos]
-          of '\0', '\x0D', '\x0A':
+          of '\0', '\r', '\n':
             break
           of '\"':
             inc(pos)
@@ -313,7 +378,7 @@ proc nimNextToken(g: var GeneralTokenizer) =
         inc(pos)
         g.kind = gtNone
   g.length = pos - g.pos
-  if g.kind != gtEof and g.length <= 0:
+  if g.kind != gtEof and g.state != gtNone and g.length <= 0:
     assert false, "nimNextToken: produced an empty token"
   g.pos = pos
 
@@ -365,12 +430,6 @@ proc generalStrLit(g: var GeneralTokenizer, position: int): int =
         inc(pos)
   result = pos
 
-proc isKeyword(x: openArray[string], y: string): int =
-  binarySearch(x, y)
-
-proc isKeywordIgnoreCase(x: openArray[string], y: string): int =
-  binarySearch(x, y, cmpIgnoreCase)
-
 type
   TokenizerFlag = enum
     hasPreprocessor, hasNestedComments
@@ -403,7 +462,7 @@ proc clikeNextToken(g: var GeneralTokenizer, keywords: openArray[string],
           g.state = gtNone
         else: inc(pos)
         break
-      of '\0', '\x0D', '\x0A':
+      of '\0', '\r', '\n':
         g.state = gtNone
         break
       of '\"':
@@ -413,14 +472,14 @@ proc clikeNextToken(g: var GeneralTokenizer, keywords: openArray[string],
       else: inc(pos)
   else:
     case g.buf[pos]
-    of ' ', '\x09'..'\x0D':
+    of ' ', '\t'..'\r':
       g.kind = gtWhitespace
-      while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
+      while g.buf[pos] in {' ', '\t'..'\r'}: inc(pos)
     of '/':
       inc(pos)
       if g.buf[pos] == '/':
         g.kind = gtComment
-        while not (g.buf[pos] in {'\0', '\x0A', '\x0D'}): inc(pos)
+        while not (g.buf[pos] in {'\0', '\n', '\r'}): inc(pos)
       elif g.buf[pos] == '*':
         g.kind = gtLongComment
         var nested = 0
@@ -440,6 +499,9 @@ proc clikeNextToken(g: var GeneralTokenizer, keywords: openArray[string],
           of '\0':
             break
           else: inc(pos)
+      else:
+        g.kind = gtOperator
+        while g.buf[pos] in OpChars: inc(pos)
     of '#':
       inc(pos)
       if hasPreprocessor in flags:
@@ -560,9 +622,9 @@ proc javaNextToken(g: var GeneralTokenizer) =
 
 proc yamlPlainStrLit(g: var GeneralTokenizer, pos: var int) =
   g.kind = gtStringLit
-  while g.buf[pos] notin {'\0', '\x09'..'\x0D', ',', ']', '}'}:
+  while g.buf[pos] notin {'\0', '\t'..'\r', ',', ']', '}'}:
     if g.buf[pos] == ':' and
-        g.buf[pos + 1] in {'\0', '\x09'..'\x0D', ' '}:
+        g.buf[pos + 1] in {'\0', '\t'..'\r', ' '}:
       break
     inc(pos)
 
@@ -575,14 +637,14 @@ proc yamlPossibleNumber(g: var GeneralTokenizer, pos: var int) =
     while g.buf[pos] in {'0'..'9'}: inc(pos)
   else: yamlPlainStrLit(g, pos)
   if g.kind == gtNone:
-    if g.buf[pos] in {'\0', '\x09'..'\x0D', ' ', ',', ']', '}'}:
+    if g.buf[pos] in {'\0', '\t'..'\r', ' ', ',', ']', '}'}:
       g.kind = gtDecNumber
     elif g.buf[pos] == '.':
       inc(pos)
       if g.buf[pos] notin {'0'..'9'}: yamlPlainStrLit(g, pos)
       else:
         while g.buf[pos] in {'0'..'9'}: inc(pos)
-        if g.buf[pos] in {'\0', '\x09'..'\x0D', ' ', ',', ']', '}'}:
+        if g.buf[pos] in {'\0', '\t'..'\r', ' ', ',', ']', '}'}:
           g.kind = gtFloatNumber
     if g.kind == gtNone:
       if g.buf[pos] in {'e', 'E'}:
@@ -591,13 +653,13 @@ proc yamlPossibleNumber(g: var GeneralTokenizer, pos: var int) =
         if g.buf[pos] notin {'0'..'9'}: yamlPlainStrLit(g, pos)
         else:
           while g.buf[pos] in {'0'..'9'}: inc(pos)
-          if g.buf[pos] in {'\0', '\x09'..'\x0D', ' ', ',', ']', '}'}:
+          if g.buf[pos] in {'\0', '\t'..'\r', ' ', ',', ']', '}'}:
             g.kind = gtFloatNumber
           else: yamlPlainStrLit(g, pos)
       else: yamlPlainStrLit(g, pos)
-  while g.buf[pos] notin {'\0', ',', ']', '}', '\x0A', '\x0D'}:
+  while g.buf[pos] notin {'\0', ',', ']', '}', '\n', '\r'}:
     inc(pos)
-    if g.buf[pos] notin {'\x09'..'\x0D', ' ', ',', ']', '}'}:
+    if g.buf[pos] notin {'\t'..'\r', ' ', ',', ']', '}'}:
       yamlPlainStrLit(g, pos)
       break
   # theoretically, we would need to parse indentation (like with block scalars)
@@ -622,19 +684,16 @@ proc yamlNextToken(g: var GeneralTokenizer) =
         of 'x':
           inc(pos)
           for i in 1..2:
-            {.unroll.}
             if g.buf[pos] in hexChars: inc(pos)
           break
         of 'u':
           inc(pos)
           for i in 1..4:
-            {.unroll.}
             if g.buf[pos] in hexChars: inc(pos)
           break
         of 'U':
           inc(pos)
           for i in 1..8:
-            {.unroll.}
             if g.buf[pos] in hexChars: inc(pos)
           break
         else: inc(pos)
@@ -669,13 +728,13 @@ proc yamlNextToken(g: var GeneralTokenizer) =
       while g.buf[pos] in {' ', '\t'}: inc(pos)
     of '#':
       g.kind = gtComment
-      while g.buf[pos] notin {'\0', '\x0A', '\x0D'}: inc(pos)
-    of '\x0A', '\x0D': discard
+      while g.buf[pos] notin {'\0', '\n', '\r'}: inc(pos)
+    of '\n', '\r': discard
     else:
       # illegal here. just don't parse a block scalar
       g.kind = gtNone
       g.state = gtOther
-    if g.buf[pos] in {'\x0A', '\x0D'} and g.state == gtCommand:
+    if g.buf[pos] in {'\n', '\r'} and g.state == gtCommand:
       g.state = gtLongStringLit
   elif g.state == gtLongStringLit:
     # beware, this is the only token where we actually have to parse
@@ -684,10 +743,10 @@ proc yamlNextToken(g: var GeneralTokenizer) =
     g.kind = gtLongStringLit
     # first, we have to find the parent indentation of the block scalar, so that
     # we know when to stop
-    assert g.buf[pos] in {'\x0A', '\x0D'}
+    assert g.buf[pos] in {'\n', '\r'}
     var lookbehind = pos - 1
     var headerStart = -1
-    while lookbehind >= 0 and g.buf[lookbehind] notin {'\x0A', '\x0D'}:
+    while lookbehind >= 0 and g.buf[lookbehind] notin {'\n', '\r'}:
       if headerStart == -1 and g.buf[lookbehind] in {'|', '>'}:
         headerStart = lookbehind
       dec(lookbehind)
@@ -698,12 +757,12 @@ proc yamlNextToken(g: var GeneralTokenizer) =
       # when the header is alone in a line, this line does not show the parent's
       # indentation, so we must go further. search the first previous line with
       # non-whitespace content.
-      while lookbehind >= 0 and g.buf[lookbehind] in {'\x0A', '\x0D'}:
+      while lookbehind >= 0 and g.buf[lookbehind] in {'\n', '\r'}:
         dec(lookbehind)
         while lookbehind >= 0 and
             g.buf[lookbehind] in {' ', '\t'}: dec(lookbehind)
       # now, find the beginning of the line...
-      while lookbehind >= 0 and g.buf[lookbehind] notin {'\x0A', '\x0D'}:
+      while lookbehind >= 0 and g.buf[lookbehind] notin {'\n', '\r'}:
         dec(lookbehind)
       # ... and its indentation
       indentation = 1
@@ -711,7 +770,7 @@ proc yamlNextToken(g: var GeneralTokenizer) =
     if lookbehind == -1: indentation = 0 # top level
     elif g.buf[lookbehind + 1] == '-' and g.buf[lookbehind + 2] == '-' and
         g.buf[lookbehind + 3] == '-' and
-        g.buf[lookbehind + 4] in {'\x09'..'\x0D', ' '}:
+        g.buf[lookbehind + 4] in {'\t'..'\r', ' '}:
       # this is a document start, therefore, we are at top level
       indentation = 0
     # because lookbehind was at newline char when calculating indentation, we're
@@ -719,7 +778,7 @@ proc yamlNextToken(g: var GeneralTokenizer) =
     let parentIndentation = indentation - 1
 
     # find first content
-    while g.buf[pos] in {' ', '\x0A', '\x0D'}:
+    while g.buf[pos] in {' ', '\n', '\r'}:
       if g.buf[pos] == ' ': inc(indentation)
       else: indentation = 0
       inc(pos)
@@ -736,12 +795,12 @@ proc yamlNextToken(g: var GeneralTokenizer) =
       if (indentation < minIndentation and g.buf[pos] == '#') or
           (indentation == 0 and g.buf[pos] == '.' and g.buf[pos + 1] == '.' and
           g.buf[pos + 2] == '.' and
-          g.buf[pos + 3] in {'\0', '\x09'..'\x0D', ' '}):
+          g.buf[pos + 3] in {'\0', '\t'..'\r', ' '}):
         # comment after end of block scalar, or end of document
         break
       minIndentation = min(indentation, minIndentation)
-      while g.buf[pos] notin {'\0', '\x0A', '\x0D'}: inc(pos)
-      while g.buf[pos] in {' ', '\x0A', '\x0D'}:
+      while g.buf[pos] notin {'\0', '\n', '\r'}: inc(pos)
+      while g.buf[pos] in {' ', '\n', '\r'}:
         if g.buf[pos] == ' ': inc(indentation)
         else: indentation = 0
         inc(pos)
@@ -750,30 +809,29 @@ proc yamlNextToken(g: var GeneralTokenizer) =
   elif g.state == gtOther:
     # gtOther means 'inside YAML document'
     case g.buf[pos]
-    of ' ', '\x09'..'\x0D':
+    of ' ', '\t'..'\r':
       g.kind = gtWhitespace
-      while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
+      while g.buf[pos] in {' ', '\t'..'\r'}: inc(pos)
     of '#':
       g.kind = gtComment
       inc(pos)
-      while g.buf[pos] notin {'\0', '\x0A', '\x0D'}: inc(pos)
+      while g.buf[pos] notin {'\0', '\n', '\r'}: inc(pos)
     of '-':
       inc(pos)
-      if g.buf[pos] in {'\0', ' ', '\x09'..'\x0D'}:
+      if g.buf[pos] in {'\0', ' ', '\t'..'\r'}:
         g.kind = gtPunctuation
       elif g.buf[pos] == '-' and
-          (pos == 1 or g.buf[pos - 2] in {'\x0A', '\x0D'}): # start of line
+          (pos == 1 or g.buf[pos - 2] in {'\n', '\r'}): # start of line
         inc(pos)
-        if g.buf[pos] == '-' and g.buf[pos + 1] in {'\0', '\x09'..'\x0D', ' '}:
+        if g.buf[pos] == '-' and g.buf[pos + 1] in {'\0', '\t'..'\r', ' '}:
           inc(pos)
           g.kind = gtKeyword
         else: yamlPossibleNumber(g, pos)
       else: yamlPossibleNumber(g, pos)
     of '.':
-      if pos == 0 or g.buf[pos - 1] in {'\x0A', '\x0D'}:
+      if pos == 0 or g.buf[pos - 1] in {'\n', '\r'}:
         inc(pos)
         for i in 1..2:
-          {.unroll.}
           if g.buf[pos] != '.': break
           inc(pos)
         if pos == g.start + 3:
@@ -783,12 +841,12 @@ proc yamlNextToken(g: var GeneralTokenizer) =
       else: yamlPlainStrLit(g, pos)
     of '?':
       inc(pos)
-      if g.buf[pos] in {'\0', ' ', '\x09'..'\x0D'}:
+      if g.buf[pos] in {'\0', ' ', '\t'..'\r'}:
         g.kind = gtPunctuation
       else: yamlPlainStrLit(g, pos)
     of ':':
       inc(pos)
-      if g.buf[pos] in {'\0', '\x09'..'\x0D', ' ', '\'', '\"'} or
+      if g.buf[pos] in {'\0', '\t'..'\r', ' ', '\'', '\"'} or
           (pos > 0 and g.buf[pos - 2] in {'}', ']', '\"', '\''}):
         g.kind = gtPunctuation
       else: yamlPlainStrLit(g, pos)
@@ -807,7 +865,7 @@ proc yamlNextToken(g: var GeneralTokenizer) =
       inc(pos)
       if g.buf[pos] == '<':
         # literal tag (e.g. `!<tag:yaml.org,2002:str>`)
-        while g.buf[pos] notin {'\0', '>', '\x09'..'\x0D', ' '}: inc(pos)
+        while g.buf[pos] notin {'\0', '>', '\t'..'\r', ' '}: inc(pos)
         if g.buf[pos] == '>': inc(pos)
       else:
         while g.buf[pos] in {'A'..'Z', 'a'..'z', '0'..'9', '-'}: inc(pos)
@@ -816,17 +874,17 @@ proc yamlNextToken(g: var GeneralTokenizer) =
           # prefixed tag (e.g. `!!str`)
           inc(pos)
           while g.buf[pos] notin
-              {'\0', '\x09'..'\x0D', ' ', ',', '[', ']', '{', '}'}: inc(pos)
-        of '\0', '\x09'..'\x0D', ' ': discard
+              {'\0', '\t'..'\r', ' ', ',', '[', ']', '{', '}'}: inc(pos)
+        of '\0', '\t'..'\r', ' ': discard
         else:
           # local tag (e.g. `!nim:system:int`)
-          while g.buf[pos] notin {'\0', '\x09'..'\x0D', ' '}: inc(pos)
+          while g.buf[pos] notin {'\0', '\t'..'\r', ' '}: inc(pos)
     of '&':
       g.kind = gtLabel
-      while g.buf[pos] notin {'\0', '\x09'..'\x0D', ' '}: inc(pos)
+      while g.buf[pos] notin {'\0', '\t'..'\r', ' '}: inc(pos)
     of '*':
       g.kind = gtReference
-      while g.buf[pos] notin {'\0', '\x09'..'\x0D', ' '}: inc(pos)
+      while g.buf[pos] notin {'\0', '\t'..'\r', ' '}: inc(pos)
     of '|', '>':
       # this can lead to incorrect tokenization when | or > appear inside flow
       # content. checking whether we're inside flow content is not
@@ -836,32 +894,111 @@ proc yamlNextToken(g: var GeneralTokenizer) =
       inc(pos)
       while g.buf[pos] in {'0'..'9', '+', '-'}: inc(pos)
     of '0'..'9': yamlPossibleNumber(g, pos)
-    of '\0': g.kind = gtEOF
+    of '\0': g.kind = gtEof
     else: yamlPlainStrLit(g, pos)
   else:
     # outside document
     case g.buf[pos]
     of '%':
-      if pos == 0 or g.buf[pos - 1] in {'\x0A', '\x0D'}:
+      if pos == 0 or g.buf[pos - 1] in {'\n', '\r'}:
         g.kind = gtDirective
-        while g.buf[pos] notin {'\0', '\x0A', '\x0D'}: inc(pos)
+        while g.buf[pos] notin {'\0', '\n', '\r'}: inc(pos)
       else:
         g.state = gtOther
         yamlPlainStrLit(g, pos)
-    of ' ', '\x09'..'\x0D':
+    of ' ', '\t'..'\r':
       g.kind = gtWhitespace
-      while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
+      while g.buf[pos] in {' ', '\t'..'\r'}: inc(pos)
     of '#':
       g.kind = gtComment
-      while g.buf[pos] notin {'\0', '\x0A', '\x0D'}: inc(pos)
-    of '\0': g.kind = gtEOF
+      while g.buf[pos] notin {'\0', '\n', '\r'}: inc(pos)
+    of '\0': g.kind = gtEof
     else:
       g.kind = gtNone
       g.state = gtOther
   g.length = pos - g.pos
   g.pos = pos
 
+proc pythonNextToken(g: var GeneralTokenizer) =
+  const
+    keywords: array[0..34, string] = [
+      "False", "None", "True", "and", "as", "assert", "async", "await",
+      "break", "class", "continue", "def", "del", "elif", "else", "except",
+      "finally", "for", "from", "global", "if", "import", "in", "is", "lambda",
+      "nonlocal", "not", "or", "pass", "raise", "return", "try", "while",
+      "with", "yield"]
+  nimNextToken(g, keywords)
+
+proc cmdNextToken(g: var GeneralTokenizer, dollarPrompt = false) =
+  var pos = g.pos
+  g.start = g.pos
+  if g.state == low(TokenClass):
+    g.state = if dollarPrompt: gtPrompt else: gtProgram
+  case g.buf[pos]
+  of ' ', '\t'..'\r':
+    g.kind = gtWhitespace
+    while g.buf[pos] in {' ', '\t'..'\r'}:
+      if g.buf[pos] == '\n':
+        g.state = if dollarPrompt: gtPrompt else: gtProgram
+      inc(pos)
+  of '\'', '"':
+    g.kind = gtOption
+    let q = g.buf[pos]
+    inc(pos)
+    while g.buf[pos] notin {q, '\0'}:
+      inc(pos)
+    if g.buf[pos] == q: inc(pos)
+  of '#':
+    g.kind = gtComment
+    while g.buf[pos] notin {'\n', '\0'}:
+      inc(pos)
+  of '&', '|':
+    g.kind = gtOperator
+    inc(pos)
+    if g.buf[pos] == g.buf[pos-1]: inc(pos)
+    g.state = gtProgram
+  of '(':
+    g.kind = gtOperator
+    g.state = gtProgram
+    inc(pos)
+  of ')':
+    g.kind = gtOperator
+    inc(pos)
+  of ';':
+    g.state = gtProgram
+    g.kind = gtOperator
+    inc(pos)
+  of '\0': g.kind = gtEof
+  elif dollarPrompt and g.state == gtPrompt:
+    if g.buf[pos] == '$' and g.buf[pos+1] in {' ', '\t'}:
+      g.kind = gtPrompt
+      inc pos, 2
+      g.state = gtProgram
+    else:
+      g.kind = gtProgramOutput
+      while g.buf[pos] notin {'\n', '\0'}:
+        inc(pos)
+  else:
+    if g.state == gtProgram:
+      g.kind = gtProgram
+      g.state = gtOption
+    else:
+      g.kind = gtOption
+    while g.buf[pos] notin {' ', '\t'..'\r', '&', '|', '(', ')', '\'', '"', '\0'}:
+      if g.buf[pos] == ';' and g.buf[pos+1] == ' ':
+        # (check space because ';' can be used inside arguments in Win bat)
+        break
+      if g.kind == gtOption and g.buf[pos] in {'/', '\\', '.'}:
+        g.kind = gtIdentifier  # for file/dir name
+      elif g.kind == gtProgram and g.buf[pos] == '=':
+        g.kind = gtIdentifier  # for env variable setting at beginning of line
+        g.state = gtProgram
+      inc(pos)
+  g.length = pos - g.pos
+  g.pos = pos
+
 proc getNextToken*(g: var GeneralTokenizer, lang: SourceLanguage) =
+  g.lang = lang
   case lang
   of langNone: assert false
   of langNim: nimNextToken(g)
@@ -870,18 +1007,33 @@ proc getNextToken*(g: var GeneralTokenizer, lang: SourceLanguage) =
   of langC: cNextToken(g)
   of langJava: javaNextToken(g)
   of langYaml: yamlNextToken(g)
+  of langPython: pythonNextToken(g)
+  of langCmd: cmdNextToken(g)
+  of langConsole: cmdNextToken(g, dollarPrompt=true)
+
+proc tokenize*(text: string, lang: SourceLanguage): seq[(string, TokenClass)] =
+  var g: GeneralTokenizer
+  initGeneralTokenizer(g, text)
+  var prevPos = 0
+  while true:
+    getNextToken(g, lang)
+    if g.kind == gtEof:
+      break
+    var s = text[prevPos ..< g.pos]
+    result.add (s, g.kind)
+    prevPos = g.pos
 
 when isMainModule:
   var keywords: seq[string]
   # Try to work running in both the subdir or at the root.
   for filename in ["doc/keywords.txt", "../../../doc/keywords.txt"]:
     try:
-      let input = string(readFile(filename))
+      let input = readFile(filename)
       keywords = input.splitWhitespace()
       break
     except:
       echo filename, " not found"
-  doAssert(not keywords.isNil, "Couldn't read any keywords.txt file!")
+  doAssert(keywords.len > 0, "Couldn't read any keywords.txt file!")
   for i in 0..min(keywords.len, nimKeywords.len)-1:
     doAssert keywords[i] == nimKeywords[i], "Unexpected keyword"
   doAssert keywords.len == nimKeywords.len, "No matching lengths"
diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim
index 7ee071c79..706c50689 100644
--- a/lib/packages/docutils/rst.nim
+++ b/lib/packages/docutils/rst.nim
@@ -7,21 +7,46 @@
 #    distribution, for details about the copyright.
 #
 
-## This module implements a `reStructuredText`:idx: parser. A large
-## subset is implemented. Some features of the `markdown`:idx: wiki syntax are
-## also supported.
+## This module implements a `reStructuredText`:idx: (RST) and
+## `Markdown`:idx: parser.
+## User's manual on supported markup syntax and command line usage can be
+## found in [Nim-flavored Markdown and reStructuredText](markdown_rst.html).
+##
+## * See also [Nim DocGen Tools Guide](docgen.html) for handling of
+##   ``.nim`` files.
+## * See also [packages/docutils/rstgen module](rstgen.html) to know how to
+##   generate HTML or Latex strings (for embedding them into custom documents).
+##
+## Choice between Markdown and RST as well as optional additional features are
+## turned on by passing ``options:`` [RstParseOptions] to [proc rstParse].
 
 import
-  os, strutils, rstast
+  std/[os, strutils, enumutils, algorithm, lists, sequtils,
+  tables, strscans]
+import dochelpers, rstidx, rstast
+import std/private/miscdollars
+from highlite import SourceLanguage, getSourceLanguage
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
+
 
 type
   RstParseOption* = enum     ## options for the RST parser
-    roSkipPounds,             ## skip ``#`` at line beginning (documentation
-                              ## embedded in Nim comments)
     roSupportSmilies,         ## make the RST parser support smilies like ``:)``
     roSupportRawDirective,    ## support the ``raw`` directive (don't support
                               ## it for sandboxing)
-    roSupportMarkdown         ## support additional features of markdown
+    roSupportMarkdown,        ## support additional features of Markdown
+    roPreferMarkdown,         ## parse as Markdown (keeping RST as "extension"
+                              ## to Markdown) -- implies `roSupportMarkdown`
+    roNimFile                 ## set for Nim files where default interpreted
+                              ## text role should be :nim:
+    roSandboxDisabled         ## this option enables certain options
+                              ## (e.g. raw, include, importdoc)
+                              ## which are disabled by default as they can
+                              ## enable users to read arbitrary data and
+                              ## perform XSS if the parser is used in a web
+                              ## app.
 
   RstParseOptions* = set[RstParseOption]
 
@@ -30,39 +55,41 @@ type
     mcWarning = "Warning",
     mcError = "Error"
 
+  # keep the order in sync with compiler/docgen.nim and compiler/lineinfos.nim:
   MsgKind* = enum          ## the possible messages
-    meCannotOpenFile,
-    meExpected,
-    meGridTableNotImplemented,
-    meNewSectionExpected,
-    meGeneralParseError,
-    meInvalidDirective,
-    mwRedefinitionOfLabel,
-    mwUnknownSubstitution,
-    mwUnsupportedLanguage,
-    mwUnsupportedField
+    meCannotOpenFile = "cannot open '$1'",
+    meExpected = "'$1' expected",
+    meMissingClosing = "$1",
+    meGridTableNotImplemented = "grid table is not implemented",
+    meMarkdownIllformedTable = "illformed delimiter row of a Markdown table",
+    meIllformedTable = "Illformed table: $1",
+    meNewSectionExpected = "new section expected $1",
+    meGeneralParseError = "general parse error",
+    meInvalidDirective = "invalid directive: '$1'",
+    meInvalidField = "invalid field: $1",
+    meFootnoteMismatch = "mismatch in number of footnotes and their refs: $1",
+    mwRedefinitionOfLabel = "redefinition of label '$1'",
+    mwUnknownSubstitution = "unknown substitution '$1'",
+    mwAmbiguousLink = "ambiguous doc link $1",
+    mwBrokenLink = "broken link '$1'",
+    mwUnsupportedLanguage = "language '$1' not supported",
+    mwUnsupportedField = "field '$1' not supported",
+    mwRstStyle = "RST style: $1",
+    mwUnusedImportdoc = "importdoc for '$1' is not used",
+    meSandboxedDirective = "disabled directive: '$1'",
 
   MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind,
-                       arg: string) {.closure.} ## what to do in case of an error
-  FindFileHandler* = proc (filename: string): string {.closure.}
-
-const
-  messages: array[MsgKind, string] = [
-    meCannotOpenFile: "cannot open '$1'",
-    meExpected: "'$1' expected",
-    meGridTableNotImplemented: "grid table is not implemented",
-    meNewSectionExpected: "new section expected",
-    meGeneralParseError: "general parse error",
-    meInvalidDirective: "invalid directive: '$1'",
-    mwRedefinitionOfLabel: "redefinition of label '$1'",
-    mwUnknownSubstitution: "unknown substitution '$1'",
-    mwUnsupportedLanguage: "language '$1' not supported",
-    mwUnsupportedField: "field '$1' not supported"
-  ]
+                       arg: string) {.closure, gcsafe.} ## what to do in case of an error
+  FindFileHandler* = proc (filename: string): string {.closure, gcsafe.}
+  FindRefFileHandler* =
+    proc (targetRelPath: string):
+         tuple[targetPath: string, linkRelPath: string] {.closure, gcsafe.}
+    ## returns where .html or .idx file should be found by its relative path;
+    ## `linkRelPath` is a prefix to be added before a link anchor from such file
 
 proc rstnodeToRefname*(n: PRstNode): string
 proc addNodes*(n: PRstNode): string
-proc getFieldValue*(n: PRstNode, fieldname: string): string
+proc getFieldValue*(n: PRstNode, fieldname: string): string {.gcsafe.}
 proc getArgument*(n: PRstNode): string
 
 # ----------------------------- scanner part --------------------------------
@@ -109,12 +136,21 @@ const
     ":geek:": "icon_e_geek",
     ":ugeek:": "icon_e_ugeek"
   }
+  SandboxDirAllowlist = [
+    "image", "code", "code-block", "admonition", "attention", "caution",
+    "container", "contents", "danger", "default-role", "error", "figure",
+    "hint", "important", "index", "note", "role", "tip", "title", "warning"]
 
 type
   TokType = enum
-    tkEof, tkIndent, tkWhite, tkWord, tkAdornment, tkPunct, tkOther
-  Token = object             # a RST token
-    kind*: TokType           # the type of the token
+    tkEof, tkIndent,
+    tkWhite, tkWord,
+    tkAdornment,              # used for chapter adornment, transitions and
+                              # horizontal table borders
+    tkPunct,                  # one or many punctuation characters
+    tkOther
+  Token = object              # a RST token
+    kind*: TokType            # the type of the token
     ival*: int                # the indentation or parsed integer value
     symbol*: string           # the parsed symbol as string
     line*, col*: int          # line and column of the token
@@ -124,7 +160,8 @@ type
     buf*: cstring
     bufpos*: int
     line*, col*, baseIndent*: int
-    skipPounds*: bool
+    adornmentLine*: bool
+    escapeNext*: bool
 
 proc getThing(L: var Lexer, tok: var Token, s: set[char]) =
   tok.kind = tkWord
@@ -132,50 +169,82 @@ proc getThing(L: var Lexer, tok: var Token, s: set[char]) =
   tok.col = L.col
   var pos = L.bufpos
   while true:
-    add(tok.symbol, L.buf[pos])
-    inc(pos)
+    tok.symbol.add(L.buf[pos])
+    inc pos
     if L.buf[pos] notin s: break
-  inc(L.col, pos - L.bufpos)
+  inc L.col, pos - L.bufpos
   L.bufpos = pos
 
-proc getAdornment(L: var Lexer, tok: var Token) =
-  tok.kind = tkAdornment
+proc isCurrentLineAdornment(L: var Lexer): bool =
+  var pos = L.bufpos
+  let c = L.buf[pos]
+  while true:
+    inc pos
+    if L.buf[pos] in {'\c', '\l', '\0'}:
+      break
+    if c == '+':  # grid table
+      if L.buf[pos] notin {'-', '=', '+'}:
+        return false
+    else:  # section adornment or table horizontal border
+      if L.buf[pos] notin {c, ' ', '\t', '\v', '\f'}:
+        return false
+  result = true
+
+proc getPunctAdornment(L: var Lexer, tok: var Token) =
+  if L.adornmentLine:
+    tok.kind = tkAdornment
+  else:
+    tok.kind = tkPunct
   tok.line = L.line
   tok.col = L.col
   var pos = L.bufpos
-  var c = L.buf[pos]
-  while true:
-    add(tok.symbol, L.buf[pos])
-    inc(pos)
-    if L.buf[pos] != c: break
-  inc(L.col, pos - L.bufpos)
+  let c = L.buf[pos]
+  if not L.escapeNext and (c != '\\' or L.adornmentLine):
+    while true:
+      tok.symbol.add(L.buf[pos])
+      inc pos
+      if L.buf[pos] != c: break
+  elif L.escapeNext:
+    tok.symbol.add(L.buf[pos])
+    inc pos
+  else:  # not L.escapeNext and c == '\\' and not L.adornmentLine
+    tok.symbol.add '\\'
+    inc pos
+    L.escapeNext = true
+  inc L.col, pos - L.bufpos
   L.bufpos = pos
+  if tok.symbol == "\\": tok.kind = tkPunct
+    # nim extension: standalone \ can not be adornment
+
+proc getBracket(L: var Lexer, tok: var Token) =
+  tok.kind = tkPunct
+  tok.line = L.line
+  tok.col = L.col
+  tok.symbol.add(L.buf[L.bufpos])
+  inc L.col
+  inc L.bufpos
 
 proc getIndentAux(L: var Lexer, start: int): int =
   var pos = start
-  var buf = L.buf
   # skip the newline (but include it in the token!)
-  if buf[pos] == '\x0D':
-    if buf[pos + 1] == '\x0A': inc(pos, 2)
-    else: inc(pos)
-  elif buf[pos] == '\x0A':
-    inc(pos)
-  if L.skipPounds:
-    if buf[pos] == '#': inc(pos)
-    if buf[pos] == '#': inc(pos)
+  if L.buf[pos] == '\r':
+    if L.buf[pos + 1] == '\n': inc pos, 2
+    else: inc pos
+  elif L.buf[pos] == '\n':
+    inc pos
   while true:
-    case buf[pos]
-    of ' ', '\x0B', '\x0C':
-      inc(pos)
-      inc(result)
-    of '\x09':
-      inc(pos)
+    case L.buf[pos]
+    of ' ', '\v', '\f':
+      inc pos
+      inc result
+    of '\t':
+      inc pos
       result = result - (result mod 8) + 8
     else:
       break                   # EndOfFile also leaves the loop
-  if buf[pos] == '\0':
+  if L.buf[pos] == '\0':
     result = 0
-  elif (buf[pos] == '\x0A') or (buf[pos] == '\x0D'):
+  elif L.buf[pos] == '\n' or L.buf[pos] == '\r':
     # look at the next line for proper indentation:
     result = getIndentAux(L, pos)
   L.bufpos = pos              # no need to set back buf
@@ -193,22 +262,28 @@ proc getIndent(L: var Lexer, tok: var Token) =
 proc rawGetTok(L: var Lexer, tok: var Token) =
   tok.symbol = ""
   tok.ival = 0
+  if L.col == 0:
+    L.adornmentLine = false
   var c = L.buf[L.bufpos]
   case c
   of 'a'..'z', 'A'..'Z', '\x80'..'\xFF', '0'..'9':
     getThing(L, tok, SymChars)
-  of ' ', '\x09', '\x0B', '\x0C':
-    getThing(L, tok, {' ', '\x09'})
+  of ' ', '\t', '\v', '\f':
+    getThing(L, tok, {' ', '\t'})
     tok.kind = tkWhite
-    if L.buf[L.bufpos] in {'\x0D', '\x0A'}:
+    if L.buf[L.bufpos] in {'\r', '\n'}:
       rawGetTok(L, tok)       # ignore spaces before \n
-  of '\x0D', '\x0A':
+  of '\r', '\n':
     getIndent(L, tok)
-  of '!', '\"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.',
-     '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{',
-     '|', '}', '~':
-    getAdornment(L, tok)
-    if len(tok.symbol) <= 3: tok.kind = tkPunct
+    L.adornmentLine = false
+  of '!', '\"', '#', '$', '%', '&', '\'',  '*', '+', ',', '-', '.',
+     '/', ':', ';', '<', '=', '>', '?', '@', '\\', '^', '_', '`',
+     '|', '~':
+    if L.col == 0:
+      L.adornmentLine = L.isCurrentLineAdornment()
+    getPunctAdornment(L, tok)
+  of '(', ')', '[', ']', '{', '}':
+    getBracket(L, tok)
   else:
     tok.line = L.line
     tok.col = L.col
@@ -216,296 +291,909 @@ proc rawGetTok(L: var Lexer, tok: var Token) =
       tok.kind = tkEof
     else:
       tok.kind = tkOther
-      add(tok.symbol, c)
-      inc(L.bufpos)
-      inc(L.col)
+      tok.symbol.add(c)
+      inc L.bufpos
+      inc L.col
   tok.col = max(tok.col - L.baseIndent, 0)
 
-proc getTokens(buffer: string, skipPounds: bool, tokens: var TokenSeq): int =
+proc getTokens(buffer: string, tokens: var TokenSeq) =
   var L: Lexer
-  var length = len(tokens)
+  var length = tokens.len
   L.buf = cstring(buffer)
   L.line = 0                  # skip UTF-8 BOM
-  if (L.buf[0] == '\xEF') and (L.buf[1] == '\xBB') and (L.buf[2] == '\xBF'):
-    inc(L.bufpos, 3)
-  L.skipPounds = skipPounds
-  if skipPounds:
-    if L.buf[L.bufpos] == '#':
-      inc(L.bufpos)
-      inc(result)
-    if L.buf[L.bufpos] == '#':
-      inc(L.bufpos)
-      inc(result)
-    L.baseIndent = 0
-    while L.buf[L.bufpos] == ' ':
-      inc(L.bufpos)
-      inc(L.baseIndent)
-      inc(result)
+  if L.buf[0] == '\xEF' and L.buf[1] == '\xBB' and L.buf[2] == '\xBF':
+    inc L.bufpos, 3
   while true:
-    inc(length)
+    inc length
     setLen(tokens, length)
+    let toEscape = L.escapeNext
     rawGetTok(L, tokens[length - 1])
+    if toEscape: L.escapeNext = false
     if tokens[length - 1].kind == tkEof: break
   if tokens[0].kind == tkWhite:
     # BUGFIX
-    tokens[0].ival = len(tokens[0].symbol)
+    tokens[0].ival = tokens[0].symbol.len
     tokens[0].kind = tkIndent
 
 type
-  LevelMap = array[char, int]
+  LevelInfo = object
+    symbol: char         # adornment character
+    hasOverline: bool    # has also overline (besides underline)?
+    line: int            # the last line of this style occurrence
+                         # (for error message)
+    hasPeers: bool       # has headings on the same level of hierarchy?
+  LiteralBlockKind = enum  # RST-style literal blocks after `::`
+    lbNone,
+    lbIndentedLiteralBlock,
+    lbQuotedLiteralBlock
+  LevelMap = seq[LevelInfo]   # Saves for each possible title adornment
+                              # style its level in the current document.
+  SubstitutionKind = enum
+    rstSubstitution = "substitution",
+    hyperlinkAlias = "hyperlink alias",
+    implicitHyperlinkAlias = "implicitly-generated hyperlink alias"
   Substitution = object
+    kind*: SubstitutionKind
     key*: string
     value*: PRstNode
-
-  SharedState = object
-    options: RstParseOptions    # parsing options
-    uLevel, oLevel: int         # counters for the section levels
+    info*: TLineInfo   # place where the substitution was defined
+  AnchorRule = enum
+    arInternalRst,  ## For automatically generated RST anchors (from
+                    ## headings, footnotes, inline internal targets):
+                    ## case-insensitive, 1-space-significant (by RST spec)
+    arExternalRst,  ## For external .nim doc comments or .rst/.md
+    arNim   ## For anchors generated by ``docgen.nim``: Nim-style case
+            ## sensitivity, etc. (see `proc normalizeNimName`_ for details)
+    arHyperlink,  ## For links with manually set anchors in
+                  ## form `text <pagename.html#anchor>`_
+  RstAnchorKind = enum
+    manualDirectiveAnchor = "manual directive anchor",
+    manualInlineAnchor = "manual inline anchor",
+    footnoteAnchor = "footnote anchor",
+    headlineAnchor = "implicitly-generated headline anchor"
+  AnchorSubst = object
+    info: TLineInfo         # the file where the anchor was defined
+    priority: int
+    case kind: range[arInternalRst .. arNim]
+    of arInternalRst:
+      anchorType: RstAnchorKind
+      target: PRstNode
+    of arExternalRst:
+      anchorTypeExt: RstAnchorKind
+      refnameExt: string
+    of arNim:
+      module: FileIndex     # anchor's module (generally not the same as file)
+      tooltip: string       # displayed tooltip for Nim-generated anchors
+      langSym: LangSymbol
+      refname: string     # A reference name that will be inserted directly
+                          # into HTML/Latex.
+      external: bool
+  AnchorSubstTable = Table[string, seq[AnchorSubst]]
+                         # use `seq` to account for duplicate anchors
+  FootnoteType = enum
+    fnManualNumber,     # manually numbered footnote like [3]
+    fnAutoNumber,       # auto-numbered footnote [#]
+    fnAutoNumberLabel,  # auto-numbered with label [#label]
+    fnAutoSymbol,       # auto-symbol footnote [*]
+    fnCitation          # simple text label like [citation2021]
+  FootnoteSubst = tuple
+    kind: FootnoteType  # discriminator
+    number: int         # valid for fnManualNumber (always) and fnAutoNumber,
+                        # fnAutoNumberLabel after resolveSubs is called
+    autoNumIdx: int     # order of occurrence: fnAutoNumber, fnAutoNumberLabel
+    autoSymIdx: int     # order of occurrence: fnAutoSymbol
+    label: string       # valid for fnAutoNumberLabel
+  RstFileTable* = object
+    filenameToIdx*: Table[string, FileIndex]
+    idxToFilename*: seq[string]
+  ImportdocInfo = object
+    used: bool             # was this import used?
+    fromInfo: TLineInfo    # place of `.. importdoc::` directive
+    idxPath: string        # full path to ``.idx`` file
+    linkRelPath: string    # prefix before target anchor
+    title: string          # document title obtained from ``.idx``
+  RstSharedState = object
+    options*: RstParseOptions   # parsing options
+    hLevels: LevelMap           # hierarchy of heading styles
+    hTitleCnt: int              # =0 if no title, =1 if only main title,
+                                # =2 if both title and subtitle are present
+    hCurLevel: int              # current section level
+    currRole: string            # current interpreted text role
+    currRoleKind: RstNodeKind   # ... and its node kind
     subs: seq[Substitution]     # substitutions
-    refs: seq[Substitution]     # references
-    underlineToLevel: LevelMap  # Saves for each possible title adornment
-                                # character its level in the
-                                # current document.
-                                # This is for single underline adornments.
-    overlineToLevel: LevelMap   # Saves for each possible title adornment
-                                # character its level in the current
-                                # document.
-                                # This is for over-underline adornments.
+    refs*: seq[Substitution]    # references
+    anchors*: AnchorSubstTable
+                                # internal target substitutions
+    lineFootnoteNum: seq[TLineInfo]     # footnote line, auto numbers .. [#]
+    lineFootnoteNumRef: seq[TLineInfo]  # footnote line, their reference [#]_
+    currFootnoteNumRef: int             # ... their counter for `resolveSubs`
+    lineFootnoteSym: seq[TLineInfo]     # footnote line, auto symbols .. [*]
+    lineFootnoteSymRef: seq[TLineInfo]  # footnote line, their reference [*]_
+    currFootnoteSymRef: int             # ... their counter for `resolveSubs`
+    footnotes: seq[FootnoteSubst] # correspondence b/w footnote label,
+                                  # number, order of occurrence
     msgHandler: MsgHandler      # How to handle errors.
-    findFile: FindFileHandler   # How to find files.
-
-  PSharedState = ref SharedState
+    findFile: FindFileHandler   # How to find files for include.
+    findRefFile: FindRefFileHandler
+                                # How to find files imported by importdoc.
+    filenames*: RstFileTable    # map file name <-> FileIndex (for storing
+                                # file names for warnings after 1st stage)
+    currFileIdx*: FileIndex     # current index in `filenames`
+    tocPart*: seq[PRstNode]     # all the headings of a document
+    hasToc*: bool
+    idxImports*: Table[string, ImportdocInfo]
+                                # map `importdoc`ed filename -> it's info
+    nimFileImported*: bool      # Was any ``.nim`` module `importdoc`ed ?
+
+  PRstSharedState* = ref RstSharedState
+  ManualAnchor = object
+    alias: string     # a (short) name that can substitute the `anchor`
+    anchor: string    # anchor = id = refname
+    info: TLineInfo
   RstParser = object of RootObj
     idx*: int
     tok*: TokenSeq
-    s*: PSharedState
+    s*: PRstSharedState
     indentStack*: seq[int]
-    filename*: string
-    line*, col*: int
-    hasToc*: bool
+    line*, col*: int            ## initial line/column of whole text or
+                                ## documenation fragment that will be added
+                                ## in case of error/warning reporting to
+                                ## (relative) line/column of the token.
+    curAnchors*: seq[ManualAnchor]
+                                ## seq to accumulate aliases for anchors:
+                                ## because RST can have >1 alias per 1 anchor
 
   EParseError* = object of ValueError
+  SectionParser = proc (p: var RstParser): PRstNode {.nimcall, gcsafe.}
+
+const
+  LineRstInit* = 1  ## Initial line number for standalone RST text
+  ColRstInit* = 0   ## Initial column number for standalone RST text
+                    ## (Nim global reporting adds ColOffset=1)
+  ColRstOffset* = 1 ## 1: a replica of ColOffset for internal use
+
+template currentTok(p: RstParser): Token = p.tok[p.idx]
+template prevTok(p: RstParser): Token = p.tok[p.idx - 1]
+template nextTok(p: RstParser): Token = p.tok[p.idx + 1]
 
 proc whichMsgClass*(k: MsgKind): MsgClass =
   ## returns which message class `k` belongs to.
-  case ($k)[1]
+  case k.symbolName[1]
   of 'e', 'E': result = mcError
   of 'w', 'W': result = mcWarning
   of 'h', 'H': result = mcHint
   else: assert false, "msgkind does not fit naming scheme"
 
 proc defaultMsgHandler*(filename: string, line, col: int, msgkind: MsgKind,
-                        arg: string) {.procvar.} =
+                        arg: string) =
   let mc = msgkind.whichMsgClass
-  let a = messages[msgkind] % arg
-  let message = "$1($2, $3) $4: $5" % [filename, $line, $col, $mc, a]
+  let a = $msgkind % arg
+  var message: string
+  toLocation(message, filename, line, col + ColRstOffset)
+  message.add " $1: $2" % [$mc, a]
   if mc == mcError: raise newException(EParseError, message)
   else: writeLine(stdout, message)
 
-proc defaultFindFile*(filename: string): string {.procvar.} =
-  if existsFile(filename): result = filename
+proc defaultFindFile*(filename: string): string =
+  if fileExists(filename): result = filename
   else: result = ""
 
-proc newSharedState(options: RstParseOptions,
-                    findFile: FindFileHandler,
-                    msgHandler: MsgHandler): PSharedState =
-  new(result)
-  result.subs = @[]
-  result.refs = @[]
-  result.options = options
-  result.msgHandler = if not isNil(msgHandler): msgHandler else: defaultMsgHandler
-  result.findFile = if not isNil(findFile): findFile else: defaultFindFile
+proc defaultFindRefFile*(filename: string): (string, string) =
+  (filename, "")
+
+proc defaultRole(options: RstParseOptions): string =
+  if roNimFile in options: "nim" else: "literal"
+
+proc whichRoleAux(sym: string): RstNodeKind =
+  let r = sym.toLowerAscii
+  case r
+  of "idx": result = rnIdx
+  of "literal": result = rnInlineLiteral
+  of "strong": result = rnStrongEmphasis
+  of "emphasis": result = rnEmphasis
+  of "sub", "subscript": result = rnSub
+  of "sup", "superscript": result = rnSup
+  # literal and code are the same in our implementation
+  of "code": result = rnInlineLiteral
+  of "program", "option", "tok": result = rnCodeFragment
+  # c++ currently can be spelled only as cpp, c# only as csharp
+  elif getSourceLanguage(r) != langNone:
+    result = rnInlineCode
+  else:  # unknown role
+    result = rnUnknownRole
+
+proc len(filenames: RstFileTable): int = filenames.idxToFilename.len
+
+proc addFilename*(s: PRstSharedState, file1: string): FileIndex =
+  ## Returns index of filename, adding it if it has not been used before
+  let nextIdx = s.filenames.len.FileIndex
+  result = getOrDefault(s.filenames.filenameToIdx, file1, default = nextIdx)
+  if result == nextIdx:
+    s.filenames.filenameToIdx[file1] = result
+    s.filenames.idxToFilename.add file1
+
+proc setCurrFilename*(s: PRstSharedState, file1: string) =
+  s.currFileIdx = addFilename(s, file1)
+
+proc getFilename(filenames: RstFileTable, fid: FileIndex): string =
+  doAssert(0 <= fid.int and fid.int < filenames.len,
+      "incorrect FileIndex $1 (range 0..$2)" % [
+        $fid.int, $(filenames.len - 1)])
+  result = filenames.idxToFilename[fid.int]
+
+proc getFilename(s: PRstSharedState, subst: AnchorSubst): string =
+  getFilename(s.filenames, subst.info.fileIndex)
+
+proc getModule(s: PRstSharedState, subst: AnchorSubst): string =
+  result = getFilename(s.filenames, subst.module)
+
+proc currFilename(s: PRstSharedState): string =
+  getFilename(s.filenames, s.currFileIdx)
+
+proc newRstSharedState*(options: RstParseOptions,
+                        filename: string,
+                        findFile: FindFileHandler,
+                        findRefFile: FindRefFileHandler,
+                        msgHandler: MsgHandler,
+                        hasToc: bool): PRstSharedState =
+  let r = defaultRole(options)
+  result = PRstSharedState(
+      currRole: r,
+      currRoleKind: whichRoleAux(r),
+      options: options,
+      msgHandler: if not isNil(msgHandler): msgHandler else: defaultMsgHandler,
+      findFile: if not isNil(findFile): findFile else: defaultFindFile,
+      findRefFile:
+        if not isNil(findRefFile): findRefFile
+        else: defaultFindRefFile,
+      hasToc: hasToc
+  )
+  setCurrFilename(result, filename)
+
+proc curLine(p: RstParser): int = p.line + currentTok(p).line
 
 proc findRelativeFile(p: RstParser; filename: string): string =
-  result = p.filename.splitFile.dir / filename
-  if not existsFile(result):
+  result = p.s.currFilename.splitFile.dir / filename
+  if not fileExists(result):
     result = p.s.findFile(filename)
 
 proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string) =
-  p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line,
-                             p.col + p.tok[p.idx].col, msgKind, arg)
+  p.s.msgHandler(p.s.currFilename, curLine(p),
+                             p.col + currentTok(p).col, msgKind, arg)
+
+proc rstMessage(s: PRstSharedState, msgKind: MsgKind, arg: string) =
+  s.msgHandler(s.currFilename, LineRstInit, ColRstInit, msgKind, arg)
+
+proc rstMessage(s: PRstSharedState, msgKind: MsgKind, arg: string;
+                line, col: int) =
+  s.msgHandler(s.currFilename, line, col, msgKind, arg)
+
+proc rstMessage(s: PRstSharedState, filename: string, msgKind: MsgKind,
+                arg: string) =
+  s.msgHandler(filename, LineRstInit, ColRstInit, msgKind, arg)
+
+proc rstMessage*(filenames: RstFileTable, f: MsgHandler,
+                 info: TLineInfo, msgKind: MsgKind, arg: string) =
+  ## Print warnings using `info`, i.e. in 2nd-pass warnings for
+  ## footnotes/substitutions/references or from ``rstgen.nim``.
+  let file = getFilename(filenames, info.fileIndex)
+  f(file, info.line.int, info.col.int, msgKind, arg)
 
 proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string, line, col: int) =
-  p.s.msgHandler(p.filename, p.line + line,
+  p.s.msgHandler(p.s.currFilename, p.line + line,
                              p.col + col, msgKind, arg)
 
 proc rstMessage(p: RstParser, msgKind: MsgKind) =
-  p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line,
-                             p.col + p.tok[p.idx].col, msgKind,
-                             p.tok[p.idx].symbol)
+  p.s.msgHandler(p.s.currFilename, curLine(p),
+                             p.col + currentTok(p).col, msgKind,
+                             currentTok(p).symbol)
+
+# Functions `isPureRst` & `stopOrWarn` address differences between
+# Markdown and RST:
+# * Markdown always tries to continue working. If it is really impossible
+#   to parse a markup element, its proc just returns `nil` and parsing
+#   continues for it as for normal text paragraph.
+#   The downside is that real mistakes/typos are often silently ignored.
+#   The same applies to legacy `RstMarkdown` mode for nimforum.
+# * RST really signals errors. The downside is that it's more intrusive -
+#   the user must escape special syntax with \ explicitly.
+#
+# TODO: we need to apply this strategy to all markup elements eventually.
+
+func isPureRst(p: RstParser): bool = roSupportMarkdown notin p.s.options
+func isRst(p: RstParser): bool = roPreferMarkdown notin p.s.options
+func isMd(p: RstParser): bool = roPreferMarkdown in p.s.options
+func isMd(s: PRstSharedState): bool = roPreferMarkdown in s.options
+
+proc stopOrWarn(p: RstParser, errorType: MsgKind, arg: string) =
+  let realMsgKind = if isPureRst(p): errorType else: mwRstStyle
+  rstMessage(p, realMsgKind, arg)
+
+proc stopOrWarn(p: RstParser, errorType: MsgKind, arg: string, line, col: int) =
+  let realMsgKind = if isPureRst(p): errorType else: mwRstStyle
+  rstMessage(p, realMsgKind, arg, line, col)
 
 proc currInd(p: RstParser): int =
   result = p.indentStack[high(p.indentStack)]
 
 proc pushInd(p: var RstParser, ind: int) =
-  add(p.indentStack, ind)
+  p.indentStack.add(ind)
 
 proc popInd(p: var RstParser) =
-  if len(p.indentStack) > 1: setLen(p.indentStack, len(p.indentStack) - 1)
+  if p.indentStack.len > 1: setLen(p.indentStack, p.indentStack.len - 1)
+
+# Working with indentation in rst.nim
+# -----------------------------------
+#
+# Every line break has an associated tkIndent.
+# The tokenizer writes back the first column of next non-blank line
+# in all preceeding tkIndent tokens to the `ival` field of tkIndent.
+#
+# RST document is separated into body elements (B.E.), every of which
+# has a dedicated handler proc (or block of logic when B.E. is a block quote)
+# that should follow the next rule:
+#   Every B.E. handler proc should finish at tkIndent (newline)
+#   after its B.E. finishes.
+#   Then its callers (which is `parseSection` or another B.E. handler)
+#   check for tkIndent ival (without necessity to advance `p.idx`)
+#   and decide themselves whether they continue processing or also stop.
+#
+# An example::
+#
+#   L    RST text fragment                  indentation
+#     +--------------------+
+#   1 |                    | <- (empty line at the start of file) no tokens
+#   2 |First paragraph.    | <- tkIndent has ival=0, and next tkWord has col=0
+#   3 |                    | <- tkIndent has ival=0
+#   4 |* bullet item and   | <- tkIndent has ival=0, and next tkPunct has col=0
+#   5 |  its continuation  | <- tkIndent has ival=2, and next tkWord has col=2
+#   6 |                    | <- tkIndent has ival=4
+#   7 |    Block quote     | <- tkIndent has ival=4, and next tkWord has col=4
+#   8 |                    | <- tkIndent has ival=0
+#   9 |                    | <- tkIndent has ival=0
+#   10|Final paragraph     | <- tkIndent has ival=0, and tkWord has col=0
+#     +--------------------+
+#    C:01234
+#
+# Here parser starts with initial `indentStack=[0]` and then calls the
+# 1st `parseSection`:
+#
+#   - `parseSection` calls `parseParagraph` and "First paragraph" is parsed
+#   - bullet list handler is started at reaching ``*`` (L4 C0), it
+#     starts bullet item logic (L4 C2), which calls `pushInd(p, ind=2)`,
+#     then calls `parseSection` (2nd call, nested) which parses
+#     paragraph "bullet list and its continuation" and then starts
+#     a block quote logic (L7 C4).
+#     The block quote logic calls calls `pushInd(p, ind=4)` and
+#     calls `parseSection` again, so a (simplified) sequence of calls now is::
+#
+#       parseSection -> parseBulletList ->
+#         parseSection (+block quote logic) -> parseSection
+#
+#     3rd `parseSection` finishes, block quote logic calls `popInd(p)`,
+#     it returns to bullet item logic, which sees that next tkIndent has
+#     ival=0 and stops there since the required indentation for a bullet item
+#     is 2 and 0<2; the bullet item logic calls `popInd(p)`.
+#     Then bullet list handler checks that next tkWord (L10 C0) has the
+#     right indentation but does not have ``*`` so stops at tkIndent (L10).
+#   - 1st `parseSection` invocation calls `parseParagraph` and the
+#     "Final paragraph" is parsed.
+#
+# If a B.E. handler has advanced `p.idx` past tkIndent to check
+# whether it should continue its processing or not, and decided not to,
+# then this B.E. handler should step back (e.g. do `dec p.idx`).
 
-proc initParser(p: var RstParser, sharedState: PSharedState) =
+proc initParser(p: var RstParser, sharedState: PRstSharedState) =
   p.indentStack = @[0]
   p.tok = @[]
   p.idx = 0
-  p.filename = ""
-  p.hasToc = false
-  p.col = 0
-  p.line = 1
+  p.col = ColRstInit
+  p.line = LineRstInit
   p.s = sharedState
 
 proc addNodesAux(n: PRstNode, result: var string) =
+  if n == nil:
+    return
   if n.kind == rnLeaf:
-    add(result, n.text)
+    result.add(n.text)
   else:
-    for i in countup(0, len(n) - 1): addNodesAux(n.sons[i], result)
+    for i in 0 ..< n.len: addNodesAux(n.sons[i], result)
 
 proc addNodes(n: PRstNode): string =
-  result = ""
-  addNodesAux(n, result)
+  n.addNodesAux(result)
+
+proc linkName(n: PRstNode): string =
+  ## Returns a normalized reference name, see:
+  ## https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#reference-names
+  n.addNodes.toLowerAscii
 
 proc rstnodeToRefnameAux(n: PRstNode, r: var string, b: var bool) =
+  template special(s) =
+    if b:
+      r.add('-')
+      b = false
+    r.add(s)
+
   if n == nil: return
   if n.kind == rnLeaf:
-    for i in countup(0, len(n.text) - 1):
+    for i in 0 ..< n.text.len:
       case n.text[i]
       of '0'..'9':
         if b:
-          add(r, '-')
+          r.add('-')
           b = false
-        if len(r) == 0: add(r, 'Z')
-        add(r, n.text[i])
-      of 'a'..'z':
+        if r.len == 0: r.add('Z')
+        r.add(n.text[i])
+      of 'a'..'z', '\128'..'\255':
         if b:
-          add(r, '-')
+          r.add('-')
           b = false
-        add(r, n.text[i])
+        r.add(n.text[i])
       of 'A'..'Z':
         if b:
-          add(r, '-')
+          r.add('-')
           b = false
-        add(r, chr(ord(n.text[i]) - ord('A') + ord('a')))
+        r.add(chr(ord(n.text[i]) - ord('A') + ord('a')))
+      of '$': special "dollar"
+      of '%': special "percent"
+      of '&': special "amp"
+      of '^': special "roof"
+      of '!': special "emark"
+      of '?': special "qmark"
+      of '*': special "star"
+      of '+': special "plus"
+      of '-': special "minus"
+      of '/': special "slash"
+      of '\\': special "backslash"
+      of '=': special "eq"
+      of '<': special "lt"
+      of '>': special "gt"
+      of '~': special "tilde"
+      of ':': special "colon"
+      of '.': special "dot"
+      of '@': special "at"
+      of '|': special "bar"
       else:
-        if (len(r) > 0): b = true
+        if r.len > 0: b = true
   else:
-    for i in countup(0, len(n) - 1): rstnodeToRefnameAux(n.sons[i], r, b)
+    for i in 0 ..< n.len: rstnodeToRefnameAux(n.sons[i], r, b)
 
 proc rstnodeToRefname(n: PRstNode): string =
-  result = ""
   var b = false
   rstnodeToRefnameAux(n, result, b)
 
-proc findSub(p: var RstParser, n: PRstNode): int =
+proc findSub(s: PRstSharedState, n: PRstNode): int =
   var key = addNodes(n)
   # the spec says: if no exact match, try one without case distinction:
-  for i in countup(0, high(p.s.subs)):
-    if key == p.s.subs[i].key:
+  for i in countup(0, high(s.subs)):
+    if key == s.subs[i].key:
       return i
-  for i in countup(0, high(p.s.subs)):
-    if cmpIgnoreStyle(key, p.s.subs[i].key) == 0:
+  for i in countup(0, high(s.subs)):
+    if cmpIgnoreStyle(key, s.subs[i].key) == 0:
       return i
   result = -1
 
+proc lineInfo(p: RstParser, iTok: int): TLineInfo =
+  result.col = int16(p.col + p.tok[iTok].col)
+  result.line = uint16(p.line + p.tok[iTok].line)
+  result.fileIndex = p.s.currFileIdx
+
+proc lineInfo(p: RstParser): TLineInfo = lineInfo(p, p.idx)
+# TODO: we need this simplification because we don't preserve exact starting
+# token of currently parsed element:
+proc prevLineInfo(p: RstParser): TLineInfo = lineInfo(p, p.idx-1)
+
 proc setSub(p: var RstParser, key: string, value: PRstNode) =
-  var length = len(p.s.subs)
-  for i in countup(0, length - 1):
+  var length = p.s.subs.len
+  for i in 0 ..< length:
     if key == p.s.subs[i].key:
       p.s.subs[i].value = value
       return
-  setLen(p.s.subs, length + 1)
-  p.s.subs[length].key = key
-  p.s.subs[length].value = value
+  p.s.subs.add(Substitution(key: key, value: value, info: prevLineInfo(p)))
 
-proc setRef(p: var RstParser, key: string, value: PRstNode) =
-  var length = len(p.s.refs)
-  for i in countup(0, length - 1):
+proc setRef(p: var RstParser, key: string, value: PRstNode,
+            refType: SubstitutionKind) =
+  var length = p.s.refs.len
+  for i in 0 ..< length:
     if key == p.s.refs[i].key:
       if p.s.refs[i].value.addNodes != value.addNodes:
         rstMessage(p, mwRedefinitionOfLabel, key)
-
       p.s.refs[i].value = value
       return
-  setLen(p.s.refs, length + 1)
-  p.s.refs[length].key = key
-  p.s.refs[length].value = value
+  p.s.refs.add(Substitution(kind: refType, key: key, value: value,
+                            info: prevLineInfo(p)))
+
+proc findRef(s: PRstSharedState, key: string): seq[Substitution] =
+  for i in countup(0, high(s.refs)):
+    if key == s.refs[i].key:
+      result.add s.refs[i]
+
+# Ambiguity in links: we don't follow procedure of removing implicit targets
+# defined in https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#implicit-hyperlink-targets
+# Instead we just give explicit links a higher priority than to implicit ones
+# and report ambiguities as warnings. Hopefully it is easy to remove
+# ambiguities manually. Nim auto-generated links from ``docgen.nim``
+# have lowest priority: 1 (for procs) and below for other symbol types.
+
+proc refPriority(k: SubstitutionKind): int =
+  case k
+  of rstSubstitution: result = 8
+  of hyperlinkAlias: result = 7
+  of implicitHyperlinkAlias: result = 2
+
+proc internalRefPriority(k: RstAnchorKind): int =
+  case k
+  of manualDirectiveAnchor: result = 6
+  of manualInlineAnchor: result = 5
+  of footnoteAnchor: result = 4
+  of headlineAnchor: result = 3
+
+proc `$`(subst: AnchorSubst): string =  # for debug
+  let s =
+    case subst.kind
+    of arInternalRst: "type=" & $subst.anchorType
+    of arExternalRst: "type=" & $subst.anchorTypeExt
+    of arNim: "langsym=" & $subst.langSym
+  result = "(kind=$1, priority=$2, $3)" % [$subst.kind, $subst.priority, s]
+
+proc addAnchorRst(p: var RstParser, name: string, target: PRstNode,
+                  anchorType: RstAnchorKind) =
+  ## Associates node `target` (which has field `anchor`) with an
+  ## alias `name` and updates the corresponding aliases in `p.curAnchors`.
+  let prio = internalRefPriority(anchorType)
+  for a in p.curAnchors:
+    p.s.anchors.mgetOrPut(a.alias, newSeq[AnchorSubst]()).add(
+        AnchorSubst(kind: arInternalRst, target: target, priority: prio,
+                    info: a.info, anchorType: manualDirectiveAnchor))
+  if name != "":
+    p.s.anchors.mgetOrPut(name, newSeq[AnchorSubst]()).add(
+        AnchorSubst(kind: arInternalRst, target: target, priority: prio,
+                    info: prevLineInfo(p), anchorType: anchorType))
+  p.curAnchors.setLen 0
+
+proc addAnchorExtRst(s: var PRstSharedState, key: string, refn: string,
+                  anchorType: RstAnchorKind, info: TLineInfo) =
+  let name = key.toLowerAscii
+  let prio = internalRefPriority(anchorType)
+  s.anchors.mgetOrPut(name, newSeq[AnchorSubst]()).add(
+      AnchorSubst(kind: arExternalRst, refnameExt: refn, priority: prio,
+                  info: info,
+                  anchorTypeExt: anchorType))
+
+proc addAnchorNim*(s: var PRstSharedState, external: bool, refn: string, tooltip: string,
+                   langSym: LangSymbol, priority: int,
+                   info: TLineInfo, module: FileIndex) =
+  ## Adds an anchor `refn`, which follows
+  ## the rule `arNim` (i.e. a symbol in ``*.nim`` file)
+  s.anchors.mgetOrPut(langSym.name, newSeq[AnchorSubst]()).add(
+      AnchorSubst(kind: arNim, external: external, refname: refn, langSym: langSym,
+                  tooltip: tooltip, priority: priority,
+                  info: info))
+
+proc findMainAnchorNim(s: PRstSharedState, signature: PRstNode,
+                       info: TLineInfo):
+                      seq[AnchorSubst] =
+  var langSym: LangSymbol
+  try:
+    langSym = toLangSymbol(signature)
+  except ValueError:  # parsing failed, not a Nim symbol
+    return
+  let substitutions = s.anchors.getOrDefault(langSym.name,
+                                             newSeq[AnchorSubst]())
+  if substitutions.len == 0:
+    return
+  # logic to select only groups instead of concrete symbols
+  # with overloads, note that the same symbol can be defined
+  # in multiple modules and `importdoc`ed:
+  type GroupKey = tuple[symKind: string, origModule: string]
+  # map (symKind, file) (like "proc", "os.nim") -> found symbols/groups:
+  var found: Table[GroupKey, seq[AnchorSubst]]
+  for subst in substitutions:
+    if subst.kind == arNim:
+      if match(subst.langSym, langSym):
+        let key: GroupKey = (subst.langSym.symKind, getModule(s, subst))
+        found.mgetOrPut(key, newSeq[AnchorSubst]()).add subst
+  for key, sList in found:
+    if sList.len == 1:
+      result.add sList[0]
+    else:  # > 1, there are overloads, potential ambiguity in this `symKind`
+      if langSym.parametersProvided:
+        # there are non-group signatures, select only them
+        for s in sList:
+          if not s.langSym.isGroup:
+            result.add s
+      else:  # when there are many overloads a link like foo_ points to all
+             # of them, so selecting the group
+        var foundGroup = false
+        for s in sList:
+          if s.langSym.isGroup:
+            result.add s
+            foundGroup = true
+            break
+        doAssert(foundGroup,
+                 "docgen has not generated the group for $1 (file $2)" % [
+                 langSym.name, getModule(s, sList[0]) ])
+
+proc findMainAnchorRst(s: PRstSharedState, linkText: string, info: TLineInfo):
+                      seq[AnchorSubst] =
+  let name = linkText.toLowerAscii
+  let substitutions = s.anchors.getOrDefault(name, newSeq[AnchorSubst]())
+  for s in substitutions:
+    if s.kind in {arInternalRst, arExternalRst}:
+      result.add s
+
+proc addFootnoteNumManual(p: var RstParser, num: int) =
+  ## add manually-numbered footnote
+  for fnote in p.s.footnotes:
+    if fnote.number == num:
+      rstMessage(p, mwRedefinitionOfLabel, $num)
+      return
+  p.s.footnotes.add((fnManualNumber, num, -1, -1, $num))
+
+proc addFootnoteNumAuto(p: var RstParser, label: string) =
+  ## add auto-numbered footnote.
+  ## Empty label [#] means it'll be resolved by the occurrence.
+  if label == "":  # simple auto-numbered [#]
+    p.s.lineFootnoteNum.add lineInfo(p)
+    p.s.footnotes.add((fnAutoNumber, -1, p.s.lineFootnoteNum.len, -1, label))
+  else:           # auto-numbered with label [#label]
+    for fnote in p.s.footnotes:
+      if fnote.label == label:
+        rstMessage(p, mwRedefinitionOfLabel, label)
+        return
+    p.s.footnotes.add((fnAutoNumberLabel, -1, -1, -1, label))
+
+proc addFootnoteSymAuto(p: var RstParser) =
+  p.s.lineFootnoteSym.add lineInfo(p)
+  p.s.footnotes.add((fnAutoSymbol, -1, -1, p.s.lineFootnoteSym.len, ""))
+
+proc orderFootnotes(s: PRstSharedState) =
+  ## numerate auto-numbered footnotes taking into account that all
+  ## manually numbered ones always have preference.
+  ## Save the result back to `s.footnotes`.
+
+  # Report an error if found any mismatch in number of automatic footnotes
+  proc listFootnotes(locations: seq[TLineInfo]): string =
+    var lines: seq[string]
+    for info in locations:
+      if s.filenames.len > 1:
+        let file = getFilename(s.filenames, info.fileIndex)
+        lines.add file & ":"
+      else:  # no need to add file name here if there is only 1
+        lines.add ""
+      lines[^1].add $info.line
+    result.add $lines.len & " (lines " & join(lines, ", ") & ")"
+  if s.lineFootnoteNum.len != s.lineFootnoteNumRef.len:
+    rstMessage(s, meFootnoteMismatch,
+      "$1 != $2" % [listFootnotes(s.lineFootnoteNum),
+                    listFootnotes(s.lineFootnoteNumRef)] &
+        " for auto-numbered footnotes")
+  if s.lineFootnoteSym.len != s.lineFootnoteSymRef.len:
+    rstMessage(s, meFootnoteMismatch,
+      "$1 != $2" % [listFootnotes(s.lineFootnoteSym),
+                    listFootnotes(s.lineFootnoteSymRef)] &
+        " for auto-symbol footnotes")
+
+  var result: seq[FootnoteSubst]
+  var manuallyN, autoN, autoSymbol: seq[FootnoteSubst]
+  for fs in s.footnotes:
+    if fs.kind == fnManualNumber: manuallyN.add fs
+    elif fs.kind in {fnAutoNumber, fnAutoNumberLabel}: autoN.add fs
+    else: autoSymbol.add fs
+
+  if autoN.len == 0:
+    result = manuallyN
+  else:
+    # fill gaps between manually numbered footnotes in ascending order
+    manuallyN.sort()  # sort by number - its first field
+    var lst = initSinglyLinkedList[FootnoteSubst]()
+    for elem in manuallyN: lst.append(elem)
+    var firstAuto = 0
+    if lst.head == nil or lst.head.value.number != 1:
+      # no manual footnote [1], start numeration from 1 for auto-numbered
+      lst.prepend (autoN[0].kind, 1, autoN[0].autoNumIdx, -1, autoN[0].label)
+      firstAuto = 1
+    var curNode = lst.head
+    var nextNode: SinglyLinkedNode[FootnoteSubst]
+    # go simultaneously through `autoN` and `lst` looking for gaps
+    for (kind, x, autoNumIdx, y, label) in autoN[firstAuto .. ^1]:
+      while (nextNode = curNode.next; nextNode != nil):
+        if nextNode.value.number - curNode.value.number > 1:
+          # gap found, insert new node `n` between curNode and nextNode:
+          var n = newSinglyLinkedNode((kind, curNode.value.number + 1,
+                                       autoNumIdx, -1, label))
+          curNode.next = n
+          n.next = nextNode
+          curNode = n
+          break
+        else:
+          curNode = nextNode
+      if nextNode == nil:  # no gap found, just append
+        lst.append (kind, curNode.value.number + 1, autoNumIdx, -1, label)
+        curNode = lst.tail
+    result = lst.toSeq
+
+  # we use ASCII symbols instead of those recommended in RST specification:
+  const footnoteAutoSymbols = ["*", "^", "+", "=", "~", "$", "@", "%", "&"]
+  for fs in autoSymbol:
+    # assignment order: *, **, ***, ^, ^^, ^^^, ... &&&, ****, *****, ...
+    let i = fs.autoSymIdx - 1
+    let symbolNum = (i div 3) mod footnoteAutoSymbols.len
+    let nSymbols = (1 + i mod 3) + 3 * (i div (3 * footnoteAutoSymbols.len))
+    let label = footnoteAutoSymbols[symbolNum].repeat(nSymbols)
+    result.add((fs.kind, -1, -1, fs.autoSymIdx, label))
+
+  s.footnotes = result
+
+proc getFootnoteNum(s: PRstSharedState, label: string): int =
+  ## get number from label. Must be called after `orderFootnotes`.
+  result = -1
+  for fnote in s.footnotes:
+    if fnote.label == label:
+      return fnote.number
 
-proc findRef(p: var RstParser, key: string): PRstNode =
-  for i in countup(0, high(p.s.refs)):
-    if key == p.s.refs[i].key:
-      return p.s.refs[i].value
+proc getFootnoteNum(s: PRstSharedState, order: int): int =
+  ## get number from occurrence. Must be called after `orderFootnotes`.
+  result = -1
+  for fnote in s.footnotes:
+    if fnote.autoNumIdx == order:
+      return fnote.number
+
+proc getAutoSymbol(s: PRstSharedState, order: int): string =
+  ## get symbol from occurrence of auto-symbol footnote.
+  result = "???"
+  for fnote in s.footnotes:
+    if fnote.autoSymIdx == order:
+      return fnote.label
+
+proc newRstNodeA(p: var RstParser, kind: RstNodeKind): PRstNode =
+  ## create node and consume the current anchor
+  result = newRstNode(kind)
+  if p.curAnchors.len > 0:
+    result.anchor = p.curAnchors[0].anchor
+    addAnchorRst(p, "", result, manualDirectiveAnchor)
+
+template newLeaf(s: string): PRstNode = newRstLeaf(s)
 
 proc newLeaf(p: var RstParser): PRstNode =
-  result = newRstNode(rnLeaf, p.tok[p.idx].symbol)
+  result = newLeaf(currentTok(p).symbol)
+
+proc validRefnamePunct(x: string): bool =
+  ## https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#reference-names
+  x.len == 1 and x[0] in {'-', '_', '.', ':', '+'}
+
+func getRefnameIdx(p: RstParser, startIdx: int): int =
+  ## Gets last token index of a refname ("word" in RST terminology):
+  ##
+  ##   reference names are single words consisting of alphanumerics plus
+  ##   isolated (no two adjacent) internal hyphens, underscores, periods,
+  ##   colons and plus signs; no whitespace or other characters are allowed.
+  ##
+  ## Refnames are used for:
+  ## - reference names
+  ## - role names
+  ## - directive names
+  ## - footnote labels
+  ##
+  # TODO: use this func in all other relevant places
+  var j = startIdx
+  if p.tok[j].kind == tkWord:
+    inc j
+    while p.tok[j].kind == tkPunct and validRefnamePunct(p.tok[j].symbol) and
+        p.tok[j+1].kind == tkWord:
+      inc j, 2
+  result = j - 1
+
+func getRefname(p: RstParser, startIdx: int): (string, int) =
+  let lastIdx = getRefnameIdx(p, startIdx)
+  result[1] = lastIdx
+  for j in startIdx..lastIdx:
+    result[0].add p.tok[j].symbol
 
 proc getReferenceName(p: var RstParser, endStr: string): PRstNode =
   var res = newRstNode(rnInner)
   while true:
-    case p.tok[p.idx].kind
+    case currentTok(p).kind
     of tkWord, tkOther, tkWhite:
-      add(res, newLeaf(p))
+      res.add(newLeaf(p))
     of tkPunct:
-      if p.tok[p.idx].symbol == endStr:
-        inc(p.idx)
+      if currentTok(p).symbol == endStr:
+        inc p.idx
         break
       else:
-        add(res, newLeaf(p))
+        res.add(newLeaf(p))
     else:
       rstMessage(p, meExpected, endStr)
       break
-    inc(p.idx)
+    inc p.idx
   result = res
 
 proc untilEol(p: var RstParser): PRstNode =
   result = newRstNode(rnInner)
-  while not (p.tok[p.idx].kind in {tkIndent, tkEof}):
-    add(result, newLeaf(p))
-    inc(p.idx)
+  while currentTok(p).kind notin {tkIndent, tkEof}:
+    result.add(newLeaf(p))
+    inc p.idx
 
 proc expect(p: var RstParser, tok: string) =
-  if p.tok[p.idx].symbol == tok: inc(p.idx)
+  if currentTok(p).symbol == tok: inc p.idx
   else: rstMessage(p, meExpected, tok)
 
-proc isInlineMarkupEnd(p: RstParser, markup: string): bool =
-  result = p.tok[p.idx].symbol == markup
-  if not result:
-    return                    # Rule 3:
-  result = not (p.tok[p.idx - 1].kind in {tkIndent, tkWhite})
-  if not result:
-    return                    # Rule 4:
-  result = (p.tok[p.idx + 1].kind in {tkIndent, tkWhite, tkEof}) or
-      (p.tok[p.idx + 1].symbol[0] in
-      {'\'', '\"', ')', ']', '}', '>', '-', '/', '\\', ':', '.', ',', ';', '!',
-       '?', '_'})
-  if not result:
-    return                    # Rule 7:
+proc inlineMarkdownEnd(p: RstParser): bool =
+  result = prevTok(p).kind notin {tkIndent, tkWhite}
+  ## (For a special case of ` we don't allow spaces surrounding it
+  ## unlike original Markdown because this behavior confusing/useless)
+
+proc inlineRstEnd(p: RstParser): bool =
+  # rst rules: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#inline-markup-recognition-rules
+  # Rule 2:
+  result = prevTok(p).kind notin {tkIndent, tkWhite}
+  if not result: return
+  # Rule 7:
+  result = nextTok(p).kind in {tkIndent, tkWhite, tkEof} or
+      nextTok(p).symbol[0] in
+      {'\'', '\"', ')', ']', '}', '>', '-', '/', '\\', ':', '.', ',', ';', '!', '?', '_'}
+
+proc isInlineMarkupEnd(p: RstParser, markup: string, exact: bool): bool =
+  if exact:
+    result = currentTok(p).symbol == markup
+  else:
+    result = currentTok(p).symbol.endsWith markup
+    if (not result) and markup == "``":
+      # check that escaping may have splitted `` to 2 tokens ` and `
+      result = currentTok(p).symbol == "`" and prevTok(p).symbol == "`"
+  if not result: return
+  # surroundings check
+  if markup in ["_", "__"]:
+    result = inlineRstEnd(p)
+  else:
+    if roPreferMarkdown in p.s.options: result = inlineMarkdownEnd(p)
+    else: result = inlineRstEnd(p)
+
+proc rstRuleSurround(p: RstParser): bool =
+  result = true
+  # Rules 4 & 5:
   if p.idx > 0:
-    if (markup != "``") and (p.tok[p.idx - 1].symbol == "\\"):
-      result = false
+    var d: char
+    var c = prevTok(p).symbol[0]
+    case c
+    of '\'', '\"': d = c
+    of '(': d = ')'
+    of '[': d = ']'
+    of '{': d = '}'
+    of '<': d = '>'
+    else: d = '\0'
+    if d != '\0': result = nextTok(p).symbol[0] != d
+
+proc inlineMarkdownStart(p: RstParser): bool =
+  result = nextTok(p).kind notin {tkIndent, tkWhite, tkEof}
+  if not result: return
+  # this rst rule is really nice, let us use it in Markdown mode too.
+  result = rstRuleSurround(p)
+
+proc inlineRstStart(p: RstParser): bool =
+  ## rst rules: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#inline-markup-recognition-rules
+  # Rule 6
+  result = p.idx == 0 or prevTok(p).kind in {tkIndent, tkWhite} or
+      prevTok(p).symbol[0] in {'\'', '\"', '(', '[', '{', '<', '-', '/', ':', '_'}
+  if not result: return
+  # Rule 1:
+  result = nextTok(p).kind notin {tkIndent, tkWhite, tkEof}
+  if not result: return
+  result = rstRuleSurround(p)
 
 proc isInlineMarkupStart(p: RstParser, markup: string): bool =
-  var d: char
-  result = p.tok[p.idx].symbol == markup
-  if not result:
-    return                    # Rule 1:
-  result = (p.idx == 0) or (p.tok[p.idx - 1].kind in {tkIndent, tkWhite}) or
-      (p.tok[p.idx - 1].symbol[0] in
-      {'\'', '\"', '(', '[', '{', '<', '-', '/', ':', '_'})
-  if not result:
-    return                    # Rule 2:
-  result = not (p.tok[p.idx + 1].kind in {tkIndent, tkWhite, tkEof})
-  if not result:
-    return                    # Rule 5 & 7:
-  if p.idx > 0:
-    if p.tok[p.idx - 1].symbol == "\\":
-      result = false
-    else:
-      var c = p.tok[p.idx - 1].symbol[0]
-      case c
-      of '\'', '\"': d = c
-      of '(': d = ')'
-      of '[': d = ']'
-      of '{': d = '}'
-      of '<': d = '>'
-      else: d = '\0'
-      if d != '\0': result = p.tok[p.idx + 1].symbol[0] != d
+  if markup != "_`":
+    result = currentTok(p).symbol == markup
+  else:  # _` is a 2 token case
+    result = currentTok(p).symbol == "_" and nextTok(p).symbol == "`"
+  if not result: return
+  # surroundings check
+  if markup in ["_", "__", "[", "|"]:
+    # Note: we require space/punctuation even before [markdown link](...)
+    result = inlineRstStart(p)
+  else:
+    if roPreferMarkdown in p.s.options: result = inlineMarkdownStart(p)
+    else: result = inlineRstStart(p)
 
 proc match(p: RstParser, start: int, expr: string): bool =
   # regular expressions are:
@@ -514,97 +1202,159 @@ proc match(p: RstParser, start: int, expr: string): bool =
   # ' '              tkWhite
   # 'a'              tkAdornment
   # 'i'              tkIndent
+  # 'I'              tkIndent or tkEof
   # 'p'              tkPunct
   # 'T'              always true
   # 'E'              whitespace, indent or eof
-  # 'e'              tkWord or '#' (for enumeration lists)
+  # 'e'              any enumeration sequence or '#' (for enumeration lists)
+  # 'x'              a..z or '#' (for enumeration lists)
+  # 'n'              0..9 or '#' (for enumeration lists)
   var i = 0
   var j = start
-  var last = len(expr) - 1
+  var last = expr.len - 1
   while i <= last:
     case expr[i]
-    of 'w': result = p.tok[j].kind == tkWord
+    of 'w':
+      let lastIdx = getRefnameIdx(p, j)
+      result = lastIdx >= j
+      if result: j = lastIdx
     of ' ': result = p.tok[j].kind == tkWhite
     of 'i': result = p.tok[j].kind == tkIndent
+    of 'I': result = p.tok[j].kind in {tkIndent, tkEof}
     of 'p': result = p.tok[j].kind == tkPunct
     of 'a': result = p.tok[j].kind == tkAdornment
     of 'o': result = p.tok[j].kind == tkOther
     of 'T': result = true
     of 'E': result = p.tok[j].kind in {tkEof, tkWhite, tkIndent}
-    of 'e':
-      result = (p.tok[j].kind == tkWord) or (p.tok[j].symbol == "#")
+    of 'e', 'x', 'n':
+      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 '0'..'9': result = allCharsInSet(p.tok[j].symbol, {'0'..'9'})
-        else: discard
+        of '#': result = true
+        of 'a'..'z', 'A'..'Z':
+          result = expr[i] in {'e', 'x'} and p.tok[j].symbol.len == 1
+        of '0'..'9':
+          result = expr[i] in {'e', 'n'} and
+                     allCharsInSet(p.tok[j].symbol, {'0'..'9'})
+        else: result = false
     else:
       var c = expr[i]
       var length = 0
-      while (i <= last) and (expr[i] == c):
-        inc(i)
-        inc(length)
-      dec(i)
-      result = (p.tok[j].kind in {tkPunct, tkAdornment}) and
-          (len(p.tok[j].symbol) == length) and (p.tok[j].symbol[0] == c)
+      while i <= last and expr[i] == c:
+        inc i
+        inc length
+      dec i
+      result = p.tok[j].kind in {tkPunct, tkAdornment} and
+          p.tok[j].symbol.len == length and p.tok[j].symbol[0] == c
     if not result: return
-    inc(j)
-    inc(i)
+    inc j
+    inc i
   result = true
 
-proc fixupEmbeddedRef(n, a, b: PRstNode) =
+proc safeProtocol*(linkStr: var string): string =
+  # Returns link's protocol and, if it's not safe, clears `linkStr`
+  result = ""
+  if scanf(linkStr, "$w:", result):
+    # if it has a protocol at all, ensure that it's not 'javascript:' or worse:
+    if cmpIgnoreCase(result, "http") == 0 or
+        cmpIgnoreCase(result, "https") == 0 or
+        cmpIgnoreCase(result, "ftp") == 0:
+      discard "it's fine"
+    else:
+      linkStr = ""
+
+proc fixupEmbeddedRef(p: var RstParser, n, a, b: PRstNode): bool =
+  # Returns `true` if the link belongs to an allowed protocol
   var sep = - 1
-  for i in countdown(len(n) - 2, 0):
+  for i in countdown(n.len - 2, 0):
     if n.sons[i].text == "<":
       sep = i
       break
-  var incr = if (sep > 0) and (n.sons[sep - 1].text[0] == ' '): 2 else: 1
-  for i in countup(0, sep - incr): add(a, n.sons[i])
-  for i in countup(sep + 1, len(n) - 2): add(b, n.sons[i])
+  var incr = if sep > 0 and n.sons[sep - 1].text[0] == ' ': 2 else: 1
+  for i in countup(0, sep - incr): a.add(n.sons[i])
+  var linkStr = ""
+  for i in countup(sep + 1, n.len - 2): linkStr.add(n.sons[i].addNodes)
+  if linkStr != "":
+    let protocol = safeProtocol(linkStr)
+    result = linkStr != ""
+    if not result:
+      rstMessage(p, mwBrokenLink, protocol,
+                 p.tok[p.idx-3].line, p.tok[p.idx-3].col)
+  b.add newLeaf(linkStr)
+
+proc whichRole(p: RstParser, sym: string): RstNodeKind =
+  result = whichRoleAux(sym)
+  if result == rnUnknownRole:
+    rstMessage(p, mwUnsupportedLanguage, sym)
+
+proc toInlineCode(n: PRstNode, language: string): PRstNode =
+  ## Creates rnInlineCode and attaches `n` contents as code (in 3rd son).
+  result = newRstNode(rnInlineCode, info=n.info)
+  let args = newRstNode(rnDirArg)
+  var lang = language
+  if language == "cpp": lang = "c++"
+  elif language == "csharp": lang = "c#"
+  args.add newLeaf(lang)
+  result.add args
+  result.add PRstNode(nil)
+  var lb = newRstNode(rnLiteralBlock)
+  var s: string
+  for i in n.sons:
+    assert i.kind == rnLeaf
+    s.add i.text
+  lb.add newLeaf(s)
+  result.add lb
+
+proc toOtherRole(n: PRstNode, kind: RstNodeKind, roleName: string): PRstNode =
+  let newN = newRstNode(rnInner, n.sons)
+  let newSons = @[newN, newLeaf(roleName)]
+  result = newRstNode(kind, newSons)
 
 proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode =
-  result = n
-  if isInlineMarkupEnd(p, "_") or isInlineMarkupEnd(p, "__"):
-    inc(p.idx)
+  ## Finalizes node `n` that was tentatively determined as interpreted text.
+  var newKind = n.kind
+  var newSons = n.sons
+
+  proc finalizeInterpreted(node: PRstNode, newKind: RstNodeKind,
+                           newSons: seq[PRstNode], roleName: string):
+                          PRstNode {.nimcall.} =
+    # fixes interpreted text (`x` or `y`:role:) to proper internal AST format
+    if newKind in {rnUnknownRole, rnCodeFragment}:
+      result = node.toOtherRole(newKind, roleName)
+    elif newKind == rnInlineCode:
+      result = node.toInlineCode(language=roleName)
+    else:
+      result = newRstNode(newKind, newSons)
+
+  if isInlineMarkupEnd(p, "_", exact=true) or
+      isInlineMarkupEnd(p, "__", exact=true):
+    inc p.idx
     if p.tok[p.idx-2].symbol == "`" and p.tok[p.idx-3].symbol == ">":
       var a = newRstNode(rnInner)
       var b = newRstNode(rnInner)
-      fixupEmbeddedRef(n, a, b)
-      if len(a) == 0:
-        result = newRstNode(rnStandaloneHyperlink)
-        add(result, b)
-      else:
-        result = newRstNode(rnHyperlink)
-        add(result, a)
-        add(result, b)
-        setRef(p, rstnodeToRefname(a), b)
-    elif n.kind == rnInterpretedText:
-      n.kind = rnRef
-    else:
-      result = newRstNode(rnRef)
-      add(result, n)
+      if fixupEmbeddedRef(p, n, a, b):
+        if a.len == 0:  # e.g. `<a_named_relative_link>`_
+          newKind = rnStandaloneHyperlink
+          newSons = @[b]
+        else:  # e.g. `link title <http://site>`_
+          newKind = rnHyperlink
+          newSons = @[a, b]
+          setRef(p, rstnodeToRefname(a), b, implicitHyperlinkAlias)
+      else:  # include as plain text, not a link
+        newKind = rnInner
+        newSons = n.sons
+      result = newRstNode(newKind, newSons)
+    else:  # some link that will be resolved in `resolveSubs`
+      newKind = rnRstRef
+      result = newRstNode(newKind, sons=newSons, info=n.info)
   elif match(p, p.idx, ":w:"):
     # a role:
-    if p.tok[p.idx + 1].symbol == "idx":
-      n.kind = rnIdx
-    elif p.tok[p.idx + 1].symbol == "literal":
-      n.kind = rnInlineLiteral
-    elif p.tok[p.idx + 1].symbol == "strong":
-      n.kind = rnStrongEmphasis
-    elif p.tok[p.idx + 1].symbol == "emphasis":
-      n.kind = rnEmphasis
-    elif (p.tok[p.idx + 1].symbol == "sub") or
-        (p.tok[p.idx + 1].symbol == "subscript"):
-      n.kind = rnSub
-    elif (p.tok[p.idx + 1].symbol == "sup") or
-        (p.tok[p.idx + 1].symbol == "supscript"):
-      n.kind = rnSup
-    else:
-      result = newRstNode(rnGeneralRole)
-      n.kind = rnInner
-      add(result, n)
-      add(result, newRstNode(rnLeaf, p.tok[p.idx + 1].symbol))
-    inc(p.idx, 3)
+    let (roleName, lastIdx) = getRefname(p, p.idx+1)
+    newKind = whichRole(p, roleName)
+    result = n.finalizeInterpreted(newKind, newSons, roleName)
+    p.idx = lastIdx + 2
+  else:
+    result = n.finalizeInterpreted(p.s.currRoleKind, newSons, p.s.currRole)
 
 proc matchVerbatim(p: RstParser, start: int, expr: string): int =
   result = start
@@ -616,7 +1366,7 @@ proc matchVerbatim(p: RstParser, start: int, expr: string): int =
   if j < expr.len: result = 0
 
 proc parseSmiley(p: var RstParser): PRstNode =
-  if p.tok[p.idx].symbol[0] notin SmileyStartChars: return
+  if currentTok(p).symbol[0] notin SmileyStartChars: return
   for key, val in items(Smilies):
     let m = matchVerbatim(p, p.idx, key)
     if m > 0:
@@ -625,277 +1375,683 @@ proc parseSmiley(p: var RstParser): PRstNode =
       result.text = val
       return
 
-when false:
-  const
-    urlChars = {'A'..'Z', 'a'..'z', '0'..'9', ':', '#', '@', '%', '/', ';',
-                 '$', '(', ')', '~', '_', '?', '+', '-', '=', '\\', '.', '&',
-                 '\128'..'\255'}
-
 proc isUrl(p: RstParser, i: int): bool =
-  result = (p.tok[i+1].symbol == ":") and (p.tok[i+2].symbol == "//") and
-    (p.tok[i+3].kind == tkWord) and
-    (p.tok[i].symbol in ["http", "https", "ftp", "telnet", "file"])
-
-proc parseUrl(p: var RstParser, father: PRstNode) =
-  #if p.tok[p.idx].symbol[strStart] == '<':
-  if isUrl(p, p.idx):
-    var n = newRstNode(rnStandaloneHyperlink)
-    while true:
-      case p.tok[p.idx].kind
-      of tkWord, tkAdornment, tkOther: discard
-      of tkPunct:
-        if p.tok[p.idx+1].kind notin {tkWord, tkAdornment, tkOther, tkPunct}:
+  result = p.tok[i+1].symbol == ":" and p.tok[i+2].symbol == "//" and
+    p.tok[i+3].kind == tkWord and
+    p.tok[i].symbol in ["http", "https", "ftp", "telnet", "file"]
+
+proc checkParen(token: Token, parensStack: var seq[char]): bool {.inline.} =
+  ## Returns `true` iff `token` is a closing parenthesis for some
+  ## previous opening parenthesis saved in `parensStack`.
+  ## This is according Markdown balanced parentheses rule
+  ## (https://spec.commonmark.org/0.29/#link-destination)
+  ## to allow links like
+  ## https://en.wikipedia.org/wiki/APL_(programming_language),
+  ## we use it for RST also.
+  result = false
+  if token.kind == tkPunct:
+    let c = token.symbol[0]
+    if c in {'(', '[', '{'}:  # push
+      parensStack.add c
+    elif c in {')', ']', '}'}:  # try pop
+      # a case like ([) inside a link is allowed and [ is also `pop`ed:
+      for i in countdown(parensStack.len - 1, 0):
+        if (parensStack[i] == '(' and c == ')' or
+            parensStack[i] == '[' and c == ']' or
+            parensStack[i] == '{' and c == '}'):
+          parensStack.setLen i
+          result = true
           break
-      else: break
-      add(n, newLeaf(p))
-      inc(p.idx)
-    add(father, n)
+
+proc parseUrl(p: var RstParser): PRstNode =
+  ## https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#standalone-hyperlinks
+  result = newRstNode(rnStandaloneHyperlink)
+  var lastIdx = p.idx
+  var closedParenIdx = p.idx - 1  # for balanced parens rule
+  var parensStack: seq[char]
+  while p.tok[lastIdx].kind in {tkWord, tkPunct, tkOther}:
+    let isClosing = checkParen(p.tok[lastIdx], parensStack)
+    if isClosing:
+      closedParenIdx = lastIdx
+    inc lastIdx
+  dec lastIdx
+  # standalone URL can not end with punctuation in RST
+  while lastIdx > closedParenIdx and p.tok[lastIdx].kind == tkPunct and
+      p.tok[lastIdx].symbol != "/":
+    dec lastIdx
+  var s = ""
+  for i in p.idx .. lastIdx: s.add p.tok[i].symbol
+  result.add s
+  p.idx = lastIdx + 1
+
+proc parseWordOrRef(p: var RstParser, father: PRstNode) =
+  ## Parses a normal word or may be a reference or URL.
+  if nextTok(p).kind != tkPunct:  # <- main path, a normal word
+    father.add newLeaf(p)
+    inc p.idx
+  elif isUrl(p, p.idx):           # URL http://something
+    father.add parseUrl(p)
   else:
-    var n = newLeaf(p)
-    inc(p.idx)
-    if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n)
-    add(father, n)
+    # check for reference (probably, long one like some.ref.with.dots_ )
+    var saveIdx = p.idx
+    var reference: PRstNode = nil
+    inc p.idx
+    while currentTok(p).kind in {tkWord, tkPunct}:
+      if currentTok(p).kind == tkPunct:
+        if isInlineMarkupEnd(p, "_", exact=true):
+          reference = newRstNode(rnRstRef, info=lineInfo(p, saveIdx))
+          break
+        if not validRefnamePunct(currentTok(p).symbol):
+          break
+      inc p.idx
+    if reference != nil:
+      for i in saveIdx..p.idx-1: reference.add newLeaf(p.tok[i].symbol)
+      father.add reference
+      inc p.idx  # skip final _
+    else:  # 1 normal word
+      father.add newLeaf(p.tok[saveIdx].symbol)
+      p.idx = saveIdx + 1
 
 proc parseBackslash(p: var RstParser, father: PRstNode) =
-  assert(p.tok[p.idx].kind == tkPunct)
-  if p.tok[p.idx].symbol == "\\\\":
-    add(father, newRstNode(rnLeaf, "\\"))
-    inc(p.idx)
-  elif p.tok[p.idx].symbol == "\\":
+  assert(currentTok(p).kind == tkPunct)
+  if currentTok(p).symbol == "\\":
     # XXX: Unicode?
-    inc(p.idx)
-    if p.tok[p.idx].kind != tkWhite: add(father, newLeaf(p))
-    if p.tok[p.idx].kind != tkEof: inc(p.idx)
+    inc p.idx
+    if currentTok(p).kind != tkWhite: father.add(newLeaf(p))
+    if currentTok(p).kind != tkEof: inc p.idx
   else:
-    add(father, newLeaf(p))
-    inc(p.idx)
-
-when false:
-  proc parseAdhoc(p: var RstParser, father: PRstNode, verbatim: bool) =
-    if not verbatim and isURL(p, p.idx):
-      var n = newRstNode(rnStandaloneHyperlink)
-      while true:
-        case p.tok[p.idx].kind
-        of tkWord, tkAdornment, tkOther: nil
-        of tkPunct:
-          if p.tok[p.idx+1].kind notin {tkWord, tkAdornment, tkOther, tkPunct}:
-            break
-        else: break
-        add(n, newLeaf(p))
-        inc(p.idx)
-      add(father, n)
-    elif not verbatim and roSupportSmilies in p.sharedState.options:
-      let n = parseSmiley(p)
-      if s != nil:
-        add(father, n)
-    else:
-      var n = newLeaf(p)
-      inc(p.idx)
-      if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n)
-      add(father, n)
+    father.add(newLeaf(p))
+    inc p.idx
 
 proc parseUntil(p: var RstParser, father: PRstNode, postfix: string,
                 interpretBackslash: bool) =
   let
-    line = p.tok[p.idx].line
-    col = p.tok[p.idx].col
+    line = currentTok(p).line
+    col = currentTok(p).col
   inc p.idx
   while true:
-    case p.tok[p.idx].kind
+    case currentTok(p).kind
     of tkPunct:
-      if isInlineMarkupEnd(p, postfix):
-        inc(p.idx)
+      if isInlineMarkupEnd(p, postfix, exact=false):
+        let l = currentTok(p).symbol.len
+        if l > postfix.len:
+          # handle cases like *emphasis with stars****. (It's valid RST!)
+          father.add newLeaf(currentTok(p).symbol[0 ..< l - postfix.len])
+        elif postfix == "``" and currentTok(p).symbol == "`" and
+            prevTok(p).symbol == "`":
+          # handle cases like ``literal\`` - delete ` already added after \
+          father.sons.setLen(father.sons.len - 1)
+        inc p.idx
         break
-      elif interpretBackslash:
-        parseBackslash(p, father)
       else:
-        add(father, newLeaf(p))
-        inc(p.idx)
+        if postfix == "`":
+          if currentTok(p).symbol == "\\":
+            if nextTok(p).symbol == "\\":
+              father.add newLeaf("\\")
+              father.add newLeaf("\\")
+              inc p.idx, 2
+            elif nextTok(p).symbol == "`":  # escape `
+              father.add newLeaf("`")
+              inc p.idx, 2
+            else:
+              father.add newLeaf("\\")
+              inc p.idx
+          else:
+            father.add(newLeaf(p))
+            inc p.idx
+        else:
+          if interpretBackslash:
+            parseBackslash(p, father)
+          else:
+            father.add(newLeaf(p))
+            inc p.idx
     of tkAdornment, tkWord, tkOther:
-      add(father, newLeaf(p))
-      inc(p.idx)
+      father.add(newLeaf(p))
+      inc p.idx
     of tkIndent:
-      add(father, newRstNode(rnLeaf, " "))
-      inc(p.idx)
-      if p.tok[p.idx].kind == tkIndent:
+      father.add newLeaf(" ")
+      inc p.idx
+      if currentTok(p).kind == tkIndent:
         rstMessage(p, meExpected, postfix, line, col)
         break
     of tkWhite:
-      add(father, newRstNode(rnLeaf, " "))
-      inc(p.idx)
+      father.add newLeaf(" ")
+      inc p.idx
     else: rstMessage(p, meExpected, postfix, line, col)
 
+proc parseMarkdownCodeblockFields(p: var RstParser): PRstNode =
+  ## Parses additional (after language string) code block parameters
+  ## in a format *suggested* in the `CommonMark Spec`_ with handling of `"`.
+  if currentTok(p).kind == tkIndent:
+    result = nil
+  else:
+    result = newRstNode(rnFieldList)
+  while currentTok(p).kind notin {tkIndent, tkEof}:
+    if currentTok(p).kind == tkWhite:
+      inc p.idx
+    else:
+      let field = newRstNode(rnField)
+      var fieldName = ""
+      while currentTok(p).kind notin {tkWhite, tkIndent, tkEof} and
+            currentTok(p).symbol != "=":
+        fieldName.add currentTok(p).symbol
+        inc p.idx
+      field.add(newRstNode(rnFieldName, @[newLeaf(fieldName)]))
+      if currentTok(p).kind == tkWhite: inc p.idx
+      let fieldBody = newRstNode(rnFieldBody)
+      if currentTok(p).symbol == "=":
+        inc p.idx
+        if currentTok(p).kind == tkWhite: inc p.idx
+        var fieldValue = ""
+        if currentTok(p).symbol == "\"":
+          while true:
+            fieldValue.add currentTok(p).symbol
+            inc p.idx
+            if currentTok(p).kind == tkEof:
+              rstMessage(p, meExpected, "\"")
+            elif currentTok(p).symbol == "\"":
+              fieldValue.add "\""
+              inc p.idx
+              break
+        else:
+          while currentTok(p).kind notin {tkWhite, tkIndent, tkEof}:
+            fieldValue.add currentTok(p).symbol
+            inc p.idx
+        fieldBody.add newLeaf(fieldValue)
+      field.add(fieldBody)
+      result.add(field)
+
+proc mayLoadFile(p: RstParser, result: var PRstNode) =
+  var filename = strip(getFieldValue(result, "file"),
+                       chars = Whitespace + {'"'})
+  if filename != "":
+    if roSandboxDisabled notin p.s.options:
+      let tok = p.tok[p.idx-2]
+      rstMessage(p, meSandboxedDirective, "file", tok.line, tok.col)
+    var path = p.findRelativeFile(filename)
+    if path == "": rstMessage(p, meCannotOpenFile, filename)
+    var n = newRstNode(rnLiteralBlock)
+    n.add newLeaf(readFile(path))
+    result.sons[2] = n
+
+proc defaultCodeLangNim(p: RstParser, result: var PRstNode) =
+  # Create a field block if the input block didn't have any.
+  if result.sons[1].isNil: result.sons[1] = newRstNode(rnFieldList)
+  assert result.sons[1].kind == rnFieldList
+  # Hook the extra field and specify the Nim language as value.
+  var extraNode = newRstNode(rnField, info=lineInfo(p))
+  extraNode.add(newRstNode(rnFieldName))
+  extraNode.add(newRstNode(rnFieldBody))
+  extraNode.sons[0].add newLeaf("default-language")
+  extraNode.sons[1].add newLeaf("Nim")
+  result.sons[1].add(extraNode)
+
 proc parseMarkdownCodeblock(p: var RstParser): PRstNode =
+  result = newRstNodeA(p, rnCodeBlock)
+  result.sons.setLen(3)
+  let line = curLine(p)
+  let baseCol = currentTok(p).col
+  let baseSym = currentTok(p).symbol  # usually just ```
+  inc p.idx
+  result.info = lineInfo(p)
   var args = newRstNode(rnDirArg)
-  if p.tok[p.idx].kind == tkWord:
-    add(args, newLeaf(p))
-    inc(p.idx)
+  if currentTok(p).kind == tkWord:
+    args.add(newLeaf(p))
+    inc p.idx
+    result.sons[1] = parseMarkdownCodeblockFields(p)
+    mayLoadFile(p, result)
   else:
     args = nil
-  var n = newRstNode(rnLeaf, "")
+  var n = newLeaf("")
+  var isFirstLine = true
   while true:
-    case p.tok[p.idx].kind
-    of tkEof:
-      rstMessage(p, meExpected, "```")
+    if currentTok(p).kind == tkEof:
+      rstMessage(p, meMissingClosing,
+                 "$1 (started at line $2)" % [baseSym, $line])
       break
-    of tkPunct:
-      if p.tok[p.idx].symbol == "```":
-        inc(p.idx)
+    elif nextTok(p).kind in {tkPunct, tkAdornment} and
+         nextTok(p).symbol[0] == baseSym[0] and
+         nextTok(p).symbol.len >= baseSym.len:
+      inc p.idx, 2
+      break
+    elif currentTok(p).kind == tkIndent:
+      if not isFirstLine:
+        n.text.add "\n"
+      if currentTok(p).ival > baseCol:
+        n.text.add " ".repeat(currentTok(p).ival - baseCol)
+      elif currentTok(p).ival < baseCol:
+        rstMessage(p, mwRstStyle,
+                   "unexpected de-indentation in Markdown code block")
+      inc p.idx
+    else:
+      n.text.add(currentTok(p).symbol)
+      inc p.idx
+    isFirstLine = false
+  result.sons[0] = args
+  if result.sons[2] == nil:
+    var lb = newRstNode(rnLiteralBlock)
+    lb.add(n)
+    result.sons[2] = lb
+  if result.sons[0].isNil and roNimFile in p.s.options:
+    defaultCodeLangNim(p, result)
+
+proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool =
+  # Parses Markdown link. If it's Pandoc auto-link then its second
+  # son (target) will be in tokenized format (rnInner with leafs).
+  var desc = newRstNode(rnInner)
+  var i = p.idx
+
+  var parensStack: seq[char]
+  template parse(endToken, dest) =
+    parensStack.setLen 0
+    inc i # skip begin token
+    while true:
+      if p.tok[i].kind == tkEof: return false
+      if p.tok[i].kind == tkIndent and p.tok[i+1].kind == tkIndent:
+        return false
+      let isClosing = checkParen(p.tok[i], parensStack)
+      if p.tok[i].symbol == endToken and not isClosing:
         break
+      let symbol = if p.tok[i].kind == tkIndent: " " else: p.tok[i].symbol
+      when dest is string: dest.add symbol
+      else: dest.add newLeaf(symbol)
+      inc i
+    inc i # skip end token
+
+  parse("]", desc)
+  if p.tok[i].symbol == "(":
+    var link = ""
+    let linkIdx = i + 1
+    parse(")", link)
+    # only commit if we detected no syntax error:
+    let protocol = safeProtocol(link)
+    if link == "":
+      result = false
+      rstMessage(p, mwBrokenLink, protocol,
+                 p.tok[linkIdx].line, p.tok[linkIdx].col)
+    else:
+      let child = newRstNode(rnHyperlink)
+      child.add newLeaf(desc.addNodes)
+      child.add link
+      father.add child
+      p.idx = i
+      result = true
+  elif roPreferMarkdown in p.s.options:
+    # Use Pandoc's implicit_header_references extension
+    var n = newRstNode(rnPandocRef)
+    if p.tok[i].symbol == "[":
+      var link = newRstNode(rnInner)
+      let targetIdx = i + 1
+      parse("]", link)
+      n.add desc
+      if link.len != 0:  # [description][target]
+        n.add link
+        n.info = lineInfo(p, targetIdx)
+      else:              # [description=target][]
+        n.add desc
+        n.info = lineInfo(p, p.idx + 1)
+    else:                # [description=target]
+      n.add desc
+      n.add desc  # target is the same as description
+      n.info = lineInfo(p, p.idx + 1)
+    father.add n
+    p.idx = i
+    result = true
+  else:
+    result = false
+
+proc getRstFootnoteType(label: PRstNode): (FootnoteType, int) =
+  if label.sons.len >= 1 and label.sons[0].kind == rnLeaf and
+      label.sons[0].text == "#":
+    if label.sons.len == 1:
+      result = (fnAutoNumber, -1)
+    else:
+      result = (fnAutoNumberLabel, -1)
+  elif label.len == 1 and label.sons[0].kind == rnLeaf and
+       label.sons[0].text == "*":
+    result = (fnAutoSymbol, -1)
+  elif label.len == 1 and label.sons[0].kind == rnLeaf:
+    try:
+      result = (fnManualNumber, parseInt(label.sons[0].text))
+    except ValueError:
+      result = (fnCitation, -1)
+  else:
+    result = (fnCitation, -1)
+
+proc getMdFootnoteType(label: PRstNode): (FootnoteType, int) =
+  try:
+    result = (fnManualNumber, parseInt(label.sons[0].text))
+  except ValueError:
+    result = (fnAutoNumberLabel, -1)
+
+proc getFootnoteType(s: PRstSharedState, label: PRstNode): (FootnoteType, int) =
+  ## Returns footnote/citation type and manual number (if present).
+  if isMd(s): getMdFootnoteType(label)
+  else: getRstFootnoteType(label)
+
+proc parseRstFootnoteName(p: var RstParser, reference: bool): PRstNode =
+  ## parse footnote/citation label. Precondition: start at `[`.
+  ## Label text should be valid ref. name symbol, otherwise nil is returned.
+  var i = p.idx + 1
+  result = newRstNode(rnInner)
+  while true:
+    if p.tok[i].kind in {tkEof, tkIndent, tkWhite}:
+      return nil
+    if p.tok[i].kind == tkPunct:
+      case p.tok[i].symbol:
+      of "]":
+        if i > p.idx + 1 and (not reference or (p.tok[i+1].kind == tkPunct and p.tok[i+1].symbol == "_")):
+          inc i                # skip ]
+          if reference: inc i  # skip _
+          break  # to succeed, it's a footnote/citation indeed
+        else:
+          return nil
+      of "#":
+        if i != p.idx + 1:
+          return nil
+      of "*":
+        if i != p.idx + 1 and p.tok[i].kind != tkPunct and p.tok[i+1].symbol != "]":
+          return nil
+      else:
+        if not validRefnamePunct(p.tok[i].symbol):
+          return nil
+    result.add newLeaf(p.tok[i].symbol)
+    inc i
+  p.idx = i
+
+proc isMdFootnoteName(p: RstParser, reference: bool): bool =
+  ## Pandoc Markdown footnote extension.
+  let j = p.idx
+  result = p.tok[j].symbol == "[" and p.tok[j+1].symbol == "^" and
+           p.tok[j+2].kind == tkWord
+
+proc parseMdFootnoteName(p: var RstParser, reference: bool): PRstNode =
+  if isMdFootnoteName(p, reference):
+    result = newRstNode(rnInner)
+    var j = p.idx + 2
+    while p.tok[j].kind in {tkWord, tkOther} or
+        validRefnamePunct(p.tok[j].symbol):
+      result.add newLeaf(p.tok[j].symbol)
+      inc j
+    if j == p.idx + 2:
+      return nil
+    if p.tok[j].symbol == "]":
+      if reference:
+        p.idx = j + 1  # skip ]
       else:
-        add(n.text, p.tok[p.idx].symbol)
-        inc(p.idx)
+        if p.tok[j+1].symbol == ":":
+          p.idx = j + 2  # skip ]:
+        else:
+          result = nil
     else:
-      add(n.text, p.tok[p.idx].symbol)
-      inc(p.idx)
-  var lb = newRstNode(rnLiteralBlock)
-  add(lb, n)
-  result = newRstNode(rnCodeBlock)
-  add(result, args)
-  add(result, nil)
-  add(result, lb)
+      result = nil
+  else:
+    result = nil
+
+proc parseFootnoteName(p: var RstParser, reference: bool): PRstNode =
+  if isMd(p): parseMdFootnoteName(p, reference)
+  else:
+    if isInlineMarkupStart(p, "["): parseRstFootnoteName(p, reference)
+    else: nil
+
+proc isMarkdownCodeBlock(p: RstParser, idx: int): bool =
+  let tok = p.tok[idx]
+  template allowedSymbol: bool =
+    (tok.symbol[0] == '`' or
+      roPreferMarkdown in p.s.options and tok.symbol[0] == '~')
+  result = (roSupportMarkdown in p.s.options and
+            tok.kind in {tkPunct, tkAdornment} and
+            allowedSymbol and
+            tok.symbol.len >= 3)
+
+proc isMarkdownCodeBlock(p: RstParser): bool =
+  isMarkdownCodeBlock(p, p.idx)
 
 proc parseInline(p: var RstParser, father: PRstNode) =
-  case p.tok[p.idx].kind
+  var n: PRstNode  # to be used in `if` condition
+  let saveIdx = p.idx
+  case currentTok(p).kind
   of tkPunct:
     if isInlineMarkupStart(p, "***"):
       var n = newRstNode(rnTripleEmphasis)
       parseUntil(p, n, "***", true)
-      add(father, n)
+      father.add(n)
     elif isInlineMarkupStart(p, "**"):
       var n = newRstNode(rnStrongEmphasis)
       parseUntil(p, n, "**", true)
-      add(father, n)
+      father.add(n)
     elif isInlineMarkupStart(p, "*"):
       var n = newRstNode(rnEmphasis)
       parseUntil(p, n, "*", true)
-      add(father, n)
-    elif roSupportMarkdown in p.s.options and p.tok[p.idx].symbol == "```":
-      inc(p.idx)
-      add(father, parseMarkdownCodeblock(p))
+      father.add(n)
+    elif isInlineMarkupStart(p, "_`"):
+      var n = newRstNode(rnInlineTarget)
+      inc p.idx
+      parseUntil(p, n, "`", false)
+      n.anchor = rstnodeToRefname(n)
+      addAnchorRst(p, name = linkName(n), target = n,
+                   anchorType=manualInlineAnchor)
+      father.add(n)
+    elif isMarkdownCodeBlock(p):
+      father.add(parseMarkdownCodeblock(p))
     elif isInlineMarkupStart(p, "``"):
       var n = newRstNode(rnInlineLiteral)
       parseUntil(p, n, "``", false)
-      add(father, n)
+      father.add(n)
+    elif match(p, p.idx, ":w:") and
+        (var lastIdx = getRefnameIdx(p, p.idx + 1);
+         p.tok[lastIdx+2].symbol == "`"):
+      let (roleName, _) = getRefname(p, p.idx+1)
+      let k = whichRole(p, roleName)
+      var n = newRstNode(k)
+      p.idx = lastIdx + 2
+      if k == rnInlineCode:
+        n = n.toInlineCode(language=roleName)
+      parseUntil(p, n, "`", false) # bug #17260
+      if k in {rnUnknownRole, rnCodeFragment}:
+        n = n.toOtherRole(k, roleName)
+      father.add(n)
     elif isInlineMarkupStart(p, "`"):
-      var n = newRstNode(rnInterpretedText)
-      parseUntil(p, n, "`", true)
+      var n = newRstNode(rnInterpretedText, info=lineInfo(p, p.idx+1))
+      parseUntil(p, n, "`", false) # bug #17260
       n = parsePostfix(p, n)
-      add(father, n)
+      father.add(n)
     elif isInlineMarkupStart(p, "|"):
-      var n = newRstNode(rnSubstitutionReferences)
+      var n = newRstNode(rnSubstitutionReferences, info=lineInfo(p, p.idx+1))
       parseUntil(p, n, "|", false)
-      add(father, n)
+      father.add(n)
+    elif currentTok(p).symbol == "[" and nextTok(p).symbol != "[" and
+         (n = parseFootnoteName(p, reference=true); n != nil):
+      var nn = newRstNode(rnFootnoteRef)
+      nn.info = lineInfo(p, saveIdx+1)
+      nn.add n
+      let (fnType, _) = getFootnoteType(p.s, n)
+      case fnType
+      of fnAutoSymbol:
+        p.s.lineFootnoteSymRef.add lineInfo(p)
+      of fnAutoNumber:
+        p.s.lineFootnoteNumRef.add lineInfo(p)
+      else: discard
+      father.add(nn)
+    elif roSupportMarkdown in p.s.options and
+        currentTok(p).symbol == "[" and nextTok(p).symbol != "[" and
+        parseMarkdownLink(p, father):
+      discard "parseMarkdownLink already processed it"
     else:
       if roSupportSmilies in p.s.options:
         let n = parseSmiley(p)
         if n != nil:
-          add(father, n)
+          father.add(n)
           return
       parseBackslash(p, father)
   of tkWord:
     if roSupportSmilies in p.s.options:
       let n = parseSmiley(p)
       if n != nil:
-        add(father, n)
+        father.add(n)
         return
-    parseUrl(p, father)
+    parseWordOrRef(p, father)
   of tkAdornment, tkOther, tkWhite:
+    if isMarkdownCodeBlock(p):
+      father.add(parseMarkdownCodeblock(p))
+      return
     if roSupportSmilies in p.s.options:
       let n = parseSmiley(p)
       if n != nil:
-        add(father, n)
+        father.add(n)
         return
-    add(father, newLeaf(p))
-    inc(p.idx)
+    father.add(newLeaf(p))
+    inc p.idx
   else: discard
 
 proc getDirective(p: var RstParser): string =
-  if p.tok[p.idx].kind == tkWhite and p.tok[p.idx+1].kind == tkWord:
-    var j = p.idx
-    inc(p.idx)
-    result = p.tok[p.idx].symbol
-    inc(p.idx)
-    while p.tok[p.idx].kind in {tkWord, tkPunct, tkAdornment, tkOther}:
-      if p.tok[p.idx].symbol == "::": break
-      add(result, p.tok[p.idx].symbol)
-      inc(p.idx)
-    if p.tok[p.idx].kind == tkWhite: inc(p.idx)
-    if p.tok[p.idx].symbol == "::":
-      inc(p.idx)
-      if (p.tok[p.idx].kind == tkWhite): inc(p.idx)
-    else:
-      p.idx = j               # set back
-      result = ""             # error
-  else:
-    result = ""
-
-proc parseComment(p: var RstParser): PRstNode =
-  case p.tok[p.idx].kind
-  of tkIndent, tkEof:
-    if p.tok[p.idx].kind != tkEof and p.tok[p.idx + 1].kind == tkIndent:
-      inc(p.idx)              # empty comment
-    else:
-      var indent = p.tok[p.idx].ival
-      while true:
-        case p.tok[p.idx].kind
-        of tkEof:
-          break
-        of tkIndent:
-          if (p.tok[p.idx].ival < indent): break
-        else:
-          discard
-        inc(p.idx)
+  result = ""
+  if currentTok(p).kind == tkWhite:
+    let (name, lastIdx) = getRefname(p, p.idx + 1)
+    let afterIdx = lastIdx + 1
+    if name.len > 0:
+      if p.tok[afterIdx].symbol == "::":
+        result = name
+        p.idx = afterIdx + 1
+        if currentTok(p).kind == tkWhite:
+          inc p.idx
+        elif currentTok(p).kind != tkIndent:
+          rstMessage(p, mwRstStyle,
+              "whitespace or newline expected after directive " & name)
+        result = result.toLowerAscii()
+      elif p.tok[afterIdx].symbol == ":":
+        rstMessage(p, mwRstStyle,
+            "double colon :: may be missing at end of '" & name & "'",
+            p.tok[afterIdx].line, p.tok[afterIdx].col)
+      elif p.tok[afterIdx].kind == tkPunct and p.tok[afterIdx].symbol[0] == ':':
+        rstMessage(p, mwRstStyle,
+            "too many colons for a directive (should be ::)",
+            p.tok[afterIdx].line, p.tok[afterIdx].col)
+
+proc parseComment(p: var RstParser, col: int): PRstNode =
+  if currentTok(p).kind != tkEof and nextTok(p).kind == tkIndent:
+    inc p.idx              # empty comment
   else:
-    while p.tok[p.idx].kind notin {tkIndent, tkEof}: inc(p.idx)
+    while currentTok(p).kind != tkEof:
+      if currentTok(p).kind == tkIndent and currentTok(p).ival > col or
+         currentTok(p).kind != tkIndent and currentTok(p).col > col:
+        inc p.idx
+      else:
+        break
   result = nil
 
-type
-  DirKind = enum             # must be ordered alphabetically!
-    dkNone, dkAuthor, dkAuthors, dkCode, dkCodeBlock, dkContainer, dkContents,
-    dkFigure, dkImage, dkInclude, dkIndex, dkRaw, dkTitle
-
-const
-  DirIds: array[0..12, string] = ["", "author", "authors", "code",
-    "code-block", "container", "contents", "figure", "image", "include",
-    "index", "raw", "title"]
-
-proc getDirKind(s: string): DirKind =
-  let i = find(DirIds, s)
-  if i >= 0: result = DirKind(i)
-  else: result = dkNone
-
 proc parseLine(p: var RstParser, father: PRstNode) =
   while true:
-    case p.tok[p.idx].kind
+    case currentTok(p).kind
     of tkWhite, tkWord, tkOther, tkPunct: parseInline(p, father)
     else: break
 
 proc parseUntilNewline(p: var RstParser, father: PRstNode) =
   while true:
-    case p.tok[p.idx].kind
+    case currentTok(p).kind
     of tkWhite, tkWord, tkAdornment, tkOther, tkPunct: parseInline(p, father)
     of tkEof, tkIndent: break
 
 proc parseSection(p: var RstParser, result: PRstNode) {.gcsafe.}
+
+proc tokenAfterNewline(p: RstParser, start: int): int =
+  result = start
+  while true:
+    case p.tok[result].kind
+    of tkEof:
+      break
+    of tkIndent:
+      inc result
+      break
+    else: inc result
+
+proc tokenAfterNewline(p: RstParser): int {.inline.} =
+  result = tokenAfterNewline(p, p.idx)
+
+proc getWrappableIndent(p: RstParser): int =
+  ## Gets baseline indentation for bodies of field lists and directives.
+  ## Handles situations like this (with possible de-indent in [case.3])::
+  ##
+  ##   :field:   definition                                          [case.1]
+  ##
+  ##   currInd   currentTok(p).col
+  ##   |         |
+  ##   v         v
+  ##
+  ##   .. Note:: defItem:                                            [case.2]
+  ##                 definition
+  ##
+  ##                 ^
+  ##                 |
+  ##                 nextIndent
+  ##
+  ##   .. Note:: - point1                                            [case.3]
+  ##       - point 2
+  ##
+  ##       ^
+  ##       |
+  ##       nextIndent
+  if currentTok(p).kind == tkIndent:
+    result = currentTok(p).ival
+  else:
+    var nextIndent = p.tok[tokenAfterNewline(p)-1].ival
+    if nextIndent <= currInd(p):          # parse only this line     [case.1]
+      result = currentTok(p).col
+    elif nextIndent >= currentTok(p).col: # may be a definition list [case.2]
+      result = currentTok(p).col
+    else:
+      result = nextIndent                 # allow parsing next lines [case.3]
+
+proc getMdBlockIndent(p: RstParser): int =
+  ## Markdown version of `getWrappableIndent`.
+  if currentTok(p).kind == tkIndent:
+    result = currentTok(p).ival
+  else:
+    var nextIndent = p.tok[tokenAfterNewline(p)-1].ival
+    # TODO: Markdown-compliant definition should allow nextIndent == currInd(p):
+    if nextIndent <= currInd(p):           # parse only this line
+      result = currentTok(p).col
+    else:
+      result = nextIndent                 # allow parsing next lines [case.3]
+
+proc indFollows(p: RstParser): bool =
+  result = currentTok(p).kind == tkIndent and currentTok(p).ival > currInd(p)
+
+proc parseBlockContent(p: var RstParser, father: var PRstNode,
+                       contentParser: SectionParser): bool {.gcsafe.} =
+  ## parse the final content part of explicit markup blocks (directives,
+  ## footnotes, etc). Returns true if succeeded.
+  if currentTok(p).kind != tkIndent or indFollows(p):
+    let blockIndent = getWrappableIndent(p)
+    pushInd(p, blockIndent)
+    let content = contentParser(p)
+    popInd(p)
+    father.add content
+    result = true
+
+proc parseSectionWrapper(p: var RstParser): PRstNode =
+  result = newRstNode(rnInner)
+  parseSection(p, result)
+  while result.kind == rnInner and result.len == 1:
+    result = result.sons[0]
+
 proc parseField(p: var RstParser): PRstNode =
   ## Returns a parsed rnField node.
   ##
   ## rnField nodes have two children nodes, a rnFieldName and a rnFieldBody.
-  result = newRstNode(rnField)
-  var col = p.tok[p.idx].col
+  result = newRstNode(rnField, info=lineInfo(p))
+  var col = currentTok(p).col
   var fieldname = newRstNode(rnFieldName)
   parseUntil(p, fieldname, ":", false)
   var fieldbody = newRstNode(rnFieldBody)
-  if p.tok[p.idx].kind != tkIndent: parseLine(p, fieldbody)
-  if p.tok[p.idx].kind == tkIndent:
-    var indent = p.tok[p.idx].ival
-    if indent > col:
-      pushInd(p, indent)
-      parseSection(p, fieldbody)
-      popInd(p)
-  add(result, fieldname)
-  add(result, fieldbody)
+  if currentTok(p).kind == tkWhite: inc p.idx
+  let indent = getWrappableIndent(p)
+  if indent > col:
+    pushInd(p, indent)
+    parseSection(p, fieldbody)
+    popInd(p)
+  result.add(fieldname)
+  result.add(fieldbody)
 
 proc parseFields(p: var RstParser): PRstNode =
   ## Parses fields for a section or directive block.
@@ -904,16 +2060,16 @@ proc parseFields(p: var RstParser): PRstNode =
   ## otherwise it will return a node of rnFieldList type with children.
   result = nil
   var atStart = p.idx == 0 and p.tok[0].symbol == ":"
-  if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx + 1].symbol == ":") or
+  if currentTok(p).kind == tkIndent and nextTok(p).symbol == ":" or
       atStart:
-    var col = if atStart: p.tok[p.idx].col else: p.tok[p.idx].ival
-    result = newRstNode(rnFieldList)
-    if not atStart: inc(p.idx)
+    var col = if atStart: currentTok(p).col else: currentTok(p).ival
+    result = newRstNodeA(p, rnFieldList)
+    if not atStart: inc p.idx
     while true:
-      add(result, parseField(p))
-      if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and
-          (p.tok[p.idx + 1].symbol == ":"):
-        inc(p.idx)
+      result.add(parseField(p))
+      if currentTok(p).kind == tkIndent and currentTok(p).ival == col and
+          nextTok(p).symbol == ":":
+        inc p.idx
       else:
         break
 
@@ -930,13 +2086,12 @@ proc getFieldValue*(n: PRstNode): string =
   result = addNodes(n.sons[1]).strip
 
 proc getFieldValue(n: PRstNode, fieldname: string): string =
-  result = ""
   if n.sons[1] == nil: return
-  if (n.sons[1].kind != rnFieldList):
+  if n.sons[1].kind != rnFieldList:
     #InternalError("getFieldValue (2): " & $n.sons[1].kind)
     # We don't like internal errors here anymore as that would break the forum!
     return
-  for i in countup(0, len(n.sons[1]) - 1):
+  for i in 0 ..< n.sons[1].len:
     var f = n.sons[1].sons[i]
     if cmpIgnoreStyle(addNodes(f.sons[0]), fieldname) == 0:
       result = addNodes(f.sons[1])
@@ -949,153 +2104,462 @@ proc getArgument(n: PRstNode): string =
 
 proc parseDotDot(p: var RstParser): PRstNode {.gcsafe.}
 proc parseLiteralBlock(p: var RstParser): PRstNode =
-  result = newRstNode(rnLiteralBlock)
-  var n = newRstNode(rnLeaf, "")
-  if p.tok[p.idx].kind == tkIndent:
-    var indent = p.tok[p.idx].ival
-    inc(p.idx)
+  result = newRstNodeA(p, rnLiteralBlock)
+  var n = newLeaf("")
+  if currentTok(p).kind == tkIndent:
+    var indent = currentTok(p).ival
+    while currentTok(p).kind == tkIndent: inc p.idx  # skip blank lines
     while true:
-      case p.tok[p.idx].kind
+      case currentTok(p).kind
       of tkEof:
         break
       of tkIndent:
-        if (p.tok[p.idx].ival < indent):
+        if currentTok(p).ival < indent:
           break
         else:
-          add(n.text, "\n")
-          add(n.text, spaces(p.tok[p.idx].ival - indent))
-          inc(p.idx)
+          n.text.add("\n")
+          n.text.add(spaces(currentTok(p).ival - indent))
+          inc p.idx
       else:
-        add(n.text, p.tok[p.idx].symbol)
-        inc(p.idx)
+        n.text.add(currentTok(p).symbol)
+        inc p.idx
   else:
-    while not (p.tok[p.idx].kind in {tkIndent, tkEof}):
-      add(n.text, p.tok[p.idx].symbol)
-      inc(p.idx)
-  add(result, n)
-
-proc getLevel(map: var LevelMap, lvl: var int, c: char): int =
-  if map[c] == 0:
-    inc(lvl)
-    map[c] = lvl
-  result = map[c]
-
-proc tokenAfterNewline(p: RstParser): int =
-  result = p.idx
-  while true:
-    case p.tok[result].kind
-    of tkEof:
-      break
-    of tkIndent:
-      inc(result)
-      break
-    else: inc(result)
+    while currentTok(p).kind notin {tkIndent, tkEof}:
+      n.text.add(currentTok(p).symbol)
+      inc p.idx
+  result.add(n)
+
+proc parseQuotedLiteralBlock(p: var RstParser): PRstNode =
+  result = newRstNodeA(p, rnLiteralBlock)
+  var n = newLeaf("")
+  if currentTok(p).kind == tkIndent:
+    var indent = currInd(p)
+    while currentTok(p).kind == tkIndent: inc p.idx  # skip blank lines
+    var quoteSym = currentTok(p).symbol[0]
+    while true:
+      case currentTok(p).kind
+      of tkEof:
+        break
+      of tkIndent:
+        if currentTok(p).ival < indent:
+          break
+        elif currentTok(p).ival == indent:
+          if nextTok(p).kind == tkPunct and nextTok(p).symbol[0] == quoteSym:
+            n.text.add("\n")
+            inc p.idx
+          elif nextTok(p).kind == tkIndent:
+            break
+          else:
+            rstMessage(p, mwRstStyle, "no newline after quoted literal block")
+            break
+        else:
+          rstMessage(p, mwRstStyle,
+                     "unexpected indentation in quoted literal block")
+          break
+      else:
+        n.text.add(currentTok(p).symbol)
+        inc p.idx
+  result.add(n)
+
+proc parseRstLiteralBlock(p: var RstParser, kind: LiteralBlockKind): PRstNode =
+  if kind == lbIndentedLiteralBlock:
+    result = parseLiteralBlock(p)
+  else:
+    result = parseQuotedLiteralBlock(p)
+
+proc getLevel(p: var RstParser, c: char, hasOverline: bool): int =
+  ## Returns (preliminary) heading level corresponding to `c` and
+  ## `hasOverline`. If level does not exist, add it first.
+  for i, hType in p.s.hLevels:
+    if hType.symbol == c and hType.hasOverline == hasOverline:
+      p.s.hLevels[i].line = curLine(p)
+      p.s.hLevels[i].hasPeers = true
+      return i
+  p.s.hLevels.add LevelInfo(symbol: c, hasOverline: hasOverline,
+                            line: curLine(p), hasPeers: false)
+  result = p.s.hLevels.len - 1
+
+proc countTitles(s: PRstSharedState, n: PRstNode) =
+  ## Fill `s.hTitleCnt`
+  if n == nil: return
+  for node in n.sons:
+    if node != nil:
+      if node.kind notin {rnOverline, rnSubstitutionDef, rnDefaultRole}:
+        break
+      if node.kind == rnOverline:
+        if s.hLevels[s.hTitleCnt].hasPeers:
+          break
+        inc s.hTitleCnt
+        if s.hTitleCnt >= 2:
+          break
+
+proc isAdornmentHeadline(p: RstParser, adornmentIdx: int): bool =
+  ## check that underline/overline length is enough for the heading.
+  ## No support for Unicode.
+  if p.tok[adornmentIdx].symbol in ["::", "..", "|"]:
+    return false
+  if isMarkdownCodeBlock(p, adornmentIdx):
+    return false
+  var headlineLen = 0
+  var failure = ""
+  if p.idx < adornmentIdx:  # check for underline
+    if p.idx > 0:
+      headlineLen = currentTok(p).col - p.tok[adornmentIdx].col
+    if headlineLen > 0:
+      rstMessage(p, mwRstStyle, "indentation of heading text allowed" &
+          " only for overline titles")
+    for i in p.idx ..< adornmentIdx-1:  # adornmentIdx-1 is a linebreak
+      headlineLen += p.tok[i].symbol.len
+    result = p.tok[adornmentIdx].symbol.len >= headlineLen and headlineLen != 0
+    if not result:
+      failure = "(underline '" & p.tok[adornmentIdx].symbol & "' is too short)"
+  else:  # p.idx == adornmentIdx, at overline. Check overline and underline
+    var i = p.idx + 2
+    headlineLen = p.tok[i].col - p.tok[adornmentIdx].col
+    while p.tok[i].kind notin {tkEof, tkIndent}:
+      headlineLen += p.tok[i].symbol.len
+      inc i
+    if p.tok[i].kind == tkIndent and
+       p.tok[i+1].kind == tkAdornment and
+       p.tok[i+1].symbol[0] == p.tok[adornmentIdx].symbol[0]:
+      result = p.tok[adornmentIdx].symbol.len >= headlineLen and
+           headlineLen != 0
+      if result:
+        result = p.tok[i+1].symbol == p.tok[adornmentIdx].symbol
+        if not result:
+          failure = "(underline '" & p.tok[i+1].symbol & "' does not match " &
+              "overline '" & p.tok[adornmentIdx].symbol & "')"
+      else:
+        failure = "(overline '" & p.tok[adornmentIdx].symbol & "' is too short)"
+    else:  # it's not overline/underline section, not reporting error
+      return false
+  if not result:
+    rstMessage(p, meNewSectionExpected, failure)
 
 proc isLineBlock(p: RstParser): bool =
   var j = tokenAfterNewline(p)
-  result = (p.tok[p.idx].col == p.tok[j].col) and (p.tok[j].symbol == "|") or
-      (p.tok[j].col > p.tok[p.idx].col)
+  result = currentTok(p).col == p.tok[j].col and p.tok[j].symbol == "|" or
+      p.tok[j].col > currentTok(p).col or
+      p.tok[j].symbol == "\n"
+
+proc isMarkdownBlockQuote(p: RstParser): bool =
+  result = currentTok(p).symbol[0] == '>'
+
+proc whichRstLiteralBlock(p: RstParser): LiteralBlockKind =
+  ## Checks that the following tokens are either Indented Literal Block or
+  ## Quoted Literal Block (which is not quite the same as Markdown quote block).
+  ## https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#quoted-literal-blocks
+  if currentTok(p).symbol == "::" and nextTok(p).kind == tkIndent:
+    if currInd(p) > nextTok(p).ival:
+      result = lbNone
+    if currInd(p) < nextTok(p).ival:
+      result = lbIndentedLiteralBlock
+    elif currInd(p) == nextTok(p).ival:
+      var i = p.idx + 1
+      while p.tok[i].kind == tkIndent: inc i
+      const validQuotingCharacters = {
+          '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-',
+          '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^',
+          '_', '`', '{', '|', '}', '~'}
+      if p.tok[i].kind in {tkPunct, tkAdornment} and
+          p.tok[i].symbol[0] in validQuotingCharacters:
+        result = lbQuotedLiteralBlock
+      else:
+        result = lbNone
+  else:
+    result = lbNone
 
 proc predNL(p: RstParser): bool =
   result = true
   if p.idx > 0:
-    result = p.tok[p.idx-1].kind == tkIndent and
-        p.tok[p.idx-1].ival == currInd(p)
+    result = prevTok(p).kind == tkIndent and
+        prevTok(p).ival == currInd(p)
 
 proc isDefList(p: RstParser): bool =
   var j = tokenAfterNewline(p)
-  result = (p.tok[p.idx].col < p.tok[j].col) and
-      (p.tok[j].kind in {tkWord, tkOther, tkPunct}) and
-      (p.tok[j - 2].symbol != "::")
+  result = currentTok(p).col < p.tok[j].col and
+      p.tok[j].kind in {tkWord, tkOther, tkPunct} and
+      p.tok[j - 2].symbol != "::"
+
+proc `$`(t: Token): string =  # for debugging only
+  result = "(" & $t.kind & " line=" & $t.line & " col=" & $t.col
+  if t.kind == tkIndent: result = result & " ival=" & $t.ival & ")"
+  else: result = result & " symbol=" & t.symbol & ")"
+
+proc skipNewlines(p: RstParser, j: int): int =
+  result = j
+  while p.tok[result].kind != tkEof and p.tok[result].kind == tkIndent:
+    inc result  # skip blank lines
+
+proc skipNewlines(p: var RstParser) =
+  p.idx = skipNewlines(p, p.idx)
+
+const maxMdRelInd = 3  ## In Markdown: maximum indentation that does not yet
+                       ## make the indented block a code
+
+proc isMdRelInd(outerInd, nestedInd: int): bool =
+  result = outerInd <= nestedInd and nestedInd <= outerInd + maxMdRelInd
+
+proc isMdDefBody(p: RstParser, j: int, termCol: int): bool =
+  let defCol = p.tok[j].col
+  result = p.tok[j].symbol == ":" and
+    isMdRelInd(termCol, defCol) and
+    p.tok[j+1].kind == tkWhite and
+    p.tok[j+2].kind in {tkWord, tkOther, tkPunct}
+
+proc isMdDefListItem(p: RstParser, idx: int): bool =
+  var j = tokenAfterNewline(p, idx)
+  j = skipNewlines(p, j)
+  let termCol = p.tok[j].col
+  result = isMdRelInd(currInd(p), termCol) and
+      isMdDefBody(p, j, termCol)
 
 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(currentTok(p).symbol) and nextTok(p).kind == tkWhite:
+      if p.tok[p.idx+2].kind in {tkWord, tkOther, tkPunct}:
+        result = true
+
+proc findPipe(p: RstParser, start: int): bool =
+  var i = start
+  while true:
+    if p.tok[i].symbol == "|": return true
+    if p.tok[i].kind in {tkIndent, tkEof}: return false
+    inc i
+
 proc whichSection(p: RstParser): RstNodeKind =
-  case p.tok[p.idx].kind
+  if currentTok(p).kind in {tkAdornment, tkPunct}:
+    # for punctuation sequences that can be both tkAdornment and tkPunct
+    if isMarkdownCodeBlock(p):
+      return rnCodeBlock
+    elif isRst(p) and currentTok(p).symbol == "::":
+      return rnLiteralBlock
+    elif currentTok(p).symbol == ".."  and
+       nextTok(p).kind in {tkWhite, tkIndent}:
+     return rnDirective
+  case currentTok(p).kind
   of tkAdornment:
-    if match(p, p.idx + 1, "ii"): result = rnTransition
+    if match(p, p.idx + 1, "iI") and currentTok(p).symbol.len >= 4:
+      result = rnTransition
+    elif match(p, p.idx, "+a+"):
+      result = rnGridTable
+      rstMessage(p, meGridTableNotImplemented)
     elif match(p, p.idx + 1, " a"): result = rnTable
-    elif match(p, p.idx + 1, "i"): result = rnOverline
-    else: result = rnLeaf
+    elif currentTok(p).symbol == "|" and isLineBlock(p):
+      result = rnLineBlock
+    elif roSupportMarkdown in p.s.options and isMarkdownBlockQuote(p):
+      result = rnMarkdownBlockQuote
+    elif (match(p, p.idx + 1, "i") and not match(p, p.idx + 2, "I")) and
+         isAdornmentHeadline(p, p.idx):
+      result = rnOverline
+    else:
+      result = rnParagraph
   of tkPunct:
-    if match(p, tokenAfterNewline(p), "ai"):
+    if isMarkdownHeadline(p):
+      result = rnMarkdownHeadline
+    elif roSupportMarkdown in p.s.options and predNL(p) and
+        match(p, p.idx, "| w") and findPipe(p, p.idx+3):
+      result = rnMarkdownTable
+    elif isMd(p) and isMdFootnoteName(p, reference=false):
+      result = rnFootnote
+    elif currentTok(p).symbol == "|" and isLineBlock(p):
+      result = rnLineBlock
+    elif roSupportMarkdown in p.s.options and isMarkdownBlockQuote(p):
+      result = rnMarkdownBlockQuote
+    elif match(p, tokenAfterNewline(p), "aI") and
+        isAdornmentHeadline(p, tokenAfterNewline(p)):
       result = rnHeadline
-    elif p.tok[p.idx].symbol == "::":
-      result = rnLiteralBlock
-    elif predNL(p) and
-        ((p.tok[p.idx].symbol == "+") or (p.tok[p.idx].symbol == "*") or
-        (p.tok[p.idx].symbol == "-")) and (p.tok[p.idx + 1].kind == tkWhite):
+    elif currentTok(p).symbol in ["+", "*", "-"] and nextTok(p).kind == tkWhite:
       result = rnBulletList
-    elif (p.tok[p.idx].symbol == "|") and isLineBlock(p):
-      result = rnLineBlock
-    elif (p.tok[p.idx].symbol == "..") and predNL(p):
-      result = rnDirective
-    elif match(p, p.idx, ":w:") and predNL(p):
-      # (p.tok[p.idx].symbol == ":")
+    elif match(p, p.idx, ":w:E"):
+      # (currentTok(p).symbol == ":")
       result = rnFieldList
-    elif match(p, p.idx, "(e) "):
+    elif match(p, p.idx, "(e) ") or match(p, p.idx, "e) ") or
+         match(p, p.idx, "e. "):
       result = rnEnumList
-    elif match(p, p.idx, "+a+"):
-      result = rnGridTable
-      rstMessage(p, meGridTableNotImplemented)
-    elif isDefList(p):
-      result = rnDefList
     elif isOptionList(p):
       result = rnOptionList
+    elif isRst(p) and isDefList(p):
+      result = rnDefList
+    elif isMd(p) and isMdDefListItem(p, p.idx):
+      result = rnMdDefList
     else:
       result = rnParagraph
   of tkWord, tkOther, tkWhite:
-    if match(p, tokenAfterNewline(p), "ai"): result = rnHeadline
+    let tokIdx = tokenAfterNewline(p)
+    if match(p, tokIdx, "aI"):
+      if isAdornmentHeadline(p, tokIdx): result = rnHeadline
+      else: result = rnParagraph
     elif match(p, p.idx, "e) ") or match(p, p.idx, "e. "): result = rnEnumList
-    elif isDefList(p): result = rnDefList
+    elif isRst(p) and isDefList(p): result = rnDefList
+    elif isMd(p) and isMdDefListItem(p, p.idx):
+      result = rnMdDefList
     else: result = rnParagraph
   else: result = rnLeaf
 
 proc parseLineBlock(p: var RstParser): PRstNode =
+  ## Returns rnLineBlock with all sons of type rnLineBlockItem
   result = nil
-  if p.tok[p.idx + 1].kind == tkWhite:
-    var col = p.tok[p.idx].col
-    result = newRstNode(rnLineBlock)
-    pushInd(p, p.tok[p.idx + 2].col)
-    inc(p.idx, 2)
+  if nextTok(p).kind in {tkWhite, tkIndent}:
+    var col = currentTok(p).col
+    result = newRstNodeA(p, rnLineBlock)
     while true:
       var item = newRstNode(rnLineBlockItem)
-      parseSection(p, item)
-      add(result, item)
-      if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and
-          (p.tok[p.idx + 1].symbol == "|") and
-          (p.tok[p.idx + 2].kind == tkWhite):
-        inc(p.idx, 3)
+      if nextTok(p).kind == tkWhite:
+        if nextTok(p).symbol.len > 1:  # pass additional indentation after '| '
+          item.lineIndent = nextTok(p).symbol
+        inc p.idx, 2
+        pushInd(p, p.tok[p.idx].col)
+        parseSection(p, item)
+        popInd(p)
+      else:  # tkIndent => add an empty line
+        item.lineIndent = "\n"
+        inc p.idx, 1
+      result.add(item)
+      if currentTok(p).kind == tkIndent and currentTok(p).ival == col and
+          nextTok(p).symbol == "|" and
+          p.tok[p.idx + 2].kind in {tkWhite, tkIndent}:
+        inc p.idx, 1
       else:
         break
-    popInd(p)
+
+proc parseDoc(p: var RstParser): PRstNode {.gcsafe.}
+
+proc getQuoteSymbol(p: RstParser, idx: int): tuple[sym: string, depth: int, tokens: int] =
+  result = ("", 0, 0)
+  var i = idx
+  result.sym &= p.tok[i].symbol
+  result.depth += p.tok[i].symbol.len
+  inc result.tokens
+  inc i
+  while p.tok[i].kind == tkWhite and i+1 < p.tok.len and
+        p.tok[i+1].kind == tkPunct and p.tok[i+1].symbol[0] == '>':
+    result.sym &= p.tok[i].symbol
+    result.sym &= p.tok[i+1].symbol
+    result.depth += p.tok[i+1].symbol.len
+    inc result.tokens, 2
+    inc i, 2
+
+proc parseMarkdownQuoteSegment(p: var RstParser, curSym: string, col: int):
+                              PRstNode =
+  ## We define *segment* as a group of lines that starts with exactly the
+  ## same quote symbol. If the following lines don't contain any `>` (*lazy*
+  ## continuation) they considered as continuation of the current segment.
+  var q: RstParser  # to delete `>` at a start of line and then parse normally
+  initParser(q, p.s)
+  q.col = p.col
+  q.line = p.line
+  var minCol = int.high  # minimum colum num in the segment
+  while true:  # move tokens of segment from `p` to `q` skipping `curSym`
+    case currentTok(p).kind
+    of tkEof:
+      break
+    of tkIndent:
+      if nextTok(p).kind in {tkIndent, tkEof}:
+        break
+      else:
+        if nextTok(p).symbol[0] == '>':
+          var (quoteSym, _, quoteTokens) = getQuoteSymbol(p, p.idx + 1)
+          if quoteSym == curSym:  # the segment continues
+            var iTok = tokenAfterNewline(p, p.idx+1)
+            if p.tok[iTok].kind notin {tkEof, tkIndent} and
+                p.tok[iTok].symbol[0] != '>':
+              rstMessage(p, mwRstStyle,
+                  "two or more quoted lines are followed by unquoted line " &
+                  $(curLine(p) + 1))
+              break
+            q.tok.add currentTok(p)
+            var ival = currentTok(p).ival + quoteSym.len
+            inc p.idx, (1 + quoteTokens)  # skip newline and > > >
+            if currentTok(p).kind == tkWhite:
+              ival += currentTok(p).symbol.len
+              inc p.idx
+            # fix up previous `tkIndent`s to ival (as if >>> were not there)
+            var j = q.tok.len - 1
+            while j >= 0 and q.tok[j].kind == tkIndent:
+              q.tok[j].ival = ival
+              dec j
+          else:  # next segment started
+            break
+        elif currentTok(p).ival < col:
+          break
+        else:  # the segment continues, a case like:
+               # > beginning
+               # continuation
+          q.tok.add currentTok(p)
+          inc p.idx
+    else:
+      if currentTok(p).col < minCol: minCol = currentTok(p).col
+      q.tok.add currentTok(p)
+      inc p.idx
+  q.indentStack = @[minCol]
+  # if initial indentation `minCol` is > 0 then final newlines
+  # should be omitted so that parseDoc could advance to the end of tokens:
+  var j = q.tok.len - 1
+  while q.tok[j].kind == tkIndent: dec j
+  q.tok.setLen (j+1)
+  q.tok.add Token(kind: tkEof, line: currentTok(p).line)
+  result = parseDoc(q)
+
+proc parseMarkdownBlockQuote(p: var RstParser): PRstNode =
+  var (curSym, quotationDepth, quoteTokens) = getQuoteSymbol(p, p.idx)
+  let col = currentTok(p).col
+  result = newRstNodeA(p, rnMarkdownBlockQuote)
+  inc p.idx, quoteTokens  # skip first >
+  while true:
+    var item = newRstNode(rnMarkdownBlockQuoteItem)
+    item.quotationDepth = quotationDepth
+    if currentTok(p).kind == tkWhite: inc p.idx
+    item.add parseMarkdownQuoteSegment(p, curSym, col)
+    result.add(item)
+    if currentTok(p).kind == tkIndent and currentTok(p).ival == col and
+        nextTok(p).kind != tkEof and nextTok(p).symbol[0] == '>':
+      (curSym, quotationDepth, quoteTokens) = getQuoteSymbol(p, p.idx + 1)
+      inc p.idx, (1 + quoteTokens)  # skip newline and > > >
+    else:
+      break
 
 proc parseParagraph(p: var RstParser, result: PRstNode) =
   while true:
-    case p.tok[p.idx].kind
+    case currentTok(p).kind
     of tkIndent:
-      if p.tok[p.idx + 1].kind == tkIndent:
-        inc(p.idx)
-        break
-      elif (p.tok[p.idx].ival == currInd(p)):
-        inc(p.idx)
+      if nextTok(p).kind == tkIndent:
+        inc p.idx
+        break  # blank line breaks paragraph for both Md & Rst
+      elif currentTok(p).ival == currInd(p) or (
+          isMd(p) and currentTok(p).ival > currInd(p)):
+          # (Md allows adding additional indentation inside paragraphs)
+        inc p.idx
         case whichSection(p)
-        of rnParagraph, rnLeaf, rnHeadline, rnOverline, rnDirective:
-          add(result, newRstNode(rnLeaf, " "))
+        of rnParagraph, rnLeaf, rnHeadline, rnMarkdownHeadline,
+            rnOverline, rnDirective:
+          result.add newLeaf(" ")
         of rnLineBlock:
-          addIfNotNil(result, parseLineBlock(p))
-        else: break
+          result.addIfNotNil(parseLineBlock(p))
+        of rnMarkdownBlockQuote:
+          result.addIfNotNil(parseMarkdownBlockQuote(p))
+        else:
+          dec p.idx  # allow subsequent block to be parsed as another section
+          break
       else:
         break
     of tkPunct:
-      if (p.tok[p.idx].symbol == "::") and
-          (p.tok[p.idx + 1].kind == tkIndent) and
-          (currInd(p) < p.tok[p.idx + 1].ival):
-        add(result, newRstNode(rnLeaf, ":"))
-        inc(p.idx)            # skip '::'
-        add(result, parseLiteralBlock(p))
+      if isRst(p) and (
+          let literalBlockKind = whichRstLiteralBlock(p);
+          literalBlockKind != lbNone):
+        result.add newLeaf(":")
+        inc p.idx            # skip '::'
+        result.add(parseRstLiteralBlock(p, literalBlockKind))
         break
       else:
         parseInline(p, result)
@@ -1103,382 +2567,743 @@ proc parseParagraph(p: var RstParser, result: PRstNode) =
       parseInline(p, result)
     else: break
 
+proc checkHeadingHierarchy(p: RstParser, lvl: int) =
+  if lvl - p.s.hCurLevel > 1:  # broken hierarchy!
+    proc descr(l: int): string =
+      (if p.s.hLevels[l].hasOverline: "overline " else: "underline ") &
+      repeat(p.s.hLevels[l].symbol, 5)
+    var msg = "(section level inconsistent: "
+    msg.add descr(lvl) & " unexpectedly found, " &
+      "while the following intermediate section level(s) are missing on lines "
+    msg.add $p.s.hLevels[p.s.hCurLevel].line & ".." & $curLine(p) & ":"
+    for l in p.s.hCurLevel+1 .. lvl-1:
+      msg.add " " & descr(l)
+      if l != lvl-1: msg.add ","
+    rstMessage(p, meNewSectionExpected, msg & ")")
+
 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 = newRstNode(rnMarkdownHeadline)
+    # Note that level hierarchy is not checked for markdown headings
+    result.level = currentTok(p).symbol.len
+    assert(nextTok(p).kind == tkWhite)
+    inc p.idx, 2
+    parseUntilNewline(p, result)
+  else:
+    result = newRstNode(rnHeadline)
+    parseUntilNewline(p, result)
+    assert(currentTok(p).kind == tkIndent)
+    assert(nextTok(p).kind == tkAdornment)
+    var c = nextTok(p).symbol[0]
+    inc p.idx, 2
+    result.level = getLevel(p, c, hasOverline=false)
+    checkHeadingHierarchy(p, result.level)
+    p.s.hCurLevel = result.level
+  addAnchorRst(p, linkName(result), result, anchorType=headlineAnchor)
+  p.s.tocPart.add result
+
+proc parseOverline(p: var RstParser): PRstNode =
+  var c = currentTok(p).symbol[0]
+  inc p.idx, 2
+  result = newRstNode(rnOverline)
+  while true:
+    parseUntilNewline(p, result)
+    if currentTok(p).kind == tkIndent:
+      inc p.idx
+      if prevTok(p).ival > currInd(p):
+        result.add newLeaf(" ")
+      else:
+        break
+    else:
+      break
+  result.level = getLevel(p, c, hasOverline=true)
+  checkHeadingHierarchy(p, result.level)
+  p.s.hCurLevel = result.level
+  if currentTok(p).kind == tkAdornment:
+    inc p.idx
+    if currentTok(p).kind == tkIndent: inc p.idx
+  addAnchorRst(p, linkName(result), result, anchorType=headlineAnchor)
+  p.s.tocPart.add result
+
+proc fixHeadlines(s: PRstSharedState) =
+  # Fix up section levels depending on presence of a title and subtitle:
+  for n in s.tocPart:
+    if n.kind in {rnHeadline, rnOverline}:
+      if s.hTitleCnt == 2:
+        if n.level == 1:    # it's the subtitle
+          n.level = 0
+        elif n.level >= 2:  # normal sections, start numbering from 1
+          n.level -= 1
+      elif s.hTitleCnt == 0:
+        n.level += 1
+  # Set headline anchors:
+  for iHeading in 0 .. s.tocPart.high:
+    let n: PRstNode = s.tocPart[iHeading]
+    if n.level >= 1:
+      n.anchor = rstnodeToRefname(n)
+      # Fix anchors for uniqueness if `.. contents::` is present
+      if s.hasToc:
+        # Find the last higher level section for unique reference name
+        var sectionPrefix = ""
+        for i in countdown(iHeading - 1, 0):
+          if s.tocPart[i].level >= 1 and s.tocPart[i].level < n.level:
+            sectionPrefix = rstnodeToRefname(s.tocPart[i]) & "-"
+            break
+        if sectionPrefix != "":
+          n.anchor = sectionPrefix & n.anchor
+  s.tocPart.setLen 0
 
 type
-  IntSeq = seq[int]
+  ColSpec = object
+    start, stop: int
+  RstCols = seq[ColSpec]
+  ColumnLimits = tuple  # for Markdown
+    first, last: int
+  ColSeq = seq[ColumnLimits]
+
+proc tokStart(p: RstParser, idx: int): int =
+  result = p.tok[idx].col
+
+proc tokStart(p: RstParser): int =
+  result = tokStart(p, p.idx)
+
+proc tokEnd(p: RstParser, idx: int): int =
+  result = p.tok[idx].col + p.tok[idx].symbol.len - 1
 
 proc tokEnd(p: RstParser): int =
-  result = p.tok[p.idx].col + len(p.tok[p.idx].symbol) - 1
+  result = tokEnd(p, p.idx)
 
-proc getColumns(p: var RstParser, cols: var IntSeq) =
+proc getColumns(p: RstParser, cols: var RstCols, startIdx: int): int =
+  # Fills table column specification (or separator) `cols` and returns
+  # the next parser index after it.
   var L = 0
+  result = startIdx
   while true:
-    inc(L)
+    inc L
     setLen(cols, L)
-    cols[L - 1] = tokEnd(p)
-    assert(p.tok[p.idx].kind == tkAdornment)
-    inc(p.idx)
-    if p.tok[p.idx].kind != tkWhite: break
-    inc(p.idx)
-    if p.tok[p.idx].kind != tkAdornment: break
-  if p.tok[p.idx].kind == tkIndent: inc(p.idx)
-  # last column has no limit:
-  cols[L - 1] = 32000
-
-proc parseDoc(p: var RstParser): PRstNode {.gcsafe.}
+    cols[L - 1].start = tokStart(p, result)
+    cols[L - 1].stop = tokEnd(p, result)
+    assert(p.tok[result].kind == tkAdornment)
+    inc result
+    if p.tok[result].kind != tkWhite: break
+    inc result
+    if p.tok[result].kind != tkAdornment: break
+  if p.tok[result].kind == tkIndent: inc result
+
+proc checkColumns(p: RstParser, cols: RstCols) =
+  var i = p.idx
+  if p.tok[i].symbol[0] != '=':
+    stopOrWarn(p, meIllformedTable,
+               "only tables with `=` columns specification are allowed")
+  for col in 0 ..< cols.len:
+    if tokEnd(p, i) != cols[col].stop:
+      stopOrWarn(p, meIllformedTable,
+                 "end of table column #$1 should end at position $2" % [
+                   $(col+1), $(cols[col].stop+ColRstOffset)],
+                 p.tok[i].line, tokEnd(p, i))
+    inc i
+    if col == cols.len - 1:
+      if p.tok[i].kind == tkWhite:
+        inc i
+      if p.tok[i].kind notin {tkIndent, tkEof}:
+        stopOrWarn(p, meIllformedTable, "extraneous column specification")
+    elif p.tok[i].kind == tkWhite:
+      inc i
+    else:
+      stopOrWarn(p, meIllformedTable,
+                 "no enough table columns", p.tok[i].line, p.tok[i].col)
+
+proc getSpans(p: RstParser, nextLine: int,
+              cols: RstCols, unitedCols: RstCols): seq[int] =
+  ## Calculates how many columns a joined cell occupies.
+  if unitedCols.len > 0:
+    result = newSeq[int](unitedCols.len)
+    var
+      iCell = 0
+      jCell = 0
+      uCell = 0
+    while jCell < cols.len:
+      if cols[jCell].stop < unitedCols[uCell].stop:
+        inc jCell
+      elif cols[jCell].stop == unitedCols[uCell].stop:
+        result[uCell] = jCell - iCell + 1
+        iCell = jCell + 1
+        jCell = jCell + 1
+        inc uCell
+      else:
+        rstMessage(p, meIllformedTable,
+                   "spanning underline does not match main table columns",
+                   p.tok[nextLine].line, p.tok[nextLine].col)
+
+proc parseSimpleTableRow(p: var RstParser, cols: RstCols, colChar: char): PRstNode =
+  ## Parses 1 row in RST simple table.
+  # Consider that columns may be spanning (united by using underline like ----):
+  let nextLine = tokenAfterNewline(p)
+  var unitedCols: RstCols
+  var afterSpan: int
+  if p.tok[nextLine].kind == tkAdornment and p.tok[nextLine].symbol[0] == '-':
+    afterSpan = getColumns(p, unitedCols, nextLine)
+    if unitedCols == cols and p.tok[nextLine].symbol[0] == colChar:
+      # legacy rst.nim compat.: allow punctuation like `----` in main boundaries
+      afterSpan = nextLine
+      unitedCols.setLen 0
+  else:
+    afterSpan = nextLine
+  template colEnd(i): int =
+    if i == cols.len - 1: high(int)  # last column has no limit
+    elif unitedCols.len > 0: unitedCols[i].stop else: cols[i].stop
+  template colStart(i): int =
+    if unitedCols.len > 0: unitedCols[i].start else: cols[i].start
+  var row = newSeq[string](if unitedCols.len > 0: unitedCols.len else: cols.len)
+  var spans: seq[int] = getSpans(p, nextLine, cols, unitedCols)
+
+  let line = currentTok(p).line
+  # Iterate over the lines a single cell may span:
+  while true:
+    var nCell = 0
+    # distribute tokens between cells in the current line:
+    while currentTok(p).kind notin {tkIndent, tkEof}:
+      if tokEnd(p) <= colEnd(nCell):
+        if tokStart(p) < colStart(nCell):
+          if currentTok(p).kind != tkWhite:
+            stopOrWarn(p, meIllformedTable,
+                       "this word crosses table column from the left")
+            row[nCell].add(currentTok(p).symbol)
+        else:
+          row[nCell].add(currentTok(p).symbol)
+        inc p.idx
+      else:
+        if tokStart(p) < colEnd(nCell) and currentTok(p).kind != tkWhite:
+          stopOrWarn(p, meIllformedTable,
+                     "this word crosses table column from the right")
+          row[nCell].add(currentTok(p).symbol)
+          inc p.idx
+        inc nCell
+    if currentTok(p).kind == tkIndent: inc p.idx
+    if tokEnd(p) <= colEnd(0): break
+    # Continued current cells because the 1st column is empty.
+    if currentTok(p).kind in {tkEof, tkAdornment}:
+      break
+    for nCell in countup(1, high(row)): row[nCell].add('\n')
+  result = newRstNode(rnTableRow)
+  var q: RstParser
+  for uCell in 0 ..< row.len:
+    initParser(q, p.s)
+    q.col = colStart(uCell)
+    q.line = line - 1
+    getTokens(row[uCell], q.tok)
+    let cell = newRstNode(rnTableDataCell)
+    cell.span = if spans.len == 0: 0 else: spans[uCell]
+    cell.add(parseDoc(q))
+    result.add(cell)
+  if afterSpan > p.idx:
+    p.idx = afterSpan
 
 proc parseSimpleTable(p: var RstParser): PRstNode =
-  var
-    cols: IntSeq
-    row: seq[string]
-    i, last, line: int
-    c: char
-    q: RstParser
-    a, b: PRstNode
-  result = newRstNode(rnTable)
-  cols = @[]
-  row = @[]
-  a = nil
-  c = p.tok[p.idx].symbol[0]
+  var cols: RstCols
+  result = newRstNodeA(p, rnTable)
+  let startIdx = getColumns(p, cols, p.idx)
+  let colChar = currentTok(p).symbol[0]
+  checkColumns(p, cols)
+  p.idx = startIdx
+  result.colCount = cols.len
   while true:
-    if p.tok[p.idx].kind == tkAdornment:
-      last = tokenAfterNewline(p)
-      if p.tok[last].kind in {tkEof, tkIndent}:
+    if currentTok(p).kind == tkAdornment:
+      checkColumns(p, cols)
+      p.idx = tokenAfterNewline(p)
+      if currentTok(p).kind in {tkEof, tkIndent}:
         # skip last adornment line:
-        p.idx = last
         break
-      getColumns(p, cols)
-      setLen(row, len(cols))
-      if a != nil:
-        for j in 0..len(a)-1: a.sons[j].kind = rnTableHeaderCell
-    if p.tok[p.idx].kind == tkEof: break
-    for j in countup(0, high(row)): row[j] = ""
-    # the following while loop iterates over the lines a single cell may span:
-    line = p.tok[p.idx].line
-    while true:
-      i = 0
-      while not (p.tok[p.idx].kind in {tkIndent, tkEof}):
-        if (tokEnd(p) <= cols[i]):
-          add(row[i], p.tok[p.idx].symbol)
-          inc(p.idx)
-        else:
-          if p.tok[p.idx].kind == tkWhite: inc(p.idx)
-          inc(i)
-      if p.tok[p.idx].kind == tkIndent: inc(p.idx)
-      if tokEnd(p) <= cols[0]: break
-      if p.tok[p.idx].kind in {tkEof, tkAdornment}: break
-      for j in countup(1, high(row)): add(row[j], '\x0A')
+      if result.sons.len > 0: result.sons[^1].endsHeader = true
+      # fix rnTableDataCell -> rnTableHeaderCell for previous table rows:
+      for nRow in 0 ..< result.sons.len:
+        for nCell in 0 ..< result.sons[nRow].len:
+          template cell: PRstNode = result.sons[nRow].sons[nCell]
+          cell = PRstNode(kind: rnTableHeaderCell, sons: cell.sons,
+                          span: cell.span, anchor: cell.anchor)
+    if currentTok(p).kind == tkEof: break
+    let tabRow = parseSimpleTableRow(p, cols, colChar)
+    result.add tabRow
+
+proc readTableRow(p: var RstParser): ColSeq =
+  if currentTok(p).symbol == "|": inc p.idx
+  while currentTok(p).kind notin {tkIndent, tkEof}:
+    var limits: ColumnLimits
+    limits.first = p.idx
+    while currentTok(p).kind notin {tkIndent, tkEof}:
+      if currentTok(p).symbol == "|" and prevTok(p).symbol != "\\": break
+      inc p.idx
+    limits.last = p.idx
+    result.add(limits)
+    if currentTok(p).kind in {tkIndent, tkEof}: break
+    inc p.idx
+  p.idx = tokenAfterNewline(p)
+
+proc getColContents(p: var RstParser, colLim: ColumnLimits): string =
+  for i in colLim.first ..< colLim.last:
+    result.add(p.tok[i].symbol)
+  result.strip
+
+proc isValidDelimiterRow(p: var RstParser, colNum: int): bool =
+  let row = readTableRow(p)
+  if row.len != colNum: return false
+  for limits in row:
+    let content = getColContents(p, limits)
+    if content.len < 3 or not (content.startsWith("--") or content.startsWith(":-")):
+      return false
+  return true
+
+proc parseMarkdownTable(p: var RstParser): PRstNode =
+  var
+    row: ColSeq
+    a, b: PRstNode
+    q: RstParser
+  result = newRstNodeA(p, rnMarkdownTable)
+
+  proc parseRow(p: var RstParser, cellKind: RstNodeKind, result: PRstNode) =
+    row = readTableRow(p)
+    if result.colCount == 0: result.colCount = row.len # table header
+    elif row.len < result.colCount: row.setLen(result.colCount)
     a = newRstNode(rnTableRow)
-    for j in countup(0, high(row)):
+    for j in 0 ..< result.colCount:
+      b = newRstNode(cellKind)
       initParser(q, p.s)
-      q.col = cols[j]
-      q.line = line - 1
-      q.filename = p.filename
-      q.col += getTokens(row[j], false, q.tok)
-      b = newRstNode(rnTableDataCell)
-      add(b, parseDoc(q))
-      add(a, b)
-    add(result, a)
+      q.col = p.col
+      q.line = currentTok(p).line - 1
+      getTokens(getColContents(p, row[j]), q.tok)
+      b.add(parseDoc(q))
+      a.add(b)
+    result.add(a)
+
+  parseRow(p, rnTableHeaderCell, result)
+  if not isValidDelimiterRow(p, result.colCount):
+    rstMessage(p, meMarkdownIllformedTable)
+  while predNL(p) and currentTok(p).symbol == "|":
+    parseRow(p, rnTableDataCell, result)
 
 proc parseTransition(p: var RstParser): PRstNode =
-  result = newRstNode(rnTransition)
-  inc(p.idx)
-  if p.tok[p.idx].kind == tkIndent: inc(p.idx)
-  if p.tok[p.idx].kind == tkIndent: inc(p.idx)
-
-proc parseOverline(p: var RstParser): PRstNode =
-  var c = p.tok[p.idx].symbol[0]
-  inc(p.idx, 2)
-  result = newRstNode(rnOverline)
-  while true:
-    parseUntilNewline(p, result)
-    if p.tok[p.idx].kind == tkIndent:
-      inc(p.idx)
-      if p.tok[p.idx - 1].ival > currInd(p):
-        add(result, newRstNode(rnLeaf, " "))
-      else:
-        break
-    else:
-      break
-  result.level = getLevel(p.s.overlineToLevel, p.s.oLevel, c)
-  if p.tok[p.idx].kind == tkAdornment:
-    inc(p.idx)                # XXX: check?
-    if p.tok[p.idx].kind == tkIndent: inc(p.idx)
+  result = newRstNodeA(p, rnTransition)
+  inc p.idx
+  if currentTok(p).kind == tkIndent: inc p.idx
+  if currentTok(p).kind == tkIndent: inc p.idx
 
 proc parseBulletList(p: var RstParser): PRstNode =
   result = nil
-  if p.tok[p.idx + 1].kind == tkWhite:
-    var bullet = p.tok[p.idx].symbol
-    var col = p.tok[p.idx].col
-    result = newRstNode(rnBulletList)
+  if nextTok(p).kind == tkWhite:
+    var bullet = currentTok(p).symbol
+    var col = currentTok(p).col
+    result = newRstNodeA(p, rnBulletList)
     pushInd(p, p.tok[p.idx + 2].col)
-    inc(p.idx, 2)
+    inc p.idx, 2
     while true:
       var item = newRstNode(rnBulletItem)
       parseSection(p, item)
-      add(result, item)
-      if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and
-          (p.tok[p.idx + 1].symbol == bullet) and
-          (p.tok[p.idx + 2].kind == tkWhite):
-        inc(p.idx, 3)
+      result.add(item)
+      if currentTok(p).kind == tkIndent and currentTok(p).ival == col and
+          nextTok(p).symbol == bullet and
+          p.tok[p.idx + 2].kind == tkWhite:
+        inc p.idx, 3
       else:
         break
     popInd(p)
 
 proc parseOptionList(p: var RstParser): PRstNode =
-  result = newRstNode(rnOptionList)
+  result = newRstNodeA(p, rnOptionList)
+  let col = currentTok(p).col
+  var order = 1
   while true:
-    if isOptionList(p):
+    if currentTok(p).col == col and isOptionList(p):
       var a = newRstNode(rnOptionGroup)
       var b = newRstNode(rnDescription)
       var c = newRstNode(rnOptionListItem)
-      if match(p, p.idx, "//w"): inc(p.idx)
-      while not (p.tok[p.idx].kind in {tkIndent, tkEof}):
-        if (p.tok[p.idx].kind == tkWhite) and (len(p.tok[p.idx].symbol) > 1):
-          inc(p.idx)
+      if match(p, p.idx, "//w"): inc p.idx
+      while currentTok(p).kind notin {tkIndent, tkEof}:
+        if currentTok(p).kind == tkWhite and currentTok(p).symbol.len > 1:
+          inc p.idx
           break
-        add(a, newLeaf(p))
-        inc(p.idx)
+        a.add(newLeaf(p))
+        inc p.idx
       var j = tokenAfterNewline(p)
-      if (j > 0) and (p.tok[j - 1].kind == tkIndent) and
-          (p.tok[j - 1].ival > currInd(p)):
+      if j > 0 and p.tok[j - 1].kind == tkIndent and p.tok[j - 1].ival > currInd(p):
         pushInd(p, p.tok[j - 1].ival)
         parseSection(p, b)
         popInd(p)
       else:
         parseLine(p, b)
-      if (p.tok[p.idx].kind == tkIndent): inc(p.idx)
-      add(c, a)
-      add(c, b)
-      add(result, c)
+      while currentTok(p).kind == tkIndent: inc p.idx
+      c.add(a)
+      c.add(b)
+      c.order = order; inc order
+      result.add(c)
+    else:
+      if currentTok(p).kind != tkEof: dec p.idx  # back to tkIndent
+      break
+
+proc parseMdDefinitionList(p: var RstParser): PRstNode =
+  ## Parses (Pandoc/kramdown/PHPextra) Markdown definition lists.
+  result = newRstNodeA(p, rnMdDefList)
+  let termCol = currentTok(p).col
+  while true:
+    var item = newRstNode(rnDefItem)
+    var term = newRstNode(rnDefName)
+    parseLine(p, term)
+    skipNewlines(p)
+    inc p.idx, 2  # skip ":" and space
+    item.add(term)
+    while true:
+      var def = newRstNode(rnDefBody)
+      let indent = getMdBlockIndent(p)
+      pushInd(p, indent)
+      parseSection(p, def)
+      popInd(p)
+      item.add(def)
+      let j = skipNewlines(p, p.idx)
+      if isMdDefBody(p, j, termCol):  # parse next definition body
+        p.idx = j + 2  # skip ":" and space
+      else:
+        break
+    result.add(item)
+    let j = skipNewlines(p, p.idx)
+    if p.tok[j].col == termCol and isMdDefListItem(p, j):
+      p.idx = j  # parse next item
     else:
       break
 
 proc parseDefinitionList(p: var RstParser): PRstNode =
   result = nil
   var j = tokenAfterNewline(p) - 1
-  if (j >= 1) and (p.tok[j].kind == tkIndent) and
-      (p.tok[j].ival > currInd(p)) and (p.tok[j - 1].symbol != "::"):
-    var col = p.tok[p.idx].col
-    result = newRstNode(rnDefList)
+  if j >= 1 and p.tok[j].kind == tkIndent and
+      p.tok[j].ival > currInd(p) and p.tok[j - 1].symbol != "::":
+    var col = currentTok(p).col
+    result = newRstNodeA(p, rnDefList)
     while true:
+      if isOptionList(p):
+        break  # option list has priority over def.list
       j = p.idx
       var a = newRstNode(rnDefName)
       parseLine(p, a)
-      if (p.tok[p.idx].kind == tkIndent) and
-          (p.tok[p.idx].ival > currInd(p)) and
-          (p.tok[p.idx + 1].symbol != "::") and
-          not (p.tok[p.idx + 1].kind in {tkIndent, tkEof}):
-        pushInd(p, p.tok[p.idx].ival)
+      if currentTok(p).kind == tkIndent and
+          currentTok(p).ival > currInd(p) and
+          nextTok(p).symbol != "::" and
+          nextTok(p).kind notin {tkIndent, tkEof}:
+        pushInd(p, currentTok(p).ival)
         var b = newRstNode(rnDefBody)
         parseSection(p, b)
         var c = newRstNode(rnDefItem)
-        add(c, a)
-        add(c, b)
-        add(result, c)
+        c.add(a)
+        c.add(b)
+        result.add(c)
         popInd(p)
       else:
         p.idx = j
         break
-      if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col):
-        inc(p.idx)
+      if currentTok(p).kind == tkIndent and currentTok(p).ival == col:
+        inc p.idx
         j = tokenAfterNewline(p) - 1
         if j >= 1 and p.tok[j].kind == tkIndent and p.tok[j].ival > col and
             p.tok[j-1].symbol != "::" and p.tok[j+1].kind != tkIndent:
           discard
         else:
           break
-    if len(result) == 0: result = nil
+    if result.len == 0: result = nil
 
 proc parseEnumList(p: var RstParser): PRstNode =
   const
-    wildcards: array[0..2, string] = ["(e) ", "e) ", "e. "]
-    wildpos: array[0..2, int] = [1, 0, 0]
-  result = nil
+    wildcards: array[0..5, string] = ["(n) ", "n) ", "n. ",
+                                      "(x) ", "x) ", "x. "]
+      # enumerator patterns, where 'x' means letter and 'n' means number
+    wildToken: array[0..5, int] = [4, 3, 3, 4, 3, 3]  # number of tokens
+    wildIndex: array[0..5, int] = [1, 0, 0, 1, 0, 0]
+      # position of enumeration sequence (number/letter) in enumerator
+  let col = currentTok(p).col
   var w = 0
-  while w <= 2:
+  while w < wildcards.len:
     if match(p, p.idx, wildcards[w]): break
-    inc(w)
-  if w <= 2:
-    var col = p.tok[p.idx].col
-    result = newRstNode(rnEnumList)
-    inc(p.idx, wildpos[w] + 3)
-    var j = tokenAfterNewline(p)
-    if (p.tok[j].col == p.tok[p.idx].col) or match(p, j, wildcards[w]):
-      pushInd(p, p.tok[p.idx].col)
-      while true:
-        var item = newRstNode(rnEnumItem)
-        parseSection(p, item)
-        add(result, item)
-        if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and
-            match(p, p.idx + 1, wildcards[w]):
-          inc(p.idx, wildpos[w] + 4)
-        else:
+    inc w
+  assert w < wildcards.len
+
+  proc checkAfterNewline(p: RstParser, report: bool): bool =
+    ## If no indentation on the next line then parse as a normal paragraph
+    ## according to the RST spec. And report a warning with suggestions
+    let j = tokenAfterNewline(p, start=p.idx+1)
+    let requiredIndent = p.tok[p.idx+wildToken[w]].col
+    if p.tok[j].kind notin {tkIndent, tkEof} and
+        p.tok[j].col < requiredIndent and
+        (p.tok[j].col > col or
+          (p.tok[j].col == col and not match(p, j, wildcards[w]))):
+      if report:
+        let n = p.line + p.tok[j].line
+        let msg = "\n" & """
+          not enough indentation on line $2
+            (should be at column $3 if it's a continuation of enum. list),
+          or no blank line after line $1 (if it should be the next paragraph),
+          or no escaping \ at the beginning of line $1
+            (if lines $1..$2 are a normal paragraph, not enum. list)""".dedent
+        let c = p.col + requiredIndent + ColRstOffset
+        rstMessage(p, mwRstStyle, msg % [$(n-1), $n, $c],
+                   p.tok[j].line, p.tok[j].col)
+      result = false
+    else:
+      result = true
+
+  if not checkAfterNewline(p, report = true):
+    return nil
+  result = newRstNodeA(p, rnEnumList)
+  let autoEnums = if roSupportMarkdown in p.s.options: @["#", "1"] else: @["#"]
+  var prevAE = ""  # so as not allow mixing auto-enumerators `1` and `#`
+  var curEnum = 1
+  for i in 0 ..< wildToken[w]-1:  # add first enumerator with (, ), and .
+    if p.tok[p.idx + i].symbol == "#":
+      prevAE = "#"
+      result.labelFmt.add "1"
+    else:
+      result.labelFmt.add p.tok[p.idx + i].symbol
+  var prevEnum = p.tok[p.idx + wildIndex[w]].symbol
+  inc p.idx, wildToken[w]
+  while true:
+    var item = newRstNode(rnEnumItem)
+    pushInd(p, currentTok(p).col)
+    parseSection(p, item)
+    popInd(p)
+    result.add(item)
+    if currentTok(p).kind == tkIndent and currentTok(p).ival == col and
+        match(p, p.idx+1, wildcards[w]):
+      # don't report to avoid duplication of warning since for
+      # subsequent enum. items parseEnumList will be called second time:
+      if not checkAfterNewline(p, report = false):
+        break
+      let enumerator = p.tok[p.idx + 1 + wildIndex[w]].symbol
+      # check that it's in sequence: enumerator == next(prevEnum)
+      if "n" in wildcards[w]:  # arabic numeral
+        let prevEnumI = try: parseInt(prevEnum) except ValueError: 1
+        if enumerator in autoEnums:
+          if prevAE != "" and enumerator != prevAE:
+            break
+          prevAE = enumerator
+          curEnum = prevEnumI + 1
+        else: curEnum = (try: parseInt(enumerator) except ValueError: 1)
+        if curEnum - prevEnumI != 1:
           break
-      popInd(p)
+        prevEnum = enumerator
+      else:  # a..z
+        let prevEnumI = ord(prevEnum[0])
+        if enumerator == "#": curEnum = prevEnumI + 1
+        else: curEnum = ord(enumerator[0])
+        if curEnum - prevEnumI != 1:
+          break
+        prevEnum = $chr(curEnum)
+      inc p.idx, 1 + wildToken[w]
     else:
-      dec(p.idx, wildpos[w] + 3)
-      result = nil
+      break
+
+proc prefix(ftnType: FootnoteType): string =
+  case ftnType
+  of fnManualNumber: result = "footnote-"
+  of fnAutoNumber: result = "footnoteauto-"
+  of fnAutoNumberLabel: result = "footnote-"
+  of fnAutoSymbol: result = "footnotesym-"
+  of fnCitation: result = "citation-"
+
+proc parseFootnote(p: var RstParser): PRstNode {.gcsafe.} =
+  ## Parses footnotes and citations, always returns 2 sons:
+  ##
+  ## 1) footnote label, always containing rnInner with 1 or more sons
+  ## 2) footnote body, which may be nil
+  var label: PRstNode
+  if isRst(p):
+    inc p.idx  # skip space after `..`
+  label = parseFootnoteName(p, reference=false)
+  if label == nil:
+    if isRst(p):
+      dec p.idx
+    return nil
+  result = newRstNode(rnFootnote)
+  result.add label
+  let (fnType, i) = getFootnoteType(p.s, label)
+  var name = ""
+  var anchor = fnType.prefix
+  case fnType
+  of fnManualNumber:
+    addFootnoteNumManual(p, i)
+    anchor.add $i
+  of fnAutoNumber, fnAutoNumberLabel:
+    name = rstnodeToRefname(label)
+    addFootnoteNumAuto(p, name)
+    if fnType == fnAutoNumberLabel:
+      anchor.add name
+    else:  # fnAutoNumber
+      result.order = p.s.lineFootnoteNum.len
+      anchor.add $result.order
+  of fnAutoSymbol:
+    addFootnoteSymAuto(p)
+    result.order = p.s.lineFootnoteSym.len
+    anchor.add $p.s.lineFootnoteSym.len
+  of fnCitation:
+    anchor.add rstnodeToRefname(label)
+  addAnchorRst(p, anchor, target = result, anchorType = footnoteAnchor)
+  result.anchor = anchor
+  if currentTok(p).kind == tkWhite: inc p.idx
+  discard parseBlockContent(p, result, parseSectionWrapper)
+  if result.len < 2:
+    result.add nil
 
 proc sonKind(father: PRstNode, i: int): RstNodeKind =
   result = rnLeaf
-  if i < len(father): result = father.sons[i].kind
+  if i < father.len: result = father.sons[i].kind
 
 proc parseSection(p: var RstParser, result: PRstNode) =
+  ## parse top-level RST elements: sections, transitions and body elements.
   while true:
     var leave = false
     assert(p.idx >= 0)
-    while p.tok[p.idx].kind == tkIndent:
-      if currInd(p) == p.tok[p.idx].ival:
-        inc(p.idx)
-      elif p.tok[p.idx].ival > currInd(p):
-        pushInd(p, p.tok[p.idx].ival)
-        var a = newRstNode(rnBlockQuote)
-        parseSection(p, a)
-        add(result, a)
-        popInd(p)
+    while currentTok(p).kind == tkIndent:
+      if currInd(p) == currentTok(p).ival:
+        inc p.idx
+      elif currentTok(p).ival > currInd(p):
+        if roPreferMarkdown in p.s.options:  # Markdown => normal paragraphs
+          if currentTok(p).ival - currInd(p) >= 4:
+            result.add parseLiteralBlock(p)
+          else:
+            pushInd(p, currentTok(p).ival)
+            parseSection(p, result)
+            popInd(p)
+        else:  # RST mode => block quotes
+          pushInd(p, currentTok(p).ival)
+          var a = newRstNodeA(p, rnBlockQuote)
+          parseSection(p, a)
+          result.add(a)
+          popInd(p)
       else:
+        while currentTok(p).kind != tkEof and nextTok(p).kind == tkIndent:
+          inc p.idx  # skip blank lines
         leave = true
         break
-    if leave or p.tok[p.idx].kind == tkEof: break
+    if leave or currentTok(p).kind == tkEof: break
     var a: PRstNode = nil
     var k = whichSection(p)
     case k
     of rnLiteralBlock:
-      inc(p.idx)              # skip '::'
+      inc p.idx              # skip '::'
       a = parseLiteralBlock(p)
     of rnBulletList: a = parseBulletList(p)
     of rnLineBlock: a = parseLineBlock(p)
+    of rnMarkdownBlockQuote: a = parseMarkdownBlockQuote(p)
     of rnDirective: a = parseDotDot(p)
+    of rnFootnote: a = parseFootnote(p)
     of rnEnumList: a = parseEnumList(p)
-    of rnLeaf: rstMessage(p, meNewSectionExpected)
+    of rnLeaf: rstMessage(p, meNewSectionExpected, "(syntax error)")
     of rnParagraph: discard
     of rnDefList: a = parseDefinitionList(p)
+    of rnMdDefList: a = parseMdDefinitionList(p)
     of rnFieldList:
-      if p.idx > 0: dec(p.idx)
+      if p.idx > 0: dec p.idx
       a = parseFields(p)
     of rnTransition: a = parseTransition(p)
-    of rnHeadline: a = parseHeadline(p)
+    of rnHeadline, rnMarkdownHeadline: a = parseHeadline(p)
     of rnOverline: a = parseOverline(p)
     of rnTable: a = parseSimpleTable(p)
+    of rnMarkdownTable: a = parseMarkdownTable(p)
     of rnOptionList: a = parseOptionList(p)
     else:
       #InternalError("rst.parseSection()")
       discard
     if a == nil and k != rnDirective:
-      a = newRstNode(rnParagraph)
+      a = newRstNodeA(p, rnParagraph)
       parseParagraph(p, a)
-    addIfNotNil(result, a)
+    result.addIfNotNil(a)
   if sonKind(result, 0) == rnParagraph and sonKind(result, 1) != rnParagraph:
-    result.sons[0].kind = rnInner
-
-proc parseSectionWrapper(p: var RstParser): PRstNode =
-  result = newRstNode(rnInner)
-  parseSection(p, result)
-  while (result.kind == rnInner) and (len(result) == 1):
-    result = result.sons[0]
-
-proc `$`(t: Token): string =
-  result = $t.kind & ' ' & (if isNil(t.symbol): "NIL" else: t.symbol)
+    result.sons[0] = newRstNode(rnInner, result.sons[0].sons,
+                                anchor=result.sons[0].anchor)
 
 proc parseDoc(p: var RstParser): PRstNode =
   result = parseSectionWrapper(p)
-  if p.tok[p.idx].kind != tkEof:
-    when false:
-      assert isAllocatedPtr(cast[pointer](p.tok))
-      for i in 0 .. high(p.tok):
-        assert isNil(p.tok[i].symbol) or
-               isAllocatedPtr(cast[pointer](p.tok[i].symbol))
-      echo "index: ", p.idx, " length: ", high(p.tok), "##",
-          p.tok[p.idx-1], p.tok[p.idx], p.tok[p.idx+1]
-    #assert isAllocatedPtr(cast[pointer](p.indentStack))
+  if currentTok(p).kind != tkEof:
     rstMessage(p, meGeneralParseError)
 
 type
   DirFlag = enum
     hasArg, hasOptions, argIsFile, argIsWord
   DirFlags = set[DirFlag]
-  SectionParser = proc (p: var RstParser): PRstNode {.nimcall.}
 
-proc parseDirective(p: var RstParser, flags: DirFlags): PRstNode =
+proc parseDirective(p: var RstParser, k: RstNodeKind, flags: DirFlags): PRstNode =
   ## Parses arguments and options for a directive block.
   ##
   ## A directive block will always have three sons: the arguments for the
-  ## directive (rnDirArg), the options (rnFieldList) and the block
-  ## (rnLineBlock). This proc parses the two first nodes, the block is left to
+  ## directive (rnDirArg), the options (rnFieldList) and the directive
+  ## content block. This proc parses the two first nodes, the 3rd is left to
   ## the outer `parseDirective` call.
   ##
   ## Both rnDirArg and rnFieldList children nodes might be nil, so you need to
   ## check them before accessing.
-  result = newRstNode(rnDirective)
+  result = newRstNodeA(p, k)
+  if k == rnCodeBlock: result.info = lineInfo(p)
   var args: PRstNode = nil
   var options: PRstNode = nil
   if hasArg in flags:
     args = newRstNode(rnDirArg)
     if argIsFile in flags:
       while true:
-        case p.tok[p.idx].kind
+        case currentTok(p).kind
         of tkWord, tkOther, tkPunct, tkAdornment:
-          add(args, newLeaf(p))
-          inc(p.idx)
+          args.add(newLeaf(p))
+          inc p.idx
         else: break
     elif argIsWord in flags:
-      while p.tok[p.idx].kind == tkWhite: inc(p.idx)
-      if p.tok[p.idx].kind == tkWord:
-        add(args, newLeaf(p))
-        inc(p.idx)
+      while currentTok(p).kind == tkWhite: inc p.idx
+      if currentTok(p).kind == tkWord:
+        args.add(newLeaf(p))
+        inc p.idx
       else:
         args = nil
     else:
       parseLine(p, args)
-  add(result, args)
+  result.add(args)
   if hasOptions in flags:
-    if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival >= 3) and
-        (p.tok[p.idx + 1].symbol == ":"):
+    if currentTok(p).kind == tkIndent and currentTok(p).ival > currInd(p) and
+        nextTok(p).symbol == ":":
+      pushInd(p, currentTok(p).ival)
       options = parseFields(p)
-  add(result, options)
-
-proc indFollows(p: RstParser): bool =
-  result = p.tok[p.idx].kind == tkIndent and p.tok[p.idx].ival > currInd(p)
+      popInd(p)
+  result.add(options)
 
-proc parseDirective(p: var RstParser, flags: DirFlags,
+proc parseDirective(p: var RstParser, k: RstNodeKind, flags: DirFlags,
                     contentParser: SectionParser): PRstNode =
-  ## Returns a generic rnDirective tree.
+  ## A helper proc that does main work for specific directive procs.
+  ## Always returns a generic rnDirective tree with these 3 children:
   ##
-  ## The children are rnDirArg, rnFieldList and rnLineBlock. Any might be nil.
-  result = parseDirective(p, flags)
-  if not isNil(contentParser) and indFollows(p):
-    pushInd(p, p.tok[p.idx].ival)
-    var content = contentParser(p)
-    popInd(p)
-    add(result, content)
+  ## 1) rnDirArg
+  ## 2) rnFieldList
+  ## 3) a node returned by `contentParser`.
+  ##
+  ## .. warning:: Any of the 3 children may be nil.
+  result = parseDirective(p, k, flags)
+  if not isNil(contentParser) and
+      parseBlockContent(p, result, contentParser):
+    discard "result is updated by parseBlockContent"
   else:
-    add(result, nil)
+    result.add(PRstNode(nil))
 
 proc parseDirBody(p: var RstParser, contentParser: SectionParser): PRstNode =
   if indFollows(p):
-    pushInd(p, p.tok[p.idx].ival)
+    pushInd(p, currentTok(p).ival)
     result = contentParser(p)
     popInd(p)
 
 proc dirInclude(p: var RstParser): PRstNode =
-  #
-  #The following options are recognized:
-  #
-  #start-after : text to find in the external data file
-  #    Only the content after the first occurrence of the specified text will
-  #    be included.
-  #end-before : text to find in the external data file
-  #    Only the content before the first occurrence of the specified text
-  #    (but after any after text) will be included.
+  ##
+  ## The following options are recognized:
+  ##
+  ## :start-after: text to find in the external data file
+  ##
+  ##     Only the content after the first occurrence of the specified
+  ##     text will be included. If text is not found inclusion will
+  ##     start from beginning of the file
+  ##
+  ## :end-before: text to find in the external data file
+  ##
+  ##     Only the content before the first occurrence of the specified
+  ##     text (but after any after text) will be included. If text is
+  ##     not found inclusion will happen until the end of the file.
   #literal : flag (empty)
   #    The entire included text is inserted into the document as a single
   #    literal block (useful for program listings).
@@ -1487,7 +3312,7 @@ proc dirInclude(p: var RstParser): PRstNode =
   #    encoding (if specified).
   #
   result = nil
-  var n = parseDirective(p, {hasArg, argIsFile, hasOptions}, nil)
+  var n = parseDirective(p, rnDirective, {hasArg, argIsFile, hasOptions}, nil)
   var filename = strip(addNodes(n.sons[0]))
   var path = p.findRelativeFile(filename)
   if path == "":
@@ -1496,16 +3321,41 @@ proc dirInclude(p: var RstParser): PRstNode =
     # XXX: error handling; recursive file inclusion!
     if getFieldValue(n, "literal") != "":
       result = newRstNode(rnLiteralBlock)
-      add(result, newRstNode(rnLeaf, readFile(path)))
+      result.add newLeaf(readFile(path))
     else:
+      let inputString = readFile(path)
+      let startPosition =
+        block:
+          let searchFor = n.getFieldValue("start-after").strip()
+          if searchFor != "":
+            let pos = inputString.find(searchFor)
+            if pos != -1: pos + searchFor.len
+            else: 0
+          else:
+            0
+
+      let endPosition =
+        block:
+          let searchFor = n.getFieldValue("end-before").strip()
+          if searchFor != "":
+            let pos = inputString.find(searchFor, start = startPosition)
+            if pos != -1: pos - 1
+            else: 0
+          else:
+            inputString.len - 1
+
       var q: RstParser
       initParser(q, p.s)
-      q.filename = path
-      q.col += getTokens(readFile(path), false, q.tok)
+      let saveFileIdx = p.s.currFileIdx
+      setCurrFilename(p.s, path)
+      getTokens(
+        inputString[startPosition..endPosition],
+        q.tok)
       # workaround a GCC bug; more like the interior pointer bug?
       #if find(q.tok[high(q.tok)].symbol, "\0\x01\x02") > 0:
       #  InternalError("Too many binary zeros in include file")
       result = parseDoc(q)
+      p.s.currFileIdx = saveFileIdx
 
 proc dirCodeBlock(p: var RstParser, nimExtension = false): PRstNode =
   ## Parses a code block.
@@ -1517,63 +3367,60 @@ proc dirCodeBlock(p: var RstParser, nimExtension = false): PRstNode =
   ## <http://docutils.sourceforge.net/docs/ref/rst/directives.html#code>`_ and
   ## the nim extension ``.. code-block::``. If the block is an extension, we
   ## want the default language syntax highlighting to be Nim, so we create a
-  ## fake internal field to comminicate with the generator. The field is named
+  ## fake internal field to communicate with the generator. The field is named
   ## ``default-language``, which is unlikely to collide with a field specified
   ## by any random rst input file.
   ##
   ## As an extension this proc will process the ``file`` extension field and if
   ## present will replace the code block with the contents of the referenced
-  ## file.
-  result = parseDirective(p, {hasArg, hasOptions}, parseLiteralBlock)
-  var filename = strip(getFieldValue(result, "file"))
-  if filename != "":
-    var path = p.findRelativeFile(filename)
-    if path == "": rstMessage(p, meCannotOpenFile, filename)
-    var n = newRstNode(rnLiteralBlock)
-    add(n, newRstNode(rnLeaf, readFile(path)))
-    result.sons[2] = n
+  ## file. This behaviour is disabled in sandboxed mode and can be re-enabled
+  ## with the `roSandboxDisabled` flag.
+  result = parseDirective(p, rnCodeBlock, {hasArg, hasOptions}, parseLiteralBlock)
+  mayLoadFile(p, result)
 
-  # Extend the field block if we are using our custom extension.
+  # Extend the field block if we are using our custom Nim extension.
   if nimExtension:
-    # Create a field block if the input block didn't have any.
-    if result.sons[1].isNil: result.sons[1] = newRstNode(rnFieldList)
-    assert result.sons[1].kind == rnFieldList
-    # Hook the extra field and specify the Nim language as value.
-    var extraNode = newRstNode(rnField)
-    extraNode.add(newRstNode(rnFieldName))
-    extraNode.add(newRstNode(rnFieldBody))
-    extraNode.sons[0].add(newRstNode(rnLeaf, "default-language"))
-    extraNode.sons[1].add(newRstNode(rnLeaf, "Nim"))
-    result.sons[1].add(extraNode)
-
-  result.kind = rnCodeBlock
+    defaultCodeLangNim(p, result)
 
 proc dirContainer(p: var RstParser): PRstNode =
-  result = parseDirective(p, {hasArg}, parseSectionWrapper)
-  assert(result.kind == rnDirective)
-  assert(len(result) == 3)
-  result.kind = rnContainer
+  result = parseDirective(p, rnContainer, {hasArg}, parseSectionWrapper)
+  assert(result.len == 3)
 
 proc dirImage(p: var RstParser): PRstNode =
-  result = parseDirective(p, {hasOptions, hasArg, argIsFile}, nil)
-  result.kind = rnImage
+  result = parseDirective(p, rnImage, {hasOptions, hasArg, argIsFile}, nil)
 
 proc dirFigure(p: var RstParser): PRstNode =
-  result = parseDirective(p, {hasOptions, hasArg, argIsFile},
+  result = parseDirective(p, rnFigure, {hasOptions, hasArg, argIsFile},
                           parseSectionWrapper)
-  result.kind = rnFigure
 
 proc dirTitle(p: var RstParser): PRstNode =
-  result = parseDirective(p, {hasArg}, nil)
-  result.kind = rnTitle
+  result = parseDirective(p, rnTitle, {hasArg}, nil)
 
 proc dirContents(p: var RstParser): PRstNode =
-  result = parseDirective(p, {hasArg}, nil)
-  result.kind = rnContents
+  result = parseDirective(p, rnContents, {hasArg}, nil)
+  p.s.hasToc = true
 
 proc dirIndex(p: var RstParser): PRstNode =
-  result = parseDirective(p, {}, parseSectionWrapper)
-  result.kind = rnIndex
+  result = parseDirective(p, rnIndex, {}, parseSectionWrapper)
+
+proc dirAdmonition(p: var RstParser, d: string): PRstNode =
+  result = parseDirective(p, rnAdmonition, {}, parseSectionWrapper)
+  result.adType = d
+
+proc dirDefaultRole(p: var RstParser): PRstNode =
+  result = parseDirective(p, rnDefaultRole, {hasArg}, nil)
+  if result.sons[0].len == 0: p.s.currRole = defaultRole(p.s.options)
+  else:
+    assert result.sons[0].sons[0].kind == rnLeaf
+    p.s.currRole = result.sons[0].sons[0].text
+  p.s.currRoleKind = whichRole(p, p.s.currRole)
+
+proc dirRole(p: var RstParser): PRstNode =
+  result = parseDirective(p, rnDirective, {hasArg, hasOptions}, nil)
+  # just check that language is supported, TODO: real role association
+  let lang = getFieldValue(result, "language").strip
+  if lang != "" and getSourceLanguage(lang) == langNone:
+    rstMessage(p, mwUnsupportedLanguage, lang)
 
 proc dirRawAux(p: var RstParser, result: var PRstNode, kind: RstNodeKind,
                contentParser: SectionParser) =
@@ -1585,10 +3432,10 @@ proc dirRawAux(p: var RstParser, result: var PRstNode, kind: RstNodeKind,
     else:
       var f = readFile(path)
       result = newRstNode(kind)
-      add(result, newRstNode(rnLeaf, f))
+      result.add newLeaf(f)
   else:
-    result.kind = kind
-    add(result, parseDirBody(p, contentParser))
+    result = newRstNode(kind, result.sons)
+    result.add(parseDirBody(p, contentParser))
 
 proc dirRaw(p: var RstParser): PRstNode =
   #
@@ -1599,7 +3446,7 @@ proc dirRaw(p: var RstParser): PRstNode =
   #
   # html
   # latex
-  result = parseDirective(p, {hasOptions, hasArg, argIsWord})
+  result = parseDirective(p, rnDirective, {hasOptions, hasArg, argIsWord})
   if result.sons[0] != nil:
     if cmpIgnoreCase(result.sons[0].sons[0].text, "html") == 0:
       dirRawAux(p, result, rnRawHtml, parseLiteralBlock)
@@ -1610,99 +3457,430 @@ proc dirRaw(p: var RstParser): PRstNode =
   else:
     dirRawAux(p, result, rnRaw, parseSectionWrapper)
 
+proc dirImportdoc(p: var RstParser): PRstNode =
+  result = parseDirective(p, rnDirective, {}, parseLiteralBlock)
+  assert result.sons[2].kind == rnLiteralBlock
+  assert result.sons[2].sons[0].kind == rnLeaf
+  let filenames: seq[string] = split(result.sons[2].sons[0].text, seps = {','})
+  proc rmSpaces(s: string): string = s.split.join("")
+  for origFilename in filenames:
+    p.s.idxImports[origFilename.rmSpaces] = ImportdocInfo(fromInfo: lineInfo(p))
+
+proc selectDir(p: var RstParser, d: string): PRstNode =
+  result = nil
+  let tok = p.tok[p.idx-2] # report on directive in ".. directive::"
+  if roSandboxDisabled notin p.s.options:
+    if d notin SandboxDirAllowlist:
+      rstMessage(p, meSandboxedDirective, d, tok.line, tok.col)
+
+  case d
+  of "admonition", "attention", "caution": result = dirAdmonition(p, d)
+  of "code": result = dirCodeBlock(p)
+  of "code-block": result = dirCodeBlock(p, nimExtension = true)
+  of "container": result = dirContainer(p)
+  of "contents": result = dirContents(p)
+  of "danger": result = dirAdmonition(p, d)
+  of "default-role": result = dirDefaultRole(p)
+  of "error": result = dirAdmonition(p, d)
+  of "figure": result = dirFigure(p)
+  of "hint": result = dirAdmonition(p, d)
+  of "image": result = dirImage(p)
+  of "important": result = dirAdmonition(p, d)
+  of "importdoc": result = dirImportdoc(p)
+  of "include": result = dirInclude(p)
+  of "index": result = dirIndex(p)
+  of "note": result = dirAdmonition(p, d)
+  of "raw":
+    if roSupportRawDirective in p.s.options:
+      result = dirRaw(p)
+    else:
+      rstMessage(p, meInvalidDirective, d)
+  of "role": result = dirRole(p)
+  of "tip": result = dirAdmonition(p, d)
+  of "title": result = dirTitle(p)
+  of "warning": result = dirAdmonition(p, d)
+  else:
+    rstMessage(p, meInvalidDirective, d, tok.line, tok.col)
+
 proc parseDotDot(p: var RstParser): PRstNode =
+  # parse "explicit markup blocks"
   result = nil
-  var col = p.tok[p.idx].col
-  inc(p.idx)
+  var n: PRstNode  # to store result, workaround for bug 16855
+  var col = currentTok(p).col
+  inc p.idx
   var d = getDirective(p)
   if d != "":
     pushInd(p, col)
-    case getDirKind(d)
-    of dkInclude: result = dirInclude(p)
-    of dkImage: result = dirImage(p)
-    of dkFigure: result = dirFigure(p)
-    of dkTitle: result = dirTitle(p)
-    of dkContainer: result = dirContainer(p)
-    of dkContents: result = dirContents(p)
-    of dkRaw:
-      if roSupportRawDirective in p.s.options:
-        result = dirRaw(p)
-      else:
-        rstMessage(p, meInvalidDirective, d)
-    of dkCode: result = dirCodeBlock(p)
-    of dkCodeBlock: result = dirCodeBlock(p, nimExtension = true)
-    of dkIndex: result = dirIndex(p)
-    else: rstMessage(p, meInvalidDirective, d)
+    result = selectDir(p, d)
     popInd(p)
   elif match(p, p.idx, " _"):
     # hyperlink target:
-    inc(p.idx, 2)
-    var a = getReferenceName(p, ":")
-    if p.tok[p.idx].kind == tkWhite: inc(p.idx)
+    inc p.idx, 2
+    var ending = ":"
+    if currentTok(p).symbol == "`":
+      inc p.idx
+      ending = "`"
+    var a = getReferenceName(p, ending)
+    if ending == "`":
+      if currentTok(p).symbol == ":":
+        inc p.idx
+      else:
+        rstMessage(p, meExpected, ":")
+    if currentTok(p).kind == tkWhite: inc p.idx
     var b = untilEol(p)
-    setRef(p, rstnodeToRefname(a), b)
+    if len(b) == 0:  # set internal anchor
+      p.curAnchors.add ManualAnchor(
+        alias: linkName(a), anchor: rstnodeToRefname(a), info: prevLineInfo(p)
+      )
+    else:  # external hyperlink
+      setRef(p, rstnodeToRefname(a), b, refType=hyperlinkAlias)
   elif match(p, p.idx, " |"):
     # substitution definitions:
-    inc(p.idx, 2)
+    inc p.idx, 2
     var a = getReferenceName(p, "|")
     var b: PRstNode
-    if p.tok[p.idx].kind == tkWhite: inc(p.idx)
-    if cmpIgnoreStyle(p.tok[p.idx].symbol, "replace") == 0:
-      inc(p.idx)
+    if currentTok(p).kind == tkWhite: inc p.idx
+    if cmpIgnoreStyle(currentTok(p).symbol, "replace") == 0:
+      inc p.idx
       expect(p, "::")
       b = untilEol(p)
-    elif cmpIgnoreStyle(p.tok[p.idx].symbol, "image") == 0:
-      inc(p.idx)
+    elif cmpIgnoreStyle(currentTok(p).symbol, "image") == 0:
+      inc p.idx
       b = dirImage(p)
     else:
-      rstMessage(p, meInvalidDirective, p.tok[p.idx].symbol)
+      rstMessage(p, meInvalidDirective, currentTok(p).symbol)
     setSub(p, addNodes(a), b)
-  elif match(p, p.idx, " ["):
-    # footnotes, citations
-    inc(p.idx, 2)
-    var a = getReferenceName(p, "]")
-    if p.tok[p.idx].kind == tkWhite: inc(p.idx)
-    var b = untilEol(p)
-    setRef(p, rstnodeToRefname(a), b)
+  elif match(p, p.idx, " [") and
+      (n = parseFootnote(p); n != nil):
+    result = n
   else:
-    result = parseComment(p)
-
-proc resolveSubs(p: var RstParser, n: PRstNode): PRstNode =
+    result = parseComment(p, col)
+
+proc rstParsePass1*(fragment: string,
+                    line, column: int,
+                    sharedState: PRstSharedState): PRstNode =
+  ## Parses an RST `fragment`.
+  ## The result should be further processed by
+  ## preparePass2_ and resolveSubs_ (which is pass 2).
+  var p: RstParser
+  initParser(p, sharedState)
+  p.line = line
+  p.col = column
+  getTokens(fragment, p.tok)
+  result = parseDoc(p)
+
+proc extractLinkEnd(x: string): string =
+  ## From links like `path/to/file.html#/%` extract `file.html#/%`.
+  let i = find(x, '#')
+  let last =
+    if i >= 0: i
+    else: x.len - 1
+  let j = rfind(x, '/', start=0, last=last)
+  if j >= 0:
+    result = x[j+1 .. ^1]
+  else:
+    result = x
+
+proc loadIdxFile(s: var PRstSharedState, origFilename: string) =
+  doAssert roSandboxDisabled in s.options
+  var info: TLineInfo
+  info.fileIndex = addFilename(s, origFilename)
+  var (dir, basename, ext) = origFilename.splitFile
+  if ext notin [".md", ".rst", ".nim", ""]:
+    rstMessage(s.filenames, s.msgHandler, s.idxImports[origFilename].fromInfo,
+               meCannotOpenFile, origFilename & ": unknown extension")
+  let idxFilename = dir / basename & ".idx"
+  let (idxPath, linkRelPath) = s.findRefFile(idxFilename)
+  s.idxImports[origFilename].linkRelPath = linkRelPath
+  var
+    fileEntries: seq[IndexEntry]
+    title: IndexEntry
+  try:
+    (fileEntries, title) = parseIdxFile(idxPath)
+  except IOError:
+    rstMessage(s.filenames, s.msgHandler, s.idxImports[origFilename].fromInfo,
+               meCannotOpenFile, idxPath)
+  except ValueError as e:
+    s.msgHandler(idxPath, LineRstInit, ColRstInit, meInvalidField, e.msg)
+
+  var isMarkup = false  # for sanity check to avoid mixing .md <-> .nim
+  for entry in fileEntries:
+    # Though target .idx already has inside it the path to HTML relative
+    # project's root, we won't rely on it and use `linkRelPath` instead.
+    let refn = extractLinkEnd(entry.link)
+    # select either markup (rst/md) or Nim cases:
+    if entry.kind in {ieMarkupTitle, ieNimTitle}:
+      s.idxImports[origFilename].title = entry.keyword
+    case entry.kind
+    of ieIdxRole, ieHeading, ieMarkupTitle:
+      if ext == ".nim" and entry.kind == ieMarkupTitle:
+        rstMessage(s, idxPath, meInvalidField,
+                   $ieMarkupTitle & " in supposedly .nim-derived file")
+      if entry.kind == ieMarkupTitle:
+        isMarkup = true
+      info.line = entry.line.uint16
+      addAnchorExtRst(s, key = entry.keyword, refn = refn,
+                      anchorType = headlineAnchor, info=info)
+    of ieNim, ieNimGroup, ieNimTitle:
+      if ext in [".md", ".rst"] or isMarkup:
+        rstMessage(s, idxPath, meInvalidField,
+                   $entry.kind & " in supposedly markup-derived file")
+      s.nimFileImported = true
+      var langSym: LangSymbol
+      if entry.kind in {ieNim, ieNimTitle}:
+        var q: RstParser
+        initParser(q, s)
+        info.line = entry.line.uint16
+        setLen(q.tok, 0)
+        q.idx = 0
+        getTokens(entry.linkTitle, q.tok)
+        var sons = newSeq[PRstNode](q.tok.len)
+        for i in 0 ..< q.tok.len: sons[i] = newLeaf(q.tok[i].symbol)
+        let linkTitle = newRstNode(rnInner, sons)
+        langSym = linkTitle.toLangSymbol
+      else:  # entry.kind == ieNimGroup
+        langSym = langSymbolGroup(kind=entry.linkTitle, name=entry.keyword)
+      addAnchorNim(s, external = true, refn = refn, tooltip = entry.linkDesc,
+                   langSym = langSym, priority = -4, # lowest
+                   info = info, module = info.fileIndex)
+  doAssert s.idxImports[origFilename].title != ""
+
+proc preparePass2*(s: var PRstSharedState, mainNode: PRstNode, importdoc = true) =
+  ## Records titles in node `mainNode` and orders footnotes.
+  countTitles(s, mainNode)
+  fixHeadlines(s)
+  orderFootnotes(s)
+  if importdoc:
+    for origFilename in s.idxImports.keys:
+      loadIdxFile(s, origFilename)
+
+proc resolveLink(s: PRstSharedState, n: PRstNode) : PRstNode =
+  # Associate this link alias with its target and change node kind to
+  # rnHyperlink or rnInternalRef appropriately.
+  var desc, alias: PRstNode
+  if n.kind == rnPandocRef:  # link like [desc][alias]
+    desc = n.sons[0]
+    alias = n.sons[1]
+  else:  # n.kind == rnRstRef, link like `desc=alias`_
+    desc = n
+    alias = n
+  type LinkDef = object
+    ar: AnchorRule
+    priority: int
+    tooltip: string
+    target: PRstNode
+    info: TLineInfo
+    externFilename: string
+      # when external anchor: origin filename where anchor was defined
+    isTitle: bool
+  proc cmp(x, y: LinkDef): int =
+    result = cmp(x.priority, y.priority)
+    if result == 0:
+      result = cmp(x.target, y.target)
+  var foundLinks: seq[LinkDef]
+  let refn = rstnodeToRefname(alias)
+  var hyperlinks = findRef(s, refn)
+  for y in hyperlinks:
+    foundLinks.add LinkDef(ar: arHyperlink, priority: refPriority(y.kind),
+                           target: y.value, info: y.info,
+                           tooltip: "(" & $y.kind & ")")
+  let substRst = findMainAnchorRst(s, alias.addNodes, n.info)
+  template getExternFilename(subst: AnchorSubst): string =
+    if subst.kind == arExternalRst or
+        (subst.kind == arNim and subst.external):
+      getFilename(s, subst)
+    else: ""
+  for subst in substRst:
+    var refname, fullRefname: string
+    if subst.kind == arInternalRst:
+      refname = subst.target.anchor
+      fullRefname = refname
+    else:  # arExternalRst
+      refname = subst.refnameExt
+      fullRefname = s.idxImports[getFilename(s, subst)].linkRelPath &
+                      "/" & refname
+    let anchorType =
+      if subst.kind == arInternalRst: subst.anchorType
+      else: subst.anchorTypeExt  # arExternalRst
+    foundLinks.add LinkDef(ar: subst.kind, priority: subst.priority,
+                           target: newLeaf(fullRefname),
+                           info: subst.info,
+                           externFilename: getExternFilename(subst),
+                           isTitle: isDocumentationTitle(refname),
+                           tooltip: "(" & $anchorType & ")")
+  # find anchors automatically generated from Nim symbols
+  if roNimFile in s.options or s.nimFileImported:
+    let substNim = findMainAnchorNim(s, signature=alias, n.info)
+    for subst in substNim:
+      let fullRefname =
+        if subst.external:
+          s.idxImports[getFilename(s, subst)].linkRelPath &
+              "/" & subst.refname
+        else: subst.refname
+      foundLinks.add LinkDef(ar: subst.kind, priority: subst.priority,
+                             target: newLeaf(fullRefname),
+                             externFilename: getExternFilename(subst),
+                             isTitle: isDocumentationTitle(subst.refname),
+                             info: subst.info, tooltip: subst.tooltip)
+  foundLinks.sort(cmp = cmp, order = Descending)
+  let aliasStr = addNodes(alias)
+  if foundLinks.len >= 1:
+    if foundLinks[0].externFilename != "":
+      s.idxImports[foundLinks[0].externFilename].used = true
+    let kind = if foundLinks[0].ar in {arHyperlink, arExternalRst}: rnHyperlink
+               elif foundLinks[0].ar == arNim:
+                 if foundLinks[0].externFilename == "": rnNimdocRef
+                 else: rnHyperlink
+               else: rnInternalRef
+    result = newRstNode(kind)
+    let documentName =  # filename without ext for `.nim`, title for `.md`
+      if foundLinks[0].ar == arNim:
+        changeFileExt(foundLinks[0].externFilename.extractFilename, "")
+      elif foundLinks[0].externFilename != "":
+        s.idxImports[foundLinks[0].externFilename].title
+      else: foundLinks[0].externFilename.extractFilename
+    let linkText =
+      if foundLinks[0].externFilename != "":
+        if foundLinks[0].isTitle: newLeaf(addNodes(desc))
+        else: newLeaf(documentName & ": " & addNodes(desc))
+      else:
+        newRstNode(rnInner, desc.sons)
+    result.sons = @[linkText, foundLinks[0].target]
+    if kind == rnNimdocRef: result.tooltip = foundLinks[0].tooltip
+    if foundLinks.len > 1:  # report ambiguous link
+      var targets = newSeq[string]()
+      for l in foundLinks:
+        var t = "    "
+        if s.filenames.len > 1:
+          t.add getFilename(s.filenames, l.info.fileIndex)
+        let n = l.info.line
+        let c = l.info.col + ColRstOffset
+        t.add "($1, $2): $3" % [$n, $c, l.tooltip]
+        targets.add t
+      rstMessage(s.filenames, s.msgHandler, n.info, mwAmbiguousLink,
+                 "`$1`\n  clash:\n$2" % [
+                   aliasStr, targets.join("\n")])
+  else:  # nothing found
+    result = n
+    rstMessage(s.filenames, s.msgHandler, n.info, mwBrokenLink, aliasStr)
+
+proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode =
+  ## Makes pass 2 of RST parsing.
+  ## Resolves substitutions and anchor aliases, groups footnotes.
+  ## Takes input node `n` and returns the same node with recursive
+  ## substitutions in `n.sons` to `result`.
   result = n
   if n == nil: return
   case n.kind
   of rnSubstitutionReferences:
-    var x = findSub(p, n)
+    var x = findSub(s, n)
     if x >= 0:
-      result = p.s.subs[x].value
+      result = s.subs[x].value
     else:
       var key = addNodes(n)
       var e = getEnv(key)
-      if e != "": result = newRstNode(rnLeaf, e)
-      else: rstMessage(p, mwUnknownSubstitution, key)
-  of rnRef:
-    var y = findRef(p, rstnodeToRefname(n))
-    if y != nil:
-      result = newRstNode(rnHyperlink)
-      n.kind = rnInner
-      add(result, n)
-      add(result, y)
+      if e != "": result = newLeaf(e)
+      else: rstMessage(s.filenames, s.msgHandler, n.info,
+                       mwUnknownSubstitution, key)
+  of rnRstRef, rnPandocRef:
+    result = resolveLink(s, n)
+  of rnFootnote:
+    var (fnType, num) = getFootnoteType(s, n.sons[0])
+    case fnType
+    of fnManualNumber, fnCitation:
+      discard "no need to alter fixed text"
+    of fnAutoNumberLabel, fnAutoNumber:
+      if fnType == fnAutoNumberLabel:
+        let labelR = rstnodeToRefname(n.sons[0])
+        num = getFootnoteNum(s, labelR)
+      else:
+        num = getFootnoteNum(s, n.order)
+      var nn = newRstNode(rnInner)
+      nn.add newLeaf($num)
+      result.sons[0] = nn
+    of fnAutoSymbol:
+      let sym = getAutoSymbol(s, n.order)
+      n.sons[0].sons[0].text = sym
+    n.sons[1] = resolveSubs(s, n.sons[1])
+  of rnFootnoteRef:
+    var (fnType, num) = getFootnoteType(s, n.sons[0])
+    template addLabel(number: int | string) =
+      var nn = newRstNode(rnInner)
+      nn.add newLeaf($number)
+      result.add(nn)
+    var refn = fnType.prefix
+    # create new rnFootnoteRef, add final label, and finalize target refn:
+    result = newRstNode(rnFootnoteRef, info = n.info)
+    case fnType
+    of fnManualNumber:
+      addLabel num
+      refn.add $num
+    of fnAutoNumber:
+      inc s.currFootnoteNumRef
+      addLabel getFootnoteNum(s, s.currFootnoteNumRef)
+      refn.add $s.currFootnoteNumRef
+    of fnAutoNumberLabel:
+      addLabel getFootnoteNum(s, rstnodeToRefname(n))
+      refn.add rstnodeToRefname(n)
+    of fnAutoSymbol:
+      inc s.currFootnoteSymRef
+      addLabel getAutoSymbol(s, s.currFootnoteSymRef)
+      refn.add $s.currFootnoteSymRef
+    of fnCitation:
+      result.add n.sons[0]
+      refn.add rstnodeToRefname(n)
+    # TODO: correctly report ambiguities
+    let anchorInfo = findMainAnchorRst(s, refn, n.info)
+    if anchorInfo.len != 0:
+      result.add newLeaf(anchorInfo[0].target.anchor)  # add link
+    else:
+      rstMessage(s.filenames, s.msgHandler, n.info, mwBrokenLink, refn)
+      result.add newLeaf(refn)  # add link
   of rnLeaf:
     discard
-  of rnContents:
-    p.hasToc = true
   else:
-    for i in countup(0, len(n) - 1): n.sons[i] = resolveSubs(p, n.sons[i])
+    var regroup = false
+    for i in 0 ..< n.len:
+      n.sons[i] = resolveSubs(s, n.sons[i])
+      if n.sons[i] != nil and n.sons[i].kind == rnFootnote:
+        regroup = true
+    if regroup:  # group footnotes together into rnFootnoteGroup
+      var newSons: seq[PRstNode]
+      var i = 0
+      while i < n.len:
+        if n.sons[i] != nil and n.sons[i].kind == rnFootnote:
+          var grp = newRstNode(rnFootnoteGroup)
+          while i < n.len and n.sons[i].kind == rnFootnote:
+            grp.sons.add n.sons[i]
+            inc i
+          newSons.add grp
+        else:
+          newSons.add n.sons[i]
+          inc i
+      result.sons = newSons
+
+proc completePass2*(s: PRstSharedState) =
+  for (filename, importdocInfo) in s.idxImports.pairs:
+    if not importdocInfo.used:
+      rstMessage(s.filenames, s.msgHandler, importdocInfo.fromInfo,
+                 mwUnusedImportdoc, filename)
 
 proc rstParse*(text, filename: string,
-               line, column: int, hasToc: var bool,
+               line, column: int,
                options: RstParseOptions,
                findFile: FindFileHandler = nil,
-               msgHandler: MsgHandler = nil): PRstNode =
-  var p: RstParser
-  initParser(p, newSharedState(options, findFile, msgHandler))
-  p.filename = filename
-  p.line = line
-  p.col = column + getTokens(text, roSkipPounds in options, p.tok)
-  result = resolveSubs(p, parseDoc(p))
-  hasToc = p.hasToc
+               findRefFile: FindRefFileHandler = nil,
+               msgHandler: MsgHandler = nil):
+              tuple[node: PRstNode, filenames: RstFileTable, hasToc: bool] =
+  ## Parses the whole `text`. The result is ready for `rstgen.renderRstToOut`,
+  ## note that 2nd tuple element should be fed to `initRstGenerator`
+  ## argument `filenames` (it is being filled here at least with `filename`
+  ## and possibly with other files from RST ``.. include::`` statement).
+  var sharedState = newRstSharedState(options, filename, findFile, findRefFile,
+                                      msgHandler, hasToc=false)
+  let unresolved = rstParsePass1(text, line, column, sharedState)
+  preparePass2(sharedState, unresolved)
+  result.node = resolveSubs(sharedState, unresolved)
+  completePass2(sharedState)
+  result.filenames = sharedState.filenames
+  result.hasToc = sharedState.hasToc
diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim
index f3596b571..2bbb0d0b8 100644
--- a/lib/packages/docutils/rstast.nim
+++ b/lib/packages/docutils/rstast.nim
@@ -9,20 +9,25 @@
 
 ## This module implements an AST for the `reStructuredText`:idx: parser.
 
-import strutils, json
+import std/[strutils, json]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 
 type
   RstNodeKind* = enum        ## the possible node kinds of an PRstNode
     rnInner,                  # an inner node or a root
     rnHeadline,               # a headline
     rnOverline,               # an over- and underlined headline
+    rnMarkdownHeadline,       # a Markdown headline
     rnTransition,             # a transition (the ------------- <hr> thingie)
     rnParagraph,              # a paragraph
     rnBulletList,             # a bullet list
     rnBulletItem,             # a bullet item
     rnEnumList,               # an enumerated list
     rnEnumItem,               # an enumerated item
-    rnDefList,                # a definition list
+    rnDefList, rnMdDefList,   # a definition list (RST/Markdown)
     rnDefItem,                # an item of a definition list consisting of ...
     rnDefName,                # ... a name part ...
     rnDefBody,                # ... and a body part ...
@@ -31,16 +36,28 @@ type
     rnFieldName,              # consisting of a field name ...
     rnFieldBody,              # ... and a field body
     rnOptionList, rnOptionListItem, rnOptionGroup, rnOption, rnOptionString,
-    rnOptionArgument, rnDescription, rnLiteralBlock, rnQuotedLiteralBlock,
+    rnOptionArgument, rnDescription, rnLiteralBlock,
+    rnMarkdownBlockQuote,     # a quote starting from punctuation like >>>
+    rnMarkdownBlockQuoteItem, # a quotation block, quote lines starting with
+                              # the same number of chars
     rnLineBlock,              # the | thingie
-    rnLineBlockItem,          # sons of the | thing
+    rnLineBlockItem,          # a son of rnLineBlock - one line inside it.
+                              # When `RstNode` lineIndent="\n" the line's empty
     rnBlockQuote,             # text just indented
-    rnTable, rnGridTable, rnTableRow, rnTableHeaderCell, rnTableDataCell,
-    rnLabel,                  # used for footnotes and other things
+    rnTable, rnGridTable, rnMarkdownTable, rnTableRow, rnTableHeaderCell, rnTableDataCell,
     rnFootnote,               # a footnote
-    rnCitation,               # similar to footnote
-    rnStandaloneHyperlink, rnHyperlink, rnRef, rnDirective, # a directive
-    rnDirArg, rnRaw, rnTitle, rnContents, rnImage, rnFigure, rnCodeBlock,
+    rnCitation,               # similar to footnote, so use rnFootnote instead
+    rnFootnoteGroup,          # footnote group - exists for a purely stylistic
+                              # reason: to display a few footnotes as 1 block
+    rnStandaloneHyperlink, rnHyperlink,
+    rnRstRef,                 # RST reference like `section name`_
+    rnPandocRef,              # Pandoc Markdown reference like [section name]
+    rnInternalRef, rnFootnoteRef,
+    rnNimdocRef,              # reference to automatically generated Nim symbol
+    rnDirective,              # a general directive
+    rnDirArg,                 # a directive argument (for some directives).
+                              # here are directives that are not rnDirective:
+    rnRaw, rnTitle, rnContents, rnImage, rnFigure, rnCodeBlock, rnAdmonition,
     rnRawHtml, rnRawLatex,
     rnContainer,              # ``container`` directive
     rnIndex,                  # index directve:
@@ -49,46 +66,103 @@ type
                               #     * `file#id <file#id>`_
                               #     * `file#id <file#id>'_
     rnSubstitutionDef,        # a definition of a substitution
-    rnGeneralRole,            # Inline markup:
+    # Inline markup:
+    rnInlineCode,             # interpreted text with code in a known language
+    rnCodeFragment,           # inline code for highlighting with the specified
+                              # class (which cannot be inferred from context)
+    rnUnknownRole,            # interpreted text with an unknown role
     rnSub, rnSup, rnIdx,
     rnEmphasis,               # "*"
     rnStrongEmphasis,         # "**"
     rnTripleEmphasis,         # "***"
-    rnInterpretedText,        # "`"
+    rnInterpretedText,        # "`" an auxiliary role for parsing that will
+                              # be converted into other kinds like rnInlineCode
     rnInlineLiteral,          # "``"
+    rnInlineTarget,           # "_`target`"
     rnSubstitutionReferences, # "|"
     rnSmiley,                 # some smiley
+    rnDefaultRole,            # .. default-role:: code
     rnLeaf                    # a leaf; the node's text field contains the
                               # leaf val
 
+  FileIndex* = distinct int32
+  TLineInfo* = object
+    line*: uint16
+    col*: int16
+    fileIndex*: FileIndex
 
   PRstNode* = ref RstNode    ## an RST node
   RstNodeSeq* = seq[PRstNode]
-  RstNode* {.acyclic, final.} = object ## an RST node's description
-    kind*: RstNodeKind       ## the node's kind
-    text*: string             ## valid for leafs in the AST; and the title of
-                              ## the document or the section
-    level*: int               ## valid for some node kinds
+  RstNode* {.acyclic, final.} = object ## AST node (result of RST parsing)
+    case kind*: RstNodeKind ## the node's kind
+    of rnLeaf, rnSmiley:
+      text*: string           ## string that is expected to be displayed
+    of rnEnumList:
+      labelFmt*: string       ## label format like "(1)"
+    of rnLineBlockItem:
+      lineIndent*: string     ## a few spaces or newline at the line beginning
+    of rnAdmonition:
+      adType*: string         ## admonition type: "note", "caution", etc. This
+                              ## text will set the style and also be displayed
+    of rnOverline, rnHeadline, rnMarkdownHeadline:
+      level*: int             ## level of headings starting from 1 (main
+                              ## chapter) to larger ones (minor sub-sections)
+                              ## level=0 means it's document title or subtitle
+    of rnFootnote, rnCitation, rnOptionListItem:
+      order*: int             ## footnote order (for auto-symbol footnotes and
+                              ## auto-numbered ones without a label)
+    of rnMarkdownBlockQuoteItem:
+      quotationDepth*: int    ## number of characters in line prefix
+    of rnRstRef, rnPandocRef, rnSubstitutionReferences,
+        rnInterpretedText, rnField, rnInlineCode, rnCodeBlock, rnFootnoteRef:
+      info*: TLineInfo        ## To have line/column info for warnings at
+                              ## nodes that are post-processed after parsing
+    of rnNimdocRef:
+      tooltip*: string
+    of rnTable, rnGridTable, rnMarkdownTable:
+      colCount*: int          ## Number of (not-united) cells in the table
+    of rnTableRow:
+      endsHeader*: bool       ## Is last row in the header of table?
+    of rnTableHeaderCell, rnTableDataCell:
+      span*: int              ## Number of table columns that the cell occupies
+    else:
+      discard
+    anchor*: string           ## anchor, internal link target
+                              ## (aka HTML id tag, aka Latex label/hypertarget)
     sons*: RstNodeSeq        ## the node's sons
 
+proc `==`*(a, b: FileIndex): bool {.borrow.}
+
 proc len*(n: PRstNode): int =
   result = len(n.sons)
 
-proc newRstNode*(kind: RstNodeKind): PRstNode =
-  new(result)
-  result.sons = @[]
-  result.kind = kind
+proc newRstNode*(kind: RstNodeKind, sons: seq[PRstNode] = @[],
+                 anchor = ""): PRstNode =
+  result = PRstNode(kind: kind, sons: sons, anchor: anchor)
 
-proc newRstNode*(kind: RstNodeKind, s: string): PRstNode =
+proc newRstNode*(kind: RstNodeKind, info: TLineInfo,
+                 sons: seq[PRstNode] = @[]): PRstNode =
+  result = PRstNode(kind: kind, sons: sons)
+  result.info = info
+
+proc newRstNode*(kind: RstNodeKind, s: string): PRstNode {.deprecated.} =
+  assert kind in {rnLeaf, rnSmiley}
   result = newRstNode(kind)
   result.text = s
 
+proc newRstLeaf*(s: string): PRstNode =
+  result = newRstNode(rnLeaf)
+  result.text = s
+
 proc lastSon*(n: PRstNode): PRstNode =
   result = n.sons[len(n.sons)-1]
 
 proc add*(father, son: PRstNode) =
   add(father.sons, son)
 
+proc add*(father: PRstNode; s: string) =
+  add(father.sons, newRstLeaf(s))
+
 proc addIfNotNil*(father, son: PRstNode) =
   if son != nil: add(father, son)
 
@@ -210,7 +284,7 @@ proc renderRstToRst(d: var RenderContext, n: PRstNode, result: var string) =
     inc(d.indent, 2)
     renderRstSons(d, n, result)
     dec(d.indent, 2)
-  of rnRef:
+  of rnRstRef:
     result.add("`")
     renderRstSons(d, n, result)
     result.add("`_")
@@ -220,7 +294,7 @@ proc renderRstToRst(d: var RenderContext, n: PRstNode, result: var string) =
     result.add(" <")
     renderRstToRst(d, n.sons[1], result)
     result.add(">`_")
-  of rnGeneralRole:
+  of rnUnknownRole:
     result.add('`')
     renderRstToRst(d, n.sons[0],result)
     result.add("`:")
@@ -293,9 +367,9 @@ proc renderRstToJsonNode(node: PRstNode): JsonNode =
       (key: "kind", val: %($node.kind)),
       (key: "level", val: %BiggestInt(node.level))
      ]
-  if node.text != nil:
+  if node.kind in {rnLeaf, rnSmiley} and node.text.len > 0:
     result.add("text", %node.text)
-  if node.sons != nil and len(node.sons) > 0:
+  if len(node.sons) > 0:
     var accm = newSeq[JsonNode](len(node.sons))
     for i, son in node.sons:
       accm[i] = renderRstToJsonNode(son)
@@ -303,11 +377,68 @@ proc renderRstToJsonNode(node: PRstNode): JsonNode =
 
 proc renderRstToJson*(node: PRstNode): string =
   ## Writes the given RST node as JSON that is in the form
-  ## ::
-  ##   {
-  ##     "kind":string node.kind,
-  ##     "text":optional string node.text,
-  ##     "level":optional int node.level,
-  ##     "sons":optional node array
-  ##   }
+  ##
+  ##     {
+  ##       "kind":string node.kind,
+  ##       "text":optional string node.text,
+  ##       "level":optional int node.level,
+  ##       "sons":optional node array
+  ##     }
   renderRstToJsonNode(node).pretty
+
+proc renderRstToText*(node: PRstNode): string =
+  ## minimal text representation of markup node
+  const code = {rnCodeFragment, rnInterpretedText, rnInlineLiteral, rnInlineCode}
+  if node == nil:
+    return ""
+  case node.kind
+  of rnLeaf, rnSmiley:
+    result.add node.text
+  else:
+    if node.kind in code: result.add "`"
+    for i in 0 ..< node.sons.len:
+      if node.kind in {rnInlineCode, rnCodeBlock} and i == 0:
+        continue  # omit language specifier
+      result.add renderRstToText(node.sons[i])
+    if node.kind in code: result.add "`"
+
+proc treeRepr*(node: PRstNode, indent=0): string =
+  ## Writes the parsed RST `node` into an AST tree with compact string
+  ## representation in the format (one line per every sub-node):
+  ## ``indent - kind - [text|level|order|adType] - anchor (if non-zero)``
+  ## (suitable for debugging of RST parsing).
+  if node == nil:
+    result.add " ".repeat(indent) & "[nil]\n"
+    return
+  result.add " ".repeat(indent) & $node.kind
+  case node.kind
+  of rnLeaf, rnSmiley:
+    result.add (if node.text == "": "" else: "  '" & node.text & "'")
+  of rnEnumList:
+    result.add "  labelFmt=" & node.labelFmt
+  of rnLineBlockItem:
+    var txt: string
+    if node.lineIndent == "\n": txt = "  (blank line)"
+    else: txt = "  lineIndent=" & $node.lineIndent.len
+    result.add txt
+  of rnAdmonition:
+    result.add "  adType=" & node.adType
+  of rnHeadline, rnOverline, rnMarkdownHeadline:
+    result.add "  level=" & $node.level
+  of rnFootnote, rnCitation, rnOptionListItem:
+    result.add (if node.order == 0:   "" else: "  order=" & $node.order)
+  of rnMarkdownBlockQuoteItem:
+    result.add "  quotationDepth=" & $node.quotationDepth
+  of rnTable, rnGridTable, rnMarkdownTable:
+    result.add "  colCount=" & $node.colCount
+  of rnTableHeaderCell, rnTableDataCell:
+    if node.span > 0:
+      result.add "  span=" & $node.span
+  of rnTableRow:
+    if node.endsHeader: result.add "  endsHeader"
+  else:
+    discard
+  result.add (if node.anchor == "": "" else: "  anchor='" & node.anchor & "'")
+  result.add "\n"
+  for son in node.sons:
+    result.add treeRepr(son, indent=indent+2)
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim
index ef456f093..7fc0ac03a 100644
--- a/lib/packages/docutils/rstgen.nim
+++ b/lib/packages/docutils/rstgen.nim
@@ -22,9 +22,33 @@
 ## other lower level methods to finally build complete documents. This requires
 ## many options and tweaking, but you are not limited to snippets and can
 ## generate `LaTeX documents <https://en.wikipedia.org/wiki/LaTeX>`_ too.
+##
+## `Docutils configuration files`_ are not supported. Instead HTML generation
+## can be tweaked by editing file ``config/nimdoc.cfg``.
+##
+## .. _Docutils configuration files: https://docutils.sourceforge.io/docs/user/config.htm
+##
+## There are stylistic difference between how this module renders some elements
+## and how original Python Docutils does:
+##
+## * Backreferences to TOC in section headings are not generated.
+##   In HTML each section is also a link that points to the section itself:
+##   this is done for user to be able to copy the link into clipboard.
+##
+## * The same goes for footnotes/citations links: they point to themselves.
+##   No backreferences are generated since finding all references of a footnote
+##   can be done by simply searching for ``[footnoteName]``.
+
+import std/[strutils, os, hashes, strtabs, tables, sequtils,
+  algorithm, parseutils, strbasics]
 
-import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils,
-  algorithm, parseutils
+import rstast, rst, rstidx, highlite
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio, formatfloat]
+
+
+import ../../std/private/since
 
 const
   HtmlExt = "html"
@@ -35,25 +59,27 @@ type
     outHtml,            # output is HTML
     outLatex            # output is Latex
 
-  TocEntry = object
-    n*: PRstNode
-    refname*, header*: string
-
   MetaEnum* = enum
-    metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion
+    metaNone, metaTitleRaw, metaTitle, metaSubtitle, metaAuthor, metaVersion
+
+  EscapeMode* = enum  # in Latex text inside options [] and URLs is
+                      # escaped slightly differently than in normal text
+    emText, emOption, emUrl  # emText is currently used for code also
 
   RstGenerator* = object of RootObj
     target*: OutputTarget
     config*: StringTableRef
     splitAfter*: int          # split too long entries in the TOC
     listingCounter*: int
-    tocPart*: seq[TocEntry]
+    tocPart*: seq[PRstNode]   # headings for Table of Contents
     hasToc*: bool
     theIndex: string # Contents of the index file to be dumped at the end.
-    options*: RstParseOptions
     findFile*: FindFileHandler
     msgHandler*: MsgHandler
-    filename*: string
+    outDir*: string      ## output directory, initialized by docgen.nim
+    destFile*: string    ## output (HTML) file, initialized by docgen.nim
+    filenames*: RstFileTable
+    filename*: string         ## source Nim or Rst file
     meta*: array[MetaEnum, string]
     currentSection: string ## \
     ## Stores the empty string or the last headline/overline found in the rst
@@ -63,7 +89,9 @@ type
     ## for hyperlinks. See renderIndexTerm proc for details.
     id*: int               ## A counter useful for generating IDs.
     onTestSnippet*: proc (d: var RstGenerator; filename, cmd: string; status: int;
-                          content: string)
+                          content: string) {.gcsafe.}
+    escMode*: EscapeMode
+    curQuotationDepth: int
 
   PDoc = var RstGenerator ## Alias to type less.
 
@@ -76,6 +104,9 @@ type
     testCmd: string
     status: int
 
+proc prettyLink*(file: string): string =
+  changeFileExt(file, "").replace("_._", "..")
+
 proc init(p: var CodeBlockParams) =
   ## Default initialisation of CodeBlockParams to sane values.
   p.startLine = 1
@@ -84,9 +115,10 @@ proc init(p: var CodeBlockParams) =
 
 proc initRstGenerator*(g: var RstGenerator, target: OutputTarget,
                        config: StringTableRef, filename: string,
-                       options: RstParseOptions,
-                       findFile: FindFileHandler=nil,
-                       msgHandler: MsgHandler=nil) =
+                       findFile: FindFileHandler = nil,
+                       msgHandler: MsgHandler = nil,
+                       filenames = default(RstFileTable),
+                       hasToc = false) =
   ## Initializes a ``RstGenerator``.
   ##
   ## You need to call this before using a ``RstGenerator`` with any other
@@ -99,11 +131,11 @@ proc initRstGenerator*(g: var RstGenerator, target: OutputTarget,
   ## index hyperlinks to the file, but you can pass an empty string here if you
   ## are parsing a stream in memory. If `filename` ends with the ``.nim``
   ## extension, the title for the document will be set by default to ``Module
-  ## filename``.  This default title can be overriden by the embedded rst, but
+  ## filename``.  This default title can be overridden by the embedded rst, but
   ## it helps to prettify the generated index if no title is found.
   ##
   ## The ``RstParseOptions``, ``FindFileHandler`` and ``MsgHandler`` types
-  ## are defined in the the `packages/docutils/rst module <rst.html>`_.
+  ## are defined in the `packages/docutils/rst module <rst.html>`_.
   ## ``options`` selects the behaviour of the rst parser.
   ##
   ## ``findFile`` is a proc used by the rst ``include`` directive among others.
@@ -122,22 +154,25 @@ proc initRstGenerator*(g: var RstGenerator, target: OutputTarget,
   ##
   ## Example:
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   import packages/docutils/rstgen
   ##
   ##   var gen: RstGenerator
   ##   gen.initRstGenerator(outHtml, defaultConfig(), "filename", {})
+  ##   ```
   g.config = config
   g.target = target
   g.tocPart = @[]
+  g.hasToc = hasToc
   g.filename = filename
+  g.filenames = filenames
   g.splitAfter = 20
   g.theIndex = ""
-  g.options = options
   g.findFile = findFile
   g.currentSection = ""
   g.id = 0
+  g.escMode = emText
+  g.curQuotationDepth = 0
   let fileParts = filename.splitFile
   if fileParts.ext == ".nim":
     g.currentSection = "Module " & fileParts.name
@@ -152,10 +187,13 @@ proc writeIndexFile*(g: var RstGenerator, outfile: string) =
   ## Writes the current index buffer to the specified output file.
   ##
   ## You previously need to add entries to the index with the `setIndexTerm()
-  ## <#setIndexTerm>`_ proc. If the index is empty the file won't be created.
+  ## <#setIndexTerm,RstGenerator,string,string,string,string,string>`_ proc.
+  ## If the index is empty the file won't be created.
   if g.theIndex.len > 0: writeFile(outfile, g.theIndex)
 
-proc addXmlChar(dest: var string, c: char) =
+proc addHtmlChar(dest: var string, c: char) =
+  # Escapes HTML characters. Note that single quote ' is not escaped as
+  # &apos; -- unlike XML (for standards pre HTML5 it was even forbidden).
   case c
   of '&': add(dest, "&amp;")
   of '<': add(dest, "&lt;")
@@ -163,35 +201,36 @@ proc addXmlChar(dest: var string, c: char) =
   of '\"': add(dest, "&quot;")
   else: add(dest, c)
 
-proc addRtfChar(dest: var string, c: char) =
-  case c
-  of '{': add(dest, "\\{")
-  of '}': add(dest, "\\}")
-  of '\\': add(dest, "\\\\")
-  else: add(dest, c)
-
-proc addTexChar(dest: var string, c: char) =
+proc addTexChar(dest: var string, c: char, escMode: EscapeMode) =
+  ## Escapes 10 special Latex characters and sometimes ` and [, ].
+  ## TODO: @ is always a normal symbol (besides the header), am I wrong?
+  ## All escapes that need to work in text and code blocks (`emText` mode)
+  ## should start from \ (to be compatible with fancyvrb/fvextra).
   case c
-  of '_': add(dest, "\\_")
-  of '{': add(dest, "\\symbol{123}")
-  of '}': add(dest, "\\symbol{125}")
-  of '[': add(dest, "\\symbol{91}")
-  of ']': add(dest, "\\symbol{93}")
-  of '\\': add(dest, "\\symbol{92}")
-  of '$': add(dest, "\\$")
-  of '&': add(dest, "\\&")
-  of '#': add(dest, "\\#")
-  of '%': add(dest, "\\%")
-  of '~': add(dest, "\\symbol{126}")
-  of '@': add(dest, "\\symbol{64}")
-  of '^': add(dest, "\\symbol{94}")
-  of '`': add(dest, "\\symbol{96}")
+  of '_', '&', '#', '%': add(dest, "\\" & c)
+  # commands \label and \pageref don't accept \$ by some reason but OK with $:
+  of '$': (if escMode == emUrl: add(dest, c) else: add(dest, "\\" & c))
+  # \~ and \^ have a special meaning unless they are followed by {}
+  of '~', '^': add(dest, "\\" & c & "{}")
+  # Latex loves to substitute ` to opening quote, even in texttt mode!
+  of '`': add(dest, "\\textasciigrave{}")
+  # add {} to avoid gobbling up space by \textbackslash
+  of '\\': add(dest, "\\textbackslash{}")
+  # Using { and } in URL in Latex: https://tex.stackexchange.com/a/469175
+  of '{':
+    add(dest, if escMode == emUrl: "\\%7B" else: "\\{")
+  of '}':
+    add(dest, if escMode == emUrl: "\\%7D" else: "\\}")
+  of ']':
+    # escape ] inside an optional argument in e.g. \section[static[T]]{..
+    add(dest, if escMode == emOption: "\\text{]}" else: "]")
   else: add(dest, c)
 
-proc escChar*(target: OutputTarget, dest: var string, c: char) {.inline.} =
+proc escChar*(target: OutputTarget, dest: var string,
+              c: char, escMode: EscapeMode) {.inline.} =
   case target
-  of outHtml:  addXmlChar(dest, c)
-  of outLatex: addTexChar(dest, c)
+  of outHtml:  addHtmlChar(dest, c)
+  of outLatex: addTexChar(dest, c, escMode)
 
 proc addSplitter(target: OutputTarget; dest: var string) {.inline.} =
   case target
@@ -210,7 +249,7 @@ proc nextSplitPoint*(s: string, start: int): int =
     inc(result)
   dec(result)                 # last valid index
 
-proc esc*(target: OutputTarget, s: string, splitAfter = -1): string =
+proc esc*(target: OutputTarget, s: string, splitAfter = -1, escMode = emText): string =
   ## Escapes the HTML.
   result = ""
   if splitAfter >= 0:
@@ -221,11 +260,11 @@ proc esc*(target: OutputTarget, s: string, splitAfter = -1): string =
       #if (splitter != " ") or (partLen + k - j + 1 > splitAfter):
       partLen = 0
       addSplitter(target, result)
-      for i in countup(j, k): escChar(target, result, s[i])
+      for i in countup(j, k): escChar(target, result, s[i], escMode)
       inc(partLen, k - j + 1)
       j = k + 1
   else:
-    for i in countup(0, len(s) - 1): escChar(target, result, s[i])
+    for i in countup(0, len(s) - 1): escChar(target, result, s[i], escMode)
 
 
 proc disp(target: OutputTarget, xml, tex: string): string =
@@ -243,64 +282,58 @@ proc dispA(target: OutputTarget, dest: var string,
   else: addf(dest, tex, args)
 
 proc `or`(x, y: string): string {.inline.} =
-  result = if x.isNil: y else: x
+  result = if x.len == 0: y else: x
 
-proc renderRstToOut*(d: var RstGenerator, n: PRstNode, result: var string)
+proc renderRstToOut*(d: var RstGenerator, n: PRstNode, result: var string) {.gcsafe.}
   ## Writes into ``result`` the rst ast ``n`` using the ``d`` configuration.
   ##
   ## Before using this proc you need to initialise a ``RstGenerator`` with
   ## ``initRstGenerator`` and parse a rst file with ``rstParse`` from the
   ## `packages/docutils/rst module <rst.html>`_. Example:
-  ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   # ...configure gen and rst vars...
-  ##   var generatedHTML = ""
-  ##   renderRstToOut(gen, rst, generatedHTML)
-  ##   echo generatedHTML
+  ##   var generatedHtml = ""
+  ##   renderRstToOut(gen, rst, generatedHtml)
+  ##   echo generatedHtml
+  ##   ```
 
 proc renderAux(d: PDoc, n: PRstNode, result: var string) =
   for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], result)
 
-proc renderAux(d: PDoc, n: PRstNode, frmtA, frmtB: string, result: var string) =
+template idS(txt: string): string =
+  if txt == "": ""
+  else:
+    case d.target
+    of outHtml:
+      " id=\"" & txt & "\""
+    of outLatex:
+      "\\label{" & txt & "}\\hypertarget{" & txt & "}{}"
+        # we add \label for page number references via \pageref, while
+        # \hypertarget is for clickable links via \hyperlink.
+
+proc renderAux(d: PDoc, n: PRstNode, html, tex: string, result: var string) =
+  # formats sons of `n` as substitution variable $1 inside strings `html` and
+  # `tex`, internal target (anchor) is provided as substitute $2.
   var tmp = ""
   for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], tmp)
-  if d.target != outLatex:
-    result.addf(frmtA, [tmp])
-  else:
-    result.addf(frmtB, [tmp])
+  case d.target
+  of outHtml:  result.addf(html, [tmp, n.anchor.idS])
+  of outLatex: result.addf(tex,  [tmp, n.anchor.idS])
 
 # ---------------- index handling --------------------------------------------
 
-proc quoteIndexColumn(text: string): string =
-  ## Returns a safe version of `text` for serialization to the ``.idx`` file.
-  ##
-  ## The returned version can be put without worries in a line based tab
-  ## separated column text file. The following character sequence replacements
-  ## will be performed for that goal:
-  ##
-  ## * ``"\\"`` => ``"\\\\"``
-  ## * ``"\n"`` => ``"\\n"``
-  ## * ``"\t"`` => ``"\\t"``
-  result = text.replace("\\", "\\\\").replace("\n", "\\n").replace("\t", "\\t")
-
-proc unquoteIndexColumn(text: string): string =
-  ## Returns the unquoted version generated by ``quoteIndexColumn``.
-  result = text.replace("\\t", "\t").replace("\\n", "\n").replace("\\\\", "\\")
-
-proc setIndexTerm*(d: var RstGenerator, id, term: string,
-                   linkTitle, linkDesc = "") =
+proc setIndexTerm*(d: var RstGenerator; k: IndexEntryKind, htmlFile, id, term: string,
+                   linkTitle, linkDesc = "", line = 0) =
   ## Adds a `term` to the index using the specified hyperlink identifier.
   ##
   ## A new entry will be added to the index using the format
-  ## ``term<tab>file#id``. The file part will come from the `filename`
-  ## parameter used in a previous call to the `initRstGenerator()
-  ## <#initRstGenerator>`_ proc.
+  ## ``term<tab>file#id``. The file part will come from the `htmlFile`
+  ## parameter.
   ##
   ## The `id` will be appended with a hash character only if its length is not
   ## zero, otherwise no specific anchor will be generated. In general you
   ## should only pass an empty `id` value for the title of standalone rst
-  ## documents (they are special for the `mergeIndexes() <#mergeIndexes>`_
+  ## documents (they are special for the `mergeIndexes() <#mergeIndexes,string>`_
   ## proc, see `Index (idx) file format <docgen.html#index-idx-file-format>`_
   ## for more information). Unlike other index terms, title entries are
   ## inserted at the beginning of the accumulated buffer to maintain a logical
@@ -310,25 +343,11 @@ proc setIndexTerm*(d: var RstGenerator, id, term: string,
   ## columns with their contents will be added.
   ##
   ## The index won't be written to disk unless you call `writeIndexFile()
-  ## <#writeIndexFile>`_. The purpose of the index is documented in the `docgen
-  ## tools guide <docgen.html#index-switch>`_.
-  assert(not d.theIndex.isNil)
-  var
-    entry = term
-    isTitle = false
-  entry.add('\t')
-  let htmlFile = changeFileExt(extractFilename(d.filename), HtmlExt)
-  entry.add(htmlFile)
-  if id.len > 0:
-    entry.add('#')
-    entry.add(id)
-  else:
-    isTitle = true
-  if linkTitle.len > 0 or linkDesc.len > 0:
-    entry.add('\t' & linkTitle.quoteIndexColumn)
-    entry.add('\t' & linkDesc.quoteIndexColumn)
-  entry.add("\n")
-
+  ## <#writeIndexFile,RstGenerator,string>`_. The purpose of the index is
+  ## documented in the `docgen tools guide
+  ## <docgen.html#related-options-index-switch>`_.
+  let (entry, isTitle) = formatIndexEntry(k, htmlFile, id, term,
+                                          linkTitle, linkDesc, line)
   if isTitle: d.theIndex.insert(entry)
   else: d.theIndex.add(entry)
 
@@ -337,14 +356,23 @@ proc hash(n: PRstNode): int =
     result = hash(n.text)
   elif n.len > 0:
     result = hash(n.sons[0])
-    for i in 1 .. <len(n):
+    for i in 1 ..< len(n):
       result = result !& hash(n.sons[i])
     result = !$result
 
+proc htmlFileRelPath(d: PDoc): string =
+  if d.outDir.len == 0:
+    # /foo/bar/zoo.nim -> zoo.html
+    changeFileExt(extractFilename(d.filename), HtmlExt)
+  else: # d is initialized in docgen.nim
+    # outDir   = /foo              -\
+    # destFile = /foo/bar/zoo.html -|-> bar/zoo.html
+    d.destFile.relativePath(d.outDir, '/')
+
 proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) =
   ## Renders the string decorated within \`foobar\`\:idx\: markers.
   ##
-  ## Additionally adds the encosed text to the index as a term. Since we are
+  ## Additionally adds the enclosed text to the index as a term. Since we are
   ## interested in different instances of the same term to have different
   ## entries, a table is used to keep track of the amount of times a term has
   ## previously appeared to give a different identifier value for each.
@@ -357,51 +385,31 @@ proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) =
 
   var term = ""
   renderAux(d, n, term)
-  setIndexTerm(d, id, term, d.currentSection)
-  dispA(d.target, result, "<span id=\"$1\">$2</span>", "$2\\label{$1}",
+  setIndexTerm(d, ieIdxRole,
+  htmlFileRelPath(d), id, term, d.currentSection)
+  dispA(d.target, result, "<span id=\"$1\">$2</span>", "\\nimindexterm{$1}{$2}",
         [id, term])
 
 type
-  IndexEntry = object
-    keyword: string
-    link: string
-    linkTitle: string ## If not nil, contains a prettier text for the href
-    linkDesc: string ## If not nil, the title attribute of the final href
-
-  IndexedDocs = Table[IndexEntry, seq[IndexEntry]] ## \
+  IndexedDocs* = Table[IndexEntry, seq[IndexEntry]] ## \
     ## Contains the index sequences for doc types.
     ##
     ## The key is a *fake* IndexEntry which will contain the title of the
     ## document in the `keyword` field and `link` will contain the html
-    ## filename for the document. `linkTitle` and `linkDesc` will be nil.
+    ## filename for the document. `linkTitle` and `linkDesc` will be empty.
     ##
     ## The value indexed by this IndexEntry is a sequence with the real index
     ## entries found in the ``.idx`` file.
 
-proc cmp(a, b: IndexEntry): int =
-  ## Sorts two ``IndexEntry`` first by `keyword` field, then by `link`.
-  result = cmpIgnoreStyle(a.keyword, b.keyword)
-  if result == 0:
-    result = cmpIgnoreStyle(a.link, b.link)
-
-proc hash(x: IndexEntry): Hash =
-  ## Returns the hash for the combined fields of the type.
-  ##
-  ## The hash is computed as the chained hash of the individual string hashes.
-  assert(not x.keyword.isNil)
-  assert(not x.link.isNil)
-  result = x.keyword.hash !& x.link.hash
-  result = result !& (x.linkTitle or "").hash
-  result = result !& (x.linkDesc or "").hash
-  result = !$result
-
-proc `<-`(a: var IndexEntry, b: IndexEntry) =
-  shallowCopy a.keyword, b.keyword
-  shallowCopy a.link, b.link
-  if b.linkTitle.isNil: a.linkTitle = nil
-  else: shallowCopy a.linkTitle, b.linkTitle
-  if b.linkDesc.isNil: a.linkDesc = nil
-  else: shallowCopy a.linkDesc, b.linkDesc
+when defined(gcDestructors):
+  template `<-`(a, b: var IndexEntry) = a = move(b)
+else:
+  proc `<-`(a: var IndexEntry, b: IndexEntry) =
+    shallowCopy a.keyword, b.keyword
+    shallowCopy a.link, b.link
+    shallowCopy a.linkTitle, b.linkTitle
+    shallowCopy a.linkDesc, b.linkDesc
+    shallowCopy a.module, b.module
 
 proc sortIndex(a: var openArray[IndexEntry]) =
   # we use shellsort here; fast and simple
@@ -424,11 +432,15 @@ proc sortIndex(a: var openArray[IndexEntry]) =
     if h == 1: break
 
 proc escapeLink(s: string): string =
+  ## This proc is mostly copied from uri/encodeUrl except that
+  ## these chars are also left unencoded: '#', '/'.
   result = newStringOfCap(s.len + s.len shr 2)
   for c in items(s):
     case c
-    of 'a'..'z', '_', 'A'..'Z', '0'..'9', '.', '#', ',', '/':
-      result.add c
+    of 'a'..'z', 'A'..'Z', '0'..'9', '-', '.', '_', '~': # same as that in uri/encodeUrl
+      add(result, c)
+    of '#', '/': # example.com/foo/#bar (don't escape the '/' and '#' in such links)
+      add(result, c)
     else:
       add(result, "%")
       add(result, toHex(ord(c), 2))
@@ -437,41 +449,39 @@ proc generateSymbolIndex(symbols: seq[IndexEntry]): string =
   result = "<dl>"
   var i = 0
   while i < symbols.len:
-    let keyword = symbols[i].keyword
-    let cleaned_keyword = keyword.escapeLink
+    let keyword = esc(outHtml, symbols[i].keyword)
+    let cleanedKeyword = keyword.escapeLink
     result.addf("<dt><a name=\"$2\" href=\"#$2\"><span>$1:</span></a></dt><dd><ul class=\"simple\">\n",
-                [keyword, cleaned_keyword])
+                [keyword, cleanedKeyword])
     var j = i
-    while j < symbols.len and keyword == symbols[j].keyword:
+    while j < symbols.len and symbols[i].keyword == symbols[j].keyword:
       let
         url = symbols[j].link.escapeLink
-        text = if not symbols[j].linkTitle.isNil: symbols[j].linkTitle else: url
-        desc = if not symbols[j].linkDesc.isNil: symbols[j].linkDesc else: ""
+        module = symbols[j].module
+        text =
+          if symbols[j].linkTitle.len > 0:
+            esc(outHtml, module & ": " & symbols[j].linkTitle)
+          else: url
+        desc = symbols[j].linkDesc
       if desc.len > 0:
         result.addf("""<li><a class="reference external"
-          title="$3" href="$1">$2</a></li>
+          title="$3" data-doc-search-tag="$2" href="$1">$2</a></li>
           """, [url, text, desc])
       else:
-        result.addf("""<li><a class="reference external" href="$1">$2</a></li>
+        result.addf("""<li><a class="reference external"
+          data-doc-search-tag="$2" href="$1">$2</a></li>
           """, [url, text])
       inc j
     result.add("</ul></dd>\n")
     i = j
   result.add("</dl>")
 
-proc isDocumentationTitle(hyperlink: string): bool =
-  ## Returns true if the hyperlink is actually a documentation title.
-  ##
-  ## Documentation titles lack the hash. See `mergeIndexes() <#mergeIndexes>`_
-  ## for a more detailed explanation.
-  result = hyperlink.find('#') < 0
-
-proc stripTOCLevel(s: string): tuple[level: int, text: string] =
+proc stripTocLevel(s: string): tuple[level: int, text: string] =
   ## Returns the *level* of the toc along with the text without it.
-  for c in 0 .. <s.len:
+  for c in 0 ..< s.len:
     result.level = c
     if s[c] != ' ': break
-  result.text = s[result.level .. <s.len]
+  result.text = s[result.level ..< s.len]
 
 proc indentToLevel(level: var int, newLevel: int): string =
   ## Returns the sequence of <ul>|</ul> characters to switch to `newLevel`.
@@ -487,28 +497,27 @@ proc indentToLevel(level: var int, newLevel: int): string =
     result = repeat("</ul></li>", level - newLevel)
   level = newLevel
 
-proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
+proc generateDocumentationToc(entries: seq[IndexEntry]): string =
   ## Returns the sequence of index entries in an HTML hierarchical list.
   result = ""
   # Build a list of levels and extracted titles to make processing easier.
   var
     titleRef: string
+    titleTag: string
     levels: seq[tuple[level: int, text: string]]
     L = 0
     level = 1
   levels.newSeq(entries.len)
   for entry in entries:
-    let (rawLevel, rawText) = stripTOCLevel(entry.linkTitle or entry.keyword)
+    let (rawLevel, rawText) = stripTocLevel(entry.linkTitle)
     if rawLevel < 1:
       # This is a normal symbol, push it *inside* one level from the last one.
       levels[L].level = level + 1
-      # Also, ignore the linkTitle and use directly the keyword.
-      levels[L].text = entry.keyword
     else:
       # The level did change, update the level indicator.
       level = rawLevel
       levels[L].level = rawLevel
-      levels[L].text = rawText
+    levels[L].text = rawText
     inc L
 
   # Now generate hierarchical lists based on the precalculated levels.
@@ -519,14 +528,14 @@ proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
     let link = entries[L].link
     if link.isDocumentationTitle:
       titleRef = link
+      titleTag = levels[L].text
     else:
       result.add(level.indentToLevel(levels[L].level))
-      result.add("<li><a href=\"" & link & "\">" &
-        levels[L].text & "</a></li>\n")
+      result.addf("""<li><a class="reference" data-doc-search-tag="$1: $2" href="$3">
+        $3</a></li>
+        """, [titleTag, levels[L].text, link, levels[L].text])
     inc L
   result.add(level.indentToLevel(1) & "</ul>\n")
-  assert(not titleRef.isNil,
-    "Can't use this proc on an API index, docs always have a title entry")
 
 proc generateDocumentationIndex(docs: IndexedDocs): string =
   ## Returns all the documentation TOCs in an HTML hierarchical list.
@@ -537,9 +546,9 @@ proc generateDocumentationIndex(docs: IndexedDocs): string =
   sort(titles, cmp)
 
   for title in titles:
-    let tocList = generateDocumentationTOC(docs.getOrDefault(title))
+    let tocList = generateDocumentationToc(docs.getOrDefault(title))
     result.add("<ul><li><a href=\"" &
-      title.link & "\">" & title.keyword & "</a>\n" & tocList & "</li></ul>\n")
+      title.link & "\">" & title.linkTitle & "</a>\n" & tocList & "</li></ul>\n")
 
 proc generateDocumentationJumps(docs: IndexedDocs): string =
   ## Returns a plain list of hyperlinks to documentation TOCs in HTML.
@@ -551,7 +560,7 @@ proc generateDocumentationJumps(docs: IndexedDocs): string =
 
   var chunks: seq[string] = @[]
   for title in titles:
-    chunks.add("<a href=\"" & title.link & "\">" & title.keyword & "</a>")
+    chunks.add("<a href=\"" & title.link & "\">" & title.linkTitle & "</a>")
 
   result.add(chunks.join(", ") & ".<br/>")
 
@@ -561,11 +570,11 @@ proc generateModuleJumps(modules: seq[string]): string =
 
   var chunks: seq[string] = @[]
   for name in modules:
-    chunks.add("<a href=\"" & name & ".html\">" & name & "</a>")
+    chunks.add("<a href=\"$1.html\">$2</a>" % [name, name.prettyLink])
 
   result.add(chunks.join(", ") & ".<br/>")
 
-proc readIndexDir(dir: string):
+proc readIndexDir*(dir: string):
     tuple[modules: seq[string], symbols: seq[IndexEntry], docs: IndexedDocs] =
   ## Walks `dir` reading ``.idx`` files converting them in IndexEntry items.
   ##
@@ -578,53 +587,39 @@ proc readIndexDir(dir: string):
   setLen(result.symbols, 0)
   var L = 0
   # Scan index files and build the list of symbols.
-  for kind, path in walkDir(dir):
-    if kind == pcFile and path.endsWith(IndexExt):
-      var
-        fileEntries: seq[IndexEntry]
-        title: IndexEntry
-        F = 0
-      newSeq(fileEntries, 500)
-      setLen(fileEntries, 0)
-      for line in lines(path):
-        let s = line.find('\t')
-        if s < 0: continue
-        setLen(fileEntries, F+1)
-        fileEntries[F].keyword = line.substr(0, s-1)
-        fileEntries[F].link = line.substr(s+1)
-        # See if we detect a title, a link without a `#foobar` trailing part.
-        if title.keyword.isNil and fileEntries[F].link.isDocumentationTitle:
-          title.keyword = fileEntries[F].keyword
-          title.link = fileEntries[F].link
-
-        if fileEntries[F].link.find('\t') > 0:
-          let extraCols = fileEntries[F].link.split('\t')
-          fileEntries[F].link = extraCols[0]
-          assert extraCols.len == 3
-          fileEntries[F].linkTitle = extraCols[1].unquoteIndexColumn
-          fileEntries[F].linkDesc = extraCols[2].unquoteIndexColumn
-        else:
-          fileEntries[F].linkTitle = nil
-          fileEntries[F].linkDesc = nil
-        inc F
+  for path in walkDirRec(dir):
+    if path.endsWith(IndexExt):
+      var (fileEntries, title) = parseIdxFile(path)
       # Depending on type add this to the list of symbols or table of APIs.
-      if title.keyword.isNil:
-        for i in 0 .. <F:
-          # Don't add to symbols TOC entries (they start with a whitespace).
-          let toc = fileEntries[i].linkTitle
-          if not toc.isNil and toc.len > 0 and toc[0] == ' ':
+
+      if title.kind == ieNimTitle:
+        for i in 0 ..< fileEntries.len:
+          if fileEntries[i].kind != ieNim:
             continue
           # Ok, non TOC entry, add it.
           setLen(result.symbols, L + 1)
           result.symbols[L] = fileEntries[i]
           inc L
-        result.modules.add(path.splitFile.name)
+        if fileEntries.len > 0:
+          var x = fileEntries[0].link
+          let i = find(x, '#')
+          if i > 0:
+            x.setLen(i)
+          if i != 0:
+            # don't add entries starting with '#'
+            result.modules.add(x.changeFileExt(""))
       else:
         # Generate the symbolic anchor for index quickjumps.
-        title.linkTitle = "doc_toc_" & $result.docs.len
+        title.aux = "doc_toc_" & $result.docs.len
         result.docs[title] = fileEntries
 
-  sort(result.modules, system.cmp)
+      for i in 0 ..< fileEntries.len:
+        if fileEntries[i].kind != ieIdxRole:
+          continue
+
+        setLen(result.symbols, L + 1)
+        result.symbols[L] = fileEntries[i]
+        inc L
 
 proc mergeIndexes*(dir: string): string =
   ## Merges all index files in `dir` and returns the generated index as HTML.
@@ -632,13 +627,15 @@ proc mergeIndexes*(dir: string): string =
   ## This proc will first scan `dir` for index files with the ``.idx``
   ## extension previously created by commands like ``nim doc|rst2html``
   ## which use the ``--index:on`` switch. These index files are the result of
-  ## calls to `setIndexTerm() <#setIndexTerm>`_ and `writeIndexFile()
-  ## <#writeIndexFile>`_, so they are simple tab separated files.
+  ## calls to `setIndexTerm()
+  ## <#setIndexTerm,RstGenerator,string,string,string,string,string>`_
+  ## and `writeIndexFile() <#writeIndexFile,RstGenerator,string>`_, so they are
+  ## simple tab separated files.
   ##
   ## As convention this proc will split index files into two categories:
   ## documentation and API. API indices will be all joined together into a
   ## single big sorted index, making the bulk of the final index. This is good
-  ## for API documentation because many symbols are repated in different
+  ## for API documentation because many symbols are repeated in different
   ## modules. On the other hand, documentation indices are essentially table of
   ## contents plus a few special markers. These documents will be rendered in a
   ## separate section which tries to maintain the order and hierarchy of the
@@ -653,7 +650,7 @@ proc mergeIndexes*(dir: string): string =
   ## Returns the merged and sorted indices into a single HTML block which can
   ## be further embedded into nimdoc templates.
   var (modules, symbols, docs) = readIndexDir(dir)
-  assert(not symbols.isNil)
+  sort(modules, system.cmp)
 
   result = ""
   # Generate a quick jump list of documents.
@@ -666,10 +663,11 @@ proc mergeIndexes*(dir: string): string =
     result.add(generateModuleJumps(modules))
     result.add("<p />")
 
-  # Generate the HTML block with API documents.
-  if docs.len > 0:
-    result.add("<h2>Documentation files</h2>\n")
-    result.add(generateDocumentationIndex(docs))
+  when false:
+    # Generate the HTML block with API documents.
+    if docs.len > 0:
+      result.add("<h2>Documentation files</h2>\n")
+      result.add(generateDocumentationIndex(docs))
 
   # Generate the HTML block with symbols.
   if symbols.len > 0:
@@ -680,63 +678,36 @@ proc mergeIndexes*(dir: string): string =
 
 # ----------------------------------------------------------------------------
 
-proc stripTOCHTML(s: string): string =
-  ## Ugly quick hack to remove HTML tags from TOC titles.
-  ##
-  ## A TocEntry.header field already contains rendered HTML tags. Instead of
-  ## implementing a proper version of renderRstToOut() which recursively
-  ## renders an rst tree to plain text, we simply remove text found between
-  ## angled brackets. Given the limited possibilities of rst inside TOC titles
-  ## this should be enough.
-  result = s
-  var first = result.find('<')
-  while first >= 0:
-    let last = result.find('>', first)
-    if last < 0:
-      # Abort, since we didn't found a closing angled bracket.
-      return
-    result.delete(first, last)
-    first = result.find('<', first)
-
 proc renderHeadline(d: PDoc, n: PRstNode, result: var string) =
   var tmp = ""
   for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], tmp)
   d.currentSection = tmp
-  # Find the last higher level section for unique reference name
-  var sectionPrefix = ""
-  for i in countdown(d.tocPart.high, 0):
-    let n2 = d.tocPart[i].n
-    if n2.level < n.level:
-      sectionPrefix = rstnodeToRefname(n2) & "-"
-      break
-  var refname = sectionPrefix & rstnodeToRefname(n)
+  var tocName = esc(d.target, renderRstToText(n), escMode = emOption)
+    # for Latex: simple text without commands that may break TOC/hyperref
   if d.hasToc:
-    var length = len(d.tocPart)
-    setLen(d.tocPart, length + 1)
-    d.tocPart[length].refname = refname
-    d.tocPart[length].n = n
-    d.tocPart[length].header = tmp
-
-    dispA(d.target, result, "\n<h$1><a class=\"toc-backref\" " &
-      "id=\"$2\" href=\"#$2\">$3</a></h$1>", "\\rsth$4{$3}\\label{$2}\n",
-      [$n.level, d.tocPart[length].refname, tmp, $chr(n.level - 1 + ord('A'))])
+    d.tocPart.add n
+    dispA(d.target, result, "\n<h$1><a class=\"toc-backref\"" &
+      "$2 href=\"#$5\">$3</a></h$1>", "\\rsth$4[$6]{$3}$2\n",
+      [$n.level, n.anchor.idS, tmp,
+       $chr(n.level - 1 + ord('A')), n.anchor, tocName])
   else:
-    dispA(d.target, result, "\n<h$1 id=\"$2\">$3</h$1>",
-                            "\\rsth$4{$3}\\label{$2}\n", [
-        $n.level, refname, tmp,
-        $chr(n.level - 1 + ord('A'))])
+    dispA(d.target, result, "\n<h$1$2>$3</h$1>",
+                            "\\rsth$4[$5]{$3}$2\n", [
+        $n.level, n.anchor.idS, tmp,
+        $chr(n.level - 1 + ord('A')), tocName])
 
   # Generate index entry using spaces to indicate TOC level for the output HTML.
   assert n.level >= 0
-  setIndexTerm(d, refname, tmp.stripTOCHTML,
-    spaces(max(0, n.level)) & tmp)
+  setIndexTerm(d, ieHeading, htmlFile = d.htmlFileRelPath, id = n.anchor,
+               term = n.addNodes, linkTitle = spaces(max(0, n.level)) & tmp)
 
 proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
-  if d.meta[metaTitle].len == 0:
+  if n.level == 0 and d.meta[metaTitle].len == 0:
+    d.meta[metaTitleRaw] = n.addNodes
     for i in countup(0, len(n)-1):
       renderRstToOut(d, n.sons[i], d.meta[metaTitle])
     d.currentSection = d.meta[metaTitle]
-  elif d.meta[metaSubtitle].len == 0:
+  elif n.level == 0 and d.meta[metaSubtitle].len == 0:
     for i in countup(0, len(n)-1):
       renderRstToOut(d, n.sons[i], d.meta[metaSubtitle])
     d.currentSection = d.meta[metaSubtitle]
@@ -744,21 +715,25 @@ proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
     var tmp = ""
     for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], tmp)
     d.currentSection = tmp
-    dispA(d.target, result, "<h$1 id=\"$2\"><center>$3</center></h$1>",
-                   "\\rstov$4{$3}\\label{$2}\n", [$n.level,
-        rstnodeToRefname(n), tmp, $chr(n.level - 1 + ord('A'))])
-
-
-proc renderTocEntry(d: PDoc, e: TocEntry, result: var string) =
+    var tocName = esc(d.target, renderRstToText(n), escMode=emOption)
+    dispA(d.target, result, "<h$1$2><center>$3</center></h$1>",
+                   "\\rstov$4[$5]{$3}$2\n", [$n.level,
+                   n.anchor.idS, tmp, $chr(n.level - 1 + ord('A')), tocName])
+    setIndexTerm(d, ieHeading, htmlFile = d.htmlFileRelPath, id = n.anchor,
+                 term = n.addNodes, linkTitle = spaces(max(0, n.level)) & tmp)
+
+proc renderTocEntry(d: PDoc, n: PRstNode, result: var string) =
+  var header = ""
+  for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], header)
   dispA(d.target, result,
     "<li><a class=\"reference\" id=\"$1_toc\" href=\"#$1\">$2</a></li>\n",
-    "\\item\\label{$1_toc} $2\\ref{$1}\n", [e.refname, e.header])
+    "\\item\\label{$1_toc} $2\\ref{$1}\n", [n.anchor, header])
 
 proc renderTocEntries*(d: var RstGenerator, j: var int, lvl: int,
                        result: var string) =
   var tmp = ""
   while j <= high(d.tocPart):
-    var a = abs(d.tocPart[j].n.level)
+    var a = abs(d.tocPart[j].level)
     if a == lvl:
       renderTocEntry(d, d.tocPart[j], tmp)
       inc(j)
@@ -804,14 +779,28 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) =
   if arg.endsWith(".mp4") or arg.endsWith(".ogg") or
      arg.endsWith(".webm"):
     htmlOut = """
-      <video src="$1"$2 autoPlay='true' loop='true' muted='true'>
+      <video$3 src="$1"$2 autoPlay='true' loop='true' muted='true'>
       Sorry, your browser doesn't support embedded videos
       </video>
     """
   else:
-    htmlOut = "<img src=\"$1\"$2/>"
-  dispA(d.target, result, htmlOut, "\\includegraphics$2{$1}",
-        [esc(d.target, arg), options])
+    htmlOut = "<img$3 src=\"$1\"$2/>"
+
+  # support for `:target:` links for images:
+  var target = esc(d.target, getFieldValue(n, "target").strip(), escMode=emUrl)
+  discard safeProtocol(target)
+
+  if target.len > 0:
+    # `htmlOut` needs to be of the following format for link to work for images:
+    # <a class="reference external" href="target"><img src=\"$1\"$2/></a>
+    var htmlOutWithLink = ""
+    dispA(d.target, htmlOutWithLink,
+      "<a class=\"reference external\" href=\"$2\">$1</a>",
+      "\\href{$2}{$1}", [htmlOut, target])
+    htmlOut = htmlOutWithLink
+
+  dispA(d.target, result, htmlOut, "$3\\includegraphics$2{$1}",
+        [esc(d.target, arg), options, n.anchor.idS])
   if len(n) >= 3: renderRstToOut(d, n.sons[2], result)
 
 proc renderSmiley(d: PDoc, n: PRstNode, result: var string) =
@@ -821,6 +810,25 @@ proc renderSmiley(d: PDoc, n: PRstNode, result: var string) =
     "\\includegraphics{$1}",
     [d.config.getOrDefault"doc.smiley_format" % n.text])
 
+proc getField1Int(d: PDoc, n: PRstNode, fieldName: string): int =
+  template err(msg: string) =
+    rstMessage(d.filenames, d.msgHandler, n.info, meInvalidField, msg)
+  let value = n.getFieldValue
+  var number: int
+  let nChars = parseInt(value, number)
+  if nChars == 0:
+    if value.len == 0:
+      # use a good default value:
+      result = 1
+    else:
+      err("field $1 requires an integer, but '$2' was given" %
+          [fieldName, value])
+  elif nChars < value.len:
+    err("extra arguments were given to $1: '$2'" %
+        [fieldName, value[nChars..^1]])
+  else:
+    result = number
+
 proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) =
   ## Parses useful fields which can appear before a code block.
   ##
@@ -830,9 +838,7 @@ proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) =
   of "number-lines":
     params.numberLines = true
     # See if the field has a parameter specifying a different line than 1.
-    var number: int
-    if parseInt(n.getFieldValue, number) > 0:
-      params.startLine = number
+    params.startLine = getField1Int(d, n, "number-lines")
   of "file", "filename":
     # The ``file`` option is a Nim extension to the official spec, it acts
     # like it would for other directives like ``raw`` or ``cvs-table``. This
@@ -843,18 +849,20 @@ proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) =
   of "test":
     params.testCmd = n.getFieldValue.strip
     if params.testCmd.len == 0:
-      params.testCmd = "nim c -r $1"
+      # factor with D20210224T221756. Note that `$docCmd` should appear before `$file`
+      # but after all other options, but currently `$options` merges both options and `$file` so it's tricky.
+      params.testCmd = "$nim r --backend:$backend --lib:$libpath $docCmd $options"
     else:
+      # consider whether `$docCmd` should be appended here too
       params.testCmd = unescape(params.testCmd)
   of "status", "exitcode":
-    var status: int
-    if parseInt(n.getFieldValue, status) > 0:
-      params.status = status
+    params.status = getField1Int(d, n, n.getArgument)
   of "default-language":
     params.langStr = n.getFieldValue.strip
     params.lang = params.langStr.getSourceLanguage
   else:
-    d.msgHandler(d.filename, 1, 0, mwUnsupportedField, n.getArgument)
+    rstMessage(d.filenames, d.msgHandler, n.info, mwUnsupportedField,
+               n.getArgument)
 
 proc parseCodeBlockParams(d: PDoc, n: PRstNode): CodeBlockParams =
   ## Iterates over all code block fields and returns processed params.
@@ -864,8 +872,7 @@ proc parseCodeBlockParams(d: PDoc, n: PRstNode): CodeBlockParams =
   result.init
   if n.isNil:
     return
-  assert n.kind == rnCodeBlock
-  assert(not n.sons[2].isNil)
+  assert n.kind in {rnCodeBlock, rnInlineCode}
 
   # Parse the field list for rendering parameters if there are any.
   if not n.sons[1].isNil:
@@ -876,7 +883,8 @@ proc parseCodeBlockParams(d: PDoc, n: PRstNode): CodeBlockParams =
   if result.langStr != "":
     result.lang = getSourceLanguage(result.langStr)
 
-proc buildLinesHTMLTable(d: PDoc; params: CodeBlockParams, code: string):
+proc buildLinesHtmlTable(d: PDoc; params: CodeBlockParams, code: string,
+                         idStr: string):
     tuple[beginTable, endTable: string] =
   ## Returns the necessary tags to start/end a code block in HTML.
   ##
@@ -888,13 +896,14 @@ proc buildLinesHTMLTable(d: PDoc; params: CodeBlockParams, code: string):
   let id = $d.listingCounter
   if not params.numberLines:
     result = (d.config.getOrDefault"doc.listing_start" %
-                [id, sourceLanguageToStr[params.lang]],
+                [id, sourceLanguageToStr[params.lang], idStr],
               d.config.getOrDefault"doc.listing_end" % id)
     return
 
   var codeLines = code.strip.countLines
   assert codeLines > 0
-  result.beginTable = """<table class="line-nums-table"><tbody><tr><td class="blob-line-nums"><pre class="line-nums">"""
+  result.beginTable = """<table$1 class="line-nums-table">""" % [idStr] &
+      """<tbody><tr><td class="blob-line-nums"><pre class="line-nums">"""
   var line = params.startLine
   while codeLines > 0:
     result.beginTable.add($line & "\n")
@@ -902,13 +911,32 @@ proc buildLinesHTMLTable(d: PDoc; params: CodeBlockParams, code: string):
     codeLines.dec
   result.beginTable.add("</pre></td><td>" & (
       d.config.getOrDefault"doc.listing_start" %
-        [id, sourceLanguageToStr[params.lang]]))
+        [id, sourceLanguageToStr[params.lang], idStr]))
   result.endTable = (d.config.getOrDefault"doc.listing_end" % id) &
       "</td></tr></tbody></table>" & (
       d.config.getOrDefault"doc.listing_button" % id)
 
-proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
-  ## Renders a code block, appending it to `result`.
+proc renderCodeLang*(result: var string, lang: SourceLanguage, code: string,
+                     target: OutputTarget) =
+  var g: GeneralTokenizer
+  initGeneralTokenizer(g, code)
+  while true:
+    getNextToken(g, lang)
+    case g.kind
+    of gtEof: break
+    of gtNone, gtWhitespace:
+      add(result, substr(code, g.start, g.length + g.start - 1))
+    else:
+      dispA(target, result, "<span class=\"$2\">$1</span>", "\\span$2{$1}", [
+        esc(target, substr(code, g.start, g.length+g.start-1)),
+        tokenClassToStr[g.kind]])
+  deinitGeneralTokenizer(g)
+
+proc renderNimCode*(result: var string, code: string, target: OutputTarget) =
+  renderCodeLang(result, langNim, code, target)
+
+proc renderCode(d: PDoc, n: PRstNode, result: var string) {.gcsafe.} =
+  ## Renders a code (code block or inline code), appending it to `result`.
   ##
   ## If the code block uses the ``number-lines`` option, a table will be
   ## generated with two columns, the first being a list of numbers and the
@@ -917,37 +945,40 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
   ## may also come from the parser through the internal ``default-language``
   ## option to differentiate between a plain code block and Nim's code block
   ## extension.
-  assert n.kind == rnCodeBlock
-  if n.sons[2] == nil: return
+  assert n.kind in {rnCodeBlock, rnInlineCode}
   var params = d.parseCodeBlockParams(n)
+  if n.sons[2] == nil: return
   var m = n.sons[2].sons[0]
   assert m.kind == rnLeaf
 
   if params.testCmd.len > 0 and d.onTestSnippet != nil:
     d.onTestSnippet(d, params.filename, params.testCmd, params.status, m.text)
 
-  let (blockStart, blockEnd) = buildLinesHTMLTable(d, params, m.text)
-
-  dispA(d.target, result, blockStart, "\\begin{rstpre}\n", [])
+  var blockStart, blockEnd: string
+  case d.target
+  of outHtml:
+    if n.kind == rnCodeBlock:
+      (blockStart, blockEnd) = buildLinesHtmlTable(d, params, m.text,
+                                                   n.anchor.idS)
+    else:  # rnInlineCode
+      blockStart = "<tt class=\"docutils literal\"><span class=\"pre\">"
+      blockEnd = "</span></tt>"
+  of outLatex:
+    if n.kind == rnCodeBlock:
+      blockStart = "\n\n" & n.anchor.idS & "\\begin{rstpre}\n"
+      blockEnd = "\n\\end{rstpre}\n\n"
+    else:  # rnInlineCode
+      blockStart = "\\rstcode{"
+      blockEnd = "}"
+  dispA(d.target, result, blockStart, blockStart, [])
   if params.lang == langNone:
-    if len(params.langStr) > 0:
-      d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, params.langStr)
-    for letter in m.text: escChar(d.target, result, letter)
+    if len(params.langStr) > 0 and params.langStr.toLowerAscii != "none":
+      rstMessage(d.filenames, d.msgHandler, n.info, mwUnsupportedLanguage,
+                 params.langStr)
+    for letter in m.text: escChar(d.target, result, letter, emText)
   else:
-    var g: GeneralTokenizer
-    initGeneralTokenizer(g, m.text)
-    while true:
-      getNextToken(g, params.lang)
-      case g.kind
-      of gtEof: break
-      of gtNone, gtWhitespace:
-        add(result, substr(m.text, g.start, g.length + g.start - 1))
-      else:
-        dispA(d.target, result, "<span class=\"$2\">$1</span>", "\\span$2{$1}", [
-          esc(d.target, substr(m.text, g.start, g.length+g.start-1)),
-          tokenClassToStr[g.kind]])
-    deinitGeneralTokenizer(g)
-  dispA(d.target, result, blockEnd, "\n\\end{rstpre}\n")
+    renderCodeLang(result, params.lang, m.text, d.target)
+  dispA(d.target, result, blockEnd, blockEnd)
 
 proc renderContainer(d: PDoc, n: PRstNode, result: var string) =
   var tmp = ""
@@ -958,10 +989,6 @@ proc renderContainer(d: PDoc, n: PRstNode, result: var string) =
   else:
     dispA(d.target, result, "<div class=\"$1\">$2</div>", "$2", [arg, tmp])
 
-proc texColumns(n: PRstNode): string =
-  result = ""
-  for i in countup(1, len(n)): add(result, "|X")
-
 proc renderField(d: PDoc, n: PRstNode, result: var string) =
   var b = false
   if d.target == outLatex:
@@ -979,41 +1006,150 @@ proc renderField(d: PDoc, n: PRstNode, result: var string) =
   if not b:
     renderAux(d, n, "<tr>$1</tr>\n", "$1", result)
 
+proc renderEnumList(d: PDoc, n: PRstNode, result: var string) =
+  var
+    specifier = ""
+    specStart = ""
+    i1 = 0
+    pre = ""
+    i2 = n.labelFmt.len - 1
+    post = ""
+  if n.labelFmt[0] == '(':
+    i1 = 1
+    pre = "("
+  if n.labelFmt[^1] == ')' or n.labelFmt[^1] == '.':
+    i2 = n.labelFmt.len - 2
+    post = $n.labelFmt[^1]
+  let enumR = i1 .. i2  # enumerator range without surrounding (, ), .
+  if d.target == outLatex:
+    result.add ("\n%" & n.labelFmt & "\n")
+    # use enumerate parameters from package enumitem
+    if n.labelFmt[i1].isDigit:
+      var labelDef = ""
+      if pre != "" or post != "":
+        labelDef = "label=" & pre & "\\arabic*" & post & ","
+      if n.labelFmt[enumR] != "1":
+        specStart = "start=$1" % [n.labelFmt[enumR]]
+      if labelDef != "" or specStart != "":
+        specifier = "[$1$2]" % [labelDef, specStart]
+    else:
+      let (first, labelDef) =
+        if n.labelFmt[i1].isUpperAscii: ('A', "label=" & pre & "\\Alph*" & post)
+        else: ('a', "label=" & pre & "\\alph*" & post)
+      if n.labelFmt[i1] != first:
+        specStart = ",start=" & $(ord(n.labelFmt[i1]) - ord(first) + 1)
+      specifier = "[$1$2]" % [labelDef, specStart]
+  else:  # HTML
+    # TODO: implement enumerator formatting using pre and post ( and ) for HTML
+    if n.labelFmt[i1].isDigit:
+      if n.labelFmt[enumR] != "1":
+        specStart = " start=\"$1\"" % [n.labelFmt[enumR]]
+      specifier = "class=\"simple\"" & specStart
+    else:
+      let (first, labelDef) =
+        if n.labelFmt[i1].isUpperAscii: ('A', "class=\"upperalpha simple\"")
+        else: ('a', "class=\"loweralpha simple\"")
+      if n.labelFmt[i1] != first:
+        specStart = " start=\"$1\"" % [ $(ord(n.labelFmt[i1]) - ord(first) + 1) ]
+      specifier = labelDef & specStart
+  renderAux(d, n, "<ol$2 " & specifier & ">$1</ol>\n",
+            "\\begin{enumerate}" & specifier & "$2$1\\end{enumerate}\n",
+            result)
+
+proc renderAdmonition(d: PDoc, n: PRstNode, result: var string) =
+  var
+    htmlCls = "admonition_warning"
+    texSz = "\\large"
+    texColor = "orange"
+  case n.adType
+  of "hint", "note", "tip":
+    htmlCls = "admonition-info"; texSz = "\\normalsize"; texColor = "green"
+  of "attention", "admonition", "important", "warning", "caution":
+    htmlCls = "admonition-warning"; texSz = "\\large"; texColor = "orange"
+  of "danger", "error":
+    htmlCls = "admonition-error"; texSz = "\\Large"; texColor = "red"
+  else: discard
+  let txt = n.adType.capitalizeAscii()
+  let htmlHead = "<div class=\"admonition " & htmlCls & "\">"
+  renderAux(d, n,
+      htmlHead & "<span$2 class=\"" & htmlCls & "-text\"><b>" & txt &
+        ":</b></span>\n" & "$1</div>\n",
+      "\n\n\\begin{rstadmonition}[borderline west={0.2em}{0pt}{" &
+        texColor & "}]$2\n" &
+        "{" & texSz & "\\color{" & texColor & "}{\\textbf{" & txt & ":}}} " &
+        "$1\n\\end{rstadmonition}\n",
+      result)
+
+proc renderHyperlink(d: PDoc, text, link: PRstNode, result: var string,
+                     external: bool, nimdoc = false, tooltip="") =
+  var linkStr = ""
+  block:
+    let mode = d.escMode
+    d.escMode = emUrl
+    renderRstToOut(d, link, linkStr)
+    d.escMode = mode
+  discard safeProtocol(linkStr)
+  var textStr = ""
+  renderRstToOut(d, text, textStr)
+  let nimDocStr = if nimdoc: " nimdoc" else: ""
+  var tooltipStr = ""
+  if tooltip != "":
+    tooltipStr = """ title="$1"""" % [ esc(d.target, tooltip) ]
+  if external:
+    dispA(d.target, result,
+      "<a class=\"reference external$3\"$4 href=\"$2\">$1</a>",
+      "\\href{$2}{$1}", [textStr, linkStr, nimDocStr, tooltipStr])
+  else:
+    dispA(d.target, result,
+      "<a class=\"reference internal$3\"$4 href=\"#$2\">$1</a>",
+      "\\hyperlink{$2}{$1} (p.~\\pageref{$2})",
+      [textStr, linkStr, nimDocStr, tooltipStr])
+
+proc traverseForIndex*(d: PDoc, n: PRstNode) =
+  ## A version of [renderRstToOut] that only fills entries for ``.idx`` files.
+  var discarded: string
+  if n == nil: return
+  case n.kind
+  of rnIdx: renderIndexTerm(d, n, discarded)
+  of rnHeadline, rnMarkdownHeadline: renderHeadline(d, n, discarded)
+  of rnOverline: renderOverline(d, n, discarded)
+  else:
+    for i in 0 ..< len(n):
+      traverseForIndex(d, n.sons[i])
+
 proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
   if n == nil: return
   case n.kind
   of rnInner: renderAux(d, n, result)
-  of rnHeadline: renderHeadline(d, n, result)
+  of rnHeadline, rnMarkdownHeadline: renderHeadline(d, n, result)
   of rnOverline: renderOverline(d, n, result)
-  of rnTransition: renderAux(d, n, "<hr />\n", "\\hrule\n", result)
-  of rnParagraph: renderAux(d, n, "<p>$1</p>\n", "$1\n\n", result)
+  of rnTransition: renderAux(d, n, "<hr$2 />\n", "\n\n\\vspace{0.6em}\\hrule$2\n", result)
+  of rnParagraph: renderAux(d, n, "<p$2>$1</p>\n", "\n\n$2\n$1\n\n", result)
   of rnBulletList:
-    renderAux(d, n, "<ul class=\"simple\">$1</ul>\n",
-                    "\\begin{itemize}$1\\end{itemize}\n", result)
+    renderAux(d, n, "<ul$2 class=\"simple\">$1</ul>\n",
+                    "\\begin{itemize}\n$2\n$1\\end{itemize}\n", result)
   of rnBulletItem, rnEnumItem:
-    renderAux(d, n, "<li>$1</li>\n", "\\item $1\n", result)
-  of rnEnumList:
-    renderAux(d, n, "<ol class=\"simple\">$1</ol>\n",
-                    "\\begin{enumerate}$1\\end{enumerate}\n", result)
-  of rnDefList:
-    renderAux(d, n, "<dl class=\"docutils\">$1</dl>\n",
-                       "\\begin{description}$1\\end{description}\n", result)
+    renderAux(d, n, "<li$2>$1</li>\n", "\\item $2$1\n", result)
+  of rnEnumList: renderEnumList(d, n, result)
+  of rnDefList, rnMdDefList:
+    renderAux(d, n, "<dl$2 class=\"docutils\">$1</dl>\n",
+                    "\\begin{description}\n$2\n$1\\end{description}\n", result)
   of rnDefItem: renderAux(d, n, result)
-  of rnDefName: renderAux(d, n, "<dt>$1</dt>\n", "\\item[$1] ", result)
-  of rnDefBody: renderAux(d, n, "<dd>$1</dd>\n", "$1\n", result)
+  of rnDefName: renderAux(d, n, "<dt$2>$1</dt>\n", "$2\\item[$1]\\  ", result)
+  of rnDefBody: renderAux(d, n, "<dd$2>$1</dd>\n", "$2\n$1\n", result)
   of rnFieldList:
     var tmp = ""
     for i in countup(0, len(n) - 1):
       renderRstToOut(d, n.sons[i], tmp)
     if tmp.len != 0:
       dispA(d.target, result,
-          "<table class=\"docinfo\" frame=\"void\" rules=\"none\">" &
+          "<table$2 class=\"docinfo\" frame=\"void\" rules=\"none\">" &
           "<col class=\"docinfo-name\" />" &
           "<col class=\"docinfo-content\" />" &
           "<tbody valign=\"top\">$1" &
           "</tbody></table>",
-          "\\begin{description}$1\\end{description}\n",
-          [tmp])
+          "\\begin{description}\n$2\n$1\\end{description}\n",
+          [tmp, n.anchor.idS])
   of rnField: renderField(d, n, result)
   of rnFieldName:
     renderAux(d, n, "<th class=\"docinfo-name\">$1:</th>",
@@ -1023,96 +1159,190 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
   of rnIndex:
     renderRstToOut(d, n.sons[2], result)
   of rnOptionList:
-    renderAux(d, n, "<table frame=\"void\">$1</table>",
-      "\\begin{description}\n$1\\end{description}\n", result)
+    renderAux(d, n, "<div$2 class=\"option-list\">$1</div>",
+        "\\begin{rstoptlist}$2\n$1\\end{rstoptlist}", result)
   of rnOptionListItem:
-    renderAux(d, n, "<tr>$1</tr>\n", "$1", result)
+    var addclass = if n.order mod 2 == 1: " odd" else: ""
+    renderAux(d, n,
+        "<div class=\"option-list-item" & addclass & "\">$1</div>\n",
+        "$1", result)
   of rnOptionGroup:
-    renderAux(d, n, "<th align=\"left\">$1</th>", "\\item[$1]", result)
+    renderAux(d, n,
+        "<div class=\"option-list-label\"><tt><span class=\"option\">" &
+        "$1</span></tt></div>",
+        "\\item[\\rstcodeitem{\\spanoption{$1}}]", result)
   of rnDescription:
-    renderAux(d, n, "<td align=\"left\">$1</td>\n", " $1\n", result)
+    renderAux(d, n, "<div class=\"option-list-description\">$1</div>",
+        " $1\n", result)
   of rnOption, rnOptionString, rnOptionArgument:
-    doAssert false, "renderRstToOut"
+    raiseAssert "renderRstToOut"
   of rnLiteralBlock:
-    renderAux(d, n, "<pre>$1</pre>\n",
-                    "\\begin{rstpre}\n$1\n\\end{rstpre}\n", result)
-  of rnQuotedLiteralBlock:
-    doAssert false, "renderRstToOut"
+    renderAux(d, n, "<pre$2>$1</pre>\n",
+                    "\n\n$2\\begin{rstpre}\n$1\n\\end{rstpre}\n\n", result)
+  of rnMarkdownBlockQuote:
+    d.curQuotationDepth = 1
+    var tmp = ""
+    renderAux(d, n, "$1", "$1", tmp)
+    let itemEnding =
+      if d.target == outHtml: "</blockquote>" else: "\\end{rstquote}"
+    tmp.add itemEnding.repeat(d.curQuotationDepth - 1)
+    dispA(d.target, result,
+        "<blockquote$2 class=\"markdown-quote\">$1</blockquote>\n",
+        "\n\\begin{rstquote}\n$2\n$1\\end{rstquote}\n", [tmp, n.anchor.idS])
+  of rnMarkdownBlockQuoteItem:
+    let addQuotationDepth = n.quotationDepth - d.curQuotationDepth
+    var itemPrefix: string  # start or ending (quotation grey bar on the left)
+    if addQuotationDepth >= 0:
+      let s =
+        if d.target == outHtml: "<blockquote class=\"markdown-quote\">"
+        else: "\\begin{rstquote}"
+      itemPrefix = s.repeat(addQuotationDepth)
+    else:
+      let s =
+        if d.target == outHtml: "</blockquote>"
+        else: "\\end{rstquote}"
+      itemPrefix = s.repeat(-addQuotationDepth)
+    renderAux(d, n, itemPrefix & "<p>$1</p>", itemPrefix & "\n$1", result)
+    d.curQuotationDepth = n.quotationDepth
   of rnLineBlock:
-    renderAux(d, n, "<p>$1</p>", "$1\n\n", result)
+    if n.sons.len == 1 and n.sons[0].lineIndent == "\n":
+      # whole line block is one empty line, no need to add extra spacing
+      renderAux(d, n, "<p$2>$1</p> ", "\n\n$2\n$1", result)
+    else:  # add extra spacing around the line block for Latex
+      renderAux(d, n, "<p$2>$1</p>",
+        "\n\\vspace{0.5em}$2\n$1\\vspace{0.5em}\n", result)
   of rnLineBlockItem:
-    renderAux(d, n, "$1<br />", "$1\\\\\n", result)
+    if n.lineIndent.len == 0:  # normal case - no additional indentation
+      renderAux(d, n, "$1<br/>", "\\noindent $1\n\n", result)
+    elif n.lineIndent == "\n":  # add one empty line
+      renderAux(d, n, "<br/>", "\\vspace{1em}\n", result)
+    else:  # additional indentation w.r.t. '| '
+      let indent = $(0.5 * (n.lineIndent.len - 1).toFloat) & "em"
+      renderAux(d, n,
+        "<span style=\"margin-left: " & indent & "\">$1</span><br/>",
+        "\\noindent\\hspace{" & indent & "}$1\n\n", result)
   of rnBlockQuote:
-    renderAux(d, n, "<blockquote><p>$1</p></blockquote>\n",
-                    "\\begin{quote}$1\\end{quote}\n", result)
-  of rnTable, rnGridTable:
+    renderAux(d, n, "<blockquote$2><p>$1</p></blockquote>\n",
+                    "\\begin{quote}\n$2\n$1\\end{quote}\n", result)
+  of rnAdmonition: renderAdmonition(d, n, result)
+  of rnTable, rnGridTable, rnMarkdownTable:
     renderAux(d, n,
-      "<table border=\"1\" class=\"docutils\">$1</table>",
-      "\\begin{table}\\begin{rsttab}{" &
-        texColumns(n) & "|}\n\\hline\n$1\\end{rsttab}\\end{table}", result)
+      "<table$2 border=\"1\" class=\"docutils\">$1</table>",
+      "\n$2\n\\begin{rsttab}{" &
+        "L".repeat(n.colCount) & "}\n\\toprule\n$1" &
+        "\\addlinespace[0.1em]\\bottomrule\n\\end{rsttab}", result)
   of rnTableRow:
     if len(n) >= 1:
-      if d.target == outLatex:
-        #var tmp = ""
-        renderRstToOut(d, n.sons[0], result)
-        for i in countup(1, len(n) - 1):
-          result.add(" & ")
-          renderRstToOut(d, n.sons[i], result)
-        result.add("\\\\\n\\hline\n")
-      else:
+      case d.target
+      of outHtml:
         result.add("<tr>")
         renderAux(d, n, result)
         result.add("</tr>\n")
-  of rnTableDataCell:
-    renderAux(d, n, "<td>$1</td>", "$1", result)
-  of rnTableHeaderCell:
-    renderAux(d, n, "<th>$1</th>", "\\textbf{$1}", result)
-  of rnLabel:
-    doAssert false, "renderRstToOut" # used for footnotes and other
-  of rnFootnote:
-    doAssert false, "renderRstToOut" # a footnote
-  of rnCitation:
-    doAssert false, "renderRstToOut" # similar to footnote
-  of rnRef:
-    var tmp = ""
-    renderAux(d, n, tmp)
+      of outLatex:
+        if n.sons[0].kind == rnTableHeaderCell:
+          result.add "\\rowcolor{gray!15} "
+        var spanLines: seq[(int, int)]
+        var nCell = 0
+        for uCell in 0 .. n.len - 1:
+          renderRstToOut(d, n.sons[uCell], result)
+          if n.sons[uCell].span > 0:
+            spanLines.add (nCell + 1, nCell + n.sons[uCell].span)
+            nCell += n.sons[uCell].span
+          else:
+            nCell += 1
+          if uCell != n.len - 1:
+            result.add(" & ")
+        result.add("\\\\")
+        if n.endsHeader: result.add("\\midrule\n")
+        for (start, stop) in spanLines:
+          result.add("\\cmidrule(lr){$1-$2}" % [$start, $stop])
+        result.add("\n")
+  of rnTableHeaderCell, rnTableDataCell:
+    case d.target
+    of outHtml:
+      let tag = if n.kind == rnTableHeaderCell: "th" else: "td"
+      var spanSpec: string
+      if n.span <= 1: spanSpec = ""
+      else:
+        spanSpec = " colspan=\"" & $n.span & "\" style=\"text-align: center\""
+      renderAux(d, n, "<$1$2>$$1</$1>" % [tag, spanSpec], "", result)
+    of outLatex:
+      let text = if n.kind == rnTableHeaderCell: "\\textbf{$1}" else: "$1"
+      var latexStr: string
+      if n.span <= 1: latexStr = text
+      else: latexStr = "\\multicolumn{" & $n.span & "}{c}{" & text & "}"
+      renderAux(d, n, "", latexStr, result)
+  of rnFootnoteGroup:
+    renderAux(d, n,
+      "<hr class=\"footnote\">" &
+          "<div class=\"footnote-group\">\n$1</div>\n",
+      "\n\n\\noindent\\rule{0.25\\linewidth}{.4pt}\n" &
+          "\\begin{rstfootnote}\n$1\\end{rstfootnote}\n\n",
+      result)
+  of rnFootnote, rnCitation:
+    var mark = ""
+    renderAux(d, n.sons[0], mark)
+    var body = ""
+    renderRstToOut(d, n.sons[1], body)
     dispA(d.target, result,
-      "<a class=\"reference external\" href=\"#$2\">$1</a>",
-      "$1\\ref{$2}", [tmp, rstnodeToRefname(n)])
+      "<div$2><div class=\"footnote-label\">" &
+          "<sup><strong><a href=\"#$4\">[$3]</a></strong></sup>" &
+          "</div> &ensp; $1\n</div>\n",
+      "\\item[\\textsuperscript{[$3]}]$2 $1\n",
+      [body, n.anchor.idS, mark, n.anchor])
+  of rnPandocRef:
+    renderHyperlink(d, text=n.sons[0], link=n.sons[1], result, external=false)
+  of rnRstRef:
+    renderHyperlink(d, text=n.sons[0], link=n.sons[0], result, external=false)
   of rnStandaloneHyperlink:
-    renderAux(d, n,
-      "<a class=\"reference external\" href=\"$1\">$1</a>",
-      "\\href{$1}{$1}", result)
+    renderHyperlink(d, text=n.sons[0], link=n.sons[0], result, external=true)
+  of rnInternalRef:
+    renderHyperlink(d, text=n.sons[0], link=n.sons[1], result, external=false)
+  of rnNimdocRef:
+    renderHyperlink(d, text=n.sons[0], link=n.sons[1], result, external=false,
+                    nimdoc=true, tooltip=n.tooltip)
   of rnHyperlink:
-    var tmp0 = ""
-    var tmp1 = ""
-    renderRstToOut(d, n.sons[0], tmp0)
-    renderRstToOut(d, n.sons[1], tmp1)
+    renderHyperlink(d, text=n.sons[0], link=n.sons[1], result, external=true)
+  of rnFootnoteRef:
+    var tmp = "["
+    renderAux(d, n.sons[0], tmp)
+    tmp.add "]"
     dispA(d.target, result,
-      "<a class=\"reference external\" href=\"$2\">$1</a>",
-      "\\href{$2}{$1}", [tmp0, tmp1])
+      "<sup><strong><a class=\"reference internal\" href=\"#$2\">" &
+          "$1</a></strong></sup>",
+      "\\textsuperscript{\\hyperlink{$2}{\\textbf{$1}}}",
+      [tmp, n.sons[1].text])
   of rnDirArg, rnRaw: renderAux(d, n, result)
   of rnRawHtml:
-    if d.target != outLatex:
+    if d.target != outLatex and not lastSon(n).isNil:
       result.add addNodes(lastSon(n))
   of rnRawLatex:
-    if d.target == outLatex:
+    if d.target == outLatex and not lastSon(n).isNil:
       result.add addNodes(lastSon(n))
 
   of rnImage, rnFigure: renderImage(d, n, result)
-  of rnCodeBlock: renderCodeBlock(d, n, result)
+  of rnCodeBlock, rnInlineCode: renderCode(d, n, result)
   of rnContainer: renderContainer(d, n, result)
   of rnSubstitutionReferences, rnSubstitutionDef:
     renderAux(d, n, "|$1|", "|$1|", result)
   of rnDirective:
     renderAux(d, n, "", "", result)
-  of rnGeneralRole:
+  of rnUnknownRole, rnCodeFragment:
     var tmp0 = ""
     var tmp1 = ""
     renderRstToOut(d, n.sons[0], tmp0)
     renderRstToOut(d, n.sons[1], tmp1)
-    dispA(d.target, result, "<span class=\"$2\">$1</span>", "\\span$2{$1}",
-          [tmp0, tmp1])
+    var class = tmp1
+    # don't allow missing role break latex compilation:
+    if d.target == outLatex and n.kind == rnUnknownRole: class = "Other"
+    if n.kind == rnCodeFragment:
+      dispA(d.target, result,
+            "<tt class=\"docutils literal\"><span class=\"pre $2\">" &
+              "$1</span></tt>",
+            "\\rstcode{\\span$2{$1}}", [tmp0, class])
+    else:  # rnUnknownRole, not necessarily code/monospace font
+      dispA(d.target, result, "<span class=\"$2\">$1</span>", "\\span$2{$1}",
+            [tmp0, class])
   of rnSub: renderAux(d, n, "<sub>$1</sub>", "\\rstsub{$1}", result)
   of rnSup: renderAux(d, n, "<sup>$1</sup>", "\\rstsup{$1}", result)
   of rnEmphasis: renderAux(d, n, "<em>$1</em>", "\\emph{$1}", result)
@@ -1121,20 +1351,27 @@ 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)
+      "\\rstcode{$1}", result)
+  of rnInlineTarget:
+    var tmp = ""
+    renderAux(d, n, tmp)
+    dispA(d.target, result,
+      "<span class=\"target\" id=\"$2\">$1</span>",
+      "\\label{$2}\\hypertarget{$2}{$1}",
+      [tmp, rstnodeToRefname(n)])
   of rnSmiley: renderSmiley(d, n, result)
-  of rnLeaf: result.add(esc(d.target, n.text))
+  of rnLeaf: result.add(esc(d.target, n.text, escMode=d.escMode))
   of rnContents: d.hasToc = true
+  of rnDefaultRole: discard
   of rnTitle:
     d.meta[metaTitle] = ""
     renderRstToOut(d, n.sons[0], d.meta[metaTitle])
+    d.meta[metaTitleRaw] = n.sons[0].addNodes
 
 # -----------------------------------------------------------------------------
 
@@ -1264,7 +1501,7 @@ $moduledesc
 $content
 </div>
 """)
-  setConfigVar("doc.listing_start", "<pre class = \"listing\">")
+  setConfigVar("doc.listing_start", "<pre$3 class = \"listing\">")
   setConfigVar("doc.listing_end", "</pre>")
   setConfigVar("doc.listing_button", "</pre>")
   setConfigVar("doc.body_no_toc", "$moduledesc $content")
@@ -1274,7 +1511,8 @@ $content
 # ---------- forum ---------------------------------------------------------
 
 proc rstToHtml*(s: string, options: RstParseOptions,
-                config: StringTableRef): string =
+                config: StringTableRef,
+                msgHandler: MsgHandler = rst.defaultMsgHandler): string {.gcsafe.} =
   ## Converts an input rst string into embeddable HTML.
   ##
   ## This convenience proc parses any input string using rst markup (it doesn't
@@ -1284,12 +1522,13 @@ proc rstToHtml*(s: string, options: RstParseOptions,
   ## work. For an explanation of the ``config`` parameter see the
   ## ``initRstGenerator`` proc. Example:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   import packages/docutils/rstgen, strtabs
   ##
   ##   echo rstToHtml("*Hello* **world**!", {},
   ##     newStringTable(modeStyleInsensitive))
   ##   # --> <em>Hello</em> <strong>world</strong>!
+  ##   ```
   ##
   ## If you need to allow the rst ``include`` directive or tweak the generated
   ## output you have to create your own ``RstGenerator`` with
@@ -1298,18 +1537,30 @@ proc rstToHtml*(s: string, options: RstParseOptions,
   proc myFindFile(filename: string): string =
     # we don't find any files in online mode:
     result = ""
+  proc myFindRefFile(filename: string): (string, string) =
+    result = ("", "")
 
   const filen = "input"
+  let (rst, filenames, t) = rstParse(s, filen,
+                                     line=LineRstInit, column=ColRstInit,
+                                     options, myFindFile, myFindRefFile, msgHandler)
   var d: RstGenerator
-  initRstGenerator(d, outHtml, config, filen, options, myFindFile,
-                   rst.defaultMsgHandler)
-  var dummyHasToc = false
-  var rst = rstParse(s, filen, 0, 1, dummyHasToc, options)
+  initRstGenerator(d, outHtml, config, filen, myFindFile, msgHandler,
+                   filenames, hasToc = t)
   result = ""
   renderRstToOut(d, rst, result)
-
-
-when isMainModule:
-  assert rstToHtml("*Hello* **world**!", {},
-    newStringTable(modeStyleInsensitive)) ==
-    "<em>Hello</em> <strong>world</strong>!"
+  strbasics.strip(result)
+
+
+proc rstToLatex*(rstSource: string; options: RstParseOptions): string {.inline, since: (1, 3).} =
+  ## Convenience proc for `renderRstToOut` and `initRstGenerator`.
+  runnableExamples: doAssert rstToLatex("*Hello* **world**", {}) == """\emph{Hello} \textbf{world}"""
+  if rstSource.len == 0: return
+  let (rst, filenames, t) = rstParse(rstSource, "",
+                                     line=LineRstInit, column=ColRstInit,
+                                     options)
+  var rstGenera: RstGenerator
+  rstGenera.initRstGenerator(outLatex, defaultConfig(), "input",
+                             filenames=filenames, hasToc = t)
+  rstGenera.renderRstToOut(rst, result)
+  strbasics.strip(result)
diff --git a/lib/packages/docutils/rstidx.nim b/lib/packages/docutils/rstidx.nim
new file mode 100644
index 000000000..1472d28fd
--- /dev/null
+++ b/lib/packages/docutils/rstidx.nim
@@ -0,0 +1,141 @@
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2022 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+
+## Nim `idx`:idx: file format related definitions.
+
+import std/[strutils, syncio, hashes]
+from std/os import splitFile
+
+type
+  IndexEntryKind* = enum ## discriminator tag
+    ieMarkupTitle = "markupTitle"
+                           ## RST/Markdown title, text in `keyword` +
+                           ## HTML text in `linkTitle`
+    ieNimTitle = "nimTitle"
+                           ## Nim title
+    ieHeading = "heading"  ## RST/Markdown markup heading, escaped
+    ieIdxRole = "idx"      ## RST :idx: definition, escaped
+    ieNim = "nim"          ## Nim symbol, unescaped
+    ieNimGroup = "nimgrp"  ## Nim overload group, unescaped
+  IndexEntry* = object
+    kind*: IndexEntryKind  ## 0.
+    keyword*: string       ## 1.
+    link*: string          ## 2.
+    linkTitle*: string     ## 3. contains a prettier text for the href
+    linkDesc*: string      ## 4. the title attribute of the final href
+    line*: int             ## 5.
+    module*: string        ## origin file, NOT a field in ``.idx`` file
+    aux*: string           ## auxuliary field, NOT a field in ``.idx`` file
+
+proc isDocumentationTitle*(hyperlink: string): bool =
+  ## Returns true if the hyperlink is actually a documentation title.
+  ##
+  ## Documentation titles lack the hash. See `mergeIndexes()
+  ## <#mergeIndexes,string>`_ for a more detailed explanation.
+  result = hyperlink.find('#') < 0
+
+proc `$`*(e: IndexEntry): string =
+  """("$1", "$2", "$3", "$4", $5)""" % [
+      e.keyword, e.link, e.linkTitle, e.linkDesc, $e.line]
+
+proc quoteIndexColumn(text: string): string =
+  ## Returns a safe version of `text` for serialization to the ``.idx`` file.
+  ##
+  ## The returned version can be put without worries in a line based tab
+  ## separated column text file. The following character sequence replacements
+  ## will be performed for that goal:
+  ##
+  ## * ``"\\"`` => ``"\\\\"``
+  ## * ``"\n"`` => ``"\\n"``
+  ## * ``"\t"`` => ``"\\t"``
+  result = newStringOfCap(text.len + 3)
+  for c in text:
+    case c
+    of '\\': result.add "\\"
+    of '\L': result.add "\\n"
+    of '\C': discard
+    of '\t': result.add "\\t"
+    else: result.add c
+
+proc unquoteIndexColumn*(text: string): string =
+  ## Returns the unquoted version generated by ``quoteIndexColumn``.
+  result = text.multiReplace(("\\t", "\t"), ("\\n", "\n"), ("\\\\", "\\"))
+
+proc formatIndexEntry*(kind: IndexEntryKind; htmlFile, id, term, linkTitle,
+                       linkDesc: string, line: int):
+                      tuple[entry: string, isTitle: bool] =
+  result.entry = $kind
+  result.entry.add('\t')
+  result.entry.add term
+  result.entry.add('\t')
+  result.entry.add(htmlFile)
+  if id.len > 0:
+    result.entry.add('#')
+    result.entry.add(id)
+    result.isTitle = false
+  else:
+    result.isTitle = true
+  result.entry.add('\t' & linkTitle.quoteIndexColumn)
+  result.entry.add('\t' & linkDesc.quoteIndexColumn)
+  result.entry.add('\t' & $line)
+  result.entry.add("\n")
+
+proc parseIndexEntryKind(s: string): IndexEntryKind =
+  result = case s:
+    of "nim": ieNim
+    of "nimgrp": ieNimGroup
+    of "heading": ieHeading
+    of "idx": ieIdxRole
+    of "nimTitle": ieNimTitle
+    of "markupTitle": ieMarkupTitle
+    else: raise newException(ValueError, "unknown index entry value $1" % [s])
+
+proc parseIdxFile*(path: string):
+    tuple[fileEntries: seq[IndexEntry], title: IndexEntry] =
+  var
+    f = 0
+  newSeq(result.fileEntries, 500)
+  setLen(result.fileEntries, 0)
+  let (_, base, _) = path.splitFile
+  for line in lines(path):
+    let s = line.find('\t')
+    if s < 0: continue
+    setLen(result.fileEntries, f+1)
+    let cols = line.split('\t')
+    result.fileEntries[f].kind = parseIndexEntryKind(cols[0])
+    result.fileEntries[f].keyword = cols[1]
+    result.fileEntries[f].link = cols[2]
+    if result.fileEntries[f].kind == ieIdxRole:
+      result.fileEntries[f].module = base
+    else:
+      if result.title.keyword.len == 0:
+        result.fileEntries[f].module = base
+      else:
+        result.fileEntries[f].module = result.title.keyword
+
+    result.fileEntries[f].linkTitle = cols[3].unquoteIndexColumn
+    result.fileEntries[f].linkDesc = cols[4].unquoteIndexColumn
+    result.fileEntries[f].line = parseInt(cols[5])
+
+    if result.fileEntries[f].kind in {ieNimTitle, ieMarkupTitle}:
+      result.title = result.fileEntries[f]
+    inc f
+
+proc cmp*(a, b: IndexEntry): int =
+  ## Sorts two ``IndexEntry`` first by `keyword` field, then by `link`.
+  result = cmpIgnoreStyle(a.keyword, b.keyword)
+  if result == 0:
+    result = cmpIgnoreStyle(a.link, b.link)
+
+proc hash*(x: IndexEntry): Hash =
+  ## Returns the hash for the combined fields of the type.
+  ##
+  ## The hash is computed as the chained hash of the individual string hashes.
+  result = x.keyword.hash !& x.link.hash
+  result = result !& x.linkTitle.hash
+  result = result !& x.linkDesc.hash
+  result = !$result
diff --git a/lib/packages/fsmonitor.nim b/lib/packages/fsmonitor.nim
deleted file mode 100644
index b22e84f44..000000000
--- a/lib/packages/fsmonitor.nim
+++ /dev/null
@@ -1,229 +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 allows you to monitor files or directories for changes using
-## asyncio.
-##
-## **Warning**: This module will likely disappear soon and be moved into a
-## new Nimble package.
-##
-## Windows support is not yet implemented.
-##
-## **Note:** This module uses ``inotify`` on Linux (Other Unixes are not yet
-## supported). ``inotify`` was merged into the 2.6.13 Linux kernel, this
-## module will therefore not work with any Linux kernel prior to that, unless
-## it has been patched to support inotify.
-
-when defined(linux) or defined(nimdoc):
-  from posix import read
-else:
-  {.error: "Your platform is not supported.".}
-
-import inotify, os, asyncio, tables
-
-type
-  FSMonitor* = ref FSMonitorObj
-  FSMonitorObj = object of RootObj
-    fd: cint
-    handleEvent: proc (m: FSMonitor, ev: MonitorEvent) {.closure.}
-    targets: Table[cint, string]
-
-  MonitorEventType* = enum ## Monitor event type
-    MonitorAccess,       ## File was accessed.
-    MonitorAttrib,       ## Metadata changed.
-    MonitorCloseWrite,   ## Writable file was closed.
-    MonitorCloseNoWrite, ## Non-writable file closed.
-    MonitorCreate,       ## Subfile was created.
-    MonitorDelete,       ## Subfile was deleted.
-    MonitorDeleteSelf,   ## Watched file/directory was itself deleted.
-    MonitorModify,       ## File was modified.
-    MonitorMoveSelf,     ## Self was moved.
-    MonitorMoved,        ## File was moved.
-    MonitorOpen,         ## File was opened.
-    MonitorAll           ## Filter for all event types.
-
-  MonitorEvent* = object
-    case kind*: MonitorEventType  ## Type of the event.
-    of MonitorMoveSelf, MonitorMoved:
-      oldPath*: string          ## Old absolute location
-      newPath*: string          ## New absolute location
-    else:
-      fullname*: string         ## Absolute filename of the file/directory affected.
-    name*: string             ## Non absolute filepath of the file/directory
-                              ## affected relative to the directory watched.
-                              ## "" if this event refers to the file/directory
-                              ## watched.
-    wd*: cint                 ## Watch descriptor.
-
-{.deprecated: [PFSMonitor: FSMonitor, TFSMonitor: FSMonitorObj,
-  TMonitorEventType: MonitorEventType, TMonitorEvent: MonitorEvent].}
-
-const
-  MaxEvents = 100
-
-proc newMonitor*(): FSMonitor =
-  ## Creates a new file system monitor.
-  new(result)
-  result.targets = initTable[cint, string]()
-  result.fd = inotifyInit()
-  if result.fd < 0:
-    raiseOSError(osLastError())
-
-proc add*(monitor: FSMonitor, target: string,
-               filters = {MonitorAll}): cint {.discardable.} =
-  ## Adds ``target`` which may be a directory or a file to the list of
-  ## watched paths of ``monitor``.
-  ## You can specify the events to report using the ``filters`` parameter.
-
-  var INFilter = 0
-  for f in filters:
-    case f
-    of MonitorAccess: INFilter = INFilter or IN_ACCESS
-    of MonitorAttrib: INFilter = INFilter or IN_ATTRIB
-    of MonitorCloseWrite: INFilter = INFilter or IN_CLOSE_WRITE
-    of MonitorCloseNoWrite: INFilter = INFilter or IN_CLOSE_NO_WRITE
-    of MonitorCreate: INFilter = INFilter or IN_CREATE
-    of MonitorDelete: INFilter = INFilter or IN_DELETE
-    of MonitorDeleteSelf: INFilter = INFilter or IN_DELETE_SELF
-    of MonitorModify: INFilter = INFilter or IN_MODIFY
-    of MonitorMoveSelf: INFilter = INFilter or IN_MOVE_SELF
-    of MonitorMoved: INFilter = INFilter or IN_MOVED_FROM or IN_MOVED_TO
-    of MonitorOpen: INFilter = INFilter or IN_OPEN
-    of MonitorAll: INFilter = INFilter or IN_ALL_EVENTS
-
-  result = inotifyAddWatch(monitor.fd, target, INFilter.uint32)
-  if result < 0:
-    raiseOSError(osLastError())
-  monitor.targets.add(result, target)
-
-proc del*(monitor: FSMonitor, wd: cint) =
-  ## Removes watched directory or file as specified by ``wd`` from ``monitor``.
-  ##
-  ## If ``wd`` is not a part of ``monitor`` an EOS error is raised.
-  if inotifyRmWatch(monitor.fd, wd) < 0:
-    raiseOSError(osLastError())
-
-proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] =
-  result = @[]
-  let size = (sizeof(INotifyEvent)+2000)*MaxEvents
-  var buffer = newString(size)
-
-  let le = read(fd, addr(buffer[0]), size)
-
-  var movedFrom = initTable[cint, tuple[wd: cint, old: string]]()
-
-  var i = 0
-  while i < le:
-    var event = cast[ptr INotifyEvent](addr(buffer[i]))
-    var mev: MonitorEvent
-    mev.wd = event.wd
-    if event.len.int != 0:
-      let cstr = event.name.addr.cstring
-      mev.name = $cstr
-    else:
-      mev.name = ""
-
-    if (event.mask.int and IN_MOVED_FROM) != 0:
-      # Moved from event, add to m's collection
-      movedFrom.add(event.cookie.cint, (mev.wd, mev.name))
-      inc(i, sizeof(INotifyEvent) + event.len.int)
-      continue
-    elif (event.mask.int and IN_MOVED_TO) != 0:
-      mev.kind = MonitorMoved
-      assert movedFrom.hasKey(event.cookie.cint)
-      # Find the MovedFrom event.
-      mev.oldPath = movedFrom[event.cookie.cint].old
-      mev.newPath = "" # Set later
-      # Delete it from the Table
-      movedFrom.del(event.cookie.cint)
-    elif (event.mask.int and IN_ACCESS) != 0: mev.kind = MonitorAccess
-    elif (event.mask.int and IN_ATTRIB) != 0: mev.kind = MonitorAttrib
-    elif (event.mask.int and IN_CLOSE_WRITE) != 0:
-      mev.kind = MonitorCloseWrite
-    elif (event.mask.int and IN_CLOSE_NOWRITE) != 0:
-      mev.kind = MonitorCloseNoWrite
-    elif (event.mask.int and IN_CREATE) != 0: mev.kind = MonitorCreate
-    elif (event.mask.int and IN_DELETE) != 0:
-      mev.kind = MonitorDelete
-    elif (event.mask.int and IN_DELETE_SELF) != 0:
-      mev.kind = MonitorDeleteSelf
-    elif (event.mask.int and IN_MODIFY) != 0: mev.kind = MonitorModify
-    elif (event.mask.int and IN_MOVE_SELF) != 0:
-      mev.kind = MonitorMoveSelf
-    elif (event.mask.int and IN_OPEN) != 0: mev.kind = MonitorOpen
-
-    if mev.kind != MonitorMoved:
-      mev.fullname = ""
-
-    result.add(mev)
-    inc(i, sizeof(INotifyEvent) + event.len.int)
-
-  # If movedFrom events have not been matched with a moveTo. File has
-  # been moved to an unwatched location, emit a MonitorDelete.
-  for cookie, t in pairs(movedFrom):
-    var mev: MonitorEvent
-    mev.kind = MonitorDelete
-    mev.wd = t.wd
-    mev.name = t.old
-    result.add(mev)
-
-proc FSMonitorRead(h: RootRef) =
-  var events = FSMonitor(h).getEvent(FSMonitor(h).fd)
-  #var newEv: MonitorEvent
-  for ev in events:
-    var target = FSMonitor(h).targets[ev.wd]
-    var newEv = ev
-    if newEv.kind == MonitorMoved:
-      newEv.oldPath = target / newEv.oldPath
-      newEv.newPath = target / newEv.name
-    else:
-      newEv.fullName = target / newEv.name
-    FSMonitor(h).handleEvent(FSMonitor(h), newEv)
-
-proc toDelegate(m: FSMonitor): Delegate =
-  result = newDelegate()
-  result.deleVal = m
-  result.fd = (type(result.fd))(m.fd)
-  result.mode = fmRead
-  result.handleRead = FSMonitorRead
-  result.open = true
-
-proc register*(d: Dispatcher, monitor: FSMonitor,
-               handleEvent: proc (m: FSMonitor, ev: MonitorEvent) {.closure.}) =
-  ## Registers ``monitor`` with dispatcher ``d``.
-  monitor.handleEvent = handleEvent
-  var deleg = toDelegate(monitor)
-  d.register(deleg)
-
-when not defined(testing) and isMainModule:
-  proc main =
-    var
-      disp = newDispatcher()
-      monitor = newMonitor()
-      n = 0
-    n = monitor.add("/tmp")
-    assert n == 1
-    n = monitor.add("/tmp", {MonitorAll})
-    assert n == 1
-    n = monitor.add("/tmp", {MonitorCloseWrite, MonitorCloseNoWrite})
-    assert n == 1
-    n = monitor.add("/tmp", {MonitorMoved, MonitorOpen, MonitorAccess})
-    assert n == 1
-    disp.register(monitor,
-      proc (m: FSMonitor, ev: MonitorEvent) =
-        echo("Got event: ", ev.kind)
-        if ev.kind == MonitorMoved:
-          echo("From ", ev.oldPath, " to ", ev.newPath)
-          echo("Name is ", ev.name)
-        else:
-          echo("Name ", ev.name, " fullname ", ev.fullName))
-
-    while true:
-      if not disp.poll(): break
-  main()
diff --git a/lib/posix/epoll.nim b/lib/posix/epoll.nim
index 2d38137bb..007488354 100644
--- a/lib/posix/epoll.nim
+++ b/lib/posix/epoll.nim
@@ -7,9 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-{.deadCodeElim: on.}  # dce option deprecated
-
-from posix import SocketHandle
+from std/posix import SocketHandle
 
 const
   EPOLLIN* = 0x00000001
@@ -23,6 +21,7 @@ const
   EPOLLWRBAND* = 0x00000200
   EPOLLMSG* = 0x00000400
   EPOLLRDHUP* = 0x00002000
+  EPOLLEXCLUSIVE* = 1 shl 28
   EPOLLWAKEUP* = 1 shl 29
   EPOLLONESHOT* = 1 shl 30
   EPOLLET* = 1 shl 31
@@ -34,56 +33,67 @@ const
   EPOLL_CTL_DEL* = 2          # Remove a file descriptor from the interface.
   EPOLL_CTL_MOD* = 3          # Change file descriptor epoll_event structure.
 
+# https://github.com/torvalds/linux/blob/ff6992735ade75aae3e35d16b17da1008d753d28/include/uapi/linux/eventpoll.h#L77
+when defined(linux) and defined(amd64):
+  {.pragma: epollPacked, packed.}
+else:
+  {.pragma: epollPacked.}
+
 type
-  EpollData* {.importc: "union epoll_data",
-      header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
+  EpollData* {.importc: "epoll_data_t",
+      header: "<sys/epoll.h>", pure, final, union.} = object
+    `ptr`* {.importc: "ptr".}: pointer
+    fd* {.importc: "fd".}: cint
+    u32* {.importc: "u32".}: uint32
     u64* {.importc: "u64".}: uint64
 
-  EpollEvent* {.importc: "struct epoll_event", header: "<sys/epoll.h>", pure, final.} = object
+  EpollEvent* {.importc: "struct epoll_event", header: "<sys/epoll.h>", pure, final, epollPacked.} = object
     events*: uint32 # Epoll events
     data*: EpollData # User data variable
 
 proc epoll_create*(size: cint): cint {.importc: "epoll_create",
     header: "<sys/epoll.h>".}
   ## Creates an epoll instance.  Returns an fd for the new instance.
-  ##   The "size" parameter is a hint specifying the number of file
-  ##   descriptors to be associated with the new instance.  The fd
-  ##   returned by epoll_create() should be closed with close().
+  ##
+  ## The "size" parameter is a hint specifying the number of file
+  ## descriptors to be associated with the new instance.  The fd
+  ## returned by epoll_create() should be closed with close().
 
 proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1",
     header: "<sys/epoll.h>".}
   ## Same as epoll_create but with an FLAGS parameter.  The unused SIZE
-  ##   parameter has been dropped.
+  ## parameter has been dropped.
 
 proc epoll_ctl*(epfd: cint; op: cint; fd: cint | SocketHandle; event: ptr EpollEvent): cint {.
     importc: "epoll_ctl", header: "<sys/epoll.h>".}
-  ## Manipulate an epoll instance "epfd". Returns 0 in case of success,
-  ##   -1 in case of error ( the "errno" variable will contain the
-  ##   specific error code ) The "op" parameter is one of the EPOLL_CTL_*
-  ##   constants defined above. The "fd" parameter is the target of the
-  ##   operation. The "event" parameter describes which events the caller
-  ##   is interested in and any associated user data.
+  ## Manipulate an epoll instance "epfd". Returns `0` in case of success,
+  ## `-1` in case of error (the "errno" variable will contain the specific error code).
+  ##
+  ## The "op" parameter is one of the `EPOLL_CTL_*`
+  ## constants defined above. The "fd" parameter is the target of the
+  ## operation. The "event" parameter describes which events the caller
+  ## is interested in and any associated user data.
 
 proc epoll_wait*(epfd: cint; events: ptr EpollEvent; maxevents: cint;
                  timeout: cint): cint {.importc: "epoll_wait",
     header: "<sys/epoll.h>".}
   ## Wait for events on an epoll instance "epfd". Returns the number of
-  ##   triggered events returned in "events" buffer. Or -1 in case of
-  ##   error with the "errno" variable set to the specific error code. The
-  ##   "events" parameter is a buffer that will contain triggered
-  ##   events. The "maxevents" is the maximum number of events to be
-  ##   returned ( usually size of "events" ). The "timeout" parameter
-  ##   specifies the maximum wait time in milliseconds (-1 == infinite).
+  ## triggered events returned in "events" buffer. Or -1 in case of
+  ## error with the "errno" variable set to the specific error code. The
+  ## "events" parameter is a buffer that will contain triggered
+  ## events. The "maxevents" is the maximum number of events to be
+  ## returned ( usually size of "events" ). The "timeout" parameter
+  ## specifies the maximum wait time in milliseconds (-1 == infinite).
   ##
-  ##   This function is a cancellation point and therefore not marked with
-  ##   __THROW.
+  ## This function is a cancellation point and therefore not marked with
+  ## __THROW.
 
 
 #proc epoll_pwait*(epfd: cint; events: ptr EpollEvent; maxevents: cint;
 #                  timeout: cint; ss: ptr sigset_t): cint {.
 #    importc: "epoll_pwait", header: "<sys/epoll.h>".}
 # Same as epoll_wait, but the thread's signal mask is temporarily
-#   and atomically replaced with the one provided as parameter.
+# and atomically replaced with the one provided as parameter.
 #
-#   This function is a cancellation point and therefore not marked with
-#   __THROW.
+# This function is a cancellation point and therefore not marked with
+# __THROW.
diff --git a/lib/posix/inotify.nim b/lib/posix/inotify.nim
index 359e88617..575accc18 100644
--- a/lib/posix/inotify.nim
+++ b/lib/posix/inotify.nim
@@ -7,67 +7,105 @@
 #    distribution, for details about the copyright.
 #
 
-{.deadCodeElim: on.}  # dce option deprecated
+when defined(nimPreviewSlimSystem):
+  import std/syncio
 
 # Get the platform-dependent flags.
 # Structure describing an inotify event.
 type
-  InotifyEvent*{.pure, final, importc: "struct inotify_event",
-                   header: "<sys/inotify.h>".} = object
-    wd*{.importc: "wd".}: cint # Watch descriptor.
-    mask*{.importc: "mask".}: uint32 # Watch mask.
-    cookie*{.importc: "cookie".}: uint32 # Cookie to synchronize two events.
-    len*{.importc: "len".}: uint32 # Length (including NULs) of name.
-    name*{.importc: "name".}: char # Name.
-{.deprecated: [Tinotify_event: InotifyEvent].}
+  InotifyEvent* {.pure, final, importc: "struct inotify_event",
+                  header: "<sys/inotify.h>",
+                  completeStruct.} = object            ## An Inotify event.
+    wd* {.importc: "wd".}: FileHandle                  ## Watch descriptor.
+    mask* {.importc: "mask".}: uint32                  ## Watch mask.
+    cookie* {.importc: "cookie".}: uint32              ## Cookie to synchronize two events.
+    len* {.importc: "len".}: uint32                    ## Length (including NULs) of name.
+    name* {.importc: "name".}: UncheckedArray[char]    ## Name.
 
 # Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH.
 const
-  IN_ACCESS* = 0x00000001   # File was accessed.
-  IN_MODIFY* = 0x00000002   # File was modified.
-  IN_ATTRIB* = 0x00000004   # Metadata changed.
-  IN_CLOSE_WRITE* = 0x00000008 # Writtable file was closed.
-  IN_CLOSE_NOWRITE* = 0x00000010 # Unwrittable file closed.
-  IN_CLOSE* = (IN_CLOSE_WRITE or IN_CLOSE_NOWRITE) # Close.
-  IN_OPEN* = 0x00000020     # File was opened.
-  IN_MOVED_FROM* = 0x00000040 # File was moved from X.
-  IN_MOVED_TO* = 0x00000080 # File was moved to Y.
-  IN_MOVE* = (IN_MOVED_FROM or IN_MOVED_TO) # Moves.
-  IN_CREATE* = 0x00000100   # Subfile was created.
-  IN_DELETE* = 0x00000200   # Subfile was deleted.
-  IN_DELETE_SELF* = 0x00000400 # Self was deleted.
-  IN_MOVE_SELF* = 0x00000800 # Self was moved.
+  IN_ACCESS* = 0x00000001                          ## File was accessed.
+  IN_MODIFY* = 0x00000002                          ## File was modified.
+  IN_ATTRIB* = 0x00000004                          ## Metadata changed.
+  IN_CLOSE_WRITE* = 0x00000008                     ## Writtable file was closed.
+  IN_CLOSE_NOWRITE* = 0x00000010                   ## Unwrittable file closed.
+  IN_CLOSE* = (IN_CLOSE_WRITE or IN_CLOSE_NOWRITE) ## Close.
+  IN_OPEN* = 0x00000020                            ## File was opened.
+  IN_MOVED_FROM* = 0x00000040                      ## File was moved from X.
+  IN_MOVED_TO* = 0x00000080                        ## File was moved to Y.
+  IN_MOVE* = (IN_MOVED_FROM or IN_MOVED_TO)        ## Moves.
+  IN_CREATE* = 0x00000100                          ## Subfile was created.
+  IN_DELETE* = 0x00000200                          ## Subfile was deleted.
+  IN_DELETE_SELF* = 0x00000400                     ## Self was deleted.
+  IN_MOVE_SELF* = 0x00000800                       ## Self was moved.
+
 # Events sent by the kernel.
 const
-  IN_UNMOUNT* = 0x00002000  # Backing fs was unmounted.
-  IN_Q_OVERFLOW* = 0x00004000 # Event queued overflowed.
-  IN_IGNORED* = 0x00008000  # File was ignored.
+  IN_UNMOUNT* = 0x00002000    ## Backing fs was unmounted.
+  IN_Q_OVERFLOW* = 0x00004000 ## Event queued overflowed.
+  IN_IGNORED* = 0x00008000    ## File was ignored.
+
 # Special flags.
 const
-  IN_ONLYDIR* = 0x01000000  # Only watch the path if it is a
-                            #        directory.
-  IN_DONT_FOLLOW* = 0x02000000 # Do not follow a sym link.
-  IN_EXCL_UNLINK* = 0x04000000 # Exclude events on unlinked
-                               #        objects.
-  IN_MASK_ADD* = 0x20000000 # Add to the mask of an already
-                            #        existing watch.
-  IN_ISDIR* = 0x40000000    # Event occurred against dir.
-  IN_ONESHOT* = 0x80000000  # Only send event once.
+  IN_ONLYDIR* = 0x01000000     ## Only watch the path if it is a directory.
+  IN_DONT_FOLLOW* = 0x02000000 ## Do not follow a sym link.
+  IN_EXCL_UNLINK* = 0x04000000 ## Exclude events on unlinked objects.
+  IN_MASK_ADD* = 0x20000000    ## Add to the mask of an already existing watch.
+  IN_ISDIR* = 0x40000000       ## Event occurred against dir.
+  IN_ONESHOT* = 0x80000000     ## Only send event once.
+
 # All events which a program can wait on.
 const
   IN_ALL_EVENTS* = (IN_ACCESS or IN_MODIFY or IN_ATTRIB or IN_CLOSE_WRITE or
       IN_CLOSE_NOWRITE or IN_OPEN or IN_MOVED_FROM or IN_MOVED_TO or
       IN_CREATE or IN_DELETE or IN_DELETE_SELF or IN_MOVE_SELF)
-# Create and initialize inotify instance.
-proc inotify_init*(): cint{.cdecl, importc: "inotify_init",
-                            header: "<sys/inotify.h>".}
-# Create and initialize inotify instance.
-proc inotify_init1*(flags: cint): cint{.cdecl, importc: "inotify_init1",
+
+
+proc inotify_init*(): FileHandle {.cdecl, importc: "inotify_init",
+    header: "<sys/inotify.h>".}
+  ## Create and initialize inotify instance.
+
+proc inotify_init1*(flags: cint): FileHandle {.cdecl, importc: "inotify_init1",
     header: "<sys/inotify.h>".}
-# Add watch of object NAME to inotify instance FD.  Notify about
-#   events specified by MASK.
-proc inotify_add_watch*(fd: cint; name: cstring; mask: uint32): cint{.
-    cdecl, importc: "inotify_add_watch", header: "<sys/inotify.h>".}
-# Remove the watch specified by WD from the inotify instance FD.
-proc inotify_rm_watch*(fd: cint; wd: cint): cint{.cdecl,
+  ## Like `inotify_init<#inotify_init>`_ ,
+  ## but has a flags argument that provides access to some extra functionality.
+
+proc inotify_add_watch*(fd: cint; name: cstring; mask: uint32): cint {.cdecl,
+    importc: "inotify_add_watch", header: "<sys/inotify.h>".}
+  ## Add watch of object NAME to inotify instance FD. Notify about events specified by MASK.
+
+proc inotify_rm_watch*(fd: cint; wd: cint): cint {.cdecl,
     importc: "inotify_rm_watch", header: "<sys/inotify.h>".}
+  ## Remove the watch specified by WD from the inotify instance FD.
+
+iterator inotify_events*(evs: pointer, n: int): ptr InotifyEvent =
+  ## Abstract the packed buffer interface to yield event object pointers.
+  runnableExamples("-r:off"):
+    when defined(linux):
+       import std/posix  # needed for FileHandle read procedure
+       const MaxWatches = 8192
+
+       let inotifyFd = inotify_init()  # create new inotify instance and get it's FileHandle
+       let wd = inotifyFd.inotify_add_watch("/tmp", IN_CREATE or IN_DELETE)  # Add new watch
+
+       var events: array[MaxWatches, byte]  # event buffer
+       while (let n = read(inotifyFd, addr events, MaxWatches); n) > 0:  # blocks until any events have been read
+         for e in inotify_events(addr events, n):
+           echo (e[].wd, e[].mask, cast[cstring](addr e[].name))    # echo watch id, mask, and name value of each event
+  var ev: ptr InotifyEvent = cast[ptr InotifyEvent](evs)
+  var n = n
+  while n > 0:
+    yield ev
+    let sz = InotifyEvent.sizeof + int(ev[].len)
+    n -= sz
+    ev = cast[ptr InotifyEvent](cast[uint](ev) + uint(sz))
+
+runnableExamples:
+  when defined(linux):
+    let inotifyFd = inotify_init()  # create and get new inotify FileHandle
+    doAssert inotifyFd >= 0         # check for errors
+
+    let wd = inotifyFd.inotify_add_watch("/tmp", IN_CREATE or IN_DELETE)  # Add new watch
+    doAssert wd >= 0                 # check for errors
+
+    discard inotifyFd.inotify_rm_watch(wd) # remove watch
diff --git a/lib/posix/kqueue.nim b/lib/posix/kqueue.nim
index 18b47f5d5..2450cdb42 100644
--- a/lib/posix/kqueue.nim
+++ b/lib/posix/kqueue.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-from posix import Timespec
+from std/posix import Timespec
 
 when defined(macosx) or defined(freebsd) or defined(openbsd) or
      defined(dragonfly):
@@ -153,7 +153,7 @@ proc kevent*(kqFD: cint,
              changelist: ptr KEvent, nchanges: cint,
              eventlist: ptr KEvent, nevents: cint, timeout: ptr Timespec): cint
      {.importc: "kevent", header: "<sys/event.h>".}
-  ## Manipulates queue for given ``kqFD`` descriptor.
+  ## Manipulates queue for given `kqFD` descriptor.
 
 proc EV_SET*(event: ptr KEvent, ident: uint, filter: cshort, flags: cushort,
              fflags: cuint, data: int, udata: pointer)
diff --git a/lib/posix/linux.nim b/lib/posix/linux.nim
index 25c7c7979..29fd4288d 100644
--- a/lib/posix/linux.nim
+++ b/lib/posix/linux.nim
@@ -1,28 +1,37 @@
-{.deadCodeElim: on.}  # dce option deprecated
-
-import posix
+import std/posix
 
+## Flags of `clone` syscall.
+## See `clone syscall manual
+## <https://man7.org/linux/man-pages/man2/clone.2.html>`_ for more information.
 const
-  CSIGNAL* = 0x000000FF
-  CLONE_VM* = 0x00000100
-  CLONE_FS* = 0x00000200
-  CLONE_FILES* = 0x00000400
-  CLONE_SIGHAND* = 0x00000800
-  CLONE_PTRACE* = 0x00002000
-  CLONE_VFORK* = 0x00004000
-  CLONE_PARENT* = 0x00008000
-  CLONE_THREAD* = 0x00010000
-  CLONE_NEWNS* = 0x00020000
-  CLONE_SYSVSEM* = 0x00040000
-  CLONE_SETTLS* = 0x00080000
-  CLONE_PARENT_SETTID* = 0x00100000
-  CLONE_CHILD_CLEARTID* = 0x00200000
-  CLONE_DETACHED* = 0x00400000
-  CLONE_UNTRACED* = 0x00800000
-  CLONE_CHILD_SETTID* = 0x01000000
-  CLONE_STOPPED* = 0x02000000
+  CSIGNAL* = 0x000000FF'i32
+  CLONE_VM* = 0x00000100'i32
+  CLONE_FS* = 0x00000200'i32
+  CLONE_FILES* = 0x00000400'i32
+  CLONE_SIGHAND* = 0x00000800'i32
+  CLONE_PIDFD* = 0x00001000'i32
+  CLONE_PTRACE* = 0x00002000'i32
+  CLONE_VFORK* = 0x00004000'i32
+  CLONE_PARENT* = 0x00008000'i32
+  CLONE_THREAD* = 0x00010000'i32
+  CLONE_NEWNS* = 0x00020000'i32
+  CLONE_SYSVSEM* = 0x00040000'i32
+  CLONE_SETTLS* = 0x00080000'i32
+  CLONE_PARENT_SETTID* = 0x00100000'i32
+  CLONE_CHILD_CLEARTID* = 0x00200000'i32
+  CLONE_DETACHED* = 0x00400000'i32
+  CLONE_UNTRACED* = 0x00800000'i32
+  CLONE_CHILD_SETTID* = 0x01000000'i32
+  CLONE_NEWCGROUP* = 0x02000000'i32
+  CLONE_NEWUTS* = 0x04000000'i32
+  CLONE_NEWIPC* = 0x08000000'i32
+  CLONE_NEWUSER* = 0x10000000'i32
+  CLONE_NEWPID* = 0x20000000'i32
+  CLONE_NEWNET* = 0x40000000'i32
+  CLONE_IO* = 0x80000000'i32
+
 
-# fn should be of type proc (a2: pointer): void {.cdecl.}
+# fn should be of type proc (a2: pointer) {.cdecl.}
 proc clone*(fn: pointer; child_stack: pointer; flags: cint;
             arg: pointer; ptid: ptr Pid; tls: pointer;
             ctid: ptr Pid): cint {.importc, header: "<sched.h>".}
diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim
index db5f575af..fbe945df3 100644
--- a/lib/posix/posix.nim
+++ b/lib/posix/posix.nim
@@ -16,7 +16,10 @@
 ## convenience: cstrings are used instead of proper Nim strings and
 ## return codes indicate errors. If you want exceptions
 ## and a proper Nim-like interface, use the OS module or write a wrapper.
-
+##
+## For high-level wrappers specialized for Linux and BSDs see:
+## `posix_utils <posix_utils.html>`_
+##
 ## Coding conventions:
 ## ALL types are named the same as in the POSIX standard except that they start
 ## with 'T' or 'P' (if they are pointers) and without the '_t' suffix to be
@@ -24,38 +27,43 @@
 ## the \`identifier\` notation is used.
 ##
 ## This library relies on the header files of your C compiler. The
-## resulting C code will just ``#include <XYZ.h>`` and *not* define the
+## resulting C code will just `#include <XYZ.h>` and *not* define the
 ## symbols declared here.
 
 # Dead code elimination ensures that we don't accidentally generate #includes
 # for files that might not exist on a specific platform! The user will get an
-# error only if they actualy try to use the missing declaration
-{.deadCodeElim: on.}  # dce option deprecated
+# error only if they actually try to use the missing declaration
+
+when defined(nimHasStyleChecks):
+  {.push styleChecks: off.}
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
 
 # TODO these constants don't seem to be fetched from a header file for unknown
 #      platforms - where do they come from and why are they here?
 when false:
   const
-    C_IRUSR = 0c000400 ## Read by owner.
-    C_IWUSR = 0c000200 ## Write by owner.
-    C_IXUSR = 0c000100 ## Execute by owner.
-    C_IRGRP = 0c000040 ## Read by group.
-    C_IWGRP = 0c000020 ## Write by group.
-    C_IXGRP = 0c000010 ## Execute by group.
-    C_IROTH = 0c000004 ## Read by others.
-    C_IWOTH = 0c000002 ## Write by others.
-    C_IXOTH = 0c000001 ## Execute by others.
-    C_ISUID = 0c004000 ## Set user ID.
-    C_ISGID = 0c002000 ## Set group ID.
-    C_ISVTX = 0c001000 ## On directories, restricted deletion flag.
-    C_ISDIR = 0c040000 ## Directory.
-    C_ISFIFO = 0c010000 ##FIFO.
-    C_ISREG = 0c100000 ## Regular file.
-    C_ISBLK = 0c060000 ## Block special.
-    C_ISCHR = 0c020000 ## Character special.
-    C_ISCTG = 0c110000 ## Reserved.
-    C_ISLNK = 0c120000 ## Symbolic link.</p>
-    C_ISSOCK = 0c140000 ## Socket.
+    C_IRUSR = 0o000400 ## Read by owner.
+    C_IWUSR = 0o000200 ## Write by owner.
+    C_IXUSR = 0o000100 ## Execute by owner.
+    C_IRGRP = 0o000040 ## Read by group.
+    C_IWGRP = 0o000020 ## Write by group.
+    C_IXGRP = 0o000010 ## Execute by group.
+    C_IROTH = 0o000004 ## Read by others.
+    C_IWOTH = 0o000002 ## Write by others.
+    C_IXOTH = 0o000001 ## Execute by others.
+    C_ISUID = 0o004000 ## Set user ID.
+    C_ISGID = 0o002000 ## Set group ID.
+    C_ISVTX = 0o001000 ## On directories, restricted deletion flag.
+    C_ISDIR = 0o040000 ## Directory.
+    C_ISFIFO = 0o010000 ##FIFO.
+    C_ISREG = 0o100000 ## Regular file.
+    C_ISBLK = 0o060000 ## Block special.
+    C_ISCHR = 0o020000 ## Character special.
+    C_ISCTG = 0o110000 ## Reserved.
+    C_ISLNK = 0o120000 ## Symbolic link.</p>
+    C_ISSOCK = 0o140000 ## Socket.
 
 const
   MM_NULLLBL* = nil
@@ -83,22 +91,29 @@ const
 type Sighandler = proc (a: cint) {.noconv.}
 
 const StatHasNanoseconds* = defined(linux) or defined(freebsd) or
-    defined(openbsd) or defined(dragonfly) ## \
+    defined(osx) or defined(openbsd) or defined(dragonfly) or defined(haiku) ## \
   ## Boolean flag that indicates if the system supports nanosecond time
-  ## resolution in the fields of ``Stat``. Note that the nanosecond based fields
-  ## (``Stat.st_atim``, ``Stat.st_mtim`` and ``Stat.st_ctim``) can be accessed
+  ## resolution in the fields of `Stat`. Note that the nanosecond based fields
+  ## (`Stat.st_atim`, `Stat.st_mtim` and `Stat.st_ctim`) can be accessed
   ## without checking this flag, because this module defines fallback procs
   ## when they are not available.
 
 # Platform specific stuff
 
-when defined(linux) and defined(amd64):
+when (defined(linux) and not defined(android)) and defined(amd64):
   include posix_linux_amd64
+elif defined(openbsd) and defined(amd64):
+  include posix_openbsd_amd64
+elif (defined(macos) or defined(macosx) or defined(bsd)) and defined(cpu64):
+  include posix_macos_amd64
+elif defined(nintendoswitch):
+  include posix_nintendoswitch
+elif defined(haiku):
+  include posix_haiku
 else:
   include posix_other
 
 # There used to be this name in posix.nim a long time ago, not sure why!
-{.deprecated: [cSIG_HOLD: SIG_HOLD].}
 
 when StatHasNanoseconds:
   proc st_atime*(s: Stat): Time {.inline.} =
@@ -111,13 +126,13 @@ when StatHasNanoseconds:
     ## Second-granularity time of last status change.
     result = s.st_ctim.tv_sec
 else:
-  proc st_atim*(s: Stat): TimeSpec {.inline.} =
+  proc st_atim*(s: Stat): Timespec {.inline.} =
     ## Nanosecond-granularity time of last access.
     result.tv_sec = s.st_atime
-  proc st_mtim*(s: Stat): TimeSpec {.inline.} =
+  proc st_mtim*(s: Stat): Timespec {.inline.} =
     ## Nanosecond-granularity time of last data modification.
     result.tv_sec = s.st_mtime
-  proc st_ctim*(s: Stat): TimeSpec {.inline.} =
+  proc st_ctim*(s: Stat): Timespec {.inline.} =
     ## Nanosecond-granularity time of last data modification.
     result.tv_sec = s.st_ctime
 
@@ -139,11 +154,13 @@ proc htons*(a1: uint16): uint16 {.importc, header: "<arpa/inet.h>".}
 proc ntohl*(a1: uint32): uint32 {.importc, header: "<arpa/inet.h>".}
 proc ntohs*(a1: uint16): uint16 {.importc, header: "<arpa/inet.h>".}
 
-proc inet_addr*(a1: cstring): InAddrT {.importc, header: "<arpa/inet.h>".}
-proc inet_ntoa*(a1: InAddr): cstring {.importc, header: "<arpa/inet.h>".}
-proc inet_ntop*(a1: cint, a2: pointer, a3: cstring, a4: int32): cstring {.
+when not defined(zephyr):
+  proc inet_addr*(a1: cstring): InAddrT {.importc, header: "<arpa/inet.h>".}
+  proc inet_ntoa*(a1: InAddr): cstring {.importc, header: "<arpa/inet.h>".}
+
+proc inet_ntop*(a1: cint, a2: pointer | ptr InAddr | ptr In6Addr, a3: cstring, a4: int32): cstring {.
   importc:"(char *)$1", header: "<arpa/inet.h>".}
-proc inet_pton*(a1: cint, a2: cstring, a3: pointer): cint {.
+proc inet_pton*(a1: cint, a2: cstring, a3: pointer | ptr InAddr | ptr In6Addr): cint {.
   importc, header: "<arpa/inet.h>".}
 
 var
@@ -155,29 +172,54 @@ proc IN6ADDR_LOOPBACK_INIT* (): In6Addr {.importc, header: "<netinet/in.h>".}
 
 # dirent.h
 proc closedir*(a1: ptr DIR): cint  {.importc, header: "<dirent.h>".}
-proc opendir*(a1: cstring): ptr DIR {.importc, header: "<dirent.h>".}
-proc readdir*(a1: ptr DIR): ptr Dirent  {.importc, header: "<dirent.h>".}
+proc opendir*(a1: cstring): ptr DIR {.importc, header: "<dirent.h>", sideEffect.}
+proc readdir*(a1: ptr DIR): ptr Dirent  {.importc, header: "<dirent.h>", sideEffect.}
 proc readdir_r*(a1: ptr DIR, a2: ptr Dirent, a3: ptr ptr Dirent): cint  {.
-                importc, header: "<dirent.h>".}
+                importc, header: "<dirent.h>", sideEffect.}
 proc rewinddir*(a1: ptr DIR)  {.importc, header: "<dirent.h>".}
 proc seekdir*(a1: ptr DIR, a2: int)  {.importc, header: "<dirent.h>".}
 proc telldir*(a1: ptr DIR): int {.importc, header: "<dirent.h>".}
 
 # dlfcn.h
-proc dlclose*(a1: pointer): cint {.importc, header: "<dlfcn.h>".}
-proc dlerror*(): cstring {.importc, header: "<dlfcn.h>".}
-proc dlopen*(a1: cstring, a2: cint): pointer {.importc, header: "<dlfcn.h>".}
-proc dlsym*(a1: pointer, a2: cstring): pointer {.importc, header: "<dlfcn.h>".}
-
-proc creat*(a1: cstring, a2: Mode): cint {.importc, header: "<fcntl.h>".}
-proc fcntl*(a1: cint | SocketHandle, a2: cint): cint {.varargs, importc, header: "<fcntl.h>".}
-proc open*(a1: cstring, a2: cint): cint {.varargs, importc, header: "<fcntl.h>".}
+proc dlclose*(a1: pointer): cint {.importc, header: "<dlfcn.h>", sideEffect.}
+proc dlerror*(): cstring {.importc, header: "<dlfcn.h>", sideEffect.}
+proc dlopen*(a1: cstring, a2: cint): pointer {.importc, header: "<dlfcn.h>", sideEffect.}
+proc dlsym*(a1: pointer, a2: cstring): pointer {.importc, header: "<dlfcn.h>", sideEffect.}
+
+proc creat*(a1: cstring, a2: Mode): cint {.importc, header: "<fcntl.h>", sideEffect.}
+proc fcntl*(a1: cint | SocketHandle, a2: cint): cint {.varargs, importc, header: "<fcntl.h>", sideEffect.}
+proc openImpl(a1: cstring, a2: cint): cint {.varargs, importc: "open", header: "<fcntl.h>", sideEffect.}
+proc open*(a1: cstring, a2: cint, mode: Mode | cint = 0.Mode): cint {.inline.} =
+  # prevents bug #17888
+  openImpl(a1, a2, mode)
+
 proc posix_fadvise*(a1: cint, a2, a3: Off, a4: cint): cint {.
   importc, header: "<fcntl.h>".}
-proc posix_fallocate*(a1: cint, a2, a3: Off): cint {.
-  importc, header: "<fcntl.h>".}
 
-when not defined(haiku) and not defined(OpenBSD):
+proc ftruncate*(a1: cint, a2: Off): cint {.importc, header: "<unistd.h>".}
+when defined(osx):              # 2001 POSIX evidently does not concern Apple
+  type FStore {.importc: "fstore_t", header: "<fcntl.h>", bycopy.} = object
+    fst_flags: uint32           ## IN: flags word
+    fst_posmode: cint           ## IN: indicates offset field
+    fst_offset,                 ## IN: start of the region
+      fst_length,               ## IN: size of the region
+      fst_bytesalloc: Off       ## OUT: number of bytes allocated
+  var F_PEOFPOSMODE {.importc, header: "<fcntl.h>".}: cint
+  var F_ALLOCATEALL {.importc, header: "<fcntl.h>".}: uint32
+  var F_PREALLOCATE {.importc, header: "<fcntl.h>".}: cint
+  proc posix_fallocate*(a1: cint, a2, a3: Off): cint =
+    var fst = FStore(fst_flags: F_ALLOCATEALL, fst_posmode: F_PEOFPOSMODE,
+                     fst_offset: a2, fst_length: a3)
+    # Must also call ftruncate to match what POSIX does. Unlike posix_fallocate,
+    # this can shrink files.  Could guard w/getFileSize, but caller likely knows
+    # present size & has no good reason to call this unless it is growing.
+    if fcntl(a1, F_PREALLOCATE, fst.addr) != cint(-1): ftruncate(a1, a2 + a3)
+    else: cint(-1)
+else:
+  proc posix_fallocate*(a1: cint, a2, a3: Off): cint {.
+    importc, header: "<fcntl.h>".}
+
+when not defined(haiku) and not defined(openbsd):
   proc fmtmsg*(a1: int, a2: cstring, a3: cint,
               a4, a5, a6: cstring): cint {.importc, header: "<fmtmsg.h>".}
 
@@ -185,7 +227,7 @@ proc fnmatch*(a1, a2: cstring, a3: cint): cint {.importc, header: "<fnmatch.h>".
 proc ftw*(a1: cstring,
          a2: proc (x1: cstring, x2: ptr Stat, x3: cint): cint {.noconv.},
          a3: cint): cint {.importc, header: "<ftw.h>".}
-when not (defined(linux) and defined(amd64)):
+when not (defined(linux) and defined(amd64)) and not defined(nintendoswitch):
   proc nftw*(a1: cstring,
             a2: proc (x1: cstring, x2: ptr Stat,
                       x3: cint, x4: ptr FTW): cint {.noconv.},
@@ -194,7 +236,9 @@ when not (defined(linux) and defined(amd64)):
 
 proc glob*(a1: cstring, a2: cint,
           a3: proc (x1: cstring, x2: cint): cint {.noconv.},
-          a4: ptr Glob): cint {.importc, header: "<glob.h>".}
+          a4: ptr Glob): cint {.importc, header: "<glob.h>", sideEffect.}
+  ## Filename globbing. Use `os.walkPattern() <os.html#glob_1>`_ and similar.
+
 proc globfree*(a1: ptr Glob) {.importc, header: "<glob.h>".}
 
 proc getgrgid*(a1: Gid): ptr Group {.importc, header: "<grp.h>".}
@@ -221,30 +265,57 @@ proc dirname*(a1: cstring): cstring {.importc, header: "<libgen.h>".}
 
 proc localeconv*(): ptr Lconv {.importc, header: "<locale.h>".}
 proc setlocale*(a1: cint, a2: cstring): cstring {.
-                importc, header: "<locale.h>".}
+                importc, header: "<locale.h>", sideEffect.}
 
 proc strfmon*(a1: cstring, a2: int, a3: cstring): int {.varargs,
    importc, header: "<monetary.h>".}
 
-proc mq_close*(a1: Mqd): cint {.importc, header: "<mqueue.h>".}
-proc mq_getattr*(a1: Mqd, a2: ptr MqAttr): cint {.
-  importc, header: "<mqueue.h>".}
-proc mq_notify*(a1: Mqd, a2: ptr SigEvent): cint {.
-  importc, header: "<mqueue.h>".}
-proc mq_open*(a1: cstring, a2: cint): Mqd {.
-  varargs, importc, header: "<mqueue.h>".}
-proc mq_receive*(a1: Mqd, a2: cstring, a3: int, a4: var int): int {.
-  importc, header: "<mqueue.h>".}
-proc mq_send*(a1: Mqd, a2: cstring, a3: int, a4: int): cint {.
-  importc, header: "<mqueue.h>".}
-proc mq_setattr*(a1: Mqd, a2, a3: ptr MqAttr): cint {.
-  importc, header: "<mqueue.h>".}
-
-proc mq_timedreceive*(a1: Mqd, a2: cstring, a3: int, a4: int,
-                      a5: ptr Timespec): int {.importc, header: "<mqueue.h>".}
-proc mq_timedsend*(a1: Mqd, a2: cstring, a3: int, a4: int,
-                   a5: ptr Timespec): cint {.importc, header: "<mqueue.h>".}
-proc mq_unlink*(a1: cstring): cint {.importc, header: "<mqueue.h>".}
+when not (defined(nintendoswitch) or defined(macos) or defined(macosx)):
+  proc mq_notify*(mqdes: Mqd, event: ptr SigEvent): cint {.
+    importc, header: "<mqueue.h>".}
+
+  proc mq_open*(name: cstring, flags: cint): Mqd {.
+    varargs, importc, header: "<mqueue.h>".}
+
+  proc mq_close*(mqdes: Mqd): cint {.importc, header: "<mqueue.h>".}
+
+  proc mq_receive*(
+    mqdes: Mqd,
+    buffer: cstring,
+    length: csize_t,
+    priority: var cuint
+  ): int {.importc, header: "<mqueue.h>".}
+
+  proc mq_timedreceive*(
+    mqdes: Mqd,
+    buffer: cstring,
+    length: csize_t,
+    priority: cuint,
+    timeout: ptr Timespec
+  ): int {.importc, header: "<mqueue.h>".}
+
+  proc mq_send*(
+    mqdes: Mqd,
+    buffer: cstring,
+    length: csize_t,
+    priority: cuint
+  ): cint {.importc, header: "<mqueue.h>".}
+
+  proc mq_timedsend*(
+    mqdes: Mqd,
+    buffer: cstring,
+    length: csize_t,
+    priority: cuint,
+    timeout: ptr Timespec
+  ): cint {.importc, header: "<mqueue.h>".}
+
+  proc mq_getattr*(mqdes: Mqd, attribute: ptr MqAttr): cint {.
+    importc, header: "<mqueue.h>".}
+
+  proc mq_setattr*(mqdes: Mqd, newAttribute, oldAttribute: ptr MqAttr): cint {.
+    importc, header: "<mqueue.h>".}
+
+  proc mq_unlink*(mqdes: cstring): cint {.importc, header: "<mqueue.h>".}
 
 
 proc getpwnam*(a1: cstring): ptr Passwd {.importc, header: "<pwd.h>".}
@@ -259,47 +330,49 @@ proc setpwent*() {.importc, header: "<pwd.h>".}
 
 proc uname*(a1: var Utsname): cint {.importc, header: "<sys/utsname.h>".}
 
+proc strerror*(errnum: cint): cstring {.importc, header: "<string.h>".}
+
 proc pthread_atfork*(a1, a2, a3: proc () {.noconv.}): cint {.
   importc, header: "<pthread.h>".}
-proc pthread_attr_destroy*(a1: ptr PthreadAttr): cint {.
+proc pthread_attr_destroy*(a1: ptr Pthread_attr): cint {.
   importc, header: "<pthread.h>".}
-proc pthread_attr_getdetachstate*(a1: ptr PthreadAttr, a2: cint): cint {.
+proc pthread_attr_getdetachstate*(a1: ptr Pthread_attr, a2: cint): cint {.
   importc, header: "<pthread.h>".}
-proc pthread_attr_getguardsize*(a1: ptr PthreadAttr, a2: var cint): cint {.
+proc pthread_attr_getguardsize*(a1: ptr Pthread_attr, a2: var cint): cint {.
   importc, header: "<pthread.h>".}
-proc pthread_attr_getinheritsched*(a1: ptr PthreadAttr,
+proc pthread_attr_getinheritsched*(a1: ptr Pthread_attr,
           a2: var cint): cint {.importc, header: "<pthread.h>".}
-proc pthread_attr_getschedparam*(a1: ptr PthreadAttr,
+proc pthread_attr_getschedparam*(a1: ptr Pthread_attr,
           a2: ptr Sched_param): cint {.importc, header: "<pthread.h>".}
-proc pthread_attr_getschedpolicy*(a1: ptr PthreadAttr,
+proc pthread_attr_getschedpolicy*(a1: ptr Pthread_attr,
           a2: var cint): cint {.importc, header: "<pthread.h>".}
-proc pthread_attr_getscope*(a1: ptr PthreadAttr,
+proc pthread_attr_getscope*(a1: ptr Pthread_attr,
           a2: var cint): cint {.importc, header: "<pthread.h>".}
-proc pthread_attr_getstack*(a1: ptr PthreadAttr,
+proc pthread_attr_getstack*(a1: ptr Pthread_attr,
          a2: var pointer, a3: var int): cint {.importc, header: "<pthread.h>".}
-proc pthread_attr_getstackaddr*(a1: ptr PthreadAttr,
+proc pthread_attr_getstackaddr*(a1: ptr Pthread_attr,
           a2: var pointer): cint {.importc, header: "<pthread.h>".}
-proc pthread_attr_getstacksize*(a1: ptr PthreadAttr,
+proc pthread_attr_getstacksize*(a1: ptr Pthread_attr,
           a2: var int): cint {.importc, header: "<pthread.h>".}
-proc pthread_attr_init*(a1: ptr PthreadAttr): cint {.
+proc pthread_attr_init*(a1: ptr Pthread_attr): cint {.
   importc, header: "<pthread.h>".}
-proc pthread_attr_setdetachstate*(a1: ptr PthreadAttr, a2: cint): cint {.
+proc pthread_attr_setdetachstate*(a1: ptr Pthread_attr, a2: cint): cint {.
   importc, header: "<pthread.h>".}
-proc pthread_attr_setguardsize*(a1: ptr PthreadAttr, a2: int): cint {.
+proc pthread_attr_setguardsize*(a1: ptr Pthread_attr, a2: int): cint {.
   importc, header: "<pthread.h>".}
-proc pthread_attr_setinheritsched*(a1: ptr PthreadAttr, a2: cint): cint {.
+proc pthread_attr_setinheritsched*(a1: ptr Pthread_attr, a2: cint): cint {.
   importc, header: "<pthread.h>".}
-proc pthread_attr_setschedparam*(a1: ptr PthreadAttr,
+proc pthread_attr_setschedparam*(a1: ptr Pthread_attr,
           a2: ptr Sched_param): cint {.importc, header: "<pthread.h>".}
-proc pthread_attr_setschedpolicy*(a1: ptr PthreadAttr, a2: cint): cint {.
+proc pthread_attr_setschedpolicy*(a1: ptr Pthread_attr, a2: cint): cint {.
   importc, header: "<pthread.h>".}
-proc pthread_attr_setscope*(a1: ptr PthreadAttr, a2: cint): cint {.importc,
+proc pthread_attr_setscope*(a1: ptr Pthread_attr, a2: cint): cint {.importc,
   header: "<pthread.h>".}
-proc pthread_attr_setstack*(a1: ptr PthreadAttr, a2: pointer, a3: int): cint {.
+proc pthread_attr_setstack*(a1: ptr Pthread_attr, a2: pointer, a3: int): cint {.
   importc, header: "<pthread.h>".}
-proc pthread_attr_setstackaddr*(a1: ptr PthreadAttr, a2: pointer): cint {.
+proc pthread_attr_setstackaddr*(a1: ptr Pthread_attr, a2: pointer): cint {.
   importc, header: "<pthread.h>".}
-proc pthread_attr_setstacksize*(a1: ptr PthreadAttr, a2: int): cint {.
+proc pthread_attr_setstacksize*(a1: ptr Pthread_attr, a2: int): cint {.
   importc, header: "<pthread.h>".}
 proc pthread_barrier_destroy*(a1: ptr Pthread_barrier): cint {.
   importc, header: "<pthread.h>".}
@@ -342,7 +415,7 @@ proc pthread_condattr_init*(a1: ptr Pthread_condattr): cint {.importc, header: "
 proc pthread_condattr_setclock*(a1: ptr Pthread_condattr,a2: ClockId): cint {.importc, header: "<pthread.h>".}
 proc pthread_condattr_setpshared*(a1: ptr Pthread_condattr, a2: cint): cint {.importc, header: "<pthread.h>".}
 
-proc pthread_create*(a1: ptr Pthread, a2: ptr PthreadAttr,
+proc pthread_create*(a1: ptr Pthread, a2: ptr Pthread_attr,
           a3: proc (x: pointer): pointer {.noconv.}, a4: pointer): cint {.importc, header: "<pthread.h>".}
 proc pthread_detach*(a1: Pthread): cint {.importc, header: "<pthread.h>".}
 proc pthread_equal*(a1, a2: Pthread): cint {.importc, header: "<pthread.h>".}
@@ -430,7 +503,7 @@ proc pthread_spin_unlock*(a1: ptr Pthread_spinlock): cint {.
 proc pthread_testcancel*() {.importc, header: "<pthread.h>".}
 
 
-proc exitnow*(code: int): void {.importc: "_exit", header: "<unistd.h>".}
+proc exitnow*(code: int) {.importc: "_exit", header: "<unistd.h>".}
 proc access*(a1: cstring, a2: cint): cint {.importc, header: "<unistd.h>".}
 proc alarm*(a1: cint): cint {.importc, header: "<unistd.h>".}
 proc chdir*(a1: cstring): cint {.importc, header: "<unistd.h>".}
@@ -443,41 +516,55 @@ proc dup*(a1: cint): cint {.importc, header: "<unistd.h>".}
 proc dup2*(a1, a2: cint): cint {.importc, header: "<unistd.h>".}
 proc encrypt*(a1: array[0..63, char], a2: cint) {.importc, header: "<unistd.h>".}
 
-proc execl*(a1, a2: cstring): cint {.varargs, importc, header: "<unistd.h>".}
-proc execle*(a1, a2: cstring): cint {.varargs, importc, header: "<unistd.h>".}
-proc execlp*(a1, a2: cstring): cint {.varargs, importc, header: "<unistd.h>".}
-proc execv*(a1: cstring, a2: cstringArray): cint {.importc, header: "<unistd.h>".}
+proc execl*(a1, a2: cstring): cint {.varargs, importc, header: "<unistd.h>", sideEffect.}
+proc execle*(a1, a2: cstring): cint {.varargs, importc, header: "<unistd.h>", sideEffect.}
+proc execlp*(a1, a2: cstring): cint {.varargs, importc, header: "<unistd.h>", sideEffect.}
+proc execv*(a1: cstring, a2: cstringArray): cint {.importc, header: "<unistd.h>", sideEffect.}
 proc execve*(a1: cstring, a2, a3: cstringArray): cint {.
-  importc, header: "<unistd.h>".}
-proc execvp*(a1: cstring, a2: cstringArray): cint {.importc, header: "<unistd.h>".}
-proc execvpe*(a1: cstring, a2: cstringArray, a3: cstringArray): cint {.importc, header: "<unistd.h>".}
-proc fchown*(a1: cint, a2: Uid, a3: Gid): cint {.importc, header: "<unistd.h>".}
-proc fchdir*(a1: cint): cint {.importc, header: "<unistd.h>".}
+  importc, header: "<unistd.h>", sideEffect.}
+proc execvp*(a1: cstring, a2: cstringArray): cint {.importc, header: "<unistd.h>", sideEffect.}
+proc execvpe*(a1: cstring, a2: cstringArray, a3: cstringArray): cint {.importc, header: "<unistd.h>", sideEffect.}
+proc fchown*(a1: cint, a2: Uid, a3: Gid): cint {.importc, header: "<unistd.h>", sideEffect.}
+proc fchdir*(a1: cint): cint {.importc, header: "<unistd.h>", sideEffect.}
 proc fdatasync*(a1: cint): cint {.importc, header: "<unistd.h>".}
-proc fork*(): Pid {.importc, header: "<unistd.h>".}
+proc fork*(): Pid {.importc, header: "<unistd.h>", sideEffect.}
 proc fpathconf*(a1, a2: cint): int {.importc, header: "<unistd.h>".}
 proc fsync*(a1: cint): cint {.importc, header: "<unistd.h>".}
-proc ftruncate*(a1: cint, a2: Off): cint {.importc, header: "<unistd.h>".}
-proc getcwd*(a1: cstring, a2: int): cstring {.importc, header: "<unistd.h>".}
-proc getegid*(): Gid {.importc, header: "<unistd.h>".}
-proc geteuid*(): Uid {.importc, header: "<unistd.h>".}
-proc getgid*(): Gid {.importc, header: "<unistd.h>".}
+ ## synchronize a file's buffer cache to the storage device
+
+proc getcwd*(a1: cstring, a2: int): cstring {.importc, header: "<unistd.h>", sideEffect.}
+proc getuid*(): Uid {.importc, header: "<unistd.h>", sideEffect.}
+ ## returns the real user ID of the calling process
+
+proc geteuid*(): Uid {.importc, header: "<unistd.h>", sideEffect.}
+ ## returns the effective user ID of the calling process
+
+proc getgid*(): Gid {.importc, header: "<unistd.h>", sideEffect.}
+ ## returns the real group ID of the calling process
+
+proc getegid*(): Gid {.importc, header: "<unistd.h>", sideEffect.}
+ ## returns the effective group ID of the calling process
 
 proc getgroups*(a1: cint, a2: ptr array[0..255, Gid]): cint {.
   importc, header: "<unistd.h>".}
-proc gethostid*(): int {.importc, header: "<unistd.h>".}
-proc gethostname*(a1: cstring, a2: int): cint {.importc, header: "<unistd.h>".}
-proc getlogin*(): cstring {.importc, header: "<unistd.h>".}
-proc getlogin_r*(a1: cstring, a2: int): cint {.importc, header: "<unistd.h>".}
+proc gethostid*(): int {.importc, header: "<unistd.h>", sideEffect.}
+proc gethostname*(a1: cstring, a2: int): cint {.importc, header: "<unistd.h>", sideEffect.}
+proc getlogin*(): cstring {.importc, header: "<unistd.h>", sideEffect.}
+proc getlogin_r*(a1: cstring, a2: int): cint {.importc, header: "<unistd.h>", sideEffect.}
 
 proc getopt*(a1: cint, a2: cstringArray, a3: cstring): cint {.
   importc, header: "<unistd.h>".}
 proc getpgid*(a1: Pid): Pid {.importc, header: "<unistd.h>".}
 proc getpgrp*(): Pid {.importc, header: "<unistd.h>".}
-proc getpid*(): Pid {.importc, header: "<unistd.h>".}
-proc getppid*(): Pid {.importc, header: "<unistd.h>".}
-proc getsid*(a1: Pid): Pid {.importc, header: "<unistd.h>".}
-proc getuid*(): Uid {.importc, header: "<unistd.h>".}
+proc getpid*(): Pid {.importc, header: "<unistd.h>", sideEffect.}
+ ## returns  the process ID (PID) of the calling process
+
+proc getppid*(): Pid {.importc, header: "<unistd.h>", sideEffect.}
+ ## returns the process ID of the parent of the calling process
+
+proc getsid*(a1: Pid): Pid {.importc, header: "<unistd.h>", sideEffect.}
+ ## returns the session ID of the calling process
+
 proc getwd*(a1: cstring): cstring {.importc, header: "<unistd.h>".}
 proc isatty*(a1: cint): cint {.importc, header: "<unistd.h>".}
 proc lchown*(a1: cstring, a2: Uid, a3: Gid): cint {.importc, header: "<unistd.h>".}
@@ -497,7 +584,8 @@ proc pread*(a1: cint, a2: pointer, a3: int, a4: Off): int {.
 proc pwrite*(a1: cint, a2: pointer, a3: int, a4: Off): int {.
   importc, header: "<unistd.h>".}
 proc read*(a1: cint, a2: pointer, a3: int): int {.importc, header: "<unistd.h>".}
-proc readlink*(a1, a2: cstring, a3: int): int {.importc, header: "<unistd.h>".}
+when not defined(nintendoswitch):
+  proc readlink*(a1, a2: cstring, a3: int): int {.importc, header: "<unistd.h>".}
 proc ioctl*(f: FileHandle, device: uint): int {.importc: "ioctl",
       header: "<sys/ioctl.h>", varargs, tags: [WriteIOEffect].}
   ## A system call for device-specific input/output operations and other
@@ -516,7 +604,10 @@ proc setsid*(): Pid {.importc, header: "<unistd.h>".}
 proc setuid*(a1: Uid): cint {.importc, header: "<unistd.h>".}
 proc sleep*(a1: cint): cint {.importc, header: "<unistd.h>".}
 proc swab*(a1, a2: pointer, a3: int) {.importc, header: "<unistd.h>".}
-proc symlink*(a1, a2: cstring): cint {.importc, header: "<unistd.h>".}
+when not defined(nintendoswitch):
+  proc symlink*(a1, a2: cstring): cint {.importc, header: "<unistd.h>".}
+else:
+  proc symlink*(a1, a2: cstring): cint = -1
 proc sync*() {.importc, header: "<unistd.h>".}
 proc sysconf*(a1: cint): int {.importc, header: "<unistd.h>".}
 proc tcgetpgrp*(a1: cint): Pid {.importc, header: "<unistd.h>".}
@@ -553,11 +644,15 @@ proc statvfs*(a1: cstring, a2: var Statvfs): cint {.
 proc fstatvfs*(a1: cint, a2: var Statvfs): cint {.
   importc, header: "<sys/statvfs.h>".}
 
-proc chmod*(a1: cstring, a2: Mode): cint {.importc, header: "<sys/stat.h>".}
-proc fchmod*(a1: cint, a2: Mode): cint {.importc, header: "<sys/stat.h>".}
-proc fstat*(a1: cint, a2: var Stat): cint {.importc, header: "<sys/stat.h>".}
-proc lstat*(a1: cstring, a2: var Stat): cint {.importc, header: "<sys/stat.h>".}
-proc mkdir*(a1: cstring, a2: Mode): cint {.importc, header: "<sys/stat.h>".}
+proc chmod*(a1: cstring, a2: Mode): cint {.importc, header: "<sys/stat.h>", sideEffect.}
+when defined(osx) or defined(freebsd):
+  proc lchmod*(a1: cstring, a2: Mode): cint {.importc, header: "<sys/stat.h>", sideEffect.}
+proc fchmod*(a1: cint, a2: Mode): cint {.importc, header: "<sys/stat.h>", sideEffect.}
+proc fstat*(a1: cint, a2: var Stat): cint {.importc, header: "<sys/stat.h>", sideEffect.}
+proc lstat*(a1: cstring, a2: var Stat): cint {.importc, header: "<sys/stat.h>", sideEffect.}
+proc mkdir*(a1: cstring, a2: Mode): cint {.importc, header: "<sys/stat.h>", sideEffect.}
+  ## Use `os.createDir() <os.html#createDir,string>`_ and similar.
+
 proc mkfifo*(a1: cstring, a2: Mode): cint {.importc, header: "<sys/stat.h>".}
 proc mknod*(a1: cstring, a2: Mode, a3: Dev): cint {.
   importc, header: "<sys/stat.h>".}
@@ -596,6 +691,7 @@ proc mmap*(a1: pointer, a2: int, a3, a4, a5: cint, a6: Off): pointer {.
 proc mprotect*(a1: pointer, a2: int, a3: cint): cint {.
   importc, header: "<sys/mman.h>".}
 proc msync*(a1: pointer, a2: int, a3: cint): cint {.importc, header: "<sys/mman.h>".}
+
 proc munlock*(a1: pointer, a2: int): cint {.importc, header: "<sys/mman.h>".}
 proc munlockall*(): cint {.importc, header: "<sys/mman.h>".}
 proc munmap*(a1: pointer, a2: int): cint {.importc, header: "<sys/mman.h>".}
@@ -603,7 +699,8 @@ proc posix_madvise*(a1: pointer, a2: int, a3: cint): cint {.
   importc, header: "<sys/mman.h>".}
 proc posix_mem_offset*(a1: pointer, a2: int, a3: var Off,
            a4: var int, a5: var cint): cint {.importc, header: "<sys/mman.h>".}
-when not (defined(linux) and defined(amd64)):
+when not (defined(linux) and defined(amd64)) and not defined(nintendoswitch) and
+     not defined(haiku):
   proc posix_typed_mem_get_info*(a1: cint,
     a2: var Posix_typed_mem_info): cint {.importc, header: "<sys/mman.h>".}
 proc posix_typed_mem_open*(a1: cstring, a2, a3: cint): cint {.
@@ -615,17 +712,17 @@ proc shm_unlink*(a1: cstring): cint {.importc, header: "<sys/mman.h>".}
 proc asctime*(a1: var Tm): cstring{.importc, header: "<time.h>".}
 
 proc asctime_r*(a1: var Tm, a2: cstring): cstring {.importc, header: "<time.h>".}
-proc clock*(): Clock {.importc, header: "<time.h>".}
+proc clock*(): Clock {.importc, header: "<time.h>", sideEffect.}
 proc clock_getcpuclockid*(a1: Pid, a2: var ClockId): cint {.
-  importc, header: "<time.h>".}
+  importc, header: "<time.h>", sideEffect.}
 proc clock_getres*(a1: ClockId, a2: var Timespec): cint {.
-  importc, header: "<time.h>".}
+  importc, header: "<time.h>", sideEffect.}
 proc clock_gettime*(a1: ClockId, a2: var Timespec): cint {.
-  importc, header: "<time.h>".}
+  importc, header: "<time.h>", sideEffect.}
 proc clock_nanosleep*(a1: ClockId, a2: cint, a3: var Timespec,
-               a4: var Timespec): cint {.importc, header: "<time.h>".}
+               a4: var Timespec): cint {.importc, header: "<time.h>", sideEffect.}
 proc clock_settime*(a1: ClockId, a2: var Timespec): cint {.
-  importc, header: "<time.h>".}
+  importc, header: "<time.h>", sideEffect.}
 
 proc `==`*(a, b: Time): bool {.borrow.}
 proc `-`*(a, b: Time): Time {.borrow.}
@@ -639,11 +736,11 @@ proc localtime*(a1: var Time): ptr Tm {.importc, header: "<time.h>".}
 proc localtime_r*(a1: var Time, a2: var Tm): ptr Tm {.importc, header: "<time.h>".}
 proc mktime*(a1: var Tm): Time  {.importc, header: "<time.h>".}
 proc timegm*(a1: var Tm): Time  {.importc, header: "<time.h>".}
-proc nanosleep*(a1, a2: var Timespec): cint {.importc, header: "<time.h>".}
+proc nanosleep*(a1, a2: var Timespec): cint {.importc, header: "<time.h>", sideEffect.}
 proc strftime*(a1: cstring, a2: int, a3: cstring,
            a4: var Tm): int {.importc, header: "<time.h>".}
 proc strptime*(a1, a2: cstring, a3: var Tm): cstring {.importc, header: "<time.h>".}
-proc time*(a1: var Time): Time {.importc, header: "<time.h>".}
+proc time*(a1: var Time): Time {.importc, header: "<time.h>", sideEffect.}
 proc timer_create*(a1: ClockId, a2: var SigEvent,
                a3: var Timer): cint {.importc, header: "<time.h>".}
 proc timer_delete*(a1: Timer): cint {.importc, header: "<time.h>".}
@@ -655,16 +752,36 @@ proc timer_settime*(a1: Timer, a2: cint, a3: var Itimerspec,
 proc tzset*() {.importc, header: "<time.h>".}
 
 
-proc wait*(a1: ptr cint): Pid {.importc, discardable, header: "<sys/wait.h>".}
+proc wait*(a1: ptr cint): Pid {.importc, discardable, header: "<sys/wait.h>", sideEffect.}
 proc waitid*(a1: cint, a2: Id, a3: var SigInfo, a4: cint): cint {.
-  importc, header: "<sys/wait.h>".}
+  importc, header: "<sys/wait.h>", sideEffect.}
 proc waitpid*(a1: Pid, a2: var cint, a3: cint): Pid {.
-  importc, header: "<sys/wait.h>".}
+  importc, header: "<sys/wait.h>", sideEffect.}
+
+type Rusage* {.importc: "struct rusage", header: "<sys/resource.h>",
+               bycopy.} = object
+  ru_utime*, ru_stime*: Timeval                       # User and system time
+  ru_maxrss*, ru_ixrss*, ru_idrss*, ru_isrss*,        # memory sizes
+    ru_minflt*, ru_majflt*, ru_nswap*,                # paging activity
+    ru_inblock*, ru_oublock*, ru_msgsnd*, ru_msgrcv*, # IO activity
+    ru_nsignals*, ru_nvcsw*, ru_nivcsw*: clong        # switching activity
+
+proc wait4*(pid: Pid, status: ptr cint, options: cint, rusage: ptr Rusage): Pid
+  {.importc, header: "<sys/wait.h>", sideEffect.}
+
+const
+  RUSAGE_SELF* = cint(0)
+  RUSAGE_CHILDREN* = cint(-1)
+  RUSAGE_THREAD* = cint(1)    # This one is less std; Linux, BSD agree though.
+
+# This can only fail if `who` is invalid or `rusage` ptr is invalid.
+proc getrusage*(who: cint, rusage: ptr Rusage): cint
+  {.importc, header: "<sys/resource.h>", discardable.}
 
 proc bsd_signal*(a1: cint, a2: proc (x: pointer) {.noconv.}) {.
   importc, header: "<signal.h>".}
-proc kill*(a1: Pid, a2: cint): cint {.importc, header: "<signal.h>".}
-proc killpg*(a1: Pid, a2: cint): cint {.importc, header: "<signal.h>".}
+proc kill*(a1: Pid, a2: cint): cint {.importc, header: "<signal.h>", sideEffect.}
+proc killpg*(a1: Pid, a2: cint): cint {.importc, header: "<signal.h>", sideEffect.}
 proc pthread_kill*(a1: Pthread, a2: cint): cint {.importc, header: "<signal.h>".}
 proc pthread_sigmask*(a1: cint, a2, a3: var Sigset): cint {.
   importc, header: "<signal.h>".}
@@ -708,17 +825,24 @@ else:
   proc sigtimedwait*(a1: var Sigset, a2: var SigInfo,
                      a3: var Timespec): cint {.importc, header: "<signal.h>".}
 
+when defined(sunos) or defined(solaris):
+  # The following compile time flag is needed on Illumos/Solaris to use the POSIX
+  # `sigwait` implementation. See the documentation here:
+  # https://docs.oracle.com/cd/E19455-01/806-5257/6je9h033k/index.html
+  # https://www.illumos.org/man/2/sigwait
+  {.passc: "-D_POSIX_PTHREAD_SEMANTICS".}
+
 proc sigwait*(a1: var Sigset, a2: var cint): cint {.
   importc, header: "<signal.h>".}
 proc sigwaitinfo*(a1: var Sigset, a2: var SigInfo): cint {.
   importc, header: "<signal.h>".}
 
-
-proc catclose*(a1: Nl_catd): cint {.importc, header: "<nl_types.h>".}
-proc catgets*(a1: Nl_catd, a2, a3: cint, a4: cstring): cstring {.
-  importc, header: "<nl_types.h>".}
-proc catopen*(a1: cstring, a2: cint): Nl_catd {.
-  importc, header: "<nl_types.h>".}
+when not defined(nintendoswitch):
+  proc catclose*(a1: Nl_catd): cint {.importc, header: "<nl_types.h>".}
+  proc catgets*(a1: Nl_catd, a2, a3: cint, a4: cstring): cstring {.
+    importc, header: "<nl_types.h>".}
+  proc catopen*(a1: cstring, a2: cint): Nl_catd {.
+    importc, header: "<nl_types.h>".}
 
 proc sched_get_priority_max*(a1: cint): cint {.importc, header: "<sched.h>".}
 proc sched_get_priority_min*(a1: cint): cint {.importc, header: "<sched.h>".}
@@ -733,7 +857,6 @@ proc sched_setscheduler*(a1: Pid, a2: cint, a3: var Sched_param): cint {.
   importc, header: "<sched.h>".}
 proc sched_yield*(): cint {.importc, header: "<sched.h>".}
 
-proc strerror*(errnum: cint): cstring {.importc, header: "<string.h>".}
 proc hstrerror*(herrnum: cint): cstring {.importc:"(char *)$1", header: "<netdb.h>".}
 
 proc FD_CLR*(a1: cint, a2: var TFdSet) {.importc, header: "<sys/select.h>".}
@@ -800,11 +923,12 @@ when hasSpawnH:
             a4: var Tposix_spawnattr,
             a5, a6: cstringArray): cint {.importc, header: "<spawn.h>".}
 
-proc getcontext*(a1: var Ucontext): cint {.importc, header: "<ucontext.h>".}
-proc makecontext*(a1: var Ucontext, a4: proc (){.noconv.}, a3: cint) {.
-  varargs, importc, header: "<ucontext.h>".}
-proc setcontext*(a1: var Ucontext): cint {.importc, header: "<ucontext.h>".}
-proc swapcontext*(a1, a2: var Ucontext): cint {.importc, header: "<ucontext.h>".}
+when not defined(nintendoswitch):
+  proc getcontext*(a1: var Ucontext): cint {.importc, header: "<ucontext.h>".}
+  proc makecontext*(a1: var Ucontext, a4: proc (){.noconv.}, a3: cint) {.
+    varargs, importc, header: "<ucontext.h>".}
+  proc setcontext*(a1: var Ucontext): cint {.importc, header: "<ucontext.h>".}
+  proc swapcontext*(a1, a2: var Ucontext): cint {.importc, header: "<ucontext.h>".}
 
 proc readv*(a1: cint, a2: ptr IOVec, a3: cint): int {.
   importc, header: "<sys/uio.h>".}
@@ -820,45 +944,55 @@ proc CMSG_NXTHDR*(mhdr: ptr Tmsghdr, cmsg: ptr Tcmsghdr): ptr Tcmsghdr {.
 proc CMSG_FIRSTHDR*(mhdr: ptr Tmsghdr): ptr Tcmsghdr {.
   importc, header: "<sys/socket.h>".}
 
+proc CMSG_SPACE*(len: csize_t): csize_t {.
+  importc, header: "<sys/socket.h>".}
+
+proc CMSG_LEN*(len: csize_t): csize_t {.
+  importc, header: "<sys/socket.h>".}
+
 const
   INVALID_SOCKET* = SocketHandle(-1)
 
 proc `==`*(x, y: SocketHandle): bool {.borrow.}
 
-proc accept*(a1: SocketHandle, a2: ptr SockAddr, a3: ptr Socklen): SocketHandle {.
-  importc, header: "<sys/socket.h>".}
+proc accept*(a1: SocketHandle, a2: ptr SockAddr, a3: ptr SockLen): SocketHandle {.
+  importc, header: "<sys/socket.h>", sideEffect.}
+
+when defined(linux) or defined(bsd) or defined(nuttx):
+  proc accept4*(a1: SocketHandle, a2: ptr SockAddr, a3: ptr SockLen,
+                flags: cint): SocketHandle {.importc, header: "<sys/socket.h>".}
 
-proc bindSocket*(a1: SocketHandle, a2: ptr SockAddr, a3: Socklen): cint {.
+proc bindSocket*(a1: SocketHandle, a2: ptr SockAddr, a3: SockLen): cint {.
   importc: "bind", header: "<sys/socket.h>".}
-  ## is Posix's ``bind``, because ``bind`` is a reserved word
+  ## is Posix's `bind`, because `bind` is a reserved word
 
-proc connect*(a1: SocketHandle, a2: ptr SockAddr, a3: Socklen): cint {.
+proc connect*(a1: SocketHandle, a2: ptr SockAddr, a3: SockLen): cint {.
   importc, header: "<sys/socket.h>".}
-proc getpeername*(a1: SocketHandle, a2: ptr SockAddr, a3: ptr Socklen): cint {.
+proc getpeername*(a1: SocketHandle, a2: ptr SockAddr, a3: ptr SockLen): cint {.
   importc, header: "<sys/socket.h>".}
-proc getsockname*(a1: SocketHandle, a2: ptr SockAddr, a3: ptr Socklen): cint {.
+proc getsockname*(a1: SocketHandle, a2: ptr SockAddr, a3: ptr SockLen): cint {.
   importc, header: "<sys/socket.h>".}
 
-proc getsockopt*(a1: SocketHandle, a2, a3: cint, a4: pointer, a5: ptr Socklen): cint {.
+proc getsockopt*(a1: SocketHandle, a2, a3: cint, a4: pointer, a5: ptr SockLen): cint {.
   importc, header: "<sys/socket.h>".}
 
 proc listen*(a1: SocketHandle, a2: cint): cint {.
-  importc, header: "<sys/socket.h>".}
+  importc, header: "<sys/socket.h>", sideEffect.}
 proc recv*(a1: SocketHandle, a2: pointer, a3: int, a4: cint): int {.
-  importc, header: "<sys/socket.h>".}
+  importc, header: "<sys/socket.h>", sideEffect.}
 proc recvfrom*(a1: SocketHandle, a2: pointer, a3: int, a4: cint,
-        a5: ptr SockAddr, a6: ptr Socklen): int {.
-  importc, header: "<sys/socket.h>".}
+        a5: ptr SockAddr, a6: ptr SockLen): int {.
+  importc, header: "<sys/socket.h>", sideEffect.}
 proc recvmsg*(a1: SocketHandle, a2: ptr Tmsghdr, a3: cint): int {.
-  importc, header: "<sys/socket.h>".}
+  importc, header: "<sys/socket.h>", sideEffect.}
 proc send*(a1: SocketHandle, a2: pointer, a3: int, a4: cint): int {.
-  importc, header: "<sys/socket.h>".}
+  importc, header: "<sys/socket.h>", sideEffect.}
 proc sendmsg*(a1: SocketHandle, a2: ptr Tmsghdr, a3: cint): int {.
-  importc, header: "<sys/socket.h>".}
+  importc, header: "<sys/socket.h>", sideEffect.}
 proc sendto*(a1: SocketHandle, a2: pointer, a3: int, a4: cint, a5: ptr SockAddr,
-             a6: Socklen): int {.
-  importc, header: "<sys/socket.h>".}
-proc setsockopt*(a1: SocketHandle, a2, a3: cint, a4: pointer, a5: Socklen): cint {.
+             a6: SockLen): int {.
+  importc, header: "<sys/socket.h>", sideEffect.}
+proc setsockopt*(a1: SocketHandle, a2, a3: cint, a4: pointer, a5: SockLen): cint {.
   importc, header: "<sys/socket.h>".}
 proc shutdown*(a1: SocketHandle, a2: cint): cint {.
   importc, header: "<sys/socket.h>".}
@@ -890,9 +1024,15 @@ proc IN6_IS_ADDR_LINKLOCAL* (a1: ptr In6Addr): cint {.
 proc IN6_IS_ADDR_SITELOCAL* (a1: ptr In6Addr): cint {.
   importc, header: "<netinet/in.h>".}
   ## Unicast site-local address.
-proc IN6_IS_ADDR_V4MAPPED* (a1: ptr In6Addr): cint {.
-  importc, header: "<netinet/in.h>".}
-  ## IPv4 mapped address.
+when defined(lwip):
+  proc IN6_IS_ADDR_V4MAPPED*(ipv6_address: ptr In6Addr): cint =
+    var bits32: ptr array[4, uint32] = cast[ptr array[4, uint32]](ipv6_address)
+    return (bits32[1] == 0'u32 and bits32[2] == htonl(0x0000FFFF)).cint
+else:
+  proc IN6_IS_ADDR_V4MAPPED* (a1: ptr In6Addr): cint {.
+    importc, header: "<netinet/in.h>".}
+    ## IPv4 mapped address.
+
 proc IN6_IS_ADDR_V4COMPAT* (a1: ptr In6Addr): cint {.
   importc, header: "<netinet/in.h>".}
   ## IPv4-compatible address.
@@ -916,7 +1056,7 @@ proc endhostent*() {.importc, header: "<netdb.h>".}
 proc endnetent*() {.importc, header: "<netdb.h>".}
 proc endprotoent*() {.importc, header: "<netdb.h>".}
 proc endservent*() {.importc, header: "<netdb.h>".}
-proc freeaddrinfo*(a1: ptr AddrInfo) {.importc, header: "<netdb.h>".}
+proc freeAddrInfo*(a1: ptr AddrInfo) {.importc: "freeaddrinfo", header: "<netdb.h>".}
 
 proc gai_strerror*(a1: cint): cstring {.importc:"(char *)$1", header: "<netdb.h>".}
 
@@ -924,7 +1064,7 @@ proc getaddrinfo*(a1, a2: cstring, a3: ptr AddrInfo,
                   a4: var ptr AddrInfo): cint {.importc, header: "<netdb.h>".}
 
 when not defined(android4):
-  proc gethostbyaddr*(a1: pointer, a2: Socklen, a3: cint): ptr Hostent {.
+  proc gethostbyaddr*(a1: pointer, a2: SockLen, a3: cint): ptr Hostent {.
                       importc, header: "<netdb.h>".}
 else:
   proc gethostbyaddr*(a1: cstring, a2: cint, a3: cint): ptr Hostent {.
@@ -932,9 +1072,9 @@ else:
 proc gethostbyname*(a1: cstring): ptr Hostent {.importc, header: "<netdb.h>".}
 proc gethostent*(): ptr Hostent {.importc, header: "<netdb.h>".}
 
-proc getnameinfo*(a1: ptr SockAddr, a2: Socklen,
-                  a3: cstring, a4: Socklen, a5: cstring,
-                  a6: Socklen, a7: cint): cint {.importc, header: "<netdb.h>".}
+proc getnameinfo*(a1: ptr SockAddr, a2: SockLen,
+                  a3: cstring, a4: SockLen, a5: cstring,
+                  a6: SockLen, a7: cint): cint {.importc, header: "<netdb.h>".}
 
 proc getnetbyaddr*(a1: int32, a2: cint): ptr Tnetent {.importc, header: "<netdb.h>".}
 proc getnetbyname*(a1: cstring): ptr Tnetent {.importc, header: "<netdb.h>".}
@@ -954,20 +1094,37 @@ proc setnetent*(a1: cint) {.importc, header: "<netdb.h>".}
 proc setprotoent*(a1: cint) {.importc, header: "<netdb.h>".}
 proc setservent*(a1: cint) {.importc, header: "<netdb.h>".}
 
-proc poll*(a1: ptr TPollfd, a2: Tnfds, a3: int): cint {.
-  importc, header: "<poll.h>".}
+when not defined(lwip):
+  proc poll*(a1: ptr TPollfd, a2: Tnfds, a3: int): cint {.
+    importc, header: "<poll.h>", sideEffect.}
 
 proc realpath*(name, resolved: cstring): cstring {.
   importc: "realpath", header: "<stdlib.h>".}
 
-proc mkstemp*(tmpl: cstring): cint {.importc, header: "<stdlib.h>".}
-  ## Create a temporary file.
+proc mkstemp*(tmpl: cstring): cint {.importc, header: "<stdlib.h>", sideEffect.}
+  ## Creates a unique temporary file.
+  ##
+  ## .. warning:: The `tmpl` argument is written to by `mkstemp` and thus
+  ##   can't be a string literal. If in doubt make a copy of the cstring before
+  ##   passing it in.
+
+proc mkstemps*(tmpl: cstring, suffixlen: int): cint {.importc, header: "<stdlib.h>", sideEffect.}
+  ## Creates a unique temporary file.
   ##
-  ## **Warning**: The `tmpl` argument is written to by `mkstemp` and thus
-  ## can't be a string literal. If in doubt copy the string before passing it.
+  ## .. warning:: The `tmpl` argument is written to by `mkstemps` and thus
+  ##   can't be a string literal. If in doubt make a copy of the cstring before
+  ##   passing it in.
+
+proc mkdtemp*(tmpl: cstring): pointer {.importc, header: "<stdlib.h>", sideEffect.}
+
+when defined(linux) or defined(bsd) or defined(osx):
+  proc mkostemp*(tmpl: cstring, oflags: cint): cint {.importc, header: "<stdlib.h>", sideEffect.}
+  proc mkostemps*(tmpl: cstring, suffixlen: cint, oflags: cint): cint {.importc, header: "<stdlib.h>", sideEffect.}
+
+  proc posix_memalign*(memptr: pointer, alignment: csize_t, size: csize_t): cint {.importc, header: "<stdlib.h>".}
 
 proc utimes*(path: cstring, times: ptr array[2, Timeval]): int {.
-  importc: "utimes", header: "<sys/time.h>".}
+  importc: "utimes", header: "<sys/time.h>", sideEffect.}
   ## Sets file access and modification times.
   ##
   ## Pass the filename and an array of times to set the access and modification
@@ -981,14 +1138,21 @@ proc utimes*(path: cstring, times: ptr array[2, Timeval]): int {.
 proc handle_signal(sig: cint, handler: proc (a: cint) {.noconv.}) {.importc: "signal", header: "<signal.h>".}
 
 template onSignal*(signals: varargs[cint], body: untyped) =
-  ## Setup code to be executed when Unix signals are received. Example:
-  ## from posix import SIGINT, SIGTERM
-  ## onSignal(SIGINT, SIGTERM):
-  ##   echo "bye"
+  ## Setup code to be executed when Unix signals are received. The
+  ## currently handled signal is injected as `sig` into the calling
+  ## scope.
+  ##
+  ## Example:
+  ##   ```Nim
+  ##   from std/posix import SIGINT, SIGTERM, onSignal
+  ##   onSignal(SIGINT, SIGTERM):
+  ##     echo "bye from signal ", sig
+  ##   ```
 
   for s in signals:
     handle_signal(s,
-      proc (sig: cint) {.noconv.} =
+      proc (signal: cint) {.noconv.} =
+        let sig {.inject.} = signal
         body
     )
 
@@ -1000,10 +1164,13 @@ type
   ## The getrlimit() and setrlimit() system calls get and set resource limits respectively.
   ## Each resource has an associated soft and hard limit, as defined by the RLimit structure
 
-proc setrlimit*(resource: cint, rlp: var RLimit): cint
-      {.importc: "setrlimit",header: "<sys/resource.h>".}
+proc setrlimit*(resource: cint, rlp: var RLimit): cint {.
+  importc: "setrlimit", header: "<sys/resource.h>".}
   ## The setrlimit() system calls sets resource limits.
 
-proc getrlimit*(resource: cint, rlp: var RLimit): cint
-      {.importc: "getrlimit",header: "<sys/resource.h>".}
+proc getrlimit*(resource: cint, rlp: var RLimit): cint {.
+  importc: "getrlimit", header: "<sys/resource.h>".}
   ## The getrlimit() system call gets resource limits.
+
+when defined(nimHasStyleChecks):
+  {.pop.} # {.push styleChecks: off.}
diff --git a/lib/posix/posix_freertos_consts.nim b/lib/posix/posix_freertos_consts.nim
new file mode 100644
index 000000000..0f0fc0aae
--- /dev/null
+++ b/lib/posix/posix_freertos_consts.nim
@@ -0,0 +1,506 @@
+# Generated by detect.nim
+
+
+
+# <errno.h>
+var E2BIG* {.importc: "E2BIG", header: "<errno.h>".}: cint
+var EACCES* {.importc: "EACCES", header: "<errno.h>".}: cint
+var EADDRINUSE* {.importc: "EADDRINUSE", header: "<errno.h>".}: cint
+var EADDRNOTAVAIL* {.importc: "EADDRNOTAVAIL", header: "<errno.h>".}: cint
+var EAFNOSUPPORT* {.importc: "EAFNOSUPPORT", header: "<errno.h>".}: cint
+var EAGAIN* {.importc: "EAGAIN", header: "<errno.h>".}: cint
+var EALREADY* {.importc: "EALREADY", header: "<errno.h>".}: cint
+var EBADF* {.importc: "EBADF", header: "<errno.h>".}: cint
+var EBADMSG* {.importc: "EBADMSG", header: "<errno.h>".}: cint
+var EBUSY* {.importc: "EBUSY", header: "<errno.h>".}: cint
+var ECANCELED* {.importc: "ECANCELED", header: "<errno.h>".}: cint
+var ECHILD* {.importc: "ECHILD", header: "<errno.h>".}: cint
+var ECONNABORTED* {.importc: "ECONNABORTED", header: "<errno.h>".}: cint
+var ECONNREFUSED* {.importc: "ECONNREFUSED", header: "<errno.h>".}: cint
+var ECONNRESET* {.importc: "ECONNRESET", header: "<errno.h>".}: cint
+var EDEADLK* {.importc: "EDEADLK", header: "<errno.h>".}: cint
+var EDESTADDRREQ* {.importc: "EDESTADDRREQ", header: "<errno.h>".}: cint
+var EDOM* {.importc: "EDOM", header: "<errno.h>".}: cint
+var EDQUOT* {.importc: "EDQUOT", header: "<errno.h>".}: cint
+var EEXIST* {.importc: "EEXIST", header: "<errno.h>".}: cint
+var EFAULT* {.importc: "EFAULT", header: "<errno.h>".}: cint
+var EFBIG* {.importc: "EFBIG", header: "<errno.h>".}: cint
+var EHOSTUNREACH* {.importc: "EHOSTUNREACH", header: "<errno.h>".}: cint
+var EIDRM* {.importc: "EIDRM", header: "<errno.h>".}: cint
+var EILSEQ* {.importc: "EILSEQ", header: "<errno.h>".}: cint
+var EINPROGRESS* {.importc: "EINPROGRESS", header: "<errno.h>".}: cint
+var EINTR* {.importc: "EINTR", header: "<errno.h>".}: cint
+var EINVAL* {.importc: "EINVAL", header: "<errno.h>".}: cint
+var EIO* {.importc: "EIO", header: "<errno.h>".}: cint
+var EISCONN* {.importc: "EISCONN", header: "<errno.h>".}: cint
+var EISDIR* {.importc: "EISDIR", header: "<errno.h>".}: cint
+var ELOOP* {.importc: "ELOOP", header: "<errno.h>".}: cint
+var EMFILE* {.importc: "EMFILE", header: "<errno.h>".}: cint
+var EMLINK* {.importc: "EMLINK", header: "<errno.h>".}: cint
+var EMSGSIZE* {.importc: "EMSGSIZE", header: "<errno.h>".}: cint
+var EMULTIHOP* {.importc: "EMULTIHOP", header: "<errno.h>".}: cint
+var ENAMETOOLONG* {.importc: "ENAMETOOLONG", header: "<errno.h>".}: cint
+var ENETDOWN* {.importc: "ENETDOWN", header: "<errno.h>".}: cint
+var ENETRESET* {.importc: "ENETRESET", header: "<errno.h>".}: cint
+var ENETUNREACH* {.importc: "ENETUNREACH", header: "<errno.h>".}: cint
+var ENFILE* {.importc: "ENFILE", header: "<errno.h>".}: cint
+var ENOBUFS* {.importc: "ENOBUFS", header: "<errno.h>".}: cint
+var ENODATA* {.importc: "ENODATA", header: "<errno.h>".}: cint
+var ENODEV* {.importc: "ENODEV", header: "<errno.h>".}: cint
+var ENOENT* {.importc: "ENOENT", header: "<errno.h>".}: cint
+var ENOEXEC* {.importc: "ENOEXEC", header: "<errno.h>".}: cint
+var ENOLCK* {.importc: "ENOLCK", header: "<errno.h>".}: cint
+var ENOLINK* {.importc: "ENOLINK", header: "<errno.h>".}: cint
+var ENOMEM* {.importc: "ENOMEM", header: "<errno.h>".}: cint
+var ENOMSG* {.importc: "ENOMSG", header: "<errno.h>".}: cint
+var ENOPROTOOPT* {.importc: "ENOPROTOOPT", header: "<errno.h>".}: cint
+var ENOSPC* {.importc: "ENOSPC", header: "<errno.h>".}: cint
+var ENOSR* {.importc: "ENOSR", header: "<errno.h>".}: cint
+var ENOSTR* {.importc: "ENOSTR", header: "<errno.h>".}: cint
+var ENOSYS* {.importc: "ENOSYS", header: "<errno.h>".}: cint
+var ENOTCONN* {.importc: "ENOTCONN", header: "<errno.h>".}: cint
+var ENOTDIR* {.importc: "ENOTDIR", header: "<errno.h>".}: cint
+var ENOTEMPTY* {.importc: "ENOTEMPTY", header: "<errno.h>".}: cint
+var ENOTSOCK* {.importc: "ENOTSOCK", header: "<errno.h>".}: cint
+var ENOTSUP* {.importc: "ENOTSUP", header: "<errno.h>".}: cint
+var ENOTTY* {.importc: "ENOTTY", header: "<errno.h>".}: cint
+var ENXIO* {.importc: "ENXIO", header: "<errno.h>".}: cint
+var EOPNOTSUPP* {.importc: "EOPNOTSUPP", header: "<errno.h>".}: cint
+var EOVERFLOW* {.importc: "EOVERFLOW", header: "<errno.h>".}: cint
+var EPERM* {.importc: "EPERM", header: "<errno.h>".}: cint
+var EPIPE* {.importc: "EPIPE", header: "<errno.h>".}: cint
+var EPROTO* {.importc: "EPROTO", header: "<errno.h>".}: cint
+var EPROTONOSUPPORT* {.importc: "EPROTONOSUPPORT", header: "<errno.h>".}: cint
+var EPROTOTYPE* {.importc: "EPROTOTYPE", header: "<errno.h>".}: cint
+var ERANGE* {.importc: "ERANGE", header: "<errno.h>".}: cint
+var EROFS* {.importc: "EROFS", header: "<errno.h>".}: cint
+var ESPIPE* {.importc: "ESPIPE", header: "<errno.h>".}: cint
+var ESRCH* {.importc: "ESRCH", header: "<errno.h>".}: cint
+var ESTALE* {.importc: "ESTALE", header: "<errno.h>".}: cint
+var ETIME* {.importc: "ETIME", header: "<errno.h>".}: cint
+var ETIMEDOUT* {.importc: "ETIMEDOUT", header: "<errno.h>".}: cint
+var ETXTBSY* {.importc: "ETXTBSY", header: "<errno.h>".}: cint
+var EWOULDBLOCK* {.importc: "EWOULDBLOCK", header: "<errno.h>".}: cint
+var EXDEV* {.importc: "EXDEV", header: "<errno.h>".}: cint
+
+# <sys/fcntl.h>
+var F_GETFD* {.importc: "F_GETFD", header: "<sys/fcntl.h>".}: cint
+# var F_SETFD* {.importc: "F_SETFD", header: "<sys/fcntl.h>".}: cint
+var F_GETFL* {.importc: "F_GETFL", header: "<sys/fcntl.h>".}: cint
+var F_SETFL* {.importc: "F_SETFL", header: "<sys/fcntl.h>".}: cint
+# var F_GETLK* {.importc: "F_GETLK", header: "<sys/fcntl.h>".}: cint
+# var F_SETLK* {.importc: "F_SETLK", header: "<sys/fcntl.h>".}: cint
+# var F_SETLKW* {.importc: "F_SETLKW", header: "<sys/fcntl.h>".}: cint
+# var F_GETOWN* {.importc: "F_GETOWN", header: "<sys/fcntl.h>".}: cint
+# var F_SETOWN* {.importc: "F_SETOWN", header: "<sys/fcntl.h>".}: cint
+# var FD_CLOEXEC* {.importc: "FD_CLOEXEC", header: "<sys/fcntl.h>".}: cint
+# var F_RDLCK* {.importc: "F_RDLCK", header: "<sys/fcntl.h>".}: cint
+# var F_UNLCK* {.importc: "F_UNLCK", header: "<sys/fcntl.h>".}: cint
+# var F_WRLCK* {.importc: "F_WRLCK", header: "<sys/fcntl.h>".}: cint
+var O_CREAT* {.importc: "O_CREAT", header: "<sys/fcntl.h>".}: cint
+var O_EXCL* {.importc: "O_EXCL", header: "<sys/fcntl.h>".}: cint
+# var O_NOCTTY* {.importc: "O_NOCTTY", header: "<sys/fcntl.h>".}: cint
+var O_TRUNC* {.importc: "O_TRUNC", header: "<sys/fcntl.h>".}: cint
+var O_APPEND* {.importc: "O_APPEND", header: "<sys/fcntl.h>".}: cint
+# var O_DSYNC* {.importc: "O_DSYNC", header: "<sys/fcntl.h>".}: cint
+var O_NONBLOCK* {.importc: "O_NONBLOCK", header: "<sys/fcntl.h>".}: cint
+# var O_RSYNC* {.importc: "O_RSYNC", header: "<sys/fcntl.h>".}: cint
+# var O_SYNC* {.importc: "O_SYNC", header: "<sys/fcntl.h>".}: cint
+# var O_ACCMODE* {.importc: "O_ACCMODE", header: "<sys/fcntl.h>".}: cint
+var O_RDONLY* {.importc: "O_RDONLY", header: "<sys/fcntl.h>".}: cint
+var O_RDWR* {.importc: "O_RDWR", header: "<sys/fcntl.h>".}: cint
+var O_WRONLY* {.importc: "O_WRONLY", header: "<sys/fcntl.h>".}: cint
+# var O_CLOEXEC* {.importc: "O_CLOEXEC", header: "<sys/fcntl.h>".}: cint
+
+# # <locale.h>
+# var LC_ALL* {.importc: "LC_ALL", header: "<locale.h>".}: cint
+# var LC_COLLATE* {.importc: "LC_COLLATE", header: "<locale.h>".}: cint
+# var LC_CTYPE* {.importc: "LC_CTYPE", header: "<locale.h>".}: cint
+# var LC_MESSAGES* {.importc: "LC_MESSAGES", header: "<locale.h>".}: cint
+# var LC_MONETARY* {.importc: "LC_MONETARY", header: "<locale.h>".}: cint
+# var LC_NUMERIC* {.importc: "LC_NUMERIC", header: "<locale.h>".}: cint
+# var LC_TIME* {.importc: "LC_TIME", header: "<locale.h>".}: cint
+
+# <netdb.h>
+var HOST_NOT_FOUND* {.importc: "HOST_NOT_FOUND", header: "<netdb.h>".}: cint
+var NO_DATA* {.importc: "NO_DATA", header: "<netdb.h>".}: cint
+var NO_RECOVERY* {.importc: "NO_RECOVERY", header: "<netdb.h>".}: cint
+var TRY_AGAIN* {.importc: "TRY_AGAIN", header: "<netdb.h>".}: cint
+var AI_PASSIVE* {.importc: "AI_PASSIVE", header: "<netdb.h>".}: cint
+var AI_CANONNAME* {.importc: "AI_CANONNAME", header: "<netdb.h>".}: cint
+var AI_NUMERICHOST* {.importc: "AI_NUMERICHOST", header: "<netdb.h>".}: cint
+var AI_NUMERICSERV* {.importc: "AI_NUMERICSERV", header: "<netdb.h>".}: cint
+var AI_V4MAPPED* {.importc: "AI_V4MAPPED", header: "<netdb.h>".}: cint
+var AI_ALL* {.importc: "AI_ALL", header: "<netdb.h>".}: cint
+var AI_ADDRCONFIG* {.importc: "AI_ADDRCONFIG", header: "<netdb.h>".}: cint
+var NI_NOFQDN* {.importc: "NI_NOFQDN", header: "<netdb.h>".}: cint
+var NI_NUMERICHOST* {.importc: "NI_NUMERICHOST", header: "<netdb.h>".}: cint
+var NI_NAMEREQD* {.importc: "NI_NAMEREQD", header: "<netdb.h>".}: cint
+var NI_NUMERICSERV* {.importc: "NI_NUMERICSERV", header: "<netdb.h>".}: cint
+var NI_DGRAM* {.importc: "NI_DGRAM", header: "<netdb.h>".}: cint
+var EAI_AGAIN* {.importc: "EAI_AGAIN", header: "<netdb.h>".}: cint
+var EAI_BADFLAGS* {.importc: "EAI_BADFLAGS", header: "<netdb.h>".}: cint
+var EAI_FAIL* {.importc: "EAI_FAIL", header: "<netdb.h>".}: cint
+var EAI_FAMILY* {.importc: "EAI_FAMILY", header: "<netdb.h>".}: cint
+var EAI_MEMORY* {.importc: "EAI_MEMORY", header: "<netdb.h>".}: cint
+var EAI_NONAME* {.importc: "EAI_NONAME", header: "<netdb.h>".}: cint
+var EAI_SERVICE* {.importc: "EAI_SERVICE", header: "<netdb.h>".}: cint
+var EAI_SOCKTYPE* {.importc: "EAI_SOCKTYPE", header: "<netdb.h>".}: cint
+
+var LWIP_DNS_API_DECLARE_H_ERRNO* {.importc: "LWIP_DNS_API_DECLARE_H_ERRNO", header: "<netdb.h>".}: cint
+var LWIP_DNS_API_DEFINE_ERRORS* {.importc: "LWIP_DNS_API_DEFINE_ERRORS", header: "<netdb.h>".}: cint
+var LWIP_DNS_API_DEFINE_FLAGS* {.importc: "LWIP_DNS_API_DEFINE_FLAGS", header: "<netdb.h>".}: cint
+var LWIP_DNS_API_DECLARE_STRUCTS* {.importc: "LWIP_DNS_API_DECLARE_STRUCTS", header: "<netdb.h>".}: cint
+var NETDB_ELEM_SIZE* {.importc: "NETDB_ELEM_SIZE", header: "<netdb.h>".}: cint
+
+
+# <netif.h>
+var ENABLE_LOOPBACK* {.importc: "ENABLE_LOOPBACK", header: "<netif.h>".}: cint
+var NETIF_MAX_HWADDR_LEN* {.importc: "NETIF_MAX_HWADDR_LEN", header: "<netif.h>".}: cint
+var NETIF_NAMESIZE* {.importc: "NETIF_NAMESIZE", header: "<netif.h>".}: cint
+var NETIF_FLAG_UP* {.importc: "NETIF_FLAG_UP", header: "<netif.h>".}: cint
+var NETIF_FLAG_BROADCAST* {.importc: "NETIF_FLAG_BROADCAST", header: "<netif.h>".}: cint
+var NETIF_FLAG_LINK_UP* {.importc: "NETIF_FLAG_LINK_UP", header: "<netif.h>".}: cint
+var NETIF_FLAG_ETHARP* {.importc: "NETIF_FLAG_ETHARP", header: "<netif.h>".}: cint
+var NETIF_FLAG_ETHERNET* {.importc: "NETIF_FLAG_ETHERNET", header: "<netif.h>".}: cint
+var NETIF_FLAG_IGMP* {.importc: "NETIF_FLAG_IGMP", header: "<netif.h>".}: cint
+var NETIF_FLAG_MLD6* {.importc: "NETIF_FLAG_MLD6", header: "<netif.h>".}: cint
+var NETIF_FLAG_GARP* {.importc: "NETIF_FLAG_GARP", header: "<netif.h>".}: cint
+var NETIF_CHECKSUM_GEN_IP* {.importc: "NETIF_CHECKSUM_GEN_IP", header: "<netif.h>".}: cint
+var NETIF_CHECKSUM_GEN_UDP* {.importc: "NETIF_CHECKSUM_GEN_UDP", header: "<netif.h>".}: cint
+var NETIF_CHECKSUM_GEN_TCP* {.importc: "NETIF_CHECKSUM_GEN_TCP", header: "<netif.h>".}: cint
+var NETIF_CHECKSUM_GEN_ICMP* {.importc: "NETIF_CHECKSUM_GEN_ICMP", header: "<netif.h>".}: cint
+var NETIF_CHECKSUM_GEN_ICMP6* {.importc: "NETIF_CHECKSUM_GEN_ICMP6", header: "<netif.h>".}: cint
+var NETIF_CHECKSUM_CHECK_IP* {.importc: "NETIF_CHECKSUM_CHECK_IP", header: "<netif.h>".}: cint
+var NETIF_CHECKSUM_CHECK_UDP* {.importc: "NETIF_CHECKSUM_CHECK_UDP", header: "<netif.h>".}: cint
+var NETIF_CHECKSUM_CHECK_TCP* {.importc: "NETIF_CHECKSUM_CHECK_TCP", header: "<netif.h>".}: cint
+var NETIF_CHECKSUM_CHECK_ICMP* {.importc: "NETIF_CHECKSUM_CHECK_ICMP", header: "<netif.h>".}: cint
+var NETIF_CHECKSUM_CHECK_ICMP6* {.importc: "NETIF_CHECKSUM_CHECK_ICMP6", header: "<netif.h>".}: cint
+var NETIF_CHECKSUM_ENABLE_ALL* {.importc: "NETIF_CHECKSUM_ENABLE_ALL", header: "<netif.h>".}: cint
+var NETIF_CHECKSUM_DISABLE_ALL* {.importc: "NETIF_CHECKSUM_DISABLE_ALL", header: "<netif.h>".}: cint
+var NETIF_ADDR_IDX_MAX* {.importc: "NETIF_ADDR_IDX_MAX", header: "<netif.h>".}: cint
+var LWIP_NETIF_USE_HINTS* {.importc: "LWIP_NETIF_USE_HINTS", header: "<netif.h>".}: cint
+var NETIF_NO_INDEX* {.importc: "NETIF_NO_INDEX", header: "<netif.h>".}: cint
+var LWIP_NSC_NONE* {.importc: "LWIP_NSC_NONE", header: "<netif.h>".}: cint
+var LWIP_NSC_NETIF_ADDED* {.importc: "LWIP_NSC_NETIF_ADDED", header: "<netif.h>".}: cint
+var LWIP_NSC_NETIF_REMOVED* {.importc: "LWIP_NSC_NETIF_REMOVED", header: "<netif.h>".}: cint
+var LWIP_NSC_LINK_CHANGED* {.importc: "LWIP_NSC_LINK_CHANGED", header: "<netif.h>".}: cint
+var LWIP_NSC_STATUS_CHANGED* {.importc: "LWIP_NSC_STATUS_CHANGED", header: "<netif.h>".}: cint
+var LWIP_NSC_IPV4_ADDRESS_CHANGED* {.importc: "LWIP_NSC_IPV4_ADDRESS_CHANGED", header: "<netif.h>".}: cint
+var LWIP_NSC_IPV4_GATEWAY_CHANGED* {.importc: "LWIP_NSC_IPV4_GATEWAY_CHANGED", header: "<netif.h>".}: cint
+var LWIP_NSC_IPV4_NETMASK_CHANGED* {.importc: "LWIP_NSC_IPV4_NETMASK_CHANGED", header: "<netif.h>".}: cint
+var LWIP_NSC_IPV4_SETTINGS_CHANGED* {.importc: "LWIP_NSC_IPV4_SETTINGS_CHANGED", header: "<netif.h>".}: cint
+var LWIP_NSC_IPV6_SET* {.importc: "LWIP_NSC_IPV6_SET", header: "<netif.h>".}: cint
+var LWIP_NSC_IPV6_ADDR_STATE_CHANGED* {.importc: "LWIP_NSC_IPV6_ADDR_STATE_CHANGED", header: "<netif.h>".}: cint
+#define NETIF_DECLARE_EXT_CALLBACK(name)
+
+
+# <net/if.h>
+var IF_NAMESIZE* {.importc: "IF_NAMESIZE", header: "<net/if.h>".}: cint
+
+# <sys/socket.h>
+var IPPROTO_IP* {.importc: "IPPROTO_IP", header: "<sys/socket.h>".}: cint
+var IPPROTO_IPV6* {.importc: "IPPROTO_IPV6", header: "<sys/socket.h>".}: cint
+var IPPROTO_ICMP* {.importc: "IPPROTO_ICMP", header: "<sys/socket.h>".}: cint
+var IPPROTO_ICMPV6* {.importc: "IPPROTO_ICMPV6", header: "<sys/socket.h>".}: cint
+var IPPROTO_RAW* {.importc: "IPPROTO_RAW", header: "<sys/socket.h>".}: cint
+var IPPROTO_TCP* {.importc: "IPPROTO_TCP", header: "<sys/socket.h>".}: cint
+var IPPROTO_UDP* {.importc: "IPPROTO_UDP", header: "<sys/socket.h>".}: cint
+var INADDR_ANY* {.importc: "INADDR_ANY", header: "<sys/socket.h>".}: InAddrScalar
+var INADDR_LOOPBACK* {.importc: "INADDR_LOOPBACK", header: "<sys/socket.h>".}: InAddrScalar
+var INADDR_BROADCAST* {.importc: "INADDR_BROADCAST", header: "<sys/socket.h>".}: InAddrScalar
+var INET_ADDRSTRLEN* {.importc: "INET_ADDRSTRLEN", header: "<sys/socket.h>".}: cint
+var INET6_ADDRSTRLEN* {.importc: "INET6_ADDRSTRLEN", header: "<sys/socket.h>".}: cint
+var IPV6_JOIN_GROUP* {.importc: "IPV6_JOIN_GROUP", header: "<sys/socket.h>".}: cint
+var IPV6_LEAVE_GROUP* {.importc: "IPV6_LEAVE_GROUP", header: "<sys/socket.h>".}: cint
+var IPV6_MULTICAST_HOPS* {.importc: "IPV6_MULTICAST_HOPS", header: "<sys/socket.h>".}: cint
+var IPV6_MULTICAST_IF* {.importc: "IPV6_MULTICAST_IF", header: "<sys/socket.h>".}: cint
+var IPV6_MULTICAST_LOOP* {.importc: "IPV6_MULTICAST_LOOP", header: "<sys/socket.h>".}: cint
+var IPV6_UNICAST_HOPS* {.importc: "IPV6_UNICAST_HOPS", header: "<sys/socket.h>".}: cint
+var IPV6_V6ONLY* {.importc: "IPV6_V6ONLY", header: "<sys/socket.h>".}: cint
+
+# <netinet/tcp.h>
+const TCP_NODELAY*    = 0x01    # don't delay send to coalesce packets
+const TCP_KEEPALIVE*  = 0x02    # send KEEPALIVE probes when idle for pcb->keep_idle milliseconds
+const TCP_KEEPIDLE*   = 0x03    # set pcb->keep_idle  - Same as TCP_KEEPALIVE, but use seconds for get/setsockopt
+const TCP_KEEPINTVL*  = 0x04    # set pcb->keep_intvl - Use seconds for get/setsockopt
+const TCP_KEEPCNT*    = 0x05    # set pcb->keep_cnt   - Use number of probes sent for get/setsockopt
+
+# <nl_types.h>
+# var NL_SETD* {.importc: "NL_SETD", header: "<nl_types.h>".}: cint
+# var NL_CAT_LOCALE* {.importc: "NL_CAT_LOCALE", header: "<nl_types.h>".}: cint
+
+# <sys/poll.h>
+# var POLLIN* {.importc: "POLLIN", header: "<sys/poll.h>".}: cshort
+# var POLLRDNORM* {.importc: "POLLRDNORM", header: "<sys/poll.h>".}: cshort
+# var POLLRDBAND* {.importc: "POLLRDBAND", header: "<sys/poll.h>".}: cshort
+# var POLLPRI* {.importc: "POLLPRI", header: "<sys/poll.h>".}: cshort
+# var POLLOUT* {.importc: "POLLOUT", header: "<sys/poll.h>".}: cshort
+# var POLLWRNORM* {.importc: "POLLWRNORM", header: "<sys/poll.h>".}: cshort
+# var POLLWRBAND* {.importc: "POLLWRBAND", header: "<sys/poll.h>".}: cshort
+# var POLLERR* {.importc: "POLLERR", header: "<sys/poll.h>".}: cshort
+# var POLLHUP* {.importc: "POLLHUP", header: "<sys/poll.h>".}: cshort
+# var POLLNVAL* {.importc: "POLLNVAL", header: "<sys/poll.h>".}: cshort
+
+# <pthread.h>
+var PTHREAD_STACK_MIN* {.importc: "PTHREAD_STACK_MIN", header: "<pthread.h>".}: cint
+# var PTHREAD_BARRIER_SERIAL_THREAD* {.importc: "PTHREAD_BARRIER_SERIAL_THREAD", header: "<pthread.h>".}: cint
+# var PTHREAD_CANCEL_ASYNCHRONOUS* {.importc: "PTHREAD_CANCEL_ASYNCHRONOUS", header: "<pthread.h>".}: cint
+# var PTHREAD_CANCEL_ENABLE* {.importc: "PTHREAD_CANCEL_ENABLE", header: "<pthread.h>".}: cint
+# var PTHREAD_CANCEL_DEFERRED* {.importc: "PTHREAD_CANCEL_DEFERRED", header: "<pthread.h>".}: cint
+# var PTHREAD_CANCEL_DISABLE* {.importc: "PTHREAD_CANCEL_DISABLE", header: "<pthread.h>".}: cint
+# var PTHREAD_CREATE_DETACHED* {.importc: "PTHREAD_CREATE_DETACHED", header: "<pthread.h>".}: cint
+# var PTHREAD_CREATE_JOINABLE* {.importc: "PTHREAD_CREATE_JOINABLE", header: "<pthread.h>".}: cint
+# var PTHREAD_EXPLICIT_SCHED* {.importc: "PTHREAD_EXPLICIT_SCHED", header: "<pthread.h>".}: cint
+# var PTHREAD_INHERIT_SCHED* {.importc: "PTHREAD_INHERIT_SCHED", header: "<pthread.h>".}: cint
+# var PTHREAD_MUTEX_DEFAULT* {.importc: "PTHREAD_MUTEX_DEFAULT", header: "<pthread.h>".}: cint
+# var PTHREAD_MUTEX_ERRORCHECK* {.importc: "PTHREAD_MUTEX_ERRORCHECK", header: "<pthread.h>".}: cint
+# var PTHREAD_MUTEX_NORMAL* {.importc: "PTHREAD_MUTEX_NORMAL", header: "<pthread.h>".}: cint
+# var PTHREAD_MUTEX_RECURSIVE* {.importc: "PTHREAD_MUTEX_RECURSIVE", header: "<pthread.h>".}: cint
+# var PTHREAD_PRIO_INHERIT* {.importc: "PTHREAD_PRIO_INHERIT", header: "<pthread.h>".}: cint
+# var PTHREAD_PRIO_NONE* {.importc: "PTHREAD_PRIO_NONE", header: "<pthread.h>".}: cint
+# var PTHREAD_PRIO_PROTECT* {.importc: "PTHREAD_PRIO_PROTECT", header: "<pthread.h>".}: cint
+# var PTHREAD_PROCESS_SHARED* {.importc: "PTHREAD_PROCESS_SHARED", header: "<pthread.h>".}: cint
+# var PTHREAD_PROCESS_PRIVATE* {.importc: "PTHREAD_PROCESS_PRIVATE", header: "<pthread.h>".}: cint
+# var PTHREAD_SCOPE_PROCESS* {.importc: "PTHREAD_SCOPE_PROCESS", header: "<pthread.h>".}: cint
+# var PTHREAD_SCOPE_SYSTEM* {.importc: "PTHREAD_SCOPE_SYSTEM", header: "<pthread.h>".}: cint
+
+# # <sched.h>
+# var SCHED_FIFO* {.importc: "SCHED_FIFO", header: "<sched.h>".}: cint
+# var SCHED_RR* {.importc: "SCHED_RR", header: "<sched.h>".}: cint
+# var SCHED_SPORADIC* {.importc: "SCHED_SPORADIC", header: "<sched.h>".}: cint
+# var SCHED_OTHER* {.importc: "SCHED_OTHER", header: "<sched.h>".}: cint
+
+# <semaphore.h>
+var SEM_FAILED* {.importc: "SEM_FAILED", header: "<semaphore.h>".}: pointer
+
+# # <signal.h>
+# var SIGEV_NONE* {.importc: "SIGEV_NONE", header: "<signal.h>".}: cint
+# var SIGEV_SIGNAL* {.importc: "SIGEV_SIGNAL", header: "<signal.h>".}: cint
+# var SIGEV_THREAD* {.importc: "SIGEV_THREAD", header: "<signal.h>".}: cint
+# var SIGABRT* {.importc: "SIGABRT", header: "<signal.h>".}: cint
+# var SIGALRM* {.importc: "SIGALRM", header: "<signal.h>".}: cint
+# var SIGBUS* {.importc: "SIGBUS", header: "<signal.h>".}: cint
+# var SIGCHLD* {.importc: "SIGCHLD", header: "<signal.h>".}: cint
+# var SIGCONT* {.importc: "SIGCONT", header: "<signal.h>".}: cint
+# var SIGFPE* {.importc: "SIGFPE", header: "<signal.h>".}: cint
+# var SIGHUP* {.importc: "SIGHUP", header: "<signal.h>".}: cint
+# var SIGILL* {.importc: "SIGILL", header: "<signal.h>".}: cint
+# var SIGINT* {.importc: "SIGINT", header: "<signal.h>".}: cint
+# var SIGKILL* {.importc: "SIGKILL", header: "<signal.h>".}: cint
+# var SIGPIPE* {.importc: "SIGPIPE", header: "<signal.h>".}: cint
+# var SIGQUIT* {.importc: "SIGQUIT", header: "<signal.h>".}: cint
+# var SIGSEGV* {.importc: "SIGSEGV", header: "<signal.h>".}: cint
+# var SIGSTOP* {.importc: "SIGSTOP", header: "<signal.h>".}: cint
+# var SIGTERM* {.importc: "SIGTERM", header: "<signal.h>".}: cint
+# var SIGTSTP* {.importc: "SIGTSTP", header: "<signal.h>".}: cint
+# var SIGTTIN* {.importc: "SIGTTIN", header: "<signal.h>".}: cint
+# var SIGTTOU* {.importc: "SIGTTOU", header: "<signal.h>".}: cint
+# var SIGUSR1* {.importc: "SIGUSR1", header: "<signal.h>".}: cint
+# var SIGUSR2* {.importc: "SIGUSR2", header: "<signal.h>".}: cint
+# var SIGPOLL* {.importc: "SIGPOLL", header: "<signal.h>".}: cint
+# var SIGPROF* {.importc: "SIGPROF", header: "<signal.h>".}: cint
+# var SIGSYS* {.importc: "SIGSYS", header: "<signal.h>".}: cint
+# var SIGTRAP* {.importc: "SIGTRAP", header: "<signal.h>".}: cint
+# var SIGURG* {.importc: "SIGURG", header: "<signal.h>".}: cint
+# var SIGVTALRM* {.importc: "SIGVTALRM", header: "<signal.h>".}: cint
+# var SIGXCPU* {.importc: "SIGXCPU", header: "<signal.h>".}: cint
+# var SIGXFSZ* {.importc: "SIGXFSZ", header: "<signal.h>".}: cint
+# var SA_NOCLDSTOP* {.importc: "SA_NOCLDSTOP", header: "<signal.h>".}: cint
+# var SIG_BLOCK* {.importc: "SIG_BLOCK", header: "<signal.h>".}: cint
+# var SIG_UNBLOCK* {.importc: "SIG_UNBLOCK", header: "<signal.h>".}: cint
+# var SIG_SETMASK* {.importc: "SIG_SETMASK", header: "<signal.h>".}: cint
+# var SA_ONSTACK* {.importc: "SA_ONSTACK", header: "<signal.h>".}: cint
+# var SA_RESETHAND* {.importc: "SA_RESETHAND", header: "<signal.h>".}: cint
+# var SA_RESTART* {.importc: "SA_RESTART", header: "<signal.h>".}: cint
+# var SA_SIGINFO* {.importc: "SA_SIGINFO", header: "<signal.h>".}: cint
+# var SA_NOCLDWAIT* {.importc: "SA_NOCLDWAIT", header: "<signal.h>".}: cint
+# var SA_NODEFER* {.importc: "SA_NODEFER", header: "<signal.h>".}: cint
+# var SS_ONSTACK* {.importc: "SS_ONSTACK", header: "<signal.h>".}: cint
+# var SS_DISABLE* {.importc: "SS_DISABLE", header: "<signal.h>".}: cint
+# var MINSIGSTKSZ* {.importc: "MINSIGSTKSZ", header: "<signal.h>".}: cint
+# var SIGSTKSZ* {.importc: "SIGSTKSZ", header: "<signal.h>".}: cint
+# var SIG_HOLD* {.importc: "SIG_HOLD", header: "<signal.h>".}: Sighandler
+# var SIG_DFL* {.importc: "SIG_DFL", header: "<signal.h>".}: Sighandler
+# var SIG_ERR* {.importc: "SIG_ERR", header: "<signal.h>".}: Sighandler
+# var SIG_IGN* {.importc: "SIG_IGN", header: "<signal.h>".}: Sighandler
+
+# # <sys/ipc.h>
+# var IPC_CREAT* {.importc: "IPC_CREAT", header: "<sys/ipc.h>".}: cint
+# var IPC_EXCL* {.importc: "IPC_EXCL", header: "<sys/ipc.h>".}: cint
+# var IPC_NOWAIT* {.importc: "IPC_NOWAIT", header: "<sys/ipc.h>".}: cint
+# var IPC_PRIVATE* {.importc: "IPC_PRIVATE", header: "<sys/ipc.h>".}: cint
+# var IPC_RMID* {.importc: "IPC_RMID", header: "<sys/ipc.h>".}: cint
+# var IPC_SET* {.importc: "IPC_SET", header: "<sys/ipc.h>".}: cint
+# var IPC_STAT* {.importc: "IPC_STAT", header: "<sys/ipc.h>".}: cint
+
+# # <sys/mman.h>
+# var PROT_READ* {.importc: "PROT_READ", header: "<sys/mman.h>".}: cint
+# var PROT_WRITE* {.importc: "PROT_WRITE", header: "<sys/mman.h>".}: cint
+# var PROT_EXEC* {.importc: "PROT_EXEC", header: "<sys/mman.h>".}: cint
+# var PROT_NONE* {.importc: "PROT_NONE", header: "<sys/mman.h>".}: cint
+# var MAP_ANONYMOUS* {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
+# var MAP_FIXED_NOREPLACE* {.importc: "MAP_FIXED_NOREPLACE", header: "<sys/mman.h>".}: cint
+# var MAP_NORESERVE* {.importc: "MAP_NORESERVE", header: "<sys/mman.h>".}: cint
+# var MAP_SHARED* {.importc: "MAP_SHARED", header: "<sys/mman.h>".}: cint
+# var MAP_PRIVATE* {.importc: "MAP_PRIVATE", header: "<sys/mman.h>".}: cint
+# var MAP_FIXED* {.importc: "MAP_FIXED", header: "<sys/mman.h>".}: cint
+# var MS_ASYNC* {.importc: "MS_ASYNC", header: "<sys/mman.h>".}: cint
+# var MS_SYNC* {.importc: "MS_SYNC", header: "<sys/mman.h>".}: cint
+# var MS_INVALIDATE* {.importc: "MS_INVALIDATE", header: "<sys/mman.h>".}: cint
+# var MCL_CURRENT* {.importc: "MCL_CURRENT", header: "<sys/mman.h>".}: cint
+# var MCL_FUTURE* {.importc: "MCL_FUTURE", header: "<sys/mman.h>".}: cint
+# var MAP_FAILED* {.importc: "MAP_FAILED", header: "<sys/mman.h>".}: pointer
+# var POSIX_MADV_NORMAL* {.importc: "POSIX_MADV_NORMAL", header: "<sys/mman.h>".}: cint
+# var POSIX_MADV_SEQUENTIAL* {.importc: "POSIX_MADV_SEQUENTIAL", header: "<sys/mman.h>".}: cint
+# var POSIX_MADV_RANDOM* {.importc: "POSIX_MADV_RANDOM", header: "<sys/mman.h>".}: cint
+# var POSIX_MADV_WILLNEED* {.importc: "POSIX_MADV_WILLNEED", header: "<sys/mman.h>".}: cint
+# var POSIX_MADV_DONTNEED* {.importc: "POSIX_MADV_DONTNEED", header: "<sys/mman.h>".}: cint
+# var POSIX_TYPED_MEM_ALLOCATE* {.importc: "POSIX_TYPED_MEM_ALLOCATE", header: "<sys/mman.h>".}: cint
+# var POSIX_TYPED_MEM_ALLOCATE_CONTIG* {.importc: "POSIX_TYPED_MEM_ALLOCATE_CONTIG", header: "<sys/mman.h>".}: cint
+# var POSIX_TYPED_MEM_MAP_ALLOCATABLE* {.importc: "POSIX_TYPED_MEM_MAP_ALLOCATABLE", header: "<sys/mman.h>".}: cint
+
+# <sys/resource.h>
+# var RLIMIT_NOFILE* {.importc: "RLIMIT_NOFILE", header: "<sys/resource.h>".}: cint
+
+var FD_MAX* {.importc: "CONFIG_LWIP_MAX_SOCKETS", header: "<lwipopts.h>".}: cint
+
+# <sys/select.h>
+var FD_SETSIZE* {.importc: "FD_SETSIZE", header: "<sys/select.h>".}: cint
+
+# <sys/socket.h>
+# struct msghdr->msg_flags bit field values 
+const MSG_TRUNC*   = 0x04
+const MSG_CTRUNC*  = 0x08
+
+# Flags we can use with send and recv.
+const MSG_PEEK*       = 0x01    # Peeks at an incoming message
+const MSG_WAITALL*    = 0x02    # Unimplemented: Requests that the function block until the full amount of data requested can be returned
+const MSG_OOB*        = 0x04    # Unimplemented: Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific
+const MSG_DONTWAIT*   = 0x08    # Nonblocking i/o for this operation only
+const MSG_MORE*       = 0x10    # Sender will send more
+# const MSG_NOSIGNAL*   = 0x20    # Uninmplemented: Requests not to send the SIGPIPE signal if an attempt to send is made on a stream-oriented socket that is no longer connected.
+
+# Alternately, they can defined like this, but the above seems better when they're known/stable values:
+# var MSG_TRUNC* {.importc: "MSG_TRUNC", header: "<sys/socket.h>".}: cint
+# var MSG_CTRUNC* {.importc: "MSG_CTRUNC", header: "<sys/socket.h>".}: cint
+# var MSG_DONTROUTE* {.importc: "MSG_DONTROUTE", header: "<sys/socket.h>".}: cint # not defined in lwip
+# var MSG_EOR* {.importc: "MSG_EOR", header: "<sys/socket.h>".}: cint # not defined in lwip
+# var MSG_OOB* {.importc: "MSG_OOB", header: "<sys/socket.h>".}: cint
+
+
+# var SCM_RIGHTS* {.importc: "SCM_RIGHTS", header: "<sys/socket.h>".}: cint
+var SO_ACCEPTCONN* {.importc: "SO_ACCEPTCONN", header: "<sys/socket.h>".}: cint
+var SO_BROADCAST* {.importc: "SO_BROADCAST", header: "<sys/socket.h>".}: cint
+var SO_DEBUG* {.importc: "SO_DEBUG", header: "<sys/socket.h>".}: cint
+var SO_DONTROUTE* {.importc: "SO_DONTROUTE", header: "<sys/socket.h>".}: cint
+var SO_ERROR* {.importc: "SO_ERROR", header: "<sys/socket.h>".}: cint
+var SO_KEEPALIVE* {.importc: "SO_KEEPALIVE", header: "<sys/socket.h>".}: cint
+var SO_LINGER* {.importc: "SO_LINGER", header: "<sys/socket.h>".}: cint
+var SO_OOBINLINE* {.importc: "SO_OOBINLINE", header: "<sys/socket.h>".}: cint
+var SO_RCVBUF* {.importc: "SO_RCVBUF", header: "<sys/socket.h>".}: cint
+var SO_RCVLOWAT* {.importc: "SO_RCVLOWAT", header: "<sys/socket.h>".}: cint
+var SO_RCVTIMEO* {.importc: "SO_RCVTIMEO", header: "<sys/socket.h>".}: cint
+var SO_REUSEADDR* {.importc: "SO_REUSEADDR", header: "<sys/socket.h>".}: cint
+var SO_SNDBUF* {.importc: "SO_SNDBUF", header: "<sys/socket.h>".}: cint
+var SO_SNDLOWAT* {.importc: "SO_SNDLOWAT", header: "<sys/socket.h>".}: cint
+var SO_SNDTIMEO* {.importc: "SO_SNDTIMEO", header: "<sys/socket.h>".}: cint
+var SO_TYPE* {.importc: "SO_TYPE", header: "<sys/socket.h>".}: cint
+var SOCK_DGRAM* {.importc: "SOCK_DGRAM", header: "<sys/socket.h>".}: cint
+var SOCK_RAW* {.importc: "SOCK_RAW", header: "<sys/socket.h>".}: cint
+
+# var SOCK_SEQPACKET* {.importc: "SOCK_SEQPACKET", header: "<sys/socket.h>".}: cint
+const SOCK_SEQPACKET* = cint(5)
+
+var SOCK_STREAM* {.importc: "SOCK_STREAM", header: "<sys/socket.h>".}: cint
+var SOL_SOCKET* {.importc: "SOL_SOCKET", header: "<sys/socket.h>".}: cint
+
+const SocketMaxConnections {.intdefine.}: int = 32
+var SOMAXCONN*: cint = SocketMaxConnections.cint
+
+var AF_INET* {.importc: "AF_INET", header: "<sys/socket.h>".}: cint
+var AF_INET6* {.importc: "AF_INET6", header: "<sys/socket.h>".}: cint
+# var AF_UNIX* {.importc: "AF_UNIX", header: "<sys/socket.h>".}: cint
+const AF_UNIX*: cint = 1 # for compat with Nim libraries, doesn't exist on freertos
+var AF_UNSPEC* {.importc: "AF_UNSPEC", header: "<sys/socket.h>".}: cint
+var SHUT_RD* {.importc: "SHUT_RD", header: "<sys/socket.h>".}: cint
+var SHUT_RDWR* {.importc: "SHUT_RDWR", header: "<sys/socket.h>".}: cint
+var SHUT_WR* {.importc: "SHUT_WR", header: "<sys/socket.h>".}: cint
+
+# # <sys/stat.h>
+# <sys/stat.h>
+
+# var S_IFBLK* {.importc: "S_IFBLK", header: "<sys/stat.h>".}: cint
+# var S_IFCHR* {.importc: "S_IFCHR", header: "<sys/stat.h>".}: cint
+# var S_IFDIR* {.importc: "S_IFDIR", header: "<sys/stat.h>".}: cint
+# var S_IFIFO* {.importc: "S_IFIFO", header: "<sys/stat.h>".}: cint
+# var S_IFLNK* {.importc: "S_IFLNK", header: "<sys/stat.h>".}: cint
+# var S_IFMT* {.importc: "S_IFMT", header: "<sys/stat.h>".}: cint
+# var S_IFREG* {.importc: "S_IFREG", header: "<sys/stat.h>".}: cint
+# var S_IFSOCK* {.importc: "S_IFSOCK", header: "<sys/stat.h>".}: cint
+var S_IRGRP* {.importc: "S_IRGRP", header: "<sys/stat.h>".}: cint
+var S_IROTH* {.importc: "S_IROTH", header: "<sys/stat.h>".}: cint
+var S_IRUSR* {.importc: "S_IRUSR", header: "<sys/stat.h>".}: cint
+# var S_IRWXG* {.importc: "S_IRWXG", header: "<sys/stat.h>".}: cint
+# var S_IRWXO* {.importc: "S_IRWXO", header: "<sys/stat.h>".}: cint
+# var S_IRWXU* {.importc: "S_IRWXU", header: "<sys/stat.h>".}: cint
+# var S_ISGID* {.importc: "S_ISGID", header: "<sys/stat.h>".}: cint
+# var S_ISUID* {.importc: "S_ISUID", header: "<sys/stat.h>".}: cint
+# var S_ISVTX* {.importc: "S_ISVTX", header: "<sys/stat.h>".}: cint
+var S_IWGRP* {.importc: "S_IWGRP", header: "<sys/stat.h>".}: cint
+var S_IWOTH* {.importc: "S_IWOTH", header: "<sys/stat.h>".}: cint
+var S_IWUSR* {.importc: "S_IWUSR", header: "<sys/stat.h>".}: cint
+var S_IXGRP* {.importc: "S_IXGRP", header: "<sys/stat.h>".}: cint
+var S_IXOTH* {.importc: "S_IXOTH", header: "<sys/stat.h>".}: cint
+var S_IXUSR* {.importc: "S_IXUSR", header: "<sys/stat.h>".}: cint
+
+# # <sys/statvfs.h>
+# var ST_RDONLY* {.importc: "ST_RDONLY", header: "<sys/statvfs.h>".}: cint
+# var ST_NOSUID* {.importc: "ST_NOSUID", header: "<sys/statvfs.h>".}: cint
+
+# # <sys/wait.h>
+# var WNOHANG* {.importc: "WNOHANG", header: "<sys/wait.h>".}: cint
+# var WUNTRACED* {.importc: "WUNTRACED", header: "<sys/wait.h>".}: cint
+# var WEXITED* {.importc: "WEXITED", header: "<sys/wait.h>".}: cint
+# var WSTOPPED* {.importc: "WSTOPPED", header: "<sys/wait.h>".}: cint
+# var WCONTINUED* {.importc: "WCONTINUED", header: "<sys/wait.h>".}: cint
+# var WNOWAIT* {.importc: "WNOWAIT", header: "<sys/wait.h>".}: cint
+# var P_ALL* {.importc: "P_ALL", header: "<sys/wait.h>".}: cint
+# var P_PID* {.importc: "P_PID", header: "<sys/wait.h>".}: cint
+# var P_PGID* {.importc: "P_PGID", header: "<sys/wait.h>".}: cint
+
+# # <spawn.h>
+# var POSIX_SPAWN_RESETIDS* {.importc: "POSIX_SPAWN_RESETIDS", header: "<spawn.h>".}: cint
+# var POSIX_SPAWN_SETPGROUP* {.importc: "POSIX_SPAWN_SETPGROUP", header: "<spawn.h>".}: cint
+# var POSIX_SPAWN_SETSCHEDPARAM* {.importc: "POSIX_SPAWN_SETSCHEDPARAM", header: "<spawn.h>".}: cint
+# var POSIX_SPAWN_SETSCHEDULER* {.importc: "POSIX_SPAWN_SETSCHEDULER", header: "<spawn.h>".}: cint
+# var POSIX_SPAWN_SETSIGDEF* {.importc: "POSIX_SPAWN_SETSIGDEF", header: "<spawn.h>".}: cint
+# var POSIX_SPAWN_SETSIGMASK* {.importc: "POSIX_SPAWN_SETSIGMASK", header: "<spawn.h>".}: cint
+
+## <stdio.h>
+# var IOFBF* {.importc: "_IOFBF", header: "<stdio.h>".}: cint
+# var IONBF* {.importc: "_IONBF", header: "<stdio.h>".}: cint
+
+# <time.h>
+var CLOCKS_PER_SEC* {.importc: "CLOCKS_PER_SEC", header: "<time.h>".}: clong
+var CLOCK_PROCESS_CPUTIME_ID* {.importc: "CLOCK_PROCESS_CPUTIME_ID", header: "<time.h>".}: cint
+var CLOCK_THREAD_CPUTIME_ID* {.importc: "CLOCK_THREAD_CPUTIME_ID", header: "<time.h>".}: cint
+var CLOCK_REALTIME* {.importc: "CLOCK_REALTIME", header: "<time.h>".}: cint
+var TIMER_ABSTIME* {.importc: "TIMER_ABSTIME", header: "<time.h>".}: cint
+var CLOCK_MONOTONIC* {.importc: "CLOCK_MONOTONIC", header: "<time.h>".}: cint
+
+# <unistd.h>
+
+const F_OK* = cint(0)
+const R_OK* = cint(4)
+const W_OK* = cint(2)
+const X_OK* = cint(1)
+const F_LOCK* = cint(1)
+const F_TEST* = cint(3)
+const F_TLOCK* = cint(2)
+const F_ULOCK* = cint(0)
+
+# <stdio.h>
+const SEEK_SET* = cint(0)
+const SEEK_CUR* = cint(1)
+const SEEK_END* = cint(2)
diff --git a/lib/posix/posix_haiku.nim b/lib/posix/posix_haiku.nim
new file mode 100644
index 000000000..32a6d24e2
--- /dev/null
+++ b/lib/posix/posix_haiku.nim
@@ -0,0 +1,603 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+when defined(nimHasStyleChecks):
+  {.push styleChecks: off.}
+
+const
+  hasSpawnH = true # should exist for every Posix system nowadays
+  hasAioH = defined(linux)
+
+when defined(linux) and not defined(android):
+  # On Linux:
+  # timer_{create,delete,settime,gettime},
+  # clock_{getcpuclockid, getres, gettime, nanosleep, settime} lives in librt
+  {.passl: "-lrt".}
+when defined(solaris):
+  # On Solaris hstrerror lives in libresolv
+  {.passl: "-lresolv".}
+
+type
+  DIR* {.importc: "DIR", header: "<dirent.h>",
+          incompleteStruct.} = object
+    ## A type representing a directory stream.
+
+type
+  SocketHandle* = distinct cint # The type used to represent socket descriptors
+
+type
+  Time* {.importc: "time_t", header: "<time.h>".} = distinct (
+    when defined(i386):
+      int32
+    else:
+      int64
+  )
+  Timespec* {.importc: "struct timespec",
+               header: "<time.h>", final, pure.} = object ## struct timespec
+    tv_sec*: Time  ## Seconds.
+    tv_nsec*: int  ## Nanoseconds.
+
+  Dirent* {.importc: "struct dirent",
+             header: "<dirent.h>", final, pure.} = object ## dirent_t struct
+    when defined(haiku):
+      d_dev*: Dev ## Device (not POSIX)
+      d_pdev*: Dev ## Parent device (only for queries) (not POSIX)
+    d_ino*: Ino  ## File serial number.
+    when defined(dragonfly):
+      # DragonflyBSD doesn't have `d_reclen` field.
+      d_type*: uint8
+    elif defined(linux) or defined(macosx) or defined(freebsd) or
+         defined(netbsd) or defined(openbsd) or defined(genode):
+      d_reclen*: cshort ## Length of this record. (not POSIX)
+      d_type*: int8 ## Type of file; not supported by all filesystem types.
+                    ## (not POSIX)
+      when defined(linux) or defined(openbsd):
+        d_off*: Off  ## Not an offset. Value that `telldir()` would return.
+    elif defined(haiku):
+      d_pino*: Ino ## Parent inode (only for queries) (not POSIX)
+      d_reclen*: cushort ## Length of this record. (not POSIX)
+
+    when not defined(haiku):
+      d_name*: array[0..255, char] ## Name of entry.
+    else:
+      d_name*: UncheckedArray[char] ## Name of entry (NUL terminated).
+
+  Tflock* {.importc: "struct flock", final, pure,
+            header: "<fcntl.h>".} = object ## flock type
+    l_type*: cshort   ## Type of lock; F_RDLCK, F_WRLCK, F_UNLCK.
+    l_whence*: cshort ## Flag for starting offset.
+    l_start*: Off     ## Relative offset in bytes.
+    l_len*: Off       ## Size; if 0 then until EOF.
+    l_pid*: Pid      ## Process ID of the process holding the lock;
+                      ## returned with F_GETLK.
+
+  FTW* {.importc: "struct FTW", header: "<ftw.h>", final, pure.} = object
+    base*: cint
+    level*: cint
+
+  Glob* {.importc: "glob_t", header: "<glob.h>",
+           final, pure.} = object ## glob_t
+    gl_pathc*: int          ## Count of paths matched by pattern.
+    gl_pathv*: cstringArray ## Pointer to a list of matched pathnames.
+    gl_offs*: int           ## Slots to reserve at the beginning of gl_pathv.
+
+  Group* {.importc: "struct group", header: "<grp.h>",
+            final, pure.} = object ## struct group
+    gr_name*: cstring     ## The name of the group.
+    gr_gid*: Gid         ## Numerical group ID.
+    gr_mem*: cstringArray ## Pointer to a null-terminated array of character
+                          ## pointers to member names.
+
+  Iconv* {.importc: "iconv_t", header: "<iconv.h>", final, pure.} =
+    object ## Identifies the conversion from one codeset to another.
+
+  Lconv* {.importc: "struct lconv", header: "<locale.h>", final,
+            pure.} = object
+    currency_symbol*: cstring
+    decimal_point*: cstring
+    frac_digits*: char
+    grouping*: cstring
+    int_curr_symbol*: cstring
+    int_frac_digits*: char
+    int_n_cs_precedes*: char
+    int_n_sep_by_space*: char
+    int_n_sign_posn*: char
+    int_p_cs_precedes*: char
+    int_p_sep_by_space*: char
+    int_p_sign_posn*: char
+    mon_decimal_point*: cstring
+    mon_grouping*: cstring
+    mon_thousands_sep*: cstring
+    negative_sign*: cstring
+    n_cs_precedes*: char
+    n_sep_by_space*: char
+    n_sign_posn*: char
+    positive_sign*: cstring
+    p_cs_precedes*: char
+    p_sep_by_space*: char
+    p_sign_posn*: char
+    thousands_sep*: cstring
+
+  Mqd* {.importc: "mqd_t", header: "<mqueue.h>", final, pure.} = object
+  MqAttr* {.importc: "struct mq_attr",
+             header: "<mqueue.h>",
+             final, pure.} = object ## message queue attribute
+    mq_flags*: int   ## Message queue flags.
+    mq_maxmsg*: int  ## Maximum number of messages.
+    mq_msgsize*: int ## Maximum message size.
+    mq_curmsgs*: int ## Number of messages currently queued.
+
+  Passwd* {.importc: "struct passwd", header: "<pwd.h>",
+             final, pure.} = object ## struct passwd
+    pw_name*: cstring   ## User's login name.
+    pw_uid*: Uid        ## Numerical user ID.
+    pw_gid*: Gid        ## Numerical group ID.
+    pw_dir*: cstring    ## Initial working directory.
+    pw_shell*: cstring  ## Program to use as shell.
+
+  Blkcnt* {.importc: "blkcnt_t", header: "<sys/types.h>".} = int64
+    ## used for file block counts
+  Blksize* {.importc: "blksize_t", header: "<sys/types.h>".} = int32
+    ## used for block sizes
+  Clock* {.importc: "clock_t", header: "<time.h>".} = int32
+  ClockId* {.importc: "clockid_t", header: "<sys/types.h>".} = int32
+  Dev* {.importc: "dev_t", header: "<sys/types.h>".} = int32
+  Fsblkcnt* {.importc: "fsblkcnt_t", header: "<sys/types.h>".} = int64
+  Fsfilcnt* {.importc: "fsfilcnt_t", header: "<sys/types.h>".} = int64
+  Gid* {.importc: "gid_t", header: "<sys/types.h>".} = uint32
+  Id* {.importc: "id_t", header: "<sys/types.h>".} = int32
+  Ino* {.importc: "ino_t", header: "<sys/types.h>".} = int64
+  Key* {.importc: "key_t", header: "<sys/types.h>".} = int32
+  Mode* {.importc: "mode_t", header: "<sys/types.h>".} = (
+    when defined(android) or defined(macos) or defined(macosx) or
+        (defined(bsd) and not defined(openbsd) and not defined(netbsd)):
+      uint16
+    else:
+      uint32
+  )
+  Nlink* {.importc: "nlink_t", header: "<sys/types.h>".} = int32
+  Off* {.importc: "off_t", header: "<sys/types.h>".} = int64
+  Pid* {.importc: "pid_t", header: "<sys/types.h>".} = int32
+  Pthread_attr* {.importc: "pthread_attr_t", header: "<sys/types.h>".} = object
+  Pthread_barrier* {.importc: "pthread_barrier_t",
+                      header: "<sys/types.h>".} = object
+  Pthread_barrierattr* {.importc: "pthread_barrierattr_t",
+                          header: "<sys/types.h>".} = object
+  Pthread_cond* {.importc: "pthread_cond_t", header: "<sys/types.h>".} = object
+  Pthread_condattr* {.importc: "pthread_condattr_t",
+                       header: "<sys/types.h>".} = object
+  Pthread_key* {.importc: "pthread_key_t", header: "<sys/types.h>".} = object
+  Pthread_mutex* {.importc: "pthread_mutex_t", header: "<sys/types.h>".} = object
+  Pthread_mutexattr* {.importc: "pthread_mutexattr_t",
+                        header: "<sys/types.h>".} = object
+  Pthread_once* {.importc: "pthread_once_t", header: "<sys/types.h>".} = object
+  Pthread_rwlock* {.importc: "pthread_rwlock_t",
+                     header: "<sys/types.h>".} = object
+  Pthread_rwlockattr* {.importc: "pthread_rwlockattr_t",
+                         header: "<sys/types.h>".} = object
+  Pthread_spinlock* {.importc: "pthread_spinlock_t",
+                       header: "<sys/types.h>".} = object
+  Pthread* {.importc: "pthread_t", header: "<sys/types.h>".} = object
+  Suseconds* {.importc: "suseconds_t", header: "<sys/types.h>".} = int32
+  #Ttime* {.importc: "time_t", header: "<sys/types.h>".} = int
+  Timer* {.importc: "timer_t", header: "<sys/types.h>".} = object
+  Uid* {.importc: "uid_t", header: "<sys/types.h>".} = uint32
+  Useconds* {.importc: "useconds_t", header: "<sys/types.h>".} = uint32
+
+  Utsname* {.importc: "struct utsname",
+              header: "<sys/utsname.h>",
+              final, pure.} = object ## struct utsname
+    sysname*,        ## Name of this implementation of the operating system.
+      nodename*,     ## Name of this node within the communications
+                     ## network to which this node is attached, if any.
+      release*,      ## Current release level of this implementation.
+      version*,      ## Current version level of this release.
+      machine*: array[32, char] ## Name of the hardware type on which the
+                                ## system is running.
+
+  Sem* {.importc: "sem_t", header: "<semaphore.h>", final, pure.} = object
+  Ipc_perm* {.importc: "struct ipc_perm",
+               header: "<sys/ipc.h>", final, pure.} = object ## struct ipc_perm
+    uid*: Uid    ## Owner's user ID.
+    gid*: Gid    ## Owner's group ID.
+    cuid*: Uid   ## Creator's user ID.
+    cgid*: Gid   ## Creator's group ID.
+    mode*: Mode  ## Read/write permission.
+
+  Stat* {.importc: "struct stat",
+           header: "<sys/stat.h>", final, pure.} = object ## struct stat
+    st_dev*: Dev          ## Device ID of device containing file.
+    st_ino*: Ino          ## File serial number.
+    st_mode*: Mode        ## Mode of file (see below).
+    st_nlink*: Nlink      ## Number of hard links to the file.
+    st_uid*: Uid          ## User ID of file.
+    st_gid*: Gid          ## Group ID of file.
+    st_rdev*: Dev         ## Device ID (if file is character or block special).
+    st_size*: Off         ## For regular files, the file size in bytes.
+                          ## For symbolic links, the length in bytes of the
+                          ## pathname contained in the symbolic link.
+                          ## For a shared memory object, the length in bytes.
+                          ## For a typed memory object, the length in bytes.
+                          ## For other file types, the use of this field is
+                          ## unspecified.
+    when StatHasNanoseconds:
+      st_atim*: Timespec  ## Time of last access.
+      st_mtim*: Timespec  ## Time of last data modification.
+      st_ctim*: Timespec  ## Time of last status change.
+    else:
+      st_atime*: Time     ## Time of last access.
+      st_mtime*: Time     ## Time of last data modification.
+      st_ctime*: Time     ## Time of last status change.
+    st_blksize*: Blksize  ## A file system-specific preferred I/O block size
+                          ## for this object. In some file system types, this
+                          ## may vary from file to file.
+    st_blocks*: Blkcnt    ## Number of blocks allocated for this object.
+
+
+  Statvfs* {.importc: "struct statvfs", header: "<sys/statvfs.h>",
+              final, pure.} = object ## struct statvfs
+    f_bsize*: culong        ## File system block size.
+    f_frsize*: culong       ## Fundamental file system block size.
+    f_blocks*: Fsblkcnt     ## Total number of blocks on file system
+                            ## in units of f_frsize.
+    f_bfree*: Fsblkcnt      ## Total number of free blocks.
+    f_bavail*: Fsblkcnt     ## Number of free blocks available to
+                            ## non-privileged process.
+    f_files*: Fsfilcnt      ## Total number of file serial numbers.
+    f_ffree*: Fsfilcnt      ## Total number of free file serial numbers.
+    f_favail*: Fsfilcnt     ## Number of file serial numbers available to
+                            ## non-privileged process.
+    f_fsid*: culong         ## File system ID.
+    f_flag*: culong         ## Bit mask of f_flag values.
+    f_namemax*: culong      ## Maximum filename length.
+
+  Tm* {.importc: "struct tm", header: "<time.h>",
+         final, pure.} = object ## struct tm
+    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.
+  Itimerspec* {.importc: "struct itimerspec", header: "<time.h>",
+                 final, pure.} = object ## struct itimerspec
+    it_interval*: Timespec  ## Timer period.
+    it_value*: Timespec     ## Timer expiration.
+
+  Sig_atomic* {.importc: "sig_atomic_t", header: "<signal.h>".} = cint
+    ## Possibly volatile-qualified integer type of an object that can be
+    ## accessed as an atomic entity, even in the presence of asynchronous
+    ## interrupts.
+  Sigset* {.importc: "sigset_t", header: "<signal.h>".} = uint64
+
+  SigEvent* {.importc: "struct sigevent",
+               header: "<signal.h>", final, pure.} = object ## struct sigevent
+    sigev_notify*: cint           ## Notification type.
+    sigev_signo*: cint            ## Signal number.
+    sigev_value*: SigVal          ## Signal value.
+    sigev_notify_function*: proc (x: SigVal) {.noconv.} ## Notification func.
+    sigev_notify_attributes*: ptr Pthread_attr ## Notification attributes.
+
+  SigVal* {.importc: "union sigval",
+             header: "<signal.h>", final, pure.} = object ## struct sigval
+    sival_ptr*: pointer ## pointer signal value;
+                        ## integer signal value not defined!
+  Sigaction* {.importc: "struct sigaction",
+                header: "<signal.h>", final, pure.} = object ## struct sigaction
+    sa_handler*: proc (x: cint) {.noconv.}  ## Pointer to a signal-catching
+                                            ## function or one of the macros
+                                            ## SIG_IGN or SIG_DFL.
+    sa_mask*: Sigset ## Set of signals to be blocked during execution of
+                      ## the signal handling function.
+    sa_flags*: cint   ## Special flags.
+    sa_sigaction*: proc (x: cint, y: ptr SigInfo, z: pointer) {.noconv.}
+
+  Stack* {.importc: "stack_t",
+            header: "<signal.h>", final, pure.} = object ## stack_t
+    ss_sp*: pointer  ## Stack base or pointer.
+    ss_size*: csize_t ## Stack size.
+    ss_flags*: cint  ## Flags.
+
+  SigInfo* {.importc: "siginfo_t",
+              header: "<signal.h>", final, pure.} = object ## siginfo_t
+    si_signo*: cint    ## Signal number.
+    si_code*: cint     ## Signal code.
+    si_errno*: cint    ## If non-zero, an errno value associated with
+                       ## this signal, as defined in <errno.h>.
+    si_pid*: Pid       ## Sending process ID.
+    si_uid*: Uid       ## Real user ID of sending process.
+    si_addr*: pointer  ## Address of faulting instruction.
+    si_status*: cint   ## Exit value or signal.
+    si_band*: int      ## Band event for SIGPOLL.
+    si_value*: SigVal  ## Signal value.
+
+  Nl_item* {.importc: "nl_item", header: "<nl_types.h>".} = cint
+  Nl_catd* {.importc: "nl_catd", header: "<nl_types.h>".} = cint
+
+  Sched_param* {.importc: "struct sched_param",
+                  header: "<sched.h>",
+                  final, pure.} = object ## struct sched_param
+    sched_priority*: cint
+
+  Timeval* {.importc: "struct timeval", header: "<sys/select.h>",
+             final, pure.} = object ## struct timeval
+    tv_sec*: Time ## Seconds.
+    tv_usec*: Suseconds ## Microseconds.
+  TFdSet* {.importc: "fd_set", header: "<sys/select.h>",
+           final, pure.} = object
+  Mcontext* {.importc: "mcontext_t", header: "<ucontext.h>",
+               final, pure.} = object
+  Ucontext* {.importc: "ucontext_t", header: "<ucontext.h>",
+               final, pure.} = object ## ucontext_t
+    uc_link*: ptr Ucontext  ## Pointer to the context that is resumed
+                            ## when this context returns.
+    uc_sigmask*: Sigset     ## The set of signals that are blocked when this
+                            ## context is active.
+    uc_stack*: Stack        ## The stack used by this context.
+    uc_mcontext*: Mcontext  ## A machine-specific representation of the saved
+                            ## context.
+
+when hasAioH:
+  type
+    Taiocb* {.importc: "struct aiocb", header: "<aio.h>",
+              final, pure.} = object ## struct aiocb
+      aio_fildes*: cint         ## File descriptor.
+      aio_offset*: Off          ## File offset.
+      aio_buf*: pointer         ## Location of buffer.
+      aio_nbytes*: int          ## Length of transfer.
+      aio_reqprio*: cint        ## Request priority offset.
+      aio_sigevent*: SigEvent   ## Signal number and value.
+      aio_lio_opcode: cint      ## Operation to be performed.
+
+when hasSpawnH:
+  type
+    Tposix_spawnattr* {.importc: "posix_spawnattr_t",
+                        header: "<spawn.h>", final, pure.} = object
+    Tposix_spawn_file_actions* {.importc: "posix_spawn_file_actions_t",
+                                 header: "<spawn.h>", final, pure.} = object
+
+when defined(linux):
+  # from sys/un.h
+  const Sockaddr_un_path_length* = 108
+elif defined(haiku):
+  # from sys/un.h
+  const Sockaddr_un_path_length* = 126
+else:
+  # according to http://pubs.opengroup.org/onlinepubs/009604499/basedefs/sys/un.h.html
+  # this is >=92
+  const Sockaddr_un_path_length* = 92
+
+type
+  SockLen* {.importc: "socklen_t", header: "<sys/socket.h>".} = uint32
+  TSa_Family* {.importc: "sa_family_t", header: "<sys/socket.h>".} = uint8
+
+  SockAddr* {.importc: "struct sockaddr", header: "<sys/socket.h>",
+              pure, final.} = object ## struct sockaddr
+    sa_family*: TSa_Family         ## Address family.
+    sa_data*: array[0..255, char] ## Socket address (variable-length data).
+
+  Sockaddr_un* {.importc: "struct sockaddr_un", header: "<sys/un.h>",
+              pure, final.} = object ## struct sockaddr_un
+    sun_family*: TSa_Family         ## Address family.
+    sun_path*: array[0..Sockaddr_un_path_length-1, char] ## Socket path
+
+  Sockaddr_storage* {.importc: "struct sockaddr_storage",
+                       header: "<sys/socket.h>",
+                       pure, final.} = object ## struct sockaddr_storage
+    ss_family*: TSa_Family ## Address family.
+
+  Tif_nameindex* {.importc: "struct if_nameindex", final,
+                   pure, header: "<net/if.h>".} = object ## struct if_nameindex
+    if_index*: cuint   ## Numeric index of the interface.
+    if_name*: cstring ## Null-terminated name of the interface.
+
+
+  IOVec* {.importc: "struct iovec", pure, final,
+            header: "<sys/uio.h>".} = object ## struct iovec
+    iov_base*: pointer ## Base address of a memory region for input or output.
+    iov_len*: csize_t  ## The size of the memory pointed to by iov_base.
+
+  Tmsghdr* {.importc: "struct msghdr", pure, final,
+             header: "<sys/socket.h>".} = object  ## struct msghdr
+    msg_name*: pointer     ## Optional address.
+    msg_namelen*: SockLen  ## Size of address.
+    msg_iov*: ptr IOVec    ## Scatter/gather array.
+    msg_iovlen*: cint      ## Members in msg_iov.
+    msg_control*: pointer  ## Ancillary data; see below.
+    msg_controllen*: SockLen ## Ancillary data buffer len.
+    msg_flags*: cint ## Flags on received message.
+
+
+  Tcmsghdr* {.importc: "struct cmsghdr", pure, final,
+              header: "<sys/socket.h>".} = object ## struct cmsghdr
+    cmsg_len*: SockLen ## Data byte count, including the cmsghdr.
+    cmsg_level*: cint   ## Originating protocol.
+    cmsg_type*: cint    ## Protocol-specific type.
+
+  TLinger* {.importc: "struct linger", pure, final,
+             header: "<sys/socket.h>".} = object ## struct linger
+    l_onoff*: cint  ## Indicates whether linger option is enabled.
+    l_linger*: cint ## Linger time, in seconds.
+
+  InPort* = uint16
+  InAddrScalar* = uint32
+
+  InAddrT* {.importc: "in_addr_t", pure, final,
+             header: "<netinet/in.h>".} = uint32
+
+  InAddr* {.importc: "struct in_addr", pure, final,
+             header: "<netinet/in.h>".} = object ## struct in_addr
+    s_addr*: InAddrScalar
+
+  Sockaddr_in* {.importc: "struct sockaddr_in", pure, final,
+                  header: "<netinet/in.h>".} = object ## struct sockaddr_in
+    sin_family*: TSa_Family ## AF_INET.
+    sin_port*: InPort      ## Port number.
+    sin_addr*: InAddr      ## IP address.
+
+  In6Addr* {.importc: "struct in6_addr", pure, final,
+              header: "<netinet/in.h>".} = object ## struct in6_addr
+    s6_addr*: array[0..15, char]
+
+  Sockaddr_in6* {.importc: "struct sockaddr_in6", pure, final,
+                   header: "<netinet/in.h>".} = object ## struct sockaddr_in6
+    sin6_family*: TSa_Family ## AF_INET6.
+    sin6_port*: InPort      ## Port number.
+    sin6_flowinfo*: int32    ## IPv6 traffic class and flow information.
+    sin6_addr*: In6Addr     ## IPv6 address.
+    sin6_scope_id*: int32    ## Set of interfaces for a scope.
+
+  Tipv6_mreq* {.importc: "struct ipv6_mreq", pure, final,
+                header: "<netinet/in.h>".} = object ## struct ipv6_mreq
+    ipv6mr_multiaddr*: In6Addr ## IPv6 multicast address.
+    ipv6mr_interface*: cuint     ## Interface index.
+
+  Hostent* {.importc: "struct hostent", pure, final,
+              header: "<netdb.h>".} = object ## struct hostent
+    h_name*: cstring           ## Official name of the host.
+    h_aliases*: cstringArray   ## A pointer to an array of pointers to
+                               ## alternative host names, terminated by a
+                               ## null pointer.
+    h_addrtype*: cint          ## Address type.
+    h_length*: cint            ## The length, in bytes, of the address.
+    h_addr_list*: cstringArray ## A pointer to an array of pointers to network
+                               ## addresses (in network byte order) for the
+                               ## host, terminated by a null pointer.
+
+  Tnetent* {.importc: "struct netent", pure, final,
+              header: "<netdb.h>".} = object ## struct netent
+    n_name*: cstring         ## Official, fully-qualified (including the
+                             ## domain) name of the host.
+    n_aliases*: cstringArray ## A pointer to an array of pointers to
+                             ## alternative network names, terminated by a
+                             ## null pointer.
+    n_addrtype*: cint        ## The address type of the network.
+    n_net*: InAddrT          ## The network number, in host byte order.
+
+  Protoent* {.importc: "struct protoent", pure, final,
+              header: "<netdb.h>".} = object ## struct protoent
+    p_name*: cstring         ## Official name of the protocol.
+    p_aliases*: cstringArray ## A pointer to an array of pointers to
+                             ## alternative protocol names, terminated by
+                             ## a null pointer.
+    p_proto*: cint           ## The protocol number.
+
+  Servent* {.importc: "struct servent", pure, final,
+             header: "<netdb.h>".} = object ## struct servent
+    s_name*: cstring         ## Official name of the service.
+    s_aliases*: cstringArray ## A pointer to an array of pointers to
+                             ## alternative service names, terminated by
+                             ## a null pointer.
+    s_port*: cint            ## The port number at which the service
+                             ## resides, in network byte order.
+    s_proto*: cstring        ## The name of the protocol to use when
+                             ## contacting the service.
+
+  AddrInfo* {.importc: "struct addrinfo", pure, final,
+              header: "<netdb.h>".} = object ## struct addrinfo
+    ai_flags*: cint         ## Input flags.
+    ai_family*: cint        ## Address family of socket.
+    ai_socktype*: cint      ## Socket type.
+    ai_protocol*: cint      ## Protocol of socket.
+    ai_addrlen*: SockLen   ## Length of socket address.
+    ai_addr*: ptr SockAddr ## Socket address of socket.
+    ai_canonname*: cstring  ## Canonical name of service location.
+    ai_next*: ptr AddrInfo ## Pointer to next in list.
+
+  TPollfd* {.importc: "struct pollfd", pure, final,
+             header: "<poll.h>".} = object ## struct pollfd
+    fd*: cint        ## The following descriptor being polled.
+    events*: cshort  ## The input event flags (see below).
+    revents*: cshort ## The output event flags (see below).
+
+  Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = culong
+
+var
+  errno* {.importc, header: "<errno.h>".}: cint ## error variable
+  h_errno* {.importc, header: "<netdb.h>".}: cint
+  daylight* {.importc, header: "<time.h>".}: cint
+  timezone* {.importc, header: "<time.h>".}: int
+
+# Regenerate using detect.nim!
+include posix_other_consts
+
+when defined(linux):
+  var
+    MAP_POPULATE* {.importc, header: "<sys/mman.h>".}: cint
+      ## Populate (prefault) page tables for a mapping.
+else:
+  var
+    MAP_POPULATE*: cint = 0
+
+when defined(linux) or defined(nimdoc):
+  when defined(alpha) or defined(mips) or defined(mipsel) or
+      defined(mips64) or defined(mips64el) or defined(parisc) or
+      defined(sparc) or defined(sparc64) or defined(nimdoc):
+    const SO_REUSEPORT* = cint(0x0200)
+      ## Multiple binding: load balancing on incoming TCP connections
+      ## or UDP packets. (Requires Linux kernel > 3.9)
+  else:
+    const SO_REUSEPORT* = cint(15)
+else:
+  var SO_REUSEPORT* {.importc, header: "<sys/socket.h>".}: cint
+
+when defined(macosx):
+  # We can't use the NOSIGNAL flag in the `send` function, it has no effect
+  # Instead we should use SO_NOSIGPIPE in setsockopt
+  const
+    MSG_NOSIGNAL* = 0'i32
+  var
+    SO_NOSIGPIPE* {.importc, header: "<sys/socket.h>".}: cint
+elif defined(solaris):
+  # Solaris doesn't have MSG_NOSIGNAL
+  const
+    MSG_NOSIGNAL* = 0'i32
+else:
+  var
+    MSG_NOSIGNAL* {.importc, header: "<sys/socket.h>".}: cint
+      ## No SIGPIPE generated when an attempt to send is made on a stream-oriented socket that is no longer connected.
+
+when defined(haiku):
+  const
+    SIGKILLTHR* = 21 ## BeOS specific: Kill just the thread, not team
+
+when hasSpawnH:
+  when defined(linux):
+    # better be safe than sorry; Linux has this flag, macosx doesn't, don't
+    # know about the other OSes
+
+    # Non-GNU systems like TCC and musl-libc  don't define __USE_GNU, so we
+    # can't get the magic number from spawn.h
+    const POSIX_SPAWN_USEVFORK* = cint(0x40)
+  else:
+    # macosx lacks this, so we define the constant to be 0 to not affect
+    # OR'ing of flags:
+    const POSIX_SPAWN_USEVFORK* = cint(0)
+
+# <sys/wait.h>
+proc WEXITSTATUS*(s: cint): cint {.importc, header: "<sys/wait.h>".}
+  ## Exit code, if WIFEXITED(s)
+proc WTERMSIG*(s: cint): cint {.importc, header: "<sys/wait.h>".}
+  ## Termination signal, if WIFSIGNALED(s)
+proc WSTOPSIG*(s: cint): cint {.importc, header: "<sys/wait.h>".}
+  ## Stop signal, if WIFSTOPPED(s)
+proc WIFEXITED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
+  ## True if child exited normally.
+proc WIFSIGNALED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
+  ## True if child exited due to uncaught signal.
+proc WIFSTOPPED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
+  ## True if child is currently stopped.
+proc WIFCONTINUED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
+  ## True if child has been continued.
+
+when defined(nimHasStyleChecks):
+  {.pop.}
diff --git a/lib/posix/posix_linux_amd64.nim b/lib/posix/posix_linux_amd64.nim
index 4f114d394..8d11c507d 100644
--- a/lib/posix/posix_linux_amd64.nim
+++ b/lib/posix/posix_linux_amd64.nim
@@ -19,7 +19,10 @@ const
 # On Linux:
 # timer_{create,delete,settime,gettime},
 # clock_{getcpuclockid, getres, gettime, nanosleep, settime} lives in librt
-{.passL: "-lrt".}
+{.passl: "-lrt".}
+
+when defined(nimHasStyleChecks):
+  {.push styleChecks: off.}
 
 # Types
 
@@ -27,16 +30,10 @@ type
   DIR* {.importc: "DIR", header: "<dirent.h>",
           incompleteStruct.} = object
     ## A type representing a directory stream.
-{.deprecated: [TDIR: DIR].}
 
 type
   SocketHandle* = distinct cint # The type used to represent socket descriptors
 
-{.deprecated: [TSocketHandle: SocketHandle].}
-
-# not detected by detect.nim, guarded by #ifdef __USE_UNIX98 in glibc
-const SIG_HOLD* = cast[SigHandler](2)
-
 type
   Time* {.importc: "time_t", header: "<time.h>".} = distinct clong
 
@@ -50,7 +47,7 @@ type
     d_ino*: Ino
     d_off*: Off
     d_reclen*: cushort
-    d_type*: int8  # cuchar really!
+    d_type*: int8  # uint8 really!
     d_name*: array[256, cchar]
 
   Tflock* {.importc: "struct flock", final, pure,
@@ -66,9 +63,9 @@ type
 
   Glob* {.importc: "glob_t", header: "<glob.h>",
            final, pure.} = object ## glob_t
-    gl_pathc*: csize          ## Count of paths matched by pattern.
-    gl_pathv*: cstringArray ## Pointer to a list of matched pathnames.
-    gl_offs*: csize           ## Slots to reserve at the beginning of gl_pathv.
+    gl_pathc*: csize_t            ## Count of paths matched by pattern.
+    gl_pathv*: cstringArray       ## Pointer to a list of matched pathnames.
+    gl_offs*: csize_t             ## Slots to reserve at the beginning of gl_pathv.
     gl_flags*: cint
     gl_closedir*: pointer
     gl_readdir*: pointer
@@ -147,7 +144,7 @@ type
   Id* {.importc: "id_t", header: "<sys/types.h>".} = cuint
   Ino* {.importc: "ino_t", header: "<sys/types.h>".} = culong
   Key* {.importc: "key_t", header: "<sys/types.h>".} = cint
-  Mode* {.importc: "mode_t", header: "<sys/types.h>".} = cint # cuint really!
+  Mode* {.importc: "mode_t", header: "<sys/types.h>".} = uint32
   Nlink* {.importc: "nlink_t", header: "<sys/types.h>".} = culong
   Off* {.importc: "off_t", header: "<sys/types.h>".} = clong
   Pid* {.importc: "pid_t", header: "<sys/types.h>".} = cint
@@ -171,7 +168,7 @@ type
   Pthread_key* {.importc: "pthread_key_t", header: "<sys/types.h>".} = cuint
   Pthread_mutex* {.importc: "pthread_mutex_t", header: "<sys/types.h>",
                    pure, final.} = object
-    abi: array[48 div sizeof(clong), clong]
+    abi: array[40 div sizeof(clong), clong]
   Pthread_mutexattr* {.importc: "pthread_mutexattr_t",
                         header: "<sys/types.h>", pure, final.} = object
     abi: array[4 div sizeof(cint), cint]
@@ -228,7 +225,7 @@ type
     st_mode*: Mode        ## Mode of file (see below).
     st_uid*: Uid          ## User ID of file.
     st_gid*: Gid          ## Group ID of file.
-    pad0: cint
+    pad0 {.importc: "__pad0".}: cint
     st_rdev*: Dev         ## Device ID (if file is character or block special).
     st_size*: Off         ## For regular files, the file size in bytes.
                            ## For symbolic links, the length in bytes of the
@@ -244,8 +241,6 @@ type
     st_atim*: Timespec   ## Time of last access.
     st_mtim*: Timespec   ## Time of last data modification.
     st_ctim*: Timespec   ## Time of last status change.
-    reserved: array[3, clong]
-
 
   Statvfs* {.importc: "struct statvfs", header: "<sys/statvfs.h>",
               final, pure.} = object ## struct statvfs
@@ -299,7 +294,7 @@ type
     sigev_signo*: cint            ## Signal number.
     sigev_notify*: cint           ## Notification type.
     sigev_notify_function*: proc (x: SigVal) {.noconv.} ## Notification func.
-    sigev_notify_attributes*: ptr PthreadAttr ## Notification attributes.
+    sigev_notify_attributes*: ptr Pthread_attr ## Notification attributes.
     abi: array[12, int]
 
   SigVal* {.importc: "union sigval",
@@ -314,7 +309,7 @@ type
     sa_mask*: Sigset ## Set of signals to be blocked during execution of
                       ## the signal handling function.
     sa_flags*: cint   ## Special flags.
-    sa_sigaction*: proc (x: cint, y: ptr SigInfo, z: pointer) {.noconv.}
+    sa_restorer: proc() {.noconv.}   ## not intended for application use.
 
   Stack* {.importc: "stack_t",
             header: "<signal.h>", final, pure.} = object ## stack_t
@@ -330,17 +325,23 @@ type
   SigInfo* {.importc: "siginfo_t",
               header: "<signal.h>", final, pure.} = object ## siginfo_t
     si_signo*: cint    ## Signal number.
-    si_code*: cint     ## Signal code.
     si_errno*: cint    ## If non-zero, an errno value associated with
                        ## this signal, as defined in <errno.h>.
+    si_code*: cint     ## Signal code.
     si_pid*: Pid       ## Sending process ID.
     si_uid*: Uid       ## Real user ID of sending process.
     si_addr*: pointer  ## Address of faulting instruction.
     si_status*: cint   ## Exit value or signal.
     si_band*: int      ## Band event for SIGPOLL.
     si_value*: SigVal  ## Signal value.
-    pad {.importc: "_pad"}: array[128 - 56, uint8]
+    pad {.importc: "_pad".}: array[128 - 56, uint8]
 
+template sa_sigaction*(v: Sigaction): proc (x: cint, y: ptr SigInfo, z: pointer) {.noconv.} =
+  cast[proc (x: cint, y: ptr SigInfo, z: pointer) {.noconv.}](v.sa_handler)
+proc `sa_sigaction=`*(v: var Sigaction, x: proc (x: cint, y: ptr SigInfo, z: pointer) {.noconv.}) =
+  v.sa_handler = cast[proc (x: cint) {.noconv.}](x)
+
+type
   Nl_item* {.importc: "nl_item", header: "<nl_types.h>".} = cint
   Nl_catd* {.importc: "nl_catd", header: "<nl_types.h>".} = pointer
 
@@ -375,29 +376,6 @@ type
                             ## context is active.
     # todo fpregds_mem
 
-{.deprecated: [TOff: Off, TPid: Pid, TGid: Gid, TMode: Mode, TDev: Dev,
-              TNlink: Nlink, TStack: Stack, TGroup: Group, TMqd: Mqd,
-              TPasswd: Passwd, TClock: Clock, TClockId: ClockId, TKey: Key,
-              TSem: Sem, Tpthread_attr: PthreadAttr, Ttimespec: Timespec,
-              Tdirent: Dirent, TGlob: Glob,
-              # Tflock: Flock, # Naming conflict if we drop the `T`
-              Ticonv: Iconv, Tlconv: Lconv, TMqAttr: MqAttr, Tblkcnt: Blkcnt,
-              Tblksize: Blksize, Tfsblkcnt: Fsblkcnt, Tfsfilcnt: Fsfilcnt,
-              Tid: Id, Tino: Ino, Tpthread_barrier: Pthread_barrier,
-              Tpthread_barrierattr: Pthread_barrierattr, Tpthread_cond: Pthread_cond,
-              TPthread_condattr: Pthread_condattr, Tpthread_key: Pthread_key,
-              Tpthread_mutex: Pthread_mutex, Tpthread_mutexattr: Pthread_mutexattr,
-              Tpthread_once: Pthread_once, Tpthread_rwlock: Pthread_rwlock,
-              Tpthread_rwlockattr: Pthread_rwlockattr, Tpthread_spinlock: Pthread_spinlock,
-              Tpthread: Pthread, Tsuseconds: Suseconds, Ttimer: Timer,
-              Tuid: Uid, Tuseconds: Useconds, Tutsname: Utsname, Tipc_perm: Ipc_perm,
-              TStat: Stat, TStatvfs: Statvfs,
-              Ttm: Tm, titimerspec: Itimerspec, Tsig_atomic: Sig_atomic, Tsigset: Sigset,
-              TsigEvent: SigEvent, TsigVal: SigVal, TSigaction: Sigaction,
-              TSigStack: SigStack, TsigInfo: SigInfo, Tnl_item: Nl_item,
-              Tnl_catd: Nl_catd, Tsched_param: Sched_param,
-              # TFdSet: FdSet, # Naming conflict if we drop the `T`
-              Tmcontext: Mcontext, Tucontext: Ucontext].}
 type
   Taiocb* {.importc: "struct aiocb", header: "<aio.h>",
             final, pure.} = object ## struct aiocb
@@ -405,7 +383,7 @@ type
     aio_lio_opcode*: cint     ## Operation to be performed.
     aio_reqprio*: cint        ## Request priority offset.
     aio_buf*: pointer         ## Location of buffer.
-    aio_nbytes*: csize        ## Length of transfer.
+    aio_nbytes*: csize_t        ## Length of transfer.
     aio_sigevent*: SigEvent   ## Signal number and value.
     next_prio: pointer
     abs_prio: cint
@@ -439,9 +417,8 @@ when hasSpawnH:
 const Sockaddr_un_path_length* = 108
 
 type
-  Socklen* {.importc: "socklen_t", header: "<sys/socket.h>".} = cuint
-  # cushort really
-  TSa_Family* {.importc: "sa_family_t", header: "<sys/socket.h>".} = cshort
+  SockLen* {.importc: "socklen_t", header: "<sys/socket.h>".} = cuint
+  TSa_Family* {.importc: "sa_family_t", header: "<sys/socket.h>".} = cushort
 
   SockAddr* {.importc: "struct sockaddr", header: "<sys/socket.h>",
               pure, final.} = object ## struct sockaddr
@@ -457,8 +434,8 @@ type
                        header: "<sys/socket.h>",
                        pure, final.} = object ## struct sockaddr_storage
     ss_family*: TSa_Family ## Address family.
-    ss_padding: array[128 - sizeof(cshort) - sizeof(culong), char]
-    ss_align: clong
+    ss_padding {.importc: "__ss_padding".}: array[128 - sizeof(cshort) - sizeof(culong), char]
+    ss_align {.importc: "__ss_align".}: clong
 
   Tif_nameindex* {.importc: "struct if_nameindex", final,
                    pure, header: "<net/if.h>".} = object ## struct if_nameindex
@@ -469,22 +446,22 @@ type
   IOVec* {.importc: "struct iovec", pure, final,
             header: "<sys/uio.h>".} = object ## struct iovec
     iov_base*: pointer ## Base address of a memory region for input or output.
-    iov_len*: csize    ## The size of the memory pointed to by iov_base.
+    iov_len*: csize_t    ## The size of the memory pointed to by iov_base.
 
   Tmsghdr* {.importc: "struct msghdr", pure, final,
              header: "<sys/socket.h>".} = object  ## struct msghdr
     msg_name*: pointer  ## Optional address.
-    msg_namelen*: Socklen  ## Size of address.
+    msg_namelen*: SockLen  ## Size of address.
     msg_iov*: ptr IOVec    ## Scatter/gather array.
-    msg_iovlen*: csize   ## Members in msg_iov.
+    msg_iovlen*: csize_t   ## Members in msg_iov.
     msg_control*: pointer  ## Ancillary data; see below.
-    msg_controllen*: csize ## Ancillary data buffer len.
+    msg_controllen*: csize_t ## Ancillary data buffer len.
     msg_flags*: cint ## Flags on received message.
 
 
   Tcmsghdr* {.importc: "struct cmsghdr", pure, final,
               header: "<sys/socket.h>".} = object ## struct cmsghdr
-    cmsg_len*: csize ## Data byte count, including the cmsghdr.
+    cmsg_len*: csize_t ## Data byte count, including the cmsghdr.
     cmsg_level*: cint   ## Originating protocol.
     cmsg_type*: cint    ## Protocol-specific type.
 
@@ -575,7 +552,7 @@ type
     ai_family*: cint        ## Address family of socket.
     ai_socktype*: cint      ## Socket type.
     ai_protocol*: cint      ## Protocol of socket.
-    ai_addrlen*: Socklen   ## Length of socket address.
+    ai_addrlen*: SockLen   ## Length of socket address.
     ai_addr*: ptr SockAddr ## Socket address of socket.
     ai_canonname*: cstring  ## Canonical name of service location.
     ai_next*: ptr AddrInfo ## Pointer to next in list.
@@ -588,13 +565,6 @@ type
 
   Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = culong
 
-{.deprecated: [TSockaddr_in: Sockaddr_in, TAddrinfo: AddrInfo,
-    TSockAddr: SockAddr, TSockLen: SockLen, TTimeval: Timeval,
-    Tsockaddr_storage: Sockaddr_storage, Tsockaddr_in6: Sockaddr_in6,
-    Thostent: Hostent, TServent: Servent,
-    TInAddr: InAddr, TIOVec: IOVec, TInPort: InPort, TInAddrT: InAddrT,
-    TIn6Addr: In6Addr, TInAddrScalar: InAddrScalar, TProtoent: Protoent].}
-
 var
   errno* {.importc, header: "<errno.h>".}: cint ## error variable
   h_errno* {.importc, header: "<netdb.h>".}: cint
@@ -604,8 +574,6 @@ var
 # Regenerate using detect.nim!
 include posix_linux_amd64_consts
 
-const POSIX_SPAWN_USEVFORK* = cint(0x40)  # needs _GNU_SOURCE!
-
 # <sys/wait.h>
 proc WEXITSTATUS*(s: cint): cint =  (s and 0xff00) shr 8
 proc WTERMSIG*(s:cint): cint = s and 0x7f
@@ -613,4 +581,7 @@ proc WSTOPSIG*(s:cint): cint = WEXITSTATUS(s)
 proc WIFEXITED*(s:cint) : bool = WTERMSIG(s) == 0
 proc WIFSIGNALED*(s:cint) : bool = (cast[int8]((s and 0x7f) + 1) shr 1) > 0
 proc WIFSTOPPED*(s:cint) : bool = (s and 0xff) == 0x7f
-proc WIFCONTINUED*(s:cint) : bool = s == W_CONTINUED
+proc WIFCONTINUED*(s:cint) : bool = s == WCONTINUED
+
+when defined(nimHasStyleChecks):
+  {.pop.} # {.push styleChecks: off.}
diff --git a/lib/posix/posix_linux_amd64_consts.nim b/lib/posix/posix_linux_amd64_consts.nim
index ee4fac1e8..fbe8d0666 100644
--- a/lib/posix/posix_linux_amd64_consts.nim
+++ b/lib/posix/posix_linux_amd64_consts.nim
@@ -100,6 +100,7 @@ const EXDEV* = cint(18)
 
 # <fcntl.h>
 const F_DUPFD* = cint(0)
+const F_DUPFD_CLOEXEC* = cint(1030)
 const F_GETFD* = cint(1)
 const F_SETFD* = cint(2)
 const F_GETFL* = cint(3)
@@ -126,6 +127,11 @@ const O_ACCMODE* = cint(3)
 const O_RDONLY* = cint(0)
 const O_RDWR* = cint(2)
 const O_WRONLY* = cint(1)
+const O_CLOEXEC* = cint(524288)
+const O_DIRECT* = cint(16384)
+const O_PATH* = cint(2097152)
+const O_NOATIME* = cint(262144)
+const O_TMPFILE* = cint(4259840)
 const POSIX_FADV_NORMAL* = cint(0)
 const POSIX_FADV_SEQUENTIAL* = cint(2)
 const POSIX_FADV_RANDOM* = cint(1)
@@ -172,13 +178,20 @@ const FNM_NOMATCH* = cint(1)
 const FNM_PATHNAME* = cint(1)
 const FNM_PERIOD* = cint(4)
 const FNM_NOESCAPE* = cint(2)
+const FNM_NOSYS* = cint(-1)
 
 # <ftw.h>
 const FTW_F* = cint(0)
 const FTW_D* = cint(1)
 const FTW_DNR* = cint(2)
+const FTW_DP* = cint(5)
 const FTW_NS* = cint(3)
 const FTW_SL* = cint(4)
+const FTW_SLN* = cint(6)
+const FTW_PHYS* = cint(1)
+const FTW_MOUNT* = cint(2)
+const FTW_DEPTH* = cint(8)
+const FTW_CHDIR* = cint(4)
 
 # <glob.h>
 const GLOB_APPEND* = cint(32)
@@ -295,12 +308,13 @@ 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)
 const INADDR_ANY* = InAddrScalar(0)
 const INADDR_LOOPBACK* = InAddrScalar(2130706433)
-const INADDR_BROADCAST* = InAddrScalar(-1)
+const INADDR_BROADCAST* = InAddrScalar(4294967295)
 const INET_ADDRSTRLEN* = cint(16)
 const INET6_ADDRSTRLEN* = cint(46)
 const IPV6_JOIN_GROUP* = cint(20)
@@ -399,6 +413,7 @@ const SS_ONSTACK* = cint(1)
 const SS_DISABLE* = cint(2)
 const MINSIGSTKSZ* = cint(2048)
 const SIGSTKSZ* = cint(8192)
+const SIG_HOLD* = cast[Sighandler](2)
 const SIG_DFL* = cast[Sighandler](0)
 const SIG_ERR* = cast[Sighandler](-1)
 const SIG_IGN* = cast[Sighandler](1)
@@ -417,6 +432,9 @@ const PROT_READ* = cint(1)
 const PROT_WRITE* = cint(2)
 const PROT_EXEC* = cint(4)
 const PROT_NONE* = cint(0)
+const MAP_ANONYMOUS* = cint(32)
+const MAP_FIXED_NOREPLACE* = cint(1048576)
+const MAP_NORESERVE* = cint(16384)
 const MAP_SHARED* = cint(1)
 const MAP_PRIVATE* = cint(2)
 const MAP_FIXED* = cint(16)
@@ -435,6 +453,7 @@ const MAP_POPULATE* = cint(32768)
 
 # <sys/resource.h>
 const RLIMIT_NOFILE* = cint(7)
+const RLIMIT_STACK* = cint(3)
 
 # <sys/select.h>
 const FD_SETSIZE* = cint(1024)
@@ -446,6 +465,7 @@ const MSG_EOR* = cint(128)
 const MSG_OOB* = cint(1)
 const SCM_RIGHTS* = cint(1)
 const SO_ACCEPTCONN* = cint(30)
+const SO_BINDTODEVICE* = cint(25)
 const SO_BROADCAST* = cint(6)
 const SO_DEBUG* = cint(1)
 const SO_DONTROUTE* = cint(5)
@@ -465,17 +485,18 @@ const SOCK_DGRAM* = cint(2)
 const SOCK_RAW* = cint(3)
 const SOCK_SEQPACKET* = cint(5)
 const SOCK_STREAM* = cint(1)
+const SOCK_CLOEXEC* = cint(524288)
 const SOL_SOCKET* = cint(1)
-const SOMAXCONN* = cint(128)
+const SOMAXCONN* = cint(4096)
 const SO_REUSEPORT* = cint(15)
 const MSG_NOSIGNAL* = cint(16384)
 const MSG_PEEK* = cint(2)
 const MSG_TRUNC* = cint(32)
 const MSG_WAITALL* = cint(256)
-const AF_INET* = TSa_Family(2)
-const AF_INET6* = TSa_Family(10)
-const AF_UNIX* = TSa_Family(1)
-const AF_UNSPEC* = TSa_Family(0)
+const AF_INET* = cint(2)
+const AF_INET6* = cint(10)
+const AF_UNIX* = cint(1)
+const AF_UNSPEC* = cint(0)
 const SHUT_RD* = cint(0)
 const SHUT_RDWR* = cint(2)
 const SHUT_WR* = cint(1)
@@ -524,6 +545,7 @@ const POSIX_SPAWN_SETSCHEDPARAM* = cint(16)
 const POSIX_SPAWN_SETSCHEDULER* = cint(32)
 const POSIX_SPAWN_SETSIGDEF* = cint(4)
 const POSIX_SPAWN_SETSIGMASK* = cint(8)
+const POSIX_SPAWN_USEVFORK* = cint(64)
 
 # <stdio.h>
 const IOFBF* = cint(0)
@@ -633,7 +655,7 @@ const SC_MQ_OPEN_MAX* = cint(27)
 const SC_MQ_PRIO_MAX* = cint(28)
 const SC_NGROUPS_MAX* = cint(3)
 const SC_OPEN_MAX* = cint(4)
-const SC_PAGE_SIZE* = cint(30)
+const SC_PAGESIZE* = cint(30)
 const SC_PRIORITIZED_IO* = cint(13)
 const SC_PRIORITY_SCHEDULING* = cint(10)
 const SC_RAW_SOCKETS* = cint(236)
diff --git a/lib/posix/posix_macos_amd64.nim b/lib/posix/posix_macos_amd64.nim
new file mode 100644
index 000000000..a4b64ed62
--- /dev/null
+++ b/lib/posix/posix_macos_amd64.nim
@@ -0,0 +1,620 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+when defined(nimHasStyleChecks):
+  {.push styleChecks: off.}
+
+const
+  hasSpawnH = true # should exist for every Posix system nowadays
+  hasAioH = false
+
+type
+  DIR* {.importc: "DIR", header: "<dirent.h>",
+          incompleteStruct.} = object
+    ## A type representing a directory stream.
+
+type
+  SocketHandle* = distinct cint # The type used to represent socket descriptors
+
+type
+  Time* {.importc: "time_t", header: "<time.h>".} = distinct clong
+
+  Timespec* {.importc: "struct timespec",
+               header: "<time.h>", final, pure.} = object ## struct timespec
+    tv_sec*: Time  ## Seconds.
+    tv_nsec*: int  ## Nanoseconds.
+
+  Dirent* {.importc: "struct dirent",
+             header: "<dirent.h>", final, pure.} = object ## dirent_t struct
+    when defined(haiku):
+      d_dev*: Dev ## Device (not POSIX)
+      d_pdev*: Dev ## Parent device (only for queries) (not POSIX)
+    d_ino*: Ino  ## File serial number.
+    when defined(dragonfly):
+      # DragonflyBSD doesn't have `d_reclen` field.
+      d_type*: uint8
+    elif defined(linux) or defined(macosx) or defined(freebsd) or
+         defined(netbsd) or defined(openbsd) or defined(genode):
+      d_reclen*: cshort ## Length of this record. (not POSIX)
+      d_type*: int8 ## Type of file; not supported by all filesystem types.
+                    ## (not POSIX)
+      when defined(linux) or defined(openbsd):
+        d_off*: Off  ## Not an offset. Value that `telldir()` would return.
+    elif defined(haiku):
+      d_pino*: Ino ## Parent inode (only for queries) (not POSIX)
+      d_reclen*: cushort ## Length of this record. (not POSIX)
+
+    d_name*: array[0..255, char] ## Name of entry.
+
+  Tflock* {.importc: "struct flock", final, pure,
+            header: "<fcntl.h>".} = object ## flock type
+    l_type*: cshort   ## Type of lock; F_RDLCK, F_WRLCK, F_UNLCK.
+    l_whence*: cshort ## Flag for starting offset.
+    l_start*: Off     ## Relative offset in bytes.
+    l_len*: Off       ## Size; if 0 then until EOF.
+    l_pid*: Pid      ## Process ID of the process holding the lock;
+                      ## returned with F_GETLK.
+
+  FTW* {.importc: "struct FTW", header: "<ftw.h>", final, pure.} = object
+    base*: cint
+    level*: cint
+
+  Glob* {.importc: "glob_t", header: "<glob.h>",
+           final, pure.} = object ## glob_t
+    gl_pathc*: int          ## Count of paths matched by pattern.
+    gl_pathv*: cstringArray ## Pointer to a list of matched pathnames.
+    gl_offs*: int           ## Slots to reserve at the beginning of gl_pathv.
+
+  Group* {.importc: "struct group", header: "<grp.h>",
+            final, pure.} = object ## struct group
+    gr_name*: cstring     ## The name of the group.
+    gr_gid*: Gid         ## Numerical group ID.
+    gr_mem*: cstringArray ## Pointer to a null-terminated array of character
+                          ## pointers to member names.
+
+  Iconv* {.importc: "iconv_t", header: "<iconv.h>", final, pure.} =
+    object ## Identifies the conversion from one codeset to another.
+
+  Lconv* {.importc: "struct lconv", header: "<locale.h>", final,
+            pure.} = object
+    currency_symbol*: cstring
+    decimal_point*: cstring
+    frac_digits*: char
+    grouping*: cstring
+    int_curr_symbol*: cstring
+    int_frac_digits*: char
+    int_n_cs_precedes*: char
+    int_n_sep_by_space*: char
+    int_n_sign_posn*: char
+    int_p_cs_precedes*: char
+    int_p_sep_by_space*: char
+    int_p_sign_posn*: char
+    mon_decimal_point*: cstring
+    mon_grouping*: cstring
+    mon_thousands_sep*: cstring
+    negative_sign*: cstring
+    n_cs_precedes*: char
+    n_sep_by_space*: char
+    n_sign_posn*: char
+    positive_sign*: cstring
+    p_cs_precedes*: char
+    p_sep_by_space*: char
+    p_sign_posn*: char
+    thousands_sep*: cstring
+
+  Passwd* {.importc: "struct passwd", header: "<pwd.h>",
+             final, pure.} = object ## struct passwd
+    pw_name*: cstring   ## User's login name.
+    pw_uid*: Uid        ## Numerical user ID.
+    pw_gid*: Gid        ## Numerical group ID.
+    pw_dir*: cstring    ## Initial working directory.
+    pw_shell*: cstring  ## Program to use as shell.
+
+  Blkcnt* {.importc: "blkcnt_t", header: "<sys/types.h>".} = int
+    ## used for file block counts
+  Blksize* {.importc: "blksize_t", header: "<sys/types.h>".} = int32
+    ## used for block sizes
+  Clock* {.importc: "clock_t", header: "<sys/types.h>".} = int
+  ClockId* {.importc: "clockid_t", header: "<sys/types.h>".} = int
+  Dev* {.importc: "dev_t", header: "<sys/types.h>".} = (
+    when defined(freebsd):
+      uint32
+    else:
+      int32)
+  Fsblkcnt* {.importc: "fsblkcnt_t", header: "<sys/types.h>".} = int
+  Fsfilcnt* {.importc: "fsfilcnt_t", header: "<sys/types.h>".} = int
+  Gid* {.importc: "gid_t", header: "<sys/types.h>".} = uint32
+  Id* {.importc: "id_t", header: "<sys/types.h>".} = int
+  Ino* {.importc: "ino_t", header: "<sys/types.h>".} = int
+  Key* {.importc: "key_t", header: "<sys/types.h>".} = int
+  Mode* {.importc: "mode_t", header: "<sys/types.h>".} = (
+    when defined(openbsd) or defined(netbsd):
+      uint32
+    else:
+      uint16
+  )
+  Nlink* {.importc: "nlink_t", header: "<sys/types.h>".} = uint16
+  Off* {.importc: "off_t", header: "<sys/types.h>".} = int64
+  Pid* {.importc: "pid_t", header: "<sys/types.h>".} = int32
+  Pthread_attr* {.importc: "pthread_attr_t", header: "<sys/types.h>".} = int
+  Pthread_barrier* {.importc: "pthread_barrier_t",
+                      header: "<sys/types.h>".} = int
+  Pthread_barrierattr* {.importc: "pthread_barrierattr_t",
+                          header: "<sys/types.h>".} = int
+  Pthread_cond* {.importc: "pthread_cond_t", header: "<sys/types.h>".} = int
+  Pthread_condattr* {.importc: "pthread_condattr_t",
+                       header: "<sys/types.h>".} = int
+  Pthread_key* {.importc: "pthread_key_t", header: "<sys/types.h>".} = int
+  Pthread_mutex* {.importc: "pthread_mutex_t", header: "<sys/types.h>".} = int
+  Pthread_mutexattr* {.importc: "pthread_mutexattr_t",
+                        header: "<sys/types.h>".} = int
+  Pthread_once* {.importc: "pthread_once_t", header: "<sys/types.h>".} = int
+  Pthread_rwlock* {.importc: "pthread_rwlock_t",
+                     header: "<sys/types.h>".} = int
+  Pthread_rwlockattr* {.importc: "pthread_rwlockattr_t",
+                         header: "<sys/types.h>".} = int
+  Pthread_spinlock* {.importc: "pthread_spinlock_t",
+                       header: "<sys/types.h>".} = int
+  Pthread* {.importc: "pthread_t", header: "<sys/types.h>".} = int
+  Suseconds* {.importc: "suseconds_t", header: "<sys/types.h>".} = int32
+  #Ttime* {.importc: "time_t", header: "<sys/types.h>".} = int
+  Timer* {.importc: "timer_t", header: "<sys/types.h>".} = int
+  Trace_attr* {.importc: "trace_attr_t", header: "<sys/types.h>".} = int
+  Trace_event_id* {.importc: "trace_event_id_t",
+                     header: "<sys/types.h>".} = int
+  Trace_event_set* {.importc: "trace_event_set_t",
+                      header: "<sys/types.h>".} = int
+  Trace_id* {.importc: "trace_id_t", header: "<sys/types.h>".} = int
+  Uid* {.importc: "uid_t", header: "<sys/types.h>".} = uint32
+  Useconds* {.importc: "useconds_t", header: "<sys/types.h>".} = int
+
+  Utsname* {.importc: "struct utsname",
+              header: "<sys/utsname.h>",
+              final, pure.} = object ## struct utsname
+    sysname*,      ## Name of this implementation of the operating system.
+      nodename*,   ## Name of this node within the communications
+                   ## network to which this node is attached, if any.
+      release*,    ## Current release level of this implementation.
+      version*,    ## Current version level of this release.
+      machine*: array[0..255, char] ## Name of the hardware type on which the
+                                     ## system is running.
+
+  Sem* {.importc: "sem_t", header: "<semaphore.h>", final, pure.} = object
+  Ipc_perm* {.importc: "struct ipc_perm",
+               header: "<sys/ipc.h>", final, pure.} = object ## struct ipc_perm
+    uid*: Uid    ## Owner's user ID.
+    gid*: Gid    ## Owner's group ID.
+    cuid*: Uid   ## Creator's user ID.
+    cgid*: Gid   ## Creator's group ID.
+    mode*: Mode  ## Read/write permission.
+
+  Stat* {.importc: "struct stat",
+           header: "<sys/stat.h>", final, pure.} = object ## struct stat
+    st_dev*: Dev          ## Device ID of device containing file.
+    st_ino*: Ino          ## File serial number.
+    st_mode*: Mode        ## Mode of file (see below).
+    st_nlink*: Nlink      ## Number of hard links to the file.
+    st_uid*: Uid          ## User ID of file.
+    st_gid*: Gid          ## Group ID of file.
+    st_rdev*: Dev         ## Device ID (if file is character or block special).
+    st_size*: Off         ## For regular files, the file size in bytes.
+                          ## For symbolic links, the length in bytes of the
+                          ## pathname contained in the symbolic link.
+                          ## For a shared memory object, the length in bytes.
+                          ## For a typed memory object, the length in bytes.
+                          ## For other file types, the use of this field is
+                          ## unspecified.
+    when defined(osx):
+      st_atim* {.importc:"st_atimespec".}: Timespec  ## Time of last access.
+      st_mtim* {.importc:"st_mtimespec".}: Timespec  ## Time of last data modification.
+      st_ctim*  {.importc:"st_ctimespec".}: Timespec  ## Time of last status change.
+    elif StatHasNanoseconds:
+      st_atim*: Timespec  ## Time of last access.
+      st_mtim*: Timespec  ## Time of last data modification.
+      st_ctim*: Timespec  ## Time of last status change.
+    else:
+      st_atime*: Time     ## Time of last access.
+      st_mtime*: Time     ## Time of last data modification.
+      st_ctime*: Time     ## Time of last status change.
+
+    st_blksize*: Blksize  ## A file system-specific preferred I/O block size
+                          ## for this object. In some file system types, this
+                          ## may vary from file to file.
+    st_blocks*: Blkcnt    ## Number of blocks allocated for this object.
+
+
+  Statvfs* {.importc: "struct statvfs", header: "<sys/statvfs.h>",
+              final, pure.} = object ## struct statvfs
+    f_bsize*: int        ## File system block size.
+    f_frsize*: int       ## Fundamental file system block size.
+    f_blocks*: Fsblkcnt  ## Total number of blocks on file system
+                         ## in units of f_frsize.
+    f_bfree*: Fsblkcnt   ## Total number of free blocks.
+    f_bavail*: Fsblkcnt  ## Number of free blocks available to
+                         ## non-privileged process.
+    f_files*: Fsfilcnt   ## Total number of file serial numbers.
+    f_ffree*: Fsfilcnt   ## Total number of free file serial numbers.
+    f_favail*: Fsfilcnt  ## Number of file serial numbers available to
+                         ## non-privileged process.
+    f_fsid*: int         ## File system ID.
+    f_flag*: int         ## Bit mask of f_flag values.
+    f_namemax*: int      ## Maximum filename length.
+
+  Posix_typed_mem_info* {.importc: "struct posix_typed_mem_info",
+                           header: "<sys/mman.h>", final, pure.} = object
+    posix_tmi_length*: int
+
+  Tm* {.importc: "struct tm", header: "<time.h>",
+         final, pure.} = object ## struct tm
+    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.
+  Itimerspec* {.importc: "struct itimerspec", header: "<time.h>",
+                 final, pure.} = object ## struct itimerspec
+    it_interval*: Timespec  ## Timer period.
+    it_value*: Timespec     ## Timer expiration.
+
+  Sig_atomic* {.importc: "sig_atomic_t", header: "<signal.h>".} = cint
+    ## Possibly volatile-qualified integer type of an object that can be
+    ## accessed as an atomic entity, even in the presence of asynchronous
+    ## interrupts.
+  Sigset* {.importc: "sigset_t", header: "<signal.h>", final, pure.} = object
+
+  SigEvent* {.importc: "struct sigevent",
+               header: "<signal.h>", final, pure.} = object ## struct sigevent
+    sigev_notify*: cint           ## Notification type.
+    sigev_signo*: cint            ## Signal number.
+    sigev_value*: SigVal          ## Signal value.
+    sigev_notify_function*: proc (x: SigVal) {.noconv.} ## Notification func.
+    sigev_notify_attributes*: ptr Pthread_attr ## Notification attributes.
+
+  SigVal* {.importc: "union sigval",
+             header: "<signal.h>", final, pure.} = object ## struct sigval
+    sival_ptr*: pointer ## pointer signal value;
+                        ## integer signal value not defined!
+  Sigaction* {.importc: "struct sigaction",
+                header: "<signal.h>", final, pure.} = object ## struct sigaction
+    sa_handler*: proc (x: cint) {.noconv.}  ## Pointer to a signal-catching
+                                            ## function or one of the macros
+                                            ## SIG_IGN or SIG_DFL.
+    sa_mask*: Sigset ## Set of signals to be blocked during execution of
+                      ## the signal handling function.
+    sa_flags*: cint   ## Special flags.
+    sa_sigaction*: proc (x: cint, y: ptr SigInfo, z: pointer) {.noconv.}
+
+  Stack* {.importc: "stack_t",
+            header: "<signal.h>", final, pure.} = object ## stack_t
+    ss_sp*: pointer  ## Stack base or pointer.
+    ss_size*: int    ## Stack size.
+    ss_flags*: cint  ## Flags.
+
+  SigStack* {.importc: "struct sigstack",
+               header: "<signal.h>", final, pure.} = object ## struct sigstack
+    ss_onstack*: cint ## Non-zero when signal stack is in use.
+    ss_sp*: pointer   ## Signal stack pointer.
+
+  SigInfo* {.importc: "siginfo_t",
+              header: "<signal.h>", final, pure.} = object ## siginfo_t
+    si_signo*: cint    ## Signal number.
+    si_code*: cint     ## Signal code.
+    si_errno*: cint    ## If non-zero, an errno value associated with
+                       ## this signal, as defined in <errno.h>.
+    si_pid*: Pid       ## Sending process ID.
+    si_uid*: Uid       ## Real user ID of sending process.
+    si_addr*: pointer  ## Address of faulting instruction.
+    si_status*: cint   ## Exit value or signal.
+    si_band*: int      ## Band event for SIGPOLL.
+    si_value*: SigVal  ## Signal value.
+
+  Nl_item* {.importc: "nl_item", header: "<nl_types.h>".} = cint
+  Nl_catd* {.importc: "nl_catd", header: "<nl_types.h>".} = cint
+
+  Sched_param* {.importc: "struct sched_param",
+                  header: "<sched.h>",
+                  final, pure.} = object ## struct sched_param
+    sched_priority*: cint
+    sched_ss_low_priority*: cint     ## Low scheduling priority for
+                                     ## sporadic server.
+    sched_ss_repl_period*: Timespec  ## Replenishment period for
+                                     ## sporadic server.
+    sched_ss_init_budget*: Timespec  ## Initial budget for sporadic server.
+    sched_ss_max_repl*: cint         ## Maximum pending replenishments for
+                                     ## sporadic server.
+
+  Timeval* {.importc: "struct timeval", header: "<sys/select.h>",
+             final, pure.} = object ## struct timeval
+    tv_sec*: Time ## Seconds.
+    tv_usec*: Suseconds ## Microseconds.
+  TFdSet* {.importc: "fd_set", header: "<sys/select.h>",
+           final, pure.} = object
+  Mcontext* {.importc: "mcontext_t", header: "<ucontext.h>",
+               final, pure.} = object
+  Ucontext* {.importc: "ucontext_t", header: "<ucontext.h>",
+               final, pure.} = object ## ucontext_t
+    uc_link*: ptr Ucontext  ## Pointer to the context that is resumed
+                            ## when this context returns.
+    uc_sigmask*: Sigset     ## The set of signals that are blocked when this
+                            ## context is active.
+    uc_stack*: Stack        ## The stack used by this context.
+    uc_mcontext*: Mcontext  ## A machine-specific representation of the saved
+                            ## context.
+
+when hasAioH:
+  type
+    Taiocb* {.importc: "struct aiocb", header: "<aio.h>",
+              final, pure.} = object ## struct aiocb
+      aio_fildes*: cint         ## File descriptor.
+      aio_offset*: Off          ## File offset.
+      aio_buf*: pointer         ## Location of buffer.
+      aio_nbytes*: int          ## Length of transfer.
+      aio_reqprio*: cint        ## Request priority offset.
+      aio_sigevent*: SigEvent   ## Signal number and value.
+      aio_lio_opcode: cint      ## Operation to be performed.
+
+when hasSpawnH:
+  type
+    Tposix_spawnattr* {.importc: "posix_spawnattr_t",
+                        header: "<spawn.h>", final, pure.} = object
+    Tposix_spawn_file_actions* {.importc: "posix_spawn_file_actions_t",
+                                 header: "<spawn.h>", final, pure.} = object
+
+
+when not defined(macos) and not defined(macosx): # freebsd
+  type
+    Mqd* {.importc: "mqd_t", header: "<mqueue.h>", final, pure.} = object
+    MqAttr* {.importc: "struct mq_attr",
+              header: "<mqueue.h>",
+              final, pure.} = object ## message queue attribute
+      mq_flags*: int   ## Message queue flags.
+      mq_maxmsg*: int  ## Maximum number of messages.
+      mq_msgsize*: int ## Maximum message size.
+      mq_curmsgs*: int ## Number of messages currently queued.
+
+when defined(linux):
+  # from sys/un.h
+  const Sockaddr_un_path_length* = 108
+else:
+  # according to http://pubs.opengroup.org/onlinepubs/009604499/basedefs/sys/un.h.html
+  # this is >=92
+  const Sockaddr_un_path_length* = 92
+
+type
+  SockLen* {.importc: "socklen_t", header: "<sys/socket.h>".} = cuint
+  TSa_Family* {.importc: "sa_family_t", header: "<sys/socket.h>".} = uint8
+
+  SockAddr* {.importc: "struct sockaddr", header: "<sys/socket.h>",
+              pure, final.} = object ## struct sockaddr
+    sa_family*: TSa_Family         ## Address family.
+    sa_data*: array[0..255, char] ## Socket address (variable-length data).
+
+  Sockaddr_un* {.importc: "struct sockaddr_un", header: "<sys/un.h>",
+              pure, final.} = object ## struct sockaddr_un
+    sun_family*: TSa_Family         ## Address family.
+    sun_path*: array[0..Sockaddr_un_path_length-1, char] ## Socket path
+
+  Sockaddr_storage* {.importc: "struct sockaddr_storage",
+                       header: "<sys/socket.h>",
+                       pure, final.} = object ## struct sockaddr_storage
+    ss_family*: TSa_Family ## Address family.
+
+  Tif_nameindex* {.importc: "struct if_nameindex", final,
+                   pure, header: "<net/if.h>".} = object ## struct if_nameindex
+    if_index*: cint   ## Numeric index of the interface.
+    if_name*: cstring ## Null-terminated name of the interface.
+
+
+  IOVec* {.importc: "struct iovec", pure, final,
+            header: "<sys/uio.h>".} = object ## struct iovec
+    iov_base*: pointer ## Base address of a memory region for input or output.
+    iov_len*: csize_t    ## The size of the memory pointed to by iov_base.
+
+  Tmsghdr* {.importc: "struct msghdr", pure, final,
+             header: "<sys/socket.h>".} = object  ## struct msghdr
+    msg_name*: pointer  ## Optional address.
+    msg_namelen*: SockLen  ## Size of address.
+    msg_iov*: ptr IOVec    ## Scatter/gather array.
+    msg_iovlen*: cint   ## Members in msg_iov.
+    msg_control*: pointer  ## Ancillary data; see below.
+    msg_controllen*: SockLen ## Ancillary data buffer len.
+    msg_flags*: cint ## Flags on received message.
+
+
+  Tcmsghdr* {.importc: "struct cmsghdr", pure, final,
+              header: "<sys/socket.h>".} = object ## struct cmsghdr
+    cmsg_len*: SockLen ## Data byte count, including the cmsghdr.
+    cmsg_level*: cint   ## Originating protocol.
+    cmsg_type*: cint    ## Protocol-specific type.
+
+  TLinger* {.importc: "struct linger", pure, final,
+             header: "<sys/socket.h>".} = object ## struct linger
+    l_onoff*: cint  ## Indicates whether linger option is enabled.
+    l_linger*: cint ## Linger time, in seconds.
+
+  InPort* = uint16
+  InAddrScalar* = uint32
+
+  InAddrT* {.importc: "in_addr_t", pure, final,
+             header: "<netinet/in.h>".} = uint32
+
+  InAddr* {.importc: "struct in_addr", pure, final,
+             header: "<netinet/in.h>".} = object ## struct in_addr
+    s_addr*: InAddrScalar
+
+  Sockaddr_in* {.importc: "struct sockaddr_in", pure, final,
+                  header: "<netinet/in.h>".} = object ## struct sockaddr_in
+    sin_family*: TSa_Family ## AF_INET.
+    sin_port*: InPort      ## Port number.
+    sin_addr*: InAddr      ## IP address.
+
+  In6Addr* {.importc: "struct in6_addr", pure, final,
+              header: "<netinet/in.h>".} = object ## struct in6_addr
+    s6_addr*: array[0..15, char]
+
+  Sockaddr_in6* {.importc: "struct sockaddr_in6", pure, final,
+                   header: "<netinet/in.h>".} = object ## struct sockaddr_in6
+    sin6_family*: TSa_Family ## AF_INET6.
+    sin6_port*: InPort      ## Port number.
+    sin6_flowinfo*: uint32    ## IPv6 traffic class and flow information.
+    sin6_addr*: In6Addr     ## IPv6 address.
+    sin6_scope_id*: uint32    ## Set of interfaces for a scope.
+
+  Tipv6_mreq* {.importc: "struct ipv6_mreq", pure, final,
+                header: "<netinet/in.h>".} = object ## struct ipv6_mreq
+    ipv6mr_multiaddr*: In6Addr ## IPv6 multicast address.
+    ipv6mr_interface*: cint     ## Interface index.
+
+  Hostent* {.importc: "struct hostent", pure, final,
+              header: "<netdb.h>".} = object ## struct hostent
+    h_name*: cstring           ## Official name of the host.
+    h_aliases*: cstringArray   ## A pointer to an array of pointers to
+                               ## alternative host names, terminated by a
+                               ## null pointer.
+    h_addrtype*: cint          ## Address type.
+    h_length*: cint            ## The length, in bytes, of the address.
+    h_addr_list*: cstringArray ## A pointer to an array of pointers to network
+                               ## addresses (in network byte order) for the
+                               ## host, terminated by a null pointer.
+
+  Tnetent* {.importc: "struct netent", pure, final,
+              header: "<netdb.h>".} = object ## struct netent
+    n_name*: cstring         ## Official, fully-qualified (including the
+                             ## domain) name of the host.
+    n_aliases*: cstringArray ## A pointer to an array of pointers to
+                             ## alternative network names, terminated by a
+                             ## null pointer.
+    n_addrtype*: cint        ## The address type of the network.
+    n_net*: uint32            ## The network number, in host byte order.
+
+  Protoent* {.importc: "struct protoent", pure, final,
+              header: "<netdb.h>".} = object ## struct protoent
+    p_name*: cstring         ## Official name of the protocol.
+    p_aliases*: cstringArray ## A pointer to an array of pointers to
+                             ## alternative protocol names, terminated by
+                             ## a null pointer.
+    p_proto*: cint           ## The protocol number.
+
+  Servent* {.importc: "struct servent", pure, final,
+             header: "<netdb.h>".} = object ## struct servent
+    s_name*: cstring         ## Official name of the service.
+    s_aliases*: cstringArray ## A pointer to an array of pointers to
+                             ## alternative service names, terminated by
+                             ## a null pointer.
+    s_port*: cint            ## The port number at which the service
+                             ## resides, in network byte order.
+    s_proto*: cstring        ## The name of the protocol to use when
+                             ## contacting the service.
+
+  AddrInfo* {.importc: "struct addrinfo", pure, final,
+              header: "<netdb.h>".} = object ## struct addrinfo
+    ai_flags*: cint         ## Input flags.
+    ai_family*: cint        ## Address family of socket.
+    ai_socktype*: cint      ## Socket type.
+    ai_protocol*: cint      ## Protocol of socket.
+    ai_addrlen*: SockLen   ## Length of socket address.
+    ai_addr*: ptr SockAddr ## Socket address of socket.
+    ai_canonname*: cstring  ## Canonical name of service location.
+    ai_next*: ptr AddrInfo ## Pointer to next in list.
+
+  TPollfd* {.importc: "struct pollfd", pure, final,
+             header: "<poll.h>".} = object ## struct pollfd
+    fd*: cint        ## The following descriptor being polled.
+    events*: cshort  ## The input event flags (see below).
+    revents*: cshort ## The output event flags (see below).
+
+  Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = cint
+
+var
+  errno* {.importc, header: "<errno.h>".}: cint ## error variable
+  h_errno* {.importc, header: "<netdb.h>".}: cint
+  daylight* {.importc, header: "<time.h>".}: cint
+  timezone* {.importc, header: "<time.h>".}: int
+
+# Regenerate using detect.nim!
+include posix_other_consts
+
+when defined(linux):
+  var
+    MAP_POPULATE* {.importc, header: "<sys/mman.h>".}: cint
+      ## Populate (prefault) page tables for a mapping.
+else:
+  var
+    MAP_POPULATE*: cint = 0
+
+when defined(linux) or defined(nimdoc):
+  when defined(alpha) or defined(mips) or defined(mipsel) or
+      defined(mips64) or defined(mips64el) or defined(parisc) or
+      defined(sparc) or defined(sparc64) or defined(nimdoc):
+    const SO_REUSEPORT* = cint(0x0200)
+      ## Multiple binding: load balancing on incoming TCP connections
+      ## or UDP packets. (Requires Linux kernel > 3.9)
+  else:
+    const SO_REUSEPORT* = cint(15)
+else:
+  var SO_REUSEPORT* {.importc, header: "<sys/socket.h>".}: cint
+
+when defined(linux) or defined(bsd):
+  var SOCK_CLOEXEC* {.importc, header: "<sys/socket.h>".}: cint
+
+when defined(macosx):
+  # We can't use the NOSIGNAL flag in the `send` function, it has no effect
+  # Instead we should use SO_NOSIGPIPE in setsockopt
+  const
+    MSG_NOSIGNAL* = 0'i32
+  var
+    SO_NOSIGPIPE* {.importc, header: "<sys/socket.h>".}: cint
+elif defined(solaris):
+  # Solaris doesn't have MSG_NOSIGNAL
+  const
+    MSG_NOSIGNAL* = 0'i32
+else:
+  var
+    MSG_NOSIGNAL* {.importc, header: "<sys/socket.h>".}: cint
+      ## No SIGPIPE generated when an attempt to send is made on a stream-oriented socket that is no longer connected.
+
+when defined(haiku):
+  const
+    SIGKILLTHR* = 21 ## BeOS specific: Kill just the thread, not team
+
+when hasSpawnH:
+  when defined(linux):
+    # better be safe than sorry; Linux has this flag, macosx doesn't, don't
+    # know about the other OSes
+
+    # Non-GNU systems like TCC and musl-libc  don't define __USE_GNU, so we
+    # can't get the magic number from spawn.h
+    const POSIX_SPAWN_USEVFORK* = cint(0x40)
+  else:
+    # macosx lacks this, so we define the constant to be 0 to not affect
+    # OR'ing of flags:
+    const POSIX_SPAWN_USEVFORK* = cint(0)
+
+# <sys/wait.h>
+proc WEXITSTATUS*(s: cint): cint {.importc, header: "<sys/wait.h>".}
+  ## Exit code, if WIFEXITED(s)
+proc WTERMSIG*(s: cint): cint {.importc, header: "<sys/wait.h>".}
+  ## Termination signal, if WIFSIGNALED(s)
+proc WSTOPSIG*(s: cint): cint {.importc, header: "<sys/wait.h>".}
+  ## Stop signal, if WIFSTOPPED(s)
+proc WIFEXITED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
+  ## True if child exited normally.
+proc WIFSIGNALED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
+  ## True if child exited due to uncaught signal.
+proc WIFSTOPPED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
+  ## True if child is currently stopped.
+proc WIFCONTINUED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
+  ## True if child has been continued.
+
+when defined(nimHasStyleChecks):
+  {.pop.}
diff --git a/lib/posix/posix_nintendoswitch.nim b/lib/posix/posix_nintendoswitch.nim
new file mode 100644
index 000000000..b66563695
--- /dev/null
+++ b/lib/posix/posix_nintendoswitch.nim
@@ -0,0 +1,506 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2018 Joey Yakimowich-Payne
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# To be included from posix.nim!
+
+const
+  hasSpawnH = true
+  hasAioH = false
+
+type
+  DIR* {.importc: "DIR", header: "<dirent.h>",
+          incompleteStruct.} = object
+
+const SIG_HOLD* = cast[Sighandler](2)
+
+type
+  SocketHandle* = distinct cint # The type used to represent socket descriptors
+
+type
+  Time* {.importc: "time_t", header: "<time.h>".} = distinct clong
+
+  Timespec* {.importc: "struct timespec",
+               header: "<time.h>", final, pure.} = object ## struct timespec
+    tv_sec*: Time  ## Seconds.
+    tv_nsec*: clong  ## Nanoseconds.
+
+  Dirent* {.importc: "struct dirent",
+            header: "<dirent.h>", final, pure.} = object ## dirent_t struct
+    d_ino*: Ino
+    d_type*: int8  # uint8 really!
+    d_name*: array[256, cchar]
+
+  Tflock* {.importc: "struct flock", final, pure,
+            header: "<fcntl.h>".} = object ## flock type
+    l_type*: cshort   ## Type of lock; F_RDLCK, F_WRLCK, F_UNLCK.
+    l_whence*: cshort ## Flag for starting offset.
+    l_start*: Off     ## Relative offset in bytes.
+    l_len*: Off       ## Size; if 0 then until EOF.
+    l_pid*: Pid      ## Process ID of the process holding the lock;
+                      ## returned with F_GETLK.
+
+  # no struct FTW on linux
+
+  Glob* {.importc: "glob_t", header: "<glob.h>",
+           final, pure.} = object ## glob_t
+    gl_pathc*: cint          ## Count of paths matched by pattern.
+    gl_matchc*: cint          ## Count of paths matching pattern
+    gl_offs*: cint           ## Slots to reserve at the beginning of gl_pathv.
+    gl_flags*: cint
+    gl_pathv*: cstringArray ## Pointer to a list of matched pathnames.
+    gl_errfunc*: pointer
+    gl_closedir*: pointer
+    gl_readdir*: pointer
+    gl_opendir*: pointer
+    gl_lstat*: pointer
+    gl_stat*: pointer
+
+  Group* {.importc: "struct group", header: "<grp.h>",
+            final, pure.} = object ## struct group
+    gr_name*: cstring     ## The name of the group.
+    gr_passwd*: cstring
+    gr_gid*: Gid         ## Numerical group ID.
+    gr_mem*: cstringArray ## Pointer to a null-terminated array of character
+                          ## pointers to member names.
+
+  Iconv* {.importc: "iconv_t", header: "<iconv.h>".} = pointer
+     ## Identifies the conversion from one codeset to another.
+
+  Lconv* {.importc: "struct lconv", header: "<locale.h>", final,
+            pure.} = object
+    decimal_point*: cstring
+    thousands_sep*: cstring
+    grouping*: cstring
+    int_curr_symbol*: cstring
+    currency_symbol*: cstring
+    mon_decimal_point*: cstring
+    mon_thousands_sep*: cstring
+    mon_grouping*: cstring
+    positive_sign*: cstring
+    negative_sign*: cstring
+    int_frac_digits*: char
+    frac_digits*: char
+    p_cs_precedes*: char
+    p_sep_by_space*: char
+    n_cs_precedes*: char
+    n_sep_by_space*: char
+    p_sign_posn*: char
+    n_sign_posn*: char
+    int_n_cs_precedes*: char
+    int_n_sep_by_space*: char
+    int_n_sign_posn*: char
+    int_p_cs_precedes*: char
+    int_p_sep_by_space*: char
+    int_p_sign_posn*: char
+
+  Passwd* {.importc: "struct passwd", header: "<pwd.h>",
+             final, pure.} = object ## struct passwd
+    pw_name*: cstring   ## User's login name.
+    pw_passwd*: cstring
+    pw_uid*: Uid        ## Numerical user ID.
+    pw_gid*: Gid        ## Numerical group ID.
+    pw_comment*: cstring
+    pw_gecos*: cstring
+    pw_dir*: cstring    ## Initial working directory.
+    pw_shell*: cstring  ## Program to use as shell.
+
+  Blkcnt* {.importc: "blkcnt_t", header: "<sys/types.h>".} = clong
+    ## used for file block counts
+  Blksize* {.importc: "blksize_t", header: "<sys/types.h>".} = clong
+    ## used for block sizes
+  Clock* {.importc: "clock_t", header: "<sys/types.h>".} = clong
+  ClockId* {.importc: "clockid_t", header: "<sys/types.h>".} = cint
+  Dev* {.importc: "dev_t", header: "<sys/types.h>".} = culong
+  Fsblkcnt* {.importc: "fsblkcnt_t", header: "<sys/types.h>".} = culong
+  Fsfilcnt* {.importc: "fsfilcnt_t", header: "<sys/types.h>".} = culong
+  Gid* {.importc: "gid_t", header: "<sys/types.h>".} = cuint
+  Id* {.importc: "id_t", header: "<sys/types.h>".} = cuint
+  Ino* {.importc: "ino_t", header: "<sys/types.h>".} = culong
+  Key* {.importc: "key_t", header: "<sys/types.h>".} = cint
+  Mode* {.importc: "mode_t", header: "<sys/types.h>".} = uint16
+  Nlink* {.importc: "nlink_t", header: "<sys/types.h>".} = culong
+  Off* {.importc: "off_t", header: "<sys/types.h>".} = clong
+  Pid* {.importc: "pid_t", header: "<sys/types.h>".} = cint
+  Pthread_attr* {.importc: "pthread_attr_t", header: "<sys/types.h>",
+                  pure, final.} = object
+    abi: array[56 div sizeof(clong), clong]
+
+  Pthread_barrier* {.importc: "pthread_barrier_t",
+                      header: "<sys/types.h>", pure, final.} = object
+    abi: array[32 div sizeof(clong), clong]
+  Pthread_barrierattr* {.importc: "pthread_barrierattr_t",
+                          header: "<sys/types.h>", pure, final.} = object
+    abi: array[4 div sizeof(cint), cint]
+
+  Pthread_cond* {.importc: "pthread_cond_t", header: "<sys/types.h>",
+                  pure, final.} = object
+    abi: array[48 div sizeof(clonglong), clonglong]
+  Pthread_condattr* {.importc: "pthread_condattr_t",
+                       header: "<sys/types.h>", pure, final.} = object
+    abi: array[4 div sizeof(cint), cint]
+  Pthread_key* {.importc: "pthread_key_t", header: "<sys/types.h>".} = cuint
+  Pthread_mutex* {.importc: "pthread_mutex_t", header: "<sys/types.h>",
+                   pure, final.} = object
+    abi: array[48 div sizeof(clong), clong]
+  Pthread_mutexattr* {.importc: "pthread_mutexattr_t",
+                        header: "<sys/types.h>", pure, final.} = object
+    abi: array[4 div sizeof(cint), cint]
+  Pthread_once* {.importc: "pthread_once_t", header: "<sys/types.h>".} = cint
+  Pthread_rwlock* {.importc: "pthread_rwlock_t",
+                     header: "<sys/types.h>", pure, final.} = object
+    abi: array[56 div sizeof(clong), clong]
+  Pthread_rwlockattr* {.importc: "pthread_rwlockattr_t",
+                         header: "<sys/types.h>".} = object
+    abi: array[8 div sizeof(clong), clong]
+  Pthread_spinlock* {.importc: "pthread_spinlock_t",
+                       header: "<sys/types.h>".} = cint
+  Pthread* {.importc: "pthread_t", header: "<sys/types.h>".} = culong
+  Suseconds* {.importc: "suseconds_t", header: "<sys/types.h>".} = clong
+  #Ttime* {.importc: "time_t", header: "<sys/types.h>".} = int
+  Timer* {.importc: "timer_t", header: "<sys/types.h>".} = pointer
+  Uid* {.importc: "uid_t", header: "<sys/types.h>".} = cuint
+  Useconds* {.importc: "useconds_t", header: "<sys/types.h>".} = cuint
+
+  Utsname* {.importc: "struct utsname",
+              header: "<sys/utsname.h>",
+              final, pure.} = object ## struct utsname
+    sysname*,      ## Name of this implementation of the operating system.
+      nodename*,   ## Name of this node within the communications
+                   ## network to which this node is attached, if any.
+      release*,    ## Current release level of this implementation.
+      version*,    ## Current version level of this release.
+      machine*,    ## Name of the hardware type on which the
+                   ## system is running.
+      domainname*: array[65, char]
+
+  Sem* {.importc: "sem_t", header: "<semaphore.h>", final, pure.} = object
+    abi: array[32 div sizeof(clong), clong]
+
+  Stat* {.importc: "struct stat",
+           header: "<sys/stat.h>", final, pure.} = object ## struct stat
+    st_dev*: Dev          ## Device ID of device containing file.
+    st_ino*: Ino          ## File serial number.
+    st_mode*: Mode        ## Mode of file (see below).
+    st_nlink*: Nlink      ## Number of hard links to the file.
+    st_uid*: Uid          ## User ID of file.
+    st_gid*: Gid          ## Group ID of file.
+    st_rdev*: Dev         ## Device ID (if file is character or block special).
+    st_size*: Off         ## For regular files, the file size in bytes.
+                           ## For symbolic links, the length in bytes of the
+                           ## pathname contained in the symbolic link.
+                           ## For a shared memory object, the length in bytes.
+                           ## For a typed memory object, the length in bytes.
+                           ## For other file types, the use of this field is
+                           ## unspecified.
+    when StatHasNanoseconds:
+      st_atim*: Timespec  ## Time of last access.
+      pad1: clong
+      st_mtim*: Timespec  ## Time of last data modification.
+      pad2: clong
+      st_ctim*: Timespec  ## Time of last status change.
+      pad3: clong
+    else:
+      st_atime*: Time     ## Time of last access.
+      pad1: clong
+      st_mtime*: Time     ## Time of last data modification.
+      pad2: clong
+      st_ctime*: Time     ## Time of last status change.
+      pad3: clong
+    st_blksize*: Blksize   ## A file system-specific preferred I/O block size
+                           ## for this object. In some file system types, this
+                           ## may vary from file to file.
+    st_blocks*: Blkcnt     ## Number of blocks allocated for this object.
+    reserved: array[2, clong]
+
+
+
+  Statvfs* {.importc: "struct statvfs", header: "<sys/statvfs.h>",
+              final, pure.} = object ## struct statvfs
+    f_bsize*: culong        ## File system block size.
+    f_frsize*: culong       ## Fundamental file system block size.
+    f_blocks*: Fsblkcnt  ## Total number of blocks on file system
+                         ## in units of f_frsize.
+    f_bfree*: Fsblkcnt   ## Total number of free blocks.
+    f_bavail*: Fsblkcnt  ## Number of free blocks available to
+                         ## non-privileged process.
+    f_files*: Fsfilcnt   ## Total number of file serial numbers.
+    f_ffree*: Fsfilcnt   ## Total number of free file serial numbers.
+    f_favail*: Fsfilcnt  ## Number of file serial numbers available to
+                         ## non-privileged process.
+    f_fsid*: culong         ## File system ID.
+    f_flag*: culong         ## Bit mask of f_flag values.
+    f_namemax*: culong      ## Maximum filename length.
+
+  # No Posix_typed_mem_info
+
+  Tm* {.importc: "struct tm", header: "<time.h>",
+         final, pure.} = object ## struct tm
+    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.
+
+  Itimerspec* {.importc: "struct itimerspec", header: "<time.h>",
+                 final, pure.} = object ## struct itimerspec
+    it_interval*: Timespec  ## Timer period.
+    it_value*: Timespec     ## Timer expiration.
+
+  Sig_atomic* {.importc: "sig_atomic_t", header: "<signal.h>".} = cint
+    ## Possibly volatile-qualified integer type of an object that can be
+    ## accessed as an atomic entity, even in the presence of asynchronous
+    ## interrupts.
+  Sigset* {.importc: "sigset_t", header: "<signal.h>", final.} = culong
+
+  SigEvent* {.importc: "struct sigevent",
+               header: "<signal.h>", final, pure.} = object ## struct sigevent
+    sigev_notify*: cint           ## Notification type.
+    sigev_signo*: cint            ## Signal number.
+    sigev_value*: SigVal          ## Signal value.
+
+  SigVal* {.importc: "union sigval",
+             header: "<signal.h>", final, pure.} = object ## struct sigval
+    sival_int*: cint    ## integer signal value
+    sival_ptr*: pointer ## pointer signal value;
+
+  Sigaction* {.importc: "struct sigaction",
+                header: "<signal.h>", final, pure.} = object ## struct sigaction
+    sa_handler*: proc (x: cint) {.noconv.}  ## Pointer to a signal-catching
+                                            ## function or one of the macros
+                                            ## SIG_IGN or SIG_DFL.
+    sa_mask*: Sigset ## Set of signals to be blocked during execution of
+                      ## the signal handling function.
+    sa_flags*: cint   ## Special flags.
+
+  Stack* {.importc: "stack_t",
+            header: "<signal.h>", final, pure.} = object ## stack_t
+    ss_sp*: pointer  ## Stack base or pointer.
+    ss_flags*: cint  ## Flags.
+    ss_size*: csize_t ## Stack size.
+
+  SigInfo* {.importc: "siginfo_t",
+              header: "<signal.h>", final, pure.} = object ## siginfo_t
+    si_signo*: cint    ## Signal number.
+    si_code*: cint     ## Signal code.
+    si_value*: SigVal  ## Signal value.
+
+  Nl_item* {.importc: "nl_item", header: "<langinfo.h>".} = cint
+
+  Sched_param* {.importc: "struct sched_param",
+                  header: "<sched.h>",
+                  final, pure.} = object ## struct sched_param
+    sched_priority*: cint
+
+  Timeval* {.importc: "struct timeval", header: "<sys/select.h>",
+             final, pure.} = object ## struct timeval
+    tv_sec*: Time       ## Seconds.
+    tv_usec*: Suseconds ## Microseconds.
+  TFdSet* {.importc: "fd_set", header: "<sys/select.h>",
+           final, pure.} = object
+    abi: array[((64+(sizeof(clong) * 8)-1) div (sizeof(clong) * 8)), clong]
+
+proc si_pid*(info: SigInfo): Pid =
+  ## This might not be correct behavior. si_pid doesn't exist in Switch's
+  ## devkitpro headers
+  raise newException(OSError, "Nintendo switch cannot get si_pid!")
+
+type
+  Taiocb* {.importc: "struct aiocb", header: "<aio.h>",
+            final, pure.} = object ## struct aiocb
+    aio_fildes*: cint         ## File descriptor.
+    aio_lio_opcode*: cint     ## Operation to be performed.
+    aio_reqprio*: cint        ## Request priority offset.
+    aio_buf*: pointer         ## Location of buffer.
+    aio_nbytes*: csize_t      ## Length of transfer.
+    aio_sigevent*: SigEvent   ## Signal number and value.
+    next_prio: pointer
+    abs_prio: cint
+    policy: cint
+    error_Code: cint
+    return_value: clong
+    aio_offset*: Off          ## File offset.
+    reserved: array[32, uint8]
+
+type
+  Tposix_spawnattr* {.importc: "posix_spawnattr_t",
+                      header: "<spawn.h>", final, pure.} = object
+  Tposix_spawn_file_actions* {.importc: "posix_spawn_file_actions_t",
+                               header: "<spawn.h>", final, pure.} = object
+
+# from sys/un.h
+const Sockaddr_un_path_length* = 108
+
+type
+  SockLen* {.importc: "socklen_t", header: "<sys/socket.h>".} = cuint
+  # cushort really
+  TSa_Family* {.importc: "sa_family_t", header: "<sys/socket.h>".} = cshort
+
+  SockAddr* {.importc: "struct sockaddr", header: "<sys/socket.h>",
+              pure, final.} = object ## struct sockaddr
+    sa_len: uint8
+    sa_family*: TSa_Family         ## Address family.
+    sa_data*: array[14, char] ## Socket address (variable-length data).
+
+  Sockaddr_storage* {.importc: "struct sockaddr_storage",
+                       header: "<sys/socket.h>",
+                       pure, final.} = object ## struct sockaddr_storage
+    ss_len: uint8
+    ss_family*: TSa_Family ## Address family.
+    ss_padding1: array[64 - sizeof(uint8) - sizeof(cshort), char]
+    ss_align: clonglong
+    ss_padding2: array[
+      128 - sizeof(uint8) - sizeof(cshort) -
+      (64 - sizeof(uint8) - sizeof(cshort)) - 64, char]
+
+  Tif_nameindex* {.importc: "struct if_nameindex", final,
+                   pure, header: "<net/if.h>".} = object ## struct if_nameindex
+    if_index*: cuint   ## Numeric index of the interface.
+    if_name*: cstring ## Null-terminated name of the interface.
+
+
+  IOVec* {.importc: "struct iovec", pure, final,
+            header: "<sys/socket.h>".} = object ## struct iovec
+    iov_base*: pointer ## Base address of a memory region for input or output.
+    iov_len*: csize_t    ## The size of the memory pointed to by iov_base.
+
+  Tmsghdr* {.importc: "struct msghdr", pure, final,
+             header: "<sys/socket.h>".} = object  ## struct msghdr
+    msg_name*: pointer  ## Optional address.
+    msg_namelen*: SockLen  ## Size of address.
+    msg_iov*: ptr IOVec    ## Scatter/gather array.
+    msg_iovlen*: csize_t   ## Members in msg_iov.
+    msg_control*: pointer  ## Ancillary data; see below.
+    msg_controllen*: csize_t ## Ancillary data buffer len.
+    msg_flags*: cint ## Flags on received message.
+
+
+  Tcmsghdr* {.importc: "struct cmsghdr", pure, final,
+              header: "<sys/socket.h>".} = object ## struct cmsghdr
+    cmsg_len*: csize_t ## Data byte count, including the cmsghdr.
+    cmsg_level*: cint   ## Originating protocol.
+    cmsg_type*: cint    ## Protocol-specific type.
+
+  TLinger* {.importc: "struct linger", pure, final,
+             header: "<sys/socket.h>".} = object ## struct linger
+    l_onoff*: cint  ## Indicates whether linger option is enabled.
+    l_linger*: cint ## Linger time, in seconds.
+    # data follows...
+
+  InPort* = uint16
+  InAddrScalar* = uint32
+
+  InAddrT* {.importc: "in_addr_t", pure, final,
+             header: "<netinet/in.h>".} = uint32
+
+  InAddr* {.importc: "struct in_addr", pure, final,
+             header: "<netinet/in.h>".} = object ## struct in_addr
+    s_addr*: InAddrScalar
+
+  Sockaddr_in* {.importc: "struct sockaddr_in", pure, final,
+                  header: "<netinet/in.h>".} = object ## struct sockaddr_in
+    sin_len*: cushort
+    sin_family*: TSa_Family ## AF_INET.
+    sin_port*: InPort      ## Port number.
+    sin_addr*: InAddr      ## IP address.
+    sin_zero: array[8, uint8]
+
+  In6Addr* {.importc: "struct in6_addr", pure, final,
+              header: "<netinet/in.h>".} = object ## struct in6_addr
+    s6_addr*: array[0..15, char]
+
+  Sockaddr_in6* {.importc: "struct sockaddr_in6", pure, final,
+                   header: "<netinet/in.h>".} = object ## struct sockaddr_in6
+    sin6_family*: TSa_Family ## AF_INET6.
+    sin6_port*: InPort      ## Port number.
+    sin6_flowinfo*: uint32    ## IPv6 traffic class and flow information.
+    sin6_addr*: In6Addr     ## IPv6 address.
+    sin6_scope_id*: uint32    ## Set of interfaces for a scope.
+
+  Hostent* {.importc: "struct hostent", pure, final,
+              header: "<netdb.h>".} = object ## struct hostent
+    h_name*: cstring           ## Official name of the host.
+    h_aliases*: cstringArray   ## A pointer to an array of pointers to
+                               ## alternative host names, terminated by a
+                               ## null pointer.
+    h_addrtype*: cint          ## Address type.
+    h_length*: cint            ## The length, in bytes, of the address.
+    h_addr_list*: cstringArray ## A pointer to an array of pointers to network
+                               ## addresses (in network byte order) for the
+                               ## host, terminated by a null pointer.
+
+  Tnetent* {.importc: "struct netent", pure, final,
+              header: "<netdb.h>".} = object ## struct netent
+    n_name*: cstring         ## Official, fully-qualified (including the
+                             ## domain) name of the host.
+    n_aliases*: cstringArray ## A pointer to an array of pointers to
+                             ## alternative network names, terminated by a
+                             ## null pointer.
+    n_addrtype*: cint        ## The address type of the network.
+    n_net*: uint32            ## The network number, in host byte order.
+
+  Protoent* {.importc: "struct protoent", pure, final,
+              header: "<netdb.h>".} = object ## struct protoent
+    p_name*: cstring         ## Official name of the protocol.
+    p_aliases*: cstringArray ## A pointer to an array of pointers to
+                             ## alternative protocol names, terminated by
+                             ## a null pointer.
+    p_proto*: cint           ## The protocol number.
+
+  Servent* {.importc: "struct servent", pure, final,
+             header: "<netdb.h>".} = object ## struct servent
+    s_name*: cstring         ## Official name of the service.
+    s_aliases*: cstringArray ## A pointer to an array of pointers to
+                             ## alternative service names, terminated by
+                             ## a null pointer.
+    s_port*: cint            ## The port number at which the service
+                             ## resides, in network byte order.
+    s_proto*: cstring        ## The name of the protocol to use when
+                             ## contacting the service.
+
+  AddrInfo* {.importc: "struct addrinfo", pure, final,
+              header: "<netdb.h>".} = object ## struct addrinfo
+    ai_flags*: cint         ## Input flags.
+    ai_family*: cint        ## Address family of socket.
+    ai_socktype*: cint      ## Socket type.
+    ai_protocol*: cint      ## Protocol of socket.
+    ai_addrlen*: SockLen   ## Length of socket address.
+    ai_canonname*: cstring  ## Canonical name of service location.
+    ai_addr*: ptr SockAddr ## Socket address of socket.
+    ai_next*: ptr AddrInfo ## Pointer to next in list.
+
+  TPollfd* {.importc: "struct pollfd", pure, final,
+             header: "<poll.h>".} = object ## struct pollfd
+    fd*: cint        ## The following descriptor being polled.
+    events*: cshort  ## The input event flags (see below).
+    revents*: cshort ## The output event flags (see below).
+
+  Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = culong
+
+var
+  errno* {.importc, header: "<errno.h>".}: cint ## error variable
+  h_errno* {.importc, header: "<netdb.h>".}: cint
+  daylight* {.importc: "_daylight", header: "<time.h>".}: cint
+  timezone* {.importc: "_timezone", header: "<time.h>".}: clong
+
+# Regenerate using detect.nim!
+include posix_nintendoswitch_consts
+
+const POSIX_SPAWN_USEVFORK* = cint(0x40)  # needs _GNU_SOURCE!
+
+# <sys/wait.h>
+proc WEXITSTATUS*(s: cint): cint =  (s shr 8) and 0xff
+proc WIFEXITED*(s:cint) : bool = (s and 0xff) == 0
+proc WTERMSIG*(s:cint): cint = s and 0x7f
+proc WSTOPSIG*(s:cint): cint = WEXITSTATUS(s)
+proc WIFSIGNALED*(s:cint) : bool = ((s and 0x7f) > 0) and ((s and 0x7f) < 0x7f)
+proc WIFSTOPPED*(s:cint) : bool = (s and 0xff) == 0x7f
diff --git a/lib/posix/posix_nintendoswitch_consts.nim b/lib/posix/posix_nintendoswitch_consts.nim
new file mode 100644
index 000000000..8cd8824b5
--- /dev/null
+++ b/lib/posix/posix_nintendoswitch_consts.nim
@@ -0,0 +1,587 @@
+# Generated by detect.nim
+
+
+# <aio.h>
+
+# <dlfcn.h>
+
+# <errno.h>
+const E2BIG* = cint(7)
+const EACCES* = cint(13)
+const EADDRINUSE* = cint(112)
+const EADDRNOTAVAIL* = cint(125)
+const EAFNOSUPPORT* = cint(106)
+const EAGAIN* = cint(11)
+const EALREADY* = cint(120)
+const EBADF* = cint(9)
+const EBADMSG* = cint(77)
+const EBUSY* = cint(16)
+const ECANCELED* = cint(140)
+const ECHILD* = cint(10)
+const ECONNABORTED* = cint(113)
+const ECONNREFUSED* = cint(111)
+const ECONNRESET* = cint(104)
+const EDEADLK* = cint(45)
+const EDESTADDRREQ* = cint(121)
+const EDOM* = cint(33)
+const EDQUOT* = cint(132)
+const EEXIST* = cint(17)
+const EFAULT* = cint(14)
+const EFBIG* = cint(27)
+const EHOSTUNREACH* = cint(118)
+const EIDRM* = cint(36)
+const EILSEQ* = cint(138)
+const EINPROGRESS* = cint(119)
+const EINTR* = cint(4)
+const EINVAL* = cint(22)
+const EIO* = cint(5)
+const EISCONN* = cint(127)
+const EISDIR* = cint(21)
+const ELOOP* = cint(92)
+const EMFILE* = cint(24)
+const EMLINK* = cint(31)
+const EMSGSIZE* = cint(122)
+const EMULTIHOP* = cint(74)
+const ENAMETOOLONG* = cint(91)
+const ENETDOWN* = cint(115)
+const ENETRESET* = cint(126)
+const ENETUNREACH* = cint(114)
+const ENFILE* = cint(23)
+const ENOBUFS* = cint(105)
+const ENODATA* = cint(61)
+const ENODEV* = cint(19)
+const ENOENT* = cint(2)
+const ENOEXEC* = cint(8)
+const ENOLCK* = cint(46)
+const ENOLINK* = cint(67)
+const ENOMEM* = cint(12)
+const ENOMSG* = cint(35)
+const ENOPROTOOPT* = cint(109)
+const ENOSPC* = cint(28)
+const ENOSR* = cint(63)
+const ENOSTR* = cint(60)
+const ENOSYS* = cint(88)
+const ENOTCONN* = cint(128)
+const ENOTDIR* = cint(20)
+const ENOTEMPTY* = cint(90)
+const ENOTSOCK* = cint(108)
+const ENOTSUP* = cint(134)
+const ENOTTY* = cint(25)
+const ENXIO* = cint(6)
+const EOPNOTSUPP* = cint(95)
+const EOVERFLOW* = cint(139)
+const EPERM* = cint(1)
+const EPIPE* = cint(32)
+const EPROTO* = cint(71)
+const EPROTONOSUPPORT* = cint(123)
+const EPROTOTYPE* = cint(107)
+const ERANGE* = cint(34)
+const EROFS* = cint(30)
+const ESPIPE* = cint(29)
+const ESRCH* = cint(3)
+const ESTALE* = cint(133)
+const ETIME* = cint(62)
+const ETIMEDOUT* = cint(116)
+const ETXTBSY* = cint(26)
+const EWOULDBLOCK* = cint(11)
+const EXDEV* = cint(18)
+
+# <fcntl.h>
+const F_DUPFD* = cint(0)
+const F_GETFD* = cint(1)
+const F_SETFD* = cint(2)
+const F_GETFL* = cint(3)
+const F_SETFL* = cint(4)
+const F_GETLK* = cint(7)
+const F_SETLK* = cint(8)
+const F_SETLKW* = cint(9)
+const F_GETOWN* = cint(5)
+const F_SETOWN* = cint(6)
+const FD_CLOEXEC* = cint(1)
+const F_RDLCK* = cint(1)
+const F_UNLCK* = cint(3)
+const F_WRLCK* = cint(2)
+const O_CREAT* = cint(512)
+const O_EXCL* = cint(2048)
+const O_NOCTTY* = cint(32768)
+const O_TRUNC* = cint(1024)
+const O_APPEND* = cint(8)
+const O_NONBLOCK* = cint(16384)
+const O_SYNC* = cint(8192)
+const O_ACCMODE* = cint(3)
+const O_RDONLY* = cint(0)
+const O_RDWR* = cint(2)
+const O_WRONLY* = cint(1)
+
+# <fenv.h>
+
+# <fmtmsg.h>
+
+# <fnmatch.h>
+const FNM_NOMATCH* = cint(1)
+const FNM_PATHNAME* = cint(2)
+const FNM_PERIOD* = cint(4)
+const FNM_NOESCAPE* = cint(1)
+
+# <ftw.h>
+
+# <glob.h>
+const GLOB_APPEND* = cint(1)
+const GLOB_DOOFFS* = cint(2)
+const GLOB_ERR* = cint(4)
+const GLOB_MARK* = cint(8)
+const GLOB_NOCHECK* = cint(16)
+const GLOB_NOSORT* = cint(32)
+const GLOB_NOSPACE* = cint(-1)
+
+# <langinfo.h>
+const CODESET* = cint(0)
+const D_T_FMT* = cint(1)
+const D_FMT* = cint(2)
+const T_FMT* = cint(3)
+const T_FMT_AMPM* = cint(4)
+const AM_STR* = cint(5)
+const PM_STR* = cint(6)
+const DAY_1* = cint(7)
+const DAY_2* = cint(8)
+const DAY_3* = cint(9)
+const DAY_4* = cint(10)
+const DAY_5* = cint(11)
+const DAY_6* = cint(12)
+const DAY_7* = cint(13)
+const ABDAY_1* = cint(14)
+const ABDAY_2* = cint(15)
+const ABDAY_3* = cint(16)
+const ABDAY_4* = cint(17)
+const ABDAY_5* = cint(18)
+const ABDAY_6* = cint(19)
+const ABDAY_7* = cint(20)
+const MON_1* = cint(21)
+const MON_2* = cint(22)
+const MON_3* = cint(23)
+const MON_4* = cint(24)
+const MON_5* = cint(25)
+const MON_6* = cint(26)
+const MON_7* = cint(27)
+const MON_8* = cint(28)
+const MON_9* = cint(29)
+const MON_10* = cint(30)
+const MON_11* = cint(31)
+const MON_12* = cint(32)
+const ABMON_1* = cint(33)
+const ABMON_2* = cint(34)
+const ABMON_3* = cint(35)
+const ABMON_4* = cint(36)
+const ABMON_5* = cint(37)
+const ABMON_6* = cint(38)
+const ABMON_7* = cint(39)
+const ABMON_8* = cint(40)
+const ABMON_9* = cint(41)
+const ABMON_10* = cint(42)
+const ABMON_11* = cint(43)
+const ABMON_12* = cint(44)
+const ERA* = cint(45)
+const ERA_D_FMT* = cint(46)
+const ERA_D_T_FMT* = cint(47)
+const ERA_T_FMT* = cint(48)
+const ALT_DIGITS* = cint(49)
+const RADIXCHAR* = cint(50)
+const THOUSEP* = cint(51)
+const YESEXPR* = cint(52)
+const NOEXPR* = cint(53)
+const CRNCYSTR* = cint(56)
+
+# <locale.h>
+const LC_ALL* = cint(0)
+const LC_COLLATE* = cint(1)
+const LC_CTYPE* = cint(2)
+const LC_MESSAGES* = cint(6)
+const LC_MONETARY* = cint(3)
+const LC_NUMERIC* = cint(4)
+const LC_TIME* = cint(5)
+
+# <netdb.h>
+const IPPORT_RESERVED* = cint(1024)
+const HOST_NOT_FOUND* = cint(1)
+const NO_DATA* = cint(4)
+const NO_RECOVERY* = cint(3)
+const TRY_AGAIN* = cint(2)
+const AI_PASSIVE* = cint(1)
+const AI_CANONNAME* = cint(2)
+const AI_NUMERICHOST* = cint(4)
+const AI_NUMERICSERV* = cint(8)
+const AI_V4MAPPED* = cint(2048)
+const AI_ALL* = cint(256)
+const AI_ADDRCONFIG* = cint(1024)
+const NI_NOFQDN* = cint(1)
+const NI_NUMERICHOST* = cint(2)
+const NI_NAMEREQD* = cint(4)
+const NI_NUMERICSERV* = cint(8)
+const NI_NUMERICSCOPE* = cint(32)
+const NI_DGRAM* = cint(16)
+const EAI_AGAIN* = cint(2)
+const EAI_BADFLAGS* = cint(3)
+const EAI_FAIL* = cint(4)
+const EAI_FAMILY* = cint(5)
+const EAI_MEMORY* = cint(6)
+const EAI_NONAME* = cint(8)
+const EAI_SERVICE* = cint(9)
+const EAI_SOCKTYPE* = cint(10)
+const EAI_SYSTEM* = cint(11)
+const EAI_OVERFLOW* = cint(14)
+
+# <net/if.h>
+const IF_NAMESIZE* = cint(16)
+
+# <netinet/in.h>
+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)
+const INADDR_ANY* = InAddrScalar(0)
+const INADDR_LOOPBACK* = InAddrScalar(2130706433)
+const INADDR_BROADCAST* = InAddrScalar(4294967295)
+const INET_ADDRSTRLEN* = cint(16)
+const INET6_ADDRSTRLEN* = cint(46)
+const IPV6_JOIN_GROUP* = cint(12)
+const IPV6_LEAVE_GROUP* = cint(13)
+const IPV6_MULTICAST_HOPS* = cint(10)
+const IPV6_MULTICAST_IF* = cint(9)
+const IPV6_MULTICAST_LOOP* = cint(11)
+const IPV6_UNICAST_HOPS* = cint(4)
+const IPV6_V6ONLY* = cint(27)
+
+# <netinet/tcp.h>
+const TCP_NODELAY* = cint(1)
+
+# <nl_types.h>
+
+# <poll.h>
+const POLLIN* = cshort(1)
+const POLLRDNORM* = cshort(64)
+const POLLRDBAND* = cshort(128)
+const POLLPRI* = cshort(2)
+const POLLOUT* = cshort(4)
+const POLLWRNORM* = cshort(4)
+const POLLWRBAND* = cshort(256)
+const POLLERR* = cshort(8)
+const POLLHUP* = cshort(16)
+const POLLNVAL* = cshort(32)
+
+# <pthread.h>
+const PTHREAD_CREATE_DETACHED* = cint(0)
+const PTHREAD_CREATE_JOINABLE* = cint(1)
+const PTHREAD_EXPLICIT_SCHED* = cint(2)
+const PTHREAD_INHERIT_SCHED* = cint(1)
+const PTHREAD_SCOPE_PROCESS* = cint(0)
+const PTHREAD_SCOPE_SYSTEM* = cint(1)
+
+# <sched.h>
+const SCHED_FIFO* = cint(1)
+const SCHED_RR* = cint(2)
+const SCHED_OTHER* = cint(0)
+
+# <semaphore.h>
+
+# <signal.h>
+const SIGEV_NONE* = cint(1)
+const SIGEV_SIGNAL* = cint(2)
+const SIGEV_THREAD* = cint(3)
+const SIGABRT* = cint(6)
+const SIGALRM* = cint(14)
+const SIGBUS* = cint(10)
+const SIGCHLD* = cint(20)
+const SIGCONT* = cint(19)
+const SIGFPE* = cint(8)
+const SIGHUP* = cint(1)
+const SIGILL* = cint(4)
+const SIGINT* = cint(2)
+const SIGKILL* = cint(9)
+const SIGPIPE* = cint(13)
+const SIGQUIT* = cint(3)
+const SIGSEGV* = cint(11)
+const SIGSTOP* = cint(17)
+const SIGTERM* = cint(15)
+const SIGTSTP* = cint(18)
+const SIGTTIN* = cint(21)
+const SIGTTOU* = cint(22)
+const SIGUSR1* = cint(30)
+const SIGUSR2* = cint(31)
+const SIGPOLL* = cint(23)
+const SIGPROF* = cint(27)
+const SIGSYS* = cint(12)
+const SIGTRAP* = cint(5)
+const SIGURG* = cint(16)
+const SIGVTALRM* = cint(26)
+const SIGXCPU* = cint(24)
+const SIGXFSZ* = cint(25)
+const SA_NOCLDSTOP* = cint(1)
+const SIG_BLOCK* = cint(1)
+const SIG_UNBLOCK* = cint(2)
+const SIG_SETMASK* = cint(0)
+const SS_ONSTACK* = cint(1)
+const SS_DISABLE* = cint(2)
+const MINSIGSTKSZ* = cint(2048)
+const SIGSTKSZ* = cint(8192)
+const SIG_DFL* = cast[Sighandler](0)
+const SIG_ERR* = cast[Sighandler](-1)
+const SIG_IGN* = cast[Sighandler](1)
+
+# <sys/ipc.h>
+
+# <sys/mman.h>
+
+# <sys/resource.h>
+
+# <sys/select.h>
+const FD_SETSIZE* = cint(64)
+
+# <sys/socket.h>
+const MSG_CTRUNC* = cint(32)
+const MSG_DONTROUTE* = cint(4)
+const MSG_EOR* = cint(8)
+const MSG_OOB* = cint(1)
+const SCM_RIGHTS* = cint(1)
+const SO_ACCEPTCONN* = cint(2)
+const SO_BROADCAST* = cint(32)
+const SO_DEBUG* = cint(1)
+const SO_DONTROUTE* = cint(16)
+const SO_ERROR* = cint(4103)
+const SO_KEEPALIVE* = cint(8)
+const SO_LINGER* = cint(128)
+const SO_OOBINLINE* = cint(256)
+const SO_RCVBUF* = cint(4098)
+const SO_RCVLOWAT* = cint(4100)
+const SO_RCVTIMEO* = cint(4102)
+const SO_REUSEADDR* = cint(4)
+const SO_SNDBUF* = cint(4097)
+const SO_SNDLOWAT* = cint(4099)
+const SO_SNDTIMEO* = cint(4101)
+const SO_TYPE* = cint(4104)
+const SOCK_DGRAM* = cint(2)
+const SOCK_RAW* = cint(3)
+const SOCK_SEQPACKET* = cint(5)
+const SOCK_STREAM* = cint(1)
+const SOL_SOCKET* = cint(65535)
+const SOMAXCONN* = cint(128)
+const SO_REUSEPORT* = cint(512)
+const MSG_NOSIGNAL* = cint(131072)
+const MSG_PEEK* = cint(2)
+const MSG_TRUNC* = cint(16)
+const MSG_WAITALL* = cint(64)
+const AF_INET* = cint(2)
+const AF_INET6* = cint(28)
+const AF_UNIX* = cint(1)
+const AF_UNSPEC* = cint(0)
+const SHUT_RD* = cint(0)
+const SHUT_RDWR* = cint(2)
+const SHUT_WR* = cint(1)
+
+# <sys/stat.h>
+const S_IFBLK* = cint(24576)
+const S_IFCHR* = cint(8192)
+const S_IFDIR* = cint(16384)
+const S_IFIFO* = cint(4096)
+const S_IFLNK* = cint(40960)
+const S_IFMT* = cint(61440)
+const S_IFREG* = cint(32768)
+const S_IFSOCK* = cint(49152)
+const S_IRGRP* = cint(32)
+const S_IROTH* = cint(4)
+const S_IRUSR* = cint(256)
+const S_IRWXG* = cint(56)
+const S_IRWXO* = cint(7)
+const S_IRWXU* = cint(448)
+const S_ISGID* = cint(1024)
+const S_ISUID* = cint(2048)
+const S_ISVTX* = cint(512)
+const S_IWGRP* = cint(16)
+const S_IWOTH* = cint(2)
+const S_IWUSR* = cint(128)
+const S_IXGRP* = cint(8)
+const S_IXOTH* = cint(1)
+const S_IXUSR* = cint(64)
+
+# <sys/statvfs.h>
+const ST_RDONLY* = cint(1)
+const ST_NOSUID* = cint(2)
+
+# <sys/wait.h>
+const WNOHANG* = cint(1)
+const WUNTRACED* = cint(2)
+
+# <spawn.h>
+const POSIX_SPAWN_RESETIDS* = cint(1)
+const POSIX_SPAWN_SETPGROUP* = cint(2)
+const POSIX_SPAWN_SETSCHEDPARAM* = cint(4)
+const POSIX_SPAWN_SETSCHEDULER* = cint(8)
+const POSIX_SPAWN_SETSIGDEF* = cint(16)
+const POSIX_SPAWN_SETSIGMASK* = cint(32)
+
+# <stdio.h>
+const IOFBF* = cint(0)
+const IONBF* = cint(2)
+
+# <time.h>
+const CLOCKS_PER_SEC* = clong(100)
+const CLOCK_REALTIME* = cint(1)
+const TIMER_ABSTIME* = cint(4)
+const CLOCK_MONOTONIC* = cint(4)
+
+# <unistd.h>
+const F_OK* = cint(0)
+const R_OK* = cint(4)
+const W_OK* = cint(2)
+const X_OK* = cint(1)
+const F_LOCK* = cint(1)
+const F_TEST* = cint(3)
+const F_TLOCK* = cint(2)
+const F_ULOCK* = cint(0)
+const PC_2_SYMLINKS* = cint(13)
+const PC_ALLOC_SIZE_MIN* = cint(15)
+const PC_ASYNC_IO* = cint(9)
+const PC_CHOWN_RESTRICTED* = cint(6)
+const PC_FILESIZEBITS* = cint(12)
+const PC_LINK_MAX* = cint(0)
+const PC_MAX_CANON* = cint(1)
+const PC_MAX_INPUT* = cint(2)
+const PC_NAME_MAX* = cint(3)
+const PC_NO_TRUNC* = cint(7)
+const PC_PATH_MAX* = cint(4)
+const PC_PIPE_BUF* = cint(5)
+const PC_PRIO_IO* = cint(10)
+const PC_REC_INCR_XFER_SIZE* = cint(16)
+const PC_REC_MIN_XFER_SIZE* = cint(18)
+const PC_REC_XFER_ALIGN* = cint(19)
+const PC_SYMLINK_MAX* = cint(14)
+const PC_SYNC_IO* = cint(11)
+const PC_VDISABLE* = cint(8)
+const SC_2_C_BIND* = cint(108)
+const SC_2_C_DEV* = cint(109)
+const SC_2_CHAR_TERM* = cint(107)
+const SC_2_FORT_DEV* = cint(110)
+const SC_2_FORT_RUN* = cint(111)
+const SC_2_LOCALEDEF* = cint(112)
+const SC_2_PBS* = cint(113)
+const SC_2_PBS_ACCOUNTING* = cint(114)
+const SC_2_PBS_CHECKPOINT* = cint(115)
+const SC_2_PBS_LOCATE* = cint(116)
+const SC_2_PBS_MESSAGE* = cint(117)
+const SC_2_PBS_TRACK* = cint(118)
+const SC_2_SW_DEV* = cint(119)
+const SC_2_UPE* = cint(120)
+const SC_2_VERSION* = cint(121)
+const SC_ADVISORY_INFO* = cint(54)
+const SC_AIO_LISTIO_MAX* = cint(34)
+const SC_AIO_MAX* = cint(35)
+const SC_AIO_PRIO_DELTA_MAX* = cint(36)
+const SC_ARG_MAX* = cint(0)
+const SC_ASYNCHRONOUS_IO* = cint(21)
+const SC_ATEXIT_MAX* = cint(55)
+const SC_BARRIERS* = cint(56)
+const SC_BC_BASE_MAX* = cint(57)
+const SC_BC_DIM_MAX* = cint(58)
+const SC_BC_SCALE_MAX* = cint(59)
+const SC_BC_STRING_MAX* = cint(60)
+const SC_CHILD_MAX* = cint(1)
+const SC_CLK_TCK* = cint(2)
+const SC_CLOCK_SELECTION* = cint(61)
+const SC_COLL_WEIGHTS_MAX* = cint(62)
+const SC_CPUTIME* = cint(63)
+const SC_DELAYTIMER_MAX* = cint(37)
+const SC_EXPR_NEST_MAX* = cint(64)
+const SC_FSYNC* = cint(22)
+const SC_GETGR_R_SIZE_MAX* = cint(50)
+const SC_GETPW_R_SIZE_MAX* = cint(51)
+const SC_HOST_NAME_MAX* = cint(65)
+const SC_IOV_MAX* = cint(66)
+const SC_IPV6* = cint(67)
+const SC_JOB_CONTROL* = cint(5)
+const SC_LINE_MAX* = cint(68)
+const SC_LOGIN_NAME_MAX* = cint(52)
+const SC_MAPPED_FILES* = cint(23)
+const SC_MEMLOCK* = cint(24)
+const SC_MEMLOCK_RANGE* = cint(25)
+const SC_MEMORY_PROTECTION* = cint(26)
+const SC_MESSAGE_PASSING* = cint(27)
+const SC_MONOTONIC_CLOCK* = cint(69)
+const SC_MQ_OPEN_MAX* = cint(13)
+const SC_MQ_PRIO_MAX* = cint(14)
+const SC_NGROUPS_MAX* = cint(3)
+const SC_OPEN_MAX* = cint(4)
+const SC_PAGE_SIZE* = cint(8)
+const SC_PRIORITIZED_IO* = cint(28)
+const SC_PRIORITY_SCHEDULING* = cint(101)
+const SC_RAW_SOCKETS* = cint(70)
+const SC_RE_DUP_MAX* = cint(73)
+const SC_READER_WRITER_LOCKS* = cint(71)
+const SC_REALTIME_SIGNALS* = cint(29)
+const SC_REGEXP* = cint(72)
+const SC_RTSIG_MAX* = cint(15)
+const SC_SAVED_IDS* = cint(6)
+const SC_SEM_NSEMS_MAX* = cint(16)
+const SC_SEM_VALUE_MAX* = cint(17)
+const SC_SEMAPHORES* = cint(30)
+const SC_SHARED_MEMORY_OBJECTS* = cint(31)
+const SC_SHELL* = cint(74)
+const SC_SIGQUEUE_MAX* = cint(18)
+const SC_SPAWN* = cint(75)
+const SC_SPIN_LOCKS* = cint(76)
+const SC_SPORADIC_SERVER* = cint(77)
+const SC_SS_REPL_MAX* = cint(78)
+const SC_STREAM_MAX* = cint(100)
+const SC_SYMLOOP_MAX* = cint(79)
+const SC_SYNCHRONIZED_IO* = cint(32)
+const SC_THREAD_ATTR_STACKADDR* = cint(43)
+const SC_THREAD_ATTR_STACKSIZE* = cint(44)
+const SC_THREAD_CPUTIME* = cint(80)
+const SC_THREAD_DESTRUCTOR_ITERATIONS* = cint(53)
+const SC_THREAD_KEYS_MAX* = cint(38)
+const SC_THREAD_PRIO_INHERIT* = cint(46)
+const SC_THREAD_PRIO_PROTECT* = cint(47)
+const SC_THREAD_PRIORITY_SCHEDULING* = cint(45)
+const SC_THREAD_PROCESS_SHARED* = cint(48)
+const SC_THREAD_SAFE_FUNCTIONS* = cint(49)
+const SC_THREAD_SPORADIC_SERVER* = cint(81)
+const SC_THREAD_STACK_MIN* = cint(39)
+const SC_THREAD_THREADS_MAX* = cint(40)
+const SC_THREADS* = cint(42)
+const SC_TIMEOUTS* = cint(82)
+const SC_TIMER_MAX* = cint(19)
+const SC_TIMERS* = cint(33)
+const SC_TRACE* = cint(83)
+const SC_TRACE_EVENT_FILTER* = cint(84)
+const SC_TRACE_EVENT_NAME_MAX* = cint(85)
+const SC_TRACE_INHERIT* = cint(86)
+const SC_TRACE_LOG* = cint(87)
+const SC_TRACE_NAME_MAX* = cint(88)
+const SC_TRACE_SYS_MAX* = cint(89)
+const SC_TRACE_USER_EVENT_MAX* = cint(90)
+const SC_TTY_NAME_MAX* = cint(41)
+const SC_TYPED_MEMORY_OBJECTS* = cint(91)
+const SC_TZNAME_MAX* = cint(20)
+const SC_V6_ILP32_OFF32* = cint(92)
+const SC_V6_ILP32_OFFBIG* = cint(93)
+const SC_V6_LP64_OFF64* = cint(94)
+const SC_V6_LPBIG_OFFBIG* = cint(95)
+const SC_VERSION* = cint(7)
+const SC_XBS5_ILP32_OFF32* = cint(92)
+const SC_XBS5_ILP32_OFFBIG* = cint(93)
+const SC_XBS5_LP64_OFF64* = cint(94)
+const SC_XBS5_LPBIG_OFFBIG* = cint(95)
+const SC_XOPEN_CRYPT* = cint(96)
+const SC_XOPEN_ENH_I18N* = cint(97)
+const SC_XOPEN_LEGACY* = cint(98)
+const SC_XOPEN_REALTIME* = cint(99)
+const SC_XOPEN_REALTIME_THREADS* = cint(102)
+const SC_XOPEN_SHM* = cint(103)
+const SC_XOPEN_STREAMS* = cint(104)
+const SC_XOPEN_UNIX* = cint(105)
+const SC_XOPEN_VERSION* = cint(106)
+const SC_NPROCESSORS_ONLN* = cint(10)
+const SEEK_SET* = cint(0)
+const SEEK_CUR* = cint(1)
+const SEEK_END* = cint(2)
diff --git a/lib/posix/posix_openbsd_amd64.nim b/lib/posix/posix_openbsd_amd64.nim
new file mode 100644
index 000000000..184cd89c0
--- /dev/null
+++ b/lib/posix/posix_openbsd_amd64.nim
@@ -0,0 +1,565 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+when defined(nimHasStyleChecks):
+  {.push styleChecks: off.}
+
+const
+  hasSpawnH = true # should exist for every Posix system nowadays
+  hasAioH = false
+
+type
+  DIR* {.importc: "DIR", header: "<dirent.h>",
+          incompleteStruct.} = object
+    ## A type representing a directory stream.
+
+type
+  SocketHandle* = distinct cint # The type used to represent socket descriptors
+
+type
+  Time* {.importc: "time_t", header: "<time.h>".} = distinct clonglong
+
+  Timespec* {.importc: "struct timespec",
+               header: "<time.h>", final, pure.} = object ## struct timespec
+    tv_sec*: Time  ## Seconds.
+    tv_nsec*: int  ## Nanoseconds.
+
+  Dirent* {.importc: "struct dirent",
+             header: "<dirent.h>", final, pure.} = object ## dirent_t struct
+    when defined(haiku):
+      d_dev*: Dev ## Device (not POSIX)
+      d_pdev*: Dev ## Parent device (only for queries) (not POSIX)
+    d_ino*: Ino  ## File serial number.
+    when defined(dragonfly):
+      # DragonflyBSD doesn't have `d_reclen` field.
+      d_type*: uint8
+    elif defined(linux) or defined(macosx) or defined(freebsd) or
+         defined(netbsd) or defined(openbsd) or defined(genode):
+      d_reclen*: cshort ## Length of this record. (not POSIX)
+      d_type*: int8 ## Type of file; not supported by all filesystem types.
+                    ## (not POSIX)
+      when defined(linux) or defined(openbsd):
+        d_off*: Off  ## Not an offset. Value that `telldir()` would return.
+    elif defined(haiku):
+      d_pino*: Ino ## Parent inode (only for queries) (not POSIX)
+      d_reclen*: cushort ## Length of this record. (not POSIX)
+
+    d_name*: array[0..255, char] ## Name of entry.
+
+  Tflock* {.importc: "struct flock", final, pure,
+            header: "<fcntl.h>".} = object ## flock type
+    l_type*: cshort   ## Type of lock; F_RDLCK, F_WRLCK, F_UNLCK.
+    l_whence*: cshort ## Flag for starting offset.
+    l_start*: Off     ## Relative offset in bytes.
+    l_len*: Off       ## Size; if 0 then until EOF.
+    l_pid*: Pid      ## Process ID of the process holding the lock;
+                      ## returned with F_GETLK.
+
+  FTW* {.importc: "struct FTW", header: "<ftw.h>", final, pure.} = object
+    base*: cint
+    level*: cint
+
+  Glob* {.importc: "glob_t", header: "<glob.h>",
+           final, pure.} = object ## glob_t
+    gl_pathc*: int          ## Count of paths matched by pattern.
+    gl_pathv*: cstringArray ## Pointer to a list of matched pathnames.
+    gl_offs*: int           ## Slots to reserve at the beginning of gl_pathv.
+
+  Group* {.importc: "struct group", header: "<grp.h>",
+            final, pure.} = object ## struct group
+    gr_name*: cstring     ## The name of the group.
+    gr_gid*: Gid         ## Numerical group ID.
+    gr_mem*: cstringArray ## Pointer to a null-terminated array of character
+                          ## pointers to member names.
+
+  Iconv* {.importc: "iconv_t", header: "<iconv.h>", final, pure.} =
+    object ## Identifies the conversion from one codeset to another.
+
+  Lconv* {.importc: "struct lconv", header: "<locale.h>", final,
+            pure.} = object
+    currency_symbol*: cstring
+    decimal_point*: cstring
+    frac_digits*: char
+    grouping*: cstring
+    int_curr_symbol*: cstring
+    int_frac_digits*: char
+    int_n_cs_precedes*: char
+    int_n_sep_by_space*: char
+    int_n_sign_posn*: char
+    int_p_cs_precedes*: char
+    int_p_sep_by_space*: char
+    int_p_sign_posn*: char
+    mon_decimal_point*: cstring
+    mon_grouping*: cstring
+    mon_thousands_sep*: cstring
+    negative_sign*: cstring
+    n_cs_precedes*: char
+    n_sep_by_space*: char
+    n_sign_posn*: char
+    positive_sign*: cstring
+    p_cs_precedes*: char
+    p_sep_by_space*: char
+    p_sign_posn*: char
+    thousands_sep*: cstring
+
+  Mqd* {.importc: "mqd_t", header: "<mqueue.h>", final, pure.} = object
+  MqAttr* {.importc: "struct mq_attr",
+             header: "<mqueue.h>",
+             final, pure.} = object ## message queue attribute
+    mq_flags*: int   ## Message queue flags.
+    mq_maxmsg*: int  ## Maximum number of messages.
+    mq_msgsize*: int ## Maximum message size.
+    mq_curmsgs*: int ## Number of messages currently queued.
+
+  Passwd* {.importc: "struct passwd", header: "<pwd.h>",
+             final, pure.} = object ## struct passwd
+    pw_name*: cstring   ## User's login name.
+    pw_uid*: Uid        ## Numerical user ID.
+    pw_gid*: Gid        ## Numerical group ID.
+    pw_dir*: cstring    ## Initial working directory.
+    pw_shell*: cstring  ## Program to use as shell.
+
+  Blkcnt* {.importc: "blkcnt_t", header: "<sys/types.h>".} = int
+    ## used for file block counts
+  Blksize* {.importc: "blksize_t", header: "<sys/types.h>".} = int32
+    ## used for block sizes
+  Clock* {.importc: "clock_t", header: "<sys/types.h>".} = int
+  ClockId* {.importc: "clockid_t", header: "<sys/types.h>".} = int
+  Dev* {.importc: "dev_t", header: "<sys/types.h>".} = (
+    when defined(freebsd):
+      uint32
+    else:
+      int32)
+  Fsblkcnt* {.importc: "fsblkcnt_t", header: "<sys/types.h>".} = int
+  Fsfilcnt* {.importc: "fsfilcnt_t", header: "<sys/types.h>".} = int
+  Gid* {.importc: "gid_t", header: "<sys/types.h>".} = uint32
+  Id* {.importc: "id_t", header: "<sys/types.h>".} = int
+  Ino* {.importc: "ino_t", header: "<sys/types.h>".} = int
+  Key* {.importc: "key_t", header: "<sys/types.h>".} = int
+  Mode* {.importc: "mode_t", header: "<sys/types.h>".} = uint32
+  Nlink* {.importc: "nlink_t", header: "<sys/types.h>".} = uint32
+  Off* {.importc: "off_t", header: "<sys/types.h>".} = int64
+  Pid* {.importc: "pid_t", header: "<sys/types.h>".} = int32
+  Pthread_attr* {.importc: "pthread_attr_t", header: "<pthread.h>".} = int
+  Pthread_barrier* {.importc: "pthread_barrier_t",
+                      header: "<pthread.h>".} = int
+  Pthread_barrierattr* {.importc: "pthread_barrierattr_t",
+                          header: "<pthread.h>".} = int
+  Pthread_cond* {.importc: "pthread_cond_t", header: "<pthread.h>".} = int
+  Pthread_condattr* {.importc: "pthread_condattr_t",
+                       header: "<pthread.h>".} = int
+  Pthread_key* {.importc: "pthread_key_t", header: "<pthread.h>".} = int
+  Pthread_mutex* {.importc: "pthread_mutex_t", header: "<pthread.h>".} = int
+  Pthread_mutexattr* {.importc: "pthread_mutexattr_t",
+                        header: "<pthread.h>".} = int
+  Pthread_once* {.importc: "pthread_once_t", header: "<pthread.h>".} = int
+  Pthread_rwlock* {.importc: "pthread_rwlock_t",
+                     header: "<pthread.h>".} = int
+  Pthread_rwlockattr* {.importc: "pthread_rwlockattr_t",
+                         header: "<pthread.h>".} = int
+  Pthread_spinlock* {.importc: "pthread_spinlock_t",
+                       header: "<pthread.h>".} = int
+  Pthread* {.importc: "pthread_t", header: "<pthread.h>".} = int
+  Suseconds* {.importc: "suseconds_t", header: "<sys/types.h>".} = int32
+  #Ttime* {.importc: "time_t", header: "<sys/types.h>".} = int
+  Timer* {.importc: "timer_t", header: "<sys/types.h>".} = int
+  Trace_attr* {.importc: "trace_attr_t", header: "<sys/types.h>".} = int
+  Trace_event_id* {.importc: "trace_event_id_t",
+                     header: "<sys/types.h>".} = int
+  Trace_event_set* {.importc: "trace_event_set_t",
+                      header: "<sys/types.h>".} = int
+  Trace_id* {.importc: "trace_id_t", header: "<sys/types.h>".} = int
+  Uid* {.importc: "uid_t", header: "<sys/types.h>".} = uint32
+  Useconds* {.importc: "useconds_t", header: "<sys/types.h>".} = int
+
+  Utsname* {.importc: "struct utsname",
+              header: "<sys/utsname.h>",
+              final, pure.} = object ## struct utsname
+    sysname*,      ## Name of this implementation of the operating system.
+      nodename*,   ## Name of this node within the communications
+                   ## network to which this node is attached, if any.
+      release*,    ## Current release level of this implementation.
+      version*,    ## Current version level of this release.
+      machine*: array[0..255, char] ## Name of the hardware type on which the
+                                     ## system is running.
+
+  Sem* {.importc: "sem_t", header: "<semaphore.h>", final, pure.} = object
+  Ipc_perm* {.importc: "struct ipc_perm",
+               header: "<sys/ipc.h>", final, pure.} = object ## struct ipc_perm
+    uid*: Uid    ## Owner's user ID.
+    gid*: Gid    ## Owner's group ID.
+    cuid*: Uid   ## Creator's user ID.
+    cgid*: Gid   ## Creator's group ID.
+    mode*: Mode  ## Read/write permission.
+
+  Stat* {.importc: "struct stat",
+           header: "<sys/stat.h>", final, pure.} = object ## struct stat
+    st_dev*: Dev          ## Device ID of device containing file.
+    st_ino*: Ino          ## File serial number.
+    st_mode*: Mode        ## Mode of file (see below).
+    st_nlink*: Nlink      ## Number of hard links to the file.
+    st_uid*: Uid          ## User ID of file.
+    st_gid*: Gid          ## Group ID of file.
+    st_rdev*: Dev         ## Device ID (if file is character or block special).
+    st_size*: Off         ## For regular files, the file size in bytes.
+                          ## For symbolic links, the length in bytes of the
+                          ## pathname contained in the symbolic link.
+                          ## For a shared memory object, the length in bytes.
+                          ## For a typed memory object, the length in bytes.
+                          ## For other file types, the use of this field is
+                          ## unspecified.
+    when StatHasNanoseconds:
+      st_atim*: Timespec  ## Time of last access.
+      st_mtim*: Timespec  ## Time of last data modification.
+      st_ctim*: Timespec  ## Time of last status change.
+    else:
+      st_atime*: Time     ## Time of last access.
+      st_mtime*: Time     ## Time of last data modification.
+      st_ctime*: Time     ## Time of last status change.
+    st_blksize*: Blksize  ## A file system-specific preferred I/O block size
+                          ## for this object. In some file system types, this
+                          ## may vary from file to file.
+    st_blocks*: Blkcnt    ## Number of blocks allocated for this object.
+
+
+  Statvfs* {.importc: "struct statvfs", header: "<sys/statvfs.h>",
+              final, pure.} = object ## struct statvfs
+    f_bsize*: int        ## File system block size.
+    f_frsize*: int       ## Fundamental file system block size.
+    f_blocks*: Fsblkcnt  ## Total number of blocks on file system
+                         ## in units of f_frsize.
+    f_bfree*: Fsblkcnt   ## Total number of free blocks.
+    f_bavail*: Fsblkcnt  ## Number of free blocks available to
+                         ## non-privileged process.
+    f_files*: Fsfilcnt   ## Total number of file serial numbers.
+    f_ffree*: Fsfilcnt   ## Total number of free file serial numbers.
+    f_favail*: Fsfilcnt  ## Number of file serial numbers available to
+                         ## non-privileged process.
+    f_fsid*: int         ## File system ID.
+    f_flag*: int         ## Bit mask of f_flag values.
+    f_namemax*: int      ## Maximum filename length.
+
+  Posix_typed_mem_info* {.importc: "struct posix_typed_mem_info",
+                           header: "<sys/mman.h>", final, pure.} = object
+    posix_tmi_length*: int
+
+  Tm* {.importc: "struct tm", header: "<time.h>",
+         final, pure.} = object ## struct tm
+    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.
+  Itimerspec* {.importc: "struct itimerspec", header: "<time.h>",
+                 final, pure.} = object ## struct itimerspec
+    it_interval*: Timespec  ## Timer period.
+    it_value*: Timespec     ## Timer expiration.
+
+  Sig_atomic* {.importc: "sig_atomic_t", header: "<signal.h>".} = cint
+    ## Possibly volatile-qualified integer type of an object that can be
+    ## accessed as an atomic entity, even in the presence of asynchronous
+    ## interrupts.
+  Sigset* {.importc: "sigset_t", header: "<signal.h>", final, pure.} = object
+
+  SigEvent* {.importc: "struct sigevent",
+               header: "<signal.h>", final, pure.} = object ## struct sigevent
+    sigev_notify*: cint           ## Notification type.
+    sigev_signo*: cint            ## Signal number.
+    sigev_value*: SigVal          ## Signal value.
+    sigev_notify_function*: proc (x: SigVal) {.noconv.} ## Notification func.
+    sigev_notify_attributes*: ptr Pthread_attr ## Notification attributes.
+
+  SigVal* {.importc: "union sigval",
+             header: "<signal.h>", final, pure.} = object ## struct sigval
+    sival_ptr*: pointer ## pointer signal value;
+                        ## integer signal value not defined!
+  Sigaction* {.importc: "struct sigaction",
+                header: "<signal.h>", final, pure.} = object ## struct sigaction
+    sa_handler*: proc (x: cint) {.noconv.}  ## Pointer to a signal-catching
+                                            ## function or one of the macros
+                                            ## SIG_IGN or SIG_DFL.
+    sa_mask*: Sigset ## Set of signals to be blocked during execution of
+                      ## the signal handling function.
+    sa_flags*: cint   ## Special flags.
+    sa_sigaction*: proc (x: cint, y: ptr SigInfo, z: pointer) {.noconv.}
+
+  Stack* {.importc: "stack_t",
+            header: "<signal.h>", final, pure.} = object ## stack_t
+    ss_sp*: pointer  ## Stack base or pointer.
+    ss_size*: int    ## Stack size.
+    ss_flags*: cint  ## Flags.
+
+  SigStack* {.importc: "struct sigstack",
+               header: "<signal.h>", final, pure.} = object ## struct sigstack
+    ss_onstack*: cint ## Non-zero when signal stack is in use.
+    ss_sp*: pointer   ## Signal stack pointer.
+
+  SigInfo* {.importc: "siginfo_t",
+              header: "<signal.h>", final, pure.} = object ## siginfo_t
+    si_signo*: cint    ## Signal number.
+    si_code*: cint     ## Signal code.
+    si_errno*: cint    ## If non-zero, an errno value associated with
+                       ## this signal, as defined in <errno.h>.
+    si_pid*: Pid       ## Sending process ID.
+    si_uid*: Uid       ## Real user ID of sending process.
+    si_addr*: pointer  ## Address of faulting instruction.
+    si_status*: cint   ## Exit value or signal.
+    si_band*: int      ## Band event for SIGPOLL.
+    si_value*: SigVal  ## Signal value.
+
+  Nl_item* {.importc: "nl_item", header: "<nl_types.h>".} = cint
+  Nl_catd* {.importc: "nl_catd", header: "<nl_types.h>".} = cint
+
+  Sched_param* {.importc: "struct sched_param",
+                  header: "<sched.h>",
+                  final, pure.} = object ## struct sched_param
+    sched_priority*: cint
+    sched_ss_low_priority*: cint     ## Low scheduling priority for
+                                     ## sporadic server.
+    sched_ss_repl_period*: Timespec  ## Replenishment period for
+                                     ## sporadic server.
+    sched_ss_init_budget*: Timespec  ## Initial budget for sporadic server.
+    sched_ss_max_repl*: cint         ## Maximum pending replenishments for
+                                     ## sporadic server.
+
+  Timeval* {.importc: "struct timeval", header: "<sys/select.h>",
+             final, pure.} = object ## struct timeval
+    tv_sec*: Time ## Seconds.
+    tv_usec*: Suseconds ## Microseconds.
+  TFdSet* {.importc: "fd_set", header: "<sys/select.h>",
+           final, pure.} = object
+  # the following aren't actually defined, but they're here to keep things happy for now
+  Mcontext* {.importc: "mcontext_t", header: "<ucontext.h>",
+               final, pure.} = object
+  Ucontext* {.importc: "ucontext_t", header: "<ucontext.h>",
+               final, pure.} = object ## ucontext_t
+    uc_link*: ptr Ucontext  ## Pointer to the context that is resumed
+                            ## when this context returns.
+    uc_sigmask*: Sigset     ## The set of signals that are blocked when this
+                            ## context is active.
+    uc_stack*: Stack        ## The stack used by this context.
+    uc_mcontext*: Mcontext  ## A machine-specific representation of the saved
+                            ## context.
+
+when hasAioH:
+  type
+    Taiocb* {.importc: "struct aiocb", header: "<aio.h>",
+              final, pure.} = object ## struct aiocb
+      aio_fildes*: cint         ## File descriptor.
+      aio_offset*: Off          ## File offset.
+      aio_buf*: pointer         ## Location of buffer.
+      aio_nbytes*: int          ## Length of transfer.
+      aio_reqprio*: cint        ## Request priority offset.
+      aio_sigevent*: SigEvent   ## Signal number and value.
+      aio_lio_opcode: cint      ## Operation to be performed.
+
+when hasSpawnH:
+  type
+    Tposix_spawnattr* {.importc: "posix_spawnattr_t",
+                        header: "<spawn.h>", final, pure.} = object
+    Tposix_spawn_file_actions* {.importc: "posix_spawn_file_actions_t",
+                                 header: "<spawn.h>", final, pure.} = object
+
+# according to http://pubs.opengroup.org/onlinepubs/009604499/basedefs/sys/un.h.html
+# this is >=92
+const Sockaddr_un_path_length* = 92
+
+type
+  SockLen* {.importc: "socklen_t", header: "<sys/socket.h>".} = cuint
+  TSa_Family* {.importc: "sa_family_t", header: "<sys/socket.h>".} = uint8
+
+  SockAddr* {.importc: "struct sockaddr", header: "<sys/socket.h>",
+              pure, final.} = object ## struct sockaddr
+    sa_family*: TSa_Family         ## Address family.
+    sa_data*: array[0..255, char] ## Socket address (variable-length data).
+
+  Sockaddr_un* {.importc: "struct sockaddr_un", header: "<sys/un.h>",
+              pure, final.} = object ## struct sockaddr_un
+    sun_family*: TSa_Family         ## Address family.
+    sun_path*: array[0..Sockaddr_un_path_length-1, char] ## Socket path
+
+  Sockaddr_storage* {.importc: "struct sockaddr_storage",
+                       header: "<sys/socket.h>",
+                       pure, final.} = object ## struct sockaddr_storage
+    ss_family*: TSa_Family ## Address family.
+
+  Tif_nameindex* {.importc: "struct if_nameindex", final,
+                   pure, header: "<net/if.h>".} = object ## struct if_nameindex
+    if_index*: cint   ## Numeric index of the interface.
+    if_name*: cstring ## Null-terminated name of the interface.
+
+
+  IOVec* {.importc: "struct iovec", pure, final,
+            header: "<sys/uio.h>".} = object ## struct iovec
+    iov_base*: pointer ## Base address of a memory region for input or output.
+    iov_len*: csize_t    ## The size of the memory pointed to by iov_base.
+
+  Tmsghdr* {.importc: "struct msghdr", pure, final,
+             header: "<sys/socket.h>".} = object  ## struct msghdr
+    msg_name*: pointer  ## Optional address.
+    msg_namelen*: SockLen  ## Size of address.
+    msg_iov*: ptr IOVec    ## Scatter/gather array.
+    msg_iovlen*: cint   ## Members in msg_iov.
+    msg_control*: pointer  ## Ancillary data; see below.
+    msg_controllen*: SockLen ## Ancillary data buffer len.
+    msg_flags*: cint ## Flags on received message.
+
+
+  Tcmsghdr* {.importc: "struct cmsghdr", pure, final,
+              header: "<sys/socket.h>".} = object ## struct cmsghdr
+    cmsg_len*: SockLen ## Data byte count, including the cmsghdr.
+    cmsg_level*: cint   ## Originating protocol.
+    cmsg_type*: cint    ## Protocol-specific type.
+
+  TLinger* {.importc: "struct linger", pure, final,
+             header: "<sys/socket.h>".} = object ## struct linger
+    l_onoff*: cint  ## Indicates whether linger option is enabled.
+    l_linger*: cint ## Linger time, in seconds.
+
+  InPort* = uint16
+  InAddrScalar* = uint32
+
+  InAddrT* {.importc: "in_addr_t", pure, final,
+             header: "<netinet/in.h>".} = uint32
+
+  InAddr* {.importc: "struct in_addr", pure, final,
+             header: "<netinet/in.h>".} = object ## struct in_addr
+    s_addr*: InAddrScalar
+
+  Sockaddr_in* {.importc: "struct sockaddr_in", pure, final,
+                  header: "<netinet/in.h>".} = object ## struct sockaddr_in
+    sin_family*: TSa_Family ## AF_INET.
+    sin_port*: InPort      ## Port number.
+    sin_addr*: InAddr      ## IP address.
+
+  In6Addr* {.importc: "struct in6_addr", pure, final,
+              header: "<netinet/in.h>".} = object ## struct in6_addr
+    s6_addr*: array[0..15, char]
+
+  Sockaddr_in6* {.importc: "struct sockaddr_in6", pure, final,
+                   header: "<netinet/in.h>".} = object ## struct sockaddr_in6
+    sin6_family*: TSa_Family ## AF_INET6.
+    sin6_port*: InPort      ## Port number.
+    sin6_flowinfo*: uint32    ## IPv6 traffic class and flow information.
+    sin6_addr*: In6Addr     ## IPv6 address.
+    sin6_scope_id*: uint32    ## Set of interfaces for a scope.
+
+  Tipv6_mreq* {.importc: "struct ipv6_mreq", pure, final,
+                header: "<netinet/in.h>".} = object ## struct ipv6_mreq
+    ipv6mr_multiaddr*: In6Addr ## IPv6 multicast address.
+    ipv6mr_interface*: cint     ## Interface index.
+
+  Hostent* {.importc: "struct hostent", pure, final,
+              header: "<netdb.h>".} = object ## struct hostent
+    h_name*: cstring           ## Official name of the host.
+    h_aliases*: cstringArray   ## A pointer to an array of pointers to
+                               ## alternative host names, terminated by a
+                               ## null pointer.
+    h_addrtype*: cint          ## Address type.
+    h_length*: cint            ## The length, in bytes, of the address.
+    h_addr_list*: cstringArray ## A pointer to an array of pointers to network
+                               ## addresses (in network byte order) for the
+                               ## host, terminated by a null pointer.
+
+  Tnetent* {.importc: "struct netent", pure, final,
+              header: "<netdb.h>".} = object ## struct netent
+    n_name*: cstring         ## Official, fully-qualified (including the
+                             ## domain) name of the host.
+    n_aliases*: cstringArray ## A pointer to an array of pointers to
+                             ## alternative network names, terminated by a
+                             ## null pointer.
+    n_addrtype*: cint        ## The address type of the network.
+    n_net*: uint32            ## The network number, in host byte order.
+
+  Protoent* {.importc: "struct protoent", pure, final,
+              header: "<netdb.h>".} = object ## struct protoent
+    p_name*: cstring         ## Official name of the protocol.
+    p_aliases*: cstringArray ## A pointer to an array of pointers to
+                             ## alternative protocol names, terminated by
+                             ## a null pointer.
+    p_proto*: cint           ## The protocol number.
+
+  Servent* {.importc: "struct servent", pure, final,
+             header: "<netdb.h>".} = object ## struct servent
+    s_name*: cstring         ## Official name of the service.
+    s_aliases*: cstringArray ## A pointer to an array of pointers to
+                             ## alternative service names, terminated by
+                             ## a null pointer.
+    s_port*: cint            ## The port number at which the service
+                             ## resides, in network byte order.
+    s_proto*: cstring        ## The name of the protocol to use when
+                             ## contacting the service.
+
+  AddrInfo* {.importc: "struct addrinfo", pure, final,
+              header: "<netdb.h>".} = object ## struct addrinfo
+    ai_flags*: cint         ## Input flags.
+    ai_family*: cint        ## Address family of socket.
+    ai_socktype*: cint      ## Socket type.
+    ai_protocol*: cint      ## Protocol of socket.
+    ai_addrlen*: SockLen   ## Length of socket address.
+    ai_addr*: ptr SockAddr ## Socket address of socket.
+    ai_canonname*: cstring  ## Canonical name of service location.
+    ai_next*: ptr AddrInfo ## Pointer to next in list.
+
+  TPollfd* {.importc: "struct pollfd", pure, final,
+             header: "<poll.h>".} = object ## struct pollfd
+    fd*: cint        ## The following descriptor being polled.
+    events*: cshort  ## The input event flags (see below).
+    revents*: cshort ## The output event flags (see below).
+
+  Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = cint
+
+var
+  errno* {.importc, header: "<errno.h>".}: cint ## error variable
+  h_errno* {.importc, header: "<netdb.h>".}: cint
+  daylight* {.importc, header: "<time.h>".}: cint
+  timezone* {.importc, header: "<time.h>".}: int
+
+# Regenerate using detect.nim!
+include posix_other_consts
+
+var
+  MAP_POPULATE*: cint = 0
+
+when defined(nimdoc):
+  const SO_REUSEPORT* = cint(0x0200)
+else:
+  var SO_REUSEPORT* {.importc, header: "<sys/socket.h>".}: cint
+
+var SOCK_CLOEXEC* {.importc, header: "<sys/socket.h>".}: cint
+
+var MSG_NOSIGNAL* {.importc, header: "<sys/socket.h>".}: cint
+
+when hasSpawnH:
+    # openbsd lacks this, so we define the constant to be 0 to not affect
+    # OR'ing of flags:
+    const POSIX_SPAWN_USEVFORK* = cint(0)
+
+# <sys/wait.h>
+proc WEXITSTATUS*(s: cint): cint {.importc, header: "<sys/wait.h>".}
+  ## Exit code, if WIFEXITED(s)
+proc WTERMSIG*(s: cint): cint {.importc, header: "<sys/wait.h>".}
+  ## Termination signal, if WIFSIGNALED(s)
+proc WSTOPSIG*(s: cint): cint {.importc, header: "<sys/wait.h>".}
+  ## Stop signal, if WIFSTOPPED(s)
+proc WIFEXITED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
+  ## True if child exited normally.
+proc WIFSIGNALED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
+  ## True if child exited due to uncaught signal.
+proc WIFSTOPPED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
+  ## True if child is currently stopped.
+proc WIFCONTINUED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
+  ## True if child has been continued.
+
+when defined(nimHasStyleChecks):
+  {.pop.}
diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim
index b7570bd15..ea8731405 100644
--- a/lib/posix/posix_other.nim
+++ b/lib/posix/posix_other.nim
@@ -7,34 +7,42 @@
 #    distribution, for details about the copyright.
 #
 
-{.deadCodeElim: on.}  # dce option deprecated
+when defined(nimHasStyleChecks):
+  {.push styleChecks: off.}
 
-const
-  hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays
-  hasAioH = defined(linux)
+when defined(freertos) or defined(zephyr):
+  const
+    hasSpawnH = false # should exist for every Posix system nowadays
+    hasAioH = false
+else:
+  const
+    hasSpawnH = true # should exist for every Posix system nowadays
+    hasAioH = defined(linux)
 
 when defined(linux) and not defined(android):
   # On Linux:
   # timer_{create,delete,settime,gettime},
   # clock_{getcpuclockid, getres, gettime, nanosleep, settime} lives in librt
-  {.passL: "-lrt".}
+  {.passl: "-lrt".}
 when defined(solaris):
   # On Solaris hstrerror lives in libresolv
-  {.passL: "-lresolv".}
+  {.passl: "-lresolv".}
 
 type
   DIR* {.importc: "DIR", header: "<dirent.h>",
           incompleteStruct.} = object
     ## A type representing a directory stream.
-{.deprecated: [TDIR: DIR].}
 
 type
   SocketHandle* = distinct cint # The type used to represent socket descriptors
 
-{.deprecated: [TSocketHandle: SocketHandle].}
-
 type
-  Time* {.importc: "time_t", header: "<time.h>".} = distinct clong
+  Time* {.importc: "time_t", header: "<time.h>".} = distinct (
+    when defined(nimUse64BitCTime):
+      int64
+    else:
+      clong
+  )
 
   Timespec* {.importc: "struct timespec",
                header: "<time.h>", final, pure.} = object ## struct timespec
@@ -43,6 +51,9 @@ type
 
   Dirent* {.importc: "struct dirent",
              header: "<dirent.h>", final, pure.} = object ## dirent_t struct
+    when defined(haiku):
+      d_dev*: Dev ## Device (not POSIX)
+      d_pdev*: Dev ## Parent device (only for queries) (not POSIX)
     d_ino*: Ino  ## File serial number.
     when defined(dragonfly):
       # DragonflyBSD doesn't have `d_reclen` field.
@@ -53,7 +64,10 @@ type
       d_type*: int8 ## Type of file; not supported by all filesystem types.
                     ## (not POSIX)
       when defined(linux) or defined(openbsd):
-        d_off*: Off  ## Not an offset. Value that ``telldir()`` would return.
+        d_off*: Off  ## Not an offset. Value that `telldir()` would return.
+    elif defined(haiku):
+      d_pino*: Ino ## Parent inode (only for queries) (not POSIX)
+      d_reclen*: cushort ## Length of this record. (not POSIX)
 
     d_name*: array[0..255, char] ## Name of entry.
 
@@ -143,7 +157,13 @@ type
   Id* {.importc: "id_t", header: "<sys/types.h>".} = int
   Ino* {.importc: "ino_t", header: "<sys/types.h>".} = int
   Key* {.importc: "key_t", header: "<sys/types.h>".} = int
-  Mode* {.importc: "mode_t", header: "<sys/types.h>".} = cint
+  Mode* {.importc: "mode_t", header: "<sys/types.h>".} = (
+    when defined(android) or defined(macos) or defined(macosx) or
+        (defined(bsd) and not defined(openbsd) and not defined(netbsd)):
+      uint16
+    else:
+      uint32
+  )
   Nlink* {.importc: "nlink_t", header: "<sys/types.h>".} = int
   Off* {.importc: "off_t", header: "<sys/types.h>".} = int64
   Pid* {.importc: "pid_t", header: "<sys/types.h>".} = int32
@@ -278,7 +298,7 @@ type
     sigev_signo*: cint            ## Signal number.
     sigev_value*: SigVal          ## Signal value.
     sigev_notify_function*: proc (x: SigVal) {.noconv.} ## Notification func.
-    sigev_notify_attributes*: ptr PthreadAttr ## Notification attributes.
+    sigev_notify_attributes*: ptr Pthread_attr ## Notification attributes.
 
   SigVal* {.importc: "union sigval",
              header: "<signal.h>", final, pure.} = object ## struct sigval
@@ -350,31 +370,7 @@ type
     uc_stack*: Stack        ## The stack used by this context.
     uc_mcontext*: Mcontext  ## A machine-specific representation of the saved
                             ## context.
-{.deprecated: [TOff: Off, TPid: Pid, TGid: Gid, TMode: Mode, TDev: Dev,
-              TNlink: Nlink, TStack: Stack, TGroup: Group, TMqd: Mqd,
-              TPasswd: Passwd, TClock: Clock, TClockId: ClockId, TKey: Key,
-              TSem: Sem, Tpthread_attr: PthreadAttr, Ttimespec: Timespec,
-              Tdirent: Dirent, TFTW: FTW, TGlob: Glob,
-              # Tflock: Flock, # Naming conflict if we drop the `T`
-              Ticonv: Iconv, Tlconv: Lconv, TMqAttr: MqAttr, Tblkcnt: Blkcnt,
-              Tblksize: Blksize, Tfsblkcnt: Fsblkcnt, Tfsfilcnt: Fsfilcnt,
-              Tid: Id, Tino: Ino, Tpthread_barrier: Pthread_barrier,
-              Tpthread_barrierattr: Pthread_barrierattr, Tpthread_cond: Pthread_cond,
-              TPthread_condattr: Pthread_condattr, Tpthread_key: Pthread_key,
-              Tpthread_mutex: Pthread_mutex, Tpthread_mutexattr: Pthread_mutexattr,
-              Tpthread_once: Pthread_once, Tpthread_rwlock: Pthread_rwlock,
-              Tpthread_rwlockattr: Pthread_rwlockattr, Tpthread_spinlock: Pthread_spinlock,
-              Tpthread: Pthread, Tsuseconds: Suseconds, Ttimer: Timer,
-              Ttrace_attr: Trace_attr, Ttrace_event_id: Trace_event_id,
-              Ttrace_event_set: Trace_event_set, Ttrace_id: Trace_id,
-              Tuid: Uid, Tuseconds: Useconds, Tutsname: Utsname, Tipc_perm: Ipc_perm,
-              TStat: Stat, TStatvfs: Statvfs, Tposix_typed_mem_info: Posix_typed_mem_info,
-              Ttm: Tm, titimerspec: Itimerspec, Tsig_atomic: Sig_atomic, Tsigset: Sigset,
-              TsigEvent: SigEvent, TsigVal: SigVal, TSigaction: Sigaction,
-              TSigStack: SigStack, TsigInfo: SigInfo, Tnl_item: Nl_item,
-              Tnl_catd: Nl_catd, Tsched_param: Sched_param,
-              # TFdSet: FdSet, # Naming conflict if we drop the `T`
-              Tmcontext: Mcontext, Tucontext: Ucontext].}
+
 when hasAioH:
   type
     Taiocb* {.importc: "struct aiocb", header: "<aio.h>",
@@ -395,32 +391,92 @@ when hasSpawnH:
                                  header: "<spawn.h>", final, pure.} = object
 
 when defined(linux):
+  const Sockaddr_max_length* = 255
   # from sys/un.h
   const Sockaddr_un_path_length* = 108
+elif defined(zephyr):
+  when defined(net_ipv6):
+    const Sockaddr_max_length* = 24
+  elif defined(net_raw):
+    const Sockaddr_max_length* = 20
+  elif defined(net_ipv4):
+    const Sockaddr_max_length* = 8
+  else:
+    const Sockaddr_max_length* = 255 # just for compilation purposes
+
+  const Sockaddr_un_path_length* = Sockaddr_max_length
+  # Zephyr is heavily customizable so it's easy to get to a state  
+  # where Nim & Zephyr IPv6 settings are out of sync, causing painful runtime failures. 
+  when defined(net_ipv4) or defined(net_ipv6) or defined(net_raw):
+    {.emit: ["NIM_STATIC_ASSERT(NET_SOCKADDR_MAX_SIZE == ",
+        Sockaddr_max_length,
+        ",\"NET_SOCKADDR_MAX_SIZE and Sockaddr_max_length size mismatch!",
+        " Check that Nim and Zephyr IPv4/IPv6 settings match.",
+        " Try adding -d:net_ipv6 to enable IPv6 for Nim on Zephyr.\" );"].}
+elif defined(freertos) or defined(lwip):
+  const Sockaddr_max_length* = 14
+  const Sockaddr_un_path_length* = 108
 else:
+  const Sockaddr_max_length* = 255
   # according to http://pubs.opengroup.org/onlinepubs/009604499/basedefs/sys/un.h.html
   # this is >=92
   const Sockaddr_un_path_length* = 92
 
 type
-  Socklen* {.importc: "socklen_t", header: "<sys/socket.h>".} = cuint
-  TSa_Family* {.importc: "sa_family_t", header: "<sys/socket.h>".} = cint
+  SockLen* {.importc: "socklen_t", header: "<sys/socket.h>".} = cuint
+  TSa_Family* {.importc: "sa_family_t", header: "<sys/socket.h>".} = cushort
+
+when defined(lwip):
+  type
+    SockAddr* {.importc: "struct sockaddr", header: "<sys/socket.h>",
+                pure, final.} = object ## struct sockaddr
+      sa_len*: uint8         ## Address family.
+      sa_family*: TSa_Family         ## Address family.
+      sa_data*: array[0..Sockaddr_max_length-sizeof(uint8)-sizeof(TSa_Family), char] ## Socket address (variable-length data).
+
+    Sockaddr_storage* {.importc: "struct sockaddr_storage",
+                        header: "<sys/socket.h>",
+                        pure, final.} = object ## struct sockaddr_storage
+      s2_len*: uint8 ## Address family.
+      ss_family*: TSa_Family ## Address family.
+      s2_data1*: array[2, char] ## Address family.
+      s2_data2*: array[3, uint32] ## Address family.
+      when defined(lwip6) or defined(net_ipv6):
+        s2_data3*: array[3, uint32] ## Address family.
+elif defined(zephyr):
+  type
+    SockAddr* {.importc: "struct sockaddr", header: "<sys/socket.h>",
+                pure, final.} = object ## struct sockaddr
+      sa_family*: TSa_Family         ## Address family.
+      data*: array[0..Sockaddr_max_length-sizeof(TSa_Family), char] ## Socket address (variable-length data).
+
+    Sockaddr_storage* {.importc: "struct sockaddr_storage",
+                        header: "<sys/socket.h>",
+                        pure, final.} = object ## struct sockaddr_storage
+      ss_family*: TSa_Family ## Address family.
+      data*: array[0..Sockaddr_max_length-sizeof(TSa_Family), char] ## Socket address (variable-length data).
+  {.emit: ["NIM_STATIC_ASSERT(sizeof(struct sockaddr) == ", sizeof(Sockaddr), ",\"struct size mismatch\" );"].}
+  {.emit: ["NIM_STATIC_ASSERT(sizeof(struct sockaddr_storage) == ", sizeof(Sockaddr_storage), ",\"struct size mismatch\" );"].}
+else:
+  type
+    SockAddr* {.importc: "struct sockaddr", header: "<sys/socket.h>",
+                pure, final.} = object ## struct sockaddr
+      sa_family*: TSa_Family         ## Address family.
+      sa_data*: array[0..Sockaddr_max_length-sizeof(TSa_Family), char] ## Socket address (variable-length data).
 
-  SockAddr* {.importc: "struct sockaddr", header: "<sys/socket.h>",
-              pure, final.} = object ## struct sockaddr
-    sa_family*: TSa_Family         ## Address family.
-    sa_data*: array[0..255, char] ## Socket address (variable-length data).
+    Sockaddr_storage* {.importc: "struct sockaddr_storage",
+                        header: "<sys/socket.h>",
+                        pure, final.} = object ## struct sockaddr_storage
+      ss_family*: TSa_Family ## Address family.
 
+type
   Sockaddr_un* {.importc: "struct sockaddr_un", header: "<sys/un.h>",
               pure, final.} = object ## struct sockaddr_un
     sun_family*: TSa_Family         ## Address family.
-    sun_path*: array[0..Sockaddr_un_path_length-1, char] ## Socket path
+    sun_path*: array[0..Sockaddr_un_path_length-sizeof(TSa_Family), char] ## Socket path
 
-  Sockaddr_storage* {.importc: "struct sockaddr_storage",
-                       header: "<sys/socket.h>",
-                       pure, final.} = object ## struct sockaddr_storage
-    ss_family*: TSa_Family ## Address family.
 
+type
   Tif_nameindex* {.importc: "struct if_nameindex", final,
                    pure, header: "<net/if.h>".} = object ## struct if_nameindex
     if_index*: cint   ## Numeric index of the interface.
@@ -430,22 +486,22 @@ type
   IOVec* {.importc: "struct iovec", pure, final,
             header: "<sys/uio.h>".} = object ## struct iovec
     iov_base*: pointer ## Base address of a memory region for input or output.
-    iov_len*: int    ## The size of the memory pointed to by iov_base.
+    iov_len*: csize_t    ## The size of the memory pointed to by iov_base.
 
   Tmsghdr* {.importc: "struct msghdr", pure, final,
              header: "<sys/socket.h>".} = object  ## struct msghdr
     msg_name*: pointer  ## Optional address.
-    msg_namelen*: Socklen  ## Size of address.
+    msg_namelen*: SockLen  ## Size of address.
     msg_iov*: ptr IOVec    ## Scatter/gather array.
     msg_iovlen*: cint   ## Members in msg_iov.
     msg_control*: pointer  ## Ancillary data; see below.
-    msg_controllen*: Socklen ## Ancillary data buffer len.
+    msg_controllen*: SockLen ## Ancillary data buffer len.
     msg_flags*: cint ## Flags on received message.
 
 
   Tcmsghdr* {.importc: "struct cmsghdr", pure, final,
               header: "<sys/socket.h>".} = object ## struct cmsghdr
-    cmsg_len*: Socklen ## Data byte count, including the cmsghdr.
+    cmsg_len*: SockLen ## Data byte count, including the cmsghdr.
     cmsg_level*: cint   ## Originating protocol.
     cmsg_type*: cint    ## Protocol-specific type.
 
@@ -464,6 +520,7 @@ type
              header: "<netinet/in.h>".} = object ## struct in_addr
     s_addr*: InAddrScalar
 
+  # TODO: Fixme for FreeRTOS/LwIP, these are incorrect
   Sockaddr_in* {.importc: "struct sockaddr_in", pure, final,
                   header: "<netinet/in.h>".} = object ## struct sockaddr_in
     sin_family*: TSa_Family ## AF_INET.
@@ -534,25 +591,25 @@ type
     ai_family*: cint        ## Address family of socket.
     ai_socktype*: cint      ## Socket type.
     ai_protocol*: cint      ## Protocol of socket.
-    ai_addrlen*: Socklen   ## Length of socket address.
+    ai_addrlen*: SockLen   ## Length of socket address.
     ai_addr*: ptr SockAddr ## Socket address of socket.
     ai_canonname*: cstring  ## Canonical name of service location.
     ai_next*: ptr AddrInfo ## Pointer to next in list.
 
-  TPollfd* {.importc: "struct pollfd", pure, final,
-             header: "<poll.h>".} = object ## struct pollfd
-    fd*: cint        ## The following descriptor being polled.
-    events*: cshort  ## The input event flags (see below).
-    revents*: cshort ## The output event flags (see below).
-
-  Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = cint
-
-{.deprecated: [TSockaddr_in: Sockaddr_in, TAddrinfo: AddrInfo,
-    TSockAddr: SockAddr, TSockLen: SockLen, TTimeval: Timeval,
-    Tsockaddr_storage: Sockaddr_storage, Tsockaddr_in6: Sockaddr_in6,
-    Thostent: Hostent, TServent: Servent,
-    TInAddr: InAddr, TIOVec: IOVec, TInPort: InPort, TInAddrT: InAddrT,
-    TIn6Addr: In6Addr, TInAddrScalar: InAddrScalar, TProtoent: Protoent].}
+when not defined(lwip):
+  type
+    TPollfd* {.importc: "struct pollfd", pure, final,
+              header: "<poll.h>".} = object ## struct pollfd
+      fd*: cint        ## The following descriptor being polled.
+      events*: cshort  ## The input event flags (see below).
+      revents*: cshort ## The output event flags (see below).
+
+  when defined(zephyr):
+    type
+      Tnfds* = distinct cint
+  else:
+    type
+      Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = cint
 
 var
   errno* {.importc, header: "<errno.h>".}: cint ## error variable
@@ -561,7 +618,10 @@ var
   timezone* {.importc, header: "<time.h>".}: int
 
 # Regenerate using detect.nim!
-include posix_other_consts
+when defined(lwip):
+  include posix_freertos_consts
+else:
+  include posix_other_consts
 
 when defined(linux):
   var
@@ -574,51 +634,65 @@ else:
 when defined(linux) or defined(nimdoc):
   when defined(alpha) or defined(mips) or defined(mipsel) or
       defined(mips64) or defined(mips64el) or defined(parisc) or
-      defined(sparc) or defined(nimdoc):
+      defined(sparc) or defined(sparc64) or defined(nimdoc):
     const SO_REUSEPORT* = cint(0x0200)
       ## Multiple binding: load balancing on incoming TCP connections
       ## or UDP packets. (Requires Linux kernel > 3.9)
   else:
     const SO_REUSEPORT* = cint(15)
+elif defined(nuttx):
+  # Not supported, use SO_REUSEADDR to avoid compilation errors.
+  var SO_REUSEPORT* {.importc: "SO_REUSEADDR", header: "<sys/socket.h>".}: cint
 else:
   var SO_REUSEPORT* {.importc, header: "<sys/socket.h>".}: cint
 
+when defined(linux) or defined(bsd) or defined(nuttx):
+  var SOCK_CLOEXEC* {.importc, header: "<sys/socket.h>".}: cint
+
 when defined(macosx):
-  # We can't use the NOSIGNAL flag in the ``send`` function, it has no effect
+  # We can't use the NOSIGNAL flag in the `send` function, it has no effect
   # Instead we should use SO_NOSIGPIPE in setsockopt
   const
     MSG_NOSIGNAL* = 0'i32
   var
     SO_NOSIGPIPE* {.importc, header: "<sys/socket.h>".}: cint
 elif defined(solaris):
-  # Solaris dont have MSG_NOSIGNAL
+  # Solaris doesn't have MSG_NOSIGNAL
   const
     MSG_NOSIGNAL* = 0'i32
+elif defined(zephyr) or defined(freertos) or defined(lwip):
+  # LwIP/FreeRTOS doesn't have MSG_NOSIGNAL
+  const
+    MSG_NOSIGNAL* = 0x20'i32
 else:
   var
     MSG_NOSIGNAL* {.importc, header: "<sys/socket.h>".}: cint
       ## No SIGPIPE generated when an attempt to send is made on a stream-oriented socket that is no longer connected.
 
+when defined(haiku):
+  const
+    SIGKILLTHR* = 21 ## BeOS specific: Kill just the thread, not team
+
 when hasSpawnH:
   when defined(linux):
-    # better be safe than sorry; Linux has this flag, macosx doesn't, don't
-    # know about the other OSes
+    # better be safe than sorry; Linux has this flag, macosx and NuttX don't,
+    # don't know about the other OSes
 
-    # Non-GNU systems like TCC and musl-libc  don't define __USE_GNU, so we
+    # Non-GNU systems like TCC and musl-libc don't define __USE_GNU, so we
     # can't get the magic number from spawn.h
     const POSIX_SPAWN_USEVFORK* = cint(0x40)
   else:
-    # macosx lacks this, so we define the constant to be 0 to not affect
+    # macosx and NuttX lack this, so we define the constant to be 0 to not affect
     # OR'ing of flags:
     const POSIX_SPAWN_USEVFORK* = cint(0)
 
 # <sys/wait.h>
 proc WEXITSTATUS*(s: cint): cint {.importc, header: "<sys/wait.h>".}
-  ## Exit code, iff WIFEXITED(s)
+  ## Exit code, if WIFEXITED(s)
 proc WTERMSIG*(s: cint): cint {.importc, header: "<sys/wait.h>".}
-  ## Termination signal, iff WIFSIGNALED(s)
+  ## Termination signal, if WIFSIGNALED(s)
 proc WSTOPSIG*(s: cint): cint {.importc, header: "<sys/wait.h>".}
-  ## Stop signal, iff WIFSTOPPED(s)
+  ## Stop signal, if WIFSTOPPED(s)
 proc WIFEXITED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
   ## True if child exited normally.
 proc WIFSIGNALED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
@@ -627,3 +701,6 @@ proc WIFSTOPPED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
   ## True if child is currently stopped.
 proc WIFCONTINUED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
   ## True if child has been continued.
+
+when defined(nimHasStyleChecks):
+  {.pop.}
diff --git a/lib/posix/posix_other_consts.nim b/lib/posix/posix_other_consts.nim
index 1b27fc5f6..d346b4150 100644
--- a/lib/posix/posix_other_consts.nim
+++ b/lib/posix/posix_other_consts.nim
@@ -99,6 +99,10 @@ var EXDEV* {.importc: "EXDEV", header: "<errno.h>".}: cint
 
 # <fcntl.h>
 var F_DUPFD* {.importc: "F_DUPFD", header: "<fcntl.h>".}: cint
+when defined(nuttx):
+  var F_DUPFD_CLOEXEC* {.importc: "F_DUPFD_CLOEXEC", header: "<fcntl.h>".}: cint
+else:
+  var F_DUPFD_CLOEXEC* {.importc: "F_DUPFD", header: "<fcntl.h>".}: cint
 var F_GETFD* {.importc: "F_GETFD", header: "<fcntl.h>".}: cint
 var F_SETFD* {.importc: "F_SETFD", header: "<fcntl.h>".}: cint
 var F_GETFL* {.importc: "F_GETFL", header: "<fcntl.h>".}: cint
@@ -125,6 +129,12 @@ var O_ACCMODE* {.importc: "O_ACCMODE", header: "<fcntl.h>".}: cint
 var O_RDONLY* {.importc: "O_RDONLY", header: "<fcntl.h>".}: cint
 var O_RDWR* {.importc: "O_RDWR", header: "<fcntl.h>".}: cint
 var O_WRONLY* {.importc: "O_WRONLY", header: "<fcntl.h>".}: cint
+var O_CLOEXEC* {.importc: "O_CLOEXEC", header: "<fcntl.h>".}: cint
+when defined(nuttx):
+  var O_DIRECT* {.importc: "O_DIRECT", header: "<fcntl.h>".}: cint
+  var O_PATH* {.importc: "O_PATH", header: "<fcntl.h>".}: cint
+  var O_NOATIME* {.importc: "O_NOATIME", header: "<fcntl.h>".}: cint
+  var O_TMPFILE* {.importc: "O_TMPFILE", header: "<fcntl.h>".}: cint
 var POSIX_FADV_NORMAL* {.importc: "POSIX_FADV_NORMAL", header: "<fcntl.h>".}: cint
 var POSIX_FADV_SEQUENTIAL* {.importc: "POSIX_FADV_SEQUENTIAL", header: "<fcntl.h>".}: cint
 var POSIX_FADV_RANDOM* {.importc: "POSIX_FADV_RANDOM", header: "<fcntl.h>".}: cint
@@ -302,6 +312,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
@@ -433,6 +444,9 @@ var PROT_READ* {.importc: "PROT_READ", header: "<sys/mman.h>".}: cint
 var PROT_WRITE* {.importc: "PROT_WRITE", header: "<sys/mman.h>".}: cint
 var PROT_EXEC* {.importc: "PROT_EXEC", header: "<sys/mman.h>".}: cint
 var PROT_NONE* {.importc: "PROT_NONE", header: "<sys/mman.h>".}: cint
+var MAP_ANONYMOUS* {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
+var MAP_FIXED_NOREPLACE* {.importc: "MAP_FIXED_NOREPLACE", header: "<sys/mman.h>".}: cint
+var MAP_NORESERVE* {.importc: "MAP_NORESERVE", header: "<sys/mman.h>".}: cint
 var MAP_SHARED* {.importc: "MAP_SHARED", header: "<sys/mman.h>".}: cint
 var MAP_PRIVATE* {.importc: "MAP_PRIVATE", header: "<sys/mman.h>".}: cint
 var MAP_FIXED* {.importc: "MAP_FIXED", header: "<sys/mman.h>".}: cint
@@ -453,9 +467,13 @@ var POSIX_TYPED_MEM_MAP_ALLOCATABLE* {.importc: "POSIX_TYPED_MEM_MAP_ALLOCATABLE
 
 # <sys/resource.h>
 var RLIMIT_NOFILE* {.importc: "RLIMIT_NOFILE", header: "<sys/resource.h>".}: cint
+var RLIMIT_STACK* {.importc: "RLIMIT_STACK", header: "<sys/resource.h>".}: cint
 
 # <sys/select.h>
 var FD_SETSIZE* {.importc: "FD_SETSIZE", header: "<sys/select.h>".}: cint
+when defined(zephyr):
+  # Zephyr specific hardcoded value
+  var FD_MAX* {.importc: "CONFIG_POSIX_MAX_FDS ", header: "<sys/select.h>".}: cint
 
 # <sys/socket.h>
 var MSG_CTRUNC* {.importc: "MSG_CTRUNC", header: "<sys/socket.h>".}: cint
@@ -464,6 +482,7 @@ var MSG_EOR* {.importc: "MSG_EOR", header: "<sys/socket.h>".}: cint
 var MSG_OOB* {.importc: "MSG_OOB", header: "<sys/socket.h>".}: cint
 var SCM_RIGHTS* {.importc: "SCM_RIGHTS", header: "<sys/socket.h>".}: cint
 var SO_ACCEPTCONN* {.importc: "SO_ACCEPTCONN", header: "<sys/socket.h>".}: cint
+var SO_BINDTODEVICE* {.importc: "SO_BINDTODEVICE", header: "<sys/socket.h>".}: cint
 var SO_BROADCAST* {.importc: "SO_BROADCAST", header: "<sys/socket.h>".}: cint
 var SO_DEBUG* {.importc: "SO_DEBUG", header: "<sys/socket.h>".}: cint
 var SO_DONTROUTE* {.importc: "SO_DONTROUTE", header: "<sys/socket.h>".}: cint
@@ -481,17 +500,22 @@ var SO_SNDTIMEO* {.importc: "SO_SNDTIMEO", header: "<sys/socket.h>".}: cint
 var SO_TYPE* {.importc: "SO_TYPE", header: "<sys/socket.h>".}: cint
 var SOCK_DGRAM* {.importc: "SOCK_DGRAM", header: "<sys/socket.h>".}: cint
 var SOCK_RAW* {.importc: "SOCK_RAW", header: "<sys/socket.h>".}: cint
-var SOCK_SEQPACKET* {.importc: "SOCK_SEQPACKET", header: "<sys/socket.h>".}: cint
+when defined(zephyr):
+  const SOCK_SEQPACKET* = cint(5)
+  var SOMAXCONN* {.importc: "CONFIG_NET_SOCKETS_POLL_MAX", header: "<sys/socket.h>".}: cint
+else:
+  var SOCK_SEQPACKET* {.importc: "SOCK_SEQPACKET", header: "<sys/socket.h>".}: cint
+  var SOMAXCONN* {.importc: "SOMAXCONN", header: "<sys/socket.h>".}: cint
+
 var SOCK_STREAM* {.importc: "SOCK_STREAM", header: "<sys/socket.h>".}: cint
 var SOL_SOCKET* {.importc: "SOL_SOCKET", header: "<sys/socket.h>".}: cint
-var SOMAXCONN* {.importc: "SOMAXCONN", header: "<sys/socket.h>".}: cint
 var MSG_PEEK* {.importc: "MSG_PEEK", header: "<sys/socket.h>".}: cint
 var MSG_TRUNC* {.importc: "MSG_TRUNC", header: "<sys/socket.h>".}: cint
 var MSG_WAITALL* {.importc: "MSG_WAITALL", header: "<sys/socket.h>".}: cint
-var AF_INET* {.importc: "AF_INET", header: "<sys/socket.h>".}: TSa_Family
-var AF_INET6* {.importc: "AF_INET6", header: "<sys/socket.h>".}: TSa_Family
-var AF_UNIX* {.importc: "AF_UNIX", header: "<sys/socket.h>".}: TSa_Family
-var AF_UNSPEC* {.importc: "AF_UNSPEC", header: "<sys/socket.h>".}: TSa_Family
+var AF_INET* {.importc: "AF_INET", header: "<sys/socket.h>".}: cint
+var AF_INET6* {.importc: "AF_INET6", header: "<sys/socket.h>".}: cint
+var AF_UNIX* {.importc: "AF_UNIX", header: "<sys/socket.h>".}: cint
+var AF_UNSPEC* {.importc: "AF_UNSPEC", header: "<sys/socket.h>".}: cint
 var SHUT_RD* {.importc: "SHUT_RD", header: "<sys/socket.h>".}: cint
 var SHUT_RDWR* {.importc: "SHUT_RDWR", header: "<sys/socket.h>".}: cint
 var SHUT_WR* {.importc: "SHUT_WR", header: "<sys/socket.h>".}: cint
@@ -654,7 +678,7 @@ var SC_MQ_OPEN_MAX* {.importc: "_SC_MQ_OPEN_MAX", header: "<unistd.h>".}: cint
 var SC_MQ_PRIO_MAX* {.importc: "_SC_MQ_PRIO_MAX", header: "<unistd.h>".}: cint
 var SC_NGROUPS_MAX* {.importc: "_SC_NGROUPS_MAX", header: "<unistd.h>".}: cint
 var SC_OPEN_MAX* {.importc: "_SC_OPEN_MAX", header: "<unistd.h>".}: cint
-var SC_PAGE_SIZE* {.importc: "_SC_PAGE_SIZE", header: "<unistd.h>".}: cint
+var SC_PAGESIZE* {.importc: "_SC_PAGESIZE", header: "<unistd.h>".}: cint
 var SC_PRIORITIZED_IO* {.importc: "_SC_PRIORITIZED_IO", header: "<unistd.h>".}: cint
 var SC_PRIORITY_SCHEDULING* {.importc: "_SC_PRIORITY_SCHEDULING", header: "<unistd.h>".}: cint
 var SC_RAW_SOCKETS* {.importc: "_SC_RAW_SOCKETS", header: "<unistd.h>".}: cint
@@ -728,3 +752,6 @@ var SEEK_SET* {.importc: "SEEK_SET", header: "<unistd.h>".}: cint
 var SEEK_CUR* {.importc: "SEEK_CUR", header: "<unistd.h>".}: cint
 var SEEK_END* {.importc: "SEEK_END", header: "<unistd.h>".}: cint
 
+# <nuttx/config.h>
+when defined(nuttx):
+  var NEPOLL_MAX* {.importc: "CONFIG_FS_NEPOLL_DESCRIPTORS", header: "<nuttx/config.h>".}: cint
diff --git a/lib/posix/posix_utils.nim b/lib/posix/posix_utils.nim
new file mode 100644
index 000000000..0c668246f
--- /dev/null
+++ b/lib/posix/posix_utils.nim
@@ -0,0 +1,133 @@
+#
+#            Nim's Runtime Library
+#    (c) Copyright 2019 Federico Ceratto and other Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## A set of helpers for the POSIX module.
+## Raw interfaces are in the other ``posix*.nim`` files.
+
+# Where possible, contribute OS-independent procs in `os <os.html>`_ instead.
+
+import std/[posix, parsecfg, os]
+import std/private/since
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
+
+type Uname* = object
+  sysname*, nodename*, release*, version*, machine*: string
+
+template charArrayToString(input: typed): string =
+  $cast[cstring](addr input)
+
+proc uname*(): Uname =
+  ## Provides system information in a `Uname` struct with sysname, nodename,
+  ## release, version and machine attributes.
+
+  when defined(posix):
+    runnableExamples:
+      echo uname().nodename, uname().release, uname().version
+      doAssert uname().sysname.len != 0
+
+  var u: Utsname
+  if uname(u) != 0:
+    raiseOSError(OSErrorCode(errno))
+
+  result.sysname = charArrayToString u.sysname
+  result.nodename = charArrayToString u.nodename
+  result.release = charArrayToString u.release
+  result.version = charArrayToString u.version
+  result.machine = charArrayToString u.machine
+
+proc fsync*(fd: int) =
+  ## synchronize a file's buffer cache to the storage device
+  if fsync(fd.cint) != 0:
+    raiseOSError(OSErrorCode(errno))
+
+proc stat*(path: string): Stat =
+  ## Returns file status in a `Stat` structure
+  if stat(path.cstring, result) != 0:
+    raiseOSError(OSErrorCode(errno))
+
+proc memoryLock*(a1: pointer, a2: int) =
+  ## Locks pages starting from a1 for a1 bytes and prevent them from being swapped.
+  if mlock(a1, a2) != 0:
+    raiseOSError(OSErrorCode(errno))
+
+proc memoryLockAll*(flags: int) =
+  ## Locks all memory for the running process to prevent swapping.
+  ##
+  ## example:
+  ##   ```nim
+  ##   memoryLockAll(MCL_CURRENT or MCL_FUTURE)
+  ##   ```
+  if mlockall(flags.cint) != 0:
+    raiseOSError(OSErrorCode(errno))
+
+proc memoryUnlock*(a1: pointer, a2: int) =
+  ## Unlock pages starting from a1 for a1 bytes and allow them to be swapped.
+  if munlock(a1, a2) != 0:
+    raiseOSError(OSErrorCode(errno))
+
+proc memoryUnlockAll*() =
+  ## Unlocks all memory for the running process to allow swapping.
+  if munlockall() != 0:
+    raiseOSError(OSErrorCode(errno))
+
+proc sendSignal*(pid: Pid, signal: int) =
+  ## Sends a signal to a running process by calling `kill`.
+  ## Raise exception in case of failure e.g. process not running.
+  if kill(pid, signal.cint) != 0:
+    raiseOSError(OSErrorCode(errno))
+
+proc mkstemp*(prefix: string, suffix=""): (string, File) =
+  ## Creates a unique temporary file from a prefix string. A six-character string
+  ## will be added. If suffix is provided it will be added to the string
+  ## The file is created with perms 0600.
+  ## Returns the filename and a file opened in r/w mode.
+  var tmpl = cstring(prefix & "XXXXXX" & suffix)
+  let fd =
+    if len(suffix) == 0:
+      when declared(mkostemp):
+        mkostemp(tmpl, O_CLOEXEC)
+      else:
+        mkstemp(tmpl)
+    else:
+      when declared(mkostemps):
+        mkostemps(tmpl, cint(len(suffix)), O_CLOEXEC)
+      else:
+        mkstemps(tmpl, cint(len(suffix)))
+  var f: File
+  if open(f, fd, fmReadWrite):
+    return ($tmpl, f)
+  raiseOSError(OSErrorCode(errno))
+
+proc mkdtemp*(prefix: string): string =
+  ## Creates a unique temporary directory from a prefix string. Adds a six chars suffix.
+  ## The directory is created with permissions 0700. Returns the directory name.
+  var tmpl = cstring(prefix & "XXXXXX")
+  if mkdtemp(tmpl) == nil:
+    raiseOSError(OSErrorCode(errno))
+  return $tmpl
+
+proc osReleaseFile*(): Config {.since: (1, 5).} =
+  ## Gets system identification from `os-release` file and returns it as a `parsecfg.Config`.
+  ## You also need to import the `parsecfg` module to gain access to this object.
+  ## The `os-release` file is an official Freedesktop.org open standard.
+  ## Available in Linux and BSD distributions, except Android and Android-based Linux.
+  ## `os-release` file is not available on Windows and OS X by design.
+  ## * https://www.freedesktop.org/software/systemd/man/os-release.html
+  runnableExamples:
+    import std/parsecfg
+    when defined(linux):
+      let data = osReleaseFile()
+      echo "OS name: ", data.getSectionValue("", "NAME") ## the data is up to each distro.
+
+  # We do not use a {.strdefine.} because Standard says it *must* be that path.
+  for osReleaseFile in ["/etc/os-release", "/usr/lib/os-release"]:
+    if fileExists(osReleaseFile):
+      return loadConfig(osReleaseFile)
+  raise newException(IOError, "File not found: /etc/os-release, /usr/lib/os-release")
diff --git a/lib/posix/termios.nim b/lib/posix/termios.nim
index 60d540107..7fb6bb81c 100644
--- a/lib/posix/termios.nim
+++ b/lib/posix/termios.nim
@@ -7,13 +7,11 @@
 #    distribution, for details about the copyright.
 #
 
-{.deadCodeElim: on.}  # dce option deprecated
-import posix
+import std/posix
 
 type
   Speed* = cuint
   Cflag* = cuint
-{.deprecated: [Tcflag: Cflag].}
 
 const
   NCCS* = when defined(macosx): 20 else: 32
@@ -122,6 +120,21 @@ var
   B9600* {.importc, header: "<termios.h>".}: Speed
   B19200* {.importc, header: "<termios.h>".}: Speed
   B38400* {.importc, header: "<termios.h>".}: Speed
+  B57600* {.importc, header: "<termios.h>".}: Speed
+  B115200* {.importc, header: "<termios.h>".}: Speed
+  B230400* {.importc, header: "<termios.h>".}: Speed
+  B460800* {.importc, header: "<termios.h>".}: Speed
+  B500000* {.importc, header: "<termios.h>".}: Speed
+  B576000* {.importc, header: "<termios.h>".}: Speed
+  B921600* {.importc, header: "<termios.h>".}: Speed
+  B1000000* {.importc, header: "<termios.h>".}: Speed
+  B1152000* {.importc, header: "<termios.h>".}: Speed
+  B1500000* {.importc, header: "<termios.h>".}: Speed
+  B2000000* {.importc, header: "<termios.h>".}: Speed
+  B2500000* {.importc, header: "<termios.h>".}: Speed
+  B3000000* {.importc, header: "<termios.h>".}: Speed
+  B3500000* {.importc, header: "<termios.h>".}: Speed
+  B4000000* {.importc, header: "<termios.h>".}: Speed
   EXTA* {.importc, header: "<termios.h>".}: Speed
   EXTB* {.importc, header: "<termios.h>".}: Speed
   CSIZE* {.importc, header: "<termios.h>".}: Cflag
@@ -224,11 +237,20 @@ proc tcFlow*(fd: cint; action: cint): cint {.importc: "tcflow",
     header: "<termios.h>".}
 # Get process group ID for session leader for controlling terminal FD.
 
-# Window size ioctl.  Should work on on any Unix that xterm has been ported to.
-var TIOCGWINSZ*{.importc, header: "<sys/ioctl.h>".}: culong
+# Window size ioctl.  Solaris based systems have an uncommen place for this.
+when defined(solaris) or defined(sunos):
+  var TIOCGWINSZ*{.importc, header: "<sys/termios.h>".}: culong
+else:
+  var TIOCGWINSZ*{.importc, header: "<sys/ioctl.h>".}: culong
+
+when defined(nimHasStyleChecks):
+  {.push styleChecks: off.}
 
 type IOctl_WinSize* = object
   ws_row*, ws_col*, ws_xpixel*, ws_ypixel*: cushort
 
+when defined(nimHasStyleChecks):
+  {.pop.}
+
 proc ioctl*(fd: cint, request: culong, reply: ptr IOctl_WinSize): int {.
   importc: "ioctl", header: "<stdio.h>", varargs.}
diff --git a/lib/prelude.nim b/lib/prelude.nim
deleted file mode 100644
index 940864207..000000000
--- a/lib/prelude.nim
+++ /dev/null
@@ -1,23 +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.
-#
-
-## This is an include file that simply imports common modules for your
-## convenience:
-##
-## .. code-block:: nim
-##   include prelude
-##
-## Same as:
-##
-## .. code-block:: nim
-##   import os, strutils, times, parseutils, parseopt, hashes, tables, sets
-
-import os, strutils, times, parseutils, parseopt, hashes, tables, sets
-
-
diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim
index 81badfae6..b12ed7cdd 100644
--- a/lib/pure/algorithm.nim
+++ b/lib/pure/algorithm.nim
@@ -7,17 +7,64 @@
 #    distribution, for details about the copyright.
 #
 
-## This module implements some common generic algorithms.
+## This module implements some common generic algorithms on `openArray`s.
+##
+## Basic usage
+## ===========
+##
+
+runnableExamples:
+  type People = tuple
+    year: int
+    name: string
+
+  var a: seq[People]
+
+  a.add((2000, "John"))
+  a.add((2005, "Marie"))
+  a.add((2010, "Jane"))
+
+  # Sorting with default system.cmp
+  a.sort()
+  assert a == @[(year: 2000, name: "John"), (year: 2005, name: "Marie"),
+                (year: 2010, name: "Jane")]
+
+  proc myCmp(x, y: People): int =
+    cmp(x.name, y.name)
+
+  # Sorting with custom proc
+  a.sort(myCmp)
+  assert a == @[(year: 2010, name: "Jane"), (year: 2000, name: "John"),
+                (year: 2005, name: "Marie")]
+
+## See also
+## ========
+## * `sequtils module<sequtils.html>`_ for working with the built-in seq type
+## * `tables module<tables.html>`_ for sorting tables
+
+import std/private/since
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 
 type
-  SortOrder* = enum   ## sort order
+  SortOrder* = enum
     Descending, Ascending
 
 proc `*`*(x: int, order: SortOrder): int {.inline.} =
-  ## flips `x` if ``order == Descending``;
-  ## if ``order == Ascending`` then `x` is returned.
-  ## `x` is supposed to be the result of a comparator, ie ``< 0`` for
-  ## *less than*, ``== 0`` for *equal*, ``> 0`` for *greater than*.
+  ## Flips the sign of `x` if `order == Descending`.
+  ## If `order == Ascending` then `x` is returned.
+  ##
+  ## `x` is supposed to be the result of a comparator, i.e.
+  ## | `< 0` for *less than*,
+  ## | `== 0` for *equal*,
+  ## | `> 0` for *greater than*.
+  runnableExamples:
+    assert -123 * Descending == 123
+    assert 123 * Descending == -123
+    assert -123 * Ascending == -123
+    assert 123 * Ascending == 123
   var y = order.ord - 1
   result = (x xor y) - y
 
@@ -28,16 +75,44 @@ template fillImpl[T](a: var openArray[T], first, last: int, value: T) =
     inc(x)
 
 proc fill*[T](a: var openArray[T], first, last: Natural, value: T) =
-  ## fills the array ``a[first..last]`` with `value`.
+  ## Assigns `value` to all elements of the slice `a[first..last]`.
+  ##
+  ## If an invalid range is passed, it raises `IndexDefect`.
+  runnableExamples:
+    var a: array[6, int]
+    a.fill(1, 3, 9)
+    assert a == [0, 9, 9, 9, 0, 0]
+    a.fill(3, 5, 7)
+    assert a == [0, 9, 9, 7, 7, 7]
+    doAssertRaises(IndexDefect, a.fill(1, 7, 9))
   fillImpl(a, first, last, value)
 
 proc fill*[T](a: var openArray[T], value: T) =
-  ## fills the array `a` with `value`.
+  ## Assigns `value` to all elements of the container `a`.
+  runnableExamples:
+    var a: array[6, int]
+    a.fill(9)
+    assert a == [9, 9, 9, 9, 9, 9]
+    a.fill(4)
+    assert a == [4, 4, 4, 4, 4, 4]
   fillImpl(a, 0, a.high, value)
 
 
 proc reverse*[T](a: var openArray[T], first, last: Natural) =
-  ## reverses the array ``a[first..last]``.
+  ## Reverses the slice `a[first..last]`.
+  ##
+  ## If an invalid range is passed, it raises `IndexDefect`.
+  ##
+  ## **See also:**
+  ## * `reversed proc<#reversed,openArray[T],Natural,int>`_ reverse a slice and returns a `seq[T]`
+  ## * `reversed proc<#reversed,openArray[T]>`_ reverse and returns a `seq[T]`
+  runnableExamples:
+    var a = [1, 2, 3, 4, 5, 6]
+    a.reverse(1, 3)
+    assert a == [1, 4, 3, 2, 5, 6]
+    a.reverse(1, 3)
+    assert a == [1, 2, 3, 4, 5, 6]
+    doAssertRaises(IndexDefect, a.reverse(1, 7))
   var x = first
   var y = last
   while x < y:
@@ -46,41 +121,63 @@ proc reverse*[T](a: var openArray[T], first, last: Natural) =
     inc(x)
 
 proc reverse*[T](a: var openArray[T]) =
-  ## reverses the array `a`.
+  ## Reverses the contents of the container `a`.
+  ##
+  ## **See also:**
+  ## * `reversed proc<#reversed,openArray[T],Natural,int>`_ reverse a slice and returns a `seq[T]`
+  ## * `reversed proc<#reversed,openArray[T]>`_ reverse and returns a `seq[T]`
+  runnableExamples:
+    var a = [1, 2, 3, 4, 5, 6]
+    a.reverse()
+    assert a == [6, 5, 4, 3, 2, 1]
+    a.reverse()
+    assert a == [1, 2, 3, 4, 5, 6]
+  # the max is needed, since a.high is -1 if a is empty
   reverse(a, 0, max(0, a.high))
 
-proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T] =
-  ## returns the reverse of the array `a[first..last]`.
-  assert last >= first-1
-  var i = last - first
-  var x = first.int
-  result = newSeq[T](i + 1)
-  while i >= 0:
-    result[i] = a[x]
-    dec(i)
-    inc(x)
-
-proc reversed*[T](a: openArray[T]): seq[T] =
-  ## returns the reverse of the array `a`.
-  reversed(a, 0, a.high)
+proc reversed*[T](a: openArray[T]): seq[T] {.inline.} =
+  ## Returns the elements of `a` in reverse order.
+  ##
+  ## **See also:**
+  ## * `reverse proc<#reverse,openArray[T]>`_
+  runnableExamples:
+    assert [10, 11, 12].reversed == @[12, 11, 10]
+    assert seq[string].default.reversed == @[]
+  let n = a.len
+  result.setLen(n)
+  for i in 0..<n: result[i] = a[n - (i + 1)]
+
+proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T]
+  {.inline, deprecated: "use: `reversed(toOpenArray(a, first, last))`".} =
+  reversed(toOpenArray(a, first, last))
+
+when defined(nimHasEffectsOf):
+  {.experimental: "strictEffects".}
+else:
+  {.pragma: effectsOf.}
 
 proc binarySearch*[T, K](a: openArray[T], key: K,
-              cmp: proc (x: T, y: K): int {.closure.}): int =
-  ## binary search for `key` in `a`. Returns -1 if not found.
+                         cmp: proc (x: T, y: K): int {.closure.}): int {.effectsOf: cmp.} =
+  ## Binary search for `key` in `a`. Return the index of `key` or -1 if not found.
+  ## Assumes that `a` is sorted according to `cmp`.
   ##
   ## `cmp` is the comparator function to use, the expected return values are
-  ## the same as that of system.cmp.
-  if a.len == 0:
-    return -1
-
+  ## the same as those of system.cmp.
+  runnableExamples:
+    assert binarySearch(["a", "b", "c", "d"], "d", system.cmp[string]) == 3
+    assert binarySearch(["a", "b", "c", "d"], "c", system.cmp[string]) == 2
   let len = a.len
 
+  if len == 0:
+    return -1
+
   if len == 1:
     if cmp(a[0], key) == 0:
       return 0
     else:
       return -1
 
+  result = 0
   if (len and (len - 1)) == 0:
     # when `len` is a power of 2, a faster shr can be used.
     var step = len shr 1
@@ -91,7 +188,7 @@ proc binarySearch*[T, K](a: openArray[T], key: K,
       if cmpRes == 0:
         return i
 
-      if cmpRes < 1:
+      if cmpRes < 0:
         result = i
       step = step shr 1
     if cmp(a[result], key) != 0: result = -1
@@ -111,31 +208,40 @@ proc binarySearch*[T, K](a: openArray[T], key: K,
     if result >= len or cmp(a[result], key) != 0: result = -1
 
 proc binarySearch*[T](a: openArray[T], key: T): int =
-  ## binary search for `key` in `a`. Returns -1 if not found.
-  binarySearch(a, key, cmp[T])
-
-proc smartBinarySearch*[T](a: openArray[T], key: T): int {.deprecated.} =
-  ## **Deprecated since version 0.18.1**; Use ``binarySearch`` instead.
+  ## Binary search for `key` in `a`. Return the index of `key` or -1 if not found.
+  ## Assumes that `a` is sorted.
+  runnableExamples:
+    assert binarySearch([0, 1, 2, 3, 4], 4) == 4
+    assert binarySearch([0, 1, 2, 3, 4], 2) == 2
   binarySearch(a, key, cmp[T])
 
 const
   onlySafeCode = true
 
-proc lowerBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {.closure.}): int =
-  ## Returns a position to the first element in the `a` that is greater than `key`, or last
-  ## if no such element is found. In other words if you have a sorted sequence and you call
-  ## insert(thing, elm, lowerBound(thing, elm))
+proc lowerBound*[T, K](a: openArray[T], key: K,
+                       cmp: proc(x: T, k: K): int {.closure.}): int {.effectsOf: cmp.} =
+  ## Returns the index of the first element in `a` that is not less than
+  ## (i.e. greater or equal to) `key`, or last if no such element is found.
+  ## In other words if you have a sorted sequence and you call
+  ## `insert(thing, elm, lowerBound(thing, elm))`
   ## the sequence will still be sorted.
+  ## Assumes that `a` is sorted according to `cmp`.
   ##
-  ## The first version uses `cmp` to compare the elements. The expected return values are
-  ## the same as that of system.cmp.
-  ## The second version uses the default comparison function `cmp`.
+  ## If an invalid range is passed, it raises `IndexDefect`.
   ##
-  ## example::
+  ## This version uses `cmp` to compare the elements.
+  ## The expected return values are the same as those of `system.cmp`.
   ##
-  ##   var arr = @[1,2,3,5,6,7,8,9]
-  ##   arr.insert(4, arr.lowerBound(4))
-  ##   # after running the above arr is `[1,2,3,4,5,6,7,8,9]`
+  ## **See also:**
+  ## * `upperBound proc<#upperBound,openArray[T],K,proc(T,K)>`_ sorted by `cmp` in the specified order
+  ## * `upperBound proc<#upperBound,openArray[T],T>`_
+  runnableExamples:
+    var arr = @[1, 2, 3, 5, 6, 7, 8, 9]
+    assert arr.lowerBound(3, system.cmp[int]) == 2
+    assert arr.lowerBound(4, system.cmp[int]) == 3
+    assert arr.lowerBound(5, system.cmp[int]) == 3
+    arr.insert(4, arr.lowerBound(4, system.cmp[int]))
+    assert arr == [1, 2, 3, 4, 5, 6, 7, 8, 9]
   result = a.low
   var count = a.high - a.low + 1
   var step, pos: int
@@ -149,23 +255,43 @@ proc lowerBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {.clo
       count = step
 
 proc lowerBound*[T](a: openArray[T], key: T): int = lowerBound(a, key, cmp[T])
-
-proc upperBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {.closure.}): int =
-  ## Returns a position to the first element in the `a` that is not less
-  ## (i.e. greater or equal to) than `key`, or last if no such element is found.
+  ## Returns the index of the first element in `a` that is not less than
+  ## (i.e. greater or equal to) `key`, or last if no such element is found.
   ## In other words if you have a sorted sequence and you call
-  ## insert(thing, elm, upperBound(thing, elm))
+  ## `insert(thing, elm, lowerBound(thing, elm))`
   ## the sequence will still be sorted.
+  ## Assumes that `a` is sorted.
   ##
-  ## The first version uses `cmp` to compare the elements. The expected return values are
-  ## the same as that of system.cmp.
-  ## The second version uses the default comparison function `cmp`.
+  ## This version uses the default comparison function `cmp`.
   ##
-  ## example::
+  ## **See also:**
+  ## * `upperBound proc<#upperBound,openArray[T],K,proc(T,K)>`_ sorted by `cmp` in the specified order
+  ## * `upperBound proc<#upperBound,openArray[T],T>`_
+
+proc upperBound*[T, K](a: openArray[T], key: K,
+                       cmp: proc(x: T, k: K): int {.closure.}): int {.effectsOf: cmp.} =
+  ## Returns the index of the first element in `a` that is greater than
+  ## `key`, or last if no such element is found.
+  ## In other words if you have a sorted sequence and you call
+  ## `insert(thing, elm, upperBound(thing, elm))`
+  ## the sequence will still be sorted.
+  ## Assumes that `a` is sorted according to `cmp`.
+  ##
+  ## If an invalid range is passed, it raises `IndexDefect`.
+  ##
+  ## This version uses `cmp` to compare the elements. The expected
+  ## return values are the same as those of `system.cmp`.
   ##
-  ##   var arr = @[1,2,3,4,6,7,8,9]
-  ##   arr.insert(5, arr.upperBound(4))
-  ##   # after running the above arr is `[1,2,3,4,5,6,7,8,9]`
+  ## **See also:**
+  ## * `lowerBound proc<#lowerBound,openArray[T],K,proc(T,K)>`_ sorted by `cmp` in the specified order
+  ## * `lowerBound proc<#lowerBound,openArray[T],T>`_
+  runnableExamples:
+    var arr = @[1, 2, 3, 5, 6, 7, 8, 9]
+    assert arr.upperBound(2, system.cmp[int]) == 2
+    assert arr.upperBound(3, system.cmp[int]) == 3
+    assert arr.upperBound(4, system.cmp[int]) == 3
+    arr.insert(4, arr.upperBound(3, system.cmp[int]))
+    assert arr == [1, 2, 3, 4, 5, 6, 7, 8, 9]
   result = a.low
   var count = a.high - a.low + 1
   var step, pos: int
@@ -179,21 +305,33 @@ proc upperBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {.clo
       count = step
 
 proc upperBound*[T](a: openArray[T], key: T): int = upperBound(a, key, cmp[T])
+  ## Returns the index of the first element in `a` that is greater than
+  ## `key`, or last if no such element is found.
+  ## In other words if you have a sorted sequence and you call
+  ## `insert(thing, elm, upperBound(thing, elm))`
+  ## the sequence will still be sorted.
+  ## Assumes that `a` is sorted.
+  ##
+  ## This version uses the default comparison function `cmp`.
+  ##
+  ## **See also:**
+  ## * `lowerBound proc<#lowerBound,openArray[T],K,proc(T,K)>`_ sorted by `cmp` in the specified order
+  ## * `lowerBound proc<#lowerBound,openArray[T],T>`_
 
-template `<-` (a, b) =
-  when false:
-    a = b
+template `<-`(a, b) =
+  when defined(gcDestructors):
+    a = move b
   elif onlySafeCode:
     shallowCopy(a, b)
   else:
     copyMem(addr(a), addr(b), sizeof(T))
 
-proc merge[T](a, b: var openArray[T], lo, m, hi: int,
-              cmp: proc (x, y: T): int {.closure.}, order: SortOrder) =
-  # optimization: If max(left) <= min(right) there is nothing to do!
-  # 1 2 3 4  ## 5 6 7 8
+proc mergeAlt[T](a, b: var openArray[T], lo, m, hi: int,
+              cmp: proc (x, y: T): int {.closure.}, order: SortOrder) {.effectsOf: cmp.} =
+  # Optimization: If max(left) <= min(right) there is nothing to do!
+  # 1 2 3 4 ## 5 6 7 8
   # -> O(n) for sorted arrays.
-  # On random data this safes up to 40% of merge calls
+  # On random data this saves up to 40% of mergeAlt calls.
   if cmp(a[m], a[m+1]) * order <= 0: return
   var j = lo
   # copy a[j..m] into b:
@@ -227,80 +365,138 @@ proc merge[T](a, b: var openArray[T], lo, m, hi: int,
   else:
     if k < j: copyMem(addr(a[k]), addr(b[i]), sizeof(T)*(j-k))
 
-proc sort*[T](a: var openArray[T],
+func sort*[T](a: var openArray[T],
               cmp: proc (x, y: T): int {.closure.},
-              order = SortOrder.Ascending) =
+              order = SortOrder.Ascending) {.effectsOf: cmp.} =
   ## Default Nim sort (an implementation of merge sort). The sorting
-  ## is guaranteed to be stable and the worst case is guaranteed to
-  ## be O(n log n).
+  ## is guaranteed to be stable (that is, equal elements stay in the same order)
+  ## and the worst case is guaranteed to be O(n log n).
+  ## Sorts by `cmp` in the specified `order`.
+  ##
   ## The current implementation uses an iterative
   ## mergesort to achieve this. It uses a temporary sequence of
-  ## length ``a.len div 2``. Currently Nim does not support a
-  ## sensible default argument for ``cmp``, so you have to provide one
-  ## of your own. However, the ``system.cmp`` procs can be used:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##    sort(myIntArray, system.cmp[int])
+  ## length `a.len div 2`. If you do not wish to provide your own
+  ## `cmp`, you may use `system.cmp` or instead call the overloaded
+  ## version of `sort`, which uses `system.cmp`.
   ##
-  ##    # do not use cmp[string] here as we want to use the specialized
-  ##    # overload:
-  ##    sort(myStrArray, system.cmp)
+  ##   ```nim
+  ##   sort(myIntArray, system.cmp[int])
+  ##   # do not use cmp[string] here as we want to use the specialized
+  ##   # overload:
+  ##   sort(myStrArray, system.cmp)
+  ##   ```
   ##
   ## You can inline adhoc comparison procs with the `do notation
   ## <manual.html#procedures-do-notation>`_. Example:
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   people.sort do (x, y: Person) -> int:
   ##     result = cmp(x.surname, y.surname)
   ##     if result == 0:
   ##       result = cmp(x.name, y.name)
+  ##   ```
+  ##
+  ## **See also:**
+  ## * `sort proc<#sort,openArray[T]>`_
+  ## * `sorted proc<#sorted,openArray[T],proc(T,T)>`_ sorted by `cmp` in the specified order
+  ## * `sorted proc<#sorted,openArray[T]>`_
+  ## * `sortedByIt template<#sortedByIt.t,untyped,untyped>`_
+  runnableExamples:
+    var d = ["boo", "fo", "barr", "qux"]
+    proc myCmp(x, y: string): int =
+      if x.len() > y.len() or x.len() == y.len(): 1
+      else: -1
+    sort(d, myCmp)
+    assert d == ["fo", "qux", "boo", "barr"]
   var n = a.len
-  var b: seq[T]
-  newSeq(b, n div 2)
+  var b = newSeq[T](n div 2)
   var s = 1
   while s < n:
     var m = n-1-s
     while m >= 0:
-      merge(a, b, max(m-s+1, 0), m, m+s, cmp, order)
+      mergeAlt(a, b, max(m-s+1, 0), m, m+s, cmp, order)
       dec(m, s*2)
     s = s*2
 
+proc sort*[T](a: var openArray[T], order = SortOrder.Ascending) = sort[T](a,
+    system.cmp[T], order)
+  ## Shortcut version of `sort` that uses `system.cmp[T]` as the comparison function.
+  ##
+  ## **See also:**
+  ## * `sort func<#sort,openArray[T],proc(T,T)>`_
+  ## * `sorted proc<#sorted,openArray[T],proc(T,T)>`_ sorted by `cmp` in the specified order
+  ## * `sorted proc<#sorted,openArray[T]>`_
+  ## * `sortedByIt template<#sortedByIt.t,untyped,untyped>`_
+
 proc sorted*[T](a: openArray[T], cmp: proc(x, y: T): int {.closure.},
-                order = SortOrder.Ascending): seq[T] =
-  ## returns `a` sorted by `cmp` in the specified `order`.
+                order = SortOrder.Ascending): seq[T] {.effectsOf: cmp.} =
+  ## Returns `a` sorted by `cmp` in the specified `order`.
+  ##
+  ## **See also:**
+  ## * `sort func<#sort,openArray[T],proc(T,T)>`_
+  ## * `sort proc<#sort,openArray[T]>`_
+  ## * `sortedByIt template<#sortedByIt.t,untyped,untyped>`_
+  runnableExamples:
+    let
+      a = [2, 3, 1, 5, 4]
+      b = sorted(a, system.cmp[int])
+      c = sorted(a, system.cmp[int], Descending)
+      d = sorted(["adam", "dande", "brian", "cat"], system.cmp[string])
+    assert b == @[1, 2, 3, 4, 5]
+    assert c == @[5, 4, 3, 2, 1]
+    assert d == @["adam", "brian", "cat", "dande"]
   result = newSeq[T](a.len)
   for i in 0 .. a.high:
     result[i] = a[i]
   sort(result, cmp, order)
 
-template sortedByIt*(seq1, op: untyped): untyped =
-  ## Convenience template around the ``sorted`` proc to reduce typing.
-  ##
-  ## The template injects the ``it`` variable which you can use directly in an
-  ## expression. Example:
-  ##
-  ## .. code-block:: nim
+proc sorted*[T](a: openArray[T], order = SortOrder.Ascending): seq[T] =
+  ## Shortcut version of `sorted` that uses `system.cmp[T]` as the comparison function.
   ##
-  ##   type Person = tuple[name: string, age: int]
-  ##   var
-  ##     p1: Person = (name: "p1", age: 60)
-  ##     p2: Person = (name: "p2", age: 20)
-  ##     p3: Person = (name: "p3", age: 30)
-  ##     p4: Person = (name: "p4", age: 30)
-  ##     people = @[p1,p2,p4,p3]
-  ##
-  ##   echo people.sortedByIt(it.name)
-  ##
-  ## Because the underlying ``cmp()`` is defined for tuples you can do
-  ## a nested sort like in the following example:
+  ## **See also:**
+  ## * `sort func<#sort,openArray[T],proc(T,T)>`_
+  ## * `sort proc<#sort,openArray[T]>`_
+  ## * `sortedByIt template<#sortedByIt.t,untyped,untyped>`_
+  runnableExamples:
+    let
+      a = [2, 3, 1, 5, 4]
+      b = sorted(a)
+      c = sorted(a, Descending)
+      d = sorted(["adam", "dande", "brian", "cat"])
+    assert b == @[1, 2, 3, 4, 5]
+    assert c == @[5, 4, 3, 2, 1]
+    assert d == @["adam", "brian", "cat", "dande"]
+  sorted[T](a, system.cmp[T], order)
+
+template sortedByIt*(seq1, op: untyped): untyped =
+  ## Convenience template around the `sorted` proc to reduce typing.
   ##
-  ## .. code-block:: nim
+  ## The template injects the `it` variable which you can use directly in an
+  ## expression.
   ##
-  ##   echo people.sortedByIt((it.age, it.name))
+  ## Because the underlying `cmp()` is defined for tuples you can also do
+  ## a nested sort.
   ##
-  var result = sorted(seq1, proc(x, y: type(seq1[0])): int =
+  ## **See also:**
+  ## * `sort func<#sort,openArray[T],proc(T,T)>`_
+  ## * `sort proc<#sort,openArray[T]>`_
+  ## * `sorted proc<#sorted,openArray[T],proc(T,T)>`_ sorted by `cmp` in the specified order
+  ## * `sorted proc<#sorted,openArray[T]>`_
+  runnableExamples:
+    type Person = tuple[name: string, age: int]
+    var
+      p1: Person = (name: "p1", age: 60)
+      p2: Person = (name: "p2", age: 20)
+      p3: Person = (name: "p3", age: 30)
+      p4: Person = (name: "p4", age: 30)
+      people = @[p1, p2, p4, p3]
+
+    assert people.sortedByIt(it.name) == @[(name: "p1", age: 60), (name: "p2",
+        age: 20), (name: "p3", age: 30), (name: "p4", age: 30)]
+    # Nested sort
+    assert people.sortedByIt((it.age, it.name)) == @[(name: "p2", age: 20),
+       (name: "p3", age: 30), (name: "p4", age: 30), (name: "p1", age: 60)]
+  var result = sorted(seq1, proc(x, y: typeof(items(seq1), typeOfIter)): int =
     var it {.inject.} = x
     let a = op
     it = y
@@ -308,60 +504,203 @@ template sortedByIt*(seq1, op: untyped): untyped =
     result = cmp(a, b))
   result
 
-proc isSorted*[T](a: openarray[T],
+func isSorted*[T](a: openArray[T],
                  cmp: proc(x, y: T): int {.closure.},
-                 order = SortOrder.Ascending): bool =
+                 order = SortOrder.Ascending): bool {.effectsOf: cmp.} =
   ## Checks to see whether `a` is already sorted in `order`
-  ## using `cmp` for the comparison. Parameters identical
-  ## to `sort`
+  ## using `cmp` for the comparison. The parameters are identical
+  ## to `sort`. Requires O(n) time.
+  ##
+  ## **See also:**
+  ## * `isSorted proc<#isSorted,openArray[T]>`_
+  runnableExamples:
+    let
+      a = [2, 3, 1, 5, 4]
+      b = [1, 2, 3, 4, 5]
+      c = [5, 4, 3, 2, 1]
+      d = ["adam", "brian", "cat", "dande"]
+      e = ["adam", "dande", "brian", "cat"]
+    assert isSorted(a) == false
+    assert isSorted(b) == true
+    assert isSorted(c) == false
+    assert isSorted(c, Descending) == true
+    assert isSorted(d) == true
+    assert isSorted(e) == false
   result = true
   for i in 0..<len(a)-1:
-    if cmp(a[i],a[i+1]) * order > 0:
+    if cmp(a[i], a[i+1]) * order > 0:
       return false
 
+proc isSorted*[T](a: openArray[T], order = SortOrder.Ascending): bool =
+  ## Shortcut version of `isSorted` that uses `system.cmp[T]` as the comparison function.
+  ##
+  ## **See also:**
+  ## * `isSorted func<#isSorted,openArray[T],proc(T,T)>`_
+  runnableExamples:
+    let
+      a = [2, 3, 1, 5, 4]
+      b = [1, 2, 3, 4, 5]
+      c = [5, 4, 3, 2, 1]
+      d = ["adam", "brian", "cat", "dande"]
+      e = ["adam", "dande", "brian", "cat"]
+    assert isSorted(a) == false
+    assert isSorted(b) == true
+    assert isSorted(c) == false
+    assert isSorted(c, Descending) == true
+    assert isSorted(d) == true
+    assert isSorted(e) == false
+  isSorted(a, system.cmp[T], order)
+
+proc merge*[T](
+  result: var seq[T],
+  x, y: openArray[T], cmp: proc(x, y: T): int {.closure.}
+) {.since: (1, 5, 1), effectsOf: cmp.} =
+  ## Merges two sorted `openArray`. `x` and `y` are assumed to be sorted.
+  ## If you do not wish to provide your own `cmp`,
+  ## you may use `system.cmp` or instead call the overloaded
+  ## version of `merge`, which uses `system.cmp`.
+  ##
+  ## .. note:: The original data of `result` is not cleared,
+  ##    new data is appended to `result`.
+  ##
+  ## **See also:**
+  ## * `merge proc<#merge,seq[T],openArray[T],openArray[T]>`_
+  runnableExamples:
+    let x = @[1, 3, 6]
+    let y = @[2, 3, 4]
+
+    block:
+      var merged = @[7] # new data is appended to merged sequence
+      merged.merge(x, y, system.cmp[int])
+      assert merged == @[7, 1, 2, 3, 3, 4, 6]
+
+    block:
+      var merged = @[7] # if you only want new data, clear merged sequence first
+      merged.setLen(0)
+      merged.merge(x, y, system.cmp[int])
+      assert merged.isSorted
+      assert merged == @[1, 2, 3, 3, 4, 6]
+
+    import std/sugar
+
+    var res: seq[(int, int)]
+    res.merge([(1, 1)], [(1, 2)], (a, b) => a[0] - b[0])
+    assert res == @[(1, 1), (1, 2)]
+
+    assert seq[int].default.dup(merge([1, 3], [2, 4])) == @[1, 2, 3, 4]
+
+  let
+    sizeX = x.len
+    sizeY = y.len
+    oldLen = result.len
+
+  result.setLen(oldLen + sizeX + sizeY)
+
+  var
+    ix = 0
+    iy = 0
+    i = oldLen
+
+  while true:
+    if ix == sizeX:
+      while iy < sizeY:
+        result[i] = y[iy]
+        inc i
+        inc iy
+      return
+
+    if iy == sizeY:
+      while ix < sizeX:
+        result[i] = x[ix]
+        inc i
+        inc ix
+      return
+
+    let itemX = x[ix]
+    let itemY = y[iy]
+
+    if cmp(itemX, itemY) > 0: # to have a stable sort
+      result[i] = itemY
+      inc iy
+    else:
+      result[i] = itemX
+      inc ix
+
+    inc i
+
+proc merge*[T](result: var seq[T], x, y: openArray[T]) {.inline, since: (1, 5, 1).} =
+  ## Shortcut version of `merge` that uses `system.cmp[T]` as the comparison function.
+  ##
+  ## **See also:**
+  ## * `merge proc<#merge,seq[T],openArray[T],openArray[T],proc(T,T)>`_
+  runnableExamples:
+    let x = [5, 10, 15, 20, 25]
+    let y = [50, 40, 30, 20, 10].sorted
+
+    var merged: seq[int]
+    merged.merge(x, y)
+    assert merged.isSorted
+    assert merged == @[5, 10, 10, 15, 20, 20, 25, 30, 40, 50]
+  merge(result, x, y, system.cmp)
+
 proc product*[T](x: openArray[seq[T]]): seq[seq[T]] =
-  ## produces the Cartesian product of the array. Warning: complexity
-  ## may explode.
+  ## Produces the Cartesian product of the array.
+  ## Every element of the result is a combination of one element from each seq in `x`,
+  ## with the ith element coming from `x[i]`.
+  ##
+  ## .. warning:: complexity may explode.
+  runnableExamples:
+    assert product(@[@[1], @[2]]) == @[@[1, 2]]
+    assert product(@[@["A", "K"], @["Q"]]) == @[@["K", "Q"], @["A", "Q"]]
+  let xLen = x.len
   result = newSeq[seq[T]]()
-  if x.len == 0:
+  if xLen == 0:
     return
-  if x.len == 1:
+  if xLen == 1:
     result = @x
     return
   var
-    indexes = newSeq[int](x.len)
-    initial = newSeq[int](x.len)
+    indices = newSeq[int](xLen)
+    initial = newSeq[int](xLen)
     index = 0
-  var next = newSeq[T]()
-  next.setLen(x.len)
-  for i in 0..(x.len-1):
+  var next = newSeq[T](xLen)
+  for i in 0 ..< xLen:
     if len(x[i]) == 0: return
-    initial[i] = len(x[i])-1
-  indexes = initial
+    initial[i] = len(x[i]) - 1
+  indices = initial
   while true:
-    while indexes[index] == -1:
-      indexes[index] = initial[index]
+    while indices[index] == -1:
+      indices[index] = initial[index]
       index += 1
-      if index == x.len: return
-      indexes[index] -= 1
-    for ni, i in indexes:
+      if index == xLen: return
+      indices[index] -= 1
+    for ni, i in indices:
       next[ni] = x[ni][i]
-    var res: seq[T]
-    shallowCopy(res, next)
-    result.add(res)
+    result.add(next)
     index = 0
-    indexes[index] -= 1
+    indices[index] -= 1
 
-proc nextPermutation*[T](x: var openarray[T]): bool {.discardable.} =
-  ## Calculates the next lexicographic permutation, directly modifying ``x``.
+proc nextPermutation*[T](x: var openArray[T]): bool {.discardable.} =
+  ## Calculates the next lexicographic permutation, directly modifying `x`.
   ## The result is whether a permutation happened, otherwise we have reached
   ## the last-ordered permutation.
   ##
-  ## .. code-block:: nim
+  ## If you start with an unsorted array/seq, the repeated permutations
+  ## will **not** give you all permutations but stop with the last.
   ##
-  ##     var v = @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
-  ##     v.nextPermutation()
-  ##     echo v # @[0, 1, 2, 3, 4, 5, 6, 7, 9, 8]
+  ## **See also:**
+  ## * `prevPermutation proc<#prevPermutation,openArray[T]>`_
+  runnableExamples:
+    var v = @[0, 1, 2, 3]
+    assert v.nextPermutation() == true
+    assert v == @[0, 1, 3, 2]
+    assert v.nextPermutation() == true
+    assert v == @[0, 2, 1, 3]
+    assert v.prevPermutation() == true
+    assert v == @[0, 1, 3, 2]
+    v = @[3, 2, 1, 0]
+    assert v.nextPermutation() == false
+    assert v == @[3, 2, 1, 0]
   if x.len < 2:
     return false
 
@@ -381,16 +720,21 @@ proc nextPermutation*[T](x: var openarray[T]): bool {.discardable.} =
 
   result = true
 
-proc prevPermutation*[T](x: var openarray[T]): bool {.discardable.} =
+proc prevPermutation*[T](x: var openArray[T]): bool {.discardable.} =
   ## Calculates the previous lexicographic permutation, directly modifying
-  ## ``x``.  The result is whether a permutation happened, otherwise we have
+  ## `x`. The result is whether a permutation happened, otherwise we have
   ## reached the first-ordered permutation.
   ##
-  ## .. code-block:: nim
-  ##
-  ##     var v = @[0, 1, 2, 3, 4, 5, 6, 7, 9, 8]
-  ##     v.prevPermutation()
-  ##     echo v # @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+  ## **See also:**
+  ## * `nextPermutation proc<#nextPermutation,openArray[T]>`_
+  runnableExamples:
+    var v = @[0, 1, 2, 3]
+    assert v.prevPermutation() == false
+    assert v == @[0, 1, 2, 3]
+    assert v.nextPermutation() == true
+    assert v == @[0, 1, 3, 2]
+    assert v.prevPermutation() == true
+    assert v == @[0, 1, 2, 3]
   if x.len < 2:
     return false
 
@@ -411,38 +755,9 @@ proc prevPermutation*[T](x: var openarray[T]): bool {.discardable.} =
 
   result = true
 
-when isMainModule:
-  # Tests for lowerBound
-  var arr = @[1,2,3,5,6,7,8,9]
-  assert arr.lowerBound(0) == 0
-  assert arr.lowerBound(4) == 3
-  assert arr.lowerBound(5) == 3
-  assert arr.lowerBound(10) == 8
-  arr = @[1,5,10]
-  assert arr.lowerBound(4) == 1
-  assert arr.lowerBound(5) == 1
-  assert arr.lowerBound(6) == 2
-  # Tests for isSorted
-  var srt1 = [1,2,3,4,4,4,4,5]
-  var srt2 = ["iello","hello"]
-  var srt3 = [1.0,1.0,1.0]
-  var srt4: seq[int]
-  assert srt1.isSorted(cmp) == true
-  assert srt2.isSorted(cmp) == false
-  assert srt3.isSorted(cmp) == true
-  assert srt4.isSorted(cmp) == true
-  var srtseq = newSeq[int]()
-  assert srtseq.isSorted(cmp) == true
-  # Tests for reversed
-  var arr1 = @[0,1,2,3,4]
-  assert arr1.reversed() == @[4,3,2,1,0]
-  for i in 0 .. high(arr1):
-    assert arr1.reversed(0, i) == arr1.reversed()[high(arr1) - i .. high(arr1)]
-    assert arr1.reversed(i, high(arr1)) == arr1.reversed()[0 .. high(arr1) - i]
-
-
-proc rotateInternal[T](arg: var openarray[T]; first, middle, last: int): int =
-  ## A port of std::rotate from c++. Ported from `this reference <http://www.cplusplus.com/reference/algorithm/rotate/>`_.
+proc rotateInternal[T](arg: var openArray[T]; first, middle, last: int): int =
+  ## A port of std::rotate from C++.
+  ## Ported from [this reference](http://www.cplusplus.com/reference/algorithm/rotate/).
   result = first + last - middle
 
   if first == middle or middle == last:
@@ -480,127 +795,123 @@ proc rotateInternal[T](arg: var openarray[T]; first, middle, last: int): int =
     elif next == last:
       next = mMiddle
 
-proc rotatedInternal[T](arg: openarray[T]; first, middle, last: int): seq[T] =
-  result = newSeq[T](arg.len)
+proc rotatedInternal[T](arg: openArray[T]; first, middle, last: int): seq[T] =
+  let argLen = arg.len
+  result = newSeq[T](argLen)
   for i in 0 ..< first:
     result[i] = arg[i]
-  let N = last - middle
-  let M = middle - first
-  for i in 0 ..< N:
+  let n = last - middle
+  let m = middle - first
+  for i in 0 ..< n:
     result[first+i] = arg[middle+i]
-  for i in 0 ..< M:
-    result[first+N+i] = arg[first+i]
-  for i in last ..< arg.len:
+  for i in 0 ..< m:
+    result[first+n+i] = arg[first+i]
+  for i in last ..< argLen:
     result[i] = arg[i]
 
-proc rotateLeft*[T](arg: var openarray[T]; slice: HSlice[int, int]; dist: int): int =
-  ## Performs a left rotation on a range of elements. If you want to rotate right, use a negative ``dist``.
-  ## Specifically, ``rotateLeft`` rotates the elements at ``slice`` by ``dist`` positions.
-  ## The element at index ``slice.a + dist`` will be at index ``slice.a``.
-  ## The element at index ``slice.b`` will be at ``slice.a + dist -1``.
-  ## The element at index ``slice.a`` will be at ``slice.b + 1 - dist``.
-  ## The element at index ``slice.a + dist - 1`` will be at ``slice.b``.
-  #
-  ## Elements outsize of ``slice`` will be left unchanged.
-  ## The time complexity is linear to ``slice.b - slice.a + 1``.
-  ##
-  ## ``slice``
-  ##   the indices of the element range that should be rotated.
-  ##
-  ## ``dist``
-  ##   the distance in amount of elements that the data should be rotated. Can be negative, can be any number.
-  ##
-  ## .. code-block:: nim
-  ##     var list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-  ##     list.rotateLeft(1 .. 8, 3)
-  ##     doAssert list == [0, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10]
+proc rotateLeft*[T](arg: var openArray[T]; slice: HSlice[int, int];
+                    dist: int): int {.discardable.} =
+  ## Performs a left rotation on a range of elements. If you want to rotate
+  ## right, use a negative `dist`. Specifically, `rotateLeft` rotates
+  ## the elements at `slice` by `dist` positions.
+  ##
+  ## | The element at index `slice.a + dist` will be at index `slice.a`.
+  ## | The element at index `slice.b` will be at `slice.a + dist - 1`.
+  ## | The element at index `slice.a` will be at `slice.b + 1 - dist`.
+  ## | The element at index `slice.a + dist - 1` will be at `slice.b`.
+  ##
+  ## Elements outside of `slice` will be left unchanged.
+  ## The time complexity is linear to `slice.b - slice.a + 1`.
+  ## If an invalid range (`HSlice`) is passed, it raises `IndexDefect`.
+  ##
+  ## `slice`
+  ## : The indices of the element range that should be rotated.
+  ##
+  ## `dist`
+  ## : The distance in amount of elements that the data should be rotated.
+  ##   Can be negative, can be any number.
+  ##
+  ## **See also:**
+  ## * `rotateLeft proc<#rotateLeft,openArray[T],int>`_ for a version which rotates the whole container
+  ## * `rotatedLeft proc<#rotatedLeft,openArray[T],HSlice[int,int],int>`_ for a version which returns a `seq[T]`
+  runnableExamples:
+    var a = [0, 1, 2, 3, 4, 5]
+    a.rotateLeft(1 .. 4, 3)
+    assert a == [0, 4, 1, 2, 3, 5]
+    a.rotateLeft(1 .. 4, 3)
+    assert a == [0, 3, 4, 1, 2, 5]
+    a.rotateLeft(1 .. 4, -3)
+    assert a == [0, 4, 1, 2, 3, 5]
+    doAssertRaises(IndexDefect, a.rotateLeft(1 .. 7, 2))
   let sliceLen = slice.b + 1 - slice.a
   let distLeft = ((dist mod sliceLen) + sliceLen) mod sliceLen
-  arg.rotateInternal(slice.a, slice.a+distLeft, slice.b + 1)
-
-proc rotateLeft*[T](arg: var openarray[T]; dist: int): int =
-  ## default arguments for slice, so that this procedure operates on the entire
-  ## ``arg``, and not just on a part of it.
-  let arglen = arg.len
-  let distLeft = ((dist mod arglen) + arglen) mod arglen
-  arg.rotateInternal(0, distLeft, arglen)
-
-proc rotatedLeft*[T](arg: openarray[T]; slice: HSlice[int, int], dist: int): seq[T] =
-  ## same as ``rotateLeft``, just with the difference that it does
-  ## not modify the argument. It creates a new ``seq`` instead
+  arg.rotateInternal(slice.a, slice.a + distLeft, slice.b + 1)
+
+proc rotateLeft*[T](arg: var openArray[T]; dist: int): int {.discardable.} =
+  ## Same as `rotateLeft`, but with default arguments for slice,
+  ## so that this procedure operates on the entire
+  ## `arg`, and not just on a part of it.
+  ##
+  ## **See also:**
+  ## * `rotateLeft proc<#rotateLeft,openArray[T],HSlice[int,int],int>`_ for a version which rotates a range
+  ## * `rotatedLeft proc<#rotatedLeft,openArray[T],int>`_ for a version which returns a `seq[T]`
+  runnableExamples:
+    var a = [1, 2, 3, 4, 5]
+    a.rotateLeft(2)
+    assert a == [3, 4, 5, 1, 2]
+    a.rotateLeft(4)
+    assert a == [2, 3, 4, 5, 1]
+    a.rotateLeft(-6)
+    assert a == [1, 2, 3, 4, 5]
+  let argLen = arg.len
+  let distLeft = ((dist mod argLen) + argLen) mod argLen
+  arg.rotateInternal(0, distLeft, argLen)
+
+proc rotatedLeft*[T](arg: openArray[T]; slice: HSlice[int, int],
+                     dist: int): seq[T] =
+  ## Same as `rotateLeft`, just with the difference that it does
+  ## not modify the argument. It creates a new `seq` instead.
+  ##
+  ## Elements outside of `slice` will be left unchanged.
+  ## If an invalid range (`HSlice`) is passed, it raises `IndexDefect`.
+  ##
+  ## `slice`
+  ## : The indices of the element range that should be rotated.
+  ##
+  ## `dist`
+  ## : The distance in amount of elements that the data should be rotated.
+  ##   Can be negative, can be any number.
+  ##
+  ## **See also:**
+  ## * `rotateLeft proc<#rotateLeft,openArray[T],HSlice[int,int],int>`_ for the in-place version of this proc
+  ## * `rotatedLeft proc<#rotatedLeft,openArray[T],int>`_ for a version which rotates the whole container
+  runnableExamples:
+    var a = @[1, 2, 3, 4, 5]
+    a = rotatedLeft(a, 1 .. 4, 3)
+    assert a == @[1, 5, 2, 3, 4]
+    a = rotatedLeft(a, 1 .. 3, 2)
+    assert a == @[1, 3, 5, 2, 4]
+    a = rotatedLeft(a, 1 .. 3, -2)
+    assert a == @[1, 5, 2, 3, 4]
   let sliceLen = slice.b + 1 - slice.a
   let distLeft = ((dist mod sliceLen) + sliceLen) mod sliceLen
-  arg.rotatedInternal(slice.a, slice.a+distLeft, slice.b+1)
-
-proc rotatedLeft*[T](arg: openarray[T]; dist: int): seq[T] =
-  ## same as ``rotateLeft``, just with the difference that it does
-  ## not modify the argument. It creates a new ``seq`` instead
-  let arglen = arg.len
-  let distLeft = ((dist mod arglen) + arglen) mod arglen
-  arg.rotatedInternal(0, distLeft, arg.len)
-
-when isMainModule:
-  var list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-  let list2 = list.rotatedLeft(1 ..< 9, 3)
-  let expected = [0, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10]
-
-  doAssert list.rotateLeft(1 ..< 9, 3) == 6
-  doAssert list == expected
-  doAssert list2 == @expected
-
-  var s0,s1,s2,s3,s4,s5 = "xxxabcdefgxxx"
-
-  doAssert s0.rotateLeft(3 ..< 10, 3) == 7
-  doAssert s0 == "xxxdefgabcxxx"
-  doAssert s1.rotateLeft(3 ..< 10, 2) == 8
-  doAssert s1 == "xxxcdefgabxxx"
-  doAssert s2.rotateLeft(3 ..< 10, 4) == 6
-  doAssert s2 == "xxxefgabcdxxx"
-  doAssert s3.rotateLeft(3 ..< 10, -3) == 6
-  doAssert s3 == "xxxefgabcdxxx"
-  doAssert s4.rotateLeft(3 ..< 10, -10) == 6
-  doAssert s4 == "xxxefgabcdxxx"
-  doAssert s5.rotateLeft(3 ..< 10, 11) == 6
-  doAssert s5 == "xxxefgabcdxxx"
-
-  block product:
-    doAssert product(newSeq[seq[int]]()) == newSeq[seq[int]](), "empty input"
-    doAssert product(@[newSeq[int](), @[], @[]]) == newSeq[seq[int]](), "bit more empty input"
-    doAssert product(@[@[1,2]]) == @[@[1,2]], "a simple case of one element"
-    doAssert product(@[@[1,2], @[3,4]]) == @[@[2,4],@[1,4],@[2,3],@[1,3]], "two elements"
-    doAssert product(@[@[1,2], @[3,4], @[5,6]]) == @[@[2,4,6],@[1,4,6],@[2,3,6],@[1,3,6], @[2,4,5],@[1,4,5],@[2,3,5],@[1,3,5]], "three elements"
-    doAssert product(@[@[1,2], @[]]) == newSeq[seq[int]](), "two elements, but one empty"
-
-  block lowerBound:
-    doAssert lowerBound([1,2,4], 3, system.cmp[int]) == 2
-    doAssert lowerBound([1,2,2,3], 4, system.cmp[int]) == 4
-    doAssert lowerBound([1,2,3,10], 11) == 4
-
-  block upperBound:
-    doAssert upperBound([1,2,4], 3, system.cmp[int]) == 2
-    doAssert upperBound([1,2,2,3], 3, system.cmp[int]) == 4
-    doAssert upperBound([1,2,3,5], 3) == 3
-
-  block fillEmptySeq:
-    var s = newSeq[int]()
-    s.fill(0)
-
-  block testBinarySearch:
-    var noData: seq[int]
-    doAssert binarySearch(noData, 7) == -1
-    let oneData = @[1]
-    doAssert binarySearch(oneData, 1) == 0
-    doAssert binarySearch(onedata, 7) == -1
-    let someData = @[1,3,4,7]
-    doAssert binarySearch(someData, 1) == 0
-    doAssert binarySearch(somedata, 7) == 3
-    doAssert binarySearch(someData, -1) == -1
-    doAssert binarySearch(someData, 5) == -1
-    doAssert binarySearch(someData, 13) == -1
-    let moreData = @[1,3,5,7,4711]
-    doAssert binarySearch(moreData, -1) == -1
-    doAssert binarySearch(moreData,  1) == 0
-    doAssert binarySearch(moreData,  5) == 2
-    doAssert binarySearch(moreData,  6) == -1
-    doAssert binarySearch(moreData,  4711) == 4
-    doAssert binarySearch(moreData,  4712) == -1
+  arg.rotatedInternal(slice.a, slice.a + distLeft, slice.b + 1)
+
+proc rotatedLeft*[T](arg: openArray[T]; dist: int): seq[T] =
+  ## Same as `rotateLeft`, just with the difference that it does
+  ## not modify the argument. It creates a new `seq` instead.
+  ##
+  ## **See also:**
+  ## * `rotateLeft proc<#rotateLeft,openArray[T],int>`_ for the in-place version of this proc
+  ## * `rotatedLeft proc<#rotatedLeft,openArray[T],HSlice[int,int],int>`_ for a version which rotates a range
+  runnableExamples:
+    var a = @[1, 2, 3, 4, 5]
+    a = rotatedLeft(a, 2)
+    assert a == @[3, 4, 5, 1, 2]
+    a = rotatedLeft(a, 4)
+    assert a == @[2, 3, 4, 5, 1]
+    a = rotatedLeft(a, -6)
+    assert a == @[1, 2, 3, 4, 5]
+  let argLen = arg.len
+  let distLeft = ((dist mod argLen) + argLen) mod argLen
+  arg.rotatedInternal(0, distLeft, argLen)
diff --git a/lib/pure/async.nim b/lib/pure/async.nim
index 97b29f81d..e4d8d41c3 100644
--- a/lib/pure/async.nim
+++ b/lib/pure/async.nim
@@ -1,6 +1,9 @@
+## Exports [asyncmacro](asyncmacro.html) and [asyncfutures](asyncfutures.html) for native backends,
+## and [asyncjs](asyncjs.html) on the JS backend. 
+
 when defined(js):
-    import asyncjs
-    export asyncjs
+  import std/asyncjs
+  export asyncjs
 else:
-    import asyncmacro, asyncfutures
-    export asyncmacro, asyncfutures
+  import std/[asyncmacro, asyncfutures]
+  export asyncmacro, asyncfutures
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index dfc7201b8..126db7a7f 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -7,41 +7,26 @@
 #    distribution, for details about the copyright.
 #
 
-include "system/inclrtl"
-
-import os, tables, strutils, times, heapqueue, lists, options, asyncstreams
-import asyncfutures except callSoon
-
-import nativesockets, net, deques
-
-export Port, SocketFlag
-export asyncfutures, asyncstreams
-
-#{.injectStmt: newGcInvariant().}
-
-## AsyncDispatch
-## *************
-##
 ## This module implements asynchronous IO. This includes a dispatcher,
-## a ``Future`` type implementation, and an ``async`` macro which allows
-## asynchronous code to be written in a synchronous style with the ``await``
+## a `Future` type implementation, and an `async` macro which allows
+## asynchronous code to be written in a synchronous style with the `await`
 ## keyword.
 ##
-## The dispatcher acts as a kind of event loop. You must call ``poll`` on it
-## (or a function which does so for you such as ``waitFor`` or ``runForever``)
+## The dispatcher acts as a kind of event loop. You must call `poll` on it
+## (or a function which does so for you such as `waitFor` or `runForever`)
 ## in order to poll for any outstanding events. The underlying implementation
 ## is based on epoll on Linux, IO Completion Ports on Windows and select on
 ## other operating systems.
 ##
-## The ``poll`` function will not, on its own, return any events. Instead
-## an appropriate ``Future`` object will be completed. A ``Future`` is a
+## The `poll` function will not, on its own, return any events. Instead
+## an appropriate `Future` object will be completed. A `Future` is a
 ## type which holds a value which is not yet available, but which *may* be
 ## available in the future. You can check whether a future is finished
-## by using the ``finished`` function. When a future is finished it means that
+## by using the `finished` function. When a future is finished it means that
 ## either the value that it holds is now available or it holds an error instead.
 ## The latter situation occurs when the operation to complete a future fails
 ## with an exception. You can distinguish between the two situations with the
-## ``failed`` function.
+## `failed` function.
 ##
 ## Future objects can also store a callback procedure which will be called
 ## automatically once the future completes.
@@ -50,50 +35,50 @@ export asyncfutures, asyncstreams
 ## pattern. In this
 ## pattern you make a request for an action, and once that action is fulfilled
 ## a future is completed with the result of that action. Requests can be
-## made by calling the appropriate functions. For example: calling the ``recv``
+## made by calling the appropriate functions. For example: calling the `recv`
 ## function will create a request for some data to be read from a socket. The
-## future which the ``recv`` function returns will then complete once the
+## future which the `recv` function returns will then complete once the
 ## requested amount of data is read **or** an exception occurs.
 ##
 ## Code to read some data from a socket may look something like this:
+##   ```Nim
+##   var future = socket.recv(100)
+##   future.addCallback(
+##     proc () =
+##       echo(future.read)
+##   )
+##   ```
 ##
-##   .. code-block::nim
-##      var future = socket.recv(100)
-##      future.addCallback(
-##        proc () =
-##          echo(future.read)
-##      )
-##
-## All asynchronous functions returning a ``Future`` will not block. They
+## All asynchronous functions returning a `Future` will not block. They
 ## will not however return immediately. An asynchronous function will have
 ## code which will be executed before an asynchronous request is made, in most
 ## cases this code sets up the request.
 ##
-## In the above example, the ``recv`` function will return a brand new
-## ``Future`` instance once the request for data to be read from the socket
-## is made. This ``Future`` instance will complete once the requested amount
+## In the above example, the `recv` function will return a brand new
+## `Future` instance once the request for data to be read from the socket
+## is made. This `Future` instance will complete once the requested amount
 ## of data is read, in this case it is 100 bytes. The second line sets a
 ## callback on this future which will be called once the future completes.
-## All the callback does is write the data stored in the future to ``stdout``.
-## The ``read`` function is used for this and it checks whether the future
-## completes with an error for you (if it did it will simply raise the
-## error), if there is no error however it returns the value of the future.
+## All the callback does is write the data stored in the future to `stdout`.
+## The `read` function is used for this and it checks whether the future
+## completes with an error for you (if it did, it will simply raise the
+## error), if there is no error, however, it returns the value of the future.
 ##
 ## Asynchronous procedures
-## -----------------------
+## =======================
 ##
 ## Asynchronous procedures remove the pain of working with callbacks. They do
 ## this by allowing you to write asynchronous code the same way as you would
 ## write synchronous code.
 ##
-## An asynchronous procedure is marked using the ``{.async.}`` pragma.
-## When marking a procedure with the ``{.async.}`` pragma it must have a
-## ``Future[T]`` return type or no return type at all. If you do not specify
-## a return type then ``Future[void]`` is assumed.
+## An asynchronous procedure is marked using the `{.async.}` pragma.
+## When marking a procedure with the `{.async.}` pragma it must have a
+## `Future[T]` return type or no return type at all. If you do not specify
+## a return type then `Future[void]` is assumed.
 ##
-## Inside asynchronous procedures ``await`` can be used to call any
+## Inside asynchronous procedures `await` can be used to call any
 ## procedures which return a
-## ``Future``; this includes asynchronous procedures. When a procedure is
+## `Future`; this includes asynchronous procedures. When a procedure is
 ## "awaited", the asynchronous procedure it is awaited in will
 ## suspend its execution
 ## until the awaited procedure's Future completes. At which point the
@@ -101,126 +86,242 @@ export asyncfutures, asyncstreams
 ## when an asynchronous procedure is suspended other asynchronous procedures
 ## will be run by the dispatcher.
 ##
-## The ``await`` call may be used in many contexts. It can be used on the right
-## hand side of a variable declaration: ``var data = await socket.recv(100)``,
+## The `await` call may be used in many contexts. It can be used on the right
+## hand side of a variable declaration: `var data = await socket.recv(100)`,
 ## in which case the variable will be set to the value of the future
-## automatically. It can be used to await a ``Future`` object, and it can
-## be used to await a procedure returning a ``Future[void]``:
-## ``await socket.send("foobar")``.
+## automatically. It can be used to await a `Future` object, and it can
+## be used to await a procedure returning a `Future[void]`:
+## `await socket.send("foobar")`.
 ##
-## If an awaited future completes with an error, then ``await`` will re-raise
-## this error. To avoid this, you can use the ``yield`` keyword instead of
-## ``await``. The following section shows different ways that you can handle
+## If an awaited future completes with an error, then `await` will re-raise
+## this error. To avoid this, you can use the `yield` keyword instead of
+## `await`. The following section shows different ways that you can handle
 ## exceptions in async procs.
 ##
-## Handling Exceptions
-## ~~~~~~~~~~~~~~~~~~~
+## .. caution::
+##     Procedures marked {.async.} do not support mutable parameters such
+##     as `var int`. References such as `ref int` should be used instead.
 ##
-## The most reliable way to handle exceptions is to use ``yield`` on a future
-## then check the future's ``failed`` property. For example:
+## Handling Exceptions
+## -------------------
 ##
-##   .. code-block:: Nim
-##     var future = sock.recv(100)
-##     yield future
-##     if future.failed:
-##       # Handle exception
+## You can handle exceptions in the same way as in ordinary Nim code;
+## by using the try statement:
 ##
-## The ``async`` procedures also offer limited support for the try statement.
+##   ```Nim
+##   try:
+##     let data = await sock.recv(100)
+##     echo("Received ", data)
+##   except:
+##     # Handle exception
+##   ```
 ##
-##    .. code-block:: Nim
-##      try:
-##        let data = await sock.recv(100)
-##        echo("Received ", data)
-##      except:
-##        # Handle exception
+## An alternative approach to handling exceptions is to use `yield` on a future
+## then check the future's `failed` property. For example:
 ##
-## Unfortunately the semantics of the try statement may not always be correct,
-## and occasionally the compilation may fail altogether.
-## As such it is better to use the former style when possible.
+##   ```Nim
+##   var future = sock.recv(100)
+##   yield future
+##   if future.failed:
+##     # Handle exception
+##   ```
 ##
 ##
 ## Discarding futures
-## ------------------
+## ==================
+##
+## Futures should **never** be discarded directly because they may contain
+## errors. If you do not care for the result of a Future then you should use
+## the `asyncCheck` procedure instead of the `discard` keyword. Note that this
+## does not wait for completion, and you should use `waitFor` or `await` for that purpose.
+##
+## .. note:: `await` also checks if the future fails, so you can safely discard
+##   its result.
+##
+## Handling futures
+## ================
+##
+## There are many different operations that apply to a future.
+## The three primary high-level operations are `asyncCheck`,
+## `waitFor`, and `await`.
 ##
-## Futures should **never** be discarded. This is because they may contain
-## errors. If you do not care for the result of a Future then you should
-## use the ``asyncCheck`` procedure instead of the ``discard`` keyword.
+## * `asyncCheck`: Raises an exception if the future fails. It neither waits
+##   for the future to finish nor returns the result of the future.
+## * `waitFor`: Polls the event loop and blocks the current thread until the
+##   future finishes. This is often used to call an async procedure from a
+##   synchronous context and should never be used in an `async` proc.
+## * `await`: Pauses execution in the current async procedure until the future
+##   finishes. While the current procedure is paused, other async procedures will
+##   continue running. Should be used instead of `waitFor` in an async
+##   procedure.
+##
+## Here is a handy quick reference chart showing their high-level differences:
+## ==============  =====================   =======================
+## Procedure       Context                 Blocking
+## ==============  =====================   =======================
+## `asyncCheck`    non-async and async     non-blocking
+## `waitFor`       non-async               blocks current thread
+## `await`         async                   suspends current proc
+## ==============  =====================   =======================
 ##
 ## Examples
-## --------
+## ========
 ##
 ## For examples take a look at the documentation for the modules implementing
 ## asynchronous IO. A good place to start is the
 ## `asyncnet module <asyncnet.html>`_.
 ##
+## Investigating pending futures
+## =============================
+##
+## It's possible to get into a situation where an async proc, or more accurately
+## a `Future[T]` gets stuck and
+## never completes. This can happen for various reasons and can cause serious
+## memory leaks. When this occurs it's hard to identify the procedure that is
+## stuck.
+##
+## Thankfully there is a mechanism which tracks the count of each pending future.
+## All you need to do to enable it is compile with `-d:futureLogging` and
+## use the `getFuturesInProgress` procedure to get the list of pending futures
+## together with the stack traces to the moment of their creation.
+##
+## You may also find it useful to use this
+## `prometheus package <https://github.com/dom96/prometheus>`_ which will log
+## the pending futures into prometheus, allowing you to analyse them via a nice
+## graph.
+##
+##
+##
 ## Limitations/Bugs
-## ----------------
+## ================
+##
+## * The effect system (`raises: []`) does not work with async procedures.
+## * Mutable parameters are not supported by async procedures.
+##
+##
+## Multiple async backend support
+## ==============================
+##
+## Thanks to its powerful macro support, Nim allows ``async``/``await`` to be
+## implemented in libraries with only minimal support from the language - as
+## such, multiple ``async`` libraries exist, including ``asyncdispatch`` and
+## ``chronos``, and more may come to be developed in the future.
+##
+## Libraries built on top of async/await may wish to support multiple async
+## backends - the best way to do so is to create separate modules for each backend
+## that may be imported side-by-side.
+##
+## An alternative way is to select backend using a global compile flag - this
+## method makes it difficult to compose applications that use both backends as may
+## happen with transitive dependencies, but may be appropriate in some cases -
+## libraries choosing this path should call the flag `asyncBackend`, allowing
+## applications to choose the backend with `-d:asyncBackend=<backend_name>`.
 ##
-## * The effect system (``raises: []``) does not work with async procedures.
-## * Can't await in a ``except`` body
-## * Forward declarations for async procs are broken,
-##   link includes workaround: https://github.com/nim-lang/Nim/issues/3182.
+## Known `async` backends include:
+##
+## * `-d:asyncBackend=none`: disable `async` support completely
+## * `-d:asyncBackend=asyncdispatch`: https://nim-lang.org/docs/asyncdispatch.html
+## * `-d:asyncBackend=chronos`: https://github.com/status-im/nim-chronos/
+##
+## ``none`` can be used when a library supports both a synchronous and
+## asynchronous API, to disable the latter.
+
+import std/[os, tables, strutils, times, heapqueue, options, asyncstreams]
+import std/[math, monotimes]
+import std/asyncfutures except callSoon
+
+import std/[nativesockets, net, deques]
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
+
+export Port, SocketFlag
+export asyncfutures except callSoon
+export asyncstreams
 
 # TODO: Check if yielded future is nil and throw a more meaningful exception
 
 type
   PDispatcherBase = ref object of RootRef
-    timers*: HeapQueue[tuple[finishAt: float, fut: Future[void]]]
-    callbacks*: Deque[proc ()]
+    timers*: HeapQueue[tuple[finishAt: MonoTime, fut: Future[void]]]
+    callbacks*: Deque[proc () {.gcsafe.}]
 
-proc processTimers(p: PDispatcherBase; didSomeWork: var bool) {.inline.} =
-  #Process just part if timers at a step
+proc processTimers(
+  p: PDispatcherBase, didSomeWork: var bool
+): Option[int] {.inline.} =
+  # Pop the timers in the order in which they will expire (smaller `finishAt`).
   var count = p.timers.len
-  let t = epochTime()
+  let t = getMonoTime()
   while count > 0 and t >= p.timers[0].finishAt:
     p.timers.pop().fut.complete()
     dec count
     didSomeWork = true
 
+  # Return the number of milliseconds in which the next timer will expire.
+  if p.timers.len == 0: return
+
+  let millisecs = (p.timers[0].finishAt - getMonoTime()).inMilliseconds
+  return some(millisecs.int + 1)
+
 proc processPendingCallbacks(p: PDispatcherBase; didSomeWork: var bool) =
   while p.callbacks.len > 0:
     var cb = p.callbacks.popFirst()
     cb()
     didSomeWork = true
 
-proc adjustedTimeout(p: PDispatcherBase, timeout: int): int {.inline.} =
-  # If dispatcher has active timers this proc returns the timeout
-  # of the nearest timer. Returns `timeout` otherwise.
-  result = timeout
-  if p.timers.len > 0:
-    let timerTimeout = p.timers[0].finishAt
-    let curTime = epochTime()
-    if timeout == -1 or (curTime + (timeout / 1000)) > timerTimeout:
-      result = int((timerTimeout - curTime) * 1000)
-      if result < 0: result = 0
+proc adjustTimeout(
+  p: PDispatcherBase, pollTimeout: int, nextTimer: Option[int]
+): int {.inline.} =
+  if p.callbacks.len != 0:
+    return 0
 
-proc callSoon(cbproc: proc ()) {.gcsafe.}
+  if nextTimer.isNone() or pollTimeout == -1:
+    return pollTimeout
+
+  result = max(nextTimer.get(), 0)
+  result = min(pollTimeout, result)
+
+proc runOnce(timeout: int): bool {.gcsafe.}
+
+proc callSoon*(cbproc: proc () {.gcsafe.}) {.gcsafe.}
+  ## Schedule `cbproc` to be called as soon as possible.
+  ## The callback is called when control returns to the event loop.
 
 proc initCallSoonProc =
   if asyncfutures.getCallSoonProc().isNil:
     asyncfutures.setCallSoonProc(callSoon)
 
+template implementSetInheritable() {.dirty.} =
+  when declared(setInheritable):
+    proc setInheritable*(fd: AsyncFD, inheritable: bool): bool =
+      ## Control whether a file handle can be inherited by child processes.
+      ## Returns `true` on success.
+      ##
+      ## This procedure is not guaranteed to be available for all platforms.
+      ## Test for availability with `declared() <system.html#declared,untyped>`_.
+      fd.FileHandle.setInheritable(inheritable)
+
 when defined(windows) or defined(nimdoc):
-  import winlean, sets, hashes
+  import std/[winlean, sets, hashes]
   type
     CompletionKey = ULONG_PTR
 
     CompletionData* = object
-      fd*: AsyncFD # TODO: Rename this.
-      cb*: proc (fd: AsyncFD, bytesTransferred: Dword,
-                errcode: OSErrorCode) {.closure,gcsafe.}
+      fd*: AsyncFD       # TODO: Rename this.
+      cb*: owned(proc (fd: AsyncFD, bytesTransferred: DWORD,
+                errcode: OSErrorCode) {.closure, gcsafe.})
       cell*: ForeignCell # we need this `cell` to protect our `cb` environment,
                          # when using RegisterWaitForSingleObject, because
                          # waiting is done in different thread.
 
     PDispatcher* = ref object of PDispatcherBase
       ioPort: Handle
-      handles: HashSet[AsyncFD]
+      handles*: HashSet[AsyncFD] # Export handles so that an external library can register them.
 
-    CustomOverlapped = object of OVERLAPPED
+    CustomObj = object of OVERLAPPED
       data*: CompletionData
 
-    PCustomOverlapped* = ref CustomOverlapped
+    CustomRef* = ref CustomObj
 
     AsyncFD* = distinct int
 
@@ -228,7 +329,7 @@ when defined(windows) or defined(nimdoc):
       ioPort: Handle
       handleFd: AsyncFD
       waitFd: Handle
-      ovl: PCustomOverlapped
+      ovl: owned CustomRef
     PostCallbackDataPtr = ptr PostCallbackData
 
     AsyncEventImpl = object
@@ -237,24 +338,22 @@ when defined(windows) or defined(nimdoc):
       pcd: PostCallbackDataPtr
     AsyncEvent* = ptr AsyncEventImpl
 
-    Callback = proc (fd: AsyncFD): bool {.closure,gcsafe.}
-  {.deprecated: [TCompletionKey: CompletionKey, TAsyncFD: AsyncFD,
-                TCustomOverlapped: CustomOverlapped, TCompletionData: CompletionData].}
+    Callback* = proc (fd: AsyncFD): bool {.closure, gcsafe.}
 
   proc hash(x: AsyncFD): Hash {.borrow.}
   proc `==`*(x: AsyncFD, y: AsyncFD): bool {.borrow.}
 
-  proc newDispatcher*(): PDispatcher =
+  proc newDispatcher*(): owned PDispatcher =
     ## Creates a new Dispatcher instance.
     new result
     result.ioPort = createIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1)
-    result.handles = initSet[AsyncFD]()
-    result.timers.newHeapQueue()
-    result.callbacks = initDeque[proc ()](64)
+    result.handles = initHashSet[AsyncFD]()
+    result.timers.clear()
+    result.callbacks = initDeque[proc () {.closure, gcsafe.}](64)
 
-  var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
+  var gDisp{.threadvar.}: owned PDispatcher ## Global dispatcher
 
-  proc setGlobalDispatcher*(disp: PDispatcher) =
+  proc setGlobalDispatcher*(disp: sink PDispatcher) =
     if not gDisp.isNil:
       assert gDisp.callbacks.len == 0
     gDisp = disp
@@ -271,7 +370,7 @@ when defined(windows) or defined(nimdoc):
     return disp.ioPort
 
   proc register*(fd: AsyncFD) =
-    ## Registers ``fd`` with the dispatcher.
+    ## Registers `fd` with the dispatcher.
     let p = getGlobalDispatcher()
 
     if createIoCompletionPort(fd.Handle, p.ioPort,
@@ -281,6 +380,7 @@ when defined(windows) or defined(nimdoc):
 
   proc verifyPresence(fd: AsyncFD) =
     ## Ensures that file descriptor has been registered with the dispatcher.
+    ## Raises ValueError if `fd` has not been registered.
     let p = getGlobalDispatcher()
     if fd notin p.handles:
       raise newException(ValueError,
@@ -292,60 +392,66 @@ when defined(windows) or defined(nimdoc):
     let p = getGlobalDispatcher()
     p.handles.len != 0 or p.timers.len != 0 or p.callbacks.len != 0
 
-  proc runOnce(timeout = 500): bool =
+  proc runOnce(timeout: int): bool =
     let p = getGlobalDispatcher()
     if p.handles.len == 0 and p.timers.len == 0 and p.callbacks.len == 0:
       raise newException(ValueError,
         "No handles or timers registered in dispatcher.")
 
     result = false
-    if p.handles.len != 0:
-      let at = p.adjustedTimeout(timeout)
-      var llTimeout =
-        if at == -1: winlean.INFINITE
-        else: at.int32
-
-      var lpNumberOfBytesTransferred: Dword
-      var lpCompletionKey: ULONG_PTR
-      var customOverlapped: PCustomOverlapped
-      let res = getQueuedCompletionStatus(p.ioPort,
-          addr lpNumberOfBytesTransferred, addr lpCompletionKey,
-          cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool
-      result = true
-
-      # http://stackoverflow.com/a/12277264/492186
-      # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
-      if res:
-        # This is useful for ensuring the reliability of the overlapped struct.
+    let nextTimer = processTimers(p, result)
+    let at = adjustTimeout(p, timeout, nextTimer)
+    var llTimeout =
+      if at == -1: winlean.INFINITE
+      else: at.int32
+
+    var lpNumberOfBytesTransferred: DWORD
+    var lpCompletionKey: ULONG_PTR
+    var customOverlapped: CustomRef
+    let res = getQueuedCompletionStatus(p.ioPort,
+        addr lpNumberOfBytesTransferred, addr lpCompletionKey,
+        cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool
+    result = true
+    # For 'gcDestructors' the destructor of 'customOverlapped' will
+    # be called at the end and we are the only owner here. This means
+    # We do not have to 'GC_unref(customOverlapped)' because the destructor
+    # does that for us.
+
+    # http://stackoverflow.com/a/12277264/492186
+    # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
+    if res:
+      # This is useful for ensuring the reliability of the overlapped struct.
+      assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
+
+      customOverlapped.data.cb(customOverlapped.data.fd,
+          lpNumberOfBytesTransferred, OSErrorCode(-1))
+
+      # If cell.data != nil, then system.protect(rawEnv(cb)) was called,
+      # so we need to dispose our `cb` environment, because it is not needed
+      # anymore.
+      if customOverlapped.data.cell.data != nil:
+        system.dispose(customOverlapped.data.cell)
+
+      when not defined(gcDestructors):
+        GC_unref(customOverlapped)
+    else:
+      let errCode = osLastError()
+      if customOverlapped != nil:
         assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
-
         customOverlapped.data.cb(customOverlapped.data.fd,
-            lpNumberOfBytesTransferred, OSErrorCode(-1))
-
-        # If cell.data != nil, then system.protect(rawEnv(cb)) was called,
-        # so we need to dispose our `cb` environment, because it is not needed
-        # anymore.
+            lpNumberOfBytesTransferred, errCode)
         if customOverlapped.data.cell.data != nil:
           system.dispose(customOverlapped.data.cell)
-
-        GC_unref(customOverlapped)
-      else:
-        let errCode = osLastError()
-        if customOverlapped != nil:
-          assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
-          customOverlapped.data.cb(customOverlapped.data.fd,
-              lpNumberOfBytesTransferred, errCode)
-          if customOverlapped.data.cell.data != nil:
-            system.dispose(customOverlapped.data.cell)
+        when not defined(gcDestructors):
           GC_unref(customOverlapped)
-        else:
-          if errCode.int32 == WAIT_TIMEOUT:
-            # Timed out
-            result = false
-          else: raiseOSError(errCode)
+      else:
+        if errCode.int32 == WAIT_TIMEOUT:
+          # Timed out
+          result = false
+        else: raiseOSError(errCode)
 
     # Timer processing.
-    processTimers(p, result)
+    discard processTimers(p, result)
     # Callback queue processing
     processPendingCallbacks(p, result)
 
@@ -356,14 +462,14 @@ when defined(windows) or defined(nimdoc):
 
   proc initPointer(s: SocketHandle, fun: var pointer, guid: var GUID): bool =
     # Ref: https://github.com/powdahound/twisted/blob/master/twisted/internet/iocpreactor/iocpsupport/winsock_pointers.c
-    var bytesRet: Dword
+    var bytesRet: DWORD
     fun = nil
     result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, addr guid,
-                      sizeof(GUID).Dword, addr fun, sizeof(pointer).Dword,
+                      sizeof(GUID).DWORD, addr fun, sizeof(pointer).DWORD,
                       addr bytesRet, nil, nil) == 0
 
   proc initAll() =
-    let dummySock = newNativeSocket()
+    let dummySock = createNativeSocket()
     if dummySock == INVALID_SOCKET:
       raiseOSError(osLastError())
     var fun: pointer = nil
@@ -378,18 +484,25 @@ when defined(windows) or defined(nimdoc):
     getAcceptExSockAddrs = cast[WSAPROC_GETACCEPTEXSOCKADDRS](fun)
     close(dummySock)
 
+  proc newCustom*(): CustomRef =
+    result = CustomRef() # 0
+    GC_ref(result) # 1  prevent destructor from doing a premature free.
+    # destructor of newCustom's caller --> 0. This means
+    # Windows holds a ref for us with RC == 0 (single owner).
+    # This is passed back to us in the IO completion port.
+
   proc recv*(socket: AsyncFD, size: int,
-             flags = {SocketFlag.SafeDisconn}): Future[string] =
-    ## Reads **up to** ``size`` bytes from ``socket``. Returned future will
+             flags = {SocketFlag.SafeDisconn}): owned(Future[string]) =
+    ## Reads **up to** `size` bytes from `socket`. Returned future will
     ## complete once all the data requested is read, a part of the data has been
     ## read, or the socket has disconnected in which case the future will
-    ## complete with a value of ``""``.
+    ## complete with a value of `""`.
     ##
-    ## **Warning**: The ``Peek`` socket flag is not supported on Windows.
+    ## .. warning:: The `Peek` socket flag is not supported on Windows.
 
 
     # Things to note:
-    #   * When WSARecv completes immediately then ``bytesReceived`` is very
+    #   * When WSARecv completes immediately then `bytesReceived` is very
     #     unreliable.
     #   * Still need to implement message-oriented socket disconnection,
     #     '\0' in the message currently signifies a socket disconnect. Who
@@ -402,12 +515,11 @@ when defined(windows) or defined(nimdoc):
     dataBuf.buf = cast[cstring](alloc0(size))
     dataBuf.len = size.ULONG
 
-    var bytesReceived: Dword
-    var flagsio = flags.toOSFlags().Dword
-    var ol = PCustomOverlapped()
-    GC_ref(ol)
+    var bytesReceived: DWORD
+    var flagsio = flags.toOSFlags().DWORD
+    var ol = newCustom()
     ol.data = CompletionData(fd: socket, cb:
-      proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
+      proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) =
         if not retFuture.finished:
           if errcode == OSErrorCode(-1):
             if bytesCount == 0 and dataBuf.buf[0] == '\0':
@@ -421,7 +533,7 @@ when defined(windows) or defined(nimdoc):
             if flags.isDisconnectionError(errcode):
               retFuture.complete("")
             else:
-              retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+              retFuture.fail(newOSError(errcode))
         if dataBuf.buf != nil:
           dealloc dataBuf.buf
           dataBuf.buf = nil
@@ -439,7 +551,7 @@ when defined(windows) or defined(nimdoc):
         if flags.isDisconnectionError(err):
           retFuture.complete("")
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(err)))
+          retFuture.fail(newOSError(err))
     elif ret == 0:
       # Request completed immediately.
       if bytesReceived != 0:
@@ -453,18 +565,18 @@ when defined(windows) or defined(nimdoc):
     return retFuture
 
   proc recvInto*(socket: AsyncFD, buf: pointer, size: int,
-                 flags = {SocketFlag.SafeDisconn}): Future[int] =
-    ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must
+                 flags = {SocketFlag.SafeDisconn}): owned(Future[int]) =
+    ## Reads **up to** `size` bytes from `socket` into `buf`, which must
     ## at least be of that size. Returned future will complete once all the
     ## data requested is read, a part of the data has been read, or the socket
     ## has disconnected in which case the future will complete with a value of
-    ## ``0``.
+    ## `0`.
     ##
-    ## **Warning**: The ``Peek`` socket flag is not supported on Windows.
+    ## .. warning:: The `Peek` socket flag is not supported on Windows.
 
 
     # Things to note:
-    #   * When WSARecv completes immediately then ``bytesReceived`` is very
+    #   * When WSARecv completes immediately then `bytesReceived` is very
     #     unreliable.
     #   * Still need to implement message-oriented socket disconnection,
     #     '\0' in the message currently signifies a socket disconnect. Who
@@ -479,12 +591,11 @@ when defined(windows) or defined(nimdoc):
     dataBuf.buf = cast[cstring](buf)
     dataBuf.len = size.ULONG
 
-    var bytesReceived: Dword
-    var flagsio = flags.toOSFlags().Dword
-    var ol = PCustomOverlapped()
-    GC_ref(ol)
+    var bytesReceived: DWORD
+    var flagsio = flags.toOSFlags().DWORD
+    var ol = newCustom()
     ol.data = CompletionData(fd: socket, cb:
-      proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
+      proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) =
         if not retFuture.finished:
           if errcode == OSErrorCode(-1):
             retFuture.complete(bytesCount)
@@ -492,7 +603,7 @@ when defined(windows) or defined(nimdoc):
             if flags.isDisconnectionError(errcode):
               retFuture.complete(0)
             else:
-              retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+              retFuture.fail(newOSError(errcode))
         if dataBuf.buf != nil:
           dataBuf.buf = nil
     )
@@ -508,7 +619,7 @@ when defined(windows) or defined(nimdoc):
         if flags.isDisconnectionError(err):
           retFuture.complete(0)
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(err)))
+          retFuture.fail(newOSError(err))
     elif ret == 0:
       # Request completed immediately.
       if bytesReceived != 0:
@@ -520,12 +631,12 @@ when defined(windows) or defined(nimdoc):
     return retFuture
 
   proc send*(socket: AsyncFD, buf: pointer, size: int,
-             flags = {SocketFlag.SafeDisconn}): Future[void] =
-    ## Sends ``size`` bytes from ``buf`` to ``socket``. The returned future
+             flags = {SocketFlag.SafeDisconn}): owned(Future[void]) =
+    ## Sends `size` bytes from `buf` to `socket`. The returned future
     ## will complete once all data has been sent.
     ##
-    ## **WARNING**: Use it with caution. If ``buf`` refers to GC'ed object,
-    ## you must use GC_ref/GC_unref calls to avoid early freeing of the buffer.
+    ## .. warning:: Use it with caution. If `buf` refers to GC'ed object,
+    ##   you must use GC_ref/GC_unref calls to avoid early freeing of the buffer.
     verifyPresence(socket)
     var retFuture = newFuture[void]("send")
 
@@ -533,11 +644,10 @@ when defined(windows) or defined(nimdoc):
     dataBuf.buf = cast[cstring](buf)
     dataBuf.len = size.ULONG
 
-    var bytesReceived, lowFlags: Dword
-    var ol = PCustomOverlapped()
-    GC_ref(ol)
+    var bytesReceived, lowFlags: DWORD
+    var ol = newCustom()
     ol.data = CompletionData(fd: socket, cb:
-      proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
+      proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) =
         if not retFuture.finished:
           if errcode == OSErrorCode(-1):
             retFuture.complete()
@@ -545,7 +655,7 @@ when defined(windows) or defined(nimdoc):
             if flags.isDisconnectionError(errcode):
               retFuture.complete()
             else:
-              retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+              retFuture.fail(newOSError(errcode))
     )
 
     let ret = WSASend(socket.SocketHandle, addr dataBuf, 1, addr bytesReceived,
@@ -557,27 +667,27 @@ when defined(windows) or defined(nimdoc):
         if flags.isDisconnectionError(err):
           retFuture.complete()
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(err)))
+          retFuture.fail(newOSError(err))
     else:
       retFuture.complete()
-      # We don't deallocate ``ol`` here because even though this completed
+      # We don't deallocate `ol` here because even though this completed
       # immediately poll will still be notified about its completion and it will
-      # free ``ol``.
+      # free `ol`.
     return retFuture
 
   proc sendTo*(socket: AsyncFD, data: pointer, size: int, saddr: ptr SockAddr,
-               saddrLen: Socklen,
-               flags = {SocketFlag.SafeDisconn}): Future[void] =
-    ## Sends ``data`` to specified destination ``saddr``, using
-    ## socket ``socket``. The returned future will complete once all data
+               saddrLen: SockLen,
+               flags = {SocketFlag.SafeDisconn}): owned(Future[void]) =
+    ## Sends `data` to specified destination `saddr`, using
+    ## socket `socket`. The returned future will complete once all data
     ## has been sent.
     verifyPresence(socket)
     var retFuture = newFuture[void]("sendTo")
     var dataBuf: TWSABuf
     dataBuf.buf = cast[cstring](data)
     dataBuf.len = size.ULONG
-    var bytesSent = 0.Dword
-    var lowFlags = 0.Dword
+    var bytesSent = 0.DWORD
+    var lowFlags = 0.DWORD
 
     # we will preserve address in our stack
     var staddr: array[128, char] # SOCKADDR_STORAGE size is 128 bytes
@@ -585,15 +695,14 @@ when defined(windows) or defined(nimdoc):
     zeroMem(addr(staddr[0]), 128)
     copyMem(addr(staddr[0]), saddr, saddrLen)
 
-    var ol = PCustomOverlapped()
-    GC_ref(ol)
+    var ol = newCustom()
     ol.data = CompletionData(fd: socket, cb:
-      proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
+      proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) =
         if not retFuture.finished:
           if errcode == OSErrorCode(-1):
             retFuture.complete()
           else:
-            retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+            retFuture.fail(newOSError(errcode))
     )
 
     let ret = WSASendTo(socket.SocketHandle, addr dataBuf, 1, addr bytesSent,
@@ -603,20 +712,20 @@ when defined(windows) or defined(nimdoc):
       let err = osLastError()
       if err.int32 != ERROR_IO_PENDING:
         GC_unref(ol)
-        retFuture.fail(newException(OSError, osErrorMsg(err)))
+        retFuture.fail(newOSError(err))
     else:
       retFuture.complete()
-      # We don't deallocate ``ol`` here because even though this completed
+      # We don't deallocate `ol` here because even though this completed
       # immediately poll will still be notified about its completion and it will
-      # free ``ol``.
+      # free `ol`.
     return retFuture
 
   proc recvFromInto*(socket: AsyncFD, data: pointer, size: int,
                      saddr: ptr SockAddr, saddrLen: ptr SockLen,
-                     flags = {SocketFlag.SafeDisconn}): Future[int] =
-    ## Receives a datagram data from ``socket`` into ``buf``, which must
-    ## be at least of size ``size``, address of datagram's sender will be
-    ## stored into ``saddr`` and ``saddrLen``. Returned future will complete
+                     flags = {SocketFlag.SafeDisconn}): owned(Future[int]) =
+    ## Receives a datagram data from `socket` into `buf`, which must
+    ## be at least of size `size`, address of datagram's sender will be
+    ## stored into `saddr` and `saddrLen`. Returned future will complete
     ## once one datagram has been received, and will return size of packet
     ## received.
     verifyPresence(socket)
@@ -624,13 +733,12 @@ when defined(windows) or defined(nimdoc):
 
     var dataBuf = TWSABuf(buf: cast[cstring](data), len: size.ULONG)
 
-    var bytesReceived = 0.Dword
-    var lowFlags = 0.Dword
+    var bytesReceived = 0.DWORD
+    var lowFlags = 0.DWORD
 
-    var ol = PCustomOverlapped()
-    GC_ref(ol)
+    var ol = newCustom()
     ol.data = CompletionData(fd: socket, cb:
-      proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
+      proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) =
         if not retFuture.finished:
           if errcode == OSErrorCode(-1):
             assert bytesCount <= size
@@ -638,7 +746,7 @@ when defined(windows) or defined(nimdoc):
           else:
             # datagram sockets don't have disconnection,
             # so we can just raise an exception
-            retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+            retFuture.fail(newOSError(errcode))
     )
 
     let res = WSARecvFrom(socket.SocketHandle, addr dataBuf, 1,
@@ -649,7 +757,7 @@ when defined(windows) or defined(nimdoc):
       let err = osLastError()
       if err.int32 != ERROR_IO_PENDING:
         GC_unref(ol)
-        retFuture.fail(newException(OSError, osErrorMsg(err)))
+        retFuture.fail(newOSError(err))
     else:
       # Request completed immediately.
       if bytesReceived != 0:
@@ -660,8 +768,9 @@ when defined(windows) or defined(nimdoc):
           retFuture.complete(bytesReceived)
     return retFuture
 
-  proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}):
-      Future[tuple[address: string, client: AsyncFD]] =
+  proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn},
+                   inheritable = defined(nimInheritHandles)):
+      owned(Future[tuple[address: string, client: AsyncFD]]) {.gcsafe.} =
     ## Accepts a new connection. Returns a future containing the client socket
     ## corresponding to that connection and the remote address of the client.
     ## The future will complete when the connection is successfully accepted.
@@ -669,22 +778,25 @@ when defined(windows) or defined(nimdoc):
     ## The resulting client socket is automatically registered to the
     ## dispatcher.
     ##
-    ## The ``accept`` call may result in an error if the connecting socket
-    ## disconnects during the duration of the ``accept``. If the ``SafeDisconn``
+    ## If `inheritable` is false (the default), the resulting client socket will
+    ## not be inheritable by child processes.
+    ##
+    ## The `accept` call may result in an error if the connecting socket
+    ## disconnects during the duration of the `accept`. If the `SafeDisconn`
     ## flag is specified then this error will not be raised and instead
     ## accept will be called again.
     verifyPresence(socket)
     var retFuture = newFuture[tuple[address: string, client: AsyncFD]]("acceptAddr")
 
-    var clientSock = newNativeSocket()
+    var clientSock = createNativeSocket(inheritable = inheritable)
     if clientSock == osInvalidSocket: raiseOSError(osLastError())
 
     const lpOutputLen = 1024
     var lpOutputBuf = newString(lpOutputLen)
-    var dwBytesReceived: Dword
-    let dwReceiveDataLength = 0.Dword # We don't want any data to be read.
-    let dwLocalAddressLength = Dword(sizeof(Sockaddr_in6) + 16)
-    let dwRemoteAddressLength = Dword(sizeof(Sockaddr_in6) + 16)
+    var dwBytesReceived: DWORD
+    let dwReceiveDataLength = 0.DWORD # We don't want any data to be read.
+    let dwLocalAddressLength = DWORD(sizeof(Sockaddr_in6) + 16)
+    let dwRemoteAddressLength = DWORD(sizeof(Sockaddr_in6) + 16)
 
     template failAccept(errcode) =
       if flags.isDisconnectionError(errcode):
@@ -696,7 +808,7 @@ when defined(windows) or defined(nimdoc):
             else:
               retFuture.complete(newAcceptFut.read)
       else:
-        retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+        retFuture.fail(newOSError(errcode))
 
     template completeAccept() {.dirty.} =
       var listenSock = socket
@@ -705,17 +817,17 @@ when defined(windows) or defined(nimdoc):
           sizeof(listenSock).SockLen)
       if setoptRet != 0:
         let errcode = osLastError()
-        discard clientSock.closeSocket()
+        discard clientSock.closesocket()
         failAccept(errcode)
       else:
         var localSockaddr, remoteSockaddr: ptr SockAddr
         var localLen, remoteLen: int32
-        getAcceptExSockaddrs(addr lpOutputBuf[0], dwReceiveDataLength,
+        getAcceptExSockAddrs(addr lpOutputBuf[0], dwReceiveDataLength,
                              dwLocalAddressLength, dwRemoteAddressLength,
                              addr localSockaddr, addr localLen,
                              addr remoteSockaddr, addr remoteLen)
         try:
-          let address = getAddrString(remoteSockAddr)
+          let address = getAddrString(remoteSockaddr)
           register(clientSock.AsyncFD)
           retFuture.complete((address: address, client: clientSock.AsyncFD))
         except:
@@ -723,10 +835,9 @@ when defined(windows) or defined(nimdoc):
           clientSock.close()
           retFuture.fail(getCurrentException())
 
-    var ol = PCustomOverlapped()
-    GC_ref(ol)
+    var ol = newCustom()
     ol.data = CompletionData(fd: socket, cb:
-      proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
+      proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) {.gcsafe.} =
         if not retFuture.finished:
           if errcode == OSErrorCode(-1):
             completeAccept()
@@ -748,47 +859,48 @@ when defined(windows) or defined(nimdoc):
         GC_unref(ol)
     else:
       completeAccept()
-      # We don't deallocate ``ol`` here because even though this completed
+      # We don't deallocate `ol` here because even though this completed
       # immediately poll will still be notified about its completion and it will
-      # free ``ol``.
+      # free `ol`.
 
     return retFuture
 
+  implementSetInheritable()
+
   proc closeSocket*(socket: AsyncFD) =
     ## Closes a socket and ensures that it is unregistered.
     socket.SocketHandle.close()
     getGlobalDispatcher().handles.excl(socket)
 
   proc unregister*(fd: AsyncFD) =
-    ## Unregisters ``fd``.
+    ## Unregisters `fd`.
     getGlobalDispatcher().handles.excl(fd)
 
   proc contains*(disp: PDispatcher, fd: AsyncFD): bool =
     return fd in disp.handles
 
-  {.push stackTrace:off.}
+  {.push stackTrace: off.}
   proc waitableCallback(param: pointer,
-                        timerOrWaitFired: WINBOOL): void {.stdcall.} =
+                        timerOrWaitFired: WINBOOL) {.stdcall.} =
     var p = cast[PostCallbackDataPtr](param)
-    discard postQueuedCompletionStatus(p.ioPort, timerOrWaitFired.Dword,
+    discard postQueuedCompletionStatus(p.ioPort, timerOrWaitFired.DWORD,
                                        ULONG_PTR(p.handleFd),
                                        cast[pointer](p.ovl))
   {.pop.}
 
-  proc registerWaitableEvent(fd: AsyncFD, cb: Callback; mask: Dword) =
+  proc registerWaitableEvent(fd: AsyncFD, cb: Callback; mask: DWORD) =
     let p = getGlobalDispatcher()
-    var flags = (WT_EXECUTEINWAITTHREAD or WT_EXECUTEONLYONCE).Dword
+    var flags = (WT_EXECUTEINWAITTHREAD or WT_EXECUTEONLYONCE).DWORD
     var hEvent = wsaCreateEvent()
     if hEvent == 0:
       raiseOSError(osLastError())
     var pcd = cast[PostCallbackDataPtr](allocShared0(sizeof(PostCallbackData)))
     pcd.ioPort = p.ioPort
     pcd.handleFd = fd
-    var ol = PCustomOverlapped()
-    GC_ref(ol)
+    var ol = newCustom()
 
     ol.data = CompletionData(fd: fd, cb:
-      proc(fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
+      proc(fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) {.gcsafe.} =
         # we excluding our `fd` because cb(fd) can register own handler
         # for this `fd`
         p.handles.excl(fd)
@@ -861,9 +973,9 @@ when defined(windows) or defined(nimdoc):
 
   proc addRead*(fd: AsyncFD, cb: Callback) =
     ## Start watching the file descriptor for read availability and then call
-    ## the callback ``cb``.
+    ## the callback `cb`.
     ##
-    ## This is not ``pure`` mechanism for Windows Completion Ports (IOCP),
+    ## This is not `pure` mechanism for Windows Completion Ports (IOCP),
     ## so if you can avoid it, please do it. Use `addRead` only if really
     ## need it (main usecase is adaptation of unix-like libraries to be
     ## asynchronous on Windows).
@@ -872,16 +984,16 @@ when defined(windows) or defined(nimdoc):
     ## or asyncdispatch.accept(), because they are using IOCP, please use
     ## nativesockets.recv() and nativesockets.accept() instead.
     ##
-    ## Be sure your callback ``cb`` returns ``true``, if you want to remove
-    ## watch of `read` notifications, and ``false``, if you want to continue
+    ## Be sure your callback `cb` returns `true`, if you want to remove
+    ## watch of `read` notifications, and `false`, if you want to continue
     ## receiving notifications.
     registerWaitableEvent(fd, cb, FD_READ or FD_ACCEPT or FD_OOB or FD_CLOSE)
 
   proc addWrite*(fd: AsyncFD, cb: Callback) =
     ## Start watching the file descriptor for write availability and then call
-    ## the callback ``cb``.
+    ## the callback `cb`.
     ##
-    ## This is not ``pure`` mechanism for Windows Completion Ports (IOCP),
+    ## This is not `pure` mechanism for Windows Completion Ports (IOCP),
     ## so if you can avoid it, please do it. Use `addWrite` only if really
     ## need it (main usecase is adaptation of unix-like libraries to be
     ## asynchronous on Windows).
@@ -890,8 +1002,8 @@ when defined(windows) or defined(nimdoc):
     ## or asyncdispatch.connect(), because they are using IOCP, please use
     ## nativesockets.send() and nativesockets.connect() instead.
     ##
-    ## Be sure your callback ``cb`` returns ``true``, if you want to remove
-    ## watch of `write` notifications, and ``false``, if you want to continue
+    ## Be sure your callback `cb` returns `true`, if you want to remove
+    ## watch of `write` notifications, and `false`, if you want to continue
     ## receiving notifications.
     registerWaitableEvent(fd, cb, FD_WRITE or FD_CONNECT or FD_CLOSE)
 
@@ -900,8 +1012,7 @@ when defined(windows) or defined(nimdoc):
     let handleFD = AsyncFD(hEvent)
     pcd.ioPort = p.ioPort
     pcd.handleFd = handleFD
-    var ol = PCustomOverlapped()
-    GC_ref(ol)
+    var ol = newCustom()
     ol.data.fd = handleFD
     ol.data.cb = handleCallback
     # We need to protect our callback environment value, so GC will not free it
@@ -911,7 +1022,7 @@ when defined(windows) or defined(nimdoc):
     pcd.ovl = ol
     if not registerWaitForSingleObject(addr(pcd.waitFd), hEvent,
                                     cast[WAITORTIMERCALLBACK](waitableCallback),
-                                    cast[pointer](pcd), timeout.Dword, flags):
+                                    cast[pointer](pcd), timeout.DWORD, flags):
       let err = osLastError()
       GC_unref(ol)
       deallocShared(cast[pointer](pcd))
@@ -924,20 +1035,20 @@ when defined(windows) or defined(nimdoc):
     deallocShared(cast[pointer](pcd))
     p.handles.excl(fd)
     if unregisterWait(waitFd) == 0:
-        let err = osLastError()
-        if err.int32 != ERROR_IO_PENDING:
-          discard closeHandle(handle)
-          raiseOSError(err)
+      let err = osLastError()
+      if err.int32 != ERROR_IO_PENDING:
+        discard closeHandle(handle)
+        raiseOSError(err)
     if closeHandle(handle) == 0:
       raiseOSError(osLastError())
 
   proc addTimer*(timeout: int, oneshot: bool, cb: Callback) =
-    ## Registers callback ``cb`` to be called when timer expired.
+    ## Registers callback `cb` to be called when timer expired.
     ##
     ## Parameters:
     ##
-    ## * ``timeout`` - timeout value in milliseconds.
-    ## * ``oneshot``
+    ## * `timeout` - timeout value in milliseconds.
+    ## * `oneshot`
     ##   * `true` - generate only one timeout event
     ##   * `false` - generate timeout events periodically
 
@@ -949,10 +1060,10 @@ when defined(windows) or defined(nimdoc):
       raiseOSError(osLastError())
 
     var pcd = cast[PostCallbackDataPtr](allocShared0(sizeof(PostCallbackData)))
-    var flags = WT_EXECUTEINWAITTHREAD.Dword
+    var flags = WT_EXECUTEINWAITTHREAD.DWORD
     if oneshot: flags = flags or WT_EXECUTEONLYONCE
 
-    proc timercb(fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
+    proc timercb(fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) =
       let res = cb(fd)
       if res or oneshot:
         closeWaitable(hEvent)
@@ -966,28 +1077,29 @@ when defined(windows) or defined(nimdoc):
     registerWaitableHandle(p, hEvent, flags, pcd, timeout, timercb)
 
   proc addProcess*(pid: int, cb: Callback) =
-    ## Registers callback ``cb`` to be called when process with process ID
-    ## ``pid`` exited.
+    ## Registers callback `cb` to be called when process with process ID
+    ## `pid` exited.
+    const NULL = Handle(0)
     let p = getGlobalDispatcher()
     let procFlags = SYNCHRONIZE
-    var hProcess = openProcess(procFlags, 0, pid.Dword)
-    if hProcess == INVALID_HANDLE_VALUE:
+    var hProcess = openProcess(procFlags, 0, pid.DWORD)
+    if hProcess == NULL:
       raiseOSError(osLastError())
 
     var pcd = cast[PostCallbackDataPtr](allocShared0(sizeof(PostCallbackData)))
-    var flags = WT_EXECUTEINWAITTHREAD.Dword
+    var flags = WT_EXECUTEINWAITTHREAD.DWORD or WT_EXECUTEONLYONCE.DWORD
 
-    proc proccb(fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
+    proc proccb(fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) =
       closeWaitable(hProcess)
       discard cb(fd)
 
     registerWaitableHandle(p, hProcess, flags, pcd, INFINITE, proccb)
 
   proc newAsyncEvent*(): AsyncEvent =
-    ## Creates a new thread-safe ``AsyncEvent`` object.
+    ## Creates a new thread-safe `AsyncEvent` object.
     ##
-    ## New ``AsyncEvent`` object is not automatically registered with
-    ## dispatcher like ``AsyncSocket``.
+    ## New `AsyncEvent` object is not automatically registered with
+    ## dispatcher like `AsyncSocket`.
     var sa = SECURITY_ATTRIBUTES(
       nLength: sizeof(SECURITY_ATTRIBUTES).cint,
       bInheritHandle: 1
@@ -999,12 +1111,12 @@ when defined(windows) or defined(nimdoc):
     result.hEvent = event
 
   proc trigger*(ev: AsyncEvent) =
-    ## Set event ``ev`` to signaled state.
+    ## Set event `ev` to signaled state.
     if setEvent(ev.hEvent) == 0:
       raiseOSError(osLastError())
 
   proc unregister*(ev: AsyncEvent) =
-    ## Unregisters event ``ev``.
+    ## Unregisters event `ev`.
     doAssert(ev.hWaiter != 0, "Event is not registered in the queue!")
     let p = getGlobalDispatcher()
     p.handles.excl(AsyncFD(ev.hEvent))
@@ -1015,23 +1127,23 @@ when defined(windows) or defined(nimdoc):
     ev.hWaiter = 0
 
   proc close*(ev: AsyncEvent) =
-    ## Closes event ``ev``.
+    ## Closes event `ev`.
     let res = closeHandle(ev.hEvent)
     deallocShared(cast[pointer](ev))
     if res == 0:
       raiseOSError(osLastError())
 
   proc addEvent*(ev: AsyncEvent, cb: Callback) =
-    ## Registers callback ``cb`` to be called when ``ev`` will be signaled
+    ## Registers callback `cb` to be called when `ev` will be signaled
     doAssert(ev.hWaiter == 0, "Event is already registered in the queue!")
 
     let p = getGlobalDispatcher()
     let hEvent = ev.hEvent
 
     var pcd = cast[PostCallbackDataPtr](allocShared0(sizeof(PostCallbackData)))
-    var flags = WT_EXECUTEINWAITTHREAD.Dword
+    var flags = WT_EXECUTEINWAITTHREAD.DWORD
 
-    proc eventcb(fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
+    proc eventcb(fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) =
       if ev.hWaiter != 0:
         if cb(fd):
           # we need this check to avoid exception, if `unregister(event)` was
@@ -1054,9 +1166,15 @@ when defined(windows) or defined(nimdoc):
 
   initAll()
 else:
-  import selectors
-  from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK,
+  import std/selectors
+  from std/posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK,
                     MSG_NOSIGNAL
+  when declared(posix.accept4):
+    from std/posix import accept4, SOCK_CLOEXEC
+  when defined(genode):
+    import genode/env # get the implicit Genode env
+    import genode/signals
+
   const
     InitCallbackListSize = 4         # initial size of callbacks sequence,
                                      # associated with file/socket descriptor.
@@ -1064,7 +1182,7 @@ else:
                                      # queue.
   type
     AsyncFD* = distinct cint
-    Callback = proc (fd: AsyncFD): bool {.closure,gcsafe.}
+    Callback* = proc (fd: AsyncFD): bool {.closure, gcsafe.}
 
     AsyncData = object
       readList: seq[Callback]
@@ -1074,7 +1192,8 @@ else:
 
     PDispatcher* = ref object of PDispatcherBase
       selector: Selector[AsyncData]
-  {.deprecated: [TAsyncFD: AsyncFD, TCallback: Callback].}
+      when defined(genode):
+        signalHandler: SignalHandler
 
   proc `==`*(x, y: AsyncFD): bool {.borrow.}
   proc `==`*(x, y: AsyncEvent): bool {.borrow.}
@@ -1085,15 +1204,28 @@ else:
       writeList: newSeqOfCap[Callback](InitCallbackListSize)
     )
 
-  proc newDispatcher*(): PDispatcher =
+  proc newDispatcher*(): owned(PDispatcher) =
     new result
     result.selector = newSelector[AsyncData]()
-    result.timers.newHeapQueue()
-    result.callbacks = initDeque[proc ()](InitDelayedCallbackListSize)
+    result.timers.clear()
+    result.callbacks = initDeque[proc () {.closure, gcsafe.}](InitDelayedCallbackListSize)
+    when defined(genode):
+      let entrypoint = ep(cast[GenodeEnv](runtimeEnv))
+      result.signalHandler = newSignalHandler(entrypoint):
+        discard runOnce(0)
+
+  var gDisp{.threadvar.}: owned PDispatcher ## Global dispatcher
 
-  var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
+  when defined(nuttx):
+    import std/exitprocs
 
-  proc setGlobalDispatcher*(disp: PDispatcher) =
+    proc cleanDispatcher() {.noconv.} =
+      gDisp = nil
+
+    proc addFinalyzer() =
+      addExitProc(cleanDispatcher)
+
+  proc setGlobalDispatcher*(disp: owned PDispatcher) =
     if not gDisp.isNil:
       assert gDisp.callbacks.len == 0
     gDisp = disp
@@ -1102,6 +1234,8 @@ else:
   proc getGlobalDispatcher*(): PDispatcher =
     if gDisp.isNil:
       setGlobalDispatcher(newDispatcher())
+      when defined(nuttx):
+        addFinalyzer()
     result = gDisp
 
   proc getIoHandler*(disp: PDispatcher): Selector[AsyncData] =
@@ -1112,18 +1246,13 @@ else:
     var data = newAsyncData()
     p.selector.registerHandle(fd.SocketHandle, {}, data)
 
-  proc closeSocket*(sock: AsyncFD) =
-    let disp = getGlobalDispatcher()
-    disp.selector.unregister(sock.SocketHandle)
-    sock.SocketHandle.close()
-
   proc unregister*(fd: AsyncFD) =
     getGlobalDispatcher().selector.unregister(fd.SocketHandle)
 
   proc unregister*(ev: AsyncEvent) =
     getGlobalDispatcher().selector.unregister(SelectEvent(ev))
-  
-  proc contains*(disp: PDispatcher, fd: AsyncFd): bool =
+
+  proc contains*(disp: PDispatcher, fd: AsyncFD): bool =
     return fd.SocketHandle in disp.selector
 
   proc addRead*(fd: AsyncFD, cb: Callback) =
@@ -1152,7 +1281,15 @@ else:
     let p = getGlobalDispatcher()
     not p.selector.isEmpty() or p.timers.len != 0 or p.callbacks.len != 0
 
-  template processBasicCallbacks(ident, rwlist: untyped) =
+  proc prependSeq(dest: var seq[Callback]; src: sink seq[Callback]) =
+    var old = move dest
+    dest = src
+    for i in 0..high(old):
+      dest.add(move old[i])
+
+  proc processBasicCallbacks(
+    fd: AsyncFD, event: Event
+  ): tuple[readCbListCount, writeCbListCount: int] =
     # Process pending descriptor and AsyncEvent callbacks.
     #
     # Invoke every callback stored in `rwlist`, until one
@@ -1165,119 +1302,157 @@ else:
     # or it can be possible to fall into endless cycle.
     var curList: seq[Callback]
 
-    withData(p.selector, ident, adata) do:
-      shallowCopy(curList, adata.rwlist)
-      adata.rwlist = newSeqOfCap[Callback](InitCallbackListSize)
+    let selector = getGlobalDispatcher().selector
+    withData(selector, fd.int, fdData):
+      case event
+      of Event.Read:
+        #shallowCopy(curList, fdData.readList)
+        curList = move fdData.readList
+        fdData.readList = newSeqOfCap[Callback](InitCallbackListSize)
+      of Event.Write:
+        #shallowCopy(curList, fdData.writeList)
+        curList = move fdData.writeList
+        fdData.writeList = newSeqOfCap[Callback](InitCallbackListSize)
+      else:
+        assert false, "Cannot process callbacks for " & $event
 
     let newLength = max(len(curList), InitCallbackListSize)
     var newList = newSeqOfCap[Callback](newLength)
 
+    var eventsExtinguished = false
     for cb in curList:
-      if len(newList) > 0:
-        # A callback has already returned with EAGAIN, don't call any others
-        # until next `poll`.
+      if eventsExtinguished:
         newList.add(cb)
+      elif not cb(fd):
+        # Callback wants to be called again.
+        newList.add(cb)
+        # This callback has returned with EAGAIN, so we don't need to
+        # call any other callbacks as they are all waiting for the same event
+        # on the same fd.
+        # We do need to ensure they are called again though.
+        eventsExtinguished = true
+
+    withData(selector, fd.int, fdData) do:
+      # Descriptor is still present in the queue.
+      case event
+      of Event.Read: prependSeq(fdData.readList, newList)
+      of Event.Write: prependSeq(fdData.writeList, newList)
       else:
-        if not cb(fd.AsyncFD):
-          # Callback wants to be called again.
-          newList.add(cb)
+        assert false, "Cannot process callbacks for " & $event
 
-    withData(p.selector, ident, adata) do:
-      # descriptor still present in queue.
-      adata.rwlist = newList & adata.rwlist
-      rLength = len(adata.readList)
-      wLength = len(adata.writeList)
+      result.readCbListCount = len(fdData.readList)
+      result.writeCbListCount = len(fdData.writeList)
     do:
-      # descriptor was unregistered in callback via `unregister()`.
-      rLength = -1
-      wLength = -1
+      # Descriptor was unregistered in callback via `unregister()`.
+      result.readCbListCount = -1
+      result.writeCbListCount = -1
 
-  template processCustomCallbacks(ident: untyped) =
+  proc processCustomCallbacks(p: PDispatcher; fd: AsyncFD) =
     # Process pending custom event callbacks. Custom events are
     # {Event.Timer, Event.Signal, Event.Process, Event.Vnode}.
     # There can be only one callback registered with one descriptor,
     # so there is no need to iterate over list.
     var curList: seq[Callback]
 
-    withData(p.selector, ident, adata) do:
-      shallowCopy(curList, adata.readList)
+    withData(p.selector, fd.int, adata) do:
+      curList = move adata.readList
       adata.readList = newSeqOfCap[Callback](InitCallbackListSize)
 
     let newLength = len(curList)
     var newList = newSeqOfCap[Callback](newLength)
 
     var cb = curList[0]
-    if not cb(fd.AsyncFD):
+    if not cb(fd):
       newList.add(cb)
 
-    withData(p.selector, ident, adata) do:
+    withData(p.selector, fd.int, adata) do:
       # descriptor still present in queue.
       adata.readList = newList & adata.readList
       if len(adata.readList) == 0:
         # if no callbacks registered with descriptor, unregister it.
-        p.selector.unregister(fd)
+        p.selector.unregister(fd.int)
     do:
       # descriptor was unregistered in callback via `unregister()`.
       discard
 
-  proc runOnce(timeout = 500): bool =
-    let p = getGlobalDispatcher()
-    when ioselSupportedPlatform:
-      let customSet = {Event.Timer, Event.Signal, Event.Process,
-                       Event.Vnode}
+  implementSetInheritable()
 
+  proc closeSocket*(sock: AsyncFD) =
+    let selector = getGlobalDispatcher().selector
+    if sock.SocketHandle notin selector:
+      raise newException(ValueError, "File descriptor not registered.")
+
+    let data = selector.getData(sock.SocketHandle)
+    sock.unregister()
+    sock.SocketHandle.close()
+    # We need to unblock the read and write callbacks which could still be
+    # waiting for the socket to become readable and/or writeable.
+    for cb in data.readList & data.writeList:
+      if not cb(sock):
+        raise newException(
+          ValueError, "Expecting async operations to stop when fd has closed."
+        )
+
+  proc runOnce(timeout: int): bool =
+    let p = getGlobalDispatcher()
     if p.selector.isEmpty() and p.timers.len == 0 and p.callbacks.len == 0:
+      when defined(genode):
+        if timeout == 0: return
       raise newException(ValueError,
         "No handles or timers registered in dispatcher.")
 
     result = false
-    if not p.selector.isEmpty():
-      var keys: array[64, ReadyKey]
-      var count = p.selector.selectInto(p.adjustedTimeout(timeout), keys)
-      for i in 0..<count:
-        var custom = false
-        let fd = keys[i].fd
-        let events = keys[i].events
-        var rLength = 0 # len(data.readList) after callback
-        var wLength = 0 # len(data.writeList) after callback
-
-        if Event.Read in events or events == {Event.Error}:
-          processBasicCallbacks(fd, readList)
+    var keys: array[64, ReadyKey]
+    let nextTimer = processTimers(p, result)
+    var count =
+      p.selector.selectInto(adjustTimeout(p, timeout, nextTimer), keys)
+    for i in 0..<count:
+      let fd = keys[i].fd.AsyncFD
+      let events = keys[i].events
+      var (readCbListCount, writeCbListCount) = (0, 0)
+
+      if Event.Read in events or events == {Event.Error}:
+        (readCbListCount, writeCbListCount) =
+          processBasicCallbacks(fd, Event.Read)
+        result = true
+
+      if Event.Write in events or events == {Event.Error}:
+        (readCbListCount, writeCbListCount) =
+          processBasicCallbacks(fd, Event.Write)
+        result = true
+
+      var isCustomEvent = false
+      if Event.User in events:
+        (readCbListCount, writeCbListCount) =
+          processBasicCallbacks(fd, Event.Read)
+        isCustomEvent = true
+        if readCbListCount == 0:
+          p.selector.unregister(fd.int)
+        result = true
+
+      when ioselSupportedPlatform:
+        const customSet = {Event.Timer, Event.Signal, Event.Process,
+                           Event.Vnode}
+        if (customSet * events) != {}:
+          isCustomEvent = true
+          processCustomCallbacks(p, fd)
           result = true
 
-        if Event.Write in events or events == {Event.Error}:
-          processBasicCallbacks(fd, writeList)
-          result = true
-
-        if Event.User in events:
-          processBasicCallbacks(fd, readList)
-          custom = true
-          if rLength == 0:
-            p.selector.unregister(fd)
-          result = true
-
-        when ioselSupportedPlatform:
-          if (customSet * events) != {}:
-            custom = true
-            processCustomCallbacks(fd)
-            result = true
-
-        # because state `data` can be modified in callback we need to update
-        # descriptor events with currently registered callbacks.
-        if not custom:
-          var newEvents: set[Event] = {}
-          if rLength != -1 and wLength != -1:
-            if rLength > 0: incl(newEvents, Event.Read)
-            if wLength > 0: incl(newEvents, Event.Write)
-            p.selector.updateHandle(SocketHandle(fd), newEvents)
+      # because state `data` can be modified in callback we need to update
+      # descriptor events with currently registered callbacks.
+      if not isCustomEvent and (readCbListCount != -1 and writeCbListCount != -1):
+        var newEvents: set[Event] = {}
+        if readCbListCount > 0: incl(newEvents, Event.Read)
+        if writeCbListCount > 0: incl(newEvents, Event.Write)
+        p.selector.updateHandle(SocketHandle(fd), newEvents)
 
     # Timer processing.
-    processTimers(p, result)
+    discard processTimers(p, result)
     # Callback queue processing
     processPendingCallbacks(p, result)
 
   proc recv*(socket: AsyncFD, size: int,
-             flags = {SocketFlag.SafeDisconn}): Future[string] =
+             flags = {SocketFlag.SafeDisconn}): owned(Future[string]) =
     var retFuture = newFuture[string]("recv")
 
     var readBuffer = newString(size)
@@ -1288,11 +1463,12 @@ else:
                      flags.toOSFlags())
       if res < 0:
         let lastError = osLastError()
-        if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
+        if lastError.int32 != EINTR and lastError.int32 != EWOULDBLOCK and
+           lastError.int32 != EAGAIN:
           if flags.isDisconnectionError(lastError):
             retFuture.complete("")
           else:
-            retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+            retFuture.fail(newOSError(lastError))
         else:
           result = false # We still want this callback to be called.
       elif res == 0:
@@ -1307,7 +1483,7 @@ else:
     return retFuture
 
   proc recvInto*(socket: AsyncFD, buf: pointer, size: int,
-                 flags = {SocketFlag.SafeDisconn}): Future[int] =
+                 flags = {SocketFlag.SafeDisconn}): owned(Future[int]) =
     var retFuture = newFuture[int]("recvInto")
 
     proc cb(sock: AsyncFD): bool =
@@ -1316,11 +1492,12 @@ else:
                      flags.toOSFlags())
       if res < 0:
         let lastError = osLastError()
-        if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
+        if lastError.int32 != EINTR and lastError.int32 != EWOULDBLOCK and
+           lastError.int32 != EAGAIN:
           if flags.isDisconnectionError(lastError):
             retFuture.complete(0)
           else:
-            retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+            retFuture.fail(newOSError(lastError))
         else:
           result = false # We still want this callback to be called.
       else:
@@ -1331,7 +1508,7 @@ else:
     return retFuture
 
   proc send*(socket: AsyncFD, buf: pointer, size: int,
-             flags = {SocketFlag.SafeDisconn}): Future[void] =
+             flags = {SocketFlag.SafeDisconn}): owned(Future[void]) =
     var retFuture = newFuture[void]("send")
 
     var written = 0
@@ -1344,11 +1521,13 @@ else:
                      MSG_NOSIGNAL)
       if res < 0:
         let lastError = osLastError()
-        if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
+        if lastError.int32 != EINTR and
+           lastError.int32 != EWOULDBLOCK and
+           lastError.int32 != EAGAIN:
           if flags.isDisconnectionError(lastError):
             retFuture.complete()
           else:
-            retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+            retFuture.fail(newOSError(lastError))
         else:
           result = false # We still want this callback to be called.
       else:
@@ -1364,9 +1543,9 @@ else:
 
   proc sendTo*(socket: AsyncFD, data: pointer, size: int, saddr: ptr SockAddr,
                saddrLen: SockLen,
-               flags = {SocketFlag.SafeDisconn}): Future[void] =
-    ## Sends ``data`` of size ``size`` in bytes to specified destination
-    ## (``saddr`` of size ``saddrLen`` in bytes, using socket ``socket``.
+               flags = {SocketFlag.SafeDisconn}): owned(Future[void]) =
+    ## Sends `data` of size `size` in bytes to specified destination
+    ## (`saddr` of size `saddrLen` in bytes, using socket `socket`.
     ## The returned future will complete once all data has been sent.
     var retFuture = newFuture[void]("sendTo")
 
@@ -1382,8 +1561,9 @@ else:
                        cast[ptr SockAddr](addr(staddr[0])), stalen)
       if res < 0:
         let lastError = osLastError()
-        if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
-          retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+        if lastError.int32 != EINTR and lastError.int32 != EWOULDBLOCK and
+           lastError.int32 != EAGAIN:
+          retFuture.fail(newOSError(lastError))
         else:
           result = false # We still want this callback to be called.
       else:
@@ -1394,10 +1574,10 @@ else:
 
   proc recvFromInto*(socket: AsyncFD, data: pointer, size: int,
                      saddr: ptr SockAddr, saddrLen: ptr SockLen,
-                     flags = {SocketFlag.SafeDisconn}): Future[int] =
-    ## Receives a datagram data from ``socket`` into ``data``, which must
-    ## be at least of size ``size`` in bytes, address of datagram's sender
-    ## will be stored into ``saddr`` and ``saddrLen``. Returned future will
+                     flags = {SocketFlag.SafeDisconn}): owned(Future[int]) =
+    ## Receives a datagram data from `socket` into `data`, which must
+    ## be at least of size `size` in bytes, address of datagram's sender
+    ## will be stored into `saddr` and `saddrLen`. Returned future will
     ## complete once one datagram has been received, and will return size
     ## of packet received.
     var retFuture = newFuture[int]("recvFromInto")
@@ -1407,8 +1587,9 @@ else:
                          saddr, saddrLen)
       if res < 0:
         let lastError = osLastError()
-        if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
-          retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+        if lastError.int32 != EINTR and lastError.int32 != EWOULDBLOCK and
+           lastError.int32 != EAGAIN:
+          retFuture.fail(newOSError(lastError))
         else:
           result = false
       else:
@@ -1416,26 +1597,40 @@ else:
     addRead(socket, cb)
     return retFuture
 
-  proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}):
-      Future[tuple[address: string, client: AsyncFD]] =
+  proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn},
+                   inheritable = defined(nimInheritHandles)):
+      owned(Future[tuple[address: string, client: AsyncFD]]) =
     var retFuture = newFuture[tuple[address: string,
         client: AsyncFD]]("acceptAddr")
-    proc cb(sock: AsyncFD): bool =
+    proc cb(sock: AsyncFD): bool {.gcsafe.} =
       result = true
       var sockAddress: Sockaddr_storage
-      var addrLen = sizeof(sockAddress).Socklen
-      var client = accept(sock.SocketHandle,
-                          cast[ptr SockAddr](addr(sockAddress)), addr(addrLen))
+      var addrLen = sizeof(sockAddress).SockLen
+      var client =
+        when declared(accept4):
+          accept4(sock.SocketHandle, cast[ptr SockAddr](addr(sockAddress)),
+                  addr(addrLen), if inheritable: 0 else: SOCK_CLOEXEC)
+        else:
+          accept(sock.SocketHandle, cast[ptr SockAddr](addr(sockAddress)),
+                 addr(addrLen))
+      when declared(setInheritable) and not declared(accept4):
+        if client != osInvalidSocket and not setInheritable(client, inheritable):
+          # Set failure first because close() itself can fail,
+          # altering osLastError().
+          retFuture.fail(newOSError(osLastError()))
+          close client
+          return false
+
       if client == osInvalidSocket:
         let lastError = osLastError()
-        assert lastError.int32 notin {EWOULDBLOCK, EAGAIN}
+        assert lastError.int32 != EWOULDBLOCK and lastError.int32 != EAGAIN
         if lastError.int32 == EINTR:
           return false
         else:
           if flags.isDisconnectionError(lastError):
             return false
           else:
-            retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+            retFuture.fail(newOSError(lastError))
       else:
         try:
           let address = getAddrString(cast[ptr SockAddr](addr sockAddress))
@@ -1452,45 +1647,45 @@ else:
 
     proc addTimer*(timeout: int, oneshot: bool, cb: Callback) =
       ## Start watching for timeout expiration, and then call the
-      ## callback ``cb``.
-      ## ``timeout`` - time in milliseconds,
-      ## ``oneshot`` - if ``true`` only one event will be dispatched,
-      ## if ``false`` continuous events every ``timeout`` milliseconds.
+      ## callback `cb`.
+      ## `timeout` - time in milliseconds,
+      ## `oneshot` - if `true` only one event will be dispatched,
+      ## if `false` continuous events every `timeout` milliseconds.
       let p = getGlobalDispatcher()
       var data = newAsyncData()
       data.readList.add(cb)
       p.selector.registerTimer(timeout, oneshot, data)
 
     proc addSignal*(signal: int, cb: Callback) =
-      ## Start watching signal ``signal``, and when signal appears, call the
-      ## callback ``cb``.
+      ## Start watching signal `signal`, and when signal appears, call the
+      ## callback `cb`.
       let p = getGlobalDispatcher()
       var data = newAsyncData()
       data.readList.add(cb)
       p.selector.registerSignal(signal, data)
 
     proc addProcess*(pid: int, cb: Callback) =
-      ## Start watching for process exit with pid ``pid``, and then call
-      ## the callback ``cb``.
+      ## Start watching for process exit with pid `pid`, and then call
+      ## the callback `cb`.
       let p = getGlobalDispatcher()
       var data = newAsyncData()
       data.readList.add(cb)
       p.selector.registerProcess(pid, data)
 
   proc newAsyncEvent*(): AsyncEvent =
-    ## Creates new ``AsyncEvent``.
+    ## Creates new `AsyncEvent`.
     result = AsyncEvent(newSelectEvent())
 
   proc trigger*(ev: AsyncEvent) =
-    ## Sets new ``AsyncEvent`` to signaled state.
+    ## Sets new `AsyncEvent` to signaled state.
     trigger(SelectEvent(ev))
 
   proc close*(ev: AsyncEvent) =
-    ## Closes ``AsyncEvent``
+    ## Closes `AsyncEvent`
     close(SelectEvent(ev))
 
   proc addEvent*(ev: AsyncEvent, cb: Callback) =
-    ## Start watching for event ``ev``, and call callback ``cb``, when
+    ## Start watching for event `ev`, and call callback `cb`, when
     ## ev will be set to signaled state.
     let p = getGlobalDispatcher()
     var data = newAsyncData()
@@ -1498,54 +1693,275 @@ else:
     p.selector.registerEvent(SelectEvent(ev), data)
 
 proc drain*(timeout = 500) =
-  ## Waits for completion events and processes them. Raises ``ValueError``
-  ## if there are no pending operations. In contrast to ``poll`` this
-  ## processes as many events as are available.
-  if runOnce(timeout):
-    while hasPendingOperations() and runOnce(0): discard
+  ## Waits for completion of **all** events and processes them. Raises `ValueError`
+  ## if there are no pending operations. In contrast to `poll` this
+  ## processes as many events as are available until the timeout has elapsed.
+  var curTimeout = timeout
+  let start = now()
+  while hasPendingOperations():
+    discard runOnce(curTimeout)
+    curTimeout -= (now() - start).inMilliseconds.int
+    if curTimeout < 0:
+      break
 
 proc poll*(timeout = 500) =
-  ## Waits for completion events and processes them. Raises ``ValueError``
+  ## Waits for completion events and processes them. Raises `ValueError`
   ## if there are no pending operations. This runs the underlying OS
   ## `epoll`:idx: or `kqueue`:idx: primitive only once.
   discard runOnce(timeout)
 
-# Common procedures between current and upcoming asyncdispatch
-include includes.asynccommon
+template createAsyncNativeSocketImpl(domain, sockType, protocol: untyped,
+                                     inheritable = defined(nimInheritHandles)) =
+  let handle = createNativeSocket(domain, sockType, protocol, inheritable)
+  if handle == osInvalidSocket:
+    return osInvalidSocket.AsyncFD
+  handle.setBlocking(false)
+  when defined(macosx) and not defined(nimdoc):
+    handle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
+  result = handle.AsyncFD
+  register(result)
+
+proc createAsyncNativeSocket*(domain: cint, sockType: cint,
+                              protocol: cint,
+                              inheritable = defined(nimInheritHandles)): AsyncFD =
+  createAsyncNativeSocketImpl(domain, sockType, protocol, inheritable)
+
+proc createAsyncNativeSocket*(domain: Domain = Domain.AF_INET,
+                              sockType: SockType = SOCK_STREAM,
+                              protocol: Protocol = IPPROTO_TCP,
+                              inheritable = defined(nimInheritHandles)): AsyncFD =
+  createAsyncNativeSocketImpl(domain, sockType, protocol, inheritable)
 
-proc sleepAsync*(ms: int | float): Future[void] =
+when defined(windows) or defined(nimdoc):
+  proc bindToDomain(handle: SocketHandle, domain: Domain) =
+    # Extracted into a separate proc, because connect() on Windows requires
+    # the socket to be initially bound.
+    template doBind(saddr) =
+      if bindAddr(handle, cast[ptr SockAddr](addr(saddr)),
+                  sizeof(saddr).SockLen) < 0'i32:
+        raiseOSError(osLastError())
+
+    if domain == Domain.AF_INET6:
+      var saddr: Sockaddr_in6
+      saddr.sin6_family = uint16(toInt(domain))
+      doBind(saddr)
+    else:
+      var saddr: Sockaddr_in
+      saddr.sin_family = uint16(toInt(domain))
+      doBind(saddr)
+
+  proc doConnect(socket: AsyncFD, addrInfo: ptr AddrInfo): owned(Future[void]) =
+    let retFuture = newFuture[void]("doConnect")
+    result = retFuture
+
+    var ol = newCustom()
+    ol.data = CompletionData(fd: socket, cb:
+      proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) =
+        if not retFuture.finished:
+          if errcode == OSErrorCode(-1):
+            const SO_UPDATE_CONNECT_CONTEXT = 0x7010
+            socket.SocketHandle.setSockOptInt(SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, 1) # 15022
+            retFuture.complete()
+          else:
+            retFuture.fail(newOSError(errcode))
+    )
+
+    let ret = connectEx(socket.SocketHandle, addrInfo.ai_addr,
+                        cint(addrInfo.ai_addrlen), nil, 0, nil,
+                        cast[POVERLAPPED](ol))
+    if ret:
+      # Request to connect completed immediately.
+      retFuture.complete()
+      # We don't deallocate `ol` here because even though this completed
+      # immediately poll will still be notified about its completion and it
+      # will free `ol`.
+    else:
+      let lastError = osLastError()
+      if lastError.int32 != ERROR_IO_PENDING:
+        # With ERROR_IO_PENDING `ol` will be deallocated in `poll`,
+        # and the future will be completed/failed there, too.
+        GC_unref(ol)
+        retFuture.fail(newOSError(lastError))
+else:
+  proc doConnect(socket: AsyncFD, addrInfo: ptr AddrInfo): owned(Future[void]) =
+    let retFuture = newFuture[void]("doConnect")
+    result = retFuture
+
+    proc cb(fd: AsyncFD): bool =
+      let ret = SocketHandle(fd).getSockOptInt(
+        cint(SOL_SOCKET), cint(SO_ERROR))
+      if ret == 0:
+        # We have connected.
+        retFuture.complete()
+        return true
+      elif ret == EINTR:
+        # interrupted, keep waiting
+        return false
+      else:
+        retFuture.fail(newOSError(OSErrorCode(ret)))
+        return true
+
+    let ret = connect(socket.SocketHandle,
+                      addrInfo.ai_addr,
+                      addrInfo.ai_addrlen.SockLen)
+    if ret == 0:
+      # Request to connect completed immediately.
+      retFuture.complete()
+    else:
+      let lastError = osLastError()
+      if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
+        addWrite(socket, cb)
+      else:
+        retFuture.fail(newOSError(lastError))
+
+template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped,
+                           protocol: Protocol = IPPROTO_RAW) =
+  ## Iterates through the AddrInfo linked list asynchronously
+  ## until the connection can be established.
+  const shouldCreateFd = not declared(fd)
+
+  when shouldCreateFd:
+    let sockType = protocol.toSockType()
+
+    var fdPerDomain: array[low(Domain).ord..high(Domain).ord, AsyncFD]
+    for i in low(fdPerDomain)..high(fdPerDomain):
+      fdPerDomain[i] = osInvalidSocket.AsyncFD
+    template closeUnusedFds(domainToKeep = -1) {.dirty.} =
+      for i, fd in fdPerDomain:
+        if fd != osInvalidSocket.AsyncFD and i != domainToKeep:
+          fd.closeSocket()
+
+  var lastException: ref Exception
+  var curAddrInfo = addrInfo
+  var domain: Domain
+  when shouldCreateFd:
+    var curFd: AsyncFD
+  else:
+    var curFd = fd
+  proc tryNextAddrInfo(fut: Future[void]) {.gcsafe.} =
+    if fut == nil or fut.failed:
+      if fut != nil:
+        lastException = fut.readError()
+
+      while curAddrInfo != nil:
+        let domainOpt = curAddrInfo.ai_family.toKnownDomain()
+        if domainOpt.isSome:
+          domain = domainOpt.unsafeGet()
+          break
+        curAddrInfo = curAddrInfo.ai_next
+
+      if curAddrInfo == nil:
+        freeAddrInfo(addrInfo)
+        when shouldCreateFd:
+          closeUnusedFds()
+        if lastException != nil:
+          retFuture.fail(lastException)
+        else:
+          retFuture.fail(newException(
+            IOError, "Couldn't resolve address: " & address))
+        return
+
+      when shouldCreateFd:
+        curFd = fdPerDomain[ord(domain)]
+        if curFd == osInvalidSocket.AsyncFD:
+          try:
+            curFd = createAsyncNativeSocket(domain, sockType, protocol)
+          except:
+            freeAddrInfo(addrInfo)
+            closeUnusedFds()
+            raise getCurrentException()
+          when defined(windows):
+            curFd.SocketHandle.bindToDomain(domain)
+          fdPerDomain[ord(domain)] = curFd
+
+      doConnect(curFd, curAddrInfo).callback = tryNextAddrInfo
+      curAddrInfo = curAddrInfo.ai_next
+    else:
+      freeAddrInfo(addrInfo)
+      when shouldCreateFd:
+        closeUnusedFds(ord(domain))
+        retFuture.complete(curFd)
+      else:
+        retFuture.complete()
+
+  tryNextAddrInfo(nil)
+
+proc dial*(address: string, port: Port,
+           protocol: Protocol = IPPROTO_TCP): owned(Future[AsyncFD]) =
+  ## Establishes connection to the specified `address`:`port` pair via the
+  ## specified protocol. The procedure iterates through possible
+  ## resolutions of the `address` until it succeeds, meaning that it
+  ## seamlessly works with both IPv4 and IPv6.
+  ## Returns the async file descriptor, registered in the dispatcher of
+  ## the current thread, ready to send or receive data.
+  let retFuture = newFuture[AsyncFD]("dial")
+  result = retFuture
+  let sockType = protocol.toSockType()
+
+  let aiList = getAddrInfo(address, port, Domain.AF_UNSPEC, sockType, protocol)
+  asyncAddrInfoLoop(aiList, noFD, protocol)
+
+proc connect*(socket: AsyncFD, address: string, port: Port,
+              domain = Domain.AF_INET): owned(Future[void]) =
+  let retFuture = newFuture[void]("connect")
+  result = retFuture
+
+  when defined(windows):
+    verifyPresence(socket)
+  else:
+    assert getSockDomain(socket.SocketHandle) == domain
+
+  let aiList = getAddrInfo(address, port, domain)
+  when defined(windows):
+    socket.SocketHandle.bindToDomain(domain)
+  asyncAddrInfoLoop(aiList, socket)
+
+proc sleepAsync*(ms: int | float): owned(Future[void]) =
   ## Suspends the execution of the current async procedure for the next
-  ## ``ms`` milliseconds.
+  ## `ms` milliseconds.
   var retFuture = newFuture[void]("sleepAsync")
   let p = getGlobalDispatcher()
-  p.timers.push((epochTime() + (ms / 1000), retFuture))
+  when ms is int:
+    p.timers.push((getMonoTime() + initDuration(milliseconds = ms), retFuture))
+  elif ms is float:
+    let ns = (ms * 1_000_000).int64
+    p.timers.push((getMonoTime() + initDuration(nanoseconds = ns), retFuture))
   return retFuture
 
-proc withTimeout*[T](fut: Future[T], timeout: int): Future[bool] =
-  ## Returns a future which will complete once ``fut`` completes or after
-  ## ``timeout`` milliseconds has elapsed.
+proc withTimeout*[T](fut: Future[T], timeout: int): owned(Future[bool]) =
+  ## Returns a future which will complete once `fut` completes or after
+  ## `timeout` milliseconds has elapsed.
   ##
-  ## If ``fut`` completes first the returned future will hold true,
-  ## otherwise, if ``timeout`` milliseconds has elapsed first, the returned
+  ## If `fut` completes first the returned future will hold true,
+  ## otherwise, if `timeout` milliseconds has elapsed first, the returned
   ## future will hold false.
 
   var retFuture = newFuture[bool]("asyncdispatch.`withTimeout`")
   var timeoutFuture = sleepAsync(timeout)
   fut.callback =
     proc () =
-      if not retFuture.finished: retFuture.complete(true)
+      if not retFuture.finished:
+        if fut.failed:
+          retFuture.fail(fut.error)
+        else:
+          retFuture.complete(true)
   timeoutFuture.callback =
     proc () =
       if not retFuture.finished: retFuture.complete(false)
   return retFuture
 
 proc accept*(socket: AsyncFD,
-    flags = {SocketFlag.SafeDisconn}): Future[AsyncFD] =
+             flags = {SocketFlag.SafeDisconn},
+             inheritable = defined(nimInheritHandles)): owned(Future[AsyncFD]) =
   ## Accepts a new connection. Returns a future containing the client socket
   ## corresponding to that connection.
+  ##
+  ## If `inheritable` is false (the default), the resulting client socket
+  ## will not be inheritable by child processes.
+  ##
   ## The future will complete when the connection is successfully accepted.
   var retFut = newFuture[AsyncFD]("accept")
-  var fut = acceptAddr(socket, flags)
+  var fut = acceptAddr(socket, flags, inheritable)
   fut.callback =
     proc (future: Future[tuple[address: string, client: AsyncFD]]) =
       assert future.finished
@@ -1555,31 +1971,33 @@ proc accept*(socket: AsyncFD,
         retFut.complete(future.read.client)
   return retFut
 
+proc keepAlive(x: string) =
+  discard "mark 'x' as escaping so that it is put into a closure for us to keep the data alive"
+
 proc send*(socket: AsyncFD, data: string,
-           flags = {SocketFlag.SafeDisconn}): Future[void] =
-  ## Sends ``data`` to ``socket``. The returned future will complete once all
+           flags = {SocketFlag.SafeDisconn}): owned(Future[void]) =
+  ## Sends `data` to `socket`. The returned future will complete once all
   ## data has been sent.
   var retFuture = newFuture[void]("send")
-
-  var copiedData = data
-  GC_ref(copiedData) # we need to protect data until send operation is completed
-                     # or failed.
-
-  let sendFut = socket.send(addr copiedData[0], data.len, flags)
-  sendFut.callback =
-    proc () =
-      GC_unref(copiedData)
-      if sendFut.failed:
-        retFuture.fail(sendFut.error)
-      else:
-        retFuture.complete()
+  if data.len > 0:
+    let sendFut = socket.send(unsafeAddr data[0], data.len, flags)
+    sendFut.callback =
+      proc () =
+        keepAlive(data)
+        if sendFut.failed:
+          retFuture.fail(sendFut.error)
+        else:
+          retFuture.complete()
+  else:
+    retFuture.complete()
 
   return retFuture
 
 # -- Await Macro
-include asyncmacro
+import std/asyncmacro
+export asyncmacro
 
-proc readAll*(future: FutureStream[string]): Future[string] {.async.} =
+proc readAll*(future: FutureStream[string]): owned(Future[string]) {.async.} =
   ## Returns a future that will complete when all the string data from the
   ## specified future stream is retrieved.
   result = ""
@@ -1590,50 +2008,7 @@ proc readAll*(future: FutureStream[string]): Future[string] {.async.} =
     else:
       break
 
-proc recvLine*(socket: AsyncFD): Future[string] {.async, deprecated.} =
-  ## Reads a line of data from ``socket``. Returned future will complete once
-  ## a full line is read or an error occurs.
-  ##
-  ## If a full line is read ``\r\L`` is not
-  ## added to ``line``, however if solely ``\r\L`` is read then ``line``
-  ## will be set to it.
-  ##
-  ## If the socket is disconnected, ``line`` will be set to ``""``.
-  ##
-  ## If the socket is disconnected in the middle of a line (before ``\r\L``
-  ## is read) then line will be set to ``""``.
-  ## The partial line **will be lost**.
-  ##
-  ## **Warning**: This assumes that lines are delimited by ``\r\L``.
-  ##
-  ## **Note**: This procedure is mostly used for testing. You likely want to
-  ## use ``asyncnet.recvLine`` instead.
-  ##
-  ## **Deprecated since version 0.15.0**: Use ``asyncnet.recvLine()`` instead.
-
-  template addNLIfEmpty(): typed =
-    if result.len == 0:
-      result.add("\c\L")
-
-  result = ""
-  var c = ""
-  while true:
-    c = await recv(socket, 1)
-    if c.len == 0:
-      return ""
-    if c == "\r":
-      c = await recv(socket, 1)
-      assert c == "\l"
-      addNLIfEmpty()
-      return
-    elif c == "\L":
-      addNLIfEmpty()
-      return
-    add(result, c)
-
-proc callSoon(cbproc: proc ()) =
-  ## Schedule `cbproc` to be called as soon as possible.
-  ## The callback is called when control returns to the event loop.
+proc callSoon(cbproc: proc () {.gcsafe.}) =
   getGlobalDispatcher().callbacks.addLast(cbproc)
 
 proc runForever*() =
@@ -1648,8 +2023,43 @@ proc waitFor*[T](fut: Future[T]): T =
 
   fut.read
 
-proc setEvent*(ev: AsyncEvent) {.deprecated.} =
-  ## Set event ``ev`` to signaled state.
-  ##
-  ## **Deprecated since v0.18.0:** Use ``trigger`` instead.
-  ev.trigger()
\ No newline at end of file
+proc activeDescriptors*(): int {.inline.} =
+  ## Returns the current number of active file descriptors for the current
+  ## event loop. This is a cheap operation that does not involve a system call.
+  when defined(windows):
+    result = getGlobalDispatcher().handles.len
+  elif not defined(nimdoc):
+    result = getGlobalDispatcher().selector.count
+
+when defined(posix):
+  import std/posix
+
+when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or
+       defined(solaris) or defined(zephyr) or defined(freertos) or defined(nuttx) or defined(haiku):
+  proc maxDescriptors*(): int {.raises: OSError.} =
+    ## Returns the maximum number of active file descriptors for the current
+    ## process. This involves a system call. For now `maxDescriptors` is
+    ## supported on the following OSes: Windows, Linux, OSX, BSD, Solaris.
+    when defined(windows):
+      result = 16_700_000
+    elif defined(zephyr) or defined(freertos):
+      result = FD_MAX
+    else:
+      var fdLim: RLimit
+      if getrlimit(RLIMIT_NOFILE, fdLim) < 0:
+        raiseOSError(osLastError())
+      result = int(fdLim.rlim_cur) - 1
+
+when defined(genode):
+  proc scheduleCallbacks*(): bool {.discardable.} =
+    ## *Genode only.*
+    ## Schedule callback processing and return immediately.
+    ## Returns `false` if there is nothing to schedule.
+    ## RPC servers should call this to dispatch `callSoon`
+    ## bodies after retiring an RPC to its client.
+    ## This is effectively a non-blocking `poll(…)` and is
+    ## equivalent to scheduling a momentary no-op timeout
+    ## but faster and with less overhead.
+    let dis = getGlobalDispatcher()
+    result = dis.callbacks.len > 0
+    if result: submit(dis.signalHandler.cap)
diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim
index 37339d3d1..0f6504342 100644
--- a/lib/pure/asyncfile.nim
+++ b/lib/pure/asyncfile.nim
@@ -9,31 +9,37 @@
 
 ## This module implements asynchronous file reading and writing.
 ##
-## .. code-block:: Nim
-##    import asyncfile, asyncdispatch, os
+##   ```Nim
+##   import std/[asyncfile, asyncdispatch, os]
 ##
-##    proc main() {.async.} =
-##      var file = openAsync(getTempDir() / "foobar.txt", fmReadWrite)
-##      await file.write("test")
-##      file.setFilePos(0)
-##      let data = await file.readAll()
-##      doAssert data == "test"
-##      file.close()
+##   proc main() {.async.} =
+##     var file = openAsync(getTempDir() / "foobar.txt", fmReadWrite)
+##     await file.write("test")
+##     file.setFilePos(0)
+##     let data = await file.readAll()
+##     doAssert data == "test"
+##     file.close()
 ##
-##    waitFor main()
+##   waitFor main()
+##   ```
 
-import asyncdispatch, os
+import std/[asyncdispatch, os]
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
+  when defined(windows) or defined(nimdoc):
+    import std/widestrs
 
 # TODO: Fix duplication introduced by PR #4683.
 
 when defined(windows) or defined(nimdoc):
-  import winlean
+  import std/winlean
 else:
-  import posix
+  import std/posix
 
 type
   AsyncFile* = ref object
-    fd: AsyncFd
+    fd: AsyncFD
     offset: int64
 
 when defined(windows) or defined(nimdoc):
@@ -72,7 +78,7 @@ else:
 proc getFileSize*(f: AsyncFile): int64 =
   ## Retrieves the specified file's size.
   when defined(windows) or defined(nimdoc):
-    var high: DWord
+    var high: DWORD
     let low = getFileSize(f.fd.Handle, addr high)
     if low == INVALID_FILE_SIZE:
       raiseOSError(osLastError())
@@ -83,32 +89,27 @@ proc getFileSize*(f: AsyncFile): int64 =
     f.offset = lseek(f.fd.cint, curPos, SEEK_SET)
     assert(f.offset == curPos)
 
-proc newAsyncFile*(fd: AsyncFd): AsyncFile =
+proc newAsyncFile*(fd: AsyncFD): AsyncFile =
   ## Creates `AsyncFile` with a previously opened file descriptor `fd`.
   new result
   result.fd = fd
   register(fd)
 
 proc openAsync*(filename: string, mode = fmRead): AsyncFile =
-  ## Opens a file specified by the path in ``filename`` using
-  ## the specified FileMode ``mode`` asynchronously.
+  ## Opens a file specified by the path in `filename` using
+  ## the specified FileMode `mode` asynchronously.
   when defined(windows) or defined(nimdoc):
     let flags = FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL
     let desiredAccess = getDesiredAccess(mode)
     let creationDisposition = getCreationDisposition(mode, filename)
-    when useWinUnicode:
-      let fd = createFileW(newWideCString(filename), desiredAccess,
-          FILE_SHARE_READ,
-          nil, creationDisposition, flags, 0)
-    else:
-      let fd = createFileA(filename, desiredAccess,
-          FILE_SHARE_READ,
-          nil, creationDisposition, flags, 0)
+    let fd = createFileW(newWideCString(filename), desiredAccess,
+        FILE_SHARE_READ,
+        nil, creationDisposition, flags, 0)
 
     if fd == INVALID_HANDLE_VALUE:
       raiseOSError(osLastError())
 
-    result = newAsyncFile(fd.AsyncFd)
+    result = newAsyncFile(fd.AsyncFD)
 
     if mode == fmAppend:
       result.offset = getFileSize(result)
@@ -121,21 +122,20 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile =
     if fd == -1:
       raiseOSError(osLastError())
 
-    result = newAsyncFile(fd.AsyncFd)
+    result = newAsyncFile(fd.AsyncFD)
 
 proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
-  ## Read ``size`` bytes from the specified file asynchronously starting at
+  ## Read `size` bytes from the specified file asynchronously starting at
   ## the current position of the file pointer.
   ##
   ## If the file pointer is past the end of the file then zero is returned
-  ## and no bytes are read into ``buf``
+  ## and no bytes are read into `buf`
   var retFuture = newFuture[int]("asyncfile.readBuffer")
 
   when defined(windows) or defined(nimdoc):
-    var ol = PCustomOverlapped()
-    GC_ref(ol)
+    var ol = newCustom()
     ol.data = CompletionData(fd: f.fd, cb:
-      proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
+      proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) =
         if not retFuture.finished:
           if errcode == OSErrorCode(-1):
             assert bytesCount > 0
@@ -146,10 +146,10 @@ proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
             if errcode.int32 == ERROR_HANDLE_EOF:
               retFuture.complete(0)
             else:
-              retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+              retFuture.fail(newOSError(errcode))
     )
-    ol.offset = DWord(f.offset and 0xffffffff)
-    ol.offsetHigh = DWord(f.offset shr 32)
+    ol.offset = DWORD(f.offset and 0xffffffff)
+    ol.offsetHigh = DWORD(f.offset shr 32)
 
     # According to MSDN we're supposed to pass nil to lpNumberOfBytesRead.
     let ret = readFile(f.fd.Handle, buf, size.int32, nil,
@@ -162,18 +162,18 @@ proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
           # This happens in Windows Server 2003
           retFuture.complete(0)
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(err)))
+          retFuture.fail(newOSError(err))
     else:
       # Request completed immediately.
-      var bytesRead: DWord
+      var bytesRead: DWORD
       let overlappedRes = getOverlappedResult(f.fd.Handle,
-          cast[POverlapped](ol), bytesRead, false.WinBool)
+          cast[POVERLAPPED](ol), bytesRead, false.WINBOOL)
       if not overlappedRes.bool:
         let err = osLastError()
         if err.int32 == ERROR_HANDLE_EOF:
           retFuture.complete(0)
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
+          retFuture.fail(newOSError(osLastError()))
       else:
         assert bytesRead > 0
         assert bytesRead <= size
@@ -186,7 +186,7 @@ proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
       if res < 0:
         let lastError = osLastError()
         if lastError.int32 != EAGAIN:
-          retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+          retFuture.fail(newOSError(lastError))
         else:
           result = false # We still want this callback to be called.
       elif res == 0:
@@ -202,20 +202,20 @@ proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
   return retFuture
 
 proc read*(f: AsyncFile, size: int): Future[string] =
-  ## Read ``size`` bytes from the specified file asynchronously starting at
-  ## the current position of the file pointer.
+  ## Read `size` bytes from the specified file asynchronously starting at
+  ## the current position of the file pointer. `size` should be greater than zero.
   ##
   ## If the file pointer is past the end of the file then an empty string is
   ## returned.
+  assert size > 0
   var retFuture = newFuture[string]("asyncfile.read")
 
   when defined(windows) or defined(nimdoc):
     var buffer = alloc0(size)
 
-    var ol = PCustomOverlapped()
-    GC_ref(ol)
+    var ol = newCustom()
     ol.data = CompletionData(fd: f.fd, cb:
-      proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
+      proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) =
         if not retFuture.finished:
           if errcode == OSErrorCode(-1):
             assert bytesCount > 0
@@ -228,13 +228,13 @@ proc read*(f: AsyncFile, size: int): Future[string] =
             if errcode.int32 == ERROR_HANDLE_EOF:
               retFuture.complete("")
             else:
-              retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+              retFuture.fail(newOSError(errcode))
         if buffer != nil:
           dealloc buffer
           buffer = nil
     )
-    ol.offset = DWord(f.offset and 0xffffffff)
-    ol.offsetHigh = DWord(f.offset shr 32)
+    ol.offset = DWORD(f.offset and 0xffffffff)
+    ol.offsetHigh = DWORD(f.offset shr 32)
 
     # According to MSDN we're supposed to pass nil to lpNumberOfBytesRead.
     let ret = readFile(f.fd.Handle, buffer, size.int32, nil,
@@ -251,18 +251,18 @@ proc read*(f: AsyncFile, size: int): Future[string] =
           # This happens in Windows Server 2003
           retFuture.complete("")
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(err)))
+          retFuture.fail(newOSError(err))
     else:
       # Request completed immediately.
-      var bytesRead: DWord
+      var bytesRead: DWORD
       let overlappedRes = getOverlappedResult(f.fd.Handle,
-          cast[POverlapped](ol), bytesRead, false.WinBool)
+          cast[POVERLAPPED](ol), bytesRead, false.WINBOOL)
       if not overlappedRes.bool:
         let err = osLastError()
         if err.int32 == ERROR_HANDLE_EOF:
           retFuture.complete("")
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
+          retFuture.fail(newOSError(osLastError()))
       else:
         assert bytesRead > 0
         assert bytesRead <= size
@@ -279,7 +279,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
       if res < 0:
         let lastError = osLastError()
         if lastError.int32 != EAGAIN:
-          retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+          retFuture.fail(newOSError(lastError))
         else:
           result = false # We still want this callback to be called.
       elif res == 0:
@@ -301,6 +301,8 @@ proc readLine*(f: AsyncFile): Future[string] {.async.} =
   result = ""
   while true:
     var c = await read(f, 1)
+    if c.len == 0:
+      break
     if c[0] == '\c':
       c = await read(f, 1)
       break
@@ -334,29 +336,28 @@ proc readAll*(f: AsyncFile): Future[string] {.async.} =
     result.add data
 
 proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
-  ## Writes ``size`` bytes from ``buf`` to the file specified asynchronously.
+  ## Writes `size` bytes from `buf` to the file specified asynchronously.
   ##
   ## The returned Future will complete once all data has been written to the
   ## specified file.
   var retFuture = newFuture[void]("asyncfile.writeBuffer")
   when defined(windows) or defined(nimdoc):
-    var ol = PCustomOverlapped()
-    GC_ref(ol)
+    var ol = newCustom()
     ol.data = CompletionData(fd: f.fd, cb:
-      proc (fd: AsyncFD, bytesCount: DWord, errcode: OSErrorCode) =
+      proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) =
         if not retFuture.finished:
           if errcode == OSErrorCode(-1):
             assert bytesCount == size.int32
             retFuture.complete()
           else:
-            retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+            retFuture.fail(newOSError(errcode))
     )
     # passing -1 here should work according to MSDN, but doesn't. For more
     # information see
     # http://stackoverflow.com/questions/33650899/does-asynchronous-file-
     #   appending-in-windows-preserve-order
-    ol.offset = DWord(f.offset and 0xffffffff)
-    ol.offsetHigh = DWord(f.offset shr 32)
+    ol.offset = DWORD(f.offset and 0xffffffff)
+    ol.offsetHigh = DWORD(f.offset shr 32)
     f.offset.inc(size)
 
     # According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten.
@@ -366,14 +367,14 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
       let err = osLastError()
       if err.int32 != ERROR_IO_PENDING:
         GC_unref(ol)
-        retFuture.fail(newException(OSError, osErrorMsg(err)))
+        retFuture.fail(newOSError(err))
     else:
       # Request completed immediately.
-      var bytesWritten: DWord
+      var bytesWritten: DWORD
       let overlappedRes = getOverlappedResult(f.fd.Handle,
-          cast[POverlapped](ol), bytesWritten, false.WinBool)
+          cast[POVERLAPPED](ol), bytesWritten, false.WINBOOL)
       if not overlappedRes.bool:
-        retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
+        retFuture.fail(newOSError(osLastError()))
       else:
         assert bytesWritten == size.int32
         retFuture.complete()
@@ -382,13 +383,13 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
 
     proc cb(fd: AsyncFD): bool =
       result = true
-      let remainderSize = size-written
+      let remainderSize = size - written
       var cbuf = cast[cstring](buf)
       let res = write(fd.cint, addr cbuf[written], remainderSize.cint)
       if res < 0:
         let lastError = osLastError()
         if lastError.int32 != EAGAIN:
-          retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+          retFuture.fail(newOSError(lastError))
         else:
           result = false # We still want this callback to be called.
       else:
@@ -404,7 +405,7 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
   return retFuture
 
 proc write*(f: AsyncFile, data: string): Future[void] =
-  ## Writes ``data`` to the file specified asynchronously.
+  ## Writes `data` to the file specified asynchronously.
   ##
   ## The returned Future will complete once all data has been written to the
   ## specified file.
@@ -412,24 +413,23 @@ proc write*(f: AsyncFile, data: string): Future[void] =
   var copy = data
   when defined(windows) or defined(nimdoc):
     var buffer = alloc0(data.len)
-    copyMem(buffer, addr copy[0], data.len)
+    copyMem(buffer, copy.cstring, data.len)
 
-    var ol = PCustomOverlapped()
-    GC_ref(ol)
+    var ol = newCustom()
     ol.data = CompletionData(fd: f.fd, cb:
-      proc (fd: AsyncFD, bytesCount: DWord, errcode: OSErrorCode) =
+      proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) =
         if not retFuture.finished:
           if errcode == OSErrorCode(-1):
             assert bytesCount == data.len.int32
             retFuture.complete()
           else:
-            retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+            retFuture.fail(newOSError(errcode))
         if buffer != nil:
           dealloc buffer
           buffer = nil
     )
-    ol.offset = DWord(f.offset and 0xffffffff)
-    ol.offsetHigh = DWord(f.offset shr 32)
+    ol.offset = DWORD(f.offset and 0xffffffff)
+    ol.offsetHigh = DWORD(f.offset shr 32)
     f.offset.inc(data.len)
 
     # According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten.
@@ -442,14 +442,14 @@ proc write*(f: AsyncFile, data: string): Future[void] =
           dealloc buffer
           buffer = nil
         GC_unref(ol)
-        retFuture.fail(newException(OSError, osErrorMsg(err)))
+        retFuture.fail(newOSError(err))
     else:
       # Request completed immediately.
-      var bytesWritten: DWord
+      var bytesWritten: DWORD
       let overlappedRes = getOverlappedResult(f.fd.Handle,
-          cast[POverlapped](ol), bytesWritten, false.WinBool)
+          cast[POVERLAPPED](ol), bytesWritten, false.WINBOOL)
       if not overlappedRes.bool:
-        retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
+        retFuture.fail(newOSError(osLastError()))
       else:
         assert bytesWritten == data.len.int32
         retFuture.complete()
@@ -458,12 +458,19 @@ proc write*(f: AsyncFile, data: string): Future[void] =
 
     proc cb(fd: AsyncFD): bool =
       result = true
-      let remainderSize = data.len-written
-      let res = write(fd.cint, addr copy[written], remainderSize.cint)
+
+      let remainderSize = data.len - written
+
+      let res =
+        if data.len == 0:
+          write(fd.cint, copy.cstring, 0)
+        else:
+          write(fd.cint, addr copy[written], remainderSize.cint)
+
       if res < 0:
         let lastError = osLastError()
         if lastError.int32 != EAGAIN:
-          retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+          retFuture.fail(newOSError(lastError))
         else:
           result = false # We still want this callback to be called.
       else:
@@ -482,13 +489,13 @@ proc setFileSize*(f: AsyncFile, length: int64) =
   ## Set a file length.
   when defined(windows) or defined(nimdoc):
     var
-      high = (length shr 32).Dword
+      high = (length shr 32).DWORD
     let
-      low = (length and 0xffffffff).Dword
+      low = (length and 0xffffffff).DWORD
       status = setFilePointer(f.fd.Handle, low, addr high, 0)
       lastErr = osLastError()
     if (status == INVALID_SET_FILE_POINTER and lastErr.int32 != NO_ERROR) or
-       (setEndOfFile(f.fd.Handle) == 0):
+        (setEndOfFile(f.fd.Handle) == 0):
       raiseOSError(osLastError())
   else:
     # will truncate if Off is a 32-bit type!
diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim
deleted file mode 100644
index 019a18f55..000000000
--- a/lib/pure/asyncftpclient.nim
+++ /dev/null
@@ -1,397 +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.
-#
-
-## This module implements an asynchronous FTP client. It allows you to connect
-## to an FTP server and perform operations on it such as for example:
-##
-## * The upload of new files.
-## * The removal of existing files.
-## * Download of files.
-## * Changing of files' permissions.
-## * Navigation through the FTP server's directories.
-##
-## Connecting to an FTP server
-## ------------------------
-##
-## In order to begin any sort of transfer of files you must first
-## connect to an FTP server. You can do so with the ``connect`` procedure.
-##
-##   .. code-block::nim
-##      import asyncdispatch, asyncftpclient
-##      proc main() {.async.} =
-##        var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test")
-##        await ftp.connect()
-##        echo("Connected")
-##      waitFor(main())
-##
-## A new ``main`` async procedure must be declared to allow the use of the
-## ``await`` keyword. The connection will complete asynchronously and the
-## client will be connected after the ``await ftp.connect()`` call.
-##
-## Uploading a new file
-## --------------------
-##
-## After a connection is made you can use the ``store`` procedure to upload
-## a new file to the FTP server. Make sure to check you are in the correct
-## working directory before you do so with the ``pwd`` procedure, you can also
-## instead specify an absolute path.
-##
-##   .. code-block::nim
-##      import asyncdispatch, asyncftpclient
-##      proc main() {.async.} =
-##        var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test")
-##        await ftp.connect()
-##        let currentDir = await ftp.pwd()
-##        assert currentDir == "/home/user/"
-##        await ftp.store("file.txt", "file.txt")
-##        echo("File finished uploading")
-##      waitFor(main())
-##
-## Checking the progress of a file transfer
-## ----------------------------------------
-##
-## The progress of either a file upload or a file download can be checked
-## by specifying a ``onProgressChanged`` procedure to the ``store`` or
-## ``retrFile`` procedures.
-##
-##   .. code-block::nim
-##      import asyncdispatch, asyncftpclient
-##
-##      proc onProgressChanged(total, progress: BiggestInt,
-##                              speed: float): Future[void] =
-##        echo("Uploaded ", progress, " of ", total, " bytes")
-##        echo("Current speed: ", speed, " kb/s")
-##
-##      proc main() {.async.} =
-##        var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test")
-##        await ftp.connect()
-##        await ftp.store("file.txt", "/home/user/file.txt", onProgressChanged)
-##        echo("File finished uploading")
-##      waitFor(main())
-
-
-import asyncdispatch, asyncnet, strutils, parseutils, os, times
-
-from ftpclient import FtpBaseObj, ReplyError, FtpEvent
-from net import BufferSize
-
-type
-  AsyncFtpClientObj* = FtpBaseObj[AsyncSocket]
-  AsyncFtpClient* = ref AsyncFtpClientObj
-
-  ProgressChangedProc* =
-    proc (total, progress: BiggestInt, speed: float):
-      Future[void] {.closure, gcsafe.}
-
-const multiLineLimit = 10000
-
-proc expectReply(ftp: AsyncFtpClient): Future[TaintedString] {.async.} =
-  result = await ftp.csock.recvLine()
-  var count = 0
-  while result[3] == '-':
-    ## Multi-line reply.
-    let line = await ftp.csock.recvLine()
-    result.add("\n" & line)
-    count.inc()
-    if count >= multiLineLimit:
-      raise newException(ReplyError, "Reached maximum multi-line reply count.")
-
-proc send*(ftp: AsyncFtpClient, m: string): Future[TaintedString] {.async.} =
-  ## Send a message to the server, and wait for a primary reply.
-  ## ``\c\L`` is added for you.
-  ##
-  ## **Note:** The server may return multiple lines of coded replies.
-  await ftp.csock.send(m & "\c\L")
-  return await ftp.expectReply()
-
-proc assertReply(received: TaintedString, expected: varargs[string]) =
-  for i in items(expected):
-    if received.string.startsWith(i): return
-  raise newException(ReplyError,
-                     "Expected reply '$1' got: $2" %
-                     [expected.join("' or '"), received.string])
-
-proc pasv(ftp: AsyncFtpClient) {.async.} =
-  ## Negotiate a data connection.
-  ftp.dsock = newAsyncSocket()
-
-  var pasvMsg = (await ftp.send("PASV")).string.strip.TaintedString
-  assertReply(pasvMsg, "227")
-  var betweenParens = captureBetween(pasvMsg.string, '(', ')')
-  var nums = betweenParens.split(',')
-  var ip = nums[0.. ^3]
-  var port = nums[^2.. ^1]
-  var properPort = port[0].parseInt()*256+port[1].parseInt()
-  await ftp.dsock.connect(ip.join("."), Port(properPort.toU16))
-  ftp.dsockConnected = true
-
-proc normalizePathSep(path: string): string =
-  return replace(path, '\\', '/')
-
-proc connect*(ftp: AsyncFtpClient) {.async.} =
-  ## Connect to the FTP server specified by ``ftp``.
-  await ftp.csock.connect(ftp.address, ftp.port)
-
-  var reply = await ftp.expectReply()
-  if reply.startsWith("120"):
-    # 120 Service ready in nnn minutes.
-    # We wait until we receive 220.
-    reply = await ftp.expectReply()
-
-  # Handle 220 messages from the server
-  assertReply(reply, "220")
-
-  if ftp.user != "":
-    assertReply(await(ftp.send("USER " & ftp.user)), "230", "331")
-
-  if ftp.pass != "":
-    assertReply(await(ftp.send("PASS " & ftp.pass)), "230")
-
-proc pwd*(ftp: AsyncFtpClient): Future[TaintedString] {.async.} =
-  ## Returns the current working directory.
-  let wd = await ftp.send("PWD")
-  assertReply wd, "257"
-  return wd.string.captureBetween('"').TaintedString # "
-
-proc cd*(ftp: AsyncFtpClient, dir: string) {.async.} =
-  ## Changes the current directory on the remote FTP server to ``dir``.
-  assertReply(await(ftp.send("CWD " & dir.normalizePathSep)), "250")
-
-proc cdup*(ftp: AsyncFtpClient) {.async.} =
-  ## Changes the current directory to the parent of the current directory.
-  assertReply(await(ftp.send("CDUP")), "200")
-
-proc getLines(ftp: AsyncFtpClient): Future[string] {.async.} =
-  ## Downloads text data in ASCII mode
-  result = ""
-  assert ftp.dsockConnected
-  while ftp.dsockConnected:
-    let r = await ftp.dsock.recvLine()
-    if r.string == "":
-      ftp.dsockConnected = false
-    else:
-      result.add(r.string & "\n")
-
-  assertReply(await(ftp.expectReply()), "226")
-
-proc listDirs*(ftp: AsyncFtpClient, dir = ""): Future[seq[string]] {.async.} =
-  ## Returns a list of filenames in the given directory. If ``dir`` is "",
-  ## the current directory is used. If ``async`` is true, this
-  ## function will return immediately and it will be your job to
-  ## use asyncio's ``poll`` to progress this operation.
-  await ftp.pasv()
-
-  assertReply(await(ftp.send("NLST " & dir.normalizePathSep)), ["125", "150"])
-
-  result = splitLines(await ftp.getLines())
-
-proc existsFile*(ftp: AsyncFtpClient, file: string): Future[bool] {.async.} =
-  ## Determines whether ``file`` exists.
-  var files = await ftp.listDirs()
-  for f in items(files):
-    if f.normalizePathSep == file.normalizePathSep: return true
-
-proc createDir*(ftp: AsyncFtpClient, dir: string, recursive = false){.async.} =
-  ## Creates a directory ``dir``. If ``recursive`` is true, the topmost
-  ## subdirectory of ``dir`` will be created first, following the secondmost...
-  ## etc. this allows you to give a full path as the ``dir`` without worrying
-  ## about subdirectories not existing.
-  if not recursive:
-    assertReply(await(ftp.send("MKD " & dir.normalizePathSep)), "257")
-  else:
-    var reply = TaintedString""
-    var previousDirs = ""
-    for p in split(dir, {os.DirSep, os.AltSep}):
-      if p != "":
-        previousDirs.add(p)
-        reply = await ftp.send("MKD " & previousDirs)
-        previousDirs.add('/')
-    assertReply reply, "257"
-
-proc chmod*(ftp: AsyncFtpClient, path: string,
-            permissions: set[FilePermission]) {.async.} =
-  ## Changes permission of ``path`` to ``permissions``.
-  var userOctal = 0
-  var groupOctal = 0
-  var otherOctal = 0
-  for i in items(permissions):
-    case i
-    of fpUserExec: userOctal.inc(1)
-    of fpUserWrite: userOctal.inc(2)
-    of fpUserRead: userOctal.inc(4)
-    of fpGroupExec: groupOctal.inc(1)
-    of fpGroupWrite: groupOctal.inc(2)
-    of fpGroupRead: groupOctal.inc(4)
-    of fpOthersExec: otherOctal.inc(1)
-    of fpOthersWrite: otherOctal.inc(2)
-    of fpOthersRead: otherOctal.inc(4)
-
-  var perm = $userOctal & $groupOctal & $otherOctal
-  assertReply(await(ftp.send("SITE CHMOD " & perm &
-                    " " & path.normalizePathSep)), "200")
-
-proc list*(ftp: AsyncFtpClient, dir = ""): Future[string] {.async.} =
-  ## Lists all files in ``dir``. If ``dir`` is ``""``, uses the current
-  ## working directory.
-  await ftp.pasv()
-
-  let reply = await ftp.send("LIST" & " " & dir.normalizePathSep)
-  assertReply(reply, ["125", "150"])
-
-  result = await ftp.getLines()
-
-proc retrText*(ftp: AsyncFtpClient, file: string): Future[string] {.async.} =
-  ## Retrieves ``file``. File must be ASCII text.
-  await ftp.pasv()
-  let reply = await ftp.send("RETR " & file.normalizePathSep)
-  assertReply(reply, ["125", "150"])
-
-  result = await ftp.getLines()
-
-proc getFile(ftp: AsyncFtpClient, file: File, total: BiggestInt,
-             onProgressChanged: ProgressChangedProc) {.async.} =
-  assert ftp.dsockConnected
-  var progress = 0
-  var progressInSecond = 0
-  var countdownFut = sleepAsync(1000)
-  var dataFut = ftp.dsock.recv(BufferSize)
-  while ftp.dsockConnected:
-    await dataFut or countdownFut
-    if countdownFut.finished:
-      asyncCheck onProgressChanged(total, progress,
-          progressInSecond.float)
-      progressInSecond = 0
-      countdownFut = sleepAsync(1000)
-
-    if dataFut.finished:
-      let data = dataFut.read
-      if data != "":
-        progress.inc(data.len)
-        progressInSecond.inc(data.len)
-        file.write(data)
-        dataFut = ftp.dsock.recv(BufferSize)
-      else:
-        ftp.dsockConnected = false
-
-  assertReply(await(ftp.expectReply()), "226")
-
-proc defaultOnProgressChanged*(total, progress: BiggestInt,
-    speed: float): Future[void] {.nimcall,gcsafe,procvar.} =
-  ## Default FTP ``onProgressChanged`` handler. Does nothing.
-  result = newFuture[void]()
-  #echo(total, " ", progress, " ", speed)
-  result.complete()
-
-proc retrFile*(ftp: AsyncFtpClient, file, dest: string,
-               onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} =
-  ## Downloads ``file`` and saves it to ``dest``.
-  ## The ``EvRetr`` event is passed to the specified ``handleEvent`` function
-  ## when the download is finished. The event's ``filename`` field will be equal
-  ## to ``file``.
-  var destFile = open(dest, mode = fmWrite)
-  await ftp.pasv()
-  var reply = await ftp.send("RETR " & file.normalizePathSep)
-  assertReply reply, ["125", "150"]
-  if {'(', ')'} notin reply.string:
-    raise newException(ReplyError, "Reply has no file size.")
-  var fileSize: BiggestInt
-  if reply.string.captureBetween('(', ')').parseBiggestInt(fileSize) == 0:
-    raise newException(ReplyError, "Reply has no file size.")
-
-  await getFile(ftp, destFile, fileSize, onProgressChanged)
-
-proc doUpload(ftp: AsyncFtpClient, file: File,
-              onProgressChanged: ProgressChangedProc) {.async.} =
-  assert ftp.dsockConnected
-
-  let total = file.getFileSize()
-  var data = newStringOfCap(4000)
-  var progress = 0
-  var progressInSecond = 0
-  var countdownFut = sleepAsync(1000)
-  var sendFut: Future[void] = nil
-  while ftp.dsockConnected:
-    if sendFut == nil or sendFut.finished:
-      progress.inc(data.len)
-      progressInSecond.inc(data.len)
-      # TODO: Async file reading.
-      let len = file.readBuffer(addr(data[0]), 4000)
-      setLen(data, len)
-      if len == 0:
-        # File finished uploading.
-        ftp.dsock.close()
-        ftp.dsockConnected = false
-
-        assertReply(await(ftp.expectReply()), "226")
-      else:
-        sendFut = ftp.dsock.send(data)
-
-    if countdownFut.finished:
-      asyncCheck onProgressChanged(total, progress, progressInSecond.float)
-      progressInSecond = 0
-      countdownFut = sleepAsync(1000)
-
-    await countdownFut or sendFut
-
-proc store*(ftp: AsyncFtpClient, file, dest: string,
-            onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} =
-  ## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this
-  ## function asynchronously is recommended to view the progress of
-  ## the download.
-  ## The ``EvStore`` event is passed to the specified ``handleEvent`` function
-  ## when the upload is finished, and the ``filename`` field will be
-  ## equal to ``file``.
-  var destFile = open(file)
-  await ftp.pasv()
-
-  let reply = await ftp.send("STOR " & dest.normalizePathSep)
-  assertReply reply, ["125", "150"]
-
-  await doUpload(ftp, destFile, onProgressChanged)
-
-proc rename*(ftp: AsyncFtpClient, nameFrom: string, nameTo: string) {.async.} =
-  ## Rename a file or directory on the remote FTP Server from current name
-  ## ``name_from`` to new name ``name_to``
-  assertReply(await ftp.send("RNFR " & name_from), "350")
-  assertReply(await ftp.send("RNTO " & name_to), "250")
-
-proc removeFile*(ftp: AsyncFtpClient, filename: string) {.async.} =
-  ## Delete a file ``filename`` on the remote FTP server
-  assertReply(await ftp.send("DELE " & filename), "250")
-
-proc removeDir*(ftp: AsyncFtpClient, dir: string) {.async.} =
-  ## Delete a directory ``dir`` on the remote FTP server
-  assertReply(await ftp.send("RMD " & dir), "250")
-
-proc newAsyncFtpClient*(address: string, port = Port(21),
-    user, pass = ""): AsyncFtpClient =
-  ## Creates a new ``AsyncFtpClient`` object.
-  new result
-  result.user = user
-  result.pass = pass
-  result.address = address
-  result.port = port
-  result.dsockConnected = false
-  result.csock = newAsyncSocket()
-
-when not defined(testing) and isMainModule:
-  var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test")
-  proc main(ftp: AsyncFtpClient) {.async.} =
-    await ftp.connect()
-    echo await ftp.pwd()
-    echo await ftp.listDirs()
-    await ftp.store("payload.jpg", "payload.jpg")
-    await ftp.retrFile("payload.jpg", "payload2.jpg")
-    await ftp.rename("payload.jpg", "payload_renamed.jpg")
-    await ftp.store("payload.jpg", "payload_remove.jpg")
-    await ftp.removeFile("payload_remove.jpg")
-    await ftp.createDir("deleteme")
-    await ftp.removeDir("deleteme")
-    echo("Finished")
-
-  waitFor main(ftp)
diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim
index 863a6843b..29ebf8f89 100644
--- a/lib/pure/asyncfutures.nim
+++ b/lib/pure/asyncfutures.nim
@@ -1,4 +1,19 @@
-import os, tables, strutils, times, heapqueue, options, deques, cstrutils
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Dominik Picheta
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+import std/[os, tables, strutils, times, heapqueue, options, deques, cstrutils, typetraits]
+
+import system/stacktraces
+
+when defined(nimPreviewSlimSystem):
+  import std/objectdollar # for StackTraceEntry
+  import std/assertions
 
 # TODO: This shouldn't need to be included, but should ideally be exported.
 type
@@ -6,46 +21,88 @@ type
 
   CallbackList = object
     function: CallbackFunc
-    next: ref CallbackList
+    next: owned(ref CallbackList)
 
-  FutureBase* = ref object of RootObj ## Untyped future.
+  FutureBase* = ref object of RootObj  ## Untyped future.
     callbacks: CallbackList
 
     finished: bool
-    error*: ref Exception ## Stored exception
+    error*: ref Exception              ## Stored exception
     errorStackTrace*: string
-    when not defined(release):
-      stackTrace: string ## For debugging purposes only.
+    when not defined(release) or defined(futureLogging):
+      stackTrace: seq[StackTraceEntry] ## For debugging purposes only.
       id: int
       fromProc: string
 
   Future*[T] = ref object of FutureBase ## Typed future.
-    value: T ## Stored value
+    value: T                            ## Stored value
 
   FutureVar*[T] = distinct Future[T]
 
-  FutureError* = object of Exception
+  FutureError* = object of Defect
     cause*: FutureBase
 
 when not defined(release):
   var currentID = 0
 
+const isFutureLoggingEnabled* = defined(futureLogging)
+
+const
+  NimAsyncContinueSuffix* = "NimAsyncContinue" ## For internal usage. Do not use.
+
+when isFutureLoggingEnabled:
+  import std/hashes
+  type
+    FutureInfo* = object
+      stackTrace*: seq[StackTraceEntry]
+      fromProc*: string
+
+  var futuresInProgress {.threadvar.}: Table[FutureInfo, int]
+
+  proc getFuturesInProgress*(): var Table[FutureInfo, int] =
+    return futuresInProgress
+
+  proc hash(s: StackTraceEntry): Hash =
+    result = hash(s.procname) !& hash(s.line) !&
+      hash(s.filename)
+    result = !$result
+
+  proc hash(fi: FutureInfo): Hash =
+    result = hash(fi.stackTrace) !& hash(fi.fromProc)
+    result = !$result
+
+  proc getFutureInfo(fut: FutureBase): FutureInfo =
+    let info = FutureInfo(
+      stackTrace: fut.stackTrace,
+      fromProc: fut.fromProc
+    )
+    return info
+
+  proc logFutureStart(fut: FutureBase) =
+    let info = getFutureInfo(fut)
+    if info notin getFuturesInProgress():
+      getFuturesInProgress()[info] = 0
+    getFuturesInProgress()[info].inc()
+
+  proc logFutureFinish(fut: FutureBase) =
+    getFuturesInProgress()[getFutureInfo(fut)].dec()
+
 var callSoonProc {.threadvar.}: proc (cbproc: proc ()) {.gcsafe.}
 
 proc getCallSoonProc*(): (proc(cbproc: proc ()) {.gcsafe.}) =
-  ## Get current implementation of ``callSoon``.
+  ## Get current implementation of `callSoon`.
   return callSoonProc
 
 proc setCallSoonProc*(p: (proc(cbproc: proc ()) {.gcsafe.})) =
-  ## Change current implementation of ``callSoon``. This is normally called when dispatcher from ``asyncdispatcher`` is initialized.
+  ## Change current implementation of `callSoon`. This is normally called when dispatcher from `asyncdispatcher` is initialized.
   callSoonProc = p
 
-proc callSoon*(cbproc: proc ()) =
-  ## Call ``cbproc`` "soon".
+proc callSoon*(cbproc: proc () {.gcsafe.}) =
+  ## Call `cbproc` "soon".
   ##
-  ## If async dispatcher is running, ``cbproc`` will be executed during next dispatcher tick.
+  ## If async dispatcher is running, `cbproc` will be executed during next dispatcher tick.
   ##
-  ## If async dispatcher is not running, ``cbproc`` will be executed immediately.
+  ## If async dispatcher is not running, `cbproc` will be executed immediately.
   if callSoonProc.isNil:
     # Loop not initialized yet. Call the function directly to allow setup code to use futures.
     cbproc()
@@ -56,34 +113,37 @@ template setupFutureBase(fromProc: string) =
   new(result)
   result.finished = false
   when not defined(release):
-    result.stackTrace = getStackTrace()
+    result.stackTrace = getStackTraceEntries()
     result.id = currentID
     result.fromProc = fromProc
     currentID.inc()
 
-proc newFuture*[T](fromProc: string = "unspecified"): Future[T] =
+proc newFuture*[T](fromProc: string = "unspecified"): owned(Future[T]) =
   ## Creates a new future.
   ##
-  ## Specifying ``fromProc``, which is a string specifying the name of the proc
+  ## Specifying `fromProc`, which is a string specifying the name of the proc
   ## that this future belongs to, is a good habit as it helps with debugging.
   setupFutureBase(fromProc)
+  when isFutureLoggingEnabled: logFutureStart(result)
 
-proc newFutureVar*[T](fromProc = "unspecified"): FutureVar[T] =
-  ## Create a new ``FutureVar``. This Future type is ideally suited for
+proc newFutureVar*[T](fromProc = "unspecified"): owned(FutureVar[T]) =
+  ## Create a new `FutureVar`. This Future type is ideally suited for
   ## situations where you want to avoid unnecessary allocations of Futures.
   ##
-  ## Specifying ``fromProc``, which is a string specifying the name of the proc
+  ## Specifying `fromProc`, which is a string specifying the name of the proc
   ## that this future belongs to, is a good habit as it helps with debugging.
-  result = FutureVar[T](newFuture[T](fromProc))
+  let fo = newFuture[T](fromProc)
+  result = typeof(result)(fo)
+  when isFutureLoggingEnabled: logFutureStart(Future[T](result))
 
 proc clean*[T](future: FutureVar[T]) =
-  ## Resets the ``finished`` status of ``future``.
+  ## Resets the `finished` status of `future`.
   Future[T](future).finished = false
   Future[T](future).error = nil
 
 proc checkFinished[T](future: Future[T]) =
   ## Checks whether `future` is finished. If it is then raises a
-  ## ``FutureError``.
+  ## `FutureError`.
   when not defined(release):
     if future.finished:
       var msg = ""
@@ -92,10 +152,10 @@ proc checkFinished[T](future: Future[T]) =
       msg.add("\n  Future ID: " & $future.id)
       msg.add("\n  Created in proc: " & future.fromProc)
       msg.add("\n  Stack trace to moment of creation:")
-      msg.add("\n" & indent(future.stackTrace.strip(), 4))
+      msg.add("\n" & indent(($future.stackTrace).strip(), 4))
       when T is string:
         msg.add("\n  Contents (string): ")
-        msg.add("\n" & indent(future.value.repr, 4))
+        msg.add("\n" & indent($future.value, 4))
       msg.add("\n  Stack trace to moment of secondary completion:")
       msg.add("\n" & indent(getStackTrace().strip(), 4))
       var err = newException(FutureError, msg)
@@ -104,7 +164,6 @@ proc checkFinished[T](future: Future[T]) =
 
 proc call(callbacks: var CallbackList) =
   var current = callbacks
-
   while true:
     if not current.function.isNil:
       callSoon(current.function)
@@ -113,7 +172,6 @@ proc call(callbacks: var CallbackList) =
       break
     else:
       current = current.next[]
-
   # callback will be called only once, let GC collect them now
   callbacks.next = nil
   callbacks.function = nil
@@ -123,39 +181,46 @@ proc add(callbacks: var CallbackList, function: CallbackFunc) =
     callbacks.function = function
     assert callbacks.next == nil
   else:
-    let newNext = new(ref CallbackList)
-    newNext.function = callbacks.function
-    newNext.next = callbacks.next
-    callbacks.next = newNext
-    callbacks.function = function
+    let newCallback = new(ref CallbackList)
+    newCallback.function = function
+    newCallback.next = nil
 
-proc complete*[T](future: Future[T], val: T) =
-  ## Completes ``future`` with value ``val``.
-  #assert(not future.finished, "Future already finished, cannot finish twice.")
-  checkFinished(future)
-  assert(future.error == nil)
-  future.value = val
-  future.finished = true
-  future.callbacks.call()
+    if callbacks.next == nil:
+      callbacks.next = newCallback
+    else:
+      var last = callbacks.next
+      while last.next != nil:
+        last = last.next
+      last.next = newCallback
 
-proc complete*(future: Future[void]) =
-  ## Completes a void ``future``.
+proc completeImpl[T, U](future: Future[T], val: sink U, isVoid: static bool) =
   #assert(not future.finished, "Future already finished, cannot finish twice.")
   checkFinished(future)
   assert(future.error == nil)
+  when not isVoid:
+    future.value = val
   future.finished = true
   future.callbacks.call()
+  when isFutureLoggingEnabled: logFutureFinish(future)
+
+proc complete*[T](future: Future[T], val: sink T) =
+  ## Completes `future` with value `val`.
+  completeImpl(future, val, false)
+
+proc complete*(future: Future[void], val = Future[void].default) =
+  completeImpl(future, (), true)
 
 proc complete*[T](future: FutureVar[T]) =
-  ## Completes a ``FutureVar``.
+  ## Completes a `FutureVar`.
   template fut: untyped = Future[T](future)
   checkFinished(fut)
   assert(fut.error == nil)
   fut.finished = true
   fut.callbacks.call()
+  when isFutureLoggingEnabled: logFutureFinish(Future[T](future))
 
-proc complete*[T](future: FutureVar[T], val: T) =
-  ## Completes a ``FutureVar`` with value ``val``.
+proc complete*[T](future: FutureVar[T], val: sink T) =
+  ## Completes a `FutureVar` with value `val`.
   ##
   ## Any previously stored value will be overwritten.
   template fut: untyped = Future[T](future)
@@ -164,9 +229,10 @@ proc complete*[T](future: FutureVar[T], val: T) =
   fut.finished = true
   fut.value = val
   fut.callbacks.call()
+  when isFutureLoggingEnabled: logFutureFinish(fut)
 
 proc fail*[T](future: Future[T], error: ref Exception) =
-  ## Completes ``future`` with ``error``.
+  ## Completes `future` with `error`.
   #assert(not future.finished, "Future already finished, cannot finish twice.")
   checkFinished(future)
   future.finished = true
@@ -174,15 +240,16 @@ proc fail*[T](future: Future[T], error: ref Exception) =
   future.errorStackTrace =
     if getStackTrace(error) == "": getStackTrace() else: getStackTrace(error)
   future.callbacks.call()
+  when isFutureLoggingEnabled: logFutureFinish(future)
 
 proc clearCallbacks*(future: FutureBase) =
   future.callbacks.function = nil
   future.callbacks.next = nil
 
-proc addCallback*(future: FutureBase, cb: proc() {.closure,gcsafe.}) =
+proc addCallback*(future: FutureBase, cb: proc() {.closure, gcsafe.}) =
   ## Adds the callbacks proc to be called when the future completes.
   ##
-  ## If future has already completed then ``cb`` will be called immediately.
+  ## If future has already completed then `cb` will be called immediately.
   assert cb != nil
   if future.finished:
     callSoon(cb)
@@ -190,78 +257,93 @@ proc addCallback*(future: FutureBase, cb: proc() {.closure,gcsafe.}) =
     future.callbacks.add cb
 
 proc addCallback*[T](future: Future[T],
-                     cb: proc (future: Future[T]) {.closure,gcsafe.}) =
+                     cb: proc (future: Future[T]) {.closure, gcsafe.}) =
   ## Adds the callbacks proc to be called when the future completes.
   ##
-  ## If future has already completed then ``cb`` will be called immediately.
+  ## If future has already completed then `cb` will be called immediately.
   future.addCallback(
     proc() =
-      cb(future)
+    cb(future)
   )
 
-proc `callback=`*(future: FutureBase, cb: proc () {.closure,gcsafe.}) =
+proc `callback=`*(future: FutureBase, cb: proc () {.closure, gcsafe.}) =
   ## Clears the list of callbacks and sets the callback proc to be called when the future completes.
   ##
-  ## If future has already completed then ``cb`` will be called immediately.
+  ## If future has already completed then `cb` will be called immediately.
   ##
-  ## It's recommended to use ``addCallback`` or ``then`` instead.
+  ## It's recommended to use `addCallback` or `then` instead.
   future.clearCallbacks
   future.addCallback cb
 
 proc `callback=`*[T](future: Future[T],
-    cb: proc (future: Future[T]) {.closure,gcsafe.}) =
+    cb: proc (future: Future[T]) {.closure, gcsafe.}) =
   ## Sets the callback proc to be called when the future completes.
   ##
-  ## If future has already completed then ``cb`` will be called immediately.
+  ## If future has already completed then `cb` will be called immediately.
   future.callback = proc () = cb(future)
 
+template getFilenameProcname(entry: StackTraceEntry): (string, string) =
+  when compiles(entry.filenameStr) and compiles(entry.procnameStr):
+    # We can't rely on "entry.filename" and "entry.procname" still being valid
+    # cstring pointers, because the "string.data" buffers they pointed to might
+    # be already garbage collected (this entry being a non-shallow copy,
+    # "entry.filename" no longer points to "entry.filenameStr.data", but to the
+    # buffer of the original object).
+    (entry.filenameStr, entry.procnameStr)
+  else:
+    ($entry.filename, $entry.procname)
+
 proc getHint(entry: StackTraceEntry): string =
   ## We try to provide some hints about stack trace entries that the user
   ## may not be familiar with, in particular calls inside the stdlib.
+
+  let (filename, procname) = getFilenameProcname(entry)
+
   result = ""
-  if entry.procname == "processPendingCallbacks":
-    if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0:
+  if procname == "processPendingCallbacks":
+    if cmpIgnoreStyle(filename, "asyncdispatch.nim") == 0:
       return "Executes pending callbacks"
-  elif entry.procname == "poll":
-    if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0:
+  elif procname == "poll":
+    if cmpIgnoreStyle(filename, "asyncdispatch.nim") == 0:
       return "Processes asynchronous completion events"
 
-  if entry.procname.endsWith("_continue"):
-    if cmpIgnoreStyle(entry.filename, "asyncmacro.nim") == 0:
+  if procname.endsWith(NimAsyncContinueSuffix):
+    if cmpIgnoreStyle(filename, "asyncmacro.nim") == 0:
       return "Resumes an async procedure"
 
-proc `$`*(entries: seq[StackTraceEntry]): string =
+proc `$`*(stackTraceEntries: seq[StackTraceEntry]): string =
+  when defined(nimStackTraceOverride):
+    let entries = addDebuggingInfo(stackTraceEntries)
+  else:
+    let entries = stackTraceEntries
+
   result = ""
   # Find longest filename & line number combo for alignment purposes.
   var longestLeft = 0
   for entry in entries:
-    if entry.procName.isNil: continue
+    let (filename, procname) = getFilenameProcname(entry)
 
-    let left = $entry.filename & $entry.line
-    if left.len > longestLeft:
-      longestLeft = left.len
+    if procname == "": continue
+
+    let leftLen = filename.len + len($entry.line)
+    if leftLen > longestLeft:
+      longestLeft = leftLen
 
-  var indent = 2
   # Format the entries.
   for entry in entries:
-    if entry.procName.isNil:
-      if entry.line == -10:
-        result.add(spaces(indent) & "#[\n")
-        indent.inc(2)
-      else:
-        indent.dec(2)
-        result.add(spaces(indent)& "]#\n")
-      continue
-
-    let left = "$#($#)" % [$entry.filename, $entry.line]
-    result.add((spaces(indent) & "$#$# $#\n") % [
+    let (filename, procname) = getFilenameProcname(entry)
+
+    if procname == "" and entry.line == reraisedFromBegin:
+      break
+
+    let left = "$#($#)" % [filename, $entry.line]
+    result.add((spaces(2) & "$# $#\n") % [
       left,
-      spaces(longestLeft - left.len + 2),
-      $entry.procName
+      procname
     ])
     let hint = getHint(entry)
     if hint.len > 0:
-      result.add(spaces(indent+2) & "## " & hint & "\n")
+      result.add(spaces(4) & "## " & hint & "\n")
 
 proc injectStacktrace[T](future: Future[T]) =
   when not defined(release):
@@ -281,74 +363,84 @@ proc injectStacktrace[T](future: Future[T]) =
     newMsg.add($entries)
 
     newMsg.add("Exception message: " & exceptionMsg & "\n")
-    newMsg.add("Exception type:")
 
     # # For debugging purposes
+    # newMsg.add("Exception type:")
     # for entry in getStackTraceEntries(future.error):
     #   newMsg.add "\n" & $entry
     future.error.msg = newMsg
 
-proc read*[T](future: Future[T] | FutureVar[T]): T =
-  ## Retrieves the value of ``future``. Future must be finished otherwise
-  ## this function will fail with a ``ValueError`` exception.
-  ##
-  ## If the result of the future is an error then that error will be raised.
-  {.push hint[ConvFromXtoItselfNotNeeded]: off.}
-  let fut = Future[T](future)
-  {.pop.}
+template readImpl(future, T) =
+  when future is Future[T]:
+    let fut {.cursor.} = future
+  else:
+    let fut {.cursor.} = Future[T](future)
   if fut.finished:
     if fut.error != nil:
       injectStacktrace(fut)
       raise fut.error
     when T isnot void:
-      return fut.value
+      result = distinctBase(future).value
   else:
     # TODO: Make a custom exception type for this?
     raise newException(ValueError, "Future still in progress.")
 
+proc read*[T](future: Future[T] | FutureVar[T]): lent T =
+  ## Retrieves the value of `future`. Future must be finished otherwise
+  ## this function will fail with a `ValueError` exception.
+  ##
+  ## If the result of the future is an error then that error will be raised.
+  readImpl(future, T)
+
+proc read*(future: Future[void] | FutureVar[void]) =
+  readImpl(future, void)
+
 proc readError*[T](future: Future[T]): ref Exception =
-  ## Retrieves the exception stored in ``future``.
+  ## Retrieves the exception stored in `future`.
   ##
-  ## An ``ValueError`` exception will be thrown if no exception exists
+  ## An `ValueError` exception will be thrown if no exception exists
   ## in the specified Future.
   if future.error != nil: return future.error
   else:
     raise newException(ValueError, "No error in future.")
 
 proc mget*[T](future: FutureVar[T]): var T =
-  ## Returns a mutable value stored in ``future``.
+  ## Returns a mutable value stored in `future`.
   ##
-  ## Unlike ``read``, this function will not raise an exception if the
+  ## Unlike `read`, this function will not raise an exception if the
   ## Future has not been finished.
   result = Future[T](future).value
 
 proc finished*(future: FutureBase | FutureVar): bool =
-  ## Determines whether ``future`` has completed.
+  ## Determines whether `future` has completed.
   ##
-  ## ``True`` may indicate an error or a value. Use ``failed`` to distinguish.
+  ## `True` may indicate an error or a value. Use `failed` to distinguish.
   when future is FutureVar:
     result = (FutureBase(future)).finished
   else:
     result = future.finished
 
 proc failed*(future: FutureBase): bool =
-  ## Determines whether ``future`` completed with an error.
+  ## Determines whether `future` completed with an error.
   return future.error != nil
 
 proc asyncCheck*[T](future: Future[T]) =
-  ## Sets a callback on ``future`` which raises an exception if the future
+  ## Sets a callback on `future` which raises an exception if the future
   ## finished with an error.
   ##
-  ## This should be used instead of ``discard`` to discard void futures.
+  ## This should be used instead of `discard` to discard void futures,
+  ## or use `waitFor` if you need to wait for the future's completion.
   assert(not future.isNil, "Future is nil")
-  future.callback =
-    proc () =
-      if future.failed:
-        injectStacktrace(future)
-        raise future.error
+  # TODO: We can likely look at the stack trace here and inject the location
+  # where the `asyncCheck` was called to give a better error stack message.
+  proc asyncCheckCallback() =
+    if future.failed:
+      injectStacktrace(future)
+      raise future.error
+  future.callback = asyncCheckCallback
 
 proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
-  ## Returns a future which will complete once both ``fut1`` and ``fut2``
+  ## Returns a future which will complete once both `fut1` and `fut2`
   ## complete.
   var retFuture = newFuture[void]("asyncdispatch.`and`")
   fut1.callback =
@@ -364,26 +456,27 @@ proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
   return retFuture
 
 proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
-  ## Returns a future which will complete once either ``fut1`` or ``fut2``
+  ## Returns a future which will complete once either `fut1` or `fut2`
   ## complete.
   var retFuture = newFuture[void]("asyncdispatch.`or`")
   proc cb[X](fut: Future[X]) =
-    if fut.failed: retFuture.fail(fut.error)
-    if not retFuture.finished: retFuture.complete()
+    if not retFuture.finished:
+      if fut.failed: retFuture.fail(fut.error)
+      else: retFuture.complete()
   fut1.callback = cb[T]
   fut2.callback = cb[Y]
   return retFuture
 
 proc all*[T](futs: varargs[Future[T]]): auto =
   ## Returns a future which will complete once
-  ## all futures in ``futs`` complete.
+  ## all futures in `futs` complete.
   ## If the argument is empty, the returned future completes immediately.
   ##
-  ## If the awaited futures are not ``Future[void]``, the returned future
+  ## If the awaited futures are not `Future[void]`, the returned future
   ## will hold the values of all awaited futures in a sequence.
   ##
-  ## If the awaited futures *are* ``Future[void]``,
-  ## this proc returns ``Future[void]``.
+  ## If the awaited futures *are* `Future[void]`,
+  ## this proc returns `Future[void]`.
 
   when T is void:
     var
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index d27c2fb9c..39e945d5e 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -11,28 +11,41 @@
 ##
 ## This HTTP server has not been designed to be used in production, but
 ## for testing applications locally. Because of this, when deploying your
-## application you should use a reverse proxy (for example nginx) instead of
-## allowing users to connect directly to this server.
-##
-##
-## Examples
-## --------
-##
-## This example will create an HTTP server on port 8080. The server will
-## respond to all requests with a ``200 OK`` response code and "Hello World"
-## as the response body.
-##
-## .. code-block::nim
-##    import asynchttpserver, asyncdispatch
-##
-##    var server = newAsyncHttpServer()
-##    proc cb(req: Request) {.async.} =
-##      await req.respond(Http200, "Hello World")
-##
-##    waitFor server.serve(Port(8080), cb)
+## application in production you should use a reverse proxy (for example nginx)
+## instead of allowing users to connect directly to this server.
+
+runnableExamples("-r:off"):
+  # This example will create an HTTP server on an automatically chosen port.
+  # It will respond to all requests with a `200 OK` response code and "Hello World"
+  # as the response body.
+  import std/asyncdispatch
+  proc main {.async.} =
+    var server = newAsyncHttpServer()
+    proc cb(req: Request) {.async.} =
+      echo (req.reqMethod, req.url, req.headers)
+      let headers = {"Content-type": "text/plain; charset=utf-8"}
+      await req.respond(Http200, "Hello World", headers.newHttpHeaders())
+
+    server.listen(Port(0)) # or Port(8080) to hardcode the standard HTTP port.
+    let port = server.getPort
+    echo "test this with: curl localhost:" & $port.uint16 & "/"
+    while true:
+      if server.shouldAcceptRequest():
+        await server.acceptRequest(cb)
+      else:
+        # too many concurrent connections, `maxFDs` exceeded
+        # wait 500ms for FDs to be closed
+        await sleepAsync(500)
+
+  waitFor main()
 
-import tables, asyncnet, asyncdispatch, parseutils, uri, strutils
-import httpcore
+import std/[asyncnet, asyncdispatch, parseutils, uri, strutils]
+import std/httpcore
+from std/nativesockets import getLocalAddr, Domain, AF_INET, AF_INET6
+import std/private/since
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 export httpcore except parseHeader
 
@@ -51,7 +64,7 @@ type
     headers*: HttpHeaders
     protocol*: tuple[orig: string, major, minor: int]
     url*: Uri
-    hostname*: string ## The hostname of the client that made the request.
+    hostname*: string    ## The hostname of the client that made the request.
     body*: string
 
   AsyncHttpServer* = ref object
@@ -59,14 +72,25 @@ type
     reuseAddr: bool
     reusePort: bool
     maxBody: int ## The maximum content-length that will be read for the body.
+    maxFDs: int
+
+proc getPort*(self: AsyncHttpServer): Port {.since: (1, 5, 1).} =
+  ## Returns the port `self` was bound to.
+  ##
+  ## Useful for identifying what port `self` is bound to, if it
+  ## was chosen automatically, for example via `listen(Port(0))`.
+  runnableExamples:
+    from std/nativesockets import Port
+    let server = newAsyncHttpServer()
+    server.listen(Port(0))
+    assert server.getPort.uint16 > 0
+    server.close()
+  result = getLocalAddr(self.socket)[1]
 
 proc newAsyncHttpServer*(reuseAddr = true, reusePort = false,
                          maxBody = 8388608): AsyncHttpServer =
-  ## Creates a new ``AsyncHttpServer`` instance.
-  new result
-  result.reuseAddr = reuseAddr
-  result.reusePort = reusePort
-  result.maxBody = maxBody
+  ## Creates a new `AsyncHttpServer` instance.
+  result = AsyncHttpServer(reuseAddr: reuseAddr, reusePort: reusePort, maxBody: maxBody)
 
 proc addHeaders(msg: var string, headers: HttpHeaders) =
   for k, v in headers:
@@ -80,35 +104,40 @@ proc sendHeaders*(req: Request, headers: HttpHeaders): Future[void] =
 
 proc respond*(req: Request, code: HttpCode, content: string,
               headers: HttpHeaders = nil): Future[void] =
-  ## Responds to the request with the specified ``HttpCode``, headers and
+  ## Responds to the request with the specified `HttpCode`, headers and
   ## content.
   ##
   ## This procedure will **not** close the client socket.
   ##
-  ## Examples
-  ## --------
-  ## .. code-block::nim
-  ##    import json
-  ##    proc handler(req: Request) {.async.} =
-  ##      if req.url.path == "/hello-world":
-  ##        let msg = %* {"message": "Hello World"}
-  ##        let headers = newHttpHeaders([("Content-Type","application/json")])
-  ##        await req.respond(Http200, $msg, headers)
-  ##      else:
-  ##        await req.respond(Http404, "Not Found")
+  ## Example:
+  ##   ```Nim
+  ##   import std/json
+  ##   proc handler(req: Request) {.async.} =
+  ##     if req.url.path == "/hello-world":
+  ##       let msg = %* {"message": "Hello World"}
+  ##       let headers = newHttpHeaders([("Content-Type","application/json")])
+  ##       await req.respond(Http200, $msg, headers)
+  ##     else:
+  ##       await req.respond(Http404, "Not Found")
+  ##   ```
   var msg = "HTTP/1.1 " & $code & "\c\L"
 
   if headers != nil:
     msg.addHeaders(headers)
-  msg.add("Content-Length: ")
-  # this particular way saves allocations:
-  msg.add content.len
-  msg.add "\c\L\c\L"
+
+  # If the headers did not contain a Content-Length use our own
+  if headers.isNil() or not headers.hasKey("Content-Length"):
+    msg.add("Content-Length: ")
+    # this particular way saves allocations:
+    msg.addInt content.len
+    msg.add "\c\L"
+
+  msg.add "\c\L"
   msg.add(content)
   result = req.client.send(msg)
 
 proc respondError(req: Request, code: HttpCode): Future[void] =
-  ## Responds to the request with the specified ``HttpCode``.
+  ## Responds to the request with the specified `HttpCode`.
   let content = $code
   var msg = "HTTP/1.1 " & content & "\c\L"
 
@@ -129,25 +158,25 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
 proc sendStatus(client: AsyncSocket, status: string): Future[void] =
   client.send("HTTP/1.1 " & status & "\c\L\c\L")
 
-proc parseUppercaseMethod(name: string): HttpMethod =
-  result =
-    case name
-    of "GET": HttpGet
-    of "POST": HttpPost
-    of "HEAD": HttpHead
-    of "PUT": HttpPut
-    of "DELETE": HttpDelete
-    of "PATCH": HttpPatch
-    of "OPTIONS": HttpOptions
-    of "CONNECT": HttpConnect
-    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.} =
+func hasChunkedEncoding(request: Request): bool =
+  ## Searches for a chunked transfer encoding
+  const transferEncoding = "Transfer-Encoding"
+
+  if request.headers.hasKey(transferEncoding):
+    for encoding in seq[string](request.headers[transferEncoding]):
+      if "chunked" == encoding.strip:
+        # Returns true if it is both an HttpPost and has chunked encoding
+        return request.reqMethod == HttpPost
+  return false
+
+proc processRequest(
+  server: AsyncHttpServer,
+  req: FutureVar[Request],
+  client: AsyncSocket,
+  address: sink 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 =
@@ -158,7 +187,10 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
   # \n
   request.headers.clear()
   request.body = ""
-  request.hostname.shallowCopy(address)
+  when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
+    request.hostname = address
+  else:
+    request.hostname.shallowCopy(address)
   assert client != nil
   request.client = client
 
@@ -167,16 +199,16 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
   for i in 0..1:
     lineFut.mget().setLen(0)
     lineFut.clean()
-    await client.recvLineInto(lineFut, maxLength=maxLine) # TODO: Timeouts.
+    await client.recvLineInto(lineFut, maxLength = maxLine) # TODO: Timeouts.
 
     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
 
@@ -185,26 +217,34 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
   for linePart in lineFut.mget.split(' '):
     case i
     of 0:
-      try:
-        request.reqMethod = parseUppercaseMethod(linePart)
-      except ValueError:
+      case linePart
+      of "GET": request.reqMethod = HttpGet
+      of "POST": request.reqMethod = HttpPost
+      of "HEAD": request.reqMethod = HttpHead
+      of "PUT": request.reqMethod = HttpPut
+      of "DELETE": request.reqMethod = HttpDelete
+      of "PATCH": request.reqMethod = HttpPatch
+      of "OPTIONS": request.reqMethod = HttpOptions
+      of "CONNECT": request.reqMethod = HttpConnect
+      of "TRACE": request.reqMethod = HttpTrace
+      else:
         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
@@ -212,13 +252,13 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
     i = 0
     lineFut.mget.setLen(0)
     lineFut.clean()
-    await client.recvLineInto(lineFut, maxLength=maxLine)
+    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 +266,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 +282,64 @@ 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 hasChunkedEncoding(request):
+    # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding
+    var sizeOrData = 0
+    var bytesToRead = 0
+    request.body = ""
+
+    while true:
+      lineFut.mget.setLen(0)
+      lineFut.clean()
+
+      # The encoding format alternates between specifying a number of bytes to read
+      # and the data to be read, of the previously specified size
+      if sizeOrData mod 2 == 0:
+        # Expect a number of chars to read
+        await client.recvLineInto(lineFut, maxLength = maxLine)
+        try:
+          bytesToRead = lineFut.mget.parseHexInt
+        except ValueError:
+          # Malformed request
+          await request.respond(Http411, ("Invalid chunked transfer encoding - " &
+                                          "chunk data size must be hex encoded"))
+          return true
+      else:
+        if bytesToRead == 0:
+          # Done reading chunked data
+          break
+
+        # Read bytesToRead and add to body
+        let chunk = await client.recv(bytesToRead)
+        request.body.add(chunk)
+        # Skip \r\n (chunk terminating bytes per spec)
+        let separator = await client.recv(2)
+        if separator != "\r\n":
+          await request.respond(Http400, "Bad Request. Encoding separator must be \\r\\n")
+          return true
+
+      inc sizeOrData
   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
+
+  # The request has been served, from this point on returning `true` means the
+  # connection will not be closed and will be kept in the connection pool.
 
   # Persistent connections
   if (request.protocol == HttpVer11 and
@@ -270,10 +350,10 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
     # header states otherwise.
     # In HTTP 1.0 we assume that the connection should not be persistent.
     # Unless the connection header states otherwise.
-    discard
+    return true
   else:
     request.client.close()
-    return
+    return false
 
 proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string,
                    callback: proc (request: Request):
@@ -285,45 +365,76 @@ 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:
+      client.close()
+      break
 
-proc serve*(server: AsyncHttpServer, port: Port,
-            callback: proc (request: Request): Future[void] {.closure,gcsafe.},
-            address = "") {.async.} =
-  ## Starts the process of listening for incoming HTTP connections on the
-  ## specified address and port.
-  ##
-  ## When a request is made by a client the specified callback will be called.
-  server.socket = newAsyncSocket()
+const
+  nimMaxDescriptorsFallback* {.intdefine.} = 16_000 ## fallback value for \
+    ## when `maxDescriptors` is not available.
+    ## This can be set on the command line during compilation
+    ## via `-d:nimMaxDescriptorsFallback=N`
+
+proc listen*(server: AsyncHttpServer; port: Port; address = ""; domain = AF_INET) =
+  ## Listen to the given port and address.
+  when declared(maxDescriptors):
+    server.maxFDs = try: maxDescriptors() except: nimMaxDescriptorsFallback
+  else:
+    server.maxFDs = nimMaxDescriptorsFallback
+  server.socket = newAsyncSocket(domain)
   if server.reuseAddr:
     server.socket.setSockOpt(OptReuseAddr, true)
-  if server.reusePort:
-    server.socket.setSockOpt(OptReusePort, true)
+  when not defined(nuttx):
+    if server.reusePort:
+      server.socket.setSockOpt(OptReusePort, true)
   server.socket.bindAddr(port, address)
   server.socket.listen()
 
+proc shouldAcceptRequest*(server: AsyncHttpServer;
+                          assumedDescriptorsPerRequest = 5): bool {.inline.} =
+  ## Returns true if the process's current number of opened file
+  ## descriptors is still within the maximum limit and so it's reasonable to
+  ## accept yet another request.
+  result = assumedDescriptorsPerRequest < 0 or
+    (activeDescriptors() + assumedDescriptorsPerRequest < server.maxFDs)
+
+proc acceptRequest*(server: AsyncHttpServer,
+            callback: proc (request: Request): Future[void] {.closure, gcsafe.}) {.async.} =
+  ## Accepts a single request. Write an explicit loop around this proc so that
+  ## errors can be handled properly.
+  var (address, client) = await server.socket.acceptAddr()
+  asyncCheck processClient(server, client, address, callback)
+
+proc serve*(server: AsyncHttpServer, port: Port,
+            callback: proc (request: Request): Future[void] {.closure, gcsafe.},
+            address = "";
+            assumedDescriptorsPerRequest = -1;
+            domain = AF_INET) {.async.} =
+  ## Starts the process of listening for incoming HTTP connections on the
+  ## specified address and port.
+  ##
+  ## When a request is made by a client the specified callback will be called.
+  ##
+  ## If `assumedDescriptorsPerRequest` is 0 or greater the server cares about
+  ## the process's maximum file descriptor limit. It then ensures that the
+  ## process still has the resources for `assumedDescriptorsPerRequest`
+  ## file descriptors before accepting a connection.
+  ##
+  ## You should prefer to call `acceptRequest` instead with a custom server
+  ## loop so that you're in control over the error handling and logging.
+  listen server, port, address, domain
   while true:
-    # TODO: Causes compiler crash.
-    #var (address, client) = await server.socket.acceptAddr()
-    var fut = await server.socket.acceptAddr()
-    asyncCheck processClient(server, fut.client, fut.address, callback)
+    if shouldAcceptRequest(server, assumedDescriptorsPerRequest):
+      var (address, client) = await server.socket.acceptAddr()
+      asyncCheck processClient(server, client, address, callback)
+    else:
+      poll()
     #echo(f.isNil)
     #echo(f.repr)
 
 proc close*(server: AsyncHttpServer) =
   ## Terminates the async http server instance.
   server.socket.close()
-
-when not defined(testing) and isMainModule:
-  proc main =
-    var server = newAsyncHttpServer()
-    proc cb(req: Request) {.async.} =
-      #echo(req.reqMethod, " ", req.url)
-      #echo(req.headers)
-      let headers = {"Date": "Tue, 29 Apr 2014 23:40:08 GMT",
-          "Content-type": "text/plain; charset=utf-8"}
-      await req.respond(Http200, "Hello World", headers.newHttpHeaders())
-
-    asyncCheck server.serve(Port(5555), cb)
-    runForever()
-  main()
diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim
index 4665ad25f..d4e72c28a 100644
--- a/lib/pure/asyncmacro.nim
+++ b/lib/pure/asyncmacro.nim
@@ -7,30 +7,26 @@
 #    distribution, for details about the copyright.
 #
 
-## AsyncMacro
-## *************
-## `asyncdispatch` module depends on the `asyncmacro` module to work properly.
+## Implements the `async` and `multisync` macros for `asyncdispatch`.
 
-import macros, strutils, asyncfutures
+import std/[macros, strutils, asyncfutures]
 
-proc skipUntilStmtList(node: NimNode): NimNode {.compileTime.} =
-  # Skips a nest of StmtList's.
-  result = node
-  if node[0].kind == nnkStmtList:
-    result = skipUntilStmtList(node[0])
+type
+  Context = ref object
+    inTry: int
+    hasRet: bool
 
-proc skipStmtList(node: NimNode): NimNode {.compileTime.} =
-  result = node
-  if node[0].kind == nnkStmtList:
-    result = node[0]
+# TODO: Ref https://github.com/nim-lang/Nim/issues/5617
+# TODO: Add more line infos
+proc newCallWithLineInfo(fromNode: NimNode; theProc: NimNode, args: varargs[NimNode]): NimNode =
+  result = newCall(theProc, args)
+  result.copyLineInfo(fromNode)
 
 template createCb(retFutureSym, iteratorNameSym,
                   strName, identName, futureVarCompletions: untyped) =
   bind finished
-
   var nameIterVar = iteratorNameSym
-  #{.push stackTrace: off.}
-  proc identName {.closure.} =
+  proc identName {.closure, stackTrace: off.} =
     try:
       if not nameIterVar.finished:
         var next = nameIterVar()
@@ -42,74 +38,37 @@ template createCb(retFutureSym, iteratorNameSym,
 
         if next == nil:
           if not retFutureSym.finished:
-            let msg = "Async procedure ($1) yielded `nil`, are you await'ing a " &
-                    "`nil` Future?"
-            raise newException(AssertionError, msg % strName)
+            let msg = "Async procedure ($1) yielded `nil`, are you await'ing a `nil` Future?"
+            raise newException(AssertionDefect, msg % strName)
         else:
           {.gcsafe.}:
-            {.push hint[ConvFromXtoItselfNotNeeded]: off.}
-            next.callback = (proc() {.closure, gcsafe.})(identName)
-            {.pop.}
+            next.addCallback cast[proc() {.closure, gcsafe.}](identName)
     except:
       futureVarCompletions
-
       if retFutureSym.finished:
         # Take a look at tasyncexceptions for the bug which this fixes.
         # That test explains it better than I can here.
         raise
       else:
         retFutureSym.fail(getCurrentException())
-
   identName()
-  #{.pop.}
-
-template useVar(result: var NimNode, futureVarNode: NimNode, valueReceiver,
-                rootReceiver: untyped, fromNode: NimNode) =
-  ## Params:
-  ##    futureVarNode: The NimNode which is a symbol identifying the Future[T]
-  ##                   variable to yield.
-  ##    fromNode: Used for better debug information (to give context).
-  ##    valueReceiver: The node which defines an expression that retrieves the
-  ##                   future's value.
-  ##
-  ##    rootReceiver: ??? TODO
-  # -> yield future<x>
-  result.add newNimNode(nnkYieldStmt, fromNode).add(futureVarNode)
-  # -> future<x>.read
-  valueReceiver = newDotExpr(futureVarNode, newIdentNode("read"))
-  result.add rootReceiver
-
-template createVar(result: var NimNode, futSymName: string,
-                   asyncProc: NimNode,
-                   valueReceiver, rootReceiver: untyped,
-                   fromNode: NimNode) =
-  result = newNimNode(nnkStmtList, fromNode)
-  var futSym = genSym(nskVar, "future")
-  result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y
-  useVar(result, futSym, valueReceiver, rootReceiver, fromNode)
 
-proc createFutureVarCompletions(futureVarIdents: seq[NimNode],
-    fromNode: NimNode): NimNode {.compileTime.} =
+proc createFutureVarCompletions(futureVarIdents: seq[NimNode], fromNode: NimNode): NimNode =
   result = newNimNode(nnkStmtList, fromNode)
   # Add calls to complete each FutureVar parameter.
   for ident in futureVarIdents:
     # Only complete them if they have not been completed already by the user.
-    # TODO: Once https://github.com/nim-lang/Nim/issues/5617 is fixed.
-    # TODO: Add line info to the complete() call!
     # In the meantime, this was really useful for debugging :)
     #result.add(newCall(newIdentNode("echo"), newStrLitNode(fromNode.lineinfo)))
     result.add newIfStmt(
       (
         newCall(newIdentNode("not"),
                 newDotExpr(ident, newIdentNode("finished"))),
-        newCall(newIdentNode("complete"), ident)
+        newCallWithLineInfo(fromNode, newIdentNode("complete"), ident)
       )
     )
 
-proc processBody(node, retFutureSym: NimNode,
-                 subTypeIsVoid: bool,
-                 futureVarIdents: seq[NimNode]): NimNode {.compileTime.} =
-  #echo(node.treeRepr)
+proc processBody(ctx: Context; node, needsCompletionSym, retFutureSym: NimNode, futureVarIdents: seq[NimNode]): NimNode =
   result = node
   case node.kind
   of nnkReturnStmt:
@@ -118,130 +77,157 @@ proc processBody(node, retFutureSym: NimNode,
     # As I've painfully found out, the order here really DOES matter.
     result.add createFutureVarCompletions(futureVarIdents, node)
 
+    ctx.hasRet = true
     if node[0].kind == nnkEmpty:
-      if not subTypeIsVoid:
-        result.add newCall(newIdentNode("complete"), retFutureSym,
-            newIdentNode("result"))
+      if ctx.inTry == 0:
+        result.add newCallWithLineInfo(node, newIdentNode("complete"), retFutureSym, newIdentNode("result"))
       else:
-        result.add newCall(newIdentNode("complete"), retFutureSym)
+        result.add newAssignment(needsCompletionSym, newLit(true))
     else:
-      let x = node[0].processBody(retFutureSym, subTypeIsVoid,
-                                  futureVarIdents)
+      let x = processBody(ctx, node[0], needsCompletionSym, retFutureSym, futureVarIdents)
       if x.kind == nnkYieldStmt: result.add x
+      elif ctx.inTry == 0:
+        result.add newCallWithLineInfo(node, newIdentNode("complete"), retFutureSym, x)
       else:
-        result.add newCall(newIdentNode("complete"), retFutureSym, x)
+        result.add newAssignment(newIdentNode("result"), x)
+        result.add newAssignment(needsCompletionSym, newLit(true))
 
     result.add newNimNode(nnkReturnStmt, node).add(newNilLit())
     return # Don't process the children of this return stmt
-  of nnkCommand, nnkCall:
-    if node[0].kind == nnkIdent and node[0].eqIdent("await"):
-      case node[1].kind
-      of nnkIdent, nnkInfix, nnkDotExpr, nnkCall, nnkCommand:
-        # await x
-        # await x or y
-        # await foo(p, x)
-        # await foo p, x
-        var futureValue: NimNode
-        result.createVar("future" & $node[1][0].toStrLit, node[1], futureValue,
-                  futureValue, node)
-      else:
-        error("Invalid node kind in 'await', got: " & $node[1].kind)
-    elif node.len > 1 and node[1].kind == nnkCommand and
-         node[1][0].kind == nnkIdent and node[1][0].eqIdent("await"):
-      # foo await x
-      var newCommand = node
-      result.createVar("future" & $node[0].toStrLit, node[1][1], newCommand[1],
-                newCommand, node)
-
-  of nnkVarSection, nnkLetSection:
-    case node[0][2].kind
-    of nnkCommand:
-      if node[0][2][0].kind == nnkIdent and node[0][2][0].eqIdent("await"):
-        # var x = await y
-        var newVarSection = node # TODO: Should this use copyNimNode?
-        result.createVar("future" & node[0][0].strVal, node[0][2][1],
-          newVarSection[0][2], newVarSection, node)
-    else: discard
-  of nnkAsgn:
-    case node[1].kind
-    of nnkCommand:
-      if node[1][0].eqIdent("await"):
-        # x = await y
-        var newAsgn = node
-        result.createVar("future" & $node[0].toStrLit, node[1][1], newAsgn[1], newAsgn, node)
-    else: discard
-  of nnkDiscardStmt:
-    # discard await x
-    if node[0].kind == nnkCommand and node[0][0].kind == nnkIdent and
-          node[0][0].eqIdent("await"):
-      var newDiscard = node
-      result.createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1],
-                newDiscard[0], newDiscard, node)
-  else: discard
-
-  for i in 0 ..< result.len:
-    result[i] = processBody(result[i], retFutureSym, subTypeIsVoid,
-                            futureVarIdents)
-
-proc getName(node: NimNode): string {.compileTime.} =
+  of RoutineNodes-{nnkTemplateDef}:
+    # skip all the nested procedure definitions
+    return
+  of nnkTryStmt:
+    if result[^1].kind == nnkFinally:
+      inc ctx.inTry
+      result[0] = processBody(ctx, result[0], needsCompletionSym, retFutureSym, futureVarIdents)
+      dec ctx.inTry
+      for i in 1 ..< result.len:
+        result[i] = processBody(ctx, result[i], needsCompletionSym, retFutureSym, futureVarIdents)
+      if ctx.inTry == 0 and ctx.hasRet:
+        let finallyNode = copyNimNode(result[^1])
+        let stmtNode = newNimNode(nnkStmtList)
+        for child in result[^1]:
+          stmtNode.add child
+        stmtNode.add newIfStmt(
+          ( needsCompletionSym,
+            newCallWithLineInfo(node, newIdentNode("complete"), retFutureSym,
+            newIdentNode("result")
+            )
+          )
+        )
+        finallyNode.add stmtNode
+        result[^1] = finallyNode
+    else:
+      for i in 0 ..< result.len:
+        result[i] = processBody(ctx, result[i], needsCompletionSym, retFutureSym, futureVarIdents)
+  else:
+    for i in 0 ..< result.len:
+      result[i] = processBody(ctx, result[i], needsCompletionSym, retFutureSym, futureVarIdents)
+
+  # echo result.repr
+
+proc getName(node: NimNode): string =
   case node.kind
   of nnkPostfix:
     return node[1].strVal
-  of nnkIdent:
+  of nnkIdent, nnkSym:
     return node.strVal
   of nnkEmpty:
     return "anonymous"
   else:
-    error("Unknown name.")
+    error("Unknown name.", node)
 
-proc getFutureVarIdents(params: NimNode): seq[NimNode] {.compileTime.} =
+proc getFutureVarIdents(params: NimNode): seq[NimNode] =
   result = @[]
   for i in 1 ..< len(params):
     expectKind(params[i], nnkIdentDefs)
     if params[i][1].kind == nnkBracketExpr and
-       params[i][1][0].eqIdent("futurevar"):
+       params[i][1][0].eqIdent(FutureVar.astToStr):
+      ## eqIdent: first char is case sensitive!!!
       result.add(params[i][0])
 
 proc isInvalidReturnType(typeName: string): bool =
   return typeName notin ["Future"] #, "FutureStream"]
 
-proc verifyReturnType(typeName: string) {.compileTime.} =
+proc verifyReturnType(typeName: string, node: NimNode = nil) =
   if typeName.isInvalidReturnType:
     error("Expected return type of 'Future' got '$1'" %
-          typeName)
+          typeName, node)
+
+template await*(f: typed): untyped {.used.} =
+  static:
+    error "await expects Future[T], got " & $typeof(f)
+
+template await*[T](f: Future[T]): auto {.used.} =
+  when not defined(nimHasTemplateRedefinitionPragma):
+    {.pragma: redefine.}
+  template yieldFuture {.redefine.} = yield FutureBase()
 
-proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
+  when compiles(yieldFuture):
+    var internalTmpFuture: FutureBase = f
+    yield internalTmpFuture
+    (cast[typeof(f)](internalTmpFuture)).read()
+  else:
+    macro errorAsync(futureError: Future[T]) =
+      error(
+        "Can only 'await' inside a proc marked as 'async'. Use " &
+        "'waitFor' when calling an 'async' proc in a non-async scope instead",
+        futureError)
+    errorAsync(f)
+
+proc asyncSingleProc(prc: NimNode): NimNode =
   ## This macro transforms a single procedure into a closure iterator.
-  ## The ``async`` macro supports a stmtList holding multiple async procedures.
+  ## The `async` macro supports a stmtList holding multiple async procedures.
+  if prc.kind == nnkProcTy:
+    result = prc
+    if prc[0][0].kind == nnkEmpty:
+      result[0][0] = quote do: Future[void]
+    return result
+
+  if prc.kind in RoutineNodes and prc.name.kind != nnkEmpty:
+    # Only non anonymous functions need/can have stack trace disabled
+    prc.addPragma(nnkExprColonExpr.newTree(ident"stackTrace", ident"off"))
+
   if prc.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}:
-      error("Cannot transform this node kind into an async proc." &
-            " proc/method definition or lambda node expected.")
+    error("Cannot transform this node kind into an async proc." &
+          " proc/method definition or lambda node expected.", prc)
+
+  if prc[4].kind != nnkEmpty:
+    for prag in prc[4]:
+      if prag.eqIdent("discardable"):
+        error("Cannot make async proc discardable. Futures have to be " &
+          "checked with `asyncCheck` instead of discarded", prag)
 
   let prcName = prc.name.getName
 
-  let returnType = prc.params[0]
+  var returnType = prc.params[0]
   var baseType: NimNode
+  if returnType.kind in nnkCallKinds and returnType[0].eqIdent("owned") and
+      returnType.len == 2:
+    returnType = returnType[1]
   # Verify that the return type is a Future[T]
   if returnType.kind == nnkBracketExpr:
     let fut = repr(returnType[0])
-    verifyReturnType(fut)
+    verifyReturnType(fut, returnType[0])
     baseType = returnType[1]
   elif returnType.kind in nnkCallKinds and returnType[0].eqIdent("[]"):
     let fut = repr(returnType[1])
-    verifyReturnType(fut)
+    verifyReturnType(fut, returnType[0])
     baseType = returnType[2]
   elif returnType.kind == nnkEmpty:
     baseType = returnType
   else:
-    verifyReturnType(repr(returnType))
-
-  let subtypeIsVoid = returnType.kind == nnkEmpty or
-        (baseType.kind == nnkIdent and returnType[1].eqIdent("void"))
+    verifyReturnType(repr(returnType), returnType)
 
   let futureVarIdents = getFutureVarIdents(prc.params)
-
   var outerProcBody = newNimNode(nnkStmtList, prc.body)
 
+  # Extract the documentation comment from the original procedure declaration.
+  # Note that we're not removing it from the body in order not to make this
+  # transformation even more complex.
+  let body2 = extractDocCommentsAndRunnables(prc.body)
+
   # -> var retFuture = newFuture[T]()
   var retFutureSym = genSym(nskVar, "retFuture")
   var subRetType =
@@ -261,35 +247,38 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
   # ->   {.pop.}
   # ->   <proc_body>
   # ->   complete(retFuture, result)
-  var iteratorNameSym = genSym(nskIterator, $prcName & "Iter")
-  var procBody = prc.body.processBody(retFutureSym, subtypeIsVoid,
-                                    futureVarIdents)
+  var iteratorNameSym = genSym(nskIterator, $prcName & " (Async)")
+  var needsCompletionSym = genSym(nskVar, "needsCompletion")
+  var ctx = Context()
+  var procBody = processBody(ctx, prc.body, needsCompletionSym, retFutureSym, futureVarIdents)
   # don't do anything with forward bodies (empty)
   if procBody.kind != nnkEmpty:
+    # fix #13899, defer should not escape its original scope
+    let blockStmt = newStmtList(newTree(nnkBlockStmt, newEmptyNode(), procBody))
+    procBody = newStmtList()
+    let resultIdent = ident"result"
+    procBody.add quote do:
+      # Check whether there is an implicit return
+      when typeof(`blockStmt`) is void:
+        `blockStmt`
+      else:
+        `resultIdent` = `blockStmt`
     procBody.add(createFutureVarCompletions(futureVarIdents, nil))
+    procBody.insert(0): quote do:
+      {.push warning[resultshadowed]: off.}
+      when `subRetType` isnot void:
+        var `resultIdent`: `subRetType`
+      else:
+        var `resultIdent`: Future[void]
+      {.pop.}
 
-    if not subtypeIsVoid:
-      procBody.insert(0, newNimNode(nnkPragma).add(newIdentNode("push"),
-        newNimNode(nnkExprColonExpr).add(newNimNode(nnkBracketExpr).add(
-          newIdentNode("warning"), newIdentNode("resultshadowed")),
-        newIdentNode("off")))) # -> {.push warning[resultshadowed]: off.}
-
-      procBody.insert(1, newNimNode(nnkVarSection, prc.body).add(
-        newIdentDefs(newIdentNode("result"), baseType))) # -> var result: T
-
-      procBody.insert(2, newNimNode(nnkPragma).add(
-        newIdentNode("pop"))) # -> {.pop.})
-
-      procBody.add(
-        newCall(newIdentNode("complete"),
-          retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result)
-    else:
-      # -> complete(retFuture)
-      procBody.add(newCall(newIdentNode("complete"), retFutureSym))
+      var `needsCompletionSym` = false
+    procBody.add quote do:
+      complete(`retFutureSym`, `resultIdent`)
 
-    var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")],
+    var closureIterator = newProc(iteratorNameSym, [quote do: owned(FutureBase)],
                                   procBody, nnkIteratorDef)
-    closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom=prc.body)
+    closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom = prc.body)
     closureIterator.addPragma(newIdentNode("closure"))
 
     # If proc has an explicit gcsafe pragma, we add it to iterator as well.
@@ -298,94 +287,48 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
     outerProcBody.add(closureIterator)
 
     # -> createCb(retFuture)
-    # NOTE: The "_continue" suffix is checked for in asyncfutures.nim to produce
+    # NOTE: The NimAsyncContinueSuffix is checked for in asyncfutures.nim to produce
     # friendlier stack traces:
-    var cbName = genSym(nskProc, prcName & "_continue")
+    var cbName = genSym(nskProc, prcName & NimAsyncContinueSuffix)
     var procCb = getAst createCb(retFutureSym, iteratorNameSym,
-                         newStrLitNode(prcName),
-                         cbName,
-                         createFutureVarCompletions(futureVarIdents, nil))
+                          newStrLitNode(prcName),
+                          cbName,
+                          createFutureVarCompletions(futureVarIdents, nil)
+                        )
     outerProcBody.add procCb
 
     # -> return retFuture
     outerProcBody.add newNimNode(nnkReturnStmt, prc.body[^1]).add(retFutureSym)
 
   result = prc
+  # Add discardable pragma.
+  if returnType.kind == nnkEmpty:
+    # xxx consider removing `owned`? it's inconsistent with non-void case
+    result.params[0] = quote do: owned(Future[void])
 
-  if subtypeIsVoid:
-    # Add discardable pragma.
-    if returnType.kind == nnkEmpty:
-      # Add Future[void]
-      result.params[0] = parseExpr("Future[void]")
+  # based on the yglukhov's patch to chronos: https://github.com/status-im/nim-chronos/pull/47
   if procBody.kind != nnkEmpty:
-    result.body = outerProcBody
-  #echo(treeRepr(result))
-  #if prcName == "recvLineInto":
-  #  echo(toStrLit(result))
+    body2.add quote do:
+      `outerProcBody`
+    result.body = body2
 
 macro async*(prc: untyped): untyped =
   ## Macro which processes async procedures into the appropriate
   ## iterators and yield statements.
   if prc.kind == nnkStmtList:
+    result = newStmtList()
     for oneProc in prc:
-      result = newStmtList()
       result.add asyncSingleProc(oneProc)
   else:
     result = asyncSingleProc(prc)
   when defined(nimDumpAsync):
     echo repr result
 
-
-# Multisync
-proc emptyNoop[T](x: T): T =
-  # The ``await``s are replaced by a call to this for simplicity.
-  when T isnot void:
-    return x
-
-proc stripAwait(node: NimNode): NimNode =
-  ## Strips out all ``await`` commands from a procedure body, replaces them
-  ## with ``emptyNoop`` for simplicity.
-  result = node
-
-  let emptyNoopSym = bindSym("emptyNoop")
-
-  case node.kind
-  of nnkCommand, nnkCall:
-    if node[0].kind == nnkIdent and node[0].eqIdent("await"):
-      node[0] = emptyNoopSym
-    elif node.len > 1 and node[1].kind == nnkCommand and
-         node[1][0].kind == nnkIdent and node[1][0].eqIdent("await"):
-      # foo await x
-      node[1][0] = emptyNoopSym
-  of nnkVarSection, nnkLetSection:
-    case node[0][2].kind
-    of nnkCommand:
-      if node[0][2][0].kind == nnkIdent and node[0][2][0].eqIdent("await"):
-        # var x = await y
-        node[0][2][0] = emptyNoopSym
-    else: discard
-  of nnkAsgn:
-    case node[1].kind
-    of nnkCommand:
-      if node[1][0].eqIdent("await"):
-        # x = await y
-        node[1][0] = emptyNoopSym
-    else: discard
-  of nnkDiscardStmt:
-    # discard await x
-    if node[0].kind == nnkCommand and node[0][0].kind == nnkIdent and
-          node[0][0].eqIdent("await"):
-      node[0][0] = emptyNoopSym
-  else: discard
-
-  for i in 0 ..< result.len:
-    result[i] = stripAwait(result[i])
-
 proc splitParamType(paramType: NimNode, async: bool): NimNode =
   result = paramType
   if paramType.kind == nnkInfix and paramType[0].strVal in ["|", "or"]:
-    let firstAsync = "async" in paramType[1].strVal.normalize
-    let secondAsync = "async" in paramType[2].strVal.normalize
+    let firstAsync = "async" in paramType[1].toStrLit().strVal.normalize
+    let secondAsync = "async" in paramType[2].toStrLit().strVal.normalize
 
     if firstAsync:
       result = paramType[if async: 1 else: 2]
@@ -397,39 +340,43 @@ proc stripReturnType(returnType: NimNode): NimNode =
   result = returnType
   if returnType.kind == nnkBracketExpr:
     let fut = repr(returnType[0])
-    verifyReturnType(fut)
+    verifyReturnType(fut, returnType)
     result = returnType[1]
 
 proc splitProc(prc: NimNode): (NimNode, NimNode) =
   ## Takes a procedure definition which takes a generic union of arguments,
   ## for example: proc (socket: Socket | AsyncSocket).
-  ## It transforms them so that ``proc (socket: Socket)`` and
-  ## ``proc (socket: AsyncSocket)`` are returned.
+  ## It transforms them so that `proc (socket: Socket)` and
+  ## `proc (socket: AsyncSocket)` are returned.
 
   result[0] = prc.copyNimTree()
   # Retrieve the `T` inside `Future[T]`.
   let returnType = stripReturnType(result[0][3][0])
-  result[0][3][0] = splitParamType(returnType, async=false)
+  result[0][3][0] = splitParamType(returnType, async = false)
   for i in 1 ..< result[0][3].len:
     # Sync proc (0) -> FormalParams (3) -> IdentDefs, the parameter (i) ->
     # parameter type (1).
     result[0][3][i][1] = splitParamType(result[0][3][i][1], async=false)
-  result[0][6] = stripAwait(result[0][6])
+  var multisyncAwait = quote:
+    template await(value: typed): untyped =
+      value
+
+  result[0][^1] = nnkStmtList.newTree(multisyncAwait, result[0][^1])
 
   result[1] = prc.copyNimTree()
   if result[1][3][0].kind == nnkBracketExpr:
-    result[1][3][0][1] = splitParamType(result[1][3][0][1], async=true)
+    result[1][3][0][1] = splitParamType(result[1][3][0][1], async = true)
   for i in 1 ..< result[1][3].len:
     # Async proc (1) -> FormalParams (3) -> IdentDefs, the parameter (i) ->
     # parameter type (1).
-    result[1][3][i][1] = splitParamType(result[1][3][i][1], async=true)
+    result[1][3][i][1] = splitParamType(result[1][3][i][1], async = true)
 
 macro multisync*(prc: untyped): untyped =
   ## Macro which processes async procedures into both asynchronous and
   ## synchronous procedures.
   ##
-  ## The generated async procedures use the ``async`` macro, whereas the
-  ## generated synchronous procedures simply strip off the ``await`` calls.
+  ## The generated async procedures use the `async` macro, whereas the
+  ## generated synchronous procedures simply strip off the `await` calls.
   let (sync, asyncPrc) = splitProc(prc)
   result = newStmtList()
   result.add(asyncSingleProc(asyncPrc))
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index e7552e3e3..ee07e599e 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -8,27 +8,27 @@
 #
 
 ## This module implements a high-level asynchronous sockets API based on the
-## asynchronous dispatcher defined in the ``asyncdispatch`` module.
+## asynchronous dispatcher defined in the `asyncdispatch` module.
 ##
 ## Asynchronous IO in Nim
-## ----------------------
+## ======================
 ##
 ## Async IO in Nim consists of multiple layers (from highest to lowest):
 ##
-## * ``asyncnet`` module
+## * `asyncnet` module
 ##
 ## * Async await
 ##
-## * ``asyncdispatch`` module (event loop)
+## * `asyncdispatch` module (event loop)
 ##
-## * ``selectors`` module
+## * `selectors` module
 ##
 ## Each builds on top of the layers below it. The selectors module is an
-## abstraction for the various system ``select()`` mechanisms such as epoll or
+## abstraction for the various system `select()` mechanisms such as epoll or
 ## kqueue. If you wish you can use it directly, and some people have done so
 ## `successfully <http://goran.krampe.se/2014/10/25/nim-socketserver/>`_.
 ## But you must be aware that on Windows it only supports
-## ``select()``.
+## `select()`.
 ##
 ## The async dispatcher implements the proactor pattern and also has an
 ## implementation of IOCP. It implements the proactor pattern for other
@@ -45,29 +45,28 @@
 ## layers interchangeably (as long as you only care about non-Windows
 ## platforms).
 ##
-## For most applications using ``asyncnet`` is the way to go as it builds
+## For most applications using `asyncnet` is the way to go as it builds
 ## over all the layers, providing some extra features such as buffering.
 ##
 ## SSL
-## ----
+## ===
 ##
-## SSL can be enabled by compiling with the ``-d:ssl`` flag.
+## SSL can be enabled by compiling with the `-d:ssl` flag.
 ##
-## You must create a new SSL context with the ``newContext`` function defined
-## in the ``net`` module. You may then call ``wrapSocket`` on your socket using
+## You must create a new SSL context with the `newContext` function defined
+## in the `net` module. You may then call `wrapSocket` on your socket using
 ## the newly created SSL context to get an SSL socket.
 ##
 ## Examples
-## --------
+## ========
 ##
 ## Chat server
-## ^^^^^^^^^^^
+## -----------
 ##
 ## The following example demonstrates a simple chat server.
 ##
-## .. code-block::nim
-##
-##   import asyncnet, asyncdispatch
+##   ```Nim
+##   import std/[asyncnet, asyncdispatch]
 ##
 ##   var clients {.threadvar.}: seq[AsyncSocket]
 ##
@@ -93,61 +92,69 @@
 ##
 ##   asyncCheck serve()
 ##   runForever()
-##
+##   ```
+
+import std/private/since
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
 
-import asyncdispatch
-import nativesockets
-import net
-import os
+import std/[asyncdispatch, nativesockets, net, os]
 
 export SOBool
 
 # TODO: Remove duplication introduced by PR #4683.
 
 const defineSsl = defined(ssl) or defined(nimdoc)
+const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) or
+    defined(nuttx)
 
 when defineSsl:
-  import openssl
+  import std/openssl
 
 type
   # TODO: I would prefer to just do:
   # AsyncSocket* {.borrow: `.`.} = distinct Socket. But that doesn't work.
-  AsyncSocketDesc  = object
+  AsyncSocketDesc = object
     fd: SocketHandle
-    closed: bool ## determines whether this socket has been closed
-    case isBuffered: bool ## determines whether this socket is buffered.
-    of true:
-      buffer: array[0..BufferSize, char]
-      currPos: int # current index in buffer
-      bufLen: int # current length of buffer
-    of false: nil
-    case isSsl: bool
-    of true:
-      when defineSsl:
-        sslHandle: SslPtr
-        sslContext: SslContext
-        bioIn: BIO
-        bioOut: BIO
-    of false: nil
+    closed: bool     ## determines whether this socket has been closed
+    isBuffered: bool ## determines whether this socket is buffered.
+    buffer: array[0..BufferSize, char]
+    currPos: int     # current index in buffer
+    bufLen: int      # current length of buffer
+    isSsl: bool
+    when defineSsl:
+      sslHandle: SslPtr
+      sslContext: SslContext
+      bioIn: BIO
+      bioOut: BIO
+      sslNoShutdown: bool
     domain: Domain
     sockType: SockType
     protocol: Protocol
   AsyncSocket* = ref AsyncSocketDesc
 
 proc newAsyncSocket*(fd: AsyncFD, domain: Domain = AF_INET,
-    sockType: SockType = SOCK_STREAM,
-    protocol: Protocol = IPPROTO_TCP, buffered = true): AsyncSocket =
-  ## Creates a new ``AsyncSocket`` based on the supplied params.
+                     sockType: SockType = SOCK_STREAM,
+                     protocol: Protocol = IPPROTO_TCP,
+                     buffered = true,
+                     inheritable = defined(nimInheritHandles)): owned(AsyncSocket) =
+  ## Creates a new `AsyncSocket` based on the supplied params.
+  ##
+  ## The supplied `fd`'s non-blocking state will be enabled implicitly.
   ##
-  ## The supplied ``fd``'s non-blocking state will be enabled implicitly.
+  ## If `inheritable` is false (the default), the supplied `fd` will not
+  ## be inheritable by child processes.
   ##
-  ## **Note**: This procedure will **NOT** register ``fd`` with the global
+  ## **Note**: This procedure will **NOT** register `fd` with the global
   ## async dispatcher. You need to do this manually. If you have used
-  ## ``newAsyncNativeSocket`` to create ``fd`` then it's already registered.
+  ## `newAsyncNativeSocket` to create `fd` then it's already registered.
   assert fd != osInvalidSocket.AsyncFD
   new(result)
   result.fd = fd.SocketHandle
   fd.SocketHandle.setBlocking(false)
+  if not fd.SocketHandle.setInheritable(inheritable):
+    raiseOSError(osLastError())
   result.isBuffered = buffered
   result.domain = domain
   result.sockType = sockType
@@ -156,32 +163,54 @@ proc newAsyncSocket*(fd: AsyncFD, domain: Domain = AF_INET,
     result.currPos = 0
 
 proc newAsyncSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
-    protocol: Protocol = IPPROTO_TCP, buffered = true): AsyncSocket =
+                     protocol: Protocol = IPPROTO_TCP, buffered = true,
+                     inheritable = defined(nimInheritHandles)): owned(AsyncSocket) =
   ## Creates a new asynchronous socket.
   ##
   ## This procedure will also create a brand new file descriptor for
   ## this socket.
-  let fd = createAsyncNativeSocket(domain, sockType, protocol)
+  ##
+  ## If `inheritable` is false (the default), the new file descriptor will not
+  ## be inheritable by child processes.
+  let fd = createAsyncNativeSocket(domain, sockType, protocol, inheritable)
   if fd.SocketHandle == osInvalidSocket:
     raiseOSError(osLastError())
-  result = newAsyncSocket(fd, domain, sockType, protocol, buffered)
+  result = newAsyncSocket(fd, domain, sockType, protocol, buffered, inheritable)
+
+proc getLocalAddr*(socket: AsyncSocket): (string, Port) =
+  ## Get the socket's local address and port number.
+  ##
+  ## This is high-level interface for `getsockname`:idx:.
+  getLocalAddr(socket.fd, socket.domain)
+
+when not useNimNetLite:
+  proc getPeerAddr*(socket: AsyncSocket): (string, Port) =
+    ## Get the socket's peer address and port number.
+    ##
+    ## This is high-level interface for `getpeername`:idx:.
+    getPeerAddr(socket.fd, socket.domain)
 
 proc newAsyncSocket*(domain, sockType, protocol: cint,
-    buffered = true): AsyncSocket =
+                     buffered = true,
+                     inheritable = defined(nimInheritHandles)): owned(AsyncSocket) =
   ## Creates a new asynchronous socket.
   ##
   ## This procedure will also create a brand new file descriptor for
   ## this socket.
-  let fd = createAsyncNativeSocket(domain, sockType, protocol)
+  ##
+  ## If `inheritable` is false (the default), the new file descriptor will not
+  ## be inheritable by child processes.
+  let fd = createAsyncNativeSocket(domain, sockType, protocol, inheritable)
   if fd.SocketHandle == osInvalidSocket:
     raiseOSError(osLastError())
   result = newAsyncSocket(fd, Domain(domain), SockType(sockType),
-                          Protocol(protocol), buffered)
+                          Protocol(protocol), buffered, inheritable)
 
 when defineSsl:
-  proc getSslError(handle: SslPtr, err: cint): cint =
+  proc getSslError(socket: AsyncSocket, err: cint): cint =
+    assert socket.isSsl
     assert err < 0
-    var ret = SSLGetError(handle, err.cint)
+    var ret = SSL_get_error(socket.sslHandle, err.cint)
     case ret
     of SSL_ERROR_ZERO_RETURN:
       raiseSSLError("TLS/SSL connection failed to initiate, socket closed prematurely.")
@@ -192,6 +221,7 @@ when defineSsl:
     of SSL_ERROR_WANT_X509_LOOKUP:
       raiseSSLError("Function for x509 lookup has been called.")
     of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
+      socket.sslNoShutdown = true
       raiseSSLError()
     else: raiseSSLError("Unknown Error")
 
@@ -200,16 +230,16 @@ when defineSsl:
     let len = bioCtrlPending(socket.bioOut)
     if len > 0:
       var data = newString(len)
-      let read = bioRead(socket.bioOut, addr data[0], len)
+      let read = bioRead(socket.bioOut, cast[cstring](addr data[0]), len)
       assert read != 0
       if read < 0:
-        raiseSslError()
+        raiseSSLError()
       data.setLen(read)
-      await socket.fd.AsyncFd.send(data, flags)
+      await socket.fd.AsyncFD.send(data, flags)
 
   proc appeaseSsl(socket: AsyncSocket, flags: set[SocketFlag],
-                  sslError: cint): Future[bool] {.async.} =
-    ## Returns ``true`` if ``socket`` is still connected, otherwise ``false``.
+                  sslError: cint): owned(Future[bool]) {.async.} =
+    ## Returns `true` if `socket` is still connected, otherwise `false`.
     result = true
     case sslError
     of SSL_ERROR_WANT_WRITE:
@@ -218,7 +248,7 @@ when defineSsl:
       var data = await recv(socket.fd.AsyncFD, BufferSize, flags)
       let length = len(data)
       if length > 0:
-        let ret = bioWrite(socket.bioIn, addr data[0], data.len.cint)
+        let ret = bioWrite(socket.bioIn, cast[cstring](addr data[0]), length.cint)
         if ret < 0:
           raiseSSLError()
       elif length == 0:
@@ -232,32 +262,35 @@ when defineSsl:
                    op: untyped) =
     var opResult {.inject.} = -1.cint
     while opResult < 0:
+      ErrClearError()
       # Call the desired operation.
       opResult = op
-      # Bit hackish here.
-      # TODO: Introduce an async template transformation pragma?
-
+      let err =
+        if opResult < 0:
+          getSslError(socket, opResult.cint)
+        else:
+          SSL_ERROR_NONE
       # Send any remaining pending SSL data.
-      yield sendPendingSslData(socket, flags)
+      await sendPendingSslData(socket, flags)
 
       # If the operation failed, try to see if SSL has some data to read
       # or write.
       if opResult < 0:
-        let err = getSslError(socket.sslHandle, opResult.cint)
         let fut = appeaseSsl(socket, flags, err.cint)
         yield fut
         if not fut.read():
           # Socket disconnected.
           if SocketFlag.SafeDisconn in flags:
+            opResult = 0.cint
             break
           else:
             raiseSSLError("Socket has been disconnected")
 
 proc dial*(address: string, port: Port, protocol = IPPROTO_TCP,
-           buffered = true): Future[AsyncSocket] {.async.} =
-  ## Establishes connection to the specified ``address``:``port`` pair via the
+           buffered = true): owned(Future[AsyncSocket]) {.async.} =
+  ## Establishes connection to the specified `address`:`port` pair via the
   ## specified protocol. The procedure iterates through possible
-  ## resolutions of the ``address`` until it succeeds, meaning that it
+  ## resolutions of the `address` until it succeeds, meaning that it
   ## seamlessly works with both IPv4 and IPv6.
   ## Returns AsyncSocket ready to send or receive data.
   let asyncFd = await asyncdispatch.dial(address, port, protocol)
@@ -266,9 +299,9 @@ proc dial*(address: string, port: Port, protocol = IPPROTO_TCP,
   result = newAsyncSocket(asyncFd, domain, sockType, protocol, buffered)
 
 proc connect*(socket: AsyncSocket, address: string, port: Port) {.async.} =
-  ## Connects ``socket`` to server at ``address:port``.
+  ## Connects `socket` to server at `address:port`.
   ##
-  ## Returns a ``Future`` which will complete when the connection succeeds
+  ## Returns a `Future` which will complete when the connection succeeds
   ## or an error occurs.
   await connect(socket.fd.AsyncFD, address, port, socket.domain)
   if socket.isSsl:
@@ -284,7 +317,7 @@ proc connect*(socket: AsyncSocket, address: string, port: Port) {.async.} =
 
 template readInto(buf: pointer, size: int, socket: AsyncSocket,
                   flags: set[SocketFlag]): int =
-  ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``. Note that
+  ## Reads **up to** `size` bytes from `socket` into `buf`. Note that
   ## this is a template and not a proc.
   assert(not socket.closed, "Cannot `recv` on a closed socket")
   var res = 0
@@ -295,10 +328,8 @@ template readInto(buf: pointer, size: int, socket: AsyncSocket,
         sslRead(socket.sslHandle, cast[cstring](buf), size.cint))
       res = opResult
   else:
-    var recvIntoFut = asyncdispatch.recvInto(socket.fd.AsyncFD, buf, size, flags)
-    yield recvIntoFut
     # Not in SSL mode.
-    res = recvIntoFut.read()
+    res = await asyncdispatch.recvInto(socket.fd.AsyncFD, buf, size, flags)
   res
 
 template readIntoBuf(socket: AsyncSocket,
@@ -309,11 +340,11 @@ template readIntoBuf(socket: AsyncSocket,
   size
 
 proc recvInto*(socket: AsyncSocket, buf: pointer, size: int,
-           flags = {SocketFlag.SafeDisconn}): Future[int] {.async.} =
-  ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``.
+           flags = {SocketFlag.SafeDisconn}): owned(Future[int]) {.async.} =
+  ## Reads **up to** `size` bytes from `socket` into `buf`.
   ##
   ## For buffered sockets this function will attempt to read all the requested
-  ## data. It will read this data in ``BufferSize`` chunks.
+  ## data. It will read this data in `BufferSize` chunks.
   ##
   ## For unbuffered sockets this function makes no effort to read
   ## all the data requested. It will return as much data as the operating system
@@ -324,7 +355,7 @@ proc recvInto*(socket: AsyncSocket, buf: pointer, size: int,
   ## requested data.
   ##
   ## If socket is disconnected and no data is available
-  ## to be read then the future will complete with a value of ``0``.
+  ## to be read then the future will complete with a value of `0`.
   if socket.isBuffered:
     let originalBufPos = socket.currPos
 
@@ -357,11 +388,11 @@ proc recvInto*(socket: AsyncSocket, buf: pointer, size: int,
     result = readInto(buf, size, socket, flags)
 
 proc recv*(socket: AsyncSocket, size: int,
-           flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} =
-  ## Reads **up to** ``size`` bytes from ``socket``.
+           flags = {SocketFlag.SafeDisconn}): owned(Future[string]) {.async.} =
+  ## Reads **up to** `size` bytes from `socket`.
   ##
   ## For buffered sockets this function will attempt to read all the requested
-  ## data. It will read this data in ``BufferSize`` chunks.
+  ## data. It will read this data in `BufferSize` chunks.
   ##
   ## For unbuffered sockets this function makes no effort to read
   ## all the data requested. It will return as much data as the operating system
@@ -372,10 +403,11 @@ proc recv*(socket: AsyncSocket, size: int,
   ## requested data.
   ##
   ## If socket is disconnected and no data is available
-  ## to be read then the future will complete with a value of ``""``.
+  ## to be read then the future will complete with a value of `""`.
   if socket.isBuffered:
     result = newString(size)
-    shallow(result)
+    when not defined(nimSeqsV2):
+      shallow(result)
     let originalBufPos = socket.currPos
 
     if socket.bufLen == 0:
@@ -410,7 +442,7 @@ proc recv*(socket: AsyncSocket, size: int,
 
 proc send*(socket: AsyncSocket, buf: pointer, size: int,
             flags = {SocketFlag.SafeDisconn}) {.async.} =
-  ## Sends ``size`` bytes from ``buf`` to ``socket``. The returned future will complete once all
+  ## Sends `size` bytes from `buf` to `socket`. The returned future will complete once all
   ## data has been sent.
   assert socket != nil
   assert(not socket.closed, "Cannot `send` on a closed socket")
@@ -424,25 +456,30 @@ proc send*(socket: AsyncSocket, buf: pointer, size: int,
 
 proc send*(socket: AsyncSocket, data: string,
            flags = {SocketFlag.SafeDisconn}) {.async.} =
-  ## Sends ``data`` to ``socket``. The returned future will complete once all
+  ## Sends `data` to `socket`. The returned future will complete once all
   ## data has been sent.
   assert socket != nil
   if socket.isSsl:
     when defineSsl:
       var copy = data
       sslLoop(socket, flags,
-        sslWrite(socket.sslHandle, addr copy[0], copy.len.cint))
+        sslWrite(socket.sslHandle, cast[cstring](addr copy[0]), copy.len.cint))
       await sendPendingSslData(socket, flags)
   else:
     await send(socket.fd.AsyncFD, data, flags)
 
-proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}):
-      Future[tuple[address: string, client: AsyncSocket]] =
+proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn},
+                 inheritable = defined(nimInheritHandles)):
+      owned(Future[tuple[address: string, client: AsyncSocket]]) =
   ## Accepts a new connection. Returns a future containing the client socket
   ## corresponding to that connection and the remote address of the client.
+  ##
+  ## If `inheritable` is false (the default), the resulting client socket will
+  ## not be inheritable by child processes.
+  ##
   ## The future will complete when the connection is successfully accepted.
   var retFuture = newFuture[tuple[address: string, client: AsyncSocket]]("asyncnet.acceptAddr")
-  var fut = acceptAddr(socket.fd.AsyncFD, flags)
+  var fut = acceptAddr(socket.fd.AsyncFD, flags, inheritable)
   fut.callback =
     proc (future: Future[tuple[address: string, client: AsyncFD]]) =
       assert future.finished
@@ -451,14 +488,16 @@ proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}):
       else:
         let resultTup = (future.read.address,
                          newAsyncSocket(future.read.client, socket.domain,
-                         socket.sockType, socket.protocol, socket.isBuffered))
+                         socket.sockType, socket.protocol, socket.isBuffered, inheritable))
         retFuture.complete(resultTup)
   return retFuture
 
 proc accept*(socket: AsyncSocket,
-    flags = {SocketFlag.SafeDisconn}): Future[AsyncSocket] =
+    flags = {SocketFlag.SafeDisconn}): owned(Future[AsyncSocket]) =
   ## Accepts a new connection. Returns a future containing the client socket
   ## corresponding to that connection.
+  ## If `inheritable` is false (the default), the resulting client socket will
+  ## not be inheritable by child processes.
   ## The future will complete when the connection is successfully accepted.
   var retFut = newFuture[AsyncSocket]("asyncnet.accept")
   var fut = acceptAddr(socket, flags)
@@ -473,28 +512,25 @@ proc accept*(socket: AsyncSocket,
 
 proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
     flags = {SocketFlag.SafeDisconn}, maxLength = MaxLineLength) {.async.} =
-  ## Reads a line of data from ``socket`` into ``resString``.
+  ## Reads a line of data from `socket` into `resString`.
   ##
-  ## If a full line is read ``\r\L`` is not
-  ## added to ``line``, however if solely ``\r\L`` is read then ``line``
+  ## If a full line is read `\r\L` is not
+  ## added to `line`, however if solely `\r\L` is read then `line`
   ## will be set to it.
   ##
-  ## If the socket is disconnected, ``line`` will be set to ``""``.
+  ## If the socket is disconnected, `line` will be set to `""`.
   ##
-  ## If the socket is disconnected in the middle of a line (before ``\r\L``
-  ## is read) then line will be set to ``""``.
+  ## If the socket is disconnected in the middle of a line (before `\r\L`
+  ## is read) then line will be set to `""`.
   ## The partial line **will be lost**.
   ##
-  ## The ``maxLength`` parameter determines the maximum amount of characters
-  ## that can be read. ``resString`` will be truncated after that.
+  ## The `maxLength` parameter determines the maximum amount of characters
+  ## that can be read. `resString` will be truncated after that.
   ##
-  ## **Warning**: The ``Peek`` flag is not yet implemented.
+  ## .. warning:: The `Peek` flag is not yet implemented.
   ##
-  ## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the
-  ## protocol uses ``\r\L`` to delimit a new line.
+  ## .. warning:: `recvLineInto` on unbuffered sockets assumes that the protocol uses `\r\L` to delimit a new line.
   assert SocketFlag.Peek notin flags ## TODO:
-  assert(not resString.mget.isNil(),
-         "String inside resString future needs to be initialised")
   result = newFuture[void]("asyncnet.recvLineInto")
 
   # TODO: Make the async transformation check for FutureVar params and complete
@@ -567,27 +603,26 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
 
 proc recvLine*(socket: AsyncSocket,
     flags = {SocketFlag.SafeDisconn},
-    maxLength = MaxLineLength): Future[string] {.async.} =
-  ## Reads a line of data from ``socket``. Returned future will complete once
+    maxLength = MaxLineLength): owned(Future[string]) {.async.} =
+  ## Reads a line of data from `socket`. Returned future will complete once
   ## a full line is read or an error occurs.
   ##
-  ## If a full line is read ``\r\L`` is not
-  ## added to ``line``, however if solely ``\r\L`` is read then ``line``
+  ## If a full line is read `\r\L` is not
+  ## added to `line`, however if solely `\r\L` is read then `line`
   ## will be set to it.
   ##
-  ## If the socket is disconnected, ``line`` will be set to ``""``.
+  ## If the socket is disconnected, `line` will be set to `""`.
   ##
-  ## If the socket is disconnected in the middle of a line (before ``\r\L``
-  ## is read) then line will be set to ``""``.
+  ## If the socket is disconnected in the middle of a line (before `\r\L`
+  ## is read) then line will be set to `""`.
   ## The partial line **will be lost**.
   ##
-  ## The ``maxLength`` parameter determines the maximum amount of characters
+  ## The `maxLength` parameter determines the maximum amount of characters
   ## that can be read. The result is truncated after that.
   ##
-  ## **Warning**: The ``Peek`` flag is not yet implemented.
+  ## .. warning:: The `Peek` flag is not yet implemented.
   ##
-  ## **Warning**: ``recvLine`` on unbuffered sockets assumes that the protocol
-  ## uses ``\r\L`` to delimit a new line.
+  ## .. warning:: `recvLine` on unbuffered sockets assumes that the protocol uses `\r\L` to delimit a new line.
   assert SocketFlag.Peek notin flags ## TODO:
 
   # TODO: Optimise this
@@ -596,71 +631,150 @@ proc recvLine*(socket: AsyncSocket,
   await socket.recvLineInto(resString, flags, maxLength)
   result = resString.mget()
 
-proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
-  ## Marks ``socket`` as accepting connections.
-  ## ``Backlog`` specifies the maximum length of the
+proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [
+    ReadIOEffect].} =
+  ## Marks `socket` as accepting connections.
+  ## `Backlog` specifies the maximum length of the
   ## queue of pending connections.
   ##
-  ## Raises an EOS error upon failure.
+  ## Raises an OSError error upon failure.
   if listen(socket.fd, backlog) < 0'i32: raiseOSError(osLastError())
 
 proc bindAddr*(socket: AsyncSocket, port = Port(0), address = "") {.
   tags: [ReadIOEffect].} =
-  ## Binds ``address``:``port`` to the socket.
+  ## Binds `address`:`port` to the socket.
   ##
-  ## If ``address`` is "" then ADDR_ANY will be bound.
+  ## If `address` is "" then ADDR_ANY will be bound.
   var realaddr = address
   if realaddr == "":
     case socket.domain
     of AF_INET6: realaddr = "::"
-    of AF_INET:  realaddr = "0.0.0.0"
+    of AF_INET: realaddr = "0.0.0.0"
     else:
       raise newException(ValueError,
         "Unknown socket address family and no address specified to bindAddr")
 
   var aiList = getAddrInfo(realaddr, port, socket.domain)
-  if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32:
+  if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32:
     freeAddrInfo(aiList)
     raiseOSError(osLastError())
   freeAddrInfo(aiList)
 
+proc hasDataBuffered*(s: AsyncSocket): bool {.since: (1, 5).} =
+  ## Determines whether an AsyncSocket has data buffered.
+  # xxx dedup with std/net
+  s.isBuffered and s.bufLen > 0 and s.currPos != s.bufLen
+
+when defined(posix) and not useNimNetLite:
+
+  proc connectUnix*(socket: AsyncSocket, path: string): owned(Future[void]) =
+    ## Binds Unix socket to `path`.
+    ## This only works on Unix-style systems: Mac OS X, BSD and Linux
+    when not defined(nimdoc):
+      let retFuture = newFuture[void]("connectUnix")
+      result = retFuture
+
+      proc cb(fd: AsyncFD): bool =
+        let ret = SocketHandle(fd).getSockOptInt(cint(SOL_SOCKET), cint(SO_ERROR))
+        if ret == 0:
+          retFuture.complete()
+          return true
+        elif ret == EINTR:
+          return false
+        else:
+          retFuture.fail(newOSError(OSErrorCode(ret)))
+          return true
+
+      var socketAddr = makeUnixAddr(path)
+      let ret = socket.fd.connect(cast[ptr SockAddr](addr socketAddr),
+                        (offsetOf(socketAddr, sun_path) + path.len + 1).SockLen)
+      if ret == 0:
+        # Request to connect completed immediately.
+        retFuture.complete()
+      else:
+        let lastError = osLastError()
+        if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
+          addWrite(AsyncFD(socket.fd), cb)
+        else:
+          retFuture.fail(newOSError(lastError))
+
+  proc bindUnix*(socket: AsyncSocket, path: string) {.
+    tags: [ReadIOEffect].} =
+    ## Binds Unix socket to `path`.
+    ## This only works on Unix-style systems: Mac OS X, BSD and Linux
+    when not defined(nimdoc):
+      var socketAddr = makeUnixAddr(path)
+      if socket.fd.bindAddr(cast[ptr SockAddr](addr socketAddr),
+          (offsetOf(socketAddr, sun_path) + path.len + 1).SockLen) != 0'i32:
+        raiseOSError(osLastError())
+
+elif defined(nimdoc):
+
+  proc connectUnix*(socket: AsyncSocket, path: string): owned(Future[void]) =
+    ## Binds Unix socket to `path`.
+    ## This only works on Unix-style systems: Mac OS X, BSD and Linux
+    discard
+
+  proc bindUnix*(socket: AsyncSocket, path: string) =
+    ## Binds Unix socket to `path`.
+    ## This only works on Unix-style systems: Mac OS X, BSD and Linux
+    discard
+
 proc close*(socket: AsyncSocket) =
   ## Closes the socket.
+  if socket.closed: return
+
   defer:
     socket.fd.AsyncFD.closeSocket()
+    socket.closed = true # TODO: Add extra debugging checks for this.
+
   when defineSsl:
-    if socket.isSSL:
-      let res = SslShutdown(socket.sslHandle)
-      SSLFree(socket.sslHandle)
+    if socket.isSsl:
+      let res =
+        # Don't call SSL_shutdown if the connection has not been fully
+        # established, see:
+        # https://github.com/openssl/openssl/issues/710#issuecomment-253897666
+        if not socket.sslNoShutdown and SSL_in_init(socket.sslHandle) == 0:
+          ErrClearError()
+          SSL_shutdown(socket.sslHandle)
+        else:
+          0
+      SSL_free(socket.sslHandle)
       if res == 0:
         discard
       elif res != 1:
-        raiseSslError()
-  socket.closed = true # TODO: Add extra debugging checks for this.
+        raiseSSLError()
 
 when defineSsl:
+  proc sslHandle*(self: AsyncSocket): SslPtr =
+    ## Retrieve the ssl pointer of `socket`.
+    ## Useful for interfacing with `openssl`.
+    self.sslHandle
+  
   proc wrapSocket*(ctx: SslContext, socket: AsyncSocket) =
     ## Wraps a socket in an SSL context. This function effectively turns
-    ## ``socket`` into an SSL socket.
+    ## `socket` into an SSL socket.
     ##
     ## **Disclaimer**: This code is not well tested, may be very unsafe and
     ## prone to security vulnerabilities.
     socket.isSsl = true
     socket.sslContext = ctx
-    socket.sslHandle = SSLNew(socket.sslContext.context)
+    socket.sslHandle = SSL_new(socket.sslContext.context)
     if socket.sslHandle == nil:
-      raiseSslError()
+      raiseSSLError()
 
-    socket.bioIn = bioNew(bio_s_mem())
-    socket.bioOut = bioNew(bio_s_mem())
+    socket.bioIn = bioNew(bioSMem())
+    socket.bioOut = bioNew(bioSMem())
     sslSetBio(socket.sslHandle, socket.bioIn, socket.bioOut)
 
+    socket.sslNoShutdown = true
+
   proc wrapConnectedSocket*(ctx: SslContext, socket: AsyncSocket,
                             handshake: SslHandshakeType,
-                            hostname: string = nil) =
+                            hostname: string = "") =
     ## Wraps a connected socket in an SSL context. This function effectively
-    ## turns ``socket`` into an SSL socket.
-    ## ``hostname`` should be specified so that the client knows which hostname
+    ## turns `socket` into an SSL socket.
+    ## `hostname` should be specified so that the client knows which hostname
     ## the server certificate should be validated against.
     ##
     ## This should be called on a connected socket, and will perform
@@ -672,7 +786,7 @@ when defineSsl:
 
     case handshake
     of handshakeAsClient:
-      if not hostname.isNil and not isIpAddress(hostname):
+      if hostname.len > 0 and not isIpAddress(hostname):
         # Set the SNI address for this connection. This call can fail if
         # we're not using TLSv1+.
         discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)
@@ -680,20 +794,31 @@ when defineSsl:
     of handshakeAsServer:
       sslSetAcceptState(socket.sslHandle)
 
+  proc getPeerCertificates*(socket: AsyncSocket): seq[Certificate] {.since: (1, 1).} =
+    ## Returns the certificate chain received by the peer we are connected to
+    ## through the given socket.
+    ## The handshake must have been completed and the certificate chain must
+    ## have been verified successfully or else an empty sequence is returned.
+    ## The chain is ordered from leaf certificate to root certificate.
+    if not socket.isSsl:
+      result = newSeq[Certificate]()
+    else:
+      result = getPeerCertificates(socket.sslHandle)
+
 proc getSockOpt*(socket: AsyncSocket, opt: SOBool, level = SOL_SOCKET): bool {.
   tags: [ReadIOEffect].} =
-  ## Retrieves option ``opt`` as a boolean value.
+  ## Retrieves option `opt` as a boolean value.
   var res = getSockOptInt(socket.fd, cint(level), toCInt(opt))
   result = res != 0
 
 proc setSockOpt*(socket: AsyncSocket, opt: SOBool, value: bool,
     level = SOL_SOCKET) {.tags: [WriteIOEffect].} =
-  ## Sets option ``opt`` to a boolean value specified by ``value``.
+  ## Sets option `opt` to a boolean value specified by `value`.
   var valuei = cint(if value: 1 else: 0)
   setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei)
 
 proc isSsl*(socket: AsyncSocket): bool =
-  ## Determines whether ``socket`` is a SSL socket.
+  ## Determines whether `socket` is a SSL socket.
   socket.isSsl
 
 proc getFd*(socket: AsyncSocket): SocketHandle =
@@ -704,6 +829,130 @@ proc isClosed*(socket: AsyncSocket): bool =
   ## Determines whether the socket has been closed.
   return socket.closed
 
+proc sendTo*(socket: AsyncSocket, address: string, port: Port, data: string,
+             flags = {SocketFlag.SafeDisconn}): owned(Future[void])
+            {.async, since: (1, 3).} =
+  ## This proc sends `data` to the specified `address`, which may be an IP
+  ## address or a hostname. If a hostname is specified this function will try
+  ## each IP of that hostname. The returned future will complete once all data
+  ## has been sent.
+  ##
+  ## If an error occurs an OSError exception will be raised.
+  ##
+  ## This proc is normally used with connectionless sockets (UDP sockets).
+  assert(socket.protocol != IPPROTO_TCP,
+         "Cannot `sendTo` on a TCP socket. Use `send` instead")
+  assert(not socket.closed, "Cannot `sendTo` on a closed socket")
+
+  let aiList = getAddrInfo(address, port, socket.domain, socket.sockType,
+                           socket.protocol)
+
+  var
+    it = aiList
+    success = false
+    lastException: ref Exception
+
+  while it != nil:
+    let fut = sendTo(socket.fd.AsyncFD, cstring(data), len(data), it.ai_addr,
+                     it.ai_addrlen.SockLen, flags)
+
+    yield fut
+
+    if not fut.failed:
+      success = true
+
+      break
+
+    lastException = fut.readError()
+
+    it = it.ai_next
+
+  freeAddrInfo(aiList)
+
+  if not success:
+    if lastException != nil:
+      raise lastException
+    else:
+      raise newException(IOError, "Couldn't resolve address: " & address)
+
+proc recvFrom*(socket: AsyncSocket, data: FutureVar[string], size: int,
+               address: FutureVar[string], port: FutureVar[Port],
+               flags = {SocketFlag.SafeDisconn}): owned(Future[int])
+              {.async, since: (1, 3).} =
+  ## Receives a datagram data from `socket` into `data`, which must be at
+  ## least of size `size`. The address and port of datagram's sender will be
+  ## stored into `address` and `port`, respectively. Returned future will
+  ## complete once one datagram has been received, and will return size of
+  ## packet received.
+  ##
+  ## If an error occurs an OSError exception will be raised.
+  ##
+  ## This proc is normally used with connectionless sockets (UDP sockets).
+  ##
+  ## **Notes**
+  ## * `data` must be initialized to the length of `size`.
+  ## * `address` must be initialized to 46 in length.
+  template adaptRecvFromToDomain(domain: Domain) =
+    var lAddr = sizeof(sAddr).SockLen
+
+    result = await recvFromInto(AsyncFD(getFd(socket)), cstring(data.mget()), size,
+                                cast[ptr SockAddr](addr sAddr), addr lAddr,
+                                flags)
+
+    data.mget().setLen(result)
+    data.complete()
+
+    getAddrString(cast[ptr SockAddr](addr sAddr), address.mget())
+
+    address.complete()
+
+    when domain == AF_INET6:
+      port.complete(ntohs(sAddr.sin6_port).Port)
+    else:
+      port.complete(ntohs(sAddr.sin_port).Port)
+
+  assert(socket.protocol != IPPROTO_TCP,
+         "Cannot `recvFrom` on a TCP socket. Use `recv` or `recvInto` instead")
+  assert(not socket.closed, "Cannot `recvFrom` on a closed socket")
+  assert(size == len(data.mget()),
+         "`date` was not initialized correctly. `size` != `len(data.mget())`")
+  assert(46 == len(address.mget()),
+         "`address` was not initialized correctly. 46 != `len(address.mget())`")
+
+  case socket.domain
+  of AF_INET6:
+    var sAddr: Sockaddr_in6
+    adaptRecvFromToDomain(AF_INET6)
+  of AF_INET:
+    var sAddr: Sockaddr_in
+    adaptRecvFromToDomain(AF_INET)
+  else:
+    raise newException(ValueError, "Unknown socket address family")
+
+proc recvFrom*(socket: AsyncSocket, size: int,
+               flags = {SocketFlag.SafeDisconn}):
+              owned(Future[tuple[data: string, address: string, port: Port]])
+              {.async, since: (1, 3).} =
+  ## Receives a datagram data from `socket`, which must be at least of size
+  ## `size`. Returned future will complete once one datagram has been received
+  ## and will return tuple with: data of packet received; and address and port
+  ## of datagram's sender.
+  ##
+  ## If an error occurs an OSError exception will be raised.
+  ##
+  ## This proc is normally used with connectionless sockets (UDP sockets).
+  var
+    data = newFutureVar[string]()
+    address = newFutureVar[string]()
+    port = newFutureVar[Port]()
+
+  data.mget().setLen(size)
+  address.mget().setLen(46)
+
+  let read = await recvFrom(socket, data, size, address, port, flags)
+
+  result = (data.mget(), address.mget(), port.mget())
+
 when not defined(testing) and isMainModule:
   type
     TestCases = enum
diff --git a/lib/pure/asyncstreams.nim b/lib/pure/asyncstreams.nim
index d3ea143f3..c97b98d55 100644
--- a/lib/pure/asyncstreams.nim
+++ b/lib/pure/asyncstreams.nim
@@ -1,25 +1,40 @@
-import asyncfutures
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Dominik Picheta
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
 
-import deques
+## Unstable API.
+
+import std/asyncfutures
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+import std/deques
 
 type
-  FutureStream*[T] = ref object   ## Special future that acts as
-                                  ## a queue. Its API is still
-                                  ## experimental and so is
-                                  ## subject to change.
+  FutureStream*[T] = ref object ## Special future that acts as
+                                ## a queue. Its API is still
+                                ## experimental and so is
+                                ## subject to change.
     queue: Deque[T]
     finished: bool
     cb: proc () {.closure, gcsafe.}
+    error*: ref Exception
 
 proc newFutureStream*[T](fromProc = "unspecified"): FutureStream[T] =
-  ## Create a new ``FutureStream``. This future's callback is activated when
+  ## Create a new `FutureStream`. This future's callback is activated when
   ## two events occur:
   ##
   ## * New data is written into the future stream.
   ## * The future stream is completed (this means that no more data will be
   ##   written).
   ##
-  ## Specifying ``fromProc``, which is a string specifying the name of the proc
+  ## Specifying `fromProc`, which is a string specifying the name of the proc
   ## that this future belongs to, is a good habit as it helps with debugging.
   ##
   ## **Note:** The API of FutureStream is still new and so has a higher
@@ -28,76 +43,103 @@ proc newFutureStream*[T](fromProc = "unspecified"): FutureStream[T] =
   result.queue = initDeque[T]()
 
 proc complete*[T](future: FutureStream[T]) =
-  ## Completes a ``FutureStream`` signalling the end of data.
+  ## Completes a `FutureStream` signalling the end of data.
+  assert(future.error == nil, "Trying to complete failed stream")
+  future.finished = true
+  if not future.cb.isNil:
+    future.cb()
+
+proc fail*[T](future: FutureStream[T], error: ref Exception) =
+  ## Completes `future` with `error`.
+  assert(not future.finished)
   future.finished = true
+  future.error = error
   if not future.cb.isNil:
     future.cb()
 
 proc `callback=`*[T](future: FutureStream[T],
-    cb: proc (future: FutureStream[T]) {.closure,gcsafe.}) =
+    cb: proc (future: FutureStream[T]) {.closure, gcsafe.}) =
   ## Sets the callback proc to be called when data was placed inside the
   ## future stream.
   ##
   ## The callback is also called when the future is completed. So you should
-  ## use ``finished`` to check whether data is available.
+  ## use `finished` to check whether data is available.
   ##
-  ## If the future stream already has data or is finished then ``cb`` will be
+  ## If the future stream already has data or is finished then `cb` will be
   ## called immediately.
-  future.cb = proc () = cb(future)
+  proc named() = cb(future)
+  future.cb = named
   if future.queue.len > 0 or future.finished:
     callSoon(future.cb)
 
 proc finished*[T](future: FutureStream[T]): bool =
-  ## Check if a ``FutureStream`` is finished. ``true`` value means that
-  ## no more data will be placed inside the stream _and_ that there is
+  ## Check if a `FutureStream` is finished. `true` value means that
+  ## no more data will be placed inside the stream *and* that there is
   ## no data waiting to be retrieved.
   result = future.finished and future.queue.len == 0
 
+proc failed*[T](future: FutureStream[T]): bool =
+  ## Determines whether `future` completed with an error.
+  return future.error != nil
+
 proc write*[T](future: FutureStream[T], value: T): Future[void] =
   ## Writes the specified value inside the specified future stream.
   ##
-  ## This will raise ``ValueError`` if ``future`` is finished.
+  ## This will raise `ValueError` if `future` is finished.
   result = newFuture[void]("FutureStream.put")
   if future.finished:
     let msg = "FutureStream is finished and so no longer accepts new data."
     result.fail(newException(ValueError, msg))
     return
   # TODO: Implement limiting of the streams storage to prevent it growing
-  # infinitely when no reads are occuring.
+  # infinitely when no reads are occurring.
   future.queue.addLast(value)
   if not future.cb.isNil: future.cb()
   result.complete()
 
-proc read*[T](future: FutureStream[T]): Future[(bool, T)] =
-  ## Returns a future that will complete when the ``FutureStream`` has data
+proc read*[T](future: FutureStream[T]): owned(Future[(bool, T)]) =
+  ## Returns a future that will complete when the `FutureStream` has data
   ## placed into it. The future will be completed with the oldest
   ## value stored inside the stream. The return value will also determine
-  ## whether data was retrieved, ``false`` means that the future stream was
+  ## whether data was retrieved, `false` means that the future stream was
   ## completed and no data was retrieved.
   ##
   ## This function will remove the data that was returned from the underlying
-  ## ``FutureStream``.
+  ## `FutureStream`.
   var resFut = newFuture[(bool, T)]("FutureStream.take")
   let savedCb = future.cb
-  future.callback =
-    proc (fs: FutureStream[T]) =
-      # We don't want this callback called again.
-      future.cb = nil
-
-      # The return value depends on whether the FutureStream has finished.
-      var res: (bool, T)
-      if finished(fs):
-        # Remember, this callback is called when the FutureStream is completed.
-        res[0] = false
-      else:
-        res[0] = true
-        res[1] = fs.queue.popFirst()
+  proc newCb(fs: FutureStream[T]) =
+    # Exit early if `resFut` is already complete. (See #8994).
+    if resFut.finished: return
+
+    # We don't want this callback called again.
+    #future.cb = nil
+
+    # The return value depends on whether the FutureStream has finished.
+    var res: (bool, T)
+    if finished(fs):
+      # Remember, this callback is called when the FutureStream is completed.
+      res[0] = false
+    else:
+      res[0] = true
+      res[1] = fs.queue.popFirst()
 
-      if not resFut.finished:
-        resFut.complete(res)
+    if fs.failed:
+      resFut.fail(fs.error)
+    else:
+      resFut.complete(res)
 
-      # If the saved callback isn't nil then let's call it.
-      if not savedCb.isNil: savedCb()
+    # If the saved callback isn't nil then let's call it.
+    if not savedCb.isNil:
+      if fs.queue.len > 0:
+        savedCb()
+      else:
+        future.cb = savedCb
+
+  if future.queue.len > 0 or future.finished:
+    newCb(future)
+  else:
+    future.callback = newCb
   return resFut
 
 proc len*[T](future: FutureStream[T]): int =
diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim
index bfb8a1666..591d22cc0 100644
--- a/lib/pure/base64.nim
+++ b/lib/pure/base64.nim
@@ -9,169 +9,265 @@
 
 ## This module implements a base64 encoder and decoder.
 ##
-## Encoding data
-## -------------
-##
-## In order to encode some text simply call the ``encode`` procedure:
+## Unstable API.
 ##
-##   .. code-block::nim
-##      import base64
-##      let encoded = encode("Hello World")
-##      echo(encoded) # SGVsbG8gV29ybGQ=
+## 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
+]##
+
+runnableExamples:
+  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
-##
-## The ``encode`` procedure takes an ``openarray`` so both arrays and sequences
-## can be passed as parameters.
-##
+
+runnableExamples:
+  let encodedInts = encode([1'u8,2,3])
+  assert encodedInts == "AQID"
+  let encodedChars = encode(['h','e','y'])
+  assert encodedChars == "aGV5"
+
+##[
 ## Decoding data
-## -------------
-##
-## To decode a base64 encoded data string simply call the ``decode``
-## procedure:
+]##
+
+runnableExamples:
+  let decoded = decode("SGVsbG8gV29ybGQ=")
+  assert decoded == "Hello World"
+
+##[
+## URL Safe Base64
+]##
+
+runnableExamples:
+  assert encode("c\xf7>", safe = true) == "Y_c-"
+  assert encode("c\xf7>", safe = false) == "Y/c+"
+
+## See also
+## ========
 ##
-##   .. code-block::nim
-##      import base64
-##      echo(decode("SGVsbG8gV29ybGQ=")) # Hello World
+## * `hashes module<hashes.html>`_ for efficient computations of hash values for diverse Nim types
+## * `md5 module<md5.html>`_ for the MD5 checksum algorithm
+## * `sha1 module<sha1.html>`_ for the SHA-1 checksum algorithm
+
+template cbBase(a, b): untyped = [
+  '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',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', a, b]
 
 const
-  cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+  cb64 = cbBase('+', '/')
+  cb64safe = cbBase('-', '_')
+
+const
+  invalidChar = 255
+
+template encodeSize(size: int): int = (size * 4 div 3) + 6
+
+template encodeInternal(s, alphabet: typed): untyped =
+  ## encodes `s` into base64 representation.
+
+  result.setLen(encodeSize(s.len))
 
-template encodeInternal(s: typed, lineLen: int, newLine: string): untyped =
-  ## encodes `s` into base64 representation. After `lineLen` characters, a
-  ## `newline` is added.
-  var total = ((len(s) + 2) div 3) * 4
-  let numLines = (total + lineLen - 1) div lineLen
-  if numLines > 0: inc(total, (numLines - 1) * newLine.len)
+  let
+    padding = s.len mod 3
+    inputEnds = s.len - padding
 
-  result = newString(total)
   var
-    i = 0
-    r = 0
-    currLine = 0
-  while i < s.len - 2:
-    let
-      a = ord(s[i])
-      b = ord(s[i+1])
-      c = ord(s[i+2])
-    result[r] = cb64[a shr 2]
-    result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
-    result[r+2] = cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
-    result[r+3] = cb64[c and 0x3F]
-    inc(r, 4)
-    inc(i, 3)
-    inc(currLine, 4)
-    # avoid index out of bounds when lineLen == encoded length
-    if currLine >= lineLen and i != s.len-2 and r < total:
-      for x in items(newLine):
-        result[r] = x
-        inc(r)
-      currLine = 0
-
-  if i < s.len-1:
-    let
-      a = ord(s[i])
-      b = ord(s[i+1])
-    result[r] = cb64[a shr 2]
-    result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
-    result[r+2] = cb64[((b and 0x0F) shl 2)]
-    result[r+3] = '='
-    if r+4 != result.len:
-      setLen(result, r+4)
-  elif i < s.len:
-    let a = ord(s[i])
-    result[r] = cb64[a shr 2]
-    result[r+1] = cb64[(a and 3) shl 4]
-    result[r+2] = '='
-    result[r+3] = '='
-    if r+4 != result.len:
-      setLen(result, r+4)
+    inputIndex = 0
+    outputIndex = 0
+    n: uint32
+    b: uint32
+
+  template inputByte(exp: untyped) =
+    b = uint32(s[inputIndex])
+    n = exp
+    inc inputIndex
+
+  template outputChar(x: typed) =
+    result[outputIndex] = alphabet[x and 63]
+    inc outputIndex
+
+  template outputChar(c: char) =
+    result[outputIndex] = c
+    inc outputIndex
+
+  while inputIndex != inputEnds:
+    inputByte(b shl 16)
+    inputByte(n or b shl 8)
+    inputByte(n or b shl 0)
+    outputChar(n shr 18)
+    outputChar(n shr 12)
+    outputChar(n shr 6)
+    outputChar(n shr 0)
+
+  if padding == 1:
+    inputByte(b shl 16)
+    outputChar(n shr 18)
+    outputChar(n shr 12)
+    outputChar('=')
+    outputChar('=')
+
+  elif padding == 2:
+    inputByte(b shl 16)
+    inputByte(n or b shl 8)
+    outputChar(n shr 18)
+    outputChar(n shr 12)
+    outputChar(n shr 6)
+    outputChar('=')
+
+  result.setLen(outputIndex)
+
+template encodeImpl() {.dirty.} =
+  if safe:
+    encodeInternal(s, cb64safe)
   else:
-    if r != result.len:
-      setLen(result, r)
-    #assert(r == result.len)
-    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.
+    encodeInternal(s, cb64)
+
+proc encode*[T: byte|char](s: openArray[T], safe = false): string =
+  ## Encodes `s` into base64 representation.
+  ##
+  ## If `safe` is `true` then it will encode using the
+  ## URL-Safe and Filesystem-safe standard alphabet characters,
+  ## which substitutes `-` instead of `+` and `_` instead of `/`.
+  ## * https://en.wikipedia.org/wiki/Base64#URL_applications
+  ## * https://tools.ietf.org/html/rfc4648#page-7
   ##
-  ## This procedure encodes an openarray (array or sequence) of either integers
-  ## or characters.
-  encodeInternal(s, lineLen, newLine)
+  ## **See also:**
+  ## * `decode proc<#decode,string>`_ for decoding a string
+  runnableExamples:
+    assert encode("Hello World") == "SGVsbG8gV29ybGQ="
+    assert encode(['n', 'i', 'm']) == "bmlt"
+    assert encode(@['n', 'i', 'm']) == "bmlt"
+    assert encode([1'u8, 2, 3, 4, 5]) == "AQIDBAU="
+  encodeImpl()
 
-proc encode*(s: string, lineLen = 75, newLine="\13\10"): string =
-  ## encodes `s` into base64 representation. After `lineLen` characters, a
-  ## `newline` is added.
+proc encode*[T: SomeInteger and not byte](s: openArray[T], safe = false): string
+  {.deprecated: "use `byte` or `char` instead".} =
+  encodeImpl()
+
+proc encodeMime*(s: string, lineLen = 75.Positive, newLine = "\r\n",
+                 safe = false): string =
+  ## Encodes `s` into base64 representation as lines.
+  ## Used in email MIME format, use `lineLen` and `newline`.
+  ##
+  ## This procedure encodes a string according to MIME spec.
+  ##
+  ## If `safe` is `true` then it will encode using the
+  ## URL-Safe and Filesystem-safe standard alphabet characters,
+  ## which substitutes `-` instead of `+` and `_` instead of `/`.
+  ## * https://en.wikipedia.org/wiki/Base64#URL_applications
+  ## * https://tools.ietf.org/html/rfc4648#page-7
   ##
-  ## This procedure encodes a string.
-  encodeInternal(s, lineLen, newLine)
+  ## **See also:**
+  ## * `encode proc<#encode,openArray[T]>`_ for encoding an openArray
+  ## * `decode proc<#decode,string>`_ for decoding a string
+  runnableExamples:
+    assert encodeMime("Hello World", 4, "\n") == "SGVs\nbG8g\nV29y\nbGQ="
+  template cpy(l, src, idx) =
+    b = l
+    while i < b:
+      result[i] = src[idx]
+      inc i
+      inc idx
+
+  if s.len == 0: return
+  let e = encode(s, safe)
+  if e.len <= lineLen or newLine.len == 0:
+    return e
+  result = newString(e.len + newLine.len * ((e.len div lineLen) - int(e.len mod lineLen == 0)))
+  var i, j, k, b: int
+  let nd = e.len - lineLen
+  while j < nd:
+    cpy(i + lineLen, e, j)
+    cpy(i + newLine.len, newLine, k)
+    k = 0
+  cpy(result.len, e, j)
+
+proc initDecodeTable*(): array[256, char] =
+  # computes a decode table at compile time
+  for i in 0 ..< 256:
+    let ch = char(i)
+    var code = invalidChar
+    if ch >= 'A' and ch <= 'Z': code = i - 0x00000041
+    if ch >= 'a' and ch <= 'z': code = i - 0x00000047
+    if ch >= '0' and ch <= '9': code = i + 0x00000004
+    if ch == '+' or ch == '-': code = 0x0000003E
+    if ch == '/' or ch == '_': code = 0x0000003F
+    result[i] = char(code)
 
-proc decodeByte(b: char): int {.inline.} =
-  case b
-  of '+': result = ord('>')
-  of '0'..'9': result = ord(b) + 4
-  of 'A'..'Z': result = ord(b) - ord('A')
-  of 'a'..'z': result = ord(b) - 71
-  else: result = 63
+const
+  decodeTable = initDecodeTable()
 
 proc decode*(s: string): string =
-  ## decodes a string in base64 representation back into its original form.
-  ## Whitespace is skipped.
-  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:
-  result = newString(total)
+  ## Decodes string `s` in base64 representation back into its original form.
+  ## The initial whitespace is skipped.
+  ##
+  ## **See also:**
+  ## * `encode proc<#encode,openArray[T]>`_ for encoding an openarray
+  runnableExamples:
+    assert decode("SGVsbG8gV29ybGQ=") == "Hello World"
+    assert decode("  SGVsbG8gV29ybGQ=") == "Hello World"
+  if s.len == 0: return
+
+  proc decodeSize(size: int): int =
+    return (size * 3 div 4) + 6
+
+  template inputChar(x: untyped) =
+    let x = int decodeTable[ord(s[inputIndex])]
+    if x == invalidChar:
+      raise newException(ValueError,
+        "Invalid base64 format character `" & s[inputIndex] &
+        "` (ord " & $s[inputIndex].ord & ") at location " & $inputIndex & ".")
+    inc inputIndex
+
+  template outputChar(x: untyped) =
+    result[outputIndex] = char(x and 255)
+    inc outputIndex
 
+  # pre allocate output string once
+  result.setLen(decodeSize(s.len))
   var
-    i = 0
-    r = 0
-  while true:
-    while i < s.len and s[i] in Whitespace: inc(i)
-    if i < s.len-3:
-      let
-        a = s[i].decodeByte
-        b = s[i+1].decodeByte
-        c = s[i+2].decodeByte
-        d = s[i+3].decodeByte
-
-      result[r] = chr((a shl 2) and 0xff or ((b shr 4) and 0x03))
-      result[r+1] = chr((b shl 4) and 0xff or ((c shr 2) and 0x0F))
-      result[r+2] = chr((c shl 6) and 0xff or (d and 0x3F))
-      inc(r, 3)
-      inc(i, 4)
-    else: break
-  assert i == s.len
-  # adjust the length:
-  if i > 0 and s[i-1] == '=':
-    dec(r)
-    if i > 1 and s[i-2] == '=': dec(r)
-  setLen(result, r)
-
-when isMainModule:
-  assert encode("leasure.") == "bGVhc3VyZS4="
-  assert encode("easure.") == "ZWFzdXJlLg=="
-  assert encode("asure.") == "YXN1cmUu"
-  assert encode("sure.") == "c3VyZS4="
-
-  const testInputExpandsTo76 = "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
-  const testInputExpands = "++++++++++++++++++++++++++++++"
-  const longText = """Man is distinguished, not only by his reason, but by this
-    singular passion from other animals, which is a lust of the mind,
-    that by a perseverance of delight in the continued and indefatigable
-    generation of knowledge, exceeds the short vehemence of any carnal
-    pleasure."""
-  const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.",
-                 "asure.", longText, testInputExpandsTo76, testInputExpands]
-
-  for t in items(tests):
-    assert decode(encode(t)) == t
-    assert decode(encode(t, lineLen=40)) == t
-    assert decode(encode(t, lineLen=76)) == t
+    inputIndex = 0
+    outputIndex = 0
+    inputLen = s.len
+    inputEnds = 0
+  # strip trailing characters
+  while inputLen > 0 and s[inputLen - 1] in {'\n', '\r', ' ', '='}:
+    dec inputLen
+  # hot loop: read 4 characters at at time
+  inputEnds = inputLen - 4
+  while inputIndex <= inputEnds:
+    while s[inputIndex] in {'\n', '\r', ' '}:
+      inc inputIndex
+    inputChar(a)
+    inputChar(b)
+    inputChar(c)
+    inputChar(d)
+    outputChar(a shl 2 or b shr 4)
+    outputChar(b shl 4 or c shr 2)
+    outputChar(c shl 6 or d shr 0)
+  # do the last 2 or 3 characters
+  var leftLen = abs((inputIndex - inputLen) mod 4)
+  if leftLen == 2:
+    inputChar(a)
+    inputChar(b)
+    outputChar(a shl 2 or b shr 4)
+  elif leftLen == 3:
+    inputChar(a)
+    inputChar(b)
+    inputChar(c)
+    outputChar(a shl 2 or b shr 4)
+    outputChar(b shl 4 or c shr 2)
+  result.setLen(outputIndex)
diff --git a/lib/pure/bitops.nim b/lib/pure/bitops.nim
index 3f213c5ea..0d3351ee5 100644
--- a/lib/pure/bitops.nim
+++ b/lib/pure/bitops.nim
@@ -8,52 +8,380 @@
 #
 
 ## This module implements a series of low level methods for bit manipulation.
-## By default, this module use compiler intrinsics to improve performance
-## on supported compilers: ``GCC``, ``LLVM_GCC``, ``CLANG``, ``VCC``, ``ICC``.
 ##
-## The module will fallback to pure nim procs incase the backend is not supported.
+## By default, compiler intrinsics are used where possible to improve performance
+## on supported compilers: `GCC`, `LLVM_GCC`, `CLANG`, `VCC`, `ICC`.
+##
+## The module will fallback to pure nim procs in case the backend is not supported.
 ## You can also use the flag `noIntrinsicsBitOpts` to disable compiler intrinsics.
 ##
-## This module is also compatible with other backends: ``Javascript``, ``Nimscript``
-## as well as the ``compiletime VM``.
+## This module is also compatible with other backends: `JavaScript`, `NimScript`
+## as well as the `compiletime VM`.
 ##
-## As a result of using optimized function/intrinsics some functions can return
+## As a result of using optimized functions/intrinsics, some functions can return
 ## undefined results if the input is invalid. You can use the flag `noUndefinedBitOpts`
 ## to force predictable behaviour for all input, causing a small performance hit.
 ##
-## At this time only `fastLog2`, `firstSetBit, `countLeadingZeroBits`, `countTrailingZeroBits`
-## may return undefined and/or platform dependant value if given invalid input.
+## At this time only `fastLog2`, `firstSetBit`, `countLeadingZeroBits` and `countTrailingZeroBits`
+## may return undefined and/or platform dependent values if given invalid input.
+
+import std/macros
+import std/private/since
+from std/private/bitops_utils import forwardImpl, castToUnsigned
+
+func bitnot*[T: SomeInteger](x: T): T {.magic: "BitnotI".}
+  ## Computes the `bitwise complement` of the integer `x`.
+
+func internalBitand[T: SomeInteger](x, y: T): T {.magic: "BitandI".}
+
+func internalBitor[T: SomeInteger](x, y: T): T {.magic: "BitorI".}
+
+func internalBitxor[T: SomeInteger](x, y: T): T {.magic: "BitxorI".}
 
+macro bitand*[T: SomeInteger](x, y: T; z: varargs[T]): T =
+  ## Computes the `bitwise and` of all arguments collectively.
+  let fn = bindSym("internalBitand")
+  result = newCall(fn, x, y)
+  for extra in z:
+    result = newCall(fn, result, extra)
 
-const useBuiltins = not defined(noIntrinsicsBitOpts)
-const noUndefined = defined(noUndefinedBitOpts)
-const useGCC_builtins = (defined(gcc) or defined(llvm_gcc) or defined(clang)) and useBuiltins
-const useICC_builtins = defined(icc) and useBuiltins
-const useVCC_builtins = defined(vcc) and useBuiltins
-const arch64 = sizeof(int) == 8
+macro bitor*[T: SomeInteger](x, y: T; z: varargs[T]): T =
+  ## Computes the `bitwise or` of all arguments collectively.
+  let fn = bindSym("internalBitor")
+  result = newCall(fn, x, y)
+  for extra in z:
+    result = newCall(fn, result, extra)
+
+macro bitxor*[T: SomeInteger](x, y: T; z: varargs[T]): T =
+  ## Computes the `bitwise xor` of all arguments collectively.
+  let fn = bindSym("internalBitxor")
+  result = newCall(fn, x, y)
+  for extra in z:
+    result = newCall(fn, result, extra)
+
+
+type BitsRange*[T] = range[0..sizeof(T)*8-1]
+  ## A range with all bit positions for type `T`.
+
+template typeMasked[T: SomeInteger](x: T): T =
+  when defined(js):
+    T(x and ((0xffffffff_ffffffff'u shr (64 - sizeof(T) * 8))))
+  else:
+    x
+
+func bitsliced*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1, 3).} =
+  ## Returns an extracted (and shifted) slice of bits from `v`.
+  runnableExamples:
+    doAssert 0b10111.bitsliced(2 .. 4) == 0b101
+    doAssert 0b11100.bitsliced(0 .. 2) == 0b100
+    doAssert 0b11100.bitsliced(0 ..< 3) == 0b100
+
+  let
+    upmost = sizeof(T) * 8 - 1
+    uv     = v.castToUnsigned
+  ((uv shl (upmost - slice.b)).typeMasked shr (upmost - slice.b + slice.a)).T
+
+proc bitslice*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, 3).} =
+  ## Mutates `v` into an extracted (and shifted) slice of bits from `v`.
+  runnableExamples:
+    var x = 0b101110
+    x.bitslice(2 .. 4)
+    doAssert x == 0b011
+
+  let
+    upmost = sizeof(T) * 8 - 1
+    uv     = v.castToUnsigned
+  v = ((uv shl (upmost - slice.b)).typeMasked shr (upmost - slice.b + slice.a)).T
+
+func toMask*[T: SomeInteger](slice: Slice[int]): T {.inline, since: (1, 3).} =
+  ## Creates a bitmask based on a slice of bits.
+  runnableExamples:
+    doAssert toMask[int32](1 .. 3) == 0b1110'i32
+    doAssert toMask[int32](0 .. 3) == 0b1111'i32
+
+  let
+    upmost = sizeof(T) * 8 - 1
+    bitmask = bitnot(0.T).castToUnsigned
+  ((bitmask shl (upmost - slice.b + slice.a)).typeMasked shr (upmost - slice.b)).T
+
+proc masked*[T: SomeInteger](v, mask :T): T {.inline, since: (1, 3).} =
+  ## Returns `v`, with only the `1` bits from `mask` matching those of
+  ## `v` set to 1.
+  ##
+  ## Effectively maps to a `bitand <#bitand.m,T,T,varargs[T]>`_ operation.
+  runnableExamples:
+    let v = 0b0000_0011'u8
+    doAssert v.masked(0b0000_1010'u8) == 0b0000_0010'u8
+
+  bitand(v, mask)
+
+func masked*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1, 3).} =
+  ## Returns `v`, with only the `1` bits in the range of `slice`
+  ## matching those of `v` set to 1.
+  ##
+  ## Effectively maps to a `bitand <#bitand.m,T,T,varargs[T]>`_ operation.
+  runnableExamples:
+    let v = 0b0000_1011'u8
+    doAssert v.masked(1 .. 3) == 0b0000_1010'u8
+
+  bitand(v, toMask[T](slice))
+
+proc mask*[T: SomeInteger](v: var T; mask: T) {.inline, since: (1, 3).} =
+  ## Mutates `v`, with only the `1` bits from `mask` matching those of
+  ## `v` set to 1.
+  ##
+  ## Effectively maps to a `bitand <#bitand.m,T,T,varargs[T]>`_ operation.
+  runnableExamples:
+    var v = 0b0000_0011'u8
+    v.mask(0b0000_1010'u8)
+    doAssert v == 0b0000_0010'u8
+
+  v = bitand(v, mask)
+
+proc mask*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, 3).} =
+  ## Mutates `v`, with only the `1` bits in the range of `slice`
+  ## matching those of `v` set to 1.
+  ##
+  ## Effectively maps to a `bitand <#bitand.m,T,T,varargs[T]>`_ operation.
+  runnableExamples:
+    var v = 0b0000_1011'u8
+    v.mask(1 .. 3)
+    doAssert v == 0b0000_1010'u8
+
+  v = bitand(v, toMask[T](slice))
+
+func setMasked*[T: SomeInteger](v, mask :T): T {.inline, since: (1, 3).} =
+  ## Returns `v`, with all the `1` bits from `mask` set to 1.
+  ##
+  ## Effectively maps to a `bitor <#bitor.m,T,T,varargs[T]>`_ operation.
+  runnableExamples:
+    let v = 0b0000_0011'u8
+    doAssert v.setMasked(0b0000_1010'u8) == 0b0000_1011'u8
+
+  bitor(v, mask)
+
+func setMasked*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1, 3).} =
+  ## Returns `v`, with all the `1` bits in the range of `slice` set to 1.
+  ##
+  ## Effectively maps to a `bitor <#bitor.m,T,T,varargs[T]>`_ operation.
+  runnableExamples:
+    let v = 0b0000_0011'u8
+    doAssert v.setMasked(2 .. 3) == 0b0000_1111'u8
+
+  bitor(v, toMask[T](slice))
+
+proc setMask*[T: SomeInteger](v: var T; mask: T) {.inline.} =
+  ## Mutates `v`, with all the `1` bits from `mask` set to 1.
+  ##
+  ## Effectively maps to a `bitor <#bitor.m,T,T,varargs[T]>`_ operation.
+  runnableExamples:
+    var v = 0b0000_0011'u8
+    v.setMask(0b0000_1010'u8)
+    doAssert v == 0b0000_1011'u8
+
+  v = bitor(v, mask)
+
+proc setMask*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, 3).} =
+  ## Mutates `v`, with all the `1` bits in the range of `slice` set to 1.
+  ##
+  ## Effectively maps to a `bitor <#bitor.m,T,T,varargs[T]>`_ operation.
+  runnableExamples:
+    var v = 0b0000_0011'u8
+    v.setMask(2 .. 3)
+    doAssert v == 0b0000_1111'u8
+
+  v = bitor(v, toMask[T](slice))
+
+func clearMasked*[T: SomeInteger](v, mask :T): T {.inline, since: (1, 3).} =
+  ## Returns `v`, with all the `1` bits from `mask` set to 0.
+  ##
+  ## Effectively maps to a `bitand <#bitand.m,T,T,varargs[T]>`_ operation
+  ## with an *inverted mask*.
+  runnableExamples:
+    let v = 0b0000_0011'u8
+    doAssert v.clearMasked(0b0000_1010'u8) == 0b0000_0001'u8
+
+  bitand(v, bitnot(mask))
+
+func clearMasked*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1, 3).} =
+  ## Returns `v`, with all the `1` bits in the range of `slice` set to 0.
+  ##
+  ## Effectively maps to a `bitand <#bitand.m,T,T,varargs[T]>`_ operation
+  ## with an *inverted mask*.
+  runnableExamples:
+    let v = 0b0000_0011'u8
+    doAssert v.clearMasked(1 .. 3) == 0b0000_0001'u8
+
+  bitand(v, bitnot(toMask[T](slice)))
+
+proc clearMask*[T: SomeInteger](v: var T; mask: T) {.inline.} =
+  ## Mutates `v`, with all the `1` bits from `mask` set to 0.
+  ##
+  ## Effectively maps to a `bitand <#bitand.m,T,T,varargs[T]>`_ operation
+  ## with an *inverted mask*.
+  runnableExamples:
+    var v = 0b0000_0011'u8
+    v.clearMask(0b0000_1010'u8)
+    doAssert v == 0b0000_0001'u8
+
+  v = bitand(v, bitnot(mask))
+
+proc clearMask*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, 3).} =
+  ## Mutates `v`, with all the `1` bits in the range of `slice` set to 0.
+  ##
+  ## Effectively maps to a `bitand <#bitand.m,T,T,varargs[T]>`_ operation
+  ## with an *inverted mask*.
+  runnableExamples:
+    var v = 0b0000_0011'u8
+    v.clearMask(1 .. 3)
+    doAssert v == 0b0000_0001'u8
+
+  v = bitand(v, bitnot(toMask[T](slice)))
+
+func flipMasked*[T: SomeInteger](v, mask :T): T {.inline, since: (1, 3).} =
+  ## Returns `v`, with all the `1` bits from `mask` flipped.
+  ##
+  ## Effectively maps to a `bitxor <#bitxor.m,T,T,varargs[T]>`_ operation.
+  runnableExamples:
+    let v = 0b0000_0011'u8
+    doAssert v.flipMasked(0b0000_1010'u8) == 0b0000_1001'u8
+
+  bitxor(v, mask)
+
+func flipMasked*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1, 3).} =
+  ## Returns `v`, with all the `1` bits in the range of `slice` flipped.
+  ##
+  ## Effectively maps to a `bitxor <#bitxor.m,T,T,varargs[T]>`_ operation.
+  runnableExamples:
+    let v = 0b0000_0011'u8
+    doAssert v.flipMasked(1 .. 3) == 0b0000_1101'u8
+
+  bitxor(v, toMask[T](slice))
+
+proc flipMask*[T: SomeInteger](v: var T; mask: T) {.inline.} =
+  ## Mutates `v`, with all the `1` bits from `mask` flipped.
+  ##
+  ## Effectively maps to a `bitxor <#bitxor.m,T,T,varargs[T]>`_ operation.
+  runnableExamples:
+    var v = 0b0000_0011'u8
+    v.flipMask(0b0000_1010'u8)
+    doAssert v == 0b0000_1001'u8
+
+  v = bitxor(v, mask)
+
+proc flipMask*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, 3).} =
+  ## Mutates `v`, with all the `1` bits in the range of `slice` flipped.
+  ##
+  ## Effectively maps to a `bitxor <#bitxor.m,T,T,varargs[T]>`_ operation.
+  runnableExamples:
+    var v = 0b0000_0011'u8
+    v.flipMask(1 .. 3)
+    doAssert v == 0b0000_1101'u8
+
+  v = bitxor(v, toMask[T](slice))
+
+proc setBit*[T: SomeInteger](v: var T; bit: BitsRange[T]) {.inline.} =
+  ## Mutates `v`, with the bit at position `bit` set to 1.
+  runnableExamples:
+    var v = 0b0000_0011'u8
+    v.setBit(5'u8)
+    doAssert v == 0b0010_0011'u8
+
+  v.setMask(1.T shl bit)
+
+proc clearBit*[T: SomeInteger](v: var T; bit: BitsRange[T]) {.inline.} =
+  ## Mutates `v`, with the bit at position `bit` set to 0.
+  runnableExamples:
+    var v = 0b0000_0011'u8
+    v.clearBit(1'u8)
+    doAssert v == 0b0000_0001'u8
+
+  v.clearMask(1.T shl bit)
+
+proc flipBit*[T: SomeInteger](v: var T; bit: BitsRange[T]) {.inline.} =
+  ## Mutates `v`, with the bit at position `bit` flipped.
+  runnableExamples:
+    var v = 0b0000_0011'u8
+    v.flipBit(1'u8)
+    doAssert v == 0b0000_0001'u8
+
+    v = 0b0000_0011'u8
+    v.flipBit(2'u8)
+    doAssert v == 0b0000_0111'u8
+
+  v.flipMask(1.T shl bit)
+
+macro setBits*(v: typed; bits: varargs[typed]): untyped =
+  ## Mutates `v`, with the bits at positions `bits` set to 1.
+  runnableExamples:
+    var v = 0b0000_0011'u8
+    v.setBits(3, 5, 7)
+    doAssert v == 0b1010_1011'u8
+
+  bits.expectKind(nnkBracket)
+  result = newStmtList()
+  for bit in bits:
+    result.add newCall("setBit", v, bit)
+
+macro clearBits*(v: typed; bits: varargs[typed]): untyped =
+  ## Mutates `v`, with the bits at positions `bits` set to 0.
+  runnableExamples:
+    var v = 0b1111_1111'u8
+    v.clearBits(1, 3, 5, 7)
+    doAssert v == 0b0101_0101'u8
+
+  bits.expectKind(nnkBracket)
+  result = newStmtList()
+  for bit in bits:
+    result.add newCall("clearBit", v, bit)
+
+macro flipBits*(v: typed; bits: varargs[typed]): untyped =
+  ## Mutates `v`, with the bits at positions `bits` set to 0.
+  runnableExamples:
+    var v = 0b0000_1111'u8
+    v.flipBits(1, 3, 5, 7)
+    doAssert v == 0b1010_0101'u8
+
+  bits.expectKind(nnkBracket)
+  result = newStmtList()
+  for bit in bits:
+    result.add newCall("flipBit", v, bit)
+
+
+proc testBit*[T: SomeInteger](v: T; bit: BitsRange[T]): bool {.inline.} =
+  ## Returns true if the bit in `v` at positions `bit` is set to 1.
+  runnableExamples:
+    let v = 0b0000_1111'u8
+    doAssert v.testBit(0)
+    doAssert not v.testBit(7)
+
+  let mask = 1.T shl bit
+  return (v and mask) == mask
 
 # #### Pure Nim version ####
 
-proc firstSetBit_nim(x: uint32): int {.inline, nosideeffect.} =
+func firstSetBitNim(x: uint32): int {.inline.} =
   ## Returns the 1-based index of the least significant set bit of x, or if x is zero, returns zero.
   # https://graphics.stanford.edu/%7Eseander/bithacks.html#ZerosOnRightMultLookup
   const lookup: array[32, uint8] = [0'u8, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15,
     25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9]
-  var v = x.uint32
-  var k = not v + 1 # get two's complement # cast[uint32](-cast[int32](v))
+  let v = x.uint32
+  let k = not v + 1 # get two's complement # cast[uint32](-cast[int32](v))
   result = 1 + lookup[uint32((v and k) * 0x077CB531'u32) shr 27].int
 
-proc firstSetBit_nim(x: uint64): int {.inline, nosideeffect.} =
+func firstSetBitNim(x: uint64): int {.inline.} =
   ## Returns the 1-based index of the least significant set bit of x, or if x is zero, returns zero.
   # https://graphics.stanford.edu/%7Eseander/bithacks.html#ZerosOnRightMultLookup
-  var v = uint64(x)
+  let v = uint64(x)
   var k = uint32(v and 0xFFFFFFFF'u32)
   if k == 0:
     k = uint32(v shr 32'u32) and 0xFFFFFFFF'u32
     result = 32
-  result += firstSetBit_nim(k)
+  else:
+    result = 0
+  result += firstSetBitNim(k)
 
-proc fastlog2_nim(x: uint32): int {.inline, nosideeffect.} =
+func fastlog2Nim(x: uint32): int {.inline.} =
   ## Quickly find the log base 2 of a 32-bit or less integer.
   # https://graphics.stanford.edu/%7Eseander/bithacks.html#IntegerLogDeBruijn
   # https://stackoverflow.com/questions/11376288/fast-computing-of-log2-for-64-bit-integers
@@ -67,7 +395,7 @@ proc fastlog2_nim(x: uint32): int {.inline, nosideeffect.} =
   v = v or v shr 16
   result = lookup[uint32(v * 0x07C4ACDD'u32) shr 27].int
 
-proc fastlog2_nim(x: uint64): int {.inline, nosideeffect.} =
+func fastlog2Nim(x: uint64): int {.inline.} =
   ## Quickly find the log base 2 of a 64-bit integer.
   # https://graphics.stanford.edu/%7Eseander/bithacks.html#IntegerLogDeBruijn
   # https://stackoverflow.com/questions/11376288/fast-computing-of-log2-for-64-bit-integers
@@ -84,27 +412,13 @@ proc fastlog2_nim(x: uint64): int {.inline, nosideeffect.} =
   v = v or v shr 32
   result = lookup[(v * 0x03F6EAF2CD271461'u64) shr 58].int
 
+import system/countbits_impl
 
-proc countSetBits_nim(n: uint32): int {.inline, noSideEffect.} =
-  ## Counts the set bits in integer. (also called Hamming weight.)
-  # generic formula is from: https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
-
-  var v = uint32(n)
-  v = v - ((v shr 1) and 0x55555555)
-  v = (v and 0x33333333) + ((v shr 2) and 0x33333333)
-  result = (((v + (v shr 4) and 0xF0F0F0F) * 0x1010101) shr 24).int
-
-proc countSetBits_nim(n: uint64): int {.inline, noSideEffect.} =
-  ## Counts the set bits in integer. (also called Hamming weight.)
-  # generic formula is from: https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
-  var v = uint64(n)
-  v = v - ((v shr 1'u64) and 0x5555555555555555'u64)
-  v = (v and 0x3333333333333333'u64) + ((v shr 2'u64) and 0x3333333333333333'u64)
-  v = (v + (v shr 4'u64) and 0x0F0F0F0F0F0F0F0F'u64)
-  result = ((v * 0x0101010101010101'u64) shr 56'u64).int
-
+const useBuiltinsRotate = (defined(amd64) or defined(i386)) and
+                          (defined(gcc) or defined(clang) or defined(vcc) or
+                           (defined(icl) and not defined(cpp))) and useBuiltins
 
-template parity_impl[T](value: T): int =
+template parityImpl[T](value: T): int =
   # formula id from: https://graphics.stanford.edu/%7Eseander/bithacks.html#ParityParallel
   var v = value
   when sizeof(T) == 8:
@@ -119,10 +433,6 @@ template parity_impl[T](value: T): int =
 
 
 when useGCC_builtins:
-  # Returns the number of set 1-bits in value.
-  proc builtin_popcount(x: cuint): cint {.importc: "__builtin_popcount", cdecl.}
-  proc builtin_popcountll(x: culonglong): cint {.importc: "__builtin_popcountll", cdecl.}
-
   # Returns the bit parity in value
   proc builtin_parity(x: cuint): cint {.importc: "__builtin_parity", cdecl.}
   proc builtin_parityll(x: culonglong): cint {.importc: "__builtin_parityll", cdecl.}
@@ -140,182 +450,200 @@ when useGCC_builtins:
   proc builtin_ctzll(x: culonglong): cint {.importc: "__builtin_ctzll", cdecl.}
 
 elif useVCC_builtins:
-  # Counts the number of one bits (population count) in a 16-, 32-, or 64-byte unsigned integer.
-  proc builtin_popcnt16(a2: uint16): uint16 {.importc: "__popcnt16" header: "<intrin.h>", nosideeffect.}
-  proc builtin_popcnt32(a2: uint32): uint32 {.importc: "__popcnt" header: "<intrin.h>", nosideeffect.}
-  proc builtin_popcnt64(a2: uint64): uint64 {.importc: "__popcnt64" header: "<intrin.h>", nosideeffect.}
-
   # Search the mask data from most significant bit (MSB) to least significant bit (LSB) for a set bit (1).
-  proc bitScanReverse(index: ptr culong, mask: culong): cuchar {.importc: "_BitScanReverse", header: "<intrin.h>", nosideeffect.}
-  proc bitScanReverse64(index: ptr culong, mask: uint64): cuchar {.importc: "_BitScanReverse64", header: "<intrin.h>", nosideeffect.}
+  func bitScanReverse(index: ptr culong, mask: culong): uint8 {.
+      importc: "_BitScanReverse", header: "<intrin.h>".}
+  func bitScanReverse64(index: ptr culong, mask: uint64): uint8 {.
+      importc: "_BitScanReverse64", header: "<intrin.h>".}
 
   # Search the mask data from least significant bit (LSB) to the most significant bit (MSB) for a set bit (1).
-  proc bitScanForward(index: ptr culong, mask: culong): cuchar {.importc: "_BitScanForward", header: "<intrin.h>", nosideeffect.}
-  proc bitScanForward64(index: ptr culong, mask: uint64): cuchar {.importc: "_BitScanForward64", header: "<intrin.h>", nosideeffect.}
+  func bitScanForward(index: ptr culong, mask: culong): uint8 {.
+      importc: "_BitScanForward", header: "<intrin.h>".}
+  func bitScanForward64(index: ptr culong, mask: uint64): uint8 {.
+      importc: "_BitScanForward64", header: "<intrin.h>".}
 
   template vcc_scan_impl(fnc: untyped; v: untyped): int =
-    var index: culong
+    var index {.inject.}: culong = 0
     discard fnc(index.addr, v)
     index.int
 
 elif useICC_builtins:
-
-  # Intel compiler intrinsics: http://fulla.fnal.gov/intel/compiler_c/main_cls/intref_cls/common/intref_allia_misc.htm
-  # see also: https://software.intel.com/en-us/node/523362
-  # Count the number of bits set to 1 in an integer a, and return that count in dst.
-  proc builtin_popcnt32(a: cint): cint {.importc: "_popcnt" header: "<immintrin.h>", nosideeffect.}
-  proc builtin_popcnt64(a: uint64): cint {.importc: "_popcnt64" header: "<immintrin.h>", nosideeffect.}
-
   # Returns the number of trailing 0-bits in x, starting at the least significant bit position. If x is 0, the result is undefined.
-  proc bitScanForward(p: ptr uint32, b: uint32): cuchar {.importc: "_BitScanForward", header: "<immintrin.h>", nosideeffect.}
-  proc bitScanForward64(p: ptr uint32, b: uint64): cuchar {.importc: "_BitScanForward64", header: "<immintrin.h>", nosideeffect.}
+  func bitScanForward(p: ptr uint32, b: uint32): uint8 {.
+      importc: "_BitScanForward", header: "<immintrin.h>".}
+  func bitScanForward64(p: ptr uint32, b: uint64): uint8 {.
+      importc: "_BitScanForward64", header: "<immintrin.h>".}
 
   # Returns the number of leading 0-bits in x, starting at the most significant bit position. If x is 0, the result is undefined.
-  proc bitScanReverse(p: ptr uint32, b: uint32): cuchar {.importc: "_BitScanReverse", header: "<immintrin.h>", nosideeffect.}
-  proc bitScanReverse64(p: ptr uint32, b: uint64): cuchar {.importc: "_BitScanReverse64", header: "<immintrin.h>", nosideeffect.}
+  func bitScanReverse(p: ptr uint32, b: uint32): uint8 {.
+      importc: "_BitScanReverse", header: "<immintrin.h>".}
+  func bitScanReverse64(p: ptr uint32, b: uint64): uint8 {.
+      importc: "_BitScanReverse64", header: "<immintrin.h>".}
 
   template icc_scan_impl(fnc: untyped; v: untyped): int =
     var index: uint32
     discard fnc(index.addr, v)
     index.int
 
+func countSetBits*(x: SomeInteger): int {.inline.} =
+  ## Counts the set bits in an integer (also called `Hamming weight`:idx:).
+  runnableExamples:
+    doAssert countSetBits(0b0000_0011'u8) == 2
+    doAssert countSetBits(0b1010_1010'u8) == 4
 
-proc countSetBits*(x: SomeInteger): int {.inline, nosideeffect.} =
-  ## Counts the set bits in integer. (also called `Hamming weight`:idx:.)
-  # TODO: figure out if ICC support _popcnt32/_popcnt64 on platform without POPCNT.
-  # like GCC and MSVC
-  when nimvm:
-    when sizeof(x) <= 4: result = countSetBits_nim(x.uint32)
-    else:                result = countSetBits_nim(x.uint64)
-  else:
-    when useGCC_builtins:
-      when sizeof(x) <= 4: result = builtin_popcount(x.cuint).int
-      else:                result = builtin_popcountll(x.culonglong).int
-    elif useVCC_builtins:
-      when sizeof(x) <= 2: result = builtin_popcnt16(x.uint16).int
-      elif sizeof(x) <= 4: result = builtin_popcnt32(x.uint32).int
-      elif arch64:         result = builtin_popcnt64(x.uint64).int
-      else:                result = builtin_popcnt32((x.uint64 and 0xFFFFFFFF'u64).uint32 ).int +
-                                    builtin_popcnt32((x.uint64 shr 32'u64).uint32 ).int
-    elif useICC_builtins:
-      when sizeof(x) <= 4: result = builtin_popcnt32(x.cint).int
-      elif arch64:         result = builtin_popcnt64(x.uint64).int
-      else:                result = builtin_popcnt32((x.uint64 and 0xFFFFFFFF'u64).cint ).int +
-                                    builtin_popcnt32((x.uint64 shr 32'u64).cint ).int
-    else:
-      when sizeof(x) <= 4: result = countSetBits_nim(x.uint32)
-      else:                result = countSetBits_nim(x.uint64)
+  result = countSetBitsImpl(x)
 
-proc popcount*(x: SomeInteger): int {.inline, nosideeffect.} =
-  ## Alias for for countSetBits (Hamming weight.)
+func popcount*(x: SomeInteger): int {.inline.} =
+  ## Alias for `countSetBits <#countSetBits,SomeInteger>`_ (Hamming weight).
   result = countSetBits(x)
 
-proc parityBits*(x: SomeInteger): int {.inline, nosideeffect.} =
-  ## Calculate the bit parity in integer. If number of 1-bit
-  ## is odd parity is 1, otherwise 0.
+func parityBits*(x: SomeInteger): int {.inline.} =
+  ## Calculate the bit parity in an integer. If the number of 1-bits
+  ## is odd, the parity is 1, otherwise 0.
+  runnableExamples:
+    doAssert parityBits(0b0000_0000'u8) == 0
+    doAssert parityBits(0b0101_0001'u8) == 1
+    doAssert parityBits(0b0110_1001'u8) == 0
+    doAssert parityBits(0b0111_1111'u8) == 1
+
   # Can be used a base if creating ASM version.
   # https://stackoverflow.com/questions/21617970/how-to-check-if-value-has-even-parity-of-bits-or-odd
+  let x = x.castToUnsigned
   when nimvm:
-    when sizeof(x) <= 4: result = parity_impl(x.uint32)
-    else:                result = parity_impl(x.uint64)
+    result = forwardImpl(parityImpl, x)
   else:
     when useGCC_builtins:
       when sizeof(x) <= 4: result = builtin_parity(x.uint32).int
-      else:                result = builtin_parityll(x.uint64).int
+      else: result = builtin_parityll(x.uint64).int
     else:
-      when sizeof(x) <= 4: result = parity_impl(x.uint32)
-      else:                result = parity_impl(x.uint64)
+      when sizeof(x) <= 4: result = parityImpl(x.uint32)
+      else: result = parityImpl(x.uint64)
+
+func firstSetBit*(x: SomeInteger): int {.inline.} =
+  ## Returns the 1-based index of the least significant set bit of `x`.
+  ## If `x` is zero, when `noUndefinedBitOpts` is set, the result is 0,
+  ## otherwise the result is undefined.
+  runnableExamples:
+    doAssert firstSetBit(0b0000_0001'u8) == 1
+    doAssert firstSetBit(0b0000_0010'u8) == 2
+    doAssert firstSetBit(0b0000_0100'u8) == 3
+    doAssert firstSetBit(0b0000_1000'u8) == 4
+    doAssert firstSetBit(0b0000_1111'u8) == 1
 
-proc firstSetBit*(x: SomeInteger): int {.inline, nosideeffect.} =
-  ## Returns the 1-based index of the least significant set bit of x.
-  ## If `x` is zero, when ``noUndefinedBitOpts`` is set, result is 0,
-  ## otherwise result is undefined.
   # GCC builtin 'builtin_ffs' already handle zero input.
+  let x = x.castToUnsigned
   when nimvm:
     when noUndefined:
       if x == 0:
         return 0
-    when sizeof(x) <= 4: result = firstSetBit_nim(x.uint32)
-    else:                result = firstSetBit_nim(x.uint64)
+    result = forwardImpl(firstSetBitNim, x)
   else:
     when noUndefined and not useGCC_builtins:
       if x == 0:
         return 0
     when useGCC_builtins:
       when sizeof(x) <= 4: result = builtin_ffs(cast[cint](x.cuint)).int
-      else:                result = builtin_ffsll(cast[clonglong](x.culonglong)).int
+      else: result = builtin_ffsll(cast[clonglong](x.culonglong)).int
     elif useVCC_builtins:
       when sizeof(x) <= 4:
         result = 1 + vcc_scan_impl(bitScanForward, x.culong)
       elif arch64:
         result = 1 + vcc_scan_impl(bitScanForward64, x.uint64)
       else:
-        result = firstSetBit_nim(x.uint64)
+        result = firstSetBitNim(x.uint64)
     elif useICC_builtins:
       when sizeof(x) <= 4:
         result = 1 + icc_scan_impl(bitScanForward, x.uint32)
       elif arch64:
         result = 1 + icc_scan_impl(bitScanForward64, x.uint64)
       else:
-        result = firstSetBit_nim(x.uint64)
+        result = firstSetBitNim(x.uint64)
     else:
-      when sizeof(x) <= 4: result = firstSetBit_nim(x.uint32)
-      else:                result = firstSetBit_nim(x.uint64)
+      when sizeof(x) <= 4: result = firstSetBitNim(x.uint32)
+      else: result = firstSetBitNim(x.uint64)
 
-proc fastLog2*(x: SomeInteger): int {.inline, nosideeffect.} =
+func fastLog2*(x: SomeInteger): int {.inline.} =
   ## Quickly find the log base 2 of an integer.
-  ## If `x` is zero, when ``noUndefinedBitOpts`` is set, result is -1,
-  ## otherwise result is undefined.
+  ## If `x` is zero, when `noUndefinedBitOpts` is set, the result is -1,
+  ## otherwise the result is undefined.
+  runnableExamples:
+    doAssert fastLog2(0b0000_0001'u8) == 0
+    doAssert fastLog2(0b0000_0010'u8) == 1
+    doAssert fastLog2(0b0000_0100'u8) == 2
+    doAssert fastLog2(0b0000_1000'u8) == 3
+    doAssert fastLog2(0b0000_1111'u8) == 3
+
+  let x = x.castToUnsigned
   when noUndefined:
     if x == 0:
       return -1
   when nimvm:
-    when sizeof(x) <= 4: result = fastlog2_nim(x.uint32)
-    else:                result = fastlog2_nim(x.uint64)
+    result = forwardImpl(fastlog2Nim, x)
   else:
     when useGCC_builtins:
       when sizeof(x) <= 4: result = 31 - builtin_clz(x.uint32).int
-      else:                result = 63 - builtin_clzll(x.uint64).int
+      else: result = 63 - builtin_clzll(x.uint64).int
     elif useVCC_builtins:
       when sizeof(x) <= 4:
         result = vcc_scan_impl(bitScanReverse, x.culong)
       elif arch64:
         result = vcc_scan_impl(bitScanReverse64, x.uint64)
       else:
-        result = fastlog2_nim(x.uint64)
+        result = fastlog2Nim(x.uint64)
     elif useICC_builtins:
       when sizeof(x) <= 4:
         result = icc_scan_impl(bitScanReverse, x.uint32)
       elif arch64:
         result = icc_scan_impl(bitScanReverse64, x.uint64)
       else:
-        result = fastlog2_nim(x.uint64)
+        result = fastlog2Nim(x.uint64)
     else:
-      when sizeof(x) <= 4: result = fastlog2_nim(x.uint32)
-      else:                result = fastlog2_nim(x.uint64)
-
-proc countLeadingZeroBits*(x: SomeInteger): int {.inline, nosideeffect.} =
-  ## Returns the number of leading zero bits in integer.
-  ## If `x` is zero, when ``noUndefinedBitOpts`` is set, result is 0,
-  ## otherwise result is undefined.
+      when sizeof(x) <= 4: result = fastlog2Nim(x.uint32)
+      else: result = fastlog2Nim(x.uint64)
+
+func countLeadingZeroBits*(x: SomeInteger): int {.inline.} =
+  ## Returns the number of leading zero bits in an integer.
+  ## If `x` is zero, when `noUndefinedBitOpts` is set, the result is 0,
+  ## otherwise the result is undefined.
+  ##
+  ## **See also:**
+  ## * `countTrailingZeroBits proc <#countTrailingZeroBits,SomeInteger>`_
+  runnableExamples:
+    doAssert countLeadingZeroBits(0b0000_0001'u8) == 7
+    doAssert countLeadingZeroBits(0b0000_0010'u8) == 6
+    doAssert countLeadingZeroBits(0b0000_0100'u8) == 5
+    doAssert countLeadingZeroBits(0b0000_1000'u8) == 4
+    doAssert countLeadingZeroBits(0b0000_1111'u8) == 4
+
+  let x = x.castToUnsigned
   when noUndefined:
     if x == 0:
       return 0
   when nimvm:
-      when sizeof(x) <= 4: result = sizeof(x)*8 - 1 - fastlog2_nim(x.uint32)
-      else:                result = sizeof(x)*8 - 1 - fastlog2_nim(x.uint64)
+    result = sizeof(x)*8 - 1 - forwardImpl(fastlog2Nim, x)
   else:
     when useGCC_builtins:
       when sizeof(x) <= 4: result = builtin_clz(x.uint32).int - (32 - sizeof(x)*8)
-      else:                result = builtin_clzll(x.uint64).int
+      else: result = builtin_clzll(x.uint64).int
     else:
-      when sizeof(x) <= 4: result = sizeof(x)*8 - 1 - fastlog2_nim(x.uint32)
-      else:                result = sizeof(x)*8 - 1 - fastlog2_nim(x.uint64)
-
-proc countTrailingZeroBits*(x: SomeInteger): int {.inline, nosideeffect.} =
-  ## Returns the number of trailing zeros in integer.
-  ## If `x` is zero, when ``noUndefinedBitOpts`` is set, result is 0,
-  ## otherwise result is undefined.
+      when sizeof(x) <= 4: result = sizeof(x)*8 - 1 - fastlog2Nim(x.uint32)
+      else: result = sizeof(x)*8 - 1 - fastlog2Nim(x.uint64)
+
+func countTrailingZeroBits*(x: SomeInteger): int {.inline.} =
+  ## Returns the number of trailing zeros in an integer.
+  ## If `x` is zero, when `noUndefinedBitOpts` is set, the result is 0,
+  ## otherwise the result is undefined.
+  ##
+  ## **See also:**
+  ## * `countLeadingZeroBits proc <#countLeadingZeroBits,SomeInteger>`_
+  runnableExamples:
+    doAssert countTrailingZeroBits(0b0000_0001'u8) == 0
+    doAssert countTrailingZeroBits(0b0000_0010'u8) == 1
+    doAssert countTrailingZeroBits(0b0000_0100'u8) == 2
+    doAssert countTrailingZeroBits(0b0000_1000'u8) == 3
+    doAssert countTrailingZeroBits(0b0000_1111'u8) == 0
+
+  let x = x.castToUnsigned
   when noUndefined:
     if x == 0:
       return 0
@@ -324,60 +652,232 @@ proc countTrailingZeroBits*(x: SomeInteger): int {.inline, nosideeffect.} =
   else:
     when useGCC_builtins:
       when sizeof(x) <= 4: result = builtin_ctz(x.uint32).int
-      else:                result = builtin_ctzll(x.uint64).int
+      else: result = builtin_ctzll(x.uint64).int
     else:
       result = firstSetBit(x) - 1
 
-
-proc rotateLeftBits*(value: uint8;
-           amount: range[0..8]): uint8 {.inline, noSideEffect.} =
-  ## Left-rotate bits in a 8-bits value.
-  # using this form instead of the one below should handle any value
-  # out of range as well as negative values.
-  # result = (value shl amount) or (value shr (8 - amount))
-  # taken from: https://en.wikipedia.org/wiki/Circular_shift#Implementing_circular_shifts
-  let amount = amount and 7
-  result = (value shl amount) or (value shr ( (-amount) and 7))
-
-proc rotateLeftBits*(value: uint16;
-           amount: range[0..16]): uint16 {.inline, noSideEffect.} =
-  ## Left-rotate bits in a 16-bits value.
-  let amount = amount and 15
-  result = (value shl amount) or (value shr ( (-amount) and 15))
-
-proc rotateLeftBits*(value: uint32;
-           amount: range[0..32]): uint32 {.inline, noSideEffect.} =
-  ## Left-rotate bits in a 32-bits value.
-  let amount = amount and 31
-  result = (value shl amount) or (value shr ( (-amount) and 31))
-
-proc rotateLeftBits*(value: uint64;
-           amount: range[0..64]): uint64 {.inline, noSideEffect.} =
-  ## Left-rotate bits in a 64-bits value.
-  let amount = amount and 63
-  result = (value shl amount) or (value shr ( (-amount) and 63))
-
-
-proc rotateRightBits*(value: uint8;
-            amount: range[0..8]): uint8 {.inline, noSideEffect.} =
-  ## Right-rotate bits in a 8-bits value.
-  let amount = amount and 7
-  result = (value shr amount) or (value shl ( (-amount) and 7))
-
-proc rotateRightBits*(value: uint16;
-            amount: range[0..16]): uint16 {.inline, noSideEffect.} =
-  ## Right-rotate bits in a 16-bits value.
-  let amount = amount and 15
-  result = (value shr amount) or (value shl ( (-amount) and 15))
-
-proc rotateRightBits*(value: uint32;
-            amount: range[0..32]): uint32 {.inline, noSideEffect.} =
-  ## Right-rotate bits in a 32-bits value.
-  let amount = amount and 31
-  result = (value shr amount) or (value shl ( (-amount) and 31))
-
-proc rotateRightBits*(value: uint64;
-            amount: range[0..64]): uint64 {.inline, noSideEffect.} =
-  ## Right-rotate bits in a 64-bits value.
-  let amount = amount and 63
-  result = (value shr amount) or (value shl ( (-amount) and 63))
+when useBuiltinsRotate:
+  when defined(gcc):
+    # GCC was tested until version 4.8.1 and intrinsics were present. Not tested
+    # in previous versions.
+    func builtin_rotl8(value: uint8, shift: cint): uint8
+                      {.importc: "__rolb", header: "<x86intrin.h>".}
+    func builtin_rotl16(value: cushort, shift: cint): cushort
+                       {.importc: "__rolw", header: "<x86intrin.h>".}
+    func builtin_rotl32(value: cuint, shift: cint): cuint
+                       {.importc: "__rold", header: "<x86intrin.h>".}
+    when defined(amd64):
+      func builtin_rotl64(value: culonglong, shift: cint): culonglong
+                         {.importc: "__rolq", header: "<x86intrin.h>".}
+
+    func builtin_rotr8(value: uint8, shift: cint): uint8
+                      {.importc: "__rorb", header: "<x86intrin.h>".}
+    func builtin_rotr16(value: cushort, shift: cint): cushort
+                       {.importc: "__rorw", header: "<x86intrin.h>".}
+    func builtin_rotr32(value: cuint, shift: cint): cuint
+                       {.importc: "__rord", header: "<x86intrin.h>".}
+    when defined(amd64):
+      func builtin_rotr64(value: culonglong, shift: cint): culonglong
+                         {.importc: "__rorq", header: "<x86intrin.h>".}
+  elif defined(clang):
+    # In CLANG, builtins have been present since version 8.0.0 and intrinsics
+    # since version 9.0.0. This implementation chose the builtins, as they have
+    # been around for longer.
+    # https://releases.llvm.org/8.0.0/tools/clang/docs/ReleaseNotes.html#non-comprehensive-list-of-changes-in-this-release
+    # https://releases.llvm.org/8.0.0/tools/clang/docs/LanguageExtensions.html#builtin-rotateleft
+    # source for correct declarations: https://github.com/llvm/llvm-project/blob/main/clang/include/clang/Basic/Builtins.def
+    func builtin_rotl8(value: uint8, shift: uint8): uint8
+                      {.importc: "__builtin_rotateleft8", nodecl.}
+    func builtin_rotl16(value: cushort, shift: cushort): cushort
+                       {.importc: "__builtin_rotateleft16", nodecl.}
+    func builtin_rotl32(value: cuint, shift: cuint): cuint
+                       {.importc: "__builtin_rotateleft32", nodecl.}
+    when defined(amd64):
+      func builtin_rotl64(value: culonglong, shift: culonglong): culonglong
+                         {.importc: "__builtin_rotateleft64", nodecl.}
+
+    func builtin_rotr8(value: uint8, shift: uint8): uint8
+                      {.importc: "__builtin_rotateright8", nodecl.}
+    func builtin_rotr16(value: cushort, shift: cushort): cushort
+                       {.importc: "__builtin_rotateright16", nodecl.}
+    func builtin_rotr32(value: cuint, shift: cuint): cuint
+                       {.importc: "__builtin_rotateright32", nodecl.}
+    when defined(amd64):
+      # shift is unsigned, refs https://github.com/llvm-mirror/clang/commit/892de415b7fde609dafc4e6c1643b7eaa0150a4d
+      func builtin_rotr64(value: culonglong, shift: culonglong): culonglong
+                         {.importc: "__builtin_rotateright64", nodecl.}
+  elif defined(vcc):
+    # Tested on Microsoft (R) C/C++ Optimizing Compiler 19.28.29335 x64 and x86.
+    # Not tested in previous versions.
+    # https://docs.microsoft.com/en-us/cpp/intrinsics/rotl8-rotl16?view=msvc-160
+    # https://docs.microsoft.com/en-us/cpp/intrinsics/rotr8-rotr16?view=msvc-160
+    # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/rotl-rotl64-rotr-rotr64?view=msvc-160
+    func builtin_rotl8(value: uint8, shift: uint8): uint8
+                      {.importc: "_rotl8", header: "<intrin.h>".}
+    func builtin_rotl16(value: cushort, shift: uint8): cushort
+                       {.importc: "_rotl16", header: "<intrin.h>".}
+    func builtin_rotl32(value: cuint, shift: cint): cuint
+                       {.importc: "_rotl", header: "<stdlib.h>".}
+    when defined(amd64):
+      func builtin_rotl64(value: culonglong, shift: cint): culonglong
+                         {.importc: "_rotl64", header: "<stdlib.h>".}
+
+    func builtin_rotr8(value: uint8, shift: uint8): uint8
+                      {.importc: "_rotr8", header: "<intrin.h>".}
+    func builtin_rotr16(value: cushort, shift: uint8): cushort
+                       {.importc: "_rotr16", header: "<intrin.h>".}
+    func builtin_rotr32(value: cuint, shift: cint): cuint
+                       {.importc: "_rotr", header: "<stdlib.h>".}
+    when defined(amd64):
+      func builtin_rotr64(value: culonglong, shift: cint): culonglong
+                         {.importc: "_rotr64", header: "<stdlib.h>".}
+  elif defined(icl):
+    # Tested on Intel(R) C++ Intel(R) 64 Compiler Classic Version 2021.1.2 Build
+    # 20201208_000000 x64 and x86. Not tested in previous versions.
+    func builtin_rotl8(value: uint8, shift: cint): uint8
+                      {.importc: "__rolb", header: "<immintrin.h>".}
+    func builtin_rotl16(value: cushort, shift: cint): cushort
+                       {.importc: "__rolw", header: "<immintrin.h>".}
+    func builtin_rotl32(value: cuint, shift: cint): cuint
+                       {.importc: "__rold", header: "<immintrin.h>".}
+    when defined(amd64):
+      func builtin_rotl64(value: culonglong, shift: cint): culonglong
+                         {.importc: "__rolq", header: "<immintrin.h>".}
+
+    func builtin_rotr8(value: uint8, shift: cint): uint8
+                      {.importc: "__rorb", header: "<immintrin.h>".}
+    func builtin_rotr16(value: cushort, shift: cint): cushort
+                       {.importc: "__rorw", header: "<immintrin.h>".}
+    func builtin_rotr32(value: cuint, shift: cint): cuint
+                       {.importc: "__rord", header: "<immintrin.h>".}
+    when defined(amd64):
+      func builtin_rotr64(value: culonglong, shift: cint): culonglong
+                         {.importc: "__rorq", header: "<immintrin.h>".}
+
+func rotl[T: SomeUnsignedInt](value: T, rot: int32): T {.inline.} =
+  ## Left-rotate bits in a `value`.
+  # https://stackoverflow.com/a/776523
+  const mask = 8 * sizeof(value) - 1
+  let rot = rot and mask
+  (value shl rot) or (value shr ((-rot) and mask))
+
+func rotr[T: SomeUnsignedInt](value: T, rot: int32): T {.inline.} =
+  ## Right-rotate bits in a `value`.
+  const mask = 8 * sizeof(value) - 1
+  let rot = rot and mask
+  (value shr rot) or (value shl ((-rot) and mask))
+
+func shiftTypeTo(size: static int, shift: int): auto {.inline.} =
+  ## Returns the `shift` for the rotation according to the compiler and the
+  ## `size`.
+  when (defined(vcc) and (size in [4, 8])) or defined(gcc) or defined(icl):
+    cint(shift)
+  elif (defined(vcc) and (size in [1, 2])) or (defined(clang) and size == 1):
+    uint8(shift)
+  elif defined(clang):
+    when size == 2:
+      cushort(shift)
+    elif size == 4:
+      cuint(shift)
+    elif size == 8:
+      culonglong(shift)
+
+func rotateLeftBits*[T: SomeUnsignedInt](value: T, shift: range[0..(sizeof(T) * 8)]): T {.inline.} =
+  ## Left-rotate bits in a `value`.
+  runnableExamples:
+    doAssert rotateLeftBits(0b0110_1001'u8, 4) == 0b1001_0110'u8
+    doAssert rotateLeftBits(0b00111100_11000011'u16, 8) ==
+      0b11000011_00111100'u16
+    doAssert rotateLeftBits(0b0000111111110000_1111000000001111'u32, 16) ==
+      0b1111000000001111_0000111111110000'u32
+    doAssert rotateLeftBits(0b00000000111111111111111100000000_11111111000000000000000011111111'u64, 32) ==
+      0b11111111000000000000000011111111_00000000111111111111111100000000'u64
+  when nimvm:
+    rotl(value, shift.int32)
+  else:
+    when useBuiltinsRotate:
+      const size = sizeof(T)
+      when size == 1:
+        builtin_rotl8(value.uint8, shiftTypeTo(size, shift)).T
+      elif size == 2:
+        builtin_rotl16(value.cushort, shiftTypeTo(size, shift)).T
+      elif size == 4:
+        builtin_rotl32(value.cuint, shiftTypeTo(size, shift)).T
+      elif size == 8 and arch64:
+        builtin_rotl64(value.culonglong, shiftTypeTo(size, shift)).T
+      else:
+        rotl(value, shift.int32)
+    else:
+      rotl(value, shift.int32)
+
+func rotateRightBits*[T: SomeUnsignedInt](value: T, shift: range[0..(sizeof(T) * 8)]): T {.inline.} =
+  ## Right-rotate bits in a `value`.
+  runnableExamples:
+    doAssert rotateRightBits(0b0110_1001'u8, 4) == 0b1001_0110'u8
+    doAssert rotateRightBits(0b00111100_11000011'u16, 8) ==
+      0b11000011_00111100'u16
+    doAssert rotateRightBits(0b0000111111110000_1111000000001111'u32, 16) ==
+      0b1111000000001111_0000111111110000'u32
+    doAssert rotateRightBits(0b00000000111111111111111100000000_11111111000000000000000011111111'u64, 32) ==
+      0b11111111000000000000000011111111_00000000111111111111111100000000'u64
+  when nimvm:
+    rotr(value, shift.int32)
+  else:
+    when useBuiltinsRotate:
+      const size = sizeof(T)
+      when size == 1:
+        builtin_rotr8(value.uint8, shiftTypeTo(size, shift)).T
+      elif size == 2:
+        builtin_rotr16(value.cushort, shiftTypeTo(size, shift)).T
+      elif size == 4:
+        builtin_rotr32(value.cuint, shiftTypeTo(size, shift)).T
+      elif size == 8 and arch64:
+        builtin_rotr64(value.culonglong, shiftTypeTo(size, shift)).T
+      else:
+        rotr(value, shift.int32)
+    else:
+      rotr(value, shift.int32)
+
+func repeatBits[T: SomeUnsignedInt](x: SomeUnsignedInt; retType: type[T]): T  =
+  result = x
+  var i = 1
+  while i != (sizeof(T) div sizeof(x)):
+    result = (result shl (sizeof(x)*8*i)) or result
+    i *= 2
+
+func reverseBits*[T: SomeUnsignedInt](x: T): T =
+  ## Return the bit reversal of x.
+  runnableExamples:
+    doAssert reverseBits(0b10100100'u8) == 0b00100101'u8
+    doAssert reverseBits(0xdd'u8) == 0xbb'u8
+    doAssert reverseBits(0xddbb'u16) == 0xddbb'u16
+    doAssert reverseBits(0xdeadbeef'u32) == 0xf77db57b'u32
+
+  template repeat(x: SomeUnsignedInt): T = repeatBits(x, T)
+
+  result = x
+  result =
+    ((repeat(0x55u8) and result) shl 1) or
+    ((repeat(0xaau8) and result) shr 1)
+  result =
+    ((repeat(0x33u8) and result) shl 2) or
+    ((repeat(0xccu8) and result) shr 2)
+  when sizeof(T) == 1:
+    result = (result shl 4) or (result shr 4)
+  when sizeof(T) >= 2:
+    result =
+      ((repeat(0x0fu8) and result) shl 4) or
+      ((repeat(0xf0u8) and result) shr 4)
+  when sizeof(T) == 2:
+    result = (result shl 8) or (result shr 8)
+  when sizeof(T) >= 4:
+    result =
+      ((repeat(0x00ffu16) and result) shl 8) or
+      ((repeat(0xff00u16) and result) shr 8)
+  when sizeof(T) == 4:
+    result = (result shl 16) or (result shr 16)
+  when sizeof(T) == 8:
+    result =
+      ((repeat(0x0000ffffu32) and result) shl 16) or
+      ((repeat(0xffff0000u32) and result) shr 16)
+    result = (result shl 32) or (result shr 32)
diff --git a/lib/pure/browsers.nim b/lib/pure/browsers.nim
index 6912b893c..59e2078df 100644
--- a/lib/pure/browsers.nim
+++ b/lib/pure/browsers.nim
@@ -9,34 +9,104 @@
 
 ## This module implements a simple proc for opening URLs with the user's
 ## default browser.
+##
+## Unstable API.
 
-import strutils
+import std/private/since # used by the deprecated `openDefaultBrowser()`
+
+import std/strutils
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 when defined(windows):
-  import winlean
+  import std/winlean
+  when defined(nimPreviewSlimSystem):
+    import std/widestrs
+  from std/os import absolutePath
 else:
-  import os, osproc
+  import std/os
+  when not defined(osx):
+    import std/osproc
+
+const osOpenCmd* =
+  when defined(macos) or defined(macosx) or defined(windows): "open" else: "xdg-open" ## \
+  ## Alias for the operating system specific *"open"* command,
+  ## `"open"` on OSX, MacOS and Windows, `"xdg-open"` on Linux, BSD, etc.
+
+proc prepare(s: string): string =
+  if s.contains("://"):
+    result = s
+  else:
+    result = "file://" & absolutePath(s)
+
+proc openDefaultBrowserRaw(url: string) =
+  ## note the url argument should be alreadly prepared, i.e. the url is passed "AS IS"
 
-proc openDefaultBrowser*(url: string) =
-  ## opens `url` with the user's default browser. This does not block.
-  ##
-  ## Under Windows, ``ShellExecute`` is used. Under Mac OS X the ``open``
-  ## command is used. Under Unix, it is checked if ``xdg-open`` exists and
-  ## used if it does. Otherwise the environment variable ``BROWSER`` is 
-  ## used to determine the default browser to use.
   when defined(windows):
-    var o = newWideCString("open")
+    var o = newWideCString(osOpenCmd)
     var u = newWideCString(url)
     discard shellExecuteW(0'i32, o, u, nil, nil, SW_SHOWNORMAL)
   elif defined(macosx):
-    discard execShellCmd("open " & quoteShell(url))
+    discard execShellCmd(osOpenCmd & " " & quoteShell(url))
   else:
     var u = quoteShell(url)
-    if execShellCmd("xdg-open " & u) == 0: return
-    for b in getEnv("BROWSER").string.split(PathSep):
+    if execShellCmd(osOpenCmd & " " & u) == 0: return
+    for b in getEnv("BROWSER").split(PathSep):
       try:
-        # we use ``startProcess`` here because we don't want to block!
-        discard startProcess(command=b, args=[url], options={poUsePath})
+        # we use `startProcess` here because we don't want to block!
+        discard startProcess(command = b, args = [url], options = {poUsePath})
         return
       except OSError:
         discard
+
+proc openDefaultBrowser*(url: string) =
+  ## Opens `url` with the user's default browser. This does not block.
+  ## The URL must not be empty string, to open on a blank page see `openDefaultBrowser()`.
+  ##
+  ## Under Windows, `ShellExecute` is used. Under Mac OS X the `open`
+  ## command is used. Under Unix, it is checked if `xdg-open` exists and
+  ## used if it does. Otherwise the environment variable `BROWSER` is
+  ## used to determine the default browser to use.
+  ##
+  ## This proc doesn't raise an exception on error, beware.
+  ##
+  ##   ```nim
+  ##   block: openDefaultBrowser("https://nim-lang.org")
+  ##   ```
+  doAssert url.len > 0, "URL must not be empty string"
+  openDefaultBrowserRaw(url)
+
+proc openDefaultBrowser*() {.since: (1, 1), deprecated: 
+  "not implemented, please open with a specific url instead".} =
+  ## Intends to open the user's default browser without any `url` (blank page).
+  ## This does not block.
+  ## Intends to implement IETF RFC-6694 Section 3,
+  ## ("about:blank" is reserved for a blank page).
+  ##
+  ## Beware that this intended behavior is **not** implemented and 
+  ## considered not worthy to implement here.
+  ##
+  ## The following describes the behavior of current implementation:
+  ## 
+  ##  - Under Windows, this will only cause a pop-up dialog \
+  ## asking the assocated application with `about` \
+  ## (as Windows simply treats `about:` as a protocol like `http`).
+  ##  - Under Mac OS X the `open "about:blank"` command is used.
+  ##  - Under Unix, it is checked if `xdg-open` exists and used \
+  ## if it does and open the application assocated with `text/html` mime \
+  ## (not `x-scheme-handler/http`, so maybe html-viewer \
+  ## other than your default browser is opened). \
+  ## Otherwise the environment variable `BROWSER` is used \
+  ## to determine the default browser to use.
+  ##
+  ## This proc doesn't raise an exception on error, beware.
+  ##
+  ##   ```nim
+  ##   block: openDefaultBrowser()
+  ##   ```
+  ##
+  ## **See also:**
+  ##
+  ## * https://tools.ietf.org/html/rfc6694#section-3
+  openDefaultBrowserRaw("about:blank")  # See IETF RFC-6694 Section 3.
diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim
index e0cdc2ec0..034f224ac 100644
--- a/lib/pure/cgi.nim
+++ b/lib/pure/cgi.nim
@@ -9,35 +9,32 @@
 
 ## This module implements helper procs for CGI applications. Example:
 ##
-## .. code-block:: Nim
+##   ```Nim
+##   import std/[strtabs, cgi]
 ##
-##    import strtabs, cgi
-##
-##    # Fill the values when debugging:
-##    when debug:
-##      setTestData("name", "Klaus", "password", "123456")
-##    # read the data into `myData`
-##    var myData = readData()
-##    # check that the data's variable names are "name" or "password"
-##    validateData(myData, "name", "password")
-##    # start generating content:
-##    writeContentType()
-##    # generate content:
-##    write(stdout, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n")
-##    write(stdout, "<html><head><title>Test</title></head><body>\n")
-##    writeLine(stdout, "your name: " & myData["name"])
-##    writeLine(stdout, "your password: " & myData["password"])
-##    writeLine(stdout, "</body></html>")
-
-import strutils, os, strtabs, cookies, uri
+##   # Fill the values when debugging:
+##   when debug:
+##     setTestData("name", "Klaus", "password", "123456")
+##   # read the data into `myData`
+##   var myData = readData()
+##   # check that the data's variable names are "name" or "password"
+##   validateData(myData, "name", "password")
+##   # start generating content:
+##   writeContentType()
+##   # generate content:
+##   write(stdout, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n")
+##   write(stdout, "<html><head><title>Test</title></head><body>\n")
+##   writeLine(stdout, "your name: " & myData["name"])
+##   writeLine(stdout, "your password: " & myData["password"])
+##   writeLine(stdout, "</body></html>")
+##   ```
+
+import std/[strutils, os, strtabs, cookies, uri]
 export uri.encodeUrl, uri.decodeUrl
 
-proc handleHexChar(c: char, x: var int) {.inline.} =
-  case c
-  of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
-  of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
-  of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
-  else: assert(false)
+when defined(nimPreviewSlimSystem):
+  import std/syncio
+
 
 proc addXmlChar(dest: var string, c: char) {.inline.} =
   case c
@@ -49,247 +46,215 @@ proc addXmlChar(dest: var string, c: char) {.inline.} =
 
 proc xmlEncode*(s: string): string =
   ## Encodes a value to be XML safe:
-  ## * ``"`` is replaced by ``&quot;``
-  ## * ``<`` is replaced by ``&lt;``
-  ## * ``>`` is replaced by ``&gt;``
-  ## * ``&`` is replaced by ``&amp;``
+  ## * `"` is replaced by `&quot;`
+  ## * `<` is replaced by `&lt;`
+  ## * `>` is replaced by `&gt;`
+  ## * `&` is replaced by `&amp;`
   ## * every other character is carried over.
   result = newStringOfCap(s.len + s.len shr 2)
   for i in 0..len(s)-1: addXmlChar(result, s[i])
 
 type
-  CgiError* = object of IOError  ## exception that is raised if a CGI error occurs
-  RequestMethod* = enum  ## the used request method
-    methodNone,          ## no REQUEST_METHOD environment variable
-    methodPost,          ## query uses the POST method
-    methodGet            ## query uses the GET method
+  CgiError* = object of IOError ## Exception that is raised if a CGI error occurs.
+  RequestMethod* = enum ## The used request method.
+    methodNone,         ## no REQUEST_METHOD environment variable
+    methodPost,         ## query uses the POST method
+    methodGet           ## query uses the GET method
 
 proc cgiError*(msg: string) {.noreturn.} =
-  ## raises an ECgi exception with message `msg`.
-  var e: ref CgiError
-  new(e)
-  e.msg = msg
-  raise e
+  ## Raises a `CgiError` exception with message `msg`.
+  raise newException(CgiError, msg)
 
 proc getEncodedData(allowedMethods: set[RequestMethod]): string =
-  case getEnv("REQUEST_METHOD").string
+  case getEnv("REQUEST_METHOD")
   of "POST":
     if methodPost notin allowedMethods:
       cgiError("'REQUEST_METHOD' 'POST' is not supported")
-    var L = parseInt(getEnv("CONTENT_LENGTH").string)
+    var L = parseInt(getEnv("CONTENT_LENGTH"))
+    if L == 0:
+      return ""
     result = newString(L)
     if readBuffer(stdin, addr(result[0]), L) != L:
       cgiError("cannot read from stdin")
   of "GET":
     if methodGet notin allowedMethods:
       cgiError("'REQUEST_METHOD' 'GET' is not supported")
-    result = getEnv("QUERY_STRING").string
+    result = getEnv("QUERY_STRING")
   else:
     if methodNone notin allowedMethods:
       cgiError("'REQUEST_METHOD' must be 'POST' or 'GET'")
 
-iterator decodeData*(data: string): tuple[key, value: TaintedString] =
+iterator decodeData*(data: string): tuple[key, value: string] =
   ## Reads and decodes CGI data and yields the (name, value) pairs the
   ## data consists of.
-  var i = 0
-  var name = ""
-  var value = ""
-  # decode everything in one pass:
-  while i < data.len:
-    setLen(name, 0) # reuse memory
-    while i < data.len:
-      case data[i]
-      of '%':
-        var x = 0
-        handleHexChar(data[i+1], x)
-        handleHexChar(data[i+2], x)
-        inc(i, 2)
-        add(name, chr(x))
-      of '+': add(name, ' ')
-      of '=', '&': break
-      else: add(name, data[i])
-      inc(i)
-    if i >= data.len or data[i] != '=': cgiError("'=' expected")
-    inc(i) # skip '='
-    setLen(value, 0) # reuse memory
-    while i < data.len:
-      case data[i]
-      of '%':
-        var x = 0
-        if i+2 < data.len:
-          handleHexChar(data[i+1], x)
-          handleHexChar(data[i+2], x)
-        inc(i, 2)
-        add(value, chr(x))
-      of '+': add(value, ' ')
-      of '&', '\0': break
-      else: add(value, data[i])
-      inc(i)
-    yield (name.TaintedString, value.TaintedString)
-    if i < data.len:
-      if data[i] == '&': inc(i)
-      else: cgiError("'&' expected")
+  for (key, value) in uri.decodeQuery(data):
+    yield (key, value)
 
 iterator decodeData*(allowedMethods: set[RequestMethod] =
-       {methodNone, methodPost, methodGet}): tuple[key, value: TaintedString] =
+       {methodNone, methodPost, methodGet}): tuple[key, value: string] =
   ## Reads and decodes CGI data and yields the (name, value) pairs the
   ## data consists of. If the client does not use a method listed in the
-  ## `allowedMethods` set, an `ECgi` exception is raised.
-  var data = getEncodedData(allowedMethods)
-  if not isNil(data):
-    for key, value in decodeData(data):
-      yield (key, value)
+  ## `allowedMethods` set, a `CgiError` exception is raised.
+  let data = getEncodedData(allowedMethods)
+  for (key, value) in uri.decodeQuery(data):
+    yield (key, value)
 
 proc readData*(allowedMethods: set[RequestMethod] =
                {methodNone, methodPost, methodGet}): StringTableRef =
-  ## Read CGI data. If the client does not use a method listed in the
-  ## `allowedMethods` set, an `ECgi` exception is raised.
+  ## Reads CGI data. If the client does not use a method listed in the
+  ## `allowedMethods` set, a `CgiError` exception is raised.
   result = newStringTable()
   for name, value in decodeData(allowedMethods):
-    result[name.string] = value.string
+    result[name] = value
+
+proc readData*(data: string): StringTableRef =
+  ## Reads CGI data from a string.
+  result = newStringTable()
+  for name, value in decodeData(data):
+    result[name] = value
 
 proc validateData*(data: StringTableRef, validKeys: varargs[string]) =
-  ## validates data; raises `ECgi` if this fails. This checks that each variable
+  ## Validates data; raises `CgiError` if this fails. This checks that each variable
   ## name of the CGI `data` occurs in the `validKeys` array.
   for key, val in pairs(data):
     if find(validKeys, key) < 0:
       cgiError("unknown variable name: " & key)
 
 proc getContentLength*(): string =
-  ## returns contents of the ``CONTENT_LENGTH`` environment variable
-  return getEnv("CONTENT_LENGTH").string
+  ## Returns contents of the `CONTENT_LENGTH` environment variable.
+  return getEnv("CONTENT_LENGTH")
 
 proc getContentType*(): string =
-  ## returns contents of the ``CONTENT_TYPE`` environment variable
-  return getEnv("CONTENT_Type").string
+  ## Returns contents of the `CONTENT_TYPE` environment variable.
+  return getEnv("CONTENT_Type")
 
 proc getDocumentRoot*(): string =
-  ## returns contents of the ``DOCUMENT_ROOT`` environment variable
-  return getEnv("DOCUMENT_ROOT").string
+  ## Returns contents of the `DOCUMENT_ROOT` environment variable.
+  return getEnv("DOCUMENT_ROOT")
 
 proc getGatewayInterface*(): string =
-  ## returns contents of the ``GATEWAY_INTERFACE`` environment variable
-  return getEnv("GATEWAY_INTERFACE").string
+  ## Returns contents of the `GATEWAY_INTERFACE` environment variable.
+  return getEnv("GATEWAY_INTERFACE")
 
 proc getHttpAccept*(): string =
-  ## returns contents of the ``HTTP_ACCEPT`` environment variable
-  return getEnv("HTTP_ACCEPT").string
+  ## Returns contents of the `HTTP_ACCEPT` environment variable.
+  return getEnv("HTTP_ACCEPT")
 
 proc getHttpAcceptCharset*(): string =
-  ## returns contents of the ``HTTP_ACCEPT_CHARSET`` environment variable
-  return getEnv("HTTP_ACCEPT_CHARSET").string
+  ## Returns contents of the `HTTP_ACCEPT_CHARSET` environment variable.
+  return getEnv("HTTP_ACCEPT_CHARSET")
 
 proc getHttpAcceptEncoding*(): string =
-  ## returns contents of the ``HTTP_ACCEPT_ENCODING`` environment variable
-  return getEnv("HTTP_ACCEPT_ENCODING").string
+  ## Returns contents of the `HTTP_ACCEPT_ENCODING` environment variable.
+  return getEnv("HTTP_ACCEPT_ENCODING")
 
 proc getHttpAcceptLanguage*(): string =
-  ## returns contents of the ``HTTP_ACCEPT_LANGUAGE`` environment variable
-  return getEnv("HTTP_ACCEPT_LANGUAGE").string
+  ## Returns contents of the `HTTP_ACCEPT_LANGUAGE` environment variable.
+  return getEnv("HTTP_ACCEPT_LANGUAGE")
 
 proc getHttpConnection*(): string =
-  ## returns contents of the ``HTTP_CONNECTION`` environment variable
-  return getEnv("HTTP_CONNECTION").string
+  ## Returns contents of the `HTTP_CONNECTION` environment variable.
+  return getEnv("HTTP_CONNECTION")
 
 proc getHttpCookie*(): string =
-  ## returns contents of the ``HTTP_COOKIE`` environment variable
-  return getEnv("HTTP_COOKIE").string
+  ## Returns contents of the `HTTP_COOKIE` environment variable.
+  return getEnv("HTTP_COOKIE")
 
 proc getHttpHost*(): string =
-  ## returns contents of the ``HTTP_HOST`` environment variable
-  return getEnv("HTTP_HOST").string
+  ## Returns contents of the `HTTP_HOST` environment variable.
+  return getEnv("HTTP_HOST")
 
 proc getHttpReferer*(): string =
-  ## returns contents of the ``HTTP_REFERER`` environment variable
-  return getEnv("HTTP_REFERER").string
+  ## Returns contents of the `HTTP_REFERER` environment variable.
+  return getEnv("HTTP_REFERER")
 
 proc getHttpUserAgent*(): string =
-  ## returns contents of the ``HTTP_USER_AGENT`` environment variable
-  return getEnv("HTTP_USER_AGENT").string
+  ## Returns contents of the `HTTP_USER_AGENT` environment variable.
+  return getEnv("HTTP_USER_AGENT")
 
 proc getPathInfo*(): string =
-  ## returns contents of the ``PATH_INFO`` environment variable
-  return getEnv("PATH_INFO").string
+  ## Returns contents of the `PATH_INFO` environment variable.
+  return getEnv("PATH_INFO")
 
 proc getPathTranslated*(): string =
-  ## returns contents of the ``PATH_TRANSLATED`` environment variable
-  return getEnv("PATH_TRANSLATED").string
+  ## Returns contents of the `PATH_TRANSLATED` environment variable.
+  return getEnv("PATH_TRANSLATED")
 
 proc getQueryString*(): string =
-  ## returns contents of the ``QUERY_STRING`` environment variable
-  return getEnv("QUERY_STRING").string
+  ## Returns contents of the `QUERY_STRING` environment variable.
+  return getEnv("QUERY_STRING")
 
 proc getRemoteAddr*(): string =
-  ## returns contents of the ``REMOTE_ADDR`` environment variable
-  return getEnv("REMOTE_ADDR").string
+  ## Returns contents of the `REMOTE_ADDR` environment variable.
+  return getEnv("REMOTE_ADDR")
 
 proc getRemoteHost*(): string =
-  ## returns contents of the ``REMOTE_HOST`` environment variable
-  return getEnv("REMOTE_HOST").string
+  ## Returns contents of the `REMOTE_HOST` environment variable.
+  return getEnv("REMOTE_HOST")
 
 proc getRemoteIdent*(): string =
-  ## returns contents of the ``REMOTE_IDENT`` environment variable
-  return getEnv("REMOTE_IDENT").string
+  ## Returns contents of the `REMOTE_IDENT` environment variable.
+  return getEnv("REMOTE_IDENT")
 
 proc getRemotePort*(): string =
-  ## returns contents of the ``REMOTE_PORT`` environment variable
-  return getEnv("REMOTE_PORT").string
+  ## Returns contents of the `REMOTE_PORT` environment variable.
+  return getEnv("REMOTE_PORT")
 
 proc getRemoteUser*(): string =
-  ## returns contents of the ``REMOTE_USER`` environment variable
-  return getEnv("REMOTE_USER").string
+  ## Returns contents of the `REMOTE_USER` environment variable.
+  return getEnv("REMOTE_USER")
 
 proc getRequestMethod*(): string =
-  ## returns contents of the ``REQUEST_METHOD`` environment variable
-  return getEnv("REQUEST_METHOD").string
+  ## Returns contents of the `REQUEST_METHOD` environment variable.
+  return getEnv("REQUEST_METHOD")
 
 proc getRequestURI*(): string =
-  ## returns contents of the ``REQUEST_URI`` environment variable
-  return getEnv("REQUEST_URI").string
+  ## Returns contents of the `REQUEST_URI` environment variable.
+  return getEnv("REQUEST_URI")
 
 proc getScriptFilename*(): string =
-  ## returns contents of the ``SCRIPT_FILENAME`` environment variable
-  return getEnv("SCRIPT_FILENAME").string
+  ## Returns contents of the `SCRIPT_FILENAME` environment variable.
+  return getEnv("SCRIPT_FILENAME")
 
 proc getScriptName*(): string =
-  ## returns contents of the ``SCRIPT_NAME`` environment variable
-  return getEnv("SCRIPT_NAME").string
+  ## Returns contents of the `SCRIPT_NAME` environment variable.
+  return getEnv("SCRIPT_NAME")
 
 proc getServerAddr*(): string =
-  ## returns contents of the ``SERVER_ADDR`` environment variable
-  return getEnv("SERVER_ADDR").string
+  ## Returns contents of the `SERVER_ADDR` environment variable.
+  return getEnv("SERVER_ADDR")
 
 proc getServerAdmin*(): string =
-  ## returns contents of the ``SERVER_ADMIN`` environment variable
-  return getEnv("SERVER_ADMIN").string
+  ## Returns contents of the `SERVER_ADMIN` environment variable.
+  return getEnv("SERVER_ADMIN")
 
 proc getServerName*(): string =
-  ## returns contents of the ``SERVER_NAME`` environment variable
-  return getEnv("SERVER_NAME").string
+  ## Returns contents of the `SERVER_NAME` environment variable.
+  return getEnv("SERVER_NAME")
 
 proc getServerPort*(): string =
-  ## returns contents of the ``SERVER_PORT`` environment variable
-  return getEnv("SERVER_PORT").string
+  ## Returns contents of the `SERVER_PORT` environment variable.
+  return getEnv("SERVER_PORT")
 
 proc getServerProtocol*(): string =
-  ## returns contents of the ``SERVER_PROTOCOL`` environment variable
-  return getEnv("SERVER_PROTOCOL").string
+  ## Returns contents of the `SERVER_PROTOCOL` environment variable.
+  return getEnv("SERVER_PROTOCOL")
 
 proc getServerSignature*(): string =
-  ## returns contents of the ``SERVER_SIGNATURE`` environment variable
-  return getEnv("SERVER_SIGNATURE").string
+  ## Returns contents of the `SERVER_SIGNATURE` environment variable.
+  return getEnv("SERVER_SIGNATURE")
 
 proc getServerSoftware*(): string =
-  ## returns contents of the ``SERVER_SOFTWARE`` environment variable
-  return getEnv("SERVER_SOFTWARE").string
+  ## Returns contents of the `SERVER_SOFTWARE` environment variable.
+  return getEnv("SERVER_SOFTWARE")
 
 proc setTestData*(keysvalues: varargs[string]) =
-  ## fills the appropriate environment variables to test your CGI application.
+  ## Fills the appropriate environment variables to test your CGI application.
   ## This can only simulate the 'GET' request method. `keysvalues` should
   ## provide embedded (name, value)-pairs. Example:
-  ##
-  ## .. code-block:: Nim
-  ##    setTestData("name", "Hanz", "password", "12345")
+  ##   ```Nim
+  ##   setTestData("name", "Hanz", "password", "12345")
+  ##   ```
   putEnv("REQUEST_METHOD", "GET")
   var i = 0
   var query = ""
@@ -302,11 +267,11 @@ proc setTestData*(keysvalues: varargs[string]) =
   putEnv("QUERY_STRING", query)
 
 proc writeContentType*() =
-  ## call this before starting to send your HTML data to `stdout`. This
+  ## Calls this before starting to send your HTML data to `stdout`. This
   ## implements this part of the CGI protocol:
-  ##
-  ## .. code-block:: Nim
-  ##     write(stdout, "Content-type: text/html\n\n")
+  ##   ```Nim
+  ##   write(stdout, "Content-type: text/html\n\n")
+  ##   ```
   write(stdout, "Content-type: text/html\n\n")
 
 proc resetForStacktrace() =
@@ -332,11 +297,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")
@@ -344,10 +304,10 @@ proc setCookie*(name, value: string) =
 var
   gcookies {.threadvar.}: StringTableRef
 
-proc getCookie*(name: string): TaintedString =
+proc getCookie*(name: string): string =
   ## Gets a cookie. If no cookie of `name` exists, "" is returned.
   if gcookies == nil: gcookies = parseCookies(getHttpCookie())
-  result = TaintedString(gcookies.getOrDefault(name))
+  result = gcookies.getOrDefault(name)
 
 proc existsCookie*(name: string): bool =
   ## Checks if a cookie of `name` exists.
diff --git a/lib/pure/collections/LockFreeHash.nim b/lib/pure/collections/LockFreeHash.nim
deleted file mode 100644
index 954d62491..000000000
--- a/lib/pure/collections/LockFreeHash.nim
+++ /dev/null
@@ -1,609 +0,0 @@
-#nim c -t:-march=i686 --cpu:amd64 --threads:on -d:release lockfreehash.nim
-
-import math, hashes
-
-#------------------------------------------------------------------------------
-## Memory Utility Functions
-
-proc newHeap*[T](): ptr T =
-  result = cast[ptr T](alloc0(sizeof(T)))
-
-proc copyNew*[T](x: var T): ptr T =
-  var
-    size = sizeof(T)
-    mem = alloc(size)
-  copyMem(mem, x.addr, size)
-  return cast[ptr T](mem)
-
-proc copyTo*[T](val: var T, dest: int) =
-  copyMem(pointer(dest), val.addr, sizeof(T))
-
-proc allocType*[T](): pointer = alloc(sizeof(T))
-
-proc newShared*[T](): ptr T =
-  result = cast[ptr T](allocShared0(sizeof(T)))
-
-proc copyShared*[T](x: var T): ptr T =
-  var
-    size = sizeof(T)
-    mem = allocShared(size)
-  copyMem(mem, x.addr, size)
-  return cast[ptr T](mem)
-
-#------------------------------------------------------------------------------
-## Pointer arithmetic
-
-proc `+`*(p: pointer, i: int): pointer {.inline.} =
-  cast[pointer](cast[int](p) + i)
-
-const
-  minTableSize = 8
-  reProbeLimit = 12
-  minCopyWork = 4096
-  intSize = sizeof(int)
-
-
-
-when sizeof(int) == 4: # 32bit
-  type
-    Raw = range[0..1073741823]
-    ## The range of uint values that can be stored directly in a value slot
-    ## when on a 32 bit platform
-  {.deprecated: [TRaw: Raw].}
-elif sizeof(int) == 8: # 64bit
-  type
-    Raw = range[0'i64..4611686018427387903'i64]
-    ## The range of uint values that can be stored directly in a value slot
-    ## when on a 64 bit platform
-  {.deprecated: [TRaw: Raw].}
-else:
-  {.error: "unsupported platform".}
-
-type
-  Entry = tuple
-    key: int
-    value: int
-
-  EntryArr = ptr array[0..10_000_000, Entry]
-
-  PConcTable[K,V] = ptr object {.pure.}
-    len: int
-    used: int
-    active: int
-    copyIdx: int
-    copyDone: int
-    next: PConcTable[K,V]
-    data: EntryArr
-{.deprecated: [TEntry: Entry, TEntryArr: EntryArr].}
-
-proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
-  expVal: int, match: bool): int
-
-#------------------------------------------------------------------------------
-
-# Create a new table
-proc newLFTable*[K,V](size: int = minTableSize): PConcTable[K,V]  =
-  let
-    dataLen = max(nextPowerOfTwo(size), minTableSize)
-    dataSize = dataLen*sizeof(Entry)
-    dataMem = allocShared0(dataSize)
-    tableSize = 7 * intSize
-    tableMem = allocShared0(tableSize)
-    table = cast[PConcTable[K,V]](tableMem)
-  table.len = dataLen
-  table.used = 0
-  table.active = 0
-  table.copyIdx = 0
-  table.copyDone = 0
-  table.next = nil
-  table.data = cast[EntryArr](dataMem)
-  result = table
-
-#------------------------------------------------------------------------------
-
-# Delete a table
-proc deleteConcTable[K,V](tbl: PConcTable[K,V]) =
-  deallocShared(tbl.data)
-  deallocShared(tbl)
-
-#------------------------------------------------------------------------------
-
-proc `[]`[K,V](table: var PConcTable[K,V], i: int): var Entry {.inline.} =
-  table.data[i]
-
-#------------------------------------------------------------------------------
-# State flags stored in ptr
-
-
-proc pack[T](x: T): int {.inline.} =
-  result = (cast[int](x) shl 2)
-  #echo("packKey ",cast[int](x) , " -> ", result)
-
-# Pop the flags off returning a 4 byte aligned ptr to our Key or Val
-proc pop(x: int): int  {.inline.} =
-  result = x and 0xFFFFFFFC'i32
-
-# Pop the raw value off of our Key or Val
-proc popRaw(x: int): int  {.inline.} =
-  result = x shr 2
-
-# Pop the flags off returning a 4 byte aligned ptr to our Key or Val
-proc popPtr[V](x: int): ptr V  {.inline.} =
-  result = cast[ptr V](pop(x))
-  #echo("popPtr " & $x & " -> " & $cast[int](result))
-
-# Ghost (sentinel)
-# K or V is no longer valid use new table
-const Ghost = 0xFFFFFFFC
-proc isGhost(x: int): bool {.inline.} =
-  result = x == 0xFFFFFFFC
-
-# Tombstone
-# applied to V = K is dead
-proc isTomb(x: int): bool {.inline.} =
-  result = (x and 0x00000002) != 0
-
-proc setTomb(x: int): int {.inline.} =
-  result = x or 0x00000002
-
-# Prime
-# K or V is in new table copied from old
-proc isPrime(x: int): bool {.inline.} =
-  result = (x and 0x00000001) != 0
-
-proc setPrime(x: int): int {.inline.} =
-  result = x or 0x00000001
-
-#------------------------------------------------------------------------------
-
-##This is for i32 only need to override for i64
-proc hashInt(x: int):int {.inline.} =
-  var h = uint32(x) #shr 2'u32
-  h = h xor (h shr 16'u32)
-  h *= 0x85ebca6b'u32
-  h = h xor (h shr 13'u32)
-  h *= 0xc2b2ae35'u32
-  h = h xor (h shr 16'u32)
-  result = int(h)
-
-#------------------------------------------------------------------------------
-
-proc resize[K,V](self: PConcTable[K,V]): PConcTable[K,V] =
-  var next = atomic_load_n(self.next.addr, ATOMIC_RELAXED)
-  #echo("next = " & $cast[int](next))
-  if next != nil:
-    #echo("A new table already exists, copy in progress")
-    return next
-  var
-    oldLen = atomic_load_n(self.len.addr, ATOMIC_RELAXED)
-    newTable = newLFTable[K,V](oldLen*2)
-    success = atomic_compare_exchange_n(self.next.addr, next.addr, newTable,
-                  false, ATOMIC_RELAXED, ATOMIC_RELAXED)
-  if not success:
-    echo("someone beat us to it! delete table we just created and return his " & $cast[int](next))
-    deleteConcTable(newTable)
-    return next
-  else:
-    echo("Created New Table! " & $cast[int](newTable) & " Size = " & $newTable.len)
-    return newTable
-
-
-#------------------------------------------------------------------------------
-#proc keyEQ[K](key1: ptr K, key2: ptr K): bool {.inline.} =
-proc keyEQ[K](key1: int, key2: int): bool {.inline.} =
-  result = false
-  when K is Raw:
-    if key1 == key2:
-      result = true
-  else:
-    var
-      p1 = popPtr[K](key1)
-      p2 = popPtr[K](key2)
-    if p1 != nil and p2 != nil:
-      if cast[int](p1) == cast[int](p2):
-        return true
-      if p1[] == p2[]:
-        return true
-
-#------------------------------------------------------------------------------
-
-#proc tableFull(self: var PConcTable[K,V]) : bool {.inline.} =
-
-
-#------------------------------------------------------------------------------
-
-proc copySlot[K,V](idx: int, oldTbl: var PConcTable[K,V], newTbl: var PConcTable[K,V]): bool =
-  #echo("Copy idx " & $idx)
-  var
-    oldVal = 0
-    oldkey = 0
-    ok = false
-  result = false
-  #Block the key so no other threads waste time here
-  while not ok:
-    ok = atomic_compare_exchange_n(oldTbl[idx].key.addr, oldKey.addr,
-      setTomb(oldKey), false, ATOMIC_RELAXED, ATOMIC_RELAXED)
-  #echo("oldKey was = " & $oldKey & "  set it to tomb " & $setTomb(oldKey))
-  #Prevent new values from appearing in the old table by priming
-  oldVal = atomic_load_n(oldTbl[idx].value.addr, ATOMIC_RELAXED)
-  while not isPrime(oldVal):
-    var box = if oldVal == 0 or isTomb(oldVal) : oldVal.setTomb.setPrime
-      else: oldVal.setPrime
-    if atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr,
-      box, false, ATOMIC_RELAXED, ATOMIC_RELAXED):
-      if isPrime(box) and isTomb(box):
-        return true
-      oldVal = box
-      break
-  #echo("oldVal was = ", oldVal, "  set it to prime ", box)
-  if isPrime(oldVal) and isTomb(oldVal):
-    #when not (K is Raw):
-    #  deallocShared(popPtr[K](oldKey))
-    return false
-  if isTomb(oldVal):
-    echo("oldVal is Tomb!!!, should not happen")
-  if pop(oldVal) != 0:
-    result = setVal(newTbl, pop(oldKey), pop(oldVal), 0, true) == 0
-  #if result:
-    #echo("Copied a Slot! idx= " & $idx & " key= " & $oldKey & " val= " & $oldVal)
-  #else:
-    #echo("copy slot failed")
-  # Our copy is done so we disable the old slot
-  while not ok:
-    ok = atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr,
-      oldVal.setTomb.setPrime , false, ATOMIC_RELAXED, ATOMIC_RELAXED)
-  #echo("disabled old slot")
-  #echo"---------------------"
-
-#------------------------------------------------------------------------------
-
-proc promote[K,V](table: var PConcTable[K,V]) =
-  var
-    newData = atomic_load_n(table.next.data.addr, ATOMIC_RELAXED)
-    newLen = atomic_load_n(table.next.len.addr, ATOMIC_RELAXED)
-    newUsed = atomic_load_n(table.next.used.addr, ATOMIC_RELAXED)
-
-  deallocShared(table.data)
-  atomic_store_n(table.data.addr, newData, ATOMIC_RELAXED)
-  atomic_store_n(table.len.addr, newLen, ATOMIC_RELAXED)
-  atomic_store_n(table.used.addr, newUsed, ATOMIC_RELAXED)
-  atomic_store_n(table.copyIdx.addr, 0, ATOMIC_RELAXED)
-  atomic_store_n(table.copyDone.addr, 0, ATOMIC_RELAXED)
-  deallocShared(table.next)
-  atomic_store_n(table.next.addr, nil, ATOMIC_RELAXED)
-  echo("new table swapped!")
-
-#------------------------------------------------------------------------------
-
-proc checkAndPromote[K,V](table: var PConcTable[K,V], workDone: int): bool =
-  var
-    oldLen = atomic_load_n(table.len.addr, ATOMIC_RELAXED)
-    copyDone = atomic_load_n(table.copyDone.addr, ATOMIC_RELAXED)
-    ok: bool
-  result = false
-  if workDone > 0:
-    #echo("len to copy =" & $oldLen)
-    #echo("copyDone + workDone = " & $copyDone & " + " & $workDone)
-    while not ok:
-      ok = atomic_compare_exchange_n(table.copyDone.addr, copyDone.addr,
-        copyDone + workDone, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
-    #if ok: echo("set copyDone")
-    # If the copy is done we can promote this table
-    if copyDone + workDone >= oldLen:
-      # Swap new data
-      #echo("work is done!")
-      table.promote
-      result = true
-
-#------------------------------------------------------------------------------
-
-proc copySlotAndCheck[K,V](table: var PConcTable[K,V], idx: int):
-  PConcTable[K,V] =
-  var
-    newTable = cast[PConcTable[K,V]](atomic_load_n(table.next.addr, ATOMIC_RELAXED))
-  result = newTable
-  if newTable != nil and copySlot(idx, table, newTable):
-    #echo("copied a single slot, idx = " & $idx)
-    if checkAndPromote(table, 1): return table
-
-
-#------------------------------------------------------------------------------
-
-proc helpCopy[K,V](table: var PConcTable[K,V]): PConcTable[K,V] =
-  var
-    newTable = cast[PConcTable[K,V]](atomic_load_n(table.next.addr, ATOMIC_RELAXED))
-  result = newTable
-  if newTable != nil:
-    var
-      oldLen = atomic_load_n(table.len.addr, ATOMIC_RELAXED)
-      copyDone = atomic_load_n(table.copyDone.addr, ATOMIC_RELAXED)
-      copyIdx = 0
-      work = min(oldLen, minCopyWork)
-      #panicStart = -1
-      workDone = 0
-    if copyDone < oldLen:
-      var ok: bool
-      while not ok:
-        ok = atomic_compare_exchange_n(table.copyIdx.addr, copyIdx.addr,
-          copyIdx + work, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
-      #echo("copy idx = ", copyIdx)
-      for i in 0..work-1:
-        var idx = (copyIdx + i) and (oldLen - 1)
-        if copySlot(idx, table, newTable):
-          workDone += 1
-      if workDone > 0:
-        #echo("did work ", workDone, " on thread ", cast[int](myThreadID[pointer]()))
-        if checkAndPromote(table, workDone): return table
-    # In case a thread finished all the work then got stalled before promotion
-    if checkAndPromote(table, 0): return table
-
-
-
-#------------------------------------------------------------------------------
-
-proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
-  expVal: int, match: bool): int =
-  #echo("-try set- in table ", " key = ", (popPtr[K](key)[]), " val = ", val)
-  when K is Raw:
-    var idx = hashInt(key)
-  else:
-    var idx = popPtr[K](key)[].hash
-  var
-    nextTable: PConcTable[K,V]
-    probes = 1
-  # spin until we find a key slot or build and jump to next table
-  while true:
-    idx = idx and (table.len - 1)
-    #echo("try set idx = " & $idx & "for" & $key)
-    var
-      probedKey = 0
-      openKey = atomic_compare_exchange_n(table[idx].key.addr, probedKey.addr,
-        key, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
-    if openKey:
-      if val.isTomb:
-        #echo("val was tomb, bail, no reason to set an open slot to tomb")
-        return val
-      #increment used slots
-      #echo("found an open slot, total used = " &
-      #$atomic_add_fetch(table.used.addr, 1, ATOMIC_RELAXED))
-      discard atomic_add_fetch(table.used.addr, 1, ATOMIC_RELAXED)
-      break # We found an open slot
-    #echo("set idx ", idx, " key = ", key, " probed = ", probedKey)
-    if keyEQ[K](probedKey, key):
-      #echo("we found the matching slot")
-      break # We found a matching slot
-    if (not(expVal != 0 and match)) and (probes >= reProbeLimit or key.isTomb):
-      if key.isTomb: echo("Key is Tombstone")
-      #if probes >= reProbeLimit: echo("Too much probing " & $probes)
-      #echo("try to resize")
-      #create next bigger table
-      nextTable = resize(table)
-      #help do some copying
-      #echo("help copy old table to new")
-      nextTable = helpCopy(table)
-      #now setVal in the new table instead
-      #echo("jumping to next table to set val")
-      return setVal(nextTable, key, val, expVal, match)
-    else:
-      idx += 1
-      probes += 1
-  # Done spinning for a new slot
-  var oldVal = atomic_load_n(table[idx].value.addr, ATOMIC_RELAXED)
-  if val == oldVal:
-    #echo("this val is alredy in the slot")
-    return oldVal
-  nextTable = atomic_load_n(table.next.addr, ATOMIC_SEQ_CST)
-  if nextTable == nil and
-    ((oldVal == 0 and
-    (probes >= reProbeLimit or table.used / table.len > 0.8)) or
-    (isPrime(oldVal))):
-    if table.used / table.len > 0.8: echo("resize because usage ratio = " &
-      $(table.used / table.len))
-    if isPrime(oldVal): echo("old val isPrime, should be a rare mem ordering event")
-    nextTable = resize(table)
-  if nextTable != nil:
-    #echo("tomb old slot then set in new table")
-    nextTable = copySlotAndCheck(table,idx)
-    return setVal(nextTable, key, val, expVal, match)
-  # Finally ready to add new val to table
-  while true:
-    if match and oldVal != expVal:
-      #echo("set failed, no match  oldVal= " & $oldVal & " expVal= " & $expVal)
-      return oldVal
-    if atomic_compare_exchange_n(table[idx].value.addr, oldVal.addr,
-        val, false, ATOMIC_RELEASE, ATOMIC_RELAXED):
-      #echo("val set at table " & $cast[int](table))
-      if expVal != 0:
-        if (oldVal == 0 or isTomb(oldVal)) and not isTomb(val):
-          discard atomic_add_fetch(table.active.addr, 1, ATOMIC_RELAXED)
-        elif not (oldVal == 0 or isTomb(oldVal)) and isTomb(val):
-          discard atomic_add_fetch(table.active.addr, -1, ATOMIC_RELAXED)
-      if oldVal == 0 and expVal != 0:
-        return setTomb(oldVal)
-      else: return oldVal
-    if isPrime(oldVal):
-      nextTable = copySlotAndCheck(table, idx)
-      return setVal(nextTable, key, val, expVal, match)
-
-#------------------------------------------------------------------------------
-
-proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
-  #echo("-try get-  key = " & $key)
-  when K is Raw:
-    var idx = hashInt(key)
-  else:
-    var idx = popPtr[K](key)[].hash
-    #echo("get idx ", idx)
-  var
-    probes = 0
-    val: int
-  while true:
-    idx = idx and (table.len - 1)
-    var
-      newTable: PConcTable[K,V] # = atomic_load_n(table.next.addr, ATOMIC_ACQUIRE)
-      probedKey = atomic_load_n(table[idx].key.addr, ATOMIC_SEQ_CST)
-    if keyEQ[K](probedKey, key):
-      #echo("found key after ", probes+1)
-      val = atomic_load_n(table[idx].value.addr, ATOMIC_ACQUIRE)
-      if not isPrime(val):
-        if isTomb(val):
-          #echo("val was tomb but not prime")
-          return 0
-        else:
-          #echo("-GotIt- idx = ", idx, " key = ", key, " val ", val )
-          return val
-      else:
-        newTable = copySlotAndCheck(table, idx)
-        return getVal(newTable, key)
-    else:
-      #echo("probe ", probes, " idx = ", idx, " key = ", key, " found ", probedKey )
-      if probes >= reProbeLimit*4 or key.isTomb:
-        if newTable == nil:
-          #echo("too many probes and no new table ", key, "  ", idx )
-          return 0
-        else:
-          newTable = helpCopy(table)
-          return getVal(newTable, key)
-      idx += 1
-      probes += 1
-
-#------------------------------------------------------------------------------
-
-#proc set*(table: var PConcTable[Raw,Raw], key: Raw, val: Raw) =
-#  discard setVal(table, pack(key), pack(key), 0, false)
-
-#proc set*[V](table: var PConcTable[Raw,V], key: Raw, val: ptr V) =
-#  discard setVal(table, pack(key), cast[int](val), 0, false)
-
-proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) =
-  when not (K is Raw):
-    var newKey = cast[int](copyShared(key))
-  else:
-    var newKey = pack(key)
-  when not (V is Raw):
-    var newVal = cast[int](copyShared(val))
-  else:
-    var newVal = pack(val)
-  var oldPtr = pop(setVal(table, newKey, newVal, 0, false))
-    #echo("oldPtr = ", cast[int](oldPtr), " newPtr = ", cast[int](newPtr))
-  when not (V is Raw):
-    if newVal != oldPtr and oldPtr != 0:
-      deallocShared(cast[ptr V](oldPtr))
-
-
-
-proc get*[K,V](table: var PConcTable[K,V], key: var K): V =
-  when not (V is Raw):
-    when not (K is Raw):
-      return popPtr[V](getVal(table, cast[int](key.addr)))[]
-    else:
-      return popPtr[V](getVal(table, pack(key)))[]
-  else:
-    when not (K is Raw):
-      return popRaw(getVal(table, cast[int](key.addr)))
-    else:
-      return popRaw(getVal(table, pack(key)))
-
-
-
-
-
-
-
-
-
-
-
-#proc `[]`[K,V](table: var PConcTable[K,V], key: K): PEntry[K,V] {.inline.} =
-#  getVal(table, key)
-
-#proc `[]=`[K,V](table: var PConcTable[K,V], key: K, val: V): PEntry[K,V] {.inline.} =
-#  setVal(table, key, val)
-
-
-
-
-
-
-#Tests ----------------------------
-when not defined(testing) and isMainModule:
-  import locks, times, mersenne
-
-  const
-    numTests  = 100000
-    numThreads = 10
-
-
-
-  type
-    TestObj = tuple
-      thr: int
-      f0: int
-      f1: int
-
-    Data = tuple[k: string,v: TestObj]
-    PDataArr = array[0..numTests-1, Data]
-    Dict = PConcTable[string,TestObj]
-  {.deprecated: [TTestObj: TestObj, TData: Data].}
-
-  var
-    thr: array[0..numThreads-1, Thread[Dict]]
-
-    table = newLFTable[string,TestObj](8)
-    rand = newMersenneTwister(2525)
-
-  proc createSampleData(len: int): PDataArr =
-    #result = cast[PDataArr](allocShared0(sizeof(Data)*numTests))
-    for i in 0..len-1:
-      result[i].k = "mark" & $(i+1)
-      #echo("mark" & $(i+1), " ", hash("mark" & $(i+1)))
-      result[i].v.thr = 0
-      result[i].v.f0 = i+1
-      result[i].v.f1 = 0
-      #echo("key = " & $(i+1) & " Val ptr = " & $cast[int](result[i].v.addr))
-
-
-
-  proc threadProc(tp: Dict) {.thread.} =
-    var t = cpuTime();
-    for i in 1..numTests:
-      var key = "mark" & $(i)
-      var got = table.get(key)
-      got.thr = cast[int](myThreadID[pointer]())
-      got.f1 = got.f1 + 1
-      table.set(key, got)
-    t = cpuTime() - t
-    echo t
-
-
-  var testData = createSampleData(numTests)
-
-  for i in 0..numTests-1:
-    table.set(testData[i].k, testData[i].v)
-
-  var i = 0
-  while i < numThreads:
-    createThread(thr[i], threadProc, table)
-    i += 1
-
-  joinThreads(thr)
-
-
-
-
-
-  var fails = 0
-
-  for i in 0..numTests-1:
-    var got = table.get(testData[i].k)
-    if got.f0 != i+1 or got.f1 != numThreads:
-      fails += 1
-      echo(got)
-
-  echo("Failed read or write = ", fails)
-
-
-  #for i in 1..numTests:
-  #  echo(i, " = ", hashInt(i) and 8191)
-
-  deleteConcTable(table)
diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim
index c94e08098..24257dacb 100644
--- a/lib/pure/collections/critbits.nim
+++ b/lib/pure/collections/critbits.nim
@@ -8,16 +8,41 @@
 #
 
 ## This module implements a `crit bit tree`:idx: which is an efficient
-## container for a sorted set of strings, or for a sorted mapping of strings. Based on the excellent paper
-## by Adam Langley.
+## container for a sorted set of strings, or for a sorted mapping of strings. Based on the
+## [excellent paper by Adam Langley](https://www.imperialviolet.org/binary/critbit.pdf).
 ## (A crit bit tree is a form of `radix tree`:idx: or `patricia trie`:idx:.)
 
-include "system/inclrtl"
+runnableExamples:
+  from std/sequtils import toSeq
+
+  var critbitAsSet: CritBitTree[void] = ["kitten", "puppy"].toCritBitTree
+  doAssert critbitAsSet.len == 2
+  critbitAsSet.incl("")
+  doAssert "" in critbitAsSet
+  critbitAsSet.excl("")
+  doAssert "" notin critbitAsSet
+  doAssert toSeq(critbitAsSet.items) == @["kitten", "puppy"]
+  let same = ["puppy", "kitten", "puppy"].toCritBitTree
+  doAssert toSeq(same.keys) == toSeq(critbitAsSet.keys)
+
+  var critbitAsDict: CritBitTree[int] = {"key1": 42}.toCritBitTree
+  doAssert critbitAsDict.len == 1
+  critbitAsDict["key2"] = 0
+  doAssert "key2" in critbitAsDict
+  doAssert critbitAsDict["key2"] == 0
+  critbitAsDict.excl("key1")
+  doAssert "key1" notin critbitAsDict
+  doAssert toSeq(critbitAsDict.pairs) == @[("key2", 0)]
+
+import std/private/since
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 type
-  NodeObj[T] = object {.acyclic.}
+  NodeObj[T] {.acyclic.} = object
     byte: int ## byte index of the difference
-    otherbits: char
+    otherBits: char
     case isLeaf: bool
     of false: child: array[0..1, ref NodeObj[T]]
     of true:
@@ -28,15 +53,17 @@ type
   Node[T] = ref NodeObj[T]
   CritBitTree*[T] = object ## The crit bit tree can either be used
                            ## as a mapping from strings to
-                           ## some type ``T`` or as a set of
-                           ## strings if ``T`` is void.
+                           ## some type `T` or as a set of
+                           ## strings if `T` is `void`.
     root: Node[T]
     count: int
 
-{.deprecated: [TCritBitTree: CritBitTree].}
+func len*[T](c: CritBitTree[T]): int {.inline.} =
+  ## Returns the number of elements in `c` in O(1).
+  runnableExamples:
+    let c = ["key1", "key2"].toCritBitTree
+    doAssert c.len == 2
 
-proc len*[T](c: CritBitTree[T]): int =
-  ## returns the number of elements in `c` in O(1).
   result = c.count
 
 proc rawGet[T](c: CritBitTree[T], key: string): Node[T] =
@@ -49,19 +76,22 @@ proc rawGet[T](c: CritBitTree[T], key: string): Node[T] =
     else:
       return if it.key == key: it else: nil
 
-proc contains*[T](c: CritBitTree[T], key: string): bool {.inline.} =
-  ## returns true iff `c` contains the given `key`.
+func contains*[T](c: CritBitTree[T], key: string): bool {.inline.} =
+  ## Returns true if `c` contains the given `key`.
+  runnableExamples:
+    var c: CritBitTree[void]
+    incl(c, "key")
+    doAssert c.contains("key")
+
   result = rawGet(c, key) != nil
 
-proc hasKey*[T](c: CritBitTree[T], key: string): bool {.inline.} =
-  ## alias for `contains`.
+func hasKey*[T](c: CritBitTree[T], key: string): bool {.inline.} =
+  ## Alias for `contains <#contains,CritBitTree[T],string>`_.
   result = rawGet(c, key) != nil
 
 proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] =
   if c.root == nil:
-    new c.root
-    c.root.isleaf = true
-    c.root.key = key
+    c.root = Node[T](isleaf: true, key: key)
     result = c.root
   else:
     var it = c.root
@@ -73,14 +103,14 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] =
     var newOtherBits = 0
     var newByte = 0
     block blockX:
-      while newbyte < key.len:
-        let ch = if newbyte < it.key.len: it.key[newbyte] else: '\0'
-        if ch != key[newbyte]:
-          newotherbits = ch.ord xor key[newbyte].ord
+      while newByte < key.len:
+        let ch = if newByte < it.key.len: it.key[newByte] else: '\0'
+        if ch != key[newByte]:
+          newOtherBits = ch.ord xor key[newByte].ord
           break blockX
-        inc newbyte
-      if newbyte < it.key.len:
-        newotherbits = it.key[newbyte].ord
+        inc newByte
+      if newByte < it.key.len:
+        newOtherBits = it.key[newByte].ord
       else:
         return it
     while (newOtherBits and (newOtherBits-1)) != 0:
@@ -91,9 +121,7 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] =
 
     var inner: Node[T]
     new inner
-    new result
-    result.isLeaf = true
-    result.key = key
+    result = Node[T](isLeaf: true, key: key)
     inner.otherBits = chr(newOtherBits)
     inner.byte = newByte
     inner.child[1 - dir] = result
@@ -111,7 +139,7 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] =
     wherep[] = inner
   inc c.count
 
-proc exclImpl[T](c: var CritBitTree[T], key: string) : int =
+func exclImpl[T](c: var CritBitTree[T], key: string): int =
   var p = c.root
   var wherep = addr(c.root)
   var whereq: ptr Node[T] = nil
@@ -136,20 +164,62 @@ proc exclImpl[T](c: var CritBitTree[T], key: string) : int =
   return c.count
 
 proc excl*[T](c: var CritBitTree[T], key: string) =
-  ## removes `key` (and its associated value) from the set `c`.
+  ## Removes `key` (and its associated value) from the set `c`.
   ## If the `key` does not exist, nothing happens.
+  ##
+  ## **See also:**
+  ## * `incl proc <#incl,CritBitTree[void],string>`_
+  ## * `incl proc <#incl,CritBitTree[T],string,T>`_
+  runnableExamples:
+    var c: CritBitTree[void]
+    incl(c, "key")
+    excl(c, "key")
+    doAssert not c.contains("key")
+
   discard exclImpl(c, key)
 
 proc missingOrExcl*[T](c: var CritBitTree[T], key: string): bool =
-  ## Returns true iff `c` does not contain the given `key`. If the key
-  ## does exist, c.excl(key) is performed.
+  ## Returns true if `c` does not contain the given `key`. If the key
+  ## does exist, `c.excl(key)` is performed.
+  ##
+  ## **See also:**
+  ## * `excl proc <#excl,CritBitTree[T],string>`_
+  ## * `containsOrIncl proc <#containsOrIncl,CritBitTree[T],string,T>`_
+  ## * `containsOrIncl proc <#containsOrIncl,CritBitTree[void],string>`_
+  runnableExamples:
+    block:
+      var c: CritBitTree[void]
+      doAssert c.missingOrExcl("key")
+    block:
+      var c: CritBitTree[void]
+      incl(c, "key")
+      doAssert not c.missingOrExcl("key")
+      doAssert not c.contains("key")
+
   let oldCount = c.count
-  var n = exclImpl(c, key)
+  discard exclImpl(c, key)
   result = c.count == oldCount
 
-proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: T): bool =
-  ## returns true iff `c` contains the given `key`. If the key does not exist
-  ## ``c[key] = val`` is performed.
+proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: sink T): bool =
+  ## Returns true if `c` contains the given `key`. If the key does not exist,
+  ## `c[key] = val` is performed.
+  ##
+  ## **See also:**
+  ## * `incl proc <#incl,CritBitTree[void],string>`_
+  ## * `incl proc <#incl,CritBitTree[T],string,T>`_
+  ## * `containsOrIncl proc <#containsOrIncl,CritBitTree[void],string>`_
+  ## * `missingOrExcl proc <#missingOrExcl,CritBitTree[T],string>`_
+  runnableExamples:
+    block:
+      var c: CritBitTree[int]
+      doAssert not c.containsOrIncl("key", 42)
+      doAssert c.contains("key")
+    block:
+      var c: CritBitTree[int]
+      incl(c, "key", 21)
+      doAssert c.containsOrIncl("key", 42)
+      doAssert c["key"] == 21
+
   let oldCount = c.count
   var n = rawInsert(c, key)
   result = c.count == oldCount
@@ -157,57 +227,99 @@ proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: T): bool =
     if not result: n.val = val
 
 proc containsOrIncl*(c: var CritBitTree[void], key: string): bool =
-  ## returns true iff `c` contains the given `key`. If the key does not exist
+  ## Returns true if `c` contains the given `key`. If the key does not exist,
   ## it is inserted into `c`.
+  ##
+  ## **See also:**
+  ## * `incl proc <#incl,CritBitTree[void],string>`_
+  ## * `incl proc <#incl,CritBitTree[T],string,T>`_
+  ## * `containsOrIncl proc <#containsOrIncl,CritBitTree[T],string,T>`_
+  ## * `missingOrExcl proc <#missingOrExcl,CritBitTree[T],string>`_
+  runnableExamples:
+    block:
+      var c: CritBitTree[void]
+      doAssert not c.containsOrIncl("key")
+      doAssert c.contains("key")
+    block:
+      var c: CritBitTree[void]
+      incl(c, "key")
+      doAssert c.containsOrIncl("key")
+
   let oldCount = c.count
-  var n = rawInsert(c, key)
+  discard rawInsert(c, key)
   result = c.count == oldCount
 
 proc inc*(c: var CritBitTree[int]; key: string, val: int = 1) =
-  ## increments `c[key]` by `val`.
+  ## Increments `c[key]` by `val`.
+  runnableExamples:
+    var c: CritBitTree[int]
+    c["key"] = 1
+    inc(c, "key")
+    doAssert c["key"] == 2
+
   var n = rawInsert(c, key)
   inc n.val, val
 
 proc incl*(c: var CritBitTree[void], key: string) =
-  ## includes `key` in `c`.
+  ## Includes `key` in `c`.
+  ##
+  ## **See also:**
+  ## * `excl proc <#excl,CritBitTree[T],string>`_
+  ## * `incl proc <#incl,CritBitTree[T],string,T>`_
+  runnableExamples:
+    var c: CritBitTree[void]
+    incl(c, "key")
+    doAssert c.hasKey("key")
+
   discard rawInsert(c, key)
 
-proc incl*[T](c: var CritBitTree[T], key: string, val: T) =
-  ## inserts `key` with value `val` into `c`.
+proc incl*[T](c: var CritBitTree[T], key: string, val: sink T) =
+  ## Inserts `key` with value `val` into `c`.
+  ##
+  ## **See also:**
+  ## * `excl proc <#excl,CritBitTree[T],string>`_
+  ## * `incl proc <#incl,CritBitTree[void],string>`_
+  runnableExamples:
+    var c: CritBitTree[int]
+    incl(c, "key", 42)
+    doAssert c["key"] == 42
+
   var n = rawInsert(c, key)
   n.val = val
 
-proc `[]=`*[T](c: var CritBitTree[T], key: string, val: T) =
-  ## puts a (key, value)-pair into `t`.
+proc `[]=`*[T](c: var CritBitTree[T], key: string, val: sink T) =
+  ## Alias for `incl <#incl,CritBitTree[T],string,T>`_.
+  ##
+  ## **See also:**
+  ## * `[] proc <#[],CritBitTree[T],string>`_
+  ## * `[] proc <#[],CritBitTree[T],string_2>`_
   var n = rawInsert(c, key)
   n.val = val
 
 template get[T](c: CritBitTree[T], key: string): T =
   let n = rawGet(c, key)
   if n == nil:
-    when compiles($key):
-      raise newException(KeyError, "key not found: " & $key)
-    else:
-      raise newException(KeyError, "key not found")
+    raise newException(KeyError, "key not found: " & key)
 
   n.val
 
-proc `[]`*[T](c: CritBitTree[T], key: string): T {.inline, deprecatedGet.} =
-  ## retrieves the value at ``c[key]``. If `key` is not in `t`, the
-  ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
+func `[]`*[T](c: CritBitTree[T], key: string): lent T {.inline.} =
+  ## Retrieves the value at `c[key]`. If `key` is not in `t`, the
+  ## `KeyError` exception is raised. One can check with `hasKey` whether
   ## the key exists.
+  ##
+  ## **See also:**
+  ## * `[] proc <#[],CritBitTree[T],string_2>`_
+  ## * `[]= proc <#[]=,CritBitTree[T],string,T>`_
   get(c, key)
 
-proc `[]`*[T](c: var CritBitTree[T], key: string): var T {.inline,
-  deprecatedGet.} =
-  ## retrieves the value at ``c[key]``. The value can be modified.
-  ## 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.
+func `[]`*[T](c: var CritBitTree[T], key: string): var T {.inline.} =
+  ## Retrieves the value at `c[key]`. The value can be modified.
+  ## If `key` is not in `t`, the `KeyError` exception is raised.
+  ##
+  ## **See also:**
+  ## * `[] proc <#[],CritBitTree[T],string>`_
+  ## * `[]= proc <#[]=,CritBitTree[T],string,T>`_
   get(c, key)
 
 iterator leaves[T](n: Node[T]): Node[T] =
@@ -224,32 +336,64 @@ iterator leaves[T](n: Node[T]): Node[T] =
       yield it
 
 iterator keys*[T](c: CritBitTree[T]): string =
-  ## yields all keys in lexicographical order.
+  ## Yields all keys in lexicographical order.
+  runnableExamples:
+    from std/sequtils import toSeq
+
+    let c = {"key1": 1, "key2": 2}.toCritBitTree
+    doAssert toSeq(c.keys) == @["key1", "key2"]
+
   for x in leaves(c.root): yield x.key
 
-iterator values*[T](c: CritBitTree[T]): T =
-  ## yields all values of `c` in the lexicographical order of the
+iterator values*[T](c: CritBitTree[T]): lent T =
+  ## Yields all values of `c` in the lexicographical order of the
   ## corresponding keys.
+  ##
+  ## **See also:**
+  ## * `mvalues iterator <#mvalues.i,CritBitTree[T]>`_
+  runnableExamples:
+    from std/sequtils import toSeq
+
+    let c = {"key1": 1, "key2": 2}.toCritBitTree
+    doAssert toSeq(c.values) == @[1, 2]
+
   for x in leaves(c.root): yield x.val
 
 iterator mvalues*[T](c: var CritBitTree[T]): var T =
-  ## yields all values of `c` in the lexicographical order of the
+  ## Yields all values of `c` in the lexicographical order of the
   ## corresponding keys. The values can be modified.
+  ##
+  ## **See also:**
+  ## * `values iterator <#values.i,CritBitTree[T]>`_
   for x in leaves(c.root): yield x.val
 
 iterator items*[T](c: CritBitTree[T]): string =
-  ## yields all keys in lexicographical order.
+  ## Alias for `keys <#keys.i,CritBitTree[T]>`_.
   for x in leaves(c.root): yield x.key
 
 iterator pairs*[T](c: CritBitTree[T]): tuple[key: string, val: T] =
-  ## yields all (key, value)-pairs of `c`.
+  ## Yields all `(key, value)`-pairs of `c` in the lexicographical order of the
+  ## corresponding keys.
+  ##
+  ## **See also:**
+  ## * `mpairs iterator <#mpairs.i,CritBitTree[T]>`_
+  runnableExamples:
+    from std/sequtils import toSeq
+
+    let c = {"key1": 1, "key2": 2}.toCritBitTree
+    doAssert toSeq(c.pairs) == @[(key: "key1", val: 1), (key: "key2", val: 2)]
+
   for x in leaves(c.root): yield (x.key, x.val)
 
 iterator mpairs*[T](c: var CritBitTree[T]): tuple[key: string, val: var T] =
-  ## yields all (key, value)-pairs of `c`. The yielded values can be modified.
+  ## Yields all `(key, value)`-pairs of `c` in the lexicographical order of the
+  ## corresponding keys. The yielded values can be modified.
+  ##
+  ## **See also:**
+  ## * `pairs iterator <#pairs.i,CritBitTree[T]>`_
   for x in leaves(c.root): yield (x.key, x.val)
 
-proc allprefixedAux[T](c: CritBitTree[T], key: string; longestMatch: bool): Node[T] =
+proc allprefixedAux[T](c: CritBitTree[T], key: string): Node[T] =
   var p = c.root
   var top = p
   if p != nil:
@@ -259,58 +403,83 @@ proc allprefixedAux[T](c: CritBitTree[T], key: string; longestMatch: bool): Node
       let dir = (1 + (ch.ord or p.otherBits.ord)) shr 8
       p = p.child[dir]
       if q.byte < key.len: top = p
-    if not longestMatch:
-      for i in 0 ..< key.len:
-        if p.key[i] != key[i]: return
+    for i in 0 ..< key.len:
+      if i >= p.key.len or p.key[i] != key[i]: return
     result = top
 
-iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string;
-                             longestMatch=false): string =
-  ## yields all keys starting with `prefix`. If `longestMatch` is true,
-  ## the longest match is returned, it doesn't have to be a complete match then.
-  let top = allprefixedAux(c, prefix, longestMatch)
-  for x in leaves(top): yield x.key
+iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string): string =
+  ## Yields all keys starting with `prefix`.
+  runnableExamples:
+    from std/sequtils import toSeq
+
+    let c = {"key1": 42, "key2": 43}.toCritBitTree
+    doAssert toSeq(c.keysWithPrefix("key")) == @["key1", "key2"]
 
-iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string;
-                            longestMatch=false): string =
-  ## yields all keys starting with `prefix`.
-  let top = allprefixedAux(c, prefix, longestMatch)
+  let top = allprefixedAux(c, prefix)
   for x in leaves(top): yield x.key
 
-iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string;
-                              longestMatch=false): T =
-  ## yields all values of `c` starting with `prefix` of the
+iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string): lent T =
+  ## Yields all values of `c` starting with `prefix` of the
   ## corresponding keys.
-  let top = allprefixedAux(c, prefix, longestMatch)
+  ##
+  ## **See also:**
+  ## * `mvaluesWithPrefix iterator <#mvaluesWithPrefix.i,CritBitTree[T],string>`_
+  runnableExamples:
+    from std/sequtils import toSeq
+
+    let c = {"key1": 42, "key2": 43}.toCritBitTree
+    doAssert toSeq(c.valuesWithPrefix("key")) == @[42, 43]
+
+  let top = allprefixedAux(c, prefix)
   for x in leaves(top): yield x.val
 
-iterator mvaluesWithPrefix*[T](c: var CritBitTree[T], prefix: string;
-                               longestMatch=false): var T =
-  ## yields all values of `c` starting with `prefix` of the
+iterator mvaluesWithPrefix*[T](c: var CritBitTree[T], prefix: string): var T =
+  ## Yields all values of `c` starting with `prefix` of the
   ## corresponding keys. The values can be modified.
-  let top = allprefixedAux(c, prefix, longestMatch)
+  ##
+  ## **See also:**
+  ## * `valuesWithPrefix iterator <#valuesWithPrefix.i,CritBitTree[T],string>`_
+  let top = allprefixedAux(c, prefix)
   for x in leaves(top): yield x.val
 
+iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string): string =
+  ## Alias for `keysWithPrefix <#keysWithPrefix.i,CritBitTree[T],string>`_.
+  let top = allprefixedAux(c, prefix)
+  for x in leaves(top): yield x.key
+
 iterator pairsWithPrefix*[T](c: CritBitTree[T],
-                             prefix: string;
-                             longestMatch=false): tuple[key: string, val: T] =
-  ## yields all (key, value)-pairs of `c` starting with `prefix`.
-  let top = allprefixedAux(c, prefix, longestMatch)
+                             prefix: string): tuple[key: string, val: T] =
+  ## Yields all (key, value)-pairs of `c` starting with `prefix`.
+  ##
+  ## **See also:**
+  ## * `mpairsWithPrefix iterator <#mpairsWithPrefix.i,CritBitTree[T],string>`_
+  runnableExamples:
+    from std/sequtils import toSeq
+
+    let c = {"key1": 42, "key2": 43}.toCritBitTree
+    doAssert toSeq(c.pairsWithPrefix("key")) == @[(key: "key1", val: 42), (key: "key2", val: 43)]
+
+  let top = allprefixedAux(c, prefix)
   for x in leaves(top): yield (x.key, x.val)
 
 iterator mpairsWithPrefix*[T](c: var CritBitTree[T],
-                              prefix: string;
-                             longestMatch=false): tuple[key: string, val: var T] =
-  ## yields all (key, value)-pairs of `c` starting with `prefix`.
+                              prefix: string): tuple[key: string, val: var T] =
+  ## Yields all (key, value)-pairs of `c` starting with `prefix`.
   ## The yielded values can be modified.
-  let top = allprefixedAux(c, prefix, longestMatch)
+  ##
+  ## **See also:**
+  ## * `pairsWithPrefix iterator <#pairsWithPrefix.i,CritBitTree[T],string>`_
+  let top = allprefixedAux(c, prefix)
   for x in leaves(top): yield (x.key, x.val)
 
-proc `$`*[T](c: CritBitTree[T]): string =
-  ## turns `c` into a string representation. Example outputs:
-  ## ``{keyA: value, keyB: value}``, ``{:}``
-  ## If `T` is void the outputs look like:
-  ## ``{keyA, keyB}``, ``{}``.
+func `$`*[T](c: CritBitTree[T]): string =
+  ## Turns `c` into a string representation.
+  runnableExamples:
+    doAssert $CritBitTree[int].default == "{:}"
+    doAssert $toCritBitTree({"key1": 1, "key2": 2}) == """{"key1": 1, "key2": 2}"""
+    doAssert $CritBitTree[void].default == "{}"
+    doAssert $toCritBitTree(["key1", "key2"]) == """{"key1", "key2"}"""
+
   if c.len == 0:
     when T is void:
       result = "{}"
@@ -336,59 +505,33 @@ proc `$`*[T](c: CritBitTree[T]): string =
         result.addQuoted(val)
     result.add("}")
 
-when isMainModule:
-  import sequtils
-
-  var r: CritBitTree[void]
-  r.incl "abc"
-  r.incl "xyz"
-  r.incl "def"
-  r.incl "definition"
-  r.incl "prefix"
-  r.incl "foo"
-
-  doAssert r.contains"def"
-
-  r.excl "def"
-  assert r.missingOrExcl("foo") == false
-  assert "foo" notin toSeq(r.items)
-
-  assert r.missingOrExcl("foo") == true
-
-  assert toSeq(r.items) == @["abc", "definition", "prefix", "xyz"]
-
-  assert toSeq(r.itemsWithPrefix("de")) == @["definition"]
-  var c = CritBitTree[int]()
-
-  c.inc("a")
-  assert c["a"] == 1
-
-  c.inc("a", 4)
-  assert c["a"] == 5
-
-  c.inc("a", -5)
-  assert c["a"] == 0
-
-  c.inc("b", 2)
-  assert c["b"] == 2
-
-  c.inc("c", 3)
-  assert c["c"] == 3
-
-  c.inc("a", 1)
-  assert c["a"] == 1
-
-  var cf = CritBitTree[float]()
-
-  cf.incl("a", 1.0)
-  assert cf["a"] == 1.0
-
-  cf.incl("b", 2.0)
-  assert cf["b"] == 2.0
-
-  cf.incl("c", 3.0)
-  assert cf["c"] == 3.0
-
-  assert cf.len == 3
-  cf.excl("c")
-  assert cf.len == 2
+func commonPrefixLen*[T](c: CritBitTree[T]): int {.inline, since((1, 3)).} =
+  ## Returns the length of the longest common prefix of all keys in `c`.
+  ## If `c` is empty, returns 0.
+  runnableExamples:
+    var c: CritBitTree[void]
+    doAssert c.commonPrefixLen == 0
+    incl(c, "key1")
+    doAssert c.commonPrefixLen == 4
+    incl(c, "key2")
+    doAssert c.commonPrefixLen == 3
+
+  if c.root != nil:
+    if c.root.isLeaf: len(c.root.key)
+    else: c.root.byte
+  else: 0
+
+proc toCritBitTree*[T](pairs: sink openArray[(string, T)]): CritBitTree[T] {.since: (1, 3).} =
+  ## Creates a new `CritBitTree` that contains the given `pairs`.
+  runnableExamples:
+    doAssert {"a": "0", "b": "1", "c": "2"}.toCritBitTree is CritBitTree[string]
+    doAssert {"a": 0, "b": 1, "c": 2}.toCritBitTree is CritBitTree[int]
+
+  for item in pairs: result.incl item[0], item[1]
+
+proc toCritBitTree*(items: sink openArray[string]): CritBitTree[void] {.since: (1, 3).} =
+  ## Creates a new `CritBitTree` that contains the given `items`.
+  runnableExamples:
+    doAssert ["a", "b", "c"].toCritBitTree is CritBitTree[void]
+
+  for item in items: result.incl item
diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim
index 328308a9b..d2b0099f2 100644
--- a/lib/pure/collections/deques.nim
+++ b/lib/pure/collections/deques.nim
@@ -7,263 +7,474 @@
 #    distribution, for details about the copyright.
 #
 
-## Implementation of a `deque`:idx: (double-ended queue).
-## The underlying implementation uses a ``seq``.
+## An implementation of a `deque`:idx: (double-ended queue).
+## The underlying implementation uses a `seq`.
 ##
-## None of the procs that get an individual value from the deque can be used
-## on an empty deque.
-## 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.
+## .. note:: None of the procs that get an individual value from the deque should be used
+##   on an empty deque.
+##
+## If compiled with the `boundChecks` option, those procs will raise an `IndexDefect`
+## on such access. This should not be relied upon, as `-d:danger` or `--checks:off` will
+## disable those checks and then the procs may return garbage or crash the program.
 ##
 ## As such, a check to see if the deque 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 deq = initDeque[int]()  # initializes the object
-##     for i in 1 ..< a: deq.addLast i  # populates the deque
-##
-##     if b < deq.len:  # checking before indexed access
-##       echo "The element at index position ", b, " is ", deq[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 deq.peekFirst == 1
-##     assert deq.peekLast == a
-##
-##     while deq.len > 0:  # checking if the deque is empty
-##       echo deq.popLast()
-##
-## Note: For inter thread communication use
-## a `Channel <channels.html>`_ instead.
 
-import math
+runnableExamples:
+  var a = [10, 20, 30, 40].toDeque
+
+  doAssertRaises(IndexDefect, echo a[4])
+
+  a.addLast(50)
+  assert $a == "[10, 20, 30, 40, 50]"
+
+  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
+
+import std/private/since
+
+import std/[assertions, hashes, math]
 
 type
   Deque*[T] = object
-    ## A double-ended queue backed with a ringed seq buffer.
+    ## A double-ended queue backed with a ringed `seq` buffer.
+    ##
+    ## To initialize an empty deque,
+    ## use the `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.
+    # `head` and `tail` are masked only when accessing an element of `data`
+    # so that `tail - head == data.len` when the deque is full.
+    # They are uint so that incrementing/decrementing them doesn't cause
+    # over/underflow. You can get a number of items with `tail - head`
+    # even if `tail` or `head` is wraps around and `tail < head`, because
+    # `tail - head == (uint.high + 1 + tail) - head` when `tail < head`.
+    head, tail: uint
+
+const
+  defaultInitialSize* = 4
+
+template initImpl(result: typed, initialSize: int) =
+  let correctSize = nextPowerOfTwo(initialSize)
+  newSeq(result.data, correctSize)
+
+template checkIfInitialized(deq: typed) =
+  if deq.data.len == 0:
+    initImpl(deq, defaultInitialSize)
+
+func mask[T](deq: Deque[T]): uint {.inline.} =
+  uint(deq.data.len) - 1
+
+proc initDeque*[T](initialSize: int = defaultInitialSize): Deque[T] =
+  ## Creates 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.
-  assert isPowerOfTwo(initialSize)
-  result.mask = initialSize-1
-  newSeq(result.data, initialSize)
+  ## Optionally, the initial capacity can be reserved via `initialSize`
+  ## as a performance optimization
+  ## (default: `defaultInitialSize <#defaultInitialSize>`_).
+  ## The length of a newly created deque will still be 0.
+  ##
+  ## **See also:**
+  ## * `toDeque proc <#toDeque,openArray[T]>`_
+  result.initImpl(initialSize)
 
-proc len*[T](deq: Deque[T]): int {.inline.} =
-  ## Return the number of elements of `deq`.
-  result = deq.count
+func len*[T](deq: Deque[T]): int {.inline.} =
+  ## Returns the number of elements of `deq`.
+  int(deq.tail - deq.head)
 
 template emptyCheck(deq) =
   # Bounds check for the regular deque access.
   when compileOption("boundChecks"):
-    if unlikely(deq.count < 1):
-      raise newException(IndexError, "Empty deque.")
+    if unlikely(deq.len < 1):
+      raise newException(IndexDefect, "Empty deque.")
 
 template xBoundsCheck(deq, i) =
   # Bounds check for the array like accesses.
-  when compileOption("boundChecks"):  # d:release should disable this.
-    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))
-
-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.
+  when compileOption("boundChecks"): # `-d:danger` or `--checks:off` should disable this.
+    if unlikely(i >= deq.len): # x < deq.low is taken care by the Natural parameter
+      raise newException(IndexDefect,
+                         "Out of bounds: " & $i & " > " & $(deq.len - 1))
+    if unlikely(i < 0): # when used with BackwardsIndex
+      raise newException(IndexDefect,
+                         "Out of bounds: " & $i & " < 0")
+
+proc `[]`*[T](deq: Deque[T], i: Natural): lent T {.inline.} =
+  ## Accesses the `i`-th element of `deq`.
+  runnableExamples:
+    let a = [10, 20, 30, 40, 50].toDeque
+    assert a[0] == 10
+    assert a[3] == 40
+    doAssertRaises(IndexDefect, echo a[8])
+
   xBoundsCheck(deq, i)
-  return deq.data[(deq.head + i) and deq.mask]
+  return deq.data[(deq.head + i.uint) 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
+  ## Accesses the `i`-th element of `deq` and returns a mutable
   ## reference to it.
+  runnableExamples:
+    var a = [10, 20, 30, 40, 50].toDeque
+    inc(a[0])
+    assert a[0] == 11
+
   xBoundsCheck(deq, i)
-  return deq.data[(deq.head + i) and deq.mask]
+  return deq.data[(deq.head + i.uint) and deq.mask]
+
+proc `[]=`*[T](deq: var Deque[T], i: Natural, val: sink T) {.inline.} =
+  ## Sets the `i`-th element of `deq` to `val`.
+  runnableExamples:
+    var a = [10, 20, 30, 40, 50].toDeque
+    a[0] = 99
+    a[3] = 66
+    assert $a == "[99, 20, 30, 66, 50]"
 
-proc `[]=`* [T] (deq: var Deque[T], i: Natural, val : T) {.inline.} =
-  ## Change the i-th element of `deq`.
+  checkIfInitialized(deq)
   xBoundsCheck(deq, i)
-  deq.data[(deq.head + i) and deq.mask] = val
+  deq.data[(deq.head + i.uint) and deq.mask] = val
 
-iterator items*[T](deq: Deque[T]): T =
-  ## Yield every element of `deq`.
-  var i = deq.head
-  for c in 0 ..< deq.count:
-    yield deq.data[i]
-    i = (i + 1) and deq.mask
+proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): lent T {.inline.} =
+  ## Accesses the backwards indexed `i`-th element.
+  ##
+  ## `deq[^1]` is the last element.
+  runnableExamples:
+    let a = [10, 20, 30, 40, 50].toDeque
+    assert a[^1] == 50
+    assert a[^4] == 20
+    doAssertRaises(IndexDefect, 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.} =
+  ## Accesses the backwards indexed `i`-th element and returns a mutable
+  ## reference to it.
+  ##
+  ## `deq[^1]` is the last element.
+  runnableExamples:
+    var a = [10, 20, 30, 40, 50].toDeque
+    inc(a[^1])
+    assert a[^1] == 51
+
+  xBoundsCheck(deq, deq.len - int(i))
+  return deq[deq.len - int(i)]
+
+proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: sink T) {.inline.} =
+  ## Sets the backwards indexed `i`-th element of `deq` to `x`.
+  ##
+  ## `deq[^1]` is the last element.
+  runnableExamples:
+    var a = [10, 20, 30, 40, 50].toDeque
+    a[^1] = 99
+    a[^3] = 77
+    assert $a == "[10, 20, 77, 40, 99]"
+
+  checkIfInitialized(deq)
+  xBoundsCheck(deq, deq.len - int(i))
+  deq[deq.len - int(i)] = x
+
+iterator items*[T](deq: Deque[T]): lent T =
+  ## Yields every element of `deq`.
+  ##
+  ## **See also:**
+  ## * `mitems iterator <#mitems.i,Deque[T]>`_
+  runnableExamples:
+    from std/sequtils import toSeq
+
+    let a = [10, 20, 30, 40, 50].toDeque
+    assert toSeq(a.items) == @[10, 20, 30, 40, 50]
+
+  for c in 0 ..< deq.len:
+    yield deq.data[(deq.head + c.uint) and deq.mask]
 
 iterator mitems*[T](deq: var Deque[T]): var T =
-  ## Yield every element of `deq`.
-  var i = deq.head
-  for c in 0 ..< deq.count:
-    yield deq.data[i]
-    i = (i + 1) and deq.mask
+  ## Yields every element of `deq`, which can be modified.
+  ##
+  ## **See also:**
+  ## * `items iterator <#items.i,Deque[T]>`_
+  runnableExamples:
+    var a = [10, 20, 30, 40, 50].toDeque
+    assert $a == "[10, 20, 30, 40, 50]"
+    for x in mitems(a):
+      x = 5 * x - 1
+    assert $a == "[49, 99, 149, 199, 249]"
+
+  for c in 0 ..< deq.len:
+    yield deq.data[(deq.head + c.uint) and deq.mask]
 
 iterator pairs*[T](deq: Deque[T]): tuple[key: int, val: T] =
-  ## Yield every (position, value) of `deq`.
-  var i = deq.head
-  for c in 0 ..< deq.count:
-    yield (c, deq.data[i])
-    i = (i + 1) and deq.mask
+  ## Yields every `(position, value)`-pair of `deq`.
+  runnableExamples:
+    from std/sequtils import toSeq
+
+    let a = [10, 20, 30].toDeque
+    assert toSeq(a.pairs) == @[(0, 10), (1, 20), (2, 30)]
+
+  for c in 0 ..< deq.len:
+    yield (c, deq.data[(deq.head + c.uint) 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``.
+  ## Returns true if `item` is in `deq` or false if not found.
   ##
-  ## .. code-block:: Nim
-  ##   if x in q:
-  ##     assert q.contains x
+  ## Usually used via the `in` operator.
+  ## It is the equivalent of `deq.find(item) >= 0`.
+  runnableExamples:
+    let q = [7, 9].toDeque
+    assert 7 in q
+    assert q.contains(7)
+    assert 8 notin q
+
   for e in deq:
     if e == item: return true
   return false
 
 proc expandIfNeeded[T](deq: var Deque[T]) =
-  var cap = deq.mask + 1
-  if unlikely(deq.count >= cap):
+  checkIfInitialized(deq)
+  let cap = deq.data.len
+  assert deq.len <= cap
+  if unlikely(deq.len == cap):
     var n = newSeq[T](cap * 2)
-    for i, x in pairs(deq):  # don't use copyMem because the GC and because it's slower.
-      shallowCopy(n[i], x)
-    shallowCopy(deq.data, n)
-    deq.mask = cap * 2 - 1
-    deq.tail = deq.count
+    var i = 0
+    for x in mitems(deq):
+      when nimvm: n[i] = x # workaround for VM bug
+      else: n[i] = move(x)
+      inc i
+    deq.data = move(n)
+    deq.tail = cap.uint
     deq.head = 0
 
-proc addFirst*[T](deq: var Deque[T], item: T) =
-  ## Add an `item` to the beginning of the `deq`.
+proc addFirst*[T](deq: var Deque[T], item: sink T) =
+  ## Adds an `item` to the beginning of `deq`.
+  ##
+  ## **See also:**
+  ## * `addLast proc <#addLast,Deque[T],sinkT>`_
+  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
-  deq.data[deq.head] = item
+  dec deq.head
+  deq.data[deq.head and deq.mask] = item
+
+proc addLast*[T](deq: var Deque[T], item: sink T) =
+  ## Adds an `item` to the end of `deq`.
+  ##
+  ## **See also:**
+  ## * `addFirst proc <#addFirst,Deque[T],sinkT>`_
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addLast(10 * i)
+    assert $a == "[10, 20, 30, 40, 50]"
 
-proc addLast*[T](deq: var Deque[T], item: T) =
-  ## Add an `item` to the end of the `deq`.
   expandIfNeeded(deq)
-  inc deq.count
-  deq.data[deq.tail] = item
-  deq.tail = (deq.tail + 1) and deq.mask
+  deq.data[deq.tail and deq.mask] = item
+  inc deq.tail
 
-proc peekFirst*[T](deq: Deque[T]): T {.inline.}=
+proc toDeque*[T](x: openArray[T]): Deque[T] {.since: (1, 3).} =
+  ## Creates a new deque that contains the elements of `x` (in the same order).
+  ##
+  ## **See also:**
+  ## * `initDeque proc <#initDeque,int>`_
+  runnableExamples:
+    let a = toDeque([7, 8, 9])
+    assert len(a) == 3
+    assert $a == "[7, 8, 9]"
+
+  result.initImpl(x.len)
+  for item in items(x):
+    result.addLast(item)
+
+proc peekFirst*[T](deq: Deque[T]): lent T {.inline.} =
   ## Returns the first element of `deq`, but does not remove it from the deque.
+  ##
+  ## **See also:**
+  ## * `peekFirst proc <#peekFirst,Deque[T]_2>`_ which returns a mutable reference
+  ## * `peekLast proc <#peekLast,Deque[T]>`_
+  runnableExamples:
+    let a = [10, 20, 30, 40, 50].toDeque
+    assert $a == "[10, 20, 30, 40, 50]"
+    assert a.peekFirst == 10
+    assert len(a) == 5
+
   emptyCheck(deq)
-  result = deq.data[deq.head]
+  result = deq.data[deq.head and deq.mask]
 
-proc peekLast*[T](deq: Deque[T]): T {.inline.} =
+proc peekLast*[T](deq: Deque[T]): lent T {.inline.} =
   ## Returns the last element of `deq`, but does not remove it from the deque.
+  ##
+  ## **See also:**
+  ## * `peekLast proc <#peekLast,Deque[T]_2>`_ which returns a mutable reference
+  ## * `peekFirst proc <#peekFirst,Deque[T]>`_
+  runnableExamples:
+    let a = [10, 20, 30, 40, 50].toDeque
+    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]
 
-template default[T](t: typedesc[T]): T =
-  var v: T
-  v
+proc peekFirst*[T](deq: var Deque[T]): var T {.inline, since: (1, 3).} =
+  ## Returns a mutable reference to the first element of `deq`,
+  ## but does not remove it from the deque.
+  ##
+  ## **See also:**
+  ## * `peekFirst proc <#peekFirst,Deque[T]>`_
+  ## * `peekLast proc <#peekLast,Deque[T]_2>`_
+  runnableExamples:
+    var a = [10, 20, 30, 40, 50].toDeque
+    a.peekFirst() = 99
+    assert $a == "[99, 20, 30, 40, 50]"
+
+  emptyCheck(deq)
+  result = deq.data[deq.head and deq.mask]
+
+proc peekLast*[T](deq: var Deque[T]): var T {.inline, since: (1, 3).} =
+  ## Returns a mutable reference to the last element of `deq`,
+  ## but does not remove it from the deque.
+  ##
+  ## **See also:**
+  ## * `peekFirst proc <#peekFirst,Deque[T]_2>`_
+  ## * `peekLast proc <#peekLast,Deque[T]>`_
+  runnableExamples:
+    var a = [10, 20, 30, 40, 50].toDeque
+    a.peekLast() = 99
+    assert $a == "[10, 20, 30, 40, 99]"
+
+  emptyCheck(deq)
+  result = deq.data[(deq.tail - 1) and deq.mask]
+
+template destroy(x: untyped) =
+  reset(x)
 
 proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
-  ## Remove and returns the first element of the `deq`.
+  ## Removes and returns the first element of the `deq`.
+  ##
+  ## See also:
+  ## * `popLast proc <#popLast,Deque[T]>`_
+  ## * `shrink proc <#shrink,Deque[T],int,int>`_
+  runnableExamples:
+    var a = [10, 20, 30, 40, 50].toDeque
+    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]
-  deq.data[deq.head] = default(type(result))
-  deq.head = (deq.head + 1) and deq.mask
+  result = move deq.data[deq.head and deq.mask]
+  inc deq.head
 
 proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
-  ## Remove and returns the last element of the `deq`.
+  ## Removes and returns the last element of the `deq`.
+  ##
+  ## **See also:**
+  ## * `popFirst proc <#popFirst,Deque[T]>`_
+  ## * `shrink proc <#shrink,Deque[T],int,int>`_
+  runnableExamples:
+    var a = [10, 20, 30, 40, 50].toDeque
+    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
-  result = deq.data[deq.tail]
-  deq.data[deq.tail] = default(type(result))
+  dec deq.tail
+  result = move deq.data[deq.tail and deq.mask]
+
+proc clear*[T](deq: var Deque[T]) {.inline.} =
+  ## Resets the deque so that it is empty.
+  ##
+  ## **See also:**
+  ## * `shrink proc <#shrink,Deque[T],int,int>`_
+  runnableExamples:
+    var a = [10, 20, 30, 40, 50].toDeque
+    assert $a == "[10, 20, 30, 40, 50]"
+    clear(a)
+    assert len(a) == 0
+
+  for el in mitems(deq): destroy(el)
+  deq.tail = deq.head
+
+proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) =
+  ## Removes `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.
+  ##
+  ## **See also:**
+  ## * `clear proc <#clear,Deque[T]>`_
+  ## * `popFirst proc <#popFirst,Deque[T]>`_
+  ## * `popLast proc <#popLast,Deque[T]>`_
+  runnableExamples:
+    var a = [10, 20, 30, 40, 50].toDeque
+    assert $a == "[10, 20, 30, 40, 50]"
+    a.shrink(fromFirst = 2, fromLast = 1)
+    assert $a == "[30, 40]"
+
+  if fromFirst + fromLast > deq.len:
+    clear(deq)
+    return
+
+  for i in 0 ..< fromFirst:
+    destroy(deq.data[deq.head and deq.mask])
+    inc deq.head
+
+  for i in 0 ..< fromLast:
+    destroy(deq.data[(deq.tail - 1) and deq.mask])
+    dec deq.tail
 
 proc `$`*[T](deq: Deque[T]): string =
-  ## Turn a deque into its string representation.
+  ## Turns a deque into its string representation.
+  runnableExamples:
+    let a = [10, 20, 30].toDeque
+    assert $a == "[10, 20, 30]"
+
   result = "["
   for x in deq:
     if result.len > 1: result.add(", ")
     result.addQuoted(x)
   result.add("]")
 
-when isMainModule:
-  var deq = initDeque[int](1)
-  deq.addLast(4)
-  deq.addFirst(9)
-  deq.addFirst(123)
-  var first = deq.popFirst()
-  deq.addLast(56)
-  assert(deq.peekLast() == 56)
-  deq.addLast(6)
-  assert(deq.peekLast() == 6)
-  var second = deq.popFirst()
-  deq.addLast(789)
-  assert(deq.peekLast() == 789)
-
-  assert first == 123
-  assert second == 9
-  assert($deq == "[4, 56, 6, 789]")
-
-  assert deq[0] == deq.peekFirst and deq.peekFirst == 4
-  #assert deq[^1] == deq.peekLast and deq.peekLast == 789
-  deq[0] = 42
-  deq[deq.len - 1] = 7
-
-  assert 6 in deq and 789 notin deq
-  assert deq.find(6) >= 0
-  assert deq.find(789) < 0
-
-  for i in -2 .. 10:
-    if i in deq:
-      assert deq.contains(i) and deq.find(i) >= 0
-    else:
-      assert(not deq.contains(i) and deq.find(i) < 0)
-
-  when compileOption("boundChecks"):
-    try:
-      echo deq[99]
-      assert false
-    except IndexError:
-      discard
-
-    try:
-      assert deq.len == 4
-      for i in 0 ..< 5: deq.popFirst()
-      assert false
-    except IndexError:
-      discard
-
-  # grabs some types of resize error.
-  deq = initDeque[int]()
-  for i in 1 .. 4: deq.addLast i
-  deq.popFirst()
-  deq.popLast()
-  for i in 5 .. 8: deq.addFirst i
-  assert $deq == "[8, 7, 6, 5, 2, 3]"
-
-  # Similar to proc from the documentation example
-  proc foo(a, b: Positive) = # assume random positive values for `a` and `b`.
-    var deq = initDeque[int]()
-    assert deq.len == 0
-    for i in 1 .. a: deq.addLast i
-
-    if b < deq.len: # checking before indexed access.
-      assert deq[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 deq.peekFirst == 1
-    assert deq.peekLast == a
-
-    while deq.len > 0: # checking if the deque is empty
-      assert deq.popFirst() > 0
-
-  #foo(0,0)
-  foo(8,5)
-  foo(10,9)
-  foo(1,1)
-  foo(2,1)
-  foo(1,5)
-  foo(3,2)
+func `==`*[T](deq1, deq2: Deque[T]): bool =
+  ## The `==` operator for Deque.
+  ## Returns `true` if both deques contains the same values in the same order.
+  runnableExamples:
+    var a, b = initDeque[int]()
+    a.addFirst(2)
+    a.addFirst(1)
+    b.addLast(1)
+    b.addLast(2)
+    doAssert a == b
+
+  if deq1.len != deq2.len:
+    return false
+
+  for i in 0 ..< deq1.len:
+    if deq1.data[(deq1.head + i.uint) and deq1.mask] != deq2.data[(deq2.head + i.uint) and deq2.mask]:
+      return false
+
+  true
+
+func hash*[T](deq: Deque[T]): Hash =
+  ## Hashing of Deque.
+  var h: Hash = 0
+  for x in deq:
+    h = h !& hash(x)
+  !$h
diff --git a/lib/pure/collections/hashcommon.nim b/lib/pure/collections/hashcommon.nim
new file mode 100644
index 000000000..17785c8c7
--- /dev/null
+++ b/lib/pure/collections/hashcommon.nim
@@ -0,0 +1,76 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2019 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# An `include` file which contains common code for
+# hash sets and tables.
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+import std / outparams
+
+const
+  growthFactor = 2
+
+# 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.
+proc isEmpty(hcode: Hash): bool {.inline.} =
+  result = hcode == 0
+
+proc isFilled(hcode: Hash): bool {.inline.} =
+  result = hcode != 0
+
+proc nextTry(h, maxHash: Hash): Hash {.inline.} =
+  result = (h + 1) and maxHash
+
+proc mustRehash[T](t: T): bool {.inline.} =
+  # If this is changed, make sure to synchronize it with `slotsNeeded` below
+  assert(t.dataLen > t.counter)
+  result = (t.dataLen * 2 < t.counter * 3) or (t.dataLen - t.counter < 4)
+
+proc slotsNeeded(count: Natural): int {.inline.} =
+  # Make sure to synchronize with `mustRehash` above
+  result = nextPowerOfTwo(count * 3 div 2 + 4)
+
+template rawGetKnownHCImpl() {.dirty.} =
+  if t.dataLen == 0:
+    return -1
+  var h: Hash = hc and maxHash(t) # start with real hash value
+  while isFilled(t.data[h].hcode):
+    # Compare hc THEN key with boolean short circuit. This makes the common case
+    # zero ==key's for missing (e.g.inserts) and exactly one ==key for present.
+    # It does slow down succeeding lookups by one extra Hash cmp&and..usually
+    # just a few clock cycles, generally worth it for any non-integer-like A.
+    if t.data[h].hcode == hc and t.data[h].key == key:
+      return h
+    h = nextTry(h, maxHash(t))
+  result = -1 - h # < 0 => MISSING; insert idx = -1 - result
+
+proc rawGetKnownHC[X, A](t: X, key: A, hc: Hash): int {.inline.} =
+  rawGetKnownHCImpl()
+
+template genHashImpl(key, hc: typed) =
+  hc = hash(key)
+  if hc == 0: # This almost never taken branch should be very predictable.
+    when sizeof(int) < 4:
+      hc = 31415 # Value doesn't matter; Any non-zero favorite is fine <= 16-bit.
+    else:
+      hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine.
+
+template genHash(key: typed): Hash =
+  var res: Hash
+  genHashImpl(key, res)
+  res
+
+template rawGetImpl() {.dirty.} =
+  genHashImpl(key, hc)
+  rawGetKnownHCImpl()
+
+proc rawGet[X, A](t: X, key: A, hc: var Hash): int {.inline, outParamsAt: [3].} =
+  rawGetImpl()
diff --git a/lib/pure/collections/heapqueue.nim b/lib/pure/collections/heapqueue.nim
index 60869142e..96f9b4430 100644
--- a/lib/pure/collections/heapqueue.nim
+++ b/lib/pure/collections/heapqueue.nim
@@ -1,4 +1,3 @@
-
 #
 #
 #            Nim's Runtime Library
@@ -7,155 +6,261 @@
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 
-##[ Heap queue algorithm (a.k.a. priority queue). Ported from Python heapq.
 
-Heaps are arrays for which a[k] <= a[2*k+1] and a[k] <= a[2*k+2] for
-all k, counting elements from 0.  For the sake of comparison,
-non-existing elements are considered to be infinite.  The interesting
-property of a heap is that a[0] is always its smallest element.
+## The `heapqueue` module implements a
+## `binary heap data structure<https://en.wikipedia.org/wiki/Binary_heap>`_
+## that can be used as a `priority queue<https://en.wikipedia.org/wiki/Priority_queue>`_.
+## They are represented as arrays for which `a[k] <= a[2*k+1]` and `a[k] <= a[2*k+2]`
+## for all indices `k` (counting elements from 0). The interesting property of a heap is that
+## `a[0]` is always its smallest element.
+##
+## Basic usage
+## -----------
+##
+runnableExamples:
+  var heap = [8, 2].toHeapQueue
+  heap.push(5)
+  # the first element is the lowest element
+  assert heap[0] == 2
+  # remove and return the lowest element
+  assert heap.pop() == 2
+  # the lowest element remaining is 5
+  assert heap[0] == 5
+
+## Usage with custom objects
+## -------------------------
+## To use a `HeapQueue` with a custom object, the `<` operator must be
+## implemented.
+
+runnableExamples:
+  type Job = object
+    priority: int
+
+  proc `<`(a, b: Job): bool = a.priority < b.priority
+
+  var jobs = initHeapQueue[Job]()
+  jobs.push(Job(priority: 1))
+  jobs.push(Job(priority: 2))
+
+  assert jobs[0].priority == 1
+
+
+import std/private/since
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
-]##
+type HeapQueue*[T] = object
+  ## A heap queue, commonly known as a priority queue.
+  data: seq[T]
 
-type HeapQueue*[T] = distinct seq[T]
+proc initHeapQueue*[T](): HeapQueue[T] =
+  ## Creates a new empty heap.
+  ##
+  ## Heaps are initialized by default, so it is not necessary to call
+  ## this function explicitly.
+  ##
+  ## **See also:**
+  ## * `toHeapQueue proc <#toHeapQueue,openArray[T]>`_
+  result = default(HeapQueue[T])
 
-proc newHeapQueue*[T](): HeapQueue[T] {.inline.} = HeapQueue[T](newSeq[T]())
-proc newHeapQueue*[T](h: var HeapQueue[T]) {.inline.} = h = HeapQueue[T](newSeq[T]())
+proc len*[T](heap: HeapQueue[T]): int {.inline.} =
+  ## Returns the number of elements of `heap`.
+  runnableExamples:
+    let heap = [9, 5, 8].toHeapQueue
+    assert heap.len == 3
 
-proc len*[T](h: HeapQueue[T]): int {.inline.} = seq[T](h).len
-proc `[]`*[T](h: HeapQueue[T], i: int): T {.inline.} = seq[T](h)[i]
-proc `[]=`[T](h: var HeapQueue[T], i: int, v: T) {.inline.} = seq[T](h)[i] = v
-proc add[T](h: var HeapQueue[T], v: T) {.inline.} = seq[T](h).add(v)
+  heap.data.len
 
-proc heapCmp[T](x, y: T): bool {.inline.} =
-  return (x < y)
+proc `[]`*[T](heap: HeapQueue[T], i: Natural): lent T {.inline.} =
+  ## Accesses the i-th element of `heap`.
+  heap.data[i]
 
-# 'heap' is a heap at all indices >= startpos, except possibly for pos.  pos
-# is the index of a leaf with a possibly out-of-order value.  Restore the
-# heap invariant.
-proc siftdown[T](heap: var HeapQueue[T], startpos, p: int) =
+iterator items*[T](heap: HeapQueue[T]): lent T {.inline, since: (2, 1, 1).} =
+  ## Iterates over each item of `heap`.
+  let L = len(heap)
+  for i in 0 .. high(heap.data):
+    yield heap.data[i]
+    assert(len(heap) == L, "the length of the HeapQueue changed while iterating over it")
+
+proc heapCmp[T](x, y: T): bool {.inline.} = x < y
+
+proc siftup[T](heap: var HeapQueue[T], startpos, p: int) =
+  ## `heap` is a heap at all indices >= `startpos`, except possibly for `p`. `p`
+  ## is the index of a leaf with a possibly out-of-order value. Restores the
+  ## heap invariant.
   var pos = p
-  var newitem = heap[pos]
+  let newitem = heap[pos]
   # Follow the path to the root, moving parents down until finding a place
   # newitem fits.
   while pos > startpos:
     let parentpos = (pos - 1) shr 1
     let parent = heap[parentpos]
     if heapCmp(newitem, parent):
-      heap[pos] = parent
+      heap.data[pos] = parent
       pos = parentpos
     else:
       break
-  heap[pos] = newitem
+  heap.data[pos] = newitem
 
-proc siftup[T](heap: var HeapQueue[T], p: int) =
+proc siftdownToBottom[T](heap: var HeapQueue[T], p: int) =
+  # This is faster when the element should be close to the bottom.
   let endpos = len(heap)
   var pos = p
   let startpos = pos
   let newitem = heap[pos]
   # Bubble up the smaller child until hitting a leaf.
-  var childpos = 2*pos + 1    # leftmost child position
+  var childpos = 2 * pos + 1 # leftmost child position
   while childpos < endpos:
     # Set childpos to index of smaller child.
     let rightpos = childpos + 1
     if rightpos < endpos and not heapCmp(heap[childpos], heap[rightpos]):
       childpos = rightpos
     # Move the smaller child up.
-    heap[pos] = heap[childpos]
+    heap.data[pos] = heap[childpos]
     pos = childpos
-    childpos = 2*pos + 1
-  # The leaf at pos is empty now.  Put newitem there, and bubble it up
+    childpos = 2 * pos + 1
+  # The leaf at pos is empty now. Put newitem there, and bubble it up
   # to its final resting place (by sifting its parents down).
-  heap[pos] = newitem
-  siftdown(heap, startpos, pos)
+  heap.data[pos] = newitem
+  siftup(heap, startpos, pos)
 
-proc push*[T](heap: var HeapQueue[T], item: T) =
-  ## Push item onto heap, maintaining the heap invariant.
-  (seq[T](heap)).add(item)
-  siftdown(heap, 0, len(heap)-1)
+proc siftdown[T](heap: var HeapQueue[T], p: int) =
+  let endpos = len(heap)
+  var pos = p
+  let newitem = heap[pos]
+  var childpos = 2 * pos + 1
+  while childpos < endpos:
+    let rightpos = childpos + 1
+    if rightpos < endpos and not heapCmp(heap[childpos], heap[rightpos]):
+      childpos = rightpos
+    if not heapCmp(heap[childpos], newitem):
+      break
+    heap.data[pos] = heap[childpos]
+    pos = childpos
+    childpos = 2 * pos + 1
+  heap.data[pos] = newitem
+
+proc push*[T](heap: var HeapQueue[T], item: sink T) =
+  ## Pushes `item` onto `heap`, maintaining the heap invariant.
+  heap.data.add(item)
+  siftup(heap, 0, len(heap) - 1)
+
+proc toHeapQueue*[T](x: openArray[T]): HeapQueue[T] {.since: (1, 3).} =
+  ## Creates a new HeapQueue that contains the elements of `x`.
+  ##
+  ## **See also:**
+  ## * `initHeapQueue proc <#initHeapQueue>`_
+  runnableExamples:
+    var heap = [9, 5, 8].toHeapQueue
+    assert heap.pop() == 5
+    assert heap[0] == 8
+
+  # see https://en.wikipedia.org/wiki/Binary_heap#Building_a_heap
+  result.data = @x
+  for i in countdown(x.len div 2 - 1, 0):
+    siftdown(result, i)
 
 proc pop*[T](heap: var HeapQueue[T]): T =
-  ## Pop the smallest item off the heap, maintaining the heap invariant.
-  let lastelt = seq[T](heap).pop()
+  ## Pops and returns the smallest item from `heap`,
+  ## maintaining the heap invariant.
+  runnableExamples:
+    var heap = [9, 5, 8].toHeapQueue
+    assert heap.pop() == 5
+
+  let lastelt = heap.data.pop()
   if heap.len > 0:
     result = heap[0]
-    heap[0] = lastelt
-    siftup(heap, 0)
+    heap.data[0] = lastelt
+    siftdownToBottom(heap, 0)
   else:
     result = lastelt
 
-proc del*[T](heap: var HeapQueue[T], index: int) =
-  ## Removes element at `index`, maintaining the heap invariant.
-  swap(seq[T](heap)[^1], seq[T](heap)[index])
+proc find*[T](heap: HeapQueue[T], x: T): int {.since: (1, 3).} =
+  ## Linear scan to find the index of the item `x` or -1 if not found.
+  runnableExamples:
+    let heap = [9, 5, 8].toHeapQueue
+    assert heap.find(5) == 0
+    assert heap.find(9) == 1
+    assert heap.find(777) == -1
+
+  result = -1
+  for i in 0 ..< heap.len:
+    if heap[i] == x: return i
+
+proc contains*[T](heap: HeapQueue[T], x: T): bool {.since: (2, 1, 1).} =
+  ## Returns true if `x` is in `heap` or false if not found. This is a shortcut
+  ## for `find(heap, x) >= 0`.
+  result = find(heap, x) >= 0
+
+proc del*[T](heap: var HeapQueue[T], index: Natural) =
+  ## Removes the element at `index` from `heap`, maintaining the heap invariant.
+  runnableExamples:
+    var heap = [9, 5, 8].toHeapQueue
+    heap.del(1)
+    assert heap[0] == 5
+    assert heap[1] == 8
+
+  swap(heap.data[^1], heap.data[index])
   let newLen = heap.len - 1
-  seq[T](heap).setLen(newLen)
+  heap.data.setLen(newLen)
   if index < newLen:
-    heap.siftup(index)
+    siftdownToBottom(heap, index)
 
-proc replace*[T](heap: var HeapQueue[T], item: T): T =
-  ## Pop and return the current smallest value, and add the new item.
-  ## This is more efficient than pop() followed by push(), and can be
-  ## more appropriate when using a fixed-size heap.  Note that the value
-  ## returned may be larger than item!  That constrains reasonable uses of
-  ## this routine unless written as part of a conditional replacement:
+proc replace*[T](heap: var HeapQueue[T], item: sink T): T =
+  ## Pops and returns the current smallest value, and add the new item.
+  ## This is more efficient than `pop()` followed by `push()`, and can be
+  ## more appropriate when using a fixed-size heap. Note that the value
+  ## returned may be larger than `item`! That constrains reasonable uses of
+  ## this routine unless written as part of a conditional replacement.
+  ##
+  ## **See also:**
+  ## * `pushpop proc <#pushpop,HeapQueue[T],sinkT>`_
+  runnableExamples:
+    var heap = [5, 12].toHeapQueue
+    assert heap.replace(6) == 5
+    assert heap.len == 2
+    assert heap[0] == 6
+    assert heap.replace(4) == 6
 
-  ##    if item > heap[0]:
-  ##        item = replace(heap, item)
   result = heap[0]
-  heap[0] = item
-  siftup(heap, 0)
-
-proc pushpop*[T](heap: var HeapQueue[T], item: T): T =
-  ## Fast version of a push followed by a pop.
-  if heap.len > 0 and heapCmp(heap[0], item):
-    swap(item, heap[0])
-    siftup(heap, 0)
-  return item
-
-when isMainModule:
-  proc toSortedSeq[T](h: HeapQueue[T]): seq[T] =
-    var tmp = h
-    result = @[]
-    while tmp.len > 0:
-      result.add(pop(tmp))
-
-  block: # Simple sanity test
-    var heap = newHeapQueue[int]()
-    let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
-    for item in data:
-      push(heap, item)
-    doAssert(heap[0] == 0)
-    doAssert(heap.toSortedSeq == @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
-
-  block: # Test del
-    var heap = newHeapQueue[int]()
-    let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
-    for item in data: push(heap, item)
-
-    heap.del(0)
-    doAssert(heap[0] == 1)
-
-    heap.del(seq[int](heap).find(7))
-    doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 5, 6, 8, 9])
-
-    heap.del(seq[int](heap).find(5))
-    doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 6, 8, 9])
-
-    heap.del(seq[int](heap).find(6))
-    doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 8, 9])
-
-    heap.del(seq[int](heap).find(2))
-    doAssert(heap.toSortedSeq == @[1, 3, 4, 8, 9])
-
-  block: # Test del last
-    var heap = newHeapQueue[int]()
-    let data = [1, 2, 3]
-    for item in data: push(heap, item)
-
-    heap.del(2)
-    doAssert(heap.toSortedSeq == @[1, 2])
+  heap.data[0] = item
+  siftdown(heap, 0)
 
-    heap.del(1)
-    doAssert(heap.toSortedSeq == @[1])
+proc pushpop*[T](heap: var HeapQueue[T], item: sink T): T =
+  ## Fast version of a `push()` followed by a `pop()`.
+  ##
+  ## **See also:**
+  ## * `replace proc <#replace,HeapQueue[T],sinkT>`_
+  runnableExamples:
+    var heap = [5, 12].toHeapQueue
+    assert heap.pushpop(6) == 5
+    assert heap.len == 2
+    assert heap[0] == 6
+    assert heap.pushpop(4) == 4
+
+  result = item
+  if heap.len > 0 and heapCmp(heap.data[0], result):
+    swap(result, heap.data[0])
+    siftdown(heap, 0)
+
+proc clear*[T](heap: var HeapQueue[T]) =
+  ## Removes all elements from `heap`, making it empty.
+  runnableExamples:
+    var heap = [9, 5, 8].toHeapQueue
+    heap.clear()
+    assert heap.len == 0
+
+  heap.data.setLen(0)
+
+proc `$`*[T](heap: HeapQueue[T]): string =
+  ## Turns a heap into its string representation.
+  runnableExamples:
+    let heap = [1, 2].toHeapQueue
+    assert $heap == "[1, 2]"
 
-    heap.del(0)
-    doAssert(heap.toSortedSeq == @[])
+  result = "["
+  for x in heap.data:
+    if result.len > 1: result.add(", ")
+    result.addQuoted(x)
+  result.add("]")
diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim
index bfecfe447..765a23e97 100644
--- a/lib/pure/collections/intsets.nim
+++ b/lib/pure/collections/intsets.nim
@@ -7,438 +7,17 @@
 #    distribution, for details about the copyright.
 #
 
-## 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.
+## Specialization of the generic `packedsets module <packedsets.html>`_
+## (see its documentation for more examples) for ordinal sparse sets.
 
-import
-  hashes, math
+import std/private/since
+import std/packedsets
+export packedsets
 
 type
-  BitScalar = int
+  IntSet* = PackedSet[int]
 
-const
-  InitIntSetSize = 8         # must be a power of two!
-  TrunkShift = 9
-  BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and
-                                  # divisible by 64
-  TrunkMask = BitsPerTrunk - 1
-  IntsPerTrunk = BitsPerTrunk div (sizeof(BitScalar) * 8)
-  IntShift = 5 + ord(sizeof(BitScalar) == 8) # 5 or 6, depending on int width
-  IntMask = 1 shl IntShift - 1
+proc toIntSet*(x: openArray[int]): IntSet {.since: (1, 3), inline.} = toPackedSet[int](x)
 
-type
-  PTrunk = ref Trunk
-  Trunk = object
-    next: PTrunk             # all nodes are connected with this pointer
-    key: int                 # start address at bit 0
-    bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector
-
-  TrunkSeq = seq[PTrunk]
-  IntSet* = object ## an efficient set of 'int' implemented as a sparse bit set
-    elems: int # only valid for small numbers
-    counter, max: int
-    head: PTrunk
-    data: TrunkSeq
-    a: array[0..33, int] # profiling shows that 34 elements are enough
-
-{.deprecated: [TIntSet: IntSet, TTrunk: Trunk, TTrunkSeq: TrunkSeq].}
-
-proc mustRehash(length, counter: int): bool {.inline.} =
-  assert(length > counter)
-  result = (length * 2 < counter * 3) or (length - counter < 4)
-
-proc nextTry(h, maxHash: Hash): Hash {.inline.} =
-  result = ((5 * h) + 1) and maxHash
-
-proc intSetGet(t: IntSet, key: int): PTrunk =
-  var h = key and t.max
-  while t.data[h] != nil:
-    if t.data[h].key == key:
-      return t.data[h]
-    h = nextTry(h, t.max)
-  result = nil
-
-proc intSetRawInsert(t: IntSet, data: var TrunkSeq, desc: PTrunk) =
-  var h = desc.key and t.max
-  while data[h] != nil:
-    assert(data[h] != desc)
-    h = nextTry(h, t.max)
-  assert(data[h] == nil)
-  data[h] = desc
-
-proc intSetEnlarge(t: var IntSet) =
-  var n: TrunkSeq
-  var oldMax = t.max
-  t.max = ((t.max + 1) * 2) - 1
-  newSeq(n, t.max + 1)
-  for i in countup(0, oldMax):
-    if t.data[i] != nil: intSetRawInsert(t, n, t.data[i])
-  swap(t.data, n)
-
-proc intSetPut(t: var IntSet, key: int): PTrunk =
-  var h = key and t.max
-  while t.data[h] != nil:
-    if t.data[h].key == key:
-      return t.data[h]
-    h = nextTry(h, t.max)
-  if mustRehash(t.max + 1, t.counter): intSetEnlarge(t)
-  inc(t.counter)
-  h = key and t.max
-  while t.data[h] != nil: h = nextTry(h, t.max)
-  assert(t.data[h] == nil)
-  new(result)
-  result.next = t.head
-  result.key = key
-  t.head = result
-  t.data[h] = result
-
-proc contains*(s: IntSet, key: int): bool =
-  ## 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
-  else:
-    var t = intSetGet(s, `shr`(key, TrunkShift))
-    if t != nil:
-      var u = key and TrunkMask
-      result = (t.bits[`shr`(u, IntShift)] and `shl`(1, u and IntMask)) != 0
-    else:
-      result = false
-
-iterator items*(s: IntSet): int {.inline.} =
-  ## iterates over any included element of `s`.
-  if s.elems <= s.a.len:
-    for i in 0..<s.elems:
-      yield s.a[i]
-  else:
-    var r = s.head
-    while r != nil:
-      var i = 0
-      while i <= high(r.bits):
-        var w = r.bits[i]
-        # taking a copy of r.bits[i] here is correct, because
-        # modifying operations are not allowed during traversation
-        var j = 0
-        while w != 0:         # test all remaining bits for zero
-          if (w and 1) != 0:  # the bit is set!
-            yield (r.key shl TrunkShift) or (i shl IntShift +% j)
-          inc(j)
-          w = w shr 1
-        inc(i)
-      r = r.next
-
-proc bitincl(s: var IntSet, key: int) {.inline.} =
-  var t = intSetPut(s, `shr`(key, TrunkShift))
-  var u = key and TrunkMask
-  t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] or
-      `shl`(1, u and IntMask)
-
-proc incl*(s: var IntSet, key: int) =
-  ## includes an element `key` in `s`.
-  if s.elems <= s.a.len:
-    for i in 0..<s.elems:
-      if s.a[i] == key: return
-    if s.elems < s.a.len:
-      s.a[s.elems] = key
-      inc s.elems
-      return
-    newSeq(s.data, InitIntSetSize)
-    s.max = InitIntSetSize-1
-    for i in 0..<s.elems:
-      bitincl(s, s.a[i])
-    s.elems = s.a.len + 1
-    # fall through:
-  bitincl(s, key)
-
-proc incl*(s: var IntSet, other: IntSet) =
-  ## Includes all elements from `other` into `s`.
-  for item in other: incl(s, item)
-
-proc exclImpl(s: var IntSet, key: int) =
-  if s.elems <= s.a.len:
-    for i in 0..<s.elems:
-      if s.a[i] == key:
-        s.a[i] = s.a[s.elems-1]
-        dec s.elems
-        return
-  else:
-    var t = intSetGet(s, `shr`(key, TrunkShift))
-    if t != nil:
-      var u = key and TrunkMask
-      t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] and
-          not `shl`(1, u and IntMask)
-
-proc excl*(s: var IntSet, key: int) =
-  ## excludes `key` from the set `s`.
-  exclImpl(s, key)
-
-proc excl*(s: var IntSet, other: IntSet) =
-  ## Excludes all elements from `other` from `s`.
-  for item in other: excl(s, item)
-
-proc missingOrExcl*(s: var IntSet, key: int) : bool =
-  ## 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`
-  ## and false is returned.
-  if s.elems <= s.a.len:
-    for i in 0..<s.elems:
-      if s.a[i] == key:
-        return true
-    incl(s, key)
-    result = false
-  else:
-    var t = intSetGet(s, `shr`(key, TrunkShift))
-    if t != nil:
-      var u = key and TrunkMask
-      result = (t.bits[`shr`(u, IntShift)] and `shl`(1, u and IntMask)) != 0
-      if not result:
-        t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] or
-            `shl`(1, u and IntMask)
-    else:
-      incl(s, key)
-      result = false
-
-proc initIntSet*: IntSet =
-  ## creates a new int set that is empty.
-
-  #newSeq(result.data, InitIntSetSize)
-  #result.max = InitIntSetSize-1
-  result.data = nil
-  result.max = 0
-  result.counter = 0
-  result.head = nil
-  result.elems = 0
-
-proc clear*(result: var IntSet) =
-  #setLen(result.data, InitIntSetSize)
-  #for i in 0..InitIntSetSize-1: result.data[i] = nil
-  #result.max = InitIntSetSize-1
-  result.data = nil
-  result.max = 0
-  result.counter = 0
-  result.head = nil
-  result.elems = 0
-
-proc isNil*(x: IntSet): bool {.inline.} = x.head.isNil and x.elems == 0
-
-proc assign*(dest: var IntSet, src: IntSet) =
-  ## copies `src` to `dest`. `dest` does not need to be initialized by
-  ## `initIntSet`.
-  if src.elems <= src.a.len:
-    dest.data = nil
-    dest.max = 0
-    dest.counter = src.counter
-    dest.head = nil
-    dest.elems = src.elems
-    dest.a = src.a
-  else:
-    dest.counter = src.counter
-    dest.max = src.max
-    newSeq(dest.data, src.data.len)
-
-    var it = src.head
-    while it != nil:
-
-      var h = it.key and dest.max
-      while dest.data[h] != nil: h = nextTry(h, dest.max)
-      assert(dest.data[h] == nil)
-
-      var n: PTrunk
-      new(n)
-      n.next = dest.head
-      n.key = it.key
-      n.bits = it.bits
-      dest.head = n
-      dest.data[h] = n
-
-      it = it.next
-
-proc union*(s1, s2: IntSet): IntSet =
-  ## Returns the union of the sets `s1` and `s2`.
-  result.assign(s1)
-  incl(result, s2)
-
-proc intersection*(s1, s2: IntSet): IntSet =
-  ## Returns the intersection of the sets `s1` and `s2`.
-  result = initIntSet()
-  for item in s1:
-    if contains(s2, item):
-      incl(result, item)
-
-proc difference*(s1, s2: IntSet): IntSet =
-  ## Returns the difference of the sets `s1` and `s2`.
-  result = initIntSet()
-  for item in s1:
-    if not contains(s2, item):
-      incl(result, item)
-
-proc symmetricDifference*(s1, s2: IntSet): IntSet =
-  ## Returns the symmetric difference of the sets `s1` and `s2`.
-  result.assign(s1)
-  for item in s2:
-    if containsOrIncl(result, item): excl(result, item)
-
-proc `+`*(s1, s2: IntSet): IntSet {.inline.} =
-  ## Alias for `union(s1, s2) <#union>`_.
-  result = union(s1, s2)
-
-proc `*`*(s1, s2: IntSet): IntSet {.inline.} =
-  ## Alias for `intersection(s1, s2) <#intersection>`_.
-  result = intersection(s1, s2)
-
-proc `-`*(s1, s2: IntSet): IntSet {.inline.} =
-  ## Alias for `difference(s1, s2) <#difference>`_.
-  result = difference(s1, s2)
-
-proc disjoint*(s1, s2: IntSet): bool =
-  ## Returns true iff the sets `s1` and `s2` have no items in common.
-  for item in s1:
-    if contains(s2, item):
-      return false
-  return true
-
-proc len*(s: IntSet): int {.inline.} =
-  ## Returns the number of keys in `s`.
-  if s.elems < s.a.len:
-    result = s.elems
-  else:
-    result = 0
-    for _ in s:
-      inc(result)
-
-proc card*(s: IntSet): int {.inline.} = 
-  ## alias for `len() <#len>` _.
-  result = s.len()
-
-proc `<=`*(s1, s2: IntSet): bool =
-  ## Returns true iff `s1` is subset of `s2`.
-  for item in s1:
-    if not s2.contains(item):
-      return false
-  return true
-
-proc `<`*(s1, s2: IntSet): bool =
-  ## Returns true iff `s1` is proper subset of `s2`.
-  return s1 <= s2 and not (s2 <= s1)
-
-proc `==`*(s1, s2: IntSet): bool =
-  ## Returns true if both `s` and `t` have the same members and set size.
-  return s1 <= s2 and s2 <= s1
-
-template dollarImpl(): untyped =
-  result = "{"
-  for key in items(s):
-    if result.len > 1: result.add(", ")
-    result.add($key)
-  result.add("}")
-
-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
-
-  var x = initIntSet()
-  x.incl(1)
-  x.incl(2)
-  x.incl(7)
-  x.incl(1056)
-
-  x.incl(1044)
-  x.excl(1044) 
-
-  assert x.containsOrIncl(888) == false
-  assert 888 in x
-  assert x.containsOrIncl(888) == true
-
-  assert x.missingOrExcl(888) == false
-  assert 888 notin x
-  assert x.missingOrExcl(888) == true
-
-  var xs = toSeq(items(x))
-  xs.sort(cmp[int])
-  assert xs == @[1, 2, 7, 1056]
-
-  var y: IntSet
-  assign(y, x)
-  var ys = toSeq(items(y))
-  ys.sort(cmp[int])
-  assert ys == @[1, 2, 7, 1056]
-
-  assert x == y
-
-  var z: IntSet
-  for i in 0..1000:
-    incl z, i
-    assert z.len() == i+1
-  for i in 0..1000:
-    assert z.contains(i)
-
-  var w = initIntSet()
-  w.incl(1)
-  w.incl(4)
-  w.incl(50)
-  w.incl(1001)
-  w.incl(1056)
-
-  var xuw = x.union(w)
-  var xuws = toSeq(items(xuw))
-  xuws.sort(cmp[int])
-  assert xuws == @[1, 2, 4, 7, 50, 1001, 1056]
-
-  var xiw = x.intersection(w)
-  var xiws = toSeq(items(xiw))
-  xiws.sort(cmp[int])
-  assert xiws == @[1, 1056]
-
-  var xdw = x.difference(w)
-  var xdws = toSeq(items(xdw))
-  xdws.sort(cmp[int])
-  assert xdws == @[2, 7]
-
-  var xsw = x.symmetricDifference(w)
-  var xsws = toSeq(items(xsw))
-  xsws.sort(cmp[int])
-  assert xsws == @[2, 4, 7, 50, 1001]
-
-  x.incl(w)
-  xs = toSeq(items(x))
-  xs.sort(cmp[int])
-  assert xs == @[1, 2, 4, 7, 50, 1001, 1056]
-
-  assert w <= x
-
-  assert w < x
-
-  assert(not disjoint(w, x))
-
-  var u = initIntSet()
-  u.incl(3)
-  u.incl(5)
-  u.incl(500)
-  assert disjoint(u, x)
-
-  var v = initIntSet()
-  v.incl(2)
-  v.incl(50)
+proc initIntSet*(): IntSet {.inline.} = initPackedSet[int]()
 
-  x.excl(v)
-  xs = toSeq(items(x))
-  xs.sort(cmp[int])
-  assert xs == @[1, 4, 7, 1001, 1056]
diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim
index e69acc8d9..6b88747ef 100644
--- a/lib/pure/collections/lists.nim
+++ b/lib/pure/collections/lists.nim
@@ -7,34 +7,96 @@
 #    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.
-
-when not defined(nimhygiene):
-  {.pragma: dirty.}
+## 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
+runnableExamples:
+  var list = initDoublyLinkedList[int]()
+  let
+    a = newDoublyLinkedNode[int](3)
+    b = newDoublyLinkedNode[int](7)
+    c = newDoublyLinkedNode[int](9)
+
+  list.add(a)
+  list.add(b)
+  list.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
+runnableExamples:
+  var ring = initSinglyLinkedRing[int]()
+  let
+    a = newSinglyLinkedNode[int](3)
+    b = newSinglyLinkedNode[int](7)
+    c = newSinglyLinkedNode[int](9)
+
+  ring.add(a)
+  ring.add(b)
+  ring.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
+
+import std/private/since
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 type
-  DoublyLinkedNodeObj*[T] = object ## a node a doubly linked list consists of
-    next*, prev*: ref DoublyLinkedNodeObj[T]
+  DoublyLinkedNodeObj*[T] = object
+    ## A node of a doubly linked list.
+    ##
+    ## It consists of a `value` field, and pointers to `next` and `prev`.
+    next*: DoublyLinkedNode[T]
+    prev* {.cursor.}: DoublyLinkedNode[T]
     value*: T
   DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T]
 
-  SinglyLinkedNodeObj*[T] = object ## a node a singly linked list consists of
-    next*: ref SinglyLinkedNodeObj[T]
+  SinglyLinkedNodeObj*[T] = object
+    ## A node of a singly linked list.
+    ##
+    ## It consists of a `value` field, and a pointer to `next`.
+    next*: SinglyLinkedNode[T]
     value*: T
   SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[T]
 
-  SinglyLinkedList*[T] = object ## a singly linked list
-    head*, tail*: SinglyLinkedNode[T]
+  SinglyLinkedList*[T] = object
+    ## A singly linked list.
+    head*: SinglyLinkedNode[T]
+    tail* {.cursor.}: SinglyLinkedNode[T]
 
-  DoublyLinkedList*[T] = object ## a doubly linked list
-    head*, tail*: DoublyLinkedNode[T]
+  DoublyLinkedList*[T] = object
+    ## A doubly linked list.
+    head*: DoublyLinkedNode[T]
+    tail* {.cursor.}: DoublyLinkedNode[T]
 
-  SinglyLinkedRing*[T] = object ## a singly linked ring
-    head*, tail*: SinglyLinkedNode[T]
+  SinglyLinkedRing*[T] = object
+    ## A singly linked ring.
+    head*: SinglyLinkedNode[T]
+    tail* {.cursor.}: SinglyLinkedNode[T]
 
-  DoublyLinkedRing*[T] = object ## a doubly linked ring
+  DoublyLinkedRing*[T] = object
+    ## A doubly linked ring.
     head*: DoublyLinkedNode[T]
 
   SomeLinkedList*[T] = SinglyLinkedList[T] | DoublyLinkedList[T]
@@ -45,49 +107,72 @@ type
 
   SomeLinkedNode*[T] = SinglyLinkedNode[T] | DoublyLinkedNode[T]
 
-{.deprecated: [TDoublyLinkedNode: DoublyLinkedNodeObj,
-    PDoublyLinkedNode: DoublyLinkedNode,
-    TSinglyLinkedNode: SinglyLinkedNodeObj,
-    PSinglyLinkedNode: SinglyLinkedNode,
-    TDoublyLinkedList: DoublyLinkedList,
-    TSinglyLinkedRing: SinglyLinkedRing,
-    TDoublyLinkedRing: DoublyLinkedRing,
-    TSinglyLinkedList: SinglyLinkedList].}
-
 proc initSinglyLinkedList*[T](): SinglyLinkedList[T] =
-  ## creates a new singly linked list that is empty.
+  ## Creates a new singly linked list that is empty.
+  ##
+  ## Singly linked lists are initialized by default, so it is not necessary to
+  ## call this function explicitly.
+  runnableExamples:
+    let 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.
+  ##
+  ## Doubly linked lists are initialized by default, so it is not necessary to
+  ## call this function explicitly.
+  runnableExamples:
+    let 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.
+  ##
+  ## Singly linked rings are initialized by default, so it is not necessary to
+  ## call this function explicitly.
+  runnableExamples:
+    let 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.
+  ##
+  ## Doubly linked rings are initialized by default, so it is not necessary to
+  ## call this function explicitly.
+  runnableExamples:
+    let 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:
+    let 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:
+    let n = newSinglyLinkedNode[int](5)
+    assert n.value == 5
+
   new(result)
   result.value = value
 
 template itemsListImpl() {.dirty.} =
-  var it = L.head
+  var it {.cursor.} = L.head
   while it != nil:
     yield it.value
     it = it.next
 
 template itemsRingImpl() {.dirty.} =
-  var it = L.head
+  var it {.cursor.} = L.head
   if it != nil:
     while true:
       yield it.value
@@ -95,43 +180,129 @@ 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]>`_
+  runnableExamples:
+    from std/sugar import collect
+    from std/sequtils import toSeq
+    let a = collect(initSinglyLinkedList):
+      for i in 1..3: 10 * i
+    assert toSeq(items(a)) == toSeq(a)
+    assert toSeq(a) == @[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]>`_
+  runnableExamples:
+    from std/sugar import collect
+    from std/sequtils import toSeq
+    let a = collect(initSinglyLinkedRing):
+      for i in 1..3: 10 * i
+    assert toSeq(items(a)) == toSeq(a)
+    assert toSeq(a) == @[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.add(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.add(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.
-  var it = L.head
+  ##
+  ## **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.add(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 {.cursor.} = L.head
   while it != nil:
-    var nxt = it.next
+    let nxt = it.next
     yield it
     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.
-  var it = L.head
+  ##
+  ## **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.add(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 {.cursor.} = L.head
   if it != nil:
     while true:
-      var nxt = it.next
+      let nxt = it.next
       yield it
       it = nxt
       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.
+  runnableExamples:
+    let a = [1, 2, 3, 4].toSinglyLinkedList
+    assert $a == "[1, 2, 3, 4]"
+
   result = "["
   for x in nodes(L):
     if result.len > 1: result.add(", ")
@@ -139,28 +310,228 @@ 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:
+    let a = [9, 8].toSinglyLinkedList
+    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. This allows the usage of the `in` and `notin`
+  ## operators.
+  ##
+  ## **See also:**
+  ## * `find proc <#find,SomeLinkedCollection[T],T>`_
+  runnableExamples:
+    let a = [9, 8].toSinglyLinkedList
+    assert a.contains(9)
+    assert 8 in a
+    assert(not a.contains(1))
+    assert 2 notin a
+
   result = find(L, value) != nil
 
+proc prepend*[T: SomeLinkedList](a: var T, b: T) {.since: (1, 5, 1).} =
+  ## Prepends a shallow copy of `b` to the beginning of `a`.
+  ##
+  ## **See also:**
+  ## * `prependMoved proc <#prependMoved,T,T>`_
+  ##   for moving the second list instead of copying
+  runnableExamples:
+    from std/sequtils import toSeq
+    var a = [4, 5].toSinglyLinkedList
+    let b = [1, 2, 3].toSinglyLinkedList
+    a.prepend(b)
+    assert a.toSeq == [1, 2, 3, 4, 5]
+    assert b.toSeq == [1, 2, 3]
+    a.prepend(a)
+    assert a.toSeq == [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
+
+  var tmp = b.copy
+  tmp.addMoved(a)
+  a = tmp
+
+proc prependMoved*[T: SomeLinkedList](a, b: var T) {.since: (1, 5, 1).} =
+  ## Moves `b` before the head of `a`. Efficiency: O(1).
+  ## Note that `b` becomes empty after the operation unless it has the same address as `a`.
+  ## Self-prepending results in a cycle.
+  ##
+  ## **See also:**
+  ## * `prepend proc <#prepend,T,T>`_
+  ##   for prepending a copy of a list
+  runnableExamples:
+    import std/[sequtils, enumerate, sugar]
+    var
+      a = [4, 5].toSinglyLinkedList
+      b = [1, 2, 3].toSinglyLinkedList
+      c = [0, 1].toSinglyLinkedList
+    a.prependMoved(b)
+    assert a.toSeq == [1, 2, 3, 4, 5]
+    assert b.toSeq == []
+    c.prependMoved(c)
+    let s = collect:
+      for i, ci in enumerate(c):
+        if i == 6: break
+        ci
+    assert s == [0, 1, 0, 1, 0, 1]
+
+  b.addMoved(a)
+  swap a, b
+
+proc add*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]) {.inline.} =
+  ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
+  ##
+  ## **See also:**
+  ## * `add proc <#add,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]()
+    let n = newSinglyLinkedNode[int](9)
+    a.add(n)
+    assert a.contains(9)
+
+  n.next = nil
+  if L.tail != nil:
+    assert(L.tail.next == nil)
+    L.tail.next = n
+  L.tail = n
+  if L.head == nil: L.head = n
+
+proc add*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
+  ## Appends (adds to the end) a value to `L`. Efficiency: O(1).
+  ##
+  ## **See also:**
+  ## * `add proc <#add,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.add(9)
+    a.add(8)
+    assert a.contains(9)
+
+  add(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:**
+  ## * `add proc <#add,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `add proc <#add,SinglyLinkedList[T],T>`_ for appending a value
+  ## * `prepend proc <#prepend,SinglyLinkedList[T],T>`_ for prepending a value
+  runnableExamples:
+    var a = initSinglyLinkedList[int]()
+    let 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:**
+  ## * `add proc <#add,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `add proc <#add,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).
+func copy*[T](a: SinglyLinkedList[T]): SinglyLinkedList[T] {.since: (1, 5, 1).} =
+  ## Creates a shallow copy of `a`.
+  runnableExamples:
+    from std/sequtils import toSeq
+    type Foo = ref object
+      x: int
+    var
+      f = Foo(x: 1)
+      a = [f].toSinglyLinkedList
+    let b = a.copy
+    a.add([f].toSinglyLinkedList)
+    assert a.toSeq == [f, f]
+    assert b.toSeq == [f] # b isn't modified...
+    f.x = 42
+    assert a.head.value.x == 42
+    assert b.head.value.x == 42 # ... but the elements are not deep copied
+
+    let c = [1, 2, 3].toSinglyLinkedList
+    assert $c == $c.copy
+
+  result = initSinglyLinkedList[T]()
+  for x in a.items:
+    result.add(x)
+
+proc addMoved*[T](a, b: var SinglyLinkedList[T]) {.since: (1, 5, 1).} =
+  ## Moves `b` to the end of `a`. Efficiency: O(1).
+  ## Note that `b` becomes empty after the operation unless it has the same address as `a`.
+  ## Self-adding results in a cycle.
+  ##
+  ## **See also:**
+  ## * `add proc <#add,T,T>`_ for adding a copy of a list
+  runnableExamples:
+    import std/[sequtils, enumerate, sugar]
+    var
+      a = [1, 2, 3].toSinglyLinkedList
+      b = [4, 5].toSinglyLinkedList
+      c = [0, 1].toSinglyLinkedList
+    a.addMoved(b)
+    assert a.toSeq == [1, 2, 3, 4, 5]
+    assert b.toSeq == []
+    c.addMoved(c)
+    let s = collect:
+      for i, ci in enumerate(c):
+        if i == 6: break
+        ci
+    assert s == [0, 1, 0, 1, 0, 1]
+
+  if b.head != nil:
+    if a.head == nil:
+      a.head = b.head
+    else:
+      a.tail.next = b.head
+    a.tail = b.tail
+  if a.addr != b.addr:
+    b.head = nil
+    b.tail = nil
+
+proc add*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
+  ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
+  ##
+  ## **See also:**
+  ## * `add proc <#add,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]()
+    let n = newDoublyLinkedNode[int](9)
+    a.add(n)
+    assert a.contains(9)
+
   n.next = nil
   n.prev = L.tail
   if L.tail != nil:
@@ -169,12 +540,41 @@ proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
   L.tail = n
   if L.head == nil: L.head = n
 
-proc append*[T](L: var DoublyLinkedList[T], value: T) =
-  ## appends a value to `L`. Efficiency: O(1).
-  append(L, newDoublyLinkedNode(value))
+proc add*[T](L: var DoublyLinkedList[T], value: T) =
+  ## Appends (adds to the end) a value to `L`. Efficiency: O(1).
+  ##
+  ## **See also:**
+  ## * `add proc <#add,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.add(9)
+    a.add(8)
+    assert a.contains(9)
+
+  add(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:**
+  ## * `add proc <#add,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `add proc <#add,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]()
+    let n = newDoublyLinkedNode[int](9)
+    a.prepend(n)
+    assert a.contains(9)
+
   n.prev = nil
   n.next = L.head
   if L.head != nil:
@@ -184,34 +584,224 @@ 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:**
+  ## * `add proc <#add,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `add proc <#add,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))
 
+func copy*[T](a: DoublyLinkedList[T]): DoublyLinkedList[T] {.since: (1, 5, 1).} =
+  ## Creates a shallow copy of `a`.
+  runnableExamples:
+    from std/sequtils import toSeq
+    type Foo = ref object
+      x: int
+    var
+      f = Foo(x: 1)
+      a = [f].toDoublyLinkedList
+    let b = a.copy
+    a.add([f].toDoublyLinkedList)
+    assert a.toSeq == [f, f]
+    assert b.toSeq == [f] # b isn't modified...
+    f.x = 42
+    assert a.head.value.x == 42
+    assert b.head.value.x == 42 # ... but the elements are not deep copied
+
+    let c = [1, 2, 3].toDoublyLinkedList
+    assert $c == $c.copy
+
+  result = initDoublyLinkedList[T]()
+  for x in a.items:
+    result.add(x)
+
+proc addMoved*[T](a, b: var DoublyLinkedList[T]) {.since: (1, 5, 1).} =
+  ## Moves `b` to the end of `a`. Efficiency: O(1).
+  ## Note that `b` becomes empty after the operation unless it has the same address as `a`.
+  ## Self-adding results in a cycle.
+  ##
+  ## **See also:**
+  ## * `add proc <#add,T,T>`_
+  ##   for adding a copy of a list
+  runnableExamples:
+    import std/[sequtils, enumerate, sugar]
+    var
+      a = [1, 2, 3].toDoublyLinkedList
+      b = [4, 5].toDoublyLinkedList
+      c = [0, 1].toDoublyLinkedList
+    a.addMoved(b)
+    assert a.toSeq == [1, 2, 3, 4, 5]
+    assert b.toSeq == []
+    c.addMoved(c)
+    let s = collect:
+      for i, ci in enumerate(c):
+        if i == 6: break
+        ci
+    assert s == [0, 1, 0, 1, 0, 1]
+
+  if b.head != nil:
+    if a.head == nil:
+      a.head = b.head
+    else:
+      b.head.prev = a.tail
+      a.tail.next = b.head
+    a.tail = b.tail
+  if a.addr != b.addr:
+    b.head = nil
+    b.tail = nil
+
+proc add*[T: SomeLinkedList](a: var T, b: T) {.since: (1, 5, 1).} =
+  ## Appends a shallow copy of `b` to the end of `a`.
+  ##
+  ## **See also:**
+  ## * `addMoved proc <#addMoved,SinglyLinkedList[T],SinglyLinkedList[T]>`_
+  ## * `addMoved proc <#addMoved,DoublyLinkedList[T],DoublyLinkedList[T]>`_
+  ##   for moving the second list instead of copying
+  runnableExamples:
+    from std/sequtils import toSeq
+    var a = [1, 2, 3].toSinglyLinkedList
+    let b = [4, 5].toSinglyLinkedList
+    a.add(b)
+    assert a.toSeq == [1, 2, 3, 4, 5]
+    assert b.toSeq == [4, 5]
+    a.add(a)
+    assert a.toSeq == [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
+
+  var tmp = b.copy
+  a.addMoved(tmp)
+
+proc remove*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]): bool {.discardable.} =
+  ## Removes a node `n` from `L`.
+  ## Returns `true` if `n` was found in `L`.
+  ## Efficiency: O(n); the list is traversed until `n` is found.
+  ## Attempting to remove an element not contained in the list is a no-op.
+  ## When the list is cyclic, the cycle is preserved after removal.
+  runnableExamples:
+    import std/[sequtils, enumerate, sugar]
+    var a = [0, 1, 2].toSinglyLinkedList
+    let n = a.head.next
+    assert n.value == 1
+    assert a.remove(n) == true
+    assert a.toSeq == [0, 2]
+    assert a.remove(n) == false
+    assert a.toSeq == [0, 2]
+    a.addMoved(a) # cycle: [0, 2, 0, 2, ...]
+    a.remove(a.head)
+    let s = collect:
+      for i, ai in enumerate(a):
+        if i == 4: break
+        ai
+    assert s == [2, 2, 2, 2]
+
+  if n == L.head:
+    L.head = n.next
+    if L.tail.next == n:
+      L.tail.next = L.head # restore cycle
+  else:
+    var prev {.cursor.} = L.head
+    while prev.next != n and prev.next != nil:
+      prev = prev.next
+    if prev.next == nil:
+      return false
+    prev.next = n.next
+    if L.tail == n:
+      L.tail = prev # update tail if we removed the last node
+  true
+
 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).
+  ## This function assumes, for the sake of efficiency, that `n` is contained in `L`,
+  ## otherwise the effects are undefined.
+  ## When the list is cyclic, the cycle is preserved after removal.
+  runnableExamples:
+    import std/[sequtils, enumerate, sugar]
+    var a = [0, 1, 2].toSinglyLinkedList
+    let n = a.head.next
+    assert n.value == 1
+    a.remove(n)
+    assert a.toSeq == [0, 2]
+    a.remove(n)
+    assert a.toSeq == [0, 2]
+    a.addMoved(a) # cycle: [0, 2, 0, 2, ...]
+    a.remove(a.head)
+    let s = collect:
+      for i, ai in enumerate(a):
+        if i == 4: break
+        ai
+    assert s == [2, 2, 2, 2]
+
   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).
+
+
+proc add*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
+  ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
+  ##
+  ## **See also:**
+  ## * `add proc <#add,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]()
+    let n = newSinglyLinkedNode[int](9)
+    a.add(n)
+    assert a.contains(9)
+
   if L.head != nil:
     n.next = L.head
     assert(L.tail != nil)
     L.tail.next = n
-    L.tail = n
   else:
     n.next = n
     L.head = n
-    L.tail = n
+  L.tail = n
 
-proc append*[T](L: var SinglyLinkedRing[T], value: T) =
-  ## appends a value to `L`. Efficiency: O(1).
-  append(L, newSinglyLinkedNode(value))
+proc add*[T](L: var SinglyLinkedRing[T], value: T) =
+  ## Appends (adds to the end) a value to `L`. Efficiency: O(1).
+  ##
+  ## **See also:**
+  ## * `add proc <#add,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.add(9)
+    a.add(8)
+    assert a.contains(9)
+
+  add(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:**
+  ## * `add proc <#add,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `add proc <#add,SinglyLinkedRing[T],T>`_ for appending a value
+  ## * `prepend proc <#prepend,SinglyLinkedRing[T],T>`_ for prepending a value
+  runnableExamples:
+    var a = initSinglyLinkedRing[int]()
+    let n = newSinglyLinkedNode[int](9)
+    a.prepend(n)
+    assert a.contains(9)
+
   if L.head != nil:
     n.next = L.head
     assert(L.tail != nil)
@@ -222,11 +812,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:**
+  ## * `add proc <#add,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `add proc <#add,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).
+
+
+proc add*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
+  ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
+  ##
+  ## **See also:**
+  ## * `add proc <#add,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]()
+    let n = newDoublyLinkedNode[int](9)
+    a.add(n)
+    assert a.contains(9)
+
   if L.head != nil:
     n.next = L.head
     n.prev = L.head.prev
@@ -237,12 +856,41 @@ proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
     n.next = n
     L.head = n
 
-proc append*[T](L: var DoublyLinkedRing[T], value: T) =
-  ## appends a value to `L`. Efficiency: O(1).
-  append(L, newDoublyLinkedNode(value))
+proc add*[T](L: var DoublyLinkedRing[T], value: T) =
+  ## Appends (adds to the end) a value to `L`. Efficiency: O(1).
+  ##
+  ## **See also:**
+  ## * `add proc <#add,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.add(9)
+    a.add(8)
+    assert a.contains(9)
+
+  add(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:**
+  ## * `add proc <#add,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `add proc <#add,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]()
+    let n = newDoublyLinkedNode[int](9)
+    a.prepend(n)
+    assert a.contains(9)
+
   if L.head != nil:
     n.next = L.head
     n.prev = L.head.prev
@@ -254,17 +902,114 @@ 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:**
+  ## * `add proc <#add,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `add proc <#add,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).
+  ## This function assumes, for the sake of efficiency, that `n` is contained in `L`,
+  ## otherwise the effects are undefined.
+  runnableExamples:
+    var a = initDoublyLinkedRing[int]()
+    let n = newDoublyLinkedNode[int](5)
+    a.add(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:
-    var p = L.head.prev
+    let p = L.head.prev
     if p == L.head:
       # only one element left:
       L.head = nil
     else:
-      L.head = L.head.prev
+      L.head = p
+
+proc append*[T](a: var (SinglyLinkedList[T] | SinglyLinkedRing[T]),
+                b: SinglyLinkedList[T] | SinglyLinkedNode[T] | T) =
+  ## Alias for `a.add(b)`.
+  ##
+  ## **See also:**
+  ## * `add proc <#add,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
+  ## * `add proc <#add,SinglyLinkedList[T],T>`_
+  ## * `add proc <#add,T,T>`_
+  a.add(b)
+
+proc append*[T](a: var (DoublyLinkedList[T] | DoublyLinkedRing[T]),
+                b: DoublyLinkedList[T] | DoublyLinkedNode[T] | T) =
+  ## Alias for `a.add(b)`.
+  ##
+  ## **See also:**
+  ## * `add proc <#add,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ## * `add proc <#add,DoublyLinkedList[T],T>`_
+  ## * `add proc <#add,T,T>`_
+  a.add(b)
+
+proc appendMoved*[T: SomeLinkedList](a, b: var T) {.since: (1, 5, 1).} =
+  ## Alias for `a.addMoved(b)`.
+  ##
+  ## **See also:**
+  ## * `addMoved proc <#addMoved,SinglyLinkedList[T],SinglyLinkedList[T]>`_
+  ## * `addMoved proc <#addMoved,DoublyLinkedList[T],DoublyLinkedList[T]>`_
+  a.addMoved(b)
+
+func toSinglyLinkedList*[T](elems: openArray[T]): SinglyLinkedList[T] {.since: (1, 5, 1).} =
+  ## Creates a new `SinglyLinkedList` from the members of `elems`.
+  runnableExamples:
+    from std/sequtils import toSeq
+    let a = [1, 2, 3, 4, 5].toSinglyLinkedList
+    assert a.toSeq == [1, 2, 3, 4, 5]
+
+  result = initSinglyLinkedList[T]()
+  for elem in elems.items:
+    result.add(elem)
+
+func toSinglyLinkedRing*[T](elems: openArray[T]): SinglyLinkedRing[T] =
+  ## Creates a new `SinglyLinkedRing` from the members of `elems`.
+  runnableExamples:
+    from std/sequtils import toSeq
+    let a = [1, 2, 3, 4, 5].toSinglyLinkedRing
+    assert a.toSeq == [1, 2, 3, 4, 5]
+
+  result = initSinglyLinkedRing[T]()
+  for elem in elems.items:
+    result.add(elem)
+
+func toDoublyLinkedList*[T](elems: openArray[T]): DoublyLinkedList[T] {.since: (1, 5, 1).} =
+  ## Creates a new `DoublyLinkedList` from the members of `elems`.
+  runnableExamples:
+    from std/sequtils import toSeq
+    let a = [1, 2, 3, 4, 5].toDoublyLinkedList
+    assert a.toSeq == [1, 2, 3, 4, 5]
+
+  result = initDoublyLinkedList[T]()
+  for elem in elems.items:
+    result.add(elem)
+
+func toDoublyLinkedRing*[T](elems: openArray[T]): DoublyLinkedRing[T] =
+  ## Creates a new `DoublyLinkedRing` from the members of `elems`.
+  runnableExamples:
+    from std/sequtils import toSeq
+    let a = [1, 2, 3, 4, 5].toDoublyLinkedRing
+    assert a.toSeq == [1, 2, 3, 4, 5]
+
+  result = initDoublyLinkedRing[T]()
+  for elem in elems.items:
+    result.add(elem)
diff --git a/lib/pure/collections/queues.nim b/lib/pure/collections/queues.nim
deleted file mode 100644
index ce792d6da..000000000
--- a/lib/pure/collections/queues.nim
+++ /dev/null
@@ -1,259 +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
-
-{.deprecated: [TQueue: Queue].}
-
-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/rtarrays.nim b/lib/pure/collections/rtarrays.nim
index 3849117a0..3c3ffda7c 100644
--- a/lib/pure/collections/rtarrays.nim
+++ b/lib/pure/collections/rtarrays.nim
@@ -10,16 +10,17 @@
 
 ## Module that implements a fixed length array whose size
 ## is determined at runtime. Note: This is not ready for other people to use!
+##
+## Unstable API.
 
 const
   ArrayPartSize = 10
 
 type
-  RtArray*[T] = object  ##
+  RtArray*[T] = object ##
     L: Natural
     spart: seq[T]
     apart: array[ArrayPartSize, T]
-  UncheckedArray* {.unchecked.}[T] = array[0, T]
 
 template usesSeqPart(x): untyped = x.L > ArrayPartSize
 
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index 44c59c627..3c0d8dc0e 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -7,36 +7,131 @@
 #    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 the `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.
-
-include "system/inclrtl"
-
-import macros
-
-when not defined(nimhygiene):
-  {.pragma: dirty.}
-
-proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
+## 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:
+## * the `sugar.collect macro<sugar.html#collect.m%2Cuntyped%2Cuntyped>`_
+## * pass an `anonymous proc<manual.html#procedures-anonymous-procs>`_
+## * import the `sugar module<sugar.html>`_  and use
+##   the `=> macro<sugar.html#%3D>.m,untyped,untyped>`_
+## * use `...It templates<#18>`_
+##   (`mapIt<#mapIt.t,typed,untyped>`_,
+##   `filterIt<#filterIt.t,untyped,untyped>`_, etc.)
+##
+## Chaining of functions is possible thanks to the
+## `method call syntax<manual.html#procedures-method-call-syntax>`_.
+
+runnableExamples:
+  import std/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)
+    baz = collect:
+      for i in 1..10:
+        let j = 2 * i
+        if j mod 6 != 0:
+          j
+
+  doAssert foo == bar
+  doAssert foo == baz
+  doAssert foo == @[2, 4, 8, 10, 14, 16, 20]
+
+  doAssert foo.any(x => x > 17)
+  doAssert not bar.allIt(it < 20)
+  doAssert foo.foldl(a + b) == 74 # sum of all members
+
+
+runnableExamples:
+  from std/strutils import join
+
+  let
+    vowels = @"aeiou"
+    foo = "sequtils is an awesome module"
+
+  doAssert (vowels is seq[char]) and (vowels == @['a', 'e', 'i', 'o', 'u'])
+  doAssert 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
+
+
+import std/private/since
+
+import std/macros
+from std/typetraits import supportsCopyMem
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
+when defined(nimHasEffectsOf):
+  {.experimental: "strictEffects".}
+else:
+  {.pragma: effectsOf.}
+
+macro evalOnceAs(expAlias, exp: untyped,
+                 letAssigneable: static[bool]): untyped =
+  ## Injects `expAlias` in caller scope, to avoid bugs involving multiple
+  ## 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 (e.g. to handle openArray) where
+  ## it just forwards `exp` unchanged.
+  expectKind(expAlias, nnkIdent)
+  var val = exp
+
+  result = newStmtList()
+  # If `exp` is not a symbol we evaluate it once here and then use the temporary
+  # symbol as alias
+  if exp.kind != nnkSym and letAssigneable:
+    val = genSym()
+    result.add(newLetStmt(val, exp))
+
+  result.add(
+    newProc(name = genSym(nskTemplate, $expAlias), params = [getType(untyped)],
+      body = val, procType = nnkTemplateDef))
+
+func 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 func<#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)
@@ -46,32 +141,48 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
       result[i] = itm
       inc(i)
 
-proc count*[T](s: openArray[T], x: T): int =
+func addUnique*[T](s: var seq[T], x: sink T) =
+  ## Adds `x` to the container `s` if it is not already present. 
+  ## Uses `==` to check if the item is already present.
+  runnableExamples:
+    var a = @[1, 2, 3]
+    a.addUnique(4)
+    a.addUnique(4)
+    assert a == @[1, 2, 3, 4]
+
+  for i in 0..high(s):
+    if s[i] == x: return
+  when declared(ensureMove):
+    s.add ensureMove(x)
+  else:
+    s.add x
+
+func 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
 
-proc cycle*[T](s: openArray[T], n: Natural): seq[T] =
+func 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:
@@ -81,93 +192,175 @@ 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
 
-proc deduplicate*[T](s: openArray[T]): seq[T] =
+func 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 = @[]
-  for itm in items(s):
-    if not result.contains(itm): result.add(itm)
-
-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.
-  ##
-  ## Example:
-  ##
-  ## .. 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"
-  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.
-  ##
-  ## 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
+  if s.len > 0:
+    if isSorted:
+      var prev = s[0]
+      result.add(prev)
+      for i in 1..s.high:
+        if s[i] != prev:
+          prev = s[i]
+          result.add(prev)
+    else:
+      for itm in items(s):
+        if not result.contains(itm): result.add(itm)
+
+func minIndex*[T](s: openArray[T]): int {.since: (1, 1).} =
+  ## Returns the index of the minimum value of `s`.
+  ## `T` needs to have a `<` operator.
+  runnableExamples:
+    let
+      a = @[1, 2, 3, 4]
+      b = @[6, 5, 4, 3]
+      c = [2, -7, 8, -5]
+      d = "ziggy"
+    assert minIndex(a) == 0
+    assert minIndex(b) == 3
+    assert minIndex(c) == 1
+    assert minIndex(d) == 2
+
+  for i in 1..high(s):
+    if s[i] < s[result]: result = i
+
+func maxIndex*[T](s: openArray[T]): int {.since: (1, 1).} =
+  ## Returns the index of the maximum value of `s`.
+  ## `T` needs to have a `<` operator.
+  runnableExamples:
+    let
+      a = @[1, 2, 3, 4]
+      b = @[6, 5, 4, 3]
+      c = [2, -7, 8, -5]
+      d = "ziggy"
+    assert maxIndex(a) == 3
+    assert maxIndex(b) == 0
+    assert maxIndex(c) == 2
+    assert maxIndex(d) == 0
+
+  for i in 1..high(s):
+    if s[i] > s[result]: result = i
+
+func minmax*[T](x: openArray[T]): (T, T) =
+  ## The minimum and maximum values of `x`. `T` needs to have a `<` operator.
+  var l = x[0]
+  var h = x[0]
+  for i in 1..high(x):
+    if x[i] < l: l = x[i]
+    if h < x[i]: h = x[i]
+  result = (l, h)
+
+
+template zipImpl(s1, s2, retType: untyped): untyped =
+  proc zip*[S, T](s1: openArray[S], s2: openArray[T]): retType =
+    ## Returns a new sequence with a combination of the two input containers.
+    ##
+    ## The input containers can be of different types.
+    ## If one container is shorter, the remaining items in the longer container
+    ## are discarded.
+    ##
+    ## **Note**: For Nim 1.0.x and older version, `zip` returned a seq of
+    ## named tuples with fields `a` and `b`. For Nim versions 1.1.x and newer,
+    ## `zip` returns a seq of unnamed tuples.
+    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)
+      assert zip1 == @[(1, 6), (2, 5), (3, 4)]
+      assert zip2 == @[(1, "one"), (2, "two"), (3, "three")]
+      assert zip1[2][0] == 3
+      assert zip2[1][1] == "two"
+      when (NimMajor, NimMinor) <= (1, 0):
+        let
+          zip3 = zip(long, letters)
+        assert zip3 == @[(a: 6, b: 'a'), (5, 'b'), (4, 'c'), (3, 'd')]
+        assert zip3[0].b == 'a'
+      else:
+        let
+          zip3: seq[tuple[num: int, letter: char]] = zip(long, letters)
+        assert zip3 == @[(6, 'a'), (5, 'b'), (4, 'c'), (3, 'd')]
+        assert zip3[0].letter == 'a'
+
+    var m = min(s1.len, s2.len)
+    newSeq(result, m)
+    for i in 0 ..< m:
+      result[i] = (s1[i], s2[i])
+
+when (NimMajor, NimMinor) <= (1, 0):
+  zipImpl(s1, s2, seq[tuple[a: S, b: T]])
+else:
+  zipImpl(s1, s2, seq[(S, T)])
+
+proc unzip*[S, T](s: openArray[(S, T)]): (seq[S], seq[T]) {.since: (1, 1).} =
+  ## Returns a tuple of two sequences split out from a sequence of 2-field tuples.
+  runnableExamples:
+    let
+      zipped = @[(1, 'a'), (2, 'b'), (3, 'c')]
+      unzipped1 = @[1, 2, 3]
+      unzipped2 = @['a', 'b', 'c']
+    assert zipped.unzip() == (unzipped1, unzipped2)
+    assert zip(unzipped1, unzipped2).unzip() == (unzipped1, unzipped2)
+  result = (newSeq[S](s.len), newSeq[T](s.len))
+  for i in 0..<s.len:
+    result[0][i] = s[i][0]
+    result[1][i] = s[i][1]
+
+func distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
+  ## 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,varargs[seq[T]]>`_ func.
+  ## 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``
+  ## func 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
+  ## On the other hand, if `spread` is true, the func will distribute evenly
   ## the remainder of the division across all sequences, which makes the result
   ## 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]
-  assert(not s.isNil, "`s` can't be nil")
+  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.
   result = newSeq[seq[T]](num)
   var
@@ -179,13 +372,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:
@@ -193,184 +384,254 @@ 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])
       first = last
 
 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`.
+                                                            seq[S] {.inline, effectsOf: op.} =
+  ## Returns a new sequence with the results of the `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:
+  ## Instead of using `map` and `filter`, consider using the `collect` macro
+  ## from the `sugar` module.
   ##
-  ## .. code-block:: nim
-  ##   let
-  ##     a = @[1, 2, 3, 4]
-  ##     b = map(a, proc(x: int): string = $x)
-  ##   assert b == @["1", "2", "3", "4"]
+  ## **See also:**
+  ## * `sugar.collect macro<sugar.html#collect.m%2Cuntyped%2Cuntyped>`_
+  ## * `mapIt template<#mapIt.t,typed,untyped>`_
+  ## * `apply proc<#apply,openArray[T],proc(T)_2>`_ for the in-place version
+  ##
+  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.
-  ## The parameter function takes a ``var T`` type parameter.
+                                                              {.inline, effectsOf: op.} =
+  ## Applies `op` to every item in `s`, modifying it directly.
   ##
-  ## Example:
+  ## Note that the container `s` must be declared as a `var`,
+  ## since `s` is modified in-place.
+  ## The parameter function takes a `var T` type parameter.
   ##
-  ## .. 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.} =
+                                                              {.inline, effectsOf: op.} =
   ## 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.
-  ## The parameter function takes and returns a ``T`` type variable.
-  ##
-  ## Example:
+  ## Note that the 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.
   ##
-  ## .. 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.
+proc apply*[T](s: openArray[T], op: proc (x: T) {.closure.}) {.inline, since: (1, 3), effectsOf: op.} =
+  ## Same as `apply` but for a proc that does not return anything
+  ## and does not mutate `s` directly.
+  runnableExamples:
+    var message: string
+    apply([0, 1, 2, 3, 4], proc(item: int) = message.addInt item)
+    assert message == "01234"
+  for i in 0 ..< s.len: op(s[i])
+
+iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T {.effectsOf: pred.} =
+  ## Iterates through a container `s` and yields every item that fulfills the
+  ## predicate `pred` (a function that returns a `bool`).
+  ##
+  ## Instead of using `map` and `filter`, consider using the `collect` macro
+  ## from the `sugar` module.
   ##
-  ## Example:
+  ## **See also:**
+  ## * `sugar.collect macro<sugar.html#collect.m%2Cuntyped%2Cuntyped>`_
+  ## * `filter 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.
-  ##
-  ## Example:
-  ##
-  ## .. 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"]
+                                                                  {.inline, effectsOf: pred.} =
+  ## Returns a new sequence with all the items of `s` that fulfill the
+  ## predicate `pred` (a function that returns a `bool`).
+  ##
+  ## Instead of using `map` and `filter`, consider using the `collect` macro
+  ## from the `sugar` module.
+  ##
+  ## **See also:**
+  ## * `sugar.collect macro<sugar.html#collect.m%2Cuntyped%2Cuntyped>`_
+  ## * `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
+  ##
+  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]):
       result.add(s[i])
 
 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.
+                                                                {.inline, effectsOf: pred.} =
+  ## Keeps the items in the passed sequence `s` if they fulfill the
+  ## predicate `pred` (a function that returns a `bool`).
+  ##
+  ## Note that `s` must be declared as a `var`.
+  ##
+  ## Similar to the `filter proc<#filter,openArray[T],proc(T)>`_,
+  ## but modifies the sequence directly.
   ##
-  ## Example:
+  ## **See also:**
+  ## * `keepItIf template<#keepItIf.t,seq,untyped>`_
+  ## * `filter proc<#filter,openArray[T],proc(T)>`_
   ##
-  ## .. 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]
+  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]):
       if pos != i:
-        shallowCopy(s[pos], s[i])
+        when defined(gcDestructors):
+          s[pos] = move(s[i])
+        else:
+          shallowCopy(s[pos], s[i])
       inc(pos)
   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:
-  ##
-  ##.. 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
-
+func delete*[T](s: var seq[T]; slice: Slice[int]) =
+  ## Deletes the items `s[slice]`, raising `IndexDefect` if the slice contains
+  ## elements out of range.
+  ##
+  ## This operation moves all elements after `s[slice]` in linear time.
+  runnableExamples:
+    var a = @[10, 11, 12, 13, 14]
+    doAssertRaises(IndexDefect): a.delete(4..5)
+    assert a == @[10, 11, 12, 13, 14]
+    a.delete(4..4)
+    assert a == @[10, 11, 12, 13]
+    a.delete(1..2)
+    assert a == @[10, 13]
+    a.delete(1..<1) # empty slice
+    assert a == @[10, 13]
+  when compileOption("boundChecks"):
+    if not (slice.a < s.len and slice.a >= 0 and slice.b < s.len):
+      raise newException(IndexDefect, $(slice: slice, len: s.len))
+  if slice.b >= slice.a:
+    template defaultImpl =
+      var i = slice.a
+      var j = slice.b + 1
+      var newLen = s.len - j + i
+      while i < newLen:
+        when defined(gcDestructors):
+          s[i] = move(s[j])
+        else:
+          s[i].shallowCopy(s[j])
+        inc(i)
+        inc(j)
+      setLen(s, newLen)
+    when nimvm: defaultImpl()
+    else:
+      when defined(js):
+        let n = slice.b - slice.a + 1
+        let first = slice.a
+        {.emit: "`s`.splice(`first`, `n`);".}
+      else:
+        defaultImpl()
+
+func delete*[T](s: var seq[T]; first, last: Natural) {.deprecated: "use `delete(s, first..last)`".} =
+  ## Deletes the items of a sequence `s` at positions `first..last`
+  ## (including both ends of the range).
+  ## This modifies `s` itself, it does not return a copy.
+  runnableExamples("--warning:deprecated:off"):
+    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
+  doAssert first <= last
+  if first >= s.len:
+    return
   var i = first
-  var j = last+1
-  var newLen = len(s)-j+i
+  var j = min(len(s), last + 1)
+  var newLen = len(s) - j + i
   while i < newLen:
-    s[i].shallowCopy(s[j])
+    when defined(gcDestructors):
+      s[i] = move(s[j])
+    else:
+      s[i].shallowCopy(s[j])
     inc(i)
     inc(j)
   setLen(s, newLen)
 
-proc insert*[T](dest: var seq[T], src: openArray[T], pos=0) =
+func 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:
+  ## Note that the elements of `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
+  var i = j + len(src)
+  if i == j: return
   dest.setLen(i + 1)
 
   # Move items after `pos` to the end of the sequence.
   while j >= pos:
-    dest[i].shallowCopy(dest[j])
+    when defined(gcDestructors):
+      dest[i] = move(dest[j])
+    else:
+      dest[i].shallowCopy(dest[j])
     dec(i)
     dec(j)
   # Insert items from `dest` into `dest` at `pos`
@@ -381,70 +642,118 @@ 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 fulfill 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:
+  ## Instead of using `mapIt` and `filterIt`, consider using the `collect` macro
+  ## from the `sugar` module.
   ##
-  ## .. 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]
-  var result = newSeq[type(s[0])]()
+  ## **See also:**
+  ## * `sugar.collect macro<sugar.html#collect.m%2Cuntyped%2Cuntyped>`_
+  ## * `filter proc<#filter,openArray[T],proc(T)>`_
+  ## * `filter iterator<#filter.i,openArray[T],proc(T)>`_
+  ##
+  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[typeof(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 fulfill the predicate.
   ##
-  ## Unlike the `proc` version, the predicate needs to be an expression using
-  ## the ``it`` variable for testing, like: ``keepItIf("abcxyz", it == 'x')``.
+  ## 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]
     if pred:
       if pos != i:
-        shallowCopy(varSeq[pos], varSeq[i])
+        when defined(gcDestructors):
+          varSeq[pos] = move(varSeq[i])
+        else:
+          shallowCopy(varSeq[pos], varSeq[i])
       inc(pos)
   setLen(varSeq, pos)
 
-proc all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool =
+since (1, 1):
+  template countIt*(s, pred: untyped): int =
+    ## Returns a count of all the items that fulfill the predicate.
+    ##
+    ## The predicate needs to be an expression using
+    ## the `it` variable for testing, like: `countIt(@[1, 2, 3], it > 2)`.
+    ##
+    runnableExamples:
+      let numbers = @[-3, -2, -1, 0, 1, 2, 3, 4, 5, 6]
+      iterator iota(n: int): int =
+        for i in 0..<n: yield i
+      assert numbers.countIt(it < 0) == 3
+      assert countIt(iota(10), it < 2) == 2
+
+    var result = 0
+    for it {.inject.} in s:
+      if pred: result += 1
+    result
+
+proc all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool {.effectsOf: pred.} =
   ## 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 = x < 10) == true
+    assert all(numbers, proc (x: int): bool = x < 9) == false
+
   for i in s:
     if not pred(i):
       return false
-  return true
+  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.
+  ##
+  ## 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')`.
   ##
-  ## Example:
+  ## **See also:**
+  ## * `all proc<#all,openArray[T],proc(T)>`_
+  ## * `anyIt template<#anyIt.t,untyped,untyped>`_
   ##
-  ## .. code-block::
-  ##   let numbers = @[1, 4, 5, 8, 9, 7, 4]
-  ##   assert allIt(numbers, it < 10) == true
-  ##   assert allIt(numbers, it < 9) == false
+  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:
@@ -452,30 +761,41 @@ template allIt*(s, pred: untyped): bool =
       break
   result
 
-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.
+proc any*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool {.effectsOf: pred.} =
+  ## Iterates through a container and checks if at least one 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 = x > 8) == true
+    assert any(numbers, proc (x: int): bool = x > 9) == false
+
   for i in s:
     if pred(i):
       return true
-  return false
+  false
 
 template anyIt*(s, pred: untyped): bool =
-  ## Checks if some item fulfills the predicate.
+  ## Iterates through a container and checks if at least one 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:
@@ -483,63 +803,126 @@ template anyIt*(s, pred: untyped): bool =
       break
   result
 
-template toSeq*(iter: untyped): untyped =
-  ## Transforms any iterator into a sequence.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   let
-  ##     numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
-  ##     odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
-  ##       if x mod 2 == 1:
-  ##         result = true)
-  ##   assert odd_numbers == @[1, 3, 5, 7, 9]
+template toSeq1(s: not iterator): untyped =
+  # overload for typed but not iterator
+  type OutType = typeof(items(s))
+  when compiles(s.len):
+    block:
+      evalOnceAs(s2, s, compiles((let _ = s)))
+      var i = 0
+      var result = newSeq[OutType](s2.len)
+      for it in s2:
+        result[i] = it
+        i += 1
+      result
+  else:
+    var result: seq[OutType]# = @[]
+    for it in s:
+      result.add(it)
+    result
 
-  when compiles(iter.len):
+template toSeq2(iter: iterator): untyped =
+  # overload for iterator
+  evalOnceAs(iter2, iter(), false)
+  when compiles(iter2.len):
     var i = 0
-    var result = newSeq[type(iter)](iter.len)
-    for x in iter:
+    var result = newSeq[typeof(iter2)](iter2.len)
+    for x in iter2:
       result[i] = x
       inc i
     result
   else:
-    var result: seq[type(iter)] = @[]
-    for x in iter:
-      result.add(x)
+    type OutType = typeof(iter2())
+    var result: seq[OutType]# = @[]
+    when compiles(iter2()):
+      evalOnceAs(iter4, iter, false)
+      let iter3 = iter4()
+      for x in iter3():
+        result.add(x)
+    else:
+      for x in iter2():
+        result.add(x)
     result
 
+template toSeq*(iter: untyped): untyped =
+  ## Transforms any iterable (anything that can be iterated over, e.g. with
+  ## a for-loop) into a sequence.
+  ##
+  runnableExamples:
+    let
+      myRange = 1..5
+      mySet: set[int8] = {5'i8, 3, 1}
+    assert typeof(myRange) is HSlice[system.int, system.int]
+    assert typeof(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, e.g.: `toSeq(myInlineIterator(3))`
+    when compiles(iter.len):
+      block:
+        evalOnceAs(iter2, iter, true)
+        var result = newSeq[typeof(iter)](iter2.len)
+        var i = 0
+        for x in iter2:
+          result[i] = x
+          inc i
+        result
+    else:
+      var result: seq[typeof(iter)] = @[]
+      for x in iter:
+        result.add(x)
+      result
+
 template foldl*(sequence, operation: untyped): untyped =
   ## Template to fold a sequence from left to right, returning the accumulation.
   ##
   ## The sequence is required to have at least a single element. Debug versions
   ## of your program will assert in this situation but release versions will
   ## happily go ahead. If the sequence has a single element it will be returned
-  ## without applying ``operation``.
+  ## without applying `operation`.
   ##
-  ## The ``operation`` parameter should be an expression which uses the
-  ## variables ``a`` and ``b`` for each step of the fold. Since this is a left
+  ## The `operation` parameter should be an expression which uses the
+  ## variables `a` and `b` for each step of the fold. Since this is a left
   ## fold, for non associative binary operations like subtraction think that
   ## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) -
   ## 3).
   ##
-  ## Example:
-  ##
-  ## .. 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"
+  ## **See also:**
+  ## * `foldl template<#foldl.t,,,>`_ with a starting parameter
+  ## * `foldr template<#foldr.t,untyped,untyped>`_
+  ##
+  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)
+      procs = @["proc", "Is", "Also", "Fine"]
+
+
+    func foo(acc, cur: string): string =
+      result = acc & cur
+
+    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"
+    assert foldl(procs, foo(a, b)) == "procIsAlsoFine"
+
   let s = sequence
   assert s.len > 0, "Can't fold empty sequences"
-  var result: type(s[0])
+  var result: typeof(s[0])
   result = s[0]
   for i in 1..<s.len:
     let
@@ -551,22 +934,23 @@ 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.
+  ## 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 therefore 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"
-  var result: type(first)
-  result = first
+  runnableExamples:
+    let
+      numbers = @[0, 8, 1, 5]
+      digits = foldl(numbers, a & (chr(b + ord('0'))), "")
+    assert digits == "0815"
+
+  var result: typeof(first) = first
   for x in items(sequence):
     let
       a {.inject.} = result
@@ -580,134 +964,159 @@ template foldr*(sequence, operation: untyped): untyped =
   ## The sequence is required to have at least a single element. Debug versions
   ## of your program will assert in this situation but release versions will
   ## happily go ahead. If the sequence has a single element it will be returned
-  ## without applying ``operation``.
+  ## without applying `operation`.
   ##
-  ## The ``operation`` parameter should be an expression which uses the
-  ## variables ``a`` and ``b`` for each step of the fold. Since this is a right
+  ## The `operation` parameter should be an expression which uses the
+  ## variables `a` and `b` for each step of the fold. Since this is a right
   ## fold, for non associative binary operations like subtraction think that
   ## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 -
   ## (3))).
   ##
-  ## Example:
-  ##
-  ## .. 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"
-  let s = sequence
-  assert s.len > 0, "Can't fold empty sequences"
-  var result: type(s[0])
-  result = sequence[s.len - 1]
-  for i in countdown(s.len - 2, 0):
+  ## **See also:**
+  ## * `foldl template<#foldl.t,untyped,untyped>`_
+  ## * `foldl template<#foldl.t,,,>`_ with a starting parameter
+  ##
+  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 # xxx inefficient, use {.evalonce.} pending #13750
+  let n = s.len
+  assert n > 0, "Can't fold empty sequences"
+  var result = s[n - 1]
+  for i in countdown(n - 2, 0):
     let
       a {.inject.} = s[i]
       b {.inject.} = result
     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.
+template mapIt*(s: typed, op: untyped): untyped =
+  ## Returns a new sequence with the results of the `op` proc applied to every
+  ## item in the container `s`.
   ##
-  ## 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, op: untyped): untyped =
-  ## Convenience template around the ``map`` proc to reduce typing.
+  ## 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
+  ## The template injects the `it` variable which you can use directly in an
   ## expression.
   ##
-  ## Example:
+  ## Instead of using `mapIt` and `filterIt`, consider using the `collect` macro
+  ## from the `sugar` module.
+  ##
+  ## **See also:**
+  ## * `sugar.collect macro<sugar.html#collect.m%2Cuntyped%2Cuntyped>`_
+  ## * `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"]
-  type outType = type((
+  runnableExamples:
+    let
+      nums = @[1, 2, 3, 4]
+      strings = nums.mapIt($(4 * it))
+    assert strings == @["4", "8", "12", "16"]
+
+  type OutType = typeof((
     block:
-      var it{.inject.}: type(items(s));
-      op))
-  var result: seq[outType]
-  when compiles(s.len):
-    let t = s
-    var i = 0
-    result = newSeq[outType](t.len)
-    for it {.inject.} in t:
-      result[i] = op
-      i += 1
+      var it{.inject.}: typeof(items(s), typeOfIter);
+      op), typeOfProc)
+  when OutType is not (proc):
+    # Here, we avoid to create closures in loops.
+    # This avoids https://github.com/nim-lang/Nim/issues/12625
+    when compiles(s.len):
+      block: # using a block avoids https://github.com/nim-lang/Nim/issues/8580
+
+        # BUG: `evalOnceAs(s2, s, false)` would lead to C compile errors
+        # (`error: use of undeclared identifier`) instead of Nim compile errors
+        evalOnceAs(s2, s, compiles((let _ = s)))
+
+        var i = 0
+        var result = newSeq[OutType](s2.len)
+        for it {.inject.} in s2:
+          result[i] = op
+          i += 1
+        result
+    else:
+      var result: seq[OutType]# = @[]
+      # use `items` to avoid https://github.com/nim-lang/Nim/issues/12639
+      for it {.inject.} in items(s):
+        result.add(op)
+      result
   else:
-    result = @[]
-    for it {.inject.} in s:
-      result.add(op)
-  result
+    # `op` is going to create closures in loops, let's fallback to `map`.
+    # NOTE: Without this fallback, developers have to define a helper function and
+    # call `map`:
+    #   [1, 2].map((it) => ((x: int) => it + x))
+    # With this fallback, above code can be simplified to:
+    #   [1, 2].mapIt((x: int) => it + x)
+    # In this case, `mapIt` is just syntax sugar for `map`.
+    type InType = typeof(items(s), typeOfIter)
+    # Use a help proc `f` to create closures for each element in `s`
+    let f = proc (x: InType): OutType =
+              let it {.inject.} = x
+              op
+    map(s, f)
 
 template applyIt*(varSeq, op: untyped) =
-  ## Convenience template around the mutable ``apply`` proc to reduce typing.
+  ## Convenience template around the mutable `apply` proc to reduce typing.
   ##
-  ## The template injects the ``it`` variable which you can use directly in an
-  ## expression. The expression has to return the same type as the sequence you
-  ## are mutating.
+  ## The template injects the `it` variable which you can use directly in an
+  ## expression. The expression has to return the same type as the elements
+  ## of 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
-  for i in 0 ..< varSeq.len:
+  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.
-  ##
-  ## Example:
-  ##
-  ## .. 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
-  var result = newSeq[type(init)](len)
-  for i in 0 ..< len:
+  ## Creates a new `seq` of length `len`, calling `init` to initialize
+  ## each value of the seq.
+  ##
+  ## Useful for creating "2D" seqs - seqs containing other seqs
+  ## or to populate fields of the created seq.
+  runnableExamples:
+    ## Creates a seq containing 5 bool seqs, 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 seq with random numbers
+    import std/random
+    var seqRand = newSeqWith(20, rand(1.0))
+    assert seqRand[0] != seqRand[1]
+  type T = typeof(init)
+  let newLen = len
+  when supportsCopyMem(T) and declared(newSeqUninit):
+    var result = newSeqUninit[T](newLen)
+  else: # TODO: use `newSeqUnsafe` when that's available
+    var result = newSeq[T](newLen)
+  for i in 0 ..< newLen:
     result[i] = init
-  result
+  move(result) # refs bug #7295
 
-proc mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool;
+func mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool;
                  filter = nnkLiterals): NimNode =
   if constructor.kind in filter:
-    result = newNimNode(nnkCall, lineInfoFrom=constructor)
+    result = newNimNode(nnkCall, lineInfoFrom = constructor)
     result.add op
     result.add constructor
   else:
@@ -720,329 +1129,34 @@ 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``
-  ## or ``"abc"`` in the specified ``constructor`` AST. This can
+  ## 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:
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int)
-  ##   doAssert x is array[4, int]
-  ##
-  ## Short notation for:
-  ##
-  ## .. 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
+  runnableExamples:
+    let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int)
+    doAssert x is array[4, int]
+    doAssert x == [int(0.1), int(1.2), int(2.3), int(3.4)]
+  ## 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))
-  ##
-  ## There are no constraints for the ``constructor`` AST, it
+  runnableExamples:
+    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.
   result = mapLitsImpl(constructor, op, nested.boolVal)
 
-when isMainModule:
-  import strutils
-  block: # concat test
-    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]
-
-  block: # count test
-    let
-      s1 = @[1, 2, 3, 2]
-      s2 = @['a', 'b', 'x', 'a']
-      a1 = [1, 2, 3, 2]
-      a2 = ['a', 'b', 'x', 'a']
-      r0 = count(s1, 0)
-      r1 = count(s1, 1)
-      r2 = count(s1, 2)
-      r3 = count(s2, 'y')
-      r4 = count(s2, 'x')
-      r5 = count(s2, 'a')
-      ar0 = count(a1, 0)
-      ar1 = count(a1, 1)
-      ar2 = count(a1, 2)
-      ar3 = count(a2, 'y')
-      ar4 = count(a2, 'x')
-      ar5 = count(a2, 'a')
-    assert r0 == 0
-    assert r1 == 1
-    assert r2 == 2
-    assert r3 == 0
-    assert r4 == 1
-    assert r5 == 2
-    assert ar0 == 0
-    assert ar1 == 1
-    assert ar2 == 2
-    assert ar3 == 0
-    assert ar4 == 1
-    assert ar5 == 2
-
-  block: # cycle tests
-    let
-      a = @[1, 2, 3]
-      b: seq[int] = @[]
-      c = [1, 2, 3]
-
-    doAssert a.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
-    doAssert a.cycle(0) == @[]
-    #doAssert a.cycle(-1) == @[] # will not compile!
-    doAssert b.cycle(3) == @[]
-    doAssert c.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
-    doAssert c.cycle(0) == @[]
-
-  block: # repeat tests
-    assert repeat(10, 5) == @[10, 10, 10, 10, 10]
-    assert repeat(@[1,2,3], 2) == @[@[1,2,3], @[1,2,3]]
-    assert repeat([1,2,3], 2) == @[[1,2,3], [1,2,3]]
-
-  block: # deduplicates test
-    let
-      dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
-      dup2 = @["a", "a", "c", "d", "d"]
-      dup3 = [1, 1, 3, 4, 2, 2, 8, 1, 4]
-      dup4 = ["a", "a", "c", "d", "d"]
-      unique1 = deduplicate(dup1)
-      unique2 = deduplicate(dup2)
-      unique3 = deduplicate(dup3)
-      unique4 = deduplicate(dup4)
-    assert unique1 == @[1, 3, 4, 2, 8]
-    assert unique2 == @["a", "c", "d"]
-    assert unique3 == @[1, 3, 4, 2, 8]
-    assert unique4 == @["a", "c", "d"]
-
-  block: # zip test
-    let
-      short = @[1, 2, 3]
-      long = @[6, 5, 4, 3, 2, 1]
-      words = @["one", "two", "three"]
-      ashort = [1, 2, 3]
-      along = [6, 5, 4, 3, 2, 1]
-      awords = ["one", "two", "three"]
-      zip1 = zip(short, long)
-      zip2 = zip(short, words)
-      zip3 = zip(ashort, along)
-      zip4 = zip(ashort, awords)
-      zip5 = zip(ashort, words)
-    assert zip1 == @[(1, 6), (2, 5), (3, 4)]
-    assert zip2 == @[(1, "one"), (2, "two"), (3, "three")]
-    assert zip3 == @[(1, 6), (2, 5), (3, 4)]
-    assert zip4 == @[(1, "one"), (2, "two"), (3, "three")]
-    assert zip5 == @[(1, "one"), (2, "two"), (3, "three")]
-    assert zip1[2].b == 4
-    assert zip2[2].b == "three"
-    assert zip3[2].b == 4
-    assert zip4[2].b == "three"
-    assert zip5[2].b == "three"
-
-  block: # distribute tests
-    let numbers = @[1, 2, 3, 4, 5, 6, 7]
-    doAssert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
-    doAssert numbers.distribute(6)[0] == @[1, 2]
-    doAssert numbers.distribute(6)[5] == @[7]
-    let a = @[1, 2, 3, 4, 5, 6, 7]
-    doAssert a.distribute(1, true)   == @[@[1, 2, 3, 4, 5, 6, 7]]
-    doAssert a.distribute(1, false)  == @[@[1, 2, 3, 4, 5, 6, 7]]
-    doAssert a.distribute(2, true)   == @[@[1, 2, 3, 4], @[5, 6, 7]]
-    doAssert a.distribute(2, false)  == @[@[1, 2, 3, 4], @[5, 6, 7]]
-    doAssert a.distribute(3, true)   == @[@[1, 2, 3], @[4, 5], @[6, 7]]
-    doAssert a.distribute(3, false)  == @[@[1, 2, 3], @[4, 5, 6], @[7]]
-    doAssert a.distribute(4, true)   == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
-    doAssert a.distribute(4, false)  == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
-    doAssert a.distribute(5, true)   == @[@[1, 2], @[3, 4], @[5], @[6], @[7]]
-    doAssert a.distribute(5, false)  == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]]
-    doAssert a.distribute(6, true)   == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]]
-    doAssert a.distribute(6, false)  == @[
-      @[1, 2], @[3, 4], @[5, 6], @[7], @[], @[]]
-    doAssert a.distribute(8, false)  == a.distribute(8, true)
-    doAssert a.distribute(90, false) == a.distribute(90, true)
-    var b = @[0]
-    for f in 1 .. 25: b.add(f)
-    doAssert b.distribute(5, true)[4].len == 5
-    doAssert b.distribute(5, false)[4].len == 2
-
-  block: # map test
-    let
-      numbers = @[1, 4, 5, 8, 9, 7, 4]
-      anumbers = [1, 4, 5, 8, 9, 7, 4]
-      m1 = map(numbers, proc(x: int): int = 2*x)
-      m2 = map(anumbers, proc(x: int): int = 2*x)
-    assert m1 == @[2, 8, 10, 16, 18, 14, 8]
-    assert m2 == @[2, 8, 10, 16, 18, 14, 8]
-
-  block: # apply test
-    var a = @["1", "2", "3", "4"]
-    apply(a, proc(x: var string) = x &= "42")
-    assert a == @["142", "242", "342", "442"]
-
-  block: # filter proc test
-    let
-      colors = @["red", "yellow", "black"]
-      acolors = ["red", "yellow", "black"]
-      f1 = filter(colors, proc(x: string): bool = x.len < 6)
-      f2 = filter(colors) do (x: string) -> bool : x.len > 5
-      f3 = filter(acolors, proc(x: string): bool = x.len < 6)
-      f4 = filter(acolors) do (x: string) -> bool : x.len > 5
-    assert f1 == @["red", "black"]
-    assert f2 == @["yellow"]
-    assert f3 == @["red", "black"]
-    assert f4 == @["yellow"]
-
-  block: # filter iterator test
-    let numbers = @[1, 4, 5, 8, 9, 7, 4]
-    let anumbers = [1, 4, 5, 8, 9, 7, 4]
-    assert toSeq(filter(numbers, proc (x: int): bool = x mod 2 == 0)) ==
-      @[4, 8, 4]
-    assert toSeq(filter(anumbers, proc (x: int): bool = x mod 2 == 0)) ==
-      @[4, 8, 4]
-
-  block: # keepIf test
-    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]
-
-  block: # delete tests
-    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, """\
-    Deleting range 3-9 from [1,1,1,2,2,2,2,2,2,1,1,1,1,1]
-    is [1,1,1,1,1,1,1,1]"""
-
-  block: # insert tests
-    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, """\
-    Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1]
-    at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]"""
-
-  block: # filterIt test
-    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]
-
-  block: # keepItIf test
-    var candidates = @["foo", "bar", "baz", "foobar"]
-    keepItIf(candidates, it.len == 3 and it[0] == 'b')
-    assert candidates == @["bar", "baz"]
-
-  block: # all
-    let
-      numbers = @[1, 4, 5, 8, 9, 7, 4]
-      anumbers = [1, 4, 5, 8, 9, 7, 4]
-      len0seq : seq[int] = @[]
-    assert all(numbers, proc (x: int): bool = return x < 10) == true
-    assert all(numbers, proc (x: int): bool = return x < 9) == false
-    assert all(len0seq, proc (x: int): bool = return false) == true
-    assert all(anumbers, proc (x: int): bool = return x < 10) == true
-    assert all(anumbers, proc (x: int): bool = return x < 9) == false
-
-  block: # allIt
-    let
-      numbers = @[1, 4, 5, 8, 9, 7, 4]
-      anumbers = [1, 4, 5, 8, 9, 7, 4]
-      len0seq : seq[int] = @[]
-    assert allIt(numbers, it < 10) == true
-    assert allIt(numbers, it < 9) == false
-    assert allIt(len0seq, false) == true
-    assert allIt(anumbers, it < 10) == true
-    assert allIt(anumbers, it < 9) == false
-
-  block: # any
-    let
-      numbers = @[1, 4, 5, 8, 9, 7, 4]
-      anumbers = [1, 4, 5, 8, 9, 7, 4]
-      len0seq : seq[int] = @[]
-    assert any(numbers, proc (x: int): bool = return x > 8) == true
-    assert any(numbers, proc (x: int): bool = return x > 9) == false
-    assert any(len0seq, proc (x: int): bool = return true) == false
-    assert any(anumbers, proc (x: int): bool = return x > 8) == true
-    assert any(anumbers, proc (x: int): bool = return x > 9) == false
-
-  block: # anyIt
-    let
-      numbers = @[1, 4, 5, 8, 9, 7, 4]
-      anumbers = [1, 4, 5, 8, 9, 7, 4]
-      len0seq : seq[int] = @[]
-    assert anyIt(numbers, it > 8) == true
-    assert anyIt(numbers, it > 9) == false
-    assert anyIt(len0seq, true) == false
-    assert anyIt(anumbers, it > 8) == true
-    assert anyIt(anumbers, it > 9) == false
-
-  block: # toSeq test
-    let
-      numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
-      odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
-        if x mod 2 == 1:
-          result = true)
-    assert odd_numbers == @[1, 3, 5, 7, 9]
-
-  block: # foldl tests
-    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"
-
-  block: # foldr tests
-    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"
-
-  block: # mapIt tests
-    var
-      nums = @[1, 2, 3, 4]
-      strings = nums.mapIt($(4 * it))
-    nums.applyIt(it * 3)
-    assert nums[0] + nums[3] == 15
-    assert strings[2] == "12"
-
-  block: # newSeqWith tests
-    var seq2D = newSeqWith(4, newSeq[bool](2))
-    seq2D[0][0] = true
-    seq2D[1][0] = true
-    seq2D[0][1] = true
-    doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]]
-
-  block: # mapLiterals tests
-    let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int)
-    doAssert x is array[4, int]
-    doAssert mapLiterals((1, ("abc"), 2), float, nested=false) == (float(1), "abc", float(2))
-    doAssert mapLiterals(([1], ("abc"), 2), `$`, nested=true) == (["1"], "abc", "2")
-
-  when not defined(testing):
-    echo "Finished doc tests"
+iterator items*[T](xs: iterator: T): T =
+  ## Iterates over each element yielded by a closure iterator. This may
+  ## not seem particularly useful on its own, but this allows closure
+  ## iterators to be used by the mapIt, filterIt, allIt, anyIt, etc.
+  ## templates.
+  for x in xs():
+    yield x
diff --git a/lib/pure/collections/setimpl.nim b/lib/pure/collections/setimpl.nim
new file mode 100644
index 000000000..360a075d6
--- /dev/null
+++ b/lib/pure/collections/setimpl.nim
@@ -0,0 +1,156 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2019 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# An `include` file for the different hash set implementations.
+
+
+template maxHash(t): untyped = high(t.data)
+template dataLen(t): untyped = len(t.data)
+
+include hashcommon
+
+template initImpl(s: typed, size: int) =
+  let correctSize = slotsNeeded(size)
+  when s is OrderedSet:
+    s.first = -1
+    s.last = -1
+  s.counter = 0
+  newSeq(s.data, correctSize)
+
+template rawInsertImpl() {.dirty.} =
+  if data.len == 0:
+    initImpl(s, defaultInitialSize)
+  data[h].key = key
+  data[h].hcode = hc
+
+proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A,
+                  hc: Hash, h: Hash) =
+  rawInsertImpl()
+
+proc enlarge[A](s: var HashSet[A]) =
+  var n: KeyValuePairSeq[A]
+  newSeq(n, len(s.data) * growthFactor)
+  swap(s.data, n) # n is now old seq
+  for i in countup(0, high(n)):
+    if isFilled(n[i].hcode):
+      var j = -1 - rawGetKnownHC(s, n[i].key, n[i].hcode)
+      rawInsert(s, s.data, n[i].key, n[i].hcode, j)
+
+template inclImpl() {.dirty.} =
+  if s.data.len == 0:
+    initImpl(s, defaultInitialSize)
+  var hc: Hash
+  var index = rawGet(s, key, hc)
+  if index < 0:
+    if mustRehash(s):
+      enlarge(s)
+      index = rawGetKnownHC(s, key, hc)
+    rawInsert(s, s.data, key, hc, -1 - index)
+    inc(s.counter)
+
+template containsOrInclImpl() {.dirty.} =
+  if s.data.len == 0:
+    initImpl(s, defaultInitialSize)
+  var hc: Hash
+  var index = rawGet(s, key, hc)
+  if index >= 0:
+    result = true
+  else:
+    result = false
+    if mustRehash(s):
+      enlarge(s)
+      index = rawGetKnownHC(s, key, hc)
+    rawInsert(s, s.data, key, hc, -1 - index)
+    inc(s.counter)
+
+template doWhile(a, b) =
+  while true:
+    b
+    if not a: break
+
+proc exclImpl[A](s: var HashSet[A], key: A): bool {.inline.} =
+  var hc: Hash
+  var i = rawGet(s, key, hc)
+  var msk = high(s.data)
+  result = true
+
+  if i >= 0:
+    result = false
+    dec(s.counter)
+    while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
+      var j = i # The correctness of this depends on (h+1) in nextTry,
+      var r = j # though may be adaptable to other simple sequences.
+      s.data[i].hcode = 0 # mark current EMPTY
+      {.push warning[UnsafeDefault]:off.}
+      reset(s.data[i].key)
+      {.pop.}
+      doWhile((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
+        i = (i + 1) and msk # increment mod table size
+        if isEmpty(s.data[i].hcode): # end of collision cluster; So all done
+          return
+        r = s.data[i].hcode and msk # "home" location of key@i
+      s.data[j] = move(s.data[i]) # data[i] will be marked EMPTY next loop
+
+template dollarImpl() {.dirty.} =
+  result = "{"
+  for key in items(s):
+    if result.len > 1: result.add(", ")
+    result.addQuoted(key)
+  result.add("}")
+
+
+
+# --------------------------- OrderedSet ------------------------------
+
+proc rawGet[A](t: OrderedSet[A], key: A, hc: var Hash): int {.inline.} =
+  rawGetImpl()
+
+proc rawInsert[A](s: var OrderedSet[A], data: var OrderedKeyValuePairSeq[A],
+                  key: A, hc: Hash, h: Hash) =
+  rawInsertImpl()
+  data[h].next = -1
+  if s.first < 0: s.first = h
+  if s.last >= 0: data[s.last].next = h
+  s.last = h
+
+proc enlarge[A](s: var OrderedSet[A]) =
+  var n: OrderedKeyValuePairSeq[A]
+  newSeq(n, len(s.data) * growthFactor)
+  var h = s.first
+  s.first = -1
+  s.last = -1
+  swap(s.data, n)
+  while h >= 0:
+    var nxt = n[h].next
+    if isFilled(n[h].hcode):
+      var j = -1 - rawGetKnownHC(s, n[h].key, n[h].hcode)
+      rawInsert(s, s.data, n[h].key, n[h].hcode, j)
+    h = nxt
+
+proc exclImpl[A](s: var OrderedSet[A], key: A): bool {.inline.} =
+  if len(s.data) == 0:
+    return true
+  var n: OrderedKeyValuePairSeq[A]
+  newSeq(n, len(s.data))
+  var h = s.first
+  s.first = -1
+  s.last = -1
+  swap(s.data, n)
+  let hc = genHash(key)
+  result = true
+  while h >= 0:
+    var nxt = n[h].next
+    if isFilled(n[h].hcode):
+      if n[h].hcode == hc and n[h].key == key:
+        dec s.counter
+        result = false
+      else:
+        var j = -1 - rawGetKnownHC(s, n[h].key, n[h].hcode)
+        rawInsert(s, s.data, n[h].key, n[h].hcode, j)
+    h = nxt
diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim
index 59c90bc2b..af13135aa 100644
--- a/lib/pure/collections/sets.nim
+++ b/lib/pure/collections/sets.nim
@@ -7,23 +7,55 @@
 #    distribution, for details about the copyright.
 #
 
-## The ``sets`` module implements an efficient `hash set`:idx: and
+## The `sets` module implements an efficient `hash set`:idx: and
 ## ordered hash set.
 ##
 ## Hash sets are different from the `built in set type
 ## <manual.html#types-set-type>`_. Sets allow you to store any value that can be
 ## `hashed <hashes.html>`_ and they don't contain duplicate entries.
 ##
-## **Note**: The data types declared here have *value semantics*: This means
-## that ``=`` performs a copy of the set.
+## Common usages of sets:
+## * removing duplicates from a container by converting it with `toHashSet proc
+##   <#toHashSet,openArray[A]>`_ (see also `sequtils.deduplicate func
+##   <sequtils.html#deduplicate,openArray[T],bool>`_)
+## * membership testing
+## * mathematical operations on two sets, such as
+##   `union <#union,HashSet[A],HashSet[A]>`_,
+##   `intersection <#intersection,HashSet[A],HashSet[A]>`_,
+##   `difference <#difference,HashSet[A],HashSet[A]>`_, and
+##   `symmetric difference <#symmetricDifference,HashSet[A],HashSet[A]>`_
+##
+## **Examples:**
+##
+##   ```Nim
+##   echo toHashSet([9, 5, 1])     # {9, 1, 5}
+##   echo toOrderedSet([9, 5, 1])  # {9, 5, 1}
+##
+##   let
+##     s1 = toHashSet([9, 5, 1])
+##     s2 = toHashSet([3, 5, 7])
+##
+##   echo s1 + s2    # {9, 1, 3, 5, 7}
+##   echo s1 - s2    # {1, 9}
+##   echo s1 * s2    # {5}
+##   echo s1 -+- s2  # {9, 1, 3, 7}
+##   ```
+##
+## Note: The data types declared here have *value semantics*: This means
+## that `=` performs a copy of the set.
+##
+## **See also:**
+## * `intsets module <intsets.html>`_ for efficient int sets
+## * `tables module <tables.html>`_ for hash tables
+
 
 import
-  hashes, math
+  std/[hashes, math]
 
-{.pragma: myShallow.}
-when not defined(nimhygiene):
-  {.pragma: dirty.}
+when not defined(nimHasEffectsOf):
+  {.pragma: effectsOf.}
 
+{.pragma: myShallow.}
 # For "integer-like A" that are too big for intsets/bit-vectors to be practical,
 # it would be best to shrink hcode to the same size as the integer.  Larger
 # codes should never be needed, and this can pack more entries per cache-line.
@@ -31,155 +63,82 @@ when not defined(nimhygiene):
 type
   KeyValuePair[A] = tuple[hcode: Hash, key: A]
   KeyValuePairSeq[A] = seq[KeyValuePair[A]]
-  HashSet* {.myShallow.}[A] = object ## \
+  HashSet*[A] {.myShallow.} = object ## \
     ## A generic hash set.
     ##
-    ## Use `init() <#init,HashSet[A],int>`_ or `initSet[type]() <#initSet>`_
+    ## Use `init proc <#init,HashSet[A]>`_ or `initHashSet proc <#initHashSet>`_
     ## before calling other procs on it.
     data: KeyValuePairSeq[A]
     counter: int
 
-{.deprecated: [TSet: HashSet].}
-
-template default[T](t: typedesc[T]): T =
-  ## Used by clear methods to get a default value.
-  var v: T
-  v
-
-proc clear*[A](s: var HashSet[A]) =
-  ## Clears the HashSet back to an empty state, without shrinking
-  ## any of the existing storage. O(n) where n is the size of the hash bucket.
-  s.counter = 0
-  for i in 0..<s.data.len:
-    s.data[i].hcode = 0
-    s.data[i].key   = default(type(s.data[i].key))
-
-# 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.
-proc isEmpty(hcode: Hash): bool {.inline.} =
-  result = hcode == 0
-
-proc isFilled(hcode: Hash): bool {.inline.} =
-  result = hcode != 0
-
-proc isValid*[A](s: HashSet[A]): bool =
-  ## Returns `true` if the set has been initialized with `initSet <#initSet>`_.
-  ##
-  ## Most operations over an uninitialized set will crash at runtime and
-  ## `assert <system.html#assert>`_ in debug builds. You can use this proc in
-  ## your own procs to verify that sets passed to your procs are correctly
-  ## initialized. Example:
-  ##
-  ## .. code-block ::
-  ##   proc savePreferences(options: HashSet[string]) =
-  ##     assert options.isValid, "Pass an initialized set!"
-  ##     # Do stuff here, may crash in release builds!
-  result = not s.data.isNil
-
-proc len*[A](s: HashSet[A]): int =
-  ## Returns the number of keys in `s`.
-  ##
-  ## Due to an implementation detail you can call this proc on variables which
-  ## have not been initialized yet. The proc will return zero as the length
-  ## then. Example:
-  ##
-  ## .. code-block::
-  ##
-  ##   var values: HashSet[int]
-  ##   assert(not values.isValid)
-  ##   assert values.len == 0
-  result = s.counter
-
-proc card*[A](s: HashSet[A]): int =
-  ## Alias for `len() <#len,TSet[A]>`_.
-  ##
-  ## Card stands for the `cardinality
-  ## <http://en.wikipedia.org/wiki/Cardinality>`_ of a set.
-  result = s.counter
+type
+  OrderedKeyValuePair[A] = tuple[
+    hcode: Hash, next: int, key: A]
+  OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]]
+  OrderedSet*[A] {.myShallow.} = object ## \
+    ## A generic hash set that remembers insertion order.
+    ##
+    ## Use `init proc <#init,OrderedSet[A]>`_ or `initOrderedSet proc
+    ## <#initOrderedSet>`_ before calling other procs on it.
+    data: OrderedKeyValuePairSeq[A]
+    counter, first, last: int
+  SomeSet*[A] = HashSet[A] | OrderedSet[A]
+    ## Type union representing `HashSet` or `OrderedSet`.
 
-iterator items*[A](s: HashSet[A]): A =
-  ## Iterates over keys in the set `s`.
-  ##
-  ## If you need a sequence with the keys you can use `sequtils.toSeq()
-  ## <sequtils.html#toSeq>`_ on the iterator. Usage example:
-  ##
-  ## .. code-block::
-  ##   type
-  ##     pair = tuple[a, b: int]
-  ##   var
-  ##     a, b = initSet[pair]()
-  ##   a.incl((2, 3))
-  ##   a.incl((3, 2))
-  ##   a.incl((2, 3))
-  ##   for x, y in a.items:
-  ##     b.incl((x - 2, y + 1))
-  ##   assert a.len == 2
-  ##   echo b
-  ##   # --> {(a: 1, b: 3), (a: 0, b: 4)}
-  assert s.isValid, "The set needs to be initialized."
-  for h in 0..high(s.data):
-    if isFilled(s.data[h].hcode): yield s.data[h].key
+const
+  defaultInitialSize* = 64
 
-proc hash*[A](s: HashSet[A]): Hash =
-  ## hashing of HashSet
-  assert s.isValid, "The set needs to be initialized."
-  for h in 0..high(s.data):
-    result = result xor s.data[h].hcode
-  result = !$result
+include setimpl
 
-const
-  growthFactor = 2
+# ---------------------------------------------------------------------
+# ------------------------------ HashSet ------------------------------
+# ---------------------------------------------------------------------
 
-proc mustRehash(length, counter: int): bool {.inline.} =
-  assert(length > counter)
-  result = (length * 2 < counter * 3) or (length - counter < 4)
 
-proc rightSize*(count: Natural): int {.inline.} =
-  ## Return the value of `initialSize` to support `count` items.
+proc init*[A](s: var HashSet[A], initialSize = defaultInitialSize) =
+  ## Initializes a hash set.
   ##
-  ## If more items are expected to be added, simply add that
-  ## expected extra amount to the parameter before calling this.
+  ## Starting from Nim v0.20, sets are initialized by default and it is
+  ## not necessary to call this function explicitly.
   ##
-  ## Internally, we want mustRehash(rightSize(x), x) == false.
-  result = nextPowerOfTwo(count * 3 div 2  +  4)
-
-proc nextTry(h, maxHash: Hash): Hash {.inline.} =
-  result = (h + 1) and maxHash
-
-template rawGetKnownHCImpl() {.dirty.} =
-  var h: Hash = hc and high(s.data)  # start with real hash value
-  while isFilled(s.data[h].hcode):
-    # Compare hc THEN key with boolean short circuit. This makes the common case
-    # zero ==key's for missing (e.g.inserts) and exactly one ==key for present.
-    # It does slow down succeeding lookups by one extra Hash cmp&and..usually
-    # just a few clock cycles, generally worth it for any non-integer-like A.
-    if s.data[h].hcode == hc and s.data[h].key == key:  # compare hc THEN key
-      return h
-    h = nextTry(h, high(s.data))
-  result = -1 - h                   # < 0 => MISSING; insert idx = -1 - result
-
-template rawGetImpl() {.dirty.} =
-  hc = hash(key)
-  if hc == 0:       # This almost never taken branch should be very predictable.
-    hc = 314159265  # Value doesn't matter; Any non-zero favorite is fine.
-  rawGetKnownHCImpl()
-
-template rawInsertImpl() {.dirty.} =
-  data[h].key = key
-  data[h].hcode = hc
-
-proc rawGetKnownHC[A](s: HashSet[A], key: A, hc: Hash): int {.inline.} =
-  rawGetKnownHCImpl()
-
-proc rawGet[A](s: HashSet[A], key: A, hc: var Hash): int {.inline.} =
-  rawGetImpl()
+  ## You can call this proc on a previously initialized hash set, which will
+  ## discard all its values. This might be more convenient than iterating over
+  ## existing values and calling `excl() <#excl,HashSet[A],A>`_ on them.
+  ##
+  ## See also:
+  ## * `initHashSet proc <#initHashSet>`_
+  ## * `toHashSet proc <#toHashSet,openArray[A]>`_
+  runnableExamples:
+    var a: HashSet[int]
+    init(a)
+
+  initImpl(s, initialSize)
+
+proc initHashSet*[A](initialSize = defaultInitialSize): HashSet[A] =
+  ## Wrapper around `init proc <#init,HashSet[A]>`_ for initialization of
+  ## hash sets.
+  ##
+  ## Returns an empty hash set you can assign directly in `var` blocks in a
+  ## single line.
+  ##
+  ## Starting from Nim v0.20, sets are initialized by default and it is
+  ## not necessary to call this function explicitly.
+  ##
+  ## See also:
+  ## * `toHashSet proc <#toHashSet,openArray[A]>`_
+  runnableExamples:
+    var a = initHashSet[int]()
+    a.incl(3)
+    assert len(a) == 1
+  result = default(HashSet[A])
+  result.init(initialSize)
 
 proc `[]`*[A](s: var HashSet[A], key: A): var A =
-  ## 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.
-  assert s.isValid, "The set needs to be initialized."
+  ## 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.
   var hc: Hash
   var index = rawGet(s, key, hc)
   if index >= 0: result = s.data[index].key
@@ -189,301 +148,318 @@ 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`.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   var values = initSet[int]()
-  ##   assert(not values.contains(2))
-  ##   values.incl(2)
-  ##   assert values.contains(2)
-  ##   values.excl(2)
-  ##   assert(not values.contains(2))
-  assert s.isValid, "The set needs to be initialized."
+  ## Returns true if `key` is in `s`.
+  ##
+  ## This allows the usage of `in` operator.
+  ##
+  ## See also:
+  ## * `incl proc <#incl,HashSet[A],A>`_
+  ## * `containsOrIncl proc <#containsOrIncl,HashSet[A],A>`_
+  runnableExamples:
+    var values = initHashSet[int]()
+    assert(not values.contains(2))
+    assert 2 notin values
+
+    values.incl(2)
+    assert values.contains(2)
+    assert 2 in values
+
   var hc: Hash
   var index = rawGet(s, key, hc)
   result = index >= 0
 
-proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A,
-                  hc: Hash, h: Hash) =
-  rawInsertImpl()
+proc len*[A](s: HashSet[A]): int =
+  ## Returns the number of elements in `s`.
+  ##
+  ## Due to an implementation detail you can call this proc on variables which
+  ## have not been initialized yet. The proc will return zero as the length
+  ## then.
+  runnableExamples:
+    var a: HashSet[string]
+    assert len(a) == 0
+    let s = toHashSet([3, 5, 7])
+    assert len(s) == 3
 
-proc enlarge[A](s: var HashSet[A]) =
-  var n: KeyValuePairSeq[A]
-  newSeq(n, len(s.data) * growthFactor)
-  swap(s.data, n)                   # n is now old seq
-  for i in countup(0, high(n)):
-    if isFilled(n[i].hcode):
-      var j = -1 - rawGetKnownHC(s, n[i].key, n[i].hcode)
-      rawInsert(s, s.data, n[i].key, n[i].hcode, j)
+  result = s.counter
 
-template inclImpl() {.dirty.} =
-  var hc: Hash
-  var index = rawGet(s, key, hc)
-  if index < 0:
-    if mustRehash(len(s.data), s.counter):
-      enlarge(s)
-      index = rawGetKnownHC(s, key, hc)
-    rawInsert(s, s.data, key, hc, -1 - index)
-    inc(s.counter)
-
-template containsOrInclImpl() {.dirty.} =
-  var hc: Hash
-  var index = rawGet(s, key, hc)
-  if index >= 0:
-    result = true
-  else:
-    if mustRehash(len(s.data), s.counter):
-      enlarge(s)
-      index = rawGetKnownHC(s, key, hc)
-    rawInsert(s, s.data, key, hc, -1 - index)
-    inc(s.counter)
+proc card*[A](s: HashSet[A]): int =
+  ## Alias for `len() <#len,HashSet[A]>`_.
+  ##
+  ## Card stands for the `cardinality
+  ## <http://en.wikipedia.org/wiki/Cardinality>`_ of a set.
+  result = s.counter
 
 proc incl*[A](s: var HashSet[A], key: A) =
   ## Includes an element `key` in `s`.
   ##
-  ## This doesn't do anything if `key` is already in `s`. Example:
+  ## This doesn't do anything if `key` is already in `s`.
   ##
-  ## .. code-block::
-  ##   var values = initSet[int]()
-  ##   values.incl(2)
-  ##   values.incl(2)
-  ##   assert values.len == 1
-  assert s.isValid, "The set needs to be initialized."
+  ## See also:
+  ## * `excl proc <#excl,HashSet[A],A>`_ for excluding an element
+  ## * `incl proc <#incl,HashSet[A],HashSet[A]>`_ for including other set
+  ## * `containsOrIncl proc <#containsOrIncl,HashSet[A],A>`_
+  runnableExamples:
+    var values = initHashSet[int]()
+    values.incl(2)
+    values.incl(2)
+    assert values.len == 1
+
   inclImpl()
 
 proc incl*[A](s: var HashSet[A], other: HashSet[A]) =
-  ## Includes all elements from `other` into `s`.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   var values = initSet[int]()
-  ##   values.incl(2)
-  ##   var others = toSet([6, 7])
-  ##   values.incl(others)
-  ##   assert values.len == 3
-  assert s.isValid, "The set `s` needs to be initialized."
-  assert other.isValid, "The set `other` needs to be initialized."
-  for item in other: incl(s, item)
-
-template doWhile(a, b) =
-  while true:
-    b
-    if not a: break
+  ## Includes all elements from `other` set into `s` (must be declared as `var`).
+  ##
+  ## This is the in-place version of `s + other <#+,HashSet[A],HashSet[A]>`_.
+  ##
+  ## See also:
+  ## * `excl proc <#excl,HashSet[A],HashSet[A]>`_ for excluding other set
+  ## * `incl proc <#incl,HashSet[A],A>`_ for including an element
+  ## * `containsOrIncl proc <#containsOrIncl,HashSet[A],A>`_
+  runnableExamples:
+    var
+      values = toHashSet([1, 2, 3])
+      others = toHashSet([3, 4, 5])
+    values.incl(others)
+    assert values.len == 5
 
-template default[T](t: typedesc[T]): T =
-  var v: T
-  v
+  for item in other: incl(s, item)
 
-proc exclImpl[A](s: var HashSet[A], key: A) : bool {. inline .} =
-  assert s.isValid, "The set needs to be initialized."
-  var hc: Hash
-  var i = rawGet(s, key, hc)
-  var msk = high(s.data)
-  result = true
+proc toHashSet*[A](keys: openArray[A]): HashSet[A] =
+  ## Creates a new hash set that contains the members of the given
+  ## collection (seq, array, or string) `keys`.
+  ##
+  ## Duplicates are removed.
+  ##
+  ## See also:
+  ## * `initHashSet proc <#initHashSet>`_
+  runnableExamples:
+    let
+      a = toHashSet([5, 3, 2])
+      b = toHashSet("abracadabra")
+    assert len(a) == 3
+    ## a == {2, 3, 5}
+    assert len(b) == 5
+    ## b == {'a', 'b', 'c', 'd', 'r'}
 
-  if i >= 0:
-    result = false
-    dec(s.counter)
-    while true:         # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
-      var j = i         # The correctness of this depends on (h+1) in nextTry,
-      var r = j         # though may be adaptable to other simple sequences.
-      s.data[i].hcode = 0              # mark current EMPTY
-      s.data[i].key = default(type(s.data[i].key))
-      doWhile((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
-        i = (i + 1) and msk            # increment mod table size
-        if isEmpty(s.data[i].hcode):   # end of collision cluster; So all done
-          return
-        r = s.data[i].hcode and msk    # "home" location of key@i
-      shallowCopy(s.data[j], s.data[i]) # data[i] will be marked EMPTY next loop
+  result = initHashSet[A](keys.len)
+  for key in items(keys): result.incl(key)
 
-proc missingOrExcl*[A](s: var HashSet[A], key: A): bool =
-  ## Excludes `key` in the set `s` and tells if `key` was removed from `s`.
+iterator items*[A](s: HashSet[A]): A =
+  ## Iterates over elements of the set `s`.
   ##
-  ## The difference with regards to the `excl() <#excl,TSet[A],A>`_ proc is
-  ## that this proc returns `true` if `key` was not present in `s`. Example:
+  ## If you need a sequence with the elements you can use `sequtils.toSeq
+  ## template <sequtils.html#toSeq.t,untyped>`_.
   ##
-  ## .. code-block::
-  ##  var s = toSet([2, 3, 6, 7])
-  ##  assert s.missingOrExcl(4) == true
-  ##  assert s.missingOrExcl(6) == false
-  exclImpl(s, key)
+  ##   ```Nim
+  ##   type
+  ##     pair = tuple[a, b: int]
+  ##   var
+  ##     a, b = initHashSet[pair]()
+  ##   a.incl((2, 3))
+  ##   a.incl((3, 2))
+  ##   a.incl((2, 3))
+  ##   for x, y in a.items:
+  ##     b.incl((x - 2, y + 1))
+  ##   assert a.len == 2
+  ##   echo b
+  ##   # --> {(a: 1, b: 3), (a: 0, b: 4)}
+  ##   ```
+  let length = s.len
+  for h in 0 .. high(s.data):
+    if isFilled(s.data[h].hcode):
+      yield s.data[h].key
+      assert(len(s) == length, "the length of the HashSet changed while iterating over it")
+
+proc containsOrIncl*[A](s: var HashSet[A], key: A): bool =
+  ## Includes `key` in the set `s` and tells if `key` was already in `s`.
+  ##
+  ## The difference with regards to the `incl proc <#incl,HashSet[A],A>`_ is
+  ## that this proc returns `true` if `s` already contained `key`. The
+  ## proc will return `false` if `key` was added as a new value to `s` during
+  ## this call.
+  ##
+  ## See also:
+  ## * `incl proc <#incl,HashSet[A],A>`_ for including an element
+  ## * `incl proc <#incl,HashSet[A],HashSet[A]>`_ for including other set
+  ## * `missingOrExcl proc <#missingOrExcl,HashSet[A],A>`_
+  runnableExamples:
+    var values = initHashSet[int]()
+    assert values.containsOrIncl(2) == false
+    assert values.containsOrIncl(2) == true
+    assert values.containsOrIncl(3) == false
+
+  containsOrInclImpl()
 
 proc excl*[A](s: var HashSet[A], key: A) =
   ## Excludes `key` from the set `s`.
   ##
-  ## This doesn't do anything if `key` is not found in `s`. Example:
+  ## This doesn't do anything if `key` is not found in `s`.
   ##
-  ## .. code-block::
-  ##   var s = toSet([2, 3, 6, 7])
-  ##   s.excl(2)
-  ##   s.excl(2)
-  ##   assert s.len == 3
+  ## See also:
+  ## * `incl proc <#incl,HashSet[A],A>`_ for including an element
+  ## * `excl proc <#excl,HashSet[A],HashSet[A]>`_ for excluding other set
+  ## * `missingOrExcl proc <#missingOrExcl,HashSet[A],A>`_
+  runnableExamples:
+    var s = toHashSet([2, 3, 6, 7])
+    s.excl(2)
+    s.excl(2)
+    assert s.len == 3
+
   discard exclImpl(s, key)
 
 proc excl*[A](s: var HashSet[A], other: HashSet[A]) =
-  ## Excludes everything in `other` from `s`.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   var
-  ##     numbers = toSet([1, 2, 3, 4, 5])
-  ##     even = toSet([2, 4, 6, 8])
-  ##   numbers.excl(even)
-  ##   echo numbers
-  ##   # --> {1, 3, 5}
-  assert s.isValid, "The set `s` needs to be initialized."
-  assert other.isValid, "The set `other` needs to be initialized."
+  ## Excludes all elements of `other` set from `s`.
+  ##
+  ## This is the in-place version of `s - other <#-,HashSet[A],HashSet[A]>`_.
+  ##
+  ## See also:
+  ## * `incl proc <#incl,HashSet[A],HashSet[A]>`_ for including other set
+  ## * `excl proc <#excl,HashSet[A],A>`_ for excluding an element
+  ## * `missingOrExcl proc <#missingOrExcl,HashSet[A],A>`_
+  runnableExamples:
+    var
+      numbers = toHashSet([1, 2, 3, 4, 5])
+      even = toHashSet([2, 4, 6, 8])
+    numbers.excl(even)
+    assert len(numbers) == 3
+    ## numbers == {1, 3, 5}
+
   for item in other: discard exclImpl(s, item)
 
-proc containsOrIncl*[A](s: var HashSet[A], key: A): bool =
-  ## Includes `key` in the set `s` and tells if `key` was added to `s`.
-  ##
-  ## The difference with regards to the `incl() <#incl,TSet[A],A>`_ proc is
-  ## that this proc returns `true` if `key` was already present in `s`. The
-  ## proc will return false if `key` was added as a new value to `s` during
-  ## this call. Example:
-  ##
-  ## .. code-block::
-  ##   var values = initSet[int]()
-  ##   assert values.containsOrIncl(2) == false
-  ##   assert values.containsOrIncl(2) == true
-  assert s.isValid, "The set needs to be initialized."
-  containsOrInclImpl()
+proc missingOrExcl*[A](s: var HashSet[A], key: A): bool =
+  ## Excludes `key` in the set `s` and tells if `key` was already missing from `s`.
+  ##
+  ## The difference with regards to the `excl proc <#excl,HashSet[A],A>`_ is
+  ## that this proc returns `true` if `key` was missing from `s`.
+  ## The proc will return `false` if `key` was in `s` and it was removed
+  ## during this call.
+  ##
+  ## See also:
+  ## * `excl proc <#excl,HashSet[A],A>`_ for excluding an element
+  ## * `excl proc <#excl,HashSet[A],HashSet[A]>`_ for excluding other set
+  ## * `containsOrIncl proc <#containsOrIncl,HashSet[A],A>`_
+  runnableExamples:
+    var s = toHashSet([2, 3, 6, 7])
+    assert s.missingOrExcl(4) == true
+    assert s.missingOrExcl(6) == false
+    assert s.missingOrExcl(6) == true
 
-proc init*[A](s: var HashSet[A], initialSize=64) =
-  ## Initializes a hash set.
-  ##
-  ## The `initialSize` parameter needs to be a power of two. You can use
-  ## `math.nextPowerOfTwo() <math.html#nextPowerOfTwo>`_ or `rightSize` to
-  ## guarantee that at runtime. All set variables must be initialized before
-  ## use with other procs from this module with the exception of `isValid()
-  ## <#isValid,TSet[A]>`_ and `len() <#len,TSet[A]>`_.
-  ##
-  ## You can call this proc on a previously initialized hash set, which will
-  ## discard all its values. This might be more convenient than iterating over
-  ## existing values and calling `excl() <#excl,TSet[A],A>`_ on them. Example:
-  ##
-  ## .. code-block ::
-  ##   var a: HashSet[int]
-  ##   a.init(4)
-  ##   a.incl(2)
-  ##   a.init
-  ##   assert a.len == 0 and a.isValid
-  assert isPowerOfTwo(initialSize)
-  s.counter = 0
-  newSeq(s.data, initialSize)
+  exclImpl(s, key)
 
-proc initSet*[A](initialSize=64): HashSet[A] =
-  ## Wrapper around `init() <#init,TSet[A],int>`_ for initialization of hash
-  ## sets.
+proc pop*[A](s: var HashSet[A]): A =
+  ## Removes and returns an arbitrary element from the set `s`.
   ##
-  ## Returns an empty hash set you can assign directly in ``var`` blocks in a
-  ## single line. Example:
+  ## Raises `KeyError` if the set `s` is empty.
   ##
-  ## .. code-block ::
-  ##   var a = initSet[int](4)
-  ##   a.incl(2)
-  result.init(initialSize)
+  ## See also:
+  ## * `clear proc <#clear,HashSet[A]>`_
+  runnableExamples:
+    var s = toHashSet([2, 1])
+    assert [s.pop, s.pop] in [[1, 2], [2,1]] # order unspecified
+    doAssertRaises(KeyError, echo s.pop)
 
-proc toSet*[A](keys: openArray[A]): HashSet[A] =
-  ## Creates a new hash set that contains the given `keys`.
+  for h in 0 .. high(s.data):
+    if isFilled(s.data[h].hcode):
+      result = s.data[h].key
+      excl(s, result)
+      return result
+  raise newException(KeyError, "set is empty")
+
+proc clear*[A](s: var HashSet[A]) =
+  ## Clears the HashSet back to an empty state, without shrinking
+  ## any of the existing storage.
   ##
-  ## Example:
+  ## `O(n)` operation, where `n` is the size of the hash bucket.
   ##
-  ## .. code-block::
-  ##   var numbers = toSet([1, 2, 3, 4, 5])
-  ##   assert numbers.contains(2)
-  ##   assert numbers.contains(4)
-  result = initSet[A](rightSize(keys.len))
-  for key in items(keys): result.incl(key)
+  ## See also:
+  ## * `pop proc <#pop,HashSet[A]>`_
+  runnableExamples:
+    var s = toHashSet([3, 5, 7])
+    clear(s)
+    assert len(s) == 0
 
-template dollarImpl() {.dirty.} =
-  result = "{"
-  for key in items(s):
-    if result.len > 1: result.add(", ")
-    result.addQuoted(key)
-  result.add("}")
+  s.counter = 0
+  for i in 0 ..< s.data.len:
+    s.data[i].hcode = 0
+    {.push warning[UnsafeDefault]:off.}
+    reset(s.data[i].key)
+    {.pop.}
 
-proc `$`*[A](s: HashSet[A]): string =
-  ## Converts the set `s` to a string, mostly for logging purposes.
-  ##
-  ## Don't use this proc for serialization, the representation may change at
-  ## any moment and values are not escaped. Example:
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   echo toSet([2, 4, 5])
-  ##   # --> {2, 4, 5}
-  ##   echo toSet(["no", "esc'aping", "is \" provided"])
-  ##   # --> {no, esc'aping, is " provided}
-  assert s.isValid, "The set needs to be initialized."
-  dollarImpl()
 
 proc union*[A](s1, s2: HashSet[A]): HashSet[A] =
   ## Returns the union of the sets `s1` and `s2`.
   ##
-  ## The union of two sets is represented mathematically as *A ∪ B* and is the
-  ## set of all objects that are members of `s1`, `s2` or both. Example:
+  ## The same as `s1 + s2 <#+,HashSet[A],HashSet[A]>`_.
   ##
-  ## .. code-block::
-  ##   var
-  ##     a = toSet(["a", "b"])
-  ##     b = toSet(["b", "c"])
-  ##     c = union(a, b)
-  ##   assert c == toSet(["a", "b", "c"])
-  assert s1.isValid, "The set `s1` needs to be initialized."
-  assert s2.isValid, "The set `s2` needs to be initialized."
+  ## The union of two sets is represented mathematically as *A ∪ B* and is the
+  ## set of all objects that are members of `s1`, `s2` or both.
+  ##
+  ## See also:
+  ## * `intersection proc <#intersection,HashSet[A],HashSet[A]>`_
+  ## * `difference proc <#difference,HashSet[A],HashSet[A]>`_
+  ## * `symmetricDifference proc <#symmetricDifference,HashSet[A],HashSet[A]>`_
+  runnableExamples:
+    let
+      a = toHashSet(["a", "b"])
+      b = toHashSet(["b", "c"])
+      c = union(a, b)
+    assert c == toHashSet(["a", "b", "c"])
+
   result = s1
   incl(result, s2)
 
 proc intersection*[A](s1, s2: HashSet[A]): HashSet[A] =
   ## Returns the intersection of the sets `s1` and `s2`.
   ##
+  ## The same as `s1 * s2 <#*,HashSet[A],HashSet[A]>`_.
+  ##
   ## The intersection of two sets is represented mathematically as *A ∩ B* and
   ## is the set of all objects that are members of `s1` and `s2` at the same
-  ## time. Example:
-  ##
-  ## .. code-block::
-  ##   var
-  ##     a = toSet(["a", "b"])
-  ##     b = toSet(["b", "c"])
-  ##     c = intersection(a, b)
-  ##   assert c == toSet(["b"])
-  assert s1.isValid, "The set `s1` needs to be initialized."
-  assert s2.isValid, "The set `s2` needs to be initialized."
-  result = initSet[A](min(s1.data.len, s2.data.len))
-  for item in s1:
-    if item in s2: incl(result, item)
+  ## time.
+  ##
+  ## See also:
+  ## * `union proc <#union,HashSet[A],HashSet[A]>`_
+  ## * `difference proc <#difference,HashSet[A],HashSet[A]>`_
+  ## * `symmetricDifference proc <#symmetricDifference,HashSet[A],HashSet[A]>`_
+  runnableExamples:
+    let
+      a = toHashSet(["a", "b"])
+      b = toHashSet(["b", "c"])
+      c = intersection(a, b)
+    assert c == toHashSet(["b"])
+
+  result = initHashSet[A](max(min(s1.data.len, s2.data.len), 2))
+
+  # iterate over the elements of the smaller set
+  if s1.data.len < s2.data.len:
+    for item in s1:
+      if item in s2: incl(result, item)
+  else:
+    for item in s2:
+      if item in s1: incl(result, item)
+
 
 proc difference*[A](s1, s2: HashSet[A]): HashSet[A] =
   ## Returns the difference of the sets `s1` and `s2`.
   ##
-  ## The difference of two sets is represented mathematically as *A \ B* and is
+  ## The same as `s1 - s2 <#-,HashSet[A],HashSet[A]>`_.
+  ##
+  ## The difference of two sets is represented mathematically as *A ∖ B* and is
   ## the set of all objects that are members of `s1` and not members of `s2`.
-  ## Example:
   ##
-  ## .. code-block::
-  ##   var
-  ##     a = toSet(["a", "b"])
-  ##     b = toSet(["b", "c"])
-  ##     c = difference(a, b)
-  ##   assert c == toSet(["a"])
-  assert s1.isValid, "The set `s1` needs to be initialized."
-  assert s2.isValid, "The set `s2` needs to be initialized."
-  result = initSet[A]()
+  ## See also:
+  ## * `union proc <#union,HashSet[A],HashSet[A]>`_
+  ## * `intersection proc <#intersection,HashSet[A],HashSet[A]>`_
+  ## * `symmetricDifference proc <#symmetricDifference,HashSet[A],HashSet[A]>`_
+  runnableExamples:
+    let
+      a = toHashSet(["a", "b"])
+      b = toHashSet(["b", "c"])
+      c = difference(a, b)
+    assert c == toHashSet(["a"])
+
+  result = initHashSet[A]()
   for item in s1:
     if not contains(s2, item):
       incl(result, item)
@@ -491,51 +467,53 @@ proc difference*[A](s1, s2: HashSet[A]): HashSet[A] =
 proc symmetricDifference*[A](s1, s2: HashSet[A]): HashSet[A] =
   ## Returns the symmetric difference of the sets `s1` and `s2`.
   ##
+  ## The same as `s1 -+- s2 <#-+-,HashSet[A],HashSet[A]>`_.
+  ##
   ## The symmetric difference of two sets is represented mathematically as *A △
   ## B* or *A ⊖ B* and is the set of all objects that are members of `s1` or
-  ## `s2` but not both at the same time. Example:
-  ##
-  ## .. code-block::
-  ##   var
-  ##     a = toSet(["a", "b"])
-  ##     b = toSet(["b", "c"])
-  ##     c = symmetricDifference(a, b)
-  ##   assert c == toSet(["a", "c"])
-  assert s1.isValid, "The set `s1` needs to be initialized."
-  assert s2.isValid, "The set `s2` needs to be initialized."
+  ## `s2` but not both at the same time.
+  ##
+  ## See also:
+  ## * `union proc <#union,HashSet[A],HashSet[A]>`_
+  ## * `intersection proc <#intersection,HashSet[A],HashSet[A]>`_
+  ## * `difference proc <#difference,HashSet[A],HashSet[A]>`_
+  runnableExamples:
+    let
+      a = toHashSet(["a", "b"])
+      b = toHashSet(["b", "c"])
+      c = symmetricDifference(a, b)
+    assert c == toHashSet(["a", "c"])
+
   result = s1
   for item in s2:
     if containsOrIncl(result, item): excl(result, item)
 
 proc `+`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} =
-  ## Alias for `union(s1, s2) <#union>`_.
+  ## Alias for `union(s1, s2) <#union,HashSet[A],HashSet[A]>`_.
   result = union(s1, s2)
 
 proc `*`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} =
-  ## Alias for `intersection(s1, s2) <#intersection>`_.
+  ## Alias for `intersection(s1, s2) <#intersection,HashSet[A],HashSet[A]>`_.
   result = intersection(s1, s2)
 
 proc `-`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} =
-  ## Alias for `difference(s1, s2) <#difference>`_.
+  ## Alias for `difference(s1, s2) <#difference,HashSet[A],HashSet[A]>`_.
   result = difference(s1, s2)
 
 proc `-+-`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} =
-  ## Alias for `symmetricDifference(s1, s2) <#symmetricDifference>`_.
+  ## Alias for `symmetricDifference(s1, s2)
+  ## <#symmetricDifference,HashSet[A],HashSet[A]>`_.
   result = symmetricDifference(s1, s2)
 
 proc disjoint*[A](s1, s2: HashSet[A]): bool =
-  ## Returns true iff the sets `s1` and `s2` have no items in common.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   var
-  ##     a = toSet(["a", "b"])
-  ##     b = toSet(["b", "c"])
-  ##   assert disjoint(a, b) == false
-  ##   assert disjoint(a, b - a) == true
-  assert s1.isValid, "The set `s1` needs to be initialized."
-  assert s2.isValid, "The set `s2` needs to be initialized."
+  ## Returns `true` if the sets `s1` and `s2` have no items in common.
+  runnableExamples:
+    let
+      a = toHashSet(["a", "b"])
+      b = toHashSet(["b", "c"])
+    assert disjoint(a, b) == false
+    assert disjoint(a, b - a) == true
+
   for item in s1:
     if item in s2: return false
   return true
@@ -544,385 +522,335 @@ proc `<`*[A](s, t: HashSet[A]): bool =
   ## Returns true if `s` is a strict or proper subset of `t`.
   ##
   ## A strict or proper subset `s` has all of its members in `t` but `t` has
-  ## more elements than `s`. Example:
-  ##
-  ## .. code-block::
-  ##   var
-  ##     a = toSet(["a", "b"])
-  ##     b = toSet(["b", "c"])
-  ##     c = intersection(a, b)
-  ##   assert c < a and c < b
-  ##   assert((a < a) == false)
+  ## more elements than `s`.
+  runnableExamples:
+    let
+      a = toHashSet(["a", "b"])
+      b = toHashSet(["b", "c"])
+      c = intersection(a, b)
+    assert c < a and c < b
+    assert(not (a < a))
+
   s.counter != t.counter and s <= t
 
 proc `<=`*[A](s, t: HashSet[A]): bool =
-  ## Returns true if `s` is subset of `t`.
+  ## Returns true if `s` is a subset of `t`.
   ##
   ## A subset `s` has all of its members in `t` and `t` doesn't necessarily
-  ## have more members than `s`. That is, `s` can be equal to `t`. Example:
-  ##
-  ## .. code-block::
-  ##   var
-  ##     a = toSet(["a", "b"])
-  ##     b = toSet(["b", "c"])
-  ##     c = intersection(a, b)
-  ##   assert c <= a and c <= b
-  ##   assert((a <= a))
+  ## have more members than `s`. That is, `s` can be equal to `t`.
+  runnableExamples:
+    let
+      a = toHashSet(["a", "b"])
+      b = toHashSet(["b", "c"])
+      c = intersection(a, b)
+    assert c <= a and c <= b
+    assert a <= a
+
   result = false
   if s.counter > t.counter: return
   result = true
-  for item in s:
+  for item in items(s):
     if not(t.contains(item)):
       result = false
       return
 
 proc `==`*[A](s, t: HashSet[A]): bool =
   ## Returns true if both `s` and `t` have the same members and set size.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   var
-  ##     a = toSet([1, 2])
-  ##     b = toSet([1])
-  ##   b.incl(2)
-  ##   assert a == b
+  runnableExamples:
+    var
+      a = toHashSet([1, 2])
+      b = toHashSet([2, 1])
+    assert a == b
+
   s.counter == t.counter and s <= t
 
-proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] =
-  ## Returns a new set after applying `op` on each of the elements of `data`.
+proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] {.effectsOf: op.} =
+  ## Returns a new set after applying `op` proc on each of the elements of
+  ##`data` set.
   ##
-  ## You can use this proc to transform the elements from a set. Example:
-  ##
-  ## .. code-block::
-  ##   var a = toSet([1, 2, 3])
-  ##   var b = a.map(proc (x: int): string = $x)
-  ##   assert b == toSet(["1", "2", "3"])
-  result = initSet[B]()
-  for item in data: result.incl(op(item))
+  ## You can use this proc to transform the elements from a set.
+  runnableExamples:
+    let
+      a = toHashSet([1, 2, 3])
+      b = a.map(proc (x: int): string = $x)
+    assert b == toHashSet(["1", "2", "3"])
 
-# ------------------------------ ordered set ------------------------------
+  result = initHashSet[B]()
+  for item in items(data): result.incl(op(item))
 
-type
-  OrderedKeyValuePair[A] = tuple[
-    hcode: Hash, next: int, key: A]
-  OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]]
-  OrderedSet* {.myShallow.}[A] = object ## \
-    ## A generic hash set that remembers insertion order.
-    ##
-    ## Use `init() <#init,OrderedSet[A],int>`_ or `initOrderedSet[type]()
-    ## <#initOrderedSet>`_ before calling other procs on it.
-    data: OrderedKeyValuePairSeq[A]
-    counter, first, last: int
+proc hash*[A](s: HashSet[A]): Hash =
+  ## Hashing of HashSet.
+  for h in 0 .. high(s.data):
+    result = result xor s.data[h].hcode
+  result = !$result
 
-{.deprecated: [TOrderedSet: OrderedSet].}
+proc `$`*[A](s: HashSet[A]): string =
+  ## Converts the set `s` to a string, mostly for logging and printing purposes.
+  ##
+  ## Don't use this proc for serialization, the representation may change at
+  ## any moment and values are not escaped.
+  ##
+  ## **Examples:**
+  ##   ```Nim
+  ##   echo toHashSet([2, 4, 5])
+  ##   # --> {2, 4, 5}
+  ##   echo toHashSet(["no", "esc'aping", "is \" provided"])
+  ##   # --> {no, esc'aping, is " provided}
+  ##   ```
+  dollarImpl()
 
-proc clear*[A](s: var OrderedSet[A]) =
-  ## Clears the OrderedSet back to an empty state, without shrinking
-  ## any of the existing storage. O(n) where n is the size of the hash bucket.
-  s.counter = 0
-  s.first = -1
-  s.last = -1
-  for i in 0..<s.data.len:
-    s.data[i].hcode = 0
-    s.data[i].next = 0
-    s.data[i].key = default(type(s.data[i].key))
 
+proc initSet*[A](initialSize = defaultInitialSize): HashSet[A] {.deprecated:
+     "Deprecated since v0.20, use 'initHashSet'".} = initHashSet[A](initialSize)
 
-proc isValid*[A](s: OrderedSet[A]): bool =
-  ## Returns `true` if the ordered set has been initialized with `initSet
-  ## <#initOrderedSet>`_.
-  ##
-  ## Most operations over an uninitialized ordered set will crash at runtime
-  ## and `assert <system.html#assert>`_ in debug builds. You can use this proc
-  ## in your own procs to verify that ordered sets passed to your procs are
-  ## correctly initialized. Example:
-  ##
-  ## .. code-block::
-  ##   proc saveTarotCards(cards: OrderedSet[int]) =
-  ##     assert cards.isValid, "Pass an initialized set!"
-  ##     # Do stuff here, may crash in release builds!
-  result = not s.data.isNil
+proc toSet*[A](keys: openArray[A]): HashSet[A] {.deprecated:
+     "Deprecated since v0.20, use 'toHashSet'".} = toHashSet[A](keys)
 
-proc len*[A](s: OrderedSet[A]): int {.inline.} =
-  ## Returns the number of keys in `s`.
-  ##
-  ## Due to an implementation detail you can call this proc on variables which
-  ## have not been initialized yet. The proc will return zero as the length
-  ## then. Example:
-  ##
-  ## .. code-block::
+proc isValid*[A](s: HashSet[A]): bool {.deprecated:
+     "Deprecated since v0.20; sets are initialized by default".} =
+  ## Returns `true` if the set has been initialized (with `initHashSet proc
+  ## <#initHashSet>`_ or `init proc <#init,HashSet[A]>`_).
   ##
-  ##   var values: OrderedSet[int]
-  ##   assert(not values.isValid)
-  ##   assert values.len == 0
-  result = s.counter
+  runnableExamples:
+    proc savePreferences(options: HashSet[string]) =
+      assert options.isValid, "Pass an initialized set!"
+      # Do stuff here, may crash in release builds!
+  result = s.data.len > 0
 
-proc card*[A](s: OrderedSet[A]): int {.inline.} =
-  ## Alias for `len() <#len,TOrderedSet[A]>`_.
-  ##
-  ## Card stands for the `cardinality
-  ## <http://en.wikipedia.org/wiki/Cardinality>`_ of a set.
-  result = s.counter
 
-template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} =
-  var h = s.first
-  var idx = 0
-  while h >= 0:
-    var nxt = s.data[h].next
-    if isFilled(s.data[h].hcode):
-      yieldStmt
-      inc(idx)
-    h = nxt
 
-iterator items*[A](s: OrderedSet[A]): A =
-  ## Iterates over keys in the ordered set `s` in insertion order.
+# ---------------------------------------------------------------------
+# --------------------------- OrderedSet ------------------------------
+# ---------------------------------------------------------------------
+
+template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} =
+  if s.data.len > 0:
+    var h = s.first
+    var idx = 0
+    while h >= 0:
+      var nxt = s.data[h].next
+      if isFilled(s.data[h].hcode):
+        yieldStmt
+        inc(idx)
+      h = nxt
+
+
+proc init*[A](s: var OrderedSet[A], initialSize = defaultInitialSize) =
+  ## Initializes an ordered hash set.
   ##
-  ## If you need a sequence with the keys you can use `sequtils.toSeq()
-  ## <sequtils.html#toSeq>`_ on the iterator. Usage example:
+  ## Starting from Nim v0.20, sets are initialized by default and it is
+  ## not necessary to call this function explicitly.
   ##
-  ## .. code-block::
-  ##   var a = initOrderedSet[int]()
-  ##   for value in [9, 2, 1, 5, 1, 8, 4, 2]:
-  ##     a.incl(value)
-  ##   for value in a.items:
-  ##     echo "Got ", value
-  ##   # --> Got 9
-  ##   # --> Got 2
-  ##   # --> Got 1
-  ##   # --> Got 5
-  ##   # --> Got 8
-  ##   # --> Got 4
-  assert s.isValid, "The set needs to be initialized."
-  forAllOrderedPairs:
-    yield s.data[h].key
+  ## You can call this proc on a previously initialized hash set, which will
+  ## discard all its values. This might be more convenient than iterating over
+  ## existing values and calling `excl() <#excl,HashSet[A],A>`_ on them.
+  ##
+  ## See also:
+  ## * `initOrderedSet proc <#initOrderedSet>`_
+  ## * `toOrderedSet proc <#toOrderedSet,openArray[A]>`_
+  runnableExamples:
+    var a: OrderedSet[int]
+    init(a)
 
-proc hash*[A](s: OrderedSet[A]): Hash =
-  ## hashing of OrderedSet
-  assert s.isValid, "The set needs to be initialized."
-  forAllOrderedPairs:
-    result = result !& s.data[h].hcode
-  result = !$result
+  initImpl(s, initialSize)
 
-iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
-  assert s.isValid, "The set needs to be initialized"
-  forAllOrderedPairs:
-    yield (idx, s.data[h].key)
+proc initOrderedSet*[A](initialSize = defaultInitialSize): OrderedSet[A] =
+  ## Wrapper around `init proc <#init,OrderedSet[A]>`_ for initialization of
+  ## ordered hash sets.
+  ##
+  ## Returns an empty ordered hash set you can assign directly in `var` blocks
+  ## in a single line.
+  ##
+  ## Starting from Nim v0.20, sets are initialized by default and it is
+  ## not necessary to call this function explicitly.
+  ##
+  ## See also:
+  ## * `toOrderedSet proc <#toOrderedSet,openArray[A]>`_
+  runnableExamples:
+    var a = initOrderedSet[int]()
+    a.incl(3)
+    assert len(a) == 1
 
-proc rawGetKnownHC[A](s: OrderedSet[A], key: A, hc: Hash): int {.inline.} =
-  rawGetKnownHCImpl()
+  result.init(initialSize)
 
-proc rawGet[A](s: OrderedSet[A], key: A, hc: var Hash): int {.inline.} =
-  rawGetImpl()
+proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] =
+  ## Creates a new hash set that contains the members of the given
+  ## collection (seq, array, or string) `keys`.
+  ##
+  ## Duplicates are removed.
+  ##
+  ## See also:
+  ## * `initOrderedSet proc <#initOrderedSet>`_
+  runnableExamples:
+    let
+      a = toOrderedSet([5, 3, 2])
+      b = toOrderedSet("abracadabra")
+    assert len(a) == 3
+    ## a == {5, 3, 2} # different than in HashSet
+    assert len(b) == 5
+    ## b == {'a', 'b', 'r', 'c', 'd'} # different than in HashSet
+
+  result = initOrderedSet[A](keys.len)
+  for key in items(keys): result.incl(key)
 
 proc contains*[A](s: OrderedSet[A], key: A): bool =
-  ## Returns true iff `key` is in `s`.
+  ## Returns true if `key` is in `s`.
   ##
-  ## Example:
+  ## This allows the usage of `in` operator.
   ##
-  ## .. code-block::
-  ##   var values = initOrderedSet[int]()
-  ##   assert(not values.contains(2))
-  ##   values.incl(2)
-  ##   assert values.contains(2)
-  assert s.isValid, "The set needs to be initialized."
+  ## See also:
+  ## * `incl proc <#incl,OrderedSet[A],A>`_
+  ## * `containsOrIncl proc <#containsOrIncl,OrderedSet[A],A>`_
+  runnableExamples:
+    var values = initOrderedSet[int]()
+    assert(not values.contains(2))
+    assert 2 notin values
+
+    values.incl(2)
+    assert values.contains(2)
+    assert 2 in values
+
   var hc: Hash
   var index = rawGet(s, key, hc)
   result = index >= 0
 
-proc rawInsert[A](s: var OrderedSet[A], data: var OrderedKeyValuePairSeq[A],
-                  key: A, hc: Hash, h: Hash) =
-  rawInsertImpl()
-  data[h].next = -1
-  if s.first < 0: s.first = h
-  if s.last >= 0: data[s.last].next = h
-  s.last = h
-
-proc enlarge[A](s: var OrderedSet[A]) =
-  var n: OrderedKeyValuePairSeq[A]
-  newSeq(n, len(s.data) * growthFactor)
-  var h = s.first
-  s.first = -1
-  s.last = -1
-  swap(s.data, n)
-  while h >= 0:
-    var nxt = n[h].next
-    if isFilled(n[h].hcode):
-      var j = -1 - rawGetKnownHC(s, n[h].key, n[h].hcode)
-      rawInsert(s, s.data, n[h].key, n[h].hcode, j)
-    h = nxt
-
 proc incl*[A](s: var OrderedSet[A], key: A) =
   ## Includes an element `key` in `s`.
   ##
-  ## This doesn't do anything if `key` is already in `s`. Example:
+  ## This doesn't do anything if `key` is already in `s`.
   ##
-  ## .. code-block::
-  ##   var values = initOrderedSet[int]()
-  ##   values.incl(2)
-  ##   values.incl(2)
-  ##   assert values.len == 1
-  assert s.isValid, "The set needs to be initialized."
+  ## See also:
+  ## * `excl proc <#excl,OrderedSet[A],A>`_ for excluding an element
+  ## * `incl proc <#incl,HashSet[A],OrderedSet[A]>`_ for including other set
+  ## * `containsOrIncl proc <#containsOrIncl,OrderedSet[A],A>`_
+  runnableExamples:
+    var values = initOrderedSet[int]()
+    values.incl(2)
+    values.incl(2)
+    assert values.len == 1
+
   inclImpl()
 
 proc incl*[A](s: var HashSet[A], other: OrderedSet[A]) =
-  ## Includes all elements from `other` into `s`.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   var values = initOrderedSet[int]()
-  ##   values.incl(2)
-  ##   var others = toOrderedSet([6, 7])
-  ##   values.incl(others)
-  ##   assert values.len == 3
-  assert s.isValid, "The set `s` needs to be initialized."
-  assert other.isValid, "The set `other` needs to be initialized."
-  for item in other: incl(s, item)
-
-proc exclImpl[A](s: var OrderedSet[A], key: A) : bool {. inline .} =
-  assert s.isValid, "The set needs to be initialized."
-  var hc: Hash
-  var i = rawGet(s, key, hc)
-  var msk = high(s.data)
-  result = true
+  ## Includes all elements from the OrderedSet `other` into
+  ## HashSet `s` (must be declared as `var`).
+  ##
+  ## See also:
+  ## * `incl proc <#incl,OrderedSet[A],A>`_ for including an element
+  ## * `containsOrIncl proc <#containsOrIncl,OrderedSet[A],A>`_
+  runnableExamples:
+    var
+      values = toHashSet([1, 2, 3])
+      others = toOrderedSet([3, 4, 5])
+    values.incl(others)
+    assert values.len == 5
 
-  if i >= 0:
-    result = false
-    # Fix ordering
-    if s.first == i:
-      s.first = s.data[i].next
-    else:
-      var itr = s.first
-      while true:
-        if (s.data[itr].next == i):
-          s.data[itr].next = s.data[i].next
-          if s.last == i:
-            s.last = itr
-          break
-        itr = s.data[itr].next
-
-    dec(s.counter)
-    while true:         # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
-      var j = i         # The correctness of this depends on (h+1) in nextTry,
-      var r = j         # though may be adaptable to other simple sequences.
-      s.data[i].hcode = 0              # mark current EMPTY
-      s.data[i].key = default(type(s.data[i].key))
-      s.data[i].next = 0
-      doWhile((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
-        i = (i + 1) and msk            # increment mod table size
-        if isEmpty(s.data[i].hcode):   # end of collision cluster; So all done
-          return
-        r = s.data[i].hcode and msk    # "home" location of key@i
-      shallowCopy(s.data[j], s.data[i]) # data[i] will be marked EMPTY next loop
+  for item in items(other): incl(s, item)
 
-proc missingOrExcl*[A](s: var OrderedSet[A], key: A): bool =
-  ## Excludes `key` in the set `s` and tells if `key` was removed from `s`. Efficiency: O(n).
+proc containsOrIncl*[A](s: var OrderedSet[A], key: A): bool =
+  ## Includes `key` in the set `s` and tells if `key` was already in `s`.
   ##
-  ## The difference with regards to the `excl() <#excl,TOrderedSet[A],A>`_ proc is
-  ## that this proc returns `true` if `key` was not present in `s`. Example:
+  ## The difference with regards to the `incl proc <#incl,OrderedSet[A],A>`_ is
+  ## that this proc returns `true` if `s` already contained `key`. The
+  ## proc will return false if `key` was added as a new value to `s` during
+  ## this call.
   ##
-  ## .. code-block::
-  ##  var s = toOrderedSet([2, 3, 6, 7])
-  ##  assert s.missingOrExcl(4) == true
-  ##  assert s.missingOrExcl(6) == false
-  exclImpl(s, key)
+  ## See also:
+  ## * `incl proc <#incl,OrderedSet[A],A>`_ for including an element
+  ## * `missingOrExcl proc <#missingOrExcl,OrderedSet[A],A>`_
+  runnableExamples:
+    var values = initOrderedSet[int]()
+    assert values.containsOrIncl(2) == false
+    assert values.containsOrIncl(2) == true
+    assert values.containsOrIncl(3) == false
 
+  containsOrInclImpl()
 
 proc excl*[A](s: var OrderedSet[A], key: A) =
-  ## Excludes `key` from the set `s`. Efficiency: O(n).
+  ## Excludes `key` from the set `s`. Efficiency: `O(n)`.
   ##
-  ## This doesn't do anything if `key` is not found in `s`. Example:
+  ## This doesn't do anything if `key` is not found in `s`.
   ##
-  ## .. code-block::
-  ##   var s = toOrderedSet([2, 3, 6, 7])
-  ##   s.excl(2)
-  ##   s.excl(2)
-  ##   assert s.len == 3
+  ## See also:
+  ## * `incl proc <#incl,OrderedSet[A],A>`_ for including an element
+  ## * `missingOrExcl proc <#missingOrExcl,OrderedSet[A],A>`_
+  runnableExamples:
+    var s = toOrderedSet([2, 3, 6, 7])
+    s.excl(2)
+    s.excl(2)
+    assert s.len == 3
+
   discard exclImpl(s, key)
 
-proc containsOrIncl*[A](s: var OrderedSet[A], key: A): bool =
-  ## Includes `key` in the set `s` and tells if `key` was added to `s`.
-  ##
-  ## The difference with regards to the `incl() <#incl,TOrderedSet[A],A>`_ proc
-  ## is that this proc returns `true` if `key` was already present in `s`. The
-  ## proc will return false if `key` was added as a new value to `s` during
-  ## this call. Example:
-  ##
-  ## .. code-block::
-  ##   var values = initOrderedSet[int]()
-  ##   assert values.containsOrIncl(2) == false
-  ##   assert values.containsOrIncl(2) == true
-  assert s.isValid, "The set needs to be initialized."
-  containsOrInclImpl()
+proc missingOrExcl*[A](s: var OrderedSet[A], key: A): bool =
+  ## Excludes `key` in the set `s` and tells if `key` was already missing from `s`.
+  ## Efficiency: O(n).
+  ##
+  ## The difference with regards to the `excl proc <#excl,OrderedSet[A],A>`_ is
+  ## that this proc returns `true` if `key` was missing from `s`.
+  ## The proc will return `false` if `key` was in `s` and it was removed
+  ## during this call.
+  ##
+  ## See also:
+  ## * `excl proc <#excl,OrderedSet[A],A>`_
+  ## * `containsOrIncl proc <#containsOrIncl,OrderedSet[A],A>`_
+  runnableExamples:
+    var s = toOrderedSet([2, 3, 6, 7])
+    assert s.missingOrExcl(4) == true
+    assert s.missingOrExcl(6) == false
+    assert s.missingOrExcl(6) == true
 
-proc init*[A](s: var OrderedSet[A], initialSize=64) =
-  ## Initializes an ordered hash set.
+  exclImpl(s, key)
+
+proc clear*[A](s: var OrderedSet[A]) =
+  ## Clears the OrderedSet back to an empty state, without shrinking
+  ## any of the existing storage.
   ##
-  ## The `initialSize` parameter needs to be a power of two. You can use
-  ## `math.nextPowerOfTwo() <math.html#nextPowerOfTwo>`_ or `rightSize` to
-  ## guarantee that at runtime. All set variables must be initialized before
-  ## use with other procs from this module with the exception of `isValid()
-  ## <#isValid,TOrderedSet[A]>`_ and `len() <#len,TOrderedSet[A]>`_.
-  ##
-  ## You can call this proc on a previously initialized ordered hash set to
-  ## discard its values. At the moment this is the only proc to remove elements
-  ## from an ordered hash set. Example:
-  ##
-  ## .. code-block ::
-  ##   var a: OrderedSet[int]
-  ##   a.init(4)
-  ##   a.incl(2)
-  ##   a.init
-  ##   assert a.len == 0 and a.isValid
-  assert isPowerOfTwo(initialSize)
+  ## `O(n)` operation where `n` is the size of the hash bucket.
+  runnableExamples:
+    var s = toOrderedSet([3, 5, 7])
+    clear(s)
+    assert len(s) == 0
+
   s.counter = 0
   s.first = -1
   s.last = -1
-  newSeq(s.data, initialSize)
+  for i in 0 ..< s.data.len:
+    s.data[i].hcode = 0
+    s.data[i].next = 0
+    {.push warning[UnsafeDefault]:off.}
+    reset(s.data[i].key)
+    {.pop.}
 
-proc initOrderedSet*[A](initialSize=64): OrderedSet[A] =
-  ## Wrapper around `init() <#init,TOrderedSet[A],int>`_ for initialization of
-  ## ordered hash sets.
-  ##
-  ## Returns an empty ordered hash set you can assign directly in ``var``
-  ## blocks in a single line. Example:
+proc len*[A](s: OrderedSet[A]): int {.inline.} =
+  ## Returns the number of elements in `s`.
   ##
-  ## .. code-block ::
-  ##   var a = initOrderedSet[int](4)
-  ##   a.incl(2)
-  result.init(initialSize)
+  ## Due to an implementation detail you can call this proc on variables which
+  ## have not been initialized yet. The proc will return zero as the length
+  ## then.
+  runnableExamples:
+    var a: OrderedSet[string]
+    assert len(a) == 0
+    let s = toHashSet([3, 5, 7])
+    assert len(s) == 3
 
-proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] =
-  ## Creates a new ordered hash set that contains the given `keys`.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   var numbers = toOrderedSet([1, 2, 3, 4, 5])
-  ##   assert numbers.contains(2)
-  ##   assert numbers.contains(4)
-  result = initOrderedSet[A](rightSize(keys.len))
-  for key in items(keys): result.incl(key)
+  result = s.counter
 
-proc `$`*[A](s: OrderedSet[A]): string =
-  ## Converts the ordered hash set `s` to a string, mostly for logging purposes.
-  ##
-  ## Don't use this proc for serialization, the representation may change at
-  ## any moment and values are not escaped. Example:
-  ##
-  ## Example:
+proc card*[A](s: OrderedSet[A]): int {.inline.} =
+  ## Alias for `len() <#len,OrderedSet[A]>`_.
   ##
-  ## .. code-block::
-  ##   echo toOrderedSet([2, 4, 5])
-  ##   # --> {2, 4, 5}
-  ##   echo toOrderedSet(["no", "esc'aping", "is \" provided"])
-  ##   # --> {no, esc'aping, is " provided}
-  assert s.isValid, "The set needs to be initialized."
-  dollarImpl()
+  ## Card stands for the `cardinality
+  ## <http://en.wikipedia.org/wiki/Cardinality>`_ of a set.
+  result = s.counter
 
 proc `==`*[A](s, t: OrderedSet[A]): bool =
   ## Equality for ordered sets.
+  runnableExamples:
+    let
+      a = toOrderedSet([1, 2])
+      b = toOrderedSet([2, 1])
+    assert(not (a == b))
+
   if s.counter != t.counter: return false
   var h = s.first
   var g = t.first
@@ -939,218 +867,64 @@ proc `==`*[A](s, t: OrderedSet[A]): bool =
     g = nxg
   result = compared == s.counter
 
-when isMainModule and not defined(release):
-  proc testModule() =
-    ## Internal micro test to validate docstrings and such.
-    block isValidTest:
-      var options: HashSet[string]
-      proc savePreferences(options: HashSet[string]) =
-        assert options.isValid, "Pass an initialized set!"
-      options = initSet[string]()
-      options.savePreferences
-
-    block lenTest:
-      var values: HashSet[int]
-      assert(not values.isValid)
-      assert values.len == 0
-      assert values.card == 0
-
-    block setIterator:
-      type pair = tuple[a, b: int]
-      var a, b = initSet[pair]()
-      a.incl((2, 3))
-      a.incl((3, 2))
-      a.incl((2, 3))
-      for x, y in a.items:
-        b.incl((x - 2, y + 1))
-      assert a.len == b.card
-      assert a.len == 2
-      #echo b
-
-    block setContains:
-      var values = initSet[int]()
-      assert(not values.contains(2))
-      values.incl(2)
-      assert values.contains(2)
-      values.excl(2)
-      assert(not values.contains(2))
-
-      values.incl(4)
-      var others = toSet([6, 7])
-      values.incl(others)
-      assert values.len == 3
-
-      values.init
-      assert values.containsOrIncl(2) == false
-      assert values.containsOrIncl(2) == true
-      var
-        a = toSet([1, 2])
-        b = toSet([1])
-      b.incl(2)
-      assert a == b
-
-    block exclusions:
-      var s = toSet([2, 3, 6, 7])
-      s.excl(2)
-      s.excl(2)
-      assert s.len == 3
-
-      var
-        numbers = toSet([1, 2, 3, 4, 5])
-        even = toSet([2, 4, 6, 8])
-      numbers.excl(even)
-      #echo numbers
-      # --> {1, 3, 5}
-
-    block toSeqAndString:
-      var a = toSet([2, 4, 5])
-      var b = initSet[int]()
-      for x in [2, 4, 5]: b.incl(x)
-      assert($a == $b)
-      #echo a
-      #echo toSet(["no", "esc'aping", "is \" provided"])
-
-    #block orderedToSeqAndString:
-    #  echo toOrderedSet([2, 4, 5])
-    #  echo toOrderedSet(["no", "esc'aping", "is \" provided"])
-
-    block setOperations:
-      var
-        a = toSet(["a", "b"])
-        b = toSet(["b", "c"])
-        c = union(a, b)
-      assert c == toSet(["a", "b", "c"])
-      var d = intersection(a, b)
-      assert d == toSet(["b"])
-      var e = difference(a, b)
-      assert e == toSet(["a"])
-      var f = symmetricDifference(a, b)
-      assert f == toSet(["a", "c"])
-      assert d < a and d < b
-      assert((a < a) == false)
-      assert d <= a and d <= b
-      assert((a <= a))
-      # Alias test.
-      assert a + b == toSet(["a", "b", "c"])
-      assert a * b == toSet(["b"])
-      assert a - b == toSet(["a"])
-      assert a -+- b == toSet(["a", "c"])
-      assert disjoint(a, b) == false
-      assert disjoint(a, b - a) == true
-
-    block mapSet:
-      var a = toSet([1, 2, 3])
-      var b = a.map(proc (x: int): string = $x)
-      assert b == toSet(["1", "2", "3"])
-
-    block isValidTest:
-      var cards: OrderedSet[string]
-      proc saveTarotCards(cards: OrderedSet[string]) =
-        assert cards.isValid, "Pass an initialized set!"
-      cards = initOrderedSet[string]()
-      cards.saveTarotCards
-
-    block lenTest:
-      var values: OrderedSet[int]
-      assert(not values.isValid)
-      assert values.len == 0
-      assert values.card == 0
-
-    block setIterator:
-      type pair = tuple[a, b: int]
-      var a, b = initOrderedSet[pair]()
-      a.incl((2, 3))
-      a.incl((3, 2))
-      a.incl((2, 3))
-      for x, y in a.items:
-        b.incl((x - 2, y + 1))
-      assert a.len == b.card
-      assert a.len == 2
-
-    block setPairsIterator:
-      var s = toOrderedSet([1, 3, 5, 7])
-      var items = newSeq[tuple[a: int, b: int]]()
-      for idx, item in s: items.add((idx, item))
-      assert items == @[(0, 1), (1, 3), (2, 5), (3, 7)]
-
-    block exclusions:
-      var s = toOrderedSet([1, 2, 3, 6, 7, 4])
-
-      s.excl(3)
-      s.excl(3)
-      s.excl(1)
-      s.excl(4)
-
-      var items = newSeq[int]()
-      for item in s: items.add item
-      assert items == @[2, 6, 7]
-
-    #block orderedSetIterator:
-    #  var a = initOrderedSet[int]()
-    #  for value in [9, 2, 1, 5, 1, 8, 4, 2]:
-    #    a.incl(value)
-    #  for value in a.items:
-    #    echo "Got ", value
-
-    block setContains:
-      var values = initOrderedSet[int]()
-      assert(not values.contains(2))
-      values.incl(2)
-      assert values.contains(2)
-
-    block toSeqAndString:
-      var a = toOrderedSet([2, 4, 5])
-      var b = initOrderedSet[int]()
-      for x in [2, 4, 5]: b.incl(x)
-      assert($a == $b)
-      assert(a == b) # https://github.com/Araq/Nim/issues/1413
-
-    block initBlocks:
-      var a: OrderedSet[int]
-      a.init(4)
-      a.incl(2)
-      a.init
-      assert a.len == 0 and a.isValid
-      a = initOrderedSet[int](4)
-      a.incl(2)
-      assert a.len == 1
-
-      var b: HashSet[int]
-      b.init(4)
-      b.incl(2)
-      b.init
-      assert b.len == 0 and b.isValid
-      b = initSet[int](4)
-      b.incl(2)
-      assert b.len == 1
-
-    for i in 0 .. 32:
-      var s = rightSize(i)
-      if s <= i or mustRehash(s, i):
-        echo "performance issue: rightSize() will not elide enlarge() at ", i
-
-    block missingOrExcl:
-      var s = toOrderedSet([2, 3, 6, 7])
-      assert s.missingOrExcl(4) == true
-      assert s.missingOrExcl(6) == false
-
-    block orderedSetEquality:
-      type pair = tuple[a, b: int]
-
-      var aa = initOrderedSet[pair]()
-      var bb = initOrderedSet[pair]()
-
-      var x = (a:1,b:2)
-      var y = (a:3,b:4)
-
-      aa.incl(x)
-      aa.incl(y)
-
-      bb.incl(x)
-      bb.incl(y)
-      assert aa == bb
-
-    when not defined(testing):
-      echo "Micro tests run successfully."
-
-  testModule()
+proc hash*[A](s: OrderedSet[A]): Hash =
+  ## Hashing of OrderedSet.
+  forAllOrderedPairs:
+    result = result !& s.data[h].hcode
+  result = !$result
+
+proc `$`*[A](s: OrderedSet[A]): string =
+  ## Converts the ordered hash set `s` to a string, mostly for logging and
+  ## printing purposes.
+  ##
+  ## Don't use this proc for serialization, the representation may change at
+  ## any moment and values are not escaped.
+  ##
+  ## **Examples:**
+  ##   ```Nim
+  ##   echo toOrderedSet([2, 4, 5])
+  ##   # --> {2, 4, 5}
+  ##   echo toOrderedSet(["no", "esc'aping", "is \" provided"])
+  ##   # --> {no, esc'aping, is " provided}
+  ##   ```
+  dollarImpl()
+
+
+
+iterator items*[A](s: OrderedSet[A]): A =
+  ## Iterates over keys in the ordered set `s` in insertion order.
+  ##
+  ## If you need a sequence with the elements you can use `sequtils.toSeq
+  ## template <sequtils.html#toSeq.t,untyped>`_.
+  ##
+  ##   ```Nim
+  ##   var a = initOrderedSet[int]()
+  ##   for value in [9, 2, 1, 5, 1, 8, 4, 2]:
+  ##     a.incl(value)
+  ##   for value in a.items:
+  ##     echo "Got ", value
+  ##   # --> Got 9
+  ##   # --> Got 2
+  ##   # --> Got 1
+  ##   # --> Got 5
+  ##   # --> Got 8
+  ##   # --> Got 4
+  ##   ```
+  let length = s.len
+  forAllOrderedPairs:
+    yield s.data[h].key
+    assert(len(s) == length, "the length of the OrderedSet changed while iterating over it")
+
+iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
+  ## Iterates through (position, value) tuples of OrderedSet `s`.
+  runnableExamples:
+    let a = toOrderedSet("abracadabra")
+    var p = newSeq[(int, char)]()
+    for x in pairs(a):
+      p.add(x)
+    assert p == @[(0, 'a'), (1, 'b'), (2, 'r'), (3, 'c'), (4, 'd')]
+
+  let length = s.len
+  forAllOrderedPairs:
+    yield (idx, s.data[h].key)
+    assert(len(s) == length, "the length of the OrderedSet changed while iterating over it")
diff --git a/lib/pure/collections/sharedlist.nim b/lib/pure/collections/sharedlist.nim
index b3e677b79..ec8f1cd86 100644
--- a/lib/pure/collections/sharedlist.nim
+++ b/lib/pure/collections/sharedlist.nim
@@ -8,11 +8,15 @@
 #
 
 ## Shared list support.
+##
+## Unstable API.
 
-{.push stackTrace:off.}
+{.deprecated.}
+
+{.push stackTrace: off.}
 
 import
-  locks
+  std/locks
 
 const
   ElemsPerNode = 100
@@ -33,8 +37,10 @@ template withLock(t, x: untyped) =
   release(t.lock)
 
 proc iterAndMutate*[A](x: var SharedList[A]; action: proc(x: A): bool) =
-  ## iterates over the list. If 'action' returns true, the
+  ## Iterates over the list. If `action` returns true, the
   ## current item is removed from the list.
+  ##
+  ## .. warning:: It may not preserve the element order after some modifications.
   withLock(x):
     var n = x.head
     while n != nil:
@@ -45,8 +51,9 @@ proc iterAndMutate*[A](x: var SharedList[A]; action: proc(x: A): bool) =
         if action(n.d[i]):
           acquire(x.lock)
           let t = x.tail
+          dec t.dataLen # TODO considering t.dataLen == 0,
+                        # probably the module should be refactored using doubly linked lists
           n.d[i] = t.d[t.dataLen]
-          dec t.dataLen
         else:
           acquire(x.lock)
           inc i
@@ -63,11 +70,14 @@ iterator items*[A](x: var SharedList[A]): A =
 proc add*[A](x: var SharedList[A]; y: A) =
   withLock(x):
     var node: SharedListNode[A]
-    if x.tail == nil or x.tail.dataLen == ElemsPerNode:
-      node = cast[type node](allocShared0(sizeof(node[])))
-      node.next = x.tail
+    if x.tail == nil:
+      node = cast[typeof node](allocShared0(sizeof(node[])))
+      x.tail = node
+      x.head = node
+    elif x.tail.dataLen == ElemsPerNode:
+      node = cast[typeof node](allocShared0(sizeof(node[])))
+      x.tail.next = node
       x.tail = node
-      if x.head == nil: x.head = node
     else:
       node = x.tail
     node.d[node.dataLen] = y
@@ -92,11 +102,4 @@ proc deinitSharedList*[A](t: var SharedList[A]) =
   clear(t)
   deinitLock t.lock
 
-proc initSharedList*[A](): SharedList[A] {.deprecated.} =
-  ## Deprecated. Use `init` instead.
-  ## This is not posix compliant, may introduce undefined behavior.
-  initLock result.lock
-  result.head = nil
-  result.tail = nil
-
 {.pop.}
diff --git a/lib/pure/collections/sharedstrings.nim b/lib/pure/collections/sharedstrings.nim
deleted file mode 100644
index 7e9de4b73..000000000
--- a/lib/pure/collections/sharedstrings.nim
+++ /dev/null
@@ -1,152 +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.
-#
-
-## Shared string support for Nim.
-
-type
-  UncheckedCharArray = UncheckedArray[char]
-
-type
-  Buffer = ptr object
-    refcount: int
-    capacity, realLen: int
-    data: UncheckedCharArray
-
-  SharedString* = object ## A string that can be shared. Slicing is O(1).
-    buffer: Buffer
-    first, len: int
-
-proc decRef(b: Buffer) {.inline.} =
-  if atomicDec(b.refcount) <= 0:
-    deallocShared(b)
-
-proc incRef(b: Buffer) {.inline.} =
-  atomicInc(b.refcount)
-
-{.experimental.}
-
-proc `=destroy`*(s: SharedString) =
-  #echo "destroyed"
-  if not s.buffer.isNil:
-    decRef(s.buffer)
-
-when false:
-  proc `=`*(dest: var SharedString; src: SharedString) =
-    incRef(src.buffer)
-    if not dest.buffer.isNil:
-      decRef(dest.buffer)
-    dest.buffer = src.buffer
-    dest.first = src.first
-    dest.len = src.len
-
-proc len*(s: SharedString): int = s.len
-
-proc `[]`*(s: SharedString; i: Natural): char =
-  if i < s.len: result = s.buffer.data[i+s.first]
-  else: raise newException(IndexError, "index out of bounds")
-
-proc `[]=`*(s: var SharedString; i: Natural; value: char) =
-  if i < s.len: s.buffer.data[i+s.first] = value
-  else: raise newException(IndexError, "index out of bounds")
-
-proc `[]`*(s: SharedString; ab: HSlice[int, int]): SharedString =
-  #incRef(src.buffer)
-  if ab.a < s.len:
-    result.buffer = s.buffer
-    result.first = ab.a
-    result.len = min(s.len, ab.b - ab.a + 1)
-  # else: produce empty string ;-)
-
-proc newBuffer(cap, len: int): Buffer =
-  assert cap >= len
-  result = cast[Buffer](allocShared0(sizeof(int)*3 + cap))
-  result.refcount = 0
-  result.capacity = cap
-  result.realLen = len
-
-proc newSharedString*(len: Natural): SharedString =
-  if len != 0:
-    # optimization: Don't have an underlying buffer when 'len == 0'
-    result.buffer = newBuffer(len, len)
-  result.first = 0
-  result.len = len
-
-proc newSharedString*(s: string): SharedString =
-  let len = s.len
-  if len != 0:
-    # optimization: Don't have an underlying buffer when 'len == 0'
-    result.buffer = newBuffer(len, len)
-    copyMem(addr result.buffer.data[0], cstring(s), s.len)
-  result.first = 0
-  result.len = len
-
-when declared(atomicLoadN):
-  template load(x): untyped = atomicLoadN(addr x, ATOMIC_SEQ_CST)
-else:
-  # XXX Fixme
-  template load(x): untyped = x
-
-proc add*(s: var SharedString; t: cstring; len: Natural) =
-  if len == 0: return
-  let newLen = s.len + len
-  if s.buffer.isNil:
-    s.buffer = newBuffer(len, len)
-    copyMem(addr s.buffer.data[0], t, len)
-    s.len = len
-  elif newLen >= s.buffer.capacity or s.first != 0 or
-      s.len != s.buffer.realLen or load(s.buffer.refcount) > 1:
-    let oldBuf = s.buffer
-    s.buffer = newBuffer(max(s.buffer.capacity * 3 div 2, newLen), newLen)
-    copyMem(addr s.buffer.data[0], addr oldBuf.data[s.first], s.len)
-    copyMem(addr s.buffer.data[s.len], t, len)
-    decRef(oldBuf)
-  else:
-    copyMem(addr s.buffer.data[s.len], t, len)
-    s.buffer.realLen += len
-    s.len += len
-
-proc add*(s: var SharedString; t: string) =
-  s.add(t.cstring, t.len)
-
-proc rawData*(s: var SharedString): pointer =
-  if s.buffer.isNil: result = nil
-  else: result = addr s.buffer.data[s.first]
-
-proc add*(s: var SharedString; t: SharedString) =
-  if t.buffer.isNil: return
-  s.add(cast[cstring](addr s.buffer.data[s.first]), t.len)
-
-proc `$`*(s: SharedString): string =
-  result = newString(s.len)
-  if s.len > 0:
-    copyMem(addr result[0], addr s.buffer.data[s.first], s.len)
-
-proc `==`*(s: SharedString; t: string): bool =
-  if s.buffer.isNil: result = t.len == 0
-  else: result = t.len == s.len and equalMem(addr s.buffer.data[s.first],
-                                             cstring(t), t.len)
-
-proc `==`*(s, t: SharedString): bool =
-  if s.buffer.isNil: result = t.len == 0
-  else: result = t.len == s.len and equalMem(addr s.buffer.data[s.first],
-                                             addr t.buffer.data[t.first], t.len)
-
-iterator items*(s: SharedString): char =
-  let buf = s.buffer.data
-  let x = s.first
-  if buf != nil:
-    for i in 0..<s.len:
-      yield buf[i+x]
-
-import hashes
-
-proc hash*(s: SharedString): THash =
-  var h: THash = 0
-  for x in s: h = h !& x.hash
-  result = !$h
diff --git a/lib/pure/collections/sharedtables.nim b/lib/pure/collections/sharedtables.nim
index 0292a27a2..b474ecd31 100644
--- a/lib/pure/collections/sharedtables.nim
+++ b/lib/pure/collections/sharedtables.nim
@@ -11,16 +11,18 @@
 ## you'll be in trouble. Uses a single lock to protect the table, lockfree
 ## implementations welcome but if lock contention is so high that you need a
 ## lockfree hash table, you're doing it wrong.
+##
+## Unstable API.
 
-import
-  hashes, math, locks
+{.deprecated.}
 
-include "system/inclrtl"
+import
+  std/[hashes, math, locks]
 
 type
   KeyValuePair[A, B] = tuple[hcode: Hash, key: A, val: B]
-  KeyValuePairSeq[A, B] = ptr array[10_000_000, KeyValuePair[A, B]]
-  SharedTable* [A, B] = object ## generic hash SharedTable
+  KeyValuePairSeq[A, B] = ptr UncheckedArray[KeyValuePair[A, B]]
+  SharedTable*[A, B] = object ## generic hash SharedTable
     data: KeyValuePairSeq[A, B]
     counter, dataLen: int
     lock: Lock
@@ -29,6 +31,14 @@ template maxHash(t): untyped = t.dataLen-1
 
 include tableimpl
 
+template st_maybeRehashPutImpl(enlarge) {.dirty.} =
+  if mustRehash(t):
+    enlarge(t)
+    index = rawGetKnownHC(t, key, hc)
+  index = -1 - index # important to transform for mgetOrPutImpl
+  rawInsert(t, t.data, key, val, hc, index)
+  inc(t.counter)
+
 proc enlarge[A, B](t: var SharedTable[A, B]) =
   let oldSize = t.dataLen
   let size = oldSize * growthFactor
@@ -52,17 +62,27 @@ template withLock(t, x: untyped) =
 
 template withValue*[A, B](t: var SharedTable[A, B], key: A,
                           value, body: untyped) =
-  ## retrieves the value at ``t[key]``.
-  ## `value` can be modified in the scope of the ``withValue`` call.
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   sharedTable.withValue(key, value) do:
-  ##     # block is executed only if ``key`` in ``t``
-  ##     # value is threadsafe in block
-  ##     value.name = "username"
-  ##     value.uid = 1000
-  ##
+  ## Retrieves the value at `t[key]`.
+  ## `value` can be modified in the scope of the `withValue` call.
+  runnableExamples:
+    var table: SharedTable[string, string]
+    init(table)
+
+    table["a"] = "x"
+    table["b"] = "y"
+    table["c"] = "z"
+
+    table.withValue("a", value):
+      assert value[] == "x"
+
+    table.withValue("b", value):
+      value[] = "modified"
+
+    table.withValue("b", value):
+      assert value[] == "modified"
+
+    table.withValue("nonexistent", value):
+      assert false # not called
   acquire(t.lock)
   try:
     var hc: Hash
@@ -76,20 +96,33 @@ template withValue*[A, B](t: var SharedTable[A, B], key: A,
 
 template withValue*[A, B](t: var SharedTable[A, B], key: A,
                           value, body1, body2: untyped) =
-  ## retrieves the value at ``t[key]``.
-  ## `value` can be modified in the scope of the ``withValue`` call.
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   sharedTable.withValue(key, value) do:
-  ##     # block is executed only if ``key`` in ``t``
-  ##     # value is threadsafe in block
-  ##     value.name = "username"
-  ##     value.uid = 1000
-  ##   do:
-  ##     # block is executed when ``key`` not in ``t``
-  ##     raise newException(KeyError, "Key not found")
-  ##
+  ## Retrieves the value at `t[key]`.
+  ## `value` can be modified in the scope of the `withValue` call.
+  runnableExamples:
+    var table: SharedTable[string, string]
+    init(table)
+
+    table["a"] = "x"
+    table["b"] = "y"
+    table["c"] = "z"
+
+
+    table.withValue("a", value):
+      value[] = "m"
+
+    var flag = false
+    table.withValue("d", value):
+      discard value
+      doAssert false
+    do: # if "d" notin table
+      flag = true
+
+    if flag:
+      table["d"] = "n"
+
+    assert table.mget("a") == "m"
+    assert table.mget("d") == "n"
+
   acquire(t.lock)
   try:
     var hc: Hash
@@ -104,8 +137,8 @@ template withValue*[A, B](t: var SharedTable[A, B], key: A,
     release(t.lock)
 
 proc mget*[A, B](t: var SharedTable[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.
+  ## Retrieves the value at `t[key]`. The value can be modified.
+  ## If `key` is not in `t`, the `KeyError` exception is raised.
   withLock t:
     var hc: Hash
     var index = rawGet(t, key, hc)
@@ -118,44 +151,47 @@ proc mget*[A, B](t: var SharedTable[A, B], key: A): var B =
       raise newException(KeyError, "key not found")
 
 proc mgetOrPut*[A, B](t: var SharedTable[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. **Note**: This is inherently
   ## unsafe in the context of multi-threading since it returns a pointer
-  ## to ``B``.
+  ## to `B`.
   withLock t:
     mgetOrPutImpl(enlarge)
 
 proc hasKeyOrPut*[A, B](t: var SharedTable[A, B], key: A, val: B): bool =
-  ## returns true iff `key` is in the table, otherwise inserts `value`.
+  ## Returns true if `key` is in the table, otherwise inserts `value`.
   withLock t:
     hasKeyOrPutImpl(enlarge)
 
+template tabMakeEmpty(i) = t.data[i].hcode = 0
+template tabCellEmpty(i) = isEmpty(t.data[i].hcode)
+template tabCellHash(i)  = t.data[i].hcode
+
 proc withKey*[A, B](t: var SharedTable[A, B], key: A,
                     mapper: proc(key: A, val: var B, pairExists: var bool)) =
-  ## Computes a new mapping for the ``key`` with the specified ``mapper``
+  ## Computes a new mapping for the `key` with the specified `mapper`
   ## procedure.
   ##
-  ## The ``mapper`` takes 3 arguments:
+  ## The `mapper` takes 3 arguments:
   ##
-  ## 1. ``key`` - the current key, if it exists, or the key passed to
-  ##    ``withKey`` otherwise;
-  ## 2. ``val`` - the current value, if the key exists, or default value
+  ## 1. `key` - the current key, if it exists, or the key passed to
+  ##    `withKey` otherwise;
+  ## 2. `val` - the current value, if the key exists, or default value
   ##    of the type otherwise;
-  ## 3. ``pairExists`` - ``true`` if the key exists, ``false`` otherwise.
+  ## 3. `pairExists` - `true` if the key exists, `false` otherwise.
   ##
-  ## The ``mapper`` can can modify ``val`` and ``pairExists`` values to change
+  ## The `mapper` can can modify `val` and `pairExists` values to change
   ## the mapping of the key or delete it from the table.
-  ## When adding a value, make sure to set ``pairExists`` to ``true`` along
-  ## with modifying the ``val``.
+  ## When adding a value, make sure to set `pairExists` to `true` along
+  ## with modifying the `val`.
   ##
   ## The operation is performed atomically and other operations on the table
-  ## will be blocked while the ``mapper`` is invoked, so it should be short and
+  ## will be blocked while the `mapper` is invoked, so it should be short and
   ## simple.
   ##
   ## Example usage:
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   # If value exists, decrement it.
   ##   # If it becomes zero or less, delete the key
   ##   t.withKey(1'i64) do (k: int64, v: var int, pairExists: var bool):
@@ -163,6 +199,7 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A,
   ##       dec v
   ##       if v <= 0:
   ##         pairExists = false
+  ##   ```
   withLock t:
     var hc: Hash
     var index = rawGet(t, key, hc)
@@ -171,36 +208,39 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A,
     if pairExists:
       mapper(t.data[index].key, t.data[index].val, pairExists)
       if not pairExists:
-        delImplIdx(t, index)
+        delImplIdx(t, index, tabMakeEmpty, tabCellEmpty, tabCellHash)
     else:
       var val: B
       mapper(key, val, pairExists)
       if pairExists:
-        maybeRehashPutImpl(enlarge)
+        st_maybeRehashPutImpl(enlarge)
 
 proc `[]=`*[A, B](t: var SharedTable[A, B], key: A, val: B) =
-  ## puts a (key, value)-pair into `t`.
+  ## Puts a (key, value)-pair into `t`.
   withLock t:
     putImpl(enlarge)
 
 proc add*[A, B](t: var SharedTable[A, B], key: A, val: B) =
-  ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
+  ## Puts a new (key, value)-pair into `t` even if `t[key]` already exists.
   ## This can introduce duplicate keys into the table!
   withLock t:
     addImpl(enlarge)
 
 proc del*[A, B](t: var SharedTable[A, B], key: A) =
-  ## deletes `key` from hash table `t`.
+  ## Deletes `key` from hash table `t`.
   withLock t:
-    delImpl()
+    delImpl(tabMakeEmpty, tabCellEmpty, tabCellHash)
 
-proc init*[A, B](t: var SharedTable[A, B], initialSize=64) =
-  ## creates a new hash table that is empty.
+proc len*[A, B](t: var SharedTable[A, B]): int =
+  ## Number of elements in `t`.
+  withLock t:
+    result = t.counter
+
+proc init*[A, B](t: var SharedTable[A, B], initialSize = 32) =
+  ## Creates a new hash 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 from this module.
-  assert isPowerOfTwo(initialSize)
+  ## This proc must be called before any other usage of `t`.
+  let initialSize = slotsNeeded(initialSize)
   t.counter = 0
   t.dataLen = initialSize
   t.data = cast[KeyValuePairSeq[A, B]](allocShared0(
@@ -210,13 +250,3 @@ proc init*[A, B](t: var SharedTable[A, B], initialSize=64) =
 proc deinitSharedTable*[A, B](t: var SharedTable[A, B]) =
   deallocShared(t.data)
   deinitLock t.lock
-
-proc initSharedTable*[A, B](initialSize=64): SharedTable[A, B] {.deprecated.} =
-  ## Deprecated. Use `init` instead.
-  ## This is not posix compliant, may introduce undefined behavior.
-  assert isPowerOfTwo(initialSize)
-  result.counter = 0
-  result.dataLen = initialSize
-  result.data = cast[KeyValuePairSeq[A, B]](allocShared0(
-                                     sizeof(KeyValuePair[A, B]) * initialSize))
-  initLock result.lock
diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim
index 9a5bffcef..3542741fa 100644
--- a/lib/pure/collections/tableimpl.nim
+++ b/lib/pure/collections/tableimpl.nim
@@ -7,51 +7,12 @@
 #    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.
-proc isEmpty(hcode: Hash): bool {.inline.} =
-  result = hcode == 0
-
-proc isFilled(hcode: Hash): bool {.inline.} =
-  result = hcode != 0
+include hashcommon
 
 const
-  growthFactor = 2
-
-proc mustRehash(length, counter: int): bool {.inline.} =
-  assert(length > counter)
-  result = (length * 2 < counter * 3) or (length - counter < 4)
-
-proc nextTry(h, maxHash: Hash): Hash {.inline.} =
-  result = (h + 1) and maxHash
-
-template rawGetKnownHCImpl() {.dirty.} =
-  var h: Hash = hc and maxHash(t)   # start with real hash value
-  while isFilled(t.data[h].hcode):
-    # Compare hc THEN key with boolean short circuit. This makes the common case
-    # zero ==key's for missing (e.g.inserts) and exactly one ==key for present.
-    # It does slow down succeeding lookups by one extra Hash cmp&and..usually
-    # just a few clock cycles, generally worth it for any non-integer-like A.
-    if t.data[h].hcode == hc and t.data[h].key == key:
-      return h
-    h = nextTry(h, maxHash(t))
-  result = -1 - h                   # < 0 => MISSING; insert idx = -1 - result
-
-template genHashImpl(key, hc: typed) =
-  hc = hash(key)
-  if hc == 0:       # This almost never taken branch should be very predictable.
-    hc = 314159265  # Value doesn't matter; Any non-zero favorite is fine.
-
-template genHash(key: typed): Hash =
-  var res: Hash
-  genHashImpl(key, res)
-  res
-
-template rawGetImpl() {.dirty.} =
-  genHashImpl(key, hc)
-  rawGetKnownHCImpl()
+  defaultInitialSize* = 32
 
 template rawGetDeepImpl() {.dirty.} =   # Search algo for unconditional add
   genHashImpl(key, hc)
@@ -65,28 +26,28 @@ template rawInsertImpl() {.dirty.} =
   data[h].val = val
   data[h].hcode = hc
 
-proc rawGetKnownHC[X, A](t: X, key: A, hc: Hash): int {.inline.} =
-  rawGetKnownHCImpl()
-
-proc rawGetDeep[X, A](t: X, key: A, hc: var Hash): int {.inline.} =
+proc rawGetDeep[X, A](t: X, key: A, hc: var Hash): int {.inline, outParamsAt: [3].} =
   rawGetDeepImpl()
 
-proc rawGet[X, A](t: X, key: A, hc: var Hash): int {.inline.} =
-  rawGetImpl()
-
 proc rawInsert[X, A, B](t: var X, data: var KeyValuePairSeq[A, B],
-                     key: A, val: B, hc: Hash, h: Hash) =
+                     key: A, val: sink B, hc: Hash, h: Hash) =
   rawInsertImpl()
 
+template checkIfInitialized() =
+  if t.dataLen == 0:
+    initImpl(t, defaultInitialSize)
+
 template addImpl(enlarge) {.dirty.} =
-  if mustRehash(t.dataLen, t.counter): enlarge(t)
+  checkIfInitialized()
+  if mustRehash(t): enlarge(t)
   var hc: Hash
   var j = rawGetDeep(t, key, hc)
   rawInsert(t, t.data, key, val, hc, j)
   inc(t.counter)
 
-template maybeRehashPutImpl(enlarge) {.dirty.} =
-  if mustRehash(t.dataLen, t.counter):
+template maybeRehashPutImpl(enlarge, val) {.dirty.} =
+  checkIfInitialized()
+  if mustRehash(t):
     enlarge(t)
     index = rawGetKnownHC(t, key, hc)
   index = -1 - index                  # important to transform for mgetOrPutImpl
@@ -94,64 +55,177 @@ template maybeRehashPutImpl(enlarge) {.dirty.} =
   inc(t.counter)
 
 template putImpl(enlarge) {.dirty.} =
-  var hc: Hash
+  checkIfInitialized()
+  var hc: Hash = default(Hash)
   var index = rawGet(t, key, hc)
   if index >= 0: t.data[index].val = val
-  else: maybeRehashPutImpl(enlarge)
+  else: maybeRehashPutImpl(enlarge, val)
 
 template mgetOrPutImpl(enlarge) {.dirty.} =
-  var hc: Hash
+  checkIfInitialized()
+  var hc: Hash = default(Hash)
   var index = rawGet(t, key, hc)
   if index < 0:
     # not present: insert (flipping index)
-    maybeRehashPutImpl(enlarge)
+    when declared(val):
+      maybeRehashPutImpl(enlarge, val)
+    else:
+      maybeRehashPutImpl(enlarge, default(B))
   # either way return modifiable val
   result = t.data[index].val
 
+# template mgetOrPutDefaultImpl(enlarge) {.dirty.} =
+#   checkIfInitialized()
+#   var hc: Hash = default(Hash)
+#   var index = rawGet(t, key, hc)
+#   if index < 0:
+#     # not present: insert (flipping index)
+#     maybeRehashPutImpl(enlarge, default(B))
+#   # either way return modifiable val
+#   result = t.data[index].val
+
 template hasKeyOrPutImpl(enlarge) {.dirty.} =
-  var hc: Hash
+  checkIfInitialized()
+  var hc: Hash = default(Hash)
   var index = rawGet(t, key, hc)
   if index < 0:
     result = false
-    maybeRehashPutImpl(enlarge)
+    maybeRehashPutImpl(enlarge, val)
   else: result = true
 
-template default[T](t: typedesc[T]): T =
-  var v: T
-  v
-
-template delImplIdx(t, i) =
+# delImplIdx is KnuthV3 Algo6.4R adapted to i=i+1 (from i=i-1) which has come to
+# be called "back shift delete".  It shifts elements in the collision cluster of
+# a victim backward to make things as-if the victim were never inserted in the
+# first place.  This is desirable to keep things "ageless" after many deletes.
+# It is trickier than you might guess since initial probe (aka "home") locations
+# of keys in a cluster may collide and since table addresses wrap around.
+#
+# A before-after diagram might look like ('.' means empty):
+#   slot:   0   1   2   3   4   5   6   7
+# before(1)
+#   hash1:  6   7   .   3   .   5   5   6  ; Really hash() and msk
+#   data1:  E   F   .   A   .   B   C   D  ; About to delete C @index 6
+# after(2)
+#   hash2:  7   .   .   3   .   5   6   6  ; Really hash() and msk
+#   data2:  F   .   .   A   .   B   D   E  ; After deletion of C
+#
+# This lowers total search depth over the whole table from 1+1+2+2+2+2=10 to 7.
+# Had the victim been B@5, C would need back shifting to slot 5.  Total depth is
+# always lowered by at least 1, e.g. victim A@3.  This is all quite fast when
+# empty slots are frequent (also needed to keep insert/miss searches fast) and
+# hash() is either fast or avoided (via `.hcode`).  It need not compare keys.
+#
+# delImplIdx realizes the above transformation, but only works for dense Linear
+# Probing, nextTry(h)=h+1.  This is not an important limitation since that's the
+# fastest sequence on any CPU made since the 1980s. { Performance analysis often
+# overweights "key cmp" neglecting cache behavior, giving bad ideas how big/slow
+# tables behave (when perf matters most!).  Comparing hcode first means usually
+# only 1 key cmp is needed for *any* seq.  Timing only predictable activity,
+# small tables, and/or integer keys often perpetuates such bad ideas. }
+
+template delImplIdx(t, i, makeEmpty, cellEmpty, cellHash) =
   let msk = maxHash(t)
   if i >= 0:
     dec(t.counter)
     block outer:
       while true:         # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
-        var j = i         # The correctness of this depends on (h+1) in nextTry,
+        var j = i         # The correctness of this depends on (h+1) in nextTry
         var r = j         # though may be adaptable to other simple sequences.
-        t.data[i].hcode = 0              # mark current EMPTY
-        t.data[i].key = default(type(t.data[i].key))
-        t.data[i].val = default(type(t.data[i].val))
+        makeEmpty(i)                     # mark current EMPTY
+        {.push warning[UnsafeDefault]:off.}
+        reset(t.data[i].key)
+        reset(t.data[i].val)
+        {.pop.}
         while true:
           i = (i + 1) and msk            # increment mod table size
-          if isEmpty(t.data[i].hcode):   # end of collision cluster; So all done
+          if cellEmpty(i):               # end of collision cluster; So all done
             break outer
-          r = t.data[i].hcode and msk    # "home" location of key@i
+          r = cellHash(i) and msk        # initial probe index for key@slot i
           if not ((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
             break
         when defined(js):
           t.data[j] = t.data[i]
         else:
-          shallowCopy(t.data[j], t.data[i]) # data[j] will be marked EMPTY next loop
+          t.data[j] = move(t.data[i]) # data[j] will be marked EMPTY next loop
 
-template delImpl() {.dirty.} =
+template delImpl(makeEmpty, cellEmpty, cellHash) {.dirty.} =
   var hc: Hash
   var i = rawGet(t, key, hc)
-  delImplIdx(t, i)
+  delImplIdx(t, i, makeEmpty, cellEmpty, cellHash)
+
+template delImplNoHCode(makeEmpty, cellEmpty, cellHash) {.dirty.} =
+  if t.dataLen > 0:
+    var i: Hash = hash(key) and maxHash(t)
+    while not cellEmpty(i):
+      if t.data[i].key == key:
+        delImplIdx(t, i, makeEmpty, cellEmpty, cellHash)
+        break
+      i = nextTry(i, maxHash(t))
 
 template clearImpl() {.dirty.} =
-  for i in 0 ..< t.data.len:
+  for i in 0 ..< t.dataLen:
     when compiles(t.data[i].hcode): # CountTable records don't contain a hcode
       t.data[i].hcode = 0
-    t.data[i].key = default(type(t.data[i].key))
-    t.data[i].val = default(type(t.data[i].val))
+    {.push warning[UnsafeDefault]:off.}
+    reset(t.data[i].key)
+    reset(t.data[i].val)
+    {.pop.}
   t.counter = 0
+
+template ctAnd(a, b): bool =
+  when a:
+    when b: true
+    else: false
+  else: false
+
+template initImpl(result: typed, size: int) =
+  let correctSize = slotsNeeded(size)
+  when ctAnd(declared(SharedTable), typeof(result) is SharedTable):
+    init(result, correctSize)
+  else:
+    result.counter = 0
+    newSeq(result.data, correctSize)
+    when compiles(result.first):
+      result.first = -1
+      result.last = -1
+
+template insertImpl() = # for CountTable
+  if t.dataLen == 0: initImpl(t, defaultInitialSize)
+  if mustRehash(t): enlarge(t)
+  ctRawInsert(t, t.data, key, val)
+  inc(t.counter)
+
+template getOrDefaultImpl(t, key): untyped =
+  mixin rawGet
+  var hc: Hash
+  var index = rawGet(t, key, hc)
+  if index >= 0: result = t.data[index].val
+
+template getOrDefaultImpl(t, key, default: untyped): untyped =
+  mixin rawGet
+  var hc: Hash
+  var index = rawGet(t, key, hc)
+  result = if index >= 0: t.data[index].val else: default
+
+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("}")
+
+template equalsImpl(s, t: 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
+  else:
+    return false
diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim
index 7b508b294..d414caeed 100644
--- a/lib/pure/collections/tables.nim
+++ b/lib/pure/collections/tables.nim
@@ -7,202 +7,627 @@
 #    distribution, for details about the copyright.
 #
 
-## The ``tables`` module implements variants of an efficient `hash table`:idx:
+## 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.
-## 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`
-## as a new independent table. b is initialised with the contents of `a`.
-## Changing `b` does not affect `a` and vice versa:
-##
-## .. code-block::
-##   import tables
-##
-##   var
-##     a = {1: "one", 2: "two"}.toTable  # creates a Table
-##     b = a
+## a mapping from keys to values.
 ##
-##   echo a, b  # output: {1: one, 2: two}{1: one, 2: two}
+## 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
 ##
-##   b[3] = "three"
-##   echo a, b  # output: {1: one, 2: two}{1: one, 2: two, 3: three}
-##   echo a == b  # output: false
+## For consistency with every other data type in Nim these have **value**
+## semantics, this means that `=` performs a copy of the hash table.
 ##
-## 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:
+## For `ref semantics<manual.html#types-reference-and-pointer-types>`_
+## use their `Ref` variants: `TableRef<#TableRef>`_,
+## `OrderedTableRef<#OrderedTableRef>`_, and `CountTableRef<#CountTableRef>`_.
 ##
-## .. code-block::
-##   import tables
+## 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:
+
+runnableExamples:
+  var
+    a = {1: "one", 2: "two"}.toTable  # creates a Table
+    b = a
+
+  assert a == b
+
+  b[3] = "three"
+  assert 3 notin a
+  assert 3 in b
+  assert a != b
+
+## 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:
+
+runnableExamples:
+  var
+    a = {1: "one", 2: "two"}.newTable  # creates a TableRef
+    b = a
+
+  assert a == b
+
+  b[3] = "three"
+
+  assert 3 in a
+  assert 3 in b
+  assert a == b
+
 ##
-##   var
-##     a = {1: "one", 2: "two"}.newTable  # creates a TableRef
-##     b = a
+## ----
 ##
-##   echo a, b  # output: {1: one, 2: two}{1: one, 2: two}
+
+## # Basic usage
+
+
+## ## Table
+runnableExamples:
+  from std/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
+
+  assert beatles == {"George": 1943, "Ringo": 1940, "Paul": 1942, "John": 1940}.toTable
+
+
+  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 exist, we create one with an empty sequence
+      # before we can add elements to it
+      beatlesByYear[birthYear] = @[]
+    beatlesByYear[birthYear].add(name)
+
+  assert beatlesByYear == {1940: @["John", "Ringo"], 1942: @["Paul"], 1943: @["George"]}.toTable
+
+## ## OrderedTable
+## `OrderedTable<#OrderedTable>`_ is used when it is important to preserve
+## the insertion order of keys.
+
+runnableExamples:
+  let
+    a = [('z', 1), ('y', 2), ('x', 3)]
+    ot = a.toOrderedTable  # ordered tables
+
+  assert $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:
+
+runnableExamples:
+  let myString = "abracadabra"
+  let letterFrequencies = toCountTable(myString)
+  assert $letterFrequencies == "{'a': 5, 'd': 1, 'b': 2, 'r': 2, 'c': 1}"
+
+## 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>`_:
+
+runnableExamples:
+  let myString = "abracadabra"
+  var letterFrequencies = initCountTable[char]()
+  for c in myString:
+    letterFrequencies.inc(c)
+  assert $letterFrequencies == "{'d': 1, 'r': 2, 'c': 1, 'a': 5, 'b': 2}"
+
 ##
-##   b[3] = "three"
-##   echo a, b  # output: {1: one, 2: two, 3: three}{1: one, 2: two, 3: three}
-##   echo a == b  # output: true
+## ----
 ##
+
+## ## Hashing
 ##
-## If you are using simple standard types like ``int`` or ``string`` for the
+## 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: int): Hash
-##   hashes.hash(x: float): Hash
-##   …
+##     Error: type mismatch: got (Person)
+##     but expected one of:
+##     hashes.hash(x: openArray[A]): Hash
+##     hashes.hash(x: int): Hash
+##     hashes.hash(x: float): Hash
 ##
 ## What is happening here is that the types used for table keys require to have
-## a ``hash()`` proc which will convert them to a `Hash <hashes.html#Hash>`_
+## a `hash()` proc which will convert them to a `Hash <hashes.html#Hash>`_
 ## value, and the compiler is listing all the hash functions it knows.
-## Additionally there has to be a ``==`` operator that provides the same
-## semantics as its corresponding ``hash`` proc.
+## Additionally there has to be a `==` operator that provides the same
+## semantics as its corresponding `hash` proc.
 ##
-## After you add ``hash`` and ``==`` for your custom type everything will work.
-## Currently however ``hash`` for objects is not defined, whereas
-## ``system.==`` for objects does exist and performs a "deep" comparison (every
+## After you add `hash` and `==` for your custom type everything will work.
+## Currently, however, `hash` for objects is not defined, whereas
+## `system.==` for objects does exist and performs a "deep" comparison (every
 ## field is compared) which is usually what you want. So in the following
-## example implementing only ``hash`` suffices:
-##
-## .. code-block::
-##   type
-##     Person = object
-##       firstName, lastName: string
-##
-##   proc hash(x: Person): Hash =
-##     ## Piggyback on the already available string hash proc.
-##     ##
-##     ## Without this proc nothing works!
-##     result = x.firstName.hash !& x.lastName.hash
-##     result = !$result
+## example implementing only `hash` suffices:
+
+runnableExamples:
+  import std/hashes
+
+  type
+    Person = object
+      firstName, lastName: string
+
+  proc hash(x: Person): Hash =
+    ## Piggyback on the already available string hash proc.
+    ##
+    ## Without this proc nothing works!
+    result = x.firstName.hash !& x.lastName.hash
+    result = !$result
+
+  var
+    salaries = initTable[Person, int]()
+    p1, p2: Person
+
+  p1.firstName = "Jon"
+  p1.lastName = "Ross"
+  salaries[p1] = 30_000
+
+  p2.firstName = "소진"
+  p2.lastName = "박"
+  salaries[p2] = 45_000
+
 ##
-##   var
-##     salaries = initTable[Person, int]()
-##     p1, p2: Person
+## ----
 ##
-##   p1.firstName = "Jon"
-##   p1.lastName = "Ross"
-##   salaries[p1] = 30_000
+
+## # See also
 ##
-##   p2.firstName = "소진"
-##   p2.lastName = "박"
-##   salaries[p2] = 45_000
+## * `json module<json.html>`_ for table-like structure which allows
+##   heterogeneous members
+## * `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 std/private/since
+import std/[hashes, math, algorithm]
 
-include "system/inclrtl"
+
+when not defined(nimHasEffectsOf):
+  {.pragma: effectsOf.}
 
 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>`_.
     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>`_.
 
-{.deprecated: [TTable: Table, PTable: TableRef].}
 
+# ------------------------------ helpers ---------------------------------
+
+# Do NOT move these to tableimpl.nim, because sharedtables uses that
+# file and has its own implementation.
 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 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 raiseKeyError[T](key: T) {.noinline, noreturn.} =
+  when compiles($key):
+    raise newException(KeyError, "key not found: " & $key)
+  else:
+    raise newException(KeyError, "key not found")
 
 template get(t, key): untyped =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If `key` is not in `t`, the ``KeyError`` exception is raised.
+  ## retrieves the value at `t[key]`. The value can be modified.
+  ## If `key` is not in `t`, the `KeyError` exception is raised.
   mixin rawGet
   var hc: Hash
   var index = rawGet(t, key, hc)
   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")
+    raiseKeyError(key)
 
-template getOrDefaultImpl(t, key): untyped =
-  mixin rawGet
-  var hc: Hash
-  var index = rawGet(t, key, hc)
-  if index >= 0: result = t.data[index].val
+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))
+      when defined(js):
+        rawInsert(t, t.data, n[i].key, n[i].val, eh, j)
+      else:
+        rawInsert(t, t.data, move n[i].key, move n[i].val, eh, j)
 
-template getOrDefaultImpl(t, key, default: untyped): untyped =
-  mixin rawGet
-  var hc: Hash
-  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
+
+
+# -------------------------------------------------------------------
+# ------------------------------ Table ------------------------------
+# -------------------------------------------------------------------
+
+proc initTable*[A, B](initialSize = defaultInitialSize): Table[A, B] =
+  ## Creates a new hash table that is empty.
+  ##
+  ## Starting from Nim v0.20, tables are initialized by default and it is
+  ## not necessary to call this function explicitly.
+  ##
+  ## See also:
+  ## * `toTable proc<#toTable,openArray[]>`_
+  ## * `newTable proc<#newTable>`_ for creating a `TableRef`
+  runnableExamples:
+    let
+      a = initTable[int, string]()
+      b = initTable[char, seq[int]]()
+  result = default(Table[A, B])
+  initImpl(result, initialSize)
+
+proc `[]=`*[A, B](t: var Table[A, B], key: A, val: sink 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 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>`_
+  ## * `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](pairs.len)
+  for key, val in items(pairs): result[key] = val
+
+proc `[]`*[A, B](t: Table[A, B], key: A): lent 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,sinkB>`_ 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 `[]`*[A, B](t: var Table[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.
+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.
+  ##
+  ## 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,sinkB>`_ 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 mget*[A, B](t: var Table[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 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
+  result = default(B)
   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`. Otherwise, `default`
-  ## is returned.
+  ## 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
+  result = default(B)
   getOrDefaultImpl(t, key, default)
 
-template withValue*[A, B](t: var Table[A, B], key: A, value, body: untyped) =
-  ## retrieves the value at ``t[key]``.
-  ## `value` can be modified in the scope of the ``withValue`` call.
+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.
+  ##
   ##
-  ## .. code-block:: nim
+  ## Note that while the value returned is of type `var B`,
+  ## it is easy to accidentally create a copy of the value at `t[key]`.
+  ## Remember that seqs and strings are value types, and therefore
+  ## cannot be copied into a separate variable for modification.
+  ## See the example below.
   ##
-  ##   sharedTable.withValue(key, value) do:
-  ##     # block is executed only if ``key`` in ``t``
-  ##     value.name = "username"
-  ##     value.uid = 1000
+  ## 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
+
+    # An example of accidentally creating a copy
+    var t = initTable[int, seq[int]]()
+    # In this example, we expect t[10] to be modified,
+    # but it is not.
+    var copiedSeq = t.mgetOrPut(10, @[10])
+    copiedSeq.add(20)
+    doAssert t[10] == @[10]
+    # Correct
+    t.mgetOrPut(25, @[25]).add(35)
+    doAssert t[25] == @[25, 35]
+
+  mgetOrPutImpl(enlarge)
+
+proc mgetOrPut*[A, B](t: var Table[A, B], key: A): var B =
+  ## Retrieves the value at `t[key]` or puts the
+  ## default initialization value for type `B` (e.g. 0 for any
+  ## integer type).
+  runnableExamples:
+    var a = {'a': 5}.newTable
+    doAssert a.mgetOrPut('a') == 5
+    a.mgetOrPut('z').inc
+    doAssert a == {'a': 5, 'z': 1}.newTable
+
+  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: sink B) {.deprecated:
+    "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} =
+  ## 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,sinkB>`_ for inserting a new
+  ## (key, value) pair in the table without introducing duplicates.
+  addImpl(enlarge)
+
+template tabMakeEmpty(i) = t.data[i].hcode = 0
+template tabCellEmpty(i) = isEmpty(t.data[i].hcode)
+template tabCellHash(i)  = t.data[i].hcode
+
+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.
+  ##
+  ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc),
+  ##   this may need to be called multiple times.
+  ##
+  ## See also:
+  ## * `pop proc<#pop,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(tabMakeEmpty, tabCellEmpty, tabCellHash)
+
+proc pop*[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.
+  ##
+  ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc),
+  ##   this may need to be called multiple times.
+  ##
+  ## 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.pop('b', i) == true
+    doAssert a == {'a': 5, 'c': 13}.toTable
+    doAssert i == 9
+    i = 0
+    doAssert a.pop('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:
+    val = move(t.data[index].val)
+    delImplIdx(t, index, tabMakeEmpty, tabCellEmpty, tabCellHash)
+
+proc take*[A, B](t: var Table[A, B], key: A, val: var B): bool {.inline.} =
+  ## Alias for:
+  ## * `pop proc<#pop,Table[A,B],A,B>`_
+  pop(t, key, val)
+
+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>`_
+  ## * `pop proc<#pop,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 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]`.
   ##
+  ## `value` can be modified in the scope of the `withValue` call.
+  runnableExamples:
+    type
+      User = object
+        name: string
+        uid: int
+
+    var t = initTable[int, User]()
+    let u = User(name: "Hello", uid: 99)
+    t[1] = u
+
+    t.withValue(1, value):
+      # block is executed only if `key` in `t`
+      value.name = "Nim"
+      value.uid = 1314
+
+    t.withValue(2, value):
+      value.name = "No"
+      value.uid = 521
+
+    assert t[1].name == "Nim"
+    assert t[1].uid == 1314
+
   mixin rawGet
   var hc: Hash
   var index = rawGet(t, key, hc)
@@ -213,19 +638,35 @@ 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]``.
-  ## `value` can be modified in the scope of the ``withValue`` call.
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   table.withValue(key, value) do:
-  ##     # block is executed only if ``key`` in ``t``
-  ##     value.name = "username"
-  ##     value.uid = 1000
-  ##   do:
-  ##     # block is executed when ``key`` not in ``t``
-  ##     raise newException(KeyError, "Key not found")
+  ## Retrieves the value at `t[key]`.
   ##
+  ## `value` can be modified in the scope of the `withValue` call.
+  runnableExamples:
+    type
+      User = object
+        name: string
+        uid: int
+
+    var t = initTable[int, User]()
+    let u = User(name: "Hello", uid: 99)
+    t[1] = u
+
+    t.withValue(1, value):
+      # block is executed only if `key` in `t`
+      value.name = "Nim"
+      value.uid = 1314
+
+    t.withValue(521, value):
+      doAssert false
+    do:
+      # block is executed when `key` not in `t`
+      t[1314] = User(name: "exist", uid: 521)
+
+    assert t[1].name == "Nim"
+    assert t[1].uid == 1314
+    assert t[1314].name == "exist"
+    assert t[1314].uid == 521
+
   mixin rawGet
   var hc: Hash
   var index = rawGet(t, key, hc)
@@ -236,326 +677,615 @@ 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`.
-  for h in 0..high(t.data):
-    if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val)
+  ## 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:**
+  ##
+  ##   ```Nim
+  ##   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]
+  ##   ```
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if isFilled(t.data[h].hcode):
+      yield (t.data[h].key, t.data[h].val)
+      assert(len(t) == L, "the length of the table changed while iterating over it")
 
 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.
-  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`.
-  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`.
-  for h in 0..high(t.data):
-    if isFilled(t.data[h].hcode): yield t.data[h].val
+  ## 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
+
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if isFilled(t.data[h].hcode):
+      yield (t.data[h].key, t.data[h].val)
+      assert(len(t) == L, "the length of the table changed while iterating over it")
+
+iterator keys*[A, B](t: Table[A, B]): lent A =
+  ## 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
+
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if isFilled(t.data[h].hcode):
+      yield t.data[h].key
+      assert(len(t) == L, "the length of the table changed while iterating over it")
+
+iterator values*[A, B](t: Table[A, B]): lent B =
+  ## 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
+
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if isFilled(t.data[h].hcode):
+      yield t.data[h].val
+      assert(len(t) == L, "the length of the table changed while iterating over it")
 
 iterator mvalues*[A, B](t: var Table[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 del*[A, B](t: var Table[A, B], key: A) =
-  ## deletes `key` from hash table `t`.
-  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)
+  ## 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
+
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if isFilled(t.data[h].hcode):
+      yield t.data[h].val
+      assert(len(t) == L, "the length of the table changed while iterating over it")
 
-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)
+iterator allValues*[A, B](t: Table[A, B]; key: A): B {.deprecated:
+    "Deprecated since v1.4; tables with duplicated keys are deprecated".} =
+  ## Iterates over any value in the table `t` that belongs to the given `key`.
+  ##
+  ## Used if you have a table with duplicate keys (as a result of using
+  ## `add proc<#add,Table[A,B],A,sinkB>`_).
+  ##
+  runnableExamples:
+    import std/[sequtils, algorithm]
 
-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)
+    var a = {'a': 3, 'b': 5}.toTable
+    for i in 1..3: a.add('z', 10*i)
+    doAssert toSeq(a.pairs).sorted == @[('a', 3), ('b', 5), ('z', 10), ('z', 20), ('z', 30)]
+    doAssert sorted(toSeq(a.allValues('z'))) == @[10, 20, 30]
+  var h: Hash = genHash(key) and high(t.data)
+  let L = len(t)
+  while isFilled(t.data[h].hcode):
+    if t.data[h].key == key:
+      yield t.data[h].val
+      assert(len(t) == L, "the length of the table changed while iterating over it")
+    h = nextTry(h, high(t.data))
 
-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)
+# -------------------------------------------------------------------
+# ---------------------------- TableRef -----------------------------
+# -------------------------------------------------------------------
 
-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.
+proc newTable*[A, B](initialSize = defaultInitialSize): TableRef[A, B] =
+  ## Creates a new ref hash 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 from this module.
-  assert isPowerOfTwo(initialSize)
-  result.counter = 0
-  newSeq(result.data, initialSize)
+  ## See also:
+  ## * `newTable proc<#newTable,openArray[]>`_ for creating a `TableRef`
+  ##   from a collection of `(key, value)` pairs
+  ## * `initTable proc<#initTable>`_ for creating a `Table`
+  runnableExamples:
+    let
+      a = newTable[int, string]()
+      b = newTable[char, seq[int]]()
 
-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
+  new(result)
+  {.noSideEffect.}:
+    result[] = initTable[A, B](initialSize)
 
-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 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>`_
+  ## * `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
 
-proc `$`*[A, B](t: Table[A, B]): string =
-  ## The `$` operator for hash tables.
-  dollarImpl()
+  new(result)
+  {.noSideEffect.}:
+    result[] = toTable[A, B](pairs)
 
-proc hasKey*[A, B](t: TableRef[A, B], key: A): bool =
-  ## returns true iff `key` is in the table `t`.
-  result = t[].hasKey(key)
+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]()
+  {.noSideEffect.}:
+    for item in collection:
+      result[index(item)] = item
 
-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 `[]`*[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,sinkB>`_ 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']
 
-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)
+  result = t[][key]
 
-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
+proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: sink 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
 
-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)
+  t[][key] = 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)
+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
 
-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
+  result = t[].hasKey(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 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
 
-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
+  return hasKey[A, B](t, 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 hasKeyOrPut*[A, B](t: 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
 
-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]
+  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`. Otherwise, `default`
-  ## is returned.
+  ## 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.
+  ##
+  ## Note that while the value returned is of type `var B`,
+  ## it is easy to accidentally create an copy of the value at `t[key]`.
+  ## Remember that seqs and strings are value types, and therefore
+  ## cannot be copied into a separate variable for modification.
+  ## See the example below.
+  ##
+  ## 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
+
+    # An example of accidentally creating a copy
+    var t = newTable[int, seq[int]]()
+    # In this example, we expect t[10] to be modified,
+    # but it is not.
+    var copiedSeq = t.mgetOrPut(10, @[10])
+    copiedSeq.add(20)
+    doAssert t[10] == @[10]
+    # Correct
+    t.mgetOrPut(25, @[25]).add(35)
+    doAssert t[25] == @[25, 35]
   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 mgetOrPut*[A, B](t: TableRef[A, B], key: A): var B =
+  ## Retrieves the value at `t[key]` or puts the
+  ## default initialization value for type `B` (e.g. 0 for any
+  ## integer type).
+  runnableExamples:
+    var a = {'a': 5}.newTable
+    doAssert a.mgetOrPut('a') == 5
+    a.mgetOrPut('z').inc
+    doAssert a == {'a': 5, 'z': 1}.newTable
 
-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)
+  t[].mgetOrPut(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
 
-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!
+  result = t.counter
+
+proc add*[A, B](t: TableRef[A, B], key: A, val: sink B) {.deprecated:
+    "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} =
+  ## 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,sinkB>`_ 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`.
+  ## Deletes `key` from hash table `t`. Does nothing if the key does not exist.
+  ##
+  ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc),
+  ##   this may need to be called multiple times.
+  ##
+  ## See also:
+  ## * `pop proc<#pop,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.
-  ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
-  ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
+proc pop*[A, B](t: TableRef[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.
-  result = t[].take(key, val)
+  ##
+  ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc),
+  ##   this may need to be called multiple times.
+  ##
+  ## 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.pop('b', i) == true
+    doAssert a == {'a': 5, 'c': 13}.newTable
+    doAssert i == 9
+    i = 0
+    doAssert a.pop('z', i) == false
+    doAssert a == {'a': 5, 'c': 13}.newTable
+    doAssert i == 0
+
+  result = t[].pop(key, val)
+
+proc take*[A, B](t: TableRef[A, B], key: A, val: var B): bool {.inline.} =
+  ## Alias for:
+  ## * `pop proc<#pop,TableRef[A,B],A,B>`_
+  pop(t, key, val)
 
-proc newTable*[A, B](initialSize=64): TableRef[A, B] =
-  new(result)
-  result[] = initTable[A, B](initialSize)
+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>`_
+  ## * `pop proc<#pop,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
 
-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)
+  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]
+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:**
+  ##
+  ##   ```Nim
+  ##   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]
+  ##   ```
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if isFilled(t.data[h].hcode):
+      yield (t.data[h].key, t.data[h].val)
+      assert(len(t) == L, "the length of the table changed while iterating over it")
 
-{.deprecated: [TOrderedTable: OrderedTable, POrderedTable: OrderedTableRef].}
+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
+
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if isFilled(t.data[h].hcode):
+      yield (t.data[h].key, t.data[h].val)
+      assert(len(t) == L, "the length of the table changed while iterating over it")
+
+iterator keys*[A, B](t: TableRef[A, B]): lent 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
+
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if isFilled(t.data[h].hcode):
+      yield t.data[h].key
+      assert(len(t) == L, "the length of the table changed while iterating over it")
+
+iterator values*[A, B](t: TableRef[A, B]): lent 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
+
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if isFilled(t.data[h].hcode):
+      yield t.data[h].val
+      assert(len(t) == L, "the length of the table changed while iterating over it")
 
-proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} =
-  ## returns the number of keys in `t`.
-  result = t.counter
+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
+
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if isFilled(t.data[h].hcode):
+      yield t.data[h].val
+      assert(len(t) == L, "the length of the table changed while iterating over it")
 
-proc clear*[A, B](t: var OrderedTable[A, B]) =
-  ## Resets the table so that it is empty.
-  clearImpl()
-  t.first = -1
-  t.last = -1
 
-proc clear*[A, B](t: var OrderedTableRef[A, B]) =
-  ## Resets the table so that is is empty.
-  clear(t[])
 
-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 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 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
+# ---------------------------------------------------------------------------
+# ------------------------------ OrderedTable -------------------------------
+# ---------------------------------------------------------------------------
+
+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>`_.
+    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>`_.
+
+
+# ------------------------------ helpers ---------------------------------
 
 proc rawGetKnownHC[A, B](t: OrderedTable[A, B], key: A, hc: Hash): int =
   rawGetKnownHCImpl()
@@ -566,46 +1296,9 @@ proc rawGetDeep[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int {.inline
 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)
-
-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)
-
-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)
-
-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 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 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 rawInsert[A, B](t: var OrderedTable[A, B],
                      data: var OrderedKeyValuePairSeq[A, B],
-                     key: A, val: B, hc: Hash, h: Hash) =
+                     key: A, val: sink B, hc: Hash, h: Hash) =
   rawInsertImpl()
   data[h].next = -1
   if t.first < 0: t.first = h
@@ -626,70 +1319,344 @@ proc enlarge[A, B](t: var OrderedTable[A, B]) =
       var j: Hash = eh and maxHash(t)
       while isFilled(t.data[j].hcode):
         j = nextTry(j, maxHash(t))
-      rawInsert(t, t.data, n[h].key, n[h].val, n[h].hcode, j)
+      rawInsert(t, t.data, move n[h].key, move 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`.
+template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} =
+  if t.counter > 0:
+    var h = t.first
+    while h >= 0:
+      var nxt = t.data[h].next
+      if isFilled(t.data[h].hcode):
+        yieldStmt
+      h = nxt
+
+# ----------------------------------------------------------------------
+
+proc initOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTable[A, B] =
+  ## Creates a new ordered hash table that is empty.
+  ##
+  ## Starting from Nim v0.20, tables are initialized by default and it is
+  ## not necessary to call this function explicitly.
+  ##
+  ## See also:
+  ## * `toOrderedTable proc<#toOrderedTable,openArray[]>`_
+  ## * `newOrderedTable proc<#newOrderedTable>`_ for creating an
+  ##   `OrderedTableRef`
+  runnableExamples:
+    let
+      a = initOrderedTable[int, string]()
+      b = initOrderedTable[char, seq[int]]()
+  result = default(OrderedTable[A, B])
+  initImpl(result, initialSize)
+
+proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: sink 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 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)
+proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] =
+  ## 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>`_
+  ## * `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](pairs.len)
+  for key, val in items(pairs): result[key] = val
+
+proc `[]`*[A, B](t: OrderedTable[A, B], key: A): lent 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,sinkB>`_ 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](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,sinkB>`_ 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 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 = default(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
+  result = default(B)
+  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
+  result = default(B)
+  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 ``value`` 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<#[],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 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 mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A): var B =
+  ## Retrieves the value at `t[key]` or puts the
+  ## default initialization value for type `B` (e.g. 0 for any
+  ## integer type).
+  runnableExamples:
+    var a = {'a': 5}.toOrderedTable
+    doAssert a.mgetOrPut('a') == 5
+    a.mgetOrPut('z').inc
+    doAssert a == {'a': 5, 'z': 1}.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 initOrderedTable*[A, B](initialSize=64): OrderedTable[A, B] =
-  ## creates a new ordered hash table that is empty.
+proc add*[A, B](t: var OrderedTable[A, B], key: A, val: sink B) {.deprecated:
+    "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} =
+  ## Puts a new `(key, value)` pair into `t` even if `t[key]` already exists.
+  ##
+  ## **This can introduce duplicate keys into the table!**
   ##
-  ## `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
-  result.first = -1
-  result.last = -1
-  newSeq(result.data, initialSize)
+  ## Use `[]= proc<#[]=,OrderedTable[A,B],A,sinkB>`_ for inserting a new
+  ## (key, value) pair in the table without introducing duplicates.
+  addImpl(enlarge)
 
-proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] =
-  ## creates a new ordered hash table that contains the given `pairs`.
-  result = initOrderedTable[A, B](rightSize(pairs.len))
-  for key, val in items(pairs): result[key] = val
+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:
+  ## * `pop proc<#pop,OrderedTable[A,B],A,B>`_
+  ## * `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
 
-proc `$`*[A, B](t: OrderedTable[A, B]): string =
-  ## The `$` operator for ordered hash tables.
-  dollarImpl()
+  if t.counter == 0: return
+  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, move n[h].key, move n[h].val, n[h].hcode, j)
+    h = nxt
 
-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 pop*[A, B](t: var OrderedTable[A, B], key: A, val: var B): bool {.since: (1, 1).} =
+  ## 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.
+  ##
+  ## O(n) complexity.
+  ##
+  ## See also:
+  ## * `del proc<#del,OrderedTable[A,B],A>`_
+  ## * `clear proc<#clear,OrderedTable[A,B]>`_ to empty the whole table
+  runnableExamples:
+    var
+      a = {'c': 5, 'b': 9, 'a': 13}.toOrderedTable
+      i: int
+    doAssert a.pop('b', i) == true
+    doAssert a == {'c': 5, 'a': 13}.toOrderedTable
+    doAssert i == 9
+    i = 0
+    doAssert a.pop('z', i) == false
+    doAssert a == {'c': 5, 'a': 13}.toOrderedTable
+    doAssert i == 0
+
+  var hc: Hash
+  var index = rawGet(t, key, hc)
+  result = index >= 0
+  if result:
+    val = move(t.data[index].val)
+    del(t, key)
 
-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
+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>`_
+  ## * `pop proc<#pop,OrderedTable[A,B],A,B>`_
+  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,
+    order = SortOrder.Ascending) {.effectsOf: cmp.} =
+  ## 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:
+    import std/[algorithm]
+    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
+    a.sort(system.cmp, order = SortOrder.Descending)
+    doAssert a == {'c': 0, 'b': 20, 'a': 10}.toOrderedTable
+
   var list = t.first
   var
     p, q, e, tail, oldhead: int
@@ -716,7 +1683,7 @@ proc sort*[A, B](t: var OrderedTable[A, B], cmp: proc (x,y: (A, B)): int) =
         elif qsize == 0 or q < 0:
           e = p; p = t.data[p].next; dec(psize)
         elif cmp((t.data[p].key, t.data[p].val),
-                 (t.data[q].key, t.data[q].val)) <= 0:
+                 (t.data[q].key, t.data[q].val)) * order <= 0:
           e = p; p = t.data[p].next; dec(psize)
         else:
           e = q; q = t.data[q].next; dec(qsize)
@@ -730,246 +1697,607 @@ 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
+  if s.counter == 0 and t.counter == 0:
+    return true
+  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:**
+  ##
+  ##   ```Nim
+  ##   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]
+  ##   ```
+
+  let L = len(t)
   forAllOrderedPairs:
     yield (t.data[h].key, t.data[h].val)
+    assert(len(t) == L, "the length of the table changed while iterating over it")
 
-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
+
+  let L = len(t)
   forAllOrderedPairs:
     yield (t.data[h].key, t.data[h].val)
+    assert(len(t) == L, "the length of the table changed while iterating over it")
 
-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]): lent 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
+
+  let L = len(t)
   forAllOrderedPairs:
     yield t.data[h].key
+    assert(len(t) == L, "the length of the table changed while iterating over it")
 
-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]): lent 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
+
+  let L = len(t)
   forAllOrderedPairs:
     yield t.data[h].val
+    assert(len(t) == L, "the length of the table changed while iterating over it")
 
-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
+
+  let L = len(t)
   forAllOrderedPairs:
     yield t.data[h].val
+    assert(len(t) == L, "the length of the table changed while iterating over it")
+
+# ---------------------------------------------------------------------------
+# --------------------------- OrderedTableRef -------------------------------
+# ---------------------------------------------------------------------------
+
+proc newOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTableRef[A, B] =
+  ## Creates a new ordered ref hash table that is empty.
+  ##
+  ## See also:
+  ## * `newOrderedTable proc<#newOrderedTable,openArray[]>`_ for creating
+  ##   an `OrderedTableRef` from a collection of `(key, value)` pairs
+  ## * `initOrderedTable proc<#initOrderedTable>`_ for creating an
+  ##   `OrderedTable`
+  runnableExamples:
+    let
+      a = newOrderedTable[int, string]()
+      b = newOrderedTable[char, seq[int]]()
+  new(result)
+  {.noSideEffect.}:
+    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>`_
+  ## * `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](pairs.len)
+  {.noSideEffect.}:
+    for key, val in items(pairs): result[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,sinkB>`_ 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: sink 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: 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 mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A): var B =
+  ## Retrieves the value at `t[key]` or puts the
+  ## default initialization value for type `B` (e.g. 0 for any
+  ## integer type).
+  runnableExamples:
+    var a = {'a': 5}.toOrderedTable
+    doAssert a.mgetOrPut('a') == 5
+    a.mgetOrPut('z').inc
+    doAssert a == {'a': 5, 'z': 1}.toOrderedTable
 
-proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool =
-  ## returns true iff `key` is in the table `t`.
-  result = t[].hasKey(key)
+  t[].mgetOrPut(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 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
 
-proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
-  ## puts a (key, value)-pair into `t`.
-  t[][key] = val
+  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!
+proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: sink B) {.deprecated:
+    "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} =
+  ## 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,sinkB>`_ 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: 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
 
-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)
+  t[].del(key)
+
+proc pop*[A, B](t: OrderedTableRef[A, B], key: A, val: var B): bool {.since: (1, 1).} =
+  ## 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,OrderedTableRef[A,B],A>`_
+  ## * `clear proc<#clear,OrderedTableRef[A,B]>`_ to empty the whole table
+  runnableExamples:
+    var
+      a = {'c': 5, 'b': 9, 'a': 13}.newOrderedTable
+      i: int
+    doAssert a.pop('b', i) == true
+    doAssert a == {'c': 5, 'a': 13}.newOrderedTable
+    doAssert i == 9
+    i = 0
+    doAssert a.pop('z', i) == false
+    doAssert a == {'c': 5, 'a': 13}.newOrderedTable
+    doAssert i == 0
+
+  pop(t[], key, val)
+
+proc clear*[A, B](t: OrderedTableRef[A, B]) =
+  ## Resets the table so that it is empty.
+  ##
+  ## See also:
+  ## * `del proc<#del,OrderedTableRef[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,
+    order = SortOrder.Ascending) {.effectsOf: cmp.} =
+  ## 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:
+    import std/[algorithm]
+    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
+    a.sort(system.cmp, order = SortOrder.Descending)
+    doAssert a == {'c': 0, 'b': 20, 'a': 10}.newOrderedTable
+
+  t[].sort(cmp, order = order)
 
 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.
-  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.
-  t[].del(key)
-
-# ------------------------------ count tables -------------------------------
+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:**
+  ##
+  ##   ```Nim
+  ##   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]
+  ##   ```
 
-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]
+  let L = len(t)
+  forAllOrderedPairs:
+    yield (t.data[h].key, t.data[h].val)
+    assert(len(t) == L, "the length of the table changed while iterating over it")
 
-{.deprecated: [TCountTable: CountTable, PCountTable: CountTableRef].}
+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
+
+  let L = len(t)
+  forAllOrderedPairs:
+    yield (t.data[h].key, t.data[h].val)
+    assert(len(t) == L, "the length of the table changed while iterating over it")
 
-proc len*[A](t: CountTable[A]): int =
-  ## returns the number of keys in `t`.
-  result = t.counter
+iterator keys*[A, B](t: OrderedTableRef[A, B]): lent 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
+
+  let L = len(t)
+  forAllOrderedPairs:
+    yield t.data[h].key
+    assert(len(t) == L, "the length of the table changed while iterating over it")
 
-proc clear*[A](t: CountTableRef[A]) =
-  ## Resets the table so that it is empty.
-  clearImpl()
+iterator values*[A, B](t: OrderedTableRef[A, B]): lent 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
+
+  let L = len(t)
+  forAllOrderedPairs:
+    yield t.data[h].val
+    assert(len(t) == L, "the length of the table changed while iterating over it")
 
-proc clear*[A](t: var CountTable[A]) =
-  ## Resets the table so that it is empty.
-  clearImpl()
+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
+
+  let L = len(t)
+  forAllOrderedPairs:
+    yield t.data[h].val
+    assert(len(t) == L, "the length of the table changed while iterating over it")
 
-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](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](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](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](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>`_.
+    data: seq[tuple[key: A, val: int]]
+    counter: int
+    isSorted: bool
+  CountTableRef*[A] = ref CountTable[A] ## Ref version of
+    ## `CountTable<#CountTable>`_.
+    ##
+    ## For creating a new empty CountTableRef, use `newCountTable proc
+    ## <#newCountTable>`_.
 
-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]],
+proc ctRawInsert[A](t: CountTable[A], data: var seq[tuple[key: A, val: int]],
                   key: A, val: int) =
   var h: Hash = hash(key) and high(data)
   while data[h].val != 0: h = nextTry(h, high(data))
@@ -980,424 +2308,665 @@ proc enlarge[A](t: var CountTable[A]) =
   var n: seq[tuple[key: A, val: int]]
   newSeq(n, len(t.data) * growthFactor)
   for i in countup(0, high(t.data)):
-    if t.data[i].val != 0: rawInsert(t, n, t.data[i].key, t.data[i].val)
+    if t.data[i].val != 0: ctRawInsert(t, n, move t.data[i].key, move t.data[i].val)
   swap(t.data, n)
 
+proc rawGet[A](t: CountTable[A], key: A): int =
+  if t.data.len == 0:
+    return -1
+  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 = defaultInitialSize): CountTable[A] =
+  ## Creates a new count table that is empty.
+  ##
+  ## Starting from Nim v0.20, tables are initialized by default and it is
+  ## not necessary to call this function explicitly.
+  ##
+  ## See also:
+  ## * `toCountTable proc<#toCountTable,openArray[A]>`_
+  ## * `newCountTable proc<#newCountTable>`_ for creating a
+  ##   `CountTableRef`
+  result = default(CountTable[A])
+  initImpl(result, 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](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
+  ## * `[]= 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
+  assert(not t.isSorted, "CountTable must not be used after sorting")
+  ctget(t, key, 0)
+
+template cntMakeEmpty(i) = t.data[i].val = 0
+template cntCellEmpty(i) = t.data[i].val == 0
+template cntCellHash(i)  = hash(t.data[i].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(not t.isSorted, "CountTable must not be used after sorting")
   assert val >= 0
-  var h = rawGet(t, key)
-  if h >= 0:
-    t.data[h].val = val
+  if val == 0:
+    delImplNoHCode(cntMakeEmpty, cntCellEmpty, cntCellHash)
   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
+    let h = rawGet(t, key)
+    if h >= 0:
+      t.data[h].val = val
+    else:
+      insertImpl()
 
 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")
+
+  assert(not t.isSorted, "CountTable must not be used after sorting")
   var index = rawGet(t, key)
   if index >= 0:
     inc(t.data[index].val, val)
-    if t.data[index].val == 0: dec(t.counter)
+    if t.data[index].val == 0:
+      delImplIdx(t, index, cntMakeEmpty, cntCellEmpty, cntCellHash)
   else:
-    if mustRehash(len(t.data), t.counter): enlarge(t)
-    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)
+    if val != 0:
+      insertImpl()
 
-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 len*[A](t: CountTable[A]): int =
+  ## Returns the number of keys in `t`.
+  result = t.counter
 
 proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] =
-  ## returns the (key,val)-pair with the smallest `val`. Efficiency: O(n)
-  assert t.len > 0
+  ## Returns the `(key, value)` pair with the smallest `val`. Efficiency: O(n)
+  ##
+  ## See also:
+  ## * `largest proc<#largest,CountTable[A]>`_
+  assert t.len > 0, "counttable is empty"
   var minIdx = -1
-  for h in 0..high(t.data):
+  for h in 0 .. high(t.data):
     if t.data[h].val > 0 and (minIdx == -1 or t.data[minIdx].val > t.data[h].val):
       minIdx = h
   result.key = t.data[minIdx].key
   result.val = t.data[minIdx].val
 
 proc largest*[A](t: CountTable[A]): tuple[key: A, val: int] =
-  ## returns the (key,val)-pair with the largest `val`. Efficiency: O(n)
-  assert t.len > 0
+  ## Returns the `(key, value)` pair with the largest `val`. Efficiency: O(n)
+  ##
+  ## See also:
+  ## * `smallest proc<#smallest,CountTable[A]>`_
+  assert t.len > 0, "counttable is empty"
   var maxIdx = 0
-  for h in 1..high(t.data):
+  for h in 1 .. high(t.data):
     if t.data[maxIdx].val < t.data[h].val: maxIdx = h
   result.key = t.data[maxIdx].key
   result.val = t.data[maxIdx].val
 
-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.
+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
+  assert(not t.isSorted, "CountTable must not be used after sorting")
+  result = rawGet(t, key) >= 0
 
-  # we use shellsort here; fast enough and simple
-  var h = 1
-  while true:
-    h = 3 * h + 1
-    if h >= high(t.data): break
-  while true:
-    h = h div 3
-    for i in countup(h, high(t.data)):
-      var j = i
-      while t.data[j-h].val <= t.data[j].val:
-        swap(t.data[j], t.data[j-h])
-        j = j-h
-        if j < h: break
-    if h == 1: break
+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 len*[A](t: CountTableRef[A]): int =
-  ## returns the number of keys in `t`.
-  result = t.counter
+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 del*[A](t: var CountTable[A], key: A) {.since: (1, 1).} =
+  ## Deletes `key` from table `t`. Does nothing if the key does not exist.
+  ##
+  ## See also:
+  ## * `pop proc<#pop,CountTable[A],A,int>`_
+  ## * `clear proc<#clear,CountTable[A]>`_ to empty the whole table
+  runnableExamples:
+    var a = toCountTable("aabbbccccc")
+    a.del('b')
+    assert a == toCountTable("aaccccc")
+    a.del('b')
+    assert a == toCountTable("aaccccc")
+    a.del('c')
+    assert a == toCountTable("aa")
+
+  delImplNoHCode(cntMakeEmpty, cntCellEmpty, cntCellHash)
+
+proc pop*[A](t: var CountTable[A], key: A, val: var int): bool {.since: (1, 1).} =
+  ## 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,CountTable[A],A>`_
+  ## * `clear proc<#clear,CountTable[A]>`_ to empty the whole table
+  runnableExamples:
+    var a = toCountTable("aabbbccccc")
+    var i = 0
+    assert a.pop('b', i)
+    assert i == 3
+    i = 99
+    assert not a.pop('b', i)
+    assert i == 99
 
-iterator pairs*[A](t: CountTableRef[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)
+  var index = rawGet(t, key)
+  result = index >= 0
+  if result:
+    val = move(t.data[index].val)
+    delImplIdx(t, index, cntMakeEmpty, cntCellEmpty, cntCellHash)
 
-iterator mpairs*[A](t: CountTableRef[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)
+proc clear*[A](t: var CountTable[A]) =
+  ## Resets the table so that it is empty.
+  ##
+  ## See also:
+  ## * `del proc<#del,CountTable[A],A>`_
+  ## * `pop proc<#pop,CountTable[A],A,int>`_
+  clearImpl()
+  t.isSorted = false
 
-iterator keys*[A](t: CountTableRef[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
+func ctCmp[T](a, b: tuple[key: T, val: int]): int =
+  result = system.cmp(a.val, b.val)
 
-iterator values*[A](t: CountTableRef[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
+proc sort*[A](t: var CountTable[A], order = SortOrder.Descending) =
+  ## Sorts the count table so that, by default, the entry with the
+  ## highest counter comes first.
+  ##
+  ## .. warning:: This is destructive! Once sorted, 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.
+  runnableExamples:
+    import std/[algorithm, sequtils]
+    var a = toCountTable("abracadabra")
+    doAssert a == "aaaaabbrrcd".toCountTable
+    a.sort()
+    doAssert toSeq(a.values) == @[5, 2, 2, 1, 1]
+    a.sort(SortOrder.Ascending)
+    doAssert toSeq(a.values) == @[1, 1, 2, 2, 5]
+
+  t.data.sort(cmp = ctCmp, order = order)
+  t.isSorted = true
 
-iterator mvalues*[A](t: CountTableRef[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 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")
+
+  assert(not s.isSorted, "CountTable must not be used after sorting")
+  for key, value in t:
+    s.inc(key, value)
 
-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]
+when (NimMajor, NimMinor) <= (1, 0):
+  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")
 
-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]
+    result = initCountTable[A](nextPowerOfTwo(max(s.len, t.len)))
+    for table in @[s, t]:
+      for key, value in table:
+        result.inc(key, value)
 
-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 `$`*[A](t: CountTable[A]): string =
+  ## The `$` operator for count tables. Used internally when calling `echo`
+  ## on a table.
+  dollarImpl()
 
-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 `==`*[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)
 
-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)
+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:**
+  ##
+  ##   ```Nim
+  ##   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
+  ##   ```
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if t.data[h].val != 0:
+      yield (t.data[h].key, t.data[h].val)
+      assert(len(t) == L, "the length of the table changed while iterating over it")
+
+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")
+
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if t.data[h].val != 0:
+      yield (t.data[h].key, t.data[h].val)
+      assert(len(t) == L, "the length of the table changed while iterating over it")
+
+iterator keys*[A](t: CountTable[A]): lent 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")
+
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if t.data[h].val != 0:
+      yield t.data[h].key
+      assert(len(t) == L, "the length of the table changed while iterating over it")
+
+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
+
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if t.data[h].val != 0:
+      yield t.data[h].val
+      assert(len(t) == L, "the length of the table changed while iterating over it")
+
+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")
+
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if t.data[h].val != 0:
+      yield t.data[h].val
+      assert(len(t) == L, "the length of the table changed while iterating over it")
+
 
-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
 
-proc inc*[A](t: CountTableRef[A], key: A, val = 1) =
-  ## increments `t[key]` by `val`.
-  t[].inc(key, val)
 
-proc newCountTable*[A](initialSize=64): CountTableRef[A] =
-  ## creates a new count table that is empty.
+
+
+
+# ---------------------------------------------------------------------------
+# ---------------------------- CountTableRef --------------------------------
+# ---------------------------------------------------------------------------
+
+proc inc*[A](t: CountTableRef[A], key: A, val = 1)
+
+proc newCountTable*[A](initialSize = defaultInitialSize): CountTableRef[A] =
+  ## Creates a new ref 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`` method in this module.
+  ## See also:
+  ## * `newCountTable proc<#newCountTable,openArray[A]>`_ for creating
+  ##   a `CountTableRef` from a collection
+  ## * `initCountTable proc<#initCountTable>`_ for creating a
+  ##   `CountTable`
   new(result)
-  result[] = initCountTable[A](initialSize)
+  {.noSideEffect.}:
+    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`.
-  result = newCountTable[A](rightSize(keys.len))
-  for key in items(keys): result.inc(key)
-
-proc `$`*[A](t: CountTableRef[A]): string =
-  ## The `$` operator for count tables.
-  dollarImpl()
+  ## 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](keys.len)
+  {.noSideEffect.}:
+    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
+  ## * `inc proc<#inc,CountTableRef[A],A,int>`_ to inc even if missing
+  ## * `[]= 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 `==`*[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
-  ## count. Insert order does not matter.
-  if isNil(s): result = isNil(t)
-  elif isNil(t): result = false
-  else: result = s[] == t[]
+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
+  {.noSideEffect.}:
+    t[][key] = val
 
-proc smallest*[A](t: CountTableRef[A]): (A, int) =
-  ## returns the (key,val)-pair with the smallest `val`. Efficiency: O(n)
+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")
+  {.noSideEffect.}:
+    t[].inc(key, val)
+
+proc smallest*[A](t: CountTableRef[A]): tuple[key: A, val: 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,val)-pair with the largest `val`. Efficiency: O(n)
+proc largest*[A](t: CountTableRef[A]): tuple[key: A, val: int] =
+  ## Returns the `(key, value)` pair with the largest `val`. Efficiency: O(n)
+  ##
+  ## See also:
+  ## * `smallest proc<#smallest,CountTable[A]>`_
   t[].largest
 
-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
+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 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 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 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 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 del*[A](t: CountTableRef[A], key: A) {.since: (1, 1).} =
+  ## Deletes `key` from table `t`. Does nothing if the key does not exist.
+  ##
+  ## See also:
+  ## * `pop proc<#pop,CountTableRef[A],A,int>`_
+  ## * `clear proc<#clear,CountTableRef[A]>`_ to empty the whole table
+  del(t[], key)
+
+proc pop*[A](t: CountTableRef[A], key: A, val: var int): bool {.since: (1, 1).} =
+  ## 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,CountTableRef[A],A>`_
+  ## * `clear proc<#clear,CountTableRef[A]>`_ to empty the whole table
+  pop(t[], key, val)
+
+proc clear*[A](t: CountTableRef[A]) =
+  ## Resets the table so that it is empty.
+  ##
+  ## See also:
+  ## * `del proc<#del,CountTableRef[A],A>`_
+  ## * `pop proc<#pop,CountTableRef[A],A,int>`_
+  clear(t[])
+
+proc sort*[A](t: CountTableRef[A], order = SortOrder.Descending) =
+  ## Sorts the count table so that, by default, 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(order = order)
 
 proc merge*[A](s, t: CountTableRef[A]) =
-  ## merges the second table into the first one
+  ## 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[])
 
-when isMainModule:
-  type
-    Person = object
-      firstName, lastName: string
+proc `$`*[A](t: CountTableRef[A]): string =
+  ## The `$` operator for count tables. Used internally when calling `echo`
+  ## on a table.
+  dollarImpl()
 
-  proc hash(x: Person): Hash =
-    ## Piggyback on the already available string hash proc.
-    ##
-    ## Without this proc nothing works!
-    result = x.firstName.hash !& x.lastName.hash
-    result = !$result
+proc `==`*[A](s, t: CountTableRef[A]): bool =
+  ## 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[]
 
-  var
-    salaries = initTable[Person, int]()
-    p1, p2: Person
-  p1.firstName = "Jon"
-  p1.lastName = "Ross"
-  salaries[p1] = 30_000
-  p2.firstName = "소진"
-  p2.lastName = "박"
-  salaries[p2] = 45_000
-  var
-    s2 = initOrderedTable[Person, int]()
-    s3 = initCountTable[Person]()
-  s2[p1] = 30_000
-  s2[p2] = 45_000
-  s3[p1] = 30_000
-  s3[p2] = 45_000
-
-  block: # Ordered table should preserve order after deletion
-    var
-      s4 = initOrderedTable[int, int]()
-    s4[1] = 1
-    s4[2] = 2
-    s4[3] = 3
-
-    var prev = 0
-    for i in s4.values:
-      doAssert(prev < i)
-      prev = i
-
-    s4.del(2)
-    doAssert(2 notin s4)
-    doAssert(s4.len == 2)
-    prev = 0
-    for i in s4.values:
-      doAssert(prev < i)
-      prev = i
-
-  block: # Deletion from OrderedTable should account for collision groups. See issue #5057.
-    # The bug is reproducible only with exact keys
-    const key1 = "boy_jackpot.inGamma"
-    const key2 = "boy_jackpot.outBlack"
-
-    var t = {
-        key1: 0,
-        key2: 0
-    }.toOrderedTable()
-
-    t.del(key1)
-    assert(t.len == 1)
-    assert(key2 in t)
 
-  var
-    t1 = initCountTable[string]()
-    t2 = initCountTable[string]()
-  t1.inc("foo")
-  t1.inc("bar", 2)
-  t1.inc("baz", 3)
-  t2.inc("foo", 4)
-  t2.inc("bar")
-  t2.inc("baz", 11)
-  merge(t1, t2)
-  assert(t1["foo"] == 5)
-  assert(t1["bar"] == 3)
-  assert(t1["baz"] == 14)
+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:**
+  ##
+  ##   ```Nim
+  ##   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
+  ##   ```
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if t.data[h].val != 0:
+      yield (t.data[h].key, t.data[h].val)
+      assert(len(t) == L, "the length of the table changed while iterating over it")
 
-  let
-    t1r = newCountTable[string]()
-    t2r = newCountTable[string]()
-  t1r.inc("foo")
-  t1r.inc("bar", 2)
-  t1r.inc("baz", 3)
-  t2r.inc("foo", 4)
-  t2r.inc("bar")
-  t2r.inc("baz", 11)
-  merge(t1r, t2r)
-  assert(t1r["foo"] == 5)
-  assert(t1r["bar"] == 3)
-  assert(t1r["baz"] == 14)
+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")
+
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if t.data[h].val != 0:
+      yield (t.data[h].key, t.data[h].val)
+      assert(len(t) == L, "table modified while iterating over it")
 
-  var
-    t1l = initCountTable[string]()
-    t2l = initCountTable[string]()
-  t1l.inc("foo")
-  t1l.inc("bar", 2)
-  t1l.inc("baz", 3)
-  t2l.inc("foo", 4)
-  t2l.inc("bar")
-  t2l.inc("baz", 11)
-  let
-    t1merging = t1l
-    t2merging = t2l
-  let merged = merge(t1merging, t2merging)
-  assert(merged["foo"] == 5)
-  assert(merged["bar"] == 3)
-  assert(merged["baz"] == 14)
-
-  block:
-    const testKey = "TESTKEY"
-    let t: CountTableRef[string] = newCountTable[string]()
-
-    # Before, does not compile with error message:
-    #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)
-    t.inc(testKey, 3)
-    doAssert 3 == t.getOrDefault(testKey)
-
-  block:
-    # Clear tests
-    var clearTable = newTable[int, string]()
-    clearTable[42] = "asd"
-    clearTable[123123] = "piuyqwb "
-    doAssert clearTable[42] == "asd"
-    clearTable.clear()
-    doAssert(not clearTable.hasKey(123123))
-    doAssert clearTable.getOrDefault(42) == nil
-
-  block: #5482
-    var a = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable()
-    var b = newOrderedTable[string, string](initialSize=2)
-    b.add("wrong?", "foo")
-    b.add("wrong?", "foo2")
-    assert a == b
-
-  block: #5482
-    var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
-    var b = newOrderedTable[string, string](initialSize=2)
-    b.add("wrong?", "foo")
-    b.add("wrong?", "foo2")
-    assert a == b
-
-  block: #5487
-    var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
-    var b = newOrderedTable[string, string]() # notice, default size!
-    b.add("wrong?", "foo")
-    b.add("wrong?", "foo2")
-    assert a == b
-
-  block: #5487
-    var a = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable()
-    var b = newOrderedTable[string, string]()  # notice, default size!
-    b.add("wrong?", "foo")
-    b.add("wrong?", "foo2")
-    assert a == b
-
-  block:
-    var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
-    var b = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable()
-    var c = newOrderedTable[string, string]() # notice, default size!
-    c.add("wrong?", "foo")
-    c.add("wrong?", "foo2")
-    assert a == b
-    assert a == c
-
-  block: #6250
-    let
-      a = {3: 1}.toOrderedTable
-      b = {3: 2}.toOrderedTable
-    assert((a == b) == false)
-    assert((b == a) == false)
+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")
+
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if t.data[h].val != 0:
+      yield t.data[h].key
+      assert(len(t) == L, "the length of the table changed while iterating over it")
 
-  block: #6250
-    let
-      a = {3: 2}.toOrderedTable
-      b = {3: 2}.toOrderedTable
-    assert((a == b) == true)
-    assert((b == a) == true)
-
-  block: # CountTable.smallest
-    let t = toCountTable([0, 0, 5, 5, 5])
-    doAssert t.smallest == (0, 2)
-
-  block:
-    var tp: Table[string, string] = initTable[string, string]()
-    doAssert "test1" == tp.getOrDefault("test1", "test1")
-    tp["test2"] = "test2"
-    doAssert "test2" == tp.getOrDefault("test2", "test1")
-    var tr: TableRef[string, string] = newTable[string, string]()
-    doAssert "test1" == tr.getOrDefault("test1", "test1")
-    tr["test2"] = "test2"
-    doAssert "test2" == tr.getOrDefault("test2", "test1")
-    var op: OrderedTable[string, string] = initOrderedTable[string, string]()
-    doAssert "test1" == op.getOrDefault("test1", "test1")
-    op["test2"] = "test2"
-    doAssert "test2" == op.getOrDefault("test2", "test1")
-    var orf: OrderedTableRef[string, string] = newOrderedTable[string, string]()
-    doAssert "test1" == orf.getOrDefault("test1", "test1")
-    orf["test2"] = "test2"
-    doAssert "test2" == orf.getOrDefault("test2", "test1")
+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
+
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if t.data[h].val != 0:
+      yield t.data[h].val
+      assert(len(t) == L, "the length of the table changed while iterating over it")
+
+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")
+
+  let L = len(t)
+  for h in 0 .. high(t.data):
+    if t.data[h].val != 0:
+      yield t.data[h].val
+      assert(len(t) == L, "the length of the table changed while iterating over it")
+
+proc hash*[K,V](s: Table[K,V]): Hash =
+  for p in pairs(s):
+    result = result xor hash(p)
+  result = !$result
+
+proc hash*[K,V](s: OrderedTable[K,V]): Hash =
+  for p in pairs(s):
+    result = result !& hash(p)
+  result = !$result
+
+proc hash*[V](s: CountTable[V]): Hash =
+  for p in pairs(s):
+    result = result xor hash(p)
+  result = !$result
diff --git a/lib/pure/colors.nim b/lib/pure/colors.nim
index 843f29a63..d3e6dc063 100644
--- a/lib/pure/colors.nim
+++ b/lib/pure/colors.nim
@@ -6,17 +6,26 @@
 #    distribution, for details about the copyright.
 #
 
-## This module implements color handling for Nim. It is used by
-## the ``graphics`` module.
+## This module implements color handling for Nim,
+## namely color mixing and parsing the CSS color names.
 
-import strutils
-from algorithm import binarySearch
+import std/strutils
+from std/algorithm import binarySearch
 
 type
-  Color* = distinct int ## a color stored as RGB
+  Color* = distinct int ## A color stored as RGB, e.g. `0xff00cc`.
 
-proc `==` *(a, b: Color): bool {.borrow.}
-  ## compares two colors.
+proc `==`*(a, b: Color): bool {.borrow.}
+  ## Compares two colors.
+  ##
+  ##   ```Nim
+  ##   var
+  ##     a = Color(0xff_00_ff)
+  ##     b = colFuchsia
+  ##     c = Color(0x00_ff_cc)
+  ##   assert a == b
+  ##   assert not (a == c)
+  ##   ```
 
 template extract(a: Color, r, g, b: untyped) =
   var r = a.int shr 16 and 0xff
@@ -40,24 +49,64 @@ proc satMinus(a, b: int): int {.inline.} =
   if result < 0: result = 0
 
 proc `+`*(a, b: Color): Color =
-  ## adds two colors: This uses saturated artithmetic, so that each color
+  ## Adds two colors.
+  ##
+  ## This uses saturated arithmetic, so that each color
   ## component cannot overflow (255 is used as a maximum).
+  ##
+  runnableExamples:
+    var
+      a = Color(0xaa_00_ff)
+      b = Color(0x11_cc_cc)
+    assert a + b == Color(0xbb_cc_ff)
+
   colorOp(satPlus)
 
 proc `-`*(a, b: Color): Color =
-  ## subtracts two colors: This uses saturated artithmetic, so that each color
-  ## component cannot overflow (255 is used as a maximum).
+  ## Subtracts two colors.
+  ##
+  ## This uses saturated arithmetic, so that each color
+  ## component cannot underflow (0 is used as a minimum).
+  ##
+  runnableExamples:
+    var
+      a = Color(0xff_33_ff)
+      b = Color(0x11_ff_cc)
+    assert a - b == Color(0xee_00_33)
+
   colorOp(satMinus)
 
 proc extractRGB*(a: Color): tuple[r, g, b: range[0..255]] =
-  ## extracts the red/green/blue components of the color `a`.
+  ## Extracts the red/green/blue components of the color `a`.
+  ##
+  runnableExamples:
+    var
+      a = Color(0xff_00_ff)
+      b = Color(0x00_ff_cc)
+    type
+      Col = range[0..255]
+    # assert extractRGB(a) == (r: 255.Col, g: 0.Col, b: 255.Col)
+    # assert extractRGB(b) == (r: 0.Col, g: 255.Col, b: 204.Col)
+    echo extractRGB(a)
+    echo typeof(extractRGB(a))
+    echo extractRGB(b)
+    echo typeof(extractRGB(b))
+
   result.r = a.int shr 16 and 0xff
   result.g = a.int shr 8 and 0xff
   result.b = a.int and 0xff
 
 proc intensity*(a: Color, f: float): Color =
-  ## returns `a` with intensity `f`. `f` should be a float from 0.0 (completely
+  ## Returns `a` with intensity `f`. `f` should be a float from 0.0 (completely
   ## dark) to 1.0 (full color intensity).
+  ##
+  runnableExamples:
+    var
+      a = Color(0xff_00_ff)
+      b = Color(0x00_42_cc)
+    assert a.intensity(0.5) == Color(0x80_00_80)
+    assert b.intensity(0.5) == Color(0x00_21_66)
+
   var r = toInt(toFloat(a.int shr 16 and 0xff) * f)
   var g = toInt(toFloat(a.int shr 8 and 0xff) * f)
   var b = toInt(toFloat(a.int and 0xff) * f)
@@ -67,10 +116,22 @@ proc intensity*(a: Color, f: float): Color =
   result = rawRGB(r, g, b)
 
 template mix*(a, b: Color, fn: untyped): untyped =
-  ## uses `fn` to mix the colors `a` and `b`. `fn` is invoked for each component
-  ## R, G, and B. This is a template because `fn` should be inlined and the
-  ## compiler cannot inline proc pointers yet. If `fn`'s result is not in the
-  ## range[0..255], it will be saturated to be so.
+  ## Uses `fn` to mix the colors `a` and `b`.
+  ##
+  ## `fn` is invoked for each component R, G, and B.
+  ## If `fn`'s result is not in the `range[0..255]`,
+  ## it will be saturated to be so.
+  ##
+  runnableExamples:
+    var
+      a = Color(0x0a2814)
+      b = Color(0x050a03)
+
+    proc myMix(x, y: int): int =
+      2 * x - 3 * y
+
+    assert mix(a, b, myMix) == Color(0x05_32_1f)
+
   template `><` (x: untyped): untyped =
     # keep it in the range 0..255
     block:
@@ -79,9 +140,9 @@ template mix*(a, b: Color, fn: untyped): untyped =
         y = if y < 0: 0 else: 255
       y
 
-  (bind extract)(a, ar, ag, ab)
-  (bind extract)(b, br, bg, bb)
-  (bind rawRGB)(><fn(ar, br), ><fn(ag, bg), ><fn(ab, bb))
+  extract(a, ar, ag, ab)
+  extract(b, br, bg, bb)
+  rawRGB(><fn(ar, br), ><fn(ag, bg), ><fn(ab, bb))
 
 
 const
@@ -111,6 +172,7 @@ const
   colDarkGoldenRod* = Color(0xB8860B)
   colDarkGray* = Color(0xA9A9A9)
   colDarkGreen* = Color(0x006400)
+  colDarkGrey* = Color(0xA9A9A9)
   colDarkKhaki* = Color(0xBDB76B)
   colDarkMagenta* = Color(0x8B008B)
   colDarkOliveGreen* = Color(0x556B2F)
@@ -121,11 +183,13 @@ const
   colDarkSeaGreen* = Color(0x8FBC8F)
   colDarkSlateBlue* = Color(0x483D8B)
   colDarkSlateGray* = Color(0x2F4F4F)
+  colDarkSlateGrey* = Color(0x2F4F4F)
   colDarkTurquoise* = Color(0x00CED1)
   colDarkViolet* = Color(0x9400D3)
   colDeepPink* = Color(0xFF1493)
   colDeepSkyBlue* = Color(0x00BFFF)
   colDimGray* = Color(0x696969)
+  colDimGrey* = Color(0x696969)
   colDodgerBlue* = Color(0x1E90FF)
   colFireBrick* = Color(0xB22222)
   colFloralWhite* = Color(0xFFFAF0)
@@ -138,6 +202,7 @@ const
   colGray* = Color(0x808080)
   colGreen* = Color(0x008000)
   colGreenYellow* = Color(0xADFF2F)
+  colGrey* = Color(0x808080)
   colHoneyDew* = Color(0xF0FFF0)
   colHotPink* = Color(0xFF69B4)
   colIndianRed* = Color(0xCD5C5C)
@@ -152,13 +217,15 @@ const
   colLightCoral* = Color(0xF08080)
   colLightCyan* = Color(0xE0FFFF)
   colLightGoldenRodYellow* = Color(0xFAFAD2)
-  colLightGrey* = Color(0xD3D3D3)
+  colLightGray* = Color(0xD3D3D3)
   colLightGreen* = Color(0x90EE90)
+  colLightGrey* = Color(0xD3D3D3)
   colLightPink* = Color(0xFFB6C1)
   colLightSalmon* = Color(0xFFA07A)
   colLightSeaGreen* = Color(0x20B2AA)
   colLightSkyBlue* = Color(0x87CEFA)
   colLightSlateGray* = Color(0x778899)
+  colLightSlateGrey* = Color(0x778899)
   colLightSteelBlue* = Color(0xB0C4DE)
   colLightYellow* = Color(0xFFFFE0)
   colLime* = Color(0x00FF00)
@@ -169,7 +236,7 @@ const
   colMediumAquaMarine* = Color(0x66CDAA)
   colMediumBlue* = Color(0x0000CD)
   colMediumOrchid* = Color(0xBA55D3)
-  colMediumPurple* = Color(0x9370D8)
+  colMediumPurple* = Color(0x9370DB)
   colMediumSeaGreen* = Color(0x3CB371)
   colMediumSlateBlue* = Color(0x7B68EE)
   colMediumSpringGreen* = Color(0x00FA9A)
@@ -190,7 +257,7 @@ const
   colPaleGoldenRod* = Color(0xEEE8AA)
   colPaleGreen* = Color(0x98FB98)
   colPaleTurquoise* = Color(0xAFEEEE)
-  colPaleVioletRed* = Color(0xD87093)
+  colPaleVioletRed* = Color(0xDB7093)
   colPapayaWhip* = Color(0xFFEFD5)
   colPeachPuff* = Color(0xFFDAB9)
   colPeru* = Color(0xCD853F)
@@ -198,6 +265,7 @@ const
   colPlum* = Color(0xDDA0DD)
   colPowderBlue* = Color(0xB0E0E6)
   colPurple* = Color(0x800080)
+  colRebeccaPurple* = Color(0x663399)
   colRed* = Color(0xFF0000)
   colRosyBrown* = Color(0xBC8F8F)
   colRoyalBlue* = Color(0x4169E1)
@@ -211,6 +279,7 @@ const
   colSkyBlue* = Color(0x87CEEB)
   colSlateBlue* = Color(0x6A5ACD)
   colSlateGray* = Color(0x708090)
+  colSlateGrey* = Color(0x708090)
   colSnow* = Color(0xFFFAFA)
   colSpringGreen* = Color(0x00FF7F)
   colSteelBlue* = Color(0x4682B4)
@@ -226,159 +295,182 @@ const
   colYellow* = Color(0xFFFF00)
   colYellowGreen* = Color(0x9ACD32)
 
-  colorNames = [
-    ("aliceblue", colAliceBlue),
-    ("antiquewhite", colAntiqueWhite),
-    ("aqua", colAqua),
-    ("aquamarine", colAquamarine),
-    ("azure", colAzure),
-    ("beige", colBeige),
-    ("bisque", colBisque),
-    ("black", colBlack),
-    ("blanchedalmond", colBlanchedAlmond),
-    ("blue", colBlue),
-    ("blueviolet", colBlueViolet),
-    ("brown", colBrown),
-    ("burlywood", colBurlyWood),
-    ("cadetblue", colCadetBlue),
-    ("chartreuse", colChartreuse),
-    ("chocolate", colChocolate),
-    ("coral", colCoral),
-    ("cornflowerblue", colCornflowerBlue),
-    ("cornsilk", colCornsilk),
-    ("crimson", colCrimson),
-    ("cyan", colCyan),
-    ("darkblue", colDarkBlue),
-    ("darkcyan", colDarkCyan),
-    ("darkgoldenrod", colDarkGoldenRod),
-    ("darkgray", colDarkGray),
-    ("darkgreen", colDarkGreen),
-    ("darkkhaki", colDarkKhaki),
-    ("darkmagenta", colDarkMagenta),
-    ("darkolivegreen", colDarkOliveGreen),
-    ("darkorange", colDarkorange),
-    ("darkorchid", colDarkOrchid),
-    ("darkred", colDarkRed),
-    ("darksalmon", colDarkSalmon),
-    ("darkseagreen", colDarkSeaGreen),
-    ("darkslateblue", colDarkSlateBlue),
-    ("darkslategray", colDarkSlateGray),
-    ("darkturquoise", colDarkTurquoise),
-    ("darkviolet", colDarkViolet),
-    ("deeppink", colDeepPink),
-    ("deepskyblue", colDeepSkyBlue),
-    ("dimgray", colDimGray),
-    ("dodgerblue", colDodgerBlue),
-    ("firebrick", colFireBrick),
-    ("floralwhite", colFloralWhite),
-    ("forestgreen", colForestGreen),
-    ("fuchsia", colFuchsia),
-    ("gainsboro", colGainsboro),
-    ("ghostwhite", colGhostWhite),
-    ("gold", colGold),
-    ("goldenrod", colGoldenRod),
-    ("gray", colGray),
-    ("green", colGreen),
-    ("greenyellow", colGreenYellow),
-    ("honeydew", colHoneyDew),
-    ("hotpink", colHotPink),
-    ("indianred", colIndianRed),
-    ("indigo", colIndigo),
-    ("ivory", colIvory),
-    ("khaki", colKhaki),
-    ("lavender", colLavender),
-    ("lavenderblush", colLavenderBlush),
-    ("lawngreen", colLawnGreen),
-    ("lemonchiffon", colLemonChiffon),
-    ("lightblue", colLightBlue),
-    ("lightcoral", colLightCoral),
-    ("lightcyan", colLightCyan),
-    ("lightgoldenrodyellow", colLightGoldenRodYellow),
-    ("lightgrey", colLightGrey),
-    ("lightgreen", colLightGreen),
-    ("lightpink", colLightPink),
-    ("lightsalmon", colLightSalmon),
-    ("lightseagreen", colLightSeaGreen),
-    ("lightskyblue", colLightSkyBlue),
-    ("lightslategray", colLightSlateGray),
-    ("lightsteelblue", colLightSteelBlue),
-    ("lightyellow", colLightYellow),
-    ("lime", colLime),
-    ("limegreen", colLimeGreen),
-    ("linen", colLinen),
-    ("magenta", colMagenta),
-    ("maroon", colMaroon),
-    ("mediumaquamarine", colMediumAquaMarine),
-    ("mediumblue", colMediumBlue),
-    ("mediumorchid", colMediumOrchid),
-    ("mediumpurple", colMediumPurple),
-    ("mediumseagreen", colMediumSeaGreen),
-    ("mediumslateblue", colMediumSlateBlue),
-    ("mediumspringgreen", colMediumSpringGreen),
-    ("mediumturquoise", colMediumTurquoise),
-    ("mediumvioletred", colMediumVioletRed),
-    ("midnightblue", colMidnightBlue),
-    ("mintcream", colMintCream),
-    ("mistyrose", colMistyRose),
-    ("moccasin", colMoccasin),
-    ("navajowhite", colNavajoWhite),
-    ("navy", colNavy),
-    ("oldlace", colOldLace),
-    ("olive", colOlive),
-    ("olivedrab", colOliveDrab),
-    ("orange", colOrange),
-    ("orangered", colOrangeRed),
-    ("orchid", colOrchid),
-    ("palegoldenrod", colPaleGoldenRod),
-    ("palegreen", colPaleGreen),
-    ("paleturquoise", colPaleTurquoise),
-    ("palevioletred", colPaleVioletRed),
-    ("papayawhip", colPapayaWhip),
-    ("peachpuff", colPeachPuff),
-    ("peru", colPeru),
-    ("pink", colPink),
-    ("plum", colPlum),
-    ("powderblue", colPowderBlue),
-    ("purple", colPurple),
-    ("red", colRed),
-    ("rosybrown", colRosyBrown),
-    ("royalblue", colRoyalBlue),
-    ("saddlebrown", colSaddleBrown),
-    ("salmon", colSalmon),
-    ("sandybrown", colSandyBrown),
-    ("seagreen", colSeaGreen),
-    ("seashell", colSeaShell),
-    ("sienna", colSienna),
-    ("silver", colSilver),
-    ("skyblue", colSkyBlue),
-    ("slateblue", colSlateBlue),
-    ("slategray", colSlateGray),
-    ("snow", colSnow),
-    ("springgreen", colSpringGreen),
-    ("steelblue", colSteelBlue),
-    ("tan", colTan),
-    ("teal", colTeal),
-    ("thistle", colThistle),
-    ("tomato", colTomato),
-    ("turquoise", colTurquoise),
-    ("violet", colViolet),
-    ("wheat", colWheat),
-    ("white", colWhite),
-    ("whitesmoke", colWhiteSmoke),
-    ("yellow", colYellow),
-    ("yellowgreen", colYellowGreen)]
+  colorNames = {
+    "aliceblue": colAliceBlue,
+    "antiquewhite": colAntiqueWhite,
+    "aqua": colAqua,
+    "aquamarine": colAquamarine,
+    "azure": colAzure,
+    "beige": colBeige,
+    "bisque": colBisque,
+    "black": colBlack,
+    "blanchedalmond": colBlanchedAlmond,
+    "blue": colBlue,
+    "blueviolet": colBlueViolet,
+    "brown": colBrown,
+    "burlywood": colBurlyWood,
+    "cadetblue": colCadetBlue,
+    "chartreuse": colChartreuse,
+    "chocolate": colChocolate,
+    "coral": colCoral,
+    "cornflowerblue": colCornflowerBlue,
+    "cornsilk": colCornsilk,
+    "crimson": colCrimson,
+    "cyan": colCyan,
+    "darkblue": colDarkBlue,
+    "darkcyan": colDarkCyan,
+    "darkgoldenrod": colDarkGoldenRod,
+    "darkgray": colDarkGray,
+    "darkgreen": colDarkGreen,
+    "darkgrey": colDarkGrey,
+    "darkkhaki": colDarkKhaki,
+    "darkmagenta": colDarkMagenta,
+    "darkolivegreen": colDarkOliveGreen,
+    "darkorange": colDarkorange,
+    "darkorchid": colDarkOrchid,
+    "darkred": colDarkRed,
+    "darksalmon": colDarkSalmon,
+    "darkseagreen": colDarkSeaGreen,
+    "darkslateblue": colDarkSlateBlue,
+    "darkslategray": colDarkSlateGray,
+    "darkslategrey": colDarkSlateGrey,
+    "darkturquoise": colDarkTurquoise,
+    "darkviolet": colDarkViolet,
+    "deeppink": colDeepPink,
+    "deepskyblue": colDeepSkyBlue,
+    "dimgray": colDimGray,
+    "dimgrey": colDimGrey,
+    "dodgerblue": colDodgerBlue,
+    "firebrick": colFireBrick,
+    "floralwhite": colFloralWhite,
+    "forestgreen": colForestGreen,
+    "fuchsia": colFuchsia,
+    "gainsboro": colGainsboro,
+    "ghostwhite": colGhostWhite,
+    "gold": colGold,
+    "goldenrod": colGoldenRod,
+    "gray": colGray,
+    "green": colGreen,
+    "greenyellow": colGreenYellow,
+    "grey": colGrey,
+    "honeydew": colHoneyDew,
+    "hotpink": colHotPink,
+    "indianred": colIndianRed,
+    "indigo": colIndigo,
+    "ivory": colIvory,
+    "khaki": colKhaki,
+    "lavender": colLavender,
+    "lavenderblush": colLavenderBlush,
+    "lawngreen": colLawnGreen,
+    "lemonchiffon": colLemonChiffon,
+    "lightblue": colLightBlue,
+    "lightcoral": colLightCoral,
+    "lightcyan": colLightCyan,
+    "lightgoldenrodyellow": colLightGoldenRodYellow,
+    "lightgray": colLightGray,
+    "lightgreen": colLightGreen,
+    "lightgrey": colLightGrey,
+    "lightpink": colLightPink,
+    "lightsalmon": colLightSalmon,
+    "lightseagreen": colLightSeaGreen,
+    "lightskyblue": colLightSkyBlue,
+    "lightslategray": colLightSlateGray,
+    "lightslategrey": colLightSlateGrey,
+    "lightsteelblue": colLightSteelBlue,
+    "lightyellow": colLightYellow,
+    "lime": colLime,
+    "limegreen": colLimeGreen,
+    "linen": colLinen,
+    "magenta": colMagenta,
+    "maroon": colMaroon,
+    "mediumaquamarine": colMediumAquaMarine,
+    "mediumblue": colMediumBlue,
+    "mediumorchid": colMediumOrchid,
+    "mediumpurple": colMediumPurple,
+    "mediumseagreen": colMediumSeaGreen,
+    "mediumslateblue": colMediumSlateBlue,
+    "mediumspringgreen": colMediumSpringGreen,
+    "mediumturquoise": colMediumTurquoise,
+    "mediumvioletred": colMediumVioletRed,
+    "midnightblue": colMidnightBlue,
+    "mintcream": colMintCream,
+    "mistyrose": colMistyRose,
+    "moccasin": colMoccasin,
+    "navajowhite": colNavajoWhite,
+    "navy": colNavy,
+    "oldlace": colOldLace,
+    "olive": colOlive,
+    "olivedrab": colOliveDrab,
+    "orange": colOrange,
+    "orangered": colOrangeRed,
+    "orchid": colOrchid,
+    "palegoldenrod": colPaleGoldenRod,
+    "palegreen": colPaleGreen,
+    "paleturquoise": colPaleTurquoise,
+    "palevioletred": colPaleVioletRed,
+    "papayawhip": colPapayaWhip,
+    "peachpuff": colPeachPuff,
+    "peru": colPeru,
+    "pink": colPink,
+    "plum": colPlum,
+    "powderblue": colPowderBlue,
+    "purple": colPurple,
+    "rebeccapurple": colRebeccaPurple,
+    "red": colRed,
+    "rosybrown": colRosyBrown,
+    "royalblue": colRoyalBlue,
+    "saddlebrown": colSaddleBrown,
+    "salmon": colSalmon,
+    "sandybrown": colSandyBrown,
+    "seagreen": colSeaGreen,
+    "seashell": colSeaShell,
+    "sienna": colSienna,
+    "silver": colSilver,
+    "skyblue": colSkyBlue,
+    "slateblue": colSlateBlue,
+    "slategray": colSlateGray,
+    "slategrey": colSlateGrey,
+    "snow": colSnow,
+    "springgreen": colSpringGreen,
+    "steelblue": colSteelBlue,
+    "tan": colTan,
+    "teal": colTeal,
+    "thistle": colThistle,
+    "tomato": colTomato,
+    "turquoise": colTurquoise,
+    "violet": colViolet,
+    "wheat": colWheat,
+    "white": colWhite,
+    "whitesmoke": colWhiteSmoke,
+    "yellow": colYellow,
+    "yellowgreen": colYellowGreen}
 
 proc `$`*(c: Color): string =
-  ## converts a color into its textual representation. Example: ``#00FF00``.
+  ## Converts a color into its textual representation.
+  ##
+  runnableExamples:
+    assert $colFuchsia == "#FF00FF"
   result = '#' & toHex(int(c), 6)
 
 proc colorNameCmp(x: tuple[name: string, col: Color], y: string): int =
   result = cmpIgnoreCase(x.name, y)
 
 proc parseColor*(name: string): Color =
-  ## parses `name` to a color value. If no valid color could be
-  ## parsed ``EInvalidValue`` is raised. Case insensitive.
-  if name[0] == '#':
+  ## Parses `name` to a color value.
+  ##
+  ## If no valid color could be parsed `ValueError` is raised.
+  ## Case insensitive.
+  ##
+  runnableExamples:
+    var
+      a = "silver"
+      b = "#0179fc"
+      c = "#zzmmtt"
+    assert parseColor(a) == Color(0xc0_c0_c0)
+    assert parseColor(b) == Color(0x01_79_fc)
+    doAssertRaises(ValueError): discard parseColor(c)
+
+  if name.len > 0 and name[0] == '#':
     result = Color(parseHexInt(name))
   else:
     var idx = binarySearch(colorNames, name, colorNameCmp)
@@ -386,16 +478,30 @@ proc parseColor*(name: string): Color =
     result = colorNames[idx][1]
 
 proc isColor*(name: string): bool =
-  ## returns true if `name` is a known color name or a hexadecimal color
-  ## prefixed with ``#``. Case insensitive.
+  ## Returns true if `name` is a known color name or a hexadecimal color
+  ## prefixed with `#`. Case insensitive.
+  ##
+  runnableExamples:
+    var
+      a = "silver"
+      b = "#0179fc"
+      c = "#zzmmtt"
+    assert a.isColor
+    assert b.isColor
+    assert not c.isColor
+
+  if name.len == 0: return false
   if name[0] == '#':
     for i in 1 .. name.len-1:
-      if name[i] notin {'0'..'9', 'a'..'f', 'A'..'F'}: return false
+      if name[i] notin HexDigits: return false
     result = true
   else:
     result = binarySearch(colorNames, name, colorNameCmp) >= 0
 
 proc rgb*(r, g, b: range[0..255]): Color =
-  ## constructs a color from RGB values.
-  result = rawRGB(r, g, b)
+  ## Constructs a color from RGB values.
+  ##
+  runnableExamples:
+    assert rgb(0, 255, 128) == Color(0x00_ff_80)
 
+  result = rawRGB(r, g, b)
diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim
index ba5c571ce..b48811eae 100644
--- a/lib/pure/complex.nim
+++ b/lib/pure/complex.nim
@@ -7,186 +7,187 @@
 #    distribution, for details about the copyright.
 #
 
+## This module implements complex numbers
+## and basic mathematical operations on them.
+##
+## Complex numbers are currently generic over 64-bit or 32-bit floats.
 
+runnableExamples:
+  from std/math import almostEqual, sqrt
 
-## This module implements complex numbers.
-{.push checks:off, line_dir:off, stack_trace:off, debugger:off.}
-# the user does not want to trace a part
-# of the standard library!
+  let
+    z1 = complex(1.0, 2.0)
+    z2 = complex(3.0, -4.0)
 
+  assert almostEqual(z1 + z2, complex(4.0, -2.0))
+  assert almostEqual(z1 - z2, complex(-2.0, 6.0))
+  assert almostEqual(z1 * z2, complex(11.0, 2.0))
+  assert almostEqual(z1 / z2, complex(-0.2, 0.4))
 
-import
-  math
+  assert almostEqual(abs(z1), sqrt(5.0))
+  assert almostEqual(conjugate(z1), complex(1.0, -2.0))
 
-const
-  EPS = 1.0e-7 ## Epsilon used for float comparisons.
+  let (r, phi) = z1.polar
+  assert almostEqual(rect(r, phi), z1)
 
-type
-  Complex* = tuple[re, im: float]
-    ## a complex number, consisting of a real and an imaginary part
+{.push checks: off, line_dir: off, stack_trace: off, debugger: off.}
+# the user does not want to trace a part of the standard library!
 
-const
-  im*: Complex = (re: 0.0, im: 1.0)
-    ## The imaginary unit. √-1.
+import std/[math, strformat]
 
-proc toComplex*(x: SomeInteger): Complex =
-  ## Convert some integer ``x`` to a complex number.
-  result.re = x
-  result.im = 0
+type
+  Complex*[T: SomeFloat] = object
+    ## A complex number, consisting of a real and an imaginary part.
+    re*, im*: T
+  Complex64* = Complex[float64]
+    ## Alias for a complex number using 64-bit floats.
+  Complex32* = Complex[float32]
+    ## Alias for a complex number using 32-bit floats.
+
+func complex*[T: SomeFloat](re: T; im: T = 0.0): Complex[T] =
+  ## Returns a `Complex[T]` with real part `re` and imaginary part `im`.
+  result.re = re
+  result.im = im
+
+func complex32*(re: float32; im: float32 = 0.0): Complex32 =
+  ## Returns a `Complex32` with real part `re` and imaginary part `im`.
+  result.re = re
+  result.im = im
+
+func complex64*(re: float64; im: float64 = 0.0): Complex64 =
+  ## Returns a `Complex64` with real part `re` and imaginary part `im`.
+  result.re = re
+  result.im = im
+
+template im*(arg: typedesc[float32]): Complex32 = complex32(0, 1)
+  ## Returns the imaginary unit (`complex32(0, 1)`).
+template im*(arg: typedesc[float64]): Complex64 = complex64(0, 1)
+  ## Returns the imaginary unit (`complex64(0, 1)`).
+template im*(arg: float32): Complex32 = complex32(0, arg)
+  ## Returns `arg` as an imaginary number (`complex32(0, arg)`).
+template im*(arg: float64): Complex64 = complex64(0, arg)
+  ## Returns `arg` as an imaginary number (`complex64(0, arg)`).
+
+func abs*[T](z: Complex[T]): T =
+  ## Returns the absolute value of `z`,
+  ## that is the distance from (0, 0) to `z`.
+  result = hypot(z.re, z.im)
+
+func abs2*[T](z: Complex[T]): T =
+  ## Returns the squared absolute value of `z`,
+  ## that is the squared distance from (0, 0) to `z`.
+  ## This is more efficient than `abs(z) ^ 2`.
+  result = z.re * z.re + z.im * z.im
+
+func sgn*[T](z: Complex[T]): Complex[T] =
+  ## Returns the phase of `z` as a unit complex number,
+  ## or 0 if `z` is 0.
+  let a = abs(z)
+  if a != 0:
+    result = z / a
+
+func conjugate*[T](z: Complex[T]): Complex[T] =
+  ## Returns the complex conjugate of `z` (`complex(z.re, -z.im)`).
+  result.re = z.re
+  result.im = -z.im
 
-proc `==` *(x, y: Complex): bool =
-  ## Compare two complex numbers `x` and `y` for equality.
-  result = x.re == y.re and x.im == y.im
+func inv*[T](z: Complex[T]): Complex[T] =
+  ## Returns the multiplicative inverse of `z` (`1/z`).
+  conjugate(z) / abs2(z)
 
-proc `=~` *(x, y: Complex): bool =
-  ## Compare two complex numbers `x` and `y` approximately.
-  result = abs(x.re-y.re)<EPS and abs(x.im-y.im)<EPS
+func `==`*[T](x, y: Complex[T]): bool =
+  ## Compares two complex numbers for equality.
+  result = x.re == y.re and x.im == y.im
 
-proc `+` *(x, y: Complex): Complex =
-  ## Add two complex numbers.
-  result.re = x.re + y.re
-  result.im = x.im + y.im
+func `+`*[T](x: T; y: Complex[T]): Complex[T] =
+  ## Adds a real number to a complex number.
+  result.re = x + y.re
+  result.im = y.im
 
-proc `+` *(x: Complex, y: float): Complex =
-  ## Add complex `x` to float `y`.
+func `+`*[T](x: Complex[T]; y: T): Complex[T] =
+  ## Adds a complex number to a real number.
   result.re = x.re + y
   result.im = x.im
 
-proc `+` *(x: float, y: Complex): Complex =
-  ## Add float `x` to complex `y`.
-  result.re = x + y.re
-  result.im = y.im
-
+func `+`*[T](x, y: Complex[T]): Complex[T] =
+  ## Adds two complex numbers.
+  result.re = x.re + y.re
+  result.im = x.im + y.im
 
-proc `-` *(z: Complex): Complex =
+func `-`*[T](z: Complex[T]): Complex[T] =
   ## Unary minus for complex numbers.
   result.re = -z.re
   result.im = -z.im
 
-proc `-` *(x, y: Complex): Complex =
-  ## Subtract two complex numbers.
-  result.re = x.re - y.re
-  result.im = x.im - y.im
-
-proc `-` *(x: Complex, y: float): Complex =
-  ## Subtracts float `y` from complex `x`.
-  result = x + (-y)
+func `-`*[T](x: T; y: Complex[T]): Complex[T] =
+  ## Subtracts a complex number from a real number.
+  result.re = x - y.re
+  result.im = -y.im
 
-proc `-` *(x: float, y: Complex): Complex =
-  ## Subtracts complex `y` from float `x`.
-  result = x + (-y)
-
-
-proc `/` *(x, y: Complex): Complex =
-  ## Divide `x` by `y`.
-  var
-    r, den: float
-  if abs(y.re) < abs(y.im):
-    r = y.re / y.im
-    den = y.im + r * y.re
-    result.re = (x.re * r + x.im) / den
-    result.im = (x.im * r - x.re) / den
-  else:
-    r = y.im / y.re
-    den = y.re + r * y.im
-    result.re = (x.re + r * x.im) / den
-    result.im = (x.im - r * x.re) / den
+func `-`*[T](x: Complex[T]; y: T): Complex[T] =
+  ## Subtracts a real number from a complex number.
+  result.re = x.re - y
+  result.im = x.im
 
-proc `/` *(x : Complex, y: float ): Complex =
-  ## Divide complex `x` by float `y`.
-  result.re = x.re/y
-  result.im = x.im/y
+func `-`*[T](x, y: Complex[T]): Complex[T] =
+  ## Subtracts two complex numbers.
+  result.re = x.re - y.re
+  result.im = x.im - y.im
 
-proc `/` *(x : float, y: Complex ): Complex =
-  ## Divide float `x` by complex `y`.
-  var num : Complex = (x, 0.0)
-  result = num/y
+func `*`*[T](x: T; y: Complex[T]): Complex[T] =
+  ## Multiplies a real number with a complex number.
+  result.re = x * y.re
+  result.im = x * y.im
 
+func `*`*[T](x: Complex[T]; y: T): Complex[T] =
+  ## Multiplies a complex number with a real number.
+  result.re = x.re * y
+  result.im = x.im * y
 
-proc `*` *(x, y: Complex): Complex =
-  ## Multiply `x` with `y`.
+func `*`*[T](x, y: Complex[T]): Complex[T] =
+  ## Multiplies two complex numbers.
   result.re = x.re * y.re - x.im * y.im
   result.im = x.im * y.re + x.re * y.im
 
-proc `*` *(x: float, y: Complex): Complex =
-  ## Multiply float `x` with complex `y`.
-  result.re = x * y.re
-  result.im = x * y.im
+func `/`*[T](x: Complex[T]; y: T): Complex[T] =
+  ## Divides a complex number by a real number.
+  result.re = x.re / y
+  result.im = x.im / y
 
-proc `*` *(x: Complex, y: float): Complex =
-  ## Multiply complex `x` with float `y`.
-  result.re = x.re * y
-  result.im = x.im * y
+func `/`*[T](x: T; y: Complex[T]): Complex[T] =
+  ## Divides a real number by a complex number.
+  result = x * inv(y)
 
+func `/`*[T](x, y: Complex[T]): Complex[T] =
+  ## Divides two complex numbers.
+  x * conjugate(y) / abs2(y)
 
-proc `+=` *(x: var Complex, y: Complex) =
-  ## Add `y` to `x`.
+func `+=`*[T](x: var Complex[T]; y: Complex[T]) =
+  ## Adds `y` to `x`.
   x.re += y.re
   x.im += y.im
 
-proc `+=` *(x: var Complex, y: float) =
-  ## Add `y` to the complex number `x`.
-  x.re += y
-
-proc `-=` *(x: var Complex, y: Complex) =
-  ## Subtract `y` from `x`.
+func `-=`*[T](x: var Complex[T]; y: Complex[T]) =
+  ## Subtracts `y` from `x`.
   x.re -= y.re
   x.im -= y.im
 
-proc `-=` *(x: var Complex, y: float) =
-  ## Subtract `y` from the complex number `x`.
-  x.re -= y
-
-proc `*=` *(x: var Complex, y: Complex) =
-  ## Multiply `y` to `x`.
+func `*=`*[T](x: var Complex[T]; y: Complex[T]) =
+  ## Multiplies `x` by `y`.
   let im = x.im * y.re + x.re * y.im
   x.re = x.re * y.re - x.im * y.im
   x.im = im
 
-proc `*=` *(x: var Complex, y: float) =
-  ## Multiply `y` to the complex number `x`.
-  x.re *= y
-  x.im *= y
-
-proc `/=` *(x: var Complex, y: Complex) =
-  ## Divide `x` by `y` in place.
+func `/=`*[T](x: var Complex[T]; y: Complex[T]) =
+  ## Divides `x` by `y` in place.
   x = x / y
 
-proc `/=` *(x : var Complex, y: float) =
-  ## Divide complex `x` by float `y` in place.
-  x.re /= y
-  x.im /= y
-
 
-proc abs*(z: Complex): float =
-  ## Return the distance from (0,0) to `z`.
-
-  # optimized by checking special cases (sqrt is expensive)
-  var x, y, temp: float
-
-  x = abs(z.re)
-  y = abs(z.im)
-  if x == 0.0:
-    result = y
-  elif y == 0.0:
-    result = x
-  elif x > y:
-    temp = y / x
-    result = x * sqrt(1.0 + temp * temp)
-  else:
-    temp = x / y
-    result = y * sqrt(1.0 + temp * temp)
-
-
-proc conjugate*(z: Complex): Complex =
-  ## Conjugate of complex number `z`.
-  result.re = z.re
-  result.im = -z.im
-
-
-proc sqrt*(z: Complex): Complex =
-  ## Square root for a complex number `z`.
-  var x, y, w, r: float
+func sqrt*[T](z: Complex[T]): Complex[T] =
+  ## Computes the
+  ## ([principal](https://en.wikipedia.org/wiki/Square_root#Principal_square_root_of_a_complex_number))
+  ## square root of a complex number `z`.
+  var x, y, w, r: T
 
   if z.re == 0.0 and z.im == 0.0:
     result = z
@@ -199,247 +200,274 @@ proc sqrt*(z: Complex): Complex =
     else:
       r = x / y
       w = sqrt(y) * sqrt(0.5 * (r + sqrt(1.0 + r * r)))
+
     if z.re >= 0.0:
       result.re = w
       result.im = z.im / (w * 2.0)
     else:
-      if z.im >= 0.0: result.im = w
-      else:           result.im = -w
+      result.im = if z.im >= 0.0: w else: -w
       result.re = z.im / (result.im + result.im)
 
-
-proc exp*(z: Complex): Complex =
-  ## e raised to the power `z`.
-  var rho   = exp(z.re)
-  var theta = z.im
-  result.re = rho*cos(theta)
-  result.im = rho*sin(theta)
-
-
-proc ln*(z: Complex): Complex =
-  ## Returns the natural log of `z`.
+func exp*[T](z: Complex[T]): Complex[T] =
+  ## Computes the exponential function (`e^z`).
+  let
+    rho = exp(z.re)
+    theta = z.im
+  result.re = rho * cos(theta)
+  result.im = rho * sin(theta)
+
+func ln*[T](z: Complex[T]): Complex[T] =
+  ## Returns the
+  ## ([principal value](https://en.wikipedia.org/wiki/Complex_logarithm#Principal_value)
+  ## of the) natural logarithm of `z`.
   result.re = ln(abs(z))
-  result.im = arctan2(z.im,z.re)
-
-proc log10*(z: Complex): Complex =
-  ## Returns the log base 10 of `z`.
-  result = ln(z)/ln(10.0)
-
-proc log2*(z: Complex): Complex =
-  ## Returns the log base 2 of `z`.
-  result = ln(z)/ln(2.0)
-
-
-proc pow*(x, y: Complex): Complex =
-  ## `x` raised to the power `y`.
-  if x.re == 0.0  and  x.im == 0.0:
-    if y.re == 0.0  and  y.im == 0.0:
+  result.im = arctan2(z.im, z.re)
+
+func log10*[T](z: Complex[T]): Complex[T] =
+  ## Returns the logarithm base 10 of `z`.
+  ##
+  ## **See also:**
+  ## * `ln func<#ln,Complex[T]>`_
+  result = ln(z) / ln(10.0)
+
+func log2*[T](z: Complex[T]): Complex[T] =
+  ## Returns the logarithm base 2 of `z`.
+  ##
+  ## **See also:**
+  ## * `ln func<#ln,Complex[T]>`_
+  result = ln(z) / ln(2.0)
+
+func pow*[T](x, y: Complex[T]): Complex[T] =
+  ## `x` raised to the power of `y`.
+  if x.re == 0.0 and x.im == 0.0:
+    if y.re == 0.0 and y.im == 0.0:
       result.re = 1.0
       result.im = 0.0
     else:
       result.re = 0.0
       result.im = 0.0
-  elif y.re == 1.0  and  y.im == 0.0:
-    result = x
-  elif y.re == -1.0  and  y.im == 0.0:
-    result = 1.0/x
+  elif y.im == 0.0:
+    if y.re == 1.0:
+      result = x
+    elif y.re == -1.0:
+      result = T(1.0) / x
+    elif y.re == 2.0:
+      result = x * x
+    elif y.re == 0.5:
+      result = sqrt(x)
+    elif x.im == 0.0:
+      # Revert to real pow when both base and exponent are real
+      result.re = pow(x.re, y.re)
+      result.im = 0.0
+    else:
+      # Special case when the exponent is real
+      let
+        rho = abs(x)
+        theta = arctan2(x.im, x.re)
+        s = pow(rho, y.re)
+        r = y.re * theta
+      result.re = s * cos(r)
+      result.im = s * sin(r)
+  elif x.im == 0.0 and x.re == E:
+   # Special case Euler's formula
+   result = exp(y)
   else:
-    var rho   = sqrt(x.re*x.re + x.im*x.im)
-    var theta = arctan2(x.im,x.re)
-    var s     = pow(rho,y.re) * exp(-y.im*theta)
-    var r     = y.re*theta + y.im*ln(rho)
-    result.re = s*cos(r)
-    result.im = s*sin(r)
+    let
+      rho = abs(x)
+      theta = arctan2(x.im, x.re)
+      s = pow(rho, y.re) * exp(-y.im * theta)
+      r = y.re * theta + y.im * ln(rho)
+    result.re = s * cos(r)
+    result.im = s * sin(r)
+
+func pow*[T](x: Complex[T]; y: T): Complex[T] =
+  ## The complex number `x` raised to the power of the real number `y`.
+  pow(x, complex[T](y))
 
 
-proc sin*(z: Complex): Complex =
+func sin*[T](z: Complex[T]): Complex[T] =
   ## Returns the sine of `z`.
-  result.re = sin(z.re)*cosh(z.im)
-  result.im = cos(z.re)*sinh(z.im)
+  result.re = sin(z.re) * cosh(z.im)
+  result.im = cos(z.re) * sinh(z.im)
 
-proc arcsin*(z: Complex): Complex =
+func arcsin*[T](z: Complex[T]): Complex[T] =
   ## Returns the inverse sine of `z`.
-  var i: Complex = (0.0,1.0)
-  result = -i*ln(i*z + sqrt(1.0-z*z))
+  result = -im(T) * ln(im(T) * z + sqrt(T(1.0) - z*z))
 
-proc cos*(z: Complex): Complex =
+func cos*[T](z: Complex[T]): Complex[T] =
   ## Returns the cosine of `z`.
-  result.re = cos(z.re)*cosh(z.im)
-  result.im = -sin(z.re)*sinh(z.im)
+  result.re = cos(z.re) * cosh(z.im)
+  result.im = -sin(z.re) * sinh(z.im)
 
-proc arccos*(z: Complex): Complex =
+func arccos*[T](z: Complex[T]): Complex[T] =
   ## Returns the inverse cosine of `z`.
-  var i: Complex = (0.0,1.0)
-  result = -i*ln(z + sqrt(z*z-1.0))
+  result = -im(T) * ln(z + sqrt(z*z - T(1.0)))
 
-proc tan*(z: Complex): Complex =
+func tan*[T](z: Complex[T]): Complex[T] =
   ## Returns the tangent of `z`.
-  result = sin(z)/cos(z)
+  result = sin(z) / cos(z)
 
-proc arctan*(z: Complex): Complex =
+func arctan*[T](z: Complex[T]): Complex[T] =
   ## Returns the inverse tangent of `z`.
-  var i: Complex = (0.0,1.0)
-  result = 0.5*i*(ln(1-i*z)-ln(1+i*z))
+  result = T(0.5)*im(T) * (ln(T(1.0) - im(T)*z) - ln(T(1.0) + im(T)*z))
 
-proc cot*(z: Complex): Complex =
+func cot*[T](z: Complex[T]): Complex[T] =
   ## Returns the cotangent of `z`.
   result = cos(z)/sin(z)
 
-proc arccot*(z: Complex): Complex =
+func arccot*[T](z: Complex[T]): Complex[T] =
   ## Returns the inverse cotangent of `z`.
-  var i: Complex = (0.0,1.0)
-  result = 0.5*i*(ln(1-i/z)-ln(1+i/z))
+  result = T(0.5)*im(T) * (ln(T(1.0) - im(T)/z) - ln(T(1.0) + im(T)/z))
 
-proc sec*(z: Complex): Complex =
+func sec*[T](z: Complex[T]): Complex[T] =
   ## Returns the secant of `z`.
-  result = 1.0/cos(z)
+  result = T(1.0) / cos(z)
 
-proc arcsec*(z: Complex): Complex =
+func arcsec*[T](z: Complex[T]): Complex[T] =
   ## Returns the inverse secant of `z`.
-  var i: Complex = (0.0,1.0)
-  result = -i*ln(i*sqrt(1-1/(z*z))+1/z)
+  result = -im(T) * ln(im(T) * sqrt(1.0 - 1.0/(z*z)) + T(1.0)/z)
 
-proc csc*(z: Complex): Complex =
+func csc*[T](z: Complex[T]): Complex[T] =
   ## Returns the cosecant of `z`.
-  result = 1.0/sin(z)
+  result = T(1.0) / sin(z)
 
-proc arccsc*(z: Complex): Complex =
+func arccsc*[T](z: Complex[T]): Complex[T] =
   ## Returns the inverse cosecant of `z`.
-  var i: Complex = (0.0,1.0)
-  result = -i*ln(sqrt(1-1/(z*z))+i/z)
-
+  result = -im(T) * ln(sqrt(T(1.0) - T(1.0)/(z*z)) + im(T)/z)
 
-proc sinh*(z: Complex): Complex =
+func sinh*[T](z: Complex[T]): Complex[T] =
   ## Returns the hyperbolic sine of `z`.
-  result = 0.5*(exp(z)-exp(-z))
+  result = T(0.5) * (exp(z) - exp(-z))
 
-proc arcsinh*(z: Complex): Complex =
+func arcsinh*[T](z: Complex[T]): Complex[T] =
   ## Returns the inverse hyperbolic sine of `z`.
-  result = ln(z+sqrt(z*z+1))
+  result = ln(z + sqrt(z*z + 1.0))
 
-proc cosh*(z: Complex): Complex =
+func cosh*[T](z: Complex[T]): Complex[T] =
   ## Returns the hyperbolic cosine of `z`.
-  result = 0.5*(exp(z)+exp(-z))
+  result = T(0.5) * (exp(z) + exp(-z))
 
-proc arccosh*(z: Complex): Complex =
+func arccosh*[T](z: Complex[T]): Complex[T] =
   ## Returns the inverse hyperbolic cosine of `z`.
-  result = ln(z+sqrt(z*z-1))
+  result = ln(z + sqrt(z*z - T(1.0)))
 
-proc tanh*(z: Complex): Complex =
+func tanh*[T](z: Complex[T]): Complex[T] =
   ## Returns the hyperbolic tangent of `z`.
-  result = sinh(z)/cosh(z)
+  result = sinh(z) / cosh(z)
 
-proc arctanh*(z: Complex): Complex =
+func arctanh*[T](z: Complex[T]): Complex[T] =
   ## Returns the inverse hyperbolic tangent of `z`.
-  result = 0.5*(ln((1+z)/(1-z)))
+  result = T(0.5) * (ln((T(1.0)+z) / (T(1.0)-z)))
+
+func coth*[T](z: Complex[T]): Complex[T] =
+  ## Returns the hyperbolic cotangent of `z`.
+  result = cosh(z) / sinh(z)
 
-proc sech*(z: Complex): Complex =
+func arccoth*[T](z: Complex[T]): Complex[T] =
+  ## Returns the inverse hyperbolic cotangent of `z`.
+  result = T(0.5) * (ln(T(1.0) + T(1.0)/z) - ln(T(1.0) - T(1.0)/z))
+
+func sech*[T](z: Complex[T]): Complex[T] =
   ## Returns the hyperbolic secant of `z`.
-  result = 2/(exp(z)+exp(-z))
+  result = T(2.0) / (exp(z) + exp(-z))
 
-proc arcsech*(z: Complex): Complex =
+func arcsech*[T](z: Complex[T]): Complex[T] =
   ## Returns the inverse hyperbolic secant of `z`.
-  result = ln(1/z+sqrt(1/z+1)*sqrt(1/z-1))
+  result = ln(1.0/z + sqrt(T(1.0)/z+T(1.0)) * sqrt(T(1.0)/z-T(1.0)))
 
-proc csch*(z: Complex): Complex =
+func csch*[T](z: Complex[T]): Complex[T] =
   ## Returns the hyperbolic cosecant of `z`.
-  result = 2/(exp(z)-exp(-z))
+  result = T(2.0) / (exp(z) - exp(-z))
 
-proc arccsch*(z: Complex): Complex =
+func arccsch*[T](z: Complex[T]): Complex[T] =
   ## Returns the inverse hyperbolic cosecant of `z`.
-  result = ln(1/z+sqrt(1/(z*z)+1))
-
-proc coth*(z: Complex): Complex =
-  ## Returns the hyperbolic cotangent of `z`.
-  result = cosh(z)/sinh(z)
+  result = ln(T(1.0)/z + sqrt(T(1.0)/(z*z) + T(1.0)))
 
-proc arccoth*(z: Complex): Complex =
-  ## Returns the inverse hyperbolic cotangent of `z`.
-  result = 0.5*(ln(1+1/z)-ln(1-1/z))
-
-proc phase*(z: Complex): float =
-  ## Returns the phase of `z`.
+func phase*[T](z: Complex[T]): T =
+  ## Returns the phase (or argument) of `z`, that is the angle in polar representation.
+  ##
+  ## | `result = arctan2(z.im, z.re)`
   arctan2(z.im, z.re)
 
-proc polar*(z: Complex): tuple[r, phi: float] =
+func polar*[T](z: Complex[T]): tuple[r, phi: T] =
   ## Returns `z` in polar coordinates.
-  result.r = abs(z)
-  result.phi = phase(z)
-
-proc rect*(r: float, phi: float): Complex =
+  ##
+  ## | `result.r = abs(z)`
+  ## | `result.phi = phase(z)`
+  ##
+  ## **See also:**
+  ## * `rect func<#rect,T,T>`_ for the inverse operation
+  (r: abs(z), phi: phase(z))
+
+func rect*[T](r, phi: T): Complex[T] =
   ## Returns the complex number with polar coordinates `r` and `phi`.
-  result.re = r * cos(phi)
-  result.im = r * sin(phi)
+  ##
+  ## | `result.re = r * cos(phi)`
+  ## | `result.im = r * sin(phi)`
+  ##
+  ## **See also:**
+  ## * `polar func<#polar,Complex[T]>`_ for the inverse operation
+  complex(r * cos(phi), r * sin(phi))
+
+func almostEqual*[T: SomeFloat](x, y: Complex[T]; unitsInLastPlace: Natural = 4): bool =
+  ## Checks if two complex values are almost equal, using the
+  ## [machine epsilon](https://en.wikipedia.org/wiki/Machine_epsilon).
+  ##
+  ## Two complex values are considered almost equal if their real and imaginary
+  ## components are almost equal.
+  ##
+  ## `unitsInLastPlace` is the max number of
+  ## [units in the last place](https://en.wikipedia.org/wiki/Unit_in_the_last_place)
+  ## difference tolerated when comparing two numbers. The larger the value, the
+  ## more error is allowed. A `0` value means that two numbers must be exactly the
+  ## same to be considered equal.
+  ##
+  ## The machine epsilon has to be scaled to the magnitude of the values used
+  ## and multiplied by the desired precision in ULPs unless the difference is
+  ## subnormal.
+  almostEqual(x.re, y.re, unitsInLastPlace = unitsInLastPlace) and
+  almostEqual(x.im, y.im, unitsInLastPlace = unitsInLastPlace)
+
+func `$`*(z: Complex): string =
+  ## Returns `z`'s string representation as `"(re, im)"`.
+  runnableExamples:
+    doAssert $complex(1.0, 2.0) == "(1.0, 2.0)"
 
-
-proc `$`*(z: Complex): string =
-  ## Returns `z`'s string representation as ``"(re, im)"``.
   result = "(" & $z.re & ", " & $z.im & ")"
 
-{.pop.}
-
+proc formatValueAsTuple(result: var string; value: Complex; specifier: string) =
+  ## Format implementation for `Complex` representing the value as a (real, imaginary) tuple.
+  result.add "("
+  formatValue(result, value.re, specifier)
+  result.add ", "
+  formatValue(result, value.im, specifier)
+  result.add ")"
+
+proc formatValueAsComplexNumber(result: var string; value: Complex; specifier: string) =
+  ## Format implementation for `Complex` representing the value as a (RE+IMj) number
+  ## By default, the real and imaginary parts are formatted using the general ('g') format
+  let specifier = if specifier.contains({'e', 'E', 'f', 'F', 'g', 'G'}):
+      specifier.replace("j")
+    else:
+      specifier.replace('j', 'g')
+  result.add "("
+  formatValue(result, value.re, specifier)
+  if value.im >= 0 and not specifier.contains({'+', '-'}):
+    result.add "+"
+  formatValue(result, value.im, specifier)
+  result.add "j)"
+
+proc formatValue*(result: var string; value: Complex; specifier: string) =
+  ## Standard format implementation for `Complex`. It makes little
+  ## sense to call this directly, but it is required to exist
+  ## by the `&` macro.
+  ## For complex numbers, we add a specific 'j' specifier, which formats
+  ## the value as (A+Bj) like in mathematics.
+  if specifier.len == 0:
+    result.add $value
+  elif 'j' in specifier:
+    formatValueAsComplexNumber(result, value, specifier)
+  else:
+    formatValueAsTuple(result, value, specifier)
 
-when isMainModule:
-  var z = (0.0, 0.0)
-  var oo = (1.0,1.0)
-  var a = (1.0, 2.0)
-  var b = (-1.0, -2.0)
-  var m1 = (-1.0, 0.0)
-  var i = (0.0,1.0)
-  var one = (1.0,0.0)
-  var tt = (10.0, 20.0)
-  var ipi = (0.0, -PI)
-
-  assert( a == a )
-  assert( (a-a) == z )
-  assert( (a+b) == z )
-  assert( (a/b) == m1 )
-  assert( (1.0/a) == (0.2, -0.4) )
-  assert( (a*b) == (3.0, -4.0) )
-  assert( 10.0*a == tt )
-  assert( a*10.0 == tt )
-  assert( tt/10.0 == a )
-  assert( oo+(-1.0) == i )
-  assert( (-1.0)+oo == i )
-  assert( abs(oo) == sqrt(2.0) )
-  assert( conjugate(a) == (1.0, -2.0) )
-  assert( sqrt(m1) == i )
-  assert( exp(ipi) =~ m1 )
-
-  assert( pow(a,b) =~ (-3.72999124927876, -1.68815826725068) )
-  assert( pow(z,a) =~ (0.0, 0.0) )
-  assert( pow(z,z) =~ (1.0, 0.0) )
-  assert( pow(a,one) =~ a )
-  assert( pow(a,m1) =~ (0.2, -0.4) )
-
-  assert( ln(a) =~ (0.804718956217050, 1.107148717794090) )
-  assert( log10(a) =~ (0.349485002168009, 0.480828578784234) )
-  assert( log2(a) =~ (1.16096404744368, 1.59727796468811) )
-
-  assert( sin(a) =~ (3.16577851321617, 1.95960104142161) )
-  assert( cos(a) =~ (2.03272300701967, -3.05189779915180) )
-  assert( tan(a) =~ (0.0338128260798967, 1.0147936161466335) )
-  assert( cot(a) =~ 1.0/tan(a) )
-  assert( sec(a) =~ 1.0/cos(a) )
-  assert( csc(a) =~ 1.0/sin(a) )
-  assert( arcsin(a) =~ (0.427078586392476, 1.528570919480998) )
-  assert( arccos(a) =~ (1.14371774040242, -1.52857091948100) )
-  assert( arctan(a) =~ (1.338972522294494, 0.402359478108525) )
-
-  assert( cosh(a) =~ (-0.642148124715520, 1.068607421382778) )
-  assert( sinh(a) =~ (-0.489056259041294, 1.403119250622040) )
-  assert( tanh(a) =~ (1.1667362572409199,-0.243458201185725) )
-  assert( sech(a) =~ 1/cosh(a) )
-  assert( csch(a) =~ 1/sinh(a) )
-  assert( coth(a) =~ 1/tanh(a) )
-  assert( arccosh(a) =~ (1.528570919480998, 1.14371774040242) )
-  assert( arcsinh(a) =~ (1.469351744368185, 1.06344002357775) )
-  assert( arctanh(a) =~ (0.173286795139986, 1.17809724509617) )
-  assert( arcsech(a) =~ arccosh(1/a) )
-  assert( arccsch(a) =~ arcsinh(1/a) )
-  assert( arccoth(a) =~ arctanh(1/a) )
-
-  assert( phase(a) == 1.1071487177940904 )
-  var t = polar(a)
-  assert( rect(t.r, t.phi) =~ a )
-  assert( rect(1.0, 2.0) =~ (-0.4161468365471424, 0.9092974268256817) )
+{.pop.}
diff --git a/lib/pure/concurrency/atomics.nim b/lib/pure/concurrency/atomics.nim
new file mode 100644
index 000000000..818f1b37a
--- /dev/null
+++ b/lib/pure/concurrency/atomics.nim
@@ -0,0 +1,433 @@
+#
+#
+#            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.
+##
+## Unstable API.
+## 
+## By default, C++ uses C11 atomic primitives. To use C++ `std::atomic`,
+## `-d:nimUseCppAtomics` can be defined.
+
+runnableExamples:
+  # Atomic
+  var loc: Atomic[int]
+  loc.store(4)
+  assert loc.load == 4
+  loc.store(2)
+  assert loc.load(moRelaxed) == 2
+  loc.store(9)
+  assert loc.load(moAcquire) == 9
+  loc.store(0, moRelease)
+  assert loc.load == 0
+
+  assert loc.exchange(7) == 0
+  assert loc.load == 7
+
+  var expected = 7
+  assert loc.compareExchange(expected, 5, moRelaxed, moRelaxed)
+  assert expected == 7
+  assert loc.load == 5
+
+  assert not loc.compareExchange(expected, 12, moRelaxed, moRelaxed)
+  assert expected == 5
+  assert loc.load == 5
+
+  assert loc.fetchAdd(1) == 5
+  assert loc.fetchAdd(2) == 6
+  assert loc.fetchSub(3) == 8
+
+  loc.atomicInc(1)
+  assert loc.load == 6
+
+  # AtomicFlag
+  var flag: AtomicFlag
+
+  assert not flag.testAndSet
+  assert flag.testAndSet
+  flag.clear(moRelaxed)
+  assert not flag.testAndSet
+
+when (defined(cpp) and defined(nimUseCppAtomics)) 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 guarantees that all threads observe the same total ordering
+        ## with other moSequentiallyConsistent operations.
+
+  type
+    Atomic*[T] {.importcpp: "std::atomic", completeStruct.} = object
+      ## An atomic object with underlying type `T`.
+      raw: T
+
+    AtomicFlag* {.importcpp: "std::atomic_flag", size: 1.} = 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 | enum | 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: "_InterlockedExchange16".}
+    proc interlockedExchange(location: pointer; desired: int32): int32 {.importc: "_InterlockedExchange".}
+    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:
+    when defined(cpp):
+      {.push, header: "<atomic>".}
+      template maybeWrapStd(x: string): string =
+        "std::" & x
+    else:
+      {.push, header: "<stdatomic.h>".}
+      template maybeWrapStd(x: string): string =
+        x
+
+    type
+      MemoryOrder* {.importc: "memory_order".maybeWrapStd.} = enum
+        moRelaxed
+        moConsume
+        moAcquire
+        moRelease
+        moAcquireRelease
+        moSequentiallyConsistent
+
+    when defined(cpp):
+      type
+        # Atomic*[T] {.importcpp: "_Atomic('0)".} = object
+
+        AtomicInt8 {.importc: "std::atomic<NI8>".} = int8
+        AtomicInt16 {.importc: "std::atomic<NI16>".} = int16
+        AtomicInt32 {.importc: "std::atomic<NI32>".} = int32
+        AtomicInt64 {.importc: "std::atomic<NI64>".} = int64
+    else:
+      type
+        # Atomic*[T] {.importcpp: "_Atomic('0)".} = object
+
+        AtomicInt8 {.importc: "_Atomic NI8".} = int8
+        AtomicInt16 {.importc: "_Atomic NI16".} = int16
+        AtomicInt32 {.importc: "_Atomic NI32".} = int32
+        AtomicInt64 {.importc: "_Atomic NI64".} = int64
+
+    type
+      AtomicFlag* {.importc: "atomic_flag".maybeWrapStd, size: 1.} = object
+
+      Atomic*[T] = object
+        when T is Trivial:
+          # Maps the size of a trivial type to it's internal atomic type
+          when sizeof(T) == 1: value: AtomicInt8
+          elif sizeof(T) == 2: value: AtomicInt16
+          elif sizeof(T) == 4: value: AtomicInt32
+          elif sizeof(T) == 8: value: AtomicInt64
+        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: "atomic_load_explicit".maybeWrapStd.}
+    proc atomic_store_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.importc: "atomic_store_explicit".maybeWrapStd.}
+    proc atomic_exchange_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_exchange_explicit".maybeWrapStd.}
+    proc atomic_compare_exchange_strong_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc: "atomic_compare_exchange_strong_explicit".maybeWrapStd.}
+    proc atomic_compare_exchange_weak_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc: "atomic_compare_exchange_weak_explicit".maybeWrapStd.}
+
+    # Numerical operations
+    proc atomic_fetch_add_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_add_explicit".maybeWrapStd.}
+    proc atomic_fetch_sub_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_sub_explicit".maybeWrapStd.}
+    proc atomic_fetch_and_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_and_explicit".maybeWrapStd.}
+    proc atomic_fetch_or_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_or_explicit".maybeWrapStd.}
+    proc atomic_fetch_xor_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_xor_explicit".maybeWrapStd.}
+
+    # 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".maybeWrapStd.}
+    proc clear*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent) {.importc: "atomic_flag_clear_explicit".maybeWrapStd.}
+
+    proc fence*(order: MemoryOrder) {.importc: "atomic_thread_fence".maybeWrapStd.}
+    proc signalFence*(order: MemoryOrder) {.importc: "atomic_signal_fence".maybeWrapStd.}
+
+    {.pop.}
+
+    proc load*[T: Trivial](location: var Atomic[T]; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](atomic_load_explicit[nonAtomicType(T), typeof(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 testAndSet(location.guard, moAcquire): discard
+    try:
+      body
+    finally:
+      clear(location.guard, 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:
+        expected = location.nonAtomicValue
+        return false
+      expected = desired
+      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.} =
+    compareExchange(location, expected, desired, success, failure)
+
+  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)
diff --git a/lib/pure/concurrency/cpuinfo.nim b/lib/pure/concurrency/cpuinfo.nim
index 6d41aa1b2..9bc3fd579 100644
--- a/lib/pure/concurrency/cpuinfo.nim
+++ b/lib/pure/concurrency/cpuinfo.nim
@@ -7,85 +7,104 @@
 #    distribution, for details about the copyright.
 #
 
-## This module implements procs to determine the number of CPUs / cores.
+## This module implements a proc to determine the number of CPUs / cores.
+
+runnableExamples:
+  doAssert countProcessors() > 0
+
 
 include "system/inclrtl"
 
-import strutils, os
+when defined(js):
+  import std/jsffi
+  proc countProcessorsImpl(): int =
+    when defined(nodejs):
+      let jsOs = require("os")
+      let jsObj = jsOs.cpus().length
+    else:
+      # `navigator.hardwareConcurrency`
+      # works on browser as well as deno.
+      let navigator{.importcpp.}: JsObject
+      let jsObj = navigator.hardwareConcurrency
+    result = jsObj.to int
+else:
+  when defined(posix) and not (defined(macosx) or defined(bsd)):
+    import std/posix
+
+  when defined(windows):
+    import std/private/win_getsysteminfo
+
+  when defined(freebsd) or defined(macosx):
+    {.emit: "#include <sys/types.h>".}
 
-when not defined(windows):
-  import posix
+  when defined(openbsd) or defined(netbsd):
+    {.emit: "#include <sys/param.h>".}
 
-when defined(linux):
-  import linux
+  when defined(macosx) or defined(bsd):
+    # we HAVE to emit param.h before sysctl.h so we cannot use .header here
+    # either. The amount of archaic bullshit in Poonix based OSes is just insane.
+    {.emit: "#include <sys/sysctl.h>".}
+    {.push nodecl.}
+    when defined(macosx):
+      proc sysctlbyname(name: cstring,
+        oldp: pointer, oldlenp: var csize_t,
+        newp: pointer, newlen: csize_t): cint {.importc.}
+    let
+      CTL_HW{.importc.}: cint
+      HW_NCPU{.importc.}: cint
+    proc sysctl[I: static[int]](name: var array[I, cint], namelen: cuint,
+      oldp: pointer, oldlenp: var csize_t,
+      newp: pointer, newlen: csize_t): cint {.importc.}
+    {.pop.}
 
-when defined(freebsd) or defined(macosx):
-  {.emit:"#include <sys/types.h>".}
+  when defined(genode):
+    import genode/env
 
-when defined(openbsd) or defined(netbsd):
-  {.emit:"#include <sys/param.h>".}
+    proc affinitySpaceTotal(env: GenodeEnvPtr): cuint {.
+      importcpp: "@->cpu().affinity_space().total()".}
 
-when defined(macosx) or defined(bsd):
-  # we HAVE to emit param.h before sysctl.h so we cannot use .header here
-  # either. The amount of archaic bullshit in Poonix based OSes is just insane.
-  {.emit:"#include <sys/sysctl.h>".}
-  const
-    CTL_HW = 6
-    HW_AVAILCPU = 25
-    HW_NCPU = 3
-  proc sysctl(x: ptr array[0..3, cint], y: cint, z: pointer,
-              a: var csize, b: pointer, c: int): cint {.
-              importc: "sysctl", nodecl.}
+  when defined(haiku):
+    type
+      SystemInfo {.importc: "system_info", header: "<OS.h>".} = object
+        cpuCount {.importc: "cpu_count".}: uint32
+
+    proc getSystemInfo(info: ptr SystemInfo): int32 {.importc: "get_system_info",
+                                                      header: "<OS.h>".}
+
+  proc countProcessorsImpl(): int {.inline.} =
+    when defined(windows):
+      var
+        si: SystemInfo
+      getSystemInfo(addr si)
+      result = int(si.dwNumberOfProcessors)
+    elif defined(macosx) or defined(bsd):
+      let dest = addr result
+      var len = sizeof(result).csize_t
+      when defined(macosx):
+        # alias of "hw.activecpu"
+        if sysctlbyname("hw.logicalcpu", dest, len, nil, 0) == 0:
+          return
+      var mib = [CTL_HW, HW_NCPU]
+      if sysctl(mib, 2, dest, len, nil, 0) == 0:
+        return
+    elif defined(hpux):
+      result = mpctl(MPC_GETNUMSPUS, nil, nil)
+    elif defined(irix):
+      var SC_NPROC_ONLN {.importc: "_SC_NPROC_ONLN", header: "<unistd.h>".}: cint
+      result = sysconf(SC_NPROC_ONLN)
+    elif defined(genode):
+      result = runtimeEnv.affinitySpaceTotal().int
+    elif defined(haiku):
+      var sysinfo: SystemInfo
+      if getSystemInfo(addr sysinfo) == 0:
+        result = sysinfo.cpuCount.int
+    else:
+      result = sysconf(SC_NPROCESSORS_ONLN)
+    if result < 0: result = 0
 
-when defined(genode):
-  include genode/env
 
-  proc affinitySpaceTotal(env: GenodeEnvPtr): cuint {.
-    importcpp: "@->cpu().affinity_space().total()".}
 
 proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
-  ## returns the numer of the processors/cores the machine has.
+  ## Returns the number of the processors/cores the machine has.
   ## Returns 0 if it cannot be detected.
-  when defined(windows):
-    type
-      SYSTEM_INFO {.final, pure.} = object
-        u1: int32
-        dwPageSize: int32
-        lpMinimumApplicationAddress: pointer
-        lpMaximumApplicationAddress: pointer
-        dwActiveProcessorMask: ptr int32
-        dwNumberOfProcessors: int32
-        dwProcessorType: int32
-        dwAllocationGranularity: int32
-        wProcessorLevel: int16
-        wProcessorRevision: int16
-
-    proc GetSystemInfo(lpSystemInfo: var SYSTEM_INFO) {.stdcall, dynlib: "kernel32", importc: "GetSystemInfo".}
-
-    var
-      si: SYSTEM_INFO
-    GetSystemInfo(si)
-    result = si.dwNumberOfProcessors
-  elif defined(macosx) or defined(bsd):
-    var
-      mib: array[0..3, cint]
-      numCPU: int
-      len: csize
-    mib[0] = CTL_HW
-    mib[1] = HW_AVAILCPU
-    len = sizeof(numCPU)
-    discard sysctl(addr(mib), 2, addr(numCPU), len, nil, 0)
-    if numCPU < 1:
-      mib[1] = HW_NCPU
-      discard sysctl(addr(mib), 2, addr(numCPU), len, nil, 0)
-    result = numCPU
-  elif defined(hpux):
-    result = mpctl(MPC_GETNUMSPUS, nil, nil)
-  elif defined(irix):
-    var SC_NPROC_ONLN {.importc: "_SC_NPROC_ONLN", header: "<unistd.h>".}: cint
-    result = sysconf(SC_NPROC_ONLN)
-  elif defined(genode):
-    result = runtimeEnv.affinitySpaceTotal().int
-  else:
-    result = sysconf(SC_NPROCESSORS_ONLN)
-  if result <= 0: result = 0
+  countProcessorsImpl()
diff --git a/lib/pure/concurrency/cpuload.nim b/lib/pure/concurrency/cpuload.nim
index 1ec739485..bfbf16721 100644
--- a/lib/pure/concurrency/cpuload.nim
+++ b/lib/pure/concurrency/cpuload.nim
@@ -9,13 +9,18 @@
 
 ## This module implements a helper for a thread pool to determine whether
 ## creating a thread is a good idea.
+##
+## Unstable API.
 
 when defined(windows):
-  import winlean, os, strutils, math
+  import std/[winlean, os, strutils, math]
 
   proc `-`(a, b: FILETIME): int64 = a.rdFileTime - b.rdFileTime
 elif defined(linux):
-  from cpuinfo import countProcessors
+  from std/cpuinfo import countProcessors
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
 
 type
   ThreadPoolAdvice* = enum
@@ -81,12 +86,12 @@ proc advice*(s: var ThreadPoolState): ThreadPoolAdvice =
     result = doNothing
   inc s.calls
 
-when not defined(testing) and isMainModule:
-  import random
+when not defined(testing) and isMainModule and not defined(nimdoc):
+  import std/random
 
   proc busyLoop() =
     while true:
-      discard random(80)
+      discard rand(80)
       os.sleep(100)
 
   spawn busyLoop()
diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim
index 6ec71e912..06ed2fe54 100644
--- a/lib/pure/concurrency/threadpool.nim
+++ b/lib/pure/concurrency/threadpool.nim
@@ -7,12 +7,25 @@
 #    distribution, for details about the copyright.
 #
 
-## Implements Nim's 'spawn'.
+{.deprecated: "use the nimble packages `malebolgia`, `taskpools` or `weave` instead".}
+
+## Implements Nim's `parallel & spawn statements <manual_experimental.html#parallel-amp-spawn>`_.
+##
+## Unstable API.
+##
+## See also
+## ========
+## * `threads module <typedthreads.html>`_ for basic thread support
+## * `locks module <locks.html>`_ for locks and condition variables
+## * `asyncdispatch module <asyncdispatch.html>`_ for asynchronous IO
 
 when not compileOption("threads"):
   {.error: "Threadpool requires --threads:on option.".}
 
-import cpuinfo, cpuload, locks
+import std/[cpuinfo, cpuload, locks, os]
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, typedthreads, sysatomics]
 
 {.push stackTrace:off.}
 
@@ -30,7 +43,7 @@ proc destroySemaphore(cv: var Semaphore) {.inline.} =
   deinitCond(cv.c)
   deinitLock(cv.L)
 
-proc await(cv: var Semaphore) =
+proc blockUntil(cv: var Semaphore) =
   acquire(cv.L)
   while cv.counter <= 0:
     wait(cv.c, cv.L)
@@ -43,45 +56,42 @@ proc signal(cv: var Semaphore) =
   release(cv.L)
   signal(cv.c)
 
-const CacheLineSize = 32 # true for most archs
+const CacheLineSize = 64 # true for most archs
 
 type
-  Barrier {.compilerProc.} = object
+  Barrier {.compilerproc.} = object
     entered: int
     cv: Semaphore # Semaphore takes 3 words at least
-    when sizeof(int) < 8:
-      cacheAlign: array[CacheLineSize-4*sizeof(int), byte]
-    left: int
-    cacheAlign2: array[CacheLineSize-sizeof(int), byte]
-    interest: bool ## wether the master is interested in the "all done" event
+    left {.align(CacheLineSize).}: int
+    interest {.align(CacheLineSize).} : bool # whether the master is interested in the "all done" event
 
-proc barrierEnter(b: ptr Barrier) {.compilerProc, inline.} =
+proc barrierEnter(b: ptr Barrier) {.compilerproc, inline.} =
   # due to the signaling between threads, it is ensured we are the only
   # one with access to 'entered' so we don't need 'atomicInc' here:
   inc b.entered
   # also we need no 'fence' instructions here as soon 'nimArgsPassingDone'
   # will be called which already will perform a fence for us.
 
-proc barrierLeave(b: ptr Barrier) {.compilerProc, inline.} =
+proc barrierLeave(b: ptr Barrier) {.compilerproc, inline.} =
   atomicInc b.left
   when not defined(x86): fence()
   # We may not have seen the final value of b.entered yet,
   # so we need to check for >= instead of ==.
   if b.interest and b.left >= b.entered: signal(b.cv)
 
-proc openBarrier(b: ptr Barrier) {.compilerProc, inline.} =
+proc openBarrier(b: ptr Barrier) {.compilerproc, inline.} =
   b.entered = 0
   b.left = 0
   b.interest = false
 
-proc closeBarrier(b: ptr Barrier) {.compilerProc.} =
+proc closeBarrier(b: ptr Barrier) {.compilerproc.} =
   fence()
   if b.left != b.entered:
     b.cv.initSemaphore()
     fence()
     b.interest = true
     fence()
-    while b.left != b.entered: await(b.cv)
+    while b.left != b.entered: blockUntil(b.cv)
     destroySemaphore(b.cv)
 
 {.pop.}
@@ -89,27 +99,24 @@ proc closeBarrier(b: ptr Barrier) {.compilerProc.} =
 # ----------------------------------------------------------------------------
 
 type
-  foreign* = object ## a region that indicates the pointer comes from a
-                    ## foreign thread heap.
   AwaitInfo = object
     cv: Semaphore
     idx: int
 
-  FlowVarBase* = ref FlowVarBaseObj ## untyped base class for 'FlowVar[T]'
-  FlowVarBaseObj = object of RootObj
+  FlowVarBase* = ref FlowVarBaseObj ## Untyped base class for `FlowVar[T] <#FlowVar>`_.
+  FlowVarBaseObj {.acyclic.} = object of RootObj
     ready, usesSemaphore, awaited: bool
-    cv: Semaphore #\
-    # for 'awaitAny' support
+    cv: Semaphore  # for 'blockUntilAny' support
     ai: ptr AwaitInfo
     idx: int
     data: pointer  # we incRef and unref it to keep it alive; note this MUST NOT
                    # be RootRef here otherwise the wrong GC keeps track of it!
     owner: pointer # ptr Worker
 
-  FlowVarObj[T] = object of FlowVarBaseObj
+  FlowVarObj[T] {.acyclic.} = object of FlowVarBaseObj
     blob: T
 
-  FlowVar*{.compilerProc.}[T] = ref FlowVarObj[T] ## a data flow variable
+  FlowVar*[T] {.compilerproc.} = ref FlowVarObj[T] ## A data flow variable.
 
   ToFreeQueue = object
     len: int
@@ -130,12 +137,15 @@ type
     q: ToFreeQueue
     readyForTask: Semaphore
 
-proc await*(fv: FlowVarBase) =
-  ## waits until the value for the flowVar arrives. Usually it is not necessary
-  ## to call this explicitly.
+const threadpoolWaitMs {.intdefine.}: int = 100
+
+proc blockUntil*(fv: var FlowVarBaseObj) =
+  ## Waits until the value for `fv` arrives.
+  ##
+  ## Usually it is not necessary to call this explicitly.
   if fv.usesSemaphore and not fv.awaited:
     fv.awaited = true
-    await(fv.cv)
+    blockUntil(fv.cv)
     destroySemaphore(fv.cv)
 
 proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool =
@@ -143,7 +153,7 @@ proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool =
     w.data = data
     w.f = fn
     signal(w.taskArrived)
-    await(w.taskStarted)
+    blockUntil(w.taskStarted)
     result = true
 
 proc cleanFlowVars(w: ptr Worker) =
@@ -177,12 +187,12 @@ proc attach(fv: FlowVarBase; i: int): bool =
     result = false
   release(fv.cv.L)
 
-proc finished(fv: FlowVarBase) =
-  doAssert fv.ai.isNil, "flowVar is still attached to an 'awaitAny'"
+proc finished(fv: var FlowVarBaseObj) =
+  doAssert fv.ai.isNil, "flowVar is still attached to an 'blockUntilAny'"
   # we have to protect against the rare cases where the owner of the flowVar
   # simply disregards the flowVar and yet the "flowVar" has not yet written
   # anything to it:
-  await(fv)
+  blockUntil(fv)
   if fv.data.isNil: return
   let owner = cast[ptr Worker](fv.owner)
   let q = addr(owner.q)
@@ -191,23 +201,27 @@ proc finished(fv: FlowVarBase) =
     #echo "EXHAUSTED!"
     release(q.lock)
     wakeupWorkerToProcessQueue(owner)
-    await(q.empty)
+    blockUntil(q.empty)
     acquire(q.lock)
   q.data[q.len] = cast[pointer](fv.data)
   inc q.len
   release(q.lock)
   fv.data = nil
+  # the worker thread waits for "data" to be set to nil before shutting down
+  owner.data = nil
 
-proc fvFinalizer[T](fv: FlowVar[T]) = finished(fv)
+proc `=destroy`[T](fv: var FlowVarObj[T]) =
+  finished(fv)
+  `=destroy`(fv.blob)
 
-proc nimCreateFlowVar[T](): FlowVar[T] {.compilerProc.} =
-  new(result, fvFinalizer)
+proc nimCreateFlowVar[T](): FlowVar[T] {.compilerproc.} =
+  new(result)
 
-proc nimFlowVarCreateSemaphore(fv: FlowVarBase) {.compilerProc.} =
+proc nimFlowVarCreateSemaphore(fv: FlowVarBase) {.compilerproc.} =
   fv.cv.initSemaphore()
   fv.usesSemaphore = true
 
-proc nimFlowVarSignal(fv: FlowVarBase) {.compilerProc.} =
+proc nimFlowVarSignal(fv: FlowVarBase) {.compilerproc.} =
   if fv.ai != nil:
     acquire(fv.ai.cv.L)
     fv.ai.idx = fv.idx
@@ -218,46 +232,51 @@ proc nimFlowVarSignal(fv: FlowVarBase) {.compilerProc.} =
     signal(fv.cv)
 
 proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) =
-  ## blocks until the ``fv`` is available and then passes its value
-  ## to ``action``. Note that due to Nim's parameter passing semantics this
-  ## means that ``T`` doesn't need to be copied and so ``awaitAndThen`` can
-  ## sometimes be more efficient than ``^``.
-  await(fv)
-  when T is string or T is seq:
+  ## Blocks until `fv` is available and then passes its value
+  ## to `action`.
+  ##
+  ## Note that due to Nim's parameter passing semantics, this
+  ## means that `T` doesn't need to be copied, so `awaitAndThen` can
+  ## sometimes be more efficient than the `^ proc <#^,FlowVar[T]>`_.
+  blockUntil(fv[])
+  when defined(nimV2):
+    action(fv.blob)
+  elif T is string or T is seq:
     action(cast[T](fv.data))
   elif T is ref:
     {.error: "'awaitAndThen' not available for FlowVar[ref]".}
   else:
     action(fv.blob)
-  finished(fv)
-
-proc unsafeRead*[T](fv: FlowVar[ref T]): foreign ptr T =
-  ## blocks until the value is available and then returns this value.
-  await(fv)
-  result = cast[foreign ptr T](fv.data)
+  finished(fv[])
 
-proc `^`*[T](fv: FlowVar[ref T]): ref T =
-  ## blocks until the value is available and then returns this value.
-  await(fv)
-  let src = cast[ref T](fv.data)
-  deepCopy result, src
+proc unsafeRead*[T](fv: FlowVar[ref T]): ptr T =
+  ## Blocks until the value is available and then returns this value.
+  blockUntil(fv[])
+  when defined(nimV2):
+    result = cast[ptr T](fv.blob)
+  else:
+    result = cast[ptr T](fv.data)
+  finished(fv[])
 
 proc `^`*[T](fv: FlowVar[T]): T =
-  ## blocks until the value is available and then returns this value.
-  await(fv)
-  when T is string or T is seq:
-    # XXX closures? deepCopy?
-    result = cast[T](fv.data)
+  ## Blocks until the value is available and then returns this value.
+  blockUntil(fv[])
+  when not defined(nimV2) and (T is string or T is seq or T is ref):
+    deepCopy result, cast[T](fv.data)
   else:
     result = fv.blob
+  finished(fv[])
 
-proc awaitAny*(flowVars: openArray[FlowVarBase]): int =
-  ## awaits any of the given flowVars. Returns the index of one flowVar for
-  ## which a value arrived. A flowVar only supports one call to 'awaitAny' at
-  ## the same time. That means if you awaitAny([a,b]) and awaitAny([b,c]) the second
-  ## call will only await 'c'. If there is no flowVar left to be able to wait
-  ## on, -1 is returned.
-  ## **Note**: This results in non-deterministic behaviour and should be avoided.
+proc blockUntilAny*(flowVars: openArray[FlowVarBase]): int =
+  ## Awaits any of the given `flowVars`. Returns the index of one `flowVar`
+  ## for which a value arrived.
+  ##
+  ## A `flowVar` only supports one call to `blockUntilAny` at the same time.
+  ## That means if you `blockUntilAny([a,b])` and `blockUntilAny([b,c])`
+  ## the second call will only block until `c`. If there is no `flowVar` left
+  ## to be able to wait on, -1 is returned.
+  ##
+  ## **Note:** This results in non-deterministic behaviour and should be avoided.
   var ai: AwaitInfo
   ai.cv.initSemaphore()
   var conflicts = 0
@@ -271,16 +290,16 @@ proc awaitAny*(flowVars: openArray[FlowVarBase]): int =
       inc conflicts
   if conflicts < flowVars.len:
     if result < 0:
-      await(ai.cv)
+      blockUntil(ai.cv)
       result = ai.idx
     for i in 0 .. flowVars.high:
       discard cas(addr flowVars[i].ai, addr ai, nil)
   destroySemaphore(ai.cv)
 
 proc isReady*(fv: FlowVarBase): bool =
-  ## Determines whether the specified ``FlowVarBase``'s value is available.
+  ## Determines whether the specified `FlowVarBase`'s value is available.
   ##
-  ## If ``true`` awaiting ``fv`` will not block.
+  ## If `true`, awaiting `fv` will not block.
   if fv.usesSemaphore and not fv.awaited:
     acquire(fv.cv.L)
     result = fv.cv.counter > 0
@@ -288,31 +307,31 @@ proc isReady*(fv: FlowVarBase): bool =
   else:
     result = true
 
-proc nimArgsPassingDone(p: pointer) {.compilerProc.} =
+proc nimArgsPassingDone(p: pointer) {.compilerproc.} =
   let w = cast[ptr Worker](p)
   signal(w.taskStarted)
 
 const
-  MaxThreadPoolSize* = 256 ## maximal size of the thread pool. 256 threads
-                           ## should be good enough for anybody ;-)
-  MaxDistinguishedThread* = 32 ## maximal number of "distinguished" threads.
+  MaxThreadPoolSize* {.intdefine.} = 256 ## Maximum size of the thread pool. 256 threads
+                                         ## should be good enough for anybody ;-)
+  MaxDistinguishedThread* {.intdefine.} = 32 ## Maximum number of "distinguished" threads.
 
 type
-  ThreadId* = range[0..MaxDistinguishedThread-1]
+  ThreadId* = range[0..MaxDistinguishedThread-1] ## A thread identifier.
 
 var
   currentPoolSize: int
   maxPoolSize = MaxThreadPoolSize
   minPoolSize = 4
-  gSomeReady : Semaphore
+  gSomeReady: Semaphore
   readyWorker: ptr Worker
 
 # A workaround for recursion deadlock issue
 # https://github.com/nim-lang/Nim/issues/4597
 var
   numSlavesLock: Lock
-  numSlavesRunning {.guard: numSlavesLock}: int
-  numSlavesWaiting {.guard: numSlavesLock}: int
+  numSlavesRunning {.guard: numSlavesLock.}: int
+  numSlavesWaiting {.guard: numSlavesLock.}: int
   isSlave {.threadvar.}: bool
 
 numSlavesLock.initLock
@@ -322,13 +341,27 @@ gSomeReady.initSemaphore()
 proc slave(w: ptr Worker) {.thread.} =
   isSlave = true
   while true:
+    if w.shutdown:
+      w.shutdown = false
+      atomicDec currentPoolSize
+      while true:
+        if w.data != nil:
+          sleep(threadpoolWaitMs)
+        else:
+          # The flowvar finalizer ("finished()") set w.data to nil, so we can
+          # safely terminate the thread.
+          #
+          # TODO: look for scenarios in which the flowvar is never finalized, so
+          # a shut down thread gets stuck in this loop until the main thread exits.
+          break
+      break
     when declared(atomicStoreN):
       atomicStoreN(addr(w.ready), true, ATOMIC_SEQ_CST)
     else:
       w.ready = true
     readyWorker = w
     signal(gSomeReady)
-    await(w.taskArrived)
+    blockUntil(w.taskArrived)
     # XXX Somebody needs to look into this (why does this assertion fail
     # in Visual Studio?)
     when not defined(vcc) and not defined(tcc): assert(not w.ready)
@@ -342,9 +375,6 @@ proc slave(w: ptr Worker) {.thread.} =
       dec numSlavesRunning
 
     if w.q.len != 0: w.cleanFlowVars
-    if w.shutdown:
-      w.shutdown = false
-      atomicDec currentPoolSize
 
 proc distinguishedSlave(w: ptr Worker) {.thread.} =
   while true:
@@ -353,7 +383,7 @@ proc distinguishedSlave(w: ptr Worker) {.thread.} =
     else:
       w.ready = true
     signal(w.readyForTask)
-    await(w.taskArrived)
+    blockUntil(w.taskArrived)
     assert(not w.ready)
     w.f(w, w.data)
     if w.q.len != 0: w.cleanFlowVars
@@ -369,12 +399,12 @@ when defined(nimPinToCpu):
   var gCpus: Natural
 
 proc setMinPoolSize*(size: range[1..MaxThreadPoolSize]) =
-  ## sets the minimal thread pool size. The default value of this is 4.
+  ## Sets the minimum thread pool size. The default value of this is 4.
   minPoolSize = size
 
 proc setMaxPoolSize*(size: range[1..MaxThreadPoolSize]) =
-  ## sets the maximal thread pool size. The default value of this
-  ## is ``MaxThreadPoolSize``.
+  ## Sets the maximum thread pool size. The default value of this
+  ## is `MaxThreadPoolSize <#MaxThreadPoolSize>`_.
   maxPoolSize = size
   if currentPoolSize > maxPoolSize:
     for i in maxPoolSize..currentPoolSize-1:
@@ -414,37 +444,48 @@ proc setup() =
   for i in 0..<currentPoolSize: activateWorkerThread(i)
 
 proc preferSpawn*(): bool =
-  ## Use this proc to determine quickly if a 'spawn' or a direct call is
-  ## preferable. If it returns 'true' a 'spawn' may make sense. In general
-  ## it is not necessary to call this directly; use 'spawnX' instead.
+  ## Use this proc to determine quickly if a `spawn` or a direct call is
+  ## preferable.
+  ##
+  ## If it returns `true`, a `spawn` may make sense. In general
+  ## it is not necessary to call this directly; use the `spawnX template
+  ## <#spawnX.t>`_ instead.
   result = gSomeReady.counter > 0
 
-proc spawn*(call: typed): void {.magic: "Spawn".}
-  ## always spawns a new task, so that the 'call' is never executed on
-  ## the calling thread. 'call' has to be proc call 'p(...)' where 'p'
-  ## is gcsafe and has a return type that is either 'void' or compatible
-  ## with ``FlowVar[T]``.
-
-proc pinnedSpawn*(id: ThreadId; call: typed): void {.magic: "Spawn".}
-  ## always spawns a new task on the worker thread with ``id``, so that
-  ## the 'call' is **always** executed on
-  ## the thread. 'call' has to be proc call 'p(...)' where 'p'
-  ## is gcsafe and has a return type that is either 'void' or compatible
-  ## with ``FlowVar[T]``.
-
-template spawnX*(call): void =
-  ## spawns a new task if a CPU core is ready, otherwise executes the
-  ## call in the calling thread. Usually it is advised to
-  ## use 'spawn' in order to not block the producer for an unknown
-  ## amount of time. 'call' has to be proc call 'p(...)' where 'p'
-  ## is gcsafe and has a return type that is either 'void' or compatible
-  ## with ``FlowVar[T]``.
+proc spawn*(call: sink typed) {.magic: "Spawn".} =
+  ## Always spawns a new task, so that the `call` is never executed on
+  ## the calling thread.
+  ##
+  ## `call` has to be a proc call `p(...)` where `p` is gcsafe and has a
+  ## return type that is either `void` or compatible with `FlowVar[T]`.
+  discard "It uses `nimSpawn3` internally"
+
+proc pinnedSpawn*(id: ThreadId; call: sink typed) {.magic: "Spawn".} =
+  ## Always spawns a new task on the worker thread with `id`, so that
+  ## the `call` is **always** executed on the thread.
+  ##
+  ## `call` has to be a proc call `p(...)` where `p` is gcsafe and has a
+  ## return type that is either `void` or compatible with `FlowVar[T]`.
+  discard "It uses `nimSpawn4` internally"
+
+template spawnX*(call) =
+  ## Spawns a new task if a CPU core is ready, otherwise executes the
+  ## call in the calling thread.
+  ##
+  ## Usually, it is advised to use the `spawn proc <#spawn,sinktyped>`_
+  ## in order to not block the producer for an unknown amount of time.
+  ##
+  ## `call` has to be a proc call `p(...)` where `p` is gcsafe and has a
+  ## return type that is either 'void' or compatible with `FlowVar[T]`.
   (if preferSpawn(): spawn call else: call)
 
 proc parallel*(body: untyped) {.magic: "Parallel".}
-  ## a parallel section can be used to execute a block in parallel. ``body``
-  ## has to be in a DSL that is a particular subset of the language. Please
-  ## refer to the manual for further information.
+  ## A parallel section can be used to execute a block in parallel.
+  ##
+  ## `body` has to be in a DSL that is a particular subset of the language.
+  ##
+  ## Please refer to `the manual <manual_experimental.html#parallel-amp-spawn>`_
+  ## for further information.
 
 var
   state: ThreadPoolState
@@ -452,7 +493,7 @@ var
 
 initLock stateLock
 
-proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} =
+proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerproc.} =
   # implementation of 'spawn' that is used by the code generator.
   while true:
     if selectWorker(readyWorker, fn, data): return
@@ -501,7 +542,7 @@ proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} =
         # on the current thread instead.
         var self = addr(workersData[localThreadId-1])
         fn(self, data)
-        await(self.taskStarted)
+        blockUntil(self.taskStarted)
         return
 
     if isSlave:
@@ -526,7 +567,7 @@ proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} =
 
         inc numSlavesWaiting
 
-    await(gSomeReady)
+    blockUntil(gSomeReady)
 
     if isSlave:
       withLock numSlavesLock:
@@ -537,30 +578,29 @@ var
 
 initLock distinguishedLock
 
-proc nimSpawn4(fn: WorkerProc; data: pointer; id: ThreadId) {.compilerProc.} =
+proc nimSpawn4(fn: WorkerProc; data: pointer; id: ThreadId) {.compilerproc.} =
   acquire(distinguishedLock)
   if not distinguishedData[id].initialized:
     activateDistinguishedThread(id)
   release(distinguishedLock)
   while true:
     if selectWorker(addr(distinguishedData[id]), fn, data): break
-    await(distinguishedData[id].readyForTask)
+    blockUntil(distinguishedData[id].readyForTask)
 
 
 proc sync*() =
-  ## a simple barrier to wait for all spawn'ed tasks. If you need more elaborate
-  ## waiting, you have to use an explicit barrier.
-  var toRelease = 0
+  ## A simple barrier to wait for all `spawn`ed tasks.
+  ##
+  ## If you need more elaborate waiting, you have to use an explicit barrier.
   while true:
     var allReady = true
     for i in 0 ..< currentPoolSize:
       if not allReady: break
       allReady = allReady and workersData[i].ready
     if allReady: break
-    await(gSomeReady)
-    inc toRelease
-
-  for i in 0 ..< toRelease:
-    signal(gSomeReady)
+    sleep(threadpoolWaitMs)
+    # We cannot "blockUntil(gSomeReady)" because workers may be shut down between
+    # the time we establish that some are not "ready" and the time we wait for a
+    # "signal(gSomeReady)" from inside "slave()" that can never come.
 
 setup()
diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim
index eaff86ae6..f628aaf6b 100644
--- a/lib/pure/cookies.nim
+++ b/lib/pure/cookies.nim
@@ -9,18 +9,28 @@
 
 ## This module implements helper procs for parsing Cookies.
 
-import strtabs, times
+import std/[strtabs, times, options]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
+type
+  SameSite* {.pure.} = enum ## The SameSite cookie attribute.
+                            ## `Default` means that `setCookie`
+                            ## proc will not set `SameSite` attribute.
+    Default, None, Lax, Strict
 
 proc parseCookies*(s: string): StringTableRef =
-  ## parses cookies into a string table.
+  ## Parses cookies into a string table.
   ##
   ## The proc is meant to parse the Cookie header set by a client, not the
   ## "Set-Cookie" header set by servers.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::Nim
-  ##     doAssert parseCookies("a=1; foo=bar") == {"a": 1, "foo": "bar"}.newStringTable
+  runnableExamples:
+    import std/strtabs
+    let cookieJar = parseCookies("a=1; foo=bar")
+    assert cookieJar["a"] == "1"
+    assert cookieJar["foo"] == "bar"
 
   result = newStringTable(modeCaseInsensitive)
   var i = 0
@@ -39,9 +49,12 @@ proc parseCookies*(s: string): StringTableRef =
 
 proc setCookie*(key, value: string, domain = "", path = "",
                 expires = "", noName = false,
-                secure = false, httpOnly = false): string =
+                secure = false, httpOnly = false,
+                maxAge = none(int), sameSite = SameSite.Default): string =
   ## Creates a command in the format of
-  ## ``Set-Cookie: key=value; Domain=...; ...``
+  ## `Set-Cookie: key=value; Domain=...; ...`
+  ##
+  ## .. tip:: Cookies can be vulnerable. Consider setting `secure=true`, `httpOnly=true` and `sameSite=Strict`.
   result = ""
   if not noName: result.add("Set-Cookie: ")
   result.add key & "=" & value
@@ -50,27 +63,19 @@ proc setCookie*(key, value: string, domain = "", path = "",
   if expires != "": result.add("; Expires=" & expires)
   if secure: result.add("; Secure")
   if httpOnly: result.add("; HttpOnly")
+  if maxAge.isSome: result.add("; Max-Age=" & $maxAge.unsafeGet)
+
+  if sameSite != SameSite.Default:
+    if sameSite == SameSite.None:
+      doAssert secure, "Cookies with SameSite=None must specify the Secure attribute!"
+    result.add("; SameSite=" & $sameSite)
 
 proc setCookie*(key, value: string, expires: DateTime|Time,
                 domain = "", path = "", noName = false,
-                secure = false, httpOnly = false): string =
+                secure = false, httpOnly = false,
+                maxAge = none(int), sameSite = SameSite.Default): string =
   ## Creates a command in the format of
-  ## ``Set-Cookie: key=value; Domain=...; ...``
-  return setCookie(key, value, domain, path,
+  ## `Set-Cookie: key=value; Domain=...; ...`
+  result = setCookie(key, value, domain, path,
                    format(expires.utc, "ddd',' dd MMM yyyy HH:mm:ss 'GMT'"),
-                   noname, secure, httpOnly)
-
-when isMainModule:
-  let expire = fromUnix(0) + 1.seconds
-
-  let cookies = [
-    setCookie("test", "value", expire),
-    setCookie("test", "value", expire.local),
-    setCookie("test", "value", expire.utc)
-  ]
-  let expected = "Set-Cookie: test=value; Expires=Thu, 01 Jan 1970 00:00:01 GMT"
-  doAssert cookies == [expected, expected, expected]
-
-  let table = parseCookies("uid=1; kp=2")
-  doAssert table["uid"] == "1"
-  doAssert table["kp"] == "2"
+                   noName, secure, httpOnly, maxAge, sameSite)
diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim
index b6ef30e7c..24836e316 100644
--- a/lib/pure/coro.nim
+++ b/lib/pure/coro.nim
@@ -6,31 +6,45 @@
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 #
-## Nim coroutines implementation supports several context switching methods:
-## ucontext: available on unix and alike (default)
-## setjmp:   available on unix and alike (x86/64 only)
-## Fibers:   available and required on windows.
+
+## Nim coroutines implementation, supports several context switching methods:
+## ========  ============
+## ucontext  available on unix and alike (default)
+## setjmp    available on unix and alike (x86/64 only)
+## fibers    available and required on windows.
+## ========  ============
+##
+## -d:nimCoroutines               Required to build this module.
+## -d:nimCoroutinesUcontext       Use ucontext backend.
+## -d:nimCoroutinesSetjmp         Use setjmp backend.
+## -d:nimCoroutinesSetjmpBundled  Use bundled setjmp implementation.
 ##
-## -d:nimCoroutines              Required to build this module.
-## -d:nimCoroutinesUcontext      Use ucontext backend.
-## -d:nimCoroutinesSetjmp        Use setjmp backend.
-## -d:nimCoroutinesSetjmpBundled Use bundled setjmp implementation.
+## Unstable API.
+
+import system/coro_detection
 
 when not nimCoroutines and not defined(nimdoc):
   when defined(noNimCoroutines):
-    {.error: "Coroutines can not be used with -d:noNimCoroutines"}
+    {.error: "Coroutines can not be used with -d:noNimCoroutines".}
   else:
     {.error: "Coroutines require -d:nimCoroutines".}
 
-import os
-import lists
+import std/[os, lists]
 include system/timers
 
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 const defaultStackSize = 512 * 1024
+const useOrcArc = defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc)
+
+when useOrcArc:
+  proc nimGC_setStackBottom*(theStackBottom: pointer) = discard
 
 proc GC_addStack(bottom: pointer) {.cdecl, importc.}
 proc GC_removeStack(bottom: pointer) {.cdecl, importc.}
 proc GC_setActiveStack(bottom: pointer) {.cdecl, importc.}
+proc GC_getActiveStack() : pointer {.cdecl, importc.}
 
 const
   CORO_BACKEND_UCONTEXT = 0
@@ -43,36 +57,45 @@ when defined(windows):
     {.warning: "ucontext coroutine backend is not available on windows, defaulting to fibers.".}
   when defined(nimCoroutinesSetjmp):
     {.warning: "setjmp coroutine backend is not available on windows, defaulting to fibers.".}
+elif defined(haiku) or defined(openbsd):
+  const coroBackend = CORO_BACKEND_SETJMP
+  when defined(nimCoroutinesUcontext):
+    {.warning: "ucontext coroutine backend is not available on haiku, defaulting to setjmp".}
 elif defined(nimCoroutinesSetjmp) or defined(nimCoroutinesSetjmpBundled):
   const coroBackend = CORO_BACKEND_SETJMP
 else:
   const coroBackend = CORO_BACKEND_UCONTEXT
 
 when coroBackend == CORO_BACKEND_FIBERS:
-  import windows.winlean
+  import std/winlean
   type
     Context = pointer
 
 elif coroBackend == CORO_BACKEND_UCONTEXT:
   type
-    stack_t {.importc, header: "<sys/ucontext.h>".} = object
+    stack_t {.importc, header: "<ucontext.h>".} = object
       ss_sp: pointer
       ss_flags: int
       ss_size: int
 
-    ucontext_t {.importc, header: "<sys/ucontext.h>".} = object
+    ucontext_t {.importc, header: "<ucontext.h>".} = object
       uc_link: ptr ucontext_t
       uc_stack: stack_t
 
     Context = ucontext_t
 
-  proc getcontext(context: var ucontext_t): int32 {.importc, header: "<sys/ucontext.h>".}
-  proc setcontext(context: var ucontext_t): int32 {.importc, header: "<sys/ucontext.h>".}
-  proc swapcontext(fromCtx, toCtx: var ucontext_t): int32 {.importc, header: "<sys/ucontext.h>".}
-  proc makecontext(context: var ucontext_t, fn: pointer, argc: int32) {.importc, header: "<sys/ucontext.h>", varargs.}
+  proc getcontext(context: var ucontext_t): int32 {.importc,
+      header: "<ucontext.h>".}
+  proc setcontext(context: var ucontext_t): int32 {.importc,
+      header: "<ucontext.h>".}
+  proc swapcontext(fromCtx, toCtx: var ucontext_t): int32 {.importc,
+      header: "<ucontext.h>".}
+  proc makecontext(context: var ucontext_t, fn: pointer, argc: int32) {.importc,
+      header: "<ucontext.h>", varargs.}
 
 elif coroBackend == CORO_BACKEND_SETJMP:
-  proc coroExecWithStack*(fn: pointer, stack: pointer) {.noreturn, importc: "narch_$1", fastcall.}
+  proc coroExecWithStack*(fn: pointer, stack: pointer) {.noreturn,
+      importc: "narch_$1", fastcall.}
   when defined(amd64):
     {.compile: "../arch/x86/amd64.S".}
   elif defined(i386):
@@ -96,14 +119,14 @@ elif coroBackend == CORO_BACKEND_SETJMP:
       {.error: "Unsupported architecture.".}
 
     proc setjmp(ctx: var JmpBuf): int {.importc: "narch_$1".}
-    proc longjmp(ctx: JmpBuf, ret=1) {.importc: "narch_$1".}
+    proc longjmp(ctx: JmpBuf, ret = 1) {.importc: "narch_$1".}
   else:
     # Use setjmp/longjmp implementation provided by the system.
     type
       JmpBuf {.importc: "jmp_buf", header: "<setjmp.h>".} = object
-    
+
     proc setjmp(ctx: var JmpBuf): int {.importc, header: "<setjmp.h>".}
-    proc longjmp(ctx: JmpBuf, ret=1) {.importc, header: "<setjmp.h>".}
+    proc longjmp(ctx: JmpBuf, ret = 1) {.importc, header: "<setjmp.h>".}
 
   type
     Context = JmpBuf
@@ -111,7 +134,12 @@ elif coroBackend == CORO_BACKEND_SETJMP:
 when defined(unix):
   # GLibc fails with "*** longjmp causes uninitialized stack frame ***" because
   # our custom stacks are not initialized to a magic value.
-  {.passC: "-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0"}
+  when defined(osx):
+    # workaround: error: The deprecated ucontext routines require _XOPEN_SOURCE to be defined
+    const extra = " -D_XOPEN_SOURCE"
+  else:
+    const extra = ""
+  {.passc: "-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0" & extra.}
 
 const
   CORO_CREATED = 0
@@ -120,8 +148,8 @@ const
 
 type
   Stack {.pure.} = object
-    top: pointer      # Top of the stack. Pointer used for deallocating stack if we own it.
-    bottom: pointer   # Very bottom of the stack, acts as unique stack identifier.
+    top: pointer    # Top of the stack. Pointer used for deallocating stack if we own it.
+    bottom: pointer # Very bottom of the stack, acts as unique stack identifier.
     size: int
 
   Coroutine {.pure.} = object
@@ -139,7 +167,7 @@ type
     ## CoroutineRef holds a pointer to actual coroutine object. Public API always returns
     ## CoroutineRef instead of CoroutinePtr in order to allow holding a reference to coroutine
     ## object while it can be safely deallocated by coroutine scheduler loop. In this case
-    ## Coroutine.reference.coro is set to nil. Public API checks for for it being nil and
+    ## Coroutine.reference.coro is set to nil. Public API checks for it being nil and
     ## gracefully fails if it is nil.
     coro: CoroutinePtr
 
@@ -147,6 +175,7 @@ type
     coroutines: DoublyLinkedList[CoroutinePtr]
     current: DoublyLinkedNode[CoroutinePtr]
     loop: Coroutine
+    ncbottom: pointer # non coroutine stack botttom
 
 var ctx {.threadvar.}: CoroutineLoopContext
 
@@ -164,6 +193,8 @@ proc initialize() =
     ctx.coroutines = initDoublyLinkedList[CoroutinePtr]()
     ctx.loop = Coroutine()
     ctx.loop.state = CORO_EXECUTING
+    when not useOrcArc:
+      ctx.ncbottom = GC_getActiveStack()
     when coroBackend == CORO_BACKEND_FIBERS:
       ctx.loop.execContext = ConvertThreadToFiberEx(nil, FIBER_FLAG_FLOAT_SWITCH)
 
@@ -173,7 +204,9 @@ proc switchTo(current, to: CoroutinePtr) =
   ## Switches execution from `current` into `to` context.
   to.lastRun = getTicks()
   # Update position of current stack so gc invoked from another stack knows how much to scan.
-  GC_setActiveStack(current.stack.bottom)
+  when not useOrcArc:
+    GC_setActiveStack(current.stack.bottom)
+  nimGC_setStackBottom(current.stack.bottom)
   var frame = getFrameState()
   block:
     # Execution will switch to another fiber now. We do not need to update current stack
@@ -190,18 +223,21 @@ proc switchTo(current, to: CoroutinePtr) =
         elif to.state == CORO_CREATED:
           # Coroutine is started.
           coroExecWithStack(runCurrentTask, to.stack.bottom)
-          doAssert false
+          #raiseAssert "unreachable"
     else:
       {.error: "Invalid coroutine backend set.".}
   # Execution was just resumed. Restore frame information and set active stack.
   setFrameState(frame)
-  GC_setActiveStack(current.stack.bottom)
+  when not useOrcArc:
+    GC_setActiveStack(current.stack.bottom)
+  nimGC_setStackBottom(ctx.ncbottom)
 
-proc suspend*(sleepTime: float=0) =
-  ## Stops coroutine execution and resumes no sooner than after ``sleeptime`` seconds.
+proc suspend*(sleepTime: float = 0) =
+  ## Stops coroutine execution and resumes no sooner than after `sleeptime` seconds.
   ## Until then other coroutines are executed.
   var current = getCurrent()
   current.sleepTime = sleepTime
+  nimGC_setStackBottom(ctx.ncbottom)
   switchTo(current, addr(ctx.loop))
 
 proc runCurrentTask() =
@@ -211,38 +247,41 @@ proc runCurrentTask() =
   block:
     var current = getCurrent()
     current.stack.bottom = sp
+    nimGC_setStackBottom(current.stack.bottom)
     # Execution of new fiber just started. Since it was entered not through `switchTo` we
     # have to set active stack here as well. GC_removeStack() has to be called in main loop
     # because we still need stack available in final suspend(0) call from which we will not
     # return.
-    GC_addStack(sp)
-    # Activate current stack because we are executing in a new coroutine.
-    GC_setActiveStack(sp)
+    when not useOrcArc:
+      GC_addStack(sp)
+      # Activate current stack because we are executing in a new coroutine.
+      GC_setActiveStack(sp)
     current.state = CORO_EXECUTING
     try:
-      current.fn()                    # Start coroutine execution
+      current.fn() # Start coroutine execution
     except:
       echo "Unhandled exception in coroutine."
       writeStackTrace()
     current.state = CORO_FINISHED
-  suspend(0)                      # Exit coroutine without returning from coroExecWithStack()
-  doAssert false
+  nimGC_setStackBottom(ctx.ncbottom)
+  suspend(0) # Exit coroutine without returning from coroExecWithStack()
+  raiseAssert "unreachable"
 
-proc start*(c: proc(), stacksize: int=defaultStackSize): CoroutineRef {.discardable.} =
+proc start*(c: proc(), stacksize: int = defaultStackSize): CoroutineRef {.discardable.} =
   ## Schedule coroutine for execution. It does not run immediately.
   if ctx == nil:
     initialize()
-  
+
   var coro: CoroutinePtr
   when coroBackend == CORO_BACKEND_FIBERS:
     coro = cast[CoroutinePtr](alloc0(sizeof(Coroutine)))
     coro.execContext = CreateFiberEx(stacksize, stacksize,
-      FIBER_FLAG_FLOAT_SWITCH, (proc(p: pointer): void {.stdcall.} = runCurrentTask()), nil)
-    coro.stack.size = stacksize
+      FIBER_FLAG_FLOAT_SWITCH,
+      (proc(p: pointer) {.stdcall.} = runCurrentTask()), nil)
   else:
     coro = cast[CoroutinePtr](alloc0(sizeof(Coroutine) + stacksize))
-    coro.stack.top = cast[pointer](cast[ByteAddress](coro) + sizeof(Coroutine))
-    coro.stack.bottom = cast[pointer](cast[ByteAddress](coro.stack.top) + stacksize)
+    coro.stack.top = cast[pointer](cast[int](coro) + sizeof(Coroutine))
+    coro.stack.bottom = cast[pointer](cast[int](coro.stack.top) + stacksize)
     when coroBackend == CORO_BACKEND_UCONTEXT:
       discard getcontext(coro.execContext)
       coro.execContext.uc_stack.ss_sp = coro.stack.top
@@ -257,9 +296,9 @@ proc start*(c: proc(), stacksize: int=defaultStackSize): CoroutineRef {.discarda
   return coro.reference
 
 proc run*() =
-  initialize()
   ## Starts main coroutine scheduler loop which exits when all coroutines exit.
   ## Calling this proc starts execution of first coroutine.
+  initialize()
   ctx.current = ctx.coroutines.head
   var minDelay: float = 0
   while ctx.current != nil:
@@ -278,12 +317,13 @@ proc run*() =
     if current.state == CORO_FINISHED:
       var next = ctx.current.prev
       if next == nil:
-        # If first coroutine ends then `prev` is nil even if more coroutines 
+        # If first coroutine ends then `prev` is nil even if more coroutines
         # are to be scheduled.
         next = ctx.current.next
       current.reference.coro = nil
       ctx.coroutines.remove(ctx.current)
-      GC_removeStack(current.stack.bottom)
+      when not useOrcArc:
+        GC_removeStack(current.stack.bottom)
       when coroBackend == CORO_BACKEND_FIBERS:
         DeleteFiber(current.execContext)
       else:
@@ -297,9 +337,9 @@ proc run*() =
       ctx.current = ctx.current.next
 
 proc alive*(c: CoroutineRef): bool = c.coro != nil and c.coro.state != CORO_FINISHED
-  ## Returns ``true`` if coroutine has not returned, ``false`` otherwise.
+  ## Returns `true` if coroutine has not returned, `false` otherwise.
 
-proc wait*(c: CoroutineRef, interval=0.01) =
-  ## Returns only after coroutine ``c`` has returned. ``interval`` is time in seconds how often.
+proc wait*(c: CoroutineRef, interval = 0.01) =
+  ## Returns only after coroutine `c` has returned. `interval` is time in seconds how often.
   while alive(c):
     suspend(interval)
diff --git a/lib/pure/cstrutils.nim b/lib/pure/cstrutils.nim
index fe9ceb68b..c907e54d8 100644
--- a/lib/pure/cstrutils.nim
+++ b/lib/pure/cstrutils.nim
@@ -7,75 +7,116 @@
 #    distribution, for details about the copyright.
 #
 
-## This module supports helper routines for working with ``cstring``
-## without having to convert ``cstring`` to ``string`` in order to
+## This module supports helper routines for working with `cstring`
+## without having to convert `cstring` to `string`, in order to
 ## save allocations.
+##
+## See also
+## ========
+## * `strutils module <strutils.html>`_ for working with `string`
 
-include "system/inclrtl"
+include system/inclrtl
+import std/private/strimpl
 
-proc toLowerAscii(c: char): char {.inline.} =
-  if c in {'A'..'Z'}:
-    result = chr(ord(c) + (ord('a') - ord('A')))
-  else:
-    result = c
 
-proc startsWith*(s, prefix: cstring): bool {.noSideEffect,
-  rtl, extern: "csuStartsWith".} =
-  ## Returns true iff ``s`` starts with ``prefix``.
-  ##
-  ## If ``prefix == ""`` true is returned.
-  var i = 0
-  while true:
-    if prefix[i] == '\0': return true
-    if s[i] != prefix[i]: return false
-    inc(i)
+when defined(js):
+  func jsStartsWith(s, prefix: cstring): bool {.importjs: "#.startsWith(#)".}
+  func jsEndsWith(s, suffix: cstring): bool {.importjs: "#.endsWith(#)".}
 
-proc endsWith*(s, suffix: cstring): bool {.noSideEffect,
-  rtl, extern: "csuEndsWith".} =
-  ## Returns true iff ``s`` ends with ``suffix``.
+
+func startsWith*(s, prefix: cstring): bool {.rtl, extern: "csuStartsWith".} =
+  ## Returns true if `s` starts with `prefix`.
   ##
-  ## If ``suffix == ""`` true is returned.
-  let slen = s.len
-  var i = 0
-  var j = slen - len(suffix)
-  while i+j <% slen:
-    if s[i+j] != suffix[i]: return false
-    inc(i)
-  if suffix[i] == '\0': return true
+  ## The JS backend uses the native `String.prototype.startsWith` function.
+  runnableExamples:
+    assert startsWith(cstring"Hello, Nimion", cstring"Hello")
+    assert not startsWith(cstring"Hello, Nimion", cstring"Nimion")
+    assert startsWith(cstring"Hello", cstring"")
 
-proc cmpIgnoreStyle*(a, b: cstring): int {.noSideEffect,
-  rtl, extern: "csuCmpIgnoreStyle".} =
-  ## 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:
+  when nimvm:
+    startsWithImpl(s, prefix)
+  else:
+    when defined(js):
+      result = jsStartsWith(s, prefix)
+    else:
+      var i = 0
+      while true:
+        if prefix[i] == '\0': return true
+        if s[i] != prefix[i]: return false
+        inc(i)
+
+func endsWith*(s, suffix: cstring): bool {.rtl, extern: "csuEndsWith".} =
+  ## Returns true if `s` ends with `suffix`.
   ##
-  ## | 0 iff a == b
-  ## | < 0 iff a < b
-  ## | > 0 iff a > b
-  var i = 0
-  var j = 0
-  while true:
-    while a[i] == '_': inc(i)
-    while b[j] == '_': inc(j) # BUGFIX: typo
-    var aa = toLowerAscii(a[i])
-    var bb = toLowerAscii(b[j])
-    result = ord(aa) - ord(bb)
-    if result != 0 or aa == '\0': break
-    inc(i)
-    inc(j)
+  ## The JS backend uses the native `String.prototype.endsWith` function.
+  runnableExamples:
+    assert endsWith(cstring"Hello, Nimion", cstring"Nimion")
+    assert not endsWith(cstring"Hello, Nimion", cstring"Hello")
+    assert endsWith(cstring"Hello", cstring"")
+
+  when nimvm:
+    endsWithImpl(s, suffix)
+  else:
+    when defined(js):
+      result = jsEndsWith(s, suffix)
+    else:
+      let slen = s.len
+      var i = 0
+      var j = slen - len(suffix)
+      while i + j <% slen:
+        if s[i + j] != suffix[i]: return false
+        inc(i)
+      if suffix[i] == '\0': return true
 
-proc cmpIgnoreCase*(a, b: cstring): int {.noSideEffect,
-  rtl, extern: "csuCmpIgnoreCase".} =
+func cmpIgnoreStyle*(a, b: cstring): int {.rtl, extern: "csuCmpIgnoreStyle".} =
+  ## 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:
+  ## * 0 if `a == b`
+  ## * < 0 if `a < b`
+  ## * \> 0 if `a > b`
+  runnableExamples:
+    assert cmpIgnoreStyle(cstring"hello", cstring"H_e_L_Lo") == 0
+
+  when nimvm:
+    cmpIgnoreStyleImpl(a, b)
+  else:
+    when defined(js):
+      cmpIgnoreStyleImpl(a, b)
+    else:
+      var i = 0
+      var j = 0
+      while true:
+        while a[i] == '_': inc(i)
+        while b[j] == '_': inc(j) # BUGFIX: typo
+        var aa = toLowerAscii(a[i])
+        var bb = toLowerAscii(b[j])
+        result = ord(aa) - ord(bb)
+        if result != 0 or aa == '\0': break
+        inc(i)
+        inc(j)
+
+func cmpIgnoreCase*(a, b: cstring): int {.rtl, extern: "csuCmpIgnoreCase".} =
   ## Compares two strings in a case insensitive manner. Returns:
-  ##
-  ## | 0 iff a == b
-  ## | < 0 iff a < b
-  ## | > 0 iff a > b
-  var i = 0
-  while true:
-    var aa = toLowerAscii(a[i])
-    var bb = toLowerAscii(b[i])
-    result = ord(aa) - ord(bb)
-    if result != 0 or aa == '\0': break
-    inc(i)
+  ## * 0 if `a == b`
+  ## * < 0 if `a < b`
+  ## * \> 0 if `a > b`
+  runnableExamples:
+    assert cmpIgnoreCase(cstring"hello", cstring"HeLLo") == 0
+    assert cmpIgnoreCase(cstring"echo", cstring"hello") < 0
+    assert cmpIgnoreCase(cstring"yellow", cstring"hello") > 0
+
+  when nimvm:
+    cmpIgnoreCaseImpl(a, b)
+  else:
+    when defined(js):
+      cmpIgnoreCaseImpl(a, b)
+    else:
+      var i = 0
+      while true:
+        var aa = toLowerAscii(a[i])
+        var bb = toLowerAscii(b[i])
+        result = ord(aa) - ord(bb)
+        if result != 0 or aa == '\0': break
+        inc(i)
diff --git a/lib/pure/db_common.nim b/lib/pure/db_common.nim
deleted file mode 100644
index f8689024b..000000000
--- a/lib/pure/db_common.nim
+++ /dev/null
@@ -1,100 +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.
-#
-
-## Common datatypes and definitions for all ``db_*.nim`` (
-## `db_mysql <db_mysql.html>`_, `db_postgres <db_postgres.html>`_,
-## and `db_sqlite <db_sqlite.html>`_) modules.
-
-type
-  DbError* = object of IOError ## exception that is raised if a database error occurs
-
-  SqlQuery* = distinct string ## an SQL query string
-
-
-  DbEffect* = object of IOEffect ## effect that denotes a database operation
-  ReadDbEffect* = object of DbEffect   ## effect that denotes a read operation
-  WriteDbEffect* = object of DbEffect  ## effect that denotes a write operation
-
-  DbTypeKind* = enum  ## a superset of datatypes that might be supported.
-    dbUnknown,        ## unknown datatype
-    dbSerial,         ## datatype used for primary auto-increment keys
-    dbNull,           ## datatype used for the NULL value
-    dbBit,            ## bit datatype
-    dbBool,           ## boolean datatype
-    dbBlob,           ## blob datatype
-    dbFixedChar,      ## string of fixed length
-    dbVarchar,        ## string datatype
-    dbJson,           ## JSON datatype
-    dbXml,            ## XML datatype
-    dbInt,            ## some integer type
-    dbUInt,           ## some unsigned integer type
-    dbDecimal,        ## decimal numbers (fixed-point number)
-    dbFloat,          ## some floating point type
-    dbDate,           ## a year-month-day description
-    dbTime,           ## HH:MM:SS information
-    dbDatetime,       ## year-month-day and HH:MM:SS information,
-                      ## plus optional time or timezone information
-    dbTimestamp,      ## Timestamp values are stored as the number of seconds
-                      ## since the epoch ('1970-01-01 00:00:00' UTC).
-    dbTimeInterval,   ## an interval [a,b] of times
-    dbEnum,           ## some enum
-    dbSet,            ## set of enum values
-    dbArray,          ## an array of values
-    dbComposite,      ## composite type (record, struct, etc)
-    dbUrl,            ## a URL
-    dbUuid,           ## a UUID
-    dbInet,           ## an IP address
-    dbMacAddress,     ## a MAC address
-    dbGeometry,       ## some geometric type
-    dbPoint,          ## Point on a plane   (x,y)
-    dbLine,           ## Infinite line ((x1,y1),(x2,y2))
-    dbLseg,           ## Finite line segment   ((x1,y1),(x2,y2))
-    dbBox,            ## Rectangular box   ((x1,y1),(x2,y2))
-    dbPath,           ## Closed or open path (similar to polygon) ((x1,y1),...)
-    dbPolygon,        ## Polygon (similar to closed path)   ((x1,y1),...)
-    dbCircle,         ## Circle   <(x,y),r> (center point and radius)
-    dbUser1,          ## user definable datatype 1 (for unknown extensions)
-    dbUser2,          ## user definable datatype 2 (for unknown extensions)
-    dbUser3,          ## user definable datatype 3 (for unknown extensions)
-    dbUser4,          ## user definable datatype 4 (for unknown extensions)
-    dbUser5           ## user definable datatype 5 (for unknown extensions)
-
-  DbType* = object    ## describes a database type
-    kind*: DbTypeKind ## the kind of the described type
-    notNull*: bool    ## does the type contain NULL?
-    name*: string     ## the name of the type
-    size*: Natural    ## the size of the datatype; 0 if of variable size
-    maxReprLen*: Natural ## maximal length required for the representation
-    precision*, scale*: Natural ## precision and scale of the number
-    min*, max*: BiggestInt ## the minimum and maximum of allowed values
-    validValues*: seq[string] ## valid values of an enum or a set
-
-  DbColumn* = object   ## information about a database column
-    name*: string      ## name of the column
-    tableName*: string ## name of the table the column belongs to (optional)
-    typ*: DbType       ## type of the column
-    primaryKey*: bool  ## is this a primary key?
-    foreignKey*: bool  ## is this a foreign key?
-  DbColumns* = seq[DbColumn]
-
-template sql*(query: string): SqlQuery =
-  ## constructs a SqlQuery from the string `query`. This is supposed to be
-  ## used as a raw-string-literal modifier:
-  ## ``sql"update user set counter = counter + 1"``
-  ##
-  ## If assertions are turned off, it does nothing. If assertions are turned
-  ## on, later versions will check the string for valid syntax.
-  SqlQuery(query)
-
-proc dbError*(msg: string) {.noreturn, noinline.} =
-  ## raises an DbError exception with message `msg`.
-  var e: ref DbError
-  new(e)
-  e.msg = msg
-  raise e
diff --git a/lib/pure/distros.nim b/lib/pure/distros.nim
index 0adba5b1e..9e71d4ce0 100644
--- a/lib/pure/distros.nim
+++ b/lib/pure/distros.nim
@@ -9,33 +9,35 @@
 
 ## This module implements the basics for Linux distribution ("distro")
 ## detection and the OS's native package manager. Its primary purpose is to
-## produce output for Nimble packages like::
+## produce output for Nimble packages, like:
 ##
-##  To complete the installation, run:
+##     To complete the installation, run:
 ##
-##  sudo apt-get libblas-dev
-##  sudo apt-get libvoodoo
+##     sudo apt-get install libblas-dev
+##     sudo apt-get install libvoodoo
 ##
 ## The above output could be the result of a code snippet like:
 ##
-## .. code-block:: nim
-##
+##   ```nim
 ##   if detectOs(Ubuntu):
 ##     foreignDep "lbiblas-dev"
 ##     foreignDep "libvoodoo"
+##   ```
 ##
+## See `packaging <packaging.html>`_ for hints on distributing Nim using OS packages.
 
-from strutils import contains, toLowerAscii
+from std/strutils import contains, toLowerAscii
 
 when not defined(nimscript):
-  from osproc import execProcess
+  from std/osproc import execProcess
+  from std/envvars import existsEnv
 
 type
   Distribution* {.pure.} = enum ## the list of known distributions
-    Windows ## some version of Windows
-    Posix   ## some Posix system
-    MacOSX  ## some version of OSX
-    Linux   ## some version of Linux
+    Windows                     ## some version of Windows
+    Posix                       ## some POSIX system
+    MacOSX                      ## some version of OSX
+    Linux                       ## some version of Linux
     Ubuntu
     Debian
     Gentoo
@@ -49,6 +51,7 @@ type
     CentOS
     Deepin
     ArchLinux
+    Artix
     Antergos
     PCLinuxOS
     Mageia
@@ -105,7 +108,7 @@ type
     Clonezilla
     SteamOS
     Absolute
-    NixOS
+    NixOS                       ## NixOS or a Nix build environment
     AUSTRUMI
     Arya
     Porteus
@@ -120,67 +123,98 @@ type
     ExTiX
     Rockstor
     GoboLinux
+    Void
 
     BSD
     FreeBSD
+    NetBSD
     OpenBSD
     DragonFlyBSD
 
+    Haiku
+
 
 const
   LacksDevPackages* = {Distribution.Gentoo, Distribution.Slackware,
-    Distribution.ArchLinux}
+      Distribution.ArchLinux, Distribution.Artix, Distribution.Antergos,
+      Distribution.BlackArch, Distribution.ArchBang}
 
-var unameRes, releaseRes: string ## we cache the result of the 'uname -a'
-                                 ## execution for faster platform detections.
+# we cache the result of the 'cmdRelease'
+# execution for faster platform detections.
+var unameRes, osReleaseIDRes, releaseRes, hostnamectlRes: string
 
-template unameRelease(cmd, cache): untyped =
+template cmdRelease(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 = cmdRelease("uname -a", unameRes)
+template osReleaseID(): untyped = cmdRelease("cat /etc/os-release | grep ^ID=", osReleaseIDRes)
+template release(): untyped = cmdRelease("lsb_release -d", releaseRes)
+template hostnamectl(): untyped = cmdRelease("hostnamectl", hostnamectlRes)
+
+proc detectOsWithAllCmd(d: Distribution): bool =
+  let dd = toLowerAscii($d)
+  result = dd in toLowerAscii(osReleaseID()) or dd in toLowerAscii(release()) or
+            dd in toLowerAscii(uname()) or ("operating system: " & dd) in
+                toLowerAscii(hostnamectl())
 
 proc detectOsImpl(d: Distribution): bool =
   case d
-  of Distribution.Windows: ## some version of Windows
-    result = defined(windows)
+  of Distribution.Windows: result = defined(windows)
   of Distribution.Posix: result = defined(posix)
   of Distribution.MacOSX: result = defined(macosx)
   of Distribution.Linux: result = defined(linux)
-  of Distribution.Ubuntu, Distribution.Gentoo, Distribution.FreeBSD,
-     Distribution.OpenBSD:
-    result = ("-" & $d & " ") in uname()
-  of Distribution.RedHat:
-    result = "Red Hat" in uname()
   of Distribution.BSD: result = defined(bsd)
-  of Distribution.ArchLinux:
-    result = "arch" in toLowerAscii(uname())
-  of Distribution.OpenSUSE:
-    result = "suse" in toLowerAscii(uname()) or "suse" in toLowerAscii(release())
-  of Distribution.GoboLinux:
-    result = "-Gobo " in uname()
-  of Distribution.OpenMandriva:
-    result = "mandriva" in toLowerAscii(uname())
-  of Distribution.Solaris:
-    let uname = toLowerAscii(uname())
-    result = ("sun" in uname) or ("solaris" in uname)
   else:
-    let dd = toLowerAscii($d)
-    result = dd in toLowerAscii(uname()) or dd in toLowerAscii(release())
+    when defined(bsd):
+      case d
+      of Distribution.FreeBSD, Distribution.NetBSD, Distribution.OpenBSD:
+        result = $d in uname()
+      else:
+        result = false
+    elif defined(linux):
+      case d
+      of Distribution.Gentoo:
+        result = ("-" & $d & " ") in uname()
+      of Distribution.Elementary, Distribution.Ubuntu, Distribution.Debian,
+        Distribution.Fedora, Distribution.OpenMandriva, Distribution.CentOS,
+        Distribution.Alpine, Distribution.Mageia, Distribution.Zorin, Distribution.Void:
+        result = toLowerAscii($d) in osReleaseID()
+      of Distribution.RedHat:
+        result = "rhel" in osReleaseID()
+      of Distribution.ArchLinux:
+        result = "arch" in osReleaseID()
+      of Distribution.Artix:
+        result = "artix" in osReleaseID()
+      of Distribution.NixOS:
+        # Check if this is a Nix build or NixOS environment
+        result = existsEnv("NIX_BUILD_TOP") or existsEnv("__NIXOS_SET_ENVIRONMENT_DONE")
+      of Distribution.OpenSUSE:
+        result = "suse" in toLowerAscii(uname()) or "suse" in toLowerAscii(release())
+      of Distribution.GoboLinux:
+        result = "-Gobo " in uname()
+      of Distribution.Solaris:
+        let uname = toLowerAscii(uname())
+        result = ("sun" in uname) or ("solaris" in uname)
+      of Distribution.Haiku:
+        result = defined(haiku)
+      else:
+        result = detectOsWithAllCmd(d)
+    else:
+      result = false
 
 template detectOs*(d: untyped): bool =
-  ## Distro/OS detection. For convenience the
-  ## required ``Distribution.`` qualifier is added to the
+  ## Distro/OS detection. For convenience, the
+  ## required `Distribution.` qualifier is added to the
   ## enum value.
   detectOsImpl(Distribution.d)
 
 when not defined(nimble):
-  var foreignDeps: seq[string] = @[]
+  var foreignDeps*: seq[string] = @[]  ## Registered foreign deps.
 
-proc foreignCmd*(cmd: string; requiresSudo=false) =
-  ## Registers a foreign command to the intern list of commands
+proc foreignCmd*(cmd: string; requiresSudo = false) =
+  ## Registers a foreign command to the internal list of commands
   ## that can be queried later.
   let c = (if requiresSudo: "sudo " else: "") & cmd
   when defined(nimble):
@@ -189,11 +223,11 @@ proc foreignCmd*(cmd: string; requiresSudo=false) =
     foreignDeps.add(c)
 
 proc foreignDepInstallCmd*(foreignPackageName: string): (string, bool) =
-  ## Returns the distro's native command line to install 'foreignPackageName'
+  ## Returns the distro's native command to install `foreignPackageName`
   ## and whether it requires root/admin rights.
   let p = foreignPackageName
   when defined(windows):
-    result = ("Chocolatey install " & p, false)
+    result = ("choco install " & p, false)
   elif defined(bsd):
     result = ("ports install " & p, true)
   elif defined(linux):
@@ -216,29 +250,30 @@ proc foreignDepInstallCmd*(foreignPackageName: string): (string, bool) =
       result = ("netpkg install " & p, true)
     elif detectOs(NixOS):
       result = ("nix-env -i " & p, false)
-    elif detectOs(Solaris):
+    elif detectOs(Solaris) or detectOs(FreeBSD):
       result = ("pkg install " & p, true)
+    elif detectOs(NetBSD) or detectOs(OpenBSD):
+      result = ("pkg_add " & p, true)
     elif detectOs(PCLinuxOS):
       result = ("rpm -ivh " & p, true)
-    elif detectOs(ArchLinux):
+    elif detectOs(ArchLinux) or detectOs(Manjaro) or detectOs(Artix):
       result = ("pacman -S " & p, true)
+    elif detectOs(Void):
+      result = ("xbps-install " & p, true)
     else:
       result = ("<your package manager here> install " & p, true)
+  elif defined(haiku):
+    result = ("pkgman install " & p, true)
   else:
     result = ("brew install " & p, false)
 
 proc foreignDep*(foreignPackageName: string) =
-  ## Registers 'foreignPackageName' to the internal list of foreign deps.
-  ## It is your job to ensure the package name
+  ## Registers `foreignPackageName` to the internal list of foreign deps.
+  ## It is your job to ensure that the package name is correct.
   let (installCmd, sudo) = foreignDepInstallCmd(foreignPackageName)
-  foreignCmd installCmd, sudo
+  foreignCmd(installCmd, sudo)
 
 proc echoForeignDeps*() =
   ## Writes the list of registered foreign deps to stdout.
   for d in foreignDeps:
     echo d
-
-when false:
-  foreignDep("libblas-dev")
-  foreignDep "libfoo"
-  echoForeignDeps()
diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim
index 5bd06f6fb..a162fe37f 100644
--- a/lib/pure/dynlib.nim
+++ b/lib/pure/dynlib.nim
@@ -8,87 +8,73 @@
 #
 
 ## This module implements the ability to access symbols from shared
-## libraries. On POSIX this uses the ``dlsym`` mechanism, on
-## Windows ``LoadLibrary``.
+## libraries. On POSIX this uses the `dlsym` mechanism, on
+## Windows `LoadLibrary`.
 ##
 ## Examples
-## --------
+## ========
 ##
 ## Loading a simple C function
-## ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+## ---------------------------
 ##
-## The following example demonstrates loading a function called 'greet'
+## The following example demonstrates loading a function called `greet`
 ## from a library that is determined at runtime based upon a language choice.
-## If the library fails to load or the function 'greet' is not found,
+## If the library fails to load or the function `greet` is not found,
 ## it quits with a failure error code.
 ##
-## .. code-block::nim
-##
-##   import dynlib
-##
-##   type
-##     greetFunction = proc(): cstring {.gcsafe, stdcall.}
-##
-##   let lang = stdin.readLine()
-##
-##   let lib = case lang
-##   of "french":
-##     loadLib("french.dll")
-##   else:
-##     loadLib("english.dll")
-##
-##   if lib == nil:
-##     echo "Error loading library"
-##     quit(QuitFailure)
-##
-##   let greet = cast[greetFunction](lib.symAddr("greet"))
-##
-##   if greet == nil:
-##     echo "Error loading 'greet' function from library"
-##     quit(QuitFailure)
-##
-##   let greeting = greet()
-##
-##   echo greeting
-##
-##   unloadLib(lib)
-##
+runnableExamples:
+  type
+    GreetFunction = proc (): cstring {.gcsafe, stdcall.}
+
+  proc loadGreet(lang: string) =
+    let lib =
+      case lang
+      of "french":
+        loadLib("french.dll")
+      else:
+        loadLib("english.dll")
+    assert lib != nil, "Error loading library"
+
+    let greet = cast[GreetFunction](lib.symAddr("greet"))
+    assert greet != nil, "Error loading 'greet' function from library"
+
+    echo greet()
 
-import strutils
+    unloadLib(lib)
+
+
+import std/strutils
 
 type
-  LibHandle* = pointer ## a handle to a dynamically loaded library
+  LibHandle* = pointer ## A handle to a dynamically loaded library.
 
-proc loadLib*(path: string, global_symbols=false): LibHandle {.gcsafe.}
-  ## loads a library from `path`. Returns nil if the library could not
+proc loadLib*(path: string, globalSymbols = false): LibHandle {.gcsafe.}
+  ## Loads a library from `path`. Returns nil if the library could not
   ## be loaded.
 
 proc loadLib*(): LibHandle {.gcsafe.}
-  ## gets the handle from the current executable. Returns nil if the
+  ## Gets the handle from the current executable. Returns nil if the
   ## library could not be loaded.
 
 proc unloadLib*(lib: LibHandle) {.gcsafe.}
-  ## unloads the library `lib`
+  ## Unloads the library `lib`.
 
 proc raiseInvalidLibrary*(name: cstring) {.noinline, noreturn.} =
-  ## raises an `EInvalidLibrary` exception.
-  var e: ref LibraryError
-  new(e)
-  e.msg = "could not find symbol: " & $name
-  raise e
+  ## Raises a `LibraryError` exception.
+  raise newException(LibraryError, "could not find symbol: " & $name)
 
 proc symAddr*(lib: LibHandle, name: cstring): pointer {.gcsafe.}
-  ## retrieves the address of a procedure/variable from `lib`. Returns nil
+  ## Retrieves the address of a procedure/variable from `lib`. Returns nil
   ## if the symbol could not be found.
 
 proc checkedSymAddr*(lib: LibHandle, name: cstring): pointer =
-  ## retrieves the address of a procedure/variable from `lib`. Raises
-  ## `EInvalidLibrary` if the symbol could not be found.
+  ## Retrieves the address of a procedure/variable from `lib`. Raises
+  ## `LibraryError` if the symbol could not be found.
   result = symAddr(lib, name)
   if result == nil: raiseInvalidLibrary(name)
 
 proc libCandidates*(s: string, dest: var seq[string]) =
-  ## given a library name pattern `s` write possible library names to `dest`.
+  ## Given a library name pattern `s`, write possible library names to `dest`.
   var le = strutils.find(s, '(')
   var ri = strutils.find(s, ')', le+1)
   if le >= 0 and ri > le:
@@ -99,17 +85,18 @@ proc libCandidates*(s: string, dest: var seq[string]) =
   else:
     add(dest, s)
 
-proc loadLibPattern*(pattern: string, global_symbols=false): LibHandle =
-  ## loads a library with name matching `pattern`, similar to what `dlimport`
+proc loadLibPattern*(pattern: string, globalSymbols = false): LibHandle =
+  ## Loads a library with name matching `pattern`, similar to what the `dynlib`
   ## pragma does. Returns nil if the library could not be loaded.
-  ## Warning: this proc uses the GC and so cannot be used to load the GC.
+  ##
+  ## .. warning:: this proc uses the GC and so cannot be used to load the GC.
   var candidates = newSeq[string]()
   libCandidates(pattern, candidates)
   for c in candidates:
-    result = loadLib(c, global_symbols)
+    result = loadLib(c, globalSymbols)
     if not result.isNil: break
 
-when defined(posix):
+when defined(posix) and not defined(nintendoswitch):
   #
   # =========================================================================
   # This is an implementation based on the dlfcn interface.
@@ -118,24 +105,66 @@ when defined(posix):
   # as an emulation layer on top of native functions.
   # =========================================================================
   #
-  var
-    RTLD_NOW {.importc: "RTLD_NOW", header: "<dlfcn.h>".}: int
-    RTLD_GLOBAL {.importc: "RTLD_GLOBAL", header: "<dlfcn.h>".}: int
-
-  proc dlclose(lib: LibHandle) {.importc, header: "<dlfcn.h>".}
-  proc dlopen(path: cstring, mode: int): LibHandle {.
-      importc, header: "<dlfcn.h>".}
-  proc dlsym(lib: LibHandle, name: cstring): pointer {.
-      importc, header: "<dlfcn.h>".}
-
-  proc loadLib(path: string, global_symbols=false): LibHandle =
-    var flags = RTLD_NOW
-    if global_symbols: flags = flags or RTLD_GLOBAL
-    return dlopen(path, flags)
-  proc loadLib(): LibHandle = return dlopen(nil, RTLD_NOW)
-  proc unloadLib(lib: LibHandle) = dlclose(lib)
+  import std/posix
+
+  proc loadLib(path: string, globalSymbols = false): LibHandle =
+    let flags =
+      if globalSymbols: RTLD_NOW or RTLD_GLOBAL
+      else: RTLD_NOW
+
+    dlopen(path, flags)
+
+  proc loadLib(): LibHandle = dlopen(nil, RTLD_NOW)
+  proc unloadLib(lib: LibHandle) = discard dlclose(lib)
+  proc symAddr(lib: LibHandle, name: cstring): pointer = dlsym(lib, name)
+
+elif defined(nintendoswitch):
+  #
+  # =========================================================================
+  # Nintendo switch DevkitPro sdk does not have these. Raise an error if called.
+  # =========================================================================
+  #
+
+  proc dlclose(lib: LibHandle) =
+    raise newException(OSError, "dlclose not implemented on Nintendo Switch!")
+  proc dlopen(path: cstring, mode: int): LibHandle =
+    raise newException(OSError, "dlopen not implemented on Nintendo Switch!")
+  proc dlsym(lib: LibHandle, name: cstring): pointer =
+    raise newException(OSError, "dlsym not implemented on Nintendo Switch!")
+  proc loadLib(path: string, global_symbols = false): LibHandle =
+    raise newException(OSError, "loadLib not implemented on Nintendo Switch!")
+  proc loadLib(): LibHandle =
+    raise newException(OSError, "loadLib not implemented on Nintendo Switch!")
+  proc unloadLib(lib: LibHandle) =
+    raise newException(OSError, "unloadLib not implemented on Nintendo Switch!")
+  proc symAddr(lib: LibHandle, name: cstring): pointer =
+    raise newException(OSError, "symAddr not implemented on Nintendo Switch!")
+
+elif defined(genode):
+  #
+  # =========================================================================
+  # Not implemented for Genode without POSIX. Raise an error if called.
+  # =========================================================================
+  #
+
+  template raiseErr(prc: string) =
+    raise newException(OSError, prc & " not implemented, compile with POSIX support")
+
+  proc dlclose(lib: LibHandle) =
+    raiseErr(OSError, "dlclose")
+  proc dlopen(path: cstring, mode: int): LibHandle =
+    raiseErr(OSError, "dlopen")
+  proc dlsym(lib: LibHandle, name: cstring): pointer =
+    raiseErr(OSError, "dlsym")
+  proc loadLib(path: string, global_symbols = false): LibHandle =
+    raiseErr(OSError, "loadLib")
+  proc loadLib(): LibHandle =
+    raiseErr(OSError, "loadLib")
+  proc unloadLib(lib: LibHandle) =
+    raiseErr(OSError, "unloadLib")
   proc symAddr(lib: LibHandle, name: cstring): pointer =
-    return dlsym(lib, name)
+    raiseErr(OSError, "symAddr")
+
 
 elif defined(windows) or defined(dos):
   #
@@ -143,28 +172,24 @@ elif defined(windows) or defined(dos):
   # Native Windows Implementation
   # =======================================================================
   #
-  when defined(cpp):
-    type
-      THINSTANCE {.importc: "HINSTANCE".} = object
-        x: pointer
-  else:
-    type
-      THINSTANCE {.importc: "HINSTANCE".} = pointer
+  type
+    HMODULE {.importc: "HMODULE".} = pointer
+    FARPROC {.importc: "FARPROC".} = pointer
 
-  proc FreeLibrary(lib: THINSTANCE) {.importc, header: "<windows.h>", stdcall.}
-  proc winLoadLibrary(path: cstring): THINSTANCE {.
+  proc FreeLibrary(lib: HMODULE) {.importc, header: "<windows.h>", stdcall.}
+  proc winLoadLibrary(path: cstring): HMODULE {.
       importc: "LoadLibraryA", header: "<windows.h>", stdcall.}
-  proc getProcAddress(lib: THINSTANCE, name: cstring): pointer {.
+  proc getProcAddress(lib: HMODULE, name: cstring): FARPROC {.
       importc: "GetProcAddress", header: "<windows.h>", stdcall.}
 
-  proc loadLib(path: string, global_symbols=false): LibHandle =
+  proc loadLib(path: string, globalSymbols = false): LibHandle =
     result = cast[LibHandle](winLoadLibrary(path))
   proc loadLib(): LibHandle =
     result = cast[LibHandle](winLoadLibrary(nil))
-  proc unloadLib(lib: LibHandle) = FreeLibrary(cast[THINSTANCE](lib))
+  proc unloadLib(lib: LibHandle) = FreeLibrary(cast[HMODULE](lib))
 
   proc symAddr(lib: LibHandle, name: cstring): pointer =
-    result = getProcAddress(cast[THINSTANCE](lib), name)
+    result = cast[pointer](getProcAddress(cast[HMODULE](lib), name))
 
 else:
   {.error: "no implementation for dynlib".}
diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim
index c67cd7579..bbadca655 100644
--- a/lib/pure/encodings.nim
+++ b/lib/pure/encodings.nim
@@ -7,15 +7,46 @@
 #    distribution, for details about the copyright.
 #
 
-## Converts between different character encodings. On UNIX, this uses
+## Routines for converting between different character encodings. On UNIX, this uses
 ## the `iconv`:idx: library, on Windows the Windows API.
+##
+## The following example shows how to change character encodings.
+runnableExamples:
+  when defined(windows):
+    let
+      orig = "öäüß"
+      # convert `orig` from "UTF-8" to "CP1252"
+      cp1252 = convert(orig, "CP1252", "UTF-8")
+      # convert `cp1252` from "CP1252" to "ibm850"
+      ibm850 = convert(cp1252, "ibm850", "CP1252")
+      current = getCurrentEncoding()
+    assert orig == "\195\182\195\164\195\188\195\159"
+    assert ibm850 == "\148\132\129\225"
+    assert convert(ibm850, current, "ibm850") == orig
+
+## The example below uses a reuseable `EncodingConverter` object which is
+## created by `open` with `destEncoding` and `srcEncoding` specified. You can use
+## `convert` on this object multiple times.
+runnableExamples:
+  when defined(windows):
+    var fromGB2312 = open("utf-8", "gb2312")
+    let first = "\203\173\197\194\163\191\210\187" &
+        "\203\242\209\204\211\234\200\206\198\189\201\250"
+    assert fromGB2312.convert(first) == "谁怕?一蓑烟雨任平生"
+
+    let second = "\211\208\176\215\205\183\200\231" &
+        "\208\194\163\172\199\227\184\199\200\231\185\202"
+    assert fromGB2312.convert(second) == "有白头如新,倾盖如故"
+
 
-import os, parseutils, strutils
+import std/os
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 when not defined(windows):
   type
     ConverterObj = object
-    EncodingConverter* = ptr ConverterObj ## can convert between two character sets
+    EncodingConverter* = ptr ConverterObj ## Can convert between two character sets.
 
 else:
   type
@@ -24,76 +55,80 @@ else:
       dest, src: CodePage
 
 type
-  EncodingError* = object of ValueError ## exception that is raised
-                                        ## for encoding errors
+  EncodingError* = object of ValueError ## Exception that is raised
+                                        ## for encoding errors.
 
 when defined(windows):
+  import std/[parseutils, strutils]
   proc eqEncodingNames(a, b: string): bool =
     var i = 0
     var j = 0
     while i < a.len and j < b.len:
       if a[i] in {'-', '_'}: inc i
       if b[j] in {'-', '_'}: inc j
-      if i < a.len and j < b.len and a[i].toLowerAscii != b[j].toLowerAscii: return false
+      if i < a.len and j < b.len and
+          a[i].toLowerAscii != b[j].toLowerAscii:
+        return false
       inc i
       inc j
     result = i == a.len and j == b.len
 
   const
     winEncodings = [
-      (1,   "OEMCP"), # current OEM codepage
-      (037, "IBM037"), # IBM EBCDIC US-Canada
-      (437, "IBM437"), # OEM United States
-      (500, "IBM500"), # IBM EBCDIC International
-      (708, "ASMO-708"), # Arabic (ASMO 708)
-      (709, "ASMO_449"), # Arabic (ASMO-449+, BCON V4)
-      (710, ""), # Arabic - Transparent Arabic
-      (720, "DOS-720"), # Arabic (Transparent ASMO); Arabic (DOS)
-      (737, "ibm737"), # OEM Greek (formerly 437G); Greek (DOS)
-      (775, "ibm775"), # OEM Baltic; Baltic (DOS)
-      (850, "ibm850"), # OEM Multilingual Latin 1; Western European (DOS)
-      (852, "ibm852"), # OEM Latin 2; Central European (DOS)
-      (855, "IBM855"), # OEM Cyrillic (primarily Russian)
-      (857, "ibm857"), # OEM Turkish; Turkish (DOS)
-      (858, "IBM00858"), # OEM Multilingual Latin 1 + Euro symbol
-      (860, "IBM860"), # OEM Portuguese; Portuguese (DOS)
-      (861, "ibm861"), # OEM Icelandic; Icelandic (DOS)
-      (862, "DOS-862"), # OEM Hebrew; Hebrew (DOS)
-      (863, "IBM863"), # OEM French Canadian; French Canadian (DOS)
-      (864, "IBM864"), # OEM Arabic; Arabic (864)
-      (865, "IBM865"), # OEM Nordic; Nordic (DOS)
-      (866, "cp866"), # OEM Russian; Cyrillic (DOS)
-      (869, "ibm869"), # OEM Modern Greek; Greek, Modern (DOS)
-      (870, "IBM870"), # IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2
-      (874, "windows-874"), # ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows)
-      (875, "cp875"), # IBM EBCDIC Greek Modern
-      (932, "shift_jis"), # ANSI/OEM Japanese; Japanese (Shift-JIS)
-      (936, "gb2312"), # ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312)
+      (1, "OEMCP"),            # current OEM codepage
+      (037, "IBM037"),         # IBM EBCDIC US-Canada
+      (437, "IBM437"),         # OEM United States
+      (500, "IBM500"),         # IBM EBCDIC International
+      (708, "ASMO-708"),       # Arabic (ASMO 708)
+      (709, "ASMO_449"),       # Arabic (ASMO-449+, BCON V4)
+      (710, ""),               # Arabic - Transparent Arabic
+      (720, "DOS-720"),        # Arabic (Transparent ASMO); Arabic (DOS)
+      (737, "ibm737"),         # OEM Greek (formerly 437G); Greek (DOS)
+      (775, "ibm775"),         # OEM Baltic; Baltic (DOS)
+      (850, "ibm850"),         # OEM Multilingual Latin 1; Western European (DOS)
+      (852, "ibm852"),         # OEM Latin 2; Central European (DOS)
+      (855, "IBM855"),         # OEM Cyrillic (primarily Russian)
+      (857, "ibm857"),         # OEM Turkish; Turkish (DOS)
+      (858, "IBM00858"),       # OEM Multilingual Latin 1 + Euro symbol
+      (860, "IBM860"),         # OEM Portuguese; Portuguese (DOS)
+      (861, "ibm861"),         # OEM Icelandic; Icelandic (DOS)
+      (862, "DOS-862"),        # OEM Hebrew; Hebrew (DOS)
+      (863, "IBM863"),         # OEM French Canadian; French Canadian (DOS)
+      (864, "IBM864"),         # OEM Arabic; Arabic (864)
+      (865, "IBM865"),         # OEM Nordic; Nordic (DOS)
+      (866, "cp866"),          # OEM Russian; Cyrillic (DOS)
+      (869, "ibm869"),         # OEM Modern Greek; Greek, Modern (DOS)
+      (870, "IBM870"),         # IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2
+      (874, "windows-874"),    # ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows)
+      (875, "cp875"),          # IBM EBCDIC Greek Modern
+      (932, "shift_jis"),      # ANSI/OEM Japanese; Japanese (Shift-JIS)
+      (936, "gb2312"),         # ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312)
+      (936, "gbk"),            # Alias for GB2312 encoding
       (949, "ks_c_5601-1987"), # ANSI/OEM Korean (Unified Hangul Code)
-      (950, "big5"), # ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5)
-      (1026, "IBM1026"), # IBM EBCDIC Turkish (Latin 5)
-      (1047, "IBM01047"), # IBM EBCDIC Latin 1/Open System
-      (1140, "IBM01140"), # IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro)
-      (1141, "IBM01141"), # IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro)
-      (1142, "IBM01142"), # IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro)
-      (1143, "IBM01143"), # IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro)
-      (1144, "IBM01144"), # IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro)
-      (1145, "IBM01145"), # IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro)
-      (1146, "IBM01146"), # IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro)
-      (1147, "IBM01147"), # IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro)
-      (1148, "IBM01148"), # IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro)
-      (1149, "IBM01149"), # IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro)
-      (1200, "utf-16"), # Unicode UTF-16, little endian byte order (BMP of ISO 10646); available only to managed applications
-      (1201, "unicodeFFFE"), # Unicode UTF-16, big endian byte order; available only to managed applications
-      (1250, "windows-1250"), # ANSI Central European; Central European (Windows)
-      (1251, "windows-1251"), # ANSI Cyrillic; Cyrillic (Windows)
-      (1252, "windows-1252"), # ANSI Latin 1; Western European (Windows)
-      (1253, "windows-1253"), # ANSI Greek; Greek (Windows)
-      (1254, "windows-1254"), # ANSI Turkish; Turkish (Windows)
-      (1255, "windows-1255"), # ANSI Hebrew; Hebrew (Windows)
-      (1256, "windows-1256"), # ANSI Arabic; Arabic (Windows)
-      (1257, "windows-1257"), # ANSI Baltic; Baltic (Windows)
-      (1258, "windows-1258"), # ANSI/OEM Vietnamese; Vietnamese (Windows)
+      (950, "big5"),           # ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5)
+      (1026, "IBM1026"),       # IBM EBCDIC Turkish (Latin 5)
+      (1047, "IBM01047"),      # IBM EBCDIC Latin 1/Open System
+      (1140, "IBM01140"),      # IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro)
+      (1141, "IBM01141"),      # IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro)
+      (1142, "IBM01142"),      # IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro)
+      (1143, "IBM01143"),      # IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro)
+      (1144, "IBM01144"),      # IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro)
+      (1145, "IBM01145"),      # IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro)
+      (1146, "IBM01146"),      # IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro)
+      (1147, "IBM01147"),      # IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro)
+      (1148, "IBM01148"),      # IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro)
+      (1149, "IBM01149"),      # IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro)
+      (1200, "utf-16"),        # Unicode UTF-16, little endian byte order (BMP of ISO 10646); available only to managed applications
+      (1201, "unicodeFFFE"),   # Unicode UTF-16, big endian byte order; available only to managed applications
+      (1250, "windows-1250"),  # ANSI Central European; Central European (Windows)
+      (1251, "windows-1251"),  # ANSI Cyrillic; Cyrillic (Windows)
+      (1252, "windows-1252"),  # ANSI Latin 1; Western European (Windows)
+      (1253, "windows-1253"),  # ANSI Greek; Greek (Windows)
+      (1254, "windows-1254"),  # ANSI Turkish; Turkish (Windows)
+      (1255, "windows-1255"),  # ANSI Hebrew; Hebrew (Windows)
+      (1256, "windows-1256"),  # ANSI Arabic; Arabic (Windows)
+      (1257, "windows-1257"),  # ANSI Baltic; Baltic (Windows)
+      (1258, "windows-1258"),  # ANSI/OEM Vietnamese; Vietnamese (Windows)
 
       (1250, "cp-1250"), # ANSI Central European; Central European (Windows)
       (1251, "cp-1251"), # ANSI Cyrillic; Cyrillic (Windows)
@@ -105,106 +140,106 @@ when defined(windows):
       (1257, "cp-1257"), # ANSI Baltic; Baltic (Windows)
       (1258, "cp-1258"), # ANSI/OEM Vietnamese; Vietnamese (Windows)
 
-      (1361, "Johab"), # Korean (Johab)
-      (10000, "macintosh"), # MAC Roman; Western European (Mac)
-      (10001, "x-mac-japanese"), # Japanese (Mac)
-      (10002, "x-mac-chinesetrad"), # MAC Traditional Chinese (Big5); Chinese Traditional (Mac)
-      (10003, "x-mac-korean"), # Korean (Mac)
-      (10004, "x-mac-arabic"), # Arabic (Mac)
-      (10005, "x-mac-hebrew"), # Hebrew (Mac)
-      (10006, "x-mac-greek"), # Greek (Mac)
-      (10007, "x-mac-cyrillic"), # Cyrillic (Mac)
-      (10008, "x-mac-chinesesimp"), # MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac)
-      (10010, "x-mac-romanian"), # Romanian (Mac)
-      (10017, "x-mac-ukrainian"), # Ukrainian (Mac)
-      (10021, "x-mac-thai"), # Thai (Mac)
-      (10029, "x-mac-ce"), # MAC Latin 2; Central European (Mac)
-      (10079, "x-mac-icelandic"), # Icelandic (Mac)
-      (10081, "x-mac-turkish"), # Turkish (Mac)
-      (10082, "x-mac-croatian"), # Croatian (Mac)
-      (12000, "utf-32"), # Unicode UTF-32, little endian byte order; available only to managed applications
-      (12001, "utf-32BE"), # Unicode UTF-32, big endian byte order; available only to managed applications
-      (20000, "x-Chinese_CNS"), # CNS Taiwan; Chinese Traditional (CNS)
-      (20001, "x-cp20001"), # TCA Taiwan
-      (20002, "x_Chinese-Eten"), # Eten Taiwan; Chinese Traditional (Eten)
-      (20003, "x-cp20003"), # IBM5550 Taiwan
-      (20004, "x-cp20004"), # TeleText Taiwan
-      (20005, "x-cp20005"), # Wang Taiwan
-      (20105, "x-IA5"), # IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5)
-      (20106, "x-IA5-German"), # IA5 German (7-bit)
-      (20107, "x-IA5-Swedish"), # IA5 Swedish (7-bit)
-      (20108, "x-IA5-Norwegian"), # IA5 Norwegian (7-bit)
-      (20127, "us-ascii"), # US-ASCII (7-bit)
-      (20261, "x-cp20261"), # T.61
-      (20269, "x-cp20269"), # ISO 6937 Non-Spacing Accent
-      (20273, "IBM273"), # IBM EBCDIC Germany
-      (20277, "IBM277"), # IBM EBCDIC Denmark-Norway
-      (20278, "IBM278"), # IBM EBCDIC Finland-Sweden
-      (20280, "IBM280"), # IBM EBCDIC Italy
-      (20284, "IBM284"), # IBM EBCDIC Latin America-Spain
-      (20285, "IBM285"), # IBM EBCDIC United Kingdom
-      (20290, "IBM290"), # IBM EBCDIC Japanese Katakana Extended
-      (20297, "IBM297"), # IBM EBCDIC France
-      (20420, "IBM420"), # IBM EBCDIC Arabic
-      (20423, "IBM423"), # IBM EBCDIC Greek
-      (20424, "IBM424"), # IBM EBCDIC Hebrew
+      (1361, "Johab"),                    # Korean (Johab)
+      (10000, "macintosh"),               # MAC Roman; Western European (Mac)
+      (10001, "x-mac-japanese"),          # Japanese (Mac)
+      (10002, "x-mac-chinesetrad"),       # MAC Traditional Chinese (Big5); Chinese Traditional (Mac)
+      (10003, "x-mac-korean"),            # Korean (Mac)
+      (10004, "x-mac-arabic"),            # Arabic (Mac)
+      (10005, "x-mac-hebrew"),            # Hebrew (Mac)
+      (10006, "x-mac-greek"),             # Greek (Mac)
+      (10007, "x-mac-cyrillic"),          # Cyrillic (Mac)
+      (10008, "x-mac-chinesesimp"),       # MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac)
+      (10010, "x-mac-romanian"),          # Romanian (Mac)
+      (10017, "x-mac-ukrainian"),         # Ukrainian (Mac)
+      (10021, "x-mac-thai"),              # Thai (Mac)
+      (10029, "x-mac-ce"),                # MAC Latin 2; Central European (Mac)
+      (10079, "x-mac-icelandic"),         # Icelandic (Mac)
+      (10081, "x-mac-turkish"),           # Turkish (Mac)
+      (10082, "x-mac-croatian"),          # Croatian (Mac)
+      (12000, "utf-32"),                  # Unicode UTF-32, little endian byte order; available only to managed applications
+      (12001, "utf-32BE"),                # Unicode UTF-32, big endian byte order; available only to managed applications
+      (20000, "x-Chinese_CNS"),           # CNS Taiwan; Chinese Traditional (CNS)
+      (20001, "x-cp20001"),               # TCA Taiwan
+      (20002, "x_Chinese-Eten"),          # Eten Taiwan; Chinese Traditional (Eten)
+      (20003, "x-cp20003"),               # IBM5550 Taiwan
+      (20004, "x-cp20004"),               # TeleText Taiwan
+      (20005, "x-cp20005"),               # Wang Taiwan
+      (20105, "x-IA5"),                   # IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5)
+      (20106, "x-IA5-German"),            # IA5 German (7-bit)
+      (20107, "x-IA5-Swedish"),           # IA5 Swedish (7-bit)
+      (20108, "x-IA5-Norwegian"),         # IA5 Norwegian (7-bit)
+      (20127, "us-ascii"),                # US-ASCII (7-bit)
+      (20261, "x-cp20261"),               # T.61
+      (20269, "x-cp20269"),               # ISO 6937 Non-Spacing Accent
+      (20273, "IBM273"),                  # IBM EBCDIC Germany
+      (20277, "IBM277"),                  # IBM EBCDIC Denmark-Norway
+      (20278, "IBM278"),                  # IBM EBCDIC Finland-Sweden
+      (20280, "IBM280"),                  # IBM EBCDIC Italy
+      (20284, "IBM284"),                  # IBM EBCDIC Latin America-Spain
+      (20285, "IBM285"),                  # IBM EBCDIC United Kingdom
+      (20290, "IBM290"),                  # IBM EBCDIC Japanese Katakana Extended
+      (20297, "IBM297"),                  # IBM EBCDIC France
+      (20420, "IBM420"),                  # IBM EBCDIC Arabic
+      (20423, "IBM423"),                  # IBM EBCDIC Greek
+      (20424, "IBM424"),                  # IBM EBCDIC Hebrew
       (20833, "x-EBCDIC-KoreanExtended"), # IBM EBCDIC Korean Extended
-      (20838, "IBM-Thai"), # IBM EBCDIC Thai
-      (20866, "koi8-r"), # Russian (KOI8-R); Cyrillic (KOI8-R)
-      (20871, "IBM871"), # IBM EBCDIC Icelandic
-      (20880, "IBM880"), # IBM EBCDIC Cyrillic Russian
-      (20905, "IBM905"), # IBM EBCDIC Turkish
-      (20924, "IBM00924"), # IBM EBCDIC Latin 1/Open System (1047 + Euro symbol)
-      (20932, "EUC-JP"), # Japanese (JIS 0208-1990 and 0121-1990)
-      (20936, "x-cp20936"), # Simplified Chinese (GB2312); Chinese Simplified (GB2312-80)
-      (20949, "x-cp20949"), # Korean Wansung
-      (21025, "cp1025"), # IBM EBCDIC Cyrillic Serbian-Bulgarian
-      (21027, ""), # (deprecated)
-      (21866, "koi8-u"), # Ukrainian (KOI8-U); Cyrillic (KOI8-U)
-      (28591, "iso-8859-1"), # ISO 8859-1 Latin 1; Western European (ISO)
-      (28592, "iso-8859-2"), # ISO 8859-2 Central European; Central European (ISO)
-      (28593, "iso-8859-3"), # ISO 8859-3 Latin 3
-      (28594, "iso-8859-4"), # ISO 8859-4 Baltic
-      (28595, "iso-8859-5"), # ISO 8859-5 Cyrillic
-      (28596, "iso-8859-6"), # ISO 8859-6 Arabic
-      (28597, "iso-8859-7"), # ISO 8859-7 Greek
-      (28598, "iso-8859-8"), # ISO 8859-8 Hebrew; Hebrew (ISO-Visual)
-      (28599, "iso-8859-9"), # ISO 8859-9 Turkish
-      (28603, "iso-8859-13"), # ISO 8859-13 Estonian
-      (28605, "iso-8859-15"), # ISO 8859-15 Latin 9
-      (29001, "x-Europa"), # Europa 3
-      (38598, "iso-8859-8-i"), # ISO 8859-8 Hebrew; Hebrew (ISO-Logical)
-      (50220, "iso-2022-jp"), # ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS)
-      (50221, "csISO2022JP"), # ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana)
-      (50222, "iso-2022-jp"), # ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI)
-      (50225, "iso-2022-kr"), # ISO 2022 Korean
-      (50227, "x-cp50227"), # ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022)
-      (50229, ""), # ISO 2022 Traditional Chinese
-      (50930, ""), # EBCDIC Japanese (Katakana) Extended
-      (50931, ""), # EBCDIC US-Canada and Japanese
-      (50933, ""), # EBCDIC Korean Extended and Korean
-      (50935, ""), # EBCDIC Simplified Chinese Extended and Simplified Chinese
-      (50936, ""), # EBCDIC Simplified Chinese
-      (50937, ""), # EBCDIC US-Canada and Traditional Chinese
-      (50939, ""), # EBCDIC Japanese (Latin) Extended and Japanese
-      (51932, "euc-jp"), # EUC Japanese
-      (51936, "EUC-CN"), # EUC Simplified Chinese; Chinese Simplified (EUC)
-      (51949, "euc-kr"), # EUC Korean
-      (51950, ""), # EUC Traditional Chinese
-      (52936, "hz-gb-2312"), # HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ)
-      (54936, "GB18030"), # Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030)
-      (57002, "x-iscii-de"), # ISCII Devanagari
-      (57003, "x-iscii-be"), # ISCII Bengali
-      (57004, "x-iscii-ta"), # ISCII Tamil
-      (57005, "x-iscii-te"), # ISCII Telugu
-      (57006, "x-iscii-as"), # ISCII Assamese
-      (57007, "x-iscii-or"), # ISCII Oriya
-      (57008, "x-iscii-ka"), # ISCII Kannada
-      (57009, "x-iscii-ma"), # ISCII Malayalam
-      (57010, "x-iscii-gu"), # ISCII Gujarati
-      (57011, "x-iscii-pa"), # ISCII Punjabi
-      (65000, "utf-7"), # Unicode (UTF-7)
-      (65001, "utf-8")] # Unicode (UTF-8)
+      (20838, "IBM-Thai"),                # IBM EBCDIC Thai
+      (20866, "koi8-r"),                  # Russian (KOI8-R); Cyrillic (KOI8-R)
+      (20871, "IBM871"),                  # IBM EBCDIC Icelandic
+      (20880, "IBM880"),                  # IBM EBCDIC Cyrillic Russian
+      (20905, "IBM905"),                  # IBM EBCDIC Turkish
+      (20924, "IBM00924"),                # IBM EBCDIC Latin 1/Open System (1047 + Euro symbol)
+      (20932, "EUC-JP"),                  # Japanese (JIS 0208-1990 and 0121-1990)
+      (20936, "x-cp20936"),               # Simplified Chinese (GB2312); Chinese Simplified (GB2312-80)
+      (20949, "x-cp20949"),               # Korean Wansung
+      (21025, "cp1025"),                  # IBM EBCDIC Cyrillic Serbian-Bulgarian
+      (21027, ""),                        # (deprecated)
+      (21866, "koi8-u"),                  # Ukrainian (KOI8-U); Cyrillic (KOI8-U)
+      (28591, "iso-8859-1"),              # ISO 8859-1 Latin 1; Western European (ISO)
+      (28592, "iso-8859-2"),              # ISO 8859-2 Central European; Central European (ISO)
+      (28593, "iso-8859-3"),              # ISO 8859-3 Latin 3
+      (28594, "iso-8859-4"),              # ISO 8859-4 Baltic
+      (28595, "iso-8859-5"),              # ISO 8859-5 Cyrillic
+      (28596, "iso-8859-6"),              # ISO 8859-6 Arabic
+      (28597, "iso-8859-7"),              # ISO 8859-7 Greek
+      (28598, "iso-8859-8"),              # ISO 8859-8 Hebrew; Hebrew (ISO-Visual)
+      (28599, "iso-8859-9"),              # ISO 8859-9 Turkish
+      (28603, "iso-8859-13"),             # ISO 8859-13 Estonian
+      (28605, "iso-8859-15"),             # ISO 8859-15 Latin 9
+      (29001, "x-Europa"),                # Europa 3
+      (38598, "iso-8859-8-i"),            # ISO 8859-8 Hebrew; Hebrew (ISO-Logical)
+      (50220, "iso-2022-jp"),             # ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS)
+      (50221, "csISO2022JP"),             # ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana)
+      (50222, "iso-2022-jp"),             # ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI)
+      (50225, "iso-2022-kr"),             # ISO 2022 Korean
+      (50227, "x-cp50227"),               # ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022)
+      (50229, ""),                        # ISO 2022 Traditional Chinese
+      (50930, ""),                        # EBCDIC Japanese (Katakana) Extended
+      (50931, ""),                        # EBCDIC US-Canada and Japanese
+      (50933, ""),                        # EBCDIC Korean Extended and Korean
+      (50935, ""),                        # EBCDIC Simplified Chinese Extended and Simplified Chinese
+      (50936, ""),                        # EBCDIC Simplified Chinese
+      (50937, ""),                        # EBCDIC US-Canada and Traditional Chinese
+      (50939, ""),                        # EBCDIC Japanese (Latin) Extended and Japanese
+      (51932, "euc-jp"),                  # EUC Japanese
+      (51936, "EUC-CN"),                  # EUC Simplified Chinese; Chinese Simplified (EUC)
+      (51949, "euc-kr"),                  # EUC Korean
+      (51950, ""),                        # EUC Traditional Chinese
+      (52936, "hz-gb-2312"),              # HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ)
+      (54936, "GB18030"),                 # Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030)
+      (57002, "x-iscii-de"),              # ISCII Devanagari
+      (57003, "x-iscii-be"),              # ISCII Bengali
+      (57004, "x-iscii-ta"),              # ISCII Tamil
+      (57005, "x-iscii-te"),              # ISCII Telugu
+      (57006, "x-iscii-as"),              # ISCII Assamese
+      (57007, "x-iscii-or"),              # ISCII Oriya
+      (57008, "x-iscii-ka"),              # ISCII Kannada
+      (57009, "x-iscii-ma"),              # ISCII Malayalam
+      (57010, "x-iscii-gu"),              # ISCII Gujarati
+      (57011, "x-iscii-pa"),              # ISCII Punjabi
+      (65000, "utf-7"),                   # Unicode (UTF-7)
+      (65001, "utf-8")]                   # Unicode (UTF-8)
 
   when false:
     # not needed yet:
@@ -213,25 +248,26 @@ when defined(windows):
         maxCharSize: int32
         defaultChar: array[0..1, char]
         leadByte: array[0..12-1, char]
-    {.deprecated: [TCpInfo: CpInfo].}
 
     proc getCPInfo(codePage: CodePage, lpCPInfo: var CpInfo): int32 {.
       stdcall, importc: "GetCPInfo", dynlib: "kernel32".}
 
-  proc nameToCodePage(name: string): CodePage =
+  proc nameToCodePage*(name: string): CodePage =
     var nameAsInt: int
     if parseInt(name, nameAsInt) == 0: nameAsInt = -1
     for no, na in items(winEncodings):
       if no == nameAsInt or eqEncodingNames(na, name): return CodePage(no)
     result = CodePage(-1)
 
-  proc codePageToName(c: CodePage): string =
+  proc codePageToName*(c: CodePage): string =
     for no, na in items(winEncodings):
       if no == int(c):
         return if na.len != 0: na else: $no
     result = ""
 
   proc getACP(): CodePage {.stdcall, importc: "GetACP", dynlib: "kernel32".}
+  proc getGetConsoleCP(): CodePage {.stdcall, importc: "GetConsoleCP",
+      dynlib: "kernel32".}
 
   proc multiByteToWideChar(
     codePage: CodePage,
@@ -249,13 +285,13 @@ when defined(windows):
     cchWideChar: cint,
     lpMultiByteStr: cstring,
     cbMultiByte: cint,
-    lpDefaultChar: cstring=nil,
-    lpUsedDefaultChar: pointer=nil): cint {.
+    lpDefaultChar: cstring = nil,
+    lpUsedDefaultChar: pointer = nil): cint {.
       stdcall, importc: "WideCharToMultiByte", dynlib: "kernel32".}
 
 else:
   when defined(haiku):
-    const iconvDll = "(libc.so.6|libiconv.so|libtextencoding.so)"
+    const iconvDll = "libiconv.so"
   elif defined(macosx):
     const iconvDll = "libiconv.dylib"
   else:
@@ -272,11 +308,15 @@ else:
     const EILSEQ = 86.cint
   elif defined(solaris):
     const EILSEQ = 88.cint
+  elif defined(haiku):
+    const EILSEQ = -2147454938.cint
 
   var errno {.importc, header: "<errno.h>".}: cint
 
-  when defined(freebsd) or defined(netbsd):
+  when defined(bsd):
     {.pragma: importIconv, cdecl, header: "<iconv.h>".}
+    when defined(openbsd):
+      {.passL: "-liconv".}
   else:
     {.pragma: importIconv, cdecl, dynlib: iconvDll.}
 
@@ -284,26 +324,25 @@ else:
     importc: "iconv_open", importIconv.}
   proc iconvClose(c: EncodingConverter) {.
     importc: "iconv_close", importIconv.}
-  proc iconv(c: EncodingConverter, inbuf: var cstring, inbytesLeft: var int,
-             outbuf: var cstring, outbytesLeft: var int): int {.
-    importc: "iconv", importIconv.}
-  proc iconv(c: EncodingConverter, inbuf: pointer, inbytesLeft: pointer,
-             outbuf: var cstring, outbytesLeft: var int): int {.
+  proc iconv(c: EncodingConverter, inbuf: ptr cstring, inbytesLeft: ptr csize_t,
+             outbuf: ptr cstring, outbytesLeft: ptr csize_t): csize_t {.
     importc: "iconv", importIconv.}
 
-proc getCurrentEncoding*(): string =
-  ## retrieves the current encoding. On Unix, always "UTF-8" is returned.
+proc getCurrentEncoding*(uiApp = false): string =
+  ## Retrieves the current encoding. On Unix, "UTF-8" is always returned.
+  ## The `uiApp` parameter is Windows specific. If true, the UI's code-page
+  ## is returned, if false, the Console's code-page is returned.
   when defined(windows):
-    result = codePageToName(getACP())
+    result = codePageToName(if uiApp: getACP() else: getGetConsoleCP())
   else:
     result = "UTF-8"
 
 proc open*(destEncoding = "UTF-8", srcEncoding = "CP1252"): EncodingConverter =
-  ## opens a converter that can convert from `srcEncoding` to `destEncoding`.
-  ## Raises `EIO` if it cannot fulfill the request.
+  ## Opens a converter that can convert from `srcEncoding` to `destEncoding`.
+  ## Raises `EncodingError` if it cannot fulfill the request.
   when not defined(windows):
     result = iconvOpen(destEncoding, srcEncoding)
-    if result == nil:
+    if result == cast[EncodingConverter](-1):
       raise newException(EncodingError,
         "cannot create encoding converter from " &
         srcEncoding & " to " & destEncoding)
@@ -318,37 +357,34 @@ proc open*(destEncoding = "UTF-8", srcEncoding = "CP1252"): EncodingConverter =
         "cannot find encoding " & srcEncoding)
 
 proc close*(c: EncodingConverter) =
-  ## frees the resources the converter `c` holds.
+  ## Frees the resources the converter `c` holds.
   when not defined(windows):
     iconvClose(c)
 
 when defined(windows):
-  proc convert*(c: EncodingConverter, s: string): string =
-    ## converts `s` to `destEncoding` that was given to the converter `c`. It
-    ## assumed that `s` is in `srcEncoding`.
-
-    # special case: empty string: needed because MultiByteToWideChar
-    # return 0 in case of error:
-    if s.len == 0: return ""
+  proc convertToWideString(codePage: CodePage, s: string): string =
     # educated guess of capacity:
     var cap = s.len + s.len shr 2
-    result = newStringOfCap(cap*2)
+    result = newString(cap*2)
     # convert to utf-16 LE
-    var m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
+    var m = multiByteToWideChar(codePage,
+                                dwFlags = 0'i32,
                                 lpMultiByteStr = cstring(s),
                                 cbMultiByte = cint(s.len),
                                 lpWideCharStr = cstring(result),
                                 cchWideChar = cint(cap))
     if m == 0:
       # try again; ask for capacity:
-      cap = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
+      cap = multiByteToWideChar(codePage,
+                                dwFlags = 0'i32,
                                 lpMultiByteStr = cstring(s),
                                 cbMultiByte = cint(s.len),
                                 lpWideCharStr = nil,
                                 cchWideChar = cint(0))
       # and do the conversion properly:
-      result = newStringOfCap(cap*2)
-      m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
+      result = newString(cap*2)
+      m = multiByteToWideChar(codePage,
+                              dwFlags = 0'i32,
                               lpMultiByteStr = cstring(s),
                               cbMultiByte = cint(s.len),
                               lpWideCharStr = cstring(result),
@@ -360,56 +396,78 @@ when defined(windows):
     else:
       assert(false) # cannot happen
 
-    # if already utf-16 LE, no further need to do something:
-    if int(c.dest) == 1200: return
-    # otherwise the fun starts again:
-    cap = s.len + s.len shr 2
-    var res = newStringOfCap(cap)
-    m = wideCharToMultiByte(
-      codePage = c.dest,
-      dwFlags = 0'i32,
-      lpWideCharStr = cstring(result),
-      cchWideChar = cint(result.len div 2),
-      lpMultiByteStr = cstring(res),
-      cbMultiByte = cap.cint)
+  proc convertFromWideString(codePage: CodePage, s: string): string =
+    let charCount = s.len div 2
+    var cap = s.len + s.len shr 2
+    result = newString(cap)
+    var m = wideCharToMultiByte(codePage,
+                                dwFlags = 0'i32,
+                                lpWideCharStr = cstring(s),
+                                cchWideChar = cint(charCount),
+                                lpMultiByteStr = cstring(result),
+                                cbMultiByte = cap.cint)
     if m == 0:
       # try again; ask for capacity:
-      cap = wideCharToMultiByte(
-        codePage = c.dest,
-        dwFlags = 0'i32,
-        lpWideCharStr = cstring(result),
-        cchWideChar = cint(result.len div 2),
-        lpMultiByteStr = nil,
-        cbMultiByte = cint(0))
+      cap = wideCharToMultiByte(codePage,
+                                dwFlags = 0'i32,
+                                lpWideCharStr = cstring(s),
+                                cchWideChar = cint(charCount),
+                                lpMultiByteStr = nil,
+                                cbMultiByte = cint(0))
       # and do the conversion properly:
-      res = newStringOfCap(cap)
-      m = wideCharToMultiByte(
-        codePage = c.dest,
-        dwFlags = 0'i32,
-        lpWideCharStr = cstring(result),
-        cchWideChar = cint(result.len div 2),
-        lpMultiByteStr = cstring(res),
-        cbMultiByte = cap.cint)
+      result = newString(cap)
+      m = wideCharToMultiByte(codePage,
+                              dwFlags = 0'i32,
+                              lpWideCharStr = cstring(s),
+                              cchWideChar = cint(charCount),
+                              lpMultiByteStr = cstring(result),
+                              cbMultiByte = cap.cint)
       if m == 0: raiseOSError(osLastError())
-      setLen(res, m)
-      result = res
+      setLen(result, m)
     elif m <= cap:
-      setLen(res, m)
-      result = res
+      setLen(result, m)
     else:
       assert(false) # cannot happen
 
+  proc convertWin(codePageFrom: CodePage, codePageTo: CodePage,
+      s: string): string =
+    # special case: empty string: needed because MultiByteToWideChar, WideCharToMultiByte
+    # return 0 in case of error
+    if s.len == 0: return ""
+    # multiByteToWideChar does not support encoding from code pages below
+    let unsupported = [1201, 12000, 12001]
+
+    if int(codePageFrom) in unsupported:
+      let message = "encoding from " & codePageToName(codePageFrom) & " is not supported on windows"
+      raise newException(EncodingError, message)
+
+    if int(codePageTo) in unsupported:
+      let message = "encoding to " & codePageToName(codePageTo) & " is not supported on windows"
+      raise newException(EncodingError, message)
+
+    # in case it's already UTF-16 little endian - conversion can be simplified
+    let wideString = if int(codePageFrom) == 1200: s
+                     else: convertToWideString(codePageFrom, s)
+    return if int(codePageTo) == 1200: wideString
+           else: convertFromWideString(codePageTo, wideString)
+
+  proc convert*(c: EncodingConverter, s: string): string =
+    result = convertWin(c.src, c.dest, s)
 else:
   proc convert*(c: EncodingConverter, s: string): string =
+    ## Converts `s` to `destEncoding` that was given to the converter `c`. It
+    ## assumes that `s` is in `srcEncoding`.
+    ##
+    ## .. warning:: UTF-16BE and UTF-32 conversions are not supported on Windows.
     result = newString(s.len)
-    var inLen = len(s)
-    var outLen = len(result)
+    var inLen = csize_t len(s)
+    var outLen = csize_t len(result)
     var src = cstring(s)
     var dst = cstring(result)
-    var iconvres: int
+    var iconvres: csize_t
     while inLen > 0:
-      iconvres = iconv(c, src, inLen, dst, outLen)
-      if iconvres == -1:
+      iconvres = iconv(c, addr src, addr inLen, addr dst, addr outLen)
+      if iconvres == high(csize_t):
         var lerr = errno
         if lerr == EILSEQ or lerr == EINVAL:
           # unknown char, skip
@@ -420,43 +478,34 @@ else:
           dec(outLen)
         elif lerr == E2BIG:
           var offset = cast[int](dst) - cast[int](cstring(result))
-          setLen(result, len(result)+inLen*2+5)
+          setLen(result, len(result) + inLen.int * 2 + 5)
           # 5 is minimally one utf-8 char
           dst = cast[cstring](cast[int](cstring(result)) + offset)
-          outLen = len(result) - offset
+          outLen = csize_t(len(result) - offset)
         else:
           raiseOSError(lerr.OSErrorCode)
     # iconv has a buffer that needs flushing, specially if the last char is
     # not '\0'
-    discard iconv(c, nil, nil, dst, outLen)
-    if iconvres == cint(-1) and errno == E2BIG:
+    discard iconv(c, nil, nil, addr dst, addr outLen)
+    if iconvres == high(csize_t) and errno == E2BIG:
       var offset = cast[int](dst) - cast[int](cstring(result))
-      setLen(result, len(result)+inLen*2+5)
+      setLen(result, len(result) + inLen.int * 2 + 5)
       # 5 is minimally one utf-8 char
       dst = cast[cstring](cast[int](cstring(result)) + offset)
-      outLen = len(result) - offset
-      discard iconv(c, nil, nil, dst, outLen)
+      outLen = csize_t(len(result) - offset)
+      discard iconv(c, nil, nil, addr dst, addr outLen)
     # trim output buffer
-    setLen(result, len(result) - outLen)
+    setLen(result, len(result) - outLen.int)
 
 proc convert*(s: string, destEncoding = "UTF-8",
                          srcEncoding = "CP1252"): string =
-  ## converts `s` to `destEncoding`. It assumed that `s` is in `srcEncoding`.
+  ## Converts `s` to `destEncoding`. It assumed that `s` is in `srcEncoding`.
   ## This opens a converter, uses it and closes it again and is thus more
-  ## convienent but also likely less efficient than re-using a converter.
+  ## convenient but also likely less efficient than re-using a converter.
+  ##
+  ## .. warning:: UTF-16BE and UTF-32 conversions are not supported on Windows.
   var c = open(destEncoding, srcEncoding)
   try:
     result = convert(c, s)
   finally:
     close(c)
-
-when not defined(testing) and isMainModule:
-  let
-    orig = "öäüß"
-    cp1252 = convert(orig, "CP1252", "UTF-8")
-    ibm850 = convert(cp1252, "ibm850", "CP1252")
-    current = getCurrentEncoding()
-  echo "Original string from source code: ", orig
-  echo "Forced ibm850 encoding: ", ibm850
-  echo "Current encoding: ", current
-  echo "From ibm850 to current: ", convert(ibm850, current, "ibm850")
diff --git a/lib/pure/endians.nim b/lib/pure/endians.nim
index 6f80d56ef..4c1d45ae5 100644
--- a/lib/pure/endians.nim
+++ b/lib/pure/endians.nim
@@ -9,60 +9,92 @@
 
 ## This module contains helpers that deal with different byte orders
 ## (`endian`:idx:).
+##
+## Endianness is the order of bytes of a value in memory. Big-endian means that
+## the most significant byte is stored at the smallest memory address,
+## while little endian means that the least-significant byte is stored
+## at the smallest address. See also https://en.wikipedia.org/wiki/Endianness.
+##
+## Unstable API.
 
 when defined(gcc) or defined(llvm_gcc) or defined(clang):
   const useBuiltinSwap = true
   proc builtin_bswap16(a: uint16): uint16 {.
-      importc: "__builtin_bswap16", nodecl, nosideeffect.}
+      importc: "__builtin_bswap16", nodecl, noSideEffect.}
 
   proc builtin_bswap32(a: uint32): uint32 {.
-      importc: "__builtin_bswap32", nodecl, nosideeffect.}
+      importc: "__builtin_bswap32", nodecl, noSideEffect.}
 
   proc builtin_bswap64(a: uint64): uint64 {.
-      importc: "__builtin_bswap64", nodecl, nosideeffect.}
+      importc: "__builtin_bswap64", nodecl, noSideEffect.}
 elif defined(icc):
   const useBuiltinSwap = true
   proc builtin_bswap16(a: uint16): uint16 {.
-      importc: "_bswap16", nodecl, nosideeffect.}
+      importc: "_bswap16", nodecl, noSideEffect.}
 
   proc builtin_bswap32(a: uint32): uint32 {.
-      importc: "_bswap", nodecl, nosideeffect.}
+      importc: "_bswap", nodecl, noSideEffect.}
 
   proc builtin_bswap64(a: uint64): uint64 {.
-      importc: "_bswap64", nodecl, nosideeffect.}
+      importc: "_bswap64", nodecl, noSideEffect.}
 elif defined(vcc):
   const useBuiltinSwap = true
   proc builtin_bswap16(a: uint16): uint16 {.
-      importc: "_byteswap_ushort", nodecl, header: "<intrin.h>", nosideeffect.}
+      importc: "_byteswap_ushort", nodecl, header: "<intrin.h>", noSideEffect.}
 
   proc builtin_bswap32(a: uint32): uint32 {.
-      importc: "_byteswap_ulong", nodecl, header: "<intrin.h>", nosideeffect.}
+      importc: "_byteswap_ulong", nodecl, header: "<intrin.h>", noSideEffect.}
 
   proc builtin_bswap64(a: uint64): uint64 {.
-      importc: "_byteswap_uint64", nodecl, header: "<intrin.h>", nosideeffect.}
+      importc: "_byteswap_uint64", nodecl, header: "<intrin.h>", noSideEffect.}
 else:
   const useBuiltinSwap = false
 
 when useBuiltinSwap:
-  proc swapEndian64*(outp, inp: pointer) {.inline, nosideeffect.}=
-    var i = cast[ptr uint64](inp)
-    var o = cast[ptr uint64](outp)
-    o[] = builtin_bswap64(i[])
+  template swapOpImpl(T: typedesc, op: untyped) =
+    ## We have to use `copyMem` here instead of a simple dereference because they
+    ## may point to a unaligned address. A sufficiently smart compiler _should_
+    ## be able to elide them when they're not necessary.
+    var tmp: T
+    copyMem(addr tmp, inp, sizeof(T))
+    tmp = op(tmp)
+    copyMem(outp, addr tmp, sizeof(T))
 
-  proc swapEndian32*(outp, inp: pointer) {.inline, nosideeffect.}=
-    var i = cast[ptr uint32](inp)
-    var o = cast[ptr uint32](outp)
-    o[] = builtin_bswap32(i[])
+  proc swapEndian64*(outp, inp: pointer) {.inline, noSideEffect.} =
+    ## Copies `inp` to `outp`, reversing the byte order.
+    ## Both buffers are supposed to contain at least 8 bytes.
+    runnableExamples:
+      var a = [1'u8, 2, 3, 4, 5, 6, 7, 8]
+      var b: array[8, uint8]
+      swapEndian64(addr b, addr a)
+      assert b == [8'u8, 7, 6, 5, 4, 3, 2, 1]
 
-  proc swapEndian16*(outp, inp: pointer) {.inline, nosideeffect.}=
-    var i = cast[ptr uint16](inp)
-    var o = cast[ptr uint16](outp)
-    o[] = builtin_bswap16(i[])
+    swapOpImpl(uint64, builtin_bswap64)
+
+  proc swapEndian32*(outp, inp: pointer) {.inline, noSideEffect.} =
+    ## Copies `inp` to `outp`, reversing the byte order.
+    ## Both buffers are supposed to contain at least 4 bytes.
+    runnableExamples:
+      var a = [1'u8, 2, 3, 4]
+      var b: array[4, uint8]
+      swapEndian32(addr b, addr a)
+      assert b == [4'u8, 3, 2, 1]
+
+    swapOpImpl(uint32, builtin_bswap32)
+
+  proc swapEndian16*(outp, inp: pointer) {.inline, noSideEffect.} =
+    ## Copies `inp` to `outp`, reversing the byte order.
+    ## Both buffers are supposed to contain at least 2 bytes.
+    runnableExamples:
+      var a = [1'u8, 2]
+      var b: array[2, uint8]
+      swapEndian16(addr b, addr a)
+      assert b == [2'u8, 1]
+
+    swapOpImpl(uint16, builtin_bswap16)
 
 else:
   proc swapEndian64*(outp, inp: pointer) =
-    ## copies `inp` to `outp` swapping bytes. Both buffers are supposed to
-    ## contain at least 8 bytes.
     var i = cast[cstring](inp)
     var o = cast[cstring](outp)
     o[0] = i[7]
@@ -75,8 +107,6 @@ else:
     o[7] = i[0]
 
   proc swapEndian32*(outp, inp: pointer) =
-    ## copies `inp` to `outp` swapping bytes. Both buffers are supposed to
-    ## contain at least 4 bytes.
     var i = cast[cstring](inp)
     var o = cast[cstring](outp)
     o[0] = i[3]
@@ -85,8 +115,6 @@ else:
     o[3] = i[0]
 
   proc swapEndian16*(outp, inp: pointer) =
-    ## copies `inp` to `outp` swapping bytes. Both buffers are supposed to
-    ## contain at least 2 bytes.
     var i = cast[cstring](inp)
     var o = cast[cstring](outp)
     o[0] = i[1]
@@ -101,8 +129,20 @@ when system.cpuEndian == bigEndian:
   proc bigEndian16*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 2)
 else:
   proc littleEndian64*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 8)
+    ## Copies `inp` to `outp`, storing it in 64-bit little-endian order.
+    ## Both buffers are supposed to contain at least 8 bytes.
   proc littleEndian32*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 4)
+    ## Copies `inp` to `outp`, storing it in 32-bit little-endian order.
+    ## Both buffers are supposed to contain at least 4 bytes.
   proc littleEndian16*(outp, inp: pointer){.inline.} = copyMem(outp, inp, 2)
+    ## Copies `inp` to `outp`, storing it in 16-bit little-endian order.
+    ## Both buffers are supposed to contain at least 2 bytes.
   proc bigEndian64*(outp, inp: pointer) {.inline.} = swapEndian64(outp, inp)
+    ## Copies `inp` to `outp`, storing it in 64-bit big-endian order.
+    ## Both buffers are supposed to contain at least 8 bytes.
   proc bigEndian32*(outp, inp: pointer) {.inline.} = swapEndian32(outp, inp)
+    ## Copies `inp` to `outp`, storing it in 32-bit big-endian order.
+    ## Both buffers are supposed to contain at least 4 bytes.
   proc bigEndian16*(outp, inp: pointer) {.inline.} = swapEndian16(outp, inp)
+    ## Copies `inp` to `outp`, storing it in 16-bit big-endian order.
+    ## Both buffers are supposed to contain at least 2 bytes.
diff --git a/lib/pure/events.nim b/lib/pure/events.nim
deleted file mode 100644
index a39dbe3bf..000000000
--- a/lib/pure/events.nim
+++ /dev/null
@@ -1,100 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2011 Alexander Mitchell-Robinson
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## :Author: Alexander Mitchell-Robinson (Amrykid)
-##
-## This module implements an event system that is not dependent on external
-## graphical toolkits. It was originally called ``NimEE`` because
-## it was inspired by Python's PyEE module. There are two ways you can use
-## events: one is a python-inspired way; the other is more of a C-style way.
-##
-## .. code-block:: Nim
-##    var ee = initEventEmitter()
-##    var genericargs: EventArgs
-##    proc handleevent(e: EventArgs) =
-##        echo("Handled!")
-##
-##    # Python way
-##    ee.on("EventName", handleevent)
-##    ee.emit("EventName", genericargs)
-##
-##    # C/Java way
-##    # Declare a type
-##    type
-##        SomeObject = object of RootObj
-##            SomeEvent: EventHandler
-##    var myobj: SomeObject
-##    myobj.SomeEvent = initEventHandler("SomeEvent")
-##    myobj.SomeEvent.addHandler(handleevent)
-##    ee.emit(myobj.SomeEvent, genericargs)
-
-type
-  EventArgs* = object of RootObj ## Base object for event arguments that are passed to callback functions.
-  EventHandler* = tuple[name: string, handlers: seq[proc(e: EventArgs) {.closure.}]] ## An eventhandler for an event.
-
-type
-  EventEmitter* = object ## An object that fires events and holds event handlers for an object.
-    s: seq[EventHandler]
-  EventError* = object of ValueError
-
-proc initEventHandler*(name: string): EventHandler =
-  ## Initializes an EventHandler with the specified name and returns it.
-  result.handlers = @[]
-  result.name = name
-
-proc addHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.}) =
-  ## Adds the callback to the specified event handler.
-  handler.handlers.add(fn)
-
-proc removeHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.}) =
-  ## Removes the callback from the specified event handler.
-  for i in countup(0, len(handler.handlers)-1):
-    if fn == handler.handlers[i]:
-      handler.handlers.del(i)
-      break
-
-proc containsHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.}): bool =
-  ## Checks if a callback is registered to this event handler.
-  return handler.handlers.contains(fn)
-
-
-proc clearHandlers*(handler: var EventHandler) =
-  ## Clears all of the callbacks from the event handler.
-  setLen(handler.handlers, 0)
-
-proc getEventHandler(emitter: var EventEmitter, event: string): int =
-  for k in 0..high(emitter.s):
-    if emitter.s[k].name == event: return k
-  return -1
-
-proc on*(emitter: var EventEmitter, event: string, fn: proc(e: EventArgs) {.closure.}) =
-  ## Assigns a event handler with the specified callback. If the event
-  ## doesn't exist, it will be created.
-  var i = getEventHandler(emitter, event)
-  if i < 0:
-    var eh = initEventHandler(event)
-    addHandler(eh, fn)
-    emitter.s.add(eh)
-  else:
-    addHandler(emitter.s[i], fn)
-
-proc emit*(emitter: var EventEmitter, eventhandler: var EventHandler,
-           args: EventArgs) =
-  ## Fires an event handler with specified event arguments.
-  for fn in items(eventhandler.handlers): fn(args)
-
-proc emit*(emitter: var EventEmitter, event: string, args: EventArgs) =
-  ## Fires an event handler with specified event arguments.
-  var i = getEventHandler(emitter, event)
-  if i >= 0:
-    emit(emitter, emitter.s[i], args)
-
-proc initEventEmitter*(): EventEmitter =
-  ## Creates and returns a new EventEmitter.
-  result.s = @[]
diff --git a/lib/pure/fenv.nim b/lib/pure/fenv.nim
index c946c4261..1d96fd6be 100644
--- a/lib/pure/fenv.nim
+++ b/lib/pure/fenv.nim
@@ -9,10 +9,10 @@
 
 ## Floating-point environment. Handling of floating-point rounding and
 ## exceptions (overflow, division by zero, etc.).
+## The types, vars and procs are bindings for the C standard library
+## [<fenv.h>](https://en.cppreference.com/w/c/numeric/fenv) header.
 
-{.deadCodeElim: on.}  # dce option deprecated
-
-when defined(Posix) and not defined(haiku):
+when defined(posix) and not defined(genode) and not defined(macosx):
   {.passl: "-lm".}
 
 var
@@ -37,8 +37,8 @@ var
   FE_UPWARD* {.importc, header: "<fenv.h>".}: cint
     ## round toward +Inf
   FE_DFL_ENV* {.importc, header: "<fenv.h>".}: cint
-    ## macro of type pointer to fenv_t to be used as the argument
-    ## to functions taking an argument of type fenv_t; in this
+    ## macro of type pointer to `fenv_t` to be used as the argument
+    ## to functions taking an argument of type `fenv_t`; in this
     ## case the default environment will be used
 
 type
@@ -102,89 +102,81 @@ proc feupdateenv*(envp: ptr Tfenv): cint {.importc, header: "<fenv.h>".}
   ## represented by object pointed to by `envp` and raise exceptions
   ## according to saved exceptions.
 
-const 
-  FLT_RADIX = 2 ## the radix of the exponent representation
-
-  FLT_MANT_DIG = 24 ## the number of base FLT_RADIX digits in the mantissa part of a float
-  FLT_DIG = 6 ## the number of digits of precision of a float
-  FLT_MIN_EXP = -125 # the minimum value of base FLT_RADIX in the exponent part of a float
-  FLT_MAX_EXP = 128 # the maximum value of base FLT_RADIX in the exponent part of a float
-  FLT_MIN_10_EXP = -37 ## the minimum value in base 10 of the exponent part of a float
-  FLT_MAX_10_EXP = 38 ## the maximum value in base 10 of the exponent part of a float
-  FLT_MIN = 1.17549435e-38'f32  ## the minimum value of a float
-  FLT_MAX = 3.40282347e+38'f32 ## the maximum value of a float
-  FLT_EPSILON = 1.19209290e-07'f32  ## the difference between 1 and the least value greater than 1 of a float
- 
-  DBL_MANT_DIG = 53 ## the number of base FLT_RADIX digits in the mantissa part of a double
-  DBL_DIG = 15 ## the number of digits of precision of a double
-  DBL_MIN_EXP = -1021 ## the minimum value of base FLT_RADIX in the exponent part of a double
-  DBL_MAX_EXP = 1024 ## the maximum value of base FLT_RADIX in the exponent part of a double
-  DBL_MIN_10_EXP = -307 ## the minimum value in base 10 of the exponent part of a double
-  DBL_MAX_10_EXP = 308 ## the maximum value in base 10 of the exponent part of a double
-  DBL_MIN = 2.2250738585072014E-308 ## the minimal value of a double
-  DBL_MAX = 1.7976931348623157E+308 ## the minimal value of a double
+const
+  FLT_RADIX = 2                     ## the radix of the exponent representation
+
+  FLT_MANT_DIG = 24                ## the number of base FLT_RADIX digits in the mantissa part of a float
+  FLT_DIG = 6                      ## the number of digits of precision of a float
+  FLT_MIN_EXP = -125               ## the minimum value of base FLT_RADIX in the exponent part of a float
+  FLT_MAX_EXP = 128                ## the maximum value of base FLT_RADIX in the exponent part of a float
+  FLT_MIN_10_EXP = -37             ## the minimum value in base 10 of the exponent part of a float
+  FLT_MAX_10_EXP = 38              ## the maximum value in base 10 of the exponent part of a float
+  FLT_MIN = 1.17549435e-38'f32     ## the minimum value of a float
+  FLT_MAX = 3.40282347e+38'f32     ## the maximum value of a float
+  FLT_EPSILON = 1.19209290e-07'f32 ## the difference between 1 and the least value greater than 1 of a float
+
+  DBL_MANT_DIG = 53                    ## the number of base FLT_RADIX digits in the mantissa part of a double
+  DBL_DIG = 15                         ## the number of digits of precision of a double
+  DBL_MIN_EXP = -1021                  ## the minimum value of base FLT_RADIX in the exponent part of a double
+  DBL_MAX_EXP = 1024                   ## the maximum value of base FLT_RADIX in the exponent part of a double
+  DBL_MIN_10_EXP = -307                ## the minimum value in base 10 of the exponent part of a double
+  DBL_MAX_10_EXP = 308                 ## the maximum value in base 10 of the exponent part of a double
+  DBL_MIN = 2.2250738585072014E-308    ## the minimal value of a double
+  DBL_MAX = 1.7976931348623157E+308    ## the minimal value of a double
   DBL_EPSILON = 2.2204460492503131E-16 ## the difference between 1 and the least value greater than 1 of a double
 
-template fpRadix* : int = FLT_RADIX
+template fpRadix*: int = FLT_RADIX
   ## The (integer) value of the radix used to represent any floating
   ## point type on the architecture used to build the program.
 
-template mantissaDigits*(T : typedesc[float32]) : int = FLT_MANT_DIG
-  ## Number of digits (in base ``floatingPointRadix``) in the mantissa
+template mantissaDigits*(T: typedesc[float32]): int = FLT_MANT_DIG
+  ## Number of digits (in base `floatingPointRadix`) in the mantissa
   ## of 32-bit floating-point numbers.
-template digits*(T : typedesc[float32]) : int = FLT_DIG
+template digits*(T: typedesc[float32]): int = FLT_DIG
   ## Number of decimal digits that can be represented in a
   ## 32-bit floating-point type without losing precision.
-template minExponent*(T : typedesc[float32]) : int = FLT_MIN_EXP
+template minExponent*(T: typedesc[float32]): int = FLT_MIN_EXP
   ## Minimum (negative) exponent for 32-bit floating-point numbers.
-template maxExponent*(T : typedesc[float32]) : int = FLT_MAX_EXP
+template maxExponent*(T: typedesc[float32]): int = FLT_MAX_EXP
   ## Maximum (positive) exponent for 32-bit floating-point numbers.
-template min10Exponent*(T : typedesc[float32]) : int = FLT_MIN_10_EXP
+template min10Exponent*(T: typedesc[float32]): int = FLT_MIN_10_EXP
   ## Minimum (negative) exponent in base 10 for 32-bit floating-point
   ## numbers.
-template max10Exponent*(T : typedesc[float32]) : int = FLT_MAX_10_EXP
+template max10Exponent*(T: typedesc[float32]): int = FLT_MAX_10_EXP
   ## Maximum (positive) exponent in base 10 for 32-bit floating-point
   ## numbers.
-template minimumPositiveValue*(T : typedesc[float32]) : float32 = FLT_MIN
+template minimumPositiveValue*(T: typedesc[float32]): float32 = FLT_MIN
   ## The smallest positive (nonzero) number that can be represented in a
   ## 32-bit floating-point type.
-template maximumPositiveValue*(T : typedesc[float32]) : float32 = FLT_MAX
+template maximumPositiveValue*(T: typedesc[float32]): float32 = FLT_MAX
   ## The largest positive number that can be represented in a 32-bit
   ## floating-point type.
-template epsilon*(T : typedesc[float32]): float32 = FLT_EPSILON
+template epsilon*(T: typedesc[float32]): float32 = FLT_EPSILON
   ## The difference between 1.0 and the smallest number greater than
   ## 1.0 that can be represented in a 32-bit floating-point type.
 
-template mantissaDigits*(T : typedesc[float64]) : int = DBL_MANT_DIG
-  ## Number of digits (in base ``floatingPointRadix``) in the mantissa
+template mantissaDigits*(T: typedesc[float64]): int = DBL_MANT_DIG
+  ## Number of digits (in base `floatingPointRadix`) in the mantissa
   ## of 64-bit floating-point numbers.
-template digits*(T : typedesc[float64]) : int = DBL_DIG
+template digits*(T: typedesc[float64]): int = DBL_DIG
   ## Number of decimal digits that can be represented in a
   ## 64-bit floating-point type without losing precision.
-template minExponent*(T : typedesc[float64]) : int = DBL_MIN_EXP
+template minExponent*(T: typedesc[float64]): int = DBL_MIN_EXP
   ## Minimum (negative) exponent for 64-bit floating-point numbers.
-template maxExponent*(T : typedesc[float64]) : int = DBL_MAX_EXP
+template maxExponent*(T: typedesc[float64]): int = DBL_MAX_EXP
   ## Maximum (positive) exponent for 64-bit floating-point numbers.
-template min10Exponent*(T : typedesc[float64]) : int = DBL_MIN_10_EXP
+template min10Exponent*(T: typedesc[float64]): int = DBL_MIN_10_EXP
   ## Minimum (negative) exponent in base 10 for 64-bit floating-point
   ## numbers.
-template max10Exponent*(T : typedesc[float64]) : int = DBL_MAX_10_EXP
+template max10Exponent*(T: typedesc[float64]): int = DBL_MAX_10_EXP
   ## Maximum (positive) exponent in base 10 for 64-bit floating-point
   ## numbers.
-template minimumPositiveValue*(T : typedesc[float64]) : float64 = DBL_MIN
+template minimumPositiveValue*(T: typedesc[float64]): float64 = DBL_MIN
   ## The smallest positive (nonzero) number that can be represented in a
   ## 64-bit floating-point type.
-template maximumPositiveValue*(T : typedesc[float64]) : float64 = DBL_MAX
+template maximumPositiveValue*(T: typedesc[float64]): float64 = DBL_MAX
   ## The largest positive number that can be represented in a 64-bit
   ## floating-point type.
-template epsilon*(T : typedesc[float64]): float64 = DBL_EPSILON
+template epsilon*(T: typedesc[float64]): float64 = DBL_EPSILON
   ## The difference between 1.0 and the smallest number greater than
   ## 1.0 that can be represented in a 64-bit floating-point type.
-
-
-when isMainModule:
-  func is_significant(x: float): bool = 
-    if x > minimumPositiveValue(float) and x < maximumPositiveValue(float): true
-    else: false
-
-  doAssert is_significant(10.0)
diff --git a/lib/pure/future.nim b/lib/pure/future.nim
deleted file mode 100644
index 40dba7846..000000000
--- a/lib/pure/future.nim
+++ /dev/null
@@ -1,4 +0,0 @@
-
-{.deprecated: "Use the new 'sugar' module instead".}
-
-include sugar
diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim
index 6535bb0d3..1038d55a1 100644
--- a/lib/pure/hashes.nim
+++ b/lib/pure/hashes.nim
@@ -9,63 +9,188 @@
 
 ## This module implements efficient computations of hash values for diverse
 ## Nim types. All the procs are based on these two building blocks:
-## - `!& proc <#!&>`_ used to start or mix a hash value, and
-## - `!$ proc <#!$>`_ used to *finish* the hash value.
-## If you want to implement hash procs for
-## your custom types you will end up writing the following kind of skeleton of
-## code:
+## - `!& proc <#!&,Hash,int>`_ used to start or mix a hash value, and
+## - `!$ proc <#!$,Hash>`_ used to finish the hash value.
 ##
-## .. code-block:: Nim
-##  proc hash(x: Something): Hash =
-##    ## Computes a Hash from `x`.
-##    var h: Hash = 0
-##    # Iterate over parts of `x`.
-##    for xAtom in x:
-##      # Mix the atom with the partial hash.
-##      h = h !& xAtom
-##    # Finish the hash.
-##    result = !$h
+## If you want to implement hash procs for your custom types,
+## you will end up writing the following kind of skeleton of code:
+
+runnableExamples:
+  type
+    Something = object
+      foo: int
+      bar: string
+
+  iterator items(x: Something): Hash =
+    yield hash(x.foo)
+    yield hash(x.bar)
+
+  proc hash(x: Something): Hash =
+    ## Computes a Hash from `x`.
+    var h: Hash = 0
+    # Iterate over parts of `x`.
+    for xAtom in x:
+      # Mix the atom with the partial hash.
+      h = h !& xAtom
+    # Finish the hash.
+    result = !$h
+
+## If your custom types contain fields for which there already is a `hash` proc,
+## you can simply hash together the hash values of the individual fields:
+
+runnableExamples:
+  type
+    Something = object
+      foo: int
+      bar: string
+
+  proc hash(x: Something): Hash =
+    ## Computes a Hash from `x`.
+    var h: Hash = 0
+    h = h !& hash(x.foo)
+    h = h !& hash(x.bar)
+    result = !$h
+
+## .. important:: Use `-d:nimPreviewHashRef` to
+##    enable hashing `ref`s. It is expected that this behavior
+##    becomes the new default in upcoming versions.
 ##
-## If your custom types contain fields for which there already is a hash proc,
-## like for example objects made up of ``strings``, you can simply hash
-## together the hash value of the individual fields:
+## .. note:: If the type has a `==` operator, the following must hold:
+##    If two values compare equal, their hashes must also be equal.
 ##
-## .. code-block:: Nim
-##  proc hash(x: Something): Hash =
-##    ## Computes a Hash from `x`.
-##    var h: Hash = 0
-##    h = h !& hash(x.foo)
-##    h = h !& hash(x.bar)
-##    result = !$h
+## See also
+## ========
+## * `md5 module <md5.html>`_ for the MD5 checksum algorithm
+## * `base64 module <base64.html>`_ for a Base64 encoder and decoder
+## * `sha1 module <sha1.html>`_ for the SHA-1 checksum algorithm
+## * `tables module <tables.html>`_ for hash tables
+
+import std/private/since
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
-import
-  strutils
 
 type
-  Hash* = int ## a hash value; hash tables using these values should
-               ## always have a size of a power of two and can use the ``and``
-               ## operator instead of ``mod`` for truncation of the hash value.
+  Hash* = int ## A hash value. Hash tables using these values should
+              ## always have a size of a power of two so they can use the `and`
+              ## operator instead of `mod` for truncation of the hash value.
 
 proc `!&`*(h: Hash, val: int): Hash {.inline.} =
-  ## mixes a hash value `h` with `val` to produce a new hash value. This is
-  ## only needed if you need to implement a hash proc for a new datatype.
-  result = h +% val
-  result = result +% result shl 10
-  result = result xor (result shr 6)
+  ## Mixes a hash value `h` with `val` to produce a new hash value.
+  ##
+  ## This is only needed if you need to implement a `hash` proc for a new datatype.
+  let h = cast[uint](h)
+  let val = cast[uint](val)
+  var res = h + val
+  res = res + res shl 10
+  res = res xor (res shr 6)
+  result = cast[Hash](res)
 
 proc `!$`*(h: Hash): Hash {.inline.} =
-  ## finishes the computation of the hash value. This is
-  ## only needed if you need to implement a hash proc for a new datatype.
-  result = h +% h shl 3
-  result = result xor (result shr 11)
-  result = result +% result shl 15
+  ## Finishes the computation of the hash value.
+  ##
+  ## This is only needed if you need to implement a `hash` proc for a new datatype.
+  let h = cast[uint](h) # Hash is practically unsigned.
+  var res = h + h shl 3
+  res = res xor (res shr 11)
+  res = res + res shl 15
+  result = cast[Hash](res)
+
+proc hiXorLoFallback64(a, b: uint64): uint64 {.inline.} =
+  let # Fall back in 64-bit arithmetic
+    aH = a shr 32
+    aL = a and 0xFFFFFFFF'u64
+    bH = b shr 32
+    bL = b and 0xFFFFFFFF'u64
+    rHH = aH * bH
+    rHL = aH * bL
+    rLH = aL * bH
+    rLL = aL * bL
+    t = rLL + (rHL shl 32)
+  var c = if t < rLL: 1'u64 else: 0'u64
+  let lo = t + (rLH shl 32)
+  c += (if lo < t: 1'u64 else: 0'u64)
+  let hi = rHH + (rHL shr 32) + (rLH shr 32) + c
+  return hi xor lo
+
+proc hiXorLo(a, b: uint64): uint64 {.inline.} =
+  # XOR of the high & low 8 bytes of the full 16 byte product.
+  when nimvm:
+    result = hiXorLoFallback64(a, b) # `result =` is necessary here.
+  else:
+    when Hash.sizeof < 8:
+      result = hiXorLoFallback64(a, b)
+    elif defined(gcc) or defined(llvm_gcc) or defined(clang):
+      {.emit: """__uint128_t r = `a`; r *= `b`; `result` = (r >> 64) ^ r;""".}
+    elif defined(windows) and not defined(tcc):
+      proc umul128(a, b: uint64, c: ptr uint64): uint64 {.importc: "_umul128", header: "intrin.h".}
+      var b = b
+      let c = umul128(a, b, addr b)
+      result = c xor b
+    else:
+      result = hiXorLoFallback64(a, b)
+
+when defined(js):
+  import std/jsbigints
+  import std/private/jsutils
+
+  proc hiXorLoJs(a, b: JsBigInt): JsBigInt =
+    let
+      prod = a * b
+      mask = big"0xffffffffffffffff" # (big"1" shl big"64") - big"1"
+    result = (prod shr big"64") xor (prod and mask)
+
+  template hashWangYiJS(x: JsBigInt): Hash =
+    let
+      P0 = big"0xa0761d6478bd642f"
+      P1 = big"0xe7037ed1a0b428db"
+      P58 = big"0xeb44accab455d16d" # big"0xeb44accab455d165" xor big"8"
+      res = hiXorLoJs(hiXorLoJs(P0, x xor P1), P58)
+    cast[Hash](toNumber(wrapToInt(res, 32)))
+
+  template toBits(num: float): JsBigInt =
+    let
+      x = newArrayBuffer(8)
+      y = newFloat64Array(x)
+    if hasBigUint64Array():
+      let z = newBigUint64Array(x)
+      y[0] = num
+      z[0]
+    else:
+      let z = newUint32Array(x)
+      y[0] = num
+      big(z[0]) + big(z[1]) shl big(32)
+
+proc hashWangYi1*(x: int64|uint64|Hash): Hash {.inline.} =
+  ## Wang Yi's hash_v1 for 64-bit ints (see https://github.com/rurban/smhasher for
+  ## more details). This passed all scrambling tests in Spring 2019 and is simple.
+  ##
+  ## **Note:** It's ok to define `proc(x: int16): Hash = hashWangYi1(Hash(x))`.
+  const P0  = 0xa0761d6478bd642f'u64
+  const P1  = 0xe7037ed1a0b428db'u64
+  const P58 = 0xeb44accab455d165'u64 xor 8'u64
+  template h(x): untyped = hiXorLo(hiXorLo(P0, uint64(x) xor P1), P58)
+  when nimvm:
+    when defined(js): # Nim int64<->JS Number & VM match => JS gets 32-bit hash
+      result = cast[Hash](h(x)) and cast[Hash](0xFFFFFFFF)
+    else:
+      result = cast[Hash](h(x))
+  else:
+    when defined(js):
+      if hasJsBigInt():
+        result = hashWangYiJS(big(x))
+      else:
+        result = cast[Hash](x) and cast[Hash](0xFFFFFFFF)
+    else:
+      result = cast[Hash](h(x))
 
 proc hashData*(data: pointer, size: int): Hash =
-  ## hashes an array of bytes of size `size`
+  ## Hashes an array of bytes of size `size`.
   var h: Hash = 0
   when defined(js):
     var p: cstring
-    asm """`p` = `Data`;"""
+    {.emit: """`p` = `Data`;""".}
   else:
     var p = cast[cstring](data)
   var i = 0
@@ -76,13 +201,23 @@ proc hashData*(data: pointer, size: int): Hash =
     dec(s)
   result = !$h
 
+proc hashIdentity*[T: Ordinal|enum](x: T): Hash {.inline, since: (1, 3).} =
+  ## The identity hash, i.e. `hashIdentity(x) = x`.
+  cast[Hash](ord(x))
+
+when defined(nimIntHash1):
+  proc hash*[T: Ordinal|enum](x: T): Hash {.inline.} =
+    ## Efficient hashing of integers.
+    cast[Hash](ord(x))
+else:
+  proc hash*[T: Ordinal|enum](x: T): Hash {.inline.} =
+    ## Efficient hashing of integers.
+    hashWangYi1(uint64(ord(x)))
+
 when defined(js):
   var objectID = 0
-
-proc hash*(x: pointer): Hash {.inline.} =
-  ## efficient hashing of pointers
-  when defined(js):
-    asm """
+  proc getObjectId(x: pointer): int =
+    {.emit: """
       if (typeof `x` == "object") {
         if ("_NimID" in `x`)
           `result` = `x`["_NimID"];
@@ -91,75 +226,378 @@ proc hash*(x: pointer): Hash {.inline.} =
           `x`["_NimID"] = `result`;
         }
       }
-    """
+    """.}
+
+proc hash*(x: pointer): Hash {.inline.} =
+  ## Efficient `hash` overload.
+  when defined(js):
+    let y = getObjectId(x)
   else:
-    result = (cast[Hash](x)) shr 3 # skip the alignment
+    let y = cast[int](x)
+  hash(y) # consistent with code expecting scrambled hashes depending on `nimIntHash1`.
+
+proc hash*[T](x: ptr[T]): Hash {.inline.} =
+  ## Efficient `hash` overload.
+  runnableExamples:
+    var a: array[10, uint8]
+    assert a[0].addr.hash != a[1].addr.hash
+    assert cast[pointer](a[0].addr).hash == a[0].addr.hash
+  hash(cast[pointer](x))
+
+when defined(nimPreviewHashRef) or defined(nimdoc):
+  proc hash*[T](x: ref[T]): Hash {.inline.} =
+    ## Efficient `hash` overload.
+    ##
+    ## .. important:: Use `-d:nimPreviewHashRef` to
+    ##    enable hashing `ref`s. It is expected that this behavior
+    ##    becomes the new default in upcoming versions.
+    runnableExamples("-d:nimPreviewHashRef"):
+      type A = ref object
+        x: int
+      let a = A(x: 3)
+      let ha = a.hash
+      assert ha != A(x: 3).hash # A(x: 3) is a different ref object from `a`.
+      a.x = 4
+      assert ha == a.hash # the hash only depends on the address
+    runnableExamples("-d:nimPreviewHashRef"):
+      # you can overload `hash` if you want to customize semantics
+      type A[T] = ref object
+        x, y: T
+      proc hash(a: A): Hash = hash(a.x)
+      assert A[int](x: 3, y: 4).hash == A[int](x: 3, y: 5).hash
+    # xxx pending bug #17733, merge as `proc hash*(pointer | ref | ptr): Hash`
+    # or `proc hash*[T: ref | ptr](x: T): Hash`
+    hash(cast[pointer](x))
 
-when not defined(booting):
-  proc hash*[T: proc](x: T): Hash {.inline.} =
-    ## efficient hashing of proc vars; closures are supported too.
-    when T is "closure":
-      result = hash(rawProc(x)) !& hash(rawEnv(x))
+proc hash*(x: float): Hash {.inline.} =
+  ## Efficient hashing of floats.
+  let y = x + 0.0 # for denormalization
+  when nimvm:
+    # workaround a JS VM bug: bug #16547
+    result = hashWangYi1(cast[int64](float64(y)))
+  else:
+    when not defined(js):
+      result = hashWangYi1(cast[Hash](y))
     else:
-      result = hash(pointer(x))
-
-proc hash*(x: int): Hash {.inline.} =
-  ## efficient hashing of integers
-  result = x
+      result = hashWangYiJS(toBits(y))
 
-proc hash*(x: int64): Hash {.inline.} =
-  ## efficient hashing of int64 integers
-  result = toU32(x)
-
-proc hash*(x: uint): Hash {.inline.} =
-  ## efficient hashing of unsigned integers
-  result = cast[int](x)
+# Forward declarations before methods that hash containers. This allows
+# containers to contain other containers
+proc hash*[A](x: openArray[A]): Hash
+proc hash*[A](x: set[A]): Hash
 
-proc hash*(x: uint64): Hash {.inline.} =
-  ## efficient hashing of uint64 integers
-  result = toU32(cast[int](x))
 
-proc hash*(x: char): Hash {.inline.} =
-  ## efficient hashing of characters
-  result = ord(x)
+when defined(js):
+  proc imul(a, b: uint32): uint32 =
+    # https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul
+    let mask = 0xffff'u32
+    var
+      aHi = (a shr 16) and mask
+      aLo = a and mask
+      bHi = (b shr 16) and mask
+      bLo = b and mask
+    result = (aLo * bLo) + (aHi * bLo + aLo * bHi) shl 16
+else:
+  template imul(a, b: uint32): untyped = a * b
+
+proc rotl32(x: uint32, r: int): uint32 {.inline.} =
+  (x shl r) or (x shr (32 - r))
+
+proc murmurHash(x: openArray[byte]): Hash =
+  # https://github.com/PeterScott/murmur3/blob/master/murmur3.c
+  const
+    c1 = 0xcc9e2d51'u32
+    c2 = 0x1b873593'u32
+    n1 = 0xe6546b64'u32
+    m1 = 0x85ebca6b'u32
+    m2 = 0xc2b2ae35'u32
+  let
+    size = len(x)
+    stepSize = 4 # 32-bit
+    n = size div stepSize
+  var
+    h1: uint32
+    i = 0
+
+
+  template impl =
+    var j = stepSize
+    while j > 0:
+      dec j
+      k1 = (k1 shl 8) or (ord(x[i+j])).uint32
+
+  # body
+  while i < n * stepSize:
+    var k1: uint32
+
+    when nimvm:
+      impl()
+    else:
+      when declared(copyMem):
+        copyMem(addr k1, addr x[i], 4)
+      else:
+        impl()
+    inc i, stepSize
+
+    k1 = imul(k1, c1)
+    k1 = rotl32(k1, 15)
+    k1 = imul(k1, c2)
+
+    h1 = h1 xor k1
+    h1 = rotl32(h1, 13)
+    h1 = h1*5 + n1
+
+  # tail
+  var k1: uint32
+  var rem = size mod stepSize
+  while rem > 0:
+    dec rem
+    k1 = (k1 shl 8) or (ord(x[i+rem])).uint32
+  k1 = imul(k1, c1)
+  k1 = rotl32(k1, 15)
+  k1 = imul(k1, c2)
+  h1 = h1 xor k1
+
+  # finalization
+  h1 = h1 xor size.uint32
+  h1 = h1 xor (h1 shr 16)
+  h1 = imul(h1, m1)
+  h1 = h1 xor (h1 shr 13)
+  h1 = imul(h1, m2)
+  h1 = h1 xor (h1 shr 16)
+  return cast[Hash](h1)
+
+proc hashVmImpl(x: cstring, sPos, ePos: int): Hash =
+  raiseAssert "implementation override in compiler/vmops.nim"
+
+proc hashVmImpl(x: string, sPos, ePos: int): Hash =
+  raiseAssert "implementation override in compiler/vmops.nim"
+
+proc hashVmImplChar(x: openArray[char], sPos, ePos: int): Hash =
+  raiseAssert "implementation override in compiler/vmops.nim"
+
+proc hashVmImplByte(x: openArray[byte], sPos, ePos: int): Hash =
+  raiseAssert "implementation override in compiler/vmops.nim"
+
+const k0 = 0xc3a5c85c97cb3127u64 # Primes on (2^63, 2^64) for various uses
+const k1 = 0xb492b66fbe98f273u64
+const k2 = 0x9ae16a3b2f90404fu64
+
+proc load4e(s: openArray[byte], o=0): uint32 {.inline.} =
+  uint32(s[o + 3]) shl 24 or uint32(s[o + 2]) shl 16 or
+  uint32(s[o + 1]) shl  8 or uint32(s[o + 0])
+
+proc load8e(s: openArray[byte], o=0): uint64 {.inline.} =
+  uint64(s[o + 7]) shl 56 or uint64(s[o + 6]) shl 48 or
+  uint64(s[o + 5]) shl 40 or uint64(s[o + 4]) shl 32 or
+  uint64(s[o + 3]) shl 24 or uint64(s[o + 2]) shl 16 or
+  uint64(s[o + 1]) shl  8 or uint64(s[o + 0])
+
+proc load4(s: openArray[byte], o=0): uint32 {.inline.} =
+  when nimvm: result = load4e(s, o)
+  else:
+    when declared copyMem: copyMem result.addr, s[o].addr, result.sizeof
+    else: result = load4e(s, o)
 
-proc hash*[T: Ordinal](x: T): Hash {.inline.} =
-  ## efficient hashing of other ordinal types (e.g., enums)
-  result = ord(x)
+proc load8(s: openArray[byte], o=0): uint64 {.inline.} =
+  when nimvm: result = load8e(s, o)
+  else:
+    when declared copyMem: copyMem result.addr, s[o].addr, result.sizeof
+    else: result = load8e(s, o)
+
+proc lenU(s: openArray[byte]): uint64 {.inline.} = s.len.uint64
+
+proc shiftMix(v: uint64): uint64 {.inline.} = v xor (v shr 47)
+
+proc rotR(v: uint64; bits: cint): uint64 {.inline.} =
+  (v shr bits) or (v shl (64 - bits))
+
+proc len16(u: uint64; v: uint64; mul: uint64): uint64 {.inline.} =
+  var a = (u xor v)*mul
+  a = a xor (a shr 47)
+  var b = (v xor a)*mul
+  b = b xor (b shr 47)
+  b*mul
+
+proc len0_16(s: openArray[byte]): uint64 {.inline.} =
+  if s.len >= 8:
+    let mul = k2 + 2*s.lenU
+    let a   = load8(s) + k2
+    let b   = load8(s, s.len - 8)
+    let c   = rotR(b, 37)*mul + a
+    let d   = (rotR(a, 25) + b)*mul
+    len16 c, d, mul
+  elif s.len >= 4:
+    let mul = k2 + 2*s.lenU
+    let a   = load4(s).uint64
+    len16 s.lenU + (a shl 3), load4(s, s.len - 4), mul
+  elif s.len > 0:
+    let a = uint32(s[0])
+    let b = uint32(s[s.len shr 1])
+    let c = uint32(s[s.len - 1])
+    let y = a      + (b shl 8)
+    let z = s.lenU + (c shl 2)
+    shiftMix(y*k2 xor z*k0)*k2
+  else: k2      # s.len == 0
+
+proc len17_32(s: openArray[byte]): uint64 {.inline.} =
+  let mul = k2 + 2*s.lenU
+  let a = load8(s)*k1
+  let b = load8(s, 8)
+  let c = load8(s, s.len - 8)*mul
+  let d = load8(s, s.len - 16)*k2
+  len16 rotR(a + b, 43) + rotR(c, 30) + d, a + rotR(b + k2, 18) + c, mul
+
+proc len33_64(s: openArray[byte]): uint64 {.inline.} =
+  let mul = k2 + 2*s.lenU
+  let a = load8(s)*k2
+  let b = load8(s, 8)
+  let c = load8(s, s.len - 8)*mul
+  let d = load8(s, s.len - 16)*k2
+  let y = rotR(a + b, 43) + rotR(c, 30) + d
+  let z = len16(y, a + rotR(b + k2, 18) + c, mul)
+  let e = load8(s, 16)*mul
+  let f = load8(s, 24)
+  let g = (y + load8(s, s.len - 32))*mul
+  let h = (z + load8(s, s.len - 24))*mul
+  len16 rotR(e + f, 43) + rotR(g, 30) + h, e + rotR(f + a, 18) + g, mul
+
+type Pair = tuple[first, second: uint64]
+
+proc weakLen32withSeeds2(w, x, y, z, a, b: uint64): Pair {.inline.} =
+  var a = a + w
+  var b = rotR(b + a + z, 21)
+  let c = a
+  a += x
+  a += y
+  b += rotR(a, 44)
+  result[0] = a + z
+  result[1] = b + c
+
+proc weakLen32withSeeds(s: openArray[byte]; o: int; a,b: uint64): Pair {.inline.} =
+  weakLen32withSeeds2 load8(s, o     ), load8(s, o + 8),
+                      load8(s, o + 16), load8(s, o + 24), a, b
+
+proc hashFarm(s: openArray[byte]): uint64 {.inline.} =
+  if s.len <= 16: return len0_16(s)
+  if s.len <= 32: return len17_32(s)
+  if s.len <= 64: return len33_64(s)
+  const seed = 81u64 # not const to use input `h`
+  var
+    o = 0         # s[] ptr arith -> variable origin variable `o`
+    x = seed
+    y = seed*k1 + 113
+    z = shiftMix(y*k2 + 113)*k2
+    v, w: Pair
+  x = x*k2 + load8(s)
+  let eos    = ((s.len - 1) div 64)*64
+  let last64 = eos + ((s.len - 1) and 63) - 63
+  while true:
+    x = rotR(x + y + v[0] + load8(s, o+8), 37)*k1
+    y = rotR(y + v[1] + load8(s, o+48), 42)*k1
+    x = x xor w[1]
+    y += v[0] + load8(s, o+40)
+    z = rotR(z + w[0], 33)*k1
+    v = weakLen32withSeeds(s, o+0 , v[1]*k1, x + w[0])
+    w = weakLen32withSeeds(s, o+32, z + w[1], y + load8(s, o+16))
+    swap z, x
+    inc o, 64
+    if o == eos: break
+  let mul = k1 + ((z and 0xff) shl 1)
+  o = last64
+  w[0] += (s.lenU - 1) and 63
+  v[0] += w[0]
+  w[0] += v[0]
+  x = rotR(x + y + v[0] + load8(s, o+8), 37)*mul
+  y = rotR(y + v[1] + load8(s, o+48), 42)*mul
+  x = x xor w[1]*9
+  y += v[0]*9 + load8(s, o+40)
+  z = rotR(z + w[0], 33)*mul
+  v = weakLen32withSeeds(s, o+0 , v[1]*mul, x + w[0])
+  w = weakLen32withSeeds(s, o+32, z + w[1], y + load8(s, o+16))
+  swap z, x
+  len16 len16(v[0],w[0],mul) + shiftMix(y)*k0 + z, len16(v[1],w[1],mul) + x, mul
+
+template jsNoInt64: untyped =
+  when defined js:
+    when compiles(compileOption("jsbigint64")):
+      when not compileOption("jsbigint64"): true
+      else: false
+    else: false
+  else: false
+const sHash2 = (when defined(nimStringHash2) or jsNoInt64(): true else: false)
+
+template maybeFailJS_Number =
+  when jsNoInt64() and not defined(nimStringHash2):
+    {.error: "Must use `-d:nimStringHash2` when using `--jsbigint64:off`".}
 
 proc hash*(x: string): Hash =
-  ## efficient hashing of strings
-  var h: Hash = 0
-  for i in 0..x.len-1:
-    h = h !& ord(x[i])
-  result = !$h
+  ## Efficient hashing of strings.
+  ##
+  ## **See also:**
+  ## * `hashIgnoreStyle <#hashIgnoreStyle,string>`_
+  ## * `hashIgnoreCase <#hashIgnoreCase,string>`_
+  runnableExamples:
+    doAssert hash("abracadabra") != hash("AbracadabrA")
+  maybeFailJS_Number()
+  when not sHash2:
+    result = cast[Hash](hashFarm(toOpenArrayByte(x, 0, x.high)))
+  else:
+    #when nimvm:
+    #  result = hashVmImpl(x, 0, high(x))
+    when true:
+      result = murmurHash(toOpenArrayByte(x, 0, high(x)))
 
 proc hash*(x: cstring): Hash =
-  ## efficient hashing of null-terminated strings
-  var h: Hash = 0
-  var i = 0
-  when defined(js):
-    while i < x.len:
-      h = h !& ord(x[i])
-      inc i
+  ## Efficient hashing of null-terminated strings.
+  runnableExamples:
+    doAssert hash(cstring"abracadabra") == hash("abracadabra")
+    doAssert hash(cstring"AbracadabrA") == hash("AbracadabrA")
+    doAssert hash(cstring"abracadabra") != hash(cstring"AbracadabrA")
+
+  maybeFailJS_Number()
+  when not sHash2:
+    when defined js:
+      let xx = $x
+      result = cast[Hash](hashFarm(toOpenArrayByte(xx, 0, xx.high)))
+    else:
+      result = cast[Hash](hashFarm(toOpenArrayByte(x, 0, x.high)))
   else:
-    while x[i] != 0.char:
-      h = h !& ord(x[i])
-      inc i
-  result = !$h
+    #when nimvm:
+    #  result = hashVmImpl(x, 0, high(x))
+    when true:
+      when not defined(js):
+        result = murmurHash(toOpenArrayByte(x, 0, x.high))
+      else:
+        let xx = $x
+        result = murmurHash(toOpenArrayByte(xx, 0, high(xx)))
 
 proc hash*(sBuf: string, sPos, ePos: int): Hash =
-  ## efficient hashing of a string buffer, from starting
-  ## position `sPos` to ending position `ePos`
+  ## Efficient hashing of a string buffer, from starting
+  ## position `sPos` to ending position `ePos` (included).
   ##
-  ## ``hash(myStr, 0, myStr.high)`` is equivalent to ``hash(myStr)``
-  var h: Hash = 0
-  for i in sPos..ePos:
-    h = h !& ord(sBuf[i])
-  result = !$h
+  ## `hash(myStr, 0, myStr.high)` is equivalent to `hash(myStr)`.
+  runnableExamples:
+    var a = "abracadabra"
+    doAssert hash(a, 0, 3) == hash(a, 7, 10)
+
+  maybeFailJS_Number()
+  when not sHash2:
+    result = cast[Hash](hashFarm(toOpenArrayByte(sBuf, sPos, ePos)))
+  else:
+    murmurHash(toOpenArrayByte(sBuf, sPos, ePos))
 
 proc hashIgnoreStyle*(x: string): Hash =
-  ## efficient hashing of strings; style is ignored
+  ## Efficient hashing of strings; style is ignored.
+  ##
+  ## **Note:** This uses a different hashing algorithm than `hash(string)`.
+  ##
+  ## **See also:**
+  ## * `hashIgnoreCase <#hashIgnoreCase,string>`_
+  runnableExamples:
+    doAssert hashIgnoreStyle("aBr_aCa_dAB_ra") == hashIgnoreStyle("abracadabra")
+    doAssert hashIgnoreStyle("abcdefghi") != hash("abcdefghi")
+
   var h: Hash = 0
   var i = 0
   let xLen = x.len
@@ -172,15 +610,20 @@ proc hashIgnoreStyle*(x: string): Hash =
         c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
       h = h !& ord(c)
       inc(i)
-
   result = !$h
 
 proc hashIgnoreStyle*(sBuf: string, sPos, ePos: int): Hash =
-  ## efficient hashing of a string buffer, from starting
-  ## position `sPos` to ending position `ePos`; style is ignored
+  ## Efficient hashing of a string buffer, from starting
+  ## position `sPos` to ending position `ePos` (included); style is ignored.
+  ##
+  ## **Note:** This uses a different hashing algorithm than `hash(string)`.
   ##
-  ## ``hashIgnoreStyle(myBuf, 0, myBuf.high)`` is equivalent
-  ## to ``hashIgnoreStyle(myBuf)``
+  ## `hashIgnoreStyle(myBuf, 0, myBuf.high)` is equivalent
+  ## to `hashIgnoreStyle(myBuf)`.
+  runnableExamples:
+    var a = "ABracada_b_r_a"
+    doAssert hashIgnoreStyle(a, 0, 3) == hashIgnoreStyle(a, 7, a.high)
+
   var h: Hash = 0
   var i = sPos
   while i <= ePos:
@@ -195,7 +638,16 @@ proc hashIgnoreStyle*(sBuf: string, sPos, ePos: int): Hash =
   result = !$h
 
 proc hashIgnoreCase*(x: string): Hash =
-  ## efficient hashing of strings; case is ignored
+  ## Efficient hashing of strings; case is ignored.
+  ##
+  ## **Note:** This uses a different hashing algorithm than `hash(string)`.
+  ##
+  ## **See also:**
+  ## * `hashIgnoreStyle <#hashIgnoreStyle,string>`_
+  runnableExamples:
+    doAssert hashIgnoreCase("ABRAcaDABRA") == hashIgnoreCase("abRACAdabra")
+    doAssert hashIgnoreCase("abcdefghi") != hash("abcdefghi")
+
   var h: Hash = 0
   for i in 0..x.len-1:
     var c = x[i]
@@ -205,11 +657,17 @@ proc hashIgnoreCase*(x: string): Hash =
   result = !$h
 
 proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash =
-  ## efficient hashing of a string buffer, from starting
-  ## position `sPos` to ending position `ePos`; case is ignored
+  ## Efficient hashing of a string buffer, from starting
+  ## position `sPos` to ending position `ePos` (included); case is ignored.
+  ##
+  ## **Note:** This uses a different hashing algorithm than `hash(string)`.
   ##
-  ## ``hashIgnoreCase(myBuf, 0, myBuf.high)`` is equivalent
-  ## to ``hashIgnoreCase(myBuf)``
+  ## `hashIgnoreCase(myBuf, 0, myBuf.high)` is equivalent
+  ## to `hashIgnoreCase(myBuf)`.
+  runnableExamples:
+    var a = "ABracadabRA"
+    doAssert hashIgnoreCase(a, 0, 3) == hashIgnoreCase(a, 7, 10)
+
   var h: Hash = 0
   for i in sPos..ePos:
     var c = sBuf[i]
@@ -218,49 +676,104 @@ proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash =
     h = h !& ord(c)
   result = !$h
 
-proc hash*(x: float): Hash {.inline.} =
-  ## efficient hashing of floats.
-  var y = x + 1.0
-  result = cast[ptr Hash](addr(y))[]
-
-
-# Forward declarations before methods that hash containers. This allows
-# containers to contain other containers
-proc hash*[A](x: openArray[A]): Hash
-proc hash*[A](x: set[A]): Hash
-
-
-proc hash*[T: tuple](x: T): Hash =
-  ## efficient hashing of tuples.
-  for f in fields(x):
-    result = result !& hash(f)
-  result = !$result
+proc hash*[T: tuple | object | proc | iterator {.closure.}](x: T): Hash =
+  ## Efficient `hash` overload.
+  runnableExamples:
+    # for `tuple|object`, `hash` must be defined for each component of `x`.
+    type Obj = object
+      x: int
+      y: string
+    type Obj2[T] = object
+      x: int
+      y: string
+    assert hash(Obj(x: 520, y: "Nim")) != hash(Obj(x: 520, y: "Nim2"))
+    # you can define custom hashes for objects (even if they're generic):
+    proc hash(a: Obj2): Hash = hash((a.x))
+    assert hash(Obj2[float](x: 520, y: "Nim")) == hash(Obj2[float](x: 520, y: "Nim2"))
+  runnableExamples:
+    # proc
+    proc fn1() = discard
+    const fn1b = fn1
+    assert hash(fn1b) == hash(fn1)
+
+    # closure
+    proc outer =
+      var a = 0
+      proc fn2() = a.inc
+      assert fn2 is "closure"
+      let fn2b = fn2
+      assert hash(fn2b) == hash(fn2)
+      assert hash(fn2) != hash(fn1)
+    outer()
+
+  when T is "closure":
+    result = hash((rawProc(x), rawEnv(x)))
+  elif T is (proc):
+    result = hash(cast[pointer](x))
+  else:
+    result = 0
+    for f in fields(x):
+      result = result !& hash(f)
+    result = !$result
 
 proc hash*[A](x: openArray[A]): Hash =
-  ## efficient hashing of arrays and sequences.
-  for it in items(x): result = result !& hash(it)
-  result = !$result
+  ## Efficient hashing of arrays and sequences.
+  ## There must be a `hash` proc defined for the element type `A`.
+  when A is byte:
+    when not sHash2:
+      result = cast[Hash](hashFarm(x))
+    else:
+      result = murmurHash(x)
+  elif A is char:
+    when not sHash2:
+      result = cast[Hash](hashFarm(toOpenArrayByte(x, 0, x.high)))
+    else:
+      #when nimvm:
+      #  result = hashVmImplChar(x, 0, x.high)
+      when true:
+        result = murmurHash(toOpenArrayByte(x, 0, x.high))
+  else:
+    result = 0
+    for a in x:
+      result = result !& hash(a)
+    result = !$result
 
 proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash =
-  ## efficient hashing of portions of arrays and sequences.
+  ## Efficient hashing of portions of arrays and sequences, from starting
+  ## position `sPos` to ending position `ePos` (included).
+  ## There must be a `hash` proc defined for the element type `A`.
   ##
-  ## ``hash(myBuf, 0, myBuf.high)`` is equivalent to ``hash(myBuf)``
-  for i in sPos..ePos:
-    result = result !& hash(aBuf[i])
-  result = !$result
+  ## `hash(myBuf, 0, myBuf.high)` is equivalent to `hash(myBuf)`.
+  runnableExamples:
+    let a = [1, 2, 5, 1, 2, 6]
+    doAssert hash(a, 0, 1) == hash(a, 3, 4)
+  when A is byte:
+    maybeFailJS_Number()
+    when not sHash2:
+      result = cast[Hash](hashFarm(toOpenArray(aBuf, sPos, ePos)))
+    else:
+      #when nimvm:
+      #  result = hashVmImplByte(aBuf, sPos, ePos)
+      when true:
+        result = murmurHash(toOpenArray(aBuf, sPos, ePos))
+  elif A is char:
+    maybeFailJS_Number()
+    when not sHash2:
+      result = cast[Hash](hashFarm(toOpenArrayByte(aBuf, sPos, ePos)))
+    else:
+      #when nimvm:
+      #  result = hashVmImplChar(aBuf, sPos, ePos)
+      when true:
+        result = murmurHash(toOpenArrayByte(aBuf, sPos, ePos))
+  else:
+    for i in sPos .. ePos:
+      result = result !& hash(aBuf[i])
+    result = !$result
 
 proc hash*[A](x: set[A]): Hash =
-  ## efficient hashing of sets.
-  for it in items(x): result = result !& hash(it)
+  ## Efficient hashing of sets.
+  ## There must be a `hash` proc defined for the element type `A`.
+  result = 0
+  for it in items(x):
+    result = result !& hash(it)
   result = !$result
-
-when isMainModule:
-  doAssert( hash("aa bb aaaa1234") == hash("aa bb aaaa1234", 0, 13) )
-  doAssert( hash("aa bb aaaa1234") == hash(cstring("aa bb aaaa1234")) )
-  doAssert( hashIgnoreCase("aa bb aaaa1234") == hash("aa bb aaaa1234") )
-  doAssert( hashIgnoreStyle("aa bb aaaa1234") == hashIgnoreCase("aa bb aaaa1234") )
-  let xx = @['H','e','l','l','o']
-  let ss = "Hello"
-  doAssert( hash(xx) == hash(ss) )
-  doAssert( hash(xx) == hash(xx, 0, xx.high) )
-  doAssert( hash(ss) == hash(ss, 0, ss.high) )
diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim
index 55b02ea60..fafa72463 100644
--- a/lib/pure/htmlgen.nim
+++ b/lib/pure/htmlgen.nim
@@ -7,36 +7,57 @@
 #    distribution, for details about the copyright.
 #
 
-## **Warning**: This module uses ``immediate`` macros which are known to
-## cause problems. Do yourself a favor and import the module
-## as ``from htmlgen import nil`` and then fully qualify the macros.
+## Do yourself a favor and import the module
+## as `from std/htmlgen import nil` and then fully qualify the macros.
+##
+## *Note*: The Karax project (`nimble install karax`) has a better
+## way to achieve the same, see https://github.com/pragmagic/karax/blob/master/tests/nativehtmlgen.nim
+## for an example.
 ##
 ##
 ## This module implements a simple `XML`:idx: and `HTML`:idx: code
 ## generator. Each commonly used HTML tag has a corresponding macro
 ## that generates a string with its HTML representation.
 ##
-## Example:
+## MathML
+## ======
+##
+## `MathML <https://wikipedia.org/wiki/MathML>`_ is supported, MathML is part of HTML5.
+## `MathML <https://wikipedia.org/wiki/MathML>`_ is an Standard ISO/IEC 40314 from year 2015.
+## MathML allows you to `draw advanced math on the web <https://developer.mozilla.org/en-US/docs/Web/MathML/Element/math#Examples>`_,
+## `visually similar to Latex math. <https://developer.mozilla.org/en-US/docs/Web/MathML/Element/semantics#Example>`_
 ##
-## .. code-block:: Nim
+## Examples
+## ========
+##
+##   ```Nim
 ##   var nim = "Nim"
-##   echo h1(a(href="http://nim-lang.org", nim))
+##   echo h1(a(href="https://nim-lang.org", nim))
+##   ```
 ##
-## Writes the string::
+## Writes the string:
 ##
-##   <h1><a href="http://nim-lang.org">Nim</a></h1>
+##     <h1><a href="https://nim-lang.org">Nim</a></h1>
 ##
 
 import
-  macros, strutils
+  std/[macros, strutils]
 
 const
-  coreAttr* = " id class title style "
-  eventAttr* = " onclick ondblclick onmousedown onmouseup " &
-    "onmouseover onmousemove onmouseout onkeypress onkeydown onkeyup onload "
-  commonAttr* = coreAttr & eventAttr
-
-proc getIdent(e: NimNode): string {.compileTime.} =
+  coreAttr* = " accesskey class contenteditable dir hidden id lang " &
+    "spellcheck style tabindex title translate " ## HTML DOM Core Attributes
+  eventAttr* = "onabort onblur oncancel oncanplay oncanplaythrough onchange " &
+    "onclick oncuechange ondblclick ondurationchange onemptied onended " &
+    "onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload " &
+    "onloadeddata onloadedmetadata onloadstart onmousedown onmouseenter " &
+    "onmouseleave onmousemove onmouseout onmouseover onmouseup onmousewheel " &
+    "onpause onplay onplaying onprogress onratechange onreset onresize " &
+    "onscroll onseeked onseeking onselect onshow onstalled onsubmit " &
+    "onsuspend ontimeupdate ontoggle onvolumechange onwaiting " ## HTML DOM Event Attributes
+  ariaAttr* = " role "                           ## HTML DOM Aria Attributes
+  commonAttr* = coreAttr & eventAttr & ariaAttr  ## HTML DOM Common Attributes
+
+proc getIdent(e: NimNode): string =
   case e.kind
   of nnkIdent:
     result = e.strVal.normalize
@@ -44,7 +65,7 @@ proc getIdent(e: NimNode): string {.compileTime.} =
     result = getIdent(e[0])
     for i in 1 .. e.len-1:
       result.add getIdent(e[i])
-  else: error("cannot extract identifier from node: " & toStrLit(e).strVal)
+  else: error("cannot extract identifier from node: " & toStrLit(e).strVal, e)
 
 proc delete[T](s: var seq[T], attr: T): bool =
   var idx = find(s, attr)
@@ -54,440 +75,738 @@ proc delete[T](s: var seq[T], attr: T): bool =
     setLen(s, L-1)
     result = true
 
-proc xmlCheckedTag*(e: NimNode, tag: string, optAttr = "", reqAttr = "",
-    isLeaf = false): NimNode {.compileTime.} =
+proc xmlCheckedTag*(argsList: NimNode, tag: string, optAttr = "", reqAttr = "",
+    isLeaf = false): NimNode =
   ## use this procedure to define a new XML tag
 
   # copy the attributes; when iterating over them these lists
   # will be modified, so that each attribute is only given one value
   var req = splitWhitespace(reqAttr)
   var opt = splitWhitespace(optAttr)
-  result = newNimNode(nnkBracket, e)
+  result = newNimNode(nnkBracket)
   result.add(newStrLitNode("<"))
   result.add(newStrLitNode(tag))
   # first pass over attributes:
-  for i in 1..e.len-1:
-    if e[i].kind == nnkExprEqExpr:
-      var name = getIdent(e[i][0])
-      if delete(req, name) or delete(opt, name):
+  for i in 0 ..< argsList.len:
+    if argsList[i].kind == nnkExprEqExpr:
+      var name = getIdent(argsList[i][0])
+      if name.startsWith("data-") or delete(req, name) or delete(opt, name):
         result.add(newStrLitNode(" "))
         result.add(newStrLitNode(name))
         result.add(newStrLitNode("=\""))
-        result.add(e[i][1])
+        result.add(argsList[i][1])
         result.add(newStrLitNode("\""))
       else:
-        error("invalid attribute for '" & tag & "' element: " & name)
+        error("invalid attribute for '" & tag & "' element: " & name, argsList[i])
   # check each required attribute exists:
   if req.len > 0:
-    error(req[0] & " attribute for '" & tag & "' element expected")
+    error(req[0] & " attribute for '" & tag & "' element expected", argsList)
   if isLeaf:
-    for i in 1..e.len-1:
-      if e[i].kind != nnkExprEqExpr:
-        error("element " & tag & " cannot be nested")
+    for i in 0 ..< argsList.len:
+      if argsList[i].kind != nnkExprEqExpr:
+        error("element " & tag & " cannot be nested", argsList[i])
     result.add(newStrLitNode(" />"))
   else:
     result.add(newStrLitNode(">"))
     # second pass over elements:
-    for i in 1..e.len-1:
-      if e[i].kind != nnkExprEqExpr: result.add(e[i])
+    for i in 0 ..< argsList.len:
+      if argsList[i].kind != nnkExprEqExpr: result.add(argsList[i])
     result.add(newStrLitNode("</"))
     result.add(newStrLitNode(tag))
     result.add(newStrLitNode(">"))
-  when compiles(nestList(ident"&", result)):
-    result = nestList(ident"&", result)
-  else:
-    result = nestList(!"&", result)
+  result = nestList(ident"&", result)
 
 macro a*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``a`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "a", "href charset type hreflang rel rev " &
-    "accesskey tabindex" & commonAttr)
+  ## Generates the HTML `a` element.
+  result = xmlCheckedTag(e, "a", "href target download rel hreflang type " &
+    commonAttr)
 
-macro acronym*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``acronym`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "acronym", commonAttr)
+macro abbr*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `abbr` element.
+  result = xmlCheckedTag(e, "abbr", commonAttr)
 
 macro address*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``address`` element.
-  let e = callsite()
+  ## Generates the HTML `address` element.
   result = xmlCheckedTag(e, "address", commonAttr)
 
 macro area*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``area`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "area", "shape coords href nohref" &
-    " accesskey tabindex" & commonAttr, "alt", true)
+  ## Generates the HTML `area` element.
+  result = xmlCheckedTag(e, "area", "coords download href hreflang rel " &
+    "shape target type" & commonAttr, "alt", true)
+
+macro article*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `article` element.
+  result = xmlCheckedTag(e, "article", commonAttr)
+
+macro aside*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `aside` element.
+  result = xmlCheckedTag(e, "aside", commonAttr)
+
+macro audio*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `audio` element.
+  result = xmlCheckedTag(e, "audio", "src crossorigin preload " &
+    "autoplay mediagroup loop muted controls" & commonAttr)
 
 macro b*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``b`` element.
-  let e = callsite()
+  ## Generates the HTML `b` element.
   result = xmlCheckedTag(e, "b", commonAttr)
 
 macro base*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``base`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "base", "", "href", true)
+  ## Generates the HTML `base` element.
+  result = xmlCheckedTag(e, "base", "href target" & commonAttr, "", true)
+
+macro bdi*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `bdi` element.
+  result = xmlCheckedTag(e, "bdi", commonAttr)
+
+macro bdo*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `bdo` element.
+  result = xmlCheckedTag(e, "bdo", commonAttr)
 
 macro big*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``big`` element.
-  let e = callsite()
+  ## Generates the HTML `big` element.
   result = xmlCheckedTag(e, "big", commonAttr)
 
 macro blockquote*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``blockquote`` element.
-  let e = callsite()
+  ## Generates the HTML `blockquote` element.
   result = xmlCheckedTag(e, "blockquote", " cite" & commonAttr)
 
 macro body*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``body`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "body", commonAttr)
+  ## Generates the HTML `body` element.
+  result = xmlCheckedTag(e, "body", "onafterprint onbeforeprint " &
+    "onbeforeunload onhashchange onmessage onoffline ononline onpagehide " &
+    "onpageshow onpopstate onstorage onunload" & commonAttr)
 
 macro br*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``br`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "br", "", "", true)
+  ## Generates the HTML `br` element.
+  result = xmlCheckedTag(e, "br", commonAttr, "", true)
 
 macro button*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``button`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "button", "accesskey tabindex " &
-    "disabled name type value" & commonAttr)
+  ## Generates the HTML `button` element.
+  result = xmlCheckedTag(e, "button", "autofocus disabled form formaction " &
+    "formenctype formmethod formnovalidate formtarget menu name type value" &
+    commonAttr)
+
+macro canvas*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `canvas` element.
+  result = xmlCheckedTag(e, "canvas", "width height" & commonAttr)
 
 macro caption*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``caption`` element.
-  let e = callsite()
+  ## Generates the HTML `caption` element.
   result = xmlCheckedTag(e, "caption", commonAttr)
 
+macro center*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `center` element.
+  result = xmlCheckedTag(e, "center", commonAttr)
+
 macro cite*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``cite`` element.
-  let e = callsite()
+  ## Generates the HTML `cite` element.
   result = xmlCheckedTag(e, "cite", commonAttr)
 
 macro code*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``code`` element.
-  let e = callsite()
+  ## Generates the HTML `code` element.
   result = xmlCheckedTag(e, "code", commonAttr)
 
 macro col*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``col`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "col", "span align valign" & commonAttr, "", true)
+  ## Generates the HTML `col` element.
+  result = xmlCheckedTag(e, "col", "span" & commonAttr, "", true)
 
 macro colgroup*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``colgroup`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "colgroup", "span align valign" & commonAttr)
+  ## Generates the HTML `colgroup` element.
+  result = xmlCheckedTag(e, "colgroup", "span" & commonAttr)
+
+macro data*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `data` element.
+  result = xmlCheckedTag(e, "data", "value" & commonAttr)
+
+macro datalist*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `datalist` element.
+  result = xmlCheckedTag(e, "datalist", commonAttr)
 
 macro dd*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``dd`` element.
-  let e = callsite()
+  ## Generates the HTML `dd` element.
   result = xmlCheckedTag(e, "dd", commonAttr)
 
 macro del*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``del`` element.
-  let e = callsite()
+  ## Generates the HTML `del` element.
   result = xmlCheckedTag(e, "del", "cite datetime" & commonAttr)
 
+macro details*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `details` element.
+  result = xmlCheckedTag(e, "details", commonAttr & "open")
+
 macro dfn*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``dfn`` element.
-  let e = callsite()
+  ## Generates the HTML `dfn` element.
   result = xmlCheckedTag(e, "dfn", commonAttr)
 
+macro dialog*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `dialog` element.
+  result = xmlCheckedTag(e, "dialog", commonAttr & "open")
+
 macro `div`*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``div`` element.
-  let e = callsite()
+  ## Generates the HTML `div` element.
   result = xmlCheckedTag(e, "div", commonAttr)
 
 macro dl*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``dl`` element.
-  let e = callsite()
+  ## Generates the HTML `dl` element.
   result = xmlCheckedTag(e, "dl", commonAttr)
 
 macro dt*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``dt`` element.
-  let e = callsite()
+  ## Generates the HTML `dt` element.
   result = xmlCheckedTag(e, "dt", commonAttr)
 
 macro em*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``em`` element.
-  let e = callsite()
+  ## Generates the HTML `em` element.
   result = xmlCheckedTag(e, "em", commonAttr)
 
+macro embed*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `embed` element.
+  result = xmlCheckedTag(e, "embed", "src type height width" &
+    commonAttr, "", true)
+
 macro fieldset*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``fieldset`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "fieldset", commonAttr)
+  ## Generates the HTML `fieldset` element.
+  result = xmlCheckedTag(e, "fieldset", "disabled form name" & commonAttr)
+
+macro figure*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `figure` element.
+  result = xmlCheckedTag(e, "figure", commonAttr)
+
+macro figcaption*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `figcaption` element.
+  result = xmlCheckedTag(e, "figcaption", commonAttr)
+
+macro footer*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `footer` element.
+  result = xmlCheckedTag(e, "footer", commonAttr)
 
 macro form*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``form`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "form", "method encype accept accept-charset" &
-    commonAttr, "action")
+  ## Generates the HTML `form` element.
+  result = xmlCheckedTag(e, "form", "accept-charset action autocomplete " &
+    "enctype method name novalidate target" & commonAttr)
 
 macro h1*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``h1`` element.
-  let e = callsite()
+  ## Generates the HTML `h1` element.
   result = xmlCheckedTag(e, "h1", commonAttr)
 
 macro h2*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``h2`` element.
-  let e = callsite()
+  ## Generates the HTML `h2` element.
   result = xmlCheckedTag(e, "h2", commonAttr)
 
 macro h3*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``h3`` element.
-  let e = callsite()
+  ## Generates the HTML `h3` element.
   result = xmlCheckedTag(e, "h3", commonAttr)
 
 macro h4*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``h4`` element.
-  let e = callsite()
+  ## Generates the HTML `h4` element.
   result = xmlCheckedTag(e, "h4", commonAttr)
 
 macro h5*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``h5`` element.
-  let e = callsite()
+  ## Generates the HTML `h5` element.
   result = xmlCheckedTag(e, "h5", commonAttr)
 
 macro h6*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``h6`` element.
-  let e = callsite()
+  ## Generates the HTML `h6` element.
   result = xmlCheckedTag(e, "h6", commonAttr)
 
 macro head*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``head`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "head", "profile")
+  ## Generates the HTML `head` element.
+  result = xmlCheckedTag(e, "head", commonAttr)
+
+macro header*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `header` element.
+  result = xmlCheckedTag(e, "header", commonAttr)
 
 macro html*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``html`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "html", "xmlns", "")
+  ## Generates the HTML `html` element.
+  result = xmlCheckedTag(e, "html", "xmlns" & commonAttr, "")
 
 macro hr*(): untyped =
-  ## generates the HTML ``hr`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "hr", commonAttr, "", true)
+  ## Generates the HTML `hr` element.
+  result = xmlCheckedTag(newNimNode(nnkArgList), "hr", commonAttr, "", true)
 
 macro i*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``i`` element.
-  let e = callsite()
+  ## Generates the HTML `i` element.
   result = xmlCheckedTag(e, "i", commonAttr)
 
+macro iframe*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `iframe` element.
+  result = xmlCheckedTag(e, "iframe", "src srcdoc name sandbox width height loading" &
+    commonAttr)
+
 macro img*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``img`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "img", "longdesc height width", "src alt", true)
+  ## Generates the HTML `img` element.
+  result = xmlCheckedTag(e, "img", "crossorigin usemap ismap height width loading" &
+    commonAttr, "src alt", true)
 
 macro input*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``input`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "input", "name type value checked maxlength src" &
-    " alt accept disabled readonly accesskey tabindex" & commonAttr, "", true)
+  ## Generates the HTML `input` element.
+  result = xmlCheckedTag(e, "input", "accept alt autocomplete autofocus " &
+    "checked dirname disabled form formaction formenctype formmethod " &
+    "formnovalidate formtarget height inputmode list max maxlength min " &
+    "minlength multiple name pattern placeholder readonly required size " &
+    "src step type value width" & commonAttr, "", true)
 
 macro ins*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``ins`` element.
-  let e = callsite()
+  ## Generates the HTML `ins` element.
   result = xmlCheckedTag(e, "ins", "cite datetime" & commonAttr)
 
 macro kbd*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``kbd`` element.
-  let e = callsite()
+  ## Generates the HTML `kbd` element.
   result = xmlCheckedTag(e, "kbd", commonAttr)
 
+macro keygen*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `keygen` element.
+  result = xmlCheckedTag(e, "keygen", "autofocus challenge disabled " &
+    "form keytype name" & commonAttr)
+
 macro label*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``label`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "label", "for accesskey" & commonAttr)
+  ## Generates the HTML `label` element.
+  result = xmlCheckedTag(e, "label", "form for" & commonAttr)
 
 macro legend*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``legend`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "legend", "accesskey" & commonAttr)
+  ## Generates the HTML `legend` element.
+  result = xmlCheckedTag(e, "legend", commonAttr)
 
 macro li*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``li`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "li", commonAttr)
+  ## Generates the HTML `li` element.
+  result = xmlCheckedTag(e, "li", "value" & commonAttr)
 
 macro link*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``link`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "link", "href charset hreflang type rel rev media" &
-    commonAttr, "", true)
+  ## Generates the HTML `link` element.
+  result = xmlCheckedTag(e, "link", "href crossorigin rel media hreflang " &
+    "type sizes" & commonAttr, "", true)
+
+macro main*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `main` element.
+  result = xmlCheckedTag(e, "main", commonAttr)
 
 macro map*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``map`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "map", "class title" & eventAttr, "id", false)
+  ## Generates the HTML `map` element.
+  result = xmlCheckedTag(e, "map", "name" & commonAttr)
+
+macro mark*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `mark` element.
+  result = xmlCheckedTag(e, "mark", commonAttr)
+
+macro marquee*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `marquee` element.
+  result = xmlCheckedTag(e, "marquee", coreAttr &
+    "behavior bgcolor direction height hspace loop scrollamount " &
+    "scrolldelay truespeed vspace width onbounce onfinish onstart")
 
 macro meta*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``meta`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "meta", "name http-equiv scheme", "content", true)
+  ## Generates the HTML `meta` element.
+  result = xmlCheckedTag(e, "meta", "name http-equiv content charset" &
+    commonAttr, "", true)
+
+macro meter*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `meter` element.
+  result = xmlCheckedTag(e, "meter", "value min max low high optimum" &
+    commonAttr)
+
+macro nav*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `nav` element.
+  result = xmlCheckedTag(e, "nav", commonAttr)
 
 macro noscript*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``noscript`` element.
-  let e = callsite()
+  ## Generates the HTML `noscript` element.
   result = xmlCheckedTag(e, "noscript", commonAttr)
 
 macro `object`*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``object`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "object", "classid data codebase declare type " &
-    "codetype archive standby width height name tabindex" & commonAttr)
+  ## Generates the HTML `object` element.
+  result = xmlCheckedTag(e, "object", "data type typemustmatch name usemap " &
+    "form width height" & commonAttr)
 
 macro ol*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``ol`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "ol", commonAttr)
+  ## Generates the HTML `ol` element.
+  result = xmlCheckedTag(e, "ol", "reversed start type" & commonAttr)
 
 macro optgroup*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``optgroup`` element.
-  let e = callsite()
+  ## Generates the HTML `optgroup` element.
   result = xmlCheckedTag(e, "optgroup", "disabled" & commonAttr, "label", false)
 
 macro option*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``option`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "option", "selected value" & commonAttr)
+  ## Generates the HTML `option` element.
+  result = xmlCheckedTag(e, "option", "disabled label selected value" &
+    commonAttr)
+
+macro output*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `output` element.
+  result = xmlCheckedTag(e, "output", "for form name" & commonAttr)
 
 macro p*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``p`` element.
-  let e = callsite()
+  ## Generates the HTML `p` element.
   result = xmlCheckedTag(e, "p", commonAttr)
 
 macro param*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``param`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "param", "value id type valuetype", "name", true)
+  ## Generates the HTML `param` element.
+  result = xmlCheckedTag(e, "param", commonAttr, "name value", true)
+
+macro picture*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `picture` element.
+  result = xmlCheckedTag(e, "picture", commonAttr)
 
 macro pre*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``pre`` element.
-  let e = callsite()
+  ## Generates the HTML `pre` element.
   result = xmlCheckedTag(e, "pre", commonAttr)
 
+macro progress*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `progress` element.
+  result = xmlCheckedTag(e, "progress", "value max" & commonAttr)
+
 macro q*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``q`` element.
-  let e = callsite()
+  ## Generates the HTML `q` element.
   result = xmlCheckedTag(e, "q", "cite" & commonAttr)
 
+macro rb*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `rb` element.
+  result = xmlCheckedTag(e, "rb", commonAttr)
+
+macro rp*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `rp` element.
+  result = xmlCheckedTag(e, "rp", commonAttr)
+
+macro rt*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `rt` element.
+  result = xmlCheckedTag(e, "rt", commonAttr)
+
+macro rtc*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `rtc` element.
+  result = xmlCheckedTag(e, "rtc", commonAttr)
+
+macro ruby*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `ruby` element.
+  result = xmlCheckedTag(e, "ruby", commonAttr)
+
+macro s*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `s` element.
+  result = xmlCheckedTag(e, "s", commonAttr)
+
 macro samp*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``samp`` element.
-  let e = callsite()
+  ## Generates the HTML `samp` element.
   result = xmlCheckedTag(e, "samp", commonAttr)
 
 macro script*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``script`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "script", "src charset defer", "type", false)
+  ## Generates the HTML `script` element.
+  result = xmlCheckedTag(e, "script", "src type charset async defer " &
+    "crossorigin" & commonAttr)
+
+macro section*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `section` element.
+  result = xmlCheckedTag(e, "section", commonAttr)
 
 macro select*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``select`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "select", "name size multiple disabled tabindex" &
-    commonAttr)
+  ## Generates the HTML `select` element.
+  result = xmlCheckedTag(e, "select", "autofocus disabled form multiple " &
+    "name required size" & commonAttr)
+
+macro slot*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `slot` element.
+  result = xmlCheckedTag(e, "slot", commonAttr)
 
 macro small*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``small`` element.
-  let e = callsite()
+  ## Generates the HTML `small` element.
   result = xmlCheckedTag(e, "small", commonAttr)
 
+macro source*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `source` element.
+  result = xmlCheckedTag(e, "source", "type" & commonAttr, "src", true)
+
 macro span*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``span`` element.
-  let e = callsite()
+  ## Generates the HTML `span` element.
   result = xmlCheckedTag(e, "span", commonAttr)
 
 macro strong*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``strong`` element.
-  let e = callsite()
+  ## Generates the HTML `strong` element.
   result = xmlCheckedTag(e, "strong", commonAttr)
 
 macro style*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``style`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "style", "media title", "type")
+  ## Generates the HTML `style` element.
+  result = xmlCheckedTag(e, "style", "media type" & commonAttr)
 
 macro sub*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``sub`` element.
-  let e = callsite()
+  ## Generates the HTML `sub` element.
   result = xmlCheckedTag(e, "sub", commonAttr)
 
+macro summary*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `summary` element.
+  result = xmlCheckedTag(e, "summary", commonAttr)
+
 macro sup*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``sup`` element.
-  let e = callsite()
+  ## Generates the HTML `sup` element.
   result = xmlCheckedTag(e, "sup", commonAttr)
 
 macro table*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``table`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "table", "summary border cellpadding cellspacing" &
-    " frame rules width" & commonAttr)
+  ## Generates the HTML `table` element.
+  result = xmlCheckedTag(e, "table", "border sortable" & commonAttr)
 
 macro tbody*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``tbody`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "tbody", "align valign" & commonAttr)
+  ## Generates the HTML `tbody` element.
+  result = xmlCheckedTag(e, "tbody", commonAttr)
 
 macro td*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``td`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "td", "colspan rowspan abbr axis headers scope" &
-    " align valign" & commonAttr)
+  ## Generates the HTML `td` element.
+  result = xmlCheckedTag(e, "td", "colspan rowspan headers" & commonAttr)
+
+macro `template`*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `template` element.
+  result = xmlCheckedTag(e, "template", commonAttr)
 
 macro textarea*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``textarea`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "textarea", " name disabled readonly accesskey" &
-    " tabindex" & commonAttr, "rows cols", false)
+  ## Generates the HTML `textarea` element.
+  result = xmlCheckedTag(e, "textarea", "autocomplete autofocus cols " &
+    "dirname disabled form inputmode maxlength minlength name placeholder " &
+    "readonly required rows wrap" & commonAttr)
 
 macro tfoot*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``tfoot`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "tfoot", "align valign" & commonAttr)
+  ## Generates the HTML `tfoot` element.
+  result = xmlCheckedTag(e, "tfoot", commonAttr)
 
 macro th*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``th`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "th", "colspan rowspan abbr axis headers scope" &
-    " align valign" & commonAttr)
+  ## Generates the HTML `th` element.
+  result = xmlCheckedTag(e, "th", "colspan rowspan headers abbr scope axis" &
+    " sorted" & commonAttr)
 
 macro thead*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``thead`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "thead", "align valign" & commonAttr)
+  ## Generates the HTML `thead` element.
+  result = xmlCheckedTag(e, "thead", commonAttr)
+
+macro time*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `time` element.
+  result = xmlCheckedTag(e, "time", "datetime" & commonAttr)
 
 macro title*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``title`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "title")
+  ## Generates the HTML `title` element.
+  result = xmlCheckedTag(e, "title", commonAttr)
 
 macro tr*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``tr`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "tr", "align valign" & commonAttr)
+  ## Generates the HTML `tr` element.
+  result = xmlCheckedTag(e, "tr", commonAttr)
+
+macro track*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `track` element.
+  result = xmlCheckedTag(e, "track", "kind srclang label default" &
+    commonAttr, "src", true)
 
 macro tt*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``tt`` element.
-  let e = callsite()
+  ## Generates the HTML `tt` element.
   result = xmlCheckedTag(e, "tt", commonAttr)
 
+macro u*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `u` element.
+  result = xmlCheckedTag(e, "u", commonAttr)
+
 macro ul*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``ul`` element.
-  let e = callsite()
+  ## Generates the HTML `ul` element.
   result = xmlCheckedTag(e, "ul", commonAttr)
 
 macro `var`*(e: varargs[untyped]): untyped =
-  ## generates the HTML ``var`` element.
-  let e = callsite()
+  ## Generates the HTML `var` element.
   result = xmlCheckedTag(e, "var", commonAttr)
 
-when isMainModule:
+macro video*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `video` element.
+  result = xmlCheckedTag(e, "video", "src crossorigin poster preload " &
+    "autoplay mediagroup loop muted controls width height" & commonAttr)
+
+macro wbr*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `wbr` element.
+  result = xmlCheckedTag(e, "wbr", commonAttr, "", true)
+
+macro portal*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `portal` element.
+  result = xmlCheckedTag(e, "portal", "width height type src disabled" & commonAttr, "", false)
+
+
+macro math*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `math` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/math#Examples
+  result = xmlCheckedTag(e, "math", "mathbackground mathcolor href overflow" & commonAttr)
+
+macro maction*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `maction` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/maction
+  result = xmlCheckedTag(e, "maction", "mathbackground mathcolor href" & commonAttr)
+
+macro menclose*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `menclose` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/menclose
+  result = xmlCheckedTag(e, "menclose", "mathbackground mathcolor href notation" & commonAttr)
+
+macro merror*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `merror` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/merror
+  result = xmlCheckedTag(e, "merror", "mathbackground mathcolor href" & commonAttr)
+
+macro mfenced*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `mfenced` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mfenced
+  result = xmlCheckedTag(e, "mfenced", "mathbackground mathcolor href open separators" & commonAttr)
+
+macro mfrac*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `mfrac` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mfrac
+  result = xmlCheckedTag(e, "mfrac", "mathbackground mathcolor href linethickness numalign" & commonAttr)
+
+macro mglyph*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `mglyph` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mglyph
+  result = xmlCheckedTag(e, "mglyph", "mathbackground mathcolor href src valign" & commonAttr)
+
+macro mi*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `mi` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mi
+  result = xmlCheckedTag(e, "mi", "mathbackground mathcolor href mathsize mathvariant" & commonAttr)
+
+macro mlabeledtr*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `mlabeledtr` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mlabeledtr
+  result = xmlCheckedTag(e, "mlabeledtr", "mathbackground mathcolor href columnalign groupalign rowalign" & commonAttr)
+
+macro mmultiscripts*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `mmultiscripts` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mmultiscripts
+  result = xmlCheckedTag(e, "mmultiscripts", "mathbackground mathcolor href subscriptshift superscriptshift" & commonAttr)
+
+macro mn*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `mn` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mn
+  result = xmlCheckedTag(e, "mn", "mathbackground mathcolor href mathsize mathvariant" & commonAttr)
+
+macro mo*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `mo` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mo
+  result = xmlCheckedTag(e, "mo",
+    "mathbackground mathcolor fence form largeop lspace mathsize mathvariant movablelimits rspace separator stretchy symmetric" & commonAttr)
+
+macro mover*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `mover` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mover
+  result = xmlCheckedTag(e, "mover", "mathbackground mathcolor accent href" & commonAttr)
+
+macro mpadded*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `mpadded` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mpadded
+  result = xmlCheckedTag(e, "mpadded", "mathbackground mathcolor depth href lspace voffset" & commonAttr)
+
+macro mphantom*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `mphantom` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mphantom
+  result = xmlCheckedTag(e, "mphantom", "mathbackground" & commonAttr)
+
+macro mroot*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `mroot` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mroot
+  result = xmlCheckedTag(e, "mroot", "mathbackground mathcolor href" & commonAttr)
+
+macro mrow*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `mrow` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mrow
+  result = xmlCheckedTag(e, "mrow", "mathbackground mathcolor href" & commonAttr)
+
+macro ms*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `ms` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/ms
+  result = xmlCheckedTag(e, "ms", "mathbackground mathcolor href lquote mathsize mathvariant rquote" & commonAttr)
+
+macro mspace*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `mspace` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mspace
+  result = xmlCheckedTag(e, "mspace", "mathbackground mathcolor href linebreak" & commonAttr)
+
+macro msqrt*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `msqrt` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/msqrt
+  result = xmlCheckedTag(e, "msqrt", "mathbackground mathcolor href" & commonAttr)
+
+macro mstyle*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `mstyle` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mstyle
+  result = xmlCheckedTag(e, "mstyle", ("mathbackground mathcolor href decimalpoint displaystyle " &
+    "infixlinebreakstyle scriptlevel scriptminsize scriptsizemultiplier" & commonAttr))
+
+macro msub*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `msub` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/msub
+  result = xmlCheckedTag(e, "msub", "mathbackground mathcolor href subscriptshift" & commonAttr)
+
+macro msubsup*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `msubsup` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/msubsup
+  result = xmlCheckedTag(e, "msubsup", "mathbackground mathcolor href subscriptshift superscriptshift" & commonAttr)
+
+macro msup*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `msup` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/msup
+  result = xmlCheckedTag(e, "msup", "mathbackground mathcolor href superscriptshift" & commonAttr)
+
+macro mtable*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `mtable` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mtable
+  result = xmlCheckedTag(e, "mtable", ("mathbackground mathcolor href align " &
+    "alignmentscope columnalign columnlines columnspacing columnwidth " &
+    "displaystyle equalcolumns equalrows frame framespacing groupalign " &
+    "rowalign rowlines rowspacing side width" & commonAttr))
+
+macro mtd*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `mtd` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mtd
+  result = xmlCheckedTag(e, "mtd",
+    "mathbackground mathcolor href columnalign columnspan groupalign rowalign rowspan" & commonAttr)
+
+macro mtext*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `mtext` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mtext
+  result = xmlCheckedTag(e, "mtext", "mathbackground mathcolor href mathsize mathvariant" & commonAttr)
+
+macro munder*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `munder` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/munder
+  result = xmlCheckedTag(e, "munder", "mathbackground mathcolor href accentunder align" & commonAttr)
+
+macro munderover*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `munderover` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/munderover
+  result = xmlCheckedTag(e, "munderover", "mathbackground mathcolor href accentunder accent align" & commonAttr)
+
+macro semantics*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `semantics` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/semantics
+  result = xmlCheckedTag(e, "semantics", "mathbackground mathcolor href definitionURL encoding cd src" & commonAttr)
+
+macro annotation*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `annotation` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/semantics
+  result = xmlCheckedTag(e, "annotation", "mathbackground mathcolor href definitionURL encoding cd src" & commonAttr)
+
+macro `annotation-xml`*(e: varargs[untyped]): untyped =
+  ## Generates the HTML `annotation-xml` element. MathML https://wikipedia.org/wiki/MathML
+  ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/semantics
+  result = xmlCheckedTag(e, "annotation", "mathbackground mathcolor href definitionURL encoding cd src" & commonAttr)
+
+
+runnableExamples:
   let nim = "Nim"
-  assert h1(a(href="http://nim-lang.org", nim)) ==
-    """<h1><a href="http://nim-lang.org">Nim</a></h1>"""
-  assert form(action="test", `accept-charset` = "Content-Type") ==
+  assert h1(a(href = "https://nim-lang.org", nim)) ==
+    """<h1><a href="https://nim-lang.org">Nim</a></h1>"""
+  assert form(action = "test", `accept-charset` = "Content-Type") ==
     """<form action="test" accept-charset="Content-Type"></form>"""
+
+
+  assert math(
+    semantics(
+      mrow(
+        msup(
+          mi("x"),
+          mn("42")
+        )
+      )
+    )
+  ) == "<math><semantics><mrow><msup><mi>x</mi><mn>42</mn></msup></mrow></semantics></math>"
+
+  assert math(
+    semantics(
+      annotation(encoding = "application/x-tex", title = "Latex on Web", r"x^{2} + y")
+    )
+  ) == """<math><semantics><annotation encoding="application/x-tex" title="Latex on Web">x^{2} + y</annotation></semantics></math>"""
diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim
index 9d4b57dc0..62919546f 100644
--- a/lib/pure/htmlparser.nim
+++ b/lib/pure/htmlparser.nim
@@ -7,19 +7,18 @@
 #    distribution, for details about the copyright.
 #
 
-## This module parses an HTML document and creates its XML tree representation.
-## It is supposed to handle the *wild* HTML the real world uses.
+## **NOTE**: The behaviour might change in future versions as it is not
+## clear what "*wild* HTML the real world uses" really implies.
 ##
 ## It can be used to parse a wild HTML document and output it as valid XHTML
 ## document (well, if you are lucky):
-##
-## .. code-block:: Nim
-##
+##   ```Nim
 ##   echo loadHtml("mydirty.html")
+##   ```
 ##
 ## Every tag in the resulting tree is in lower case.
 ##
-## **Note:** The resulting ``XmlNode`` already uses the ``clientData`` field,
+## **Note:** The resulting `XmlNode` already uses the `clientData` field,
 ## so it cannot be used by clients of this library.
 ##
 ## Example: Transforming hyperlinks
@@ -27,157 +26,161 @@
 ##
 ## This code demonstrates how you can iterate over all the tags in an HTML file
 ## and write back the modified version. In this case we look for hyperlinks
-## ending with the extension ``.rst`` and convert them to ``.html``.
-##
-## .. code-block:: Nim
+## ending with the extension `.rst` and convert them to `.html`.
 ##
-##   import htmlparser
-##   import xmltree  # To use '$' for XmlNode
-##   import strtabs  # To access XmlAttributes
-##   import os       # To use splitFile
-##   import strutils # To use cmpIgnoreCase
+##   ```Nim test
+##   import std/htmlparser
+##   import std/xmltree  # To use '$' for XmlNode
+##   import std/strtabs  # To access XmlAttributes
+##   import std/os       # To use splitFile
+##   import std/strutils # To use cmpIgnoreCase
 ##
 ##   proc transformHyperlinks() =
-##     let html = loadHTML("input.html")
+##     let html = loadHtml("input.html")
 ##
 ##     for a in html.findAll("a"):
-##       let href = a.attrs["href"]
-##       if not href.isNil:
-##         let (dir, filename, ext) = splitFile(href)
+##       if a.attrs.hasKey "href":
+##         let (dir, filename, ext) = splitFile(a.attrs["href"])
 ##         if cmpIgnoreCase(ext, ".rst") == 0:
 ##           a.attrs["href"] = dir / filename & ".html"
 ##
 ##     writeFile("output.html", $html)
+##   ```
+
+{.deprecated: "use `nimble install htmlparser` and import `pkg/htmlparser` instead".}
+
+import std/[strutils, streams, parsexml, xmltree, unicode, strtabs]
 
-import strutils, streams, parsexml, xmltree, unicode, strtabs
+when defined(nimPreviewSlimSystem):
+  import std/syncio
 
 type
-  HtmlTag* = enum ## list of all supported HTML tags; order will always be
+  HtmlTag* = enum  ## list of all supported HTML tags; order will always be
                    ## alphabetically
     tagUnknown,    ## unknown HTML element
-    tagA,          ## the HTML ``a`` element
-    tagAbbr,       ## the deprecated HTML ``abbr`` element
-    tagAcronym,    ## the HTML ``acronym`` element
-    tagAddress,    ## the HTML ``address`` element
-    tagApplet,     ## the deprecated HTML ``applet`` element
-    tagArea,       ## the HTML ``area`` element
-    tagArticle,    ## the HTML ``article`` element
-    tagAside,      ## the HTML ``aside`` element
-    tagAudio,      ## the HTML ``audio`` element
-    tagB,          ## the HTML ``b`` element
-    tagBase,       ## the HTML ``base`` element
-    tagBdi,        ## the HTML ``bdi`` element
-    tagBdo,        ## the deprecated HTML ``dbo`` element
-    tagBasefont,   ## the deprecated HTML ``basefont`` element
-    tagBig,        ## the HTML ``big`` element
-    tagBlockquote, ## the HTML ``blockquote`` element
-    tagBody,       ## the HTML ``body`` element
-    tagBr,         ## the HTML ``br`` element
-    tagButton,     ## the HTML ``button`` element
-    tagCanvas,     ## the HTML ``canvas`` element
-    tagCaption,    ## the HTML ``caption`` element
-    tagCenter,     ## the deprecated HTML ``center`` element
-    tagCite,       ## the HTML ``cite`` element
-    tagCode,       ## the HTML ``code`` element
-    tagCol,        ## the HTML ``col`` element
-    tagColgroup,   ## the HTML ``colgroup`` element
-    tagCommand,    ## the HTML ``command`` element
-    tagDatalist,   ## the HTML ``datalist`` element
-    tagDd,         ## the HTML ``dd`` element
-    tagDel,        ## the HTML ``del`` element
-    tagDetails,    ## the HTML ``details`` element
-    tagDfn,        ## the HTML ``dfn`` element
-    tagDialog,     ## the HTML ``dialog`` element
-    tagDiv,        ## the HTML ``div`` element
-    tagDir,        ## the deprecated HTLM ``dir`` element
-    tagDl,         ## the HTML ``dl`` element
-    tagDt,         ## the HTML ``dt`` element
-    tagEm,         ## the HTML ``em`` element
-    tagEmbed,      ## the HTML ``embed`` element
-    tagFieldset,   ## the HTML ``fieldset`` element
-    tagFigcaption, ## the HTML ``figcaption`` element
-    tagFigure,     ## the HTML ``figure`` element
-    tagFont,       ## the deprecated HTML ``font`` element
-    tagFooter,     ## the HTML ``footer`` element
-    tagForm,       ## the HTML ``form`` element
-    tagFrame,      ## the HTML ``frame`` element
-    tagFrameset,   ## the deprecated HTML ``frameset`` element
-    tagH1,         ## the HTML ``h1`` element
-    tagH2,         ## the HTML ``h2`` element
-    tagH3,         ## the HTML ``h3`` element
-    tagH4,         ## the HTML ``h4`` element
-    tagH5,         ## the HTML ``h5`` element
-    tagH6,         ## the HTML ``h6`` element
-    tagHead,       ## the HTML ``head`` element
-    tagHeader,     ## the HTML ``header`` element
-    tagHgroup,     ## the HTML ``hgroup`` element
-    tagHtml,       ## the HTML ``html`` element
-    tagHr,         ## the HTML ``hr`` element
-    tagI,          ## the HTML ``i`` element
-    tagIframe,     ## the deprecated HTML ``iframe`` element
-    tagImg,        ## the HTML ``img`` element
-    tagInput,      ## the HTML ``input`` element
-    tagIns,        ## the HTML ``ins`` element
-    tagIsindex,    ## the deprecated HTML ``isindex`` element
-    tagKbd,        ## the HTML ``kbd`` element
-    tagKeygen,     ## the HTML ``keygen`` element
-    tagLabel,      ## the HTML ``label`` element
-    tagLegend,     ## the HTML ``legend`` element
-    tagLi,         ## the HTML ``li`` element
-    tagLink,       ## the HTML ``link`` element
-    tagMap,        ## the HTML ``map`` element
-    tagMark,       ## the HTML ``mark`` element
-    tagMenu,       ## the deprecated HTML ``menu`` element
-    tagMeta,       ## the HTML ``meta`` element
-    tagMeter,      ## the HTML ``meter`` element
-    tagNav,        ## the HTML ``nav`` element
-    tagNobr,       ## the deprecated HTML ``nobr`` element
-    tagNoframes,   ## the deprecated HTML ``noframes`` element
-    tagNoscript,   ## the HTML ``noscript`` element
-    tagObject,     ## the HTML ``object`` element
-    tagOl,         ## the HTML ``ol`` element
-    tagOptgroup,   ## the HTML ``optgroup`` element
-    tagOption,     ## the HTML ``option`` element
-    tagOutput,     ## the HTML ``output`` element
-    tagP,          ## the HTML ``p`` element
-    tagParam,      ## the HTML ``param`` element
-    tagPre,        ## the HTML ``pre`` element
-    tagProgress,   ## the HTML ``progress`` element
-    tagQ,          ## the HTML ``q`` element
-    tagRp,         ## the HTML ``rp`` element
-    tagRt,         ## the HTML ``rt`` element
-    tagRuby,       ## the HTML ``ruby`` element
-    tagS,          ## the deprecated HTML ``s`` element
-    tagSamp,       ## the HTML ``samp`` element
-    tagScript,     ## the HTML ``script`` element
-    tagSection,    ## the HTML ``section`` element
-    tagSelect,     ## the HTML ``select`` element
-    tagSmall,      ## the HTML ``small`` element
-    tagSource,     ## the HTML ``source`` element
-    tagSpan,       ## the HTML ``span`` element
-    tagStrike,     ## the deprecated HTML ``strike`` element
-    tagStrong,     ## the HTML ``strong`` element
-    tagStyle,      ## the HTML ``style`` element
-    tagSub,        ## the HTML ``sub`` element
-    tagSummary,    ## the HTML ``summary`` element
-    tagSup,        ## the HTML ``sup`` element
-    tagTable,      ## the HTML ``table`` element
-    tagTbody,      ## the HTML ``tbody`` element
-    tagTd,         ## the HTML ``td`` element
-    tagTextarea,   ## the HTML ``textarea`` element
-    tagTfoot,      ## the HTML ``tfoot`` element
-    tagTh,         ## the HTML ``th`` element
-    tagThead,      ## the HTML ``thead`` element
-    tagTime,       ## the HTML ``time`` element
-    tagTitle,      ## the HTML ``title`` element
-    tagTr,         ## the HTML ``tr`` element
-    tagTrack,      ## the HTML ``track`` element
-    tagTt,         ## the HTML ``tt`` element
-    tagU,          ## the deprecated HTML ``u`` element
-    tagUl,         ## the HTML ``ul`` element
-    tagVar,        ## the HTML ``var`` element
-    tagVideo,      ## the HTML ``video`` element
-    tagWbr         ## the HTML ``wbr`` element
+    tagA,          ## the HTML `a` element
+    tagAbbr,       ## the deprecated HTML `abbr` element
+    tagAcronym,    ## the HTML `acronym` element
+    tagAddress,    ## the HTML `address` element
+    tagApplet,     ## the deprecated HTML `applet` element
+    tagArea,       ## the HTML `area` element
+    tagArticle,    ## the HTML `article` element
+    tagAside,      ## the HTML `aside` element
+    tagAudio,      ## the HTML `audio` element
+    tagB,          ## the HTML `b` element
+    tagBase,       ## the HTML `base` element
+    tagBdi,        ## the HTML `bdi` element
+    tagBdo,        ## the deprecated HTML `dbo` element
+    tagBasefont,   ## the deprecated HTML `basefont` element
+    tagBig,        ## the HTML `big` element
+    tagBlockquote, ## the HTML `blockquote` element
+    tagBody,       ## the HTML `body` element
+    tagBr,         ## the HTML `br` element
+    tagButton,     ## the HTML `button` element
+    tagCanvas,     ## the HTML `canvas` element
+    tagCaption,    ## the HTML `caption` element
+    tagCenter,     ## the deprecated HTML `center` element
+    tagCite,       ## the HTML `cite` element
+    tagCode,       ## the HTML `code` element
+    tagCol,        ## the HTML `col` element
+    tagColgroup,   ## the HTML `colgroup` element
+    tagCommand,    ## the HTML `command` element
+    tagDatalist,   ## the HTML `datalist` element
+    tagDd,         ## the HTML `dd` element
+    tagDel,        ## the HTML `del` element
+    tagDetails,    ## the HTML `details` element
+    tagDfn,        ## the HTML `dfn` element
+    tagDialog,     ## the HTML `dialog` element
+    tagDiv,        ## the HTML `div` element
+    tagDir,        ## the deprecated HTLM `dir` element
+    tagDl,         ## the HTML `dl` element
+    tagDt,         ## the HTML `dt` element
+    tagEm,         ## the HTML `em` element
+    tagEmbed,      ## the HTML `embed` element
+    tagFieldset,   ## the HTML `fieldset` element
+    tagFigcaption, ## the HTML `figcaption` element
+    tagFigure,     ## the HTML `figure` element
+    tagFont,       ## the deprecated HTML `font` element
+    tagFooter,     ## the HTML `footer` element
+    tagForm,       ## the HTML `form` element
+    tagFrame,      ## the HTML `frame` element
+    tagFrameset,   ## the deprecated HTML `frameset` element
+    tagH1,         ## the HTML `h1` element
+    tagH2,         ## the HTML `h2` element
+    tagH3,         ## the HTML `h3` element
+    tagH4,         ## the HTML `h4` element
+    tagH5,         ## the HTML `h5` element
+    tagH6,         ## the HTML `h6` element
+    tagHead,       ## the HTML `head` element
+    tagHeader,     ## the HTML `header` element
+    tagHgroup,     ## the HTML `hgroup` element
+    tagHtml,       ## the HTML `html` element
+    tagHr,         ## the HTML `hr` element
+    tagI,          ## the HTML `i` element
+    tagIframe,     ## the deprecated HTML `iframe` element
+    tagImg,        ## the HTML `img` element
+    tagInput,      ## the HTML `input` element
+    tagIns,        ## the HTML `ins` element
+    tagIsindex,    ## the deprecated HTML `isindex` element
+    tagKbd,        ## the HTML `kbd` element
+    tagKeygen,     ## the HTML `keygen` element
+    tagLabel,      ## the HTML `label` element
+    tagLegend,     ## the HTML `legend` element
+    tagLi,         ## the HTML `li` element
+    tagLink,       ## the HTML `link` element
+    tagMap,        ## the HTML `map` element
+    tagMark,       ## the HTML `mark` element
+    tagMenu,       ## the deprecated HTML `menu` element
+    tagMeta,       ## the HTML `meta` element
+    tagMeter,      ## the HTML `meter` element
+    tagNav,        ## the HTML `nav` element
+    tagNobr,       ## the deprecated HTML `nobr` element
+    tagNoframes,   ## the deprecated HTML `noframes` element
+    tagNoscript,   ## the HTML `noscript` element
+    tagObject,     ## the HTML `object` element
+    tagOl,         ## the HTML `ol` element
+    tagOptgroup,   ## the HTML `optgroup` element
+    tagOption,     ## the HTML `option` element
+    tagOutput,     ## the HTML `output` element
+    tagP,          ## the HTML `p` element
+    tagParam,      ## the HTML `param` element
+    tagPre,        ## the HTML `pre` element
+    tagProgress,   ## the HTML `progress` element
+    tagQ,          ## the HTML `q` element
+    tagRp,         ## the HTML `rp` element
+    tagRt,         ## the HTML `rt` element
+    tagRuby,       ## the HTML `ruby` element
+    tagS,          ## the deprecated HTML `s` element
+    tagSamp,       ## the HTML `samp` element
+    tagScript,     ## the HTML `script` element
+    tagSection,    ## the HTML `section` element
+    tagSelect,     ## the HTML `select` element
+    tagSmall,      ## the HTML `small` element
+    tagSource,     ## the HTML `source` element
+    tagSpan,       ## the HTML `span` element
+    tagStrike,     ## the deprecated HTML `strike` element
+    tagStrong,     ## the HTML `strong` element
+    tagStyle,      ## the HTML `style` element
+    tagSub,        ## the HTML `sub` element
+    tagSummary,    ## the HTML `summary` element
+    tagSup,        ## the HTML `sup` element
+    tagTable,      ## the HTML `table` element
+    tagTbody,      ## the HTML `tbody` element
+    tagTd,         ## the HTML `td` element
+    tagTextarea,   ## the HTML `textarea` element
+    tagTfoot,      ## the HTML `tfoot` element
+    tagTh,         ## the HTML `th` element
+    tagThead,      ## the HTML `thead` element
+    tagTime,       ## the HTML `time` element
+    tagTitle,      ## the HTML `title` element
+    tagTr,         ## the HTML `tr` element
+    tagTrack,      ## the HTML `track` element
+    tagTt,         ## the HTML `tt` element
+    tagU,          ## the deprecated HTML `u` element
+    tagUl,         ## the HTML `ul` element
+    tagVar,        ## the HTML `var` element
+    tagVideo,      ## the HTML `video` element
+    tagWbr         ## the HTML `wbr` element
 
 const
   tagToStr* = [
@@ -215,7 +218,7 @@ const
     tagMenu, tagNoframes}
   SingleTags* = {tagArea, tagBase, tagBasefont,
     tagBr, tagCol, tagFrame, tagHr, tagImg, tagIsindex,
-    tagLink, tagMeta, tagParam, tagWbr}
+    tagLink, tagMeta, tagParam, tagWbr, tagSource}
 
 proc allLower(s: string): bool =
   for c in s:
@@ -351,13 +354,13 @@ proc toHtmlTag(s: string): HtmlTag =
 
 
 proc htmlTag*(n: XmlNode): HtmlTag =
-  ## Gets `n`'s tag as a ``HtmlTag``.
+  ## Gets `n`'s tag as a `HtmlTag`.
   if n.clientData == 0:
     n.clientData = toHtmlTag(n.tag).ord
   result = HtmlTag(n.clientData)
 
 proc htmlTag*(s: string): HtmlTag =
-  ## Converts `s` to a ``HtmlTag``. If `s` is no HTML tag, ``tagUnknown`` is
+  ## Converts `s` to a `HtmlTag`. If `s` is no HTML tag, `tagUnknown` is
   ## returned.
   let s = if allLower(s): s else: toLowerAscii(s)
   result = toHtmlTag(s)
@@ -365,7 +368,7 @@ proc htmlTag*(s: string): HtmlTag =
 proc runeToEntity*(rune: Rune): string =
   ## converts a Rune to its numeric HTML entity equivalent.
   runnableExamples:
-    import unicode
+    import std/unicode
     doAssert runeToEntity(Rune(0)) == ""
     doAssert runeToEntity(Rune(-1)) == ""
     doAssert runeToEntity("Ü".runeAt(0)) == "#220"
@@ -374,11 +377,11 @@ proc runeToEntity*(rune: Rune): string =
   else: result = '#' & $rune.ord
 
 proc entityToRune*(entity: string): Rune =
-  ## Converts an HTML entity name like ``&Uuml;`` or values like ``&#220;``
-  ## or ``&#x000DC;`` to its UTF-8 equivalent.
+  ## Converts an HTML entity name like `&Uuml;` or values like `&#220;`
+  ## or `&#x000DC;` to its UTF-8 equivalent.
   ## Rune(0) is returned if the entity name is unknown.
   runnableExamples:
-    import unicode
+    import std/unicode
     doAssert entityToRune("") == Rune(0)
     doAssert entityToRune("a") == Rune(0)
     doAssert entityToRune("gt") == ">".runeAt(0)
@@ -387,1502 +390,1505 @@ proc entityToRune*(entity: string): Rune =
     doAssert entityToRune("#x0003F") == "?".runeAt(0)
   if entity.len < 2: return # smallest entity has length 2
   if entity[0] == '#':
+    var runeValue = 0
     case entity[1]
     of '0'..'9':
-      try: return Rune(parseInt(entity[1..^1]))
-      except: return
+      try: runeValue = parseInt(entity[1..^1])
+      except ValueError: discard
     of 'x', 'X': # not case sensitive here
-      try: return Rune(parseHexInt(entity[2..^1]))
-      except: return
-    else: return # other entities are not defined with prefix ``#``
+      try: runeValue = parseHexInt(entity[2..^1])
+      except ValueError: discard
+    else: discard # other entities are not defined with prefix `#`
+    if runeValue notin 0..0x10FFFF: runeValue = 0 # only return legal values
+    return Rune(runeValue)
   case entity # entity names are case sensitive
-  of "Tab": result = Rune(0x00009)
-  of "NewLine": result = Rune(0x0000A)
-  of "excl": result = Rune(0x00021)
-  of "quot", "QUOT": result = Rune(0x00022)
-  of "num": result = Rune(0x00023)
-  of "dollar": result = Rune(0x00024)
-  of "percnt": result = Rune(0x00025)
-  of "amp", "AMP": result = Rune(0x00026)
-  of "apos": result = Rune(0x00027)
-  of "lpar": result = Rune(0x00028)
-  of "rpar": result = Rune(0x00029)
-  of "ast", "midast": result = Rune(0x0002A)
-  of "plus": result = Rune(0x0002B)
-  of "comma": result = Rune(0x0002C)
-  of "period": result = Rune(0x0002E)
-  of "sol": result = Rune(0x0002F)
-  of "colon": result = Rune(0x0003A)
-  of "semi": result = Rune(0x0003B)
-  of "lt", "LT": result = Rune(0x0003C)
-  of "equals": result = Rune(0x0003D)
-  of "gt", "GT": result = Rune(0x0003E)
-  of "quest": result = Rune(0x0003F)
-  of "commat": result = Rune(0x00040)
-  of "lsqb", "lbrack": result = Rune(0x0005B)
-  of "bsol": result = Rune(0x0005C)
-  of "rsqb", "rbrack": result = Rune(0x0005D)
-  of "Hat": result = Rune(0x0005E)
-  of "lowbar": result = Rune(0x0005F)
-  of "grave", "DiacriticalGrave": result = Rune(0x00060)
-  of "lcub", "lbrace": result = Rune(0x0007B)
-  of "verbar", "vert", "VerticalLine": result = Rune(0x0007C)
-  of "rcub", "rbrace": result = Rune(0x0007D)
-  of "nbsp", "NonBreakingSpace": result = Rune(0x000A0)
-  of "iexcl": result = Rune(0x000A1)
-  of "cent": result = Rune(0x000A2)
-  of "pound": result = Rune(0x000A3)
-  of "curren": result = Rune(0x000A4)
-  of "yen": result = Rune(0x000A5)
-  of "brvbar": result = Rune(0x000A6)
-  of "sect": result = Rune(0x000A7)
-  of "Dot", "die", "DoubleDot", "uml": result = Rune(0x000A8)
-  of "copy", "COPY": result = Rune(0x000A9)
-  of "ordf": result = Rune(0x000AA)
-  of "laquo": result = Rune(0x000AB)
-  of "not": result = Rune(0x000AC)
-  of "shy": result = Rune(0x000AD)
-  of "reg", "circledR", "REG": result = Rune(0x000AE)
-  of "macr", "OverBar", "strns": result = Rune(0x000AF)
-  of "deg": result = Rune(0x000B0)
-  of "plusmn", "pm", "PlusMinus": result = Rune(0x000B1)
-  of "sup2": result = Rune(0x000B2)
-  of "sup3": result = Rune(0x000B3)
-  of "acute", "DiacriticalAcute": result = Rune(0x000B4)
-  of "micro": result = Rune(0x000B5)
-  of "para": result = Rune(0x000B6)
-  of "middot", "centerdot", "CenterDot": result = Rune(0x000B7)
-  of "cedil", "Cedilla": result = Rune(0x000B8)
-  of "sup1": result = Rune(0x000B9)
-  of "ordm": result = Rune(0x000BA)
-  of "raquo": result = Rune(0x000BB)
-  of "frac14": result = Rune(0x000BC)
-  of "frac12", "half": result = Rune(0x000BD)
-  of "frac34": result = Rune(0x000BE)
-  of "iquest": result = Rune(0x000BF)
-  of "Agrave": result = Rune(0x000C0)
-  of "Aacute": result = Rune(0x000C1)
-  of "Acirc": result = Rune(0x000C2)
-  of "Atilde": result = Rune(0x000C3)
-  of "Auml": result = Rune(0x000C4)
-  of "Aring": result = Rune(0x000C5)
-  of "AElig": result = Rune(0x000C6)
-  of "Ccedil": result = Rune(0x000C7)
-  of "Egrave": result = Rune(0x000C8)
-  of "Eacute": result = Rune(0x000C9)
-  of "Ecirc": result = Rune(0x000CA)
-  of "Euml": result = Rune(0x000CB)
-  of "Igrave": result = Rune(0x000CC)
-  of "Iacute": result = Rune(0x000CD)
-  of "Icirc": result = Rune(0x000CE)
-  of "Iuml": result = Rune(0x000CF)
-  of "ETH": result = Rune(0x000D0)
-  of "Ntilde": result = Rune(0x000D1)
-  of "Ograve": result = Rune(0x000D2)
-  of "Oacute": result = Rune(0x000D3)
-  of "Ocirc": result = Rune(0x000D4)
-  of "Otilde": result = Rune(0x000D5)
-  of "Ouml": result = Rune(0x000D6)
-  of "times": result = Rune(0x000D7)
-  of "Oslash": result = Rune(0x000D8)
-  of "Ugrave": result = Rune(0x000D9)
-  of "Uacute": result = Rune(0x000DA)
-  of "Ucirc": result = Rune(0x000DB)
-  of "Uuml": result = Rune(0x000DC)
-  of "Yacute": result = Rune(0x000DD)
-  of "THORN": result = Rune(0x000DE)
-  of "szlig": result = Rune(0x000DF)
-  of "agrave": result = Rune(0x000E0)
-  of "aacute": result = Rune(0x000E1)
-  of "acirc": result = Rune(0x000E2)
-  of "atilde": result = Rune(0x000E3)
-  of "auml": result = Rune(0x000E4)
-  of "aring": result = Rune(0x000E5)
-  of "aelig": result = Rune(0x000E6)
-  of "ccedil": result = Rune(0x000E7)
-  of "egrave": result = Rune(0x000E8)
-  of "eacute": result = Rune(0x000E9)
-  of "ecirc": result = Rune(0x000EA)
-  of "euml": result = Rune(0x000EB)
-  of "igrave": result = Rune(0x000EC)
-  of "iacute": result = Rune(0x000ED)
-  of "icirc": result = Rune(0x000EE)
-  of "iuml": result = Rune(0x000EF)
-  of "eth": result = Rune(0x000F0)
-  of "ntilde": result = Rune(0x000F1)
-  of "ograve": result = Rune(0x000F2)
-  of "oacute": result = Rune(0x000F3)
-  of "ocirc": result = Rune(0x000F4)
-  of "otilde": result = Rune(0x000F5)
-  of "ouml": result = Rune(0x000F6)
-  of "divide", "div": result = Rune(0x000F7)
-  of "oslash": result = Rune(0x000F8)
-  of "ugrave": result = Rune(0x000F9)
-  of "uacute": result = Rune(0x000FA)
-  of "ucirc": result = Rune(0x000FB)
-  of "uuml": result = Rune(0x000FC)
-  of "yacute": result = Rune(0x000FD)
-  of "thorn": result = Rune(0x000FE)
-  of "yuml": result = Rune(0x000FF)
-  of "Amacr": result = Rune(0x00100)
-  of "amacr": result = Rune(0x00101)
-  of "Abreve": result = Rune(0x00102)
-  of "abreve": result = Rune(0x00103)
-  of "Aogon": result = Rune(0x00104)
-  of "aogon": result = Rune(0x00105)
-  of "Cacute": result = Rune(0x00106)
-  of "cacute": result = Rune(0x00107)
-  of "Ccirc": result = Rune(0x00108)
-  of "ccirc": result = Rune(0x00109)
-  of "Cdot": result = Rune(0x0010A)
-  of "cdot": result = Rune(0x0010B)
-  of "Ccaron": result = Rune(0x0010C)
-  of "ccaron": result = Rune(0x0010D)
-  of "Dcaron": result = Rune(0x0010E)
-  of "dcaron": result = Rune(0x0010F)
-  of "Dstrok": result = Rune(0x00110)
-  of "dstrok": result = Rune(0x00111)
-  of "Emacr": result = Rune(0x00112)
-  of "emacr": result = Rune(0x00113)
-  of "Edot": result = Rune(0x00116)
-  of "edot": result = Rune(0x00117)
-  of "Eogon": result = Rune(0x00118)
-  of "eogon": result = Rune(0x00119)
-  of "Ecaron": result = Rune(0x0011A)
-  of "ecaron": result = Rune(0x0011B)
-  of "Gcirc": result = Rune(0x0011C)
-  of "gcirc": result = Rune(0x0011D)
-  of "Gbreve": result = Rune(0x0011E)
-  of "gbreve": result = Rune(0x0011F)
-  of "Gdot": result = Rune(0x00120)
-  of "gdot": result = Rune(0x00121)
-  of "Gcedil": result = Rune(0x00122)
-  of "Hcirc": result = Rune(0x00124)
-  of "hcirc": result = Rune(0x00125)
-  of "Hstrok": result = Rune(0x00126)
-  of "hstrok": result = Rune(0x00127)
-  of "Itilde": result = Rune(0x00128)
-  of "itilde": result = Rune(0x00129)
-  of "Imacr": result = Rune(0x0012A)
-  of "imacr": result = Rune(0x0012B)
-  of "Iogon": result = Rune(0x0012E)
-  of "iogon": result = Rune(0x0012F)
-  of "Idot": result = Rune(0x00130)
-  of "imath", "inodot": result = Rune(0x00131)
-  of "IJlig": result = Rune(0x00132)
-  of "ijlig": result = Rune(0x00133)
-  of "Jcirc": result = Rune(0x00134)
-  of "jcirc": result = Rune(0x00135)
-  of "Kcedil": result = Rune(0x00136)
-  of "kcedil": result = Rune(0x00137)
-  of "kgreen": result = Rune(0x00138)
-  of "Lacute": result = Rune(0x00139)
-  of "lacute": result = Rune(0x0013A)
-  of "Lcedil": result = Rune(0x0013B)
-  of "lcedil": result = Rune(0x0013C)
-  of "Lcaron": result = Rune(0x0013D)
-  of "lcaron": result = Rune(0x0013E)
-  of "Lmidot": result = Rune(0x0013F)
-  of "lmidot": result = Rune(0x00140)
-  of "Lstrok": result = Rune(0x00141)
-  of "lstrok": result = Rune(0x00142)
-  of "Nacute": result = Rune(0x00143)
-  of "nacute": result = Rune(0x00144)
-  of "Ncedil": result = Rune(0x00145)
-  of "ncedil": result = Rune(0x00146)
-  of "Ncaron": result = Rune(0x00147)
-  of "ncaron": result = Rune(0x00148)
-  of "napos": result = Rune(0x00149)
-  of "ENG": result = Rune(0x0014A)
-  of "eng": result = Rune(0x0014B)
-  of "Omacr": result = Rune(0x0014C)
-  of "omacr": result = Rune(0x0014D)
-  of "Odblac": result = Rune(0x00150)
-  of "odblac": result = Rune(0x00151)
-  of "OElig": result = Rune(0x00152)
-  of "oelig": result = Rune(0x00153)
-  of "Racute": result = Rune(0x00154)
-  of "racute": result = Rune(0x00155)
-  of "Rcedil": result = Rune(0x00156)
-  of "rcedil": result = Rune(0x00157)
-  of "Rcaron": result = Rune(0x00158)
-  of "rcaron": result = Rune(0x00159)
-  of "Sacute": result = Rune(0x0015A)
-  of "sacute": result = Rune(0x0015B)
-  of "Scirc": result = Rune(0x0015C)
-  of "scirc": result = Rune(0x0015D)
-  of "Scedil": result = Rune(0x0015E)
-  of "scedil": result = Rune(0x0015F)
-  of "Scaron": result = Rune(0x00160)
-  of "scaron": result = Rune(0x00161)
-  of "Tcedil": result = Rune(0x00162)
-  of "tcedil": result = Rune(0x00163)
-  of "Tcaron": result = Rune(0x00164)
-  of "tcaron": result = Rune(0x00165)
-  of "Tstrok": result = Rune(0x00166)
-  of "tstrok": result = Rune(0x00167)
-  of "Utilde": result = Rune(0x00168)
-  of "utilde": result = Rune(0x00169)
-  of "Umacr": result = Rune(0x0016A)
-  of "umacr": result = Rune(0x0016B)
-  of "Ubreve": result = Rune(0x0016C)
-  of "ubreve": result = Rune(0x0016D)
-  of "Uring": result = Rune(0x0016E)
-  of "uring": result = Rune(0x0016F)
-  of "Udblac": result = Rune(0x00170)
-  of "udblac": result = Rune(0x00171)
-  of "Uogon": result = Rune(0x00172)
-  of "uogon": result = Rune(0x00173)
-  of "Wcirc": result = Rune(0x00174)
-  of "wcirc": result = Rune(0x00175)
-  of "Ycirc": result = Rune(0x00176)
-  of "ycirc": result = Rune(0x00177)
-  of "Yuml": result = Rune(0x00178)
-  of "Zacute": result = Rune(0x00179)
-  of "zacute": result = Rune(0x0017A)
-  of "Zdot": result = Rune(0x0017B)
-  of "zdot": result = Rune(0x0017C)
-  of "Zcaron": result = Rune(0x0017D)
-  of "zcaron": result = Rune(0x0017E)
-  of "fnof": result = Rune(0x00192)
-  of "imped": result = Rune(0x001B5)
-  of "gacute": result = Rune(0x001F5)
-  of "jmath": result = Rune(0x00237)
-  of "circ": result = Rune(0x002C6)
-  of "caron", "Hacek": result = Rune(0x002C7)
-  of "breve", "Breve": result = Rune(0x002D8)
-  of "dot", "DiacriticalDot": result = Rune(0x002D9)
-  of "ring": result = Rune(0x002DA)
-  of "ogon": result = Rune(0x002DB)
-  of "tilde", "DiacriticalTilde": result = Rune(0x002DC)
-  of "dblac", "DiacriticalDoubleAcute": result = Rune(0x002DD)
-  of "DownBreve": result = Rune(0x00311)
-  of "UnderBar": result = Rune(0x00332)
-  of "Alpha": result = Rune(0x00391)
-  of "Beta": result = Rune(0x00392)
-  of "Gamma": result = Rune(0x00393)
-  of "Delta": result = Rune(0x00394)
-  of "Epsilon": result = Rune(0x00395)
-  of "Zeta": result = Rune(0x00396)
-  of "Eta": result = Rune(0x00397)
-  of "Theta": result = Rune(0x00398)
-  of "Iota": result = Rune(0x00399)
-  of "Kappa": result = Rune(0x0039A)
-  of "Lambda": result = Rune(0x0039B)
-  of "Mu": result = Rune(0x0039C)
-  of "Nu": result = Rune(0x0039D)
-  of "Xi": result = Rune(0x0039E)
-  of "Omicron": result = Rune(0x0039F)
-  of "Pi": result = Rune(0x003A0)
-  of "Rho": result = Rune(0x003A1)
-  of "Sigma": result = Rune(0x003A3)
-  of "Tau": result = Rune(0x003A4)
-  of "Upsilon": result = Rune(0x003A5)
-  of "Phi": result = Rune(0x003A6)
-  of "Chi": result = Rune(0x003A7)
-  of "Psi": result = Rune(0x003A8)
-  of "Omega": result = Rune(0x003A9)
-  of "alpha": result = Rune(0x003B1)
-  of "beta": result = Rune(0x003B2)
-  of "gamma": result = Rune(0x003B3)
-  of "delta": result = Rune(0x003B4)
-  of "epsiv", "varepsilon", "epsilon": result = Rune(0x003B5)
-  of "zeta": result = Rune(0x003B6)
-  of "eta": result = Rune(0x003B7)
-  of "theta": result = Rune(0x003B8)
-  of "iota": result = Rune(0x003B9)
-  of "kappa": result = Rune(0x003BA)
-  of "lambda": result = Rune(0x003BB)
-  of "mu": result = Rune(0x003BC)
-  of "nu": result = Rune(0x003BD)
-  of "xi": result = Rune(0x003BE)
-  of "omicron": result = Rune(0x003BF)
-  of "pi": result = Rune(0x003C0)
-  of "rho": result = Rune(0x003C1)
-  of "sigmav", "varsigma", "sigmaf": result = Rune(0x003C2)
-  of "sigma": result = Rune(0x003C3)
-  of "tau": result = Rune(0x003C4)
-  of "upsi", "upsilon": result = Rune(0x003C5)
-  of "phi", "phiv", "varphi": result = Rune(0x003C6)
-  of "chi": result = Rune(0x003C7)
-  of "psi": result = Rune(0x003C8)
-  of "omega": result = Rune(0x003C9)
-  of "thetav", "vartheta", "thetasym": result = Rune(0x003D1)
-  of "Upsi", "upsih": result = Rune(0x003D2)
-  of "straightphi": result = Rune(0x003D5)
-  of "piv", "varpi": result = Rune(0x003D6)
-  of "Gammad": result = Rune(0x003DC)
-  of "gammad", "digamma": result = Rune(0x003DD)
-  of "kappav", "varkappa": result = Rune(0x003F0)
-  of "rhov", "varrho": result = Rune(0x003F1)
-  of "epsi", "straightepsilon": result = Rune(0x003F5)
-  of "bepsi", "backepsilon": result = Rune(0x003F6)
-  of "IOcy": result = Rune(0x00401)
-  of "DJcy": result = Rune(0x00402)
-  of "GJcy": result = Rune(0x00403)
-  of "Jukcy": result = Rune(0x00404)
-  of "DScy": result = Rune(0x00405)
-  of "Iukcy": result = Rune(0x00406)
-  of "YIcy": result = Rune(0x00407)
-  of "Jsercy": result = Rune(0x00408)
-  of "LJcy": result = Rune(0x00409)
-  of "NJcy": result = Rune(0x0040A)
-  of "TSHcy": result = Rune(0x0040B)
-  of "KJcy": result = Rune(0x0040C)
-  of "Ubrcy": result = Rune(0x0040E)
-  of "DZcy": result = Rune(0x0040F)
-  of "Acy": result = Rune(0x00410)
-  of "Bcy": result = Rune(0x00411)
-  of "Vcy": result = Rune(0x00412)
-  of "Gcy": result = Rune(0x00413)
-  of "Dcy": result = Rune(0x00414)
-  of "IEcy": result = Rune(0x00415)
-  of "ZHcy": result = Rune(0x00416)
-  of "Zcy": result = Rune(0x00417)
-  of "Icy": result = Rune(0x00418)
-  of "Jcy": result = Rune(0x00419)
-  of "Kcy": result = Rune(0x0041A)
-  of "Lcy": result = Rune(0x0041B)
-  of "Mcy": result = Rune(0x0041C)
-  of "Ncy": result = Rune(0x0041D)
-  of "Ocy": result = Rune(0x0041E)
-  of "Pcy": result = Rune(0x0041F)
-  of "Rcy": result = Rune(0x00420)
-  of "Scy": result = Rune(0x00421)
-  of "Tcy": result = Rune(0x00422)
-  of "Ucy": result = Rune(0x00423)
-  of "Fcy": result = Rune(0x00424)
-  of "KHcy": result = Rune(0x00425)
-  of "TScy": result = Rune(0x00426)
-  of "CHcy": result = Rune(0x00427)
-  of "SHcy": result = Rune(0x00428)
-  of "SHCHcy": result = Rune(0x00429)
-  of "HARDcy": result = Rune(0x0042A)
-  of "Ycy": result = Rune(0x0042B)
-  of "SOFTcy": result = Rune(0x0042C)
-  of "Ecy": result = Rune(0x0042D)
-  of "YUcy": result = Rune(0x0042E)
-  of "YAcy": result = Rune(0x0042F)
-  of "acy": result = Rune(0x00430)
-  of "bcy": result = Rune(0x00431)
-  of "vcy": result = Rune(0x00432)
-  of "gcy": result = Rune(0x00433)
-  of "dcy": result = Rune(0x00434)
-  of "iecy": result = Rune(0x00435)
-  of "zhcy": result = Rune(0x00436)
-  of "zcy": result = Rune(0x00437)
-  of "icy": result = Rune(0x00438)
-  of "jcy": result = Rune(0x00439)
-  of "kcy": result = Rune(0x0043A)
-  of "lcy": result = Rune(0x0043B)
-  of "mcy": result = Rune(0x0043C)
-  of "ncy": result = Rune(0x0043D)
-  of "ocy": result = Rune(0x0043E)
-  of "pcy": result = Rune(0x0043F)
-  of "rcy": result = Rune(0x00440)
-  of "scy": result = Rune(0x00441)
-  of "tcy": result = Rune(0x00442)
-  of "ucy": result = Rune(0x00443)
-  of "fcy": result = Rune(0x00444)
-  of "khcy": result = Rune(0x00445)
-  of "tscy": result = Rune(0x00446)
-  of "chcy": result = Rune(0x00447)
-  of "shcy": result = Rune(0x00448)
-  of "shchcy": result = Rune(0x00449)
-  of "hardcy": result = Rune(0x0044A)
-  of "ycy": result = Rune(0x0044B)
-  of "softcy": result = Rune(0x0044C)
-  of "ecy": result = Rune(0x0044D)
-  of "yucy": result = Rune(0x0044E)
-  of "yacy": result = Rune(0x0044F)
-  of "iocy": result = Rune(0x00451)
-  of "djcy": result = Rune(0x00452)
-  of "gjcy": result = Rune(0x00453)
-  of "jukcy": result = Rune(0x00454)
-  of "dscy": result = Rune(0x00455)
-  of "iukcy": result = Rune(0x00456)
-  of "yicy": result = Rune(0x00457)
-  of "jsercy": result = Rune(0x00458)
-  of "ljcy": result = Rune(0x00459)
-  of "njcy": result = Rune(0x0045A)
-  of "tshcy": result = Rune(0x0045B)
-  of "kjcy": result = Rune(0x0045C)
-  of "ubrcy": result = Rune(0x0045E)
-  of "dzcy": result = Rune(0x0045F)
-  of "ensp": result = Rune(0x02002)
-  of "emsp": result = Rune(0x02003)
-  of "emsp13": result = Rune(0x02004)
-  of "emsp14": result = Rune(0x02005)
-  of "numsp": result = Rune(0x02007)
-  of "puncsp": result = Rune(0x02008)
-  of "thinsp", "ThinSpace": result = Rune(0x02009)
-  of "hairsp", "VeryThinSpace": result = Rune(0x0200A)
+  of "Tab": Rune(0x00009)
+  of "NewLine": Rune(0x0000A)
+  of "excl": Rune(0x00021)
+  of "quot", "QUOT": Rune(0x00022)
+  of "num": Rune(0x00023)
+  of "dollar": Rune(0x00024)
+  of "percnt": Rune(0x00025)
+  of "amp", "AMP": Rune(0x00026)
+  of "apos": Rune(0x00027)
+  of "lpar": Rune(0x00028)
+  of "rpar": Rune(0x00029)
+  of "ast", "midast": Rune(0x0002A)
+  of "plus": Rune(0x0002B)
+  of "comma": Rune(0x0002C)
+  of "period": Rune(0x0002E)
+  of "sol": Rune(0x0002F)
+  of "colon": Rune(0x0003A)
+  of "semi": Rune(0x0003B)
+  of "lt", "LT": Rune(0x0003C)
+  of "equals": Rune(0x0003D)
+  of "gt", "GT": Rune(0x0003E)
+  of "quest": Rune(0x0003F)
+  of "commat": Rune(0x00040)
+  of "lsqb", "lbrack": Rune(0x0005B)
+  of "bsol": Rune(0x0005C)
+  of "rsqb", "rbrack": Rune(0x0005D)
+  of "Hat": Rune(0x0005E)
+  of "lowbar": Rune(0x0005F)
+  of "grave", "DiacriticalGrave": Rune(0x00060)
+  of "lcub", "lbrace": Rune(0x0007B)
+  of "verbar", "vert", "VerticalLine": Rune(0x0007C)
+  of "rcub", "rbrace": Rune(0x0007D)
+  of "nbsp", "NonBreakingSpace": Rune(0x000A0)
+  of "iexcl": Rune(0x000A1)
+  of "cent": Rune(0x000A2)
+  of "pound": Rune(0x000A3)
+  of "curren": Rune(0x000A4)
+  of "yen": Rune(0x000A5)
+  of "brvbar": Rune(0x000A6)
+  of "sect": Rune(0x000A7)
+  of "Dot", "die", "DoubleDot", "uml": Rune(0x000A8)
+  of "copy", "COPY": Rune(0x000A9)
+  of "ordf": Rune(0x000AA)
+  of "laquo": Rune(0x000AB)
+  of "not": Rune(0x000AC)
+  of "shy": Rune(0x000AD)
+  of "reg", "circledR", "REG": Rune(0x000AE)
+  of "macr", "OverBar", "strns": Rune(0x000AF)
+  of "deg": Rune(0x000B0)
+  of "plusmn", "pm", "PlusMinus": Rune(0x000B1)
+  of "sup2": Rune(0x000B2)
+  of "sup3": Rune(0x000B3)
+  of "acute", "DiacriticalAcute": Rune(0x000B4)
+  of "micro": Rune(0x000B5)
+  of "para": Rune(0x000B6)
+  of "middot", "centerdot", "CenterDot": Rune(0x000B7)
+  of "cedil", "Cedilla": Rune(0x000B8)
+  of "sup1": Rune(0x000B9)
+  of "ordm": Rune(0x000BA)
+  of "raquo": Rune(0x000BB)
+  of "frac14": Rune(0x000BC)
+  of "frac12", "half": Rune(0x000BD)
+  of "frac34": Rune(0x000BE)
+  of "iquest": Rune(0x000BF)
+  of "Agrave": Rune(0x000C0)
+  of "Aacute": Rune(0x000C1)
+  of "Acirc": Rune(0x000C2)
+  of "Atilde": Rune(0x000C3)
+  of "Auml": Rune(0x000C4)
+  of "Aring": Rune(0x000C5)
+  of "AElig": Rune(0x000C6)
+  of "Ccedil": Rune(0x000C7)
+  of "Egrave": Rune(0x000C8)
+  of "Eacute": Rune(0x000C9)
+  of "Ecirc": Rune(0x000CA)
+  of "Euml": Rune(0x000CB)
+  of "Igrave": Rune(0x000CC)
+  of "Iacute": Rune(0x000CD)
+  of "Icirc": Rune(0x000CE)
+  of "Iuml": Rune(0x000CF)
+  of "ETH": Rune(0x000D0)
+  of "Ntilde": Rune(0x000D1)
+  of "Ograve": Rune(0x000D2)
+  of "Oacute": Rune(0x000D3)
+  of "Ocirc": Rune(0x000D4)
+  of "Otilde": Rune(0x000D5)
+  of "Ouml": Rune(0x000D6)
+  of "times": Rune(0x000D7)
+  of "Oslash": Rune(0x000D8)
+  of "Ugrave": Rune(0x000D9)
+  of "Uacute": Rune(0x000DA)
+  of "Ucirc": Rune(0x000DB)
+  of "Uuml": Rune(0x000DC)
+  of "Yacute": Rune(0x000DD)
+  of "THORN": Rune(0x000DE)
+  of "szlig": Rune(0x000DF)
+  of "agrave": Rune(0x000E0)
+  of "aacute": Rune(0x000E1)
+  of "acirc": Rune(0x000E2)
+  of "atilde": Rune(0x000E3)
+  of "auml": Rune(0x000E4)
+  of "aring": Rune(0x000E5)
+  of "aelig": Rune(0x000E6)
+  of "ccedil": Rune(0x000E7)
+  of "egrave": Rune(0x000E8)
+  of "eacute": Rune(0x000E9)
+  of "ecirc": Rune(0x000EA)
+  of "euml": Rune(0x000EB)
+  of "igrave": Rune(0x000EC)
+  of "iacute": Rune(0x000ED)
+  of "icirc": Rune(0x000EE)
+  of "iuml": Rune(0x000EF)
+  of "eth": Rune(0x000F0)
+  of "ntilde": Rune(0x000F1)
+  of "ograve": Rune(0x000F2)
+  of "oacute": Rune(0x000F3)
+  of "ocirc": Rune(0x000F4)
+  of "otilde": Rune(0x000F5)
+  of "ouml": Rune(0x000F6)
+  of "divide", "div": Rune(0x000F7)
+  of "oslash": Rune(0x000F8)
+  of "ugrave": Rune(0x000F9)
+  of "uacute": Rune(0x000FA)
+  of "ucirc": Rune(0x000FB)
+  of "uuml": Rune(0x000FC)
+  of "yacute": Rune(0x000FD)
+  of "thorn": Rune(0x000FE)
+  of "yuml": Rune(0x000FF)
+  of "Amacr": Rune(0x00100)
+  of "amacr": Rune(0x00101)
+  of "Abreve": Rune(0x00102)
+  of "abreve": Rune(0x00103)
+  of "Aogon": Rune(0x00104)
+  of "aogon": Rune(0x00105)
+  of "Cacute": Rune(0x00106)
+  of "cacute": Rune(0x00107)
+  of "Ccirc": Rune(0x00108)
+  of "ccirc": Rune(0x00109)
+  of "Cdot": Rune(0x0010A)
+  of "cdot": Rune(0x0010B)
+  of "Ccaron": Rune(0x0010C)
+  of "ccaron": Rune(0x0010D)
+  of "Dcaron": Rune(0x0010E)
+  of "dcaron": Rune(0x0010F)
+  of "Dstrok": Rune(0x00110)
+  of "dstrok": Rune(0x00111)
+  of "Emacr": Rune(0x00112)
+  of "emacr": Rune(0x00113)
+  of "Edot": Rune(0x00116)
+  of "edot": Rune(0x00117)
+  of "Eogon": Rune(0x00118)
+  of "eogon": Rune(0x00119)
+  of "Ecaron": Rune(0x0011A)
+  of "ecaron": Rune(0x0011B)
+  of "Gcirc": Rune(0x0011C)
+  of "gcirc": Rune(0x0011D)
+  of "Gbreve": Rune(0x0011E)
+  of "gbreve": Rune(0x0011F)
+  of "Gdot": Rune(0x00120)
+  of "gdot": Rune(0x00121)
+  of "Gcedil": Rune(0x00122)
+  of "Hcirc": Rune(0x00124)
+  of "hcirc": Rune(0x00125)
+  of "Hstrok": Rune(0x00126)
+  of "hstrok": Rune(0x00127)
+  of "Itilde": Rune(0x00128)
+  of "itilde": Rune(0x00129)
+  of "Imacr": Rune(0x0012A)
+  of "imacr": Rune(0x0012B)
+  of "Iogon": Rune(0x0012E)
+  of "iogon": Rune(0x0012F)
+  of "Idot": Rune(0x00130)
+  of "imath", "inodot": Rune(0x00131)
+  of "IJlig": Rune(0x00132)
+  of "ijlig": Rune(0x00133)
+  of "Jcirc": Rune(0x00134)
+  of "jcirc": Rune(0x00135)
+  of "Kcedil": Rune(0x00136)
+  of "kcedil": Rune(0x00137)
+  of "kgreen": Rune(0x00138)
+  of "Lacute": Rune(0x00139)
+  of "lacute": Rune(0x0013A)
+  of "Lcedil": Rune(0x0013B)
+  of "lcedil": Rune(0x0013C)
+  of "Lcaron": Rune(0x0013D)
+  of "lcaron": Rune(0x0013E)
+  of "Lmidot": Rune(0x0013F)
+  of "lmidot": Rune(0x00140)
+  of "Lstrok": Rune(0x00141)
+  of "lstrok": Rune(0x00142)
+  of "Nacute": Rune(0x00143)
+  of "nacute": Rune(0x00144)
+  of "Ncedil": Rune(0x00145)
+  of "ncedil": Rune(0x00146)
+  of "Ncaron": Rune(0x00147)
+  of "ncaron": Rune(0x00148)
+  of "napos": Rune(0x00149)
+  of "ENG": Rune(0x0014A)
+  of "eng": Rune(0x0014B)
+  of "Omacr": Rune(0x0014C)
+  of "omacr": Rune(0x0014D)
+  of "Odblac": Rune(0x00150)
+  of "odblac": Rune(0x00151)
+  of "OElig": Rune(0x00152)
+  of "oelig": Rune(0x00153)
+  of "Racute": Rune(0x00154)
+  of "racute": Rune(0x00155)
+  of "Rcedil": Rune(0x00156)
+  of "rcedil": Rune(0x00157)
+  of "Rcaron": Rune(0x00158)
+  of "rcaron": Rune(0x00159)
+  of "Sacute": Rune(0x0015A)
+  of "sacute": Rune(0x0015B)
+  of "Scirc": Rune(0x0015C)
+  of "scirc": Rune(0x0015D)
+  of "Scedil": Rune(0x0015E)
+  of "scedil": Rune(0x0015F)
+  of "Scaron": Rune(0x00160)
+  of "scaron": Rune(0x00161)
+  of "Tcedil": Rune(0x00162)
+  of "tcedil": Rune(0x00163)
+  of "Tcaron": Rune(0x00164)
+  of "tcaron": Rune(0x00165)
+  of "Tstrok": Rune(0x00166)
+  of "tstrok": Rune(0x00167)
+  of "Utilde": Rune(0x00168)
+  of "utilde": Rune(0x00169)
+  of "Umacr": Rune(0x0016A)
+  of "umacr": Rune(0x0016B)
+  of "Ubreve": Rune(0x0016C)
+  of "ubreve": Rune(0x0016D)
+  of "Uring": Rune(0x0016E)
+  of "uring": Rune(0x0016F)
+  of "Udblac": Rune(0x00170)
+  of "udblac": Rune(0x00171)
+  of "Uogon": Rune(0x00172)
+  of "uogon": Rune(0x00173)
+  of "Wcirc": Rune(0x00174)
+  of "wcirc": Rune(0x00175)
+  of "Ycirc": Rune(0x00176)
+  of "ycirc": Rune(0x00177)
+  of "Yuml": Rune(0x00178)
+  of "Zacute": Rune(0x00179)
+  of "zacute": Rune(0x0017A)
+  of "Zdot": Rune(0x0017B)
+  of "zdot": Rune(0x0017C)
+  of "Zcaron": Rune(0x0017D)
+  of "zcaron": Rune(0x0017E)
+  of "fnof": Rune(0x00192)
+  of "imped": Rune(0x001B5)
+  of "gacute": Rune(0x001F5)
+  of "jmath": Rune(0x00237)
+  of "circ": Rune(0x002C6)
+  of "caron", "Hacek": Rune(0x002C7)
+  of "breve", "Breve": Rune(0x002D8)
+  of "dot", "DiacriticalDot": Rune(0x002D9)
+  of "ring": Rune(0x002DA)
+  of "ogon": Rune(0x002DB)
+  of "tilde", "DiacriticalTilde": Rune(0x002DC)
+  of "dblac", "DiacriticalDoubleAcute": Rune(0x002DD)
+  of "DownBreve": Rune(0x00311)
+  of "UnderBar": Rune(0x00332)
+  of "Alpha": Rune(0x00391)
+  of "Beta": Rune(0x00392)
+  of "Gamma": Rune(0x00393)
+  of "Delta": Rune(0x00394)
+  of "Epsilon": Rune(0x00395)
+  of "Zeta": Rune(0x00396)
+  of "Eta": Rune(0x00397)
+  of "Theta": Rune(0x00398)
+  of "Iota": Rune(0x00399)
+  of "Kappa": Rune(0x0039A)
+  of "Lambda": Rune(0x0039B)
+  of "Mu": Rune(0x0039C)
+  of "Nu": Rune(0x0039D)
+  of "Xi": Rune(0x0039E)
+  of "Omicron": Rune(0x0039F)
+  of "Pi": Rune(0x003A0)
+  of "Rho": Rune(0x003A1)
+  of "Sigma": Rune(0x003A3)
+  of "Tau": Rune(0x003A4)
+  of "Upsilon": Rune(0x003A5)
+  of "Phi": Rune(0x003A6)
+  of "Chi": Rune(0x003A7)
+  of "Psi": Rune(0x003A8)
+  of "Omega": Rune(0x003A9)
+  of "alpha": Rune(0x003B1)
+  of "beta": Rune(0x003B2)
+  of "gamma": Rune(0x003B3)
+  of "delta": Rune(0x003B4)
+  of "epsiv", "varepsilon", "epsilon": Rune(0x003B5)
+  of "zeta": Rune(0x003B6)
+  of "eta": Rune(0x003B7)
+  of "theta": Rune(0x003B8)
+  of "iota": Rune(0x003B9)
+  of "kappa": Rune(0x003BA)
+  of "lambda": Rune(0x003BB)
+  of "mu": Rune(0x003BC)
+  of "nu": Rune(0x003BD)
+  of "xi": Rune(0x003BE)
+  of "omicron": Rune(0x003BF)
+  of "pi": Rune(0x003C0)
+  of "rho": Rune(0x003C1)
+  of "sigmav", "varsigma", "sigmaf": Rune(0x003C2)
+  of "sigma": Rune(0x003C3)
+  of "tau": Rune(0x003C4)
+  of "upsi", "upsilon": Rune(0x003C5)
+  of "phi", "phiv", "varphi": Rune(0x003C6)
+  of "chi": Rune(0x003C7)
+  of "psi": Rune(0x003C8)
+  of "omega": Rune(0x003C9)
+  of "thetav", "vartheta", "thetasym": Rune(0x003D1)
+  of "Upsi", "upsih": Rune(0x003D2)
+  of "straightphi": Rune(0x003D5)
+  of "piv", "varpi": Rune(0x003D6)
+  of "Gammad": Rune(0x003DC)
+  of "gammad", "digamma": Rune(0x003DD)
+  of "kappav", "varkappa": Rune(0x003F0)
+  of "rhov", "varrho": Rune(0x003F1)
+  of "epsi", "straightepsilon": Rune(0x003F5)
+  of "bepsi", "backepsilon": Rune(0x003F6)
+  of "IOcy": Rune(0x00401)
+  of "DJcy": Rune(0x00402)
+  of "GJcy": Rune(0x00403)
+  of "Jukcy": Rune(0x00404)
+  of "DScy": Rune(0x00405)
+  of "Iukcy": Rune(0x00406)
+  of "YIcy": Rune(0x00407)
+  of "Jsercy": Rune(0x00408)
+  of "LJcy": Rune(0x00409)
+  of "NJcy": Rune(0x0040A)
+  of "TSHcy": Rune(0x0040B)
+  of "KJcy": Rune(0x0040C)
+  of "Ubrcy": Rune(0x0040E)
+  of "DZcy": Rune(0x0040F)
+  of "Acy": Rune(0x00410)
+  of "Bcy": Rune(0x00411)
+  of "Vcy": Rune(0x00412)
+  of "Gcy": Rune(0x00413)
+  of "Dcy": Rune(0x00414)
+  of "IEcy": Rune(0x00415)
+  of "ZHcy": Rune(0x00416)
+  of "Zcy": Rune(0x00417)
+  of "Icy": Rune(0x00418)
+  of "Jcy": Rune(0x00419)
+  of "Kcy": Rune(0x0041A)
+  of "Lcy": Rune(0x0041B)
+  of "Mcy": Rune(0x0041C)
+  of "Ncy": Rune(0x0041D)
+  of "Ocy": Rune(0x0041E)
+  of "Pcy": Rune(0x0041F)
+  of "Rcy": Rune(0x00420)
+  of "Scy": Rune(0x00421)
+  of "Tcy": Rune(0x00422)
+  of "Ucy": Rune(0x00423)
+  of "Fcy": Rune(0x00424)
+  of "KHcy": Rune(0x00425)
+  of "TScy": Rune(0x00426)
+  of "CHcy": Rune(0x00427)
+  of "SHcy": Rune(0x00428)
+  of "SHCHcy": Rune(0x00429)
+  of "HARDcy": Rune(0x0042A)
+  of "Ycy": Rune(0x0042B)
+  of "SOFTcy": Rune(0x0042C)
+  of "Ecy": Rune(0x0042D)
+  of "YUcy": Rune(0x0042E)
+  of "YAcy": Rune(0x0042F)
+  of "acy": Rune(0x00430)
+  of "bcy": Rune(0x00431)
+  of "vcy": Rune(0x00432)
+  of "gcy": Rune(0x00433)
+  of "dcy": Rune(0x00434)
+  of "iecy": Rune(0x00435)
+  of "zhcy": Rune(0x00436)
+  of "zcy": Rune(0x00437)
+  of "icy": Rune(0x00438)
+  of "jcy": Rune(0x00439)
+  of "kcy": Rune(0x0043A)
+  of "lcy": Rune(0x0043B)
+  of "mcy": Rune(0x0043C)
+  of "ncy": Rune(0x0043D)
+  of "ocy": Rune(0x0043E)
+  of "pcy": Rune(0x0043F)
+  of "rcy": Rune(0x00440)
+  of "scy": Rune(0x00441)
+  of "tcy": Rune(0x00442)
+  of "ucy": Rune(0x00443)
+  of "fcy": Rune(0x00444)
+  of "khcy": Rune(0x00445)
+  of "tscy": Rune(0x00446)
+  of "chcy": Rune(0x00447)
+  of "shcy": Rune(0x00448)
+  of "shchcy": Rune(0x00449)
+  of "hardcy": Rune(0x0044A)
+  of "ycy": Rune(0x0044B)
+  of "softcy": Rune(0x0044C)
+  of "ecy": Rune(0x0044D)
+  of "yucy": Rune(0x0044E)
+  of "yacy": Rune(0x0044F)
+  of "iocy": Rune(0x00451)
+  of "djcy": Rune(0x00452)
+  of "gjcy": Rune(0x00453)
+  of "jukcy": Rune(0x00454)
+  of "dscy": Rune(0x00455)
+  of "iukcy": Rune(0x00456)
+  of "yicy": Rune(0x00457)
+  of "jsercy": Rune(0x00458)
+  of "ljcy": Rune(0x00459)
+  of "njcy": Rune(0x0045A)
+  of "tshcy": Rune(0x0045B)
+  of "kjcy": Rune(0x0045C)
+  of "ubrcy": Rune(0x0045E)
+  of "dzcy": Rune(0x0045F)
+  of "ensp": Rune(0x02002)
+  of "emsp": Rune(0x02003)
+  of "emsp13": Rune(0x02004)
+  of "emsp14": Rune(0x02005)
+  of "numsp": Rune(0x02007)
+  of "puncsp": Rune(0x02008)
+  of "thinsp", "ThinSpace": Rune(0x02009)
+  of "hairsp", "VeryThinSpace": Rune(0x0200A)
   of "ZeroWidthSpace", "NegativeVeryThinSpace", "NegativeThinSpace",
-    "NegativeMediumSpace", "NegativeThickSpace": result = Rune(0x0200B)
-  of "zwnj": result = Rune(0x0200C)
-  of "zwj": result = Rune(0x0200D)
-  of "lrm": result = Rune(0x0200E)
-  of "rlm": result = Rune(0x0200F)
-  of "hyphen", "dash": result = Rune(0x02010)
-  of "ndash": result = Rune(0x02013)
-  of "mdash": result = Rune(0x02014)
-  of "horbar": result = Rune(0x02015)
-  of "Verbar", "Vert": result = Rune(0x02016)
-  of "lsquo", "OpenCurlyQuote": result = Rune(0x02018)
-  of "rsquo", "rsquor", "CloseCurlyQuote": result = Rune(0x02019)
-  of "lsquor", "sbquo": result = Rune(0x0201A)
-  of "ldquo", "OpenCurlyDoubleQuote": result = Rune(0x0201C)
-  of "rdquo", "rdquor", "CloseCurlyDoubleQuote": result = Rune(0x0201D)
-  of "ldquor", "bdquo": result = Rune(0x0201E)
-  of "dagger": result = Rune(0x02020)
-  of "Dagger", "ddagger": result = Rune(0x02021)
-  of "bull", "bullet": result = Rune(0x02022)
-  of "nldr": result = Rune(0x02025)
-  of "hellip", "mldr": result = Rune(0x02026)
-  of "permil": result = Rune(0x02030)
-  of "pertenk": result = Rune(0x02031)
-  of "prime": result = Rune(0x02032)
-  of "Prime": result = Rune(0x02033)
-  of "tprime": result = Rune(0x02034)
-  of "bprime", "backprime": result = Rune(0x02035)
-  of "lsaquo": result = Rune(0x02039)
-  of "rsaquo": result = Rune(0x0203A)
-  of "oline": result = Rune(0x0203E)
-  of "caret": result = Rune(0x02041)
-  of "hybull": result = Rune(0x02043)
-  of "frasl": result = Rune(0x02044)
-  of "bsemi": result = Rune(0x0204F)
-  of "qprime": result = Rune(0x02057)
-  of "MediumSpace": result = Rune(0x0205F)
-  of "NoBreak": result = Rune(0x02060)
-  of "ApplyFunction", "af": result = Rune(0x02061)
-  of "InvisibleTimes", "it": result = Rune(0x02062)
-  of "InvisibleComma", "ic": result = Rune(0x02063)
-  of "euro": result = Rune(0x020AC)
-  of "tdot", "TripleDot": result = Rune(0x020DB)
-  of "DotDot": result = Rune(0x020DC)
-  of "Copf", "complexes": result = Rune(0x02102)
-  of "incare": result = Rune(0x02105)
-  of "gscr": result = Rune(0x0210A)
-  of "hamilt", "HilbertSpace", "Hscr": result = Rune(0x0210B)
-  of "Hfr", "Poincareplane": result = Rune(0x0210C)
-  of "quaternions", "Hopf": result = Rune(0x0210D)
-  of "planckh": result = Rune(0x0210E)
-  of "planck", "hbar", "plankv", "hslash": result = Rune(0x0210F)
-  of "Iscr", "imagline": result = Rune(0x02110)
-  of "image", "Im", "imagpart", "Ifr": result = Rune(0x02111)
-  of "Lscr", "lagran", "Laplacetrf": result = Rune(0x02112)
-  of "ell": result = Rune(0x02113)
-  of "Nopf", "naturals": result = Rune(0x02115)
-  of "numero": result = Rune(0x02116)
-  of "copysr": result = Rune(0x02117)
-  of "weierp", "wp": result = Rune(0x02118)
-  of "Popf", "primes": result = Rune(0x02119)
-  of "rationals", "Qopf": result = Rune(0x0211A)
-  of "Rscr", "realine": result = Rune(0x0211B)
-  of "real", "Re", "realpart", "Rfr": result = Rune(0x0211C)
-  of "reals", "Ropf": result = Rune(0x0211D)
-  of "rx": result = Rune(0x0211E)
-  of "trade", "TRADE": result = Rune(0x02122)
-  of "integers", "Zopf": result = Rune(0x02124)
-  of "ohm": result = Rune(0x02126)
-  of "mho": result = Rune(0x02127)
-  of "Zfr", "zeetrf": result = Rune(0x02128)
-  of "iiota": result = Rune(0x02129)
-  of "angst": result = Rune(0x0212B)
-  of "bernou", "Bernoullis", "Bscr": result = Rune(0x0212C)
-  of "Cfr", "Cayleys": result = Rune(0x0212D)
-  of "escr": result = Rune(0x0212F)
-  of "Escr", "expectation": result = Rune(0x02130)
-  of "Fscr", "Fouriertrf": result = Rune(0x02131)
-  of "phmmat", "Mellintrf", "Mscr": result = Rune(0x02133)
-  of "order", "orderof", "oscr": result = Rune(0x02134)
-  of "alefsym", "aleph": result = Rune(0x02135)
-  of "beth": result = Rune(0x02136)
-  of "gimel": result = Rune(0x02137)
-  of "daleth": result = Rune(0x02138)
-  of "CapitalDifferentialD", "DD": result = Rune(0x02145)
-  of "DifferentialD", "dd": result = Rune(0x02146)
-  of "ExponentialE", "exponentiale", "ee": result = Rune(0x02147)
-  of "ImaginaryI", "ii": result = Rune(0x02148)
-  of "frac13": result = Rune(0x02153)
-  of "frac23": result = Rune(0x02154)
-  of "frac15": result = Rune(0x02155)
-  of "frac25": result = Rune(0x02156)
-  of "frac35": result = Rune(0x02157)
-  of "frac45": result = Rune(0x02158)
-  of "frac16": result = Rune(0x02159)
-  of "frac56": result = Rune(0x0215A)
-  of "frac18": result = Rune(0x0215B)
-  of "frac38": result = Rune(0x0215C)
-  of "frac58": result = Rune(0x0215D)
-  of "frac78": result = Rune(0x0215E)
+    "NegativeMediumSpace", "NegativeThickSpace": Rune(0x0200B)
+  of "zwnj": Rune(0x0200C)
+  of "zwj": Rune(0x0200D)
+  of "lrm": Rune(0x0200E)
+  of "rlm": Rune(0x0200F)
+  of "hyphen", "dash": Rune(0x02010)
+  of "ndash": Rune(0x02013)
+  of "mdash": Rune(0x02014)
+  of "horbar": Rune(0x02015)
+  of "Verbar", "Vert": Rune(0x02016)
+  of "lsquo", "OpenCurlyQuote": Rune(0x02018)
+  of "rsquo", "rsquor", "CloseCurlyQuote": Rune(0x02019)
+  of "lsquor", "sbquo": Rune(0x0201A)
+  of "ldquo", "OpenCurlyDoubleQuote": Rune(0x0201C)
+  of "rdquo", "rdquor", "CloseCurlyDoubleQuote": Rune(0x0201D)
+  of "ldquor", "bdquo": Rune(0x0201E)
+  of "dagger": Rune(0x02020)
+  of "Dagger", "ddagger": Rune(0x02021)
+  of "bull", "bullet": Rune(0x02022)
+  of "nldr": Rune(0x02025)
+  of "hellip", "mldr": Rune(0x02026)
+  of "permil": Rune(0x02030)
+  of "pertenk": Rune(0x02031)
+  of "prime": Rune(0x02032)
+  of "Prime": Rune(0x02033)
+  of "tprime": Rune(0x02034)
+  of "bprime", "backprime": Rune(0x02035)
+  of "lsaquo": Rune(0x02039)
+  of "rsaquo": Rune(0x0203A)
+  of "oline": Rune(0x0203E)
+  of "caret": Rune(0x02041)
+  of "hybull": Rune(0x02043)
+  of "frasl": Rune(0x02044)
+  of "bsemi": Rune(0x0204F)
+  of "qprime": Rune(0x02057)
+  of "MediumSpace": Rune(0x0205F)
+  of "NoBreak": Rune(0x02060)
+  of "ApplyFunction", "af": Rune(0x02061)
+  of "InvisibleTimes", "it": Rune(0x02062)
+  of "InvisibleComma", "ic": Rune(0x02063)
+  of "euro": Rune(0x020AC)
+  of "tdot", "TripleDot": Rune(0x020DB)
+  of "DotDot": Rune(0x020DC)
+  of "Copf", "complexes": Rune(0x02102)
+  of "incare": Rune(0x02105)
+  of "gscr": Rune(0x0210A)
+  of "hamilt", "HilbertSpace", "Hscr": Rune(0x0210B)
+  of "Hfr", "Poincareplane": Rune(0x0210C)
+  of "quaternions", "Hopf": Rune(0x0210D)
+  of "planckh": Rune(0x0210E)
+  of "planck", "hbar", "plankv", "hslash": Rune(0x0210F)
+  of "Iscr", "imagline": Rune(0x02110)
+  of "image", "Im", "imagpart", "Ifr": Rune(0x02111)
+  of "Lscr", "lagran", "Laplacetrf": Rune(0x02112)
+  of "ell": Rune(0x02113)
+  of "Nopf", "naturals": Rune(0x02115)
+  of "numero": Rune(0x02116)
+  of "copysr": Rune(0x02117)
+  of "weierp", "wp": Rune(0x02118)
+  of "Popf", "primes": Rune(0x02119)
+  of "rationals", "Qopf": Rune(0x0211A)
+  of "Rscr", "realine": Rune(0x0211B)
+  of "real", "Re", "realpart", "Rfr": Rune(0x0211C)
+  of "reals", "Ropf": Rune(0x0211D)
+  of "rx": Rune(0x0211E)
+  of "trade", "TRADE": Rune(0x02122)
+  of "integers", "Zopf": Rune(0x02124)
+  of "ohm": Rune(0x02126)
+  of "mho": Rune(0x02127)
+  of "Zfr", "zeetrf": Rune(0x02128)
+  of "iiota": Rune(0x02129)
+  of "angst": Rune(0x0212B)
+  of "bernou", "Bernoullis", "Bscr": Rune(0x0212C)
+  of "Cfr", "Cayleys": Rune(0x0212D)
+  of "escr": Rune(0x0212F)
+  of "Escr", "expectation": Rune(0x02130)
+  of "Fscr", "Fouriertrf": Rune(0x02131)
+  of "phmmat", "Mellintrf", "Mscr": Rune(0x02133)
+  of "order", "orderof", "oscr": Rune(0x02134)
+  of "alefsym", "aleph": Rune(0x02135)
+  of "beth": Rune(0x02136)
+  of "gimel": Rune(0x02137)
+  of "daleth": Rune(0x02138)
+  of "CapitalDifferentialD", "DD": Rune(0x02145)
+  of "DifferentialD", "dd": Rune(0x02146)
+  of "ExponentialE", "exponentiale", "ee": Rune(0x02147)
+  of "ImaginaryI", "ii": Rune(0x02148)
+  of "frac13": Rune(0x02153)
+  of "frac23": Rune(0x02154)
+  of "frac15": Rune(0x02155)
+  of "frac25": Rune(0x02156)
+  of "frac35": Rune(0x02157)
+  of "frac45": Rune(0x02158)
+  of "frac16": Rune(0x02159)
+  of "frac56": Rune(0x0215A)
+  of "frac18": Rune(0x0215B)
+  of "frac38": Rune(0x0215C)
+  of "frac58": Rune(0x0215D)
+  of "frac78": Rune(0x0215E)
   of "larr", "leftarrow", "LeftArrow", "slarr",
-    "ShortLeftArrow": result = Rune(0x02190)
-  of "uarr", "uparrow", "UpArrow", "ShortUpArrow": result = Rune(0x02191)
+    "ShortLeftArrow": Rune(0x02190)
+  of "uarr", "uparrow", "UpArrow", "ShortUpArrow": Rune(0x02191)
   of "rarr", "rightarrow", "RightArrow", "srarr",
-    "ShortRightArrow": result = Rune(0x02192)
+    "ShortRightArrow": Rune(0x02192)
   of "darr", "downarrow", "DownArrow",
-    "ShortDownArrow": result = Rune(0x02193)
-  of "harr", "leftrightarrow", "LeftRightArrow": result = Rune(0x02194)
-  of "varr", "updownarrow", "UpDownArrow": result = Rune(0x02195)
-  of "nwarr", "UpperLeftArrow", "nwarrow": result = Rune(0x02196)
-  of "nearr", "UpperRightArrow", "nearrow": result = Rune(0x02197)
-  of "searr", "searrow", "LowerRightArrow": result = Rune(0x02198)
-  of "swarr", "swarrow", "LowerLeftArrow": result = Rune(0x02199)
-  of "nlarr", "nleftarrow": result = Rune(0x0219A)
-  of "nrarr", "nrightarrow": result = Rune(0x0219B)
-  of "rarrw", "rightsquigarrow": result = Rune(0x0219D)
-  of "Larr", "twoheadleftarrow": result = Rune(0x0219E)
-  of "Uarr": result = Rune(0x0219F)
-  of "Rarr", "twoheadrightarrow": result = Rune(0x021A0)
-  of "Darr": result = Rune(0x021A1)
-  of "larrtl", "leftarrowtail": result = Rune(0x021A2)
-  of "rarrtl", "rightarrowtail": result = Rune(0x021A3)
-  of "LeftTeeArrow", "mapstoleft": result = Rune(0x021A4)
-  of "UpTeeArrow", "mapstoup": result = Rune(0x021A5)
-  of "map", "RightTeeArrow", "mapsto": result = Rune(0x021A6)
-  of "DownTeeArrow", "mapstodown": result = Rune(0x021A7)
-  of "larrhk", "hookleftarrow": result = Rune(0x021A9)
-  of "rarrhk", "hookrightarrow": result = Rune(0x021AA)
-  of "larrlp", "looparrowleft": result = Rune(0x021AB)
-  of "rarrlp", "looparrowright": result = Rune(0x021AC)
-  of "harrw", "leftrightsquigarrow": result = Rune(0x021AD)
-  of "nharr", "nleftrightarrow": result = Rune(0x021AE)
-  of "lsh", "Lsh": result = Rune(0x021B0)
-  of "rsh", "Rsh": result = Rune(0x021B1)
-  of "ldsh": result = Rune(0x021B2)
-  of "rdsh": result = Rune(0x021B3)
-  of "crarr": result = Rune(0x021B5)
-  of "cularr", "curvearrowleft": result = Rune(0x021B6)
-  of "curarr", "curvearrowright": result = Rune(0x021B7)
-  of "olarr", "circlearrowleft": result = Rune(0x021BA)
-  of "orarr", "circlearrowright": result = Rune(0x021BB)
-  of "lharu", "LeftVector", "leftharpoonup": result = Rune(0x021BC)
-  of "lhard", "leftharpoondown", "DownLeftVector": result = Rune(0x021BD)
-  of "uharr", "upharpoonright", "RightUpVector": result = Rune(0x021BE)
-  of "uharl", "upharpoonleft", "LeftUpVector": result = Rune(0x021BF)
-  of "rharu", "RightVector", "rightharpoonup": result = Rune(0x021C0)
-  of "rhard", "rightharpoondown", "DownRightVector": result = Rune(0x021C1)
-  of "dharr", "RightDownVector", "downharpoonright": result = Rune(0x021C2)
-  of "dharl", "LeftDownVector", "downharpoonleft": result = Rune(0x021C3)
-  of "rlarr", "rightleftarrows", "RightArrowLeftArrow": result = Rune(0x021C4)
-  of "udarr", "UpArrowDownArrow": result = Rune(0x021C5)
-  of "lrarr", "leftrightarrows", "LeftArrowRightArrow": result = Rune(0x021C6)
-  of "llarr", "leftleftarrows": result = Rune(0x021C7)
-  of "uuarr", "upuparrows": result = Rune(0x021C8)
-  of "rrarr", "rightrightarrows": result = Rune(0x021C9)
-  of "ddarr", "downdownarrows": result = Rune(0x021CA)
+    "ShortDownArrow": Rune(0x02193)
+  of "harr", "leftrightarrow", "LeftRightArrow": Rune(0x02194)
+  of "varr", "updownarrow", "UpDownArrow": Rune(0x02195)
+  of "nwarr", "UpperLeftArrow", "nwarrow": Rune(0x02196)
+  of "nearr", "UpperRightArrow", "nearrow": Rune(0x02197)
+  of "searr", "searrow", "LowerRightArrow": Rune(0x02198)
+  of "swarr", "swarrow", "LowerLeftArrow": Rune(0x02199)
+  of "nlarr", "nleftarrow": Rune(0x0219A)
+  of "nrarr", "nrightarrow": Rune(0x0219B)
+  of "rarrw", "rightsquigarrow": Rune(0x0219D)
+  of "Larr", "twoheadleftarrow": Rune(0x0219E)
+  of "Uarr": Rune(0x0219F)
+  of "Rarr", "twoheadrightarrow": Rune(0x021A0)
+  of "Darr": Rune(0x021A1)
+  of "larrtl", "leftarrowtail": Rune(0x021A2)
+  of "rarrtl", "rightarrowtail": Rune(0x021A3)
+  of "LeftTeeArrow", "mapstoleft": Rune(0x021A4)
+  of "UpTeeArrow", "mapstoup": Rune(0x021A5)
+  of "map", "RightTeeArrow", "mapsto": Rune(0x021A6)
+  of "DownTeeArrow", "mapstodown": Rune(0x021A7)
+  of "larrhk", "hookleftarrow": Rune(0x021A9)
+  of "rarrhk", "hookrightarrow": Rune(0x021AA)
+  of "larrlp", "looparrowleft": Rune(0x021AB)
+  of "rarrlp", "looparrowright": Rune(0x021AC)
+  of "harrw", "leftrightsquigarrow": Rune(0x021AD)
+  of "nharr", "nleftrightarrow": Rune(0x021AE)
+  of "lsh", "Lsh": Rune(0x021B0)
+  of "rsh", "Rsh": Rune(0x021B1)
+  of "ldsh": Rune(0x021B2)
+  of "rdsh": Rune(0x021B3)
+  of "crarr": Rune(0x021B5)
+  of "cularr", "curvearrowleft": Rune(0x021B6)
+  of "curarr", "curvearrowright": Rune(0x021B7)
+  of "olarr", "circlearrowleft": Rune(0x021BA)
+  of "orarr", "circlearrowright": Rune(0x021BB)
+  of "lharu", "LeftVector", "leftharpoonup": Rune(0x021BC)
+  of "lhard", "leftharpoondown", "DownLeftVector": Rune(0x021BD)
+  of "uharr", "upharpoonright", "RightUpVector": Rune(0x021BE)
+  of "uharl", "upharpoonleft", "LeftUpVector": Rune(0x021BF)
+  of "rharu", "RightVector", "rightharpoonup": Rune(0x021C0)
+  of "rhard", "rightharpoondown", "DownRightVector": Rune(0x021C1)
+  of "dharr", "RightDownVector", "downharpoonright": Rune(0x021C2)
+  of "dharl", "LeftDownVector", "downharpoonleft": Rune(0x021C3)
+  of "rlarr", "rightleftarrows", "RightArrowLeftArrow": Rune(0x021C4)
+  of "udarr", "UpArrowDownArrow": Rune(0x021C5)
+  of "lrarr", "leftrightarrows", "LeftArrowRightArrow": Rune(0x021C6)
+  of "llarr", "leftleftarrows": Rune(0x021C7)
+  of "uuarr", "upuparrows": Rune(0x021C8)
+  of "rrarr", "rightrightarrows": Rune(0x021C9)
+  of "ddarr", "downdownarrows": Rune(0x021CA)
   of "lrhar", "ReverseEquilibrium",
-    "leftrightharpoons": result = Rune(0x021CB)
-  of "rlhar", "rightleftharpoons", "Equilibrium": result = Rune(0x021CC)
-  of "nlArr", "nLeftarrow": result = Rune(0x021CD)
-  of "nhArr", "nLeftrightarrow": result = Rune(0x021CE)
-  of "nrArr", "nRightarrow": result = Rune(0x021CF)
-  of "lArr", "Leftarrow", "DoubleLeftArrow": result = Rune(0x021D0)
-  of "uArr", "Uparrow", "DoubleUpArrow": result = Rune(0x021D1)
+    "leftrightharpoons": Rune(0x021CB)
+  of "rlhar", "rightleftharpoons", "Equilibrium": Rune(0x021CC)
+  of "nlArr", "nLeftarrow": Rune(0x021CD)
+  of "nhArr", "nLeftrightarrow": Rune(0x021CE)
+  of "nrArr", "nRightarrow": Rune(0x021CF)
+  of "lArr", "Leftarrow", "DoubleLeftArrow": Rune(0x021D0)
+  of "uArr", "Uparrow", "DoubleUpArrow": Rune(0x021D1)
   of "rArr", "Rightarrow", "Implies",
-    "DoubleRightArrow": result = Rune(0x021D2)
-  of "dArr", "Downarrow", "DoubleDownArrow": result = Rune(0x021D3)
+    "DoubleRightArrow": Rune(0x021D2)
+  of "dArr", "Downarrow", "DoubleDownArrow": Rune(0x021D3)
   of "hArr", "Leftrightarrow", "DoubleLeftRightArrow",
-    "iff": result = Rune(0x021D4)
-  of "vArr", "Updownarrow", "DoubleUpDownArrow": result = Rune(0x021D5)
-  of "nwArr": result = Rune(0x021D6)
-  of "neArr": result = Rune(0x021D7)
-  of "seArr": result = Rune(0x021D8)
-  of "swArr": result = Rune(0x021D9)
-  of "lAarr", "Lleftarrow": result = Rune(0x021DA)
-  of "rAarr", "Rrightarrow": result = Rune(0x021DB)
-  of "zigrarr": result = Rune(0x021DD)
-  of "larrb", "LeftArrowBar": result = Rune(0x021E4)
-  of "rarrb", "RightArrowBar": result = Rune(0x021E5)
-  of "duarr", "DownArrowUpArrow": result = Rune(0x021F5)
-  of "loarr": result = Rune(0x021FD)
-  of "roarr": result = Rune(0x021FE)
-  of "hoarr": result = Rune(0x021FF)
-  of "forall", "ForAll": result = Rune(0x02200)
-  of "comp", "complement": result = Rune(0x02201)
-  of "part", "PartialD": result = Rune(0x02202)
-  of "exist", "Exists": result = Rune(0x02203)
-  of "nexist", "NotExists", "nexists": result = Rune(0x02204)
-  of "empty", "emptyset", "emptyv", "varnothing": result = Rune(0x02205)
-  of "nabla", "Del": result = Rune(0x02207)
-  of "isin", "isinv", "Element", "in": result = Rune(0x02208)
-  of "notin", "NotElement", "notinva": result = Rune(0x02209)
-  of "niv", "ReverseElement", "ni", "SuchThat": result = Rune(0x0220B)
-  of "notni", "notniva", "NotReverseElement": result = Rune(0x0220C)
-  of "prod", "Product": result = Rune(0x0220F)
-  of "coprod", "Coproduct": result = Rune(0x02210)
-  of "sum", "Sum": result = Rune(0x02211)
-  of "minus": result = Rune(0x02212)
-  of "mnplus", "mp", "MinusPlus": result = Rune(0x02213)
-  of "plusdo", "dotplus": result = Rune(0x02214)
+    "iff": Rune(0x021D4)
+  of "vArr", "Updownarrow", "DoubleUpDownArrow": Rune(0x021D5)
+  of "nwArr": Rune(0x021D6)
+  of "neArr": Rune(0x021D7)
+  of "seArr": Rune(0x021D8)
+  of "swArr": Rune(0x021D9)
+  of "lAarr", "Lleftarrow": Rune(0x021DA)
+  of "rAarr", "Rrightarrow": Rune(0x021DB)
+  of "zigrarr": Rune(0x021DD)
+  of "larrb", "LeftArrowBar": Rune(0x021E4)
+  of "rarrb", "RightArrowBar": Rune(0x021E5)
+  of "duarr", "DownArrowUpArrow": Rune(0x021F5)
+  of "loarr": Rune(0x021FD)
+  of "roarr": Rune(0x021FE)
+  of "hoarr": Rune(0x021FF)
+  of "forall", "ForAll": Rune(0x02200)
+  of "comp", "complement": Rune(0x02201)
+  of "part", "PartialD": Rune(0x02202)
+  of "exist", "Exists": Rune(0x02203)
+  of "nexist", "NotExists", "nexists": Rune(0x02204)
+  of "empty", "emptyset", "emptyv", "varnothing": Rune(0x02205)
+  of "nabla", "Del": Rune(0x02207)
+  of "isin", "isinv", "Element", "in": Rune(0x02208)
+  of "notin", "NotElement", "notinva": Rune(0x02209)
+  of "niv", "ReverseElement", "ni", "SuchThat": Rune(0x0220B)
+  of "notni", "notniva", "NotReverseElement": Rune(0x0220C)
+  of "prod", "Product": Rune(0x0220F)
+  of "coprod", "Coproduct": Rune(0x02210)
+  of "sum", "Sum": Rune(0x02211)
+  of "minus": Rune(0x02212)
+  of "mnplus", "mp", "MinusPlus": Rune(0x02213)
+  of "plusdo", "dotplus": Rune(0x02214)
   of "setmn", "setminus", "Backslash", "ssetmn",
-    "smallsetminus": result = Rune(0x02216)
-  of "lowast": result = Rune(0x02217)
-  of "compfn", "SmallCircle": result = Rune(0x02218)
-  of "radic", "Sqrt": result = Rune(0x0221A)
+    "smallsetminus": Rune(0x02216)
+  of "lowast": Rune(0x02217)
+  of "compfn", "SmallCircle": Rune(0x02218)
+  of "radic", "Sqrt": Rune(0x0221A)
   of "prop", "propto", "Proportional", "vprop",
-    "varpropto": result = Rune(0x0221D)
-  of "infin": result = Rune(0x0221E)
-  of "angrt": result = Rune(0x0221F)
-  of "ang", "angle": result = Rune(0x02220)
-  of "angmsd", "measuredangle": result = Rune(0x02221)
-  of "angsph": result = Rune(0x02222)
-  of "mid", "VerticalBar", "smid", "shortmid": result = Rune(0x02223)
-  of "nmid", "NotVerticalBar", "nsmid", "nshortmid": result = Rune(0x02224)
+    "varpropto": Rune(0x0221D)
+  of "infin": Rune(0x0221E)
+  of "angrt": Rune(0x0221F)
+  of "ang", "angle": Rune(0x02220)
+  of "angmsd", "measuredangle": Rune(0x02221)
+  of "angsph": Rune(0x02222)
+  of "mid", "VerticalBar", "smid", "shortmid": Rune(0x02223)
+  of "nmid", "NotVerticalBar", "nsmid", "nshortmid": Rune(0x02224)
   of "par", "parallel", "DoubleVerticalBar", "spar",
-    "shortparallel": result = Rune(0x02225)
+    "shortparallel": Rune(0x02225)
   of "npar", "nparallel", "NotDoubleVerticalBar", "nspar",
-    "nshortparallel": result = Rune(0x02226)
-  of "and", "wedge": result = Rune(0x02227)
-  of "or", "vee": result = Rune(0x02228)
-  of "cap": result = Rune(0x02229)
-  of "cup": result = Rune(0x0222A)
-  of "int", "Integral": result = Rune(0x0222B)
-  of "Int": result = Rune(0x0222C)
-  of "tint", "iiint": result = Rune(0x0222D)
-  of "conint", "oint", "ContourIntegral": result = Rune(0x0222E)
-  of "Conint", "DoubleContourIntegral": result = Rune(0x0222F)
-  of "Cconint": result = Rune(0x02230)
-  of "cwint": result = Rune(0x02231)
-  of "cwconint", "ClockwiseContourIntegral": result = Rune(0x02232)
-  of "awconint", "CounterClockwiseContourIntegral": result = Rune(0x02233)
-  of "there4", "therefore", "Therefore": result = Rune(0x02234)
-  of "becaus", "because", "Because": result = Rune(0x02235)
-  of "ratio": result = Rune(0x02236)
-  of "Colon", "Proportion": result = Rune(0x02237)
-  of "minusd", "dotminus": result = Rune(0x02238)
-  of "mDDot": result = Rune(0x0223A)
-  of "homtht": result = Rune(0x0223B)
-  of "sim", "Tilde", "thksim", "thicksim": result = Rune(0x0223C)
-  of "bsim", "backsim": result = Rune(0x0223D)
-  of "ac", "mstpos": result = Rune(0x0223E)
-  of "acd": result = Rune(0x0223F)
-  of "wreath", "VerticalTilde", "wr": result = Rune(0x02240)
-  of "nsim", "NotTilde": result = Rune(0x02241)
-  of "esim", "EqualTilde", "eqsim": result = Rune(0x02242)
-  of "sime", "TildeEqual", "simeq": result = Rune(0x02243)
-  of "nsime", "nsimeq", "NotTildeEqual": result = Rune(0x02244)
-  of "cong", "TildeFullEqual": result = Rune(0x02245)
-  of "simne": result = Rune(0x02246)
-  of "ncong", "NotTildeFullEqual": result = Rune(0x02247)
+    "nshortparallel": Rune(0x02226)
+  of "and", "wedge": Rune(0x02227)
+  of "or", "vee": Rune(0x02228)
+  of "cap": Rune(0x02229)
+  of "cup": Rune(0x0222A)
+  of "int", "Integral": Rune(0x0222B)
+  of "Int": Rune(0x0222C)
+  of "tint", "iiint": Rune(0x0222D)
+  of "conint", "oint", "ContourIntegral": Rune(0x0222E)
+  of "Conint", "DoubleContourIntegral": Rune(0x0222F)
+  of "Cconint": Rune(0x02230)
+  of "cwint": Rune(0x02231)
+  of "cwconint", "ClockwiseContourIntegral": Rune(0x02232)
+  of "awconint", "CounterClockwiseContourIntegral": Rune(0x02233)
+  of "there4", "therefore", "Therefore": Rune(0x02234)
+  of "becaus", "because", "Because": Rune(0x02235)
+  of "ratio": Rune(0x02236)
+  of "Colon", "Proportion": Rune(0x02237)
+  of "minusd", "dotminus": Rune(0x02238)
+  of "mDDot": Rune(0x0223A)
+  of "homtht": Rune(0x0223B)
+  of "sim", "Tilde", "thksim", "thicksim": Rune(0x0223C)
+  of "bsim", "backsim": Rune(0x0223D)
+  of "ac", "mstpos": Rune(0x0223E)
+  of "acd": Rune(0x0223F)
+  of "wreath", "VerticalTilde", "wr": Rune(0x02240)
+  of "nsim", "NotTilde": Rune(0x02241)
+  of "esim", "EqualTilde", "eqsim": Rune(0x02242)
+  of "sime", "TildeEqual", "simeq": Rune(0x02243)
+  of "nsime", "nsimeq", "NotTildeEqual": Rune(0x02244)
+  of "cong", "TildeFullEqual": Rune(0x02245)
+  of "simne": Rune(0x02246)
+  of "ncong", "NotTildeFullEqual": Rune(0x02247)
   of "asymp", "ap", "TildeTilde", "approx", "thkap",
-    "thickapprox": result = Rune(0x02248)
-  of "nap", "NotTildeTilde", "napprox": result = Rune(0x02249)
-  of "ape", "approxeq": result = Rune(0x0224A)
-  of "apid": result = Rune(0x0224B)
-  of "bcong", "backcong": result = Rune(0x0224C)
-  of "asympeq", "CupCap": result = Rune(0x0224D)
-  of "bump", "HumpDownHump", "Bumpeq": result = Rune(0x0224E)
-  of "bumpe", "HumpEqual", "bumpeq": result = Rune(0x0224F)
-  of "esdot", "DotEqual", "doteq": result = Rune(0x02250)
-  of "eDot", "doteqdot": result = Rune(0x02251)
-  of "efDot", "fallingdotseq": result = Rune(0x02252)
-  of "erDot", "risingdotseq": result = Rune(0x02253)
-  of "colone", "coloneq", "Assign": result = Rune(0x02254)
-  of "ecolon", "eqcolon": result = Rune(0x02255)
-  of "ecir", "eqcirc": result = Rune(0x02256)
-  of "cire", "circeq": result = Rune(0x02257)
-  of "wedgeq": result = Rune(0x02259)
-  of "veeeq": result = Rune(0x0225A)
-  of "trie", "triangleq": result = Rune(0x0225C)
-  of "equest", "questeq": result = Rune(0x0225F)
-  of "ne", "NotEqual": result = Rune(0x02260)
-  of "equiv", "Congruent": result = Rune(0x02261)
-  of "nequiv", "NotCongruent": result = Rune(0x02262)
-  of "le", "leq": result = Rune(0x02264)
-  of "ge", "GreaterEqual", "geq": result = Rune(0x02265)
-  of "lE", "LessFullEqual", "leqq": result = Rune(0x02266)
-  of "gE", "GreaterFullEqual", "geqq": result = Rune(0x02267)
-  of "lnE", "lneqq": result = Rune(0x02268)
-  of "gnE", "gneqq": result = Rune(0x02269)
-  of "Lt", "NestedLessLess", "ll": result = Rune(0x0226A)
-  of "Gt", "NestedGreaterGreater", "gg": result = Rune(0x0226B)
-  of "twixt", "between": result = Rune(0x0226C)
-  of "NotCupCap": result = Rune(0x0226D)
-  of "nlt", "NotLess", "nless": result = Rune(0x0226E)
-  of "ngt", "NotGreater", "ngtr": result = Rune(0x0226F)
-  of "nle", "NotLessEqual", "nleq": result = Rune(0x02270)
-  of "nge", "NotGreaterEqual", "ngeq": result = Rune(0x02271)
-  of "lsim", "LessTilde", "lesssim": result = Rune(0x02272)
-  of "gsim", "gtrsim", "GreaterTilde": result = Rune(0x02273)
-  of "nlsim", "NotLessTilde": result = Rune(0x02274)
-  of "ngsim", "NotGreaterTilde": result = Rune(0x02275)
-  of "lg", "lessgtr", "LessGreater": result = Rune(0x02276)
-  of "gl", "gtrless", "GreaterLess": result = Rune(0x02277)
-  of "ntlg", "NotLessGreater": result = Rune(0x02278)
-  of "ntgl", "NotGreaterLess": result = Rune(0x02279)
-  of "pr", "Precedes", "prec": result = Rune(0x0227A)
-  of "sc", "Succeeds", "succ": result = Rune(0x0227B)
-  of "prcue", "PrecedesSlantEqual", "preccurlyeq": result = Rune(0x0227C)
-  of "sccue", "SucceedsSlantEqual", "succcurlyeq": result = Rune(0x0227D)
-  of "prsim", "precsim", "PrecedesTilde": result = Rune(0x0227E)
-  of "scsim", "succsim", "SucceedsTilde": result = Rune(0x0227F)
-  of "npr", "nprec", "NotPrecedes": result = Rune(0x02280)
-  of "nsc", "nsucc", "NotSucceeds": result = Rune(0x02281)
-  of "sub", "subset": result = Rune(0x02282)
-  of "sup", "supset", "Superset": result = Rune(0x02283)
-  of "nsub": result = Rune(0x02284)
-  of "nsup": result = Rune(0x02285)
-  of "sube", "SubsetEqual", "subseteq": result = Rune(0x02286)
-  of "supe", "supseteq", "SupersetEqual": result = Rune(0x02287)
-  of "nsube", "nsubseteq", "NotSubsetEqual": result = Rune(0x02288)
-  of "nsupe", "nsupseteq", "NotSupersetEqual": result = Rune(0x02289)
-  of "subne", "subsetneq": result = Rune(0x0228A)
-  of "supne", "supsetneq": result = Rune(0x0228B)
-  of "cupdot": result = Rune(0x0228D)
-  of "uplus", "UnionPlus": result = Rune(0x0228E)
-  of "sqsub", "SquareSubset", "sqsubset": result = Rune(0x0228F)
-  of "sqsup", "SquareSuperset", "sqsupset": result = Rune(0x02290)
-  of "sqsube", "SquareSubsetEqual", "sqsubseteq": result = Rune(0x02291)
-  of "sqsupe", "SquareSupersetEqual", "sqsupseteq": result = Rune(0x02292)
-  of "sqcap", "SquareIntersection": result = Rune(0x02293)
-  of "sqcup", "SquareUnion": result = Rune(0x02294)
-  of "oplus", "CirclePlus": result = Rune(0x02295)
-  of "ominus", "CircleMinus": result = Rune(0x02296)
-  of "otimes", "CircleTimes": result = Rune(0x02297)
-  of "osol": result = Rune(0x02298)
-  of "odot", "CircleDot": result = Rune(0x02299)
-  of "ocir", "circledcirc": result = Rune(0x0229A)
-  of "oast", "circledast": result = Rune(0x0229B)
-  of "odash", "circleddash": result = Rune(0x0229D)
-  of "plusb", "boxplus": result = Rune(0x0229E)
-  of "minusb", "boxminus": result = Rune(0x0229F)
-  of "timesb", "boxtimes": result = Rune(0x022A0)
-  of "sdotb", "dotsquare": result = Rune(0x022A1)
-  of "vdash", "RightTee": result = Rune(0x022A2)
-  of "dashv", "LeftTee": result = Rune(0x022A3)
-  of "top", "DownTee": result = Rune(0x022A4)
-  of "bottom", "bot", "perp", "UpTee": result = Rune(0x022A5)
-  of "models": result = Rune(0x022A7)
-  of "vDash", "DoubleRightTee": result = Rune(0x022A8)
-  of "Vdash": result = Rune(0x022A9)
-  of "Vvdash": result = Rune(0x022AA)
-  of "VDash": result = Rune(0x022AB)
-  of "nvdash": result = Rune(0x022AC)
-  of "nvDash": result = Rune(0x022AD)
-  of "nVdash": result = Rune(0x022AE)
-  of "nVDash": result = Rune(0x022AF)
-  of "prurel": result = Rune(0x022B0)
-  of "vltri", "vartriangleleft", "LeftTriangle": result = Rune(0x022B2)
-  of "vrtri", "vartriangleright", "RightTriangle": result = Rune(0x022B3)
-  of "ltrie", "trianglelefteq", "LeftTriangleEqual": result = Rune(0x022B4)
-  of "rtrie", "trianglerighteq", "RightTriangleEqual": result = Rune(0x022B5)
-  of "origof": result = Rune(0x022B6)
-  of "imof": result = Rune(0x022B7)
-  of "mumap", "multimap": result = Rune(0x022B8)
-  of "hercon": result = Rune(0x022B9)
-  of "intcal", "intercal": result = Rune(0x022BA)
-  of "veebar": result = Rune(0x022BB)
-  of "barvee": result = Rune(0x022BD)
-  of "angrtvb": result = Rune(0x022BE)
-  of "lrtri": result = Rune(0x022BF)
-  of "xwedge", "Wedge", "bigwedge": result = Rune(0x022C0)
-  of "xvee", "Vee", "bigvee": result = Rune(0x022C1)
-  of "xcap", "Intersection", "bigcap": result = Rune(0x022C2)
-  of "xcup", "Union", "bigcup": result = Rune(0x022C3)
-  of "diam", "diamond", "Diamond": result = Rune(0x022C4)
-  of "sdot": result = Rune(0x022C5)
-  of "sstarf", "Star": result = Rune(0x022C6)
-  of "divonx", "divideontimes": result = Rune(0x022C7)
-  of "bowtie": result = Rune(0x022C8)
-  of "ltimes": result = Rune(0x022C9)
-  of "rtimes": result = Rune(0x022CA)
-  of "lthree", "leftthreetimes": result = Rune(0x022CB)
-  of "rthree", "rightthreetimes": result = Rune(0x022CC)
-  of "bsime", "backsimeq": result = Rune(0x022CD)
-  of "cuvee", "curlyvee": result = Rune(0x022CE)
-  of "cuwed", "curlywedge": result = Rune(0x022CF)
-  of "Sub", "Subset": result = Rune(0x022D0)
-  of "Sup", "Supset": result = Rune(0x022D1)
-  of "Cap": result = Rune(0x022D2)
-  of "Cup": result = Rune(0x022D3)
-  of "fork", "pitchfork": result = Rune(0x022D4)
-  of "epar": result = Rune(0x022D5)
-  of "ltdot", "lessdot": result = Rune(0x022D6)
-  of "gtdot", "gtrdot": result = Rune(0x022D7)
-  of "Ll": result = Rune(0x022D8)
-  of "Gg", "ggg": result = Rune(0x022D9)
-  of "leg", "LessEqualGreater", "lesseqgtr": result = Rune(0x022DA)
-  of "gel", "gtreqless", "GreaterEqualLess": result = Rune(0x022DB)
-  of "cuepr", "curlyeqprec": result = Rune(0x022DE)
-  of "cuesc", "curlyeqsucc": result = Rune(0x022DF)
-  of "nprcue", "NotPrecedesSlantEqual": result = Rune(0x022E0)
-  of "nsccue", "NotSucceedsSlantEqual": result = Rune(0x022E1)
-  of "nsqsube", "NotSquareSubsetEqual": result = Rune(0x022E2)
-  of "nsqsupe", "NotSquareSupersetEqual": result = Rune(0x022E3)
-  of "lnsim": result = Rune(0x022E6)
-  of "gnsim": result = Rune(0x022E7)
-  of "prnsim", "precnsim": result = Rune(0x022E8)
-  of "scnsim", "succnsim": result = Rune(0x022E9)
-  of "nltri", "ntriangleleft", "NotLeftTriangle": result = Rune(0x022EA)
-  of "nrtri", "ntriangleright", "NotRightTriangle": result = Rune(0x022EB)
+    "thickapprox": Rune(0x02248)
+  of "nap", "NotTildeTilde", "napprox": Rune(0x02249)
+  of "ape", "approxeq": Rune(0x0224A)
+  of "apid": Rune(0x0224B)
+  of "bcong", "backcong": Rune(0x0224C)
+  of "asympeq", "CupCap": Rune(0x0224D)
+  of "bump", "HumpDownHump", "Bumpeq": Rune(0x0224E)
+  of "bumpe", "HumpEqual", "bumpeq": Rune(0x0224F)
+  of "esdot", "DotEqual", "doteq": Rune(0x02250)
+  of "eDot", "doteqdot": Rune(0x02251)
+  of "efDot", "fallingdotseq": Rune(0x02252)
+  of "erDot", "risingdotseq": Rune(0x02253)
+  of "colone", "coloneq", "Assign": Rune(0x02254)
+  of "ecolon", "eqcolon": Rune(0x02255)
+  of "ecir", "eqcirc": Rune(0x02256)
+  of "cire", "circeq": Rune(0x02257)
+  of "wedgeq": Rune(0x02259)
+  of "veeeq": Rune(0x0225A)
+  of "trie", "triangleq": Rune(0x0225C)
+  of "equest", "questeq": Rune(0x0225F)
+  of "ne", "NotEqual": Rune(0x02260)
+  of "equiv", "Congruent": Rune(0x02261)
+  of "nequiv", "NotCongruent": Rune(0x02262)
+  of "le", "leq": Rune(0x02264)
+  of "ge", "GreaterEqual", "geq": Rune(0x02265)
+  of "lE", "LessFullEqual", "leqq": Rune(0x02266)
+  of "gE", "GreaterFullEqual", "geqq": Rune(0x02267)
+  of "lnE", "lneqq": Rune(0x02268)
+  of "gnE", "gneqq": Rune(0x02269)
+  of "Lt", "NestedLessLess", "ll": Rune(0x0226A)
+  of "Gt", "NestedGreaterGreater", "gg": Rune(0x0226B)
+  of "twixt", "between": Rune(0x0226C)
+  of "NotCupCap": Rune(0x0226D)
+  of "nlt", "NotLess", "nless": Rune(0x0226E)
+  of "ngt", "NotGreater", "ngtr": Rune(0x0226F)
+  of "nle", "NotLessEqual", "nleq": Rune(0x02270)
+  of "nge", "NotGreaterEqual", "ngeq": Rune(0x02271)
+  of "lsim", "LessTilde", "lesssim": Rune(0x02272)
+  of "gsim", "gtrsim", "GreaterTilde": Rune(0x02273)
+  of "nlsim", "NotLessTilde": Rune(0x02274)
+  of "ngsim", "NotGreaterTilde": Rune(0x02275)
+  of "lg", "lessgtr", "LessGreater": Rune(0x02276)
+  of "gl", "gtrless", "GreaterLess": Rune(0x02277)
+  of "ntlg", "NotLessGreater": Rune(0x02278)
+  of "ntgl", "NotGreaterLess": Rune(0x02279)
+  of "pr", "Precedes", "prec": Rune(0x0227A)
+  of "sc", "Succeeds", "succ": Rune(0x0227B)
+  of "prcue", "PrecedesSlantEqual", "preccurlyeq": Rune(0x0227C)
+  of "sccue", "SucceedsSlantEqual", "succcurlyeq": Rune(0x0227D)
+  of "prsim", "precsim", "PrecedesTilde": Rune(0x0227E)
+  of "scsim", "succsim", "SucceedsTilde": Rune(0x0227F)
+  of "npr", "nprec", "NotPrecedes": Rune(0x02280)
+  of "nsc", "nsucc", "NotSucceeds": Rune(0x02281)
+  of "sub", "subset": Rune(0x02282)
+  of "sup", "supset", "Superset": Rune(0x02283)
+  of "nsub": Rune(0x02284)
+  of "nsup": Rune(0x02285)
+  of "sube", "SubsetEqual", "subseteq": Rune(0x02286)
+  of "supe", "supseteq", "SupersetEqual": Rune(0x02287)
+  of "nsube", "nsubseteq", "NotSubsetEqual": Rune(0x02288)
+  of "nsupe", "nsupseteq", "NotSupersetEqual": Rune(0x02289)
+  of "subne", "subsetneq": Rune(0x0228A)
+  of "supne", "supsetneq": Rune(0x0228B)
+  of "cupdot": Rune(0x0228D)
+  of "uplus", "UnionPlus": Rune(0x0228E)
+  of "sqsub", "SquareSubset", "sqsubset": Rune(0x0228F)
+  of "sqsup", "SquareSuperset", "sqsupset": Rune(0x02290)
+  of "sqsube", "SquareSubsetEqual", "sqsubseteq": Rune(0x02291)
+  of "sqsupe", "SquareSupersetEqual", "sqsupseteq": Rune(0x02292)
+  of "sqcap", "SquareIntersection": Rune(0x02293)
+  of "sqcup", "SquareUnion": Rune(0x02294)
+  of "oplus", "CirclePlus": Rune(0x02295)
+  of "ominus", "CircleMinus": Rune(0x02296)
+  of "otimes", "CircleTimes": Rune(0x02297)
+  of "osol": Rune(0x02298)
+  of "odot", "CircleDot": Rune(0x02299)
+  of "ocir", "circledcirc": Rune(0x0229A)
+  of "oast", "circledast": Rune(0x0229B)
+  of "odash", "circleddash": Rune(0x0229D)
+  of "plusb", "boxplus": Rune(0x0229E)
+  of "minusb", "boxminus": Rune(0x0229F)
+  of "timesb", "boxtimes": Rune(0x022A0)
+  of "sdotb", "dotsquare": Rune(0x022A1)
+  of "vdash", "RightTee": Rune(0x022A2)
+  of "dashv", "LeftTee": Rune(0x022A3)
+  of "top", "DownTee": Rune(0x022A4)
+  of "bottom", "bot", "perp", "UpTee": Rune(0x022A5)
+  of "models": Rune(0x022A7)
+  of "vDash", "DoubleRightTee": Rune(0x022A8)
+  of "Vdash": Rune(0x022A9)
+  of "Vvdash": Rune(0x022AA)
+  of "VDash": Rune(0x022AB)
+  of "nvdash": Rune(0x022AC)
+  of "nvDash": Rune(0x022AD)
+  of "nVdash": Rune(0x022AE)
+  of "nVDash": Rune(0x022AF)
+  of "prurel": Rune(0x022B0)
+  of "vltri", "vartriangleleft", "LeftTriangle": Rune(0x022B2)
+  of "vrtri", "vartriangleright", "RightTriangle": Rune(0x022B3)
+  of "ltrie", "trianglelefteq", "LeftTriangleEqual": Rune(0x022B4)
+  of "rtrie", "trianglerighteq", "RightTriangleEqual": Rune(0x022B5)
+  of "origof": Rune(0x022B6)
+  of "imof": Rune(0x022B7)
+  of "mumap", "multimap": Rune(0x022B8)
+  of "hercon": Rune(0x022B9)
+  of "intcal", "intercal": Rune(0x022BA)
+  of "veebar": Rune(0x022BB)
+  of "barvee": Rune(0x022BD)
+  of "angrtvb": Rune(0x022BE)
+  of "lrtri": Rune(0x022BF)
+  of "xwedge", "Wedge", "bigwedge": Rune(0x022C0)
+  of "xvee", "Vee", "bigvee": Rune(0x022C1)
+  of "xcap", "Intersection", "bigcap": Rune(0x022C2)
+  of "xcup", "Union", "bigcup": Rune(0x022C3)
+  of "diam", "diamond", "Diamond": Rune(0x022C4)
+  of "sdot": Rune(0x022C5)
+  of "sstarf", "Star": Rune(0x022C6)
+  of "divonx", "divideontimes": Rune(0x022C7)
+  of "bowtie": Rune(0x022C8)
+  of "ltimes": Rune(0x022C9)
+  of "rtimes": Rune(0x022CA)
+  of "lthree", "leftthreetimes": Rune(0x022CB)
+  of "rthree", "rightthreetimes": Rune(0x022CC)
+  of "bsime", "backsimeq": Rune(0x022CD)
+  of "cuvee", "curlyvee": Rune(0x022CE)
+  of "cuwed", "curlywedge": Rune(0x022CF)
+  of "Sub", "Subset": Rune(0x022D0)
+  of "Sup", "Supset": Rune(0x022D1)
+  of "Cap": Rune(0x022D2)
+  of "Cup": Rune(0x022D3)
+  of "fork", "pitchfork": Rune(0x022D4)
+  of "epar": Rune(0x022D5)
+  of "ltdot", "lessdot": Rune(0x022D6)
+  of "gtdot", "gtrdot": Rune(0x022D7)
+  of "Ll": Rune(0x022D8)
+  of "Gg", "ggg": Rune(0x022D9)
+  of "leg", "LessEqualGreater", "lesseqgtr": Rune(0x022DA)
+  of "gel", "gtreqless", "GreaterEqualLess": Rune(0x022DB)
+  of "cuepr", "curlyeqprec": Rune(0x022DE)
+  of "cuesc", "curlyeqsucc": Rune(0x022DF)
+  of "nprcue", "NotPrecedesSlantEqual": Rune(0x022E0)
+  of "nsccue", "NotSucceedsSlantEqual": Rune(0x022E1)
+  of "nsqsube", "NotSquareSubsetEqual": Rune(0x022E2)
+  of "nsqsupe", "NotSquareSupersetEqual": Rune(0x022E3)
+  of "lnsim": Rune(0x022E6)
+  of "gnsim": Rune(0x022E7)
+  of "prnsim", "precnsim": Rune(0x022E8)
+  of "scnsim", "succnsim": Rune(0x022E9)
+  of "nltri", "ntriangleleft", "NotLeftTriangle": Rune(0x022EA)
+  of "nrtri", "ntriangleright", "NotRightTriangle": Rune(0x022EB)
   of "nltrie", "ntrianglelefteq",
-    "NotLeftTriangleEqual": result = Rune(0x022EC)
+    "NotLeftTriangleEqual": Rune(0x022EC)
   of "nrtrie", "ntrianglerighteq",
-    "NotRightTriangleEqual": result = Rune(0x022ED)
-  of "vellip": result = Rune(0x022EE)
-  of "ctdot": result = Rune(0x022EF)
-  of "utdot": result = Rune(0x022F0)
-  of "dtdot": result = Rune(0x022F1)
-  of "disin": result = Rune(0x022F2)
-  of "isinsv": result = Rune(0x022F3)
-  of "isins": result = Rune(0x022F4)
-  of "isindot": result = Rune(0x022F5)
-  of "notinvc": result = Rune(0x022F6)
-  of "notinvb": result = Rune(0x022F7)
-  of "isinE": result = Rune(0x022F9)
-  of "nisd": result = Rune(0x022FA)
-  of "xnis": result = Rune(0x022FB)
-  of "nis": result = Rune(0x022FC)
-  of "notnivc": result = Rune(0x022FD)
-  of "notnivb": result = Rune(0x022FE)
-  of "barwed", "barwedge": result = Rune(0x02305)
-  of "Barwed", "doublebarwedge": result = Rune(0x02306)
-  of "lceil", "LeftCeiling": result = Rune(0x02308)
-  of "rceil", "RightCeiling": result = Rune(0x02309)
-  of "lfloor", "LeftFloor": result = Rune(0x0230A)
-  of "rfloor", "RightFloor": result = Rune(0x0230B)
-  of "drcrop": result = Rune(0x0230C)
-  of "dlcrop": result = Rune(0x0230D)
-  of "urcrop": result = Rune(0x0230E)
-  of "ulcrop": result = Rune(0x0230F)
-  of "bnot": result = Rune(0x02310)
-  of "profline": result = Rune(0x02312)
-  of "profsurf": result = Rune(0x02313)
-  of "telrec": result = Rune(0x02315)
-  of "target": result = Rune(0x02316)
-  of "ulcorn", "ulcorner": result = Rune(0x0231C)
-  of "urcorn", "urcorner": result = Rune(0x0231D)
-  of "dlcorn", "llcorner": result = Rune(0x0231E)
-  of "drcorn", "lrcorner": result = Rune(0x0231F)
-  of "frown", "sfrown": result = Rune(0x02322)
-  of "smile", "ssmile": result = Rune(0x02323)
-  of "cylcty": result = Rune(0x0232D)
-  of "profalar": result = Rune(0x0232E)
-  of "topbot": result = Rune(0x02336)
-  of "ovbar": result = Rune(0x0233D)
-  of "solbar": result = Rune(0x0233F)
-  of "angzarr": result = Rune(0x0237C)
-  of "lmoust", "lmoustache": result = Rune(0x023B0)
-  of "rmoust", "rmoustache": result = Rune(0x023B1)
-  of "tbrk", "OverBracket": result = Rune(0x023B4)
-  of "bbrk", "UnderBracket": result = Rune(0x023B5)
-  of "bbrktbrk": result = Rune(0x023B6)
-  of "OverParenthesis": result = Rune(0x023DC)
-  of "UnderParenthesis": result = Rune(0x023DD)
-  of "OverBrace": result = Rune(0x023DE)
-  of "UnderBrace": result = Rune(0x023DF)
-  of "trpezium": result = Rune(0x023E2)
-  of "elinters": result = Rune(0x023E7)
-  of "blank": result = Rune(0x02423)
-  of "oS", "circledS": result = Rune(0x024C8)
-  of "boxh", "HorizontalLine": result = Rune(0x02500)
-  of "boxv": result = Rune(0x02502)
-  of "boxdr": result = Rune(0x0250C)
-  of "boxdl": result = Rune(0x02510)
-  of "boxur": result = Rune(0x02514)
-  of "boxul": result = Rune(0x02518)
-  of "boxvr": result = Rune(0x0251C)
-  of "boxvl": result = Rune(0x02524)
-  of "boxhd": result = Rune(0x0252C)
-  of "boxhu": result = Rune(0x02534)
-  of "boxvh": result = Rune(0x0253C)
-  of "boxH": result = Rune(0x02550)
-  of "boxV": result = Rune(0x02551)
-  of "boxdR": result = Rune(0x02552)
-  of "boxDr": result = Rune(0x02553)
-  of "boxDR": result = Rune(0x02554)
-  of "boxdL": result = Rune(0x02555)
-  of "boxDl": result = Rune(0x02556)
-  of "boxDL": result = Rune(0x02557)
-  of "boxuR": result = Rune(0x02558)
-  of "boxUr": result = Rune(0x02559)
-  of "boxUR": result = Rune(0x0255A)
-  of "boxuL": result = Rune(0x0255B)
-  of "boxUl": result = Rune(0x0255C)
-  of "boxUL": result = Rune(0x0255D)
-  of "boxvR": result = Rune(0x0255E)
-  of "boxVr": result = Rune(0x0255F)
-  of "boxVR": result = Rune(0x02560)
-  of "boxvL": result = Rune(0x02561)
-  of "boxVl": result = Rune(0x02562)
-  of "boxVL": result = Rune(0x02563)
-  of "boxHd": result = Rune(0x02564)
-  of "boxhD": result = Rune(0x02565)
-  of "boxHD": result = Rune(0x02566)
-  of "boxHu": result = Rune(0x02567)
-  of "boxhU": result = Rune(0x02568)
-  of "boxHU": result = Rune(0x02569)
-  of "boxvH": result = Rune(0x0256A)
-  of "boxVh": result = Rune(0x0256B)
-  of "boxVH": result = Rune(0x0256C)
-  of "uhblk": result = Rune(0x02580)
-  of "lhblk": result = Rune(0x02584)
-  of "block": result = Rune(0x02588)
-  of "blk14": result = Rune(0x02591)
-  of "blk12": result = Rune(0x02592)
-  of "blk34": result = Rune(0x02593)
-  of "squ", "square", "Square": result = Rune(0x025A1)
+    "NotRightTriangleEqual": Rune(0x022ED)
+  of "vellip": Rune(0x022EE)
+  of "ctdot": Rune(0x022EF)
+  of "utdot": Rune(0x022F0)
+  of "dtdot": Rune(0x022F1)
+  of "disin": Rune(0x022F2)
+  of "isinsv": Rune(0x022F3)
+  of "isins": Rune(0x022F4)
+  of "isindot": Rune(0x022F5)
+  of "notinvc": Rune(0x022F6)
+  of "notinvb": Rune(0x022F7)
+  of "isinE": Rune(0x022F9)
+  of "nisd": Rune(0x022FA)
+  of "xnis": Rune(0x022FB)
+  of "nis": Rune(0x022FC)
+  of "notnivc": Rune(0x022FD)
+  of "notnivb": Rune(0x022FE)
+  of "barwed", "barwedge": Rune(0x02305)
+  of "Barwed", "doublebarwedge": Rune(0x02306)
+  of "lceil", "LeftCeiling": Rune(0x02308)
+  of "rceil", "RightCeiling": Rune(0x02309)
+  of "lfloor", "LeftFloor": Rune(0x0230A)
+  of "rfloor", "RightFloor": Rune(0x0230B)
+  of "drcrop": Rune(0x0230C)
+  of "dlcrop": Rune(0x0230D)
+  of "urcrop": Rune(0x0230E)
+  of "ulcrop": Rune(0x0230F)
+  of "bnot": Rune(0x02310)
+  of "profline": Rune(0x02312)
+  of "profsurf": Rune(0x02313)
+  of "telrec": Rune(0x02315)
+  of "target": Rune(0x02316)
+  of "ulcorn", "ulcorner": Rune(0x0231C)
+  of "urcorn", "urcorner": Rune(0x0231D)
+  of "dlcorn", "llcorner": Rune(0x0231E)
+  of "drcorn", "lrcorner": Rune(0x0231F)
+  of "frown", "sfrown": Rune(0x02322)
+  of "smile", "ssmile": Rune(0x02323)
+  of "cylcty": Rune(0x0232D)
+  of "profalar": Rune(0x0232E)
+  of "topbot": Rune(0x02336)
+  of "ovbar": Rune(0x0233D)
+  of "solbar": Rune(0x0233F)
+  of "angzarr": Rune(0x0237C)
+  of "lmoust", "lmoustache": Rune(0x023B0)
+  of "rmoust", "rmoustache": Rune(0x023B1)
+  of "tbrk", "OverBracket": Rune(0x023B4)
+  of "bbrk", "UnderBracket": Rune(0x023B5)
+  of "bbrktbrk": Rune(0x023B6)
+  of "OverParenthesis": Rune(0x023DC)
+  of "UnderParenthesis": Rune(0x023DD)
+  of "OverBrace": Rune(0x023DE)
+  of "UnderBrace": Rune(0x023DF)
+  of "trpezium": Rune(0x023E2)
+  of "elinters": Rune(0x023E7)
+  of "blank": Rune(0x02423)
+  of "oS", "circledS": Rune(0x024C8)
+  of "boxh", "HorizontalLine": Rune(0x02500)
+  of "boxv": Rune(0x02502)
+  of "boxdr": Rune(0x0250C)
+  of "boxdl": Rune(0x02510)
+  of "boxur": Rune(0x02514)
+  of "boxul": Rune(0x02518)
+  of "boxvr": Rune(0x0251C)
+  of "boxvl": Rune(0x02524)
+  of "boxhd": Rune(0x0252C)
+  of "boxhu": Rune(0x02534)
+  of "boxvh": Rune(0x0253C)
+  of "boxH": Rune(0x02550)
+  of "boxV": Rune(0x02551)
+  of "boxdR": Rune(0x02552)
+  of "boxDr": Rune(0x02553)
+  of "boxDR": Rune(0x02554)
+  of "boxdL": Rune(0x02555)
+  of "boxDl": Rune(0x02556)
+  of "boxDL": Rune(0x02557)
+  of "boxuR": Rune(0x02558)
+  of "boxUr": Rune(0x02559)
+  of "boxUR": Rune(0x0255A)
+  of "boxuL": Rune(0x0255B)
+  of "boxUl": Rune(0x0255C)
+  of "boxUL": Rune(0x0255D)
+  of "boxvR": Rune(0x0255E)
+  of "boxVr": Rune(0x0255F)
+  of "boxVR": Rune(0x02560)
+  of "boxvL": Rune(0x02561)
+  of "boxVl": Rune(0x02562)
+  of "boxVL": Rune(0x02563)
+  of "boxHd": Rune(0x02564)
+  of "boxhD": Rune(0x02565)
+  of "boxHD": Rune(0x02566)
+  of "boxHu": Rune(0x02567)
+  of "boxhU": Rune(0x02568)
+  of "boxHU": Rune(0x02569)
+  of "boxvH": Rune(0x0256A)
+  of "boxVh": Rune(0x0256B)
+  of "boxVH": Rune(0x0256C)
+  of "uhblk": Rune(0x02580)
+  of "lhblk": Rune(0x02584)
+  of "block": Rune(0x02588)
+  of "blk14": Rune(0x02591)
+  of "blk12": Rune(0x02592)
+  of "blk34": Rune(0x02593)
+  of "squ", "square", "Square": Rune(0x025A1)
   of "squf", "squarf", "blacksquare",
-    "FilledVerySmallSquare": result = Rune(0x025AA)
-  of "EmptyVerySmallSquare": result = Rune(0x025AB)
-  of "rect": result = Rune(0x025AD)
-  of "marker": result = Rune(0x025AE)
-  of "fltns": result = Rune(0x025B1)
-  of "xutri", "bigtriangleup": result = Rune(0x025B3)
-  of "utrif", "blacktriangle": result = Rune(0x025B4)
-  of "utri", "triangle": result = Rune(0x025B5)
-  of "rtrif", "blacktriangleright": result = Rune(0x025B8)
-  of "rtri", "triangleright": result = Rune(0x025B9)
-  of "xdtri", "bigtriangledown": result = Rune(0x025BD)
-  of "dtrif", "blacktriangledown": result = Rune(0x025BE)
-  of "dtri", "triangledown": result = Rune(0x025BF)
-  of "ltrif", "blacktriangleleft": result = Rune(0x025C2)
-  of "ltri", "triangleleft": result = Rune(0x025C3)
-  of "loz", "lozenge": result = Rune(0x025CA)
-  of "cir": result = Rune(0x025CB)
-  of "tridot": result = Rune(0x025EC)
-  of "xcirc", "bigcirc": result = Rune(0x025EF)
-  of "ultri": result = Rune(0x025F8)
-  of "urtri": result = Rune(0x025F9)
-  of "lltri": result = Rune(0x025FA)
-  of "EmptySmallSquare": result = Rune(0x025FB)
-  of "FilledSmallSquare": result = Rune(0x025FC)
-  of "starf", "bigstar": result = Rune(0x02605)
-  of "star": result = Rune(0x02606)
-  of "phone": result = Rune(0x0260E)
-  of "female": result = Rune(0x02640)
-  of "male": result = Rune(0x02642)
-  of "spades", "spadesuit": result = Rune(0x02660)
-  of "clubs", "clubsuit": result = Rune(0x02663)
-  of "hearts", "heartsuit": result = Rune(0x02665)
-  of "diams", "diamondsuit": result = Rune(0x02666)
-  of "sung": result = Rune(0x0266A)
-  of "flat": result = Rune(0x0266D)
-  of "natur", "natural": result = Rune(0x0266E)
-  of "sharp": result = Rune(0x0266F)
-  of "check", "checkmark": result = Rune(0x02713)
-  of "cross": result = Rune(0x02717)
-  of "malt", "maltese": result = Rune(0x02720)
-  of "sext": result = Rune(0x02736)
-  of "VerticalSeparator": result = Rune(0x02758)
-  of "lbbrk": result = Rune(0x02772)
-  of "rbbrk": result = Rune(0x02773)
-  of "lobrk", "LeftDoubleBracket": result = Rune(0x027E6)
-  of "robrk", "RightDoubleBracket": result = Rune(0x027E7)
-  of "lang", "LeftAngleBracket", "langle": result = Rune(0x027E8)
-  of "rang", "RightAngleBracket", "rangle": result = Rune(0x027E9)
-  of "Lang": result = Rune(0x027EA)
-  of "Rang": result = Rune(0x027EB)
-  of "loang": result = Rune(0x027EC)
-  of "roang": result = Rune(0x027ED)
-  of "xlarr", "longleftarrow", "LongLeftArrow": result = Rune(0x027F5)
-  of "xrarr", "longrightarrow", "LongRightArrow": result = Rune(0x027F6)
+    "FilledVerySmallSquare": Rune(0x025AA)
+  of "EmptyVerySmallSquare": Rune(0x025AB)
+  of "rect": Rune(0x025AD)
+  of "marker": Rune(0x025AE)
+  of "fltns": Rune(0x025B1)
+  of "xutri", "bigtriangleup": Rune(0x025B3)
+  of "utrif", "blacktriangle": Rune(0x025B4)
+  of "utri", "triangle": Rune(0x025B5)
+  of "rtrif", "blacktriangleright": Rune(0x025B8)
+  of "rtri", "triangleright": Rune(0x025B9)
+  of "xdtri", "bigtriangledown": Rune(0x025BD)
+  of "dtrif", "blacktriangledown": Rune(0x025BE)
+  of "dtri", "triangledown": Rune(0x025BF)
+  of "ltrif", "blacktriangleleft": Rune(0x025C2)
+  of "ltri", "triangleleft": Rune(0x025C3)
+  of "loz", "lozenge": Rune(0x025CA)
+  of "cir": Rune(0x025CB)
+  of "tridot": Rune(0x025EC)
+  of "xcirc", "bigcirc": Rune(0x025EF)
+  of "ultri": Rune(0x025F8)
+  of "urtri": Rune(0x025F9)
+  of "lltri": Rune(0x025FA)
+  of "EmptySmallSquare": Rune(0x025FB)
+  of "FilledSmallSquare": Rune(0x025FC)
+  of "starf", "bigstar": Rune(0x02605)
+  of "star": Rune(0x02606)
+  of "phone": Rune(0x0260E)
+  of "female": Rune(0x02640)
+  of "male": Rune(0x02642)
+  of "spades", "spadesuit": Rune(0x02660)
+  of "clubs", "clubsuit": Rune(0x02663)
+  of "hearts", "heartsuit": Rune(0x02665)
+  of "diams", "diamondsuit": Rune(0x02666)
+  of "sung": Rune(0x0266A)
+  of "flat": Rune(0x0266D)
+  of "natur", "natural": Rune(0x0266E)
+  of "sharp": Rune(0x0266F)
+  of "check", "checkmark": Rune(0x02713)
+  of "cross": Rune(0x02717)
+  of "malt", "maltese": Rune(0x02720)
+  of "sext": Rune(0x02736)
+  of "VerticalSeparator": Rune(0x02758)
+  of "lbbrk": Rune(0x02772)
+  of "rbbrk": Rune(0x02773)
+  of "lobrk", "LeftDoubleBracket": Rune(0x027E6)
+  of "robrk", "RightDoubleBracket": Rune(0x027E7)
+  of "lang", "LeftAngleBracket", "langle": Rune(0x027E8)
+  of "rang", "RightAngleBracket", "rangle": Rune(0x027E9)
+  of "Lang": Rune(0x027EA)
+  of "Rang": Rune(0x027EB)
+  of "loang": Rune(0x027EC)
+  of "roang": Rune(0x027ED)
+  of "xlarr", "longleftarrow", "LongLeftArrow": Rune(0x027F5)
+  of "xrarr", "longrightarrow", "LongRightArrow": Rune(0x027F6)
   of "xharr", "longleftrightarrow",
-    "LongLeftRightArrow": result = Rune(0x027F7)
-  of "xlArr", "Longleftarrow", "DoubleLongLeftArrow": result = Rune(0x027F8)
-  of "xrArr", "Longrightarrow", "DoubleLongRightArrow": result = Rune(0x027F9)
+    "LongLeftRightArrow": Rune(0x027F7)
+  of "xlArr", "Longleftarrow", "DoubleLongLeftArrow": Rune(0x027F8)
+  of "xrArr", "Longrightarrow", "DoubleLongRightArrow": Rune(0x027F9)
   of "xhArr", "Longleftrightarrow",
-    "DoubleLongLeftRightArrow": result = Rune(0x027FA)
-  of "xmap", "longmapsto": result = Rune(0x027FC)
-  of "dzigrarr": result = Rune(0x027FF)
-  of "nvlArr": result = Rune(0x02902)
-  of "nvrArr": result = Rune(0x02903)
-  of "nvHarr": result = Rune(0x02904)
-  of "Map": result = Rune(0x02905)
-  of "lbarr": result = Rune(0x0290C)
-  of "rbarr", "bkarow": result = Rune(0x0290D)
-  of "lBarr": result = Rune(0x0290E)
-  of "rBarr", "dbkarow": result = Rune(0x0290F)
-  of "RBarr", "drbkarow": result = Rune(0x02910)
-  of "DDotrahd": result = Rune(0x02911)
-  of "UpArrowBar": result = Rune(0x02912)
-  of "DownArrowBar": result = Rune(0x02913)
-  of "Rarrtl": result = Rune(0x02916)
-  of "latail": result = Rune(0x02919)
-  of "ratail": result = Rune(0x0291A)
-  of "lAtail": result = Rune(0x0291B)
-  of "rAtail": result = Rune(0x0291C)
-  of "larrfs": result = Rune(0x0291D)
-  of "rarrfs": result = Rune(0x0291E)
-  of "larrbfs": result = Rune(0x0291F)
-  of "rarrbfs": result = Rune(0x02920)
-  of "nwarhk": result = Rune(0x02923)
-  of "nearhk": result = Rune(0x02924)
-  of "searhk", "hksearow": result = Rune(0x02925)
-  of "swarhk", "hkswarow": result = Rune(0x02926)
-  of "nwnear": result = Rune(0x02927)
-  of "nesear", "toea": result = Rune(0x02928)
-  of "seswar", "tosa": result = Rune(0x02929)
-  of "swnwar": result = Rune(0x0292A)
-  of "rarrc": result = Rune(0x02933)
-  of "cudarrr": result = Rune(0x02935)
-  of "ldca": result = Rune(0x02936)
-  of "rdca": result = Rune(0x02937)
-  of "cudarrl": result = Rune(0x02938)
-  of "larrpl": result = Rune(0x02939)
-  of "curarrm": result = Rune(0x0293C)
-  of "cularrp": result = Rune(0x0293D)
-  of "rarrpl": result = Rune(0x02945)
-  of "harrcir": result = Rune(0x02948)
-  of "Uarrocir": result = Rune(0x02949)
-  of "lurdshar": result = Rune(0x0294A)
-  of "ldrushar": result = Rune(0x0294B)
-  of "LeftRightVector": result = Rune(0x0294E)
-  of "RightUpDownVector": result = Rune(0x0294F)
-  of "DownLeftRightVector": result = Rune(0x02950)
-  of "LeftUpDownVector": result = Rune(0x02951)
-  of "LeftVectorBar": result = Rune(0x02952)
-  of "RightVectorBar": result = Rune(0x02953)
-  of "RightUpVectorBar": result = Rune(0x02954)
-  of "RightDownVectorBar": result = Rune(0x02955)
-  of "DownLeftVectorBar": result = Rune(0x02956)
-  of "DownRightVectorBar": result = Rune(0x02957)
-  of "LeftUpVectorBar": result = Rune(0x02958)
-  of "LeftDownVectorBar": result = Rune(0x02959)
-  of "LeftTeeVector": result = Rune(0x0295A)
-  of "RightTeeVector": result = Rune(0x0295B)
-  of "RightUpTeeVector": result = Rune(0x0295C)
-  of "RightDownTeeVector": result = Rune(0x0295D)
-  of "DownLeftTeeVector": result = Rune(0x0295E)
-  of "DownRightTeeVector": result = Rune(0x0295F)
-  of "LeftUpTeeVector": result = Rune(0x02960)
-  of "LeftDownTeeVector": result = Rune(0x02961)
-  of "lHar": result = Rune(0x02962)
-  of "uHar": result = Rune(0x02963)
-  of "rHar": result = Rune(0x02964)
-  of "dHar": result = Rune(0x02965)
-  of "luruhar": result = Rune(0x02966)
-  of "ldrdhar": result = Rune(0x02967)
-  of "ruluhar": result = Rune(0x02968)
-  of "rdldhar": result = Rune(0x02969)
-  of "lharul": result = Rune(0x0296A)
-  of "llhard": result = Rune(0x0296B)
-  of "rharul": result = Rune(0x0296C)
-  of "lrhard": result = Rune(0x0296D)
-  of "udhar", "UpEquilibrium": result = Rune(0x0296E)
-  of "duhar", "ReverseUpEquilibrium": result = Rune(0x0296F)
-  of "RoundImplies": result = Rune(0x02970)
-  of "erarr": result = Rune(0x02971)
-  of "simrarr": result = Rune(0x02972)
-  of "larrsim": result = Rune(0x02973)
-  of "rarrsim": result = Rune(0x02974)
-  of "rarrap": result = Rune(0x02975)
-  of "ltlarr": result = Rune(0x02976)
-  of "gtrarr": result = Rune(0x02978)
-  of "subrarr": result = Rune(0x02979)
-  of "suplarr": result = Rune(0x0297B)
-  of "lfisht": result = Rune(0x0297C)
-  of "rfisht": result = Rune(0x0297D)
-  of "ufisht": result = Rune(0x0297E)
-  of "dfisht": result = Rune(0x0297F)
-  of "lopar": result = Rune(0x02985)
-  of "ropar": result = Rune(0x02986)
-  of "lbrke": result = Rune(0x0298B)
-  of "rbrke": result = Rune(0x0298C)
-  of "lbrkslu": result = Rune(0x0298D)
-  of "rbrksld": result = Rune(0x0298E)
-  of "lbrksld": result = Rune(0x0298F)
-  of "rbrkslu": result = Rune(0x02990)
-  of "langd": result = Rune(0x02991)
-  of "rangd": result = Rune(0x02992)
-  of "lparlt": result = Rune(0x02993)
-  of "rpargt": result = Rune(0x02994)
-  of "gtlPar": result = Rune(0x02995)
-  of "ltrPar": result = Rune(0x02996)
-  of "vzigzag": result = Rune(0x0299A)
-  of "vangrt": result = Rune(0x0299C)
-  of "angrtvbd": result = Rune(0x0299D)
-  of "ange": result = Rune(0x029A4)
-  of "range": result = Rune(0x029A5)
-  of "dwangle": result = Rune(0x029A6)
-  of "uwangle": result = Rune(0x029A7)
-  of "angmsdaa": result = Rune(0x029A8)
-  of "angmsdab": result = Rune(0x029A9)
-  of "angmsdac": result = Rune(0x029AA)
-  of "angmsdad": result = Rune(0x029AB)
-  of "angmsdae": result = Rune(0x029AC)
-  of "angmsdaf": result = Rune(0x029AD)
-  of "angmsdag": result = Rune(0x029AE)
-  of "angmsdah": result = Rune(0x029AF)
-  of "bemptyv": result = Rune(0x029B0)
-  of "demptyv": result = Rune(0x029B1)
-  of "cemptyv": result = Rune(0x029B2)
-  of "raemptyv": result = Rune(0x029B3)
-  of "laemptyv": result = Rune(0x029B4)
-  of "ohbar": result = Rune(0x029B5)
-  of "omid": result = Rune(0x029B6)
-  of "opar": result = Rune(0x029B7)
-  of "operp": result = Rune(0x029B9)
-  of "olcross": result = Rune(0x029BB)
-  of "odsold": result = Rune(0x029BC)
-  of "olcir": result = Rune(0x029BE)
-  of "ofcir": result = Rune(0x029BF)
-  of "olt": result = Rune(0x029C0)
-  of "ogt": result = Rune(0x029C1)
-  of "cirscir": result = Rune(0x029C2)
-  of "cirE": result = Rune(0x029C3)
-  of "solb": result = Rune(0x029C4)
-  of "bsolb": result = Rune(0x029C5)
-  of "boxbox": result = Rune(0x029C9)
-  of "trisb": result = Rune(0x029CD)
-  of "rtriltri": result = Rune(0x029CE)
-  of "LeftTriangleBar": result = Rune(0x029CF)
-  of "RightTriangleBar": result = Rune(0x029D0)
-  of "race": result = Rune(0x029DA)
-  of "iinfin": result = Rune(0x029DC)
-  of "infintie": result = Rune(0x029DD)
-  of "nvinfin": result = Rune(0x029DE)
-  of "eparsl": result = Rune(0x029E3)
-  of "smeparsl": result = Rune(0x029E4)
-  of "eqvparsl": result = Rune(0x029E5)
-  of "lozf", "blacklozenge": result = Rune(0x029EB)
-  of "RuleDelayed": result = Rune(0x029F4)
-  of "dsol": result = Rune(0x029F6)
-  of "xodot", "bigodot": result = Rune(0x02A00)
-  of "xoplus", "bigoplus": result = Rune(0x02A01)
-  of "xotime", "bigotimes": result = Rune(0x02A02)
-  of "xuplus", "biguplus": result = Rune(0x02A04)
-  of "xsqcup", "bigsqcup": result = Rune(0x02A06)
-  of "qint", "iiiint": result = Rune(0x02A0C)
-  of "fpartint": result = Rune(0x02A0D)
-  of "cirfnint": result = Rune(0x02A10)
-  of "awint": result = Rune(0x02A11)
-  of "rppolint": result = Rune(0x02A12)
-  of "scpolint": result = Rune(0x02A13)
-  of "npolint": result = Rune(0x02A14)
-  of "pointint": result = Rune(0x02A15)
-  of "quatint": result = Rune(0x02A16)
-  of "intlarhk": result = Rune(0x02A17)
-  of "pluscir": result = Rune(0x02A22)
-  of "plusacir": result = Rune(0x02A23)
-  of "simplus": result = Rune(0x02A24)
-  of "plusdu": result = Rune(0x02A25)
-  of "plussim": result = Rune(0x02A26)
-  of "plustwo": result = Rune(0x02A27)
-  of "mcomma": result = Rune(0x02A29)
-  of "minusdu": result = Rune(0x02A2A)
-  of "loplus": result = Rune(0x02A2D)
-  of "roplus": result = Rune(0x02A2E)
-  of "Cross": result = Rune(0x02A2F)
-  of "timesd": result = Rune(0x02A30)
-  of "timesbar": result = Rune(0x02A31)
-  of "smashp": result = Rune(0x02A33)
-  of "lotimes": result = Rune(0x02A34)
-  of "rotimes": result = Rune(0x02A35)
-  of "otimesas": result = Rune(0x02A36)
-  of "Otimes": result = Rune(0x02A37)
-  of "odiv": result = Rune(0x02A38)
-  of "triplus": result = Rune(0x02A39)
-  of "triminus": result = Rune(0x02A3A)
-  of "tritime": result = Rune(0x02A3B)
-  of "iprod", "intprod": result = Rune(0x02A3C)
-  of "amalg": result = Rune(0x02A3F)
-  of "capdot": result = Rune(0x02A40)
-  of "ncup": result = Rune(0x02A42)
-  of "ncap": result = Rune(0x02A43)
-  of "capand": result = Rune(0x02A44)
-  of "cupor": result = Rune(0x02A45)
-  of "cupcap": result = Rune(0x02A46)
-  of "capcup": result = Rune(0x02A47)
-  of "cupbrcap": result = Rune(0x02A48)
-  of "capbrcup": result = Rune(0x02A49)
-  of "cupcup": result = Rune(0x02A4A)
-  of "capcap": result = Rune(0x02A4B)
-  of "ccups": result = Rune(0x02A4C)
-  of "ccaps": result = Rune(0x02A4D)
-  of "ccupssm": result = Rune(0x02A50)
-  of "And": result = Rune(0x02A53)
-  of "Or": result = Rune(0x02A54)
-  of "andand": result = Rune(0x02A55)
-  of "oror": result = Rune(0x02A56)
-  of "orslope": result = Rune(0x02A57)
-  of "andslope": result = Rune(0x02A58)
-  of "andv": result = Rune(0x02A5A)
-  of "orv": result = Rune(0x02A5B)
-  of "andd": result = Rune(0x02A5C)
-  of "ord": result = Rune(0x02A5D)
-  of "wedbar": result = Rune(0x02A5F)
-  of "sdote": result = Rune(0x02A66)
-  of "simdot": result = Rune(0x02A6A)
-  of "congdot": result = Rune(0x02A6D)
-  of "easter": result = Rune(0x02A6E)
-  of "apacir": result = Rune(0x02A6F)
-  of "apE": result = Rune(0x02A70)
-  of "eplus": result = Rune(0x02A71)
-  of "pluse": result = Rune(0x02A72)
-  of "Esim": result = Rune(0x02A73)
-  of "Colone": result = Rune(0x02A74)
-  of "Equal": result = Rune(0x02A75)
-  of "eDDot", "ddotseq": result = Rune(0x02A77)
-  of "equivDD": result = Rune(0x02A78)
-  of "ltcir": result = Rune(0x02A79)
-  of "gtcir": result = Rune(0x02A7A)
-  of "ltquest": result = Rune(0x02A7B)
-  of "gtquest": result = Rune(0x02A7C)
-  of "les", "LessSlantEqual", "leqslant": result = Rune(0x02A7D)
-  of "ges", "GreaterSlantEqual", "geqslant": result = Rune(0x02A7E)
-  of "lesdot": result = Rune(0x02A7F)
-  of "gesdot": result = Rune(0x02A80)
-  of "lesdoto": result = Rune(0x02A81)
-  of "gesdoto": result = Rune(0x02A82)
-  of "lesdotor": result = Rune(0x02A83)
-  of "gesdotol": result = Rune(0x02A84)
-  of "lap", "lessapprox": result = Rune(0x02A85)
-  of "gap", "gtrapprox": result = Rune(0x02A86)
-  of "lne", "lneq": result = Rune(0x02A87)
-  of "gne", "gneq": result = Rune(0x02A88)
-  of "lnap", "lnapprox": result = Rune(0x02A89)
-  of "gnap", "gnapprox": result = Rune(0x02A8A)
-  of "lEg", "lesseqqgtr": result = Rune(0x02A8B)
-  of "gEl", "gtreqqless": result = Rune(0x02A8C)
-  of "lsime": result = Rune(0x02A8D)
-  of "gsime": result = Rune(0x02A8E)
-  of "lsimg": result = Rune(0x02A8F)
-  of "gsiml": result = Rune(0x02A90)
-  of "lgE": result = Rune(0x02A91)
-  of "glE": result = Rune(0x02A92)
-  of "lesges": result = Rune(0x02A93)
-  of "gesles": result = Rune(0x02A94)
-  of "els", "eqslantless": result = Rune(0x02A95)
-  of "egs", "eqslantgtr": result = Rune(0x02A96)
-  of "elsdot": result = Rune(0x02A97)
-  of "egsdot": result = Rune(0x02A98)
-  of "el": result = Rune(0x02A99)
-  of "eg": result = Rune(0x02A9A)
-  of "siml": result = Rune(0x02A9D)
-  of "simg": result = Rune(0x02A9E)
-  of "simlE": result = Rune(0x02A9F)
-  of "simgE": result = Rune(0x02AA0)
-  of "LessLess": result = Rune(0x02AA1)
-  of "GreaterGreater": result = Rune(0x02AA2)
-  of "glj": result = Rune(0x02AA4)
-  of "gla": result = Rune(0x02AA5)
-  of "ltcc": result = Rune(0x02AA6)
-  of "gtcc": result = Rune(0x02AA7)
-  of "lescc": result = Rune(0x02AA8)
-  of "gescc": result = Rune(0x02AA9)
-  of "smt": result = Rune(0x02AAA)
-  of "lat": result = Rune(0x02AAB)
-  of "smte": result = Rune(0x02AAC)
-  of "late": result = Rune(0x02AAD)
-  of "bumpE": result = Rune(0x02AAE)
-  of "pre", "preceq", "PrecedesEqual": result = Rune(0x02AAF)
-  of "sce", "succeq", "SucceedsEqual": result = Rune(0x02AB0)
-  of "prE": result = Rune(0x02AB3)
-  of "scE": result = Rune(0x02AB4)
-  of "prnE", "precneqq": result = Rune(0x02AB5)
-  of "scnE", "succneqq": result = Rune(0x02AB6)
-  of "prap", "precapprox": result = Rune(0x02AB7)
-  of "scap", "succapprox": result = Rune(0x02AB8)
-  of "prnap", "precnapprox": result = Rune(0x02AB9)
-  of "scnap", "succnapprox": result = Rune(0x02ABA)
-  of "Pr": result = Rune(0x02ABB)
-  of "Sc": result = Rune(0x02ABC)
-  of "subdot": result = Rune(0x02ABD)
-  of "supdot": result = Rune(0x02ABE)
-  of "subplus": result = Rune(0x02ABF)
-  of "supplus": result = Rune(0x02AC0)
-  of "submult": result = Rune(0x02AC1)
-  of "supmult": result = Rune(0x02AC2)
-  of "subedot": result = Rune(0x02AC3)
-  of "supedot": result = Rune(0x02AC4)
-  of "subE", "subseteqq": result = Rune(0x02AC5)
-  of "supE", "supseteqq": result = Rune(0x02AC6)
-  of "subsim": result = Rune(0x02AC7)
-  of "supsim": result = Rune(0x02AC8)
-  of "subnE", "subsetneqq": result = Rune(0x02ACB)
-  of "supnE", "supsetneqq": result = Rune(0x02ACC)
-  of "csub": result = Rune(0x02ACF)
-  of "csup": result = Rune(0x02AD0)
-  of "csube": result = Rune(0x02AD1)
-  of "csupe": result = Rune(0x02AD2)
-  of "subsup": result = Rune(0x02AD3)
-  of "supsub": result = Rune(0x02AD4)
-  of "subsub": result = Rune(0x02AD5)
-  of "supsup": result = Rune(0x02AD6)
-  of "suphsub": result = Rune(0x02AD7)
-  of "supdsub": result = Rune(0x02AD8)
-  of "forkv": result = Rune(0x02AD9)
-  of "topfork": result = Rune(0x02ADA)
-  of "mlcp": result = Rune(0x02ADB)
-  of "Dashv", "DoubleLeftTee": result = Rune(0x02AE4)
-  of "Vdashl": result = Rune(0x02AE6)
-  of "Barv": result = Rune(0x02AE7)
-  of "vBar": result = Rune(0x02AE8)
-  of "vBarv": result = Rune(0x02AE9)
-  of "Vbar": result = Rune(0x02AEB)
-  of "Not": result = Rune(0x02AEC)
-  of "bNot": result = Rune(0x02AED)
-  of "rnmid": result = Rune(0x02AEE)
-  of "cirmid": result = Rune(0x02AEF)
-  of "midcir": result = Rune(0x02AF0)
-  of "topcir": result = Rune(0x02AF1)
-  of "nhpar": result = Rune(0x02AF2)
-  of "parsim": result = Rune(0x02AF3)
-  of "parsl": result = Rune(0x02AFD)
-  of "fflig": result = Rune(0x0FB00)
-  of "filig": result = Rune(0x0FB01)
-  of "fllig": result = Rune(0x0FB02)
-  of "ffilig": result = Rune(0x0FB03)
-  of "ffllig": result = Rune(0x0FB04)
-  of "Ascr": result = Rune(0x1D49C)
-  of "Cscr": result = Rune(0x1D49E)
-  of "Dscr": result = Rune(0x1D49F)
-  of "Gscr": result = Rune(0x1D4A2)
-  of "Jscr": result = Rune(0x1D4A5)
-  of "Kscr": result = Rune(0x1D4A6)
-  of "Nscr": result = Rune(0x1D4A9)
-  of "Oscr": result = Rune(0x1D4AA)
-  of "Pscr": result = Rune(0x1D4AB)
-  of "Qscr": result = Rune(0x1D4AC)
-  of "Sscr": result = Rune(0x1D4AE)
-  of "Tscr": result = Rune(0x1D4AF)
-  of "Uscr": result = Rune(0x1D4B0)
-  of "Vscr": result = Rune(0x1D4B1)
-  of "Wscr": result = Rune(0x1D4B2)
-  of "Xscr": result = Rune(0x1D4B3)
-  of "Yscr": result = Rune(0x1D4B4)
-  of "Zscr": result = Rune(0x1D4B5)
-  of "ascr": result = Rune(0x1D4B6)
-  of "bscr": result = Rune(0x1D4B7)
-  of "cscr": result = Rune(0x1D4B8)
-  of "dscr": result = Rune(0x1D4B9)
-  of "fscr": result = Rune(0x1D4BB)
-  of "hscr": result = Rune(0x1D4BD)
-  of "iscr": result = Rune(0x1D4BE)
-  of "jscr": result = Rune(0x1D4BF)
-  of "kscr": result = Rune(0x1D4C0)
-  of "lscr": result = Rune(0x1D4C1)
-  of "mscr": result = Rune(0x1D4C2)
-  of "nscr": result = Rune(0x1D4C3)
-  of "pscr": result = Rune(0x1D4C5)
-  of "qscr": result = Rune(0x1D4C6)
-  of "rscr": result = Rune(0x1D4C7)
-  of "sscr": result = Rune(0x1D4C8)
-  of "tscr": result = Rune(0x1D4C9)
-  of "uscr": result = Rune(0x1D4CA)
-  of "vscr": result = Rune(0x1D4CB)
-  of "wscr": result = Rune(0x1D4CC)
-  of "xscr": result = Rune(0x1D4CD)
-  of "yscr": result = Rune(0x1D4CE)
-  of "zscr": result = Rune(0x1D4CF)
-  of "Afr": result = Rune(0x1D504)
-  of "Bfr": result = Rune(0x1D505)
-  of "Dfr": result = Rune(0x1D507)
-  of "Efr": result = Rune(0x1D508)
-  of "Ffr": result = Rune(0x1D509)
-  of "Gfr": result = Rune(0x1D50A)
-  of "Jfr": result = Rune(0x1D50D)
-  of "Kfr": result = Rune(0x1D50E)
-  of "Lfr": result = Rune(0x1D50F)
-  of "Mfr": result = Rune(0x1D510)
-  of "Nfr": result = Rune(0x1D511)
-  of "Ofr": result = Rune(0x1D512)
-  of "Pfr": result = Rune(0x1D513)
-  of "Qfr": result = Rune(0x1D514)
-  of "Sfr": result = Rune(0x1D516)
-  of "Tfr": result = Rune(0x1D517)
-  of "Ufr": result = Rune(0x1D518)
-  of "Vfr": result = Rune(0x1D519)
-  of "Wfr": result = Rune(0x1D51A)
-  of "Xfr": result = Rune(0x1D51B)
-  of "Yfr": result = Rune(0x1D51C)
-  of "afr": result = Rune(0x1D51E)
-  of "bfr": result = Rune(0x1D51F)
-  of "cfr": result = Rune(0x1D520)
-  of "dfr": result = Rune(0x1D521)
-  of "efr": result = Rune(0x1D522)
-  of "ffr": result = Rune(0x1D523)
-  of "gfr": result = Rune(0x1D524)
-  of "hfr": result = Rune(0x1D525)
-  of "ifr": result = Rune(0x1D526)
-  of "jfr": result = Rune(0x1D527)
-  of "kfr": result = Rune(0x1D528)
-  of "lfr": result = Rune(0x1D529)
-  of "mfr": result = Rune(0x1D52A)
-  of "nfr": result = Rune(0x1D52B)
-  of "ofr": result = Rune(0x1D52C)
-  of "pfr": result = Rune(0x1D52D)
-  of "qfr": result = Rune(0x1D52E)
-  of "rfr": result = Rune(0x1D52F)
-  of "sfr": result = Rune(0x1D530)
-  of "tfr": result = Rune(0x1D531)
-  of "ufr": result = Rune(0x1D532)
-  of "vfr": result = Rune(0x1D533)
-  of "wfr": result = Rune(0x1D534)
-  of "xfr": result = Rune(0x1D535)
-  of "yfr": result = Rune(0x1D536)
-  of "zfr": result = Rune(0x1D537)
-  of "Aopf": result = Rune(0x1D538)
-  of "Bopf": result = Rune(0x1D539)
-  of "Dopf": result = Rune(0x1D53B)
-  of "Eopf": result = Rune(0x1D53C)
-  of "Fopf": result = Rune(0x1D53D)
-  of "Gopf": result = Rune(0x1D53E)
-  of "Iopf": result = Rune(0x1D540)
-  of "Jopf": result = Rune(0x1D541)
-  of "Kopf": result = Rune(0x1D542)
-  of "Lopf": result = Rune(0x1D543)
-  of "Mopf": result = Rune(0x1D544)
-  of "Oopf": result = Rune(0x1D546)
-  of "Sopf": result = Rune(0x1D54A)
-  of "Topf": result = Rune(0x1D54B)
-  of "Uopf": result = Rune(0x1D54C)
-  of "Vopf": result = Rune(0x1D54D)
-  of "Wopf": result = Rune(0x1D54E)
-  of "Xopf": result = Rune(0x1D54F)
-  of "Yopf": result = Rune(0x1D550)
-  of "aopf": result = Rune(0x1D552)
-  of "bopf": result = Rune(0x1D553)
-  of "copf": result = Rune(0x1D554)
-  of "dopf": result = Rune(0x1D555)
-  of "eopf": result = Rune(0x1D556)
-  of "fopf": result = Rune(0x1D557)
-  of "gopf": result = Rune(0x1D558)
-  of "hopf": result = Rune(0x1D559)
-  of "iopf": result = Rune(0x1D55A)
-  of "jopf": result = Rune(0x1D55B)
-  of "kopf": result = Rune(0x1D55C)
-  of "lopf": result = Rune(0x1D55D)
-  of "mopf": result = Rune(0x1D55E)
-  of "nopf": result = Rune(0x1D55F)
-  of "oopf": result = Rune(0x1D560)
-  of "popf": result = Rune(0x1D561)
-  of "qopf": result = Rune(0x1D562)
-  of "ropf": result = Rune(0x1D563)
-  of "sopf": result = Rune(0x1D564)
-  of "topf": result = Rune(0x1D565)
-  of "uopf": result = Rune(0x1D566)
-  of "vopf": result = Rune(0x1D567)
-  of "wopf": result = Rune(0x1D568)
-  of "xopf": result = Rune(0x1D569)
-  of "yopf": result = Rune(0x1D56A)
-  of "zopf": result = Rune(0x1D56B)
-  else: discard
+    "DoubleLongLeftRightArrow": Rune(0x027FA)
+  of "xmap", "longmapsto": Rune(0x027FC)
+  of "dzigrarr": Rune(0x027FF)
+  of "nvlArr": Rune(0x02902)
+  of "nvrArr": Rune(0x02903)
+  of "nvHarr": Rune(0x02904)
+  of "Map": Rune(0x02905)
+  of "lbarr": Rune(0x0290C)
+  of "rbarr", "bkarow": Rune(0x0290D)
+  of "lBarr": Rune(0x0290E)
+  of "rBarr", "dbkarow": Rune(0x0290F)
+  of "RBarr", "drbkarow": Rune(0x02910)
+  of "DDotrahd": Rune(0x02911)
+  of "UpArrowBar": Rune(0x02912)
+  of "DownArrowBar": Rune(0x02913)
+  of "Rarrtl": Rune(0x02916)
+  of "latail": Rune(0x02919)
+  of "ratail": Rune(0x0291A)
+  of "lAtail": Rune(0x0291B)
+  of "rAtail": Rune(0x0291C)
+  of "larrfs": Rune(0x0291D)
+  of "rarrfs": Rune(0x0291E)
+  of "larrbfs": Rune(0x0291F)
+  of "rarrbfs": Rune(0x02920)
+  of "nwarhk": Rune(0x02923)
+  of "nearhk": Rune(0x02924)
+  of "searhk", "hksearow": Rune(0x02925)
+  of "swarhk", "hkswarow": Rune(0x02926)
+  of "nwnear": Rune(0x02927)
+  of "nesear", "toea": Rune(0x02928)
+  of "seswar", "tosa": Rune(0x02929)
+  of "swnwar": Rune(0x0292A)
+  of "rarrc": Rune(0x02933)
+  of "cudarrr": Rune(0x02935)
+  of "ldca": Rune(0x02936)
+  of "rdca": Rune(0x02937)
+  of "cudarrl": Rune(0x02938)
+  of "larrpl": Rune(0x02939)
+  of "curarrm": Rune(0x0293C)
+  of "cularrp": Rune(0x0293D)
+  of "rarrpl": Rune(0x02945)
+  of "harrcir": Rune(0x02948)
+  of "Uarrocir": Rune(0x02949)
+  of "lurdshar": Rune(0x0294A)
+  of "ldrushar": Rune(0x0294B)
+  of "LeftRightVector": Rune(0x0294E)
+  of "RightUpDownVector": Rune(0x0294F)
+  of "DownLeftRightVector": Rune(0x02950)
+  of "LeftUpDownVector": Rune(0x02951)
+  of "LeftVectorBar": Rune(0x02952)
+  of "RightVectorBar": Rune(0x02953)
+  of "RightUpVectorBar": Rune(0x02954)
+  of "RightDownVectorBar": Rune(0x02955)
+  of "DownLeftVectorBar": Rune(0x02956)
+  of "DownRightVectorBar": Rune(0x02957)
+  of "LeftUpVectorBar": Rune(0x02958)
+  of "LeftDownVectorBar": Rune(0x02959)
+  of "LeftTeeVector": Rune(0x0295A)
+  of "RightTeeVector": Rune(0x0295B)
+  of "RightUpTeeVector": Rune(0x0295C)
+  of "RightDownTeeVector": Rune(0x0295D)
+  of "DownLeftTeeVector": Rune(0x0295E)
+  of "DownRightTeeVector": Rune(0x0295F)
+  of "LeftUpTeeVector": Rune(0x02960)
+  of "LeftDownTeeVector": Rune(0x02961)
+  of "lHar": Rune(0x02962)
+  of "uHar": Rune(0x02963)
+  of "rHar": Rune(0x02964)
+  of "dHar": Rune(0x02965)
+  of "luruhar": Rune(0x02966)
+  of "ldrdhar": Rune(0x02967)
+  of "ruluhar": Rune(0x02968)
+  of "rdldhar": Rune(0x02969)
+  of "lharul": Rune(0x0296A)
+  of "llhard": Rune(0x0296B)
+  of "rharul": Rune(0x0296C)
+  of "lrhard": Rune(0x0296D)
+  of "udhar", "UpEquilibrium": Rune(0x0296E)
+  of "duhar", "ReverseUpEquilibrium": Rune(0x0296F)
+  of "RoundImplies": Rune(0x02970)
+  of "erarr": Rune(0x02971)
+  of "simrarr": Rune(0x02972)
+  of "larrsim": Rune(0x02973)
+  of "rarrsim": Rune(0x02974)
+  of "rarrap": Rune(0x02975)
+  of "ltlarr": Rune(0x02976)
+  of "gtrarr": Rune(0x02978)
+  of "subrarr": Rune(0x02979)
+  of "suplarr": Rune(0x0297B)
+  of "lfisht": Rune(0x0297C)
+  of "rfisht": Rune(0x0297D)
+  of "ufisht": Rune(0x0297E)
+  of "dfisht": Rune(0x0297F)
+  of "lopar": Rune(0x02985)
+  of "ropar": Rune(0x02986)
+  of "lbrke": Rune(0x0298B)
+  of "rbrke": Rune(0x0298C)
+  of "lbrkslu": Rune(0x0298D)
+  of "rbrksld": Rune(0x0298E)
+  of "lbrksld": Rune(0x0298F)
+  of "rbrkslu": Rune(0x02990)
+  of "langd": Rune(0x02991)
+  of "rangd": Rune(0x02992)
+  of "lparlt": Rune(0x02993)
+  of "rpargt": Rune(0x02994)
+  of "gtlPar": Rune(0x02995)
+  of "ltrPar": Rune(0x02996)
+  of "vzigzag": Rune(0x0299A)
+  of "vangrt": Rune(0x0299C)
+  of "angrtvbd": Rune(0x0299D)
+  of "ange": Rune(0x029A4)
+  of "range": Rune(0x029A5)
+  of "dwangle": Rune(0x029A6)
+  of "uwangle": Rune(0x029A7)
+  of "angmsdaa": Rune(0x029A8)
+  of "angmsdab": Rune(0x029A9)
+  of "angmsdac": Rune(0x029AA)
+  of "angmsdad": Rune(0x029AB)
+  of "angmsdae": Rune(0x029AC)
+  of "angmsdaf": Rune(0x029AD)
+  of "angmsdag": Rune(0x029AE)
+  of "angmsdah": Rune(0x029AF)
+  of "bemptyv": Rune(0x029B0)
+  of "demptyv": Rune(0x029B1)
+  of "cemptyv": Rune(0x029B2)
+  of "raemptyv": Rune(0x029B3)
+  of "laemptyv": Rune(0x029B4)
+  of "ohbar": Rune(0x029B5)
+  of "omid": Rune(0x029B6)
+  of "opar": Rune(0x029B7)
+  of "operp": Rune(0x029B9)
+  of "olcross": Rune(0x029BB)
+  of "odsold": Rune(0x029BC)
+  of "olcir": Rune(0x029BE)
+  of "ofcir": Rune(0x029BF)
+  of "olt": Rune(0x029C0)
+  of "ogt": Rune(0x029C1)
+  of "cirscir": Rune(0x029C2)
+  of "cirE": Rune(0x029C3)
+  of "solb": Rune(0x029C4)
+  of "bsolb": Rune(0x029C5)
+  of "boxbox": Rune(0x029C9)
+  of "trisb": Rune(0x029CD)
+  of "rtriltri": Rune(0x029CE)
+  of "LeftTriangleBar": Rune(0x029CF)
+  of "RightTriangleBar": Rune(0x029D0)
+  of "race": Rune(0x029DA)
+  of "iinfin": Rune(0x029DC)
+  of "infintie": Rune(0x029DD)
+  of "nvinfin": Rune(0x029DE)
+  of "eparsl": Rune(0x029E3)
+  of "smeparsl": Rune(0x029E4)
+  of "eqvparsl": Rune(0x029E5)
+  of "lozf", "blacklozenge": Rune(0x029EB)
+  of "RuleDelayed": Rune(0x029F4)
+  of "dsol": Rune(0x029F6)
+  of "xodot", "bigodot": Rune(0x02A00)
+  of "xoplus", "bigoplus": Rune(0x02A01)
+  of "xotime", "bigotimes": Rune(0x02A02)
+  of "xuplus", "biguplus": Rune(0x02A04)
+  of "xsqcup", "bigsqcup": Rune(0x02A06)
+  of "qint", "iiiint": Rune(0x02A0C)
+  of "fpartint": Rune(0x02A0D)
+  of "cirfnint": Rune(0x02A10)
+  of "awint": Rune(0x02A11)
+  of "rppolint": Rune(0x02A12)
+  of "scpolint": Rune(0x02A13)
+  of "npolint": Rune(0x02A14)
+  of "pointint": Rune(0x02A15)
+  of "quatint": Rune(0x02A16)
+  of "intlarhk": Rune(0x02A17)
+  of "pluscir": Rune(0x02A22)
+  of "plusacir": Rune(0x02A23)
+  of "simplus": Rune(0x02A24)
+  of "plusdu": Rune(0x02A25)
+  of "plussim": Rune(0x02A26)
+  of "plustwo": Rune(0x02A27)
+  of "mcomma": Rune(0x02A29)
+  of "minusdu": Rune(0x02A2A)
+  of "loplus": Rune(0x02A2D)
+  of "roplus": Rune(0x02A2E)
+  of "Cross": Rune(0x02A2F)
+  of "timesd": Rune(0x02A30)
+  of "timesbar": Rune(0x02A31)
+  of "smashp": Rune(0x02A33)
+  of "lotimes": Rune(0x02A34)
+  of "rotimes": Rune(0x02A35)
+  of "otimesas": Rune(0x02A36)
+  of "Otimes": Rune(0x02A37)
+  of "odiv": Rune(0x02A38)
+  of "triplus": Rune(0x02A39)
+  of "triminus": Rune(0x02A3A)
+  of "tritime": Rune(0x02A3B)
+  of "iprod", "intprod": Rune(0x02A3C)
+  of "amalg": Rune(0x02A3F)
+  of "capdot": Rune(0x02A40)
+  of "ncup": Rune(0x02A42)
+  of "ncap": Rune(0x02A43)
+  of "capand": Rune(0x02A44)
+  of "cupor": Rune(0x02A45)
+  of "cupcap": Rune(0x02A46)
+  of "capcup": Rune(0x02A47)
+  of "cupbrcap": Rune(0x02A48)
+  of "capbrcup": Rune(0x02A49)
+  of "cupcup": Rune(0x02A4A)
+  of "capcap": Rune(0x02A4B)
+  of "ccups": Rune(0x02A4C)
+  of "ccaps": Rune(0x02A4D)
+  of "ccupssm": Rune(0x02A50)
+  of "And": Rune(0x02A53)
+  of "Or": Rune(0x02A54)
+  of "andand": Rune(0x02A55)
+  of "oror": Rune(0x02A56)
+  of "orslope": Rune(0x02A57)
+  of "andslope": Rune(0x02A58)
+  of "andv": Rune(0x02A5A)
+  of "orv": Rune(0x02A5B)
+  of "andd": Rune(0x02A5C)
+  of "ord": Rune(0x02A5D)
+  of "wedbar": Rune(0x02A5F)
+  of "sdote": Rune(0x02A66)
+  of "simdot": Rune(0x02A6A)
+  of "congdot": Rune(0x02A6D)
+  of "easter": Rune(0x02A6E)
+  of "apacir": Rune(0x02A6F)
+  of "apE": Rune(0x02A70)
+  of "eplus": Rune(0x02A71)
+  of "pluse": Rune(0x02A72)
+  of "Esim": Rune(0x02A73)
+  of "Colone": Rune(0x02A74)
+  of "Equal": Rune(0x02A75)
+  of "eDDot", "ddotseq": Rune(0x02A77)
+  of "equivDD": Rune(0x02A78)
+  of "ltcir": Rune(0x02A79)
+  of "gtcir": Rune(0x02A7A)
+  of "ltquest": Rune(0x02A7B)
+  of "gtquest": Rune(0x02A7C)
+  of "les", "LessSlantEqual", "leqslant": Rune(0x02A7D)
+  of "ges", "GreaterSlantEqual", "geqslant": Rune(0x02A7E)
+  of "lesdot": Rune(0x02A7F)
+  of "gesdot": Rune(0x02A80)
+  of "lesdoto": Rune(0x02A81)
+  of "gesdoto": Rune(0x02A82)
+  of "lesdotor": Rune(0x02A83)
+  of "gesdotol": Rune(0x02A84)
+  of "lap", "lessapprox": Rune(0x02A85)
+  of "gap", "gtrapprox": Rune(0x02A86)
+  of "lne", "lneq": Rune(0x02A87)
+  of "gne", "gneq": Rune(0x02A88)
+  of "lnap", "lnapprox": Rune(0x02A89)
+  of "gnap", "gnapprox": Rune(0x02A8A)
+  of "lEg", "lesseqqgtr": Rune(0x02A8B)
+  of "gEl", "gtreqqless": Rune(0x02A8C)
+  of "lsime": Rune(0x02A8D)
+  of "gsime": Rune(0x02A8E)
+  of "lsimg": Rune(0x02A8F)
+  of "gsiml": Rune(0x02A90)
+  of "lgE": Rune(0x02A91)
+  of "glE": Rune(0x02A92)
+  of "lesges": Rune(0x02A93)
+  of "gesles": Rune(0x02A94)
+  of "els", "eqslantless": Rune(0x02A95)
+  of "egs", "eqslantgtr": Rune(0x02A96)
+  of "elsdot": Rune(0x02A97)
+  of "egsdot": Rune(0x02A98)
+  of "el": Rune(0x02A99)
+  of "eg": Rune(0x02A9A)
+  of "siml": Rune(0x02A9D)
+  of "simg": Rune(0x02A9E)
+  of "simlE": Rune(0x02A9F)
+  of "simgE": Rune(0x02AA0)
+  of "LessLess": Rune(0x02AA1)
+  of "GreaterGreater": Rune(0x02AA2)
+  of "glj": Rune(0x02AA4)
+  of "gla": Rune(0x02AA5)
+  of "ltcc": Rune(0x02AA6)
+  of "gtcc": Rune(0x02AA7)
+  of "lescc": Rune(0x02AA8)
+  of "gescc": Rune(0x02AA9)
+  of "smt": Rune(0x02AAA)
+  of "lat": Rune(0x02AAB)
+  of "smte": Rune(0x02AAC)
+  of "late": Rune(0x02AAD)
+  of "bumpE": Rune(0x02AAE)
+  of "pre", "preceq", "PrecedesEqual": Rune(0x02AAF)
+  of "sce", "succeq", "SucceedsEqual": Rune(0x02AB0)
+  of "prE": Rune(0x02AB3)
+  of "scE": Rune(0x02AB4)
+  of "prnE", "precneqq": Rune(0x02AB5)
+  of "scnE", "succneqq": Rune(0x02AB6)
+  of "prap", "precapprox": Rune(0x02AB7)
+  of "scap", "succapprox": Rune(0x02AB8)
+  of "prnap", "precnapprox": Rune(0x02AB9)
+  of "scnap", "succnapprox": Rune(0x02ABA)
+  of "Pr": Rune(0x02ABB)
+  of "Sc": Rune(0x02ABC)
+  of "subdot": Rune(0x02ABD)
+  of "supdot": Rune(0x02ABE)
+  of "subplus": Rune(0x02ABF)
+  of "supplus": Rune(0x02AC0)
+  of "submult": Rune(0x02AC1)
+  of "supmult": Rune(0x02AC2)
+  of "subedot": Rune(0x02AC3)
+  of "supedot": Rune(0x02AC4)
+  of "subE", "subseteqq": Rune(0x02AC5)
+  of "supE", "supseteqq": Rune(0x02AC6)
+  of "subsim": Rune(0x02AC7)
+  of "supsim": Rune(0x02AC8)
+  of "subnE", "subsetneqq": Rune(0x02ACB)
+  of "supnE", "supsetneqq": Rune(0x02ACC)
+  of "csub": Rune(0x02ACF)
+  of "csup": Rune(0x02AD0)
+  of "csube": Rune(0x02AD1)
+  of "csupe": Rune(0x02AD2)
+  of "subsup": Rune(0x02AD3)
+  of "supsub": Rune(0x02AD4)
+  of "subsub": Rune(0x02AD5)
+  of "supsup": Rune(0x02AD6)
+  of "suphsub": Rune(0x02AD7)
+  of "supdsub": Rune(0x02AD8)
+  of "forkv": Rune(0x02AD9)
+  of "topfork": Rune(0x02ADA)
+  of "mlcp": Rune(0x02ADB)
+  of "Dashv", "DoubleLeftTee": Rune(0x02AE4)
+  of "Vdashl": Rune(0x02AE6)
+  of "Barv": Rune(0x02AE7)
+  of "vBar": Rune(0x02AE8)
+  of "vBarv": Rune(0x02AE9)
+  of "Vbar": Rune(0x02AEB)
+  of "Not": Rune(0x02AEC)
+  of "bNot": Rune(0x02AED)
+  of "rnmid": Rune(0x02AEE)
+  of "cirmid": Rune(0x02AEF)
+  of "midcir": Rune(0x02AF0)
+  of "topcir": Rune(0x02AF1)
+  of "nhpar": Rune(0x02AF2)
+  of "parsim": Rune(0x02AF3)
+  of "parsl": Rune(0x02AFD)
+  of "fflig": Rune(0x0FB00)
+  of "filig": Rune(0x0FB01)
+  of "fllig": Rune(0x0FB02)
+  of "ffilig": Rune(0x0FB03)
+  of "ffllig": Rune(0x0FB04)
+  of "Ascr": Rune(0x1D49C)
+  of "Cscr": Rune(0x1D49E)
+  of "Dscr": Rune(0x1D49F)
+  of "Gscr": Rune(0x1D4A2)
+  of "Jscr": Rune(0x1D4A5)
+  of "Kscr": Rune(0x1D4A6)
+  of "Nscr": Rune(0x1D4A9)
+  of "Oscr": Rune(0x1D4AA)
+  of "Pscr": Rune(0x1D4AB)
+  of "Qscr": Rune(0x1D4AC)
+  of "Sscr": Rune(0x1D4AE)
+  of "Tscr": Rune(0x1D4AF)
+  of "Uscr": Rune(0x1D4B0)
+  of "Vscr": Rune(0x1D4B1)
+  of "Wscr": Rune(0x1D4B2)
+  of "Xscr": Rune(0x1D4B3)
+  of "Yscr": Rune(0x1D4B4)
+  of "Zscr": Rune(0x1D4B5)
+  of "ascr": Rune(0x1D4B6)
+  of "bscr": Rune(0x1D4B7)
+  of "cscr": Rune(0x1D4B8)
+  of "dscr": Rune(0x1D4B9)
+  of "fscr": Rune(0x1D4BB)
+  of "hscr": Rune(0x1D4BD)
+  of "iscr": Rune(0x1D4BE)
+  of "jscr": Rune(0x1D4BF)
+  of "kscr": Rune(0x1D4C0)
+  of "lscr": Rune(0x1D4C1)
+  of "mscr": Rune(0x1D4C2)
+  of "nscr": Rune(0x1D4C3)
+  of "pscr": Rune(0x1D4C5)
+  of "qscr": Rune(0x1D4C6)
+  of "rscr": Rune(0x1D4C7)
+  of "sscr": Rune(0x1D4C8)
+  of "tscr": Rune(0x1D4C9)
+  of "uscr": Rune(0x1D4CA)
+  of "vscr": Rune(0x1D4CB)
+  of "wscr": Rune(0x1D4CC)
+  of "xscr": Rune(0x1D4CD)
+  of "yscr": Rune(0x1D4CE)
+  of "zscr": Rune(0x1D4CF)
+  of "Afr": Rune(0x1D504)
+  of "Bfr": Rune(0x1D505)
+  of "Dfr": Rune(0x1D507)
+  of "Efr": Rune(0x1D508)
+  of "Ffr": Rune(0x1D509)
+  of "Gfr": Rune(0x1D50A)
+  of "Jfr": Rune(0x1D50D)
+  of "Kfr": Rune(0x1D50E)
+  of "Lfr": Rune(0x1D50F)
+  of "Mfr": Rune(0x1D510)
+  of "Nfr": Rune(0x1D511)
+  of "Ofr": Rune(0x1D512)
+  of "Pfr": Rune(0x1D513)
+  of "Qfr": Rune(0x1D514)
+  of "Sfr": Rune(0x1D516)
+  of "Tfr": Rune(0x1D517)
+  of "Ufr": Rune(0x1D518)
+  of "Vfr": Rune(0x1D519)
+  of "Wfr": Rune(0x1D51A)
+  of "Xfr": Rune(0x1D51B)
+  of "Yfr": Rune(0x1D51C)
+  of "afr": Rune(0x1D51E)
+  of "bfr": Rune(0x1D51F)
+  of "cfr": Rune(0x1D520)
+  of "dfr": Rune(0x1D521)
+  of "efr": Rune(0x1D522)
+  of "ffr": Rune(0x1D523)
+  of "gfr": Rune(0x1D524)
+  of "hfr": Rune(0x1D525)
+  of "ifr": Rune(0x1D526)
+  of "jfr": Rune(0x1D527)
+  of "kfr": Rune(0x1D528)
+  of "lfr": Rune(0x1D529)
+  of "mfr": Rune(0x1D52A)
+  of "nfr": Rune(0x1D52B)
+  of "ofr": Rune(0x1D52C)
+  of "pfr": Rune(0x1D52D)
+  of "qfr": Rune(0x1D52E)
+  of "rfr": Rune(0x1D52F)
+  of "sfr": Rune(0x1D530)
+  of "tfr": Rune(0x1D531)
+  of "ufr": Rune(0x1D532)
+  of "vfr": Rune(0x1D533)
+  of "wfr": Rune(0x1D534)
+  of "xfr": Rune(0x1D535)
+  of "yfr": Rune(0x1D536)
+  of "zfr": Rune(0x1D537)
+  of "Aopf": Rune(0x1D538)
+  of "Bopf": Rune(0x1D539)
+  of "Dopf": Rune(0x1D53B)
+  of "Eopf": Rune(0x1D53C)
+  of "Fopf": Rune(0x1D53D)
+  of "Gopf": Rune(0x1D53E)
+  of "Iopf": Rune(0x1D540)
+  of "Jopf": Rune(0x1D541)
+  of "Kopf": Rune(0x1D542)
+  of "Lopf": Rune(0x1D543)
+  of "Mopf": Rune(0x1D544)
+  of "Oopf": Rune(0x1D546)
+  of "Sopf": Rune(0x1D54A)
+  of "Topf": Rune(0x1D54B)
+  of "Uopf": Rune(0x1D54C)
+  of "Vopf": Rune(0x1D54D)
+  of "Wopf": Rune(0x1D54E)
+  of "Xopf": Rune(0x1D54F)
+  of "Yopf": Rune(0x1D550)
+  of "aopf": Rune(0x1D552)
+  of "bopf": Rune(0x1D553)
+  of "copf": Rune(0x1D554)
+  of "dopf": Rune(0x1D555)
+  of "eopf": Rune(0x1D556)
+  of "fopf": Rune(0x1D557)
+  of "gopf": Rune(0x1D558)
+  of "hopf": Rune(0x1D559)
+  of "iopf": Rune(0x1D55A)
+  of "jopf": Rune(0x1D55B)
+  of "kopf": Rune(0x1D55C)
+  of "lopf": Rune(0x1D55D)
+  of "mopf": Rune(0x1D55E)
+  of "nopf": Rune(0x1D55F)
+  of "oopf": Rune(0x1D560)
+  of "popf": Rune(0x1D561)
+  of "qopf": Rune(0x1D562)
+  of "ropf": Rune(0x1D563)
+  of "sopf": Rune(0x1D564)
+  of "topf": Rune(0x1D565)
+  of "uopf": Rune(0x1D566)
+  of "vopf": Rune(0x1D567)
+  of "wopf": Rune(0x1D568)
+  of "xopf": Rune(0x1D569)
+  of "yopf": Rune(0x1D56A)
+  of "zopf": Rune(0x1D56B)
+  else: Rune(0)
 
 proc entityToUtf8*(entity: string): string =
-  ## Converts an HTML entity name like ``&Uuml;`` or values like ``&#220;``
-  ## or ``&#x000DC;`` to its UTF-8 equivalent.
+  ## Converts an HTML entity name like `&Uuml;` or values like `&#220;`
+  ## or `&#x000DC;` to its UTF-8 equivalent.
   ## "" is returned if the entity name is unknown. The HTML parser
   ## already converts entities to UTF-8.
   runnableExamples:
-    doAssert entityToUtf8(nil) == ""
+    const sigma = "Σ"
     doAssert entityToUtf8("") == ""
     doAssert entityToUtf8("a") == ""
     doAssert entityToUtf8("gt") == ">"
     doAssert entityToUtf8("Uuml") == "Ü"
     doAssert entityToUtf8("quest") == "?"
     doAssert entityToUtf8("#63") == "?"
-    doAssert entityToUtf8("Sigma") == "Σ"
-    doAssert entityToUtf8("#931") == "Σ"
-    doAssert entityToUtf8("#0931") == "Σ"
-    doAssert entityToUtf8("#x3A3") == "Σ"
-    doAssert entityToUtf8("#x03A3") == "Σ"
-    doAssert entityToUtf8("#x3a3") == "Σ"
-    doAssert entityToUtf8("#X3a3") == "Σ"
+    doAssert entityToUtf8("Sigma") == sigma
+    doAssert entityToUtf8("#931") == sigma
+    doAssert entityToUtf8("#0931") == sigma
+    doAssert entityToUtf8("#x3A3") == sigma
+    doAssert entityToUtf8("#x03A3") == sigma
+    doAssert entityToUtf8("#x3a3") == sigma
+    doAssert entityToUtf8("#X3a3") == sigma
   let rune = entityToRune(entity)
   if rune.ord <= 0: result = ""
   else: result = toUTF8(rune)
@@ -1890,7 +1896,7 @@ proc entityToUtf8*(entity: string): string =
 proc addNode(father, son: XmlNode) =
   if son != nil: add(father, son)
 
-proc parse(x: var XmlParser, errors: var seq[string]): XmlNode
+proc parse(x: var XmlParser, errors: var seq[string]): XmlNode {.gcsafe.}
 
 proc expected(x: var XmlParser, n: XmlNode): string =
   result = errorMsg(x, "</" & n.tag & "> expected")
@@ -1902,7 +1908,7 @@ template adderr(x: untyped) =
 
 proc untilElementEnd(x: var XmlParser, result: XmlNode,
                      errors: var seq[string]) =
-  # we parsed e.g. ``<br>`` and don't really expect a ``</br>``:
+  # we parsed e.g. `<br>` and don't really expect a `</br>`:
   if result.htmlTag in SingleTags:
     if x.kind != xmlElementEnd or cmpIgnoreCase(x.elemName, result.tag) != 0:
       return
@@ -1911,8 +1917,8 @@ proc untilElementEnd(x: var XmlParser, result: XmlNode,
     of xmlElementStart, xmlElementOpen:
       case result.htmlTag
       of tagP, tagInput, tagOption:
-        # some tags are common to have no ``</end>``, like ``<li>`` but
-        # allow ``<p>`` in `<dd>`, `<dt>` and ``<li>`` in next case
+        # some tags are common to have no `</end>`, like `<li>` but
+        # allow `<p>` in `<dd>`, `<dt>` and `<li>` in next case
         if htmlTag(x.elemName) in {tagLi, tagP, tagDt, tagDd, tagInput,
                                    tagOption}:
           adderr(expected(x, result))
@@ -1942,7 +1948,8 @@ proc untilElementEnd(x: var XmlParser, result: XmlNode,
         adderr(expected(x, result))
         # this seems to do better match error corrections in browsers:
         while x.kind in {xmlElementEnd, xmlWhitespace}:
-          if x.kind == xmlElementEnd and cmpIgnoreCase(x.elemName, result.tag) == 0:
+          if x.kind == xmlElementEnd and cmpIgnoreCase(x.elemName,
+              result.tag) == 0:
             break
           next(x)
       next(x)
@@ -2008,10 +2015,11 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
 
 proc parseHtml*(s: Stream, filename: string,
                 errors: var seq[string]): XmlNode =
-  ## Parses the XML from stream `s` and returns a ``XmlNode``. Every
+  ## Parses the XML from stream `s` and returns a `XmlNode`. Every
   ## occurred parsing error is added to the `errors` sequence.
   var x: XmlParser
-  open(x, s, filename, {reportComments, reportWhitespace})
+  open(x, s, filename, {reportComments, reportWhitespace, allowUnquotedAttribs,
+    allowEmptyAttribs})
   next(x)
   # skip the DOCTYPE:
   if x.kind == xmlSpecial: next(x)
@@ -2021,7 +2029,7 @@ proc parseHtml*(s: Stream, filename: string,
   #if x.kind != xmlEof:
   #  adderr(errorMsg(x, "EOF expected"))
   while x.kind != xmlEof:
-    var oldPos = x.bufpos # little hack to see if we made any progess
+    var oldPos = x.bufpos # little hack to see if we made any progress
     result.addNode(parse(x, errors))
     if x.bufpos == oldPos:
       # force progress!
@@ -2031,32 +2039,32 @@ proc parseHtml*(s: Stream, filename: string,
     result = result[0]
 
 proc parseHtml*(s: Stream): XmlNode =
-  ## Parses the HTML from stream `s` and returns a ``XmlNode``. All parsing
+  ## Parses the HTML from stream `s` and returns a `XmlNode`. All parsing
   ## errors are ignored.
   var errors: seq[string] = @[]
   result = parseHtml(s, "unknown_html_doc", errors)
 
 proc parseHtml*(html: string): XmlNode =
-  ## Parses the HTML from string ``html`` and returns a ``XmlNode``. All parsing
+  ## Parses the HTML from string `html` and returns a `XmlNode`. All parsing
   ## errors are ignored.
   parseHtml(newStringStream(html))
 
 proc loadHtml*(path: string, errors: var seq[string]): XmlNode =
-  ## Loads and parses HTML from file specified by ``path``, and returns
-  ## a ``XmlNode``. Every occurred parsing error is added to
+  ## Loads and parses HTML from file specified by `path`, and returns
+  ## a `XmlNode`. Every occurred parsing error is added to
   ## the `errors` sequence.
   var s = newFileStream(path, fmRead)
   if s == nil: raise newException(IOError, "Unable to read file: " & path)
   result = parseHtml(s, path, errors)
 
 proc loadHtml*(path: string): XmlNode =
-  ## Loads and parses HTML from file specified by ``path``, and returns
-  ## a ``XmlNode``. All parsing errors are ignored.
+  ## Loads and parses HTML from file specified by `path`, and returns
+  ## a `XmlNode`. All parsing errors are ignored.
   var errors: seq[string] = @[]
   result = loadHtml(path, errors)
 
 when not defined(testing) and isMainModule:
-  import os
+  import std/os
 
   var errors: seq[string] = @[]
   var x = loadHtml(paramStr(1), errors)
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 8b4fb0f8c..08ea99627 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nim's Runtime Library
-#        (c) Copyright 2016 Dominik Picheta, Andreas Rumpf
+#        (c) Copyright 2019 Nim Contributors
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -10,60 +10,103 @@
 ## This module implements a simple HTTP client that can be used to retrieve
 ## webpages and other data.
 ##
+## .. warning:: Validate untrusted inputs: URI parsers and getters are not detecting malicious URIs.
+##
 ## Retrieving a website
 ## ====================
 ##
 ## This example uses HTTP GET to retrieve
-## ``http://google.com``:
+## `http://google.com`:
 ##
-## .. code-block:: Nim
+##   ```Nim
+##   import std/httpclient
 ##   var client = newHttpClient()
-##   echo client.getContent("http://google.com")
+##   try:
+##     echo client.getContent("http://google.com")
+##   finally:
+##     client.close()
+##   ```
 ##
 ## The same action can also be performed asynchronously, simply use the
-## ``AsyncHttpClient``:
+## `AsyncHttpClient`:
+##
+##   ```Nim
+##   import std/[asyncdispatch, httpclient]
 ##
-## .. code-block:: Nim
-##   var client = newAsyncHttpClient()
-##   echo await client.getContent("http://google.com")
+##   proc asyncProc(): Future[string] {.async.} =
+##     var client = newAsyncHttpClient()
+##     try:
+##       return await client.getContent("http://google.com")
+##     finally:
+##       client.close()
 ##
-## The functionality implemented by ``HttpClient`` and ``AsyncHttpClient``
+##   echo waitFor asyncProc()
+##   ```
+##
+## The functionality implemented by `HttpClient` and `AsyncHttpClient`
 ## is the same, so you can use whichever one suits you best in the examples
 ## shown here.
 ##
-## **Note:** You will need to run asynchronous examples in an async proc
-## otherwise you will get an ``Undeclared identifier: 'await'`` error.
+## **Note:** You need to run asynchronous examples in an async proc
+## otherwise you will get an `Undeclared identifier: 'await'` error.
+##
+## **Note:** An asynchronous client instance can only deal with one
+## request at a time. To send multiple requests in parallel, use
+## multiple client instances.
 ##
 ## Using HTTP POST
 ## ===============
 ##
 ## This example demonstrates the usage of the W3 HTML Validator, it
-## uses ``multipart/form-data`` as the ``Content-Type`` to send the HTML to be
+## uses `multipart/form-data` as the `Content-Type` to send the HTML to be
 ## validated to the server.
 ##
-## .. code-block:: Nim
+##   ```Nim
 ##   var client = newHttpClient()
 ##   var data = newMultipartData()
 ##   data["output"] = "soap12"
 ##   data["uploaded_file"] = ("test.html", "text/html",
 ##     "<html><head></head><body><p>test</p></body></html>")
+##   try:
+##     echo client.postContent("http://validator.w3.org/check", multipart=data)
+##   finally:
+##     client.close()
+##   ```
 ##
-##   echo client.postContent("http://validator.w3.org/check", multipart=data)
+## To stream files from disk when performing the request, use `addFiles`.
+##
+## **Note:** This will allocate a new `Mimetypes` database every time you call
+## it, you can pass your own via the `mimeDb` parameter to avoid this.
+##
+##   ```Nim
+##   let mimes = newMimetypes()
+##   var client = newHttpClient()
+##   var data = newMultipartData()
+##   data.addFiles({"uploaded_file": "test.html"}, mimeDb = mimes)
+##   try:
+##     echo client.postContent("http://validator.w3.org/check", multipart=data)
+##   finally:
+##     client.close()
+##   ```
 ##
 ## You can also make post requests with custom headers.
-## This example sets ``Content-Type`` to ``application/json``
+## This example sets `Content-Type` to `application/json`
 ## and uses a json object for the body
 ##
-## .. code-block:: Nim
-##   import httpclient, json
+##   ```Nim
+##   import std/[httpclient, json]
 ##
 ##   let client = newHttpClient()
 ##   client.headers = newHttpHeaders({ "Content-Type": "application/json" })
 ##   let body = %*{
 ##       "data": "some text"
 ##   }
-##   let response = client.request("http://some.api", httpMethod = HttpPost, body = $body)
-##   echo response.status
+##   try:
+##     let response = client.request("http://some.api", httpMethod = HttpPost, body = $body)
+##     echo response.status
+##   finally:
+##     client.close()
+##   ```
 ##
 ## Progress reporting
 ## ==================
@@ -72,37 +115,64 @@
 ## This callback will be executed every second with information about the
 ## progress of the HTTP request.
 ##
-## .. code-block:: Nim
-##    import asyncdispatch, httpclient
+##   ```Nim
+##   import std/[asyncdispatch, httpclient]
 ##
-##    proc onProgressChanged(total, progress, speed: BiggestInt) {.async.} =
-##      echo("Downloaded ", progress, " of ", total)
-##      echo("Current rate: ", speed div 1000, "kb/s")
+##   proc onProgressChanged(total, progress, speed: BiggestInt) {.async.} =
+##     echo("Downloaded ", progress, " of ", total)
+##     echo("Current rate: ", speed div 1000, "kb/s")
 ##
-##    proc asyncProc() {.async.} =
-##      var client = newAsyncHttpClient()
-##      client.onProgressChanged = onProgressChanged
-##      discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test")
+##   proc asyncProc() {.async.} =
+##     var client = newAsyncHttpClient()
+##     client.onProgressChanged = onProgressChanged
+##     try:
+##       discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test")
+##     finally:
+##       client.close()
 ##
-##    waitFor asyncProc()
+##   waitFor asyncProc()
+##   ```
 ##
-## If you would like to remove the callback simply set it to ``nil``.
+## If you would like to remove the callback simply set it to `nil`.
 ##
-## .. code-block:: Nim
+##   ```Nim
 ##   client.onProgressChanged = nil
+##   ```
 ##
-## **Warning:** The ``total`` reported by httpclient may be 0 in some cases.
+## .. warning:: The `total` reported by httpclient may be 0 in some cases.
 ##
 ##
 ## SSL/TLS support
 ## ===============
-## This requires the OpenSSL library, fortunately it's widely used and installed
+## This requires the OpenSSL library. Fortunately it's widely used and installed
 ## on many operating systems. httpclient will use SSL automatically if you give
-## any of the functions a url with the ``https`` schema, for example:
-## ``https://github.com/``.
+## any of the functions a url with the `https` schema, for example:
+## `https://github.com/`.
 ##
-## You will also have to compile with ``ssl`` defined like so:
-## ``nim c -d:ssl ...``.
+## You will also have to compile with `ssl` defined like so:
+## `nim c -d:ssl ...`.
+##
+## Certificate validation is performed by default.
+##
+## A set of directories and files from the `ssl_certs <ssl_certs.html>`_
+## module are scanned to locate CA certificates.
+##
+## Example of setting SSL verification parameters in a new client:
+##
+##   ```Nim
+##   import httpclient
+##   var client = newHttpClient(sslContext=newContext(verifyMode=CVerifyPeer))
+##   ```
+##
+## There are three options for verify mode:
+##
+## * ``CVerifyNone``: certificates are not verified;
+## * ``CVerifyPeer``: certificates are verified;
+## * ``CVerifyPeerUseEnvVars``: certificates are verified and the optional
+##   environment variables SSL_CERT_FILE and SSL_CERT_DIR are also used to
+##   locate certificates
+##
+## See `newContext <net.html#newContext.string,string,string,string>`_ to tweak or disable certificate validation.
 ##
 ## Timeouts
 ## ========
@@ -117,21 +187,87 @@
 ## individual internal calls on the socket are affected. In practice this means
 ## that as long as the server is sending data an exception will not be raised,
 ## if however data does not reach the client within the specified timeout a
-## ``TimeoutError`` exception will be raised.
+## `TimeoutError` exception will be raised.
+##
+## Here is how to set a timeout when creating an `HttpClient` instance:
+##
+##   ```Nim
+##   import std/httpclient
+##
+##   let client = newHttpClient(timeout = 42)
+##   ```
 ##
 ## Proxy
 ## =====
 ##
 ## A proxy can be specified as a param to any of the procedures defined in
-## this module. To do this, use the ``newProxy`` constructor. Unfortunately,
+## this module. To do this, use the `newProxy` constructor. Unfortunately,
 ## only basic authentication is supported at the moment.
+##
+## Some examples on how to configure a Proxy for `HttpClient`:
+##
+##   ```Nim
+##   import std/httpclient
+##
+##   let myProxy = newProxy("http://myproxy.network")
+##   let client = newHttpClient(proxy = myProxy)
+##   ```
+##
+## Use proxies with basic authentication:
+##
+##   ```Nim
+##   import std/httpclient
+##
+##   let myProxy = newProxy("http://myproxy.network", auth="user:password")
+##   let client = newHttpClient(proxy = myProxy)
+##   ```
+##
+## Get Proxy URL from environment variables:
+##
+##   ```Nim
+##   import std/httpclient
+##
+##   var url = ""
+##   try:
+##     if existsEnv("http_proxy"):
+##       url = getEnv("http_proxy")
+##     elif existsEnv("https_proxy"):
+##       url = getEnv("https_proxy")
+##   except ValueError:
+##     echo "Unable to parse proxy from environment variables."
+##
+##   let myProxy = newProxy(url = url)
+##   let client = newHttpClient(proxy = myProxy)
+##   ```
+##
+## Redirects
+## =========
+##
+## The maximum redirects can be set with the `maxRedirects` of `int` type,
+## it specifies the maximum amount of redirects to follow,
+## it defaults to `5`, you can set it to `0` to disable redirects.
+##
+## Here you can see an example about how to set the `maxRedirects` of `HttpClient`:
+##
+##   ```Nim
+##   import std/httpclient
+##
+##   let client = newHttpClient(maxRedirects = 0)
+##   ```
+##
+
+import std/private/since
 
-import net, strutils, uri, parseutils, strtabs, base64, os, mimetypes,
-  math, random, httpcore, times, tables, streams
-import asyncnet, asyncdispatch, asyncfile
-import nativesockets
+import std/[
+  net, strutils, uri, parseutils, base64, os, mimetypes,
+  math, random, httpcore, times, tables, streams, monotimes,
+  asyncnet, asyncdispatch, asyncfile, nativesockets,
+]
 
-export httpcore except parseHeader # TODO: The ``except`` doesn't work
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
+
+export httpcore except parseHeader # TODO: The `except` doesn't work
 
 type
   Response* = ref object
@@ -149,14 +285,14 @@ type
     bodyStream*: FutureStream[string]
 
 proc code*(response: Response | AsyncResponse): HttpCode
-           {.raises: [ValueError, OverflowError].} =
-  ## Retrieves the specified response's ``HttpCode``.
+           {.raises: [ValueError, OverflowDefect].} =
+  ## Retrieves the specified response's `HttpCode`.
   ##
-  ## Raises a ``ValueError`` if the response's ``status`` does not have a
-  ## corresponding ``HttpCode``.
+  ## Raises a `ValueError` if the response's `status` does not have a
+  ## corresponding `HttpCode`.
   return response.status[0 .. 2].parseInt.HttpCode
 
-proc contentType*(response: Response | AsyncResponse): string =
+proc contentType*(response: Response | AsyncResponse): string {.inline.} =
   ## Retrieves the specified response's content type.
   ##
   ## This is effectively the value of the "Content-Type" header.
@@ -167,38 +303,33 @@ proc contentLength*(response: Response | AsyncResponse): int =
   ##
   ## This is effectively the value of the "Content-Length" header.
   ##
-  ## A ``ValueError`` exception will be raised if the value is not an integer.
-  var contentLengthHeader = response.headers.getOrDefault("Content-Length")
-  return contentLengthHeader.parseInt()
+  ## A `ValueError` exception will be raised if the value is not an integer.
+  ## If the Content-Length header is not set in the response, ContentLength is set to the value -1.
+  var contentLengthHeader = response.headers.getOrDefault("Content-Length", HttpHeaderValues(@["-1"]))
+  result = contentLengthHeader.parseInt()
 
 proc lastModified*(response: Response | AsyncResponse): DateTime =
   ## Retrieves the specified response's last modified time.
   ##
   ## This is effectively the value of the "Last-Modified" header.
   ##
-  ## Raises a ``ValueError`` if the parsing fails or the value is not a correctly
+  ## Raises a `ValueError` if the parsing fails or the value is not a correctly
   ## formatted time.
   var lastModifiedHeader = response.headers.getOrDefault("last-modified")
-  result = parse(lastModifiedHeader, "dd, dd MMM yyyy HH:mm:ss Z")
+  result = parse(lastModifiedHeader, "ddd, dd MMM yyyy HH:mm:ss 'GMT'", utc())
 
 proc body*(response: Response): string =
   ## Retrieves the specified response's body.
   ##
   ## The response's body stream is read synchronously.
-  if response.body.isNil():
+  if response.body.len == 0:
     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.
-  if response.body.isNil:
+  if response.body.len == 0:
     response.body = await readAll(response.bodyStream)
   return response.body
 
@@ -207,19 +338,28 @@ type
     url*: Uri
     auth*: string
 
-  MultipartEntries* = openarray[tuple[name, content: string]]
+  MultipartEntry = object
+    name, content: string
+    case isFile: bool
+    of true:
+      filename, contentType: string
+      fileSize: int64
+      isStream: bool
+    else: discard
+
+  MultipartEntries* = openArray[tuple[name, content: string]]
   MultipartData* = ref object
-    content: seq[string]
+    content: seq[MultipartEntry]
 
-  ProtocolError* = object of IOError   ## exception that is raised when server
-                                       ## does not conform to the implemented
-                                       ## protocol
+  ProtocolError* = object of IOError ## exception that is raised when server
+                                     ## does not conform to the implemented
+                                     ## protocol
 
-  HttpRequestError* = object of IOError ## Thrown in the ``getContent`` proc
-                                        ## and ``postContent`` proc,
+  HttpRequestError* = object of IOError ## Thrown in the `getContent` proc
+                                        ## and `postContent` proc,
                                         ## when the server returns an error
 
-const defUserAgent* = "Nim httpclient/" & NimVersion
+const defUserAgent* = "Nim-httpclient/" & NimVersion
 
 proc httpError(msg: string) =
   var e: ref ProtocolError
@@ -233,180 +373,78 @@ proc fileError(msg: string) =
   e.msg = msg
   raise e
 
-proc parseChunks(s: Socket, timeout: int): string =
-  result = ""
-  var ri = 0
-  while true:
-    var chunkSizeStr = ""
-    var chunkSize = 0
-    s.readLine(chunkSizeStr, timeout)
-    var i = 0
-    if chunkSizeStr == "":
-      httpError("Server terminated connection prematurely")
-    while i < chunkSizeStr.len:
-      case chunkSizeStr[i]
-      of '0'..'9':
-        chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0'))
-      of 'a'..'f':
-        chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10)
-      of 'A'..'F':
-        chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10)
-      of ';':
-        # http://tools.ietf.org/html/rfc2616#section-3.6.1
-        # We don't care about chunk-extensions.
-        break
-      else:
-        httpError("Invalid chunk size: " & chunkSizeStr)
-      inc(i)
-    if chunkSize <= 0:
-      s.skip(2, timeout) # Skip \c\L
-      break
-    result.setLen(ri+chunkSize)
-    var bytesRead = 0
-    while bytesRead != chunkSize:
-      let ret = recv(s, addr(result[ri]), chunkSize-bytesRead, timeout)
-      ri += ret
-      bytesRead += ret
-    s.skip(2, timeout) # Skip \c\L
-    # Trailer headers will only be sent if the request specifies that we want
-    # them: http://tools.ietf.org/html/rfc2616#section-3.6.1
-
-proc parseBody(s: Socket, headers: HttpHeaders, httpVersion: string, timeout: int): string =
-  result = ""
-  if headers.getOrDefault"Transfer-Encoding" == "chunked":
-    result = parseChunks(s, timeout)
-  else:
-    # -REGION- Content-Length
-    # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.3
-    var contentLengthHeader = headers.getOrDefault"Content-Length"
-    if contentLengthHeader != "":
-      var length = contentLengthHeader.parseint()
-      if length > 0:
-        result = newString(length)
-        var received = 0
-        while true:
-          if received >= length: break
-          let r = s.recv(addr(result[received]), length-received, timeout)
-          if r == 0: break
-          received += r
-        if received != length:
-          httpError("Got invalid content length. Expected: " & $length &
-                    " got: " & $received)
-    else:
-      # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.4 TODO
-
-      # -REGION- Connection: Close
-      # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.5
-      if headers.getOrDefault"Connection" == "close" or httpVersion == "1.0":
-        var buf = ""
-        while true:
-          buf = newString(4000)
-          let r = s.recv(addr(buf[0]), 4000, timeout)
-          if r == 0: break
-          buf.setLen(r)
-          result.add(buf)
-
-proc parseResponse(s: Socket, getBody: bool, timeout: int): Response =
-  new result
-  var parsedStatus = false
-  var linei = 0
-  var fullyRead = false
-  var line = ""
-  result.headers = newHttpHeaders()
-  while true:
-    line = ""
-    linei = 0
-    s.readLine(line, timeout)
-    if line == "": break # We've been disconnected.
-    if line == "\c\L":
-      fullyRead = true
-      break
-    if not parsedStatus:
-      # Parse HTTP version info and status code.
-      var le = skipIgnoreCase(line, "HTTP/", linei)
-      if le <= 0: httpError("invalid http version")
-      inc(linei, le)
-      le = skipIgnoreCase(line, "1.1", linei)
-      if le > 0: result.version = "1.1"
-      else:
-        le = skipIgnoreCase(line, "1.0", linei)
-        if le <= 0: httpError("unsupported http version")
-        result.version = "1.0"
-      inc(linei, le)
-      # Status code
-      linei.inc skipWhitespace(line, linei)
-      result.status = line[linei .. ^1]
-      parsedStatus = true
-    else:
-      # Parse headers
-      var name = ""
-      var le = parseUntil(line, name, ':', linei)
-      if le <= 0: httpError("invalid headers")
-      inc(linei, le)
-      if line[linei] != ':': httpError("invalid headers")
-      inc(linei) # Skip :
-
-      result.headers.add(name, line[linei.. ^1].strip())
-      # Ensure the server isn't trying to DoS us.
-      if result.headers.len > headerLimit:
-        httpError("too many headers")
-
-  if not fullyRead:
-    httpError("Connection was closed before full request has been made")
-  if getBody:
-    result.body = parseBody(s, result.headers, result.version, timeout)
-  else:
-    result.body = ""
-
 when not defined(ssl):
-  type SSLContext = ref object
-var defaultSSLContext {.threadvar.}: SSLContext
+  type SslContext = ref object
+var defaultSslContext {.threadvar.}: SslContext
 
-proc getDefaultSSL(): SSLContext =
+proc getDefaultSSL(): SslContext =
   result = defaultSslContext
   when defined(ssl):
     if result == nil:
-      defaultSSLContext = newContext(verifyMode = CVerifyNone)
-      result = defaultSSLContext
+      defaultSslContext = newContext(verifyMode = CVerifyPeer)
+      result = defaultSslContext
       doAssert result != nil, "failure to initialize the SSL context"
 
-proc newProxy*(url: string, auth = ""): Proxy =
-  ## Constructs a new ``TProxy`` object.
+proc newProxy*(url: string; auth = ""): Proxy =
+  ## Constructs a new `TProxy` object.
   result = Proxy(url: parseUri(url), auth: auth)
 
-proc newMultipartData*: MultipartData =
-  ## Constructs a new ``MultipartData`` object.
-  MultipartData(content: @[])
-
-proc add*(p: var MultipartData, name, content: string, filename: string = nil,
-          contentType: string = nil) =
-  ## Add a value to the multipart data. Raises a `ValueError` exception if
+proc newProxy*(url: Uri; auth = ""): Proxy =
+  ## Constructs a new `TProxy` object.
+  result = Proxy(url: url, auth: auth)
+
+proc newMultipartData*: MultipartData {.inline.} =
+  ## Constructs a new `MultipartData` object.
+  MultipartData()
+
+proc `$`*(data: MultipartData): string {.since: (1, 1).} =
+  ## convert MultipartData to string so it's human readable when echo
+  ## see https://github.com/nim-lang/Nim/issues/11863
+  const sep = "-".repeat(30)
+  for pos, entry in data.content:
+    result.add(sep & center($pos, 3) & sep)
+    result.add("\nname=\"" & entry.name & "\"")
+    if entry.isFile:
+      result.add("; filename=\"" & entry.filename & "\"\n")
+      result.add("Content-Type: " & entry.contentType)
+    result.add("\n\n" & entry.content & "\n")
+
+proc add*(p: MultipartData, name, content: string, filename: string = "",
+          contentType: string = "", useStream = true) =
+  ## Add a value to the multipart data.
+  ##
+  ## When `useStream` is `false`, the file will be read into memory.
+  ##
+  ## Raises a `ValueError` exception if
   ## `name`, `filename` or `contentType` contain newline characters.
-
-  if {'\c','\L'} in name:
+  if {'\c', '\L'} in name:
     raise newException(ValueError, "name contains a newline character")
-  if filename != nil and {'\c','\L'} in filename:
+  if {'\c', '\L'} in filename:
     raise newException(ValueError, "filename contains a newline character")
-  if contentType != nil and {'\c','\L'} in contentType:
+  if {'\c', '\L'} in contentType:
     raise newException(ValueError, "contentType contains a newline character")
 
-  var str = "Content-Disposition: form-data; name=\"" & name & "\""
-  if filename != nil:
-    str.add("; filename=\"" & filename & "\"")
-  str.add("\c\L")
-  if contentType != nil:
-    str.add("Content-Type: " & contentType & "\c\L")
-  str.add("\c\L" & content & "\c\L")
+  var entry = MultipartEntry(
+    name: name,
+    content: content,
+    isFile: filename.len > 0
+  )
+
+  if entry.isFile:
+    entry.isStream = useStream
+    entry.filename = filename
+    entry.contentType = contentType
 
-  p.content.add(str)
+  p.content.add(entry)
 
-proc add*(p: var MultipartData, xs: MultipartEntries): MultipartData
+proc add*(p: MultipartData, xs: MultipartEntries): MultipartData
          {.discardable.} =
   ## Add a list of multipart entries to the multipart data `p`. All values are
   ## added without a filename and without a content type.
   ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   data.add({"action": "login", "format": "json"})
+  ##   ```
   for name, content in xs.items:
     p.add(name, content)
   result = p
@@ -415,328 +453,96 @@ proc newMultipartData*(xs: MultipartEntries): MultipartData =
   ## Create a new multipart data object and fill it with the entries `xs`
   ## directly.
   ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   var data = newMultipartData({"action": "login", "format": "json"})
-  result = MultipartData(content: @[])
-  result.add(xs)
-
-proc addFiles*(p: var MultipartData, xs: openarray[tuple[name, file: string]]):
-              MultipartData {.discardable.} =
-  ## Add files to a multipart data object. The file will be opened from your
-  ## disk, read and sent with the automatically determined MIME type. Raises an
-  ## `IOError` if the file cannot be opened or reading fails. To manually
-  ## specify file content, filename and MIME type, use `[]=` instead.
+  ##   ```
+  result = MultipartData()
+  for entry in xs:
+    result.add(entry.name, entry.content)
+
+proc addFiles*(p: MultipartData, xs: openArray[tuple[name, file: string]],
+               mimeDb = newMimetypes(), useStream = true):
+               MultipartData {.discardable.} =
+  ## Add files to a multipart data object. The files will be streamed from disk
+  ## when the request is being made. When `stream` is `false`, the files are
+  ## instead read into memory, but beware this is very memory ineffecient even
+  ## for small files. The MIME types will automatically be determined.
+  ## Raises an `IOError` if the file cannot be opened or reading fails. To
+  ## manually specify file content, filename and MIME type, use `[]=` instead.
   ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   data.addFiles({"uploaded_file": "public/test.html"})
-  var m = newMimetypes()
+  ##   ```
   for name, file in xs.items:
     var contentType: string
     let (_, fName, ext) = splitFile(file)
     if ext.len > 0:
-      contentType = m.getMimetype(ext[1..ext.high], nil)
-    p.add(name, readFile(file), fName & ext, contentType)
+      contentType = mimeDb.getMimetype(ext[1..ext.high], "")
+    let content = if useStream: file else: readFile(file)
+    p.add(name, content, fName & ext, contentType, useStream = useStream)
   result = p
 
-proc `[]=`*(p: var MultipartData, name, content: string) =
+proc `[]=`*(p: MultipartData, name, content: string) {.inline.} =
   ## Add a multipart entry to the multipart data `p`. The value is added
   ## without a filename and without a content type.
   ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   data["username"] = "NimUser"
+  ##   ```
   p.add(name, content)
 
-proc `[]=`*(p: var MultipartData, name: string,
-            file: tuple[name, contentType, content: string]) =
-  ## Add a file to the multipart data `p`, specifying filename, contentType and
-  ## content manually.
+proc `[]=`*(p: MultipartData, name: string,
+            file: tuple[name, contentType, content: string]) {.inline.} =
+  ## Add a file to the multipart data `p`, specifying filename, contentType
+  ## and content manually.
   ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   data["uploaded_file"] = ("test.html", "text/html",
   ##     "<html><head></head><body><p>test</p></body></html>")
-  p.add(name, file.content, file.name, file.contentType)
+  ##   ```
+  p.add(name, file.content, file.name, file.contentType, useStream = false)
 
-proc format(p: MultipartData): tuple[contentType, body: string] =
-  if p == nil or p.content == nil or p.content.len == 0:
-    return ("", "")
-
-  # Create boundary that is not in the data to be formatted
-  var bound: string
+proc getBoundary(p: MultipartData): string =
+  if p == nil or p.content.len == 0: return
   while true:
-    bound = $random(int.high)
-    var found = false
-    for s in p.content:
-      if bound in s:
-        found = true
-    if not found:
-      break
-
-  result.contentType = "multipart/form-data; boundary=" & bound
-  result.body = ""
-  for s in p.content:
-    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):
-    if status.startsWith(i):
-      return true
-
-proc getNewLocation(lastURL: string, headers: HttpHeaders): string =
-  result = headers.getOrDefault"Location"
-  if result == "": httpError("location header expected")
+    result = $rand(int.high)
+    for i, entry in p.content:
+      if result in entry.content: break
+      elif i == p.content.high: return
+
+proc sendFile(socket: Socket | AsyncSocket,
+              entry: MultipartEntry) {.multisync.} =
+  const chunkSize = 2^18
+  let file =
+    when socket is AsyncSocket: openAsync(entry.content)
+    else: newFileStream(entry.content, fmRead)
+
+  var buffer: string
+  while true:
+    buffer =
+      when socket is AsyncSocket: (await read(file, chunkSize))
+      else: readStr(file, chunkSize)
+    if buffer.len == 0: break
+    await socket.send(buffer)
+  file.close()
+
+proc getNewLocation(lastURL: Uri, headers: HttpHeaders): Uri =
+  let newLocation = headers.getOrDefault"Location"
+  if newLocation == "": httpError("location header expected")
   # Relative URLs. (Not part of the spec, but soon will be.)
-  let r = parseUri(result)
-  if r.hostname == "" and r.path != "":
-    var parsed = parseUri(lastURL)
-    parsed.path = r.path
-    parsed.query = r.query
-    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)
+  let parsedLocation = parseUri(newLocation)
+  if parsedLocation.hostname == "" and parsedLocation.path != "":
+    result = lastURL
+    result.path = parsedLocation.path
+    result.query = parsedLocation.query
+    result.anchor = parsedLocation.anchor
   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
+    result = parsedLocation
 
-  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 =
+proc generateHeaders(requestUrl: Uri, httpMethod: HttpMethod, headers: HttpHeaders,
+                     proxy: Proxy): string =
   # GET
-  let upperMethod = httpMethod.toUpperAscii()
-  result = upperMethod
+  result = $httpMethod
   result.add ' '
 
   if proxy.isNil or requestUrl.scheme == "https":
@@ -752,33 +558,28 @@ proc generateHeaders(requestUrl: Uri, httpMethod: string,
     result.add($modifiedUrl)
 
   # HTTP/1.1\c\l
-  result.add(" HTTP/1.1\c\L")
+  result.add(" HTTP/1.1" & httpNewLine)
 
   # Host header.
-  if requestUrl.port == "":
-    add(result, "Host: " & requestUrl.hostname & "\c\L")
-  else:
-    add(result, "Host: " & requestUrl.hostname & ":" & requestUrl.port & "\c\L")
+  if not headers.hasKey("Host"):
+    if requestUrl.port == "":
+      add(result, "Host: " & requestUrl.hostname & httpNewLine)
+    else:
+      add(result, "Host: " & requestUrl.hostname & ":" & requestUrl.port & httpNewLine)
 
   # Connection header.
   if not headers.hasKey("Connection"):
-    add(result, "Connection: Keep-Alive\c\L")
-
-  # Content length header.
-  const requiresBody = ["POST", "PUT", "PATCH"]
-  let needsContentLength = body.len > 0 or upperMethod in requiresBody
-  if needsContentLength and not headers.hasKey("Content-Length"):
-    add(result, "Content-Length: " & $body.len & "\c\L")
+    add(result, "Connection: Keep-Alive" & httpNewLine)
 
   # Proxy auth header.
   if not proxy.isNil and proxy.auth != "":
-    let auth = base64.encode(proxy.auth, newline = "")
-    add(result, "Proxy-Authorization: basic " & auth & "\c\L")
+    let auth = base64.encode(proxy.auth)
+    add(result, "Proxy-Authorization: Basic " & auth & httpNewLine)
 
   for key, val in headers:
-    add(result, key & ": " & val & "\c\L")
+    add(result, key & ": " & val & httpNewLine)
 
-  add(result, "\c\L")
+  add(result, httpNewLine)
 
 type
   ProgressChangedProc*[ReturnType] =
@@ -788,13 +589,13 @@ type
   HttpClientBase*[SocketType] = ref object
     socket: SocketType
     connected: bool
-    currentURL: Uri ## Where we are currently connected.
+    currentURL: Uri       ## Where we are currently connected.
     headers*: HttpHeaders ## Headers to send in requests.
-    maxRedirects: int
+    maxRedirects: Natural ## Maximum redirects, set to `0` to disable.
     userAgent: string
-    timeout: int ## Only used for blocking HttpClient for now.
+    timeout*: int         ## Only used for blocking HttpClient for now.
     proxy: Proxy
-    ## ``nil`` or the callback to call when request progress changes.
+    ## `nil` or the callback to call when request progress changes.
     when SocketType is Socket:
       onProgressChanged*: ProgressChangedProc[void]
     else:
@@ -804,36 +605,47 @@ type
     contentTotal: BiggestInt
     contentProgress: BiggestInt
     oneSecondProgress: BiggestInt
-    lastProgressReport: float
+    lastProgressReport: MonoTime
     when SocketType is AsyncSocket:
       bodyStream: FutureStream[string]
+      parseBodyFut: Future[void]
     else:
       bodyStream: Stream
-    getBody: bool ## When `false`, the body is never read in requestAux.
+    getBody: bool         ## When `false`, the body is never read in requestAux.
 
 type
   HttpClient* = HttpClientBase[Socket]
 
-proc newHttpClient*(userAgent = defUserAgent,
-    maxRedirects = 5, sslContext = getDefaultSSL(), proxy: Proxy = nil,
-    timeout = -1): HttpClient =
+proc newHttpClient*(userAgent = defUserAgent, maxRedirects = 5,
+                    sslContext = getDefaultSSL(), proxy: Proxy = nil,
+                    timeout = -1, headers = newHttpHeaders()): HttpClient =
   ## Creates a new HttpClient instance.
   ##
-  ## ``userAgent`` specifies the user agent that will be used when making
+  ## `userAgent` specifies the user agent that will be used when making
   ## requests.
   ##
-  ## ``maxRedirects`` specifies the maximum amount of redirects to follow,
+  ## `maxRedirects` specifies the maximum amount of redirects to follow,
   ## default is 5.
   ##
-  ## ``sslContext`` specifies the SSL context to use for HTTPS requests.
+  ## `sslContext` specifies the SSL context to use for HTTPS requests.
+  ## See `SSL/TLS support <#sslslashtls-support>`_
   ##
-  ## ``proxy`` specifies an HTTP proxy to use for this HTTP client's
+  ## `proxy` specifies an HTTP proxy to use for this HTTP client's
   ## connections.
   ##
-  ## ``timeout`` specifies the number of milliseconds to allow before a
-  ## ``TimeoutError`` is raised.
+  ## `timeout` specifies the number of milliseconds to allow before a
+  ## `TimeoutError` is raised.
+  ##
+  ## `headers` specifies the HTTP Headers.
+  runnableExamples:
+    import std/strutils
+
+    let exampleHtml = newHttpClient().getContent("http://example.com")
+    assert "Example Domain" in exampleHtml
+    assert "Pizza" notin exampleHtml
+
   new result
-  result.headers = newHttpHeaders()
+  result.headers = headers
   result.userAgent = userAgent
   result.maxRedirects = maxRedirects
   result.proxy = proxy
@@ -847,25 +659,36 @@ proc newHttpClient*(userAgent = defUserAgent,
 type
   AsyncHttpClient* = HttpClientBase[AsyncSocket]
 
-{.deprecated: [PAsyncHttpClient: AsyncHttpClient].}
-
-proc newAsyncHttpClient*(userAgent = defUserAgent,
-    maxRedirects = 5, sslContext = getDefaultSSL(),
-    proxy: Proxy = nil): AsyncHttpClient =
+proc newAsyncHttpClient*(userAgent = defUserAgent, maxRedirects = 5,
+                         sslContext = getDefaultSSL(), proxy: Proxy = nil,
+                         headers = newHttpHeaders()): AsyncHttpClient =
   ## Creates a new AsyncHttpClient instance.
   ##
-  ## ``userAgent`` specifies the user agent that will be used when making
+  ## `userAgent` specifies the user agent that will be used when making
   ## requests.
   ##
-  ## ``maxRedirects`` specifies the maximum amount of redirects to follow,
+  ## `maxRedirects` specifies the maximum amount of redirects to follow,
   ## default is 5.
   ##
-  ## ``sslContext`` specifies the SSL context to use for HTTPS requests.
+  ## `sslContext` specifies the SSL context to use for HTTPS requests.
   ##
-  ## ``proxy`` specifies an HTTP proxy to use for this HTTP client's
+  ## `proxy` specifies an HTTP proxy to use for this HTTP client's
   ## connections.
+  ##
+  ## `headers` specifies the HTTP Headers.
+  runnableExamples:
+    import std/[asyncdispatch, strutils]
+
+    proc asyncProc(): Future[string] {.async.} =
+      let client = newAsyncHttpClient()
+      result = await client.getContent("http://example.com")
+
+    let exampleHtml = waitFor asyncProc()
+    assert "Example Domain" in exampleHtml
+    assert "Pizza" notin exampleHtml
+  
   new result
-  result.headers = newHttpHeaders()
+  result.headers = headers
   result.userAgent = userAgent
   result.maxRedirects = maxRedirects
   result.proxy = proxy
@@ -882,17 +705,32 @@ proc close*(client: HttpClient | AsyncHttpClient) =
     client.socket.close()
     client.connected = false
 
+proc getSocket*(client: HttpClient): Socket {.inline.} =
+  ## Get network socket, useful if you want to find out more details about the connection.
+  ##
+  ## This example shows info about local and remote endpoints:
+  ##
+  ##   ```Nim
+  ##   if client.connected:
+  ##     echo client.getSocket.getLocalAddr
+  ##     echo client.getSocket.getPeerAddr
+  ##   ```
+  return client.socket
+
+proc getSocket*(client: AsyncHttpClient): AsyncSocket {.inline.} =
+  return client.socket
+
 proc reportProgress(client: HttpClient | AsyncHttpClient,
                     progress: BiggestInt) {.multisync.} =
   client.contentProgress += progress
   client.oneSecondProgress += progress
-  if epochTime() - client.lastProgressReport >= 1.0:
+  if (getMonoTime() - client.lastProgressReport).inSeconds >= 1:
     if not client.onProgressChanged.isNil:
       await client.onProgressChanged(client.contentTotal,
                                      client.contentProgress,
                                      client.oneSecondProgress)
       client.oneSecondProgress = 0
-      client.lastProgressReport = epochTime()
+      client.lastProgressReport = getMonoTime()
 
 proc recvFull(client: HttpClient | AsyncHttpClient, size: int, timeout: int,
               keep: bool): Future[int] {.multisync.} =
@@ -957,14 +795,13 @@ proc parseChunks(client: HttpClient | AsyncHttpClient): Future[void]
     # Trailer headers will only be sent if the request specifies that we want
     # them: http://tools.ietf.org/html/rfc2616#section-3.6.1
 
-proc parseBody(client: HttpClient | AsyncHttpClient,
-               headers: HttpHeaders,
+proc parseBody(client: HttpClient | AsyncHttpClient, headers: HttpHeaders,
                httpVersion: string): Future[void] {.multisync.} =
   # Reset progress from previous requests.
   client.contentTotal = 0
   client.contentProgress = 0
   client.oneSecondProgress = 0
-  client.lastProgressReport = 0
+  client.lastProgressReport = MonoTime()
 
   when client is AsyncHttpClient:
     assert(not client.bodyStream.finished)
@@ -976,7 +813,7 @@ proc parseBody(client: HttpClient | AsyncHttpClient,
     # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.3
     var contentLengthHeader = headers.getOrDefault"Content-Length"
     if contentLengthHeader != "":
-      var length = contentLengthHeader.parseint()
+      var length = contentLengthHeader.parseInt()
       client.contentTotal = length
       if length > 0:
         let recvLen = await client.recvFull(length, client.timeout, true)
@@ -985,13 +822,17 @@ proc parseBody(client: HttpClient | AsyncHttpClient,
           httpError("Got disconnected while trying to read body.")
         if recvLen != length:
           httpError("Received length doesn't match expected length. Wanted " &
-                    $length & " got " & $recvLen)
+                    $length & " got: " & $recvLen)
     else:
       # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.4 TODO
 
       # -REGION- Connection: Close
       # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.5
-      if headers.getOrDefault"Connection" == "close" or httpVersion == "1.0":
+      let implicitConnectionClose =
+        httpVersion == "1.0" or
+        # This doesn't match the HTTP spec, but it fixes issues for non-conforming servers.
+        (httpVersion == "1.1" and headers.getOrDefault"Connection" == "")
+      if headers.getOrDefault"Connection" == "close" or implicitConnectionClose:
         while true:
           let recvLen = await client.recvFull(4000, client.timeout, true)
           if recvLen != 4000:
@@ -1015,6 +856,7 @@ proc parseResponse(client: HttpClient | AsyncHttpClient,
   var parsedStatus = false
   var linei = 0
   var fullyRead = false
+  var lastHeaderName = ""
   var line = ""
   result.headers = newHttpHeaders()
   while true:
@@ -1027,14 +869,14 @@ proc parseResponse(client: HttpClient | AsyncHttpClient,
       # We've been disconnected.
       client.close()
       break
-    if line == "\c\L":
+    if line == httpNewLine:
       fullyRead = true
       break
     if not parsedStatus:
       # Parse HTTP version info and status code.
       var le = skipIgnoreCase(line, "HTTP/", linei)
       if le <= 0:
-        httpError("invalid http version, " & line.repr)
+        httpError("invalid http version, `" & line & "`")
       inc(linei, le)
       le = skipIgnoreCase(line, "1.1", linei)
       if le > 0: result.version = "1.1"
@@ -1049,27 +891,52 @@ proc parseResponse(client: HttpClient | AsyncHttpClient,
       parsedStatus = true
     else:
       # Parse headers
-      var name = ""
-      var le = parseUntil(line, name, ':', linei)
-      if le <= 0: httpError("invalid headers")
-      inc(linei, le)
-      if line[linei] != ':': httpError("invalid headers")
-      inc(linei) # Skip :
-
-      result.headers.add(name, line[linei.. ^1].strip())
-      if result.headers.len > headerLimit:
-        httpError("too many headers")
+      # There's at least one char because empty lines are handled above (with client.close)
+      if line[0] in {' ', '\t'}:
+        # Check if it's a multiline header value, if so, append to the header we're currently parsing
+        # This works because a line with a header must start with the header name without any leading space
+        # See https://datatracker.ietf.org/doc/html/rfc7230, section 3.2 and 3.2.4
+        # Multiline headers are deprecated in the spec, but it's better to parse them than crash
+        if lastHeaderName == "":
+          # Some extra unparsable lines in the HTTP output - we ignore them
+          discard
+        else:
+          result.headers.table[result.headers.toCaseInsensitive(lastHeaderName)][^1].add "\n" & line
+      else:
+        var name = ""
+        var le = parseUntil(line, name, ':', linei)
+        if le <= 0: httpError("Invalid headers - received empty header name")
+        if line.len == le: httpError("Invalid headers - no colon after header name")
+        inc(linei, le) # Skip the parsed header name
+        inc(linei) # Skip :
+        # If we want to be HTTP spec compliant later, error on linei == line.len (for empty header value)
+        lastHeaderName = name # Remember the header name for the possible multi-line header
+        result.headers.add(name, line[linei .. ^1].strip())
+        if result.headers.len > headerLimit:
+          httpError("too many headers")
 
   if not fullyRead:
     httpError("Connection was closed before full request has been made")
 
-  if getBody:
+  when client is HttpClient:
+    result.bodyStream = newStringStream()
+  else:
+    result.bodyStream = newFutureStream[string]("parseResponse")
+
+  if getBody and result.code != Http204:
+    client.bodyStream = result.bodyStream
     when client is HttpClient:
-      client.bodyStream = newStringStream()
+      parseBody(client, result.headers, result.version)
     else:
-      client.bodyStream = newFutureStream[string]("parseResponse")
-    await parseBody(client, result.headers, result.version)
-    result.bodyStream = client.bodyStream
+      assert(client.parseBodyFut.isNil or client.parseBodyFut.finished)
+      # do not wait here for the body request to complete
+      client.parseBodyFut = parseBody(client, result.headers, result.version)
+      client.parseBodyFut.addCallback do():
+        if client.parseBodyFut.failed:
+          client.bodyStream.fail(client.parseBodyFut.error)
+  else:
+    when client is AsyncHttpClient:
+      result.bodyStream.complete()
 
 proc newConnection(client: HttpClient | AsyncHttpClient,
                    url: Uri) {.multisync.} =
@@ -1123,7 +990,8 @@ proc newConnection(client: HttpClient | AsyncHttpClient,
         connectUrl.hostname = url.hostname
         connectUrl.port = if url.port != "": url.port else: "443"
 
-        let proxyHeaderString = generateHeaders(connectUrl, $HttpConnect, newHttpHeaders(), "", client.proxy)
+        let proxyHeaderString = generateHeaders(connectUrl, HttpConnect,
+            newHttpHeaders(), client.proxy)
         await client.socket.send(proxyHeaderString)
         let proxyResp = await parseResponse(client, false)
 
@@ -1141,157 +1009,315 @@ proc newConnection(client: HttpClient | AsyncHttpClient,
     client.currentURL = url
     client.connected = true
 
+proc readFileSizes(client: HttpClient | AsyncHttpClient,
+                   multipart: MultipartData) {.multisync.} =
+  for entry in multipart.content.mitems():
+    if not entry.isFile: continue
+    if not entry.isStream:
+      entry.fileSize = entry.content.len
+      continue
+
+    # TODO: look into making getFileSize work with async
+    let fileSize = getFileSize(entry.content)
+    entry.fileSize = fileSize
+
+proc format(entry: MultipartEntry, boundary: string): string =
+  result = "--" & boundary & httpNewLine
+  result.add("Content-Disposition: form-data; name=\"" & entry.name & "\"")
+  if entry.isFile:
+    result.add("; filename=\"" & entry.filename & "\"" & httpNewLine)
+    result.add("Content-Type: " & entry.contentType & httpNewLine)
+  else:
+    result.add(httpNewLine & httpNewLine & entry.content)
+
+proc format(client: HttpClient | AsyncHttpClient,
+            multipart: MultipartData): Future[seq[string]] {.multisync.} =
+  let bound = getBoundary(multipart)
+  client.headers["Content-Type"] = "multipart/form-data; boundary=" & bound
+
+  await client.readFileSizes(multipart)
+
+  var length: int64
+  for entry in multipart.content:
+    result.add(format(entry, bound) & httpNewLine)
+    if entry.isFile:
+      length += entry.fileSize + httpNewLine.len
+
+  result.add "--" & bound & "--" & httpNewLine
+
+  for s in result: length += s.len
+  client.headers["Content-Length"] = $length
+
 proc override(fallback, override: HttpHeaders): HttpHeaders =
   # Right-biased map union for `HttpHeaders`
-  if override.isNil:
-    return fallback
 
   result = newHttpHeaders()
   # Copy by value
   result.table[] = fallback.table[]
+
+  if override.isNil:
+    # Return the copy of fallback so it does not get modified
+    return result
+
   for k, vs in override.table:
     result[k] = vs
 
-proc requestAux(client: HttpClient | AsyncHttpClient, url: string,
-                httpMethod: string, body = "",
-                headers: HttpHeaders = nil): Future[Response | AsyncResponse]
+proc requestAux(client: HttpClient | AsyncHttpClient, url: Uri,
+                httpMethod: HttpMethod, body = "", headers: HttpHeaders = nil,
+                multipart: MultipartData = nil): Future[Response | AsyncResponse]
                 {.multisync.} =
   # Helper that actually makes the request. Does not handle redirects.
-  let requestUrl = parseUri(url)
+  if url.scheme == "":
+    raise newException(ValueError, "No uri scheme supplied.")
 
-  await newConnection(client, requestUrl)
-
-  let effectiveHeaders = client.headers.override(headers)
+  when client is AsyncHttpClient:
+    if not client.parseBodyFut.isNil:
+      # let the current operation finish before making another request
+      await client.parseBodyFut
+      client.parseBodyFut = nil
 
-  if not effectiveHeaders.hasKey("user-agent") and client.userAgent != "":
-    effectiveHeaders["User-Agent"] = client.userAgent
+  await newConnection(client, url)
 
-  var headersString = generateHeaders(requestUrl, httpMethod,
-                                      effectiveHeaders, body, client.proxy)
+  var newHeaders: HttpHeaders
 
-  await client.socket.send(headersString)
-  if body != "":
+  var data: seq[string]
+  if multipart != nil and multipart.content.len > 0:
+    # `format` modifies `client.headers`, see 
+    # https://github.com/nim-lang/Nim/pull/18208#discussion_r647036979
+    data = await client.format(multipart)
+    newHeaders = client.headers.override(headers)
+  else:
+    newHeaders = client.headers.override(headers)
+    # Only change headers if they have not been specified already
+    if not newHeaders.hasKey("Content-Length"):
+      if body.len != 0:
+        newHeaders["Content-Length"] = $body.len
+      elif httpMethod notin {HttpGet, HttpHead}:
+        newHeaders["Content-Length"] = "0"
+
+  if not newHeaders.hasKey("user-agent") and client.userAgent.len > 0:
+    newHeaders["User-Agent"] = client.userAgent
+
+  let headerString = generateHeaders(url, httpMethod, newHeaders,
+                                     client.proxy)
+  await client.socket.send(headerString)
+
+  if data.len > 0:
+    var buffer: string
+    for i, entry in multipart.content:
+      buffer.add data[i]
+      if not entry.isFile: continue
+      if buffer.len > 0:
+        await client.socket.send(buffer)
+        buffer.setLen(0)
+      if entry.isStream:
+        await client.socket.sendFile(entry)
+      else:
+        await client.socket.send(entry.content)
+      buffer.add httpNewLine
+    # send the rest and the last boundary
+    await client.socket.send(buffer & data[^1])
+  elif body.len > 0:
     await client.socket.send(body)
 
-  let getBody = httpMethod.toLowerAscii() notin ["head", "connect"] and
+  let getBody = httpMethod notin {HttpHead, HttpConnect} and
                 client.getBody
   result = await parseResponse(client, getBody)
 
-proc request*(client: HttpClient | AsyncHttpClient, url: string,
-              httpMethod: string, body = "",
-              headers: HttpHeaders = nil): Future[Response | AsyncResponse]
+proc request*(client: HttpClient | AsyncHttpClient, url: Uri | string,
+              httpMethod: HttpMethod | string = HttpGet, body = "",
+              headers: HttpHeaders = nil,
+              multipart: MultipartData = nil): Future[Response | AsyncResponse]
               {.multisync.} =
   ## Connects to the hostname specified by the URL and performs a request
-  ## using the custom method string specified by ``httpMethod``.
+  ## using the custom method string specified by `httpMethod`.
   ##
-  ## Connection will be kept alive. Further requests on the same ``client`` to
+  ## Connection will be kept alive. Further requests on the same `client` to
   ## the same hostname will not require a new connection to be made. The
-  ## connection can be closed by using the ``close`` procedure.
+  ## connection can be closed by using the `close` procedure.
   ##
   ## This procedure will follow redirects up to a maximum number of redirects
-  ## specified in ``client.maxRedirects``.
-  result = await client.requestAux(url, httpMethod, body, headers)
+  ## specified in `client.maxRedirects`.
+  ##
+  ## You need to make sure that the `url` doesn't contain any newline
+  ## characters. Failing to do so will raise `AssertionDefect`.
+  ##
+  ## `headers` are HTTP headers that override the `client.headers` for
+  ## this specific request only and will not be persisted.
+  ##
+  ## **Deprecated since v1.5**: use HttpMethod enum instead; string parameter httpMethod is deprecated
+  when url is string:
+    doAssert(not url.contains({'\c', '\L'}), "url shouldn't contain any newline characters")
+    let url = parseUri(url)
+
+  when httpMethod is string:
+    {.warning:
+       "Deprecated since v1.5; use HttpMethod enum instead; string parameter httpMethod is deprecated".}
+    let httpMethod = case httpMethod
+      of "HEAD":
+        HttpHead
+      of "GET":
+        HttpGet
+      of "POST":
+        HttpPost
+      of "PUT":
+        HttpPut
+      of "DELETE":
+        HttpDelete
+      of "TRACE":
+        HttpTrace
+      of "OPTIONS":
+        HttpOptions
+      of "CONNECT":
+        HttpConnect
+      of "PATCH":
+        HttpPatch
+      else:
+        raise newException(ValueError, "Invalid HTTP method name: " & httpMethod)
+
+  result = await client.requestAux(url, httpMethod, body, headers, multipart)
 
   var lastURL = url
   for i in 1..client.maxRedirects:
-    if result.status.redirection():
-      let redirectTo = getNewLocation(lastURL, result.headers)
-      result = await client.requestAux(redirectTo, httpMethod, body, headers)
-      lastURL = redirectTo
+    let statusCode = result.code
 
+    if statusCode notin {Http301, Http302, Http303, Http307, Http308}:
+      break
 
-proc request*(client: HttpClient | AsyncHttpClient, url: string,
-              httpMethod = HttpGET, body = "",
-              headers: HttpHeaders = nil): Future[Response | AsyncResponse]
-              {.multisync.} =
-  ## Connects to the hostname specified by the URL and performs a request
-  ## using the method specified.
+    let redirectTo = getNewLocation(lastURL, result.headers)
+    var redirectMethod: HttpMethod
+    var redirectBody: string
+    # For more informations about the redirect methods see:
+    # https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
+    case statusCode
+    of Http301, Http302, Http303:
+      # The method is changed to GET unless it is GET or HEAD (RFC2616)
+      if httpMethod notin {HttpGet, HttpHead}:
+        redirectMethod = HttpGet
+      else:
+        redirectMethod = httpMethod
+      # The body is stripped away
+      redirectBody = ""
+      # Delete any header value associated with the body
+      if not headers.isNil():
+        headers.del("Content-Length")
+        headers.del("Content-Type")
+        headers.del("Transfer-Encoding")
+    of Http307, Http308:
+      # The method and the body are unchanged
+      redirectMethod = httpMethod
+      redirectBody = body
+    else:
+      # Unreachable
+      doAssert(false)
+
+    # Check if the redirection is to the same domain or a sub-domain (foo.com
+    # -> sub.foo.com)
+    if redirectTo.hostname != lastURL.hostname and
+      not redirectTo.hostname.endsWith("." & lastURL.hostname):
+      # Perform some cleanup of the header values
+      if headers != nil:
+        # Delete the Host header
+        headers.del("Host")
+        # Do not send any sensitive info to a unknown host
+        headers.del("Authorization")
+
+    result = await client.requestAux(redirectTo, redirectMethod, redirectBody,
+                                     headers, multipart)
+    lastURL = redirectTo
+
+proc responseContent(resp: Response | AsyncResponse): Future[string] {.multisync.} =
+  ## Returns the content of a response as a string.
   ##
-  ## Connection will be kept alive. Further requests on the same ``client`` to
-  ## the same hostname will not require a new connection to be made. The
-  ## connection can be closed by using the ``close`` procedure.
+  ## A `HttpRequestError` will be raised if the server responds with a
+  ## client error (status code 4xx) or a server error (status code 5xx).
+  if resp.code.is4xx or resp.code.is5xx:
+    raise newException(HttpRequestError, resp.status.move)
+  else:
+    return await resp.bodyStream.readAll()
+
+proc head*(client: HttpClient | AsyncHttpClient,
+          url: Uri | string): Future[Response | AsyncResponse] {.multisync.} =
+  ## Connects to the hostname specified by the URL and performs a HEAD request.
   ##
-  ## When a request is made to a different hostname, the current connection will
-  ## be closed.
-  result = await request(client, url, $httpMethod, body, headers)
+  ## This procedure uses httpClient values such as `client.maxRedirects`.
+  result = await client.request(url, HttpHead)
 
 proc get*(client: HttpClient | AsyncHttpClient,
-          url: string): Future[Response | AsyncResponse] {.multisync.} =
+          url: Uri | string): Future[Response | AsyncResponse] {.multisync.} =
   ## Connects to the hostname specified by the URL and performs a GET request.
   ##
-  ## This procedure will follow redirects up to a maximum number of redirects
-  ## specified in ``client.maxRedirects``.
-  result = await client.request(url, HttpGET)
+  ## This procedure uses httpClient values such as `client.maxRedirects`.
+  result = await client.request(url, HttpGet)
 
 proc getContent*(client: HttpClient | AsyncHttpClient,
-                 url: string): Future[string] {.multisync.} =
-  ## Connects to the hostname specified by the URL and performs a GET request.
-  ##
-  ## This procedure will follow redirects up to a maximum number of redirects
-  ## specified in ``client.maxRedirects``.
-  ##
-  ## A ``HttpRequestError`` will be raised if the server responds with a
-  ## client error (status code 4xx) or a server error (status code 5xx).
+                 url: Uri | string): Future[string] {.multisync.} =
+  ## Connects to the hostname specified by the URL and returns the content of a GET request.
   let resp = await get(client, url)
-  if resp.code.is4xx or resp.code.is5xx:
-    raise newException(HttpRequestError, resp.status)
-  else:
-    return await resp.bodyStream.readAll()
+  return await responseContent(resp)
 
-proc post*(client: HttpClient | AsyncHttpClient, url: string, body = "",
+proc delete*(client: HttpClient | AsyncHttpClient,
+             url: Uri | string): Future[Response | AsyncResponse] {.multisync.} =
+  ## Connects to the hostname specified by the URL and performs a DELETE request.
+  ## This procedure uses httpClient values such as `client.maxRedirects`.
+  result = await client.request(url, HttpDelete)
+
+proc deleteContent*(client: HttpClient | AsyncHttpClient,
+                    url: Uri | string): Future[string] {.multisync.} =
+  ## Connects to the hostname specified by the URL and returns the content of a DELETE request.
+  let resp = await delete(client, url)
+  return await responseContent(resp)
+
+proc post*(client: HttpClient | AsyncHttpClient, url: Uri | string, body = "",
            multipart: MultipartData = nil): Future[Response | AsyncResponse]
            {.multisync.} =
   ## Connects to the hostname specified by the URL and performs a POST request.
-  ##
-  ## This procedure will follow redirects up to a maximum number of redirects
-  ## specified in ``client.maxRedirects``.
-  let (mpContentType, mpBody) = format(multipart)
-  # TODO: Support FutureStream for `body` parameter.
-  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 headers = newHttpHeaders()
-  if multipart != nil:
-    headers["Content-Type"] = mpContentType
-  headers["Content-Length"] = $len(xb)
+  ## This procedure uses httpClient values such as `client.maxRedirects`.
+  result = await client.request(url, HttpPost, body, multipart=multipart)
 
-  result = await client.requestAux(url, $HttpPOST, xb, headers)
-  # Handle redirects.
-  var lastURL = url
-  for i in 1..client.maxRedirects:
-    if result.status.redirection():
-      let redirectTo = getNewLocation(lastURL, result.headers)
-      var meth = if result.status != "307": HttpGet else: HttpPost
-      result = await client.requestAux(redirectTo, $meth, xb, headers)
-      lastURL = redirectTo
-
-proc postContent*(client: HttpClient | AsyncHttpClient, url: string,
-                  body = "",
+proc postContent*(client: HttpClient | AsyncHttpClient, url: Uri | string, body = "",
                   multipart: MultipartData = nil): Future[string]
                   {.multisync.} =
-  ## Connects to the hostname specified by the URL and performs a POST request.
-  ##
-  ## This procedure will follow redirects up to a maximum number of redirects
-  ## specified in ``client.maxRedirects``.
-  ##
-  ## A ``HttpRequestError`` will be raised if the server responds with a
-  ## client error (status code 4xx) or a server error (status code 5xx).
+  ## Connects to the hostname specified by the URL and returns the content of a POST request.
   let resp = await post(client, url, body, multipart)
-  if resp.code.is4xx or resp.code.is5xx:
-    raise newException(HttpRequestError, resp.status)
-  else:
-    return await resp.bodyStream.readAll()
+  return await responseContent(resp)
+
+proc put*(client: HttpClient | AsyncHttpClient, url: Uri | string, body = "",
+          multipart: MultipartData = nil): Future[Response | AsyncResponse]
+          {.multisync.} =
+  ## Connects to the hostname specified by the URL and performs a PUT request.
+  ## This procedure uses httpClient values such as `client.maxRedirects`.
+  result = await client.request(url, HttpPut, body, multipart=multipart)
+
+proc putContent*(client: HttpClient | AsyncHttpClient, url: Uri | string, body = "",
+                 multipart: MultipartData = nil): Future[string] {.multisync.} =
+  ## Connects to the hostname specified by the URL andreturns the content of a PUT request.
+  let resp = await put(client, url, body, multipart)
+  return await responseContent(resp)
+
+proc patch*(client: HttpClient | AsyncHttpClient, url: Uri | string, body = "",
+            multipart: MultipartData = nil): Future[Response | AsyncResponse]
+            {.multisync.} =
+  ## Connects to the hostname specified by the URL and performs a PATCH request.
+  ## This procedure uses httpClient values such as `client.maxRedirects`.
+  result = await client.request(url, HttpPatch, body, multipart=multipart)
+
+proc patchContent*(client: HttpClient | AsyncHttpClient, url: Uri | string, body = "",
+                   multipart: MultipartData = nil): Future[string]
+                  {.multisync.} =
+  ## Connects to the hostname specified by the URL and returns the content of a PATCH request.
+  let resp = await patch(client, url, body, multipart)
+  return await responseContent(resp)
 
-proc downloadFile*(client: HttpClient, url: string, filename: string) =
-  ## Downloads ``url`` and saves it to ``filename``.
+proc downloadFile*(client: HttpClient, url: Uri | string, filename: string) =
+  ## Downloads `url` and saves it to `filename`.
   client.getBody = false
   defer:
     client.getBody = true
   let resp = client.get(url)
+  
+  if resp.code.is4xx or resp.code.is5xx:
+    raise newException(HttpRequestError, resp.status)
 
   client.bodyStream = newFileStream(filename, fmWrite)
   if client.bodyStream.isNil:
@@ -1299,30 +1325,30 @@ proc downloadFile*(client: HttpClient, url: string, filename: string) =
   parseBody(client, resp.headers, resp.version)
   client.bodyStream.close()
 
+proc downloadFileEx(client: AsyncHttpClient,
+                    url: Uri | string, filename: string): Future[void] {.async.} =
+  ## Downloads `url` and saves it to `filename`.
+  client.getBody = false
+  let resp = await client.get(url)
+  
   if resp.code.is4xx or resp.code.is5xx:
     raise newException(HttpRequestError, resp.status)
 
-proc downloadFile*(client: AsyncHttpClient, url: string,
+  client.bodyStream = newFutureStream[string]("downloadFile")
+  var file = openAsync(filename, fmWrite)
+  defer: file.close()
+  # Let `parseBody` write response data into client.bodyStream in the
+  # background.
+  let parseBodyFut = parseBody(client, resp.headers, resp.version)
+  parseBodyFut.addCallback do():
+    if parseBodyFut.failed:
+      client.bodyStream.fail(parseBodyFut.error)
+  # The `writeFromStream` proc will complete once all the data in the
+  # `bodyStream` has been written to the file.
+  await file.writeFromStream(client.bodyStream)
+
+proc downloadFile*(client: AsyncHttpClient, url: Uri | string,
                    filename: string): Future[void] =
-  proc downloadFileEx(client: AsyncHttpClient,
-                      url, filename: string): Future[void] {.async.} =
-    ## Downloads ``url`` and saves it to ``filename``.
-    client.getBody = false
-    let resp = await client.get(url)
-
-    client.bodyStream = newFutureStream[string]("downloadFile")
-    var file = openAsync(filename, fmWrite)
-    # Let `parseBody` write response data into client.bodyStream in the
-    # background.
-    asyncCheck parseBody(client, resp.headers, resp.version)
-    # The `writeFromStream` proc will complete once all the data in the
-    # `bodyStream` has been written to the file.
-    await file.writeFromStream(client.bodyStream)
-    file.close()
-
-    if resp.code.is4xx or resp.code.is5xx:
-      raise newException(HttpRequestError, resp.status)
-
   result = newFuture[void]("downloadFile")
   try:
     result = downloadFileEx(client, url, filename)
diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim
index f85375111..5ccab379c 100644
--- a/lib/pure/httpcore.nim
+++ b/lib/pure/httpcore.nim
@@ -7,14 +7,17 @@
 #    distribution, for details about the copyright.
 #
 
-## Contains functionality shared between the ``httpclient`` and
-## ``asynchttpserver`` modules.
-
-import tables, strutils, parseutils
+## Contains functionality shared between the `httpclient` and
+## `asynchttpserver` modules.
+##
+## Unstable API.
+import std/private/since
+import std/[tables, strutils, parseutils]
 
 type
   HttpHeaders* = ref object
     table*: TableRef[string, seq[string]]
+    isTitleCase: bool
 
   HttpHeaderValues* = distinct seq[string]
 
@@ -26,29 +29,33 @@ type
     HttpVer11,
     HttpVer10
 
-  HttpMethod* = enum  ## the requested HttpMethod
-    HttpHead,         ## Asks for the response identical to the one that would
-                      ## correspond to a GET request, but without the response
-                      ## body.
-    HttpGet,          ## Retrieves the specified resource.
-    HttpPost,         ## Submits data to be processed to the identified
-                      ## resource. The data is included in the body of the
-                      ## request.
-    HttpPut,          ## Uploads a representation of the specified resource.
-    HttpDelete,       ## Deletes the specified resource.
-    HttpTrace,        ## Echoes back the received request, so that a client
-                      ## can see what intermediate servers are adding or
-                      ## changing in the request.
-    HttpOptions,      ## Returns the HTTP methods that the server supports
-                      ## for specified address.
-    HttpConnect,      ## Converts the request connection to a transparent
-                      ## TCP/IP tunnel, usually used for proxies.
-    HttpPatch         ## Applies partial modifications to a resource.
+  HttpMethod* = enum         ## the requested HttpMethod
+    HttpHead = "HEAD"        ## Asks for the response identical to the one that
+                             ## would correspond to a GET request, but without
+                             ## the response body.
+    HttpGet = "GET"          ## Retrieves the specified resource.
+    HttpPost = "POST"        ## Submits data to be processed to the identified
+                             ## resource. The data is included in the body of
+                             ## the request.
+    HttpPut = "PUT"          ## Uploads a representation of the specified
+                             ## resource.
+    HttpDelete = "DELETE"    ## Deletes the specified resource.
+    HttpTrace = "TRACE"      ## Echoes back the received request, so that a
+                             ## client
+                             ## can see what intermediate servers are adding or
+                             ## changing in the request.
+    HttpOptions = "OPTIONS"  ## Returns the HTTP methods that the server
+                             ## supports for specified address.
+    HttpConnect = "CONNECT"  ## Converts the request connection to a transparent
+                             ## TCP/IP tunnel, usually used for proxies.
+    HttpPatch = "PATCH"      ## Applies partial modifications to a resource.
 
 
 const
   Http100* = HttpCode(100)
   Http101* = HttpCode(101)
+  Http102* = HttpCode(102)  ## https://tools.ietf.org/html/rfc2518.html WebDAV
+  Http103* = HttpCode(103)  ## https://tools.ietf.org/html/rfc8297.html Early hints
   Http200* = HttpCode(200)
   Http201* = HttpCode(201)
   Http202* = HttpCode(202)
@@ -56,6 +63,9 @@ const
   Http204* = HttpCode(204)
   Http205* = HttpCode(205)
   Http206* = HttpCode(206)
+  Http207* = HttpCode(207)  ## https://tools.ietf.org/html/rfc4918.html WebDAV
+  Http208* = HttpCode(208)  ## https://tools.ietf.org/html/rfc5842.html WebDAV, Section 7.1
+  Http226* = HttpCode(226)  ## https://tools.ietf.org/html/rfc3229.html Delta encoding, Section 10.4.1
   Http300* = HttpCode(300)
   Http301* = HttpCode(301)
   Http302* = HttpCode(302)
@@ -63,8 +73,10 @@ const
   Http304* = HttpCode(304)
   Http305* = HttpCode(305)
   Http307* = HttpCode(307)
+  Http308* = HttpCode(308)
   Http400* = HttpCode(400)
   Http401* = HttpCode(401)
+  Http402* = HttpCode(402)  ## https://tools.ietf.org/html/rfc7231.html Payment required, Section 6.5.2
   Http403* = HttpCode(403)
   Http404* = HttpCode(404)
   Http405* = HttpCode(405)
@@ -83,6 +95,9 @@ const
   Http418* = HttpCode(418)
   Http421* = HttpCode(421)
   Http422* = HttpCode(422)
+  Http423* = HttpCode(423)  ## https://tools.ietf.org/html/rfc4918.html WebDAV, Section 11.3
+  Http424* = HttpCode(424)  ## https://tools.ietf.org/html/rfc4918.html WebDAV, Section 11.3
+  Http425* = HttpCode(425)  ## https://tools.ietf.org/html/rfc8470.html Early data
   Http426* = HttpCode(426)
   Http428* = HttpCode(428)
   Http429* = HttpCode(429)
@@ -94,68 +109,99 @@ const
   Http503* = HttpCode(503)
   Http504* = HttpCode(504)
   Http505* = HttpCode(505)
+  Http506* = HttpCode(506)  ## https://tools.ietf.org/html/rfc2295.html Content negotiation, Section 8.1
+  Http507* = HttpCode(507)  ## https://tools.ietf.org/html/rfc4918.html WebDAV, Section 11.5
+  Http508* = HttpCode(508)  ## https://tools.ietf.org/html/rfc5842.html WebDAV, Section 7.2
+  Http510* = HttpCode(510)  ## https://tools.ietf.org/html/rfc2774.html Extension framework, Section 7
+  Http511* = HttpCode(511)  ## https://tools.ietf.org/html/rfc6585.html Additional status code, Section 6
+
 
+const httpNewLine* = "\c\L"
 const headerLimit* = 10_000
 
-proc newHttpHeaders*(): HttpHeaders =
-  new result
-  result.table = newTable[string, seq[string]]()
+func toTitleCase(s: string): string =
+  result = newString(len(s))
+  var upper = true
+  for i in 0..len(s) - 1:
+    result[i] = if upper: toUpperAscii(s[i]) else: toLowerAscii(s[i])
+    upper = s[i] == '-'
+
+func toCaseInsensitive*(headers: HttpHeaders, s: string): string {.inline.} =
+  ## For internal usage only. Do not use.
+  return if headers.isTitleCase: toTitleCase(s) else: toLowerAscii(s)
+
+func newHttpHeaders*(titleCase=false): HttpHeaders =
+  ## Returns a new `HttpHeaders` object. if `titleCase` is set to true,
+  ## headers are passed to the server in title case (e.g. "Content-Length")
+  result = HttpHeaders(table: newTable[string, seq[string]](), isTitleCase: titleCase)
+
+func newHttpHeaders*(keyValuePairs:
+    openArray[tuple[key: string, val: string]], titleCase=false): HttpHeaders =
+  ## Returns a new `HttpHeaders` object from an array. if `titleCase` is set to true,
+  ## headers are passed to the server in title case (e.g. "Content-Length")
+  result = HttpHeaders(table: newTable[string, seq[string]](), isTitleCase: titleCase)
 
-proc newHttpHeaders*(keyValuePairs:
-    openarray[tuple[key: string, val: string]]): HttpHeaders =
-  var pairs: seq[tuple[key: string, val: seq[string]]] = @[]
   for pair in keyValuePairs:
-    pairs.add((pair.key.toLowerAscii(), @[pair.val]))
-  new result
-  result.table = newTable[string, seq[string]](pairs)
+    let key = result.toCaseInsensitive(pair.key)
+    {.cast(noSideEffect).}:
+      if key in result.table:
+        result.table[key].add(pair.val)
+      else:
+        result.table[key] = @[pair.val]
 
-proc `$`*(headers: HttpHeaders): string =
-  return $headers.table
+func `$`*(headers: HttpHeaders): string {.inline.} =
+  $headers.table
 
-proc clear*(headers: HttpHeaders) =
+proc clear*(headers: HttpHeaders) {.inline.} =
   headers.table.clear()
 
-proc `[]`*(headers: HttpHeaders, key: string): HttpHeaderValues =
-  ## Returns the values associated with the given ``key``. If the returned
-  ## values are passed to a procedure expecting a ``string``, the first
+func `[]`*(headers: HttpHeaders, key: string): HttpHeaderValues =
+  ## Returns the values associated with the given `key`. If the returned
+  ## values are passed to a procedure expecting a `string`, the first
   ## value is automatically picked. If there are
   ## no values associated with the key, an exception is raised.
   ##
-  ## To access multiple values of a key, use the overloaded ``[]`` below or
-  ## to get all of them access the ``table`` field directly.
-  return headers.table[key.toLowerAscii].HttpHeaderValues
+  ## To access multiple values of a key, use the overloaded `[]` below or
+  ## to get all of them access the `table` field directly.
+  {.cast(noSideEffect).}:
+    let tmp = headers.table[headers.toCaseInsensitive(key)]
+    return HttpHeaderValues(tmp)
 
 converter toString*(values: HttpHeaderValues): string =
   return seq[string](values)[0]
 
-proc `[]`*(headers: HttpHeaders, key: string, i: int): string =
-  ## Returns the ``i``'th value associated with the given key. If there are
-  ## no values associated with the key or the ``i``'th value doesn't exist,
+func `[]`*(headers: HttpHeaders, key: string, i: int): string =
+  ## Returns the `i`'th value associated with the given key. If there are
+  ## no values associated with the key or the `i`'th value doesn't exist,
   ## an exception is raised.
-  return headers.table[key.toLowerAscii][i]
+  {.cast(noSideEffect).}:
+    return headers.table[headers.toCaseInsensitive(key)][i]
 
 proc `[]=`*(headers: HttpHeaders, key, value: string) =
-  ## Sets the header entries associated with ``key`` to the specified value.
+  ## Sets the header entries associated with `key` to the specified value.
   ## Replaces any existing values.
-  headers.table[key.toLowerAscii] = @[value]
+  headers.table[headers.toCaseInsensitive(key)] = @[value]
 
 proc `[]=`*(headers: HttpHeaders, key: string, value: seq[string]) =
-  ## Sets the header entries associated with ``key`` to the specified list of
-  ## values.
-  ## Replaces any existing values.
-  headers.table[key.toLowerAscii] = value
+  ## Sets the header entries associated with `key` to the specified list of
+  ## values. Replaces any existing values. If `value` is empty,
+  ## deletes the header entries associated with `key`.
+  if value.len > 0:
+    headers.table[headers.toCaseInsensitive(key)] = value
+  else:
+    headers.table.del(headers.toCaseInsensitive(key))
 
 proc add*(headers: HttpHeaders, key, value: string) =
   ## Adds the specified value to the specified key. Appends to any existing
   ## values associated with the key.
-  if not headers.table.hasKey(key.toLowerAscii):
-    headers.table[key.toLowerAscii] = @[value]
+  if not headers.table.hasKey(headers.toCaseInsensitive(key)):
+    headers.table[headers.toCaseInsensitive(key)] = @[value]
   else:
-    headers.table[key.toLowerAscii].add(value)
+    headers.table[headers.toCaseInsensitive(key)].add(value)
 
 proc del*(headers: HttpHeaders, key: string) =
-  ## Delete the header entries associated with ``key``
-  headers.table.del(key.toLowerAscii)
+  ## Deletes the header entries associated with `key`
+  headers.table.del(headers.toCaseInsensitive(key))
 
 iterator pairs*(headers: HttpHeaders): tuple[key, value: string] =
   ## Yields each key, value pair.
@@ -163,54 +209,57 @@ iterator pairs*(headers: HttpHeaders): tuple[key, value: string] =
     for value in v:
       yield (k, value)
 
-proc contains*(values: HttpHeaderValues, value: string): bool =
-  ## Determines if ``value`` is one of the values inside ``values``. Comparison
+func contains*(values: HttpHeaderValues, value: string): bool =
+  ## Determines if `value` is one of the values inside `values`. Comparison
   ## is performed without case sensitivity.
   for val in seq[string](values):
     if val.toLowerAscii == value.toLowerAscii: return true
 
-proc hasKey*(headers: HttpHeaders, key: string): bool =
-  return headers.table.hasKey(key.toLowerAscii())
+func hasKey*(headers: HttpHeaders, key: string): bool =
+  return headers.table.hasKey(headers.toCaseInsensitive(key))
 
-proc getOrDefault*(headers: HttpHeaders, key: string,
+func getOrDefault*(headers: HttpHeaders, key: string,
     default = @[""].HttpHeaderValues): HttpHeaderValues =
-  ## Returns the values associated with the given ``key``. If there are no
-  ## values associated with the key, then ``default`` is returned.
+  ## Returns the values associated with the given `key`. If there are no
+  ## values associated with the key, then `default` is returned.
   if headers.hasKey(key):
     return headers[key]
   else:
     return default
 
-proc len*(headers: HttpHeaders): int = return headers.table.len
+func len*(headers: HttpHeaders): int {.inline.} = headers.table.len
 
-proc parseList(line: string, list: var seq[string], start: int): int =
+func parseList(line: string, list: var seq[string], start: int): int =
   var i = 0
   var current = ""
   while start+i < line.len and line[start + i] notin {'\c', '\l'}:
     i += line.skipWhitespace(start + i)
     i += line.parseUntil(current, {'\c', '\l', ','}, start + i)
-    list.add(current)
+    list.add(move current)  # implicit current.setLen(0)
     if start+i < line.len and line[start + i] == ',':
       i.inc # Skip ,
-    current.setLen(0)
 
-proc parseHeader*(line: string): tuple[key: string, value: seq[string]] =
+func parseHeader*(line: string): tuple[key: string, value: seq[string]] =
   ## Parses a single raw header HTTP line into key value pairs.
   ##
-  ## Used by ``asynchttpserver`` and ``httpclient`` internally and should not
+  ## Used by `asynchttpserver` and `httpclient` internally and should not
   ## be used by you.
   result.value = @[]
   var i = 0
   i = line.parseUntil(result.key, ':')
   inc(i) # skip :
   if i < len(line):
-    i += parseList(line, result.value, i)
+    if cmpIgnoreCase(result.key, "cookie") == 0:
+      i += line.skipWhitespace(i)
+      result.value.add line.substr(i)
+    else:
+      i += parseList(line, result.value, i)
   elif result.key.len > 0:
     result.value = @[""]
   else:
     result.value = @[]
 
-proc `==`*(protocol: tuple[orig: string, major, minor: int],
+func `==`*(protocol: tuple[orig: string, major, minor: int],
            ver: HttpVersion): bool =
   let major =
     case ver
@@ -221,19 +270,18 @@ proc `==`*(protocol: tuple[orig: string, major, minor: int],
     of HttpVer10: 0
   result = protocol.major == major and protocol.minor == minor
 
-proc contains*(methods: set[HttpMethod], x: string): bool =
+func contains*(methods: set[HttpMethod], x: string): bool =
   return parseEnum[HttpMethod](x) in methods
 
-proc `$`*(code: HttpCode): string =
-  ## Converts the specified ``HttpCode`` into a HTTP status.
-  ##
-  ## For example:
-  ##
-  ##   .. code-block:: nim
-  ##       doAssert($Http404 == "404 Not Found")
+func `$`*(code: HttpCode): string =
+  ## Converts the specified `HttpCode` into a HTTP status.
+  runnableExamples:
+    doAssert($Http404 == "404 Not Found")
   case code.int
   of 100: "100 Continue"
   of 101: "101 Switching Protocols"
+  of 102: "102 Processing"
+  of 103: "103 Early Hints"
   of 200: "200 OK"
   of 201: "201 Created"
   of 202: "202 Accepted"
@@ -241,6 +289,9 @@ proc `$`*(code: HttpCode): string =
   of 204: "204 No Content"
   of 205: "205 Reset Content"
   of 206: "206 Partial Content"
+  of 207: "207 Multi-Status"
+  of 208: "208 Already Reported"
+  of 226: "226 IM Used"
   of 300: "300 Multiple Choices"
   of 301: "301 Moved Permanently"
   of 302: "302 Found"
@@ -248,8 +299,10 @@ proc `$`*(code: HttpCode): string =
   of 304: "304 Not Modified"
   of 305: "305 Use Proxy"
   of 307: "307 Temporary Redirect"
+  of 308: "308 Permanent Redirect"
   of 400: "400 Bad Request"
   of 401: "401 Unauthorized"
+  of 402: "402 Payment Required"
   of 403: "403 Forbidden"
   of 404: "404 Not Found"
   of 405: "405 Method Not Allowed"
@@ -268,6 +321,9 @@ proc `$`*(code: HttpCode): string =
   of 418: "418 I'm a teapot"
   of 421: "421 Misdirected Request"
   of 422: "422 Unprocessable Entity"
+  of 423: "423 Locked"
+  of 424: "424 Failed Dependency"
+  of 425: "425 Too Early"
   of 426: "426 Upgrade Required"
   of 428: "428 Precondition Required"
   of 429: "429 Too Many Requests"
@@ -279,46 +335,34 @@ proc `$`*(code: HttpCode): string =
   of 503: "503 Service Unavailable"
   of 504: "504 Gateway Timeout"
   of 505: "505 HTTP Version Not Supported"
+  of 506: "506 Variant Also Negotiates"
+  of 507: "507 Insufficient Storage"
+  of 508: "508 Loop Detected"
+  of 510: "510 Not Extended"
+  of 511: "511 Network Authentication Required"
   else: $(int(code))
 
-proc `==`*(a, b: HttpCode): bool {.borrow.}
-
-proc `==`*(rawCode: string, code: HttpCode): bool =
-  return cmpIgnoreCase(rawCode, $code) == 0
-
-proc is2xx*(code: HttpCode): bool =
-  ## Determines whether ``code`` is a 2xx HTTP status code.
-  return code.int in {200 .. 299}
-
-proc is3xx*(code: HttpCode): bool =
-  ## Determines whether ``code`` is a 3xx HTTP status code.
-  return code.int in {300 .. 399}
+func `==`*(a, b: HttpCode): bool {.borrow.}
 
-proc is4xx*(code: HttpCode): bool =
-  ## Determines whether ``code`` is a 4xx HTTP status code.
-  return code.int in {400 .. 499}
+func is1xx*(code: HttpCode): bool {.inline, since: (1, 5).} =
+  ## Determines whether `code` is a 1xx HTTP status code.
+  runnableExamples:
+    doAssert is1xx(HttpCode(103))
 
-proc is5xx*(code: HttpCode): bool =
-  ## Determines whether ``code`` is a 5xx HTTP status code.
-  return code.int in {500 .. 599}
+  code.int in 100 .. 199
 
-proc `$`*(httpMethod: HttpMethod): string =
-  return (system.`$`(httpMethod))[4 .. ^1].toUpperAscii()
+func is2xx*(code: HttpCode): bool {.inline.} =
+  ## Determines whether `code` is a 2xx HTTP status code.
+  code.int in 200 .. 299
 
-when isMainModule:
-  var test = newHttpHeaders()
-  test["Connection"] = @["Upgrade", "Close"]
-  doAssert test["Connection", 0] == "Upgrade"
-  doAssert test["Connection", 1] == "Close"
-  test.add("Connection", "Test")
-  doAssert test["Connection", 2] == "Test"
-  doAssert "upgrade" in test["Connection"]
+func is3xx*(code: HttpCode): bool {.inline.} =
+  ## Determines whether `code` is a 3xx HTTP status code.
+  code.int in 300 .. 399
 
-  # Bug #5344.
-  doAssert parseHeader("foobar: ") == ("foobar", @[""])
-  let (key, value) = parseHeader("foobar: ")
-  test = newHttpHeaders()
-  test[key] = value
-  doAssert test["foobar"] == ""
+func is4xx*(code: HttpCode): bool {.inline.} =
+  ## Determines whether `code` is a 4xx HTTP status code.
+  code.int in 400 .. 499
 
-  doAssert parseHeader("foobar:") == ("foobar", @[""])
\ No newline at end of file
+func is5xx*(code: HttpCode): bool {.inline.} =
+  ## Determines whether `code` is a 5xx HTTP status code.
+  code.int in 500 .. 599
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/asynccommon.nim b/lib/pure/includes/asynccommon.nim
deleted file mode 100644
index 06f4958c6..000000000
--- a/lib/pure/includes/asynccommon.nim
+++ /dev/null
@@ -1,211 +0,0 @@
-template createAsyncNativeSocketImpl(domain, sockType, protocol) =
-  let handle = newNativeSocket(domain, sockType, protocol)
-  if handle == osInvalidSocket:
-    return osInvalidSocket.AsyncFD
-  handle.setBlocking(false)
-  when defined(macosx) and not defined(nimdoc):
-    handle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
-  result = handle.AsyncFD
-  register(result)
-
-proc createAsyncNativeSocket*(domain: cint, sockType: cint,
-                           protocol: cint): AsyncFD =
-  createAsyncNativeSocketImpl(domain, sockType, protocol)
-
-proc createAsyncNativeSocket*(domain: Domain = Domain.AF_INET,
-                           sockType: SockType = SOCK_STREAM,
-                           protocol: Protocol = IPPROTO_TCP): AsyncFD =
-  createAsyncNativeSocketImpl(domain, sockType, protocol)
-
-proc newAsyncNativeSocket*(domain: cint, sockType: cint,
-                           protocol: cint): AsyncFD {.deprecated: "use createAsyncNativeSocket instead".} =
-  createAsyncNativeSocketImpl(domain, sockType, protocol)
-
-proc newAsyncNativeSocket*(domain: Domain = Domain.AF_INET,
-                           sockType: SockType = SOCK_STREAM,
-                           protocol: Protocol = IPPROTO_TCP): AsyncFD
-                           {.deprecated: "use createAsyncNativeSocket instead".} =
-  createAsyncNativeSocketImpl(domain, sockType, protocol)
-
-when defined(windows) or defined(nimdoc):
-  proc bindToDomain(handle: SocketHandle, domain: Domain) =
-    # Extracted into a separate proc, because connect() on Windows requires
-    # the socket to be initially bound.
-    template doBind(saddr) =
-      if bindAddr(handle, cast[ptr SockAddr](addr(saddr)),
-                  sizeof(saddr).SockLen) < 0'i32:
-        raiseOSError(osLastError())
-
-    if domain == Domain.AF_INET6:
-      var saddr: Sockaddr_in6
-      saddr.sin6_family = int16(toInt(domain))
-      doBind(saddr)
-    else:
-      var saddr: Sockaddr_in
-      saddr.sin_family = int16(toInt(domain))
-      doBind(saddr)
-
-  proc doConnect(socket: AsyncFD, addrInfo: ptr AddrInfo): Future[void] =
-    let retFuture = newFuture[void]("doConnect")
-    result = retFuture
-
-    var ol = PCustomOverlapped()
-    GC_ref(ol)
-    ol.data = CompletionData(fd: socket, cb:
-      proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
-        if not retFuture.finished:
-          if errcode == OSErrorCode(-1):
-            retFuture.complete()
-          else:
-            retFuture.fail(newException(OSError, osErrorMsg(errcode)))
-    )
-
-    let ret = connectEx(socket.SocketHandle, addrInfo.ai_addr,
-                        cint(addrInfo.ai_addrlen), nil, 0, nil,
-                        cast[POVERLAPPED](ol))
-    if ret:
-      # Request to connect completed immediately.
-      retFuture.complete()
-      # We don't deallocate ``ol`` here because even though this completed
-      # immediately poll will still be notified about its completion and it
-      # will free ``ol``.
-    else:
-      let lastError = osLastError()
-      if lastError.int32 != ERROR_IO_PENDING:
-        # With ERROR_IO_PENDING ``ol`` will be deallocated in ``poll``,
-        # and the future will be completed/failed there, too.
-        GC_unref(ol)
-        retFuture.fail(newException(OSError, osErrorMsg(lastError)))
-else:
-  proc doConnect(socket: AsyncFD, addrInfo: ptr AddrInfo): Future[void] =
-    let retFuture = newFuture[void]("doConnect")
-    result = retFuture
-
-    proc cb(fd: AsyncFD): bool =
-      let ret = SocketHandle(fd).getSockOptInt(
-        cint(SOL_SOCKET), cint(SO_ERROR))
-      if ret == 0:
-        # We have connected.
-        retFuture.complete()
-        return true
-      elif ret == EINTR:
-        # interrupted, keep waiting
-        return false
-      else:
-        retFuture.fail(newException(OSError, osErrorMsg(OSErrorCode(ret))))
-        return true
-
-    let ret = connect(socket.SocketHandle,
-                      addrInfo.ai_addr,
-                      addrInfo.ai_addrlen.Socklen)
-    if ret == 0:
-      # Request to connect completed immediately.
-      retFuture.complete()
-    else:
-      let lastError = osLastError()
-      if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
-        addWrite(socket, cb)
-      else:
-        retFuture.fail(newException(OSError, osErrorMsg(lastError)))
-
-template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped,
-                           protocol: Protocol = IPPROTO_RAW) =
-  ## Iterates through the AddrInfo linked list asynchronously
-  ## until the connection can be established.
-  const shouldCreateFd = not declared(fd)
-
-  when shouldCreateFd:
-    let sockType = protocol.toSockType()
-
-    var fdPerDomain: array[low(Domain).ord..high(Domain).ord, AsyncFD]
-    for i in low(fdPerDomain)..high(fdPerDomain):
-      fdPerDomain[i] = osInvalidSocket.AsyncFD
-    template closeUnusedFds(domainToKeep = -1) {.dirty.} =
-      for i, fd in fdPerDomain:
-        if fd != osInvalidSocket.AsyncFD and i != domainToKeep:
-          fd.closeSocket()
-
-  var lastException: ref Exception
-  var curAddrInfo = addrInfo
-  var domain: Domain
-  when shouldCreateFd:
-    var curFd: AsyncFD
-  else:
-    var curFd = fd
-  proc tryNextAddrInfo(fut: Future[void]) {.gcsafe.} =
-    if fut == nil or fut.failed:
-      if fut != nil:
-        lastException = fut.readError()
-
-      while curAddrInfo != nil:
-        let domainOpt = curAddrInfo.ai_family.toKnownDomain()
-        if domainOpt.isSome:
-          domain = domainOpt.unsafeGet()
-          break
-        curAddrInfo = curAddrInfo.ai_next
-
-      if curAddrInfo == nil:
-        freeAddrInfo(addrInfo)
-        when shouldCreateFd:
-          closeUnusedFds()
-        if lastException != nil:
-          retFuture.fail(lastException)
-        else:
-          retFuture.fail(newException(
-            IOError, "Couldn't resolve address: " & address))
-        return
-
-      when shouldCreateFd:
-        curFd = fdPerDomain[ord(domain)]
-        if curFd == osInvalidSocket.AsyncFD:
-          try:
-            curFd = newAsyncNativeSocket(domain, sockType, protocol)
-          except:
-            freeAddrInfo(addrInfo)
-            closeUnusedFds()
-            raise getCurrentException()
-          when defined(windows):
-            curFd.SocketHandle.bindToDomain(domain)
-          fdPerDomain[ord(domain)] = curFd
-
-      doConnect(curFd, curAddrInfo).callback = tryNextAddrInfo
-      curAddrInfo = curAddrInfo.ai_next
-    else:
-      freeAddrInfo(addrInfo)
-      when shouldCreateFd:
-        closeUnusedFds(ord(domain))
-        retFuture.complete(curFd)
-      else:
-        retFuture.complete()
-
-  tryNextAddrInfo(nil)
-
-proc dial*(address: string, port: Port,
-           protocol: Protocol = IPPROTO_TCP): Future[AsyncFD] =
-  ## Establishes connection to the specified ``address``:``port`` pair via the
-  ## specified protocol. The procedure iterates through possible
-  ## resolutions of the ``address`` until it succeeds, meaning that it
-  ## seamlessly works with both IPv4 and IPv6.
-  ## Returns the async file descriptor, registered in the dispatcher of
-  ## the current thread, ready to send or receive data.
-  let retFuture = newFuture[AsyncFD]("dial")
-  result = retFuture
-  let sockType = protocol.toSockType()
-
-  let aiList = getAddrInfo(address, port, Domain.AF_UNSPEC, sockType, protocol)
-  asyncAddrInfoLoop(aiList, noFD, protocol)
-
-proc connect*(socket: AsyncFD, address: string, port: Port,
-              domain = Domain.AF_INET): Future[void] =
-  let retFuture = newFuture[void]("connect")
-  result = retFuture
-
-  when defined(windows):
-    verifyPresence(socket)
-  else:
-    assert getSockDomain(socket.SocketHandle) == domain
-
-  let aiList = getAddrInfo(address, port, domain)
-  when defined(windows):
-    socket.SocketHandle.bindToDomain(domain)
-  asyncAddrInfoLoop(aiList, socket)
diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim
deleted file mode 100644
index ae62a5c4e..000000000
--- a/lib/pure/includes/osenv.nim
+++ /dev/null
@@ -1,159 +0,0 @@
-## Include file that implements 'getEnv' and friends. Do not import it!
-
-when not declared(ospaths):
-  {.error: "This is an include file for ospaths.nim!".}
-
-proc c_getenv(env: cstring): cstring {.
-  importc: "getenv", header: "<stdlib.h>".}
-proc c_putenv(env: cstring): cint {.
-  importc: "putenv", header: "<stdlib.h>".}
-
-# Environment handling cannot be put into RTL, because the ``envPairs``
-# iterator depends on ``environment``.
-
-var
-  envComputed {.threadvar.}: bool
-  environment {.threadvar.}: seq[string]
-
-when defined(windows) and not defined(nimscript):
-  # because we support Windows GUI applications, things get really
-  # messy here...
-  when useWinUnicode:
-    when defined(cpp):
-      proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.
-        importcpp: "(NI16*)wcschr((const wchar_t *)#, #)", header: "<string.h>".}
-    else:
-      proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.
-        importc: "wcschr", header: "<string.h>".}
-  else:
-    proc strEnd(cstr: cstring, c = 0'i32): cstring {.
-      importc: "strchr", header: "<string.h>".}
-
-  proc getEnvVarsC() =
-    if not envComputed:
-      environment = @[]
-      when useWinUnicode:
-        var
-          env = getEnvironmentStringsW()
-          e = env
-        if e == nil: return # an error occurred
-        while true:
-          var eend = strEnd(e)
-          add(environment, $e)
-          e = cast[WideCString](cast[ByteAddress](eend)+2)
-          if eend[1].int == 0: break
-        discard freeEnvironmentStringsW(env)
-      else:
-        var
-          env = getEnvironmentStringsA()
-          e = env
-        if e == nil: return # an error occurred
-        while true:
-          var eend = strEnd(e)
-          add(environment, $e)
-          e = cast[cstring](cast[ByteAddress](eend)+1)
-          if eend[1] == '\0': break
-        discard freeEnvironmentStringsA(env)
-      envComputed = true
-
-else:
-  const
-    useNSGetEnviron = (defined(macosx) and not defined(ios)) or defined(nimscript)
-
-  when useNSGetEnviron:
-    # From the manual:
-    # Shared libraries and bundles don't have direct access to environ,
-    # which is only available to the loader ld(1) when a complete program
-    # is being linked.
-    # The environment routines can still be used, but if direct access to
-    # environ is needed, the _NSGetEnviron() routine, defined in
-    # <crt_externs.h>, can be used to retrieve the address of environ
-    # at runtime.
-    proc NSGetEnviron(): ptr cstringArray {.
-      importc: "_NSGetEnviron", header: "<crt_externs.h>".}
-  else:
-    var gEnv {.importc: "environ".}: cstringArray
-
-  proc getEnvVarsC() =
-    # retrieves the variables of char** env of C's main proc
-    if not envComputed:
-      environment = @[]
-      when useNSGetEnviron:
-        var gEnv = NSGetEnviron()[]
-      var i = 0
-      while true:
-        if gEnv[i] == nil: break
-        add environment, $gEnv[i]
-        inc(i)
-      envComputed = true
-
-proc findEnvVar(key: string): int =
-  getEnvVarsC()
-  var temp = key & '='
-  for i in 0..high(environment):
-    if startsWith(environment[i], temp): return i
-  return -1
-
-proc getEnv*(key: string, default = ""): TaintedString {.tags: [ReadEnvEffect].} =
-  ## Returns the value of the `environment variable`:idx: named `key`.
-  ##
-  ## If the variable does not exist, "" is returned. To distinguish
-  ## whether a variable exists or it's value is just "", call
-  ## `existsEnv(key)`.
-  when nimvm:
-    discard "built into the compiler"
-  else:
-    var i = findEnvVar(key)
-    if i >= 0:
-      return TaintedString(substr(environment[i], find(environment[i], '=')+1))
-    else:
-      var env = c_getenv(key)
-      if env == nil: return TaintedString(default)
-      result = TaintedString($env)
-
-proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
-  ## Checks whether the environment variable named `key` exists.
-  ## Returns true if it exists, false otherwise.
-  when nimvm:
-    discard "built into the compiler"
-  else:
-    if c_getenv(key) != nil: return true
-    else: return findEnvVar(key) >= 0
-
-proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
-  ## Sets the value of the `environment variable`:idx: named `key` to `val`.
-  ## If an error occurs, `EInvalidEnvVar` is raised.
-
-  # Note: by storing the string in the environment sequence,
-  # we guarantee that we don't free the memory before the program
-  # ends (this is needed for POSIX compliance). It is also needed so that
-  # the process itself may access its modified environment variables!
-  when nimvm:
-    discard "built into the compiler"
-  else:
-    var indx = findEnvVar(key)
-    if indx >= 0:
-      environment[indx] = key & '=' & val
-    else:
-      add environment, (key & '=' & val)
-      indx = high(environment)
-    when defined(windows) and not defined(nimscript):
-      when useWinUnicode:
-        var k = newWideCString(key)
-        var v = newWideCString(val)
-        if setEnvironmentVariableW(k, v) == 0'i32: raiseOSError(osLastError())
-      else:
-        if setEnvironmentVariableA(key, val) == 0'i32: raiseOSError(osLastError())
-    else:
-      if c_putenv(environment[indx]) != 0'i32:
-        raiseOSError(osLastError())
-
-iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} =
-  ## Iterate over all `environments variables`:idx:. In the first component
-  ## of the tuple is the name of the current variable stored, in the second
-  ## its value.
-  getEnvVarsC()
-  for i in 0..high(environment):
-    var p = find(environment[i], '=')
-    yield (TaintedString(substr(environment[i], 0, p-1)),
-           TaintedString(substr(environment[i], p+1)))
diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim
deleted file mode 100644
index bfb6118f2..000000000
--- a/lib/pure/includes/oserr.nim
+++ /dev/null
@@ -1,88 +0,0 @@
-## Include file that implements 'osErrorMsg' and friends. Do not import it!
-
-when not declared(ospaths):
-  {.error: "This is an include file for ospaths.nim!".}
-
-when not defined(nimscript):
-  var errno {.importc, header: "<errno.h>".}: cint
-
-  proc c_strerror(errnum: cint): cstring {.
-    importc: "strerror", header: "<string.h>".}
-
-  when defined(windows):
-    import winlean
-
-proc `==`*(err1, err2: OSErrorCode): bool {.borrow.}
-proc `$`*(err: OSErrorCode): string {.borrow.}
-
-proc osErrorMsg*(errorCode: OSErrorCode): string =
-  ## Converts an OS error code into a human readable string.
-  ##
-  ## The error code can be retrieved using the ``osLastError`` proc.
-  ##
-  ## If conversion fails, or ``errorCode`` is ``0`` then ``""`` will be
-  ## returned.
-  ##
-  ## On Windows, the ``-d:useWinAnsi`` compilation flag can be used to
-  ## make this procedure use the non-unicode Win API calls to retrieve the
-  ## message.
-  result = ""
-  when defined(nimscript):
-    discard
-  elif defined(Windows):
-    if errorCode != OSErrorCode(0'i32):
-      when useWinUnicode:
-        var msgbuf: WideCString
-        if formatMessageW(0x00000100 or 0x00001000 or 0x00000200,
-                        nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
-          result = $msgbuf
-          if msgbuf != nil: localFree(cast[pointer](msgbuf))
-      else:
-        var msgbuf: cstring
-        if formatMessageA(0x00000100 or 0x00001000 or 0x00000200,
-                        nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
-          result = $msgbuf
-          if msgbuf != nil: localFree(msgbuf)
-  else:
-    if errorCode != OSErrorCode(0'i32):
-      result = $c_strerror(errorCode.int32)
-
-proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
-  ## Raises an ``OSError`` exception. The ``errorCode`` will determine the
-  ## message, ``osErrorMsg`` will be used to get this message.
-  ##
-  ## The error code can be retrieved using the ``osLastError`` proc.
-  ##
-  ## If the error code is ``0`` or an error message could not be retrieved,
-  ## the message ``unknown OS error`` will be used.
-  var e: ref OSError; new(e)
-  e.errorCode = errorCode.int32
-  if additionalInfo.len == 0:
-    e.msg = osErrorMsg(errorCode)
-  else:
-    e.msg = osErrorMsg(errorCode) & "\nAdditional info: " & additionalInfo
-  if e.msg == "":
-    e.msg = "unknown OS error"
-  raise e
-
-{.push stackTrace:off.}
-proc osLastError*(): OSErrorCode =
-  ## Retrieves the last operating system error code.
-  ##
-  ## This procedure is useful in the event when an OS call fails. In that case
-  ## this procedure will return the error code describing the reason why the
-  ## OS call failed. The ``OSErrorMsg`` procedure can then be used to convert
-  ## this code into a string.
-  ##
-  ## **Warning**:
-  ## The behaviour of this procedure varies between Windows and POSIX systems.
-  ## On Windows some OS calls can reset the error code to ``0`` causing this
-  ## procedure to return ``0``. It is therefore advised to call this procedure
-  ## immediately after an OS call fails. On POSIX systems this is not a problem.
-  when defined(nimscript):
-    discard
-  elif defined(windows):
-    result = OSErrorCode(getLastError())
-  else:
-    result = OSErrorCode(errno)
-{.pop.}
diff --git a/lib/pure/includes/unicode_ranges.nim b/lib/pure/includes/unicode_ranges.nim
new file mode 100644
index 000000000..04ccfb747
--- /dev/null
+++ b/lib/pure/includes/unicode_ranges.nim
@@ -0,0 +1,2007 @@
+# This file was created from a script.
+
+const
+  toLowerRanges = [
+    0x00041'i32, 0x0005A'i32, 532,
+    0x000C0'i32, 0x000D6'i32, 532,
+    0x000D8'i32, 0x000DE'i32, 532,
+    0x00189'i32, 0x0018A'i32, 705,
+    0x001B1'i32, 0x001B2'i32, 717,
+    0x00388'i32, 0x0038A'i32, 537,
+    0x0038E'i32, 0x0038F'i32, 563,
+    0x00391'i32, 0x003A1'i32, 532,
+    0x003A3'i32, 0x003AB'i32, 532,
+    0x003FD'i32, 0x003FF'i32, 370,
+    0x00400'i32, 0x0040F'i32, 580,
+    0x00410'i32, 0x0042F'i32, 532,
+    0x00531'i32, 0x00556'i32, 548,
+    0x010A0'i32, 0x010C5'i32, 7764,
+    0x013A0'i32, 0x013EF'i32, 39364,
+    0x013F0'i32, 0x013F5'i32, 508,
+    0x01C90'i32, 0x01CBA'i32, -2508,
+    0x01CBD'i32, 0x01CBF'i32, -2508,
+    0x01F08'i32, 0x01F0F'i32, 492,
+    0x01F18'i32, 0x01F1D'i32, 492,
+    0x01F28'i32, 0x01F2F'i32, 492,
+    0x01F38'i32, 0x01F3F'i32, 492,
+    0x01F48'i32, 0x01F4D'i32, 492,
+    0x01F68'i32, 0x01F6F'i32, 492,
+    0x01F88'i32, 0x01F8F'i32, 492,
+    0x01F98'i32, 0x01F9F'i32, 492,
+    0x01FA8'i32, 0x01FAF'i32, 492,
+    0x01FB8'i32, 0x01FB9'i32, 492,
+    0x01FBA'i32, 0x01FBB'i32, 426,
+    0x01FC8'i32, 0x01FCB'i32, 414,
+    0x01FD8'i32, 0x01FD9'i32, 492,
+    0x01FDA'i32, 0x01FDB'i32, 400,
+    0x01FE8'i32, 0x01FE9'i32, 492,
+    0x01FEA'i32, 0x01FEB'i32, 388,
+    0x01FF8'i32, 0x01FF9'i32, 372,
+    0x01FFA'i32, 0x01FFB'i32, 374,
+    0x02C00'i32, 0x02C2E'i32, 548,
+    0x02C7E'i32, 0x02C7F'i32, -10315,
+    0x0FF21'i32, 0x0FF3A'i32, 532,
+    0x10400'i32, 0x10427'i32, 540,
+    0x104B0'i32, 0x104D3'i32, 540,
+    0x10C80'i32, 0x10CB2'i32, 564,
+    0x118A0'i32, 0x118BF'i32, 532,
+    0x16E40'i32, 0x16E5F'i32, 532,
+    0x1E900'i32, 0x1E921'i32, 534,
+  ]
+
+  toLowerSinglets = [
+    0x00100'i32, 501,
+    0x00102'i32, 501,
+    0x00104'i32, 501,
+    0x00106'i32, 501,
+    0x00108'i32, 501,
+    0x0010A'i32, 501,
+    0x0010C'i32, 501,
+    0x0010E'i32, 501,
+    0x00110'i32, 501,
+    0x00112'i32, 501,
+    0x00114'i32, 501,
+    0x00116'i32, 501,
+    0x00118'i32, 501,
+    0x0011A'i32, 501,
+    0x0011C'i32, 501,
+    0x0011E'i32, 501,
+    0x00120'i32, 501,
+    0x00122'i32, 501,
+    0x00124'i32, 501,
+    0x00126'i32, 501,
+    0x00128'i32, 501,
+    0x0012A'i32, 501,
+    0x0012C'i32, 501,
+    0x0012E'i32, 501,
+    0x00130'i32, 301,
+    0x00132'i32, 501,
+    0x00134'i32, 501,
+    0x00136'i32, 501,
+    0x00139'i32, 501,
+    0x0013B'i32, 501,
+    0x0013D'i32, 501,
+    0x0013F'i32, 501,
+    0x00141'i32, 501,
+    0x00143'i32, 501,
+    0x00145'i32, 501,
+    0x00147'i32, 501,
+    0x0014A'i32, 501,
+    0x0014C'i32, 501,
+    0x0014E'i32, 501,
+    0x00150'i32, 501,
+    0x00152'i32, 501,
+    0x00154'i32, 501,
+    0x00156'i32, 501,
+    0x00158'i32, 501,
+    0x0015A'i32, 501,
+    0x0015C'i32, 501,
+    0x0015E'i32, 501,
+    0x00160'i32, 501,
+    0x00162'i32, 501,
+    0x00164'i32, 501,
+    0x00166'i32, 501,
+    0x00168'i32, 501,
+    0x0016A'i32, 501,
+    0x0016C'i32, 501,
+    0x0016E'i32, 501,
+    0x00170'i32, 501,
+    0x00172'i32, 501,
+    0x00174'i32, 501,
+    0x00176'i32, 501,
+    0x00178'i32, 379,
+    0x00179'i32, 501,
+    0x0017B'i32, 501,
+    0x0017D'i32, 501,
+    0x00181'i32, 710,
+    0x00182'i32, 501,
+    0x00184'i32, 501,
+    0x00186'i32, 706,
+    0x00187'i32, 501,
+    0x0018B'i32, 501,
+    0x0018E'i32, 579,
+    0x0018F'i32, 702,
+    0x00190'i32, 703,
+    0x00191'i32, 501,
+    0x00193'i32, 705,
+    0x00194'i32, 707,
+    0x00196'i32, 711,
+    0x00197'i32, 709,
+    0x00198'i32, 501,
+    0x0019C'i32, 711,
+    0x0019D'i32, 713,
+    0x0019F'i32, 714,
+    0x001A0'i32, 501,
+    0x001A2'i32, 501,
+    0x001A4'i32, 501,
+    0x001A6'i32, 718,
+    0x001A7'i32, 501,
+    0x001A9'i32, 718,
+    0x001AC'i32, 501,
+    0x001AE'i32, 718,
+    0x001AF'i32, 501,
+    0x001B3'i32, 501,
+    0x001B5'i32, 501,
+    0x001B7'i32, 719,
+    0x001B8'i32, 501,
+    0x001BC'i32, 501,
+    0x001C4'i32, 502,
+    0x001C5'i32, 501,
+    0x001C7'i32, 502,
+    0x001C8'i32, 501,
+    0x001CA'i32, 502,
+    0x001CB'i32, 501,
+    0x001CD'i32, 501,
+    0x001CF'i32, 501,
+    0x001D1'i32, 501,
+    0x001D3'i32, 501,
+    0x001D5'i32, 501,
+    0x001D7'i32, 501,
+    0x001D9'i32, 501,
+    0x001DB'i32, 501,
+    0x001DE'i32, 501,
+    0x001E0'i32, 501,
+    0x001E2'i32, 501,
+    0x001E4'i32, 501,
+    0x001E6'i32, 501,
+    0x001E8'i32, 501,
+    0x001EA'i32, 501,
+    0x001EC'i32, 501,
+    0x001EE'i32, 501,
+    0x001F1'i32, 502,
+    0x001F2'i32, 501,
+    0x001F4'i32, 501,
+    0x001F6'i32, 403,
+    0x001F7'i32, 444,
+    0x001F8'i32, 501,
+    0x001FA'i32, 501,
+    0x001FC'i32, 501,
+    0x001FE'i32, 501,
+    0x00200'i32, 501,
+    0x00202'i32, 501,
+    0x00204'i32, 501,
+    0x00206'i32, 501,
+    0x00208'i32, 501,
+    0x0020A'i32, 501,
+    0x0020C'i32, 501,
+    0x0020E'i32, 501,
+    0x00210'i32, 501,
+    0x00212'i32, 501,
+    0x00214'i32, 501,
+    0x00216'i32, 501,
+    0x00218'i32, 501,
+    0x0021A'i32, 501,
+    0x0021C'i32, 501,
+    0x0021E'i32, 501,
+    0x00220'i32, 370,
+    0x00222'i32, 501,
+    0x00224'i32, 501,
+    0x00226'i32, 501,
+    0x00228'i32, 501,
+    0x0022A'i32, 501,
+    0x0022C'i32, 501,
+    0x0022E'i32, 501,
+    0x00230'i32, 501,
+    0x00232'i32, 501,
+    0x0023A'i32, 11295,
+    0x0023B'i32, 501,
+    0x0023D'i32, 337,
+    0x0023E'i32, 11292,
+    0x00241'i32, 501,
+    0x00243'i32, 305,
+    0x00244'i32, 569,
+    0x00245'i32, 571,
+    0x00246'i32, 501,
+    0x00248'i32, 501,
+    0x0024A'i32, 501,
+    0x0024C'i32, 501,
+    0x0024E'i32, 501,
+    0x00370'i32, 501,
+    0x00372'i32, 501,
+    0x00376'i32, 501,
+    0x0037F'i32, 616,
+    0x00386'i32, 538,
+    0x0038C'i32, 564,
+    0x003CF'i32, 508,
+    0x003D8'i32, 501,
+    0x003DA'i32, 501,
+    0x003DC'i32, 501,
+    0x003DE'i32, 501,
+    0x003E0'i32, 501,
+    0x003E2'i32, 501,
+    0x003E4'i32, 501,
+    0x003E6'i32, 501,
+    0x003E8'i32, 501,
+    0x003EA'i32, 501,
+    0x003EC'i32, 501,
+    0x003EE'i32, 501,
+    0x003F4'i32, 440,
+    0x003F7'i32, 501,
+    0x003F9'i32, 493,
+    0x003FA'i32, 501,
+    0x00460'i32, 501,
+    0x00462'i32, 501,
+    0x00464'i32, 501,
+    0x00466'i32, 501,
+    0x00468'i32, 501,
+    0x0046A'i32, 501,
+    0x0046C'i32, 501,
+    0x0046E'i32, 501,
+    0x00470'i32, 501,
+    0x00472'i32, 501,
+    0x00474'i32, 501,
+    0x00476'i32, 501,
+    0x00478'i32, 501,
+    0x0047A'i32, 501,
+    0x0047C'i32, 501,
+    0x0047E'i32, 501,
+    0x00480'i32, 501,
+    0x0048A'i32, 501,
+    0x0048C'i32, 501,
+    0x0048E'i32, 501,
+    0x00490'i32, 501,
+    0x00492'i32, 501,
+    0x00494'i32, 501,
+    0x00496'i32, 501,
+    0x00498'i32, 501,
+    0x0049A'i32, 501,
+    0x0049C'i32, 501,
+    0x0049E'i32, 501,
+    0x004A0'i32, 501,
+    0x004A2'i32, 501,
+    0x004A4'i32, 501,
+    0x004A6'i32, 501,
+    0x004A8'i32, 501,
+    0x004AA'i32, 501,
+    0x004AC'i32, 501,
+    0x004AE'i32, 501,
+    0x004B0'i32, 501,
+    0x004B2'i32, 501,
+    0x004B4'i32, 501,
+    0x004B6'i32, 501,
+    0x004B8'i32, 501,
+    0x004BA'i32, 501,
+    0x004BC'i32, 501,
+    0x004BE'i32, 501,
+    0x004C0'i32, 515,
+    0x004C1'i32, 501,
+    0x004C3'i32, 501,
+    0x004C5'i32, 501,
+    0x004C7'i32, 501,
+    0x004C9'i32, 501,
+    0x004CB'i32, 501,
+    0x004CD'i32, 501,
+    0x004D0'i32, 501,
+    0x004D2'i32, 501,
+    0x004D4'i32, 501,
+    0x004D6'i32, 501,
+    0x004D8'i32, 501,
+    0x004DA'i32, 501,
+    0x004DC'i32, 501,
+    0x004DE'i32, 501,
+    0x004E0'i32, 501,
+    0x004E2'i32, 501,
+    0x004E4'i32, 501,
+    0x004E6'i32, 501,
+    0x004E8'i32, 501,
+    0x004EA'i32, 501,
+    0x004EC'i32, 501,
+    0x004EE'i32, 501,
+    0x004F0'i32, 501,
+    0x004F2'i32, 501,
+    0x004F4'i32, 501,
+    0x004F6'i32, 501,
+    0x004F8'i32, 501,
+    0x004FA'i32, 501,
+    0x004FC'i32, 501,
+    0x004FE'i32, 501,
+    0x00500'i32, 501,
+    0x00502'i32, 501,
+    0x00504'i32, 501,
+    0x00506'i32, 501,
+    0x00508'i32, 501,
+    0x0050A'i32, 501,
+    0x0050C'i32, 501,
+    0x0050E'i32, 501,
+    0x00510'i32, 501,
+    0x00512'i32, 501,
+    0x00514'i32, 501,
+    0x00516'i32, 501,
+    0x00518'i32, 501,
+    0x0051A'i32, 501,
+    0x0051C'i32, 501,
+    0x0051E'i32, 501,
+    0x00520'i32, 501,
+    0x00522'i32, 501,
+    0x00524'i32, 501,
+    0x00526'i32, 501,
+    0x00528'i32, 501,
+    0x0052A'i32, 501,
+    0x0052C'i32, 501,
+    0x0052E'i32, 501,
+    0x010C7'i32, 7764,
+    0x010CD'i32, 7764,
+    0x01E00'i32, 501,
+    0x01E02'i32, 501,
+    0x01E04'i32, 501,
+    0x01E06'i32, 501,
+    0x01E08'i32, 501,
+    0x01E0A'i32, 501,
+    0x01E0C'i32, 501,
+    0x01E0E'i32, 501,
+    0x01E10'i32, 501,
+    0x01E12'i32, 501,
+    0x01E14'i32, 501,
+    0x01E16'i32, 501,
+    0x01E18'i32, 501,
+    0x01E1A'i32, 501,
+    0x01E1C'i32, 501,
+    0x01E1E'i32, 501,
+    0x01E20'i32, 501,
+    0x01E22'i32, 501,
+    0x01E24'i32, 501,
+    0x01E26'i32, 501,
+    0x01E28'i32, 501,
+    0x01E2A'i32, 501,
+    0x01E2C'i32, 501,
+    0x01E2E'i32, 501,
+    0x01E30'i32, 501,
+    0x01E32'i32, 501,
+    0x01E34'i32, 501,
+    0x01E36'i32, 501,
+    0x01E38'i32, 501,
+    0x01E3A'i32, 501,
+    0x01E3C'i32, 501,
+    0x01E3E'i32, 501,
+    0x01E40'i32, 501,
+    0x01E42'i32, 501,
+    0x01E44'i32, 501,
+    0x01E46'i32, 501,
+    0x01E48'i32, 501,
+    0x01E4A'i32, 501,
+    0x01E4C'i32, 501,
+    0x01E4E'i32, 501,
+    0x01E50'i32, 501,
+    0x01E52'i32, 501,
+    0x01E54'i32, 501,
+    0x01E56'i32, 501,
+    0x01E58'i32, 501,
+    0x01E5A'i32, 501,
+    0x01E5C'i32, 501,
+    0x01E5E'i32, 501,
+    0x01E60'i32, 501,
+    0x01E62'i32, 501,
+    0x01E64'i32, 501,
+    0x01E66'i32, 501,
+    0x01E68'i32, 501,
+    0x01E6A'i32, 501,
+    0x01E6C'i32, 501,
+    0x01E6E'i32, 501,
+    0x01E70'i32, 501,
+    0x01E72'i32, 501,
+    0x01E74'i32, 501,
+    0x01E76'i32, 501,
+    0x01E78'i32, 501,
+    0x01E7A'i32, 501,
+    0x01E7C'i32, 501,
+    0x01E7E'i32, 501,
+    0x01E80'i32, 501,
+    0x01E82'i32, 501,
+    0x01E84'i32, 501,
+    0x01E86'i32, 501,
+    0x01E88'i32, 501,
+    0x01E8A'i32, 501,
+    0x01E8C'i32, 501,
+    0x01E8E'i32, 501,
+    0x01E90'i32, 501,
+    0x01E92'i32, 501,
+    0x01E94'i32, 501,
+    0x01E9E'i32, -7115,
+    0x01EA0'i32, 501,
+    0x01EA2'i32, 501,
+    0x01EA4'i32, 501,
+    0x01EA6'i32, 501,
+    0x01EA8'i32, 501,
+    0x01EAA'i32, 501,
+    0x01EAC'i32, 501,
+    0x01EAE'i32, 501,
+    0x01EB0'i32, 501,
+    0x01EB2'i32, 501,
+    0x01EB4'i32, 501,
+    0x01EB6'i32, 501,
+    0x01EB8'i32, 501,
+    0x01EBA'i32, 501,
+    0x01EBC'i32, 501,
+    0x01EBE'i32, 501,
+    0x01EC0'i32, 501,
+    0x01EC2'i32, 501,
+    0x01EC4'i32, 501,
+    0x01EC6'i32, 501,
+    0x01EC8'i32, 501,
+    0x01ECA'i32, 501,
+    0x01ECC'i32, 501,
+    0x01ECE'i32, 501,
+    0x01ED0'i32, 501,
+    0x01ED2'i32, 501,
+    0x01ED4'i32, 501,
+    0x01ED6'i32, 501,
+    0x01ED8'i32, 501,
+    0x01EDA'i32, 501,
+    0x01EDC'i32, 501,
+    0x01EDE'i32, 501,
+    0x01EE0'i32, 501,
+    0x01EE2'i32, 501,
+    0x01EE4'i32, 501,
+    0x01EE6'i32, 501,
+    0x01EE8'i32, 501,
+    0x01EEA'i32, 501,
+    0x01EEC'i32, 501,
+    0x01EEE'i32, 501,
+    0x01EF0'i32, 501,
+    0x01EF2'i32, 501,
+    0x01EF4'i32, 501,
+    0x01EF6'i32, 501,
+    0x01EF8'i32, 501,
+    0x01EFA'i32, 501,
+    0x01EFC'i32, 501,
+    0x01EFE'i32, 501,
+    0x01F59'i32, 492,
+    0x01F5B'i32, 492,
+    0x01F5D'i32, 492,
+    0x01F5F'i32, 492,
+    0x01FBC'i32, 491,
+    0x01FCC'i32, 491,
+    0x01FEC'i32, 493,
+    0x01FFC'i32, 491,
+    0x02126'i32, -7017,
+    0x0212A'i32, -7883,
+    0x0212B'i32, -7762,
+    0x02132'i32, 528,
+    0x02183'i32, 501,
+    0x02C60'i32, 501,
+    0x02C62'i32, -10243,
+    0x02C63'i32, -3314,
+    0x02C64'i32, -10227,
+    0x02C67'i32, 501,
+    0x02C69'i32, 501,
+    0x02C6B'i32, 501,
+    0x02C6D'i32, -10280,
+    0x02C6E'i32, -10249,
+    0x02C6F'i32, -10283,
+    0x02C70'i32, -10282,
+    0x02C72'i32, 501,
+    0x02C75'i32, 501,
+    0x02C80'i32, 501,
+    0x02C82'i32, 501,
+    0x02C84'i32, 501,
+    0x02C86'i32, 501,
+    0x02C88'i32, 501,
+    0x02C8A'i32, 501,
+    0x02C8C'i32, 501,
+    0x02C8E'i32, 501,
+    0x02C90'i32, 501,
+    0x02C92'i32, 501,
+    0x02C94'i32, 501,
+    0x02C96'i32, 501,
+    0x02C98'i32, 501,
+    0x02C9A'i32, 501,
+    0x02C9C'i32, 501,
+    0x02C9E'i32, 501,
+    0x02CA0'i32, 501,
+    0x02CA2'i32, 501,
+    0x02CA4'i32, 501,
+    0x02CA6'i32, 501,
+    0x02CA8'i32, 501,
+    0x02CAA'i32, 501,
+    0x02CAC'i32, 501,
+    0x02CAE'i32, 501,
+    0x02CB0'i32, 501,
+    0x02CB2'i32, 501,
+    0x02CB4'i32, 501,
+    0x02CB6'i32, 501,
+    0x02CB8'i32, 501,
+    0x02CBA'i32, 501,
+    0x02CBC'i32, 501,
+    0x02CBE'i32, 501,
+    0x02CC0'i32, 501,
+    0x02CC2'i32, 501,
+    0x02CC4'i32, 501,
+    0x02CC6'i32, 501,
+    0x02CC8'i32, 501,
+    0x02CCA'i32, 501,
+    0x02CCC'i32, 501,
+    0x02CCE'i32, 501,
+    0x02CD0'i32, 501,
+    0x02CD2'i32, 501,
+    0x02CD4'i32, 501,
+    0x02CD6'i32, 501,
+    0x02CD8'i32, 501,
+    0x02CDA'i32, 501,
+    0x02CDC'i32, 501,
+    0x02CDE'i32, 501,
+    0x02CE0'i32, 501,
+    0x02CE2'i32, 501,
+    0x02CEB'i32, 501,
+    0x02CED'i32, 501,
+    0x02CF2'i32, 501,
+    0x0A640'i32, 501,
+    0x0A642'i32, 501,
+    0x0A644'i32, 501,
+    0x0A646'i32, 501,
+    0x0A648'i32, 501,
+    0x0A64A'i32, 501,
+    0x0A64C'i32, 501,
+    0x0A64E'i32, 501,
+    0x0A650'i32, 501,
+    0x0A652'i32, 501,
+    0x0A654'i32, 501,
+    0x0A656'i32, 501,
+    0x0A658'i32, 501,
+    0x0A65A'i32, 501,
+    0x0A65C'i32, 501,
+    0x0A65E'i32, 501,
+    0x0A660'i32, 501,
+    0x0A662'i32, 501,
+    0x0A664'i32, 501,
+    0x0A666'i32, 501,
+    0x0A668'i32, 501,
+    0x0A66A'i32, 501,
+    0x0A66C'i32, 501,
+    0x0A680'i32, 501,
+    0x0A682'i32, 501,
+    0x0A684'i32, 501,
+    0x0A686'i32, 501,
+    0x0A688'i32, 501,
+    0x0A68A'i32, 501,
+    0x0A68C'i32, 501,
+    0x0A68E'i32, 501,
+    0x0A690'i32, 501,
+    0x0A692'i32, 501,
+    0x0A694'i32, 501,
+    0x0A696'i32, 501,
+    0x0A698'i32, 501,
+    0x0A69A'i32, 501,
+    0x0A722'i32, 501,
+    0x0A724'i32, 501,
+    0x0A726'i32, 501,
+    0x0A728'i32, 501,
+    0x0A72A'i32, 501,
+    0x0A72C'i32, 501,
+    0x0A72E'i32, 501,
+    0x0A732'i32, 501,
+    0x0A734'i32, 501,
+    0x0A736'i32, 501,
+    0x0A738'i32, 501,
+    0x0A73A'i32, 501,
+    0x0A73C'i32, 501,
+    0x0A73E'i32, 501,
+    0x0A740'i32, 501,
+    0x0A742'i32, 501,
+    0x0A744'i32, 501,
+    0x0A746'i32, 501,
+    0x0A748'i32, 501,
+    0x0A74A'i32, 501,
+    0x0A74C'i32, 501,
+    0x0A74E'i32, 501,
+    0x0A750'i32, 501,
+    0x0A752'i32, 501,
+    0x0A754'i32, 501,
+    0x0A756'i32, 501,
+    0x0A758'i32, 501,
+    0x0A75A'i32, 501,
+    0x0A75C'i32, 501,
+    0x0A75E'i32, 501,
+    0x0A760'i32, 501,
+    0x0A762'i32, 501,
+    0x0A764'i32, 501,
+    0x0A766'i32, 501,
+    0x0A768'i32, 501,
+    0x0A76A'i32, 501,
+    0x0A76C'i32, 501,
+    0x0A76E'i32, 501,
+    0x0A779'i32, 501,
+    0x0A77B'i32, 501,
+    0x0A77D'i32, -34832,
+    0x0A77E'i32, 501,
+    0x0A780'i32, 501,
+    0x0A782'i32, 501,
+    0x0A784'i32, 501,
+    0x0A786'i32, 501,
+    0x0A78B'i32, 501,
+    0x0A78D'i32, -41780,
+    0x0A790'i32, 501,
+    0x0A792'i32, 501,
+    0x0A796'i32, 501,
+    0x0A798'i32, 501,
+    0x0A79A'i32, 501,
+    0x0A79C'i32, 501,
+    0x0A79E'i32, 501,
+    0x0A7A0'i32, 501,
+    0x0A7A2'i32, 501,
+    0x0A7A4'i32, 501,
+    0x0A7A6'i32, 501,
+    0x0A7A8'i32, 501,
+    0x0A7AA'i32, -41808,
+    0x0A7AB'i32, -41819,
+    0x0A7AC'i32, -41815,
+    0x0A7AD'i32, -41805,
+    0x0A7AE'i32, -41808,
+    0x0A7B0'i32, -41758,
+    0x0A7B1'i32, -41782,
+    0x0A7B2'i32, -41761,
+    0x0A7B3'i32, 1428,
+    0x0A7B4'i32, 501,
+    0x0A7B6'i32, 501,
+    0x0A7B8'i32, 501,
+    0x0A7BA'i32, 501,
+    0x0A7BC'i32, 501,
+    0x0A7BE'i32, 501,
+    0x0A7C2'i32, 501,
+    0x0A7C4'i32, 452,
+    0x0A7C5'i32, -41807,
+    0x0A7C6'i32, -34884,
+  ]
+
+  toUpperRanges = [
+    0x00061'i32, 0x0007A'i32, 468,
+    0x000E0'i32, 0x000F6'i32, 468,
+    0x000F8'i32, 0x000FE'i32, 468,
+    0x0023F'i32, 0x00240'i32, 11315,
+    0x00256'i32, 0x00257'i32, 295,
+    0x0028A'i32, 0x0028B'i32, 283,
+    0x0037B'i32, 0x0037D'i32, 630,
+    0x003AD'i32, 0x003AF'i32, 463,
+    0x003B1'i32, 0x003C1'i32, 468,
+    0x003C3'i32, 0x003CB'i32, 468,
+    0x003CD'i32, 0x003CE'i32, 437,
+    0x00430'i32, 0x0044F'i32, 468,
+    0x00450'i32, 0x0045F'i32, 420,
+    0x00561'i32, 0x00586'i32, 452,
+    0x010D0'i32, 0x010FA'i32, 3508,
+    0x010FD'i32, 0x010FF'i32, 3508,
+    0x013F8'i32, 0x013FD'i32, 492,
+    0x01C83'i32, 0x01C84'i32, -5742,
+    0x01F00'i32, 0x01F07'i32, 508,
+    0x01F10'i32, 0x01F15'i32, 508,
+    0x01F20'i32, 0x01F27'i32, 508,
+    0x01F30'i32, 0x01F37'i32, 508,
+    0x01F40'i32, 0x01F45'i32, 508,
+    0x01F60'i32, 0x01F67'i32, 508,
+    0x01F70'i32, 0x01F71'i32, 574,
+    0x01F72'i32, 0x01F75'i32, 586,
+    0x01F76'i32, 0x01F77'i32, 600,
+    0x01F78'i32, 0x01F79'i32, 628,
+    0x01F7A'i32, 0x01F7B'i32, 612,
+    0x01F7C'i32, 0x01F7D'i32, 626,
+    0x01F80'i32, 0x01F87'i32, 508,
+    0x01F90'i32, 0x01F97'i32, 508,
+    0x01FA0'i32, 0x01FA7'i32, 508,
+    0x01FB0'i32, 0x01FB1'i32, 508,
+    0x01FD0'i32, 0x01FD1'i32, 508,
+    0x01FE0'i32, 0x01FE1'i32, 508,
+    0x02C30'i32, 0x02C5E'i32, 452,
+    0x02D00'i32, 0x02D25'i32, -6764,
+    0x0AB70'i32, 0x0ABBF'i32, -38364,
+    0x0FF41'i32, 0x0FF5A'i32, 468,
+    0x10428'i32, 0x1044F'i32, 460,
+    0x104D8'i32, 0x104FB'i32, 460,
+    0x10CC0'i32, 0x10CF2'i32, 436,
+    0x118C0'i32, 0x118DF'i32, 468,
+    0x16E60'i32, 0x16E7F'i32, 468,
+    0x1E922'i32, 0x1E943'i32, 466,
+  ]
+
+  toUpperSinglets = [
+    0x000B5'i32, 1243,
+    0x000FF'i32, 621,
+    0x00101'i32, 499,
+    0x00103'i32, 499,
+    0x00105'i32, 499,
+    0x00107'i32, 499,
+    0x00109'i32, 499,
+    0x0010B'i32, 499,
+    0x0010D'i32, 499,
+    0x0010F'i32, 499,
+    0x00111'i32, 499,
+    0x00113'i32, 499,
+    0x00115'i32, 499,
+    0x00117'i32, 499,
+    0x00119'i32, 499,
+    0x0011B'i32, 499,
+    0x0011D'i32, 499,
+    0x0011F'i32, 499,
+    0x00121'i32, 499,
+    0x00123'i32, 499,
+    0x00125'i32, 499,
+    0x00127'i32, 499,
+    0x00129'i32, 499,
+    0x0012B'i32, 499,
+    0x0012D'i32, 499,
+    0x0012F'i32, 499,
+    0x00131'i32, 268,
+    0x00133'i32, 499,
+    0x00135'i32, 499,
+    0x00137'i32, 499,
+    0x0013A'i32, 499,
+    0x0013C'i32, 499,
+    0x0013E'i32, 499,
+    0x00140'i32, 499,
+    0x00142'i32, 499,
+    0x00144'i32, 499,
+    0x00146'i32, 499,
+    0x00148'i32, 499,
+    0x0014B'i32, 499,
+    0x0014D'i32, 499,
+    0x0014F'i32, 499,
+    0x00151'i32, 499,
+    0x00153'i32, 499,
+    0x00155'i32, 499,
+    0x00157'i32, 499,
+    0x00159'i32, 499,
+    0x0015B'i32, 499,
+    0x0015D'i32, 499,
+    0x0015F'i32, 499,
+    0x00161'i32, 499,
+    0x00163'i32, 499,
+    0x00165'i32, 499,
+    0x00167'i32, 499,
+    0x00169'i32, 499,
+    0x0016B'i32, 499,
+    0x0016D'i32, 499,
+    0x0016F'i32, 499,
+    0x00171'i32, 499,
+    0x00173'i32, 499,
+    0x00175'i32, 499,
+    0x00177'i32, 499,
+    0x0017A'i32, 499,
+    0x0017C'i32, 499,
+    0x0017E'i32, 499,
+    0x0017F'i32, 200,
+    0x00180'i32, 695,
+    0x00183'i32, 499,
+    0x00185'i32, 499,
+    0x00188'i32, 499,
+    0x0018C'i32, 499,
+    0x00192'i32, 499,
+    0x00195'i32, 597,
+    0x00199'i32, 499,
+    0x0019A'i32, 663,
+    0x0019E'i32, 630,
+    0x001A1'i32, 499,
+    0x001A3'i32, 499,
+    0x001A5'i32, 499,
+    0x001A8'i32, 499,
+    0x001AD'i32, 499,
+    0x001B0'i32, 499,
+    0x001B4'i32, 499,
+    0x001B6'i32, 499,
+    0x001B9'i32, 499,
+    0x001BD'i32, 499,
+    0x001BF'i32, 556,
+    0x001C5'i32, 499,
+    0x001C6'i32, 498,
+    0x001C8'i32, 499,
+    0x001C9'i32, 498,
+    0x001CB'i32, 499,
+    0x001CC'i32, 498,
+    0x001CE'i32, 499,
+    0x001D0'i32, 499,
+    0x001D2'i32, 499,
+    0x001D4'i32, 499,
+    0x001D6'i32, 499,
+    0x001D8'i32, 499,
+    0x001DA'i32, 499,
+    0x001DC'i32, 499,
+    0x001DD'i32, 421,
+    0x001DF'i32, 499,
+    0x001E1'i32, 499,
+    0x001E3'i32, 499,
+    0x001E5'i32, 499,
+    0x001E7'i32, 499,
+    0x001E9'i32, 499,
+    0x001EB'i32, 499,
+    0x001ED'i32, 499,
+    0x001EF'i32, 499,
+    0x001F2'i32, 499,
+    0x001F3'i32, 498,
+    0x001F5'i32, 499,
+    0x001F9'i32, 499,
+    0x001FB'i32, 499,
+    0x001FD'i32, 499,
+    0x001FF'i32, 499,
+    0x00201'i32, 499,
+    0x00203'i32, 499,
+    0x00205'i32, 499,
+    0x00207'i32, 499,
+    0x00209'i32, 499,
+    0x0020B'i32, 499,
+    0x0020D'i32, 499,
+    0x0020F'i32, 499,
+    0x00211'i32, 499,
+    0x00213'i32, 499,
+    0x00215'i32, 499,
+    0x00217'i32, 499,
+    0x00219'i32, 499,
+    0x0021B'i32, 499,
+    0x0021D'i32, 499,
+    0x0021F'i32, 499,
+    0x00223'i32, 499,
+    0x00225'i32, 499,
+    0x00227'i32, 499,
+    0x00229'i32, 499,
+    0x0022B'i32, 499,
+    0x0022D'i32, 499,
+    0x0022F'i32, 499,
+    0x00231'i32, 499,
+    0x00233'i32, 499,
+    0x0023C'i32, 499,
+    0x00242'i32, 499,
+    0x00247'i32, 499,
+    0x00249'i32, 499,
+    0x0024B'i32, 499,
+    0x0024D'i32, 499,
+    0x0024F'i32, 499,
+    0x00250'i32, 11283,
+    0x00251'i32, 11280,
+    0x00252'i32, 11282,
+    0x00253'i32, 290,
+    0x00254'i32, 294,
+    0x00259'i32, 298,
+    0x0025B'i32, 297,
+    0x0025C'i32, 42819,
+    0x00260'i32, 295,
+    0x00261'i32, 42815,
+    0x00263'i32, 293,
+    0x00265'i32, 42780,
+    0x00266'i32, 42808,
+    0x00268'i32, 291,
+    0x00269'i32, 289,
+    0x0026A'i32, 42808,
+    0x0026B'i32, 11243,
+    0x0026C'i32, 42805,
+    0x0026F'i32, 289,
+    0x00271'i32, 11249,
+    0x00272'i32, 287,
+    0x00275'i32, 286,
+    0x0027D'i32, 11227,
+    0x00280'i32, 282,
+    0x00282'i32, 42807,
+    0x00283'i32, 282,
+    0x00287'i32, 42782,
+    0x00288'i32, 282,
+    0x00289'i32, 431,
+    0x0028C'i32, 429,
+    0x00292'i32, 281,
+    0x0029D'i32, 42761,
+    0x0029E'i32, 42758,
+    0x00371'i32, 499,
+    0x00373'i32, 499,
+    0x00377'i32, 499,
+    0x003AC'i32, 462,
+    0x003C2'i32, 469,
+    0x003CC'i32, 436,
+    0x003D0'i32, 438,
+    0x003D1'i32, 443,
+    0x003D5'i32, 453,
+    0x003D6'i32, 446,
+    0x003D7'i32, 492,
+    0x003D9'i32, 499,
+    0x003DB'i32, 499,
+    0x003DD'i32, 499,
+    0x003DF'i32, 499,
+    0x003E1'i32, 499,
+    0x003E3'i32, 499,
+    0x003E5'i32, 499,
+    0x003E7'i32, 499,
+    0x003E9'i32, 499,
+    0x003EB'i32, 499,
+    0x003ED'i32, 499,
+    0x003EF'i32, 499,
+    0x003F0'i32, 414,
+    0x003F1'i32, 420,
+    0x003F2'i32, 507,
+    0x003F3'i32, 384,
+    0x003F5'i32, 404,
+    0x003F8'i32, 499,
+    0x003FB'i32, 499,
+    0x00461'i32, 499,
+    0x00463'i32, 499,
+    0x00465'i32, 499,
+    0x00467'i32, 499,
+    0x00469'i32, 499,
+    0x0046B'i32, 499,
+    0x0046D'i32, 499,
+    0x0046F'i32, 499,
+    0x00471'i32, 499,
+    0x00473'i32, 499,
+    0x00475'i32, 499,
+    0x00477'i32, 499,
+    0x00479'i32, 499,
+    0x0047B'i32, 499,
+    0x0047D'i32, 499,
+    0x0047F'i32, 499,
+    0x00481'i32, 499,
+    0x0048B'i32, 499,
+    0x0048D'i32, 499,
+    0x0048F'i32, 499,
+    0x00491'i32, 499,
+    0x00493'i32, 499,
+    0x00495'i32, 499,
+    0x00497'i32, 499,
+    0x00499'i32, 499,
+    0x0049B'i32, 499,
+    0x0049D'i32, 499,
+    0x0049F'i32, 499,
+    0x004A1'i32, 499,
+    0x004A3'i32, 499,
+    0x004A5'i32, 499,
+    0x004A7'i32, 499,
+    0x004A9'i32, 499,
+    0x004AB'i32, 499,
+    0x004AD'i32, 499,
+    0x004AF'i32, 499,
+    0x004B1'i32, 499,
+    0x004B3'i32, 499,
+    0x004B5'i32, 499,
+    0x004B7'i32, 499,
+    0x004B9'i32, 499,
+    0x004BB'i32, 499,
+    0x004BD'i32, 499,
+    0x004BF'i32, 499,
+    0x004C2'i32, 499,
+    0x004C4'i32, 499,
+    0x004C6'i32, 499,
+    0x004C8'i32, 499,
+    0x004CA'i32, 499,
+    0x004CC'i32, 499,
+    0x004CE'i32, 499,
+    0x004CF'i32, 485,
+    0x004D1'i32, 499,
+    0x004D3'i32, 499,
+    0x004D5'i32, 499,
+    0x004D7'i32, 499,
+    0x004D9'i32, 499,
+    0x004DB'i32, 499,
+    0x004DD'i32, 499,
+    0x004DF'i32, 499,
+    0x004E1'i32, 499,
+    0x004E3'i32, 499,
+    0x004E5'i32, 499,
+    0x004E7'i32, 499,
+    0x004E9'i32, 499,
+    0x004EB'i32, 499,
+    0x004ED'i32, 499,
+    0x004EF'i32, 499,
+    0x004F1'i32, 499,
+    0x004F3'i32, 499,
+    0x004F5'i32, 499,
+    0x004F7'i32, 499,
+    0x004F9'i32, 499,
+    0x004FB'i32, 499,
+    0x004FD'i32, 499,
+    0x004FF'i32, 499,
+    0x00501'i32, 499,
+    0x00503'i32, 499,
+    0x00505'i32, 499,
+    0x00507'i32, 499,
+    0x00509'i32, 499,
+    0x0050B'i32, 499,
+    0x0050D'i32, 499,
+    0x0050F'i32, 499,
+    0x00511'i32, 499,
+    0x00513'i32, 499,
+    0x00515'i32, 499,
+    0x00517'i32, 499,
+    0x00519'i32, 499,
+    0x0051B'i32, 499,
+    0x0051D'i32, 499,
+    0x0051F'i32, 499,
+    0x00521'i32, 499,
+    0x00523'i32, 499,
+    0x00525'i32, 499,
+    0x00527'i32, 499,
+    0x00529'i32, 499,
+    0x0052B'i32, 499,
+    0x0052D'i32, 499,
+    0x0052F'i32, 499,
+    0x01C80'i32, -5754,
+    0x01C81'i32, -5753,
+    0x01C82'i32, -5744,
+    0x01C85'i32, -5743,
+    0x01C86'i32, -5736,
+    0x01C87'i32, -5681,
+    0x01C88'i32, 35766,
+    0x01D79'i32, 35832,
+    0x01D7D'i32, 4314,
+    0x01D8E'i32, 35884,
+    0x01E01'i32, 499,
+    0x01E03'i32, 499,
+    0x01E05'i32, 499,
+    0x01E07'i32, 499,
+    0x01E09'i32, 499,
+    0x01E0B'i32, 499,
+    0x01E0D'i32, 499,
+    0x01E0F'i32, 499,
+    0x01E11'i32, 499,
+    0x01E13'i32, 499,
+    0x01E15'i32, 499,
+    0x01E17'i32, 499,
+    0x01E19'i32, 499,
+    0x01E1B'i32, 499,
+    0x01E1D'i32, 499,
+    0x01E1F'i32, 499,
+    0x01E21'i32, 499,
+    0x01E23'i32, 499,
+    0x01E25'i32, 499,
+    0x01E27'i32, 499,
+    0x01E29'i32, 499,
+    0x01E2B'i32, 499,
+    0x01E2D'i32, 499,
+    0x01E2F'i32, 499,
+    0x01E31'i32, 499,
+    0x01E33'i32, 499,
+    0x01E35'i32, 499,
+    0x01E37'i32, 499,
+    0x01E39'i32, 499,
+    0x01E3B'i32, 499,
+    0x01E3D'i32, 499,
+    0x01E3F'i32, 499,
+    0x01E41'i32, 499,
+    0x01E43'i32, 499,
+    0x01E45'i32, 499,
+    0x01E47'i32, 499,
+    0x01E49'i32, 499,
+    0x01E4B'i32, 499,
+    0x01E4D'i32, 499,
+    0x01E4F'i32, 499,
+    0x01E51'i32, 499,
+    0x01E53'i32, 499,
+    0x01E55'i32, 499,
+    0x01E57'i32, 499,
+    0x01E59'i32, 499,
+    0x01E5B'i32, 499,
+    0x01E5D'i32, 499,
+    0x01E5F'i32, 499,
+    0x01E61'i32, 499,
+    0x01E63'i32, 499,
+    0x01E65'i32, 499,
+    0x01E67'i32, 499,
+    0x01E69'i32, 499,
+    0x01E6B'i32, 499,
+    0x01E6D'i32, 499,
+    0x01E6F'i32, 499,
+    0x01E71'i32, 499,
+    0x01E73'i32, 499,
+    0x01E75'i32, 499,
+    0x01E77'i32, 499,
+    0x01E79'i32, 499,
+    0x01E7B'i32, 499,
+    0x01E7D'i32, 499,
+    0x01E7F'i32, 499,
+    0x01E81'i32, 499,
+    0x01E83'i32, 499,
+    0x01E85'i32, 499,
+    0x01E87'i32, 499,
+    0x01E89'i32, 499,
+    0x01E8B'i32, 499,
+    0x01E8D'i32, 499,
+    0x01E8F'i32, 499,
+    0x01E91'i32, 499,
+    0x01E93'i32, 499,
+    0x01E95'i32, 499,
+    0x01E9B'i32, 441,
+    0x01EA1'i32, 499,
+    0x01EA3'i32, 499,
+    0x01EA5'i32, 499,
+    0x01EA7'i32, 499,
+    0x01EA9'i32, 499,
+    0x01EAB'i32, 499,
+    0x01EAD'i32, 499,
+    0x01EAF'i32, 499,
+    0x01EB1'i32, 499,
+    0x01EB3'i32, 499,
+    0x01EB5'i32, 499,
+    0x01EB7'i32, 499,
+    0x01EB9'i32, 499,
+    0x01EBB'i32, 499,
+    0x01EBD'i32, 499,
+    0x01EBF'i32, 499,
+    0x01EC1'i32, 499,
+    0x01EC3'i32, 499,
+    0x01EC5'i32, 499,
+    0x01EC7'i32, 499,
+    0x01EC9'i32, 499,
+    0x01ECB'i32, 499,
+    0x01ECD'i32, 499,
+    0x01ECF'i32, 499,
+    0x01ED1'i32, 499,
+    0x01ED3'i32, 499,
+    0x01ED5'i32, 499,
+    0x01ED7'i32, 499,
+    0x01ED9'i32, 499,
+    0x01EDB'i32, 499,
+    0x01EDD'i32, 499,
+    0x01EDF'i32, 499,
+    0x01EE1'i32, 499,
+    0x01EE3'i32, 499,
+    0x01EE5'i32, 499,
+    0x01EE7'i32, 499,
+    0x01EE9'i32, 499,
+    0x01EEB'i32, 499,
+    0x01EED'i32, 499,
+    0x01EEF'i32, 499,
+    0x01EF1'i32, 499,
+    0x01EF3'i32, 499,
+    0x01EF5'i32, 499,
+    0x01EF7'i32, 499,
+    0x01EF9'i32, 499,
+    0x01EFB'i32, 499,
+    0x01EFD'i32, 499,
+    0x01EFF'i32, 499,
+    0x01F51'i32, 508,
+    0x01F53'i32, 508,
+    0x01F55'i32, 508,
+    0x01F57'i32, 508,
+    0x01FB3'i32, 509,
+    0x01FBE'i32, -6705,
+    0x01FC3'i32, 509,
+    0x01FE5'i32, 507,
+    0x01FF3'i32, 509,
+    0x0214E'i32, 472,
+    0x02184'i32, 499,
+    0x02C61'i32, 499,
+    0x02C65'i32, -10295,
+    0x02C66'i32, -10292,
+    0x02C68'i32, 499,
+    0x02C6A'i32, 499,
+    0x02C6C'i32, 499,
+    0x02C73'i32, 499,
+    0x02C76'i32, 499,
+    0x02C81'i32, 499,
+    0x02C83'i32, 499,
+    0x02C85'i32, 499,
+    0x02C87'i32, 499,
+    0x02C89'i32, 499,
+    0x02C8B'i32, 499,
+    0x02C8D'i32, 499,
+    0x02C8F'i32, 499,
+    0x02C91'i32, 499,
+    0x02C93'i32, 499,
+    0x02C95'i32, 499,
+    0x02C97'i32, 499,
+    0x02C99'i32, 499,
+    0x02C9B'i32, 499,
+    0x02C9D'i32, 499,
+    0x02C9F'i32, 499,
+    0x02CA1'i32, 499,
+    0x02CA3'i32, 499,
+    0x02CA5'i32, 499,
+    0x02CA7'i32, 499,
+    0x02CA9'i32, 499,
+    0x02CAB'i32, 499,
+    0x02CAD'i32, 499,
+    0x02CAF'i32, 499,
+    0x02CB1'i32, 499,
+    0x02CB3'i32, 499,
+    0x02CB5'i32, 499,
+    0x02CB7'i32, 499,
+    0x02CB9'i32, 499,
+    0x02CBB'i32, 499,
+    0x02CBD'i32, 499,
+    0x02CBF'i32, 499,
+    0x02CC1'i32, 499,
+    0x02CC3'i32, 499,
+    0x02CC5'i32, 499,
+    0x02CC7'i32, 499,
+    0x02CC9'i32, 499,
+    0x02CCB'i32, 499,
+    0x02CCD'i32, 499,
+    0x02CCF'i32, 499,
+    0x02CD1'i32, 499,
+    0x02CD3'i32, 499,
+    0x02CD5'i32, 499,
+    0x02CD7'i32, 499,
+    0x02CD9'i32, 499,
+    0x02CDB'i32, 499,
+    0x02CDD'i32, 499,
+    0x02CDF'i32, 499,
+    0x02CE1'i32, 499,
+    0x02CE3'i32, 499,
+    0x02CEC'i32, 499,
+    0x02CEE'i32, 499,
+    0x02CF3'i32, 499,
+    0x02D27'i32, -6764,
+    0x02D2D'i32, -6764,
+    0x0A641'i32, 499,
+    0x0A643'i32, 499,
+    0x0A645'i32, 499,
+    0x0A647'i32, 499,
+    0x0A649'i32, 499,
+    0x0A64B'i32, 499,
+    0x0A64D'i32, 499,
+    0x0A64F'i32, 499,
+    0x0A651'i32, 499,
+    0x0A653'i32, 499,
+    0x0A655'i32, 499,
+    0x0A657'i32, 499,
+    0x0A659'i32, 499,
+    0x0A65B'i32, 499,
+    0x0A65D'i32, 499,
+    0x0A65F'i32, 499,
+    0x0A661'i32, 499,
+    0x0A663'i32, 499,
+    0x0A665'i32, 499,
+    0x0A667'i32, 499,
+    0x0A669'i32, 499,
+    0x0A66B'i32, 499,
+    0x0A66D'i32, 499,
+    0x0A681'i32, 499,
+    0x0A683'i32, 499,
+    0x0A685'i32, 499,
+    0x0A687'i32, 499,
+    0x0A689'i32, 499,
+    0x0A68B'i32, 499,
+    0x0A68D'i32, 499,
+    0x0A68F'i32, 499,
+    0x0A691'i32, 499,
+    0x0A693'i32, 499,
+    0x0A695'i32, 499,
+    0x0A697'i32, 499,
+    0x0A699'i32, 499,
+    0x0A69B'i32, 499,
+    0x0A723'i32, 499,
+    0x0A725'i32, 499,
+    0x0A727'i32, 499,
+    0x0A729'i32, 499,
+    0x0A72B'i32, 499,
+    0x0A72D'i32, 499,
+    0x0A72F'i32, 499,
+    0x0A733'i32, 499,
+    0x0A735'i32, 499,
+    0x0A737'i32, 499,
+    0x0A739'i32, 499,
+    0x0A73B'i32, 499,
+    0x0A73D'i32, 499,
+    0x0A73F'i32, 499,
+    0x0A741'i32, 499,
+    0x0A743'i32, 499,
+    0x0A745'i32, 499,
+    0x0A747'i32, 499,
+    0x0A749'i32, 499,
+    0x0A74B'i32, 499,
+    0x0A74D'i32, 499,
+    0x0A74F'i32, 499,
+    0x0A751'i32, 499,
+    0x0A753'i32, 499,
+    0x0A755'i32, 499,
+    0x0A757'i32, 499,
+    0x0A759'i32, 499,
+    0x0A75B'i32, 499,
+    0x0A75D'i32, 499,
+    0x0A75F'i32, 499,
+    0x0A761'i32, 499,
+    0x0A763'i32, 499,
+    0x0A765'i32, 499,
+    0x0A767'i32, 499,
+    0x0A769'i32, 499,
+    0x0A76B'i32, 499,
+    0x0A76D'i32, 499,
+    0x0A76F'i32, 499,
+    0x0A77A'i32, 499,
+    0x0A77C'i32, 499,
+    0x0A77F'i32, 499,
+    0x0A781'i32, 499,
+    0x0A783'i32, 499,
+    0x0A785'i32, 499,
+    0x0A787'i32, 499,
+    0x0A78C'i32, 499,
+    0x0A791'i32, 499,
+    0x0A793'i32, 499,
+    0x0A794'i32, 548,
+    0x0A797'i32, 499,
+    0x0A799'i32, 499,
+    0x0A79B'i32, 499,
+    0x0A79D'i32, 499,
+    0x0A79F'i32, 499,
+    0x0A7A1'i32, 499,
+    0x0A7A3'i32, 499,
+    0x0A7A5'i32, 499,
+    0x0A7A7'i32, 499,
+    0x0A7A9'i32, 499,
+    0x0A7B5'i32, 499,
+    0x0A7B7'i32, 499,
+    0x0A7B9'i32, 499,
+    0x0A7BB'i32, 499,
+    0x0A7BD'i32, 499,
+    0x0A7BF'i32, 499,
+    0x0A7C3'i32, 499,
+    0x0AB53'i32, -428,
+  ]
+
+  toTitleSinglets = [
+    0x001C4'i32, 501,
+    0x001C6'i32, 499,
+    0x001C7'i32, 501,
+    0x001C9'i32, 499,
+    0x001CA'i32, 501,
+    0x001CC'i32, 499,
+    0x001F1'i32, 501,
+    0x001F3'i32, 499,
+  ]
+
+  alphaRanges = [
+    0x00041'i32, 0x0005A'i32,
+    0x00061'i32, 0x0007A'i32,
+    0x000C0'i32, 0x000D6'i32,
+    0x000D8'i32, 0x000F6'i32,
+    0x000F8'i32, 0x002C1'i32,
+    0x002C6'i32, 0x002D1'i32,
+    0x002E0'i32, 0x002E4'i32,
+    0x00370'i32, 0x00374'i32,
+    0x00376'i32, 0x00377'i32,
+    0x0037A'i32, 0x0037D'i32,
+    0x00388'i32, 0x0038A'i32,
+    0x0038E'i32, 0x003A1'i32,
+    0x003A3'i32, 0x003F5'i32,
+    0x003F7'i32, 0x00481'i32,
+    0x0048A'i32, 0x0052F'i32,
+    0x00531'i32, 0x00556'i32,
+    0x00560'i32, 0x00588'i32,
+    0x005D0'i32, 0x005EA'i32,
+    0x005EF'i32, 0x005F2'i32,
+    0x00620'i32, 0x0064A'i32,
+    0x0066E'i32, 0x0066F'i32,
+    0x00671'i32, 0x006D3'i32,
+    0x006E5'i32, 0x006E6'i32,
+    0x006EE'i32, 0x006EF'i32,
+    0x006FA'i32, 0x006FC'i32,
+    0x00712'i32, 0x0072F'i32,
+    0x0074D'i32, 0x007A5'i32,
+    0x007CA'i32, 0x007EA'i32,
+    0x007F4'i32, 0x007F5'i32,
+    0x00800'i32, 0x00815'i32,
+    0x00840'i32, 0x00858'i32,
+    0x00860'i32, 0x0086A'i32,
+    0x008A0'i32, 0x008B4'i32,
+    0x008B6'i32, 0x008BD'i32,
+    0x00904'i32, 0x00939'i32,
+    0x00958'i32, 0x00961'i32,
+    0x00971'i32, 0x00980'i32,
+    0x00985'i32, 0x0098C'i32,
+    0x0098F'i32, 0x00990'i32,
+    0x00993'i32, 0x009A8'i32,
+    0x009AA'i32, 0x009B0'i32,
+    0x009B6'i32, 0x009B9'i32,
+    0x009DC'i32, 0x009DD'i32,
+    0x009DF'i32, 0x009E1'i32,
+    0x009F0'i32, 0x009F1'i32,
+    0x00A05'i32, 0x00A0A'i32,
+    0x00A0F'i32, 0x00A10'i32,
+    0x00A13'i32, 0x00A28'i32,
+    0x00A2A'i32, 0x00A30'i32,
+    0x00A32'i32, 0x00A33'i32,
+    0x00A35'i32, 0x00A36'i32,
+    0x00A38'i32, 0x00A39'i32,
+    0x00A59'i32, 0x00A5C'i32,
+    0x00A72'i32, 0x00A74'i32,
+    0x00A85'i32, 0x00A8D'i32,
+    0x00A8F'i32, 0x00A91'i32,
+    0x00A93'i32, 0x00AA8'i32,
+    0x00AAA'i32, 0x00AB0'i32,
+    0x00AB2'i32, 0x00AB3'i32,
+    0x00AB5'i32, 0x00AB9'i32,
+    0x00AE0'i32, 0x00AE1'i32,
+    0x00B05'i32, 0x00B0C'i32,
+    0x00B0F'i32, 0x00B10'i32,
+    0x00B13'i32, 0x00B28'i32,
+    0x00B2A'i32, 0x00B30'i32,
+    0x00B32'i32, 0x00B33'i32,
+    0x00B35'i32, 0x00B39'i32,
+    0x00B5C'i32, 0x00B5D'i32,
+    0x00B5F'i32, 0x00B61'i32,
+    0x00B85'i32, 0x00B8A'i32,
+    0x00B8E'i32, 0x00B90'i32,
+    0x00B92'i32, 0x00B95'i32,
+    0x00B99'i32, 0x00B9A'i32,
+    0x00B9E'i32, 0x00B9F'i32,
+    0x00BA3'i32, 0x00BA4'i32,
+    0x00BA8'i32, 0x00BAA'i32,
+    0x00BAE'i32, 0x00BB9'i32,
+    0x00C05'i32, 0x00C0C'i32,
+    0x00C0E'i32, 0x00C10'i32,
+    0x00C12'i32, 0x00C28'i32,
+    0x00C2A'i32, 0x00C39'i32,
+    0x00C58'i32, 0x00C5A'i32,
+    0x00C60'i32, 0x00C61'i32,
+    0x00C85'i32, 0x00C8C'i32,
+    0x00C8E'i32, 0x00C90'i32,
+    0x00C92'i32, 0x00CA8'i32,
+    0x00CAA'i32, 0x00CB3'i32,
+    0x00CB5'i32, 0x00CB9'i32,
+    0x00CE0'i32, 0x00CE1'i32,
+    0x00CF1'i32, 0x00CF2'i32,
+    0x00D05'i32, 0x00D0C'i32,
+    0x00D0E'i32, 0x00D10'i32,
+    0x00D12'i32, 0x00D3A'i32,
+    0x00D54'i32, 0x00D56'i32,
+    0x00D5F'i32, 0x00D61'i32,
+    0x00D7A'i32, 0x00D7F'i32,
+    0x00D85'i32, 0x00D96'i32,
+    0x00D9A'i32, 0x00DB1'i32,
+    0x00DB3'i32, 0x00DBB'i32,
+    0x00DC0'i32, 0x00DC6'i32,
+    0x00E01'i32, 0x00E30'i32,
+    0x00E32'i32, 0x00E33'i32,
+    0x00E40'i32, 0x00E46'i32,
+    0x00E81'i32, 0x00E82'i32,
+    0x00E86'i32, 0x00E8A'i32,
+    0x00E8C'i32, 0x00EA3'i32,
+    0x00EA7'i32, 0x00EB0'i32,
+    0x00EB2'i32, 0x00EB3'i32,
+    0x00EC0'i32, 0x00EC4'i32,
+    0x00EDC'i32, 0x00EDF'i32,
+    0x00F40'i32, 0x00F47'i32,
+    0x00F49'i32, 0x00F6C'i32,
+    0x00F88'i32, 0x00F8C'i32,
+    0x01000'i32, 0x0102A'i32,
+    0x01050'i32, 0x01055'i32,
+    0x0105A'i32, 0x0105D'i32,
+    0x01065'i32, 0x01066'i32,
+    0x0106E'i32, 0x01070'i32,
+    0x01075'i32, 0x01081'i32,
+    0x010A0'i32, 0x010C5'i32,
+    0x010D0'i32, 0x010FA'i32,
+    0x010FC'i32, 0x01248'i32,
+    0x0124A'i32, 0x0124D'i32,
+    0x01250'i32, 0x01256'i32,
+    0x0125A'i32, 0x0125D'i32,
+    0x01260'i32, 0x01288'i32,
+    0x0128A'i32, 0x0128D'i32,
+    0x01290'i32, 0x012B0'i32,
+    0x012B2'i32, 0x012B5'i32,
+    0x012B8'i32, 0x012BE'i32,
+    0x012C2'i32, 0x012C5'i32,
+    0x012C8'i32, 0x012D6'i32,
+    0x012D8'i32, 0x01310'i32,
+    0x01312'i32, 0x01315'i32,
+    0x01318'i32, 0x0135A'i32,
+    0x01380'i32, 0x0138F'i32,
+    0x013A0'i32, 0x013F5'i32,
+    0x013F8'i32, 0x013FD'i32,
+    0x01401'i32, 0x0166C'i32,
+    0x0166F'i32, 0x0167F'i32,
+    0x01681'i32, 0x0169A'i32,
+    0x016A0'i32, 0x016EA'i32,
+    0x016F1'i32, 0x016F8'i32,
+    0x01700'i32, 0x0170C'i32,
+    0x0170E'i32, 0x01711'i32,
+    0x01720'i32, 0x01731'i32,
+    0x01740'i32, 0x01751'i32,
+    0x01760'i32, 0x0176C'i32,
+    0x0176E'i32, 0x01770'i32,
+    0x01780'i32, 0x017B3'i32,
+    0x01820'i32, 0x01878'i32,
+    0x01880'i32, 0x01884'i32,
+    0x01887'i32, 0x018A8'i32,
+    0x018B0'i32, 0x018F5'i32,
+    0x01900'i32, 0x0191E'i32,
+    0x01950'i32, 0x0196D'i32,
+    0x01970'i32, 0x01974'i32,
+    0x01980'i32, 0x019AB'i32,
+    0x019B0'i32, 0x019C9'i32,
+    0x01A00'i32, 0x01A16'i32,
+    0x01A20'i32, 0x01A54'i32,
+    0x01B05'i32, 0x01B33'i32,
+    0x01B45'i32, 0x01B4B'i32,
+    0x01B83'i32, 0x01BA0'i32,
+    0x01BAE'i32, 0x01BAF'i32,
+    0x01BBA'i32, 0x01BE5'i32,
+    0x01C00'i32, 0x01C23'i32,
+    0x01C4D'i32, 0x01C4F'i32,
+    0x01C5A'i32, 0x01C7D'i32,
+    0x01C80'i32, 0x01C88'i32,
+    0x01C90'i32, 0x01CBA'i32,
+    0x01CBD'i32, 0x01CBF'i32,
+    0x01CE9'i32, 0x01CEC'i32,
+    0x01CEE'i32, 0x01CF3'i32,
+    0x01CF5'i32, 0x01CF6'i32,
+    0x01D00'i32, 0x01DBF'i32,
+    0x01E00'i32, 0x01F15'i32,
+    0x01F18'i32, 0x01F1D'i32,
+    0x01F20'i32, 0x01F45'i32,
+    0x01F48'i32, 0x01F4D'i32,
+    0x01F50'i32, 0x01F57'i32,
+    0x01F5F'i32, 0x01F7D'i32,
+    0x01F80'i32, 0x01FB4'i32,
+    0x01FB6'i32, 0x01FBC'i32,
+    0x01FC2'i32, 0x01FC4'i32,
+    0x01FC6'i32, 0x01FCC'i32,
+    0x01FD0'i32, 0x01FD3'i32,
+    0x01FD6'i32, 0x01FDB'i32,
+    0x01FE0'i32, 0x01FEC'i32,
+    0x01FF2'i32, 0x01FF4'i32,
+    0x01FF6'i32, 0x01FFC'i32,
+    0x02090'i32, 0x0209C'i32,
+    0x0210A'i32, 0x02113'i32,
+    0x02119'i32, 0x0211D'i32,
+    0x0212A'i32, 0x0212D'i32,
+    0x0212F'i32, 0x02139'i32,
+    0x0213C'i32, 0x0213F'i32,
+    0x02145'i32, 0x02149'i32,
+    0x02183'i32, 0x02184'i32,
+    0x02C00'i32, 0x02C2E'i32,
+    0x02C30'i32, 0x02C5E'i32,
+    0x02C60'i32, 0x02CE4'i32,
+    0x02CEB'i32, 0x02CEE'i32,
+    0x02CF2'i32, 0x02CF3'i32,
+    0x02D00'i32, 0x02D25'i32,
+    0x02D30'i32, 0x02D67'i32,
+    0x02D80'i32, 0x02D96'i32,
+    0x02DA0'i32, 0x02DA6'i32,
+    0x02DA8'i32, 0x02DAE'i32,
+    0x02DB0'i32, 0x02DB6'i32,
+    0x02DB8'i32, 0x02DBE'i32,
+    0x02DC0'i32, 0x02DC6'i32,
+    0x02DC8'i32, 0x02DCE'i32,
+    0x02DD0'i32, 0x02DD6'i32,
+    0x02DD8'i32, 0x02DDE'i32,
+    0x03005'i32, 0x03006'i32,
+    0x03031'i32, 0x03035'i32,
+    0x0303B'i32, 0x0303C'i32,
+    0x03041'i32, 0x03096'i32,
+    0x0309D'i32, 0x0309F'i32,
+    0x030A1'i32, 0x030FA'i32,
+    0x030FC'i32, 0x030FF'i32,
+    0x03105'i32, 0x0312F'i32,
+    0x03131'i32, 0x0318E'i32,
+    0x031A0'i32, 0x031BA'i32,
+    0x031F0'i32, 0x031FF'i32,
+    0x03400'i32, 0x04DB5'i32,
+    0x04E00'i32, 0x09FEF'i32,
+    0x0A000'i32, 0x0A48C'i32,
+    0x0A4D0'i32, 0x0A4FD'i32,
+    0x0A500'i32, 0x0A60C'i32,
+    0x0A610'i32, 0x0A61F'i32,
+    0x0A62A'i32, 0x0A62B'i32,
+    0x0A640'i32, 0x0A66E'i32,
+    0x0A67F'i32, 0x0A69D'i32,
+    0x0A6A0'i32, 0x0A6E5'i32,
+    0x0A717'i32, 0x0A71F'i32,
+    0x0A722'i32, 0x0A788'i32,
+    0x0A78B'i32, 0x0A7BF'i32,
+    0x0A7C2'i32, 0x0A7C6'i32,
+    0x0A7F7'i32, 0x0A801'i32,
+    0x0A803'i32, 0x0A805'i32,
+    0x0A807'i32, 0x0A80A'i32,
+    0x0A80C'i32, 0x0A822'i32,
+    0x0A840'i32, 0x0A873'i32,
+    0x0A882'i32, 0x0A8B3'i32,
+    0x0A8F2'i32, 0x0A8F7'i32,
+    0x0A8FD'i32, 0x0A8FE'i32,
+    0x0A90A'i32, 0x0A925'i32,
+    0x0A930'i32, 0x0A946'i32,
+    0x0A960'i32, 0x0A97C'i32,
+    0x0A984'i32, 0x0A9B2'i32,
+    0x0A9E0'i32, 0x0A9E4'i32,
+    0x0A9E6'i32, 0x0A9EF'i32,
+    0x0A9FA'i32, 0x0A9FE'i32,
+    0x0AA00'i32, 0x0AA28'i32,
+    0x0AA40'i32, 0x0AA42'i32,
+    0x0AA44'i32, 0x0AA4B'i32,
+    0x0AA60'i32, 0x0AA76'i32,
+    0x0AA7E'i32, 0x0AAAF'i32,
+    0x0AAB5'i32, 0x0AAB6'i32,
+    0x0AAB9'i32, 0x0AABD'i32,
+    0x0AADB'i32, 0x0AADD'i32,
+    0x0AAE0'i32, 0x0AAEA'i32,
+    0x0AAF2'i32, 0x0AAF4'i32,
+    0x0AB01'i32, 0x0AB06'i32,
+    0x0AB09'i32, 0x0AB0E'i32,
+    0x0AB11'i32, 0x0AB16'i32,
+    0x0AB20'i32, 0x0AB26'i32,
+    0x0AB28'i32, 0x0AB2E'i32,
+    0x0AB30'i32, 0x0AB5A'i32,
+    0x0AB5C'i32, 0x0AB67'i32,
+    0x0AB70'i32, 0x0ABE2'i32,
+    0x0AC00'i32, 0x0D7A3'i32,
+    0x0D7B0'i32, 0x0D7C6'i32,
+    0x0D7CB'i32, 0x0D7FB'i32,
+    0x0F900'i32, 0x0FA6D'i32,
+    0x0FA70'i32, 0x0FAD9'i32,
+    0x0FB00'i32, 0x0FB06'i32,
+    0x0FB13'i32, 0x0FB17'i32,
+    0x0FB1F'i32, 0x0FB28'i32,
+    0x0FB2A'i32, 0x0FB36'i32,
+    0x0FB38'i32, 0x0FB3C'i32,
+    0x0FB40'i32, 0x0FB41'i32,
+    0x0FB43'i32, 0x0FB44'i32,
+    0x0FB46'i32, 0x0FBB1'i32,
+    0x0FBD3'i32, 0x0FD3D'i32,
+    0x0FD50'i32, 0x0FD8F'i32,
+    0x0FD92'i32, 0x0FDC7'i32,
+    0x0FDF0'i32, 0x0FDFB'i32,
+    0x0FE70'i32, 0x0FE74'i32,
+    0x0FE76'i32, 0x0FEFC'i32,
+    0x0FF21'i32, 0x0FF3A'i32,
+    0x0FF41'i32, 0x0FF5A'i32,
+    0x0FF66'i32, 0x0FFBE'i32,
+    0x0FFC2'i32, 0x0FFC7'i32,
+    0x0FFCA'i32, 0x0FFCF'i32,
+    0x0FFD2'i32, 0x0FFD7'i32,
+    0x0FFDA'i32, 0x0FFDC'i32,
+    0x10000'i32, 0x1000B'i32,
+    0x1000D'i32, 0x10026'i32,
+    0x10028'i32, 0x1003A'i32,
+    0x1003C'i32, 0x1003D'i32,
+    0x1003F'i32, 0x1004D'i32,
+    0x10050'i32, 0x1005D'i32,
+    0x10080'i32, 0x100FA'i32,
+    0x10280'i32, 0x1029C'i32,
+    0x102A0'i32, 0x102D0'i32,
+    0x10300'i32, 0x1031F'i32,
+    0x1032D'i32, 0x10340'i32,
+    0x10342'i32, 0x10349'i32,
+    0x10350'i32, 0x10375'i32,
+    0x10380'i32, 0x1039D'i32,
+    0x103A0'i32, 0x103C3'i32,
+    0x103C8'i32, 0x103CF'i32,
+    0x10400'i32, 0x1049D'i32,
+    0x104B0'i32, 0x104D3'i32,
+    0x104D8'i32, 0x104FB'i32,
+    0x10500'i32, 0x10527'i32,
+    0x10530'i32, 0x10563'i32,
+    0x10600'i32, 0x10736'i32,
+    0x10740'i32, 0x10755'i32,
+    0x10760'i32, 0x10767'i32,
+    0x10800'i32, 0x10805'i32,
+    0x1080A'i32, 0x10835'i32,
+    0x10837'i32, 0x10838'i32,
+    0x1083F'i32, 0x10855'i32,
+    0x10860'i32, 0x10876'i32,
+    0x10880'i32, 0x1089E'i32,
+    0x108E0'i32, 0x108F2'i32,
+    0x108F4'i32, 0x108F5'i32,
+    0x10900'i32, 0x10915'i32,
+    0x10920'i32, 0x10939'i32,
+    0x10980'i32, 0x109B7'i32,
+    0x109BE'i32, 0x109BF'i32,
+    0x10A10'i32, 0x10A13'i32,
+    0x10A15'i32, 0x10A17'i32,
+    0x10A19'i32, 0x10A35'i32,
+    0x10A60'i32, 0x10A7C'i32,
+    0x10A80'i32, 0x10A9C'i32,
+    0x10AC0'i32, 0x10AC7'i32,
+    0x10AC9'i32, 0x10AE4'i32,
+    0x10B00'i32, 0x10B35'i32,
+    0x10B40'i32, 0x10B55'i32,
+    0x10B60'i32, 0x10B72'i32,
+    0x10B80'i32, 0x10B91'i32,
+    0x10C00'i32, 0x10C48'i32,
+    0x10C80'i32, 0x10CB2'i32,
+    0x10CC0'i32, 0x10CF2'i32,
+    0x10D00'i32, 0x10D23'i32,
+    0x10F00'i32, 0x10F1C'i32,
+    0x10F30'i32, 0x10F45'i32,
+    0x10FE0'i32, 0x10FF6'i32,
+    0x11003'i32, 0x11037'i32,
+    0x11083'i32, 0x110AF'i32,
+    0x110D0'i32, 0x110E8'i32,
+    0x11103'i32, 0x11126'i32,
+    0x11150'i32, 0x11172'i32,
+    0x11183'i32, 0x111B2'i32,
+    0x111C1'i32, 0x111C4'i32,
+    0x11200'i32, 0x11211'i32,
+    0x11213'i32, 0x1122B'i32,
+    0x11280'i32, 0x11286'i32,
+    0x1128A'i32, 0x1128D'i32,
+    0x1128F'i32, 0x1129D'i32,
+    0x1129F'i32, 0x112A8'i32,
+    0x112B0'i32, 0x112DE'i32,
+    0x11305'i32, 0x1130C'i32,
+    0x1130F'i32, 0x11310'i32,
+    0x11313'i32, 0x11328'i32,
+    0x1132A'i32, 0x11330'i32,
+    0x11332'i32, 0x11333'i32,
+    0x11335'i32, 0x11339'i32,
+    0x1135D'i32, 0x11361'i32,
+    0x11400'i32, 0x11434'i32,
+    0x11447'i32, 0x1144A'i32,
+    0x11480'i32, 0x114AF'i32,
+    0x114C4'i32, 0x114C5'i32,
+    0x11580'i32, 0x115AE'i32,
+    0x115D8'i32, 0x115DB'i32,
+    0x11600'i32, 0x1162F'i32,
+    0x11680'i32, 0x116AA'i32,
+    0x11700'i32, 0x1171A'i32,
+    0x11800'i32, 0x1182B'i32,
+    0x118A0'i32, 0x118DF'i32,
+    0x119A0'i32, 0x119A7'i32,
+    0x119AA'i32, 0x119D0'i32,
+    0x11A0B'i32, 0x11A32'i32,
+    0x11A5C'i32, 0x11A89'i32,
+    0x11AC0'i32, 0x11AF8'i32,
+    0x11C00'i32, 0x11C08'i32,
+    0x11C0A'i32, 0x11C2E'i32,
+    0x11C72'i32, 0x11C8F'i32,
+    0x11D00'i32, 0x11D06'i32,
+    0x11D08'i32, 0x11D09'i32,
+    0x11D0B'i32, 0x11D30'i32,
+    0x11D60'i32, 0x11D65'i32,
+    0x11D67'i32, 0x11D68'i32,
+    0x11D6A'i32, 0x11D89'i32,
+    0x11EE0'i32, 0x11EF2'i32,
+    0x12000'i32, 0x12399'i32,
+    0x12480'i32, 0x12543'i32,
+    0x13000'i32, 0x1342E'i32,
+    0x14400'i32, 0x14646'i32,
+    0x16800'i32, 0x16A38'i32,
+    0x16A40'i32, 0x16A5E'i32,
+    0x16AD0'i32, 0x16AED'i32,
+    0x16B00'i32, 0x16B2F'i32,
+    0x16B40'i32, 0x16B43'i32,
+    0x16B63'i32, 0x16B77'i32,
+    0x16B7D'i32, 0x16B8F'i32,
+    0x16E40'i32, 0x16E7F'i32,
+    0x16F00'i32, 0x16F4A'i32,
+    0x16F93'i32, 0x16F9F'i32,
+    0x16FE0'i32, 0x16FE1'i32,
+    0x17000'i32, 0x187F7'i32,
+    0x18800'i32, 0x18AF2'i32,
+    0x1B000'i32, 0x1B11E'i32,
+    0x1B150'i32, 0x1B152'i32,
+    0x1B164'i32, 0x1B167'i32,
+    0x1B170'i32, 0x1B2FB'i32,
+    0x1BC00'i32, 0x1BC6A'i32,
+    0x1BC70'i32, 0x1BC7C'i32,
+    0x1BC80'i32, 0x1BC88'i32,
+    0x1BC90'i32, 0x1BC99'i32,
+    0x1D400'i32, 0x1D454'i32,
+    0x1D456'i32, 0x1D49C'i32,
+    0x1D49E'i32, 0x1D49F'i32,
+    0x1D4A5'i32, 0x1D4A6'i32,
+    0x1D4A9'i32, 0x1D4AC'i32,
+    0x1D4AE'i32, 0x1D4B9'i32,
+    0x1D4BD'i32, 0x1D4C3'i32,
+    0x1D4C5'i32, 0x1D505'i32,
+    0x1D507'i32, 0x1D50A'i32,
+    0x1D50D'i32, 0x1D514'i32,
+    0x1D516'i32, 0x1D51C'i32,
+    0x1D51E'i32, 0x1D539'i32,
+    0x1D53B'i32, 0x1D53E'i32,
+    0x1D540'i32, 0x1D544'i32,
+    0x1D54A'i32, 0x1D550'i32,
+    0x1D552'i32, 0x1D6A5'i32,
+    0x1D6A8'i32, 0x1D6C0'i32,
+    0x1D6C2'i32, 0x1D6DA'i32,
+    0x1D6DC'i32, 0x1D6FA'i32,
+    0x1D6FC'i32, 0x1D714'i32,
+    0x1D716'i32, 0x1D734'i32,
+    0x1D736'i32, 0x1D74E'i32,
+    0x1D750'i32, 0x1D76E'i32,
+    0x1D770'i32, 0x1D788'i32,
+    0x1D78A'i32, 0x1D7A8'i32,
+    0x1D7AA'i32, 0x1D7C2'i32,
+    0x1D7C4'i32, 0x1D7CB'i32,
+    0x1E100'i32, 0x1E12C'i32,
+    0x1E137'i32, 0x1E13D'i32,
+    0x1E2C0'i32, 0x1E2EB'i32,
+    0x1E800'i32, 0x1E8C4'i32,
+    0x1E900'i32, 0x1E943'i32,
+    0x1EE00'i32, 0x1EE03'i32,
+    0x1EE05'i32, 0x1EE1F'i32,
+    0x1EE21'i32, 0x1EE22'i32,
+    0x1EE29'i32, 0x1EE32'i32,
+    0x1EE34'i32, 0x1EE37'i32,
+    0x1EE4D'i32, 0x1EE4F'i32,
+    0x1EE51'i32, 0x1EE52'i32,
+    0x1EE61'i32, 0x1EE62'i32,
+    0x1EE67'i32, 0x1EE6A'i32,
+    0x1EE6C'i32, 0x1EE72'i32,
+    0x1EE74'i32, 0x1EE77'i32,
+    0x1EE79'i32, 0x1EE7C'i32,
+    0x1EE80'i32, 0x1EE89'i32,
+    0x1EE8B'i32, 0x1EE9B'i32,
+    0x1EEA1'i32, 0x1EEA3'i32,
+    0x1EEA5'i32, 0x1EEA9'i32,
+    0x1EEAB'i32, 0x1EEBB'i32,
+    0x20000'i32, 0x2A6D6'i32,
+    0x2A700'i32, 0x2B734'i32,
+    0x2B740'i32, 0x2B81D'i32,
+    0x2B820'i32, 0x2CEA1'i32,
+    0x2CEB0'i32, 0x2EBE0'i32,
+    0x2F800'i32, 0x2FA1D'i32,
+  ]
+
+  alphaSinglets = [
+    0x000AA'i32,
+    0x000B5'i32,
+    0x000BA'i32,
+    0x002EC'i32,
+    0x002EE'i32,
+    0x0037F'i32,
+    0x00386'i32,
+    0x0038C'i32,
+    0x00559'i32,
+    0x006D5'i32,
+    0x006FF'i32,
+    0x00710'i32,
+    0x007B1'i32,
+    0x007FA'i32,
+    0x0081A'i32,
+    0x00824'i32,
+    0x00828'i32,
+    0x0093D'i32,
+    0x00950'i32,
+    0x009B2'i32,
+    0x009BD'i32,
+    0x009CE'i32,
+    0x009FC'i32,
+    0x00A5E'i32,
+    0x00ABD'i32,
+    0x00AD0'i32,
+    0x00AF9'i32,
+    0x00B3D'i32,
+    0x00B71'i32,
+    0x00B83'i32,
+    0x00B9C'i32,
+    0x00BD0'i32,
+    0x00C3D'i32,
+    0x00C80'i32,
+    0x00CBD'i32,
+    0x00CDE'i32,
+    0x00D3D'i32,
+    0x00D4E'i32,
+    0x00DBD'i32,
+    0x00E84'i32,
+    0x00EA5'i32,
+    0x00EBD'i32,
+    0x00EC6'i32,
+    0x00F00'i32,
+    0x0103F'i32,
+    0x01061'i32,
+    0x0108E'i32,
+    0x010C7'i32,
+    0x010CD'i32,
+    0x01258'i32,
+    0x012C0'i32,
+    0x017D7'i32,
+    0x017DC'i32,
+    0x018AA'i32,
+    0x01AA7'i32,
+    0x01CFA'i32,
+    0x01F59'i32,
+    0x01F5B'i32,
+    0x01F5D'i32,
+    0x01FBE'i32,
+    0x02071'i32,
+    0x0207F'i32,
+    0x02102'i32,
+    0x02107'i32,
+    0x02115'i32,
+    0x02124'i32,
+    0x02126'i32,
+    0x02128'i32,
+    0x0214E'i32,
+    0x02D27'i32,
+    0x02D2D'i32,
+    0x02D6F'i32,
+    0x02E2F'i32,
+    0x0A8FB'i32,
+    0x0A9CF'i32,
+    0x0AA7A'i32,
+    0x0AAB1'i32,
+    0x0AAC0'i32,
+    0x0AAC2'i32,
+    0x0FB1D'i32,
+    0x0FB3E'i32,
+    0x10808'i32,
+    0x1083C'i32,
+    0x10A00'i32,
+    0x10F27'i32,
+    0x11144'i32,
+    0x11176'i32,
+    0x111DA'i32,
+    0x111DC'i32,
+    0x11288'i32,
+    0x1133D'i32,
+    0x11350'i32,
+    0x1145F'i32,
+    0x114C7'i32,
+    0x11644'i32,
+    0x116B8'i32,
+    0x118FF'i32,
+    0x119E1'i32,
+    0x119E3'i32,
+    0x11A00'i32,
+    0x11A3A'i32,
+    0x11A50'i32,
+    0x11A9D'i32,
+    0x11C40'i32,
+    0x11D46'i32,
+    0x11D98'i32,
+    0x16F50'i32,
+    0x16FE3'i32,
+    0x1D4A2'i32,
+    0x1D4BB'i32,
+    0x1D546'i32,
+    0x1E14E'i32,
+    0x1E94B'i32,
+    0x1EE24'i32,
+    0x1EE27'i32,
+    0x1EE39'i32,
+    0x1EE3B'i32,
+    0x1EE42'i32,
+    0x1EE47'i32,
+    0x1EE49'i32,
+    0x1EE4B'i32,
+    0x1EE54'i32,
+    0x1EE57'i32,
+    0x1EE59'i32,
+    0x1EE5B'i32,
+    0x1EE5D'i32,
+    0x1EE5F'i32,
+    0x1EE64'i32,
+    0x1EE7E'i32,
+  ]
+
+  spaceRanges = [
+    0x00009'i32, 0x0000D'i32,
+    0x00020'i32, 0x00020'i32,
+    0x00085'i32, 0x00085'i32,
+    0x000A0'i32, 0x000A0'i32,
+    0x01680'i32, 0x01680'i32,
+    0x02000'i32, 0x0200A'i32,
+    0x02028'i32, 0x02029'i32,
+    0x0202F'i32, 0x0202F'i32,
+    0x0205F'i32, 0x0205F'i32,
+    0x03000'i32, 0x03000'i32,
+  ]
+
+  unicodeSpaces = [
+    Rune 0x00009,
+    Rune 0x0000A,
+    Rune 0x0000B,
+    Rune 0x0000C,
+    Rune 0x0000D,
+    Rune 0x00020,
+    Rune 0x00085,
+    Rune 0x000A0,
+    Rune 0x01680,
+    Rune 0x02000,
+    Rune 0x02001,
+    Rune 0x02002,
+    Rune 0x02003,
+    Rune 0x02004,
+    Rune 0x02005,
+    Rune 0x02006,
+    Rune 0x02007,
+    Rune 0x02008,
+    Rune 0x02009,
+    Rune 0x0200A,
+    Rune 0x02028,
+    Rune 0x02029,
+    Rune 0x0202F,
+    Rune 0x0205F,
+    Rune 0x03000,
+  ]
+
diff --git a/lib/pure/ioselects/ioselectors_epoll.nim b/lib/pure/ioselects/ioselectors_epoll.nim
index 36145abc7..10658b78e 100644
--- a/lib/pure/ioselects/ioselectors_epoll.nim
+++ b/lib/pure/ioselects/ioselectors_epoll.nim
@@ -9,7 +9,7 @@
 
 # This module implements Linux epoll().
 
-import posix, times, epoll
+import std/[posix, times, epoll]
 
 # Maximum number of events that can be returned
 const MAX_EPOLL_EVENTS = 64
@@ -53,16 +53,18 @@ when hasThreadSupport:
     SelectorImpl[T] = object
       epollFD: cint
       maxFD: int
+      numFD: int
       fds: ptr SharedArray[SelectorKey[T]]
-      count: int
+      count*: int
     Selector*[T] = ptr SelectorImpl[T]
 else:
   type
     SelectorImpl[T] = object
       epollFD: cint
       maxFD: int
+      numFD: int
       fds: seq[SelectorKey[T]]
-      count: int
+      count*: int
     Selector*[T] = ref SelectorImpl[T]
 type
   SelectEventImpl = object
@@ -70,27 +72,36 @@ type
   SelectEvent* = ptr SelectEventImpl
 
 proc newSelector*[T](): Selector[T] =
+  proc initialNumFD(): int {.inline.} =
+    when defined(nuttx):
+      result = NEPOLL_MAX
+    else:
+      result = 1024
   # Retrieve the maximum fd count (for current OS) via getrlimit()
-  var a = RLimit()
-  if getrlimit(posix.RLIMIT_NOFILE, a) != 0:
-    raiseOsError(osLastError())
-  var maxFD = int(a.rlim_max)
+  var maxFD = maxDescriptors()
   doAssert(maxFD > 0)
+  # Start with a reasonable size, checkFd() will grow this on demand
+  let numFD = initialNumFD()
 
-  var epollFD = epoll_create(MAX_EPOLL_EVENTS)
+  var epollFD = epoll_create1(O_CLOEXEC)
   if epollFD < 0:
-    raiseOsError(osLastError())
+    raiseOSError(osLastError())
 
   when hasThreadSupport:
     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 ..< numFD:
+    result.fds[i].ident = InvalidIdent
 
 proc close*[T](s: Selector[T]) =
   let res = posix.close(s.epollFD)
@@ -100,17 +111,10 @@ proc close*[T](s: Selector[T]) =
   if res != 0:
     raiseIOSelectorsError(osLastError())
 
-template clearKey[T](key: ptr SelectorKey[T]) =
-  var empty: T
-  key.ident = 0
-  key.events = {}
-  key.data = empty
-
 proc newSelectEvent*(): SelectEvent =
-  let fdci = eventfd(0, 0)
+  let fdci = eventfd(0, O_CLOEXEC or O_NONBLOCK)
   if fdci == -1:
     raiseIOSelectorsError(osLastError())
-  setNonBlocking(fdci)
   result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl)))
   result.efd = fdci
 
@@ -130,12 +134,22 @@ 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, s.numFD, 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) =
   let fdi = int(fd)
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0, "Descriptor $# already registered" % $fdi)
+  doAssert(s.fds[fdi].ident == InvalidIdent, "Descriptor $# already registered" % $fdi)
   s.setKey(fdi, events, 0, data)
   if events != {}:
     var epv = EpollEvent(events: EPOLLRDHUP)
@@ -152,7 +166,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle, events: set[Event]
   let fdi = int(fd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0,
+  doAssert(pkey.ident != InvalidIdent,
            "Descriptor $# is not registered in the selector!" % $fdi)
   doAssert(pkey.events * maskEvents == {})
   if pkey.events != events:
@@ -180,11 +194,11 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
   let fdi = int(fd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0,
+  doAssert(pkey.ident != InvalidIdent,
            "Descriptor $# is not registered in the selector!" % $fdi)
   if pkey.events != {}:
     when not defined(android):
-      if pkey.events * {Event.Read, Event.Write} != {}:
+      if Event.Read in pkey.events or Event.Write in pkey.events or Event.User in pkey.events:
         var epv = EpollEvent()
         # TODO: Refactor all these EPOLL_CTL_DEL + dec(s.count) into a proc.
         if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) != 0:
@@ -224,7 +238,7 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
         if posix.close(cint(fdi)) != 0:
           raiseIOSelectorsError(osLastError())
     else:
-      if pkey.events * {Event.Read, Event.Write} != {}:
+      if Event.Read in pkey.events or Event.Write in pkey.events or Event.User in pkey.events:
         var epv = EpollEvent()
         if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) != 0:
           raiseIOSelectorsError(osLastError())
@@ -243,7 +257,7 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) =
   let fdi = int(ev.efd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0, "Event is not registered in the queue!")
+  doAssert(pkey.ident != InvalidIdent, "Event is not registered in the queue!")
   doAssert(Event.User in pkey.events)
   var epv = EpollEvent()
   if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) != 0:
@@ -254,34 +268,33 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) =
 proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
                        data: T): int {.discardable.} =
   var
-    new_ts: Itimerspec
-    old_ts: Itimerspec
-  let fdi = timerfd_create(CLOCK_MONOTONIC, 0).int
+    newTs: Itimerspec
+    oldTs: Itimerspec
+  let fdi = timerfd_create(CLOCK_MONOTONIC, O_CLOEXEC or O_NONBLOCK).int
   if fdi == -1:
     raiseIOSelectorsError(osLastError())
-  setNonBlocking(fdi.cint)
 
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+  doAssert(s.fds[fdi].ident == InvalidIdent)
 
   var events = {Event.Timer}
   var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
   epv.data.u64 = fdi.uint
 
   if oneshot:
-    new_ts.it_interval.tv_sec = posix.Time(0)
-    new_ts.it_interval.tv_nsec = 0
-    new_ts.it_value.tv_sec = posix.Time(timeout div 1_000)
-    new_ts.it_value.tv_nsec = (timeout %% 1_000) * 1_000_000
+    newTs.it_interval.tv_sec = posix.Time(0)
+    newTs.it_interval.tv_nsec = 0
+    newTs.it_value.tv_sec = posix.Time(timeout div 1_000)
+    newTs.it_value.tv_nsec = (timeout %% 1_000) * 1_000_000
     incl(events, Event.Oneshot)
     epv.events = epv.events or EPOLLONESHOT
   else:
-    new_ts.it_interval.tv_sec = posix.Time(timeout div 1000)
-    new_ts.it_interval.tv_nsec = (timeout %% 1_000) * 1_000_000
-    new_ts.it_value.tv_sec = new_ts.it_interval.tv_sec
-    new_ts.it_value.tv_nsec = new_ts.it_interval.tv_nsec
+    newTs.it_interval.tv_sec = posix.Time(timeout div 1000)
+    newTs.it_interval.tv_nsec = (timeout %% 1_000) * 1_000_000
+    newTs.it_value.tv_sec = newTs.it_interval.tv_sec
+    newTs.it_value.tv_nsec = newTs.it_interval.tv_nsec
 
-  if timerfd_settime(fdi.cint, cint(0), new_ts, old_ts) != 0:
+  if timerfd_settime(fdi.cint, cint(0), newTs, oldTs) != 0:
     raiseIOSelectorsError(osLastError())
   if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) != 0:
     raiseIOSelectorsError(osLastError())
@@ -301,13 +314,12 @@ when not defined(android):
     discard sigaddset(nmask, cint(signal))
     blockSignals(nmask, omask)
 
-    let fdi = signalfd(-1, nmask, 0).int
+    let fdi = signalfd(-1, nmask, O_CLOEXEC or O_NONBLOCK).int
     if fdi == -1:
       raiseIOSelectorsError(osLastError())
-    setNonBlocking(fdi.cint)
 
     s.checkFd(fdi)
-    doAssert(s.fds[fdi].ident == 0)
+    doAssert(s.fds[fdi].ident == InvalidIdent)
 
     var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
     epv.data.u64 = fdi.uint
@@ -328,13 +340,12 @@ when not defined(android):
     discard sigaddset(nmask, posix.SIGCHLD)
     blockSignals(nmask, omask)
 
-    let fdi = signalfd(-1, nmask, 0).int
+    let fdi = signalfd(-1, nmask, O_CLOEXEC or O_NONBLOCK).int
     if fdi == -1:
       raiseIOSelectorsError(osLastError())
-    setNonBlocking(fdi.cint)
 
     s.checkFd(fdi)
-    doAssert(s.fds[fdi].ident == 0)
+    doAssert(s.fds[fdi].ident == InvalidIdent)
 
     var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
     epv.data.u64 = fdi.uint
@@ -347,7 +358,7 @@ when not defined(android):
 
 proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
   let fdi = int(ev.efd)
-  doAssert(s.fds[fdi].ident == 0, "Event is already registered in the queue!")
+  doAssert(s.fds[fdi].ident == InvalidIdent, "Event is already registered in the queue!")
   s.setKey(fdi, {Event.User}, 0, data)
   var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
   epv.data.u64 = ev.efd.uint
@@ -356,7 +367,7 @@ proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
   inc(s.count)
 
 proc selectInto*[T](s: Selector[T], timeout: int,
-                    results: var openarray[ReadyKey]): int =
+                    results: var openArray[ReadyKey]): int =
   var
     resTable: array[MAX_EPOLL_EVENTS, EpollEvent]
     maxres = MAX_EPOLL_EVENTS
@@ -365,6 +376,8 @@ proc selectInto*[T](s: Selector[T], timeout: int,
   if maxres > len(results):
     maxres = len(results)
 
+  verifySelectParams(timeout)
+
   let count = epoll_wait(s.epollFD, addr(resTable[0]), maxres.cint,
                          timeout.cint)
   if count < 0:
@@ -381,19 +394,19 @@ proc selectInto*[T](s: Selector[T], timeout: int,
       let fdi = int(resTable[i].data.u64)
       let pevents = resTable[i].events
       var pkey = addr(s.fds[fdi])
-      doAssert(pkey.ident != 0)
+      doAssert(pkey.ident != InvalidIdent)
       var rkey = ReadyKey(fd: fdi, events: {})
 
       if (pevents and EPOLLERR) != 0 or (pevents and EPOLLHUP) != 0:
         if (pevents and EPOLLHUP) != 0:
-          rkey.errorCode = ECONNRESET.OSErrorCode
+          rkey.errorCode = OSErrorCode ECONNRESET
         else:
           # Try reading SO_ERROR from fd.
           var error: cint
-          var size = sizeof(error).SockLen
-          if getsockopt(fdi.SocketHandle, SOL_SOCKET, SO_ERROR, addr(error),
+          var size = SockLen sizeof(error)
+          if getsockopt(SocketHandle fdi, SOL_SOCKET, SO_ERROR, addr(error),
                         addr(size)) == 0'i32:
-            rkey.errorCode = error.OSErrorCode
+            rkey.errorCode = OSErrorCode error
 
         rkey.events.incl(Event.Error)
       if (pevents and EPOLLOUT) != 0:
@@ -482,7 +495,7 @@ template isEmpty*[T](s: Selector[T]): bool =
   (s.count == 0)
 
 proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
-  return s.fds[fd.int].ident != 0
+  return s.fds[fd.int].ident != InvalidIdent
 
 proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
   let fdi = int(fd)
@@ -503,7 +516,7 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
   let fdi = int(fd)
   s.checkFd(fdi)
   if fdi in s:
-    var value = addr(s.getData(fdi))
+    var value = addr(s.fds[fdi].data)
     body
 
 template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
@@ -512,10 +525,10 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
   let fdi = int(fd)
   s.checkFd(fdi)
   if fdi in s:
-    var value = addr(s.getData(fdi))
+    var value = addr(s.fds[fdi].data)
     body1
   else:
     body2
 
 proc getFd*[T](s: Selector[T]): int =
-  return s.epollFd.int
+  return s.epollFD.int
diff --git a/lib/pure/ioselects/ioselectors_kqueue.nim b/lib/pure/ioselects/ioselectors_kqueue.nim
index 10e23c072..513578eda 100644
--- a/lib/pure/ioselects/ioselectors_kqueue.nim
+++ b/lib/pure/ioselects/ioselectors_kqueue.nim
@@ -9,7 +9,7 @@
 
 #  This module implements BSD kqueue().
 
-import posix, times, kqueue
+import std/[posix, times, kqueue, nativesockets]
 
 const
   # Maximum number of events that can be returned.
@@ -27,28 +27,28 @@ when defined(macosx) or defined(freebsd) or defined(dragonfly):
     const MAX_DESCRIPTORS_ID = 29 # KERN_MAXFILESPERPROC (MacOS)
   else:
     const MAX_DESCRIPTORS_ID = 27 # KERN_MAXFILESPERPROC (FreeBSD)
-  proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize,
-              newp: pointer, newplen: csize): cint
+  proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize_t,
+              newp: pointer, newplen: csize_t): cint
        {.importc: "sysctl",header: """#include <sys/types.h>
-                                      #include <sys/sysctl.h>"""}
+                                      #include <sys/sysctl.h>""".}
 elif defined(netbsd) or defined(openbsd):
   # OpenBSD and NetBSD don't have KERN_MAXFILESPERPROC, so we are using
   # KERN_MAXFILES, because KERN_MAXFILES is always bigger,
   # than KERN_MAXFILESPERPROC.
   const MAX_DESCRIPTORS_ID = 7 # KERN_MAXFILES
-  proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize,
-              newp: pointer, newplen: csize): cint
+  proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize_t,
+              newp: pointer, newplen: csize_t): cint
        {.importc: "sysctl",header: """#include <sys/param.h>
-                                      #include <sys/sysctl.h>"""}
+                                      #include <sys/sysctl.h>""".}
 
 when hasThreadSupport:
   type
     SelectorImpl[T] = object
-      kqFD : cint
-      maxFD : int
+      kqFD: cint
+      maxFD: int
       changes: ptr SharedArray[KEvent]
       fds: ptr SharedArray[SelectorKey[T]]
-      count: int
+      count*: int
       changesLock: Lock
       changesSize: int
       changesLength: int
@@ -57,11 +57,11 @@ when hasThreadSupport:
 else:
   type
     SelectorImpl[T] = object
-      kqFD : cint
-      maxFD : int
+      kqFD: cint
+      maxFD: int
       changes: seq[KEvent]
       fds: seq[SelectorKey[T]]
-      count: int
+      count*: int
       sock: cint
     Selector*[T] = ref SelectorImpl[T]
 
@@ -76,13 +76,13 @@ type
 
 proc getUnique[T](s: Selector[T]): int {.inline.} =
   # we create duplicated handles to get unique indexes for our `fds` array.
-  result = posix.fcntl(s.sock, F_DUPFD, s.sock)
+  result = posix.fcntl(s.sock, F_DUPFD_CLOEXEC, s.sock)
   if result == -1:
     raiseIOSelectorsError(osLastError())
 
-proc newSelector*[T](): Selector[T] =
+proc newSelector*[T](): owned(Selector[T]) =
   var maxFD = 0.cint
-  var size = csize(sizeof(cint))
+  var size = csize_t(sizeof(cint))
   var namearr = [1.cint, MAX_DESCRIPTORS_ID.cint]
   # Obtain maximum number of opened file descriptors for process
   if sysctl(addr(namearr[0]), 2, cast[pointer](addr maxFD), addr size,
@@ -96,8 +96,8 @@ proc newSelector*[T](): Selector[T] =
   # we allocating empty socket to duplicate it handle in future, to get unique
   # indexes for `fds` array. This is needed to properly identify
   # {Event.Timer, Event.Signal, Event.Process} events.
-  let usock = posix.socket(posix.AF_INET, posix.SOCK_STREAM,
-                             posix.IPPROTO_TCP).cint
+  let usock = createNativeSocket(posix.AF_INET, posix.SOCK_STREAM,
+                                 posix.IPPROTO_TCP).cint
   if usock == -1:
     let err = osLastError()
     discard posix.close(kqFD)
@@ -114,6 +114,9 @@ proc newSelector*[T](): Selector[T] =
     result.fds = newSeq[SelectorKey[T]](maxFD)
     result.changes = newSeqOfCap[KEvent](MAX_KQUEUE_EVENTS)
 
+  for i in 0 ..< maxFD:
+    result.fds[i].ident = InvalidIdent
+
   result.sock = usock
   result.kqFD = kqFD
   result.maxFD = maxFD.int
@@ -128,12 +131,6 @@ proc close*[T](s: Selector[T]) =
   if res1 != 0 or res2 != 0:
     raiseIOSelectorsError(osLastError())
 
-template clearKey[T](key: ptr SelectorKey[T]) =
-  var empty: T
-  key.ident = 0
-  key.events = {}
-  key.data = empty
-
 proc newSelectEvent*(): SelectEvent =
   var fds: array[2, cint]
   if posix.pipe(fds) != 0:
@@ -197,7 +194,9 @@ when hasThreadSupport:
         if s.changesLength > 0:
           if kevent(s.kqFD, addr(s.changes[0]), cint(s.changesLength),
                     nil, 0, nil) == -1:
-            raiseIOSelectorsError(osLastError())
+            let res = osLastError()
+            if cint(res) != ENOENT: # ignore pipes whose read end is closed
+              raiseIOSelectorsError(res)
           s.changesLength = 0
 else:
   template modifyKQueue[T](s: Selector[T], nident: uint, nfilter: cshort,
@@ -214,14 +213,16 @@ else:
       if length > 0:
         if kevent(s.kqFD, addr(s.changes[0]), length,
                   nil, 0, nil) == -1:
-          raiseIOSelectorsError(osLastError())
+          let res = osLastError()
+          if cint(res) != ENOENT: # ignore pipes whose read end is closed
+            raiseIOSelectorsError(res)
         s.changes.setLen(0)
 
 proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
                         events: set[Event], data: T) =
   let fdi = int(fd)
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+  doAssert(s.fds[fdi].ident == InvalidIdent)
   s.setKey(fdi, events, 0, data)
 
   if events != {}:
@@ -242,7 +243,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
   let fdi = int(fd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0,
+  doAssert(pkey.ident != InvalidIdent,
            "Descriptor $# is not registered in the queue!" % $fdi)
   doAssert(pkey.events * maskEvents == {})
 
@@ -269,7 +270,7 @@ proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
                        data: T): int {.discardable.} =
   let fdi = getUnique(s)
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+  doAssert(s.fds[fdi].ident == InvalidIdent)
 
   let events = if oneshot: {Event.Timer, Event.Oneshot} else: {Event.Timer}
   let flags: cushort = if oneshot: EV_ONESHOT or EV_ADD else: EV_ADD
@@ -291,7 +292,7 @@ proc registerSignal*[T](s: Selector[T], signal: int,
                         data: T): int {.discardable.} =
   let fdi = getUnique(s)
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+  doAssert(s.fds[fdi].ident == InvalidIdent)
 
   s.setKey(fdi, {Event.Signal}, signal, data)
   var nmask, omask: Sigset
@@ -315,7 +316,7 @@ proc registerProcess*[T](s: Selector[T], pid: int,
                          data: T): int {.discardable.} =
   let fdi = getUnique(s)
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+  doAssert(s.fds[fdi].ident == InvalidIdent)
 
   var kflags: cushort = EV_ONESHOT or EV_ADD
   setKey(s, fdi, {Event.Process, Event.Oneshot}, pid, data)
@@ -331,7 +332,7 @@ proc registerProcess*[T](s: Selector[T], pid: int,
 
 proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
   let fdi = ev.rfd.int
-  doAssert(s.fds[fdi].ident == 0, "Event is already registered in the queue!")
+  doAssert(s.fds[fdi].ident == InvalidIdent, "Event is already registered in the queue!")
   setKey(s, fdi, {Event.User}, 0, data)
 
   modifyKQueue(s, fdi.uint, EVFILT_READ, EV_ADD, 0, 0, nil)
@@ -374,7 +375,7 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
   let fdi = int(fd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0,
+  doAssert(pkey.ident != InvalidIdent,
            "Descriptor [" & $fdi & "] is not registered in the queue!")
 
   if pkey.events != {}:
@@ -434,7 +435,7 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) =
   let fdi = int(ev.rfd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0, "Event is not registered in the queue!")
+  doAssert(pkey.ident != InvalidIdent, "Event is not registered in the queue!")
   doAssert(Event.User in pkey.events)
   modifyKQueue(s, uint(fdi), EVFILT_READ, EV_DELETE, 0, 0, nil)
   when not declared(CACHE_EVENTS):
@@ -443,13 +444,15 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) =
   dec(s.count)
 
 proc selectInto*[T](s: Selector[T], timeout: int,
-                    results: var openarray[ReadyKey]): int =
+                    results: var openArray[ReadyKey]): int =
   var
     tv: Timespec
     resTable: array[MAX_KQUEUE_EVENTS, KEvent]
     ptv = addr tv
     maxres = MAX_KQUEUE_EVENTS
 
+  verifySelectParams(timeout)
+
   if timeout != -1:
     if timeout >= 1000:
       tv.tv_sec = posix.Time(timeout div 1_000)
@@ -503,7 +506,7 @@ proc selectInto*[T](s: Selector[T], timeout: int,
 
       if (kevent.flags and EV_ERROR) != 0:
         rkey.events = {Event.Error}
-        rkey.errorCode = kevent.data.OSErrorCode
+        rkey.errorCode = OSErrorCode(kevent.data)
 
       case kevent.filter:
       of EVFILT_READ:
@@ -570,13 +573,16 @@ proc selectInto*[T](s: Selector[T], timeout: int,
         doAssert(true, "Unsupported kqueue filter in the queue!")
 
       if (kevent.flags and EV_EOF) != 0:
+        # TODO this error handling needs to be rethought.
+        # `fflags` can sometimes be `0x80000000` and thus we use 'cast'
+        # here:
         if kevent.fflags != 0:
-          rkey.errorCode = kevent.fflags.OSErrorCode
+          rkey.errorCode = cast[OSErrorCode](kevent.fflags)
         else:
           # This assumes we are dealing with sockets.
           # TODO: For future-proofing it might be a good idea to give the
           #       user access to the raw `kevent`.
-          rkey.errorCode = ECONNRESET.OSErrorCode
+          rkey.errorCode = OSErrorCode(ECONNRESET)
         rkey.events.incl(Event.Error)
 
       results[k] = rkey
@@ -593,7 +599,7 @@ template isEmpty*[T](s: Selector[T]): bool =
   (s.count == 0)
 
 proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
-  return s.fds[fd.int].ident != 0
+  return s.fds[fd.int].ident != InvalidIdent
 
 proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
   let fdi = int(fd)
@@ -614,7 +620,7 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
   let fdi = int(fd)
   s.checkFd(fdi)
   if fdi in s:
-    var value = addr(s.getData(fdi))
+    var value = addr(s.fds[fdi].data)
     body
 
 template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
@@ -623,11 +629,11 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
   let fdi = int(fd)
   s.checkFd(fdi)
   if fdi in s:
-    var value = addr(s.getData(fdi))
+    var value = addr(s.fds[fdi].data)
     body1
   else:
     body2
 
 
 proc getFd*[T](s: Selector[T]): int =
-  return s.kqFD.int
\ No newline at end of file
+  return s.kqFD.int
diff --git a/lib/pure/ioselects/ioselectors_poll.nim b/lib/pure/ioselects/ioselectors_poll.nim
index c36750c8d..7c5347156 100644
--- a/lib/pure/ioselects/ioselectors_poll.nim
+++ b/lib/pure/ioselects/ioselectors_poll.nim
@@ -9,11 +9,17 @@
 
 # This module implements Posix poll().
 
-import posix, times
+import std/[posix, times]
 
 # Maximum number of events that can be returned
 const MAX_POLL_EVENTS = 64
 
+const hasEventFds = defined(zephyr) or defined(nimPollHasEventFds)
+
+when hasEventFds:
+  proc eventfd(count: cuint, flags: cint): cint
+     {.cdecl, importc: "eventfd", header: "<sys/eventfd.h>".}
+
 when hasThreadSupport:
   type
     SelectorImpl[T] = object
@@ -21,7 +27,7 @@ when hasThreadSupport:
       pollcnt: int
       fds: ptr SharedArray[SelectorKey[T]]
       pollfds: ptr SharedArray[TPollFd]
-      count: int
+      count*: int
       lock: Lock
     Selector*[T] = ptr SelectorImpl[T]
 else:
@@ -31,7 +37,7 @@ else:
       pollcnt: int
       fds: seq[SelectorKey[T]]
       pollfds: seq[TPollFd]
-      count: int
+      count*: int
     Selector*[T] = ref SelectorImpl[T]
 
 type
@@ -53,10 +59,7 @@ else:
     body
 
 proc newSelector*[T](): Selector[T] =
-  var a = RLimit()
-  if getrlimit(posix.RLIMIT_NOFILE, a) != 0:
-    raiseIOSelectorsError(osLastError())
-  var maxFD = int(a.rlim_max)
+  var maxFD = maxDescriptors()
 
   when hasThreadSupport:
     result = cast[Selector[T]](allocShared0(sizeof(SelectorImpl[T])))
@@ -70,6 +73,9 @@ proc newSelector*[T](): Selector[T] =
     result.fds = newSeq[SelectorKey[T]](maxFD)
     result.pollfds = newSeq[TPollFd](maxFD)
 
+  for i in 0 ..< maxFD:
+    result.fds[i].ident = InvalidIdent
+
 proc close*[T](s: Selector[T]) =
   when hasThreadSupport:
     deinitLock(s.lock)
@@ -77,12 +83,6 @@ proc close*[T](s: Selector[T]) =
     deallocSharedArray(s.pollfds)
     deallocShared(cast[pointer](s))
 
-template clearKey[T](key: ptr SelectorKey[T]) =
-  var empty: T
-  key.ident = 0
-  key.events = {}
-  key.data = empty
-
 template pollAdd[T](s: Selector[T], sock: cint, events: set[Event]) =
   withPollLock(s):
     var pollev: cshort = 0
@@ -135,7 +135,7 @@ proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
                         events: set[Event], data: T) =
   var fdi = int(fd)
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+  doAssert(s.fds[fdi].ident == InvalidIdent)
   setKey(s, fdi, events, 0, data)
   if events != {}: s.pollAdd(fdi.cint, events)
 
@@ -146,7 +146,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
   let fdi = int(fd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0,
+  doAssert(pkey.ident != InvalidIdent,
            "Descriptor [" & $fdi & "] is not registered in the queue!")
   doAssert(pkey.events * maskEvents == {})
 
@@ -162,7 +162,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
 
 proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
   var fdi = int(ev.rfd)
-  doAssert(s.fds[fdi].ident == 0, "Event is already registered in the queue!")
+  doAssert(s.fds[fdi].ident == InvalidIdent, "Event is already registered in the queue!")
   var events = {Event.User}
   setKey(s, fdi, events, 0, data)
   events.incl(Event.Read)
@@ -172,31 +172,40 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
   let fdi = int(fd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0,
+  doAssert(pkey.ident != InvalidIdent,
            "Descriptor [" & $fdi & "] is not registered in the queue!")
-  pkey.ident = 0
-  pkey.events = {}
-  s.pollRemove(fdi.cint)
+  pkey.ident = InvalidIdent
+  if pkey.events != {}:
+    pkey.events = {}
+    s.pollRemove(fdi.cint)
 
 proc unregister*[T](s: Selector[T], ev: SelectEvent) =
   let fdi = int(ev.rfd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0, "Event is not registered in the queue!")
+  doAssert(pkey.ident != InvalidIdent, "Event is not registered in the queue!")
   doAssert(Event.User in pkey.events)
-  pkey.ident = 0
+  pkey.ident = InvalidIdent
   pkey.events = {}
   s.pollRemove(fdi.cint)
 
 proc newSelectEvent*(): SelectEvent =
-  var fds: array[2, cint]
-  if posix.pipe(fds) != 0:
-    raiseIOSelectorsError(osLastError())
-  setNonBlocking(fds[0])
-  setNonBlocking(fds[1])
-  result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl)))
-  result.rfd = fds[0]
-  result.wfd = fds[1]
+  when not hasEventFds: 
+    var fds: array[2, cint]
+    if posix.pipe(fds) != 0:
+      raiseIOSelectorsError(osLastError())
+    setNonBlocking(fds[0])
+    setNonBlocking(fds[1])
+    result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl)))
+    result.rfd = fds[0]
+    result.wfd = fds[1]
+  else: 
+    let fdci = eventfd(0, posix.O_NONBLOCK)
+    if fdci == -1:
+      raiseIOSelectorsError(osLastError())
+    result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl)))
+    result.rfd = fdci
+    result.wfd = fdci
 
 proc trigger*(ev: SelectEvent) =
   var data: uint64 = 1
@@ -205,17 +214,22 @@ proc trigger*(ev: SelectEvent) =
 
 proc close*(ev: SelectEvent) =
   let res1 = posix.close(ev.rfd)
-  let res2 = posix.close(ev.wfd)
+  let res2 = 
+    when  hasEventFds: 0
+    else: posix.close(ev.wfd)
+
   deallocShared(cast[pointer](ev))
   if res1 != 0 or res2 != 0:
     raiseIOSelectorsError(osLastError())
 
 proc selectInto*[T](s: Selector[T], timeout: int,
-                    results: var openarray[ReadyKey]): int =
+                    results: var openArray[ReadyKey]): int =
   var maxres = MAX_POLL_EVENTS
   if maxres > len(results):
     maxres = len(results)
 
+  verifySelectParams(timeout)
+
   s.withPollLock():
     let count = posix.poll(addr(s.pollfds[0]), Tnfds(s.pollcnt), timeout)
     if count < 0:
@@ -270,7 +284,7 @@ template isEmpty*[T](s: Selector[T]): bool =
   (s.count == 0)
 
 proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
-  return s.fds[fd.int].ident != 0
+  return s.fds[fd.int].ident != InvalidIdent
 
 proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
   let fdi = int(fd)
diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim
index 7ed250307..6c516395b 100644
--- a/lib/pure/ioselects/ioselectors_select.nim
+++ b/lib/pure/ioselects/ioselectors_select.nim
@@ -9,14 +9,14 @@
 
 # This module implements Posix and Windows select().
 
-import times, nativesockets
+import std/[times, nativesockets]
 
 when defined(windows):
-  import winlean
+  import std/winlean
   when defined(gcc):
-    {.passL: "-lws2_32".}
+    {.passl: "-lws2_32".}
   elif defined(vcc):
-    {.passL: "ws2_32.lib".}
+    {.passl: "ws2_32.lib".}
   const platformHeaders = """#include <winsock2.h>
                              #include <windows.h>"""
   const EAGAIN = WSAEWOULDBLOCK
@@ -26,27 +26,27 @@ else:
                              #include <sys/types.h>
                              #include <unistd.h>"""
 type
-  Fdset {.importc: "fd_set", header: platformHeaders, pure, final.} = object
+  FdSet {.importc: "fd_set", header: platformHeaders, pure, final.} = object
 var
   FD_SETSIZE {.importc: "FD_SETSIZE", header: platformHeaders.}: cint
 
-proc IOFD_SET(fd: SocketHandle, fdset: ptr Fdset)
+proc IOFD_SET(fd: SocketHandle, fdset: ptr FdSet)
      {.cdecl, importc: "FD_SET", header: platformHeaders, inline.}
-proc IOFD_CLR(fd: SocketHandle, fdset: ptr Fdset)
+proc IOFD_CLR(fd: SocketHandle, fdset: ptr FdSet)
      {.cdecl, importc: "FD_CLR", header: platformHeaders, inline.}
-proc IOFD_ZERO(fdset: ptr Fdset)
+proc IOFD_ZERO(fdset: ptr FdSet)
      {.cdecl, importc: "FD_ZERO", header: platformHeaders, inline.}
 
 when defined(windows):
-  proc IOFD_ISSET(fd: SocketHandle, fdset: ptr Fdset): cint
+  proc IOFD_ISSET(fd: SocketHandle, fdset: ptr FdSet): cint
        {.stdcall, importc: "FD_ISSET", header: platformHeaders, inline.}
-  proc ioselect(nfds: cint, readFds, writeFds, exceptFds: ptr Fdset,
+  proc ioselect(nfds: cint, readFds, writeFds, exceptFds: ptr FdSet,
                 timeout: ptr Timeval): cint
        {.stdcall, importc: "select", header: platformHeaders.}
 else:
-  proc IOFD_ISSET(fd: SocketHandle, fdset: ptr Fdset): cint
+  proc IOFD_ISSET(fd: SocketHandle, fdset: ptr FdSet): cint
        {.cdecl, importc: "FD_ISSET", header: platformHeaders, inline.}
-  proc ioselect(nfds: cint, readFds, writeFds, exceptFds: ptr Fdset,
+  proc ioselect(nfds: cint, readFds, writeFds, exceptFds: ptr FdSet,
                 timeout: ptr Timeval): cint
        {.cdecl, importc: "select", header: platformHeaders.}
 
@@ -58,7 +58,7 @@ when hasThreadSupport:
       eSet: FdSet
       maxFD: int
       fds: ptr SharedArray[SelectorKey[T]]
-      count: int
+      count*: int
       lock: Lock
     Selector*[T] = ptr SelectorImpl[T]
 else:
@@ -69,7 +69,7 @@ else:
       eSet: FdSet
       maxFD: int
       fds: seq[SelectorKey[T]]
-      count: int
+      count*: int
     Selector*[T] = ref SelectorImpl[T]
 
 type
@@ -99,6 +99,9 @@ proc newSelector*[T](): Selector[T] =
     result = Selector[T]()
     result.fds = newSeq[SelectorKey[T]](FD_SETSIZE)
 
+  for i in 0 ..< FD_SETSIZE:
+    result.fds[i].ident = InvalidIdent
+
   IOFD_ZERO(addr result.rSet)
   IOFD_ZERO(addr result.wSet)
   IOFD_ZERO(addr result.eSet)
@@ -107,11 +110,12 @@ proc close*[T](s: Selector[T]) =
   when hasThreadSupport:
     deallocSharedArray(s.fds)
     deallocShared(cast[pointer](s))
+    deinitLock(s.lock)
 
 when defined(windows):
   proc newSelectEvent*(): SelectEvent =
-    var ssock = newNativeSocket()
-    var wsock = newNativeSocket()
+    var ssock = createNativeSocket()
+    var wsock = createNativeSocket()
     var rsock: SocketHandle = INVALID_SOCKET
     var saddr = Sockaddr_in()
 
@@ -195,7 +199,7 @@ proc setSelectKey[T](s: Selector[T], fd: SocketHandle, events: set[Event],
   var i = 0
   let fdi = int(fd)
   while i < FD_SETSIZE:
-    if s.fds[i].ident == 0:
+    if s.fds[i].ident == InvalidIdent:
       var pkey = addr(s.fds[i])
       pkey.ident = fdi
       pkey.events = events
@@ -221,7 +225,7 @@ proc delKey[T](s: Selector[T], fd: SocketHandle) =
   var i = 0
   while i < FD_SETSIZE:
     if s.fds[i].ident == fd.int:
-      s.fds[i].ident = 0
+      s.fds[i].ident = InvalidIdent
       s.fds[i].events = {}
       s.fds[i].data = empty
       break
@@ -283,7 +287,7 @@ proc unregister*[T](s: Selector[T], fd: SocketHandle|int) =
   s.withSelectLock():
     let fd = fd.SocketHandle
     var pkey = s.getKey(fd)
-    if Event.Read in pkey.events:
+    if Event.Read in pkey.events or Event.User in pkey.events:
       IOFD_CLR(fd, addr s.rSet)
       dec(s.count)
     if Event.Write in pkey.events:
@@ -301,13 +305,18 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) =
     s.delKey(fd)
 
 proc selectInto*[T](s: Selector[T], timeout: int,
-                    results: var openarray[ReadyKey]): int =
+                    results: var openArray[ReadyKey]): int =
   var tv = Timeval()
   var ptv = addr tv
   var rset, wset, eset: FdSet
 
+  verifySelectParams(timeout)
+
   if timeout != -1:
-    tv.tv_sec = timeout.int32 div 1_000
+    when defined(genode) or defined(freertos) or defined(zephyr) or defined(nuttx):
+      tv.tv_sec = posix.Time(timeout div 1_000)
+    else:
+      tv.tv_sec = timeout.int32 div 1_000
     tv.tv_usec = (timeout.int32 %% 1_000) * 1_000
   else:
     ptv = nil
@@ -335,7 +344,7 @@ proc selectInto*[T](s: Selector[T], timeout: int,
     var k = 0
 
     while (i < FD_SETSIZE) and (k < count):
-      if s.fds[i].ident != 0:
+      if s.fds[i].ident != InvalidIdent:
         var flag = false
         var pkey = addr(s.fds[i])
         var rkey = ReadyKey(fd: int(pkey.ident), events: {})
@@ -388,19 +397,6 @@ proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
     for i in 0..<FD_SETSIZE:
       if s.fds[i].ident == fdi:
         return true
-      inc(i)
-
-when hasThreadSupport:
-  template withSelectLock[T](s: Selector[T], body: untyped) =
-    acquire(s.lock)
-    {.locks: [s.lock].}:
-      try:
-        body
-      finally:
-        release(s.lock)
-else:
-  template withSelectLock[T](s: Selector[T], body: untyped) =
-    body
 
 proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
   s.withSelectLock():
@@ -455,4 +451,4 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
 
 
 proc getFd*[T](s: Selector[T]): int =
-  return -1
\ No newline at end of file
+  return -1
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index 1bd53edb7..53fa7553a 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -14,53 +14,124 @@
 ## JSON is based on a subset of the JavaScript Programming Language,
 ## Standard ECMA-262 3rd Edition - December 1999.
 ##
-## Dynamically retrieving fields from JSON
-## =======================================
+## See also
+## ========
+## * `std/parsejson <parsejson.html>`_
+## * `std/jsonutils <jsonutils.html>`_
+## * `std/marshal <marshal.html>`_
+## * `std/jscore <jscore.html>`_
 ##
-## This module allows you to access fields in a parsed JSON object in two
-## different ways, one of them is described in this section.
 ##
-## The ``parseJson`` procedure takes a string containing JSON and returns a
-## ``JsonNode`` object. This is an object variant and it is either a
-## ``JObject``, ``JArray``, ``JString``, ``JInt``, ``JFloat``, ``JBool`` or
-## ``JNull``. You
-## check the kind of this object variant by using the ``kind`` accessor.
+## Overview
+## ========
 ##
-## For a ``JsonNode`` who's kind is ``JObject``, you can acess its fields using
-## the ``[]`` operator. The following example shows how to do this:
+## Parsing JSON
+## ------------
+##
+## JSON often arrives into your program (via an API or a file) as a `string`.
+## The first step is to change it from its serialized form into a nested object
+## structure called a `JsonNode`.
+##
+## The `parseJson` procedure takes a string containing JSON and returns a
+## `JsonNode` object. This is an object variant and it is either a
+## `JObject`, `JArray`, `JString`, `JInt`, `JFloat`, `JBool` or
+## `JNull`. You check the kind of this object variant by using the `kind`
+## accessor.
+##
+## For a `JsonNode` who's kind is `JObject`, you can access its fields using
+## the `[]` operator. The following example shows how to do this:
+##
+##   ```Nim
+##   import std/json
 ##
-## .. code-block:: Nim
 ##   let jsonNode = parseJson("""{"key": 3.14}""")
+##
 ##   doAssert jsonNode.kind == JObject
 ##   doAssert jsonNode["key"].kind == JFloat
+##   ```
 ##
-## Retrieving the value of a JSON node can then be achieved using one of the
-## helper procedures, which include:
+## Reading values
+## --------------
 ##
-## * ``getInt``
-## * ``getFloat``
-## * ``getStr``
-## * ``getBool``
+## Once you have a `JsonNode`, retrieving the values can then be achieved
+## by using one of the helper procedures, which include:
 ##
-## To retrieve the value of ``"key"`` you can do the following:
+## * `getInt`
+## * `getFloat`
+## * `getStr`
+## * `getBool`
+##
+## To retrieve the value of `"key"` you can do the following:
+##
+##   ```Nim
+##   import std/json
+##
+##   let jsonNode = parseJson("""{"key": 3.14}""")
 ##
-## .. code-block:: Nim
 ##   doAssert jsonNode["key"].getFloat() == 3.14
+##   ```
+##
+## **Important:** The `[]` operator will raise an exception when the
+## specified field does not exist.
+##
+## Handling optional keys
+## ----------------------
+##
+## By using the `{}` operator instead of `[]`, it will return `nil`
+## when the field is not found. The `get`-family of procedures will return a
+## type's default value when called on `nil`.
+##
+##   ```Nim
+##   import std/json
+##
+##   let jsonNode = parseJson("{}")
+##
+##   doAssert jsonNode{"nope"}.getInt() == 0
+##   doAssert jsonNode{"nope"}.getFloat() == 0
+##   doAssert jsonNode{"nope"}.getStr() == ""
+##   doAssert jsonNode{"nope"}.getBool() == false
+##   ```
+##
+## Using default values
+## --------------------
+##
+## The `get`-family helpers also accept an additional parameter which allow
+## you to fallback to a default value should the key's values be `null`:
 ##
-## The ``[]`` operator will raise an exception when the specified field does
-## not exist. If you wish to avoid this behaviour you can use the ``{}``
-## operator instead, it will simply return ``nil`` when the field is not found.
-## The ``get``-family of procedures will return a default value when called on
-## ``nil``.
+##   ```Nim
+##   import std/json
 ##
-## Unmarshalling JSON into a type
-## ==============================
+##   let jsonNode = parseJson("""{"key": 3.14, "key2": null}""")
 ##
-## This module allows you to access fields in a parsed JSON object in two
-## different ways, one of them is described in this section.
+##   doAssert jsonNode["key"].getFloat(6.28) == 3.14
+##   doAssert jsonNode["key2"].getFloat(3.14) == 3.14
+##   doAssert jsonNode{"nope"}.getFloat(3.14) == 3.14 # note the {}
+##   ```
 ##
-## This is done using the ``to`` macro. Take a look at
-## `its documentation <#to.m,JsonNode,typedesc>`_ to see an example of its use.
+## Unmarshalling
+## -------------
+##
+## In addition to reading dynamic data, Nim can also unmarshal JSON directly
+## into a type with the `to` macro.
+##
+## Note: Use `Option <options.html#Option>`_ for keys sometimes missing in json
+## responses, and backticks around keys with a reserved keyword as name.
+##
+##   ```Nim
+##   import std/json
+##   import std/options
+##
+##   type
+##     User = object
+##       name: string
+##       age: int
+##       `type`: Option[string]
+##
+##   let userJson = parseJson("""{ "name": "Nim", "age": 12 }""")
+##   let user = to(userJson, User)
+##   if user.`type`.isSome():
+##     assert user.`type`.get() != "robot"
+##   ```
 ##
 ## Creating JSON
 ## =============
@@ -68,28 +139,39 @@
 ## This module can also be used to comfortably create JSON using the `%*`
 ## operator:
 ##
-## .. code-block:: nim
+##   ```nim
+##   import std/json
 ##
 ##   var hisName = "John"
 ##   let herAge = 31
 ##   var j = %*
 ##     [
-##       {
-##         "name": hisName,
-##         "age": 30
-##       },
-##       {
-##         "name": "Susan",
-##         "age": herAge
-##       }
+##       { "name": hisName, "age": 30 },
+##       { "name": "Susan", "age": herAge }
 ##     ]
 ##
-##    var j2 = %* {"name": "Isaac", "books": ["Robot Dreams"]}
-##    j2["details"] = %* {"age":35, "pi":3.1415}
-##    echo j2
+##   var j2 = %* {"name": "Isaac", "books": ["Robot Dreams"]}
+##   j2["details"] = %* {"age":35, "pi":3.1415}
+##   echo j2
+##   ```
+##
+## See also: std/jsonutils for hookable json serialization/deserialization
+## of arbitrary types.
+
+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 std/[hashes, tables, strutils, lexbase, streams, macros, parsejson]
 
-import
-  hashes, tables, strutils, lexbase, streams, unicode, macros, parsejson
+import std/options # xxx remove this dependency using same approach as https://github.com/nim-lang/Nim/pull/14563
+import std/private/since
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions, formatfloat]
 
 export
   tables.`$`
@@ -97,12 +179,7 @@ export
 export
   parsejson.JsonEventKind, parsejson.JsonError, JsonParser, JsonKindError,
   open, close, str, getInt, getFloat, kind, getColumn, getLine, getFilename,
-  errorMsg, errorMsgExpected, next, JsonParsingError, raiseParseErr
-
-when defined(nimJsonGet):
-  {.pragma: deprecatedGet, deprecated.}
-else:
-  {.pragma: deprecatedGet.}
+  errorMsg, errorMsgExpected, next, JsonParsingError, raiseParseErr, nimIdentNormalize
 
 type
   JsonNodeKind* = enum ## possible JSON node types
@@ -116,6 +193,8 @@ type
 
   JsonNode* = ref JsonNodeObj ## JSON node
   JsonNodeObj* {.acyclic.} = object
+    isUnquoted: bool # the JString was a number-like token and
+                     # so shouldn't be quoted
     case kind*: JsonNodeKind
     of JString:
       str*: string
@@ -132,114 +211,94 @@ type
     of JArray:
       elems*: seq[JsonNode]
 
+const DepthLimit = 1000
+
 proc newJString*(s: string): JsonNode =
   ## Creates a new `JString JsonNode`.
-  new(result)
-  result.kind = JString
-  result.str = s
+  result = JsonNode(kind: JString, str: s)
 
-proc newJStringMove(s: string): JsonNode =
-  new(result)
-  result.kind = JString
-  shallowCopy(result.str, s)
+proc newJRawNumber(s: string): JsonNode =
+  ## Creates a "raw JS number", that is a number that does not
+  ## fit into Nim's `BiggestInt` field. This is really a `JString`
+  ## with the additional information that it should be converted back
+  ## to the string representation without the quotes.
+  result = JsonNode(kind: JString, str: s, isUnquoted: true)
 
 proc newJInt*(n: BiggestInt): JsonNode =
   ## Creates a new `JInt JsonNode`.
-  new(result)
-  result.kind = JInt
-  result.num  = n
+  result = JsonNode(kind: JInt, num: n)
 
 proc newJFloat*(n: float): JsonNode =
   ## Creates a new `JFloat JsonNode`.
-  new(result)
-  result.kind = JFloat
-  result.fnum  = n
+  result = JsonNode(kind: JFloat, fnum: n)
 
 proc newJBool*(b: bool): JsonNode =
   ## Creates a new `JBool JsonNode`.
-  new(result)
-  result.kind = JBool
-  result.bval = b
+  result = JsonNode(kind: JBool, bval: b)
 
 proc newJNull*(): JsonNode =
   ## Creates a new `JNull JsonNode`.
-  new(result)
+  result = JsonNode(kind: JNull)
 
 proc newJObject*(): JsonNode =
   ## Creates a new `JObject JsonNode`
-  new(result)
-  result.kind = JObject
-  result.fields = initOrderedTable[string, JsonNode](4)
+  result = JsonNode(kind: JObject, fields: initOrderedTable[string, JsonNode](2))
 
 proc newJArray*(): JsonNode =
   ## Creates a new `JArray JsonNode`
-  new(result)
-  result.kind = JArray
-  result.elems = @[]
+  result = JsonNode(kind: JArray, elems: @[])
 
 proc getStr*(n: JsonNode, default: string = ""): string =
   ## Retrieves the string value of a `JString JsonNode`.
   ##
-  ## Returns ``default`` if ``n`` is not a ``JString``, or if ``n`` is nil.
+  ## Returns `default` if `n` is not a `JString`, or if `n` is nil.
   if n.isNil or n.kind != JString: return default
   else: return n.str
 
 proc getInt*(n: JsonNode, default: int = 0): int =
   ## Retrieves the int value of a `JInt JsonNode`.
   ##
-  ## Returns ``default`` if ``n`` is not a ``JInt``, or if ``n`` is nil.
+  ## Returns `default` if `n` is not a `JInt`, or if `n` is nil.
   if n.isNil or n.kind != JInt: return default
   else: return int(n.num)
 
 proc getBiggestInt*(n: JsonNode, default: BiggestInt = 0): BiggestInt =
   ## Retrieves the BiggestInt value of a `JInt JsonNode`.
   ##
-  ## Returns ``default`` if ``n`` is not a ``JInt``, or if ``n`` is nil.
+  ## Returns `default` if `n` is not a `JInt`, or if `n` is nil.
   if n.isNil or n.kind != JInt: return default
   else: return n.num
 
-proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt {.deprecated: "use getInt or getBiggestInt instead".} =
-  ## **Deprecated since v0.18.2:** use ``getInt`` or ``getBiggestInt`` instead.
-  getBiggestInt(n, default)
-
 proc getFloat*(n: JsonNode, default: float = 0.0): float =
   ## Retrieves the float value of a `JFloat JsonNode`.
   ##
-  ## Returns ``default`` if ``n`` is not a ``JFloat`` or ``JInt``, or if ``n`` is nil.
+  ## Returns `default` if `n` is not a `JFloat` or `JInt`, or if `n` is nil.
   if n.isNil: return default
   case n.kind
   of JFloat: return n.fnum
   of JInt: return float(n.num)
   else: return default
 
-proc getFNum*(n: JsonNode, default: float = 0.0): float {.deprecated: "use getFloat instead".} =
-  ## **Deprecated since v0.18.2:** use ``getFloat`` instead.
-  getFloat(n, default)
-
 proc getBool*(n: JsonNode, default: bool = false): bool =
   ## Retrieves the bool value of a `JBool JsonNode`.
   ##
-  ## Returns ``default`` if ``n`` is not a ``JBool``, or if ``n`` is nil.
+  ## Returns `default` if `n` is not a `JBool`, or if `n` is nil.
   if n.isNil or n.kind != JBool: return default
   else: return n.bval
 
-proc getBVal*(n: JsonNode, default: bool = false): bool {.deprecated: "use getBool instead".} =
-  ## **Deprecated since v0.18.2:** use ``getBool`` instead.
-  getBool(n, default)
-
 proc getFields*(n: JsonNode,
-    default = initOrderedTable[string, JsonNode](4)):
+    default = initOrderedTable[string, JsonNode](2)):
         OrderedTable[string, JsonNode] =
   ## Retrieves the key, value pairs of a `JObject JsonNode`.
   ##
-  ## Returns ``default`` if ``n`` is not a ``JObject``, or if ``n`` is nil.
+  ## Returns `default` if `n` is not a `JObject`, or if `n` is nil.
   if n.isNil or n.kind != JObject: return default
   else: return n.fields
 
 proc getElems*(n: JsonNode, default: seq[JsonNode] = @[]): seq[JsonNode] =
   ## Retrieves the array of a `JArray JsonNode`.
   ##
-  ## Returns ``default`` if ``n`` is not a ``JArray``, or if ``n`` is nil.
+  ## Returns `default` if `n` is not a `JArray`, or if `n` is nil.
   if n.isNil or n.kind != JArray: return default
   else: return n.elems
 
@@ -255,32 +314,50 @@ proc add*(obj: JsonNode, key: string, val: JsonNode) =
 
 proc `%`*(s: string): JsonNode =
   ## Generic constructor for JSON data. Creates a new `JString JsonNode`.
-  new(result)
-  if s.isNil: return
-  result.kind = JString
-  result.str = s
+  result = JsonNode(kind: JString, str: s)
+
+proc `%`*(n: uint): JsonNode =
+  ## Generic constructor for JSON data. Creates a new `JInt JsonNode`.
+  if n > cast[uint](int.high):
+    result = newJRawNumber($n)
+  else:
+    result = JsonNode(kind: JInt, num: BiggestInt(n))
+
+proc `%`*(n: int): JsonNode =
+  ## Generic constructor for JSON data. Creates a new `JInt JsonNode`.
+  result = JsonNode(kind: JInt, num: n)
+
+proc `%`*(n: BiggestUInt): JsonNode =
+  ## Generic constructor for JSON data. Creates a new `JInt JsonNode`.
+  if n > cast[BiggestUInt](BiggestInt.high):
+    result = newJRawNumber($n)
+  else:
+    result = JsonNode(kind: JInt, num: BiggestInt(n))
 
 proc `%`*(n: BiggestInt): JsonNode =
   ## Generic constructor for JSON data. Creates a new `JInt JsonNode`.
-  new(result)
-  result.kind = JInt
-  result.num  = n
+  result = JsonNode(kind: JInt, num: n)
 
 proc `%`*(n: float): JsonNode =
   ## Generic constructor for JSON data. Creates a new `JFloat JsonNode`.
-  new(result)
-  result.kind = JFloat
-  result.fnum  = n
+  runnableExamples:
+    assert $(%[NaN, Inf, -Inf, 0.0, -0.0, 1.0, 1e-2]) == """["nan","inf","-inf",0.0,-0.0,1.0,0.01]"""
+    assert (%NaN).kind == JString
+    assert (%0.0).kind == JFloat
+  # for those special cases, we could also have used `newJRawNumber` but then
+  # it would've been inconsisten with the case of `parseJson` vs `%` for representing them.
+  if n != n: newJString("nan")
+  elif n == Inf: newJString("inf")
+  elif n == -Inf: newJString("-inf")
+  else: JsonNode(kind: JFloat, fnum: n)
 
 proc `%`*(b: bool): JsonNode =
   ## Generic constructor for JSON data. Creates a new `JBool JsonNode`.
-  new(result)
-  result.kind = JBool
-  result.bval = b
+  result = JsonNode(kind: JBool, bval: b)
 
 proc `%`*(keyVals: openArray[tuple[key: string, val: JsonNode]]): JsonNode =
   ## Generic constructor for JSON data. Creates a new `JObject JsonNode`
-  if keyvals.len == 0: return newJArray()
+  if keyVals.len == 0: return newJArray()
   result = newJObject()
   for key, val in items(keyVals): result.fields[key] = val
 
@@ -291,21 +368,36 @@ proc `%`*[T](elements: openArray[T]): JsonNode =
   result = newJArray()
   for elem in elements: result.add(%elem)
 
+proc `%`*[T](table: Table[string, T]|OrderedTable[string, T]): JsonNode =
+  ## Generic constructor for JSON data. Creates a new `JObject JsonNode`.
+  result = newJObject()
+  for k, v in table: result[k] = %v
+
+proc `%`*[T](opt: Option[T]): JsonNode =
+  ## Generic constructor for JSON data. Creates a new `JNull JsonNode`
+  ## if `opt` is empty, otherwise it delegates to the underlying value.
+  if opt.isSome: %opt.get else: newJNull()
+
 when false:
   # For 'consistency' we could do this, but that only pushes people further
   # into that evil comfort zone where they can use Nim without understanding it
   # causing problems later on.
   proc `%`*(elements: set[bool]): JsonNode =
     ## Generic constructor for JSON data. Creates a new `JObject JsonNode`.
-    ## This can only be used with the empty set ``{}`` and is supported
-    ## to prevent the gotcha ``%*{}`` which used to produce an empty
+    ## This can only be used with the empty set `{}` and is supported
+    ## to prevent the gotcha `%*{}` which used to produce an empty
     ## JSON array.
     result = newJObject()
     assert false notin elements, "usage error: only empty sets allowed"
     assert true notin elements, "usage error: only empty sets allowed"
 
-proc `%`*(o: object): JsonNode =
-  ## Generic constructor for JSON data. Creates a new `JObject JsonNode`
+proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} =
+  ## Sets a field from a `JObject`.
+  assert(obj.kind == JObject)
+  obj.fields[key] = val
+
+proc `%`*[T: object](o: T): JsonNode =
+  ## Construct JsonNode from tuples and objects.
   result = newJObject()
   for k, v in o.fieldPairs: result[k] = %v
 
@@ -318,23 +410,23 @@ proc `%`*(o: ref object): JsonNode =
 
 proc `%`*(o: enum): JsonNode =
   ## Construct a JsonNode that represents the specified enum value as a
-  ## string. Creates a new ``JString JsonNode``.
+  ## string. Creates a new `JString JsonNode`.
   result = %($o)
 
-proc toJson(x: NimNode): NimNode {.compiletime.} =
+proc toJsonImpl(x: NimNode): NimNode =
   case x.kind
   of nnkBracket: # array
     if x.len == 0: return newCall(bindSym"newJArray")
     result = newNimNode(nnkBracket)
     for i in 0 ..< x.len:
-      result.add(toJson(x[i]))
+      result.add(toJsonImpl(x[i]))
     result = newCall(bindSym("%", brOpen), result)
   of nnkTableConstr: # object
     if x.len == 0: return newCall(bindSym"newJObject")
     result = newNimNode(nnkTableConstr)
     for i in 0 ..< x.len:
       x[i].expectKind nnkExprColonExpr
-      result.add newTree(nnkExprColonExpr, x[i][0], toJson(x[i][1]))
+      result.add newTree(nnkExprColonExpr, x[i][0], toJsonImpl(x[i][1]))
     result = newCall(bindSym("%", brOpen), result)
   of nnkCurly: # empty object
     x.expectLen(0)
@@ -342,7 +434,7 @@ proc toJson(x: NimNode): NimNode {.compiletime.} =
   of nnkNilLit:
     result = newCall(bindSym"newJNull")
   of nnkPar:
-    if x.len == 1: result = toJson(x[0])
+    if x.len == 1: result = toJsonImpl(x[0])
     else: result = newCall(bindSym("%", brOpen), x)
   else:
     result = newCall(bindSym("%", brOpen), x)
@@ -350,9 +442,9 @@ proc toJson(x: NimNode): NimNode {.compiletime.} =
 macro `%*`*(x: untyped): untyped =
   ## Convert an expression to a JsonNode directly, without having to specify
   ## `%` for every element.
-  result = toJson(x)
+  result = toJsonImpl(x)
 
-proc `==`* (a, b: JsonNode): bool =
+proc `==`*(a, b: JsonNode): bool {.noSideEffect, raises: [].} =
   ## Check two nodes for equality
   if a.isNil:
     if b.isNil: return true
@@ -372,19 +464,25 @@ proc `==`* (a, b: JsonNode): bool =
     of JNull:
       result = true
     of JArray:
-      result = a.elems == b.elems
+      {.cast(raises: []).}: # bug #19303
+        result = a.elems == b.elems
     of JObject:
-     # we cannot use OrderedTable's equality here as
-     # the order does not matter for equality here.
-     if a.fields.len != b.fields.len: return false
-     for key, val in a.fields:
-       if not b.fields.hasKey(key): return false
-       if b.fields[key] != val: return false
-     result = true
+      # we cannot use OrderedTable's equality here as
+      # the order does not matter for equality here.
+      if a.fields.len != b.fields.len: return false
+      for key, val in a.fields:
+        if not b.fields.hasKey(key): return false
+        {.cast(raises: []).}:
+          when defined(nimHasEffectsOf):
+            {.noSideEffect.}:
+              if b.fields[key] != val: return false
+          else:
+            if b.fields[key] != val: return false
+      result = true
 
 proc hash*(n: OrderedTable[string, JsonNode]): Hash {.noSideEffect.}
 
-proc hash*(n: JsonNode): Hash =
+proc hash*(n: JsonNode): Hash {.noSideEffect.} =
   ## Compute the hash for a JSON node
   case n.kind
   of JArray:
@@ -416,13 +514,9 @@ proc len*(n: JsonNode): int =
   of JObject: result = n.fields.len
   else: discard
 
-proc `[]`*(node: JsonNode, name: string): JsonNode {.inline, deprecatedGet.} =
+proc `[]`*(node: JsonNode, name: string): JsonNode {.inline.} =
   ## Gets a field from a `JObject`, which must not be nil.
   ## If the value at `name` does not exist, raises KeyError.
-  ##
-  ## **Note:** The behaviour of this procedure changed in version 0.14.0. To
-  ## get a list of usages and to restore the old behaviour of this procedure,
-  ## compile with the ``-d:nimJsonGet`` flag.
   assert(not isNil(node))
   assert(node.kind == JObject)
   when defined(nimJsonGet):
@@ -437,6 +531,37 @@ proc `[]`*(node: JsonNode, index: int): JsonNode {.inline.} =
   assert(node.kind == JArray)
   return node.elems[index]
 
+proc `[]`*(node: JsonNode, index: BackwardsIndex): JsonNode {.inline, since: (1, 5, 1).} =
+  ## Gets the node at `array.len-i` in an array through the `^` operator.
+  ##
+  ## i.e. `j[^i]` is a shortcut for `j[j.len-i]`.
+  runnableExamples:
+    let
+      j = parseJson("[1,2,3,4,5]")
+
+    doAssert j[^1].getInt == 5
+    doAssert j[^2].getInt == 4
+
+  `[]`(node, node.len - int(index))
+
+proc `[]`*[U, V](a: JsonNode, x: HSlice[U, V]): JsonNode =
+  ## Slice operation for JArray.
+  ##
+  ## Returns the inclusive range `[a[x.a], a[x.b]]`:
+  runnableExamples:
+    import std/json
+    let arr = %[0,1,2,3,4,5]
+    doAssert arr[2..4] == %[2,3,4]
+    doAssert arr[2..^2] == %[2,3,4]
+    doAssert arr[^4..^2] == %[2,3,4]
+
+  assert(a.kind == JArray)
+  result = newJArray()
+  let xa = (when x.a is BackwardsIndex: a.len - int(x.a) else: int(x.a))
+  let L = (when x.b is BackwardsIndex: a.len - int(x.b) else: int(x.b)) - xa + 1
+  for i in 0..<L:
+    result.add(a[i + xa])
+
 proc hasKey*(node: JsonNode, key: string): bool =
   ## Checks if `key` exists in `node`.
   assert(node.kind == JObject)
@@ -452,25 +577,18 @@ proc contains*(node: JsonNode, val: JsonNode): bool =
   assert(node.kind == JArray)
   find(node.elems, val) >= 0
 
-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
+  ## keys do not exist, returns `nil`. Also returns `nil` if one of the
   ## intermediate data structures is not an object.
   ##
   ## This proc can be used to create tree structures on the
   ## fly (sometimes called `autovivification`:idx:):
   ##
-  ## .. code-block:: nim
-  ##   myjson{"parent", "child", "grandchild"} = newJInt(1)
-  ##
+  runnableExamples:
+    var myjson = %* {"parent": {"child": {"grandchild": 1}}}
+    doAssert myjson{"parent", "child", "grandchild"} == newJInt(1)
+
   result = node
   for key in keys:
     if isNil(result) or result.kind != JObject:
@@ -479,7 +597,7 @@ proc `{}`*(node: JsonNode, keys: varargs[string]): JsonNode =
 
 proc `{}`*(node: JsonNode, index: varargs[int]): JsonNode =
   ## Traverses the node and gets the given value. If any of the
-  ## indexes do not exist, returns ``nil``. Also returns ``nil`` if one of the
+  ## indexes do not exist, returns `nil`. Also returns `nil` if one of the
   ## intermediate data structures is not an array.
   result = node
   for i in index:
@@ -493,11 +611,14 @@ proc getOrDefault*(node: JsonNode, key: string): JsonNode =
   if not isNil(node) and node.kind == JObject:
     result = node.fields.getOrDefault(key)
 
-template simpleGetOrDefault*{`{}`(node, [key])}(node: JsonNode, key: string): JsonNode = node.getOrDefault(key)
+proc `{}`*(node: JsonNode, key: string): JsonNode =
+  ## Gets a field from a `node`. If `node` is nil or not an object or
+  ## value at `key` does not exist, returns nil
+  node.getOrDefault(key)
 
 proc `{}=`*(node: JsonNode, keys: varargs[string], value: JsonNode) =
   ## Traverses the node and tries to set the value at the given location
-  ## to ``value``. If any of the keys are missing, they are added.
+  ## to `value`. If any of the keys are missing, they are added.
   var node = node
   for i in 0..(keys.len-2):
     if not node.hasKey(keys[i]):
@@ -506,17 +627,18 @@ proc `{}=`*(node: JsonNode, keys: varargs[string], value: JsonNode) =
   node[keys[keys.len-1]] = value
 
 proc delete*(obj: JsonNode, key: string) =
-  ## Deletes ``obj[key]``.
+  ## Deletes `obj[key]`.
   assert(obj.kind == JObject)
   if not obj.fields.hasKey(key):
     raise newException(KeyError, "key not in object")
   obj.fields.del(key)
 
 proc copy*(p: JsonNode): JsonNode =
-  ## Performs a deep copy of `a`.
+  ## Performs a deep copy of `p`.
   case p.kind
   of JString:
     result = newJString(p.str)
+    result.isUnquoted = p.isUnquoted
   of JInt:
     result = newJInt(p.num)
   of JFloat:
@@ -546,29 +668,81 @@ proc newIndent(curr, indent: int, ml: bool): int =
 proc nl(s: var string, ml: bool) =
   s.add(if ml: "\n" else: " ")
 
-proc escapeJson*(s: string; result: var string) =
-  ## Converts a string `s` to its JSON representation.
-  ## Appends to ``result``.
-  result.add("\"")
+proc escapeJsonUnquoted*(s: string; result: var string) =
+  ## Converts a string `s` to its JSON representation without quotes.
+  ## Appends to `result`.
   for c in s:
     case c
     of '\L': result.add("\\n")
     of '\b': result.add("\\b")
     of '\f': result.add("\\f")
     of '\t': result.add("\\t")
+    of '\v': result.add("\\u000b")
     of '\r': result.add("\\r")
     of '"': result.add("\\\"")
     of '\0'..'\7': result.add("\\u000" & $ord(c))
-    of '\14'..'\31': result.add("\\u00" & $ord(c))
+    of '\14'..'\31': result.add("\\u00" & toHex(ord(c), 2))
     of '\\': result.add("\\\\")
     else: result.add(c)
+
+proc escapeJsonUnquoted*(s: string): string =
+  ## Converts a string `s` to its JSON representation without quotes.
+  result = newStringOfCap(s.len + s.len shr 3)
+  escapeJsonUnquoted(s, result)
+
+proc escapeJson*(s: string; result: var string) =
+  ## Converts a string `s` to its JSON representation with quotes.
+  ## Appends to `result`.
+  result.add("\"")
+  escapeJsonUnquoted(s, result)
   result.add("\"")
 
 proc escapeJson*(s: string): string =
-  ## Converts a string `s` to its JSON representation.
+  ## Converts a string `s` to its JSON representation with quotes.
   result = newStringOfCap(s.len + s.len shr 3)
   escapeJson(s, result)
 
+proc toUgly*(result: var string, node: JsonNode) =
+  ## Converts `node` to its JSON Representation, without
+  ## regard for human readability. Meant to improve `$` string
+  ## conversion performance.
+  ##
+  ## JSON representation is stored in the passed `result`
+  ##
+  ## This provides higher efficiency than the `pretty` procedure as it
+  ## does **not** attempt to format the resulting JSON to make it human readable.
+  var comma = false
+  case node.kind:
+  of JArray:
+    result.add "["
+    for child in node.elems:
+      if comma: result.add ","
+      else: comma = true
+      result.toUgly child
+    result.add "]"
+  of JObject:
+    result.add "{"
+    for key, value in pairs(node.fields):
+      if comma: result.add ","
+      else: comma = true
+      key.escapeJson(result)
+      result.add ":"
+      result.toUgly value
+    result.add "}"
+  of JString:
+    if node.isUnquoted:
+      result.add node.str
+    else:
+      escapeJson(node.str, result)
+  of JInt:
+    result.addInt(node.num)
+  of JFloat:
+    result.addFloat(node.fnum)
+  of JBool:
+    result.add(if node.bval: "true" else: "false")
+  of JNull:
+    result.add "null"
+
 proc toPretty(result: var string, node: JsonNode, indent = 2, ml = true,
               lstArr = false, currIndent = 0) =
   case node.kind
@@ -596,16 +770,13 @@ proc toPretty(result: var string, node: JsonNode, indent = 2, ml = true,
       result.add("{}")
   of JString:
     if lstArr: result.indent(currIndent)
-    escapeJson(node.str, result)
+    toUgly(result, node)
   of JInt:
     if lstArr: result.indent(currIndent)
-    when defined(js): result.add($node.num)
-    else: result.add(node.num)
+    result.addInt(node.num)
   of JFloat:
     if lstArr: result.indent(currIndent)
-    # Fixme: implement new system.add ops for the JS target
-    when defined(js): result.add($node.fnum)
-    else: result.add(node.fnum)
+    result.addFloat(node.fnum)
   of JBool:
     if lstArr: result.indent(currIndent)
     result.add(if node.bval: "true" else: "false")
@@ -631,49 +802,25 @@ proc toPretty(result: var string, node: JsonNode, indent = 2, ml = true,
 proc pretty*(node: JsonNode, indent = 2): string =
   ## Returns a JSON Representation of `node`, with indentation and
   ## on multiple lines.
+  ##
+  ## Similar to prettyprint in Python.
+  runnableExamples:
+    let j = %* {"name": "Isaac", "books": ["Robot Dreams"],
+                "details": {"age": 35, "pi": 3.1415}}
+    doAssert pretty(j) == """
+{
+  "name": "Isaac",
+  "books": [
+    "Robot Dreams"
+  ],
+  "details": {
+    "age": 35,
+    "pi": 3.1415
+  }
+}"""
   result = ""
   toPretty(result, node, indent)
 
-proc toUgly*(result: var string, node: JsonNode) =
-  ## Converts `node` to its JSON Representation, without
-  ## regard for human readability. Meant to improve ``$`` string
-  ## conversion performance.
-  ##
-  ## JSON representation is stored in the passed `result`
-  ##
-  ## This provides higher efficiency than the ``pretty`` procedure as it
-  ## does **not** attempt to format the resulting JSON to make it human readable.
-  var comma = false
-  case node.kind:
-  of JArray:
-    result.add "["
-    for child in node.elems:
-      if comma: result.add ","
-      else:     comma = true
-      result.toUgly child
-    result.add "]"
-  of JObject:
-    result.add "{"
-    for key, value in pairs(node.fields):
-      if comma: result.add ","
-      else:     comma = true
-      key.escapeJson(result)
-      result.add ":"
-      result.toUgly value
-    result.add "}"
-  of JString:
-    node.str.escapeJson(result)
-  of JInt:
-    when defined(js): result.add($node.num)
-    else: result.add(node.num)
-  of JFloat:
-    when defined(js): result.add($node.fnum)
-    else: result.add(node.fnum)
-  of JBool:
-    result.add(if node.bval: "true" else: "false")
-  of JNull:
-    result.add "null"
-
 proc `$`*(node: JsonNode): string =
   ## Converts `node` to its JSON Representation on one line.
   result = newStringOfCap(node.len shl 1)
@@ -681,43 +828,65 @@ proc `$`*(node: JsonNode): string =
 
 iterator items*(node: JsonNode): JsonNode =
   ## Iterator for the items of `node`. `node` has to be a JArray.
-  assert node.kind == JArray
+  assert node.kind == JArray, ": items() can not iterate a JsonNode of kind " & $node.kind
   for i in items(node.elems):
     yield i
 
 iterator mitems*(node: var JsonNode): var JsonNode =
   ## Iterator for the items of `node`. `node` has to be a JArray. Items can be
   ## modified.
-  assert node.kind == JArray
+  assert node.kind == JArray, ": mitems() can not iterate a JsonNode of kind " & $node.kind
   for i in mitems(node.elems):
     yield i
 
 iterator pairs*(node: JsonNode): tuple[key: string, val: JsonNode] =
   ## Iterator for the child elements of `node`. `node` has to be a JObject.
-  assert node.kind == JObject
+  assert node.kind == JObject, ": pairs() can not iterate a JsonNode of kind " & $node.kind
   for key, val in pairs(node.fields):
     yield (key, val)
 
+iterator keys*(node: JsonNode): string =
+  ## Iterator for the keys in `node`. `node` has to be a JObject.
+  assert node.kind == JObject, ": keys() can not iterate a JsonNode of kind " & $node.kind
+  for key in node.fields.keys:
+    yield key
+
 iterator mpairs*(node: var JsonNode): tuple[key: string, val: var JsonNode] =
   ## Iterator for the child elements of `node`. `node` has to be a JObject.
   ## Values can be modified
-  assert node.kind == JObject
+  assert node.kind == JObject, ": mpairs() can not iterate a JsonNode of kind " & $node.kind
   for key, val in mpairs(node.fields):
     yield (key, val)
 
-proc parseJson(p: var JsonParser): JsonNode =
+proc parseJson(p: var JsonParser; rawIntegers, rawFloats: bool, depth = 0): JsonNode =
   ## Parses JSON from a JSON Parser `p`.
   case p.tok
   of tkString:
     # we capture 'p.a' here, so we need to give it a fresh buffer afterwards:
-    result = newJStringMove(p.a)
-    p.a = ""
+    when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
+      result = JsonNode(kind: JString, str: move p.a)
+    else:
+      result = JsonNode(kind: JString)
+      shallowCopy(result.str, p.a)
+      p.a = ""
     discard getTok(p)
   of tkInt:
-    result = newJInt(parseBiggestInt(p.a))
+    if rawIntegers:
+      result = newJRawNumber(p.a)
+    else:
+      try:
+        result = newJInt(parseBiggestInt(p.a))
+      except ValueError:
+        result = newJRawNumber(p.a)
     discard getTok(p)
   of tkFloat:
-    result = newJFloat(parseFloat(p.a))
+    if rawFloats:
+      result = newJRawNumber(p.a)
+    else:
+      try:
+        result = newJFloat(parseFloat(p.a))
+      except ValueError:
+        result = newJRawNumber(p.a)
     discard getTok(p)
   of tkTrue:
     result = newJBool(true)
@@ -729,6 +898,8 @@ proc parseJson(p: var JsonParser): JsonNode =
     result = newJNull()
     discard getTok(p)
   of tkCurlyLe:
+    if depth > DepthLimit:
+      raiseParseErr(p, "}")
     result = newJObject()
     discard getTok(p)
     while p.tok != tkCurlyRi:
@@ -737,65 +908,81 @@ proc parseJson(p: var JsonParser): JsonNode =
       var key = p.a
       discard getTok(p)
       eat(p, tkColon)
-      var val = parseJson(p)
+      var val = parseJson(p, rawIntegers, rawFloats, depth+1)
       result[key] = val
       if p.tok != tkComma: break
       discard getTok(p)
     eat(p, tkCurlyRi)
   of tkBracketLe:
+    if depth > DepthLimit:
+      raiseParseErr(p, "]")
     result = newJArray()
     discard getTok(p)
     while p.tok != tkBracketRi:
-      result.add(parseJson(p))
+      result.add(parseJson(p, rawIntegers, rawFloats, depth+1))
       if p.tok != tkComma: break
       discard getTok(p)
     eat(p, tkBracketRi)
   of tkError, tkCurlyRi, tkBracketRi, tkColon, tkComma, tkEof:
     raiseParseErr(p, "{")
 
-when not defined(js):
-  proc parseJson*(s: Stream, filename: string = ""): JsonNode =
-    ## Parses from a stream `s` into a `JsonNode`. `filename` is only needed
-    ## for nice error messages.
-    ## If `s` contains extra data, it will raise `JsonParsingError`.
-    var p: JsonParser
-    p.open(s, filename)
-    try:
-      discard getTok(p) # read first token
-      result = p.parseJson()
-      eat(p, tkEof) # check if there is no extra data
-    finally:
-      p.close()
-
-  proc parseJson*(buffer: string): JsonNode =
-    ## Parses JSON from `buffer`.
-    ## If `buffer` contains extra data, it will raise `JsonParsingError`.
-    result = parseJson(newStringStream(buffer), "input")
+iterator parseJsonFragments*(s: Stream, filename: string = ""; rawIntegers = false, rawFloats = false): JsonNode =
+  ## Parses from a stream `s` into `JsonNodes`. `filename` is only needed
+  ## for nice error messages.
+  ## The JSON fragments are separated by whitespace. This can be substantially
+  ## faster than the comparable loop
+  ## `for x in splitWhitespace(s): yield parseJson(x)`.
+  ## This closes the stream `s` after it's done.
+  ## If `rawIntegers` is true, integer literals will not be converted to a `JInt`
+  ## field but kept as raw numbers via `JString`.
+  ## If `rawFloats` is true, floating point literals will not be converted to a `JFloat`
+  ## field but kept as raw numbers via `JString`.
+  var p: JsonParser
+  p.open(s, filename)
+  try:
+    discard getTok(p) # read first token
+    while p.tok != tkEof:
+      yield p.parseJson(rawIntegers, rawFloats)
+  finally:
+    p.close()
+
+proc parseJson*(s: Stream, filename: string = ""; rawIntegers = false, rawFloats = false): JsonNode =
+  ## Parses from a stream `s` into a `JsonNode`. `filename` is only needed
+  ## for nice error messages.
+  ## If `s` contains extra data, it will raise `JsonParsingError`.
+  ## This closes the stream `s` after it's done.
+  ## If `rawIntegers` is true, integer literals will not be converted to a `JInt`
+  ## field but kept as raw numbers via `JString`.
+  ## If `rawFloats` is true, floating point literals will not be converted to a `JFloat`
+  ## field but kept as raw numbers via `JString`.
+  var p: JsonParser
+  p.open(s, filename)
+  try:
+    discard getTok(p) # read first token
+    result = p.parseJson(rawIntegers, rawFloats)
+    eat(p, tkEof) # check if there is no extra data
+  finally:
+    p.close()
 
-  proc parseFile*(filename: string): JsonNode =
-    ## Parses `file` into a `JsonNode`.
-    ## If `file` contains extra data, it will raise `JsonParsingError`.
-    var stream = newFileStream(filename, fmRead)
-    if stream == nil:
-      raise newException(IOError, "cannot read from file: " & filename)
-    result = parseJson(stream, filename)
-else:
-  from math import `mod`
-  type
-    JSObject = object
+when defined(js):
+  from std/math import `mod`
+  from std/jsffi import JsObject, `[]`, to
+  from std/private/jsutils import getProtoName, isInteger, isSafeInteger
 
-  proc parseNativeJson(x: cstring): JSObject {.importc: "JSON.parse".}
+  proc parseNativeJson(x: cstring): JsObject {.importjs: "JSON.parse(#)".}
 
-  proc getVarType(x: JSObject): JsonNodeKind =
+  proc getVarType(x: JsObject, isRawNumber: var bool): JsonNodeKind =
     result = JNull
-    proc getProtoName(y: JSObject): cstring
-      {.importc: "Object.prototype.toString.call".}
     case $getProtoName(x) # TODO: Implicit returns fail here.
     of "[object Array]": return JArray
     of "[object Object]": return JObject
     of "[object Number]":
-      if cast[float](x) mod 1.0 == 0:
-        return JInt
+      if isInteger(x) and 1.0 / cast[float](x) != -Inf: # preserve -0.0 as float
+        if isSafeInteger(x):
+          return JInt
+        else:
+          isRawNumber = true
+          return JString
       else:
         return JFloat
     of "[object Boolean]": return JBool
@@ -803,84 +990,77 @@ else:
     of "[object String]": return JString
     else: assert false
 
-  proc len(x: JSObject): int =
-    assert x.getVarType == JArray
-    asm """
+  proc len(x: JsObject): int =
+    {.emit: """
       `result` = `x`.length;
-    """
-
-  proc `[]`(x: JSObject, y: string): JSObject =
-    assert x.getVarType == JObject
-    asm """
-      `result` = `x`[`y`];
-    """
-
-  proc `[]`(x: JSObject, y: int): JSObject =
-    assert x.getVarType == JArray
-    asm """
-      `result` = `x`[`y`];
-    """
-
-  proc convertObject(x: JSObject): JsonNode =
-    case getVarType(x)
+    """.}
+
+  proc convertObject(x: JsObject): JsonNode =
+    var isRawNumber = false
+    case getVarType(x, isRawNumber)
     of JArray:
       result = newJArray()
       for i in 0 ..< x.len:
         result.add(x[i].convertObject())
     of JObject:
       result = newJObject()
-      asm """for (var property in `x`) {
+      {.emit: """for (var property in `x`) {
         if (`x`.hasOwnProperty(property)) {
-      """
+      """.}
+
       var nimProperty: cstring
-      var nimValue: JSObject
-      asm "`nimProperty` = property; `nimValue` = `x`[property];"
+      var nimValue: JsObject
+      {.emit: "`nimProperty` = property; `nimValue` = `x`[property];".}
       result[$nimProperty] = nimValue.convertObject()
-      asm "}}"
+      {.emit: "}}".}
     of JInt:
-      result = newJInt(cast[int](x))
+      result = newJInt(x.to(int))
     of JFloat:
-      result = newJFloat(cast[float](x))
+      result = newJFloat(x.to(float))
     of JString:
-      result = newJString($cast[cstring](x))
+      # Dunno what to do with isUnquoted here
+      if isRawNumber:
+        var value: cstring
+        {.emit: "`value` = `x`.toString();".}
+        result = newJRawNumber($value)
+      else:
+        result = newJString($x.to(cstring))
     of JBool:
-      result = newJBool(cast[bool](x))
+      result = newJBool(x.to(bool))
     of JNull:
       result = newJNull()
 
   proc parseJson*(buffer: string): JsonNode =
-    return parseNativeJson(buffer).convertObject()
-
-# -- Json deserialiser macro. --
-
-proc createJsonIndexer(jsonNode: NimNode,
-                       index: string | int | NimNode): NimNode
-    {.compileTime.} =
-  when index is string:
-    let indexNode = newStrLitNode(index)
-  elif index is int:
-    let indexNode = newIntLitNode(index)
-  elif index is NimNode:
-    let indexNode = index
-
-  result = newNimNode(nnkBracketExpr).add(
-    jsonNode,
-    indexNode
-  )
-
-proc transformJsonIndexer(jsonNode: NimNode): NimNode =
-  case jsonNode.kind
-  of nnkBracketExpr:
-    result = newNimNode(nnkCurlyExpr)
-  else:
-    result = jsonNode.copy()
+    when nimvm:
+      return parseJson(newStringStream(buffer), "input")
+    else:
+      return parseNativeJson(buffer).convertObject()
 
-  for child in jsonNode:
-    result.add(transformJsonIndexer(child))
+else:
+  proc parseJson*(buffer: string; rawIntegers = false, rawFloats = false): JsonNode =
+    ## Parses JSON from `buffer`.
+    ## If `buffer` contains extra data, it will raise `JsonParsingError`.
+    ## If `rawIntegers` is true, integer literals will not be converted to a `JInt`
+    ## field but kept as raw numbers via `JString`.
+    ## If `rawFloats` is true, floating point literals will not be converted to a `JFloat`
+    ## field but kept as raw numbers via `JString`.
+    result = parseJson(newStringStream(buffer), "input", rawIntegers, rawFloats)
+
+  proc parseFile*(filename: string): JsonNode =
+    ## Parses `file` into a `JsonNode`.
+    ## If `file` contains extra data, it will raise `JsonParsingError`.
+    var stream = newFileStream(filename, fmRead)
+    if stream == nil:
+      raise newException(IOError, "cannot read from file: " & filename)
+    result = parseJson(stream, filename, rawIntegers=false, rawFloats=false)
+
+# -- Json deserialiser. --
 
 template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind],
                         ast: string) =
-  if node.kind notin kinds:
+  if node == nil:
+    raise newException(KeyError, "key not found: " & ast)
+  elif  node.kind notin kinds:
     let msg = "Incorrect JSON kind. Wanted '$1' in '$2' but got '$3'." % [
       $kinds,
       ast,
@@ -888,501 +1068,267 @@ template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind],
     ]
     raise newException(JsonKindError, msg)
 
-proc getEnum(node: JsonNode, ast: string, T: typedesc): T =
-  when T is SomeInteger:
-    # TODO: I shouldn't need this proc.
-    proc convert[T](x: BiggestInt): T = T(x)
-    verifyJsonKind(node, {JInt}, ast)
-    return convert[T](node.getBiggestInt())
-  else:
-    verifyJsonKind(node, {JString}, ast)
-    return parseEnum[T](node.getStr())
-
-proc toIdentNode(typeNode: NimNode): NimNode =
-  ## Converts a Sym type node (returned by getType et al.) into an
-  ## Ident node. Placing Sym type nodes inside the resulting code AST is
-  ## unsound (according to @Araq) so this is necessary.
-  case typeNode.kind
-  of nnkSym:
-    return newIdentNode($typeNode)
-  of nnkBracketExpr:
-    result = typeNode
-    for i in 0..<len(result):
-      result[i] = newIdentNode($result[i])
-  of nnkIdent:
-    return typeNode
+macro isRefSkipDistinct*(arg: typed): untyped =
+  ## internal only, do not use
+  var impl = getTypeImpl(arg)
+  if impl.kind == nnkBracketExpr and impl[0].eqIdent("typeDesc"):
+    impl = getTypeImpl(impl[1])
+  while impl.kind == nnkDistinctTy:
+    impl = getTypeImpl(impl[0])
+  result = newLit(impl.kind == nnkRefTy)
+
+# The following forward declarations don't work in older versions of Nim
+
+# forward declare all initFromJson
+
+proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string)
+proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[S, T](dst: var array[S, T]; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[T](dst: var Table[string, T]; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[T](dst: var OrderedTable[string, T]; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string)
+
+# initFromJson definitions
+
+proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string) =
+  verifyJsonKind(jsonNode, {JString, JNull}, jsonPath)
+  # since strings don't have a nil state anymore, this mapping of
+  # JNull to the default string is questionable. `none(string)` and
+  # `some("")` have the same potentional json value `JNull`.
+  if jsonNode.kind == JNull:
+    dst = ""
   else:
-    doAssert false, "Cannot convert typeNode to an ident node: " & $typeNode.kind
-
-proc createGetEnumCall(jsonNode, kindType: NimNode): NimNode =
-  # -> getEnum(`jsonNode`, `kindType`)
-  let getEnumSym = bindSym("getEnum")
-  let astStrLit = toStrLit(jsonNode)
-  let getEnumCall = newCall(getEnumSym, jsonNode, astStrLit, kindType)
-  return getEnumCall
-
-proc createOfBranchCond(ofBranch, getEnumCall: NimNode): NimNode =
-  ## Creates an expression that acts as the condition for an ``of`` branch.
-  var cond = newIdentNode("false")
-  for ofCond in ofBranch:
-    if ofCond.kind == nnkRecList:
-      break
-
-    let comparison = infix(getEnumCall, "==", ofCond)
-    cond = infix(cond, "or", comparison)
-
-  return cond
+    dst = jsonNode.str
 
-proc processObjField(field, jsonNode: NimNode): seq[NimNode] {.compileTime.}
-proc processOfBranch(ofBranch, jsonNode, kindType,
-                     kindJsonNode: NimNode): seq[NimNode] {.compileTime.} =
-  ## Processes each field inside of an object's ``of`` branch.
-  ## For each field a new ExprColonExpr node is created and put in the
-  ## resulting list.
-  ##
-  ## Sample ``ofBranch`` AST:
-  ##
-  ## .. code-block::plain
-  ##     OfBranch                      of 0, 1:
-  ##       IntLit 0                      foodPos: float
-  ##       IntLit 1                      enemyPos: float
-  ##       RecList
-  ##         Sym "foodPos"
-  ##         Sym "enemyPos"
-  result = @[]
-  let getEnumCall = createGetEnumCall(kindJsonNode, kindType)
-
-  for branchField in ofBranch[^1]:
-    let objFields = processObjField(branchField, jsonNode)
-
-    for objField in objFields:
-      let exprColonExpr = newNimNode(nnkExprColonExpr)
-      result.add(exprColonExpr)
-      # Add the name of the field.
-      exprColonExpr.add(toIdentNode(objField[0]))
-
-      # Add the value of the field.
-      let cond = createOfBranchCond(ofBranch, getEnumCall)
-      exprColonExpr.add(newIfStmt(
-        (cond, objField[1])
-      ))
-
-proc processElseBranch(recCaseNode, elseBranch, jsonNode, kindType,
-                       kindJsonNode: NimNode): seq[NimNode] {.compileTime.} =
-  ## Processes each field inside of a variant object's ``else`` branch.
-  ##
-  ## ..code-block::plain
-  ##   Else
-  ##     RecList
-  ##       Sym "other"
-  result = @[]
-  let getEnumCall = createGetEnumCall(kindJsonNode, kindType)
-
-  # We need to build up a list of conditions from each ``of`` branch so that
-  # we can then negate it to get ``else``.
-  var cond = newIdentNode("false")
-  for i in 1 ..< len(recCaseNode):
-    if recCaseNode[i].kind == nnkElse:
-      break
-
-    cond = infix(cond, "or", createOfBranchCond(recCaseNode[i], getEnumCall))
-
-  # Negate the condition.
-  cond = prefix(cond, "not")
-
-  for branchField in elseBranch[^1]:
-    let objFields = processObjField(branchField, jsonNode)
-
-    for objField in objFields:
-      let exprColonExpr = newNimNode(nnkExprColonExpr)
-      result.add(exprColonExpr)
-      # Add the name of the field.
-      exprColonExpr.add(toIdentNode(objField[0]))
+proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string) =
+  verifyJsonKind(jsonNode, {JBool}, jsonPath)
+  dst = jsonNode.bval
 
-      # Add the value of the field.
-      let ifStmt = newIfStmt((cond, objField[1]))
-      exprColonExpr.add(ifStmt)
+proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string) =
+  if jsonNode == nil:
+    raise newException(KeyError, "key not found: " & jsonPath)
+  dst = jsonNode.copy
 
-proc createConstructor(typeSym, jsonNode: NimNode): NimNode {.compileTime.}
-proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
-  ## Process a field from a ``RecList``.
-  ##
-  ## The field will typically be a simple ``Sym`` node, but for object variants
-  ## it may also be a ``RecCase`` in which case things become complicated.
-  result = @[]
-  case field.kind
-  of nnkSym:
-    # Ordinary field. For example, `name: string`.
-    let exprColonExpr = newNimNode(nnkExprColonExpr)
-    result.add(exprColonExpr)
-
-    # Add the field name.
-    exprColonExpr.add(toIdentNode(field))
-
-    # Add the field value.
-    # -> jsonNode["`field`"]
-    let indexedJsonNode = createJsonIndexer(jsonNode, $field)
-    exprColonExpr.add(createConstructor(getTypeInst(field), indexedJsonNode))
-
-  of nnkRecCase:
-    # A "case" field that introduces a variant.
-    let exprColonExpr = newNimNode(nnkExprColonExpr)
-    result.add(exprColonExpr)
-
-    # Add the "case" field name (usually "kind").
-    exprColonExpr.add(toIdentNode(field[0]))
-
-    # -> jsonNode["`field[0]`"]
-    let kindJsonNode = createJsonIndexer(jsonNode, $field[0])
-
-    # Add the "case" field's value.
-    let kindType = toIdentNode(getTypeInst(field[0]))
-    let getEnumSym = bindSym("getEnum")
-    let astStrLit = toStrLit(kindJsonNode)
-    let getEnumCall = newCall(getEnumSym, kindJsonNode, astStrLit, kindType)
-    exprColonExpr.add(getEnumCall)
-
-    # Iterate through each `of` branch.
-    for i in 1 ..< field.len:
-      case field[i].kind
-      of nnkOfBranch:
-        result.add processOfBranch(field[i], jsonNode, kindType, kindJsonNode)
-      of nnkElse:
-        result.add processElseBranch(field, field[i], jsonNode, kindType, kindJsonNode)
-      else:
-        doAssert false, "Expected OfBranch or Else node kinds, got: " & $field[i].kind
-  else:
-    doAssert false, "Unable to process object field: " & $field.kind
-
-  doAssert result.len > 0
-
-proc processFields(obj: NimNode,
-                   jsonNode: NimNode): seq[NimNode] {.compileTime.} =
-  ## Process all the fields of an ``ObjectTy`` and any of its
-  ## parent type's fields (via inheritance).
-  result = @[]
-  case obj.kind
-  of nnkObjectTy:
-    expectKind(obj[2], nnkRecList)
-    for field in obj[2]:
-      let nodes = processObjField(field, jsonNode)
-      result.add(nodes)
-
-    # process parent type fields
-    case obj[1].kind
-    of nnkBracketExpr:
-      assert $obj[1][0] == "ref"
-      result.add(processFields(getType(obj[1][1]), jsonNode))
-    of nnkSym:
-      result.add(processFields(getType(obj[1]), jsonNode))
+proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string) =
+  when T is uint|uint64 or int.sizeof == 4:
+    verifyJsonKind(jsonNode, {JInt, JString}, jsonPath)
+    case jsonNode.kind
+    of JString:
+      let x = parseBiggestUInt(jsonNode.str)
+      dst = cast[T](x)
     else:
-      discard
-  of nnkTupleTy:
-    for identDefs in obj:
-      expectKind(identDefs, nnkIdentDefs)
-      let nodes = processObjField(identDefs[0], jsonNode)
-      result.add(nodes)
+      dst = T(jsonNode.num)
   else:
-    doAssert false, "Unable to process field type: " & $obj.kind
-
-proc processType(typeName: NimNode, obj: NimNode,
-                 jsonNode: NimNode, isRef: bool): NimNode {.compileTime.} =
-  ## Process a type such as ``Sym "float"`` or ``ObjectTy ...``.
-  ##
-  ## Sample ``ObjectTy``:
-  ##
-  ## .. code-block::plain
-  ##     ObjectTy
-  ##       Empty
-  ##       InheritanceInformation
-  ##       RecList
-  ##         Sym "events"
-  case obj.kind
-  of nnkObjectTy, nnkTupleTy:
-    # Create object constructor.
-    result =
-      if obj.kind == nnkObjectTy: newNimNode(nnkObjConstr)
-      else: newNimNode(nnkPar)
-
-    if obj.kind == nnkObjectTy:
-      result.add(typeName) # Name of the type to construct.
-
-    # Process each object/tuple field and add it as an exprColonExpr
-    result.add(processFields(obj, jsonNode))
-
-    # Object might be null. So we need to check for that.
-    if isRef:
-      result = quote do:
-        verifyJsonKind(`jsonNode`, {JObject, JNull}, astToStr(`jsonNode`))
-        if `jsonNode`.kind == JNull:
-          nil
-        else:
-          `result`
-    else:
-      result = quote do:
-        verifyJsonKind(`jsonNode`, {JObject}, astToStr(`jsonNode`));
-        `result`
-
-  of nnkEnumTy:
-    let instType = toIdentNode(getTypeInst(typeName))
-    let getEnumCall = createGetEnumCall(jsonNode, instType)
-    result = quote do:
-      (
-        `getEnumCall`
-      )
-  of nnkSym:
-    let name = ($typeName).normalize
-    case name
-    of "string":
-      result = quote do:
-        (
-          verifyJsonKind(`jsonNode`, {JString, JNull}, astToStr(`jsonNode`));
-          if `jsonNode`.kind == JNull: nil else: `jsonNode`.str
-        )
-    of "biggestint":
-      result = quote do:
-        (
-          verifyJsonKind(`jsonNode`, {JInt}, astToStr(`jsonNode`));
-          `jsonNode`.num
-        )
-    of "bool":
-      result = quote do:
-        (
-          verifyJsonKind(`jsonNode`, {JBool}, astToStr(`jsonNode`));
-          `jsonNode`.bval
-        )
+    verifyJsonKind(jsonNode, {JInt}, jsonPath)
+    dst = cast[T](jsonNode.num)
+
+proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
+  verifyJsonKind(jsonNode, {JInt, JFloat, JString}, jsonPath)
+  if jsonNode.kind == JString:
+    case jsonNode.str
+    of "nan":
+      let b = NaN
+      dst = T(b)
+      # dst = NaN # would fail some tests because range conversions would cause CT error
+      # in some cases; but this is not a hot-spot inside this branch and backend can optimize this.
+    of "inf":
+      let b = Inf
+      dst = T(b)
+    of "-inf":
+      let b = -Inf
+      dst = T(b)
+    else: raise newException(JsonKindError, "expected 'nan|inf|-inf', got " & jsonNode.str)
+  else:
+    if jsonNode.kind == JFloat:
+      dst = T(jsonNode.fnum)
     else:
-      if name.startsWith("int") or name.startsWith("uint"):
-        result = quote do:
-          (
-            verifyJsonKind(`jsonNode`, {JInt}, astToStr(`jsonNode`));
-            `jsonNode`.num.`obj`
-          )
-      elif name.startsWith("float"):
-        result = quote do:
-          (
-            verifyJsonKind(`jsonNode`, {JInt, JFloat}, astToStr(`jsonNode`));
-            if `jsonNode`.kind == JFloat: `jsonNode`.fnum.`obj` else: `jsonNode`.num.`obj`
-          )
-      else:
-        doAssert false, "Unable to process nnkSym " & $typeName
+      dst = T(jsonNode.num)
+
+proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
+  verifyJsonKind(jsonNode, {JString}, jsonPath)
+  dst = parseEnum[T](jsonNode.getStr)
+
+proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string) =
+  verifyJsonKind(jsonNode, {JArray}, jsonPath)
+  dst.setLen jsonNode.len
+  let orignalJsonPathLen = jsonPath.len
+  for i in 0 ..< jsonNode.len:
+    jsonPath.add '['
+    jsonPath.addInt i
+    jsonPath.add ']'
+    initFromJson(dst[i], jsonNode[i], jsonPath)
+    jsonPath.setLen orignalJsonPathLen
+
+proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: var string) =
+  verifyJsonKind(jsonNode, {JArray}, jsonPath)
+  let originalJsonPathLen = jsonPath.len
+  for i in 0 ..< jsonNode.len:
+    jsonPath.add '['
+    jsonPath.addInt i
+    jsonPath.add ']'
+    initFromJson(dst[i.S], jsonNode[i], jsonPath) # `.S` for enum indexed arrays
+    jsonPath.setLen originalJsonPathLen
+
+proc initFromJson[T](dst: var Table[string,T]; jsonNode: JsonNode; jsonPath: var string) =
+  dst = initTable[string, T]()
+  verifyJsonKind(jsonNode, {JObject}, jsonPath)
+  let originalJsonPathLen = jsonPath.len
+  for key in keys(jsonNode.fields):
+    jsonPath.add '.'
+    jsonPath.add key
+    initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath)
+    jsonPath.setLen originalJsonPathLen
+
+proc initFromJson[T](dst: var OrderedTable[string,T]; jsonNode: JsonNode; jsonPath: var string) =
+  dst = initOrderedTable[string,T]()
+  verifyJsonKind(jsonNode, {JObject}, jsonPath)
+  let originalJsonPathLen = jsonPath.len
+  for key in keys(jsonNode.fields):
+    jsonPath.add '.'
+    jsonPath.add key
+    initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath)
+    jsonPath.setLen originalJsonPathLen
+
+proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) =
+  verifyJsonKind(jsonNode, {JObject, JNull}, jsonPath)
+  if jsonNode.kind == JNull:
+    dst = nil
   else:
-    doAssert false, "Unable to process type: " & $obj.kind
-
-  doAssert(not result.isNil(), "processType not initialised.")
-
-import options
-proc workaroundMacroNone[T](): Option[T] =
-  none(T)
-
-proc depth(n: NimNode, current = 0): int =
-  result = 1
-  for child in n:
-    let d = 1 + child.depth(current + 1)
-    if d > result:
-      result = d
-
-proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
-  ## Accepts a type description, i.e. "ref Type", "seq[Type]", "Type" etc.
-  ##
-  ## The ``jsonNode`` refers to the node variable that we are deserialising.
-  ##
-  ## Returns an object constructor node.
-  # echo("--createConsuctor-- \n", treeRepr(typeSym))
-  # echo()
-
-  if depth(jsonNode) > 150:
-    error("The `to` macro does not support ref objects with cycles.", jsonNode)
-
-  case typeSym.kind
-  of nnkBracketExpr:
-    var bracketName = ($typeSym[0]).normalize
-    case bracketName
-    of "option":
-      # TODO: Would be good to verify that this is Option[T] from
-      # options module I suppose.
-      let lenientJsonNode = transformJsonIndexer(jsonNode)
-
-      let optionGeneric = typeSym[1]
-      let value = createConstructor(typeSym[1], jsonNode)
-      let workaround = bindSym("workaroundMacroNone") # TODO: Nim Bug: This shouldn't be necessary.
-
-      result = quote do:
-        (
-          if `lenientJsonNode`.isNil or `jsonNode`.kind == JNull: `workaround`[`optionGeneric`]() else: some[`optionGeneric`](`value`)
-        )
-    of "table", "orderedtable":
-      let tableKeyType = typeSym[1]
-      if ($tableKeyType).cmpIgnoreStyle("string") != 0:
-        error("JSON doesn't support keys of type " & $tableKeyType)
-      let tableValueType = typeSym[2]
-
-      let forLoopKey = genSym(nskForVar, "key")
-      let indexerNode = createJsonIndexer(jsonNode, forLoopKey)
-      let constructorNode = createConstructor(tableValueType, indexerNode)
-
-      let tableInit =
-        if bracketName == "table":
-          bindSym("initTable")
-        else:
-          bindSym("initOrderedTable")
-
-      # Create a statement expression containing a for loop.
-      result = quote do:
-        (
-          var map = `tableInit`[`tableKeyType`, `tableValueType`]();
-          verifyJsonKind(`jsonNode`, {JObject}, astToStr(`jsonNode`));
-          for `forLoopKey` in keys(`jsonNode`.fields): map[`forLoopKey`] = `constructorNode`;
-          map
-        )
-    of "ref":
-      # Ref type.
-      var typeName = $typeSym[1]
-      # Remove the `:ObjectType` suffix.
-      if typeName.endsWith(":ObjectType"):
-        typeName = typeName[0 .. ^12]
-
-      let obj = getType(typeSym[1])
-      result = processType(newIdentNode(typeName), obj, jsonNode, true)
-    of "seq":
-      let seqT = typeSym[1]
-      let forLoopI = genSym(nskForVar, "i")
-      let indexerNode = createJsonIndexer(jsonNode, forLoopI)
-      let constructorNode = createConstructor(seqT, indexerNode)
-
-      # Create a statement expression containing a for loop.
-      result = quote do:
-        (
-          var list: `typeSym` = @[];
-          verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`));
-          for `forLoopI` in 0 ..< `jsonNode`.len: list.add(`constructorNode`);
-          list
-        )
-    of "array":
-      let arrayT = typeSym[2]
-      let forLoopI = genSym(nskForVar, "i")
-      let indexerNode = createJsonIndexer(jsonNode, forLoopI)
-      let constructorNode = createConstructor(arrayT, indexerNode)
-
-      # Create a statement expression containing a for loop.
-      result = quote do:
-        (
-          var list: `typeSym`;
-          verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`));
-          for `forLoopI` in 0 ..< `jsonNode`.len: list[`forLoopI`] =`constructorNode`;
-          list
-        )
+    dst = new(T)
+    initFromJson(dst[], jsonNode, jsonPath)
 
+proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) =
+  if jsonNode != nil and jsonNode.kind != JNull:
+    when T is ref:
+      dst = some(new(T))
     else:
-      # Generic type.
-      let obj = getType(typeSym)
-      result = processType(typeSym, obj, jsonNode, false)
-  of nnkSym:
-    # Handle JsonNode.
-    if ($typeSym).cmpIgnoreStyle("jsonnode") == 0:
-      return jsonNode
-
-    # Handle all other types.
-    let obj = getType(typeSym)
-    if obj.kind == nnkBracketExpr:
-      # When `Sym "Foo"` turns out to be a `ref object`.
-      result = createConstructor(obj, jsonNode)
-    else:
-      result = processType(typeSym, obj, jsonNode, false)
-  of nnkTupleTy:
-    result = processType(typeSym, typeSym, jsonNode, false)
-  of nnkPar, nnkTupleConstr:
-    # TODO: The fact that `jsonNode` here works to give a good line number
-    # is weird. Specifying typeSym should work but doesn't.
-    error("Use a named tuple instead of: " & $toStrLit(typeSym), jsonNode)
-  else:
-    doAssert false, "Unable to create constructor for: " & $typeSym.kind
+      dst = some(default(T))
+    initFromJson(dst.get, jsonNode, jsonPath)
 
-  doAssert(not result.isNil(), "Constructor not initialised.")
+macro assignDistinctImpl[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: var string) =
+  let typInst = getTypeInst(dst)
+  let typImpl = getTypeImpl(dst)
+  let baseTyp = typImpl[0]
 
-proc postProcess(node: NimNode): NimNode
-proc postProcessValue(value: NimNode): NimNode =
-  ## Looks for object constructors and calls the ``postProcess`` procedure
-  ## on them. Otherwise it just returns the node as-is.
-  case value.kind
-  of nnkObjConstr:
-    result = postProcess(value)
-  else:
-    result = value
-    for i in 0 ..< len(result):
-      result[i] = postProcessValue(result[i])
-
-proc postProcessExprColonExpr(exprColonExpr, resIdent: NimNode): NimNode =
-  ## Transform each field mapping in the ExprColonExpr into a simple
-  ## field assignment. Special processing is performed if the field mapping
-  ## has an if statement.
-  ##
-  ## ..code-block::plain
-  ##    field: (if true: 12)  ->  if true: `resIdent`.field = 12
-  expectKind(exprColonExpr, nnkExprColonExpr)
-  let fieldName = exprColonExpr[0]
-  let fieldValue = exprColonExpr[1]
-  case fieldValue.kind
-  of nnkIfStmt:
-    doAssert fieldValue.len == 1, "Cannot postProcess two ElifBranches."
-    expectKind(fieldValue[0], nnkElifBranch)
-
-    let cond = fieldValue[0][0]
-    let bodyValue = postProcessValue(fieldValue[0][1])
-    doAssert(bodyValue.kind != nnkNilLit)
-    result =
-      quote do:
-        if `cond`:
-          `resIdent`.`fieldName` = `bodyValue`
-  else:
-    let fieldValue = postProcessValue(fieldValue)
-    doAssert(fieldValue.kind != nnkNilLit)
-    result =
-      quote do:
-        `resIdent`.`fieldName` = `fieldValue`
+  result = quote do:
+    initFromJson(`baseTyp`(`dst`), `jsonNode`, `jsonPath`)
 
+proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
+  assignDistinctImpl(dst, jsonNode, jsonPath)
 
-proc postProcess(node: NimNode): NimNode =
-  ## The ``createConstructor`` proc creates a ObjConstr node which contains
-  ## if statements for fields that may not be assignable (due to an object
-  ## variant). Nim doesn't handle this, but may do in the future.
-  ##
-  ## For simplicity, we post process the object constructor into multiple
-  ## assignments.
-  ##
-  ## For example:
-  ##
-  ## ..code-block::plain
-  ##    Object(                           (var res = Object();
-  ##      field: if true: 12      ->       if true: res.field = 12;
-  ##    )                                  res)
-  result = newNimNode(nnkStmtListExpr)
+proc detectIncompatibleType(typeExpr, lineinfoNode: NimNode) =
+  if typeExpr.kind == nnkTupleConstr:
+    error("Use a named tuple instead of: " & typeExpr.repr, lineinfoNode)
 
-  expectKind(node, nnkObjConstr)
+proc foldObjectBody(dst, typeNode, tmpSym, jsonNode, jsonPath, originalJsonPathLen: NimNode) =
+  case typeNode.kind
+  of nnkEmpty:
+    discard
+  of nnkRecList, nnkTupleTy:
+    for it in typeNode:
+      foldObjectBody(dst, it, tmpSym, jsonNode, jsonPath, originalJsonPathLen)
+
+  of nnkIdentDefs:
+    typeNode.expectLen 3
+    let fieldSym = typeNode[0]
+    let fieldNameLit = newLit(fieldSym.strVal)
+    let fieldPathLit = newLit("." & fieldSym.strVal)
+    let fieldType = typeNode[1]
+
+    # Detecting incompatiple tuple types in `assignObjectImpl` only
+    # would be much cleaner, but the ast for tuple types does not
+    # contain usable type information.
+    detectIncompatibleType(fieldType, fieldSym)
+
+    dst.add quote do:
+      jsonPath.add `fieldPathLit`
+      when nimvm:
+        when isRefSkipDistinct(`tmpSym`.`fieldSym`):
+          # workaround #12489
+          var tmp: `fieldType`
+          initFromJson(tmp, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`)
+          `tmpSym`.`fieldSym` = tmp
+        else:
+          initFromJson(`tmpSym`.`fieldSym`, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`)
+      else:
+        initFromJson(`tmpSym`.`fieldSym`, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`)
+      jsonPath.setLen `originalJsonPathLen`
 
-  # Create the type.
-  # -> var res = Object()
-  var resIdent = genSym(nskVar, "res")
-  # TODO: Placing `node[0]` inside quote is buggy
-  var resType = toIdentNode(node[0])
+  of nnkRecCase:
+    let kindSym = typeNode[0][0]
+    let kindNameLit = newLit(kindSym.strVal)
+    let kindPathLit = newLit("." & kindSym.strVal)
+    let kindType = typeNode[0][1]
+    let kindOffsetLit = newLit(uint(getOffset(kindSym)))
+    dst.add quote do:
+      var kindTmp: `kindType`
+      jsonPath.add `kindPathLit`
+      initFromJson(kindTmp, `jsonNode`[`kindNameLit`], `jsonPath`)
+      jsonPath.setLen `originalJsonPathLen`
+      when defined js:
+        `tmpSym`.`kindSym` = kindTmp
+      else:
+        when nimvm:
+          `tmpSym`.`kindSym` = kindTmp
+        else:
+          # fuck it, assign kind field anyway
+          ((cast[ptr `kindType`](cast[uint](`tmpSym`.addr) + `kindOffsetLit`))[]) = kindTmp
+    dst.add nnkCaseStmt.newTree(nnkDotExpr.newTree(tmpSym, kindSym))
+    for i in 1 ..< typeNode.len:
+      foldObjectBody(dst, typeNode[i], tmpSym, jsonNode, jsonPath, originalJsonPathLen)
+
+  of nnkOfBranch, nnkElse:
+    let ofBranch = newNimNode(typeNode.kind)
+    for i in 0 ..< typeNode.len-1:
+      ofBranch.add copyNimTree(typeNode[i])
+    let dstInner = newNimNode(nnkStmtListExpr)
+    foldObjectBody(dstInner, typeNode[^1], tmpSym, jsonNode, jsonPath, originalJsonPathLen)
+    # resOuter now contains the inner stmtList
+    ofBranch.add dstInner
+    dst[^1].expectKind nnkCaseStmt
+    dst[^1].add ofBranch
 
-  result.add(
-    quote do:
-      var `resIdent` = `resType`();
-  )
+  of nnkObjectTy:
+    typeNode[0].expectKind nnkEmpty
+    typeNode[1].expectKind {nnkEmpty, nnkOfInherit}
+    if typeNode[1].kind == nnkOfInherit:
+      let base = typeNode[1][0]
+      var impl = getTypeImpl(base)
+      while impl.kind in {nnkRefTy, nnkPtrTy}:
+        impl = getTypeImpl(impl[0])
+      foldObjectBody(dst, impl, tmpSym, jsonNode, jsonPath, originalJsonPathLen)
+    let body = typeNode[2]
+    foldObjectBody(dst, body, tmpSym, jsonNode, jsonPath, originalJsonPathLen)
 
-  # Process each ExprColonExpr.
-  for i in 1..<len(node):
-    result.add postProcessExprColonExpr(node[i], resIdent)
+  else:
+    error("unhandled kind: " & $typeNode.kind, typeNode)
 
-  # Return the `res` variable.
-  result.add(
-    quote do:
-      `resIdent`
-  )
+macro assignObjectImpl[T](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
+  let typeSym = getTypeInst(dst)
+  let originalJsonPathLen = genSym(nskLet, "originalJsonPathLen")
+  result = newStmtList()
+  result.add quote do:
+    let `originalJsonPathLen` = len(`jsonPath`)
+  if typeSym.kind in {nnkTupleTy, nnkTupleConstr}:
+    # both, `dst` and `typeSym` don't have good lineinfo. But nothing
+    # else is available here.
+    detectIncompatibleType(typeSym, dst)
+    foldObjectBody(result, typeSym, dst, jsonNode, jsonPath, originalJsonPathLen)
+  else:
+    foldObjectBody(result, typeSym.getTypeImpl, dst, jsonNode, jsonPath, originalJsonPathLen)
 
+proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
+  assignObjectImpl(dst, jsonNode, jsonPath)
 
-macro to*(node: JsonNode, T: typedesc): untyped =
+proc to*[T](node: JsonNode, t: typedesc[T]): T =
   ## `Unmarshals`:idx: the specified node into the object type specified.
   ##
   ## Known limitations:
@@ -1391,53 +1337,37 @@ macro to*(node: JsonNode, T: typedesc): untyped =
   ##   * Sets in object variants are not supported.
   ##   * Not nil annotations are not supported.
   ##
-  ## Example:
-  ##
-  ## .. code-block:: Nim
-  ##     let jsonNode = parseJson("""
-  ##        {
-  ##          "person": {
-  ##            "name": "Nimmer",
-  ##            "age": 21
-  ##          },
-  ##          "list": [1, 2, 3, 4]
-  ##        }
-  ##     """)
-  ##
-  ##     type
-  ##       Person = object
-  ##         name: string
-  ##         age: int
-  ##
-  ##       Data = object
-  ##         person: Person
-  ##         list: seq[int]
-  ##
-  ##     var data = to(jsonNode, Data)
-  ##     doAssert data.person.name == "Nimmer"
-  ##     doAssert data.person.age == 21
-  ##     doAssert data.list == @[1, 2, 3, 4]
-
-  let typeNode = getTypeInst(T)
-  expectKind(typeNode, nnkBracketExpr)
-  doAssert(($typeNode[0]).normalize == "typedesc")
-
-  # Create `temp` variable to store the result in case the user calls this
-  # on `parseJson` (see bug #6604).
-  result = newNimNode(nnkStmtListExpr)
-  let temp = genSym(nskLet, "temp")
-  result.add quote do:
-    let `temp` = `node`
+  runnableExamples:
+    let jsonNode = parseJson("""
+      {
+        "person": {
+          "name": "Nimmer",
+          "age": 21
+        },
+        "list": [1, 2, 3, 4]
+      }
+    """)
 
-  let constructor = createConstructor(typeNode[1], temp)
-  # TODO: Rename postProcessValue and move it (?)
-  result.add(postProcessValue(constructor))
+    type
+      Person = object
+        name: string
+        age: int
 
-  # echo(treeRepr(result))
-  # echo(toStrLit(result))
+      Data = object
+        person: Person
+        list: seq[int]
+
+    var data = to(jsonNode, Data)
+    doAssert data.person.name == "Nimmer"
+    doAssert data.person.age == 21
+    doAssert data.list == @[1, 2, 3, 4]
+
+  var jsonPath = ""
+  result = default(T)
+  initFromJson(result, node, jsonPath)
 
 when false:
-  import os
+  import std/os
   var s = newFileStream(paramStr(1), fmRead)
   if s == nil: quit("cannot open the file" & paramStr(1))
   var x: JsonParser
@@ -1462,145 +1392,3 @@ when false:
 
 # { "json": 5 }
 # To get that we shall use, obj["json"]
-
-when isMainModule:
-  # Note: Macro tests are in tests/stdlib/tjsonmacro.nim
-
-  let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd", "c": "\ud83c\udf83", "d": "\u00E6"}"""
-  # nil passthrough
-  doAssert(testJson{"doesnt_exist"}{"anything"}.isNil)
-  testJson{["e", "f"]} = %true
-  doAssert(testJson["e"]["f"].bval)
-
-  # make sure UTF-16 decoding works.
-  doAssert(testJson["c"].str == "🎃")
-  doAssert(testJson["d"].str == "æ")
-
-  # make sure no memory leek when parsing invalid string
-  let startMemory = getOccupiedMem()
-  for i in 0 .. 10000:
-    try:
-      discard parseJson"""{ invalid"""
-    except:
-      discard
-  # memory diff should less than 4M
-  doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024)
-
-
-  # test `$`
-  let stringified = $testJson
-  let parsedAgain = parseJson(stringified)
-  doAssert(parsedAgain["b"].str == "asd")
-
-  parsedAgain["abc"] = %5
-  doAssert parsedAgain["abc"].num == 5
-
-  # Bounds checking
-  try:
-    let a = testJson["a"][9]
-    doAssert(false, "IndexError not thrown")
-  except IndexError:
-    discard
-  try:
-    let a = testJson["a"][-1]
-    doAssert(false, "IndexError not thrown")
-  except IndexError:
-    discard
-  try:
-    doAssert(testJson["a"][0].num == 1, "Index doesn't correspond to its value")
-  except:
-    doAssert(false, "IndexError thrown for valid index")
-
-  doAssert(testJson{"b"}.str=="asd", "Couldn't fetch a singly nested key with {}")
-  doAssert(isNil(testJson{"nonexistent"}), "Non-existent keys should return nil")
-  doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil")
-  doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil")
-  doAssert(testJson{"a"}==parseJson"[1, 2, 3, 4]", "Didn't return a non-JObject when there was one to be found")
-  doAssert(isNil(parseJson("[1, 2, 3]"){"foo"}), "Indexing directly into a list should return nil")
-
-  # Generator:
-  var j = %* [{"name": "John", "age": 30}, {"name": "Susan", "age": 31}]
-  doAssert j == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
-
-  var j2 = %*
-    [
-      {
-        "name": "John",
-        "age": 30
-      },
-      {
-        "name": "Susan",
-        "age": 31
-      }
-    ]
-  doAssert j2 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
-
-  var name = "John"
-  let herAge = 30
-  const hisAge = 31
-
-  var j3 = %*
-    [ { "name": "John"
-      , "age": herAge
-      }
-    , { "name": "Susan"
-      , "age": hisAge
-      }
-    ]
-  doAssert j3 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
-
-  var j4 = %*{"test": nil}
-  doAssert j4 == %{"test": newJNull()}
-
-  let seqOfNodes = @[%1, %2]
-  let jSeqOfNodes = %seqOfNodes
-  doAssert(jSeqOfNodes[1].num == 2)
-
-  type MyObj = object
-    a, b: int
-    s: string
-    f32: float32
-    f64: float64
-    next: ref MyObj
-  var m: MyObj
-  m.s = "hi"
-  m.a = 5
-  let jMyObj = %m
-  doAssert(jMyObj["a"].num == 5)
-  doAssert(jMyObj["s"].str == "hi")
-
-  # Test loading of file.
-  when not defined(js):
-    echo("99% of tests finished. Going to try loading file.")
-    var parsed = parseFile("tests/testdata/jsontest.json")
-
-    try:
-      discard parsed["key2"][12123]
-      doAssert(false)
-    except IndexError: doAssert(true)
-
-    var parsed2 = parseFile("tests/testdata/jsontest2.json")
-    doAssert(parsed2{"repository", "description"}.str=="IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}")
-
-  doAssert escapeJson("\10Foo🎃barÄ") == "\"\\nFoo🎃barÄ\""
-  doAssert escapeJson("\0\7\20") == "\"\\u0000\\u0007\\u0020\"" # for #7887
-
-  # Test with extra data
-  when not defined(js):
-    try:
-      discard parseJson("123 456")
-      doAssert(false)
-    except JsonParsingError:
-      doAssert getCurrentExceptionMsg().contains(errorMessages[errEofExpected])
-
-    try:
-      discard parseFile("tests/testdata/jsonwithextradata.json")
-      doAssert(false)
-    except JsonParsingError:
-      doAssert getCurrentExceptionMsg().contains(errorMessages[errEofExpected])
-
-  # bug #6438
-  doAssert($ %*[] == "[]")
-  doAssert($ %*{} == "{}")
-
-  echo("Tests succeeded!")
diff --git a/lib/pure/lenientops.nim b/lib/pure/lenientops.nim
index cc7784c19..a8fc78e39 100644
--- a/lib/pure/lenientops.nim
+++ b/lib/pure/lenientops.nim
@@ -8,53 +8,51 @@
 #
 
 ## This module offers implementations of common binary operations
-## like ``+``, ``-``, ``*``, ``/`` and comparison operations,
+## like `+`, `-`, `*`, `/` and comparison operations,
 ## which work for mixed float/int operands.
 ## All operations convert the integer operand into the
 ## type of the float operand. For numerical expressions, the return
-## type is always the type of the float involved in the expresssion,
+## type is always the type of the float involved in the expression,
 ## i.e., there is no auto conversion from float32 to float64.
 ##
-## Note: In general, auto-converting from int to float loses
+## **Note:** In general, auto-converting from int to float loses
 ## information, which is why these operators live in a separate
 ## module. Use with care.
 ##
 ## Regarding binary comparison, this module only provides unequal operators.
-## The equality operator ``==`` is omitted, because depending on the use case
+## The equality operator `==` is omitted, because depending on the use case
 ## either casting to float or rounding to int might be preferred, and users
 ## should make an explicit choice.
 
-import typetraits
-
-proc `+`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.noSideEffect, inline.} =
+func `+`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.inline.} =
   F(i) + f
-proc `+`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.noSideEffect, inline.} =
+func `+`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.inline.} =
   f + F(i)
 
-proc `-`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.noSideEffect, inline.} =
+func `-`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.inline.} =
   F(i) - f
-proc `-`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.noSideEffect, inline.} =
+func `-`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.inline.} =
   f - F(i)
 
-proc `*`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.noSideEffect, inline.} =
+func `*`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.inline.} =
   F(i) * f
-proc `*`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.noSideEffect, inline.} =
+func `*`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.inline.} =
   f * F(i)
 
-proc `/`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.noSideEffect, inline.} =
+func `/`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.inline.} =
   F(i) / f
-proc `/`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.noSideEffect, inline.} =
+func `/`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.inline.} =
   f / F(i)
 
-proc `<`*[I: SomeInteger, F: SomeFloat](i: I, f: F): bool {.noSideEffect, inline.} =
+func `<`*[I: SomeInteger, F: SomeFloat](i: I, f: F): bool {.inline.} =
   F(i) < f
-proc `<`*[I: SomeInteger, F: SomeFloat](f: F, i: I): bool {.noSideEffect, inline.} =
+func `<`*[I: SomeInteger, F: SomeFloat](f: F, i: I): bool {.inline.} =
   f < F(i)
-proc `<=`*[I: SomeInteger, F: SomeFloat](i: I, f: F): bool {.noSideEffect, inline.} =
+func `<=`*[I: SomeInteger, F: SomeFloat](i: I, f: F): bool {.inline.} =
   F(i) <= f
-proc `<=`*[I: SomeInteger, F: SomeFloat](f: F, i: I): bool {.noSideEffect, inline.} =
+func `<=`*[I: SomeInteger, F: SomeFloat](f: F, i: I): bool {.inline.} =
   f <= F(i)
 
 # Note that we must not defined `>=` and `>`, because system.nim already has a
 # template with signature (x, y: untyped): untyped, which would lead to
-# ambigous calls.
+# ambiguous calls.
diff --git a/lib/pure/lexbase.nim b/lib/pure/lexbase.nim
index e38acd5ef..1efd97b24 100644
--- a/lib/pure/lexbase.nim
+++ b/lib/pure/lexbase.nim
@@ -1,6 +1,6 @@
 #
 #
-#           The Nim Compiler
+#            Nim's Runtime Library
 #        (c) Copyright 2009 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
@@ -12,10 +12,13 @@
 ## needs refilling.
 
 import
-  strutils, streams
+  std/[strutils, streams]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 const
-  EndOfFile* = '\0'           ## end of file marker
+  EndOfFile* = '\0' ## end of file marker
   NewLines* = {'\c', '\L'}
 
 # Buffer handling:
@@ -27,26 +30,17 @@ const
 type
   BaseLexer* = object of RootObj ## the base lexer. Inherit your lexer from
                                  ## this object.
-    bufpos*: int              ## the current position within the buffer
-    when defined(js):         ## the buffer itself
-      buf*: string
-    else:
-      buf*: cstring
-    bufLen*: int              ## length of buffer in characters
-    input: Stream            ## the input stream
-    lineNumber*: int          ## the current line number
+    bufpos*: int                 ## the current position within the buffer
+    buf*: string                 ## the buffer itself
+    input: Stream                ## the input stream
+    lineNumber*: int             ## the current line number
     sentinel: int
-    lineStart: int            # index of last line start in buffer
-    offsetBase*: int          # use ``offsetBase + bufpos`` to get the offset
+    lineStart: int               # index of last line start in buffer
+    offsetBase*: int             # use `offsetBase + bufpos` to get the offset
     refillChars: set[char]
 
-const
-  chrSize = sizeof(char)
-
 proc close*(L: var BaseLexer) =
   ## closes the base lexer. This closes `L`'s associated stream too.
-  when not defined(js):
-    dealloc(L.buf)
   close(L.input)
 
 proc fillBuffer(L: var BaseLexer) =
@@ -57,26 +51,31 @@ proc fillBuffer(L: var BaseLexer) =
     oldBufLen: int
   # we know here that pos == L.sentinel, but not if this proc
   # is called the first time by initBaseLexer()
-  assert(L.sentinel < L.bufLen)
-  toCopy = L.bufLen - L.sentinel - 1
+  assert(L.sentinel + 1 <= L.buf.len)
+  toCopy = L.buf.len - (L.sentinel + 1)
   assert(toCopy >= 0)
   if toCopy > 0:
-    when defined(js):
-      for i in 0 ..< toCopy: L.buf[i] = L.buf[L.sentinel + 1 + i]
+    when defined(js) or defined(nimscript):
+      # nimscript has to be here to avoid compiling other branch (moveMem)
+      for i in 0 ..< toCopy:
+        L.buf[i] = L.buf[L.sentinel + 1 + i]
     else:
-      # "moveMem" handles overlapping regions
-      moveMem(L.buf, addr L.buf[L.sentinel + 1], toCopy * chrSize)
-  charsRead = readData(L.input, addr(L.buf[toCopy]),
-                       (L.sentinel + 1) * chrSize) div chrSize
+      when nimvm:
+        for i in 0 ..< toCopy:
+          L.buf[i] = L.buf[L.sentinel + 1 + i]
+      else:
+        # "moveMem" handles overlapping regions
+        moveMem(addr L.buf[0], addr L.buf[L.sentinel + 1], toCopy)
+  charsRead = L.input.readDataStr(L.buf, toCopy ..< toCopy + L.sentinel + 1)
   s = toCopy + charsRead
   if charsRead < L.sentinel + 1:
-    L.buf[s] = EndOfFile      # set end marker
+    L.buf[s] = EndOfFile # set end marker
     L.sentinel = s
   else:
     # compute sentinel:
-    dec(s)                    # BUGFIX (valgrind)
+    dec(s) # BUGFIX (valgrind)
     while true:
-      assert(s < L.bufLen)
+      assert(s < L.buf.len)
       while s >= 0 and L.buf[s] notin L.refillChars: dec(s)
       if s >= 0:
         # we found an appropriate character for a sentinel:
@@ -85,25 +84,19 @@ proc fillBuffer(L: var BaseLexer) =
       else:
         # rather than to give up here because the line is too long,
         # double the buffer's size and try again:
-        oldBufLen = L.bufLen
-        L.bufLen = L.bufLen * 2
-        when defined(js):
-          L.buf.setLen(L.bufLen)
-        else:
-          L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize))
-        assert(L.bufLen - oldBufLen == oldBufLen)
-        charsRead = readData(L.input, addr(L.buf[oldBufLen]),
-                             oldBufLen * chrSize) div chrSize
+        oldBufLen = L.buf.len
+        L.buf.setLen(L.buf.len * 2)
+        charsRead = readDataStr(L.input, L.buf, oldBufLen ..< L.buf.len)
         if charsRead < oldBufLen:
           L.buf[oldBufLen + charsRead] = EndOfFile
           L.sentinel = oldBufLen + charsRead
           break
-        s = L.bufLen - 1
+        s = L.buf.len - 1
 
 proc fillBaseLexer(L: var BaseLexer, pos: int): int =
   assert(pos <= L.sentinel)
   if pos < L.sentinel:
-    result = pos + 1          # nothing to do
+    result = pos + 1 # nothing to do
   else:
     fillBuffer(L)
     L.offsetBase += pos
@@ -111,9 +104,9 @@ proc fillBaseLexer(L: var BaseLexer, pos: int): int =
     result = 0
 
 proc handleCR*(L: var BaseLexer, pos: int): int =
-  ## Call this if you scanned over '\c' in the buffer; it returns the the
+  ## Call this if you scanned over `'\c'` in the buffer; it returns the
   ## position to continue the scanning from. `pos` must be the position
-  ## of the '\c'.
+  ## of the `'\c'`.
   assert(L.buf[pos] == '\c')
   inc(L.lineNumber)
   result = fillBaseLexer(L, pos)
@@ -122,16 +115,17 @@ proc handleCR*(L: var BaseLexer, pos: int): int =
   L.lineStart = result
 
 proc handleLF*(L: var BaseLexer, pos: int): int =
-  ## Call this if you scanned over '\L' in the buffer; it returns the the
+  ## Call this if you scanned over `'\L'` in the buffer; it returns the
   ## position to continue the scanning from. `pos` must be the position
-  ## of the '\L'.
+  ## of the `'\L'`.
   assert(L.buf[pos] == '\L')
   inc(L.lineNumber)
   result = fillBaseLexer(L, pos) #L.lastNL := result-1; // BUGFIX: was: result;
   L.lineStart = result
 
 proc handleRefillChar*(L: var BaseLexer, pos: int): int =
-  ## To be documented.
+  ## Call this if a terminator character other than a new line is scanned
+  ## at `pos`; it returns the position to continue the scanning from.
   assert(L.buf[pos] in L.refillChars)
   result = fillBaseLexer(L, pos) #L.lastNL := result-1; // BUGFIX: was: result;
 
@@ -148,15 +142,11 @@ proc open*(L: var BaseLexer, input: Stream, bufLen: int = 8192;
   L.input = input
   L.bufpos = 0
   L.offsetBase = 0
-  L.bufLen = bufLen
   L.refillChars = refillChars
-  when defined(js):
-    L.buf = newString(bufLen)
-  else:
-    L.buf = cast[cstring](alloc(bufLen * chrSize))
+  L.buf = newString(bufLen)
   L.sentinel = bufLen - 1
   L.lineStart = 0
-  L.lineNumber = 1            # lines start at 1
+  L.lineNumber = 1 # lines start at 1
   fillBuffer(L)
   skipUtf8Bom(L)
 
diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim
index cdff1f548..c30f68af8 100644
--- a/lib/pure/logging.nim
+++ b/lib/pure/logging.nim
@@ -7,110 +7,310 @@
 #    distribution, for details about the copyright.
 #
 
-## This module implements a simple logger. It has been designed to be as simple
-## as possible to avoid bloat, if this library does not fulfill your needs,
-## write your own.
+## This module implements a simple logger.
 ##
-## Format strings support the following variables which must be prefixed with
-## the dollar operator (``$``):
+## It has been designed to be as simple as possible to avoid bloat.
+## If this library does not fulfill your needs, write your own.
+##
+## Basic usage
+## ===========
+##
+## To get started, first create a logger:
+##
+##   ```Nim
+##   import std/logging
+##
+##   var logger = newConsoleLogger()
+##   ```
+##
+## The logger that was created above logs to the console, but this module
+## also provides loggers that log to files, such as the
+## `FileLogger<#FileLogger>`_. Creating custom loggers is also possible by
+## inheriting from the `Logger<#Logger>`_ type.
+##
+## Once a logger has been created, call its `log proc
+## <#log.e,ConsoleLogger,Level,varargs[string,]>`_ to log a message:
+##
+##   ```Nim
+##   logger.log(lvlInfo, "a log message")
+##   # Output: INFO a log message
+##   ```
+##
+## The ``INFO`` within the output is the result of a format string being
+## prepended to the message, and it will differ depending on the message's
+## level. Format strings are `explained in more detail
+## here<#basic-usage-format-strings>`_.
+##
+## There are six logging levels: debug, info, notice, warn, error, and fatal.
+## They are described in more detail within the `Level enum's documentation
+## <#Level>`_. A message is logged if its level is at or above both the logger's
+## ``levelThreshold`` field and the global log filter. The latter can be changed
+## with the `setLogFilter proc<#setLogFilter,Level>`_.
+##
+## .. warning::
+##   For loggers that log to a console or to files, only error and fatal
+##   messages will cause their output buffers to be flushed immediately by default.
+##   set ``flushThreshold`` when creating the logger to change this.
+##
+## Handlers
+## --------
+##
+## When using multiple loggers, calling the log proc for each logger can
+## become repetitive. Instead of doing that, register each logger that will be
+## used with the `addHandler proc<#addHandler,Logger>`_, which is demonstrated
+## in the following example:
+##
+##   ```Nim
+##   import std/logging
+##
+##   var consoleLog = newConsoleLogger()
+##   var fileLog = newFileLogger("errors.log", levelThreshold=lvlError)
+##   var rollingLog = newRollingFileLogger("rolling.log")
+##
+##   addHandler(consoleLog)
+##   addHandler(fileLog)
+##   addHandler(rollingLog)
+##   ```
+##
+## After doing this, use either the `log template
+## <#log.t,Level,varargs[string,]>`_ or one of the level-specific templates,
+## such as the `error template<#error.t,varargs[string,]>`_, to log messages
+## to all registered handlers at once.
+##
+##   ```Nim
+##   # This example uses the loggers created above
+##   log(lvlError, "an error occurred")
+##   error("an error occurred")  # Equivalent to the above line
+##   info("something normal happened")  # Will not be written to errors.log
+##   ```
+##
+## Note that a message's level is still checked against each handler's
+## ``levelThreshold`` and the global log filter.
+##
+## Format strings
+## --------------
+##
+## Log messages are prefixed with format strings. These strings contain
+## placeholders for variables, such as ``$time``, that are replaced with their
+## corresponding values, such as the current time, before they are prepended to
+## a log message. Characters that are not part of variables are unaffected.
+##
+## The format string used by a logger can be specified by providing the `fmtStr`
+## argument when creating the logger or by setting its `fmtStr` field afterward.
+## If not specified, the `default format string<#defaultFmtStr>`_ is used.
+##
+## The following variables, which must be prefixed with a dollar sign (``$``),
+## are available:
 ##
 ## ============  =======================
-##   Operator     Output
+##   Variable      Output
 ## ============  =======================
 ## $date         Current date
 ## $time         Current time
 ## $datetime     $dateT$time
-## $app          ``os.getAppFilename()``
-## $appname      base name of $app
-## $appdir       directory name of $app
-## $levelid      first letter of log level
-## $levelname    log level name
+## $app          `os.getAppFilename()<os.html#getAppFilename>`_
+## $appname      Base name of ``$app``
+## $appdir       Directory name of ``$app``
+## $levelid      First letter of log level
+## $levelname    Log level name
 ## ============  =======================
 ##
+## Note that ``$app``, ``$appname``, and ``$appdir`` are not supported when
+## using the JavaScript backend.
 ##
-## The following example demonstrates logging to three different handlers
-## simultaneously:
+## The following example illustrates how to use format strings:
 ##
-## .. code-block:: nim
+##   ```Nim
+##   import std/logging
 ##
-##    var L = newConsoleLogger()
-##    var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
-##    var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
-##    addHandler(L)
-##    addHandler(fL)
-##    addHandler(rL)
-##    info("920410:52 accepted")
-##    warn("4 8 15 16 23 4-- Error")
-##    error("922044:16 SYSTEM FAILURE")
-##    fatal("SYSTEM FAILURE SYSTEM FAILURE")
+##   var logger = newConsoleLogger(fmtStr="[$time] - $levelname: ")
+##   logger.log(lvlInfo, "this is a message")
+##   # Output: [19:50:13] - INFO: this is a message
+##   ```
 ##
-## **Warning:** The global list of handlers is a thread var, this means that
-## the handlers must be re-added in each thread.
-## **Warning:** When logging on disk or console, only error and fatal messages
-## are flushed out immediately. Use flushFile() where needed.
-
-import strutils, times
+## Notes when using multiple threads
+## ---------------------------------
+##
+## There are a few details to keep in mind when using this module within
+## multiple threads:
+## * The global log filter is actually a thread-local variable, so it needs to
+##   be set in each thread that uses this module.
+## * The list of registered handlers is also a thread-local variable. If a
+##   handler will be used in multiple threads, it needs to be registered in
+##   each of those threads.
+##
+## See also
+## ========
+## * `strutils module<strutils.html>`_ for common string functions
+## * `strformat module<strformat.html>`_ for string interpolation and formatting
+## * `strscans module<strscans.html>`_ for ``scanf`` and ``scanp`` macros, which
+##   offer easier substring extraction than regular expressions
+
+import std/[strutils, times]
 when not defined(js):
-  import os
+  import std/os
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
 
 type
-  Level* = enum  ## logging level
-    lvlAll,       ## all levels active
-    lvlDebug,     ## debug level (and any above) active
-    lvlInfo,      ## info level (and any above) active
-    lvlNotice,    ## info notice (and any above) active
-    lvlWarn,      ## warn level (and any above) active
-    lvlError,     ## error level (and any above) active
-    lvlFatal,     ## fatal level (and any above) active
-    lvlNone       ## no levels active
+  Level* = enum ## \
+    ## Enumeration of logging levels.
+    ##
+    ## Debug messages represent the lowest logging level, and fatal error
+    ## messages represent the highest logging level. ``lvlAll`` can be used
+    ## to enable all messages, while ``lvlNone`` can be used to disable all
+    ## messages.
+    ##
+    ## Typical usage for each logging level, from lowest to highest, is
+    ## described below:
+    ##
+    ## * **Debug** - debugging information helpful only to developers
+    ## * **Info** - anything associated with normal operation and without
+    ##   any particular importance
+    ## * **Notice** - more important information that users should be
+    ##   notified about
+    ## * **Warn** - impending problems that require some attention
+    ## * **Error** - error conditions that the application can recover from
+    ## * **Fatal** - fatal errors that prevent the application from continuing
+    ##
+    ## It is completely up to the application how to utilize each level.
+    ##
+    ## Individual loggers have a ``levelThreshold`` field that filters out
+    ## any messages with a level lower than the threshold. There is also
+    ## a global filter that applies to all log messages, and it can be changed
+    ## using the `setLogFilter proc<#setLogFilter,Level>`_.
+    lvlAll,     ## All levels active
+    lvlDebug,   ## Debug level and above are active
+    lvlInfo,    ## Info level and above are active
+    lvlNotice,  ## Notice level and above are active
+    lvlWarn,    ## Warn level and above are active
+    lvlError,   ## Error level and above are active
+    lvlFatal,   ## Fatal level and above are active
+    lvlNone     ## No levels active; nothing is logged
 
 const
   LevelNames*: array[Level, string] = [
     "DEBUG", "DEBUG", "INFO", "NOTICE", "WARN", "ERROR", "FATAL", "NONE"
-  ]
+  ] ## Array of strings representing each logging level.
 
-  defaultFmtStr* = "$levelname " ## default format string
-  verboseFmtStr* = "$levelid, [$datetime] -- $appname: "
+  defaultFmtStr* = "$levelname "                         ## The default format string.
+  verboseFmtStr* = "$levelid, [$datetime] -- $appname: " ## \
+  ## A more verbose format string.
+  ##
+  ## This string can be passed as the ``frmStr`` argument to procs that create
+  ## new loggers, such as the `newConsoleLogger proc<#newConsoleLogger>`_.
+  ##
+  ## If a different format string is preferred, refer to the
+  ## `documentation about format strings<#basic-usage-format-strings>`_
+  ## for more information, including a list of available variables.
+  defaultFlushThreshold = when NimMajor >= 2:
+      when defined(nimV1LogFlushBehavior): lvlError else: lvlAll
+    else:
+      when defined(nimFlushAllLogs): lvlAll else: lvlError
+  ## The threshold above which log messages to file-like loggers
+  ## are automatically flushed.
+  ## 
+  ## By default, only error and fatal messages are logged,
+  ## but defining ``-d:nimFlushAllLogs`` will make all levels be flushed
 
 type
-  Logger* = ref object of RootObj ## abstract logger; the base type of all loggers
-    levelThreshold*: Level    ## only messages of level >= levelThreshold
-                              ## should be processed
-    fmtStr*: string ## = defaultFmtStr by default, see substituteLog for $date etc.
-
-  ConsoleLogger* = ref object of Logger ## logger that writes the messages to the
-                                        ## console
+  Logger* = ref object of RootObj
+    ## The abstract base type of all loggers.
+    ##
+    ## Custom loggers should inherit from this type. They should also provide
+    ## their own implementation of the
+    ## `log method<#log.e,Logger,Level,varargs[string,]>`_.
+    ##
+    ## See also:
+    ## * `ConsoleLogger<#ConsoleLogger>`_
+    ## * `FileLogger<#FileLogger>`_
+    ## * `RollingFileLogger<#RollingFileLogger>`_
+    levelThreshold*: Level ## Only messages that are at or above this
+                           ## threshold will be logged
+    fmtStr*: string ## Format string to prepend to each log message;
+                    ## defaultFmtStr is the default
+
+  ConsoleLogger* = ref object of Logger
+    ## A logger that writes log messages to the console.
+    ##
+    ## Create a new ``ConsoleLogger`` with the `newConsoleLogger proc
+    ## <#newConsoleLogger>`_.
+    ##
+    ## See also:
+    ## * `FileLogger<#FileLogger>`_
+    ## * `RollingFileLogger<#RollingFileLogger>`_
+    useStderr*: bool ## If true, writes to stderr; otherwise, writes to stdout
+    flushThreshold*: Level ## Only messages that are at or above this
+                           ## threshold will be flushed immediately
 
 when not defined(js):
   type
-    FileLogger* = ref object of Logger ## logger that writes the messages to a file
-      file*: File  ## the wrapped file.
-
-    RollingFileLogger* = ref object of FileLogger ## logger that writes the
-                                                  ## messages to a file and
-                                                  ## performs log rotation
+    FileLogger* = ref object of Logger
+      ## A logger that writes log messages to a file.
+      ##
+      ## Create a new ``FileLogger`` with the `newFileLogger proc
+      ## <#newFileLogger,File>`_.
+      ##
+      ## **Note:** This logger is not available for the JavaScript backend.
+      ##
+      ## See also:
+      ## * `ConsoleLogger<#ConsoleLogger>`_
+      ## * `RollingFileLogger<#RollingFileLogger>`_
+      file*: File ## The wrapped file
+      flushThreshold*: Level ## Only messages that are at or above this
+                           ## threshold will be flushed immediately
+
+    RollingFileLogger* = ref object of FileLogger
+      ## A logger that writes log messages to a file while performing log
+      ## rotation.
+      ##
+      ## Create a new ``RollingFileLogger`` with the `newRollingFileLogger proc
+      ## <#newRollingFileLogger,FileMode,Positive,int>`_.
+      ##
+      ## **Note:** This logger is not available for the JavaScript backend.
+      ##
+      ## See also:
+      ## * `ConsoleLogger<#ConsoleLogger>`_
+      ## * `FileLogger<#FileLogger>`_
       maxLines: int # maximum number of lines
-      curLine : int
+      curLine: int
       baseName: string # initial filename
       baseMode: FileMode # initial file mode
       logFiles: int # how many log files already created, e.g. basename.1, basename.2...
       bufSize: int # size of output buffer (-1: use system defaults, 0: unbuffered, >0: fixed buffer size)
 
 var
-  level {.threadvar.}: Level   ## global log filter
+  level {.threadvar.}: Level          ## global log filter
   handlers {.threadvar.}: seq[Logger] ## handlers with their own log levels
 
-proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): string =
-  ## Format a log message using the ``frmt`` format string, ``level`` and varargs.
-  ## See the module documentation for the format string syntax.
-  const nilString = "nil"
-
+proc substituteLog*(frmt: string, level: Level,
+                    args: varargs[string, `$`]): string =
+  ## Formats a log message at the specified level with the given format string.
+  ##
+  ## The `format variables<#basic-usage-format-strings>`_ present within
+  ## ``frmt`` will be replaced with the corresponding values before being
+  ## prepended to ``args`` and returned.
+  ##
+  ## Unless you are implementing a custom logger, there is little need to call
+  ## this directly. Use either a logger's log method or one of the logging
+  ## templates.
+  ##
+  ## See also:
+  ## * `log method<#log.e,ConsoleLogger,Level,varargs[string,]>`_
+  ##   for the ConsoleLogger
+  ## * `log method<#log.e,FileLogger,Level,varargs[string,]>`_
+  ##   for the FileLogger
+  ## * `log method<#log.e,RollingFileLogger,Level,varargs[string,]>`_
+  ##   for the RollingFileLogger
+  ## * `log template<#log.t,Level,varargs[string,]>`_
+  runnableExamples:
+    doAssert substituteLog(defaultFmtStr, lvlInfo, "a message") == "INFO a message"
+    doAssert substituteLog("$levelid - ", lvlError, "an error") == "E - an error"
+    doAssert substituteLog("$levelid", lvlDebug, "error") == "Derror"
   var msgLen = 0
   for arg in args:
-    if arg.isNil:
-      msgLen += nilString.len
-    else:
-      msgLen += arg.len
+    msgLen += arg.len
   result = newStringOfCap(frmt.len + msgLen + 20)
   var i = 0
   while i < frmt.len:
@@ -121,14 +321,14 @@ proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): str
       inc(i)
       var v = ""
       let app = when defined(js): "" else: getAppFilename()
-      while frmt[i] in IdentChars:
+      while i < frmt.len and frmt[i] in IdentChars:
         v.add(toLowerAscii(frmt[i]))
         inc(i)
       case v
       of "date": result.add(getDateStr())
       of "time": result.add(getClockStr())
       of "datetime": result.add(getDateStr() & "T" & getClockStr())
-      of "app":  result.add(app)
+      of "app": result.add(app)
       of "appdir":
         when not defined(js): result.add(app.splitFile.dir)
       of "appname":
@@ -137,75 +337,208 @@ proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): str
       of "levelname": result.add(LevelNames[level])
       else: discard
   for arg in args:
-    if arg.isNil:
-      result.add(nilString)
-    else:
-      result.add(arg)
+    result.add(arg)
 
 method log*(logger: Logger, level: Level, args: varargs[string, `$`]) {.
             raises: [Exception], gcsafe,
-            tags: [TimeEffect, WriteIOEffect, ReadIOEffect], base.} =
-  ## Override this method in custom loggers. Default implementation does
+            tags: [RootEffect], base.} =
+  ## Override this method in custom loggers. The default implementation does
   ## nothing.
+  ##
+  ## See also:
+  ## * `log method<#log.e,ConsoleLogger,Level,varargs[string,]>`_
+  ##   for the ConsoleLogger
+  ## * `log method<#log.e,FileLogger,Level,varargs[string,]>`_
+  ##   for the FileLogger
+  ## * `log method<#log.e,RollingFileLogger,Level,varargs[string,]>`_
+  ##   for the RollingFileLogger
+  ## * `log template<#log.t,Level,varargs[string,]>`_
   discard
 
 method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) =
-  ## Logs to the console using ``logger`` only.
+  ## Logs to the console with the given `ConsoleLogger<#ConsoleLogger>`_ only.
+  ##
+  ## This method ignores the list of registered handlers.
+  ##
+  ## Whether the message is logged depends on both the ConsoleLogger's
+  ## ``levelThreshold`` field and the global log filter set using the
+  ## `setLogFilter proc<#setLogFilter,Level>`_.
+  ##
+  ## **Note:** Only error and fatal messages will cause the output buffer
+  ## to be flushed immediately by default. Set ``flushThreshold`` when creating
+  ## the logger to change this.
+  ##
+  ## See also:
+  ## * `log method<#log.e,FileLogger,Level,varargs[string,]>`_
+  ##   for the FileLogger
+  ## * `log method<#log.e,RollingFileLogger,Level,varargs[string,]>`_
+  ##   for the RollingFileLogger
+  ## * `log template<#log.t,Level,varargs[string,]>`_
+  ##
+  ## **Examples:**
+  ##
+  ##   ```Nim
+  ##   var consoleLog = newConsoleLogger()
+  ##   consoleLog.log(lvlInfo, "this is a message")
+  ##   consoleLog.log(lvlError, "error code is: ", 404)
+  ##   ```
   if level >= logging.level and level >= logger.levelThreshold:
     let ln = substituteLog(logger.fmtStr, level, args)
     when defined(js):
-      let cln: cstring = ln
-      {.emit: "console.log(`cln`);".}
+      let cln = ln.cstring
+      case level
+      of lvlDebug: {.emit: "console.debug(`cln`);".}
+      of lvlInfo:  {.emit: "console.info(`cln`);".}
+      of lvlWarn:  {.emit: "console.warn(`cln`);".}
+      of lvlError: {.emit: "console.error(`cln`);".}
+      else:        {.emit: "console.log(`cln`);".}
     else:
       try:
-        writeLine(stdout, ln)
-        if level in {lvlError, lvlFatal}: flushFile(stdout)
+        var handle = stdout
+        if logger.useStderr:
+          handle = stderr
+        writeLine(handle, ln)
+        if level >= logger.flushThreshold: flushFile(handle)
       except IOError:
         discard
 
-proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr): ConsoleLogger =
-  ## Creates a new console logger. This logger logs to the console.
+proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr,
+    useStderr = false, flushThreshold = defaultFlushThreshold): ConsoleLogger =
+  ## Creates a new `ConsoleLogger<#ConsoleLogger>`_.
+  ##
+  ## By default, log messages are written to ``stdout``. If ``useStderr`` is
+  ## true, they are written to ``stderr`` instead.
+  ##
+  ## For the JavaScript backend, log messages are written to the console,
+  ## and ``useStderr`` is ignored.
+  ##
+  ## See also:
+  ## * `newFileLogger proc<#newFileLogger,File>`_ that uses a file handle
+  ## * `newFileLogger proc<#newFileLogger,FileMode,int>`_
+  ##   that accepts a filename
+  ## * `newRollingFileLogger proc<#newRollingFileLogger,FileMode,Positive,int>`_
+  ##
+  ## **Examples:**
+  ##
+  ##   ```Nim
+  ##   var normalLog = newConsoleLogger()
+  ##   var formatLog = newConsoleLogger(fmtStr=verboseFmtStr)
+  ##   var errorLog = newConsoleLogger(levelThreshold=lvlError, useStderr=true)
+  ##   ```
   new result
   result.fmtStr = fmtStr
   result.levelThreshold = levelThreshold
+  result.flushThreshold = flushThreshold
+  result.useStderr = useStderr
 
 when not defined(js):
   method log*(logger: FileLogger, level: Level, args: varargs[string, `$`]) =
-    ## Logs to a file using ``logger`` only.
+    ## Logs a message at the specified level using the given
+    ## `FileLogger<#FileLogger>`_ only.
+    ##
+    ## This method ignores the list of registered handlers.
+    ##
+    ## Whether the message is logged depends on both the FileLogger's
+    ## ``levelThreshold`` field and the global log filter set using the
+    ## `setLogFilter proc<#setLogFilter,Level>`_.
+    ##
+    ## **Notes:**
+    ## * Only error and fatal messages will cause the output buffer
+    ##   to be flushed immediately by default. Set ``flushThreshold`` when creating
+    ##   the logger to change this.
+    ## * This method is not available for the JavaScript backend.
+    ##
+    ## See also:
+    ## * `log method<#log.e,ConsoleLogger,Level,varargs[string,]>`_
+    ##   for the ConsoleLogger
+    ## * `log method<#log.e,RollingFileLogger,Level,varargs[string,]>`_
+    ##   for the RollingFileLogger
+    ## * `log template<#log.t,Level,varargs[string,]>`_
+    ##
+    ## **Examples:**
+    ##
+    ##   ```Nim
+    ##   var fileLog = newFileLogger("messages.log")
+    ##   fileLog.log(lvlInfo, "this is a message")
+    ##   fileLog.log(lvlError, "error code is: ", 404)
+    ##   ```
     if level >= logging.level and level >= logger.levelThreshold:
       writeLine(logger.file, substituteLog(logger.fmtStr, level, args))
-      if level in {lvlError, lvlFatal}: flushFile(logger.file)
+      if level >= logger.flushThreshold: flushFile(logger.file)
 
   proc defaultFilename*(): string =
-    ## Returns the default filename for a logger.
+    ## Returns the filename that is used by default when naming log files.
+    ##
+    ## **Note:** This proc is not available for the JavaScript backend.
     var (path, name, _) = splitFile(getAppFilename())
     result = changeFileExt(path / name, "log")
 
   proc newFileLogger*(file: File,
                       levelThreshold = lvlAll,
-                      fmtStr = defaultFmtStr): FileLogger =
-    ## Creates a new file logger. This logger logs to ``file``.
+                      fmtStr = defaultFmtStr,
+                      flushThreshold = defaultFlushThreshold): FileLogger =
+    ## Creates a new `FileLogger<#FileLogger>`_ that uses the given file handle.
+    ##
+    ## **Note:** This proc is not available for the JavaScript backend.
+    ##
+    ## See also:
+    ## * `newConsoleLogger proc<#newConsoleLogger>`_
+    ## * `newFileLogger proc<#newFileLogger,FileMode,int>`_
+    ##   that accepts a filename
+    ## * `newRollingFileLogger proc<#newRollingFileLogger,FileMode,Positive,int>`_
+    ##
+    ## **Examples:**
+    ##
+    ##   ```Nim
+    ##   var messages = open("messages.log", fmWrite)
+    ##   var formatted = open("formatted.log", fmWrite)
+    ##   var errors = open("errors.log", fmWrite)
+    ##
+    ##   var normalLog = newFileLogger(messages)
+    ##   var formatLog = newFileLogger(formatted, fmtStr=verboseFmtStr)
+    ##   var errorLog = newFileLogger(errors, levelThreshold=lvlError)
+    ##   ```
     new(result)
     result.file = file
     result.levelThreshold = levelThreshold
+    result.flushThreshold = flushThreshold
     result.fmtStr = fmtStr
 
   proc newFileLogger*(filename = defaultFilename(),
                       mode: FileMode = fmAppend,
                       levelThreshold = lvlAll,
                       fmtStr = defaultFmtStr,
-                      bufSize: int = -1): FileLogger =
-    ## Creates a new file logger. This logger logs to a file, specified
-    ## by ``fileName``.
-    ## Use ``bufSize`` as size of the output buffer when writing the file
-    ## (-1: use system defaults, 0: unbuffered, >0: fixed buffer size).
+                      bufSize: int = -1,
+                      flushThreshold = defaultFlushThreshold): FileLogger =
+    ## Creates a new `FileLogger<#FileLogger>`_ that logs to a file with the
+    ## given filename.
+    ##
+    ## ``bufSize`` controls the size of the output buffer that is used when
+    ## writing to the log file. The following values can be provided:
+    ## * ``-1`` - use system defaults
+    ## * ``0`` - unbuffered
+    ## * ``> 0`` - fixed buffer size
+    ##
+    ## **Note:** This proc is not available for the JavaScript backend.
+    ##
+    ## See also:
+    ## * `newConsoleLogger proc<#newConsoleLogger>`_
+    ## * `newFileLogger proc<#newFileLogger,File>`_ that uses a file handle
+    ## * `newRollingFileLogger proc<#newRollingFileLogger,FileMode,Positive,int>`_
+    ##
+    ## **Examples:**
+    ##
+    ##   ```Nim
+    ##   var normalLog = newFileLogger("messages.log")
+    ##   var formatLog = newFileLogger("formatted.log", fmtStr=verboseFmtStr)
+    ##   var errorLog = newFileLogger("errors.log", levelThreshold=lvlError)
+    ##   ```
     let file = open(filename, mode, bufSize = bufSize)
-    newFileLogger(file, levelThreshold, fmtStr)
+    newFileLogger(file, levelThreshold, fmtStr, flushThreshold)
 
   # ------
 
   proc countLogLines(logger: RollingFileLogger): int =
-    result = 0
     let fp = open(logger.baseName, fmRead)
     for line in fp.lines():
       result.inc()
@@ -232,21 +565,46 @@ when not defined(js):
                             mode: FileMode = fmReadWrite,
                             levelThreshold = lvlAll,
                             fmtStr = defaultFmtStr,
-                            maxLines = 1000,
-                            bufSize: int = -1): RollingFileLogger =
-    ## Creates a new rolling file logger. Once a file reaches ``maxLines`` lines
-    ## a new log file will be started and the old will be renamed.
-    ## Use ``bufSize`` as size of the output buffer when writing the file
-    ## (-1: use system defaults, 0: unbuffered, >0: fixed buffer size).
+                            maxLines: Positive = 1000,
+                            bufSize: int = -1,
+                            flushThreshold = defaultFlushThreshold): RollingFileLogger =
+    ## Creates a new `RollingFileLogger<#RollingFileLogger>`_.
+    ##
+    ## Once the current log file being written to contains ``maxLines`` lines,
+    ## a new log file will be created, and the old log file will be renamed.
+    ##
+    ## ``bufSize`` controls the size of the output buffer that is used when
+    ## writing to the log file. The following values can be provided:
+    ## * ``-1`` - use system defaults
+    ## * ``0`` - unbuffered
+    ## * ``> 0`` - fixed buffer size
+    ##
+    ## **Note:** This proc is not available in the JavaScript backend.
+    ##
+    ## See also:
+    ## * `newConsoleLogger proc<#newConsoleLogger>`_
+    ## * `newFileLogger proc<#newFileLogger,File>`_ that uses a file handle
+    ## * `newFileLogger proc<#newFileLogger,FileMode,int>`_
+    ##   that accepts a filename
+    ##
+    ## **Examples:**
+    ##
+    ##   ```Nim
+    ##   var normalLog = newRollingFileLogger("messages.log")
+    ##   var formatLog = newRollingFileLogger("formatted.log", fmtStr=verboseFmtStr)
+    ##   var shortLog = newRollingFileLogger("short.log", maxLines=200)
+    ##   var errorLog = newRollingFileLogger("errors.log", levelThreshold=lvlError)
+    ##   ```
     new(result)
     result.levelThreshold = levelThreshold
     result.fmtStr = fmtStr
     result.maxLines = maxLines
     result.bufSize = bufSize
-    result.file = open(filename, mode, bufSize=result.bufSize)
+    result.file = open(filename, mode, bufSize = result.bufSize)
     result.curLine = 0
     result.baseName = filename
     result.baseMode = mode
+    result.flushThreshold = flushThreshold
 
     result.logFiles = countFiles(filename)
 
@@ -262,17 +620,46 @@ when not defined(js):
               dir / (name & ext & ExtSep & $(i+1)))
 
   method log*(logger: RollingFileLogger, level: Level, args: varargs[string, `$`]) =
-    ## Logs to a file using rolling ``logger`` only.
+    ## Logs a message at the specified level using the given
+    ## `RollingFileLogger<#RollingFileLogger>`_ only.
+    ##
+    ## This method ignores the list of registered handlers.
+    ##
+    ## Whether the message is logged depends on both the RollingFileLogger's
+    ## ``levelThreshold`` field and the global log filter set using the
+    ## `setLogFilter proc<#setLogFilter,Level>`_.
+    ##
+    ## **Notes:**
+    ## * Only error and fatal messages will cause the output buffer
+    ##   to be flushed immediately by default. Set ``flushThreshold`` when creating
+    ##   the logger to change this.
+    ## * This method is not available for the JavaScript backend.
+    ##
+    ## See also:
+    ## * `log method<#log.e,ConsoleLogger,Level,varargs[string,]>`_
+    ##   for the ConsoleLogger
+    ## * `log method<#log.e,FileLogger,Level,varargs[string,]>`_
+    ##   for the FileLogger
+    ## * `log template<#log.t,Level,varargs[string,]>`_
+    ##
+    ## **Examples:**
+    ##
+    ##   ```Nim
+    ##   var rollingLog = newRollingFileLogger("messages.log")
+    ##   rollingLog.log(lvlInfo, "this is a message")
+    ##   rollingLog.log(lvlError, "error code is: ", 404)
+    ##   ```
     if level >= logging.level and level >= logger.levelThreshold:
       if logger.curLine >= logger.maxLines:
         logger.file.close()
         rotate(logger)
         logger.logFiles.inc
         logger.curLine = 0
-        logger.file = open(logger.baseName, logger.baseMode, bufSize = logger.bufSize)
+        logger.file = open(logger.baseName, logger.baseMode,
+            bufSize = logger.bufSize)
 
       writeLine(logger.file, substituteLog(logger.fmtStr, level, args))
-      if level in {lvlError, lvlFatal}: flushFile(logger.file)
+      if level >= logger.flushThreshold: flushFile(logger.file)
       logger.curLine.inc
 
 # --------
@@ -283,7 +670,28 @@ proc logLoop(level: Level, args: varargs[string, `$`]) =
       log(logger, level, args)
 
 template log*(level: Level, args: varargs[string, `$`]) =
-  ## Logs a message to all registered handlers at the given level.
+  ## Logs a message at the specified level to all registered handlers.
+  ##
+  ## Whether the message is logged depends on both the FileLogger's
+  ## `levelThreshold` field and the global log filter set using the
+  ## `setLogFilter proc<#setLogFilter,Level>`_.
+  ##
+  ## **Examples:**
+  ##
+  ##   ```Nim
+  ##   var logger = newConsoleLogger()
+  ##   addHandler(logger)
+  ##
+  ##   log(lvlInfo, "This is an example.")
+  ##   ```
+  ##
+  ## See also:
+  ## * `debug template<#debug.t,varargs[string,]>`_
+  ## * `info template<#info.t,varargs[string,]>`_
+  ## * `notice template<#notice.t,varargs[string,]>`_
+  ## * `warn template<#warn.t,varargs[string,]>`_
+  ## * `error template<#error.t,varargs[string,]>`_
+  ## * `fatal template<#fatal.t,varargs[string,]>`_
   bind logLoop
   bind `%`
   bind logging.level
@@ -294,63 +702,192 @@ template log*(level: Level, args: varargs[string, `$`]) =
 template debug*(args: varargs[string, `$`]) =
   ## Logs a debug message to all registered handlers.
   ##
-  ## Messages that are useful to the application developer only and are usually
-  ## turned off in release.
+  ## Debug messages are typically useful to the application developer only,
+  ## and they are usually disabled in release builds, although this template
+  ## does not make that distinction.
+  ##
+  ## **Examples:**
+  ##
+  ##   ```Nim
+  ##   var logger = newConsoleLogger()
+  ##   addHandler(logger)
+  ##
+  ##   debug("myProc called with arguments: foo, 5")
+  ##   ```
+  ##
+  ## See also:
+  ## * `log template<#log.t,Level,varargs[string,]>`_
+  ## * `info template<#info.t,varargs[string,]>`_
+  ## * `notice template<#notice.t,varargs[string,]>`_
   log(lvlDebug, args)
 
 template info*(args: varargs[string, `$`]) =
   ## Logs an info message to all registered handlers.
   ##
-  ## Messages that are generated during the normal operation of an application
-  ## and are of no particular importance. Useful to aggregate for potential
-  ## later analysis.
+  ## Info messages are typically generated during the normal operation
+  ## of an application and are of no particular importance. It can be useful to
+  ## aggregate these messages for later analysis.
+  ##
+  ## **Examples:**
+  ##
+  ##   ```Nim
+  ##   var logger = newConsoleLogger()
+  ##   addHandler(logger)
+  ##
+  ##   info("Application started successfully.")
+  ##   ```
+  ##
+  ## See also:
+  ## * `log template<#log.t,Level,varargs[string,]>`_
+  ## * `debug template<#debug.t,varargs[string,]>`_
+  ## * `notice template<#notice.t,varargs[string,]>`_
   log(lvlInfo, args)
 
 template notice*(args: varargs[string, `$`]) =
-  ## Logs an notice message to all registered handlers.
+  ## Logs an notice to all registered handlers.
+  ##
+  ## Notices are semantically very similar to info messages, but they are meant
+  ## to be messages that the user should be actively notified about, depending
+  ## on the application.
+  ##
+  ## **Examples:**
+  ##
+  ##   ```Nim
+  ##   var logger = newConsoleLogger()
+  ##   addHandler(logger)
   ##
-  ## Semantically very similar to `info`, but meant to be messages you want to
-  ## be actively notified about (depending on your application).
-  ## These could be, for example, grouped by hour and mailed out.
+  ##   notice("An important operation has completed.")
+  ##   ```
+  ##
+  ## See also:
+  ## * `log template<#log.t,Level,varargs[string,]>`_
+  ## * `debug template<#debug.t,varargs[string,]>`_
+  ## * `info template<#info.t,varargs[string,]>`_
   log(lvlNotice, args)
 
 template warn*(args: varargs[string, `$`]) =
   ## Logs a warning message to all registered handlers.
   ##
-  ## A non-error message that may indicate a potential problem rising or
-  ## impacted performance.
+  ## A warning is a non-error message that may indicate impending problems or
+  ## degraded performance.
+  ##
+  ## **Examples:**
+  ##
+  ##   ```Nim
+  ##   var logger = newConsoleLogger()
+  ##   addHandler(logger)
+  ##
+  ##   warn("The previous operation took too long to process.")
+  ##   ```
+  ##
+  ## See also:
+  ## * `log template<#log.t,Level,varargs[string,]>`_
+  ## * `error template<#error.t,varargs[string,]>`_
+  ## * `fatal template<#fatal.t,varargs[string,]>`_
   log(lvlWarn, args)
 
 template error*(args: varargs[string, `$`]) =
   ## Logs an error message to all registered handlers.
   ##
-  ## A application-level error condition. For example, some user input generated
-  ## an exception. The application will continue to run, but functionality or
-  ## data was impacted, possibly visible to users.
+  ## Error messages are for application-level error conditions, such as when
+  ## some user input generated an exception. Typically, the application will
+  ## continue to run, but with degraded functionality or loss of data, and
+  ## these effects might be visible to users.
+  ##
+  ## **Examples:**
+  ##
+  ##   ```Nim
+  ##   var logger = newConsoleLogger()
+  ##   addHandler(logger)
+  ##
+  ##   error("An exception occurred while processing the form.")
+  ##   ```
+  ##
+  ## See also:
+  ## * `log template<#log.t,Level,varargs[string,]>`_
+  ## * `warn template<#warn.t,varargs[string,]>`_
+  ## * `fatal template<#fatal.t,varargs[string,]>`_
   log(lvlError, args)
 
 template fatal*(args: varargs[string, `$`]) =
   ## Logs a fatal error message to all registered handlers.
   ##
-  ## A application-level fatal condition. FATAL usually means that the application
-  ## cannot go on and will exit (but this logging event will not do that for you).
+  ## Fatal error messages usually indicate that the application cannot continue
+  ## to run and will exit due to a fatal condition. This template only logs the
+  ## message, and it is the application's responsibility to exit properly.
+  ##
+  ## **Examples:**
+  ##
+  ##   ```Nim
+  ##   var logger = newConsoleLogger()
+  ##   addHandler(logger)
+  ##
+  ##   fatal("Can't open database -- exiting.")
+  ##   ```
+  ##
+  ## See also:
+  ## * `log template<#log.t,Level,varargs[string,]>`_
+  ## * `warn template<#warn.t,varargs[string,]>`_
+  ## * `error template<#error.t,varargs[string,]>`_
   log(lvlFatal, args)
 
 proc addHandler*(handler: Logger) =
-  ## Adds ``handler`` to the list of handlers.
-  if handlers.isNil: handlers = @[]
+  ## Adds a logger to the list of registered handlers.
+  ##
+  ## .. warning:: The list of handlers is a thread-local variable. If the given
+  ##   handler will be used in multiple threads, this proc should be called in
+  ##   each of those threads.
+  ##
+  ## See also:
+  ## * `removeHandler proc`_
+  ## * `getHandlers proc<#getHandlers>`_
+  runnableExamples:
+    var logger = newConsoleLogger()
+    addHandler(logger)
+    doAssert logger in getHandlers()
   handlers.add(handler)
 
+proc removeHandler*(handler: Logger) =
+  ## Removes a logger from the list of registered handlers.
+  ##
+  ## Note that for n times a logger is registered, n calls to this proc
+  ## are required to remove that logger.
+  for i, hnd in handlers:
+    if hnd == handler:
+      handlers.delete(i)
+      return
+
 proc getHandlers*(): seq[Logger] =
   ## Returns a list of all the registered handlers.
+  ##
+  ## See also:
+  ## * `addHandler proc<#addHandler,Logger>`_
   return handlers
 
 proc setLogFilter*(lvl: Level) =
   ## Sets the global log filter.
+  ##
+  ## Messages below the provided level will not be logged regardless of an
+  ## individual logger's ``levelThreshold``. By default, all messages are
+  ## logged.
+  ##
+  ## .. warning:: The global log filter is a thread-local variable. If logging
+  ##   is being performed in multiple threads, this proc should be called in each
+  ##   thread unless it is intended that different threads should log at different
+  ##   logging levels.
+  ##
+  ## See also:
+  ## * `getLogFilter proc<#getLogFilter>`_
+  runnableExamples:
+    setLogFilter(lvlError)
+    doAssert getLogFilter() == lvlError
   level = lvl
 
 proc getLogFilter*(): Level =
   ## Gets the global log filter.
+  ##
+  ## See also:
+  ## * `setLogFilter proc<#setLogFilter,Level>`_
   return level
 
 # --------------
diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim
index b90d2899c..f9b3d3e4c 100644
--- a/lib/pure/marshal.nim
+++ b/lib/pure/marshal.nim
@@ -7,38 +7,57 @@
 #    distribution, for details about the copyright.
 #
 
-## This module contains procs for `serialization`:idx: and `deseralization`:idx:
+## This module contains procs for `serialization`:idx: and `deserialization`:idx:
 ## of arbitrary Nim data structures. The serialization format uses `JSON`:idx:.
-## Warning: The serialization format could change in future!
 ##
-## **Restriction**: For objects their type is **not** serialized. This means
+## **Restriction:** For objects, their type is **not** serialized. This means
 ## essentially that it does not work if the object has some other runtime
-## type than its compiletime type:
+## type than its compiletime type.
 ##
-## .. code-block:: nim
 ##
-##   type
-##     A = object of RootObj
-##     B = object of A
-##       f: int
+## Basic usage
+## ===========
 ##
-##   var
-##     a: ref A
-##     b: ref B
-##
-##   new(b)
-##   a = b
-##   echo($$a[]) # produces "{}", not "{f: 0}"
+runnableExamples:
+  type
+    A = object of RootObj
+    B = object of A
+      f: int
+
+  let a: ref A = new(B)
+  assert $$a[] == "{}" # not "{f: 0}"
+
+  # unmarshal
+  let c = to[B]("""{"f": 2}""")
+  assert typeof(c) is B
+  assert c.f == 2
+
+  # marshal
+  assert $$c == """{"f": 2}"""
+
+## **Note:** The `to` and `$$` operations are available at compile-time!
 ##
-##   # unmarshal
-##   let c = to[B]("""{"f": 2}""")
 ##
-##   # marshal
-##   let s = $$c
+## See also
+## ========
+## * `streams module <streams.html>`_
+## * `json module <json.html>`_
+
+const unsupportedPlatform =
+  when defined(js): "javascript"
+  elif defined(nimscript): "nimscript"
+  else: ""
+
+when unsupportedPlatform != "":
+  {.error: "marshal module is not supported in " & unsupportedPlatform & """.
+Please use alternative packages for serialization.
+It is possible to reimplement this module using generics and type traits.
+Please contribute a new implementation.""".}
 
-## **Note**: The ``to`` and ``$$`` operations are available at compile-time!
+import std/[streams, typeinfo, json, intsets, tables, unicode]
 
-import streams, typeinfo, json, intsets, tables, unicode
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, formatfloat]
 
 proc ptrToInt(x: pointer): int {.inline.} =
   result = cast[int](x) # don't skip alignment
@@ -54,13 +73,11 @@ proc storeAny(s: Stream, a: Any, stored: var IntSet) =
     else:
       s.write($int(ch))
   of akArray, akSequence:
-    if a.kind == akSequence and isNil(a): s.write("null")
-    else:
-      s.write("[")
-      for i in 0 .. a.len-1:
-        if i > 0: s.write(", ")
-        storeAny(s, a[i], stored)
-      s.write("]")
+    s.write("[")
+    for i in 0 .. a.len-1:
+      if i > 0: s.write(", ")
+      storeAny(s, a[i], stored)
+    s.write("]")
   of akObject, akTuple:
     s.write("{")
     var i = 0
@@ -98,8 +115,7 @@ proc storeAny(s: Stream, a: Any, stored: var IntSet) =
   of akProc, akPointer, akCString: s.write($a.getPointer.ptrToInt)
   of akString:
     var x = getString(a)
-    if isNil(x): s.write("null")
-    elif x.validateUtf8() == -1: s.write(escapeJson(x))
+    if x.validateUtf8() == -1: s.write(escapeJson(x))
     else:
       s.write("[")
       var i = 0
@@ -150,7 +166,10 @@ proc loadAny(p: var JsonParser, a: Any, t: var Table[BiggestInt, pointer]) =
   of akSequence:
     case p.kind
     of jsonNull:
-      setPointer(a, nil)
+      when defined(nimSeqsV2):
+        invokeNewSeq(a, 0)
+      else:
+        setPointer(a, nil)
       next(p)
     of jsonArrayStart:
       next(p)
@@ -191,7 +210,8 @@ proc loadAny(p: var JsonParser, a: Any, t: var Table[BiggestInt, pointer]) =
       setPointer(a, nil)
       next(p)
     of jsonInt:
-      setPointer(a, t.getOrDefault(p.getInt))
+      var raw = t.getOrDefault(p.getInt)
+      setPointer(a, addr raw)
       next(p)
     of jsonArrayStart:
       next(p)
@@ -217,7 +237,10 @@ proc loadAny(p: var JsonParser, a: Any, t: var Table[BiggestInt, pointer]) =
   of akString:
     case p.kind
     of jsonNull:
-      setPointer(a, nil)
+      when defined(nimSeqsV2):
+        setString(a, "")
+      else:
+        setPointer(a, nil)
       next(p)
     of jsonString:
       setString(a, p.str)
@@ -257,104 +280,88 @@ proc loadAny(s: Stream, a: Any, t: var Table[BiggestInt, pointer]) =
   close(p)
 
 proc load*[T](s: Stream, data: var T) =
-  ## loads `data` from the stream `s`. Raises `EIO` in case of an error.
+  ## Loads `data` from the stream `s`. Raises `IOError` in case of an error.
+  runnableExamples:
+    import std/streams
+
+    var s = newStringStream("[1, 3, 5]")
+    var a: array[3, int]
+    load(s, a)
+    assert a == [1, 3, 5]
+
   var tab = initTable[BiggestInt, pointer]()
   loadAny(s, toAny(data), tab)
 
-proc store*[T](s: Stream, data: T) =
-  ## stores `data` into the stream `s`. Raises `EIO` in case of an error.
-  var stored = initIntSet()
-  var d: T
-  shallowCopy(d, data)
-  storeAny(s, toAny(d), stored)
+proc store*[T](s: Stream, data: sink T) =
+  ## Stores `data` into the stream `s`. Raises `IOError` in case of an error.
+  runnableExamples:
+    import std/streams
+
+    var s = newStringStream("")
+    var a = [1, 3, 5]
+    store(s, a)
+    s.setPosition(0)
+    assert s.readAll() == "[1, 3, 5]"
 
-proc `$$`*[T](x: T): string =
-  ## returns a string representation of `x`.
-  ##
-  ## Note: to serialize `x` to JSON use $(%x) from the ``json`` module
   var stored = initIntSet()
   var d: T
-  shallowCopy(d, x)
-  var s = newStringStream()
+  when defined(gcArc) or defined(gcOrc)or defined(gcAtomicArc):
+    d = data
+  else:
+    shallowCopy(d, data)
   storeAny(s, toAny(d), stored)
-  result = s.data
-
-proc to*[T](data: string): T =
-  ## reads data and transforms it to a ``T``.
-  var tab = initTable[BiggestInt, pointer]()
-  loadAny(newStringStream(data), toAny(result), tab)
-
-when not defined(testing) and isMainModule:
-  template testit(x: untyped) = echo($$to[type(x)]($$x))
-
-  var x: array[0..4, array[0..4, string]] = [
-    ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
-    ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
-    ["test", "1", "2", "3", "4"]]
-  testit(x)
-  var test2: tuple[name: string, s: uint] = ("tuple test", 56u)
-  testit(test2)
-
-  type
-    TE = enum
-      blah, blah2
-
-    TestObj = object
-      test, asd: int
-      case test2: TE
-      of blah:
-        help: string
-      else:
-        nil
-
-    PNode = ref Node
-    Node = object
-      next, prev: PNode
-      data: string
-
-  proc buildList(): PNode =
-    new(result)
-    new(result.next)
-    new(result.prev)
-    result.data = "middle"
-    result.next.data = "next"
-    result.prev.data = "prev"
-    result.next.next = result.prev
-    result.next.prev = result
-    result.prev.next = result
-    result.prev.prev = result.next
-
-  var test3: TestObj
-  test3.test = 42
-  test3.test2 = blah
-  testit(test3)
-
-  var test4: ref tuple[a, b: string]
-  new(test4)
-  test4.a = "ref string test: A"
-  test4.b = "ref string test: B"
-  testit(test4)
-
-  var test5 = @[(0,1),(2,3),(4,5)]
-  testit(test5)
 
-  var test6: set[char] = {'A'..'Z', '_'}
-  testit(test6)
+proc loadVM[T](typ: typedesc[T], x: T): string =
+  discard "the implementation is in the compiler/vmops"
 
-  var test7 = buildList()
-  echo($$test7)
-  testit(test7)
+proc `$$`*[T](x: sink T): string =
+  ## Returns a string representation of `x` (serialization, marshalling).
+  ##
+  ## **Note:** to serialize `x` to JSON use `%x` from the `json` module
+  ## or `jsonutils.toJson(x)`.
+  runnableExamples:
+    type
+      Foo = object
+        id: int
+        bar: string
+    let x = Foo(id: 1, bar: "baz")
+    ## serialize:
+    let y = $$x
+    assert y == """{"id": 1, "bar": "baz"}"""
 
-  type
-    A {.inheritable.} = object
-    B = object of A
-      f: int
+  when nimvm:
+    result = loadVM(T, x)
+  else:
+    var stored = initIntSet()
+    var d: T
+    when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
+      d = x
+    else:
+      shallowCopy(d, x)
+    var s = newStringStream()
+    storeAny(s, toAny(d), stored)
+    result = s.data
 
-  var
-    a: ref A
-    b: ref B
-  new(b)
-  a = b
-  echo($$a[]) # produces "{}", not "{f: 0}"
+proc toVM[T](typ: typedesc[T], data: string): T =
+  discard "the implementation is in the compiler/vmops"
 
+proc to*[T](data: string): T =
+  ## Reads data and transforms it to a type `T` (deserialization, unmarshalling).
+  runnableExamples:
+    type
+      Foo = object
+        id: int
+        bar: string
+    let y = """{"id": 1, "bar": "baz"}"""
+    assert typeof(y) is string
+    ## deserialize to type 'Foo':
+    let z = y.to[:Foo]
+    assert typeof(z) is Foo
+    assert z.id == 1
+    assert z.bar == "baz"
 
+  when nimvm:
+    result = toVM(T, data)
+  else:
+    var tab = initTable[BiggestInt, pointer]()
+    loadAny(newStringStream(data), toAny(result), tab)
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 b921b1841..ed7d2382f 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -7,39 +7,146 @@
 #    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.
-## 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.
+## The helper functions `degToRad <#degToRad,T>`_ and `radToDeg <#radToDeg,T>`_
+## provide conversion between radians and degrees.
+
+runnableExamples:
+  from std/fenv import epsilon
+  from std/random import rand
+
+  proc generateGaussianNoise(mu: float = 0.0, sigma: float = 1.0): (float, float) =
+    # Generates values from a normal distribution.
+    # Translated from https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform#Implementation.
+    var u1: float
+    var u2: float
+    while true:
+      u1 = rand(1.0)
+      u2 = rand(1.0)
+      if u1 > epsilon(float): break
+    let mag = sigma * sqrt(-2 * ln(u1))
+    let z0 = mag * cos(2 * PI * u2) + mu
+    let z1 = mag * sin(2 * PI * u2) + mu
+    (z0, z1)
+
+  echo generateGaussianNoise()
 
-include "system/inclrtl"
-{.push debugger:off .} # the user does not want to trace a part
+## This module is available for the `JavaScript target
+## <backends.html#backends-the-javascript-target>`_.
+##
+## 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-divide, etc.)
+## * `random module <random.html>`_ for a fast and tiny random number generator
+## * `stats module <stats.html>`_ for statistical analysis
+## * `strformat module <strformat.html>`_ for formatting floats for printing
+## * `system module <system.html>`_ for some very basic and trivial math operators
+##   (`shr`, `shl`, `xor`, `clamp`, etc.)
+
+
+import std/private/since
+{.push debugger: off.} # the user does not want to trace a part
                        # of the standard library!
 
-import bitops
+import std/[bitops, fenv]
+import system/countbits_impl
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
+when not defined(js) and not defined(nimscript): # C
+  proc c_isnan(x: float): bool {.importc: "isnan", header: "<math.h>".}
+    # a generic like `x: SomeFloat` might work too if this is implemented via a C macro.
+
+  proc c_copysign(x, y: cfloat): cfloat {.importc: "copysignf", header: "<math.h>".}
+  proc c_copysign(x, y: cdouble): cdouble {.importc: "copysign", header: "<math.h>".}
+
+  proc c_signbit(x: SomeFloat): cint {.importc: "signbit", header: "<math.h>".}
+
+  # don't export `c_frexp` in the future and remove `c_frexp2`.
+  func c_frexp2(x: cfloat, exponent: var cint): cfloat {.
+      importc: "frexpf", header: "<math.h>".}
+  func c_frexp2(x: cdouble, exponent: var cint): cdouble {.
+      importc: "frexp", header: "<math.h>".}
+  
+  type
+    div_t {.importc, header: "<stdlib.h>".} = object
+      quot: cint
+      rem: cint
+    ldiv_t {.importc, header: "<stdlib.h>".} = object
+      quot: clong
+      rem: clong
+    lldiv_t {.importc, header: "<stdlib.h>".} = object
+      quot: clonglong
+      rem: clonglong
+  
+  when cint isnot clong:
+    func divmod_c(x, y: cint): div_t {.importc: "div", header: "<stdlib.h>".}
+  when clong isnot clonglong:
+    func divmod_c(x, y: clonglong): lldiv_t {.importc: "lldiv", header: "<stdlib.h>".}
+  func divmod_c(x, y: clong): ldiv_t {.importc: "ldiv", header: "<stdlib.h>".}
+  func divmod*[T: SomeInteger](x, y: T): (T, T) {.inline.} = 
+    ## Specialized instructions for computing both division and modulus.
+    ## Return structure is: (quotient, remainder)
+    runnableExamples:
+      doAssert divmod(5, 2) == (2, 1)
+      doAssert divmod(5, -3) == (-1, 2)
+    when T is cint | clong | clonglong:
+      when compileOption("overflowChecks"):
+        if y == 0:
+          raise new(DivByZeroDefect)
+        elif (x == T.low and y == -1.T):
+          raise new(OverflowDefect)
+      let res = divmod_c(x, y)
+      result[0] = res.quot
+      result[1] = res.rem
+    else:
+      result[0] = x div y
+      result[1] = x mod y
+
+func binom*(n, k: int): int =
+  ## Computes the [binomial coefficient](https://en.wikipedia.org/wiki/Binomial_coefficient).
+  runnableExamples:
+    doAssert binom(6, 2) == 15
+    doAssert binom(-6, 2) == 1
+    doAssert binom(6, 0) == 1
 
-proc binom*(n, k: int): int {.noSideEffect.} =
-  ## Computes the binomial coefficient
   if k <= 0: return 1
-  if 2*k > n: return binom(n, n-k)
+  if 2 * k > n: return binom(n, n - k)
   result = n
   for i in countup(2, k):
     result = (result * (n + 1 - i)) div i
 
-proc createFactTable[N: static[int]]: array[N, int] =
+func createFactTable[N: static[int]]: array[N, int] =
   result[0] = 1
   for i in 1 ..< N:
     result[i] = result[i - 1] * i
 
-proc fac*(n: int): int =
-  ## Computes the faculty/factorial function.
+func fac*(n: int): int =
+  ## Computes the [factorial](https://en.wikipedia.org/wiki/Factorial) of
+  ## a non-negative integer `n`.
+  ##
+  ## **See also:**
+  ## * `prod func <#prod,openArray[T]>`_
+  runnableExamples:
+    doAssert fac(0) == 1
+    doAssert fac(4) == 24
+    doAssert fac(10) == 3628800
+
   const factTable =
-    when sizeof(int) == 4:
+    when sizeof(int) == 2:
+      createFactTable[5]()
+    elif sizeof(int) == 4:
       createFactTable[13]()
     else:
       createFactTable[21]()
@@ -47,64 +154,201 @@ proc fac*(n: int): int =
   assert(n < factTable.len, $n & " is too large to look up in the table")
   factTable[n]
 
-{.push checks:off, line_dir:off, stack_trace:off.}
+{.push checks: off, line_dir: off, stack_trace: off.}
 
-when defined(Posix) and not defined(haiku):
+when defined(posix) and not defined(genode) and not defined(macosx):
   {.passl: "-lm".}
 
 const
-  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
-                            ## after the decimal point for Nim's
-                            ## ``float64`` type.
-  MaxFloat32Precision* = 8  ## maximum number of meaningful digits
-                            ## after the decimal point for Nim's
-                            ## ``float32`` type.
-  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
+  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
+                                             ## after the decimal point for Nim's
+                                             ## `float64` type.
+  MaxFloat32Precision* = 8                   ## Maximum number of meaningful digits
+                                             ## after the decimal point for Nim's
+                                             ## `float32` type.
+  MaxFloatPrecision* = MaxFloat64Precision   ## Maximum number of
+                                             ## meaningful digits
+                                             ## after the decimal point
+                                             ## for Nim's `float` type.
+  MinFloatNormal* = 2.225073858507201e-308   ## Smallest normal number for Nim's
+                                             ## `float` type (= 2^-1022).
+  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`.
-    fcNormal,    ## value is an ordinary nonzero floating point value
-    fcSubnormal, ## value is a subnormal (a very small) floating point value
-    fcZero,      ## value is zero
-    fcNegZero,   ## value is the negative zero
-    fcNan,       ## value is Not-A-Number (NAN)
-    fcInf,       ## value is positive infinity
-    fcNegInf     ## value is negative infinity
-
-proc classify*(x: float): FloatClass =
-  ## Classifies a floating point value. Returns `x`'s class as specified by
-  ## `FloatClass`.
+  FloatClass* = enum ## Describes the class a floating point value belongs to.
+                     ## This is the type that is returned by the
+                     ## `classify func <#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
+    fcNegZero,       ## value is the negative zero
+    fcNan,           ## value is Not a Number (NaN)
+    fcInf,           ## value is positive infinity
+    fcNegInf         ## value is negative infinity
+
+func isNaN*(x: SomeFloat): bool {.inline, since: (1,5,1).} =
+  ## Returns whether `x` is a `NaN`, more efficiently than via `classify(x) == fcNan`.
+  ## Works even with `--passc:-ffast-math`.
+  runnableExamples:
+    doAssert NaN.isNaN
+    doAssert not Inf.isNaN
+    doAssert not isNaN(3.1415926)
+
+  template fn: untyped = result = x != x
+  when nimvm: fn()
+  else:
+    when defined(js) or defined(nimscript): fn()
+    else: result = c_isnan(x)
+
+when defined(js):
+  import std/private/jsutils
+
+  proc toBitsImpl(x: float): array[2, uint32] =
+    let buffer = newArrayBuffer(8)
+    let a = newFloat64Array(buffer)
+    let b = newUint32Array(buffer)
+    a[0] = x
+    {.emit: "`result` = `b`;".}
+    # result = cast[array[2, uint32]](b)
+
+  proc jsSetSign(x: float, sgn: bool): float =
+    let buffer = newArrayBuffer(8)
+    let a = newFloat64Array(buffer)
+    let b = newUint32Array(buffer)
+    a[0] = x
+    {.emit: """
+    function updateBit(num, bitPos, bitVal) {
+      return (num & ~(1 << bitPos)) | (bitVal << bitPos);
+    }
+    `b`[1] = updateBit(`b`[1], 31, `sgn`);
+    `result` = `a`[0];
+    """.}
+
+proc signbit*(x: SomeFloat): bool {.inline, since: (1, 5, 1).} =
+  ## Returns true if `x` is negative, false otherwise.
+  runnableExamples:
+    doAssert not signbit(0.0)
+    doAssert signbit(-0.0)
+    doAssert signbit(-0.1)
+    doAssert not signbit(0.1)
+
+  when defined(js):
+    let uintBuffer = toBitsImpl(x)
+    result = (uintBuffer[1] shr 31) != 0
+  else:
+    result = c_signbit(x) != 0
+
+func copySign*[T: SomeFloat](x, y: T): T {.inline, since: (1, 5, 1).} =
+  ## Returns a value with the magnitude of `x` and the sign of `y`;
+  ## this works even if x or y are NaN, infinity or zero, all of which can carry a sign.
+  runnableExamples:
+    doAssert copySign(10.0, 1.0) == 10.0
+    doAssert copySign(10.0, -1.0) == -10.0
+    doAssert copySign(-Inf, -0.0) == -Inf
+    doAssert copySign(NaN, 1.0).isNaN
+    doAssert copySign(1.0, copySign(NaN, -1.0)) == -1.0
+
+  # TODO: use signbit for examples
+  when defined(js):
+    let uintBuffer = toBitsImpl(y)
+    let sgn = (uintBuffer[1] shr 31) != 0
+    result = jsSetSign(x, sgn)
+  else:
+    when nimvm: # not exact but we have a vmops for recent enough nim
+      if y > 0.0 or (y == 0.0 and 1.0 / y > 0.0):
+        result = abs(x)
+      elif y <= 0.0:
+        result = -abs(x)
+      else: # must be NaN
+        result = abs(x)
+    else: result = c_copysign(x, y)
+
+func classify*(x: float): FloatClass =
+  ## Classifies a floating point value.
+  ##
+  ## Returns `x`'s class as specified by the `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
+    doAssert classify(5.0e-324) == fcSubnormal
 
   # JavaScript and most C compilers have no classify:
+  if isNan(x): return fcNan
   if x == 0.0:
-    if 1.0/x == Inf:
+    if 1.0 / x == Inf:
       return fcZero
     else:
       return fcNegZero
-  if x*0.5 == x:
+  if x * 0.5 == x:
     if x > 0.0: return fcInf
     else: return fcNegInf
-  if x != x: return fcNan
+  if abs(x) < MinFloatNormal:
+    return fcSubnormal
   return fcNormal
-  # XXX: fcSubnormal is not detected!
 
-proc isPowerOfTwo*(x: int): bool {.noSideEffect.} =
-  ## Returns true, if `x` is a power of two, false otherwise.
+func almostEqual*[T: SomeFloat](x, y: T; unitsInLastPlace: Natural = 4): bool {.
+    since: (1, 5), inline.} =
+  ## Checks if two float values are almost equal, using the
+  ## [machine epsilon](https://en.wikipedia.org/wiki/Machine_epsilon).
+  ##
+  ## `unitsInLastPlace` is the max number of
+  ## [units in the last place](https://en.wikipedia.org/wiki/Unit_in_the_last_place)
+  ## difference tolerated when comparing two numbers. The larger the value, the
+  ## more error is allowed. A `0` value means that two numbers must be exactly the
+  ## same to be considered equal.
+  ##
+  ## The machine epsilon has to be scaled to the magnitude of the values used
+  ## and multiplied by the desired precision in ULPs unless the difference is
+  ## subnormal.
+  ##
+  # taken from: https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon
+  runnableExamples:
+    doAssert almostEqual(PI, 3.14159265358979)
+    doAssert almostEqual(Inf, Inf)
+    doAssert not almostEqual(NaN, NaN)
+
+  if x == y:
+    # short circuit exact equality -- needed to catch two infinities of
+    # the same sign. And perhaps speeds things up a bit sometimes.
+    return true
+  let diff = abs(x - y)
+  result = diff <= epsilon(T) * abs(x + y) * T(unitsInLastPlace) or
+      diff < minimumPositiveValue(T)
+
+func isPowerOfTwo*(x: int): bool =
+  ## Returns `true`, if `x` is a power of two, `false` otherwise.
+  ##
   ## Zero and negative numbers are not a power of two.
+  ##
+  ## **See also:**
+  ## * `nextPowerOfTwo func <#nextPowerOfTwo,int>`_
+  runnableExamples:
+    doAssert isPowerOfTwo(16)
+    doAssert not isPowerOfTwo(5)
+    doAssert not isPowerOfTwo(0)
+    doAssert not isPowerOfTwo(-16)
+
   return (x > 0) and ((x and (x - 1)) == 0)
 
-proc nextPowerOfTwo*(x: int): int {.noSideEffect.} =
+func nextPowerOfTwo*(x: int): int =
   ## Returns `x` rounded up to the nearest power of two.
+  ##
   ## Zero and negative numbers get rounded up to 1.
+  ##
+  ## **See also:**
+  ## * `isPowerOfTwo func <#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)
@@ -115,213 +359,354 @@ proc nextPowerOfTwo*(x: int): int {.noSideEffect.} =
   result = result or (result shr 4)
   result = result or (result shr 2)
   result = result or (result shr 1)
-  result += 1 + ord(x<=0)
+  result += 1 + ord(x <= 0)
 
-proc countBits32*(n: int32): int {.noSideEffect.} =
-  ## Counts the set bits in `n`.
-  var v = n
-  v = v -% ((v shr 1'i32) and 0x55555555'i32)
-  v = (v and 0x33333333'i32) +% ((v shr 2'i32) and 0x33333333'i32)
-  result = ((v +% (v shr 4'i32) and 0xF0F0F0F'i32) *% 0x1010101'i32) shr 24'i32
 
-proc sum*[T](x: openArray[T]): T {.noSideEffect.} =
-  ## Computes the sum of the elements in `x`.
-  ## If `x` is empty, 0 is returned.
-  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.
-  result = 1.T
-  for i in items(x): result = result * 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>".}
+when not defined(js): # C
+  func sqrt*(x: float32): float32 {.importc: "sqrtf", header: "<math.h>".}
+  func sqrt*(x: float64): float64 {.importc: "sqrt", header: "<math.h>".} =
     ## Computes the square root of `x`.
-  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`
-
-  proc ln*(x: float32): float32 {.importc: "logf", header: "<math.h>".}
-  proc ln*(x: float64): float64 {.importc: "log", header: "<math.h>".}
-    ## Computes the natural log of `x`
+    ##
+    ## **See also:**
+    ## * `cbrt func <#cbrt,float64>`_ for the cube root
+    runnableExamples:
+      doAssert almostEqual(sqrt(4.0), 2.0)
+      doAssert almostEqual(sqrt(1.44), 1.2)
+  func cbrt*(x: float32): float32 {.importc: "cbrtf", header: "<math.h>".}
+  func cbrt*(x: float64): float64 {.importc: "cbrt", header: "<math.h>".} =
+    ## Computes the cube root of `x`.
+    ##
+    ## **See also:**
+    ## * `sqrt func <#sqrt,float64>`_ for the square root
+    runnableExamples:
+      doAssert almostEqual(cbrt(8.0), 2.0)
+      doAssert almostEqual(cbrt(2.197), 1.3)
+      doAssert almostEqual(cbrt(-27.0), -3.0)
+  func ln*(x: float32): float32 {.importc: "logf", header: "<math.h>".}
+  func ln*(x: float64): float64 {.importc: "log", header: "<math.h>".} =
+    ## Computes the [natural logarithm](https://en.wikipedia.org/wiki/Natural_logarithm)
+    ## of `x`.
+    ##
+    ## **See also:**
+    ## * `log func <#log,T,T>`_
+    ## * `log10 func <#log10,float64>`_
+    ## * `log2 func <#log2,float64>`_
+    ## * `exp func <#exp,float64>`_
+    runnableExamples:
+      doAssert almostEqual(ln(exp(4.0)), 4.0)
+      doAssert almostEqual(ln(1.0), 0.0)
+      doAssert almostEqual(ln(0.0), -Inf)
+      doAssert ln(-7.0).isNaN
 else: # JS
-  proc sqrt*(x: float32): float32 {.importc: "Math.sqrt", nodecl.}
-  proc sqrt*(x: float64): float64 {.importc: "Math.sqrt", nodecl.}
+  func sqrt*(x: float32): float32 {.importc: "Math.sqrt", nodecl.}
+  func sqrt*(x: float64): float64 {.importc: "Math.sqrt", nodecl.}
+
+  func cbrt*(x: float32): float32 {.importc: "Math.cbrt", nodecl.}
+  func cbrt*(x: float64): float64 {.importc: "Math.cbrt", nodecl.}
 
-  proc ln*(x: float32): float32 {.importc: "Math.log", nodecl.}
-  proc ln*(x: float64): float64 {.importc: "Math.log", nodecl.}
+  func ln*(x: float32): float32 {.importc: "Math.log", nodecl.}
+  func ln*(x: float64): float64 {.importc: "Math.log", nodecl.}
+
+func log*[T: SomeFloat](x, base: T): T =
+  ## Computes the logarithm of `x` to base `base`.
+  ##
+  ## **See also:**
+  ## * `ln func <#ln,float64>`_
+  ## * `log10 func <#log10,float64>`_
+  ## * `log2 func <#log2,float64>`_
+  runnableExamples:
+    doAssert almostEqual(log(9.0, 3.0), 2.0)
+    doAssert almostEqual(log(0.0, 2.0), -Inf)
+    doAssert log(-7.0, 4.0).isNaN
+    doAssert log(8.0, -2.0).isNaN
 
-proc log*[T: SomeFloat](x, base: T): T =
-  ## Computes the logarithm ``base`` of ``x``
   ln(x) / ln(base)
 
-when not defined(JS): # C
-  proc log10*(x: float32): float32 {.importc: "log10f", header: "<math.h>".}
-  proc log10*(x: float64): float64 {.importc: "log10", header: "<math.h>".}
-    ## Computes the common logarithm (base 10) of `x`
-  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`
-  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))
-
-  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`
-  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`
-  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`
-
-  proc sinh*(x: float32): float32 {.importc: "sinhf", header: "<math.h>".}
-  proc sinh*(x: float64): float64 {.importc: "sinh", header: "<math.h>".}
-    ## Computes the hyperbolic sine of `x`
-  proc cosh*(x: float32): float32 {.importc: "coshf", header: "<math.h>".}
-  proc cosh*(x: float64): float64 {.importc: "cosh", header: "<math.h>".}
-    ## Computes the hyperbolic cosine of `x`
-  proc tanh*(x: float32): float32 {.importc: "tanhf", header: "<math.h>".}
-  proc tanh*(x: float64): float64 {.importc: "tanh", header: "<math.h>".}
-    ## Computes the hyperbolic tangent of `x`
-
-  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`
-  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`
-  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 `y` / `x`
-  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`.
-    ## `atan2` 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).
-
-  proc arcsinh*(x: float32): float32 {.importc: "asinhf", header: "<math.h>".}
-  proc arcsinh*(x: float64): float64 {.importc: "asinh", header: "<math.h>".}
-    ## Computes the inverse hyperbolic sine of `x`
-  proc arccosh*(x: float32): float32 {.importc: "acoshf", header: "<math.h>".}
-  proc arccosh*(x: float64): float64 {.importc: "acosh", header: "<math.h>".}
-    ## Computes the inverse hyperbolic cosine of `x`
-  proc arctanh*(x: float32): float32 {.importc: "atanhf", header: "<math.h>".}
-  proc arctanh*(x: float64): float64 {.importc: "atanh", header: "<math.h>".}
-    ## Computes the inverse hyperbolic tangent of `x`
+when not defined(js): # C
+  func log10*(x: float32): float32 {.importc: "log10f", header: "<math.h>".}
+  func log10*(x: float64): float64 {.importc: "log10", header: "<math.h>".} =
+    ## Computes the common logarithm (base 10) of `x`.
+    ##
+    ## **See also:**
+    ## * `ln func <#ln,float64>`_
+    ## * `log func <#log,T,T>`_
+    ## * `log2 func <#log2,float64>`_
+    runnableExamples:
+      doAssert almostEqual(log10(100.0) , 2.0)
+      doAssert almostEqual(log10(0.0), -Inf)
+      doAssert log10(-100.0).isNaN
+  func exp*(x: float32): float32 {.importc: "expf", header: "<math.h>".}
+  func exp*(x: float64): float64 {.importc: "exp", header: "<math.h>".} =
+    ## Computes the exponential function of `x` (`e^x`).
+    ##
+    ## **See also:**
+    ## * `ln func <#ln,float64>`_
+    runnableExamples:
+      doAssert almostEqual(exp(1.0), E)
+      doAssert almostEqual(ln(exp(4.0)), 4.0)
+      doAssert almostEqual(exp(0.0), 1.0)
+  func sin*(x: float32): float32 {.importc: "sinf", header: "<math.h>".}
+  func sin*(x: float64): float64 {.importc: "sin", header: "<math.h>".} =
+    ## Computes the sine of `x`.
+    ##
+    ## **See also:**
+    ## * `arcsin func <#arcsin,float64>`_
+    runnableExamples:
+      doAssert almostEqual(sin(PI / 6), 0.5)
+      doAssert almostEqual(sin(degToRad(90.0)), 1.0)
+  func cos*(x: float32): float32 {.importc: "cosf", header: "<math.h>".}
+  func cos*(x: float64): float64 {.importc: "cos", header: "<math.h>".} =
+    ## Computes the cosine of `x`.
+    ##
+    ## **See also:**
+    ## * `arccos func <#arccos,float64>`_
+    runnableExamples:
+      doAssert almostEqual(cos(2 * PI), 1.0)
+      doAssert almostEqual(cos(degToRad(60.0)), 0.5)
+  func tan*(x: float32): float32 {.importc: "tanf", header: "<math.h>".}
+  func tan*(x: float64): float64 {.importc: "tan", header: "<math.h>".} =
+    ## Computes the tangent of `x`.
+    ##
+    ## **See also:**
+    ## * `arctan func <#arctan,float64>`_
+    runnableExamples:
+      doAssert almostEqual(tan(degToRad(45.0)), 1.0)
+      doAssert almostEqual(tan(PI / 4), 1.0)
+  func sinh*(x: float32): float32 {.importc: "sinhf", header: "<math.h>".}
+  func 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:**
+    ## * `arcsinh func <#arcsinh,float64>`_
+    runnableExamples:
+      doAssert almostEqual(sinh(0.0), 0.0)
+      doAssert almostEqual(sinh(1.0), 1.175201193643801)
+  func cosh*(x: float32): float32 {.importc: "coshf", header: "<math.h>".}
+  func 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:**
+    ## * `arccosh func <#arccosh,float64>`_
+    runnableExamples:
+      doAssert almostEqual(cosh(0.0), 1.0)
+      doAssert almostEqual(cosh(1.0), 1.543080634815244)
+  func tanh*(x: float32): float32 {.importc: "tanhf", header: "<math.h>".}
+  func 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:**
+    ## * `arctanh func <#arctanh,float64>`_
+    runnableExamples:
+      doAssert almostEqual(tanh(0.0), 0.0)
+      doAssert almostEqual(tanh(1.0), 0.7615941559557649)
+  func arcsin*(x: float32): float32 {.importc: "asinf", header: "<math.h>".}
+  func arcsin*(x: float64): float64 {.importc: "asin", header: "<math.h>".} =
+    ## Computes the arc sine of `x`.
+    ##
+    ## **See also:**
+    ## * `sin func <#sin,float64>`_
+    runnableExamples:
+      doAssert almostEqual(radToDeg(arcsin(0.0)), 0.0)
+      doAssert almostEqual(radToDeg(arcsin(1.0)), 90.0)
+  func arccos*(x: float32): float32 {.importc: "acosf", header: "<math.h>".}
+  func arccos*(x: float64): float64 {.importc: "acos", header: "<math.h>".} =
+    ## Computes the arc cosine of `x`.
+    ##
+    ## **See also:**
+    ## * `cos func <#cos,float64>`_
+    runnableExamples:
+      doAssert almostEqual(radToDeg(arccos(0.0)), 90.0)
+      doAssert almostEqual(radToDeg(arccos(1.0)), 0.0)
+  func arctan*(x: float32): float32 {.importc: "atanf", header: "<math.h>".}
+  func arctan*(x: float64): float64 {.importc: "atan", header: "<math.h>".} =
+    ## Calculate the arc tangent of `x`.
+    ##
+    ## **See also:**
+    ## * `arctan2 func <#arctan2,float64,float64>`_
+    ## * `tan func <#tan,float64>`_
+    runnableExamples:
+      doAssert almostEqual(arctan(1.0), 0.7853981633974483)
+      doAssert almostEqual(radToDeg(arctan(1.0)), 45.0)
+  func arctan2*(y, x: float32): float32 {.importc: "atan2f", header: "<math.h>".}
+  func arctan2*(y, x: float64): float64 {.importc: "atan2", header: "<math.h>".} =
+    ## Calculate 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).
+    ##
+    ## **See also:**
+    ## * `arctan func <#arctan,float64>`_
+    runnableExamples:
+      doAssert almostEqual(arctan2(1.0, 0.0), PI / 2.0)
+      doAssert almostEqual(radToDeg(arctan2(1.0, 0.0)), 90.0)
+  func arcsinh*(x: float32): float32 {.importc: "asinhf", header: "<math.h>".}
+  func arcsinh*(x: float64): float64 {.importc: "asinh", header: "<math.h>".}
+    ## Computes the inverse hyperbolic sine of `x`.
+    ##
+    ## **See also:**
+    ## * `sinh func <#sinh,float64>`_
+  func arccosh*(x: float32): float32 {.importc: "acoshf", header: "<math.h>".}
+  func arccosh*(x: float64): float64 {.importc: "acosh", header: "<math.h>".}
+    ## Computes the inverse hyperbolic cosine of `x`.
+    ##
+    ## **See also:**
+    ## * `cosh func <#cosh,float64>`_
+  func arctanh*(x: float32): float32 {.importc: "atanhf", header: "<math.h>".}
+  func arctanh*(x: float64): float64 {.importc: "atanh", header: "<math.h>".}
+    ## Computes the inverse hyperbolic tangent of `x`.
+    ##
+    ## **See also:**
+    ## * `tanh func <#tanh,float64>`_
 
 else: # JS
-  proc log10*(x: float32): float32 {.importc: "Math.log10", nodecl.}
-  proc log10*(x: float64): float64 {.importc: "Math.log10", nodecl.}
-  proc log2*(x: float32): float32 {.importc: "Math.log2", nodecl.}
-  proc log2*(x: float64): float64 {.importc: "Math.log2", nodecl.}
-  proc exp*(x: float32): float32 {.importc: "Math.exp", nodecl.}
-  proc exp*(x: float64): float64 {.importc: "Math.exp", nodecl.}
-
-  proc sin*[T: float32|float64](x: T): T {.importc: "Math.sin", nodecl.}
-  proc cos*[T: float32|float64](x: T): T {.importc: "Math.cos", nodecl.}
-  proc tan*[T: float32|float64](x: T): T {.importc: "Math.tan", nodecl.}
-
-  proc sinh*[T: float32|float64](x: T): T {.importc: "Math.sinh", nodecl.}
-  proc cosh*[T: float32|float64](x: T): T {.importc: "Math.cosh", nodecl.}
-  proc tanh*[T: float32|float64](x: T): T {.importc: "Math.tanh", nodecl.}
-
-  proc arcsin*[T: float32|float64](x: T): T {.importc: "Math.asin", nodecl.}
-  proc arccos*[T: float32|float64](x: T): T {.importc: "Math.acos", nodecl.}
-  proc arctan*[T: float32|float64](x: T): T {.importc: "Math.atan", nodecl.}
-  proc arctan2*[T: float32|float64](y, x: T): T {.importC: "Math.atan2", nodecl.}
-
-  proc arcsinh*[T: float32|float64](x: T): T {.importc: "Math.asinh", nodecl.}
-  proc arccosh*[T: float32|float64](x: T): T {.importc: "Math.acosh", nodecl.}
-  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`
-proc sec*[T: float32|float64](x: T): T = 1.0 / cos(x)
-  ## Computes the secant of `x`.
-proc csc*[T: float32|float64](x: T): T = 1.0 / sin(x)
-  ## Computes the cosecant of `x`
-
-proc coth*[T: float32|float64](x: T): T = 1.0 / tanh(x)
-  ## Computes the hyperbolic cotangent of `x`
-proc sech*[T: float32|float64](x: T): T = 1.0 / cosh(x)
-  ## Computes the hyperbolic secant of `x`
-proc csch*[T: float32|float64](x: T): T = 1.0 / sinh(x)
-  ## Computes the hyperbolic cosecant of `x`
-
-proc arccot*[T: float32|float64](x: T): T = arctan(1.0 / x)
-  ## Computes the inverse cotangent of `x`
-proc arcsec*[T: float32|float64](x: T): T = arccos(1.0 / x)
-  ## Computes the inverse secant of `x`
-proc arccsc*[T: float32|float64](x: T): T = arcsin(1.0 / x)
-  ## Computes the inverse cosecant of `x`
-
-proc arccoth*[T: float32|float64](x: T): T = arctanh(1.0 / x)
-  ## Computes the inverse hyperbolic cotangent of `x`
-proc arcsech*[T: float32|float64](x: T): T = arccosh(1.0 / x)
-  ## Computes the inverse hyperbolic secant of `x`
-proc arccsch*[T: float32|float64](x: T): T = arcsinh(1.0 / x)
-  ## Computes the inverse hyperbolic cosecant of `x`
-
-when not defined(JS): # C
-  proc hypot*(x, y: float32): float32 {.importc: "hypotf", header: "<math.h>".}
-  proc hypot*(x, y: float64): float64 {.importc: "hypot", header: "<math.h>".}
-    ## Computes the hypotenuse of a right-angle triangle with `x` and
-    ## `y` as its base and height. Equivalent to ``sqrt(x*x + y*y)``.
-
-  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.
+  func log10*(x: float32): float32 {.importc: "Math.log10", nodecl.}
+  func log10*(x: float64): float64 {.importc: "Math.log10", nodecl.}
+  func log2*(x: float32): float32 {.importc: "Math.log2", nodecl.}
+  func log2*(x: float64): float64 {.importc: "Math.log2", nodecl.}
+  func exp*(x: float32): float32 {.importc: "Math.exp", nodecl.}
+  func exp*(x: float64): float64 {.importc: "Math.exp", nodecl.}
+
+  func sin*[T: float32|float64](x: T): T {.importc: "Math.sin", nodecl.}
+  func cos*[T: float32|float64](x: T): T {.importc: "Math.cos", nodecl.}
+  func tan*[T: float32|float64](x: T): T {.importc: "Math.tan", nodecl.}
+
+  func sinh*[T: float32|float64](x: T): T {.importc: "Math.sinh", nodecl.}
+  func cosh*[T: float32|float64](x: T): T {.importc: "Math.cosh", nodecl.}
+  func tanh*[T: float32|float64](x: T): T {.importc: "Math.tanh", nodecl.}
+
+  func arcsin*[T: float32|float64](x: T): T {.importc: "Math.asin", nodecl.}
+    # keep this as generic or update test in `tvmops.nim` to make sure we
+    # keep testing that generic importc procs work
+  func arccos*[T: float32|float64](x: T): T {.importc: "Math.acos", nodecl.}
+  func arctan*[T: float32|float64](x: T): T {.importc: "Math.atan", nodecl.}
+  func arctan2*[T: float32|float64](y, x: T): T {.importc: "Math.atan2", nodecl.}
+
+  func arcsinh*[T: float32|float64](x: T): T {.importc: "Math.asinh", nodecl.}
+  func arccosh*[T: float32|float64](x: T): T {.importc: "Math.acosh", nodecl.}
+  func arctanh*[T: float32|float64](x: T): T {.importc: "Math.atanh", nodecl.}
+
+func cot*[T: float32|float64](x: T): T = 1.0 / tan(x)
+  ## Computes the cotangent of `x` (`1/tan(x)`).
+func sec*[T: float32|float64](x: T): T = 1.0 / cos(x)
+  ## Computes the secant of `x` (`1/cos(x)`).
+func csc*[T: float32|float64](x: T): T = 1.0 / sin(x)
+  ## Computes the cosecant of `x` (`1/sin(x)`).
+
+func coth*[T: float32|float64](x: T): T = 1.0 / tanh(x)
+  ## Computes the hyperbolic cotangent of `x` (`1/tanh(x)`).
+func sech*[T: float32|float64](x: T): T = 1.0 / cosh(x)
+  ## Computes the hyperbolic secant of `x` (`1/cosh(x)`).
+func csch*[T: float32|float64](x: T): T = 1.0 / sinh(x)
+  ## Computes the hyperbolic cosecant of `x` (`1/sinh(x)`).
+
+func arccot*[T: float32|float64](x: T): T = arctan(1.0 / x)
+  ## Computes the inverse cotangent of `x` (`arctan(1/x)`).
+func arcsec*[T: float32|float64](x: T): T = arccos(1.0 / x)
+  ## Computes the inverse secant of `x` (`arccos(1/x)`).
+func arccsc*[T: float32|float64](x: T): T = arcsin(1.0 / x)
+  ## Computes the inverse cosecant of `x` (`arcsin(1/x)`).
+
+func arccoth*[T: float32|float64](x: T): T = arctanh(1.0 / x)
+  ## Computes the inverse hyperbolic cotangent of `x` (`arctanh(1/x)`).
+func arcsech*[T: float32|float64](x: T): T = arccosh(1.0 / x)
+  ## Computes the inverse hyperbolic secant of `x` (`arccosh(1/x)`).
+func arccsch*[T: float32|float64](x: T): T = arcsinh(1.0 / x)
+  ## Computes the inverse hyperbolic cosecant of `x` (`arcsinh(1/x)`).
+
+const windowsCC89 = defined(windows) and defined(bcc)
+
+when not defined(js): # C
+  func hypot*(x, y: float32): float32 {.importc: "hypotf", header: "<math.h>".}
+  func hypot*(x, y: float64): float64 {.importc: "hypot", header: "<math.h>".} =
+    ## Computes the length of the hypotenuse of a right-angle triangle with
+    ## `x` as its base and `y` as its height. Equivalent to `sqrt(x*x + y*y)`.
+    runnableExamples:
+      doAssert almostEqual(hypot(3.0, 4.0), 5.0)
+  func pow*(x, y: float32): float32 {.importc: "powf", header: "<math.h>".}
+  func pow*(x, y: float64): float64 {.importc: "pow", header: "<math.h>".} =
+    ## Computes `x` raised to the power of `y`.
     ##
-    ## To compute power between integers, use `^` e.g. 2 ^ 6
-
-  proc erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".}
-  proc erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".}
-    ## The error function
-  proc erfc*(x: float32): float32 {.importc: "erfcf", header: "<math.h>".}
-  proc erfc*(x: float64): float64 {.importc: "erfc", header: "<math.h>".}
-    ## The complementary error function
-
-  proc gamma*(x: float32): float32 {.importc: "tgammaf", header: "<math.h>".}
-  proc gamma*(x: float64): float64 {.importc: "tgamma", header: "<math.h>".}
-    ## The gamma function
-  proc tgamma*(x: float32): float32
-    {.deprecated: "use gamma instead", importc: "tgammaf", header: "<math.h>".}
-  proc tgamma*(x: float64): float64
-    {.deprecated: "use gamma instead", importc: "tgamma", header: "<math.h>".}
-    ## The gamma function
-    ## **Deprecated since version 0.19.0**: Use ``gamma`` instead.
-  proc lgamma*(x: float32): float32 {.importc: "lgammaf", header: "<math.h>".}
-  proc lgamma*(x: float64): float64 {.importc: "lgamma", header: "<math.h>".}
-    ## Natural log of the gamma function
-
-  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`)
+    ## To compute the power between integers (e.g. 2^6),
+    ## use the `^ func <#^,T,Natural>`_.
     ##
-    ## .. code-block:: nim
-    ##  echo floor(-3.5) ## -4.0
+    ## **See also:**
+    ## * `^ func <#^,T,Natural>`_
+    ## * `sqrt func <#sqrt,float64>`_
+    ## * `cbrt func <#cbrt,float64>`_
+    runnableExamples:
+      doAssert almostEqual(pow(100, 1.5), 1000.0)
+      doAssert almostEqual(pow(16.0, 0.5), 4.0)
+
+  # TODO: add C89 version on windows
+  when not windowsCC89:
+    func erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".}
+    func erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".}
+      ## Computes the [error function](https://en.wikipedia.org/wiki/Error_function) for `x`.
+      ##
+      ## **Note:** Not available for the JS backend.
+    func erfc*(x: float32): float32 {.importc: "erfcf", header: "<math.h>".}
+    func erfc*(x: float64): float64 {.importc: "erfc", header: "<math.h>".}
+      ## Computes the [complementary error function](https://en.wikipedia.org/wiki/Error_function#Complementary_error_function) for `x`.
+      ##
+      ## **Note:** Not available for the JS backend.
+    func gamma*(x: float32): float32 {.importc: "tgammaf", header: "<math.h>".}
+    func gamma*(x: float64): float64 {.importc: "tgamma", header: "<math.h>".} =
+      ## Computes the [gamma function](https://en.wikipedia.org/wiki/Gamma_function) for `x`.
+      ##
+      ## **Note:** Not available for the JS backend.
+      ##
+      ## **See also:**
+      ## * `lgamma func <#lgamma,float64>`_ for the natural logarithm of the gamma function
+      runnableExamples:
+        doAssert almostEqual(gamma(1.0), 1.0)
+        doAssert almostEqual(gamma(4.0), 6.0)
+        doAssert almostEqual(gamma(11.0), 3628800.0)
+    func lgamma*(x: float32): float32 {.importc: "lgammaf", header: "<math.h>".}
+    func lgamma*(x: float64): float64 {.importc: "lgamma", header: "<math.h>".} =
+      ## Computes the natural logarithm of the gamma function for `x`.
+      ##
+      ## **Note:** Not available for the JS backend.
+      ##
+      ## **See also:**
+      ## * `gamma func <#gamma,float64>`_ for gamma function
 
-  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`)
+  func floor*(x: float32): float32 {.importc: "floorf", header: "<math.h>".}
+  func floor*(x: float64): float64 {.importc: "floor", header: "<math.h>".} =
+    ## Computes the floor function (i.e. the largest integer not greater than `x`).
     ##
-    ## .. code-block:: nim
-    ##  echo ceil(-2.1) ## -2.0
-
-  when defined(windows) and (defined(vcc) or defined(bcc)):
+    ## **See also:**
+    ## * `ceil func <#ceil,float64>`_
+    ## * `round func <#round,float64>`_
+    ## * `trunc func <#trunc,float64>`_
+    runnableExamples:
+      doAssert floor(2.1)  == 2.0
+      doAssert floor(2.9)  == 2.0
+      doAssert floor(-3.5) == -4.0
+
+  func ceil*(x: float32): float32 {.importc: "ceilf", header: "<math.h>".}
+  func ceil*(x: float64): float64 {.importc: "ceil", header: "<math.h>".} =
+    ## Computes the ceiling function (i.e. the smallest integer not smaller
+    ## than `x`).
+    ##
+    ## **See also:**
+    ## * `floor func <#floor,float64>`_
+    ## * `round func <#round,float64>`_
+    ## * `trunc func <#trunc,float64>`_
+    runnableExamples:
+      doAssert ceil(2.1)  == 3.0
+      doAssert ceil(2.9)  == 3.0
+      doAssert ceil(-2.1) == -2.0
+
+  when windowsCC89:
     # MSVC 2010 don't have trunc/truncf
     # this implementation was inspired by Go-lang Math.Trunc
-    proc truncImpl(f: float64): float64 =
+    func truncImpl(f: float64): float64 =
       const
-        mask : uint64 = 0x7FF
+        mask: uint64 = 0x7FF
         shift: uint64 = 64 - 12
-        bias : uint64 = 0x3FF
+        bias: uint64 = 0x3FF
 
       if f < 1:
         if f < 0: return -truncImpl(-f)
@@ -332,16 +717,16 @@ when not defined(JS): # C
       let e = (x shr shift) and mask - bias
 
       # Keep the top 12+e bits, the integer part; clear the rest.
-      if e < 64-12:
-        x = x and (not (1'u64 shl (64'u64-12'u64-e) - 1'u64))
+      if e < 64 - 12:
+        x = x and (not (1'u64 shl (64'u64 - 12'u64 - e) - 1'u64))
 
       result = cast[float64](x)
 
-    proc truncImpl(f: float32): float32 =
+    func truncImpl(f: float32): float32 =
       const
-        mask : uint32 = 0xFF
+        mask: uint32 = 0xFF
         shift: uint32 = 32 - 9
-        bias : uint32 = 0x7F
+        bias: uint32 = 0x7F
 
       if f < 1:
         if f < 0: return -truncImpl(-f)
@@ -352,131 +737,338 @@ when not defined(JS): # C
       let e = (x shr shift) and mask - bias
 
       # Keep the top 9+e bits, the integer part; clear the rest.
-      if e < 32-9:
-        x = x and (not (1'u32 shl (32'u32-9'u32-e) - 1'u32))
+      if e < 32 - 9:
+        x = x and (not (1'u32 shl (32'u32 - 9'u32 - e) - 1'u32))
 
       result = cast[float32](x)
 
-    proc trunc*(x: float64): float64 =
+    func trunc*(x: float64): float64 =
       if classify(x) in {fcZero, fcNegZero, fcNan, fcInf, fcNegInf}: return x
       result = truncImpl(x)
 
-    proc trunc*(x: float32): float32 =
+    func trunc*(x: float32): float32 =
       if classify(x) in {fcZero, fcNegZero, fcNan, fcInf, fcNegInf}: return x
       result = truncImpl(x)
 
-    proc round0[T: float32|float64](x: T): T =
+    func round*[T: float32|float64](x: T): T =
       ## Windows compilers prior to MSVC 2012 do not implement 'round',
       ## 'roundl' or 'roundf'.
       result = if x < 0.0: ceil(x - T(0.5)) else: floor(x + T(0.5))
   else:
-    proc round0(x: float32): float32 {.importc: "roundf", header: "<math.h>".}
-    proc round0(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.
-
-    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
+    func round*(x: float32): float32 {.importc: "roundf", header: "<math.h>".}
+    func round*(x: float64): float64 {.importc: "round", header: "<math.h>".} =
+      ## Rounds a float to zero decimal places.
       ##
-      ## .. code-block:: nim
-      ##  echo trunc(PI) # 3.0
-
-  proc fmod*(x, y: float32): float32 {.deprecated, importc: "fmodf", header: "<math.h>".}
-  proc fmod*(x, y: float64): float64 {.deprecated, importc: "fmod", header: "<math.h>".}
-    ## Computes the remainder of `x` divided by `y`
+      ## Used internally by the `round func <#round,T,int>`_
+      ## when the specified number of places is 0.
+      ##
+      ## **See also:**
+      ## * `round func <#round,T,int>`_ for rounding to the specific
+      ##   number of decimal places
+      ## * `floor func <#floor,float64>`_
+      ## * `ceil func <#ceil,float64>`_
+      ## * `trunc func <#trunc,float64>`_
+      runnableExamples:
+        doAssert round(3.4) == 3.0
+        doAssert round(3.5) == 4.0
+        doAssert round(4.5) == 5.0
+
+    func trunc*(x: float32): float32 {.importc: "truncf", header: "<math.h>".}
+    func trunc*(x: float64): float64 {.importc: "trunc", header: "<math.h>".} =
+      ## Truncates `x` to the decimal point.
+      ##
+      ## **See also:**
+      ## * `floor func <#floor,float64>`_
+      ## * `ceil func <#ceil,float64>`_
+      ## * `round func <#round,float64>`_
+      runnableExamples:
+        doAssert trunc(PI) == 3.0
+        doAssert trunc(-1.85) == -1.0
+
+  func `mod`*(x, y: float32): float32 {.importc: "fmodf", header: "<math.h>".}
+  func `mod`*(x, y: float64): float64 {.importc: "fmod", header: "<math.h>".} =
+    ## Computes the modulo operation for float values (the remainder of `x` divided by `y`).
     ##
-    ## .. code-block:: nim
-    ##  echo fmod(-2.5, 0.3) ## -0.1
+    ## **See also:**
+    ## * `floorMod func <#floorMod,T,T>`_ for Python-like (`%` operator) behavior
+    runnableExamples:
+      doAssert  6.5 mod  2.5 ==  1.5
+      doAssert -6.5 mod  2.5 == -1.5
+      doAssert  6.5 mod -2.5 ==  1.5
+      doAssert -6.5 mod -2.5 == -1.5
 
-  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 operators.
 else: # JS
-  proc hypot*[T: float32|float64](x, y: T): T = return sqrt(x*x + y*y)
-  proc pow*(x, y: float32): float32 {.importC: "Math.pow", nodecl.}
-  proc pow*(x, y: float64): float64 {.importc: "Math.pow", nodecl.}
-  proc floor*(x: float32): float32 {.importc: "Math.floor", nodecl.}
-  proc floor*(x: float64): float64 {.importc: "Math.floor", nodecl.}
-  proc ceil*(x: float32): float32 {.importc: "Math.ceil", nodecl.}
-  proc ceil*(x: float64): float64 {.importc: "Math.ceil", nodecl.}
-  proc round0(x: float): float {.importc: "Math.round", nodecl.}
-  proc trunc*(x: float32): float32 {.importc: "Math.trunc", nodecl.}
-  proc trunc*(x: float64): float64 {.importc: "Math.trunc", nodecl.}
-
-  proc `mod`*(x, y: float32): float32 {.importcpp: "# % #".}
-  proc `mod`*(x, y: float64): float64 {.importcpp: "# % #".}
-  ## Computes the modulo operation for float operators.
-
-proc round*[T: float32|float64](x: T, places: int = 0): T =
-  ## Round a floating point number.
-  ##
-  ## 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 greater than 0, round to the given number of decimal
-  ## places, e.g. `round(54.346, 2) -> 54.35`.
-  ## If `places` is negative, round to the left of the decimal place, e.g.
-  ## `round(537.345, -1) -> 540.0`
+  func hypot*(x, y: float32): float32 {.importc: "Math.hypot", varargs, nodecl.}
+  func hypot*(x, y: float64): float64 {.importc: "Math.hypot", varargs, nodecl.}
+  func pow*(x, y: float32): float32 {.importc: "Math.pow", nodecl.}
+  func pow*(x, y: float64): float64 {.importc: "Math.pow", nodecl.}
+  func floor*(x: float32): float32 {.importc: "Math.floor", nodecl.}
+  func floor*(x: float64): float64 {.importc: "Math.floor", nodecl.}
+  func ceil*(x: float32): float32 {.importc: "Math.ceil", nodecl.}
+  func ceil*(x: float64): float64 {.importc: "Math.ceil", nodecl.}
+
+  when (NimMajor, NimMinor) < (1, 5) or defined(nimLegacyJsRound):
+    func round*(x: float): float {.importc: "Math.round", nodecl.}
+  else:
+    func jsRound(x: float): float {.importc: "Math.round", nodecl.}
+    func round*[T: float64 | float32](x: T): T =
+      if x >= 0: result = jsRound(x)
+      else:
+        result = ceil(x)
+        if result - x >= T(0.5):
+          result -= T(1.0)
+  func trunc*(x: float32): float32 {.importc: "Math.trunc", nodecl.}
+  func trunc*(x: float64): float64 {.importc: "Math.trunc", nodecl.}
+
+  func `mod`*(x, y: float32): float32 {.importjs: "(# % #)".}
+  func `mod`*(x, y: float64): float64 {.importjs: "(# % #)".} =
+    ## Computes the modulo operation for float values (the remainder of `x` divided by `y`).
+    runnableExamples:
+      doAssert  6.5 mod  2.5 ==  1.5
+      doAssert -6.5 mod  2.5 == -1.5
+      doAssert  6.5 mod -2.5 ==  1.5
+      doAssert -6.5 mod -2.5 == -1.5
+  
+  func divmod*[T:SomeInteger](num, denom: T): (T, T) = 
+    runnableExamples:
+      doAssert  divmod(5, 2) ==  (2, 1)
+      doAssert divmod(5, -3) == (-1, 2)
+    result[0] = num div denom
+    result[1] = num mod denom
+  
+
+func round*[T: float32|float64](x: T, places: int): T =
+  ## 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),
+  ## round to the nearest integral value following normal mathematical
+  ## 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) -> 540.0`.
+  runnableExamples:
+    doAssert round(PI, 2) == 3.14
+    doAssert round(PI, 4) == 3.1416
+
   if places == 0:
-    result = round0(x)
+    result = round(x)
   else:
-    var mult = pow(10.0, places.T)
-    result = round0(x*mult)/mult
-
-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.
+    var mult = pow(10.0, T(places))
+    result = round(x * mult) / mult
+
+func floorDiv*[T: SomeInteger](x, y: T): T =
+  ## Floor division is conceptually defined as `floor(x / y)`.
+  ##
+  ## 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 func <#floorMod,T,T>`_ for Python-like (`%` operator) behavior
+  runnableExamples:
+    doAssert floorDiv( 13,  3) ==  4
+    doAssert floorDiv(-13,  3) == -5
+    doAssert floorDiv( 13, -3) == -5
+    doAssert floorDiv(-13, -3) ==  4
+
   result = x div y
   let r = x mod y
   if (r > 0 and y < 0) or (r < 0 and y > 0): result.dec 1
 
-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.
+func floorMod*[T: SomeNumber](x, y: T): T =
+  ## Floor modulo is conceptually defined as `x - (floorDiv(x, y) * y)`.
+  ##
+  ## This func behaves the same as the `%` operator in Python.
+  ##
+  ## **See also:**
+  ## * `mod func <#mod,float64,float64>`_
+  ## * `floorDiv func <#floorDiv,T,T>`_
+  runnableExamples:
+    doAssert floorMod( 13,  3) ==  1
+    doAssert floorMod(-13,  3) ==  2
+    doAssert floorMod( 13, -3) == -2
+    doAssert floorMod(-13, -3) == -1
+
   result = x mod y
   if (result > 0 and y < 0) or (result < 0 and y > 0): result += y
 
-when not defined(JS):
-  proc c_frexp*(x: float32, exponent: var int32): float32 {.
-    importc: "frexp", header: "<math.h>".}
-  proc c_frexp*(x: float64, exponent: var int32): float64 {.
-    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
-    ## m.
-    var exp: int32
-    result = c_frexp(x, exp)
-    exponent = exp
-else:
-  proc frexp*[T: float32|float64](x: T, exponent: var int): T =
+func euclDiv*[T: SomeInteger](x, y: T): T {.since: (1, 5, 1).} =
+  ## Returns euclidean division of `x` by `y`.
+  runnableExamples:
+    doAssert euclDiv(13, 3) == 4
+    doAssert euclDiv(-13, 3) == -5
+    doAssert euclDiv(13, -3) == -4
+    doAssert euclDiv(-13, -3) == 5
+
+  result = x div y
+  if x mod y < 0:
+    if y > 0:
+      dec result
+    else:
+      inc result
+
+func euclMod*[T: SomeNumber](x, y: T): T {.since: (1, 5, 1).} =
+  ## Returns euclidean modulo of `x` by `y`.
+  ## `euclMod(x, y)` is non-negative.
+  runnableExamples:
+    doAssert euclMod(13, 3) == 1
+    doAssert euclMod(-13, 3) == 2
+    doAssert euclMod(13, -3) == 1
+    doAssert euclMod(-13, -3) == 2
+
+  result = x mod y
+  if result < 0:
+    result += abs(y)
+
+func ceilDiv*[T: SomeInteger](x, y: T): T {.inline, since: (1, 5, 1).} =
+  ## Ceil division is conceptually defined as `ceil(x / y)`.
+  ##
+  ## Assumes `x >= 0` and `y > 0` (and `x + y - 1 <= high(T)` if T is SomeUnsignedInt).
+  ##
+  ## This is different from the `system.div <system.html#div,int,int>`_
+  ## operator, which works like `trunc(x / y)`.
+  ## That is, `div` rounds towards `0` and `ceilDiv` rounds up.
+  ##
+  ## This function has the above input limitation, because that allows the
+  ## compiler to generate faster code and it is rarely used with
+  ## negative values or unsigned integers close to `high(T)/2`.
+  ## If you need a `ceilDiv` that works with any input, see:
+  ## https://github.com/demotomohiro/divmath.
+  ##
+  ## **See also:**
+  ## * `system.div proc <system.html#div,int,int>`_ for integer division
+  ## * `floorDiv func <#floorDiv,T,T>`_ for integer division which rounds down.
+  runnableExamples:
+    assert ceilDiv(12, 3) ==  4
+    assert ceilDiv(13, 3) ==  5
+
+  when sizeof(T) == 8:
+    type UT = uint64
+  elif sizeof(T) == 4:
+    type UT = uint32
+  elif sizeof(T) == 2:
+    type UT = uint16
+  elif sizeof(T) == 1:
+    type UT = uint8
+  else:
+    {.fatal: "Unsupported int type".}
+
+  assert x >= 0 and y > 0
+  when T is SomeUnsignedInt:
+    assert x + y - 1 >= x
+
+  # If the divisor is const, the backend C/C++ compiler generates code without a `div`
+  # instruction, as it is slow on most CPUs.
+  # If the divisor is a power of 2 and a const unsigned integer type, the
+  # compiler generates faster code.
+  # If the divisor is const and a signed integer, generated code becomes slower
+  # than the code with unsigned integers, because division with signed integers
+  # need to works for both positive and negative value without `idiv`/`sdiv`.
+  # That is why this code convert parameters to unsigned.
+  # This post contains a comparison of the performance of signed/unsigned integers:
+  # https://github.com/nim-lang/Nim/pull/18596#issuecomment-894420984.
+  # If signed integer arguments were not converted to unsigned integers,
+  # `ceilDiv` wouldn't work for any positive signed integer value, because
+  # `x + (y - 1)` can overflow.
+  ((x.UT + (y.UT - 1.UT)) div y.UT).T
+
+func frexp*[T: float32|float64](x: T): tuple[frac: T, exp: int] {.inline.} =
+  ## Splits `x` into a normalized fraction `frac` and an integral power of 2 `exp`,
+  ## such that `abs(frac) in 0.5..<1` and `x == frac * 2 ^ exp`, except for special
+  ## cases shown below.
+  runnableExamples:
+    doAssert frexp(8.0) == (0.5, 4)
+    doAssert frexp(-8.0) == (-0.5, 4)
+    doAssert frexp(0.0) == (0.0, 0)
+
+    # special cases:
+    when sizeof(int) == 8:
+      doAssert frexp(-0.0).frac.signbit # signbit preserved for +-0
+      doAssert frexp(Inf).frac == Inf # +- Inf preserved
+      doAssert frexp(NaN).frac.isNaN
+
+  when not defined(js):
+    var exp: cint
+    result.frac = c_frexp2(x, exp)
+    result.exp = exp
+  else:
     if x == 0.0:
-      exponent = 0
-      result = 0.0
+      # reuse signbit implementation
+      let uintBuffer = toBitsImpl(x)
+      if (uintBuffer[1] shr 31) != 0:
+        # x is -0.0
+        result = (-0.0, 0)
+      else:
+        result = (0.0, 0)
     elif x < 0.0:
-      result = -frexp(-x, exponent)
+      result = frexp(-x)
+      result.frac = -result.frac
     else:
       var ex = trunc(log2(x))
-      exponent = int(ex)
-      result = x / pow(2.0, ex)
-      if abs(result) >= 1:
-        inc(exponent)
-        result = result / 2
-      if exponent == 1024 and result == 0.0:
-        result = 0.99999999999999988898
+      result.exp = int(ex)
+      result.frac = x / pow(2.0, ex)
+      if abs(result.frac) >= 1:
+        inc(result.exp)
+        result.frac = result.frac / 2
+      if result.exp == 1024 and result.frac == 0.0:
+        result.frac = 0.99999999999999988898
+
+func frexp*[T: float32|float64](x: T, exponent: var int): T {.inline.} =
+  ## Overload of `frexp` that calls `(result, exponent) = frexp(x)`.
+  runnableExamples:
+    var x: int
+    doAssert frexp(5.0, x) == 0.625
+    doAssert x == 3
+
+  (result, exponent) = frexp(x)
+
+
+when not defined(js):
+  when windowsCC89:
+    # taken from Go-lang Math.Log2
+    const ln2 = 0.693147180559945309417232121458176568075500134360255254120680009
+    template log2Impl[T](x: T): T =
+      var exp: int
+      var frac = frexp(x, exp)
+      # Make sure exact powers of two give an exact answer.
+      # Don't depend on Log(0.5)*(1/Ln2)+exp being exactly exp-1.
+      if frac == 0.5: return T(exp - 1)
+      log10(frac) * (1 / ln2) + T(exp)
+
+    func log2*(x: float32): float32 = log2Impl(x)
+    func log2*(x: float64): float64 = log2Impl(x)
+      ## Log2 returns the binary logarithm of x.
+      ## The special cases are the same as for Log.
 
-proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] =
-  ## Breaks `x` into an integral and a fractional part.
+  else:
+    func log2*(x: float32): float32 {.importc: "log2f", header: "<math.h>".}
+    func log2*(x: float64): float64 {.importc: "log2", header: "<math.h>".} =
+      ## Computes the binary logarithm (base 2) of `x`.
+      ##
+      ## **See also:**
+      ## * `log func <#log,T,T>`_
+      ## * `log10 func <#log10,float64>`_
+      ## * `ln func <#ln,float64>`_
+      runnableExamples:
+        doAssert almostEqual(log2(8.0), 3.0)
+        doAssert almostEqual(log2(1.0), 0.0)
+        doAssert almostEqual(log2(0.0), -Inf)
+        doAssert log2(-2.0).isNaN
+
+func splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] =
+  ## Breaks `x` into an integer and a fractional part.
   ##
-  ## Returns a tuple containing intpart and floatpart representing
-  ## the integer part and the fractional part respectively.
+  ## Returns a tuple containing `intpart` and `floatpart`, representing
+  ## the integer part and the fractional part, respectively.
   ##
   ## Both parts have the same sign as `x`.  Analogous to the `modf`
   ## function in C.
+  runnableExamples:
+    doAssert splitDecimal(5.25) == (intpart: 5.0, floatpart: 0.25)
+    doAssert splitDecimal(-2.73) == (intpart: -2.0, floatpart: -0.73)
+
   var
     absolute: T
   absolute = abs(x)
@@ -486,209 +1078,237 @@ proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] =
     result.intpart = -result.intpart
     result.floatpart = -result.floatpart
 
-{.pop.}
 
-proc degToRad*[T: float32|float64](d: T): T {.inline.} =
-  ## Convert from degrees to radians
-  result = T(d) * RadPerDeg
+func degToRad*[T: float32|float64](d: T): T {.inline.} =
+  ## Converts from degrees to radians.
+  ##
+  ## **See also:**
+  ## * `radToDeg func <#radToDeg,T>`_
+  runnableExamples:
+    doAssert almostEqual(degToRad(180.0), PI)
+
+  result = d * T(RadPerDeg)
+
+func radToDeg*[T: float32|float64](d: T): T {.inline.} =
+  ## Converts from radians to degrees.
+  ##
+  ## **See also:**
+  ## * `degToRad func <#degToRad,T>`_
+  runnableExamples:
+    doAssert almostEqual(radToDeg(2 * PI), 360.0)
 
-proc radToDeg*[T: float32|float64](d: T): T {.inline.} =
-  ## Convert from radians to degrees
-  result = T(d) / RadPerDeg
+  result = d / T(RadPerDeg)
+
+func sgn*[T: SomeNumber](x: T): int {.inline.} =
+  ## Sign function.
+  ##
+  ## Returns:
+  ## * `-1` for negative numbers and `NegInf`,
+  ## * `1` for positive numbers and `Inf`,
+  ## * `0` for positive zero, negative zero and `NaN`
+  runnableExamples:
+    doAssert sgn(5) == 1
+    doAssert sgn(0) == 0
+    doAssert sgn(-4.1) == -1
 
-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`.
   ord(T(0) < x) - ord(x < T(0))
 
 {.pop.}
 {.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.
-  when compiles(y >= T(0)):
-    assert y >= T(0)
-  else:
-    assert T(y) >= T(0)
-  var (x, y) = (x, y)
-  result = 1
+func sum*[T](x: openArray[T]): T =
+  ## Computes the sum of the elements in `x`.
+  ##
+  ## If `x` is empty, 0 is returned.
+  ##
+  ## **See also:**
+  ## * `prod func <#prod,openArray[T]>`_
+  ## * `cumsum func <#cumsum,openArray[T]>`_
+  ## * `cumsummed func <#cumsummed,openArray[T]>`_
+  runnableExamples:
+    doAssert sum([1, 2, 3, 4]) == 10
+    doAssert sum([-4, 3, 5]) == 4
 
-  while true:
-    if (y and 1) != 0:
-      result *= x
-    y = y shr 1
-    if y == 0:
-      break
-    x *= x
+  for i in items(x): result = result + i
+
+func prod*[T](x: openArray[T]): T =
+  ## Computes the product of the elements in `x`.
+  ##
+  ## If `x` is empty, 1 is returned.
+  ##
+  ## **See also:**
+  ## * `sum func <#sum,openArray[T]>`_
+  ## * `fac func <#fac,int>`_
+  runnableExamples:
+    doAssert prod([1, 2, 3, 4]) == 24
+    doAssert prod([-4, 3, 5]) == -60
 
-proc gcd*[T](x, y: T): T =
-  ## Computes the greatest common (positive) divisor of ``x`` and ``y``.
+  result = T(1)
+  for i in items(x): result = result * i
+
+func cumsummed*[T](x: openArray[T]): seq[T] =
+  ## Returns the cumulative (aka prefix) summation of `x`.
+  ##
+  ## If `x` is empty, `@[]` is returned.
+  ##
+  ## **See also:**
+  ## * `sum func <#sum,openArray[T]>`_
+  ## * `cumsum func <#cumsum,openArray[T]>`_ for the in-place version
+  runnableExamples:
+    doAssert cumsummed([1, 2, 3, 4]) == @[1, 3, 6, 10]
+
+  let xLen = x.len
+  if xLen == 0:
+    return @[]
+  result.setLen(xLen)
+  result[0] = x[0]
+  for i in 1 ..< xLen: result[i] = result[i - 1] + x[i]
+
+func cumsum*[T](x: var openArray[T]) =
+  ## Transforms `x` in-place (must be declared as `var`) into its
+  ## cumulative (aka prefix) summation.
+  ##
+  ## **See also:**
+  ## * `sum func <#sum,openArray[T]>`_
+  ## * `cumsummed func <#cumsummed,openArray[T]>`_ for a version which
+  ##   returns a 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]
+
+func `^`*[T: SomeNumber](x: T, y: Natural): T =
+  ## Computes `x` to the power of `y`.
+  ##
+  ## The exponent `y` must be non-negative, use
+  ## `pow <#pow,float64,float64>`_ for negative exponents.
+  ##
+  ## **See also:**
+  ## * `pow func <#pow,float64,float64>`_ for negative exponent or
+  ##   floats
+  ## * `sqrt func <#sqrt,float64>`_
+  ## * `cbrt func <#cbrt,float64>`_
+  runnableExamples:
+    doAssert -3 ^ 0 == 1
+    doAssert -3 ^ 1 == -3
+    doAssert -3 ^ 2 == 9
+
+  case y
+  of 0: result = 1
+  of 1: result = x
+  of 2: result = x * x
+  of 3: result = x * x * x
+  else:
+    var (x, y) = (x, y)
+    result = 1
+    while true:
+      if (y and 1) != 0:
+        result *= x
+      y = y shr 1
+      if y == 0:
+        break
+      x *= x
+
+func 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."
+  ## "greatest decimal `z` such that `z*N == x and z*M == y`
+  ## where N and M are positive integers".
+  ##
+  ## **See also:**
+  ## * `gcd func <#gcd,SomeInteger,SomeInteger>`_ for an integer version
+  ## * `lcm func <#lcm,T,T>`_
+  runnableExamples:
+    doAssert gcd(13.5, 9.0) == 4.5
+
   var (x, y) = (x, y)
   while y != 0:
     x = x mod y
     swap x, y
   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.
-  when x is SomeSignedInt:
-    var x = abs(x)
-  else:
-    var x = x
-  when y is SomeSignedInt:
-    var y = abs(y)
-  else:
-    var y = y
-
-  if x == 0:
-    return y
-  if y == 0:
-    return x
-
-  let shift = countTrailingZeroBits(x or y)
-  y = y shr countTrailingZeroBits(y)
-  while x != 0:
-    x = x shr countTrailingZeroBits(x)
-    if y > x:
-      swap y, x
-    x -= y
-  y shl shift
-
-proc lcm*[T](x, y: T): T =
-  ## Computes the least common multiple of ``x`` and ``y``.
-  x div gcd(x, y) * y
-
-when isMainModule and not defined(JS):
-  # Check for no side effect annotation
-  proc mySqrt(num: float): float {.noSideEffect.} =
-    return sqrt(num)
-
-  # check gamma function
-  assert(gamma(5.0) == 24.0) # 4!
-  assert($tgamma(5.0) == $24.0) # 4!
-  assert(lgamma(1.0) == 0.0) # ln(1.0) == 0.0
-  assert(erf(6.0) > erf(5.0))
-  assert(erfc(6.0) < erfc(5.0))
-
-when isMainModule:
-  # Function for approximate comparison of floats
-  proc `==~`(x, y: float): bool = (abs(x-y) < 1e-9)
-
-  block: # prod
-    doAssert prod([1, 2, 3, 4]) == 24
-    doAssert prod([1.5, 3.4]) == 5.1
-    let x: seq[float] = @[]
-    doAssert prod(x) == 1.0
-
-  block: # round() tests
-    # Round to 0 decimal places
-    doAssert round(54.652) ==~ 55.0
-    doAssert round(54.352) ==~ 54.0
-    doAssert round(-54.652) ==~ -55.0
-    doAssert round(-54.352) ==~ -54.0
-    doAssert round(0.0) ==~ 0.0
-    # Round to positive decimal places
-    doAssert round(-547.652, 1) ==~ -547.7
-    doAssert round(547.652, 1) ==~ 547.7
-    doAssert round(-547.652, 2) ==~ -547.65
-    doAssert round(547.652, 2) ==~ 547.65
-    # Round to negative decimal places
-    doAssert round(547.652, -1) ==~ 550.0
-    doAssert round(547.652, -2) ==~ 500.0
-    doAssert round(547.652, -3) ==~ 1000.0
-    doAssert round(547.652, -4) ==~ 0.0
-    doAssert round(-547.652, -1) ==~ -550.0
-    doAssert round(-547.652, -2) ==~ -500.0
-    doAssert round(-547.652, -3) ==~ -1000.0
-    doAssert round(-547.652, -4) ==~ 0.0
-
-  block: # splitDecimal() tests
-    doAssert splitDecimal(54.674).intpart ==~ 54.0
-    doAssert splitDecimal(54.674).floatpart ==~ 0.674
-    doAssert splitDecimal(-693.4356).intpart ==~ -693.0
-    doAssert splitDecimal(-693.4356).floatpart ==~ -0.4356
-    doAssert splitDecimal(0.0).intpart ==~ 0.0
-    doAssert splitDecimal(0.0).floatpart ==~ 0.0
-
-  block: # trunc tests for vcc
-    doAssert(trunc(-1.1) == -1)
-    doAssert(trunc(1.1) == 1)
-    doAssert(trunc(-0.1) == -0)
-    doAssert(trunc(0.1) == 0)
-
-    #special case
-    doAssert(classify(trunc(1e1000000)) == fcInf)
-    doAssert(classify(trunc(-1e1000000)) == fcNegInf)
-    doAssert(classify(trunc(0.0/0.0)) == fcNan)
-    doAssert(classify(trunc(0.0)) == fcZero)
-
-    #trick the compiler to produce signed zero
-    let
-      f_neg_one = -1.0
-      f_zero = 0.0
-      f_nan = f_zero / f_zero
-
-    doAssert(classify(trunc(f_neg_one*f_zero)) == fcNegZero)
-
-    doAssert(trunc(-1.1'f32) == -1)
-    doAssert(trunc(1.1'f32) == 1)
-    doAssert(trunc(-0.1'f32) == -0)
-    doAssert(trunc(0.1'f32) == 0)
-    doAssert(classify(trunc(1e1000000'f32)) == fcInf)
-    doAssert(classify(trunc(-1e1000000'f32)) == fcNegInf)
-    doAssert(classify(trunc(f_nan.float32)) == fcNan)
-    doAssert(classify(trunc(0.0'f32)) == fcZero)
-
-  block: # sgn() tests
-    assert sgn(1'i8) == 1
-    assert sgn(1'i16) == 1
-    assert sgn(1'i32) == 1
-    assert sgn(1'i64) == 1
-    assert sgn(1'u8) == 1
-    assert sgn(1'u16) == 1
-    assert sgn(1'u32) == 1
-    assert sgn(1'u64) == 1
-    assert sgn(-12342.8844'f32) == -1
-    assert sgn(123.9834'f64) == 1
-    assert sgn(0'i32) == 0
-    assert sgn(0'f32) == 0
-    assert sgn(NegInf) == -1
-    assert sgn(Inf) == 1
-    assert sgn(NaN) == 0
-
-  block: # fac() tests
-    try:
-      discard fac(-1)
-    except AssertionError:
-      discard
-
-    doAssert fac(0) == 1
-    doAssert fac(1) == 1
-    doAssert fac(2) == 2
-    doAssert fac(3) == 6
-    doAssert fac(4) == 24
-
-  block: # floorMod/floorDiv
-    doAssert floorDiv(8, 3) == 2
-    doAssert floorMod(8, 3) == 2
-
-    doAssert floorDiv(8, -3) == -3
-    doAssert floorMod(8, -3) == -1
+when useBuiltins:
+  ## this func uses bitwise comparisons from C compilers, which are not always available.
+  func gcd*(x, y: SomeInteger): SomeInteger =
+    ## Computes the greatest common (positive) divisor of `x` and `y`,
+    ## using the binary GCD (aka Stein's) algorithm.
+    ##
+    ## **See also:**
+    ## * `gcd func <#gcd,T,T>`_ for a float version
+    ## * `lcm func <#lcm,T,T>`_
+    runnableExamples:
+      doAssert gcd(12, 8) == 4
+      doAssert gcd(17, 63) == 1
+  
+    when x is SomeSignedInt:
+      var x = abs(x)
+    else:
+      var x = x
+    when y is SomeSignedInt:
+      var y = abs(y)
+    else:
+      var y = y
+  
+    if x == 0:
+      return y
+    if y == 0:
+      return x
+  
+    let shift = countTrailingZeroBits(x or y)
+    y = y shr countTrailingZeroBits(y)
+    while x != 0:
+      x = x shr countTrailingZeroBits(x)
+      if y > x:
+        swap y, x
+      x -= y
+    y shl shift
+  
+func gcd*[T](x: openArray[T]): T {.since: (1, 1).} =
+  ## Computes the greatest common (positive) divisor of the elements of `x`.
+  ##
+  ## **See also:**
+  ## * `gcd func <#gcd,T,T>`_ for a version with two arguments
+  runnableExamples:
+    doAssert gcd(@[13.5, 9.0]) == 4.5
 
-    doAssert floorDiv(-8, 3) == -3
-    doAssert floorMod(-8, 3) == 1
+  result = x[0]
+  for i in 1 ..< x.len:
+    result = gcd(result, x[i])
 
-    doAssert floorDiv(-8, -3) == 2
-    doAssert floorMod(-8, -3) == -2
+func lcm*[T](x, y: T): T =
+  ## Computes the least common multiple of `x` and `y`.
+  ##
+  ## **See also:**
+  ## * `gcd func <#gcd,T,T>`_
+  runnableExamples:
+    doAssert lcm(24, 30) == 120
+    doAssert lcm(13, 39) == 39
 
-    doAssert floorMod(8.0, -3.0) ==~ -1.0
-    doAssert floorMod(-8.5, 3.0) ==~ 0.5
+  x div gcd(x, y) * y
 
-  block: # log
-    doAssert log(4.0, 3.0) == ln(4.0) / ln(3.0)
+func clamp*[T](val: T, bounds: Slice[T]): T {.since: (1, 5), inline.} =
+  ## Like `system.clamp`, but takes a slice, so you can easily clamp within a range.
+  runnableExamples:
+    assert clamp(10, 1 .. 5) == 5
+    assert clamp(1, 1 .. 3) == 1
+    type A = enum a0, a1, a2, a3, a4, a5
+    assert a1.clamp(a2..a4) == a2
+    assert clamp((3, 0), (1, 0) .. (2, 9)) == (2, 9)
+    doAssertRaises(AssertionDefect): discard clamp(1, 3..2) # invalid bounds
+  assert bounds.a <= bounds.b, $(bounds.a, bounds.b)
+  clamp(val, bounds.a, bounds.b)
+
+func lcm*[T](x: openArray[T]): T {.since: (1, 1).} =
+  ## Computes the least common multiple of the elements of `x`.
+  ##
+  ## **See also:**
+  ## * `lcm func <#lcm,T,T>`_ for a version with two arguments
+  runnableExamples:
+    doAssert lcm(@[24, 30]) == 120
+
+  result = x[0]
+  for i in 1 ..< x.len:
+    result = lcm(result, x[i])
diff --git a/lib/pure/md5.nim b/lib/pure/md5.nim
index 1ff3a9824..9c3f6d51b 100644
--- a/lib/pure/md5.nim
+++ b/lib/pure/md5.nim
@@ -7,13 +7,28 @@
 #    distribution, for details about the copyright.
 #
 
-## Module for computing MD5 checksums.
+## Module for computing [MD5 checksums](https://en.wikipedia.org/wiki/MD5).
+##
+## This module also works at compile time and in JavaScript.
+##
+## See also
+## ========
+## * `base64 module<base64.html>`_ for a Base64 encoder and decoder
+## * `sha1 module <sha1.html>`_ for the SHA-1 checksum algorithm
+## * `hashes module<hashes.html>`_ for efficient computations of hash values
+##   for diverse Nim types
+
+{.deprecated: "use command `nimble install checksums` and import `checksums/md5` instead".}
+
+when defined(nimHasStyleChecks):
+  {.push styleChecks: off.}
 
 type
   MD5State = array[0..3, uint32]
   MD5Block = array[0..15, uint32]
   MD5CBits = array[0..7, uint8]
   MD5Digest* = array[0..15, uint8]
+    ## MD5 checksum of a string, obtained with the `toMD5 proc <#toMD5,string>`_.
   MD5Buffer = array[0..63, uint8]
   MD5Context* {.final.} = object
     state: MD5State
@@ -21,15 +36,16 @@ type
     buffer: MD5Buffer
 
 const
-  padding: cstring = "\x80\0\0\0" &
-                     "\0\0\0\0\0\0\0\0" &
-                     "\0\0\0\0\0\0\0\0" &
-                     "\0\0\0\0\0\0\0\0" &
-                     "\0\0\0\0\0\0\0\0" &
-                     "\0\0\0\0\0\0\0\0" &
-                     "\0\0\0\0\0\0\0\0" &
-                     "\0\0\0\0\0\0\0\0" &
-                     "\0\0\0\0"
+  padding: array[0..63, uint8] = [
+    0x80'u8, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0
+  ]
 
 proc F(x, y, z: uint32): uint32 {.inline.} =
   result = (x and y) or ((not x) and z)
@@ -66,7 +82,7 @@ proc II(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
   rot(a, s)
   a = a + b
 
-proc encode(dest: var MD5Block, src: cstring) =
+proc encode(dest: var MD5Block, src: openArray[uint8]) =
   var j = 0
   for i in 0..high(dest):
     dest[i] = uint32(ord(src[j])) or
@@ -84,10 +100,35 @@ proc decode(dest: var openArray[uint8], src: openArray[uint32]) =
     dest[i+3] = uint8(src[j] shr 24 and 0xff'u32)
     inc(i, 4)
 
-proc transform(buffer: pointer, state: var MD5State) =
+template slice(s: cstring, a, b): openArray[uint8] =
+  when nimvm:
+    # toOpenArray is not implemented in VM
+    toOpenArrayByte($s, a, b)
+  else:
+    when defined(js):
+      # toOpenArrayByte for cstring is not implemented in JS
+      toOpenArrayByte($s, a, b)
+    else:
+      s.toOpenArrayByte(a, b)
+
+template slice(s: openArray[uint8], a, b): openArray[uint8] =
+  s.toOpenArray(a, b)
+
+const useMem = declared(copyMem)
+
+template memOrNot(withMem, withoutMem): untyped =
+  when nimvm:
+    withoutMem
+  else:
+    when useMem:
+      withMem
+    else:
+      withoutMem
+
+proc transform(buffer: openArray[uint8], state: var MD5State) =
   var
     myBlock: MD5Block
-  encode(myBlock, cast[cstring](buffer))
+  encode(myBlock, buffer)
   var a = state[0]
   var b = state[1]
   var c = state[2]
@@ -161,58 +202,37 @@ proc transform(buffer: pointer, state: var MD5State) =
   state[2] = state[2] + c
   state[3] = state[3] + d
 
-proc md5Init*(c: var MD5Context) =
-  ## initializes a MD5Context
-  c.state[0] = 0x67452301'u32
-  c.state[1] = 0xEFCDAB89'u32
-  c.state[2] = 0x98BADCFE'u32
-  c.state[3] = 0x10325476'u32
-  c.count[0] = 0'u32
-  c.count[1] = 0'u32
-  zeroMem(addr(c.buffer), sizeof(MD5buffer))
+proc md5Init*(c: var MD5Context) {.raises: [], tags: [], gcsafe.}
+proc md5Update*(c: var MD5Context, input: openArray[uint8]) {.raises: [],
+    tags: [], gcsafe.}
+proc md5Final*(c: var MD5Context, digest: var MD5Digest) {.raises: [], tags: [], gcsafe.}
 
-proc md5Update*(c: var MD5Context, input: cstring, len: int) =
-  ## updates the MD5Context with the `input` data of length `len`
-  var input = input
-  var Index = int((c.count[0] shr 3) and 0x3F)
-  c.count[0] = c.count[0] + (uint32(len) shl 3)
-  if c.count[0] < (uint32(len) shl 3): c.count[1] = c.count[1] + 1'u32
-  c.count[1] = c.count[1] + (uint32(len) shr 29)
-  var PartLen = 64 - Index
-  if len >= PartLen:
-    copyMem(addr(c.buffer[Index]), input, PartLen)
-    transform(addr(c.buffer), c.state)
-    var i = PartLen
-    while i + 63 < len:
-      transform(addr(input[i]), c.state)
-      inc(i, 64)
-    copyMem(addr(c.buffer[0]), addr(input[i]), len-i)
-  else:
-    copyMem(addr(c.buffer[Index]), addr(input[0]), len)
+proc md5Update*(c: var MD5Context, input: cstring, len: int) {.raises: [],
+    tags: [], gcsafe.} =
+  ## Updates the `MD5Context` with the `input` data of length `len`.
+  ##
+  ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this
+  ## function explicitly.
+  md5Update(c, input.slice(0, len - 1))
 
-proc md5Final*(c: var MD5Context, digest: var MD5Digest) =
-  ## finishes the MD5Context and stores the result in `digest`
-  var
-    Bits: MD5CBits
-    PadLen: int
-  decode(Bits, c.count)
-  var Index = int((c.count[0] shr 3) and 0x3F)
-  if Index < 56: PadLen = 56 - Index
-  else: PadLen = 120 - Index
-  md5Update(c, padding, PadLen)
-  md5Update(c, cast[cstring](addr(Bits)), 8)
-  decode(digest, c.state)
-  zeroMem(addr(c), sizeof(MD5Context))
 
 proc toMD5*(s: string): MD5Digest =
-  ## computes the MD5Digest value for a string `s`
+  ## Computes the `MD5Digest` value for a string `s`.
+  ##
+  ## **See also:**
+  ## * `getMD5 proc <#getMD5,string>`_ which returns a string representation
+  ##   of the `MD5Digest`
+  ## * `$ proc <#$,MD5Digest>`_ for converting MD5Digest to string
+  runnableExamples:
+    assert $toMD5("abc") == "900150983cd24fb0d6963f7d28e17f72"
+
   var c: MD5Context
   md5Init(c)
-  md5Update(c, cstring(s), len(s))
+  md5Update(c, s.slice(0, s.len - 1))
   md5Final(c, result)
 
 proc `$`*(d: MD5Digest): string =
-  ## converts a MD5Digest value into its string representation
+  ## Converts a `MD5Digest` value into its string representation.
   const digits = "0123456789abcdef"
   result = ""
   for i in 0..15:
@@ -220,24 +240,96 @@ proc `$`*(d: MD5Digest): string =
     add(result, digits[d[i].int and 0xF])
 
 proc getMD5*(s: string): string =
-  ## computes an MD5 value of `s` and returns its string representation
+  ## Computes an MD5 value of `s` and returns its string representation.
+  ##
+  ## **See also:**
+  ## * `toMD5 proc <#toMD5,string>`_ which returns the `MD5Digest` of a string
+  runnableExamples:
+    assert getMD5("abc") == "900150983cd24fb0d6963f7d28e17f72"
+
   var
     c: MD5Context
     d: MD5Digest
   md5Init(c)
-  md5Update(c, cstring(s), len(s))
+  md5Update(c, s.slice(0, s.len - 1))
   md5Final(c, d)
   result = $d
 
 proc `==`*(D1, D2: MD5Digest): bool =
-  ## checks if two MD5Digest values are identical
+  ## Checks if two `MD5Digest` values are identical.
   for i in 0..15:
     if D1[i] != D2[i]: return false
   return true
 
-when isMainModule:
-  assert(getMD5("Franz jagt im komplett verwahrlosten Taxi quer durch Bayern") ==
-    "a3cca2b2aa1e3b5b3b5aad99a8529074")
-  assert(getMD5("Frank jagt im komplett verwahrlosten Taxi quer durch Bayern") ==
-    "7e716d0e702df0505fc72e2b89467910")
-  assert($toMD5("") == "d41d8cd98f00b204e9800998ecf8427e")
+
+proc clearBuffer(c: var MD5Context) {.inline.} =
+  memOrNot:
+    zeroMem(addr(c.buffer), sizeof(MD5Buffer))
+  do:
+    reset(c.buffer)
+
+proc md5Init*(c: var MD5Context) =
+  ## Initializes an `MD5Context`.
+  ##
+  ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this
+  ## function explicitly.
+  c.state[0] = 0x67452301'u32
+  c.state[1] = 0xEFCDAB89'u32
+  c.state[2] = 0x98BADCFE'u32
+  c.state[3] = 0x10325476'u32
+  c.count[0] = 0'u32
+  c.count[1] = 0'u32
+  clearBuffer(c)
+
+proc writeBuffer(c: var MD5Context, index: int,
+                 input: openArray[uint8], inputIndex, len: int) {.inline.} =
+  memOrNot:
+    copyMem(addr(c.buffer[index]), unsafeAddr(input[inputIndex]), len)
+  do:
+    # cannot use system.`[]=` for arrays and openarrays as
+    # it can raise RangeDefect which gets tracked
+    for i in 0..<len:
+      c.buffer[index + i] = input[inputIndex + i]
+
+proc md5Update*(c: var MD5Context, input: openArray[uint8]) =
+  ## Updates the `MD5Context` with the `input` data.
+  ##
+  ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this
+  ## function explicitly.
+  var Index = int((c.count[0] shr 3) and 0x3F)
+  c.count[0] = c.count[0] + (uint32(input.len) shl 3)
+  if c.count[0] < (uint32(input.len) shl 3): c.count[1] = c.count[1] + 1'u32
+  c.count[1] = c.count[1] + (uint32(input.len) shr 29)
+  var PartLen = 64 - Index
+  if input.len >= PartLen:
+    writeBuffer(c, Index, input, 0, PartLen)
+    transform(c.buffer, c.state)
+    var i = PartLen
+    while i + 63 < input.len:
+      transform(input.slice(i, i + 63), c.state)
+      inc(i, 64)
+    if i < input.len:
+      writeBuffer(c, 0, input, i, input.len - i)
+  elif input.len > 0:
+    writeBuffer(c, Index, input, 0, input.len)
+
+proc md5Final*(c: var MD5Context, digest: var MD5Digest) =
+  ## Finishes the `MD5Context` and stores the result in `digest`.
+  ##
+  ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this
+  ## function explicitly.
+  var
+    Bits: MD5CBits
+    PadLen: int
+  decode(Bits, c.count)
+  var Index = int((c.count[0] shr 3) and 0x3F)
+  if Index < 56: PadLen = 56 - Index
+  else: PadLen = 120 - Index
+  md5Update(c, padding.slice(0, PadLen - 1))
+  md5Update(c, Bits)
+  decode(digest, c.state)
+  clearBuffer(c)
+
+
+when defined(nimHasStyleChecks):
+  {.pop.} #{.push styleChecks: off.}
\ No newline at end of file
diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim
index c7b8ebbd8..8eec551c4 100644
--- a/lib/pure/memfiles.nim
+++ b/lib/pure/memfiles.nim
@@ -16,38 +16,76 @@
 ## other "line-like", variable length, delimited records).
 
 when defined(windows):
-  import winlean
+  import std/winlean
+  when defined(nimPreviewSlimSystem):
+    import std/widestrs
 elif defined(posix):
-  import posix
+  import std/posix
 else:
   {.error: "the memfiles module is not supported on your operating system!".}
 
-import os, streams
+import std/streams
+import std/oserrors
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions]
+
 
 proc newEIO(msg: string): ref IOError =
   new(result)
   result.msg = msg
 
+proc setFileSize(fh: FileHandle, newFileSize = -1, oldSize = -1): OSErrorCode =
+  ## Set the size of open file pointed to by `fh` to `newFileSize` if != -1,
+  ## allocating | freeing space from the file system.  This routine returns the
+  ## last OSErrorCode found rather than raising to support old rollback/clean-up
+  ## code style. [ Should maybe move to std/osfiles. ]
+  if newFileSize < 0 or newFileSize == oldSize:
+    return
+  when defined(windows):
+    var sizeHigh = int32(newFileSize shr 32)
+    let sizeLow = int32(newFileSize and 0xffffffff)
+    let status = setFilePointer(fh, sizeLow, addr(sizeHigh), FILE_BEGIN)
+    let lastErr = osLastError()
+    if (status == INVALID_SET_FILE_POINTER and lastErr.int32 != NO_ERROR) or
+        setEndOfFile(fh) == 0:
+      result = lastErr
+  else:
+    if newFileSize > oldSize: # grow the file
+      var e: cint # posix_fallocate truncates up when needed.
+      when declared(posix_fallocate):
+        while (e = posix_fallocate(fh, 0, newFileSize); e == EINTR):
+          discard
+      if e in [EINVAL, EOPNOTSUPP] and ftruncate(fh, newFileSize) == -1:
+        result = osLastError() # fallback arguable; Most portable BUT allows SEGV
+      elif e != 0:
+        result = osLastError()
+    else: # shrink the file
+      if ftruncate(fh.cint, newFileSize) == -1:
+        result = osLastError()
+
 type
-  MemFile* = object  ## represents a memory mapped file
-    mem*: pointer    ## a pointer to the memory mapped file. The pointer
-                     ## can be used directly to change the contents of the
-                     ## file, if it was opened with write access.
-    size*: int       ## size of the memory mapped file
+  MemFile* = object      ## represents a memory mapped file
+    mem*: pointer        ## a pointer to the memory mapped file. The pointer
+                         ## can be used directly to change the contents of the
+                         ## file, if it was opened with write access.
+    size*: int           ## size of the memory mapped file
 
     when defined(windows):
-      fHandle: Handle
-      mapHandle: Handle
-      wasOpened: bool   ## only close if wasOpened
+      fHandle*: Handle   ## **Caution**: Windows specific public field to allow
+                         ## even more low level trickery.
+      mapHandle*: Handle ## **Caution**: Windows specific public field.
+      wasOpened*: bool   ## **Caution**: Windows specific public field.
     else:
-      handle: cint
+      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
-  ## ``offset`` must be multiples of the PAGE SIZE of your OS
+  ## `mappedSize` of `-1` maps to the whole file, and
+  ## `offset` must be multiples of the PAGE SIZE of your OS
   if mode == fmAppend:
     raise newEIO("The append mode is not supported.")
 
@@ -58,29 +96,35 @@ proc mapMem*(m: var MemFile, mode: FileMode = fmRead,
       if readonly: FILE_MAP_READ else: FILE_MAP_READ or FILE_MAP_WRITE,
       int32(offset shr 32),
       int32(offset and 0xffffffff),
-      if mappedSize == -1: 0 else: mappedSize,
+      WinSizeT(if mappedSize == -1: 0 else: mappedSize),
       nil)
     if result == nil:
       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())
 
 
 proc unmapMem*(f: var MemFile, p: pointer, size: int) =
-  ## unmaps the memory region ``(p, <p+size)`` of the mapped file `f`.
+  ## unmaps the memory region `(p, <p+size)` of the mapped file `f`.
   ## All changes are written back to the file system, if `f` was opened
   ## with write access.
   ##
-  ## ``size`` must be of exactly the size that was requested
-  ## via ``mapMem``.
+  ## `size` must be of exactly the size that was requested
+  ## via `mapMem`.
   when defined(windows):
     if unmapViewOfFile(p) == 0: raiseOSError(osLastError())
   else:
@@ -89,24 +133,28 @@ 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 =
-  ## opens a memory mapped file. If this fails, ``EOS`` is raised.
+           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
+  ## `newFileSize` can only be set if the file does not exist and is opened
   ## with write access (e.g., with fmReadWrite).
   ##
-  ##``mappedSize`` and ``offset``
+  ##`mappedSize` and `offset`
   ## can be used to map only a slice of the file.
   ##
-  ## ``offset`` must be multiples of the PAGE SIZE of your OS
+  ## `offset` must be multiples of the PAGE SIZE of your OS
   ## (usually 4K or 8K but is unique to your OS)
   ##
-  ## ``allowRemap`` only needs to be true if you want to call ``mapMem`` on
+  ## `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
+  ##   ```nim
   ##   var
   ##     mm, mm_full, mm_half: MemFile
   ##
@@ -118,6 +166,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
   ##
   ##   # Read the first 512 bytes
   ##   mm_half = memfiles.open("/tmp/test.mmap", mode = fmReadWrite, mappedSize = 512)
+  ##   ```
 
   # The file can be resized only when write mode is used:
   if mode == fmAppend:
@@ -141,7 +190,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
       if result.mapHandle != 0: discard closeHandle(result.mapHandle)
       raiseOSError(errCode)
       # return false
-      #raise newException(EIO, msg)
+      #raise newException(IOError, msg)
 
     template callCreateFile(winApiProc, filename): untyped =
       winApiProc(
@@ -151,28 +200,17 @@ proc open*(filename: string, mode: FileMode = fmRead,
         if readonly: shareMode else: shareMode or FILE_SHARE_WRITE,
         nil,
         if newFileSize != -1: CREATE_ALWAYS else: OPEN_EXISTING,
-        if readonly: FILE_ATTRIBUTE_READONLY or flags else: FILE_ATTRIBUTE_NORMAL or flags,
+        if readonly: FILE_ATTRIBUTE_READONLY or flags
+        else: FILE_ATTRIBUTE_NORMAL or flags,
         0)
 
-    when useWinUnicode:
-      result.fHandle = callCreateFile(createFileW, newWideCString(filename))
-    else:
-      result.fHandle = callCreateFile(createFileA, filename)
+    result.fHandle = callCreateFile(createFileW, newWideCString(filename))
 
     if result.fHandle == INVALID_HANDLE_VALUE:
       fail(osLastError(), "error opening file")
 
-    if newFileSize != -1:
-      var
-        sizeHigh = int32(newFileSize shr 32)
-        sizeLow  = int32(newFileSize and 0xffffffff)
-
-      var status = setFilePointer(result.fHandle, sizeLow, addr(sizeHigh),
-                                  FILE_BEGIN)
-      let lastErr = osLastError()
-      if (status == INVALID_SET_FILE_POINTER and lastErr.int32 != NO_ERROR) or
-         (setEndOfFile(result.fHandle) == 0):
-        fail(lastErr, "error setting file size")
+    if (let e = setFileSize(result.fHandle.FileHandle, newFileSize);
+        e != 0.OSErrorCode): fail(e, "error setting file size")
 
     # since the strings are always 'nil', we simply always call
     # CreateFileMappingW which should be slightly faster anyway:
@@ -206,7 +244,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
 
     result.wasOpened = true
     if not allowRemap and result.fHandle != INVALID_HANDLE_VALUE:
-      if closeHandle(result.fHandle) == 0:
+      if closeHandle(result.fHandle) != 0:
         result.fHandle = INVALID_HANDLE_VALUE
 
   else:
@@ -215,43 +253,37 @@ proc open*(filename: string, mode: FileMode = fmRead,
       if result.handle != -1: discard close(result.handle)
       raiseOSError(errCode)
 
-    var flags = if readonly: O_RDONLY else: O_RDWR
+    var flags = (if readonly: O_RDONLY else: O_RDWR) or O_CLOEXEC
 
     if newFileSize != -1:
       flags = flags or O_CREAT or O_TRUNC
-      var permissions_mode = S_IRUSR or S_IWUSR
-      result.handle = open(filename, flags, permissions_mode)
+      var permissionsMode = S_IRUSR or S_IWUSR
+      result.handle = open(filename, flags, permissionsMode)
+      if result.handle != -1:
+        if (let e = setFileSize(result.handle.FileHandle, newFileSize);
+            e != 0.OSErrorCode): fail(e, "error setting file size")
     else:
       result.handle = open(filename, flags)
 
     if result.handle == -1:
-      # XXX: errno is supposed to be set here
-      # Is there an exception that wraps it?
       fail(osLastError(), "error opening file")
 
-    if newFileSize != -1:
-      if ftruncate(result.handle, newFileSize) == -1:
-        fail(osLastError(), "error setting file size")
-
-    if mappedSize != -1:
-      result.size = mappedSize
-    else:
-      var stat: Stat
+    if mappedSize != -1: #XXX Logic here differs from `when windows` branch ..
+      result.size = mappedSize #.. which always fstats&Uses min(mappedSize, st).
+    else: # if newFileSize!=-1: result.size=newFileSize # if trust setFileSize
+      var stat: Stat  #^^.. BUT some FSes (eg. Linux HugeTLBfs) round to 2MiB.
       if fstat(result.handle, stat) != -1:
-        # XXX: Hmm, this could be unsafe
-        # Why is mmap taking int anyway?
-        result.size = int(stat.st_size)
+        result.size = stat.st_size.int # int may be 32-bit-unsafe for 2..<4 GiB
       else:
         fail(osLastError(), "error getting file size")
 
-    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.handle,
-      offset)
+    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
 
+    let pr = if readonly: PROT_READ else: PROT_READ or PROT_WRITE
+    result.mem = mmap(nil, result.size, pr, result.flags, result.handle, offset)
     if result.mem == cast[pointer](MAP_FAILED):
       fail(osLastError(), "file mapping failed")
 
@@ -281,6 +313,59 @@ proc flush*(f: var MemFile; attempts: Natural = 3) =
       if lastErr != EBUSY.OSErrorCode:
         raiseOSError(lastErr, "error flushing mapping")
 
+proc resize*(f: var MemFile, newFileSize: int) {.raises: [IOError, OSError].} =
+  ## Resize & re-map the file underlying an `allowRemap MemFile`.  If the OS/FS
+  ## supports it, file space is reserved to ensure room for new virtual pages.
+  ## Caller should wait often enough for `flush` to finish to limit use of
+  ## system RAM for write buffering, perhaps just prior to this call.
+  ## **Note**: this assumes the entire file is mapped read-write at offset 0.
+  ## Also, the value of `.mem` will probably change.
+  if newFileSize < 1: # Q: include system/bitmasks & use PageSize ?
+    raise newException(IOError, "Cannot resize MemFile to < 1 byte")
+  when defined(windows):
+    if not f.wasOpened:
+      raise newException(IOError, "Cannot resize unopened MemFile")
+    if f.fHandle == INVALID_HANDLE_VALUE:
+      raise newException(IOError,
+                         "Cannot resize MemFile opened with allowRemap=false")
+    if unmapViewOfFile(f.mem) == 0 or closeHandle(f.mapHandle) == 0: # Un-do map
+      raiseOSError(osLastError())
+    if newFileSize != f.size: # Seek to size & `setEndOfFile` => allocated.
+      if (let e = setFileSize(f.fHandle.FileHandle, newFileSize);
+          e != 0.OSErrorCode): raiseOSError(e)
+    f.mapHandle = createFileMappingW(f.fHandle, nil, PAGE_READWRITE, 0,0,nil)
+    if f.mapHandle == 0:                                             # Re-do map
+      raiseOSError(osLastError())
+    if (let m = mapViewOfFileEx(f.mapHandle, FILE_MAP_READ or FILE_MAP_WRITE,
+                                0, 0, WinSizeT(newFileSize), nil); m != nil):
+      f.mem  = m
+      f.size = newFileSize
+    else:
+      raiseOSError(osLastError())
+  elif defined(posix):
+    if f.handle == -1:
+      raise newException(IOError,
+                         "Cannot resize MemFile opened with allowRemap=false")
+    if newFileSize != f.size:
+      if (let e = setFileSize(f.handle.FileHandle, newFileSize, f.size);
+          e != 0.OSErrorCode): raiseOSError(e)
+    when defined(linux): #Maybe NetBSD, too?
+      # On Linux this can be over 100 times faster than a munmap,mmap cycle.
+      proc mremap(old: pointer; oldSize, newSize: csize_t; flags: cint):
+          pointer {.importc: "mremap", header: "<sys/mman.h>".}
+      let newAddr = mremap(f.mem, csize_t(f.size), csize_t(newFileSize), 1.cint)
+      if newAddr == cast[pointer](MAP_FAILED):
+        raiseOSError(osLastError())
+    else:
+      if munmap(f.mem, f.size) != 0:
+        raiseOSError(osLastError())
+      let newAddr = mmap(nil, newFileSize, PROT_READ or PROT_WRITE,
+                         f.flags, f.handle, 0)
+      if newAddr == cast[pointer](MAP_FAILED):
+        raiseOSError(osLastError())
+    f.mem = newAddr
+    f.size = newFileSize
+
 proc close*(f: var MemFile) =
   ## closes the memory mapped file `f`. All changes are written back to the
   ## file system, if `f` was opened with write access.
@@ -316,24 +401,21 @@ proc close*(f: var MemFile) =
 
   if error: raiseOSError(lastErr)
 
-type MemSlice* = object  ## represent slice of a MemFile for iteration over delimited lines/records
+type MemSlice* = object ## represent slice of a MemFile for iteration over delimited lines/records
   data*: pointer
   size*: int
 
 proc `==`*(x, y: MemSlice): bool =
   ## Compare a pair of MemSlice for strict equality.
-  proc memcmp(a, b: pointer, n:int):int {.importc: "memcmp",header: "string.h".}
-  result = (x.size == y.size and memcmp(x.data, y.data, x.size) == 0)
+  result = (x.size == y.size and equalMem(x.data, y.data, x.size))
 
 proc `$`*(ms: MemSlice): string {.inline.} =
   ## Return a Nim string built from a MemSlice.
-  var buf = newString(ms.size)
-  copyMem(addr(buf[0]), ms.data, ms.size)
-  buf[ms.size] = '\0'
-  result = buf
+  result.setLen(ms.size)
+  copyMem(result.cstring, ms.data, ms.size)
 
-iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} =
-  ## Iterates over [optional `eat`] `delim`-delimited slices in MemFile `mfile`.
+iterator memSlices*(mfile: MemFile, delim = '\l', eat = '\r'): MemSlice {.inline.} =
+  ## Iterates over \[optional `eat`] `delim`-delimited slices in MemFile `mfile`.
   ##
   ## Default parameters parse lines ending in either Unix(\\l) or Windows(\\r\\l)
   ## style on on a line-by-line basis.  I.e., not every line needs the same ending.
@@ -356,65 +438,67 @@ iterator memSlices*(mfile: MemFile, delim='\l', eat='\r'): MemSlice {.inline.} =
   ## functions, not str* functions).
   ##
   ## Example:
-  ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   var count = 0
   ##   for slice in memSlices(memfiles.open("foo")):
   ##     if slice.size > 0 and cast[cstring](slice.data)[0] != '#':
   ##       inc(count)
   ##   echo count
+  ##   ```
 
-  proc c_memchr(cstr: pointer, c: char, n: csize): pointer {.
-       importc: "memchr", header: "<string.h>" .}
+  proc c_memchr(cstr: pointer, c: char, n: csize_t): pointer {.
+       importc: "memchr", header: "<string.h>".}
   proc `-!`(p, q: pointer): int {.inline.} = return cast[int](p) -% cast[int](q)
   var ms: MemSlice
   var ending: pointer
   ms.data = mfile.mem
   var remaining = mfile.size
   while remaining > 0:
-    ending = c_memchr(ms.data, delim, remaining)
-    if ending == nil:                               # unterminated final slice
-      ms.size = remaining                           # Weird case..check eat?
+    ending = c_memchr(ms.data, delim, csize_t(remaining))
+    if ending == nil: # unterminated final slice
+      ms.size = remaining # Weird case..check eat?
       yield ms
       break
-    ms.size = ending -! ms.data                     # delim is NOT included
+    ms.size = ending -! ms.data # delim is NOT included
     if eat != '\0' and ms.size > 0 and cast[cstring](ms.data)[ms.size - 1] == eat:
-      dec(ms.size)                                  # trim pre-delim char
+      dec(ms.size) # trim pre-delim char
     yield ms
-    ms.data = cast[pointer](cast[int](ending) +% 1)     # skip delim
+    ms.data = cast[pointer](cast[int](ending) +% 1) # skip delim
     remaining = mfile.size - (ms.data -! mfile.mem)
 
-iterator lines*(mfile: MemFile, buf: var TaintedString, delim='\l', eat='\r'): TaintedString {.inline.} =
+iterator lines*(mfile: MemFile, buf: var string, delim = '\l',
+    eat = '\r'): string {.inline.} =
   ## Replace contents of passed buffer with each new line, like
-  ## `readLine(File) <system.html#readLine,File,TaintedString>`_.
-  ## `delim`, `eat`, and delimiting logic is exactly as for
-  ## `memSlices <#memSlices>`_, but Nim strings are returned.
+  ## `readLine(File) <syncio.html#readLine,File,string>`_.
+  ## `delim`, `eat`, and delimiting logic is exactly as for `memSlices
+  ## <#memSlices.i,MemFile,char,char>`_, but Nim strings are returned.
   ##
   ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   var buffer: TaintedString = ""
+  ##   ```nim
+  ##   var buffer: string = ""
   ##   for line in lines(memfiles.open("foo"), buffer):
   ##     echo line
+  ##   ```
 
   for ms in memSlices(mfile, delim, eat):
-    setLen(buf.string, ms.size)
-    copyMem(buf.cstring, ms.data, ms.size)
+    setLen(buf, ms.size)
+    if ms.size > 0:
+      copyMem(addr buf[0], ms.data, ms.size)
     yield buf
 
-iterator lines*(mfile: MemFile, delim='\l', eat='\r'): TaintedString {.inline.} =
+iterator lines*(mfile: MemFile, delim = '\l', eat = '\r'): string {.inline.} =
   ## Return each line in a file as a Nim string, like
-  ## `lines(File) <system.html#lines.i,File>`_.
-  ## `delim`, `eat`, and delimiting logic is exactly as for
-  ## `memSlices <#memSlices>`_, but Nim strings are returned.
+  ## `lines(File) <syncio.html#lines.i,File>`_.
+  ## `delim`, `eat`, and delimiting logic is exactly as for `memSlices
+  ## <#memSlices.i,MemFile,char,char>`_, but Nim strings are returned.
   ##
   ## Example:
-  ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   for line in lines(memfiles.open("foo")):
   ##     echo line
+  ##   ```
 
-  var buf = TaintedString(newStringOfCap(80))
+  var buf = newStringOfCap(80)
   for line in lines(mfile, buf, delim, eat):
     yield buf
 
@@ -432,7 +516,7 @@ proc mmsClose(s: Stream) =
 proc mmsFlush(s: Stream) = flush(MemMapFileStream(s).mf)
 
 proc mmsAtEnd(s: Stream): bool = (MemMapFileStream(s).pos >= MemMapFileStream(s).mf.size) or
-                                 (MemMapFileStream(s).pos < 0)
+                                  (MemMapFileStream(s).pos < 0)
 
 proc mmsSetPosition(s: Stream, pos: int) =
   if pos > MemMapFileStream(s).mf.size or pos < 0:
@@ -442,8 +526,8 @@ proc mmsSetPosition(s: Stream, pos: int) =
 proc mmsGetPosition(s: Stream): int = MemMapFileStream(s).pos
 
 proc mmsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
-  let startAddress = cast[ByteAddress](MemMapFileStream(s).mf.mem)
-  let p = cast[ByteAddress](MemMapFileStream(s).pos)
+  let startAddress = cast[int](MemMapFileStream(s).mf.mem)
+  let p = cast[int](MemMapFileStream(s).pos)
   let l = min(bufLen, MemMapFileStream(s).mf.size - p)
   moveMem(buffer, cast[pointer](startAddress + p), l)
   result = l
@@ -458,17 +542,17 @@ proc mmsWriteData(s: Stream, buffer: pointer, bufLen: int) =
   let size = MemMapFileStream(s).mf.size
   if MemMapFileStream(s).pos + bufLen > size:
     raise newEIO("cannot write to stream")
-  let p = cast[ByteAddress](MemMapFileStream(s).mf.mem) +
-          cast[ByteAddress](MemMapFileStream(s).pos)
+  let p = cast[int](MemMapFileStream(s).mf.mem) +
+          cast[int](MemMapFileStream(s).pos)
   moveMem(cast[pointer](p), buffer, bufLen)
   inc(MemMapFileStream(s).pos, bufLen)
 
-proc newMemMapFileStream*(filename: string, mode: FileMode = fmRead, fileSize: int = -1):
-  MemMapFileStream =
+proc newMemMapFileStream*(filename: string, mode: FileMode = fmRead,
+    fileSize: int = -1): MemMapFileStream =
   ## creates a new stream from the file named `filename` with the mode `mode`.
-  ## Raises ## `EOS` if the file cannot be opened. See the `system
+  ## Raises ## `OSError` if the file cannot be opened. See the `system
   ## <system.html>`_ module for a list of available FileMode enums.
-  ## ``fileSize`` can only be set if the file does not exist and is opened
+  ## `fileSize` can only be set if the file does not exist and is opened
   ## with write access (e.g., with fmReadWrite).
   var mf: MemFile = open(filename, mode, newFileSize = fileSize)
   new(result)
diff --git a/lib/pure/mersenne.nim b/lib/pure/mersenne.nim
deleted file mode 100644
index b2227f114..000000000
--- a/lib/pure/mersenne.nim
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2015 Nim Contributors
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-type
-  MersenneTwister* = object
-    mt: array[0..623, uint32]
-    index: int
-
-proc newMersenneTwister*(seed: uint32): MersenneTwister =
-  result.index = 0
-  result.mt[0] = seed
-  for i in 1'u32 .. 623'u32:
-    result.mt[i] = (0x6c078965'u32 * (result.mt[i-1] xor (result.mt[i-1] shr 30'u32)) + i)
-
-proc generateNumbers(m: var MersenneTwister) =
-  for i in 0..623:
-    var y = (m.mt[i] and 0x80000000'u32) +
-            (m.mt[(i+1) mod 624] and 0x7fffffff'u32)
-    m.mt[i] = m.mt[(i+397) mod 624] xor uint32(y shr 1'u32)
-    if (y mod 2'u32) != 0:
-      m.mt[i] = m.mt[i] xor 0x9908b0df'u32
-
-proc getNum*(m: var MersenneTwister): uint32 =
-  ## Returns the next pseudo random number ranging from 0 to high(uint32)
-  if m.index == 0:
-    generateNumbers(m)
-  result = m.mt[m.index]
-  m.index = (m.index + 1) mod m.mt.len
-
-  result = result xor (result shr 11'u32)
-  result = result xor ((result shl 7'u32) and 0x9d2c5680'u32)
-  result = result xor ((result shl 15'u32) and 0xefc60000'u32)
-  result = result xor (result shr 18'u32)
-
-# Test
-when not defined(testing) and isMainModule:
-  var mt = newMersenneTwister(2525)
-
-  for i in 0..99:
-    echo mt.getNum
diff --git a/lib/pure/mimetypes.nim b/lib/pure/mimetypes.nim
index ff69ba61e..ff639e8e5 100644
--- a/lib/pure/mimetypes.nim
+++ b/lib/pure/mimetypes.nim
@@ -8,518 +8,1064 @@
 #
 
 ## This module implements a mimetypes database
-import strtabs
+
+runnableExamples:
+  var m = newMimetypes()
+  doAssert m.getMimetype("mp4") == "video/mp4"
+  doAssert m.getExt("text/html") == "html"
+  ## Values can be uppercase too.
+  doAssert m.getMimetype("MP4") == "video/mp4"
+  doAssert m.getExt("TEXT/HTML") == "html"
+  ## If values are invalid then `default` is returned.
+  doAssert m.getMimetype("INVALID") == "text/plain"
+  doAssert m.getExt("INVALID/NONEXISTENT") == "txt"
+  doAssert m.getMimetype("") == "text/plain"
+  doAssert m.getExt("") == "txt"
+  ## Register new Mimetypes.
+  m.register(ext = "fakext", mimetype = "text/fakelang")
+  doAssert m.getMimetype("fakext") == "text/fakelang"
+  doAssert m.getMimetype("FaKeXT") == "text/fakelang"
+
+import std/tables
+from std/strutils import startsWith, toLowerAscii, strip
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
 type
   MimeDB* = object
-    mimes: StringTableRef
+    mimes: OrderedTable[string, string]
 
 const mimes* = {
-    "ez": "application/andrew-inset",
-    "anx": "application/annodex",
-    "atom": "application/atom+xml",
-    "atomcat": "application/atomcat+xml",
-    "atomsrv": "application/atomserv+xml",
-    "lin": "application/bbolin",
-    "cap": "application/cap",
-    "pcap": "application/cap",
-    "cu": "application/cu-seeme",
-    "davmount": "application/davmount+xml",
-    "tsp": "application/dsptype",
-    "es": "application/ecmascript",
-    "spl": "application/futuresplash",
-    "hta": "application/hta",
-    "jar": "application/java-archive",
-    "ser": "application/java-serialized-object",
-    "class": "application/java-vm",
-    "js": "application/javascript",
-    "m3g": "application/m3g",
-    "hqx": "application/mac-binhex40",
-    "cpt": "application/mac-compactpro",
-    "nb": "application/mathematica",
-    "nbp": "application/mathematica",
-    "mdb": "application/msaccess",
-    "doc": "application/msword",
-    "dot": "application/msword",
-    "mxf": "application/mxf",
-    "bin": "application/octet-stream",
-    "oda": "application/oda",
-    "ogx": "application/ogg",
-    "pdf": "application/pdf",
-    "key": "application/pgp-keys",
-    "pgp": "application/pgp-signature",
-    "prf": "application/pics-rules",
-    "ps": "application/postscript",
-    "ai": "application/postscript",
-    "eps": "application/postscript",
-    "epsi": "application/postscript",
-    "epsf": "application/postscript",
-    "eps2": "application/postscript",
-    "eps3": "application/postscript",
-    "rar": "application/rar",
-    "rdf": "application/rdf+xml",
-    "rss": "application/rss+xml",
-    "rtf": "application/rtf",
-    "smi": "application/smil",
-    "smil": "application/smil",
-    "xhtml": "application/xhtml+xml",
-    "xht": "application/xhtml+xml",
-    "xml": "application/xml",
-    "xsl": "application/xml",
-    "xsd": "application/xml",
-    "xspf": "application/xspf+xml",
-    "zip": "application/zip",
-    "apk": "application/vnd.android.package-archive",
-    "cdy": "application/vnd.cinderella",
-    "kml": "application/vnd.google-earth.kml+xml",
-    "kmz": "application/vnd.google-earth.kmz",
-    "xul": "application/vnd.mozilla.xul+xml",
-    "xls": "application/vnd.ms-excel",
-    "xlb": "application/vnd.ms-excel",
-    "xlt": "application/vnd.ms-excel",
-    "cat": "application/vnd.ms-pki.seccat",
-    "stl": "application/vnd.ms-pki.stl",
-    "ppt": "application/vnd.ms-powerpoint",
-    "pps": "application/vnd.ms-powerpoint",
-    "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
-    "xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
-    "pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
-    "ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
-    "potx": "application/vnd.openxmlformats-officedocument.presentationml.template",
-    "docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
-    "dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
-    "cod": "application/vnd.rim.cod",
-    "mmf": "application/vnd.smaf",
-    "sis": "application/vnd.symbian.install",
-    "vsd": "application/vnd.visio",
-    "wbxml": "application/vnd.wap.wbxml",
-    "wmlc": "application/vnd.wap.wmlc",
-    "wmlsc": "application/vnd.wap.wmlscriptc",
-    "wpd": "application/vnd.wordperfect",
-    "wp5": "application/vnd.wordperfect5.1",
-    "wk": "application/x-123",
-    "7z": "application/x-7z-compressed",
-    "abw": "application/x-abiword",
-    "dmg": "application/x-apple-diskimage",
-    "bcpio": "application/x-bcpio",
-    "torrent": "application/x-bittorrent",
-    "cab": "application/x-cab",
-    "cbr": "application/x-cbr",
-    "cbz": "application/x-cbz",
-    "cdf": "application/x-cdf",
-    "cda": "application/x-cdf",
-    "vcd": "application/x-cdlink",
-    "pgn": "application/x-chess-pgn",
-    "cpio": "application/x-cpio",
-    "csh": "application/x-csh",
-    "deb": "application/x-debian-package",
-    "udeb": "application/x-debian-package",
-    "dcr": "application/x-director",
-    "dir": "application/x-director",
-    "dxr": "application/x-director",
-    "dms": "application/x-dms",
-    "wad": "application/x-doom",
-    "dvi": "application/x-dvi",
-    "rhtml": "application/x-httpd-eruby",
-    "pfa": "application/x-font",
-    "pfb": "application/x-font",
-    "gsf": "application/x-font",
-    "pcf": "application/x-font",
-    "pcf.Z": "application/x-font",
-    "mm": "application/x-freemind",
-    "spl": "application/x-futuresplash",
-    "gnumeric": "application/x-gnumeric",
-    "sgf": "application/x-go-sgf",
-    "gcf": "application/x-graphing-calculator",
-    "gtar": "application/x-gtar",
-    "tgz": "application/x-gtar",
-    "taz": "application/x-gtar",
-    "hdf": "application/x-hdf",
-    "phtml": "application/x-httpd-php",
-    "pht": "application/x-httpd-php",
-    "php": "application/x-httpd-php",
-    "phps": "application/x-httpd-php-source",
-    "php3": "application/x-httpd-php3",
-    "php3p": "application/x-httpd-php3-preprocessed",
-    "php4": "application/x-httpd-php4",
-    "php5": "application/x-httpd-php5",
-    "ica": "application/x-ica",
-    "info": "application/x-info",
-    "ins": "application/x-internet-signup",
-    "isp": "application/x-internet-signup",
-    "iii": "application/x-iphone",
-    "iso": "application/x-iso9660-image",
-    "jam": "application/x-jam",
-    "jnlp": "application/x-java-jnlp-file",
-    "jmz": "application/x-jmol",
-    "chrt": "application/x-kchart",
-    "kil": "application/x-killustrator",
-    "skp": "application/x-koan",
-    "skd": "application/x-koan",
-    "skt": "application/x-koan",
-    "skm": "application/x-koan",
-    "kpr": "application/x-kpresenter",
-    "kpt": "application/x-kpresenter",
-    "ksp": "application/x-kspread",
-    "kwd": "application/x-kword",
-    "kwt": "application/x-kword",
-    "latex": "application/x-latex",
-    "lha": "application/x-lha",
-    "lyx": "application/x-lyx",
-    "lzh": "application/x-lzh",
-    "lzx": "application/x-lzx",
-    "frm": "application/x-maker",
-    "maker": "application/x-maker",
-    "frame": "application/x-maker",
-    "fm": "application/x-maker",
-    "fb": "application/x-maker",
-    "book": "application/x-maker",
-    "fbdoc": "application/x-maker",
-    "mif": "application/x-mif",
-    "wmd": "application/x-ms-wmd",
-    "wmz": "application/x-ms-wmz",
-    "com": "application/x-msdos-program",
-    "exe": "application/x-msdos-program",
-    "bat": "application/x-msdos-program",
-    "dll": "application/x-msdos-program",
-    "msi": "application/x-msi",
-    "nc": "application/x-netcdf",
-    "pac": "application/x-ns-proxy-autoconfig",
-    "dat": "application/x-ns-proxy-autoconfig",
-    "nwc": "application/x-nwc",
-    "o": "application/x-object",
-    "oza": "application/x-oz-application",
-    "p7r": "application/x-pkcs7-certreqresp",
-    "crl": "application/x-pkcs7-crl",
-    "pyc": "application/x-python-code",
-    "pyo": "application/x-python-code",
-    "qgs": "application/x-qgis",
-    "shp": "application/x-qgis",
-    "shx": "application/x-qgis",
-    "qtl": "application/x-quicktimeplayer",
-    "rpm": "application/x-redhat-package-manager",
-    "rb": "application/x-ruby",
-    "sh": "application/x-sh",
-    "shar": "application/x-shar",
-    "swf": "application/x-shockwave-flash",
-    "swfl": "application/x-shockwave-flash",
-    "scr": "application/x-silverlight",
-    "sit": "application/x-stuffit",
-    "sitx": "application/x-stuffit",
-    "sv4cpio": "application/x-sv4cpio",
-    "sv4crc": "application/x-sv4crc",
-    "tar": "application/x-tar",
-    "tcl": "application/x-tcl",
-    "gf": "application/x-tex-gf",
-    "pk": "application/x-tex-pk",
-    "texinfo": "application/x-texinfo",
-    "texi": "application/x-texinfo",
-    "~": "application/x-trash",
-    "%": "application/x-trash",
-    "bak": "application/x-trash",
-    "old": "application/x-trash",
-    "sik": "application/x-trash",
-    "t": "application/x-troff",
-    "tr": "application/x-troff",
-    "roff": "application/x-troff",
-    "man": "application/x-troff-man",
-    "me": "application/x-troff-me",
-    "ms": "application/x-troff-ms",
-    "ustar": "application/x-ustar",
-    "src": "application/x-wais-source",
-    "wz": "application/x-wingz",
-    "crt": "application/x-x509-ca-cert",
-    "xcf": "application/x-xcf",
-    "fig": "application/x-xfig",
-    "xpi": "application/x-xpinstall",
-    "amr": "audio/amr",
-    "awb": "audio/amr-wb",
-    "amr": "audio/amr",
-    "awb": "audio/amr-wb",
-    "axa": "audio/annodex",
-    "au": "audio/basic",
-    "snd": "audio/basic",
-    "flac": "audio/flac",
-    "mid": "audio/midi",
-    "midi": "audio/midi",
-    "kar": "audio/midi",
-    "mpga": "audio/mpeg",
-    "mpega": "audio/mpeg",
-    "mp2": "audio/mpeg",
-    "mp3": "audio/mpeg",
-    "m4a": "audio/mpeg",
-    "m3u": "audio/mpegurl",
-    "oga": "audio/ogg",
-    "ogg": "audio/ogg",
-    "spx": "audio/ogg",
-    "sid": "audio/prs.sid",
-    "aif": "audio/x-aiff",
-    "aiff": "audio/x-aiff",
-    "aifc": "audio/x-aiff",
-    "gsm": "audio/x-gsm",
-    "m3u": "audio/x-mpegurl",
-    "wma": "audio/x-ms-wma",
-    "wax": "audio/x-ms-wax",
-    "ra": "audio/x-pn-realaudio",
-    "rm": "audio/x-pn-realaudio",
-    "ram": "audio/x-pn-realaudio",
-    "ra": "audio/x-realaudio",
-    "pls": "audio/x-scpls",
-    "sd2": "audio/x-sd2",
-    "wav": "audio/x-wav",
-    "alc": "chemical/x-alchemy",
-    "cac": "chemical/x-cache",
-    "cache": "chemical/x-cache",
-    "csf": "chemical/x-cache-csf",
-    "cbin": "chemical/x-cactvs-binary",
-    "cascii": "chemical/x-cactvs-binary",
-    "ctab": "chemical/x-cactvs-binary",
-    "cdx": "chemical/x-cdx",
-    "cer": "chemical/x-cerius",
-    "c3d": "chemical/x-chem3d",
-    "chm": "chemical/x-chemdraw",
-    "cif": "chemical/x-cif",
-    "cmdf": "chemical/x-cmdf",
-    "cml": "chemical/x-cml",
-    "cpa": "chemical/x-compass",
-    "bsd": "chemical/x-crossfire",
-    "csml": "chemical/x-csml",
-    "csm": "chemical/x-csml",
-    "ctx": "chemical/x-ctx",
-    "cxf": "chemical/x-cxf",
-    "cef": "chemical/x-cxf",
-    "smi": "#chemical/x-daylight-smiles",
-    "emb": "chemical/x-embl-dl-nucleotide",
-    "embl": "chemical/x-embl-dl-nucleotide",
-    "spc": "chemical/x-galactic-spc",
-    "inp": "chemical/x-gamess-input",
-    "gam": "chemical/x-gamess-input",
-    "gamin": "chemical/x-gamess-input",
-    "fch": "chemical/x-gaussian-checkpoint",
-    "fchk": "chemical/x-gaussian-checkpoint",
-    "cub": "chemical/x-gaussian-cube",
-    "gau": "chemical/x-gaussian-input",
-    "gjc": "chemical/x-gaussian-input",
-    "gjf": "chemical/x-gaussian-input",
-    "gal": "chemical/x-gaussian-log",
-    "gcg": "chemical/x-gcg8-sequence",
-    "gen": "chemical/x-genbank",
-    "hin": "chemical/x-hin",
-    "istr": "chemical/x-isostar",
-    "ist": "chemical/x-isostar",
-    "jdx": "chemical/x-jcamp-dx",
-    "dx": "chemical/x-jcamp-dx",
-    "kin": "chemical/x-kinemage",
-    "mcm": "chemical/x-macmolecule",
-    "mmd": "chemical/x-macromodel-input",
-    "mmod": "chemical/x-macromodel-input",
-    "mol": "chemical/x-mdl-molfile",
-    "rd": "chemical/x-mdl-rdfile",
-    "rxn": "chemical/x-mdl-rxnfile",
-    "sd": "chemical/x-mdl-sdfile",
-    "sdf": "chemical/x-mdl-sdfile",
-    "tgf": "chemical/x-mdl-tgf",
-    "mif": "#chemical/x-mif",
-    "mcif": "chemical/x-mmcif",
-    "mol2": "chemical/x-mol2",
-    "b": "chemical/x-molconn-Z",
-    "gpt": "chemical/x-mopac-graph",
-    "mop": "chemical/x-mopac-input",
-    "mopcrt": "chemical/x-mopac-input",
-    "mpc": "chemical/x-mopac-input",
-    "zmt": "chemical/x-mopac-input",
-    "moo": "chemical/x-mopac-out",
-    "mvb": "chemical/x-mopac-vib",
-    "asn": "chemical/x-ncbi-asn1",
-    "prt": "chemical/x-ncbi-asn1-ascii",
-    "ent": "chemical/x-ncbi-asn1-ascii",
-    "val": "chemical/x-ncbi-asn1-binary",
-    "aso": "chemical/x-ncbi-asn1-binary",
-    "asn": "chemical/x-ncbi-asn1-spec",
-    "pdb": "chemical/x-pdb",
-    "ent": "chemical/x-pdb",
-    "ros": "chemical/x-rosdal",
-    "sw": "chemical/x-swissprot",
-    "vms": "chemical/x-vamas-iso14976",
-    "vmd": "chemical/x-vmd",
-    "xtel": "chemical/x-xtel",
-    "xyz": "chemical/x-xyz",
-    "gif": "image/gif",
-    "ief": "image/ief",
-    "jpeg": "image/jpeg",
-    "jpg": "image/jpeg",
-    "jpe": "image/jpeg",
-    "pcx": "image/pcx",
-    "png": "image/png",
-    "svg": "image/svg+xml",
-    "svgz": "image/svg+xml",
-    "tiff": "image/tiff",
-    "tif": "image/tiff",
-    "djvu": "image/vnd.djvu",
-    "djv": "image/vnd.djvu",
-    "wbmp": "image/vnd.wap.wbmp",
-    "cr2": "image/x-canon-cr2",
-    "crw": "image/x-canon-crw",
-    "ras": "image/x-cmu-raster",
-    "cdr": "image/x-coreldraw",
-    "pat": "image/x-coreldrawpattern",
-    "cdt": "image/x-coreldrawtemplate",
-    "cpt": "image/x-corelphotopaint",
-    "erf": "image/x-epson-erf",
-    "ico": "image/x-icon",
-    "art": "image/x-jg",
-    "jng": "image/x-jng",
-    "bmp": "image/x-ms-bmp",
-    "nef": "image/x-nikon-nef",
-    "orf": "image/x-olympus-orf",
-    "psd": "image/x-photoshop",
-    "pnm": "image/x-portable-anymap",
-    "pbm": "image/x-portable-bitmap",
-    "pgm": "image/x-portable-graymap",
-    "ppm": "image/x-portable-pixmap",
-    "rgb": "image/x-rgb",
-    "xbm": "image/x-xbitmap",
-    "xpm": "image/x-xpixmap",
-    "xwd": "image/x-xwindowdump",
-    "eml": "message/rfc822",
-    "igs": "model/iges",
-    "iges": "model/iges",
-    "msh": "model/mesh",
-    "mesh": "model/mesh",
-    "silo": "model/mesh",
-    "wrl": "model/vrml",
-    "vrml": "model/vrml",
-    "x3dv": "model/x3d+vrml",
-    "x3d": "model/x3d+xml",
-    "x3db": "model/x3d+binary",
-    "manifest": "text/cache-manifest",
-    "ics": "text/calendar",
-    "icz": "text/calendar",
-    "css": "text/css",
-    "csv": "text/csv",
-    "323": "text/h323",
-    "html": "text/html",
-    "htm": "text/html",
-    "shtml": "text/html",
-    "uls": "text/iuls",
-    "mml": "text/mathml",
-    "asc": "text/plain",
-    "txt": "text/plain",
-    "text": "text/plain",
-    "pot": "text/plain",
-    "brf": "text/plain",
-    "rtx": "text/richtext",
-    "sct": "text/scriptlet",
-    "wsc": "text/scriptlet",
-    "tm": "text/texmacs",
-    "ts": "text/texmacs",
-    "tsv": "text/tab-separated-values",
-    "jad": "text/vnd.sun.j2me.app-descriptor",
-    "wml": "text/vnd.wap.wml",
-    "wmls": "text/vnd.wap.wmlscript",
-    "bib": "text/x-bibtex",
-    "boo": "text/x-boo",
-    "h++": "text/x-c++hdr",
-    "hpp": "text/x-c++hdr",
-    "hxx": "text/x-c++hdr",
-    "hh": "text/x-c++hdr",
-    "c++": "text/x-c++src",
-    "cpp": "text/x-c++src",
-    "cxx": "text/x-c++src",
-    "cc": "text/x-c++src",
-    "h": "text/x-chdr",
-    "htc": "text/x-component",
-    "csh": "text/x-csh",
-    "c": "text/x-csrc",
-    "d": "text/x-dsrc",
-    "diff": "text/x-diff",
-    "patch": "text/x-diff",
-    "hs": "text/x-haskell",
-    "java": "text/x-java",
-    "lhs": "text/x-literate-haskell",
-    "moc": "text/x-moc",
-    "p": "text/x-pascal",
-    "pas": "text/x-pascal",
-    "gcd": "text/x-pcs-gcd",
-    "pl": "text/x-perl",
-    "pm": "text/x-perl",
-    "py": "text/x-python",
-    "scala": "text/x-scala",
-    "etx": "text/x-setext",
-    "sh": "text/x-sh",
-    "tcl": "text/x-tcl",
-    "tk": "text/x-tcl",
-    "tex": "text/x-tex",
-    "ltx": "text/x-tex",
-    "sty": "text/x-tex",
-    "cls": "text/x-tex",
-    "vcs": "text/x-vcalendar",
-    "vcf": "text/x-vcard",
-    "3gp": "video/3gpp",
-    "axv": "video/annodex",
-    "dl": "video/dl",
-    "dif": "video/dv",
-    "dv": "video/dv",
-    "fli": "video/fli",
-    "gl": "video/gl",
-    "mpeg": "video/mpeg",
-    "mpg": "video/mpeg",
-    "mpe": "video/mpeg",
-    "mp4": "video/mp4",
-    "qt": "video/quicktime",
-    "mov": "video/quicktime",
-    "ogv": "video/ogg",
-    "mxu": "video/vnd.mpegurl",
-    "flv": "video/x-flv",
-    "lsf": "video/x-la-asf",
-    "lsx": "video/x-la-asf",
-    "mng": "video/x-mng",
-    "asf": "video/x-ms-asf",
-    "asx": "video/x-ms-asf",
-    "wm": "video/x-ms-wm",
-    "wmv": "video/x-ms-wmv",
-    "wmx": "video/x-ms-wmx",
-    "wvx": "video/x-ms-wvx",
-    "avi": "video/x-msvideo",
-    "movie": "video/x-sgi-movie",
-    "mpv": "video/x-matroska",
-    "mkv": "video/x-matroska",
-    "ice": "x-conference/x-cooltalk",
-    "sisx": "x-epoc/x-sisx-app",
-    "vrm": "x-world/x-vrml",
-    "vrml": "x-world/x-vrml",
-    "wrl": "x-world/x-vrml"}
+  "ez": "application/andrew-inset",
+  "aw": "application/applixware",
+  "atom": "application/atom+xml",
+  "atomcat": "application/atomcat+xml",
+  "atomsvc": "application/atomsvc+xml",
+  "ccxml": "application/ccxml+xml",
+  "cdmia": "application/cdmi-capability",
+  "cdmic": "application/cdmi-container",
+  "cdmid": "application/cdmi-domain",
+  "cdmio": "application/cdmi-object",
+  "cdmiq": "application/cdmi-queue",
+  "cu": "application/cu-seeme",
+  "davmount": "application/davmount+xml",
+  "dbk": "application/docbook+xml",
+  "dssc": "application/dssc+der",
+  "xdssc": "application/dssc+xml",
+  "ecma": "application/ecmascript",
+  "emma": "application/emma+xml",
+  "epub": "application/epub+zip",
+  "exi": "application/exi",
+  "pfr": "application/font-tdpfr",
+  "gml": "application/gml+xml",
+  "gpx": "application/gpx+xml",
+  "gxf": "application/gxf",
+  "stk": "application/hyperstudio",
+  "ink": "application/inkml+xml",
+  "inkml": "application/inkml+xml",
+  "ipfix": "application/ipfix",
+  "jar": "application/java-archive",
+  "ser": "application/java-serialized-object",
+  "class": "application/java-vm",
+  "json": "application/json",
+  "jsonml": "application/jsonml+json",
+  "lostxml": "application/lost+xml",
+  "hqx": "application/mac-binhex40",
+  "cpt": "application/mac-compactpro",
+  "mads": "application/mads+xml",
+  "mrc": "application/marc",
+  "mrcx": "application/marcxml+xml",
+  "ma": "application/mathematica",
+  "nb": "application/mathematica",
+  "mb": "application/mathematica",
+  "mathml": "application/mathml+xml",
+  "mbox": "application/mbox",
+  "mscml": "application/mediaservercontrol+xml",
+  "metalink": "application/metalink+xml",
+  "meta4": "application/metalink4+xml",
+  "mets": "application/mets+xml",
+  "mods": "application/mods+xml",
+  "m21": "application/mp21",
+  "mp21": "application/mp21",
+  "mp4s": "application/mp4",
+  "doc": "application/msword",
+  "dot": "application/msword",
+  "mxf": "application/mxf",
+  "bin": "application/octet-stream",
+  "dms": "application/octet-stream",
+  "lrf": "application/octet-stream",
+  "mar": "application/octet-stream",
+  "so": "application/octet-stream",
+  "dist": "application/octet-stream",
+  "distz": "application/octet-stream",
+  "pkg": "application/octet-stream",
+  "bpk": "application/octet-stream",
+  "dump": "application/octet-stream",
+  "elc": "application/octet-stream",
+  "deploy": "application/octet-stream",
+  "oda": "application/oda",
+  "opf": "application/oebps-package+xml",
+  "ogx": "application/ogg",
+  "omdoc": "application/omdoc+xml",
+  "onetoc": "application/onenote",
+  "onetoc2": "application/onenote",
+  "onetmp": "application/onenote",
+  "onepkg": "application/onenote",
+  "oxps": "application/oxps",
+  "xer": "application/patch-ops-error+xml",
+  "pdf": "application/pdf",
+  "pgp": "application/pgp-encrypted",
+  "asc": "application/pgp-signature",
+  "sig": "application/pgp-signature",
+  "prf": "application/pics-rules",
+  "p10": "application/pkcs10",
+  "p7m": "application/pkcs7-mime",
+  "p7c": "application/pkcs7-mime",
+  "p7s": "application/pkcs7-signature",
+  "p8": "application/pkcs8",
+  "ac": "application/pkix-attr-cert",
+  "cer": "application/pkix-cert",
+  "crl": "application/pkix-crl",
+  "pkipath": "application/pkix-pkipath",
+  "pki": "application/pkixcmp",
+  "pls": "application/pls+xml",
+  "ai": "application/postscript",
+  "eps": "application/postscript",
+  "ps": "application/postscript",
+  "cww": "application/prs.cww",
+  "pskcxml": "application/pskc+xml",
+  "rdf": "application/rdf+xml",
+  "rif": "application/reginfo+xml",
+  "rnc": "application/relax-ng-compact-syntax",
+  "rl": "application/resource-lists+xml",
+  "rld": "application/resource-lists-diff+xml",
+  "rs": "application/rls-services+xml",
+  "gbr": "application/rpki-ghostbusters",
+  "mft": "application/rpki-manifest",
+  "roa": "application/rpki-roa",
+  "rsd": "application/rsd+xml",
+  "rss": "application/rss+xml",
+  "rtf": "application/rtf",
+  "sbml": "application/sbml+xml",
+  "scq": "application/scvp-cv-request",
+  "scs": "application/scvp-cv-response",
+  "spq": "application/scvp-vp-request",
+  "spp": "application/scvp-vp-response",
+  "sdp": "application/sdp",
+  "setpay": "application/set-payment-initiation",
+  "setreg": "application/set-registration-initiation",
+  "shf": "application/shf+xml",
+  "smi": "application/smil+xml",
+  "smil": "application/smil+xml",
+  "rq": "application/sparql-query",
+  "srx": "application/sparql-results+xml",
+  "gram": "application/srgs",
+  "grxml": "application/srgs+xml",
+  "sru": "application/sru+xml",
+  "ssdl": "application/ssdl+xml",
+  "ssml": "application/ssml+xml",
+  "tei": "application/tei+xml",
+  "teicorpus": "application/tei+xml",
+  "tfi": "application/thraud+xml",
+  "tsd": "application/timestamped-data",
+  "plb": "application/vnd.3gpp.pic-bw-large",
+  "psb": "application/vnd.3gpp.pic-bw-small",
+  "pvb": "application/vnd.3gpp.pic-bw-var",
+  "tcap": "application/vnd.3gpp2.tcap",
+  "pwn": "application/vnd.3m.post-it-notes",
+  "aso": "application/vnd.accpac.simply.aso",
+  "imp": "application/vnd.accpac.simply.imp",
+  "acu": "application/vnd.acucobol",
+  "atc": "application/vnd.acucorp",
+  "acutc": "application/vnd.acucorp",
+  "air": "application/vnd.adobe.air-application-installer-package+zip",
+  "fcdt": "application/vnd.adobe.formscentral.fcdt",
+  "fxp": "application/vnd.adobe.fxp",
+  "fxpl": "application/vnd.adobe.fxp",
+  "xdp": "application/vnd.adobe.xdp+xml",
+  "xfdf": "application/vnd.adobe.xfdf",
+  "ahead": "application/vnd.ahead.space",
+  "azf": "application/vnd.airzip.filesecure.azf",
+  "azs": "application/vnd.airzip.filesecure.azs",
+  "azw": "application/vnd.amazon.ebook",
+  "acc": "application/vnd.americandynamics.acc",
+  "ami": "application/vnd.amiga.ami",
+  "apk": "application/vnd.android.package-archive",
+  "cii": "application/vnd.anser-web-certificate-issue-initiation",
+  "fti": "application/vnd.anser-web-funds-transfer-initiation",
+  "atx": "application/vnd.antix.game-component",
+  "mpkg": "application/vnd.apple.installer+xml",
+  "m3u8": "application/vnd.apple.mpegurl",
+  "swi": "application/vnd.aristanetworks.swi",
+  "iota": "application/vnd.astraea-software.iota",
+  "aep": "application/vnd.audiograph",
+  "mpm": "application/vnd.blueice.multipass",
+  "bmi": "application/vnd.bmi",
+  "rep": "application/vnd.businessobjects",
+  "cdxml": "application/vnd.chemdraw+xml",
+  "mmd": "application/vnd.chipnuts.karaoke-mmd",
+  "cdy": "application/vnd.cinderella",
+  "cla": "application/vnd.claymore",
+  "rp9": "application/vnd.cloanto.rp9",
+  "c4g": "application/vnd.clonk.c4group",
+  "c4d": "application/vnd.clonk.c4group",
+  "c4f": "application/vnd.clonk.c4group",
+  "c4p": "application/vnd.clonk.c4group",
+  "c4u": "application/vnd.clonk.c4group",
+  "c11amc": "application/vnd.cluetrust.cartomobile-config",
+  "c11amz": "application/vnd.cluetrust.cartomobile-config-pkg",
+  "csp": "application/vnd.commonspace",
+  "cdbcmsg": "application/vnd.contact.cmsg",
+  "cmc": "application/vnd.cosmocaller",
+  "clkx": "application/vnd.crick.clicker",
+  "clkk": "application/vnd.crick.clicker.keyboard",
+  "clkp": "application/vnd.crick.clicker.palette",
+  "clkt": "application/vnd.crick.clicker.template",
+  "clkw": "application/vnd.crick.clicker.wordbank",
+  "wbs": "application/vnd.criticaltools.wbs+xml",
+  "pml": "application/vnd.ctc-posml",
+  "ppd": "application/vnd.cups-ppd",
+  "car": "application/vnd.curl.car",
+  "pcurl": "application/vnd.curl.pcurl",
+  "dart": "application/vnd.dart",
+  "rdz": "application/vnd.data-vision.rdz",
+  "uvf": "application/vnd.dece.data",
+  "uvvf": "application/vnd.dece.data",
+  "uvd": "application/vnd.dece.data",
+  "uvvd": "application/vnd.dece.data",
+  "uvt": "application/vnd.dece.ttml+xml",
+  "uvvt": "application/vnd.dece.ttml+xml",
+  "uvx": "application/vnd.dece.unspecified",
+  "uvvx": "application/vnd.dece.unspecified",
+  "uvz": "application/vnd.dece.zip",
+  "uvvz": "application/vnd.dece.zip",
+  "fe_launch": "application/vnd.denovo.fcselayout-link",
+  "dna": "application/vnd.dna",
+  "mlp": "application/vnd.dolby.mlp",
+  "dpg": "application/vnd.dpgraph",
+  "dfac": "application/vnd.dreamfactory",
+  "kpxx": "application/vnd.ds-keypoint",
+  "ait": "application/vnd.dvb.ait",
+  "svc": "application/vnd.dvb.service",
+  "geo": "application/vnd.dynageo",
+  "mag": "application/vnd.ecowin.chart",
+  "nml": "application/vnd.enliven",
+  "esf": "application/vnd.epson.esf",
+  "msf": "application/vnd.epson.msf",
+  "qam": "application/vnd.epson.quickanime",
+  "slt": "application/vnd.epson.salt",
+  "ssf": "application/vnd.epson.ssf",
+  "es3": "application/vnd.eszigno3+xml",
+  "et3": "application/vnd.eszigno3+xml",
+  "ez2": "application/vnd.ezpix-album",
+  "ez3": "application/vnd.ezpix-package",
+  "fdf": "application/vnd.fdf",
+  "mseed": "application/vnd.fdsn.mseed",
+  "seed": "application/vnd.fdsn.seed",
+  "dataless": "application/vnd.fdsn.seed",
+  "gph": "application/vnd.flographit",
+  "ftc": "application/vnd.fluxtime.clip",
+  "fm": "application/vnd.framemaker",
+  "frame": "application/vnd.framemaker",
+  "maker": "application/vnd.framemaker",
+  "book": "application/vnd.framemaker",
+  "fnc": "application/vnd.frogans.fnc",
+  "ltf": "application/vnd.frogans.ltf",
+  "fsc": "application/vnd.fsc.weblaunch",
+  "oas": "application/vnd.fujitsu.oasys",
+  "oa2": "application/vnd.fujitsu.oasys2",
+  "oa3": "application/vnd.fujitsu.oasys3",
+  "fg5": "application/vnd.fujitsu.oasysgp",
+  "bh2": "application/vnd.fujitsu.oasysprs",
+  "ddd": "application/vnd.fujixerox.ddd",
+  "xdw": "application/vnd.fujixerox.docuworks",
+  "xbd": "application/vnd.fujixerox.docuworks.binder",
+  "fzs": "application/vnd.fuzzysheet",
+  "txd": "application/vnd.genomatix.tuxedo",
+  "ggb": "application/vnd.geogebra.file",
+  "ggs": "application/vnd.geogebra.slides",
+  "ggt": "application/vnd.geogebra.tool",
+  "gex": "application/vnd.geometry-explorer",
+  "gre": "application/vnd.geometry-explorer",
+  "gxt": "application/vnd.geonext",
+  "g2w": "application/vnd.geoplan",
+  "g3w": "application/vnd.geospace",
+  "gmx": "application/vnd.gmx",
+  "kml": "application/vnd.google-earth.kml+xml",
+  "kmz": "application/vnd.google-earth.kmz",
+  "gqf": "application/vnd.grafeq",
+  "gqs": "application/vnd.grafeq",
+  "gac": "application/vnd.groove-account",
+  "ghf": "application/vnd.groove-help",
+  "gim": "application/vnd.groove-identity-message",
+  "grv": "application/vnd.groove-injector",
+  "gtm": "application/vnd.groove-tool-message",
+  "tpl": "application/vnd.groove-tool-template",
+  "vcg": "application/vnd.groove-vcard",
+  "hal": "application/vnd.hal+xml",
+  "zmm": "application/vnd.handheld-entertainment+xml",
+  "hbci": "application/vnd.hbci",
+  "les": "application/vnd.hhe.lesson-player",
+  "hpgl": "application/vnd.hp-hpgl",
+  "hpid": "application/vnd.hp-hpid",
+  "hps": "application/vnd.hp-hps",
+  "jlt": "application/vnd.hp-jlyt",
+  "pcl": "application/vnd.hp-pcl",
+  "pclxl": "application/vnd.hp-pclxl",
+  "sfd-hdstx": "application/vnd.hydrostatix.sof-data",
+  "mpy": "application/vnd.ibm.minipay",
+  "afp": "application/vnd.ibm.modcap",
+  "listafp": "application/vnd.ibm.modcap",
+  "list3820": "application/vnd.ibm.modcap",
+  "irm": "application/vnd.ibm.rights-management",
+  "sc": "application/vnd.ibm.secure-container",
+  "icc": "application/vnd.iccprofile",
+  "icm": "application/vnd.iccprofile",
+  "igl": "application/vnd.igloader",
+  "ivp": "application/vnd.immervision-ivp",
+  "ivu": "application/vnd.immervision-ivu",
+  "igm": "application/vnd.insors.igm",
+  "xpw": "application/vnd.intercon.formnet",
+  "xpx": "application/vnd.intercon.formnet",
+  "i2g": "application/vnd.intergeo",
+  "qbo": "application/vnd.intu.qbo",
+  "qfx": "application/vnd.intu.qfx",
+  "rcprofile": "application/vnd.ipunplugged.rcprofile",
+  "irp": "application/vnd.irepository.package+xml",
+  "xpr": "application/vnd.is-xpr",
+  "fcs": "application/vnd.isac.fcs",
+  "jam": "application/vnd.jam",
+  "rms": "application/vnd.jcp.javame.midlet-rms",
+  "jisp": "application/vnd.jisp",
+  "joda": "application/vnd.joost.joda-archive",
+  "ktz": "application/vnd.kahootz",
+  "ktr": "application/vnd.kahootz",
+  "karbon": "application/vnd.kde.karbon",
+  "chrt": "application/vnd.kde.kchart",
+  "kfo": "application/vnd.kde.kformula",
+  "flw": "application/vnd.kde.kivio",
+  "kon": "application/vnd.kde.kontour",
+  "kpr": "application/vnd.kde.kpresenter",
+  "kpt": "application/vnd.kde.kpresenter",
+  "ksp": "application/vnd.kde.kspread",
+  "kwd": "application/vnd.kde.kword",
+  "kwt": "application/vnd.kde.kword",
+  "htke": "application/vnd.kenameaapp",
+  "kia": "application/vnd.kidspiration",
+  "kne": "application/vnd.kinar",
+  "knp": "application/vnd.kinar",
+  "skp": "application/vnd.koan",
+  "skd": "application/vnd.koan",
+  "skt": "application/vnd.koan",
+  "skm": "application/vnd.koan",
+  "sse": "application/vnd.kodak-descriptor",
+  "lasxml": "application/vnd.las.las+xml",
+  "lbd": "application/vnd.llamagraphics.life-balance.desktop",
+  "lbe": "application/vnd.llamagraphics.life-balance.exchange+xml",
+  "123": "application/vnd.lotus-1-2-3",
+  "apr": "application/vnd.lotus-approach",
+  "pre": "application/vnd.lotus-freelance",
+  "nsf": "application/vnd.lotus-notes",
+  "org": "application/vnd.lotus-organizer",
+  "scm": "application/vnd.lotus-screencam",
+  "lwp": "application/vnd.lotus-wordpro",
+  "portpkg": "application/vnd.macports.portpkg",
+  "mcd": "application/vnd.mcd",
+  "mc1": "application/vnd.medcalcdata",
+  "cdkey": "application/vnd.mediastation.cdkey",
+  "mwf": "application/vnd.mfer",
+  "mfm": "application/vnd.mfmp",
+  "flo": "application/vnd.micrografx.flo",
+  "igx": "application/vnd.micrografx.igx",
+  "mif": "application/vnd.mif",
+  "daf": "application/vnd.mobius.daf",
+  "dis": "application/vnd.mobius.dis",
+  "mbk": "application/vnd.mobius.mbk",
+  "mqy": "application/vnd.mobius.mqy",
+  "msl": "application/vnd.mobius.msl",
+  "plc": "application/vnd.mobius.plc",
+  "txf": "application/vnd.mobius.txf",
+  "mpn": "application/vnd.mophun.application",
+  "mpc": "application/vnd.mophun.certificate",
+  "xul": "application/vnd.mozilla.xul+xml",
+  "cil": "application/vnd.ms-artgalry",
+  "cab": "application/vnd.ms-cab-compressed",
+  "xls": "application/vnd.ms-excel",
+  "xlm": "application/vnd.ms-excel",
+  "xla": "application/vnd.ms-excel",
+  "xlc": "application/vnd.ms-excel",
+  "xlt": "application/vnd.ms-excel",
+  "xlw": "application/vnd.ms-excel",
+  "xlam": "application/vnd.ms-excel.addin.macroenabled.12",
+  "xlsb": "application/vnd.ms-excel.sheet.binary.macroenabled.12",
+  "xlsm": "application/vnd.ms-excel.sheet.macroenabled.12",
+  "xltm": "application/vnd.ms-excel.template.macroenabled.12",
+  "eot": "application/vnd.ms-fontobject",
+  "chm": "application/vnd.ms-htmlhelp",
+  "ims": "application/vnd.ms-ims",
+  "lrm": "application/vnd.ms-lrm",
+  "thmx": "application/vnd.ms-officetheme",
+  "cat": "application/vnd.ms-pki.seccat",
+  "stl": "application/vnd.ms-pki.stl",
+  "ppt": "application/vnd.ms-powerpoint",
+  "pps": "application/vnd.ms-powerpoint",
+  "pot": "application/vnd.ms-powerpoint",
+  "ppam": "application/vnd.ms-powerpoint.addin.macroenabled.12",
+  "pptm": "application/vnd.ms-powerpoint.presentation.macroenabled.12",
+  "sldm": "application/vnd.ms-powerpoint.slide.macroenabled.12",
+  "ppsm": "application/vnd.ms-powerpoint.slideshow.macroenabled.12",
+  "potm": "application/vnd.ms-powerpoint.template.macroenabled.12",
+  "mpp": "application/vnd.ms-project",
+  "mpt": "application/vnd.ms-project",
+  "docm": "application/vnd.ms-word.document.macroenabled.12",
+  "dotm": "application/vnd.ms-word.template.macroenabled.12",
+  "wps": "application/vnd.ms-works",
+  "wks": "application/vnd.ms-works",
+  "wcm": "application/vnd.ms-works",
+  "wdb": "application/vnd.ms-works",
+  "wpl": "application/vnd.ms-wpl",
+  "xps": "application/vnd.ms-xpsdocument",
+  "mseq": "application/vnd.mseq",
+  "mus": "application/vnd.musician",
+  "msty": "application/vnd.muvee.style",
+  "taglet": "application/vnd.mynfc",
+  "nlu": "application/vnd.neurolanguage.nlu",
+  "nim": "text/nim",
+  "nimble": "text/nimble",
+  "nimf": "text/nim",
+  "nims": "text/nim",
+  "ntf": "application/vnd.nitf",
+  "nitf": "application/vnd.nitf",
+  "nnd": "application/vnd.noblenet-directory",
+  "nns": "application/vnd.noblenet-sealer",
+  "nnw": "application/vnd.noblenet-web",
+  "ngdat": "application/vnd.nokia.n-gage.data",
+  "n-gage": "application/vnd.nokia.n-gage.symbian.install",
+  "rpst": "application/vnd.nokia.radio-preset",
+  "rpss": "application/vnd.nokia.radio-presets",
+  "edm": "application/vnd.novadigm.edm",
+  "edx": "application/vnd.novadigm.edx",
+  "ext": "application/vnd.novadigm.ext",
+  "odc": "application/vnd.oasis.opendocument.chart",
+  "otc": "application/vnd.oasis.opendocument.chart-template",
+  "odb": "application/vnd.oasis.opendocument.database",
+  "odf": "application/vnd.oasis.opendocument.formula",
+  "odft": "application/vnd.oasis.opendocument.formula-template",
+  "odg": "application/vnd.oasis.opendocument.graphics",
+  "otg": "application/vnd.oasis.opendocument.graphics-template",
+  "odi": "application/vnd.oasis.opendocument.image",
+  "oti": "application/vnd.oasis.opendocument.image-template",
+  "odp": "application/vnd.oasis.opendocument.presentation",
+  "otp": "application/vnd.oasis.opendocument.presentation-template",
+  "ods": "application/vnd.oasis.opendocument.spreadsheet",
+  "ots": "application/vnd.oasis.opendocument.spreadsheet-template",
+  "odt": "application/vnd.oasis.opendocument.text",
+  "odm": "application/vnd.oasis.opendocument.text-master",
+  "ott": "application/vnd.oasis.opendocument.text-template",
+  "oth": "application/vnd.oasis.opendocument.text-web",
+  "xo": "application/vnd.olpc-sugar",
+  "dd2": "application/vnd.oma.dd2+xml",
+  "oxt": "application/vnd.openofficeorg.extension",
+  "pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
+  "sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide",
+  "ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
+  "potx": "application/vnd.openxmlformats-officedocument.presentationml.template",
+  "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+  "xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
+  "docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+  "dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
+  "mgp": "application/vnd.osgeo.mapguide.package",
+  "dp": "application/vnd.osgi.dp",
+  "esa": "application/vnd.osgi.subsystem",
+  "pdb": "application/vnd.palm",
+  "pqa": "application/vnd.palm",
+  "oprc": "application/vnd.palm",
+  "paw": "application/vnd.pawaafile",
+  "str": "application/vnd.pg.format",
+  "ei6": "application/vnd.pg.osasli",
+  "efif": "application/vnd.picsel",
+  "wg": "application/vnd.pmi.widget",
+  "plf": "application/vnd.pocketlearn",
+  "pbd": "application/vnd.powerbuilder6",
+  "box": "application/vnd.previewsystems.box",
+  "mgz": "application/vnd.proteus.magazine",
+  "qps": "application/vnd.publishare-delta-tree",
+  "ptid": "application/vnd.pvi.ptid1",
+  "qxd": "application/vnd.quark.quarkxpress",
+  "qxt": "application/vnd.quark.quarkxpress",
+  "qwd": "application/vnd.quark.quarkxpress",
+  "qwt": "application/vnd.quark.quarkxpress",
+  "qxl": "application/vnd.quark.quarkxpress",
+  "qxb": "application/vnd.quark.quarkxpress",
+  "bed": "application/vnd.realvnc.bed",
+  "mxl": "application/vnd.recordare.musicxml",
+  "musicxml": "application/vnd.recordare.musicxml+xml",
+  "cryptonote": "application/vnd.rig.cryptonote",
+  "cod": "application/vnd.rim.cod",
+  "rm": "application/vnd.rn-realmedia",
+  "rmvb": "application/vnd.rn-realmedia-vbr",
+  "link66": "application/vnd.route66.link66+xml",
+  "st": "application/vnd.sailingtracker.track",
+  "see": "application/vnd.seemail",
+  "sema": "application/vnd.sema",
+  "semd": "application/vnd.semd",
+  "semf": "application/vnd.semf",
+  "ifm": "application/vnd.shana.informed.formdata",
+  "itp": "application/vnd.shana.informed.formtemplate",
+  "iif": "application/vnd.shana.informed.interchange",
+  "ipk": "application/vnd.shana.informed.package",
+  "twd": "application/vnd.simtech-mindmapper",
+  "twds": "application/vnd.simtech-mindmapper",
+  "mmf": "application/vnd.smaf",
+  "teacher": "application/vnd.smart.teacher",
+  "sdkm": "application/vnd.solent.sdkm+xml",
+  "sdkd": "application/vnd.solent.sdkm+xml",
+  "dxp": "application/vnd.spotfire.dxp",
+  "sfs": "application/vnd.spotfire.sfs",
+  "sdc": "application/vnd.stardivision.calc",
+  "sda": "application/vnd.stardivision.draw",
+  "sdd": "application/vnd.stardivision.impress",
+  "smf": "application/vnd.stardivision.math",
+  "sdw": "application/vnd.stardivision.writer",
+  "vor": "application/vnd.stardivision.writer",
+  "sgl": "application/vnd.stardivision.writer-global",
+  "smzip": "application/vnd.stepmania.package",
+  "sm": "application/vnd.stepmania.stepchart",
+  "sxc": "application/vnd.sun.xml.calc",
+  "stc": "application/vnd.sun.xml.calc.template",
+  "sxd": "application/vnd.sun.xml.draw",
+  "std": "application/vnd.sun.xml.draw.template",
+  "sxi": "application/vnd.sun.xml.impress",
+  "sti": "application/vnd.sun.xml.impress.template",
+  "sxm": "application/vnd.sun.xml.math",
+  "sxw": "application/vnd.sun.xml.writer",
+  "sxg": "application/vnd.sun.xml.writer.global",
+  "stw": "application/vnd.sun.xml.writer.template",
+  "sus": "application/vnd.sus-calendar",
+  "susp": "application/vnd.sus-calendar",
+  "svd": "application/vnd.svd",
+  "sis": "application/vnd.symbian.install",
+  "sisx": "application/vnd.symbian.install",
+  "xsm": "application/vnd.syncml+xml",
+  "bdm": "application/vnd.syncml.dm+wbxml",
+  "xdm": "application/vnd.syncml.dm+xml",
+  "tao": "application/vnd.tao.intent-module-archive",
+  "pcap": "application/vnd.tcpdump.pcap",
+  "cap": "application/vnd.tcpdump.pcap",
+  "dmp": "application/vnd.tcpdump.pcap",
+  "tmo": "application/vnd.tmobile-livetv",
+  "tpt": "application/vnd.trid.tpt",
+  "mxs": "application/vnd.triscape.mxs",
+  "tra": "application/vnd.trueapp",
+  "ufd": "application/vnd.ufdl",
+  "ufdl": "application/vnd.ufdl",
+  "utz": "application/vnd.uiq.theme",
+  "umj": "application/vnd.umajin",
+  "unityweb": "application/vnd.unity",
+  "uoml": "application/vnd.uoml+xml",
+  "vcx": "application/vnd.vcx",
+  "vsd": "application/vnd.visio",
+  "vst": "application/vnd.visio",
+  "vss": "application/vnd.visio",
+  "vsw": "application/vnd.visio",
+  "vis": "application/vnd.visionary",
+  "vsf": "application/vnd.vsf",
+  "wbxml": "application/vnd.wap.wbxml",
+  "wmlc": "application/vnd.wap.wmlc",
+  "wmlsc": "application/vnd.wap.wmlscriptc",
+  "wtb": "application/vnd.webturbo",
+  "nbp": "application/vnd.wolfram.player",
+  "wpd": "application/vnd.wordperfect",
+  "wqd": "application/vnd.wqd",
+  "stf": "application/vnd.wt.stf",
+  "xar": "application/vnd.xara",
+  "xfdl": "application/vnd.xfdl",
+  "hvd": "application/vnd.yamaha.hv-dic",
+  "hvs": "application/vnd.yamaha.hv-script",
+  "hvp": "application/vnd.yamaha.hv-voice",
+  "osf": "application/vnd.yamaha.openscoreformat",
+  "osfpvg": "application/vnd.yamaha.openscoreformat.osfpvg+xml",
+  "saf": "application/vnd.yamaha.smaf-audio",
+  "spf": "application/vnd.yamaha.smaf-phrase",
+  "cmp": "application/vnd.yellowriver-custom-menu",
+  "zir": "application/vnd.zul",
+  "zirz": "application/vnd.zul",
+  "zaz": "application/vnd.zzazz.deck+xml",
+  "vxml": "application/voicexml+xml",
+  "wasm": "application/wasm",
+  "wgt": "application/widget",
+  "hlp": "application/winhlp",
+  "wsdl": "application/wsdl+xml",
+  "wspolicy": "application/wspolicy+xml",
+  "7z": "application/x-7z-compressed",
+  "abw": "application/x-abiword",
+  "ace": "application/x-ace-compressed",
+  "dmg": "application/x-apple-diskimage",
+  "aab": "application/x-authorware-bin",
+  "x32": "application/x-authorware-bin",
+  "u32": "application/x-authorware-bin",
+  "vox": "application/x-authorware-bin",
+  "aam": "application/x-authorware-map",
+  "aas": "application/x-authorware-seg",
+  "bcpio": "application/x-bcpio",
+  "torrent": "application/x-bittorrent",
+  "blb": "application/x-blorb",
+  "blorb": "application/x-blorb",
+  "bz": "application/x-bzip",
+  "bz2": "application/x-bzip2",
+  "boz": "application/x-bzip2",
+  "cbr": "application/x-cbr",
+  "cba": "application/x-cbr",
+  "cbt": "application/x-cbr",
+  "cbz": "application/x-cbr",
+  "cb7": "application/x-cbr",
+  "vcd": "application/x-cdlink",
+  "cfs": "application/x-cfs-compressed",
+  "chat": "application/x-chat",
+  "pgn": "application/x-chess-pgn",
+  "nsc": "application/x-conference",
+  "cpio": "application/x-cpio",
+  "csh": "application/x-csh",
+  "deb": "application/x-debian-package",
+  "udeb": "application/x-debian-package",
+  "dgc": "application/x-dgc-compressed",
+  "dir": "application/x-director",
+  "dcr": "application/x-director",
+  "dxr": "application/x-director",
+  "cst": "application/x-director",
+  "cct": "application/x-director",
+  "cxt": "application/x-director",
+  "w3d": "application/x-director",
+  "fgd": "application/x-director",
+  "swa": "application/x-director",
+  "wad": "application/x-doom",
+  "ncx": "application/x-dtbncx+xml",
+  "dtb": "application/x-dtbook+xml",
+  "res": "application/x-dtbresource+xml",
+  "dvi": "application/x-dvi",
+  "evy": "application/x-envoy",
+  "eva": "application/x-eva",
+  "bdf": "application/x-font-bdf",
+  "gsf": "application/x-font-ghostscript",
+  "psf": "application/x-font-linux-psf",
+  "pcf": "application/x-font-pcf",
+  "snf": "application/x-font-snf",
+  "pfa": "application/x-font-type1",
+  "pfb": "application/x-font-type1",
+  "pfm": "application/x-font-type1",
+  "afm": "application/x-font-type1",
+  "arc": "application/x-freearc",
+  "spl": "application/x-futuresplash",
+  "gca": "application/x-gca-compressed",
+  "ulx": "application/x-glulx",
+  "gnumeric": "application/x-gnumeric",
+  "gramps": "application/x-gramps-xml",
+  "gtar": "application/x-gtar",
+  "hdf": "application/x-hdf",
+  "install": "application/x-install-instructions",
+  "iso": "application/x-iso9660-image",
+  "jnlp": "application/x-java-jnlp-file",
+  "latex": "application/x-latex",
+  "lzh": "application/x-lzh-compressed",
+  "lha": "application/x-lzh-compressed",
+  "mie": "application/x-mie",
+  "prc": "application/x-mobipocket-ebook",
+  "mobi": "application/x-mobipocket-ebook",
+  "application": "application/x-ms-application",
+  "lnk": "application/x-ms-shortcut",
+  "wmd": "application/x-ms-wmd",
+  "wmz": "application/x-ms-wmz",
+  "xbap": "application/x-ms-xbap",
+  "mdb": "application/x-msaccess",
+  "obd": "application/x-msbinder",
+  "crd": "application/x-mscardfile",
+  "clp": "application/x-msclip",
+  "exe": "application/x-msdownload",
+  "dll": "application/x-msdownload",
+  "com": "application/x-msdownload",
+  "bat": "application/x-msdownload",
+  "msi": "application/x-msdownload",
+  "mvb": "application/x-msmediaview",
+  "m13": "application/x-msmediaview",
+  "m14": "application/x-msmediaview",
+  "wmf": "application/x-msmetafile",
+  "wmz": "application/x-msmetafile",
+  "emf": "application/x-msmetafile",
+  "emz": "application/x-msmetafile",
+  "mny": "application/x-msmoney",
+  "pub": "application/x-mspublisher",
+  "scd": "application/x-msschedule",
+  "trm": "application/x-msterminal",
+  "wri": "application/x-mswrite",
+  "nc": "application/x-netcdf",
+  "cdf": "application/x-netcdf",
+  "nzb": "application/x-nzb",
+  "p12": "application/x-pkcs12",
+  "pfx": "application/x-pkcs12",
+  "p7b": "application/x-pkcs7-certificates",
+  "spc": "application/x-pkcs7-certificates",
+  "p7r": "application/x-pkcs7-certreqresp",
+  "rar": "application/x-rar-compressed",
+  "ris": "application/x-research-info-systems",
+  "sh": "application/x-sh",
+  "shar": "application/x-shar",
+  "swf": "application/x-shockwave-flash",
+  "xap": "application/x-silverlight-app",
+  "sql": "application/x-sql",
+  "sit": "application/x-stuffit",
+  "sitx": "application/x-stuffitx",
+  "srt": "application/x-subrip",
+  "sv4cpio": "application/x-sv4cpio",
+  "sv4crc": "application/x-sv4crc",
+  "t3": "application/x-t3vm-image",
+  "gam": "application/x-tads",
+  "tar": "application/x-tar",
+  "tcl": "application/x-tcl",
+  "tex": "application/x-tex",
+  "tfm": "application/x-tex-tfm",
+  "texinfo": "application/x-texinfo",
+  "texi": "application/x-texinfo",
+  "obj": "application/x-tgif",
+  "ustar": "application/x-ustar",
+  "src": "application/x-wais-source",
+  "der": "application/x-x509-ca-cert",
+  "crt": "application/x-x509-ca-cert",
+  "fig": "application/x-xfig",
+  "xlf": "application/x-xliff+xml",
+  "xpi": "application/x-xpinstall",
+  "xz": "application/x-xz",
+  "z1": "application/x-zmachine",
+  "z2": "application/x-zmachine",
+  "z3": "application/x-zmachine",
+  "z4": "application/x-zmachine",
+  "z5": "application/x-zmachine",
+  "z6": "application/x-zmachine",
+  "z7": "application/x-zmachine",
+  "z8": "application/x-zmachine",
+  "xaml": "application/xaml+xml",
+  "xdf": "application/xcap-diff+xml",
+  "xenc": "application/xenc+xml",
+  "xhtml": "application/xhtml+xml",
+  "xht": "application/xhtml+xml",
+  "xml": "application/xml",
+  "xsl": "application/xml",
+  "dtd": "application/xml-dtd",
+  "xop": "application/xop+xml",
+  "xpl": "application/xproc+xml",
+  "xslt": "application/xslt+xml",
+  "xspf": "application/xspf+xml",
+  "mxml": "application/xv+xml",
+  "xhvml": "application/xv+xml",
+  "xvml": "application/xv+xml",
+  "xvm": "application/xv+xml",
+  "yang": "application/yang",
+  "yin": "application/yin+xml",
+  "zip": "application/zip",
+  "adp": "audio/adpcm",
+  "au": "audio/basic",
+  "snd": "audio/basic",
+  "mid": "audio/midi",
+  "midi": "audio/midi",
+  "kar": "audio/midi",
+  "rmi": "audio/midi",
+  "m4a": "audio/mp4",
+  "mp4a": "audio/mp4",
+  "mpga": "audio/mpeg",
+  "mp2": "audio/mpeg",
+  "mp2a": "audio/mpeg",
+  "mp3": "audio/mpeg",
+  "m2a": "audio/mpeg",
+  "m3a": "audio/mpeg",
+  "oga": "audio/ogg",
+  "ogg": "audio/ogg",
+  "spx": "audio/ogg",
+  "opus": "audio/ogg",
+  "s3m": "audio/s3m",
+  "sil": "audio/silk",
+  "uva": "audio/vnd.dece.audio",
+  "uvva": "audio/vnd.dece.audio",
+  "eol": "audio/vnd.digital-winds",
+  "dra": "audio/vnd.dra",
+  "dts": "audio/vnd.dts",
+  "dtshd": "audio/vnd.dts.hd",
+  "lvp": "audio/vnd.lucent.voice",
+  "pya": "audio/vnd.ms-playready.media.pya",
+  "ecelp4800": "audio/vnd.nuera.ecelp4800",
+  "ecelp7470": "audio/vnd.nuera.ecelp7470",
+  "ecelp9600": "audio/vnd.nuera.ecelp9600",
+  "rip": "audio/vnd.rip",
+  "weba": "audio/webm",
+  "aac": "audio/x-aac",
+  "aif": "audio/x-aiff",
+  "aiff": "audio/x-aiff",
+  "aifc": "audio/x-aiff",
+  "caf": "audio/x-caf",
+  "flac": "audio/x-flac",
+  "mka": "audio/x-matroska",
+  "m3u": "audio/x-mpegurl",
+  "wax": "audio/x-ms-wax",
+  "wma": "audio/x-ms-wma",
+  "ram": "audio/x-pn-realaudio",
+  "ra": "audio/x-pn-realaudio",
+  "rmp": "audio/x-pn-realaudio-plugin",
+  "wav": "audio/x-wav",
+  "xm": "audio/xm",
+  "cdx": "chemical/x-cdx",
+  "cif": "chemical/x-cif",
+  "cmdf": "chemical/x-cmdf",
+  "cml": "chemical/x-cml",
+  "csml": "chemical/x-csml",
+  "xyz": "chemical/x-xyz",
+  "ttc": "font/collection",
+  "otf": "font/otf",
+  "ttf": "font/ttf",
+  "woff": "font/woff",
+  "woff2": "font/woff2",
+  "bmp": "image/bmp",
+  "cgm": "image/cgm",
+  "g3": "image/g3fax",
+  "gif": "image/gif",
+  "ief": "image/ief",
+  "jpeg": "image/jpeg",
+  "jpg": "image/jpeg",
+  "jpe": "image/jpeg",
+  "ktx": "image/ktx",
+  "png": "image/png",
+  "btif": "image/prs.btif",
+  "sgi": "image/sgi",
+  "svg": "image/svg+xml",
+  "svgz": "image/svg+xml",
+  "tiff": "image/tiff",
+  "tif": "image/tiff",
+  "psd": "image/vnd.adobe.photoshop",
+  "uvi": "image/vnd.dece.graphic",
+  "uvvi": "image/vnd.dece.graphic",
+  "uvg": "image/vnd.dece.graphic",
+  "uvvg": "image/vnd.dece.graphic",
+  "djvu": "image/vnd.djvu",
+  "djv": "image/vnd.djvu",
+  "sub": "image/vnd.dvb.subtitle",
+  "dwg": "image/vnd.dwg",
+  "dxf": "image/vnd.dxf",
+  "fbs": "image/vnd.fastbidsheet",
+  "fpx": "image/vnd.fpx",
+  "fst": "image/vnd.fst",
+  "mmr": "image/vnd.fujixerox.edmics-mmr",
+  "rlc": "image/vnd.fujixerox.edmics-rlc",
+  "mdi": "image/vnd.ms-modi",
+  "wdp": "image/vnd.ms-photo",
+  "npx": "image/vnd.net-fpx",
+  "wbmp": "image/vnd.wap.wbmp",
+  "xif": "image/vnd.xiff",
+  "webp": "image/webp",
+  "3ds": "image/x-3ds",
+  "ras": "image/x-cmu-raster",
+  "cmx": "image/x-cmx",
+  "fh": "image/x-freehand",
+  "fhc": "image/x-freehand",
+  "fh4": "image/x-freehand",
+  "fh5": "image/x-freehand",
+  "fh7": "image/x-freehand",
+  "ico": "image/x-icon",
+  "sid": "image/x-mrsid-image",
+  "pcx": "image/x-pcx",
+  "pic": "image/x-pict",
+  "pct": "image/x-pict",
+  "pnm": "image/x-portable-anymap",
+  "pbm": "image/x-portable-bitmap",
+  "pgm": "image/x-portable-graymap",
+  "ppm": "image/x-portable-pixmap",
+  "rgb": "image/x-rgb",
+  "tga": "image/x-tga",
+  "xbm": "image/x-xbitmap",
+  "xpm": "image/x-xpixmap",
+  "xwd": "image/x-xwindowdump",
+  "eml": "message/rfc822",
+  "mime": "message/rfc822",
+  "igs": "model/iges",
+  "iges": "model/iges",
+  "msh": "model/mesh",
+  "mesh": "model/mesh",
+  "silo": "model/mesh",
+  "dae": "model/vnd.collada+xml",
+  "dwf": "model/vnd.dwf",
+  "gdl": "model/vnd.gdl",
+  "gtw": "model/vnd.gtw",
+  "mts": "model/vnd.mts",
+  "vtu": "model/vnd.vtu",
+  "wrl": "model/vrml",
+  "vrml": "model/vrml",
+  "x3db": "model/x3d+binary",
+  "x3dbz": "model/x3d+binary",
+  "x3dv": "model/x3d+vrml",
+  "x3dvz": "model/x3d+vrml",
+  "x3d": "model/x3d+xml",
+  "x3dz": "model/x3d+xml",
+  "appcache": "text/cache-manifest",
+  "ics": "text/calendar",
+  "ifb": "text/calendar",
+  "css": "text/css",
+  "csv": "text/csv",
+  "html": "text/html",
+  "htm": "text/html",
+  "js": "text/javascript",
+  "mjs": "text/javascript",
+  "n3": "text/n3",
+  "txt": "text/plain",
+  "text": "text/plain",
+  "conf": "text/plain",
+  "def": "text/plain",
+  "list": "text/plain",
+  "log": "text/plain",
+  "in": "text/plain",
+  "dsc": "text/prs.lines.tag",
+  "rtx": "text/richtext",
+  "sgml": "text/sgml",
+  "sgm": "text/sgml",
+  "tsv": "text/tab-separated-values",
+  "t": "text/troff",
+  "tr": "text/troff",
+  "roff": "text/troff",
+  "man": "text/troff",
+  "me": "text/troff",
+  "ms": "text/troff",
+  "ttl": "text/turtle",
+  "uri": "text/uri-list",
+  "uris": "text/uri-list",
+  "urls": "text/uri-list",
+  "vcard": "text/vcard",
+  "curl": "text/vnd.curl",
+  "dcurl": "text/vnd.curl.dcurl",
+  "mcurl": "text/vnd.curl.mcurl",
+  "scurl": "text/vnd.curl.scurl",
+  "sub": "text/vnd.dvb.subtitle",
+  "fly": "text/vnd.fly",
+  "flx": "text/vnd.fmi.flexstor",
+  "gv": "text/vnd.graphviz",
+  "3dml": "text/vnd.in3d.3dml",
+  "spot": "text/vnd.in3d.spot",
+  "jad": "text/vnd.sun.j2me.app-descriptor",
+  "wml": "text/vnd.wap.wml",
+  "wmls": "text/vnd.wap.wmlscript",
+  "s": "text/x-asm",
+  "asm": "text/x-asm",
+  "c": "text/x-c",
+  "cc": "text/x-c",
+  "cxx": "text/x-c",
+  "cpp": "text/x-c",
+  "h": "text/x-c",
+  "hh": "text/x-c",
+  "dic": "text/x-c",
+  "f": "text/x-fortran",
+  "for": "text/x-fortran",
+  "f77": "text/x-fortran",
+  "f90": "text/x-fortran",
+  "java": "text/x-java-source",
+  "nfo": "text/x-nfo",
+  "opml": "text/x-opml",
+  "p": "text/x-pascal",
+  "pas": "text/x-pascal",
+  "etx": "text/x-setext",
+  "sfv": "text/x-sfv",
+  "uu": "text/x-uuencode",
+  "vcs": "text/x-vcalendar",
+  "vcf": "text/x-vcard",
+  "3gp": "video/3gpp",
+  "3g2": "video/3gpp2",
+  "h261": "video/h261",
+  "h263": "video/h263",
+  "h264": "video/h264",
+  "jpgv": "video/jpeg",
+  "jpm": "video/jpm",
+  "jpgm": "video/jpm",
+  "mj2": "video/mj2",
+  "mjp2": "video/mj2",
+  "mp4": "video/mp4",
+  "mp4v": "video/mp4",
+  "mpg4": "video/mp4",
+  "mpeg": "video/mpeg",
+  "mpg": "video/mpeg",
+  "mpe": "video/mpeg",
+  "m1v": "video/mpeg",
+  "m2v": "video/mpeg",
+  "ogv": "video/ogg",
+  "qt": "video/quicktime",
+  "mov": "video/quicktime",
+  "uvh": "video/vnd.dece.hd",
+  "uvvh": "video/vnd.dece.hd",
+  "uvm": "video/vnd.dece.mobile",
+  "uvvm": "video/vnd.dece.mobile",
+  "uvp": "video/vnd.dece.pd",
+  "uvvp": "video/vnd.dece.pd",
+  "uvs": "video/vnd.dece.sd",
+  "uvvs": "video/vnd.dece.sd",
+  "uvv": "video/vnd.dece.video",
+  "uvvv": "video/vnd.dece.video",
+  "dvb": "video/vnd.dvb.file",
+  "fvt": "video/vnd.fvt",
+  "mxu": "video/vnd.mpegurl",
+  "m4u": "video/vnd.mpegurl",
+  "pyv": "video/vnd.ms-playready.media.pyv",
+  "uvu": "video/vnd.uvvu.mp4",
+  "uvvu": "video/vnd.uvvu.mp4",
+  "viv": "video/vnd.vivo",
+  "webm": "video/webm",
+  "f4v": "video/x-f4v",
+  "fli": "video/x-fli",
+  "flv": "video/x-flv",
+  "m4v": "video/x-m4v",
+  "mkv": "video/x-matroska",
+  "mk3d": "video/x-matroska",
+  "mks": "video/x-matroska",
+  "mng": "video/x-mng",
+  "asf": "video/x-ms-asf",
+  "asx": "video/x-ms-asf",
+  "vob": "video/x-ms-vob",
+  "wm": "video/x-ms-wm",
+  "wmv": "video/x-ms-wmv",
+  "wmx": "video/x-ms-wmx",
+  "wvx": "video/x-ms-wvx",
+  "avi": "video/x-msvideo",
+  "movie": "video/x-sgi-movie",
+  "smv": "video/x-smv",
+  "ice": "x-conference/x-cooltalk",
+}
 
-from strutils import startsWith
 
-proc newMimetypes*(): MimeDB =
+func newMimetypes*(): MimeDB =
   ## Creates a new Mimetypes database. The database will contain the most
   ## common mimetypes.
-  result.mimes = mimes.newStringTable()
+  {.cast(noSideEffect).}:
+    result.mimes = mimes.toOrderedTable()
 
-proc getMimetype*(mimedb: MimeDB, ext: string, default = "text/plain"): string =
-  ## Gets mimetype which corresponds to ``ext``. Returns ``default`` if ``ext``
-  ## could not be found. ``ext`` can start with an optional dot which is ignored.
+func getMimetype*(mimedb: MimeDB, ext: string, default = "text/plain"): string =
+  ## Gets mimetype which corresponds to `ext`. Returns `default` if `ext`
+  ## could not be found. `ext` can start with an optional dot which is ignored.
+  ## `ext` is lowercased before querying `mimedb`.
   if ext.startsWith("."):
-    result = mimedb.mimes.getOrDefault(ext.substr(1))
+    result = mimedb.mimes.getOrDefault(ext.toLowerAscii.substr(1))
   else:
-    result = mimedb.mimes.getOrDefault(ext)
+    result = mimedb.mimes.getOrDefault(ext.toLowerAscii())
   if result == "":
     return default
 
-proc getExt*(mimedb: MimeDB, mimetype: string, default = "txt"): string =
-  ## Gets extension which corresponds to ``mimetype``. Returns ``default`` if
-  ## ``mimetype`` could not be found. Extensions are returned without the
-  ## leading dot.
+func getExt*(mimedb: MimeDB, mimetype: string, default = "txt"): string =
+  ## Gets extension which corresponds to `mimetype`. Returns `default` if
+  ## `mimetype` could not be found. Extensions are returned without the
+  ## leading dot. `mimetype` is lowercased before querying `mimedb`.
   result = default
+  let mimeLowered = mimetype.toLowerAscii()
   for e, m in mimedb.mimes:
-    if m == mimetype:
+    if m == mimeLowered:
       result = e
+      break
 
-proc register*(mimedb: var MimeDB, ext: string, mimetype: string) =
-  ## Adds ``mimetype`` to the ``mimedb``.
-  mimedb.mimes[ext] = mimetype
-
-when isMainModule:
-  var m = newMimetypes()
-  assert m.getMimetype("mp4") == "video/mp4"
-  assert m.getExt("text/html") == "html"
+func register*(mimedb: var MimeDB, ext: string, mimetype: string) =
+  ## Adds `mimetype` to the `mimedb`.
+  ## `mimetype` and `ext` are lowercased before registering on `mimedb`.
+  assert ext.strip.len > 0, "ext argument can not be empty string"
+  assert mimetype.strip.len > 0, "mimetype argument can not be empty string"
+  {.noSideEffect.}:
+    mimedb.mimes[ext.toLowerAscii()] = mimetype.toLowerAscii()
diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim
index 5545ca2d1..656c98a20 100644
--- a/lib/pure/nativesockets.nim
+++ b/lib/pure/nativesockets.nim
@@ -8,33 +8,43 @@
 #
 
 ## This module implements a low-level cross-platform sockets interface. Look
-## at the ``net`` module for the higher-level version.
+## at the `net` module for the higher-level version.
 
 # TODO: Clean up the exports a bit and everything else in general.
 
-import os, options
+import std/[os, options]
+import std/private/since
+import std/strbasics
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
 
 when hostOS == "solaris":
   {.passl: "-lsocket -lnsl".}
 
-const useWinVersion = defined(Windows) or defined(nimdoc)
+const useWinVersion = defined(windows) or defined(nimdoc)
+const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) or
+    defined(nuttx)
 
 when useWinVersion:
-  import winlean
+  import std/winlean
   export WSAEWOULDBLOCK, WSAECONNRESET, WSAECONNABORTED, WSAENETRESET,
          WSANOTINITIALISED, WSAENOTSOCK, WSAEINPROGRESS, WSAEINTR,
          WSAEDISCON, ERROR_NETNAME_DELETED
 else:
-  import posix
+  import std/posix
   export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL,
     EINTR, EINPROGRESS, ECONNRESET, EPIPE, ENETRESET, EBADF
   export Sockaddr_storage, Sockaddr_un, Sockaddr_un_path_length
 
 export SocketHandle, Sockaddr_in, Addrinfo, INADDR_ANY, SockAddr, SockLen,
   Sockaddr_in6, Sockaddr_storage,
-  inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto,
+  recv, `==`, connect, send, accept, recvfrom, sendto,
   freeAddrInfo
 
+when not useNimNetLite:
+  export inet_ntoa
+
 export
   SO_ERROR,
   SOL_SOCKET,
@@ -44,19 +54,20 @@ export
   MSG_PEEK
 
 when defined(macosx) and not defined(nimdoc):
-    export SO_NOSIGPIPE
+  export SO_NOSIGPIPE
 
 type
-  Port* = distinct uint16  ## port type
-
-  Domain* = enum    ## domain, which specifies the protocol family of the
-                    ## created socket. Other domains than those that are listed
-                    ## here are unsupported.
-    AF_UNSPEC = 0,  ## unspecified domain (can be detected automatically by
-                    ## some procedures, such as getaddrinfo)
-    AF_UNIX = 1,    ## for local socket (using a file). Unsupported on Windows.
-    AF_INET = 2,    ## for network protocol IPv4 or
-    AF_INET6 = when defined(macosx): 30 else: 23   ## for network protocol IPv6.
+  Port* = distinct uint16 ## port type
+
+  Domain* = enum ## \
+    ## domain, which specifies the protocol family of the
+    ## created socket. Other domains than those that are listed
+    ## here are unsupported.
+    AF_UNSPEC = 0, ## unspecified domain (can be detected automatically by
+                   ## some procedures, such as getaddrinfo)
+    AF_UNIX = 1,   ## for local socket (using a file). Unsupported on Windows.
+    AF_INET = 2,   ## for network protocol IPv4 or
+    AF_INET6 = when defined(macosx): 30 elif defined(windows): 23 else: 10 ## for network protocol IPv6.
 
   SockType* = enum     ## second argument to `socket` proc
     SOCK_STREAM = 1,   ## reliable stream-oriented service or Stream Sockets
@@ -64,13 +75,14 @@ type
     SOCK_RAW = 3,      ## raw protocols atop the network layer.
     SOCK_SEQPACKET = 5 ## reliable sequenced packet service
 
-  Protocol* = enum      ## third argument to `socket` proc
-    IPPROTO_TCP = 6,    ## Transmission control protocol.
-    IPPROTO_UDP = 17,   ## User datagram protocol.
-    IPPROTO_IP,         ## Internet protocol. Unsupported on Windows.
-    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.
+  Protocol* = enum    ## third argument to `socket` proc
+    IPPROTO_TCP = 6,  ## Transmission control protocol.
+    IPPROTO_UDP = 17, ## User datagram protocol.
+    IPPROTO_IP,       ## Internet protocol.
+    IPPROTO_IPV6,     ## Internet Protocol Version 6.
+    IPPROTO_RAW,      ## Raw IP Packets Protocol. Unsupported on Windows.
+    IPPROTO_ICMP      ## Internet Control message protocol.
+    IPPROTO_ICMPV6    ## Internet Control message protocol for IPv6.
 
   Servent* = object ## information about a service
     name*: string
@@ -85,6 +97,8 @@ type
     length*: int
     addrList*: seq[string]
 
+const IPPROTO_NONE* = IPPROTO_IP ## Use this if your socket type requires a protocol value of zero (e.g. Unix sockets).
+
 when useWinVersion:
   let
     osInvalidSocket* = winlean.INVALID_SOCKET
@@ -93,7 +107,7 @@ when useWinVersion:
     IOCPARM_MASK* = 127
     IOC_IN* = int(-2147483648)
     FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or
-                             (102 shl 8) or 126
+               (102 shl 8) or 126
     nativeAfInet = winlean.AF_INET
     nativeAfInet6 = winlean.AF_INET6
 
@@ -105,72 +119,88 @@ else:
     osInvalidSocket* = posix.INVALID_SOCKET
     nativeAfInet = posix.AF_INET
     nativeAfInet6 = posix.AF_INET6
+    nativeAfUnix = posix.AF_UNIX
 
 proc `==`*(a, b: Port): bool {.borrow.}
-  ## ``==`` for ports.
+  ## `==` for ports.
 
 proc `$`*(p: Port): string {.borrow.}
-  ## returns the port number as a string
+  ## Returns the port number as a string
 
-proc toInt*(domain: Domain): cshort
-  ## Converts the Domain enum to a platform-dependent ``cint``.
+proc toInt*(domain: Domain): cint
+  ## Converts the Domain enum to a platform-dependent `cint`.
 
 proc toInt*(typ: SockType): cint
-  ## Converts the SockType enum to a platform-dependent ``cint``.
+  ## Converts the SockType enum to a platform-dependent `cint`.
 
 proc toInt*(p: Protocol): cint
-  ## Converts the Protocol enum to a platform-dependent ``cint``.
+  ## Converts the Protocol enum to a platform-dependent `cint`.
 
 when not useWinVersion:
-  proc toInt(domain: Domain): cshort =
+  proc toInt(domain: Domain): cint =
     case domain
-    of AF_UNSPEC:      result = posix.AF_UNSPEC.cshort
-    of AF_UNIX:        result = posix.AF_UNIX.cshort
-    of AF_INET:        result = posix.AF_INET.cshort
-    of AF_INET6:       result = posix.AF_INET6.cshort
+    of AF_UNSPEC: result = posix.AF_UNSPEC.cint
+    of AF_UNIX: result = posix.AF_UNIX.cint
+    of AF_INET: result = posix.AF_INET.cint
+    of AF_INET6: result = posix.AF_INET6.cint
 
   proc toKnownDomain*(family: cint): Option[Domain] =
-    ## Converts the platform-dependent ``cint`` to the Domain or none(),
-    ## if the ``cint`` is not known.
-    result = if   family == posix.AF_UNSPEC: some(Domain.AF_UNSPEC)
-             elif family == posix.AF_UNIX:   some(Domain.AF_UNIX)
-             elif family == posix.AF_INET:   some(Domain.AF_INET)
-             elif family == posix.AF_INET6:  some(Domain.AF_INET6)
+    ## Converts the platform-dependent `cint` to the Domain or none(),
+    ## if the `cint` is not known.
+    result = if family == posix.AF_UNSPEC: some(Domain.AF_UNSPEC)
+             elif family == posix.AF_UNIX: some(Domain.AF_UNIX)
+             elif family == posix.AF_INET: some(Domain.AF_INET)
+             elif family == posix.AF_INET6: some(Domain.AF_INET6)
              else: none(Domain)
 
   proc toInt(typ: SockType): cint =
     case typ
-    of SOCK_STREAM:    result = posix.SOCK_STREAM
-    of SOCK_DGRAM:     result = posix.SOCK_DGRAM
+    of SOCK_STREAM: result = posix.SOCK_STREAM
+    of SOCK_DGRAM: result = posix.SOCK_DGRAM
     of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
-    of SOCK_RAW:       result = posix.SOCK_RAW
+    of SOCK_RAW: result = posix.SOCK_RAW
 
   proc toInt(p: Protocol): cint =
     case p
-    of IPPROTO_TCP:    result = posix.IPPROTO_TCP
-    of IPPROTO_UDP:    result = posix.IPPROTO_UDP
-    of IPPROTO_IP:     result = posix.IPPROTO_IP
-    of IPPROTO_IPV6:   result = posix.IPPROTO_IPV6
-    of IPPROTO_RAW:    result = posix.IPPROTO_RAW
-    of IPPROTO_ICMP:   result = posix.IPPROTO_ICMP
+    of IPPROTO_TCP: result = posix.IPPROTO_TCP
+    of IPPROTO_UDP: result = posix.IPPROTO_UDP
+    of IPPROTO_IP: result = posix.IPPROTO_IP
+    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): cshort =
-    result = toU16(ord(domain))
+  proc toInt(domain: Domain): cint =
+    result = cast[cint](uint32(ord(domain)))
 
   proc toKnownDomain*(family: cint): Option[Domain] =
-    ## Converts the platform-dependent ``cint`` to the Domain or none(),
-    ## if the ``cint`` is not known.
-    result = if   family == winlean.AF_UNSPEC: some(Domain.AF_UNSPEC)
-             elif family == winlean.AF_INET:   some(Domain.AF_INET)
-             elif family == winlean.AF_INET6:  some(Domain.AF_INET6)
+    ## Converts the platform-dependent `cint` to the Domain or none(),
+    ## if the `cint` is not known.
+    result = if family == winlean.AF_UNSPEC: some(Domain.AF_UNSPEC)
+             elif family == winlean.AF_INET: some(Domain.AF_INET)
+             elif family == winlean.AF_INET6: some(Domain.AF_INET6)
              else: none(Domain)
 
   proc toInt(typ: SockType): cint =
     result = cint(ord(typ))
 
   proc toInt(p: Protocol): cint =
-    result = cint(ord(p))
+    case p
+    of IPPROTO_IP:
+      result = 0.cint
+    of IPPROTO_ICMP:
+      result = 1.cint
+    of IPPROTO_TCP:
+      result = 6.cint
+    of IPPROTO_UDP:
+      result = 17.cint
+    of IPPROTO_IPV6:
+      result = 41.cint
+    of IPPROTO_ICMPV6:
+      result = 58.cint
+    else:
+      result = cint(ord(p))
 
 proc toSockType*(protocol: Protocol): SockType =
   result = case protocol
@@ -178,58 +208,77 @@ 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,
-                      sockType: SockType = SOCK_STREAM,
-                      protocol: Protocol = IPPROTO_TCP): SocketHandle =
-  ## Creates a new socket; returns `osInvalidSocket` if an error occurs.
-  socket(toInt(domain), toInt(sockType), toInt(protocol))
-
-proc createNativeSocket*(domain: cint, sockType: cint,
-                      protocol: cint): SocketHandle =
-  ## Creates a new socket; returns `osInvalidSocket` if an error occurs.
-  ##
-  ## Use this overload if one of the enums specified above does
-  ## not contain what you need.
-  socket(domain, sockType, protocol)
+proc getProtoByName*(name: string): int {.since: (1, 3, 5).} =
+  ## Returns a protocol code from the database that matches the protocol `name`.
+  when useWinVersion:
+    let protoent = winlean.getprotobyname(name.cstring)
+  else:
+    let protoent = posix.getprotobyname(name.cstring)
 
-proc newNativeSocket*(domain: Domain = AF_INET,
-                      sockType: SockType = SOCK_STREAM,
-                      protocol: Protocol = IPPROTO_TCP): SocketHandle
-                      {.deprecated.} =
-  ## Creates a new socket; returns `osInvalidSocket` if an error occurs.
-  ##
-  ## **Deprecated since v0.18.0:** Use ``createNativeSocket`` instead.
-  createNativeSocket(domain, sockType, protocol)
+  if protoent == nil:
+    raise newException(OSError, "protocol not found: " & name)
 
-proc newNativeSocket*(domain: cint, sockType: cint,
-                      protocol: cint): SocketHandle
-                      {.deprecated.} =
-  ## Creates a new socket; returns `osInvalidSocket` if an error occurs.
-  ##
-  ## Use this overload if one of the enums specified above does
-  ## not contain what you need.
-  ##
-  ## **Deprecated since v0.18.0:** Use ``createNativeSocket`` instead.
-  createNativeSocket(domain, sockType, protocol)
+  result = protoent.p_proto.int
 
 proc close*(socket: SocketHandle) =
-  ## closes a socket.
+  ## Closes a socket.
   when useWinVersion:
     discard winlean.closesocket(socket)
   else:
     discard posix.close(socket)
-  # TODO: These values should not be discarded. An EOS should be raised.
+  # TODO: These values should not be discarded. An OSError should be raised.
   # http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
 
-proc bindAddr*(socket: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint =
+when declared(setInheritable) or defined(nimdoc):
+  proc setInheritable*(s: SocketHandle, inheritable: bool): bool {.inline.} =
+    ## Set whether a socket is inheritable by child processes. Returns `true`
+    ## on success.
+    ##
+    ## This function is not implemented on all platform, test for availability
+    ## with `declared() <system.html#declared,untyped>`.
+    setInheritable(FileHandle s, inheritable)
+
+proc createNativeSocket*(domain: cint, sockType: cint, protocol: cint,
+                         inheritable: bool = defined(nimInheritHandles)): SocketHandle =
+  ## Creates a new socket; returns `osInvalidSocket` if an error occurs.
+  ##
+  ## `inheritable` decides if the resulting SocketHandle can be inherited
+  ## by child processes.
+  ##
+  ## Use this overload if one of the enums specified above does
+  ## not contain what you need.
+  let sockType =
+    when (defined(linux) or defined(bsd)) and not defined(nimdoc):
+      if inheritable: sockType and not SOCK_CLOEXEC else: sockType or SOCK_CLOEXEC
+    else:
+      sockType
+  result = socket(domain, sockType, protocol)
+  when declared(setInheritable) and not (defined(linux) or defined(bsd)):
+    if not setInheritable(result, inheritable):
+      close result
+      return osInvalidSocket
+
+proc createNativeSocket*(domain: Domain = AF_INET,
+                         sockType: SockType = SOCK_STREAM,
+                         protocol: Protocol = IPPROTO_TCP,
+                         inheritable: bool = defined(nimInheritHandles)): SocketHandle =
+  ## Creates a new socket; returns `osInvalidSocket` if an error occurs.
+  ##
+  ## `inheritable` decides if the resulting SocketHandle can be inherited
+  ## by child processes.
+  createNativeSocket(toInt(domain), toInt(sockType), toInt(protocol), inheritable)
+
+proc bindAddr*(socket: SocketHandle, name: ptr SockAddr,
+    namelen: SockLen): cint =
   result = bindSocket(socket, name, namelen)
 
-proc listen*(socket: SocketHandle, backlog = SOMAXCONN): cint {.tags: [ReadIOEffect].} =
-  ## Marks ``socket`` as accepting connections.
-  ## ``Backlog`` specifies the maximum length of the
+proc listen*(socket: SocketHandle, backlog = SOMAXCONN): cint {.tags: [
+    ReadIOEffect].} =
+  ## Marks `socket` as accepting connections.
+  ## `Backlog` specifies the maximum length of the
   ## queue of pending connections.
   when useWinVersion:
     result = winlean.listen(socket, cint(backlog))
@@ -241,47 +290,37 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET,
                   protocol: Protocol = IPPROTO_TCP): ptr AddrInfo =
   ##
   ##
-  ## **Warning**: The resulting ``ptr AddrInfo`` must be freed using ``freeAddrInfo``!
+  ## .. warning:: The resulting `ptr AddrInfo` must be freed using `freeAddrInfo`!
   var hints: AddrInfo
   result = nil
   hints.ai_family = toInt(domain)
   hints.ai_socktype = toInt(sockType)
   hints.ai_protocol = toInt(protocol)
   # OpenBSD doesn't support AI_V4MAPPED and doesn't define the macro AI_V4MAPPED.
-  # FreeBSD doesn't support AI_V4MAPPED but defines the macro.
+  # FreeBSD, Haiku don't support AI_V4MAPPED but defines the macro.
   # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198092
-  when not defined(freebsd) and not defined(openbsd) and not defined(netbsd) and not defined(android):
+  # https://dev.haiku-os.org/ticket/14323
+  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 socketPort = if sockType == SOCK_RAW: "" else: $port
+  var gaiResult = getaddrinfo(address, socketPort.cstring, addr(hints), result)
   if gaiResult != 0'i32:
-    when useWinVersion:
+    when useWinVersion or defined(freertos) or defined(nuttx):
       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,
   ## this is a no-op; otherwise, it performs a 4-byte swap operation.
   when cpuEndian == bigEndian: result = x
   else: result = (x shr 24'u32) or
-                 (x shr 8'u32 and 0xff00'u32) or
-                 (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)))
+                  (x shr 8'u32 and 0xff00'u32) or
+                  (x shl 8'u32 and 0xff0000'u32) or
+                  (x shl 24'u32)
 
 proc ntohs*(x: uint16): uint16 =
   ## Converts 16-bit unsigned integers from network to host byte order. On
@@ -290,290 +329,408 @@ 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
   ## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
   nativesockets.ntohs(x)
 
-proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} =
-  ## Searches the database from the beginning and finds the first entry for
-  ## which the service name specified by ``name`` matches the s_name member
-  ## and the protocol name specified by ``proto`` matches the s_proto member.
-  ##
-  ## On posix this will search through the ``/etc/services`` file.
-  when useWinVersion:
-    var s = winlean.getservbyname(name, proto)
-  else:
-    var s = posix.getservbyname(name, proto)
-  if s == nil: raiseOSError(osLastError(), "Service not found.")
-  result.name = $s.s_name
-  result.aliases = cstringArrayToSeq(s.s_aliases)
-  result.port = Port(s.s_port)
-  result.proto = $s.s_proto
-
-proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} =
-  ## Searches the database from the beginning and finds the first entry for
-  ## which the port specified by ``port`` matches the s_port member and the
-  ## protocol name specified by ``proto`` matches the s_proto member.
-  ##
-  ## On posix this will search through the ``/etc/services`` file.
-  when useWinVersion:
-    var s = winlean.getservbyport(ze(int16(port)).cint, proto)
-  else:
-    var s = posix.getservbyport(ze(int16(port)).cint, proto)
-  if s == nil: raiseOSError(osLastError(), "Service not found.")
-  result.name = $s.s_name
-  result.aliases = cstringArrayToSeq(s.s_aliases)
-  result.port = Port(s.s_port)
-  result.proto = $s.s_proto
-
-proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} =
-  ## This function will lookup the hostname of an IP Address.
-  var myaddr: InAddr
-  myaddr.s_addr = inet_addr(ip)
-
-  when useWinVersion:
-    var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
-                                  cint(AF_INET))
-    if s == nil: raiseOSError(osLastError())
-  else:
-    var s =
-      when defined(android4):
-        posix.gethostbyaddr(cast[cstring](addr(myaddr)), sizeof(myaddr).cint,
-                            cint(posix.AF_INET))
-      else:
-        posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen,
-                            cint(posix.AF_INET))
-    if s == nil:
-      raiseOSError(osLastError(), $hstrerror(h_errno))
-
-  result.name = $s.h_name
-  result.aliases = cstringArrayToSeq(s.h_aliases)
-  when useWinVersion:
-    result.addrtype = Domain(s.h_addrtype)
-  else:
-    if s.h_addrtype == posix.AF_INET:
-      result.addrtype = AF_INET
-    elif s.h_addrtype == posix.AF_INET6:
-      result.addrtype = AF_INET6
-    else:
-      raiseOSError(osLastError(), "unknown h_addrtype")
-  if result.addrtype == AF_INET:
-    result.addrlist = @[]
-    var i = 0
-    while not isNil(s.h_addrlist[i]):
-      var inaddr_ptr = cast[ptr InAddr](s.h_addr_list[i])
-      result.addrlist.add($inet_ntoa(inaddr_ptr[]))
-      inc(i)
-  else:
-    result.addrList = cstringArrayToSeq(s.h_addr_list)
-  result.length = int(s.h_length)
-
-proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
-  ## This function will lookup the IP address of a hostname.
-  when useWinVersion:
-    var s = winlean.gethostbyname(name)
-  else:
-    var s = posix.gethostbyname(name)
-  if s == nil: raiseOSError(osLastError())
-  result.name = $s.h_name
-  result.aliases = cstringArrayToSeq(s.h_aliases)
-  when useWinVersion:
-    result.addrtype = Domain(s.h_addrtype)
-  else:
-    if s.h_addrtype == posix.AF_INET:
-      result.addrtype = AF_INET
-    elif s.h_addrtype == posix.AF_INET6:
-      result.addrtype = AF_INET6
-    else:
-      raiseOSError(osLastError(), "unknown h_addrtype")
-  if result.addrtype == AF_INET:
-    result.addrlist = @[]
-    var i = 0
-    while not isNil(s.h_addrlist[i]):
-      var inaddr_ptr = cast[ptr InAddr](s.h_addr_list[i])
-      result.addrlist.add($inet_ntoa(inaddr_ptr[]))
-      inc(i)
-  else:
-    result.addrList = cstringArrayToSeq(s.h_addr_list)
-  result.length = int(s.h_length)
-
-proc getHostname*(): string {.tags: [ReadIOEffect].} =
-  ## Returns the local hostname (not the FQDN)
-  # https://tools.ietf.org/html/rfc1035#section-2.3.1
-  # https://tools.ietf.org/html/rfc2181#section-11
-  const size = 64
-  result = newString(size)
-  when useWinVersion:
-    let success = winlean.getHostname(result, size)
-  else:
-    # Posix
-    let success = posix.getHostname(result, size)
-  if success != 0.cint:
-    raiseOSError(osLastError())
-  let x = len(cstring(result))
-  result.setLen(x)
-
 proc getSockDomain*(socket: SocketHandle): Domain =
-  ## returns the socket's domain (AF_INET or AF_INET6).
+  ## Returns the socket's domain (AF_INET or AF_INET6).
   var name: Sockaddr_in6
   var namelen = sizeof(name).SockLen
   if getsockname(socket, cast[ptr SockAddr](addr(name)),
                  addr(namelen)) == -1'i32:
     raiseOSError(osLastError())
-  if name.sin6_family == nativeAfInet:
-    result = AF_INET
-  elif name.sin6_family == nativeAfInet6:
-    result = AF_INET6
+  let knownDomain = toKnownDomain(name.sin6_family.cint)
+  if knownDomain.isSome:
+    result = knownDomain.get()
   else:
-    raiseOSError(osLastError(), "unknown socket family in getSockFamily")
-
-
-proc getAddrString*(sockAddr: ptr SockAddr): string =
-  ## return the string representation of address within sockAddr
-  if sockAddr.sa_family == nativeAfInet:
-    result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr)
-  elif sockAddr.sa_family == nativeAfInet6:
-    let addrLen = when not useWinVersion: posix.INET6_ADDRSTRLEN
-                  else: 46 # it's actually 46 in both cases
-    result = newString(addrLen)
-    let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr
-    when not useWinVersion:
-      if posix.inet_ntop(posix.AF_INET6, addr6, addr result[0],
-                         result.len.int32) == nil:
-        raiseOSError(osLastError())
-      if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
-        result = result.substr("::ffff:".len)
+    raise newException(IOError, "Unknown socket family in getSockDomain")
+
+when not useNimNetLite:
+  proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} =
+    ## Searches the database from the beginning and finds the first entry for
+    ## which the service name specified by `name` matches the s_name member
+    ## and the protocol name specified by `proto` matches the s_proto member.
+    ##
+    ## On posix this will search through the `/etc/services` file.
+    when useWinVersion:
+      var s = winlean.getservbyname(name, proto)
     else:
-      if winlean.inet_ntop(winlean.AF_INET6, addr6, addr result[0],
-                           result.len.int32) == nil:
-        raiseOSError(osLastError())
-    setLen(result, len(cstring(result)))
-  else:
-    raise newException(IOError, "Unknown socket family in getAddrString")
-
-proc getSockName*(socket: SocketHandle): Port =
-  ## returns the socket's associated port number.
-  var name: Sockaddr_in
-  when useWinVersion:
-    name.sin_family = int16(ord(AF_INET))
-  else:
-    name.sin_family = posix.AF_INET
-  #name.sin_port = htons(cint16(port))
-  #name.sin_addr.s_addr = htonl(INADDR_ANY)
-  var namelen = sizeof(name).SockLen
-  if getsockname(socket, cast[ptr SockAddr](addr(name)),
-                 addr(namelen)) == -1'i32:
-    raiseOSError(osLastError())
-  result = Port(nativesockets.ntohs(name.sin_port))
+      var s = posix.getservbyname(name, proto)
+    if s == nil: raiseOSError(osLastError(), "Service not found.")
+    result.name = $s.s_name
+    result.aliases = cstringArrayToSeq(s.s_aliases)
+    result.port = Port(s.s_port)
+    result.proto = $s.s_proto
+
+  proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} =
+    ## Searches the database from the beginning and finds the first entry for
+    ## which the port specified by `port` matches the s_port member and the
+    ## protocol name specified by `proto` matches the s_proto member.
+    ##
+    ## On posix this will search through the `/etc/services` file.
+    when useWinVersion:
+      var s = winlean.getservbyport(uint16(port).cint, proto)
+    else:
+      var s = posix.getservbyport(uint16(port).cint, proto)
+    if s == nil: raiseOSError(osLastError(), "Service not found.")
+    result.name = $s.s_name
+    result.aliases = cstringArrayToSeq(s.s_aliases)
+    result.port = Port(s.s_port)
+    result.proto = $s.s_proto
+
+  proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} =
+    ## This function will lookup the hostname of an IP Address.
+    var
+      addrInfo = getAddrInfo(ip, Port(0), AF_UNSPEC)
+      myAddr: pointer
+      addrLen = 0
+      family = 0
+    
+    defer: freeAddrInfo(addrInfo)
+
+    if addrInfo.ai_addr.sa_family.cint == nativeAfInet:
+      family = nativeAfInet
+      myAddr = addr cast[ptr Sockaddr_in](addrInfo.ai_addr).sin_addr
+      addrLen = 4
+    elif addrInfo.ai_addr.sa_family.cint == nativeAfInet6:
+      family = nativeAfInet6
+      myAddr = addr cast[ptr Sockaddr_in6](addrInfo.ai_addr).sin6_addr
+      addrLen = 16
+    else:
+      raise newException(IOError, "Unknown socket family in `getHostByAddr()`")
 
-proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
-  ## returns the socket's local address and port number.
-  ##
-  ## Similar to POSIX's `getsockname`:idx:.
-  case domain
-  of AF_INET:
-    var name: Sockaddr_in
     when useWinVersion:
-      name.sin_family = int16(ord(AF_INET))
+      var s = winlean.gethostbyaddr(cast[ptr InAddr](myAddr), addrLen.cuint,
+                                    cint(family))
+      if s == nil: raiseOSError(osLastError())
     else:
-      name.sin_family = posix.AF_INET
-    var namelen = sizeof(name).SockLen
-    if getsockname(socket, cast[ptr SockAddr](addr(name)),
-                   addr(namelen)) == -1'i32:
-      raiseOSError(osLastError())
-    result = ($inet_ntoa(name.sin_addr),
-              Port(nativesockets.ntohs(name.sin_port)))
-  of AF_INET6:
-    var name: Sockaddr_in6
+      var s =
+        when defined(android4):
+          posix.gethostbyaddr(cast[cstring](myAddr), addrLen.cint,
+                              cint(family))
+        else:
+          posix.gethostbyaddr(myAddr, addrLen.SockLen,
+                              cint(family))
+      if s == nil:
+        raiseOSError(osLastError(), $hstrerror(h_errno))
+
+    result.name = $s.h_name
+    result.aliases = cstringArrayToSeq(s.h_aliases)
     when useWinVersion:
-      name.sin6_family = int16(ord(AF_INET6))
+      result.addrtype = Domain(s.h_addrtype)
     else:
-      name.sin6_family = posix.AF_INET6
-    var namelen = sizeof(name).SockLen
-    if getsockname(socket, cast[ptr SockAddr](addr(name)),
-                   addr(namelen)) == -1'i32:
-      raiseOSError(osLastError())
-    # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
-    result[0] = newString(64)
-    if inet_ntop(name.sin6_family.cint,
-                 addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil:
-      raiseOSError(osLastError())
-    setLen(result[0], result[0].cstring.len)
-    result[1] = Port(nativesockets.ntohs(name.sin6_port))
-  else:
-    raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
-
-proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
-  ## returns the socket's peer address and port number.
-  ##
-  ## Similar to POSIX's `getpeername`:idx:
-  case domain
-  of AF_INET:
-    var name: Sockaddr_in
+      if s.h_addrtype == posix.AF_INET:
+        result.addrtype = AF_INET
+      elif s.h_addrtype == posix.AF_INET6:
+        result.addrtype = AF_INET6
+      else:
+        raiseOSError(osLastError(), "unknown h_addrtype")
+    if result.addrtype == AF_INET:
+      result.addrList = @[]
+      var i = 0
+      while not isNil(s.h_addr_list[i]):
+        var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i])
+        result.addrList.add($inet_ntoa(inaddrPtr[]))
+        inc(i)
+    else:
+      let strAddrLen = when not useWinVersion: posix.INET6_ADDRSTRLEN.int
+                       else: 46
+      var i = 0
+      while not isNil(s.h_addr_list[i]):
+        var ipStr = newString(strAddrLen)
+        if inet_ntop(nativeAfInet6, cast[pointer](s.h_addr_list[i]),
+                     cstring(ipStr), len(ipStr).int32) == nil:
+          raiseOSError(osLastError())
+        when not useWinVersion:
+          if posix.IN6_IS_ADDR_V4MAPPED(cast[ptr In6Addr](s.h_addr_list[i])) != 0:
+            ipStr.setSlice("::ffff:".len..<strAddrLen)
+        setLen(ipStr, len(cstring(ipStr)))
+        result.addrList.add(ipStr)
+        inc(i)
+    result.length = int(s.h_length)
+
+  proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
+    ## This function will lookup the IP address of a hostname.
     when useWinVersion:
-      name.sin_family = int16(ord(AF_INET))
+      var s = winlean.gethostbyname(name)
     else:
-      name.sin_family = posix.AF_INET
-    var namelen = sizeof(name).SockLen
-    if getpeername(socket, cast[ptr SockAddr](addr(name)),
-                   addr(namelen)) == -1'i32:
+      var s = posix.gethostbyname(name)
+    if s == nil: raiseOSError(osLastError())
+    result.name = $s.h_name
+    result.aliases = cstringArrayToSeq(s.h_aliases)
+    when useWinVersion:
+      result.addrtype = Domain(s.h_addrtype)
+    else:
+      if s.h_addrtype == posix.AF_INET:
+        result.addrtype = AF_INET
+      elif s.h_addrtype == posix.AF_INET6:
+        result.addrtype = AF_INET6
+      else:
+        raiseOSError(osLastError(), "unknown h_addrtype")
+    if result.addrtype == AF_INET:
+      result.addrList = @[]
+      var i = 0
+      while not isNil(s.h_addr_list[i]):
+        var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i])
+        result.addrList.add($inet_ntoa(inaddrPtr[]))
+        inc(i)
+    else:
+      result.addrList = cstringArrayToSeq(s.h_addr_list)
+    result.length = int(s.h_length)
+
+  proc getHostname*(): string {.tags: [ReadIOEffect].} =
+    ## Returns the local hostname (not the FQDN)
+    # https://tools.ietf.org/html/rfc1035#section-2.3.1
+    # https://tools.ietf.org/html/rfc2181#section-11
+    const size = 256
+    result = newString(size)
+    when useWinVersion:
+      let success = winlean.gethostname(result.cstring, size)
+    else:
+      # Posix
+      let success = posix.gethostname(result.cstring, size)
+    if success != 0.cint:
       raiseOSError(osLastError())
-    result = ($inet_ntoa(name.sin_addr),
-              Port(nativesockets.ntohs(name.sin_port)))
-  of AF_INET6:
-    var name: Sockaddr_in6
+    let x = len(cstring(result))
+    result.setLen(x)
+
+  proc getAddrString*(sockAddr: ptr SockAddr): string =
+    ## Returns the string representation of address within sockAddr
+    if sockAddr.sa_family.cint == nativeAfInet:
+      result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr)
+    elif sockAddr.sa_family.cint == nativeAfInet6:
+      let addrLen = when not useWinVersion: posix.INET6_ADDRSTRLEN.int
+                    else: 46 # it's actually 46 in both cases
+      result = newString(addrLen)
+      let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr
+      when not useWinVersion:
+        if posix.inet_ntop(posix.AF_INET6, addr6, cast[cstring](addr result[0]),
+                          result.len.int32) == nil:
+          raiseOSError(osLastError())
+        if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
+          result.setSlice("::ffff:".len..<addrLen)
+      else:
+        if winlean.inet_ntop(winlean.AF_INET6, addr6, cast[cstring](addr result[0]),
+                            result.len.int32) == nil:
+          raiseOSError(osLastError())
+      setLen(result, len(cstring(result)))
+    else:
+      when defined(posix) and not defined(nimdoc):
+        if sockAddr.sa_family.cint == nativeAfUnix:
+          return "unix"
+      raise newException(IOError, "Unknown socket family in getAddrString")
+
+  proc getAddrString*(sockAddr: ptr SockAddr, strAddress: var string) =
+    ## Stores in `strAddress` the string representation of the address inside
+    ## `sockAddr`
+    ##
+    ## **Note**
+    ## * `strAddress` must be initialized to 46 in length.
+    const length = 46
+    assert(length == len(strAddress),
+          "`strAddress` was not initialized correctly. 46 != `len(strAddress)`")
+    if sockAddr.sa_family.cint == nativeAfInet:
+      let addr4 = addr cast[ptr Sockaddr_in](sockAddr).sin_addr
+      when not useWinVersion:
+        if posix.inet_ntop(posix.AF_INET, addr4, cast[cstring](addr strAddress[0]),
+                          strAddress.len.int32) == nil:
+          raiseOSError(osLastError())
+      else:
+        if winlean.inet_ntop(winlean.AF_INET, addr4, cast[cstring](addr strAddress[0]),
+                            strAddress.len.int32) == nil:
+          raiseOSError(osLastError())
+    elif sockAddr.sa_family.cint == nativeAfInet6:
+      let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr
+      when not useWinVersion:
+        if posix.inet_ntop(posix.AF_INET6, addr6, cast[cstring](addr strAddress[0]),
+                          strAddress.len.int32) == nil:
+          raiseOSError(osLastError())
+        if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
+          strAddress.setSlice("::ffff:".len..<length)
+      else:
+        if winlean.inet_ntop(winlean.AF_INET6, addr6, cast[cstring](addr strAddress[0]),
+                            strAddress.len.int32) == nil:
+          raiseOSError(osLastError())
+    else:
+      raise newException(IOError, "Unknown socket family in getAddrString")
+    setLen(strAddress, len(cstring(strAddress)))
+
+  when defined(posix) and not defined(nimdoc):
+    proc makeUnixAddr*(path: string): Sockaddr_un =
+      result.sun_family = AF_UNIX.TSa_Family
+      if path.len >= Sockaddr_un_path_length:
+        raise newException(ValueError, "socket path too long")
+      copyMem(addr result.sun_path, path.cstring, path.len + 1)
+
+  proc getSockName*(socket: SocketHandle): Port =
+    ## Returns the socket's associated port number.
+    var name: Sockaddr_in
     when useWinVersion:
-      name.sin6_family = int16(ord(AF_INET6))
+      name.sin_family = uint16(ord(AF_INET))
     else:
-      name.sin6_family = posix.AF_INET6
+      name.sin_family = TSa_Family(posix.AF_INET)
+    #name.sin_port = htons(cint16(port))
+    #name.sin_addr.s_addr = htonl(INADDR_ANY)
     var namelen = sizeof(name).SockLen
-    if getpeername(socket, cast[ptr SockAddr](addr(name)),
-                   addr(namelen)) == -1'i32:
+    if getsockname(socket, cast[ptr SockAddr](addr(name)),
+                  addr(namelen)) == -1'i32:
       raiseOSError(osLastError())
-    # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
-    result[0] = newString(64)
-    if inet_ntop(name.sin6_family.cint,
-                 addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil:
+    result = Port(nativesockets.ntohs(name.sin_port))
+
+  proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
+    ## Returns the socket's local address and port number.
+    ##
+    ## Similar to POSIX's `getsockname`:idx:.
+    case domain
+    of AF_INET:
+      var name: Sockaddr_in
+      when useWinVersion:
+        name.sin_family = uint16(ord(AF_INET))
+      else:
+        name.sin_family = TSa_Family(posix.AF_INET)
+      var namelen = sizeof(name).SockLen
+      if getsockname(socket, cast[ptr SockAddr](addr(name)),
+                    addr(namelen)) == -1'i32:
+        raiseOSError(osLastError())
+      result = ($inet_ntoa(name.sin_addr),
+                Port(nativesockets.ntohs(name.sin_port)))
+    of AF_INET6:
+      var name: Sockaddr_in6
+      when useWinVersion:
+        name.sin6_family = uint16(ord(AF_INET6))
+      else:
+        name.sin6_family = TSa_Family(posix.AF_INET6)
+      var namelen = sizeof(name).SockLen
+      if getsockname(socket, cast[ptr SockAddr](addr(name)),
+                    addr(namelen)) == -1'i32:
+        raiseOSError(osLastError())
+      # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
+      result[0] = newString(64)
+      if inet_ntop(name.sin6_family.cint,
+          addr name.sin6_addr, cast[cstring](addr result[0][0]), (result[0].len+1).int32).isNil:
+        raiseOSError(osLastError())
+      setLen(result[0], result[0].cstring.len)
+      result[1] = Port(nativesockets.ntohs(name.sin6_port))
+    else:
+      raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
+
+  proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
+    ## Returns the socket's peer address and port number.
+    ##
+    ## Similar to POSIX's `getpeername`:idx:
+    case domain
+    of AF_INET:
+      var name: Sockaddr_in
+      when useWinVersion:
+        name.sin_family = uint16(ord(AF_INET))
+      else:
+        name.sin_family = TSa_Family(posix.AF_INET)
+      var namelen = sizeof(name).SockLen
+      if getpeername(socket, cast[ptr SockAddr](addr(name)),
+                    addr(namelen)) == -1'i32:
+        raiseOSError(osLastError())
+      result = ($inet_ntoa(name.sin_addr),
+                Port(nativesockets.ntohs(name.sin_port)))
+    of AF_INET6:
+      var name: Sockaddr_in6
+      when useWinVersion:
+        name.sin6_family = uint16(ord(AF_INET6))
+      else:
+        name.sin6_family = TSa_Family(posix.AF_INET6)
+      var namelen = sizeof(name).SockLen
+      if getpeername(socket, cast[ptr SockAddr](addr(name)),
+                    addr(namelen)) == -1'i32:
+        raiseOSError(osLastError())
+      # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
+      result[0] = newString(64)
+      if inet_ntop(name.sin6_family.cint,
+          addr name.sin6_addr, cast[cstring](addr result[0][0]), (result[0].len+1).int32).isNil:
+        raiseOSError(osLastError())
+      setLen(result[0], result[0].cstring.len)
+      result[1] = Port(nativesockets.ntohs(name.sin6_port))
+    else:
+      raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
+
+when useNimNetLite: 
+
+  when useWinVersion:
+    const
+      INET_ADDRSTRLEN = 16
+      INET6_ADDRSTRLEN = 46 # it's actually 46 in both cases
+
+  proc sockAddrToStr(sa: ptr SockAddr): string {.noinit.} =
+    let af_family = sa.sa_family
+    var nl, v4Slice: cint
+    var si_addr: ptr InAddr
+
+    if af_family == AF_INET.TSa_Family:
+      nl = INET_ADDRSTRLEN
+      si_addr = cast[ptr Sockaddr_in](sa).sin_addr.addr()
+    elif af_family == AF_INET6.TSa_Family:
+      nl = INET6_ADDRSTRLEN
+      let si6_addr = cast[ptr Sockaddr_in6](sa).sin6_addr.addr()
+      si_addr = cast[ptr InAddr](si6_addr) # let's us reuse logic below 
+      when defined(posix) and not defined(nimdoc) and not defined(zephyr):
+        if posix.IN6_IS_ADDR_V4MAPPED(si6_addr) != 0:
+          v4Slice = "::ffff:".len()
+    else:
+      when defined(posix) and not defined(nimdoc):
+        if af_family.cint == nativeAfUnix:
+          return "unix"
+      return ""
+
+    result = newString(nl)
+    let namePtr = result.cstring()
+    if namePtr == inet_ntop(af_family.cint, si_addr, namePtr, nl):
+      result.setLen(len(namePtr))
+      if v4Slice > 0: result.setSlice(v4Slice.int ..< nl.int)
+    else:
+      return ""
+
+  proc sockAddrToStr(sa: var Sockaddr_in | var Sockaddr_in6): string =
+    result = sockAddrToStr(cast[ptr SockAddr](unsafeAddr(sa)))
+
+  proc getAddrString*(sockAddr: ptr SockAddr): string =
+    result = sockAddrToStr(sockAddr)
+    if result.len() == 0:
       raiseOSError(osLastError())
-    setLen(result[0], result[0].cstring.len)
-    result[1] = Port(nativesockets.ntohs(name.sin6_port))
-  else:
-    raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
+
+  proc getAddrString*(sockAddr: ptr SockAddr, strAddress: var string) {.noinit.} =
+    strAddress = getAddrString(sockAddr)
+
+  proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
+    ## Returns the socket's local address and port number.
+    ##
+    ## Similar to POSIX's `getsockname`:idx:.
+    template sockGetNameOrRaiseError(socket: untyped, name: untyped) =
+      var namelen = sizeof(socket).SockLen
+      if getsockname(socket, cast[ptr SockAddr](addr(name)),
+                    addr(namelen)) == -1'i32:
+        raiseOSError(osLastError())
+
+    case domain
+    of AF_INET:
+      var name = Sockaddr_in(sin_family: TSa_Family(posix.AF_INET))
+      sockGetNameOrRaiseError(socket, name)
+      result = (sockAddrToStr(name),
+                Port(nativesockets.ntohs(name.sin_port)))
+    of AF_INET6:
+      var name = Sockaddr_in6(sin6_family: TSa_Family(posix.AF_INET6))
+      sockGetNameOrRaiseError(socket, name)
+      result = (sockAddrToStr(name),
+                Port(nativesockets.ntohs(name.sin6_port)))
+    else:
+      raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
+
 
 proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {.
   tags: [ReadIOEffect].} =
@@ -596,7 +753,7 @@ proc setSockOptInt*(socket: SocketHandle, level, optname, optval: int) {.
 proc setBlocking*(s: SocketHandle, blocking: bool) =
   ## Sets blocking mode on socket.
   ##
-  ## Raises EOS on error.
+  ## Raises OSError on error.
   when useWinVersion:
     var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
     if ioctlsocket(s, FIONBIO, addr(mode)) == -1:
@@ -637,38 +794,15 @@ 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
+  ## 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``.
+  ## read from. The sockets which cannot be read from will also be removed
+  ## from `readfds`.
   ##
-  ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for
+  ## `timeout` is specified in milliseconds and `-1` can be specified for
   ## an unlimited time.
-  var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
+  var tv {.noinit.}: Timeval = timeValFromMilliseconds(timeout)
 
   var rd: TFdSet
   var m = 0
@@ -683,14 +817,14 @@ proc selectRead*(readfds: var seq[SocketHandle], timeout = 500): int =
 
 proc selectWrite*(writefds: var seq[SocketHandle],
                   timeout = 500): int {.tags: [ReadIOEffect].} =
-  ## When a socket in ``writefds`` is ready to be written to then a non-zero
+  ## When a socket in `writefds` is ready to be written to then a non-zero
   ## value will be returned specifying the count of the sockets which can be
-  ## written to. The sockets which can be written to will also be removed
-  ## from ``writefds``.
+  ## written to. The sockets which cannot be written to will also be removed
+  ## from `writefds`.
   ##
-  ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for
+  ## `timeout` is specified in milliseconds and `-1` can be specified for
   ## an unlimited time.
-  var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
+  var tv {.noinit.}: Timeval = timeValFromMilliseconds(timeout)
 
   var wr: TFdSet
   var m = 0
@@ -703,19 +837,34 @@ proc selectWrite*(writefds: var seq[SocketHandle],
 
   pruneSocketSet(writefds, (wr))
 
-proc accept*(fd: SocketHandle): (SocketHandle, string) =
+proc accept*(fd: SocketHandle, inheritable = defined(nimInheritHandles)): (SocketHandle, string) =
   ## Accepts a new client connection.
   ##
+  ## `inheritable` decides if the resulting SocketHandle can be inherited by
+  ## child processes.
+  ##
   ## Returns (osInvalidSocket, "") if an error occurred.
-  var sockAddress: Sockaddr_in
+  var sockAddress: SockAddr
   var addrLen = sizeof(sockAddress).SockLen
-  var sock = accept(fd, cast[ptr SockAddr](addr(sockAddress)),
-                    addr(addrLen))
+  var sock =
+    when (defined(linux) or defined(bsd)) and not defined(nimdoc):
+      accept4(fd, addr(sockAddress), addr(addrLen),
+              if inheritable: 0 else: SOCK_CLOEXEC)
+    else:
+      accept(fd, addr(sockAddress), addr(addrLen))
+  when declared(setInheritable) and not (defined(linux) or defined(bsd)):
+    if not setInheritable(sock, inheritable):
+      close sock
+      sock = osInvalidSocket
   if sock == osInvalidSocket:
     return (osInvalidSocket, "")
   else:
-    return (sock, $inet_ntoa(sockAddress.sin_addr))
+    when useNimNetLite:
+      var name = sockAddrToStr(addr sockAddress)
+      return (sock, name)
+    else:
+      return (sock, $inet_ntoa(cast[Sockaddr_in](sockAddress).sin_addr))
 
-when defined(Windows):
+when defined(windows):
   var wsa: WSAData
   if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError())
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index 5d2efebee..24c94b651 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -9,18 +9,31 @@
 
 ## This module implements a high-level cross-platform sockets interface.
 ## The procedures implemented in this module are primarily for blocking sockets.
-## For asynchronous non-blocking sockets use the ``asyncnet`` module together
-## with the ``asyncdispatch`` module.
+## For asynchronous non-blocking sockets use the `asyncnet` module together
+## with the `asyncdispatch` module.
 ##
 ## The first thing you will always need to do in order to start using sockets,
-## is to create a new instance of the ``Socket`` type using the ``newSocket``
+## is to create a new instance of the `Socket` type using the `newSocket`
 ## procedure.
 ##
 ## SSL
 ## ====
 ##
 ## In order to use the SSL procedures defined in this module, you will need to
-## compile your application with the ``-d:ssl`` flag.
+## compile your application with the `-d:ssl` flag. See the
+## `newContext<net.html#newContext%2Cstring%2Cstring%2Cstring%2Cstring>`_
+## procedure for additional details.
+##
+##
+## SSL on Windows
+## ==============
+##
+## On Windows the SSL library checks for valid certificates.
+## It uses the `cacert.pem` file for this purpose which was extracted
+## from `https://curl.se/ca/cacert.pem`. Besides
+## the OpenSSL DLLs (e.g. libssl-1_1-x64.dll, libcrypto-1_1-x64.dll) you
+## also need to ship `cacert.pem` with your `.exe` file.
+##
 ##
 ## Examples
 ## ========
@@ -28,65 +41,87 @@
 ## Connecting to a server
 ## ----------------------
 ##
-## After you create a socket with the ``newSocket`` procedure, you can easily
+## After you create a socket with the `newSocket` procedure, you can easily
 ## connect it to a server running at a known hostname (or IP address) and port.
 ## To do so over TCP, use the example below.
-##
-## .. code-block:: Nim
-##   var socket = newSocket()
-##   socket.connect("google.com", Port(80))
-##
+
+runnableExamples("-r:off"):
+  let socket = newSocket()
+  socket.connect("google.com", Port(80))
+
+## For SSL, use the following example:
+
+runnableExamples("-r:off -d:ssl"):
+  let socket = newSocket()
+  let ctx = newContext()
+  wrapSocket(ctx, socket)
+  socket.connect("google.com", Port(443))
+
 ## UDP is a connectionless protocol, so UDP sockets don't have to explicitly
-## call the ``connect`` procedure. They can simply start sending data
-## immediately.
-##
-## .. code-block:: Nim
-##   var socket = newSocket()
-##   socket.sendTo("192.168.0.1", Port(27960), "status\n")
-##
+## call the `connect <net.html#connect%2CSocket%2Cstring>`_ procedure. They can
+## simply start sending data immediately.
+
+runnableExamples("-r:off"):
+  let socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
+  socket.sendTo("192.168.0.1", Port(27960), "status\n")
+
+runnableExamples("-r:off"):
+  let socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
+  let ip = parseIpAddress("192.168.0.1")
+  doAssert socket.sendTo(ip, Port(27960), "status\c\l") == 8
+
 ## Creating a server
 ## -----------------
 ##
-## After you create a socket with the ``newSocket`` procedure, you can create a
-## TCP server by calling the ``bindAddr`` and ``listen`` procedures.
-##
-## .. code-block:: Nim
-##   var socket = newSocket()
-##   socket.bindAddr(Port(1234))
-##   socket.listen()
-##
-## You can then begin accepting connections using the ``accept`` procedure.
-##
-## .. code-block:: Nim
-##   var client = new Socket
-##   var address = ""
-##   while true:
-##     socket.acceptAddr(client, address)
-##     echo("Client connected from: ", address)
-##
-## **Note:** The ``client`` variable is initialised with ``new Socket`` **not**
-## ``newSocket()``. The difference is that the latter creates a new file
-## descriptor.
+## After you create a socket with the `newSocket` procedure, you can create a
+## TCP server by calling the `bindAddr` and `listen` procedures.
+
+runnableExamples("-r:off"):
+  let socket = newSocket()
+  socket.bindAddr(Port(1234))
+  socket.listen()
+
+  # You can then begin accepting connections using the `accept` procedure.
+  var client: Socket
+  var address = ""
+  while true:
+    socket.acceptAddr(client, address)
+    echo "Client connected from: ", address
 
-{.deadCodeElim: on.}  # dce option deprecated
-import nativesockets, os, strutils, parseutils, times, sets, options
-export Port, `$`, `==`
-export Domain, SockType, Protocol
+import std/private/since
 
-const useWinVersion = defined(Windows) or defined(nimdoc)
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+import std/nativesockets
+import std/[os, strutils, times, sets, options, monotimes]
+import std/ssl_config
+export nativesockets.Port, nativesockets.`$`, nativesockets.`==`
+export Domain, SockType, Protocol, IPPROTO_NONE
+
+const useWinVersion = defined(windows) or defined(nimdoc)
+const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) or
+    defined(nuttx)
 const defineSsl = defined(ssl) or defined(nimdoc)
 
+when useWinVersion:
+  from std/winlean import WSAESHUTDOWN
+
 when defineSsl:
-  import openssl
+  import std/openssl
+  when not defined(nimDisableCertificateValidation):
+    from std/ssl_certs import scanSSLCertificates
 
 # Note: The enumerations are mapped to Window's constants.
 
 when defineSsl:
   type
-    SslError* = object of Exception
+    Certificate* = string ## DER encoded certificate
+
+    SslError* = object of CatchableError
 
     SslCVerifyMode* = enum
-      CVerifyNone, CVerifyPeer
+      CVerifyNone, CVerifyPeer, CVerifyPeerUseEnvVars
 
     SslProtVersion* = enum
       protSSLv2, protSSLv3, protTLSv1, protSSLv23
@@ -112,30 +147,27 @@ when defineSsl:
 
 else:
   type
-    SslContext* = void # TODO: Workaround #4797.
+    SslContext* = ref object # TODO: Workaround #4797.
 
 const
   BufferSize*: int = 4000 ## size of a buffered socket's buffer
   MaxLineLength* = 1_000_000
 
 type
-  SocketImpl* = object ## socket type
+  SocketImpl* = object     ## socket type
     fd: SocketHandle
-    case isBuffered: bool # determines whether this socket is buffered.
-    of true:
-      buffer: array[0..BufferSize, char]
-      currPos: int # current index in buffer
-      bufLen: int # current length of buffer
-    of false: nil
+    isBuffered: bool       # determines whether this socket is buffered.
+    buffer: array[0..BufferSize, char]
+    currPos: int           # current index in buffer
+    bufLen: int            # current length of buffer
     when defineSsl:
-      case isSsl: bool
-      of true:
-        sslHandle: SSLPtr
-        sslContext: SSLContext
-        sslNoHandshake: bool # True if needs handshake.
-        sslHasPeekChar: bool
-        sslPeekChar: char
-      of false: nil
+      isSsl: bool
+      sslHandle: SslPtr
+      sslContext: SslContext
+      sslNoHandshake: bool # True if needs handshake.
+      sslHasPeekChar: bool
+      sslPeekChar: char
+      sslNoShutdown: bool # True if shutdown shouldn't be done.
     lastError: OSErrorCode ## stores the last error on this socket
     domain: Domain
     sockType: SockType
@@ -150,40 +182,76 @@ type
   ReadLineResult* = enum ## result for readLineAsync
     ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone
 
-  TimeoutError* = object of Exception
+  TimeoutError* = object of CatchableError
 
   SocketFlag* {.pure.} = enum
     Peek,
     SafeDisconn ## Ensures disconnection exceptions (ECONNRESET, EPIPE etc) are not thrown.
 
+when defined(nimHasStyleChecks):
+  {.push styleChecks: off.}
+
 type
   IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address
-    IPv6, ## IPv6 address
-    IPv4  ## IPv4 address
+    IPv6,                          ## IPv6 address
+    IPv4                           ## IPv4 address
 
-  IpAddress* = object ## stores an arbitrary IP address
-    case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6)
+  IpAddress* = object                  ## stores an arbitrary IP address
+    case family*: IpAddressFamily      ## the type of the IP address (IPv4 or IPv6)
     of IpAddressFamily.IPv6:
       address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in
                                        ## case of IPv6
     of IpAddressFamily.IPv4:
-      address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in
-                                      ## case of IPv4
+      address_v4*: array[0..3, uint8]  ## Contains the IP address in bytes in
+                                       ## case of IPv4
+when defined(nimHasStyleChecks):
+  {.pop.}
+
+
+when defined(posix) and not defined(lwip):
+  from std/posix import TPollfd, POLLIN, POLLPRI, POLLOUT, POLLWRBAND, Tnfds
+
+  template monitorPollEvent(x: var SocketHandle, y: cint, timeout: int): int =
+    var tpollfd: TPollfd
+    tpollfd.fd = cast[cint](x)
+    tpollfd.events = y
+    posix.poll(addr(tpollfd), Tnfds(1), timeout)
+
+proc timeoutRead(fd: var SocketHandle, timeout = 500): int =
+  when defined(windows) or defined(lwip):
+    var fds = @[fd]
+    selectRead(fds, timeout)
+  else:
+    monitorPollEvent(fd, POLLIN or POLLPRI, timeout)
+
+proc timeoutWrite(fd: var SocketHandle, timeout = 500): int =
+  when defined(windows) or defined(lwip):
+    var fds = @[fd]
+    selectWrite(fds, timeout)
+  else:
+    monitorPollEvent(fd, POLLOUT or POLLWRBAND, timeout)
 
 proc socketError*(socket: Socket, err: int = -1, async = false,
-                  lastError = (-1).OSErrorCode): void {.gcsafe.}
+                  lastError = (-1).OSErrorCode,
+                  flags: set[SocketFlag] = {}) {.gcsafe.}
 
 proc isDisconnectionError*(flags: set[SocketFlag],
     lastError: OSErrorCode): bool =
-  ## Determines whether ``lastError`` is a disconnection error. Only does this
-  ## if flags contains ``SafeDisconn``.
+  ## Determines whether `lastError` is a disconnection error. Only does this
+  ## if flags contains `SafeDisconn`.
   when useWinVersion:
     SocketFlag.SafeDisconn in flags and
-      lastError.int32 in {WSAECONNRESET, WSAECONNABORTED, WSAENETRESET,
-                          WSAEDISCON, ERROR_NETNAME_DELETED}
+      (lastError.int32 == WSAECONNRESET or
+       lastError.int32 == WSAECONNABORTED or
+       lastError.int32 == WSAENETRESET or
+       lastError.int32 == WSAEDISCON or
+       lastError.int32 == WSAESHUTDOWN or
+       lastError.int32 == ERROR_NETNAME_DELETED)
   else:
     SocketFlag.SafeDisconn in flags and
-      lastError.int32 in {ECONNRESET, EPIPE, ENETRESET}
+      (lastError.int32 == ECONNRESET or
+       lastError.int32 == EPIPE or
+       lastError.int32 == ENETRESET)
 
 proc toOSFlags*(socketFlags: set[SocketFlag]): cint =
   ## Converts the flags into the underlying OS representation.
@@ -195,7 +263,7 @@ proc toOSFlags*(socketFlags: set[SocketFlag]): cint =
 
 proc newSocket*(fd: SocketHandle, domain: Domain = AF_INET,
     sockType: SockType = SOCK_STREAM,
-    protocol: Protocol = IPPROTO_TCP, buffered = true): Socket =
+    protocol: Protocol = IPPROTO_TCP, buffered = true): owned(Socket) =
   ## Creates a new socket as specified by the params.
   assert fd != osInvalidSocket
   result = Socket(
@@ -211,89 +279,106 @@ proc newSocket*(fd: SocketHandle, domain: Domain = AF_INET,
   when defined(macosx) and not defined(nimdoc):
     setSockOptInt(fd, SOL_SOCKET, SO_NOSIGPIPE, 1)
 
-proc newSocket*(domain, sockType, protocol: cint, buffered = true): Socket =
+proc newSocket*(domain, sockType, protocol: cint, buffered = true,
+                inheritable = defined(nimInheritHandles)): owned(Socket) =
   ## Creates a new socket.
   ##
-  ## If an error occurs EOS will be raised.
-  let fd = createNativeSocket(domain, sockType, protocol)
+  ## The SocketHandle associated with the resulting Socket will not be
+  ## inheritable by child processes by default. This can be changed via
+  ## the `inheritable` parameter.
+  ##
+  ## If an error occurs OSError will be raised.
+  let fd = createNativeSocket(domain, sockType, protocol, inheritable)
   if fd == osInvalidSocket:
     raiseOSError(osLastError())
   result = newSocket(fd, domain.Domain, sockType.SockType, protocol.Protocol,
                      buffered)
 
 proc newSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
-                protocol: Protocol = IPPROTO_TCP, buffered = true): Socket =
+                protocol: Protocol = IPPROTO_TCP, buffered = true,
+                inheritable = defined(nimInheritHandles)): owned(Socket) =
   ## Creates a new socket.
   ##
-  ## If an error occurs EOS will be raised.
-  let fd = createNativeSocket(domain, sockType, protocol)
+  ## The SocketHandle associated with the resulting Socket will not be
+  ## inheritable by child processes by default. This can be changed via
+  ## the `inheritable` parameter.
+  ##
+  ## If an error occurs OSError will be raised.
+  let fd = createNativeSocket(domain, sockType, protocol, inheritable)
   if fd == osInvalidSocket:
     raiseOSError(osLastError())
   result = newSocket(fd, domain, sockType, protocol, buffered)
 
-proc parseIPv4Address(address_str: string): IpAddress =
-  ## Parses IPv4 adresses
-  ## Raises EInvalidValue on errors
+proc parseIPv4Address(addressStr: string): IpAddress =
+  ## Parses IPv4 addresses
+  ## Raises ValueError on errors
   var
     byteCount = 0
-    currentByte:uint16 = 0
-    seperatorValid = false
+    currentByte: uint16 = 0
+    separatorValid = false
+    leadingZero = false
 
-  result.family = IpAddressFamily.IPv4
+  result = IpAddress(family: IpAddressFamily.IPv4)
 
-  for i in 0 .. high(address_str):
-    if address_str[i] in strutils.Digits: # Character is a number
+  for i in 0 .. high(addressStr):
+    if addressStr[i] in strutils.Digits: # Character is a number
+      if leadingZero:
+        raise newException(ValueError,
+          "Invalid IP address. Octal numbers are not allowed")
       currentByte = currentByte * 10 +
-        cast[uint16](ord(address_str[i]) - ord('0'))
-      if currentByte > 255'u16:
+        cast[uint16](ord(addressStr[i]) - ord('0'))
+      if currentByte == 0'u16:
+        leadingZero = true
+      elif currentByte > 255'u16:
         raise newException(ValueError,
           "Invalid IP Address. Value is out of range")
-      seperatorValid = true
-    elif address_str[i] == '.': # IPv4 address separator
-      if not seperatorValid or byteCount >= 3:
+      separatorValid = true
+    elif addressStr[i] == '.': # IPv4 address separator
+      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
+      leadingZero = 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)
 
-proc parseIPv6Address(address_str: string): IpAddress =
-  ## Parses IPv6 adresses
-  ## Raises EInvalidValue on errors
-  result.family = IpAddressFamily.IPv6
-  if address_str.len < 2:
+proc parseIPv6Address(addressStr: string): IpAddress =
+  ## Parses IPv6 addresses
+  ## Raises ValueError on errors
+  result = IpAddress(family: IpAddressFamily.IPv6)
+  if addressStr.len < 2:
     raise newException(ValueError, "Invalid IP Address")
 
   var
     groupCount = 0
     currentGroupStart = 0
-    currentShort:uint32 = 0
-    seperatorValid = true
+    currentShort: uint32 = 0
+    separatorValid = true
     dualColonGroup = -1
     lastWasColon = false
     v4StartPos = -1
     byteCount = 0
 
-  for i,c in address_str:
+  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
-      elif i != 0 and i != high(address_str):
+        separatorValid = false
+      elif i != 0 and i != high(addressStr):
         if groupCount >= 8:
           raise newException(ValueError,
             "Invalid IP Address. The address consists of too many groups")
@@ -301,23 +386,23 @@ proc parseIPv6Address(address_str: 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 address_str[1] != ':':
+        if addressStr[1] != ':':
           raise newException(ValueError,
             "Invalid IP Address. Address may not start with \":\"")
-      else: # i == high(address_str) - only valid if address ends with ::
-        if address_str[high(address_str)-1] != ':':
+      else: # i == high(addressStr) - only valid if address ends with ::
+        if addressStr[high(addressStr)-1] != ':':
           raise newException(ValueError,
             "Invalid IP Address. Address may not end with \":\"")
       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
@@ -330,14 +415,14 @@ proc parseIPv6Address(address_str: 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")
@@ -345,25 +430,32 @@ proc parseIPv6Address(address_str: string): IpAddress =
       result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
       groupCount.inc()
   else: # Must parse IPv4 address
-    for i,c in address_str[v4StartPos..high(address_str)]:
+    var leadingZero = false
+    for i, c in addressStr[v4StartPos..high(addressStr)]:
       if c in strutils.Digits: # Character is a number
+        if leadingZero:
+          raise newException(ValueError,
+            "Invalid IP address. Octal numbers not allowed")
         currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0'))
-        if currentShort > 255'u32:
+        if currentShort == 0'u32:
+          leadingZero = true
+        elif 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
+        leadingZero = 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
@@ -386,55 +478,60 @@ proc parseIPv6Address(address_str: string): IpAddress =
     raise newException(ValueError,
       "Invalid IP Address. The address consists of too many groups")
 
-proc parseIpAddress*(address_str: string): IpAddress =
+proc parseIpAddress*(addressStr: string): IpAddress =
   ## Parses an IP address
-  ## Raises EInvalidValue on error
-  if address_str == nil:
-    raise newException(ValueError, "IP Address string is nil")
-  if address_str.contains(':'):
-    return parseIPv6Address(address_str)
+  ##
+  ## Raises ValueError on error.
+  ##
+  ## For IPv4 addresses, only the strict form as
+  ## defined in RFC 6943 is considered valid, see
+  ## https://datatracker.ietf.org/doc/html/rfc6943#section-3.1.1.
+  if addressStr.len == 0:
+    raise newException(ValueError, "IP Address string is empty")
+  if addressStr.contains(':'):
+    return parseIPv6Address(addressStr)
   else:
-    return parseIPv4Address(address_str)
+    return parseIPv4Address(addressStr)
 
-proc isIpAddress*(address_str: string): bool {.tags: [].} =
+proc isIpAddress*(addressStr: string): bool {.tags: [].} =
   ## Checks if a string is an IP address
   ## Returns true if it is, false otherwise
   try:
-    discard parseIpAddress(address_str)
+    discard parseIpAddress(addressStr)
   except ValueError:
     return false
   return true
 
 proc toSockAddr*(address: IpAddress, port: Port, sa: var Sockaddr_storage,
-                 sl: var Socklen) =
-  ## Converts `IpAddress` and `Port` to `SockAddr` and `Socklen`
+                 sl: var SockLen) =
+  ## Converts `IpAddress` and `Port` to `SockAddr` and `SockLen`
   let port = htons(uint16(port))
   case address.family
   of IpAddressFamily.IPv4:
-    sl = sizeof(Sockaddr_in).Socklen
+    sl = sizeof(Sockaddr_in).SockLen
     let s = cast[ptr Sockaddr_in](addr sa)
-    s.sin_family = type(s.sin_family)(toInt(AF_INET))
+    s.sin_family = typeof(s.sin_family)(toInt(AF_INET))
     s.sin_port = port
     copyMem(addr s.sin_addr, unsafeAddr address.address_v4[0],
             sizeof(s.sin_addr))
   of IpAddressFamily.IPv6:
-    sl = sizeof(Sockaddr_in6).Socklen
+    sl = sizeof(Sockaddr_in6).SockLen
     let s = cast[ptr Sockaddr_in6](addr sa)
-    s.sin6_family = type(s.sin6_family)(toInt(AF_INET6))
+    s.sin6_family = typeof(s.sin6_family)(toInt(AF_INET6))
     s.sin6_port = port
     copyMem(addr s.sin6_addr, unsafeAddr address.address_v6[0],
             sizeof(s.sin6_addr))
 
-proc fromSockAddrAux(sa: ptr Sockaddr_storage, sl: Socklen,
+proc fromSockAddrAux(sa: ptr Sockaddr_storage, sl: SockLen,
                      address: var IpAddress, port: var Port) =
-  if sa.ss_family.int == toInt(AF_INET) and sl == sizeof(Sockaddr_in).Socklen:
+  if sa.ss_family.cint == toInt(AF_INET) and sl == sizeof(Sockaddr_in).SockLen:
     address = IpAddress(family: IpAddressFamily.IPv4)
     let s = cast[ptr Sockaddr_in](sa)
     copyMem(addr address.address_v4[0], addr s.sin_addr,
             sizeof(address.address_v4))
     port = ntohs(s.sin_port).Port
-  elif sa.ss_family.int == toInt(AF_INET6) and
-       sl == sizeof(Sockaddr_in6).Socklen:
+  elif sa.ss_family.cint == toInt(AF_INET6) and
+       sl == sizeof(Sockaddr_in6).SockLen:
     address = IpAddress(family: IpAddressFamily.IPv6)
     let s = cast[ptr Sockaddr_in6](sa)
     copyMem(addr address.address_v6[0], addr s.sin6_addr,
@@ -444,47 +541,52 @@ proc fromSockAddrAux(sa: ptr Sockaddr_storage, sl: Socklen,
     raise newException(ValueError, "Neither IPv4 nor IPv6")
 
 proc fromSockAddr*(sa: Sockaddr_storage | SockAddr | Sockaddr_in | Sockaddr_in6,
-    sl: Socklen, address: var IpAddress, port: var Port) {.inline.} =
-  ## Converts `SockAddr` and `Socklen` to `IpAddress` and `Port`. Raises
-  ## `ObjectConversionError` in case of invalid `sa` and `sl` arguments.
+    sl: SockLen, address: var IpAddress, port: var Port) {.inline.} =
+  ## Converts `SockAddr` and `SockLen` to `IpAddress` and `Port`. Raises
+  ## `ObjectConversionDefect` in case of invalid `sa` and `sl` arguments.
   fromSockAddrAux(cast[ptr Sockaddr_storage](unsafeAddr sa), sl, address, port)
 
 when defineSsl:
-  CRYPTO_malloc_init()
-  doAssert SslLibraryInit() == 1
-  SslLoadErrorStrings()
-  ErrLoadBioStrings()
-  OpenSSL_add_all_algorithms()
-
-  proc raiseSSLError*(s = "") =
+  # OpenSSL >= 1.1.0 does not need explicit init.
+  when not useOpenssl3:
+    CRYPTO_malloc_init()
+    doAssert SslLibraryInit() == 1
+    SSL_load_error_strings()
+    ERR_load_BIO_strings()
+    OpenSSL_add_all_algorithms()
+
+  proc sslHandle*(self: Socket): SslPtr =
+    ## Retrieve the ssl pointer of `socket`.
+    ## Useful for interfacing with `openssl`.
+    self.sslHandle
+
+  proc raiseSSLError*(s = "") {.raises: [SslError].}=
     ## Raises a new SSL error.
     if s != "":
-      raise newException(SSLError, s)
-    let err = ErrPeekLastError()
+      raise newException(SslError, s)
+    let err = ERR_peek_last_error()
     if err == 0:
-      raise newException(SSLError, "No error reported.")
-    if err == -1:
-      raiseOSError(osLastError())
-    var errStr = $ErrErrorString(err, nil)
+      raise newException(SslError, "No error reported.")
+    var errStr = $ERR_error_string(err, nil)
     case err
     of 336032814, 336032784:
       errStr = "Please upgrade your OpenSSL library, it does not support the " &
                "necessary protocols. OpenSSL error is: " & errStr
     else:
       discard
-    raise newException(SSLError, errStr)
+    raise newException(SslError, errStr)
 
-  proc getExtraData*(ctx: SSLContext, index: int): RootRef =
-    ## Retrieves arbitrary data stored inside SSLContext.
+  proc getExtraData*(ctx: SslContext, index: int): RootRef =
+    ## Retrieves arbitrary data stored inside SslContext.
     if index notin ctx.referencedData:
-      raise newException(IndexError, "No data with that index.")
+      raise newException(IndexDefect, "No data with that index.")
     let res = ctx.context.SSL_CTX_get_ex_data(index.cint)
     if cast[int](res) == 0:
       raiseSSLError()
     return cast[RootRef](res)
 
-  proc setExtraData*(ctx: SSLContext, index: int, data: RootRef) =
-    ## Stores arbitrary data inside SSLContext. The unique `index`
+  proc setExtraData*(ctx: SslContext, index: int, data: RootRef) =
+    ## Stores arbitrary data inside SslContext. The unique `index`
     ## should be retrieved using getSslContextExtraDataIndex.
     if index in ctx.referencedData:
       GC_unref(getExtraData(ctx, index))
@@ -497,14 +599,15 @@ when defineSsl:
     GC_ref(data)
 
   # http://simplestcodings.blogspot.co.uk/2010/08/secure-server-client-using-openssl-in-c.html
-  proc loadCertificates(ctx: SSL_CTX, certFile, keyFile: string) =
-    if certFile != "" and not existsFile(certFile):
-      raise newException(system.IOError, "Certificate file could not be found: " & certFile)
-    if keyFile != "" and not existsFile(keyFile):
+  proc loadCertificates(ctx: SslCtx, certFile, keyFile: string) =
+    if certFile != "" and not fileExists(certFile):
+      raise newException(system.IOError,
+          "Certificate file could not be found: " & certFile)
+    if keyFile != "" and not fileExists(keyFile):
       raise newException(system.IOError, "Key file could not be found: " & keyFile)
 
     if certFile != "":
-      var ret = SSLCTXUseCertificateChainFile(ctx, certFile)
+      var ret = SSL_CTX_use_certificate_chain_file(ctx, certFile)
       if ret != 1:
         raiseSSLError()
 
@@ -518,88 +621,156 @@ when defineSsl:
         raiseSSLError("Verification of private key file failed.")
 
   proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer,
-                   certFile = "", keyFile = "", cipherList = "ALL"): SSLContext =
+                   certFile = "", keyFile = "", cipherList = CiphersIntermediate,
+                   caDir = "", caFile = "", ciphersuites = CiphersModern): SslContext =
     ## Creates an SSL context.
     ##
-    ## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1
-    ## are available with the addition of ``protSSLv23`` which allows for
-    ## compatibility with all of them.
+    ## Protocol version is currently ignored by default and TLS is used.
+    ## With `-d:openssl10`, only SSLv23 and TLSv1 may be used.
+    ##
+    ## There are three options for verify mode:
+    ## `CVerifyNone`: certificates are not verified;
+    ## `CVerifyPeer`: certificates are verified;
+    ## `CVerifyPeerUseEnvVars`: certificates are verified and the optional
+    ## environment variables SSL_CERT_FILE and SSL_CERT_DIR are also used to
+    ## locate certificates
     ##
-    ## There are currently only two options for verify mode;
-    ## one is ``CVerifyNone`` and with it certificates will not be verified
-    ## the other is ``CVerifyPeer`` and certificates will be verified for
-    ## it, ``CVerifyPeer`` is the safest choice.
+    ## The `nimDisableCertificateValidation` define overrides verifyMode and
+    ## disables certificate verification globally!
+    ##
+    ## CA certificates will be loaded, in the following order, from:
+    ##
+    ## - caFile, caDir, parameters, if set
+    ## - if `verifyMode` is set to `CVerifyPeerUseEnvVars`,
+    ##   the SSL_CERT_FILE and SSL_CERT_DIR environment variables are used
+    ## - a set of files and directories from the `ssl_certs <ssl_certs.html>`_ file.
     ##
     ## The last two parameters specify the certificate file path and the key file
     ## path, a server socket will most likely not work without these.
+    ##
     ## Certificates can be generated using the following command:
-    ## ``openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem``.
-    var newCTX: SSL_CTX
-    case protVersion
-    of protSSLv23:
-      newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support.
-    of protSSLv2:
-      raiseSslError("SSLv2 is no longer secure and has been deprecated, use protSSLv23")
-    of protSSLv3:
-      raiseSslError("SSLv3 is no longer secure and has been deprecated, use protSSLv23")
-    of protTLSv1:
-      newCTX = SSL_CTX_new(TLSv1_method())
-
-    if newCTX.SSLCTXSetCipherList(cipherList) != 1:
+    ## - `openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout mykey.pem -out mycert.pem`
+    ## or using ECDSA:
+    ## - `openssl ecparam -out mykey.pem -name secp256k1 -genkey`
+    ## - `openssl req -new -key mykey.pem -x509 -nodes -days 365 -out mycert.pem`
+    var mtd: PSSL_METHOD
+    when defined(openssl10):
+      case protVersion
+      of protSSLv23:
+        mtd = SSLv23_method()
+      of protSSLv2:
+        raiseSSLError("SSLv2 is no longer secure and has been deprecated, use protSSLv23")
+      of protSSLv3:
+        raiseSSLError("SSLv3 is no longer secure and has been deprecated, use protSSLv23")
+      of protTLSv1:
+        mtd = TLSv1_method()
+    else:
+      mtd = TLS_method()
+    if mtd == nil:
+      raiseSSLError("Failed to create TLS context")
+    var newCTX = SSL_CTX_new(mtd)
+    if newCTX == nil:
+      raiseSSLError("Failed to create TLS context")
+
+    if newCTX.SSL_CTX_set_cipher_list(cipherList) != 1:
       raiseSSLError()
-    case verifyMode
-    of CVerifyPeer:
-      newCTX.SSLCTXSetVerify(SSLVerifyPeer, nil)
-    of CVerifyNone:
-      newCTX.SSLCTXSetVerify(SSLVerifyNone, nil)
+    when not defined(openssl10) and not defined(libressl):
+      let sslVersion = getOpenSSLVersion()
+      if sslVersion >= 0x010101000 and sslVersion != 0x020000000:
+        # In OpenSSL >= 1.1.1, TLSv1.3 cipher suites can only be configured via
+        # this API.
+        if newCTX.SSL_CTX_set_ciphersuites(ciphersuites) != 1:
+          raiseSSLError()
+    # Automatically the best ECDH curve for client exchange. Without this, ECDH
+    # ciphers will be ignored by the server.
+    #
+    # From OpenSSL >= 1.1.0, this setting is set by default and can't be
+    # overridden.
+    if newCTX.SSL_CTX_set_ecdh_auto(1) != 1:
+      raiseSSLError()
+
+    when defined(nimDisableCertificateValidation):
+      newCTX.SSL_CTX_set_verify(SSL_VERIFY_NONE, nil)
+    else:
+      case verifyMode
+      of CVerifyPeer, CVerifyPeerUseEnvVars:
+        newCTX.SSL_CTX_set_verify(SSL_VERIFY_PEER, nil)
+      of CVerifyNone:
+        newCTX.SSL_CTX_set_verify(SSL_VERIFY_NONE, nil)
+
     if newCTX == nil:
       raiseSSLError()
 
     discard newCTX.SSLCTXSetMode(SSL_MODE_AUTO_RETRY)
     newCTX.loadCertificates(certFile, keyFile)
 
-    result = SSLContext(context: newCTX, referencedData: initSet[int](),
+    const VerifySuccess = 1 # SSL_CTX_load_verify_locations returns 1 on success.
+
+    when not defined(nimDisableCertificateValidation):
+      if verifyMode != CVerifyNone:
+        # Use the caDir and caFile parameters if set
+        if caDir != "" or caFile != "":
+          if newCTX.SSL_CTX_load_verify_locations(if caFile == "": nil else: caFile.cstring, if caDir == "": nil else: caDir.cstring) != VerifySuccess:
+            raise newException(IOError, "Failed to load SSL/TLS CA certificate(s).")
+
+        else:
+          # Scan for certs in known locations. For CVerifyPeerUseEnvVars also scan
+          # the SSL_CERT_FILE and SSL_CERT_DIR env vars
+          var found = false
+          let useEnvVars = (if verifyMode == CVerifyPeerUseEnvVars: true else: false)
+          for fn in scanSSLCertificates(useEnvVars = useEnvVars):
+            if fn.extractFilename == "":
+              if newCTX.SSL_CTX_load_verify_locations(nil, cstring(fn.normalizePathEnd(false))) == VerifySuccess:
+                found = true
+                break
+            elif newCTX.SSL_CTX_load_verify_locations(cstring(fn), nil) == VerifySuccess:
+              found = true
+              break
+          if not found:
+            raise newException(IOError, "No SSL/TLS CA certificates found.")
+
+    result = SslContext(context: newCTX, referencedData: initHashSet[int](),
       extraInternal: new(SslContextExtraInternal))
 
-  proc getExtraInternal(ctx: SSLContext): SslContextExtraInternal =
+  proc getExtraInternal(ctx: SslContext): SslContextExtraInternal =
     return ctx.extraInternal
 
-  proc destroyContext*(ctx: SSLContext) =
-    ## Free memory referenced by SSLContext.
+  proc destroyContext*(ctx: SslContext) =
+    ## Free memory referenced by SslContext.
 
     # We assume here that OpenSSL's internal indexes increase by 1 each time.
     # That means we can assume that the next internal index is the length of
     # extra data indexes.
     for i in ctx.referencedData:
-      GC_unref(getExtraData(ctx, i).RootRef)
+      GC_unref(getExtraData(ctx, i))
     ctx.context.SSL_CTX_free()
 
-  proc `pskIdentityHint=`*(ctx: SSLContext, hint: string) =
+  proc `pskIdentityHint=`*(ctx: SslContext, hint: string) =
     ## Sets the identity hint passed to server.
     ##
     ## Only used in PSK ciphersuites.
     if ctx.context.SSL_CTX_use_psk_identity_hint(hint) <= 0:
       raiseSSLError()
 
-  proc clientGetPskFunc*(ctx: SSLContext): SslClientGetPskFunc =
+  proc clientGetPskFunc*(ctx: SslContext): SslClientGetPskFunc =
     return ctx.getExtraInternal().clientGetPskFunc
 
-  proc pskClientCallback(ssl: SslPtr; hint: cstring; identity: cstring; max_identity_len: cuint; psk: ptr cuchar;
-    max_psk_len: cuint): cuint {.cdecl.} =
-    let ctx = SSLContext(context: ssl.SSL_get_SSL_CTX)
-    let hintString = if hint == nil: nil else: $hint
+  proc pskClientCallback(ssl: SslPtr; hint: cstring; identity: cstring;
+      max_identity_len: cuint; psk: ptr uint8;
+      max_psk_len: cuint): cuint {.cdecl.} =
+    let ctx = SslContext(context: ssl.SSL_get_SSL_CTX)
+    let hintString = if hint == nil: "" else: $hint
     let (identityString, pskString) = (ctx.clientGetPskFunc)(hintString)
-    if psk.len.cuint > max_psk_len:
+    if pskString.len.cuint > max_psk_len:
       return 0
     if identityString.len.cuint >= max_identity_len:
       return 0
-
-    copyMem(identity, identityString.cstring, pskString.len + 1) # with the last zero byte
+    copyMem(identity, identityString.cstring, identityString.len + 1) # with the last zero byte
     copyMem(psk, pskString.cstring, pskString.len)
 
     return pskString.len.cuint
 
-  proc `clientGetPskFunc=`*(ctx: SSLContext, fun: SslClientGetPskFunc) =
+  proc `clientGetPskFunc=`*(ctx: SslContext, fun: SslClientGetPskFunc) =
     ## Sets function that returns the client identity and the PSK based on identity
     ## hint from the server.
     ##
@@ -608,19 +779,20 @@ when defineSsl:
     ctx.context.SSL_CTX_set_psk_client_callback(
         if fun == nil: nil else: pskClientCallback)
 
-  proc serverGetPskFunc*(ctx: SSLContext): SslServerGetPskFunc =
+  proc serverGetPskFunc*(ctx: SslContext): SslServerGetPskFunc =
     return ctx.getExtraInternal().serverGetPskFunc
 
-  proc pskServerCallback(ssl: SslCtx; identity: cstring; psk: ptr cuchar; max_psk_len: cint): cuint {.cdecl.} =
-    let ctx = SSLContext(context: ssl.SSL_get_SSL_CTX)
+  proc pskServerCallback(ssl: SslCtx; identity: cstring; psk: ptr uint8;
+      max_psk_len: cint): cuint {.cdecl.} =
+    let ctx = SslContext(context: ssl.SSL_get_SSL_CTX)
     let pskString = (ctx.serverGetPskFunc)($identity)
-    if psk.len.cint > max_psk_len:
+    if pskString.len.cint > max_psk_len:
       return 0
     copyMem(psk, pskString.cstring, pskString.len)
 
     return pskString.len.cuint
 
-  proc `serverGetPskFunc=`*(ctx: SSLContext, fun: SslServerGetPskFunc) =
+  proc `serverGetPskFunc=`*(ctx: SslContext, fun: SslServerGetPskFunc) =
     ## Sets function that returns PSK based on the client identity.
     ##
     ## Only used in PSK ciphersuites.
@@ -630,59 +802,137 @@ when defineSsl:
 
   proc getPskIdentity*(socket: Socket): string =
     ## Gets the PSK identity provided by the client.
-    assert socket.isSSL
+    assert socket.isSsl
     return $(socket.sslHandle.SSL_get_psk_identity)
 
-  proc wrapSocket*(ctx: SSLContext, socket: Socket) =
+  proc wrapSocket*(ctx: SslContext, socket: Socket) =
     ## Wraps a socket in an SSL context. This function effectively turns
-    ## ``socket`` into an SSL socket.
+    ## `socket` into an SSL socket.
     ##
     ## This must be called on an unconnected socket; an SSL session will
     ## be started when the socket is connected.
     ##
+    ## FIXME:
     ## **Disclaimer**: This code is not well tested, may be very unsafe and
     ## prone to security vulnerabilities.
 
-    assert(not socket.isSSL)
-    socket.isSSL = true
+    assert(not socket.isSsl)
+    socket.isSsl = true
     socket.sslContext = ctx
-    socket.sslHandle = SSLNew(socket.sslContext.context)
+    socket.sslHandle = SSL_new(socket.sslContext.context)
     socket.sslNoHandshake = false
     socket.sslHasPeekChar = false
+    socket.sslNoShutdown = false
     if socket.sslHandle == nil:
       raiseSSLError()
 
-    if SSLSetFd(socket.sslHandle, socket.fd) != 1:
+    if SSL_set_fd(socket.sslHandle, socket.fd) != 1:
       raiseSSLError()
 
-  proc wrapConnectedSocket*(ctx: SSLContext, socket: Socket,
+  proc checkCertName(socket: Socket, hostname: string) {.raises: [SslError], tags:[RootEffect].} =
+    ## Check if the certificate Subject Alternative Name (SAN) or Subject CommonName (CN) matches hostname.
+    ## Wildcards match only in the left-most label.
+    ## When name starts with a dot it will be matched by a certificate valid for any subdomain
+    when not defined(nimDisableCertificateValidation) and not defined(windows):
+      assert socket.isSsl
+      try:
+        let certificate = socket.sslHandle.SSL_get_peer_certificate()
+        if certificate.isNil:
+          raiseSSLError("No SSL certificate found.")
+
+        const X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT = 0x1.cuint
+        # https://www.openssl.org/docs/man1.1.1/man3/X509_check_host.html
+        let match = certificate.X509_check_host(hostname.cstring, hostname.len.cint,
+          X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, nil)
+        # https://www.openssl.org/docs/man1.1.1/man3/SSL_get_peer_certificate.html
+        X509_free(certificate)
+        if match != 1:
+          raiseSSLError("SSL Certificate check failed.")
+
+      except LibraryError:
+        raiseSSLError("SSL import failed")
+
+  proc wrapConnectedSocket*(ctx: SslContext, socket: Socket,
                             handshake: SslHandshakeType,
-                            hostname: string = nil) =
+                            hostname: string = "") =
     ## Wraps a connected socket in an SSL context. This function effectively
-    ## turns ``socket`` into an SSL socket.
-    ## ``hostname`` should be specified so that the client knows which hostname
+    ## turns `socket` into an SSL socket.
+    ## `hostname` should be specified so that the client knows which hostname
     ## the server certificate should be validated against.
     ##
     ## This should be called on a connected socket, and will perform
     ## an SSL handshake immediately.
     ##
+    ## FIXME:
     ## **Disclaimer**: This code is not well tested, may be very unsafe and
     ## prone to security vulnerabilities.
     wrapSocket(ctx, socket)
     case handshake
     of handshakeAsClient:
-      if not hostname.isNil and not isIpAddress(hostname):
+      if hostname.len > 0 and not isIpAddress(hostname):
         # Discard result in case OpenSSL version doesn't support SNI, or we're
         # not using TLSv1+
         discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)
-      let ret = SSLConnect(socket.sslHandle)
+      ErrClearError()
+      let ret = SSL_connect(socket.sslHandle)
       socketError(socket, ret)
+      when not defined(nimDisableCertificateValidation) and not defined(windows):
+        # FIXME: this should be skipped on CVerifyNone
+        if hostname.len > 0 and not isIpAddress(hostname):
+          socket.checkCertName(hostname)
     of handshakeAsServer:
-      let ret = SSLAccept(socket.sslHandle)
+      ErrClearError()
+      let ret = SSL_accept(socket.sslHandle)
       socketError(socket, ret)
 
+  proc getPeerCertificates*(sslHandle: SslPtr): seq[Certificate] {.since: (1, 1).} =
+    ## Returns the certificate chain received by the peer we are connected to
+    ## through the OpenSSL connection represented by `sslHandle`.
+    ## The handshake must have been completed and the certificate chain must
+    ## have been verified successfully or else an empty sequence is returned.
+    ## The chain is ordered from leaf certificate to root certificate.
+    result = newSeq[Certificate]()
+    if SSL_get_verify_result(sslHandle) != X509_V_OK:
+      return
+    let stack = SSL_get0_verified_chain(sslHandle)
+    if stack == nil:
+      return
+    let length = OPENSSL_sk_num(stack)
+    if length == 0:
+      return
+    for i in 0 .. length - 1:
+      let x509 = cast[PX509](OPENSSL_sk_value(stack, i))
+      result.add(i2d_X509(x509))
+
+  proc getPeerCertificates*(socket: Socket): seq[Certificate] {.since: (1, 1).} =
+    ## Returns the certificate chain received by the peer we are connected to
+    ## through the given socket.
+    ## The handshake must have been completed and the certificate chain must
+    ## have been verified successfully or else an empty sequence is returned.
+    ## The chain is ordered from leaf certificate to root certificate.
+    if not socket.isSsl:
+      result = newSeq[Certificate]()
+    else:
+      result = getPeerCertificates(socket.sslHandle)
+
+  proc `sessionIdContext=`*(ctx: SslContext, sidCtx: string) =
+    ## Sets the session id context in which a session can be reused.
+    ## Used for permitting clients to reuse a session id instead of
+    ## doing a new handshake.
+    ##
+    ## TLS clients might attempt to resume a session using the session id context,
+    ## thus it must be set if verifyMode is set to CVerifyPeer or CVerifyPeerUseEnvVars,
+    ## otherwise the connection will fail and SslError will be raised if resumption occurs.
+    ##
+    ## - Only useful if set server-side.
+    ## - Should be unique per-application to prevent clients from malfunctioning.
+    ## - sidCtx must be at most 32 characters in length.
+    if sidCtx.len > 32:
+      raiseSSLError("sessionIdContext must be shorter than 32 characters")
+    SSL_CTX_set_session_id_context(ctx.context, sidCtx, sidCtx.len)
+
 proc getSocketError*(socket: Socket): OSErrorCode =
-  ## Checks ``osLastError`` for a valid error. If it has been reset it uses
+  ## Checks `osLastError` for a valid error. If it has been reset it uses
   ## the last error stored in the socket object.
   result = osLastError()
   if result == 0.OSErrorCode:
@@ -691,18 +941,22 @@ proc getSocketError*(socket: Socket): OSErrorCode =
     raiseOSError(result, "No valid socket error code available")
 
 proc socketError*(socket: Socket, err: int = -1, async = false,
-                  lastError = (-1).OSErrorCode) =
-  ## Raises an OSError based on the error code returned by ``SSLGetError``
-  ## (for SSL sockets) and ``osLastError`` otherwise.
+                  lastError = (-1).OSErrorCode,
+                  flags: set[SocketFlag] = {}) =
+  ## Raises an OSError based on the error code returned by `SSL_get_error`
+  ## (for SSL sockets) and `osLastError` otherwise.
   ##
-  ## If ``async`` is ``true`` no error will be thrown in the case when the
+  ## If `async` is `true` no error will be thrown in the case when the
   ## error was caused by no data being available to be read.
   ##
-  ## If ``err`` is not lower than 0 no exception will be raised.
+  ## If `err` is not lower than 0 no exception will be raised.
+  ##
+  ## If `flags` contains `SafeDisconn`, no exception will be raised
+  ## when the error was caused by a peer disconnection.
   when defineSsl:
-    if socket.isSSL:
+    if socket.isSsl:
       if err <= 0:
-        var ret = SSLGetError(socket.sslHandle, err.cint)
+        var ret = SSL_get_error(socket.sslHandle, err.cint)
         case ret
         of SSL_ERROR_ZERO_RETURN:
           raiseSSLError("TLS/SSL connection failed to initiate, socket closed prematurely.")
@@ -717,95 +971,100 @@ proc socketError*(socket: Socket, err: int = -1, async = false,
         of SSL_ERROR_WANT_X509_LOOKUP:
           raiseSSLError("Function for x509 lookup has been called.")
         of SSL_ERROR_SYSCALL:
-          var errStr = "IO error has occurred "
-          let sslErr = ErrPeekLastError()
-          if sslErr == 0 and err == 0:
-            errStr.add "because an EOF was observed that violates the protocol"
-          elif sslErr == 0 and err == -1:
-            errStr.add "in the BIO layer"
-          else:
-            let errStr = $ErrErrorString(sslErr, nil)
-            raiseSSLError(errStr & ": " & errStr)
+          # SSL shutdown must not be done if a fatal error occurred.
+          socket.sslNoShutdown = true
           let osErr = osLastError()
-          raiseOSError(osErr, errStr)
+          if not flags.isDisconnectionError(osErr):
+            var errStr = "IO error has occurred "
+            let sslErr = ERR_peek_last_error()
+            if sslErr == 0 and err == 0:
+              errStr.add "because an EOF was observed that violates the protocol"
+            elif sslErr == 0 and err == -1:
+              errStr.add "in the BIO layer"
+            else:
+              let errStr = $ERR_error_string(sslErr, nil)
+              raiseSSLError(errStr & ": " & errStr)
+            raiseOSError(osErr, errStr)
         of SSL_ERROR_SSL:
+          # SSL shutdown must not be done if a fatal error occurred.
+          socket.sslNoShutdown = true
           raiseSSLError()
         else: raiseSSLError("Unknown Error")
 
-  if err == -1 and not (when defineSsl: socket.isSSL else: false):
+  if err == -1 and not (when defineSsl: socket.isSsl else: false):
     var lastE = if lastError.int == -1: getSocketError(socket) else: lastError
-    if async:
-      when useWinVersion:
-        if lastE.int32 == WSAEWOULDBLOCK:
-          return
-        else: raiseOSError(lastE)
-      else:
-        if lastE.int32 == EAGAIN or lastE.int32 == EWOULDBLOCK:
-          return
-        else: raiseOSError(lastE)
-    else: raiseOSError(lastE)
+    if not flags.isDisconnectionError(lastE):
+      if async:
+        when useWinVersion:
+          if lastE.int32 == WSAEWOULDBLOCK:
+            return
+          else: raiseOSError(lastE)
+        else:
+          if lastE.int32 == EAGAIN or lastE.int32 == EWOULDBLOCK:
+            return
+          else: raiseOSError(lastE)
+      else: raiseOSError(lastE)
 
 proc listen*(socket: Socket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
-  ## Marks ``socket`` as accepting connections.
-  ## ``Backlog`` specifies the maximum length of the
+  ## Marks `socket` as accepting connections.
+  ## `Backlog` specifies the maximum length of the
   ## queue of pending connections.
   ##
-  ## Raises an EOS error upon failure.
+  ## Raises an OSError error upon failure.
   if nativesockets.listen(socket.fd, backlog) < 0'i32:
     raiseOSError(osLastError())
 
 proc bindAddr*(socket: Socket, port = Port(0), address = "") {.
   tags: [ReadIOEffect].} =
-  ## Binds ``address``:``port`` to the socket.
+  ## Binds `address`:`port` to the socket.
   ##
-  ## If ``address`` is "" then ADDR_ANY will be bound.
-
-  if address == "":
-    var name: Sockaddr_in
-    when useWinVersion:
-      name.sin_family = toInt(AF_INET).int16
+  ## If `address` is "" then ADDR_ANY will be bound.
+  var realaddr = address
+  if realaddr == "":
+    case socket.domain
+    of AF_INET6: realaddr = "::"
+    of AF_INET: realaddr = "0.0.0.0"
     else:
-      name.sin_family = toInt(AF_INET)
-    name.sin_port = htons(port.uint16)
-    name.sin_addr.s_addr = htonl(INADDR_ANY)
-    if bindAddr(socket.fd, cast[ptr SockAddr](addr(name)),
-                  sizeof(name).SockLen) < 0'i32:
-      raiseOSError(osLastError())
-  else:
-    var aiList = getAddrInfo(address, port, socket.domain)
-    if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32:
-      freeAddrInfo(aiList)
-      raiseOSError(osLastError())
+      raise newException(ValueError,
+        "Unknown socket address family and no address specified to bindAddr")
+
+  var aiList = getAddrInfo(realaddr, port, socket.domain)
+  if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32:
     freeAddrInfo(aiList)
+    var address2: string
+    address2.addQuoted address
+    raiseOSError(osLastError(), "address: $# port: $#" % [address2, $port])
+  freeAddrInfo(aiList)
 
-proc acceptAddr*(server: Socket, client: var Socket, address: var string,
-                 flags = {SocketFlag.SafeDisconn}) {.
-                 tags: [ReadIOEffect], gcsafe, locks: 0.} =
+proc acceptAddr*(server: Socket, client: var owned(Socket), address: var string,
+                 flags = {SocketFlag.SafeDisconn},
+                 inheritable = defined(nimInheritHandles)) {.
+                 tags: [ReadIOEffect], gcsafe.} =
   ## Blocks until a connection is being made from a client. When a connection
-  ## is made sets ``client`` to the client socket and ``address`` to the address
+  ## is made sets `client` to the client socket and `address` to the address
   ## of the connecting client.
-  ## This function will raise EOS if an error occurs.
+  ## This function will raise OSError if an error occurs.
   ##
   ## The resulting client will inherit any properties of the server socket. For
   ## example: whether the socket is buffered or not.
   ##
-  ## **Note**: ``client`` must be initialised (with ``new``), this function
-  ## makes no effort to initialise the ``client`` variable.
+  ## The SocketHandle associated with the resulting client will not be
+  ## inheritable by child processes by default. This can be changed via
+  ## the `inheritable` parameter.
   ##
-  ## The ``accept`` call may result in an error if the connecting socket
-  ## disconnects during the duration of the ``accept``. If the ``SafeDisconn``
+  ## The `accept` call may result in an error if the connecting socket
+  ## disconnects during the duration of the `accept`. If the `SafeDisconn`
   ## flag is specified then this error will not be raised and instead
   ## accept will be called again.
-  assert(client != nil)
-  assert client.fd.int <= 0, "Client socket needs to be initialised with " &
-                             "`new`, not `newSocket`."
-  let ret = accept(server.fd)
+  if client.isNil:
+    new(client)
+  let ret = accept(server.fd, inheritable)
   let sock = ret[0]
 
   if sock == osInvalidSocket:
     let err = osLastError()
     if flags.isDisconnectionError(err):
-      acceptAddr(server, client, address, flags)
+      acceptAddr(server, client, address, flags, inheritable)
     raiseOSError(err)
   else:
     address = ret[1]
@@ -815,42 +1074,44 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string,
 
     # Handle SSL.
     when defineSsl:
-      if server.isSSL:
+      if server.isSsl:
         # We must wrap the client sock in a ssl context.
 
         server.sslContext.wrapSocket(client)
-        let ret = SSLAccept(client.sslHandle)
+        ErrClearError()
+        let ret = SSL_accept(client.sslHandle)
         socketError(client, ret, false)
 
 when false: #defineSsl:
   proc acceptAddrSSL*(server: Socket, client: var Socket,
-                      address: var string): SSLAcceptResult {.
+                      address: var string): SSL_acceptResult {.
                       tags: [ReadIOEffect].} =
     ## This procedure should only be used for non-blocking **SSL** sockets.
     ## It will immediately return with one of the following values:
     ##
-    ## ``AcceptSuccess`` will be returned when a client has been successfully
+    ## `AcceptSuccess` will be returned when a client has been successfully
     ## accepted and the handshake has been successfully performed between
-    ## ``server`` and the newly connected client.
+    ## `server` and the newly connected client.
     ##
-    ## ``AcceptNoHandshake`` will be returned when a client has been accepted
+    ## `AcceptNoHandshake` will be returned when a client has been accepted
     ## but no handshake could be performed. This can happen when the client
     ## connects but does not yet initiate a handshake. In this case
-    ## ``acceptAddrSSL`` should be called again with the same parameters.
+    ## `acceptAddrSSL` should be called again with the same parameters.
     ##
-    ## ``AcceptNoClient`` will be returned when no client is currently attempting
+    ## `AcceptNoClient` will be returned when no client is currently attempting
     ## to connect.
     template doHandshake(): untyped =
       when defineSsl:
-        if server.isSSL:
+        if server.isSsl:
           client.setBlocking(false)
           # We must wrap the client sock in a ssl context.
 
-          if not client.isSSL or client.sslHandle == nil:
+          if not client.isSsl or client.sslHandle == nil:
             server.sslContext.wrapSocket(client)
-          let ret = SSLAccept(client.sslHandle)
+          ErrClearError()
+          let ret = SSL_accept(client.sslHandle)
           while ret <= 0:
-            let err = SSLGetError(client.sslHandle, ret)
+            let err = SSL_get_error(client.sslHandle, ret)
             if err != SSL_ERROR_WANT_ACCEPT:
               case err
               of SSL_ERROR_ZERO_RETURN:
@@ -867,59 +1128,152 @@ when false: #defineSsl:
                 raiseSSLError("Unknown error")
           client.sslNoHandshake = false
 
-    if client.isSSL and client.sslNoHandshake:
+    if client.isSsl and client.sslNoHandshake:
       doHandshake()
       return AcceptSuccess
     else:
       acceptAddrPlain(AcceptNoClient, AcceptSuccess):
         doHandshake()
 
-proc accept*(server: Socket, client: var Socket,
-             flags = {SocketFlag.SafeDisconn}) {.tags: [ReadIOEffect].} =
-  ## Equivalent to ``acceptAddr`` but doesn't return the address, only the
+proc accept*(server: Socket, client: var owned(Socket),
+             flags = {SocketFlag.SafeDisconn},
+             inheritable = defined(nimInheritHandles))
+            {.tags: [ReadIOEffect].} =
+  ## Equivalent to `acceptAddr` but doesn't return the address, only the
   ## socket.
   ##
-  ## **Note**: ``client`` must be initialised (with ``new``), this function
-  ## makes no effort to initialise the ``client`` variable.
+  ## The SocketHandle associated with the resulting client will not be
+  ## inheritable by child processes by default. This can be changed via
+  ## the `inheritable` parameter.
   ##
-  ## The ``accept`` call may result in an error if the connecting socket
-  ## disconnects during the duration of the ``accept``. If the ``SafeDisconn``
+  ## The `accept` call may result in an error if the connecting socket
+  ## disconnects during the duration of the `accept`. If the `SafeDisconn`
   ## flag is specified then this error will not be raised and instead
   ## accept will be called again.
   var addrDummy = ""
   acceptAddr(server, client, addrDummy, flags)
 
-proc close*(socket: Socket) =
+when defined(posix) and not defined(lwip):
+  from std/posix import Sigset, sigwait, sigismember, sigemptyset, sigaddset,
+    sigprocmask, pthread_sigmask, SIGPIPE, SIG_BLOCK, SIG_UNBLOCK
+
+template blockSigpipe(body: untyped): untyped =
+  ## Temporary block SIGPIPE within the provided code block. If SIGPIPE is
+  ## raised for the duration of the code block, it will be queued and will be
+  ## raised once the block ends.
+  ##
+  ## Within the block a `selectSigpipe()` template is provided which can be
+  ## used to remove SIGPIPE from the queue. Note that if SIGPIPE is **not**
+  ## raised at the time of call, it will block until SIGPIPE is raised.
+  ##
+  ## If SIGPIPE has already been blocked at the time of execution, the
+  ## signal mask is left as-is and `selectSigpipe()` will become a no-op.
+  ##
+  ## For convenience, this template is also available for non-POSIX system,
+  ## where `body` will be executed as-is.
+  when not defined(posix) or defined(lwip):
+    body
+  else:
+    template sigmask(how: cint, set, oset: var Sigset): untyped {.gensym.} =
+      ## Alias for pthread_sigmask or sigprocmask depending on the status
+      ## of --threads
+      when compileOption("threads"):
+        pthread_sigmask(how, set, oset)
+      else:
+        sigprocmask(how, set, oset)
+
+    var oldSet, watchSet: Sigset
+    if sigemptyset(oldSet) == -1:
+      raiseOSError(osLastError())
+    if sigemptyset(watchSet) == -1:
+      raiseOSError(osLastError())
+
+    if sigaddset(watchSet, SIGPIPE) == -1:
+      raiseOSError(osLastError(), "Couldn't add SIGPIPE to Sigset")
+
+    if sigmask(SIG_BLOCK, watchSet, oldSet) == -1:
+      raiseOSError(osLastError(), "Couldn't block SIGPIPE")
+
+    let alreadyBlocked = sigismember(oldSet, SIGPIPE) == 1
+
+    template selectSigpipe(): untyped {.used.} =
+      if not alreadyBlocked:
+        var signal: cint
+        let err = sigwait(watchSet, signal)
+        if err != 0:
+          raiseOSError(err.OSErrorCode, "Couldn't select SIGPIPE")
+        assert signal == SIGPIPE
+
+    try:
+      body
+    finally:
+      if not alreadyBlocked:
+        if sigmask(SIG_UNBLOCK, watchSet, oldSet) == -1:
+          raiseOSError(osLastError(), "Couldn't unblock SIGPIPE")
+
+proc close*(socket: Socket, flags = {SocketFlag.SafeDisconn}) =
   ## Closes a socket.
+  ##
+  ## If `socket` is an SSL/TLS socket, this proc will also send a closure
+  ## notification to the peer. If `SafeDisconn` is in `flags`, failure to do so
+  ## due to disconnections will be ignored. This is generally safe in
+  ## practice. See
+  ## `here <https://security.stackexchange.com/a/82044>`_ for more details.
   try:
     when defineSsl:
-      if socket.isSSL and socket.sslHandle != nil:
-        ErrClearError()
-        # As we are closing the underlying socket immediately afterwards,
-        # it is valid, under the TLS standard, to perform a unidirectional
-        # shutdown i.e not wait for the peers "close notify" alert with a second
-        # call to SSLShutdown
-        let res = SSLShutdown(socket.sslHandle)
-        if res == 0:
-          discard
-        elif res != 1:
-          socketError(socket, res)
+      if socket.isSsl and socket.sslHandle != nil:
+        # Don't call SSL_shutdown if the connection has not been fully
+        # established, see:
+        # https://github.com/openssl/openssl/issues/710#issuecomment-253897666
+        if not socket.sslNoShutdown and SSL_in_init(socket.sslHandle) == 0:
+          # As we are closing the underlying socket immediately afterwards,
+          # it is valid, under the TLS standard, to perform a unidirectional
+          # shutdown i.e not wait for the peers "close notify" alert with a second
+          # call to SSL_shutdown
+          blockSigpipe:
+            ErrClearError()
+            let res = SSL_shutdown(socket.sslHandle)
+            if res == 0:
+              discard
+            elif res != 1:
+              let
+                err = osLastError()
+                sslError = SSL_get_error(socket.sslHandle, res)
+
+              # If a close notification is received, failures outside of the
+              # protocol will be returned as SSL_ERROR_ZERO_RETURN instead
+              # of SSL_ERROR_SYSCALL. This fact is deduced by digging into
+              # SSL_get_error() source code.
+              if sslError == SSL_ERROR_ZERO_RETURN or
+                 sslError == SSL_ERROR_SYSCALL:
+                when defined(posix) and not defined(macosx) and
+                     not defined(nimdoc):
+                  if err == EPIPE.OSErrorCode:
+                    # Clear the SIGPIPE that's been raised due to
+                    # the disconnection.
+                    selectSigpipe()
+                else:
+                  discard
+                if not flags.isDisconnectionError(err):
+                  socketError(socket, res, lastError = err, flags = flags)
+              else:
+                socketError(socket, res, lastError = err, flags = flags)
   finally:
     when defineSsl:
-      if socket.isSSL and socket.sslHandle != nil:
-        SSLFree(socket.sslHandle)
+      if socket.isSsl and socket.sslHandle != nil:
+        SSL_free(socket.sslHandle)
         socket.sslHandle = nil
 
     socket.fd.close()
     socket.fd = osInvalidSocket
 
 when defined(posix):
-  from posix import TCP_NODELAY
+  from std/posix import TCP_NODELAY
 else:
-  from winlean import TCP_NODELAY
+  from std/winlean import TCP_NODELAY
 
 proc toCInt*(opt: SOBool): cint =
-  ## Converts a ``SOBool`` into its Socket Option cint representation.
+  ## Converts a `SOBool` into its Socket Option cint representation.
   case opt
   of OptAcceptConn: SO_ACCEPTCONN
   of OptBroadcast: SO_BROADCAST
@@ -933,7 +1287,7 @@ proc toCInt*(opt: SOBool): cint =
 
 proc getSockOpt*(socket: Socket, opt: SOBool, level = SOL_SOCKET): bool {.
   tags: [ReadIOEffect].} =
-  ## Retrieves option ``opt`` as a boolean value.
+  ## Retrieves option `opt` as a boolean value.
   var res = getSockOptInt(socket.fd, cint(level), toCInt(opt))
   result = res != 0
 
@@ -943,39 +1297,31 @@ proc getLocalAddr*(socket: Socket): (string, Port) =
   ## This is high-level interface for `getsockname`:idx:.
   getLocalAddr(socket.fd, socket.domain)
 
-proc getPeerAddr*(socket: Socket): (string, Port) =
-  ## Get the socket's peer address and port number.
-  ##
-  ## This is high-level interface for `getpeername`:idx:.
-  getPeerAddr(socket.fd, socket.domain)
-
-proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {.
-  tags: [WriteIOEffect].} =
-  ## Sets option ``opt`` to a boolean value specified by ``value``.
-  ##
-  ## .. code-block:: Nim
-  ##   var socket = newSocket()
-  ##   socket.setSockOpt(OptReusePort, true)
-  ##   socket.setSockOpt(OptNoDelay, true, level=IPPROTO_TCP.toInt)
-  ##
+when not useNimNetLite:
+  proc getPeerAddr*(socket: Socket): (string, Port) =
+    ## Get the socket's peer address and port number.
+    ##
+    ## This is high-level interface for `getpeername`:idx:.
+    getPeerAddr(socket.fd, socket.domain)
+
+proc setSockOpt*(socket: Socket, opt: SOBool, value: bool,
+    level = SOL_SOCKET) {.tags: [WriteIOEffect].} =
+  ## Sets option `opt` to a boolean value specified by `value`.
+  runnableExamples("-r:off"):
+    let socket = newSocket()
+    socket.setSockOpt(OptReusePort, true)
+    socket.setSockOpt(OptNoDelay, true, level = IPPROTO_TCP.cint)
   var valuei = cint(if value: 1 else: 0)
   setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei)
 
-when defined(posix) and not defined(nimdoc):
-  proc makeUnixAddr(path: string): Sockaddr_un =
-    result.sun_family = AF_UNIX.toInt
-    if path.len >= Sockaddr_un_path_length:
-      raise newException(ValueError, "socket path too long")
-    copyMem(addr result.sun_path, path.cstring, path.len + 1)
-
-when defined(posix) or defined(nimdoc):
+when defined(nimdoc) or (defined(posix) and not useNimNetLite):
   proc connectUnix*(socket: Socket, path: string) =
     ## Connects to Unix socket on `path`.
     ## This only works on Unix-style systems: Mac OS X, BSD and Linux
     when not defined(nimdoc):
       var socketAddr = makeUnixAddr(path)
       if socket.fd.connect(cast[ptr SockAddr](addr socketAddr),
-                        sizeof(socketAddr).Socklen) != 0'i32:
+          (offsetOf(socketAddr, sun_path) + path.len + 1).SockLen) != 0'i32:
         raiseOSError(osLastError())
 
   proc bindUnix*(socket: Socket, path: string) =
@@ -984,49 +1330,16 @@ when defined(posix) or defined(nimdoc):
     when not defined(nimdoc):
       var socketAddr = makeUnixAddr(path)
       if socket.fd.bindAddr(cast[ptr SockAddr](addr socketAddr),
-                            sizeof(socketAddr).Socklen) != 0'i32:
+          (offsetOf(socketAddr, sun_path) + path.len + 1).SockLen) != 0'i32:
         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 ESSL 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.")
-
+when defineSsl:
   proc gotHandshake*(socket: Socket): bool =
-    ## Determines whether a handshake has occurred between a client (``socket``)
-    ## and the server that ``socket`` is connected to.
+    ## Determines whether a handshake has occurred between a client (`socket`)
+    ## and the server that `socket` is connected to.
     ##
-    ## Throws ESSL if ``socket`` is not an SSL socket.
-    if socket.isSSL:
+    ## Throws SslError if `socket` is not an SSL socket.
+    if socket.isSsl:
       return not socket.sslNoHandshake
     else:
       raiseSSLError("Socket is not an SSL socket.")
@@ -1038,17 +1351,9 @@ proc hasDataBuffered*(s: Socket): bool =
     result = s.bufLen > 0 and s.currPos != s.bufLen
 
   when defineSsl:
-    if s.isSSL and not result:
+    if s.isSsl and not result:
       result = s.sslHasPeekChar
 
-proc select(readfd: Socket, timeout = 500): int =
-  ## Used for socket operation timeouts.
-  if readfd.hasDataBuffered:
-    return 1
-
-  var fds = @[readfd.fd]
-  result = select(fds, timeout)
-
 proc isClosed(socket: Socket): bool =
   socket.fd == osInvalidSocket
 
@@ -1061,7 +1366,8 @@ proc uniRecv(socket: Socket, buffer: pointer, size, flags: cint): int =
   assert(not socket.isClosed, "Cannot `recv` on a closed socket")
   when defineSsl:
     if socket.isSsl:
-      return SSLRead(socket.sslHandle, buffer, size)
+      ErrClearError()
+      return SSL_read(socket.sslHandle, buffer, size)
 
   return recv(socket.fd, buffer, size, flags)
 
@@ -1087,11 +1393,12 @@ template retRead(flags, readBytes: int) {.dirty.} =
     else:
       return res
 
-proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ReadIOEffect].} =
+proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [
+    ReadIOEffect].} =
   ## Receives data from a socket.
   ##
   ## **Note**: This is a low-level function, you may be interested in the higher
-  ## level versions of this function which are also named ``recv``.
+  ## level versions of this function which are also named `recv`.
   if size == 0: return
   if socket.isBuffered:
     if socket.bufLen == 0:
@@ -1112,7 +1419,7 @@ proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ReadIOEffect]
     result = read
   else:
     when defineSsl:
-      if socket.isSSL:
+      if socket.isSsl:
         if socket.sslHasPeekChar: # TODO: Merge this peek char mess into uniRecv
           copyMem(data, addr(socket.sslPeekChar), 1)
           socket.sslHasPeekChar = false
@@ -1131,44 +1438,47 @@ proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ReadIOEffect]
       # Save the error in case it gets reset.
       socket.lastError = osLastError()
 
-proc waitFor(socket: Socket, waited: var float, timeout, size: int,
+proc waitFor(socket: Socket, waited: var Duration, timeout, size: int,
              funcName: string): int {.tags: [TimeEffect].} =
   ## determines the amount of characters that can be read. Result will never
-  ## be larger than ``size``. For unbuffered sockets this will be ``1``.
-  ## For buffered sockets it can be as big as ``BufferSize``.
+  ## be larger than `size`. For unbuffered sockets this will be `1`.
+  ## For buffered sockets it can be as big as `BufferSize`.
   ##
   ## If this function does not determine that there is data on the socket
-  ## within ``timeout`` ms, an ETimeout error will be raised.
+  ## within `timeout` ms, a TimeoutError error will be raised.
   result = 1
   if size <= 0: assert false
   if timeout == -1: return size
-  if socket.isBuffered and socket.bufLen != 0 and socket.bufLen != socket.currPos:
+  if socket.isBuffered and socket.bufLen != 0 and
+      socket.bufLen != socket.currPos:
     result = socket.bufLen - socket.currPos
     result = min(result, size)
   else:
-    if timeout - int(waited * 1000.0) < 1:
+    if timeout - waited.inMilliseconds < 1:
       raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
 
     when defineSsl:
-      if socket.isSSL:
+      if socket.isSsl:
         if socket.hasDataBuffered:
           # sslPeekChar is present.
           return 1
-        let sslPending = SSLPending(socket.sslHandle)
+        let sslPending = SSL_pending(socket.sslHandle)
         if sslPending != 0:
           return min(sslPending, size)
 
-    var startTime = epochTime()
-    let selRet = select(socket, timeout - int(waited * 1000.0))
+    var startTime = getMonoTime()
+    let selRet = if socket.hasDataBuffered: 1
+      else:
+        timeoutRead(socket.fd, (timeout - waited.inMilliseconds).int)
     if selRet < 0: raiseOSError(osLastError())
     if selRet != 1:
       raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
-    waited += (epochTime() - startTime)
+    waited += (getMonoTime() - startTime)
 
 proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {.
   tags: [ReadIOEffect, TimeEffect].} =
-  ## overload with a ``timeout`` parameter in milliseconds.
-  var waited = 0.0 # number of seconds already waited
+  ## overload with a `timeout` parameter in milliseconds.
+  var waited: Duration # duration already waited
 
   var read = 0
   while read < size:
@@ -1185,7 +1495,16 @@ proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {.
 
 proc recv*(socket: Socket, data: var string, size: int, timeout = -1,
            flags = {SocketFlag.SafeDisconn}): int =
-  ## Higher-level version of ``recv``.
+  ## Higher-level version of `recv`.
+  ##
+  ## Reads **up to** `size` bytes from `socket` into `data`.
+  ##
+  ## For buffered sockets this function will attempt to read all the requested
+  ## data. It will read this data in `BufferSize` chunks.
+  ##
+  ## For unbuffered sockets this function makes no effort to read
+  ## all the data requested. It will return as much data as the operating system
+  ## gives it.
   ##
   ## When 0 is returned the socket's connection has been closed.
   ##
@@ -1193,11 +1512,9 @@ proc recv*(socket: Socket, data: var string, size: int, timeout = -1,
   ## lower than 0 is never returned.
   ##
   ## A timeout may be specified in milliseconds, if enough data is not received
-  ## within the time specified an TimeoutError exception will be raised.
-  ##
-  ## **Note**: ``data`` must be initialised.
+  ## within the time specified a TimeoutError exception will be raised.
   ##
-  ## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
+  ## .. warning:: Only the `SafeDisconn` flag is currently supported.
   data.setLen(size)
   result =
     if timeout == -1:
@@ -1207,23 +1524,32 @@ proc recv*(socket: Socket, data: var string, size: int, timeout = -1,
   if result < 0:
     data.setLen(0)
     let lastError = getSocketError(socket)
-    if flags.isDisconnectionError(lastError): return
-    socket.socketError(result, lastError = lastError)
-  data.setLen(result)
+    socket.socketError(result, lastError = lastError, flags = flags)
+  else:
+    data.setLen(result)
 
 proc recv*(socket: Socket, size: int, timeout = -1,
            flags = {SocketFlag.SafeDisconn}): string {.inline.} =
-  ## Higher-level version of ``recv`` which returns a string.
+  ## Higher-level version of `recv` which returns a string.
+  ##
+  ## Reads **up to** `size` bytes from `socket` into the result.
   ##
-  ## When ``""`` is returned the socket's connection has been closed.
+  ## For buffered sockets this function will attempt to read all the requested
+  ## data. It will read this data in `BufferSize` chunks.
   ##
-  ## This function will throw an EOS exception when an error occurs.
+  ## For unbuffered sockets this function makes no effort to read
+  ## all the data requested. It will return as much data as the operating system
+  ## gives it.
+  ##
+  ## When `""` is returned the socket's connection has been closed.
+  ##
+  ## This function will throw an OSError exception when an error occurs.
   ##
   ## A timeout may be specified in milliseconds, if enough data is not received
-  ## within the time specified an ETimeout exception will be raised.
+  ## within the time specified a TimeoutError exception will be raised.
   ##
   ##
-  ## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
+  ## .. warning:: Only the `SafeDisconn` flag is currently supported.
   result = newString(size)
   discard recv(socket, result, size, timeout, flags)
 
@@ -1238,7 +1564,7 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} =
     c = socket.buffer[socket.currPos]
   else:
     when defineSsl:
-      if socket.isSSL:
+      if socket.isSsl:
         if not socket.sslHasPeekChar:
           result = uniRecv(socket, addr(socket.sslPeekChar), 1, 0'i32)
           socket.sslHasPeekChar = true
@@ -1247,45 +1573,47 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} =
         return
     result = recv(socket.fd, addr(c), 1, MSG_PEEK)
 
-proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
+proc readLine*(socket: Socket, line: var string, timeout = -1,
                flags = {SocketFlag.SafeDisconn}, maxLength = MaxLineLength) {.
   tags: [ReadIOEffect, TimeEffect].} =
-  ## Reads a line of data from ``socket``.
+  ## Reads a line of data from `socket`.
   ##
-  ## If a full line is read ``\r\L`` is not
-  ## added to ``line``, however if solely ``\r\L`` is read then ``line``
+  ## If a full line is read `\r\L` is not
+  ## added to `line`, however if solely `\r\L` is read then `line`
   ## will be set to it.
   ##
-  ## If the socket is disconnected, ``line`` will be set to ``""``.
+  ## If the socket is disconnected, `line` will be set to `""`.
   ##
-  ## An EOS exception will be raised in the case of a socket error.
+  ## An OSError exception will be raised in the case of a socket error.
   ##
   ## A timeout can be specified in milliseconds, if data is not received within
-  ## the specified time an ETimeout exception will be raised.
+  ## the specified time a TimeoutError exception will be raised.
   ##
-  ## The ``maxLength`` parameter determines the maximum amount of characters
+  ## The `maxLength` parameter determines the maximum amount of characters
   ## that can be read. The result is truncated after that.
   ##
-  ## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
+  ## .. warning:: Only the `SafeDisconn` flag is currently supported.
 
   template addNLIfEmpty() =
     if line.len == 0:
-      line.string.add("\c\L")
+      line.add("\c\L")
 
   template raiseSockError() {.dirty.} =
     let lastError = getSocketError(socket)
-    if flags.isDisconnectionError(lastError): setLen(line.string, 0); return
-    socket.socketError(n, lastError = lastError)
+    if flags.isDisconnectionError(lastError):
+      setLen(line, 0)
+    socket.socketError(n, lastError = lastError, flags = flags)
+    return
 
-  var waited = 0.0
+  var waited: Duration
 
-  setLen(line.string, 0)
+  setLen(line, 0)
   while true:
     var c: char
     discard waitFor(socket, waited, timeout, 1, "readLine")
     var n = recv(socket, addr(c), 1)
     if n < 0: raiseSockError()
-    elif n == 0: setLen(line.string, 0); return
+    elif n == 0: setLen(line, 0); return
     if c == '\r':
       discard waitFor(socket, waited, timeout, 1, "readLine")
       n = peekChar(socket, c)
@@ -1297,70 +1625,90 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
     elif c == '\L':
       addNLIfEmpty()
       return
-    add(line.string, c)
+    add(line, c)
 
     # Verify that this isn't a DOS attack: #3847.
-    if line.string.len > maxLength: break
+    if line.len > maxLength: break
 
 proc recvLine*(socket: Socket, timeout = -1,
                flags = {SocketFlag.SafeDisconn},
-               maxLength = MaxLineLength): TaintedString =
-  ## Reads a line of data from ``socket``.
+               maxLength = MaxLineLength): string =
+  ## Reads a line of data from `socket`.
   ##
-  ## If a full line is read ``\r\L`` is not
-  ## added to the result, however if solely ``\r\L`` is read then the result
+  ## If a full line is read `\r\L` is not
+  ## added to the result, however if solely `\r\L` is read then the result
   ## will be set to it.
   ##
-  ## If the socket is disconnected, the result will be set to ``""``.
+  ## If the socket is disconnected, the result will be set to `""`.
   ##
-  ## An EOS exception will be raised in the case of a socket error.
+  ## An OSError exception will be raised in the case of a socket error.
   ##
   ## A timeout can be specified in milliseconds, if data is not received within
-  ## the specified time an ETimeout exception will be raised.
+  ## the specified time a TimeoutError exception will be raised.
   ##
-  ## The ``maxLength`` parameter determines the maximum amount of characters
+  ## The `maxLength` parameter determines the maximum amount of characters
   ## that can be read. The result is truncated after that.
   ##
-  ## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
+  ## .. warning:: Only the `SafeDisconn` flag is currently supported.
   result = ""
   readLine(socket, result, timeout, flags, maxLength)
 
-proc recvFrom*(socket: Socket, data: var string, length: int,
-               address: var string, port: var Port, flags = 0'i32): int {.
+proc recvFrom*[T: string | IpAddress](socket: Socket, data: var string, length: int,
+               address: var T, port: var Port, flags = 0'i32): int {.
                tags: [ReadIOEffect].} =
-  ## Receives data from ``socket``. This function should normally be used with
-  ## connection-less sockets (UDP sockets).
+  ## Receives data from `socket`. This function should normally be used with
+  ## connection-less sockets (UDP sockets). The source address of the data
+  ## packet is stored in the `address` argument as either a string or an IpAddress.
   ##
-  ## If an error occurs an EOS exception will be raised. Otherwise the return
+  ## If an error occurs an OSError exception will be raised. Otherwise the return
   ## value will be the length of data received.
   ##
-  ## **Warning:** This function does not yet have a buffered implementation,
-  ## so when ``socket`` is buffered the non-buffered implementation will be
-  ## used. Therefore if ``socket`` contains something in its buffer this
-  ## function will make no effort to return it.
+  ## .. warning:: This function does not yet have a buffered implementation,
+  ##   so when `socket` is buffered the non-buffered implementation will be
+  ##   used. Therefore if `socket` contains something in its buffer this
+  ##   function will make no effort to return it.
+  template adaptRecvFromToDomain(sockAddress: untyped, domain: Domain) =
+    var addrLen = SockLen(sizeof(sockAddress))
+    result = recvfrom(socket.fd, cstring(data), length.cint, flags.cint,
+                      cast[ptr SockAddr](addr(sockAddress)), addr(addrLen))
+
+    if result != -1:
+      data.setLen(result)
+
+      when typeof(address) is string:
+        address = getAddrString(cast[ptr SockAddr](addr(sockAddress)))
+        when domain == AF_INET6:
+          port = ntohs(sockAddress.sin6_port).Port
+        else:
+          port = ntohs(sockAddress.sin_port).Port
+      else:
+        data.setLen(result)
+        sockAddress.fromSockAddr(addrLen, address, port)
+    else:
+      raiseOSError(osLastError())
 
+  assert(socket.protocol != IPPROTO_TCP, "Cannot `recvFrom` on a TCP socket")
   # TODO: Buffered sockets
   data.setLen(length)
-  var sockAddress: Sockaddr_in
-  var addrLen = sizeof(sockAddress).SockLen
-  result = recvfrom(socket.fd, cstring(data), length.cint, flags.cint,
-                    cast[ptr SockAddr](addr(sockAddress)), addr(addrLen))
 
-  if result != -1:
-    data.setLen(result)
-    address = $inet_ntoa(sockAddress.sin_addr)
-    port = ntohs(sockAddress.sin_port).Port
+  case socket.domain
+  of AF_INET6:
+    var sockAddress: Sockaddr_in6
+    adaptRecvFromToDomain(sockAddress, AF_INET6)
+  of AF_INET:
+    var sockAddress: Sockaddr_in
+    adaptRecvFromToDomain(sockAddress, AF_INET)
   else:
-    raiseOSError(osLastError())
+    raise newException(ValueError, "Unknown socket address family")
 
 proc skip*(socket: Socket, size: int, timeout = -1) =
-  ## Skips ``size`` amount of bytes.
+  ## Skips `size` amount of bytes.
   ##
   ## An optional timeout can be specified in milliseconds, if skipping the
-  ## bytes takes longer than specified an ETimeout exception will be raised.
+  ## bytes takes longer than specified a TimeoutError exception will be raised.
   ##
   ## Returns the number of skipped bytes.
-  var waited = 0.0
+  var waited: Duration
   var dummy = alloc(size)
   var bytesSkipped = 0
   while bytesSkipped != size:
@@ -1372,12 +1720,13 @@ proc send*(socket: Socket, data: pointer, size: int): int {.
   tags: [WriteIOEffect].} =
   ## Sends data to a socket.
   ##
-  ## **Note**: This is a low-level version of ``send``. You likely should use
+  ## **Note**: This is a low-level version of `send`. You likely should use
   ## the version below.
   assert(not socket.isClosed, "Cannot `send` on a closed socket")
   when defineSsl:
-    if socket.isSSL:
-      return SSLWrite(socket.sslHandle, cast[cstring](data), size)
+    if socket.isSsl:
+      ErrClearError()
+      return SSL_write(socket.sslHandle, cast[cstring](data), size)
 
   when useWinVersion or defined(macosx):
     result = send(socket.fd, data, size.cint, 0'i32)
@@ -1387,43 +1736,67 @@ proc send*(socket: Socket, data: pointer, size: int): int {.
     result = send(socket.fd, data, size, int32(MSG_NOSIGNAL))
 
 proc send*(socket: Socket, data: string,
-           flags = {SocketFlag.SafeDisconn}) {.tags: [WriteIOEffect].} =
-  ## sends data to a socket.
-  let sent = send(socket, cstring(data), data.len)
-  if sent < 0:
-    let lastError = osLastError()
-    if flags.isDisconnectionError(lastError): return
-    socketError(socket, lastError = lastError)
+           flags = {SocketFlag.SafeDisconn}, maxRetries = 100) {.tags: [WriteIOEffect].} =
+  ## Sends data to a socket. Will try to send all the data by handling interrupts
+  ## and incomplete writes up to `maxRetries`.
+  var written = 0
+  var attempts = 0
+  while data.len - written > 0:
+    let sent = send(socket, cstring(data), data.len)
+
+    if sent < 0:
+      let lastError = osLastError()
+      let isBlockingErr =
+        when defined(nimdoc):
+          false
+        elif useWinVersion:
+          lastError.int32 == WSAEINTR or
+          lastError.int32 == WSAEWOULDBLOCK
+        else:
+          lastError.int32 == EINTR or
+          lastError.int32 == EWOULDBLOCK or
+          lastError.int32 == EAGAIN
 
-  if sent != data.len:
-    raiseOSError(osLastError(), "Could not send all data.")
+      if not isBlockingErr:
+        let lastError = osLastError()
+        socketError(socket, lastError = lastError, flags = flags)
+      else:
+        attempts.inc()
+        if attempts > maxRetries:
+          raiseOSError(osLastError(), "Could not send all data.")
+    else:
+      written.inc(sent)
 
 template `&=`*(socket: Socket; data: typed) =
   ## an alias for 'send'.
   send(socket, data)
 
 proc trySend*(socket: Socket, data: string): bool {.tags: [WriteIOEffect].} =
-  ## Safe alternative to ``send``. Does not raise an EOS when an error occurs,
-  ## and instead returns ``false`` on failure.
+  ## Safe alternative to `send`. Does not raise an OSError when an error occurs,
+  ## and instead returns `false` on failure.
   result = send(socket, cstring(data), data.len) == data.len
 
 proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
-             size: int, af: Domain = AF_INET, flags = 0'i32): int {.
+             size: int, af: Domain = AF_INET, flags = 0'i32) {.
              tags: [WriteIOEffect].} =
-  ## This proc sends ``data`` to the specified ``address``,
+  ## This proc sends `data` to the specified `address`,
   ## which may be an IP address or a hostname, if a hostname is specified
-  ## this function will try each IP of that hostname.
+  ## this function will try each IP of that hostname. This function
+  ## should normally be used with connection-less sockets (UDP sockets).
   ##
+  ## If an error occurs an OSError exception will be raised.
   ##
   ## **Note:** You may wish to use the high-level version of this function
   ## which is defined below.
   ##
   ## **Note:** This proc is not available for SSL sockets.
+  assert(socket.protocol != IPPROTO_TCP, "Cannot `sendTo` on a TCP socket")
   assert(not socket.isClosed, "Cannot `sendTo` on a closed socket")
   var aiList = getAddrInfo(address, port, af, socket.sockType, socket.protocol)
   # try all possibilities:
   var success = false
   var it = aiList
+  var result = 0
   while it != nil:
     result = sendto(socket.fd, data, size.cint, flags.cint, it.ai_addr,
                     it.ai_addrlen.SockLen)
@@ -1432,28 +1805,73 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
       break
     it = it.ai_next
 
+  let osError = osLastError()
   freeAddrInfo(aiList)
 
+  if not success:
+    raiseOSError(osError)
+
 proc sendTo*(socket: Socket, address: string, port: Port,
-             data: string): int {.tags: [WriteIOEffect].} =
-  ## This proc sends ``data`` to the specified ``address``,
+             data: string) {.tags: [WriteIOEffect].} =
+  ## This proc sends `data` to the specified `address`,
   ## which may be an IP address or a hostname, if a hostname is specified
   ## this function will try each IP of that hostname.
   ##
-  ## This is the high-level version of the above ``sendTo`` function.
-  result = socket.sendTo(address, port, cstring(data), data.len, socket.domain )
+  ## Generally for use with connection-less (UDP) sockets.
+  ##
+  ## If an error occurs an OSError exception will be raised.
+  ##
+  ## This is the high-level version of the above `sendTo` function.
+  socket.sendTo(address, port, cstring(data), data.len, socket.domain)
+
+proc sendTo*(socket: Socket, address: IpAddress, port: Port,
+             data: string, flags = 0'i32): int {.
+              discardable, tags: [WriteIOEffect].} =
+  ## This proc sends `data` to the specified `IpAddress` and returns
+  ## the number of bytes written.
+  ##
+  ## Generally for use with connection-less (UDP) sockets.
+  ##
+  ## If an error occurs an OSError exception will be raised.
+  ##
+  ## This is the high-level version of the above `sendTo` function.
+  assert(socket.protocol != IPPROTO_TCP, "Cannot `sendTo` on a TCP socket")
+  assert(not socket.isClosed, "Cannot `sendTo` on a closed socket")
+
+  var sa: Sockaddr_storage
+  var sl: SockLen
+  toSockAddr(address, port, sa, sl)
+  result = sendto(socket.fd, cstring(data), data.len().cint, flags.cint,
+                  cast[ptr SockAddr](addr sa), sl)
+
+  if result == -1'i32:
+    let osError = osLastError()
+    raiseOSError(osError)
 
 
 proc isSsl*(socket: Socket): bool =
-  ## Determines whether ``socket`` is a SSL socket.
+  ## Determines whether `socket` is a SSL socket.
   when defineSsl:
-    result = socket.isSSL
+    result = socket.isSsl
   else:
     result = false
 
 proc getFd*(socket: Socket): SocketHandle = return socket.fd
   ## Returns the socket's file descriptor
 
+when defined(zephyr) or defined(nimNetSocketExtras): # Remove in future
+  proc getDomain*(socket: Socket): Domain = return socket.domain
+    ## Returns the socket's domain
+
+  proc getType*(socket: Socket): SockType = return socket.sockType
+    ## Returns the socket's type
+
+  proc getProtocol*(socket: Socket): Protocol = return socket.protocol
+    ## Returns the socket's protocol
+
+when defined(nimHasStyleChecks):
+  {.push styleChecks: off.}
+
 proc IPv4_any*(): IpAddress =
   ## Returns the IPv4 any address, which can be used to listen on all available
   ## network adapters
@@ -1486,6 +1904,9 @@ proc IPv6_loopback*(): IpAddress =
     family: IpAddressFamily.IPv6,
     address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
 
+when defined(nimHasStyleChecks):
+  {.pop.}
+
 proc `==`*(lhs, rhs: IpAddress): bool =
   ## Compares two IpAddresses for Equality. Returns true if the addresses are equal
   if lhs.family != rhs.family: return false
@@ -1499,14 +1920,18 @@ proc `==`*(lhs, rhs: IpAddress): bool =
 
 proc `$`*(address: IpAddress): string =
   ## Converts an IpAddress into the textual representation
-  result = ""
   case address.family
   of IpAddressFamily.IPv4:
-    for i in 0 .. 3:
-      if i != 0:
-        result.add('.')
-      result.add($address.address_v4[i])
+    result = newStringOfCap(15)
+    result.addInt address.address_v4[0]
+    result.add '.'
+    result.addInt address.address_v4[1]
+    result.add '.'
+    result.addInt address.address_v4[2]
+    result.add '.'
+    result.addInt address.address_v4[3]
   of IpAddressFamily.IPv6:
+    result = newStringOfCap(39)
     var
       currentZeroStart = -1
       currentZeroCount = 0
@@ -1532,7 +1957,7 @@ proc `$`*(address: IpAddress): string =
     else: # Print address
       var printedLastGroup = false
       for i in 0..7:
-        var word:uint16 = (cast[uint16](address.address_v6[i*2])) shl 8
+        var word: uint16 = (cast[uint16](address.address_v6[i*2])) shl 8
         word = word or cast[uint16](address.address_v6[i*2+1])
 
         if biggestZeroCount != 0 and # Check if group is in skip group
@@ -1555,14 +1980,18 @@ proc `$`*(address: IpAddress): string =
                 result.add(chr(uint16(ord('a'))+val-0xA))
               afterLeadingZeros = true
             mask = mask shr 4
+
+          if not afterLeadingZeros:
+            result.add '0'
+
           printedLastGroup = true
 
 proc dial*(address: string, port: Port,
-           protocol = IPPROTO_TCP, buffered = true): Socket
+           protocol = IPPROTO_TCP, buffered = true): owned(Socket)
            {.tags: [ReadIOEffect, WriteIOEffect].} =
-  ## Establishes connection to the specified ``address``:``port`` pair via the
+  ## Establishes connection to the specified `address`:`port` pair via the
   ## specified protocol. The procedure iterates through possible
-  ## resolutions of the ``address`` until it succeeds, meaning that it
+  ## resolutions of the `address` until it succeeds, meaning that it
   ## seamlessly works with both IPv4 and IPv6.
   ## Returns Socket ready to send or receive data.
   let sockType = protocol.toSockType()
@@ -1609,20 +2038,22 @@ proc dial*(address: string, port: Port,
   closeUnusedFds(ord(domain))
 
   if success:
-    result = newSocket(lastFd, domain, sockType, protocol)
+    result = newSocket(lastFd, domain, sockType, protocol, buffered)
   elif lastError != 0.OSErrorCode:
+    lastFd.close()
     raiseOSError(lastError)
   else:
+    lastFd.close()
     raise newException(IOError, "Couldn't resolve address: " & address)
 
 proc connect*(socket: Socket, address: string,
-    port = Port(0)) {.tags: [ReadIOEffect].} =
-  ## Connects socket to ``address``:``port``. ``Address`` can be an IP address or a
-  ## host name. If ``address`` is a host name, this function will try each IP
-  ## of that host name. ``htons`` is already performed on ``port`` so you must
+    port = Port(0)) {.tags: [ReadIOEffect, RootEffect].} =
+  ## Connects socket to `address`:`port`. `Address` can be an IP address or a
+  ## host name. If `address` is a host name, this function will try each IP
+  ## of that host name. `htons` is already performed on `port` so you must
   ## not do it.
   ##
-  ## If ``socket`` is an SSL socket a handshake will be automatically performed.
+  ## If `socket` is an SSL socket a handshake will be automatically performed.
   var aiList = getAddrInfo(address, port, socket.domain)
   # try all possibilities:
   var success = false
@@ -1639,25 +2070,29 @@ proc connect*(socket: Socket, address: string,
   if not success: raiseOSError(lastError)
 
   when defineSsl:
-    if socket.isSSL:
+    if socket.isSsl:
       # RFC3546 for SNI specifies that IP addresses are not allowed.
       if not isIpAddress(address):
         # Discard result in case OpenSSL version doesn't support SNI, or we're
         # not using TLSv1+
         discard SSL_set_tlsext_host_name(socket.sslHandle, address)
 
-      let ret = SSLConnect(socket.sslHandle)
+      ErrClearError()
+      let ret = SSL_connect(socket.sslHandle)
       socketError(socket, ret)
+      when not defined(nimDisableCertificateValidation) and not defined(windows):
+        if not isIpAddress(address):
+          socket.checkCertName(address)
 
 proc connectAsync(socket: Socket, name: string, port = Port(0),
                   af: Domain = AF_INET) {.tags: [ReadIOEffect].} =
-  ## A variant of ``connect`` for non-blocking sockets.
+  ## A variant of `connect` for non-blocking sockets.
   ##
   ## This procedure will immediately return, it will not block until a connection
   ## is made. It is up to the caller to make sure the connection has been established
-  ## by checking (using ``select``) whether the socket is writeable.
+  ## by checking (using `select`) whether the socket is writeable.
   ##
-  ## **Note**: For SSL sockets, the ``handshake`` procedure must be called
+  ## **Note**: For SSL sockets, the `handshake` procedure must be called
   ## whenever the socket successfully connects to a server.
   var aiList = getAddrInfo(name, port, af)
   # try all possibilities:
@@ -1687,25 +2122,54 @@ proc connectAsync(socket: Socket, name: string, port = Port(0),
   if not success: raiseOSError(lastError)
 
 proc connect*(socket: Socket, address: string, port = Port(0),
-    timeout: int) {.tags: [ReadIOEffect, WriteIOEffect].} =
-  ## Connects to server as specified by ``address`` on port specified by ``port``.
+    timeout: int) {.tags: [ReadIOEffect, WriteIOEffect, RootEffect].} =
+  ## Connects to server as specified by `address` on port specified by `port`.
   ##
-  ## The ``timeout`` paremeter specifies the time in milliseconds to allow for
+  ## The `timeout` parameter specifies the time in milliseconds to allow for
   ## the connection to the server to be made.
   socket.fd.setBlocking(false)
 
   socket.connectAsync(address, port, socket.domain)
-  var s = @[socket.fd]
-  if selectWrite(s, timeout) != 1:
+  if timeoutWrite(socket.fd, timeout) != 1:
     raise newException(TimeoutError, "Call to 'connect' timed out.")
   else:
     let res = getSockOptInt(socket.fd, SOL_SOCKET, SO_ERROR)
     if res != 0:
       raiseOSError(OSErrorCode(res))
     when defineSsl and not defined(nimdoc):
-      if socket.isSSL:
+      if socket.isSsl:
         socket.fd.setBlocking(true)
-        {.warning[Deprecated]: off.}
-        doAssert socket.handshake()
-        {.warning[Deprecated]: on.}
+        # RFC3546 for SNI specifies that IP addresses are not allowed.
+        if not isIpAddress(address):
+          # Discard result in case OpenSSL version doesn't support SNI, or we're
+          # not using TLSv1+
+          discard SSL_set_tlsext_host_name(socket.sslHandle, address)
+
+        ErrClearError()
+        let ret = SSL_connect(socket.sslHandle)
+        socketError(socket, ret)
+        when not defined(nimDisableCertificateValidation):
+          if not isIpAddress(address):
+            socket.checkCertName(address)
   socket.fd.setBlocking(true)
+
+proc getPrimaryIPAddr*(dest = parseIpAddress("8.8.8.8")): IpAddress =
+  ## Finds the local IP address, usually assigned to eth0 on LAN or wlan0 on WiFi,
+  ## used to reach an external address. Useful to run local services.
+  ##
+  ## No traffic is sent.
+  ##
+  ## Supports IPv4 and v6.
+  ## Raises OSError if external networking is not set up.
+  runnableExamples("-r:off"):
+    echo getPrimaryIPAddr() # "192.168.1.2"
+  let socket =
+    if dest.family == IpAddressFamily.IPv4:
+      newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
+    else:
+      newSocket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)
+  try:
+    socket.connect($dest, 80.Port)
+    result = socket.getLocalAddr()[0].parseIpAddress()
+  finally:
+    socket.close()
diff --git a/lib/pure/nimprof.nim b/lib/pure/nimprof.nim
index 506c6bfaa..bf8367d1d 100644
--- a/lib/pure/nimprof.nim
+++ b/lib/pure/nimprof.nim
@@ -8,16 +8,22 @@
 #
 
 ## Profiling support for Nim. This is an embedded profiler that requires
-## ``--profiler:on``. You only need to import this module to get a profiling
-## report at program exit.
+## `--profiler:on`. You only need to import this module to get a profiling
+## report at program exit. See `Embedded Stack Trace Profiler <estp.html>`_
+## for usage.
 
 when not defined(profiler) and not defined(memProfiler):
   {.error: "Profiling support is turned off! Enable profiling by passing `--profiler:on --stackTrace:on` to the compiler (see the Nim Compiler User Guide for more options).".}
 
+{.used.}
+
 # We don't want to profile the profiling code ...
 {.push profiler: off.}
 
-import hashes, algorithm, strutils, tables, sets
+import std/[hashes, algorithm, strutils, tables, sets]
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, sysatomics]
 
 when not defined(memProfiler):
   include "system/timers"
@@ -63,7 +69,7 @@ when not defined(memProfiler):
     else: interval = intervalInUs * 1000 - tickCountCorrection
 
 when withThreads:
-  import locks
+  import std/locks
   var
     profilingLock: Lock
 
@@ -178,7 +184,7 @@ proc writeProfile() {.noconv.} =
 
     var perProc = initCountTable[string]()
     for i in 0..entries-1:
-      var dups = initSet[string]()
+      var dups = initHashSet[string]()
       for ii in 0..high(StackTrace.lines):
         let procname = profileData[i].st[ii]
         if isNil(procname): break
@@ -198,7 +204,8 @@ proc writeProfile() {.noconv.} =
           let procname = profileData[i].st[ii]
           let filename = profileData[i].st.files[ii]
           if isNil(procname): break
-          writeLine(f, "  ", $filename & ": " & $procname, " ", perProc[$procname] // totalCalls)
+          writeLine(f, "  ", $filename & ": " & $procname, " ",
+                    perProc[$procname] // totalCalls)
     close(f)
     echo "... done"
   else:
@@ -218,8 +225,9 @@ proc enableProfiling*() =
       system.profilingRequestedHook = requestedHook
 
 when declared(system.StackTrace):
+  import std/exitprocs
   system.profilingRequestedHook = requestedHook
   system.profilerHook = hook
-  addQuitProc(writeProfile)
+  addExitProc(writeProfile)
 
 {.pop.}
diff --git a/lib/pure/nimtracker.nim b/lib/pure/nimtracker.nim
deleted file mode 100644
index a66dfc2ea..000000000
--- a/lib/pure/nimtracker.nim
+++ /dev/null
@@ -1,88 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2016 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Memory tracking support for Nim.
-
-when not defined(memTracker) and not isMainModule:
-  {.error: "Memory tracking support is turned off!".}
-
-{.push memtracker: off.}
-# we import the low level wrapper and are careful not to use Nim's
-# memory manager for anything here.
-import sqlite3
-
-var
-  dbHandle: PSqlite3
-  insertStmt {.threadvar.}: Pstmt
-
-const insertQuery = "INSERT INTO tracking(op, address, size, file, line) values (?, ?, ?, ?, ?)"
-
-template sbind(x: int; value) =
-  when value is cstring:
-    let ret = insertStmt.bindText(x, value, value.len.int32, SQLITE_TRANSIENT)
-    if ret != SQLITE_OK:
-      quit "could not bind value"
-  else:
-    let ret = insertStmt.bindInt64(x, value)
-    if ret != SQLITE_OK:
-      quit "could not bind value"
-
-when defined(memTracker):
-  proc logEntries(log: TrackLog) {.nimcall, locks: 0, tags: [], gcsafe.} =
-    if insertStmt.isNil:
-      if prepare_v2(dbHandle, insertQuery,
-          insertQuery.len, insertStmt, nil) != SQLITE_OK:
-        quit "could not bind query to insertStmt " & $sqlite3.errmsg(dbHandle)
-    for i in 0..log.count-1:
-      var success = false
-      let e = log.data[i]
-      discard sqlite3.reset(insertStmt)
-      discard clearBindings(insertStmt)
-      sbind 1, e.op
-      sbind(2, cast[int](e.address))
-      sbind 3, e.size
-      sbind 4, e.file
-      sbind 5, e.line
-      if step(insertStmt) == SQLITE_DONE:
-        success = true
-      if not success:
-        quit "could not write to database! " & $sqlite3.errmsg(dbHandle)
-
-proc execQuery(q: string) =
-  var s: Pstmt
-  if prepare_v2(dbHandle, q, q.len.int32, s, nil) == SQLITE_OK:
-    discard step(s)
-    if finalize(s) != SQLITE_OK:
-      quit "could not finalize " & $sqlite3.errmsg(dbHandle)
-  else:
-    quit "could not prepare statement " & $sqlite3.errmsg(dbHandle)
-
-proc setupDb() =
-  execQuery """create table if not exists tracking(
-       id integer primary key,
-       op varchar not null,
-       address integer not null,
-       size integer not null,
-       file varchar not null,
-       line integer not null
-     )"""
-  execQuery "delete from tracking"
-
-if sqlite3.open("memtrack.db", dbHandle) == SQLITE_OK:
-  setupDb()
-  const query = "INSERT INTO tracking(op, address, size, file, line) values (?, ?, ?, ?, ?)"
-  if prepare_v2(dbHandle, insertQuery,
-      insertQuery.len, insertStmt, nil) == SQLITE_OK:
-    when defined(memTracker):
-      setTrackLogger logEntries
-  else:
-    quit "could not prepare statement B " & $sqlite3.errmsg(dbHandle)
-else:
-  quit "could not setup sqlite " & $sqlite3.errmsg(dbHandle)
-{.pop.}
diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim
index d6369b5f9..4d6ceefd7 100644
--- a/lib/pure/oids.nim
+++ b/lib/pure/oids.nim
@@ -9,85 +9,91 @@
 
 ## Nim OID support. An OID is a global ID that consists of a timestamp,
 ## a unique counter and a random value. This combination should suffice to
-## produce a globally distributed unique ID. This implementation was extracted
-## from the Mongodb interface and it thus binary compatible with a Mongo OID.
+## produce a globally distributed unique ID.
 ##
-## This implementation calls ``math.randomize()`` for the first call of
-## ``genOid``.
+## This implementation calls `initRand()` for the first call of
+## `genOid`.
 
-import times, endians
+import std/[hashes, times, endians, random]
+from std/private/decode_helpers import handleHexChar
+
+when defined(nimPreviewSlimSystem):
+  import std/sysatomics
 
 type
-  Oid* = object ## an OID
-    time: int32  ##
-    fuzz: int32  ##
-    count: int32 ##
-
-proc `==`*(oid1: Oid, oid2: Oid): bool =
-  ## Compare two Mongo Object IDs for equality
-  return (oid1.time == oid2.time) and (oid1.fuzz == oid2.fuzz) and (oid1.count == oid2.count)
-
-proc hexbyte*(hex: char): int =
-  case hex
-  of '0'..'9': result = (ord(hex) - ord('0'))
-  of 'a'..'f': result = (ord(hex) - ord('a') + 10)
-  of 'A'..'F': result = (ord(hex) - ord('A') + 10)
-  else: discard
+  Oid* = object ## An OID.
+    time: int64
+    fuzz: int32
+    count: int32
+
+proc `==`*(oid1: Oid, oid2: Oid): bool {.inline.} =
+  ## Compares two OIDs for equality.
+  result = (oid1.time == oid2.time) and (oid1.fuzz == oid2.fuzz) and
+          (oid1.count == oid2.count)
+
+proc hash*(oid: Oid): Hash =
+  ## Generates the hash of an OID for use in hashtables.
+  var h: Hash = 0
+  h = h !& hash(oid.time)
+  h = h !& hash(oid.fuzz)
+  h = h !& hash(oid.count)
+  result = !$h
+
+proc hexbyte*(hex: char): int {.inline.} =
+  result = handleHexChar(hex)
 
 proc parseOid*(str: cstring): Oid =
-  ## parses an OID.
-  var bytes = cast[cstring](addr(result.time))
+  ## Parses an OID.
+  var bytes = cast[cstring](cast[pointer](cast[int](addr(result.time)) + 4))
   var i = 0
   while i < 12:
     bytes[i] = chr((hexbyte(str[2 * i]) shl 4) or hexbyte(str[2 * i + 1]))
     inc(i)
 
-proc oidToString*(oid: Oid, str: cstring) =
+proc `$`*(oid: Oid): string =
+  ## Converts an OID to a string.
   const hex = "0123456789abcdef"
-  # work around a compiler bug:
-  var str = str
+
+  result.setLen 24
+
   var o = oid
-  var bytes = cast[cstring](addr(o))
+  var bytes = cast[cstring](cast[pointer](cast[int](addr(o)) + 4))
   var i = 0
   while i < 12:
     let b = bytes[i].ord
-    str[2 * i] = hex[(b and 0xF0) shr 4]
-    str[2 * i + 1] = hex[b and 0xF]
+    result[2 * i] = hex[(b and 0xF0) shr 4]
+    result[2 * i + 1] = hex[b and 0xF]
     inc(i)
-  str[24] = '\0'
 
-proc `$`*(oid: Oid): string =
-  result = newString(24)
-  oidToString(oid, result)
+let
+  t = getTime().toUnix
 
 var
-  incr: int
-  fuzz: int32
+  seed = initRand(t)
+  incr: int = seed.rand(int.high)
 
-proc genOid*(): Oid =
-  ## generates a new OID.
-  proc rand(): cint {.importc: "rand", header: "<stdlib.h>", nodecl.}
-  proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>", nodecl.}
+let fuzz = cast[int32](seed.rand(high(int)))
 
-  var t = getTime().toUnix.int32
 
-  var i = int32(atomicInc(incr))
+template genOid(result: var Oid, incr: var int, fuzz: int32) =
+  var time = getTime().toUnix
+  var i = cast[int32](atomicInc(incr))
 
-  if fuzz == 0:
-    # racy, but fine semantically:
-    srand(t)
-    fuzz = rand()
-  bigEndian32(addr result.time, addr(t))
+  bigEndian64(addr result.time, addr(time))
   result.fuzz = fuzz
   bigEndian32(addr result.count, addr(i))
 
+proc genOid*(): Oid =
+  ## Generates a new OID.
+  runnableExamples:
+    doAssert ($genOid()).len == 24
+  runnableExamples("-r:off"):
+    echo $genOid() # for example, "5fc7f546ddbbc84800006aaf"
+  genOid(result, incr, fuzz)
+
 proc generatedTime*(oid: Oid): Time =
-  ## returns the generated timestamp of the OID.
-  var tmp: int32
+  ## Returns the generated timestamp of the OID.
+  var tmp: int64
   var dummy = oid.time
-  bigEndian32(addr(tmp), addr(dummy))
+  bigEndian64(addr(tmp), addr(dummy))
   result = fromUnix(tmp)
-
-when not defined(testing) and isMainModule:
-  let xo = genOid()
-  echo xo.generatedTime
diff --git a/lib/pure/options.nim b/lib/pure/options.nim
index bd01b208a..b34ff72c0 100644
--- a/lib/pure/options.nim
+++ b/lib/pure/options.nim
@@ -7,294 +7,375 @@
 #    distribution, for details about the copyright.
 #
 
-## Abstract
-## ========
-##
-## This module implements types which encapsulate an optional value.
-##
-## A value of type ``Option[T]`` either contains a value `x` (represented as
-## ``some(x)``) or is empty (``none(T)``).
-##
-## This can be useful when you have a value that can be present or not. The
-## absence of a value is often represented by ``nil``, but it is not always
-## available, nor is it always a good solution.
-##
-##
-## Tutorial
-## ========
-##
-## Let's start with an example: a procedure that finds the index of a character
-## in a string.
-##
-## .. code-block:: nim
-##
-##   import options
-##
-##   proc find(haystack: string, needle: char): Option[int] =
-##     for i, c in haystack:
-##       if c == needle:
-##         return some(i)
-##     return none(int)  # This line is actually optional,
-##                       # because the default is empty
-##
-## .. code-block:: nim
-##
-##   try:
-##     assert("abc".find('c').get() == 2)  # Immediately extract the value
-##   except UnpackError:  # If there is no value
-##     assert false  # This will not be reached, because the value is present
-##
-## The ``get`` operation demonstrated above returns the underlying value, or
-## raises ``UnpackError`` if there is no value. There is another option for
-## obtaining the value: ``unsafeGet``, but you must only use it when you are
-## absolutely sure the value is present (e.g. after checking ``isSome``). If
-## you do not care about the tiny overhead that ``get`` causes, you should
-## simply never use ``unsafeGet``.
-##
-## How to deal with an absence of a value:
-##
-## .. code-block:: nim
-##
-##   let result = "team".find('i')
-##
-##   # Nothing was found, so the result is `none`.
-##   assert(result == none(int))
-##   # It has no value:
-##   assert(result.isNone)
-##
-##   try:
-##     echo result.get()
-##     assert(false)  # This will not be reached
-##   except UnpackError:  # Because an exception is raised
-##     discard
-import typetraits
+##[
+This module implements types which encapsulate an optional value.
 
-type
-  SomePointer = ref | ptr | pointer
+A value of type `Option[T]` either contains a value `x` (represented as
+`some(x)`) or is empty (`none(T)`).
+
+This can be useful when you have a value that can be present or not. The
+absence of a value is often represented by `nil`, but that is not always
+available, nor is it always a good solution.
+
+
+Basic usage
+===========
+
+Let's start with an example: a procedure that finds the index of a character
+in a string.
+]##
+
+runnableExamples:
+  proc find(haystack: string, needle: char): Option[int] =
+    for i, c in haystack:
+      if c == needle:
+        return some(i)
+    return none(int)  # This line is actually optional,
+                      # because the default is empty
+
+  let found = "abc".find('c')
+  assert found.isSome and found.get() == 2
+
+##[
+The `get` operation demonstrated above returns the underlying value, or
+raises `UnpackDefect` if there is no value. Note that `UnpackDefect`
+inherits from `system.Defect` and should therefore never be caught.
+Instead, rely on checking if the option contains a value with the
+`isSome <#isSome,Option[T]>`_ and `isNone <#isNone,Option[T]>`_ procs.
+
+
+Pattern matching
+================
+
+.. note:: This requires the [fusion](https://github.com/nim-lang/fusion) package.
+
+[fusion/matching](https://nim-lang.github.io/fusion/src/fusion/matching.html)
+supports pattern matching on `Option`s, with the `Some(<pattern>)` and
+`None()` patterns.
+
+  ```nim
+  {.experimental: "caseStmtMacros".}
+
+  import fusion/matching
+
+  case some(42)
+  of Some(@a):
+    assert a == 42
+  of None():
+    assert false
+
+  assertMatch(some(some(none(int))), Some(Some(None())))
+  ```
+]##
+# xxx pending https://github.com/timotheecour/Nim/issues/376 use `runnableExamples` and `whichModule`
+
+when defined(nimHasEffectsOf):
+  {.experimental: "strictEffects".}
+else:
+  {.pragma: effectsOf.}
+
+import std/typetraits
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
+when (NimMajor, NimMinor) >= (1, 1):
+  type
+    SomePointer = ref | ptr | pointer | proc | iterator {.closure.}
+else:
+  type
+    SomePointer = ref | ptr | pointer
 
 type
   Option*[T] = object
-    ## An optional type that stores its value and state separately in a boolean.
+    ## An optional type that may or may not contain a value of type `T`.
+    ## When `T` is a a pointer type (`ptr`, `pointer`, `ref`, `proc` or `iterator {.closure.}`),
+    ## `none(T)` is represented as `nil`.
     when T is SomePointer:
       val: T
     else:
       val: T
       has: bool
 
-  UnpackError* = ref object of ValueError
+  UnpackDefect* = object of Defect
+  UnpackError* {.deprecated: "See corresponding Defect".} = UnpackDefect
+
+proc option*[T](val: sink T): Option[T] {.inline.} =
+  ## Can be used to convert a pointer type (`ptr`, `pointer`, `ref` or `proc`) to an option type.
+  ## It converts `nil` to `none(T)`. When `T` is no pointer type, this is equivalent to `some(val)`.
+  ##
+  ## **See also:**
+  ## * `some proc <#some,T>`_
+  ## * `none proc <#none,typedesc>`_
+  runnableExamples:
+    type
+      Foo = ref object
+        a: int
+        b: string
+
+    assert option[Foo](nil).isNone
+    assert option(42).isSome
+
+  when T is SomePointer:
+    result = Option[T](val: val)
+  else:
+    result = Option[T](has: true, val: val)
+
+proc some*[T](val: sink T): Option[T] {.inline.} =
+  ## Returns an `Option` that has the value `val`.
+  ##
+  ## **See also:**
+  ## * `option proc <#option,T>`_
+  ## * `none proc <#none,typedesc>`_
+  ## * `isSome proc <#isSome,Option[T]>`_
+  runnableExamples:
+    let a = some("abc")
+
+    assert a.isSome
+    assert a.get == "abc"
 
-proc some*[T](val: T): Option[T] =
-  ## Returns a ``Option`` that has this value.
   when T is SomePointer:
-    assert val != nil
-    result.val = val
+    assert not val.isNil
+    result = Option[T](val: val)
   else:
-    result.has = true
-    result.val = val
-
-proc option*[T](val: T): Option[T] =
-  ## Can be used to convert a pointer type to an option type. It
-  ## converts ``nil`` to the none-option.
-  result.val = val
-  when T isnot SomePointer:
-    result.has = true
-
-proc none*(T: typedesc): Option[T] =
-  ## Returns an ``Option`` for this type that has no value.
+    result = Option[T](has: true, val: val)
+
+proc none*(T: typedesc): Option[T] {.inline.} =
+  ## Returns an `Option` for this type that has no value.
+  ##
+  ## **See also:**
+  ## * `option proc <#option,T>`_
+  ## * `some proc <#some,T>`_
+  ## * `isNone proc <#isNone,Option[T]>`_
+  runnableExamples:
+    assert none(int).isNone
+
   # the default is the none type
-  discard
+  result = Option[T]()
 
-proc none*[T]: Option[T] =
-  ## Alias for ``none(T)``.
+proc none*[T]: Option[T] {.inline.} =
+  ## Alias for `none(T) <#none,typedesc>`_.
   none(T)
 
 proc isSome*[T](self: Option[T]): bool {.inline.} =
+  ## Checks if an `Option` contains a value.
+  ##
+  ## **See also:**
+  ## * `isNone proc <#isNone,Option[T]>`_
+  ## * `some proc <#some,T>`_
+  runnableExamples:
+    assert some(42).isSome
+    assert not none(string).isSome
+
   when T is SomePointer:
-    self.val != nil
+    not self.val.isNil
   else:
     self.has
 
 proc isNone*[T](self: Option[T]): bool {.inline.} =
+  ## Checks if an `Option` is empty.
+  ##
+  ## **See also:**
+  ## * `isSome proc <#isSome,Option[T]>`_
+  ## * `none proc <#none,typedesc>`_
+  runnableExamples:
+    assert not some(42).isNone
+    assert none(string).isNone
+
   when T is SomePointer:
-    self.val == nil
+    self.val.isNil
   else:
     not self.has
 
-proc unsafeGet*[T](self: Option[T]): T =
-  ## Returns the value of a ``some``. Behavior is undefined for ``none``.
-  assert self.isSome
-  self.val
+proc get*[T](self: Option[T]): lent T {.inline.} =
+  ## Returns the content of an `Option`. If it has no value,
+  ## an `UnpackDefect` exception is raised.
+  ##
+  ## **See also:**
+  ## * `get proc <#get,Option[T],T>`_ with a default return value
+  runnableExamples:
+    assert some(42).get == 42
+    doAssertRaises(UnpackDefect):
+      echo none(string).get
 
-proc get*[T](self: Option[T]): T =
-  ## Returns contents of the Option. If it is none, then an exception is
-  ## thrown.
   if self.isNone:
-    raise UnpackError(msg : "Can't obtain a value from a `none`")
-  self.val
+    raise newException(UnpackDefect, "Can't obtain a value from a `none`")
+  result = self.val
+
+proc get*[T](self: Option[T], otherwise: T): T {.inline.} =
+  ## Returns the content of the `Option` or `otherwise` if
+  ## the `Option` has no value.
+  runnableExamples:
+    assert some(42).get(9999) == 42
+    assert none(int).get(9999) == 9999
 
-proc get*[T](self: Option[T], otherwise: T): T =
-  ## Returns the contents of this option or `otherwise` if the option is none.
   if self.isSome:
     self.val
   else:
     otherwise
 
-proc map*[T](self: Option[T], callback: proc (input: T)) =
-  ## Applies a callback to the value in this Option
+proc get*[T](self: var Option[T]): var T {.inline.} =
+  ## Returns the content of the `var Option` mutably. If it has no value,
+  ## an `UnpackDefect` exception is raised.
+  runnableExamples:
+    var
+      a = some(42)
+      b = none(string)
+    inc(a.get)
+    assert a.get == 43
+    doAssertRaises(UnpackDefect):
+      echo b.get
+
+  if self.isNone:
+    raise newException(UnpackDefect, "Can't obtain a value from a `none`")
+  return self.val
+
+proc map*[T](self: Option[T], callback: proc (input: T)) {.inline, effectsOf: callback.} =
+  ## Applies a `callback` function to the value of the `Option`, if it has one.
+  ##
+  ## **See also:**
+  ## * `map proc <#map,Option[T],proc(T)_2>`_ for a version with a callback
+  ##   which returns a value
+  runnableExamples:
+    var d = 0
+    proc saveDouble(x: int) =
+      d = 2 * x
+
+    none(int).map(saveDouble)
+    assert d == 0
+    some(42).map(saveDouble)
+    assert d == 84
+
   if self.isSome:
     callback(self.val)
 
-proc map*[T, R](self: Option[T], callback: proc (input: T): R): Option[R] =
-  ## Applies a callback to the value in this Option and returns an option
-  ## containing the new value. If this option is None, None will be returned
+proc map*[T, R](self: Option[T], callback: proc (input: T): R): Option[R] {.inline, effectsOf: callback.} =
+  ## Applies a `callback` function to the value of the `Option` and returns an
+  ## `Option` containing the new value.
+  ##
+  ## If the `Option` has no value, `none(R)` will be returned.
+  ##
+  ## **See also:**
+  ## * `map proc <#map,Option[T],proc(T)>`_
+  ## * `flatMap proc <#flatMap,Option[T],proc(T)>`_ for a version with a
+  ##   callback that returns an `Option`
+  runnableExamples:
+    proc isEven(x: int): bool =
+      x mod 2 == 0
+
+    assert some(42).map(isEven) == some(true)
+    assert none(int).map(isEven) == none(bool)
+
   if self.isSome:
-    some[R]( callback(self.val) )
+    some[R](callback(self.val))
   else:
     none(R)
 
-proc flatten*[A](self: Option[Option[A]]): Option[A] =
-  ## Remove one level of structure in a nested Option.
+proc flatten*[T](self: Option[Option[T]]): Option[T] {.inline.} =
+  ## Remove one level of structure in a nested `Option`.
+  ##
+  ## **See also:**
+  ## * `flatMap proc <#flatMap,Option[T],proc(T)>`_
+  runnableExamples:
+    assert flatten(some(some(42))) == some(42)
+    assert flatten(none(Option[int])) == none(int)
+
   if self.isSome:
     self.val
   else:
-    none(A)
-
-proc flatMap*[A, B](self: Option[A], callback: proc (input: A): Option[B]): Option[B] =
-  ## Applies a callback to the value in this Option and returns an
-  ## option containing the new value. If this option is None, None will be
-  ## returned. Similar to ``map``, with the difference that the callback
-  ## returns an Option, not a raw value. This allows multiple procs with a
-  ## signature of ``A -> Option[B]`` (including A = B) to be chained together.
+    none(T)
+
+proc flatMap*[T, R](self: Option[T],
+                    callback: proc (input: T): Option[R]): Option[R] {.inline, effectsOf: callback.} =
+  ## Applies a `callback` function to the value of the `Option` and returns the new value.
+  ##
+  ## If the `Option` has no value, `none(R)` will be returned.
+  ##
+  ## This is similar to `map`, with the difference that the `callback` returns an
+  ## `Option`, not a raw value. This allows multiple procs with a
+  ## signature of `A -> Option[B]` to be chained together.
+  ##
+  ## See also:
+  ## * `flatten proc <#flatten,Option[Option[A]]>`_
+  ## * `filter proc <#filter,Option[T],proc(T)>`_
+  runnableExamples:
+    proc doublePositives(x: int): Option[int] =
+      if x > 0:
+        some(2 * x)
+      else:
+        none(int)
+
+    assert some(42).flatMap(doublePositives) == some(84)
+    assert none(int).flatMap(doublePositives) == none(int)
+    assert some(-11).flatMap(doublePositives) == none(int)
+
   map(self, callback).flatten()
 
-proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] =
-  ## Applies a callback to the value in this Option. If the callback returns
-  ## `true`, the option is returned as a Some. If it returns false, it is
-  ## returned as a None.
+proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] {.inline, effectsOf: callback.} =
+  ## Applies a `callback` to the value of the `Option`.
+  ##
+  ## If the `callback` returns `true`, the option is returned as `some`.
+  ## If it returns `false`, it is returned as `none`.
+  ##
+  ## **See also:**
+  ## * `flatMap proc <#flatMap,Option[A],proc(A)>`_
+  runnableExamples:
+    proc isEven(x: int): bool =
+      x mod 2 == 0
+
+    assert some(42).filter(isEven) == some(42)
+    assert none(int).filter(isEven) == none(int)
+    assert some(-11).filter(isEven) == none(int)
+
   if self.isSome and not callback(self.val):
     none(T)
   else:
     self
 
-proc `==`*(a, b: Option): bool =
-  ## Returns ``true`` if both ``Option``s are ``none``,
-  ## or if they have equal values
-  (a.isSome and b.isSome and a.val == b.val) or (not a.isSome and not b.isSome)
+proc `==`*[T](a, b: Option[T]): bool {.inline.} =
+  ## Returns `true` if both `Option`s are `none`,
+  ## or if they are both `some` and have equal values.
+  runnableExamples:
+    let
+      a = some(42)
+      b = none(int)
+      c = some(42)
+      d = none(int)
+
+    assert a == c
+    assert b == d
+    assert not (a == b)
+
+  when T is SomePointer:
+    a.val == b.val
+  else:
+    (a.isSome and b.isSome and a.val == b.val) or (a.isNone and b.isNone)
 
 proc `$`*[T](self: Option[T]): string =
-  ## Get the string representation of this option. If the option has a value,
-  ## the result will be `Some(x)` where `x` is the string representation of the contained value.
-  ## If the option does not have a value, the result will be `None[T]` where `T` is the name of
-  ## the type contained in the option.
+  ## Get the string representation of the `Option`.
+  runnableExamples:
+    assert $some(42) == "some(42)"
+    assert $none(int) == "none(int)"
+
   if self.isSome:
-    "Some(" & $self.val & ")"
+    when defined(nimLagacyOptionsDollar):
+      result = "Some("
+    else:
+      result = "some("
+    result.addQuoted self.val
+    result.add ")"
   else:
-    "None[" & T.name & "]"
-
-when isMainModule:
-  import unittest, sequtils
-
-  suite "options":
-    # work around a bug in unittest
-    let intNone = none(int)
-    let stringNone = none(string)
-
-    test "example":
-      proc find(haystack: string, needle: char): Option[int] =
-        for i, c in haystack:
-          if c == needle:
-            return some i
-
-      check("abc".find('c').get() == 2)
-
-      let result = "team".find('i')
-
-      check result == intNone
-      check result.isNone
-
-    test "some":
-      check some(6).get() == 6
-      check some("a").unsafeGet() == "a"
-      check some(6).isSome
-      check some("a").isSome
-
-    test "none":
-      expect UnpackError:
-        discard none(int).get()
-      check(none(int).isNone)
-      check(not none(string).isSome)
-
-    test "equality":
-      check some("a") == some("a")
-      check some(7) != some(6)
-      check some("a") != stringNone
-      check intNone == intNone
-
-      when compiles(some("a") == some(5)):
-        check false
-      when compiles(none(string) == none(int)):
-        check false
-
-    test "get with a default value":
-      check(some("Correct").get("Wrong") == "Correct")
-      check(stringNone.get("Correct") == "Correct")
-
-    test "$":
-      check($(some("Correct")) == "Some(Correct)")
-      check($(stringNone) == "None[string]")
-
-    test "map with a void result":
-      var procRan = 0
-      some(123).map(proc (v: int) = procRan = v)
-      check procRan == 123
-      intNone.map(proc (v: int) = check false)
-
-    test "map":
-      check(some(123).map(proc (v: int): int = v * 2) == some(246))
-      check(intNone.map(proc (v: int): int = v * 2).isNone)
-
-    test "filter":
-      check(some(123).filter(proc (v: int): bool = v == 123) == some(123))
-      check(some(456).filter(proc (v: int): bool = v == 123).isNone)
-      check(intNone.filter(proc (v: int): bool = check false).isNone)
-
-    test "flatMap":
-      proc addOneIfNotZero(v: int): Option[int] =
-        if v != 0:
-          result = some(v + 1)
-        else:
-          result = none(int)
-
-      check(some(1).flatMap(addOneIfNotZero) == some(2))
-      check(some(0).flatMap(addOneIfNotZero) == none(int))
-      check(some(1).flatMap(addOneIfNotZero).flatMap(addOneIfNotZero) == some(3))
-
-      proc maybeToString(v: int): Option[string] =
-        if v != 0:
-          result = some($v)
-        else:
-          result = none(string)
-
-      check(some(1).flatMap(maybeToString) == some("1"))
-
-      proc maybeExclaim(v: string): Option[string] =
-        if v != "":
-          result = some v & "!"
-        else:
-          result = none(string)
-
-      check(some(1).flatMap(maybeToString).flatMap(maybeExclaim) == some("1!"))
-      check(some(0).flatMap(maybeToString).flatMap(maybeExclaim) == none(string))
-
-    test "SomePointer":
-      var intref: ref int
-      check(option(intref).isNone)
-      intref.new
-      check(option(intref).isSome)
-
-      let tmp = option(intref)
-      check(sizeof(tmp) == sizeof(ptr int))
-
-    test "none[T]":
-      check(none[int]().isNone)
-      check(none(int) == none[int]())
+    when defined(nimLagacyOptionsDollar):
+      result = "None[" & name(T) & "]"
+    else:
+      result = "none(" & name(T) & ")"
+
+proc unsafeGet*[T](self: Option[T]): lent T {.inline.}=
+  ## Returns the value of a `some`. The behavior is undefined for `none`.
+  ##
+  ## **Note:** Use this only when you are **absolutely sure** the value is present
+  ## (e.g. after checking with `isSome <#isSome,Option[T]>`_).
+  ## Generally, using the `get proc <#get,Option[T]>`_ is preferred.
+  assert self.isSome
+  result = self.val
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 3bc87728b..78ebb1c88 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -8,152 +8,242 @@
 #
 
 ## This module contains basic operating system facilities like
-## retrieving environment variables, reading command line arguments,
-## working with directories, running shell commands, etc.
-{.deadCodeElim: on.}  # dce option deprecated
+## retrieving environment variables, working with directories,
+## running shell commands, etc.
 
-{.push debugger: off.}
+## .. importdoc:: symlinks.nim, appdirs.nim, dirs.nim, ospaths2.nim
 
-include "system/inclrtl"
+runnableExamples:
+  let myFile = "/path/to/my/file.nim"
+  assert splitPath(myFile) == (head: "/path/to/my", tail: "file.nim")
+  when defined(posix):
+    assert parentDir(myFile) == "/path/to/my"
+  assert splitFile(myFile) == (dir: "/path/to/my", name: "file", ext: ".nim")
+  assert myFile.changeFileExt("c") == "/path/to/my/file.c"
+
+## **See also:**
+## * `paths <paths.html>`_ and `files <files.html>`_ modules for high-level file manipulation
+## * `osproc module <osproc.html>`_ for process communication beyond
+##   `execShellCmd proc`_
+## * `uri module <uri.html>`_
+## * `distros module <distros.html>`_
+## * `dynlib module <dynlib.html>`_
+## * `streams module <streams.html>`_
+import std/private/ospaths2
+export ospaths2
+
+import std/private/osfiles
+export osfiles
+
+import std/private/osdirs
+export osdirs
+
+import std/private/ossymlinks
+export ossymlinks
+
+import std/private/osappdirs
+export osappdirs
+
+import std/private/oscommon
+
+include system/inclrtl
+import std/private/since
+
+import std/cmdline
+export cmdline
+
+import std/[strutils, pathnorm]
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions, widestrs]
 
-import
-  strutils, times
+const weirdTarget = defined(nimscript) or defined(js)
 
-when defined(windows):
-  import winlean
+since (1, 1):
+  const
+    invalidFilenameChars* = {'/', '\\', ':', '*', '?', '"', '<', '>', '|', '^', '\0'} ## \
+    ## Characters that may produce invalid filenames across Linux, Windows and Mac.
+    ## You can check if your filename contains any of these chars and strip them for safety.
+    ## Mac bans ``':'``, Linux bans ``'/'``, Windows bans all others.
+    invalidFilenames* = [
+      "CON", "PRN", "AUX", "NUL",
+      "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
+      "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"] ## \
+    ## Filenames that may be invalid across Linux, Windows, Mac, etc.
+    ## You can check if your filename match these and rename it for safety
+    ## (Currently all invalid filenames are from Windows only).
+
+when weirdTarget:
+  discard
+elif defined(windows):
+  import std/[winlean, times]
 elif defined(posix):
-  import posix
+  import std/[posix, times]
 
   proc toTime(ts: Timespec): times.Time {.inline.} =
     result = initTime(ts.tv_sec.int64, ts.tv_nsec.int)
-
 else:
   {.error: "OS module not ported to your operating system!".}
 
-import ospaths
-export ospaths
-
-proc c_remove(filename: cstring): cint {.
-  importc: "remove", header: "<stdio.h>".}
-proc c_rename(oldname, newname: cstring): cint {.
-  importc: "rename", header: "<stdio.h>".}
-proc c_system(cmd: cstring): cint {.
-  importc: "system", header: "<stdlib.h>".}
-proc c_strlen(a: cstring): cint {.
-  importc: "strlen", header: "<string.h>", noSideEffect.}
-proc c_free(p: pointer) {.
-  importc: "free", header: "<stdlib.h>".}
-
-
-when defined(windows):
-  when useWinUnicode:
-    template wrapUnary(varname, winApiProc, arg: untyped) =
-      var varname = winApiProc(newWideCString(arg))
-
-    template wrapBinary(varname, winApiProc, arg, arg2: untyped) =
-      var varname = winApiProc(newWideCString(arg), arg2)
-    proc findFirstFile(a: string, b: var WIN32_FIND_DATA): Handle =
-      result = findFirstFileW(newWideCString(a), b)
-    template findNextFile(a, b: untyped): untyped = findNextFileW(a, b)
-    template getCommandLine(): untyped = getCommandLineW()
-
-    template getFilename(f: untyped): untyped =
-      $cast[WideCString](addr(f.cFilename[0]))
-  else:
-    template findFirstFile(a, b: untyped): untyped = findFirstFileA(a, b)
-    template findNextFile(a, b: untyped): untyped = findNextFileA(a, b)
-    template getCommandLine(): untyped = getCommandLineA()
-
-    template getFilename(f: untyped): untyped = $f.cFilename
-
-  proc skipFindData(f: WIN32_FIND_DATA): bool {.inline.} =
-    # Note - takes advantage of null delimiter in the cstring
-    const dot = ord('.')
-    result = f.cFileName[0].int == dot and (f.cFileName[1].int == 0 or
-             f.cFileName[1].int == dot and f.cFileName[2].int == 0)
-
-proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
-                                          tags: [ReadDirEffect].} =
-  ## Returns true if `filename` exists and is a regular file or symlink.
-  ## (directories, device files, named pipes and sockets return false)
-  when defined(windows):
-    when useWinUnicode:
-      wrapUnary(a, getFileAttributesW, filename)
-    else:
-      var a = getFileAttributesA(filename)
-    if a != -1'i32:
-      result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32
+when weirdTarget:
+  {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".}
+else:
+  {.pragma: noWeirdTarget.}
+
+when defined(nimscript):
+  # for procs already defined in scriptconfig.nim
+  template noNimJs(body): untyped = discard
+elif defined(js):
+  {.pragma: noNimJs, error: "this proc is not available on the js target".}
+else:
+  {.pragma: noNimJs.}
+
+
+import std/oserrors
+export oserrors
+import std/envvars
+export envvars
+
+import std/private/osseps
+export osseps
+
+
+
+proc expandTilde*(path: string): string {.
+  tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Expands ``~`` or a path starting with ``~/`` to a full path, replacing
+  ## ``~`` with `getHomeDir()`_ (otherwise returns ``path`` unmodified).
+  ##
+  ## Windows: this is still supported despite the Windows platform not having this
+  ## convention; also, both ``~/`` and ``~\`` are handled.
+  ##
+  ## See also:
+  ## * `getHomeDir proc`_
+  ## * `getConfigDir proc`_
+  ## * `getTempDir proc`_
+  ## * `getCurrentDir proc`_
+  ## * `setCurrentDir proc`_
+  runnableExamples:
+    assert expandTilde("~" / "appname.cfg") == getHomeDir() / "appname.cfg"
+    assert expandTilde("~/foo/bar") == getHomeDir() / "foo/bar"
+    assert expandTilde("/foo/bar") == "/foo/bar"
+
+  if len(path) == 0 or path[0] != '~':
+    result = path
+  elif len(path) == 1:
+    result = getHomeDir()
+  elif (path[1] in {DirSep, AltSep}):
+    result = getHomeDir() / path.substr(2)
   else:
-    var res: Stat
-    return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
+    # TODO: handle `~bob` and `~bob/` which means home of bob
+    result = path
 
-proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect].} =
-  ## Returns true iff the directory `dir` exists. If `dir` is a file, false
-  ## is returned.
-  when defined(windows):
-    when useWinUnicode:
-      wrapUnary(a, getFileAttributesW, dir)
+proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
+  ## Quote `s`, so it can be safely passed to Windows API.
+  ##
+  ## Based on Python's `subprocess.list2cmdline`.
+  ## See `this link <http://msdn.microsoft.com/en-us/library/17w5ykft.aspx>`_
+  ## for more details.
+  let needQuote = {' ', '\t'} in s or s.len == 0
+  result = ""
+  var backslashBuff = ""
+  if needQuote:
+    result.add("\"")
+
+  for c in s:
+    if c == '\\':
+      backslashBuff.add(c)
+    elif c == '\"':
+      for i in 0..<backslashBuff.len*2:
+        result.add('\\')
+      backslashBuff.setLen(0)
+      result.add("\\\"")
     else:
-      var a = getFileAttributesA(dir)
-    if a != -1'i32:
-      result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32
+      if backslashBuff.len != 0:
+        result.add(backslashBuff)
+        backslashBuff.setLen(0)
+      result.add(c)
+
+  if backslashBuff.len > 0:
+    result.add(backslashBuff)
+  if needQuote:
+    result.add(backslashBuff)
+    result.add("\"")
+
+
+proc quoteShellPosix*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
+  ## Quote ``s``, so it can be safely passed to POSIX shell.
+  const safeUnixChars = {'%', '+', '-', '.', '/', '_', ':', '=', '@',
+                         '0'..'9', 'A'..'Z', 'a'..'z'}
+  if s.len == 0:
+    result = "''"
+  elif s.allCharsInSet(safeUnixChars):
+    result = s
   else:
-    var res: Stat
-    return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
+    result = "'" & s.replace("'", "'\"'\"'") & "'"
 
-proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
-                                          tags: [ReadDirEffect].} =
-  ## Returns true iff the symlink `link` exists. Will return true
-  ## regardless of whether the link points to a directory or file.
-  when defined(windows):
-    when useWinUnicode:
-      wrapUnary(a, getFileAttributesW, link)
+when defined(windows) or defined(posix) or defined(nintendoswitch):
+  proc quoteShell*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
+    ## Quote ``s``, so it can be safely passed to shell.
+    ##
+    ## When on Windows, it calls `quoteShellWindows proc`_.
+    ## Otherwise, calls `quoteShellPosix proc`_.
+    when defined(windows):
+      result = quoteShellWindows(s)
     else:
-      var a = getFileAttributesA(link)
-    if a != -1'i32:
-      result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32
-  else:
-    var res: Stat
-    return lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode)
+      result = quoteShellPosix(s)
 
-proc fileExists*(filename: string): bool {.inline.} =
-  ## Synonym for existsFile
-  existsFile(filename)
+  proc quoteShellCommand*(args: openArray[string]): string =
+    ## Concatenates and quotes shell arguments `args`.
+    runnableExamples:
+      when defined(posix):
+        assert quoteShellCommand(["aaa", "", "c d"]) == "aaa '' 'c d'"
+      when defined(windows):
+        assert quoteShellCommand(["aaa", "", "c d"]) == "aaa \"\" \"c d\""
 
-proc dirExists*(dir: string): bool {.inline.} =
-  ## Synonym for existsDir
-  existsDir(dir)
+    # can't use `map` pending https://github.com/nim-lang/Nim/issues/8303
+    result = ""
+    for i in 0..<args.len:
+      if i > 0: result.add " "
+      result.add quoteShell(args[i])
+
+when not weirdTarget:
+  proc c_system(cmd: cstring): cint {.
+    importc: "system", header: "<stdlib.h>".}
+
+  when not defined(windows):
+    proc c_free(p: pointer) {.
+      importc: "free", header: "<stdlib.h>".}
 
-when not defined(windows):
-  proc checkSymlink(path: string): bool =
-    var rawInfo: Stat
-    if lstat(path, rawInfo) < 0'i32: result = false
-    else: result = S_ISLNK(rawInfo.st_mode)
 
 const
-  ExeExts* = when defined(windows): ["exe", "cmd", "bat"] else: [""] ## \
-    ## platform specific file extension for executables. On Windows
-    ## ``["exe", "cmd", "bat"]``, on Posix ``[""]``.
+  ExeExts* = ## Platform specific file extension for executables.
+    ## On Windows ``["exe", "cmd", "bat"]``, on Posix ``[""]``.
+    when defined(windows): ["exe", "cmd", "bat"] else: [""]
 
 proc findExe*(exe: string, followSymlinks: bool = true;
-              extensions: openarray[string]=ExeExts): string {.
-  tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect].} =
+              extensions: openArray[string]=ExeExts): string {.
+  tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect], noNimJs.} =
   ## Searches for `exe` in the current working directory and then
   ## in directories listed in the ``PATH`` environment variable.
-  ## Returns "" if the `exe` cannot be found. `exe`
-  ## is added the `ExeExts <#ExeExts>`_ file extensions if it has none.
+  ##
+  ## Returns `""` if the `exe` cannot be found. `exe`
+  ## is added the `ExeExts`_ file extensions if it has none.
+  ##
   ## If the system supports symlinks it also resolves them until it
-  ## meets the actual file. This behavior can be disabled if desired.
+  ## meets the actual file. This behavior can be disabled if desired
+  ## by setting `followSymlinks = false`.
+
   if exe.len == 0: return
   template checkCurrentDir() =
     for ext in extensions:
       result = addFileExt(exe, ext)
-      if existsFile(result): return
+      if fileExists(result): return
   when defined(posix):
     if '/' in exe: checkCurrentDir()
   else:
     checkCurrentDir()
-  let path = string(getEnv("PATH"))
+  let path = getEnv("PATH")
   for candidate in split(path, PathSep):
     if candidate.len == 0: continue
     when defined(windows):
@@ -164,17 +254,17 @@ proc findExe*(exe: string, followSymlinks: bool = true;
       var x = expandTilde(candidate) / exe
     for ext in extensions:
       var x = addFileExt(x, ext)
-      if existsFile(x):
-        when not defined(windows):
+      if fileExists(x):
+        when not (defined(windows) or defined(nintendoswitch)):
           while followSymlinks: # doubles as if here
-            if x.checkSymlink:
-              var r = newString(256)
-              var len = readlink(x, r, 256)
+            if x.symlinkExists:
+              var r = newString(maxSymlinkLen)
+              var len = readlink(x.cstring, r.cstring, maxSymlinkLen)
               if len < 0:
-                raiseOSError(osLastError())
-              if len > 256:
+                raiseOSError(osLastError(), exe)
+              if len > maxSymlinkLen:
                 r = newString(len+1)
-                len = readlink(x, r, len)
+                len = readlink(x.cstring, r.cstring, len)
               setLen(r, len)
               if isAbsolute(r):
                 x = r
@@ -185,1147 +275,232 @@ proc findExe*(exe: string, followSymlinks: bool = true;
         return x
   result = ""
 
-proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
+when weirdTarget:
+  const times = "fake const"
+  template Time(x: untyped): untyped = string
+
+proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1", noWeirdTarget.} =
   ## Returns the `file`'s last modification time.
+  ##
+  ## See also:
+  ## * `getLastAccessTime proc`_
+  ## * `getCreationTime proc`_
+  ## * `fileNewer proc`_
   when defined(posix):
     var res: Stat
-    if stat(file, res) < 0'i32: raiseOSError(osLastError())
+    if stat(file, res) < 0'i32: raiseOSError(osLastError(), file)
     result = res.st_mtim.toTime
   else:
     var f: WIN32_FIND_DATA
     var h = findFirstFile(file, f)
-    if h == -1'i32: raiseOSError(osLastError())
+    if h == -1'i32: raiseOSError(osLastError(), file)
     result = fromWinTime(rdFileTime(f.ftLastWriteTime))
     findClose(h)
 
-proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
+proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1", noWeirdTarget.} =
   ## Returns the `file`'s last read or write access time.
+  ##
+  ## See also:
+  ## * `getLastModificationTime proc`_
+  ## * `getCreationTime proc`_
+  ## * `fileNewer proc`_
   when defined(posix):
     var res: Stat
-    if stat(file, res) < 0'i32: raiseOSError(osLastError())
+    if stat(file, res) < 0'i32: raiseOSError(osLastError(), file)
     result = res.st_atim.toTime
   else:
     var f: WIN32_FIND_DATA
     var h = findFirstFile(file, f)
-    if h == -1'i32: raiseOSError(osLastError())
+    if h == -1'i32: raiseOSError(osLastError(), file)
     result = fromWinTime(rdFileTime(f.ftLastAccessTime))
     findClose(h)
 
-proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
+proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1", noWeirdTarget.} =
   ## Returns the `file`'s creation time.
   ##
   ## **Note:** Under POSIX OS's, the returned time may actually be the time at
   ## which the file's attribute's were last modified. See
   ## `here <https://github.com/nim-lang/Nim/issues/1058>`_ for details.
+  ##
+  ## See also:
+  ## * `getLastModificationTime proc`_
+  ## * `getLastAccessTime proc`_
+  ## * `fileNewer proc`_
   when defined(posix):
     var res: Stat
-    if stat(file, res) < 0'i32: raiseOSError(osLastError())
+    if stat(file, res) < 0'i32: raiseOSError(osLastError(), file)
     result = res.st_ctim.toTime
   else:
     var f: WIN32_FIND_DATA
     var h = findFirstFile(file, f)
-    if h == -1'i32: raiseOSError(osLastError())
+    if h == -1'i32: raiseOSError(osLastError(), file)
     result = fromWinTime(rdFileTime(f.ftCreationTime))
     findClose(h)
 
-proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} =
+proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1", noWeirdTarget.} =
   ## Returns true if the file `a` is newer than file `b`, i.e. if `a`'s
   ## modification time is later than `b`'s.
+  ##
+  ## See also:
+  ## * `getLastModificationTime proc`_
+  ## * `getLastAccessTime proc`_
+  ## * `getCreationTime proc`_
   when defined(posix):
     # If we don't have access to nanosecond resolution, use '>='
-    when not StatHasNanoseconds:  
+    when not StatHasNanoseconds:
       result = getLastModificationTime(a) >= getLastModificationTime(b)
     else:
       result = getLastModificationTime(a) > getLastModificationTime(b)
   else:
     result = getLastModificationTime(a) > getLastModificationTime(b)
 
-proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
-  ## Returns the `current working directory`:idx:.
-  when defined(windows):
-    var bufsize = MAX_PATH.int32
-    when useWinUnicode:
-      var res = newWideCString("", bufsize)
-      while true:
-        var L = getCurrentDirectoryW(bufsize, res)
-        if L == 0'i32:
-          raiseOSError(osLastError())
-        elif L > bufsize:
-          res = newWideCString("", L)
-          bufsize = L
-        else:
-          result = res$L
-          break
-    else:
-      result = newString(bufsize)
-      while true:
-        var L = getCurrentDirectoryA(bufsize, result)
-        if L == 0'i32:
-          raiseOSError(osLastError())
-        elif L > bufsize:
-          result = newString(L)
-          bufsize = L
-        else:
-          setLen(result, L)
-          break
-  else:
-    var bufsize = 1024 # should be enough
-    result = newString(bufsize)
-    while true:
-      if getcwd(result, bufsize) != nil:
-        setLen(result, c_strlen(result))
-        break
-      else:
-        let err = osLastError()
-        if err.int32 == ERANGE:
-          bufsize = bufsize shl 1
-          doAssert(bufsize >= 0)
-          result = newString(bufsize)
-        else:
-          raiseOSError(osLastError())
-
-proc setCurrentDir*(newDir: string) {.inline, tags: [].} =
-  ## Sets the `current working directory`:idx:; `OSError` is raised if
-  ## `newDir` cannot been set.
-  when defined(Windows):
-    when useWinUnicode:
-      if setCurrentDirectoryW(newWideCString(newDir)) == 0'i32:
-        raiseOSError(osLastError())
-    else:
-      if setCurrentDirectoryA(newDir) == 0'i32: raiseOSError(osLastError())
-  else:
-    if chdir(newDir) != 0'i32: raiseOSError(osLastError())
 
-proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
-  tags: [ReadDirEffect].} =
-  ## Returns the full (`absolute`:idx:) path of the file `filename`,
-  ## raises OSError in case of an error.
+proc isAdmin*: bool {.noWeirdTarget.} =
+  ## Returns whether the caller's process is a member of the Administrators local
+  ## group (on Windows) or a root (on POSIX), via `geteuid() == 0`.
   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))
-
-when defined(Windows):
-  proc openHandle(path: string, followSymlink=true, writeAccess=false): Handle =
-    var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL
-    if not followSymlink:
-      flags = flags or FILE_FLAG_OPEN_REPARSE_POINT
-    let access = if writeAccess: GENERIC_WRITE else: 0'i32
-
-    when useWinUnicode:
-      result = createFileW(
-        newWideCString(path), access,
-        FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
-        nil, OPEN_EXISTING, flags, 0
-        )
-    else:
-      result = createFileA(
-        path, access,
-        FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
-        nil, OPEN_EXISTING, flags, 0
-        )
-
-proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
-  tags: [ReadDirEffect].} =
-  ## Returns true if both pathname arguments refer to the same physical
-  ## file or directory. Raises an exception if any of the files does not
-  ## exist or information about it can not be obtained.
-  ##
-  ## This proc will return true if given two alternative hard-linked or
-  ## sym-linked paths to the same file or directory.
-  when defined(Windows):
-    var success = true
-    var f1 = openHandle(path1)
-    var f2 = openHandle(path2)
-
-    var lastErr: OSErrorCode
-    if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
-      var fi1, fi2: BY_HANDLE_FILE_INFORMATION
-
-      if getFileInformationByHandle(f1, addr(fi1)) != 0 and
-         getFileInformationByHandle(f2, addr(fi2)) != 0:
-        result = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber and
-                 fi1.nFileIndexHigh == fi2.nFileIndexHigh and
-                 fi1.nFileIndexLow == fi2.nFileIndexLow
-      else:
-        lastErr = osLastError()
-        success = false
-    else:
-      lastErr = osLastError()
-      success = false
-
-    discard closeHandle(f1)
-    discard closeHandle(f2)
-
-    if not success: raiseOSError(lastErr)
-  else:
-    var a, b: Stat
-    if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32:
-      raiseOSError(osLastError())
-    else:
-      result = a.st_dev == b.st_dev and a.st_ino == b.st_ino
-
-proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1",
-  tags: [ReadIOEffect].} =
-  ## Returns true if both pathname arguments refer to files with identical
-  ## binary content.
-  const
-    bufSize = 8192 # 8K buffer
-  var
-    a, b: File
-  if not open(a, path1): return false
-  if not open(b, path2):
-    close(a)
-    return false
-  var bufA = alloc(bufSize)
-  var bufB = alloc(bufSize)
-  while true:
-    var readA = readBuffer(a, bufA, bufSize)
-    var readB = readBuffer(b, bufB, bufSize)
-    if readA != readB:
-      result = false
-      break
-    if readA == 0:
-      result = true
-      break
-    result = equalMem(bufA, bufB, readA)
-    if not result: break
-    if readA != bufSize: break # end of file
-  dealloc(bufA)
-  dealloc(bufB)
-  close(a)
-  close(b)
+    # Rewrite of the example from Microsoft Docs:
+    # https://docs.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-checktokenmembership#examples
+    # and corresponding PostgreSQL function:
+    # https://doxygen.postgresql.org/win32security_8c.html#ae6b61e106fa5d6c5d077a9d14ee80569
+    var ntAuthority = SID_IDENTIFIER_AUTHORITY(value: SECURITY_NT_AUTHORITY)
+    var administratorsGroup: PSID
+    if not isSuccess(allocateAndInitializeSid(addr ntAuthority,
+                                              BYTE(2),
+                                              SECURITY_BUILTIN_DOMAIN_RID,
+                                              DOMAIN_ALIAS_RID_ADMINS,
+                                              0, 0, 0, 0, 0, 0,
+                                              addr administratorsGroup)):
+      raiseOSError(osLastError(), "could not get SID for Administrators group")
 
-type
-  FilePermission* = enum   ## file access permission; modelled after UNIX
-    fpUserExec,            ## execute access for the file owner
-    fpUserWrite,           ## write access for the file owner
-    fpUserRead,            ## read access for the file owner
-    fpGroupExec,           ## execute access for the group
-    fpGroupWrite,          ## write access for the group
-    fpGroupRead,           ## read access for the group
-    fpOthersExec,          ## execute access for others
-    fpOthersWrite,         ## write access for others
-    fpOthersRead           ## read access for others
-
-proc getFilePermissions*(filename: string): set[FilePermission] {.
-  rtl, extern: "nos$1", tags: [ReadDirEffect].} =
-  ## retrieves file permissions for `filename`. `OSError` is raised in case of
-  ## an error. On Windows, only the ``readonly`` flag is checked, every other
-  ## permission is available in any case.
-  when defined(posix):
-    var a: Stat
-    if stat(filename, a) < 0'i32: raiseOSError(osLastError())
-    result = {}
-    if (a.st_mode and S_IRUSR) != 0'i32: result.incl(fpUserRead)
-    if (a.st_mode and S_IWUSR) != 0'i32: result.incl(fpUserWrite)
-    if (a.st_mode and S_IXUSR) != 0'i32: result.incl(fpUserExec)
-
-    if (a.st_mode and S_IRGRP) != 0'i32: result.incl(fpGroupRead)
-    if (a.st_mode and S_IWGRP) != 0'i32: result.incl(fpGroupWrite)
-    if (a.st_mode and S_IXGRP) != 0'i32: result.incl(fpGroupExec)
-
-    if (a.st_mode and S_IROTH) != 0'i32: result.incl(fpOthersRead)
-    if (a.st_mode and S_IWOTH) != 0'i32: result.incl(fpOthersWrite)
-    if (a.st_mode and S_IXOTH) != 0'i32: result.incl(fpOthersExec)
-  else:
-    when useWinUnicode:
-      wrapUnary(res, getFileAttributesW, filename)
-    else:
-      var res = getFileAttributesA(filename)
-    if res == -1'i32: raiseOSError(osLastError())
-    if (res and FILE_ATTRIBUTE_READONLY) != 0'i32:
-      result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead,
-                fpOthersExec, fpOthersRead}
-    else:
-      result = {fpUserExec..fpOthersRead}
-
-proc setFilePermissions*(filename: string, permissions: set[FilePermission]) {.
-  rtl, extern: "nos$1", tags: [WriteDirEffect].} =
-  ## sets the file permissions for `filename`. `OSError` is raised in case of
-  ## an error. On Windows, only the ``readonly`` flag is changed, depending on
-  ## ``fpUserWrite``.
-  when defined(posix):
-    var p = 0'i32
-    if fpUserRead in permissions: p = p or S_IRUSR
-    if fpUserWrite in permissions: p = p or S_IWUSR
-    if fpUserExec in permissions: p = p or S_IXUSR
-
-    if fpGroupRead in permissions: p = p or S_IRGRP
-    if fpGroupWrite in permissions: p = p or S_IWGRP
-    if fpGroupExec in permissions: p = p or S_IXGRP
+    try:
+      var b: WINBOOL
+      if not isSuccess(checkTokenMembership(0, administratorsGroup, addr b)):
+        raiseOSError(osLastError(), "could not check access token membership")
 
-    if fpOthersRead in permissions: p = p or S_IROTH
-    if fpOthersWrite in permissions: p = p or S_IWOTH
-    if fpOthersExec in permissions: p = p or S_IXOTH
+      result = isSuccess(b)
+    finally:
+      if freeSid(administratorsGroup) != nil:
+        raiseOSError(osLastError(), "failed to free SID for Administrators group")
 
-    if chmod(filename, p) != 0: raiseOSError(osLastError())
   else:
-    when useWinUnicode:
-      wrapUnary(res, getFileAttributesW, filename)
-    else:
-      var res = getFileAttributesA(filename)
-    if res == -1'i32: raiseOSError(osLastError())
-    if fpUserWrite in permissions:
-      res = res and not FILE_ATTRIBUTE_READONLY
-    else:
-      res = res or FILE_ATTRIBUTE_READONLY
-    when useWinUnicode:
-      wrapBinary(res2, setFileAttributesW, filename, res)
-    else:
-      var res2 = setFileAttributesA(filename, res)
-    if res2 == - 1'i32: raiseOSError(osLastError())
+    result = geteuid() == 0
 
-proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
-  tags: [ReadIOEffect, WriteIOEffect].} =
-  ## Copies a file from `source` to `dest`.
-  ##
-  ## If this fails, `OSError` is raised. On the Windows platform this proc will
-  ## copy the source file's attributes into dest. On other platforms you need
-  ## to use `getFilePermissions() <#getFilePermissions>`_ and
-  ## `setFilePermissions() <#setFilePermissions>`_ to copy them by hand (or use
-  ## the convenience `copyFileWithPermissions() <#copyFileWithPermissions>`_
-  ## proc), otherwise `dest` will inherit the default permissions of a newly
-  ## created file for the user. If `dest` already exists, the file attributes
-  ## will be preserved and the content overwritten.
-  when defined(Windows):
-    when useWinUnicode:
-      let s = newWideCString(source)
-      let d = newWideCString(dest)
-      if copyFileW(s, d, 0'i32) == 0'i32: raiseOSError(osLastError())
-    else:
-      if copyFileA(source, dest, 0'i32) == 0'i32: raiseOSError(osLastError())
-  else:
-    # generic version of copyFile which works for any platform:
-    const bufSize = 8000 # better for memory manager
-    var d, s: File
-    if not open(s, source): raiseOSError(osLastError())
-    if not open(d, dest, fmWrite):
-      close(s)
-      raiseOSError(osLastError())
-    var buf = alloc(bufSize)
-    while true:
-      var bytesread = readBuffer(s, buf, bufSize)
-      if bytesread > 0:
-        var byteswritten = writeBuffer(d, buf, bytesread)
-        if bytesread != byteswritten:
-          dealloc(buf)
-          close(s)
-          close(d)
-          raiseOSError(osLastError())
-      if bytesread != bufSize: break
-    dealloc(buf)
-    close(s)
-    flushFile(d)
-    close(d)
-
-when not declared(ENOENT) and not defined(Windows):
-  when NoFakeVars:
-    const ENOENT = cint(2) # 2 on most systems including Solaris
-  else:
-    var ENOENT {.importc, header: "<errno.h>".}: cint
 
-when defined(Windows):
-  when useWinUnicode:
-    template deleteFile(file: untyped): untyped  = deleteFileW(file)
-    template setFileAttributes(file, attrs: untyped): untyped =
-      setFileAttributesW(file, attrs)
-  else:
-    template deleteFile(file: untyped): untyped = deleteFileA(file)
-    template setFileAttributes(file, attrs: untyped): untyped =
-      setFileAttributesA(file, attrs)
-
-proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirEffect].} =
-  ## Removes the `file`. If this fails, returns `false`. This does not fail
-  ## if the file never existed in the first place.
-  ## On Windows, ignores the read-only attribute.
-  result = true
-  when defined(Windows):
-    when useWinUnicode:
-      let f = newWideCString(file)
+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:
-      let f = file
-    if deleteFile(f) == 0:
-      result = false
-      let err = getLastError()
-      if err == ERROR_FILE_NOT_FOUND or err == ERROR_PATH_NOT_FOUND:
-        result = true
-      elif err == ERROR_ACCESS_DENIED and
-         setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) != 0 and
-         deleteFile(f) != 0:
-        result = true
+      WEXITSTATUS(status)
   else:
-    if c_remove(file) != 0'i32 and errno != ENOENT:
-      result = false
-
-proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} =
-  ## Removes the `file`. If this fails, `OSError` is raised. This does not fail
-  ## if the file never existed in the first place.
-  ## On Windows, ignores the read-only attribute.
-  if not tryRemoveFile(file):
-    when defined(Windows):
-      raiseOSError(osLastError())
-    else:
-      raiseOSError(osLastError(), $strerror(errno))
-
-proc tryMoveFSObject(source, dest: string): bool =
-  ## Moves a file or directory from `source` to `dest`. Returns false in case
-  ## of `EXDEV` error. In case of other errors `OSError` is raised. Returns
-  ## true in case of success.
-  when defined(Windows):
-    when useWinUnicode:
-      let s = newWideCString(source)
-      let d = newWideCString(dest)
-      if moveFileExW(s, d, MOVEFILE_COPY_ALLOWED) == 0'i32: raiseOSError(osLastError())
-    else:
-      if moveFileExA(source, dest, MOVEFILE_COPY_ALLOWED) == 0'i32: raiseOSError(osLastError())
-  else:
-    if c_rename(source, dest) != 0'i32:
-      let err = osLastError()
-      if err == EXDEV.OSErrorCode:
-        return false
-      else:
-        raiseOSError(err, $strerror(errno))
-  return true
-
-proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
-  tags: [ReadIOEffect, WriteIOEffect].} =
-  ## Moves a file from `source` to `dest`. If this fails, `OSError` is raised.
-  ## Can be used to `rename files`:idx:
-  if not tryMoveFSObject(source, dest):
-    when not defined(windows):
-      # Fallback to copy & del
-      copyFile(source, dest)
-      try:
-        removeFile(source)
-      except:
-        discard tryRemoveFile(dest)
-        raise
+    status
 
 proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
-  tags: [ExecIOEffect].} =
+  tags: [ExecIOEffect], noWeirdTarget.} =
   ## Executes a `shell command`:idx:.
   ##
   ## Command has the form 'program args' where args are the command
   ## 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)
-
-# Templates for filtering directories and files
-when defined(windows):
-  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 =
-    dirExists(f)
-  template isFile(f: string): bool =
-    fileExists(f)
-
-template defaultWalkFilter(item): bool =
-  ## Walk filter used to return true on both
-  ## files and directories
-  true
-
-template walkCommon(pattern: string, filter) =
-  ## Common code for getting the files and directories with the
-  ## specified `pattern`
-  when defined(windows):
-    var
-      f: WIN32_FIND_DATA
-      res: int
-    res = findFirstFile(pattern, f)
-    if res != -1:
-      defer: findClose(res)
-      let dotPos = searchExtPos(pattern)
-      while true:
-        if not skipFindData(f) and filter(f):
-          # Windows bug/gotcha: 't*.nim' matches 'tfoo.nims' -.- so we check
-          # that the file extensions have the same length ...
-          let ff = getFilename(f)
-          let idx = ff.len - pattern.len + dotPos
-          if dotPos < 0 or idx >= ff.len or ff[idx] == '.' or
-              pattern[dotPos+1] == '*':
-            yield splitFile(pattern).dir / extractFilename(ff)
-        if findNextFile(res, f) == 0'i32:
-          let errCode = getLastError()
-          if errCode == ERROR_NO_MORE_FILES: break
-          else: raiseOSError(errCode.OSErrorCode)
-  else: # here we use glob
-    var
-      f: Glob
-      res: int
-    f.gl_offs = 0
-    f.gl_pathc = 0
-    f.gl_pathv = nil
-    res = glob(pattern, 0, nil, addr(f))
-    defer: globfree(addr(f))
-    if res == 0:
-      for i in 0.. f.gl_pathc - 1:
-        assert(f.gl_pathv[i] != nil)
-        let path = $f.gl_pathv[i]
-        if filter(path):
-          yield path
-
-iterator walkPattern*(pattern: string): string {.tags: [ReadDirEffect].} =
-  ## Iterate over all the files and directories that match the `pattern`.
-  ## On POSIX this uses the `glob`:idx: call.
+  ## of the shell when it has finished (zero if there is no error).
+  ## The proc does not return until the process has finished.
   ##
-  ## `pattern` is OS dependent, but at least the "\*.ext"
-  ## notation is supported.
-  walkCommon(pattern, defaultWalkFilter)
-
-iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect].} =
-  ## Iterate over all the files that match the `pattern`. On POSIX this uses
-  ## the `glob`:idx: call.
-  ##
-  ## `pattern` is OS dependent, but at least the "\*.ext"
-  ## notation is supported.
-  walkCommon(pattern, isFile)
-
-iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect].} =
-  ## Iterate over all the directories that match the `pattern`.
-  ## On POSIX this uses the `glob`:idx: call.
+  ## To execute a program without having a shell involved, use `osproc.execProcess proc
+  ## <osproc.html#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_.
   ##
-  ## `pattern` is OS dependent, but at least the "\*.ext"
-  ## notation is supported.
-  walkCommon(pattern, isDir)
-
-type
-  PathComponent* = enum   ## Enumeration specifying a path component.
-    pcFile,               ## path refers to a file
-    pcLinkToFile,         ## path refers to a symbolic link to a file
-    pcDir,                ## path refers to a directory
-    pcLinkToDir           ## path refers to a symbolic link to a directory
-
-
-when defined(posix):
-  proc getSymlinkFileKind(path: string): PathComponent =
-    # Helper function.
-    var s: Stat
-    assert(path != "")
-    if stat(path, s) < 0'i32:
-      raiseOSError(osLastError())
-    if S_ISDIR(s.st_mode):
-      result = pcLinkToDir
-    else:
-      result = pcLinkToFile
-
-
-proc staticWalkDir(dir: string; relative: bool): seq[
-                  tuple[kind: PathComponent, path: string]] =
-  discard
+  ## **Examples:**
+  ##   ```Nim
+  ##   discard execShellCmd("ls -la")
+  ##   ```
+  result = exitStatusLikeShell(c_system(command))
 
-iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: string] {.
-  tags: [ReadDirEffect].} =
-  ## walks over the directory `dir` and yields for each directory or file in
-  ## `dir`. The component type and full path for each item is returned.
-  ## Walking is not recursive. If ``relative`` is true the resulting path is
-  ## shortened to be relative to ``dir``.
-  ## Example: This directory structure::
-  ##   dirA / dirB / fileB1.txt
-  ##        / dirC
-  ##        / fileA1.txt
-  ##        / fileA2.txt
-  ##
-  ## and this code:
-  ##
-  ## .. code-block:: Nim
-  ##     for kind, path in walkDir("dirA"):
-  ##       echo(path)
-  ##
-  ## produces this output (but not necessarily in this order!)::
-  ##   dirA/dirB
-  ##   dirA/dirC
-  ##   dirA/fileA1.txt
-  ##   dirA/fileA2.txt
-  when nimvm:
-    for k, v in items(staticWalkDir(dir, relative)):
-      yield (k, v)
-  else:
-    when defined(windows):
-      var f: WIN32_FIND_DATA
-      var h = findFirstFile(dir / "*", f)
-      if h != -1:
-        defer: findClose(h)
-        while true:
-          var k = pcFile
-          if not skipFindData(f):
-            if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
-              k = pcDir
-            if (f.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
-              k = succ(k)
-            let xx = if relative: extractFilename(getFilename(f))
-                     else: dir / extractFilename(getFilename(f))
-            yield (k, xx)
-          if findNextFile(h, f) == 0'i32:
-            let errCode = getLastError()
-            if errCode == ERROR_NO_MORE_FILES: break
-            else: raiseOSError(errCode.OSErrorCode)
-    else:
-      var d = opendir(dir)
-      if d != nil:
-        defer: discard closedir(d)
-        while true:
-          var x = readdir(d)
-          if x == nil: break
-          when defined(nimNoArrayToCstringConversion):
-            var y = $cstring(addr x.d_name)
-          else:
-            var y = $x.d_name.cstring
-          if y != "." and y != "..":
-            var s: Stat
-            if not relative:
-              y = dir / y
-            var k = pcFile
-
-            when defined(linux) or defined(macosx) or defined(bsd) or defined(genode):
-              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
-                  else: k = pcLinkToFile
-                yield (k, y)
-                continue
-
-            if lstat(y, s) < 0'i32: break
-            if S_ISDIR(s.st_mode):
-              k = pcDir
-            elif S_ISLNK(s.st_mode):
-              k = getSymlinkFileKind(y)
-            yield (k, y)
-
-iterator walkDirRec*(dir: string, yieldFilter = {pcFile},
-                     followFilter = {pcDir}): string {.tags: [ReadDirEffect].} =
-  ## Recursively walks over the directory `dir` and yields for each file
-  ## or directory in `dir`.
-  ## The full path for each file or directory is returned.
-  ## **Warning**:
-  ## Modifying the directory structure while the iterator
-  ## is traversing may result in undefined behavior!
-  ##
-  ## Walking is recursive. `filters` controls the behaviour of the iterator:
-  ##
-  ## ---------------------   ---------------------------------------------
-  ## yieldFilter             meaning
-  ## ---------------------   ---------------------------------------------
-  ## ``pcFile``              yield real files
-  ## ``pcLinkToFile``        yield symbolic links to files
-  ## ``pcDir``               yield real directories
-  ## ``pcLinkToDir``         yield symbolic links to directories
-  ## ---------------------   ---------------------------------------------
-  ##
-  ## ---------------------   ---------------------------------------------
-  ## followFilter            meaning
-  ## ---------------------   ---------------------------------------------
-  ## ``pcDir``               follow real directories
-  ## ``pcLinkToDir``         follow symbolic links to directories
-  ## ---------------------   ---------------------------------------------
+proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
+  tags: [ReadDirEffect], noWeirdTarget.} =
+  ## Returns the full (`absolute`:idx:) path of an existing file `filename`.
   ##
-  var stack = @[dir]
-  while stack.len > 0:
-    for k, p in walkDir(stack.pop()):
-      if k in {pcDir, pcLinkToDir} and k in followFilter:
-        stack.add(p)
-      if k in yieldFilter:
-        yield p
-
-proc rawRemoveDir(dir: string) =
+  ## Raises `OSError` in case of an error. Follows symlinks.
   when defined(windows):
-    when useWinUnicode:
-      wrapUnary(res, removeDirectoryW, dir)
-    else:
-      var res = removeDirectoryA(dir)
-    let lastError = osLastError()
-    if res == 0'i32 and lastError.int32 != 3'i32 and
-        lastError.int32 != 18'i32 and lastError.int32 != 2'i32:
-      raiseOSError(lastError)
-  else:
-    if rmdir(dir) != 0'i32 and errno != ENOENT: raiseOSError(osLastError())
-
-proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [
-  WriteDirEffect, ReadDirEffect], benign.} =
-  ## Removes the directory `dir` including all subdirectories and files
-  ## in `dir` (recursively).
-  ##
-  ## If this fails, `OSError` is raised. This does not fail if the directory never
-  ## existed in the first place.
-  for kind, path in walkDir(dir):
-    case kind
-    of pcFile, pcLinkToFile, pcLinkToDir: removeFile(path)
-    of pcDir: removeDir(path)
-  rawRemoveDir(dir)
-
-proc rawCreateDir(dir: string): bool =
-  # Try to create one directory (not the whole path).
-  # returns `true` for success, `false` if the path has previously existed
-  #
-  # This is a thin wrapper over mkDir (or alternatives on other systems),
-  # so in case of a pre-existing path we don't check that it is a directory.
-  when defined(solaris):
-    let res = mkdir(dir, 0o777)
-    if res == 0'i32:
-      result = true
-    elif errno in {EEXIST, ENOSYS}:
-      result = false
-    else:
-      raiseOSError(osLastError())
-  elif defined(posix):
-    let res = mkdir(dir, 0o777)
-    if res == 0'i32:
-      result = true
-    elif errno == EEXIST:
-      result = false
-    else:
-      #echo res
-      raiseOSError(osLastError())
+    var bufsize = MAX_PATH.int32
+    var unused: WideCString = nil
+    var res = newWideCString(bufsize)
+    while true:
+      var L = getFullPathNameW(newWideCString(filename), bufsize, res, unused)
+      if L == 0'i32:
+        raiseOSError(osLastError(), filename)
+      elif L > bufsize:
+        res = newWideCString(L)
+        bufsize = L
+      else:
+        result = res$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):
+      result = x
+    if not fileExists(result) and not dirExists(result):
+      # consider using: `raiseOSError(osLastError(), result)`
+      raise newException(OSError, "file '" & result & "' does not exist")
   else:
-    when useWinUnicode:
-      wrapUnary(res, createDirectoryW, dir)
-    else:
-      let res = createDirectoryA(dir)
-
-    if res != 0'i32:
-      result = true
-    elif getLastError() == 183'i32:
-      result = false
+    # 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(), filename)
     else:
-      raiseOSError(osLastError())
-
-proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1",
-  tags: [WriteDirEffect, ReadDirEffect].} =
-  ## Check if a `directory`:idx: `dir` exists, and create it otherwise.
-  ##
-  ## Does not create parent directories (fails if parent does not exist).
-  ## Returns `true` if the directory already exists, and `false`
-  ## otherwise.
-  result = not rawCreateDir(dir)
-  if result:
-    # path already exists - need to check that it is indeed a directory
-    if not existsDir(dir):
-      raise newException(IOError, "Failed to create the directory")
-
-proc createDir*(dir: string) {.rtl, extern: "nos$1",
-  tags: [WriteDirEffect, ReadDirEffect].} =
-  ## Creates the `directory`:idx: `dir`.
-  ##
-  ## The directory may contain several subdirectories that do not exist yet.
-  ## The full path is created. If this fails, `OSError` is raised. It does **not**
-  ## fail if the directory already exists because for most usages this does not
-  ## indicate an error.
-  var omitNext = false
-  when doslikeFileSystem:
-    omitNext = isAbsolute(dir)
-  for i in 1.. dir.len-1:
-    if dir[i] in {DirSep, AltSep}:
-      if omitNext:
-        omitNext = false
-      else:
-        discard existsOrCreateDir(substr(dir, 0, i-1))
-
-  # The loop does not create the dir itself if it doesn't end in separator
-  if dir.len > 0 and not omitNext and
-     dir[^1] notin {DirSep, AltSep}:
-    discard existsOrCreateDir(dir)
+      result = $r
+      c_free(cast[pointer](r))
 
-proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
-  tags: [WriteIOEffect, ReadIOEffect], benign.} =
-  ## Copies a directory from `source` to `dest`.
-  ##
-  ## If this fails, `OSError` is raised. On the Windows platform this proc will
-  ## copy the attributes from `source` into `dest`. On other platforms created
-  ## files and directories will inherit the default permissions of a newly
-  ## created file/directory for the user. To preserve attributes recursively on
-  ## these platforms use `copyDirWithPermissions() <#copyDirWithPermissions>`_.
-  createDir(dest)
-  for kind, path in walkDir(source):
-    var noSource = path.substr(source.len()+1)
-    case kind
-    of pcFile:
-      copyFile(path, dest / noSource)
-    of pcDir:
-      copyDir(path, dest / noSource)
-    else: discard
-
-proc createSymlink*(src, dest: string) =
-  ## Create a symbolic link at `dest` which points to the item specified
-  ## by `src`. On most operating systems, will fail if a link already exists.
+proc getCurrentCompilerExe*(): string {.compileTime.} = discard
+  ## Returns the path of the currently running Nim compiler or nimble executable.
   ##
-  ## **Warning**:
-  ## Some OS's (such as Microsoft Windows) restrict the creation
-  ## of symlinks to root users (administrators).
-  when defined(Windows):
-    # 2 is the SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE. This allows
-    # anyone with developer mode on to create a link
-    let flag = dirExists(src).int32 or 2
-    when useWinUnicode:
-      var wSrc = newWideCString(src)
-      var wDst = newWideCString(dest)
-      if createSymbolicLinkW(wDst, wSrc, flag) == 0 or getLastError() != 0:
-        raiseOSError(osLastError())
-    else:
-      if createSymbolicLinkA(dest, src, flag) == 0 or getLastError() != 0:
-        raiseOSError(osLastError())
-  else:
-    if symlink(src, dest) != 0:
-      raiseOSError(osLastError())
+  ## Can be used to retrieve 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).
 
-proc createHardlink*(src, dest: string) =
+proc createHardlink*(src, dest: string) {.noWeirdTarget.} =
   ## Create a hard link at `dest` which points to the item specified
   ## by `src`.
   ##
-  ## **Warning**: Some OS's restrict the creation of hard links to
-  ## root users (administrators).
-  when defined(Windows):
-    when useWinUnicode:
-      var wSrc = newWideCString(src)
-      var wDst = newWideCString(dest)
-      if createHardLinkW(wDst, wSrc, nil) == 0:
-        raiseOSError(osLastError())
-    else:
-      if createHardLinkA(dest, src, nil) == 0:
-        raiseOSError(osLastError())
+  ## .. warning:: Some OS's restrict the creation of hard links to
+  ##   root users (administrators).
+  ##
+  ## See also:
+  ## * `createSymlink proc`_
+  when defined(windows):
+    var wSrc = newWideCString(src)
+    var wDst = newWideCString(dest)
+    if createHardLinkW(wDst, wSrc, nil) == 0:
+      raiseOSError(osLastError(), $(src, dest))
   else:
     if link(src, dest) != 0:
-      raiseOSError(osLastError())
-
-proc parseCmdLine*(c: string): seq[string] {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Splits a `command line`:idx: into several components;
-  ## This proc is only occasionally useful, better use the `parseopt` module.
-  ##
-  ## On Windows, it uses the following parsing rules
-  ## (see http://msdn.microsoft.com/en-us/library/17w5ykft.aspx ):
-  ##
-  ## * Arguments are delimited by white space, which is either a space or a tab.
-  ## * The caret character (^) is not recognized as an escape character or
-  ##   delimiter. The character is handled completely by the command-line parser
-  ##   in the operating system before being passed to the argv array in the
-  ##   program.
-  ## * A string surrounded by double quotation marks ("string") is interpreted
-  ##   as a single argument, regardless of white space contained within. A
-  ##   quoted string can be embedded in an argument.
-  ## * A double quotation mark preceded by a backslash (\") is interpreted as a
-  ##   literal double quotation mark character (").
-  ## * Backslashes are interpreted literally, unless they immediately precede
-  ##   a double quotation mark.
-  ## * If an even number of backslashes is followed by a double quotation mark,
-  ##   one backslash is placed in the argv array for every pair of backslashes,
-  ##   and the double quotation mark is interpreted as a string delimiter.
-  ## * If an odd number of backslashes is followed by a double quotation mark,
-  ##   one backslash is placed in the argv array for every pair of backslashes,
-  ##   and the double quotation mark is "escaped" by the remaining backslash,
-  ##   causing a literal double quotation mark (") to be placed in argv.
-  ##
-  ## On Posix systems, it uses the following parsing rules:
-  ## Components are separated by whitespace unless the whitespace
-  ## occurs within ``"`` or ``'`` quotes.
-  result = @[]
-  var i = 0
-  var a = ""
-  while true:
-    setLen(a, 0)
-    # eat all delimiting whitespace
-    while i < c.len and c[i] in {' ', '\t', '\l', '\r'}: inc(i)
-    if i >= c.len: break
-    when defined(windows):
-      # parse a single argument according to the above rules:
-      var inQuote = false
-      while i < c.len:
-        case c[i]
-        of '\\':
-          var j = i
-          while j < c.len and c[j] == '\\': inc(j)
-          if j < c.len and c[j] == '"':
-            for k in 1..(j-i) div 2: a.add('\\')
-            if (j-i) mod 2 == 0:
-              i = j
-            else:
-              a.add('"')
-              i = j+1
-          else:
-            a.add(c[i])
-            inc(i)
-        of '"':
-          inc(i)
-          if not inQuote: inQuote = true
-          elif i < c.len and c[i] == '"':
-            a.add(c[i])
-            inc(i)
-          else:
-            inQuote = false
-            break
-        of ' ', '\t':
-          if not inQuote: break
-          a.add(c[i])
-          inc(i)
-        else:
-          a.add(c[i])
-          inc(i)
-    else:
-      case c[i]
-      of '\'', '\"':
-        var delim = c[i]
-        inc(i) # skip ' or "
-        while i < c.len and c[i] != delim:
-          add a, c[i]
-          inc(i)
-        if i < c.len: inc(i)
-      else:
-        while i < c.len and c[i] > ' ':
-          add(a, c[i])
-          inc(i)
-    add(result, a)
-
-proc copyFileWithPermissions*(source, dest: string,
-                              ignorePermissionErrors = true) =
-  ## Copies a file from `source` to `dest` preserving file permissions.
-  ##
-  ## This is a wrapper proc around `copyFile() <#copyFile>`_,
-  ## `getFilePermissions() <#getFilePermissions>`_ and `setFilePermissions()
-  ## <#setFilePermissions>`_ on non Windows platform. On Windows this proc is
-  ## just a wrapper for `copyFile() <#copyFile>`_ since that proc already
-  ## copies attributes.
-  ##
-  ## On non Windows systems permissions are copied after the file itself has
-  ## been copied, which won't happen atomically and could lead to a race
-  ## condition. If `ignorePermissionErrors` is true, errors while
-  ## reading/setting file attributes will be ignored, otherwise will raise
-  ## `OSError`.
-  copyFile(source, dest)
-  when not defined(Windows):
-    try:
-      setFilePermissions(dest, getFilePermissions(source))
-    except:
-      if not ignorePermissionErrors:
-        raise
-
-proc copyDirWithPermissions*(source, dest: string,
-    ignorePermissionErrors = true) {.rtl, extern: "nos$1",
-    tags: [WriteIOEffect, ReadIOEffect], benign.} =
-  ## Copies a directory from `source` to `dest` preserving file permissions.
-  ##
-  ## If this fails, `OSError` is raised. This is a wrapper proc around `copyDir()
-  ## <#copyDir>`_ and `copyFileWithPermissions() <#copyFileWithPermissions>`_
-  ## on non Windows platforms. On Windows this proc is just a wrapper for
-  ## `copyDir() <#copyDir>`_ since that proc already copies attributes.
-  ##
-  ## On non Windows systems permissions are copied after the file or directory
-  ## itself has been copied, which won't happen atomically and could lead to a
-  ## race condition. If `ignorePermissionErrors` is true, errors while
-  ## reading/setting file attributes will be ignored, otherwise will raise
-  ## `OSError`.
-  createDir(dest)
-  when not defined(Windows):
-    try:
-      setFilePermissions(dest, getFilePermissions(source))
-    except:
-      if not ignorePermissionErrors:
-        raise
-  for kind, path in walkDir(source):
-    var noSource = path.substr(source.len()+1)
-    case kind
-    of pcFile:
-      copyFileWithPermissions(path, dest / noSource, ignorePermissionErrors)
-    of pcDir:
-      copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors)
-    else: discard
+      raiseOSError(osLastError(), $(src, dest))
 
 proc inclFilePermissions*(filename: string,
                           permissions: set[FilePermission]) {.
-  rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect].} =
-  ## a convenience procedure for:
-  ##
-  ## .. code-block:: nim
+  rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect], noWeirdTarget.} =
+  ## A convenience proc for:
+  ##   ```nim
   ##   setFilePermissions(filename, getFilePermissions(filename)+permissions)
+  ##   ```
   setFilePermissions(filename, getFilePermissions(filename)+permissions)
 
 proc exclFilePermissions*(filename: string,
                           permissions: set[FilePermission]) {.
-  rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect].} =
-  ## a convenience procedure for:
-  ##
-  ## .. code-block:: nim
+  rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect], noWeirdTarget.} =
+  ## A convenience proc for:
+  ##   ```nim
   ##   setFilePermissions(filename, getFilePermissions(filename)-permissions)
+  ##   ```
   setFilePermissions(filename, getFilePermissions(filename)-permissions)
 
-proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect].} =
-  ## Moves a directory from `source` to `dest`. If this fails, `OSError` is raised.
-  if not tryMoveFSObject(source, dest):
-    when not defined(windows):
-      # Fallback to copy & del
-      copyDir(source, dest)
-      removeDir(source)
-
-#include ospaths
-
-proc expandSymlink*(symlinkPath: string): string =
-  ## Returns a string representing the path to which the symbolic link points.
-  ##
-  ## On Windows this is a noop, ``symlinkPath`` is simply returned.
-  when defined(windows):
-    result = symlinkPath
-  else:
-    result = newString(256)
-    var len = readlink(symlinkPath, result, 256)
-    if len < 0:
-      raiseOSError(osLastError())
-    if len > 256:
-      result = newString(len+1)
-      len = readlink(symlinkPath, result, len)
-    setLen(result, len)
-
-when defined(nimdoc):
-  # Common forward declaration docstring block for parameter retrieval procs.
-  proc paramCount*(): int {.tags: [ReadIOEffect].} =
-    ## Returns the number of `command line arguments`:idx: given to the
-    ## application.
-    ##
-    ## Unlike `argc`:idx: in C, if your binary was called without parameters this
-    ## will return zero.
-    ## You can query each individual paramater with `paramStr() <#paramStr>`_
-    ## or retrieve all of them in one go with `commandLineParams()
-    ## <#commandLineParams>`_.
-    ##
-    ## **Availability**: When generating a dynamic library (see --app:lib) on
-    ## Posix this proc is not defined.
-    ## Test for availability using `declared() <system.html#declared>`_.
-    ## Example:
-    ##
-    ## .. code-block:: nim
-    ##   when declared(paramCount):
-    ##     # Use paramCount() here
-    ##   else:
-    ##     # Do something else!
-
-  proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} =
-    ## Returns the `i`-th `command line argument`:idx: given to the application.
-    ##
-    ## `i` should be in the range `1..paramCount()`, the `IndexError`
-    ## exception will be raised for invalid values.  Instead of iterating over
-    ## `paramCount() <#paramCount>`_ with this proc you can call the
-    ## convenience `commandLineParams() <#commandLineParams>`_.
-    ##
-    ## Similarly to `argv`:idx: in C,
-    ## it is possible to call ``paramStr(0)`` but this will return OS specific
-    ## contents (usually the name of the invoked executable). You should avoid
-    ## this and call `getAppFilename() <#getAppFilename>`_ instead.
-    ##
-    ## **Availability**: When generating a dynamic library (see --app:lib) on
-    ## Posix this proc is not defined.
-    ## Test for availability using `declared() <system.html#declared>`_.
-    ## Example:
-    ##
-    ## .. code-block:: nim
-    ##   when declared(paramStr):
-    ##     # Use paramStr() here
-    ##   else:
-    ##     # Do something else!
-
-elif defined(windows):
-  # Since we support GUI applications with Nim, we sometimes generate
-  # a WinMain entry proc. But a WinMain proc has no access to the parsed
-  # command line arguments. The way to get them differs. Thus we parse them
-  # ourselves. This has the additional benefit that the program's behaviour
-  # is always the same -- independent of the used C compiler.
-  var
-    ownArgv {.threadvar.}: seq[string]
-
-  proc paramCount*(): int {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =
-    # Docstring in nimdoc block.
-    if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLine())
-    result = ownArgv.len-1
-
-  proc paramStr*(i: int): TaintedString {.rtl, extern: "nos$1",
-    tags: [ReadIOEffect].} =
-    # Docstring in nimdoc block.
-    if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLine())
-    if i < ownArgv.len and i >= 0: return TaintedString(ownArgv[i])
-    raise newException(IndexError, "invalid index")
-
-elif not defined(createNimRtl) and
-  not(defined(posix) and appType == "lib") and
-  not defined(genode):
-  # On Posix, there is no portable way to get the command line from a DLL.
-  var
-    cmdCount {.importc: "cmdCount".}: cint
-    cmdLine {.importc: "cmdLine".}: cstringArray
-
-  proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} =
-    # Docstring in nimdoc block.
-    if i < cmdCount and i >= 0: return TaintedString($cmdLine[i])
-    raise newException(IndexError, "invalid index")
-
-  proc paramCount*(): int {.tags: [ReadIOEffect].} =
-    # Docstring in nimdoc block.
-    result = cmdCount-1
-
-when declared(paramCount) or defined(nimdoc):
-  proc commandLineParams*(): seq[TaintedString] =
-    ## Convenience proc which returns the command line parameters.
-    ##
-    ## This returns **only** the parameters. If you want to get the application
-    ## executable filename, call `getAppFilename() <#getAppFilename>`_.
-    ##
-    ## **Availability**: On Posix there is no portable way to get the command
-    ## line from a DLL and thus the proc isn't defined in this environment. You
-    ## can test for its availability with `declared() <system.html#declared>`_.
-    ## Example:
-    ##
-    ## .. code-block:: nim
-    ##   when declared(commandLineParams):
-    ##     # Use commandLineParams() here
-    ##   else:
-    ##     # Do something else!
-    result = @[]
-    for i in 1..paramCount():
-      result.add(paramStr(i))
-
-when defined(freebsd) or defined(dragonfly):
-  proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize,
-              newp: pointer, newplen: csize): cint
+when not weirdTarget and (defined(freebsd) or defined(dragonfly) or defined(netbsd)):
+  proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize_t,
+              newp: pointer, newplen: csize_t): cint
        {.importc: "sysctl",header: """#include <sys/types.h>
-                                      #include <sys/sysctl.h>"""}
+                                      #include <sys/sysctl.h>""".}
   const
     CTL_KERN = 1
     KERN_PROC = 14
@@ -1333,47 +508,89 @@ when defined(freebsd) or defined(dragonfly):
 
   when defined(freebsd):
     const KERN_PROC_PATHNAME = 12
+  elif defined(netbsd):
+    const KERN_PROC_ARGS = 48
+    const KERN_PROC_PATHNAME = 5
   else:
     const KERN_PROC_PATHNAME = 9
 
   proc getApplFreebsd(): string =
-    var pathLength = csize(MAX_PATH)
-    result = newString(pathLength)
-    var req = [CTL_KERN.cint, KERN_PROC.cint, KERN_PROC_PATHNAME.cint, -1.cint]
-    while true:
-      let res = sysctl(addr req[0], 4, cast[pointer](addr result[0]),
-                       addr pathLength, nil, 0)
-      if res < 0:
-        let err = osLastError()
-        if err.int32 == ENOMEM:
-          result = newString(pathLength)
-        else:
-          result.setLen(0) # error!
-          break
-      else:
-        result.setLen(pathLength)
-        break
+    var pathLength = csize_t(0)
+
+    when defined(netbsd):
+      var req = [CTL_KERN.cint, KERN_PROC_ARGS.cint, -1.cint, KERN_PROC_PATHNAME.cint]
+    else:
+      var req = [CTL_KERN.cint, KERN_PROC.cint, KERN_PROC_PATHNAME.cint, -1.cint]
+
+    # first call to get the required length
+    var res = sysctl(addr req[0], 4, nil, addr pathLength, nil, 0)
+
+    if res < 0:
+      return ""
 
-when defined(linux) or defined(solaris) or defined(bsd) or defined(aix):
+    result.setLen(pathLength)
+    res = sysctl(addr req[0], 4, addr result[0], addr pathLength, nil, 0)
+
+    if res < 0:
+      return ""
+
+    let realLen = len(cstring(result))
+    setLen(result, realLen)
+
+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)
-    if len > 256:
+    result = newString(maxSymlinkLen)
+    var len = readlink(procPath, result.cstring, maxSymlinkLen)
+    if len > maxSymlinkLen:
       result = newString(len+1)
-      len = readlink(procPath, result, len)
+      len = readlink(procPath, result.cstring, len)
     setLen(result, len)
 
-when not (defined(windows) or defined(macosx)):
+when not weirdTarget and defined(openbsd):
+  proc getApplOpenBsd(): string =
+    # similar to getApplHeuristic, but checks current working directory
+    when declared(paramStr):
+      result = ""
+
+      # POSIX guaranties that this contains the executable
+      # as it has been executed by the calling process
+      let exePath = paramStr(0)
+
+      if len(exePath) == 0:
+        return ""
+
+      if exePath[0] == DirSep:
+        # path is absolute
+        result = exePath
+      else:
+        # not an absolute path, check if it's relative to the current working directory
+        for i in 1..<len(exePath):
+          if exePath[i] == DirSep:
+            result = joinPath(getCurrentDir(), exePath)
+            break
+
+      if len(result) > 0:
+        return expandFilename(result)
+
+      # search in path
+      for p in split(getEnv("PATH"), {PathSep}):
+        var x = joinPath(p, exePath)
+        if fileExists(x):
+          return expandFilename(x)
+    else:
+      result = ""
+
+when not (defined(windows) or defined(macosx) or weirdTarget):
   proc getApplHeuristic(): string =
     when declared(paramStr):
-      result = string(paramStr(0))
+      result = paramStr(0)
       # POSIX guaranties that this contains the executable
       # as it has been executed by the calling process
       if len(result) > 0 and result[0] != DirSep: # not an absolute path?
         # iterate over any path in the $PATH environment variable
-        for p in split(string(getEnv("PATH")), {PathSep}):
+        for p in split(getEnv("PATH"), {PathSep}):
           var x = joinPath(p, result)
-          if existsFile(x): return x
+          if fileExists(x): return x
     else:
       result = ""
 
@@ -1389,10 +606,33 @@ when defined(macosx):
   proc getExecPath2(c: cstring, size: var cuint32): bool {.
     importc: "_NSGetExecutablePath", header: "<mach-o/dyld.h>".}
 
-proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =
+when defined(haiku):
+  const
+    PATH_MAX = 1024
+    B_FIND_PATH_IMAGE_PATH = 1000
+
+  proc find_path(codePointer: pointer, baseDirectory: cint, subPath: cstring,
+                 pathBuffer: cstring, bufferSize: csize_t): int32
+                {.importc, header: "<FindDirectory.h>".}
+
+  proc getApplHaiku(): string =
+    result = newString(PATH_MAX)
+
+    if find_path(nil, B_FIND_PATH_IMAGE_PATH, nil, result, PATH_MAX) == 0:
+      let realLen = len(cstring(result))
+      setLen(result, realLen)
+    else:
+      result = ""
+
+proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noWeirdTarget, raises: [].} =
   ## Returns the filename of the application's executable.
+  ## This proc will resolve symlinks.
   ##
-  ## This procedure will resolve symlinks.
+  ## Returns empty string when name is unavailable
+  ##
+  ## See also:
+  ## * `getAppDir proc`_
+  ## * `getCurrentCompilerExe proc`_
 
   # Linux: /proc/<pid>/exe
   # Solaris:
@@ -1400,60 +640,62 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =
   # /proc/<pid>/path/a.out (complete pathname)
   when defined(windows):
     var bufsize = int32(MAX_PATH)
-    when useWinUnicode:
-      var buf = newWideCString("", bufsize)
-      while true:
-        var L = getModuleFileNameW(0, buf, bufsize)
-        if L == 0'i32:
-          result = "" # error!
-          break
-        elif L > bufsize:
-          buf = newWideCString("", L)
-          bufsize = L
-        else:
-          result = buf$L
-          break
-    else:
-      result = newString(bufsize)
-      while true:
-        var L = getModuleFileNameA(0, result, bufsize)
-        if L == 0'i32:
-          result = "" # error!
-          break
-        elif L > bufsize:
-          result = newString(L)
-          bufsize = L
-        else:
-          setLen(result, L)
-          break
+    var buf = newWideCString(bufsize)
+    while true:
+      var L = getModuleFileNameW(0, buf, bufsize)
+      if L == 0'i32:
+        result = "" # error!
+        break
+      elif L > bufsize:
+        buf = newWideCString(L)
+        bufsize = L
+      else:
+        result = buf$L
+        break
   elif defined(macosx):
-    var size: cuint32
+    var size = cuint32(0)
     getExecPath1(nil, size)
     result = newString(int(size))
-    if getExecPath2(result, size):
+    if getExecPath2(result.cstring, size):
       result = "" # error!
     if result.len > 0:
-      result = result.expandFilename
+      try:
+        result = result.expandFilename
+      except OSError:
+        result = ""
   else:
-    when defined(linux) or defined(aix) or defined(netbsd):
+    when defined(linux) or defined(aix):
       result = getApplAux("/proc/self/exe")
     elif defined(solaris):
       result = getApplAux("/proc/" & $getpid() & "/path/a.out")
     elif defined(genode):
-      raiseOSError(OSErrorCode(-1), "POSIX command line not supported")
-    elif defined(freebsd) or defined(dragonfly):
+      result = "" # Not supported
+    elif defined(freebsd) or defined(dragonfly) or defined(netbsd):
       result = getApplFreebsd()
+    elif defined(haiku):
+      result = getApplHaiku()
+    elif defined(openbsd):
+      result = try: getApplOpenBsd() except OSError: ""
+    elif defined(nintendoswitch):
+      result = ""
+
     # little heuristic that may work on other POSIX-like systems:
     if result.len == 0:
-      result = getApplHeuristic()
+      result = try: getApplHeuristic() except OSError: ""
 
-proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =
+proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noWeirdTarget.} =
   ## Returns the directory of the application's executable.
+  ##
+  ## See also:
+  ## * `getAppFilename proc`_
   result = splitFile(getAppFilename()).dir
 
-proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect].} =
-  ## sleeps `milsecs` milliseconds.
+proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect], noWeirdTarget.} =
+  ## Sleeps `milsecs` milliseconds.
+  ## A negative `milsecs` causes sleep to return immediately.
   when defined(windows):
+    if milsecs < 0:
+      return  # fixes #23732
     winlean.sleep(int32(milsecs))
   else:
     var a, b: Timespec
@@ -1462,23 +704,22 @@ proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect].} =
     discard posix.nanosleep(a, b)
 
 proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1",
-  tags: [ReadIOEffect].} =
-  ## returns the file size of `file` (in bytes). An ``OSError`` exception is
+  tags: [ReadIOEffect], noWeirdTarget.} =
+  ## Returns the file size of `file` (in bytes). ``OSError`` is
   ## raised in case of an error.
   when defined(windows):
     var a: WIN32_FIND_DATA
     var resA = findFirstFile(file, a)
-    if resA == -1: raiseOSError(osLastError())
+    if resA == -1: raiseOSError(osLastError(), file)
     result = rdFileSize(a)
     findClose(resA)
   else:
-    var f: File
-    if open(f, file):
-      result = getFileSize(f)
-      close(f)
-    else: raiseOSError(osLastError())
+    var rawInfo: Stat
+    if stat(file, rawInfo) < 0'i32:
+      raiseOSError(osLastError(), file)
+    rawInfo.st_size
 
-when defined(Windows):
+when defined(windows) or weirdTarget:
   type
     DeviceId* = int32
     FileId* = int64
@@ -1490,55 +731,74 @@ else:
 type
   FileInfo* = object
     ## Contains information associated with a file object.
-    id*: tuple[device: DeviceId, file: FileId] # Device and file id.
-    kind*: PathComponent # Kind of file object - directory, symlink, etc.
-    size*: BiggestInt # Size of file.
-    permissions*: set[FilePermission] # File permissions
-    linkCount*: BiggestInt # Number of hard links the file object has.
-    lastAccessTime*: times.Time # Time file was last accessed.
-    lastWriteTime*: times.Time # Time file was last modified/written to.
-    creationTime*: times.Time # Time file was created. Not supported on all systems!
+    ##
+    ## See also:
+    ## * `getFileInfo(handle) proc`_
+    ## * `getFileInfo(file) proc`_
+    ## * `getFileInfo(path, followSymlink) proc`_
+    id*: tuple[device: DeviceId, file: FileId] ## Device and file id.
+    kind*: PathComponent              ## Kind of file object - directory, symlink, etc.
+    size*: BiggestInt                 ## Size of file.
+    permissions*: set[FilePermission] ## File permissions
+    linkCount*: BiggestInt            ## Number of hard links the file object has.
+    lastAccessTime*: times.Time       ## Time file was last accessed.
+    lastWriteTime*: times.Time        ## Time file was last modified/written to.
+    creationTime*: times.Time         ## Time file was created. Not supported on all systems!
+    blockSize*: int                   ## Preferred I/O block size for this object.
+                                      ## In some filesystems, this may vary from file to file.
+    isSpecial*: bool                  ## Is file special? (on Unix some "files"
+                                      ## can be special=non-regular like FIFOs,
+                                      ## devices); for directories `isSpecial`
+                                      ## is always `false`, for symlinks it is
+                                      ## the same as for the link's target.
 
 template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
   ## Transforms the native file info structure into the one nim uses.
   ## 'rawInfo' is either a 'BY_HANDLE_FILE_INFORMATION' structure on Windows,
   ## or a 'Stat' structure on posix
-  when defined(Windows):
-    template merge(a, b): untyped = a or (b shl 32)
+  when defined(windows):
+    template merge[T](a, b): untyped =
+       cast[T](
+        (uint64(cast[uint32](a))) or
+        (uint64(cast[uint32](b)) shl 32)
+       )
     formalInfo.id.device = rawInfo.dwVolumeSerialNumber
-    formalInfo.id.file = merge(rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh)
-    formalInfo.size = merge(rawInfo.nFileSizeLow, rawInfo.nFileSizeHigh)
+    formalInfo.id.file = merge[FileId](rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh)
+    formalInfo.size = merge[BiggestInt](rawInfo.nFileSizeLow, rawInfo.nFileSizeHigh)
     formalInfo.linkCount = rawInfo.nNumberOfLinks
     formalInfo.lastAccessTime = fromWinTime(rdFileTime(rawInfo.ftLastAccessTime))
     formalInfo.lastWriteTime = fromWinTime(rdFileTime(rawInfo.ftLastWriteTime))
     formalInfo.creationTime = fromWinTime(rdFileTime(rawInfo.ftCreationTime))
+    formalInfo.blockSize = 8192 # xxx use Windows API instead of hardcoding
 
     # Retrieve basic permissions
     if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_READONLY) != 0'i32:
       formalInfo.permissions = {fpUserExec, fpUserRead, fpGroupExec,
                                 fpGroupRead, fpOthersExec, fpOthersRead}
     else:
-      result.permissions = {fpUserExec..fpOthersRead}
+      formalInfo.permissions = {fpUserExec..fpOthersRead}
 
     # Retrieve basic file kind
-    result.kind = pcFile
     if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
       formalInfo.kind = pcDir
+    else:
+      formalInfo.kind = pcFile
     if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
-      formalInfo.kind = succ(result.kind)
+      formalInfo.kind = succ(formalInfo.kind)
 
   else:
     template checkAndIncludeMode(rawMode, formalMode: untyped) =
-      if (rawInfo.st_mode and rawMode) != 0'i32:
+      if (rawInfo.st_mode and rawMode.Mode) != 0.Mode:
         formalInfo.permissions.incl(formalMode)
     formalInfo.id = (rawInfo.st_dev, rawInfo.st_ino)
     formalInfo.size = rawInfo.st_size
-    formalInfo.linkCount = rawInfo.st_Nlink.BiggestInt
+    formalInfo.linkCount = rawInfo.st_nlink.BiggestInt
     formalInfo.lastAccessTime = rawInfo.st_atim.toTime
     formalInfo.lastWriteTime = rawInfo.st_mtim.toTime
     formalInfo.creationTime = rawInfo.st_ctim.toTime
+    formalInfo.blockSize = rawInfo.st_blksize
 
-    result.permissions = {}
+    formalInfo.permissions = {}
     checkAndIncludeMode(S_IRUSR, fpUserRead)
     checkAndIncludeMode(S_IWUSR, fpUserWrite)
     checkAndIncludeMode(S_IXUSR, fpUserExec)
@@ -1551,101 +811,169 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
     checkAndIncludeMode(S_IWOTH, fpOthersWrite)
     checkAndIncludeMode(S_IXOTH, fpOthersExec)
 
-    formalInfo.kind = pcFile
-    if S_ISDIR(rawInfo.st_mode):
-      formalInfo.kind = pcDir
-    elif S_ISLNK(rawInfo.st_mode):
-      assert(path != "") # symlinks can't occur for file handles
-      formalInfo.kind = getSymlinkFileKind(path)
+    (formalInfo.kind, formalInfo.isSpecial) =
+      if S_ISDIR(rawInfo.st_mode):
+        (pcDir, false)
+      elif S_ISLNK(rawInfo.st_mode):
+        assert(path != "") # symlinks can't occur for file handles
+        getSymlinkFileKind(path)
+      else:
+        (pcFile, not S_ISREG(rawInfo.st_mode))
 
-proc getFileInfo*(handle: FileHandle): FileInfo =
+when defined(js):
+  when not declared(FileHandle):
+    type FileHandle = distinct int32
+  when not declared(File):
+    type File = object
+
+proc getFileInfo*(handle: FileHandle): FileInfo {.noWeirdTarget.} =
   ## Retrieves file information for the file object represented by the given
   ## handle.
   ##
   ## If the information cannot be retrieved, such as when the file handle
-  ## is invalid, an error will be thrown.
+  ## is invalid, `OSError` is raised.
+  ##
+  ## See also:
+  ## * `getFileInfo(file) proc`_
+  ## * `getFileInfo(path, followSymlink) proc`_
+
   # Done: ID, Kind, Size, Permissions, Link Count
-  when defined(Windows):
+  when defined(windows):
     var rawInfo: BY_HANDLE_FILE_INFORMATION
     # We have to use the super special '_get_osfhandle' call (wrapped above)
-    # To transform the C file descripter to a native file handle.
+    # To transform the C file descriptor to a native file handle.
     var realHandle = get_osfhandle(handle)
     if getFileInformationByHandle(realHandle, addr rawInfo) == 0:
-      raiseOSError(osLastError())
+      raiseOSError(osLastError(), $handle)
     rawToFormalFileInfo(rawInfo, "", result)
   else:
     var rawInfo: Stat
     if fstat(handle, rawInfo) < 0'i32:
-      raiseOSError(osLastError())
+      raiseOSError(osLastError(), $handle)
     rawToFormalFileInfo(rawInfo, "", result)
 
-proc getFileInfo*(file: File): FileInfo =
+proc getFileInfo*(file: File): FileInfo {.noWeirdTarget.} =
+  ## Retrieves file information for the file object.
+  ##
+  ## See also:
+  ## * `getFileInfo(handle) proc`_
+  ## * `getFileInfo(path, followSymlink) proc`_
   if file.isNil:
     raise newException(IOError, "File is nil")
   result = getFileInfo(file.getFileHandle())
 
-proc getFileInfo*(path: string, followSymlink = true): FileInfo =
+proc getFileInfo*(path: string, followSymlink = true): FileInfo {.noWeirdTarget.} =
   ## Retrieves file information for the file object pointed to by `path`.
   ##
   ## Due to intrinsic differences between operating systems, the information
-  ## contained by the returned `FileInfo` structure will be slightly different
-  ## across platforms, and in some cases, incomplete or inaccurate.
+  ## contained by the returned `FileInfo object`_ will be slightly
+  ## different across platforms, and in some cases, incomplete or inaccurate.
   ##
-  ## When `followSymlink` is true, symlinks are followed and the information
-  ## retrieved is information related to the symlink's target. Otherwise,
-  ## information on the symlink itself is retrieved.
+  ## When `followSymlink` is true (default), symlinks are followed and the
+  ## information retrieved is information related to the symlink's target.
+  ## Otherwise, information on the symlink itself is retrieved (however,
+  ## field `isSpecial` is still determined from the target on Unix).
   ##
   ## If the information cannot be retrieved, such as when the path doesn't
   ## exist, or when permission restrictions prevent the program from retrieving
-  ## file information, an error will be thrown.
-  when defined(Windows):
+  ## file information, `OSError` is raised.
+  ##
+  ## See also:
+  ## * `getFileInfo(handle) proc`_
+  ## * `getFileInfo(file) proc`_
+  when defined(windows):
     var
       handle = openHandle(path, followSymlink)
       rawInfo: BY_HANDLE_FILE_INFORMATION
     if handle == INVALID_HANDLE_VALUE:
-      raiseOSError(osLastError())
+      raiseOSError(osLastError(), path)
     if getFileInformationByHandle(handle, addr rawInfo) == 0:
-      raiseOSError(osLastError())
+      raiseOSError(osLastError(), path)
     rawToFormalFileInfo(rawInfo, path, result)
     discard closeHandle(handle)
   else:
     var rawInfo: Stat
     if followSymlink:
       if stat(path, rawInfo) < 0'i32:
-        raiseOSError(osLastError())
+        raiseOSError(osLastError(), path)
     else:
       if lstat(path, rawInfo) < 0'i32:
-        raiseOSError(osLastError())
+        raiseOSError(osLastError(), path)
     rawToFormalFileInfo(rawInfo, path, result)
 
-proc isHidden*(path: string): bool =
-  ## Determines whether a given path is hidden or not. Returns false if the
-  ## file doesn't exist. The given path must be accessible from the current
-  ## working directory of the program.
+proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1",
+  tags: [ReadIOEffect], noWeirdTarget.} =
+  ## Returns true if both pathname arguments refer to files with identical
+  ## binary content.
   ##
-  ## On Windows, a file is hidden if the file's 'hidden' attribute is set.
-  ## On Unix-like systems, a file is hidden if it starts with a '.' (period)
-  ## and is not *just* '.' or '..' ' ."
-  when defined(Windows):
-    when useWinUnicode:
-      wrapUnary(attributes, getFileAttributesW, path)
-    else:
-      var attributes = getFileAttributesA(path)
+  ## See also:
+  ## * `sameFile proc`_
+  var
+    a, b: File
+  if not open(a, path1): return false
+  if not open(b, path2):
+    close(a)
+    return false
+  let bufSize = getFileInfo(a).blockSize
+  var bufA = alloc(bufSize)
+  var bufB = alloc(bufSize)
+  while true:
+    var readA = readBuffer(a, bufA, bufSize)
+    var readB = readBuffer(b, bufB, bufSize)
+    if readA != readB:
+      result = false
+      break
+    if readA == 0:
+      result = true
+      break
+    result = equalMem(bufA, bufB, readA)
+    if not result: break
+    if readA != bufSize: break # end of file
+  dealloc(bufA)
+  dealloc(bufB)
+  close(a)
+  close(b)
+
+proc isHidden*(path: string): bool {.noWeirdTarget.} =
+  ## Determines whether ``path`` is hidden or not, using `this
+  ## reference <https://en.wikipedia.org/wiki/Hidden_file_and_hidden_directory>`_.
+  ##
+  ## On Windows: returns true if it exists and its "hidden" attribute is set.
+  ##
+  ## On posix: returns true if ``lastPathPart(path)`` starts with ``.`` and is
+  ## not ``.`` or ``..``.
+  ##
+  ## **Note**: paths are not normalized to determine `isHidden`.
+  runnableExamples:
+    when defined(posix):
+      assert ".foo".isHidden
+      assert not ".foo/bar".isHidden
+      assert not ".".isHidden
+      assert not "..".isHidden
+      assert not "".isHidden
+      assert ".foo/".isHidden
+
+  when defined(windows):
+    wrapUnary(attributes, getFileAttributesW, path)
     if attributes != -1'i32:
       result = (attributes and FILE_ATTRIBUTE_HIDDEN) != 0'i32
   else:
-    if fileExists(path):
-      let
-        fileName = extractFilename(path)
-        nameLen = len(fileName)
-      if nameLen == 2:
-        result = (fileName[0] == '.') and (fileName[1] != '.')
-      elif nameLen > 2:
-        result = (fileName[0] == '.') and (fileName[3] != '.')
-
-{.pop.}
-
-proc setLastModificationTime*(file: string, t: times.Time) =
+    let fileName = lastPathPart(path)
+    result = len(fileName) >= 2 and fileName[0] == '.' and fileName != ".."
+
+proc getCurrentProcessId*(): int {.noWeirdTarget.} =
+  ## Return current process ID.
+  ##
+  ## See also:
+  ## * `osproc.processID(p: Process) <osproc.html#processID,Process>`_
+  when defined(windows):
+    proc GetCurrentProcessId(): DWORD {.stdcall, dynlib: "kernel32",
+                                        importc: "GetCurrentProcessId".}
+    result = GetCurrentProcessId().int
+  else:
+    result = getpid()
+
+proc setLastModificationTime*(file: string, t: times.Time) {.noWeirdTarget.} =
   ## Sets the `file`'s last modification time. `OSError` is raised in case of
   ## an error.
   when defined(posix):
@@ -1653,11 +981,52 @@ proc setLastModificationTime*(file: string, t: times.Time) =
     let micro = convert(Nanoseconds, Microseconds, t.nanosecond)
     var timevals = [Timeval(tv_sec: unixt, tv_usec: micro),
       Timeval(tv_sec: unixt, tv_usec: micro)] # [last access, last modification]
-    if utimes(file, timevals.addr) != 0: raiseOSError(osLastError())
+    if utimes(file, timevals.addr) != 0: raiseOSError(osLastError(), file)
   else:
     let h = openHandle(path = file, writeAccess = true)
-    if h == INVALID_HANDLE_VALUE: raiseOSError(osLastError())
+    if h == INVALID_HANDLE_VALUE: raiseOSError(osLastError(), file)
     var ft = t.toWinTime.toFILETIME
     let res = setFileTime(h, nil, nil, ft.addr)
     discard h.closeHandle
-    if res == 0'i32: raiseOSError(osLastError())
+    if res == 0'i32: raiseOSError(osLastError(), file)
+
+
+func isValidFilename*(filename: string, maxLen = 259.Positive): bool {.since: (1, 1).} =
+  ## Returns `true` if `filename` is valid for crossplatform use.
+  ##
+  ## This is useful if you want to copy or save files across Windows, Linux, Mac, etc.
+  ## It uses `invalidFilenameChars`, `invalidFilenames` and `maxLen` to verify the specified `filename`.
+  ##
+  ## See also:
+  ##
+  ## * https://docs.microsoft.com/en-us/dotnet/api/system.io.pathtoolongexception
+  ## * https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
+  ## * https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
+  ##
+  ## .. warning:: This only checks filenames, not whole paths
+  ##    (because basically you can mount anything as a path on Linux).
+  runnableExamples:
+    assert not isValidFilename(" foo")     # Leading white space
+    assert not isValidFilename("foo ")     # Trailing white space
+    assert not isValidFilename("foo.")     # Ends with dot
+    assert not isValidFilename("con.txt")  # "CON" is invalid (Windows)
+    assert not isValidFilename("OwO:UwU")  # ":" is invalid (Mac)
+    assert not isValidFilename("aux.bat")  # "AUX" is invalid (Windows)
+    assert not isValidFilename("")         # Empty string
+    assert not isValidFilename("foo/")     # Filename is empty
+
+  result = true
+  let f = filename.splitFile()
+  if unlikely(f.name.len + f.ext.len > maxLen or f.name.len == 0 or
+    f.name[0] == ' ' or f.name[^1] == ' ' or f.name[^1] == '.' or
+    find(f.name, invalidFilenameChars) != -1): return false
+  for invalid in invalidFilenames:
+    if cmpIgnoreCase(f.name, invalid) == 0: return false
+
+
+# deprecated declarations
+when not weirdTarget:
+  template existsFile*(args: varargs[untyped]): untyped {.deprecated: "use fileExists".} =
+    fileExists(args)
+  template existsDir*(args: varargs[untyped]): untyped {.deprecated: "use dirExists".} =
+    dirExists(args)
diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim
deleted file mode 100644
index c0d3ef512..000000000
--- a/lib/pure/ospaths.nim
+++ /dev/null
@@ -1,619 +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.
-#
-
-# Forwarded by the ``os`` module but a module in its own right for NimScript
-# support.
-
-include "system/inclrtl"
-
-import strutils
-
-type
-  ReadEnvEffect* = object of ReadIOEffect   ## effect that denotes a read
-                                            ## from an environment variable
-  WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write
-                                            ## to an environment variable
-
-  ReadDirEffect* = object of ReadIOEffect   ## effect that denotes a read
-                                            ## operation from the directory
-                                            ## structure
-  WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write
-                                            ## operation to
-                                            ## the directory structure
-
-  OSErrorCode* = distinct int32 ## Specifies an OS Error Code.
-
-const
-  doslikeFileSystem* = defined(windows) or defined(OS2) or defined(DOS)
-
-when defined(Nimdoc): # only for proper documentation:
-  const
-    CurDir* = '.'
-      ## The constant string used by the operating system to refer to the
-      ## current directory.
-      ##
-      ## For example: '.' for POSIX or ':' for the classic Macintosh.
-
-    ParDir* = ".."
-      ## The constant string used by the operating system to refer to the
-      ## parent directory.
-      ##
-      ## For example: ".." for POSIX or "::" for the classic Macintosh.
-
-    DirSep* = '/'
-      ## The character used by the operating system to separate pathname
-      ## components, for example, '/' for POSIX or ':' for the classic
-      ## Macintosh.
-
-    AltSep* = '/'
-      ## An alternative character used by the operating system to separate
-      ## pathname components, or the same as `DirSep` if only one separator
-      ## character exists. This is set to '/' on Windows systems
-      ## where `DirSep` is a backslash.
-
-    PathSep* = ':'
-      ## The character conventionally used by the operating system to separate
-      ## search patch components (as in PATH), such as ':' for POSIX
-      ## or ';' for Windows.
-
-    FileSystemCaseSensitive* = true
-      ## true if the file system is case sensitive, false otherwise. Used by
-      ## `cmpPaths` to compare filenames properly.
-
-    ExeExt* = ""
-      ## The file extension of native executables. For example:
-      ## "" for POSIX, "exe" on Windows.
-
-    ScriptExt* = ""
-      ## The file extension of a script file. For example: "" for POSIX,
-      ## "bat" on Windows.
-
-    DynlibFormat* = "lib$1.so"
-      ## The format string to turn a filename into a `DLL`:idx: file (also
-      ## called `shared object`:idx: on some operating systems).
-
-elif defined(macos):
-  const
-    CurDir* = ':'
-    ParDir* = "::"
-    DirSep* = ':'
-    AltSep* = Dirsep
-    PathSep* = ','
-    FileSystemCaseSensitive* = false
-    ExeExt* = ""
-    ScriptExt* = ""
-    DynlibFormat* = "$1.dylib"
-
-  #  MacOS paths
-  #  ===========
-  #  MacOS directory separator is a colon ":" which is the only character not
-  #  allowed in filenames.
-  #
-  #  A path containing no colon or which begins with a colon is a partial
-  #  path.
-  #  E.g. ":kalle:petter" ":kalle" "kalle"
-  #
-  #  All other paths are full (absolute) paths. E.g. "HD:kalle:" "HD:"
-  #  When generating paths, one is safe if one ensures that all partial paths
-  #  begin with a colon, and all full paths end with a colon.
-  #  In full paths the first name (e g HD above) is the name of a mounted
-  #  volume.
-  #  These names are not unique, because, for instance, two diskettes with the
-  #  same names could be inserted. This means that paths on MacOS are not
-  #  waterproof. In case of equal names the first volume found will do.
-  #  Two colons "::" are the relative path to the parent. Three is to the
-  #  grandparent etc.
-elif doslikeFileSystem:
-  const
-    CurDir* = '.'
-    ParDir* = ".."
-    DirSep* = '\\' # seperator within paths
-    AltSep* = '/'
-    PathSep* = ';' # seperator between paths
-    FileSystemCaseSensitive* = false
-    ExeExt* = "exe"
-    ScriptExt* = "bat"
-    DynlibFormat* = "$1.dll"
-elif defined(PalmOS) or defined(MorphOS):
-  const
-    DirSep* = '/'
-    AltSep* = Dirsep
-    PathSep* = ';'
-    ParDir* = ".."
-    FileSystemCaseSensitive* = false
-    ExeExt* = ""
-    ScriptExt* = ""
-    DynlibFormat* = "$1.prc"
-elif defined(RISCOS):
-  const
-    DirSep* = '.'
-    AltSep* = '.'
-    ParDir* = ".." # is this correct?
-    PathSep* = ','
-    FileSystemCaseSensitive* = true
-    ExeExt* = ""
-    ScriptExt* = ""
-    DynlibFormat* = "lib$1.so"
-else: # UNIX-like operating system
-  const
-    CurDir* = '.'
-    ParDir* = ".."
-    DirSep* = '/'
-    AltSep* = DirSep
-    PathSep* = ':'
-    FileSystemCaseSensitive* = true
-    ExeExt* = ""
-    ScriptExt* = ""
-    DynlibFormat* = when defined(macosx): "lib$1.dylib" else: "lib$1.so"
-
-const
-  ExtSep* = '.'
-    ## The character which separates the base filename from the extension;
-    ## for example, the '.' in ``os.nim``.
-
-
-proc joinPath*(head, tail: string): string {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Joins two directory names to one.
-  ##
-  ## For example on Unix:
-  ##
-  ## .. code-block:: nim
-  ##   joinPath("usr", "lib")
-  ##
-  ## results in:
-  ##
-  ## .. code-block:: nim
-  ##   "usr/lib"
-  ##
-  ## If head is the empty string, tail is returned. If tail is the empty
-  ## string, head is returned with a trailing path separator. If tail starts
-  ## with a path separator it will be removed when concatenated to head. Other
-  ## path separators not located on boundaries won't be modified. More
-  ## examples on Unix:
-  ##
-  ## .. code-block:: nim
-  ##   assert joinPath("usr", "") == "usr/"
-  ##   assert joinPath("", "lib") == "lib"
-  ##   assert joinPath("", "/lib") == "/lib"
-  ##   assert joinPath("usr/", "/lib") == "usr/lib"
-  if len(head) == 0:
-    result = tail
-  elif head[len(head)-1] in {DirSep, AltSep}:
-    if tail[0] in {DirSep, AltSep}:
-      result = head & substr(tail, 1)
-    else:
-      result = head & tail
-  else:
-    if tail.len > 0 and tail[0] in {DirSep, AltSep}:
-      result = head & tail
-    else:
-      result = head & DirSep & tail
-
-proc joinPath*(parts: varargs[string]): string {.noSideEffect,
-  rtl, extern: "nos$1OpenArray".} =
-  ## The same as `joinPath(head, tail)`, but works with any number of
-  ## directory parts. You need to pass at least one element or the proc
-  ## will assert in debug builds and crash on release builds.
-  result = parts[0]
-  for i in 1..high(parts):
-    result = joinPath(result, parts[i])
-
-proc `/` * (head, tail: string): string {.noSideEffect.} =
-  ## The same as ``joinPath(head, tail)``
-  ##
-  ## Here are some examples for Unix:
-  ##
-  ## .. code-block:: nim
-  ##   assert "usr" / "" == "usr/"
-  ##   assert "" / "lib" == "lib"
-  ##   assert "" / "/lib" == "/lib"
-  ##   assert "usr/" / "/lib" == "usr/lib"
-  return joinPath(head, tail)
-
-proc splitPath*(path: string): tuple[head, tail: string] {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Splits a directory into (head, tail), so that
-  ## ``head / tail == path`` (except for edge cases like "/usr").
-  ##
-  ## Examples:
-  ##
-  ## .. code-block:: nim
-  ##   splitPath("usr/local/bin") -> ("usr/local", "bin")
-  ##   splitPath("usr/local/bin/") -> ("usr/local/bin", "")
-  ##   splitPath("bin") -> ("", "bin")
-  ##   splitPath("/bin") -> ("", "bin")
-  ##   splitPath("") -> ("", "")
-  var sepPos = -1
-  for i in countdown(len(path)-1, 0):
-    if path[i] in {DirSep, AltSep}:
-      sepPos = i
-      break
-  if sepPos >= 0:
-    result.head = substr(path, 0, sepPos-1)
-    result.tail = substr(path, sepPos+1)
-  else:
-    result.head = ""
-    result.tail = path
-
-proc parentDirPos(path: string): int =
-  var q = 1
-  if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2
-  for i in countdown(len(path)-q, 0):
-    if path[i] in {DirSep, AltSep}: return i
-  result = -1
-
-proc parentDir*(path: string): string {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Returns the parent directory of `path`.
-  ##
-  ## This is often the same as the ``head`` result of ``splitPath``.
-  ## If there is no parent, "" is returned.
-  ## | Example: ``parentDir("/usr/local/bin") == "/usr/local"``.
-  ## | Example: ``parentDir("/usr/local/bin/") == "/usr/local"``.
-  let sepPos = parentDirPos(path)
-  if sepPos >= 0:
-    result = substr(path, 0, sepPos-1)
-  else:
-    result = ""
-
-proc tailDir*(path: string): string {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Returns the tail part of `path`..
-  ##
-  ## | Example: ``tailDir("/usr/local/bin") == "local/bin"``.
-  ## | Example: ``tailDir("usr/local/bin/") == "local/bin"``.
-  ## | Example: ``tailDir("bin") == ""``.
-  var q = 1
-  if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2
-  for i in 0..len(path)-q:
-    if path[i] in {DirSep, AltSep}:
-      return substr(path, i+1)
-  result = ""
-
-proc isRootDir*(path: string): bool {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Checks whether a given `path` is a root directory
-  result = parentDirPos(path) < 0
-
-iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string =
-  ## Walks over all parent directories of a given `path`
-  ##
-  ## If `fromRoot` is set, the traversal will start from the file system root
-  ## diretory. If `inclusive` is set, the original argument will be included
-  ## in the traversal.
-  ##
-  ## Relative paths won't be expanded by this proc. Instead, it will traverse
-  ## only the directories appearing in the relative path.
-  if not fromRoot:
-    var current = path
-    if inclusive: yield path
-    while true:
-      if current.isRootDir: break
-      current = current.parentDir
-      yield current
-  else:
-    for i in countup(0, path.len - 2): # ignore the last /
-      # deal with non-normalized paths such as /foo//bar//baz
-      if path[i] in {DirSep, AltSep} and
-          (i == 0 or path[i-1] notin {DirSep, AltSep}):
-        yield path.substr(0, i)
-
-    if inclusive: yield path
-
-proc `/../`*(head, tail: string): string {.noSideEffect.} =
-  ## The same as ``parentDir(head) / tail`` unless there is no parent
-  ## directory. Then ``head / tail`` is performed instead.
-  let sepPos = parentDirPos(head)
-  if sepPos >= 0:
-    result = substr(head, 0, sepPos-1) / tail
-  else:
-    result = head / tail
-
-proc normExt(ext: string): string =
-  if ext == "" or ext[0] == ExtSep: result = ext # no copy needed here
-  else: result = ExtSep & ext
-
-proc searchExtPos*(path: string): int =
-  ## Returns index of the '.' char in `path` if it signifies the beginning
-  ## of extension. Returns -1 otherwise.
-  # BUGFIX: do not search until 0! .DS_Store is no file extension!
-  result = -1
-  for i in countdown(len(path)-1, 1):
-    if path[i] == ExtSep:
-      result = i
-      break
-    elif path[i] in {DirSep, AltSep}:
-      break # do not skip over path
-
-proc splitFile*(path: string): tuple[dir, name, ext: string] {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Splits a filename into (dir, filename, extension).
-  ## `dir` does not end in `DirSep`.
-  ## `extension` includes the leading dot.
-  ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   var (dir, name, ext) = splitFile("usr/local/nimc.html")
-  ##   assert dir == "usr/local"
-  ##   assert name == "nimc"
-  ##   assert ext == ".html"
-  ##
-  ## 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, "", "")
-  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
-      elif path[i] in {DirSep, AltSep}:
-        sepPos = i
-        break
-    result.dir = substr(path, 0, sepPos-1)
-    result.name = substr(path, sepPos+1, dotPos-1)
-    result.ext = substr(path, dotPos)
-
-proc extractFilename*(path: string): string {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Extracts the filename of a given `path`. This is the same as
-  ## ``name & ext`` from ``splitFile(path)``.
-  if path.len == 0 or path[path.len-1] in {DirSep, AltSep}:
-    result = ""
-  else:
-    result = splitPath(path).tail
-
-
-proc changeFileExt*(filename, ext: string): string {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Changes the file extension to `ext`.
-  ##
-  ## If the `filename` has no extension, `ext` will be added.
-  ## If `ext` == "" then any extension is removed.
-  ## `Ext` should be given without the leading '.', because some
-  ## filesystems may use a different character. (Although I know
-  ## of none such beast.)
-  var extPos = searchExtPos(filename)
-  if extPos < 0: result = filename & normExt(ext)
-  else: result = substr(filename, 0, extPos-1) & normExt(ext)
-
-proc addFileExt*(filename, ext: string): string {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Adds the file extension `ext` to `filename`, unless
-  ## `filename` already has an extension.
-  ##
-  ## `Ext` should be given without the leading '.', because some
-  ## filesystems may use a different character.
-  ## (Although I know of none such beast.)
-  var extPos = searchExtPos(filename)
-  if extPos < 0: result = filename & normExt(ext)
-  else: result = filename
-
-proc cmpPaths*(pathA, pathB: string): int {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Compares two paths.
-  ##
-  ## On a case-sensitive filesystem this is done
-  ## case-sensitively otherwise case-insensitively. Returns:
-  ##
-  ## | 0 iff pathA == pathB
-  ## | < 0 iff pathA < pathB
-  ## | > 0 iff pathA > pathB
-  if FileSystemCaseSensitive:
-    result = cmp(pathA, pathB)
-  else:
-    when defined(nimscript):
-      result = cmpic(pathA, pathB)
-    elif defined(nimdoc): discard
-    else:
-      result = cmpIgnoreCase(pathA, pathB)
-
-proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
-  ## Checks whether a given `path` is absolute.
-  ##
-  ## On Windows, network paths are considered absolute too.
-  when doslikeFileSystem:
-    var len = len(path)
-    result = (len > 0 and path[0] in {'/', '\\'}) or
-              (len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':')
-  elif defined(macos):
-    result = path.len > 0 and path[0] != ':'
-  elif defined(RISCOS):
-    result = path[0] == '$'
-  elif defined(posix):
-    result = path[0] == '/'
-
-proc unixToNativePath*(path: string, drive=""): string {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Converts an UNIX-like path to a native one.
-  ##
-  ## On an UNIX system this does nothing. Else it converts
-  ## '/', '.', '..' to the appropriate things.
-  ##
-  ## On systems with a concept of "drives", `drive` is used to determine
-  ## which drive label to use during absolute path conversion.
-  ## `drive` defaults to the drive of the current working directory, and is
-  ## ignored on systems that do not have a concept of "drives".
-
-  when defined(unix):
-    result = path
-  else:
-    var start: int
-    if path[0] == '/':
-      # an absolute path
-      when doslikeFileSystem:
-        if drive != "":
-          result = drive & ":" & DirSep
-        else:
-          result = $DirSep
-      elif defined(macos):
-        result = "" # must not start with ':'
-      else:
-        result = $DirSep
-      start = 1
-    elif path[0] == '.' and path[1] == '/':
-      # current directory
-      result = $CurDir
-      start = 2
-    else:
-      result = ""
-      start = 0
-
-    var i = start
-    while i < len(path): # ../../../ --> ::::
-      if i+2 < path.len and path[i] == '.' and path[i+1] == '.' and path[i+2] == '/':
-        # parent directory
-        when defined(macos):
-          if result[high(result)] == ':':
-            add result, ':'
-          else:
-            add result, ParDir
-        else:
-          add result, ParDir & DirSep
-        inc(i, 3)
-      elif path[i] == '/':
-        add result, DirSep
-        inc(i)
-      else:
-        add result, path[i]
-        inc(i)
-
-include "includes/oserr"
-when not defined(nimscript):
-  include "includes/osenv"
-
-proc getHomeDir*(): string {.rtl, extern: "nos$1",
-  tags: [ReadEnvEffect, ReadIOEffect].} =
-  ## Returns the home directory of the current user.
-  ##
-  ## This proc is wrapped by the expandTilde proc for the convenience of
-  ## processing paths coming from user configuration files.
-  when defined(windows): return string(getEnv("USERPROFILE")) & "\\"
-  else: return string(getEnv("HOME")) & "/"
-
-proc getConfigDir*(): string {.rtl, extern: "nos$1",
-  tags: [ReadEnvEffect, ReadIOEffect].} =
-  ## Returns the config directory of the current user for applications.
-  ##
-  ## On non-Windows OSs, this proc conforms to the XDG Base Directory
-  ## spec. Thus, this proc returns the value of the XDG_CONFIG_DIR environment
-  ## variable if it is set, and returns the default configuration directory,
-  ## "~/.config/", otherwise.
-  ##
-  ## An OS-dependent trailing slash is always present at the end of the
-  ## returned string; `\\` on Windows and `/` on all other OSs.
-  when defined(windows): return string(getEnv("APPDATA")) & "\\"
-  elif getEnv("XDG_CONFIG_DIR"): return string(getEnv("XDG_CONFIG_DIR")) & "/"
-  else: return string(getEnv("HOME")) & "/.config/"
-
-proc getTempDir*(): string {.rtl, extern: "nos$1",
-  tags: [ReadEnvEffect, ReadIOEffect].} =
-  ## Returns the temporary directory of the current user for applications to
-  ## save temporary files in.
-  ##
-  ## **Please do not use this**: On Android, it currently
-  ## returns ``getHomeDir()``, and on other Unix based systems it can cause
-  ## security problems too. That said, you can override this implementation
-  ## by adding ``-d:tempDir=mytempname`` to your compiler invokation.
-  when defined(tempDir):
-    const tempDir {.strdefine.}: string = nil
-    return tempDir
-  elif defined(windows): return string(getEnv("TEMP")) & "\\"
-  elif defined(android): return getHomeDir()
-  else: return "/tmp/"
-
-proc expandTilde*(path: string): string {.
-  tags: [ReadEnvEffect, ReadIOEffect].} =
-  ## Expands a path starting with ``~/`` to a full path.
-  ##
-  ## If `path` starts with the tilde character and is followed by `/` or `\\`
-  ## this proc will return the reminder of the path appended to the result of
-  ## the getHomeDir() proc, otherwise the input path will be returned without
-  ## modification.
-  ##
-  ## The behaviour of this proc is the same on the Windows platform despite
-  ## not having this convention. Example:
-  ##
-  ## .. code-block:: nim
-  ##   let configFile = expandTilde("~" / "appname.cfg")
-  ##   echo configFile
-  ##   # --> C:\Users\amber\appname.cfg
-  if len(path) > 1 and path[0] == '~' and (path[1] == '/' or path[1] == '\\'):
-    result = getHomeDir() / path.substr(2)
-  else:
-    result = path
-
-proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
-  ## Quote s, so it can be safely passed to Windows API.
-  ## Based on Python's subprocess.list2cmdline
-  ## See http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
-  let needQuote = {' ', '\t'} in s or s.len == 0
-
-  result = ""
-  var backslashBuff = ""
-  if needQuote:
-    result.add("\"")
-
-  for c in s:
-    if c == '\\':
-      backslashBuff.add(c)
-    elif c == '\"':
-      result.add(backslashBuff)
-      result.add(backslashBuff)
-      backslashBuff.setLen(0)
-      result.add("\\\"")
-    else:
-      if backslashBuff.len != 0:
-        result.add(backslashBuff)
-        backslashBuff.setLen(0)
-      result.add(c)
-
-  if needQuote:
-    result.add("\"")
-
-proc quoteShellPosix*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
-  ## Quote ``s``, so it can be safely passed to POSIX shell.
-  ## Based on Python's pipes.quote
-  const safeUnixChars = {'%', '+', '-', '.', '/', '_', ':', '=', '@',
-                         '0'..'9', 'A'..'Z', 'a'..'z'}
-  if s.len == 0:
-    return "''"
-
-  let safe = s.allCharsInSet(safeUnixChars)
-
-  if safe:
-    return s
-  else:
-    return "'" & s.replace("'", "'\"'\"'") & "'"
-
-when defined(windows) or defined(posix):
-  proc quoteShell*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
-    ## Quote ``s``, so it can be safely passed to shell.
-    when defined(windows):
-      return quoteShellWindows(s)
-    else:
-      return quoteShellPosix(s)
-
-when isMainModule:
-  assert quoteShellWindows("aaa") == "aaa"
-  assert quoteShellWindows("aaa\"") == "aaa\\\""
-  assert quoteShellWindows("") == "\"\""
-
-  assert quoteShellPosix("aaa") == "aaa"
-  assert quoteShellPosix("aaa a") == "'aaa a'"
-  assert quoteShellPosix("") == "''"
-  assert quoteShellPosix("a'a") == "'a'\"'\"'a'"
-
-  when defined(posix):
-    assert quoteShell("") == "''"
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index e8bca4bdd..c304ecca6 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -9,40 +9,52 @@
 
 ## This module implements an advanced facility for executing OS processes
 ## and process communication.
+##
+## **See also:**
+## * `os module <os.html>`_
+## * `streams module <streams.html>`_
+## * `memfiles module <memfiles.html>`_
 
 include "system/inclrtl"
 
 import
-  strutils, os, strtabs, streams, cpuinfo
+  std/[strutils, os, strtabs, streams, cpuinfo, streamwrapper,
+  private/since]
 
-from ospaths import quoteShell, quoteShellWindows, quoteShellPosix
 export quoteShell, quoteShellWindows, quoteShellPosix
 
 when defined(windows):
-  import winlean
+  import std/winlean
 else:
-  import posix
+  import std/posix
+
+when defined(linux) and defined(useClone):
+  import std/linux
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions]
+  when defined(windows):
+    import std/widestrs
 
-when defined(linux):
-  import linux
 
 type
-  ProcessOption* = enum ## options that can be passed `startProcess`
-    poEchoCmd,           ## echo the command before execution
-    poUsePath,           ## Asks system to search for executable using PATH environment
-                         ## variable.
-                         ## On Windows, this is the default.
-    poEvalCommand,       ## Pass `command` directly to the shell, without quoting.
-                         ## Use it only if `command` comes from trusted source.
-    poStdErrToStdOut,    ## merge stdout and stderr to the stdout stream
-    poParentStreams,     ## use the parent's streams
-    poInteractive,       ## optimize the buffer handling for responsiveness for
-                         ## UI applications. Currently this only affects
-                         ## Windows: Named pipes are used so that you can peek
-                         ## at the process' output streams.
-    poDemon              ## Windows: The program creates no Window.
-                         ## Unix: Start the program as a demon. This is still
-                         ## work in progress!
+  ProcessOption* = enum ## Options that can be passed to `startProcess proc
+                        ## <#startProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_.
+    poEchoCmd,          ## Echo the command before execution.
+    poUsePath,          ## Asks system to search for executable using PATH environment
+                        ## variable.
+                        ## On Windows, this is the default.
+    poEvalCommand,      ## Pass `command` directly to the shell, without quoting.
+                        ## Use it only if `command` comes from trusted source.
+    poStdErrToStdOut,   ## Merge stdout and stderr to the stdout stream.
+    poParentStreams,    ## Use the parent's streams.
+    poInteractive,      ## Optimize the buffer handling for responsiveness for
+                        ## UI applications. Currently this only affects
+                        ## Windows: Named pipes are used so that you can peek
+                        ## at the process' output streams.
+    poDaemon            ## Windows: The program creates no Window.
+                        ## Unix: Start the program as a daemon. This is still
+                        ## work in progress!
 
   ProcessObj = object of RootObj
     when defined(windows):
@@ -52,65 +64,76 @@ type
       id: Handle
     else:
       inHandle, outHandle, errHandle: FileHandle
-      inStream, outStream, errStream: Stream
       id: Pid
+    inStream, outStream, errStream: owned(Stream)
     exitStatus: cint
     exitFlag: bool
     options: set[ProcessOption]
 
-  Process* = ref ProcessObj ## represents an operating system process
-
+  Process* = ref ProcessObj ## Represents an operating system process.
 
-const poUseShell* {.deprecated.} = poUsePath
-  ## Deprecated alias for poUsePath.
 
-proc execProcess*(command: string,
-                  args: openArray[string] = [],
-                  env: StringTableRef = nil,
-                  options: set[ProcessOption] = {poStdErrToStdOut,
-                                                  poUsePath,
-                                                  poEvalCommand}): TaintedString {.
-                                                  rtl, extern: "nosp$1",
-                                                  tags: [ExecIOEffect, ReadIOEffect,
-                                                  RootEffect].}
+proc execProcess*(command: string, workingDir: string = "",
+    args: openArray[string] = [], env: StringTableRef = nil,
+    options: set[ProcessOption] = {poStdErrToStdOut, poUsePath, poEvalCommand}):
+  string {.rtl, extern: "nosp$1", raises: [OSError, IOError],
+                  tags: [ExecIOEffect, ReadIOEffect, RootEffect].}
   ## A convenience procedure that executes ``command`` with ``startProcess``
   ## and returns its output as a string.
-  ## WARNING: this function uses poEvalCommand by default for backward compatibility.
-  ## Make sure to pass options explicitly.
   ##
-  ## .. code-block:: Nim
+  ## .. warning:: This function uses `poEvalCommand` by default for backwards
+  ##   compatibility. Make sure to pass options explicitly.
+  ##
+  ## See also:
+  ## * `startProcess proc
+  ##   <#startProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_
+  ## * `execProcesses proc <#execProcesses,openArray[string],proc(int),proc(int,Process)>`_
+  ## * `execCmd proc <#execCmd,string>`_
+  ##
+  ## Example:
+  ##   ```Nim
+  ##   let outp = execProcess("nim", args=["c", "-r", "mytestfile.nim"], options={poUsePath})
+  ##   let outp_shell = execProcess("nim c -r mytestfile.nim")
+  ##   # Note: outp may have an interleave of text from the nim compile
+  ##   # and any output from mytestfile when it runs
+  ##   ```
+
+proc execCmd*(command: string): int {.rtl, extern: "nosp$1",
+    tags: [ExecIOEffect, ReadIOEffect, RootEffect].}
+  ## Executes ``command`` and returns its error code.
   ##
-  ##  let outp = execProcess("nim c -r mytestfile.nim")
-  ##  # Note: outp may have an interleave of text from the nim compile
-  ##  # and any output from mytestfile when it runs
-
-proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [ExecIOEffect,
-  ReadIOEffect, RootEffect].}
-  ## Executes ``command`` and returns its error code. Standard input, output,
-  ## error streams are inherited from the calling process. This operation
-  ## is also often called `system`:idx:.
+  ## Standard input, output, error streams are inherited from the calling process.
+  ## This operation is also often called `system`:idx:.
   ##
-  ## .. code-block:: Nim
+  ## See also:
+  ## * `execCmdEx proc <#execCmdEx,string,set[ProcessOption],StringTableRef,string,string>`_
+  ## * `startProcess proc
+  ##   <#startProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_
+  ## * `execProcess proc
+  ##   <#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_
   ##
-  ##  let errC = execCmd("nim c -r mytestfile.nim")
-
-proc startProcess*(command: string,
-                   workingDir: string = "",
-                   args: openArray[string] = [],
-                   env: StringTableRef = nil,
-                   options: set[ProcessOption] = {poStdErrToStdOut}):
-              Process {.rtl, extern: "nosp$1", tags: [ExecIOEffect, ReadEnvEffect,
-              RootEffect].}
+  ## Example:
+  ##   ```Nim
+  ##   let errC = execCmd("nim c -r mytestfile.nim")
+  ##   ```
+
+proc startProcess*(command: string, workingDir: string = "",
+    args: openArray[string] = [], env: StringTableRef = nil,
+    options: set[ProcessOption] = {poStdErrToStdOut}):
+  owned(Process) {.rtl, extern: "nosp$1", raises: [OSError, IOError],
+                   tags: [ExecIOEffect, ReadEnvEffect, RootEffect].}
   ## Starts a process. `Command` is the executable file, `workingDir` is the
   ## process's working directory. If ``workingDir == ""`` the current directory
-  ## is used. `args` are the command line arguments that are passed to the
+  ## is used (default). `args` are the command line arguments that are passed to the
   ## process. On many operating systems, the first command line argument is the
-  ## name of the executable. `args` should not contain this argument!
+  ## name of the executable. `args` should *not* contain this argument!
   ## `env` is the environment that will be passed to the process.
-  ## If ``env == nil`` the environment is inherited of
+  ## If ``env == nil`` (default) the environment is inherited of
   ## the parent process. `options` are additional flags that may be passed
-  ## to `startProcess`. See the documentation of ``ProcessOption`` for the
-  ## meaning of these flags. You need to `close` the process when done.
+  ## to `startProcess`. See the documentation of `ProcessOption<#ProcessOption>`_
+  ## for the meaning of these flags.
+  ##
+  ## You need to `close <#close,Process>`_ the process when done.
   ##
   ## Note that you can't pass any `args` if you use the option
   ## ``poEvalCommand``, which invokes the system shell to run the specified
@@ -121,121 +144,219 @@ proc startProcess*(command: string,
   ## invocation if possible as it leads to non portable software.
   ##
   ## Return value: The newly created process object. Nil is never returned,
-  ## but ``EOS`` is raised in case of an error.
-
-proc startCmd*(command: string, options: set[ProcessOption] = {
-               poStdErrToStdOut, poUsePath}): Process {.
-               tags: [ExecIOEffect, ReadEnvEffect, RootEffect], deprecated.} =
-  ## Deprecated - use `startProcess` directly.
-  result = startProcess(command=command, options=options + {poEvalCommand})
+  ## but ``OSError`` is raised in case of an error.
+  ##
+  ## See also:
+  ## * `execProcesses proc <#execProcesses,openArray[string],proc(int),proc(int,Process)>`_
+  ## * `execProcess proc
+  ##   <#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_
+  ## * `execCmd proc <#execCmd,string>`_
 
-proc close*(p: Process) {.rtl, extern: "nosp$1", tags: [].}
+proc close*(p: Process) {.rtl, extern: "nosp$1", raises: [IOError, OSError], tags: [WriteIOEffect].}
   ## When the process has finished executing, cleanup related handles.
   ##
-  ## **Warning:** If the process has not finished executing, this will forcibly
-  ## terminate the process. Doing so may result in zombie processes and
-  ## `pty leaks <http://stackoverflow.com/questions/27021641/how-to-fix-request-failed-on-channel-0>`_.
+  ## .. warning:: If the process has not finished executing, this will forcibly
+  ##   terminate the process. Doing so may result in zombie processes and
+  ##   `pty leaks <http://stackoverflow.com/questions/27021641/how-to-fix-request-failed-on-channel-0>`_.
 
 proc suspend*(p: Process) {.rtl, extern: "nosp$1", tags: [].}
   ## Suspends the process `p`.
+  ##
+  ## See also:
+  ## * `resume proc <#resume,Process>`_
+  ## * `terminate proc <#terminate,Process>`_
+  ## * `kill proc <#kill,Process>`_
+
 
 proc resume*(p: Process) {.rtl, extern: "nosp$1", tags: [].}
   ## Resumes the process `p`.
+  ##
+  ## See also:
+  ## * `suspend proc <#suspend,Process>`_
+  ## * `terminate proc <#terminate,Process>`_
+  ## * `kill proc <#kill,Process>`_
 
 proc terminate*(p: Process) {.rtl, extern: "nosp$1", tags: [].}
-  ## Stop the process `p`. On Posix OSes the procedure sends ``SIGTERM``
-  ## to the process. On Windows the Win32 API function ``TerminateProcess()``
+  ## Stop the process `p`.
+  ##
+  ## On Posix OSes the procedure sends ``SIGTERM`` to the process.
+  ## On Windows the Win32 API function ``TerminateProcess()``
   ## is called to stop the process.
+  ##
+  ## See also:
+  ## * `suspend proc <#suspend,Process>`_
+  ## * `resume proc <#resume,Process>`_
+  ## * `kill proc <#kill,Process>`_
+  ## * `posix_utils.sendSignal(pid: Pid, signal: int) <posix_utils.html#sendSignal,Pid,int>`_
 
 proc kill*(p: Process) {.rtl, extern: "nosp$1", tags: [].}
-  ## Kill the process `p`. On Posix OSes the procedure sends ``SIGKILL`` to
-  ## the process. On Windows ``kill()`` is simply an alias for ``terminate()``.
+  ## Kill the process `p`.
+  ##
+  ## On Posix OSes the procedure sends ``SIGKILL`` to the process.
+  ## On Windows ``kill`` is simply an alias for `terminate() <#terminate,Process>`_.
+  ##
+  ## See also:
+  ## * `suspend proc <#suspend,Process>`_
+  ## * `resume proc <#resume,Process>`_
+  ## * `terminate proc <#terminate,Process>`_
+  ## * `posix_utils.sendSignal(pid: Pid, signal: int) <posix_utils.html#sendSignal,Pid,int>`_
 
-proc running*(p: Process): bool {.rtl, extern: "nosp$1", tags: [].}
-  ## Returns true iff the process `p` is still running. Returns immediately.
+proc running*(p: Process): bool {.rtl, extern: "nosp$1", raises: [OSError], tags: [].}
+  ## Returns true if the process `p` is still running. Returns immediately.
 
 proc processID*(p: Process): int {.rtl, extern: "nosp$1".} =
-  ## returns `p`'s process ID.
+  ## Returns `p`'s process ID.
+  ##
+  ## See also:
+  ## * `os.getCurrentProcessId proc <os.html#getCurrentProcessId>`_
   return p.id
 
 proc waitForExit*(p: Process, timeout: int = -1): int {.rtl,
-  extern: "nosp$1", tags: [].}
-  ## waits for the process to finish and returns `p`'s error code.
+    extern: "nosp$1", raises: [OSError, ValueError], tags: [TimeEffect].}
+  ## Waits for the process to finish and returns `p`'s error code.
   ##
-  ## **Warning**: Be careful when using waitForExit for processes created without
-  ## poParentStreams because they may fill output buffers, causing deadlock.
+  ## .. warning:: Be careful when using `waitForExit` for processes created without
+  ##   `poParentStreams` because they may fill output buffers, causing deadlock.
   ##
   ## On posix, if the process has exited because of a signal, 128 + signal
   ## number will be returned.
+  ##
+  ## .. warning:: When working with `timeout` parameters, remember that the value is 
+  ##   typically expressed in milliseconds, and ensure that the correct unit of time
+  ##   is used to avoid unexpected behavior.
 
-proc peekExitCode*(p: Process): int {.rtl, extern: "nosp$1", tags: [].}
-  ## return -1 if the process is still running. Otherwise the process' exit code
+proc peekExitCode*(p: Process): int {.rtl, extern: "nosp$1", raises: [OSError], tags: [].}
+  ## Return `-1` if the process is still running. Otherwise the process' exit code.
   ##
   ## On posix, if the process has exited because of a signal, 128 + signal
   ## number will be returned.
 
 proc inputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].}
-  ## returns ``p``'s input stream for writing to.
+  ## Returns ``p``'s input stream for writing to.
+  ##
+  ## .. warning:: The returned `Stream` should not be closed manually as it
+  ##   is closed when closing the Process ``p``.
   ##
-  ## **Warning**: The returned `Stream` should not be closed manually as it
-  ## is closed when closing the Process ``p``.
+  ## See also:
+  ## * `outputStream proc <#outputStream,Process>`_
+  ## * `errorStream proc <#errorStream,Process>`_
 
-proc outputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].}
-  ## returns ``p``'s output stream for reading from.
+proc outputStream*(p: Process): Stream {.rtl, extern: "nosp$1", raises: [IOError, OSError], tags: [].}
+  ## Returns ``p``'s output stream for reading from.
+  ##
+  ## You cannot perform peek/write/setOption operations to this stream.
+  ## Use `peekableOutputStream proc <#peekableOutputStream,Process>`_
+  ## if you need to peek stream.
+  ##
+  ## .. warning:: The returned `Stream` should not be closed manually as it
+  ##   is closed when closing the Process ``p``.
   ##
-  ## **Warning**: The returned `Stream` should not be closed manually as it
-  ## is closed when closing the Process ``p``.
+  ## See also:
+  ## * `inputStream proc <#inputStream,Process>`_
+  ## * `errorStream proc <#errorStream,Process>`_
 
 proc errorStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].}
-  ## returns ``p``'s error stream for reading from.
+  ## Returns ``p``'s error stream for reading from.
   ##
-  ## **Warning**: The returned `Stream` should not be closed manually as it
-  ## is closed when closing the Process ``p``.
+  ## You cannot perform peek/write/setOption operations to this stream.
+  ## Use `peekableErrorStream proc <#peekableErrorStream,Process>`_
+  ## if you need to peek stream.
+  ##
+  ## .. warning:: The returned `Stream` should not be closed manually as it
+  ##   is closed when closing the Process ``p``.
+  ##
+  ## See also:
+  ## * `inputStream proc <#inputStream,Process>`_
+  ## * `outputStream proc <#outputStream,Process>`_
+
+proc peekableOutputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [], since: (1, 3).}
+  ## Returns ``p``'s output stream for reading from.
+  ##
+  ## You can peek returned stream.
+  ##
+  ## .. warning:: The returned `Stream` should not be closed manually as it
+  ##   is closed when closing the Process ``p``.
+  ##
+  ## See also:
+  ## * `outputStream proc <#outputStream,Process>`_
+  ## * `peekableErrorStream proc <#peekableErrorStream,Process>`_
+
+proc peekableErrorStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [], since: (1, 3).}
+  ## Returns ``p``'s error stream for reading from.
+  ##
+  ## You can run peek operation to returned stream.
+  ##
+  ## .. warning:: The returned `Stream` should not be closed manually as it
+  ##   is closed when closing the Process ``p``.
+  ##
+  ## See also:
+  ## * `errorStream proc <#errorStream,Process>`_
+  ## * `peekableOutputStream proc <#peekableOutputStream,Process>`_
 
-proc inputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1",
+proc inputHandle*(p: Process): FileHandle {.rtl, raises: [], extern: "nosp$1",
   tags: [].} =
-  ## returns ``p``'s input file handle for writing to.
+  ## Returns ``p``'s input file handle for writing to.
   ##
-  ## **Warning**: The returned `FileHandle` should not be closed manually as
-  ## it is closed when closing the Process ``p``.
+  ## .. warning:: The returned `FileHandle` should not be closed manually as
+  ##   it is closed when closing the Process ``p``.
+  ##
+  ## See also:
+  ## * `outputHandle proc <#outputHandle,Process>`_
+  ## * `errorHandle proc <#errorHandle,Process>`_
   result = p.inHandle
 
 proc outputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1",
-  tags: [].} =
-  ## returns ``p``'s output file handle for reading from.
+    raises: [], tags: [].} =
+  ## Returns ``p``'s output file handle for reading from.
+  ##
+  ## .. warning:: The returned `FileHandle` should not be closed manually as
+  ##   it is closed when closing the Process ``p``.
   ##
-  ## **Warning**: The returned `FileHandle` should not be closed manually as
-  ## it is closed when closing the Process ``p``.
+  ## See also:
+  ## * `inputHandle proc <#inputHandle,Process>`_
+  ## * `errorHandle proc <#errorHandle,Process>`_
   result = p.outHandle
 
 proc errorHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1",
-  tags: [].} =
-  ## returns ``p``'s error file handle for reading from.
+    raises: [], tags: [].} =
+  ## Returns ``p``'s error file handle for reading from.
+  ##
+  ## .. warning:: The returned `FileHandle` should not be closed manually as
+  ##   it is closed when closing the Process ``p``.
   ##
-  ## **Warning**: The returned `FileHandle` should not be closed manually as
-  ## it is closed when closing the Process ``p``.
+  ## See also:
+  ## * `inputHandle proc <#inputHandle,Process>`_
+  ## * `outputHandle proc <#outputHandle,Process>`_
   result = p.errHandle
 
-proc countProcessors*(): int {.rtl, extern: "nosp$1".} =
-  ## returns the numer of the processors/cores the machine has.
+proc countProcessors*(): int {.rtl, extern: "nosp$1", raises: [].} =
+  ## Returns the number of the processors/cores the machine has.
   ## Returns 0 if it cannot be detected.
+  ## It is implemented just calling `cpuinfo.countProcessors`.
   result = cpuinfo.countProcessors()
 
+when not defined(nimHasEffectsOf):
+  {.pragma: effectsOf.}
+
 proc execProcesses*(cmds: openArray[string],
-                    options = {poStdErrToStdOut, poParentStreams},
-                    n = countProcessors(),
-                    beforeRunEvent: proc(idx: int) = nil,
-                    afterRunEvent: proc(idx: int, p: Process) = nil): int
-                    {.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
-  ## is returned. Runs `beforeRunEvent` before running each command.
+    options = {poStdErrToStdOut, poParentStreams}, n = countProcessors(),
+    beforeRunEvent: proc(idx: int) = nil,
+    afterRunEvent: proc(idx: int, p: Process) = nil):
+  int {.rtl, extern: "nosp$1",
+        raises: [ValueError, OSError, IOError],
+        tags: [ExecIOEffect, TimeEffect, ReadEnvEffect, RootEffect],
+        effectsOf: [beforeRunEvent, afterRunEvent].} =
+  ## Executes the commands `cmds` in parallel.
+  ## Creates `n` processes that execute in parallel.
+  ##
+  ## The highest (absolute) return value of all processes is returned.
+  ## Runs `beforeRunEvent` before running each command.
 
   assert n > 0
   if n > 1:
     var i = 0
     var q = newSeq[Process](n)
+    var idxs = newSeq[int](n) # map process index to cmds index
 
     when defined(windows):
       var w: WOHandleArray
@@ -248,6 +369,7 @@ proc execProcesses*(cmds: openArray[string],
       if beforeRunEvent != nil:
         beforeRunEvent(i)
       q[i] = startProcess(cmds[i], options = options + {poEvalCommand})
+      idxs[i] = i
       when defined(windows):
         w[i] = q[i].fProcessHandle
       inc(i)
@@ -303,20 +425,23 @@ proc execProcesses*(cmds: openArray[string],
             raiseOSError(err)
 
       if rexit >= 0:
+        when defined(windows):
+          let processHandle = q[rexit].fProcessHandle
         result = max(result, abs(q[rexit].peekExitCode()))
-        if afterRunEvent != nil: afterRunEvent(rexit, q[rexit])
+        if afterRunEvent != nil: afterRunEvent(idxs[rexit], q[rexit])
         close(q[rexit])
         if i < len(cmds):
           if beforeRunEvent != nil: beforeRunEvent(i)
           q[rexit] = startProcess(cmds[i],
                                   options = options + {poEvalCommand})
+          idxs[rexit] = i
           when defined(windows):
             w[rexit] = q[rexit].fProcessHandle
           inc(i)
         else:
           when defined(windows):
             for k in 0..wcount - 1:
-              if w[k] == q[rexit].fProcessHandle:
+              if w[k] == processHandle:
                 w[k] = w[wcount - 1]
                 w[wcount - 1] = 0
                 dec(wcount)
@@ -327,54 +452,106 @@ proc execProcesses*(cmds: openArray[string],
     for i in 0..high(cmds):
       if beforeRunEvent != nil:
         beforeRunEvent(i)
-      var p = startProcess(cmds[i], options=options + {poEvalCommand})
+      var p = startProcess(cmds[i], options = options + {poEvalCommand})
       result = max(abs(waitForExit(p)), result)
       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`.
+iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), raises: [OSError, IOError, ValueError], tags: [ReadIOEffect, TimeEffect].} =
+  ## Convenience iterator for working with `startProcess` to read data from a
+  ## background process.
+  ##
+  ## See also:
+  ## * `readLines proc <#readLines,Process>`_
+  ##
+  ## Example:
+  ##   ```Nim
+  ##   const opts = {poUsePath, poDaemon, poStdErrToStdOut}
+  ##   var ps: seq[Process]
+  ##   for prog in ["a", "b"]: # run 2 progs in parallel
+  ##     ps.add startProcess("nim", "", ["r", prog], nil, opts)
+  ##   for p in ps:
+  ##     var i = 0
+  ##     for line in p.lines:
+  ##       echo line
+  ##       i.inc
+  ##       if i > 100: break
+  ##     p.close
+  ##   ```
+  var outp = p.outputStream
+  var line = newStringOfCap(120)
+  while outp.readLine(line):
+    if keepNewLines:
+      line.add("\n")
+    yield line
+  discard waitForExit(p)
+
+proc readLines*(p: Process): (seq[string], int) {.since: (1, 3),
+    raises: [OSError, IOError, ValueError], tags: [ReadIOEffect, TimeEffect].} =
+  ## Convenience function for working with `startProcess` to read data from a
+  ## background process.
   ##
-  ## **Warning**: This function may give unexpected or completely wrong
-  ## results on Windows.
+  ## See also:
+  ## * `lines iterator <#lines.i,Process>`_
   ##
-  ## **Deprecated since version 0.17.0**: This procedure isn't cross-platform
-  ## and so should not be used in newly written code.
+  ## Example:
+  ##   ```Nim
+  ##   const opts = {poUsePath, poDaemon, poStdErrToStdOut}
+  ##   var ps: seq[Process]
+  ##   for prog in ["a", "b"]: # run 2 progs in parallel
+  ##     ps.add startProcess("nim", "", ["r", prog], nil, opts)
+  ##   for p in ps:
+  ##     let (lines, exCode) = p.readLines
+  ##     if exCode != 0:
+  ##       for line in lines: echo line
+  ##     p.close
+  ##   ```
+  for line in p.lines: result[0].add(line)
+  result[1] = p.peekExitCode
 
 when not defined(useNimRtl):
-  proc execProcess(command: string,
-                   args: openArray[string] = [],
-                   env: StringTableRef = nil,
-                   options: set[ProcessOption] = {poStdErrToStdOut,
-                                                   poUsePath,
-                                                   poEvalCommand}): TaintedString =
-    var p = startProcess(command, args=args, env=env, options=options)
+  proc execProcess(command: string, workingDir: string = "",
+      args: openArray[string] = [], env: StringTableRef = nil,
+      options: set[ProcessOption] = {poStdErrToStdOut, poUsePath,
+          poEvalCommand}):
+    string =
+
+    var p = startProcess(command, workingDir = workingDir, args = args,
+        env = env, options = options)
     var outp = outputStream(p)
-    result = TaintedString""
-    var line = newStringOfCap(120).TaintedString
+    result = ""
+    var line = newStringOfCap(120)
+    # consider `p.lines(keepNewLines=true)` to circumvent `running` busy-wait
     while true:
       # FIXME: converts CR-LF to LF.
       if outp.readLine(line):
-        result.string.add(line.string)
-        result.string.add("\n")
+        result.add(line)
+        result.add("\n")
       elif not running(p): break
     close(p)
 
 template streamAccess(p) =
   assert poParentStreams notin p.options, "API usage error: stream access not allowed when you use poParentStreams"
 
-when defined(Windows) and not defined(useNimRtl):
+when defined(windows) and not defined(useNimRtl):
   # We need to implement a handle stream for Windows:
   type
     FileHandleStream = ref object of StreamObj
       handle: Handle
       atTheEnd: bool
 
-  proc hsClose(s: Stream) = discard # nothing to do here
+  proc closeHandleCheck(handle: Handle) {.inline.} =
+    if handle.closeHandle() == 0:
+      raiseOSError(osLastError())
+
+  proc fileClose[T: Handle | FileHandle](h: var T) {.inline.} =
+    if h > 4:
+      closeHandleCheck(h)
+      h = INVALID_HANDLE_VALUE.T
+
+  proc hsClose(s: Stream) =
+    FileHandleStream(s).handle.fileClose()
+
   proc hsAtEnd(s: Stream): bool = return FileHandleStream(s).atTheEnd
 
   proc hsReadData(s: Stream, buffer: pointer, bufLen: int): int =
@@ -396,13 +573,9 @@ when defined(Windows) and not defined(useNimRtl):
                               addr bytesWritten, nil)
     if a == 0: raiseOSError(osLastError())
 
-  proc newFileHandleStream(handle: Handle): FileHandleStream =
-    new(result)
-    result.handle = handle
-    result.closeImpl = hsClose
-    result.atEndImpl = hsAtEnd
-    result.readDataImpl = hsReadData
-    result.writeDataImpl = hsWriteData
+  proc newFileHandleStream(handle: Handle): owned FileHandleStream =
+    result = FileHandleStream(handle: handle, closeImpl: hsClose, atEndImpl: hsAtEnd,
+      readDataImpl: hsReadData, writeDataImpl: hsWriteData)
 
   proc buildCommandLine(a: string, args: openArray[string]): string =
     result = quoteShell(a)
@@ -427,16 +600,14 @@ when defined(Windows) and not defined(useNimRtl):
   #var
   #  O_WRONLY {.importc: "_O_WRONLY", header: "<fcntl.h>".}: int
   #  O_RDONLY {.importc: "_O_RDONLY", header: "<fcntl.h>".}: int
-  proc myDup(h: Handle; inherit: WinBool=1): Handle =
+  proc myDup(h: Handle; inherit: WINBOOL = 1): Handle =
     let thisProc = getCurrentProcess()
-    if duplicateHandle(thisProc, h,
-                       thisProc, addr result,0,inherit,
+    if duplicateHandle(thisProc, h, thisProc, addr result, 0, inherit,
                        DUPLICATE_SAME_ACCESS) == 0:
       raiseOSError(osLastError())
 
   proc createAllPipeHandles(si: var STARTUPINFO;
-                            stdin, stdout, stderr: var Handle;
-                            hash: int) =
+                            stdin, stdout, stderr: var Handle; hash: int) =
     var sa: SECURITY_ATTRIBUTES
     sa.nLength = sizeof(SECURITY_ATTRIBUTES).cint
     sa.lpSecurityDescriptor = nil
@@ -444,44 +615,44 @@ when defined(Windows) and not defined(useNimRtl):
     let pipeOutName = newWideCString(r"\\.\pipe\stdout" & $hash)
     let pipeInName = newWideCString(r"\\.\pipe\stdin" & $hash)
     let pipeOut = createNamedPipe(pipeOutName,
-      dwOpenMode=PIPE_ACCESS_INBOUND or FILE_FLAG_WRITE_THROUGH,
-      dwPipeMode=PIPE_NOWAIT,
-      nMaxInstances=1,
-      nOutBufferSize=1024, nInBufferSize=1024,
-      nDefaultTimeOut=0,addr sa)
+      dwOpenMode = PIPE_ACCESS_INBOUND or FILE_FLAG_WRITE_THROUGH,
+      dwPipeMode = PIPE_NOWAIT,
+      nMaxInstances = 1,
+      nOutBufferSize = 1024, nInBufferSize = 1024,
+      nDefaultTimeOut = 0, addr sa)
     if pipeOut == INVALID_HANDLE_VALUE:
       raiseOSError(osLastError())
     let pipeIn = createNamedPipe(pipeInName,
-      dwOpenMode=PIPE_ACCESS_OUTBOUND or FILE_FLAG_WRITE_THROUGH,
-      dwPipeMode=PIPE_NOWAIT,
-      nMaxInstances=1,
-      nOutBufferSize=1024, nInBufferSize=1024,
-      nDefaultTimeOut=0,addr sa)
+      dwOpenMode = PIPE_ACCESS_OUTBOUND or FILE_FLAG_WRITE_THROUGH,
+      dwPipeMode = PIPE_NOWAIT,
+      nMaxInstances = 1,
+      nOutBufferSize = 1024, nInBufferSize = 1024,
+      nDefaultTimeOut = 0, addr sa)
     if pipeIn == INVALID_HANDLE_VALUE:
       raiseOSError(osLastError())
 
     si.hStdOutput = createFileW(pipeOutName,
         FILE_WRITE_DATA or SYNCHRONIZE, 0, addr sa,
         OPEN_EXISTING, # very important flag!
-        FILE_ATTRIBUTE_NORMAL,
-        0 # no template file for OPEN_EXISTING
-      )
+      FILE_ATTRIBUTE_NORMAL,
+      0 # no template file for OPEN_EXISTING
+    )
     if si.hStdOutput == INVALID_HANDLE_VALUE:
       raiseOSError(osLastError())
     si.hStdError = myDup(si.hStdOutput)
     si.hStdInput = createFileW(pipeInName,
         FILE_READ_DATA or SYNCHRONIZE, 0, addr sa,
         OPEN_EXISTING, # very important flag!
-        FILE_ATTRIBUTE_NORMAL,
-        0 # no template file for OPEN_EXISTING
-      )
-    if si.hStdOutput == INVALID_HANDLE_VALUE:
+      FILE_ATTRIBUTE_NORMAL,
+      0 # no template file for OPEN_EXISTING
+    )
+    if si.hStdInput == INVALID_HANDLE_VALUE:
       raiseOSError(osLastError())
 
     stdin = myDup(pipeIn, 0)
     stdout = myDup(pipeOut, 0)
-    discard closeHandle(pipeIn)
-    discard closeHandle(pipeOut)
+    closeHandleCheck(pipeIn)
+    closeHandleCheck(pipeOut)
     stderr = stdout
 
   proc createPipeHandles(rdHandle, wrHandle: var Handle) =
@@ -492,14 +663,10 @@ when defined(Windows) and not defined(useNimRtl):
     if createPipe(rdHandle, wrHandle, sa, 0) == 0'i32:
       raiseOSError(osLastError())
 
-  proc fileClose(h: Handle) {.inline.} =
-    if h > 4: discard closeHandle(h)
-
-  proc startProcess(command: string,
-                 workingDir: string = "",
-                 args: openArray[string] = [],
-                 env: StringTableRef = nil,
-                 options: set[ProcessOption] = {poStdErrToStdOut}): Process =
+  proc startProcess(command: string, workingDir: string = "",
+      args: openArray[string] = [], env: StringTableRef = nil,
+      options: set[ProcessOption] = {poStdErrToStdOut}):
+    owned Process =
     var
       si: STARTUPINFO
       procInfo: PROCESS_INFORMATION
@@ -520,11 +687,11 @@ when defined(Windows) and not defined(useNimRtl):
         else:
           createPipeHandles(he, si.hStdError)
           if setHandleInformation(he, DWORD(1), DWORD(0)) == 0'i32:
-            raiseOsError(osLastError())
+            raiseOSError(osLastError())
         if setHandleInformation(hi, DWORD(1), DWORD(0)) == 0'i32:
-          raiseOsError(osLastError())
+          raiseOSError(osLastError())
         if setHandleInformation(ho, DWORD(1), DWORD(0)) == 0'i32:
-          raiseOsError(osLastError())
+          raiseOSError(osLastError())
       else:
         createAllPipeHandles(si, hi, ho, he, cast[int](result))
       result.inHandle = FileHandle(hi)
@@ -551,19 +718,15 @@ when defined(Windows) and not defined(useNimRtl):
     if len(workingDir) > 0: wd = workingDir
     if env != nil: e = buildEnv(env)
     if poEchoCmd in options: echo($cmdl)
-    when useWinUnicode:
-      var tmp = newWideCString(cmdl)
-      var ee =
-        if e.str.isNil: nil
-        else: newWideCString(e.str, e.len)
-      var wwd = newWideCString(wd)
-      var flags = NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT
-      if poDemon in options: flags = flags or CREATE_NO_WINDOW
-      success = winlean.createProcessW(nil, tmp, nil, nil, 1, flags,
-        ee, wwd, si, procInfo)
-    else:
-      success = winlean.createProcessA(nil,
-        cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, si, procInfo)
+    var tmp = newWideCString(cmdl)
+    var ee =
+      if e.str.isNil: newWideCString(cstring(nil))
+      else: newWideCString(e.str, e.len)
+    var wwd = newWideCString(wd)
+    var flags = NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT
+    if poDaemon in options: flags = flags or CREATE_NO_WINDOW
+    success = winlean.createProcessW(nil, tmp, nil, nil, 1, flags,
+      ee, wwd, si, procInfo)
     let lastError = osLastError()
 
     if poParentStreams notin options:
@@ -579,7 +742,7 @@ when defined(Windows) and not defined(useNimRtl):
       const errFileNotFound = 2.int
       if lastError.int in {errInvalidParameter, errFileNotFound}:
         raiseOSError(lastError,
-            "Requested command not found: '$1'. OS error:" % command)
+              "Requested command not found: '" & command & "'. OS error:")
       else:
         raiseOSError(lastError, command)
     result.fProcessHandle = procInfo.hProcess
@@ -587,13 +750,31 @@ when defined(Windows) and not defined(useNimRtl):
     result.id = procInfo.dwProcessId
     result.exitFlag = false
 
+  proc closeThreadAndProcessHandle(p: Process) =
+    if p.fThreadHandle != 0:
+      closeHandleCheck(p.fThreadHandle)
+      p.fThreadHandle = 0
+
+    if p.fProcessHandle != 0:
+      closeHandleCheck(p.fProcessHandle)
+      p.fProcessHandle = 0
+
   proc close(p: Process) =
     if poParentStreams notin p.options:
-      discard closeHandle(p.inHandle)
-      discard closeHandle(p.outHandle)
-      discard closeHandle(p.errHandle)
-    discard closeHandle(p.fThreadHandle)
-    discard closeHandle(p.fProcessHandle)
+      if p.inStream == nil:
+        p.inHandle.fileClose()
+      else:
+        # p.inHandle can be already closed via inputStream.
+        p.inStream.close
+
+      # You may NOT close outputStream and errorStream.
+      assert p.outStream == nil or FileHandleStream(p.outStream).handle != INVALID_HANDLE_VALUE
+      assert p.errStream == nil or FileHandleStream(p.errStream).handle != INVALID_HANDLE_VALUE
+
+      if p.outHandle != p.errHandle:
+        p.errHandle.fileClose()
+      p.outHandle.fileClose()
+    p.closeThreadAndProcessHandle()
 
   proc suspend(p: Process) =
     discard suspendThread(p.fThreadHandle)
@@ -627,8 +808,7 @@ when defined(Windows) and not defined(useNimRtl):
     if status != STILL_ACTIVE:
       p.exitFlag = true
       p.exitStatus = status
-      discard closeHandle(p.fThreadHandle)
-      discard closeHandle(p.fProcessHandle)
+      p.closeThreadAndProcessHandle()
       result = status
     else:
       result = -1
@@ -644,21 +824,38 @@ when defined(Windows) and not defined(useNimRtl):
       discard getExitCodeProcess(p.fProcessHandle, status)
       p.exitFlag = true
       p.exitStatus = status
-      discard closeHandle(p.fThreadHandle)
-      discard closeHandle(p.fProcessHandle)
+      p.closeThreadAndProcessHandle()
       result = status
 
   proc inputStream(p: Process): Stream =
     streamAccess(p)
-    result = newFileHandleStream(p.inHandle)
+    if p.inStream == nil:
+      p.inStream = newFileHandleStream(p.inHandle)
+    result = p.inStream
 
   proc outputStream(p: Process): Stream =
     streamAccess(p)
-    result = newFileHandleStream(p.outHandle)
+    if p.outStream == nil:
+      p.outStream = newFileHandleStream(p.outHandle)
+    result = p.outStream
 
   proc errorStream(p: Process): Stream =
     streamAccess(p)
-    result = newFileHandleStream(p.errHandle)
+    if p.errStream == nil:
+      p.errStream = newFileHandleStream(p.errHandle)
+    result = p.errStream
+
+  proc peekableOutputStream(p: Process): Stream =
+    streamAccess(p)
+    if p.outStream == nil:
+      p.outStream = newFileHandleStream(p.outHandle).newPipeOutStream
+    result = p.outStream
+
+  proc peekableErrorStream(p: Process): Stream =
+    streamAccess(p)
+    if p.errStream == nil:
+      p.errStream = newFileHandleStream(p.errHandle).newPipeOutStream
+    result = p.errStream
 
   proc execCmd(command: string): int =
     var
@@ -670,13 +867,9 @@ when defined(Windows) and not defined(useNimRtl):
     si.hStdError = getStdHandle(STD_ERROR_HANDLE)
     si.hStdInput = getStdHandle(STD_INPUT_HANDLE)
     si.hStdOutput = getStdHandle(STD_OUTPUT_HANDLE)
-    when useWinUnicode:
-      var c = newWideCString(command)
-      var res = winlean.createProcessW(nil, c, nil, nil, 0,
-        NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo)
-    else:
-      var res = winlean.createProcessA(nil, command, nil, nil, 0,
-        NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo)
+    var c = newWideCString(command)
+    var res = winlean.createProcessW(nil, c, nil, nil, 0,
+      NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo)
     if res == 0:
       raiseOSError(osLastError())
     else:
@@ -709,7 +902,7 @@ when defined(Windows) and not defined(useNimRtl):
 
   proc hasData*(p: Process): bool =
     var x: int32
-    if peekNamedPipe(p.outHandle, lpTotalBytesAvail=addr x):
+    if peekNamedPipe(p.outHandle, lpTotalBytesAvail = addr x):
       result = x > 0
 
 elif not defined(useNimRtl):
@@ -720,13 +913,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
@@ -742,7 +928,7 @@ elif not defined(useNimRtl):
     result = cast[cstringArray](alloc0((counter + 1) * sizeof(cstring)))
     var i = 0
     for key, val in envPairs():
-      var x = key.string & "=" & val.string
+      var x = key & "=" & val
       result[i] = cast[cstring](alloc(x.len+1))
       copyMem(result[i], addr(x[0]), x.len+1)
       inc(i)
@@ -760,20 +946,19 @@ elif not defined(useNimRtl):
                              not defined(useClone) and not defined(linux)
   when useProcessAuxSpawn:
     proc startProcessAuxSpawn(data: StartProcessData): Pid {.
-      tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.}
+      raises: [OSError], tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.}
   else:
     proc startProcessAuxFork(data: StartProcessData): Pid {.
-      tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.}
-  {.push stacktrace: off, profiler: off.}
-  proc startProcessAfterFork(data: ptr StartProcessData) {.
-    tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], cdecl, gcsafe.}
-  {.pop.}
-
-  proc startProcess(command: string,
-                 workingDir: string = "",
-                 args: openArray[string] = [],
-                 env: StringTableRef = nil,
-                 options: set[ProcessOption] = {poStdErrToStdOut}): Process =
+      raises: [OSError], tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.}
+    {.push stacktrace: off, profiler: off.}
+    proc startProcessAfterFork(data: ptr StartProcessData) {.
+      raises: [OSError], tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], cdecl, gcsafe.}
+    {.pop.}
+
+  proc startProcess(command: string, workingDir: string = "",
+      args: openArray[string] = [], env: StringTableRef = nil,
+      options: set[ProcessOption] = {poStdErrToStdOut}):
+    owned Process =
     var
       pStdin, pStdout, pStderr: array[0..1, cint]
     new(result)
@@ -785,17 +970,17 @@ elif not defined(useNimRtl):
          pipe(pStderr) != 0'i32:
         raiseOSError(osLastError())
 
-    var sysCommand: string
+    var data: StartProcessData
     var sysArgsRaw: seq[string]
     if poEvalCommand in options:
       const useShPath {.strdefine.} =
         when not defined(android): "/bin/sh"
         else: "/system/bin/sh"
-      sysCommand = useShPath
-      sysArgsRaw = @[sysCommand, "-c", command]
+      data.sysCommand = useShPath
+      sysArgsRaw = @[useShPath, "-c", command]
       assert args.len == 0, "`args` has to be empty when using poEvalCommand."
     else:
-      sysCommand = command
+      data.sysCommand = command
       sysArgsRaw = @[command]
       for arg in args.items:
         sysArgsRaw.add arg
@@ -812,8 +997,6 @@ elif not defined(useNimRtl):
 
     defer: deallocCStringArray(sysEnv)
 
-    var data: StartProcessData
-    shallowCopy(data.sysCommand, sysCommand)
     data.sysArgs = sysArgs
     data.sysEnv = sysEnv
     data.pStdin = pStdin
@@ -832,7 +1015,7 @@ elif not defined(useNimRtl):
 
     # Parent process. Copy process information.
     if poEchoCmd in options:
-      when declared(echo): echo(command, " ", join(args, " "))
+      echo(command, " ", join(args, " "))
     result.id = pid
     result.exitFlag = false
 
@@ -870,13 +1053,15 @@ elif not defined(useNimRtl):
       var mask: Sigset
       chck sigemptyset(mask)
       chck posix_spawnattr_setsigmask(attr, mask)
-      if poDemon in data.options:
-        chck posix_spawnattr_setpgroup(attr, 0'i32)
+      when not defined(nuttx):
+        if poDaemon in data.options:
+          chck posix_spawnattr_setpgroup(attr, 0'i32)
 
       var flags = POSIX_SPAWN_USEVFORK or
                   POSIX_SPAWN_SETSIGMASK
-      if poDemon in data.options:
-        flags = flags or POSIX_SPAWN_SETPGROUP
+      when not defined(nuttx):
+        if poDaemon in data.options:
+          flags = flags or POSIX_SPAWN_SETPGROUP
       chck posix_spawnattr_setflags(attr, flags)
 
       if not (poParentStreams in data.options):
@@ -885,7 +1070,7 @@ elif not defined(useNimRtl):
         chck posix_spawn_file_actions_addclose(fops, data.pStdout[readIdx])
         chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], writeIdx)
         chck posix_spawn_file_actions_addclose(fops, data.pStderr[readIdx])
-        if (poStdErrToStdOut in data.options):
+        if poStdErrToStdOut in data.options:
           chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], 2)
         else:
           chck posix_spawn_file_actions_adddup2(fops, data.pStderr[writeIdx], 2)
@@ -896,13 +1081,13 @@ elif not defined(useNimRtl):
       var pid: Pid
 
       if (poUsePath in data.options):
-        res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
+        res = posix_spawnp(pid, data.sysCommand.cstring, fops, attr, data.sysArgs, data.sysEnv)
       else:
-        res = posix_spawn(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
+        res = posix_spawn(pid, data.sysCommand.cstring, fops, attr, data.sysArgs, data.sysEnv)
 
       discard posix_spawn_file_actions_destroy(fops)
       discard posix_spawnattr_destroy(attr)
-      if res != 0'i32: raiseOSError(OSErrorCode(res))
+      if res != 0'i32: raiseOSError(OSErrorCode(res), data.sysCommand)
 
       return pid
   else:
@@ -938,62 +1123,65 @@ elif not defined(useNimRtl):
       var error: cint
       let sizeRead = read(data.pErrorPipe[readIdx], addr error, sizeof(error))
       if sizeRead == sizeof(error):
-        raiseOSError(osLastError(),
-                     "Could not find command: '$1'. OS error: $2" %
-                     [$data.sysCommand, $strerror(error)])
+        raiseOSError(OSErrorCode(error),
+                      "Could not find command: '" & $data.sysCommand & "'. OS error: " & $strerror(error))
 
       return pid
 
-  {.push stacktrace: off, profiler: off.}
-  proc startProcessFail(data: ptr StartProcessData) =
-    var error: cint = errno
-    discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error))
-    exitnow(1)
-
-  when not defined(uClibc) and (not defined(linux) or defined(android)):
-    var environ {.importc.}: cstringArray
-
-  proc startProcessAfterFork(data: ptr StartProcessData) =
-    # Warning: no GC here!
-    # Or anything that touches global structures - all called nim procs
-    # must be marked with stackTrace:off. Inspect C code after making changes.
-    if not (poParentStreams in data.options):
-      discard close(data.pStdin[writeIdx])
-      if dup2(data.pStdin[readIdx], readIdx) < 0:
-        startProcessFail(data)
-      discard close(data.pStdout[readIdx])
-      if dup2(data.pStdout[writeIdx], writeIdx) < 0:
-        startProcessFail(data)
-      discard close(data.pStderr[readIdx])
-      if (poStdErrToStdOut in data.options):
-        if dup2(data.pStdout[writeIdx], 2) < 0:
+    {.push stacktrace: off, profiler: off.}
+    proc startProcessFail(data: ptr StartProcessData, error: cint = errno) =
+      discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error))
+      exitnow(1)
+
+    when not defined(uClibc) and (not defined(linux) or defined(android)) and
+         not defined(haiku):
+      var environ {.importc.}: cstringArray
+
+    proc startProcessAfterFork(data: ptr StartProcessData) =
+      # Warning: no GC here!
+      # Or anything that touches global structures - all called nim procs
+      # must be marked with stackTrace:off. Inspect C code after making changes.
+      if not (poParentStreams in data.options):
+        discard close(data.pStdin[writeIdx])
+        if dup2(data.pStdin[readIdx], readIdx) < 0:
           startProcessFail(data)
-      else:
-        if dup2(data.pStderr[writeIdx], 2) < 0:
+        discard close(data.pStdout[readIdx])
+        if dup2(data.pStdout[writeIdx], writeIdx) < 0:
           startProcessFail(data)
+        discard close(data.pStderr[readIdx])
+        if (poStdErrToStdOut in data.options):
+          if dup2(data.pStdout[writeIdx], 2) < 0:
+            startProcessFail(data)
+        else:
+          if dup2(data.pStderr[writeIdx], 2) < 0:
+            startProcessFail(data)
 
-    if data.workingDir.len > 0:
-      if chdir(data.workingDir) < 0:
-        startProcessFail(data)
+      if data.workingDir.len > 0:
+        if chdir(data.workingDir) < 0:
+          startProcessFail(data)
 
-    discard close(data.pErrorPipe[readIdx])
-    discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC)
+      discard close(data.pErrorPipe[readIdx])
+      discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC)
 
-    if (poUsePath in data.options):
-      when defined(uClibc) or defined(linux):
-        # uClibc environment (OpenWrt included) doesn't have the full execvpe
-        let exe = findExe(data.sysCommand)
-        discard execve(exe, data.sysArgs, data.sysEnv)
+      if (poUsePath in data.options):
+        when defined(uClibc) or defined(linux) or defined(haiku):
+          # uClibc environment (OpenWrt included) doesn't have the full execvpe
+          var exe: string
+          try:
+            exe = findExe(data.sysCommand)
+          except OSError as e:
+            startProcessFail(data, e.errorCode)
+          discard execve(exe.cstring, data.sysArgs, data.sysEnv)
+        else:
+          # MacOSX doesn't have execvpe, so we need workaround.
+          # On MacOSX we can arrive here only from fork, so this is safe:
+          environ = data.sysEnv
+          discard execvp(data.sysCommand.cstring, data.sysArgs)
       else:
-        # MacOSX doesn't have execvpe, so we need workaround.
-        # On MacOSX we can arrive here only from fork, so this is safe:
-        environ = data.sysEnv
-        discard execvp(data.sysCommand, data.sysArgs)
-    else:
-      discard execve(data.sysCommand, data.sysArgs, data.sysEnv)
+        discard execve(data.sysCommand.cstring, data.sysArgs, data.sysEnv)
 
-    startProcessFail(data)
-  {.pop}
+      startProcessFail(data)
+    {.pop.}
 
   proc close(p: Process) =
     if poParentStreams notin p.options:
@@ -1013,10 +1201,10 @@ elif not defined(useNimRtl):
         discard close(p.errHandle)
 
   proc suspend(p: Process) =
-    if kill(p.id, SIGSTOP) != 0'i32: raiseOsError(osLastError())
+    if kill(p.id, SIGSTOP) != 0'i32: raiseOSError(osLastError())
 
   proc resume(p: Process) =
-    if kill(p.id, SIGCONT) != 0'i32: raiseOsError(osLastError())
+    if kill(p.id, SIGCONT) != 0'i32: raiseOSError(osLastError())
 
   proc running(p: Process): bool =
     if p.exitFlag:
@@ -1038,19 +1226,19 @@ elif not defined(useNimRtl):
 
   proc terminate(p: Process) =
     if kill(p.id, SIGTERM) != 0'i32:
-      raiseOsError(osLastError())
+      raiseOSError(osLastError())
 
   proc kill(p: Process) =
     if kill(p.id, SIGKILL) != 0'i32:
-      raiseOsError(osLastError())
+      raiseOSError(osLastError())
 
   when defined(macosx) or defined(freebsd) or defined(netbsd) or
        defined(openbsd) or defined(dragonfly):
-    import kqueue, times
+    import std/kqueue
 
     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
@@ -1105,40 +1293,26 @@ elif not defined(useNimRtl):
         finally:
           discard posix.close(kqFD)
 
-      result = exitStatus(p.exitStatus)
-  else:
-    import times
-
+      result = exitStatusLikeShell(p.exitStatus)
+  elif defined(haiku):
     const
-      hasThreadSupport = compileOption("threads") and not defined(nimscript)
+      B_OBJECT_TYPE_THREAD = 3
+      B_EVENT_INVALID = 0x1000
+      B_RELATIVE_TIMEOUT = 0x8
 
-    proc waitForExit(p: Process, timeout: int = -1): int =
-      template adjustTimeout(t, s, e: Timespec) =
-        var diff: int
-        var b: Timespec
-        b.tv_sec = e.tv_sec
-        b.tv_nsec = e.tv_nsec
-        e.tv_sec = e.tv_sec - s.tv_sec
-        if e.tv_nsec >= s.tv_nsec:
-          e.tv_nsec -= s.tv_nsec
-        else:
-          if e.tv_sec == posix.Time(0):
-            raise newException(ValueError, "System time was modified")
-          else:
-            diff = s.tv_nsec - e.tv_nsec
-            e.tv_nsec = 1_000_000_000 - diff
-        t.tv_sec = t.tv_sec - e.tv_sec
-        if t.tv_nsec >= e.tv_nsec:
-          t.tv_nsec -= e.tv_nsec
-        else:
-          t.tv_sec = t.tv_sec - posix.Time(1)
-          diff = e.tv_nsec - t.tv_nsec
-          t.tv_nsec = 1_000_000_000 - diff
-        s.tv_sec = b.tv_sec
-        s.tv_nsec = b.tv_nsec
+    type
+      ObjectWaitInfo {.importc: "object_wait_info", header: "OS.h".} = object
+        obj {.importc: "object".}: int32
+        typ {.importc: "type".}: uint16
+        events: uint16
 
+    proc waitForObjects(infos: ptr ObjectWaitInfo, numInfos: cint, flags: uint32,
+                        timeout: int64): clong
+                       {.importc: "wait_for_objects_etc", header: "OS.h".}
+
+    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
@@ -1147,121 +1321,157 @@ elif not defined(useNimRtl):
         p.exitFlag = true
         p.exitStatus = status
       else:
-        var nmask, omask: Sigset
-        var sinfo: SigInfo
-        var stspec, enspec, tmspec: Timespec
+        var info = ObjectWaitInfo(
+          obj: p.id, # Haiku's PID is actually the main thread ID.
+          typ: B_OBJECT_TYPE_THREAD,
+          events: B_EVENT_INVALID # notify when the thread die.
+        )
+
+        while true:
+          var status: cint = 1
+          let count = waitForObjects(addr info, 1, B_RELATIVE_TIMEOUT, timeout)
+
+          if count < 0:
+            let err = count.cint
+            if err == ETIMEDOUT:
+              # timeout expired, so we try to kill the process
+              if posix.kill(p.id, SIGKILL) == -1:
+                raiseOSError(osLastError())
+              if waitpid(p.id, status, 0) < 0:
+                raiseOSError(osLastError())
+              p.exitFlag = true
+              p.exitStatus = status
+              break
+            elif err != EINTR:
+              raiseOSError(err.OSErrorCode)
+          elif count > 0:
+            if waitpid(p.id, status, 0) < 0:
+              raiseOSError(osLastError())
+            p.exitFlag = true
+            p.exitStatus = status
+            break
+          else:
+            raiseAssert "unreachable!"
 
-        discard sigemptyset(nmask)
-        discard sigemptyset(omask)
-        discard sigaddset(nmask, SIGCHLD)
+      result = exitStatusLikeShell(p.exitStatus)
 
-        when hasThreadSupport:
-          if pthread_sigmask(SIG_BLOCK, nmask, omask) == -1:
-            raiseOSError(osLastError())
-        else:
-          if sigprocmask(SIG_BLOCK, nmask, omask) == -1:
-            raiseOSError(osLastError())
+  else:
+    import std/times except getTime
+    import std/monotimes
 
-        if timeout >= 1000:
-          tmspec.tv_sec = posix.Time(timeout div 1_000)
-          tmspec.tv_nsec = (timeout %% 1_000) * 1_000_000
-        else:
-          tmspec.tv_sec = posix.Time(0)
-          tmspec.tv_nsec = (timeout * 1_000_000)
+    proc waitForExit(p: Process, timeout: int = -1): int =
+      if p.exitFlag:
+        return exitStatusLikeShell(p.exitStatus)
 
-        try:
-          if clock_gettime(CLOCK_REALTIME, stspec) == -1:
-            raiseOSError(osLastError())
-          while true:
-            let res = sigtimedwait(nmask, sinfo, tmspec)
-            if res == SIGCHLD:
-              if sinfo.si_pid == p.id:
-                var status: cint = 1
-                if waitpid(p.id, status, 0) < 0:
-                  raiseOSError(osLastError())
-                p.exitFlag = true
-                p.exitStatus = status
-                break
-              else:
-                # we have SIGCHLD, but not for process we are waiting,
-                # so we need to adjust timeout value and continue
-                if clock_gettime(CLOCK_REALTIME, enspec) == -1:
-                  raiseOSError(osLastError())
-                adjustTimeout(tmspec, stspec, enspec)
-            elif res < 0:
-              let err = osLastError()
-              if err.cint == EINTR:
-                # we have received another signal, so we need to
-                # adjust timeout and continue
-                if clock_gettime(CLOCK_REALTIME, enspec) == -1:
-                  raiseOSError(osLastError())
-                adjustTimeout(tmspec, stspec, enspec)
-              elif err.cint == EAGAIN:
-                # timeout expired, so we trying to kill process
-                if posix.kill(p.id, SIGKILL) == -1:
-                  raiseOSError(osLastError())
-                var status: cint = 1
-                if waitpid(p.id, status, 0) < 0:
-                  raiseOSError(osLastError())
-                p.exitFlag = true
-                p.exitStatus = status
-                break
-              else:
-                raiseOSError(err)
-        finally:
-          when hasThreadSupport:
-            if pthread_sigmask(SIG_UNBLOCK, nmask, omask) == -1:
-              raiseOSError(osLastError())
+      if timeout < 0:
+        # Backwards compatibility with previous verison to
+        # handle cases where timeout == -1, but extend
+        # to handle cases where timeout < 0
+        var status: cint
+        if waitpid(p.id, status, 0) < 0:
+          raiseOSError(osLastError())
+        p.exitFlag = true
+        p.exitStatus = status
+      else:
+        # Max 50ms delay
+        const maxWait = initDuration(milliseconds = 50)
+        let wait = initDuration(milliseconds = timeout)
+        let deadline = getMonoTime() + wait
+        # starting 50μs delay
+        var delay = initDuration(microseconds = 50)
+        
+        while true:
+          var status: cint
+          let pid = waitpid(p.id, status, WNOHANG)
+          if p.id == pid :
+            p.exitFlag = true
+            p.exitStatus = status
+            break
+          elif pid.int == -1:
+            raiseOsError(osLastError())
           else:
-            if sigprocmask(SIG_UNBLOCK, nmask, omask) == -1:
-              raiseOSError(osLastError())
-
-      result = exitStatus(p.exitStatus)
+            # Continue waiting if needed
+            if getMonoTime() >= deadline:
+              # Previous version of `waitForExit`
+              # foricibly killed the process.
+              # We keep this so we don't break programs
+              # that depend on this behavior
+              if posix.kill(p.id, SIGKILL) < 0:
+                raiseOSError(osLastError())
+            else:
+              const max = 1_000_000_000
+              let 
+                newWait = getMonoTime() + delay
+                ticks = newWait.ticks()
+                ns = ticks mod max
+                secs = ticks div max
+              var 
+                waitSpec: TimeSpec
+                unused: Timespec
+              waitSpec.tv_sec = posix.Time(secs)
+              waitSpec.tv_nsec = clong ns 
+              discard posix.clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, waitSpec, unused)
+              let remaining = deadline - getMonoTime()
+              delay = min([delay * 2, remaining, maxWait])
+
+      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) =
+  proc createStream(handle: var FileHandle,
+                    fileMode: FileMode): owned FileStream =
     var f: File
     if not open(f, handle, fileMode): raiseOSError(osLastError())
-    stream = newFileStream(f)
+    return newFileStream(f)
 
   proc inputStream(p: Process): Stream =
     streamAccess(p)
     if p.inStream == nil:
-      createStream(p.inStream, p.inHandle, fmWrite)
+      p.inStream = createStream(p.inHandle, fmWrite)
     return p.inStream
 
   proc outputStream(p: Process): Stream =
     streamAccess(p)
     if p.outStream == nil:
-      createStream(p.outStream, p.outHandle, fmRead)
+      p.outStream = createStream(p.outHandle, fmRead)
     return p.outStream
 
   proc errorStream(p: Process): Stream =
     streamAccess(p)
     if p.errStream == nil:
-      createStream(p.errStream, p.errHandle, fmRead)
+      p.errStream = createStream(p.errHandle, fmRead)
+    return p.errStream
+
+  proc peekableOutputStream(p: Process): Stream =
+    streamAccess(p)
+    if p.outStream == nil:
+      p.outStream = createStream(p.outHandle, fmRead).newPipeOutStream
+    return p.outStream
+
+  proc peekableErrorStream(p: Process): Stream =
+    streamAccess(p)
+    if p.errStream == nil:
+      p.errStream = createStream(p.errHandle, fmRead).newPipeOutStream
     return p.errStream
 
   proc csystem(cmd: cstring): cint {.nodecl, importc: "system",
                                      header: "<stdlib.h>".}
 
   proc execCmd(command: string): int =
-    when defined(linux):
+    when defined(posix):
       let tmp = csystem(command)
-      result = if tmp == -1: tmp else: exitStatus(tmp)
+      result = if tmp == -1: tmp else: exitStatusLikeShell(tmp)
     else:
       result = csystem(command)
 
@@ -1285,7 +1495,7 @@ elif not defined(useNimRtl):
   proc select(readfds: var seq[Process], timeout = 500): int =
     var tv: Timeval
     tv.tv_sec = posix.Time(0)
-    tv.tv_usec = timeout * 1000
+    tv.tv_usec = Suseconds(timeout * 1000)
 
     var rd: TFdSet
     var m = 0
@@ -1309,24 +1519,62 @@ elif not defined(useNimRtl):
 
 
 proc execCmdEx*(command: string, options: set[ProcessOption] = {
-                poStdErrToStdOut, poUsePath}): tuple[
-                output: TaintedString,
-                exitCode: int] {.tags:
+                poStdErrToStdOut, poUsePath}, env: StringTableRef = nil,
+                workingDir = "", input = ""): tuple[
+                output: string,
+                exitCode: int] {.raises: [OSError, IOError], tags:
                 [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} =
-  ## a convenience proc that runs the `command`, grabs all its output and
-  ## exit code and returns both.
+  ## A convenience proc that runs the `command`, and returns its `output` and
+  ## `exitCode`. `env` and `workingDir` params behave as for `startProcess`.
+  ## If `input.len > 0`, it is passed as stdin.
   ##
-  ## .. code-block:: Nim
+  ## Note: this could block if `input.len` is greater than your OS's maximum
+  ## pipe buffer size.
   ##
-  ##  let (outp, errC) = execCmdEx("nim c -r mytestfile.nim")
-  var p = startProcess(command, options=options + {poEvalCommand})
+  ## See also:
+  ## * `execCmd proc <#execCmd,string>`_
+  ## * `startProcess proc
+  ##   <#startProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_
+  ## * `execProcess proc
+  ##   <#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_
+  ##
+  ## Example:
+  ##   ```Nim
+  ##   var result = execCmdEx("nim r --hints:off -", options = {}, input = "echo 3*4")
+  ##   import std/[strutils, strtabs]
+  ##   stripLineEnd(result[0]) ## portable way to remove trailing newline, if any
+  ##   doAssert result == ("12", 0)
+  ##   doAssert execCmdEx("ls --nonexistent").exitCode != 0
+  ##   when defined(posix):
+  ##     assert execCmdEx("echo $FO", env = newStringTable({"FO": "B"})) == ("B\n", 0)
+  ##     assert execCmdEx("echo $PWD", workingDir = "/") == ("/\n", 0)
+  ##   ```
+
+  when (NimMajor, NimMinor, NimPatch) < (1, 3, 5):
+    doAssert input.len == 0
+    doAssert workingDir.len == 0
+    doAssert env == nil
+
+  var p = startProcess(command, options = options + {poEvalCommand},
+    workingDir = workingDir, env = env)
   var outp = outputStream(p)
-  result = (TaintedString"", -1)
-  var line = newStringOfCap(120).TaintedString
+
+  if input.len > 0:
+    # There is no way to provide input for the child process
+    # anymore. Closing it will create EOF on stdin instead of eternal
+    # blocking.
+    # Writing in chunks would require a selectors (eg kqueue/epoll) to avoid
+    # blocking on io.
+    inputStream(p).write(input)
+  close inputStream(p)
+
+  # consider `p.lines(keepNewLines=true)` to avoid exit code test
+  result = ("", -1)
+  var line = newStringOfCap(120)
   while true:
     if outp.readLine(line):
-      result[0].string.add(line.string)
-      result[0].string.add("\n")
+      result[0].add(line)
+      result[0].add("\n")
     else:
       result[1] = peekExitCode(p)
       if result[1] != -1: break
diff --git a/lib/pure/oswalkdir.nim b/lib/pure/oswalkdir.nim
deleted file mode 100644
index 23ca0566a..000000000
--- a/lib/pure/oswalkdir.nim
+++ /dev/null
@@ -1,35 +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.
-#
-
-## Compile-time only version for walkDir if you need it at compile-time
-## for JavaScript.
-
-type
-  PathComponent* = enum   ## Enumeration specifying a path component.
-    pcFile,               ## path refers to a file
-    pcLinkToFile,         ## path refers to a symbolic link to a file
-    pcDir,                ## path refers to a directory
-    pcLinkToDir           ## path refers to a symbolic link to a directory
-
-proc staticWalkDir(dir: string; relative: bool): seq[
-                  tuple[kind: PathComponent, path: string]] =
-  discard
-
-iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: string] =
-  for k, v in items(staticWalkDir(dir, relative)):
-    yield (k, v)
-
-iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string =
-  var stack = @[dir]
-  while stack.len > 0:
-    for k,p in walkDir(stack.pop()):
-      if k in filter:
-        case k
-        of pcFile, pcLinkToFile: yield p
-        of pcDir, pcLinkToDir: stack.add(p)
diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim
index 5fa2d8dc3..8a43daf54 100644
--- a/lib/pure/parsecfg.nim
+++ b/lib/pure/parsecfg.nim
@@ -7,119 +7,209 @@
 #    distribution, for details about the copyright.
 #
 
-## The ``parsecfg`` module implements a high performance configuration file
-## parser. The configuration file's syntax is similar to the Windows ``.ini``
+## The `parsecfg` module implements a high performance configuration file
+## parser. The configuration file's syntax is similar to the Windows `.ini`
 ## format, but much more powerful, as it is not a line based parser. String
 ## literals, raw string literals and triple quoted string literals are supported
 ## as in the Nim programming language.
-
-## This is an example of how a configuration file may look like:
+##
+## Example of how a configuration file may look like:
 ##
 ## .. include:: ../../doc/mytest.cfg
 ##     :literal:
-## The file ``examples/parsecfgex.nim`` demonstrates how to use the
-## configuration file parser:
-##
-## .. code-block:: nim
-##     :file: ../../examples/parsecfgex.nim
-##
-## Examples
-## --------
-##
-## This is an example of a configuration file.
-##
-## ::
 ##
+## Here is an example of how to use the configuration file parser:
+runnableExamples("-r:off"):
+  import std/[strutils, streams]
+
+  let configFile = "example.ini"
+  var f = newFileStream(configFile, fmRead)
+  assert f != nil, "cannot open " & configFile
+  var p: CfgParser
+  open(p, f, configFile)
+  while true:
+    var e = next(p)
+    case e.kind
+    of cfgEof: break
+    of cfgSectionStart:   ## a `[section]` has been parsed
+      echo "new section: " & e.section
+    of cfgKeyValuePair:
+      echo "key-value-pair: " & e.key & ": " & e.value
+    of cfgOption:
+      echo "command: " & e.key & ": " & e.value
+    of cfgError:
+      echo e.msg
+  close(p)
+
+##[
+## Configuration file example
+]##
+
+##     ```none
 ##     charset = "utf-8"
 ##     [Package]
 ##     name = "hello"
 ##     --threads:on
 ##     [Author]
-##     name = "lihf8515"
-##     qq = "10214028"
-##     email = "lihaifeng@wxm.com"
-##
-## Creating a configuration file.
-## ==============================
-## .. code-block:: nim
-##
-##     import parsecfg
-##     var dict=newConfig()
-##     dict.setSectionKey("","charset","utf-8")
-##     dict.setSectionKey("Package","name","hello")
-##     dict.setSectionKey("Package","--threads","on")
-##     dict.setSectionKey("Author","name","lihf8515")
-##     dict.setSectionKey("Author","qq","10214028")
-##     dict.setSectionKey("Author","email","lihaifeng@wxm.com")
-##     dict.writeConfig("config.ini")
-##
-## Reading a configuration file.
-## =============================
-## .. code-block:: nim
-##
-##     import parsecfg
-##     var dict = loadConfig("config.ini")
-##     var charset = dict.getSectionValue("","charset")
-##     var threads = dict.getSectionValue("Package","--threads")
-##     var pname = dict.getSectionValue("Package","name")
-##     var name = dict.getSectionValue("Author","name")
-##     var qq = dict.getSectionValue("Author","qq")
-##     var email = dict.getSectionValue("Author","email")
-##     echo pname & "\n" & name & "\n" & qq & "\n" & email
-##
-## Modifying a configuration file.
-## ===============================
-## .. code-block:: nim
-##
-##     import parsecfg
-##     var dict = loadConfig("config.ini")
-##     dict.setSectionKey("Author","name","lhf")
-##     dict.writeConfig("config.ini")
-##
-## Deleting a section key in a configuration file.
-## ===============================================
-## .. code-block:: nim
-##
-##     import parsecfg
-##     var dict = loadConfig("config.ini")
-##     dict.delSectionKey("Author","email")
-##     dict.writeConfig("config.ini")
-
-import
-  hashes, strutils, lexbase, streams, tables
+##     name = "nim-lang"
+##     website = "nim-lang.org"
+##     ```
+
+##[
+## Creating a configuration file
+]##
+
+runnableExamples:
+  var dict = newConfig()
+  dict.setSectionKey("","charset", "utf-8")
+  dict.setSectionKey("Package", "name", "hello")
+  dict.setSectionKey("Package", "--threads", "on")
+  dict.setSectionKey("Author", "name", "nim-lang")
+  dict.setSectionKey("Author", "website", "nim-lang.org")
+  assert $dict == """
+charset=utf-8
+[Package]
+name=hello
+--threads:on
+[Author]
+name=nim-lang
+website=nim-lang.org
+"""
+
+##[
+## Reading a configuration file
+]##
+
+runnableExamples("-r:off"):
+  let dict = loadConfig("config.ini")
+  let charset = dict.getSectionValue("","charset")
+  let threads = dict.getSectionValue("Package","--threads")
+  let pname = dict.getSectionValue("Package","name")
+  let name = dict.getSectionValue("Author","name")
+  let website = dict.getSectionValue("Author","website")
+  echo pname & "\n" & name & "\n" & website
+
+##[
+## Modifying a configuration file
+]##
+
+runnableExamples("-r:off"):
+  var dict = loadConfig("config.ini")
+  dict.setSectionKey("Author", "name", "nim-lang")
+  dict.writeConfig("config.ini")
+
+##[
+## Deleting a section key in a configuration file
+]##
+
+runnableExamples("-r:off"):
+  var dict = loadConfig("config.ini")
+  dict.delSectionKey("Author", "website")
+  dict.writeConfig("config.ini")
+
+##[
+## Supported INI File structure
+]##
+
+# taken from https://docs.python.org/3/library/configparser.html#supported-ini-file-structure
+runnableExamples:
+  import std/streams
+
+  var dict = loadConfig(newStringStream("""[Simple Values]
+    key=value
+    spaces in keys=allowed
+    spaces in values=allowed as well
+    spaces around the delimiter = obviously
+    you can also use : to delimit keys from values
+    [All Values Are Strings]
+    values like this: 19990429
+    or this: 3.14159265359
+    are they treated as numbers : no
+    integers floats and booleans are held as: strings
+    can use the API to get converted values directly: true
+    [No Values]
+    key_without_value
+    # empty string value is not allowed =
+    [ Seletion A   ]
+    space around section name will be ignored
+    [You can use comments]
+    # like this
+    ; or this
+    # By default only in an empty line.
+    # Inline comments can be harmful because they prevent users
+    # from using the delimiting characters as parts of values.
+    # That being said, this can be customized.
+        [Sections Can Be Indented]
+            can_values_be_as_well = True
+            does_that_mean_anything_special = False
+            purpose = formatting for readability
+            # Did I mention we can indent comments, too?
+    """)
+  )
+
+  let section1 = "Simple Values"
+  assert dict.getSectionValue(section1, "key") == "value"
+  assert dict.getSectionValue(section1, "spaces in keys") == "allowed"
+  assert dict.getSectionValue(section1, "spaces in values") == "allowed as well"
+  assert dict.getSectionValue(section1, "spaces around the delimiter") == "obviously"
+  assert dict.getSectionValue(section1, "you can also use") == "to delimit keys from values"
+
+  let section2 = "All Values Are Strings"
+  assert dict.getSectionValue(section2, "values like this") == "19990429"
+  assert dict.getSectionValue(section2, "or this") == "3.14159265359"
+  assert dict.getSectionValue(section2, "are they treated as numbers") == "no"
+  assert dict.getSectionValue(section2, "integers floats and booleans are held as") == "strings"
+  assert dict.getSectionValue(section2, "can use the API to get converted values directly") == "true"
+
+  let section3 = "Seletion A"
+  assert dict.getSectionValue(section3, 
+    "space around section name will be ignored", "not an empty value") == ""
+
+  let section4 = "Sections Can Be Indented"
+  assert dict.getSectionValue(section4, "can_values_be_as_well") == "True"
+  assert dict.getSectionValue(section4, "does_that_mean_anything_special") == "False"
+  assert dict.getSectionValue(section4, "purpose") == "formatting for readability"
+
+import std/[strutils, lexbase, streams, tables]
+import std/private/decode_helpers
+import std/private/since
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
 
 include "system/inclrtl"
 
+
 type
   CfgEventKind* = enum ## enumeration of all events that may occur when parsing
-    cfgEof,             ## end of file reached
-    cfgSectionStart,    ## a ``[section]`` has been parsed
-    cfgKeyValuePair,    ## a ``key=value`` pair has been detected
-    cfgOption,          ## a ``--key=value`` command line option
-    cfgError            ## an error occurred during parsing
+    cfgEof,            ## end of file reached
+    cfgSectionStart,   ## a `[section]` has been parsed
+    cfgKeyValuePair,   ## a `key=value` pair has been detected
+    cfgOption,         ## a `--key=value` command line option
+    cfgError           ## an error occurred during parsing
 
   CfgEvent* = object of RootObj ## describes a parsing event
     case kind*: CfgEventKind    ## the kind of the event
     of cfgEof: nil
     of cfgSectionStart:
-      section*: string           ## `section` contains the name of the
-                                 ## parsed section start (syntax: ``[section]``)
+      section*: string          ## `section` contains the name of the
+                                ## parsed section start (syntax: `[section]`)
     of cfgKeyValuePair, cfgOption:
-      key*, value*: string       ## contains the (key, value) pair if an option
-                                 ## of the form ``--key: value`` or an ordinary
-                                 ## ``key= value`` pair has been parsed.
-                                 ## ``value==""`` if it was not specified in the
-                                 ## configuration file.
-    of cfgError:                 ## the parser encountered an error: `msg`
-      msg*: string               ## contains the error message. No exceptions
-                                 ## are thrown if a parse error occurs.
+      key*, value*: string      ## contains the (key, value) pair if an option
+                                ## of the form `--key: value` or an ordinary
+                                ## `key= value` pair has been parsed.
+                                ## `value==""` if it was not specified in the
+                                ## configuration file.
+    of cfgError:                ## the parser encountered an error: `msg`
+      msg*: string              ## contains the error message. No exceptions
+                                ## are thrown if a parse error occurs.
 
   TokKind = enum
     tkInvalid, tkEof,
     tkSymbol, tkEquals, tkColon, tkBracketLe, tkBracketRi, tkDashDash
-  Token = object             # a token
-    kind: TokKind            # the type of the token
-    literal: string          # the parsed (string) literal
+  Token = object    # a token
+    kind: TokKind   # the type of the token
+    literal: string # the parsed (string) literal
 
   CfgParser* = object of BaseLexer ## the parser object.
     tok: Token
@@ -128,13 +218,13 @@ type
 # implementation
 
 const
-  SymChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.', '/', '\\', '-'}
+  SymChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', ' ', '\x80'..'\xFF', '.', '/', '\\', '-'}
 
 proc rawGetTok(c: var CfgParser, tok: var Token) {.gcsafe.}
 
 proc open*(c: var CfgParser, input: Stream, filename: string,
            lineOffset = 0) {.rtl, extern: "npc$1".} =
-  ## initializes the parser with an input stream. `Filename` is only used
+  ## Initializes the parser with an input stream. `Filename` is only used
   ## for nice error messages. `lineOffset` can be used to influence the line
   ## number information in the generated error messages.
   lexbase.open(c, input)
@@ -145,42 +235,28 @@ proc open*(c: var CfgParser, input: Stream, filename: string,
   rawGetTok(c, c.tok)
 
 proc close*(c: var CfgParser) {.rtl, extern: "npc$1".} =
-  ## closes the parser `c` and its associated input stream.
+  ## Closes the parser `c` and its associated input stream.
   lexbase.close(c)
 
 proc getColumn*(c: CfgParser): int {.rtl, extern: "npc$1".} =
-  ## get the current column the parser has arrived at.
+  ## Gets the current column the parser has arrived at.
   result = getColNumber(c, c.bufpos)
 
 proc getLine*(c: CfgParser): int {.rtl, extern: "npc$1".} =
-  ## get the current line the parser has arrived at.
+  ## Gets the current line the parser has arrived at.
   result = c.lineNumber
 
 proc getFilename*(c: CfgParser): string {.rtl, extern: "npc$1".} =
-  ## get the filename of the file that the parser processes.
+  ## Gets the filename of the file that the parser processes.
   result = c.filename
 
-proc handleHexChar(c: var CfgParser, xi: var int) =
-  case c.buf[c.bufpos]
-  of '0'..'9':
-    xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
-    inc(c.bufpos)
-  of 'a'..'f':
-    xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10)
-    inc(c.bufpos)
-  of 'A'..'F':
-    xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
-    inc(c.bufpos)
-  else:
-    discard
-
 proc handleDecChars(c: var CfgParser, xi: var int) =
   while c.buf[c.bufpos] in {'0'..'9'}:
     xi = (xi * 10) + (ord(c.buf[c.bufpos]) - ord('0'))
     inc(c.bufpos)
 
 proc getEscapedChar(c: var CfgParser, tok: var Token) =
-  inc(c.bufpos)               # skip '\'
+  inc(c.bufpos) # skip '\'
   case c.buf[c.bufpos]
   of 'n', 'N':
     add(tok.literal, "\n")
@@ -218,8 +294,10 @@ proc getEscapedChar(c: var CfgParser, tok: var Token) =
   of 'x', 'X':
     inc(c.bufpos)
     var xi = 0
-    handleHexChar(c, xi)
-    handleHexChar(c, xi)
+    if handleHexChar(c.buf[c.bufpos], xi):
+      inc(c.bufpos)
+      if handleHexChar(c.buf[c.bufpos], xi):
+        inc(c.bufpos)
     add(tok.literal, chr(xi))
   of '0'..'9':
     var xi = 0
@@ -235,38 +313,35 @@ proc handleCRLF(c: var CfgParser, pos: int): int =
   else: result = pos
 
 proc getString(c: var CfgParser, tok: var Token, rawMode: bool) =
-  var pos = c.bufpos + 1          # skip "
-  var buf = c.buf                 # put `buf` in a register
+  var pos = c.bufpos + 1 # skip "
   tok.kind = tkSymbol
-  if (buf[pos] == '"') and (buf[pos + 1] == '"'):
+  if (c.buf[pos] == '"') and (c.buf[pos + 1] == '"'):
     # long string literal:
-    inc(pos, 2)               # skip ""
+    inc(pos, 2) # skip ""
                               # skip leading newline:
     pos = handleCRLF(c, pos)
-    buf = c.buf
     while true:
-      case buf[pos]
+      case c.buf[pos]
       of '"':
-        if (buf[pos + 1] == '"') and (buf[pos + 2] == '"'): break
+        if (c.buf[pos + 1] == '"') and (c.buf[pos + 2] == '"'): break
         add(tok.literal, '"')
         inc(pos)
       of '\c', '\L':
         pos = handleCRLF(c, pos)
-        buf = c.buf
         add(tok.literal, "\n")
       of lexbase.EndOfFile:
         tok.kind = tkInvalid
         break
       else:
-        add(tok.literal, buf[pos])
+        add(tok.literal, c.buf[pos])
         inc(pos)
-    c.bufpos = pos + 3       # skip the three """
+    c.bufpos = pos + 3 # skip the three """
   else:
     # ordinary string literal
     while true:
-      var ch = buf[pos]
+      var ch = c.buf[pos]
       if ch == '"':
-        inc(pos)              # skip '"'
+        inc(pos) # skip '"'
         break
       if ch in {'\c', '\L', lexbase.EndOfFile}:
         tok.kind = tkInvalid
@@ -282,28 +357,29 @@ proc getString(c: var CfgParser, tok: var Token, rawMode: bool) =
 
 proc getSymbol(c: var CfgParser, tok: var Token) =
   var pos = c.bufpos
-  var buf = c.buf
   while true:
-    add(tok.literal, buf[pos])
+    add(tok.literal, c.buf[pos])
     inc(pos)
-    if not (buf[pos] in SymChars): break
+    if not (c.buf[pos] in SymChars): break
+
+  while tok.literal.len > 0 and tok.literal[^1] == ' ':
+    tok.literal.setLen(tok.literal.len - 1)
+
   c.bufpos = pos
   tok.kind = tkSymbol
 
 proc skip(c: var CfgParser) =
   var pos = c.bufpos
-  var buf = c.buf
   while true:
-    case buf[pos]
+    case c.buf[pos]
     of ' ', '\t':
       inc(pos)
     of '#', ';':
-      while not (buf[pos] in {'\c', '\L', lexbase.EndOfFile}): inc(pos)
+      while not (c.buf[pos] in {'\c', '\L', lexbase.EndOfFile}): inc(pos)
     of '\c', '\L':
       pos = handleCRLF(c, pos)
-      buf = c.buf
     else:
-      break                   # EndOfFile also leaves the loop
+      break # EndOfFile also leaves the loop
   c.bufpos = pos
 
 proc rawGetTok(c: var CfgParser, tok: var Token) =
@@ -337,7 +413,7 @@ proc rawGetTok(c: var CfgParser, tok: var Token) =
   of '[':
     tok.kind = tkBracketLe
     inc(c.bufpos)
-    tok.literal = "]"
+    tok.literal = "["
   of ']':
     tok.kind = tkBracketRi
     inc(c.bufpos)
@@ -350,19 +426,19 @@ proc rawGetTok(c: var CfgParser, tok: var Token) =
   else: getSymbol(c, tok)
 
 proc errorStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} =
-  ## returns a properly formatted error message containing current line and
+  ## Returns a properly formatted error message containing current line and
   ## column information.
   result = `%`("$1($2, $3) Error: $4",
-               [c.filename, $getLine(c), $getColumn(c), msg])
+                [c.filename, $getLine(c), $getColumn(c), msg])
 
 proc warningStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} =
-  ## returns a properly formatted warning message containing current line and
+  ## Returns a properly formatted warning message containing current line and
   ## column information.
   result = `%`("$1($2, $3) Warning: $4",
-               [c.filename, $getLine(c), $getColumn(c), msg])
+                [c.filename, $getLine(c), $getColumn(c), msg])
 
 proc ignoreMsg*(c: CfgParser, e: CfgEvent): string {.rtl, extern: "npc$1".} =
-  ## returns a properly formatted warning message containing that
+  ## Returns a properly formatted warning message containing that
   ## an entry is ignored.
   case e.kind
   of cfgSectionStart: result = c.warningStr("section ignored: " & e.section)
@@ -374,29 +450,29 @@ proc ignoreMsg*(c: CfgParser, e: CfgEvent): string {.rtl, extern: "npc$1".} =
 
 proc getKeyValPair(c: var CfgParser, kind: CfgEventKind): CfgEvent =
   if c.tok.kind == tkSymbol:
-    result.kind = kind
-    result.key = c.tok.literal
-    result.value = ""
+    case kind
+    of cfgOption, cfgKeyValuePair:
+      result = CfgEvent(kind: kind, key: c.tok.literal.move, value: "")
+    else: discard
     rawGetTok(c, c.tok)
     if c.tok.kind in {tkEquals, tkColon}:
       rawGetTok(c, c.tok)
       if c.tok.kind == tkSymbol:
         result.value = c.tok.literal
       else:
-        reset result
-        result.kind = cfgError
-        result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
+        result = CfgEvent(kind: cfgError,
+          msg: errorStr(c, "symbol expected, but found: " & c.tok.literal))
       rawGetTok(c, c.tok)
   else:
-    result.kind = cfgError
-    result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
+    result = CfgEvent(kind: cfgError,
+      msg: errorStr(c, "symbol expected, but found: " & c.tok.literal))
     rawGetTok(c, c.tok)
 
 proc next*(c: var CfgParser): CfgEvent {.rtl, extern: "npc$1".} =
-  ## retrieves the first/next event. This controls the parser.
+  ## Retrieves the first/next event. This controls the parser.
   case c.tok.kind
   of tkEof:
-    result.kind = cfgEof
+    result = CfgEvent(kind: cfgEof)
   of tkDashDash:
     rawGetTok(c, c.tok)
     result = getKeyValPair(c, cfgOption)
@@ -405,21 +481,19 @@ proc next*(c: var CfgParser): CfgEvent {.rtl, extern: "npc$1".} =
   of tkBracketLe:
     rawGetTok(c, c.tok)
     if c.tok.kind == tkSymbol:
-      result.kind = cfgSectionStart
-      result.section = c.tok.literal
+      result = CfgEvent(kind: cfgSectionStart, section: c.tok.literal.move)
     else:
-      result.kind = cfgError
-      result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
+      result = CfgEvent(kind: cfgError,
+        msg: errorStr(c, "symbol expected, but found: " & c.tok.literal))
     rawGetTok(c, c.tok)
     if c.tok.kind == tkBracketRi:
       rawGetTok(c, c.tok)
     else:
-      reset(result)
-      result.kind = cfgError
-      result.msg = errorStr(c, "']' expected, but found: " & c.tok.literal)
+      result = CfgEvent(kind: cfgError,
+        msg: errorStr(c, "']' expected, but found: " & c.tok.literal))
   of tkInvalid, tkEquals, tkColon, tkBracketRi:
-    result.kind = cfgError
-    result.msg = errorStr(c, "invalid token: " & c.tok.literal)
+    result = CfgEvent(kind: cfgError,
+      msg: errorStr(c, "invalid token: " & c.tok.literal))
     rawGetTok(c, c.tok)
 
 # ---------------- Configuration file related operations ----------------
@@ -427,12 +501,12 @@ type
   Config* = OrderedTableRef[string, OrderedTableRef[string, string]]
 
 proc newConfig*(): Config =
-  ## Create a new configuration table.
+  ## Creates a new configuration table.
   ## Useful when wanting to create a configuration file.
   result = newOrderedTable[string, OrderedTableRef[string, string]]()
 
 proc loadConfig*(stream: Stream, filename: string = "[stream]"): Config =
-  ## Load the specified configuration from stream into a new Config instance.
+  ## Loads the specified configuration from stream into a new Config instance.
   ## `filename` parameter is only used for nicer error messages.
   var dict = newOrderedTable[string, OrderedTableRef[string, string]]()
   var curSection = "" ## Current section,
@@ -465,7 +539,7 @@ proc loadConfig*(stream: Stream, filename: string = "[stream]"): Config =
   result = dict
 
 proc loadConfig*(filename: string): Config =
-  ## Load the specified configuration file into a new Config instance.
+  ## Loads the specified configuration file into a new Config instance.
   let file = open(filename, fmRead)
   let fileStream = newFileStream(file)
   defer: fileStream.close()
@@ -477,12 +551,12 @@ proc replace(s: string): string =
   while i < s.len():
     if s[i] == '\\':
       d.add(r"\\")
-    elif s[i] == '\c' and s[i+1] == '\L':
-      d.add(r"\n")
+    elif s[i] == '\c' and s[i+1] == '\l':
+      d.add(r"\c\l")
       inc(i)
     elif s[i] == '\c':
       d.add(r"\n")
-    elif s[i] == '\L':
+    elif s[i] == '\l':
       d.add(r"\n")
     else:
       d.add(s[i])
@@ -490,9 +564,9 @@ proc replace(s: string): string =
   result = d
 
 proc writeConfig*(dict: Config, stream: Stream) =
-  ## Writes the contents of the table to the specified stream
+  ## Writes the contents of the table to the specified stream.
   ##
-  ## **Note:** Comment statement will be ignored.
+  ## .. note:: Comment statement will be ignored.
   for section, sectionData in dict.pairs():
     if section != "": ## Not general section
       if not allCharsInSet(section, SymChars): ## Non system character
@@ -531,7 +605,8 @@ proc writeConfig*(dict: Config, stream: Stream) =
 
 proc `$`*(dict: Config): string =
   ## Writes the contents of the table to string.
-  ## Note: Comment statement will be ignored.
+  ## 
+  ## .. note:: Comment statement will be ignored.
   let stream = newStringStream()
   defer: stream.close()
   dict.writeConfig(stream)
@@ -539,21 +614,23 @@ proc `$`*(dict: Config): string =
 
 proc writeConfig*(dict: Config, filename: string) =
   ## Writes the contents of the table to the specified configuration file.
-  ## Note: Comment statement will be ignored.
+  ## 
+  ## .. note:: Comment statement will be ignored.
   let file = open(filename, fmWrite)
   defer: file.close()
   let fileStream = newFileStream(file)
   dict.writeConfig(fileStream)
 
-proc getSectionValue*(dict: Config, section, key: string): string =
-  ## Gets the Key value of the specified Section.
-  if dict.haskey(section):
+proc getSectionValue*(dict: Config, section, key: string, defaultVal = ""): string =
+  ## Gets the key value of the specified Section.
+  ## Returns the specified default value if the specified key does not exist.
+  if dict.hasKey(section):
     if dict[section].hasKey(key):
       result = dict[section][key]
     else:
-      result = ""
+      result = defaultVal
   else:
-    result = ""
+    result = defaultVal
 
 proc setSectionKey*(dict: var Config, section, key, value: string) =
   ## Sets the Key value of the specified Section.
@@ -568,10 +645,15 @@ proc delSection*(dict: var Config, section: string) =
   dict.del(section)
 
 proc delSectionKey*(dict: var Config, section, key: string) =
-  ## Delete the key of the specified section.
-  if dict.haskey(section):
+  ## Deletes the key of the specified section.
+  if dict.hasKey(section):
     if dict[section].hasKey(key):
-      if dict[section].len() == 1:
+      if dict[section].len == 1:
         dict.del(section)
       else:
         dict[section].del(key)
+
+iterator sections*(dict: Config): lent string {.since: (1, 5).} =
+  ## Iterates through the sections in the `dict`.
+  for section in dict.keys:
+    yield section
diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim
index 796114d37..c7bf0c9c1 100644
--- a/lib/pure/parsecsv.nim
+++ b/lib/pure/parsecsv.nim
@@ -10,13 +10,18 @@
 ## This module implements a simple high performance `CSV`:idx:
 ## (`comma separated value`:idx:) parser.
 ##
-## Example: How to use the parser
-## ==============================
+## Basic usage
+## ===========
+##
+##   ```nim
+##   import std/parsecsv
+##   from std/os import paramStr
+##   from std/streams import newFileStream
 ##
-## .. code-block:: nim
-##   import os, parsecsv, streams
 ##   var s = newFileStream(paramStr(1), fmRead)
-##   if s == nil: quit("cannot open the file" & paramStr(1))
+##   if s == nil:
+##     quit("cannot open the file" & paramStr(1))
+##
 ##   var x: CsvParser
 ##   open(x, s, paramStr(1))
 ##   while readRow(x):
@@ -24,13 +29,14 @@
 ##     for val in items(x.row):
 ##       echo "##", val, "##"
 ##   close(x)
+##   ```
 ##
 ## For CSV files with a header row, the header can be read and then used as a
-## reference for item access with `rowEntry <#rowEntry.CsvParser.string>`_:
+## reference for item access with `rowEntry <#rowEntry,CsvParser,string>`_:
+##
+##   ```nim
+##   import std/parsecsv
 ##
-## .. code-block:: nim
-##   import parsecsv
-##   import os
 ##   # Prepare a file
 ##   let content = """One,Two,Three,Four
 ##   1,2,3,4
@@ -47,24 +53,43 @@
 ##     for col in items(p.headers):
 ##       echo "##", col, ":", p.rowEntry(col), "##"
 ##   p.close()
+##   ```
+##
+## See also
+## ========
+##
+## * `streams module <streams.html>`_ for using
+##   `open proc <#open,CsvParser,Stream,string,char,char,char>`_
+##   and other stream processing (like `close proc <streams.html#close,Stream>`_)
+## * `parseopt module <parseopt.html>`_ for a command line parser
+## * `parsecfg module <parsecfg.html>`_ for a configuration file parser
+## * `parsexml module <parsexml.html>`_ for a XML / HTML parser
+## * `parsesql module <parsesql.html>`_ for a SQL parser
+## * `other parsers <lib.html#pure-libraries-parsers>`_ for other parsers
+
+import std/[lexbase, streams]
 
-import
-  lexbase, streams
+when defined(nimPreviewSlimSystem):
+  import std/syncio
 
 type
-  CsvRow* = seq[string] ## a row in a CSV file
-  CsvParser* = object of BaseLexer ## the parser object.
-    row*: CsvRow                    ## the current row
+  CsvRow* = seq[string] ## A row in a CSV file.
+  CsvParser* = object of BaseLexer ## The parser object.
+                                   ##
+                                   ## It consists of two public fields:
+                                   ## * `row` is the current row
+                                   ## * `headers` are the columns that are defined in the csv file
+                                   ##   (read using `readHeaderRow <#readHeaderRow,CsvParser>`_).
+                                   ##   Used with `rowEntry <#rowEntry,CsvParser,string>`_).
+    row*: CsvRow
     filename: string
     sep, quote, esc: char
     skipWhite: bool
     currRow: int
-    headers*: seq[string] ## The columns that are defined in the csv file
-                          ## (read using `readHeaderRow <#readHeaderRow.CsvParser>`_).
-                          ## Used with `rowEntry <#rowEntry.CsvParser.string>`_).
+    headers*: seq[string]
 
-  CsvError* = object of IOError ## exception that is raised if
-                                ## a parsing error occurs
+  CsvError* = object of IOError ## An exception that is raised if
+                                ## a parsing error occurs.
 
 proc raiseEInvalidCsv(filename: string, line, col: int,
                       msg: string) {.noreturn.} =
@@ -76,151 +101,255 @@ proc raiseEInvalidCsv(filename: string, line, col: int,
     e.msg = filename & "(" & $line & ", " & $col & ") Error: " & msg
   raise e
 
-proc error(my: CsvParser, pos: int, msg: string) =
-  raiseEInvalidCsv(my.filename, my.lineNumber, getColNumber(my, pos), msg)
+proc error(self: CsvParser, pos: int, msg: string) =
+  raiseEInvalidCsv(self.filename, self.lineNumber, getColNumber(self, pos), msg)
 
-proc open*(my: var CsvParser, input: Stream, filename: string,
+proc open*(self: var CsvParser, input: Stream, filename: string,
            separator = ',', quote = '"', escape = '\0',
            skipInitialSpace = false) =
-  ## initializes the parser with an input stream. `Filename` is only used
+  ## Initializes the parser with an input stream. `Filename` is only used
   ## for nice error messages. The parser's behaviour can be controlled by
   ## the diverse optional parameters:
   ## - `separator`: character used to separate fields
   ## - `quote`: Used to quote fields containing special characters like
-  ##   `separator`, `quote` or new-line characters. '\0' disables the parsing
+  ##   `separator`, `quote` or new-line characters. '\\0' disables the parsing
   ##   of quotes.
   ## - `escape`: removes any special meaning from the following character;
-  ##   '\0' disables escaping; if escaping is disabled and `quote` is not '\0',
+  ##   '\\0' disables escaping; if escaping is disabled and `quote` is not '\\0',
   ##   two `quote` characters are parsed one literal `quote` character.
   ## - `skipInitialSpace`: If true, whitespace immediately following the
   ##   `separator` is ignored.
-  lexbase.open(my, input)
-  my.filename = filename
-  my.sep = separator
-  my.quote = quote
-  my.esc = escape
-  my.skipWhite = skipInitialSpace
-  my.row = @[]
-  my.currRow = 0
+  ##
+  ## See also:
+  ## * `open proc <#open,CsvParser,string,char,char,char>`_ which creates the
+  ##   file stream for you
+  runnableExamples:
+    import std/streams
+    var strm = newStringStream("One,Two,Three\n1,2,3\n10,20,30")
+    var parser: CsvParser
+    parser.open(strm, "tmp.csv")
+    parser.close()
+    strm.close()
 
-proc open*(my: var CsvParser, filename: string,
+  lexbase.open(self, input)
+  self.filename = filename
+  self.sep = separator
+  self.quote = quote
+  self.esc = escape
+  self.skipWhite = skipInitialSpace
+
+proc open*(self: var CsvParser, filename: string,
            separator = ',', quote = '"', escape = '\0',
            skipInitialSpace = false) =
-  ## same as the other `open` but creates the file stream for you.
+  ## Similar to the `other open proc<#open,CsvParser,Stream,string,char,char,char>`_,
+  ## but creates the file stream for you.
+  runnableExamples:
+    from std/os import removeFile
+    writeFile("tmp.csv", "One,Two,Three\n1,2,3\n10,20,300")
+    var parser: CsvParser
+    parser.open("tmp.csv")
+    parser.close()
+    removeFile("tmp.csv")
+
   var s = newFileStream(filename, fmRead)
-  if s == nil: my.error(0, "cannot open: " & filename)
-  open(my, s, filename, separator,
+  if s == nil: self.error(0, "cannot open: " & filename)
+  open(self, s, filename, separator,
        quote, escape, skipInitialSpace)
 
-proc parseField(my: var CsvParser, a: var string) =
-  var pos = my.bufpos
-  var buf = my.buf
-  if my.skipWhite:
-    while buf[pos] in {' ', '\t'}: inc(pos)
+proc parseField(self: var CsvParser, a: var string) =
+  var pos = self.bufpos
+  if self.skipWhite:
+    while self.buf[pos] in {' ', '\t'}: inc(pos)
   setLen(a, 0) # reuse memory
-  if buf[pos] == my.quote and my.quote != '\0':
+  if self.buf[pos] == self.quote and self.quote != '\0':
     inc(pos)
     while true:
-      let c = buf[pos]
+      let c = self.buf[pos]
       if c == '\0':
-        my.bufpos = pos # can continue after exception?
-        error(my, pos, my.quote & " expected")
+        self.bufpos = pos # can continue after exception?
+        error(self, pos, self.quote & " expected")
         break
-      elif c == my.quote:
-        if my.esc == '\0' and buf[pos+1] == my.quote:
-          add(a, my.quote)
+      elif c == self.quote:
+        if self.esc == '\0' and self.buf[pos + 1] == self.quote:
+          add(a, self.quote)
           inc(pos, 2)
         else:
           inc(pos)
           break
-      elif c == my.esc:
-        add(a, buf[pos+1])
+      elif c == self.esc:
+        add(a, self.buf[pos + 1])
         inc(pos, 2)
       else:
         case c
         of '\c':
-          pos = handleCR(my, pos)
-          buf = my.buf
+          pos = handleCR(self, pos)
           add(a, "\n")
         of '\l':
-          pos = handleLF(my, pos)
-          buf = my.buf
+          pos = handleLF(self, pos)
           add(a, "\n")
         else:
           add(a, c)
           inc(pos)
   else:
     while true:
-      let c = buf[pos]
-      if c == my.sep: break
+      let c = self.buf[pos]
+      if c == self.sep: break
       if c in {'\c', '\l', '\0'}: break
       add(a, c)
       inc(pos)
-  my.bufpos = pos
+  self.bufpos = pos
+
+proc processedRows*(self: var CsvParser): int {.inline.} =
+  ## Returns number of the processed rows.
+  ##
+  ## But even if `readRow <#readRow,CsvParser,int>`_ arrived at EOF then
+  ## processed rows counter is incremented.
+  runnableExamples:
+    import std/streams
 
-proc processedRows*(my: var CsvParser): int =
-  ## returns number of the processed rows
-  return my.currRow
+    var strm = newStringStream("One,Two,Three\n1,2,3")
+    var parser: CsvParser
+    parser.open(strm, "tmp.csv")
+    doAssert parser.readRow()
+    doAssert parser.processedRows() == 1
+    doAssert parser.readRow()
+    doAssert parser.processedRows() == 2
+    ## Even if `readRow` arrived at EOF then `processedRows` is incremented.
+    doAssert parser.readRow() == false
+    doAssert parser.processedRows() == 3
+    doAssert parser.readRow() == false
+    doAssert parser.processedRows() == 4
+    parser.close()
+    strm.close()
 
-proc readRow*(my: var CsvParser, columns = 0): bool =
-  ## reads the next row; if `columns` > 0, it expects the row to have
+  self.currRow
+
+proc readRow*(self: var CsvParser, columns = 0): bool =
+  ## Reads the next row; if `columns` > 0, it expects the row to have
   ## exactly this many columns. Returns false if the end of the file
   ## has been encountered else true.
   ##
   ## Blank lines are skipped.
+  runnableExamples:
+    import std/streams
+    var strm = newStringStream("One,Two,Three\n1,2,3\n\n10,20,30")
+    var parser: CsvParser
+    parser.open(strm, "tmp.csv")
+    doAssert parser.readRow()
+    doAssert parser.row == @["One", "Two", "Three"]
+    doAssert parser.readRow()
+    doAssert parser.row == @["1", "2", "3"]
+    ## Blank lines are skipped.
+    doAssert parser.readRow()
+    doAssert parser.row == @["10", "20", "30"]
+
+    var emptySeq: seq[string]
+    doAssert parser.readRow() == false
+    doAssert parser.row == emptySeq
+    doAssert parser.readRow() == false
+    doAssert parser.row == emptySeq
+
+    parser.close()
+    strm.close()
+
   var col = 0 # current column
-  let oldpos = my.bufpos
-  while my.buf[my.bufpos] != '\0':
-    let oldlen = my.row.len
-    if oldlen < col+1:
-      setLen(my.row, col+1)
-      my.row[col] = ""
-    parseField(my, my.row[col])
+  let oldpos = self.bufpos
+  # skip initial empty lines #8365
+  while true:
+    case self.buf[self.bufpos]
+    of '\c': self.bufpos = handleCR(self, self.bufpos)
+    of '\l': self.bufpos = handleLF(self, self.bufpos)
+    else: break
+  while self.buf[self.bufpos] != '\0':
+    let oldlen = self.row.len
+    if oldlen < col + 1:
+      setLen(self.row, col + 1)
+      self.row[col] = ""
+    parseField(self, self.row[col])
     inc(col)
-    if my.buf[my.bufpos] == my.sep:
-      inc(my.bufpos)
+    if self.buf[self.bufpos] == self.sep:
+      inc(self.bufpos)
     else:
-      case my.buf[my.bufpos]
+      case self.buf[self.bufpos]
       of '\c', '\l':
         # skip empty lines:
         while true:
-          case my.buf[my.bufpos]
-          of '\c': my.bufpos = handleCR(my, my.bufpos)
-          of '\l': my.bufpos = handleLF(my, my.bufpos)
+          case self.buf[self.bufpos]
+          of '\c': self.bufpos = handleCR(self, self.bufpos)
+          of '\l': self.bufpos = handleLF(self, self.bufpos)
           else: break
       of '\0': discard
-      else: error(my, my.bufpos, my.sep & " expected")
+      else: error(self, self.bufpos, self.sep & " expected")
       break
 
-  setLen(my.row, col)
+  setLen(self.row, col)
   result = col > 0
   if result and col != columns and columns > 0:
-    error(my, oldpos+1, $columns & " columns expected, but found " &
+    error(self, oldpos + 1, $columns & " columns expected, but found " &
           $col & " columns")
-  inc(my.currRow)
+  inc(self.currRow)
 
-proc close*(my: var CsvParser) {.inline.} =
-  ## closes the parser `my` and its associated input stream.
-  lexbase.close(my)
+proc close*(self: var CsvParser) {.inline.} =
+  ## Closes the parser `self` and its associated input stream.
+  lexbase.close(self)
 
-proc readHeaderRow*(my: var CsvParser) =
+proc readHeaderRow*(self: var CsvParser) =
   ## Reads the first row and creates a look-up table for column numbers
-  ## See also `rowEntry <#rowEntry.CsvParser.string>`_.
-  let present = my.readRow()
+  ## See also:
+  ## * `rowEntry proc <#rowEntry,CsvParser,string>`_
+  runnableExamples:
+    import std/streams
+
+    var strm = newStringStream("One,Two,Three\n1,2,3")
+    var parser: CsvParser
+    parser.open(strm, "tmp.csv")
+
+    parser.readHeaderRow()
+    doAssert parser.headers == @["One", "Two", "Three"]
+    doAssert parser.row == @["One", "Two", "Three"]
+
+    doAssert parser.readRow()
+    doAssert parser.headers == @["One", "Two", "Three"]
+    doAssert parser.row == @["1", "2", "3"]
+
+    parser.close()
+    strm.close()
+
+  let present = self.readRow()
   if present:
-    my.headers = my.row
+    self.headers = self.row
 
-proc rowEntry*(my: var CsvParser, entry: string): var string =
-  ## Acceses a specified `entry` from the current row.
+proc rowEntry*(self: var CsvParser, entry: string): var string =
+  ## Accesses a specified `entry` from the current row.
   ##
-  ## Assumes that `readHeaderRow <#readHeaderRow.CsvParser>`_ has already been
+  ## Assumes that `readHeaderRow <#readHeaderRow,CsvParser>`_ has already been
   ## called.
-  let index = my.headers.find(entry)
+  ##
+  ## If specified `entry` does not exist, raises KeyError.
+  runnableExamples:
+    import std/streams
+    var strm = newStringStream("One,Two,Three\n1,2,3\n\n10,20,30")
+    var parser: CsvParser
+    parser.open(strm, "tmp.csv")
+    ## Requires calling `readHeaderRow`.
+    parser.readHeaderRow()
+    doAssert parser.readRow()
+    doAssert parser.rowEntry("One") == "1"
+    doAssert parser.rowEntry("Two") == "2"
+    doAssert parser.rowEntry("Three") == "3"
+    doAssertRaises(KeyError):
+      discard parser.rowEntry("NonexistentEntry")
+    parser.close()
+    strm.close()
+
+  let index = self.headers.find(entry)
   if index >= 0:
-    result = my.row[index]
+    result = self.row[index]
+  else:
+    raise newException(KeyError, "Entry `" & entry & "` doesn't exist")
 
 when not defined(testing) and isMainModule:
-  import os
+  import std/os
   var s = newFileStream(paramStr(1), fmRead)
   if s == nil: quit("cannot open the file" & paramStr(1))
   var x: CsvParser
@@ -230,36 +359,3 @@ when not defined(testing) and isMainModule:
     for val in items(x.row):
       echo "##", val, "##"
   close(x)
-
-when isMainModule:
-  import os
-  import strutils
-  block: # Tests for reading the header row
-    let content = "One,Two,Three,Four\n1,2,3,4\n10,20,30,40,\n100,200,300,400\n"
-    writeFile("temp.csv", content)
-
-    var p: CsvParser
-    p.open("temp.csv")
-    p.readHeaderRow()
-    while p.readRow():
-      let zeros = repeat('0', p.currRow-2)
-      doAssert p.rowEntry("One") == "1" & zeros
-      doAssert p.rowEntry("Two") == "2" & zeros
-      doAssert p.rowEntry("Three") == "3" & zeros
-      doAssert p.rowEntry("Four") == "4" & zeros
-    p.close()
-
-    when not defined(testing):
-      var parser: CsvParser
-      parser.open("temp.csv")
-      parser.readHeaderRow()
-      while parser.readRow():
-        echo "new row: "
-        for col in items(parser.headers):
-          echo "##", col, ":", parser.rowEntry(col), "##"
-      parser.close()
-      removeFile("temp.csv")
-
-    # Tidy up
-    removeFile("temp.csv")
-
diff --git a/lib/pure/parsejson.nim b/lib/pure/parsejson.nim
index 9c53af6a6..9292a8596 100644
--- a/lib/pure/parsejson.nim
+++ b/lib/pure/parsejson.nim
@@ -8,28 +8,31 @@
 #
 
 ## This module implements a json parser. It is used
-## and exported by the ``json`` standard library
+## and exported by the `json` standard library
 ## module, but can also be used in its own right.
 
-import
-  strutils, lexbase, streams, unicode
+import std/[strutils, lexbase, streams, unicode]
+import std/private/decode_helpers
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 type
-  JsonEventKind* = enum  ## enumeration of all events that may occur when parsing
-    jsonError,           ## an error occurred during parsing
-    jsonEof,             ## end of file reached
-    jsonString,          ## a string literal
-    jsonInt,             ## an integer literal
-    jsonFloat,           ## a float literal
-    jsonTrue,            ## the value ``true``
-    jsonFalse,           ## the value ``false``
-    jsonNull,            ## the value ``null``
-    jsonObjectStart,     ## start of an object: the ``{`` token
-    jsonObjectEnd,       ## end of an object: the ``}`` token
-    jsonArrayStart,      ## start of an array: the ``[`` token
-    jsonArrayEnd         ## start of an array: the ``]`` token
+  JsonEventKind* = enum ## enumeration of all events that may occur when parsing
+    jsonError,          ## an error occurred during parsing
+    jsonEof,            ## end of file reached
+    jsonString,         ## a string literal
+    jsonInt,            ## an integer literal
+    jsonFloat,          ## a float literal
+    jsonTrue,           ## the value `true`
+    jsonFalse,          ## the value `false`
+    jsonNull,           ## the value `null`
+    jsonObjectStart,    ## start of an object: the `{` token
+    jsonObjectEnd,      ## end of an object: the `}` token
+    jsonArrayStart,     ## start of an array: the `[` token
+    jsonArrayEnd        ## end of an array: the `]` token
 
-  TokKind* = enum         # must be synchronized with TJsonEventKind!
+  TokKind* = enum # must be synchronized with TJsonEventKind!
     tkError,
     tkEof,
     tkString,
@@ -45,18 +48,18 @@ type
     tkColon,
     tkComma
 
-  JsonError* = enum        ## enumeration that lists all errors that can occur
-    errNone,               ## no error
-    errInvalidToken,       ## invalid token
-    errStringExpected,     ## string expected
-    errColonExpected,      ## ``:`` expected
-    errCommaExpected,      ## ``,`` expected
-    errBracketRiExpected,  ## ``]`` expected
-    errCurlyRiExpected,    ## ``}`` expected
-    errQuoteExpected,      ## ``"`` or ``'`` expected
-    errEOC_Expected,       ## ``*/`` expected
-    errEofExpected,        ## EOF expected
-    errExprExpected        ## expr expected
+  JsonError* = enum       ## enumeration that lists all errors that can occur
+    errNone,              ## no error
+    errInvalidToken,      ## invalid token
+    errStringExpected,    ## string expected
+    errColonExpected,     ## `:` expected
+    errCommaExpected,     ## `,` expected
+    errBracketRiExpected, ## `]` expected
+    errCurlyRiExpected,   ## `}` expected
+    errQuoteExpected,     ## `"` or `'` expected
+    errEOC_Expected,      ## `*/` expected
+    errEofExpected,       ## EOF expected
+    errExprExpected       ## expr expected
 
   ParserState = enum
     stateEof, stateStart, stateObject, stateArray, stateExpectArrayComma,
@@ -71,7 +74,7 @@ type
     filename: string
     rawStringLiterals: bool
 
-  JsonKindError* = object of ValueError ## raised by the ``to`` macro if the
+  JsonKindError* = object of ValueError ## raised by the `to` macro if the
                                         ## JSON kind is incorrect.
   JsonParsingError* = object of ValueError ## is raised for a JSON error
 
@@ -105,7 +108,7 @@ proc open*(my: var JsonParser, input: Stream, filename: string;
            rawStringLiterals = false) =
   ## initializes the parser with an input stream. `Filename` is only used
   ## for nice error messages. If `rawStringLiterals` is true, string literals
-  ## are kepts with their surrounding quotes and escape sequences in them are
+  ## are kept with their surrounding quotes and escape sequences in them are
   ## left untouched too.
   lexbase.open(my, input)
   my.filename = filename
@@ -119,18 +122,18 @@ proc close*(my: var JsonParser) {.inline.} =
   lexbase.close(my)
 
 proc str*(my: JsonParser): string {.inline.} =
-  ## returns the character data for the events: ``jsonInt``, ``jsonFloat``,
-  ## ``jsonString``
+  ## returns the character data for the events: `jsonInt`, `jsonFloat`,
+  ## `jsonString`
   assert(my.kind in {jsonInt, jsonFloat, jsonString})
   return my.a
 
 proc getInt*(my: JsonParser): BiggestInt {.inline.} =
-  ## returns the number for the event: ``jsonInt``
+  ## returns the number for the event: `jsonInt`
   assert(my.kind == jsonInt)
   return parseBiggestInt(my.a)
 
 proc getFloat*(my: JsonParser): float {.inline.} =
-  ## returns the number for the event: ``jsonFloat``
+  ## returns the number for the event: `jsonFloat`
   assert(my.kind == jsonFloat)
   return parseFloat(my.a)
 
@@ -151,7 +154,7 @@ proc getFilename*(my: JsonParser): string {.inline.} =
   result = my.filename
 
 proc errorMsg*(my: JsonParser): string =
-  ## returns a helpful error message for the event ``jsonError``
+  ## returns a helpful error message for the event `jsonError`
   assert(my.kind == jsonError)
   result = "$1($2, $3) Error: $4" % [
     my.filename, $getLine(my), $getColumn(my), errorMessages[my.err]]
@@ -162,18 +165,11 @@ proc errorMsgExpected*(my: JsonParser, e: string): string =
   result = "$1($2, $3) Error: $4" % [
     my.filename, $getLine(my), $getColumn(my), e & " expected"]
 
-proc handleHexChar(c: char, x: var int): bool =
-  result = true # Success
-  case c
-  of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
-  of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
-  of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
-  else: result = false # error
-
 proc parseEscapedUTF16*(buf: cstring, pos: var int): int =
   result = 0
   #UTF-16 escape is always 4 bytes.
   for _ in 0..3:
+    # if char in '0' .. '9', 'a' .. 'f', 'A' .. 'F'
     if handleHexChar(buf[pos], result):
       inc(pos)
     else:
@@ -182,11 +178,10 @@ proc parseEscapedUTF16*(buf: cstring, pos: var int): int =
 proc parseString(my: var JsonParser): TokKind =
   result = tkString
   var pos = my.bufpos + 1
-  var buf = my.buf
   if my.rawStringLiterals:
     add(my.a, '"')
   while true:
-    case buf[pos]
+    case my.buf[pos]
     of '\0':
       my.err = errQuoteExpected
       result = tkError
@@ -199,9 +194,9 @@ proc parseString(my: var JsonParser): TokKind =
     of '\\':
       if my.rawStringLiterals:
         add(my.a, '\\')
-      case buf[pos+1]
+      case my.buf[pos+1]
       of '\\', '"', '\'', '/':
-        add(my.a, buf[pos+1])
+        add(my.a, my.buf[pos+1])
         inc(pos, 2)
       of 'b':
         add(my.a, '\b')
@@ -218,22 +213,25 @@ proc parseString(my: var JsonParser): TokKind =
       of 't':
         add(my.a, '\t')
         inc(pos, 2)
+      of 'v':
+        add(my.a, '\v')
+        inc(pos, 2)
       of 'u':
         if my.rawStringLiterals:
           add(my.a, 'u')
         inc(pos, 2)
         var pos2 = pos
-        var r = parseEscapedUTF16(buf, pos)
+        var r = parseEscapedUTF16(cstring(my.buf), pos)
         if r < 0:
           my.err = errInvalidToken
           break
         # Deal with surrogates
         if (r and 0xfc00) == 0xd800:
-          if buf[pos] != '\\' or buf[pos+1] != 'u':
+          if my.buf[pos] != '\\' or my.buf[pos+1] != 'u':
             my.err = errInvalidToken
             break
           inc(pos, 2)
-          var s = parseEscapedUTF16(buf, pos)
+          var s = parseEscapedUTF16(cstring(my.buf), pos)
           if (s and 0xfc00) == 0xdc00 and s > 0:
             r = 0x10000 + (((r - 0xd800) shl 10) or (s - 0xdc00))
           else:
@@ -242,8 +240,8 @@ proc parseString(my: var JsonParser): TokKind =
         if my.rawStringLiterals:
           let length = pos - pos2
           for i in 1 .. length:
-            if buf[pos2] in {'0'..'9', 'A'..'F', 'a'..'f'}:
-              add(my.a, buf[pos2])
+            if my.buf[pos2] in {'0'..'9', 'A'..'F', 'a'..'f'}:
+              add(my.a, my.buf[pos2])
               inc pos2
             else:
               break
@@ -251,61 +249,54 @@ proc parseString(my: var JsonParser): TokKind =
           add(my.a, toUTF8(Rune(r)))
       else:
         # don't bother with the error
-        add(my.a, buf[pos])
+        add(my.a, my.buf[pos])
         inc(pos)
     of '\c':
       pos = lexbase.handleCR(my, pos)
-      buf = my.buf
       add(my.a, '\c')
     of '\L':
       pos = lexbase.handleLF(my, pos)
-      buf = my.buf
       add(my.a, '\L')
     else:
-      add(my.a, buf[pos])
+      add(my.a, my.buf[pos])
       inc(pos)
   my.bufpos = pos # store back
 
 proc skip(my: var JsonParser) =
   var pos = my.bufpos
-  var buf = my.buf
   while true:
-    case buf[pos]
+    case my.buf[pos]
     of '/':
-      if buf[pos+1] == '/':
+      if my.buf[pos+1] == '/':
         # skip line comment:
         inc(pos, 2)
         while true:
-          case buf[pos]
+          case my.buf[pos]
           of '\0':
             break
           of '\c':
             pos = lexbase.handleCR(my, pos)
-            buf = my.buf
             break
           of '\L':
             pos = lexbase.handleLF(my, pos)
-            buf = my.buf
             break
           else:
             inc(pos)
-      elif buf[pos+1] == '*':
+      elif my.buf[pos+1] == '*':
         # skip long comment:
         inc(pos, 2)
         while true:
-          case buf[pos]
+          case my.buf[pos]
           of '\0':
             my.err = errEOC_Expected
             break
           of '\c':
             pos = lexbase.handleCR(my, pos)
-            buf = my.buf
           of '\L':
             pos = lexbase.handleLF(my, pos)
-            buf = my.buf
           of '*':
             inc(pos)
-            if buf[pos] == '/':
+            if my.buf[pos] == '/':
               inc(pos)
               break
           else:
@@ -316,51 +307,47 @@ proc skip(my: var JsonParser) =
       inc(pos)
     of '\c':
       pos = lexbase.handleCR(my, pos)
-      buf = my.buf
     of '\L':
       pos = lexbase.handleLF(my, pos)
-      buf = my.buf
     else:
       break
   my.bufpos = pos
 
 proc parseNumber(my: var JsonParser) =
   var pos = my.bufpos
-  var buf = my.buf
-  if buf[pos] == '-':
+  if my.buf[pos] == '-':
     add(my.a, '-')
     inc(pos)
-  if buf[pos] == '.':
+  if my.buf[pos] == '.':
     add(my.a, "0.")
     inc(pos)
   else:
-    while buf[pos] in Digits:
-      add(my.a, buf[pos])
+    while my.buf[pos] in Digits:
+      add(my.a, my.buf[pos])
       inc(pos)
-    if buf[pos] == '.':
+    if my.buf[pos] == '.':
       add(my.a, '.')
       inc(pos)
   # digits after the dot:
-  while buf[pos] in Digits:
-    add(my.a, buf[pos])
+  while my.buf[pos] in Digits:
+    add(my.a, my.buf[pos])
     inc(pos)
-  if buf[pos] in {'E', 'e'}:
-    add(my.a, buf[pos])
+  if my.buf[pos] in {'E', 'e'}:
+    add(my.a, my.buf[pos])
     inc(pos)
-    if buf[pos] in {'+', '-'}:
-      add(my.a, buf[pos])
+    if my.buf[pos] in {'+', '-'}:
+      add(my.a, my.buf[pos])
       inc(pos)
-    while buf[pos] in Digits:
-      add(my.a, buf[pos])
+    while my.buf[pos] in Digits:
+      add(my.a, my.buf[pos])
       inc(pos)
   my.bufpos = pos
 
 proc parseName(my: var JsonParser) =
   var pos = my.bufpos
-  var buf = my.buf
-  if buf[pos] in IdentStartChars:
-    while buf[pos] in IdentChars:
-      add(my.a, buf[pos])
+  if my.buf[pos] in IdentStartChars:
+    while my.buf[pos] in IdentChars:
+      add(my.a, my.buf[pos])
       inc(pos)
   my.bufpos = pos
 
diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim
index 58e1be0e4..03f151b66 100644
--- a/lib/pure/parseopt.nim
+++ b/lib/pure/parseopt.nim
@@ -11,196 +11,447 @@
 ## It supports one convenience iterator over all command line options and some
 ## lower-level features.
 ##
-## Supported syntax with default empty ``shortNoVal``/``longNoVal``:
+## Supported Syntax
+## ================
 ##
-## 1. short options - ``-abcd``, where a, b, c, d are names
-## 2. long option - ``--foo:bar``, ``--foo=bar`` or ``--foo``
-## 3. argument - everything else
+## The following syntax is supported when arguments for the `shortNoVal` and
+## `longNoVal` parameters, which are
+## `described later<#nimshortnoval-and-nimlongnoval>`_, are not provided:
 ##
-## When ``shortNoVal``/``longNoVal`` are non-empty then the ':' and '=' above
-## are still accepted, but become optional.  Note that these option key sets
-## must be updated along with the set of option keys taking no value, but
-## keys which do take values need no special updates as their set evolves.
+## 1. Short options: `-abcd`, `-e:5`, `-e=5`
+## 2. Long options: `--foo:bar`, `--foo=bar`, `--foo`
+## 3. Arguments: everything that does not start with a `-`
 ##
-## When option values begin with ':' or '=' they need to be doubled up (as in
-## ``--delim::``) or alternated (as in ``--delim=:``).
+## These three kinds of tokens are enumerated in the
+## `CmdLineKind enum<#CmdLineKind>`_.
 ##
-## The common ``--`` non-option argument delimiter appears as an empty string
-## long option key.  ``OptParser.cmd``, ``OptParser.pos``, and
-## ``os.parseCmdLine`` may be used to complete parsing in that case.
+## When option values begin with ':' or '=', they need to be doubled up (as in
+## `--delim::`) or alternated (as in `--delim=:`).
+##
+## The `--` option, commonly used to denote that every token that follows is
+## an argument, is interpreted as a long option, and its name is the empty
+## string.
+##
+## Parsing
+## =======
+##
+## Use an `OptParser<#OptParser>`_ to parse command line options. It can be
+## created with `initOptParser<#initOptParser,string,set[char],seq[string]>`_,
+## and `next<#next,OptParser>`_ advances the parser by one token.
+##
+## For each token, the parser's `kind`, `key`, and `val` fields give
+## information about that token. If the token is a long or short option, `key`
+## is the option's name, and  `val` is either the option's value, if provided,
+## or the empty string. For arguments, the `key` field contains the argument
+## itself, and `val` is unused. To check if the end of the command line has
+## been reached, check if `kind` is equal to `cmdEnd`.
+##
+## Here is an example:
+##
+##   ```Nim
+##   import std/parseopt
+##
+##   var p = initOptParser("-ab -e:5 --foo --bar=20 file.txt")
+##   while true:
+##     p.next()
+##     case p.kind
+##     of cmdEnd: break
+##     of cmdShortOption, cmdLongOption:
+##       if p.val == "":
+##         echo "Option: ", p.key
+##       else:
+##         echo "Option and value: ", p.key, ", ", p.val
+##     of cmdArgument:
+##       echo "Argument: ", p.key
+##
+##   # Output:
+##   # Option: a
+##   # Option: b
+##   # Option and value: e, 5
+##   # Option: foo
+##   # Option and value: bar, 20
+##   # Argument: file.txt
+##   ```
+##
+## The `getopt iterator<#getopt.i,OptParser>`_, which is provided for
+## convenience, can be used to iterate through all command line options as well.
+##
+## To set a default value for a variable assigned through `getopt` and accept arguments from the cmd line.
+## Assign the default value to a variable before parsing.
+## Then set the variable to the new value while parsing.
+##
+## Here is an example:
+##
+##   ```Nim
+##   import std/parseopt
+##
+##   var varName: string = "defaultValue"
+##
+##   for kind, key, val in getopt():
+##     case kind
+##     of cmdArgument:
+##       discard
+##     of cmdLongOption, cmdShortOption:
+##       case key:
+##       of "varName": # --varName:<value> in the console when executing
+##         varName = val # do input sanitization in production systems
+##     of cmdEnd:
+##       discard
+##   ```
+##
+## `shortNoVal` and `longNoVal`
+## ============================
+##
+## The optional `shortNoVal` and `longNoVal` parameters present in
+## `initOptParser<#initOptParser,string,set[char],seq[string]>`_ are for
+## specifying which short and long options do not accept values.
+##
+## When `shortNoVal` is non-empty, users are not required to separate short
+## options and their values with a ':' or '=' since the parser knows which
+## options accept values and which ones do not. This behavior also applies for
+## long options if `longNoVal` is non-empty. For short options, `-j4`
+## becomes supported syntax, and for long options, `--foo bar` becomes
+## supported. This is in addition to the `previously mentioned
+## syntax<#supported-syntax>`_. Users can still separate options and their
+## values with ':' or '=', but that becomes optional.
+##
+## As more options which do not accept values are added to your program,
+## remember to amend `shortNoVal` and `longNoVal` accordingly.
+##
+## The following example illustrates the difference between having an empty
+## `shortNoVal` and `longNoVal`, which is the default, and providing
+## arguments for those two parameters:
+##
+##   ```Nim
+##   import std/parseopt
+##
+##   proc printToken(kind: CmdLineKind, key: string, val: string) =
+##     case kind
+##     of cmdEnd: doAssert(false)  # Doesn't happen with getopt()
+##     of cmdShortOption, cmdLongOption:
+##       if val == "":
+##         echo "Option: ", key
+##       else:
+##         echo "Option and value: ", key, ", ", val
+##     of cmdArgument:
+##       echo "Argument: ", key
+##
+##   let cmdLine = "-j4 --first bar"
+##
+##   var emptyNoVal = initOptParser(cmdLine)
+##   for kind, key, val in emptyNoVal.getopt():
+##     printToken(kind, key, val)
+##
+##   # Output:
+##   # Option: j
+##   # Option: 4
+##   # Option: first
+##   # Argument: bar
+##
+##   var withNoVal = initOptParser(cmdLine, shortNoVal = {'c'},
+##                                 longNoVal = @["second"])
+##   for kind, key, val in withNoVal.getopt():
+##     printToken(kind, key, val)
+##
+##   # Output:
+##   # Option and value: j, 4
+##   # Option and value: first, bar
+##   ```
+##
+## See also
+## ========
+##
+## * `os module<os.html>`_ for lower-level command line parsing procs
+## * `parseutils module<parseutils.html>`_ for helpers that parse tokens,
+##   numbers, identifiers, etc.
+## * `strutils module<strutils.html>`_ for common string handling operations
+## * `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
+## * `parsexml module<parsexml.html>`_ for a XML / HTML parser
+## * `other parsers<lib.html#pure-libraries-parsers>`_ for more parsers
 
 {.push debugger: off.}
 
 include "system/inclrtl"
 
-import
-  os, strutils
+import std/strutils
+import std/os
 
 type
-  CmdLineKind* = enum         ## the detected command line token
-    cmdEnd,                   ## end of command line reached
-    cmdArgument,              ## argument detected
-    cmdLongOption,            ## a long option ``--option`` detected
-    cmdShortOption            ## a short option ``-c`` detected
-  OptParser* =
-      object of RootObj ## this object implements the command line parser
-    cmd*: string              #  cmd,pos exported so caller can catch "--" as..
-    pos*: int                 # ..empty key or subcmd cmdArg & handle specially
+  CmdLineKind* = enum ## The detected command line token.
+    cmdEnd,           ## End of command line reached
+    cmdArgument,      ## An argument such as a filename
+    cmdLongOption,    ## A long option such as --option
+    cmdShortOption    ## A short option such as -c
+  OptParser* = object of RootObj ## \
+    ## Implementation of the command line parser.
+    ##
+    ## To initialize it, use the
+    ## `initOptParser proc<#initOptParser,string,set[char],seq[string]>`_.
+    pos: int
     inShortState: bool
+    allowWhitespaceAfterColon: bool
     shortNoVal: set[char]
     longNoVal: seq[string]
-    kind*: CmdLineKind        ## the dected command line token
-    key*, val*: TaintedString ## key and value pair; ``key`` is the option
-                              ## or the argument, ``value`` is not "" if
-                              ## the option was given a value
+    cmds: seq[string]
+    idx: int
+    kind*: CmdLineKind           ## The detected command line token
+    key*, val*: string           ## Key and value pair; the key is the option
+                                 ## or the argument, and the value is not "" if
+                                 ## the option was given a value
 
 proc parseWord(s: string, i: int, w: var string,
-               delim: set[char] = {'\x09', ' '}): int =
+               delim: set[char] = {'\t', ' '}): int =
   result = i
   if result < s.len and s[result] == '\"':
     inc(result)
-    while result < s.len and s[result] != '\"':
+    while result < s.len:
+      if s[result] == '"':
+        inc result
+        break
       add(w, s[result])
       inc(result)
-    if result < s.len and s[result] == '\"': inc(result)
   else:
     while result < s.len and s[result] notin delim:
       add(w, s[result])
       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, {' ', '\x09', ':', '='})
-        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 '"'
+proc initOptParser*(cmdline: seq[string], shortNoVal: set[char] = {},
+                    longNoVal: seq[string] = @[];
+                    allowWhitespaceAfterColon = true): OptParser =
+  ## Initializes the command line parser.
+  ##
+  ## If `cmdline.len == 0`, the real command line as provided by the
+  ## `os` module is retrieved instead if it is available. If the
+  ## command line is not available, a `ValueError` will be raised.
+  ## Behavior of the other parameters remains the same as in
+  ## `initOptParser(string, ...)
+  ## <#initOptParser,string,set[char],seq[string]>`_.
+  ##
+  ## See also:
+  ## * `getopt iterator<#getopt.i,seq[string],set[char],seq[string]>`_
+  runnableExamples:
+    var p = initOptParser()
+    p = initOptParser(@["--left", "--debug:3", "-l", "-r:2"])
+    p = initOptParser(@["--left", "--debug:3", "-l", "-r:2"],
+                      shortNoVal = {'l'}, longNoVal = @["left"])
+
+  result.pos = 0
+  result.idx = 0
+  result.inShortState = false
+  result.shortNoVal = shortNoVal
+  result.longNoVal = longNoVal
+  result.allowWhitespaceAfterColon = allowWhitespaceAfterColon
+  if cmdline.len != 0:
+    result.cmds = newSeq[string](cmdline.len)
+    for i in 0..<cmdline.len:
+      result.cmds[i] = cmdline[i]
+  else:
+    when declared(paramCount):
+      when defined(nimscript):
+        var ctr = 0
+        var firstNimsFound = false
+        for i in countup(0, paramCount()):
+          if firstNimsFound: 
+            result.cmds[ctr] = paramStr(i)
+            inc ctr, 1
+          if paramStr(i).endsWith(".nims") and not firstNimsFound:
+            firstNimsFound = true 
+            result.cmds = newSeq[string](paramCount()-i)
       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!
-
-  proc initOptParser*(cmdline = "", shortNoVal: set[char]={},
-                      longNoVal: seq[string] = @[]): OptParser =
-    ## inits the option parser. If ``cmdline == ""``, the real command line
-    ## (as provided by the ``OS`` module) is taken.  If ``shortNoVal`` is
-    ## provided command users do not need to delimit short option keys and
-    ## values with a ':' or '='.  If ``longNoVal`` is provided command users do
-    ## not need to delimit long option keys and values with a ':' or '='
-    ## (though they still need at least a space).  In both cases, ':' or '='
-    ## may still be used if desired.  They just become optional.
-    result.pos = 0
-    result.inShortState = false
-    result.shortNoVal = shortNoVal
-    result.longNoVal = longNoVal
-    if cmdline != "":
-      result.cmd = cmdline
-    else:
-      result.cmd = ""
-      for i in countup(1, paramCount()):
-        result.cmd.add quote(paramStr(i).string)
-        result.cmd.add ' '
-    result.kind = cmdEnd
-    result.key = TaintedString""
-    result.val = TaintedString""
-
-  proc initOptParser*(cmdline: seq[TaintedString], shortNoVal: set[char]={},
-                      longNoVal: seq[string] = @[]): OptParser =
-    ## inits the option parser. If ``cmdline.len == 0``, the real command line
-    ## (as provided by the ``OS`` module) is taken. ``shortNoVal`` and
-    ## ``longNoVal`` behavior is the same as for ``initOptParser(string,...)``.
-    result.pos = 0
-    result.inShortState = false
-    result.shortNoVal = shortNoVal
-    result.longNoVal = longNoVal
-    result.cmd = ""
-    if cmdline.len != 0:
-      for i in 0..<cmdline.len:
-        result.cmd.add quote(cmdline[i].string)
-        result.cmd.add ' '
+        result.cmds = newSeq[string](paramCount())
+        for i in countup(1, paramCount()):
+          result.cmds[i-1] = paramStr(i)
     else:
-      for i in countup(1, paramCount()):
-        result.cmd.add quote(paramStr(i).string)
-        result.cmd.add ' '
-    result.kind = cmdEnd
-    result.key = TaintedString""
-    result.val = TaintedString""
-
-proc handleShortOption(p: var OptParser) =
+      # we cannot provide this for NimRtl creation on Posix, because we can't
+      # access the command line arguments then!
+      raiseAssert "empty command line given but" &
+        " real command line is not accessible"
+  result.kind = cmdEnd
+  result.key = ""
+  result.val = ""
+
+proc initOptParser*(cmdline = "", shortNoVal: set[char] = {},
+                    longNoVal: seq[string] = @[];
+                    allowWhitespaceAfterColon = true): OptParser =
+  ## Initializes the command line parser.
+  ##
+  ## If `cmdline == ""`, the real command line as provided by the
+  ## `os` module is retrieved instead if it is available. If the
+  ## command line is not available, a `ValueError` will be raised.
+  ##
+  ## `shortNoVal` and `longNoVal` are used to specify which options
+  ## do not take values. See the `documentation about these
+  ## parameters<#nimshortnoval-and-nimlongnoval>`_ for more information on
+  ## how this affects parsing.
+  ##
+  ## This does not provide a way of passing default values to arguments.
+  ##
+  ## See also:
+  ## * `getopt iterator<#getopt.i,OptParser>`_
+  runnableExamples:
+    var p = initOptParser()
+    p = initOptParser("--left --debug:3 -l -r:2")
+    p = initOptParser("--left --debug:3 -l -r:2",
+                      shortNoVal = {'l'}, longNoVal = @["left"])
+
+  initOptParser(parseCmdLine(cmdline), shortNoVal, longNoVal, allowWhitespaceAfterColon)
+
+proc handleShortOption(p: var OptParser; cmd: string) =
   var i = p.pos
   p.kind = cmdShortOption
-  add(p.key.string, p.cmd[i])
-  inc(i)
+  if i < cmd.len:
+    add(p.key, cmd[i])
+    inc(i)
   p.inShortState = true
-  while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}:
+  while i < cmd.len and cmd[i] in {'\t', ' '}:
     inc(i)
     p.inShortState = false
-  if i < p.cmd.len and p.cmd[i] in {':', '='} or
-      card(p.shortNoVal) > 0 and p.key.string[0] notin p.shortNoVal:
-    if i < p.cmd.len and p.cmd[i] in {':', '='}:
+  if i < cmd.len and (cmd[i] in {':', '='} or
+      card(p.shortNoVal) > 0 and p.key[0] notin p.shortNoVal):
+    if i < cmd.len and cmd[i] in {':', '='}:
       inc(i)
     p.inShortState = false
-    while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i)
-    i = parseWord(p.cmd, i, p.val.string)
-  if i >= p.cmd.len: p.inShortState = false
-  p.pos = i
+    while i < cmd.len and cmd[i] in {'\t', ' '}: inc(i)
+    p.val = substr(cmd, i)
+    p.pos = 0
+    inc p.idx
+  else:
+    p.pos = i
+  if i >= cmd.len:
+    p.inShortState = false
+    p.pos = 0
+    inc p.idx
 
 proc next*(p: var OptParser) {.rtl, extern: "npo$1".} =
-  ## parses the first or next option; ``p.kind`` describes what token has been
-  ## parsed. ``p.key`` and ``p.val`` are set accordingly.
+  ## Parses the next token.
+  ##
+  ## `p.kind` describes what kind of token has been parsed. `p.key` and
+  ## `p.val` are set accordingly.
+  runnableExamples:
+    var p = initOptParser("--left -r:2 file.txt")
+    p.next()
+    doAssert p.kind == cmdLongOption and p.key == "left"
+    p.next()
+    doAssert p.kind == cmdShortOption and p.key == "r" and p.val == "2"
+    p.next()
+    doAssert p.kind == cmdArgument and p.key == "file.txt"
+    p.next()
+    doAssert p.kind == cmdEnd
+
+  if p.idx >= p.cmds.len:
+    p.kind = cmdEnd
+    return
+
   var i = p.pos
-  while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i)
+  while i < p.cmds[p.idx].len and p.cmds[p.idx][i] in {'\t', ' '}: inc(i)
   p.pos = i
-  setLen(p.key.string, 0)
-  setLen(p.val.string, 0)
+  setLen(p.key, 0)
+  setLen(p.val, 0)
   if p.inShortState:
-    handleShortOption(p)
-    return
-  if i >= p.cmd.len:
-    p.kind = cmdEnd
-    return
-  if p.cmd[i] == '-':
+    p.inShortState = false
+    if i >= p.cmds[p.idx].len:
+      inc(p.idx)
+      p.pos = 0
+      if p.idx >= p.cmds.len:
+        p.kind = cmdEnd
+        return
+    else:
+      handleShortOption(p, p.cmds[p.idx])
+      return
+
+  if i < p.cmds[p.idx].len and p.cmds[p.idx][i] == '-':
     inc(i)
-    if i < p.cmd.len and p.cmd[i] == '-':
+    if i < p.cmds[p.idx].len and p.cmds[p.idx][i] == '-':
       p.kind = cmdLongOption
       inc(i)
-      i = parseWord(p.cmd, i, p.key.string, {' ', '\x09', ':', '='})
-      while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i)
-      if i < p.cmd.len and p.cmd[i] in {':', '='} or
-          len(p.longNoVal) > 0 and p.key.string notin p.longNoVal:
-        if i < p.cmd.len and p.cmd[i] in {':', '='}:
-          inc(i)
-        while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}: inc(i)
-        p.pos = parseWord(p.cmd, i, p.val.string)
+      i = parseWord(p.cmds[p.idx], i, p.key, {' ', '\t', ':', '='})
+      while i < p.cmds[p.idx].len and p.cmds[p.idx][i] in {'\t', ' '}: inc(i)
+      if i < p.cmds[p.idx].len and p.cmds[p.idx][i] in {':', '='}:
+        inc(i)
+        while i < p.cmds[p.idx].len and p.cmds[p.idx][i] in {'\t', ' '}: inc(i)
+        # if we're at the end, use the next command line option:
+        if i >= p.cmds[p.idx].len and p.idx < p.cmds.len and
+            p.allowWhitespaceAfterColon:
+          inc p.idx
+          i = 0
+        if p.idx < p.cmds.len:
+          p.val = p.cmds[p.idx].substr(i)
+      elif len(p.longNoVal) > 0 and p.key notin p.longNoVal and p.idx+1 < p.cmds.len:
+        p.val = p.cmds[p.idx+1]
+        inc p.idx
       else:
-        p.pos = i
+        p.val = ""
+      inc p.idx
+      p.pos = 0
     else:
       p.pos = i
-      handleShortOption(p)
+      handleShortOption(p, p.cmds[p.idx])
   else:
     p.kind = cmdArgument
-    p.pos = parseWord(p.cmd, i, p.key.string)
+    p.key = p.cmds[p.idx]
+    inc p.idx
+    p.pos = 0
 
-proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1".} =
-  ## retrieves the rest of the command line that has not been parsed yet.
-  result = strip(substr(p.cmd, p.pos, len(p.cmd) - 1)).TaintedString
+when declared(quoteShellCommand):
+  proc cmdLineRest*(p: OptParser): string {.rtl, extern: "npo$1".} =
+    ## Retrieves the rest of the command line that has not been parsed yet.
+    ##
+    ## See also:
+    ## * `remainingArgs proc<#remainingArgs,OptParser>`_
+    ##
+    ## **Examples:**
+    ##   ```Nim
+    ##   var p = initOptParser("--left -r:2 -- foo.txt bar.txt")
+    ##   while true:
+    ##     p.next()
+    ##     if p.kind == cmdLongOption and p.key == "":  # Look for "--"
+    ##       break
+    ##   doAssert p.cmdLineRest == "foo.txt bar.txt"
+    ##   ```
+    result = p.cmds[p.idx .. ^1].quoteShellCommand
+
+proc remainingArgs*(p: OptParser): seq[string] {.rtl, extern: "npo$1".} =
+  ## Retrieves a sequence of the arguments that have not been parsed yet.
+  ##
+  ## See also:
+  ## * `cmdLineRest proc<#cmdLineRest,OptParser>`_
+  ##
+  ## **Examples:**
+  ##   ```Nim
+  ##   var p = initOptParser("--left -r:2 -- foo.txt bar.txt")
+  ##   while true:
+  ##     p.next()
+  ##     if p.kind == cmdLongOption and p.key == "":  # Look for "--"
+  ##       break
+  ##   doAssert p.remainingArgs == @["foo.txt", "bar.txt"]
+  ##   ```
+  result = @[]
+  for i in p.idx..<p.cmds.len: result.add 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.
-  ## Example:
+iterator getopt*(p: var OptParser): tuple[kind: CmdLineKind, key,
+    val: string] =
+  ## Convenience iterator for iterating over the given
+  ## `OptParser<#OptParser>`_.
+  ##
+  ## There is no need to check for `cmdEnd` while iterating. If using `getopt`
+  ## with case switching, checking for `cmdEnd` is required.
+  ##
+  ## See also:
+  ## * `initOptParser proc<#initOptParser,string,set[char],seq[string]>`_
   ##
-  ## .. code-block:: nim
+  ## **Examples:**
+  ##
+  ##   ```Nim
+  ##   # these are placeholders, of course
+  ##   proc writeHelp() = discard
+  ##   proc writeVersion() = discard
+  ##
+  ##   var filename: string
   ##   var p = initOptParser("--left --debug:3 -l -r:2")
+  ##
   ##   for kind, key, val in p.getopt():
   ##     case kind
   ##     of cmdArgument:
@@ -211,42 +462,64 @@ iterator getopt*(p: var OptParser): tuple[kind: CmdLineKind, key, val: TaintedSt
   ##       of "version", "v": writeVersion()
   ##     of cmdEnd: assert(false) # cannot happen
   ##   if filename == "":
-  ##     # no filename has been given, so we show the help:
+  ##     # no filename has been given, so we show the help
   ##     writeHelp()
+  ##   ```
   p.pos = 0
+  p.idx = 0
   while true:
     next(p)
     if p.kind == cmdEnd: break
     yield (p.kind, p.key, p.val)
 
-when declared(initOptParser):
-  iterator getopt*(cmdline: seq[TaintedString] = commandLineParams(),
-                   shortNoVal: set[char]={}, longNoVal: seq[string] = @[]):
-             tuple[kind: CmdLineKind, key, val: TaintedString] =
-    ## This is an convenience iterator for iterating over command line arguments.
-    ## This creates a new OptParser.  See the above ``getopt(var OptParser)``
-    ## example for using default empty ``NoVal`` parameters.  This example is
-    ## for the same option keys as that example but here option key-value
-    ## separators become optional for command users:
-    ##
-    ## .. code-block:: nim
-    ##   for kind, key, val in getopt(shortNoVal = { 'l' },
-    ##                                longNoVal = @[ "left" ]):
-    ##     case kind
-    ##     of cmdArgument:
-    ##       filename = key
-    ##     of cmdLongOption, cmdShortOption:
-    ##       case key
-    ##       of "help", "h": writeHelp()
-    ##       of "version", "v": writeVersion()
-    ##     of cmdEnd: assert(false) # cannot happen
-    ##   if filename == "":
-    ##     writeHelp()
-    ##
-    var p = initOptParser(cmdline, shortNoVal=shortNoVal, longNoVal=longNoVal)
-    while true:
-      next(p)
-      if p.kind == cmdEnd: break
-      yield (p.kind, p.key, p.val)
+iterator getopt*(cmdline: seq[string] = @[],
+                  shortNoVal: set[char] = {}, longNoVal: seq[string] = @[]):
+            tuple[kind: CmdLineKind, key, val: string] =
+  ## Convenience iterator for iterating over command line arguments.
+  ##
+  ## This creates a new `OptParser<#OptParser>`_. If no command line
+  ## arguments are provided, the real command line as provided by the
+  ## `os` module is retrieved instead.
+  ##
+  ## `shortNoVal` and `longNoVal` are used to specify which options
+  ## do not take values. See the `documentation about these
+  ## parameters<#nimshortnoval-and-nimlongnoval>`_ for more information on
+  ## how this affects parsing.
+  ##
+  ## There is no need to check for `cmdEnd` while iterating. If using `getopt`
+  ## with case switching, checking for `cmdEnd` is required.
+  ##
+  ## See also:
+  ## * `initOptParser proc<#initOptParser,seq[string],set[char],seq[string]>`_
+  ##
+  ## **Examples:**
+  ##
+  ##   ```Nim
+  ##   # these are placeholders, of course
+  ##   proc writeHelp() = discard
+  ##   proc writeVersion() = discard
+  ##
+  ##   var filename: string
+  ##   let params = @["--left", "--debug:3", "-l", "-r:2"]
+  ##
+  ##   for kind, key, val in getopt(params):
+  ##     case kind
+  ##     of cmdArgument:
+  ##       filename = key
+  ##     of cmdLongOption, cmdShortOption:
+  ##       case key
+  ##       of "help", "h": writeHelp()
+  ##       of "version", "v": writeVersion()
+  ##     of cmdEnd: assert(false) # cannot happen
+  ##   if filename == "":
+  ##     # no filename has been written, so we show the help
+  ##     writeHelp()
+  ##   ```
+  var p = initOptParser(cmdline, shortNoVal = shortNoVal,
+      longNoVal = longNoVal)
+  while true:
+    next(p)
+    if p.kind == cmdEnd: break
+    yield (p.kind, p.key, p.val)
 
 {.pop.}
diff --git a/lib/pure/parseopt2.nim b/lib/pure/parseopt2.nim
deleted file mode 100644
index b54a56c0c..000000000
--- a/lib/pure/parseopt2.nim
+++ /dev/null
@@ -1,164 +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 provides the standard Nim command line parser.
-## It supports one convenience iterator over all command line options and some
-## lower-level features.
-##
-## Supported syntax:
-##
-## 1. short options - ``-abcd``, where a, b, c, d are names
-## 2. long option - ``--foo:bar``, ``--foo=bar`` or ``--foo``
-## 3. argument - everything else
-
-{.deprecated: "Use the 'parseopt' module instead".}
-{.push debugger: off.}
-
-include "system/inclrtl"
-
-import
-  os, strutils
-
-type
-  CmdLineKind* = enum         ## the detected command line token
-    cmdEnd,                   ## end of command line reached
-    cmdArgument,              ## argument detected
-    cmdLongOption,            ## a long option ``--option`` detected
-    cmdShortOption            ## a short option ``-c`` detected
-  OptParser* =
-      object of RootObj ## this object implements the command line parser
-    cmd: seq[string]
-    pos: int
-    remainingShortOptions: string
-    kind*: CmdLineKind        ## the detected command line token
-    key*, val*: TaintedString ## key and value pair; ``key`` is the option
-                              ## or the argument, ``value`` is not "" if
-                              ## the option was given a value
-
-proc initOptParser*(cmdline: seq[string]): OptParser {.rtl.} =
-  ## Initalizes option parses with cmdline. cmdline should not contain
-  ## argument 0 - program name.
-  ## If cmdline == nil default to current command line arguments.
-  result.remainingShortOptions = ""
-  when not defined(createNimRtl):
-    if cmdline == nil:
-      result.cmd = commandLineParams()
-      return
-  else:
-    assert cmdline != nil, "Cannot determine command line arguments."
-
-  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(seq[string](nil))
-  else:
-    return initOptParser(cmdline.split)
-
-when not defined(createNimRtl):
-  proc initOptParser*(): OptParser =
-    ## Initializes option parser from current command line arguments.
-    return initOptParser(commandLineParams())
-
-proc next*(p: var OptParser) {.rtl, extern: "npo2$1".}
-
-proc nextOption(p: var OptParser, token: string, allowEmpty: bool) =
-  for splitchar in [':', '=']:
-    if splitchar in token:
-      let pos = token.find(splitchar)
-      p.key = token[0..pos-1]
-      p.val = token[pos+1..token.len-1]
-      return
-
-  p.key = token
-  if allowEmpty:
-    p.val = ""
-  else:
-    p.remainingShortOptions = token[0..token.len-1]
-    p.next()
-
-proc next(p: var OptParser) =
-  if p.remainingShortOptions.len != 0:
-    p.kind = cmdShortOption
-    p.key = TaintedString(p.remainingShortOptions[0..0])
-    p.val = ""
-    p.remainingShortOptions = p.remainingShortOptions[1..p.remainingShortOptions.len-1]
-    return
-
-  if p.pos >= p.cmd.len:
-    p.kind = cmdEnd
-    return
-
-  let token = p.cmd[p.pos]
-  p.pos += 1
-
-  if token.startsWith("--"):
-    p.kind = cmdLongOption
-    nextOption(p, token[2..token.len-1], allowEmpty=true)
-  elif token.startsWith("-"):
-    p.kind = cmdShortOption
-    nextOption(p, token[1..token.len-1], allowEmpty=true)
-  else:
-    p.kind = cmdArgument
-    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(" ")
-
-type
-  GetoptResult* = tuple[kind: CmdLineKind, key, val: TaintedString]
-
-iterator getopt*(p: var OptParser): GetoptResult =
-  ## This is an convenience iterator for iterating over the given OptParser object.
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   var p = initOptParser("--left --debug:3 -l=4 -r:2")
-  ##   for kind, key, val in p.getopt():
-  ##     case kind
-  ##     of cmdArgument:
-  ##       filename = key
-  ##     of cmdLongOption, cmdShortOption:
-  ##       case key
-  ##       of "help", "h": writeHelp()
-  ##       of "version", "v": writeVersion()
-  ##     of cmdEnd: assert(false) # cannot happen
-  ##   if filename == "":
-  ##     # no filename has been given, so we show the help:
-  ##     writeHelp()
-  p.pos = 0
-  while true:
-    next(p)
-    if p.kind == cmdEnd: break
-    yield (p.kind, p.key, p.val)
-
-when declared(paramCount):
-  iterator getopt*(): GetoptResult =
-    ## This is an convenience iterator for iterating over the command line arguments.
-    ## This create a new OptParser object.
-    ## See above for a more detailed example
-    ##
-    ## .. code-block:: nim
-    ##   for kind, key, val in getopt():
-    ##     # this will iterate over all arguments passed to the cmdline.
-    ##     continue
-    ##
-    var p = initOptParser()
-    while true:
-      next(p)
-      if p.kind == cmdEnd: break
-      yield (p.kind, p.key, p.val)
-
-{.pop.}
diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim
index e3bab9a8d..a7c938d01 100644
--- a/lib/pure/parsesql.nim
+++ b/lib/pure/parsesql.nim
@@ -7,21 +7,26 @@
 #    distribution, for details about the copyright.
 #
 
-## The ``parsesql`` module implements a high performance SQL file
+## The `parsesql` module implements a high performance SQL file
 ## parser. It parses PostgreSQL syntax and the SQL ANSI standard.
+##
+## Unstable API.
 
-import
-  hashes, strutils, lexbase
+import std/[strutils, lexbase]
+import std/private/decode_helpers
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 # ------------------- scanner -------------------------------------------------
 
 type
-  TokKind = enum       ## enumeration of all SQL tokens
-    tkInvalid,          ## invalid token
-    tkEof,              ## end of file reached
-    tkIdentifier,       ## abc
-    tkQuotedIdentifier, ## "abc"
-    tkStringConstant,   ## 'abc'
+  TokKind = enum            ## enumeration of all SQL tokens
+    tkInvalid,              ## invalid token
+    tkEof,                  ## end of file reached
+    tkIdentifier,           ## abc
+    tkQuotedIdentifier,     ## "abc"
+    tkStringConstant,       ## 'abc'
     tkEscapeConstant,       ## e'abc'
     tkDollarQuotedConstant, ## $tag$abc$tag$
     tkBitStringConstant,    ## B'00011'
@@ -38,9 +43,9 @@ type
     tkBracketRi,            ## ']'
     tkDot                   ## '.'
 
-  Token = object  # a token
-    kind: TokKind           # the type of the token
-    literal: string          # the parsed (string) literal
+  Token = object    # a token
+    kind: TokKind   # the type of the token
+    literal: string # the parsed (string) literal
 
   SqlLexer* = object of BaseLexer ## the parser object.
     filename: string
@@ -55,7 +60,7 @@ const
 
   reservedKeywords = @[
     # statements
-    "select", "from", "where", "group", "limit", "having",
+    "select", "from", "where", "group", "limit", "offset", "having",
     # functions
     "count",
   ]
@@ -70,20 +75,6 @@ proc getColumn(L: SqlLexer): int =
 proc getLine(L: SqlLexer): int =
   result = L.lineNumber
 
-proc handleHexChar(c: var SqlLexer, xi: var int) =
-  case c.buf[c.bufpos]
-  of '0'..'9':
-    xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
-    inc(c.bufpos)
-  of 'a'..'f':
-    xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10)
-    inc(c.bufpos)
-  of 'A'..'F':
-    xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
-    inc(c.bufpos)
-  else:
-    discard
-
 proc handleOctChar(c: var SqlLexer, xi: var int) =
   if c.buf[c.bufpos] in {'0'..'7'}:
     xi = (xi shl 3) or (ord(c.buf[c.bufpos]) - ord('0'))
@@ -128,8 +119,10 @@ proc getEscapedChar(c: var SqlLexer, tok: var Token) =
   of 'x', 'X':
     inc(c.bufpos)
     var xi = 0
-    handleHexChar(c, xi)
-    handleHexChar(c, xi)
+    if handleHexChar(c.buf[c.bufpos], xi):
+      inc(c.bufpos)
+      if handleHexChar(c.buf[c.bufpos], xi):
+        inc(c.bufpos)
     add(tok.literal, chr(xi))
   of '0'..'7':
     var xi = 0
@@ -148,35 +141,33 @@ proc handleCRLF(c: var SqlLexer, pos: int): int =
 
 proc skip(c: var SqlLexer) =
   var pos = c.bufpos
-  var buf = c.buf
   var nested = 0
   while true:
-    case buf[pos]
+    case c.buf[pos]
     of ' ', '\t':
       inc(pos)
     of '-':
-      if buf[pos+1] == '-':
-        while not (buf[pos] in {'\c', '\L', lexbase.EndOfFile}): inc(pos)
+      if c.buf[pos+1] == '-':
+        while not (c.buf[pos] in {'\c', '\L', lexbase.EndOfFile}): inc(pos)
       else:
         break
     of '/':
-      if buf[pos+1] == '*':
-        inc(pos,2)
+      if c.buf[pos+1] == '*':
+        inc(pos, 2)
         while true:
-          case buf[pos]
+          case c.buf[pos]
           of '\0': break
           of '\c', '\L':
             pos = handleCRLF(c, pos)
-            buf = c.buf
           of '*':
-            if buf[pos+1] == '/':
+            if c.buf[pos+1] == '/':
               inc(pos, 2)
               if nested <= 0: break
               dec(nested)
             else:
               inc(pos)
           of '/':
-            if buf[pos+1] == '*':
+            if c.buf[pos+1] == '*':
               inc(pos, 2)
               inc(nested)
             else:
@@ -185,21 +176,19 @@ proc skip(c: var SqlLexer) =
       else: break
     of '\c', '\L':
       pos = handleCRLF(c, pos)
-      buf = c.buf
     else:
-      break                   # EndOfFile also leaves the loop
+      break # EndOfFile also leaves the loop
   c.bufpos = pos
 
 proc getString(c: var SqlLexer, tok: var Token, kind: TokKind) =
   var pos = c.bufpos + 1
-  var buf = c.buf
   tok.kind = kind
   block parseLoop:
     while true:
       while true:
-        var ch = buf[pos]
+        var ch = c.buf[pos]
         if ch == '\'':
-          if buf[pos+1] == '\'':
+          if c.buf[pos+1] == '\'':
             inc(pos, 2)
             add(tok.literal, '\'')
           else:
@@ -221,30 +210,27 @@ proc getString(c: var SqlLexer, tok: var Token, kind: TokKind) =
       if c.lineNumber > line:
         # a new line whitespace has been parsed, so we check if the string
         # continues after the whitespace:
-        buf = c.buf # may have been reallocated
         pos = c.bufpos
-        if buf[pos] == '\'': inc(pos)
+        if c.buf[pos] == '\'': inc(pos)
         else: break parseLoop
       else: break parseLoop
   c.bufpos = pos
 
 proc getDollarString(c: var SqlLexer, tok: var Token) =
   var pos = c.bufpos + 1
-  var buf = c.buf
   tok.kind = tkDollarQuotedConstant
   var tag = "$"
-  while buf[pos] in IdentChars:
-    add(tag, buf[pos])
+  while c.buf[pos] in IdentChars:
+    add(tag, c.buf[pos])
     inc(pos)
-  if buf[pos] == '$': inc(pos)
+  if c.buf[pos] == '$': inc(pos)
   else:
     tok.kind = tkInvalid
     return
   while true:
-    case buf[pos]
+    case c.buf[pos]
     of '\c', '\L':
       pos = handleCRLF(c, pos)
-      buf = c.buf
       add(tok.literal, "\L")
     of '\0':
       tok.kind = tkInvalid
@@ -252,37 +238,36 @@ proc getDollarString(c: var SqlLexer, tok: var Token) =
     of '$':
       inc(pos)
       var tag2 = "$"
-      while buf[pos] in IdentChars:
-        add(tag2, buf[pos])
+      while c.buf[pos] in IdentChars:
+        add(tag2, c.buf[pos])
         inc(pos)
-      if buf[pos] == '$': inc(pos)
+      if c.buf[pos] == '$': inc(pos)
       if tag2 == tag: break
       add(tok.literal, tag2)
       add(tok.literal, '$')
     else:
-      add(tok.literal, buf[pos])
+      add(tok.literal, c.buf[pos])
       inc(pos)
   c.bufpos = pos
 
 proc getSymbol(c: var SqlLexer, tok: var Token) =
   var pos = c.bufpos
-  var buf = c.buf
   while true:
-    add(tok.literal, buf[pos])
+    add(tok.literal, c.buf[pos])
     inc(pos)
-    if buf[pos] notin {'a'..'z','A'..'Z','0'..'9','_','$', '\128'..'\255'}:
+    if c.buf[pos] notin {'a'..'z', 'A'..'Z', '0'..'9', '_', '$',
+        '\128'..'\255'}:
       break
   c.bufpos = pos
   tok.kind = tkIdentifier
 
-proc getQuotedIdentifier(c: var SqlLexer, tok: var Token, quote='\"') =
+proc getQuotedIdentifier(c: var SqlLexer, tok: var Token, quote = '\"') =
   var pos = c.bufpos + 1
-  var buf = c.buf
   tok.kind = tkQuotedIdentifier
   while true:
-    var ch = buf[pos]
+    var ch = c.buf[pos]
     if ch == quote:
-      if buf[pos+1] == quote:
+      if c.buf[pos+1] == quote:
         inc(pos, 2)
         add(tok.literal, quote)
       else:
@@ -298,11 +283,10 @@ proc getQuotedIdentifier(c: var SqlLexer, tok: var Token, quote='\"') =
 
 proc getBitHexString(c: var SqlLexer, tok: var Token, validChars: set[char]) =
   var pos = c.bufpos + 1
-  var buf = c.buf
   block parseLoop:
     while true:
       while true:
-        var ch = buf[pos]
+        var ch = c.buf[pos]
         if ch in validChars:
           add(tok.literal, ch)
           inc(pos)
@@ -318,9 +302,8 @@ proc getBitHexString(c: var SqlLexer, tok: var Token, validChars: set[char]) =
       if c.lineNumber > line:
         # a new line whitespace has been parsed, so we check if the string
         # continues after the whitespace:
-        buf = c.buf # may have been reallocated
         pos = c.bufpos
-        if buf[pos] == '\'': inc(pos)
+        if c.buf[pos] == '\'': inc(pos)
         else: break parseLoop
       else: break parseLoop
   c.bufpos = pos
@@ -328,29 +311,28 @@ proc getBitHexString(c: var SqlLexer, tok: var Token, validChars: set[char]) =
 proc getNumeric(c: var SqlLexer, tok: var Token) =
   tok.kind = tkInteger
   var pos = c.bufpos
-  var buf = c.buf
-  while buf[pos] in Digits:
-    add(tok.literal, buf[pos])
+  while c.buf[pos] in Digits:
+    add(tok.literal, c.buf[pos])
     inc(pos)
-  if buf[pos] == '.':
+  if c.buf[pos] == '.':
     tok.kind = tkNumeric
-    add(tok.literal, buf[pos])
+    add(tok.literal, c.buf[pos])
     inc(pos)
-    while buf[pos] in Digits:
-      add(tok.literal, buf[pos])
+    while c.buf[pos] in Digits:
+      add(tok.literal, c.buf[pos])
       inc(pos)
-  if buf[pos] in {'E', 'e'}:
+  if c.buf[pos] in {'E', 'e'}:
     tok.kind = tkNumeric
-    add(tok.literal, buf[pos])
+    add(tok.literal, c.buf[pos])
     inc(pos)
-    if buf[pos] == '+':
+    if c.buf[pos] == '+':
       inc(pos)
-    elif buf[pos] == '-':
-      add(tok.literal, buf[pos])
+    elif c.buf[pos] == '-':
+      add(tok.literal, c.buf[pos])
       inc(pos)
-    if buf[pos] in Digits:
-      while buf[pos] in Digits:
-        add(tok.literal, buf[pos])
+    if c.buf[pos] in Digits:
+      while c.buf[pos] in Digits:
+        add(tok.literal, c.buf[pos])
         inc(pos)
     else:
       tok.kind = tkInvalid
@@ -361,24 +343,23 @@ proc getOperator(c: var SqlLexer, tok: var Token) =
                      '^', '&', '|', '`', '?'}
   tok.kind = tkOperator
   var pos = c.bufpos
-  var buf = c.buf
   var trailingPlusMinus = false
   while true:
-    case buf[pos]
+    case c.buf[pos]
     of '-':
-      if buf[pos] == '-': break
-      if not trailingPlusMinus and buf[pos+1] notin operators and
+      if c.buf[pos] == '-': break
+      if not trailingPlusMinus and c.buf[pos+1] notin operators and
            tok.literal.len > 0: break
     of '/':
-      if buf[pos] == '*': break
+      if c.buf[pos] == '*': break
     of '~', '!', '@', '#', '%', '^', '&', '|', '`', '?':
       trailingPlusMinus = true
     of '+':
-      if not trailingPlusMinus and buf[pos+1] notin operators and
+      if not trailingPlusMinus and c.buf[pos+1] notin operators and
            tok.literal.len > 0: break
     of '*', '<', '>', '=': discard
     else: break
-    add(tok.literal, buf[pos])
+    add(tok.literal, c.buf[pos])
     inc(pos)
   c.bufpos = pos
 
@@ -414,7 +395,7 @@ proc getTok(c: var SqlLexer, tok: var Token) =
   of 'x', 'X':
     if c.buf[c.bufpos + 1] == '\'':
       tok.kind = tkHexStringConstant
-      getBitHexString(c, tok, {'a'..'f','A'..'F','0'..'9'})
+      getBitHexString(c, tok, {'a'..'f', 'A'..'F', '0'..'9'})
     else:
       getSymbol(c, tok)
   of '$': getDollarString(c, tok)
@@ -515,7 +496,7 @@ type
     nkConstraint,
     nkUnique,
     nkIdentity,
-    nkColumnDef,        ## name, datatype, constraints
+    nkColumnDef,      ## name, datatype, constraints
     nkInsert,
     nkUpdate,
     nkDelete,
@@ -528,6 +509,7 @@ type
     nkFromItemPair,
     nkGroup,
     nkLimit,
+    nkOffset,
     nkHaving,
     nkOrder,
     nkJoin,
@@ -554,34 +536,34 @@ const
 
 type
   SqlParseError* = object of ValueError ## Invalid SQL encountered
-  SqlNode* = ref SqlNodeObj        ## an SQL abstract syntax tree node
-  SqlNodeObj* = object              ## an SQL abstract syntax tree node
-    case kind*: SqlNodeKind      ## kind of syntax tree
+  SqlNode* = ref SqlNodeObj ## an SQL abstract syntax tree node
+  SqlNodeObj* = object      ## an SQL abstract syntax tree node
+    case kind*: SqlNodeKind ## kind of syntax tree
     of LiteralNodes:
-      strVal*: string             ## AST leaf: the identifier, numeric literal
-                                  ## string literal, etc.
+      strVal*: string       ## AST leaf: the identifier, numeric literal
+                            ## string literal, etc.
     else:
-      sons*: seq[SqlNode]        ## the node's children
+      sons*: seq[SqlNode]   ## the node's children
 
   SqlParser* = object of SqlLexer ## SQL parser object
     tok: Token
 
-
-{.deprecated: [EInvalidSql: SqlParseError, PSqlNode: SqlNode,
-    TSqlNode: SqlNodeObj, TSqlParser: SqlParser, TSqlNodeKind: SqlNodeKind].}
-
 proc newNode*(k: SqlNodeKind): SqlNode =
-  new(result)
-  result.kind = k
+  when defined(js): # bug #14117
+    case k
+    of LiteralNodes:
+      result = SqlNode(kind: k, strVal: "")
+    else:
+      result = SqlNode(kind: k, sons: @[])
+  else:
+    result = SqlNode(kind: k)
 
 proc newNode*(k: SqlNodeKind, s: string): SqlNode =
-  new(result)
-  result.kind = k
+  result = SqlNode(kind: k)
   result.strVal = s
 
 proc newNode*(k: SqlNodeKind, sons: seq[SqlNode]): SqlNode =
-  new(result)
-  result.kind = k
+  result = SqlNode(kind: k)
   result.sons = sons
 
 proc len*(n: SqlNode): int =
@@ -591,9 +573,9 @@ proc len*(n: SqlNode): int =
     result = n.sons.len
 
 proc `[]`*(n: SqlNode; i: int): SqlNode = n.sons[i]
+proc `[]`*(n: SqlNode; i: BackwardsIndex): SqlNode = n.sons[n.len - int(i)]
 
 proc add*(father, n: SqlNode) =
-  if isNil(father.sons): father.sons = @[]
   add(father.sons, n)
 
 proc getTok(p: var SqlParser) =
@@ -675,7 +657,7 @@ proc getPrecedence(p: SqlParser): int =
     result = 5
   elif isOpr(p, "=") or isOpr(p, "<") or isOpr(p, ">") or isOpr(p, ">=") or
        isOpr(p, "<=") or isOpr(p, "<>") or isOpr(p, "!=") or isKeyw(p, "is") or
-       isKeyw(p, "like"):
+       isKeyw(p, "like") or isKeyw(p, "in"):
     result = 4
   elif isKeyw(p, "and"):
     result = 3
@@ -689,8 +671,8 @@ proc getPrecedence(p: SqlParser): int =
   else:
     result = - 1
 
-proc parseExpr(p: var SqlParser): SqlNode
-proc parseSelect(p: var SqlParser): SqlNode
+proc parseExpr(p: var SqlParser): SqlNode {.gcsafe.}
+proc parseSelect(p: var SqlParser): SqlNode {.gcsafe.}
 
 proc identOrLiteral(p: var SqlParser): SqlNode =
   case p.tok.kind
@@ -718,7 +700,10 @@ proc identOrLiteral(p: var SqlParser): SqlNode =
   of tkParLe:
     getTok(p)
     result = newNode(nkPrGroup)
-    result.add(parseExpr(p))
+    while true:
+      result.add(parseExpr(p))
+      if p.tok.kind != tkComma: break
+      getTok(p)
     eat(p, tkParRi)
   else:
     if p.tok.literal == "*":
@@ -726,10 +711,11 @@ proc identOrLiteral(p: var SqlParser): SqlNode =
       getTok(p)
     else:
       sqlError(p, "expression expected")
-      getTok(p) # we must consume a token here to prevend endless loops!
+      getTok(p) # we must consume a token here to prevent endless loops!
 
 proc primary(p: var SqlParser): SqlNode =
-  if (p.tok.kind == tkOperator and (p.tok.literal == "+" or p.tok.literal == "-")) or isKeyw(p, "not"):
+  if (p.tok.kind == tkOperator and (p.tok.literal == "+" or p.tok.literal ==
+      "-")) or isKeyw(p, "not"):
     result = newNode(nkPrefix)
     result.add(newNode(nkIdent, p.tok.literal))
     getTok(p)
@@ -1000,7 +986,7 @@ proc parseInsert(p: var SqlParser): SqlNode =
     parseParIdentList(p, n)
     result.add n
   else:
-    result.add(nil)
+    result.add(newNode(nkNone))
   if isKeyw(p, "default"):
     getTok(p)
     eat(p, "values")
@@ -1035,7 +1021,7 @@ proc parseUpdate(p: var SqlParser): SqlNode =
   if isKeyw(p, "where"):
     result.add(parseWhere(p))
   else:
-    result.add(nil)
+    result.add(newNode(nkNone))
 
 proc parseDelete(p: var SqlParser): SqlNode =
   getTok(p)
@@ -1047,7 +1033,7 @@ proc parseDelete(p: var SqlParser): SqlNode =
   if isKeyw(p, "where"):
     result.add(parseWhere(p))
   else:
-    result.add(nil)
+    result.add(newNode(nkNone))
 
 proc parseSelect(p: var SqlParser): SqlNode =
   getTok(p)
@@ -1141,6 +1127,11 @@ proc parseSelect(p: var SqlParser): SqlNode =
     var l = newNode(nkLimit)
     l.add(parseExpr(p))
     result.add(l)
+  if isKeyw(p, "offset"):
+    getTok(p)
+    var o = newNode(nkOffset)
+    o.add(parseExpr(p))
+    result.add(o)
 
 proc parseStmt(p: var SqlParser; parent: SqlNode) =
   if isKeyw(p, "create"):
@@ -1197,9 +1188,12 @@ type
 proc add(s: var SqlWriter, thing: char) =
   s.buffer.add(thing)
 
-proc add(s: var SqlWriter, thing: string) =
+proc prepareAdd(s: var SqlWriter) {.inline.} =
   if s.buffer.len > 0 and s.buffer[^1] notin {' ', '\L', '(', '.'}:
     s.buffer.add(" ")
+
+proc add(s: var SqlWriter, thing: string) =
+  s.prepareAdd
   s.buffer.add(thing)
 
 proc addKeyw(s: var SqlWriter, thing: string) =
@@ -1214,7 +1208,7 @@ proc addIden(s: var SqlWriter, thing: string) =
     iden = '"' & iden & '"'
   s.add(iden)
 
-proc ra(n: SqlNode, s: var SqlWriter)
+proc ra(n: SqlNode, s: var SqlWriter) {.gcsafe.}
 
 proc rs(n: SqlNode, s: var SqlWriter, prefix = "(", suffix = ")", sep = ", ") =
   if n.len > 0:
@@ -1241,6 +1235,17 @@ proc addMulti(s: var SqlWriter, n: SqlNode, sep = ',', prefix, suffix: char) =
 proc quoted(s: string): string =
   "\"" & replace(s, "\"", "\"\"") & "\""
 
+func escape(result: var string; s: string) =
+  result.add('\'')
+  for c in items(s):
+    case c
+    of '\0'..'\31':
+      result.add("\\x")
+      result.add(toHex(ord(c), 2))
+    of '\'': result.add("''")
+    else: result.add(c)
+  result.add('\'')
+
 proc ra(n: SqlNode, s: var SqlWriter) =
   if n == nil: return
   case n.kind
@@ -1253,7 +1258,8 @@ proc ra(n: SqlNode, s: var SqlWriter) =
   of nkQuotedIdent:
     s.add(quoted(n.strVal))
   of nkStringLit:
-    s.add(escape(n.strVal, "'", "'"))
+    s.prepareAdd
+    s.buffer.escape(n.strVal)
   of nkBitStringLit:
     s.add("b'" & n.strVal & "'")
   of nkHexStringLit:
@@ -1388,6 +1394,9 @@ proc ra(n: SqlNode, s: var SqlWriter) =
   of nkLimit:
     s.addKeyw("limit")
     s.addMulti(n)
+  of nkOffset:
+    s.addKeyw("offset")
+    s.addMulti(n)
   of nkHaving:
     s.addKeyw("having")
     s.addMulti(n)
@@ -1454,7 +1463,7 @@ proc ra(n: SqlNode, s: var SqlWriter) =
     s.addKeyw("enum")
     rs(n, s)
 
-proc renderSQL*(n: SqlNode, upperCase=false): string =
+proc renderSql*(n: SqlNode, upperCase = false): string =
   ## Converts an SQL abstract syntax tree to its string representation.
   var s: SqlWriter
   s.buffer = ""
@@ -1463,37 +1472,52 @@ proc renderSQL*(n: SqlNode, upperCase=false): string =
   return s.buffer
 
 proc `$`*(n: SqlNode): string =
-  ## an alias for `renderSQL`.
-  renderSQL(n)
-
-when not defined(js):
-  import streams
-
-  proc open(L: var SqlLexer, input: Stream, filename: string) =
-    lexbase.open(L, input)
-    L.filename = filename
-
-  proc open(p: var SqlParser, input: Stream, filename: string) =
-    ## opens the parser `p` and assigns the input stream `input` to it.
-    ## `filename` is only used for error messages.
-    open(SqlLexer(p), input, filename)
-    p.tok.kind = tkInvalid
-    p.tok.literal = ""
-    getTok(p)
+  ## an alias for `renderSql`.
+  renderSql(n)
 
-  proc parseSQL*(input: Stream, filename: string): SqlNode =
-    ## parses the SQL from `input` into an AST and returns the AST.
-    ## `filename` is only used for error messages.
-    ## Syntax errors raise an `SqlParseError` exception.
-    var p: SqlParser
-    open(p, input, filename)
-    try:
-      result = parse(p)
-    finally:
-      close(p)
-
-  proc parseSQL*(input: string, filename=""): SqlNode =
-    ## parses the SQL from `input` into an AST and returns the AST.
-    ## `filename` is only used for error messages.
-    ## Syntax errors raise an `SqlParseError` exception.
-    parseSQL(newStringStream(input), "")
+proc treeReprAux(s: SqlNode, level: int, result: var string) =
+  result.add('\n')
+  for i in 0 ..< level: result.add("  ")
+
+  result.add($s.kind)
+  if s.kind in LiteralNodes:
+    result.add(' ')
+    result.add(s.strVal)
+  else:
+    for son in s.sons:
+      treeReprAux(son, level + 1, result)
+
+proc treeRepr*(s: SqlNode): string =
+  result = newStringOfCap(128)
+  treeReprAux(s, 0, result)
+
+import std/streams
+
+proc open(L: var SqlLexer, input: Stream, filename: string) =
+  lexbase.open(L, input)
+  L.filename = filename
+
+proc open(p: var SqlParser, input: Stream, filename: string) =
+  ## opens the parser `p` and assigns the input stream `input` to it.
+  ## `filename` is only used for error messages.
+  open(SqlLexer(p), input, filename)
+  p.tok.kind = tkInvalid
+  p.tok.literal = ""
+  getTok(p)
+
+proc parseSql*(input: Stream, filename: string): SqlNode =
+  ## parses the SQL from `input` into an AST and returns the AST.
+  ## `filename` is only used for error messages.
+  ## Syntax errors raise an `SqlParseError` exception.
+  var p: SqlParser
+  open(p, input, filename)
+  try:
+    result = parse(p)
+  finally:
+    close(p)
+
+proc parseSql*(input: string, filename = ""): SqlNode =
+  ## parses the SQL from `input` into an AST and returns the AST.
+  ## `filename` is only used for error messages.
+  ## Syntax errors raise an `SqlParseError` exception.
+  parseSql(newStringStream(input), "")
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index e633d8cf7..2ca255fa0 100644
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -7,17 +7,50 @@
 #    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.
+##
+##   ```nim test
+##   let logs = @["2019-01-10: OK_", "2019-01-11: FAIL_", "2019-01: aaaa"]
+##   var outp: seq[string]
+##
+##   for log in logs:
+##     var res: string
+##     if parseUntil(log, res, ':') == 10: # YYYY-MM-DD == 10
+##       outp.add(res & " - " & captureBetween(log, ' ', '_'))
+##   doAssert outp == @["2019-01-10 - OK", "2019-01-11 - FAIL"]
+##   ```
+##
+##   ```nim test
+##   from std/strutils import Digits, parseInt
+##
+##   let
+##     input1 = "2019 school start"
+##     input2 = "3 years back"
+##     startYear = input1[0 .. skipWhile(input1, Digits)-1] # 2019
+##     yearsBack = input2[0 .. skipWhile(input2, Digits)-1] # 3
+##     examYear = parseInt(startYear) + parseInt(yearsBack)
+##   doAssert "Examination is in " & $examYear == "Examination is in 2022"
+##   ```
+##
+## **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
-
-{.push debugger:off .} # the user does not want to trace a part
+{.push debugger: off.} # the user does not want to trace a part
                        # of the standard library!
 
 include "system/inclrtl"
 
+template toOa(s: string): openArray[char] = openArray[char](s)
+
 const
   Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
   IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
@@ -27,227 +60,373 @@ const
 proc toLower(c: char): char {.inline.} =
   result = if c in {'A'..'Z'}: chr(ord(c)-ord('A')+ord('a')) else: c
 
-proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {.
-  rtl, extern: "npuParseHex", noSideEffect.}  =
-  ## Parses a hexadecimal number and stores its value in ``number``.
+proc parseBin*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): int {.noSideEffect.} =
+  ## Parses a binary number and stores its value in ``number``.
   ##
-  ## Returns the number of the parsed characters or 0 in case of an error. This
-  ## 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:
+  ## Returns the number of the parsed characters or 0 in case of an error.
+  ## If error, the value of ``number`` is not changed.
   ##
-  ## .. 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
+  ## If ``maxLen == 0``, the parsing continues until the first non-bin character
+  ## or to the end of the string. Otherwise, no more than ``maxLen`` characters
+  ## are parsed starting from the ``start`` position.
   ##
-  ## 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.
-  var i = start
+  ## It does not check for overflow. If the value represented by the string is
+  ## too big to fit into ``number``, only the value of last fitting characters
+  ## will be stored in ``number`` without producing an error.
+  runnableExamples:
+    var num: int
+    doAssert parseBin("0100_1110_0110_1001_1110_1101", num) == 29
+    doAssert num == 5138925
+    doAssert parseBin("3", num) == 0
+    var num8: int8
+    doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8) == 32
+    doAssert num8 == 0b1110_1101'i8
+    doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8, 3, 9) == 9
+    doAssert num8 == 0b0100_1110'i8
+    var num8u: uint8
+    doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8u) == 32
+    doAssert num8u == 237
+    var num64: int64
+    doAssert parseBin("0100111001101001111011010100111001101001", num64) == 40
+    doAssert num64 == 336784608873
+  var i = 0
+  var output = T(0)
   var foundDigit = false
-  # get last index based on minimum `start + maxLen` or `s.len`
-  let last = min(s.len, if maxLen == 0: s.len else: i+maxLen)
-  if i+1 < last and s[i] == '0' and (s[i+1] in {'x', 'X'}): inc(i, 2)
-  elif i < last and s[i] == '#': inc(i)
+  let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
+  if i + 1 < last and s[i] == '0' and (s[i+1] in {'b', 'B'}): inc(i, 2)
   while i < last:
     case s[i]
     of '_': discard
-    of '0'..'9':
-      number = number shl 4 or (ord(s[i]) - ord('0'))
-      foundDigit = true
-    of 'a'..'f':
-      number = number shl 4 or (ord(s[i]) - ord('a') + 10)
-      foundDigit = true
-    of 'A'..'F':
-      number = number shl 4 or (ord(s[i]) - ord('A') + 10)
+    of '0'..'1':
+      output = output shl 1 or T(ord(s[i]) - ord('0'))
       foundDigit = true
     else: break
     inc(i)
-  if foundDigit: result = i-start
+  if foundDigit:
+    number = output
+    result = i
 
-proc parseOct*(s: string, number: var int, start = 0, maxLen = 0): int  {.
-  rtl, extern: "npuParseOct", noSideEffect.} =
-  ## Parses an octal number and stores its value in ``number``. Returns
-  ## the number of the parsed characters or 0 in case of an error.
+proc parseOct*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): int {.noSideEffect.} =
+  ## Parses an octal number and stores its value in ``number``.
   ##
-  ## 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.
-  var i = start
+  ## Returns the number of the parsed characters or 0 in case of an error.
+  ## If error, the value of ``number`` is not changed.
+  ##
+  ## If ``maxLen == 0``, the parsing continues until the first non-oct character
+  ## or to the end of the string. Otherwise, no more than ``maxLen`` characters
+  ## are parsed starting from the ``start`` position.
+  ##
+  ## It does not check for overflow. If the value represented by the string is
+  ## too big to fit into ``number``, only the value of last fitting characters
+  ## will be stored in ``number`` without producing an error.
+  runnableExamples:
+    var num: int
+    doAssert parseOct("0o23464755", num) == 10
+    doAssert num == 5138925
+    doAssert parseOct("8", num) == 0
+    var num8: int8
+    doAssert parseOct("0o_1464_755", num8) == 11
+    doAssert num8 == -19
+    doAssert parseOct("0o_1464_755", num8, 3, 3) == 3
+    doAssert num8 == 102
+    var num8u: uint8
+    doAssert parseOct("1464755", num8u) == 7
+    doAssert num8u == 237
+    var num64: int64
+    doAssert parseOct("2346475523464755", num64) == 16
+    doAssert num64 == 86216859871725
+  var i = 0
+  var output = T(0)
   var foundDigit = false
-  # get last index based on minimum `start + maxLen` or `s.len`
-  let last = min(s.len, if maxLen == 0: s.len else: i+maxLen)
-  if i+1 < last and s[i] == '0' and (s[i+1] in {'o', 'O'}): inc(i, 2)
+  let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
+  if i + 1 < last and s[i] == '0' and (s[i+1] in {'o', 'O'}): inc(i, 2)
   while i < last:
     case s[i]
     of '_': discard
     of '0'..'7':
-      number = number shl 3 or (ord(s[i]) - ord('0'))
+      output = output shl 3 or T(ord(s[i]) - ord('0'))
       foundDigit = true
     else: break
     inc(i)
-  if foundDigit: result = i-start
+  if foundDigit:
+    number = output
+    result = i
 
-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.
+proc parseHex*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): int {.noSideEffect.} =
+  ## Parses a hexadecimal number and stores its value in ``number``.
   ##
-  ## 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.
-  var i = start
+  ## Returns the number of the parsed characters or 0 in case of an error.
+  ## If error, the value of ``number`` is not changed.
+  ##
+  ## If ``maxLen == 0``, the parsing continues until the first non-hex character
+  ## or to the end of the string. Otherwise, no more than ``maxLen`` characters
+  ## are parsed starting from the ``start`` position.
+  ##
+  ## It does not check for overflow. If the value represented by the string is
+  ## too big to fit into ``number``, only the value of last fitting characters
+  ## will be stored in ``number`` without producing an error.
+  runnableExamples:
+    var num: int
+    doAssert parseHex("4E_69_ED", num) == 8
+    doAssert num == 5138925
+    doAssert parseHex("X", num) == 0
+    doAssert parseHex("#ABC", num) == 4
+    var num8: int8
+    doAssert parseHex("0x_4E_69_ED", num8) == 11
+    doAssert num8 == 0xED'i8
+    doAssert parseHex("0x_4E_69_ED", num8, 3, 2) == 2
+    doAssert num8 == 0x4E'i8
+    var num8u: uint8
+    doAssert parseHex("0x_4E_69_ED", num8u) == 11
+    doAssert num8u == 237
+    var num64: int64
+    doAssert parseHex("4E69ED4E69ED", num64) == 12
+    doAssert num64 == 86216859871725
+  var i = 0
+  var output = T(0)
   var foundDigit = false
-  # get last index based on minimum `start + maxLen` or `s.len`
-  let last = min(s.len, if maxLen == 0: s.len else: i+maxLen)
-  if i+1 < last and s[i] == '0' and (s[i+1] in {'b', 'B'}): inc(i, 2)
+  let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
+  if i + 1 < last and s[i] == '0' and (s[i+1] in {'x', 'X'}): inc(i, 2)
+  elif i < last and s[i] == '#': inc(i)
   while i < last:
     case s[i]
     of '_': discard
-    of '0'..'1':
-      number = number shl 1 or (ord(s[i]) - ord('0'))
+    of '0'..'9':
+      output = output shl 4 or T(ord(s[i]) - ord('0'))
+      foundDigit = true
+    of 'a'..'f':
+      output = output shl 4 or T(ord(s[i]) - ord('a') + 10)
+      foundDigit = true
+    of 'A'..'F':
+      output = output shl 4 or T(ord(s[i]) - ord('A') + 10)
       foundDigit = true
     else: break
     inc(i)
-  if foundDigit: result = i-start
+  if foundDigit:
+    number = output
+    result = i
 
-proc parseIdent*(s: string, ident: var string, start = 0): int =
-  ## parses an identifier and stores it in ``ident``. Returns
+proc parseIdent*(s: openArray[char], ident: var string): int =
+  ## Parses an identifier and stores it in ``ident``. Returns
   ## the number of the parsed characters or 0 in case of an error.
-  var i = start
+  ## If error, the value of `ident` is not changed.
+  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 = 0
   if i < s.len and s[i] in IdentStartChars:
     inc(i)
     while i < s.len and s[i] in IdentChars: inc(i)
-    ident = substr(s, start, i-1)
-    result = i-start
+    ident = substr(s.toOpenArray(0, i-1))
+    result = i
 
-proc parseIdent*(s: string, start = 0): string =
-  ## parses an identifier and stores it in ``ident``.
-  ## Returns the parsed identifier or an empty string in case of an error.
+proc parseIdent*(s: openArray[char]): string =
+  ## 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
+  var i = 0
   if i < s.len and s[i] in IdentStartChars:
     inc(i)
     while i < s.len and s[i] in IdentChars: inc(i)
-    result = substr(s, start, i-1)
+    result = substr(s.toOpenArray(0, 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 parseChar*(s: openArray[char], c: var char): int =
+  ## Parses a single character, stores it in `c` and returns 1.
+  ## In case of error (if start >= s.len) it returns 0
+  ## and the value of `c` is unchanged.
+  runnableExamples:
+    var c: char
+    doAssert "nim".parseChar(c, 3) == 0
+    doAssert c == '\0'
+    doAssert "nim".parseChar(c, 0) == 1
+    doAssert c == 'n'
+  if s.len > 0:
+    c = s[0]
+    result = 1
 
-proc skipWhitespace*(s: string, start = 0): int {.inline.} =
-  ## skips the whitespace starting at ``s[start]``. Returns the number of
+proc skipWhitespace*(s: openArray[char]): int {.inline.} =
+  ## Skips the whitespace starting at ``s[start]``. Returns the number of
   ## skipped characters.
-  while start+result < s.len and s[start+result] in Whitespace: inc(result)
+  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
+  result = 0
+  while result < s.len and s[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`
+proc skip*(s, token: openArray[char]): int {.inline.} =
+  ## Skips the `token` starting at ``s[start]``. Returns the length of `token`
   ## or 0 if there was no `token` at ``s[start]``.
-  while start+result < s.len and result < token.len and
-      s[result+start] == token[result]:
+  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
+  result = 0
+  while result < s.len and result < token.len and
+      s[result] == 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.
-  while start+result < s.len and result < token.len and
-      toLower(s[result+start]) == toLower(token[result]): inc(result)
+proc skipIgnoreCase*(s, token: openArray[char]): int =
+  ## Same as `skip` but case is ignored for token matching.
+  runnableExamples:
+    doAssert skipIgnoreCase("CAPlow", "CAP", 0) == 3
+    doAssert skipIgnoreCase("CAPlow", "cap", 0) == 3
+  result = 0
+  while result < s.len and result < token.len and
+      toLower(s[result]) == toLower(token[result]): inc(result)
   if result != token.len: result = 0
 
-proc skipUntil*(s: string, until: set[char], start = 0): int {.inline.} =
+proc skipUntil*(s: openArray[char], until: set[char]): int {.inline.} =
   ## Skips all characters until one char from the set `until` is found
   ## or the end is reached.
   ## Returns number of characters skipped.
-  while start+result < s.len and s[result+start] notin until: inc(result)
+  runnableExamples:
+    doAssert skipUntil("Hello World", {'W', 'e'}, 0) == 1
+    doAssert skipUntil("Hello World", {'W'}, 0) == 6
+    doAssert skipUntil("Hello World", {'W', 'd'}, 0) == 6
+  result = 0
+  while result < s.len and s[result] notin until: inc(result)
 
-proc skipUntil*(s: string, until: char, start = 0): int {.inline.} =
+proc skipUntil*(s: openArray[char], until: char): int {.inline.} =
   ## Skips all characters until the char `until` is found
   ## or the end is reached.
   ## Returns number of characters skipped.
-  while start+result < s.len and s[result+start] != until: inc(result)
+  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
+  result = 0
+  while result < s.len and s[result] != 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.
+proc skipWhile*(s: openArray[char], toSkip: set[char]): int {.inline.} =
+  ## Skips all characters while one char from the set `toSkip` is found.
   ## Returns number of characters skipped.
-  while start+result < s.len and s[result+start] in toSkip: inc(result)
+  runnableExamples:
+    doAssert skipWhile("Hello World", {'H', 'e'}) == 2
+    doAssert skipWhile("Hello World", {'e'}) == 0
+    doAssert skipWhile("Hello World", {'W', 'o', 'r'}, 6) == 3
+  result = 0
+  while result < s.len and s[result] 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
+proc fastSubstr(s: openArray[char]; token: var string; length: int) =
+  token.setLen length
+  for i in 0 ..< length: token[i] = s[i]
+
+proc parseUntil*(s: openArray[char], token: var string, until: set[char]): int {.inline.} =
+  ## 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`.
-  var i = start
+  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 = 0
   while i < s.len and s[i] notin until: inc(i)
-  result = i-start
-  token = substr(s, start, i-1)
+  result = i
+  fastSubstr(s, token, result)
+  #token = substr(s, start, i-1)
 
-proc parseUntil*(s: string, token: var string, until: char,
-                 start = 0): int {.inline.} =
-  ## parses a token and stores it in ``token``. Returns
+proc parseUntil*(s: openArray[char], token: var string, until: char): int {.inline.} =
+  ## 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.
-  var i = start
+  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 = 0
   while i < s.len and s[i] != until: inc(i)
-  result = i-start
-  token = substr(s, start, i-1)
+  result = i
+  fastSubstr(s, token, result)
+  #token = substr(s, start, i-1)
 
-proc parseUntil*(s: string, token: var string, until: string,
-                 start = 0): int {.inline.} =
-  ## parses a token and stores it in ``token``. Returns
+proc parseUntil*(s: openArray[char], token: var string, until: string): int {.inline.} =
+  ## 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.
-  if until.len == 0:
-    token.setLen(0)
-    return 0
-  var i = start
+  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 "
+  when (NimMajor, NimMinor) <= (1, 0):
+    if until.len == 0:
+      token.setLen(0)
+      return 0
+  var i = 0
   while i < s.len:
-    if s[i] == until[0]:
+    if until.len > 0 and s[i] == until[0]:
       var u = 1
       while i+u < s.len and u < until.len and s[i+u] == until[u]:
         inc u
       if u >= until.len: break
     inc(i)
-  result = i-start
-  token = substr(s, start, i-1)
+  result = i
+  fastSubstr(s, token, result)
+  #token = substr(s, start, i-1)
 
-proc parseWhile*(s: string, token: var string, validChars: set[char],
-                 start = 0): int {.inline.} =
-  ## parses a token and stores it in ``token``. Returns
+proc parseWhile*(s: openArray[char], token: var string, validChars: set[char]): int {.inline.} =
+  ## 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`.
-  var i = start
+  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 = 0
   while i < s.len and s[i] in validChars: inc(i)
-  result = i-start
-  token = substr(s, start, i-1)
+  result = i
+  fastSubstr(s, token, result)
+  #token = substr(s, start, i-1)
 
-proc captureBetween*(s: string, first: char, second = '\0', start = 0): string =
+proc captureBetween*(s: openArray[char], first: char, second = '\0'): string =
   ## Finds the first occurrence of ``first``, then returns everything from there
   ## up to ``second`` (if ``second`` is '\0', then ``first`` is used).
-  var i = skipUntil(s, first, start)+1+start
+  runnableExamples:
+    doAssert captureBetween("Hello World", 'e') == "llo World"
+    doAssert captureBetween("Hello World", 'e', 'r') == "llo Wo"
+    doAssert captureBetween("Hello World".toOpenArray(6, "Hello World".high), 'l') == "d"
+  var i = skipUntil(s, first) + 1
   result = ""
-  discard s.parseUntil(result, if second == '\0': first else: second, i)
+  discard parseUntil(s.toOpenArray(i, s.high), result, if second == '\0': first else: second)
+
+proc integerOutOfRangeError() {.noinline.} =
+  raise newException(ValueError, "Parsed integer outside of valid range")
 
-{.push overflowChecks: on.}
-# this must be compiled with overflow checking turned on:
-proc rawParseInt(s: string, b: var BiggestInt, start = 0): int =
+# See #6752
+when defined(js):
+  {.push overflowChecks: off.}
+
+proc rawParseInt(s: openArray[char], b: var BiggestInt): int =
   var
     sign: BiggestInt = -1
-    i = start
+    i = 0
   if i < s.len:
     if s[i] == '+': inc(i)
     elif s[i] == '-':
@@ -256,49 +435,68 @@ 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
+
+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`.
+proc parseBiggestInt*(s: openArray[char], number: var BiggestInt): int {.
+  rtl, extern: "npuParseBiggestInt", noSideEffect, raises: [ValueError].} =
+  ## Parses an integer and stores the value into `number`.
   ## Result is the number of processed chars or 0 if there is no integer.
-  ## `EOverflow` is raised if an overflow occurs.
-  var res: BiggestInt
+  ## `ValueError` is raised if the parsed integer is out of the valid range.
+  runnableExamples:
+    var res: BiggestInt
+    doAssert parseBiggestInt("9223372036854775807", res) == 19
+    doAssert res == 9223372036854775807
+    doAssert parseBiggestInt("-2024_05_09", res) == 11
+    doAssert res == -20240509
+  var res = BiggestInt(0)
   # use 'res' for exception safety (don't write to 'number' in case of an
   # overflow exception):
-  result = rawParseInt(s, res, start)
-  number = res
+  result = rawParseInt(s, 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`.
+proc parseInt*(s: openArray[char], number: var int): int {.
+  rtl, extern: "npuParseInt", noSideEffect, raises: [ValueError].} =
+  ## Parses an integer and stores the value into `number`.
   ## Result is the number of processed chars or 0 if there is no integer.
-  ## `EOverflow` is raised if an overflow occurs.
-  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:
+  ## `ValueError` is raised if the parsed integer is out of the valid range.
+  runnableExamples:
+    var res: int
+    doAssert parseInt("-2024_05_02", res) == 11
+    doAssert res == -20240502
+  var res = BiggestInt(0)
+  result = parseBiggestInt(s, res)
+  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
-  ## error. Instead of an ``Overflow`` exception ``high(int)`` is returned.
+proc parseSaturatedNatural*(s: openArray[char], b: var int): 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
-  var i = start
+  runnableExamples:
+    var res = 0
+    discard parseSaturatedNatural("848", res)
+    doAssert res == 848
+  var i = 0
   if i < s.len and s[i] == '+': inc(i)
   if i < s.len and s[i] in {'0'..'9'}:
     b = 0
@@ -310,14 +508,15 @@ proc parseSaturatedNatural*(s: string, b: var int, start = 0): int =
         b = high(int)
       inc(i)
       while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
-    result = i - start
+    result = i
 
-# overflowChecks doesn't work with BiggestUInt
-proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
+proc rawParseUInt(s: openArray[char], b: var BiggestUInt): int =
   var
     res = 0.BiggestUInt
     prev = 0.BiggestUInt
-    i = start
+    i = 0
+  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,82 +524,162 @@ 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
+    result = i
 
-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
+proc parseBiggestUInt*(s: openArray[char], number: var BiggestUInt): int {.
+  rtl, extern: "npuParseBiggestUInt", noSideEffect, raises: [ValueError].} =
+  ## Parses an unsigned integer and stores the value
   ## into `number`.
-  ## Result is the number of processed chars or 0 if there is no integer
-  ## or overflow detected.
-  var res: BiggestUInt
+  ## `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(0)
   # use 'res' for exception safety (don't write to 'number' in case of an
   # overflow exception):
-  result = rawParseUInt(s, res, start)
-  number = res
+  result = rawParseUInt(s, 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
+proc parseUInt*(s: openArray[char], number: var uint): int {.
+  rtl, extern: "npuParseUInt", noSideEffect, raises: [ValueError].} =
+  ## Parses an unsigned integer and stores the value
   ## into `number`.
-  ## Result is the number of processed chars or 0 if there is no integer or
-  ## overflow detected.
-  var res: BiggestUInt
-  result = parseBiggestUInt(s, res, start)
+  ## `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(0)
+  result = parseBiggestUInt(s, res)
   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 {.
+proc parseBiggestFloat*(s: openArray[char], number: var BiggestFloat): int {.
   magic: "ParseBiggestFloat", importc: "nimParseBiggestFloat", noSideEffect.}
-  ## parses a float starting at `start` and stores the value into `number`.
+  ## Parses a float 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 {.
+proc parseFloat*(s: openArray[char], number: var float): int {.
   rtl, extern: "npuParseFloat", noSideEffect.} =
-  ## parses a float starting at `start` and stores the value into `number`.
+  ## Parses a float and stores the value into `number`.
   ## Result is the number of processed chars or 0 if there occurred a parsing
   ## error.
-  var bf: BiggestFloat
-  result = parseBiggestFloat(s, bf, start)
+  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(0.0)
+  result = parseBiggestFloat(s, bf)
   if result != 0:
     number = bf
 
-type
-  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
-    ikDollar,                ## escaped ``$`` part of the interpolated string
-    ikVar,                   ## ``var`` part of the interpolated string
-    ikExpr                   ## ``expr`` part of the interpolated string
+func toLowerAscii(c: char): char =
+  if c in {'A'..'Z'}: char(uint8(c) xor 0b0010_0000'u8) else: c
 
-iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
-  value: string] =
-  ## Tokenizes the string `s` into substrings for interpolation purposes.
+func parseSize*(s: openArray[char], size: var int64, alwaysBin=false): int =
+  ## Parse a size qualified by binary or metric units into `size`.  This format
+  ## is often called "human readable".  Result is the number of processed chars
+  ## or 0 on parse errors and size is rounded to the nearest integer.  Trailing
+  ## garbage like "/s" in "1k/s" is allowed and detected by `result < s.len`.
   ##
-  ## Example:
+  ## To simplify use, following non-rare wild conventions, and since fractional
+  ## data like milli-bytes is so rare, unit matching is case-insensitive but for
+  ## the 'i' distinguishing binary-metric from metric (which cannot be 'I').
   ##
-  ## .. code-block:: nim
-  ##   for k, v in interpolatedFragments("  $this is ${an  example}  $$"):
-  ##     echo "(", k, ", \"", v, "\")"
+  ## An optional trailing 'B|b' is ignored but processed.  I.e., you must still
+  ## know if units are bytes | bits or infer this fact via the case of s[^1] (if
+  ## users can even be relied upon to use 'B' for byte and 'b' for bit or have
+  ## that be s[^1]).
   ##
-  ## Results in:
+  ## If `alwaysBin==true` then scales are always binary-metric, but e.g. "KiB"
+  ## is still accepted for clarity.  If the value would exceed the range of
+  ## `int64`, `size` saturates to `int64.high`.  Supported metric prefix chars
+  ## include k, m, g, t, p, e, z, y (but z & y saturate unless the number is a
+  ## small fraction).
   ##
-  ## .. code-block:: nim
-  ##   (ikString, "  ")
-  ##   (ikExpr, "this")
-  ##   (ikString, " is ")
-  ##   (ikExpr, "an  example")
-  ##   (ikString, "  ")
-  ##   (ikDollar, "$")
+  ## **See also:**
+  ## * https://en.wikipedia.org/wiki/Binary_prefix
+  ## * `formatSize module<strutils.html>`_ for formatting
+  runnableExamples:
+    var res: int64  # caller must still know if 'b' refers to bytes|bits
+    doAssert parseSize("10.5 MB", res) == 7
+    doAssert res == 10_500_000  # decimal metric Mega prefix
+    doAssert parseSize("64 mib", res) == 6
+    doAssert res == 67108864    # 64 shl 20
+    doAssert parseSize("1G/h", res, true) == 2 # '/' stops parse
+    doAssert res == 1073741824  # 1 shl 30, forced binary metric
+  const prefix = "b" & "kmgtpezy"       # byte|bit & lowCase metric-ish prefixes
+  const scaleM = [1.0, 1e3, 1e6, 1e9, 1e12, 1e15, 1e18, 1e21, 1e24] # 10^(3*idx)
+  const scaleB = [1.0, 1024, 1048576, 1073741824, 1099511627776.0,  # 2^(10*idx)
+                  1125899906842624.0, 1152921504606846976.0,        # ldexp?
+                  1.180591620717411303424e21, 1.208925819614629174706176e24]
+  var number: float
+  var scale = 1.0
+  result = parseFloat(s, number)
+  if number < 0:                        # While parseFloat accepts negatives ..
+    result = 0                          #.. we do not since sizes cannot be < 0
+  if result > 0:
+    let start = result                  # Save spot to maybe unwind white to EOS
+    while result < s.len and s[result] in Whitespace:
+      inc result
+    if result < s.len:                  # Illegal starting char => unity
+      if (let si = prefix.find(s[result].toLowerAscii); si >= 0):
+        inc result                      # Now parse the scale
+        scale = if alwaysBin: scaleB[si] else: scaleM[si]
+        if result < s.len and s[result] == 'i':
+          scale = scaleB[si]            # Switch from default to binary-metric
+          inc result
+        if result < s.len and s[result].toLowerAscii == 'b':
+          inc result                    # Skip optional '[bB]'
+    else:                               # Unwind result advancement when there..
+      result = start                    #..is no unit to the end of `s`.
+    var sizeF = number * scale + 0.5    # Saturate to int64.high when too big
+    size = if sizeF > 9223372036854774784.0: int64.high else: sizeF.int64
+# Above constant=2^63-1024 avoids C UB; github.com/nim-lang/Nim/issues/20102 or
+# stackoverflow.com/questions/20923556/math-pow2-63-1-math-pow2-63-512-is-true
+
+type
+  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
+    ikDollar,              ## escaped ``$`` part of the interpolated string
+    ikVar,                 ## ``var`` part of the interpolated string
+    ikExpr                 ## ``expr`` part of the interpolated string
+
+iterator interpolatedFragments*(s: openArray[char]): tuple[kind: InterpolatedKind,
+  value: string] =
+  ## Tokenizes the string `s` into substrings for interpolation purposes.
+  ##
+  runnableExamples:
+    var outp: seq[tuple[kind: InterpolatedKind, value: string]]
+    for k, v in interpolatedFragments("  $this is ${an  example}  $$"):
+      outp.add (k, v)
+    doAssert outp == @[(ikStr, "  "),
+                       (ikVar, "this"),
+                       (ikStr, " is "),
+                       (ikExpr, "an  example"),
+                       (ikStr, "  "),
+                       (ikDollar, "$")]
+
   var i = 0
   var kind: InterpolatedKind
   while true:
@@ -421,7 +700,7 @@ iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
             else: discard
             inc j
           raise newException(ValueError,
-            "Expected closing '}': " & substr(s, i, s.high))
+            "Expected closing '}': " & substr(s.toOpenArray(i, s.high)))
         inc i, 2 # skip ${
         kind = ikExpr
       elif j+1 < s.len and s[j+1] in IdentStartChars:
@@ -435,59 +714,374 @@ iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
         kind = ikDollar
       else:
         raise newException(ValueError,
-          "Unable to parse a varible name at " & substr(s, i, s.high))
+          "Unable to parse a variable name at " & substr(s.toOpenArray(i, s.high)))
     else:
       while j < s.len and s[j] != '$': inc j
       kind = ikStr
     if j > i:
       # do not copy the trailing } for ikExpr:
-      yield (kind, substr(s, i, j-1-ord(kind == ikExpr)))
+      yield (kind, substr(s.toOpenArray(i, j-1-ord(kind == ikExpr))))
     else:
       break
     i = j
 
-when isMainModule:
-  import sequtils
-  let input = "$test{}  $this is ${an{  example}}  "
-  let expected = @[(ikVar, "test"), (ikStr, "{}  "), (ikVar, "this"),
-                   (ikStr, " is "), (ikExpr, "an{  example}"), (ikStr, "  ")]
-  doAssert toSeq(interpolatedFragments(input)) == expected
-
-  var value = 0
-  discard parseHex("0x38", value)
-  doAssert value == 56
-  discard parseHex("0x34", value)
-  doAssert value == 56 * 256 + 52
-  value = -1
-  discard parseHex("0x38", value)
-  doAssert value == -200
-
-  value = -1
-  doAssert(parseSaturatedNatural("848", value) == 3)
-  doAssert value == 848
-
-  value = -1
-  discard parseSaturatedNatural("84899999999999999999324234243143142342135435342532453", value)
-  doAssert value == high(int)
-
-  value = -1
-  discard parseSaturatedNatural("9223372036854775808", value)
-  doAssert value == high(int)
-
-  value = -1
-  discard parseSaturatedNatural("9223372036854775807", value)
-  doAssert value == high(int)
-
-  value = -1
-  discard parseSaturatedNatural("18446744073709551616", value)
-  doAssert value == high(int)
-
-  value = -1
-  discard parseSaturatedNatural("18446744073709551615", value)
-  doAssert value == high(int)
-
-  value = -1
-  doAssert(parseSaturatedNatural("1_000_000", value) == 9)
-  doAssert value == 1_000_000
-
 {.pop.}
+
+
+proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0,
+    maxLen = 0): int {.noSideEffect.} =
+  ## Parses a binary number and stores its value in ``number``.
+  ##
+  ## Returns the number of the parsed characters or 0 in case of an error.
+  ## If error, the value of ``number`` is not changed.
+  ##
+  ## If ``maxLen == 0``, the parsing continues until the first non-bin character
+  ## or to the end of the string. Otherwise, no more than ``maxLen`` characters
+  ## are parsed starting from the ``start`` position.
+  ##
+  ## It does not check for overflow. If the value represented by the string is
+  ## too big to fit into ``number``, only the value of last fitting characters
+  ## will be stored in ``number`` without producing an error.
+  runnableExamples:
+    var num: int
+    doAssert parseBin("0100_1110_0110_1001_1110_1101", num) == 29
+    doAssert num == 5138925
+    doAssert parseBin("3", num) == 0
+    var num8: int8
+    doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8) == 32
+    doAssert num8 == 0b1110_1101'i8
+    doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8, 3, 9) == 9
+    doAssert num8 == 0b0100_1110'i8
+    var num8u: uint8
+    doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8u) == 32
+    doAssert num8u == 237
+    var num64: int64
+    doAssert parseBin("0100111001101001111011010100111001101001", num64) == 40
+    doAssert num64 == 336784608873
+  parseBin(s.toOpenArray(start, s.high), number, maxLen)
+
+proc parseOct*[T: SomeInteger](s: string, number: var T, start = 0,
+    maxLen = 0): int {.noSideEffect.} =
+  ## Parses an octal number and stores its value in ``number``.
+  ##
+  ## Returns the number of the parsed characters or 0 in case of an error.
+  ## If error, the value of ``number`` is not changed.
+  ##
+  ## If ``maxLen == 0``, the parsing continues until the first non-oct character
+  ## or to the end of the string. Otherwise, no more than ``maxLen`` characters
+  ## are parsed starting from the ``start`` position.
+  ##
+  ## It does not check for overflow. If the value represented by the string is
+  ## too big to fit into ``number``, only the value of last fitting characters
+  ## will be stored in ``number`` without producing an error.
+  runnableExamples:
+    var num: int
+    doAssert parseOct("0o23464755", num) == 10
+    doAssert num == 5138925
+    doAssert parseOct("8", num) == 0
+    var num8: int8
+    doAssert parseOct("0o_1464_755", num8) == 11
+    doAssert num8 == -19
+    doAssert parseOct("0o_1464_755", num8, 3, 3) == 3
+    doAssert num8 == 102
+    var num8u: uint8
+    doAssert parseOct("1464755", num8u) == 7
+    doAssert num8u == 237
+    var num64: int64
+    doAssert parseOct("2346475523464755", num64) == 16
+    doAssert num64 == 86216859871725
+  parseOct(s.toOpenArray(start, s.high), number, maxLen)
+
+proc parseHex*[T: SomeInteger](s: string, number: var T, start = 0,
+    maxLen = 0): int {.noSideEffect.} =
+  ## Parses a hexadecimal number and stores its value in ``number``.
+  ##
+  ## Returns the number of the parsed characters or 0 in case of an error.
+  ## If error, the value of ``number`` is not changed.
+  ##
+  ## If ``maxLen == 0``, the parsing continues until the first non-hex character
+  ## or to the end of the string. Otherwise, no more than ``maxLen`` characters
+  ## are parsed starting from the ``start`` position.
+  ##
+  ## It does not check for overflow. If the value represented by the string is
+  ## too big to fit into ``number``, only the value of last fitting characters
+  ## will be stored in ``number`` without producing an error.
+  runnableExamples:
+    var num: int
+    doAssert parseHex("4E_69_ED", num) == 8
+    doAssert num == 5138925
+    doAssert parseHex("X", num) == 0
+    doAssert parseHex("#ABC", num) == 4
+    var num8: int8
+    doAssert parseHex("0x_4E_69_ED", num8) == 11
+    doAssert num8 == 0xED'i8
+    doAssert parseHex("0x_4E_69_ED", num8, 3, 2) == 2
+    doAssert num8 == 0x4E'i8
+    var num8u: uint8
+    doAssert parseHex("0x_4E_69_ED", num8u) == 11
+    doAssert num8u == 237
+    var num64: int64
+    doAssert parseHex("4E69ED4E69ED", num64) == 12
+    doAssert num64 == 86216859871725
+  parseHex(s.toOpenArray(start, s.high), number, maxLen)
+
+proc parseIdent*(s: string, ident: var string, start = 0): int =
+  ## Parses an identifier and stores it in ``ident``. Returns
+  ## the number of the parsed characters or 0 in case of an error.
+  ## If error, the value of `ident` is not changed.
+  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"
+  parseIdent(s.toOpenArray(start, s.high), ident)
+
+proc parseIdent*(s: string, start = 0): string =
+  ## 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"
+  parseIdent(s.toOpenArray(start, s.high))
+
+proc parseChar*(s: string, c: var char, start = 0): int =
+  ## Parses a single character, stores it in `c` and returns 1.
+  ## In case of error (if start >= s.len) it returns 0
+  ## and the value of `c` is unchanged.
+  runnableExamples:
+    var c: char
+    doAssert "nim".parseChar(c, 3) == 0
+    doAssert c == '\0'
+    doAssert "nim".parseChar(c, 0) == 1
+    doAssert c == 'n'
+  parseChar(s.toOpenArray(start, s.high), c)
+
+proc skipWhitespace*(s: string, start = 0): int {.inline.} =
+  ## 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
+  skipWhitespace(s.toOpenArray(start, s.high))
+
+proc skip*(s, token: string, start = 0): int {.inline.} =
+  ## 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
+  skip(s.toOpenArray(start, s.high), token)
+
+proc skipIgnoreCase*(s, token: string, start = 0): int =
+  ## Same as `skip` but case is ignored for token matching.
+  runnableExamples:
+    doAssert skipIgnoreCase("CAPlow", "CAP", 0) == 3
+    doAssert skipIgnoreCase("CAPlow", "cap", 0) == 3
+  skipIgnoreCase(s.toOpenArray(start, s.high), token)
+
+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
+  skipUntil(s.toOpenArray(start, s.high), until)
+
+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
+  skipUntil(s.toOpenArray(start, s.high), until)
+
+proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} =
+  ## Skips all characters while one char from the set `toSkip` 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
+  skipWhile(s.toOpenArray(start, s.high), toSkip)
+
+proc parseUntil*(s: string, token: var string, until: set[char],
+                 start = 0): int {.inline.} =
+  ## 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 "
+  parseUntil(s.toOpenArray(start, s.high), token, until)
+
+proc parseUntil*(s: string, token: var string, until: char,
+                 start = 0): int {.inline.} =
+  ## 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"
+  parseUntil(s.toOpenArray(start, s.high), token, until)
+
+proc parseUntil*(s: string, token: var string, until: string,
+                 start = 0): int {.inline.} =
+  ## 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 "
+  parseUntil(s.toOpenArray(start, s.high), token, until)
+
+proc parseWhile*(s: string, token: var string, validChars: set[char],
+                 start = 0): int {.inline.} =
+  ## 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"
+  parseWhile(s.toOpenArray(start, s.high), token, validChars)
+
+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"
+  captureBetween(s.toOpenArray(start, s.high), first, second)
+
+proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {.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.
+  ## `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
+    doAssert parseBiggestInt("-2024_05_09", res) == 11
+    doAssert res == -20240509
+    doAssert parseBiggestInt("-2024_05_02", res, 7) == 4
+    doAssert res == 502
+  parseBiggestInt(s.toOpenArray(start, s.high), number)
+
+proc parseInt*(s: string, number: var int, start = 0): int {.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.
+  ## `ValueError` is raised if the parsed integer is out of the valid range.
+  runnableExamples:
+    var res: int
+    doAssert parseInt("-2024_05_02", res) == 11
+    doAssert res == -20240502
+    doAssert parseInt("-2024_05_02", res, 7) == 4
+    doAssert res == 502
+  parseInt(s.toOpenArray(start, s.high), number)
+
+
+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:.
+  runnableExamples:
+    var res = 0
+    discard parseSaturatedNatural("848", res)
+    doAssert res == 848
+  parseSaturatedNatural(s.toOpenArray(start, s.high), b)
+
+
+proc parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int {.noSideEffect, raises: [ValueError].} =
+  ## Parses an unsigned integer starting at `start` and stores the value
+  ## into `number`.
+  ## `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
+  parseBiggestUInt(s.toOpenArray(start, s.high), number)
+
+proc parseUInt*(s: string, number: var uint, start = 0): int {.noSideEffect, raises: [ValueError].} =
+  ## Parses an unsigned integer starting at `start` and stores the value
+  ## into `number`.
+  ## `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
+  parseUInt(s.toOpenArray(start, s.high), number)
+
+proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {.noSideEffect.} =
+  ## 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.
+  parseFloat(s.toOpenArray(start, s.high), number)
+
+proc parseFloat*(s: string, number: var float, start = 0): int {.noSideEffect.} =
+  ## 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
+  parseFloat(s.toOpenArray(start, s.high), number)
+
+iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
+  value: string] =
+  ## Tokenizes the string `s` into substrings for interpolation purposes.
+  ##
+  runnableExamples:
+    var outp: seq[tuple[kind: InterpolatedKind, value: string]]
+    for k, v in interpolatedFragments("  $this is ${an  example}  $$"):
+      outp.add (k, v)
+    doAssert outp == @[(ikStr, "  "),
+                       (ikVar, "this"),
+                       (ikStr, " is "),
+                       (ikExpr, "an  example"),
+                       (ikStr, "  "),
+                       (ikDollar, "$")]
+  for x in s.toOa.interpolatedFragments:
+    yield x
+
diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim
index e0000aad3..c760799a2 100644
--- a/lib/pure/parsexml.nim
+++ b/lib/pure/parsexml.nim
@@ -26,75 +26,180 @@
 ##   creates.
 ##
 ##
-## Example 1: Retrieve HTML title
-## ==============================
-##
-## The file ``examples/htmltitle.nim`` demonstrates how to use the
-## XML parser to accomplish a simple task: To determine the title of an HTML
-## document.
-##
-## .. code-block:: nim
-##     :file: ../../examples/htmltitle.nim
-##
-##
-## Example 2: Retrieve all HTML links
-## ==================================
-##
-## The file ``examples/htmlrefs.nim`` demonstrates how to use the
-## XML parser to accomplish another simple task: To determine all the links
-## an HTML document contains.
-##
-## .. code-block:: nim
-##     :file: ../../examples/htmlrefs.nim
-##
+
+##[
+
+Example 1: Retrieve HTML title
+==============================
+
+The file ``examples/htmltitle.nim`` demonstrates how to use the
+XML parser to accomplish a simple task: To determine the title of an HTML
+document.
+
+  ```nim
+  # Example program to show the parsexml module
+  # This program reads an HTML file and writes its title to stdout.
+  # Errors and whitespace are ignored.
+
+  import std/[os, streams, parsexml, strutils]
+
+  if paramCount() < 1:
+    quit("Usage: htmltitle filename[.html]")
+
+  var filename = addFileExt(paramStr(1), "html")
+  var s = newFileStream(filename, fmRead)
+  if s == nil: quit("cannot open the file " & filename)
+  var x: XmlParser
+  open(x, s, filename)
+  while true:
+    x.next()
+    case x.kind
+    of xmlElementStart:
+      if cmpIgnoreCase(x.elementName, "title") == 0:
+        var title = ""
+        x.next()  # skip "<title>"
+        while x.kind == xmlCharData:
+          title.add(x.charData)
+          x.next()
+        if x.kind == xmlElementEnd and cmpIgnoreCase(x.elementName, "title") == 0:
+          echo("Title: " & title)
+          quit(0) # Success!
+        else:
+          echo(x.errorMsgExpected("/title"))
+
+    of xmlEof: break # end of file reached
+    else: discard # ignore other events
+
+  x.close()
+  quit("Could not determine title!")
+  ```
+
+]##
+
+##[
+
+Example 2: Retrieve all HTML links
+==================================
+
+The file ``examples/htmlrefs.nim`` demonstrates how to use the
+XML parser to accomplish another simple task: To determine all the links
+an HTML document contains.
+
+  ```nim
+  # Example program to show the new parsexml module
+  # This program reads an HTML file and writes all its used links to stdout.
+  # Errors and whitespace are ignored.
+
+  import std/[os, streams, parsexml, strutils]
+
+  proc `=?=` (a, b: string): bool =
+    # little trick: define our own comparator that ignores case
+    return cmpIgnoreCase(a, b) == 0
+
+  if paramCount() < 1:
+    quit("Usage: htmlrefs filename[.html]")
+
+  var links = 0 # count the number of links
+  var filename = addFileExt(paramStr(1), "html")
+  var s = newFileStream(filename, fmRead)
+  if s == nil: quit("cannot open the file " & filename)
+  var x: XmlParser
+  open(x, s, filename)
+  next(x) # get first event
+  block mainLoop:
+    while true:
+      case x.kind
+      of xmlElementOpen:
+        # the <a href = "xyz"> tag we are interested in always has an attribute,
+        # thus we search for ``xmlElementOpen`` and not for ``xmlElementStart``
+        if x.elementName =?= "a":
+          x.next()
+          if x.kind == xmlAttribute:
+            if x.attrKey =?= "href":
+              var link = x.attrValue
+              inc(links)
+              # skip until we have an ``xmlElementClose`` event
+              while true:
+                x.next()
+                case x.kind
+                of xmlEof: break mainLoop
+                of xmlElementClose: break
+                else: discard
+              x.next() # skip ``xmlElementClose``
+              # now we have the description for the ``a`` element
+              var desc = ""
+              while x.kind == xmlCharData:
+                desc.add(x.charData)
+                x.next()
+              echo(desc & ": " & link)
+        else:
+          x.next()
+      of xmlEof: break # end of file reached
+      of xmlError:
+        echo(errorMsg(x))
+        x.next()
+      else: x.next() # skip other events
+
+  echo($links & " link(s) found!")
+  x.close()
+  ```
+
+]##
 
 import
-  hashes, strutils, lexbase, streams, unicode
+  std/[strutils, lexbase, streams, unicode]
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
 
 # the parser treats ``<br />`` as ``<br></br>``
 
 #  xmlElementCloseEnd, ## ``/>``
 
 type
-  XmlEventKind* = enum ## enumation of all events that may occur when parsing
-    xmlError,           ## an error occurred during parsing
-    xmlEof,             ## end of file reached
-    xmlCharData,        ## character data
-    xmlWhitespace,      ## whitespace has been parsed
-    xmlComment,         ## a comment has been parsed
-    xmlPI,              ## processing instruction (``<?name something ?>``)
-    xmlElementStart,    ## ``<elem>``
-    xmlElementEnd,      ## ``</elem>``
-    xmlElementOpen,     ## ``<elem
-    xmlAttribute,       ## ``key = "value"`` pair
-    xmlElementClose,    ## ``>``
-    xmlCData,           ## ``<![CDATA[`` ... data ... ``]]>``
-    xmlEntity,          ## &entity;
-    xmlSpecial          ## ``<! ... data ... >``
-
-  XmlErrorKind* = enum       ## enumeration that lists all errors that can occur
-    errNone,                 ## no error
-    errEndOfCDataExpected,   ## ``]]>`` expected
-    errNameExpected,         ## name expected
-    errSemicolonExpected,    ## ``;`` expected
-    errQmGtExpected,         ## ``?>`` expected
-    errGtExpected,           ## ``>`` expected
-    errEqExpected,           ## ``=`` expected
-    errQuoteExpected,        ## ``"`` or ``'`` expected
-    errEndOfCommentExpected  ## ``-->`` expected
+  XmlEventKind* = enum ## enumeration of all events that may occur when parsing
+    xmlError,          ## an error occurred during parsing
+    xmlEof,            ## end of file reached
+    xmlCharData,       ## character data
+    xmlWhitespace,     ## whitespace has been parsed
+    xmlComment,        ## a comment has been parsed
+    xmlPI,             ## processing instruction (``<?name something ?>``)
+    xmlElementStart,   ## ``<elem>``
+    xmlElementEnd,     ## ``</elem>``
+    xmlElementOpen,    ## ``<elem
+    xmlAttribute,      ## ``key = "value"`` pair
+    xmlElementClose,   ## ``>``
+    xmlCData,          ## ``<![CDATA[`` ... data ... ``]]>``
+    xmlEntity,         ## &entity;
+    xmlSpecial         ## ``<! ... data ... >``
+
+  XmlErrorKind* = enum        ## enumeration that lists all errors that can occur
+    errNone,                  ## no error
+    errEndOfCDataExpected,    ## ``]]>`` expected
+    errNameExpected,          ## name expected
+    errSemicolonExpected,     ## ``;`` expected
+    errQmGtExpected,          ## ``?>`` expected
+    errGtExpected,            ## ``>`` expected
+    errEqExpected,            ## ``=`` expected
+    errQuoteExpected,         ## ``"`` or ``'`` expected
+    errEndOfCommentExpected   ## ``-->`` expected
+    errAttributeValueExpected ## non-empty attribute value expected
 
   ParserState = enum
     stateStart, stateNormal, stateAttr, stateEmptyElementTag, stateError
 
-  XmlParseOption* = enum  ## options for the XML parser
-    reportWhitespace,      ## report whitespace
-    reportComments         ## report comments
+  XmlParseOption* = enum ## options for the XML parser
+    reportWhitespace,    ## report whitespace
+    reportComments       ## report comments
+    allowUnquotedAttribs ## allow unquoted attribute values (for HTML)
+    allowEmptyAttribs    ## allow empty attributes (without explicit value)
 
   XmlParser* = object of BaseLexer ## the parser object.
     a, b, c: string
     kind: XmlEventKind
     err: XmlErrorKind
     state: ParserState
+    cIsEmpty: bool
     filename: string
     options: set[XmlParseOption]
 
@@ -108,7 +213,8 @@ const
     "'>' expected",
     "'=' expected",
     "'\"' or \"'\" expected",
-    "'-->' expected"
+    "'-->' expected",
+    "attribute value expected"
   ]
 
 proc open*(my: var XmlParser, input: Stream, filename: string,
@@ -125,7 +231,8 @@ proc open*(my: var XmlParser, input: Stream, filename: string,
   my.kind = xmlError
   my.a = ""
   my.b = ""
-  my.c = nil
+  my.c = ""
+  my.cIsEmpty = true
   my.options = options
 
 proc close*(my: var XmlParser) {.inline.} =
@@ -195,15 +302,15 @@ template piRest*(my: XmlParser): string =
   assert(my.kind == xmlPI)
   my.b
 
-proc rawData*(my: XmlParser): string {.inline.} =
+proc rawData*(my: var XmlParser): lent string {.inline.} =
   ## returns the underlying 'data' string by reference.
   ## This is only used for speed hacks.
-  shallowCopy(result, my.a)
+  result = my.a
 
-proc rawData2*(my: XmlParser): string {.inline.} =
+proc rawData2*(my: var XmlParser): lent string {.inline.} =
   ## returns the underlying second 'data' string by reference.
   ## This is only used for speed hacks.
-  shallowCopy(result, my.b)
+  result = my.b
 
 proc getColumn*(my: XmlParser): int {.inline.} =
   ## get the current column the parser has arrived at.
@@ -241,11 +348,10 @@ proc markError(my: var XmlParser, kind: XmlErrorKind) {.inline.} =
 
 proc parseCDATA(my: var XmlParser) =
   var pos = my.bufpos + len("<![CDATA[")
-  var buf = my.buf
   while true:
-    case buf[pos]
+    case my.buf[pos]
     of ']':
-      if buf[pos+1] == ']' and buf[pos+2] == '>':
+      if my.buf[pos+1] == ']' and my.buf[pos+2] == '>':
         inc(pos, 3)
         break
       add(my.a, ']')
@@ -255,29 +361,25 @@ proc parseCDATA(my: var XmlParser) =
       break
     of '\c':
       pos = lexbase.handleCR(my, pos)
-      buf = my.buf
       add(my.a, '\L')
     of '\L':
       pos = lexbase.handleLF(my, pos)
-      buf = my.buf
       add(my.a, '\L')
     of '/':
       pos = lexbase.handleRefillChar(my, pos)
-      buf = my.buf
       add(my.a, '/')
     else:
-      add(my.a, buf[pos])
+      add(my.a, my.buf[pos])
       inc(pos)
   my.bufpos = pos # store back
   my.kind = xmlCData
 
 proc parseComment(my: var XmlParser) =
   var pos = my.bufpos + len("<!--")
-  var buf = my.buf
   while true:
-    case buf[pos]
+    case my.buf[pos]
     of '-':
-      if buf[pos+1] == '-' and buf[pos+2] == '>':
+      if my.buf[pos+1] == '-' and my.buf[pos+2] == '>':
         inc(pos, 3)
         break
       if my.options.contains(reportComments): add(my.a, '-')
@@ -287,38 +389,32 @@ proc parseComment(my: var XmlParser) =
       break
     of '\c':
       pos = lexbase.handleCR(my, pos)
-      buf = my.buf
       if my.options.contains(reportComments): add(my.a, '\L')
     of '\L':
       pos = lexbase.handleLF(my, pos)
-      buf = my.buf
       if my.options.contains(reportComments): add(my.a, '\L')
     of '/':
       pos = lexbase.handleRefillChar(my, pos)
-      buf = my.buf
       if my.options.contains(reportComments): add(my.a, '/')
     else:
-      if my.options.contains(reportComments): add(my.a, buf[pos])
+      if my.options.contains(reportComments): add(my.a, my.buf[pos])
       inc(pos)
   my.bufpos = pos
   my.kind = xmlComment
 
-proc parseWhitespace(my: var XmlParser, skip=false) =
+proc parseWhitespace(my: var XmlParser, skip = false) =
   var pos = my.bufpos
-  var buf = my.buf
   while true:
-    case buf[pos]
+    case my.buf[pos]
     of ' ', '\t':
-      if not skip: add(my.a, buf[pos])
+      if not skip: add(my.a, my.buf[pos])
       inc(pos)
     of '\c':
       # the specification says that CR-LF, CR are to be transformed to LF
       pos = lexbase.handleCR(my, pos)
-      buf = my.buf
       if not skip: add(my.a, '\L')
     of '\L':
       pos = lexbase.handleLF(my, pos)
-      buf = my.buf
       if not skip: add(my.a, '\L')
     else:
       break
@@ -330,82 +426,82 @@ const
 
 proc parseName(my: var XmlParser, dest: var string) =
   var pos = my.bufpos
-  var buf = my.buf
-  if buf[pos] in NameStartChar:
+  if my.buf[pos] in NameStartChar:
     while true:
-      add(dest, buf[pos])
+      add(dest, my.buf[pos])
       inc(pos)
-      if buf[pos] notin NameChar: break
+      if my.buf[pos] notin NameChar: break
     my.bufpos = pos
   else:
     markError(my, errNameExpected)
 
 proc parseEntity(my: var XmlParser, dest: var string) =
   var pos = my.bufpos+1
-  var buf = my.buf
   my.kind = xmlCharData
-  if buf[pos] == '#':
+  if my.buf[pos] == '#':
     var r: int
     inc(pos)
-    if buf[pos] == 'x':
+    if my.buf[pos] == 'x':
       inc(pos)
       while true:
-        case buf[pos]
-        of '0'..'9': r = (r shl 4) or (ord(buf[pos]) - ord('0'))
-        of 'a'..'f': r = (r shl 4) or (ord(buf[pos]) - ord('a') + 10)
-        of 'A'..'F': r = (r shl 4) or (ord(buf[pos]) - ord('A') + 10)
+        case my.buf[pos]
+        of '0'..'9': r = (r shl 4) or (ord(my.buf[pos]) - ord('0'))
+        of 'a'..'f': r = (r shl 4) or (ord(my.buf[pos]) - ord('a') + 10)
+        of 'A'..'F': r = (r shl 4) or (ord(my.buf[pos]) - ord('A') + 10)
         else: break
         inc(pos)
     else:
-      while buf[pos] in {'0'..'9'}:
-        r = r * 10 + (ord(buf[pos]) - ord('0'))
+      while my.buf[pos] in {'0'..'9'}:
+        r = r * 10 + (ord(my.buf[pos]) - ord('0'))
         inc(pos)
     add(dest, toUTF8(Rune(r)))
-  elif buf[pos] == 'l' and buf[pos+1] == 't' and buf[pos+2] == ';':
+  elif my.buf[pos] == 'l' and my.buf[pos+1] == 't' and my.buf[pos+2] == ';':
     add(dest, '<')
     inc(pos, 2)
-  elif buf[pos] == 'g' and buf[pos+1] == 't' and buf[pos+2] == ';':
+  elif my.buf[pos] == 'g' and my.buf[pos+1] == 't' and my.buf[pos+2] == ';':
     add(dest, '>')
     inc(pos, 2)
-  elif buf[pos] == 'a' and buf[pos+1] == 'm' and buf[pos+2] == 'p' and
-      buf[pos+3] == ';':
+  elif my.buf[pos] == 'a' and my.buf[pos+1] == 'm' and my.buf[pos+2] == 'p' and
+      my.buf[pos+3] == ';':
     add(dest, '&')
     inc(pos, 3)
-  elif buf[pos] == 'a' and buf[pos+1] == 'p' and buf[pos+2] == 'o' and
-      buf[pos+3] == 's' and buf[pos+4] == ';':
+  elif my.buf[pos] == 'a' and my.buf[pos+1] == 'p' and my.buf[pos+2] == 'o' and
+      my.buf[pos+3] == 's' and my.buf[pos+4] == ';':
     add(dest, '\'')
     inc(pos, 4)
-  elif buf[pos] == 'q' and buf[pos+1] == 'u' and buf[pos+2] == 'o' and
-      buf[pos+3] == 't' and buf[pos+4] == ';':
+  elif my.buf[pos] == 'q' and my.buf[pos+1] == 'u' and my.buf[pos+2] == 'o' and
+      my.buf[pos+3] == 't' and my.buf[pos+4] == ';':
     add(dest, '"')
     inc(pos, 4)
   else:
     my.bufpos = pos
-    parseName(my, dest)
+    var name = ""
+    parseName(my, name)
     pos = my.bufpos
-    if my.err != errNameExpected:
+    if my.err != errNameExpected and my.buf[pos] == ';':
       my.kind = xmlEntity
     else:
       add(dest, '&')
-  if buf[pos] == ';':
+    add(dest, name)
+  if my.buf[pos] == ';':
     inc(pos)
   else:
-    markError(my, errSemicolonExpected)
+    my.err = errSemicolonExpected
+    # do not overwrite 'my.state' here, it's a benign error
   my.bufpos = pos
 
 proc parsePI(my: var XmlParser) =
   inc(my.bufpos, "<?".len)
   parseName(my, my.a)
   var pos = my.bufpos
-  var buf = my.buf
   setLen(my.b, 0)
   while true:
-    case buf[pos]
+    case my.buf[pos]
     of '\0':
       markError(my, errQmGtExpected)
       break
     of '?':
-      if buf[pos+1] == '>':
+      if my.buf[pos+1] == '>':
         inc(pos, 2)
         break
       add(my.b, '?')
@@ -413,18 +509,15 @@ proc parsePI(my: var XmlParser) =
     of '\c':
       # the specification says that CR-LF, CR are to be transformed to LF
       pos = lexbase.handleCR(my, pos)
-      buf = my.buf
       add(my.b, '\L')
     of '\L':
       pos = lexbase.handleLF(my, pos)
-      buf = my.buf
       add(my.b, '\L')
     of '/':
       pos = lexbase.handleRefillChar(my, pos)
-      buf = my.buf
       add(my.b, '/')
     else:
-      add(my.b, buf[pos])
+      add(my.b, my.buf[pos])
       inc(pos)
   my.bufpos = pos
   my.kind = xmlPI
@@ -432,10 +525,9 @@ proc parsePI(my: var XmlParser) =
 proc parseSpecial(my: var XmlParser) =
   # things that start with <!
   var pos = my.bufpos + 2
-  var buf = my.buf
   var opentags = 0
   while true:
-    case buf[pos]
+    case my.buf[pos]
     of '\0':
       markError(my, errGtExpected)
       break
@@ -452,18 +544,15 @@ proc parseSpecial(my: var XmlParser) =
       add(my.a, '>')
     of '\c':
       pos = lexbase.handleCR(my, pos)
-      buf = my.buf
       add(my.a, '\L')
     of '\L':
       pos = lexbase.handleLF(my, pos)
-      buf = my.buf
       add(my.a, '\L')
     of '/':
       pos = lexbase.handleRefillChar(my, pos)
-      buf = my.buf
       add(my.b, '/')
     else:
-      add(my.a, buf[pos])
+      add(my.a, my.buf[pos])
       inc(pos)
   my.bufpos = pos
   my.kind = xmlSpecial
@@ -476,12 +565,13 @@ proc parseTag(my: var XmlParser) =
     my.kind = xmlCharData
     add(my.a, '<')
     return
-  parseWhitespace(my, skip=true)
+  parseWhitespace(my, skip = true)
   if my.buf[my.bufpos] in NameStartChar:
     # an attribute follows:
     my.kind = xmlElementOpen
     my.state = stateAttr
     my.c = my.a # save for later
+    my.cIsEmpty = false
   else:
     my.kind = xmlElementStart
     let slash = my.buf[my.bufpos] == '/'
@@ -490,7 +580,8 @@ proc parseTag(my: var XmlParser) =
     if slash and my.buf[my.bufpos] == '>':
       inc(my.bufpos)
       my.state = stateEmptyElementTag
-      my.c = nil
+      my.c = ""
+      my.cIsEmpty = true
     elif my.buf[my.bufpos] == '>':
       inc(my.bufpos)
     else:
@@ -500,7 +591,7 @@ proc parseEndTag(my: var XmlParser) =
   my.bufpos = lexbase.handleRefillChar(my, my.bufpos+1)
   #inc(my.bufpos, 2)
   parseName(my, my.a)
-  parseWhitespace(my, skip=true)
+  parseWhitespace(my, skip = true)
   if my.buf[my.bufpos] == '>':
     inc(my.bufpos)
   else:
@@ -516,21 +607,25 @@ proc parseAttribute(my: var XmlParser) =
   if my.a.len == 0:
     markError(my, errGtExpected)
     return
-  parseWhitespace(my, skip=true)
+
+  let startPos = my.bufpos
+  parseWhitespace(my, skip = true)
   if my.buf[my.bufpos] != '=':
-    markError(my, errEqExpected)
+    if allowEmptyAttribs notin my.options or
+        (my.buf[my.bufpos] != '>' and my.bufpos == startPos):
+      markError(my, errEqExpected)
     return
+
   inc(my.bufpos)
-  parseWhitespace(my, skip=true)
+  parseWhitespace(my, skip = true)
 
   var pos = my.bufpos
-  var buf = my.buf
-  if buf[pos] in {'\'', '"'}:
-    var quote = buf[pos]
+  if my.buf[pos] in {'\'', '"'}:
+    var quote = my.buf[pos]
     var pendingSpace = false
     inc(pos)
     while true:
-      case buf[pos]
+      case my.buf[pos]
       of '\0':
         markError(my, errQuoteExpected)
         break
@@ -547,56 +642,67 @@ proc parseAttribute(my: var XmlParser) =
         inc(pos)
       of '\c':
         pos = lexbase.handleCR(my, pos)
-        buf = my.buf
         pendingSpace = true
       of '\L':
         pos = lexbase.handleLF(my, pos)
-        buf = my.buf
         pendingSpace = true
       of '/':
         pos = lexbase.handleRefillChar(my, pos)
-        buf = my.buf
         add(my.b, '/')
       else:
-        if buf[pos] == quote:
+        if my.buf[pos] == quote:
           inc(pos)
           break
         else:
           if pendingSpace:
             add(my.b, ' ')
             pendingSpace = false
-          add(my.b, buf[pos])
+          add(my.b, my.buf[pos])
           inc(pos)
+  elif allowUnquotedAttribs in my.options:
+    const disallowedChars = {'"', '\'', '`', '=', '<', '>', ' ',
+                             '\0', '\t', '\L', '\F', '\f'}
+    let startPos = pos
+    while (let c = my.buf[pos]; c notin disallowedChars):
+      if c == '&':
+        my.bufpos = pos
+        parseEntity(my, my.b)
+        my.kind = xmlAttribute # parseEntity overwrites my.kind!
+        pos = my.bufpos
+      elif c == '/':
+        pos = lexbase.handleRefillChar(my, pos)
+        add(my.b, '/')
+      else:
+        add(my.b, c)
+        inc(pos)
+    if pos == startPos:
+      markError(my, errAttributeValueExpected)
   else:
     markError(my, errQuoteExpected)
     # error corrections: guess what was meant
-    while buf[pos] != '>' and buf[pos] > ' ':
-      add(my.b, buf[pos])
+    while my.buf[pos] != '>' and my.buf[pos] > ' ':
+      add(my.b, my.buf[pos])
       inc pos
   my.bufpos = pos
-  parseWhitespace(my, skip=true)
+  parseWhitespace(my, skip = true)
 
 proc parseCharData(my: var XmlParser) =
   var pos = my.bufpos
-  var buf = my.buf
   while true:
-    case buf[pos]
+    case my.buf[pos]
     of '\0', '<', '&': break
     of '\c':
       # the specification says that CR-LF, CR are to be transformed to LF
       pos = lexbase.handleCR(my, pos)
-      buf = my.buf
       add(my.a, '\L')
     of '\L':
       pos = lexbase.handleLF(my, pos)
-      buf = my.buf
       add(my.a, '\L')
     of '/':
       pos = lexbase.handleRefillChar(my, pos)
-      buf = my.buf
       add(my.a, '/')
     else:
-      add(my.a, buf[pos])
+      add(my.a, my.buf[pos])
       inc(pos)
   my.bufpos = pos
   my.kind = xmlCharData
@@ -605,18 +711,18 @@ proc rawGetTok(my: var XmlParser) =
   my.kind = xmlError
   setLen(my.a, 0)
   var pos = my.bufpos
-  var buf = my.buf
-  case buf[pos]
+  case my.buf[pos]
   of '<':
-    case buf[pos+1]
+    case my.buf[pos+1]
     of '/':
       parseEndTag(my)
     of '!':
-      if buf[pos+2] == '[' and buf[pos+3] == 'C' and buf[pos+4] == 'D' and
-          buf[pos+5] == 'A' and buf[pos+6] == 'T' and buf[pos+7] == 'A' and
-          buf[pos+8] == '[':
+      if my.buf[pos+2] == '[' and my.buf[pos+3] == 'C' and
+          my.buf[pos+4] == 'D' and my.buf[pos+5] == 'A' and
+          my.buf[pos+6] == 'T' and my.buf[pos+7] == 'A' and
+          my.buf[pos+8] == '[':
         parseCDATA(my)
-      elif buf[pos+2] == '-' and buf[pos+3] == '-':
+      elif my.buf[pos+2] == '-' and my.buf[pos+3] == '-':
         parseComment(my)
       else:
         parseSpecial(my)
@@ -643,7 +749,8 @@ proc getTok(my: var XmlParser) =
     of xmlComment:
       if my.options.contains(reportComments): break
     of xmlWhitespace:
-      if my.options.contains(reportWhitespace) or lastKind in {xmlCharData, xmlComment, xmlEntity}:
+      if my.options.contains(reportWhitespace) or lastKind in {xmlCharData,
+          xmlComment, xmlEntity}:
         break
     else: break
 
@@ -678,14 +785,14 @@ proc next*(my: var XmlParser) =
   of stateEmptyElementTag:
     my.state = stateNormal
     my.kind = xmlElementEnd
-    if not my.c.isNil:
+    if not my.cIsEmpty:
       my.a = my.c
   of stateError:
     my.kind = xmlError
     my.state = stateNormal
 
 when not defined(testing) and isMainModule:
-  import os
+  import std/os
   var s = newFileStream(paramStr(1), fmRead)
   if s == nil: quit("cannot open the file" & paramStr(1))
   var x: XmlParser
@@ -715,4 +822,3 @@ when not defined(testing) and isMainModule:
     of xmlSpecial:
       echo("SPECIAL: " & x.charData)
   close(x)
-
diff --git a/lib/pure/pathnorm.nim b/lib/pure/pathnorm.nim
new file mode 100644
index 000000000..4cdc02303
--- /dev/null
+++ b/lib/pure/pathnorm.nim
@@ -0,0 +1,121 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## OS-Path normalization. Used by `os.nim` but also
+## generally useful for dealing with paths.
+##
+## Unstable API.
+
+# Yes, this uses import here, not include so that
+# we don't end up exporting these symbols from pathnorm and os:
+import std/private/osseps
+
+type
+  PathIter* = object
+    i, prev: int
+    notFirst: bool
+
+proc hasNext*(it: PathIter; x: string): bool =
+  it.i < x.len
+
+proc next*(it: var PathIter; x: string): (int, int) =
+  it.prev = it.i
+  if not it.notFirst and x[it.i] in {DirSep, AltSep}:
+    # absolute path:
+    inc it.i
+  else:
+    while it.i < x.len and x[it.i] notin {DirSep, AltSep}: inc it.i
+  if it.i > it.prev:
+    result = (it.prev, it.i-1)
+  elif hasNext(it, x):
+    result = next(it, x)
+  # skip all separators:
+  while it.i < x.len and x[it.i] in {DirSep, AltSep}: inc it.i
+  it.notFirst = true
+
+iterator dirs(x: string): (int, int) =
+  var it = default PathIter
+  while hasNext(it, x): yield next(it, x)
+
+proc isDot(x: string; bounds: (int, int)): bool =
+  bounds[1] == bounds[0] and x[bounds[0]] == '.'
+
+proc isDotDot(x: string; bounds: (int, int)): bool =
+  bounds[1] == bounds[0] + 1 and x[bounds[0]] == '.' and x[bounds[0]+1] == '.'
+
+proc isSlash(x: string; bounds: (int, int)): bool =
+  bounds[1] == bounds[0] and x[bounds[0]] in {DirSep, AltSep}
+
+when doslikeFileSystem:
+  import std/private/ntpath
+
+proc addNormalizePath*(x: string; result: var string; state: var int;
+    dirSep = DirSep) =
+  ## Low level proc. Undocumented.
+
+  when doslikeFileSystem: # Add Windows drive at start without normalization
+    var x = x
+    if result == "":
+      let (drive, file) = splitDrive(x)
+      x = file
+      result.add drive
+      for c in result.mitems:
+        if c in {DirSep, AltSep}:
+          c = dirSep
+
+  # state: 0th bit set if isAbsolute path. Other bits count
+  # the number of path components.
+  var it: PathIter
+  it.notFirst = (state shr 1) > 0
+  if it.notFirst:
+    while it.i < x.len and x[it.i] in {DirSep, AltSep}: inc it.i
+  while hasNext(it, x):
+    let b = next(it, x)
+    if (state shr 1 == 0) and isSlash(x, b):
+      if result.len == 0 or result[result.len - 1] notin {DirSep, AltSep}:
+        result.add dirSep
+      state = state or 1
+    elif isDotDot(x, b):
+      if (state shr 1) >= 1:
+        var d = result.len
+        # f/..
+        # We could handle stripping trailing sep here: foo// => foo like this:
+        # while (d-1) > (state and 1) and result[d-1] in {DirSep, AltSep}: dec d
+        # but right now we instead handle it inside os.joinPath
+
+        # strip path component: foo/bar => foo
+        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[result.len - 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[result.len - 1] notin {DirSep, AltSep}:
+        result.add dirSep
+      result.add substr(x, b[0], b[1])
+      inc state, 2
+  if result == "" and x != "": result = "."
+
+proc normalizePath*(path: string; dirSep = DirSep): string =
+  runnableExamples:
+    when defined(posix):
+      doAssert normalizePath("./foo//bar/../baz") == "foo/baz"
+
+  ## - Turns multiple slashes into single slashes.
+  ## - Resolves `'/foo/../bar'` to `'/bar'`.
+  ## - Removes `'./'` from the path, but `"foo/.."` becomes `"."`.
+  result = newStringOfCap(path.len)
+  var state = 0
+  addNormalizePath(path, result, state, dirSep)
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
index d16527a56..2969fd6d7 100644
--- a/lib/pure/pegs.nim
+++ b/lib/pure/pegs.nim
@@ -16,15 +16,18 @@
 ##
 
 include "system/inclrtl"
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions]
 
 const
   useUnicode = true ## change this to deactivate proper UTF-8 support
 
-import
-  strutils
+import std/[strutils, macros]
+import std/private/decode_helpers
 
 when useUnicode:
-  import unicode
+  import std/unicode
+  export unicode.`==`
 
 const
   InlineThreshold = 5  ## number of leaves; -1 to disable inlining
@@ -34,46 +37,46 @@ const
 type
   PegKind* = enum
     pkEmpty,
-    pkAny,              ## any character (.)
-    pkAnyRune,          ## any Unicode character (_)
-    pkNewLine,          ## CR-LF, LF, CR
-    pkLetter,           ## Unicode letter
-    pkLower,            ## Unicode lower case letter
-    pkUpper,            ## Unicode upper case letter
-    pkTitle,            ## Unicode title character
-    pkWhitespace,       ## Unicode whitespace character
+    pkAny,            ## any character (.)
+    pkAnyRune,        ## any Unicode character (_)
+    pkNewLine,        ## CR-LF, LF, CR
+    pkLetter,         ## Unicode letter
+    pkLower,          ## Unicode lower case letter
+    pkUpper,          ## Unicode upper case letter
+    pkTitle,          ## Unicode title character
+    pkWhitespace,     ## Unicode whitespace character
     pkTerminal,
     pkTerminalIgnoreCase,
     pkTerminalIgnoreStyle,
-    pkChar,             ## single character to match
+    pkChar,           ## single character to match
     pkCharChoice,
     pkNonTerminal,
-    pkSequence,         ## a b c ... --> Internal DSL: peg(a, b, c)
-    pkOrderedChoice,    ## a / b / ... --> Internal DSL: a / b or /[a, b, c]
-    pkGreedyRep,        ## a*     --> Internal DSL: *a
-                        ## a+     --> (a a*)
-    pkGreedyRepChar,    ## x* where x is a single character (superop)
-    pkGreedyRepSet,     ## [set]* (superop)
-    pkGreedyAny,        ## .* or _* (superop)
-    pkOption,           ## a?     --> Internal DSL: ?a
-    pkAndPredicate,     ## &a     --> Internal DSL: &a
-    pkNotPredicate,     ## !a     --> Internal DSL: !a
-    pkCapture,          ## {a}    --> Internal DSL: capture(a)
-    pkBackRef,          ## $i     --> Internal DSL: backref(i)
+    pkSequence,       ## a b c ... --> Internal DSL: peg(a, b, c)
+    pkOrderedChoice,  ## a / b / ... --> Internal DSL: a / b or /[a, b, c]
+    pkGreedyRep,      ## a*     --> Internal DSL: *a
+                      ## a+     --> (a a*)
+    pkGreedyRepChar,  ## x* where x is a single character (superop)
+    pkGreedyRepSet,   ## [set]* (superop)
+    pkGreedyAny,      ## .* or _* (superop)
+    pkOption,         ## a?     --> Internal DSL: ?a
+    pkAndPredicate,   ## &a     --> Internal DSL: &a
+    pkNotPredicate,   ## !a     --> Internal DSL: !a
+    pkCapture,        ## {a}    --> Internal DSL: capture(a)
+    pkBackRef,        ## $i     --> Internal DSL: backref(i)
     pkBackRefIgnoreCase,
     pkBackRefIgnoreStyle,
-    pkSearch,           ## @a     --> Internal DSL: !*a
-    pkCapturedSearch,   ## {@} a  --> Internal DSL: !*\a
-    pkRule,             ## a <- b
-    pkList,             ## a, b
-    pkStartAnchor       ## ^      --> Internal DSL: startAnchor()
+    pkSearch,         ## @a     --> Internal DSL: !*a
+    pkCapturedSearch, ## {@} a  --> Internal DSL: !*\a
+    pkRule,           ## a <- b
+    pkList,           ## a, b
+    pkStartAnchor     ## ^      --> Internal DSL: startAnchor()
   NonTerminalFlag* = enum
     ntDeclared, ntUsed
-  NonTerminalObj = object         ## represents a non terminal symbol
-    name: string                  ## the name of the symbol
-    line: int                     ## line the symbol has been declared/used in
-    col: int                      ## column the symbol has been declared/used in
-    flags: set[NonTerminalFlag]   ## the nonterminal's flags
+  NonTerminalObj = object       ## represents a non terminal symbol
+    name: string                ## the name of the symbol
+    line: int                   ## line the symbol has been declared/used in
+    col: int                    ## column the symbol has been declared/used in
+    flags: set[NonTerminalFlag] ## the nonterminal's flags
     rule: Peg                   ## the rule that the symbol refers to
   Peg* {.shallow.} = object ## type that represents a PEG
     case kind: PegKind
@@ -82,67 +85,97 @@ type
     of pkChar, pkGreedyRepChar: ch: char
     of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char]
     of pkNonTerminal: nt: NonTerminal
-    of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns]
+    of pkBackRef..pkBackRefIgnoreStyle: index: range[-MaxSubpatterns..MaxSubpatterns-1]
     else: sons: seq[Peg]
   NonTerminal* = ref NonTerminalObj
 
-proc name*(nt: NonTerminal): string = nt.name
-proc line*(nt: NonTerminal): int = nt.line
-proc col*(nt: NonTerminal): int = nt.col
-proc flags*(nt: NonTerminal): set[NonTerminalFlag] = nt.flags
-proc rule*(nt: NonTerminal): Peg = nt.rule
-
-proc kind*(p: Peg): PegKind = p.kind
-proc term*(p: Peg): string = p.term
-proc ch*(p: Peg): char = p.ch
-proc charChoice*(p: Peg): ref set[char] = p.charChoice
-proc nt*(p: Peg): NonTerminal = p.nt
-proc index*(p: Peg): range[0..MaxSubpatterns] = p.index
+func kind*(p: Peg): PegKind = p.kind
+  ## Returns the *PegKind* of a given *Peg* object.
+
+func term*(p: Peg): string = p.term
+  ## Returns the *string* representation of a given *Peg* variant object
+  ## where present.
+
+func ch*(p: Peg): char = p.ch
+  ## Returns the *char* representation of a given *Peg* variant object
+  ## where present.
+
+func charChoice*(p: Peg): ref set[char] = p.charChoice
+  ## Returns the *charChoice* field of a given *Peg* variant object
+  ## where present.
+
+func nt*(p: Peg): NonTerminal = p.nt
+  ## Returns the *NonTerminal* object of a given *Peg* variant object
+  ## where present.
+
+func index*(p: Peg): range[-MaxSubpatterns..MaxSubpatterns-1] = p.index
+  ## Returns the back-reference index of a captured sub-pattern in the
+  ## *Captures* object for a given *Peg* variant object where present.
+
 iterator items*(p: Peg): Peg {.inline.} =
+  ## Yields the child nodes of a *Peg* variant object where present.
   for s in p.sons:
     yield s
+
 iterator pairs*(p: Peg): (int, Peg) {.inline.} =
+  ## Yields the indices and child nodes of a *Peg* variant object where present.
   for i in 0 ..< p.sons.len:
     yield (i, p.sons[i])
 
-proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} =
+func name*(nt: NonTerminal): string = nt.name
+  ## Gets the name of the symbol represented by the parent *Peg* object variant
+  ## of a given *NonTerminal*.
+
+func line*(nt: NonTerminal): int = nt.line
+  ## Gets the line number of the definition of the parent *Peg* object variant
+  ## of a given *NonTerminal*.
+
+func col*(nt: NonTerminal): int = nt.col
+  ## Gets the column number of the definition of the parent *Peg* object variant
+  ## of a given *NonTerminal*.
+
+func flags*(nt: NonTerminal): set[NonTerminalFlag] = nt.flags
+  ## Gets the *NonTerminalFlag*-typed flags field of the parent *Peg* variant
+  ## object of a given *NonTerminal*.
+
+func rule*(nt: NonTerminal): Peg = nt.rule
+  ## Gets the *Peg* object representing the rule definition of the parent *Peg*
+  ## object variant of a given *NonTerminal*.
+
+func term*(t: string): Peg {.rtl, extern: "npegs$1Str".} =
   ## constructs a PEG from a terminal string
   if t.len != 1:
-    result.kind = pkTerminal
-    result.term = t
+    result = Peg(kind: pkTerminal, term: t)
   else:
-    result.kind = pkChar
-    result.ch = t[0]
+    result = Peg(kind: pkChar, ch: t[0])
 
-proc termIgnoreCase*(t: string): Peg {.
-  nosideEffect, rtl, extern: "npegs$1".} =
+func termIgnoreCase*(t: string): Peg {.
+  rtl, extern: "npegs$1".} =
   ## constructs a PEG from a terminal string; ignore case for matching
-  result.kind = pkTerminalIgnoreCase
-  result.term = t
+  result = Peg(kind: pkTerminalIgnoreCase, term: t)
 
-proc termIgnoreStyle*(t: string): Peg {.
-  nosideEffect, rtl, extern: "npegs$1".} =
+func termIgnoreStyle*(t: string): Peg {.
+  rtl, extern: "npegs$1".} =
   ## constructs a PEG from a terminal string; ignore style for matching
-  result.kind = pkTerminalIgnoreStyle
-  result.term = t
+  result = Peg(kind: pkTerminalIgnoreStyle, term: t)
 
-proc term*(t: char): Peg {.nosideEffect, rtl, extern: "npegs$1Char".} =
+func term*(t: char): Peg {.rtl, extern: "npegs$1Char".} =
   ## constructs a PEG from a terminal char
   assert t != '\0'
-  result.kind = pkChar
-  result.ch = t
+  result = Peg(kind: pkChar, ch: t)
 
-proc charSet*(s: set[char]): Peg {.nosideEffect, rtl, extern: "npegs$1".} =
+func charSet*(s: set[char]): Peg {.rtl, extern: "npegs$1".} =
   ## constructs a PEG from a character set `s`
   assert '\0' notin s
-  result.kind = pkCharChoice
-  new(result.charChoice)
-  result.charChoice[] = s
+  result = Peg(kind: pkCharChoice)
+  {.cast(noSideEffect).}:
+    new(result.charChoice)
+    result.charChoice[] = s
 
-proc len(a: Peg): int {.inline.} = return a.sons.len
-proc add(d: var Peg, s: Peg) {.inline.} = add(d.sons, s)
+func len(a: Peg): int {.inline.} = return a.sons.len
+func add(d: var Peg, s: Peg) {.inline.} = add(d.sons, s)
 
-proc addChoice(dest: var Peg, elem: Peg) =
+func addChoice(dest: var Peg, elem: Peg) =
   var L = dest.len-1
   if L >= 0 and dest.sons[L].kind == pkCharChoice:
     # caution! Do not introduce false aliasing here!
@@ -155,8 +188,7 @@ proc addChoice(dest: var Peg, elem: Peg) =
   else: add(dest, elem)
 
 template multipleOp(k: PegKind, localOpt: untyped) =
-  result.kind = k
-  result.sons = @[]
+  result = Peg(kind: k, sons: @[])
   for x in items(a):
     if x.kind == k:
       for y in items(x.sons):
@@ -166,12 +198,12 @@ template multipleOp(k: PegKind, localOpt: untyped) =
   if result.len == 1:
     result = result.sons[0]
 
-proc `/`*(a: varargs[Peg]): Peg {.
-  nosideEffect, rtl, extern: "npegsOrderedChoice".} =
+func `/`*(a: varargs[Peg]): Peg {.
+  rtl, extern: "npegsOrderedChoice".} =
   ## constructs an ordered choice with the PEGs in `a`
   multipleOp(pkOrderedChoice, addChoice)
 
-proc addSequence(dest: var Peg, elem: Peg) =
+func addSequence(dest: var Peg, elem: Peg) =
   var L = dest.len-1
   if L >= 0 and dest.sons[L].kind == pkTerminal:
     # caution! Do not introduce false aliasing here!
@@ -183,12 +215,12 @@ proc addSequence(dest: var Peg, elem: Peg) =
     else: add(dest, elem)
   else: add(dest, elem)
 
-proc sequence*(a: varargs[Peg]): Peg {.
-  nosideEffect, rtl, extern: "npegs$1".} =
+func sequence*(a: varargs[Peg]): Peg {.
+  rtl, extern: "npegs$1".} =
   ## constructs a sequence with all the PEGs from `a`
   multipleOp(pkSequence, addSequence)
 
-proc `?`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsOptional".} =
+func `?`*(a: Peg): Peg {.rtl, extern: "npegsOptional".} =
   ## constructs an optional for the PEG `a`
   if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar,
                 pkGreedyRepSet}:
@@ -196,125 +228,116 @@ proc `?`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsOptional".} =
     # a? ?  --> a?
     result = a
   else:
-    result.kind = pkOption
-    result.sons = @[a]
+    result = Peg(kind: pkOption, sons: @[a])
 
-proc `*`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsGreedyRep".} =
+func `*`*(a: Peg): Peg {.rtl, extern: "npegsGreedyRep".} =
   ## constructs a "greedy repetition" for the PEG `a`
   case a.kind
   of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption:
     assert false
     # produces endless loop!
   of pkChar:
-    result.kind = pkGreedyRepChar
-    result.ch = a.ch
+    result = Peg(kind: pkGreedyRepChar, ch: a.ch)
   of pkCharChoice:
-    result.kind = pkGreedyRepSet
-    result.charChoice = a.charChoice # copying a reference suffices!
+    result = Peg(kind: pkGreedyRepSet, charChoice: a.charChoice)
   of pkAny, pkAnyRune:
-    result.kind = pkGreedyAny
+    result = Peg(kind: pkGreedyAny)
   else:
-    result.kind = pkGreedyRep
-    result.sons = @[a]
+    result = Peg(kind: pkGreedyRep, sons: @[a])
 
-proc `!*`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsSearch".} =
+func `!*`*(a: Peg): Peg {.rtl, extern: "npegsSearch".} =
   ## constructs a "search" for the PEG `a`
-  result.kind = pkSearch
-  result.sons = @[a]
+  result = Peg(kind: pkSearch, sons: @[a])
 
-proc `!*\`*(a: Peg): Peg {.noSideEffect, rtl,
+func `!*\`*(a: Peg): Peg {.rtl,
                              extern: "npgegsCapturedSearch".} =
   ## constructs a "captured search" for the PEG `a`
-  result.kind = pkCapturedSearch
-  result.sons = @[a]
+  result = Peg(kind: pkCapturedSearch, sons: @[a])
 
-proc `+`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsGreedyPosRep".} =
+func `+`*(a: Peg): Peg {.rtl, extern: "npegsGreedyPosRep".} =
   ## constructs a "greedy positive repetition" with the PEG `a`
   return sequence(a, *a)
 
-proc `&`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsAndPredicate".} =
+func `&`*(a: Peg): Peg {.rtl, extern: "npegsAndPredicate".} =
   ## constructs an "and predicate" with the PEG `a`
-  result.kind = pkAndPredicate
-  result.sons = @[a]
+  result = Peg(kind: pkAndPredicate, sons: @[a])
 
-proc `!`*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsNotPredicate".} =
+func `!`*(a: Peg): Peg {.rtl, extern: "npegsNotPredicate".} =
   ## constructs a "not predicate" with the PEG `a`
-  result.kind = pkNotPredicate
-  result.sons = @[a]
+  result = Peg(kind: pkNotPredicate, sons: @[a])
 
-proc any*: Peg {.inline.} =
+func any*: Peg {.inline.} =
   ## constructs the PEG `any character`:idx: (``.``)
-  result.kind = pkAny
+  result = Peg(kind: pkAny)
 
-proc anyRune*: Peg {.inline.} =
+func anyRune*: Peg {.inline.} =
   ## constructs the PEG `any rune`:idx: (``_``)
-  result.kind = pkAnyRune
+  result = Peg(kind: pkAnyRune)
 
-proc newLine*: Peg {.inline.} =
+func newLine*: Peg {.inline.} =
   ## constructs the PEG `newline`:idx: (``\n``)
-  result.kind = pkNewLine
+  result = Peg(kind: pkNewLine)
 
-proc unicodeLetter*: Peg {.inline.} =
+func unicodeLetter*: Peg {.inline.} =
   ## constructs the PEG ``\letter`` which matches any Unicode letter.
-  result.kind = pkLetter
+  result = Peg(kind: pkLetter)
 
-proc unicodeLower*: Peg {.inline.} =
+func unicodeLower*: Peg {.inline.} =
   ## constructs the PEG ``\lower`` which matches any Unicode lowercase letter.
-  result.kind = pkLower
+  result = Peg(kind: pkLower)
 
-proc unicodeUpper*: Peg {.inline.} =
+func unicodeUpper*: Peg {.inline.} =
   ## constructs the PEG ``\upper`` which matches any Unicode uppercase letter.
-  result.kind = pkUpper
+  result = Peg(kind: pkUpper)
 
-proc unicodeTitle*: Peg {.inline.} =
+func unicodeTitle*: Peg {.inline.} =
   ## constructs the PEG ``\title`` which matches any Unicode title letter.
-  result.kind = pkTitle
+  result = Peg(kind: pkTitle)
 
-proc unicodeWhitespace*: Peg {.inline.} =
+func unicodeWhitespace*: Peg {.inline.} =
   ## constructs the PEG ``\white`` which matches any Unicode
   ## whitespace character.
-  result.kind = pkWhitespace
+  result = Peg(kind: pkWhitespace)
 
-proc startAnchor*: Peg {.inline.} =
+func startAnchor*: Peg {.inline.} =
   ## constructs the PEG ``^`` which matches the start of the input.
-  result.kind = pkStartAnchor
+  result = Peg(kind: pkStartAnchor)
 
-proc endAnchor*: Peg {.inline.} =
+func endAnchor*: Peg {.inline.} =
   ## constructs the PEG ``$`` which matches the end of the input.
   result = !any()
 
-proc capture*(a: Peg): Peg {.nosideEffect, rtl, extern: "npegsCapture".} =
+func capture*(a: Peg = Peg(kind: pkEmpty)): Peg {.rtl, extern: "npegsCapture".} =
   ## constructs a capture with the PEG `a`
-  result.kind = pkCapture
-  result.sons = @[a]
+  result = Peg(kind: pkCapture, sons: @[a])
 
-proc backref*(index: range[1..MaxSubpatterns]): Peg {.
-  nosideEffect, rtl, extern: "npegs$1".} =
+func backref*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {.
+  rtl, extern: "npegs$1".} =
   ## constructs a back reference of the given `index`. `index` starts counting
-  ## from 1.
-  result.kind = pkBackRef
-  result.index = index-1
+  ## from 1. `reverse` specifies whether indexing starts from the end of the
+  ## capture list.
+  result = Peg(kind: pkBackRef, index: (if reverse: -index else: index - 1))
 
-proc backrefIgnoreCase*(index: range[1..MaxSubpatterns]): Peg {.
-  nosideEffect, rtl, extern: "npegs$1".} =
+func backrefIgnoreCase*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {.
+  rtl, extern: "npegs$1".} =
   ## constructs a back reference of the given `index`. `index` starts counting
-  ## from 1. Ignores case for matching.
-  result.kind = pkBackRefIgnoreCase
-  result.index = index-1
+  ## from 1. `reverse` specifies whether indexing starts from the end of the
+  ## capture list. Ignores case for matching.
+  result = Peg(kind: pkBackRefIgnoreCase, index: (if reverse: -index else: index - 1))
 
-proc backrefIgnoreStyle*(index: range[1..MaxSubpatterns]): Peg {.
-  nosideEffect, rtl, extern: "npegs$1".}=
+func backrefIgnoreStyle*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {.
+  rtl, extern: "npegs$1".} =
   ## constructs a back reference of the given `index`. `index` starts counting
-  ## from 1. Ignores style for matching.
-  result.kind = pkBackRefIgnoreStyle
-  result.index = index-1
+  ## from 1. `reverse` specifies whether indexing starts from the end of the
+  ## capture list. Ignores style for matching.
+  result = Peg(kind: pkBackRefIgnoreStyle, index: (if reverse: -index else: index - 1))
 
-proc spaceCost(n: Peg): int =
+func spaceCost(n: Peg): int =
   case n.kind
   of pkEmpty: discard
   of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar,
      pkGreedyRepChar, pkCharChoice, pkGreedyRepSet,
-     pkAny..pkWhitespace, pkGreedyAny:
+     pkAny..pkWhitespace, pkGreedyAny, pkBackRef..pkBackRefIgnoreStyle:
     result = 1
   of pkNonTerminal:
     # we cannot inline a rule with a non-terminal
@@ -324,24 +347,20 @@ proc spaceCost(n: Peg): int =
       inc(result, spaceCost(n.sons[i]))
       if result >= InlineThreshold: break
 
-proc nonterminal*(n: NonTerminal): Peg {.
-  nosideEffect, rtl, extern: "npegs$1".} =
+func nonterminal*(n: NonTerminal): Peg {.
+  rtl, extern: "npegs$1".} =
   ## constructs a PEG that consists of the nonterminal symbol
   assert n != nil
   if ntDeclared in n.flags and spaceCost(n.rule) < InlineThreshold:
     when false: echo "inlining symbol: ", n.name
     result = n.rule # inlining of rule enables better optimizations
   else:
-    result.kind = pkNonTerminal
-    result.nt = n
+    result = Peg(kind: pkNonTerminal, nt: n)
 
-proc newNonTerminal*(name: string, line, column: int): NonTerminal {.
-  nosideEffect, rtl, extern: "npegs$1".} =
+func newNonTerminal*(name: string, line, column: int): NonTerminal {.
+  rtl, extern: "npegs$1".} =
   ## constructs a nonterminal symbol
-  new(result)
-  result.name = name
-  result.line = line
-  result.col = column
+  result = NonTerminal(name: name, line: line, col: column)
 
 template letters*: Peg =
   ## expands to ``charset({'A'..'Z', 'a'..'z'})``
@@ -374,7 +393,7 @@ template natural*: Peg =
 
 # ------------------------- debugging -----------------------------------------
 
-proc esc(c: char, reserved = {'\0'..'\255'}): string =
+func esc(c: char, reserved = {'\0'..'\255'}): string =
   case c
   of '\b': result = "\\b"
   of '\t': result = "\\t"
@@ -390,14 +409,14 @@ proc esc(c: char, reserved = {'\0'..'\255'}): string =
   elif c in reserved: result = '\\' & c
   else: result = $c
 
-proc singleQuoteEsc(c: char): string = return "'" & esc(c, {'\''}) & "'"
+func singleQuoteEsc(c: char): string = return "'" & esc(c, {'\''}) & "'"
 
-proc singleQuoteEsc(str: string): string =
+func singleQuoteEsc(str: string): string =
   result = "'"
   for c in items(str): add result, esc(c, {'\''})
   add result, '\''
 
-proc charSetEscAux(cc: set[char]): string =
+func charSetEscAux(cc: set[char]): string =
   const reserved = {'^', '-', ']'}
   result = ""
   var c1 = 0
@@ -414,13 +433,13 @@ proc charSetEscAux(cc: set[char]): string =
       c1 = c2
     inc(c1)
 
-proc charSetEsc(cc: set[char]): string =
+func charSetEsc(cc: set[char]): string =
   if card(cc) >= 128+64:
     result = "[^" & charSetEscAux({'\1'..'\xFF'} - cc) & ']'
   else:
     result = '[' & charSetEscAux(cc) & ']'
 
-proc toStrAux(r: Peg, res: var string) =
+func toStrAux(r: Peg, res: var string) =
   case r.kind
   of pkEmpty: add(res, "()")
   of pkAny: add(res, '.')
@@ -506,7 +525,7 @@ proc toStrAux(r: Peg, res: var string) =
   of pkStartAnchor:
     add(res, '^')
 
-proc `$` *(r: Peg): string {.nosideEffect, rtl, extern: "npegsToString".} =
+func `$` *(r: Peg): string {.rtl, extern: "npegsToString".} =
   ## converts a PEG to its string representation
   result = ""
   toStrAux(r, result)
@@ -519,9 +538,7 @@ type
     ml: int
     origStart: int
 
-{.deprecated: [TCaptures: Captures].}
-
-proc bounds*(c: Captures,
+func bounds*(c: Captures,
              i: range[0..MaxSubpatterns-1]): tuple[first, last: int] =
   ## returns the bounds ``[first..last]`` of the `i`'th capture.
   result = c.matches[i]
@@ -534,229 +551,526 @@ when not useUnicode:
     inc(i)
   template runeLenAt(s, i): untyped = 1
 
-  proc isAlpha(a: char): bool {.inline.} = return a in {'a'..'z','A'..'Z'}
-  proc isUpper(a: char): bool {.inline.} = return a in {'A'..'Z'}
-  proc isLower(a: char): bool {.inline.} = return a in {'a'..'z'}
-  proc isTitle(a: char): bool {.inline.} = return false
-  proc isWhiteSpace(a: char): bool {.inline.} = return a in {' ', '\9'..'\13'}
-
-proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
-               nosideEffect, rtl, extern: "npegs$1".} =
-  ## low-level matching proc that implements the PEG interpreter. Use this
-  ## for maximum efficiency (every other PEG operation ends up calling this
-  ## proc).
-  ## Returns -1 if it does not match, else the length of the match
-  case p.kind
-  of pkEmpty: result = 0 # match of length 0
-  of pkAny:
-    if start < s.len: result = 1
-    else: result = -1
-  of pkAnyRune:
-    if start < s.len:
-      result = runeLenAt(s, start)
-    else:
-      result = -1
-  of pkLetter:
-    if start < s.len:
-      var a: Rune
-      result = start
-      fastRuneAt(s, result, a)
-      if isAlpha(a): dec(result, start)
-      else: result = -1
-    else:
-      result = -1
-  of pkLower:
-    if start < s.len:
-      var a: Rune
-      result = start
-      fastRuneAt(s, result, a)
-      if isLower(a): dec(result, start)
-      else: result = -1
-    else:
-      result = -1
-  of pkUpper:
-    if start < s.len:
-      var a: Rune
-      result = start
-      fastRuneAt(s, result, a)
-      if isUpper(a): dec(result, start)
-      else: result = -1
-    else:
-      result = -1
-  of pkTitle:
-    if start < s.len:
-      var a: Rune
-      result = start
-      fastRuneAt(s, result, a)
-      if isTitle(a): dec(result, start)
+  func isAlpha(a: char): bool {.inline.} = return a in {'a'..'z', 'A'..'Z'}
+  func isUpper(a: char): bool {.inline.} = return a in {'A'..'Z'}
+  func isLower(a: char): bool {.inline.} = return a in {'a'..'z'}
+  func isTitle(a: char): bool {.inline.} = return false
+  func isWhiteSpace(a: char): bool {.inline.} = return a in {' ', '\9'..'\13'}
+
+template matchOrParse(mopProc: untyped) =
+  # Used to make the main matcher proc *rawMatch* as well as event parser
+  # procs. For the former, *enter* and *leave* event handler code generators
+  # are provided which just return *discard*.
+
+  proc mopProc(s: string, p: Peg, start: int, c: var Captures): int {.gcsafe, raises: [].} =
+    proc matchBackRef(s: string, p: Peg, start: int, c: var Captures): int =
+      # Parse handler code must run in an *of* clause of its own for each
+      # *PegKind*, so we encapsulate the identical clause body for
+      # *pkBackRef..pkBackRefIgnoreStyle* here.
+      var index = p.index
+      if index < 0: index.inc(c.ml)
+      if index < 0 or index >= c.ml: return -1
+      var (a, b) = c.matches[index]
+      var n: Peg
+      case p.kind
+      of pkBackRef:
+        n = Peg(kind: pkTerminal, term: s.substr(a, b))
+      of pkBackRefIgnoreStyle:
+        n = Peg(kind: pkTerminalIgnoreStyle, term: s.substr(a, b))
+      of pkBackRefIgnoreCase:
+        n = Peg(kind: pkTerminalIgnoreCase, term: s.substr(a, b))
+      else: assert(false, "impossible case")
+      mopProc(s, n, start, c)
+
+    case p.kind
+    of pkEmpty:
+      enter(pkEmpty, s, p, start)
+      result = 0 # match of length 0
+      leave(pkEmpty, s, p, start, result)
+    of pkAny:
+      enter(pkAny, s, p, start)
+      if start < s.len: result = 1
       else: result = -1
-    else:
-      result = -1
-  of pkWhitespace:
-    if start < s.len:
-      var a: Rune
-      result = start
-      fastRuneAt(s, result, a)
-      if isWhiteSpace(a): dec(result, start)
-      else: result = -1
-    else:
-      result = -1
-  of pkGreedyAny:
-    result = len(s) - start
-  of pkNewLine:
-    if start < s.len and s[start] == '\L': result = 1
-    elif start < s.len and s[start] == '\C':
-      if start+1 < s.len and s[start+1] == '\L': result = 2
-      else: result = 1
-    else: result = -1
-  of pkTerminal:
-    result = len(p.term)
-    for i in 0..result-1:
-      if start+i >= s.len or p.term[i] != s[start+i]:
+      leave(pkAny, s, p, start, result)
+    of pkAnyRune:
+      enter(pkAnyRune, s, p, start)
+      if start < s.len:
+        result = runeLenAt(s, start)
+      else:
         result = -1
-        break
-  of pkTerminalIgnoreCase:
-    var
-      i = 0
-      a, b: Rune
-    result = start
-    while i < len(p.term):
-      if result >= s.len:
+      leave(pkAnyRune, s, p, start, result)
+    of pkLetter:
+      enter(pkLetter, s, p, start)
+      if start < s.len:
+        var a: Rune
+        result = start
+        fastRuneAt(s, result, a)
+        if isAlpha(a): dec(result, start)
+        else: result = -1
+      else:
         result = -1
-        break
-      fastRuneAt(p.term, i, a)
-      fastRuneAt(s, result, b)
-      if toLower(a) != toLower(b):
+      leave(pkLetter, s, p, start, result)
+    of pkLower:
+      enter(pkLower, s, p, start)
+      if start < s.len:
+        var a: Rune
+        result = start
+        fastRuneAt(s, result, a)
+        if isLower(a): dec(result, start)
+        else: result = -1
+      else:
         result = -1
-        break
-    dec(result, start)
-  of pkTerminalIgnoreStyle:
-    var
-      i = 0
-      a, b: Rune
-    result = start
-    while i < len(p.term):
+      leave(pkLower, s, p, start, result)
+    of pkUpper:
+      enter(pkUpper, s, p, start)
+      if start < s.len:
+        var a: Rune
+        result = start
+        fastRuneAt(s, result, a)
+        if isUpper(a): dec(result, start)
+        else: result = -1
+      else:
+        result = -1
+      leave(pkUpper, s, p, start, result)
+    of pkTitle:
+      enter(pkTitle, s, p, start)
+      if start < s.len:
+        var a: Rune
+        result = start
+        fastRuneAt(s, result, a)
+        if isTitle(a): dec(result, start)
+        else: result = -1
+      else:
+        result = -1
+      leave(pkTitle, s, p, start, result)
+    of pkWhitespace:
+      enter(pkWhitespace, s, p, start)
+      if start < s.len:
+        var a: Rune
+        result = start
+        fastRuneAt(s, result, a)
+        if isWhiteSpace(a): dec(result, start)
+        else: result = -1
+      else:
+        result = -1
+      leave(pkWhitespace, s, p, start, result)
+    of pkGreedyAny:
+      enter(pkGreedyAny, s, p, start)
+      result = len(s) - start
+      leave(pkGreedyAny, s, p, start, result)
+    of pkNewLine:
+      enter(pkNewLine, s, p, start)
+      if start < s.len and s[start] == '\L': result = 1
+      elif start < s.len and s[start] == '\C':
+        if start+1 < s.len and s[start+1] == '\L': result = 2
+        else: result = 1
+      else: result = -1
+      leave(pkNewLine, s, p, start, result)
+    of pkTerminal:
+      enter(pkTerminal, s, p, start)
+      result = len(p.term)
+      for i in 0..result-1:
+        if start+i >= s.len or p.term[i] != s[start+i]:
+          result = -1
+          break
+      leave(pkTerminal, s, p, start, result)
+    of pkTerminalIgnoreCase:
+      enter(pkTerminalIgnoreCase, s, p, start)
+      var
+        i = 0
+        a, b: Rune
+      result = start
       while i < len(p.term):
+        if result >= s.len:
+          result = -1
+          break
         fastRuneAt(p.term, i, a)
-        if a != Rune('_'): break
-      while result < s.len:
         fastRuneAt(s, result, b)
-        if b != Rune('_'): break
-      if result >= s.len:
-        if i >= p.term.len: break
-        else:
+        if toLower(a) != toLower(b):
           result = -1
           break
-      elif toLower(a) != toLower(b):
-        result = -1
-        break
-    dec(result, start)
-  of pkChar:
-    if start < s.len and p.ch == s[start]: result = 1
-    else: result = -1
-  of pkCharChoice:
-    if start < s.len and contains(p.charChoice[], s[start]): result = 1
-    else: result = -1
-  of pkNonTerminal:
-    var oldMl = c.ml
-    when false: echo "enter: ", p.nt.name
-    result = rawMatch(s, p.nt.rule, start, c)
-    when false: echo "leave: ", p.nt.name
-    if result < 0: c.ml = oldMl
-  of pkSequence:
-    var oldMl = c.ml
-    result = 0
-    for i in 0..high(p.sons):
-      var x = rawMatch(s, p.sons[i], start+result, c)
-      if x < 0:
+      dec(result, start)
+      leave(pkTerminalIgnoreCase, s, p, start, result)
+    of pkTerminalIgnoreStyle:
+      enter(pkTerminalIgnoreStyle, s, p, start)
+      var
+        i = 0
+        a, b: Rune
+      result = start
+      while i < len(p.term):
+        while i < len(p.term):
+          fastRuneAt(p.term, i, a)
+          if a != Rune('_'): break
+        while result < s.len:
+          fastRuneAt(s, result, b)
+          if b != Rune('_'): break
+        if result >= s.len:
+          if i >= p.term.len: break
+          else:
+            result = -1
+            break
+        elif toLower(a) != toLower(b):
+          result = -1
+          break
+      dec(result, start)
+      leave(pkTerminalIgnoreStyle, s, p, start, result)
+    of pkChar:
+      enter(pkChar, s, p, start)
+      if start < s.len and p.ch == s[start]: result = 1
+      else: result = -1
+      leave(pkChar, s, p, start, result)
+    of pkCharChoice:
+      enter(pkCharChoice, s, p, start)
+      if start < s.len and contains(p.charChoice[], s[start]): result = 1
+      else: result = -1
+      leave(pkCharChoice, s, p, start, result)
+    of pkNonTerminal:
+      enter(pkNonTerminal, s, p, start)
+      var oldMl = c.ml
+      when false: echo "enter: ", p.nt.name
+      result = mopProc(s, p.nt.rule, start, c)
+      when false: echo "leave: ", p.nt.name
+      if result < 0: c.ml = oldMl
+      leave(pkNonTerminal, s, p, start, result)
+    of pkSequence:
+      enter(pkSequence, s, p, start)
+      var oldMl = c.ml
+      result = 0
+      for i in 0..high(p.sons):
+        var x = mopProc(s, p.sons[i], start+result, c)
+        if x < 0:
+          c.ml = oldMl
+          result = -1
+          break
+        else: inc(result, x)
+      leave(pkSequence, s, p, start, result)
+    of pkOrderedChoice:
+      enter(pkOrderedChoice, s, p, start)
+      var oldMl = c.ml
+      for i in 0..high(p.sons):
+        result = mopProc(s, p.sons[i], start, c)
+        if result >= 0: break
         c.ml = oldMl
-        result = -1
-        break
-      else: inc(result, x)
-  of pkOrderedChoice:
-    var oldMl = c.ml
-    for i in 0..high(p.sons):
-      result = rawMatch(s, p.sons[i], start, c)
-      if result >= 0: break
-      c.ml = oldMl
-  of pkSearch:
-    var oldMl = c.ml
-    result = 0
-    while start+result <= s.len:
-      var x = rawMatch(s, p.sons[0], start+result, c)
-      if x >= 0:
-        inc(result, x)
-        return
-      inc(result)
-    result = -1
-    c.ml = oldMl
-  of pkCapturedSearch:
-    var idx = c.ml # reserve a slot for the subpattern
-    inc(c.ml)
-    result = 0
-    while start+result <= s.len:
-      var x = rawMatch(s, p.sons[0], start+result, c)
-      if x >= 0:
-        if idx < MaxSubpatterns:
-          c.matches[idx] = (start, start+result-1)
-        #else: silently ignore the capture
-        inc(result, x)
-        return
-      inc(result)
-    result = -1
-    c.ml = idx
-  of pkGreedyRep:
-    result = 0
-    while true:
-      var x = rawMatch(s, p.sons[0], start+result, c)
-      # if x == 0, we have an endless loop; so the correct behaviour would be
-      # not to break. But endless loops can be easily introduced:
-      # ``(comment / \w*)*`` is such an example. Breaking for x == 0 does the
-      # expected thing in this case.
-      if x <= 0: break
-      inc(result, x)
-  of pkGreedyRepChar:
-    result = 0
-    var ch = p.ch
-    while start+result < s.len and ch == s[start+result]: inc(result)
-  of pkGreedyRepSet:
-    result = 0
-    while start+result < s.len and contains(p.charChoice[], s[start+result]): inc(result)
-  of pkOption:
-    result = max(0, rawMatch(s, p.sons[0], start, c))
-  of pkAndPredicate:
-    var oldMl = c.ml
-    result = rawMatch(s, p.sons[0], start, c)
-    if result >= 0: result = 0 # do not consume anything
-    else: c.ml = oldMl
-  of pkNotPredicate:
-    var oldMl = c.ml
-    result = rawMatch(s, p.sons[0], start, c)
-    if result < 0: result = 0
-    else:
+      leave(pkOrderedChoice, s, p, start, result)
+    of pkSearch:
+      enter(pkSearch, s, p, start)
+      var oldMl = c.ml
+      result = 0
+      while start+result <= s.len:
+        var x = mopProc(s, p.sons[0], start+result, c)
+        if x >= 0:
+          inc(result, x)
+          leave(pkSearch, s, p, start, result)
+          return
+        inc(result)
+      result = -1
       c.ml = oldMl
+      leave(pkSearch, s, p, start, result)
+    of pkCapturedSearch:
+      enter(pkCapturedSearch, s, p, start)
+      var idx = c.ml # reserve a slot for the subpattern
+      inc(c.ml)
+      result = 0
+      while start+result <= s.len:
+        var x = mopProc(s, p.sons[0], start+result, c)
+        if x >= 0:
+          if idx < MaxSubpatterns:
+            c.matches[idx] = (start, start+result-1)
+          #else: silently ignore the capture
+          inc(result, x)
+          leave(pkCapturedSearch, s, p, start, result)
+          return
+        inc(result)
       result = -1
-  of pkCapture:
-    var idx = c.ml # reserve a slot for the subpattern
-    inc(c.ml)
-    result = rawMatch(s, p.sons[0], start, c)
-    if result >= 0:
-      if idx < MaxSubpatterns:
-        c.matches[idx] = (start, start+result-1)
-      #else: silently ignore the capture
-    else:
       c.ml = idx
-  of pkBackRef..pkBackRefIgnoreStyle:
-    if p.index >= c.ml: return -1
-    var (a, b) = c.matches[p.index]
-    var n: Peg
-    n.kind = succ(pkTerminal, ord(p.kind)-ord(pkBackRef))
-    n.term = s.substr(a, b)
-    result = rawMatch(s, n, start, c)
-  of pkStartAnchor:
-    if c.origStart == start: result = 0
-    else: result = -1
-  of pkRule, pkList: assert false
+      leave(pkCapturedSearch, s, p, start, result)
+    of pkGreedyRep:
+      enter(pkGreedyRep, s, p, start)
+      result = 0
+      while true:
+        var x = mopProc(s, p.sons[0], start+result, c)
+        # if x == 0, we have an endless loop; so the correct behaviour would be
+        # not to break. But endless loops can be easily introduced:
+        # ``(comment / \w*)*`` is such an example. Breaking for x == 0 does the
+        # expected thing in this case.
+        if x <= 0: break
+        inc(result, x)
+      leave(pkGreedyRep, s, p, start, result)
+    of pkGreedyRepChar:
+      enter(pkGreedyRepChar, s, p, start)
+      result = 0
+      var ch = p.ch
+      while start+result < s.len and ch == s[start+result]: inc(result)
+      leave(pkGreedyRepChar, s, p, start, result)
+    of pkGreedyRepSet:
+      enter(pkGreedyRepSet, s, p, start)
+      result = 0
+      while start+result < s.len and contains(p.charChoice[], s[start+result]):
+        inc(result)
+      leave(pkGreedyRepSet, s, p, start, result)
+    of pkOption:
+      enter(pkOption, s, p, start)
+      result = max(0, mopProc(s, p.sons[0], start, c))
+      leave(pkOption, s, p, start, result)
+    of pkAndPredicate:
+      enter(pkAndPredicate, s, p, start)
+      var oldMl = c.ml
+      result = mopProc(s, p.sons[0], start, c)
+      if result >= 0: result = 0 # do not consume anything
+      else: c.ml = oldMl
+      leave(pkAndPredicate, s, p, start, result)
+    of pkNotPredicate:
+      enter(pkNotPredicate, s, p, start)
+      var oldMl = c.ml
+      result = mopProc(s, p.sons[0], start, c)
+      if result < 0: result = 0
+      else:
+        c.ml = oldMl
+        result = -1
+      leave(pkNotPredicate, s, p, start, result)
+    of pkCapture:
+      enter(pkCapture, s, p, start)
+      if p.sons.len == 0 or p.sons[0].kind == pkEmpty:
+        # empty capture removes last match
+        dec(c.ml)
+        c.matches[c.ml] = (0, 0)
+        result = 0 # match of length 0
+      else:
+        var idx = c.ml # reserve a slot for the subpattern
+        result = mopProc(s, p.sons[0], start, c)
+        if result >= 0:
+          if idx < MaxSubpatterns:
+            if idx != c.ml:
+              for i in countdown(c.ml, idx):
+                c.matches[i+1] = c.matches[i]
+            c.matches[idx] = (start, start+result-1)
+          #else: silently ignore the capture
+          inc(c.ml)
+      leave(pkCapture, s, p, start, result)
+    of pkBackRef:
+      enter(pkBackRef, s, p, start)
+      result = matchBackRef(s, p, start, c)
+      leave(pkBackRef, s, p, start, result)
+    of pkBackRefIgnoreCase:
+      enter(pkBackRefIgnoreCase, s, p, start)
+      result = matchBackRef(s, p, start, c)
+      leave(pkBackRefIgnoreCase, s, p, start, result)
+    of pkBackRefIgnoreStyle:
+      enter(pkBackRefIgnoreStyle, s, p, start)
+      result = matchBackRef(s, p, start, c)
+      leave(pkBackRefIgnoreStyle, s, p, start, result)
+    of pkStartAnchor:
+      enter(pkStartAnchor, s, p, start)
+      if c.origStart == start: result = 0
+      else: result = -1
+      leave(pkStartAnchor, s, p, start, result)
+    of pkRule, pkList: assert false
+
+func rawMatch*(s: string, p: Peg, start: int, c: var Captures): int
+      {.rtl, extern: "npegs$1".} =
+  ## low-level matching proc that implements the PEG interpreter. Use this
+  ## for maximum efficiency (every other PEG operation ends up calling this
+  ## proc).
+  ## Returns -1 if it does not match, else the length of the match
+
+  # Set the handler generators to produce do-nothing handlers.
+  template enter(pk, s, p, start) =
+    discard
+  template leave(pk, s, p, start, length) =
+    discard
+  matchOrParse(matchIt)
+  {.cast(noSideEffect).}:
+    # This cast is allowed because the `matchOrParse` template is used for
+    # both matching and parsing, but side effects are only possible when it's
+    # used by `eventParser`.
+    result = matchIt(s, p, start, c)
+
+macro mkHandlerTplts(handlers: untyped): untyped =
+  # Transforms the handler spec in *handlers* into handler templates.
+  # The AST structure of *handlers[0]*:
+  #
+  #   ```
+  #   StmtList
+  #     Call
+  #       Ident "pkNonTerminal"
+  #       StmtList
+  #         Call
+  #           Ident "enter"
+  #           StmtList
+  #             <handler code block>
+  #         Call
+  #           Ident "leave"
+  #           StmtList
+  #             <handler code block>
+  #     Call
+  #       Ident "pkChar"
+  #       StmtList
+  #         Call
+  #           Ident "leave"
+  #           StmtList
+  #             <handler code block>
+  #     ...
+  #   ```
+  func mkEnter(hdName, body: NimNode): NimNode =
+    template helper(hdName, body) {.dirty.} =
+      template hdName(s, p, start) =
+        let s {.inject.} = s
+        let p {.inject.} = p
+        let start {.inject.} = start
+        body
+    result = getAst(helper(hdName, body))
+
+  template mkLeave(hdPostf, body) {.dirty.} =
+    # this has to be dirty to be able to capture *result* as *length* in
+    # *leaveXX* calls.
+    template `leave hdPostf`(s, p, start, length) =
+      body
+
+  result = newStmtList()
+  for topCall in handlers[0]:
+    if topCall.kind notin nnkCallKinds:
+      error("Call syntax expected.", topCall)
+    let pegKind = topCall[0]
+    if pegKind.kind notin {nnkIdent, nnkSym}:
+      error("PegKind expected.", pegKind)
+    if 2 == topCall.len:
+      for hdDef in topCall[1]:
+        if hdDef.kind notin nnkCallKinds:
+          error("Call syntax expected.", hdDef)
+        if hdDef[0].kind notin {nnkIdent, nnkSym}:
+          error("Handler identifier expected.", hdDef[0])
+        if 2 == hdDef.len:
+          let hdPostf = substr(pegKind.strVal, 2)
+          case hdDef[0].strVal
+          of "enter":
+            result.add mkEnter(newIdentNode("enter" & hdPostf), hdDef[1])
+          of "leave":
+            result.add getAst(mkLeave(ident(hdPostf), hdDef[1]))
+          else:
+            error(
+              "Unsupported handler identifier, expected 'enter' or 'leave'.",
+              hdDef[0]
+            )
+
+template eventParser*(pegAst, handlers: untyped): (proc(s: string): int) =
+  ## Generates an interpreting event parser *proc* according to the specified
+  ## PEG AST and handler code blocks. The *proc* can be called with a string
+  ## to be parsed and will execute the handler code blocks whenever their
+  ## associated grammar element is matched. It returns -1 if the string does not
+  ## match, else the length of the total match. The following example code
+  ## evaluates an arithmetic expression defined by a simple PEG:
+  ##
+  ##   ```nim
+  ##   import std/[strutils, pegs]
+  ##
+  ##   let
+  ##     pegAst = """
+  ##   Expr    <- Sum
+  ##   Sum     <- Product (('+' / '-')Product)*
+  ##   Product <- Value (('*' / '/')Value)*
+  ##   Value   <- [0-9]+ / '(' Expr ')'
+  ##     """.peg
+  ##     txt = "(5+3)/2-7*22"
+  ##
+  ##   var
+  ##     pStack: seq[string] = @[]
+  ##     valStack: seq[float] = @[]
+  ##     opStack = ""
+  ##   let
+  ##     parseArithExpr = pegAst.eventParser:
+  ##       pkNonTerminal:
+  ##         enter:
+  ##           pStack.add p.nt.name
+  ##         leave:
+  ##           pStack.setLen pStack.high
+  ##           if length > 0:
+  ##             let matchStr = s.substr(start, start+length-1)
+  ##             case p.nt.name
+  ##             of "Value":
+  ##               try:
+  ##                 valStack.add matchStr.parseFloat
+  ##                 echo valStack
+  ##               except ValueError:
+  ##                 discard
+  ##             of "Sum", "Product":
+  ##               try:
+  ##                 let val = matchStr.parseFloat
+  ##               except ValueError:
+  ##                 if valStack.len > 1 and opStack.len > 0:
+  ##                   valStack[^2] = case opStack[^1]
+  ##                   of '+': valStack[^2] + valStack[^1]
+  ##                   of '-': valStack[^2] - valStack[^1]
+  ##                   of '*': valStack[^2] * valStack[^1]
+  ##                   else: valStack[^2] / valStack[^1]
+  ##                   valStack.setLen valStack.high
+  ##                   echo valStack
+  ##                   opStack.setLen opStack.high
+  ##                   echo opStack
+  ##       pkChar:
+  ##         leave:
+  ##           if length == 1 and "Value" != pStack[^1]:
+  ##             let matchChar = s[start]
+  ##             opStack.add matchChar
+  ##             echo opStack
+  ##
+  ##   let pLen = parseArithExpr(txt)
+  ##   ```
+  ##
+  ## The *handlers* parameter consists of code blocks for *PegKinds*,
+  ## which define the grammar elements of interest. Each block can contain
+  ## handler code to be executed when the parser enters and leaves text
+  ## matching the grammar element. An *enter* handler can access the specific
+  ## PEG AST node being matched as *p*, the entire parsed string as *s*
+  ## and the position of the matched text segment in *s* as *start*. A *leave*
+  ## handler can access *p*, *s*, *start* and also the length of the matched
+  ## text segment as *length*. For an unsuccessful match, the *enter* and
+  ## *leave* handlers will be executed, with *length* set to -1.
+  ##
+  ## Symbols  declared in an *enter* handler can be made visible in the
+  ## corresponding *leave* handler by annotating them with an *inject* pragma.
+  proc rawParse(s: string, p: Peg, start: int, c: var Captures): int
+      {.gensym.} =
+
+    # binding from *macros*
+    bind strVal
+
+    mkHandlerTplts:
+      handlers
+
+    macro enter(pegKind, s, pegNode, start: untyped): untyped =
+      # This is called by the matcher code in *matchOrParse* at the
+      # start of the code for a grammar element of kind *pegKind*.
+      # Expands to a call to the handler template if one was generated
+      # by *mkHandlerTplts*.
+      template mkDoEnter(hdPostf, s, pegNode, start) =
+        when declared(`enter hdPostf`):
+          `enter hdPostf`(s, pegNode, start)
+        else:
+          discard
+      let hdPostf = ident(substr($pegKind, 2))
+      getAst(mkDoEnter(hdPostf, s, pegNode, start))
+
+    macro leave(pegKind, s, pegNode, start, length: untyped): untyped =
+      # Like *enter*, but called at the end of the matcher code for
+      # a grammar element of kind *pegKind*.
+      template mkDoLeave(hdPostf, s, pegNode, start, length) =
+        when declared(`leave hdPostf`):
+          `leave hdPostf`(s, pegNode, start, length)
+        else:
+          discard
+      let hdPostf = ident(substr($pegKind, 2))
+      getAst(mkDoLeave(hdPostf, s, pegNode, start, length))
+
+    matchOrParse(parseIt)
+    parseIt(s, p, start, c)
+
+  proc parser(s: string): int {.gensym.} =
+    # the proc to be returned
+    var
+      ms: array[MaxSubpatterns, (int, int)]
+      cs = Captures(matches: ms, ml: 0, origStart: 0)
+    rawParse(s, pegAst, 0, cs)
+  parser
 
 template fillMatches(s, caps, c) =
   for k in 0..c.ml-1:
@@ -765,10 +1079,10 @@ template fillMatches(s, caps, c) =
     if startIdx != -1:
       caps[k] = substr(s, startIdx, endIdx)
     else:
-      caps[k] = nil
+      caps[k] = ""
 
-proc matchLen*(s: string, pattern: Peg, matches: var openArray[string],
-               start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
+func matchLen*(s: string, pattern: Peg, matches: var openArray[string],
+               start = 0): int {.rtl, extern: "npegs$1Capture".} =
   ## the same as ``match``, but it returns the length of the match,
   ## if there is no match, -1 is returned. Note that a match length
   ## of zero can happen. It's possible that a suffix of `s` remains
@@ -778,8 +1092,8 @@ proc matchLen*(s: string, pattern: Peg, matches: var openArray[string],
   result = rawMatch(s, pattern, start, c)
   if result >= 0: fillMatches(s, matches, c)
 
-proc matchLen*(s: string, pattern: Peg,
-               start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
+func matchLen*(s: string, pattern: Peg,
+               start = 0): int {.rtl, extern: "npegs$1".} =
   ## the same as ``match``, but it returns the length of the match,
   ## if there is no match, -1 is returned. Note that a match length
   ## of zero can happen. It's possible that a suffix of `s` remains
@@ -788,22 +1102,22 @@ proc matchLen*(s: string, pattern: Peg,
   c.origStart = start
   result = rawMatch(s, pattern, start, c)
 
-proc match*(s: string, pattern: Peg, matches: var openArray[string],
-            start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} =
+func match*(s: string, pattern: Peg, matches: var openArray[string],
+            start = 0): bool {.rtl, extern: "npegs$1Capture".} =
   ## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
   ## the captured substrings in the array ``matches``. If it does not
   ## match, nothing is written into ``matches`` and ``false`` is
   ## returned.
   result = matchLen(s, pattern, matches, start) != -1
 
-proc match*(s: string, pattern: Peg,
-            start = 0): bool {.nosideEffect, rtl, extern: "npegs$1".} =
+func match*(s: string, pattern: Peg,
+            start = 0): bool {.rtl, extern: "npegs$1".} =
   ## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``.
   result = matchLen(s, pattern, start) != -1
 
 
-proc find*(s: string, pattern: Peg, matches: var openArray[string],
-           start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
+func find*(s: string, pattern: Peg, matches: var openArray[string],
+           start = 0): int {.rtl, extern: "npegs$1Capture".} =
   ## returns the starting position of ``pattern`` in ``s`` and the captured
   ## substrings in the array ``matches``. If it does not match, nothing
   ## is written into ``matches`` and -1 is returned.
@@ -817,9 +1131,9 @@ proc find*(s: string, pattern: Peg, matches: var openArray[string],
   return -1
   # could also use the pattern here: (!P .)* P
 
-proc findBounds*(s: string, pattern: Peg, matches: var openArray[string],
+func findBounds*(s: string, pattern: Peg, matches: var openArray[string],
                  start = 0): tuple[first, last: int] {.
-                 nosideEffect, rtl, extern: "npegs$1Capture".} =
+                 rtl, extern: "npegs$1Capture".} =
   ## returns the starting position and end position of ``pattern`` in ``s``
   ## and the captured
   ## substrings in the array ``matches``. If it does not match, nothing
@@ -834,8 +1148,8 @@ proc findBounds*(s: string, pattern: Peg, matches: var openArray[string],
       return (i, i+L-1)
   return (-1, 0)
 
-proc find*(s: string, pattern: Peg,
-           start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
+func find*(s: string, pattern: Peg,
+           start = 0): int {.rtl, extern: "npegs$1".} =
   ## returns the starting position of ``pattern`` in ``s``. If it does not
   ## match, -1 is returned.
   var c: Captures
@@ -858,21 +1172,18 @@ iterator findAll*(s: string, pattern: Peg, start = 0): string =
       yield substr(s, i, i+L-1)
       inc(i, L)
 
-proc findAll*(s: string, pattern: Peg, start = 0): seq[string] {.
-  nosideEffect, rtl, extern: "npegs$1".} =
+func findAll*(s: string, pattern: Peg, start = 0): seq[string] {.
+  rtl, extern: "npegs$1".} =
   ## returns all matching *substrings* of `s` that match `pattern`.
-  ## If it does not match, @[] is returned.
-  accumulateResult(findAll(s, pattern, start))
-
-when not defined(nimhygiene):
-  {.pragma: inject.}
+  ## If it does not match, `@[]` is returned.
+  result = @[]
+  for it in findAll(s, pattern, start): result.add it
 
 template `=~`*(s: string, pattern: Peg): bool =
   ## This calls ``match`` with an implicit declared ``matches`` array that
   ## can be used in the scope of the ``=~`` call:
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}":
   ##     # matches a key=value pair:
   ##     echo("Key: ", matches[0])
@@ -884,50 +1195,51 @@ template `=~`*(s: string, pattern: Peg): bool =
   ##     echo("comment: ", matches[0])
   ##   else:
   ##     echo("syntax error")
-  ##
+  ##   ```
   bind MaxSubpatterns
   when not declaredInScope(matches):
-    var matches {.inject.}: array[0..MaxSubpatterns-1, string]
+    var matches {.inject.} = default(array[0..MaxSubpatterns-1, string])
   match(s, pattern, matches)
 
 # ------------------------- more string handling ------------------------------
 
-proc contains*(s: string, pattern: Peg, start = 0): bool {.
-  nosideEffect, rtl, extern: "npegs$1".} =
+func contains*(s: string, pattern: Peg, start = 0): bool {.
+  rtl, extern: "npegs$1".} =
   ## same as ``find(s, pattern, start) >= 0``
   return find(s, pattern, start) >= 0
 
-proc contains*(s: string, pattern: Peg, matches: var openArray[string],
-              start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} =
+func contains*(s: string, pattern: Peg, matches: var openArray[string],
+              start = 0): bool {.rtl, extern: "npegs$1Capture".} =
   ## same as ``find(s, pattern, matches, start) >= 0``
   return find(s, pattern, matches, start) >= 0
 
-proc startsWith*(s: string, prefix: Peg, start = 0): bool {.
-  nosideEffect, rtl, extern: "npegs$1".} =
+func startsWith*(s: string, prefix: Peg, start = 0): bool {.
+  rtl, extern: "npegs$1".} =
   ## returns true if `s` starts with the pattern `prefix`
   result = matchLen(s, prefix, start) >= 0
 
-proc endsWith*(s: string, suffix: Peg, start = 0): bool {.
-  nosideEffect, rtl, extern: "npegs$1".} =
+func endsWith*(s: string, suffix: Peg, start = 0): bool {.
+  rtl, extern: "npegs$1".} =
   ## returns true if `s` ends with the pattern `suffix`
   var c: Captures
   c.origStart = start
   for i in start .. s.len-1:
     if rawMatch(s, suffix, i, c) == s.len - i: return true
 
-proc replacef*(s: string, sub: Peg, by: string): string {.
-  nosideEffect, rtl, extern: "npegs$1".} =
+func replacef*(s: string, sub: Peg, by: string): string {.
+  rtl, extern: "npegs$1".} =
   ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
   ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   "var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2")
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   "var1<-keykey; val2<-key2key2"
+  ##   ```
   result = ""
   var i = 0
   var caps: array[0..MaxSubpatterns-1, string]
@@ -944,8 +1256,8 @@ proc replacef*(s: string, sub: Peg, by: string): string {.
       inc(i, x)
   add(result, substr(s, i))
 
-proc replace*(s: string, sub: Peg, by = ""): string {.
-  nosideEffect, rtl, extern: "npegs$1".} =
+func replace*(s: string, sub: Peg, by = ""): string {.
+  rtl, extern: "npegs$1".} =
   ## Replaces `sub` in `s` by the string `by`. Captures cannot be accessed
   ## in `by`.
   result = ""
@@ -961,9 +1273,9 @@ proc replace*(s: string, sub: Peg, by = ""): string {.
       inc(i, x)
   add(result, substr(s, i))
 
-proc parallelReplace*(s: string, subs: varargs[
+func parallelReplace*(s: string, subs: varargs[
                       tuple[pattern: Peg, repl: string]]): string {.
-                      nosideEffect, rtl, extern: "npegs$1".} =
+                      rtl, extern: "npegs$1".} =
   ## Returns a modified copy of `s` with the substitutions in `subs`
   ## applied in parallel.
   result = ""
@@ -985,16 +1297,18 @@ proc parallelReplace*(s: string, subs: varargs[
   # copy the rest:
   add(result, substr(s, i))
 
-proc replace*(s: string, sub: Peg, cb: proc(
+when not defined(nimHasEffectsOf):
+  {.pragma: effectsOf.}
+
+func replace*(s: string, sub: Peg, cb: proc(
               match: int, cnt: int, caps: openArray[string]): string): string {.
-              rtl, extern: "npegs$1cb".}=
+              rtl, extern: "npegs$1cb", effectsOf: cb.} =
   ## Replaces `sub` in `s` by the resulting strings from the callback.
   ## The callback proc receives the index of the current match (starting with 0),
   ## the count of captures and an open array with the captures of each match. Examples:
   ##
-  ## .. code-block:: nim
-  ##
-  ##   proc handleMatches*(m: int, n: int, c: openArray[string]): string =
+  ##   ```nim
+  ##   func handleMatches*(m: int, n: int, c: openArray[string]): string =
   ##     result = ""
   ##     if m > 0:
   ##       result.add ", "
@@ -1005,12 +1319,13 @@ proc replace*(s: string, sub: Peg, cb: proc(
   ##
   ##   let s = "Var1=key1;var2=Key2;   VAR3"
   ##   echo s.replace(peg"{\ident}('='{\ident})* ';'* \s*", handleMatches)
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   "var1: 'key1', var2: 'Key2', var3: ''"
+  ##   ```
   result = ""
   var i = 0
   var caps: array[0..MaxSubpatterns-1, string]
@@ -1034,11 +1349,11 @@ when not defined(js):
                       subs: varargs[tuple[pattern: Peg, repl: string]]) {.
                       rtl, extern: "npegs$1".} =
     ## reads in the file `infile`, performs a parallel replacement (calls
-    ## `parallelReplace`) and writes back to `outfile`. Raises ``EIO`` if an
+    ## `parallelReplace`) and writes back to `outfile`. Raises ``IOError`` if an
     ## error occurs. This is supposed to be used for quick scripting.
     ##
     ## **Note**: this proc does not exist while using the JS backend.
-    var x = readFile(infile).string
+    var x = readFile(infile)
     writeFile(outfile, x.parallelReplace(subs))
 
 
@@ -1048,18 +1363,19 @@ iterator split*(s: string, sep: Peg): string =
   ## Substrings are separated by the PEG `sep`.
   ## Examples:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   for word in split("00232this02939is39an22example111", peg"\d+"):
   ##     writeLine(stdout, word)
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   "this"
   ##   "is"
   ##   "an"
   ##   "example"
-  ##
+  ##   ```
   var c: Captures
   var
     first = 0
@@ -1077,10 +1393,11 @@ iterator split*(s: string, sep: Peg): string =
     if first < last:
       yield substr(s, first, last-1)
 
-proc split*(s: string, sep: Peg): seq[string] {.
-  nosideEffect, rtl, extern: "npegs$1".} =
+func split*(s: string, sep: Peg): seq[string] {.
+  rtl, extern: "npegs$1".} =
   ## Splits the string `s` into substrings.
-  accumulateResult(split(s, sep))
+  result = @[]
+  for it in split(s, sep): result.add it
 
 # ------------------- scanner -------------------------------------------------
 
@@ -1090,70 +1407,71 @@ type
     modVerbatim,
     modIgnoreCase,
     modIgnoreStyle
-  TokKind = enum       ## enumeration of all tokens
-    tkInvalid,          ## invalid token
-    tkEof,              ## end of file reached
-    tkAny,              ## .
-    tkAnyRune,          ## _
-    tkIdentifier,       ## abc
-    tkStringLit,        ## "abc" or 'abc'
-    tkCharSet,          ## [^A-Z]
-    tkParLe,            ## '('
-    tkParRi,            ## ')'
-    tkCurlyLe,          ## '{'
-    tkCurlyRi,          ## '}'
-    tkCurlyAt,          ## '{@}'
-    tkArrow,            ## '<-'
-    tkBar,              ## '/'
-    tkStar,             ## '*'
-    tkPlus,             ## '+'
-    tkAmp,              ## '&'
-    tkNot,              ## '!'
-    tkOption,           ## '?'
-    tkAt,               ## '@'
-    tkBuiltin,          ## \identifier
-    tkEscaped,          ## \\
-    tkBackref,          ## '$'
-    tkDollar,           ## '$'
-    tkHat               ## '^'
-
-  Token {.final.} = object  ## a token
-    kind: TokKind           ## the type of the token
+  TokKind = enum  ## enumeration of all tokens
+    tkInvalid,    ## invalid token
+    tkEof,        ## end of file reached
+    tkAny,        ## .
+    tkAnyRune,    ## _
+    tkIdentifier, ## abc
+    tkStringLit,  ## "abc" or 'abc'
+    tkCharSet,    ## [^A-Z]
+    tkParLe,      ## '('
+    tkParRi,      ## ')'
+    tkCurlyLe,    ## '{'
+    tkCurlyRi,    ## '}'
+    tkCurlyAt,    ## '{@}'
+    tkEmptyCurl,  ## '{}'
+    tkArrow,      ## '<-'
+    tkBar,        ## '/'
+    tkStar,       ## '*'
+    tkPlus,       ## '+'
+    tkAmp,        ## '&'
+    tkNot,        ## '!'
+    tkOption,     ## '?'
+    tkAt,         ## '@'
+    tkBuiltin,    ## \identifier
+    tkEscaped,    ## \\
+    tkBackref,    ## '$'
+    tkDollar,     ## '$'
+    tkHat         ## '^'
+
+  Token {.final.} = object ## a token
+    kind: TokKind          ## the type of the token
     modifier: Modifier
-    literal: string          ## the parsed (string) literal
-    charset: set[char]       ## if kind == tkCharSet
-    index: int               ## if kind == tkBackref
-
-  PegLexer {.inheritable.} = object          ## the lexer object.
-    bufpos: int               ## the current position within the buffer
-    buf: cstring              ## the buffer itself
-    lineNumber: int           ## the current line number
-    lineStart: int            ## index of last line start in buffer
-    colOffset: int            ## column to add
+    literal: string        ## the parsed (string) literal
+    charset: set[char]     ## if kind == tkCharSet
+    index: int             ## if kind == tkBackref
+
+  PegLexer {.inheritable.} = object ## the lexer object.
+    bufpos: int                     ## the current position within the buffer
+    buf: string                     ## the buffer itself
+    lineNumber: int                 ## the current line number
+    lineStart: int                  ## index of last line start in buffer
+    colOffset: int                  ## column to add
     filename: string
 
 const
   tokKindToStr: array[TokKind, string] = [
     "invalid", "[EOF]", ".", "_", "identifier", "string literal",
-    "character set", "(", ")", "{", "}", "{@}",
+    "character set", "(", ")", "{", "}", "{@}", "{}",
     "<-", "/", "*", "+", "&", "!", "?",
     "@", "built-in", "escaped", "$", "$", "^"
   ]
 
-proc handleCR(L: var PegLexer, pos: int): int =
+func handleCR(L: var PegLexer, pos: int): int =
   assert(L.buf[pos] == '\c')
   inc(L.lineNumber)
   result = pos+1
   if result < L.buf.len and L.buf[result] == '\L': inc(result)
   L.lineStart = result
 
-proc handleLF(L: var PegLexer, pos: int): int =
+func handleLF(L: var PegLexer, pos: int): int =
   assert(L.buf[pos] == '\L')
   inc(L.lineNumber)
   result = pos+1
   L.lineStart = result
 
-proc init(L: var PegLexer, input, filename: string, line = 1, col = 0) =
+func init(L: var PegLexer, input, filename: string, line = 1, col = 0) =
   L.buf = input
   L.bufpos = 0
   L.lineNumber = line
@@ -1161,32 +1479,22 @@ proc init(L: var PegLexer, input, filename: string, line = 1, col = 0) =
   L.lineStart = 0
   L.filename = filename
 
-proc getColumn(L: PegLexer): int {.inline.} =
+func getColumn(L: PegLexer): int {.inline.} =
   result = abs(L.bufpos - L.lineStart) + L.colOffset
 
-proc getLine(L: PegLexer): int {.inline.} =
+func getLine(L: PegLexer): int {.inline.} =
   result = L.lineNumber
 
-proc errorStr(L: PegLexer, msg: string, line = -1, col = -1): string =
+func errorStr(L: PegLexer, msg: string, line = -1, col = -1): string =
   var line = if line < 0: getLine(L) else: line
   var col = if col < 0: getColumn(L) else: col
   result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg]
 
-proc handleHexChar(c: var PegLexer, xi: var int) =
-  case c.buf[c.bufpos]
-  of '0'..'9':
-    xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0'))
-    inc(c.bufpos)
-  of 'a'..'f':
-    xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10)
-    inc(c.bufpos)
-  of 'A'..'F':
-    xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
-    inc(c.bufpos)
-  else: discard
-
-proc getEscapedChar(c: var PegLexer, tok: var Token) =
+func getEscapedChar(c: var PegLexer, tok: var Token) =
   inc(c.bufpos)
+  if c.bufpos >= len(c.buf):
+    tok.kind = tkInvalid
+    return
   case c.buf[c.bufpos]
   of 'r', 'R', 'c', 'C':
     add(tok.literal, '\c')
@@ -1214,16 +1522,21 @@ proc getEscapedChar(c: var PegLexer, tok: var Token) =
     inc(c.bufpos)
   of 'x', 'X':
     inc(c.bufpos)
+    if c.bufpos >= len(c.buf):
+      tok.kind = tkInvalid
+      return
     var xi = 0
-    handleHexChar(c, xi)
-    handleHexChar(c, xi)
+    if handleHexChar(c.buf[c.bufpos], xi):
+      inc(c.bufpos)
+      if handleHexChar(c.buf[c.bufpos], xi):
+        inc(c.bufpos)
     if xi == 0: tok.kind = tkInvalid
     else: add(tok.literal, chr(xi))
   of '0'..'9':
     var val = ord(c.buf[c.bufpos]) - ord('0')
     inc(c.bufpos)
     var i = 1
-    while (i <= 3) and (c.buf[c.bufpos] in {'0'..'9'}):
+    while (c.bufpos < len(c.buf)) and (i <= 3) and (c.buf[c.bufpos] in {'0'..'9'}):
       val = val * 10 + ord(c.buf[c.bufpos]) - ord('0')
       inc(c.bufpos)
       inc(i)
@@ -1237,33 +1550,29 @@ proc getEscapedChar(c: var PegLexer, tok: var Token) =
     add(tok.literal, c.buf[c.bufpos])
     inc(c.bufpos)
 
-proc skip(c: var PegLexer) =
+func skip(c: var PegLexer) =
   var pos = c.bufpos
-  var buf = c.buf
   while pos < c.buf.len:
-    case buf[pos]
+    case c.buf[pos]
     of ' ', '\t':
       inc(pos)
     of '#':
       while (pos < c.buf.len) and
-             not (buf[pos] in {'\c', '\L', '\0'}): inc(pos)
+             not (c.buf[pos] in {'\c', '\L', '\0'}): inc(pos)
     of '\c':
       pos = handleCR(c, pos)
-      buf = c.buf
     of '\L':
       pos = handleLF(c, pos)
-      buf = c.buf
     else:
-      break                   # EndOfFile also leaves the loop
+      break # EndOfFile also leaves the loop
   c.bufpos = pos
 
-proc getString(c: var PegLexer, tok: var Token) =
+func getString(c: var PegLexer, tok: var Token) =
   tok.kind = tkStringLit
   var pos = c.bufpos + 1
-  var buf = c.buf
-  var quote = buf[pos-1]
+  var quote = c.buf[pos-1]
   while pos < c.buf.len:
-    case buf[pos]
+    case c.buf[pos]
     of '\\':
       c.bufpos = pos
       getEscapedChar(c, tok)
@@ -1271,95 +1580,101 @@ proc getString(c: var PegLexer, tok: var Token) =
     of '\c', '\L', '\0':
       tok.kind = tkInvalid
       break
-    elif buf[pos] == quote:
+    elif c.buf[pos] == quote:
       inc(pos)
       break
     else:
-      add(tok.literal, buf[pos])
+      add(tok.literal, c.buf[pos])
       inc(pos)
   c.bufpos = pos
 
-proc getDollar(c: var PegLexer, tok: var Token) =
+func getDollar(c: var PegLexer, tok: var Token) =
   var pos = c.bufpos + 1
-  var buf = c.buf
-  if buf[pos] in {'0'..'9'}:
+  var neg = false
+  if pos < c.buf.len and c.buf[pos] == '^':
+    neg = true
+    inc(pos)
+  if pos < c.buf.len and c.buf[pos] in {'0'..'9'}:
     tok.kind = tkBackref
     tok.index = 0
-    while pos < c.buf.len and buf[pos] in {'0'..'9'}:
-      tok.index = tok.index * 10 + ord(buf[pos]) - ord('0')
+    while pos < c.buf.len and c.buf[pos] in {'0'..'9'}:
+      tok.index = tok.index * 10 + ord(c.buf[pos]) - ord('0')
       inc(pos)
+    if neg:
+      tok.index = -tok.index
   else:
+    if neg:
+      dec(pos)
     tok.kind = tkDollar
   c.bufpos = pos
 
-proc getCharSet(c: var PegLexer, tok: var Token) =
+func getCharSet(c: var PegLexer, tok: var Token) =
   tok.kind = tkCharSet
   tok.charset = {}
   var pos = c.bufpos + 1
-  var buf = c.buf
   var caret = false
-  if buf[pos] == '^':
-    inc(pos)
-    caret = true
-  while pos < c.buf.len:
-    var ch: char
-    case buf[pos]
-    of ']':
-      if pos < c.buf.len: inc(pos)
-      break
-    of '\\':
-      c.bufpos = pos
-      getEscapedChar(c, tok)
-      pos = c.bufpos
-      ch = tok.literal[tok.literal.len-1]
-    of '\C', '\L', '\0':
-      tok.kind = tkInvalid
-      break
-    else:
-      ch = buf[pos]
+  if pos < c.buf.len:
+    if c.buf[pos] == '^':
       inc(pos)
-    incl(tok.charset, ch)
-    if buf[pos] == '-':
-      if pos+1 < c.buf.len and buf[pos+1] == ']':
-        incl(tok.charset, '-')
-        inc(pos)
+      caret = true
+    while pos < c.buf.len:
+      var ch: char
+      case c.buf[pos]
+      of ']':
+        if pos < c.buf.len: inc(pos)
+        break
+      of '\\':
+        c.bufpos = pos
+        getEscapedChar(c, tok)
+        pos = c.bufpos
+        ch = tok.literal[tok.literal.len-1]
+      of '\C', '\L', '\0':
+        tok.kind = tkInvalid
+        break
       else:
-        if pos+1 < c.buf.len:
+        ch = c.buf[pos]
+        inc(pos)
+      incl(tok.charset, ch)
+      if c.buf[pos] == '-':
+        if pos+1 < c.buf.len and c.buf[pos+1] == ']':
+          incl(tok.charset, '-')
           inc(pos)
         else:
-          break
-        var ch2: char
-        case buf[pos]
-        of '\\':
-          c.bufpos = pos
-          getEscapedChar(c, tok)
-          pos = c.bufpos
-          ch2 = tok.literal[tok.literal.len-1]
-        of '\C', '\L', '\0':
-          tok.kind = tkInvalid
-          break
-        else:
           if pos+1 < c.buf.len:
-            ch2 = buf[pos]
             inc(pos)
           else:
             break
-        for i in ord(ch)+1 .. ord(ch2):
-          incl(tok.charset, chr(i))
+          var ch2: char
+          case c.buf[pos]
+          of '\\':
+            c.bufpos = pos
+            getEscapedChar(c, tok)
+            pos = c.bufpos
+            ch2 = tok.literal[tok.literal.len-1]
+          of '\C', '\L', '\0':
+            tok.kind = tkInvalid
+            break
+          else:
+            if pos+1 < c.buf.len:
+              ch2 = c.buf[pos]
+              inc(pos)
+            else:
+              break
+          for i in ord(ch)+1 .. ord(ch2):
+            incl(tok.charset, chr(i))
   c.bufpos = pos
   if caret: tok.charset = {'\1'..'\xFF'} - tok.charset
 
-proc getSymbol(c: var PegLexer, tok: var Token) =
+func getSymbol(c: var PegLexer, tok: var Token) =
   var pos = c.bufpos
-  var buf = c.buf
   while pos < c.buf.len:
-    add(tok.literal, buf[pos])
+    add(tok.literal, c.buf[pos])
     inc(pos)
-    if pos < buf.len and buf[pos] notin strutils.IdentChars: break
+    if pos < c.buf.len and c.buf[pos] notin strutils.IdentChars: break
   c.bufpos = pos
   tok.kind = tkIdentifier
 
-proc getBuiltin(c: var PegLexer, tok: var Token) =
+func getBuiltin(c: var PegLexer, tok: var Token) =
   if c.bufpos+1 < c.buf.len and c.buf[c.bufpos+1] in strutils.Letters:
     inc(c.bufpos)
     getSymbol(c, tok)
@@ -1368,12 +1683,19 @@ proc getBuiltin(c: var PegLexer, tok: var Token) =
     tok.kind = tkEscaped
     getEscapedChar(c, tok) # may set tok.kind to tkInvalid
 
-proc getTok(c: var PegLexer, tok: var Token) =
+func getTok(c: var PegLexer, tok: var Token) =
   tok.kind = tkInvalid
   tok.modifier = modNone
   setLen(tok.literal, 0)
   skip(c)
 
+  if c.bufpos >= c.buf.len:
+    tok.kind = tkEof
+    tok.literal = "[EOF]"
+    add(tok.literal, '\0')
+    inc(c.bufpos)
+    return
+
   case c.buf[c.bufpos]
   of '{':
     inc(c.bufpos)
@@ -1382,6 +1704,10 @@ proc getTok(c: var PegLexer, tok: var Token) =
       tok.kind = tkCurlyAt
       inc(c.bufpos, 2)
       add(tok.literal, "{@}")
+    elif c.buf[c.bufpos] == '}' and c.bufpos < c.buf.len:
+      tok.kind = tkEmptyCurl
+      inc(c.bufpos)
+      add(tok.literal, "{}")
     else:
       tok.kind = tkCurlyLe
       add(tok.literal, '{')
@@ -1413,9 +1739,11 @@ proc getTok(c: var PegLexer, tok: var Token) =
   of '$': getDollar(c, tok)
   of 'a'..'z', 'A'..'Z', '\128'..'\255':
     getSymbol(c, tok)
+    if c.bufpos >= c.buf.len:
+      return
     if c.buf[c.bufpos] in {'\'', '"'} or
         c.buf[c.bufpos] == '$' and c.bufpos+1 < c.buf.len and
-        c.buf[c.bufpos+1] in {'0'..'9'}:
+        c.buf[c.bufpos+1] in {'^', '0'..'9'}:
       case tok.literal
       of "i": tok.modifier = modIgnoreCase
       of "y": tok.modifier = modIgnoreStyle
@@ -1477,17 +1805,19 @@ proc getTok(c: var PegLexer, tok: var Token) =
     add(tok.literal, c.buf[c.bufpos])
     inc(c.bufpos)
 
-proc arrowIsNextTok(c: PegLexer): bool =
+func arrowIsNextTok(c: PegLexer): bool =
   # the only look ahead we need
   var pos = c.bufpos
   while pos < c.buf.len and c.buf[pos] in {'\t', ' '}: inc(pos)
-  result = c.buf[pos] == '<' and (pos+1 < c.buf.len) and c.buf[pos+1] == '-'
+  if pos+1 >= c.buf.len:
+    return
+  result = c.buf[pos] == '<' and c.buf[pos+1] == '-'
 
 # ----------------------------- parser ----------------------------------------
 
 type
   EInvalidPeg* = object of ValueError ## raised if an invalid
-                                         ## PEG has been detected
+                                      ## PEG has been detected
   PegParser = object of PegLexer ## the PEG parser object
     tok: Token
     nonterms: seq[NonTerminal]
@@ -1496,23 +1826,21 @@ type
     identIsVerbatim: bool
     skip: Peg
 
-proc pegError(p: PegParser, msg: string, line = -1, col = -1) =
-  var e: ref EInvalidPeg
-  new(e)
-  e.msg = errorStr(p, msg, line, col)
+func pegError(p: PegParser, msg: string, line = -1, col = -1) =
+  var e = (ref EInvalidPeg)(msg: errorStr(p, msg, line, col))
   raise e
 
-proc getTok(p: var PegParser) =
+func getTok(p: var PegParser) =
   getTok(p, p.tok)
   if p.tok.kind == tkInvalid: pegError(p, "'" & p.tok.literal & "' is invalid token")
 
-proc eat(p: var PegParser, kind: TokKind) =
+func eat(p: var PegParser, kind: TokKind) =
   if p.tok.kind == kind: getTok(p)
   else: pegError(p, tokKindToStr[kind] & " expected")
 
-proc parseExpr(p: var PegParser): Peg {.gcsafe.}
+func parseExpr(p: var PegParser): Peg {.gcsafe.}
 
-proc getNonTerminal(p: var PegParser, name: string): NonTerminal =
+func getNonTerminal(p: var PegParser, name: string): NonTerminal =
   for i in 0..high(p.nonterms):
     result = p.nonterms[i]
     if cmpIgnoreStyle(result.name, name) == 0: return
@@ -1520,19 +1848,22 @@ proc getNonTerminal(p: var PegParser, name: string): NonTerminal =
   result = newNonTerminal(name, getLine(p), getColumn(p))
   add(p.nonterms, result)
 
-proc modifiedTerm(s: string, m: Modifier): Peg =
+func modifiedTerm(s: string, m: Modifier): Peg =
   case m
   of modNone, modVerbatim: result = term(s)
   of modIgnoreCase: result = termIgnoreCase(s)
   of modIgnoreStyle: result = termIgnoreStyle(s)
 
-proc modifiedBackref(s: int, m: Modifier): Peg =
+func modifiedBackref(s: int, m: Modifier): Peg =
+  var
+    reverse = s < 0
+    index = if reverse: -s else: s
   case m
-  of modNone, modVerbatim: result = backref(s)
-  of modIgnoreCase: result = backrefIgnoreCase(s)
-  of modIgnoreStyle: result = backrefIgnoreStyle(s)
+  of modNone, modVerbatim: result = backref(index, reverse)
+  of modIgnoreCase: result = backrefIgnoreCase(index, reverse)
+  of modIgnoreStyle: result = backrefIgnoreStyle(index, reverse)
 
-proc builtin(p: var PegParser): Peg =
+func builtin(p: var PegParser): Peg =
   # do not use "y", "skip" or "i" as these would be ambiguous
   case p.tok.literal
   of "n": result = newLine()
@@ -1541,7 +1872,7 @@ proc builtin(p: var PegParser): Peg =
   of "s": result = charSet({' ', '\9'..'\13'})
   of "S": result = charSet({'\1'..'\xff'} - {' ', '\9'..'\13'})
   of "w": result = charSet({'a'..'z', 'A'..'Z', '_', '0'..'9'})
-  of "W": result = charSet({'\1'..'\xff'} - {'a'..'z','A'..'Z','_','0'..'9'})
+  of "W": result = charSet({'\1'..'\xff'} - {'a'..'z', 'A'..'Z', '_', '0'..'9'})
   of "a": result = charSet({'a'..'z', 'A'..'Z'})
   of "A": result = charSet({'\1'..'\xff'} - {'a'..'z', 'A'..'Z'})
   of "ident": result = pegs.ident
@@ -1552,11 +1883,11 @@ proc builtin(p: var PegParser): Peg =
   of "white": result = unicodeWhitespace()
   else: pegError(p, "unknown built-in: " & p.tok.literal)
 
-proc token(terminal: Peg, p: PegParser): Peg =
+func token(terminal: Peg, p: PegParser): Peg =
   if p.skip.kind == pkEmpty: result = terminal
   else: result = sequence(p.skip, terminal)
 
-proc primary(p: var PegParser): Peg =
+func primary(p: var PegParser): Peg =
   case p.tok.kind
   of tkAmp:
     getTok(p)
@@ -1580,7 +1911,8 @@ proc primary(p: var PegParser): Peg =
       getTok(p)
     elif not arrowIsNextTok(p):
       var nt = getNonTerminal(p, p.tok.literal)
-      incl(nt.flags, ntUsed)
+      {.cast(noSideEffect).}:
+        incl(nt.flags, ntUsed)
       result = nonterminal(nt).token(p)
       getTok(p)
     else:
@@ -1604,6 +1936,9 @@ proc primary(p: var PegParser): Peg =
     result = capture(parseExpr(p)).token(p)
     eat(p, tkCurlyRi)
     inc(p.captures)
+  of tkEmptyCurl:
+    result = capture()
+    getTok(p)
   of tkAny:
     result = any().token(p)
     getTok(p)
@@ -1623,11 +1958,11 @@ proc primary(p: var PegParser): Peg =
     result = startAnchor()
     getTok(p)
   of tkBackref:
+    if abs(p.tok.index) > p.captures or p.tok.index == 0:
+      pegError(p, "invalid back reference index: " & $p.tok.index)
     var m = p.tok.modifier
     if m == modNone: m = p.modifier
     result = modifiedBackref(p.tok.index, m).token(p)
-    if p.tok.index < 0 or p.tok.index > p.captures:
-      pegError(p, "invalid back reference index: " & $p.tok.index)
     getTok(p)
   else:
     pegError(p, "expression expected, but found: " & p.tok.literal)
@@ -1645,13 +1980,13 @@ proc primary(p: var PegParser): Peg =
       getTok(p)
     else: break
 
-proc seqExpr(p: var PegParser): Peg =
+func seqExpr(p: var PegParser): Peg =
   result = primary(p)
   while true:
     case p.tok.kind
     of tkAmp, tkNot, tkAt, tkStringLit, tkCharSet, tkParLe, tkCurlyLe,
        tkAny, tkAnyRune, tkBuiltin, tkEscaped, tkDollar, tkBackref,
-       tkHat, tkCurlyAt:
+       tkHat, tkCurlyAt, tkEmptyCurl:
       result = sequence(result, primary(p))
     of tkIdentifier:
       if not arrowIsNextTok(p):
@@ -1659,27 +1994,29 @@ proc seqExpr(p: var PegParser): Peg =
       else: break
     else: break
 
-proc parseExpr(p: var PegParser): Peg =
+func parseExpr(p: var PegParser): Peg =
   result = seqExpr(p)
   while p.tok.kind == tkBar:
     getTok(p)
     result = result / seqExpr(p)
 
-proc parseRule(p: var PegParser): NonTerminal =
+func parseRule(p: var PegParser): NonTerminal =
   if p.tok.kind == tkIdentifier and arrowIsNextTok(p):
     result = getNonTerminal(p, p.tok.literal)
     if ntDeclared in result.flags:
       pegError(p, "attempt to redefine: " & result.name)
-    result.line = getLine(p)
-    result.col = getColumn(p)
+    {.cast(noSideEffect).}:
+      result.line = getLine(p)
+      result.col = getColumn(p)
     getTok(p)
     eat(p, tkArrow)
-    result.rule = parseExpr(p)
-    incl(result.flags, ntDeclared) # NOW inlining may be attempted
+    {.cast(noSideEffect).}:
+      result.rule = parseExpr(p)
+      incl(result.flags, ntDeclared) # NOW inlining may be attempted
   else:
     pegError(p, "rule expected, but found: " & p.tok.literal)
 
-proc rawParse(p: var PegParser): Peg =
+func rawParse(p: var PegParser): Peg =
   ## parses a rule or a PEG expression
   while p.tok.kind == tkBuiltin:
     case p.tok.literal
@@ -1709,7 +2046,7 @@ proc rawParse(p: var PegParser): Peg =
     elif ntUsed notin nt.flags and i > 0:
       pegError(p, "unused rule: " & nt.name, nt.line, nt.col)
 
-proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg =
+func parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg =
   ## constructs a Peg object from `pattern`. `filename`, `line`, `col` are
   ## used for error messages, but they only provide start offsets. `parsePeg`
   ## keeps track of line and column numbers within `pattern`.
@@ -1724,14 +2061,14 @@ proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg =
   getTok(p)
   result = rawParse(p)
 
-proc peg*(pattern: string): Peg =
+func peg*(pattern: string): Peg =
   ## constructs a Peg object from the `pattern`. The short name has been
-  ## chosen to encourage its use as a raw string modifier::
+  ## chosen to encourage its use as a raw string modifier:
   ##
-  ##   peg"{\ident} \s* '=' \s* {.*}"
+  ##     peg"{\ident} \s* '=' \s* {.*}"
   result = parsePeg(pattern, "pattern")
 
-proc escapePeg*(s: string): string =
+func escapePeg*(s: string): string =
   ## escapes `s` so that it is matched verbatim when used as a peg.
   result = ""
   var inQuote = false
@@ -1749,143 +2086,3 @@ proc escapePeg*(s: string): string =
         inQuote = true
       result.add(c)
   if inQuote: result.add('\'')
-
-when isMainModule:
-  assert escapePeg("abc''def'") == r"'abc'\x27\x27'def'\x27"
-  assert match("(a b c)", peg"'(' @ ')'")
-  assert match("W_HI_Le", peg"\y 'while'")
-  assert(not match("W_HI_L", peg"\y 'while'"))
-  assert(not match("W_HI_Le", peg"\y v'while'"))
-  assert match("W_HI_Le", peg"y'while'")
-
-  assert($ +digits == $peg"\d+")
-  assert "0158787".match(peg"\d+")
-  assert "ABC 0232".match(peg"\w+\s+\d+")
-  assert "ABC".match(peg"\d+ / \w+")
-
-  var accum: seq[string] = @[]
-  for word in split("00232this02939is39an22example111", peg"\d+"):
-    accum.add(word)
-  assert(accum == @["this", "is", "an", "example"])
-
-  assert matchLen("key", ident) == 3
-
-  var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident)
-  assert matchLen("key1=  cal9", pattern) == 11
-
-  var ws = newNonTerminal("ws", 1, 1)
-  ws.rule = *whitespace
-
-  var expr = newNonTerminal("expr", 1, 1)
-  expr.rule = sequence(capture(ident), *sequence(
-                nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr)))
-
-  var c: Captures
-  var s = "a+b +  c +d+e+f"
-  assert rawMatch(s, expr.rule, 0, c) == len(s)
-  var a = ""
-  for i in 0..c.ml-1:
-    a.add(substr(s, c.matches[i][0], c.matches[i][1]))
-  assert a == "abcdef"
-  #echo expr.rule
-
-  #const filename = "lib/devel/peg/grammar.txt"
-  #var grammar = parsePeg(newFileStream(filename, fmRead), filename)
-  #echo "a <- [abc]*?".match(grammar)
-  assert find("_____abc_______", term("abc"), 2) == 5
-  assert match("_______ana", peg"A <- 'ana' / . A")
-  assert match("abcs%%%", peg"A <- ..A / .A / '%'")
-
-  var matches: array[0..MaxSubpatterns-1, string]
-  if "abc" =~ peg"{'a'}'bc' 'xyz' / {\ident}":
-    assert matches[0] == "abc"
-  else:
-    assert false
-
-  var g2 = peg"""S <- A B / C D
-                 A <- 'a'+
-                 B <- 'b'+
-                 C <- 'c'+
-                 D <- 'd'+
-              """
-  assert($g2 == "((A B) / (C D))")
-  assert match("cccccdddddd", g2)
-  assert("var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2") ==
-         "var1<-keykey; var2<-key2key2")
-  assert("var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2") ==
-         "$1<-$2$2; $1<-$2$2")
-  assert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}")
-
-  if "aaaaaa" =~ peg"'aa' !. / ({'a'})+":
-    assert matches[0] == "a"
-  else:
-    assert false
-
-  if match("abcdefg", peg"c {d} ef {g}", matches, 2):
-    assert matches[0] == "d"
-    assert matches[1] == "g"
-  else:
-    assert false
-
-  accum = @[]
-  for x in findAll("abcdef", peg".", 3):
-    accum.add(x)
-  assert(accum == @["d", "e", "f"])
-
-  for x in findAll("abcdef", peg"^{.}", 3):
-    assert x == "d"
-
-  if "f(a, b)" =~ peg"{[0-9]+} / ({\ident} '(' {@} ')')":
-    assert matches[0] == "f"
-    assert matches[1] == "a, b"
-  else:
-    assert false
-
-  assert match("eine übersicht und außerdem", peg"(\letter \white*)+")
-  # ß is not a lower cased letter?!
-  assert match("eine übersicht und auerdem", peg"(\lower \white*)+")
-  assert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+")
-  assert(not match("456678", peg"(\letter)+"))
-
-  assert("var1 = key; var2 = key2".replacef(
-    peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") ==
-         "var1<-keykey;var2<-key2key2")
-
-  assert match("prefix/start", peg"^start$", 7)
-
-  if "foo" =~ peg"{'a'}?.*":
-    assert matches[0] == nil
-  else: assert false
-
-  if "foo" =~ peg"{''}.*":
-    assert matches[0] == ""
-  else: assert false
-
-  if "foo" =~ peg"{'foo'}":
-    assert matches[0] == "foo"
-  else: assert false
-
-  let empty_test = peg"^\d*"
-  let str = "XYZ"
-
-  assert(str.find(empty_test) == 0)
-  assert(str.match(empty_test))
-
-  proc handleMatches*(m: int, n: int, c: openArray[string]): string =
-    result = ""
-
-    if m > 0:
-      result.add ", "
-
-    result.add case n:
-      of 2: toLowerAscii(c[0]) & ": '" & c[1] & "'"
-      of 1: toLowerAscii(c[0]) & ": ''"
-      else: ""
-
-  assert("Var1=key1;var2=Key2;   VAR3".
-         replace(peg"{\ident}('='{\ident})* ';'* \s*",
-         handleMatches)=="var1: 'key1', var2: 'Key2', var3: ''")
-
-
-  doAssert "test1".match(peg"""{@}$""")
-  doAssert "test2".match(peg"""{(!$ .)*} $""")
diff --git a/lib/pure/prelude.nim b/lib/pure/prelude.nim
new file mode 100644
index 000000000..9428f29eb
--- /dev/null
+++ b/lib/pure/prelude.nim
@@ -0,0 +1,28 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+when defined(nimdoc) and isMainModule:
+  from std/compileSettings import nil
+  when compileSettings.querySetting(compileSettings.SingleValueSetting.projectFull) == currentSourcePath:
+    ## This is an include file that simply imports common modules for your convenience.
+    runnableExamples:
+      include std/prelude
+        # same as:
+        # import std/[os, strutils, times, parseutils, hashes, tables, sets, sequtils, parseopt, strformat]
+      let x = 1
+      assert "foo $# $#" % [$x, "bar"] == "foo 1 bar"
+      assert toSeq(1..3) == @[1, 2, 3]
+      when not defined(js) or defined(nodejs):
+        assert getCurrentDir().len > 0
+        assert ($now()).startsWith "20"
+
+  # xxx `nim doc -b:js -d:nodejs --doccmd:-d:nodejs lib/pure/prelude.nim` fails for some reason
+  # specific to `nim doc`, but the code otherwise works with nodejs.
+
+import std/[os, strutils, times, parseutils, hashes, tables, sets, sequtils, parseopt, strformat]
diff --git a/lib/pure/punycode.nim b/lib/pure/punycode.nim
deleted file mode 100644
index ab6501ed1..000000000
--- a/lib/pure/punycode.nim
+++ /dev/null
@@ -1,174 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2016 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-import strutils
-import unicode
-
-# issue #3045
-
-const
-  Base = 36
-  TMin = 1
-  TMax = 26
-  Skew = 38
-  Damp = 700
-  InitialBias = 72
-  InitialN = 128
-  Delimiter = '-'
-
-type
-  PunyError* = object of Exception
-
-proc decodeDigit(x: char): int {.raises: [PunyError].} =
-  if '0' <= x and x <= '9':
-    result = ord(x) - (ord('0') - 26)
-  elif 'A' <= x and x <= 'Z':
-    result = ord(x) - ord('A')
-  elif 'a' <= x and x <= 'z':
-    result = ord(x) - ord('a')
-  else:
-    raise newException(PunyError, "Bad input")
-
-proc encodeDigit(digit: int): Rune {.raises: [PunyError].} =
-  if 0 <= digit and digit < 26:
-    result = Rune(digit + ord('a'))
-  elif 26 <= digit and digit < 36:
-    result = Rune(digit + (ord('0') - 26))
-  else:
-    raise newException(PunyError, "internal error in punycode encoding")
-
-proc isBasic(c: char): bool = ord(c) < 0x80
-proc isBasic(r: Rune): bool = int(r) < 0x80
-
-proc adapt(delta, numPoints: int, first: bool): int =
-  var d = if first: delta div Damp else: delta div 2
-  d += d div numPoints
-  var k = 0
-  while d > ((Base-TMin)*TMax) div 2:
-    d = d div (Base - TMin)
-    k += Base
-  result = k + (Base - TMin + 1) * d div (d + Skew)
-
-proc encode*(prefix, s: string): string {.raises: [PunyError].} =
-  ## Encode a string that may contain Unicode.
-  ## Prepend `prefix` to the result
-  result = prefix
-  var (d, n, bias) = (0, InitialN, InitialBias)
-  var (b, remaining) = (0, 0)
-  for r in s.runes:
-    if r.isBasic:
-      # basic Ascii character
-      inc b
-      result.add($r)
-    else:
-      # special character
-      inc remaining
-
-  var h = b
-  if b > 0:
-    result.add(Delimiter) # we have some Ascii chars
-  while remaining != 0:
-    var m: int = high(int32)
-    for r in s.runes:
-      if m > int(r) and int(r) >= n:
-        m = int(r)
-    d += (m - n) * (h + 1)
-    if d < 0:
-      raise newException(PunyError, "invalid label " & s)
-    n = m
-    for r in s.runes:
-      if int(r) < n:
-        inc d
-        if d < 0:
-          raise newException(PunyError, "invalid label " & s)
-        continue
-      if int(r) > n:
-        continue
-      var q = d
-      var k = Base
-      while true:
-        var t = k - bias
-        if t < TMin:
-          t = TMin
-        elif t > TMax:
-          t = TMax
-        if q < t:
-          break
-        result.add($encodeDigit(t + (q - t) mod (Base - t)))
-        q = (q - t) div (Base - t)
-        k += Base
-      result.add($encodeDigit(q))
-      bias = adapt(d, h + 1, h == b)
-      d = 0
-      inc h
-      dec remaining
-    inc d
-    inc n
-
-proc encode*(s: string): string {.raises: [PunyError].} =
-  ## Encode a string that may contain Unicode. Prefix is empty.
-  result = encode("", s)
-
-proc decode*(encoded: string): string {.raises: [PunyError].}  =
-  ## Decode a Punycode-encoded string
-  var
-    n = InitialN
-    i = 0
-    bias = InitialBias
-  var d = rfind(encoded, Delimiter)
-  result = ""
-
-  if d > 0:
-    # found Delimiter
-    for j in 0..<d:
-      var c = encoded[j] # char
-      if not c.isBasic:
-        raise newException(PunyError, "Encoded contains a non-basic char")
-      result.add(c) # add the character
-    inc d
-  else:
-    d = 0 # set to first index
-
-  while (d < len(encoded)):
-    var oldi = i
-    var w = 1
-    var k = Base
-    while true:
-      if d == len(encoded):
-        raise newException(PunyError, "Bad input: " & encoded)
-      var c = encoded[d]; inc d
-      var digit = int(decodeDigit(c))
-      if digit > (high(int32) - i) div w:
-        raise newException(PunyError, "Too large a value: " & $digit)
-      i += digit * w
-      var t: int
-      if k <= bias:
-        t = TMin
-      elif k >= bias + TMax:
-        t = TMax
-      else:
-        t = k - bias
-      if digit < t:
-        break
-      w *= Base - t
-      k += Base
-    bias = adapt(i - oldi, runelen(result) + 1, oldi == 0)
-
-    if i div (runelen(result) + 1) > high(int32) - n:
-      raise newException(PunyError, "Value too large")
-
-    n += i div (runelen(result) + 1)
-    i = i mod (runelen(result) + 1)
-    insert(result, $Rune(n), i)
-    inc i
-
-when isMainModule:
-  assert(decode(encode("", "bücher")) == "bücher")
-  assert(decode(encode("münchen")) == "münchen")
-  assert encode("xn--", "münchen") == "xn--mnchen-3ya"
diff --git a/lib/pure/random.nim b/lib/pure/random.nim
index 01ea9c845..3ec77d37e 100644
--- a/lib/pure/random.nim
+++ b/lib/pure/random.nim
@@ -7,46 +7,161 @@
 #    distribution, for details about the copyright.
 #
 
-## Nim's standard random number generator. Based on
-## the ``xoroshiro128+`` (xor/rotate/shift/rotate) library.
-## * More information: http://xoroshiro.di.unimi.it/
+## Nim's standard random number generator (RNG).
+##
+## Its implementation is based on the `xoroshiro128+`
+## (xor/rotate/shift/rotate) library.
+## * More information: http://xoroshiro.di.unimi.it
 ## * C implementation: http://xoroshiro.di.unimi.it/xoroshiro128plus.c
 ##
 ## **Do not use this module for cryptographic purposes!**
+##
+## Basic usage
+## ===========
+##
+runnableExamples:
+  # Call randomize() once to initialize the default random number generator.
+  # If this is not called, the same results will occur every time these
+  # examples are run.
+  randomize()
+
+  # Pick a number in 0..100.
+  let num = rand(100)
+  doAssert num in 0..100
+
+  # Roll a six-sided die.
+  let roll = rand(1..6)
+  doAssert roll in 1..6
+
+  # Pick a marble from a bag.
+  let marbles = ["red", "blue", "green", "yellow", "purple"]
+  let pick = sample(marbles)
+  doAssert pick in marbles
+
+  # Shuffle some cards.
+  var cards = ["Ace", "King", "Queen", "Jack", "Ten"]
+  shuffle(cards)
+  doAssert cards.len == 5
+
+## These examples all use the default RNG. The
+## `Rand type <#Rand>`_ represents the state of an RNG.
+## For convenience, this module contains a default Rand state that corresponds
+## to the default RNG. Most procs in this module which do
+## not take in a Rand parameter, including those called in the above examples,
+## use the default generator. Those procs are **not** thread-safe.
+##
+## Note that the default generator always starts in the same state.
+## The `randomize proc <#randomize>`_ can be called to initialize the default
+## generator with a seed based on the current time, and it only needs to be
+## called once before the first usage of procs from this module. If
+## `randomize` is not called, the default generator will always produce
+## the same results.
+##
+## RNGs that are independent of the default one can be created with the
+## `initRand proc <#initRand,int64>`_.
+##
+## Again, it is important to remember that this module must **not** be used for
+## cryptographic applications.
+##
+## See also
+## ========
+## * `std/sysrand module <sysrand.html>`_ for a cryptographically secure pseudorandom number generator
+## * `math module <math.html>`_ for basic math routines
+## * `stats module <stats.html>`_ for statistical analysis
+## * `list of cryptographic and hashing modules <lib.html#pure-libraries-hashing>`_
+##   in the standard library
+
+import std/[algorithm, math]
+import std/private/[since, jsutils]
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions]
+
+include system/inclrtl
+{.push debugger: off.}
+template whenHasBigInt64(yes64, no64): untyped =
+  when defined(js):
+    when compiles(compileOption("jsbigint64")):
+      when compileOption("jsbigint64"):
+        yes64
+      else:
+        no64
+    else:
+      no64
+  else:
+    yes64
 
-include "system/inclrtl"
-{.push debugger:off.}
 
-when defined(JS):
-  type ui = uint32
+whenHasBigInt64:
+  type Ui = uint64
+
+  const randMax = 18_446_744_073_709_551_615u64
+do:
+  type Ui = uint32
 
   const randMax = 4_294_967_295u32
-else:
-  type ui = uint64
 
-  const randMax = 18_446_744_073_709_551_615u64
 
 type
-  Rand* = object ## State of the random number generator.
-                 ## The procs that use the default state
-                 ## are **not** thread-safe!
-    a0, a1: ui
+  Rand* = object ## State of a random number generator.
+                 ##
+                 ## Create a new Rand state using the `initRand proc <#initRand,int64>`_.
+                 ##
+                 ## The module contains a default Rand state for convenience.
+                 ## It corresponds to the default RNG's state.
+                 ## The default Rand state always starts with the same values, but the
+                 ## `randomize proc <#randomize>`_ can be used to seed the default generator
+                 ## with a value based on the current time.
+                 ##
+                 ## Many procs have two variations: one that takes in a Rand parameter and
+                 ## another that uses the default generator. The procs that use the default
+                 ## generator are **not** thread-safe!
+    a0, a1: Ui
+
+whenHasBigInt64:
+  const DefaultRandSeed = Rand(
+    a0: 0x69B4C98CB8530805u64,
+    a1: 0xFED1DD3004688D67CAu64)
 
-when defined(JS):
+  # racy for multi-threading but good enough for now:
+  var state = DefaultRandSeed # global for backwards compatibility
+do:
   var state = Rand(
     a0: 0x69B4C98Cu32,
     a1: 0xFED1DD30u32) # global for backwards compatibility
-else:
-  # racy for multi-threading but good enough for now:
-  var state = Rand(
-    a0: 0x69B4C98CB8530805u64,
-    a1: 0xFED1DD3004688D67CAu64) # global for backwards compatibility
 
-proc rotl(x, k: ui): ui =
-  result = (x shl k) or (x shr (ui(64) - k))
+func isValid(r: Rand): bool {.inline.} =
+  ## Check whether state of `r` is valid.
+  ##
+  ## In `xoroshiro128+`, if all bits of `a0` and `a1` are zero,
+  ## they are always zero after calling `next(r: var Rand)`.
+  not (r.a0 == 0 and r.a1 == 0)
+
+since (1, 5):
+  template randState*(): untyped =
+    ## Makes the default Rand state accessible from other modules.
+    ## Useful for module authors.
+    state
+
+proc rotl(x, k: Ui): Ui =
+  result = (x shl k) or (x shr (Ui(64) - k))
 
 proc next*(r: var Rand): uint64 =
-  ## Uses the state to compute a new ``uint64`` random number.
+  ## Computes a random `uint64` number using the given state.
+  ##
+  ## **See also:**
+  ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer between zero and
+  ##   a given upper bound
+  ## * `rand proc<#rand,Rand,range[]>`_ that returns a float
+  ## * `rand proc<#rand,Rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_
+  ##   that accepts a slice
+  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
+  ## * `skipRandomNumbers proc<#skipRandomNumbers,Rand>`_
+  runnableExamples("-r:off"):
+    var r = initRand(2019)
+    assert r.next() == 13223559681708962501'u64 # implementation defined
+    assert r.next() == 7229677234260823147'u64 # ditto
+
   let s0 = r.a0
   var s1 = r.a1
   result = s0 + s1
@@ -55,167 +170,597 @@ proc next*(r: var Rand): uint64 =
   r.a1 = rotl(s1, 36) # c
 
 proc skipRandomNumbers*(s: var Rand) =
-  ## This is the jump function for the generator. It is equivalent
-  ## to 2^64 calls to next(); it can be used to generate 2^64
-  ## non-overlapping subsequences for parallel computations.
-  when defined(JS):
-    const helper = [0xbeac0467u32, 0xd86b048bu32]
-  else:
+  ## The jump function for the generator.
+  ##
+  ## This proc is equivalent to `2^64` calls to `next <#next,Rand>`_, and it can
+  ## be used to generate `2^64` non-overlapping subsequences for parallel
+  ## computations.
+  ##
+  ## When multiple threads are generating random numbers, each thread must
+  ## own the `Rand <#Rand>`_ state it is using so that the thread can safely
+  ## obtain random numbers. However, if each thread creates its own Rand state,
+  ## the subsequences of random numbers that each thread generates may overlap,
+  ## even if the provided seeds are unique. This is more likely to happen as the
+  ## number of threads and amount of random numbers generated increases.
+  ##
+  ## If many threads will generate random numbers concurrently, it is better to
+  ## create a single Rand state and pass it to each thread. After passing the
+  ## Rand state to a thread, call this proc before passing it to the next one.
+  ## By using the Rand state this way, the subsequences of random numbers
+  ## generated in each thread will never overlap as long as no thread generates
+  ## more than `2^64` random numbers.
+  ##
+  ## **See also:**
+  ## * `next proc<#next,Rand>`_
+  runnableExamples("--threads:on"):
+    import std/random
+
+    const numbers = 100000
+
+    var
+      thr: array[0..3, Thread[(Rand, int)]]
+      vals: array[0..3, int]
+
+    proc randomSum(params: tuple[r: Rand, index: int]) {.thread.} =
+      var r = params.r
+      var s = 0 # avoid cache thrashing
+      for i in 1..numbers:
+        s += r.rand(0..10)
+      vals[params.index] = s
+
+    var r = initRand(2019)
+    for i in 0..<thr.len:
+      createThread(thr[i], randomSum, (r, i))
+      r.skipRandomNumbers()
+
+    joinThreads(thr)
+
+    for val in vals:
+      doAssert abs(val - numbers * 5) / numbers < 0.1
+
+    doAssert vals == [501737, 497901, 500683, 500157]
+
+
+  whenHasBigInt64:
     const helper = [0xbeac0467eba5facbu64, 0xd86b048b86aa9922u64]
+  do:
+    const helper = [0xbeac0467u32, 0xd86b048bu32]
   var
-    s0 = ui 0
-    s1 = ui 0
+    s0 = Ui 0
+    s1 = Ui 0
   for i in 0..high(helper):
-    for b in 0..< 64:
-      if (helper[i] and (ui(1) shl ui(b))) != 0:
+    for b in 0 ..< 64:
+      if (helper[i] and (Ui(1) shl Ui(b))) != 0:
         s0 = s0 xor s.a0
         s1 = s1 xor s.a1
       discard next(s)
   s.a0 = s0
   s.a1 = s1
 
-proc random*(max: int): int {.benign, deprecated.} =
-  ## Returns a random number in the range 0..max-1. The sequence of
-  ## random number is always the same, unless `randomize` is called
-  ## which initializes the random number generator with a "random"
-  ## number, i.e. a tickcount. **Deprecated since version 0.18.0**.
-  ## Use ``rand`` instead.
-  while true:
-    let x = next(state)
-    if x < randMax - (randMax mod ui(max)):
-      return int(x mod uint64(max))
-
-proc random*(max: float): float {.benign, deprecated.} =
-  ## Returns a random number in the range 0..<max. The sequence of
-  ## random number is always the same, unless `randomize` is called
-  ## which initializes the random number generator with a "random"
-  ## number, i.e. a tickcount. **Deprecated since version 0.18.0**.
-  ## Use ``rand`` instead.
-  let x = next(state)
-  when defined(JS):
-    result = (float(x) / float(high(uint32))) * max
-  else:
-    let u = (0x3FFu64 shl 52u64) or (x shr 12u64)
-    result = (cast[float](u) - 1.0) * max
-
-proc random*[T](x: HSlice[T, T]): T {.deprecated.} =
-  ## For a slice `a .. b` returns a value in the range `a .. b-1`.
-  ## **Deprecated since version 0.18.0**.
-  ## Use ``rand`` instead.
-  result = T(random(x.b - x.a)) + x.a
-
-proc random*[T](a: openArray[T]): T {.deprecated.} =
-  ## returns a random element from the openarray `a`.
-  ## **Deprecated since version 0.18.0**.
-  ## Use ``rand`` instead.
-  result = a[random(a.low..a.len)]
-
-proc rand*(r: var Rand; max: int): int {.benign.} =
-  ## Returns a random number in the range 0..max. The sequence of
-  ## random number is always the same, unless `randomize` is called
-  ## which initializes the random number generator with a "random"
-  ## number, i.e. a tickcount.
+proc rand[T: uint | uint64](r: var Rand; max: T): T =
+  # xxx export in future work
   if max == 0: return
-  while true:
-    let x = next(r)
-    if x <= randMax - (randMax mod ui(max)):
-      return int(x mod (uint64(max)+1u64))
+  else:
+    let max = uint64(max)
+    when T.high.uint64 == uint64.high:
+      if max == uint64.high: return T(next(r))
+    var iters = 0
+    while true:
+      let x = next(r)
+      # avoid `mod` bias
+      if x <= randMax - (randMax mod max) or iters > 20:
+        return T(x mod (max + 1))
+      else:
+        inc iters
+
+proc rand*(r: var Rand; max: Natural): int {.benign.} =
+  ## Returns a random integer in the range `0..max` using the given state.
+  ##
+  ## **See also:**
+  ## * `rand proc<#rand,int>`_ that returns an integer using the default RNG
+  ## * `rand proc<#rand,Rand,range[]>`_ that returns a float
+  ## * `rand proc<#rand,Rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_
+  ##   that accepts a slice
+  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
+  runnableExamples:
+    var r = initRand(123)
+    if false:
+      assert r.rand(100) == 96 # implementation defined
+  # bootstrap: can't use `runnableExamples("-r:off")`
+  cast[int](rand(r, uint64(max)))
+    # xxx toUnsigned pending https://github.com/nim-lang/Nim/pull/18445
 
 proc rand*(max: int): int {.benign.} =
-  ## Returns a random number in the range 0..max. The sequence of
-  ## random number is always the same, unless `randomize` is called
-  ## which initializes the random number generator with a "random"
-  ## number, i.e. a tickcount.
+  ## Returns a random integer in the range `0..max`.
+  ##
+  ## If `randomize <#randomize>`_ has not been called, the sequence of random
+  ## numbers returned from this proc will always be the same.
+  ##
+  ## This proc uses the default RNG. Thus, it is **not** thread-safe.
+  ##
+  ## **See also:**
+  ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer using a
+  ##   provided state
+  ## * `rand proc<#rand,float>`_ that returns a float
+  ## * `rand proc<#rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_
+  ##   that accepts a slice
+  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
+  runnableExamples("-r:off"):
+    randomize(123)
+    assert [rand(100), rand(100)] == [96, 63] # implementation defined
+
   rand(state, max)
 
-proc rand*(r: var Rand; max: float): float {.benign.} =
-  ## Returns a random number in the range 0..max. The sequence of
-  ## random number is always the same, unless `randomize` is called
-  ## which initializes the random number generator with a "random"
-  ## number, i.e. a tickcount.
+proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} =
+  ## Returns a random floating point number in the range `0.0..max`
+  ## using the given state.
+  ##
+  ## **See also:**
+  ## * `rand proc<#rand,float>`_ that returns a float using the default RNG
+  ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer
+  ## * `rand proc<#rand,Rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_
+  ##   that accepts a slice
+  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
+  runnableExamples:
+    var r = initRand(234)
+    let f = r.rand(1.0) # 8.717181376738381e-07
+
   let x = next(r)
-  when defined(JS):
-    result = (float(x) / float(high(uint32))) * max
+  when defined(js):
+    when compiles(compileOption("jsbigint64")):
+      when compileOption("jsbigint64"):
+        result = (float(x) / float(high(uint64))) * max
+      else:
+        result = (float(x) / float(high(uint32))) * max
+    else:
+      result = (float(x) / float(high(uint32))) * max
   else:
     let u = (0x3FFu64 shl 52u64) or (x shr 12u64)
     result = (cast[float](u) - 1.0) * max
 
 proc rand*(max: float): float {.benign.} =
-  ## Returns a random number in the range 0..max. The sequence of
-  ## random number is always the same, unless `randomize` is called
-  ## which initializes the random number generator with a "random"
-  ## number, i.e. a tickcount.
+  ## Returns a random floating point number in the range `0.0..max`.
+  ##
+  ## If `randomize <#randomize>`_ has not been called, the sequence of random
+  ## numbers returned from this proc will always be the same.
+  ##
+  ## This proc uses the default RNG. Thus, it is **not** thread-safe.
+  ##
+  ## **See also:**
+  ## * `rand proc<#rand,Rand,range[]>`_ that returns a float using a
+  ##   provided state
+  ## * `rand proc<#rand,int>`_ that returns an integer
+  ## * `rand proc<#rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_
+  ##   that accepts a slice
+  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
+  runnableExamples:
+    randomize(234)
+    let f = rand(1.0) # 8.717181376738381e-07
+
   rand(state, max)
 
-proc rand*[T](r: var Rand; x: HSlice[T, T]): T =
-  ## For a slice `a .. b` returns a value in the range `a .. b`.
-  result = T(rand(r, x.b - x.a)) + x.a
+proc rand*[T: Ordinal or SomeFloat](r: var Rand; x: HSlice[T, T]): T =
+  ## For a slice `a..b`, returns a value in the range `a..b` using the given
+  ## state.
+  ##
+  ## Allowed types for `T` are integers, floats, and enums without holes.
+  ##
+  ## **See also:**
+  ## * `rand proc<#rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_
+  ##   that accepts a slice and uses the default RNG
+  ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer
+  ## * `rand proc<#rand,Rand,range[]>`_ that returns a float
+  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
+  runnableExamples:
+    var r = initRand(345)
+    assert r.rand(1..5) <= 5
+    assert r.rand(-1.1 .. 1.2) >= -1.1
+  assert x.a <= x.b
+  when T is SomeFloat:
+    result = rand(r, x.b - x.a) + x.a
+  else: # Integers and Enum types
+    whenJsNoBigInt64:
+      result = cast[T](rand(r, cast[uint](x.b) - cast[uint](x.a)) + cast[uint](x.a))
+    do:
+      result = cast[T](rand(r, cast[uint64](x.b) - cast[uint64](x.a)) + cast[uint64](x.a))
+
+proc rand*[T: Ordinal or SomeFloat](x: HSlice[T, T]): T =
+  ## For a slice `a..b`, returns a value in the range `a..b`.
+  ##
+  ## Allowed types for `T` are integers, floats, and enums without holes.
+  ##
+  ## If `randomize <#randomize>`_ has not been called, the sequence of random
+  ## numbers returned from this proc will always be the same.
+  ##
+  ## This proc uses the default RNG. Thus, it is **not** thread-safe.
+  ##
+  ## **See also:**
+  ## * `rand proc<#rand,Rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_
+  ##   that accepts a slice and uses a provided state
+  ## * `rand proc<#rand,int>`_ that returns an integer
+  ## * `rand proc<#rand,float>`_ that returns a floating point number
+  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
+  runnableExamples:
+    randomize(345)
+    assert rand(1..6) <= 6
 
-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`.
-  result = a[rand(r, a.low..a.high)]
+proc rand*[T: Ordinal](r: var Rand; t: typedesc[T]): T {.since: (1, 7, 1).} =
+  ## Returns a random Ordinal in the range `low(T)..high(T)`.
+  ##
+  ## If `randomize <#randomize>`_ has not been called, the sequence of random
+  ## numbers returned from this proc will always be the same.
+  ##
+  ## **See also:**
+  ## * `rand proc<#rand,int>`_ that returns an integer
+  ## * `rand proc<#rand,float>`_ that returns a floating point number
+  ## * `rand proc<#rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_
+  ##   that accepts a slice
+  when T is range or T is enum:
+    result = rand(r, low(T)..high(T))
+  elif T is bool:
+    result = r.next < randMax div 2
+  else:
+    whenJsNoBigInt64:
+      result = cast[T](r.next shr (sizeof(uint)*8 - sizeof(T)*8))
+    do:
+      result = cast[T](r.next shr (sizeof(uint64)*8 - sizeof(T)*8))
+
+proc rand*[T: Ordinal](t: typedesc[T]): T =
+  ## Returns a random Ordinal in the range `low(T)..high(T)`.
+  ##
+  ## If `randomize <#randomize>`_ has not been called, the sequence of random
+  ## numbers returned from this proc will always be the same.
+  ##
+  ## This proc uses the default RNG. Thus, it is **not** thread-safe.
+  ##
+  ## **See also:**
+  ## * `rand proc<#rand,int>`_ that returns an integer
+  ## * `rand proc<#rand,float>`_ that returns a floating point number
+  ## * `rand proc<#rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_
+  ##   that accepts a slice
+  runnableExamples:
+    randomize(567)
+    type E = enum a, b, c, d
+
+    assert rand(E) in a..d
+    assert rand(char) in low(char)..high(char)
+    assert rand(int8) in low(int8)..high(int8)
+    assert rand(uint32) in low(uint32)..high(uint32)
+    assert rand(range[1..16]) in 1..16
+
+  result = rand(state, t)
+
+proc sample*[T](r: var Rand; s: set[T]): T =
+  ## Returns a random element from the set `s` using the given state.
+  ##
+  ## **See also:**
+  ## * `sample proc<#sample,set[T]>`_ that uses the default RNG
+  ## * `sample proc<#sample,Rand,openArray[T]>`_ for `openArray`s
+  ## * `sample proc<#sample,Rand,openArray[T],openArray[U]>`_ that uses a
+  ##   cumulative distribution function
+  runnableExamples:
+    var r = initRand(987)
+    let s = {1, 3, 5, 7, 9}
+    assert r.sample(s) in s
+
+  assert card(s) != 0
+  var i = rand(r, card(s) - 1)
+  for e in s:
+    if i == 0: return e
+    dec(i)
+
+proc sample*[T](s: set[T]): T =
+  ## Returns a random element from the set `s`.
+  ##
+  ## If `randomize <#randomize>`_ has not been called, the order of outcomes
+  ## from this proc will always be the same.
+  ##
+  ## This proc uses the default RNG. Thus, it is **not** thread-safe.
+  ##
+  ## **See also:**
+  ## * `sample proc<#sample,Rand,set[T]>`_ that uses a provided state
+  ## * `sample proc<#sample,openArray[T]>`_ for `openArray`s
+  ## * `sample proc<#sample,openArray[T],openArray[U]>`_ that uses a
+  ##   cumulative distribution function
+  runnableExamples:
+    randomize(987)
+    let s = {1, 3, 5, 7, 9}
+    assert sample(s) in s
+
+  sample(state, s)
+
+proc sample*[T](r: var Rand; a: openArray[T]): T =
+  ## Returns a random element from `a` using the given state.
+  ##
+  ## **See also:**
+  ## * `sample proc<#sample,openArray[T]>`_ that uses the default RNG
+  ## * `sample proc<#sample,Rand,openArray[T],openArray[U]>`_ that uses a
+  ##   cumulative distribution function
+  ## * `sample proc<#sample,Rand,set[T]>`_ for sets
+  runnableExamples:
+    let marbles = ["red", "blue", "green", "yellow", "purple"]
+    var r = initRand(456)
+    assert r.sample(marbles) in marbles
+
+  result = a[r.rand(a.low..a.high)]
+
+proc sample*[T](a: openArray[T]): lent T =
+  ## Returns a random element from `a`.
+  ##
+  ## If `randomize <#randomize>`_ has not been called, the order of outcomes
+  ## from this proc will always be the same.
+  ##
+  ## This proc uses the default RNG. Thus, it is **not** thread-safe.
+  ##
+  ## **See also:**
+  ## * `sample proc<#sample,Rand,openArray[T]>`_ that uses a provided state
+  ## * `sample proc<#sample,openArray[T],openArray[U]>`_ that uses a
+  ##   cumulative distribution function
+  ## * `sample proc<#sample,set[T]>`_ for sets
+  runnableExamples:
+    let marbles = ["red", "blue", "green", "yellow", "purple"]
+    randomize(456)
+    assert sample(marbles) in marbles
 
-proc rand*[T](a: openArray[T]): T =
-  ## returns a random element from the openarray `a`.
   result = a[rand(a.low..a.high)]
 
+proc sample*[T, U](r: var Rand; a: openArray[T]; cdf: openArray[U]): T =
+  ## Returns an element from `a` using a cumulative distribution function
+  ## (CDF) and the given state.
+  ##
+  ## The `cdf` argument does not have to be normalized, and it could contain
+  ## any type of elements that can be converted to a `float`. It must be
+  ## the same length as `a`. Each element in `cdf` should be greater than
+  ## or equal to the previous element.
+  ##
+  ## The outcome of the `cumsum<math.html#cumsum,openArray[T]>`_ proc and the
+  ## return value of the `cumsummed<math.html#cumsummed,openArray[T]>`_ proc,
+  ## which are both in the math module, can be used as the `cdf` argument.
+  ##
+  ## **See also:**
+  ## * `sample proc<#sample,openArray[T],openArray[U]>`_ that also utilizes
+  ##   a CDF but uses the default RNG
+  ## * `sample proc<#sample,Rand,openArray[T]>`_ that does not use a CDF
+  ## * `sample proc<#sample,Rand,set[T]>`_ for sets
+  runnableExamples:
+    from std/math import cumsummed
+
+    let marbles = ["red", "blue", "green", "yellow", "purple"]
+    let count = [1, 6, 8, 3, 4]
+    let cdf = count.cumsummed
+    var r = initRand(789)
+    assert r.sample(marbles, cdf) in marbles
+
+  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 =
+  ## Returns an element from `a` using a cumulative distribution function
+  ## (CDF).
+  ##
+  ## This proc works similarly to
+  ## `sample <#sample,Rand,openArray[T],openArray[U]>`_.
+  ## See that proc's documentation for more details.
+  ##
+  ## If `randomize <#randomize>`_ has not been called, the order of outcomes
+  ## from this proc will always be the same.
+  ##
+  ## This proc uses the default RNG. Thus, it is **not** thread-safe.
+  ##
+  ## **See also:**
+  ## * `sample proc<#sample,Rand,openArray[T],openArray[U]>`_ that also utilizes
+  ##   a CDF but uses a provided state
+  ## * `sample proc<#sample,openArray[T]>`_ that does not use a CDF
+  ## * `sample proc<#sample,set[T]>`_ for sets
+  runnableExamples:
+    from std/math import cumsummed
+
+    let marbles = ["red", "blue", "green", "yellow", "purple"]
+    let count = [1, 6, 8, 3, 4]
+    let cdf = count.cumsummed
+    randomize(789)
+    assert sample(marbles, cdf) in marbles
+
+  state.sample(a, cdf)
+
+proc gauss*(r: var Rand; mu = 0.0; sigma = 1.0): float {.since: (1, 3).} =
+  ## Returns a Gaussian random variate,
+  ## with mean `mu` and standard deviation `sigma`
+  ## using the given state.
+  # Ratio of uniforms method for normal
+  # https://www2.econ.osaka-u.ac.jp/~tanizaki/class/2013/econome3/13.pdf
+  const K = sqrt(2 / E)
+  var
+    a = 0.0
+    b = 0.0
+  while true:
+    a = rand(r, 1.0)
+    b = (2.0 * rand(r, 1.0) - 1.0) * K
+    if  b * b <= -4.0 * a * a * ln(a): break
+  result = mu + sigma * (b / a)
+
+proc gauss*(mu = 0.0, sigma = 1.0): float {.since: (1, 3).} =
+  ## Returns a Gaussian random variate,
+  ## with mean `mu` and standard deviation `sigma`.
+  ##
+  ## If `randomize <#randomize>`_ has not been called, the order of outcomes
+  ## from this proc will always be the same.
+  ##
+  ## This proc uses the default RNG. Thus, it is **not** thread-safe.
+  result = gauss(state, mu, sigma)
 
 proc initRand*(seed: int64): Rand =
-  ## Creates a new ``Rand`` state from ``seed``.
-  result.a0 = ui(seed shr 16)
-  result.a1 = ui(seed and 0xffff)
+  ## Initializes a new `Rand <#Rand>`_ state using the given seed.
+  ##
+  ## Providing a specific seed will produce the same results for that seed each time.
+  ##
+  ## The resulting state is independent of the default RNG's state. When `seed == 0`,
+  ## we internally set the seed to an implementation defined non-zero value.
+  ##
+  ## **See also:**
+  ## * `initRand proc<#initRand>`_ that uses the current time
+  ## * `randomize proc<#randomize,int64>`_ that accepts a seed for the default RNG
+  ## * `randomize proc<#randomize>`_ that initializes the default RNG using the current time
+  runnableExamples:
+    from std/times import getTime, toUnix, nanosecond
+
+    var r1 = initRand(123)
+    let now = getTime()
+    var r2 = initRand(now.toUnix * 1_000_000_000 + now.nanosecond)
+  const seedFallback0 = int32.high # arbitrary
+  let seed = if seed != 0: seed else: seedFallback0 # because 0 is a fixed point
+  result.a0 = Ui(seed shr 16)
+  result.a1 = Ui(seed and 0xffff)
+  when not defined(nimLegacyRandomInitRand):
+    # calling `discard next(result)` (even a few times) would still produce
+    # skewed numbers for the 1st call to `rand()`.
+    skipRandomNumbers(result)
   discard next(result)
 
 proc randomize*(seed: int64) {.benign.} =
-  ## Initializes the default random number generator
-  ## with a specific seed.
+  ## Initializes the default random number generator with the given seed.
+  ##
+  ## Providing a specific seed will produce the same results for that seed each time.
+  ##
+  ## **See also:**
+  ## * `initRand proc<#initRand,int64>`_ that initializes a Rand state
+  ##   with a given seed
+  ## * `randomize proc<#randomize>`_ that uses the current time instead
+  ## * `initRand proc<#initRand>`_ that initializes a Rand state using
+  ##   the current time
+  runnableExamples:
+    from std/times import getTime, toUnix, nanosecond
+
+    randomize(123)
+
+    let now = getTime()
+    randomize(now.toUnix * 1_000_000_000 + now.nanosecond)
+
   state = initRand(seed)
 
 proc shuffle*[T](r: var Rand; x: var openArray[T]) =
-  ## Swaps the positions of elements in a sequence randomly.
+  ## Shuffles a sequence of elements in-place using the given state.
+  ##
+  ## **See also:**
+  ## * `shuffle proc<#shuffle,openArray[T]>`_ that uses the default RNG
+  runnableExamples:
+    var cards = ["Ace", "King", "Queen", "Jack", "Ten"]
+    var r = initRand(678)
+    r.shuffle(cards)
+    import std/algorithm
+    assert cards.sorted == @["Ace", "Jack", "King", "Queen", "Ten"]
+
   for i in countdown(x.high, 1):
     let j = r.rand(i)
     swap(x[i], x[j])
 
 proc shuffle*[T](x: var openArray[T]) =
-  ## Swaps the positions of elements in a sequence randomly.
+  ## Shuffles a sequence of elements in-place.
+  ##
+  ## If `randomize <#randomize>`_ has not been called, the order of outcomes
+  ## from this proc will always be the same.
+  ##
+  ## This proc uses the default RNG. Thus, it is **not** thread-safe.
+  ##
+  ## **See also:**
+  ## * `shuffle proc<#shuffle,Rand,openArray[T]>`_ that uses a provided state
+  runnableExamples:
+    var cards = ["Ace", "King", "Queen", "Jack", "Ten"]
+    randomize(678)
+    shuffle(cards)
+    import std/algorithm
+    assert cards.sorted == @["Ace", "Jack", "King", "Queen", "Ten"]
+
   shuffle(state, x)
 
-when not defined(nimscript):
-  import times
+when not defined(standalone):
+  when defined(js):
+    import std/times
+  else:
+    when defined(nimscript):
+      import std/hashes
+    else:
+      import std/[hashes, os, sysrand, monotimes]
+
+      when compileOption("threads"):
+        import std/locks
+        var baseSeedLock: Lock
+        baseSeedLock.initLock
+
+    var baseState: Rand
+
+  proc initRand(): Rand =
+    ## Initializes a new Rand state.
+    ##
+    ## The resulting state is independent of the default RNG's state.
+    ##
+    ## **Note:** Does not work for the compile-time VM.
+    ##
+    ## See also:
+    ## * `initRand proc<#initRand,int64>`_ that accepts a seed for a new Rand state
+    ## * `randomize proc<#randomize>`_ that initializes the default RNG using the current time
+    ## * `randomize proc<#randomize,int64>`_ that accepts a seed for the default RNG
+    when defined(js):
+      let time = int64(times.epochTime() * 1000) and 0x7fff_ffff
+      result = initRand(time)
+    else:
+      proc getRandomState(): Rand =
+        when defined(nimscript):
+          result = Rand(
+            a0: CompileTime.hash.Ui,
+            a1: CompileDate.hash.Ui)
+          if not result.isValid:
+            result = DefaultRandSeed
+        else:
+          var urand: array[sizeof(Rand), byte]
+
+          for i in 0 .. 7:
+            if sysrand.urandom(urand):
+              copyMem(result.addr, urand[0].addr, sizeof(Rand))
+              if result.isValid:
+                break
+
+          if not result.isValid:
+            # Don't try to get alternative random values from other source like time or process/thread id,
+            # because such code would be never tested and is a liability for security.
+            quit("Failed to initializes baseState in random module as sysrand.urandom doesn't work.")
+
+      when compileOption("threads"):
+        baseSeedLock.withLock:
+          if not baseState.isValid:
+            baseState = getRandomState()
+          result = baseState
+          baseState.skipRandomNumbers
+      else:
+        if not baseState.isValid:
+          baseState = getRandomState()
+        result = baseState
+        baseState.skipRandomNumbers
+
+  since (1, 5, 1):
+    export initRand
 
   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)
+    ## Initializes the default random number generator with a seed based on
+    ## random number source.
+    ##
+    ## This proc only needs to be called once, and it should be called before
+    ## the first usage of procs from this module that use the default RNG.
+    ##
+    ## **Note:** Does not work for the compile-time VM.
+    ##
+    ## **See also:**
+    ## * `randomize proc<#randomize,int64>`_ that accepts a seed
+    ## * `initRand proc<#initRand>`_ that initializes a Rand state using
+    ##   the current time
+    ## * `initRand proc<#initRand,int64>`_ that initializes a Rand state
+    ##   with a given seed
+    state = initRand()
 
 {.pop.}
-
-when isMainModule:
-  proc main =
-    var occur: array[1000, int]
-
-    var x = 8234
-    for i in 0..100_000:
-      x = rand(high(occur))
-      inc occur[x]
-    for i, oc in occur:
-      if oc < 69:
-        doAssert false, "too few occurrences of " & $i
-      elif oc > 150:
-        doAssert false, "too many occurrences of " & $i
-
-    var a = [0, 1]
-    shuffle(a)
-    doAssert a[0] == 1
-    doAssert a[1] == 0
-
-    doAssert rand(0) == 0
-    doAssert rand("a") == 'a'
-
-  main()
diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim
index 3946cf85b..5f806bd70 100644
--- a/lib/pure/rationals.nim
+++ b/lib/pure/rationals.nim
@@ -8,55 +8,95 @@
 #
 
 
-## This module implements rational numbers, consisting of a numerator `num` and
-## a denominator `den`, both of type int. The denominator can not be 0.
+## This module implements rational numbers, consisting of a numerator and
+## a denominator. The denominator can not be 0.
 
-import math
-import hashes
+runnableExamples:
+  let
+    r1 = 1 // 2
+    r2 = -3 // 4
+
+  doAssert r1 + r2 == -1 // 4
+  doAssert r1 - r2 ==  5 // 4
+  doAssert r1 * r2 == -3 // 8
+  doAssert r1 / r2 == -2 // 3
+
+import std/[math, hashes]
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 type Rational*[T] = object
-  ## a rational number, consisting of a numerator and denominator
+  ## A rational number, consisting of a numerator `num` and a denominator `den`.
   num*, den*: T
 
-proc initRational*[T:SomeInteger](num, den: T): Rational[T] =
-  ## Create a new rational number.
-  assert(den != 0, "a denominator of zero value is invalid")
+func reduce*[T: SomeInteger](x: var Rational[T]) =
+  ## Reduces the rational number `x`, so that the numerator and denominator
+  ## have no common divisors other than 1 (and -1).
+  ## If `x` is 0, raises `DivByZeroDefect`.
+  ##
+  ## **Note:** This is called automatically by the various operations on rationals.
+  runnableExamples:
+    var r = Rational[int](num: 2, den: 4) # 1/2
+    reduce(r)
+    doAssert r.num == 1
+    doAssert r.den == 2
+  if x.den == 0:
+    raise newException(DivByZeroDefect, "division by zero")
+  let common = gcd(x.num, x.den)
+  if x.den > 0:
+    x.num = x.num div common
+    x.den = x.den div common
+  when T isnot SomeUnsignedInt:
+    if x.den < 0:
+      x.num = -x.num div common
+      x.den = -x.den div common
+
+func initRational*[T: SomeInteger](num, den: T): Rational[T] =
+  ## Creates a new rational number with numerator `num` and denominator `den`.
+  ## `den` must not be 0.
+  ##
+  ## **Note:** `den != 0` is not checked when assertions are turned off.
+  assert(den != 0, "a denominator of zero is invalid")
   result.num = num
   result.den = den
+  reduce(result)
 
-proc `//`*[T](num, den: T): Rational[T] = initRational[T](num, den)
-  ## A friendlier version of `initRational`. Example usage:
-  ##
-  ## .. code-block:: nim
-  ##   var x = 1//3 + 1//5
+func `//`*[T](num, den: T): Rational[T] =
+  ## A friendlier version of `initRational <#initRational,T,T>`_.
+  runnableExamples:
+    let x = 1 // 3 + 1 // 5
+    doAssert x == 8 // 15
+
+  initRational[T](num, den)
+
+func `$`*[T](x: Rational[T]): string =
+  ## Turns a rational number into a string.
+  runnableExamples:
+    doAssert $(1 // 2) == "1/2"
 
-proc `$`*[T](x: Rational[T]): string =
-  ## Turn a rational number into a string.
   result = $x.num & "/" & $x.den
 
-proc toRational*[T:SomeInteger](x: T): Rational[T] =
-  ## Convert some integer `x` to a rational number.
+func toRational*[T: SomeInteger](x: T): Rational[T] =
+  ## Converts some integer `x` to a rational number.
+  runnableExamples:
+    doAssert toRational(42) == 42 // 1
+
   result.num = x
   result.den = 1
 
-proc toRational*(x: float, n: int = high(int) shr (sizeof(int) div 2 * 8)): Rational[int] =
-  ## Calculates the best rational numerator and denominator
-  ## that approximates to `x`, where the denominator is
-  ## smaller than `n` (default is the largest possible
-  ## int to give maximum resolution).
+func toRational*(x: float,
+                 n: int = high(int) shr (sizeof(int) div 2 * 8)): Rational[int] =
+  ## Calculates the best rational approximation of `x`,
+  ## where the denominator is smaller than `n`
+  ## (default is the largest possible `int` for maximal resolution).
   ##
   ## The algorithm is based on the theory of continued fractions.
-  ##
-  ## .. code-block:: Nim
-  ##  import math, rationals
-  ##  for i in 1..10:
-  ##    let t = (10 ^ (i+3)).int
-  ##    let x = toRational(PI, t)
-  ##    let newPI = x.num / x.den
-  ##    echo x, " ", newPI, " error: ", PI - newPI, "  ", t
-
   # David Eppstein / UC Irvine / 8 Aug 1993
   # With corrections from Arno Formella, May 2008
+  runnableExamples:
+    let x = 1.2
+    doAssert x.toRational.toFloat == x
+
   var
     m11, m22 = 1
     m12, m21 = 0
@@ -67,125 +107,114 @@ proc toRational*(x: float, n: int = high(int) shr (sizeof(int) div 2 * 8)): Rati
     swap m22, m21
     m11 = m12 * ai + m11
     m21 = m22 * ai + m21
-    if x == float(ai): break  # division by zero
-    x = 1/(x - float(ai))
-    if x > float(high(int32)): break  # representation failure
+    if x == float(ai): break # division by zero
+    x = 1 / (x - float(ai))
+    if x > float(high(int32)): break # representation failure
     ai = int(x)
   result = m11 // m21
 
-proc toFloat*[T](x: Rational[T]): float =
-  ## Convert a rational number `x` to a float.
+func toFloat*[T](x: Rational[T]): float =
+  ## Converts a rational number `x` to a `float`.
   x.num / x.den
 
-proc toInt*[T](x: Rational[T]): int =
-  ## Convert a rational number `x` to an int. Conversion rounds towards 0 if
+func toInt*[T](x: Rational[T]): int =
+  ## Converts a rational number `x` to an `int`. Conversion rounds towards 0 if
   ## `x` does not contain an integer value.
   x.num div x.den
 
-proc reduce*[T:SomeInteger](x: var Rational[T]) =
-  ## Reduce rational `x`.
-  let common = gcd(x.num, x.den)
-  if x.den > 0:
-    x.num = x.num div common
-    x.den = x.den div common
-  elif x.den < 0:
-    x.num = -x.num div common
-    x.den = -x.den div common
-  else:
-    raise newException(DivByZeroError, "division by zero")
-
-proc `+` *[T](x, y: Rational[T]): Rational[T] =
-  ## Add two rational numbers.
+func `+`*[T](x, y: Rational[T]): Rational[T] =
+  ## Adds two rational numbers.
   let common = lcm(x.den, y.den)
   result.num = common div x.den * x.num + common div y.den * y.num
   result.den = common
   reduce(result)
 
-proc `+` *[T](x: Rational[T], y: T): Rational[T] =
-  ## Add rational `x` to int `y`.
+func `+`*[T](x: Rational[T], y: T): Rational[T] =
+  ## Adds the rational `x` to the int `y`.
   result.num = x.num + y * x.den
   result.den = x.den
 
-proc `+` *[T](x: T, y: Rational[T]): Rational[T] =
-  ## Add int `x` to rational `y`.
+func `+`*[T](x: T, y: Rational[T]): Rational[T] =
+  ## Adds the int `x` to the rational `y`.
   result.num = x * y.den + y.num
   result.den = y.den
 
-proc `+=` *[T](x: var Rational[T], y: Rational[T]) =
-  ## Add rational `y` to rational `x`.
+func `+=`*[T](x: var Rational[T], y: Rational[T]) =
+  ## Adds the rational `y` to the rational `x` in-place.
   let common = lcm(x.den, y.den)
   x.num = common div x.den * x.num + common div y.den * y.num
   x.den = common
   reduce(x)
 
-proc `+=` *[T](x: var Rational[T], y: T) =
-  ## Add int `y` to rational `x`.
+func `+=`*[T](x: var Rational[T], y: T) =
+  ## Adds the int `y` to the rational `x` in-place.
   x.num += y * x.den
 
-proc `-` *[T](x: Rational[T]): Rational[T] =
+func `-`*[T](x: Rational[T]): Rational[T] =
   ## Unary minus for rational numbers.
   result.num = -x.num
   result.den = x.den
 
-proc `-` *[T](x, y: Rational[T]): Rational[T] =
-  ## Subtract two rational numbers.
+func `-`*[T](x, y: Rational[T]): Rational[T] =
+  ## Subtracts two rational numbers.
   let common = lcm(x.den, y.den)
   result.num = common div x.den * x.num - common div y.den * y.num
   result.den = common
   reduce(result)
 
-proc `-` *[T](x: Rational[T], y: T): Rational[T] =
-  ## Subtract int `y` from rational `x`.
+func `-`*[T](x: Rational[T], y: T): Rational[T] =
+  ## Subtracts the int `y` from the rational `x`.
   result.num = x.num - y * x.den
   result.den = x.den
 
-proc `-` *[T](x: T, y: Rational[T]): Rational[T] =
-  ## Subtract rational `y` from int `x`.
+func `-`*[T](x: T, y: Rational[T]): Rational[T] =
+  ## Subtracts the rational `y` from the int `x`.
   result.num = x * y.den - y.num
   result.den = y.den
 
-proc `-=` *[T](x: var Rational[T], y: Rational[T]) =
-  ## Subtract rational `y` from rational `x`.
+func `-=`*[T](x: var Rational[T], y: Rational[T]) =
+  ## Subtracts the rational `y` from the rational `x` in-place.
   let common = lcm(x.den, y.den)
   x.num = common div x.den * x.num - common div y.den * y.num
   x.den = common
   reduce(x)
 
-proc `-=` *[T](x: var Rational[T], y: T) =
-  ## Subtract int `y` from rational `x`.
+func `-=`*[T](x: var Rational[T], y: T) =
+  ## Subtracts the int `y` from the rational `x` in-place.
   x.num -= y * x.den
 
-proc `*` *[T](x, y: Rational[T]): Rational[T] =
-  ## Multiply two rational numbers.
+func `*`*[T](x, y: Rational[T]): Rational[T] =
+  ## Multiplies two rational numbers.
   result.num = x.num * y.num
   result.den = x.den * y.den
   reduce(result)
 
-proc `*` *[T](x: Rational[T], y: T): Rational[T] =
-  ## Multiply rational `x` with int `y`.
+func `*`*[T](x: Rational[T], y: T): Rational[T] =
+  ## Multiplies the rational `x` with the int `y`.
   result.num = x.num * y
   result.den = x.den
   reduce(result)
 
-proc `*` *[T](x: T, y: Rational[T]): Rational[T] =
-  ## Multiply int `x` with rational `y`.
+func `*`*[T](x: T, y: Rational[T]): Rational[T] =
+  ## Multiplies the int `x` with the rational `y`.
   result.num = x * y.num
   result.den = y.den
   reduce(result)
 
-proc `*=` *[T](x: var Rational[T], y: Rational[T]) =
-  ## Multiply rationals `y` to `x`.
+func `*=`*[T](x: var Rational[T], y: Rational[T]) =
+  ## Multiplies the rational `x` by `y` in-place.
   x.num *= y.num
   x.den *= y.den
   reduce(x)
 
-proc `*=` *[T](x: var Rational[T], y: T) =
-  ## Multiply int `y` to rational `x`.
+func `*=`*[T](x: var Rational[T], y: T) =
+  ## Multiplies the rational `x` by the int `y` in-place.
   x.num *= y
   reduce(x)
 
-proc reciprocal*[T](x: Rational[T]): Rational[T] =
-  ## Calculate the reciprocal of `x`. (1/x)
+func reciprocal*[T](x: Rational[T]): Rational[T] =
+  ## Calculates the reciprocal of `x` (`1/x`).
+  ## If `x` is 0, raises `DivByZeroDefect`.
   if x.num > 0:
     result.num = x.den
     result.den = x.num
@@ -193,83 +222,94 @@ proc reciprocal*[T](x: Rational[T]): Rational[T] =
     result.num = -x.den
     result.den = -x.num
   else:
-    raise newException(DivByZeroError, "division by zero")
+    raise newException(DivByZeroDefect, "division by zero")
 
-proc `/`*[T](x, y: Rational[T]): Rational[T] =
-  ## Divide rationals `x` by `y`.
+func `/`*[T](x, y: Rational[T]): Rational[T] =
+  ## Divides the rational `x` by the rational `y`.
   result.num = x.num * y.den
   result.den = x.den * y.num
   reduce(result)
 
-proc `/`*[T](x: Rational[T], y: T): Rational[T] =
-  ## Divide rational `x` by int `y`.
+func `/`*[T](x: Rational[T], y: T): Rational[T] =
+  ## Divides the rational `x` by the int `y`.
   result.num = x.num
   result.den = x.den * y
   reduce(result)
 
-proc `/`*[T](x: T, y: Rational[T]): Rational[T] =
-  ## Divide int `x` by Rational `y`.
+func `/`*[T](x: T, y: Rational[T]): Rational[T] =
+  ## Divides the int `x` by the rational `y`.
   result.num = x * y.den
   result.den = y.num
   reduce(result)
 
-proc `/=`*[T](x: var Rational[T], y: Rational[T]) =
-  ## Divide rationals `x` by `y` in place.
+func `/=`*[T](x: var Rational[T], y: Rational[T]) =
+  ## Divides the rational `x` by the rational `y` in-place.
   x.num *= y.den
   x.den *= y.num
   reduce(x)
 
-proc `/=`*[T](x: var Rational[T], y: T) =
-  ## Divide rational `x` by int `y` in place.
+func `/=`*[T](x: var Rational[T], y: T) =
+  ## Divides the rational `x` by the int `y` in-place.
   x.den *= y
   reduce(x)
 
-proc cmp*(x, y: Rational): int {.procvar.} =
-  ## Compares two rationals.
+func cmp*(x, y: Rational): int =
+  ## Compares two rationals. Returns
+  ## * a value less than zero, if `x < y`
+  ## * a value greater than zero, if `x > y`
+  ## * zero, if `x == y`
   (x - y).num
 
-proc `<` *(x, y: Rational): bool =
+func `<`*(x, y: Rational): bool =
+  ## Returns true if `x` is less than `y`.
   (x - y).num < 0
 
-proc `<=` *(x, y: Rational): bool =
+func `<=`*(x, y: Rational): bool =
+  ## Returns tue if `x` is less than or equal to `y`.
   (x - y).num <= 0
 
-proc `==` *(x, y: Rational): bool =
+func `==`*(x, y: Rational): bool =
+  ## Compares two rationals for equality.
   (x - y).num == 0
 
-proc abs*[T](x: Rational[T]): Rational[T] =
+func abs*[T](x: Rational[T]): Rational[T] =
+  ## Returns the absolute value of `x`.
+  runnableExamples:
+    doAssert abs(1 // 2) == 1 // 2
+    doAssert abs(-1 // 2) == 1 // 2
+
   result.num = abs x.num
   result.den = abs x.den
 
-proc `div`*[T: SomeInteger](x, y: Rational[T]): T =
+func `div`*[T: SomeInteger](x, y: Rational[T]): T =
   ## Computes the rational truncated division.
   (x.num * y.den) div (y.num * x.den)
 
-proc `mod`*[T: SomeInteger](x, y: Rational[T]): Rational[T] =
+func `mod`*[T: SomeInteger](x, y: Rational[T]): Rational[T] =
   ## Computes the rational modulo by truncated division (remainder).
-  ## This is same as ``x - (x div y) * y``.
+  ## This is same as `x - (x div y) * y`.
   result = ((x.num * y.den) mod (y.num * x.den)) // (x.den * y.den)
   reduce(result)
 
-proc floorDiv*[T: SomeInteger](x, y: Rational[T]): T =
+func floorDiv*[T: SomeInteger](x, y: Rational[T]): T =
   ## Computes the rational floor division.
   ##
-  ## 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``
+  ## 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.
   floorDiv(x.num * y.den, y.num * x.den)
 
-proc floorMod*[T: SomeInteger](x, y: Rational[T]): Rational[T] =
+func floorMod*[T: SomeInteger](x, y: Rational[T]): Rational[T] =
   ## Computes the rational modulo by floor division (modulo).
   ##
-  ## This is same as ``x - floorDiv(x, y) * y``.
-  ## This proc behaves the same as the ``%`` operator in python.
+  ## This is same as `x - floorDiv(x, y) * y`.
+  ## This func behaves the same as the `%` operator in Python.
   result = floorMod(x.num * y.den, y.num * x.den) // (x.den * y.den)
   reduce(result)
 
-proc hash*[T](x: Rational[T]): Hash =
-  ## Computes hash for rational `x`
+func hash*[T](x: Rational[T]): Hash =
+  ## Computes the hash for the rational `x`.
   # reduce first so that hash(x) == hash(y) for x == y
   var copy = x
   reduce(copy)
@@ -279,99 +319,22 @@ proc hash*[T](x: Rational[T]): Hash =
   h = h !& hash(copy.den)
   result = !$h
 
-when isMainModule:
-  var
-    z = Rational[int](num: 0, den: 1)
-    o = initRational(num=1, den=1)
-    a = initRational(1, 2)
-    b = -1 // -2
-    m1 = -1 // 1
-    tt = 10 // 2
-
-  assert( a     == a )
-  assert( (a-a) == z )
-  assert( (a+b) == o )
-  assert( (a/b) == o )
-  assert( (a*b) == 1 // 4 )
-  assert( (3/a) == 6 // 1 )
-  assert( (a/3) == 1 // 6 )
-  assert( a*b   == 1 // 4 )
-  assert( tt*z  == z )
-  assert( 10*a  == tt )
-  assert( a*10  == tt )
-  assert( tt/10 == a  )
-  assert( a-m1  == 3 // 2 )
-  assert( a+m1  == -1 // 2 )
-  assert( m1+tt == 16 // 4 )
-  assert( m1-tt == 6 // -1 )
-
-  assert( z < o )
-  assert( z <= o )
-  assert( z == z )
-  assert( cmp(z, o) < 0 )
-  assert( cmp(o, z) > 0 )
-
-  assert( o == o )
-  assert( o >= o )
-  assert( not(o > o) )
-  assert( cmp(o, o) == 0 )
-  assert( cmp(z, z) == 0 )
-  assert( hash(o) == hash(o) )
-
-  assert( a == b )
-  assert( a >= b )
-  assert( not(b > a) )
-  assert( cmp(a, b) == 0 )
-  assert( hash(a) == hash(b) )
-
-  var x = 1//3
-
-  x *= 5//1
-  assert( x == 5//3 )
-  x += 2 // 9
-  assert( x == 17//9 )
-  x -= 9//18
-  assert( x == 25//18 )
-  x /= 1//2
-  assert( x == 50//18 )
-
-  var y = 1//3
-
-  y *= 4
-  assert( y == 4//3 )
-  y += 5
-  assert( y == 19//3 )
-  y -= 2
-  assert( y == 13//3 )
-  y /= 9
-  assert( y == 13//27 )
-
-  assert toRational(5) == 5//1
-  assert abs(toFloat(y) - 0.4814814814814815) < 1.0e-7
-  assert toInt(z) == 0
-
-  when sizeof(int) == 8:
-    assert toRational(0.98765432) == 2111111029 // 2137499919
-    assert toRational(PI) == 817696623 // 260280919
-  when sizeof(int) == 4:
-    assert toRational(0.98765432) == 80 // 81
-    assert toRational(PI) == 355 // 113
-
-  assert toRational(0.1) == 1 // 10
-  assert toRational(0.9) == 9 // 10
-
-  assert toRational(0.0) == 0 // 1
-  assert toRational(-0.25) == 1 // -4
-  assert toRational(3.2) == 16 // 5
-  assert toRational(0.33) == 33 // 100
-  assert toRational(0.22) == 11 // 50
-  assert toRational(10.0) == 10 // 1
-
-  assert (1//1) div (3//10) == 3
-  assert (-1//1) div (3//10) == -3
-  assert (3//10) mod (1//1) == 3//10
-  assert (-3//10) mod (1//1) == -3//10
-  assert floorDiv(1//1, 3//10) == 3
-  assert floorDiv(-1//1, 3//10) == -4
-  assert floorMod(3//10, 1//1) == 3//10
-  assert floorMod(-3//10, 1//1) == 7//10
+func `^`*[T: SomeInteger](x: Rational[T], y: T): Rational[T] =
+  ## Computes `x` to the power of `y`.
+  ##
+  ## The exponent `y` must be an integer. Negative exponents are supported
+  ## but floating point exponents are not.
+  runnableExamples:
+    doAssert (-3 // 5) ^ 0 == (1 // 1)
+    doAssert (-3 // 5) ^ 1 == (-3 // 5)
+    doAssert (-3 // 5) ^ 2 == (9 // 25)
+    doAssert (-3 // 5) ^ -2 == (25 // 9)
+
+  if y >= 0:
+    result.num = x.num ^ y
+    result.den = x.den ^ y
+  else:
+    result.num = x.den ^ -y
+    result.den = x.num ^ -y
+  # Note that all powers of reduced rationals are already reduced,
+  # so we don't need to call reduce() here
diff --git a/lib/pure/reservedmem.nim b/lib/pure/reservedmem.nim
new file mode 100644
index 000000000..ffa0128dc
--- /dev/null
+++ b/lib/pure/reservedmem.nim
@@ -0,0 +1,229 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Nim Contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## :Authors: Zahary Karadjov
+##
+## This module provides utilities for reserving portions of the
+## address space of a program without consuming physical memory.
+## It can be used to implement a dynamically resizable buffer that
+## is guaranteed to remain in the same memory location. The buffer
+## will be able to grow up to the size of the initially reserved
+## portion of the address space.
+##
+## Unstable API.
+
+from std/oserrors import raiseOSError, osLastError
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+template distance*(lhs, rhs: pointer): int =
+  cast[int](rhs) - cast[int](lhs)
+
+template shift*(p: pointer, distance: int): pointer =
+  cast[pointer](cast[int](p) + distance)
+
+type
+  MemAccessFlags* = int
+
+  ReservedMem* = object
+    memStart: pointer
+    usedMemEnd: pointer
+    committedMemEnd: pointer
+    memEnd: pointer
+    maxCommittedAndUnusedPages: int
+    accessFlags: MemAccessFlags
+
+  ReservedMemSeq*[T] = object
+    mem: ReservedMem
+
+when defined(windows):
+  import std/winlean
+  import std/private/win_getsysteminfo
+
+  proc getAllocationGranularity: uint =
+    var sysInfo: SystemInfo
+    getSystemInfo(addr sysInfo)
+    return uint(sysInfo.dwAllocationGranularity)
+
+  let allocationGranularity = getAllocationGranularity().int
+
+  const
+    memNoAccess = MemAccessFlags(PAGE_NOACCESS)
+    memExec* = MemAccessFlags(PAGE_EXECUTE)
+    memExecRead* = MemAccessFlags(PAGE_EXECUTE_READ)
+    memExecReadWrite* = MemAccessFlags(PAGE_EXECUTE_READWRITE)
+    memRead* = MemAccessFlags(PAGE_READONLY)
+    memReadWrite* = MemAccessFlags(PAGE_READWRITE)
+
+  template check(expr) =
+    let r = expr
+    if r == cast[typeof(r)](0):
+      raiseOSError(osLastError())
+
+else:
+  import std/posix
+
+  let allocationGranularity = sysconf(SC_PAGESIZE)
+
+  let
+    memNoAccess = MemAccessFlags(PROT_NONE)
+    memExec* = MemAccessFlags(PROT_EXEC)
+    memExecRead* = MemAccessFlags(PROT_EXEC or PROT_READ)
+    memExecReadWrite* = MemAccessFlags(PROT_EXEC or PROT_READ or PROT_WRITE)
+    memRead* = MemAccessFlags(PROT_READ)
+    memReadWrite* = MemAccessFlags(PROT_READ or PROT_WRITE)
+
+  template check(expr) =
+    if not expr:
+      raiseOSError(osLastError())
+
+func nextAlignedOffset(n, alignment: int): int =
+  result = n
+  let m = n mod alignment
+  if m != 0: result += alignment - m
+
+
+when defined(windows):
+  const
+    MEM_DECOMMIT = 0x4000
+    MEM_RESERVE = 0x2000
+    MEM_COMMIT = 0x1000
+  proc virtualFree(lpAddress: pointer, dwSize: int,
+                   dwFreeType: int32): cint {.header: "<windows.h>", stdcall,
+                   importc: "VirtualFree".}
+  proc virtualAlloc(lpAddress: pointer, dwSize: int, flAllocationType,
+                    flProtect: int32): pointer {.
+                    header: "<windows.h>", stdcall, importc: "VirtualAlloc".}
+
+proc init*(T: type ReservedMem,
+           maxLen: Natural,
+           initLen: Natural = 0,
+           initCommitLen = initLen,
+           memStart = pointer(nil),
+           accessFlags = memReadWrite,
+           maxCommittedAndUnusedPages = 3): ReservedMem =
+
+  assert initLen <= initCommitLen
+  let commitSize = nextAlignedOffset(initCommitLen, allocationGranularity)
+
+  when defined(windows):
+    result.memStart = virtualAlloc(memStart, maxLen, MEM_RESERVE,
+        accessFlags.cint)
+    check result.memStart
+    if commitSize > 0:
+      check virtualAlloc(result.memStart, commitSize, MEM_COMMIT,
+          accessFlags.cint)
+  else:
+    var allocFlags = MAP_PRIVATE or MAP_ANONYMOUS # or MAP_NORESERVE
+                                                  # if memStart != nil:
+                                                  #  allocFlags = allocFlags or MAP_FIXED_NOREPLACE
+    result.memStart = mmap(memStart, maxLen, PROT_NONE, allocFlags, -1, 0)
+    check result.memStart != MAP_FAILED
+    if commitSize > 0:
+      check mprotect(result.memStart, commitSize, cint(accessFlags)) == 0
+
+  result.usedMemEnd = result.memStart.shift(initLen)
+  result.committedMemEnd = result.memStart.shift(commitSize)
+  result.memEnd = result.memStart.shift(maxLen)
+  result.accessFlags = accessFlags
+  result.maxCommittedAndUnusedPages = maxCommittedAndUnusedPages
+
+func len*(m: ReservedMem): int =
+  distance(m.memStart, m.usedMemEnd)
+
+func commitedLen*(m: ReservedMem): int =
+  distance(m.memStart, m.committedMemEnd)
+
+func maxLen*(m: ReservedMem): int =
+  distance(m.memStart, m.memEnd)
+
+proc setLen*(m: var ReservedMem, newLen: int) =
+  let len = m.len
+  m.usedMemEnd = m.memStart.shift(newLen)
+  if newLen > len:
+    let d = distance(m.committedMemEnd, m.usedMemEnd)
+    if d > 0:
+      let commitExtensionSize = nextAlignedOffset(d, allocationGranularity)
+      when defined(windows):
+        check virtualAlloc(m.committedMemEnd, commitExtensionSize,
+                           MEM_COMMIT, m.accessFlags.cint)
+      else:
+        check mprotect(m.committedMemEnd, commitExtensionSize,
+            m.accessFlags.cint) == 0
+  else:
+    let d = distance(m.usedMemEnd, m.committedMemEnd) -
+            m.maxCommittedAndUnusedPages * allocationGranularity
+    if d > 0:
+      let commitSizeShrinkage = nextAlignedOffset(d, allocationGranularity)
+      let newCommitEnd = m.committedMemEnd.shift(-commitSizeShrinkage)
+
+      when defined(windows):
+        check virtualFree(newCommitEnd, commitSizeShrinkage, MEM_DECOMMIT)
+      else:
+        check posix_madvise(newCommitEnd, commitSizeShrinkage,
+                            POSIX_MADV_DONTNEED) == 0
+
+      m.committedMemEnd = newCommitEnd
+
+proc init*(SeqType: type ReservedMemSeq,
+           maxLen: Natural,
+           initLen: Natural = 0,
+           initCommitLen: Natural = 0,
+           memStart = pointer(nil),
+           accessFlags = memReadWrite,
+           maxCommittedAndUnusedPages = 3): SeqType =
+
+  let elemSize = sizeof(SeqType.T)
+  result.mem = ReservedMem.init(maxLen * elemSize,
+                                initLen * elemSize,
+                                initCommitLen * elemSize,
+                                memStart, accessFlags,
+                                maxCommittedAndUnusedPages)
+
+func `[]`*[T](s: ReservedMemSeq[T], pos: Natural): lent T =
+  let elemAddr = s.mem.memStart.shift(pos * sizeof(T))
+  rangeCheck elemAddr < s.mem.usedMemEnd
+  result = (cast[ptr T](elemAddr))[]
+
+func `[]`*[T](s: var ReservedMemSeq[T], pos: Natural): var T =
+  let elemAddr = s.mem.memStart.shift(pos * sizeof(T))
+  rangeCheck elemAddr < s.mem.usedMemEnd
+  result = (cast[ptr T](elemAddr))[]
+
+func `[]`*[T](s: ReservedMemSeq[T], rpos: BackwardsIndex): lent T =
+  return s[int(s.len) - int(rpos)]
+
+func `[]`*[T](s: var ReservedMemSeq[T], rpos: BackwardsIndex): var T =
+  return s[int(s.len) - int(rpos)]
+
+func len*[T](s: ReservedMemSeq[T]): int =
+  s.mem.len div sizeof(T)
+
+proc setLen*[T](s: var ReservedMemSeq[T], newLen: int) =
+  # TODO call destructors
+  s.mem.setLen(newLen * sizeof(T))
+
+proc add*[T](s: var ReservedMemSeq[T], val: T) =
+  let len = s.len
+  s.setLen(len + 1)
+  s[len] = val
+
+proc pop*[T](s: var ReservedMemSeq[T]): T =
+  assert s.usedMemEnd != s.memStart
+  let lastIdx = s.len - 1
+  result = s[lastIdx]
+  s.setLen(lastIdx)
+
+func commitedLen*[T](s: ReservedMemSeq[T]): int =
+  s.mem.commitedLen div sizeof(T)
+
+func maxLen*[T](s: ReservedMemSeq[T]): int =
+  s.mem.maxLen div sizeof(T)
+
diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim
index 9b9cdb52a..8750aca87 100644
--- a/lib/pure/ropes.nim
+++ b/lib/pure/ropes.nim
@@ -8,20 +8,21 @@
 #
 
 ## This module contains support for a `rope`:idx: data type.
-## Ropes can represent very long strings efficiently; especially concatenation
+## Ropes can represent very long strings efficiently; in particular, concatenation
 ## is done in O(1) instead of O(n). They are essentially concatenation
 ## trees that are only flattened when converting to a native Nim
-## string. The empty string is represented by ``nil``. Ropes are immutable and
+## string. The empty string is represented by `nil`. Ropes are immutable and
 ## subtrees can be shared without copying.
 ## Leaves can be cached for better memory efficiency at the cost of
 ## runtime efficiency.
 
-include "system/inclrtl"
-import streams
+include system/inclrtl
+import std/streams
 
-{.deadCodeElim: on.}  # dce option deprecated
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, formatfloat, assertions]
 
-{.push debugger:off .} # the user does not want to trace a part
+{.push debugger: off.} # the user does not want to trace a part
                        # of the standard library!
 
 const
@@ -31,13 +32,11 @@ var
   cacheEnabled = false
 
 type
-  Rope* = ref RopeObj ## empty rope is represented by nil
-  RopeObj {.acyclic.} = object
+  Rope* {.acyclic.} = ref object
+    ## A rope data type. The empty rope is represented by `nil`.
     left, right: Rope
     length: int
-    data: string # != nil if a leaf
-
-proc isConc(r: Rope): bool {.inline.} = return isNil(r.data)
+    data: string # not empty if a leaf
 
 # Note that the left and right pointers are not needed for leafs.
 # Leaves have relatively high memory overhead (~30 bytes on a 32
@@ -48,9 +47,8 @@ proc isConc(r: Rope): bool {.inline.} = return isNil(r.data)
 # pointers.
 
 proc len*(a: Rope): int {.rtl, extern: "nro$1".} =
-  ## the rope's length
-  if a == nil: result = 0
-  else: result = a.length
+  ## The rope's length.
+  if a == nil: 0 else: a.length
 
 proc newRope(): Rope = new(result)
 proc newRope(data: string): Rope =
@@ -59,8 +57,8 @@ proc newRope(data: string): Rope =
   result.data = data
 
 var
-  cache {.threadvar.}: Rope     # the root of the cache tree
-  N {.threadvar.}: Rope         # dummy rope needed for splay algorithm
+  cache {.threadvar.}: Rope # the root of the cache tree
+  N {.threadvar.}: Rope     # dummy rope needed for splay algorithm
 
 when countCacheMisses:
   var misses, hits: int
@@ -69,7 +67,7 @@ proc splay(s: string, tree: Rope, cmpres: var int): Rope =
   var c: int
   var t = tree
   N.left = nil
-  N.right = nil               # reset to nil
+  N.right = nil # reset to nil
   var le = N
   var r = N
   while true:
@@ -129,8 +127,12 @@ proc insertInCache(s: string, tree: Rope): Rope =
       result.left = t
       t.right = nil
 
-proc rope*(s: string = nil): Rope {.rtl, extern: "nro$1Str".} =
+proc rope*(s: string = ""): Rope {.rtl, extern: "nro$1Str".} =
   ## Converts a string to a rope.
+  runnableExamples:
+    let r = rope("I'm a rope")
+    doAssert $r == "I'm a rope"
+
   if s.len == 0:
     result = nil
   else:
@@ -146,10 +148,18 @@ proc rope*(s: string = nil): Rope {.rtl, extern: "nro$1Str".} =
 
 proc rope*(i: BiggestInt): Rope {.rtl, extern: "nro$1BiggestInt".} =
   ## Converts an int to a rope.
+  runnableExamples:
+    let r = rope(429)
+    doAssert $r == "429"
+
   result = rope($i)
 
 proc rope*(f: BiggestFloat): Rope {.rtl, extern: "nro$1BiggestFloat".} =
   ## Converts a float to a rope.
+  runnableExamples:
+    let r = rope(4.29)
+    doAssert $r == "4.29"
+
   result = rope($f)
 
 proc enableCache*() {.rtl, extern: "nro$1".} =
@@ -158,12 +168,16 @@ proc enableCache*() {.rtl, extern: "nro$1".} =
   cacheEnabled = true
 
 proc disableCache*() {.rtl, extern: "nro$1".} =
-  ## the cache is discarded and disabled. The GC will reuse its used memory.
+  ## The cache is discarded and disabled. The GC will reuse its used memory.
   cache = nil
   cacheEnabled = false
 
 proc `&`*(a, b: Rope): Rope {.rtl, extern: "nroConcRopeRope".} =
-  ## the concatenation operator for ropes.
+  ## The concatenation operator for ropes.
+  runnableExamples:
+    let r = rope("Hello, ") & rope("Nim!")
+    doAssert $r == "Hello, Nim!"
+
   if a == nil:
     result = b
   elif b == nil:
@@ -171,137 +185,127 @@ proc `&`*(a, b: Rope): Rope {.rtl, extern: "nroConcRopeRope".} =
   else:
     result = newRope()
     result.length = a.length + b.length
-    when false:
-      # XXX rebalancing would be nice, but is too expensive.
-      result.left = a.left
-      var x = newRope()
-      x.left = a.right
-      x.right = b
-      result.right = x
-    else:
-      result.left = a
-      result.right = b
+    result.left = a
+    result.right = b
 
 proc `&`*(a: Rope, b: string): Rope {.rtl, extern: "nroConcRopeStr".} =
-  ## the concatenation operator for ropes.
+  ## The concatenation operator for ropes.
+  runnableExamples:
+    let r = rope("Hello, ") & "Nim!"
+    doAssert $r == "Hello, Nim!"
+
   result = a & rope(b)
 
 proc `&`*(a: string, b: Rope): Rope {.rtl, extern: "nroConcStrRope".} =
-  ## the concatenation operator for ropes.
+  ## The concatenation operator for ropes.
+  runnableExamples:
+    let r = "Hello, " & rope("Nim!")
+    doAssert $r == "Hello, Nim!"
+
   result = rope(a) & b
 
 proc `&`*(a: openArray[Rope]): Rope {.rtl, extern: "nroConcOpenArray".} =
-  ## the concatenation operator for an openarray of ropes.
-  for i in countup(0, high(a)): result = result & a[i]
+  ## The concatenation operator for an `openArray` of ropes.
+  runnableExamples:
+    let r = &[rope("Hello, "), rope("Nim"), rope("!")]
+    doAssert $r == "Hello, Nim!"
+
+  for item in a: result = result & item
 
 proc add*(a: var Rope, b: Rope) {.rtl, extern: "nro$1Rope".} =
-  ## adds `b` to the rope `a`.
+  ## Adds `b` to the rope `a`.
+  runnableExamples:
+    var r = rope("Hello, ")
+    r.add(rope("Nim!"))
+    doAssert $r == "Hello, Nim!"
+
   a = a & b
 
 proc add*(a: var Rope, b: string) {.rtl, extern: "nro$1Str".} =
-  ## adds `b` to the rope `a`.
+  ## Adds `b` to the rope `a`.
+  runnableExamples:
+    var r = rope("Hello, ")
+    r.add("Nim!")
+    doAssert $r == "Hello, Nim!"
+
   a = a & b
 
 proc `[]`*(r: Rope, i: int): char {.rtl, extern: "nroCharAt".} =
-  ## returns the character at position `i` in the rope `r`. This is quite
-  ## expensive! Worst-case: O(n). If ``i >= r.len``, ``\0`` is returned.
+  ## Returns the character at position `i` in the rope `r`. This is quite
+  ## expensive! Worst-case: O(n). If `i >= r.len or i < 0`, `\0` is returned.
+  runnableExamples:
+    let r = rope("Hello, Nim!")
+
+    doAssert r[0] == 'H'
+    doAssert r[7] == 'N'
+    doAssert r[22] == '\0'
+
   var x = r
   var j = i
-  if x == nil: return
+  if x == nil or i < 0 or i >= r.len: return
   while true:
-    if not isConc(x):
-      if x.data.len <% j: return x.data[j]
-      return '\0'
+    if x != nil and x.data.len > 0:
+      # leaf
+      return x.data[j]
     else:
-      if x.left.len >% j:
+      if x.left.length > j:
         x = x.left
       else:
+        dec(j, x.left.length)
         x = x.right
-        dec(j, x.len)
 
 iterator leaves*(r: Rope): string =
-  ## iterates over any leaf string in the rope `r`.
+  ## Iterates over any leaf string in the rope `r`.
+  runnableExamples:
+    let r = rope("Hello") & rope(", Nim!")
+    let s = ["Hello", ", Nim!"]
+    var index = 0
+    for leave in r.leaves:
+      doAssert leave == s[index]
+      inc(index)
+
   if r != nil:
     var stack = @[r]
     while stack.len > 0:
       var it = stack.pop
-      while isConc(it):
+      while it.left != nil:
+        assert(it.right != nil)
         stack.add(it.right)
         it = it.left
         assert(it != nil)
-      assert(it.data != nil)
       yield it.data
 
 iterator items*(r: Rope): char =
-  ## iterates over any character in the rope `r`.
+  ## Iterates over any character in the rope `r`.
   for s in leaves(r):
     for c in items(s): yield c
 
 proc write*(f: File, r: Rope) {.rtl, extern: "nro$1".} =
-  ## writes a rope to a file.
+  ## Writes a rope to a file.
   for s in leaves(r): write(f, s)
 
 proc write*(s: Stream, r: Rope) {.rtl, extern: "nroWriteStream".} =
-  ## writes a rope to a stream.
+  ## Writes a rope to a stream.
   for rs in leaves(r): write(s, rs)
 
-proc `$`*(r: Rope): string  {.rtl, extern: "nroToString".}=
-  ## converts a rope back to a string.
+proc `$`*(r: Rope): string {.rtl, extern: "nroToString".} =
+  ## Converts a rope back to a string.
   result = newStringOfCap(r.len)
   for s in leaves(r): add(result, s)
 
-when false:
-  # Format string caching seems reasonable: All leaves can be shared and format
-  # string parsing has to be done only once. A compiled format string is stored
-  # as a rope. A negative length is used for the index into the args array.
-  proc compiledArg(idx: int): Rope =
-    new(result)
-    result.length = -idx
-
-  proc compileFrmt(frmt: string): Rope =
-    var i = 0
-    var length = len(frmt)
-    result = nil
-    var num = 0
-    while i < length:
-      if frmt[i] == '$':
-        inc(i)
-        case frmt[i]
-        of '$':
-          add(result, "$")
-          inc(i)
-        of '#':
-          inc(i)
-          add(result, compiledArg(num+1))
-          inc(num)
-        of '0'..'9':
-          var j = 0
-          while true:
-            j = j * 10 + ord(frmt[i]) - ord('0')
-            inc(i)
-            if frmt[i] notin {'0'..'9'}: break
-          add(s, compiledArg(j))
-        of '{':
-          inc(i)
-          var j = 0
-          while frmt[i] in {'0'..'9'}:
-            j = j * 10 + ord(frmt[i]) - ord('0')
-            inc(i)
-          if frmt[i] == '}': inc(i)
-          else: raise newException(EInvalidValue, "invalid format string")
-          add(s, compiledArg(j))
-        else: raise newException(EInvalidValue, "invalid format string")
-      var start = i
-      while i < length:
-        if frmt[i] != '$': inc(i)
-        else: break
-      if i - 1 >= start:
-        add(result, substr(frmt, start, i-1))
-
-proc `%`*(frmt: string, args: openArray[Rope]): Rope {.
-  rtl, extern: "nroFormat".} =
-  ## `%` substitution operator for ropes. Does not support the ``$identifier``
-  ## nor ``${identifier}`` notations.
+proc `%`*(frmt: string, args: openArray[Rope]): Rope {.rtl, extern: "nroFormat".} =
+  ## `%` substitution operator for ropes. Does not support the `$identifier`
+  ## nor `${identifier}` notations.
+  runnableExamples:
+    let r1 = "$1 $2 $3" % [rope("Nim"), rope("is"), rope("a great language")]
+    doAssert $r1 == "Nim is a great language"
+
+    let r2 = "$# $# $#" % [rope("Nim"), rope("is"), rope("a great language")]
+    doAssert $r2 == "Nim is a great language"
+
+    let r3 = "${1} ${2} ${3}" % [rope("Nim"), rope("is"), rope("a great language")]
+    doAssert $r3 == "Nim is a great language"
+
   var i = 0
   var length = len(frmt)
   result = nil
@@ -322,7 +326,7 @@ proc `%`*(frmt: string, args: openArray[Rope]): Rope {.
         while true:
           j = j * 10 + ord(frmt[i]) - ord('0')
           inc(i)
-          if frmt[i] notin {'0'..'9'}: break
+          if i >= frmt.len or frmt[i] notin {'0'..'9'}: break
         add(result, args[j-1])
       of '{':
         inc(i)
@@ -342,51 +346,54 @@ proc `%`*(frmt: string, args: openArray[Rope]): Rope {.
     if i - 1 >= start:
       add(result, substr(frmt, start, i - 1))
 
-proc addf*(c: var Rope, frmt: string, args: openArray[Rope]) {.
-  rtl, extern: "nro$1".} =
-  ## shortcut for ``add(c, frmt % args)``.
-  add(c, frmt % args)
+proc addf*(c: var Rope, frmt: string, args: openArray[Rope]) {.rtl, extern: "nro$1".} =
+  ## Shortcut for `add(c, frmt % args)`.
+  runnableExamples:
+    var r = rope("Dash: ")
+    r.addf "$1 $2 $3", [rope("Nim"), rope("is"), rope("a great language")]
+    doAssert $r == "Dash: Nim is a great language"
 
-const
-  bufSize = 1024              # 1 KB is reasonable
-
-proc equalsFile*(r: Rope, f: File): bool {.rtl, extern: "nro$1File".} =
-  ## returns true if the contents of the file `f` equal `r`.
-  var
-    buf: array[bufSize, char]
-    bpos = buf.len
-    blen = buf.len
+  add(c, frmt % args)
 
-  for s in leaves(r):
-    var spos = 0
-    let slen = s.len
-    while spos < slen:
-      if bpos == blen:
-        # Read more data
-        bpos = 0
-        blen = readBuffer(f, addr(buf[0]), buf.len)
-        if blen == 0:  # no more data in file
-          result = false
-          return
-      let n = min(blen - bpos, slen - spos)
-      # TODO There's gotta be a better way of comparing here...
-      if not equalMem(addr(buf[bpos]),
-                      cast[pointer](cast[int](cstring(s))+spos), n):
-        result = false
-        return
-      spos += n
-      bpos += n
-
-  result = readBuffer(f, addr(buf[0]), 1) == 0  # check that we've read all
-
-proc equalsFile*(r: Rope, filename: string): bool {.rtl, extern: "nro$1Str".} =
-  ## returns true if the contents of the file `f` equal `r`. If `f` does not
-  ## exist, false is returned.
-  var f: File
-  result = open(f, filename)
-  if result:
-    result = equalsFile(r, f)
-    close(f)
+when not defined(js) and not defined(nimscript):
+  const
+    bufSize = 1024 # 1 KB is reasonable
+
+  proc equalsFile*(r: Rope, f: File): bool {.rtl, extern: "nro$1File".} =
+    ## Returns true if the contents of the file `f` equal `r`.
+    var
+      buf: array[bufSize, char]
+      bpos = buf.len
+      blen = buf.len
+
+    for s in leaves(r):
+      var spos = 0
+      let slen = s.len
+      while spos < slen:
+        if bpos == blen:
+          # Read more data
+          bpos = 0
+          blen = readBuffer(f, addr(buf[0]), buf.len)
+          if blen == 0: # no more data in file
+            return false
+        let n = min(blen - bpos, slen - spos)
+        # TODO: There's gotta be a better way of comparing here...
+        if not equalMem(addr(buf[bpos]),
+                        cast[pointer](cast[int](cstring(s)) + spos), n):
+          return false
+        spos += n
+        bpos += n
+
+    result = readBuffer(f, addr(buf[0]), 1) == 0 # check that we've read all
+
+  proc equalsFile*(r: Rope, filename: string): bool {.rtl, extern: "nro$1Str".} =
+    ## Returns true if the contents of the file `f` equal `r`. If `f` does not
+    ## exist, false is returned.
+    var f: File
+    result = open(f, filename)
+    if result:
+      result = equalsFile(r, f)
+      close(f)
 
 new(N) # init dummy node for splay algorithm
 
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/securehash.nim b/lib/pure/securehash.nim
deleted file mode 100644
index c6cde599a..000000000
--- a/lib/pure/securehash.nim
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-## This module is a deprecated alias for the ``sha1`` module.
-{.deprecated.}
-
-include "../std/sha1"
diff --git a/lib/pure/segfaults.nim b/lib/pure/segfaults.nim
index 373508430..65b059e86 100644
--- a/lib/pure/segfaults.nim
+++ b/lib/pure/segfaults.nim
@@ -8,25 +8,29 @@
 #
 
 ## This modules registers a signal handler that turns access violations /
-## segfaults into a ``NilAccessError`` exception. To be able to catch
-## a NilAccessError all you have to do is to import this module.
+## segfaults into a ``NilAccessDefect`` exception. To be able to catch
+## a NilAccessDefect all you have to do is to import this module.
 ##
 ## Tested on these OSes: Linux, Windows, OSX
 
+# xxx possibly broken on arm64, see bug #17178
+
+{.used.}
+
 # do allocate memory upfront:
-var se: ref NilAccessError
+var se: ref NilAccessDefect
 new(se)
-se.name = "NilAccessError"
-se.msg = ""
+se.name = "NilAccessDefect"
+se.msg = "Could not access value because it is nil."
 
 when defined(windows):
   include "../system/ansi_c"
 
-  import winlean
+  import std/winlean
 
   const
-    EXCEPTION_ACCESS_VIOLATION = DWORD(0xc0000005)
-    EXCEPTION_CONTINUE_SEARCH = Long(0)
+    EXCEPTION_ACCESS_VIOLATION = DWORD(0xc0000005'i32)
+    EXCEPTION_CONTINUE_SEARCH = LONG(0)
 
   type
     PEXCEPTION_RECORD = ptr object
@@ -39,7 +43,7 @@ when defined(windows):
     VectoredHandler = proc (p: PEXCEPTION_POINTERS): LONG {.stdcall.}
   proc addVectoredExceptionHandler(firstHandler: ULONG,
                                    handler: VectoredHandler): pointer {.
-    importc: "AddVectoredExceptionHandler", stdcall, dynlib: "kernel32.dll"}
+    importc: "AddVectoredExceptionHandler", stdcall, dynlib: "kernel32.dll".}
 
   {.push stackTrace: off.}
   proc segfaultHandler(p: PEXCEPTION_POINTERS): LONG {.stdcall.} =
@@ -61,7 +65,7 @@ when defined(windows):
     c_signal(SIGSEGV, segfaultHandler)
 
 else:
-  import posix
+  import std/posix
 
   var sa: Sigaction
 
diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim
index 640df8282..ac180e2bd 100644
--- a/lib/pure/selectors.nim
+++ b/lib/pure/selectors.nim
@@ -9,11 +9,11 @@
 
 ## This module allows high-level and efficient I/O multiplexing.
 ##
-## Supported OS primitives: ``epoll``, ``kqueue``, ``poll`` and
-## Windows ``select``.
+## Supported OS primitives: `epoll`, `kqueue`, `poll` and
+## Windows `select`.
 ##
 ## To use threadsafe version of this module, it needs to be compiled
-## with both ``-d:threadsafe`` and ``--threads:on`` options.
+## with both `-d:threadsafe` and `--threads:on` options.
 ##
 ## Supported features: files, sockets, pipes, timers, processes, signals
 ## and user events.
@@ -25,18 +25,22 @@
 ## Solaris (files, sockets, handles and user events).
 ## Android (files, sockets, handles and user events).
 ##
-## TODO: ``/dev/poll``, ``event ports`` and filesystem events.
+## TODO: `/dev/poll`, `event ports` and filesystem events.
 
-import os, strutils, nativesockets
+import std/nativesockets
+import std/oserrors
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 const hasThreadSupport = compileOption("threads") and defined(threadsafe)
 
 const ioselSupportedPlatform* = defined(macosx) or defined(freebsd) or
                                 defined(netbsd) or defined(openbsd) or
-                                defined(dragonfly) or
-                                (defined(linux) and not defined(android))
+                                defined(dragonfly) or defined(nuttx) or
+                                (defined(linux) and not defined(android) and not defined(emscripten))
   ## This constant is used to determine whether the destination platform is
-  ## fully supported by ``ioselectors`` module.
+  ## fully supported by `ioselectors` module.
 
 const bsdPlatform = defined(macosx) or defined(freebsd) or
                     defined(netbsd) or defined(openbsd) or
@@ -47,6 +51,9 @@ when defined(nimdoc):
     Selector*[T] = ref object
       ## An object which holds descriptors to be checked for read/write status
 
+    IOSelectorsException* = object of CatchableError
+      ## Exception that is raised if an IOSelectors error occurs.
+
     Event* {.pure.} = enum
       ## An enum which hold event types
       Read,        ## Descriptor is available for read
@@ -83,62 +90,62 @@ when defined(nimdoc):
 
   proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
                           events: set[Event], data: T) =
-    ## Registers file/socket descriptor ``fd`` to selector ``s``
-    ## with events set in ``events``. The ``data`` is application-defined
+    ## Registers file/socket descriptor `fd` to selector `s`
+    ## with events set in `events`. The `data` is application-defined
     ## data, which will be passed when an event is triggered.
 
   proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
                         events: set[Event]) =
-    ## Update file/socket descriptor ``fd``, registered in selector
-    ## ``s`` with new events set ``event``.
+    ## Update file/socket descriptor `fd`, registered in selector
+    ## `s` with new events set `event`.
 
   proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
                          data: T): int {.discardable.} =
-    ## Registers timer notification with ``timeout`` (in milliseconds)
-    ## to selector ``s``.
+    ## Registers timer notification with `timeout` (in milliseconds)
+    ## to selector `s`.
     ##
-    ## If ``oneshot`` is ``true``, timer will be notified only once.
+    ## If `oneshot` is `true`, timer will be notified only once.
     ##
-    ## Set ``oneshot`` to ``false`` if you want periodic notifications.
+    ## Set `oneshot` to `false` if you want periodic notifications.
     ##
-    ## The ``data`` is application-defined data, which will be passed, when
+    ## The `data` is application-defined data, which will be passed, when
     ## the timer is triggered.
     ##
     ## Returns the file descriptor for the registered timer.
 
   proc registerSignal*[T](s: Selector[T], signal: int,
                           data: T): int {.discardable.} =
-    ## Registers Unix signal notification with ``signal`` to selector
-    ## ``s``.
+    ## Registers Unix signal notification with `signal` to selector
+    ## `s`.
     ##
-    ## The ``data`` is application-defined data, which will be
+    ## The `data` is application-defined data, which will be
     ## passed when signal raises.
     ##
     ## Returns the file descriptor for the registered signal.
     ##
-    ## **Note:** This function is not supported on ``Windows``.
+    ## **Note:** This function is not supported on `Windows`.
 
   proc registerProcess*[T](s: Selector[T], pid: int,
                            data: T): int {.discardable.} =
     ## Registers a process id (pid) notification (when process has
-    ## exited) in selector ``s``.
+    ## exited) in selector `s`.
     ##
-    ## The ``data`` is application-defined data, which will be passed when
-    ## process with ``pid`` has exited.
+    ## The `data` is application-defined data, which will be passed when
+    ## process with `pid` has exited.
     ##
     ## Returns the file descriptor for the registered signal.
 
   proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
-    ## Registers selector event ``ev`` in selector ``s``.
+    ## Registers selector event `ev` in selector `s`.
     ##
-    ## The ``data`` is application-defined data, which will be passed when
-    ## ``ev`` happens.
+    ## The `data` is application-defined data, which will be passed when
+    ## `ev` happens.
 
   proc registerVnode*[T](s: Selector[T], fd: cint, events: set[Event],
                          data: T) =
     ## Registers selector BSD/MacOSX specific vnode events for file
-    ## descriptor ``fd`` and events ``events``.
-    ## ``data`` application-defined data, which to be passed, when
+    ## descriptor `fd` and events `events`.
+    ## `data` application-defined data, which to be passed, when
     ## vnode event happens.
     ##
     ## **Note:** This function is supported only by BSD and MacOSX.
@@ -147,79 +154,77 @@ when defined(nimdoc):
     ## Creates a new user-defined event.
 
   proc trigger*(ev: SelectEvent) =
-    ## Trigger event ``ev``.
+    ## Trigger event `ev`.
 
   proc close*(ev: SelectEvent) =
-    ## Closes user-defined event ``ev``.
+    ## Closes user-defined event `ev`.
 
   proc unregister*[T](s: Selector[T], ev: SelectEvent) =
-    ## Unregisters user-defined event ``ev`` from selector ``s``.
+    ## Unregisters user-defined event `ev` from selector `s`.
 
   proc unregister*[T](s: Selector[T], fd: int|SocketHandle|cint) =
-    ## Unregisters file/socket descriptor ``fd`` from selector ``s``.
+    ## Unregisters file/socket descriptor `fd` from selector `s`.
 
   proc selectInto*[T](s: Selector[T], timeout: int,
-                      results: var openarray[ReadyKey]): int =
-    ## Waits for events registered in selector ``s``.
+                      results: var openArray[ReadyKey]): int =
+    ## Waits for events registered in selector `s`.
     ##
-    ## The ``timeout`` argument specifies the maximum number of milliseconds
+    ## The `timeout` argument specifies the maximum number of milliseconds
     ## the function will be blocked for if no events are ready. Specifying a
-    ## timeout of ``-1`` causes the function to block indefinitely.
-    ## All available events will be stored in ``results`` array.
+    ## timeout of `-1` causes the function to block indefinitely.
+    ## All available events will be stored in `results` array.
     ##
     ## Returns number of triggered events.
 
   proc select*[T](s: Selector[T], timeout: int): seq[ReadyKey] =
-    ## Waits for events registered in selector ``s``.
+    ## Waits for events registered in selector `s`.
     ##
-    ## The ``timeout`` argument specifies the maximum number of milliseconds
+    ## The `timeout` argument specifies the maximum number of milliseconds
     ## the function will be blocked for if no events are ready. Specifying a
-    ## timeout of ``-1`` causes the function to block indefinitely.
+    ## timeout of `-1` causes the function to block indefinitely.
     ##
     ## Returns a list of triggered events.
 
   proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
-    ## Retrieves application-defined ``data`` associated with descriptor ``fd``.
-    ## If specified descriptor ``fd`` is not registered, empty/default value
+    ## Retrieves application-defined `data` associated with descriptor `fd`.
+    ## If specified descriptor `fd` is not registered, empty/default value
     ## will be returned.
 
   proc setData*[T](s: Selector[T], fd: SocketHandle|int, data: var T): bool =
-    ## Associate application-defined ``data`` with descriptor ``fd``.
+    ## Associate application-defined `data` with descriptor `fd`.
     ##
-    ## Returns ``true``, if data was succesfully updated, ``false`` otherwise.
+    ## Returns `true`, if data was successfully updated, `false` otherwise.
 
   template isEmpty*[T](s: Selector[T]): bool = # TODO: Why is this a template?
-    ## Returns ``true``, if there are no registered events or descriptors
+    ## Returns `true`, if there are no registered events or descriptors
     ## in selector.
 
   template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
                         body: untyped) =
-    ## Retrieves the application-data assigned with descriptor ``fd``
-    ## to ``value``. This ``value`` can be modified in the scope of
-    ## the ``withData`` call.
-    ##
-    ## .. code-block:: nim
+    ## Retrieves the application-data assigned with descriptor `fd`
+    ## to `value`. This `value` can be modified in the scope of
+    ## the `withData` call.
     ##
+    ##   ```nim
     ##   s.withData(fd, value) do:
-    ##     # block is executed only if ``fd`` registered in selector ``s``
+    ##     # block is executed only if `fd` registered in selector `s`
     ##     value.uid = 1000
-    ##
+    ##   ```
 
   template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
                         body1, body2: untyped) =
-    ## Retrieves the application-data assigned with descriptor ``fd``
-    ## to ``value``. This ``value`` can be modified in the scope of
-    ## the ``withData`` call.
-    ##
-    ## .. code-block:: nim
+    ## Retrieves the application-data assigned with descriptor `fd`
+    ## to `value`. This `value` can be modified in the scope of
+    ## the `withData` call.
     ##
+    ##   ```nim
     ##   s.withData(fd, value) do:
-    ##     # block is executed only if ``fd`` registered in selector ``s``.
+    ##     # block is executed only if `fd` registered in selector `s`.
     ##     value.uid = 1000
     ##   do:
-    ##     # block is executed if ``fd`` not registered in selector ``s``.
+    ##     # block is executed if `fd` not registered in selector `s`.
     ##     raise
-    ##
+    ##   ```
 
   proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
     ## Determines whether selector contains a file descriptor.
@@ -227,11 +232,12 @@ when defined(nimdoc):
   proc getFd*[T](s: Selector[T]): int =
     ## Retrieves the underlying selector's file descriptor.
     ##
-    ## For *poll* and *select* selectors ``-1`` is returned.
+    ## For *poll* and *select* selectors `-1` is returned.
 
 else:
+  import std/strutils
   when hasThreadSupport:
-    import locks
+    import std/locks
 
     type
       SharedArray[T] = UncheckedArray[T]
@@ -239,6 +245,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], oldsize, nsize: int): ptr SharedArray[T] =
+      result = cast[ptr SharedArray[T]](reallocShared0(sa, oldsize * sizeof(T), sizeof(T) * nsize))
+
     proc deallocSharedArray[T](sa: ptr SharedArray[T]) =
       deallocShared(cast[pointer](sa))
   type
@@ -248,10 +257,10 @@ else:
       VnodeRename, VnodeRevoke
 
   type
-    IOSelectorsException* = object of Exception
+    IOSelectorsException* = object of CatchableError
 
     ReadyKey* = object
-      fd* : int
+      fd*: int
       events*: set[Event]
       errorCode*: OSErrorCode
 
@@ -261,6 +270,9 @@ else:
       param: int
       data: T
 
+  const
+    InvalidIdent = -1
+
   proc raiseIOSelectorsError[T](message: T) =
     var msg = ""
     when T is string:
@@ -276,14 +288,14 @@ else:
     setBlocking(fd.SocketHandle, false)
 
   when not defined(windows):
-    import posix
+    import std/posix
 
     template setKey(s, pident, pevents, pparam, pdata: untyped) =
       var skey = addr(s.fds[pident])
       skey.ident = pident
       skey.events = pevents
       skey.param = pparam
-      skey.data = data
+      skey.data = pdata
 
   when ioselSupportedPlatform:
     template blockSignals(newmask: var Sigset, oldmask: var Sigset) =
@@ -302,7 +314,46 @@ else:
         if posix.sigprocmask(SIG_UNBLOCK, newmask, oldmask) == -1:
           raiseIOSelectorsError(osLastError())
 
-  when defined(linux):
+  template clearKey[T](key: ptr SelectorKey[T]) =
+    var empty: T
+    key.ident = InvalidIdent
+    key.events = {}
+    key.data = empty
+
+  proc verifySelectParams(timeout: int) =
+    # Timeout of -1 means: wait forever
+    # Anything higher is the time to wait in milliseconds.
+    doAssert(timeout >= -1, "Cannot select with a negative value, got: " & $timeout)
+
+  when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or
+       defined(solaris) or defined(zephyr) or defined(freertos) or defined(nuttx) or defined(haiku):
+    template maxDescriptors*(): int =
+      ## Returns the maximum number of active file descriptors for the current
+      ## process. This involves a system call. For now `maxDescriptors` is
+      ## supported on the following OSes: Windows, Linux, OSX, BSD, Solaris.
+      when defined(windows):
+        16_700_000
+      elif defined(zephyr) or defined(freertos):
+        FD_MAX
+      else:
+        var fdLim: RLimit
+        var res = int(getrlimit(RLIMIT_NOFILE, fdLim))
+        if res >= 0:
+          res = int(fdLim.rlim_cur) - 1
+        res
+
+  when defined(nimIoselector):
+    when nimIoselector == "epoll":
+      include ioselects/ioselectors_epoll
+    elif nimIoselector == "kqueue":
+      include ioselects/ioselectors_kqueue
+    elif nimIoselector == "poll":
+      include ioselects/ioselectors_poll
+    elif nimIoselector == "select":
+      include ioselects/ioselectors_select
+    else:
+      {.fatal: "Unknown nimIoselector specified by define.".}
+  elif defined(linux) and not defined(emscripten):
     include ioselects/ioselectors_epoll
   elif bsdPlatform:
     include ioselects/ioselectors_kqueue
@@ -312,24 +363,13 @@ else:
     include ioselects/ioselectors_poll # need to replace it with event ports
   elif defined(genode):
     include ioselects/ioselectors_select # TODO: use the native VFS layer
+  elif defined(nintendoswitch):
+    include ioselects/ioselectors_select
+  elif defined(freertos) or defined(lwip):
+    include ioselects/ioselectors_select
+  elif defined(zephyr):
+    include ioselects/ioselectors_poll
+  elif defined(nuttx):
+    include ioselects/ioselectors_epoll
   else:
     include ioselects/ioselectors_poll
-
-proc register*[T](s: Selector[T], fd: int | SocketHandle,
-                  events: set[Event], data: T) {.deprecated: "use registerHandle instead".} =
-  ## **Deprecated since v0.18.0:** Use ``registerHandle`` instead.
-  s.registerHandle(fd, events, data)
-
-proc setEvent*(ev: SelectEvent) {.deprecated: "use trigger instead".} =
-  ## Trigger event ``ev``.
-  ##
-  ## **Deprecated since v0.18.0:** Use ``trigger`` instead.
-  ev.trigger()
-
-proc update*[T](s: Selector[T], fd: int | SocketHandle,
-                events: set[Event]) {.deprecated: "use updateHandle instead".} =
-  ## Update file/socket descriptor ``fd``, registered in selector
-  ## ``s`` with new events set ``event``.
-  ##
-  ## **Deprecated since v0.18.0:** Use ``updateHandle`` instead.
-  s.updateHandle()
diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim
deleted file mode 100644
index c2c674b84..000000000
--- a/lib/pure/smtp.nim
+++ /dev/null
@@ -1,245 +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 implements the SMTP client protocol as specified by RFC 5321,
-## this can be used to send mail to any SMTP Server.
-##
-## This module also implements the protocol used to format messages,
-## as specified by RFC 2822.
-##
-## Example gmail use:
-##
-##
-## .. code-block:: Nim
-##   var msg = createMessage("Hello from Nim's SMTP",
-##                           "Hello!.\n Is this awesome or what?",
-##                           @["foo@gmail.com"])
-##   let smtpConn = newSmtp(useSsl = true, debug=true)
-##   smtpConn.connect("smtp.gmail.com", Port 465)
-##   smtpConn.auth("username", "password")
-##   smtpConn.sendmail("username@gmail.com", @["foo@gmail.com"], $msg)
-##
-##
-## For SSL support this module relies on OpenSSL. If you want to
-## enable SSL, compile with ``-d:ssl``.
-
-import net, strutils, strtabs, base64, os
-import asyncnet, asyncdispatch
-
-export Port
-
-type
-  Message* = object
-    msgTo: seq[string]
-    msgCc: seq[string]
-    msgSubject: string
-    msgOtherHeaders: StringTableRef
-    msgBody: string
-
-  ReplyError* = object of IOError
-
-  SmtpBase[SocketType] = ref object
-    sock: SocketType
-    debug: bool
-
-  Smtp* = SmtpBase[Socket]
-  AsyncSmtp* = SmtpBase[AsyncSocket]
-
-proc debugSend(smtp: Smtp | AsyncSmtp, cmd: string) {.multisync.} =
-  if smtp.debug:
-    echo("C:" & cmd)
-  await smtp.sock.send(cmd)
-
-proc debugRecv(smtp: Smtp | AsyncSmtp): Future[TaintedString] {.multisync.} =
-  result = await smtp.sock.recvLine()
-  if smtp.debug:
-    echo("S:" & result.string)
-
-proc quitExcpt(smtp: Smtp, msg: string) =
-  smtp.debugSend("QUIT")
-  raise newException(ReplyError, msg)
-
-const compiledWithSsl = defined(ssl)
-
-when not defined(ssl):
-  type PSSLContext = ref object
-  let defaultSSLContext: PSSLContext = nil
-else:
-  let defaultSSLContext = newContext(verifyMode = CVerifyNone)
-
-proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string],
-                otherHeaders: openarray[tuple[name, value: string]]): Message =
-  ## Creates a new MIME compliant message.
-  result.msgTo = mTo
-  result.msgCc = mCc
-  result.msgSubject = mSubject
-  result.msgBody = mBody
-  result.msgOtherHeaders = newStringTable()
-  for n, v in items(otherHeaders):
-    result.msgOtherHeaders[n] = v
-
-proc createMessage*(mSubject, mBody: string, mTo,
-                    mCc: seq[string] = @[]): Message =
-  ## Alternate version of the above.
-  result.msgTo = mTo
-  result.msgCc = mCc
-  result.msgSubject = mSubject
-  result.msgBody = mBody
-  result.msgOtherHeaders = newStringTable()
-
-proc `$`*(msg: Message): string =
-  ## stringify for ``Message``.
-  result = ""
-  if msg.msgTo.len() > 0:
-    result = "TO: " & msg.msgTo.join(", ") & "\c\L"
-  if msg.msgCc.len() > 0:
-    result.add("CC: " & msg.msgCc.join(", ") & "\c\L")
-  # TODO: Folding? i.e when a line is too long, shorten it...
-  result.add("Subject: " & msg.msgSubject & "\c\L")
-  for key, value in pairs(msg.msgOtherHeaders):
-    result.add(key & ": " & value & "\c\L")
-
-  result.add("\c\L")
-  result.add(msg.msgBody)
-
-proc newSmtp*(useSsl = false, debug=false,
-              sslContext = defaultSslContext): Smtp =
-  ## Creates a new ``Smtp`` instance.
-  new result
-  result.debug = debug
-
-  result.sock = newSocket()
-  if useSsl:
-    when compiledWithSsl:
-      sslContext.wrapSocket(result.sock)
-    else:
-      raise newException(SystemError,
-                         "SMTP module compiled without SSL support")
-
-proc newAsyncSmtp*(useSsl = false, debug=false,
-                   sslContext = defaultSslContext): AsyncSmtp =
-  ## Creates a new ``AsyncSmtp`` instance.
-  new result
-  result.debug = debug
-
-  result.sock = newAsyncSocket()
-  if useSsl:
-    when compiledWithSsl:
-      sslContext.wrapSocket(result.sock)
-    else:
-      raise newException(SystemError,
-                         "SMTP module compiled without SSL support")
-
-proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] =
-  var retFuture = newFuture[void]()
-  var sendFut = smtp.debugSend("QUIT")
-  sendFut.callback =
-    proc () =
-      # TODO: Fix this in async procs.
-      raise newException(ReplyError, msg)
-  return retFuture
-
-proc checkReply(smtp: Smtp | AsyncSmtp, reply: string) {.multisync.} =
-  var line = await smtp.debugRecv()
-  if not line.startswith(reply):
-    await quitExcpt(smtp, "Expected " & reply & " reply, got: " & line)
-
-proc connect*(smtp: Smtp | AsyncSmtp,
-              address: string, port: Port) {.multisync.} =
-  ## Establishes a connection with a SMTP server.
-  ## May fail with ReplyError or with a socket error.
-  await smtp.sock.connect(address, port)
-
-  await smtp.checkReply("220")
-  await smtp.debugSend("HELO " & address & "\c\L")
-  await smtp.checkReply("250")
-
-proc auth*(smtp: Smtp | AsyncSmtp, username, password: string) {.multisync.} =
-  ## Sends an AUTH command to the server to login as the `username`
-  ## using `password`.
-  ## May fail with ReplyError.
-
-  await smtp.debugSend("AUTH LOGIN\c\L")
-  await smtp.checkReply("334") # TODO: Check whether it's asking for the "Username:"
-                               # i.e "334 VXNlcm5hbWU6"
-  await smtp.debugSend(encode(username) & "\c\L")
-  await smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?)
-
-  await smtp.debugSend(encode(password) & "\c\L")
-  await smtp.checkReply("235") # Check whether the authentification was successful.
-
-proc sendMail*(smtp: Smtp | AsyncSmtp, fromAddr: string,
-               toAddrs: seq[string], msg: string) {.multisync.} =
-  ## Sends ``msg`` from ``fromAddr`` to the addresses specified in ``toAddrs``.
-  ## Messages may be formed using ``createMessage`` by converting the
-  ## Message into a string.
-
-  await smtp.debugSend("MAIL FROM:<" & fromAddr & ">\c\L")
-  await smtp.checkReply("250")
-  for address in items(toAddrs):
-    await smtp.debugSend("RCPT TO:<" & address & ">\c\L")
-    await smtp.checkReply("250")
-
-  # Send the message
-  await smtp.debugSend("DATA " & "\c\L")
-  await smtp.checkReply("354")
-  await smtp.sock.send(msg & "\c\L")
-  await smtp.debugSend(".\c\L")
-  await smtp.checkReply("250")
-
-proc close*(smtp: Smtp | AsyncSmtp) {.multisync.} =
-  ## Disconnects from the SMTP server and closes the socket.
-  await smtp.debugSend("QUIT\c\L")
-  smtp.sock.close()
-
-when not defined(testing) and isMainModule:
-  # To test with a real SMTP service, create a smtp.ini file, e.g.:
-  # username = ""
-  # password = ""
-  # smtphost = "smtp.gmail.com"
-  # port = 465
-  # use_tls = true
-  # sender = ""
-  # recipient = ""
-
-  import parsecfg
-
-  proc `[]`(c: Config, key: string): string = c.getSectionValue("", key)
-
-  let
-    conf = loadConfig("smtp.ini")
-    msg = createMessage("Hello from Nim's SMTP!",
-      "Hello!\n Is this awesome or what?", @[conf["recipient"]])
-
-  assert conf["smtphost"] != ""
-
-  proc async_test() {.async.} =
-    let client = newAsyncSmtp(
-      conf["use_tls"].parseBool,
-      debug=true
-    )
-    await client.connect(conf["smtphost"], conf["port"].parseInt.Port)
-    await client.auth(conf["username"], conf["password"])
-    await client.sendMail(conf["sender"], @[conf["recipient"]], $msg)
-    await client.close()
-    echo "async email sent"
-
-  proc sync_test() =
-    var smtpConn = newSmtp(
-      conf["use_tls"].parseBool,
-      debug=true
-    )
-    smtpConn.connect(conf["smtphost"], conf["port"].parseInt.Port)
-    smtpConn.auth(conf["username"], conf["password"])
-    smtpConn.sendMail(conf["sender"], @[conf["recipient"]], $msg)
-    smtpConn.close()
-    echo "sync email sent"
-
-  waitFor async_test()
-  sync_test()
diff --git a/lib/pure/smtp.nim.cfg b/lib/pure/smtp.nim.cfg
deleted file mode 100644
index 521e21de4..000000000
--- a/lib/pure/smtp.nim.cfg
+++ /dev/null
@@ -1 +0,0 @@
--d:ssl
diff --git a/lib/pure/ssl_certs.nim b/lib/pure/ssl_certs.nim
new file mode 100644
index 000000000..d60cd22eb
--- /dev/null
+++ b/lib/pure/ssl_certs.nim
@@ -0,0 +1,172 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2017 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+## Scan for SSL/TLS CA certificates on disk
+## The default locations can be overridden using the SSL_CERT_FILE and
+## SSL_CERT_DIR environment variables.
+
+import std/[os, strutils]
+
+# FWIW look for files before scanning entire dirs.
+
+when defined(macosx):
+  const certificatePaths = [
+    "/etc/ssl/cert.pem",
+    "/System/Library/OpenSSL/certs/cert.pem"
+  ]
+elif defined(linux):
+  const certificatePaths = [
+    # Debian, Ubuntu, Arch: maintained by update-ca-certificates, SUSE, Gentoo
+    # NetBSD (security/mozilla-rootcerts)
+    # SLES10/SLES11, https://golang.org/issue/12139
+    "/etc/ssl/certs/ca-certificates.crt",
+    # OpenSUSE
+    "/etc/ssl/ca-bundle.pem",
+    # Red Hat 5+, Fedora, Centos
+    "/etc/pki/tls/certs/ca-bundle.crt",
+    # Red Hat 4
+    "/usr/share/ssl/certs/ca-bundle.crt",
+    # Fedora/RHEL
+    "/etc/pki/tls/certs",
+    # Android
+    "/data/data/com.termux/files/usr/etc/tls/cert.pem",
+    "/system/etc/security/cacerts",
+  ]
+elif defined(bsd):
+  const certificatePaths = [
+    # Debian, Ubuntu, Arch: maintained by update-ca-certificates, SUSE, Gentoo
+    # NetBSD (security/mozilla-rootcerts)
+    # SLES10/SLES11, https://golang.org/issue/12139
+    "/etc/ssl/certs/ca-certificates.crt",
+    # FreeBSD (security/ca-root-nss package)
+    "/usr/local/share/certs/ca-root-nss.crt",
+    # OpenBSD, FreeBSD (optional symlink)
+    "/etc/ssl/cert.pem",
+    # FreeBSD
+    "/usr/local/share/certs",
+    # NetBSD
+    "/etc/openssl/certs",
+  ]
+else:
+  const certificatePaths = [
+    # Debian, Ubuntu, Arch: maintained by update-ca-certificates, SUSE, Gentoo
+    # NetBSD (security/mozilla-rootcerts)
+    # SLES10/SLES11, https://golang.org/issue/12139
+    "/etc/ssl/certs/ca-certificates.crt",
+    # OpenSUSE
+    "/etc/ssl/ca-bundle.pem",
+    # Red Hat 5+, Fedora, Centos
+    "/etc/pki/tls/certs/ca-bundle.crt",
+    # Red Hat 4
+    "/usr/share/ssl/certs/ca-bundle.crt",
+    # FreeBSD (security/ca-root-nss package)
+    "/usr/local/share/certs/ca-root-nss.crt",
+    # CentOS/RHEL 7
+    "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem",
+    # OpenBSD, FreeBSD (optional symlink)
+    "/etc/ssl/cert.pem",
+    # Fedora/RHEL
+    "/etc/pki/tls/certs",
+    # Android
+    "/system/etc/security/cacerts",
+    # FreeBSD
+    "/usr/local/share/certs",
+    # NetBSD
+    "/etc/openssl/certs",
+  ]
+
+when defined(haiku):
+  const
+    B_FIND_PATH_EXISTING_ONLY = 0x4
+    B_FIND_PATH_DATA_DIRECTORY = 6
+
+  proc find_paths_etc(architecture: cstring, baseDirectory: cint,
+                      subPath: cstring, flags: uint32,
+                      paths: var ptr UncheckedArray[cstring],
+                      pathCount: var csize_t): int32
+                     {.importc, header: "<FindDirectory.h>".}
+  proc free(p: pointer) {.importc, header: "<stdlib.h>".}
+
+iterator scanSSLCertificates*(useEnvVars = false): string =
+  ## Scan for SSL/TLS CA certificates on disk.
+  ##
+  ## if `useEnvVars` is true, the SSL_CERT_FILE and SSL_CERT_DIR
+  ## environment variables can be used to override the certificate
+  ## directories to scan or specify a CA certificate file.
+  if useEnvVars and existsEnv("SSL_CERT_FILE"):
+    yield getEnv("SSL_CERT_FILE")
+
+  elif useEnvVars and existsEnv("SSL_CERT_DIR"):
+    let p = getEnv("SSL_CERT_DIR")
+    for fn in joinPath(p, "*").walkFiles():
+      yield fn
+
+  else:
+    when defined(windows):
+      const cacert = "cacert.pem"
+      let pem = getAppDir() / cacert
+      if fileExists(pem):
+        yield pem
+      else:
+        let path = getEnv("PATH")
+        for candidate in split(path, PathSep):
+          if candidate.len != 0:
+            let x = (if candidate[0] == '"' and candidate[^1] == '"':
+                      substr(candidate, 1, candidate.len-2) else: candidate) / cacert
+            if fileExists(x):
+              yield x
+    elif not defined(haiku):
+      for p in certificatePaths:
+        if p.endsWith(".pem") or p.endsWith(".crt"):
+          if fileExists(p):
+            yield p
+        elif dirExists(p):
+          # check if it's a dir where each cert is one file
+          # named by it's hasg
+          for fn in joinPath(p, "*.0").walkFiles:
+            yield p.normalizePathEnd(true)
+            break
+          for fn in joinPath(p, "*").walkFiles():
+
+            yield fn
+    else:
+      var
+        paths: ptr UncheckedArray[cstring]
+        size: csize_t
+      let err = find_paths_etc(
+        nil, B_FIND_PATH_DATA_DIRECTORY, "ssl/CARootCertificates.pem",
+        B_FIND_PATH_EXISTING_ONLY, paths, size
+      )
+      if err == 0:
+        defer: free(paths)
+        for i in 0 ..< size:
+          yield $paths[i]
+
+# Certificates management on windows
+# when defined(windows) or defined(nimdoc):
+#
+#   import std/openssl
+#
+#   type
+#     PCCertContext {.final, pure.} = pointer
+#     X509 {.final, pure.} = pointer
+#     CertStore {.final, pure.} = pointer
+#
+#   # OpenSSL cert store
+#
+#   {.push stdcall, dynlib: "kernel32", importc.}
+#
+#   proc CertOpenSystemStore*(hprov: pointer=nil, szSubsystemProtocol: cstring): CertStore
+#
+#   proc CertEnumCertificatesInStore*(hCertStore: CertStore, pPrevCertContext: PCCertContext): pointer
+#
+#   proc CertFreeCertificateContext*(pContext: PCCertContext): bool
+#
+#   proc CertCloseStore*(hCertStore:CertStore, flags:cint): bool
+#
+#   {.pop.}
diff --git a/lib/pure/ssl_config.nim b/lib/pure/ssl_config.nim
new file mode 100644
index 000000000..14f66ede4
--- /dev/null
+++ b/lib/pure/ssl_config.nim
@@ -0,0 +1,51 @@
+# This file was automatically generated by tools/ssl_config_parser on 2020-06-03T22:02:05Z. DO NOT EDIT.
+
+## This module contains SSL configuration parameters obtained from
+## `Mozilla OpSec <https://wiki.mozilla.org/Security/Server_Side_TLS>`_.
+##
+## The configuration file used to generate this module: https://ssl-config.mozilla.org/guidelines/5.4.json
+
+const CiphersModern* = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256"
+  ## An OpenSSL-compatible list of secure ciphers for ``modern`` compatibility
+  ## per Mozilla's recommendations.
+  ##
+  ## Oldest clients supported by this list:
+  ## * Firefox 63
+  ## * Android 10.0
+  ## * Chrome 70
+  ## * Edge 75
+  ## * Java 11
+  ## * OpenSSL 1.1.1
+  ## * Opera 57
+  ## * Safari 12.1
+
+const CiphersIntermediate* = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
+  ## An OpenSSL-compatible list of secure ciphers for ``intermediate`` compatibility
+  ## per Mozilla's recommendations.
+  ##
+  ## Oldest clients supported by this list:
+  ## * Firefox 27
+  ## * Android 4.4.2
+  ## * Chrome 31
+  ## * Edge
+  ## * IE 11 on Windows 7
+  ## * Java 8u31
+  ## * OpenSSL 1.0.1
+  ## * Opera 20
+  ## * Safari 9
+
+const CiphersOld* = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA"
+  ## An OpenSSL-compatible list of secure ciphers for ``old`` compatibility
+  ## per Mozilla's recommendations.
+  ##
+  ## Oldest clients supported by this list:
+  ## * Firefox 1
+  ## * Android 2.3
+  ## * Chrome 1
+  ## * Edge 12
+  ## * IE8 on Windows XP
+  ## * Java 6
+  ## * OpenSSL 0.9.8
+  ## * Opera 5
+  ## * Safari 1
+
diff --git a/lib/pure/stats.nim b/lib/pure/stats.nim
index ce32108c2..6a4fd8f01 100644
--- a/lib/pure/stats.nim
+++ b/lib/pure/stats.nim
@@ -9,67 +9,77 @@
 
 ## Statistical analysis framework for performing
 ## basic statistical analysis of data.
-## The data is analysed in a single pass, when a data value
-## is pushed to the ``RunningStat`` or ``RunningRegress`` objects
+## The data is analysed in a single pass, when it
+## is pushed to a `RunningStat` or `RunningRegress` object.
 ##
-## ``RunningStat`` calculates for a single data set
+## `RunningStat` calculates for a single data set
 ## - n (data count)
-## - min  (smallest value)
-## - max  (largest value)
+## - min (smallest value)
+## - max (largest value)
 ## - sum
 ## - mean
 ## - variance
-## - varianceS (sample var)
+## - varianceS (sample variance)
 ## - standardDeviation
-## - standardDeviationS  (sample stddev)
+## - standardDeviationS (sample standard deviation)
 ## - skewness (the third statistical moment)
 ## - kurtosis (the fourth statistical moment)
 ##
-## ``RunningRegress`` calculates for two sets of data
-## - n
+## `RunningRegress` calculates for two sets of data
+## - n (data count)
 ## - slope
 ## - intercept
 ## - correlation
 ##
-## Procs have been provided to calculate statistics on arrays and sequences.
+## Procs are provided to calculate statistics on `openArray`s.
 ##
 ## However, if more than a single statistical calculation is required, it is more
-## efficient to push the data once to the RunningStat object, and
-## call the numerous statistical procs for the RunningStat object.
-##
-## .. code-block:: Nim
-##
-##  var rs: RunningStat
-##  rs.push(MySeqOfData)
-##  rs.mean()
-##  rs.variance()
-##  rs.skewness()
-##  rs.kurtosis()
+## efficient to push the data once to a `RunningStat` object and then
+## call the numerous statistical procs for the `RunningStat` object:
+
+runnableExamples:
+  from std/math import almostEqual
+
+  template `~=`(a, b: float): bool = almostEqual(a, b)
+
+  var statistics: RunningStat  # must be var
+  statistics.push(@[1.0, 2.0, 1.0, 4.0, 1.0, 4.0, 1.0, 2.0])
+  doAssert statistics.n == 8
+  doAssert statistics.mean() ~= 2.0
+  doAssert statistics.variance() ~= 1.5
+  doAssert statistics.varianceS() ~= 1.714285714285715
+  doAssert statistics.skewness() ~= 0.8164965809277261
+  doAssert statistics.skewnessS() ~= 1.018350154434631
+  doAssert statistics.kurtosis() ~= -1.0
+  doAssert statistics.kurtosisS() ~= -0.7000000000000008
+
+from std/math import FloatClass, sqrt, pow, round
 
-from math import FloatClass, sqrt, pow, round
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, formatfloat]
 
-{.push debugger:off .} # the user does not want to trace a part
+{.push debugger: off.} # the user does not want to trace a part
                        # of the standard library!
-{.push checks:off, line_dir:off, stack_trace:off.}
+{.push checks: off, line_dir: off, stack_trace: off.}
 
 type
-  RunningStat* = object             ## an accumulator for statistical data
-    n*: int                         ## number of pushed data
-    min*, max*, sum*: float         ## self-explaining
-    mom1, mom2, mom3, mom4: float   ## statistical moments, mom1 is mean
+  RunningStat* = object           ## An accumulator for statistical data.
+    n*: int                       ## amount of pushed data
+    min*, max*, sum*: float       ## self-explaining
+    mom1, mom2, mom3, mom4: float ## statistical moments, mom1 is mean
 
-
-  RunningRegress* = object  ## an accumulator for regression calculations
-    n*: int                 ## number of pushed data
-    x_stats*: RunningStat   ## stats for first set of data
-    y_stats*: RunningStat   ## stats for second set of data
-    s_xy: float             ## accumulated data for combined xy
+  RunningRegress* = object ## An accumulator for regression calculations.
+    n*: int                ## amount of pushed data
+    x_stats*: RunningStat  ## stats for the first set of data
+    y_stats*: RunningStat  ## stats for the second set of data
+    s_xy: float            ## accumulated data for combined xy
 
 # ----------- RunningStat --------------------------
+
 proc clear*(s: var RunningStat) =
-  ## reset `s`
+  ## Resets `s`.
   s.n = 0
-  s.min = toBiggestFloat(int.high)
+  s.min = 0.0
   s.max = 0.0
   s.sum = 0.0
   s.mom1 = 0.0
@@ -78,12 +88,15 @@ proc clear*(s: var RunningStat) =
   s.mom4 = 0.0
 
 proc push*(s: var RunningStat, x: float) =
-  ## pushes a value `x` for processing
-  if s.n == 0: s.min = x
+  ## Pushes a value `x` for processing.
+  if s.n == 0:
+    s.min = x
+    s.max = x
+  else:
+    if s.min > x: s.min = x
+    if s.max < x: s.max = x
   inc(s.n)
   # See Knuth TAOCP vol 2, 3rd edition, page 232
-  if s.min > x: s.min = x
-  if s.max < x: s.max = x
   s.sum += x
   let n = toFloat(s.n)
   let delta = x - s.mom1
@@ -97,63 +110,63 @@ proc push*(s: var RunningStat, x: float) =
   s.mom1 += delta_n
 
 proc push*(s: var RunningStat, x: int) =
-  ## pushes a value `x` for processing.
+  ## Pushes a value `x` for processing.
   ##
-  ## `x` is simply converted to ``float``
+  ## `x` is simply converted to `float`
   ## and the other push operation is called.
   s.push(toFloat(x))
 
-proc push*(s: var RunningStat, x: openarray[float|int]) =
-  ## pushes all values of `x` for processing.
+proc push*(s: var RunningStat, x: openArray[float|int]) =
+  ## Pushes all values of `x` for processing.
   ##
-  ## Int values of `x` are simply converted to ``float`` and
+  ## Int values of `x` are simply converted to `float` and
   ## the other push operation is called.
   for val in x:
     s.push(val)
 
 proc mean*(s: RunningStat): float =
-  ## computes the current mean of `s`
+  ## Computes the current mean of `s`.
   result = s.mom1
 
 proc variance*(s: RunningStat): float =
-  ## computes the current population variance of `s`
+  ## Computes the current population variance of `s`.
   result = s.mom2 / toFloat(s.n)
 
 proc varianceS*(s: RunningStat): float =
-  ## computes the current sample variance of `s`
+  ## Computes the current sample variance of `s`.
   if s.n > 1: result = s.mom2 / toFloat(s.n - 1)
 
 proc standardDeviation*(s: RunningStat): float =
-  ## computes the current population standard deviation of `s`
+  ## Computes the current population standard deviation of `s`.
   result = sqrt(variance(s))
 
 proc standardDeviationS*(s: RunningStat): float =
-  ## computes the current sample standard deviation of `s`
+  ## Computes the current sample standard deviation of `s`.
   result = sqrt(varianceS(s))
 
 proc skewness*(s: RunningStat): float =
-  ## computes the current population skewness of `s`
+  ## Computes the current population skewness of `s`.
   result = sqrt(toFloat(s.n)) * s.mom3 / pow(s.mom2, 1.5)
 
 proc skewnessS*(s: RunningStat): float =
-  ## computes the current sample skewness of `s`
+  ## Computes the current sample skewness of `s`.
   let s2 = skewness(s)
   result = sqrt(toFloat(s.n*(s.n-1)))*s2 / toFloat(s.n-2)
 
 proc kurtosis*(s: RunningStat): float =
-  ## computes the current population kurtosis of `s`
+  ## Computes the current population kurtosis of `s`.
   result = toFloat(s.n) * s.mom4 / (s.mom2 * s.mom2) - 3.0
 
 proc kurtosisS*(s: RunningStat): float =
-  ## computes the current sample kurtosis of `s`
+  ## Computes the current sample kurtosis of `s`.
   result = toFloat(s.n-1) / toFloat((s.n-2)*(s.n-3)) *
               (toFloat(s.n+1)*kurtosis(s) + 6)
 
 proc `+`*(a, b: RunningStat): RunningStat =
-  ## combine two RunningStats.
+  ## Combines two `RunningStat`s.
   ##
-  ## Useful if performing parallel analysis of data series
-  ## and need to re-combine parallel result sets
+  ## Useful when performing parallel analysis of data series
+  ## and needing to re-combine parallel result sets.
   result.clear()
   result.n = a.n + b.n
 
@@ -175,14 +188,14 @@ proc `+`*(a, b: RunningStat): RunningStat =
                 (n*n) +
                 4.0*delta*(a.n.float*b.mom3 - b.n.float*a.mom3) / n
   result.max = max(a.max, b.max)
-  result.min = max(a.min, b.min)
+  result.min = min(a.min, b.min)
 
 proc `+=`*(a: var RunningStat, b: RunningStat) {.inline.} =
-  ## add a second RunningStats `b` to `a`
+  ## Adds the `RunningStat` `b` to `a`.
   a = a + b
 
 proc `$`*(a: RunningStat): string =
-  ## produces a string representation of the ``RunningStat``. The exact
+  ## Produces a string representation of the `RunningStat`. The exact
   ## format is currently unspecified and subject to change. Currently
   ## it contains:
   ##
@@ -199,56 +212,57 @@ proc `$`*(a: RunningStat): string =
   result.add ")"
 
 # ---------------------- standalone array/seq stats ---------------------
+
 proc mean*[T](x: openArray[T]): float =
-  ## computes the mean of `x`
+  ## Computes the mean of `x`.
   var rs: RunningStat
   rs.push(x)
   result = rs.mean()
 
 proc variance*[T](x: openArray[T]): float =
-  ## computes the population variance of `x`
+  ## Computes the population variance of `x`.
   var rs: RunningStat
   rs.push(x)
   result = rs.variance()
 
 proc varianceS*[T](x: openArray[T]): float =
-  ## computes the sample variance of `x`
+  ## Computes the sample variance of `x`.
   var rs: RunningStat
   rs.push(x)
   result = rs.varianceS()
 
 proc standardDeviation*[T](x: openArray[T]): float =
-  ## computes the population standardDeviation of `x`
+  ## Computes the population standard deviation of `x`.
   var rs: RunningStat
   rs.push(x)
   result = rs.standardDeviation()
 
 proc standardDeviationS*[T](x: openArray[T]): float =
-  ## computes the sanple standardDeviation of `x`
+  ## Computes the sample standard deviation of `x`.
   var rs: RunningStat
   rs.push(x)
   result = rs.standardDeviationS()
 
 proc skewness*[T](x: openArray[T]): float =
-  ## computes the population skewness of `x`
+  ## Computes the population skewness of `x`.
   var rs: RunningStat
   rs.push(x)
   result = rs.skewness()
 
 proc skewnessS*[T](x: openArray[T]): float =
-  ## computes the sample skewness of `x`
+  ## Computes the sample skewness of `x`.
   var rs: RunningStat
   rs.push(x)
   result = rs.skewnessS()
 
 proc kurtosis*[T](x: openArray[T]): float =
-  ## computes the population kurtosis of `x`
+  ## Computes the population kurtosis of `x`.
   var rs: RunningStat
   rs.push(x)
   result = rs.kurtosis()
 
 proc kurtosisS*[T](x: openArray[T]): float =
-  ## computes the sample kurtosis of `x`
+  ## Computes the sample kurtosis of `x`.
   var rs: RunningStat
   rs.push(x)
   result = rs.kurtosisS()
@@ -256,53 +270,53 @@ proc kurtosisS*[T](x: openArray[T]): float =
 # ---------------------- Running Regression -----------------------------
 
 proc clear*(r: var RunningRegress) =
-  ## reset `r`
+  ## Resets `r`.
   r.x_stats.clear()
   r.y_stats.clear()
   r.s_xy = 0.0
   r.n = 0
 
 proc push*(r: var RunningRegress, x, y: float) =
-  ## pushes two values `x` and `y` for processing
-  r.s_xy += (r.x_stats.mean() - x)*(r.y_stats.mean() - y)*
+  ## Pushes two values `x` and `y` for processing.
+  r.s_xy += (r.x_stats.mean() - x)*(r.y_stats.mean() - y) *
                 toFloat(r.n) / toFloat(r.n + 1)
   r.x_stats.push(x)
   r.y_stats.push(y)
   inc(r.n)
 
 proc push*(r: var RunningRegress, x, y: int) {.inline.} =
-  ## pushes two values `x` and `y` for processing.
+  ## Pushes two values `x` and `y` for processing.
   ##
-  ## `x` and `y` are converted to ``float``
+  ## `x` and `y` are converted to `float`
   ## and the other push operation is called.
   r.push(toFloat(x), toFloat(y))
 
-proc push*(r: var RunningRegress, x, y: openarray[float|int]) =
-  ## pushes two sets of values `x` and `y` for processing.
+proc push*(r: var RunningRegress, x, y: openArray[float|int]) =
+  ## Pushes two sets of values `x` and `y` for processing.
   assert(x.len == y.len)
   for i in 0..<x.len:
     r.push(x[i], y[i])
 
 proc slope*(r: RunningRegress): float =
-  ## computes the current slope of `r`
+  ## Computes the current slope of `r`.
   let s_xx = r.x_stats.varianceS()*toFloat(r.n - 1)
   result = r.s_xy / s_xx
 
 proc intercept*(r: RunningRegress): float =
-  ## computes the current intercept of `r`
+  ## Computes the current intercept of `r`.
   result = r.y_stats.mean() - r.slope()*r.x_stats.mean()
 
 proc correlation*(r: RunningRegress): float =
-  ## computes the current correlation of the two data
-  ## sets pushed into `r`
+  ## Computes the current correlation of the two data
+  ## sets pushed into `r`.
   let t = r.x_stats.standardDeviation() * r.y_stats.standardDeviation()
-  result = r.s_xy / ( toFloat(r.n) * t )
+  result = r.s_xy / (toFloat(r.n) * t)
 
 proc `+`*(a, b: RunningRegress): RunningRegress =
-  ## combine two `RunningRegress` objects.
+  ## Combines two `RunningRegress` objects.
   ##
-  ## Useful if performing parallel analysis of data series
-  ## and need to re-combine parallel result sets
+  ## Useful when performing parallel analysis of data series
+  ## and needing to re-combine parallel result sets
   result.clear()
   result.x_stats = a.x_stats + b.x_stats
   result.y_stats = a.y_stats + b.y_stats
@@ -314,54 +328,8 @@ proc `+`*(a, b: RunningRegress): RunningRegress =
       toFloat(a.n*b.n)*delta_x*delta_y/toFloat(result.n)
 
 proc `+=`*(a: var RunningRegress, b: RunningRegress) =
-  ## add RunningRegress `b` to `a`
+  ## Adds the `RunningRegress` `b` to `a`.
   a = a + b
 
 {.pop.}
 {.pop.}
-
-when isMainModule:
-  proc clean(x: float): float =
-    result = round(1.0e8*x).float * 1.0e-8
-
-  var rs: RunningStat
-  rs.push(@[1.0, 2.0, 1.0, 4.0, 1.0, 4.0, 1.0, 2.0])
-  doAssert(rs.n == 8)
-  doAssert(clean(rs.mean) == 2.0)
-  doAssert(clean(rs.variance()) == 1.5)
-  doAssert(clean(rs.varianceS()) == 1.71428571)
-  doAssert(clean(rs.skewness()) == 0.81649658)
-  doAssert(clean(rs.skewnessS()) == 1.01835015)
-  doAssert(clean(rs.kurtosis()) == -1.0)
-  doAssert(clean(rs.kurtosisS()) == -0.7000000000000001)
-
-  var rs1, rs2: RunningStat
-  rs1.push(@[1.0, 2.0, 1.0, 4.0])
-  rs2.push(@[1.0, 4.0, 1.0, 2.0])
-  let rs3 = rs1 + rs2
-  doAssert(clean(rs3.mom2) == clean(rs.mom2))
-  doAssert(clean(rs3.mom3) == clean(rs.mom3))
-  doAssert(clean(rs3.mom4) == clean(rs.mom4))
-  rs1 += rs2
-  doAssert(clean(rs1.mom2) == clean(rs.mom2))
-  doAssert(clean(rs1.mom3) == clean(rs.mom3))
-  doAssert(clean(rs1.mom4) == clean(rs.mom4))
-  rs1.clear()
-  rs1.push(@[1.0, 2.2, 1.4, 4.9])
-  doAssert(rs1.sum == 9.5)
-  doAssert(rs1.mean() == 2.375)
-
-  when not defined(cpu32):
-    # XXX For some reason on 32bit CPUs these results differ
-    var rr: RunningRegress
-    rr.push(@[0.0,1.0,2.8,3.0,4.0], @[0.0,1.0,2.3,3.0,4.0])
-    doAssert(rr.slope() == 0.9695585996955861)
-    doAssert(rr.intercept() == -0.03424657534246611)
-    doAssert(rr.correlation() == 0.9905100362239381)
-    var rr1, rr2: RunningRegress
-    rr1.push(@[0.0,1.0], @[0.0,1.0])
-    rr2.push(@[2.8,3.0,4.0], @[2.3,3.0,4.0])
-    let rr3 = rr1 + rr2
-    doAssert(rr3.correlation() == rr.correlation())
-    doAssert(clean(rr3.slope()) == clean(rr.slope()))
-    doAssert(clean(rr3.intercept()) == clean(rr.intercept()))
diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim
index a0bba05a4..56f49d7b1 100644
--- a/lib/pure/streams.nim
+++ b/lib/pure/streams.nim
@@ -8,296 +8,1088 @@
 #
 
 ## This module provides a stream interface and two implementations thereof:
-## the `FileStream` and the `StringStream` which implement the stream
-## interface for Nim file objects (`File`) and strings. Other modules
-## may provide other implementations for this standard stream interface.
+## the `FileStream <#FileStream>`_ and the `StringStream <#StringStream>`_
+## which implement the stream interface for Nim file objects (`File`) and
+## strings.
 ##
-## Examples:
+## Other modules may provide other implementations for this standard
+## stream interface.
 ##
-## .. code-block:: Nim
+## .. warning:: Due to the use of `pointer`, the `readData`, `peekData` and
+## `writeData` interfaces are not available on the compile-time VM, and must
+## be cast from a `ptr string` on the JS backend. However, `readDataStr` is
+## available generally in place of `readData`.
 ##
-##  import streams
-##  var
-##    ss = newStringStream("""The first line
-##  the second line
-##  the third line""")
-##    line = ""
-##  while ss.readLine(line):
-##    echo line
-##  ss.close()
+## Basic usage
+## ===========
 ##
-##  var fs = newFileStream("somefile.txt", fmRead)
-##  if not isNil(fs):
-##    while fs.readLine(line):
-##      echo line
-##    fs.close()
+## The basic flow of using this module is:
+##
+## 1. Open input stream
+## 2. Read or write stream
+## 3. Close stream
+##
+## StringStream example
+## --------------------
+##
+##   ```Nim
+##   import std/streams
+##
+##   var strm = newStringStream("""The first line
+##   the second line
+##   the third line""")
+##
+##   var line = ""
+##
+##   while strm.readLine(line):
+##     echo line
+##
+##   # Output:
+##   # The first line
+##   # the second line
+##   # the third line
+##
+##   strm.close()
+##   ```
+##
+## FileStream example
+## ------------------
+##
+## Write file stream example:
+##
+##   ```Nim
+##   import std/streams
+##
+##   var strm = newFileStream("somefile.txt", fmWrite)
+##   var line = ""
+##
+##   if not isNil(strm):
+##     strm.writeLine("The first line")
+##     strm.writeLine("the second line")
+##     strm.writeLine("the third line")
+##     strm.close()
+##
+##   # Output (somefile.txt):
+##   # The first line
+##   # the second line
+##   # the third line
+##   ```
+##
+## Read file stream example:
+##
+##   ```Nim
+##   import std/streams
+##
+##   var strm = newFileStream("somefile.txt", fmRead)
+##   var line = ""
+##
+##   if not isNil(strm):
+##     while strm.readLine(line):
+##       echo line
+##     strm.close()
+##
+##   # Output:
+##   # The first line
+##   # the second line
+##   # the third line
+##   ```
+##
+## See also
+## ========
+## * `asyncstreams module <asyncstreams.html>`_
+## * `io module <syncio.html>`_ for `FileMode enum <syncio.html#FileMode>`_
 
-include "system/inclrtl"
+import std/private/since
 
-proc newEIO(msg: string): ref IOError =
+when defined(nimPreviewSlimSystem):
+  import std/syncio
+  export FileMode
+
+proc newEIO(msg: string): owned(ref IOError) =
   new(result)
   result.msg = msg
 
 type
   Stream* = ref StreamObj
-  StreamObj* = object of RootObj ## Stream interface that supports
-                                 ## writing or reading. Note that these fields
-                                 ## here shouldn't be used directly. They are
-                                 ## accessible so that a stream implementation
-                                 ## can override them.
-    closeImpl*: proc (s: Stream) {.nimcall, tags: [], gcsafe.}
-    atEndImpl*: proc (s: Stream): bool {.nimcall, tags: [], gcsafe.}
-    setPositionImpl*: proc (s: Stream, pos: int) {.nimcall, tags: [], gcsafe.}
-    getPositionImpl*: proc (s: Stream): int {.nimcall, tags: [], gcsafe.}
-    readDataImpl*: proc (s: Stream, buffer: pointer,
-                         bufLen: int): int {.nimcall, tags: [ReadIOEffect], gcsafe.}
-    peekDataImpl*: proc (s: Stream, buffer: pointer,
-                         bufLen: int): int {.nimcall, tags: [ReadIOEffect], gcsafe.}
-    writeDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int) {.nimcall,
-      tags: [WriteIOEffect], gcsafe.}
-    flushImpl*: proc (s: Stream) {.nimcall, tags: [WriteIOEffect], gcsafe.}
+    ## All procedures of this module use this type.
+    ## Procedures don't directly use `StreamObj <#StreamObj>`_.
+  StreamObj* = object of RootObj
+    ## Stream interface that supports writing or reading.
+    ##
+    ## **Note:**
+    ## * That these fields here shouldn't be used directly.
+    ##   They are accessible so that a stream implementation can override them.
+    closeImpl*: proc (s: Stream)
+      {.nimcall, raises: [IOError, OSError], tags: [WriteIOEffect], gcsafe.}
+    atEndImpl*: proc (s: Stream): bool
+      {.nimcall, raises: [Defect, IOError, OSError], tags: [], gcsafe.}
+    setPositionImpl*: proc (s: Stream, pos: int)
+      {.nimcall, raises: [Defect, IOError, OSError], tags: [], gcsafe.}
+    getPositionImpl*: proc (s: Stream): int
+      {.nimcall, raises: [Defect, IOError, OSError], tags: [], gcsafe.}
+
+    readDataStrImpl*: proc (s: Stream, buffer: var string, slice: Slice[int]): int
+      {.nimcall, raises: [Defect, IOError, OSError], tags: [ReadIOEffect], gcsafe.}
+
+    readLineImpl*: proc(s: Stream, line: var string): bool
+      {.nimcall, raises: [Defect, IOError, OSError], tags: [ReadIOEffect], gcsafe.}
+
+    readDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int): int
+      {.nimcall, raises: [Defect, IOError, OSError], tags: [ReadIOEffect], gcsafe.}
+    peekDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int): int
+      {.nimcall, raises: [Defect, IOError, OSError], tags: [ReadIOEffect], gcsafe.}
+    writeDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int)
+      {.nimcall, raises: [Defect, IOError, OSError], tags: [WriteIOEffect], gcsafe.}
+
+    flushImpl*: proc (s: Stream)
+      {.nimcall, raises: [Defect, IOError, OSError], tags: [WriteIOEffect], gcsafe.}
 
 proc flush*(s: Stream) =
-  ## flushes the buffers that the stream `s` might use.
+  ## Flushes the buffers that the stream `s` might use.
+  ##
+  ## This procedure causes any unwritten data for that stream to be delivered
+  ## to the host environment to be written to the file.
+  ##
+  ## See also:
+  ## * `close proc <#close,Stream>`_
+  runnableExamples:
+    from std/os import removeFile
+
+    var strm = newFileStream("somefile.txt", fmWrite)
+
+    doAssert "Before write:" & readFile("somefile.txt") == "Before write:"
+    strm.write("hello")
+    doAssert "After  write:" & readFile("somefile.txt") == "After  write:"
+
+    strm.flush()
+    doAssert "After  flush:" & readFile("somefile.txt") == "After  flush:hello"
+    strm.write("HELLO")
+    strm.flush()
+    doAssert "After  flush:" & readFile("somefile.txt") == "After  flush:helloHELLO"
+
+    strm.close()
+    doAssert "After  close:" & readFile("somefile.txt") == "After  close:helloHELLO"
+    removeFile("somefile.txt")
+
   if not isNil(s.flushImpl): s.flushImpl(s)
 
 proc close*(s: Stream) =
-  ## closes the stream `s`.
-  if not isNil(s.closeImpl): s.closeImpl(s)
-
-proc close*(s, unused: Stream) {.deprecated.} =
-  ## closes the stream `s`.
-  s.closeImpl(s)
+  ## Closes the stream `s`.
+  ##
+  ## See also:
+  ## * `flush proc <#flush,Stream>`_
+  runnableExamples:
+    block:
+      let strm = newStringStream("The first line\nthe second line\nthe third line")
+      ## do something...
+      strm.close()
+      
+    block:
+      let strm = newFileStream("amissingfile.txt")
+      # deferring works even if newFileStream fails
+      defer: strm.close()
+      if not isNil(strm):
+        ## do something...
+
+  if not isNil(s) and not isNil(s.closeImpl):
+    s.closeImpl(s)
 
 proc atEnd*(s: Stream): bool =
-  ## checks if more data can be read from `f`. Returns true if all data has
+  ## Checks if more data can be read from `s`. Returns ``true`` if all data has
   ## been read.
+  runnableExamples:
+    var strm = newStringStream("The first line\nthe second line\nthe third line")
+    var line = ""
+    doAssert strm.atEnd() == false
+    while strm.readLine(line):
+      discard
+    doAssert strm.atEnd() == true
+    strm.close()
+
   result = s.atEndImpl(s)
 
 proc setPosition*(s: Stream, pos: int) =
-  ## sets the position `pos` of the stream `s`.
+  ## Sets the position `pos` of the stream `s`.
+  runnableExamples:
+    var strm = newStringStream("The first line\nthe second line\nthe third line")
+    strm.setPosition(4)
+    doAssert strm.readLine() == "first line"
+    strm.setPosition(0)
+    doAssert strm.readLine() == "The first line"
+    strm.close()
+
   s.setPositionImpl(s, pos)
 
 proc getPosition*(s: Stream): int =
-  ## retrieves the current position in the stream `s`.
+  ## Retrieves the current position in the stream `s`.
+  runnableExamples:
+    var strm = newStringStream("The first line\nthe second line\nthe third line")
+    doAssert strm.getPosition() == 0
+    discard strm.readLine()
+    doAssert strm.getPosition() == 15
+    strm.close()
+
   result = s.getPositionImpl(s)
 
 proc readData*(s: Stream, buffer: pointer, bufLen: int): int =
-  ## low level proc that reads data into an untyped `buffer` of `bufLen` size.
+  ## Low level proc that reads data into an untyped `buffer` of `bufLen` size.
+  ##
+  ## **JS note:** `buffer` is treated as a ``ptr string`` and written to between
+  ## ``0..<bufLen``.
+  runnableExamples:
+    var strm = newStringStream("abcde")
+    var buffer: array[6, char]
+    doAssert strm.readData(addr(buffer), 1024) == 5
+    doAssert buffer == ['a', 'b', 'c', 'd', 'e', '\x00']
+    doAssert strm.atEnd() == true
+    strm.close()
+
   result = s.readDataImpl(s, buffer, bufLen)
 
-when not defined(js):
+proc readDataStr*(s: Stream, buffer: var string, slice: Slice[int]): int =
+  ## Low level proc that reads data into a string ``buffer`` at ``slice``.
+  runnableExamples:
+    var strm = newStringStream("abcde")
+    var buffer = "12345"
+    doAssert strm.readDataStr(buffer, 0..3) == 4
+    doAssert buffer == "abcd5"
+    strm.close()
+
+  if s.readDataStrImpl != nil:
+    result = s.readDataStrImpl(s, buffer, slice)
+  else:
+    # fallback
+    when declared(prepareMutation):
+      # buffer might potentially be a CoW literal with ARC
+      prepareMutation(buffer)
+    result = s.readData(addr buffer[slice.a], slice.b + 1 - slice.a)
+
+template jsOrVmBlock(caseJsOrVm, caseElse: untyped): untyped =
+  when nimvm:
+    block:
+      caseJsOrVm
+  else:
+    block:
+      when defined(js) or defined(nimscript):
+        # nimscript has to be here to avoid semantic checking of caseElse
+        caseJsOrVm
+      else:
+        caseElse
+
+when (NimMajor, NimMinor) >= (1, 3) or not defined(js):
   proc readAll*(s: Stream): string =
     ## Reads all available data.
+    runnableExamples:
+      var strm = newStringStream("The first line\nthe second line\nthe third line")
+      doAssert strm.readAll() == "The first line\nthe second line\nthe third line"
+      doAssert strm.atEnd() == true
+      strm.close()
+
     const bufferSize = 1024
-    var buffer {.noinit.}: array[bufferSize, char]
-    while true:
-      let readBytes = readData(s, addr(buffer[0]), bufferSize)
-      if readBytes == 0:
-        break
-      let prevLen = result.len
-      result.setLen(prevLen + readBytes)
-      copyMem(addr(result[prevLen]), addr(buffer[0]), readBytes)
-      if readBytes < bufferSize:
-        break
+    jsOrVmBlock:
+      var buffer2: string
+      buffer2.setLen(bufferSize)
+      while true:
+        let readBytes = readDataStr(s, buffer2, 0..<bufferSize)
+        if readBytes == 0:
+          break
+        let prevLen = result.len
+        result.setLen(prevLen + readBytes)
+        result[prevLen..<prevLen+readBytes] = buffer2[0..<readBytes]
+        if readBytes < bufferSize:
+          break
+    do: # not JS or VM
+      var buffer {.noinit.}: array[bufferSize, char]
+      while true:
+        let readBytes = readData(s, addr(buffer[0]), bufferSize)
+        if readBytes == 0:
+          break
+        let prevLen = result.len
+        result.setLen(prevLen + readBytes)
+        copyMem(addr(result[prevLen]), addr(buffer[0]), readBytes)
+        if readBytes < bufferSize:
+          break
 
 proc peekData*(s: Stream, buffer: pointer, bufLen: int): int =
-  ## low level proc that reads data into an untyped `buffer` of `bufLen` size
-  ## without moving stream position
+  ## Low level proc that reads data into an untyped `buffer` of `bufLen` size
+  ## without moving stream position.
+  ##
+  ## **JS note:** `buffer` is treated as a ``ptr string`` and written to between
+  ## ``0..<bufLen``.
+  runnableExamples:
+    var strm = newStringStream("abcde")
+    var buffer: array[6, char]
+    doAssert strm.peekData(addr(buffer), 1024) == 5
+    doAssert buffer == ['a', 'b', 'c', 'd', 'e', '\x00']
+    doAssert strm.atEnd() == false
+    strm.close()
+
   result = s.peekDataImpl(s, buffer, bufLen)
 
 proc writeData*(s: Stream, buffer: pointer, bufLen: int) =
-  ## low level proc that writes an untyped `buffer` of `bufLen` size
+  ## Low level proc that writes an untyped `buffer` of `bufLen` size
   ## to the stream `s`.
-  s.writeDataImpl(s, buffer, bufLen)
+  ##
+  ## **JS note:** `buffer` is treated as a ``ptr string`` and read between
+  ## ``0..<bufLen``.
+  runnableExamples:
+    ## writeData
+    var strm = newStringStream("")
+    var buffer = ['a', 'b', 'c', 'd', 'e']
+    strm.writeData(addr(buffer), sizeof(buffer))
+    doAssert strm.atEnd() == true
+    ## readData
+    strm.setPosition(0)
+    var buffer2: array[6, char]
+    doAssert strm.readData(addr(buffer2), sizeof(buffer2)) == 5
+    doAssert buffer2 == ['a', 'b', 'c', 'd', 'e', '\x00']
+    strm.close()
 
-proc writeData*(s, unused: Stream, buffer: pointer,
-                bufLen: int) {.deprecated.} =
-  ## low level proc that writes an untyped `buffer` of `bufLen` size
-  ## to the stream `s`.
   s.writeDataImpl(s, buffer, bufLen)
 
 proc write*[T](s: Stream, x: T) =
-  ## generic write procedure. Writes `x` to the stream `s`. Implementation:
+  ## Generic write procedure. Writes `x` to the stream `s`. Implementation:
   ##
-  ## .. code-block:: Nim
+  ## **Note:** Not available for JS backend. Use `write(Stream, string)
+  ## <#write,Stream,string>`_ for now.
   ##
-  ##     s.writeData(s, addr(x), sizeof(x))
-  var y: T
-  shallowCopy(y, x)
-  writeData(s, addr(y), sizeof(y))
+  ##   ```Nim
+  ##   s.writeData(s, unsafeAddr(x), sizeof(x))
+  ##   ```
+  runnableExamples:
+    var strm = newStringStream("")
+    strm.write("abcde")
+    strm.setPosition(0)
+    doAssert strm.readAll() == "abcde"
+    strm.close()
+
+  writeData(s, unsafeAddr(x), sizeof(x))
 
 proc write*(s: Stream, x: string) =
-  ## writes the string `x` to the the stream `s`. No length field or
+  ## Writes the string `x` to the stream `s`. No length field or
   ## terminating zero is written.
+  runnableExamples:
+    var strm = newStringStream("")
+    strm.write("THE FIRST LINE")
+    strm.setPosition(0)
+    doAssert strm.readLine() == "THE FIRST LINE"
+    strm.close()
+
   when nimvm:
     writeData(s, cstring(x), x.len)
   else:
-    if x.len > 0: writeData(s, cstring(x), x.len)
+    if x.len > 0:
+      when defined(js):
+        var x = x
+        writeData(s, addr(x), x.len)
+      else:
+        writeData(s, cstring(x), x.len)
+
+proc write*(s: Stream, args: varargs[string, `$`]) =
+  ## Writes one or more strings to the the stream. No length fields or
+  ## terminating zeros are written.
+  runnableExamples:
+    var strm = newStringStream("")
+    strm.write(1, 2, 3, 4)
+    strm.setPosition(0)
+    doAssert strm.readLine() == "1234"
+    strm.close()
+
+  for str in args: write(s, str)
 
 proc writeLine*(s: Stream, args: varargs[string, `$`]) =
-  ## writes one or more strings to the the stream `s` followed
+  ## Writes one or more strings to the the stream `s` followed
   ## by a new line. No length field or terminating zero is written.
+  runnableExamples:
+    var strm = newStringStream("")
+    strm.writeLine(1, 2)
+    strm.writeLine(3, 4)
+    strm.setPosition(0)
+    doAssert strm.readAll() == "12\n34\n"
+    strm.close()
+
   for str in args: write(s, str)
   write(s, "\n")
 
-proc read[T](s: Stream, result: var T) =
-  ## generic read procedure. Reads `result` from the stream `s`.
+proc read*[T](s: Stream, result: var T) =
+  ## Generic read procedure. Reads `result` from the stream `s`.
+  ##
+  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream("012")
+    ## readInt
+    var i: int8
+    strm.read(i)
+    doAssert i == 48
+    ## readData
+    var buffer: array[2, char]
+    strm.read(buffer)
+    doAssert buffer == ['1', '2']
+    strm.close()
+
   if readData(s, addr(result), sizeof(T)) != sizeof(T):
     raise newEIO("cannot read from stream")
 
-proc peek[T](s: Stream, result: var T) =
-  ## generic peek procedure. Peeks `result` from the stream `s`.
+proc peek*[T](s: Stream, result: var T) =
+  ## Generic peek procedure. Peeks `result` from the stream `s`.
+  ##
+  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream("012")
+    ## peekInt
+    var i: int8
+    strm.peek(i)
+    doAssert i == 48
+    ## peekData
+    var buffer: array[2, char]
+    strm.peek(buffer)
+    doAssert buffer == ['0', '1']
+    strm.close()
+
   if peekData(s, addr(result), sizeof(T)) != sizeof(T):
     raise newEIO("cannot read from stream")
 
 proc readChar*(s: Stream): char =
-  ## reads a char from the stream `s`. Raises `EIO` if an error occurred.
-  ## Returns '\0' as an EOF marker.
-  if readData(s, addr(result), sizeof(result)) != 1: result = '\0'
+  ## Reads a char from the stream `s`.
+  ##
+  ## Raises `IOError` if an error occurred.
+  ## Returns '\\0' as an EOF marker.
+  runnableExamples:
+    var strm = newStringStream("12\n3")
+    doAssert strm.readChar() == '1'
+    doAssert strm.readChar() == '2'
+    doAssert strm.readChar() == '\n'
+    doAssert strm.readChar() == '3'
+    doAssert strm.readChar() == '\x00'
+    strm.close()
+
+  jsOrVmBlock:
+    var str = " "
+    if readDataStr(s, str, 0..0) != 1: result = '\0'
+    else: result = str[0]
+  do:
+    if readData(s, addr(result), sizeof(result)) != 1: result = '\0'
 
 proc peekChar*(s: Stream): char =
-  ## peeks a char from the stream `s`. Raises `EIO` if an error occurred.
-  ## Returns '\0' as an EOF marker.
-  if peekData(s, addr(result), sizeof(result)) != 1: result = '\0'
+  ## Peeks a char from the stream `s`. Raises `IOError` if an error occurred.
+  ## Returns '\\0' as an EOF marker.
+  runnableExamples:
+    var strm = newStringStream("12\n3")
+    doAssert strm.peekChar() == '1'
+    doAssert strm.peekChar() == '1'
+    discard strm.readAll()
+    doAssert strm.peekChar() == '\x00'
+    strm.close()
+
+  when defined(js):
+    var str = " "
+    if peekData(s, addr(str), sizeof(result)) != 1: result = '\0'
+    else: result = str[0]
+  else:
+    if peekData(s, addr(result), sizeof(result)) != 1: result = '\0'
 
 proc readBool*(s: Stream): bool =
-  ## reads a bool from the stream `s`. Raises `EIO` if an error occurred.
-  read(s, result)
+  ## Reads a bool from the stream `s`.
+  ##
+  ## A bool is one byte long and it is `true` for every non-zero
+  ## (`0000_0000`) value.
+  ## Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(true)
+    strm.write(false)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readBool() == true
+    doAssert strm.readBool() == false
+    doAssertRaises(IOError): discard strm.readBool()
+    strm.close()
+
+  var t: byte
+  read(s, t)
+  result = t != 0.byte
 
 proc peekBool*(s: Stream): bool =
-  ## peeks a bool from the stream `s`. Raises `EIO` if an error occurred.
-  peek(s, result)
+  ## Peeks a bool from the stream `s`.
+  ##
+  ## A bool is one byte long and it is `true` for every non-zero
+  ## (`0000_0000`) value.
+  ## Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(true)
+    strm.write(false)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekBool() == true
+    ## not false
+    doAssert strm.peekBool() == true
+    doAssert strm.readBool() == true
+    doAssert strm.peekBool() == false
+    strm.close()
+
+  var t: byte
+  peek(s, t)
+  result = t != 0.byte
 
 proc readInt8*(s: Stream): int8 =
-  ## reads an int8 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Reads an int8 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'i8)
+    strm.write(2'i8)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readInt8() == 1'i8
+    doAssert strm.readInt8() == 2'i8
+    doAssertRaises(IOError): discard strm.readInt8()
+    strm.close()
+
   read(s, result)
 
 proc peekInt8*(s: Stream): int8 =
-  ## peeks an int8 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Peeks an int8 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'i8)
+    strm.write(2'i8)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekInt8() == 1'i8
+    ## not 2'i8
+    doAssert strm.peekInt8() == 1'i8
+    doAssert strm.readInt8() == 1'i8
+    doAssert strm.peekInt8() == 2'i8
+    strm.close()
+
   peek(s, result)
 
 proc readInt16*(s: Stream): int16 =
-  ## reads an int16 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Reads an int16 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'i16)
+    strm.write(2'i16)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readInt16() == 1'i16
+    doAssert strm.readInt16() == 2'i16
+    doAssertRaises(IOError): discard strm.readInt16()
+    strm.close()
+
   read(s, result)
 
 proc peekInt16*(s: Stream): int16 =
-  ## peeks an int16 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Peeks an int16 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'i16)
+    strm.write(2'i16)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekInt16() == 1'i16
+    ## not 2'i16
+    doAssert strm.peekInt16() == 1'i16
+    doAssert strm.readInt16() == 1'i16
+    doAssert strm.peekInt16() == 2'i16
+    strm.close()
+
   peek(s, result)
 
 proc readInt32*(s: Stream): int32 =
-  ## reads an int32 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Reads an int32 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'i32)
+    strm.write(2'i32)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readInt32() == 1'i32
+    doAssert strm.readInt32() == 2'i32
+    doAssertRaises(IOError): discard strm.readInt32()
+    strm.close()
+
   read(s, result)
 
 proc peekInt32*(s: Stream): int32 =
-  ## peeks an int32 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Peeks an int32 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'i32)
+    strm.write(2'i32)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekInt32() == 1'i32
+    ## not 2'i32
+    doAssert strm.peekInt32() == 1'i32
+    doAssert strm.readInt32() == 1'i32
+    doAssert strm.peekInt32() == 2'i32
+    strm.close()
+
   peek(s, result)
 
 proc readInt64*(s: Stream): int64 =
-  ## reads an int64 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Reads an int64 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'i64)
+    strm.write(2'i64)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readInt64() == 1'i64
+    doAssert strm.readInt64() == 2'i64
+    doAssertRaises(IOError): discard strm.readInt64()
+    strm.close()
+
   read(s, result)
 
 proc peekInt64*(s: Stream): int64 =
-  ## peeks an int64 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Peeks an int64 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'i64)
+    strm.write(2'i64)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekInt64() == 1'i64
+    ## not 2'i64
+    doAssert strm.peekInt64() == 1'i64
+    doAssert strm.readInt64() == 1'i64
+    doAssert strm.peekInt64() == 2'i64
+    strm.close()
+
   peek(s, result)
 
 proc readUint8*(s: Stream): uint8 =
-  ## reads an uint8 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Reads an uint8 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'u8)
+    strm.write(2'u8)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readUint8() == 1'u8
+    doAssert strm.readUint8() == 2'u8
+    doAssertRaises(IOError): discard strm.readUint8()
+    strm.close()
+
   read(s, result)
 
 proc peekUint8*(s: Stream): uint8 =
-  ## peeks an uint8 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Peeks an uint8 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'u8)
+    strm.write(2'u8)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekUint8() == 1'u8
+    ## not 2'u8
+    doAssert strm.peekUint8() == 1'u8
+    doAssert strm.readUint8() == 1'u8
+    doAssert strm.peekUint8() == 2'u8
+    strm.close()
+
   peek(s, result)
 
 proc readUint16*(s: Stream): uint16 =
-  ## reads an uint16 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Reads an uint16 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'u16)
+    strm.write(2'u16)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readUint16() == 1'u16
+    doAssert strm.readUint16() == 2'u16
+    doAssertRaises(IOError): discard strm.readUint16()
+    strm.close()
+
   read(s, result)
 
 proc peekUint16*(s: Stream): uint16 =
-  ## peeks an uint16 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Peeks an uint16 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'u16)
+    strm.write(2'u16)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekUint16() == 1'u16
+    ## not 2'u16
+    doAssert strm.peekUint16() == 1'u16
+    doAssert strm.readUint16() == 1'u16
+    doAssert strm.peekUint16() == 2'u16
+    strm.close()
+
   peek(s, result)
 
 proc readUint32*(s: Stream): uint32 =
-  ## reads an uint32 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Reads an uint32 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'u32)
+    strm.write(2'u32)
+    strm.flush()
+    strm.setPosition(0)
+
+    ## get data
+    doAssert strm.readUint32() == 1'u32
+    doAssert strm.readUint32() == 2'u32
+    doAssertRaises(IOError): discard strm.readUint32()
+    strm.close()
+
   read(s, result)
 
 proc peekUint32*(s: Stream): uint32 =
-  ## peeks an uint32 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Peeks an uint32 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'u32)
+    strm.write(2'u32)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekUint32() == 1'u32
+    ## not 2'u32
+    doAssert strm.peekUint32() == 1'u32
+    doAssert strm.readUint32() == 1'u32
+    doAssert strm.peekUint32() == 2'u32
+    strm.close()
+
   peek(s, result)
 
 proc readUint64*(s: Stream): uint64 =
-  ## reads an uint64 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Reads an uint64 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'u64)
+    strm.write(2'u64)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readUint64() == 1'u64
+    doAssert strm.readUint64() == 2'u64
+    doAssertRaises(IOError): discard strm.readUint64()
+    strm.close()
+
   read(s, result)
 
 proc peekUint64*(s: Stream): uint64 =
-  ## peeks an uint64 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Peeks an uint64 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'u64)
+    strm.write(2'u64)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekUint64() == 1'u64
+    ## not 2'u64
+    doAssert strm.peekUint64() == 1'u64
+    doAssert strm.readUint64() == 1'u64
+    doAssert strm.peekUint64() == 2'u64
+    strm.close()
+
   peek(s, result)
 
 proc readFloat32*(s: Stream): float32 =
-  ## reads a float32 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Reads a float32 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'f32)
+    strm.write(2'f32)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readFloat32() == 1'f32
+    doAssert strm.readFloat32() == 2'f32
+    doAssertRaises(IOError): discard strm.readFloat32()
+    strm.close()
+
   read(s, result)
 
 proc peekFloat32*(s: Stream): float32 =
-  ## peeks a float32 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Peeks a float32 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'f32)
+    strm.write(2'f32)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekFloat32() == 1'f32
+    ## not 2'f32
+    doAssert strm.peekFloat32() == 1'f32
+    doAssert strm.readFloat32() == 1'f32
+    doAssert strm.peekFloat32() == 2'f32
+    strm.close()
+
   peek(s, result)
 
 proc readFloat64*(s: Stream): float64 =
-  ## reads a float64 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Reads a float64 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'f64)
+    strm.write(2'f64)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readFloat64() == 1'f64
+    doAssert strm.readFloat64() == 2'f64
+    doAssertRaises(IOError): discard strm.readFloat64()
+    strm.close()
+
   read(s, result)
 
 proc peekFloat64*(s: Stream): float64 =
-  ## peeks a float64 from the stream `s`. Raises `EIO` if an error occurred.
+  ## Peeks a float64 from the stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'f64)
+    strm.write(2'f64)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekFloat64() == 1'f64
+    ## not 2'f64
+    doAssert strm.peekFloat64() == 1'f64
+    doAssert strm.readFloat64() == 1'f64
+    doAssert strm.peekFloat64() == 2'f64
+    strm.close()
+
   peek(s, result)
 
-proc readStr*(s: Stream, length: int): TaintedString =
-  ## reads a string of length `length` from the stream `s`. Raises `EIO` if
+proc readStrPrivate(s: Stream, length: int, str: var string) =
+  if length > len(str): setLen(str, length)
+  var L: int
+  when nimvm:
+    L = readDataStr(s, str, 0..length-1)
+  else:
+    when defined(js):
+      L = readData(s, addr(str), length)
+    else:
+      L = readData(s, cstring(str), length)
+  if L != len(str): setLen(str, L)
+
+proc readStr*(s: Stream, length: int, str: var string) {.since: (1, 3).} =
+  ## Reads a string of length `length` from the stream `s`. Raises `IOError` if
   ## an error occurred.
-  result = newString(length).TaintedString
-  var L = readData(s, cstring(result), length)
-  if L != length: setLen(result.string, L)
+  readStrPrivate(s, length, str)
 
-proc peekStr*(s: Stream, length: int): TaintedString =
-  ## peeks a string of length `length` from the stream `s`. Raises `EIO` if
+proc readStr*(s: Stream, length: int): string =
+  ## Reads a string of length `length` from the stream `s`. Raises `IOError` if
   ## an error occurred.
-  result = newString(length).TaintedString
-  var L = peekData(s, cstring(result), length)
-  if L != length: setLen(result.string, L)
+  runnableExamples:
+    var strm = newStringStream("abcde")
+    doAssert strm.readStr(2) == "ab"
+    doAssert strm.readStr(2) == "cd"
+    doAssert strm.readStr(2) == "e"
+    doAssert strm.readStr(2) == ""
+    strm.close()
+  result = newString(length)
+  readStrPrivate(s, length, result)
+
+proc peekStrPrivate(s: Stream, length: int, str: var string) =
+  if length > len(str): setLen(str, length)
+  when defined(js):
+    let L = peekData(s, addr(str), length)
+  else:
+    let L = peekData(s, cstring(str), length)
+  if L != len(str): setLen(str, L)
 
-proc readLine*(s: Stream, line: var TaintedString): bool =
-  ## reads a line of text from the stream `s` into `line`. `line` must not be
+proc peekStr*(s: Stream, length: int, str: var string) {.since: (1, 3).} =
+  ## Peeks a string of length `length` from the stream `s`. Raises `IOError` if
+  ## an error occurred.
+  peekStrPrivate(s, length, str)
+
+proc peekStr*(s: Stream, length: int): string =
+  ## Peeks a string of length `length` from the stream `s`. Raises `IOError` if
+  ## an error occurred.
+  runnableExamples:
+    var strm = newStringStream("abcde")
+    doAssert strm.peekStr(2) == "ab"
+    ## not "cd
+    doAssert strm.peekStr(2) == "ab"
+    doAssert strm.readStr(2) == "ab"
+    doAssert strm.peekStr(2) == "cd"
+    strm.close()
+  result = newString(length)
+  peekStrPrivate(s, length, result)
+
+proc readLine*(s: Stream, line: var string): 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 ``CR``, ``LF`` or
-  ## ``CRLF``. The newline character(s) are not part of the returned string.
+  ##
+  ## 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.
-  line.string.setLen(0)
-  while true:
-    var c = readChar(s)
-    if c == '\c':
-      c = readChar(s)
-      break
-    elif c == '\L': break
-    elif c == '\0':
-      if line.len > 0: break
-      else: return false
-    line.string.add(c)
-  result = true
-
-proc peekLine*(s: Stream, line: var TaintedString): bool =
-  ## peeks a line of text from the stream `s` into `line`. `line` must not be
+  ##
+  ## See also:
+  ## * `readLine(Stream) proc <#readLine,Stream>`_
+  ## * `peekLine(Stream) proc <#peekLine,Stream>`_
+  ## * `peekLine(Stream, string) proc <#peekLine,Stream,string>`_
+  runnableExamples:
+    var strm = newStringStream("The first line\nthe second line\nthe third line")
+    var line = ""
+    doAssert strm.readLine(line) == true
+    doAssert line == "The first line"
+    doAssert strm.readLine(line) == true
+    doAssert line == "the second line"
+    doAssert strm.readLine(line) == true
+    doAssert line == "the third line"
+    doAssert strm.readLine(line) == false
+    doAssert line == ""
+    strm.close()
+
+  if s.readLineImpl != nil:
+    result = s.readLineImpl(s, line)
+  else:
+    # fallback
+    line.setLen(0)
+    while true:
+      var c = readChar(s)
+      if c == '\c':
+        c = readChar(s)
+        break
+      elif c == '\L': break
+      elif c == '\0':
+        if line.len > 0: break
+        else: return false
+      line.add(c)
+    result = true
+
+proc peekLine*(s: Stream, line: var string): bool =
+  ## Peeks 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 ``CR``, ``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.
+  ##
+  ## See also:
+  ## * `readLine(Stream) proc <#readLine,Stream>`_
+  ## * `readLine(Stream, string) proc <#readLine,Stream,string>`_
+  ## * `peekLine(Stream) proc <#peekLine,Stream>`_
+  runnableExamples:
+    var strm = newStringStream("The first line\nthe second line\nthe third line")
+    var line = ""
+    doAssert strm.peekLine(line) == true
+    doAssert line == "The first line"
+    doAssert strm.peekLine(line) == true
+    ## not "the second line"
+    doAssert line == "The first line"
+    doAssert strm.readLine(line) == true
+    doAssert line == "The first line"
+    doAssert strm.peekLine(line) == true
+    doAssert line == "the second line"
+    strm.close()
+
   let pos = getPosition(s)
   defer: setPosition(s, pos)
   result = readLine(s, line)
 
-proc readLine*(s: Stream): TaintedString =
-  ## Reads a line from a stream `s`. Note: This is not very efficient. Raises
-  ## `EIO` if an error occurred.
-  result = TaintedString""
+proc readLine*(s: Stream): string =
+  ## Reads a line from a stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** This is not very efficient.
+  ##
+  ## See also:
+  ## * `readLine(Stream, string) proc <#readLine,Stream,string>`_
+  ## * `peekLine(Stream) proc <#peekLine,Stream>`_
+  ## * `peekLine(Stream, string) proc <#peekLine,Stream,string>`_
+  runnableExamples:
+    var strm = newStringStream("The first line\nthe second line\nthe third line")
+    doAssert strm.readLine() == "The first line"
+    doAssert strm.readLine() == "the second line"
+    doAssert strm.readLine() == "the third line"
+    doAssertRaises(IOError): discard strm.readLine()
+    strm.close()
+
+  result = ""
   if s.atEnd:
     raise newEIO("cannot read from stream")
   while true:
@@ -308,23 +1100,109 @@ proc readLine*(s: Stream): TaintedString =
     if c == '\L' or c == '\0':
       break
     else:
-      result.string.add(c)
+      result.add(c)
+
+proc peekLine*(s: Stream): string =
+  ## Peeks a line from a stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** This is not very efficient.
+  ##
+  ## See also:
+  ## * `readLine(Stream) proc <#readLine,Stream>`_
+  ## * `readLine(Stream, string) proc <#readLine,Stream,string>`_
+  ## * `peekLine(Stream, string) proc <#peekLine,Stream,string>`_
+  runnableExamples:
+    var strm = newStringStream("The first line\nthe second line\nthe third line")
+    doAssert strm.peekLine() == "The first line"
+    ## not "the second line"
+    doAssert strm.peekLine() == "The first line"
+    doAssert strm.readLine() == "The first line"
+    doAssert strm.peekLine() == "the second line"
+    strm.close()
 
-proc peekLine*(s: Stream): TaintedString =
-  ## Peeks a line from a stream `s`. Note: This is not very efficient. Raises
-  ## `EIO` if an error occurred.
   let pos = getPosition(s)
   defer: setPosition(s, pos)
   result = readLine(s)
 
-when not defined(js):
+iterator lines*(s: Stream): string =
+  ## Iterates over every line in the stream.
+  ## The iteration is based on ``readLine``.
+  ##
+  ## See also:
+  ## * `readLine(Stream) proc <#readLine,Stream>`_
+  ## * `readLine(Stream, string) proc <#readLine,Stream,string>`_
+  runnableExamples:
+    var strm = newStringStream("The first line\nthe second line\nthe third line")
+    var lines: seq[string]
+    for line in strm.lines():
+      lines.add line
+    doAssert lines == @["The first line", "the second line", "the third line"]
+    strm.close()
+
+  var line: string
+  while s.readLine(line):
+    yield line
 
-  type
-    StringStream* = ref StringStreamObj ## a stream that encapsulates a string
-    StringStreamObj* = object of StreamObj
-      data*: string
-      pos: int
+type
+  StringStream* = ref StringStreamObj
+    ## A stream that encapsulates a string.
+  StringStreamObj* = object of StreamObj
+    ## A string stream object.
+    data*: string ## A string data.
+                  ## This is updated when called `writeLine` etc.
+    pos: int
+
+when (NimMajor, NimMinor) < (1, 3) and defined(js):
+  proc ssAtEnd(s: Stream): bool {.compileTime.} =
+    var s = StringStream(s)
+    return s.pos >= s.data.len
+
+  proc ssSetPosition(s: Stream, pos: int) {.compileTime.} =
+    var s = StringStream(s)
+    s.pos = clamp(pos, 0, s.data.len)
+
+  proc ssGetPosition(s: Stream): int {.compileTime.} =
+    var s = StringStream(s)
+    return s.pos
+
+  proc ssReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int {.compileTime.} =
+    var s = StringStream(s)
+    result = min(slice.b + 1 - slice.a, s.data.len - s.pos)
+    if result > 0:
+      buffer[slice.a..<slice.a+result] = s.data[s.pos..<s.pos+result]
+      inc(s.pos, result)
+    else:
+      result = 0
 
+  proc ssClose(s: Stream) {.compileTime.} =
+    var s = StringStream(s)
+    s.data = ""
+
+  proc newStringStream*(s: string = ""): owned StringStream {.compileTime.} =
+    new(result)
+    result.data = s
+    result.pos = 0
+    result.closeImpl = ssClose
+    result.atEndImpl = ssAtEnd
+    result.setPositionImpl = ssSetPosition
+    result.getPositionImpl = ssGetPosition
+    result.readDataStrImpl = ssReadDataStr
+
+  proc readAll*(s: Stream): string {.compileTime.} =
+    const bufferSize = 1024
+    var bufferr: string
+    bufferr.setLen(bufferSize)
+    while true:
+      let readBytes = readDataStr(s, bufferr, 0..<bufferSize)
+      if readBytes == 0:
+        break
+      let prevLen = result.len
+      result.setLen(prevLen + readBytes)
+      result[prevLen..<prevLen+readBytes] = bufferr[0..<readBytes]
+      if readBytes < bufferSize:
+        break
+
+else: # after 1.3 or JS not defined
   proc ssAtEnd(s: Stream): bool =
     var s = StringStream(s)
     return s.pos >= s.data.len
@@ -337,11 +1215,35 @@ when not defined(js):
     var s = StringStream(s)
     return s.pos
 
+  proc ssReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int =
+    var s = StringStream(s)
+    when nimvm:
+      discard
+    else:
+      when declared(prepareMutation):
+        prepareMutation(buffer) # buffer might potentially be a CoW literal with ARC
+    result = min(slice.b + 1 - slice.a, s.data.len - s.pos)
+    if result > 0:
+      jsOrVmBlock:
+        buffer[slice.a..<slice.a+result] = s.data[s.pos..<s.pos+result]
+      do:
+        copyMem(unsafeAddr buffer[slice.a], addr s.data[s.pos], result)
+      inc(s.pos, result)
+    else:
+      result = 0
+
   proc ssReadData(s: Stream, buffer: pointer, bufLen: int): int =
     var s = StringStream(s)
     result = min(bufLen, s.data.len - s.pos)
     if result > 0:
-      copyMem(buffer, addr(s.data[s.pos]), result)
+      when defined(js):
+        try:
+          cast[ptr string](buffer)[][0..<result] = s.data[s.pos..<s.pos+result]
+        except:
+          raise newException(Defect, "could not read string stream, " &
+            "did you use a non-string buffer pointer?", getCurrentException())
+      elif not defined(nimscript):
+        copyMem(buffer, addr(s.data[s.pos]), result)
       inc(s.pos, result)
     else:
       result = 0
@@ -350,7 +1252,14 @@ when not defined(js):
     var s = StringStream(s)
     result = min(bufLen, s.data.len - s.pos)
     if result > 0:
-      copyMem(buffer, addr(s.data[s.pos]), result)
+      when defined(js):
+        try:
+          cast[ptr string](buffer)[][0..<result] = s.data[s.pos..<s.pos+result]
+        except:
+          raise newException(Defect, "could not peek string stream, " &
+            "did you use a non-string buffer pointer?", getCurrentException())
+      elif not defined(nimscript):
+        copyMem(buffer, addr(s.data[s.pos]), result)
     else:
       result = 0
 
@@ -360,86 +1269,211 @@ when not defined(js):
       return
     if s.pos + bufLen > s.data.len:
       setLen(s.data, s.pos + bufLen)
-    copyMem(addr(s.data[s.pos]), buffer, bufLen)
+    when defined(js):
+      try:
+        s.data[s.pos..<s.pos+bufLen] = cast[ptr string](buffer)[][0..<bufLen]
+      except:
+        raise newException(Defect, "could not write to string stream, " &
+          "did you use a non-string buffer pointer?", getCurrentException())
+    elif not defined(nimscript):
+      copyMem(addr(s.data[s.pos]), buffer, bufLen)
     inc(s.pos, bufLen)
 
   proc ssClose(s: Stream) =
     var s = StringStream(s)
-    s.data = nil
+    s.data = ""
+
+  proc newStringStream*(s: sink string = ""): owned StringStream =
+    ## Creates a new stream from the string `s`.
+    ##
+    ## See also:
+    ## * `newFileStream proc <#newFileStream,File>`_ creates a file stream from
+    ##   opened File.
+    ## * `newFileStream proc <#newFileStream,string,FileMode,int>`_  creates a
+    ##   file stream from the file name and the mode.
+    ## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a
+    ##   file stream from the file name and the mode.
+    runnableExamples:
+      var strm = newStringStream("The first line\nthe second line\nthe third line")
+      doAssert strm.readLine() == "The first line"
+      doAssert strm.readLine() == "the second line"
+      doAssert strm.readLine() == "the third line"
+      strm.close()
 
-  proc newStringStream*(s: string = ""): StringStream =
-    ## creates a new stream from the string `s`.
     new(result)
     result.data = s
+    when nimvm:
+      discard
+    else:
+      when declared(prepareMutation):
+        prepareMutation(result.data) # Allows us to mutate using `addr` logic like `copyMem`, otherwise it errors.
     result.pos = 0
     result.closeImpl = ssClose
     result.atEndImpl = ssAtEnd
     result.setPositionImpl = ssSetPosition
     result.getPositionImpl = ssGetPosition
-    result.readDataImpl = ssReadData
-    result.peekDataImpl = ssPeekData
-    result.writeDataImpl = ssWriteData
+    result.readDataStrImpl = ssReadDataStr
+    when nimvm:
+      discard
+    else:
+      result.readDataImpl = ssReadData
+      result.peekDataImpl = ssPeekData
+      result.writeDataImpl = ssWriteData
 
-  type
-    FileStream* = ref FileStreamObj ## a stream that encapsulates a `File`
-    FileStreamObj* = object of Stream
-      f: File
-
-  proc fsClose(s: Stream) =
-    if FileStream(s).f != nil:
-      close(FileStream(s).f)
-      FileStream(s).f = nil
-  proc fsFlush(s: Stream) = flushFile(FileStream(s).f)
-  proc fsAtEnd(s: Stream): bool = return endOfFile(FileStream(s).f)
-  proc fsSetPosition(s: Stream, pos: int) = setFilePos(FileStream(s).f, pos)
-  proc fsGetPosition(s: Stream): int = return int(getFilePos(FileStream(s).f))
-
-  proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
-    result = readBuffer(FileStream(s).f, buffer, bufLen)
-
-  proc fsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
-    let pos = fsGetPosition(s)
-    defer: fsSetPosition(s, pos)
-    result = readBuffer(FileStream(s).f, buffer, bufLen)
-
-  proc fsWriteData(s: Stream, buffer: pointer, bufLen: int) =
-    if writeBuffer(FileStream(s).f, buffer, bufLen) != bufLen:
-      raise newEIO("cannot write to stream")
-
-  proc newFileStream*(f: File): FileStream =
-    ## creates a new stream from the file `f`.
-    new(result)
-    result.f = f
-    result.closeImpl = fsClose
-    result.atEndImpl = fsAtEnd
-    result.setPositionImpl = fsSetPosition
-    result.getPositionImpl = fsGetPosition
-    result.readDataImpl = fsReadData
-    result.peekDataImpl = fsPeekData
-    result.writeDataImpl = fsWriteData
-    result.flushImpl = fsFlush
-
-  proc newFileStream*(filename: string, mode: FileMode = fmRead, bufSize: int = -1): FileStream =
-    ## creates a new stream from the file named `filename` with the mode `mode`.
-    ## If the file cannot be opened, nil is returned. See the `system
-    ## <system.html>`_ module for a list of available FileMode enums.
-    ## **This function returns nil in case of failure. To prevent unexpected
-    ## behavior and ensure proper error handling, use openFileStream instead.**
+type
+  FileStream* = ref FileStreamObj
+    ## A stream that encapsulates a `File`.
+    ##
+    ## **Note:** Not available for JS backend.
+  FileStreamObj* = object of Stream
+    ## A file stream object.
+    ##
+    ## **Note:** Not available for JS backend.
+    f: File
+
+proc fsClose(s: Stream) =
+  if FileStream(s).f != nil:
+    close(FileStream(s).f)
+    FileStream(s).f = nil
+proc fsFlush(s: Stream) = flushFile(FileStream(s).f)
+proc fsAtEnd(s: Stream): bool = return endOfFile(FileStream(s).f)
+proc fsSetPosition(s: Stream, pos: int) = setFilePos(FileStream(s).f, pos)
+proc fsGetPosition(s: Stream): int = return int(getFilePos(FileStream(s).f))
+
+proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
+  result = readBuffer(FileStream(s).f, buffer, bufLen)
+
+proc fsReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int =
+  result = readBuffer(FileStream(s).f, addr buffer[slice.a], slice.b + 1 - slice.a)
+
+proc fsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
+  let pos = fsGetPosition(s)
+  defer: fsSetPosition(s, pos)
+  result = readBuffer(FileStream(s).f, buffer, bufLen)
+
+proc fsWriteData(s: Stream, buffer: pointer, bufLen: int) =
+  if writeBuffer(FileStream(s).f, buffer, bufLen) != bufLen:
+    raise newEIO("cannot write to stream")
+
+proc fsReadLine(s: Stream, line: var string): bool =
+  result = readLine(FileStream(s).f, line)
+
+proc newFileStream*(f: File): owned FileStream =
+  ## Creates a new stream from the file `f`.
+  ##
+  ## **Note:** Not available for JS backend.
+  ##
+  ## See also:
+  ## * `newStringStream proc <#newStringStream,string>`_ creates a new stream
+  ##   from string.
+  ## * `newFileStream proc <#newFileStream,string,FileMode,int>`_ is the same
+  ##   as using `open proc <syncio.html#open,File,string,FileMode,int>`_
+  ##   on Examples.
+  ## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a
+  ##   file stream from the file name and the mode.
+  runnableExamples:
+    ## Input (somefile.txt):
+    ## The first line
+    ## the second line
+    ## the third line
     var f: File
-    if open(f, filename, mode, bufSize): result = newFileStream(f)
+    if open(f, "somefile.txt", fmRead, -1):
+      var strm = newFileStream(f)
+      var line = ""
+      while strm.readLine(line):
+        echo line
+      ## Output:
+      ## The first line
+      ## the second line
+      ## the third line
+      strm.close()
 
-  proc openFileStream*(filename: string, mode: FileMode = fmRead, bufSize: int = -1): FileStream =
-    ## creates a new stream from the file named `filename` with the mode `mode`.
-    ## If the file cannot be opened, an IO exception is raised.
-    var f: File
-    if open(f, filename, mode, bufSize):
-      return newFileStream(f)
-    else:
-      raise newEIO("cannot open file")
+  new(result)
+  result.f = f
+  result.closeImpl = fsClose
+  result.atEndImpl = fsAtEnd
+  result.setPositionImpl = fsSetPosition
+  result.getPositionImpl = fsGetPosition
+  result.readDataStrImpl = fsReadDataStr
+  result.readDataImpl = fsReadData
+  result.readLineImpl = fsReadLine
+  result.peekDataImpl = fsPeekData
+  result.writeDataImpl = fsWriteData
+  result.flushImpl = fsFlush
+
+proc newFileStream*(filename: string, mode: FileMode = fmRead,
+    bufSize: int = -1): owned FileStream =
+  ## Creates a new stream from the file named `filename` with the mode `mode`.
+  ##
+  ## If the file cannot be opened, `nil` is returned. See the `io module
+  ## <syncio.html>`_ for a list of available `FileMode enums <syncio.html#FileMode>`_.
+  ##
+  ## **Note:**
+  ## * **This function returns nil in case of failure.**
+  ##   To prevent unexpected behavior and ensure proper error handling,
+  ##   use `openFileStream proc <#openFileStream,string,FileMode,int>`_
+  ##   instead.
+  ## * Not available for JS backend.
+  ##
+  ## See also:
+  ## * `newStringStream proc <#newStringStream,string>`_ creates a new stream
+  ##   from string.
+  ## * `newFileStream proc <#newFileStream,File>`_ creates a file stream from
+  ##   opened File.
+  ## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a
+  ##   file stream from the file name and the mode.
+  runnableExamples:
+    from std/os import removeFile
+    var strm = newFileStream("somefile.txt", fmWrite)
+    if not isNil(strm):
+      strm.writeLine("The first line")
+      strm.writeLine("the second line")
+      strm.writeLine("the third line")
+      strm.close()
+      ## Output (somefile.txt)
+      ## The first line
+      ## the second line
+      ## the third line
+      removeFile("somefile.txt")
+
+  var f: File
+  if open(f, filename, mode, bufSize): result = newFileStream(f)
+
+proc openFileStream*(filename: string, mode: FileMode = fmRead,
+    bufSize: int = -1): owned FileStream =
+  ## Creates a new stream from the file named `filename` with the mode `mode`.
+  ## If the file cannot be opened, an IO exception is raised.
+  ##
+  ## **Note:** Not available for JS backend.
+  ##
+  ## See also:
+  ## * `newStringStream proc <#newStringStream,string>`_ creates a new stream
+  ##   from string.
+  ## * `newFileStream proc <#newFileStream,File>`_ creates a file stream from
+  ##   opened File.
+  ## * `newFileStream proc <#newFileStream,string,FileMode,int>`_  creates a
+  ##   file stream from the file name and the mode.
+  runnableExamples:
+    try:
+      ## Input (somefile.txt):
+      ## The first line
+      ## the second line
+      ## the third line
+      var strm = openFileStream("somefile.txt")
+      echo strm.readLine()
+      ## Output:
+      ## The first line
+      strm.close()
+    except:
+      stderr.write getCurrentExceptionMsg()
+
+  var f: File
+  if open(f, filename, mode, bufSize):
+    return newFileStream(f)
+  else:
+    raise newEIO("cannot open file stream: " & filename)
 
-when true:
-  discard
-else:
+when false:
   type
     FileHandleStream* = ref FileHandleStreamObj
     FileHandleStreamObj* = object of Stream
@@ -457,7 +1491,7 @@ else:
     # do not import windows as this increases compile times:
     discard
   else:
-    import posix
+    import std/posix
 
     proc hsSetPosition(s: FileHandleStream, pos: int) =
       discard lseek(s.handle, pos, SEEK_SET)
@@ -481,7 +1515,7 @@ else:
         raise newEIO("cannot write to stream")
       inc(s.pos, bufLen)
 
-  proc newFileHandleStream*(handle: FileHandle): FileHandleStream =
+  proc newFileHandleStream*(handle: FileHandle): owned FileHandleStream =
     new(result)
     result.handle = handle
     result.pos = 0
@@ -494,31 +1528,18 @@ else:
     result.writeData = hsWriteData
 
   proc newFileHandleStream*(filename: string,
-                            mode: FileMode): FileHandleStream =
+                            mode: FileMode): owned FileHandleStream =
     when defined(windows):
       discard
     else:
       var flags: cint
       case mode
-      of fmRead:              flags = posix.O_RDONLY
-      of fmWrite:             flags = O_WRONLY or int(O_CREAT)
-      of fmReadWrite:         flags = O_RDWR or int(O_CREAT)
+      of fmRead: flags = posix.O_RDONLY
+      of fmWrite: flags = O_WRONLY or int(O_CREAT)
+      of fmReadWrite: flags = O_RDWR or int(O_CREAT)
       of fmReadWriteExisting: flags = O_RDWR
-      of fmAppend:            flags = O_WRONLY or int(O_CREAT) or O_APPEND
+      of fmAppend: flags = O_WRONLY or int(O_CREAT) or O_APPEND
+      static: raiseAssert "unreachable" # handle bug #17888
       var handle = open(filename, flags)
       if handle < 0: raise newEOS("posix.open() call failed")
     result = newFileHandleStream(handle)
-
-when isMainModule and defined(testing):
-  var ss = newStringStream("The quick brown fox jumped over the lazy dog.\nThe lazy dog ran")
-  assert(ss.getPosition == 0)
-  assert(ss.peekStr(5) == "The q")
-  assert(ss.getPosition == 0) # haven't moved
-  assert(ss.readStr(5) == "The q")
-  assert(ss.getPosition == 5) # did move
-  assert(ss.peekLine() == "uick brown fox jumped over the lazy dog.")
-  assert(ss.getPosition == 5) # haven't moved
-  var str = newString(100)
-  assert(ss.peekLine(str))
-  assert(str == "uick brown fox jumped over the lazy dog.")
-  assert(ss.getPosition == 5) # haven't moved
diff --git a/lib/pure/streamwrapper.nim b/lib/pure/streamwrapper.nim
new file mode 100644
index 000000000..99752a9ab
--- /dev/null
+++ b/lib/pure/streamwrapper.nim
@@ -0,0 +1,121 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements stream wrapper.
+##
+## **Since** version 1.2.
+
+import std/[deques, streams]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
+type
+  PipeOutStream*[T] = ref object of T
+    # When stream peek operation is called, it reads from base stream
+    # type using `baseReadDataImpl` and stores the content to this buffer.
+    # Next stream read operation returns data in the buffer so that previus peek
+    # operation looks like didn't changed read positon.
+    # When stream read operation that returns N byte data is called and the size is smaller than buffer size,
+    # first N elements are removed from buffer.
+    # Deque type can do such operation more efficiently than seq type.
+    buffer: Deque[char]
+    baseReadLineImpl: typeof(StreamObj.readLineImpl)
+    baseReadDataImpl: typeof(StreamObj.readDataImpl)
+
+proc posReadLine[T](s: Stream, line: var string): bool =
+  var s = PipeOutStream[T](s)
+  assert s.baseReadLineImpl != nil
+
+  let n = s.buffer.len
+  line.setLen(0)
+  for i in 0..<n:
+    var c = s.buffer.popFirst
+    if c == '\c':
+      c = readChar(s)
+      return true
+    elif c == '\L': return true
+    elif c == '\0':
+      return line.len > 0
+    line.add(c)
+
+  var line2: string
+  result = s.baseReadLineImpl(s, line2)
+  line.add line2
+
+proc posReadData[T](s: Stream, buffer: pointer, bufLen: int): int =
+  var s = PipeOutStream[T](s)
+  assert s.baseReadDataImpl != nil
+
+  let
+    dest = cast[ptr UncheckedArray[char]](buffer)
+    n = min(s.buffer.len, bufLen)
+  result = n
+  for i in 0..<n:
+    dest[i] = s.buffer.popFirst
+  if bufLen > n:
+    result += s.baseReadDataImpl(s, addr dest[n], bufLen - n)
+
+proc posReadDataStr[T](s: Stream, buffer: var string, slice: Slice[int]): int =
+  posReadData[T](s, addr buffer[slice.a], slice.len)
+
+proc posPeekData[T](s: Stream, buffer: pointer, bufLen: int): int =
+  var s = PipeOutStream[T](s)
+  assert s.baseReadDataImpl != nil
+
+  let
+    dest = cast[ptr UncheckedArray[char]](buffer)
+    n = min(s.buffer.len, bufLen)
+
+  result = n
+  for i in 0..<n:
+    dest[i] = s.buffer[i]
+
+  if bufLen > n:
+    let
+      newDataNeeded = bufLen - n
+      numRead = s.baseReadDataImpl(s, addr dest[n], newDataNeeded)
+    result += numRead
+    for i in 0..<numRead:
+      s.buffer.addLast dest[n + i]
+
+proc newPipeOutStream*[T](s: sink (ref T)): owned PipeOutStream[T] =
+  ## Wrap pipe for reading with PipeOutStream so that you can use peek* procs and generate runtime error
+  ## when setPosition/getPosition is called or write operation is performed.
+  ##
+  ## Example:
+  ##   ```Nim
+  ##   import std/[osproc, streamwrapper]
+  ##   var
+  ##     p = startProcess(exePath)
+  ##     outStream = p.outputStream().newPipeOutStream()
+  ##   echo outStream.peekChar
+  ##   p.close()
+  ##   ```
+
+  assert s.readDataImpl != nil
+
+  new(result)
+  for dest, src in fields((ref T)(result)[], s[]):
+    dest = src
+  wasMoved(s[])
+  if result.readLineImpl != nil:
+    result.baseReadLineImpl = result.readLineImpl
+    result.readLineImpl = posReadLine[T]
+  result.baseReadDataImpl = result.readDataImpl
+  result.readDataImpl = posReadData[T]
+  result.readDataStrImpl = posReadDataStr[T]
+  result.peekDataImpl = posPeekData[T]
+
+  # Set nil to anything you may not call.
+  result.setPositionImpl = nil
+  result.getPositionImpl = nil
+  result.writeDataImpl = nil
+  result.flushImpl = nil
diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim
index 36404cdf7..7d093ebb3 100644
--- a/lib/pure/strformat.nim
+++ b/lib/pure/strformat.nim
@@ -9,332 +9,325 @@
 
 ##[
 String `interpolation`:idx: / `format`:idx: inspired by
-Python's ``f``-strings.
+Python's f-strings.
 
-``fmt`` vs. ``&``
-=================
+# `fmt` vs. `&`
 
-You can use either ``fmt`` or the unary ``&`` operator for formatting. The
+You can use either `fmt` or the unary `&` operator for formatting. The
 difference between them is subtle but important.
 
-The ``fmt"{expr}"`` syntax is more aesthetically pleasing, but it hides a small
+The `fmt"{expr}"` syntax is more aesthetically pleasing, but it hides a small
 gotcha. The string is a
 `generalized raw string literal <manual.html#lexical-analysis-generalized-raw-string-literals>`_.
 This has some surprising effects:
+]##
 
-.. code-block:: nim
-
-    import strformat
-    let msg = "hello"
-    doAssert fmt"{msg}\n" == "hello\\n"
+runnableExamples:
+  let msg = "hello"
+  assert fmt"{msg}\n" == "hello\\n"
 
-Because the literal is a raw string literal, the ``\n`` is not interpreted as
+##[
+Because the literal is a raw string literal, the `\n` is not interpreted as
 an escape sequence.
 
-There are multiple ways to get around this, including the use of the ``&``
-operator:
-
-.. code-block:: nim
+There are multiple ways to get around this, including the use of the `&` operator:
+]##
 
-    import strformat
-    let msg = "hello"
+runnableExamples:
+  let msg = "hello"
 
-    doAssert &"{msg}\n" == "hello\n"
+  assert &"{msg}\n" == "hello\n"
 
-    doAssert fmt"{msg}{'\n'}" == "hello\n"
-    doAssert fmt("{msg}\n") == "hello\n"
-    doAssert "{msg}\n".fmt == "hello\n"
+  assert fmt"{msg}{'\n'}" == "hello\n"
+  assert fmt("{msg}\n") == "hello\n"
+  assert "{msg}\n".fmt == "hello\n"
 
+##[
 The choice of style is up to you.
 
-Formatting strings
-==================
+# Formatting strings
+]##
+
+runnableExamples:
+  assert &"""{"abc":>4}""" == " abc"
+  assert &"""{"abc":<4}""" == "abc "
+
+##[
+# Formatting floats
+]##
 
-.. code-block:: nim
+runnableExamples:
+  assert fmt"{-12345:08}" == "-0012345"
+  assert fmt"{-1:3}" == " -1"
+  assert fmt"{-1:03}" == "-01"
+  assert fmt"{16:#X}" == "0x10"
 
-    import strformat
+  assert fmt"{123.456}" == "123.456"
+  assert fmt"{123.456:>9.3f}" == "  123.456"
+  assert fmt"{123.456:9.3f}" == "  123.456"
+  assert fmt"{123.456:9.4f}" == " 123.4560"
+  assert fmt"{123.456:>9.0f}" == "     123."
+  assert fmt"{123.456:<9.4f}" == "123.4560 "
 
-    doAssert &"""{"abc":>4}""" == " abc"
-    doAssert &"""{"abc":<4}""" == "abc "
+  assert fmt"{123.456:e}" == "1.234560e+02"
+  assert fmt"{123.456:>13e}" == " 1.234560e+02"
+  assert fmt"{123.456:13e}" == " 1.234560e+02"
 
-Formatting floats
-=================
+##[
+# Expressions
+]##
+runnableExamples:
+  let x = 3.14
+  assert fmt"{(if x!=0: 1.0/x else: 0):.5}" == "0.31847"
+  assert fmt"""{(block:
+    var res: string
+    for i in 1..15:
+      res.add (if i mod 15 == 0: "FizzBuzz"
+        elif i mod 5 == 0: "Buzz"
+        elif i mod 3 == 0: "Fizz"
+        else: $i) & " "
+    res)}""" == "1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz "
+##[
+# Debugging strings
 
-.. code-block:: nim
+`fmt"{expr=}"` expands to `fmt"expr={expr}"` namely the text of the expression,
+an equal sign and the results of evaluated expression.
+]##
 
-    import strformat
+runnableExamples:
+  assert fmt"{123.456=}" == "123.456=123.456"
+  assert fmt"{123.456=:>9.3f}" == "123.456=  123.456"
 
-    doAssert fmt"{-12345:08}" == "-0012345"
-    doAssert fmt"{-1:3}" == " -1"
-    doAssert fmt"{-1:03}" == "-01"
-    doAssert fmt"{16:#X}" == "0x10"
+  let x = "hello"
+  assert fmt"{x=}" == "x=hello"
+  assert fmt"{x =}" == "x =hello"
 
-    doAssert fmt"{123.456}" == "123.456"
-    doAssert fmt"{123.456:>9.3f}" == "  123.456"
-    doAssert fmt"{123.456:9.3f}" == "  123.456"
-    doAssert fmt"{123.456:9.4f}" == " 123.4560"
-    doAssert fmt"{123.456:>9.0f}" == "     123."
-    doAssert fmt"{123.456:<9.4f}" == "123.4560 "
+  let y = 3.1415926
+  assert fmt"{y=:.2f}" == fmt"y={y:.2f}"
+  assert fmt"{y=}" == fmt"y={y}"
+  assert fmt"{y = : <8}" == fmt"y = 3.14159 "
 
-    doAssert fmt"{123.456:e}" == "1.234560e+02"
-    doAssert fmt"{123.456:>13e}" == " 1.234560e+02"
-    doAssert fmt"{123.456:13e}" == " 1.234560e+02"
+  proc hello(a: string, b: float): int = 12
+  assert fmt"{hello(x, y) = }" == "hello(x, y) = 12"
+  assert fmt"{x.hello(y) = }" == "x.hello(y) = 12"
+  assert fmt"{hello x, y = }" == "hello x, y = 12"
 
+##[
+Note that it is space sensitive:
+]##
 
-Implementation details
-======================
+runnableExamples:
+  let x = "12"
+  assert fmt"{x=}" == "x=12"
+  assert fmt"{x =:}" == "x =12"
+  assert fmt"{x =}" == "x =12"
+  assert fmt"{x= :}" == "x= 12"
+  assert fmt"{x= }" == "x= 12"
+  assert fmt"{x = :}" == "x = 12"
+  assert fmt"{x = }" == "x = 12"
+  assert fmt"{x   =  :}" == "x   =  12"
+  assert fmt"{x   =  }" == "x   =  12"
 
-An expression like ``&"{key} is {value:arg} {{z}}"`` is transformed into:
+##[
+# Implementation details
 
-.. code-block:: nim
+An expression like `&"{key} is {value:arg} {{z}}"` is transformed into:
+
+  ```nim
   var temp = newStringOfCap(educatedCapGuess)
-  format(key, temp)
-  format(" is ", temp)
-  format(value, arg, temp)
-  format(" {z}", temp)
+  temp.formatValue(key, "")
+  temp.add(" is ")
+  temp.formatValue(value, arg)
+  temp.add(" {z}")
   temp
+  ```
 
 Parts of the string that are enclosed in the curly braces are interpreted
-as Nim code, to escape an ``{`` or ``}`` double it.
+as Nim code. To escape a `{` or `}`, double it.
 
-``&`` delegates most of the work to an open overloaded set
-of ``format`` procs. The required signature for a type ``T`` that supports
-formatting is usually ``proc format(x: T; result: var string)`` for efficiency
-but can also be ``proc format(x: T): string``. ``add`` and ``$`` procs are
-used as the fallback implementation.
+Within a curly expression, however, `{`, `}`, must be escaped with a backslash.
 
-This is the concrete lookup algorithm that ``&`` uses:
+To enable evaluating Nim expressions within curlies, colons inside parentheses
+do not need to be escaped.
+]##
 
-.. code-block:: nim
-
-  when compiles(format(arg, res)):
-    format(arg, res)
-  elif compiles(format(arg)):
-    res.add format(arg)
-  elif compiles(add(res, arg)):
-    res.add(arg)
-  else:
-    res.add($arg)
+runnableExamples:
+  let x = "hello"
+  assert fmt"""{ "\{(" & x & ")\}" }""" == "{(hello)}"
+  assert fmt"""{{({ x })}}""" == "{(hello)}"
+  assert fmt"""{ $(\{x:1,"world":2\}) }""" == """[("hello", 1), ("world", 2)]"""
 
+##[
+`&` delegates most of the work to an open overloaded set
+of `formatValue` procs. The required signature for a type `T` that supports
+formatting is usually `proc formatValue(result: var string; x: T; specifier: string)`.
 
 The subexpression after the colon
-(``arg`` in ``&"{key} is {value:arg} {{z}}"``) is an optional argument
-passed to ``format``.
-
-If an optional argument is present the following lookup algorithm is used:
-
-.. code-block:: nim
-
-  when compiles(format(arg, option, res)):
-    format(arg, option, res)
-  else:
-    res.add format(arg, option)
-
+(`arg` in `&"{key} is {value:arg} {{z}}"`) is optional. It will be passed as
+the last argument to `formatValue`. When the colon with the subexpression it is
+left out, an empty string will be taken instead.
 
 For strings and numeric types the optional argument is a so-called
 "standard format specifier".
 
+# Standard format specifiers for strings, integers and floats
 
-Standard format specifier for strings, integers and floats
-==========================================================
-
+The general form of a standard format specifier is:
 
-The general form of a standard format specifier is::
+    [[fill]align][sign][#][0][minimumwidth][.precision][type]
 
-  [[fill]align][sign][#][0][minimumwidth][.precision][type]
+The square brackets `[]` indicate an optional element.
 
-The square brackets ``[]`` indicate an optional element.
+The optional `align` flag can be one of the following:
 
-The optional align flag can be one of the following:
-
-'<'
-    Forces the field to be left-aligned within the available
+`<`
+:   Forces the field to be left-aligned within the available
     space. (This is the default for strings.)
 
-'>'
-    Forces the field to be right-aligned within the available space.
+`>`
+:   Forces the field to be right-aligned within the available space.
     (This is the default for numbers.)
 
-'^'
-    Forces the field to be centered within the available space.
+`^`
+:   Forces the field to be centered within the available space.
 
 Note that unless a minimum field width is defined, the field width
 will always be the same size as the data to fill it, so that the alignment
 option has no meaning in this case.
 
-The optional 'fill' character defines the character to be used to pad
+The optional `fill` character defines the character to be used to pad
 the field to the minimum width. The fill character, if present, must be
 followed by an alignment flag.
 
-The 'sign' option is only valid for numeric types, and can be one of the following:
+The `sign` option is only valid for numeric types, and can be one of the following:
 
 =================        ====================================================
   Sign                   Meaning
 =================        ====================================================
-``+``                    Indicates that a sign should be used for both
+`+`                      Indicates that a sign should be used for both
                          positive as well as negative numbers.
-``-``                    Indicates that a sign should be used only for
+`-`                      Indicates that a sign should be used only for
                          negative numbers (this is the default behavior).
 (space)                  Indicates that a leading space should be used on
                          positive numbers.
 =================        ====================================================
 
-If the '#' character is present, integers use the 'alternate form' for formatting.
-This means that binary, octal, and hexadecimal output will be prefixed
-with '0b', '0o', and '0x', respectively.
+If the `#` character is present, integers use the 'alternate form' for formatting.
+This means that binary, octal and hexadecimal output will be prefixed
+with `0b`, `0o` and `0x`, respectively.
 
-'width' is a decimal integer defining the minimum field width. If not specified,
+`width` is a decimal integer defining the minimum field width. If not specified,
 then the field width will be determined by the content.
 
-If the width field is preceded by a zero ('0') character, this enables
+If the width field is preceded by a zero (`0`) character, this enables
 zero-padding.
 
-The 'precision' is a decimal number indicating how many digits should be displayed
+The `precision` is a decimal number indicating how many digits should be displayed
 after the decimal point in a floating point conversion. For non-numeric types the
 field indicates the maximum field size - in other words, how many characters will
 be used from the field content. The precision is ignored for integer conversions.
 
-Finally, the 'type' determines how the data should be presented.
+Finally, the `type` determines how the data should be presented.
 
 The available integer presentation types are:
 
-
 =================        ====================================================
   Type                   Result
 =================        ====================================================
-``b``                    Binary. Outputs the number in base 2.
-``d``                    Decimal Integer. Outputs the number in base 10.
-``o``                    Octal format. Outputs the number in base 8.
-``x``                    Hex format. Outputs the number in base 16, using
+`b`                      Binary. Outputs the number in base 2.
+`d`                      Decimal Integer. Outputs the number in base 10.
+`o`                      Octal format. Outputs the number in base 8.
+`x`                      Hex format. Outputs the number in base 16, using
                          lower-case letters for the digits above 9.
-``X``                    Hex format. Outputs the number in base 16, using
+`X`                      Hex format. Outputs the number in base 16, using
                          uppercase letters for the digits above 9.
-(None)                   the same as 'd'
+(None)                   The same as `d`.
 =================        ====================================================
 
-
 The available floating point presentation types are:
 
 =================        ====================================================
   Type                   Result
 =================        ====================================================
-``e``                    Exponent notation. Prints the number in scientific
-                         notation using the letter 'e' to indicate the
+`e`                      Exponent notation. Prints the number in scientific
+                         notation using the letter `e` to indicate the
                          exponent.
-``E``                    Exponent notation. Same as 'e' except it converts
+`E`                      Exponent notation. Same as `e` except it converts
                          the number to uppercase.
-``f``                    Fixed point. Displays the number as a fixed-point
+`f`                      Fixed point. Displays the number as a fixed-point
                          number.
-``F``                    Fixed point. Same as 'f' except it converts the
+`F`                      Fixed point. Same as `f` except it converts the
                          number to uppercase.
-``g``                    General format. This prints the number as a
+`g`                      General format. This prints the number as a
                          fixed-point number, unless the number is too
-                         large, in which case it switches to 'e'
+                         large, in which case it switches to `e`
                          exponent notation.
-``G``                    General format. Same as 'g' except switches to 'E'
+`G`                      General format. Same as `g` except it switches to `E`
                          if the number gets to large.
-(None)                   similar to 'g', except that it prints at least one
+`i`                      Complex General format. This is only supported for
+                         complex numbers, which it prints using the mathematical
+                         (RE+IMj) format. The real and imaginary parts are printed
+                         using the general format `g` by default, but it is
+                         possible to combine this format with one of the other
+                         formats (e.g `jf`).
+(None)                   Similar to `g`, except that it prints at least one
                          digit after the decimal point.
 =================        ====================================================
 
+# Limitations
 
-Future directions
-=================
+Because of the well defined order how templates and macros are
+expanded, strformat cannot expand template arguments:
 
-A curly expression with commas in it like ``{x, argA, argB}`` could be
-transformed to ``format(x, argA, argB, res)`` in order to support
-formatters that do not need to parse a custom language within a custom
-language but instead prefer to use Nim's existing syntax. This also
-helps in readability since there is only so much you can cram into
-single letter DSLs.
+  ```nim
+  template myTemplate(arg: untyped): untyped =
+    echo "arg is: ", arg
+    echo &"--- {arg} ---"
 
-]##
+  let x = "abc"
+  myTemplate(x)
+  ```
 
-import macros, parseutils, unicode
-import strutils
-
-template callFormat(res, arg) {.dirty.} =
-  when arg is string:
-    # workaround in order to circumvent 'strutils.format' which matches
-    # too but doesn't adhere to our protocol.
-    res.add arg
-  elif compiles(format(arg, res)):
-    format(arg, res)
-  elif compiles(format(arg)):
-    res.add format(arg)
-  elif compiles(add(res, arg)):
-    res.add(arg)
-  else:
-    res.add($arg)
+First the template `myTemplate` is expanded, where every identifier
+`arg` is substituted with its argument. The `arg` inside the
+format string is not seen by this process, because it is part of a
+quoted string literal. It is not an identifier yet. Then the strformat
+macro creates the `arg` identifier from the string literal, an
+identifier that cannot be resolved anymore.
 
-template callFormatOption(res, arg, option) {.dirty.} =
-  when compiles(format(arg, option, res)):
-    format(arg, option, res)
-  elif compiles(format(arg, option)):
-    res.add format(arg, option)
-  else:
-    format($arg, option, res)
+The workaround for this is to bind the template argument to a new local variable.
 
-macro `&`*(pattern: string): untyped =
-  ## For a specification of the ``&`` macro, see the module level documentation.
-  if pattern.kind notin {nnkStrLit..nnkTripleStrLit}:
-    error "string formatting (fmt(), &) only works with string literals", pattern
-  let f = pattern.strVal
-  var i = 0
-  let res = genSym(nskVar, "fmtRes")
-  result = newNimNode(nnkStmtListExpr, lineInfoFrom=pattern)
-  result.add newVarStmt(res, newCall(bindSym"newStringOfCap", newLit(f.len + count(f, '{')*10)))
-  var strlit = ""
-  while i < f.len:
-    if f[i] == '{':
-      inc i
-      if f[i] == '{':
-        inc i
-        strlit.add '{'
-      else:
-        if strlit.len > 0:
-          result.add newCall(bindSym"add", res, newLit(strlit))
-          strlit = ""
+  ```nim
+  template myTemplate(arg: untyped): untyped =
+    block:
+      let arg1 {.inject.} = arg
+      echo "arg is: ", arg1
+      echo &"--- {arg1} ---"
+  ```
 
-        var subexpr = ""
-        while i < f.len and f[i] != '}' and f[i] != ':':
-          subexpr.add f[i]
-          inc i
-        let x = parseExpr(subexpr)
+The use of `{.inject.}` here is necessary again because of template
+expansion order and hygienic templates. But since we generally want to
+keep the hygiene of `myTemplate`, and we do not want `arg1`
+to be injected into the context where `myTemplate` is expanded,
+everything is wrapped in a `block`.
 
-        if f[i] == ':':
-          inc i
-          var options = ""
-          while i < f.len and f[i] != '}':
-            options.add f[i]
-            inc i
-          result.add getAst(callFormatOption(res, x, newLit(options)))
-        else:
-          result.add getAst(callFormat(res, x))
-        if f[i] == '}':
-          inc i
-        else:
-          doAssert false, "invalid format string: missing '}'"
-    elif f[i] == '}':
-      if f[i+1] == '}':
-        strlit.add '}'
-        inc i, 2
-      else:
-        doAssert false, "invalid format string: '}' instead of '}}'"
-        inc i
-    else:
-      strlit.add f[i]
-      inc i
-  if strlit.len > 0:
-    result.add newCall(bindSym"add", res, newLit(strlit))
-  result.add res
-  when defined(debugFmtDsl):
-    echo repr result
 
-template fmt*(pattern: string): untyped =
-  ## An alias for ``&``.
-  bind `&`
-  &pattern
+# Future directions
+
+A curly expression with commas in it like `{x, argA, argB}` could be
+transformed to `formatValue(result, x, argA, argB)` in order to support
+formatters that do not need to parse a custom language within a custom
+language but instead prefer to use Nim's existing syntax. This would also
+help with readability, since there is only so much you can cram into
+single letter DSLs.
+]##
+
+import std/[macros, parseutils, unicode]
+import std/strutils except format
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 
 proc mkDigit(v: int, typ: char): string {.inline.} =
   assert(v < 26)
@@ -344,8 +337,8 @@ proc mkDigit(v: int, typ: char): string {.inline.} =
     result = $chr(ord(if typ == 'x': 'a' else: 'A') + v - 10)
 
 proc alignString*(s: string, minimumWidth: int; align = '\0'; fill = ' '): string =
-  ## Aligns ``s`` using ``fill`` char.
-  ## This is only of interest if you want to write a custom ``format`` proc that
+  ## Aligns `s` using the `fill` char.
+  ## This is only of interest if you want to write a custom `format` proc that
   ## should support the standard format specifiers.
   if minimumWidth == 0:
     result = s
@@ -364,30 +357,33 @@ proc alignString*(s: string, minimumWidth: int; align = '\0'; fill = ' '): strin
 
 type
   StandardFormatSpecifier* = object ## Type that describes "standard format specifiers".
-    fill*, align*: char             ## Desired fill and alignment.
-    sign*: char                     ## Desired sign.
-    alternateForm*: bool            ## Whether to prefix binary, octal and hex numbers
-                                    ## with ``0b``, ``0o``, ``0x``.
-    padWithZero*: bool              ## Whether to pad with zeros rather than spaces.
-    minimumWidth*, precision*: int  ## Desired minium width and precision.
-    typ*: char                      ## Type like 'f', 'g' or 'd'.
-    endPosition*: int ## End position in the format specifier after
-                      ## ``parseStandardFormatSpecifier`` returned.
+    fill*, align*: char            ## Desired fill and alignment.
+    sign*: char                    ## Desired sign.
+    alternateForm*: bool           ## Whether to prefix binary, octal and hex numbers
+                                   ## with `0b`, `0o`, `0x`.
+    padWithZero*: bool             ## Whether to pad with zeros rather than spaces.
+    minimumWidth*, precision*: int ## Desired minimum width and precision.
+    typ*: char                     ## Type like 'f', 'g' or 'd'.
+    endPosition*: int              ## End position in the format specifier after
+                                   ## `parseStandardFormatSpecifier` returned.
 
 proc formatInt(n: SomeNumber; radix: int; spec: StandardFormatSpecifier): string =
-  ## Converts ``n`` to string. If ``n`` is `SomeFloat`, it casts to `int64`.
-  ## Conversion is done using ``radix``. If result's length is lesser than
-  ## ``minimumWidth``, it aligns result to the right or left (depending on ``a``)
-  ## with ``fill`` char.
+  ## Converts `n` to a string. If `n` is `SomeFloat`, it casts to `int64`.
+  ## Conversion is done using `radix`. If result's length is less than
+  ## `minimumWidth`, it aligns result to the right or left (depending on `a`)
+  ## with the `fill` char.
   when n is SomeUnsignedInt:
     var v = n.uint64
     let negative = false
   else:
-    var v = n.int64
-    let negative = v.int64 < 0
-    if negative:
-      # FIXME: overflow error for low(int64)
-      v = v * -1
+    let n = n.int64
+    let negative = n < 0
+    var v =
+      if negative:
+        # `uint64(-n)`, but accounts for `n == low(int64)`
+        uint64(not n) + 1
+      else:
+        uint64(n)
 
   var xx = ""
   if spec.alternateForm:
@@ -402,9 +398,9 @@ proc formatInt(n: SomeNumber; radix: int; spec: StandardFormatSpecifier): string
     result = "0"
   else:
     result = ""
-    while v > type(v)(0):
-      let d = v mod type(v)(radix)
-      v = v div type(v)(radix)
+    while v > typeof(v)(0):
+      let d = v mod typeof(v)(radix)
+      v = v div typeof(v)(radix)
       result.add(mkDigit(d.int, spec.typ))
     for idx in 0..<(result.len div 2):
       swap result[idx], result[result.len - idx - 1]
@@ -436,13 +432,13 @@ proc formatInt(n: SomeNumber; radix: int; spec: StandardFormatSpecifier): string
 proc parseStandardFormatSpecifier*(s: string; start = 0;
                                    ignoreUnknownSuffix = false): StandardFormatSpecifier =
   ## An exported helper proc that parses the "standard format specifiers",
-  ## as specified by the grammar::
+  ## as specified by the grammar:
   ##
-  ##   [[fill]align][sign][#][0][minimumwidth][.precision][type]
+  ##     [[fill]align][sign][#][0][minimumwidth][.precision][type]
   ##
-  ## This is only of interest if you want to write a custom ``format`` proc that
-  ## should support the standard format specifiers. If ``ignoreUnknownSuffix`` is true,
-  ## an unknown suffix after the ``type`` field is not an error.
+  ## This is only of interest if you want to write a custom `format` proc that
+  ## should support the standard format specifiers. If `ignoreUnknownSuffix` is true,
+  ## an unknown suffix after the `type` field is not an error.
   const alignChars = {'<', '>', '^'}
   result.fill = ' '
   result.align = '\0'
@@ -464,7 +460,7 @@ proc parseStandardFormatSpecifier*(s: string; start = 0;
     result.alternateForm = true
     inc i
 
-  if i+1 < s.len and s[i] == '0' and s[i+1] in {'0'..'9'}:
+  if i + 1 < s.len and s[i] == '0' and s[i+1] in {'0'..'9'}:
     result.padWithZero = true
     inc i
 
@@ -485,50 +481,54 @@ proc parseStandardFormatSpecifier*(s: string; start = 0;
     raise newException(ValueError,
       "invalid format string, cannot parse: " & s[i..^1])
 
-
-proc format*(value: SomeInteger; specifier: string; res: var string) =
-  ## Standard format implementation for ``SomeInteger``. It makes little
-  ## sense to call this directly, but it is required to exist
-  ## by the ``&`` macro.
-  let spec = parseStandardFormatSpecifier(specifier)
-  var radix = 10
-  case spec.typ
-  of 'x', 'X': radix = 16
-  of 'd', '\0': discard
-  of 'b': radix = 2
-  of 'o': radix = 8
+proc toRadix(typ: char): int =
+  case typ
+  of 'x', 'X': 16
+  of 'd', '\0': 10
+  of 'o': 8
+  of 'b': 2
   else:
     raise newException(ValueError,
       "invalid type in format string for number, expected one " &
-      " of 'x', 'X', 'b', 'd', 'o' but got: " & spec.typ)
-  res.add formatInt(value, radix, spec)
+      " of 'x', 'X', 'b', 'd', 'o' but got: " & typ)
 
-proc format*(value: SomeFloat; specifier: string; res: var string) =
-  ## Standard format implementation for ``SomeFloat``. It makes little
+proc formatValue*[T: SomeInteger](result: var string; value: T;
+                                  specifier: static string) =
+  ## Standard format implementation for `SomeInteger`. It makes little
   ## sense to call this directly, but it is required to exist
-  ## by the ``&`` macro.
-  let spec = parseStandardFormatSpecifier(specifier)
+  ## by the `&` macro.
+  when specifier.len == 0:
+    result.add $value
+  else:
+    const
+      spec = parseStandardFormatSpecifier(specifier)
+      radix = toRadix(spec.typ)
+
+    result.add formatInt(value, radix, spec)
 
-  var fmode = ffDefault
-  case spec.typ
-  of 'e', 'E':
-    fmode = ffScientific
-  of 'f', 'F':
-    fmode = ffDecimal
-  of 'g', 'G':
-    fmode = ffDefault
-  of '\0': discard
+proc formatValue*[T: SomeInteger](result: var string; value: T;
+                                  specifier: string) =
+  ## Standard format implementation for `SomeInteger`. It makes little
+  ## sense to call this directly, but it is required to exist
+  ## by the `&` macro.
+  if specifier.len == 0:
+    result.add $value
   else:
-    raise newException(ValueError,
-      "invalid type in format string for number, expected one " &
-      " of 'e', 'E', 'f', 'F', 'g', 'G' but got: " & spec.typ)
+    let
+      spec = parseStandardFormatSpecifier(specifier)
+      radix = toRadix(spec.typ)
 
+    result.add formatInt(value, radix, spec)
+
+proc formatFloat(
+    result: var string, value: SomeFloat, fmode: FloatFormatMode,
+    spec: StandardFormatSpecifier) =
   var f = formatBiggestFloat(value, fmode, spec.precision)
   var sign = false
   if value >= 0.0:
     if spec.sign != '-':
       sign = true
-      if  value == 0.0:
+      if value == 0.0:
         if 1.0 / value == Inf:
           # only insert the sign if value != negZero
           f.insert($spec.sign, 0)
@@ -538,171 +538,253 @@ proc format*(value: SomeFloat; specifier: string; res: var string) =
     sign = true
 
   if spec.padWithZero:
-    var sign_str = ""
+    var signStr = ""
     if sign:
-      sign_str = $f[0]
+      signStr = $f[0]
       f = f[1..^1]
 
     let toFill = spec.minimumWidth - f.len - ord(sign)
     if toFill > 0:
       f = repeat('0', toFill) & f
     if sign:
-      f = sign_str & f
+      f = signStr & f
 
   # the default for numbers is right-alignment:
   let align = if spec.align == '\0': '>' else: spec.align
-  let result = alignString(f, spec.minimumWidth,
-                           align, spec.fill)
+  let res = alignString(f, spec.minimumWidth, align, spec.fill)
   if spec.typ in {'A'..'Z'}:
-    res.add toUpperAscii(result)
+    result.add toUpperAscii(res)
+  else:
+    result.add res
+
+proc toFloatFormatMode(typ: char): FloatFormatMode =
+  case typ
+  of 'e', 'E': ffScientific
+  of 'f', 'F': ffDecimal
+  of 'g', 'G': ffDefault
+  of '\0': ffDefault
   else:
-    res.add result
+    raise newException(ValueError,
+      "invalid type in format string for number, expected one " &
+      " of 'e', 'E', 'f', 'F', 'g', 'G' but got: " & typ)
 
-proc format*(value: string; specifier: string; res: var string) =
-  ## Standard format implementation for ``string``. It makes little
+proc formatValue*(result: var string; value: SomeFloat; specifier: static string) =
+  ## Standard format implementation for `SomeFloat`. It makes little
   ## sense to call this directly, but it is required to exist
-  ## by the ``&`` macro.
-  let spec = parseStandardFormatSpecifier(specifier)
-  var value = value
-  case spec.typ
-  of 's', '\0': discard
+  ## by the `&` macro.
+  when specifier.len == 0:
+    result.add $value
   else:
-    raise newException(ValueError,
-      "invalid type in format string for string, expected 's', but got " &
-      spec.typ)
+    const
+      spec = parseStandardFormatSpecifier(specifier)
+      fmode = toFloatFormatMode(spec.typ)
+
+    formatFloat(result, value, fmode, spec)
+
+proc formatValue*(result: var string; value: SomeFloat; specifier: string) =
+  ## Standard format implementation for `SomeFloat`. It makes little
+  ## sense to call this directly, but it is required to exist
+  ## by the `&` macro.
+  if specifier.len == 0:
+    result.add $value
+  else:
+    let
+      spec = parseStandardFormatSpecifier(specifier)
+      fmode = toFloatFormatMode(spec.typ)
+
+    formatFloat(result, value, fmode, spec)
+
+proc formatValue*(result: var string; value: string; specifier: static string) =
+  ## Standard format implementation for `string`. It makes little
+  ## sense to call this directly, but it is required to exist
+  ## by the `&` macro.
+  const spec = parseStandardFormatSpecifier(specifier)
+  var value =
+    when spec.typ in {'s', '\0'}: value
+    else: static:
+      raise newException(ValueError,
+        "invalid type in format string for string, expected 's', but got " &
+        spec.typ)
+  when spec.precision != -1:
+    if spec.precision < runeLen(value):
+      const precision = cast[Natural](spec.precision)
+      setLen(value, Natural(runeOffset(value, precision)))
+
+  result.add alignString(value, spec.minimumWidth, spec.align, spec.fill)
+
+proc formatValue*(result: var string; value: string; specifier: string) =
+  ## Standard format implementation for `string`. It makes little
+  ## sense to call this directly, but it is required to exist
+  ## by the `&` macro.
+  let spec = parseStandardFormatSpecifier(specifier)
+  var value =
+    if spec.typ in {'s', '\0'}: value
+    else:
+      raise newException(ValueError,
+        "invalid type in format string for string, expected 's', but got " &
+        spec.typ)
   if spec.precision != -1:
-    if spec.precision < runelen(value):
-      setLen(value, runeOffset(value, spec.precision))
-  res.add alignString(value, spec.minimumWidth, spec.align, spec.fill)
-
-when isMainModule:
-  template check(actual, expected: string) =
-    doAssert actual == expected
-
-  from strutils import toUpperAscii, repeat
-
-  # Basic tests
-  let s = "string"
-  check &"{0} {s}", "0 string"
-  check &"{s[0..2].toUpperAscii}", "STR"
-  check &"{-10:04}", "-010"
-  check &"{-10:<04}", "-010"
-  check &"{-10:>04}", "-010"
-  check &"0x{10:02X}", "0x0A"
-
-  check &"{10:#04X}", "0x0A"
-
-  check &"""{"test":#>5}""", "#test"
-  check &"""{"test":>5}""", " test"
-
-  check &"""{"test":#^7}""", "#test##"
-
-  check &"""{"test": <5}""", "test "
-  check &"""{"test":<5}""", "test "
-  check &"{1f:.3f}", "1.000"
-  check &"Hello, {s}!", "Hello, string!"
-
-  # Tests for identifers without parenthesis
-  check &"{s} works{s}", "string worksstring"
-  check &"{s:>7}", " string"
-  doAssert(not compiles(&"{s_works}")) # parsed as identifier `s_works`
-
-  # Misc general tests
-  check &"{{}}", "{}"
-  check &"{0}%", "0%"
-  check &"{0}%asdf", "0%asdf"
-  check &("\n{\"\\n\"}\n"), "\n\n\n"
-  check &"""{"abc"}s""", "abcs"
-
-  # String tests
-  check &"""{"abc"}""", "abc"
-  check &"""{"abc":>4}""", " abc"
-  check &"""{"abc":<4}""", "abc "
-  check &"""{"":>4}""", "    "
-  check &"""{"":<4}""", "    "
-
-  # Int tests
-  check &"{12345}", "12345"
-  check &"{ - 12345}", "-12345"
-  check &"{12345:6}", " 12345"
-  check &"{12345:>6}", " 12345"
-  check &"{12345:4}", "12345"
-  check &"{12345:08}", "00012345"
-  check &"{-12345:08}", "-0012345"
-  check &"{0:0}", "0"
-  check &"{0:02}", "00"
-  check &"{-1:3}", " -1"
-  check &"{-1:03}", "-01"
-  check &"{10}", "10"
-  check &"{16:#X}", "0x10"
-  check &"{16:^#7X}", " 0x10  "
-  check &"{16:^+#7X}", " +0x10 "
-
-  # Hex tests
-  check &"{0:x}", "0"
-  check &"{-0:x}", "0"
-  check &"{255:x}", "ff"
-  check &"{255:X}", "FF"
-  check &"{-255:x}", "-ff"
-  check &"{-255:X}", "-FF"
-  check &"{255:x} uNaffeCteD CaSe", "ff uNaffeCteD CaSe"
-  check &"{255:X} uNaffeCteD CaSe", "FF uNaffeCteD CaSe"
-  check &"{255:4x}", "  ff"
-  check &"{255:04x}", "00ff"
-  check &"{-255:4x}", " -ff"
-  check &"{-255:04x}", "-0ff"
-
-  # Float tests
-  check &"{123.456}", "123.456"
-  check &"{-123.456}", "-123.456"
-  check &"{123.456:.3f}", "123.456"
-  check &"{123.456:+.3f}", "+123.456"
-  check &"{-123.456:+.3f}", "-123.456"
-  check &"{-123.456:.3f}", "-123.456"
-  check &"{123.456:1g}", "123.456"
-  check &"{123.456:.1f}", "123.5"
-  check &"{123.456:.0f}", "123."
-  #check &"{123.456:.0f}", "123."
-  check &"{123.456:>9.3f}", "  123.456"
-  check &"{123.456:9.3f}", "  123.456"
-  check &"{123.456:>9.4f}", " 123.4560"
-  check &"{123.456:>9.0f}", "     123."
-  check &"{123.456:<9.4f}", "123.4560 "
-
-  # Float (scientific) tests
-  check &"{123.456:e}", "1.234560e+02"
-  check &"{123.456:>13e}", " 1.234560e+02"
-  check &"{123.456:<13e}", "1.234560e+02 "
-  check &"{123.456:.1e}", "1.2e+02"
-  check &"{123.456:.2e}", "1.23e+02"
-  check &"{123.456:.3e}", "1.235e+02"
-
-  # Note: times.format adheres to the format protocol. Test that this
-  # works:
-  import times
-
-  var nullTime: DateTime
-  check &"{nullTime:yyyy-mm-dd}", "0000-00-00"
-
-  # Unicode string tests
-  check &"""{"αβγ"}""", "αβγ"
-  check &"""{"αβγ":>5}""", "  αβγ"
-  check &"""{"αβγ":<5}""", "αβγ  "
-  check &"""a{"a"}α{"α"}€{"€"}𐍈{"𐍈"}""", "aaαα€€𐍈𐍈"
-  check &"""a{"a":2}α{"α":2}€{"€":2}𐍈{"𐍈":2}""", "aa αα €€ 𐍈𐍈 "
-  # Invalid unicode sequences should be handled as plain strings.
-  # Invalid examples taken from: https://stackoverflow.com/a/3886015/1804173
-  let invalidUtf8 = [
-    "\xc3\x28", "\xa0\xa1",
-    "\xe2\x28\xa1", "\xe2\x82\x28",
-    "\xf0\x28\x8c\xbc", "\xf0\x90\x28\xbc", "\xf0\x28\x8c\x28"
-  ]
-  for s in invalidUtf8:
-    check &"{s:>5}", repeat(" ", 5-s.len) & s
-
-
-  import json
-
-  doAssert fmt"{'a'} {'b'}" == "a b"
-
-  echo("All tests ok")
+    if spec.precision < runeLen(value):
+      let precision = cast[Natural](spec.precision)
+      setLen(value, Natural(runeOffset(value, precision)))
+
+  result.add alignString(value, spec.minimumWidth, spec.align, spec.fill)
+
+proc formatValue[T: not SomeInteger](result: var string; value: T; specifier: static string) =
+  mixin `$`
+  formatValue(result, $value, specifier)
+
+proc formatValue[T: not SomeInteger](result: var string; value: T; specifier: string) =
+  mixin `$`
+  formatValue(result, $value, specifier)
+
+template formatValue(result: var string; value: char; specifier: string) =
+  result.add value
+
+template formatValue(result: var string; value: cstring; specifier: string) =
+  result.add value
+
+proc strformatImpl(f: string; openChar, closeChar: char,
+                   lineInfoNode: NimNode = nil): NimNode =
+  template missingCloseChar =
+    error("invalid format string: missing closing character '" & closeChar & "'")
+
+  if openChar == ':' or closeChar == ':':
+    error "openChar and closeChar must not be ':'"
+  var i = 0
+  let res = genSym(nskVar, "fmtRes")
+  result = newNimNode(nnkStmtListExpr, lineInfoNode)
+  # XXX: https://github.com/nim-lang/Nim/issues/8405
+  # When compiling with -d:useNimRtl, certain procs such as `count` from the strutils
+  # module are not accessible at compile-time:
+  let expectedGrowth = when defined(useNimRtl): 0 else: count(f, openChar) * 10
+  result.add newVarStmt(res, newCall(bindSym"newStringOfCap",
+                                     newLit(f.len + expectedGrowth)))
+  var strlit = ""
+  while i < f.len:
+    if f[i] == openChar:
+      inc i
+      if f[i] == openChar:
+        inc i
+        strlit.add openChar
+      else:
+        if strlit.len > 0:
+          result.add newCall(bindSym"add", res, newLit(strlit))
+          strlit = ""
+
+        var subexpr = ""
+        var inParens = 0
+        var inSingleQuotes = false
+        var inDoubleQuotes = false
+        template notEscaped:bool = f[i-1]!='\\'
+        while i < f.len and f[i] != closeChar and (f[i] != ':' or inParens != 0):
+          case f[i]
+          of '\\':
+            if i < f.len-1 and f[i+1] in {openChar,closeChar,':'}: inc i
+          of '\'':
+            if not inDoubleQuotes and notEscaped: inSingleQuotes = not inSingleQuotes
+          of '\"':
+            if notEscaped: inDoubleQuotes = not inDoubleQuotes
+          of '(':
+            if not (inSingleQuotes or inDoubleQuotes): inc inParens
+          of ')':
+            if not (inSingleQuotes or inDoubleQuotes): dec inParens
+          of '=':
+            let start = i
+            inc i
+            i += f.skipWhitespace(i)
+            if i == f.len:
+              missingCloseChar
+            if f[i] == closeChar or f[i] == ':':
+              result.add newCall(bindSym"add", res, newLit(subexpr & f[start ..< i]))
+            else:
+              subexpr.add f[start ..< i]
+            continue
+          else: discard
+          subexpr.add f[i]
+          inc i
+
+        if i == f.len:
+          missingCloseChar
+
+        var x: NimNode
+        try:
+          x = parseExpr(subexpr)
+        except ValueError as e:
+          error("could not parse `$#` in `$#`.\n$#" % [subexpr, f, e.msg])
+        x.copyLineInfo(lineInfoNode)
+        let formatSym = bindSym("formatValue", brOpen)
+        var options = ""
+        if f[i] == ':':
+          inc i
+          while i < f.len and f[i] != closeChar:
+            options.add f[i]
+            inc i
+        if i == f.len:
+          missingCloseChar
+        if f[i] == closeChar:
+          inc i
+        result.add newCall(formatSym, res, x, newLit(options))
+    elif f[i] == closeChar:
+      if i<f.len-1 and f[i+1] == closeChar:
+        strlit.add closeChar
+        inc i, 2
+      else:
+        raiseAssert "invalid format string: '$1' instead of '$1$1'" % $closeChar
+    else:
+      strlit.add f[i]
+      inc i
+  if strlit.len > 0:
+    result.add newCall(bindSym"add", res, newLit(strlit))
+  result.add res
+  # workaround for #20381
+  var blockExpr = newNimNode(nnkBlockExpr, lineInfoNode)
+  blockExpr.add(newEmptyNode())
+  blockExpr.add(result)
+  result = blockExpr
+  when defined(debugFmtDsl):
+    echo repr result
+
+macro fmt(pattern: static string; openChar: static char, closeChar: static char, lineInfoNode: untyped): string =
+  ## version of `fmt` with dummy untyped param for line info
+  strformatImpl(pattern, openChar, closeChar, lineInfoNode)
+
+when not defined(nimHasCallsitePragma):
+  {.pragma: callsite.}
+
+template fmt*(pattern: static string; openChar: static char, closeChar: static char): string {.callsite.} =
+  ## Interpolates `pattern` using symbols in scope.
+  runnableExamples:
+    let x = 7
+    assert "var is {x * 2}".fmt == "var is 14"
+    assert "var is {{x}}".fmt == "var is {x}" # escape via doubling
+    const s = "foo: {x}"
+    assert s.fmt == "foo: 7" # also works with const strings
+
+    assert fmt"\n" == r"\n" # raw string literal
+    assert "\n".fmt == "\n" # regular literal (likewise with `fmt("\n")` or `fmt "\n"`)
+  runnableExamples:
+    # custom `openChar`, `closeChar`
+    let x = 7
+    assert "<x>".fmt('<', '>') == "7"
+    assert "<<<x>>>".fmt('<', '>') == "<7>"
+    assert "`x`".fmt('`', '`') == "7"
+  fmt(pattern, openChar, closeChar, dummyForLineInfo)
+
+template fmt*(pattern: static string): untyped {.callsite.} =
+  ## Alias for `fmt(pattern, '{', '}')`.
+  fmt(pattern, '{', '}', dummyForLineInfo)
+
+template `&`*(pattern: string{lit}): string {.callsite.} =
+  ## `&pattern` is the same as `pattern.fmt`.
+  ## For a specification of the `&` macro, see the module level documentation.
+  # pending bug #18275, bug #18278, use `pattern: static string`
+  # consider deprecating this, it's redundant with `fmt` and `fmt` is strictly
+  # more flexible, readable (no confusion with the binary `&`), self-documenting,
+  # not to mention #18275, bug #18278.
+  runnableExamples:
+    let x = 7
+    assert &"{x}\n" == "7\n" # regular string literal
+    assert &"{x}\n" == "{x}\n".fmt # `fmt` can be used instead
+    assert &"{x}\n" != fmt"{x}\n" # see `fmt` docs, this would use a raw string literal
+  fmt(pattern, '{', '}', dummyForLineInfo)
diff --git a/lib/pure/strmisc.nim b/lib/pure/strmisc.nim
index d1ff920c9..a3e539e7e 100644
--- a/lib/pure/strmisc.nim
+++ b/lib/pure/strmisc.nim
@@ -8,31 +8,36 @@
 #
 
 ## This module contains various string utility routines that are uncommonly
-## used in comparison to `strutils <strutils.html>`_.
+## used in comparison to the ones in `strutils <strutils.html>`_.
 
-import strutils
+import std/strutils
 
-{.deadCodeElim: on.}  # dce option deprecated
-
-proc expandTabs*(s: string, tabSize: int = 8): string {.noSideEffect,
-  procvar.} =
-  ## Expand tab characters in `s` by `tabSize` spaces
+func expandTabs*(s: string, tabSize: int = 8): string =
+  ## Expands tab characters in `s`, replacing them by spaces.
+  ##
+  ## The amount of inserted spaces for each tab character is the difference
+  ## between the current column number and the next tab position. Tab positions
+  ## occur every `tabSize` characters.
+  ## The column number starts at 0 and is increased with every single character
+  ## and inserted space, except for newline, which resets the column number
+  ## back to 0.
+  runnableExamples:
+    doAssert expandTabs("\t", 4) == "    "
+    doAssert expandTabs("\tfoo\t", 4) == "    foo "
+    doAssert expandTabs("a\tb\n\txy\t", 3) == "a  b\n   xy "
 
   result = newStringOfCap(s.len + s.len shr 2)
-  var pos = 0
 
   template addSpaces(n) =
-    for j in 0 ..< n:
+    for _ in 1..n:
       result.add(' ')
-      pos += 1
+    pos += n
 
-  for i in 0 ..< len(s):
-    let c = s[i]
+  var pos = 0
+  let denominator = if tabSize > 0: tabSize else: 1
+  for c in s:
     if c == '\t':
-      let
-        denominator = if tabSize > 0: tabSize else: 1
-        numSpaces = tabSize - pos mod denominator
-
+      let numSpaces = tabSize - pos mod denominator
       addSpaces(numSpaces)
     else:
       result.add(c)
@@ -40,44 +45,39 @@ proc expandTabs*(s: string, tabSize: int = 8): string {.noSideEffect,
     if c == '\l':
       pos = 0
 
-proc partition*(s: string, sep: string,
-                right: bool = false): (string, string, string)
-                {.noSideEffect, procvar.} =
-  ## Split the string at the first or last occurrence of `sep` into a 3-tuple
+func partition*(s: string, sep: string,
+                right: bool = false): (string, string, string) =
+  ## Splits the string at the first (if `right` is false)
+  ## or last (if `right` is true) occurrence of `sep` into a 3-tuple.
   ##
-  ## Returns a 3 string tuple of (beforeSep, `sep`, afterSep) or
-  ## (`s`, "", "") if `sep` is not found and `right` is false or
-  ## ("", "", `s`) if `sep` is not found and `right` is true
+  ## Returns a 3-tuple of strings, `(beforeSep, sep, afterSep)` or
+  ## `(s, "", "")` if `sep` is not found and `right` is false or
+  ## `("", "", s)` if `sep` is not found and `right` is true.
+  ##
+  ## **See also:**
+  ## * `rpartition proc <#rpartition,string,string>`_
+  runnableExamples:
+    doAssert partition("foo:bar:baz", ":") == ("foo", ":", "bar:baz")
+    doAssert partition("foo:bar:baz", ":", right = true) == ("foo:bar", ":", "baz")
+    doAssert partition("foobar", ":") == ("foobar", "", "")
+    doAssert partition("foobar", ":", right = true) == ("", "", "foobar")
+
   let position = if right: s.rfind(sep) else: s.find(sep)
   if position != -1:
     return (s[0 ..< position], sep, s[position + sep.len ..< s.len])
   return if right: ("", "", s) else: (s, "", "")
 
-proc rpartition*(s: string, sep: string): (string, string, string)
-                {.noSideEffect, procvar.} =
-  ## Split the string at the last occurrence of `sep` into a 3-tuple
+func rpartition*(s: string, sep: string): (string, string, string) =
+  ## Splits the string at the last occurrence of `sep` into a 3-tuple.
   ##
-  ## Returns a 3 string tuple of (beforeSep, `sep`, afterSep) or
-  ## ("", "", `s`) if `sep` is not found
-  return partition(s, sep, right = true)
-
-when isMainModule:
-  doAssert expandTabs("\t", 4) == "    "
-  doAssert expandTabs("\tfoo\t", 4) == "    foo "
-  doAssert expandTabs("\tfoo\tbar", 4) == "    foo bar"
-  doAssert expandTabs("\tfoo\tbar\t", 4) == "    foo bar "
-  doAssert expandTabs("", 4) == ""
-  doAssert expandTabs("", 0) == ""
-  doAssert expandTabs("\t\t\t", 0) == ""
-
-  doAssert partition("foo:bar", ":") == ("foo", ":", "bar")
-  doAssert partition("foobarbar", "bar") == ("foo", "bar", "bar")
-  doAssert partition("foobarbar", "bank") == ("foobarbar", "", "")
-  doAssert partition("foobarbar", "foo") == ("", "foo", "barbar")
-  doAssert partition("foofoobar", "bar") == ("foofoo", "bar", "")
+  ## Returns a 3-tuple of strings, `(beforeSep, sep, afterSep)` or
+  ## `("", "", s)` if `sep` is not found. This is the same as
+  ## `partition(s, sep, right = true)`.
+  ##
+  ## **See also:**
+  ## * `partition proc <#partition,string,string,bool>`_
+  runnableExamples:
+    doAssert rpartition("foo:bar:baz", ":") == ("foo:bar", ":", "baz")
+    doAssert rpartition("foobar", ":") == ("", "", "foobar")
 
-  doAssert rpartition("foo:bar", ":") == ("foo", ":", "bar")
-  doAssert rpartition("foobarbar", "bar") == ("foobar", "bar", "")
-  doAssert rpartition("foobarbar", "bank") == ("", "", "foobarbar")
-  doAssert rpartition("foobarbar", "foo") == ("", "foo", "barbar")
-  doAssert rpartition("foofoobar", "bar") == ("foofoo", "bar", "")
+  partition(s, sep, right = true)
diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim
index 11f182495..16ef9e642 100644
--- a/lib/pure/strscans.nim
+++ b/lib/pure/strscans.nim
@@ -10,9 +10,9 @@
 ##[
 This module contains a `scanf`:idx: macro that can be used for extracting
 substrings from an input string. This is often easier than regular expressions.
-Some examples as an apetizer:
+Some examples as an appetizer:
 
-.. code-block:: nim
+  ```nim
   # check if input string matches a triple of integers:
   const input = "(1,2,4)"
   var x, y, z: int
@@ -26,6 +26,7 @@ Some examples as an apetizer:
   var myfloat: float
   if scanf(input, "$i-$i-$i $w$s$f", year, month, day, identifier, myfloat):
     echo "yes, we have a match!"
+  ```
 
 As can be seen from the examples, strings are matched verbatim except for
 substrings starting with ``$``. These constructions are available:
@@ -35,8 +36,9 @@ substrings starting with ``$``. These constructions are available:
 ``$o``              Matches an octal integer. This uses ``parseutils.parseOct``.
 ``$i``              Matches a decimal integer. This uses ``parseutils.parseInt``.
 ``$h``              Matches a hex integer. This uses ``parseutils.parseHex``.
-``$f``              Matches a floating pointer number. Uses ``parseFloat``.
-``$w``              Matches an ASCII identifier: ``[A-Z-a-z_][A-Za-z_0-9]*``.
+``$f``              Matches a floating-point number. Uses ``parseFloat``.
+``$w``              Matches an ASCII identifier: ``[A-Za-z_][A-Za-z_0-9]*``.
+``$c``              Matches a single ASCII character.
 ``$s``              Skips optional whitespace.
 ``$$``              Matches a single dollar sign.
 ``$.``              Matches if the end of the input string has been reached.
@@ -51,7 +53,7 @@ substrings starting with ``$``. These constructions are available:
 =================   ========================================================
 
 Even though ``$*`` and ``$+`` look similar to the regular expressions ``.*``
-and ``.+`` they work quite differently, there is no non-deterministic
+and ``.+``, they work quite differently. There is no non-deterministic
 state machine involved and the matches are non-greedy. ``[$*]``
 matches ``[xyz]`` via ``parseutils.parseUntil``.
 
@@ -76,14 +78,13 @@ One very nice advantage over regular expressions is that ``scanf`` is
 extensible with ordinary Nim procs. The proc is either enclosed in ``${}``
 or in ``$[]``. ``${}`` matches and binds the result
 to a variable (that was passed to the ``scanf`` macro) while ``$[]`` merely
-optional tokens.
+matches optional tokens without any result binding.
 
 
 In this example, we define a helper proc ``someSep`` that skips some separators
 which we then use in our scanf pattern to help us in the matching process:
 
-.. code-block:: nim
-
+  ```nim
   proc someSep(input: string; start: int; seps: set[char] = {':','-','.'}): int =
     # Note: The parameters and return value must match to what ``scanf`` requires
     result = 0
@@ -91,11 +92,11 @@ which we then use in our scanf pattern to help us in the matching process:
 
   if scanf(input, "$w$[someSep]$w", key, value):
     ...
+  ```
 
-It also possible to pass arguments to a user definable matcher:
-
-.. code-block:: nim
+It is also possible to pass arguments to a user definable matcher:
 
+  ```nim
   proc ndigits(input: string; intVal: var int; start: int; n: int): int =
     # matches exactly ``n`` digits. Matchers need to return 0 if nothing
     # matched or otherwise the number of processed chars.
@@ -114,6 +115,7 @@ It also possible to pass arguments to a user definable matcher:
   var year, month, day: int
   if scanf("2013-01-03", "${ndigits(4)}-${ndigits(2)}-${ndigits(2)}$.", year, month, day):
     ...
+  ```
 
 
 The scanp macro
@@ -129,9 +131,9 @@ to use prefix instead of postfix operators.
 ``+E``           One or more
 ``?E``           Zero or One
 ``E{n,m}``       From ``n`` up to ``m`` times ``E``
-``~Ε``           Not predicate
+``~E``           Not predicate
 ``a ^* b``       Shortcut for ``?(a *(b a))``. Usually used for separators.
-``a ^* b``       Shortcut for ``?(a +(b a))``. Usually used for separators.
+``a ^+ b``       Shortcut for ``?(a +(b a))``. Usually used for separators.
 ``'a'``          Matches a single character
 ``{'a'..'b'}``   Matches a character set
 ``"s"``          Matches a string
@@ -144,8 +146,7 @@ not implemented.
 
 Simple example that parses the ``/etc/passwd`` file line by line:
 
-.. code-block:: nim
-
+  ```nim
   const
     etc_passwd = """root:x:0:0:root:/root:/bin/bash
   daemon:x:1:1:daemon:/usr/sbin:/bin/sh
@@ -164,17 +165,17 @@ Simple example that parses the ``/etc/passwd`` file line by line:
         result.add entry
       else:
         break
+  ```
 
 The ``scanp`` maps the grammar code into Nim code that performs the parsing.
 The parsing is performed with the help of 3 helper templates that that can be
 implemented for a custom type.
 
 These templates need to be named ``atom`` and ``nxt``. ``atom`` should be
-overloaded to handle both single characters and sets of character.
+overloaded to handle both `char` and `set[char]`.
 
-.. code-block:: nim
-
-  import streams
+  ```nim
+  import std/streams
 
   template atom(input: Stream; idx: int; c: char): bool =
     ## Used in scanp for the matching of atoms (usually chars).
@@ -189,11 +190,11 @@ overloaded to handle both single characters and sets of character.
 
   if scanp(content, idx, +( ~{'\L', '\0'} -> entry.add(peekChar($input))), '\L'):
     result.add entry
+  ```
 
 Calling ordinary Nim procs inside the macro is possible:
 
-.. code-block:: nim
-
+  ```nim
   proc digits(s: string; intVal: var int; start: int): int =
     var x = 0
     while result+start < s.len and s[result+start] in {'0'..'9'} and s[result+start] != ':':
@@ -219,12 +220,12 @@ Calling ordinary Nim procs inside the macro is possible:
           result.add login & " " & homedir
       else:
         break
+  ```
 
 When used for matching, keep in mind that likewise scanf, no backtracking
 is performed.
 
-.. code-block:: nim
-
+  ```nim
   proc skipUntil(s: string; until: string; unless = '\0'; start: int): int =
     # Skips all characters until the string `until` is found. Returns 0
     # if the char `unless` is found first or the end is reached.
@@ -255,12 +256,12 @@ is performed.
 
   for r in collectLinks(body):
     echo r
+  ```
 
 In this example both macros are combined seamlessly in order to maximise
 efficiency and perform different checks.
 
-.. code-block:: nim
-
+  ```nim
   iterator parseIps*(soup: string): string =
     ## ipv4 only!
     const digits = {'0'..'9'}
@@ -278,11 +279,16 @@ efficiency and perform different checks.
           yield buf
       buf.setLen(0) # need to clear `buf` each time, cause it might contain garbage
       idx.inc
-
+  ```
 ]##
 
 
-import macros, parseutils
+import std/[macros, parseutils]
+import std/private/since
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 
 proc conditionsToIfChain(n, idx, res: NimNode; start: int): NimNode =
   assert n.kind == nnkStmtList
@@ -308,7 +314,7 @@ proc buildUserCall(x: string; args: varargs[NimNode]): NimNode =
     for i in 1..<y.len: result.add y[i]
 
 macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): bool =
-  ## See top level documentation of his module of how ``scanf`` works.
+  ## See top level documentation of this module about how ``scanf`` works.
   template matchBind(parser) {.dirty.} =
     var resLen = genSym(nskLet, "resLen")
     conds.add newLetStmt(resLen, newCall(bindSym(parser), inp, results[i], idx))
@@ -317,15 +323,16 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
 
   template at(s: string; i: int): char = (if i < s.len: s[i] else: '\0')
   template matchError() =
-    error("type mismatch between pattern '$" & pattern[p] & "' (position: " & $p & ") and " & $getType(results[i]) &
-          " var '" & repr(results[i]) & "'")
+    error("type mismatch between pattern '$" & pattern[p] & "' (position: " & $p &
+      ") and " & $getTypeInst(results[i]) & " var '" & repr(results[i]) & "'")
 
   var i = 0
   var p = 0
   var idx = genSym(nskVar, "idx")
   var res = genSym(nskVar, "res")
   let inp = genSym(nskLet, "inp")
-  result = newTree(nnkStmtListExpr, newLetStmt(inp, input), newVarStmt(idx, newLit 0), newVarStmt(res, newLit false))
+  result = newTree(nnkStmtListExpr, newLetStmt(inp, input),
+                   newVarStmt(idx, newLit 0), newVarStmt(res, newLit false))
   var conds = newTree(nnkStmtList)
   var fullMatch = false
   while p < pattern.len:
@@ -334,7 +341,8 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
       case pattern[p]
       of '$':
         var resLen = genSym(nskLet, "resLen")
-        conds.add newLetStmt(resLen, newCall(bindSym"skip", inp, newLit($pattern[p]), idx))
+        conds.add newLetStmt(resLen, newCall(bindSym"skip", inp,
+                                             newLit($pattern[p]), idx))
         conds.add resLen.notZero
         conds.add resLen
       of 'w':
@@ -343,6 +351,12 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
         else:
           matchError
         inc i
+      of 'c':
+        if i < results.len and getType(results[i]).typeKind == ntyChar:
+          matchBind "parseChar"
+        else:
+          matchError
+        inc i
       of 'b':
         if i < results.len and getType(results[i]).typeKind == ntyInt:
           matchBind "parseBin"
@@ -374,7 +388,8 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
           matchError
         inc i
       of 's':
-        conds.add newCall(bindSym"inc", idx, newCall(bindSym"skipWhitespace", inp, idx))
+        conds.add newCall(bindSym"inc", idx,
+                          newCall(bindSym"skipWhitespace", inp, idx))
         conds.add newEmptyNode()
         conds.add newEmptyNode()
       of '.':
@@ -385,14 +400,15 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
       of '*', '+':
         if i < results.len and getType(results[i]).typeKind == ntyString:
           var min = ord(pattern[p] == '+')
-          var q=p+1
+          var q = p+1
           var token = ""
           while q < pattern.len and pattern[q] != '$':
             token.add pattern[q]
             inc q
           var resLen = genSym(nskLet, "resLen")
-          conds.add newLetStmt(resLen, newCall(bindSym"parseUntil", inp, results[i], newLit(token), idx))
-          conds.add newCall(bindSym"!=", resLen, newLit min)
+          conds.add newLetStmt(resLen, newCall(bindSym"parseUntil", inp,
+              results[i], newLit(token), idx))
+          conds.add newCall(bindSym">=", resLen, newLit min)
           conds.add resLen
         else:
           matchError
@@ -454,12 +470,61 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
   else:
     result.add res
 
+macro scanTuple*(input: untyped; pattern: static[string]; matcherTypes: varargs[untyped]): untyped {.since: (1, 5).}=
+  ## Works identically as scanf, but instead of predeclaring variables it returns a tuple.
+  ## Tuple is started with a bool which indicates if the scan was successful
+  ## followed by the requested data.
+  ## If using a user defined matcher, provide the types in order they appear after pattern:
+  ## `line.scanTuple("${yourMatcher()}", int)`
+  runnableExamples:
+    let (success, year, month, day, time) = scanTuple("1000-01-01 00:00:00", "$i-$i-$i$s$+")
+    if success:
+      assert year == 1000
+      assert month == 1
+      assert day == 1
+      assert time == "00:00:00"
+  var
+    p = 0
+    userMatches = 0
+    arguments: seq[NimNode]
+  result = newStmtList()
+  template addVar(typ: string) =
+    let varIdent = ident("temp" & $arguments.len)
+    result.add(newNimNode(nnkVarSection).add(newIdentDefs(varIdent, ident(typ), newEmptyNode())))
+    arguments.add(varIdent)
+  while p < pattern.len:
+    if pattern[p] == '$':
+      inc p
+      case pattern[p]
+      of 'w', '*', '+':
+        addVar("string")
+      of 'c':
+        addVar("char")
+      of 'b', 'o', 'i', 'h':
+        addVar("int")
+      of 'f':
+        addVar("float")
+      of '{':
+        if userMatches < matcherTypes.len:
+          let varIdent = ident("temp" & $arguments.len)
+          result.add(newNimNode(nnkVarSection).add(newIdentDefs(varIdent, matcherTypes[userMatches], newEmptyNode())))
+          arguments.add(varIdent)
+          inc userMatches
+      else: discard
+    inc p
+  result.add nnkTupleConstr.newTree(newCall(ident("scanf"), input, newStrLitNode(pattern)))
+  for arg in arguments:
+    result[^1][0].add arg
+    result[^1].add arg
+  result = newBlockStmt(result)
+
 template atom*(input: string; idx: int; c: char): bool =
   ## Used in scanp for the matching of atoms (usually chars).
-  idx < input.len and input[idx] == c
+  ## EOF is matched as ``'\0'``.
+  (idx < input.len and input[idx] == c) or (idx == input.len and c == '\0')
 
 template atom*(input: string; idx: int; s: set[char]): bool =
-  idx < input.len and input[idx] in s
+  (idx < input.len and input[idx] in s) or (idx == input.len and '\0' in s)
 
 template hasNxt*(input: string; idx: int): bool = idx < input.len
 
@@ -469,7 +534,7 @@ template success*(x: int): bool = x != 0
 template nxt*(input: string; idx, step: int = 1) = inc(idx, step)
 
 macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
-  ## See top level documentation of his module of how ``scanf`` works.
+  ## See top level documentation of this module about how ``scanp`` works.
   type StmtTriple = tuple[init, cond, action: NimNode]
 
   template interf(x): untyped = bindSym(x, brForceOpen)
@@ -516,13 +581,14 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
     of nnkCallKinds:
       # *{'A'..'Z'} !! s.add(!_)
       template buildWhile(input, idx, init, cond, action): untyped =
+        mixin hasNxt
         while hasNxt(input, idx):
           init
           if not cond: break
           action
 
       # (x) a  # bind action a to (x)
-      if it[0].kind == nnkPar and it.len == 2:
+      if it[0].kind in {nnkPar, nnkTupleConstr} and it.len == 2:
         result = atm(it[0], input, idx, placeholder(it[1], input, idx))
       elif it.kind == nnkInfix and it[0].eqIdent"->":
         # bind matching to some action:
@@ -562,8 +628,8 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
         if a.cond.kind == nnkEmpty or b.cond.kind == nnkEmpty:
           error("'|' operator applied to a non-condition")
         else:
-          result = (newStmtList(a.init,
-                newIfStmt((a.cond, a.action), (newTree(nnkStmtListExpr, b.init, b.cond), b.action))),
+          result = (newStmtList(a.init, newIfStmt((a.cond, a.action),
+                (newTree(nnkStmtListExpr, b.init, b.cond), b.action))),
               newEmptyNode(), newEmptyNode())
       elif it.kind == nnkInfix and it[0].eqIdent"^*":
         # a ^* b  is rewritten to:  (a *(b a))?
@@ -581,18 +647,22 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
       else:
         var resLen = genSym(nskLet, "resLen")
         result = (newLetStmt(resLen, placeholder(it, input, idx)),
-                  newCall(interf"success", resLen), !!newCall(interf"nxt", input, idx, resLen))
+                  newCall(interf"success", resLen),
+                  !!newCall(interf"nxt", input, idx, resLen))
     of nnkStrLit..nnkTripleStrLit:
       var resLen = genSym(nskLet, "resLen")
       result = (newLetStmt(resLen, newCall(interf"skip", input, it, idx)),
-                newCall(interf"success", resLen), !!newCall(interf"nxt", input, idx, resLen))
+                newCall(interf"success", resLen),
+                !!newCall(interf"nxt", input, idx, resLen))
     of nnkCurly, nnkAccQuoted, nnkCharLit:
-      result = (newEmptyNode(), newCall(interf"atom", input, idx, it), !!newCall(interf"nxt", input, idx))
+      result = (newEmptyNode(), newCall(interf"atom", input, idx, it),
+                !!newCall(interf"nxt", input, idx))
     of nnkCurlyExpr:
       if it.len == 3 and it[1].kind == nnkIntLit and it[2].kind == nnkIntLit:
         var h = newTree(nnkTupleConstr, it[0])
         for count in 2i64 .. it[1].intVal: h.add(it[0])
-        for count in it[1].intVal .. it[2].intVal-1: h.add(newTree(nnkPrefix, ident"?", it[0]))
+        for count in it[1].intVal .. it[2].intVal-1:
+          h.add(newTree(nnkPrefix, ident"?", it[0]))
         result = atm(h, input, idx, attached)
       elif it.len == 2 and it[1].kind == nnkIntLit:
         var h = newTree(nnkTupleConstr, it[0])
@@ -616,7 +686,7 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
   #var idx = genSym(nskVar, "idx")
   var res = genSym(nskVar, "res")
   result = newTree(nnkStmtListExpr, #newVarStmt(idx, newCall(interf"prepare", input)),
-                                    newVarStmt(res, newLit false))
+    newVarStmt(res, newLit false))
   var conds: seq[StmtTriple] = @[]
   for it in pattern:
     conds.add atm(it, input, idx, nil)
@@ -624,111 +694,3 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
   result.add res
   when defined(debugScanp):
     echo repr result
-
-
-when isMainModule:
-  proc twoDigits(input: string; x: var int; start: int): int =
-    if start+1 < input.len and input[start] == '0' and input[start+1] == '0':
-      result = 2
-      x = 13
-    else:
-      result = 0
-
-  proc someSep(input: string; start: int; seps: set[char] = {';',',','-','.'}): int =
-    result = 0
-    while start+result < input.len and input[start+result] in seps: inc result
-
-  proc demangle(s: string; res: var string; start: int): int =
-    while result+start < s.len and s[result+start] in {'_', '@'}: inc result
-    res = ""
-    while result+start < s.len and s[result+start] > ' ' and s[result+start] != '_':
-      res.add s[result+start]
-      inc result
-    while result+start < s.len and s[result+start] > ' ':
-      inc result
-
-  proc parseGDB(resp: string): seq[string] =
-    const
-      digits = {'0'..'9'}
-      hexdigits = digits + {'a'..'f', 'A'..'F'}
-      whites = {' ', '\t', '\C', '\L'}
-    result = @[]
-    var idx = 0
-    while true:
-      var prc = ""
-      var info = ""
-      if scanp(resp, idx, *`whites`, '#', *`digits`, +`whites`, ?("0x", *`hexdigits`, " in "),
-               demangle($input, prc, $index), *`whites`, '(', * ~ ')', ')',
-                *`whites`, "at ", +(~{'\C', '\L'} -> info.add($_)) ):
-        result.add prc & " " & info
-      else:
-        break
-
-  var key, val: string
-  var intval: int
-  var floatval: float
-  doAssert scanf("abc:: xyz 89  33.25", "$w$s::$s$w$s$i  $f", key, val, intval, floatVal)
-  doAssert key == "abc"
-  doAssert val == "xyz"
-  doAssert intval == 89
-  doAssert floatVal == 33.25
-
-  var binval: int
-  var octval: int
-  var hexval: int
-  doAssert scanf("0b0101 0o1234 0xabcd", "$b$s$o$s$h", binval, octval, hexval)
-  doAssert binval == 0b0101
-  doAssert octval == 0o1234
-  doAssert hexval == 0xabcd
-
-  let xx = scanf("$abc", "$$$i", intval)
-  doAssert xx == false
-
-
-  let xx2 = scanf("$1234", "$$$i", intval)
-  doAssert xx2
-
-  let yy = scanf(";.--Breakpoint00 [output]", "$[someSep]Breakpoint${twoDigits}$[someSep({';','.','-'})] [$+]$.", intVal, key)
-  doAssert yy
-  doAssert key == "output"
-  doAssert intVal == 13
-
-  var ident = ""
-  var idx = 0
-  let zz = scanp("foobar x x  x   xWZ", idx, +{'a'..'z'} -> add(ident, $_), *(*{' ', '\t'}, "x"), ~'U', "Z")
-  doAssert zz
-  doAssert ident == "foobar"
-
-  const digits = {'0'..'9'}
-  var year = 0
-  var idx2 = 0
-  if scanp("201655-8-9", idx2, `digits`{4,6} -> (year = year * 10 + ord($_) - ord('0')), "-8", "-9"):
-    doAssert year == 201655
-
-  const gdbOut = """
-      #0  @foo_96013_1208911747@8 (x0=...)
-          at c:/users/anwender/projects/nim/temp.nim:11
-      #1  0x00417754 in tempInit000 () at c:/users/anwender/projects/nim/temp.nim:13
-      #2  0x0041768d in NimMainInner ()
-          at c:/users/anwender/projects/nim/lib/system.nim:2605
-      #3  0x004176b1 in NimMain ()
-          at c:/users/anwender/projects/nim/lib/system.nim:2613
-      #4  0x004176db in main (argc=1, args=0x712cc8, env=0x711ca8)
-          at c:/users/anwender/projects/nim/lib/system.nim:2620"""
-  const result = @["foo c:/users/anwender/projects/nim/temp.nim:11",
-          "tempInit000 c:/users/anwender/projects/nim/temp.nim:13",
-          "NimMainInner c:/users/anwender/projects/nim/lib/system.nim:2605",
-          "NimMain c:/users/anwender/projects/nim/lib/system.nim:2613",
-          "main c:/users/anwender/projects/nim/lib/system.nim:2620"]
-  #doAssert parseGDB(gdbOut) == result
-
-  # bug #6487
-  var count = 0
-
-  proc test(): string =
-    inc count
-    result = ",123123"
-
-  var a: int
-  discard scanf(test(), ",$i", a)
-  doAssert count == 1
diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim
index d8a23286a..4b07aca5a 100644
--- a/lib/pure/strtabs.nim
+++ b/lib/pure/strtabs.nim
@@ -9,24 +9,67 @@
 
 ## The ``strtabs`` module implements an efficient hash table that is a mapping
 ## from strings to strings. Supports a case-sensitive, case-insensitive and
-## style-insensitive mode. An efficient string substitution operator  ``%``
-## for the string table is also provided.
+## style-insensitive mode.
+
+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"
+
+## An efficient string substitution operator
+## `% <#%25,string,StringTableRef,set[FormatFlag]>`_ for the string table
+## is also provided.
+runnableExamples:
+  var t = {"name": "John", "city": "Monaco"}.newStringTable
+  doAssert "${name} lives in ${city}" % t == "John lives in Monaco"
+
+## **See also:**
+## * `tables module <tables.html>`_ for general hash tables
+## * `sharedtables module<sharedtables.html>`_ for shared hash table support
+## * `strutils module<strutils.html>`_ for common string functions
+## * `json module<json.html>`_ for table-like structure which allows
+##   heterogeneous members
+
+import std/private/since
 
 import
-  hashes, strutils
+  std/[hashes, strutils]
 
-when defined(js):
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
+when defined(js) or defined(nimscript) or defined(Standalone):
   {.pragma: rtlFunc.}
 else:
   {.pragma: rtlFunc, rtl.}
-  import os
-  include "system/inclrtl"
+  import std/envvars
+
+include "system/inclrtl"
 
 type
-  StringTableMode* = enum     ## describes the tables operation mode
-    modeCaseSensitive,        ## the table is case sensitive
-    modeCaseInsensitive,      ## the table is case insensitive
-    modeStyleInsensitive      ## the table is style insensitive
+  StringTableMode* = enum ## Describes the tables operation mode.
+    modeCaseSensitive,    ## the table is case sensitive
+    modeCaseInsensitive,  ## the table is case insensitive
+    modeStyleInsensitive  ## the table is style insensitive
   KeyValuePair = tuple[key, val: string, hasValue: bool]
   KeyValuePairSeq = seq[KeyValuePair]
   StringTableObj* = object of RootObj
@@ -34,43 +77,42 @@ type
     data: KeyValuePairSeq
     mode: StringTableMode
 
-  StringTableRef* = ref StringTableObj ## use this type to declare string tables
+  StringTableRef* = ref StringTableObj
 
-proc len*(t: StringTableRef): int {.rtlFunc, extern: "nst$1".} =
-  ## returns the number of keys in `t`.
-  result = t.counter
+  FormatFlag* = enum ## Flags for the `%` operator.
+    useEnvironment,  ## Use environment variable if the ``$key``
+                     ## is not found in the table.
+                     ## Does nothing when using `js` target.
+    useEmpty,        ## Use the empty string as a default, thus it
+                     ## won't throw an exception if ``$key`` is not
+                     ## in the table.
+    useKey           ## Do not replace ``$key`` if it is not found
+                     ## in the table (or in the environment).
+
+const
+  growthFactor = 2
+  startSize = 64
+
+proc mode*(t: StringTableRef): StringTableMode {.inline.} = t.mode
 
 iterator pairs*(t: StringTableRef): tuple[key, value: string] =
-  ## iterates over every (key, value) pair in the table `t`.
+  ## Iterates over every `(key, value)` pair in the table `t`.
   for h in 0..high(t.data):
     if t.data[h].hasValue:
       yield (t.data[h].key, t.data[h].val)
 
 iterator keys*(t: StringTableRef): string =
-  ## iterates over every key in the table `t`.
+  ## Iterates over every key in the table `t`.
   for h in 0..high(t.data):
     if t.data[h].hasValue:
       yield t.data[h].key
 
 iterator values*(t: StringTableRef): string =
-  ## iterates over every value in the table `t`.
+  ## Iterates over every value in the table `t`.
   for h in 0..high(t.data):
     if t.data[h].hasValue:
       yield t.data[h].val
 
-type
-  FormatFlag* = enum          ## flags for the `%` operator
-    useEnvironment,           ## use environment variable if the ``$key``
-                              ## is not found in the table. Does nothing when using `js` target.
-    useEmpty,                 ## use the empty string as a default, thus it
-                              ## won't throw an exception if ``$key`` is not
-                              ## in the table
-    useKey                    ## do not replace ``$key`` if it is not found
-                              ## in the table (or in the environment)
-
-const
-  growthFactor = 2
-  startSize = 64
 
 proc myhash(t: StringTableRef, key: string): Hash =
   case t.mode
@@ -89,7 +131,7 @@ proc mustRehash(length, counter: int): bool =
   result = (length * 2 < counter * 3) or (length - counter < 4)
 
 proc nextTry(h, maxHash: Hash): Hash {.inline.} =
-  result = ((5 * h) + 1) and maxHash
+  result = (h + 1) and maxHash
 
 proc rawGet(t: StringTableRef, key: string): int =
   var h: Hash = myhash(t, key) and high(t.data) # start with real hash value
@@ -103,29 +145,78 @@ template get(t: StringTableRef, key: string) =
   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")
+    raise newException(KeyError, "key not found: " & key)
+
+
+proc len*(t: StringTableRef): int {.rtlFunc, extern: "nst$1".} =
+  ## Returns the number of keys in `t`.
+  result = t.counter
 
 proc `[]`*(t: StringTableRef, key: string): var string {.
            rtlFunc, extern: "nstTake".} =
-  ## retrieves the location at ``t[key]``. If `key` is not in `t`, the
-  ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
-  ## the key exists.
+  ## Retrieves the location at ``t[key]``.
+  ##
+  ## If `key` is not in `t`, the ``KeyError`` exception is raised.
+  ## One can check with `hasKey proc <#hasKey,StringTableRef,string>`_
+  ## whether the key exists.
+  ##
+  ## See also:
+  ## * `getOrDefault proc <#getOrDefault,StringTableRef,string,string>`_
+  ## * `[]= proc <#[]=,StringTableRef,string,string>`_ for inserting a new
+  ##   (key, value) pair in the table
+  ## * `hasKey proc <#hasKey,StringTableRef,string>`_ for checking if a key
+  ##   is in the table
+  runnableExamples:
+    var t = {"name": "John", "city": "Monaco"}.newStringTable
+    doAssert t["name"] == "John"
+    doAssertRaises(KeyError):
+      echo t["occupation"]
   get(t, key)
 
-proc getOrDefault*(t: StringTableRef; key: string, default: string = ""): string =
+proc getOrDefault*(t: StringTableRef; key: string,
+    default: string = ""): string =
+  ## Retrieves the location at ``t[key]``.
+  ##
+  ## If `key` is not in `t`, the default value is returned (if not specified,
+  ## it is an empty string (`""`)).
+  ##
+  ## See also:
+  ## * `[] proc <#[],StringTableRef,string>`_ for retrieving a value of a key
+  ## * `hasKey proc <#hasKey,StringTableRef,string>`_ for checking if a key
+  ##   is in the table
+  ## * `[]= proc <#[]=,StringTableRef,string,string>`_ for inserting a new
+  ##   (key, value) pair in the table
+  runnableExamples:
+    var t = {"name": "John", "city": "Monaco"}.newStringTable
+    doAssert t.getOrDefault("name") == "John"
+    doAssert t.getOrDefault("occupation") == ""
+    doAssert t.getOrDefault("occupation", "teacher") == "teacher"
+    doAssert t.getOrDefault("name", "Paul") == "John"
+
   var index = rawGet(t, key)
   if index >= 0: result = t.data[index].val
   else: result = default
 
-proc hasKey*(t: StringTableRef, key: string): bool {.rtlFunc, extern: "nst$1".} =
-  ## returns true iff `key` is in the table `t`.
+proc hasKey*(t: StringTableRef, key: string): bool {.rtlFunc,
+    extern: "nst$1".} =
+  ## Returns true if `key` is in the table `t`.
+  ##
+  ## See also:
+  ## * `getOrDefault proc <#getOrDefault,StringTableRef,string,string>`_
+  ## * `contains proc <#contains,StringTableRef,string>`_
+  runnableExamples:
+    var t = {"name": "John", "city": "Monaco"}.newStringTable
+    doAssert t.hasKey("name")
+    doAssert not t.hasKey("occupation")
   result = rawGet(t, key) >= 0
 
 proc contains*(t: StringTableRef, key: string): bool =
-  ## alias of `hasKey` for use with the `in` operator.
+  ## Alias of `hasKey proc <#hasKey,StringTableRef,string>`_ for use with
+  ## the `in` operator.
+  runnableExamples:
+    var t = {"name": "John", "city": "Monaco"}.newStringTable
+    doAssert "name" in t
+    doAssert "occupation" notin t
   return hasKey(t, key)
 
 proc rawInsert(t: StringTableRef, data: var KeyValuePairSeq, key, val: string) =
@@ -140,11 +231,21 @@ proc enlarge(t: StringTableRef) =
   var n: KeyValuePairSeq
   newSeq(n, len(t.data) * growthFactor)
   for i in countup(0, high(t.data)):
-    if t.data[i].hasValue: rawInsert(t, n, t.data[i].key, t.data[i].val)
+    if t.data[i].hasValue: rawInsert(t, n, move t.data[i].key, move t.data[i].val)
   swap(t.data, n)
 
-proc `[]=`*(t: StringTableRef, key, val: string) {.rtlFunc, extern: "nstPut".} =
-  ## puts a (key, value)-pair into `t`.
+proc `[]=`*(t: StringTableRef, key, val: string) {.
+  rtlFunc, extern: "nstPut".} =
+  ## Inserts a `(key, value)` pair into `t`.
+  ##
+  ## See also:
+  ## * `[] proc <#[],StringTableRef,string>`_ for retrieving a value of a key
+  ## * `del proc <#del,StringTableRef,string>`_ for removing a key from the table
+  runnableExamples:
+    var t = {"name": "John", "city": "Monaco"}.newStringTable
+    t["occupation"] = "teacher"
+    doAssert t.hasKey("occupation")
+
   var index = rawGet(t, key)
   if index >= 0:
     t.data[index].val = val
@@ -153,67 +254,146 @@ proc `[]=`*(t: StringTableRef, key, val: string) {.rtlFunc, extern: "nstPut".} =
     rawInsert(t, t.data, key, val)
     inc(t.counter)
 
+proc newStringTable*(mode: StringTableMode): owned(StringTableRef) {.
+  rtlFunc, extern: "nst$1", noSideEffect.} =
+  ## Creates a new empty string table.
+  ##
+  ## See also:
+  ## * `newStringTable(keyValuePairs) proc
+  ##   <#newStringTable,varargs[tuple[string,string]],StringTableMode>`_
+  result = StringTableRef(mode: mode, counter: 0, data: newSeq[KeyValuePair](startSize))
+
+proc newStringTable*(keyValuePairs: varargs[string],
+                     mode: StringTableMode): owned(StringTableRef) {.
+  rtlFunc, extern: "nst$1WithPairs", noSideEffect.} =
+  ## Creates a new string table with given `key, value` string pairs.
+  ##
+  ## `StringTableMode` must be specified.
+  runnableExamples:
+    var mytab = newStringTable("key1", "val1", "key2", "val2",
+                               modeCaseInsensitive)
+
+  result = newStringTable(mode)
+  var i = 0
+  while i < high(keyValuePairs):
+    {.noSideEffect.}:
+      result[keyValuePairs[i]] = keyValuePairs[i + 1]
+    inc(i, 2)
+
+proc newStringTable*(keyValuePairs: varargs[tuple[key, val: string]],
+    mode: StringTableMode = modeCaseSensitive): owned(StringTableRef) {.
+    rtlFunc, extern: "nst$1WithTableConstr", noSideEffect.} =
+  ## Creates a new string table with given `(key, value)` tuple pairs.
+  ##
+  ## The default mode is case sensitive.
+  runnableExamples:
+    var
+      mytab1 = newStringTable({"key1": "val1", "key2": "val2"}, modeCaseInsensitive)
+      mytab2 = newStringTable([("key3", "val3"), ("key4", "val4")])
+
+  result = newStringTable(mode)
+  for key, val in items(keyValuePairs):
+    {.noSideEffect.}:
+      result[key] = val
+
 proc raiseFormatException(s: string) =
-  var e: ref ValueError
-  new(e)
-  e.msg = "format string: key not found: " & s
-  raise e
+  raise newException(ValueError, "format string: key not found: " & s)
 
 proc getValue(t: StringTableRef, flags: set[FormatFlag], key: string): string =
   if hasKey(t, key): return t.getOrDefault(key)
-  # hm difficult: assume safety in taint mode here. XXX This is dangerous!
-  when defined(js):
+  when defined(js) or defined(nimscript) or defined(Standalone):
     result = ""
   else:
-    if useEnvironment in flags: result = os.getEnv(key).string
+    if useEnvironment in flags: result = getEnv(key)
     else: result = ""
   if result.len == 0:
     if useKey in flags: result = '$' & key
     elif useEmpty notin flags: raiseFormatException(key)
 
-proc newStringTable*(mode: StringTableMode): StringTableRef {.
-  rtlFunc, extern: "nst$1".} =
-  ## creates a new string table that is empty.
-  new(result)
-  result.mode = mode
-  result.counter = 0
-  newSeq(result.data, startSize)
-
 proc clear*(s: StringTableRef, mode: StringTableMode) {.
   rtlFunc, extern: "nst$1".} =
-  ## resets a string table to be empty again.
+  ## Resets a string table to be empty again, perhaps altering the mode.
+  ##
+  ## See also:
+  ## * `del proc <#del,StringTableRef,string>`_ for removing a key from the table
+  runnableExamples:
+    var t = {"name": "John", "city": "Monaco"}.newStringTable
+    clear(t, modeCaseSensitive)
+    doAssert len(t) == 0
+    doAssert "name" notin t
+    doAssert "city" notin t
   s.mode = mode
   s.counter = 0
   s.data.setLen(startSize)
   for i in 0..<s.data.len:
     s.data[i].hasValue = false
 
-proc newStringTable*(keyValuePairs: varargs[string],
-                     mode: StringTableMode): StringTableRef {.
-  rtlFunc, extern: "nst$1WithPairs".} =
-  ## creates a new string table with given key value pairs.
-  ## Example::
-  ##   var mytab = newStringTable("key1", "val1", "key2", "val2",
-  ##                              modeCaseInsensitive)
-  result = newStringTable(mode)
-  var i = 0
-  while i < high(keyValuePairs):
-    result[keyValuePairs[i]] = keyValuePairs[i + 1]
-    inc(i, 2)
+proc clear*(s: StringTableRef) {.since: (1, 1).} =
+  ## Resets a string table to be empty again without changing the mode.
+  s.clear(s.mode)
+
+proc del*(t: StringTableRef, key: string) =
+  ## Removes `key` from `t`.
+  ##
+  ## See also:
+  ## * `clear proc <#clear,StringTableRef,StringTableMode>`_ for resetting a
+  ##   table to be empty
+  ## * `[]= proc <#[]=,StringTableRef,string,string>`_ for inserting a new
+  ##   (key, value) pair in the table
+  runnableExamples:
+    var t = {"name": "John", "city": "Monaco"}.newStringTable
+    t.del("name")
+    doAssert len(t) == 1
+    doAssert "name" notin t
+    doAssert "city" in t
+
+  # Impl adapted from `tableimpl.delImplIdx`
+  var i = rawGet(t, key)
+  let msk = high(t.data)
+  if i >= 0:
+    dec(t.counter)
+    block outer:
+      while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
+        var j = i # The correctness of this depends on (h+1) in nextTry,
+        var r = j # though may be adaptable to other simple sequences.
+        t.data[i].hasValue = false # mark current EMPTY
+        t.data[i].key = ""
+        t.data[i].val = ""
+        while true:
+          i = (i + 1) and msk # increment mod table size
+          if not t.data[i].hasValue: # end of collision cluster; So all done
+            break outer
+          r = t.myhash(t.data[i].key) and msk # "home" location of key@i
+          if not ((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
+            break
+        when defined(js):
+          t.data[j] = t.data[i]
+        elif defined(gcDestructors):
+          t.data[j] = move t.data[i]
+        else:
+          shallowCopy(t.data[j], t.data[i]) # data[j] will be marked EMPTY next loop
 
-proc newStringTable*(keyValuePairs: varargs[tuple[key, val: string]],
-                     mode: StringTableMode = modeCaseSensitive): StringTableRef {.
-  rtlFunc, extern: "nst$1WithTableConstr".} =
-  ## creates a new string table with given key value pairs.
-  ## Example::
-  ##   var mytab = newStringTable({"key1": "val1", "key2": "val2"},
-  ##                              modeCaseInsensitive)
-  result = newStringTable(mode)
-  for key, val in items(keyValuePairs): result[key] = val
+proc `$`*(t: StringTableRef): string {.rtlFunc, extern: "nstDollar".} =
+  ## The `$` operator for string tables. Used internally when calling
+  ## `echo` on a table.
+  if t.len == 0:
+    result = "{:}"
+  else:
+    result = "{"
+    for key, val in pairs(t):
+      if result.len > 1: result.add(", ")
+      result.add(key)
+      result.add(": ")
+      result.add(val)
+    result.add("}")
 
 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 = ""
@@ -240,28 +420,3 @@ proc `%`*(f: string, t: StringTableRef, flags: set[FormatFlag] = {}): string {.
     else:
       add(result, f[i])
       inc(i)
-
-proc `$`*(t: StringTableRef): string {.rtlFunc, extern: "nstDollar".} =
-  ## The `$` operator for string tables.
-  if t.len == 0:
-    result = "{:}"
-  else:
-    result = "{"
-    for key, val in pairs(t):
-      if result.len > 1: result.add(", ")
-      result.add(key)
-      result.add(": ")
-      result.add(val)
-    result.add("}")
-
-when isMainModule:
-  var x = {"k": "v", "11": "22", "565": "67"}.newStringTable
-  assert x["k"] == "v"
-  assert x["11"] == "22"
-  assert x["565"] == "67"
-  x["11"] = "23"
-  assert x["11"] == "23"
-
-  x.clear(modeCaseInsensitive)
-  x["11"] = "22"
-  assert x["11"] == "22"
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index ab34a0b2d..81be7db17 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -7,209 +7,215 @@
 #    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.
-## This module is available for the `JavaScript target
-## <backends.html#the-javascript-target>`_.
+## 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.
+
+runnableExamples:
+  let
+    numbers = @[867, 5309]
+    multiLineString = "first line\nsecond line\nthird line"
+
+  let jenny = numbers.join("-")
+  assert jenny == "867-5309"
 
-import parseutils
-from math import pow, round, floor, log10
-from algorithm import reverse
+  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"
 
-when defined(nimVmExportFixed):
-  from unicode import toLower, toUpper
-  export toLower, toUpper
+## The chaining of functions is possible thanks to the
+## `method call syntax<manual.html#procedures-method-call-syntax>`_:
 
-{.deadCodeElim: on.}  # dce option deprecated
+runnableExamples:
+  from std/sequtils import map
 
-{.push debugger:off .} # the user does not want to trace a part
-                       # of the standard library!
+  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#backends-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<sequtils.html>`_ for operations on container
+##   types (including strings)
+## * `parsecsv module<parsecsv.html>`_ for a high-performance CSV parser
+## * `parseutils module<parseutils.html>`_ for lower-level parsing of tokens,
+##   numbers, identifiers, etc.
+## * `parseopt module<parseopt.html>`_ for command-line parsing
+## * `pegs module<pegs.html>`_ for PEG (Parsing Expression Grammar) support
+## * `strtabs module<strtabs.html>`_ for efficient hash tables
+##   (dictionaries, in some programming languages) mapping from strings to strings
+## * `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 std/parseutils
+from std/math import pow, floor, log10
+from std/algorithm import fill, reverse
+import std/enumutils
+
+from std/unicode import toLower, toUpper
+export toLower, toUpper
 
 include "system/inclrtl"
+import std/private/[since, jsutils]
+from std/private/strimpl import cmpIgnoreStyleImpl, cmpIgnoreCaseImpl,
+    startsWithImpl, endsWithImpl
 
-{.pop.}
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
-# Support old split with set[char]
-when defined(nimOldSplit):
-  {.pragma: deprecatedSplit, deprecated.}
-else:
-  {.pragma: deprecatedSplit.}
 
 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
+    ## The set of letters.
+
+  UppercaseLetters* = {'A'..'Z'}
+    ## The set of uppercase ASCII letters.
+
+  LowercaseLetters* = {'a'..'z'}
+    ## The set of lowercase ASCII letters.
+
+  PunctuationChars* = {'!'..'/', ':'..'@', '['..'`', '{'..'~'}
+    ## The set of all ASCII punctuation characters.
 
   Digits* = {'0'..'9'}
-    ## the set of digits
+    ## The set of digits.
 
   HexDigits* = {'0'..'9', 'A'..'F', 'a'..'f'}
-    ## the set of hexadecimal digits
+    ## The set of hexadecimal digits.
 
   IdentChars* = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
-    ## the set of characters an identifier can consist of
+    ## The set of characters an identifier can consist of.
 
   IdentStartChars* = {'a'..'z', 'A'..'Z', '_'}
-    ## the set of characters an identifier can start with
+    ## The set of characters an identifier can start with.
+
+  Newlines* = {'\13', '\10'}
+    ## The set of characters a newline terminator can start with (carriage
+    ## return, line feed).
 
-  NewLines* = {'\13', '\10'}
-    ## the set of characters a newline terminator can start with
+  PrintableChars* = Letters + Digits + PunctuationChars + Whitespace
+    ## The set of all printable ASCII characters (letters, digits, whitespace, and punctuation characters).
 
   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:
-    ##
-    ## .. code-block:: nim
+    ## make the `find func<#find,string,set[char],Natural,int>`_
+    ## find **invalid** characters in strings. Example:
+    ##   ```nim
     ##   let invalid = AllChars - Digits
     ##   doAssert "01234".find(invalid) == -1
     ##   doAssert "01A34".find(invalid) == 2
+    ##   ```
 
-proc isAlphaAscii*(c: char): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsAlphaAsciiChar".}=
-  ## Checks whether or not `c` is alphabetical.
+func isAlphaAscii*(c: char): bool {.rtl, extern: "nsuIsAlphaAsciiChar".} =
+  ## 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
+    doAssert isAlphaAscii('8') == false
   return c in Letters
 
-proc isAlphaNumeric*(c: char): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsAlphaNumericChar".} =
+func isAlphaNumeric*(c: char): bool {.rtl, extern: "nsuIsAlphaNumericChar".} =
   ## Checks whether or not `c` is alphanumeric.
   ##
   ## This checks a-z, A-Z, 0-9 ASCII characters only.
+  runnableExamples:
+    doAssert isAlphaNumeric('n') == true
+    doAssert isAlphaNumeric('8') == true
+    doAssert isAlphaNumeric(' ') == false
   return c in Letters+Digits
 
-proc isDigit*(c: char): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsDigitChar".} =
+func isDigit*(c: char): bool {.rtl, extern: "nsuIsDigitChar".} =
   ## Checks whether or not `c` is a number.
   ##
   ## This checks 0-9 ASCII characters only.
+  runnableExamples:
+    doAssert isDigit('n') == false
+    doAssert isDigit('8') == true
   return c in Digits
 
-proc isSpaceAscii*(c: char): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsSpaceAsciiChar".} =
+func isSpaceAscii*(c: char): bool {.rtl, extern: "nsuIsSpaceAsciiChar".} =
   ## Checks whether or not `c` is a whitespace character.
+  runnableExamples:
+    doAssert isSpaceAscii('n') == false
+    doAssert isSpaceAscii(' ') == true
+    doAssert isSpaceAscii('\t') == true
   return c in Whitespace
 
-proc isLowerAscii*(c: char): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsLowerAsciiChar".} =
+func isLowerAscii*(c: char): bool {.rtl, extern: "nsuIsLowerAsciiChar".} =
   ## Checks whether or not `c` is a lower case character.
   ##
   ## This checks ASCII characters only.
-  return c in {'a'..'z'}
-
-proc isUpperAscii*(c: char): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsUpperAsciiChar".} =
-  ## Checks whether or not `c` is an upper case character.
-  ##
-  ## This checks ASCII characters only.
-  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".} =
-  ## Checks whether or not `s` is alphabetical.
+  ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
   ##
-  ## 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`.
-  isImpl isAlphaAscii
-
-proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsAlphaNumericStr".} =
-  ## 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`.
-  isImpl isAlphaNumeric
-
-proc isDigit*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsDigitStr".} =
-  ## 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`.
-  isImpl isDigit
-
-proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsSpaceAsciiStr".} =
-  ## 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`.
-  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 =
-  ## 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.
-  isCaseImpl(s, isLowerAscii, skipNonAlpha)
+  ## See also:
+  ## * `toLowerAscii func<#toLowerAscii,char>`_
+  runnableExamples:
+    doAssert isLowerAscii('e') == true
+    doAssert isLowerAscii('E') == false
+    doAssert isLowerAscii('7') == false
+  return c in LowercaseLetters
 
-proc isUpperAscii*(s: string, skipNonAlpha: bool): bool =
-  ## Checks whether ``s`` is upper case.
+func isUpperAscii*(c: char): bool {.rtl, extern: "nsuIsUpperAsciiChar".} =
+  ## Checks whether or not `c` is an upper case character.
   ##
   ## This checks ASCII characters only.
+  ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
   ##
-  ## 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.
-  isCaseImpl(s, isUpperAscii, skipNonAlpha)
+  ## See also:
+  ## * `toUpperAscii func<#toUpperAscii,char>`_
+  runnableExamples:
+    doAssert isUpperAscii('e') == false
+    doAssert isUpperAscii('E') == true
+    doAssert isUpperAscii('7') == false
+  return c in UppercaseLetters
 
-proc toLowerAscii*(c: char): char {.noSideEffect, procvar,
-  rtl, extern: "nsuToLowerAsciiChar".} =
-  ## Converts `c` into lower case.
+func toLowerAscii*(c: char): char {.rtl, extern: "nsuToLowerAsciiChar".} =
+  ## 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
+  ## This works only for the letters `A-Z`. See `unicode.toLower
+  ## <unicode.html#toLower,Rune>`_ for a version that works for any Unicode
   ## character.
-  if c in {'A'..'Z'}:
-    result = chr(ord(c) + (ord('a') - ord('A')))
+  ##
+  ## See also:
+  ## * `isLowerAscii func<#isLowerAscii,char>`_
+  ## * `toLowerAscii func<#toLowerAscii,string>`_ for converting a string
+  runnableExamples:
+    doAssert toLowerAscii('A') == 'a'
+    doAssert toLowerAscii('e') == 'e'
+  if c in UppercaseLetters:
+    result = char(uint8(c) xor 0b0010_0000'u8)
   else:
     result = c
 
@@ -218,54 +224,106 @@ template toImpl(call) =
   for i in 0..len(s) - 1:
     result[i] = call(s[i])
 
-proc toLowerAscii*(s: string): string {.noSideEffect, procvar,
-  rtl, extern: "nsuToLowerAsciiStr".} =
-  ## Converts `s` into lower case.
+func toLowerAscii*(s: string): string {.rtl, extern: "nsuToLowerAsciiStr".} =
+  ## 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
+  ## This works only for the letters `A-Z`. See `unicode.toLower
+  ## <unicode.html#toLower,string>`_ for a version that works for any Unicode
   ## character.
+  ##
+  ## See also:
+  ## * `normalize func<#normalize,string>`_
+  runnableExamples:
+    doAssert toLowerAscii("FooBar!") == "foobar!"
   toImpl toLowerAscii
 
-proc toUpperAscii*(c: char): char {.noSideEffect, procvar,
-  rtl, extern: "nsuToUpperAsciiChar".} =
-  ## Converts `c` into upper case.
+func toUpperAscii*(c: char): char {.rtl, extern: "nsuToUpperAsciiChar".} =
+  ## 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
+  ## This works only for the letters `A-Z`.  See `unicode.toUpper
+  ## <unicode.html#toUpper,Rune>`_ for a version that works for any Unicode
   ## character.
-  if c in {'a'..'z'}:
-    result = chr(ord(c) - (ord('a') - ord('A')))
+  ##
+  ## See also:
+  ## * `isUpperAscii func<#isUpperAscii,char>`_
+  ## * `toUpperAscii func<#toUpperAscii,string>`_ for converting a string
+  ## * `capitalizeAscii func<#capitalizeAscii,string>`_
+  runnableExamples:
+    doAssert toUpperAscii('a') == 'A'
+    doAssert toUpperAscii('E') == 'E'
+  if c in LowercaseLetters:
+    result = char(uint8(c) xor 0b0010_0000'u8)
   else:
     result = c
 
-proc toUpperAscii*(s: string): string {.noSideEffect, procvar,
-  rtl, extern: "nsuToUpperAsciiStr".} =
-  ## Converts `s` into upper case.
+func toUpperAscii*(s: string): string {.rtl, extern: "nsuToUpperAsciiStr".} =
+  ## 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
+  ## This works only for the letters `A-Z`.  See `unicode.toUpper
+  ## <unicode.html#toUpper,string>`_ for a version that works for any Unicode
   ## character.
+  ##
+  ## See also:
+  ## * `capitalizeAscii func<#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.
+func capitalizeAscii*(s: string): string {.rtl, extern: "nsuCapitalizeAscii".} =
+  ## 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.
   ##
-  ## This works only for the letters ``A-Z``.
+  ## See also:
+  ## * `toUpperAscii func<#toUpperAscii,char>`_
+  runnableExamples:
+    doAssert capitalizeAscii("foo") == "Foo"
+    doAssert capitalizeAscii("-bar") == "-bar"
   if s.len == 0: result = ""
   else: result = toUpperAscii(s[0]) & substr(s, 1)
 
-proc normalize*(s: string): string {.noSideEffect, procvar,
-  rtl, extern: "nsuNormalize".} =
+func nimIdentNormalize*(s: string): string =
+  ## Normalizes the string `s` as a Nim identifier.
+  ##
+  ## That means to convert to lower case and remove any '_' on all characters
+  ## except first one.
+  ##
+  ## .. Warning:: Backticks (`) are not handled: they remain *as is* and
+  ##    spaces are preserved. See `nimIdentBackticksNormalize
+  ##    <dochelpers.html#nimIdentBackticksNormalize,string>`_ for
+  ##    an alternative approach.
+  runnableExamples:
+    doAssert nimIdentNormalize("Foo_bar") == "Foobar"
+  result = newString(s.len)
+  if s.len == 0:
+    return
+  result[0] = s[0]
+  var j = 1
+  for i in 1..len(s) - 1:
+    if s[i] in UppercaseLetters:
+      result[j] = chr(ord(s[i]) + (ord('a') - ord('A')))
+      inc j
+    elif s[i] != '_':
+      result[j] = s[i]
+      inc j
+  if j != s.len: setLen(result, j)
+
+func normalize*(s: string): string {.rtl, extern: "nsuNormalize".} =
   ## Normalizes the string `s`.
   ##
   ## That means to convert it to lower case and remove any '_'. This
   ## should NOT be used to normalize Nim identifier names.
+  ##
+  ## See also:
+  ## * `toLowerAscii func<#toLowerAscii,string>`_
+  runnableExamples:
+    doAssert normalize("Foo_bar") == "foobar"
+    doAssert normalize("Foo Bar") == "foo bar"
   result = newString(s.len)
   var j = 0
   for i in 0..len(s) - 1:
-    if s[i] in {'A'..'Z'}:
+    if s[i] in UppercaseLetters:
       result[j] = chr(ord(s[i]) + (ord('a') - ord('A')))
       inc j
     elif s[i] != '_':
@@ -273,107 +331,49 @@ proc normalize*(s: string): string {.noSideEffect, procvar,
       inc j
   if j != s.len: setLen(result, j)
 
-proc cmpIgnoreCase*(a, b: string): int {.noSideEffect,
-  rtl, extern: "nsuCmpIgnoreCase", procvar.} =
+func cmpIgnoreCase*(a, b: string): int {.rtl, extern: "nsuCmpIgnoreCase".} =
   ## Compares two strings in a case insensitive manner. Returns:
   ##
-  ## | 0 iff a == b
-  ## | < 0 iff a < b
-  ## | > 0 iff a > b
-  var i = 0
-  var m = min(a.len, b.len)
-  while i < m:
-    result = ord(toLowerAscii(a[i])) - ord(toLowerAscii(b[i]))
-    if result != 0: return
-    inc(i)
-  result = a.len - b.len
+  ## | `0` if a == b
+  ## | `< 0` if a < b
+  ## | `> 0` if a > b
+  runnableExamples:
+    doAssert cmpIgnoreCase("FooBar", "foobar") == 0
+    doAssert cmpIgnoreCase("bar", "Foo") < 0
+    doAssert cmpIgnoreCase("Foo5", "foo4") > 0
+  cmpIgnoreCaseImpl(a, b)
 
-{.push checks: off, line_trace: off .} # this is a hot-spot in the compiler!
-                                       # thus we compile without checks here
+{.push checks: off, line_trace: off.} # this is a hot-spot in the compiler!
+                                      # thus we compile without checks here
 
-proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
-  rtl, extern: "nsuCmpIgnoreStyle", procvar.} =
-  ## Semantically the same as ``cmp(normalize(a), normalize(b))``. It
+func cmpIgnoreStyle*(a, b: string): int {.rtl, extern: "nsuCmpIgnoreStyle".} =
+  ## 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.
   ##
-  ## | 0 iff a == b
-  ## | < 0 iff a < b
-  ## | > 0 iff a > b
-  var i = 0
-  var j = 0
-  while true:
-    while i < a.len and a[i] == '_': inc i
-    while j < b.len and b[j] == '_': inc j
-    var aa = if i < a.len: toLowerAscii(a[i]) else: '\0'
-    var bb = if j < b.len: toLowerAscii(b[j]) else: '\0'
-    result = ord(aa) - ord(bb)
-    if result != 0: return result
-    # the characters are identical:
-    if i >= a.len:
-      # both cursors at the end:
-      if j >= b.len: return 0
-      # not yet at the end of 'b':
-      return -1
-    elif j >= b.len:
-      return 1
-    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.
+  ## Returns:
   ##
-  ## 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.
-  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.
-  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.
-  if len(s) == 0:
-    return true
+  ## | `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
+  cmpIgnoreStyleImpl(a, b)
+{.pop.}
 
-  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
+func substrEq(s: string, pos: int, substr: string): bool =
+  # Always returns false for empty `substr`
   var length = substr.len
-  while i < length and s[pos+i] == substr[i]:
-    inc i
-  return i == length
-
-# --------- Private templates for different split separators -----------
+  if length > 0:
+    var i = 0
+    while i < length and pos+i < s.len and s[pos+i] == substr[i]:
+      inc i
+    i == length
+  else: false
 
 template stringHasSep(s: string, index: int, seps: set[char]): bool =
   s[index] in seps
@@ -385,7 +385,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
 
@@ -413,116 +413,97 @@ 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:
+  ##   ```nim
+  ##   for word in split(";;this;is;an;;example;;;", ';'):
+  ##     writeLine(stdout, word)
+  ##   ```
+  ## Results in:
+  ##   ```
+  ##   ""
+  ##   ""
+  ##   "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 func<#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.
   ##
   ## Substrings are separated by a substring containing only `seps`.
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   for word in split("this\lis an\texample"):
   ##     writeLine(stdout, word)
+  ##   ```
   ##
   ## ...generates this output:
   ##
-  ## .. code-block::
+  ##   ```
   ##   "this"
   ##   "is"
   ##   "an"
   ##   "example"
+  ##   ```
   ##
   ## And the following code:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   for word in split("this:is;an$example", {';', ':', '$'}):
   ##     writeLine(stdout, word)
+  ##   ```
   ##
   ## ...produces the same output as the first example. The code:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   let date = "2012-11-20T22:08:08.398990"
   ##   let separators = {' ', '-', ':', 'T'}
   ##   for number in split(date, separators):
   ##     writeLine(stdout, number)
+  ##   ```
   ##
   ## ...results in:
   ##
-  ## .. code-block::
+  ##   ```
   ##   "2012"
   ##   "11"
   ##   "20"
   ##   "22"
   ##   "08"
   ##   "08.398990"
+  ##   ```
   ##
-  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)
-
-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.
-  accumulateResult(splitWhitespace(s, maxsplit))
-
-iterator split*(s: string, sep: char, maxsplit: int = -1): string =
-  ## Splits the string `s` into substrings using a single separator.
+  ##  .. note:: Empty separator set results in returning an original string,
+  ##   following the interpretation "split by no element".
   ##
-  ## 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)
+  ## See also:
+  ## * `rsplit iterator<#rsplit.i,string,set[char],int>`_
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
+  ## * `split func<#split,string,set[char],int>`_
+  splitCommon(s, seps, maxsplit, 1)
 
 iterator split*(s: string, sep: string, maxsplit: int = -1): string =
   ## Splits the string `s` into substrings using a string separator.
@@ -530,18 +511,31 @@ iterator split*(s: string, sep: string, maxsplit: int = -1): string =
   ## Substrings are separated by the string `sep`.
   ## The code:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   for word in split("thisDATAisDATAcorrupted", "DATA"):
   ##     writeLine(stdout, word)
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block::
+  ##   ```
   ##   "this"
   ##   "is"
   ##   "corrupted"
+  ##   ```
+  ##
+  ##  .. note:: Empty separator string results in returning an original string,
+  ##   following the interpretation "split by no element".
   ##
-  splitCommon(s, sep, maxsplit, sep.len)
+  ## See also:
+  ## * `rsplit iterator<#rsplit.i,string,string,int,bool>`_
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
+  ## * `split func<#split,string,string,int>`_
+  let sepLen = if sep.len == 0: 1 # prevents infinite loop
+    else: sep.len
+  splitCommon(s, sep, maxsplit, sepLen)
+
 
 template rsplitCommon(s, sep, maxsplit, sepLen) =
   ## Common code for rsplit functions
@@ -567,79 +561,113 @@ 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.
+  ## <#split.i,string,char,int>`_ except in **reverse** order.
   ##
-  ## .. code-block:: nim
-  ##   for piece in "foo bar".rsplit(WhiteSpace):
+  ##   ```nim
+  ##   for piece in "foo:bar".rsplit(':'):
   ##     echo piece
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block:: nim
+  ##   ```
   ##   "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 func<#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.
+  ## <#split.i,string,char,int>`_ except in **reverse** order.
   ##
-  ## .. code-block:: nim
-  ##   for piece in "foo:bar".rsplit(':'):
+  ##   ```nim
+  ##   for piece in "foo bar".rsplit(WhiteSpace):
   ##     echo piece
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block:: nim
+  ##   ```
   ##   "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`
+  ##
+  ##  .. note:: Empty separator set results in returning an original string,
+  ##   following the interpretation "split by no element".
+  ##
+  ## See also:
+  ## * `split iterator<#split.i,string,set[char],int>`_
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
+  ## * `rsplit func<#rsplit,string,set[char],int>`_
+  rsplitCommon(s, seps, maxsplit, 1)
 
 iterator rsplit*(s: string, sep: string, maxsplit: int = -1,
                  keepSeparators: bool = false): string =
   ## Splits the string `s` into substrings from the right using a
   ## string separator. Works exactly the same as `split iterator
-  ## <#split.i,string,string,int>`_ except in reverse order.
+  ## <#split.i,string,string,int>`_ except in **reverse** order.
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   for piece in "foothebar".rsplit("the"):
   ##     echo piece
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block:: nim
+  ##   ```
   ##   "bar"
   ##   "foo"
+  ##   ```
   ##
   ## Substrings are separated from the right by the string `sep`
-  rsplitCommon(s, sep, maxsplit, sep.len)
+  ##
+  ##  .. note:: Empty separator string results in returning an original string,
+  ##   following the interpretation "split by no element".
+  ##
+  ## See also:
+  ## * `split iterator<#split.i,string,string,int>`_
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
+  ## * `rsplit func<#rsplit,string,string,int>`_
+  let sepLen = if sep.len == 0: 1 # prevents infinite loop
+    else: sep.len
+  rsplitCommon(s, sep, maxsplit, sepLen)
 
-iterator splitLines*(s: string): string =
+iterator splitLines*(s: string, keepEol = false): string =
   ## Splits the string `s` into its containing lines.
   ##
-  ## Every `character literal <manual.html#character-literals>`_ newline
-  ## combination (CR, LF, CR-LF) is supported. The result strings contain no
-  ## trailing ``\n``.
+  ## Every `character literal <manual.html#lexical-analysis-character-literals>`_
+  ## newline combination (CR, LF, CR-LF) is supported. The result strings
+  ## contain no trailing end of line characters unless the parameter `keepEol`
+  ## is set to `true`.
   ##
   ## Example:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   for line in splitLines("\nthis\nis\nan\n\nexample\n"):
   ##     writeLine(stdout, line)
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   ""
   ##   "this"
   ##   "is"
@@ -647,87 +675,137 @@ iterator splitLines*(s: string): string =
   ##   ""
   ##   "example"
   ##   ""
+  ##   ```
+  ##
+  ## See also:
+  ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
+  ## * `splitLines func<#splitLines,string>`_
   var first = 0
   var last = 0
+  var eolpos = 0
   while true:
     while last < s.len and s[last] notin {'\c', '\l'}: inc(last)
-    yield substr(s, first, last-1)
-    # skip newlines:
-    if last >= s.len: break
-    if s[last] == '\l': inc(last)
-    elif s[last] == '\c':
-      inc(last)
-      if last < s.len and s[last] == '\l': inc(last)
-    first = last
 
-proc splitLines*(s: string): 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.
-  accumulateResult(splitLines(s))
+    eolpos = last
+    if last < s.len:
+      if s[last] == '\l': inc(last)
+      elif s[last] == '\c':
+        inc(last)
+        if last < s.len and s[last] == '\l': inc(last)
 
-proc countLines*(s: string): int {.noSideEffect,
-  rtl, extern: "nsuCountLines".} =
-  ## Returns the number of lines in the string `s`.
+    yield substr(s, first, if keepEol: last-1 else: eolpos-1)
+
+    # no eol characters consumed means that the string is over
+    if eolpos == last:
+      break
+
+    first = last
+
+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.
-  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
+  ##   ```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:
+  ##
+  ##   ```
+  ##   ------ 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 func<#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({' '}) == @[""]
-  accumulateResult(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.
+func split*(s: string, sep: char, maxsplit: int = -1): seq[string] {.rtl,
+    extern: "nsuSplitChar".} =
+  ## The same as the `split iterator <#split.i,string,char,int>`_ (see its
+  ## documentation), but is a func that returns a sequence of substrings.
+  ##
+  ## See also:
+  ## * `split iterator <#split.i,string,char,int>`_
+  ## * `rsplit func<#rsplit,string,char,int>`_
+  ## * `splitLines func<#splitLines,string>`_
+  ## * `splitWhitespace func<#splitWhitespace,string,int>`_
   runnableExamples:
     doAssert "a,b,c".split(',') == @["a", "b", "c"]
     doAssert "".split(' ') == @[""]
-  accumulateResult(split(s, sep, maxsplit))
+  accResult(split(s, sep, maxsplit))
 
-proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEffect,
-  rtl, extern: "nsuSplitString".} =
+func split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[
+    string] {.rtl, extern: "nsuSplitCharSet".} =
+  ## The same as the `split iterator <#split.i,string,set[char],int>`_ (see its
+  ## documentation), but is a func that returns a sequence of substrings.
+  ##
+  ##  .. note:: Empty separator set results in returning an original string,
+  ##   following the interpretation "split by no element".
+  ##
+  ## See also:
+  ## * `split iterator <#split.i,string,set[char],int>`_
+  ## * `rsplit func<#rsplit,string,set[char],int>`_
+  ## * `splitLines func<#splitLines,string>`_
+  ## * `splitWhitespace func<#splitWhitespace,string,int>`_
+  runnableExamples:
+    doAssert "a,b;c".split({',', ';'}) == @["a", "b", "c"]
+    doAssert "".split({' '}) == @[""]
+    doAssert "empty seps return unsplit s".split({}) == @["empty seps return unsplit s"]
+  accResult(split(s, seps, maxsplit))
+
+func split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.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>`_.
+  ##
+  ##  .. note:: Empty separator string results in returning an original string,
+  ##   following the interpretation "split by no element".
+  ##
+  ## See also:
+  ## * `split iterator <#split.i,string,string,int>`_
+  ## * `rsplit func<#rsplit,string,string,int>`_
+  ## * `splitLines func<#splitLines,string>`_
+  ## * `splitWhitespace func<#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)
+    doAssert "a  largely    spaced sentence".split(" ") == @["a", "", "largely",
+        "", "", "", "spaced", "sentence"]
+    doAssert "a  largely    spaced sentence".split(" ", maxsplit = 1) == @["a", " largely    spaced sentence"]
+    doAssert "empty sep returns unsplit s".split("") == @["empty sep returns unsplit s"]
+  accResult(split(s, sep, maxsplit))
 
-  accumulateResult(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.
+func rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string] {.rtl,
+    extern: "nsuRSplitChar".} =
+  ## The same as the `rsplit iterator <#rsplit.i,string,char,int>`_, but is a func
+  ## that returns a sequence of substrings in original order.
   ##
   ## A possible common use case for `rsplit` is path manipulation,
   ## particularly on systems that don't use a common delimiter.
@@ -735,21 +813,29 @@ proc rsplit*(s: string, seps: set[char] = Whitespace,
   ## For example, if a system had `#` as a delimiter, you could
   ## do the following to get the tail of the path:
   ##
-  ## .. code-block:: nim
-  ##   var tailSplit = rsplit("Root#Object#Method#Index", {'#'}, maxsplit=1)
+  ##   ```nim
+  ##   var tailSplit = rsplit("Root#Object#Method#Index", '#', maxsplit=1)
+  ##   ```
   ##
   ## Results in `tailSplit` containing:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   @["Root#Object#Method", "Index"]
-  ##
-  accumulateResult(rsplit(s, seps, maxsplit))
+  ##   ```
+  ##
+  ## See also:
+  ## * `rsplit iterator <#rsplit.i,string,char,int>`_
+  ## * `split func<#split,string,char,int>`_
+  ## * `splitLines func<#splitLines,string>`_
+  ## * `splitWhitespace func<#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.
+func rsplit*(s: string, seps: set[char] = Whitespace,
+             maxsplit: int = -1): seq[string]
+             {.rtl, extern: "nsuRSplitCharSet".} =
+  ## The same as the `rsplit iterator <#rsplit.i,string,set[char],int>`_, but is a
+  ## func that returns a sequence of substrings in original order.
   ##
   ## A possible common use case for `rsplit` is path manipulation,
   ## particularly on systems that don't use a common delimiter.
@@ -757,21 +843,31 @@ proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string]
   ## For example, if a system had `#` as a delimiter, you could
   ## do the following to get the tail of the path:
   ##
-  ## .. code-block:: nim
-  ##   var tailSplit = rsplit("Root#Object#Method#Index", '#', maxsplit=1)
+  ##   ```nim
+  ##   var tailSplit = rsplit("Root#Object#Method#Index", {'#'}, maxsplit=1)
+  ##   ```
   ##
   ## Results in `tailSplit` containing:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   @["Root#Object#Method", "Index"]
+  ##   ```
+  ##
+  ##  .. note:: Empty separator set results in returning an original string,
+  ##   following the interpretation "split by no element".
   ##
-  accumulateResult(rsplit(s, sep, maxsplit))
+  ## See also:
+  ## * `rsplit iterator <#rsplit.i,string,set[char],int>`_
+  ## * `split func<#split,string,set[char],int>`_
+  ## * `splitLines func<#splitLines,string>`_
+  ## * `splitWhitespace func<#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
-  ## that returns a sequence of substrings.
+func rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string] {.rtl,
+    extern: "nsuRSplitString".} =
+  ## The same as the `rsplit iterator <#rsplit.i,string,string,int,bool>`_, but is a func
+  ## that returns a sequence of substrings in original order.
   ##
   ## A possible common use case for `rsplit` is path manipulation,
   ## particularly on systems that don't use a common delimiter.
@@ -779,50 +875,167 @@ proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string]
   ## For example, if a system had `#` as a delimiter, you could
   ## do the following to get the tail of the path:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   var tailSplit = rsplit("Root#Object#Method#Index", "#", maxsplit=1)
+  ##   ```
   ##
   ## Results in `tailSplit` containing:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   @["Root#Object#Method", "Index"]
+  ##   ```
+  ##
+  ##  .. note:: Empty separator string results in returning an original string,
+  ##   following the interpretation "split by no element".
   ##
+  ## See also:
+  ## * `rsplit iterator <#rsplit.i,string,string,int,bool>`_
+  ## * `split func<#split,string,string,int>`_
+  ## * `splitLines func<#splitLines,string>`_
+  ## * `splitWhitespace func<#splitWhitespace,string,int>`_
   runnableExamples:
-    doAssert "a  largely    spaced sentence".rsplit(" ", maxsplit=1) == @["a  largely    spaced", "sentence"]
-
+    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 "a man a plan a canal panama".rsplit("a ") == @["", "man ",
+        "plan ", "canal panama"]
     doAssert "".rsplit("Elon Musk") == @[""]
-    doAssert "a  largely    spaced sentence".rsplit(" ") == @["a", "", "largely", "", "", "", "spaced", "sentence"]
-  accumulateResult(rsplit(s, sep, maxsplit))
+    doAssert "a  largely    spaced sentence".rsplit(" ") == @["a", "",
+        "largely", "", "", "", "spaced", "sentence"]
+    doAssert "empty sep returns unsplit s".rsplit("") == @["empty sep returns unsplit s"]
+  accResult(rsplit(s, sep, maxsplit))
   result.reverse()
 
-proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect,
-  rtl, extern: "nsuToHex".} =
-  ## Converts `x` to its hexadecimal representation.
+func splitLines*(s: string, keepEol = false): seq[string] {.rtl,
+    extern: "nsuSplitLines".} =
+  ## The same as the `splitLines iterator<#splitLines.i,string>`_ (see its
+  ## documentation), but is a func that returns a sequence of substrings.
   ##
-  ## The resulting string will be exactly `len` characters long. No prefix like
-  ## ``0x`` is generated. `x` is treated as an unsigned value.
+  ## See also:
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace func<#splitWhitespace,string,int>`_
+  ## * `countLines func<#countLines,string>`_
+  accResult(splitLines(s, keepEol = keepEol))
+
+func splitWhitespace*(s: string, maxsplit: int = -1): seq[string] {.rtl,
+    extern: "nsuSplitWhitespace".} =
+  ## The same as the `splitWhitespace iterator <#splitWhitespace.i,string,int>`_
+  ## (see its documentation), but is a func that returns a sequence of substrings.
+  ##
+  ## See also:
+  ## * `splitWhitespace iterator <#splitWhitespace.i,string,int>`_
+  ## * `splitLines func<#splitLines,string>`_
+  accResult(splitWhitespace(s, maxsplit))
+
+func toBin*(x: BiggestInt, len: Positive): string {.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 = BiggestUInt 1
+    shift = BiggestUInt 0
+  assert(len > 0)
+  result = newString(len)
+  for j in countdown(len-1, 0):
+    result[j] = chr(int((BiggestUInt(x) and mask) shr shift) + ord('0'))
+    inc shift
+    mask = mask shl BiggestUInt(1)
+
+func toOct*(x: BiggestInt, len: Positive): string {.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 func<#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 = BiggestUInt 7
+    shift = BiggestUInt 0
+  assert(len > 0)
+  result = newString(len)
+  for j in countdown(len-1, 0):
+    result[j] = chr(int((BiggestUInt(x) and mask) shr shift) + ord('0'))
+    inc shift, 3
+    mask = mask shl BiggestUInt(3)
+
+func toHexImpl(x: BiggestUInt, len: Positive, handleNegative: bool): string =
   const
     HexChars = "0123456789ABCDEF"
-  var
-    n = x
+  var n = x
   result = newString(len)
   for j in countdown(len-1, 0):
     result[j] = HexChars[int(n and 0xF)]
     n = n shr 4
     # handle negative overflow
-    if n == 0 and x < 0: n = -1
+    if n == 0 and handleNegative: n = not(BiggestUInt 0)
+
+func toHex*[T: SomeInteger](x: T, len: Positive): string =
+  ## Converts `x` to its hexadecimal representation.
+  ##
+  ## The resulting string will be exactly `len` characters long. No prefix like
+  ## `0x` is generated. `x` is treated as an unsigned value.
+  runnableExamples:
+    let
+      a = 62'u64
+      b = 4097'u64
+    doAssert a.toHex(3) == "03E"
+    doAssert b.toHex(3) == "001"
+    doAssert b.toHex(4) == "1001"
+    doAssert toHex(62, 3) == "03E"
+    doAssert toHex(-8, 6) == "FFFFF8"
+  whenJsNoBigInt64:
+    toHexImpl(cast[BiggestUInt](x), len, x < 0)
+  do:
+    when T is SomeSignedInt:
+      toHexImpl(cast[BiggestUInt](BiggestInt(x)), len, x < 0)
+    else:
+      toHexImpl(BiggestUInt(x), len, x < 0)
 
-proc toHex*[T](x: T): string =
-  ## Shortcut for ``toHex(x, T.sizeOf * 2)``
-  toHex(BiggestInt(x), T.sizeOf * 2)
+func toHex*[T: SomeInteger](x: T): string =
+  ## Shortcut for `toHex(x, T.sizeof * 2)`
+  runnableExamples:
+    doAssert toHex(1984'i64) == "00000000000007C0"
+    doAssert toHex(1984'i16) == "07C0"
+  whenJsNoBigInt64:
+    toHexImpl(cast[BiggestUInt](x), 2*sizeof(T), x < 0)
+  do:
+    when T is SomeSignedInt:
+      toHexImpl(cast[BiggestUInt](BiggestInt(x)), 2*sizeof(T), x < 0)
+    else:
+      toHexImpl(BiggestUInt(x), 2*sizeof(T), x < 0)
 
-proc toHex*(s: string): string {.noSideEffect, rtl.} =
+func toHex*(s: string): string {.rtl.} =
   ## Converts a bytes string to its hexadecimal representation.
   ##
   ## The output is twice the input long. No prefix like
-  ## ``0x`` is generated.
+  ## `0x` is generated.
+  ##
+  ## See also:
+  ## * `parseHexStr func<#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:
@@ -831,120 +1044,234 @@ proc toHex*(s: string): string {.noSideEffect, rtl.} =
     n = n shr 4
     result[pos * 2] = HexChars[n]
 
-proc intToStr*(x: int, minchars: Positive = 1): string {.noSideEffect,
-  rtl, extern: "nsuIntToStr".} =
+func toOctal*(c: char): string {.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 func<#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
+
+func fromBin*[T: SomeInteger](s: string): T =
+  ## Parses a binary integer value from a string `s`.
+  ##
+  ## 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.
+  ##
+  ## Does not check for overflow. If the value represented by `s`
+  ## is too big to fit into a return type, only the value of the rightmost
+  ## binary digits of `s` is returned without producing an error.
+  runnableExamples:
+    let s = "0b_0100_1000_1000_1000_1110_1110_1001_1001"
+    doAssert fromBin[int](s) == 1216933529
+    doAssert fromBin[int8](s) == 0b1001_1001'i8
+    doAssert fromBin[int8](s) == -103'i8
+    doAssert fromBin[uint8](s) == 153
+    doAssert s.fromBin[:int16] == 0b1110_1110_1001_1001'i16
+    doAssert s.fromBin[:uint64] == 1216933529'u64
+
+  let p = parseutils.parseBin(s, result)
+  if p != s.len or p == 0:
+    raise newException(ValueError, "invalid binary integer: " & s)
+
+func fromOct*[T: SomeInteger](s: string): T =
+  ## Parses an octal integer value from a string `s`.
+  ##
+  ## If `s` is not a valid octal integer, `ValueError` is raised. `s` can have
+  ## one of the following optional prefixes: `0o`, `0O`. Underscores within
+  ## `s` are ignored.
+  ##
+  ## Does not check for overflow. If the value represented by `s`
+  ## is too big to fit into a return type, only the value of the rightmost
+  ## octal digits of `s` is returned without producing an error.
+  runnableExamples:
+    let s = "0o_123_456_777"
+    doAssert fromOct[int](s) == 21913087
+    doAssert fromOct[int8](s) == 0o377'i8
+    doAssert fromOct[int8](s) == -1'i8
+    doAssert fromOct[uint8](s) == 255'u8
+    doAssert s.fromOct[:int16] == 24063'i16
+    doAssert s.fromOct[:uint64] == 21913087'u64
+
+  let p = parseutils.parseOct(s, result)
+  if p != s.len or p == 0:
+    raise newException(ValueError, "invalid oct integer: " & s)
+
+func fromHex*[T: SomeInteger](s: string): T =
+  ## Parses a hex integer value from a string `s`.
+  ##
+  ## If `s` is not a valid hex integer, `ValueError` is raised. `s` can have
+  ## one of the following optional prefixes: `0x`, `0X`, `#`. Underscores within
+  ## `s` are ignored.
+  ##
+  ## Does not check for overflow. If the value represented by `s`
+  ## is too big to fit into a return type, only the value of the rightmost
+  ## hex digits of `s` is returned without producing an error.
+  runnableExamples:
+    let s = "0x_1235_8df6"
+    doAssert fromHex[int](s) == 305499638
+    doAssert fromHex[int8](s) == 0xf6'i8
+    doAssert fromHex[int8](s) == -10'i8
+    doAssert fromHex[uint8](s) == 246'u8
+    doAssert s.fromHex[:int16] == -29194'i16
+    doAssert s.fromHex[:uint64] == 305499638'u64
+
+  let p = parseutils.parseHex(s, result)
+  if p != s.len or p == 0:
+    raise newException(ValueError, "invalid hex integer: " & s)
+
+func intToStr*(x: int, minchars: Positive = 1): string {.rtl,
+    extern: "nsuIntToStr".} =
   ## Converts `x` to its decimal representation.
   ##
   ## The resulting string will be minimally `minchars` characters long. This is
   ## achieved by adding leading zeros.
+  runnableExamples:
+    doAssert intToStr(1984) == "1984"
+    doAssert intToStr(1984, 6) == "001984"
   result = $abs(x)
   for i in 1 .. minchars - len(result):
     result = '0' & result
   if x < 0:
     result = '-' & result
 
-proc parseInt*(s: string): int {.noSideEffect, procvar,
-  rtl, extern: "nsuParseInt".} =
+func parseInt*(s: string): int {.rtl, extern: "nsuParseInt".} =
   ## Parses a decimal integer value contained in `s`.
   ##
   ## If `s` is not a valid integer, `ValueError` is raised.
+  runnableExamples:
+    doAssert parseInt("-0042") == -42
+  result = 0
   let L = parseutils.parseInt(s, result, 0)
   if L != s.len or L == 0:
     raise newException(ValueError, "invalid integer: " & s)
 
-proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, procvar,
-  rtl, extern: "nsuParseBiggestInt".} =
+func parseBiggestInt*(s: string): BiggestInt {.rtl,
+    extern: "nsuParseBiggestInt".} =
   ## Parses a decimal integer value contained in `s`.
   ##
   ## If `s` is not a valid integer, `ValueError` is raised.
+  result = BiggestInt(0)
   let L = parseutils.parseBiggestInt(s, result, 0)
   if L != s.len or L == 0:
     raise newException(ValueError, "invalid integer: " & s)
 
-proc parseUInt*(s: string): uint {.noSideEffect, procvar,
-  rtl, extern: "nsuParseUInt".} =
+func parseUInt*(s: string): uint {.rtl, extern: "nsuParseUInt".} =
   ## Parses a decimal unsigned integer value contained in `s`.
   ##
   ## If `s` is not a valid integer, `ValueError` is raised.
+  result = uint(0)
   let L = parseutils.parseUInt(s, result, 0)
   if L != s.len or L == 0:
     raise newException(ValueError, "invalid unsigned integer: " & s)
 
-proc parseBiggestUInt*(s: string): BiggestUInt {.noSideEffect, procvar,
-  rtl, extern: "nsuParseBiggestUInt".} =
+func parseBiggestUInt*(s: string): BiggestUInt {.rtl,
+    extern: "nsuParseBiggestUInt".} =
   ## Parses a decimal unsigned integer value contained in `s`.
   ##
   ## If `s` is not a valid integer, `ValueError` is raised.
+  result = BiggestUInt(0)
   let L = parseutils.parseBiggestUInt(s, result, 0)
   if L != s.len or L == 0:
     raise newException(ValueError, "invalid unsigned integer: " & s)
 
-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).
+func parseFloat*(s: string): float {.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).
+  runnableExamples:
+    doAssert parseFloat("3.14") == 3.14
+    doAssert parseFloat("inf") == 1.0/0
+  result = 0.0
   let L = parseutils.parseFloat(s, result, 0)
   if L != s.len or L == 0:
     raise newException(ValueError, "invalid float: " & s)
 
-proc parseBinInt*(s: string): int {.noSideEffect, procvar,
-  rtl, extern: "nsuParseBinInt".} =
+func parseBinInt*(s: string): int {.rtl, extern: "nsuParseBinInt".} =
   ## Parses a binary integer value contained in `s`.
   ##
   ## If `s` is not a valid binary integer, `ValueError` is raised. `s` can have
-  ## one of the following optional prefixes: ``0b``, ``0B``. Underscores within
+  ## 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
+
+  result = 0
   let L = parseutils.parseBin(s, result, 0)
   if L != s.len or L == 0:
     raise newException(ValueError, "invalid binary integer: " & s)
 
-proc parseOctInt*(s: string): int {.noSideEffect,
-  rtl, extern: "nsuParseOctInt".} =
+func parseOctInt*(s: string): int {.rtl, extern: "nsuParseOctInt".} =
   ## Parses an octal integer value contained in `s`.
   ##
   ## If `s` is not a valid oct integer, `ValueError` is raised. `s` can have one
-  ## of the following optional prefixes: ``0o``, ``0O``.  Underscores within
+  ## of the following optional prefixes: `0o`, `0O`.  Underscores within
   ## `s` are ignored.
+  result = 0
   let L = parseutils.parseOct(s, result, 0)
   if L != s.len or L == 0:
-    raise newException(ValueError, "invalid oct integer: " & s)  
+    raise newException(ValueError, "invalid oct integer: " & s)
 
-proc parseHexInt*(s: string): int {.noSideEffect, procvar,
-  rtl, extern: "nsuParseHexInt".} =
+func parseHexInt*(s: string): int {.rtl, extern: "nsuParseHexInt".} =
   ## Parses a hexadecimal integer value contained in `s`.
   ##
   ## If `s` is not a valid hex integer, `ValueError` is raised. `s` can have one
-  ## of the following optional prefixes: ``0x``, ``0X``, ``#``.  Underscores
+  ## of the following optional prefixes: `0x`, `0X`, `#`.  Underscores
   ## within `s` are ignored.
+  result = 0
   let L = parseutils.parseHex(s, result, 0)
   if L != s.len or L == 0:
     raise newException(ValueError, "invalid hex integer: " & s)
 
-proc generateHexCharToValueMap(): string =
-  ## Generate a string to map a hex digit to uint value
+func generateHexCharToValueMap(): string =
+  ## Generates a string to map a hex digit to uint value.
   result = ""
   for inp in 0..255:
     let ch = chr(inp)
     let o =
-      case ch:
-        of '0'..'9': inp - ord('0')
-        of 'a'..'f': inp - ord('a') + 10
-        of 'A'..'F': inp - ord('A') + 10
-        else: 17  # indicates an invalid hex char
+      case ch
+      of '0'..'9': inp - ord('0')
+      of 'a'..'f': inp - ord('a') + 10
+      of 'A'..'F': inp - ord('A') + 10
+      else: 17 # indicates an invalid hex char
     result.add chr(o)
 
 const hexCharToValueMap = generateHexCharToValueMap()
 
-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"
+func parseHexStr*(s: string): string {.rtl, extern: "nsuParseHexStr".} =
+  ## Converts hex-encoded string to byte string, e.g.:
   ##
-  ## Raises ``ValueError`` for an invalid hex values. The comparison is
+  ## Raises `ValueError` for an invalid hex values. The comparison is
   ## case-insensitive.
+  ##
+  ## See also:
+  ## * `toHex func<#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)
@@ -952,95 +1279,124 @@ proc parseHexStr*(s: string): string {.noSideEffect, procvar,
   for pos, c in s:
     let val = hexCharToValueMap[ord(c)].ord
     if val == 17:
-      raise newException(ValueError, "Invalid hex char " & repr(c))
+      raise newException(ValueError, "Invalid hex char `" &
+                         c & "` (ord " & $c.ord & ")")
     if pos mod 2 == 0:
       buf = val
     else:
       result[pos div 2] = chr(val + buf shl 4)
 
-proc parseBool*(s: string): bool =
+func parseBool*(s: string): bool =
   ## Parses a value into a `bool`.
   ##
-  ## If ``s`` is one of the following values: ``y, yes, true, 1, on``, then
-  ## 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.
+  ## If `s` is one of the following values: `y, yes, true, 1, on`, then
+  ## 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
   else: raise newException(ValueError, "cannot interpret as a bool: " & s)
 
-proc parseEnum*[T: enum](s: string): T =
-  ## Parses an enum ``T``.
+func parseEnum*[T: enum](s: string): T =
+  ## Parses an enum `T`. This errors at compile time, if the given enum
+  ## type contains multiple fields with the same string value.
   ##
-  ## Raises ``ValueError`` for an invalid value in `s`. The comparison is
-  ## done in a style insensitive way.
-  for e in low(T)..high(T):
-    if cmpIgnoreStyle(s, $e) == 0:
-      return e
-  raise newException(ValueError, "invalid enum value: " & s)
+  ## Raises `ValueError` for an invalid value in `s`. The comparison is
+  ## done in a style insensitive way (first letter is still case-sensitive).
+  runnableExamples:
+    type
+      MyEnum = enum
+        first = "1st",
+        second,
+        third = "3rd"
+
+    doAssert parseEnum[MyEnum]("1_st") == first
+    doAssert parseEnum[MyEnum]("second") == second
+    doAssertRaises(ValueError):
+      echo parseEnum[MyEnum]("third")
+
+  genEnumCaseStmt(T, s, default = nil, ord(low(T)), ord(high(T)), nimIdentNormalize)
 
-proc parseEnum*[T: enum](s: string, default: T): T =
-  ## Parses an enum ``T``.
+func parseEnum*[T: enum](s: string, default: T): T =
+  ## Parses an enum `T`. This errors at compile time, if the given enum
+  ## type contains multiple fields with the same string value.
   ##
   ## Uses `default` for an invalid value in `s`. The comparison is done in a
-  ## style insensitive way.
-  for e in low(T)..high(T):
-    if cmpIgnoreStyle(s, $e) == 0:
-      return e
-  result = default
-
-proc repeat*(c: char, count: Natural): string {.noSideEffect,
-  rtl, extern: "nsuRepeatChar".} =
+  ## style insensitive way (first letter is still case-sensitive).
+  runnableExamples:
+    type
+      MyEnum = enum
+        first = "1st",
+        second,
+        third = "3rd"
+
+    doAssert parseEnum[MyEnum]("1_st") == first
+    doAssert parseEnum[MyEnum]("second") == second
+    doAssert parseEnum[MyEnum]("last", third) == third
+
+  genEnumCaseStmt(T, s, default, ord(low(T)), ord(high(T)), nimIdentNormalize)
+
+func repeat*(c: char, count: Natural): string {.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), "+++"
+func repeat*(s: string, n: Natural): string {.rtl, extern: "nsuRepeatStr".} =
+  ## 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:
+func spaces*(n: Natural): string {.inline.} =
+  ## Returns a string with `n` space characters. You can use this func
+  ## 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)) & "|"
-
-proc align*(s: string, count: Natural, padding = ' '): string {.
-  noSideEffect, rtl, extern: "nsuAlignString".} =
+  ## See also:
+  ## * `align func<#align,string,Natural,char>`_
+  ## * `alignLeft func<#alignLeft,string,Natural,char>`_
+  ## * `indent func<#indent,string,Natural,string>`_
+  ## * `center func<#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)
+
+func align*(s: string, count: Natural, padding = ' '): string {.rtl,
+    extern: "nsuAlignString".} =
   ## Aligns a string `s` with `padding`, so that it is of length `count`.
   ##
   ## `padding` characters (by default spaces) are added before `s` resulting in
-  ## right alignment. If ``s.len >= count``, no spaces are added and `s` is
+  ## 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:
+  ## func<#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 func<#alignLeft,string,Natural,char>`_
+  ## * `spaces func<#spaces,Natural>`_
+  ## * `indent func<#indent,string,Natural,string>`_
+  ## * `center func<#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
@@ -1049,19 +1405,24 @@ proc align*(s: string, count: Natural, padding = ' '): string {.
   else:
     result = s
 
-proc alignLeft*(s: string, count: Natural, padding = ' '): string {.noSideEffect.} =
+func alignLeft*(s: string, count: Natural, padding = ' '): string =
   ## Left-Aligns a string `s` with `padding`, so that it is of length `count`.
   ##
   ## `padding` characters (by default spaces) are added after `s` resulting in
-  ## left alignment. If ``s.len >= count``, no spaces are added and `s` is
+  ## 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:
+  ## func<#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 func<#align,string,Natural,char>`_
+  ## * `spaces func<#spaces,Natural>`_
+  ## * `indent func<#indent,string,Natural,string>`_
+  ## * `center func<#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:
@@ -1071,80 +1432,56 @@ 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)
+func center*(s: string, width: int, fillChar: char = ' '): string {.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".} =
-  ## 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 func<#align,string,Natural,char>`_
+  ## * `alignLeft func<#alignLeft,string,Natural,char>`_
+  ## * `spaces func<#spaces,Natural>`_
+  ## * `indent func<#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``.
+func indent*(s: string, count: Natural, padding: string = " "): string {.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``.
+  ## **Note:** This does not preserve the new line characters used in `s`.
+  ##
+  ## See also:
+  ## * `align func<#align,string,Natural,char>`_
+  ## * `alignLeft func<#alignLeft,string,Natural,char>`_
+  ## * `spaces func<#spaces,Natural>`_
+  ## * `unindent func<#unindent,string,Natural,string>`_
+  ## * `dedent func<#dedent,string,Natural>`_
+  runnableExamples:
+    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():
@@ -1155,11 +1492,25 @@ proc indent*(s: string, count: Natural, padding: string = " "): string
     result.add(line)
     i.inc
 
-proc unindent*(s: string, count: Natural, padding: string = " "): string
-    {.noSideEffect, rtl, extern: "nsuUnindent".} =
-  ## Unindents each line in ``s`` by ``count`` amount of ``padding``.
+func unindent*(s: string, count: Natural = int.high,
+               padding: string = " "): string {.rtl, extern: "nsuUnindent".} =
+  ## Unindents each line in `s` by `count` amount of `padding`.
+  ##
+  ## **Note:** This does not preserve the new line characters used in `s`.
   ##
-  ## **Note:** This does not preserve the new line characters used in ``s``.
+  ## See also:
+  ## * `dedent func<#dedent,string,Natural>`_
+  ## * `align func<#align,string,Natural,char>`_
+  ## * `alignLeft func<#alignLeft,string,Natural,char>`_
+  ## * `spaces func<#spaces,Natural>`_
+  ## * `indent func<#indent,string,Natural,string>`_
+  runnableExamples:
+    let x = """
+      Hello
+        There
+    """.unindent()
+
+    doAssert x == "Hello\nThere\n"
   result = ""
   var i = 0
   for line in s.splitLines():
@@ -1174,94 +1525,328 @@ proc unindent*(s: string, count: Natural, padding: string = " "): string
     result.add(line[indentCount*padding.len .. ^1])
     i.inc
 
-proc unindent*(s: string): string
-    {.noSideEffect, rtl, extern: "nsuUnindentAll".} =
-  ## Removes all indentation composed of whitespace from each line in ``s``.
-  ##
-  ## For example:
-  ##
-  ## .. code-block:: nim
-  ##   const x = """
-  ##     Hello
-  ##     There
-  ##   """.unindent()
-  ##
-  ##   doAssert x == "Hello\nThere\n"
-  unindent(s, 1000) # TODO: Passing a 1000 is a bit hackish.
+func indentation*(s: string): Natural {.since: (1, 3).} =
+  ## Returns the amount of indentation all lines of `s` have in common,
+  ## ignoring lines that consist only of whitespace.
+  result = int.high
+  for line in s.splitLines:
+    for i, c in line:
+      if i >= result: break
+      elif c != ' ':
+        result = i
+        break
+  if result == int.high:
+    result = 0
+
+func dedent*(s: string, count: Natural = indentation(s)): string {.rtl,
+    extern: "nsuDedent", since: (1, 3).} =
+  ## Unindents each line in `s` by `count` amount of `padding`.
+  ## The only difference between this and the
+  ## `unindent func<#unindent,string,Natural,string>`_ is that this by default
+  ## only cuts off the amount of indentation that all lines of `s` share as
+  ## opposed to all indentation. It only supports spaces as padding.
+  ##
+  ## **Note:** This does not preserve the new line characters used in `s`.
+  ##
+  ## See also:
+  ## * `unindent func<#unindent,string,Natural,string>`_
+  ## * `align func<#align,string,Natural,char>`_
+  ## * `alignLeft func<#alignLeft,string,Natural,char>`_
+  ## * `spaces func<#spaces,Natural>`_
+  ## * `indent func<#indent,string,Natural,string>`_
+  runnableExamples:
+    let x = """
+      Hello
+        There
+    """.dedent()
+
+    doAssert x == "Hello\n  There\n"
+  unindent(s, count, " ")
 
-proc startsWith*(s, prefix: string): bool {.noSideEffect,
-  rtl, extern: "nsuStartsWith".} =
-  ## Returns true iff ``s`` starts with ``prefix``.
+func delete*(s: var string, slice: Slice[int]) =
+  ## Deletes the items `s[slice]`, raising `IndexDefect` if the slice contains
+  ## elements out of range.
   ##
-  ## If ``prefix == ""`` true is returned.
-  var i = 0
-  while true:
-    if i >= prefix.len: return true
-    if i >= s.len or s[i] != prefix[i]: return false
+  ## This operation moves all elements after `s[slice]` in linear time, and
+  ## is the string analog to `sequtils.delete`.
+  runnableExamples:
+    var a = "abcde"
+    doAssertRaises(IndexDefect): a.delete(4..5)
+    assert a == "abcde"
+    a.delete(4..4)
+    assert a == "abcd"
+    a.delete(1..2)
+    assert a == "ad"
+    a.delete(1..<1) # empty slice
+    assert a == "ad"
+  when compileOption("boundChecks"):
+    if not (slice.a < s.len and slice.a >= 0 and slice.b < s.len):
+      raise newException(IndexDefect, $(slice: slice, len: s.len))
+  if slice.b >= slice.a:
+    var i = slice.a
+    var j = slice.b + 1
+    var newLen = s.len - j + i
+    # if j < s.len: moveMem(addr s[i], addr s[j], s.len - j) # pending benchmark
+    while i < newLen:
+      s[i] = s[j]
+      inc(i)
+      inc(j)
+    setLen(s, newLen)
+
+func delete*(s: var string, first, last: int) {.rtl, extern: "nsuDelete",
+    deprecated: "use `delete(s, first..last)`".} =
+  ## Deletes in `s` the characters at positions `first .. last` (both ends included).
+  runnableExamples("--warning:deprecated:off"):
+    var a = "abracadabra"
+
+    a.delete(4, 5)
+    doAssert a == "abradabra"
+
+    a.delete(1, 6)
+    doAssert a == "ara"
+
+    a.delete(2, 999)
+    doAssert a == "ar"
+
+  var i = first
+  var j = min(len(s), 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 iff ``s`` starts with ``prefix``.
+func startsWith*(s: string, prefix: char): bool {.inline.} =
+  ## Returns true if `s` starts with character `prefix`.
+  ##
+  ## See also:
+  ## * `endsWith func<#endsWith,string,char>`_
+  ## * `continuesWith func<#continuesWith,string,string,Natural>`_
+  ## * `removePrefix func<#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 endsWith*(s, suffix: string): bool {.noSideEffect,
-  rtl, extern: "nsuEndsWith".} =
-  ## Returns true iff ``s`` ends with ``suffix``.
+func startsWith*(s, prefix: string): bool {.rtl, extern: "nsuStartsWith".} =
+  ## Returns true if `s` starts with string `prefix`.
   ##
-  ## If ``suffix == ""`` true is returned.
-  var i = 0
-  var j = len(s) - len(suffix)
-  while i+j <% s.len:
-    if s[i+j] != suffix[i]: return false
-    inc(i)
-  if i >= suffix.len: return true
-
-proc endsWith*(s: string, suffix: char): bool {.noSideEffect, inline.} =
-  ## Returns true iff ``s`` ends with ``suffix``.
+  ## If `prefix == ""` true is returned.
+  ##
+  ## See also:
+  ## * `endsWith func<#endsWith,string,string>`_
+  ## * `continuesWith func<#continuesWith,string,string,Natural>`_
+  ## * `removePrefix func<#removePrefix,string,string>`_
+  runnableExamples:
+    let a = "abracadabra"
+    doAssert a.startsWith("abra") == true
+    doAssert a.startsWith("bra") == false
+  startsWithImpl(s, prefix)
+
+func endsWith*(s: string, suffix: char): bool {.inline.} =
+  ## Returns true if `s` ends with `suffix`.
+  ##
+  ## See also:
+  ## * `startsWith func<#startsWith,string,char>`_
+  ## * `continuesWith func<#continuesWith,string,string,Natural>`_
+  ## * `removeSuffix func<#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 continuesWith*(s, substr: string, start: Natural): bool {.noSideEffect,
-  rtl, extern: "nsuContinuesWith".} =
-  ## Returns true iff ``s`` continues with ``substr`` at position ``start``.
+func endsWith*(s, suffix: string): bool {.rtl, extern: "nsuEndsWith".} =
+  ## Returns true if `s` ends with `suffix`.
+  ##
+  ## If `suffix == ""` true is returned.
+  ##
+  ## See also:
+  ## * `startsWith func<#startsWith,string,string>`_
+  ## * `continuesWith func<#continuesWith,string,string,Natural>`_
+  ## * `removeSuffix func<#removeSuffix,string,string>`_
+  runnableExamples:
+    let a = "abracadabra"
+    doAssert a.endsWith("abra") == true
+    doAssert a.endsWith("dab") == false
+  endsWithImpl(s, suffix)
+
+func continuesWith*(s, substr: string, start: Natural): bool {.rtl,
+    extern: "nsuContinuesWith".} =
+  ## Returns true if `s` continues with `substr` at position `start`.
   ##
-  ## If ``substr == ""`` true is returned.
+  ## If `substr == ""` true is returned.
+  ##
+  ## See also:
+  ## * `startsWith func<#startsWith,string,string>`_
+  ## * `endsWith func<#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 addSep*(dest: var string, sep = ", ", startLen: Natural = 0)
-  {.noSideEffect, inline.} =
+
+func 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 func<#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)
+
+func 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 func<#removeSuffix,string,char>`_
+  ## * `startsWith func<#startsWith,string,char>`_
+  runnableExamples:
+    var ident = "pControl"
+    ident.removePrefix('p')
+    doAssert ident == "Control"
+  removePrefix(s, chars = {c})
+
+func removePrefix*(s: var string, prefix: string) {.rtl,
+    extern: "nsuRemovePrefixString".} =
+  ## Remove the first matching prefix (in-place) from a string.
+  ##
+  ## See also:
+  ## * `removeSuffix func<#removeSuffix,string,string>`_
+  ## * `startsWith func<#startsWith,string,string>`_
+  runnableExamples:
+    var answers = "yesyes"
+    answers.removePrefix("yes")
+    doAssert answers == "yes"
+  if s.startsWith(prefix) and prefix.len > 0:
+    s.delete(0..prefix.len - 1)
+
+func 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 func<#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)
+
+func 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 func<#removePrefix,string,char>`_
+  ## * `endsWith func<#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})
+
+func removeSuffix*(s: var string, suffix: string) {.rtl,
+    extern: "nsuRemoveSuffixString".} =
+  ## Remove the first matching suffix (in-place) from a string.
+  ##
+  ## See also:
+  ## * `removePrefix func<#removePrefix,string,string>`_
+  ## * `endsWith func<#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)
+
+
+func addSep*(dest: var string, sep = ", ", startLen: Natural = 0) {.inline.} =
   ## Adds a separator to `dest` only if its length is bigger than `startLen`.
   ##
   ## A shorthand for:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   if dest.len > startLen: add(dest, sep)
+  ##   ```
   ##
   ## This is often useful for generating some code where the items need to
   ## be *separated* by `sep`. `sep` is only added if `dest` is longer than
   ## `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`.
+func allCharsInSet*(s: string, theSet: set[char]): bool =
+  ## 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` if not ambiguous.
+func abbrev*(s: string, possibilities: openArray[string]): int =
+  ## 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:
+    doAssert abbrev("fac", ["college", "faculty", "industry"]) == 1
+    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):
@@ -1273,9 +1858,12 @@ 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`.
+func join*(a: openArray[string], sep: string = ""): string {.rtl,
+    extern: "nsuJoinSep".} =
+  ## 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)
@@ -1287,10 +1875,12 @@ proc join*(a: openArray[string], sep: string = ""): string {.
   else:
     result = ""
 
-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`.
+proc join*[T: not string](a: openArray[T], sep: string = ""): string =
+  ## 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:
@@ -1298,37 +1888,44 @@ proc join*[T: not string](a: openArray[T], sep: string = ""): string {.
     add(result, $x)
 
 type
-  SkipTable* = array[char, int]
+  SkipTable* = array[char, int] ## Character table for efficient substring search.
 
-proc initSkipTable*(a: var SkipTable, sub: string)
-  {.noSideEffect, rtl, extern: "nsuInitSkipTable".} =
-  ## Preprocess table `a` for `sub`.
+func initSkipTable*(a: var SkipTable, sub: string) {.rtl,
+    extern: "nsuInitSkipTable".} =
+  ## Initializes table `a` for efficient search of substring `sub`.
+  ##
+  ## See also:
+  ## * `initSkipTable func<#initSkipTable,string>`_
+  ## * `find func<#find,SkipTable,string,string,Natural,int>`_
+  # TODO: this should be the `default()` initializer for the type.
   let m = len(sub)
-  var i = 0
-  while i <= 0xff-7:
-    a[chr(i + 0)] = m
-    a[chr(i + 1)] = m
-    a[chr(i + 2)] = m
-    a[chr(i + 3)] = m
-    a[chr(i + 4)] = m
-    a[chr(i + 5)] = m
-    a[chr(i + 6)] = m
-    a[chr(i + 7)] = m
-    i += 8
+  fill(a, m)
 
   for i in 0 ..< m - 1:
     a[sub[i]] = m - 1 - i
 
-proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last: Natural = 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`.
+func initSkipTable*(sub: string): SkipTable {.noinit, rtl,
+    extern: "nsuInitNewSkipTable".} =
+  ## Returns a new table initialized for `sub`.
   ##
-  ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
+  ## See also:
+  ## * `initSkipTable func<#initSkipTable,SkipTable,string>`_
+  ## * `find func<#find,SkipTable,string,string,Natural,int>`_
+  initSkipTable(result, sub)
 
+func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = -1): int {.
+    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` (the last
+  ## element).
+  ##
+  ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
+  ##
+  ## See also:
+  ## * `initSkipTable func<#initSkipTable,string>`_
+  ## * `initSkipTable func<#initSkipTable,SkipTable,string>`_
   let
-    last = if last==0: s.high else: last
-    sLen = last - start + 1
+    last = if last < 0: s.high else: last
     subLast = sub.len - 1
 
   if subLast == -1:
@@ -1338,6 +1935,7 @@ proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last: Natural = 0):
 
   # This is an implementation of the Boyer-Moore Horspool algorithms
   # https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm
+  result = -1
   var skip = start
 
   while last - skip >= subLast:
@@ -1348,65 +1946,162 @@ proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last: Natural = 0):
       dec i
     inc skip, a[s[skip + subLast]]
 
-  return -1
-
 when not (defined(js) or defined(nimdoc) or defined(nimscript)):
-  proc c_memchr(cstr: pointer, c: char, n: csize): pointer {.
-                importc: "memchr", header: "<string.h>" .}
+  func c_memchr(cstr: pointer, c: char, n: csize_t): pointer {.
+                importc: "memchr", header: "<string.h>".}
   const hasCStringBuiltin = true
 else:
   const hasCStringBuiltin = false
 
-proc find*(s: string, sub: char, start: Natural = 0, last: Natural = 0): int {.noSideEffect,
-  rtl, extern: "nsuFindChar".} =
-  ## Searches for `sub` in `s` inside range `start`..`last`.
-  ## If `last` is unspecified, it defaults to `s.high`.
+func find*(s: string, sub: char, start: Natural = 0, last = -1): int {.rtl,
+    extern: "nsuFindChar".} =
+  ## Searches for `sub` in `s` inside range `start..last` (both ends included).
+  ## If `last` is unspecified or negative, 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
+  ## Otherwise the index returned is relative to `s[0]`, not `start`.
+  ## Subtract `start` from the result for a `start`-origin index.
+  ##
+  ## See also:
+  ## * `rfind func<#rfind,string,char,Natural,int>`_
+  ## * `replace func<#replace,string,char,char>`_
+  result = -1
+  let last = if last < 0: s.high else: last
+
+  template findImpl =
+    for i in int(start)..last:
+      if s[i] == sub:
+        return i
+
   when nimvm:
-    for i in start..last:
-      if sub == s[i]: return i
+    findImpl()
   else:
     when hasCStringBuiltin:
-      let found = c_memchr(s[start].unsafeAddr, sub, last-start+1)
-      if not found.isNil:
-        return cast[ByteAddress](found) -% cast[ByteAddress](s.cstring)
+      let length = last-start+1
+      if length > 0:
+        let found = c_memchr(s[start].unsafeAddr, sub, cast[csize_t](length))
+        if not found.isNil:
+          return cast[int](found) -% cast[int](s.cstring)
     else:
-      for i in start..last:
-        if sub == s[i]: return i
-  return -1
+      findImpl()
 
-proc find*(s, sub: string, start: Natural = 0, last: Natural = 0): int {.noSideEffect,
-  rtl, extern: "nsuFindStr".} =
-  ## Searches for `sub` in `s` inside range `start`..`last`.
-  ## If `last` is unspecified, it defaults to `s.high`.
+func find*(s: string, chars: set[char], start: Natural = 0, last = -1): int {.
+    rtl, extern: "nsuFindCharSet".} =
+  ## Searches for `chars` in `s` inside range `start..last` (both ends included).
+  ## If `last` is unspecified or negative, it defaults to `s.high` (the last element).
+  ##
+  ## If `s` contains none of the characters in `chars`, -1 is returned.
+  ## Otherwise the index returned is relative to `s[0]`, not `start`.
+  ## Subtract `start` from the result for a `start`-origin index.
+  ##
+  ## See also:
+  ## * `rfind func<#rfind,string,set[char],Natural,int>`_
+  ## * `multiReplace func<#multiReplace,string,varargs[]>`_
+  result = -1
+  let last = if last < 0: s.high else: last
+  for i in int(start)..last:
+    if s[i] in chars:
+      return i
+
+when defined(linux):
+  proc memmem(haystack: pointer, haystacklen: csize_t,
+              needle: pointer, needlelen: csize_t): pointer {.importc, header: """#define _GNU_SOURCE
+#include <string.h>""".}
+elif defined(bsd) or (defined(macosx) and not defined(ios)):
+  proc memmem(haystack: pointer, haystacklen: csize_t,
+              needle: pointer, needlelen: csize_t): pointer {.importc, header: "#include <string.h>".}
+
+func find*(s, sub: string, start: Natural = 0, last = -1): int {.rtl,
+    extern: "nsuFindStr".} =
+  ## Searches for `sub` in `s` inside range `start..last` (both ends included).
+  ## If `last` is unspecified or negative, it defaults to `s.high` (the last element).
   ##
   ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
-  if sub.len > s.len: return -1
+  ## Otherwise the index returned is relative to `s[0]`, not `start`.
+  ## Subtract `start` from the result for a `start`-origin index.
+  ##
+  ## See also:
+  ## * `rfind func<#rfind,string,string,Natural,int>`_
+  ## * `replace func<#replace,string,string,string>`_
+  if sub.len > s.len - start: 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: Natural = 0): int {.noSideEffect,
-  rtl, extern: "nsuFindCharSet".} =
-  ## Searches for `chars` in `s` inside range `start`..`last`.
-  ## If `last` is unspecified, it defaults to `s.high`.
+  template useSkipTable =
+    result = find(initSkipTable(sub), s, sub, start, last)
+
+  when nimvm:
+    useSkipTable()
+  else:
+    when declared(memmem):
+      let subLen = sub.len
+      if last < 0 and start < s.len and subLen != 0:
+        let found = memmem(s[start].unsafeAddr, csize_t(s.len - start), sub.cstring, csize_t(subLen))
+        result = if not found.isNil:
+            cast[int](found) -% cast[int](s.cstring)
+          else:
+            -1
+      else:
+        useSkipTable()
+    else:
+      useSkipTable()
+
+func rfind*(s: string, sub: char, start: Natural = 0, last = -1): int {.rtl,
+    extern: "nsuRFindChar".} =
+  ## Searches for `sub` in `s` inside range `start..last` (both ends included)
+  ## in reverse -- starting at high indexes and moving lower to the first
+  ## character or `start`.  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.
+  ## Otherwise the index returned is relative to `s[0]`, not `start`.
+  ## Subtract `start` from the result for a `start`-origin index.
+  ##
+  ## See also:
+  ## * `find func<#find,string,char,Natural,int>`_
+  let last = if last == -1: s.high else: last
+  for i in countdown(last, start):
+    if sub == s[i]: return i
+  return -1
+
+func rfind*(s: string, chars: set[char], start: Natural = 0, last = -1): int {.
+    rtl, extern: "nsuRFindCharSet".} =
+  ## Searches for `chars` in `s` inside range `start..last` (both ends
+  ## included) in reverse -- starting at high indexes and moving lower to the
+  ## first character or `start`. If `last` is unspecified, it defaults to
+  ## `s.high` (the last element).
   ##
   ## If `s` contains none of the characters in `chars`, -1 is returned.
-  let last = if last==0: s.high else: last
-  for i in start..last:
+  ## Otherwise the index returned is relative to `s[0]`, not `start`.
+  ## Subtract `start` from the result for a `start`-origin index.
+  ##
+  ## See also:
+  ## * `find func<#find,string,set[char],Natural,int>`_
+  let last = if last == -1: s.high else: last
+  for i in countdown(last, start):
     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.
+func rfind*(s, sub: string, start: Natural = 0, last = -1): int {.rtl,
+    extern: "nsuRFindStr".} =
+  ## Searches for `sub` in `s` inside range `start..last` (both ends included)
+  ## included) in reverse -- starting at high indexes and moving lower to the
+  ## first character or `start`. 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 realStart = if start == -1: s.len else: start
-  for i in countdown(realStart-sub.len, 0):
+  ## Otherwise the index returned is relative to `s[0]`, not `start`.
+  ## Subtract `start` from the result for a `start`-origin index.
+  ##
+  ## See also:
+  ## * `find func<#find,string,string,Natural,int>`_
+  if sub.len == 0:
+    let rightIndex: Natural = if last < 0: s.len else: last
+    return max(start, rightIndex)
+  if sub.len > s.len - start:
+    return -1
+  let last = if last == -1: s.high else: last
+  result = 0
+  for i in countdown(last - sub.len + 1, start):
     for j in 0..sub.len-1:
       result = i
       if sub[j] != s[i+j]:
@@ -1415,54 +2110,37 @@ 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`.
+func count*(s: string, sub: char): int {.rtl, extern: "nsuCountChar".} =
+  ## Counts 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 func<#countLines,string>`_
+  result = 0
+  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.
+func count*(s: string, subs: set[char]): int {.rtl,
+    extern: "nsuCountCharSet".} =
+  ## Counts 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 func<#countLines,string>`_
+  doAssert card(subs) > 0
+  result = 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`.
+func count*(s: string, sub: string, overlapping: bool = false): int {.rtl,
+    extern: "nsuCountString".} =
+  ## Counts 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 func<#countLines,string>`_
+  doAssert sub.len > 0
+  result = 0
   var i = 0
   while true:
     i = s.find(sub, i)
@@ -1471,54 +2149,65 @@ 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`.
-  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`.
+func countLines*(s: string): int {.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 temporary objects. Every
+  ## `character literal <manual.html#lexical-analysis-character-literals>`_
+  ## newline combination (CR, LF, CR-LF) is supported.
+  ##
+  ## In this context, a line is any string separated by a newline combination.
+  ## A line can be an empty string.
   ##
-  ## **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
+  ## See also:
+  ## * `splitLines func<#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``.
+func contains*(s, sub: string): bool =
+  ## Same as `find(s, sub) >= 0`.
+  ##
+  ## See also:
+  ## * `find func<#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``.
+func contains*(s: string, chars: set[char]): bool =
+  ## Same as `find(s, chars) >= 0`.
+  ##
+  ## See also:
+  ## * `find func<#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`.
+func replace*(s, sub: string, by = ""): string {.rtl,
+    extern: "nsuReplaceStr".} =
+  ## Replaces every occurrence of the string `sub` in `s` with the string `by`.
+  ##
+  ## See also:
+  ## * `find func<#find,string,string,Natural,int>`_
+  ## * `replace func<#replace,string,char,char>`_ for replacing
+  ##   single characters
+  ## * `replaceWord func<#replaceWord,string,string,string>`_
+  ## * `multiReplace func<#multiReplace,string,varargs[]>`_
   result = ""
   let subLen = sub.len
   if subLen == 0:
-    for c in s:
-      add result, by
-      add result, c
-    add result, by
-    return
+    result = s
   elif subLen == 1:
     # when the pattern is a single char, we use a faster
     # char-based search that doesn't need a skip table:
-    var c = sub[0]
+    let c = sub[0]
     let last = s.high
     var i = 0
     while true:
@@ -1530,8 +2219,7 @@ proc replace*(s, sub: string, by = ""): string {.noSideEffect,
     # copy the rest:
     add result, substr(s, i)
   else:
-    var a {.noinit.}: SkipTable
-    initSkipTable(a, sub)
+    var a = initSkipTable(sub)
     let last = s.high
     var i = 0
     while true:
@@ -1543,11 +2231,18 @@ proc replace*(s, sub: string, by = ""): string {.noSideEffect,
     # copy the rest:
     add result, substr(s, i)
 
-proc replace*(s: string, sub, by: char): string {.noSideEffect,
-  rtl, extern: "nsuReplaceChar".} =
-  ## Replaces `sub` in `s` by the character `by`.
+func replace*(s: string, sub, by: char): string {.rtl,
+    extern: "nsuReplaceChar".} =
+  ## Replaces every occurrence of the character `sub` in `s` with the character
+  ## `by`.
+  ##
+  ## Optimized version of `replace <#replace,string,string,string>`_ for
+  ## characters.
   ##
-  ## Optimized version of `replace <#replace,string,string>`_ for characters.
+  ## See also:
+  ## * `find func<#find,string,char,Natural,int>`_
+  ## * `replaceWord func<#replaceWord,string,string,string>`_
+  ## * `multiReplace func<#multiReplace,string,varargs[]>`_
   result = newString(s.len)
   var i = 0
   while i < s.len:
@@ -1555,143 +2250,138 @@ proc replace*(s: string, sub, by: char): string {.noSideEffect,
     else: result[i] = s[i]
     inc(i)
 
-proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
-  rtl, extern: "nsuReplaceWord".} =
-  ## Replaces `sub` in `s` by the string `by`.
+func replaceWord*(s, sub: string, by = ""): string {.rtl,
+    extern: "nsuReplaceWord".} =
+  ## Replaces every occurrence of the string `sub` in `s` with 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'}
-  var a {.noinit.}: SkipTable
   result = ""
-  initSkipTable(a, sub)
+  var a = initSkipTable(sub)
   var i = 0
   let last = s.high
-  let sublen = max(sub.len, 1)
-  while true:
-    var j = find(a, s, sub, i, last)
-    if j < 0: break
-    # word boundary?
-    if (j == 0 or s[j-1] notin wordChars) and
-        (j+sub.len >= s.len or s[j+sub.len] notin wordChars):
-      add result, substr(s, i, j - 1)
-      add result, by
-      i = j + sublen
-    else:
-      add result, substr(s, i, j)
-      i = j + 1
-  # copy the rest:
-  add result, substr(s, i)
+  let sublen = sub.len
+  if sublen > 0:
+    while true:
+      var j = find(a, s, sub, i, last)
+      if j < 0: break
+      # word boundary?
+      if (j == 0 or s[j-1] notin wordChars) and
+          (j+sub.len >= s.len or s[j+sub.len] notin wordChars):
+        add result, substr(s, i, j - 1)
+        add result, by
+        i = j + sublen
+      else:
+        add result, substr(s, i, j)
+        i = j + 1
+    # copy the rest:
+    add result, substr(s, i)
 
-proc multiReplace*(s: string, replacements: varargs[(string, string)]): string {.noSideEffect.} =
-  ## Same as replace, but specialized for doing multiple replacements in a single
-  ## pass through the input string.
+func multiReplace*(s: string, replacements: varargs[(string, string)]): string =
+  ## Same as `replace<#replace,string,string,string>`_, but specialized for
+  ## doing multiple replacements in a single pass through the input string.
+  ##
+  ## `multiReplace` scans the input string from left to right and replaces the
+  ## matching substrings in the same order as passed in the argument list.
   ##
-  ## multiReplace performs all replacements in a single pass, this means it can be used
-  ## to swap the occurences of "a" and "b", for instance.
+  ## The implications of the order of scanning the string and matching the
+  ## replacements:
+  ##   - In case of multiple matches at a given position, the earliest
+  ##     replacement is applied.
+  ##   - Overlaps are not handled. After performing a replacement, the scan
+  ##     continues from the character after the matched substring. If the
+  ##     resulting string then contains a possible match starting in a newly
+  ##     placed substring, the additional replacement is not performed.
   ##
-  ## 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.
+  runnableExamples:
+    # Swapping occurrences of 'a' and 'b':
+    doAssert multireplace("abba", [("a", "b"), ("b", "a")]) == "baab"
+
+    # The second replacement ("ab") is matched and performed first, the scan then
+    # continues from 'c', so the "bc" replacement is never matched and thus skipped.
+    doAssert multireplace("abc", [("bc", "x"), ("ab", "_b")]) == "_bc"
   result = newStringOfCap(s.len)
   var i = 0
   var fastChk: set[char] = {}
-  for tup in replacements: fastChk.incl(tup[0][0]) # Include first character of all replacements
+  for sub, by in replacements.items:
+    if sub.len > 0:
+      # Include first character of all replacements
+      fastChk.incl sub[0]
   while i < s.len:
     block sIteration:
       # Assume most chars in s are not candidates for any replacement operation
       if s[i] in fastChk:
-        for tup in replacements:
-          if s.continuesWith(tup[0], i):
-            add result, tup[1]
-            inc(i, tup[0].len)
+        for sub, by in replacements.items:
+          if sub.len > 0 and s.continuesWith(sub, i):
+            add result, by
+            inc(i, sub.len)
             break sIteration
       # No matching replacement found
       # copy current character from s
       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.
+func insertSep*(s: string, sep = '_', digits = 3): string {.rtl,
+    extern: "nsuInsertSep".} =
+  ## 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.
   runnableExamples:
     doAssert insertSep("1000000") == "1_000_000"
-
-  var L = (s.len-1) div digits + s.len
-  result = newString(L)
+  result = newStringOfCap(s.len)
+  let hasPrefix = isDigit(s[s.low]) == false
+  var idx: int
+  if hasPrefix:
+    result.add s[s.low]
+    for i in (s.low + 1)..s.high:
+      idx = i
+      if not isDigit(s[i]):
+        result.add s[i]
+      else:
+        break
+  let partsLen = s.len - idx
+  var L = (partsLen-1) div digits + partsLen
+  result.setLen(L + idx)
   var j = 0
   dec(L)
-  for i in countdown(len(s)-1, 0):
+  for i in countdown(partsLen-1, 0):
     if j == digits:
-      result[L] = sep
+      result[L + idx] = sep
       dec(L)
       j = 0
-    result[L] = s[i]
+    result[L + idx] = s[i + idx]
     inc(j)
     dec(L)
 
-proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
-  rtl, extern: "nsuEscape", deprecated.} =
-  ## Escapes a string `s`. See `system.addEscapedChar <system.html#addEscapedChar>`_
-  ## for the escaping scheme.
+func escape*(s: string, prefix = "\"", suffix = "\""): string {.rtl,
+    extern: "nsuEscape".} =
+  ## Escapes a string `s`.
+  ##
+  ## .. note:: The escaping scheme is different from
+  ##    `system.addEscapedChar`.
+  ##
+  ## * replaces `'\0'..'\31'` and `'\127'..'\255'` by `\xHH` where `HH` is its hexadecimal value
+  ## * replaces ``\`` by `\\`
+  ## * replaces `'` by `\'`
+  ## * replaces `"` by `\"`
   ##
   ## The resulting string is prefixed with `prefix` and suffixed with `suffix`.
   ## Both may be empty strings.
   ##
-  ## **Warning:** This procedure is deprecated because it's to easy to missuse.
+  ## See also:
+  ## * `addEscapedChar proc<system.html#addEscapedChar,string,char>`_
+  ## * `unescape func<#unescape,string,string,string>`_ for the opposite
+  ##   operation
   result = newStringOfCap(s.len + s.len shr 2)
   result.add(prefix)
   for c in items(s):
@@ -1705,17 +2395,15 @@ proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
     else: add(result, c)
   add(result, suffix)
 
-proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
-  rtl, extern: "nsuUnescape", deprecated.} =
+func unescape*(s: string, prefix = "\"", suffix = "\""): string {.rtl,
+    extern: "nsuUnescape".} =
   ## Unescapes a string `s`.
   ##
-  ## This complements `escape <#escape>`_ as it performs the opposite
-  ## operations.
+  ## This complements `escape func<#escape,string,string,string>`_
+  ## as it performs the opposite operations.
   ##
-  ## If `s` does not begin with ``prefix`` and end with ``suffix`` a
+  ## If `s` does not begin with `prefix` and end with `suffix` a
   ## ValueError exception will be raised.
-  ##
-  ## **Warning:** This procedure is deprecated because it's to easy to missuse.
   result = newStringOfCap(s.len)
   var i = prefix.len
   if not s.startsWith(prefix):
@@ -1730,8 +2418,8 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
       case s[i+1]:
       of 'x':
         inc i, 2
-        var c: int
-        i += parseutils.parseHex(s, c, i, maxLen=2)
+        var c = 0
+        i += parseutils.parseHex(s, c, i, maxLen = 2)
         result.add(chr(c))
         dec i, 2
       of '\\':
@@ -1750,239 +2438,158 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
     raise newException(ValueError,
                        "String does not end in: " & suffix)
 
-proc validIdentifier*(s: string): bool {.noSideEffect,
-  rtl, extern: "nsuValidIdentifier".} =
+func validIdentifier*(s: string): bool {.rtl, extern: "nsuValidIdentifier".} =
   ## Returns true if `s` is a valid identifier.
   ##
   ## A valid identifier starts with a character of the set `IdentStartChars`
   ## 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
 
-proc editDistance*(a, b: string): int {.noSideEffect,
-  rtl, extern: "nsuEditDistance".} =
-  ## 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]
-
-# floating point formating:
+
+# floating point formatting:
 when not defined(js):
-  proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
-                                     importc: "sprintf", varargs, noSideEffect.}
+  func c_snprintf(buf: cstring, n: csize_t, frmt: cstring): cint {.header: "<stdio.h>",
+                                     importc: "snprintf", varargs.}
 
 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 formatting.
+    ffDefault,   ## use the shorter floating point notation
+    ffDecimal,   ## use decimal floating point notation
+    ffScientific ## use scientific notation (using `e` character)
 
-{.deprecated: [TFloatFormat: FloatFormatMode].}
-
-proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
+func formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
                          precision: range[-1..32] = 16;
-                         decimalSep = '.'): string {.
-                         noSideEffect, rtl, extern: "nsu$1".} =
+                         decimalSep = '.'): string {.rtl, extern: "nsu$1".} =
   ## Converts a floating point value `f` to a string.
   ##
-  ## If ``format == ffDecimal`` then precision is the number of digits to
+  ## If `format == ffDecimal` then precision is the number of digits to
   ## be printed after the decimal point.
-  ## If ``format == ffScientific`` then precision is the maximum number
+  ## If `format == ffScientific` then precision is the maximum number
   ## of significant digits to be printed.
   ## `precision`'s default value is the maximum number of meaningful digits
-  ## after the decimal point for Nim's ``biggestFloat`` type.
-  ##
-  ## If ``precision == -1``, it tries to format it nicely.
-  when defined(js):
-    var precision = precision
-    if precision == -1:
-      # use the same default precision as c_sprintf
-      precision = 6
-    var res: cstring
-    case format
-    of ffDefault:
-      {.emit: "`res` = `f`.toString();".}
-    of ffDecimal:
-      {.emit: "`res` = `f`.toFixed(`precision`);".}
-    of ffScientific:
-      {.emit: "`res` = `f`.toExponential(`precision`);".}
-    result = $res
-    if 1.0 / f == -Inf:
-      # JavaScript removes the "-" from negative Zero, add it back here
-      result = "-" & $res
-    for i in 0 ..< result.len:
-      # Depending on the locale either dot or comma is produced,
-      # but nothing else is possible:
-      if result[i] in {'.', ','}: result[i] = decimalsep
+  ## 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 nimvm:
+    discard "implemented in the vmops"
   else:
-    const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e']
-    var
-      frmtstr {.noinit.}: array[0..5, char]
-      buf {.noinit.}: array[0..2500, char]
-      L: cint
-    frmtstr[0] = '%'
-    if precision >= 0:
-      frmtstr[1] = '#'
-      frmtstr[2] = '.'
-      frmtstr[3] = '*'
-      frmtstr[4] = floatFormatToChar[format]
-      frmtstr[5] = '\0'
-      when defined(nimNoArrayToCstringConversion):
-        L = c_sprintf(addr buf, addr frmtstr, precision, f)
-      else:
-        L = c_sprintf(buf, frmtstr, precision, f)
+    when defined(js):
+      var precision = precision
+      if precision == -1:
+        # use the same default precision as c_snprintf
+        precision = 6
+      var res: cstring
+      case format
+      of ffDefault:
+        {.emit: "`res` = `f`.toString();".}
+      of ffDecimal:
+        {.emit: "`res` = `f`.toFixed(`precision`);".}
+      of ffScientific:
+        {.emit: "`res` = `f`.toExponential(`precision`);".}
+      result = $res
+      if 1.0 / f == -Inf:
+        # JavaScript removes the "-" from negative Zero, add it back here
+        result = "-" & $res
+      for i in 0 ..< result.len:
+        # Depending on the locale either dot or comma is produced,
+        # but nothing else is possible:
+        if result[i] in {'.', ','}: result[i] = decimalSep
     else:
-      frmtstr[1] = floatFormatToChar[format]
-      frmtstr[2] = '\0'
-      when defined(nimNoArrayToCstringConversion):
-        L = c_sprintf(addr buf, addr frmtstr, f)
+      const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e']
+      var
+        frmtstr {.noinit.}: array[0..5, char]
+        buf {.noinit.}: array[0..2500, char]
+        L: cint
+      frmtstr[0] = '%'
+      if precision >= 0:
+        frmtstr[1] = '#'
+        frmtstr[2] = '.'
+        frmtstr[3] = '*'
+        frmtstr[4] = floatFormatToChar[format]
+        frmtstr[5] = '\0'
+        L = c_snprintf(cast[cstring](addr buf), csize_t(2501), cast[cstring](addr frmtstr), precision, f)
       else:
-        L = c_sprintf(buf, frmtstr, f)
-    result = newString(L)
-    for i in 0 ..< L:
-      # Depending on the locale either dot or comma is produced,
-      # but nothing else is possible:
-      if buf[i] in {'.', ','}: result[i] = decimalsep
-      else: result[i] = buf[i]
-    when defined(windows):
-      # VS pre 2015 violates the C standard: "The exponent always contains at
-      # least two digits, and only as many more digits as necessary to
-      # represent the exponent." [C11 §7.21.6.1]
-      # The following post-processing fixes this behavior.
-      if result.len > 4 and result[^4] == '+' and result[^3] == '0':
-        result[^3] = result[^2]
-        result[^2] = result[^1]
-        result.setLen(result.len - 1)
-
-proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
+        frmtstr[1] = floatFormatToChar[format]
+        frmtstr[2] = '\0'
+        L = c_snprintf(cast[cstring](addr buf), csize_t(2501), cast[cstring](addr frmtstr), f)
+      result = newString(L)
+      for i in 0 ..< L:
+        # Depending on the locale either dot or comma is produced,
+        # but nothing else is possible:
+        if buf[i] in {'.', ','}: result[i] = decimalSep
+        else: result[i] = buf[i]
+      when defined(windows):
+        # VS pre 2015 violates the C standard: "The exponent always contains at
+        # least two digits, and only as many more digits as necessary to
+        # represent the exponent." [C11 §7.21.6.1]
+        # The following post-processing fixes this behavior.
+        if result.len > 4 and result[^4] == '+' and result[^3] == '0':
+          result[^3] = result[^2]
+          result[^2] = result[^1]
+          result.setLen(result.len - 1)
+
+func formatFloat*(f: float, format: FloatFormatMode = ffDefault,
                   precision: range[-1..32] = 16; decimalSep = '.'): string {.
-                  noSideEffect, rtl, extern: "nsu$1".} =
+                  rtl, extern: "nsu$1".} =
   ## Converts a floating point value `f` to a string.
   ##
-  ## If ``format == ffDecimal`` then precision is the number of digits to
+  ## If `format == ffDecimal` then precision is the number of digits to
   ## be printed after the decimal point.
-  ## If ``format == ffScientific`` then precision is the maximum number
+  ## If `format == ffScientific` then precision is the maximum number
   ## of significant digits to be printed.
   ## `precision`'s default value is the maximum number of meaningful digits
-  ## after the decimal point for Nim's ``float`` type.
+  ## after the decimal point for Nim's `float` type.
   ##
-  ## If ``precision == -1``, it tries to format it nicely.
+  ## If `precision == -1`, it tries to format it nicely.
   runnableExamples:
     let x = 123.456
     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.} =
+func trimZeros*(x: var string; decimalSep = '.') =
   ## Trim trailing zeros from a formatted floating point
-  ## value (`x`).  Modifies the passed value.
-  var spl: seq[string]
-  if x.contains('.') or x.contains(','):
-    if x.contains('e'):
-      spl = x.split('e')
-      x = spl[0]
-    while x[x.high] == '0':
-      x.setLen(x.len-1)
-    if x[x.high] in [',', '.']:
-      x.setLen(x.len-1)
-    if spl.len > 0:
-      x &= "e" & spl[1]
+  ## 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"
+
+  let sPos = find(x, decimalSep)
+  if sPos >= 0:
+    var last = find(x, 'e', start = sPos)
+    last = if last >= 0: last - 1 else: high(x)
+    var pos = last
+    while pos >= 0 and x[pos] == '0': dec(pos)
+    if pos > sPos: inc(pos)
+    if last >= pos:
+      x.delete(pos..last)
 
 type
-  BinaryPrefixMode* = enum ## the different names for binary prefixes
-    bpIEC, # use the IEC/ISO standard prefixes such as kibi
-    bpColloquial # use the colloquial kilo, mega etc
+  BinaryPrefixMode* = enum ## The different names for binary prefixes.
+    bpIEC,                 # use the IEC/ISO standard prefixes such as kibi
+    bpColloquial           # use the colloquial kilo, mega etc
 
-proc formatSize*(bytes: int64,
+func formatSize*(bytes: int64,
                  decimalSep = '.',
                  prefix = bpIEC,
-                 includeSpace = false): string {.noSideEffect.} =
+                 includeSpace = false): string =
   ## Rounds and formats `bytes`.
   ##
   ## By default, uses the IEC/ISO standard binary prefixes, so 1024 will be
@@ -1991,20 +2598,24 @@ 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"
-    doAssert formatSize(4096, includeSpace=true) == "4 KiB"
-    doAssert formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB"
+    doAssert formatSize(4096, includeSpace = true) == "4 KiB"
+    doAssert formatSize(4096, prefix = bpColloquial, includeSpace = true) == "4 kB"
     doAssert formatSize(4096) == "4KiB"
-    doAssert formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB"
+    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
     xb: int64 = bytes
     fbytes: float
-    last_xb: int64 = bytes
-    matchedIndex: int
+    lastXb: int64 = bytes
+    matchedIndex = 0
     prefixes: array[9, string]
   if prefix == bpColloquial:
     prefixes = collPrefixes
@@ -2014,29 +2625,30 @@ proc formatSize*(bytes: int64,
   # Iterate through prefixes seeing if value will be greater than
   # 0 in each case
   for index in 1..<prefixes.len:
-    last_xb = xb
+    lastXb = xb
     xb = bytes div (1'i64 shl (index*10))
     matchedIndex = index
     if xb == 0:
-      xb = last_xb
+      xb = lastXb
       matchedIndex = index - 1
       break
   # xb has the integer number for the latest value; index should be correct
   fbytes = bytes.float / (1'i64 shl (matchedIndex*10)).float
-  result = formatFloat(fbytes, format=ffDecimal, precision=3, decimalSep=decimalSep)
-  result.trimZeros()
+  result = formatFloat(fbytes, format = ffDecimal, precision = 3,
+      decimalSep = decimalSep)
+  result.trimZeros(decimalSep)
   if includeSpace:
     result &= " "
   result &= prefixes[matchedIndex]
   result &= "B"
 
-proc formatEng*(f: BiggestFloat,
+func formatEng*(f: BiggestFloat,
                 precision: range[0..32] = 10,
                 trim: bool = true,
                 siPrefix: bool = false,
                 unit: string = "",
                 decimalSep = '.',
-                useUnitSpace = false): string {.noSideEffect.} =
+                useUnitSpace = false): string =
   ## Converts a floating point value `f` to a string using engineering notation.
   ##
   ## Numbers in of the range -1000.0<f<1000.0 will be formatted without an
@@ -2059,13 +2671,13 @@ proc formatEng*(f: BiggestFloat,
   ## decimal point or (if `trim` is true) the maximum number of digits to be
   ## shown.
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##    formatEng(0, 2, trim=false) == "0.00"
   ##    formatEng(0, 2) == "0"
   ##    formatEng(0.053, 0) == "53e-3"
   ##    formatEng(52731234, 2) == "52.73e6"
   ##    formatEng(-52731234, 2) == "-52.73e6"
+  ##    ```
   ##
   ## If `siPrefix` is set to true, the number will be displayed with the SI
   ## prefix corresponding to the exponent. For example 4100 will be displayed
@@ -2080,8 +2692,7 @@ proc formatEng*(f: BiggestFloat,
   ## different to appending the unit to the result as the location of the space
   ## is altered depending on whether there is an exponent.
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##    formatEng(4100, siPrefix=true, unit="V") == "4.1 kV"
   ##    formatEng(4.1, siPrefix=true, unit="V") == "4.1 V"
   ##    formatEng(4.1, siPrefix=true) == "4.1" # Note lack of space
@@ -2091,8 +2702,12 @@ proc formatEng*(f: BiggestFloat,
   ##    formatEng(4100) == "4.1e3"
   ##    formatEng(4100, unit="V") == "4.1e3 V"
   ##    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
@@ -2100,11 +2715,12 @@ proc formatEng*(f: BiggestFloat,
     exponent: int
     splitResult: seq[string]
     suffix: string = ""
-  proc getPrefix(exp: int): char =
+  func getPrefix(exp: int): char =
     ## Get the SI prefix for a given exponent
     ##
     ## Assumes exponent is a multiple of 3; returns ' ' if no prefix found
-    const siPrefixes = ['a','f','p','n','u','m',' ','k','M','G','T','P','E']
+    const siPrefixes = ['a', 'f', 'p', 'n', 'u', 'm', ' ', 'k', 'M', 'G', 'T',
+        'P', 'E']
     var index: int = (exp div 3) + 6
     result = ' '
     if index in low(siPrefixes)..high(siPrefixes):
@@ -2117,23 +2733,24 @@ proc formatEng*(f: BiggestFloat,
   if absolute == 0.0:
     # Simple case: just format it and force the exponent to 0
     exponent = 0
-    result = significand.formatBiggestFloat(ffDecimal, precision, decimalSep='.')
+    result = significand.formatBiggestFloat(ffDecimal, precision,
+        decimalSep = '.')
   else:
     # Find the best exponent that's a multiple of 3
-    fexponent = round(floor(log10(absolute)))
-    fexponent = 3.0 * round(floor(fexponent / 3.0))
+    fexponent = floor(log10(absolute))
+    fexponent = 3.0 * floor(fexponent / 3.0)
     # Adjust the significand for the new exponent
     significand /= pow(10.0, fexponent)
 
-    # Round the significand and check whether it has affected
+    # Adjust the significand and check whether it has affected
     # the exponent
-    significand = round(significand, precision)
     absolute = abs(significand)
     if absolute >= 1000.0:
       significand *= 0.001
       fexponent += 3
     # Components of the result:
-    result = significand.formatBiggestFloat(ffDecimal, precision, decimalSep='.')
+    result = significand.formatBiggestFloat(ffDecimal, precision,
+        decimalSep = '.')
     exponent = fexponent.int()
 
   splitResult = result.split('.')
@@ -2144,11 +2761,11 @@ proc formatEng*(f: BiggestFloat,
     # we can be a bit more efficient through knowledge that there will never be
     # an exponent in this part.
     if trim:
-        while splitResult[1].endsWith("0"):
-          # Trim last character
-          splitResult[1].setLen(splitResult[1].len-1)
-        if splitResult[1].len() > 0:
-          result &= decimalSep & splitResult[1]
+      while splitResult[1].endsWith("0"):
+        # Trim last character
+        splitResult[1].setLen(splitResult[1].len-1)
+      if splitResult[1].len() > 0:
+        result &= decimalSep & splitResult[1]
     else:
       result &= decimalSep & splitResult[1]
 
@@ -2165,7 +2782,7 @@ proc formatEng*(f: BiggestFloat,
     result &= "e" & $exponent
   result &= suffix
 
-proc findNormalized(x: string, inArray: openArray[string]): int =
+func findNormalized(x: string, inArray: openArray[string]): int =
   var i = 0
   while i < high(inArray):
     if cmpIgnoreStyle(x, inArray[i]) == 0: return i
@@ -2173,12 +2790,12 @@ proc findNormalized(x: string, inArray: openArray[string]): int =
               # security hole...
   return -1
 
-proc invalidFormatString() {.noinline.} =
-  raise newException(ValueError, "invalid format string")
+func invalidFormatString(formatstr: string) {.noinline.} =
+  raise newException(ValueError, "invalid format string: " & formatstr)
 
-proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.
-  noSideEffect, rtl, extern: "nsuAddf".} =
-  ## The same as ``add(s, formatstr % a)``, but more efficient.
+func addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.rtl,
+    extern: "nsuAddf".} =
+  ## The same as `add(s, formatstr % a)`, but more efficient.
   const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
   var i = 0
   var num = 0
@@ -2186,7 +2803,7 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.
     if formatstr[i] == '$' and i+1 < len(formatstr):
       case formatstr[i+1]
       of '#':
-        if num > a.high: invalidFormatString()
+        if num > a.high: invalidFormatString(formatstr)
         add s, a[num]
         inc i, 2
         inc num
@@ -2202,7 +2819,7 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.
           j = j * 10 + ord(formatstr[i]) - ord('0')
           inc(i)
         let idx = if not negative: j-1 else: a.len-j
-        if idx < 0 or idx > a.high: invalidFormatString()
+        if idx < 0 or idx > a.high: invalidFormatString(formatstr)
         add s, a[idx]
       of '{':
         var j = i+2
@@ -2219,28 +2836,28 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.
           inc(j)
         if isNumber == 1:
           let idx = if not negative: k-1 else: a.len-k
-          if idx < 0 or idx > a.high: invalidFormatString()
+          if idx < 0 or idx > a.high: invalidFormatString(formatstr)
           add s, a[idx]
         else:
           var x = findNormalized(substr(formatstr, i+2, j-1), a)
           if x >= 0 and x < high(a): add s, a[x+1]
-          else: invalidFormatString()
+          else: invalidFormatString(formatstr)
         i = j+1
       of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':
         var j = i+1
         while j < formatstr.len and formatstr[j] in PatternChars: inc(j)
         var x = findNormalized(substr(formatstr, i+1, j-1), a)
         if x >= 0 and x < high(a): add s, a[x+1]
-        else: invalidFormatString()
+        else: invalidFormatString(formatstr)
         i = j
       else:
-        invalidFormatString()
+        invalidFormatString(formatstr)
     else:
       add s, formatstr[i]
       inc(i)
 
-proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect,
-  rtl, extern: "nsuFormatOpenArray".} =
+func `%`*(formatstr: string, a: openArray[string]): string {.rtl,
+    extern: "nsuFormatOpenArray".} =
   ## Interpolates a format string with the values from `a`.
   ##
   ## The `substitution`:idx: operator performs string substitutions in
@@ -2249,395 +2866,167 @@ proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect,
   ##
   ## This is best explained by an example:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   "$1 eats $2." % ["The cat", "fish"]
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   "The cat eats fish."
+  ##   ```
   ##
-  ## The substitution variables (the thing after the ``$``) are enumerated
-  ## from 1 to ``a.len``.
-  ## To produce a verbatim ``$``, use ``$$``.
-  ## The notation ``$#`` can be used to refer to the next substitution
+  ## The substitution variables (the thing after the `$`) are enumerated
+  ## from 1 to `a.len`.
+  ## To produce a verbatim `$`, use `$$`.
+  ## The notation `$#` can be used to refer to the next substitution
   ## variable:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   "$# eats $#." % ["The cat", "fish"]
+  ##   ```
   ##
   ## Substitution variables can also be words (that is
-  ## ``[A-Za-z_]+[A-Za-z0-9_]*``) in which case the arguments in `a` with even
+  ## `[A-Za-z_]+[A-Za-z0-9_]*`) in which case the arguments in `a` with even
   ## indices are keys and with odd indices are the corresponding values.
   ## An example:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   "$animal eats $food." % ["animal", "The cat", "food", "fish"]
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   "The cat eats fish."
+  ##   ```
   ##
   ## 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]``.
+func `%`*(formatstr, a: string): string {.rtl,
+    extern: "nsuFormatSingleElem".} =
+  ## This is the same as `formatstr % [a]` (see
+  ## `% func<#%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
+func format*(formatstr: string, a: varargs[string, `$`]): string {.rtl,
+    extern: "nsuFormatVarargs".} =
+  ## This is the same as `formatstr % a` (see
+  ## `% func<#%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).
+func strip*(s: string, leading = true, trailing = true,
+            chars: set[char] = Whitespace): string {.rtl, extern: "nsuStrip".} =
+  ## Strips leading or trailing `chars` (default: whitespace characters)
+  ## from `s` and returns the resulting string.
+  ##
+  ## 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:
+  ## * `strip proc<strbasics.html#strip,string,set[char]>`_ Inplace version.
+  ## * `stripLineEnd func<#stripLineEnd,string>`_
   runnableExamples:
-     var userInput = "Hello World!*~\r\n"
-     userInput.removeSuffix
-     doAssert userInput == "Hello World!*~"
-     userInput.removeSuffix({'~', '*'})
-     doAssert userInput == "Hello World!"
+    let a = "  vhellov   "
+    let b = strip(a)
+    doAssert b == "vhellov"
 
-     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)
+    doAssert a.strip(leading = false) == "  vhellov"
+    doAssert a.strip(trailing = false) == "vhellov   "
 
-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.
-  ##
-  runnableExamples:
-     var table = "users"
-     table.removeSuffix('s')
-     doAssert table == "user"
+    doAssert b.strip(chars = {'v'}) == "hello"
+    doAssert b.strip(leading = false, chars = {'v'}) == "vhello"
 
-     var dots = "Trailing dots......."
-     dots.removeSuffix('.')
-     doAssert dots == "Trailing dots"
-  removeSuffix(s, chars = {c})
+    let c = "blaXbla"
+    doAssert c.strip(chars = {'b', 'a'}) == "laXbl"
+    doAssert c.strip(chars = {'b', 'a', 'l'}) == "X"
 
-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)
+  var
+    first = 0
+    last = len(s)-1
+  if leading:
+    while first <= last and s[first] in chars: inc(first)
+  if trailing:
+    while last >= first and s[last] in chars: dec(last)
+  result = substr(s, first, last)
 
-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).
-  ##
+func stripLineEnd*(s: var string) =
+  ## Strips one of these suffixes from `s` in-place:
+  ## `\r, \n, \r\n, \f, \v` (at most once instance).
+  ## For example, can be useful in conjunction with `osproc.execCmdEx`.
+  ## aka: `chomp`:idx:
   runnableExamples:
-     var userInput = "\r\n*~Hello World!"
-     userInput.removePrefix
-     doAssert userInput == "*~Hello World!"
-     userInput.removePrefix({'~', '*'})
-     doAssert userInput == "Hello World!"
+    var s = "foo\n\n"
+    s.stripLineEnd
+    doAssert s == "foo\n"
+    s = "foo\r\n"
+    s.stripLineEnd
+    doAssert s == "foo"
+
+  if s.len > 0:
+    case s[^1]
+    of '\n':
+      if s.len > 1 and s[^2] == '\r':
+        s.setLen s.len-2
+      else:
+        s.setLen s.len-1
+    of '\r', '\v', '\f':
+      s.setLen s.len-1
+    else:
+      discard
 
-     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.
+iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[
+  token: string, isSep: bool] =
+  ## Tokenizes the string `s` into substrings.
   ##
-  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.
+  ## Substrings are separated by a substring containing only `seps`.
+  ## Example:
   ##
-  runnableExamples:
-     var answers = "yesyes"
-     answers.removePrefix("yes")
-     doAssert answers == "yes"
-  if s.startsWith(prefix):
-    s.delete(0, prefix.len - 1)
-
-when isMainModule:
-  proc nonStaticTests =
-    doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
-    doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235."
-    doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6"
-    doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
-    doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in
-                                                     ["1,0e-11", "1,0e-011"]
-    # bug #6589
-    doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02"
-
-    doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
-    doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"
-
-    block: # formatSize tests
-      doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"
-      doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
-      doAssert formatSize(4096) == "4KiB"
-      doAssert formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB"
-      doAssert formatSize(4096, includeSpace=true) == "4 KiB"
-      doAssert formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB"
-
-    block: # formatEng tests
-      doAssert formatEng(0, 2, trim=false) == "0.00"
-      doAssert formatEng(0, 2) == "0"
-      doAssert formatEng(53, 2, trim=false) == "53.00"
-      doAssert formatEng(0.053, 2, trim=false) == "53.00e-3"
-      doAssert formatEng(0.053, 4, trim=false) == "53.0000e-3"
-      doAssert formatEng(0.053, 4, trim=true) == "53e-3"
-      doAssert formatEng(0.053, 0) == "53e-3"
-      doAssert formatEng(52731234) == "52.731234e6"
-      doAssert formatEng(-52731234) == "-52.731234e6"
-      doAssert formatEng(52731234, 1) == "52.7e6"
-      doAssert formatEng(-52731234, 1) == "-52.7e6"
-      doAssert formatEng(52731234, 1, decimalSep=',') == "52,7e6"
-      doAssert formatEng(-52731234, 1, decimalSep=',') == "-52,7e6"
-
-      doAssert formatEng(4100, siPrefix=true, unit="V") == "4.1 kV"
-      doAssert formatEng(4.1, siPrefix=true, unit="V", useUnitSpace=true) == "4.1 V"
-      doAssert formatEng(4.1, siPrefix=true) == "4.1" # Note lack of space
-      doAssert formatEng(4100, siPrefix=true) == "4.1 k"
-      doAssert formatEng(4.1, siPrefix=true, unit="", useUnitSpace=true) == "4.1 " # Includes space
-      doAssert formatEng(4100, siPrefix=true, unit="") == "4.1 k"
-      doAssert formatEng(4100) == "4.1e3"
-      doAssert formatEng(4100, unit="V", useUnitSpace=true) == "4.1e3 V"
-      doAssert formatEng(4100, unit="", useUnitSpace=true) == "4.1e3 "
-      # Don't use SI prefix as number is too big
-      doAssert formatEng(3.1e22, siPrefix=true, unit="a", useUnitSpace=true) == "31e21 a"
-      # Don't use SI prefix as number is too small
-      doAssert formatEng(3.1e-25, siPrefix=true, unit="A", useUnitSpace=true) == "310e-27 A"
-
-  proc staticTests =
-    doAssert align("abc", 4) == " abc"
-    doAssert align("a", 0) == "a"
-    doAssert align("1232", 6) == "  1232"
-    doAssert align("1232", 6, '#') == "##1232"
-
-    doAssert alignLeft("abc", 4) == "abc "
-    doAssert alignLeft("a", 0) == "a"
-    doAssert alignLeft("1232", 6) == "1232  "
-    doAssert alignLeft("1232", 6, '#') == "1232##"
-
-    let
-      inp = """ this is a long text --  muchlongerthan10chars and here
-                 it goes"""
-      outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
-    doAssert wordWrap(inp, 10, false) == outp
+  ##   ```nim
+  ##   for word in tokenize("  this is an  example  "):
+  ##     writeLine(stdout, word)
+  ##   ```
+  ##
+  ## Results in:
+  ##
+  ##   ```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
 
-    let
-      longInp = """ThisIsOneVeryLongStringWhichWeWillSplitIntoEightSeparatePartsNow"""
-      longOutp = "ThisIsOn\neVeryLon\ngStringW\nhichWeWi\nllSplitI\nntoEight\nSeparate\nPartsNow"
-    doAssert wordWrap(longInp, 8, true) == longOutp
-
-    doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
-             "The cat eats fish."
-
-    doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz "
-    doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz  abc"
-
-    doAssert "-lda-ldz -ld abc".replaceWord("") == "-lda-ldz -ld abc"
-    doAssert "oo".replace("", "abc") == "abcoabcoabc"
-
-    type MyEnum = enum enA, enB, enC, enuD, enE
-    doAssert parseEnum[MyEnum]("enu_D") == enuD
-
-    doAssert parseEnum("invalid enum value", enC) == enC
-
-    doAssert center("foo", 13) == "     foo     "
-    doAssert center("foo", 0) == "foo"
-    doAssert center("foo", 3, fillChar = 'a') == "foo"
-    doAssert center("foo", 10, fillChar = '\t') == "\t\t\tfoo\t\t\t\t"
-
-    doAssert count("foofoofoo", "foofoo") == 1
-    doAssert count("foofoofoo", "foofoo", overlapping = true) == 2
-    doAssert count("foofoofoo", 'f') == 3
-    doAssert count("foofoofoobar", {'f','b'}) == 4
-
-    doAssert strip("  foofoofoo  ") == "foofoofoo"
-    doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo"
-    doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo"
-    doAssert strip("stripme but don't strip this stripme",
-                   chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) ==
-                   " but don't strip this "
-    doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo"
-    doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos"
-
-    doAssert "  foo\n  bar".indent(4, "Q") == "QQQQ  foo\nQQQQ  bar"
-
-    doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab"
-    doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.", "PEOPLE!")) == "HELLO PEOPLE!"
-    doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa"
-
-    doAssert isAlphaAscii('r')
-    doAssert isAlphaAscii('A')
-    doAssert(not isAlphaAscii('$'))
-
-    doAssert isAlphaAscii("Rasp")
-    doAssert isAlphaAscii("Args")
-    doAssert(not isAlphaAscii("$Tomato"))
-
-    doAssert isAlphaNumeric('3')
-    doAssert isAlphaNumeric('R')
-    doAssert(not isAlphaNumeric('!'))
-
-    doAssert isAlphaNumeric("34ABc")
-    doAssert isAlphaNumeric("Rad")
-    doAssert isAlphaNumeric("1234")
-    doAssert(not isAlphaNumeric("@nose"))
-
-    doAssert isDigit('3')
-    doAssert(not isDigit('a'))
-    doAssert(not isDigit('%'))
-
-    doAssert isDigit("12533")
-    doAssert(not isDigit("12.33"))
-    doAssert(not isDigit("A45b"))
-
-    doAssert isSpaceAscii('\t')
-    doAssert isSpaceAscii('\l')
-    doAssert(not isSpaceAscii('A'))
-
-    doAssert isSpaceAscii("\t\l \v\r\f")
-    doAssert isSpaceAscii("       ")
-    doAssert(not isSpaceAscii("ABc   \td"))
-
-    doAssert(isNilOrWhitespace(""))
-    doAssert(isNilOrWhitespace("       "))
-    doAssert(isNilOrWhitespace("\t\l \v\r\f"))
-    doAssert(not isNilOrWhitespace("ABc   \td"))
-
-    doAssert isLowerAscii('a')
-    doAssert isLowerAscii('z')
-    doAssert(not isLowerAscii('A'))
-    doAssert(not isLowerAscii('5'))
-    doAssert(not isLowerAscii('&'))
-    doAssert(not isLowerAscii(' '))
-
-    doAssert isLowerAscii("abcd", false)
-    doAssert(not isLowerAscii("33aa", false))
-    doAssert(not isLowerAscii("a b", false))
-
-    doAssert(not isLowerAscii("abCD", true))
-    doAssert isLowerAscii("33aa", true)
-    doAssert isLowerAscii("a b", true)
-    doAssert isLowerAscii("1, 2, 3 go!", true)
-    doAssert(not isLowerAscii(" ", true))
-    doAssert(not isLowerAscii("(*&#@(^#$ ", true)) # None of the string chars are alphabets
-
-    doAssert isUpperAscii('A')
-    doAssert(not isUpperAscii('b'))
-    doAssert(not isUpperAscii('5'))
-    doAssert(not isUpperAscii('%'))
-
-    doAssert isUpperAscii("ABC", false)
-    doAssert(not isUpperAscii("A#$", false))
-    doAssert(not isUpperAscii("A B", false))
-
-    doAssert(not isUpperAscii("AAcc", true))
-    doAssert isUpperAscii("A#$", true)
-    doAssert isUpperAscii("A B", true)
-    doAssert isUpperAscii("1, 2, 3 GO!", true)
-    doAssert(not isUpperAscii(" ", true))
-    doAssert(not isUpperAscii("(*&#@(^#$ ", true)) # None of the string chars are alphabets
-
-    doAssert rsplit("foo bar", seps=Whitespace) == @["foo", "bar"]
-    doAssert rsplit(" foo bar", seps=Whitespace, maxsplit=1) == @[" foo", "bar"]
-    doAssert rsplit(" foo bar ", seps=Whitespace, maxsplit=1) == @[" foo bar", ""]
-    doAssert rsplit(":foo:bar", sep=':') == @["", "foo", "bar"]
-    doAssert rsplit(":foo:bar", sep=':', maxsplit=2) == @["", "foo", "bar"]
-    doAssert rsplit(":foo:bar", sep=':', maxsplit=3) == @["", "foo", "bar"]
-    doAssert rsplit("foothebar", sep="the") == @["foo", "bar"]
-
-    doAssert(unescape(r"\x013", "", "") == "\x013")
-
-    doAssert join(["foo", "bar", "baz"]) == "foobarbaz"
-    doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz"
-    doAssert join([1, 2, 3]) == "123"
-    doAssert join(@[1, 2, 3], ", ") == "1, 2, 3"
-
-    doAssert """~~!!foo
-~~!!bar
-~~!!baz""".unindent(2, "~~!!") == "foo\nbar\nbaz"
-
-    doAssert """~~!!foo
-~~!!bar
-~~!!baz""".unindent(2, "~~!!aa") == "~~!!foo\n~~!!bar\n~~!!baz"
-    doAssert """~~foo
-~~  bar
-~~  baz""".unindent(4, "~") == "foo\n  bar\n  baz"
-    doAssert """foo
-bar
-    baz
-  """.unindent(4) == "foo\nbar\nbaz\n"
-    doAssert """foo
-    bar
-    baz
-  """.unindent(2) == "foo\n  bar\n  baz\n"
-    doAssert """foo
-    bar
-    baz
-  """.unindent(100) == "foo\nbar\nbaz\n"
-
-    doAssert """foo
-    foo
-    bar
-  """.unindent() == "foo\nfoo\nbar\n"
-
-    let s = " this is an example  "
-    let s2 = ":this;is;an:example;;"
-
-    doAssert s.split() == @["", "this", "is", "an", "example", "", ""]
-    doAssert s2.split(seps={':', ';'}) == @["", "this", "is", "an", "example", "", ""]
-    doAssert s.split(maxsplit=4) == @["", "this", "is", "an", "example  "]
-    doAssert s.split(' ', maxsplit=1) == @["", "this is an example  "]
-    doAssert s.split(" ", maxsplit=4) == @["", "this", "is", "an", "example  "]
-
-    doAssert s.splitWhitespace() == @["this", "is", "an", "example"]
-    doAssert s.splitWhitespace(maxsplit=1) == @["this", "is an example  "]
-    doAssert s.splitWhitespace(maxsplit=2) == @["this", "is", "an example  "]
-    doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example  "]
-    doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"]
-
-    block: # startsWith / endsWith char tests
-      var s = "abcdef"
-      doAssert s.startsWith('a')
-      doAssert s.startsWith('b') == false
-      doAssert s.endsWith('f')
-      doAssert s.endsWith('a') == false
-      doAssert s.endsWith('\0') == false
-
-    #echo("strutils tests passed")
-
-  nonStaticTests()
-  staticTests()
-  static: staticTests()
+func isEmptyOrWhitespace*(s: string): bool {.rtl,
+    extern: "nsuIsEmptyOrWhitespace".} =
+  ## Checks if `s` is empty or consists entirely of whitespace characters.
+  result = s.allCharsInSet(Whitespace)
diff --git a/lib/pure/subexes.nim b/lib/pure/subexes.nim
deleted file mode 100644
index 8149c72cc..000000000
--- a/lib/pure/subexes.nim
+++ /dev/null
@@ -1,408 +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"
-
-
-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("index out of bounds: " & $result)
-  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
-
-{.deprecated: [TSubex: Subex].}
-
-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/sugar.nim b/lib/pure/sugar.nim
index 258b40191..90ba20c13 100644
--- a/lib/pure/sugar.nim
+++ b/lib/pure/sugar.nim
@@ -10,15 +10,23 @@
 ## This module implements nice syntactic sugar based on Nim's
 ## macro system.
 
-import macros
+import std/private/since
+import std/macros
 
-proc createProcType(p, b: NimNode): NimNode {.compileTime.} =
-  #echo treeRepr(p)
-  #echo treeRepr(b)
+proc checkPragma(ex, prag: var NimNode) =
+  since (1, 3):
+    if ex.kind == nnkPragmaExpr:
+      prag = ex[1]
+      ex = ex[0]
+
+proc createProcType(p, b: NimNode): NimNode =
   result = newNimNode(nnkProcTy)
-  var formalParams = newNimNode(nnkFormalParams)
+  var
+    formalParams = newNimNode(nnkFormalParams).add(b)
+    p = p
+    prag = newEmptyNode()
 
-  formalParams.add b
+  checkPragma(p, prag)
 
   case p.kind
   of nnkPar, nnkTupleConstr:
@@ -42,159 +50,380 @@ proc createProcType(p, b: NimNode): NimNode {.compileTime.} =
     formalParams.add identDefs
 
   result.add formalParams
-  result.add newEmptyNode()
-  #echo(treeRepr(result))
-  #echo(result.toStrLit())
+  result.add prag
 
 macro `=>`*(p, b: untyped): untyped =
-  ## Syntax sugar for anonymous procedures.
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   proc passTwoAndTwo(f: (int, int) -> int): int =
-  ##     f(2, 2)
+  ## Syntax sugar for anonymous procedures. It also supports pragmas.
   ##
-  ##   passTwoAndTwo((x, y) => x + y) # 4
+  ## .. warning:: Semicolons can not be used to separate procedure arguments.
+  runnableExamples:
+    proc passTwoAndTwo(f: (int, int) -> int): int = f(2, 2)
+
+    assert passTwoAndTwo((x, y) => x + y) == 4
+
+    type
+      Bot = object
+        call: (string {.noSideEffect.} -> string)
+
+    var myBot = Bot()
+
+    myBot.call = (name: string) {.noSideEffect.} => "Hello " & name & ", I'm a bot."
+    assert myBot.call("John") == "Hello John, I'm a bot."
+
+    let f = () => (discard) # simplest proc that returns void
+    f()
+
+  var
+    params = @[ident"auto"]
+    name = newEmptyNode()
+    kind = nnkLambda
+    pragma = newEmptyNode()
+    p = p
 
-  #echo treeRepr(p)
-  #echo(treeRepr(b))
-  var params: seq[NimNode] = @[newIdentNode("auto")]
+  checkPragma(p, pragma)
+
+  if p.kind == nnkInfix and p[0].kind == nnkIdent and p[0].eqIdent"->":
+    params[0] = p[2]
+    p = p[1]
+
+  checkPragma(p, pragma) # check again after -> transform
 
   case p.kind
   of nnkPar, nnkTupleConstr:
-    for c in children(p):
+    var untypedBeforeColon = 0
+    for i, c in p:
       var identDefs = newNimNode(nnkIdentDefs)
       case c.kind
       of nnkExprColonExpr:
+        let t = c[1]
+        since (1, 3):
+          # + 1 here because of return type in params
+          for j in (i - untypedBeforeColon + 1) .. i:
+            params[j][1] = t
+        untypedBeforeColon = 0
         identDefs.add(c[0])
-        identDefs.add(c[1])
+        identDefs.add(t)
         identDefs.add(newEmptyNode())
       of nnkIdent:
         identDefs.add(c)
         identDefs.add(newIdentNode("auto"))
         identDefs.add(newEmptyNode())
+        inc untypedBeforeColon
       of nnkInfix:
-        if c[0].kind == nnkIdent and c[0].ident == !"->":
+        if c[0].kind == nnkIdent and c[0].eqIdent"->":
           var procTy = createProcType(c[1], c[2])
           params[0] = procTy[0][0]
           for i in 1 ..< procTy[0].len:
             params.add(procTy[0][i])
         else:
-          error("Expected proc type (->) got (" & $c[0].ident & ").")
+          error("Expected proc type (->) got (" & c[0].strVal & ").", c)
         break
       else:
-        echo treeRepr c
-        error("Incorrect procedure parameter list.")
+        error("Incorrect procedure parameter.", c)
       params.add(identDefs)
-  of nnkIdent:
+  of nnkIdent, nnkOpenSymChoice, nnkClosedSymChoice, nnkSym:
     var identDefs = newNimNode(nnkIdentDefs)
-    identDefs.add(p)
-    identDefs.add(newIdentNode("auto"))
+    identDefs.add(ident $p)
+    identDefs.add(ident"auto")
     identDefs.add(newEmptyNode())
     params.add(identDefs)
-  of nnkInfix:
-    if p[0].kind == nnkIdent and p[0].ident == !"->":
-      var procTy = createProcType(p[1], p[2])
-      params[0] = procTy[0][0]
-      for i in 1 ..< procTy[0].len:
-        params.add(procTy[0][i])
-    else:
-      error("Expected proc type (->) got (" & $p[0].ident & ").")
   else:
-    error("Incorrect procedure parameter list.")
-  result = newProc(params = params, body = b, procType = nnkLambda)
-  #echo(result.treeRepr)
-  #echo(result.toStrLit())
-  #return result # TODO: Bug?
+    error("Incorrect procedure parameter list.", p)
+  result = newProc(body = b, params = params,
+                   pragmas = pragma, name = name,
+                   procType = kind)
 
 macro `->`*(p, b: untyped): untyped =
-  ## Syntax sugar for procedure types.
+  ## Syntax sugar for procedure types. It also supports pragmas.
   ##
-  ## .. code-block:: nim
-  ##
-  ##   proc pass2(f: (float, float) -> float): float =
-  ##     f(2, 2)
-  ##
-  ##   # is the same as:
-  ##
-  ##   proc pass2(f: proc (x, y: float): float): float =
-  ##     f(2, 2)
+  ## .. warning:: Semicolons can not be used to separate procedure arguments.
+  runnableExamples:
+    proc passTwoAndTwo(f: (int, int) -> int): int = f(2, 2)
+    # is the same as:
+    # proc passTwoAndTwo(f: proc (x, y: int): int): int = f(2, 2)
 
-  result = createProcType(p, b)
+    assert passTwoAndTwo((x, y) => x + y) == 4
 
-type ListComprehension = object
-var lc*: ListComprehension
+    proc passOne(f: (int {.noSideEffect.} -> int)): int = f(1)
+    # is the same as:
+    # proc passOne(f: proc (x: int): int {.noSideEffect.}): int = f(1)
 
-macro `[]`*(lc: ListComprehension, comp, typ: untyped): untyped =
-  ## List comprehension, returns a sequence. `comp` is the actual list
-  ## comprehension, for example ``x | (x <- 1..10, x mod 2 == 0)``. `typ` is
-  ## the type that will be stored inside the result seq.
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   echo lc[x | (x <- 1..10, x mod 2 == 0), int]
-  ##
-  ##   const n = 20
-  ##   echo lc[(x,y,z) | (x <- 1..n, y <- x..n, z <- y..n, x*x + y*y == z*z),
-  ##           tuple[a,b,c: int]]
-
-  expectLen(comp, 3)
-  expectKind(comp, nnkInfix)
-  expectKind(comp[0], nnkIdent)
-  assert($comp[0].ident == "|")
-
-  result = newCall(
-    newDotExpr(
-      newIdentNode("result"),
-      newIdentNode("add")),
-    comp[1])
-
-  for i in countdown(comp[2].len-1, 0):
-    let x = comp[2][i]
-    expectMinLen(x, 1)
-    if x[0].kind == nnkIdent and $x[0].ident == "<-":
-      expectLen(x, 3)
-      result = newNimNode(nnkForStmt).add(x[1], x[2], result)
-    else:
-      result = newIfStmt((x, result))
-
-  result = newNimNode(nnkCall).add(
-    newNimNode(nnkPar).add(
-      newNimNode(nnkLambda).add(
-        newEmptyNode(),
-        newEmptyNode(),
-        newEmptyNode(),
-        newNimNode(nnkFormalParams).add(
-          newNimNode(nnkBracketExpr).add(
-            newIdentNode("seq"),
-            typ)),
-        newEmptyNode(),
-        newEmptyNode(),
-        newStmtList(
-          newAssignment(
-            newIdentNode("result"),
-            newNimNode(nnkPrefix).add(
-              newIdentNode("@"),
-              newNimNode(nnkBracket))),
-          result))))
-
-
-macro dump*(x: typed): untyped =
+    assert passOne(x {.noSideEffect.} => x + 1) == 2
+
+  result = createProcType(p, b)
+
+macro dump*(x: untyped): untyped =
   ## Dumps the content of an expression, useful for debugging.
   ## It accepts any expression and prints a textual representation
   ## of the tree representing the expression - as it would appear in
   ## source code - together with the value of the expression.
   ##
-  ## As an example,
-  ##
-  ## .. code-block:: nim
-  ##   let
-  ##     x = 10
-  ##     y = 20
-  ##   dump(x + y)
-  ##
-  ## will print ``x + y = 30``.
+  ## See also: `dumpToString` which is more convenient and useful since
+  ## it expands intermediate templates/macros, returns a string instead of
+  ## calling `echo`, and works with statements and expressions.
+  runnableExamples("-r:off"):
+    let
+      x = 10
+      y = 20
+    dump(x + y) # prints: `x + y = 30`
+
   let s = x.toStrLit
-  let r = quote do:
+  result = quote do:
     debugEcho `s`, " = ", `x`
-  return r
+
+macro dumpToStringImpl(s: static string, x: typed): string =
+  let s2 = x.toStrLit
+  if x.typeKind == ntyVoid:
+    result = quote do:
+      `s` & ": " & `s2`
+  else:
+    result = quote do:
+      `s` & ": " & `s2` & " = " & $`x`
+
+macro dumpToString*(x: untyped): string =
+  ## Returns the content of a statement or expression `x` after semantic analysis,
+  ## useful for debugging.
+  runnableExamples:
+    const a = 1
+    let x = 10
+    assert dumpToString(a + 2) == "a + 2: 3 = 3"
+    assert dumpToString(a + x) == "a + x: 1 + x = 11"
+    template square(x): untyped = x * x
+    assert dumpToString(square(x)) == "square(x): x * x = 100"
+    assert not compiles dumpToString(1 + nonexistent)
+    import std/strutils
+    assert "failedAssertImpl" in dumpToString(assert true) # example with a statement
+  result = newCall(bindSym"dumpToStringImpl")
+  result.add newLit repr(x)
+  result.add x
+
+# TODO: consider exporting this in macros.nim
+proc freshIdentNodes(ast: NimNode): NimNode =
+  # Replace NimIdent and NimSym by a fresh ident node
+  # see also https://github.com/nim-lang/Nim/pull/8531#issuecomment-410436458
+  proc inspect(node: NimNode): NimNode =
+    case node.kind:
+    of nnkIdent, nnkSym, nnkOpenSymChoice, nnkClosedSymChoice, nnkOpenSym:
+      result = ident($node)
+    of nnkEmpty, nnkLiterals:
+      result = node
+    else:
+      result = node.kind.newTree()
+      for child in node:
+        result.add inspect(child)
+  result = inspect(ast)
+
+macro capture*(locals: varargs[typed], body: untyped): untyped {.since: (1, 1).} =
+  ## Useful when creating a closure in a loop to capture some local loop variables
+  ## by their current iteration values.
+  runnableExamples:
+    import std/strformat
+
+    var myClosure: () -> string
+    for i in 5..7:
+      for j in 7..9:
+        if i * j == 42:
+          capture i, j:
+            myClosure = () => fmt"{i} * {j} = 42"
+    assert myClosure() == "6 * 7 = 42"
+
+  var params = @[newIdentNode("auto")]
+  let locals = if locals.len == 1 and locals[0].kind == nnkBracket: locals[0]
+               else: locals
+  for arg in locals:
+    proc getIdent(n: NimNode): NimNode =
+      case n.kind
+      of nnkIdent, nnkSym:
+        let nStr = n.strVal
+        if nStr == "result":
+          error("The variable name cannot be `result`!", n)
+        result = ident(nStr)
+      of nnkHiddenDeref: result = n[0].getIdent()
+      else:
+        error("The argument to be captured `" & n.repr & "` is not a pure identifier. " &
+          "It is an unsupported `" & $n.kind & "` node.", n)
+    let argName = getIdent(arg)
+    params.add(newIdentDefs(argName, freshIdentNodes getTypeInst arg))
+  result = newNimNode(nnkCall)
+  result.add(newProc(newEmptyNode(), params, body, nnkLambda))
+  for arg in locals: result.add(arg)
+
+since (1, 1):
+  import std/private/underscored_calls
+
+  macro dup*[T](arg: T, calls: varargs[untyped]): T =
+    ## Turns an `in-place`:idx: algorithm into one that works on
+    ## a copy and returns this copy, without modifying its input.
+    ##
+    ## This macro also allows for (otherwise in-place) function chaining.
+    ##
+    ## **Since:** Version 1.2.
+    runnableExamples:
+      import std/algorithm
+
+      let a = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
+      assert a.dup(sort) == sorted(a)
+
+      # Chaining:
+      var aCopy = a
+      aCopy.insert(10)
+      assert a.dup(insert(10), sort) == sorted(aCopy)
+
+      let s1 = "abc"
+      let s2 = "xyz"
+      assert s1 & s2 == s1.dup(&= s2)
+
+      # An underscore (_) can be used to denote the place of the argument you're passing:
+      assert "".dup(addQuoted(_, "foo")) == "\"foo\""
+      # but `_` is optional here since the substitution is in 1st position:
+      assert "".dup(addQuoted("foo")) == "\"foo\""
+
+      proc makePalindrome(s: var string) =
+        for i in countdown(s.len-2, 0):
+          s.add(s[i])
+
+      let c = "xyz"
+
+      # chaining:
+      let d = dup c:
+        makePalindrome # xyzyx
+        sort(_, SortOrder.Descending) # zyyxx
+        makePalindrome # zyyxxxyyz
+      assert d == "zyyxxxyyz"
+
+    result = newNimNode(nnkStmtListExpr, arg)
+    let tmp = genSym(nskVar, "dupResult")
+    result.add newVarStmt(tmp, arg)
+    underscoredCalls(result, calls, tmp)
+    result.add tmp
+
+proc trans(n, res, bracketExpr: NimNode): (NimNode, NimNode, NimNode) {.since: (1, 1).} =
+  # Looks for the last statement of the last statement, etc...
+  case n.kind
+  of nnkIfExpr, nnkIfStmt, nnkTryStmt, nnkCaseStmt, nnkWhenStmt:
+    result[0] = copyNimTree(n)
+    result[1] = copyNimTree(n)
+    result[2] = copyNimTree(n)
+    for i in ord(n.kind == nnkCaseStmt) ..< n.len:
+      (result[0][i], result[1][^1], result[2][^1]) = trans(n[i], res, bracketExpr)
+  of nnkStmtList, nnkStmtListExpr, nnkBlockStmt, nnkBlockExpr, nnkWhileStmt,
+      nnkForStmt, nnkElifBranch, nnkElse, nnkElifExpr, nnkOfBranch, nnkExceptBranch:
+    result[0] = copyNimTree(n)
+    result[1] = copyNimTree(n)
+    result[2] = copyNimTree(n)
+    if n.len >= 1:
+      (result[0][^1], result[1][^1], result[2][^1]) = trans(n[^1],
+          res, bracketExpr)
+  of nnkTableConstr:
+    result[1] = n[0][0]
+    result[2] = n[0][1]
+    if bracketExpr.len == 0:
+      bracketExpr.add(ident"initTable") # don't import tables
+    if bracketExpr.len == 1:
+      bracketExpr.add([newCall(bindSym"typeof",
+          newEmptyNode()), newCall(bindSym"typeof", newEmptyNode())])
+    template adder(res, k, v) = res[k] = v
+    result[0] = getAst(adder(res, n[0][0], n[0][1]))
+  of nnkCurly:
+    result[2] = n[0]
+    if bracketExpr.len == 0:
+      bracketExpr.add(ident"initHashSet")
+    if bracketExpr.len == 1:
+      bracketExpr.add(newCall(bindSym"typeof", newEmptyNode()))
+    template adder(res, v) = res.incl(v)
+    result[0] = getAst(adder(res, n[0]))
+  else:
+    result[2] = n
+    if bracketExpr.len == 0:
+      bracketExpr.add(bindSym"newSeq")
+    if bracketExpr.len == 1:
+      bracketExpr.add(newCall(bindSym"typeof", newEmptyNode()))
+    template adder(res, v) = res.add(v)
+    result[0] = getAst(adder(res, n))
+
+proc collectImpl(init, body: NimNode): NimNode {.since: (1, 1).} =
+  let res = genSym(nskVar, "collectResult")
+  var bracketExpr: NimNode
+  if init != nil:
+    expectKind init, {nnkCall, nnkIdent, nnkSym, nnkClosedSymChoice, nnkOpenSymChoice, nnkOpenSym}
+    bracketExpr = newTree(nnkBracketExpr,
+      if init.kind in {nnkCall, nnkClosedSymChoice, nnkOpenSymChoice, nnkOpenSym}:
+        freshIdentNodes(init[0]) else: freshIdentNodes(init))
+  else:
+    bracketExpr = newTree(nnkBracketExpr)
+  let (resBody, keyType, valueType) = trans(body, res, bracketExpr)
+  if bracketExpr.len == 3:
+    bracketExpr[1][1] = keyType
+    bracketExpr[2][1] = valueType
+  else:
+    bracketExpr[1][1] = valueType
+  let call = newTree(nnkCall, bracketExpr)
+  if init != nil and init.kind == nnkCall:
+    for i in 1 ..< init.len:
+      call.add init[i]
+  result = newTree(nnkStmtListExpr, newVarStmt(res, call), resBody, res)
+
+macro collect*(init, body: untyped): untyped {.since: (1, 1).} =
+  ## Comprehension for seqs/sets/tables.
+  ##
+  ## The last expression of `body` has special syntax that specifies
+  ## the collection's add operation. Use `{e}` for set's `incl`,
+  ## `{k: v}` for table's `[]=` and `e` for seq's `add`.
+  # analyse the body, find the deepest expression 'it' and replace it via
+  # 'result.add it'
+  runnableExamples:
+    import std/[sets, tables]
+
+    let data = @["bird", "word"]
+
+    ## seq:
+    let k = collect(newSeq):
+      for i, d in data.pairs:
+        if i mod 2 == 0: d
+    assert k == @["bird"]
+
+    ## seq with initialSize:
+    let x = collect(newSeqOfCap(4)):
+      for i, d in data.pairs:
+        if i mod 2 == 0: d
+    assert x == @["bird"]
+
+    ## HashSet:
+    let y = collect(initHashSet()):
+      for d in data.items: {d}
+    assert y == data.toHashSet
+
+    ## Table:
+    let z = collect(initTable(2)):
+      for i, d in data.pairs: {i: d}
+    assert z == {0: "bird", 1: "word"}.toTable
+
+  result = collectImpl(init, body)
+
+macro collect*(body: untyped): untyped {.since: (1, 5).} =
+  ## Same as `collect` but without an `init` parameter.
+  ##
+  ## **See also:**
+  ## * `sequtils.toSeq proc<sequtils.html#toSeq.t%2Cuntyped>`_
+  ## * `sequtils.mapIt template<sequtils.html#mapIt.t%2Ctyped%2Cuntyped>`_
+  runnableExamples:
+    import std/[sets, tables]
+    let data = @["bird", "word"]
+
+    # seq:
+    let k = collect:
+      for i, d in data.pairs:
+        if i mod 2 == 0: d
+    assert k == @["bird"]
+
+    ## HashSet:
+    let n = collect:
+      for d in data.items: {d}
+    assert n == data.toHashSet
+
+    ## Table:
+    let m = collect:
+      for i, d in data.pairs: {i: d}
+    assert m == {0: "bird", 1: "word"}.toTable
+
+  result = collectImpl(nil, body)
diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim
index ac41a0aad..53b3d61da 100644
--- a/lib/pure/terminal.nim
+++ b/lib/pure/terminal.nim
@@ -12,38 +12,95 @@
 ## sequences and does not depend on any other module, on Windows it uses the
 ## Windows API.
 ## Changing the style is permanent even after program termination! Use the
-## code ``system.addQuitProc(resetAttributes)`` to restore the defaults.
+## code `exitprocs.addExitProc(resetAttributes)` to restore the defaults.
 ## Similarly, if you hide the cursor, make sure to unhide it with
-## ``showCursor`` before quitting.
+## `showCursor` before quitting.
+##
+## Progress bar
+## ============
+##
+## Basic progress bar example:
+runnableExamples("-r:off"):
+  import std/[os, strutils]
+
+  for i in 0..100:
+    stdout.styledWriteLine(fgRed, "0% ", fgWhite, '#'.repeat i, if i > 50: fgGreen else: fgYellow, "\t", $i , "%")
+    sleep 42
+    cursorUp 1
+    eraseLine()
 
-import macros
-import strformat
-from strutils import toLowerAscii
-import colors
+  stdout.resetAttributes()
 
-const
-  hasThreadSupport = compileOption("threads")
+##[
+## Playing with colorful and styled text
+]##
+
+## Procs like `styledWriteLine`, `styledEcho` etc. have a temporary effect on
+## text parameters. Style parameters only affect the text parameter right after them.
+## After being called, these procs will reset the default style of the terminal.
+## While `setBackGroundColor`, `setForeGroundColor` etc. have a lasting
+## influence on the terminal, you can use `resetAttributes` to
+## reset the default style of the terminal.
+runnableExamples("-r:off"):
+  stdout.styledWriteLine({styleBright, styleBlink, styleUnderscore}, "styled text ")
+  stdout.styledWriteLine(fgRed, "red text ")
+  stdout.styledWriteLine(fgWhite, bgRed, "white text in red background")
+  stdout.styledWriteLine(" ordinary text without style ")
 
-when not hasThreadSupport:
-  import tables
-  var
-    colorsFGCache = initTable[Color, string]()
-    colorsBGCache = initTable[Color, string]()
-    styleCache = initTable[int, string]()
+  stdout.setBackGroundColor(bgCyan, true)
+  stdout.setForeGroundColor(fgBlue)
+  stdout.write("blue text in cyan background")
+  stdout.resetAttributes()
+
+  # You can specify multiple text parameters. Style parameters
+  # only affect the text parameter right after them.
+  styledEcho styleBright, fgGreen, "[PASS]", resetStyle, fgGreen, " Yay!"
+
+  stdout.styledWriteLine(fgRed, "red text ", styleBright, "bold red", fgDefault, " bold text")
+
+import std/macros
+import std/strformat
+from std/strutils import toLowerAscii, `%`, parseInt
+import std/colors
+
+when defined(windows):
+  import std/winlean
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions]
+
+type
+  PTerminal = ref object
+    trueColorIsSupported: bool
+    trueColorIsEnabled: bool
+    fgSetColor: bool
+    when defined(windows):
+      hStdout: Handle
+      hStderr: Handle
+      oldStdoutAttr: int16
+      oldStderrAttr: int16
+
+var gTerm {.threadvar.}: owned(PTerminal)
+
+when defined(windows) and defined(consoleapp):
+  proc newTerminal(): owned(PTerminal) {.gcsafe, raises: [OSError].}
+else:
+  proc newTerminal(): owned(PTerminal) {.gcsafe, raises: [].}
 
-var
-  trueColorIsSupported: bool
-  trueColorIsEnabled: bool
-  fgSetColor: bool
+proc getTerminal(): PTerminal {.inline.} =
+  if isNil(gTerm):
+    gTerm = newTerminal()
+  result = gTerm
 
 const
-  fgPrefix = "\x1b[38;2;"
-  bgPrefix = "\x1b[48;2;"
+  fgPrefix = "\e[38;2;"
+  bgPrefix = "\e[48;2;"
   ansiResetCode* = "\e[0m"
+  getPos = "\e[6n"
   stylePrefix = "\e["
 
 when defined(windows):
-  import winlean, os
+  import std/[winlean, os]
 
   const
     DUPLICATE_SAME_ACCESS = 2
@@ -63,14 +120,14 @@ when defined(windows):
   type
     SHORT = int16
     COORD = object
-      X: SHORT
-      Y: SHORT
+      x: SHORT
+      y: SHORT
 
     SMALL_RECT = object
-      Left: SHORT
-      Top: SHORT
-      Right: SHORT
-      Bottom: SHORT
+      left: SHORT
+      top: SHORT
+      right: SHORT
+      bottom: SHORT
 
     CONSOLE_SCREEN_BUFFER_INFO = object
       dwSize: COORD
@@ -83,22 +140,22 @@ when defined(windows):
       dwSize: DWORD
       bVisible: WINBOOL
 
-  proc duplicateHandle(hSourceProcessHandle: HANDLE, hSourceHandle: HANDLE,
-                       hTargetProcessHandle: HANDLE, lpTargetHandle: ptr HANDLE,
+  proc duplicateHandle(hSourceProcessHandle: Handle, hSourceHandle: Handle,
+                       hTargetProcessHandle: Handle, lpTargetHandle: ptr Handle,
                        dwDesiredAccess: DWORD, bInheritHandle: WINBOOL,
                        dwOptions: DWORD): WINBOOL{.stdcall, dynlib: "kernel32",
       importc: "DuplicateHandle".}
-  proc getCurrentProcess(): HANDLE{.stdcall, dynlib: "kernel32",
+  proc getCurrentProcess(): Handle{.stdcall, dynlib: "kernel32",
                                      importc: "GetCurrentProcess".}
-  proc getConsoleScreenBufferInfo(hConsoleOutput: HANDLE,
+  proc getConsoleScreenBufferInfo(hConsoleOutput: Handle,
     lpConsoleScreenBufferInfo: ptr CONSOLE_SCREEN_BUFFER_INFO): WINBOOL{.stdcall,
     dynlib: "kernel32", importc: "GetConsoleScreenBufferInfo".}
 
-  proc getConsoleCursorInfo(hConsoleOutput: HANDLE,
+  proc getConsoleCursorInfo(hConsoleOutput: Handle,
       lpConsoleCursorInfo: ptr CONSOLE_CURSOR_INFO): WINBOOL{.
       stdcall, dynlib: "kernel32", importc: "GetConsoleCursorInfo".}
 
-  proc setConsoleCursorInfo(hConsoleOutput: HANDLE,
+  proc setConsoleCursorInfo(hConsoleOutput: Handle,
       lpConsoleCursorInfo: ptr CONSOLE_CURSOR_INFO): WINBOOL{.
       stdcall, dynlib: "kernel32", importc: "SetConsoleCursorInfo".}
 
@@ -106,47 +163,49 @@ when defined(windows):
     var csbi: CONSOLE_SCREEN_BUFFER_INFO
     for h in handles:
       if getConsoleScreenBufferInfo(h, addr csbi) != 0:
-        return int(csbi.srWindow.Right - csbi.srWindow.Left + 1)
+        return int(csbi.srWindow.right - csbi.srWindow.left + 1)
     return 0
 
   proc terminalHeightIoctl*(handles: openArray[Handle]): int =
     var csbi: CONSOLE_SCREEN_BUFFER_INFO
     for h in handles:
       if getConsoleScreenBufferInfo(h, addr csbi) != 0:
-        return int(csbi.srWindow.Bottom - csbi.srWindow.Top + 1)
+        return int(csbi.srWindow.bottom - csbi.srWindow.top + 1)
     return 0
 
   proc terminalWidth*(): int =
+    ## Returns the terminal width in columns.
     var w: int = 0
-    w = terminalWidthIoctl([ getStdHandle(STD_INPUT_HANDLE),
+    w = terminalWidthIoctl([getStdHandle(STD_INPUT_HANDLE),
                              getStdHandle(STD_OUTPUT_HANDLE),
-                             getStdHandle(STD_ERROR_HANDLE) ] )
+                             getStdHandle(STD_ERROR_HANDLE)])
     if w > 0: return w
     return 80
 
   proc terminalHeight*(): int =
+    ## Returns the terminal height in rows.
     var h: int = 0
-    h = terminalHeightIoctl([ getStdHandle(STD_INPUT_HANDLE),
+    h = terminalHeightIoctl([getStdHandle(STD_INPUT_HANDLE),
                               getStdHandle(STD_OUTPUT_HANDLE),
-                              getStdHandle(STD_ERROR_HANDLE) ] )
+                              getStdHandle(STD_ERROR_HANDLE)])
     if h > 0: return h
     return 0
 
-  proc setConsoleCursorPosition(hConsoleOutput: HANDLE,
+  proc setConsoleCursorPosition(hConsoleOutput: Handle,
                                 dwCursorPosition: COORD): WINBOOL{.
       stdcall, dynlib: "kernel32", importc: "SetConsoleCursorPosition".}
 
   proc fillConsoleOutputCharacter(hConsoleOutput: Handle, cCharacter: char,
-                                  nLength: DWORD, dwWriteCoord: Coord,
+                                  nLength: DWORD, dwWriteCoord: COORD,
                                   lpNumberOfCharsWritten: ptr DWORD): WINBOOL{.
       stdcall, dynlib: "kernel32", importc: "FillConsoleOutputCharacterA".}
 
-  proc fillConsoleOutputAttribute(hConsoleOutput: HANDLE, wAttribute: int16,
+  proc fillConsoleOutputAttribute(hConsoleOutput: Handle, wAttribute: int16,
                                   nLength: DWORD, dwWriteCoord: COORD,
                                   lpNumberOfAttrsWritten: ptr DWORD): WINBOOL{.
       stdcall, dynlib: "kernel32", importc: "FillConsoleOutputAttribute".}
 
-  proc setConsoleTextAttribute(hConsoleOutput: HANDLE,
+  proc setConsoleTextAttribute(hConsoleOutput: Handle,
                                wAttributes: int16): WINBOOL{.
       stdcall, dynlib: "kernel32", importc: "SetConsoleTextAttribute".}
 
@@ -156,56 +215,53 @@ when defined(windows):
   proc setConsoleMode(hConsoleHandle: Handle, dwMode: DWORD): WINBOOL{.
       stdcall, dynlib: "kernel32", importc: "SetConsoleMode".}
 
-  var
-    hStdout: Handle # = createFile("CONOUT$", GENERIC_WRITE, 0, nil,
-                    #              OPEN_ALWAYS, 0, 0)
-    hStderr: Handle
-
-  block:
-    var hStdoutTemp = getStdHandle(STD_OUTPUT_HANDLE)
-    if duplicateHandle(getCurrentProcess(), hStdoutTemp, getCurrentProcess(),
-                       addr(hStdout), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
-      when defined(consoleapp):
-        raiseOSError(osLastError())
-    var hStderrTemp = getStdHandle(STD_ERROR_HANDLE)
-    if duplicateHandle(getCurrentProcess(), hStderrTemp, getCurrentProcess(),
-                       addr(hStderr), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
-      when defined(consoleapp):
-        raiseOSError(osLastError())
-
-  proc getCursorPos(h: Handle): tuple [x,y: int] =
-    var c: CONSOLESCREENBUFFERINFO
+  proc getCursorPos(h: Handle): tuple [x, y: int] =
+    var c: CONSOLE_SCREEN_BUFFER_INFO
     if getConsoleScreenBufferInfo(h, addr(c)) == 0:
       raiseOSError(osLastError())
-    return (int(c.dwCursorPosition.X), int(c.dwCursorPosition.Y))
+    return (int(c.dwCursorPosition.x), int(c.dwCursorPosition.y))
+
+  proc getCursorPos*(): tuple [x, y: int] {.raises: [ValueError, IOError, OSError].} =
+    return getCursorPos(getStdHandle(STD_OUTPUT_HANDLE))
 
   proc setCursorPos(h: Handle, x, y: int) =
     var c: COORD
-    c.X = int16(x)
-    c.Y = int16(y)
+    c.x = int16(x)
+    c.y = int16(y)
     if setConsoleCursorPosition(h, c) == 0:
       raiseOSError(osLastError())
 
   proc getAttributes(h: Handle): int16 =
-    var c: CONSOLESCREENBUFFERINFO
+    var c: CONSOLE_SCREEN_BUFFER_INFO
     # workaround Windows bugs: try several times
     if getConsoleScreenBufferInfo(h, addr(c)) != 0:
       return c.wAttributes
     return 0x70'i16 # ERROR: return white background, black text
 
-  var
-    oldStdoutAttr = getAttributes(hStdout)
-    oldStderrAttr = getAttributes(hStderr)
+  proc initTerminal(term: PTerminal) =
+    var hStdoutTemp = getStdHandle(STD_OUTPUT_HANDLE)
+    if duplicateHandle(getCurrentProcess(), hStdoutTemp, getCurrentProcess(),
+                       addr(term.hStdout), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
+      when defined(consoleapp):
+        raiseOSError(osLastError())
+    var hStderrTemp = getStdHandle(STD_ERROR_HANDLE)
+    if duplicateHandle(getCurrentProcess(), hStderrTemp, getCurrentProcess(),
+                       addr(term.hStderr), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
+      when defined(consoleapp):
+        raiseOSError(osLastError())
+    term.oldStdoutAttr = getAttributes(term.hStdout)
+    term.oldStderrAttr = getAttributes(term.hStderr)
 
   template conHandle(f: File): Handle =
-    if f == stderr: hStderr else: hStdout
+    let term = getTerminal()
+    if f == stderr: term.hStderr else: term.hStdout
 
 else:
-  import termios, posix, os, parseutils
+  import std/[termios, posix, os, parseutils]
 
   proc setRaw(fd: FileHandle, time: cint = TCSAFLUSH) =
     var mode: Termios
-    discard fd.tcgetattr(addr mode)
+    discard fd.tcGetAttr(addr mode)
     mode.c_iflag = mode.c_iflag and not Cflag(BRKINT or ICRNL or INPCK or
       ISTRIP or IXON)
     mode.c_oflag = mode.c_oflag and not Cflag(OPOST)
@@ -213,7 +269,49 @@ else:
     mode.c_lflag = mode.c_lflag and not Cflag(ECHO or ICANON or IEXTEN or ISIG)
     mode.c_cc[VMIN] = 1.cuchar
     mode.c_cc[VTIME] = 0.cuchar
-    discard fd.tcsetattr(time, addr mode)
+    discard fd.tcSetAttr(time, addr mode)
+
+  proc getCursorPos*(): tuple [x, y: int] {.raises: [ValueError, IOError].} =
+    ## Returns cursor position (x, y)
+    ## writes to stdout and expects the terminal to respond via stdin
+    var
+      xStr = ""
+      yStr = ""
+      ch: char
+      ct: int
+      readX = false
+
+    # use raw mode to ask terminal for cursor position
+    let fd = getFileHandle(stdin)
+    var oldMode: Termios
+    discard fd.tcGetAttr(addr oldMode)
+    fd.setRaw()
+    stdout.write(getPos)
+    flushFile(stdout)
+
+    try:
+      # parse response format: [yyy;xxxR
+      while true:
+        let n = readBuffer(stdin, addr ch, 1)
+        if n == 0 or ch == 'R':
+          if xStr == "" or yStr == "":
+            raise newException(ValueError, "Got character position message that was missing data")
+          break
+        ct += 1
+        if ct > 16:
+          raise newException(ValueError, "Got unterminated character position message from terminal")
+        if ch == ';':
+          readX = true
+        elif ch in {'0'..'9'}:
+          if readX:
+            xStr.add(ch)
+          else:
+            yStr.add(ch)
+    finally:
+      # restore previous terminal mode
+      discard fd.tcSetAttr(TCSADRAIN, addr oldMode)
+
+    return (parseInt(xStr), parseInt(yStr))
 
   proc terminalWidthIoctl*(fds: openArray[int]): int =
     ## Returns terminal width from first fd that supports the ioctl.
@@ -239,36 +337,65 @@ else:
     ## Returns some reasonable terminal width from either standard file
     ## descriptors, controlling terminal, environment variables or tradition.
 
-    var w = terminalWidthIoctl([0, 1, 2])   #Try standard file descriptors
+    # POSIX environment variable takes precendence.
+    # _COLUMNS_: This variable shall represent a decimal integer >0 used
+    # to indicate the user's preferred width in column positions for
+    # the terminal screen or window. If this variable is unset or null,
+    # the implementation determines the number of columns, appropriate
+    # for the terminal or window, in an unspecified manner.
+    # When COLUMNS is set, any terminal-width information implied by TERM
+    # is overridden. Users and conforming applications should not set COLUMNS
+    # unless they wish to override the system selection and produce output
+    # unrelated to the terminal characteristics.
+    # See POSIX Base Definitions Section 8.1 Environment Variable Definition
+
+    var w: int
+    var s = getEnv("COLUMNS") # Try standard env var
+    if len(s) > 0 and parseSaturatedNatural(s, w) > 0 and w > 0:
+      return w
+    w = terminalWidthIoctl([0, 1, 2]) # Try standard file descriptors
     if w > 0: return w
-    var cterm = newString(L_ctermid)        #Try controlling tty
+    var cterm = newString(L_ctermid) # Try controlling tty
     var fd = open(ctermid(cstring(cterm)), O_RDONLY)
     if fd != -1:
-      w = terminalWidthIoctl([ int(fd) ])
+      w = terminalWidthIoctl([int(fd)])
     discard close(fd)
     if w > 0: return w
-    var s = getEnv("COLUMNS")               #Try standard env var
-    if len(s) > 0 and parseInt(string(s), w) > 0 and w > 0:
-      return w
-    return 80                               #Finally default to venerable value
+    return 80 # Finally default to venerable value
 
   proc terminalHeight*(): int =
     ## Returns some reasonable terminal height from either standard file
     ## descriptors, controlling terminal, environment variables or tradition.
     ## Zero is returned if the height could not be determined.
 
-    var h = terminalHeightIoctl([0, 1, 2])  # Try standard file descriptors
+    # POSIX environment variable takes precendence.
+    # _LINES_: This variable shall represent a decimal integer >0 used
+    # to indicate the user's preferred number of lines on a page or
+    # the vertical screen or window size in lines. A line in this case
+    # is a vertical measure large enough to hold the tallest character
+    # in the character set being displayed. If this variable is unset or null,
+    # the implementation determines the number of lines, appropriate
+    # for the terminal or window (size, terminal baud rate, and so on),
+    # in an unspecified manner.
+    # When LINES is set, any terminal-height information implied by TERM
+    # is overridden. Users and conforming applications should not set LINES
+    # unless they wish to override the system selection and produce output
+    # unrelated to the terminal characteristics.
+    # See POSIX Base Definitions Section 8.1 Environment Variable Definition
+
+    var h: int
+    var s = getEnv("LINES") # Try standard env var
+    if len(s) > 0 and parseSaturatedNatural(s, h) > 0 and h > 0:
+      return h
+    h = terminalHeightIoctl([0, 1, 2]) # Try standard file descriptors
     if h > 0: return h
-    var cterm = newString(L_ctermid)        # Try controlling tty
+    var cterm = newString(L_ctermid) # Try controlling tty
     var fd = open(ctermid(cstring(cterm)), O_RDONLY)
     if fd != -1:
-      h = terminalHeightIoctl([ int(fd) ])
+      h = terminalHeightIoctl([int(fd)])
     discard close(fd)
     if h > 0: return h
-    var s = getEnv("LINES")                 # Try standard env var
-    if len(s) > 0 and parseInt(string(s), h) > 0 and h > 0:
-      return h
-    return 0                                # Could not determine height
+    return 0 # Could not determine height
 
 proc terminalSize*(): tuple[w, h: int] =
   ## Returns the terminal width and height as a tuple. Internally calls
@@ -306,42 +433,45 @@ proc setCursorPos*(f: File, x, y: int) =
     let h = conHandle(f)
     setCursorPos(h, x, y)
   else:
-    f.write(fmt"{stylePrefix}{y};{x}f")
+    f.write(fmt"{stylePrefix}{y+1};{x+1}f")
 
 proc setCursorXPos*(f: File, x: int) =
   ## Sets the terminal's cursor to the x position.
   ## The y position is not changed.
   when defined(windows):
     let h = conHandle(f)
-    var scrbuf: CONSOLESCREENBUFFERINFO
+    var scrbuf: CONSOLE_SCREEN_BUFFER_INFO
     if getConsoleScreenBufferInfo(h, addr(scrbuf)) == 0:
       raiseOSError(osLastError())
     var origin = scrbuf.dwCursorPosition
-    origin.X = int16(x)
+    origin.x = int16(x)
     if setConsoleCursorPosition(h, origin) == 0:
       raiseOSError(osLastError())
   else:
-    f.write(fmt"{stylePrefix}{x}G")
+    f.write(fmt"{stylePrefix}{x+1}G")
 
 when defined(windows):
   proc setCursorYPos*(f: File, y: int) =
     ## Sets the terminal's cursor to the y position.
     ## The x position is not changed.
-    ## **Warning**: This is not supported on UNIX!
+    ## .. warning:: This is not supported on UNIX!
     when defined(windows):
       let h = conHandle(f)
-      var scrbuf: CONSOLESCREENBUFFERINFO
+      var scrbuf: CONSOLE_SCREEN_BUFFER_INFO
       if getConsoleScreenBufferInfo(h, addr(scrbuf)) == 0:
         raiseOSError(osLastError())
       var origin = scrbuf.dwCursorPosition
-      origin.Y = int16(y)
+      origin.y = int16(y)
       if setConsoleCursorPosition(h, origin) == 0:
         raiseOSError(osLastError())
     else:
       discard
 
-proc cursorUp*(f: File, count=1) =
+proc cursorUp*(f: File, count = 1) =
   ## Moves the cursor up by `count` rows.
+  runnableExamples("-r:off"):
+    stdout.cursorUp(2)
+    write(stdout, "Hello World!") # anything written at that location will be erased/replaced with this
   when defined(windows):
     let h = conHandle(f)
     var p = getCursorPos(h)
@@ -350,8 +480,11 @@ proc cursorUp*(f: File, count=1) =
   else:
     f.write("\e[" & $count & 'A')
 
-proc cursorDown*(f: File, count=1) =
+proc cursorDown*(f: File, count = 1) =
   ## Moves the cursor down by `count` rows.
+  runnableExamples("-r:off"):
+    stdout.cursorDown(2)
+    write(stdout, "Hello World!") # anything written at that location will be erased/replaced with this
   when defined(windows):
     let h = conHandle(f)
     var p = getCursorPos(h)
@@ -360,8 +493,11 @@ proc cursorDown*(f: File, count=1) =
   else:
     f.write(fmt"{stylePrefix}{count}B")
 
-proc cursorForward*(f: File, count=1) =
+proc cursorForward*(f: File, count = 1) =
   ## Moves the cursor forward by `count` columns.
+  runnableExamples("-r:off"):
+    stdout.cursorForward(2)
+    write(stdout, "Hello World!") # anything written at that location will be erased/replaced with this
   when defined(windows):
     let h = conHandle(f)
     var p = getCursorPos(h)
@@ -370,8 +506,11 @@ proc cursorForward*(f: File, count=1) =
   else:
     f.write(fmt"{stylePrefix}{count}C")
 
-proc cursorBackward*(f: File, count=1) =
+proc cursorBackward*(f: File, count = 1) =
   ## Moves the cursor backward by `count` columns.
+  runnableExamples("-r:off"):
+    stdout.cursorBackward(2)
+    write(stdout, "Hello World!") # anything written at that location will be erased/replaced with this
   when defined(windows):
     let h = conHandle(f)
     var p = getCursorPos(h)
@@ -413,17 +552,20 @@ else:
 
 proc eraseLine*(f: File) =
   ## Erases the entire current line.
+  runnableExamples("-r:off"):
+    write(stdout, "never mind")
+    stdout.eraseLine() # nothing will be printed on the screen
   when defined(windows):
     let h = conHandle(f)
-    var scrbuf: CONSOLESCREENBUFFERINFO
+    var scrbuf: CONSOLE_SCREEN_BUFFER_INFO
     var numwrote: DWORD
     if getConsoleScreenBufferInfo(h, addr(scrbuf)) == 0:
       raiseOSError(osLastError())
     var origin = scrbuf.dwCursorPosition
-    origin.X = 0'i16
+    origin.x = 0'i16
     if setConsoleCursorPosition(h, origin) == 0:
       raiseOSError(osLastError())
-    var wt: DWORD = scrbuf.dwSize.X - origin.X
+    var wt: DWORD = scrbuf.dwSize.x - origin.x
     if fillConsoleOutputCharacter(h, ' ', wt,
                                   origin, addr(numwrote)) == 0:
       raiseOSError(osLastError())
@@ -438,13 +580,13 @@ proc eraseScreen*(f: File) =
   ## Erases the screen with the background colour and moves the cursor to home.
   when defined(windows):
     let h = conHandle(f)
-    var scrbuf: CONSOLESCREENBUFFERINFO
+    var scrbuf: CONSOLE_SCREEN_BUFFER_INFO
     var numwrote: DWORD
     var origin: COORD # is inititalized to 0, 0
 
     if getConsoleScreenBufferInfo(h, addr(scrbuf)) == 0:
       raiseOSError(osLastError())
-    let numChars = int32(scrbuf.dwSize.X)*int32(scrbuf.dwSize.Y)
+    let numChars = int32(scrbuf.dwSize.x)*int32(scrbuf.dwSize.y)
 
     if fillConsoleOutputCharacter(h, ' ', numChars,
                                   origin, addr(numwrote)) == 0:
@@ -456,44 +598,38 @@ proc eraseScreen*(f: File) =
   else:
     f.write("\e[2J")
 
+when not defined(windows):
+  var
+    gFG {.threadvar.}: int
+    gBG {.threadvar.}: int
+
 proc resetAttributes*(f: File) =
   ## Resets all attributes.
   when defined(windows):
+    let term = getTerminal()
     if f == stderr:
-      discard setConsoleTextAttribute(hStderr, oldStderrAttr)
+      discard setConsoleTextAttribute(term.hStderr, term.oldStderrAttr)
     else:
-      discard setConsoleTextAttribute(hStdout, oldStdoutAttr)
+      discard setConsoleTextAttribute(term.hStdout, term.oldStdoutAttr)
   else:
     f.write(ansiResetCode)
+    gFG = 0
+    gBG = 0
 
 type
-  Style* = enum          ## different styles for text output
-    styleBright = 1,     ## bright text
-    styleDim,            ## dim text
-    styleItalic,         ## italic (or reverse on terminals not supporting)
-    styleUnderscore = 4, ## underscored text
-    styleBlink,          ## blinking/bold text
-    styleReverse = 7,    ## reverse
-    styleHidden          ## hidden text
-    styleStrikethrough,  ## strikethrough
-
-{.deprecated: [TStyle: Style].}
-{.deprecated: [styleUnknown: styleItalic].}
-
-when not defined(windows):
-  var
-    gFG {.threadvar.}: int
-    gBG {.threadvar.}: int
+  Style* = enum        ## Different styles for text output.
+    styleBright = 1,   ## bright text
+    styleDim,          ## dim text
+    styleItalic,       ## italic (or reverse on terminals not supporting)
+    styleUnderscore,   ## underscored text
+    styleBlink,        ## blinking/bold text
+    styleBlinkRapid,   ## rapid blinking/bold text (not widely supported)
+    styleReverse,      ## reverse
+    styleHidden,       ## hidden text
+    styleStrikethrough ## strikethrough
 
 proc ansiStyleCode*(style: int): string =
-  when hasThreadSupport:
-    result = fmt"{stylePrefix}{style}m"
-  else:
-    if styleCache.hasKey(style):
-      result = styleCache[style]
-    else:
-      result = fmt"{stylePrefix}{style}m"
-      styleCache[style] = result
+  result = fmt"{stylePrefix}{style}m"
 
 template ansiStyleCode*(style: Style): string =
   ansiStyleCode(style.int)
@@ -520,10 +656,11 @@ proc setStyle*(f: File, style: set[Style]) =
 proc writeStyled*(txt: string, style: set[Style] = {styleBright}) =
   ## Writes the text `txt` in a given `style` to stdout.
   when defined(windows):
-    var old = getAttributes(hStdout)
+    let term = getTerminal()
+    var old = getAttributes(term.hStdout)
     stdout.setStyle(style)
     stdout.write(txt)
-    discard setConsoleTextAttribute(hStdout, old)
+    discard setConsoleTextAttribute(term.hStdout, old)
   else:
     stdout.setStyle(style)
     stdout.write(txt)
@@ -534,37 +671,34 @@ proc writeStyled*(txt: string, style: set[Style] = {styleBright}) =
       stdout.write(ansiStyleCode(gBG))
 
 type
-  ForegroundColor* = enum  ## terminal's foreground colors
-    fgBlack = 30,          ## black
-    fgRed,                 ## red
-    fgGreen,               ## green
-    fgYellow,              ## yellow
-    fgBlue,                ## blue
-    fgMagenta,             ## magenta
-    fgCyan,                ## cyan
-    fgWhite,               ## white
-    fg8Bit,                ## 256-color (not supported, see ``enableTrueColors`` instead.)
-    fgDefault              ## default terminal foreground color
-
-  BackgroundColor* = enum  ## terminal's background colors
-    bgBlack = 40,          ## black
-    bgRed,                 ## red
-    bgGreen,               ## green
-    bgYellow,              ## yellow
-    bgBlue,                ## blue
-    bgMagenta,             ## magenta
-    bgCyan,                ## cyan
-    bgWhite,               ## white
-    bg8Bit,                ## 256-color (not supported, see ``enableTrueColors`` instead.)
-    bgDefault              ## default terminal background color
-
-{.deprecated: [TForegroundColor: ForegroundColor,
-               TBackgroundColor: BackgroundColor].}
+  ForegroundColor* = enum ## Terminal's foreground colors.
+    fgBlack = 30,         ## black
+    fgRed,                ## red
+    fgGreen,              ## green
+    fgYellow,             ## yellow
+    fgBlue,               ## blue
+    fgMagenta,            ## magenta
+    fgCyan,               ## cyan
+    fgWhite,              ## white
+    fg8Bit,               ## 256-color (not supported, see `enableTrueColors` instead.)
+    fgDefault             ## default terminal foreground color
+
+  BackgroundColor* = enum ## Terminal's background colors.
+    bgBlack = 40,         ## black
+    bgRed,                ## red
+    bgGreen,              ## green
+    bgYellow,             ## yellow
+    bgBlue,               ## blue
+    bgMagenta,            ## magenta
+    bgCyan,               ## cyan
+    bgWhite,              ## white
+    bg8Bit,               ## 256-color (not supported, see `enableTrueColors` instead.)
+    bgDefault             ## default terminal background color
 
 when defined(windows):
   var defaultForegroundColor, defaultBackgroundColor: int16 = 0xFFFF'i16 # Default to an invalid value 0xFFFF
 
-proc setForegroundColor*(f: File, fg: ForegroundColor, bright=false) =
+proc setForegroundColor*(f: File, fg: ForegroundColor, bright = false) =
   ## Sets the terminal's foreground color.
   when defined(windows):
     let h = conHandle(f)
@@ -572,7 +706,7 @@ proc setForegroundColor*(f: File, fg: ForegroundColor, bright=false) =
     if defaultForegroundColor == 0xFFFF'i16:
       defaultForegroundColor = old
     old = if bright: old or FOREGROUND_INTENSITY
-          else:      old and not(FOREGROUND_INTENSITY)
+          else: old and not(FOREGROUND_INTENSITY)
     const lookup: array[ForegroundColor, int] = [
       0, # ForegroundColor enum with ordinal 30
       (FOREGROUND_RED),
@@ -582,18 +716,18 @@ proc setForegroundColor*(f: File, fg: ForegroundColor, bright=false) =
       (FOREGROUND_RED or FOREGROUND_BLUE),
       (FOREGROUND_BLUE or FOREGROUND_GREEN),
       (FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED),
-      0, # fg8Bit not supported, see ``enableTrueColors`` instead.
+      0, # fg8Bit not supported, see `enableTrueColors` instead.
       0] # unused
     if fg == fgDefault:
-      discard setConsoleTextAttribute(h, toU16(old or defaultForegroundColor))
+      discard setConsoleTextAttribute(h, cast[int16](cast[uint16](old) or cast[uint16](defaultForegroundColor)))
     else:
-      discard setConsoleTextAttribute(h, toU16(old or lookup[fg]))
+      discard setConsoleTextAttribute(h, cast[int16](cast[uint16](old) or cast[uint16](lookup[fg])))
   else:
     gFG = ord(fg)
     if bright: inc(gFG, 60)
     f.write(ansiStyleCode(gFG))
 
-proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) =
+proc setBackgroundColor*(f: File, bg: BackgroundColor, bright = false) =
   ## Sets the terminal's background color.
   when defined(windows):
     let h = conHandle(f)
@@ -601,7 +735,7 @@ proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) =
     if defaultBackgroundColor == 0xFFFF'i16:
       defaultBackgroundColor = old
     old = if bright: old or BACKGROUND_INTENSITY
-          else:      old and not(BACKGROUND_INTENSITY)
+          else: old and not(BACKGROUND_INTENSITY)
     const lookup: array[BackgroundColor, int] = [
       0, # BackgroundColor enum with ordinal 40
       (BACKGROUND_RED),
@@ -611,18 +745,18 @@ proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) =
       (BACKGROUND_RED or BACKGROUND_BLUE),
       (BACKGROUND_BLUE or BACKGROUND_GREEN),
       (BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED),
-      0, # bg8Bit not supported, see ``enableTrueColors`` instead.
+      0, # bg8Bit not supported, see `enableTrueColors` instead.
       0] # unused
     if bg == bgDefault:
-      discard setConsoleTextAttribute(h, toU16(old or defaultBackgroundColor))
+      discard setConsoleTextAttribute(h, cast[int16](cast[uint16](old) or cast[uint16](defaultBackgroundColor)))
     else:
-      discard setConsoleTextAttribute(h, toU16(old or lookup[bg]))
+      discard setConsoleTextAttribute(h, cast[int16](cast[uint16](old) or cast[uint16](lookup[bg])))
   else:
     gBG = ord(bg)
     if bright: inc(gBG, 60)
     f.write(ansiStyleCode(gBG))
 
-proc ansiForegroundColorCode*(fg: ForegroundColor, bright=false): string =
+proc ansiForegroundColorCode*(fg: ForegroundColor, bright = false): string =
   var style = ord(fg)
   if bright: inc(style, 60)
   return ansiStyleCode(style)
@@ -632,49 +766,36 @@ template ansiForegroundColorCode*(fg: static[ForegroundColor],
   ansiStyleCode(fg.int + bright.int * 60)
 
 proc ansiForegroundColorCode*(color: Color): string =
-  when hasThreadSupport:
-    let rgb = extractRGB(color)
-    result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
-  else:
-    if colorsFGCache.hasKey(color):
-      result = colorsFGCache[color]
-    else:
-      let rgb = extractRGB(color)
-      result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
-      colorsFGCache[color] = result
+  let rgb = extractRGB(color)
+  result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
 
 template ansiForegroundColorCode*(color: static[Color]): string =
   const rgb = extractRGB(color)
-  (static(fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"))
+  # no usage of `fmt`, see issue #7632
+  (static("$1$2;$3;$4m" % [$fgPrefix, $(rgb.r), $(rgb.g), $(rgb.b)]))
 
 proc ansiBackgroundColorCode*(color: Color): string =
-  when hasThreadSupport:
-    let rgb = extractRGB(color)
-    result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
-  else:
-    if colorsBGCache.hasKey(color):
-      result = colorsBGCache[color]
-    else:
-      let rgb = extractRGB(color)
-      result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
-      colorsFGCache[color] = result
+  let rgb = extractRGB(color)
+  result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
 
 template ansiBackgroundColorCode*(color: static[Color]): string =
   const rgb = extractRGB(color)
-  (static(fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"))
+  # no usage of `fmt`, see issue #7632
+  (static("$1$2;$3;$4m" % [$bgPrefix, $(rgb.r), $(rgb.g), $(rgb.b)]))
 
 proc setForegroundColor*(f: File, color: Color) =
   ## Sets the terminal's foreground true color.
-  if trueColorIsEnabled:
+  if getTerminal().trueColorIsEnabled:
     f.write(ansiForegroundColorCode(color))
 
 proc setBackgroundColor*(f: File, color: Color) =
   ## Sets the terminal's background true color.
-  if trueColorIsEnabled:
+  if getTerminal().trueColorIsEnabled:
     f.write(ansiBackgroundColorCode(color))
 
 proc setTrueColor(f: File, color: Color) =
-  if fgSetColor:
+  let term = getTerminal()
+  if term.fgSetColor:
     setForegroundColor(f, color)
   else:
     setBackgroundColor(f, color)
@@ -691,10 +812,10 @@ proc isatty*(f: File): bool =
   result = isatty(getFileHandle(f)) != 0'i32
 
 type
-  TerminalCmd* = enum  ## commands that can be expressed as arguments
-    resetStyle,        ## reset attributes
-    fgColor,           ## set foreground's true color
-    bgColor            ## set background's true color
+  TerminalCmd* = enum ## commands that can be expressed as arguments
+    resetStyle,       ## reset attributes
+    fgColor,          ## set foreground's true color
+    bgColor           ## set background's true color
 
 template styledEchoProcessArg(f: File, s: string) = write f, s
 template styledEchoProcessArg(f: File, style: Style) = setStyle(f, {style})
@@ -708,29 +829,23 @@ template styledEchoProcessArg(f: File, color: Color) =
 template styledEchoProcessArg(f: File, cmd: TerminalCmd) =
   when cmd == resetStyle:
     resetAttributes(f)
-  when cmd == fgColor:
-    fgSetColor = true
-  when cmd == bgColor:
-    fgSetColor = false
+  elif cmd in {fgColor, bgColor}:
+    let term = getTerminal()
+    term.fgSetColor = cmd == fgColor
 
 macro styledWrite*(f: File, m: varargs[typed]): untyped =
-  ## Similar to ``write``, but treating terminal style arguments specially.
-  ## When some argument is ``Style``, ``set[Style]``, ``ForegroundColor``,
-  ## ``BackgroundColor`` or ``TerminalCmd`` then it is not sent directly to
-  ## ``f``, but instead corresponding terminal style proc is called.
-  ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   stdout.styledWrite(fgRed, "red text ")
-  ##   stdout.styledWrite(fgGreen, "green text")
-  ##
-  let m = callsite()
+  ## Similar to `write`, but treating terminal style arguments specially.
+  ## When some argument is `Style`, `set[Style]`, `ForegroundColor`,
+  ## `BackgroundColor` or `TerminalCmd` then it is not sent directly to
+  ## `f`, but instead corresponding terminal style proc is called.
+  runnableExamples("-r:off"):
+    stdout.styledWrite(fgRed, "red text ")
+    stdout.styledWrite(fgGreen, "green text")
+
   var reset = false
   result = newNimNode(nnkStmtList)
 
-  for i in countup(2, m.len - 1):
+  for i in countup(0, m.len - 1):
     let item = m[i]
     case item.kind
     of nnkStrLit..nnkTripleStrLit:
@@ -748,24 +863,20 @@ macro styledWrite*(f: File, m: varargs[typed]): untyped =
   if reset: result.add(newCall(bindSym"resetAttributes", f))
 
 template styledWriteLine*(f: File, args: varargs[untyped]) =
-  ## Calls ``styledWrite`` and appends a newline at the end.
-  ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   proc error(msg: string) =
-  ##     styledWriteLine(stderr, fgRed, "Error: ", resetStyle, msg)
-  ##
+  ## Calls `styledWrite` and appends a newline at the end.
+  runnableExamples:
+    proc error(msg: string) =
+      styledWriteLine(stderr, fgRed, "Error: ", resetStyle, msg)
+
   styledWrite(f, args)
   write(f, "\n")
 
 template styledEcho*(args: varargs[untyped]) =
-  ## Echoes styles arguments to stdout using ``styledWriteLine``.
+  ## Echoes styles arguments to stdout using `styledWriteLine`.
   stdout.styledWriteLine(args)
 
 proc getch*(): char =
-  ## Read a single character from the terminal, blocking until it is entered.
+  ## Reads a single character from the terminal, blocking until it is entered.
   ## The character is not printed to the terminal.
   when defined(windows):
     let fd = getStdHandle(STD_INPUT_HANDLE)
@@ -781,62 +892,55 @@ proc getch*(): char =
   else:
     let fd = getFileHandle(stdin)
     var oldMode: Termios
-    discard fd.tcgetattr(addr oldMode)
+    discard fd.tcGetAttr(addr oldMode)
     fd.setRaw()
     result = stdin.readChar()
-    discard fd.tcsetattr(TCSADRAIN, addr oldMode)
+    discard fd.tcSetAttr(TCSADRAIN, addr oldMode)
 
 when defined(windows):
-  from unicode import toUTF8, Rune, runeLenAt
-
-  proc readPasswordFromStdin*(prompt: string, password: var TaintedString):
+  proc readPasswordFromStdin*(prompt: string, password: var string):
                               bool {.tags: [ReadIOEffect, WriteIOEffect].} =
     ## Reads a `password` from stdin without printing it. `password` must not
-    ## be ``nil``! Returns ``false`` if the end of the file has been reached,
-    ## ``true`` otherwise.
-    password.string.setLen(0)
+    ## be `nil`! Returns `false` if the end of the file has been reached,
+    ## `true` otherwise.
+    password.setLen(0)
     stdout.write(prompt)
-    while true:
-      let c = getch()
-      case c.char
-      of '\r', chr(0xA):
-        break
-      of '\b':
-        # ensure we delete the whole UTF-8 character:
-        var i = 0
-        var x = 1
-        while i < password.len:
-          x = runeLenAt(password.string, i)
-          inc i, x
-        password.string.setLen(max(password.len - x, 0))
-      of chr(0x0):
-        # modifier key - ignore - for details see
-        # https://github.com/nim-lang/Nim/issues/7764
-        continue
-      else:
-        password.string.add(toUTF8(c.Rune))
+    let hi = createFileA("CONIN$",
+      GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0)
+    var mode = DWORD 0
+    discard getConsoleMode(hi, addr mode)
+    let origMode = mode
+    const
+      ENABLE_PROCESSED_INPUT = 1
+      ENABLE_ECHO_INPUT = 4
+    mode = (mode or ENABLE_PROCESSED_INPUT) and not ENABLE_ECHO_INPUT
+
+    discard setConsoleMode(hi, mode)
+    result = readLine(stdin, password)
+    discard setConsoleMode(hi, origMode)
+    discard closeHandle(hi)
     stdout.write "\n"
 
 else:
-  import termios
+  import std/termios
 
-  proc readPasswordFromStdin*(prompt: string, password: var TaintedString):
+  proc readPasswordFromStdin*(prompt: string, password: var string):
                             bool {.tags: [ReadIOEffect, WriteIOEffect].} =
-    password.string.setLen(0)
+    password.setLen(0)
     let fd = stdin.getFileHandle()
     var cur, old: Termios
-    discard fd.tcgetattr(cur.addr)
+    discard fd.tcGetAttr(cur.addr)
     old = cur
     cur.c_lflag = cur.c_lflag and not Cflag(ECHO)
-    discard fd.tcsetattr(TCSADRAIN, cur.addr)
+    discard fd.tcSetAttr(TCSADRAIN, cur.addr)
     stdout.write prompt
     result = stdin.readLine(password)
     stdout.write "\n"
-    discard fd.tcsetattr(TCSADRAIN, old.addr)
+    discard fd.tcSetAttr(TCSADRAIN, old.addr)
 
-proc readPasswordFromStdin*(prompt = "password: "): TaintedString =
+proc readPasswordFromStdin*(prompt = "password: "): string =
   ## Reads a password from stdin without printing it.
-  result = TaintedString("")
+  result = ""
   discard readPasswordFromStdin(prompt, result)
 
 
@@ -844,20 +948,20 @@ proc readPasswordFromStdin*(prompt = "password: "): TaintedString =
 template hideCursor*() = hideCursor(stdout)
 template showCursor*() = showCursor(stdout)
 template setCursorPos*(x, y: int) = setCursorPos(stdout, x, y)
-template setCursorXPos*(x: int)   = setCursorXPos(stdout, x)
+template setCursorXPos*(x: int) = setCursorXPos(stdout, x)
 when defined(windows):
-  template setCursorYPos*(x: int)  = setCursorYPos(stdout, x)
-template cursorUp*(count=1)       = cursorUp(stdout, count)
-template cursorDown*(count=1)     = cursorDown(stdout, count)
-template cursorForward*(count=1)  = cursorForward(stdout, count)
-template cursorBackward*(count=1) = cursorBackward(stdout, count)
-template eraseLine*()             = eraseLine(stdout)
-template eraseScreen*()           = eraseScreen(stdout)
+  template setCursorYPos*(x: int) = setCursorYPos(stdout, x)
+template cursorUp*(count = 1) = cursorUp(stdout, count)
+template cursorDown*(count = 1) = cursorDown(stdout, count)
+template cursorForward*(count = 1) = cursorForward(stdout, count)
+template cursorBackward*(count = 1) = cursorBackward(stdout, count)
+template eraseLine*() = eraseLine(stdout)
+template eraseScreen*() = eraseScreen(stdout)
 template setStyle*(style: set[Style]) =
   setStyle(stdout, style)
-template setForegroundColor*(fg: ForegroundColor, bright=false) =
+template setForegroundColor*(fg: ForegroundColor, bright = false) =
   setForegroundColor(stdout, fg, bright)
-template setBackgroundColor*(bg: BackgroundColor, bright=false) =
+template setBackgroundColor*(bg: BackgroundColor, bright = false) =
   setBackgroundColor(stdout, bg, bright)
 template setForegroundColor*(color: Color) =
   setForegroundColor(stdout, color)
@@ -866,96 +970,64 @@ template setBackgroundColor*(color: Color) =
 proc resetAttributes*() {.noconv.} =
   ## Resets all attributes on stdout.
   ## It is advisable to register this as a quit proc with
-  ## ``system.addQuitProc(resetAttributes)``.
+  ## `exitprocs.addExitProc(resetAttributes)`.
   resetAttributes(stdout)
 
 proc isTrueColorSupported*(): bool =
   ## Returns true if a terminal supports true color.
-  return trueColorIsSupported
+  return getTerminal().trueColorIsSupported
 
 when defined(windows):
-  import os
+  import std/os
 
 proc enableTrueColors*() =
-  ## Enable true color.
+  ## Enables true color.
+  var term = getTerminal()
   when defined(windows):
     var
       ver: OSVERSIONINFO
     ver.dwOSVersionInfoSize = sizeof(ver).DWORD
     let res = getVersionExW(addr ver)
     if res == 0:
-      trueColorIsSupported = false
+      term.trueColorIsSupported = false
     else:
-      trueColorIsSupported = ver.dwMajorVersion > 10 or
+      term.trueColorIsSupported = ver.dwMajorVersion > 10 or
         (ver.dwMajorVersion == 10 and (ver.dwMinorVersion > 0 or
         (ver.dwMinorVersion == 0 and ver.dwBuildNumber >= 10586)))
-    if not trueColorIsSupported:
-      trueColorIsSupported = getEnv("ANSICON_DEF").len > 0
+    if not term.trueColorIsSupported:
+      term.trueColorIsSupported = getEnv("ANSICON_DEF").len > 0
 
-    if trueColorIsSupported:
+    if term.trueColorIsSupported:
       if getEnv("ANSICON_DEF").len == 0:
         var mode: DWORD = 0
         if getConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), addr(mode)) != 0:
           mode = mode or ENABLE_VIRTUAL_TERMINAL_PROCESSING
           if setConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), mode) != 0:
-            trueColorIsEnabled = true
+            term.trueColorIsEnabled = true
           else:
-            trueColorIsEnabled = false
+            term.trueColorIsEnabled = false
       else:
-        trueColorIsEnabled = true
+        term.trueColorIsEnabled = true
   else:
-    trueColorIsSupported = string(getEnv("COLORTERM")).toLowerAscii() in ["truecolor", "24bit"]
-    trueColorIsEnabled = trueColorIsSupported
+    term.trueColorIsSupported = getEnv("COLORTERM").toLowerAscii() in [
+        "truecolor", "24bit"]
+    term.trueColorIsEnabled = term.trueColorIsSupported
 
 proc disableTrueColors*() =
-  ## Disable true color.
+  ## Disables true color.
+  var term = getTerminal()
   when defined(windows):
-    if trueColorIsSupported:
+    if term.trueColorIsSupported:
       if getEnv("ANSICON_DEF").len == 0:
         var mode: DWORD = 0
         if getConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), addr(mode)) != 0:
           mode = mode and not ENABLE_VIRTUAL_TERMINAL_PROCESSING
           discard setConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), mode)
-      trueColorIsEnabled = false
+      term.trueColorIsEnabled = false
   else:
-    trueColorIsEnabled = false
+    term.trueColorIsEnabled = false
 
-when not defined(testing) and isMainModule:
-  #system.addQuitProc(resetAttributes)
-  write(stdout, "never mind")
-  stdout.eraseLine()
-  stdout.styledWriteLine({styleBright, styleBlink, styleUnderscore}, "styled text ")
-  stdout.styledWriteLine("italic text ", {styleItalic})
-  stdout.setBackGroundColor(bgCyan, true)
-  stdout.setForeGroundColor(fgBlue)
-  stdout.write("blue text in cyan background")
-  stdout.resetAttributes()
-  echo ""
-  stdout.writeLine("ordinary text")
-  echo "more ordinary text"
-  styledEcho styleBright, fgGreen, "[PASS]", resetStyle, fgGreen, " Yay!"
-  echo "ordinary text again"
-  styledEcho styleBright, fgRed, "[FAIL]", resetStyle, fgRed, " Nay :("
-  echo "ordinary text again"
-  setForeGroundColor(fgGreen)
-  echo "green text"
-  echo "more green text"
-  setForeGroundColor(fgBlue)
-  echo "blue text"
-  resetAttributes()
-  echo "ordinary text"
-
-  stdout.styledWriteLine(fgRed, "red text ")
-  stdout.styledWriteLine(fgWhite, bgRed, "white text in red background")
-  stdout.styledWriteLine(" ordinary text ")
-  stdout.styledWriteLine(fgGreen, "green text")
-
-  stdout.styledWrite(fgRed, "red text ")
-  stdout.styledWrite(fgWhite, bgRed, "white text in red background")
-  stdout.styledWrite(" ordinary text ")
-  stdout.styledWrite(fgGreen, "green text")
-  echo ""
-  echo "ordinary text"
-  stdout.styledWriteLine(fgRed, "red text ", styleBright, "bold red", fgDefault, " bold text")
-  stdout.styledWriteLine(bgYellow, "text in yellow bg", styleBright, " bold text in yellow bg", bgDefault, " bold text")
-  echo "ordinary text"
+proc newTerminal(): owned(PTerminal) =
+  new result
+  when defined(windows):
+    initTerminal(result)
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 7fd60b818..e59153455 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -1,203 +1,408 @@
 #
 #
 #            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.
 #
 
+##[
+  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 `times` module supports nanosecond time resolution, the
+  resolution used by `getTime()` depends on the platform and backend
+  (JS is limited to millisecond precision).
+
+  Examples
+  ========
+
+    ```nim
+    import std/[times, os]
+    # Simple benchmarking
+    let time = cpuTime()
+    sleep(100) # Replace this with something to be timed
+    echo "Time taken: ", cpuTime() - time
+
+    # 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
+
+    # 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
+  ============================
+
+  The `DateTime` type can be parsed and formatted using the different
+  `parse` and `format` procedures.
+
+    ```nim
+    let dt = parse("2000-01-01", "yyyy-MM-dd")
+    echo dt.format("yyyy-MM-dd")
+    ```
+
+  The different format patterns that are supported are documented below.
+
+  ===========  =================================================================================  ==============================================
+  Pattern      Description                                                                        Example
+  ===========  =================================================================================  ==============================================
+  `d`          Numeric value representing the day of the month,                                   | `1/04/2012 -> 1`
+               it will be either one or two digits long.                                          | `21/04/2012 -> 21`
+  `dd`         Same as above, but is always two digits.                                           | `1/04/2012 -> 01`
+                                                                                                  | `21/04/2012 -> 21`
+  `ddd`        Three letter string which indicates the day of the week.                           | `Saturday -> Sat`
+                                                                                                  | `Monday -> Mon`
+  `dddd`       Full string for the day of the week.                                               | `Saturday -> Saturday`
+                                                                                                  | `Monday -> Monday`
+  `GG`         The last two digits of the Iso Week-Year                                           | `30/12/2012 -> 13`
+  `GGGG`       The Iso week-calendar year padded to four digits                                   | `30/12/2012 -> 2013`
+  `h`          The hours in one digit if possible. Ranging from 1-12.                             | `5pm -> 5`
+                                                                                                  | `2am -> 2`
+  `hh`         The hours in two digits always. If the hour is one digit, 0 is prepended.          | `5pm -> 05`
+                                                                                                  | `11am -> 11`
+  `H`          The hours in one digit if possible, ranging from 0-23.                             | `5pm -> 17`
+                                                                                                  | `2am -> 2`
+  `HH`         The hours in two digits always. 0 is prepended if the hour is one digit.           | `5pm -> 17`
+                                                                                                  | `2am -> 02`
+  `m`          The minutes in one digit if possible.                                              | `5:30 -> 30`
+                                                                                                  | `2:01 -> 1`
+  `mm`         Same as above but always two digits, 0 is prepended if the minute is one digit.    | `5:30 -> 30`
+                                                                                                  | `2:01 -> 01`
+  `M`          The month in one digit if possible.                                                | `September -> 9`
+                                                                                                  | `December -> 12`
+  `MM`         The month in two digits always. 0 is prepended if the month value is one digit.    | `September -> 09`
+                                                                                                  | `December -> 12`
+  `MMM`        Abbreviated three-letter form of the month.                                        | `September -> Sep`
+                                                                                                  | `December -> Dec`
+  `MMMM`       Full month string, properly capitalized.                                           | `September -> September`
+  `s`          Seconds as one digit if possible.                                                  | `00:00:06 -> 6`
+  `ss`         Same as above but always two digits. 0 is prepended if the second is one digit.    | `00:00:06 -> 06`
+  `t`          `A` when time is in the AM. `P` when time is in the PM.                            | `5pm -> P`
+                                                                                                  | `2am -> A`
+  `tt`         Same as above, but `AM` and `PM` instead of `A` and `P` respectively.              | `5pm -> PM`
+                                                                                                  | `2am -> AM`
+  `yy`         The last two digits of the year. When parsing, the current century is assumed.     | `2012 AD -> 12`
+  `yyyy`       The year, padded to at least four digits.                                          | `2012 AD -> 2012`
+               Is always positive, even when the year is BC.                                      | `24 AD -> 0024`
+               When the year is more than four digits, '+' is prepended.                          | `24 BC -> 00024`
+                                                                                                  | `12345 AD -> +12345`
+  `YYYY`       The year without any padding.                                                      | `2012 AD -> 2012`
+               Is always positive, even when the year is BC.                                      | `24 AD -> 24`
+                                                                                                  | `24 BC -> 24`
+                                                                                                  | `12345 AD -> 12345`
+  `uuuu`       The year, padded to at least four digits. Will be negative when the year is BC.    | `2012 AD -> 2012`
+               When the year is more than four digits, '+' is prepended unless the year is BC.    | `24 AD -> 0024`
+                                                                                                  | `24 BC -> -0023`
+                                                                                                  | `12345 AD -> +12345`
+  `UUUU`       The year without any padding. Will be negative when the year is BC.                | `2012 AD -> 2012`
+                                                                                                  | `24 AD -> 24`
+                                                                                                  | `24 BC -> -23`
+                                                                                                  | `12345 AD -> 12345`
+  `V`          The Iso Week-Number as one or two digits                                           | `3/2/2012 -> 5`
+                                                                                                  | `1/4/2012 -> 13`
+  `VV`         The Iso Week-Number as two digits always. 0 is prepended if one digit.             | `3/2/2012 -> 05`
+                                                                                                  | `1/4/2012 -> 13`
+  `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`
+  `ZZZ`        Same as above but with `mm` where *mm* represents minutes.                         | `UTC+7 -> +0700`
+                                                                                                  | `UTC-5 -> -0500`
+  `zzzz`       Same as above but with `:ss` where *ss* represents seconds.                        | `UTC+7 -> +07:00:00`
+                                                                                                  | `UTC-5 -> -05:00:00`
+  `ZZZZ`       Same as above but with `ss` where *ss* represents seconds.                         | `UTC+7 -> +070000`
+                                                                                                  | `UTC-5 -> -050000`
+  `g`          Era: AD or BC                                                                      | `300 AD -> AD`
+                                                                                                  | `300 BC -> BC`
+  `fff`        Milliseconds display                                                               | `1000000 nanoseconds -> 1`
+  `ffffff`     Microseconds display                                                               | `1000000 nanoseconds -> 1000`
+  `fffffffff`  Nanoseconds display                                                                | `1000000 nanoseconds -> 1000000`
+  ===========  =================================================================================  ==============================================
+
+  Other strings can be inserted by putting them in `''`. For example
+  `hh'->'mm` will give `01->56`.  In addition to spaces,
+  the following characters can be inserted without quoting them:
+  `:` `-` `,` `.` `(` `)` `/` `[` `]`.
+  A literal `'` can be specified with `''`.
+
+  However you don't need to necessarily separate format patterns, as an
+  unambiguous format string like `yyyyMMddhhmmss` is also valid (although
+  only for years in the range 1..9999).
+
+  Duration vs TimeInterval
+  ============================
+  The `times` module exports two similar types that are both used to
+  represent some amount of time: `Duration <#Duration>`_ and
+  `TimeInterval <#TimeInterval>`_.
+  This section explains how they differ and when one should be preferred 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 equivalent.
+
+  Arithmetic 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 preferred.
+
+  TimeInterval
+  ----------------------------
+  A `TimeInterval` represents an 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 separate fields for every unit. The
+  `TimeInterval`'s returned from this module generally don't normalize
+  **anything**, so even units that could be normalized (like seconds,
+  milliseconds and so on) are left untouched.
+
+  Arithmetic 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 exactly one day has
+  passed. However, the UTC offsets are different, which means that the
+  UTC offset was changed somewhere in 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.
+
+  See also
+  ========
+  * `monotimes module <monotimes.html>`_
+]##
+
+import std/[strutils, math, options]
+
+import std/private/since
+include "system/inclrtl"
 
-## This module contains routines and types for dealing with time using a proleptic Gregorian calendar.
-## It's is available for the `JavaScript target <backends.html#the-javascript-target>`_.
-##
-## The types uses nanosecond time resolution, but the underlying resolution used by ``getTime()``
-## depends on the platform and backend (JS is limited to millisecond precision).
-##
-## Examples:
-##
-## .. code-block:: nim
-##
-##  import times, os
-##  let time = cpuTime()
-##
-##  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()
-##
-##  echo "cpuTime()  float value: ", cpuTime()
-##  echo "An hour from now      : ", now() + 1.hours
-##  echo "An hour from (UTC) now: ", getTime().utc + initDuration(hours = 1)
-
-{.push debugger:off.} # the user does not want to trace a part
-                      # of the standard library!
-
-import
-  strutils, parseutils, algorithm, math
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
-include "system/inclrtl"
 
-# This is really bad, but overflow checks are broken badly for
-# ints on the JS backend. See #6752.
-when defined(JS):
+when defined(js):
+  import std/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):
-  import posix
+elif defined(posix):
+  import std/posix
 
   type CTime = posix.Time
 
-  var CLOCK_REALTIME {.importc: "CLOCK_REALTIME", header: "<time.h>".}: Clockid
-
-  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 defined(macosx):
+    proc gettimeofday(tp: var Timeval, unused: pointer = nil)
+      {.importc: "gettimeofday", header: "<sys/time.h>", sideEffect.}
 
 elif defined(windows):
-  import winlean
+  import std/winlean, std/time_t
 
-  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
+    CTime = time_t.Time
+    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>", sideEffect.}
 
 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]``.
-    mJan = 1, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec
+  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"
+    mApr = "April"
+    mMay = "May"
+    mJun = "June"
+    mJul = "July"
+    mAug = "August"
+    mSep = "September"
+    mOct = "October"
+    mNov = "November"
+    mDec = "December"
 
   WeekDay* = enum ## Represents a weekday.
-    dMon, dTue, dWed, dThu, dFri, dSat, dSun
+    dMon = "Monday"
+    dTue = "Tuesday"
+    dWed = "Wednesday"
+    dThu = "Thursday"
+    dFri = "Friday"
+    dSat = "Saturday"
+    dSun = "Sunday"
 
+type
   MonthdayRange* = range[1..31]
   HourRange* = range[0..23]
   MinuteRange* = range[0..59]
-  SecondRange* = range[0..60]
+  SecondRange* = range[0..60] ## \
+    ## Includes the value 60 to allow for a leap second. Note however
+    ## that the `second` of a `DateTime` will never be a leap second.
   YeardayRange* = range[0..365]
   NanosecondRange* = range[0..999_999_999]
 
+  IsoWeekRange* = range[1 .. 53]
+    ## An ISO 8601 calendar week number.
+  IsoYear* = distinct int
+    ## An ISO 8601 calendar year number.
+    ##
+    ## .. warning:: The ISO week-based year can correspond to the following or previous year from 29 December to January 3.
+
   Time* = object ## Represents a point in time.
     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.
-    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,
-                              ## normally in the range 0 to 59, but can
-                              ## be up to 60 to allow for a leap second.
-    minute*: MinuteRange      ## The number of minutes after the hour,
-                              ## in the range 0 to 59.
-    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.
-    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.
-
-    nanoseconds*: int  ## The number of nanoseconds
-    microseconds*: int ## The number of microseconds
-    milliseconds*: int ## The number of milliseconds
-    seconds*: int      ## The number of seconds
-    minutes*: int      ## The number of minutes
-    hours*: int        ## The number of hours
-    days*: int         ## The number of days
-    weeks*: int        ## The number of weeks
-    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.
+  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.
+    nanosecond: NanosecondRange
+    second: SecondRange
+    minute: MinuteRange
+    hour: HourRange
+    monthdayZero: int
+    monthZero: int
+    year: int
+    weekday: WeekDay
+    yearday: YeardayRange
+    isDst: bool
+    timezone: Timezone
+    utcOffset: int
+
+  Duration* = object ## Represents a fixed duration of time, meaning a duration
+                     ## that has constant length independent of the context.
+                     ##
+                     ## To create a new `Duration`, use `initDuration
+                     ## <#initDuration,int64,int64,int64,int64,int64,int64,int64,int64>`_.
+                     ## Instead of trying to access the private attributes, use
+                     ## `inSeconds <#inSeconds,Duration>`_ for converting to seconds and
+                     ## `inNanoseconds <#inNanoseconds,Duration>`_ for converting to nanoseconds.
     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* = object ## Timezone interface for supporting ``DateTime``'s of arbritary timezones.
-                     ## The ``times`` module only supplies implementations for the systems local time and UTC.
-                     ## The members ``zoneInfoFromUtc`` and ``zoneInfoFromTz`` should not be accessed directly
-                     ## and are only exported so that ``Timezone`` can be implemented by other modules.
-    zoneInfoFromUtc*: proc (time: Time): ZonedTime {.tags: [], raises: [], benign.}
-    zoneInfoFromTz*:  proc (adjTime: Time): ZonedTime {.tags: [], raises: [], benign.}
-    name*: string ## The name of the timezone, f.ex 'Europe/Stockholm' or 'Etc/UTC'. Used for checking equality.
-                  ## Se also: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
-
-  ZonedTime* = object ## Represents a zoned instant in time that is not associated with any calendar.
-                      ## This type is only used for implementing timezones.
-    adjTime*: Time  ## Time adjusted to a timezone.
-    utcOffset*: int ## Offset from UTC in seconds.
-                    ## The point in time represented by ``ZonedTime`` is ``adjTime + utcOffset.seconds``.
-    isDst*: bool    ## Determines whether DST is in effect.
+    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 <#DateTime>`_ or
+      ## `Time <#Time>`_.
+      ##
+      ## Create a new `TimeInterval` with `initTimeInterval proc
+      ## <#initTimeInterval,int,int,int,int,int,int,int,int,int,int>`_.
+      ##
+      ## 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 <#Duration>`_ should be used instead.
+    nanoseconds*: int    ## The number of nanoseconds
+    microseconds*: int   ## The number of microseconds
+    milliseconds*: int   ## The number of milliseconds
+    seconds*: int        ## The number of seconds
+    minutes*: int        ## The number of minutes
+    hours*: int          ## The number of hours
+    days*: int           ## The number of days
+    weeks*: int          ## The number of weeks
+    months*: int         ## The number of months
+    years*: int          ## The number of years
+
+  Timezone* = ref object ## \
+      ## Timezone interface for supporting `DateTime <#DateTime>`_\s of arbitrary
+      ## timezones. The `times` module only supplies implementations for the
+      ## system's local time and UTC.
+    zonedTimeFromTimeImpl: proc (x: Time): ZonedTime
+        {.tags: [], raises: [], benign.}
+    zonedTimeFromAdjTimeImpl: proc (x: Time): ZonedTime
+        {.tags: [], raises: [], benign.}
+    name: string
+
+  ZonedTime* = object ## Represents a point in time with an associated
+                      ## UTC offset and DST flag. This type is only used for
+                      ## implementing timezones.
+    time*: Time       ## The point in time being represented.
+    utcOffset*: int   ## The offset in seconds west of UTC,
+                      ## including any offset due to DST.
+    isDst*: bool      ## Determines whether DST is in effect.
 
   DurationParts* = array[FixedTimeUnit, int64] # Array of Duration parts starts
   TimeIntervalParts* = array[TimeUnit, int] # Array of Duration parts starts
-  TimesMutableTypes = DateTime | Time | Duration | TimeInterval
-
-{.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time,
-    TTimeInterval: TimeInterval, TTimeInfo: DateTime, TimeInfo: DateTime].}
 
 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).
+                          # The number of hectonanoseconds between 1601/01/01 (windows epoch)
+                          # and 1970/01/01 (unix epoch).
   epochDiff = 116444736000000000'i64
 
 const unitWeights: array[FixedTimeUnit, int64] = [
@@ -211,8 +416,26 @@ const unitWeights: array[FixedTimeUnit, int64] = [
   7 * secondsInDay * 1e9.int64,
 ]
 
-proc convert*[T: SomeInteger](unitFrom, unitTo: FixedTimeUnit, quantity: T): T {.inline.} =
+when (NimMajor, NimMinor) >= (1, 4):
+  # Newer versions of Nim don't track defects
+  {.pragma: parseFormatRaises, raises: [TimeParseError, TimeFormatParseError].}
+  {.pragma: parseRaises, raises: [TimeParseError].}
+else:
+  # Still track when using older versions
+  {.pragma: parseFormatRaises, raises: [TimeParseError, TimeFormatParseError, Defect].}
+  {.pragma: parseRaises, raises: [TimeParseError, Defect].}
+
+
+#
+# Helper procs
+#
+
+{.pragma: operator, rtl, noSideEffect, benign.}
+
+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
@@ -224,8 +447,8 @@ proc convert*[T: SomeInteger](unitFrom, unitTo: FixedTimeUnit, quantity: T): T {
 
 proc normalize[T: Duration|Time](seconds, nanoseconds: int64): T =
   ## Normalize a (seconds, nanoseconds) pair and return it as either
-  ## a ``Duration`` or ``Time``. A normalized ``Duration|Time`` has a
-  ## positive nanosecond part in the range ``NanosecondRange``.
+  ## a `Duration` or `Time`. A normalized `Duration|Time` has a
+  ## positive nanosecond part in the range `NanosecondRange`.
   result.seconds = seconds + convert(Nanoseconds, Seconds, nanoseconds)
   var nanosecond = nanoseconds mod convert(Seconds, Nanoseconds, 1)
   if nanosecond < 0:
@@ -233,129 +456,36 @@ proc normalize[T: Duration|Time](seconds, nanoseconds: int64): T =
     result.seconds -= 1
   result.nanosecond = nanosecond.int
 
-# Forward declarations
-proc utcZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .}
-proc utcZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .}
-proc localZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .}
-proc localZoneInfoFromTz(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.}
-
-proc nanosecond*(time: Time): NanosecondRange =
-  ## Get the fractional part of a ``Time`` as the number
-  ## of nanoseconds of the second.
-  time.nanosecond
-
-
-proc weeks*(dur: Duration): int64 {.inline.} =
-  ## Number of whole weeks represented by the duration.
-  convert(Seconds, Weeks, dur.seconds)
-
-proc days*(dur: Duration): int64 {.inline.} =
-  ## Number of whole days represented by the duration.
-  convert(Seconds, Days, dur.seconds)
-
-proc minutes*(dur: Duration): int64 {.inline.} =
-  ## Number of whole minutes represented by the duration.
-  convert(Seconds, Minutes, dur.seconds)
-
-proc hours*(dur: Duration): int64 {.inline.} =
-  ## Number of whole hours represented by the duration.
-  convert(Seconds, Hours, dur.seconds)
-
-proc seconds*(dur: Duration): int64 {.inline.} =
-  ## Number of whole seconds represented by the duration.
-  dur.seconds
-
-proc milliseconds*(dur: Duration): int {.inline.} =
-  ## Number of whole milliseconds represented by the **fractional**
-  ## part of the duration.
-  runnableExamples:
-    let dur = initDuration(seconds = 1, milliseconds = 1)
-    doAssert dur.milliseconds == 1
-  convert(Nanoseconds, Milliseconds, dur.nanosecond)
-
-proc microseconds*(dur: Duration): int {.inline.} =
-  ## Number of whole microseconds represented by the **fractional**
-  ## part of the duration.
-  runnableExamples:
-    let dur = initDuration(seconds = 1, microseconds = 1)
-    doAssert dur.microseconds == 1
-  convert(Nanoseconds, Microseconds, dur.nanosecond)
-
-proc nanoseconds*(dur: Duration): int {.inline.} =
-  ## Number of whole nanoseconds represented by the **fractional**
-  ## part of the duration.
-  runnableExamples:
-    let dur = initDuration(seconds = 1, nanoseconds = 1)
-    doAssert dur.nanoseconds == 1
-  dur.nanosecond
-
-proc fractional*(dur: Duration): Duration {.inline.} =
-  ## The fractional part of duration, as a duration.
-  runnableExamples:
-    let dur = initDuration(seconds = 1, nanoseconds = 5)
-    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``.
-  runnableExamples:
-    doAssert $fromUnix(0).utc == "1970-01-01T00:00:00+00:00"
-  initTime(unix, 0)
-
-proc toUnix*(t: Time): int64 {.benign, tags: [], raises: [], noSideEffect.} =
-  ## Convert ``t`` to a unix timestamp (seconds since ``1970-01-01T00:00:00Z``).
-  runnableExamples:
-    doAssert fromUnix(0).toUnix() == 0
-
-  t.seconds
-
-proc fromWinTime*(win: int64): Time =
-  ## Convert a Windows file time (100-nanosecond intervals since ``1601-01-01T00:00:00Z``)
-  ## to a ``Time``.
-  let hnsecsSinceEpoch = (win - epochDiff)
-  var seconds = hnsecsSinceEpoch div rateDiff
-  var nanos = ((hnsecsSinceEpoch mod rateDiff) * 100).int
-  if nanos < 0:
-    nanos += convert(Seconds, Nanoseconds, 1)
-    seconds -= 1
-  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``).
-  result = t.seconds * rateDiff + epochDiff + t.nanosecond div 100
-
 proc isLeapYear*(year: int): bool =
-  ## Returns true if ``year`` is a leap year.
+  ## Returns true if `year` is a leap year.
+  runnableExamples:
+    doAssert isLeapYear(2000)
+    doAssert not isLeapYear(1900)
   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
+  runnableExamples:
+    doAssert getDaysInMonth(mFeb, 2000) == 29
+    doAssert getDaysInMonth(mFeb, 2001) == 28
   case month
   of mFeb: result = if isLeapYear(year): 29 else: 28
   of mApr, mJun, mSep, mNov: result = 30
   else: result = 31
 
-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
@@ -366,9 +496,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
@@ -382,32 +514,122 @@ 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(day, month, year).yearday``.
+  ## Equivalent with `dateTime(year, month, monthday, 0, 0, 0, 0).yearday`.
+  runnableExamples:
+    doAssert getDayOfYear(1, mJan, 2000) == 0
+    doAssert getDayOfYear(10, mJan, 2000) == 9
+    doAssert getDayOfYear(10, mFeb, 2000) == 40
+
   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(day, month, year).weekday``.
+  ## Equivalent with `dateTime(year, month, monthday, 0, 0, 0, 0).weekday`.
+  runnableExamples:
+    doAssert getDayOfWeek(13, mJun, 1990) == dWed
+    doAssert $getDayOfWeek(13, mJun, 1990) == "Wednesday"
+
   assertValidDate monthday, month, year
   # 1970-01-01 is a Thursday, we adjust to the previous Monday
-  let days = toEpochday(monthday, month, year) - 3
-  let weeks = (if days >= 0: days else: days - 6) div 7
+  let days = toEpochDay(monthday, month, year) - 3
+  let weeks = floorDiv(days, 7'i64)
   let wd = days - weeks * 7
   # The value of d is 0 for a Sunday, 1 for a Monday, 2 for a Tuesday, etc.
   # so we must correct for the WeekDay type.
   result = if wd == 0: dSun else: WeekDay(wd - 1)
 
+proc getDaysInYear*(year: int): int =
+  ## Get the number of days in a `year`
+  runnableExamples:
+    doAssert getDaysInYear(2000) == 366
+    doAssert getDaysInYear(2001) == 365
+  result = 365 + (if isLeapYear(year): 1 else: 0)
+
+proc `==`*(a, b: IsoYear): bool {.borrow.}
+proc `$`*(p: IsoYear): string {.borrow.}
+
+proc getWeeksInIsoYear*(y: IsoYear): IsoWeekRange {.since: (1, 5).} =
+  ## Returns the number of weeks in the specified ISO 8601 week-based year, which can be
+  ## either 53 or 52.
+  runnableExamples:
+    assert getWeeksInIsoYear(IsoYear(2019)) == 52
+    assert getWeeksInIsoYear(IsoYear(2020)) == 53
+
+  var y = int(y)
 
-{. pragma: operator, rtl, noSideEffect, benign .}
+  # support negative years
+  y = if y < 0: 400 + y mod 400 else: y
+
+  # source: https://webspace.science.uu.nl/~gent0113/calendar/isocalendar.htm
+  let p = (y + (y div 4) - (y div 100) + (y div 400)) mod 7
+  let y1 = y - 1
+  let p1 = (y1 + (y1 div 4) - (y1 div 100) + (y1 div 400)) mod 7
+  if p == 4 or p1 == 3: 53 else: 52
+
+proc getIsoWeekAndYear*(dt: DateTime):
+  tuple[isoweek: IsoWeekRange, isoyear: IsoYear] {.since: (1, 5).} =
+  ## Returns the ISO 8601 week and year.
+  ##
+  ## .. warning:: The ISO week-based year can correspond to the following or previous year from 29 December to January 3.
+  runnableExamples:
+    assert getIsoWeekAndYear(initDateTime(21, mApr, 2018, 00, 00, 00)) == (isoweek: 16.IsoWeekRange, isoyear: 2018.IsoYear)
+    block:
+      let (w, y) = getIsoWeekAndYear(initDateTime(30, mDec, 2019, 00, 00, 00))
+      assert w == 01.IsoWeekRange
+      assert y == 2020.IsoYear
+    assert getIsoWeekAndYear(initDateTime(13, mSep, 2020, 00, 00, 00)) == (isoweek: 37.IsoWeekRange, isoyear: 2020.IsoYear)
+    block:
+      let (w, y) = getIsoWeekAndYear(initDateTime(2, mJan, 2021, 00, 00, 00))
+      assert w.int > 52
+      assert w.int < 54
+      assert y.int mod 100 == 20
+
+  # source: https://webspace.science.uu.nl/~gent0113/calendar/isocalendar.htm
+  var w = (dt.yearday.int - dt.weekday.int + 10) div 7
+  if w < 1:
+    (isoweek: getWeeksInIsoYear(IsoYear(dt.year - 1)), isoyear: IsoYear(dt.year - 1))
+  elif (w > getWeeksInIsoYear(IsoYear(dt.year))):
+    (isoweek: IsoWeekRange(1), isoyear: IsoYear(dt.year + 1))
+  else:
+    (isoweek: IsoWeekRange(w), isoyear: IsoYear(dt.year))
+
+proc stringifyUnit(value: int | int64, unit: TimeUnit): string =
+  ## Stringify time unit with it's name, lowercased
+  let strUnit = $unit
+  result = ""
+  result.addInt value
+  result.add ' '
+  if abs(value) != 1:
+    result.add(strUnit.toLowerAscii())
+  else:
+    result.add(strUnit[0..^2].toLowerAscii())
+
+proc humanizeParts(parts: seq[string]): string =
+  ## Make date string parts human-readable
+  result = ""
+  if parts.len == 0:
+    result.add "0 nanoseconds"
+  elif parts.len == 1:
+    result = parts[0]
+  elif parts.len == 2:
+    result = parts[0] & " and " & parts[1]
+  else:
+    for i in 0..high(parts)-1:
+      result.add parts[i] & ", "
+    result.add "and " & parts[high(parts)]
 
 template subImpl[T: Duration|Time](a: Duration|Time, b: Duration|Time): T =
   normalize[T](a.seconds - b.seconds, a.nanosecond - b.nanosecond)
@@ -426,12 +648,24 @@ 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
 
+#
+# Duration
+#
+
+const DurationZero* = Duration() ## \
+  ## Zero value for durations. Useful for comparisons.
+  ##   ```nim
+  ##   doAssert initDuration(seconds = 1) > DurationZero
+  ##   doAssert initDuration(seconds = 0) == DurationZero
+  ##   ```
+
 proc initDuration*(nanoseconds, microseconds, milliseconds,
                    seconds, minutes, hours, days, weeks: int64 = 0): Duration =
+  ## Create a new `Duration <#Duration>`_.
   runnableExamples:
     let dur = initDuration(seconds = 1, milliseconds = 1)
-    doAssert dur.milliseconds == 1
-    doAssert dur.seconds == 1
+    doAssert dur.inMilliseconds == 1001
+    doAssert dur.inSeconds == 1
 
   let seconds = convert(Weeks, Seconds, weeks) +
     convert(Days, Seconds, days) +
@@ -447,25 +681,93 @@ proc initDuration*(nanoseconds, microseconds, milliseconds,
   # Nanoseconds might be negative so we must normalize.
   result = normalize[Duration](seconds, nanoseconds)
 
-const DurationZero* = initDuration() ## \
-  ## Zero value for durations. Useful for comparisons.
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   doAssert initDuration(seconds = 1) > DurationZero
-  ##   doAssert initDuration(seconds = 0) == DurationZero
+template convert(dur: Duration, unit: static[FixedTimeUnit]): int64 =
+  # The correction is required due to how durations are normalized.
+  # For example,` initDuration(nanoseconds = -1)` is stored as
+  # { seconds = -1, nanoseconds = 999999999 }.
+  when unit == Nanoseconds:
+    dur.seconds * 1_000_000_000 + dur.nanosecond
+  else:
+    let correction = dur.seconds < 0 and dur.nanosecond > 0
+    when unit >= Seconds:
+      convert(Seconds, unit, dur.seconds + ord(correction))
+    else:
+      if correction:
+        convert(Seconds, unit, dur.seconds + 1) -
+          convert(Nanoseconds, unit,
+            convert(Seconds, Nanoseconds, 1) - dur.nanosecond)
+      else:
+        convert(Seconds, unit, dur.seconds) +
+          convert(Nanoseconds, unit, dur.nanosecond)
+
+proc inWeeks*(dur: Duration): int64 =
+  ## Converts the duration to the number of whole weeks.
+  runnableExamples:
+    let dur = initDuration(days = 8)
+    doAssert dur.inWeeks == 1
+  dur.convert(Weeks)
+
+proc inDays*(dur: Duration): int64 =
+  ## Converts the duration to the number of whole days.
+  runnableExamples:
+    let dur = initDuration(hours = -50)
+    doAssert dur.inDays == -2
+  dur.convert(Days)
+
+proc inHours*(dur: Duration): int64 =
+  ## Converts the duration to the number of whole hours.
+  runnableExamples:
+    let dur = initDuration(minutes = 60, days = 2)
+    doAssert dur.inHours == 49
+  dur.convert(Hours)
+
+proc inMinutes*(dur: Duration): int64 =
+  ## Converts the duration to the number of whole minutes.
+  runnableExamples:
+    let dur = initDuration(hours = 2, seconds = 10)
+    doAssert dur.inMinutes == 120
+  dur.convert(Minutes)
+
+proc inSeconds*(dur: Duration): int64 =
+  ## Converts the duration to the number of whole seconds.
+  runnableExamples:
+    let dur = initDuration(hours = 2, milliseconds = 10)
+    doAssert dur.inSeconds == 2 * 60 * 60
+  dur.convert(Seconds)
+
+proc inMilliseconds*(dur: Duration): int64 =
+  ## Converts the duration to the number of whole milliseconds.
+  runnableExamples:
+    let dur = initDuration(seconds = -2)
+    doAssert dur.inMilliseconds == -2000
+  dur.convert(Milliseconds)
+
+proc inMicroseconds*(dur: Duration): int64 =
+  ## Converts the duration to the number of whole microseconds.
+  runnableExamples:
+    let dur = initDuration(seconds = -2)
+    doAssert dur.inMicroseconds == -2000000
+  dur.convert(Microseconds)
+
+proc inNanoseconds*(dur: Duration): int64 =
+  ## Converts the duration to the number of whole nanoseconds.
+  runnableExamples:
+    let dur = initDuration(seconds = -2)
+    doAssert dur.inNanoseconds == -2000000000
+  dur.convert(Nanoseconds)
 
 proc toParts*(dur: Duration): DurationParts =
   ## Converts a duration into an array consisting of fixed time units.
   ##
   ## Each value in the array gives information about a specific unit of
-  ## time, for example ``result[Days]`` gives a count of days.
+  ## time, for example `result[Days]` gives a count of days.
   ##
-  ## This procedure is useful for converting ``Duration`` values to strings.
+  ## 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
+    doAssert dp[Minutes] == 0
     dp = toParts(initDuration(days = -1))
     doAssert dp[Days] == -1
 
@@ -489,178 +791,383 @@ proc toParts*(dur: Duration): DurationParts =
 
     result[unit] = quantity
 
-proc stringifyUnit*(value: int | int64, unit: string): string =
-  ## Stringify time unit with it's name, lowercased
-  runnableExamples:
-    doAssert stringifyUnit(2, "Seconds") == "2 seconds"
-    doAssert stringifyUnit(1, "Years") == "1 year"
-  result = ""
-  result.add($value)
-  result.add(" ")
-  if abs(value) != 1:
-    result.add(unit.toLowerAscii())
-  else:
-    result.add(unit[0..^2].toLowerAscii())
-
-proc humanizeParts(parts: seq[string]): string =
-  ## Make date string parts human-readable
-
-  result = ""
-  if parts.len == 0:
-    result.add "0 nanoseconds"
-  elif parts.len == 1:
-    result = parts[0]
-  elif parts.len == 2:
-    result = parts[0] & " and " & parts[1]
-  else:
-    for part in parts[0..high(parts)-1]:
-      result.add part & ", "
-    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)
 
   for unit in countdown(Weeks, Nanoseconds):
     let quantity = numParts[unit]
     if quantity != 0.int64:
-      parts.add(stringifyUnit(quantity, $unit))
+      parts.add(stringifyUnit(quantity, unit))
 
   result = humanizeParts(parts)
 
-proc `+`*(a, b: Duration): Duration {.operator.} =
+proc `+`*(a, b: Duration): Duration {.operator, extern: "ntAddDuration".} =
   ## Add two durations together.
   runnableExamples:
     doAssert initDuration(seconds = 1) + initDuration(days = 1) ==
       initDuration(seconds = 1, days = 1)
   addImpl[Duration](a, b)
 
-proc `-`*(a, b: Duration): Duration {.operator.} =
+proc `-`*(a, b: Duration): Duration {.operator, extern: "ntSubDuration".} =
   ## Subtract a duration from another.
   runnableExamples:
     doAssert initDuration(seconds = 1, days = 1) - initDuration(seconds = 1) ==
       initDuration(days = 1)
   subImpl[Duration](a, b)
 
-proc `-`*(a: Duration): Duration {.operator.} =
+proc `-`*(a: Duration): Duration {.operator, extern: "ntReverseDuration".} =
   ## Reverse a duration.
   runnableExamples:
     doAssert -initDuration(seconds = 1) == initDuration(seconds = -1)
   normalize[Duration](-a.seconds, -a.nanosecond)
 
-proc `<`*(a, b: Duration): bool {.operator.} =
+proc `<`*(a, b: Duration): bool {.operator, extern: "ntLtDuration".} =
   ## Note that a duration can be negative,
-  ## so even if ``a < b`` is true ``a`` might
+  ## so even if `a < b` is true `a` might
   ## represent a larger absolute duration.
-  ## Use ``abs(a) < abs(b)`` to compare the absolute
+  ## 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)
+    doAssert initDuration(seconds = -2).abs < initDuration(seconds = 1).abs == false
   ltImpl(a, b)
 
-proc `<=`*(a, b: Duration): bool {.operator.} =
+proc `<=`*(a, b: Duration): bool {.operator, extern: "ntLeDuration".} =
   lqImpl(a, b)
 
-proc `==`*(a, b: Duration): bool {.operator.} =
+proc `==`*(a, b: Duration): bool {.operator, extern: "ntEqDuration".} =
+  runnableExamples:
+    let
+      d1 = initDuration(weeks = 1)
+      d2 = initDuration(days = 7)
+    doAssert d1 == d2
   eqImpl(a, b)
 
-proc `*`*(a: int64, b: Duration): Duration {.operator} =
+proc `*`*(a: int64, b: Duration): Duration {.operator,
+    extern: "ntMulInt64Duration".} =
   ## Multiply a duration by some scalar.
   runnableExamples:
     doAssert 5 * initDuration(seconds = 1) == initDuration(seconds = 5)
+    doAssert 3 * initDuration(minutes = 45) == initDuration(hours = 2, minutes = 15)
   normalize[Duration](a * b.seconds, a * b.nanosecond)
 
-proc `*`*(a: Duration, b: int64): Duration {.operator} =
+proc `*`*(a: Duration, b: int64): Duration {.operator,
+    extern: "ntMulDuration".} =
   ## Multiply a duration by some scalar.
   runnableExamples:
     doAssert initDuration(seconds = 1) * 5 == initDuration(seconds = 5)
+    doAssert initDuration(minutes = 45) * 3 == initDuration(hours = 2, minutes = 15)
   b * a
 
-proc `div`*(a: Duration, b: int64): Duration {.operator} =
+proc `+=`*(d1: var Duration, d2: Duration) =
+  d1 = d1 + d2
+
+proc `-=`*(dt: var Duration, ti: Duration) =
+  dt = dt - ti
+
+proc `*=`*(a: var Duration, b: int) =
+  a = a * b
+
+proc `div`*(a: Duration, b: int64): Duration {.operator,
+    extern: "ntDivDuration".} =
   ## 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(minutes = 45) div 30 ==
+      initDuration(minutes = 1, seconds = 30)
+    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)
 
+proc high*(typ: typedesc[Duration]): Duration =
+  ## Get the longest representable duration.
+  initDuration(seconds = high(int64), nanoseconds = high(NanosecondRange))
+
+proc low*(typ: typedesc[Duration]): Duration =
+  ## Get the longest representable duration of negative direction.
+  initDuration(seconds = low(int64))
+
+proc abs*(a: Duration): Duration =
+  runnableExamples:
+    doAssert initDuration(milliseconds = -1500).abs ==
+      initDuration(milliseconds = 1500)
+  initDuration(seconds = abs(a.seconds), nanoseconds = -a.nanosecond)
+
+#
+# Time
+#
+
 proc initTime*(unix: int64, nanosecond: NanosecondRange): Time =
-  ## Create a ``Time`` from a unix timestamp and a nanosecond part.
+  ## Create a `Time <#Time>`_ from a unix timestamp and a nanosecond part.
   result.seconds = unix
   result.nanosecond = nanosecond
 
+proc nanosecond*(time: Time): NanosecondRange =
+  ## Get the fractional part of a `Time` as the number
+  ## of nanoseconds of the second.
+  time.nanosecond
+
+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)
+
+proc toUnix*(t: Time): int64 {.benign, tags: [], raises: [], noSideEffect.} =
+  ## Convert `t` to a unix timestamp (seconds since `1970-01-01T00:00:00Z`).
+  ## See also `toUnixFloat` for subsecond resolution.
+  runnableExamples:
+    doAssert fromUnix(0).toUnix() == 0
+  t.seconds
+
+proc fromUnixFloat(seconds: float): Time {.benign, tags: [], raises: [], noSideEffect.} =
+  ## Convert a unix timestamp in seconds to a `Time`; same as `fromUnix`
+  ## but with subsecond resolution.
+  runnableExamples:
+    doAssert fromUnixFloat(123456.0) == fromUnixFloat(123456)
+    doAssert fromUnixFloat(-123456.0) == fromUnixFloat(-123456)
+  let secs = seconds.floor
+  let nsecs = (seconds - secs) * 1e9
+  initTime(secs.int64, nsecs.NanosecondRange)
+
+proc toUnixFloat(t: Time): float {.benign, tags: [], raises: [].} =
+  ## Same as `toUnix` but using subsecond resolution.
+  runnableExamples:
+    let t = getTime()
+    # `<` because of rounding errors
+    doAssert abs(t.toUnixFloat().fromUnixFloat - t) < initDuration(nanoseconds = 1000)
+  t.seconds.float + t.nanosecond / convert(Seconds, Nanoseconds, 1)
+
+since((1, 1)):
+  export fromUnixFloat
+  export toUnixFloat
+
+
+proc fromWinTime*(win: int64): 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`).
+  result = t.seconds * rateDiff + epochDiff + t.nanosecond div 100
+
+proc getTimeImpl(typ: typedesc[Time]): Time =
+  discard "implemented in the vm"
+
+proc getTime*(): Time {.tags: [TimeEffect], benign.} =
+  ## Gets the current time as a `Time` with up to nanosecond resolution.
+  when nimvm:
+    result = getTimeImpl(Time)
+  else:
+    when defined(js):
+      let millis = newDate().getTime()
+      let seconds = convert(Milliseconds, Seconds, millis)
+      let nanos = convert(Milliseconds, Nanoseconds,
+        millis mod convert(Seconds, Milliseconds, 1).int)
+      result = initTime(seconds, nanos)
+    elif defined(macosx):
+      var a {.noinit.}: Timeval
+      gettimeofday(a)
+      result = initTime(a.tv_sec.int64,
+                        convert(Microseconds, Nanoseconds, a.tv_usec.int))
+    elif defined(posix):
+      var ts {.noinit.}: Timespec
+      discard clock_gettime(CLOCK_REALTIME, ts)
+      result = initTime(ts.tv_sec.int64, ts.tv_nsec.int)
+    elif defined(windows):
+      var f {.noinit.}: FILETIME
+      getSystemTimeAsFileTime(f)
+      result = fromWinTime(rdFileTime(f))
+
 proc `-`*(a, b: Time): Duration {.operator, extern: "ntDiffTime".} =
   ## Computes the duration between two points in time.
+  runnableExamples:
+    doAssert initTime(1000, 100) - initTime(500, 20) ==
+      initDuration(minutes = 8, seconds = 20, nanoseconds = 80)
   subImpl[Duration](a, b)
 
 proc `+`*(a: Time, b: Duration): Time {.operator, extern: "ntAddTime".} =
-  ## Add a duration of time to a ``Time``.
+  ## Add a duration of time to a `Time`.
   runnableExamples:
     doAssert (fromUnix(0) + initDuration(seconds = 1)) == fromUnix(1)
   addImpl[Time](a, b)
 
 proc `-`*(a: Time, b: Duration): Time {.operator, extern: "ntSubTime".} =
-  ## Subtracts a duration of time from a ``Time``.
+  ## Subtracts a duration of time from a `Time`.
   runnableExamples:
     doAssert (fromUnix(0) - initDuration(seconds = 1)) == fromUnix(-1)
   subImpl[Time](a, b)
 
 proc `<`*(a, b: Time): bool {.operator, extern: "ntLtTime".} =
-  ## Returns true iff ``a < b``, that is iff a happened before b.
+  ## Returns true if `a < b`, that is if `a` happened before `b`.
+  runnableExamples:
+    doAssert initTime(50, 0) < initTime(99, 0)
   ltImpl(a, b)
 
-proc `<=` * (a, b: Time): bool {.operator, extern: "ntLeTime".} =
-  ## Returns true iff ``a <= b``.
+proc `<=`*(a, b: Time): bool {.operator, extern: "ntLeTime".} =
+  ## Returns true if `a <= b`.
   lqImpl(a, b)
 
 proc `==`*(a, b: Time): bool {.operator, extern: "ntEqTime".} =
-  ## Returns true if ``a == b``, that is if both times represent the same point in time.
+  ## Returns true if `a == b`, that is if both times represent the same point in time.
   eqImpl(a, b)
 
+proc `+=`*(t: var Time, b: Duration) =
+  t = t + b
+
+proc `-=`*(t: var Time, b: Duration) =
+  t = t - b
+
 proc high*(typ: typedesc[Time]): Time =
   initTime(high(int64), high(NanosecondRange))
 
 proc low*(typ: typedesc[Time]): Time =
-  initTime(low(int64), 0)
+  initTime(0, 0)
 
-proc high*(typ: typedesc[Duration]): Duration =
-  ## Get the longest representable duration.
-  initDuration(seconds = high(int64), nanoseconds = high(NanosecondRange))
+#
+# DateTime & Timezone
+#
 
-proc low*(typ: typedesc[Duration]): Duration =
-  ## Get the longest representable duration of negative direction.
-  initDuration(seconds = low(int64))
+template assertDateTimeInitialized(dt: DateTime) =
+  assert dt.monthdayZero != 0, "Uninitialized datetime"
+
+proc nanosecond*(dt: DateTime): NanosecondRange {.inline.} =
+  ## The number of nanoseconds after the second,
+  ## in the range 0 to 999_999_999.
+  assertDateTimeInitialized(dt)
+  dt.nanosecond
+
+proc second*(dt: DateTime): SecondRange {.inline.} =
+  ## The number of seconds after the minute,
+  ## in the range 0 to 59.
+  assertDateTimeInitialized(dt)
+  dt.second
+
+proc minute*(dt: DateTime): MinuteRange {.inline.} =
+  ## The number of minutes after the hour,
+  ## in the range 0 to 59.
+  assertDateTimeInitialized(dt)
+  dt.minute
+
+proc hour*(dt: DateTime): HourRange {.inline.} =
+  ## The number of hours past midnight,
+  ## in the range 0 to 23.
+  assertDateTimeInitialized(dt)
+  dt.hour
+
+proc monthday*(dt: DateTime): MonthdayRange {.inline.} =
+  ## The day of the month, in the range 1 to 31.
+  assertDateTimeInitialized(dt)
+  # 'cast' to avoid extra range check
+  cast[MonthdayRange](dt.monthdayZero)
+
+proc month*(dt: DateTime): Month =
+  ## The month as an enum, the ordinal value
+  ## is in the range 1 to 12.
+  assertDateTimeInitialized(dt)
+  # 'cast' to avoid extra range check
+  cast[Month](dt.monthZero)
+
+proc year*(dt: DateTime): int {.inline.} =
+  ## The year, using astronomical year numbering
+  ## (meaning that before year 1 is year 0,
+  ## then year -1 and so on).
+  assertDateTimeInitialized(dt)
+  dt.year
+
+proc weekday*(dt: DateTime): WeekDay {.inline.} =
+  ## The day of the week as an enum, the ordinal
+  ## value is in the range 0 (monday) to 6 (sunday).
+  assertDateTimeInitialized(dt)
+  dt.weekday
+
+proc yearday*(dt: DateTime): YeardayRange {.inline.} =
+  ## The number of days since January 1,
+  ## in the range 0 to 365.
+  assertDateTimeInitialized(dt)
+  dt.yearday
+
+proc isDst*(dt: DateTime): bool {.inline.} =
+  ## Determines whether DST is in effect.
+  ## Always false for the JavaScript backend.
+  assertDateTimeInitialized(dt)
+  dt.isDst
+
+proc timezone*(dt: DateTime): Timezone {.inline.} =
+  ## The timezone represented as an implementation
+  ## of `Timezone`.
+  assertDateTimeInitialized(dt)
+  dt.timezone
+
+proc utcOffset*(dt: DateTime): int {.inline.} =
+  ## 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`).
+  assertDateTimeInitialized(dt)
+  dt.utcOffset
+
+proc isInitialized(dt: DateTime): bool =
+  # Returns true if `dt` is not the (invalid) default value for `DateTime`.
+  runnableExamples:
+    doAssert now().isInitialized
+    doAssert not default(DateTime).isInitialized
+  dt.monthZero != 0
 
-proc abs*(a: Duration): Duration =
+since((1, 3)):
+  export isInitialized
+
+proc isLeapDay*(dt: DateTime): bool {.since: (1, 1).} =
+  ## Returns whether `t` is a leap day, i.e. Feb 29 in a leap year. This matters
+  ## as it affects time offset calculations.
   runnableExamples:
-    doAssert initDuration(milliseconds = -1500).abs ==
-      initDuration(milliseconds = 1500)
-  initDuration(seconds = abs(a.seconds), nanoseconds = -a.nanosecond)
+    let dt = dateTime(2020, mFeb, 29, 00, 00, 00, 00, utc())
+    doAssert dt.isLeapDay
+    doAssert dt+1.years-1.years != dt
+    let dt2 = dateTime(2020, mFeb, 28, 00, 00, 00, 00, utc())
+    doAssert not dt2.isLeapDay
+    doAssert dt2+1.years-1.years == dt2
+    doAssertRaises(Exception): discard dateTime(2021, mFeb, 29, 00, 00, 00, 00, utc())
+  assertDateTimeInitialized dt
+  dt.year.isLeapYear and dt.month == mFeb and dt.monthday == 29
 
 proc toTime*(dt: DateTime): Time {.tags: [], raises: [], benign.} =
-  ## Converts a broken-down time structure to
-  ## calendar time representation.
-  let epochDay = toEpochday(dt.monthday, dt.month, dt.year)
+  ## Converts a `DateTime` to a `Time` representing the same point in time.
+  assertDateTimeInitialized dt
+  let epochDay = toEpochDay(dt.monthday, dt.month, dt.year)
   var seconds = epochDay * secondsInDay
   seconds.inc dt.hour * secondsInHour
   seconds.inc dt.minute * 60
   seconds.inc dt.second
-  # The code above ignores the UTC offset of `timeInfo`,
-  # so we need to compensate for that here.
   seconds.inc dt.utcOffset
   result = initTime(seconds, dt.nanosecond)
 
 proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime =
-  ## Create a new ``DateTime`` using ``ZonedTime`` in the specified timezone.
-  let s = zt.adjTime.seconds
-  let epochday = (if s >= 0: s else: s - (secondsInDay - 1)) div secondsInDay
+  ## Create a new `DateTime` using `ZonedTime` in the specified timezone.
+  let adjTime = zt.time - initDuration(seconds = zt.utcOffset)
+  let s = adjTime.seconds
+  let epochday = floorDiv(s, secondsInDay)
   var rem = s - epochday * secondsInDay
   let hour = rem div secondsInHour
   rem = rem - hour * secondsInHour
@@ -668,16 +1175,16 @@ proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime =
   rem = rem - minute * secondsInMin
   let second = rem
 
-  let (d, m, y) = fromEpochday(epochday)
+  let (d, m, y) = fromEpochDay(epochday)
 
   DateTime(
     year: y,
-    month: m,
-    monthday: d,
+    monthZero: m.int,
+    monthdayZero: d,
     hour: hour,
     minute: minute,
     second: second,
-    nanosecond: zt.adjTime.nanosecond,
+    nanosecond: zt.time.nanosecond,
     weekday: getDayOfWeek(d, m, y),
     yearday: getDayOfYear(d, m, y),
     isDst: zt.isDst,
@@ -685,136 +1192,155 @@ proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime =
     utcOffset: zt.utcOffset
   )
 
-proc inZone*(time: Time, zone: Timezone): DateTime {.tags: [], raises: [], benign.} =
-  ## Break down ``time`` into a ``DateTime`` using ``zone`` as the timezone.
-  let zoneInfo = zone.zoneInfoFromUtc(time)
-  result = initDateTime(zoneInfo, zone)
+proc newTimezone*(
+      name: string,
+      zonedTimeFromTimeImpl: proc (time: Time): ZonedTime
+          {.tags: [], raises: [], benign.},
+      zonedTimeFromAdjTimeImpl: proc (adjTime: Time): ZonedTime
+          {.tags: [], raises: [], benign.}
+    ): owned Timezone =
+  ## Create a new `Timezone`.
+  ##
+  ## `zonedTimeFromTimeImpl` and `zonedTimeFromAdjTimeImpl` is used
+  ## as the underlying implementations for `zonedTimeFromTime` and
+  ## `zonedTimeFromAdjTime`.
+  ##
+  ## If possible, the name parameter should match the name used in the
+  ## tz database. If the timezone doesn't exist in the tz database, or if the
+  ## timezone name is unknown, then any string that describes the timezone
+  ## unambiguously can be used. Note that the timezones name is used for
+  ## checking equality!
+  runnableExamples:
+    proc utcTzInfo(time: Time): ZonedTime =
+      ZonedTime(utcOffset: 0, isDst: false, time: time)
+    let utc = newTimezone("Etc/UTC", utcTzInfo, utcTzInfo)
+  Timezone(
+    name: name,
+    zonedTimeFromTimeImpl: zonedTimeFromTimeImpl,
+    zonedTimeFromAdjTimeImpl: zonedTimeFromAdjTimeImpl
+  )
+
+proc name*(zone: Timezone): string =
+  ## The name of the timezone.
+  ##
+  ## If possible, the name will be the name used in the tz database.
+  ## If the timezone doesn't exist in the tz database, or if the timezone
+  ## name is unknown, then any string that describes the timezone
+  ## unambiguously might be used. For example, the string "LOCAL" is used
+  ## for the system's local timezone.
+  ##
+  ## See also: https://en.wikipedia.org/wiki/Tz_database
+  zone.name
+
+proc zonedTimeFromTime*(zone: Timezone, time: Time): ZonedTime =
+  ## Returns the `ZonedTime` for some point in time.
+  zone.zonedTimeFromTimeImpl(time)
 
-proc inZone*(dt: DateTime, zone: Timezone): DateTime  {.tags: [], raises: [], benign.} =
-  ## Convert ``dt`` into a ``DateTime`` using ``zone`` as the timezone.
-  dt.toTime.inZone(zone)
+proc zonedTimeFromAdjTime*(zone: Timezone, adjTime: Time): ZonedTime =
+  ## Returns the `ZonedTime` for some local time.
+  ##
+  ## Note that the `Time` argument does not represent a point in time, it
+  ## represent a local time! E.g if `adjTime` is `fromUnix(0)`, it should be
+  ## interpreted as 1970-01-01T00:00:00 in the `zone` timezone, not in UTC.
+  zone.zonedTimeFromAdjTimeImpl(adjTime)
 
 proc `$`*(zone: Timezone): string =
   ## Returns the name of the timezone.
-  zone.name
+  if zone != nil: result = zone.name
 
 proc `==`*(zone1, zone2: Timezone): bool =
-  ## Two ``Timezone``'s are considered equal if their name is equal.
+  ## Two `Timezone`'s are considered equal if their name is equal.
+  runnableExamples:
+    doAssert local() == local()
+    doAssert local() != utc()
+  if system.`==`(zone1, zone2):
+    return true
+  if zone1.isNil or zone2.isNil:
+    return false
   zone1.name == zone2.name
 
+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.} =
+  ## Returns a `DateTime` representing the same point in time as `dt` but
+  ## using `zone` as the timezone.
+  assertDateTimeInitialized dt
+  dt.toTime.inZone(zone)
+
 proc toAdjTime(dt: DateTime): Time =
-  let epochDay = toEpochday(dt.monthday, dt.month, dt.year)
+  let epochDay = toEpochDay(dt.monthday, dt.month, dt.year)
   var seconds = epochDay * secondsInDay
   seconds.inc dt.hour * secondsInHour
   seconds.inc dt.minute * secondsInMin
   seconds.inc dt.second
   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 localZoneInfoFromUtc(time: Time): ZonedTime =
-      let jsDate = newDate(time.seconds.float * 1000)
-      let offset = jsDate.getTimezoneOffset() * secondsInMin
-      result.adjTime = time - initDuration(seconds = offset)
-      result.utcOffset = offset
-      result.isDst = false
-
-    proc localZoneInfoFromTz(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.adjTime = adjTime
-      result.utcOffset = localDate.getTimezoneOffset() * secondsInMin
-      result.isDst = false
+when defined(js):
+  proc localZonedTimeFromTime(time: Time): ZonedTime {.benign.} =
+    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):
-          gmtoff {.importc: "tm_gmtoff".}: clong
-          zone {.importc: "tm_zone".}: cstring
-  type
-    StructTmPtr = ptr StructTm
+  proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime {.benign.} =
+    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] =
-    var a = unix.CTime
-    let tmPtr = localtime(addr(a))
+    # Windows can't handle unix < 0, so we fall back to unix = 0.
+    # FIXME: This should be improved by falling back to the WinAPI instead.
+    when defined(windows):
+      if unix < 0:
+        var a = 0.CTime
+        let tmPtr = localtime(a)
+        if not tmPtr.isNil:
+          let tm = tmPtr[]
+          return ((0 - tm.toAdjUnix).int, false)
+        return (0, false)
+
+    # In case of a 32-bit time_t, we fallback to the closest available
+    # timezone information.
+    var a = clamp(unix, low(CTime).int64, high(CTime).int64).CTime
+    let tmPtr = localtime(a)
     if not tmPtr.isNil:
       let tm = tmPtr[]
-      return ((unix - tm.toAdjUnix).int, tm.isdst > 0)
+      return ((a.int64 - tm.toAdjUnix).int, tm.tm_isdst > 0)
     return (0, false)
 
-  proc localZoneInfoFromUtc(time: Time): ZonedTime =
+  proc localZonedTimeFromTime(time: Time): ZonedTime {.benign.} =
     let (offset, dst) = getLocalOffsetAndDst(time.seconds)
-    result.adjTime = time - initDuration(seconds = offset)
+    result.time = time
     result.utcOffset = offset
     result.isDst = dst
 
-  proc localZoneInfoFromTz(adjTime: Time): ZonedTime  =
+  proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime {.benign.} =
     var adjUnix = adjTime.seconds
     let past = adjUnix - secondsInDay
     let (pastOffset, _) = getLocalOffsetAndDst(past)
@@ -824,7 +1350,7 @@ else:
 
     var utcOffset: int
     if pastOffset == futureOffset:
-        utcOffset = pastOffset.int
+      utcOffset = pastOffset.int
     else:
       if pastOffset > futureOffset:
         adjUnix -= secondsInHour
@@ -836,88 +1362,1010 @@ else:
     # as a result of offset changes (normally due to dst)
     let utcUnix = adjTime.seconds + utcOffset
     let (finalOffset, dst) = getLocalOffsetAndDst(utcUnix)
-    result.adjTime = initTime(utcUnix - finalOffset, adjTime.nanosecond)
+    result.time = initTime(utcUnix, adjTime.nanosecond)
     result.utcOffset = finalOffset
     result.isDst = dst
 
-proc utcZoneInfoFromUtc(time: Time): ZonedTime =
-  result.adjTime = time
-  result.utcOffset = 0
-  result.isDst = false
+proc utcTzInfo(time: Time): ZonedTime =
+  ZonedTime(utcOffset: 0, isDst: false, time: time)
 
-proc utcZoneInfoFromTz(adjTime: Time): ZonedTime =
-  utcZoneInfoFromUtc(adjTime) # adjTime == time since we are in UTC
+var utcInstance {.threadvar.}: Timezone
+var localInstance {.threadvar.}: Timezone
 
-proc utc*(): TimeZone =
-  ## Get the ``Timezone`` implementation for the UTC timezone.
+proc utc*(): Timezone =
+  ## Get the `Timezone` implementation for the UTC timezone.
   runnableExamples:
     doAssert now().utc.timezone == utc()
     doAssert utc().name == "Etc/UTC"
-  Timezone(zoneInfoFromUtc: utcZoneInfoFromUtc, zoneInfoFromTz: utcZoneInfoFromTz, name: "Etc/UTC")
+  if utcInstance.isNil:
+    utcInstance = newTimezone("Etc/UTC", utcTzInfo, utcTzInfo)
+  result = utcInstance
 
-proc local*(): TimeZone =
-  ## Get the ``Timezone`` implementation for the local timezone.
+proc local*(): Timezone =
+  ## Get the `Timezone` implementation for the local timezone.
   runnableExamples:
-   doAssert now().timezone == local()
-   doAssert local().name == "LOCAL"
-  Timezone(zoneInfoFromUtc: localZoneInfoFromUtc, zoneInfoFromTz: localZoneInfoFromTz, name: "LOCAL")
+    doAssert now().timezone == local()
+    doAssert local().name == "LOCAL"
+  if localInstance.isNil:
+    localInstance = newTimezone("LOCAL", localZonedTimeFromTime,
+      localZonedTimeFromAdjTime)
+  result = localInstance
 
 proc utc*(dt: DateTime): DateTime =
-  ## Shorthand for ``dt.inZone(utc())``.
+  ## Shorthand for `dt.inZone(utc())`.
   dt.inZone(utc())
 
 proc local*(dt: DateTime): DateTime =
-  ## Shorthand for ``dt.inZone(local())``.
+  ## Shorthand for `dt.inZone(local())`.
   dt.inZone(local())
 
 proc utc*(t: Time): DateTime =
-  ## Shorthand for ``t.inZone(utc())``.
+  ## Shorthand for `t.inZone(utc())`.
   t.inZone(utc())
 
 proc local*(t: Time): DateTime =
-  ## Shorthand for ``t.inZone(local())``.
+  ## Shorthand for `t.inZone(local())`.
   t.inZone(local())
 
-proc getTime*(): Time {.tags: [TimeEffect], benign.} =
-  ## Gets the current time as a ``Time`` with nanosecond resolution.
-  when defined(JS):
-    let millis = newDate().getTime()
-    let seconds = convert(Milliseconds, Seconds, millis)
-    let nanos = convert(Milliseconds, Nanoseconds,
-      millis mod convert(Seconds, Milliseconds, 1).int)
-    result = initTime(seconds, nanos)
-  # I'm not entirely certain if freebsd needs to use `gettimeofday`.
-  elif defined(macosx) or defined(freebsd):
-    var a: Timeval
-    gettimeofday(a)
-    result = initTime(a.tv_sec.int64, convert(Microseconds, Nanoseconds, a.tv_usec.int))
-  elif defined(posix):
-    var ts: Timespec
-    discard clock_gettime(CLOCK_REALTIME, ts)
-    result = initTime(ts.tv_sec.int64, ts.tv_nsec.int)
-  elif defined(windows):
-    var f: FILETIME
-    getSystemTimeAsFileTime(f)
-    result = fromWinTime(rdFileTime(f))
-
 proc now*(): DateTime {.tags: [TimeEffect], benign.} =
-  ## Get the current time as a  ``DateTime`` in the local timezone.
+  ## Get the current time as a  `DateTime` in the local timezone.
+  ## Shorthand for `getTime().local`.
   ##
-  ## Shorthand for ``getTime().local``.
+  ## .. warning:: Unsuitable for benchmarking, use `monotimes.getMonoTime` or
+  ##    `cpuTime` instead, depending on the use case.
   getTime().local
 
+proc dateTime*(year: int, month: Month, monthday: MonthdayRange,
+               hour: HourRange = 0, minute: MinuteRange = 0, second: SecondRange = 0,
+               nanosecond: NanosecondRange = 0,
+               zone: Timezone = local()): DateTime =
+  ## Create a new `DateTime <#DateTime>`_ in the specified timezone.
+  runnableExamples:
+    assert $dateTime(2017, mMar, 30, zone = utc()) == "2017-03-30T00:00:00Z"
+
+  assertValidDate monthday, month, year
+  let dt = DateTime(
+    monthdayZero: monthday,
+    year: year,
+    monthZero: month.int,
+    hour: hour,
+    minute: minute,
+    second: second,
+    nanosecond: nanosecond
+  )
+  result = initDateTime(zone.zonedTimeFromAdjTime(dt.toAdjTime), zone)
+
+proc initDateTime*(monthday: MonthdayRange, month: Month, year: int,
+                   hour: HourRange, minute: MinuteRange, second: SecondRange,
+                   nanosecond: NanosecondRange,
+                   zone: Timezone = local()): DateTime {.deprecated: "use `dateTime`".} =
+  ## Create a new `DateTime <#DateTime>`_ in the specified timezone.
+  runnableExamples("--warning:deprecated:off"):
+    assert $initDateTime(30, mMar, 2017, 00, 00, 00, 00, utc()) == "2017-03-30T00:00:00Z"
+  dateTime(year, month, monthday, hour, minute, second, nanosecond, zone)
+
+proc initDateTime*(monthday: MonthdayRange, month: Month, year: int,
+                   hour: HourRange, minute: MinuteRange, second: SecondRange,
+                   zone: Timezone = local()): DateTime {.deprecated: "use `dateTime`".} =
+  ## Create a new `DateTime <#DateTime>`_ in the specified timezone.
+  runnableExamples("--warning:deprecated:off"):
+    assert $initDateTime(30, mMar, 2017, 00, 00, 00, utc()) == "2017-03-30T00:00:00Z"
+  dateTime(year, month, monthday, hour, minute, second, 0, zone)
+
+proc `+`*(dt: DateTime, dur: Duration): DateTime =
+  runnableExamples:
+    let dt = dateTime(2017, mMar, 30, 00, 00, 00, 00, utc())
+    let dur = initDuration(hours = 5)
+    doAssert $(dt + dur) == "2017-03-30T05:00:00Z"
+
+  (dt.toTime + dur).inZone(dt.timezone)
+
+proc `-`*(dt: DateTime, dur: Duration): DateTime =
+  runnableExamples:
+    let dt = dateTime(2017, mMar, 30, 00, 00, 00, 00, utc())
+    let dur = initDuration(days = 5)
+    doAssert $(dt - dur) == "2017-03-25T00:00:00Z"
+
+  (dt.toTime - dur).inZone(dt.timezone)
+
+proc `-`*(dt1, dt2: DateTime): Duration =
+  ## Compute the duration between `dt1` and `dt2`.
+  runnableExamples:
+    let dt1 = dateTime(2017, mMar, 30, 00, 00, 00, 00, utc())
+    let dt2 = dateTime(2017, mMar, 25, 00, 00, 00, 00, utc())
+
+    doAssert dt1 - dt2 == initDuration(days = 5)
+
+  dt1.toTime - dt2.toTime
+
+proc `<`*(a, b: DateTime): bool =
+  ## Returns true if `a` happened before `b`.
+  return a.toTime < b.toTime
+
+proc `<=`*(a, b: DateTime): bool =
+  ## Returns true if `a` happened before or at the same time as `b`.
+  return a.toTime <= b.toTime
+
+proc `==`*(a, b: DateTime): bool =
+  ## Returns true if `a` and `b` represent the same point in time.
+  if not a.isInitialized: not b.isInitialized
+  elif not b.isInitialized: false
+  else: a.toTime == b.toTime
+
+proc `+=`*(a: var DateTime, b: Duration) =
+  a = a + b
+
+proc `-=`*(a: var DateTime, b: Duration) =
+  a = a - b
+
+proc getDateStr*(dt = now()): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
+  ## Gets the current local date as a string of the format `YYYY-MM-dd`.
+  runnableExamples:
+    echo getDateStr(now() - 1.months)
+  assertDateTimeInitialized dt
+  result = newStringOfCap(10)  # len("YYYY-MM-dd") == 10
+  result.addInt dt.year
+  result.add '-'
+  result.add intToStr(dt.monthZero, 2)
+  result.add '-'
+  result.add intToStr(dt.monthday, 2)
+
+proc getClockStr*(dt = now()): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
+  ## Gets the current local clock time as a string of the format `HH:mm:ss`.
+  runnableExamples:
+    echo getClockStr(now() - 1.hours)
+  assertDateTimeInitialized dt
+  result = newStringOfCap(8)  # len("HH:mm:ss") == 8
+  result.add intToStr(dt.hour, 2)
+  result.add ':'
+  result.add intToStr(dt.minute, 2)
+  result.add ':'
+  result.add intToStr(dt.second, 2)
+
+
+#
+# Iso week forward declarations
+#
+
+proc initDateTime*(weekday: WeekDay, isoweek: IsoWeekRange, isoyear: IsoYear,
+                   hour: HourRange, minute: MinuteRange, second: SecondRange,
+                   nanosecond: NanosecondRange,
+                   zone: Timezone = local()): DateTime {.gcsafe, raises: [], tags: [], since: (1, 5).}
+
+proc initDateTime*(weekday: WeekDay, isoweek: IsoWeekRange, isoyear: IsoYear,
+                   hour: HourRange, minute: MinuteRange, second: SecondRange,
+                   zone: Timezone = local()): DateTime {.gcsafe, raises: [], tags: [], since: (1, 5).}
+
+#
+# TimeFormat
+#
+
+when defined(nimHasStyleChecks):
+  {.push styleChecks: off.}
+
+type
+  DateTimeLocale* = object
+    MMM*: array[mJan..mDec, string]
+    MMMM*: array[mJan..mDec, string]
+    ddd*: array[dMon..dSun, string]
+    dddd*: array[dMon..dSun, string]
+
+when defined(nimHasStyleChecks):
+  {.pop.}
+
+type
+  AmPm = enum
+    apUnknown, apAm, apPm
+
+  Era = enum
+    eraUnknown, eraAd, eraBc
+
+  ParsedTime = object
+    amPm: AmPm
+    era: Era
+    year: Option[int]
+    month: Option[int]
+    monthday: Option[int]
+    isoyear: Option[int]
+    yearweek: Option[int]
+    weekday: Option[WeekDay]
+    utcOffset: Option[int]
+
+    # '0' as default for these work fine
+    # so no need for `Option`.
+    hour: int
+    minute: int
+    second: int
+    nanosecond: int
+
+  FormatTokenKind = enum
+    tkPattern, tkLiteral
+
+  FormatPattern {.pure.} = enum
+    d, dd, ddd, dddd
+    GG, GGGG
+    h, hh, H, HH
+    m, mm, M, MM, MMM, MMMM
+    s, ss
+    fff, ffffff, fffffffff
+    t, tt
+    yy, yyyy
+    YYYY
+    uuuu
+    UUUU
+    V, VV
+    z, zz, zzz, zzzz
+    ZZZ, ZZZZ
+    g
+
+    # This is a special value used to mark literal format values.
+    # See the doc comment for `TimeFormat.patterns`.
+    Lit
+
+  TimeFormat* = object  ## Represents a format for parsing and printing
+                        ## time types.
+                        ##
+                        ## To create a new `TimeFormat` use `initTimeFormat proc
+                        ## <#initTimeFormat,string>`_.
+    patterns: seq[byte] ## \
+      ## Contains the patterns encoded as bytes.
+      ## Literal values are encoded in a special way.
+      ## They start with `Lit.byte`, then the length of the literal, then the
+      ## raw char values of the literal. For example, the literal `foo` would
+      ## be encoded as `@[Lit.byte, 3.byte, 'f'.byte, 'o'.byte, 'o'.byte]`.
+    formatStr: string
+
+  TimeParseError* = object of ValueError ## \
+    ## Raised when parsing input using a `TimeFormat` fails.
+
+  TimeFormatParseError* = object of ValueError ## \
+    ## Raised when parsing a `TimeFormat` string fails.
+
+const
+  DefaultLocale* = DateTimeLocale(
+    MMM: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
+        "Nov", "Dec"],
+    MMMM: ["January", "February", "March", "April", "May", "June", "July",
+        "August", "September", "October", "November", "December"],
+    ddd: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
+    dddd: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
+        "Sunday"],
+  )
+
+  FormatLiterals = {' ', '-', '/', ':', '(', ')', '[', ']', ',', '.'}
+
+proc `$`*(f: TimeFormat): string =
+  ## Returns the format string that was used to construct `f`.
+  runnableExamples:
+    let f = initTimeFormat("yyyy-MM-dd")
+    doAssert $f == "yyyy-MM-dd"
+  f.formatStr
+
+proc raiseParseException(f: TimeFormat, input: string, msg: string) =
+  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 = ""
+
+  template yieldCurrToken() =
+    if currToken.len != 0:
+      yield (tkPattern, currToken)
+      currToken = ""
+
+  while i < f.len:
+    case f[i]
+    of '\'':
+      yieldCurrToken()
+      if i.succ < f.len and f[i.succ] == '\'':
+        yield (tkLiteral, "'")
+        i.inc 2
+      else:
+        var token = ""
+        inc(i) # Skip '
+        while i < f.len and f[i] != '\'':
+          token.add f[i]
+          i.inc
+
+        if i > f.high:
+          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
+    else:
+      # Check if the letter being added matches previous accumulated buffer.
+      if currToken.len == 0 or currToken[0] == f[i]:
+        currToken.add(f[i])
+        i.inc
+      else:
+        yield (tkPattern, currToken)
+        currToken = $f[i]
+        i.inc
+
+  yieldCurrToken()
+
+proc stringToPattern(str: string): FormatPattern =
+  case str
+  of "d": result = d
+  of "dd": result = dd
+  of "ddd": result = ddd
+  of "dddd": result = dddd
+  of "GG": result = GG
+  of "GGGG": result = GGGG
+  of "h": result = h
+  of "hh": result = hh
+  of "H": result = H
+  of "HH": result = HH
+  of "m": result = m
+  of "mm": result = mm
+  of "M": result = M
+  of "MM": result = MM
+  of "MMM": result = MMM
+  of "MMMM": result = MMMM
+  of "s": result = s
+  of "ss": result = ss
+  of "fff": result = fff
+  of "ffffff": result = ffffff
+  of "fffffffff": result = fffffffff
+  of "t": result = t
+  of "tt": result = tt
+  of "yy": result = yy
+  of "yyyy": result = yyyy
+  of "YYYY": result = YYYY
+  of "uuuu": result = uuuu
+  of "UUUU": result = UUUU
+  of "V": result = V
+  of "VV": result = VV
+  of "z": result = z
+  of "zz": result = zz
+  of "zzz": result = zzz
+  of "zzzz": result = zzzz
+  of "ZZZ": result = ZZZ
+  of "ZZZZ": result = ZZZZ
+  of "g": result = g
+  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.
+  ##
+  ## See `Parsing and formatting dates`_ for documentation of the
+  ## `format` argument.
+  runnableExamples:
+    let f = initTimeFormat("yyyy-MM-dd")
+    doAssert "2000-01-01" == "2000-01-01".parse(f).format(f)
+  result.formatStr = format
+  result.patterns = @[]
+  for kind, token in format.tokens:
+    case kind
+    of tkLiteral:
+      case token
+      else:
+        result.patterns.add(FormatPattern.Lit.byte)
+        if token.len > 255:
+          raise newException(TimeFormatParseError,
+                             "Format literal is to long:" & token)
+        result.patterns.add(token.len.byte)
+        for c in token:
+          result.patterns.add(c.byte)
+    of tkPattern:
+      result.patterns.add(stringToPattern(token).byte)
+
+proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string,
+    loc: DateTimeLocale) =
+  template yearOfEra(dt: DateTime): int =
+    if dt.year <= 0: abs(dt.year) + 1 else: dt.year
+
+  case pattern
+  of d:
+    result.add $dt.monthday
+  of dd:
+    result.add dt.monthday.intToStr(2)
+  of ddd:
+    result.add loc.ddd[dt.weekday]
+  of dddd:
+    result.add loc.dddd[dt.weekday]
+  of GG:
+    result.add (dt.getIsoWeekAndYear.isoyear.int mod 100).intToStr(2)
+  of GGGG:
+    result.add $dt.getIsoWeekAndYear.isoyear
+  of h:
+    result.add(
+      if dt.hour == 0: "12"
+      elif dt.hour > 12: $(dt.hour - 12)
+      else: $dt.hour
+    )
+  of hh:
+    result.add(
+      if dt.hour == 0: "12"
+      elif dt.hour > 12: (dt.hour - 12).intToStr(2)
+      else: dt.hour.intToStr(2)
+    )
+  of H:
+    result.add $dt.hour
+  of HH:
+    result.add dt.hour.intToStr(2)
+  of m:
+    result.add $dt.minute
+  of mm:
+    result.add dt.minute.intToStr(2)
+  of M:
+    result.add $ord(dt.month)
+  of MM:
+    result.add ord(dt.month).intToStr(2)
+  of MMM:
+    result.add loc.MMM[dt.month]
+  of MMMM:
+    result.add loc.MMMM[dt.month]
+  of s:
+    result.add $dt.second
+  of ss:
+    result.add dt.second.intToStr(2)
+  of fff:
+    result.add(intToStr(convert(Nanoseconds, Milliseconds, dt.nanosecond), 3))
+  of ffffff:
+    result.add(intToStr(convert(Nanoseconds, Microseconds, dt.nanosecond), 6))
+  of fffffffff:
+    result.add(intToStr(dt.nanosecond, 9))
+  of t:
+    result.add if dt.hour >= 12: "P" else: "A"
+  of tt:
+    result.add if dt.hour >= 12: "PM" else: "AM"
+  of yy:
+    result.add (dt.yearOfEra mod 100).intToStr(2)
+  of yyyy:
+    let year = dt.yearOfEra
+    if year < 10000:
+      result.add year.intToStr(4)
+    else:
+      result.add '+' & $year
+  of YYYY:
+    if dt.year < 1:
+      result.add $(abs(dt.year) + 1)
+    else:
+      result.add $dt.year
+  of uuuu:
+    let year = dt.year
+    if year < 10000 or year < 0:
+      result.add year.intToStr(4)
+    else:
+      result.add '+' & $year
+  of UUUU:
+    result.add $dt.year
+  of V:
+    result.add $dt.getIsoWeekAndYear.isoweek
+  of VV:
+    result.add dt.getIsoWeekAndYear.isoweek.intToStr(2)
+  of z, zz, zzz, zzzz, ZZZ, ZZZZ:
+    if dt.timezone != nil and dt.timezone.name == "Etc/UTC":
+      result.add 'Z'
+    else:
+      result.add if -dt.utcOffset >= 0: '+' else: '-'
+      let absOffset = abs(dt.utcOffset)
+      case pattern:
+      of z:
+        result.add $(absOffset div 3600)
+      of zz:
+        result.add (absOffset div 3600).intToStr(2)
+      of zzz, ZZZ:
+        let h = (absOffset div 3600).intToStr(2)
+        let m = ((absOffset div 60) mod 60).intToStr(2)
+        let sep = if pattern == zzz: ":" else: ""
+        result.add h & sep & m
+      of zzzz, ZZZZ:
+        let absOffset = abs(dt.utcOffset)
+        let h = (absOffset div 3600).intToStr(2)
+        let m = ((absOffset div 60) mod 60).intToStr(2)
+        let s = (absOffset mod 60).intToStr(2)
+        let sep = if pattern == zzzz: ":" else: ""
+        result.add h & sep & m & sep & s
+      else: assert false
+  of g:
+    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, loc: DateTimeLocale): bool =
+  template takeInt(allowedWidth: Slice[int], allowSign = false): int =
+    var sv = 0
+    var pd = parseInt(input, sv, i, allowedWidth.b, allowSign)
+    if pd < allowedWidth.a:
+      return false
+    i.inc pd
+    sv
+
+  template contains[T](t: typedesc[T], i: int): bool =
+    i in low(t)..high(t)
+
+  result = true
+
+  case pattern
+  of d:
+    let monthday = takeInt(1..2)
+    parsed.monthday = some(monthday)
+    result = monthday in MonthdayRange
+  of dd:
+    let monthday = takeInt(2..2)
+    parsed.monthday = some(monthday)
+    result = monthday in MonthdayRange
+  of ddd:
+    result = false
+    for d, v in loc.ddd:
+      if input.substr(i, i+v.len-1).cmpIgnoreCase(v) == 0:
+        parsed.weekday = some(d.WeekDay)
+        result = true
+        i.inc v.len
+        break
+  of dddd:
+    result = false
+    for d, v in loc.dddd:
+      if input.substr(i, i+v.len-1).cmpIgnoreCase(v) == 0:
+        parsed.weekday = some(d.WeekDay)
+        result = true
+        i.inc v.len
+        break
+  of GG:
+    # Assumes current century
+    var isoyear = takeInt(2..2)
+    var thisCen = now().year div 100
+    parsed.isoyear = some(thisCen*100 + isoyear)
+    result = isoyear > 0
+  of GGGG:
+    let isoyear = takeInt(1..high(int))
+    parsed.isoyear = some(isoyear)
+    result = isoyear > 0
+  of h, H:
+    parsed.hour = takeInt(1..2)
+    result = parsed.hour in HourRange
+  of hh, HH:
+    parsed.hour = takeInt(2..2)
+    result = parsed.hour in HourRange
+  of m:
+    parsed.minute = takeInt(1..2)
+    result = parsed.hour in MinuteRange
+  of mm:
+    parsed.minute = takeInt(2..2)
+    result = parsed.hour in MinuteRange
+  of M:
+    let month = takeInt(1..2)
+    result = month in 1..12
+    parsed.month = some(month)
+  of MM:
+    let month = takeInt(2..2)
+    result = month in 1..12
+    parsed.month = some(month)
+  of MMM:
+    result = false
+    for n, v in loc.MMM:
+      if input.substr(i, i+v.len-1).cmpIgnoreCase(v) == 0:
+        result = true
+        i.inc v.len
+        parsed.month = some(n.int)
+        break
+  of MMMM:
+    result = false
+    for n, v in loc.MMMM:
+      if input.substr(i, i+v.len-1).cmpIgnoreCase(v) == 0:
+        result = true
+        i.inc v.len
+        parsed.month = some(n.int)
+        break
+  of s:
+    parsed.second = takeInt(1..2)
+  of ss:
+    parsed.second = takeInt(2..2)
+  of fff, ffffff, fffffffff:
+    let len = ($pattern).len
+    let v = takeInt(len..len)
+    parsed.nanosecond = v * 10^(9 - len)
+    result = parsed.nanosecond in NanosecondRange
+  of t:
+    case input[i]:
+    of 'P':
+      parsed.amPm = apPm
+    of 'A':
+      parsed.amPm = apAm
+    else:
+      result = false
+    i.inc 1
+  of tt:
+    if input.substr(i, i+1).cmpIgnoreCase("AM") == 0:
+      parsed.amPm = apAm
+      i.inc 2
+    elif input.substr(i, i+1).cmpIgnoreCase("PM") == 0:
+      parsed.amPm = apPm
+      i.inc 2
+    else:
+      result = false
+  of yy:
+    # Assumes current century
+    var year = takeInt(2..2)
+    var thisCen = now().year div 100
+    parsed.year = some(thisCen*100 + year)
+    result = year > 0
+  of yyyy:
+    let year =
+      if input[i] in {'+', '-'}:
+        takeInt(4..high(int), allowSign = true)
+      else:
+        takeInt(4..4)
+    result = year > 0
+    parsed.year = some(year)
+  of YYYY:
+    let year = takeInt(1..high(int))
+    parsed.year = some(year)
+    result = year > 0
+  of uuuu:
+    let year =
+      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), allowSign = true))
+  of V:
+    let yearweek = takeInt(1..2)
+    parsed.yearweek = some(yearweek)
+    result = yearweek in IsoWeekRange
+  of VV:
+    let yearweek = takeInt(2..2)
+    parsed.yearweek = some(yearweek)
+    result = yearweek in IsoWeekRange
+  of z, zz, zzz, zzzz, ZZZ, ZZZZ:
+    case input[i]
+    of '+', '-':
+      let sign = if input[i] == '-': 1 else: -1
+      i.inc
+      var offset = 0
+      case pattern
+      of z:
+        offset = takeInt(1..2) * 3600
+      of zz:
+        offset = takeInt(2..2) * 3600
+      of zzz, ZZZ:
+        offset.inc takeInt(2..2) * 3600
+        if pattern == zzz:
+          if input[i] != ':':
+            return false
+          i.inc
+        offset.inc takeInt(2..2) * 60
+      of zzzz, ZZZZ:
+        offset.inc takeInt(2..2) * 3600
+        if pattern == zzzz:
+          if input[i] != ':':
+            return false
+          i.inc
+        offset.inc takeInt(2..2) * 60
+        if pattern == zzzz:
+          if input[i] != ':':
+            return false
+          i.inc
+        offset.inc takeInt(2..2)
+      else: assert false
+      parsed.utcOffset = some(offset * sign)
+    of 'Z':
+      parsed.utcOffset = some(0)
+      i.inc
+    else:
+      result = false
+  of g:
+    if input.substr(i, i+1).cmpIgnoreCase("BC") == 0:
+      parsed.era = eraBc
+      i.inc 2
+    elif input.substr(i, i+1).cmpIgnoreCase("AD") == 0:
+      parsed.era = eraAd
+      i.inc 2
+    else:
+      result = false
+  of Lit: raiseAssert "Can't happen"
+
+proc toDateTime(p: ParsedTime, zone: Timezone, f: TimeFormat,
+                input: string): DateTime =
+  var year = p.year.get(0)
+  var month = p.month.get(1).Month
+  var monthday = p.monthday.get(1)
+  year =
+    case p.era
+    of eraUnknown:
+      year
+    of eraBc:
+      if year < 1:
+        raiseParseException(f, input,
+          "Expected year to be positive " &
+          "(use 'UUUU' or 'uuuu' for negative years).")
+      -year + 1
+    of eraAd:
+      if year < 1:
+        raiseParseException(f, input,
+          "Expected year to be positive " &
+          "(use 'UUUU' or 'uuuu' for negative years).")
+      year
+
+  let hour =
+    case p.amPm
+    of apUnknown:
+      p.hour
+    of apAm:
+      if p.hour notin 1..12:
+        raiseParseException(f, input,
+          "AM/PM time must be in the interval 1..12")
+      if p.hour == 12: 0 else: p.hour
+    of apPm:
+      if p.hour notin 1..12:
+        raiseParseException(f, input,
+          "AM/PM time must be in the interval 1..12")
+      if p.hour == 12: p.hour else: p.hour + 12
+  let minute = p.minute
+  let second = p.second
+  let nanosecond = p.nanosecond
+
+  if monthday > getDaysInMonth(month, year):
+    raiseParseException(f, input,
+      $year & "-" & ord(month).intToStr(2) &
+      "-" & $monthday & " is not a valid date")
+
+  if p.utcOffset.isNone:
+    # No timezone parsed - assume timezone is `zone`
+    result = dateTime(year, month, monthday, hour, minute, second, nanosecond, zone)
+  else:
+    # Otherwise convert to `zone`
+    result = (dateTime(year, month, monthday, hour, minute, second, nanosecond, utc()).toTime +
+      initDuration(seconds = p.utcOffset.get())).inZone(zone)
+
+proc toDateTimeByWeek(p: ParsedTime, zone: Timezone, f: TimeFormat,
+                   input: string): DateTime =
+  var isoyear = p.isoyear.get(0)
+  var yearweek = p.yearweek.get(1)
+  var weekday = p.weekday.get(dMon)
+
+  if p.amPm != apUnknown:
+    raiseParseException(f, input, "Parsing iso weekyear dates does not support am/pm")
+
+  if p.year.isSome:
+    raiseParseException(f, input, "Use iso-year GG or GGGG as year with iso week number")
+
+  if p.month.isSome:
+    raiseParseException(f, input, "Use either iso week number V or VV or month")
+
+  if p.monthday.isSome:
+    raiseParseException(f, input, "Use weekday ddd or dddd as day with with iso week number")
+
+  if p.isoyear.isNone:
+    raiseParseException(f, input, "Need iso-year with week number")
+
+  let hour = p.hour
+  let minute = p.minute
+  let second = p.second
+  let nanosecond = p.nanosecond
+
+  if p.utcOffset.isNone:
+    result = initDateTime(weekday, yearweek.IsoWeekRange, isoyear.IsoYear, hour, minute, second, nanosecond, zone)
+  else:
+    result = (initDateTime(weekday, yearweek.IsoWeekRange, isoyear.IsoYear, hour, minute, second, nanosecond, zone).toTime +
+      initDuration(seconds = p.utcOffset.get())).inZone(zone)
+
+proc format*(dt: DateTime, f: TimeFormat,
+    loc: DateTimeLocale = DefaultLocale): string {.raises: [].} =
+  ## Format `dt` using the format specified by `f`.
+  runnableExamples:
+    let f = initTimeFormat("yyyy-MM-dd")
+    let dt = dateTime(2000, mJan, 01, 00, 00, 00, 00, utc())
+    doAssert "2000-01-01" == dt.format(f)
+  assertDateTimeInitialized dt
+  result = ""
+  var idx = 0
+  while idx <= f.patterns.high:
+    case f.patterns[idx].FormatPattern
+    of Lit:
+      idx.inc
+      let len = f.patterns[idx]
+      for i in 1'u8..len:
+        idx.inc
+        result.add f.patterns[idx].char
+      idx.inc
+    else:
+      formatPattern(dt, f.patterns[idx].FormatPattern, result = result, loc = loc)
+      idx.inc
+
+proc format*(dt: DateTime, f: string, loc: DateTimeLocale = DefaultLocale): string
+    {.raises: [TimeFormatParseError].} =
+  ## Shorthand for constructing a `TimeFormat` and using it to format `dt`.
+  ##
+  ## See `Parsing and formatting dates`_ for documentation of the
+  ## `format` argument.
+  runnableExamples:
+    let dt = dateTime(2000, mJan, 01, 00, 00, 00, 00, utc())
+    doAssert "2000-01-01" == format(dt, "yyyy-MM-dd")
+  let dtFormat = initTimeFormat(f)
+  result = dt.format(dtFormat, loc)
+
+proc format*(dt: DateTime, f: static[string]): string {.raises: [].} =
+  ## Overload that validates `format` at compile time.
+  const f2 = initTimeFormat(f)
+  result = dt.format(f2)
+
+proc formatValue*(result: var string; value: DateTime | Time, specifier: string) =
+  ## adapter for strformat. Not intended to be called directly.
+  result.add format(value,
+    if specifier.len == 0: "yyyy-MM-dd'T'HH:mm:sszzz" else: specifier)
+
+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`.
+  ##
+  ## See `Parsing and formatting dates`_ for documentation of the
+  ## `f` argument.
+  runnableExamples:
+    var dt = dateTime(1970, mJan, 01, 00, 00, 00, 00, utc())
+    var tm = dt.toTime()
+    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
+    {.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(),
+    loc: DateTimeLocale = DefaultLocale): DateTime {.parseRaises.} =
+  ## 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
+  ## converted to the `zone` timezone.
+  ##
+  ## Month and day names from the passed in `loc` are used.
+  runnableExamples:
+    let f = initTimeFormat("yyyy-MM-dd")
+    let dt = dateTime(2000, mJan, 01, 00, 00, 00, 00, utc())
+    doAssert dt == "2000-01-01".parse(f, utc())
+  var inpIdx = 0 # Input index
+  var patIdx = 0 # Pattern index
+  var parsed: ParsedTime
+  while inpIdx <= input.high and patIdx <= f.patterns.high:
+    let pattern = f.patterns[patIdx].FormatPattern
+    case pattern
+    of Lit:
+      patIdx.inc
+      let len = f.patterns[patIdx]
+      patIdx.inc
+      for _ in 1'u8..len:
+        if input[inpIdx] != f.patterns[patIdx].char:
+          raiseParseException(f, input,
+                              "Unexpected character: " & input[inpIdx])
+        inpIdx.inc
+        patIdx.inc
+    else:
+      if not parsePattern(input, pattern, inpIdx, parsed, loc):
+        raiseParseException(f, input, "Failed on pattern '" & $pattern & "'")
+      patIdx.inc
+
+  if inpIdx <= input.high:
+    raiseParseException(f, input,
+                        "Parsing ended but there was still input remaining")
+
+  if patIdx <= f.patterns.high:
+    raiseParseException(f, input,
+                            "Parsing ended but there was still patterns remaining")
+
+  if parsed.yearweek.isSome:
+    result = toDateTimeByWeek(parsed, zone, f, input)
+  elif parsed.isoyear.isSome:
+    raiseParseException(f, input, "Iso year GG or GGGG require iso week V or VV")
+  else:
+    result = toDateTime(parsed, zone, f, input)
+
+proc parse*(input, f: string, tz: Timezone = local(),
+    loc: DateTimeLocale = DefaultLocale): DateTime {.parseFormatRaises.} =
+  ## Shorthand for constructing a `TimeFormat` and using it to parse
+  ## `input` as a `DateTime`.
+  ##
+  ## See `Parsing and formatting dates`_ for documentation of the
+  ## `f` argument.
+  runnableExamples:
+    let dt = dateTime(2000, mJan, 01, 00, 00, 00, 00, utc())
+    doAssert dt == parse("2000-01-01", "yyyy-MM-dd", utc())
+  let dtFormat = initTimeFormat(f)
+  result = input.parse(dtFormat, tz, loc = loc)
+
+proc parse*(input: string, f: static[string], zone: Timezone = local(),
+    loc: DateTimeLocale = DefaultLocale): DateTime {.parseRaises.} =
+  ## Overload that validates `f` at compile time.
+  const f2 = initTimeFormat(f)
+  result = input.parse(f2, zone, loc = loc)
+
+proc parseTime*(input, f: string, zone: Timezone): Time {.parseFormatRaises.} =
+  ## Shorthand for constructing a `TimeFormat` and using it to parse
+  ## `input` as a `DateTime`, then converting it a `Time`.
+  ##
+  ## See `Parsing and formatting dates`_ for documentation of the
+  ## `format` argument.
+  runnableExamples:
+    let tStr = "1970-01-01T00:00:00+00:00"
+    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
+    {.parseRaises.} =
+  ## Overload that validates `format` at compile time.
+  const f2 = initTimeFormat(f)
+  result = input.parse(f2, zone).toTime()
+
+proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} =
+  ## Converts a `DateTime` object to a string representation.
+  ## It uses the format `yyyy-MM-dd'T'HH:mm:sszzz`.
+  runnableExamples:
+    let dt = dateTime(2000, mJan, 01, 12, 00, 00, 00, utc())
+    doAssert $dt == "2000-01-01T12:00:00Z"
+    doAssert $default(DateTime) == "Uninitialized DateTime"
+  if not dt.isInitialized:
+    result = "Uninitialized DateTime"
+  else:
+    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
+  ## time zone and use the format `yyyy-MM-dd'T'HH:mm:sszzz`.
+  runnableExamples:
+    let dt = dateTime(1970, mJan, 01, 00, 00, 00, 00, local())
+    let tm = dt.toTime()
+    doAssert $tm == "1970-01-01T00:00:00" & format(dt, "zzz")
+  $time.local
+
+#
+# TimeInterval
+#
+
 proc initTimeInterval*(nanoseconds, microseconds, milliseconds,
                        seconds, minutes, hours,
                        days, weeks, months, years: int = 0): TimeInterval =
-  ## Creates a new ``TimeInterval``.
+  ## Creates a new `TimeInterval <#TimeInterval>`_.
   ##
-  ## You can also use the convenience procedures called ``milliseconds``,
-  ## ``seconds``, ``minutes``, ``hours``, ``days``, ``months``, and ``years``.
+  ## 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 dt = initDateTime(01, mJan, 2000, 12, 00, 00, utc())
-    doAssert $(dt + day) == "2000-01-02T12:00:00+00:00"
+    let day = initTimeInterval(hours = 24)
+    let dt = dateTime(2000, mJan, 01, 12, 00, 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
@@ -930,7 +2378,7 @@ proc initTimeInterval*(nanoseconds, microseconds, milliseconds,
   result.years = years
 
 proc `+`*(ti1, ti2: TimeInterval): TimeInterval =
-  ## Adds two ``TimeInterval`` objects together.
+  ## Adds two `TimeInterval` objects together.
   result.nanoseconds = ti1.nanoseconds + ti2.nanoseconds
   result.microseconds = ti1.microseconds + ti2.microseconds
   result.milliseconds = ti1.milliseconds + ti2.milliseconds
@@ -945,7 +2393,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(
@@ -962,50 +2410,148 @@ proc `-`*(ti: TimeInterval): TimeInterval =
   )
 
 proc `-`*(ti1, ti2: TimeInterval): TimeInterval =
-  ## Subtracts TimeInterval ``ti1`` from ``ti2``.
+  ## Subtracts TimeInterval `ti1` from `ti2`.
   ##
   ## 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)
 
-proc getDateStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
-  ## Gets the current date as a string of the format ``YYYY-MM-DD``.
-  var ti = now()
-  result = $ti.year & '-' & intToStr(ord(ti.month), 2) &
-    '-' & intToStr(ti.monthday, 2)
-
-proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
-  ## Gets the current clock time as a string of the format ``HH:MM:SS``.
-  var ti = now()
-  result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) &
-    ':' & intToStr(ti.second, 2)
-
-proc `$`*(day: WeekDay): string =
-  ## Stringify operator for ``WeekDay``.
-  const lookup: array[WeekDay, string] = ["Monday", "Tuesday", "Wednesday",
-     "Thursday", "Friday", "Saturday", "Sunday"]
-  return lookup[day]
-
-proc `$`*(m: Month): string =
-  ## Stringify operator for ``Month``.
-  const lookup: array[Month, string] = ["January", "February", "March",
-      "April", "May", "June", "July", "August", "September", "October",
-      "November", "December"]
-  return lookup[m]
-
-
-proc toParts* (ti: TimeInterval): TimeIntervalParts =
+proc `+=`*(a: var TimeInterval, b: TimeInterval) =
+  a = a + b
+
+proc `-=`*(a: var TimeInterval, b: TimeInterval) =
+  a = a - b
+
+proc isStaticInterval(interval: TimeInterval): bool =
+  interval.years == 0 and interval.months == 0 and
+    interval.days == 0 and interval.weeks == 0
+
+proc evaluateStaticInterval(interval: TimeInterval): Duration =
+  assert interval.isStaticInterval
+  initDuration(nanoseconds = interval.nanoseconds,
+    microseconds = interval.microseconds,
+    milliseconds = interval.milliseconds,
+    seconds = interval.seconds,
+    minutes = interval.minutes,
+    hours = interval.hours)
+
+proc between*(startDt, endDt: DateTime): TimeInterval =
+  ## Gives the difference between `startDt` and `endDt` as a
+  ## `TimeInterval`. The following guarantees about the result is given:
+  ##
+  ## - All fields will have the same sign.
+  ## - If `startDt.timezone == endDt.timezone`, it is guaranteed that
+  ##   `startDt + between(startDt, endDt) == endDt`.
+  ## - If `startDt.timezone != endDt.timezone`, then the result will be
+  ##   equivalent to `between(startDt.utc, endDt.utc)`.
+  runnableExamples:
+    var a = dateTime(2015, mMar, 25, 12, 0, 0, 00, utc())
+    var b = dateTime(2017, mApr, 1, 15, 0, 15, 00, utc())
+    var ti = initTimeInterval(years = 2, weeks = 1, hours = 3, seconds = 15)
+    doAssert between(a, b) == ti
+    doAssert between(a, b) == -between(b, a)
+
+  if startDt.timezone != endDt.timezone:
+    return between(startDt.utc, endDt.utc)
+  elif endDt < startDt:
+    return -between(endDt, startDt)
+
+  type Date = tuple[year, month, monthday: int]
+  var startDate: Date = (startDt.year, startDt.month.ord, startDt.monthday)
+  var endDate: Date = (endDt.year, endDt.month.ord, endDt.monthday)
+
+  # Subtract one day from endDate if time of day is earlier than startDay
+  # The subtracted day will be counted by fixed units (hour and lower)
+  # at the end of this proc
+  if (endDt.hour, endDt.minute, endDt.second, endDt.nanosecond) <
+      (startDt.hour, startDt.minute, startDt.second, startDt.nanosecond):
+    if endDate.month == 1 and endDate.monthday == 1:
+      endDate.year.dec
+      endDate.monthday = 31
+      endDate.month = 12
+    elif endDate.monthday == 1:
+      endDate.month.dec
+      endDate.monthday = getDaysInMonth(endDate.month.Month, endDate.year)
+    else:
+      endDate.monthday.dec
+
+  # Years
+  result.years = endDate.year - startDate.year - 1
+  if (startDate.month, startDate.monthday) <= (endDate.month, endDate.monthday):
+    result.years.inc
+  startDate.year.inc result.years
+
+  # Months
+  if startDate.year < endDate.year:
+    result.months.inc 12 - startDate.month # Move to dec
+    if endDate.month != 1 or (startDate.monthday <= endDate.monthday):
+      result.months.inc
+      startDate.year = endDate.year
+      startDate.month = 1
+    else:
+      startDate.month = 12
+  if startDate.year == endDate.year:
+    if (startDate.monthday <= endDate.monthday):
+      result.months.inc endDate.month - startDate.month
+      startDate.month = endDate.month
+    elif endDate.month != 1:
+      let month = endDate.month - 1
+      let daysInMonth = getDaysInMonth(month.Month, startDate.year)
+      if daysInMonth < startDate.monthday:
+        if startDate.monthday - daysInMonth < endDate.monthday:
+          result.months.inc endDate.month - startDate.month - 1
+          startDate.month = endDate.month
+          startDate.monthday = startDate.monthday - daysInMonth
+        else:
+          result.months.inc endDate.month - startDate.month - 2
+          startDate.month = endDate.month - 2
+      else:
+        result.months.inc endDate.month - startDate.month - 1
+        startDate.month = endDate.month - 1
+
+  # Days
+  # This means that start = dec and end = jan
+  if startDate.year < endDate.year:
+    result.days.inc 31 - startDate.monthday + endDate.monthday
+    startDate = endDate
+  else:
+    while startDate.month < endDate.month:
+      let daysInMonth = getDaysInMonth(startDate.month.Month, startDate.year)
+      result.days.inc daysInMonth - startDate.monthday + 1
+      startDate.month.inc
+      startDate.monthday = 1
+    result.days.inc endDate.monthday - startDate.monthday
+    result.weeks = result.days div 7
+    result.days = result.days mod 7
+    startDate = endDate
+
+  # Handle hours, minutes, seconds, milliseconds, microseconds and nanoseconds
+  let newStartDt = dateTime(startDate.year, startDate.month.Month,
+    startDate.monthday, startDt.hour, startDt.minute, startDt.second,
+    startDt.nanosecond, startDt.timezone)
+  let dur = endDt - newStartDt
+  let parts = toParts(dur)
+  # There can still be a full day in `parts` since `Duration` and `TimeInterval`
+  # models days differently.
+  result.hours = parts[Hours].int + parts[Days].int * 24
+  result.minutes = parts[Minutes].int
+  result.seconds = parts[Seconds].int
+  result.milliseconds = parts[Milliseconds].int
+  result.microseconds = parts[Microseconds].int
+  result.nanoseconds = parts[Nanoseconds].int
+
+proc toParts*(ti: TimeInterval): TimeIntervalParts =
   ## Converts a `TimeInterval` into an array consisting of its time units,
-  ## starting with nanoseconds and ending with years
+  ## starting with nanoseconds and ending with years.
   ##
-  ## This procedure is useful for converting ``TimeInterval`` values to strings.
+  ## 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
 
@@ -1015,83 +2561,86 @@ 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] = @[]
   var tiParts = toParts(ti)
   for unit in countdown(Years, Nanoseconds):
     if tiParts[unit] != 0:
-      parts.add(stringifyUnit(tiParts[unit], $unit))
+      parts.add(stringifyUnit(tiParts[unit], unit))
 
   result = humanizeParts(parts)
 
 proc nanoseconds*(nanos: int): TimeInterval {.inline.} =
-  ## TimeInterval of ``nanos`` nanoseconds.
+  ## TimeInterval of `nanos` nanoseconds.
   initTimeInterval(nanoseconds = nanos)
 
 proc microseconds*(micros: int): TimeInterval {.inline.} =
-  ## TimeInterval of ``micros`` microseconds.
+  ## TimeInterval of `micros` microseconds.
   initTimeInterval(microseconds = micros)
 
 proc milliseconds*(ms: int): TimeInterval {.inline.} =
-  ## TimeInterval of ``ms`` milliseconds.
+  ## TimeInterval of `ms` milliseconds.
   initTimeInterval(milliseconds = ms)
 
 proc seconds*(s: int): TimeInterval {.inline.} =
-  ## TimeInterval of ``s`` seconds.
+  ## TimeInterval of `s` seconds.
   ##
-  ## ``echo getTime() + 5.second``
+  ## `echo getTime() + 5.seconds`
   initTimeInterval(seconds = s)
 
 proc minutes*(m: int): TimeInterval {.inline.} =
-  ## TimeInterval of ``m`` minutes.
+  ## TimeInterval of `m` minutes.
   ##
-  ## ``echo getTime() + 5.minutes``
+  ## `echo getTime() + 5.minutes`
   initTimeInterval(minutes = m)
 
 proc hours*(h: int): TimeInterval {.inline.} =
-  ## TimeInterval of ``h`` hours.
+  ## TimeInterval of `h` hours.
   ##
-  ## ``echo getTime() + 2.hours``
+  ## `echo getTime() + 2.hours`
   initTimeInterval(hours = h)
 
 proc days*(d: int): TimeInterval {.inline.} =
-  ## TimeInterval of ``d`` days.
+  ## TimeInterval of `d` days.
   ##
-  ## ``echo getTime() + 2.days``
+  ## `echo getTime() + 2.days`
   initTimeInterval(days = d)
 
 proc weeks*(w: int): TimeInterval {.inline.} =
-  ## TimeInterval of ``w`` weeks.
+  ## TimeInterval of `w` weeks.
   ##
-  ## ``echo getTime() + 2.weeks``
+  ## `echo getTime() + 2.weeks`
   initTimeInterval(weeks = w)
 
 proc months*(m: int): TimeInterval {.inline.} =
-  ## TimeInterval of ``m`` months.
+  ## TimeInterval of `m` months.
   ##
-  ## ``echo getTime() + 2.months``
+  ## `echo getTime() + 2.months`
   initTimeInterval(months = m)
 
 proc years*(y: int): TimeInterval {.inline.} =
-  ## TimeInterval of ``y`` years.
+  ## TimeInterval of `y` years.
   ##
-  ## ``echo getTime() + 2.years``
+  ## `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``.
+  ## in the context of `dt`.
   ## The result in split into an adjusted diff and an absolute diff.
   var months = interval.years * 12 + interval.months
   var curYear = dt.year
   var curMonth = dt.month
+  result = default(tuple[adjDur, absDur: Duration])
   # Subtracting
   if months < 0:
-    for mth in countDown(-1 * months, 1):
+    for mth in countdown(-1 * months, 1):
       if curMonth == mJan:
         curMonth = mDec
         curYear.dec
@@ -1121,230 +2670,46 @@ 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 =
-  ## Create a new ``DateTime`` in the specified timezone.
-  runnableExamples:
-    let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, 00, utc())
-    doAssert $dt1 == "2017-03-30T00:00:00+00:00"
-
-  assertValidDate monthday, month, year
-  let dt = DateTime(
-    monthday:  monthday,
-    year:  year,
-    month:  month,
-    hour:  hour,
-    minute:  minute,
-    second:  second,
-    nanosecond: nanosecond
-  )
-  result = initDateTime(zone.zoneInfoFromTz(dt.toAdjTime), zone)
-
-proc initDateTime*(monthday: MonthdayRange, month: Month, year: int,
-                   hour: HourRange, minute: MinuteRange, second: SecondRange,
-                   zone: Timezone = local()): DateTime =
-  ## Create a new ``DateTime`` in the specified timezone.
-  runnableExamples:
-    let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, utc())
-    doAssert $dt1 == "2017-03-30T00:00:00+00:00"
-  initDateTime(monthday, month, year, hour, minute, second, 0, zone)
-
-
 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`.
+  ## 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`.
   runnableExamples:
-    let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc())
-    doAssert $(dt + 1.months) == "2017-04-30T00:00:00+00:00"
+    let dt = dateTime(2017, mMar, 30, 00, 00, 00, 00, utc())
+    doAssert $(dt + 1.months) == "2017-04-30T00:00:00Z"
     # This is correct and happens due to monthday overflow.
-    doAssert $(dt - 1.months) == "2017-03-02T00:00:00+00:00"
+    doAssert $(dt - 1.months) == "2017-03-02T00:00:00Z"
   let (adjDur, absDur) = evaluateInterval(dt, interval)
 
   if adjDur != DurationZero:
-    var zInfo = dt.timezone.zoneInfoFromTz(dt.toAdjTime + adjDur)
+    var zt = dt.timezone.zonedTimeFromAdjTime(dt.toAdjTime + adjDur)
     if absDur != DurationZero:
-      let offsetDur = initDuration(seconds = zInfo.utcOffset)
-      zInfo = dt.timezone.zoneInfoFromUtc(zInfo.adjTime + offsetDur + absDur)
-      result = initDateTime(zInfo, dt.timezone)
+      zt = dt.timezone.zonedTimeFromTime(zt.time + absDur)
+      result = initDateTime(zt, dt.timezone)
     else:
-      result = initDateTime(zInfo, dt.timezone)
+      result = initDateTime(zt, dt.timezone)
   else:
-    var zInfo = dt.timezone.zoneInfoFromUtc(dt.toTime + absDur)
-    result = initDateTime(zInfo, dt.timezone)
+    var zt = dt.timezone.zonedTimeFromTime(dt.toTime + absDur)
+    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:00+00:00"
+    let dt = dateTime(2017, mMar, 30, 00, 00, 00, 00, utc())
+    doAssert $(dt - 5.days) == "2017-03-25T00:00:00Z"
 
   dt + (-interval)
 
-proc `+`*(dt: DateTime, dur: Duration): DateTime =
-  runnableExamples:
-    let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc())
-    let dur = initDuration(hours = 5)
-    doAssert $(dt + dur) == "2017-03-30T05:00:00+00:00"
-
-  (dt.toTime + dur).inZone(dt.timezone)
-
-proc `-`*(dt: DateTime, dur: Duration): DateTime =
-  runnableExamples:
-    let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc())
-    let dur = initDuration(days = 5)
-    doAssert $(dt - dur) == "2017-03-25T00:00:00+00:00"
-
-  (dt.toTime - dur).inZone(dt.timezone)
-
-proc `-`*(dt1, dt2: DateTime): Duration =
-  ## Compute the duration between ``dt1`` and ``dt2``.
-  runnableExamples:
-    let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, utc())
-    let dt2 = initDateTime(25, mMar, 2017, 00, 00, 00, utc())
-
-    doAssert dt1 - dt2 == initDuration(days = 5)
-
-  dt1.toTime - dt2.toTime
-
-proc `<`*(a, b: DateTime): bool =
-  ## Returns true iff ``a < b``, that is iff a happened before b.
-  return a.toTime < b.toTime
-
-proc `<=` * (a, b: DateTime): bool =
-  ## Returns true iff ``a <= 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 datetime.
-  return a.toTime == b.toTime
-
-
-proc isStaticInterval(interval: TimeInterval): bool =
-  interval.years == 0 and interval.months == 0 and
-    interval.days == 0 and interval.weeks == 0
-
-proc evaluateStaticInterval(interval: TimeInterval): Duration =
-  assert interval.isStaticInterval
-  initDuration(nanoseconds = interval.nanoseconds,
-    microseconds = interval.microseconds,
-    milliseconds = interval.milliseconds,
-    seconds = interval.seconds,
-    minutes = interval.minutes,
-    hours = interval.hours)
-
-proc between*(startDt, endDt: DateTime): TimeInterval =
-  ## Evaluate difference between two dates in ``TimeInterval`` format, so, it
-  ## will be relative.
-  ##
-  ## **Warning:** It's not recommended to use ``between`` for ``DateTime's`` in
-  ## different ``TimeZone's``.
-  ## ``a + between(a, b) == b`` is only guaranteed when ``a`` and ``b`` are in UTC.
-  runnableExamples:
-    var a = initDateTime(year = 2018, month = Month(3), monthday = 25,
-                     hour = 0, minute = 59, second = 59, nanosecond = 1,
-                     zone = utc()).local
-    var b = initDateTime(year = 2018, month = Month(3), monthday = 25,
-                     hour = 1, minute =  1, second =  1, nanosecond = 0,
-                     zone = utc()).local
-    doAssert between(a, b) == initTimeInterval(
-      nanoseconds=999, milliseconds=999, microseconds=999, seconds=1, minutes=1)
-
-    a = parse("2018-01-09T00:00:00+00:00", "yyyy-MM-dd'T'HH:mm:sszzz", utc())
-    b = parse("2018-01-10T23:00:00-02:00", "yyyy-MM-dd'T'HH:mm:sszzz")
-    doAssert between(a, b) == initTimeInterval(hours=1, days=2)
-    ## Though, here correct answer should be 1 day 25 hours (cause this day in
-    ## this tz is actually 26 hours). That's why operating different TZ is
-    ## discouraged
-
-  var startDt = startDt.utc()
-  var endDt = endDt.utc()
-
-  if endDt == startDt:
-    return initTimeInterval()
-  elif endDt < startDt:
-    return -between(endDt, startDt)
-
-  var coeffs: array[FixedTimeUnit, int64] = unitWeights
-  var timeParts: array[FixedTimeUnit, int]
-  for unit in Nanoseconds..Weeks:
-    timeParts[unit] = 0
-
-  for unit in Seconds..Days:
-    coeffs[unit] = coeffs[unit] div unitWeights[Seconds]
-
-  var startTimepart = initTime(
-    nanosecond = startDt.nanosecond,
-    unix = startDt.hour * coeffs[Hours] + startDt.minute * coeffs[Minutes] +
-    startDt.second
-  )
-  var endTimepart = initTime(
-    nanosecond = endDt.nanosecond,
-    unix = endDt.hour * coeffs[Hours] + endDt.minute * coeffs[Minutes] +
-    endDt.second
-  )
-  # We wand timeParts for Seconds..Hours be positive, so we'll borrow one day
-  if endTimepart < startTimepart:
-    timeParts[Days] = -1
-
-  let diffTime = endTimepart - startTimepart
-  timeParts[Seconds] = diffTime.seconds.int()
-  #Nanoseconds - preliminary count
-  timeParts[Nanoseconds] = diffTime.nanoseconds
-  for unit in countdown(Milliseconds, Microseconds):
-    timeParts[unit] += timeParts[Nanoseconds] div coeffs[unit].int()
-    timeParts[Nanoseconds] -= timeParts[unit] * coeffs[unit].int()
-
-  #Counting Seconds .. Hours - final, Days - preliminary
-  for unit in countdown(Days, Minutes):
-    timeParts[unit] += timeParts[Seconds] div coeffs[unit].int()
-    # Here is accounted the borrowed day
-    timeParts[Seconds] -= timeParts[unit] * coeffs[unit].int()
-
-  # Set Nanoseconds .. Hours in result
-  result.nanoseconds = timeParts[Nanoseconds]
-  result.microseconds = timeParts[Microseconds]
-  result.milliseconds = timeParts[Milliseconds]
-  result.seconds = timeParts[Seconds]
-  result.minutes = timeParts[Minutes]
-  result.hours = timeParts[Hours]
-
-  #Days
-  if endDt.monthday.int + timeParts[Days] < startDt.monthday.int():
-    if endDt.month > 1.Month:
-      endDt.month -= 1.Month
-    else:
-      endDt.month = 12.Month
-      endDt.year -= 1
-    timeParts[Days] += endDt.monthday.int() + getDaysInMonth(
-      endDt.month, endDt.year) - startDt.monthday.int()
-  else:
-    timeParts[Days] += endDt.monthday.int() -
-      startDt.monthday.int()
-
-  result.days = timeParts[Days]
-
-  #Months
-  if endDt.month < startDt.month:
-      result.months = endDt.month.int() + 12 - startDt.month.int()
-      endDt.year -= 1
-  else:
-    result.months = endDt.month.int() -
-      startDt.month.int()
-
-  # Years
-  result.years = endDt.year - startDt.year
-
 proc `+`*(time: Time, interval: TimeInterval): Time =
   ## Adds `interval` to `time`.
   ## If `interval` contains any years, months, weeks or days the operation
@@ -1371,788 +2736,154 @@ proc `-`*(time: Time, interval: TimeInterval): Time =
   else:
     toTime(time.local - interval)
 
-proc `+=`*[T, U: TimesMutableTypes](a: var T, b: U) =
-  ## Modify ``a`` in place by adding ``b``.
-  runnableExamples:
-    var tm = fromUnix(0)
-    tm += initDuration(seconds = 1)
-    doAssert tm == fromUnix(1)
+proc `+=`*(a: var DateTime, b: TimeInterval) =
   a = a + b
 
-proc `-=`*[T, U: TimesMutableTypes](a: var T, b: U) =
-  ## Modify ``a`` in place by subtracting ``b``.
-  runnableExamples:
-    var tm = fromUnix(5)
-    tm -= initDuration(seconds = 5)
-    doAssert tm == fromUnix(0)
+proc `-=`*(a: var DateTime, b: TimeInterval) =
   a = a - b
 
-proc `*=`*[T: TimesMutableTypes, U](a: var T, b: U) =
-  # Mutable type is often multiplied by number
-  runnableExamples:
-    var dur = initDuration(seconds = 1)
-    dur *= 5
-    doAssert dur == initDuration(seconds = 5)
+proc `+=`*(t: var Time, b: TimeInterval) =
+  t = t + b
 
-  a = a * b
+proc `-=`*(t: var Time, b: TimeInterval) =
+  t = t - b
 
-proc formatToken(dt: DateTime, token: string, buf: var string) =
-  ## Helper of the format proc to parse individual tokens.
-  ##
-  ## Pass the found token in the user input string, and the buffer where the
-  ## final string is being built. This has to be a var value because certain
-  ## formatting tokens require modifying the previous characters.
-  case token
-  of "d":
-    buf.add($dt.monthday)
-  of "dd":
-    if dt.monthday < 10:
-      buf.add("0")
-    buf.add($dt.monthday)
-  of "ddd":
-    buf.add(($dt.weekday)[0 .. 2])
-  of "dddd":
-    buf.add($dt.weekday)
-  of "h":
-    if dt.hour == 0: buf.add("12")
-    else: buf.add($(if dt.hour > 12: dt.hour - 12 else: dt.hour))
-  of "hh":
-    if dt.hour == 0:
-      buf.add("12")
-    else:
-      let amerHour = if dt.hour > 12: dt.hour - 12 else: dt.hour
-      if amerHour < 10:
-        buf.add('0')
-      buf.add($amerHour)
-  of "H":
-    buf.add($dt.hour)
-  of "HH":
-    if dt.hour < 10:
-      buf.add('0')
-    buf.add($dt.hour)
-  of "m":
-    buf.add($dt.minute)
-  of "mm":
-    if dt.minute < 10:
-      buf.add('0')
-    buf.add($dt.minute)
-  of "M":
-    buf.add($ord(dt.month))
-  of "MM":
-    if dt.month < mOct:
-      buf.add('0')
-    buf.add($ord(dt.month))
-  of "MMM":
-    buf.add(($dt.month)[0..2])
-  of "MMMM":
-    buf.add($dt.month)
-  of "s":
-    buf.add($dt.second)
-  of "ss":
-    if dt.second < 10:
-      buf.add('0')
-    buf.add($dt.second)
-  of "t":
-    if dt.hour >= 12:
-      buf.add('P')
-    else: buf.add('A')
-  of "tt":
-    if dt.hour >= 12:
-      buf.add("PM")
-    else: buf.add("AM")
-  of "y":
-    var fr = ($dt.year).len()-1
-    if fr < 0: fr = 0
-    buf.add(($dt.year)[fr .. ($dt.year).len()-1])
-  of "yy":
-    var fr = ($dt.year).len()-2
-    if fr < 0: fr = 0
-    var fyear = ($dt.year)[fr .. ($dt.year).len()-1]
-    if fyear.len != 2: fyear = repeat('0', 2-fyear.len()) & fyear
-    buf.add(fyear)
-  of "yyy":
-    var fr = ($dt.year).len()-3
-    if fr < 0: fr = 0
-    var fyear = ($dt.year)[fr .. ($dt.year).len()-1]
-    if fyear.len != 3: fyear = repeat('0', 3-fyear.len()) & fyear
-    buf.add(fyear)
-  of "yyyy":
-    var fr = ($dt.year).len()-4
-    if fr < 0: fr = 0
-    var fyear = ($dt.year)[fr .. ($dt.year).len()-1]
-    if fyear.len != 4: fyear = repeat('0', 4-fyear.len()) & fyear
-    buf.add(fyear)
-  of "yyyyy":
-    var fr = ($dt.year).len()-5
-    if fr < 0: fr = 0
-    var fyear = ($dt.year)[fr .. ($dt.year).len()-1]
-    if fyear.len != 5: fyear = repeat('0', 5-fyear.len()) & fyear
-    buf.add(fyear)
-  of "z":
-    let
-      nonDstTz = dt.utcOffset
-      hours = abs(nonDstTz) div secondsInHour
-    if nonDstTz <= 0: buf.add('+')
-    else: buf.add('-')
-    buf.add($hours)
-  of "zz":
-    let
-      nonDstTz = dt.utcOffset
-      hours = abs(nonDstTz) div secondsInHour
-    if nonDstTz <= 0: buf.add('+')
-    else: buf.add('-')
-    if hours < 10: buf.add('0')
-    buf.add($hours)
-  of "zzz":
-    let
-      nonDstTz = dt.utcOffset
-      hours = abs(nonDstTz) div secondsInHour
-      minutes = (abs(nonDstTz) div secondsInMin) mod minutesInHour
-    if nonDstTz <= 0: buf.add('+')
-    else: buf.add('-')
-    if hours < 10: buf.add('0')
-    buf.add($hours)
-    buf.add(':')
-    if minutes < 10: buf.add('0')
-    buf.add($minutes)
-  of "fff":
-    buf.add(intToStr(convert(Nanoseconds, Milliseconds, dt.nanosecond), 3))
-  of "ffffff":
-    buf.add(intToStr(convert(Nanoseconds, Microseconds, dt.nanosecond), 6))
-  of "fffffffff":
-    buf.add(intToStr(dt.nanosecond, 9))
-  of "":
-    discard
-  else:
-    raise newException(ValueError, "Invalid format string: " & token)
+#
+# Iso week
+#
 
-proc format*(dt: DateTime, f: string): string {.tags: [].}=
-  ## This procedure formats `dt` as specified by `f`. The following format
-  ## specifiers are available:
-  ##
-  ## ============  =================================================================================  ================================================
-  ## Specifier     Description                                                                        Example
-  ## ============  =================================================================================  ================================================
-  ##    d          Numeric value of the day of the month, it will be one or two digits long.          ``1/04/2012 -> 1``, ``21/04/2012 -> 21``
-  ##    dd         Same as above, but always two digits.                                              ``1/04/2012 -> 01``, ``21/04/2012 -> 21``
-  ##    ddd        Three letter string which indicates the day of the week.                           ``Saturday -> Sat``, ``Monday -> Mon``
-  ##    dddd       Full string for the day of the week.                                               ``Saturday -> Saturday``, ``Monday -> Monday``
-  ##    h          The hours in one digit if possible. Ranging from 0-12.                             ``5pm -> 5``, ``2am -> 2``
-  ##    hh         The hours in two digits always. If the hour is one digit 0 is prepended.           ``5pm -> 05``, ``11am -> 11``
-  ##    H          The hours in one digit if possible, randing from 0-24.                             ``5pm -> 17``, ``2am -> 2``
-  ##    HH         The hours in two digits always. 0 is prepended if the hour is one digit.           ``5pm -> 17``, ``2am -> 02``
-  ##    m          The minutes in 1 digit if possible.                                                ``5:30 -> 30``, ``2:01 -> 1``
-  ##    mm         Same as above but always 2 digits, 0 is prepended if the minute is one digit.      ``5:30 -> 30``, ``2:01 -> 01``
-  ##    M          The month in one digit if possible.                                                ``September -> 9``, ``December -> 12``
-  ##    MM         The month in two digits always. 0 is prepended.                                    ``September -> 09``, ``December -> 12``
-  ##    MMM        Abbreviated three-letter form of the month.                                        ``September -> Sep``, ``December -> Dec``
-  ##    MMMM       Full month string, properly capitalized.                                           ``September -> September``
-  ##    s          Seconds as one digit if possible.                                                  ``00:00:06 -> 6``
-  ##    ss         Same as above but always two digits. 0 is prepended.                               ``00:00:06 -> 06``
-  ##    t          ``A`` when time is in the AM. ``P`` when time is in the PM.
-  ##    tt         Same as above, but ``AM`` and ``PM`` instead of ``A`` and ``P`` respectively.
-  ##    y(yyyy)    This displays the year to different digits. You most likely only want 2 or 4 'y's
-  ##    yy         Displays the year to two digits.                                                   ``2012 -> 12``
-  ##    yyyy       Displays the year to four digits.                                                  ``2012 -> 2012``
-  ##    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``
-  ##    fff        Milliseconds display                                                               ``1000000 nanoseconds -> 1``
-  ##    ffffff     Microseconds display                                                               ``1000000 nanoseconds -> 1000``
-  ##    fffffffff  Nanoseconds display                                                                ``1000000 nanoseconds -> 1000000``
-  ## ============  =================================================================================  ================================================
+proc initDateTime*(weekday: WeekDay, isoweek: IsoWeekRange, isoyear: IsoYear,
+                   hour: HourRange, minute: MinuteRange, second: SecondRange,
+                   nanosecond: NanosecondRange,
+                   zone: Timezone = local()): DateTime {.raises: [], tags: [], since: (1, 5).} =
+  ## Create a new `DateTime <#DateTime>`_ from a weekday and an ISO 8601 week number and year
+  ## in the specified timezone.
   ##
-  ## Other strings can be inserted by putting them in ``''``. For example
-  ## ``hh'->'mm`` will give ``01->56``.  The following characters can be
-  ## inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]``
-  ## ``,``. However you don't need to necessarily separate format specifiers, a
-  ## unambiguous format string like ``yyyyMMddhhmmss`` is valid too.
-  runnableExamples:
-    let dt = initDateTime(01, mJan, 2000, 12, 00, 00, 01, utc())
-    doAssert format(dt, "yyyy-MM-dd'T'HH:mm:ss'.'fffffffffzzz") == "2000-01-01T12:00:00.000000001+00:00"
-
-  result = ""
-  var i = 0
-  var currentF = ""
-  while i < f.len:
-    case f[i]
-    of ' ', '-', '/', ':', '\'', '(', ')', '[', ']', ',':
-      formatToken(dt, currentF, result)
-
-      currentF = ""
-
-      if f[i] == '\'':
-        inc(i) # Skip '
-        while i < f.len-1 and f[i] != '\'':
-          result.add(f[i])
-          inc(i)
-      else: result.add(f[i])
-
-    else:
-      # Check if the letter being added matches previous accumulated buffer.
-      if currentF.len == 0 or currentF[high(currentF)] == f[i]:
-        currentF.add(f[i])
-      else:
-        formatToken(dt, currentF, result)
-        dec(i) # Move position back to re-process the character separately.
-        currentF = ""
-
-    inc(i)
-  formatToken(dt, currentF, result)
-
-proc format*(time: Time, f: string, zone: Timezone = local()): string {.tags: [].} =
-  ## Converts a `Time` value to a string representation. It will use format from
-  ## ``format(dt: DateTime, f: string)``.
+  ## .. warning:: The ISO week-based year can correspond to the following or previous year from 29 December to January 3.
   runnableExamples:
-    var dt = initDateTime(01, mJan, 1970, 00, 00, 00, utc())
-    var tm = dt.toTime()
-    doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", utc()) == "1970-01-01T00:00:00"
-  time.inZone(zone).format(f)
+    assert initDateTime(21, mApr, 2018, 00, 00, 00) == initDateTime(dSat, 16, 2018.IsoYear, 00, 00, 00)
+    assert initDateTime(30, mDec, 2019, 00, 00, 00) == initDateTime(dMon, 01, 2020.IsoYear, 00, 00, 00)
+    assert initDateTime(13, mSep, 2020, 00, 00, 00) == initDateTime(dSun, 37, 2020.IsoYear, 00, 00, 00)
+    assert initDateTime(2, mJan, 2021, 00, 00, 00) == initDateTime(dSat, 53, 2020.IsoYear, 00, 00, 00)
 
-proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} =
-  ## Converts a `DateTime` object to a string representation.
-  ## It uses the format ``yyyy-MM-dd'T'HH-mm-sszzz``.
-  runnableExamples:
-    let dt = initDateTime(01, mJan, 2000, 12, 00, 00, utc())
-    doAssert $dt == "2000-01-01T12:00:00+00:00"
-  try:
-    result = format(dt, "yyyy-MM-dd'T'HH:mm:sszzz") # todo: optimize this
-  except ValueError: assert false # cannot happen because format string is valid
+  # source https://webspace.science.uu.nl/~gent0113/calendar/isocalendar.htm
+  let d = isoweek * 7 + weekday.int - initDateTime(4, mJan, isoyear.int, 00, 00, 00, zone).weekday.int - 4
+  initDateTime(1, mJan, isoyear.int, hour, minute, second, nanosecond, zone) + initTimeInterval(days=d)
 
-proc `$`*(time: Time): string {.tags: [], raises: [], benign.} =
-  ## 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())
-    let tm = dt.toTime()
-    doAssert $tm == "1970-01-01T00:00:00" & format(dt, "zzz")
-  $time.local
+proc initDateTime*(weekday: WeekDay, isoweek: IsoWeekRange, isoyear: IsoYear,
+                   hour: HourRange, minute: MinuteRange, second: SecondRange,
+                   zone: Timezone = local()): DateTime {.raises: [], tags: [], since: (1, 5).} =
+  initDateTime(weekday, isoweek, isoyear, hour, minute, second, 0, zone)
 
-{.pop.}
-
-proc parseToken(dt: var DateTime; token, value: string; j: var int) =
-  ## Helper of the parse proc to parse individual tokens.
-
-  # Overwrite system.`[]` to raise a ValueError on index out of bounds.
-  proc `[]`[T, U](s: string, x: HSlice[T, U]): string =
-    if x.a >= s.len or x.b >= s.len:
-      raise newException(ValueError, "Value is missing required tokens, got: " &
-                         s)
-    return system.`[]`(s, x)
-
-  var sv: int
-  case token
-  of "d":
-    var pd = parseInt(value[j..j+1], sv)
-    dt.monthday = sv
-    j += pd
-  of "dd":
-    dt.monthday = value[j..j+1].parseInt()
-    j += 2
-  of "ddd":
-    case value[j..j+2].toLowerAscii()
-    of "sun": dt.weekday = dSun
-    of "mon": dt.weekday = dMon
-    of "tue": dt.weekday = dTue
-    of "wed": dt.weekday = dWed
-    of "thu": dt.weekday = dThu
-    of "fri": dt.weekday = dFri
-    of "sat": dt.weekday = dSat
-    else:
-      raise newException(ValueError,
-        "Couldn't parse day of week (ddd), got: " & value[j..j+2])
-    j += 3
-  of "dddd":
-    if value.len >= j+6 and value[j..j+5].cmpIgnoreCase("sunday") == 0:
-      dt.weekday = dSun
-      j += 6
-    elif value.len >= j+6 and value[j..j+5].cmpIgnoreCase("monday") == 0:
-      dt.weekday = dMon
-      j += 6
-    elif value.len >= j+7 and value[j..j+6].cmpIgnoreCase("tuesday") == 0:
-      dt.weekday = dTue
-      j += 7
-    elif value.len >= j+9 and value[j..j+8].cmpIgnoreCase("wednesday") == 0:
-      dt.weekday = dWed
-      j += 9
-    elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("thursday") == 0:
-      dt.weekday = dThu
-      j += 8
-    elif value.len >= j+6 and value[j..j+5].cmpIgnoreCase("friday") == 0:
-      dt.weekday = dFri
-      j += 6
-    elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("saturday") == 0:
-      dt.weekday = dSat
-      j += 8
-    else:
-      raise newException(ValueError,
-        "Couldn't parse day of week (dddd), got: " & value)
-  of "h", "H":
-    var pd = parseInt(value[j..j+1], sv)
-    dt.hour = sv
-    j += pd
-  of "hh", "HH":
-    dt.hour = value[j..j+1].parseInt()
-    j += 2
-  of "m":
-    var pd = parseInt(value[j..j+1], sv)
-    dt.minute = sv
-    j += pd
-  of "mm":
-    dt.minute = value[j..j+1].parseInt()
-    j += 2
-  of "M":
-    var pd = parseInt(value[j..j+1], sv)
-    dt.month = sv.Month
-    j += pd
-  of "MM":
-    var month = value[j..j+1].parseInt()
-    j += 2
-    dt.month = month.Month
-  of "MMM":
-    case value[j..j+2].toLowerAscii():
-    of "jan": dt.month = mJan
-    of "feb": dt.month = mFeb
-    of "mar": dt.month = mMar
-    of "apr": dt.month = mApr
-    of "may": dt.month = mMay
-    of "jun": dt.month = mJun
-    of "jul": dt.month = mJul
-    of "aug": dt.month = mAug
-    of "sep": dt.month = mSep
-    of "oct": dt.month = mOct
-    of "nov": dt.month = mNov
-    of "dec": dt.month = mDec
-    else:
-      raise newException(ValueError,
-        "Couldn't parse month (MMM), got: " & value)
-    j += 3
-  of "MMMM":
-    if value.len >= j+7 and value[j..j+6].cmpIgnoreCase("january") == 0:
-      dt.month = mJan
-      j += 7
-    elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("february") == 0:
-      dt.month = mFeb
-      j += 8
-    elif value.len >= j+5 and value[j..j+4].cmpIgnoreCase("march") == 0:
-      dt.month = mMar
-      j += 5
-    elif value.len >= j+5 and value[j..j+4].cmpIgnoreCase("april") == 0:
-      dt.month = mApr
-      j += 5
-    elif value.len >= j+3 and value[j..j+2].cmpIgnoreCase("may") == 0:
-      dt.month = mMay
-      j += 3
-    elif value.len >= j+4 and value[j..j+3].cmpIgnoreCase("june") == 0:
-      dt.month = mJun
-      j += 4
-    elif value.len >= j+4 and value[j..j+3].cmpIgnoreCase("july") == 0:
-      dt.month = mJul
-      j += 4
-    elif value.len >= j+6 and value[j..j+5].cmpIgnoreCase("august") == 0:
-      dt.month = mAug
-      j += 6
-    elif value.len >= j+9 and value[j..j+8].cmpIgnoreCase("september") == 0:
-      dt.month = mSep
-      j += 9
-    elif value.len >= j+7 and value[j..j+6].cmpIgnoreCase("october") == 0:
-      dt.month = mOct
-      j += 7
-    elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("november") == 0:
-      dt.month = mNov
-      j += 8
-    elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("december") == 0:
-      dt.month = mDec
-      j += 8
-    else:
-      raise newException(ValueError,
-        "Couldn't parse month (MMMM), got: " & value)
-  of "s":
-    var pd = parseInt(value[j..j+1], sv)
-    dt.second = sv
-    j += pd
-  of "ss":
-    dt.second = value[j..j+1].parseInt()
-    j += 2
-  of "t":
-    if value[j] == 'A' and dt.hour == 12:
-      dt.hour = 0
-    elif value[j] == 'P' and dt.hour > 0 and dt.hour < 12:
-      dt.hour += 12
-    j += 1
-  of "tt":
-    if value[j..j+1] == "AM" and dt.hour == 12:
-      dt.hour = 0
-    elif value[j..j+1] == "PM" and dt.hour > 0 and dt.hour < 12:
-      dt.hour += 12
-    j += 2
-  of "yy":
-    # Assumes current century
-    var year = value[j..j+1].parseInt()
-    var thisCen = now().year div 100
-    dt.year = thisCen*100 + year
-    j += 2
-  of "yyyy":
-    dt.year = value[j..j+3].parseInt()
-    j += 4
-  of "z":
-    dt.isDst = false
-    let ch = if j < value.len: value[j] else: '\0'
-    if ch == '+':
-      dt.utcOffset = 0 - parseInt($value[j+1]) * secondsInHour
-    elif ch == '-':
-      dt.utcOffset = parseInt($value[j+1]) * secondsInHour
-    elif ch == 'Z':
-      dt.utcOffset = 0
-      j += 1
-      return
-    else:
-      raise newException(ValueError,
-        "Couldn't parse timezone offset (z), got: " & ch)
-    j += 2
-  of "zz":
-    dt.isDst = false
-    let ch = if j < value.len: value[j] else: '\0'
-    if ch == '+':
-      dt.utcOffset = 0 - value[j+1..j+2].parseInt() * secondsInHour
-    elif ch == '-':
-      dt.utcOffset = value[j+1..j+2].parseInt() * secondsInHour
-    elif ch == 'Z':
-      dt.utcOffset = 0
-      j += 1
-      return
-    else:
-      raise newException(ValueError,
-        "Couldn't parse timezone offset (zz), got: " & ch)
-    j += 3
-  of "zzz":
-    dt.isDst = false
-    var factor = 0
-    let ch = if j < value.len: value[j] else: '\0'
-    if ch == '+': factor = -1
-    elif ch == '-': factor = 1
-    elif ch == 'Z':
-      dt.utcOffset = 0
-      j += 1
-      return
-    else:
-      raise newException(ValueError,
-        "Couldn't parse timezone offset (zzz), got: " & ch)
-    dt.utcOffset = factor * value[j+1..j+2].parseInt() * secondsInHour
-    j += 4
-    dt.utcOffset += factor * value[j..j+1].parseInt() * 60
-    j += 2
-  of "fff", "ffffff", "fffffffff":
-    var numStr = ""
-    let n = parseWhile(value[j..len(value) - 1], numStr, {'0'..'9'})
-    dt.nanosecond = parseInt(numStr) * (10 ^ (9 - n))
-    j += n
-  else:
-    # Ignore the token and move forward in the value string by the same length
-    j += token.len
+#
+# Other
+#
 
-proc parse*(value, layout: string, zone: Timezone = local()): DateTime =
-  ## This procedure parses a date/time string using the standard format
-  ## identifiers as listed below. The procedure defaults information not provided
-  ## in the format string from the running program (month, year, etc).
-  ##
-  ## The return value will always be in the `zone` timezone. If no UTC offset was
-  ## parsed, then the input will be assumed to be specified in the `zone` timezone
-  ## already, so no timezone conversion will be done in that case.
+proc epochTime*(): float {.tags: [TimeEffect].} =
+  ## Gets time after the UNIX epoch (1970) in seconds. It is a float
+  ## because sub-second resolution is likely to be supported (depending
+  ## on the hardware/OS).
   ##
-  ## =======================  =================================================================================  ================================================
-  ## Specifier                Description                                                                        Example
-  ## =======================  =================================================================================  ================================================
-  ##    d                     Numeric value of the day of the month, it will be one or two digits long.          ``1/04/2012 -> 1``, ``21/04/2012 -> 21``
-  ##    dd                    Same as above, but always two digits.                                              ``1/04/2012 -> 01``, ``21/04/2012 -> 21``
-  ##    ddd                   Three letter string which indicates the day of the week.                           ``Saturday -> Sat``, ``Monday -> Mon``
-  ##    dddd                  Full string for the day of the week.                                               ``Saturday -> Saturday``, ``Monday -> Monday``
-  ##    h                     The hours in one digit if possible. Ranging from 0-12.                             ``5pm -> 5``, ``2am -> 2``
-  ##    hh                    The hours in two digits always. If the hour is one digit 0 is prepended.           ``5pm -> 05``, ``11am -> 11``
-  ##    H                     The hours in one digit if possible, randing from 0-24.                             ``5pm -> 17``, ``2am -> 2``
-  ##    HH                    The hours in two digits always. 0 is prepended if the hour is one digit.           ``5pm -> 17``, ``2am -> 02``
-  ##    m                     The minutes in 1 digit if possible.                                                ``5:30 -> 30``, ``2:01 -> 1``
-  ##    mm                    Same as above but always 2 digits, 0 is prepended if the minute is one digit.      ``5:30 -> 30``, ``2:01 -> 01``
-  ##    M                     The month in one digit if possible.                                                ``September -> 9``, ``December -> 12``
-  ##    MM                    The month in two digits always. 0 is prepended.                                    ``September -> 09``, ``December -> 12``
-  ##    MMM                   Abbreviated three-letter form of the month.                                        ``September -> Sep``, ``December -> Dec``
-  ##    MMMM                  Full month string, properly capitalized.                                           ``September -> September``
-  ##    s                     Seconds as one digit if possible.                                                  ``00:00:06 -> 6``
-  ##    ss                    Same as above but always two digits. 0 is prepended.                               ``00:00:06 -> 06``
-  ##    t                     ``A`` when time is in the AM. ``P`` when time is in the PM.
-  ##    tt                    Same as above, but ``AM`` and ``PM`` instead of ``A`` and ``P`` respectively.
-  ##    yy                    Displays the year to two digits.                                                   ``2012 -> 12``
-  ##    yyyy                  Displays the year to four digits.                                                  ``2012 -> 2012``
-  ##    z                     Displays the timezone offset from UTC. ``Z`` is parsed as ``+0``                   ``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``
-  ##    fff/ffffff/fffffffff  for consistency with format - nanoseconds                                          ``1 -> 1 nanosecond``
-  ## =======================  =================================================================================  ================================================
+  ## `getTime` should generally be preferred over this proc.
   ##
-  ## Other strings can be inserted by putting them in ``''``. For example
-  ## ``hh'->'mm`` will give ``01->56``.  The following characters can be
-  ## inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]``
-  ## ``,``. However you don't need to necessarily separate format specifiers, a
-  ## unambiguous format string like ``yyyyMMddhhmmss`` is valid too.
-  runnableExamples:
-    let tStr = "1970-01-01T00:00:00.0+00:00"
-    doAssert parse(tStr, "yyyy-MM-dd'T'HH:mm:ss.fffzzz") == fromUnix(0).utc
-
-  var i = 0 # pointer for format string
-  var j = 0 # pointer for value string
-  var token = ""
-  # Assumes current day of month, month and year, but time is reset to 00:00:00. Weekday will be reset after parsing.
-  var dt = now()
-  dt.hour = 0
-  dt.minute = 0
-  dt.second = 0
-  dt.nanosecond = 0
-  dt.isDst = true # using this is flag for checking whether a timezone has \
-      # been read (because DST is always false when a tz is parsed)
-  while i < layout.len:
-    case layout[i]
-    of ' ', '-', '/', ':', '\'', '(', ')', '[', ']', ',':
-      if token.len > 0:
-        parseToken(dt, token, value, j)
-      # Reset token
-      token = ""
-      # Skip separator and everything between single quotes
-      # These are literals in both the layout and the value string
-      if layout[i] == '\'':
-        inc(i)
-        while i < layout.len-1 and layout[i] != '\'':
-          inc(i)
-          inc(j)
-        inc(i)
-      else:
-        inc(i)
-        inc(j)
-    else:
-      # Check if the letter being added matches previous accumulated buffer.
-      if token.len == 0 or token[high(token)] == layout[i]:
-        token.add(layout[i])
-        inc(i)
-      else:
-        parseToken(dt, token, value, j)
-        token = ""
-
-  if i >= layout.len and token.len > 0:
-    parseToken(dt, token, value, j)
-  if dt.isDst:
-    # No timezone parsed - assume timezone is `zone`
-    result = initDateTime(zone.zoneInfoFromTz(dt.toAdjTime), zone)
+  ## .. warning:: Unsuitable for benchmarking (but still better than `now`),
+  ##    use `monotimes.getMonoTime` or `cpuTime` instead, depending on the use case.
+  when defined(js):
+    result = newDate().getTime() / 1000
+  elif defined(macosx):
+    var a {.noinit.}: Timeval
+    gettimeofday(a)
+    result = toBiggestFloat(a.tv_sec.int64) + toBiggestFloat(
+        a.tv_usec)*0.00_0001
+  elif defined(posix):
+    var ts {.noinit.}: Timespec
+    discard clock_gettime(CLOCK_REALTIME, ts)
+    result = toBiggestFloat(ts.tv_sec.int64) +
+      toBiggestFloat(ts.tv_nsec.int64) / 1_000_000_000
+  elif defined(windows):
+    var f {.noinit.}: winlean.FILETIME
+    getSystemTimeAsFileTime(f)
+    var i64 = rdFileTime(f) - epochDiff
+    var secs = i64 div rateDiff
+    var subsecs = i64 mod rateDiff
+    result = toFloat(int(secs)) + toFloat(int(subsecs)) * 0.0000001
   else:
-    # Otherwise convert to `zone`
-    result = dt.toTime.inZone(zone)
-
-proc parseTime*(value, layout: string, zone: Timezone): Time =
-  ## Simple wrapper for parsing string to time
-  runnableExamples:
-    let tStr = "1970-01-01T00:00:00+00:00"
-    doAssert parseTime(tStr, "yyyy-MM-dd'T'HH:mm:sszzz", local()) == fromUnix(0)
-  parse(value, layout, zone).toTime()
-
-proc countLeapYears*(yearSpan: int): int =
-  ## Returns the number of leap years spanned by a given number of years.
-  ##
-  ## **Note:** For leap years, start date is assumed to be 1 AD.
-  ## counts the number of leap years up to January 1st of a given year.
-  ## Keep in mind that if specified year is a leap year, the leap day
-  ## has not happened before January 1st of that year.
-  (yearSpan - 1) div 4 - (yearSpan - 1) div 100 + (yearSpan - 1) div 400
-
-proc countDays*(yearSpan: int): int =
-  ## Returns the number of days spanned by a given number of years.
-  (yearSpan - 1) * 365 + countLeapYears(yearSpan)
-
-proc countYears*(daySpan: int): int =
-  ## Returns the number of years spanned by a given number of days.
-  ((daySpan - countLeapYears(daySpan div 365)) div 365)
-
-proc countYearsAndDays*(daySpan: int): tuple[years: int, days: int] =
-  ## Returns the number of years spanned by a given number of days and the
-  ## remainder as days.
-  let days = daySpan - countLeapYears(daySpan div 365)
-  result.years = days div 365
-  result.days = days mod 365
-
-proc toTimeInterval*(time: Time): TimeInterval =
-  ## Converts a Time to a TimeInterval.
-  ##
-  ## To be used when diffing times. Consider using `between` instead.
-  runnableExamples:
-    let a = fromUnix(10)
-    let b = fromUnix(1_500_000_000)
-    let ti = b.toTimeInterval() - a.toTimeInterval()
-    doAssert a + ti == b
-  var dt = time.local
-  initTimeInterval(dt.nanosecond, 0, 0, dt.second, dt.minute, dt.hour,
-    dt.monthday, 0, dt.month.ord - 1, dt.year)
+    {.error: "unknown OS".}
 
-when not defined(JS):
+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], used, sideEffect.}
 
   var
-    clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int
-
-  when not defined(useNimRtl):
-    proc cpuTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].} =
-      ## gets time spent that the CPU spent to run the current process in
-      ## seconds. This may be more useful for benchmarking than ``epochTime``.
-      ## However, it may measure the real time instead (depending on the OS).
-      ## The value of the result has no meaning.
-      ## To generate useful timing values, take the difference between
-      ## the results of two ``cpuTime`` calls:
-      runnableExamples:
-        var t0 = cpuTime()
-        # some useless work here (calculate fibonacci)
-        var fib = @[0, 1, 1]
-        for i in 1..10:
-          fib.add(fib[^1] + fib[^2])
-        echo "CPU time [s] ", cpuTime() - t0
-        echo "Fib is [s] ", fib
+    clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl, used.}: int
+
+  proc cpuTime*(): float {.tags: [TimeEffect].} =
+    ## Gets time spent that the CPU spent to run the current process in
+    ## seconds. This may be more useful for benchmarking than `epochTime`.
+    ## However, it may measure the real time instead (depending on the OS).
+    ## The value of the result has no meaning.
+    ## To generate useful timing values, take the difference between
+    ## the results of two `cpuTime` calls:
+    runnableExamples:
+      var t0 = cpuTime()
+      # some useless work here (calculate fibonacci)
+      var fib = @[0, 1, 1]
+      for i in 1..10:
+        fib.add(fib[^1] + fib[^2])
+      echo "CPU time [s] ", cpuTime() - t0
+      echo "Fib is [s] ", fib
+    ## When the flag `--benchmarkVM` is passed to the compiler, this proc is
+    ## also available at compile time
+    when defined(posix) and not defined(osx) and declared(CLOCK_THREAD_CPUTIME_ID):
+      # 'clocksPerSec' is a compile-time constant, possibly a
+      # rather awful one, so use clock_gettime instead
+      var ts: Timespec
+      discard clock_gettime(CLOCK_THREAD_CPUTIME_ID, ts)
+      result = toFloat(ts.tv_sec.int) +
+        toFloat(ts.tv_nsec.int) / 1_000_000_000
+    else:
       result = toFloat(int(getClock())) / toFloat(clocksPerSec)
 
-    proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].} =
-      ## gets time after the UNIX epoch (1970) in seconds. It is a float
-      ## because sub-second resolution is likely to be supported (depending
-      ## on the hardware/OS).
-      ##
-      ## ``getTime`` should generally be prefered over this proc.
-      when defined(posix):
-        var a: Timeval
-        gettimeofday(a)
-        result = toBiggestFloat(a.tv_sec.int64) + toFloat(a.tv_usec)*0.00_0001
-      elif defined(windows):
-        var f: winlean.FILETIME
-        getSystemTimeAsFileTime(f)
-        var i64 = rdFileTime(f) - epochDiff
-        var secs = i64 div rateDiff
-        var subsecs = i64 mod rateDiff
-        result = toFloat(int(secs)) + toFloat(int(subsecs)) * 0.0000001
-      else:
-        {.error: "unknown OS".}
 
-when defined(JS):
-  proc epochTime*(): float {.tags: [TimeEffect].} =
-    newDate().getTime() / 1000
+#
+# Deprecations
+#
 
-# Deprecated procs
+proc `nanosecond=`*(dt: var DateTime, value: NanosecondRange) {.deprecated: "Deprecated since v1.3.1".} =
+  dt.nanosecond = value
 
-when not defined(JS):
-  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 `second=`*(dt: var DateTime, value: SecondRange) {.deprecated: "Deprecated since v1.3.1".} =
+  dt.second = value
 
-  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 `minute=`*(dt: var DateTime, value: MinuteRange) {.deprecated: "Deprecated since v1.3.1".} =
+  dt.minute = value
 
-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 `hour=`*(dt: var DateTime, value: HourRange) {.deprecated: "Deprecated since v1.3.1".} =
+  dt.hour = value
 
-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
-  initTime(since1970.int64, nanos)
+proc `monthdayZero=`*(dt: var DateTime, value: int) {.deprecated: "Deprecated since v1.3.1".} =
+  dt.monthdayZero = value
 
-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 `monthZero=`*(dt: var DateTime, value: int) {.deprecated: "Deprecated since v1.3.1".} =
+  dt.monthZero = value
 
-proc toSeconds*(time: Time): float {.tags: [], raises: [], benign, deprecated.} =
-  ## Returns the time in seconds since the unix epoch.
-  ##
-  ## **Deprecated since v0.18.0:** use ``fromUnix`` instead
-  time.seconds.float + time.nanosecond / convert(Seconds, Nanoseconds, 1)
+proc `year=`*(dt: var DateTime, value: int) {.deprecated: "Deprecated since v1.3.1".} =
+  dt.year = value
 
-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 `weekday=`*(dt: var DateTime, value: WeekDay) {.deprecated: "Deprecated since v1.3.1".} =
+  dt.weekday = value
 
-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 `yearday=`*(dt: var DateTime, value: YeardayRange) {.deprecated: "Deprecated since v1.3.1".} =
+  dt.yearday = value
 
-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
-  ## utc offset (including DST).
-  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)
-  else:
-    return timezone
+proc `isDst=`*(dt: var DateTime, value: bool) {.deprecated: "Deprecated since v1.3.1".} =
+  dt.isDst = value
 
-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.} =
-    ## get the milliseconds from the start of the program.
-    ## **Deprecated since v0.8.10:** use ``epochTime`` or ``cpuTime`` instead.
-    let dur = getTime() - start
-    result = (convert(Seconds, Milliseconds, dur.seconds) +
-      convert(Nanoseconds, Milliseconds, dur.nanosecond)).int
-else:
-  proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} =
-    when defined(macosx):
-      result = toInt(toFloat(int(getClock())) / (toFloat(clocksPerSec) / 1000.0))
-    else:
-      result = int(getClock()) div (clocksPerSec div 1000)
+proc `timezone=`*(dt: var DateTime, value: Timezone) {.deprecated: "Deprecated since v1.3.1".} =
+  dt.timezone = value
 
-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.} =
-  ## **Deprecated since v0.18.0:** use
-  ## ``getDayOfWeek(monthday: MonthdayRange; month: Month; year: int)`` instead.
-  getDayOfWeek(day, month.Month, year)
-
-proc getDayOfWeekJulian*(day, month, year: int): WeekDay {.deprecated.} =
-  ## Returns the day of the week enum from day, month and year,
-  ## according to the Julian calendar.
-  ## **Deprecated since v0.18.0:**
-  # Day & month start from one.
-  let
-    a = (14 - month) div 12
-    y = year - a
-    m = month + (12*a) - 2
-    d = (5 + day + y + (y div 4) + (31*m) div 12) mod 7
-  result = d.WeekDay
+proc `utcOffset=`*(dt: var DateTime, value: int) {.deprecated: "Deprecated since v1.3.1".} =
+  dt.utcOffset = value
diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim
index 2047abda4..78af84fdd 100644
--- a/lib/pure/typetraits.nim
+++ b/lib/pure/typetraits.nim
@@ -8,60 +8,369 @@
 #
 
 ## This module defines compile-time reflection procs for
-## working with types
+## working with types.
+##
+## Unstable API.
 
-proc name*(t: typedesc): string {.magic: "TypeTrait".}
-  ## Returns the name of the given type.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##
-  ##   import typetraits
+import std/private/since
+export system.`$` # for backward compatibility
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
+type HoleyEnum* = (not Ordinal) and enum ## Enum with holes.
+type OrdinalEnum* = Ordinal and enum ## Enum without holes.
+
+runnableExamples:
+  type A = enum a0 = 2, a1 = 4, a2
+  type B = enum b0 = 2, b1, b2
+  assert A is enum
+  assert A is HoleyEnum
+  assert A isnot OrdinalEnum
+  assert B isnot HoleyEnum
+  assert B is OrdinalEnum
+  assert int isnot HoleyEnum
+  type C[T] = enum h0 = 2, h1 = 4
+  assert C[float] is HoleyEnum
+
+proc name*(t: typedesc): string {.magic: "TypeTrait".} =
+  ## Returns the name of `t`.
   ##
-  ##   proc `$`*(T: typedesc): string = name(T)
+  ## Alias for `system.\`$\`(t) <dollars.html#$,typedesc>`_ since Nim v0.20.
+  runnableExamples:
+    doAssert name(int) == "int"
+    doAssert name(seq[string]) == "seq[string]"
+
+proc arity*(t: typedesc): int {.magic: "TypeTrait".} =
+  ## Returns the arity of `t`. This is the number of "type"
+  ## components or the number of generic parameters a given type `t` has.
+  runnableExamples:
+    doAssert arity(int) == 0
+    doAssert arity(seq[string]) == 1
+    doAssert arity(array[3, int]) == 2
+    doAssert arity((int, int, float, string)) == 4
+
+proc genericHead*(t: typedesc): typedesc {.magic: "TypeTrait".} =
+  ## Accepts an instantiated generic type and returns its
+  ## uninstantiated form.
+  ## A compile-time error will be produced if the supplied type
+  ## is not generic.
   ##
-  ##   template test(x): typed =
-  ##     echo "type: ", type(x), ", value: ", x
+  ## **See also:**
+  ## * `stripGenericParams proc <#stripGenericParams,typedesc>`_
+  runnableExamples:
+    type
+      Foo[T] = object
+      FooInst = Foo[int]
+      Foo2 = genericHead(FooInst)
+
+    doAssert Foo2 is Foo and Foo is Foo2
+    doAssert genericHead(Foo[seq[string]]) is Foo
+    doAssert not compiles(genericHead(int))
+
+    type Generic = concept f
+      type _ = genericHead(typeof(f))
+
+    proc bar(a: Generic): typeof(a) = a
+
+    doAssert bar(Foo[string].default) == Foo[string]()
+    doAssert not compiles bar(string.default)
+
+    when false: # these don't work yet
+      doAssert genericHead(Foo[int])[float] is Foo[float]
+      doAssert seq[int].genericHead is seq
+
+proc stripGenericParams*(t: typedesc): typedesc {.magic: "TypeTrait".} =
+  ## This trait is similar to `genericHead <#genericHead,typedesc>`_, but
+  ## instead of producing an error for non-generic types, it will just return
+  ## them unmodified.
+  runnableExamples:
+    type Foo[T] = object
+
+    doAssert stripGenericParams(Foo[string]) is Foo
+    doAssert stripGenericParams(int) is int
+
+proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".}
+  ## Returns true if `t` is safe to use for `copyMem`:idx:.
   ##
-  ##   test 42
-  ##   # --> type: int, value: 42
-  ##   test "Foo"
-  ##   # --> type: string, value: Foo
-  ##   test(@['A','B'])
-  ##   # --> type: seq[char], value: @[A, B]
+  ## Other languages name a type like these `blob`:idx:.
 
-proc `$`*(t: typedesc): string =
-  ## An alias for `name`.
-  name(t)
+proc hasDefaultValue*(t: typedesc): bool {.magic: "TypeTrait".} =
+  ## Returns true if `t` has a valid default value.
+  runnableExamples:
+    {.experimental: "strictNotNil".}
+    type
+      NilableObject = ref object
+        a: int
+      Object = NilableObject not nil
+      RequiresInit[T] = object
+        a {.requiresInit.}: T
 
-proc arity*(t: typedesc): int {.magic: "TypeTrait".}
-  ## Returns the arity of the given type
+    assert hasDefaultValue(NilableObject)
+    assert not hasDefaultValue(Object)
+    assert hasDefaultValue(string)
+    assert not hasDefaultValue(var string)
+    assert not hasDefaultValue(RequiresInit[int])
 
-proc genericHead*(t: typedesc): typedesc {.magic: "TypeTrait".}
-  ## Accepts an instantiated generic type and returns its
-  ## uninstantiated form.
+proc isNamedTuple*(T: typedesc): bool {.magic: "TypeTrait".} =
+  ## Returns true for named tuples, false for any other type.
+  runnableExamples:
+    doAssert not isNamedTuple(int)
+    doAssert not isNamedTuple((string, int))
+    doAssert isNamedTuple(tuple[name: string, age: int])
+
+template pointerBase*[T](_: typedesc[ptr T | ref T]): typedesc =
+  ## Returns `T` for `ref T | ptr T`.
+  runnableExamples:
+    assert (ref int).pointerBase is int
+    type A = ptr seq[float]
+    assert A.pointerBase is seq[float]
+    assert (ref A).pointerBase is A # not seq[float]
+    assert (var s = "abc"; s[0].addr).typeof.pointerBase is char
+  T
+
+proc rangeBase*(T: typedesc[range]): typedesc {.magic: "TypeTrait".} =
+  ## Returns the base type for range types, or the type itself otherwise.
   ##
-  ## For example:
-  ##   seq[int].genericHead will be just seq
-  ##   seq[int].genericHead[float] will be seq[float]
+  ## **See also:**
+  ## * `rangeBase template <#rangeBase.t,T>`_
+  runnableExamples:
+    type MyRange = range[0..5]
+    type MyEnum = enum a, b, c
+    type MyEnumRange = range[b..c]
+    doAssert rangeBase(MyRange) is int
+    doAssert rangeBase(MyEnumRange) is MyEnum
+    doAssert rangeBase(range['a'..'z']) is char
+
+template rangeBase*[T: range](a: T): untyped =
+  ## Overload of `rangeBase <#rangeBase,typedesc,static[bool]>`_ for values.
+  runnableExamples:
+    type MyRange = range[0..5]
+    type MyEnum = enum a, b, c
+    type MyEnumRange = range[b..c]
+    let x = MyRange(3)
+    doAssert rangeBase(x) is int
+    doAssert $typeof(rangeBase(x)) == "int"
+    doAssert rangeBase(x) == 3
+    let y: set[MyEnumRange] = {c}
+    for e in y:
+      doAssert rangeBase(e) is MyEnum
+      doAssert $typeof(rangeBase(e)) == "MyEnum"
+      doAssert rangeBase(e) == c
+    let z: seq[range['a'..'z']] = @['c']
+    doAssert rangeBase(z[0]) is char
+    doAssert $typeof(rangeBase(z[0])) == "char"
+    doAssert rangeBase(z[0]) == 'c'
+  rangeBase(typeof(T))(a)
+
+proc distinctBase*(T: typedesc, recursive: static bool = true): typedesc {.magic: "TypeTrait".} =
+  ## Returns the base type for distinct types, or the type itself otherwise.
+  ## If `recursive` is false, only the immediate distinct base will be returned.
   ##
-  ## A compile-time error will be produced if the supplied type
-  ## is not generic
+  ## **See also:**
+  ## * `distinctBase template <#distinctBase.t,T,static[bool]>`_
+  runnableExamples:
+    type MyInt = distinct int
+    type MyOtherInt = distinct MyInt
+    doAssert distinctBase(MyInt) is int
+    doAssert distinctBase(MyOtherInt) is int
+    doAssert distinctBase(MyOtherInt, false) is MyInt
+    doAssert distinctBase(int) is int
 
-proc stripGenericParams*(t: typedesc): typedesc {.magic: "TypeTrait".}
-  ## This trait is similar to `genericHead`, but instead of producing
-  ## error for non-generic types, it will just return them unmodified
+since (1, 1):
+  template distinctBase*[T](a: T, recursive: static bool = true): untyped =
+    ## Overload of `distinctBase <#distinctBase,typedesc,static[bool]>`_ for values.
+    runnableExamples:
+      type MyInt = distinct int
+      type MyOtherInt = distinct MyInt
+      doAssert 12.MyInt.distinctBase == 12
+      doAssert 12.MyOtherInt.distinctBase == 12
+      doAssert 12.MyOtherInt.distinctBase(false) is MyInt
+      doAssert 12.distinctBase == 12
+    when T is distinct:
+      distinctBase(typeof(a), recursive)(a)
+    else: # avoids hint ConvFromXtoItselfNotNeeded
+      a
 
-proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".}
-  ## This trait returns true iff the type ``t`` is safe to use for
-  ## `copyMem`:idx:. Other languages name a type like these `blob`:idx:.
+  proc tupleLen*(T: typedesc[tuple]): int {.magic: "TypeTrait".} =
+    ## Returns the number of elements of the tuple type `T`.
+    ##
+    ## **See also:**
+    ## * `tupleLen template <#tupleLen.t>`_
+    runnableExamples:
+      doAssert tupleLen((int, int, float, string)) == 4
+      doAssert tupleLen(tuple[name: string, age: int]) == 2
+
+  template tupleLen*(t: tuple): int =
+    ## Returns the number of elements of the tuple `t`.
+    ##
+    ## **See also:**
+    ## * `tupleLen proc <#tupleLen,typedesc>`_
+    runnableExamples:
+      doAssert tupleLen((1, 2)) == 2
+
+    tupleLen(typeof(t))
+
+  template get*(T: typedesc[tuple], i: static int): untyped =
+    ## Returns the `i`-th element of `T`.
+    # Note: `[]` currently gives: `Error: no generic parameters allowed for ...`
+    runnableExamples:
+      doAssert get((int, int, float, string), 2) is float
+
+    typeof(default(T)[i])
+
+  type StaticParam*[value: static type] = object
+    ## Used to wrap a static value in `genericParams <#genericParams.t,typedesc>`_.
+
+since (1, 3, 5):
+  template elementType*(a: untyped): typedesc =
+    ## Returns the element type of `a`, which can be any iterable (over which you
+    ## can iterate).
+    runnableExamples:
+      iterator myiter(n: int): auto =
+        for i in 0 ..< n:
+          yield i
+
+      doAssert elementType(@[1,2]) is int
+      doAssert elementType("asdf") is char
+      doAssert elementType(myiter(3)) is int
+
+    typeof(block: (for ai in a: ai))
+
+import std/macros
+
+macro enumLen*(T: typedesc[enum]): int =
+  ## Returns the number of items in the enum `T`.
+  runnableExamples:
+    type Foo = enum
+      fooItem1
+      fooItem2
+
+    doAssert Foo.enumLen == 2
+
+  let bracketExpr = getType(T)
+  expectKind(bracketExpr, nnkBracketExpr)
+  let enumTy = bracketExpr[1]
+  expectKind(enumTy, nnkEnumTy)
+  result = newLit(enumTy.len - 1)
+
+macro genericParamsImpl(T: typedesc): untyped =
+  # auxiliary macro needed, can't do it directly in `genericParams`
+  result = newNimNode(nnkTupleConstr)
+  var impl = getTypeImpl(T)
+  expectKind(impl, nnkBracketExpr)
+  impl = impl[1]
+  while true:
+    case impl.kind
+    of nnkSym:
+      impl = impl.getImpl
+    of nnkTypeDef:
+      impl = impl[2]
+    of nnkTypeOfExpr:
+      impl = getTypeInst(impl[0])
+    of nnkBracketExpr:
+      for i in 1..<impl.len:
+        let ai = impl[i]
+        var ret: NimNode = nil
+        case ai.typeKind
+        of ntyTypeDesc:
+          ret = ai
+        of ntyStatic: raiseAssert "unreachable"
+        else:
+          # getType from a resolved symbol might return a typedesc symbol.
+          # If so, use it directly instead of wrapping it in StaticParam.
+          if (ai.kind == nnkSym and ai.symKind == nskType) or
+              (ai.kind == nnkBracketExpr and ai[0].kind == nnkSym and
+              ai[0].symKind == nskType) or ai.kind in {nnkRefTy, nnkVarTy, nnkPtrTy, nnkProcTy}:
+            ret = ai
+          elif ai.kind == nnkInfix and ai[0].kind == nnkIdent and
+                ai[0].strVal == "..":
+            # For built-in array types, the "2" is translated to "0..1" then
+            # automagically translated to "range[0..1]". However this is not
+            # reflected in the AST, thus requiring manual transformation here.
+            #
+            # We will also be losing some context here:
+            #   var a: array[10, int]
+            # will be translated to:
+            #   var a: array[0..9, int]
+            # after typecheck. This means that we can't get the exact
+            # definition as typed by the user, which will cause confusion for
+            # users expecting:
+            #   genericParams(typeof(a)) is (StaticParam(10), int)
+            # to be true while in fact the result will be:
+            #   genericParams(typeof(a)) is (range[0..9], int)
+            ret = newTree(nnkBracketExpr, @[bindSym"range", ai])
+          else:
+            since (1, 1):
+              ret = newTree(nnkBracketExpr, @[bindSym"StaticParam", ai])
+        result.add ret
+      break
+    else:
+      error "wrong kind: " & $impl.kind, impl
+
+since (1, 1):
+  template genericParams*(T: typedesc): untyped =
+    ## Returns the tuple of generic parameters for the generic type `T`.
+    ##
+    ## **Note:** For the builtin array type, the index generic parameter will
+    ## **always** become a range type after it's bound to a variable.
+    runnableExamples:
+      type Foo[T1, T2] = object
+
+      doAssert genericParams(Foo[float, string]) is (float, string)
+
+      type Bar[N: static float, T] = object
+
+      doAssert genericParams(Bar[1.0, string]) is (StaticParam[1.0], string)
+      doAssert genericParams(Bar[1.0, string]).get(0).value == 1.0
+      doAssert genericParams(seq[Bar[2.0, string]]).get(0) is Bar[2.0, string]
+      var s: seq[Bar[3.0, string]]
+      doAssert genericParams(typeof(s)) is (Bar[3.0, string],)
+
+      doAssert genericParams(array[10, int]) is (StaticParam[10], int)
+      var a: array[10, int]
+      doAssert genericParams(typeof(a)) is (range[0..9], int)
+
+    type T2 = T
+    genericParamsImpl(T2)
+
+
+proc hasClosureImpl(n: NimNode): bool = discard "see compiler/vmops.nim"
+
+proc hasClosure*(fn: NimNode): bool {.since: (1, 5, 1).} =
+  ## Returns true if the func/proc/etc `fn` has `closure`.
+  ## `fn` has to be a resolved symbol of kind `nnkSym`. This
+  ## implies that the macro that calls this proc should accept `typed`
+  ## arguments and not `untyped` arguments.
+  expectKind fn, nnkSym
+  result = hasClosureImpl(fn)
 
+template toUnsigned*(T: typedesc[SomeInteger and not range]): untyped =
+  ## Returns an unsigned type with same bit size as `T`.
+  runnableExamples:
+    assert int8.toUnsigned is uint8
+    assert uint.toUnsigned is uint
+    assert int.toUnsigned is uint
+    # range types are currently unsupported:
+    assert not compiles(toUnsigned(range[0..7]))
+  when T is int8: uint8
+  elif T is int16: uint16
+  elif T is int32: uint32
+  elif T is int64: uint64
+  elif T is int: uint
+  else: T
 
-when isMainModule:
-  # echo type(42)
-  import streams
-  var ss = newStringStream()
-  ss.write($type(42)) # needs `$`
-  ss.setPosition(0)
-  doAssert ss.readAll() == "int"
+template toSigned*(T: typedesc[SomeInteger and not range]): untyped =
+  ## Returns a signed type with same bit size as `T`.
+  runnableExamples:
+    assert int8.toSigned is int8
+    assert uint16.toSigned is int16
+    # range types are currently unsupported:
+    assert not compiles(toSigned(range[0..7]))
+  when T is uint8: int8
+  elif T is uint16: int16
+  elif T is uint32: int32
+  elif T is uint64: int64
+  elif T is uint: int
+  else: T
diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim
index 978f569ac..8cbe117bb 100644
--- a/lib/pure/unicode.nim
+++ b/lib/pure/unicode.nim
@@ -8,163 +8,216 @@
 #
 
 ## This module provides support to handle the Unicode UTF-8 encoding.
-
-{.deadCodeElim: on.}  # dce option deprecated
+##
+## 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.
+##
+## The current version is compatible with Unicode v12.0.0.
+##
+## **See also:**
+## * `strutils module <strutils.html>`_
+## * `unidecode module <unidecode.html>`_
+## * `encodings module <encodings.html>`_
 
 include "system/inclrtl"
+import std/strbasics
+template toOa(s: string): auto = s.toOpenArray(0, s.high)
+
+proc substr(s: openArray[char] , first, last: int): string =
+  # Copied substr from system
+  let first = max(first, 0)
+  let L = max(min(last, high(s)) - first + 1, 0)
+  result = newString(L)
+  for i in 0 .. L-1:
+    result[i] = s[i+first]
 
 type
   RuneImpl = int32 # underlying type of Rune
-  Rune* = distinct RuneImpl   ## type that can hold any Unicode character
-  Rune16* = distinct int16 ## 16 bit Unicode character
-
-{.deprecated: [TRune: Rune, TRune16: Rune16].}
-
-proc `<=%`*(a, b: Rune): bool = return int(a) <=% int(b)
-proc `<%`*(a, b: Rune): bool = return int(a) <% int(b)
-proc `==`*(a, b: Rune): bool = return int(a) == int(b)
+  Rune* = distinct RuneImpl ## \
+    ## Type that can hold a single Unicode code point.
+    ##
+    ## A Rune may be composed with other Runes to a character on the screen.
+    ## `RuneImpl` is the underlying type used to store Runes, currently `int32`.
 
 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``
+proc runeLen*(s: openArray[char]): int {.rtl, extern: "nuc$1".} =
+  ## Returns the number of runes of the string ``s``.
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.runeLen == 6
+    ## note: a.len == 8
+
+  result = 0
   var i = 0
   while i < len(s):
-    if ord(s[i]) <=% 127: inc(i)
-    elif ord(s[i]) shr 5 == 0b110: inc(i, 2)
-    elif ord(s[i]) shr 4 == 0b1110: inc(i, 3)
-    elif ord(s[i]) shr 3 == 0b11110: inc(i, 4)
-    elif ord(s[i]) shr 2 == 0b111110: inc(i, 5)
-    elif ord(s[i]) shr 1 == 0b1111110: inc(i, 6)
+    if uint(s[i]) <= 127: inc(i)
+    elif uint(s[i]) shr 5 == 0b110: inc(i, 2)
+    elif uint(s[i]) shr 4 == 0b1110: inc(i, 3)
+    elif uint(s[i]) shr 3 == 0b11110: inc(i, 4)
+    elif uint(s[i]) shr 2 == 0b111110: inc(i, 5)
+    elif uint(s[i]) shr 1 == 0b1111110: inc(i, 6)
     else: inc i
     inc(result)
 
-proc runeLenAt*(s: string, i: Natural): int =
-  ## Returns the number of bytes the rune starting at ``s[i]`` takes
-  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
-  elif ord(s[i]) shr 3 == 0b11110: result = 4
-  elif ord(s[i]) shr 2 == 0b111110: result = 5
-  elif ord(s[i]) shr 1 == 0b1111110: result = 6
+proc runeLenAt*(s: openArray[char], i: Natural): int =
+  ## Returns the number of bytes the rune starting at ``s[i]`` takes.
+  ##
+  ## See also:
+  ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.runeLenAt(0) == 1
+    doAssert a.runeLenAt(1) == 2
+
+  if uint(s[i]) <= 127: result = 1
+  elif uint(s[i]) shr 5 == 0b110: result = 2
+  elif uint(s[i]) shr 4 == 0b1110: result = 3
+  elif uint(s[i]) shr 3 == 0b11110: result = 4
+  elif uint(s[i]) shr 2 == 0b111110: result = 5
+  elif uint(s[i]) shr 1 == 0b1111110: result = 6
   else: result = 1
 
 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``
-  ## ``i`` is incremented by the number of bytes that have been processed.
+template fastRuneAt*(s: openArray[char] or string, i: int, result: untyped, doInc = true) =
+  ## Returns the rune ``s[i]`` in ``result``.
+  ##
+  ## If ``doInc == true`` (default), ``i`` is incremented by the number
+  ## of bytes that have been processed.
   bind ones
-  if ord(s[i]) <=% 127:
-    result = Rune(ord(s[i]))
+  if uint(s[i]) <= 127:
+    result = Rune(uint(s[i]))
     when doInc: inc(i)
-  elif ord(s[i]) shr 5 == 0b110:
-    # assert(ord(s[i+1]) shr 6 == 0b10)
+  elif uint(s[i]) shr 5 == 0b110:
+    # assert(uint(s[i+1]) shr 6 == 0b10)
     if i <= s.len - 2:
-      result = Rune((ord(s[i]) and (ones(5))) shl 6 or
-                    (ord(s[i+1]) and ones(6)))
+      result = Rune((uint(s[i]) and (ones(5))) shl 6 or
+                    (uint(s[i+1]) and ones(6)))
       when doInc: inc(i, 2)
     else:
       result = replRune
       when doInc: inc(i)
-  elif ord(s[i]) shr 4 == 0b1110:
-    # assert(ord(s[i+1]) shr 6 == 0b10)
-    # assert(ord(s[i+2]) shr 6 == 0b10)
+  elif uint(s[i]) shr 4 == 0b1110:
+    # assert(uint(s[i+1]) shr 6 == 0b10)
+    # assert(uint(s[i+2]) shr 6 == 0b10)
     if i <= s.len - 3:
-      result = Rune((ord(s[i]) and ones(4)) shl 12 or
-               (ord(s[i+1]) and ones(6)) shl 6 or
-               (ord(s[i+2]) and ones(6)))
+      result = Rune((uint(s[i]) and ones(4)) shl 12 or
+                    (uint(s[i+1]) and ones(6)) shl 6 or
+                    (uint(s[i+2]) and ones(6)))
       when doInc: inc(i, 3)
     else:
       result = replRune
       when doInc: inc(i)
-  elif ord(s[i]) shr 3 == 0b11110:
-    # assert(ord(s[i+1]) shr 6 == 0b10)
-    # assert(ord(s[i+2]) shr 6 == 0b10)
-    # assert(ord(s[i+3]) shr 6 == 0b10)
+  elif uint(s[i]) shr 3 == 0b11110:
+    # assert(uint(s[i+1]) shr 6 == 0b10)
+    # assert(uint(s[i+2]) shr 6 == 0b10)
+    # assert(uint(s[i+3]) shr 6 == 0b10)
     if i <= s.len - 4:
-      result = Rune((ord(s[i]) and ones(3)) shl 18 or
-               (ord(s[i+1]) and ones(6)) shl 12 or
-               (ord(s[i+2]) and ones(6)) shl 6 or
-               (ord(s[i+3]) and ones(6)))
+      result = Rune((uint(s[i]) and ones(3)) shl 18 or
+                    (uint(s[i+1]) and ones(6)) shl 12 or
+                    (uint(s[i+2]) and ones(6)) shl 6 or
+                    (uint(s[i+3]) and ones(6)))
       when doInc: inc(i, 4)
     else:
       result = replRune
       when doInc: inc(i)
-  elif ord(s[i]) shr 2 == 0b111110:
-    # assert(ord(s[i+1]) shr 6 == 0b10)
-    # assert(ord(s[i+2]) shr 6 == 0b10)
-    # assert(ord(s[i+3]) shr 6 == 0b10)
-    # assert(ord(s[i+4]) shr 6 == 0b10)
+  elif uint(s[i]) shr 2 == 0b111110:
+    # assert(uint(s[i+1]) shr 6 == 0b10)
+    # assert(uint(s[i+2]) shr 6 == 0b10)
+    # assert(uint(s[i+3]) shr 6 == 0b10)
+    # assert(uint(s[i+4]) shr 6 == 0b10)
     if i <= s.len - 5:
-      result = Rune((ord(s[i]) and ones(2)) shl 24 or
-               (ord(s[i+1]) and ones(6)) shl 18 or
-               (ord(s[i+2]) and ones(6)) shl 12 or
-               (ord(s[i+3]) and ones(6)) shl 6 or
-               (ord(s[i+4]) and ones(6)))
+      result = Rune((uint(s[i]) and ones(2)) shl 24 or
+                (uint(s[i+1]) and ones(6)) shl 18 or
+                (uint(s[i+2]) and ones(6)) shl 12 or
+                (uint(s[i+3]) and ones(6)) shl 6 or
+                (uint(s[i+4]) and ones(6)))
       when doInc: inc(i, 5)
     else:
       result = replRune
       when doInc: inc(i)
-  elif ord(s[i]) shr 1 == 0b1111110:
-    # assert(ord(s[i+1]) shr 6 == 0b10)
-    # assert(ord(s[i+2]) shr 6 == 0b10)
-    # assert(ord(s[i+3]) shr 6 == 0b10)
-    # assert(ord(s[i+4]) shr 6 == 0b10)
-    # assert(ord(s[i+5]) shr 6 == 0b10)
+  elif uint(s[i]) shr 1 == 0b1111110:
+    # assert(uint(s[i+1]) shr 6 == 0b10)
+    # assert(uint(s[i+2]) shr 6 == 0b10)
+    # assert(uint(s[i+3]) shr 6 == 0b10)
+    # assert(uint(s[i+4]) shr 6 == 0b10)
+    # assert(uint(s[i+5]) shr 6 == 0b10)
     if i <= s.len - 6:
-      result = Rune((ord(s[i]) and ones(1)) shl 30 or
-               (ord(s[i+1]) and ones(6)) shl 24 or
-               (ord(s[i+2]) and ones(6)) shl 18 or
-               (ord(s[i+3]) and ones(6)) shl 12 or
-               (ord(s[i+4]) and ones(6)) shl 6 or
-               (ord(s[i+5]) and ones(6)))
+      result = Rune((uint(s[i]) and ones(1)) shl 30 or
+                    (uint(s[i+1]) and ones(6)) shl 24 or
+                    (uint(s[i+2]) and ones(6)) shl 18 or
+                    (uint(s[i+3]) and ones(6)) shl 12 or
+                    (uint(s[i+4]) and ones(6)) shl 6 or
+                    (uint(s[i+5]) and ones(6)))
       when doInc: inc(i, 6)
     else:
       result = replRune
       when doInc: inc(i)
   else:
-    result = Rune(ord(s[i]))
+    result = Rune(uint(s[i]))
     when doInc: inc(i)
 
-proc validateUtf8*(s: string): int =
+proc runeAt*(s: openArray[char], i: Natural): Rune =
+  ## Returns the rune in ``s`` at **byte index** ``i``.
+  ##
+  ## See also:
+  ## * `runeAtPos proc <#runeAtPos,string,int>`_
+  ## * `runeStrAtPos proc <#runeStrAtPos,string,Natural>`_
+  ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_
+  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)
+
+proc validateUtf8*(s: openArray[char]): int =
   ## Returns the position of the invalid byte in ``s`` if the string ``s`` does
   ## not hold valid UTF-8 data. Otherwise ``-1`` is returned.
+  ##
+  ## See also:
+  ## * `toUTF8 proc <#toUTF8,Rune>`_
+  ## * `$ proc <#$,Rune>`_ alias for `toUTF8`
+  ## * `fastToUTF8Copy template <#fastToUTF8Copy.t,Rune,string,int>`_
   var i = 0
   let L = s.len
   while i < L:
-    if ord(s[i]) <=% 127:
+    if uint(s[i]) <= 127:
       inc(i)
-    elif ord(s[i]) shr 5 == 0b110:
-      if ord(s[i]) < 0xc2: return i # Catch overlong ascii representations.
-      if i+1 < L and ord(s[i+1]) shr 6 == 0b10: inc(i, 2)
+    elif uint(s[i]) shr 5 == 0b110:
+      if uint(s[i]) < 0xc2: return i # Catch overlong ascii representations.
+      if i+1 < L and uint(s[i+1]) shr 6 == 0b10: inc(i, 2)
       else: return i
-    elif ord(s[i]) shr 4 == 0b1110:
-      if i+2 < L and ord(s[i+1]) shr 6 == 0b10 and ord(s[i+2]) shr 6 == 0b10:
+    elif uint(s[i]) shr 4 == 0b1110:
+      if i+2 < L and uint(s[i+1]) shr 6 == 0b10 and uint(s[i+2]) shr 6 == 0b10:
         inc i, 3
       else: return i
-    elif ord(s[i]) shr 3 == 0b11110:
-      if i+3 < L and ord(s[i+1]) shr 6 == 0b10 and
-                     ord(s[i+2]) shr 6 == 0b10 and
-                     ord(s[i+3]) shr 6 == 0b10:
+    elif uint(s[i]) shr 3 == 0b11110:
+      if i+3 < L and uint(s[i+1]) shr 6 == 0b10 and
+                     uint(s[i+2]) shr 6 == 0b10 and
+                     uint(s[i+3]) shr 6 == 0b10:
         inc i, 4
       else: return i
     else:
       return i
   return -1
 
-proc runeAt*(s: string, i: Natural): Rune =
-  ## Returns the unicode character in ``s`` at byte index ``i``
-  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`` (default), ``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``.
+  ##
+  ## See also:
+  ## * `validateUtf8 proc <#validateUtf8,string>`_
+  ## * `toUTF8 proc <#toUTF8,Rune>`_
+  ## * `$ proc <#$,Rune>`_ alias for `toUTF8`
   var i = RuneImpl(c)
   if i <=% 127:
     s.setLen(pos+1)
@@ -209,27 +262,71 @@ 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.
+  ##
+  ## See also:
+  ## * `validateUtf8 proc <#validateUtf8,string>`_
+  ## * `$ proc <#$,Rune>`_ alias for `toUTF8`
+  ## * `utf8 iterator <#utf8.i,string>`_
+  ## * `fastToUTF8Copy template <#fastToUTF8Copy.t,Rune,string,int>`_
+  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,Rune>`_.
+  ##
+  ## See also:
+  ## * `validateUtf8 proc <#validateUtf8,string>`_
+  ## * `fastToUTF8Copy template <#fastToUTF8Copy.t,Rune,string,int>`_
   rune.toUTF8
 
 proc `$`*(runes: seq[Rune]): string =
-  ## Converts a sequence of Runes to a string
+  ## Converts a sequence of Runes to a string.
+  ##
+  ## See also:
+  ## * `toRunes <#toRunes,string>`_ for a reverse operation
+  runnableExamples:
+    let
+      someString = "öÑ"
+      someRunes = toRunes(someString)
+    doAssert $someRunes == someString
+
   result = ""
-  for rune in runes: result.add(rune.toUTF8)
+  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
+proc runeOffset*(s: openArray[char], pos: Natural, start: Natural = 0): int =
+  ## 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 are solve more efficient by using an iterator
+  ## **Beware:** This can lead to unoptimized code and slow execution!
+  ## Most problems can be solved more efficiently by using an iterator
   ## or conversion to a seq of Rune.
+  ##
+  ## See also:
+  ## * `runeReverseOffset proc <#runeReverseOffset,string,Positive>`_
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.runeOffset(1) == 1
+    doAssert a.runeOffset(3) == 4
+    doAssert a.runeOffset(4) == 6
+
   var
     i = 0
     o = start
@@ -240,66 +337,87 @@ proc runeOffset*(s: string, pos:Natural, start: Natural = 0): int =
     inc i
   return o
 
-proc runeAtPos*(s: string, pos: int): Rune =
-  ## Returns the unicode character at position pos
+proc runeReverseOffset*(s: openArray[char], rev: Positive): (int, int) =
+  ## Returns a tuple with the byte offset of the
+  ## rune at position ``rev`` in ``s``, counting
+  ## from the end (starting with 1) and the total
+  ## number of runes in the string.
   ##
-  ## Beware: This can lead to unoptimized code and slow execution!
-  ## Most problems are solve more efficient by using an iterator
-  ## or conversion to a seq of 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 a negative value for offset if there are too few runes in
+  ## the string to satisfy the request.
   ##
-  ## Beware: This can lead to unoptimized code and slow execution!
-  ## Most problems are solve more efficient by using an iterator
+  ## **Beware:** This can lead to unoptimized code and slow execution!
+  ## Most problems can be solved more efficiently by using an iterator
   ## or conversion to a seq of Rune.
-  let o = runeOffset(s, pos)
-  s[o.. (o+runeLenAt(s, o)-1)]
-
-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
-  ## 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
-  ## satisfy the request.
   ##
-  ## Beware: This can lead to unoptimized code and slow execution!
-  ## Most problems are solve more efficient by using an iterator
-  ## or conversion to a seq of Rune.
+  ## See also:
+  ## * `runeOffset proc <#runeOffset,string,Natural,Natural>`_
   var
     a = rev.int
     o = 0
     x = 0
+  let times = 2*rev.int-s.runeLen # transformed from rev.int - a < s.runeLen - rev.int
   while o < s.len:
     let r = runeLenAt(s, o)
     o += r
-    if a < 0:
+    if a > times:
       x += r
     dec a
+  result = if a > 0: (-a, rev.int-a) else: (x, -a+rev.int)
 
-  if a > 0:
-    return (-a, rev.int-a)
-  return (x, -a+rev.int)
+proc runeAtPos*(s: openArray[char], pos: int): Rune =
+  ## 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
+  ## or conversion to a seq of Rune.
+  ##
+  ## See also:
+  ## * `runeAt proc <#runeAt,string,Natural>`_
+  ## * `runeStrAtPos proc <#runeStrAtPos,string,Natural>`_
+  ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_
+  fastRuneAt(s, runeOffset(s, pos), result, false)
 
-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.
+proc runeStrAtPos*(s: openArray[char], pos: Natural): 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
+  ## or conversion to a seq of Rune.
+  ##
+  ## See also:
+  ## * `runeAt proc <#runeAt,string,Natural>`_
+  ## * `runeAtPos proc <#runeAtPos,string,int>`_
+  ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_
+  let o = runeOffset(s, pos)
+  substr(s.toOpenArray(o,  (o+runeLenAt(s, o)-1)))
+
+proc runeSubStr*(s: openArray[char], pos: int, len: int = int.high): string =
+  ## Returns the UTF-8 substring starting at code point ``pos``
+  ## with ``len`` code points.
   ##
-  ## (Needs some examples)
+  ## 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.
+  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:
-      result = s.substr(o, s.len-1)
+      result = s.substr(o, s.high)
     elif len < 0:
       let e = rl + len
       if e < 0:
         result = ""
       else:
-        result = s.substr(o, runeOffset(s, e-(rl+pos) , o)-1)
+        result = s.substr(o, runeOffset(s, e-(rl+pos), o)-1)
     else:
       result = s.substr(o, runeOffset(s, len, o)-1)
   else:
@@ -321,972 +439,32 @@ proc runeSubStr*(s: string, pos:int, len:int = int.high): string =
         e = s.len
       result = s.substr(o, e-1)
 
-const
-  alphaRanges = [
-    0x00d8,  0x00f6,  #  -
-    0x00f8,  0x01f5,  #  -
-    0x0250,  0x02a8,  #  -
-    0x038e,  0x03a1,  #  -
-    0x03a3,  0x03ce,  #  -
-    0x03d0,  0x03d6,  #  -
-    0x03e2,  0x03f3,  #  -
-    0x0490,  0x04c4,  #  -
-    0x0561,  0x0587,  #  -
-    0x05d0,  0x05ea,  #  -
-    0x05f0,  0x05f2,  #  -
-    0x0621,  0x063a,  #  -
-    0x0640,  0x064a,  #  -
-    0x0671,  0x06b7,  #  -
-    0x06ba,  0x06be,  #  -
-    0x06c0,  0x06ce,  #  -
-    0x06d0,  0x06d3,  #  -
-    0x0905,  0x0939,  #  -
-    0x0958,  0x0961,  #  -
-    0x0985,  0x098c,  #  -
-    0x098f,  0x0990,  #  -
-    0x0993,  0x09a8,  #  -
-    0x09aa,  0x09b0,  #  -
-    0x09b6,  0x09b9,  #  -
-    0x09dc,  0x09dd,  #  -
-    0x09df,  0x09e1,  #  -
-    0x09f0,  0x09f1,  #  -
-    0x0a05,  0x0a0a,  #  -
-    0x0a0f,  0x0a10,  #  -
-    0x0a13,  0x0a28,  #  -
-    0x0a2a,  0x0a30,  #  -
-    0x0a32,  0x0a33,  #  -
-    0x0a35,  0x0a36,  #  -
-    0x0a38,  0x0a39,  #  -
-    0x0a59,  0x0a5c,  #  -
-    0x0a85,  0x0a8b,  #  -
-    0x0a8f,  0x0a91,  #  -
-    0x0a93,  0x0aa8,  #  -
-    0x0aaa,  0x0ab0,  #  -
-    0x0ab2,  0x0ab3,  #  -
-    0x0ab5,  0x0ab9,  #  -
-    0x0b05,  0x0b0c,  #  -
-    0x0b0f,  0x0b10,  #  -
-    0x0b13,  0x0b28,  #  -
-    0x0b2a,  0x0b30,  #  -
-    0x0b32,  0x0b33,  #  -
-    0x0b36,  0x0b39,  #  -
-    0x0b5c,  0x0b5d,  #  -
-    0x0b5f,  0x0b61,  #  -
-    0x0b85,  0x0b8a,  #  -
-    0x0b8e,  0x0b90,  #  -
-    0x0b92,  0x0b95,  #  -
-    0x0b99,  0x0b9a,  #  -
-    0x0b9e,  0x0b9f,  #  -
-    0x0ba3,  0x0ba4,  #  -
-    0x0ba8,  0x0baa,  #  -
-    0x0bae,  0x0bb5,  #  -
-    0x0bb7,  0x0bb9,  #  -
-    0x0c05,  0x0c0c,  #  -
-    0x0c0e,  0x0c10,  #  -
-    0x0c12,  0x0c28,  #  -
-    0x0c2a,  0x0c33,  #  -
-    0x0c35,  0x0c39,  #  -
-    0x0c60,  0x0c61,  #  -
-    0x0c85,  0x0c8c,  #  -
-    0x0c8e,  0x0c90,  #  -
-    0x0c92,  0x0ca8,  #  -
-    0x0caa,  0x0cb3,  #  -
-    0x0cb5,  0x0cb9,  #  -
-    0x0ce0,  0x0ce1,  #  -
-    0x0d05,  0x0d0c,  #  -
-    0x0d0e,  0x0d10,  #  -
-    0x0d12,  0x0d28,  #  -
-    0x0d2a,  0x0d39,  #  -
-    0x0d60,  0x0d61,  #  -
-    0x0e01,  0x0e30,  #  -
-    0x0e32,  0x0e33,  #  -
-    0x0e40,  0x0e46,  #  -
-    0x0e5a,  0x0e5b,  #  -
-    0x0e81,  0x0e82,  #  -
-    0x0e87,  0x0e88,  #  -
-    0x0e94,  0x0e97,  #  -
-    0x0e99,  0x0e9f,  #  -
-    0x0ea1,  0x0ea3,  #  -
-    0x0eaa,  0x0eab,  #  -
-    0x0ead,  0x0eae,  #  -
-    0x0eb2,  0x0eb3,  #  -
-    0x0ec0,  0x0ec4,  #  -
-    0x0edc,  0x0edd,  #  -
-    0x0f18,  0x0f19,  #  -
-    0x0f40,  0x0f47,  #  -
-    0x0f49,  0x0f69,  #  -
-    0x10d0,  0x10f6,  #  -
-    0x1100,  0x1159,  #  -
-    0x115f,  0x11a2,  #  -
-    0x11a8,  0x11f9,  #  -
-    0x1e00,  0x1e9b,  #  -
-    0x1f50,  0x1f57,  #  -
-    0x1f80,  0x1fb4,  #  -
-    0x1fb6,  0x1fbc,  #  -
-    0x1fc2,  0x1fc4,  #  -
-    0x1fc6,  0x1fcc,  #  -
-    0x1fd0,  0x1fd3,  #  -
-    0x1fd6,  0x1fdb,  #  -
-    0x1fe0,  0x1fec,  #  -
-    0x1ff2,  0x1ff4,  #  -
-    0x1ff6,  0x1ffc,  #  -
-    0x210a,  0x2113,  #  -
-    0x2115,  0x211d,  #  -
-    0x2120,  0x2122,  #  -
-    0x212a,  0x2131,  #  -
-    0x2133,  0x2138,  #  -
-    0x3041,  0x3094,  #  -
-    0x30a1,  0x30fa,  #  -
-    0x3105,  0x312c,  #  -
-    0x3131,  0x318e,  #  -
-    0x3192,  0x319f,  #  -
-    0x3260,  0x327b,  #  -
-    0x328a,  0x32b0,  #  -
-    0x32d0,  0x32fe,  #  -
-    0x3300,  0x3357,  #  -
-    0x3371,  0x3376,  #  -
-    0x337b,  0x3394,  #  -
-    0x3399,  0x339e,  #  -
-    0x33a9,  0x33ad,  #  -
-    0x33b0,  0x33c1,  #  -
-    0x33c3,  0x33c5,  #  -
-    0x33c7,  0x33d7,  #  -
-    0x33d9,  0x33dd,  #  -
-    0x4e00,  0x9fff,  #  -
-    0xac00,  0xd7a3,  #  -
-    0xf900,  0xfb06,  #  -
-    0xfb13,  0xfb17,  #  -
-    0xfb1f,  0xfb28,  #  -
-    0xfb2a,  0xfb36,  #  -
-    0xfb38,  0xfb3c,  #  -
-    0xfb40,  0xfb41,  #  -
-    0xfb43,  0xfb44,  #  -
-    0xfb46,  0xfbb1,  #  -
-    0xfbd3,  0xfd3d,  #  -
-    0xfd50,  0xfd8f,  #  -
-    0xfd92,  0xfdc7,  #  -
-    0xfdf0,  0xfdf9,  #  -
-    0xfe70,  0xfe72,  #  -
-    0xfe76,  0xfefc,  #  -
-    0xff66,  0xff6f,  #  -
-    0xff71,  0xff9d,  #  -
-    0xffa0,  0xffbe,  #  -
-    0xffc2,  0xffc7,  #  -
-    0xffca,  0xffcf,  #  -
-    0xffd2,  0xffd7,  #  -
-    0xffda,  0xffdc]  #  -
-
-  alphaSinglets = [
-    0x00aa,  #
-    0x00b5,  #
-    0x00ba,  #
-    0x03da,  #
-    0x03dc,  #
-    0x03de,  #
-    0x03e0,  #
-    0x06d5,  #
-    0x09b2,  #
-    0x0a5e,  #
-    0x0a8d,  #
-    0x0ae0,  #
-    0x0b9c,  #
-    0x0cde,  #
-    0x0e4f,  #
-    0x0e84,  #
-    0x0e8a,  #
-    0x0e8d,  #
-    0x0ea5,  #
-    0x0ea7,  #
-    0x0eb0,  #
-    0x0ebd,  #
-    0x1fbe,  #
-    0x207f,  #
-    0x20a8,  #
-    0x2102,  #
-    0x2107,  #
-    0x2124,  #
-    0x2126,  #
-    0x2128,  #
-    0xfb3e,  #
-    0xfe74]  #
-
-  spaceRanges = [
-    0x0009,  0x000d,  # tab and newline
-    0x0020,  0x0020,  # space
-    0x0085,  0x0085,  # next line
-    0x00a0,  0x00a0,  #
-    0x1680,  0x1680,  # Ogham space mark
-    0x2000,  0x200b,  # en dash .. zero-width space
-    0x200e,  0x200f,  # LTR mark .. RTL mark (pattern whitespace)
-    0x2028,  0x2029,  #  -     0x3000,  0x3000,  #
-    0x202f,  0x202f,  # narrow no-break space
-    0x205f,  0x205f,  # medium mathematical space
-    0x3000,  0x3000,  # ideographic space
-    0xfeff,  0xfeff]  #
-
-  toupperRanges = [
-    0x0061,  0x007a, 468,  # a-z A-Z
-    0x00e0,  0x00f6, 468,  # - -
-    0x00f8,  0x00fe, 468,  # - -
-    0x0256,  0x0257, 295,  # - -
-    0x0258,  0x0259, 298,  # - -
-    0x028a,  0x028b, 283,  # - -
-    0x03ad,  0x03af, 463,  # - -
-    0x03b1,  0x03c1, 468,  # - -
-    0x03c3,  0x03cb, 468,  # - -
-    0x03cd,  0x03ce, 437,  # - -
-    0x0430,  0x044f, 468,  # - -
-    0x0451,  0x045c, 420,  # - -
-    0x045e,  0x045f, 420,  # - -
-    0x0561,  0x0586, 452,  # - -
-    0x1f00,  0x1f07, 508,  # - -
-    0x1f10,  0x1f15, 508,  # - -
-    0x1f20,  0x1f27, 508,  # - -
-    0x1f30,  0x1f37, 508,  # - -
-    0x1f40,  0x1f45, 508,  # - -
-    0x1f60,  0x1f67, 508,  # - -
-    0x1f70,  0x1f71, 574,  # - -
-    0x1f72,  0x1f75, 586,  # - -
-    0x1f76,  0x1f77, 600,  # - -
-    0x1f78,  0x1f79, 628,  # - -
-    0x1f7a,  0x1f7b, 612,  # - -
-    0x1f7c,  0x1f7d, 626,  # - -
-    0x1f80,  0x1f87, 508,  # - -
-    0x1f90,  0x1f97, 508,  # - -
-    0x1fa0,  0x1fa7, 508,  # - -
-    0x1fb0,  0x1fb1, 508,  # - -
-    0x1fd0,  0x1fd1, 508,  # - -
-    0x1fe0,  0x1fe1, 508,  # - -
-    0x2170,  0x217f, 484,  # - -
-    0x24d0,  0x24e9, 474,  # - -
-    0xff41,  0xff5a, 468]  # - -
-
-  toupperSinglets = [
-    0x00ff, 621,  #
-    0x0101, 499,  #
-    0x0103, 499,  #
-    0x0105, 499,  #
-    0x0107, 499,  #
-    0x0109, 499,  #
-    0x010b, 499,  #
-    0x010d, 499,  #
-    0x010f, 499,  #
-    0x0111, 499,  #
-    0x0113, 499,  #
-    0x0115, 499,  #
-    0x0117, 499,  #
-    0x0119, 499,  #
-    0x011b, 499,  #
-    0x011d, 499,  #
-    0x011f, 499,  #
-    0x0121, 499,  #
-    0x0123, 499,  #
-    0x0125, 499,  #
-    0x0127, 499,  #
-    0x0129, 499,  #
-    0x012b, 499,  #
-    0x012d, 499,  #
-    0x012f, 499,  #
-    0x0131, 268,  #  I
-    0x0133, 499,  #
-    0x0135, 499,  #
-    0x0137, 499,  #
-    0x013a, 499,  #
-    0x013c, 499,  #
-    0x013e, 499,  #
-    0x0140, 499,  #
-    0x0142, 499,  #
-    0x0144, 499,  #
-    0x0146, 499,  #
-    0x0148, 499,  #
-    0x014b, 499,  #
-    0x014d, 499,  #
-    0x014f, 499,  #
-    0x0151, 499,  #
-    0x0153, 499,  #
-    0x0155, 499,  #
-    0x0157, 499,  #
-    0x0159, 499,  #
-    0x015b, 499,  #
-    0x015d, 499,  #
-    0x015f, 499,  #
-    0x0161, 499,  #
-    0x0163, 499,  #
-    0x0165, 499,  #
-    0x0167, 499,  #
-    0x0169, 499,  #
-    0x016b, 499,  #
-    0x016d, 499,  #
-    0x016f, 499,  #
-    0x0171, 499,  #
-    0x0173, 499,  #
-    0x0175, 499,  #
-    0x0177, 499,  #
-    0x017a, 499,  #
-    0x017c, 499,  #
-    0x017e, 499,  #
-    0x017f, 200,  #  S
-    0x0183, 499,  #
-    0x0185, 499,  #
-    0x0188, 499,  #
-    0x018c, 499,  #
-    0x0192, 499,  #
-    0x0199, 499,  #
-    0x01a1, 499,  #
-    0x01a3, 499,  #
-    0x01a5, 499,  #
-    0x01a8, 499,  #
-    0x01ad, 499,  #
-    0x01b0, 499,  #
-    0x01b4, 499,  #
-    0x01b6, 499,  #
-    0x01b9, 499,  #
-    0x01bd, 499,  #
-    0x01c5, 499,  #
-    0x01c6, 498,  #
-    0x01c8, 499,  #
-    0x01c9, 498,  #
-    0x01cb, 499,  #
-    0x01cc, 498,  #
-    0x01ce, 499,  #
-    0x01d0, 499,  #
-    0x01d2, 499,  #
-    0x01d4, 499,  #
-    0x01d6, 499,  #
-    0x01d8, 499,  #
-    0x01da, 499,  #
-    0x01dc, 499,  #
-    0x01df, 499,  #
-    0x01e1, 499,  #
-    0x01e3, 499,  #
-    0x01e5, 499,  #
-    0x01e7, 499,  #
-    0x01e9, 499,  #
-    0x01eb, 499,  #
-    0x01ed, 499,  #
-    0x01ef, 499,  #
-    0x01f2, 499,  #
-    0x01f3, 498,  #
-    0x01f5, 499,  #
-    0x01fb, 499,  #
-    0x01fd, 499,  #
-    0x01ff, 499,  #
-    0x0201, 499,  #
-    0x0203, 499,  #
-    0x0205, 499,  #
-    0x0207, 499,  #
-    0x0209, 499,  #
-    0x020b, 499,  #
-    0x020d, 499,  #
-    0x020f, 499,  #
-    0x0211, 499,  #
-    0x0213, 499,  #
-    0x0215, 499,  #
-    0x0217, 499,  #
-    0x0253, 290,  #
-    0x0254, 294,  #
-    0x025b, 297,  #
-    0x0260, 295,  #
-    0x0263, 293,  #
-    0x0268, 291,  #
-    0x0269, 289,  #
-    0x026f, 289,  #
-    0x0272, 287,  #
-    0x0283, 282,  #
-    0x0288, 282,  #
-    0x0292, 281,  #
-    0x03ac, 462,  #
-    0x03cc, 436,  #
-    0x03d0, 438,  #
-    0x03d1, 443,  #
-    0x03d5, 453,  #
-    0x03d6, 446,  #
-    0x03e3, 499,  #
-    0x03e5, 499,  #
-    0x03e7, 499,  #
-    0x03e9, 499,  #
-    0x03eb, 499,  #
-    0x03ed, 499,  #
-    0x03ef, 499,  #
-    0x03f0, 414,  #
-    0x03f1, 420,  #
-    0x0461, 499,  #
-    0x0463, 499,  #
-    0x0465, 499,  #
-    0x0467, 499,  #
-    0x0469, 499,  #
-    0x046b, 499,  #
-    0x046d, 499,  #
-    0x046f, 499,  #
-    0x0471, 499,  #
-    0x0473, 499,  #
-    0x0475, 499,  #
-    0x0477, 499,  #
-    0x0479, 499,  #
-    0x047b, 499,  #
-    0x047d, 499,  #
-    0x047f, 499,  #
-    0x0481, 499,  #
-    0x0491, 499,  #
-    0x0493, 499,  #
-    0x0495, 499,  #
-    0x0497, 499,  #
-    0x0499, 499,  #
-    0x049b, 499,  #
-    0x049d, 499,  #
-    0x049f, 499,  #
-    0x04a1, 499,  #
-    0x04a3, 499,  #
-    0x04a5, 499,  #
-    0x04a7, 499,  #
-    0x04a9, 499,  #
-    0x04ab, 499,  #
-    0x04ad, 499,  #
-    0x04af, 499,  #
-    0x04b1, 499,  #
-    0x04b3, 499,  #
-    0x04b5, 499,  #
-    0x04b7, 499,  #
-    0x04b9, 499,  #
-    0x04bb, 499,  #
-    0x04bd, 499,  #
-    0x04bf, 499,  #
-    0x04c2, 499,  #
-    0x04c4, 499,  #
-    0x04c8, 499,  #
-    0x04cc, 499,  #
-    0x04d1, 499,  #
-    0x04d3, 499,  #
-    0x04d5, 499,  #
-    0x04d7, 499,  #
-    0x04d9, 499,  #
-    0x04db, 499,  #
-    0x04dd, 499,  #
-    0x04df, 499,  #
-    0x04e1, 499,  #
-    0x04e3, 499,  #
-    0x04e5, 499,  #
-    0x04e7, 499,  #
-    0x04e9, 499,  #
-    0x04eb, 499,  #
-    0x04ef, 499,  #
-    0x04f1, 499,  #
-    0x04f3, 499,  #
-    0x04f5, 499,  #
-    0x04f9, 499,  #
-    0x1e01, 499,  #
-    0x1e03, 499,  #
-    0x1e05, 499,  #
-    0x1e07, 499,  #
-    0x1e09, 499,  #
-    0x1e0b, 499,  #
-    0x1e0d, 499,  #
-    0x1e0f, 499,  #
-    0x1e11, 499,  #
-    0x1e13, 499,  #
-    0x1e15, 499,  #
-    0x1e17, 499,  #
-    0x1e19, 499,  #
-    0x1e1b, 499,  #
-    0x1e1d, 499,  #
-    0x1e1f, 499,  #
-    0x1e21, 499,  #
-    0x1e23, 499,  #
-    0x1e25, 499,  #
-    0x1e27, 499,  #
-    0x1e29, 499,  #
-    0x1e2b, 499,  #
-    0x1e2d, 499,  #
-    0x1e2f, 499,  #
-    0x1e31, 499,  #
-    0x1e33, 499,  #
-    0x1e35, 499,  #
-    0x1e37, 499,  #
-    0x1e39, 499,  #
-    0x1e3b, 499,  #
-    0x1e3d, 499,  #
-    0x1e3f, 499,  #
-    0x1e41, 499,  #
-    0x1e43, 499,  #
-    0x1e45, 499,  #
-    0x1e47, 499,  #
-    0x1e49, 499,  #
-    0x1e4b, 499,  #
-    0x1e4d, 499,  #
-    0x1e4f, 499,  #
-    0x1e51, 499,  #
-    0x1e53, 499,  #
-    0x1e55, 499,  #
-    0x1e57, 499,  #
-    0x1e59, 499,  #
-    0x1e5b, 499,  #
-    0x1e5d, 499,  #
-    0x1e5f, 499,  #
-    0x1e61, 499,  #
-    0x1e63, 499,  #
-    0x1e65, 499,  #
-    0x1e67, 499,  #
-    0x1e69, 499,  #
-    0x1e6b, 499,  #
-    0x1e6d, 499,  #
-    0x1e6f, 499,  #
-    0x1e71, 499,  #
-    0x1e73, 499,  #
-    0x1e75, 499,  #
-    0x1e77, 499,  #
-    0x1e79, 499,  #
-    0x1e7b, 499,  #
-    0x1e7d, 499,  #
-    0x1e7f, 499,  #
-    0x1e81, 499,  #
-    0x1e83, 499,  #
-    0x1e85, 499,  #
-    0x1e87, 499,  #
-    0x1e89, 499,  #
-    0x1e8b, 499,  #
-    0x1e8d, 499,  #
-    0x1e8f, 499,  #
-    0x1e91, 499,  #
-    0x1e93, 499,  #
-    0x1e95, 499,  #
-    0x1ea1, 499,  #
-    0x1ea3, 499,  #
-    0x1ea5, 499,  #
-    0x1ea7, 499,  #
-    0x1ea9, 499,  #
-    0x1eab, 499,  #
-    0x1ead, 499,  #
-    0x1eaf, 499,  #
-    0x1eb1, 499,  #
-    0x1eb3, 499,  #
-    0x1eb5, 499,  #
-    0x1eb7, 499,  #
-    0x1eb9, 499,  #
-    0x1ebb, 499,  #
-    0x1ebd, 499,  #
-    0x1ebf, 499,  #
-    0x1ec1, 499,  #
-    0x1ec3, 499,  #
-    0x1ec5, 499,  #
-    0x1ec7, 499,  #
-    0x1ec9, 499,  #
-    0x1ecb, 499,  #
-    0x1ecd, 499,  #
-    0x1ecf, 499,  #
-    0x1ed1, 499,  #
-    0x1ed3, 499,  #
-    0x1ed5, 499,  #
-    0x1ed7, 499,  #
-    0x1ed9, 499,  #
-    0x1edb, 499,  #
-    0x1edd, 499,  #
-    0x1edf, 499,  #
-    0x1ee1, 499,  #
-    0x1ee3, 499,  #
-    0x1ee5, 499,  #
-    0x1ee7, 499,  #
-    0x1ee9, 499,  #
-    0x1eeb, 499,  #
-    0x1eed, 499,  #
-    0x1eef, 499,  #
-    0x1ef1, 499,  #
-    0x1ef3, 499,  #
-    0x1ef5, 499,  #
-    0x1ef7, 499,  #
-    0x1ef9, 499,  #
-    0x1f51, 508,  #
-    0x1f53, 508,  #
-    0x1f55, 508,  #
-    0x1f57, 508,  #
-    0x1fb3, 509,  #
-    0x1fc3, 509,  #
-    0x1fe5, 507,  #
-    0x1ff3, 509]  #
-
-  tolowerRanges = [
-    0x0041,  0x005a, 532,  # A-Z a-z
-    0x00c0,  0x00d6, 532,  # - -
-    0x00d8,  0x00de, 532,  # - -
-    0x0189,  0x018a, 705,  # - -
-    0x018e,  0x018f, 702,  # - -
-    0x01b1,  0x01b2, 717,  # - -
-    0x0388,  0x038a, 537,  # - -
-    0x038e,  0x038f, 563,  # - -
-    0x0391,  0x03a1, 532,  # - -
-    0x03a3,  0x03ab, 532,  # - -
-    0x0401,  0x040c, 580,  # - -
-    0x040e,  0x040f, 580,  # - -
-    0x0410,  0x042f, 532,  # - -
-    0x0531,  0x0556, 548,  # - -
-    0x10a0,  0x10c5, 548,  # - -
-    0x1f08,  0x1f0f, 492,  # - -
-    0x1f18,  0x1f1d, 492,  # - -
-    0x1f28,  0x1f2f, 492,  # - -
-    0x1f38,  0x1f3f, 492,  # - -
-    0x1f48,  0x1f4d, 492,  # - -
-    0x1f68,  0x1f6f, 492,  # - -
-    0x1f88,  0x1f8f, 492,  # - -
-    0x1f98,  0x1f9f, 492,  # - -
-    0x1fa8,  0x1faf, 492,  # - -
-    0x1fb8,  0x1fb9, 492,  # - -
-    0x1fba,  0x1fbb, 426,  # - -
-    0x1fc8,  0x1fcb, 414,  # - -
-    0x1fd8,  0x1fd9, 492,  # - -
-    0x1fda,  0x1fdb, 400,  # - -
-    0x1fe8,  0x1fe9, 492,  # - -
-    0x1fea,  0x1feb, 388,  # - -
-    0x1ff8,  0x1ff9, 372,  # - -
-    0x1ffa,  0x1ffb, 374,  # - -
-    0x2160,  0x216f, 516,  # - -
-    0x24b6,  0x24cf, 526,  # - -
-    0xff21,  0xff3a, 532]  # - -
-
-  tolowerSinglets = [
-    0x0100, 501,  #
-    0x0102, 501,  #
-    0x0104, 501,  #
-    0x0106, 501,  #
-    0x0108, 501,  #
-    0x010a, 501,  #
-    0x010c, 501,  #
-    0x010e, 501,  #
-    0x0110, 501,  #
-    0x0112, 501,  #
-    0x0114, 501,  #
-    0x0116, 501,  #
-    0x0118, 501,  #
-    0x011a, 501,  #
-    0x011c, 501,  #
-    0x011e, 501,  #
-    0x0120, 501,  #
-    0x0122, 501,  #
-    0x0124, 501,  #
-    0x0126, 501,  #
-    0x0128, 501,  #
-    0x012a, 501,  #
-    0x012c, 501,  #
-    0x012e, 501,  #
-    0x0130, 301,  #  i
-    0x0132, 501,  #
-    0x0134, 501,  #
-    0x0136, 501,  #
-    0x0139, 501,  #
-    0x013b, 501,  #
-    0x013d, 501,  #
-    0x013f, 501,  #
-    0x0141, 501,  #
-    0x0143, 501,  #
-    0x0145, 501,  #
-    0x0147, 501,  #
-    0x014a, 501,  #
-    0x014c, 501,  #
-    0x014e, 501,  #
-    0x0150, 501,  #
-    0x0152, 501,  #
-    0x0154, 501,  #
-    0x0156, 501,  #
-    0x0158, 501,  #
-    0x015a, 501,  #
-    0x015c, 501,  #
-    0x015e, 501,  #
-    0x0160, 501,  #
-    0x0162, 501,  #
-    0x0164, 501,  #
-    0x0166, 501,  #
-    0x0168, 501,  #
-    0x016a, 501,  #
-    0x016c, 501,  #
-    0x016e, 501,  #
-    0x0170, 501,  #
-    0x0172, 501,  #
-    0x0174, 501,  #
-    0x0176, 501,  #
-    0x0178, 379,  #
-    0x0179, 501,  #
-    0x017b, 501,  #
-    0x017d, 501,  #
-    0x0181, 710,  #
-    0x0182, 501,  #
-    0x0184, 501,  #
-    0x0186, 706,  #
-    0x0187, 501,  #
-    0x018b, 501,  #
-    0x0190, 703,  #
-    0x0191, 501,  #
-    0x0193, 705,  #
-    0x0194, 707,  #
-    0x0196, 711,  #
-    0x0197, 709,  #
-    0x0198, 501,  #
-    0x019c, 711,  #
-    0x019d, 713,  #
-    0x01a0, 501,  #
-    0x01a2, 501,  #
-    0x01a4, 501,  #
-    0x01a7, 501,  #
-    0x01a9, 718,  #
-    0x01ac, 501,  #
-    0x01ae, 718,  #
-    0x01af, 501,  #
-    0x01b3, 501,  #
-    0x01b5, 501,  #
-    0x01b7, 719,  #
-    0x01b8, 501,  #
-    0x01bc, 501,  #
-    0x01c4, 502,  #
-    0x01c5, 501,  #
-    0x01c7, 502,  #
-    0x01c8, 501,  #
-    0x01ca, 502,  #
-    0x01cb, 501,  #
-    0x01cd, 501,  #
-    0x01cf, 501,  #
-    0x01d1, 501,  #
-    0x01d3, 501,  #
-    0x01d5, 501,  #
-    0x01d7, 501,  #
-    0x01d9, 501,  #
-    0x01db, 501,  #
-    0x01de, 501,  #
-    0x01e0, 501,  #
-    0x01e2, 501,  #
-    0x01e4, 501,  #
-    0x01e6, 501,  #
-    0x01e8, 501,  #
-    0x01ea, 501,  #
-    0x01ec, 501,  #
-    0x01ee, 501,  #
-    0x01f1, 502,  #
-    0x01f2, 501,  #
-    0x01f4, 501,  #
-    0x01fa, 501,  #
-    0x01fc, 501,  #
-    0x01fe, 501,  #
-    0x0200, 501,  #
-    0x0202, 501,  #
-    0x0204, 501,  #
-    0x0206, 501,  #
-    0x0208, 501,  #
-    0x020a, 501,  #
-    0x020c, 501,  #
-    0x020e, 501,  #
-    0x0210, 501,  #
-    0x0212, 501,  #
-    0x0214, 501,  #
-    0x0216, 501,  #
-    0x0386, 538,  #
-    0x038c, 564,  #
-    0x03e2, 501,  #
-    0x03e4, 501,  #
-    0x03e6, 501,  #
-    0x03e8, 501,  #
-    0x03ea, 501,  #
-    0x03ec, 501,  #
-    0x03ee, 501,  #
-    0x0460, 501,  #
-    0x0462, 501,  #
-    0x0464, 501,  #
-    0x0466, 501,  #
-    0x0468, 501,  #
-    0x046a, 501,  #
-    0x046c, 501,  #
-    0x046e, 501,  #
-    0x0470, 501,  #
-    0x0472, 501,  #
-    0x0474, 501,  #
-    0x0476, 501,  #
-    0x0478, 501,  #
-    0x047a, 501,  #
-    0x047c, 501,  #
-    0x047e, 501,  #
-    0x0480, 501,  #
-    0x0490, 501,  #
-    0x0492, 501,  #
-    0x0494, 501,  #
-    0x0496, 501,  #
-    0x0498, 501,  #
-    0x049a, 501,  #
-    0x049c, 501,  #
-    0x049e, 501,  #
-    0x04a0, 501,  #
-    0x04a2, 501,  #
-    0x04a4, 501,  #
-    0x04a6, 501,  #
-    0x04a8, 501,  #
-    0x04aa, 501,  #
-    0x04ac, 501,  #
-    0x04ae, 501,  #
-    0x04b0, 501,  #
-    0x04b2, 501,  #
-    0x04b4, 501,  #
-    0x04b6, 501,  #
-    0x04b8, 501,  #
-    0x04ba, 501,  #
-    0x04bc, 501,  #
-    0x04be, 501,  #
-    0x04c1, 501,  #
-    0x04c3, 501,  #
-    0x04c7, 501,  #
-    0x04cb, 501,  #
-    0x04d0, 501,  #
-    0x04d2, 501,  #
-    0x04d4, 501,  #
-    0x04d6, 501,  #
-    0x04d8, 501,  #
-    0x04da, 501,  #
-    0x04dc, 501,  #
-    0x04de, 501,  #
-    0x04e0, 501,  #
-    0x04e2, 501,  #
-    0x04e4, 501,  #
-    0x04e6, 501,  #
-    0x04e8, 501,  #
-    0x04ea, 501,  #
-    0x04ee, 501,  #
-    0x04f0, 501,  #
-    0x04f2, 501,  #
-    0x04f4, 501,  #
-    0x04f8, 501,  #
-    0x1e00, 501,  #
-    0x1e02, 501,  #
-    0x1e04, 501,  #
-    0x1e06, 501,  #
-    0x1e08, 501,  #
-    0x1e0a, 501,  #
-    0x1e0c, 501,  #
-    0x1e0e, 501,  #
-    0x1e10, 501,  #
-    0x1e12, 501,  #
-    0x1e14, 501,  #
-    0x1e16, 501,  #
-    0x1e18, 501,  #
-    0x1e1a, 501,  #
-    0x1e1c, 501,  #
-    0x1e1e, 501,  #
-    0x1e20, 501,  #
-    0x1e22, 501,  #
-    0x1e24, 501,  #
-    0x1e26, 501,  #
-    0x1e28, 501,  #
-    0x1e2a, 501,  #
-    0x1e2c, 501,  #
-    0x1e2e, 501,  #
-    0x1e30, 501,  #
-    0x1e32, 501,  #
-    0x1e34, 501,  #
-    0x1e36, 501,  #
-    0x1e38, 501,  #
-    0x1e3a, 501,  #
-    0x1e3c, 501,  #
-    0x1e3e, 501,  #
-    0x1e40, 501,  #
-    0x1e42, 501,  #
-    0x1e44, 501,  #
-    0x1e46, 501,  #
-    0x1e48, 501,  #
-    0x1e4a, 501,  #
-    0x1e4c, 501,  #
-    0x1e4e, 501,  #
-    0x1e50, 501,  #
-    0x1e52, 501,  #
-    0x1e54, 501,  #
-    0x1e56, 501,  #
-    0x1e58, 501,  #
-    0x1e5a, 501,  #
-    0x1e5c, 501,  #
-    0x1e5e, 501,  #
-    0x1e60, 501,  #
-    0x1e62, 501,  #
-    0x1e64, 501,  #
-    0x1e66, 501,  #
-    0x1e68, 501,  #
-    0x1e6a, 501,  #
-    0x1e6c, 501,  #
-    0x1e6e, 501,  #
-    0x1e70, 501,  #
-    0x1e72, 501,  #
-    0x1e74, 501,  #
-    0x1e76, 501,  #
-    0x1e78, 501,  #
-    0x1e7a, 501,  #
-    0x1e7c, 501,  #
-    0x1e7e, 501,  #
-    0x1e80, 501,  #
-    0x1e82, 501,  #
-    0x1e84, 501,  #
-    0x1e86, 501,  #
-    0x1e88, 501,  #
-    0x1e8a, 501,  #
-    0x1e8c, 501,  #
-    0x1e8e, 501,  #
-    0x1e90, 501,  #
-    0x1e92, 501,  #
-    0x1e94, 501,  #
-    0x1ea0, 501,  #
-    0x1ea2, 501,  #
-    0x1ea4, 501,  #
-    0x1ea6, 501,  #
-    0x1ea8, 501,  #
-    0x1eaa, 501,  #
-    0x1eac, 501,  #
-    0x1eae, 501,  #
-    0x1eb0, 501,  #
-    0x1eb2, 501,  #
-    0x1eb4, 501,  #
-    0x1eb6, 501,  #
-    0x1eb8, 501,  #
-    0x1eba, 501,  #
-    0x1ebc, 501,  #
-    0x1ebe, 501,  #
-    0x1ec0, 501,  #
-    0x1ec2, 501,  #
-    0x1ec4, 501,  #
-    0x1ec6, 501,  #
-    0x1ec8, 501,  #
-    0x1eca, 501,  #
-    0x1ecc, 501,  #
-    0x1ece, 501,  #
-    0x1ed0, 501,  #
-    0x1ed2, 501,  #
-    0x1ed4, 501,  #
-    0x1ed6, 501,  #
-    0x1ed8, 501,  #
-    0x1eda, 501,  #
-    0x1edc, 501,  #
-    0x1ede, 501,  #
-    0x1ee0, 501,  #
-    0x1ee2, 501,  #
-    0x1ee4, 501,  #
-    0x1ee6, 501,  #
-    0x1ee8, 501,  #
-    0x1eea, 501,  #
-    0x1eec, 501,  #
-    0x1eee, 501,  #
-    0x1ef0, 501,  #
-    0x1ef2, 501,  #
-    0x1ef4, 501,  #
-    0x1ef6, 501,  #
-    0x1ef8, 501,  #
-    0x1f59, 492,  #
-    0x1f5b, 492,  #
-    0x1f5d, 492,  #
-    0x1f5f, 492,  #
-    0x1fbc, 491,  #
-    0x1fcc, 491,  #
-    0x1fec, 493,  #
-    0x1ffc, 491]  #
-
-  toTitleSinglets = [
-    0x01c4, 501,  #
-    0x01c6, 499,  #
-    0x01c7, 501,  #
-    0x01c9, 499,  #
-    0x01ca, 501,  #
-    0x01cc, 499,  #
-    0x01f1, 501,  #
-    0x01f3, 499]  #
-
-proc binarySearch(c: RuneImpl, tab: openArray[int], len, stride: int): int =
+proc `<=%`*(a, b: Rune): bool =
+  ## Checks if code point of `a` is smaller or equal to code point of `b`.
+  runnableExamples:
+    let
+      a = "ú".runeAt(0)
+      b = "ü".runeAt(0)
+    doAssert a <=% b
+  return int(a) <=% int(b)
+
+proc `<%`*(a, b: Rune): bool =
+  ## Checks if code point of `a` is smaller than code point of `b`.
+  runnableExamples:
+    let
+      a = "ú".runeAt(0)
+      b = "ü".runeAt(0)
+    doAssert a <% b
+  return int(a) <% int(b)
+
+proc `==`*(a, b: Rune): bool =
+  ## Checks if two runes are equal.
+  return int(a) == int(b)
+
+
+include "includes/unicode_ranges"
+
+proc binarySearch(c: RuneImpl, tab: openArray[int32], len, stride: int): int =
   var n = len
   var t = 0
   while n > 1:
@@ -1301,64 +479,102 @@ proc binarySearch(c: RuneImpl, tab: openArray[int], len, stride: int): int =
     return t
   return -1
 
-proc toLower*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
-  ## Converts ``c`` into lower case. This works for any Unicode character.
+proc toLower*(c: Rune): Rune {.rtl, extern: "nuc$1".} =
+  ## Converts ``c`` into lower case. This works for any rune.
+  ##
   ## If possible, prefer ``toLower`` over ``toUpper``.
+  ##
+  ## See also:
+  ## * `toUpper proc <#toUpper,Rune>`_
+  ## * `toTitle proc <#toTitle,Rune>`_
+  ## * `isLower proc <#isLower,Rune>`_
   var c = RuneImpl(c)
-  var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3)
-  if p >= 0 and c >= tolowerRanges[p] and c <= tolowerRanges[p+1]:
-    return Rune(c + tolowerRanges[p+2] - 500)
-  p = binarySearch(c, tolowerSinglets, len(tolowerSinglets) div 2, 2)
-  if p >= 0 and c == tolowerSinglets[p]:
-    return Rune(c + tolowerSinglets[p+1] - 500)
+  var p = binarySearch(c, toLowerRanges, len(toLowerRanges) div 3, 3)
+  if p >= 0 and c >= toLowerRanges[p] and c <= toLowerRanges[p+1]:
+    return Rune(c + toLowerRanges[p+2] - 500)
+  p = binarySearch(c, toLowerSinglets, len(toLowerSinglets) div 2, 2)
+  if p >= 0 and c == toLowerSinglets[p]:
+    return Rune(c + toLowerSinglets[p+1] - 500)
   return Rune(c)
 
-proc toUpper*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
-  ## Converts ``c`` into upper case. This works for any Unicode character.
+proc toUpper*(c: Rune): Rune {.rtl, extern: "nuc$1".} =
+  ## Converts ``c`` into upper case. This works for any rune.
+  ##
   ## If possible, prefer ``toLower`` over ``toUpper``.
+  ##
+  ## See also:
+  ## * `toLower proc <#toLower,Rune>`_
+  ## * `toTitle proc <#toTitle,Rune>`_
+  ## * `isUpper proc <#isUpper,Rune>`_
   var c = RuneImpl(c)
-  var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3)
-  if p >= 0 and c >= toupperRanges[p] and c <= toupperRanges[p+1]:
-    return Rune(c + toupperRanges[p+2] - 500)
-  p = binarySearch(c, toupperSinglets, len(toupperSinglets) div 2, 2)
-  if p >= 0 and c == toupperSinglets[p]:
-    return Rune(c + toupperSinglets[p+1] - 500)
+  var p = binarySearch(c, toUpperRanges, len(toUpperRanges) div 3, 3)
+  if p >= 0 and c >= toUpperRanges[p] and c <= toUpperRanges[p+1]:
+    return Rune(c + toUpperRanges[p+2] - 500)
+  p = binarySearch(c, toUpperSinglets, len(toUpperSinglets) div 2, 2)
+  if p >= 0 and c == toUpperSinglets[p]:
+    return Rune(c + toUpperSinglets[p+1] - 500)
   return Rune(c)
 
-proc toTitle*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
-  ## Converts ``c`` to title case
+proc toTitle*(c: Rune): Rune {.rtl, extern: "nuc$1".} =
+  ## Converts ``c`` to title case.
+  ##
+  ## See also:
+  ## * `toLower proc <#toLower,Rune>`_
+  ## * `toUpper proc <#toUpper,Rune>`_
+  ## * `isTitle proc <#isTitle,Rune>`_
   var c = RuneImpl(c)
   var p = binarySearch(c, toTitleSinglets, len(toTitleSinglets) div 2, 2)
   if p >= 0 and c == toTitleSinglets[p]:
     return Rune(c + toTitleSinglets[p+1] - 500)
   return Rune(c)
 
-proc isLower*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
-  ## Returns true iff ``c`` is a lower case Unicode character.
+proc isLower*(c: Rune): bool {.rtl, extern: "nuc$1".} =
+  ## Returns true if ``c`` is a lower case rune.
+  ##
   ## If possible, prefer ``isLower`` over ``isUpper``.
+  ##
+  ## See also:
+  ## * `toLower proc <#toLower,Rune>`_
+  ## * `isUpper proc <#isUpper,Rune>`_
+  ## * `isTitle proc <#isTitle,Rune>`_
   var c = RuneImpl(c)
   # Note: toUpperRanges is correct here!
-  var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3)
-  if p >= 0 and c >= toupperRanges[p] and c <= toupperRanges[p+1]:
+  var p = binarySearch(c, toUpperRanges, len(toUpperRanges) div 3, 3)
+  if p >= 0 and c >= toUpperRanges[p] and c <= toUpperRanges[p+1]:
     return true
-  p = binarySearch(c, toupperSinglets, len(toupperSinglets) div 2, 2)
-  if p >= 0 and c == toupperSinglets[p]:
+  p = binarySearch(c, toUpperSinglets, len(toUpperSinglets) div 2, 2)
+  if p >= 0 and c == toUpperSinglets[p]:
     return true
 
-proc isUpper*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
-  ## Returns true iff ``c`` is a upper case Unicode character.
+proc isUpper*(c: Rune): bool {.rtl, extern: "nuc$1".} =
+  ## Returns true if ``c`` is a upper case rune.
+  ##
   ## If possible, prefer ``isLower`` over ``isUpper``.
+  ##
+  ## See also:
+  ## * `toUpper proc <#toUpper,Rune>`_
+  ## * `isLower proc <#isLower,Rune>`_
+  ## * `isTitle proc <#isTitle,Rune>`_
+  ## * `isAlpha proc <#isAlpha,Rune>`_
+  ## * `isWhiteSpace proc <#isWhiteSpace,Rune>`_
   var c = RuneImpl(c)
   # Note: toLowerRanges is correct here!
-  var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3)
-  if p >= 0 and c >= tolowerRanges[p] and c <= tolowerRanges[p+1]:
+  var p = binarySearch(c, toLowerRanges, len(toLowerRanges) div 3, 3)
+  if p >= 0 and c >= toLowerRanges[p] and c <= toLowerRanges[p+1]:
     return true
-  p = binarySearch(c, tolowerSinglets, len(tolowerSinglets) div 2, 2)
-  if p >= 0 and c == tolowerSinglets[p]:
+  p = binarySearch(c, toLowerSinglets, len(toLowerSinglets) div 2, 2)
+  if p >= 0 and c == toLowerSinglets[p]:
     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)
+proc isAlpha*(c: Rune): bool {.rtl, extern: "nuc$1".} =
+  ## Returns true if ``c`` is an *alpha* rune (i.e., a letter).
+  ##
+  ## See also:
+  ## * `isLower proc <#isLower,Rune>`_
+  ## * `isTitle proc <#isTitle,Rune>`_
+  ## * `isAlpha proc <#isAlpha,Rune>`_
+  ## * `isWhiteSpace proc <#isWhiteSpace,Rune>`_
+  ## * `isCombining proc <#isCombining,Rune>`_
   if isUpper(c) or isLower(c):
     return true
   var c = RuneImpl(c)
@@ -1369,19 +585,38 @@ proc isAlpha*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
   if p >= 0 and c == alphaSinglets[p]:
     return true
 
-proc isTitle*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
-  ## Returns true iff ``c`` is a Unicode titlecase character
+proc isTitle*(c: Rune): bool {.rtl, extern: "nuc$1".} =
+  ## Returns true if ``c`` is a Unicode titlecase code point.
+  ##
+  ## See also:
+  ## * `toTitle proc <#toTitle,Rune>`_
+  ## * `isLower proc <#isLower,Rune>`_
+  ## * `isUpper proc <#isUpper,Rune>`_
+  ## * `isAlpha proc <#isAlpha,Rune>`_
+  ## * `isWhiteSpace proc <#isWhiteSpace,Rune>`_
   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
+proc isWhiteSpace*(c: Rune): bool {.rtl, extern: "nuc$1".} =
+  ## Returns true if ``c`` is a Unicode whitespace code point.
+  ##
+  ## See also:
+  ## * `isLower proc <#isLower,Rune>`_
+  ## * `isUpper proc <#isUpper,Rune>`_
+  ## * `isTitle proc <#isTitle,Rune>`_
+  ## * `isAlpha proc <#isAlpha,Rune>`_
   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
+proc isCombining*(c: Rune): bool {.rtl, extern: "nuc$1".} =
+  ## Returns true if ``c`` is a Unicode combining code unit.
+  ##
+  ## See also:
+  ## * `isLower proc <#isLower,Rune>`_
+  ## * `isUpper proc <#isUpper,Rune>`_
+  ## * `isTitle proc <#isTitle,Rune>`_
+  ## * `isAlpha proc <#isAlpha,Rune>`_
   var c = RuneImpl(c)
 
   # Optimized to return false immediately for ASCII
@@ -1394,155 +629,116 @@ proc isCombining*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
 template runeCheck(s, runeProc) =
   ## Common code for isAlpha and isSpace.
   result = if len(s) == 0: false else: true
-
   var
     i = 0
     rune: Rune
-
   while i < len(s) and result:
-    fastRuneAt(s, i, rune, doInc=true)
+    fastRuneAt(s, i, rune, doInc = true)
     result = runeProc(rune) and result
 
-proc isAlpha*(s: string): bool {.noSideEffect, procvar,
+proc isAlpha*(s: openArray[char]): bool {.noSideEffect,
   rtl, extern: "nuc$1Str".} =
-  ## Returns true iff `s` contains all alphabetic unicode characters.
+  ## Returns true if ``s`` contains all alphabetic runes.
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.isAlpha
   runeCheck(s, isAlpha)
 
-proc isSpace*(s: string): bool {.noSideEffect, procvar,
+proc isSpace*(s: openArray[char]): bool {.noSideEffect,
   rtl, extern: "nuc$1Str".} =
-  ## Returns true iff `s` contains all whitespace unicode characters.
+  ## Returns true if ``s`` contains all whitespace runes.
+  runnableExamples:
+    let a = "\t\l \v\r\f"
+    doAssert a.isSpace
   runeCheck(s, isWhiteSpace)
 
-template runeCaseCheck(s, runeProc, skipNonAlpha) =
-  ## Common code for rune.isLower and rune.isUpper.
-  if len(s) == 0: return false
-
-  var
-    i = 0
-    rune: Rune
-    hasAtleastOneAlphaRune = false
-
-  while i < len(s):
-    fastRuneAt(s, i, rune, doInc=true)
-    if skipNonAlpha:
-      var runeIsAlpha = isAlpha(rune)
-      if not hasAtleastOneAlphaRune:
-        hasAtleastOneAlphaRune = runeIsAlpha
-      if runeIsAlpha and (not runeProc(rune)):
-        return false
-    else:
-      if not runeProc(rune):
-        return false
-  return if skipNonAlpha: hasAtleastOneAlphaRune else: true
-
-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 alphabetical.
-  ##
-  ## If ``skipNonAlpha`` is false, returns true only if all runes in
-  ## ``s`` are alphabetical and lower case.
-  ##
-  ## For either value of ``skipNonAlpha``, returns false if ``s`` is
-  ## an empty string.
-  runeCaseCheck(s, isLower, skipNonAlpha)
-
-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 alphabetical.
-  ##
-  ## If ``skipNonAlpha`` is false, returns true only if all runes in
-  ## ``s`` are alphabetical and upper case.
-  ##
-  ## For either value of ``skipNonAlpha``, returns false if ``s`` is
-  ## an empty string.
-  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
     i = 0
-    lastIndex = 0
+    resultIndex = 0
     rune: Rune
-
   while i < len(s):
-    lastIndex = i
-    fastRuneAt(s, i, rune, doInc=true)
+    fastRuneAt(s, i, rune, doInc = true)
     rune = runeProc(rune)
+    fastToUTF8Copy(rune, result, resultIndex, doInc = true)
 
-    rune.fastToUTF8Copy(result, lastIndex)
-
-proc toUpper*(s: string): string {.noSideEffect, procvar,
+proc toUpper*(s: openArray[char]): string {.noSideEffect,
   rtl, extern: "nuc$1Str".} =
-  ## Converts `s` into upper-case unicode characters.
+  ## Converts ``s`` into upper-case runes.
+  runnableExamples:
+    doAssert toUpper("abγ") == "ABΓ"
   convertRune(s, toUpper)
 
-proc toLower*(s: string): string {.noSideEffect, procvar,
+proc toLower*(s: openArray[char]): string {.noSideEffect,
   rtl, extern: "nuc$1Str".} =
-  ## Converts `s` into lower-case unicode characters.
+  ## Converts ``s`` into lower-case runes.
+  runnableExamples:
+    doAssert toLower("ABΓ") == "abγ"
   convertRune(s, toLower)
 
-proc swapCase*(s: string): string {.noSideEffect, procvar,
+proc swapCase*(s: openArray[char]): string {.noSideEffect,
   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.
+  runnableExamples:
+    doAssert swapCase("Αlpha Βeta Γamma") == "αLPHA βETA γAMMA"
 
   var
     i = 0
-    lastIndex = 0
+    resultIndex = 0
     rune: Rune
-
   result = newString(len(s))
-
   while i < len(s):
-    lastIndex = i
-
     fastRuneAt(s, i, rune)
-
     if rune.isUpper():
       rune = rune.toLower()
     elif rune.isLower():
       rune = rune.toUpper()
+    fastToUTF8Copy(rune, result, resultIndex, doInc = true)
 
-    rune.fastToUTF8Copy(result, lastIndex)
-
-proc capitalize*(s: string): string {.noSideEffect, procvar,
+proc capitalize*(s: openArray[char]): string {.noSideEffect,
   rtl, extern: "nuc$1".} =
-  ## Converts the first character of `s` into an upper-case unicode character.
-  if len(s) == 0:
-    return s
+  ## Converts the first character of ``s`` into an upper-case rune.
+  runnableExamples:
+    doAssert capitalize("βeta") == "Βeta"
 
+  if len(s) == 0:
+    return ""
   var
     rune: Rune
     i = 0
+  fastRuneAt(s, i, rune, doInc = true)
+  result = $toUpper(rune) & substr(s.toOpenArray(i, s.high))
 
-  fastRuneAt(s, i, rune, doInc=true)
+when not defined(nimHasEffectsOf):
+  {.pragma: effectsOf.}
 
-  result = $toUpper(rune) & substr(s, i)
-
-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
+proc translate*(s: openArray[char], replacements: proc(key: string): string): string {.
+  rtl, extern: "nuc$1", effectsOf: 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.
+  runnableExamples:
+    proc wordToNumber(s: string): string =
+      case s
+      of "one": "1"
+      of "two": "2"
+      else: s
+    let a = "one two three four"
+    doAssert a.translate(wordToNumber) == "1 2 three four"
 
   # Allocate memory for the new string based on the old one.
   # If the new string length is less than the old, no allocations
   # will be needed. If the new string length is greater than the
   # old, then maybe only one allocation is needed
   result = newStringOfCap(s.len)
-
   var
     index = 0
     lastIndex = 0
@@ -1552,17 +748,14 @@ proc translate*(s: string, replacements: proc(key: string): string): string {.
 
   while index < len(s):
     lastIndex = index
-
     fastRuneAt(s, index, rune)
-
     let whiteSpace = rune.isWhiteSpace()
 
     if whiteSpace and inWord:
       # If we've reached the end of a word
-      let word = s[wordStart ..< lastIndex]
+      let word = substr(s.toOpenArray(wordStart, lastIndex - 1))
       result.add(replacements(word))
       result.add($rune)
-
       inWord = false
     elif not whiteSpace and not inWord:
       # If we've hit a non space character and
@@ -1575,66 +768,37 @@ proc translate*(s: string, replacements: proc(key: string): string): string {.
 
   if wordStart < len(s) and inWord:
     # Get the trailing word at the end
-    let word = s[wordStart .. ^1]
+    let word = substr(s.toOpenArray(wordStart,  s.high))
     result.add(replacements(word))
 
-proc title*(s: string): string {.noSideEffect, procvar,
+proc title*(s: openArray[char]): string {.noSideEffect,
   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.
+  runnableExamples:
+    doAssert title("αlpha βeta γamma") == "Αlpha Βeta Γamma"
 
   var
     i = 0
-    lastIndex = 0
+    resultIndex = 0
     rune: Rune
-
   result = newString(len(s))
-
   var firstRune = true
 
   while i < len(s):
-    lastIndex = i
-
     fastRuneAt(s, i, rune)
-
     if not rune.isWhiteSpace() and firstRune:
       rune = rune.toUpper()
       firstRune = false
     elif rune.isWhiteSpace():
       firstRune = true
+    fastToUTF8Copy(rune, result, resultIndex, doInc = true)
 
-    rune.fastToUTF8Copy(result, lastIndex)
-
-proc isTitle*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nuc$1Str".}=
-  ## 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`.
-  if s.len() == 0:
-    return false
-
-  result = true
 
-  var
-    i = 0
-    rune: Rune
-
-  var firstRune = true
-
-  while i < len(s) and result:
-    fastRuneAt(s, i, rune, doInc=true)
-
-    if not rune.isWhiteSpace() and firstRune:
-      result = rune.isUpper() and result
-      firstRune = false
-    elif rune.isWhiteSpace():
-      firstRune = true
-
-iterator runes*(s: string): Rune =
-  ## Iterates over any unicode character of the string ``s`` returning runes
+iterator runes*(s: openArray[char]): Rune =
+  ## Iterates over any rune of the string ``s`` returning runes.
   var
     i = 0
     result: Rune
@@ -1642,26 +806,39 @@ iterator runes*(s: string): Rune =
     fastRuneAt(s, i, result, true)
     yield result
 
-iterator utf8*(s: string): string =
-  ## Iterates over any unicode character of the string ``s`` returning utf8 values
+iterator utf8*(s: openArray[char]): string =
+  ## Iterates over any rune of the string ``s`` returning utf8 values.
+  ##
+  ## See also:
+  ## * `validateUtf8 proc <#validateUtf8,string>`_
+  ## * `toUTF8 proc <#toUTF8,Rune>`_
+  ## * `$ proc <#$,Rune>`_ alias for `toUTF8`
+  ## * `fastToUTF8Copy template <#fastToUTF8Copy.t,Rune,string,int>`_
   var o = 0
   while o < s.len:
     let n = runeLenAt(s, o)
-    yield s[o.. (o+n-1)]
+    yield substr(s.toOpenArray(o, (o+n-1)))
     o += n
 
-proc toRunes*(s: string): seq[Rune] =
-  ## Obtains a sequence containing the Runes in ``s``
+proc toRunes*(s: openArray[char]): seq[Rune] =
+  ## Obtains a sequence containing the Runes in ``s``.
+  ##
+  ## See also:
+  ## * `$ proc <#$,Rune>`_ for a reverse operation
+  runnableExamples:
+    let a = toRunes("aáä")
+    doAssert a == @["a".runeAt(0), "á".runeAt(0), "ä".runeAt(0)]
+
   result = newSeq[Rune]()
   for r in s.runes:
     result.add(r)
 
-proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} =
+proc cmpRunesIgnoreCase*(a, b: openArray[char]): int {.rtl, extern: "nuc$1".} =
   ## Compares two UTF-8 strings and ignores the case. 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
   var i = 0
   var j = 0
   var ar, br: Rune
@@ -1669,20 +846,25 @@ proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} =
     # slow path:
     fastRuneAt(a, i, ar)
     fastRuneAt(b, j, br)
-    result = RuneImpl(toLower(ar)) - RuneImpl(toLower(br))
+    when sizeof(int) < 4:
+      const lo = low(int).int32
+      const hi = high(int).int32
+      result = clamp(RuneImpl(toLower(ar)) - RuneImpl(toLower(br)), lo, hi).int
+    else:
+      result = RuneImpl(toLower(ar)) - RuneImpl(toLower(br))
     if result != 0: return
   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
+proc reversed*(s: openArray[char]): string =
+  ## Returns the reverse of ``s``, interpreting it as runes.
   ##
-  ##   assert reversed("Reverse this!") == "!siht esreveR"
-  ##   assert reversed("先秦兩漢") == "漢兩秦先"
-  ##   assert reversed("as⃝df̅") == "f̅ds⃝a"
-  ##   assert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞"
+  ## 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
@@ -1708,9 +890,15 @@ 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
-  ## characters.
+proc graphemeLen*(s: openArray[char]; i: Natural): Natural =
+  ## The number of bytes belonging to byte index ``s[i]``,
+  ## including following combining code units.
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.graphemeLen(1) == 2 ## ñ
+    doAssert a.graphemeLen(2) == 1
+    doAssert a.graphemeLen(4) == 2 ## ó
+
   var j = i.int
   var r, r2: Rune
   if j < s.len:
@@ -1721,180 +909,607 @@ proc graphemeLen*(s: string; i: Natural): Natural =
       if not isCombining(r2): break
       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
+proc lastRune*(s: openArray[char]; last: int): (Rune, int) =
+  ## 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)
   else:
     var L = 0
-    while last-L >= 0 and ord(s[last-L]) shr 6 == 0b10: inc(L)
+    while last-L >= 0 and uint(s[last-L]) shr 6 == 0b10: inc(L)
     var r: Rune
     fastRuneAt(s, last-L, r, false)
     result = (r, L+1)
 
-when isMainModule:
+proc size*(r: Rune): int {.noSideEffect.} =
+  ## Returns the number of bytes the rune ``r`` takes.
+  runnableExamples:
+    let a = toRunes "aá"
+    doAssert size(a[0]) == 1
+    doAssert size(a[1]) == 2
+
+  let v = r.uint32
+  if v <= 0x007F'u32: result = 1
+  elif v <= 0x07FF'u32: result = 2
+  elif v <= 0xFFFF'u32: result = 3
+  elif v <= 0x1FFFFF'u32: result = 4
+  elif v <= 0x3FFFFFF'u32: result = 5
+  elif v <= 0x7FFFFFFF'u32: result = 6
+  else: result = 1
+
+# --------- Private templates for different split separators -----------
+proc stringHasSep(s: openArray[char], index: int, seps: openArray[Rune]): bool =
+  var rune: Rune
+  fastRuneAt(s, index, rune, false)
+  return seps.contains(rune)
+
+proc stringHasSep(s: openArray[char], index: int, sep: Rune): bool =
+  var rune: Rune
+  fastRuneAt(s, index, rune, false)
+  return sep == rune
+
+template splitCommon(s, sep, maxsplit: untyped) =
+  ## Common code for split procedures.
   let
-    someString = "öÑ"
-    someRunes = @[runeAt(someString, 0), runeAt(someString, 2)]
-    compared = (someString == $someRunes)
-  doAssert compared == true
-
-  proc test_replacements(word: string): string =
-    case word
-    of "two":
-      return "2"
-    of "foo":
-      return "BAR"
-    of "βeta":
-      return "beta"
-    of "alpha":
-      return "αlpha"
-    else:
-      return "12345"
-
-  doAssert translate("two not alpha foo βeta", test_replacements) == "2 12345 αlpha BAR beta"
-  doAssert translate("  two not foo βeta  ", test_replacements) == "  2 12345 BAR beta  "
-
-  doAssert title("foo bar") == "Foo Bar"
-  doAssert title("αlpha βeta γamma") == "Αlpha Βeta Γamma"
-  doAssert title("") == ""
-
-  doAssert capitalize("βeta") == "Βeta"
-  doAssert capitalize("foo") == "Foo"
-  doAssert capitalize("") == ""
-
-  doAssert isTitle("Foo")
-  doAssert(not isTitle("Foo bar"))
-  doAssert(not isTitle("αlpha Βeta"))
-  doAssert(isTitle("Αlpha Βeta Γamma"))
-  doAssert(not isTitle("fFoo"))
-
-  doAssert swapCase("FooBar") == "fOObAR"
-  doAssert swapCase(" ") == " "
-  doAssert swapCase("Αlpha Βeta Γamma") == "αLPHA βETA γAMMA"
-  doAssert swapCase("a✓B") == "A✓b"
-  doAssert swapCase("") == ""
-
-  doAssert isAlpha("r")
-  doAssert isAlpha("α")
-  doAssert(not isAlpha("$"))
-  doAssert(not isAlpha(""))
-
-  doAssert isAlpha("Βeta")
-  doAssert isAlpha("Args")
-  doAssert(not isAlpha("$Foo✓"))
-
-  doAssert isSpace("\t")
-  doAssert isSpace("\l")
-  doAssert(not isSpace("Β"))
-  doAssert(not isSpace("Βeta"))
-
-  doAssert isSpace("\t\l \v\r\f")
-  doAssert isSpace("       ")
-  doAssert(not isSpace(""))
-  doAssert(not isSpace("ΑΓc   \td"))
-
-  doAssert(not isLower(' '.Rune))
-
-  doAssert isLower("a", false)
-  doAssert isLower("γ", true)
-  doAssert(not isLower("Γ", false))
-  doAssert(not isLower("4", true))
-  doAssert(not isLower("", false))
-  doAssert isLower("abcdγ", false)
-  doAssert(not isLower("33aaΓ", false))
-  doAssert(not isLower("a b", false))
-
-  doAssert(not isLower("abCDΓ", true))
-  doAssert isLower("a b", true)
-  doAssert isLower("1, 2, 3 go!", true)
-  doAssert(not isLower(" ", true))
-  doAssert(not isLower("(*&#@(^#$✓ ", true)) # None of the string runes are alphabets
-
-  doAssert(not isUpper(' '.Rune))
-
-  doAssert isUpper("Γ", false)
-  doAssert(not isUpper("α", false))
-  doAssert(not isUpper("", false))
-  doAssert isUpper("ΑΒΓ", false)
-  doAssert(not isUpper("A#$β", false))
-  doAssert(not isUpper("A B", false))
-
-  doAssert(not isUpper("b", true))
-  doAssert(not isUpper("✓", true))
-  doAssert(not isUpper("AAccβ", true))
-  doAssert isUpper("A B", true)
-  doAssert isUpper("1, 2, 3 GO!", true)
-  doAssert(not isUpper(" ", true))
-  doAssert(not isUpper("(*&#@(^#$✓ ", true)) # None of the string runes are alphabets
-
-  doAssert toUpper("Γ") == "Γ"
-  doAssert toUpper("b") == "B"
-  doAssert toUpper("α") == "Α"
-  doAssert toUpper("✓") == "✓"
-  doAssert toUpper("") == ""
-
-  doAssert toUpper("ΑΒΓ") == "ΑΒΓ"
-  doAssert toUpper("AAccβ") == "AACCΒ"
-  doAssert toUpper("A✓$β") == "A✓$Β"
-
-  doAssert toLower("a") == "a"
-  doAssert toLower("γ") == "γ"
-  doAssert toLower("Γ") == "γ"
-  doAssert toLower("4") == "4"
-  doAssert toLower("") == ""
-
-  doAssert toLower("abcdγ") == "abcdγ"
-  doAssert toLower("abCDΓ") == "abcdγ"
-  doAssert toLower("33aaΓ") == "33aaγ"
-
-  doAssert reversed("Reverse this!") == "!siht esreveR"
-  doAssert reversed("先秦兩漢") == "漢兩秦先"
-  doAssert reversed("as⃝df̅") == "f̅ds⃝a"
-  doAssert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞"
-  doAssert len(toRunes("as⃝df̅")) == runeLen("as⃝df̅")
-  const test = "as⃝"
-  doAssert lastRune(test, test.len-1)[1] == 3
-  doAssert graphemeLen("è", 0) == 2
-
-  # test for rune positioning and runeSubStr()
-  let s = "Hänsel  ««: 10,00€"
-
-  var t = ""
-  for c in s.utf8:
-    t.add c
-
-  doAssert(s == t)
-
-  doAssert(runeReverseOffset(s, 1) == (20, 18))
-  doAssert(runeReverseOffset(s, 19) == (-1, 18))
-
-  doAssert(runeStrAtPos(s, 0) == "H")
-  doAssert(runeSubStr(s, 0, 1) == "H")
-  doAssert(runeStrAtPos(s, 10) == ":")
-  doAssert(runeSubStr(s, 10, 1) == ":")
-  doAssert(runeStrAtPos(s, 9) == "«")
-  doAssert(runeSubStr(s, 9, 1) == "«")
-  doAssert(runeStrAtPos(s, 17) == "€")
-  doAssert(runeSubStr(s, 17, 1) == "€")
-  # echo runeStrAtPos(s, 18) # index error
-
-  doAssert(runeSubStr(s, 0) == "Hänsel  ««: 10,00€")
-  doAssert(runeSubStr(s, -18) == "Hänsel  ««: 10,00€")
-  doAssert(runeSubStr(s, 10) == ": 10,00€")
-  doAssert(runeSubStr(s, 18) == "")
-  doAssert(runeSubStr(s, 0, 10) == "Hänsel  ««")
-
-  doAssert(runeSubStr(s, 12) == "10,00€")
-  doAssert(runeSubStr(s, -6) == "10,00€")
-
-  doAssert(runeSubStr(s, 12, 5) == "10,00")
-  doAssert(runeSubStr(s, 12, -1) == "10,00")
-  doAssert(runeSubStr(s, -6, 5) == "10,00")
-  doAssert(runeSubStr(s, -6, -1) == "10,00")
-
-  doAssert(runeSubStr(s, 0, 100) == "Hänsel  ««: 10,00€")
-  doAssert(runeSubStr(s, -100, 100) == "Hänsel  ««: 10,00€")
-  doAssert(runeSubStr(s, 0, -100) == "")
-  doAssert(runeSubStr(s, 100, -100) == "")
+    sLen = len(s)
+  var
+    last = 0
+    splits = maxsplit
+  if sLen > 0:
+    while last <= sLen:
+      var first = last
+      while last < sLen and not stringHasSep(s, last, sep):
+        inc(last, runeLenAt(s, last))
+      if splits == 0: last = sLen
+      yield substr(s.toOpenArray(first, (last - 1)))
+      if splits == 0: break
+      dec(splits)
+      inc(last, if last < sLen: runeLenAt(s, last) else: 1)
+
+iterator split*(s: openArray[char], seps: openArray[Rune] = unicodeSpaces,
+  maxsplit: int = -1): string =
+  ## Splits the unicode string ``s`` into substrings using a group of separators.
+  ##
+  ## Substrings are separated by a substring containing only ``seps``.
+  runnableExamples:
+    import std/sequtils
+
+    assert toSeq("hÃllo\lthis\lis an\texample\l是".split) ==
+      @["hÃllo", "this", "is", "an", "example", "是"]
+
+    # And the following code splits the same string using a sequence of Runes.
+    assert toSeq(split("añyóng:hÃllo;是$example", ";:$".toRunes)) ==
+      @["añyóng", "hÃllo", "是", "example"]
+
+    # example with a `Rune` separator and unused one `;`:
+    assert toSeq(split("ab是de:f:", ";:是".toRunes)) == @["ab", "de", "f", ""]
+
+    # Another example that splits a string containing a date.
+    let date = "2012-11-20T22:08:08.398990"
+
+    assert toSeq(split(date, " -:T".toRunes)) ==
+      @["2012", "11", "20", "22", "08", "08.398990"]
+
+  splitCommon(s, seps, maxsplit)
+
+iterator splitWhitespace*(s: openArray[char]): string =
+  ## Splits a unicode string at whitespace runes.
+  splitCommon(s, unicodeSpaces, -1)
+
+template accResult(iter: untyped) =
+  result = @[]
+  for x in iter: add(result, x)
+
+proc splitWhitespace*(s: openArray[char]): seq[string] {.noSideEffect,
+  rtl, extern: "ncuSplitWhitespace".} =
+  ## The same as the `splitWhitespace <#splitWhitespace.i,string>`_
+  ## iterator, but is a proc that returns a sequence of substrings.
+  accResult(splitWhitespace(s))
+
+iterator split*(s: openArray[char], sep: Rune, maxsplit: int = -1): string =
+  ## Splits the unicode string ``s`` into substrings using a single separator.
+  ## Substrings are separated by the rune ``sep``.
+  runnableExamples:
+    import std/sequtils
+
+    assert toSeq(split(";;hÃllo;this;is;an;;example;;;是", ";".runeAt(0))) ==
+      @["", "", "hÃllo", "this", "is", "an", "", "example", "", "", "是"]
+
+  splitCommon(s, sep, maxsplit)
+
+proc split*(s: openArray[char], seps: openArray[Rune] = unicodeSpaces, maxsplit: int = -1):
+    seq[string] {.noSideEffect, rtl, extern: "nucSplitRunes".} =
+  ## The same as the `split iterator <#split.i,string,openArray[Rune],int>`_,
+  ## but is a proc that returns a sequence of substrings.
+  accResult(split(s, seps, maxsplit))
+
+proc split*(s: openArray[char], sep: Rune, maxsplit: int = -1): seq[string] {.noSideEffect,
+  rtl, extern: "nucSplitRune".} =
+  ## The same as the `split iterator <#split.i,string,Rune,int>`_, but is a proc
+  ## that returns a sequence of substrings.
+  accResult(split(s, sep, maxsplit))
+
+proc strip*(s: openArray[char], leading = true, trailing = true,
+            runes: openArray[Rune] = unicodeSpaces): string {.noSideEffect,
+            rtl, extern: "nucStrip".} =
+  ## Strips leading or trailing ``runes`` from ``s`` and returns
+  ## the resulting string.
+  ##
+  ## If ``leading`` is true (default), leading ``runes`` are stripped.
+  ## If ``trailing`` is true (default), trailing ``runes`` are stripped.
+  ## If both are false, the string is returned unchanged.
+  runnableExamples:
+    let a = "\táñyóng   "
+    doAssert a.strip == "áñyóng"
+    doAssert a.strip(leading = false) == "\táñyóng"
+    doAssert a.strip(trailing = false) == "áñyóng   "
+
+  var
+    sI = 0          ## starting index into string ``s``
+    eI = len(s) - 1 ## ending index into ``s``, where the last ``Rune`` starts
+  if leading:
+    var
+      i = 0
+      xI: int ## value of ``sI`` at the beginning of the iteration
+      rune: Rune
+    while i < len(s):
+      xI = i
+      fastRuneAt(s, i, rune)
+      sI = i # Assume to start from next rune
+      if not runes.contains(rune):
+        sI = xI # Go back to where the current rune starts
+        break
+  if trailing:
+    var
+      i = eI
+      xI: int
+      rune: Rune
+    while i >= 0:
+      xI = i
+      fastRuneAt(s, xI, rune)
+      var yI = i - 1
+      while yI >= 0:
+        var
+          yIend = yI
+          pRune: Rune
+        fastRuneAt(s, yIend, pRune)
+        if yIend < xI: break
+        i = yI
+        rune = pRune
+        dec(yI)
+      if not runes.contains(rune):
+        eI = xI - 1
+        break
+      dec(i)
+  let newLen = eI - sI + 1
+  result = newStringOfCap(newLen)
+  if newLen > 0:
+    result.add substr(s.toOpenArray(sI, eI))
+
+proc repeat*(c: Rune, count: Natural): string {.noSideEffect,
+  rtl, extern: "nucRepeatRune".} =
+  ## Returns a string of ``count`` Runes ``c``.
+  ##
+  ## The returned string will have a rune-length of ``count``.
+  runnableExamples:
+    let a = "ñ".runeAt(0)
+    doAssert a.repeat(5) == "ñññññ"
+
+  let s = $c
+  result = newStringOfCap(count * s.len)
+  for i in 0 ..< count:
+    result.add s
+
+proc align*(s: openArray[char], 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``.
+  ##
+  ## ``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,string,Natural>`_.
+  runnableExamples:
+    assert align("abc", 4) == " abc"
+    assert align("a", 0) == "a"
+    assert align("1232", 6) == "  1232"
+    assert align("1232", 6, '#'.Rune) == "##1232"
+    assert align("Åge", 5) == "  Åge"
+    assert align("×", 4, '_'.Rune) == "___×"
+
+  let sLen = s.runeLen
+  if sLen < count:
+    let padStr = $padding
+    result = newStringOfCap(padStr.len * count)
+    let spaces = count - sLen
+    for i in 0 ..< spaces: result.add padStr
+    result.add s
+  else:
+    result = s.substr
+
+proc alignLeft*(s: openArray[char], count: Natural, padding = ' '.Rune): string {.
+    noSideEffect.} =
+  ## 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
+  ## returned unchanged. If you need to right align a string use the `align
+  ## proc <#align,string,Natural>`_.
+  runnableExamples:
+    assert alignLeft("abc", 4) == "abc "
+    assert alignLeft("a", 0) == "a"
+    assert alignLeft("1232", 6) == "1232  "
+    assert alignLeft("1232", 6, '#'.Rune) == "1232##"
+    assert alignLeft("Åge", 5) == "Åge  "
+    assert alignLeft("×", 4, '_'.Rune) == "×___"
+  let sLen = s.runeLen
+  if sLen < count:
+    let padStr = $padding
+    result = newStringOfCap(s.len + (count - sLen) * padStr.len)
+    result.add s
+    for i in sLen ..< count:
+      result.add padStr
+  else:
+    result = s.substr
+
+
+proc runeLen*(s: string): int {.inline.} =
+  ## Returns the number of runes of the string ``s``.
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.runeLen == 6
+    ## note: a.len == 8
+  runeLen(toOa(s))
+
+proc runeLenAt*(s: string, i: Natural): int {.inline.} =
+  ## Returns the number of bytes the rune starting at ``s[i]`` takes.
+  ##
+  ## See also:
+  ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.runeLenAt(0) == 1
+    doAssert a.runeLenAt(1) == 2
+  runeLenAt(toOa(s), i)
+
+proc runeAt*(s: string, i: Natural): Rune {.inline.} =
+  ## Returns the rune in ``s`` at **byte index** ``i``.
+  ##
+  ## See also:
+  ## * `runeAtPos proc <#runeAtPos,string,int>`_
+  ## * `runeStrAtPos proc <#runeStrAtPos,string,Natural>`_
+  ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_
+  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)
+
+proc validateUtf8*(s: string): int {.inline.} =
+  ## Returns the position of the invalid byte in ``s`` if the string ``s`` does
+  ## not hold valid UTF-8 data. Otherwise ``-1`` is returned.
+  ##
+  ## See also:
+  ## * `toUTF8 proc <#toUTF8,Rune>`_
+  ## * `$ proc <#$,Rune>`_ alias for `toUTF8`
+  ## * `fastToUTF8Copy template <#fastToUTF8Copy.t,Rune,string,int>`_
+  validateUtf8(toOa(s))
+
+proc runeOffset*(s: string, pos: Natural, start: Natural = 0): int {.inline.} =
+  ## 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
+  ## or conversion to a seq of Rune.
+  ##
+  ## See also:
+  ## * `runeReverseOffset proc <#runeReverseOffset,string,Positive>`_
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.runeOffset(1) == 1
+    doAssert a.runeOffset(3) == 4
+    doAssert a.runeOffset(4) == 6
+  runeOffset(toOa(s), pos, start)
+
+proc runeReverseOffset*(s: string, rev: Positive): (int, int) {.inline.} =
+  ## Returns a tuple with the byte offset of the
+  ## 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 too few runes in
+  ## the string to satisfy the request.
+  ##
+  ## **Beware:** This can lead to unoptimized code and slow execution!
+  ## Most problems can be solved more efficiently by using an iterator
+  ## or conversion to a seq of Rune.
+  ##
+  ## See also:
+  ## * `runeOffset proc <#runeOffset,string,Natural,Natural>`_
+  runeReverseOffset(toOa(s), rev)
+
+proc runeAtPos*(s: string, pos: int): Rune {.inline.} =
+  ## 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
+  ## or conversion to a seq of Rune.
+  ##
+  ## See also:
+  ## * `runeAt proc <#runeAt,string,Natural>`_
+  ## * `runeStrAtPos proc <#runeStrAtPos,string,Natural>`_
+  ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_
+  fastRuneAt(toOa(s), runeOffset(s, pos), result, false)
+
+proc runeStrAtPos*(s: string, pos: Natural): string {.inline.} =
+  ## 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
+  ## or conversion to a seq of Rune.
+  ##
+  ## See also:
+  ## * `runeAt proc <#runeAt,string,Natural>`_
+  ## * `runeAtPos proc <#runeAtPos,string,int>`_
+  ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_
+  let o = runeOffset(s, pos)
+  substr(s.toOpenArray(o, (o+runeLenAt(s, o)-1)))
+
+proc runeSubStr*(s: string, pos: int, len: int = int.high): string {.inline.} =
+  ## Returns the UTF-8 substring starting at code point ``pos``
+  ## with ``len`` code points.
+  ##
+  ## 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.
+  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,")
+  runeSubStr(toOa(s), pos, len)
+
+
+proc isAlpha*(s: string): bool {.noSideEffect, inline.} =
+  ## Returns true if ``s`` contains all alphabetic runes.
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.isAlpha
+  isAlpha(toOa(s))
+
+proc isSpace*(s: string): bool {.noSideEffect, inline.} =
+  ## Returns true if ``s`` contains all whitespace runes.
+  runnableExamples:
+    let a = "\t\l \v\r\f"
+    doAssert a.isSpace
+  isSpace(toOa(s))
+
+
+proc toUpper*(s: string): string {.noSideEffect, inline.} =
+  ## Converts ``s`` into upper-case runes.
+  runnableExamples:
+    doAssert toUpper("abγ") == "ABΓ"
+  toUpper(toOa(s))
+
+proc toLower*(s: string): string {.noSideEffect, inline.} =
+  ## Converts ``s`` into lower-case runes.
+  runnableExamples:
+    doAssert toLower("ABΓ") == "abγ"
+  toLower(toOa(s))
+
+proc swapCase*(s: string): string {.noSideEffect, inline.} =
+  ## Swaps the case of runes in ``s``.
+  ##
+  ## Returns a new string such that the cases of all runes
+  ## are swapped if possible.
+  runnableExamples:
+    doAssert swapCase("Αlpha Βeta Γamma") == "αLPHA βETA γAMMA"
+  swapCase(toOa(s))
+
+proc capitalize*(s: string): string {.noSideEffect.} =
+  ## Converts the first character of ``s`` into an upper-case rune.
+  runnableExamples:
+    doAssert capitalize("βeta") == "Βeta"
+  capitalize(toOa(s))
+
+
+proc translate*(s: string, replacements: proc(key: string): string): string {.effectsOf: replacements, inline.} =
+  ## 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
+  ## a new word to fill it's place.
+  runnableExamples:
+    proc wordToNumber(s: string): string =
+      case s
+      of "one": "1"
+      of "two": "2"
+      else: s
+    let a = "one two three four"
+    doAssert a.translate(wordToNumber) == "1 2 three four"
+  translate(toOa(s), replacements)
+
+proc title*(s: string): string {.noSideEffect, inline.} =
+  ## Converts ``s`` to a unicode title.
+  ##
+  ## Returns a new string such that the first character
+  ## in each word inside ``s`` is capitalized.
+  runnableExamples:
+    doAssert title("αlpha βeta γamma") == "Αlpha Βeta Γamma"
+  title(toOa(s))
+
+
+iterator runes*(s: string): Rune =
+  ## Iterates over any rune of the string ``s`` returning runes.
+  for rune in runes(toOa(s)):
+    yield rune
+
+iterator utf8*(s: string): string =
+  ## Iterates over any rune of the string ``s`` returning utf8 values.
+  ##
+  ## See also:
+  ## * `validateUtf8 proc <#validateUtf8,string>`_
+  ## * `toUTF8 proc <#toUTF8,Rune>`_
+  ## * `$ proc <#$,Rune>`_ alias for `toUTF8`
+  ## * `fastToUTF8Copy template <#fastToUTF8Copy.t,Rune,string,int>`_
+  for str in utf8(toOa(s)):
+    yield str
+
+proc toRunes*(s: string): seq[Rune] {.inline.} =
+  ## Obtains a sequence containing the Runes in ``s``.
+  ##
+  ## See also:
+  ## * `$ proc <#$,Rune>`_ for a reverse operation
+  runnableExamples:
+    let a = toRunes("aáä")
+    doAssert a == @["a".runeAt(0), "á".runeAt(0), "ä".runeAt(0)]
+  toRunes(toOa(s))
+
+proc cmpRunesIgnoreCase*(a, b: string): int {.inline.} =
+  ## Compares two UTF-8 strings and ignores the case. Returns:
+  ##
+  ## | `0` if a == b
+  ## | `< 0` if a < b
+  ## | `> 0` if a > b
+  cmpRunesIgnoreCase(a.toOa(), b.toOa())
+
+proc reversed*(s: string): string {.inline.} =
+  ## 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⃞"
+  reversed(toOa(s))
+
+proc graphemeLen*(s: string; i: Natural): Natural {.inline.} =
+  ## The number of bytes belonging to byte index ``s[i]``,
+  ## including following combining code unit.
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.graphemeLen(1) == 2 ## ñ
+    doAssert a.graphemeLen(2) == 1
+    doAssert a.graphemeLen(4) == 2 ## ó
+  graphemeLen(toOa(s), i)
+
+proc lastRune*(s: string; last: int): (Rune, int) {.inline.} =
+  ## Length of the last rune in ``s[0..last]``. Returns the rune and its length
+  ## in bytes.
+  lastRune(toOa(s), last)
+
+iterator split*(s: string, seps: openArray[Rune] = unicodeSpaces,
+  maxsplit: int = -1): string =
+  ## Splits the unicode string ``s`` into substrings using a group of separators.
+  ##
+  ## Substrings are separated by a substring containing only ``seps``.
+  runnableExamples:
+    import std/sequtils
+
+    assert toSeq("hÃllo\lthis\lis an\texample\l是".split) ==
+      @["hÃllo", "this", "is", "an", "example", "是"]
+
+    # And the following code splits the same string using a sequence of Runes.
+    assert toSeq(split("añyóng:hÃllo;是$example", ";:$".toRunes)) ==
+      @["añyóng", "hÃllo", "是", "example"]
+
+    # example with a `Rune` separator and unused one `;`:
+    assert toSeq(split("ab是de:f:", ";:是".toRunes)) == @["ab", "de", "f", ""]
+
+    # Another example that splits a string containing a date.
+    let date = "2012-11-20T22:08:08.398990"
+
+    assert toSeq(split(date, " -:T".toRunes)) ==
+      @["2012", "11", "20", "22", "08", "08.398990"]
+
+  splitCommon(toOa(s), seps, maxsplit)
+
+iterator splitWhitespace*(s: string): string =
+  ## Splits a unicode string at whitespace runes.
+  splitCommon(s.toOa(), unicodeSpaces, -1)
+
+
+proc splitWhitespace*(s: string): seq[string] {.noSideEffect, inline.}=
+  ## The same as the `splitWhitespace <#splitWhitespace.i,string>`_
+  ## iterator, but is a proc that returns a sequence of substrings.
+  accResult(splitWhitespace(toOa(s)))
+
+iterator split*(s: string, sep: Rune, maxsplit: int = -1): string =
+  ## Splits the unicode string ``s`` into substrings using a single separator.
+  ## Substrings are separated by the rune ``sep``.
+  runnableExamples:
+    import std/sequtils
+
+    assert toSeq(split(";;hÃllo;this;is;an;;example;;;是", ";".runeAt(0))) ==
+      @["", "", "hÃllo", "this", "is", "an", "", "example", "", "", "是"]
+
+  splitCommon(toOa(s), sep, maxsplit)
+
+proc split*(s: string, seps: openArray[Rune] = unicodeSpaces, maxsplit: int = -1):
+    seq[string] {.noSideEffect, inline.} =
+  ## The same as the `split iterator <#split.i,string,openArray[Rune],int>`_,
+  ## but is a proc that returns a sequence of substrings.
+  accResult(split(toOa(s), seps, maxsplit))
+
+proc split*(s: string, sep: Rune, maxsplit: int = -1): seq[string] {.noSideEffect, inline.} =
+  ## The same as the `split iterator <#split.i,string,Rune,int>`_, but is a proc
+  ## that returns a sequence of substrings.
+  accResult(split(toOa(s), sep, maxsplit))
+
+proc strip*(s: string, leading = true, trailing = true,
+            runes: openArray[Rune] = unicodeSpaces): string {.noSideEffect, inline.} =
+  ## Strips leading or trailing ``runes`` from ``s`` and returns
+  ## the resulting string.
+  ##
+  ## If ``leading`` is true (default), leading ``runes`` are stripped.
+  ## If ``trailing`` is true (default), trailing ``runes`` are stripped.
+  ## If both are false, the string is returned unchanged.
+  runnableExamples:
+    let a = "\táñyóng   "
+    doAssert a.strip == "áñyóng"
+    doAssert a.strip(leading = false) == "\táñyóng"
+    doAssert a.strip(trailing = false) == "áñyóng   "
+  strip(toOa(s), leading, trailing, runes)
+
+
+proc align*(s: string, count: Natural, padding = ' '.Rune): string {.noSideEffect, inline.} =
+  ## 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
+  ## returned unchanged. If you need to left align a string use the `alignLeft
+  ## proc <#alignLeft,string,Natural>`_.
+  runnableExamples:
+    assert align("abc", 4) == " abc"
+    assert align("a", 0) == "a"
+    assert align("1232", 6) == "  1232"
+    assert align("1232", 6, '#'.Rune) == "##1232"
+    assert align("Åge", 5) == "  Åge"
+    assert align("×", 4, '_'.Rune) == "___×"
+  align(toOa(s), count, padding)
+
+proc alignLeft*(s: string, count: Natural, padding = ' '.Rune): string {.noSideEffect, inline.} =
+  ## 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
+  ## returned unchanged. If you need to right align a string use the `align
+  ## proc <#align,string,Natural>`_.
+  runnableExamples:
+    assert alignLeft("abc", 4) == "abc "
+    assert alignLeft("a", 0) == "a"
+    assert alignLeft("1232", 6) == "1232  "
+    assert alignLeft("1232", 6, '#'.Rune) == "1232##"
+    assert alignLeft("Åge", 5) == "Åge  "
+    assert alignLeft("×", 4, '_'.Rune) == "×___"
+  alignLeft(toOa(s), count, padding)
diff --git a/lib/pure/unidecode/gen.py b/lib/pure/unidecode/gen.py
index 8da0136ff..2fb69f7b2 100644
--- a/lib/pure/unidecode/gen.py
+++ b/lib/pure/unidecode/gen.py
@@ -1,26 +1,30 @@
-#! usr/bin/env python
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 
 # Generates the unidecode.dat module
 # (c) 2010 Andreas Rumpf
 
 from unidecode import unidecode
+try:
+  import warnings
+  warnings.simplefilter("ignore")
+except ImportError:
+  pass
 
-def main2(): 
-  data = []
-  for x in xrange(128, 0xffff + 1):
-    u = eval("u'\u%04x'" % x)
-    
-    val = unidecode(u)
-    data.append(val)
-    
-    
-  f = open("unidecode.dat", "wb+") 
-  for d in data:
-    f.write("%s\n" % d)
-  f.close()
+def main():
+  f = open("unidecode.dat", "wb+")
+  for x in range(128, 0xffff + 1):
+    u = eval("u'\\u%04x'" % x)
 
+    val = unidecode(u)
 
-main2()
+    # f.write("%x | " % x)
+    if x == 0x2028: # U+2028 = LINE SEPARATOR
+      val = ""
+    elif x == 0x2029: # U+2029 = PARAGRAPH SEPARATOR
+      val = ""
+    f.write("%s\n" % val)
 
+  f.close()
 
+main()
diff --git a/lib/pure/unidecode/unidecode.dat b/lib/pure/unidecode/unidecode.dat
index 9dff0a4a9..5f4c075d8 100644
--- a/lib/pure/unidecode/unidecode.dat
+++ b/lib/pure/unidecode/unidecode.dat
@@ -58,9 +58,9 @@ P
 1
 o
 >>
-1/4
-1/2
-3/4
+ 1/4 
+ 1/2 
+ 3/4 
 ?
 A
 A
@@ -91,7 +91,7 @@ U
 U
 U
 U
-U
+Y
 Th
 ss
 a
@@ -177,7 +177,7 @@ i
 I
 i
 IJ
-
+ij
 J
 j
 K
@@ -368,7 +368,7 @@ ZH
 zh
 j
 DZ
-D
+Dz
 dz
 G
 g
@@ -414,8 +414,8 @@ Y
 y
 H
 h
-[?]
-[?]
+N
+d
 OU
 ou
 Z
@@ -434,34 +434,34 @@ O
 o
 Y
 y
+l
+n
+t
+j
+db
+qp
+A
+C
+c
+L
+T
+s
+z
 [?]
 [?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
+B
+U
+^
+E
+e
+J
+j
+q
+q
+R
+r
+Y
+y
 a
 a
 a
@@ -503,13 +503,13 @@ o
 OE
 O
 F
-R
-R
-R
-R
 r
 r
-R
+r
+r
+r
+r
+r
 R
 R
 s
@@ -519,12 +519,12 @@ S
 S
 t
 t
-U
+u
 U
 v
 ^
-W
-Y
+w
+y
 Y
 z
 z
@@ -556,9 +556,9 @@ ls
 lz
 WW
 ]]
-[?]
-[?]
-k
+h
+h
+h
 h
 j
 r
@@ -737,19 +737,19 @@ V
 
 
 
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
+a
+e
+i
+o
+u
+c
+d
+h
+m
+r
+t
+v
+x
 [?]
 [?]
 [?]
@@ -1287,7 +1287,7 @@ o
 f
 ew
 [?]
-.
+:
 -
 [?]
 [?]
@@ -1340,9 +1340,9 @@ o
 u
 '
 
+-
 
-
-
+|
 
 
 :
@@ -7402,41 +7402,41 @@ bh
 
 
 
+b
+d
+f
+m
+n
+p
+r
+r
+s
+t
+z
+g
 
 
 
 
 
+p
 
 
+b
+d
+f
+g
+k
+l
+m
+n
+p
+r
+s
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+v
+x
+z
 
 
 
@@ -7708,7 +7708,7 @@ a
 S
 [?]
 [?]
-[?]
+Ss
 [?]
 A
 a
@@ -8109,9 +8109,6 @@ _
 
 
 
-
-
-
  
 %0
 %00
@@ -8136,19 +8133,23 @@ _
 /
 -[
 ]-
-[?]
+??
 ?!
 !?
 7
 PP
 (]
 [)
+*
 [?]
 [?]
 [?]
+%
+~
 [?]
 [?]
 [?]
+''''
 [?]
 [?]
 [?]
@@ -8156,12 +8157,8 @@ PP
 [?]
 [?]
 [?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
+ 
+
 [?]
 [?]
 [?]
@@ -8178,7 +8175,7 @@ PP
 
 
 0
-
+i
 
 
 4
@@ -8209,19 +8206,19 @@ n
 (
 )
 [?]
+a
+e
+o
+x
 [?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
+h
+k
+l
+m
+n
+p
+s
+t
 [?]
 [?]
 [?]
@@ -8237,26 +8234,26 @@ Rs
 W
 NS
 D
-EU
+EUR
 K
 T
 Dr
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
+Pf
+P
+G
+A
+UAH
+C|
+L
+Sm
+T
+Rs
+L
+M
+m
+R
+l
+BTC
 [?]
 [?]
 [?]
@@ -8294,6 +8291,7 @@ Dr
 
 
 [?]
+
 [?]
 [?]
 [?]
@@ -8319,63 +8317,67 @@ Dr
 [?]
 [?]
 [?]
-[?]
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
 
+ a/c 
+ a/s 
+C
 
 
+ c/o 
+ c/u 
 
 
 
+g
+H
+H
+H
+h
 
+I
+I
+L
+l
 
+N
+No. 
 
 
+P
+Q
+R
+R
+R
 
 
+(sm)
+TEL
+(tm)
 
+Z
 
 
 
+Z
 
+K
+A
+B
+C
+e
+e
+E
+F
+F
+M
+o
 
 
 
 
+i
 
+FAX
 
 
 
@@ -8385,25 +8387,20 @@ Dr
 [?]
 [?]
 [?]
+D
+d
+e
+i
+j
 [?]
 [?]
 [?]
 [?]
+F
 [?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
+ 1/7 
+ 1/9 
+ 1/10 
  1/3 
  2/3 
  1/5 
@@ -8458,7 +8455,7 @@ D)
 [?]
 [?]
 [?]
-[?]
+ 0/3 
 [?]
 [?]
 [?]
@@ -8595,8 +8592,12 @@ V
 [?]
 [?]
 [?]
+-
 [?]
 [?]
+/
+\
+*
 [?]
 [?]
 [?]
@@ -8608,6 +8609,7 @@ V
 [?]
 [?]
 [?]
+|
 [?]
 [?]
 [?]
@@ -8626,11 +8628,13 @@ V
 [?]
 [?]
 [?]
+:
 [?]
 [?]
 [?]
 [?]
 [?]
+~
 [?]
 [?]
 [?]
@@ -8670,17 +8674,10 @@ V
 [?]
 [?]
 [?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
+<=
+>=
+<=
+>=
 [?]
 [?]
 [?]
@@ -8836,6 +8833,7 @@ V
 [?]
 [?]
 [?]
+^
 [?]
 [?]
 [?]
@@ -8873,9 +8871,8 @@ V
 [?]
 [?]
 [?]
-[?]
-[?]
-[?]
+<
+> 
 [?]
 [?]
 [?]
@@ -9185,166 +9182,166 @@ V
 [?]
 [?]
 [?]
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+(1)
+(2)
+(3)
+(4)
+(5)
+(6)
+(7)
+(8)
+(9)
+(10)
+(11)
+(12)
+(13)
+(14)
+(15)
+(16)
+(17)
+(18)
+(19)
+(20)
+1.
+2.
+3.
+4.
+5.
+6.
+7.
+8.
+9.
+10.
+11.
+12.
+13.
+14.
+15.
+16.
+17.
+18.
+19.
+20.
+(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
+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
+0
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+0
 -
 -
 |
@@ -9712,7 +9709,7 @@ O
 
 
 
-
+#
 
 
 [?]
@@ -9906,6 +9903,7 @@ O
 
 
 
+*
 
 
 
@@ -9944,8 +9942,7 @@ O
 
 
 
-
-
+|
 
 
 
@@ -9955,7 +9952,7 @@ O
 [?]
 [?]
 
-
+!
 
 
 
@@ -10087,10 +10084,10 @@ O
 [?]
 [?]
 [?]
+[
 [?]
-[?]
-[?]
-[?]
+<
+> 
 [?]
 [?]
 [?]
@@ -10500,6 +10497,8 @@ y
 
 
 
+{
+} 
 
 
 
@@ -10739,6 +10738,9 @@ y
 
 
 
+::=
+==
+===
 
 
 
@@ -11228,27 +11230,22 @@ y
 
 
 
+L
+l
+L
+P
+R
+a
+t
+H
+h
+K
+k
+Z
+z
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+M
+A
 
 
 
@@ -12754,21 +12751,21 @@ H
 [?]
 [?]
 [?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
 (g)
 (n)
 (d)
@@ -12850,21 +12847,21 @@ KIS
 (Zi) 
 (Xie) 
 (Ye) 
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
-[?]
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
 1M
 2M
 3M
@@ -12877,10 +12874,10 @@ KIS
 10M
 11M
 12M
-[?]
-[?]
-[?]
-[?]
+Hg
+erg
+eV
+LTD
 a
 i
 u
@@ -13042,16 +13039,16 @@ watt
 22h
 23h
 24h
-HPA
+hPa
 da
 AU
 bar
 oV
 pc
-[?]
-[?]
-[?]
-[?]
+dm
+dm^2
+dm^3
+IU
 Heisei
 Syouwa
 Taisyou
@@ -13092,7 +13089,7 @@ mm^2
 cm^2
 m^2
 km^2
-mm^4
+mm^3
 cm^3
 m^3
 km^3
@@ -13184,7 +13181,7 @@ Wb
 29d
 30d
 31d
-
+gal
 
 
 
@@ -19841,7 +19838,7 @@ Wb
 [?]
 [?]
 
-[?] 
+Yi 
 Ding 
 Kao 
 Qi 
diff --git a/lib/pure/unidecode/unidecode.nim b/lib/pure/unidecode/unidecode.nim
index 9d8843f06..9affc53f6 100644
--- a/lib/pure/unidecode/unidecode.nim
+++ b/lib/pure/unidecode/unidecode.nim
@@ -7,30 +7,31 @@
 #    distribution, for details about the copyright.
 #
 
-## This module is based on Python's Unidecode module by Tomaz Solc,
-## which in turn is based on the ``Text::Unidecode`` Perl module by
-## Sean M. Burke
-## (http://search.cpan.org/~sburke/Text-Unidecode-0.04/lib/Text/Unidecode.pm ).
+## This module is based on Python's [Unidecode](https://pypi.org/project/Unidecode/)
+## module by Tomaz Solc, which in turn is based on the
+## [Text::Unidecode](https://metacpan.org/pod/Text::Unidecode)
+## Perl module by Sean M. Burke.
 ##
-## It provides a single proc that does Unicode to ASCII transliterations:
-## It finds the sequence of ASCII characters that is the closest approximation
-## to the Unicode string.
+## It provides a `unidecode proc <#unidecode,string>`_ that does
+## Unicode to ASCII transliterations: It finds the sequence of ASCII characters
+## that is the closest approximation to the Unicode string.
 ##
 ## For example, the closest to string "Äußerst" in ASCII is "Ausserst". Some
 ## information is lost in this transformation, of course, since several Unicode
-## strings can be transformed in the same ASCII representation. So this is a
-## strictly one-way transformation. However a human reader will probably
-## still be able to guess what original string was meant from the context.
+## strings can be transformed to the same ASCII representation. So this is a
+## strictly one-way transformation. However, a human reader will probably
+## still be able to guess from the context, what the original string was.
 ##
-## This module needs the data file "unidecode.dat" to work: You can either
-## ship this file with your application and initialize this module with the
-## `loadUnidecodeTable` proc or you can define the ``embedUnidecodeTable``
-## symbol to embed the file as a resource into your application.
+## This module needs the data file `unidecode.dat` to work: This file is
+## embedded as a resource into your application by default. You can also
+## define the symbol `--define:noUnidecodeTable` during compile time and
+## use the `loadUnidecodeTable proc <#loadUnidecodeTable>`_ to initialize
+## this module.
 
-import unicode
+import std/unicode
 
-when defined(embedUnidecodeTable):
-  import strutils
+when not defined(noUnidecodeTable):
+  import std/strutils
 
   const translationTable = splitLines(slurp"unidecode/unidecode.dat")
 else:
@@ -38,37 +39,26 @@ else:
   var translationTable: seq[string]
 
 proc loadUnidecodeTable*(datafile = "unidecode.dat") =
-  ## loads the datafile that `unidecode` to work. Unless this module is
-  ## compiled with the ``embedUnidecodeTable`` symbol defined, this needs
-  ## to be called by the main thread before any thread can make a call
-  ## to `unidecode`.
-  when not defined(embedUnidecodeTable):
+  ## Loads the datafile that `unidecode <#unidecode,string>`_ needs to work.
+  ## This is only required if the module was compiled with the
+  ## `--define:noUnidecodeTable` switch. This needs to be called by the
+  ## main thread before any thread can make a call to `unidecode`.
+  when defined(noUnidecodeTable):
     newSeq(translationTable, 0xffff)
     var i = 0
     for line in lines(datafile):
-      translationTable[i] = line.string
+      translationTable[i] = line
       inc(i)
 
 proc unidecode*(s: string): string =
   ## Finds the sequence of ASCII characters that is the closest approximation
   ## to the UTF-8 string `s`.
-  ##
-  ## Example:
-  ##
-  ## ..code-block:: nim
-  ##
-  ##   unidecode("\x53\x17\x4E\xB0")
-  ##
-  ## Results in: "Bei Jing"
-  ##
-  assert(not isNil(translationTable))
+  runnableExamples:
+    doAssert unidecode("北京") == "Bei Jing "
+    doAssert unidecode("Äußerst") == "Ausserst"
+
   result = ""
   for r in runes(s):
     var c = int(r)
     if c <=% 127: add(result, chr(c))
-    elif c <% translationTable.len: add(result, translationTable[c-128])
-
-when isMainModule:
-  loadUnidecodeTable("lib/pure/unidecode/unidecode.dat")
-  assert unidecode("Äußerst") == "Ausserst"
-
+    elif c <% translationTable.len: add(result, translationTable[c - 128])
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index d804ba7c8..cfb762258 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -17,51 +17,59 @@
 ## parent test as failed. Setup and teardown are inherited. Setup can be
 ## overridden locally.
 ##
-## Compiled test files return the number of failed test as exit code, while
-## ``nim c -r <testfile.nim>`` exits with 0 or 1
+## Compiled test files as well as `nim c -r <testfile.nim>`
+## exit with 0 for success (no failed tests) or 1 for failure.
+##
+## Testament
+## =========
+##
+## Instead of `unittest`, please consider using
+## `the Testament tool <testament.html>`_ which offers process isolation for your tests.
+##
+## Alternatively using `when isMainModule: doAssert conditionHere` is usually a
+## much simpler solution for testing purposes.
 ##
 ## Running a single test
 ## =====================
 ##
 ## Specify the test name as a command line argument.
 ##
-## .. code::
-##
+##   ```cmd
 ##   nim c -r test "my test name" "another test"
+##   ```
 ##
 ## Multiple arguments can be used.
 ##
 ## Running a single test suite
 ## ===========================
 ##
-## Specify the suite name delimited by ``"::"``.
-##
-## .. code::
+## Specify the suite name delimited by `"::"`.
 ##
+##   ```cmd
 ##   nim c -r test "my test name::"
+##   ```
 ##
 ## Selecting tests by pattern
 ## ==========================
 ##
 ## A single ``"*"`` can be used for globbing.
 ##
-## Delimit the end of a suite name with ``"::"``.
+## Delimit the end of a suite name with `"::"`.
 ##
 ## Tests matching **any** of the arguments are executed.
 ##
-## .. code::
-##
+##   ```cmd
 ##   nim c -r test fast_suite::mytest1 fast_suite::mytest2
 ##   nim c -r test "fast_suite::mytest*"
 ##   nim c -r test "auth*::" "crypto::hashing*"
 ##   # Run suites starting with 'bug #' and standalone tests starting with '#'
 ##   nim c -r test 'bug #*::' '::#*'
+##   ```
 ##
-## Example
-## -------
-##
-## .. code:: nim
+## Examples
+## ========
 ##
+##   ```nim
 ##   suite "description for this stuff":
 ##     echo "suite setup: run once before the tests"
 ##
@@ -83,19 +91,35 @@
 ##
 ##     test "out of bounds error is thrown on bad access":
 ##       let v = @[1, 2, 3]  # you can do initialization here
-##       expect(IndexError):
+##       expect(IndexDefect):
 ##         discard v[4]
 ##
 ##     echo "suite teardown: run once after the tests"
+##   ```
+##
+## Limitations/Bugs
+## ================
+## Since `check` will rewrite some expressions for supporting checkpoints
+## (namely assigns expressions to variables), some type conversions are not supported.
+## For example `check 4.0 == 2 + 2` won't work. But `doAssert 4.0 == 2 + 2` works.
+## Make sure both sides of the operator (such as `==`, `>=` and so on) have the same type.
+##
+
+import std/private/since
+import std/exitprocs
 
-import
-  macros, strutils, streams, times, sets
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+import std/[macros, strutils, streams, times, sets, sequtils]
 
 when declared(stdout):
-  import os
+  import std/os
+
+const useTerminal = not defined(js)
 
-when not defined(ECMAScript):
-  import terminal
+when useTerminal:
+  import std/terminal
 
 type
   TestStatus* = enum ## The status of a test when it is done.
@@ -103,10 +127,10 @@ type
     FAILED,
     SKIPPED
 
-  OutputLevel* = enum  ## The output verbosity of the tests.
-    PRINT_ALL,         ## Print as much as possible.
-    PRINT_FAILURES,    ## Print only the failed tests.
-    PRINT_NONE         ## Print nothing.
+  OutputLevel* = enum ## The output verbosity of the tests.
+    PRINT_ALL,        ## Print as much as possible.
+    PRINT_FAILURES,   ## Print only the failed tests.
+    PRINT_NONE        ## Print nothing.
 
   TestResult* = object
     suiteName*: string
@@ -121,21 +145,19 @@ type
   ConsoleOutputFormatter* = ref object of OutputFormatter
     colorOutput: bool
       ## Have test results printed in color.
-      ## Default is true for the non-js target,
-      ## for which ``stdout`` is a tty.
-      ## Setting the environment variable
-      ## ``NIMTEST_COLOR`` to ``always`` or
-      ## ``never`` changes the default for the
-      ## non-js target to true or false respectively.
-      ## The deprecated environment variable
-      ## ``NIMTEST_NO_COLOR``, when set,
-      ## changes the defualt to true, if
-      ## ``NIMTEST_COLOR`` is undefined.
+      ## Default is `auto` depending on `isatty(stdout)`, or override it with
+      ## `-d:nimUnittestColor:auto|on|off`.
+      ##
+      ## Deprecated: Setting the environment variable `NIMTEST_COLOR` to `always`
+      ## or `never` changes the default for the non-js target to true or false respectively.
+      ## Deprecated: the environment variable `NIMTEST_NO_COLOR`, when set, changes the
+      ## default to true, if `NIMTEST_COLOR` is undefined.
     outputLevel: OutputLevel
       ## Set the verbosity of test results.
-      ## Default is ``PRINT_ALL``, unless
-      ## the ``NIMTEST_OUTPUT_LVL`` environment
-      ## variable is set for the non-js target.
+      ## Default is `PRINT_ALL`, or override with:
+      ## `-d:nimUnittestOutputLevel:PRINT_ALL|PRINT_FAILURES|PRINT_NONE`.
+      ##
+      ## Deprecated: the `NIMTEST_OUTPUT_LVL` environment variable is set for the non-js target.
     isInSuite: bool
     isInTest: bool
 
@@ -145,28 +167,41 @@ type
     testStartTime: float
     testStackTrace: string
 
-{.deprecated: [TTestStatus: TestStatus, TOutputLevel: OutputLevel]}
-
 var
   abortOnError* {.threadvar.}: bool ## Set to true in order to quit
                                     ## immediately on fail. Default is false,
-                                    ## unless the ``NIMTEST_ABORT_ON_ERROR``
-                                    ## environment variable is set for
-                                    ## the non-js target.
+                                    ## or override with `-d:nimUnittestAbortOnError:on|off`.
+                                    ##
+                                    ## Deprecated: can also override depending on whether
+                                    ## `NIMTEST_ABORT_ON_ERROR` environment variable is set.
 
   checkpoints {.threadvar.}: seq[string]
   formatters {.threadvar.}: seq[OutputFormatter]
   testsFilters {.threadvar.}: HashSet[string]
   disabledParamFiltering {.threadvar.}: bool
 
+const
+  outputLevelDefault = PRINT_ALL
+  nimUnittestOutputLevel {.strdefine.} = $outputLevelDefault
+  nimUnittestColor {.strdefine.} = "auto" ## auto|on|off
+  nimUnittestAbortOnError {.booldefine.} = false
+
+template deprecateEnvVarHere() =
+  # xxx issue a runtime warning to deprecate this envvar.
+  discard
+
+abortOnError = nimUnittestAbortOnError
 when declared(stdout):
-  abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
+  if existsEnv("NIMTEST_ABORT_ON_ERROR"):
+    deprecateEnvVarHere()
+    abortOnError = true
 
 method suiteStarted*(formatter: OutputFormatter, suiteName: string) {.base, gcsafe.} =
   discard
 method testStarted*(formatter: OutputFormatter, testName: string) {.base, gcsafe.} =
   discard
-method failureOccurred*(formatter: OutputFormatter, checkpoints: seq[string], stackTrace: string) {.base, gcsafe.} =
+method failureOccurred*(formatter: OutputFormatter, checkpoints: seq[string],
+    stackTrace: string) {.base, gcsafe.} =
   ## ``stackTrace`` is provided only if the failure occurred due to an exception.
   ## ``checkpoints`` is never ``nil``.
   discard
@@ -176,45 +211,57 @@ method suiteEnded*(formatter: OutputFormatter) {.base, gcsafe.} =
   discard
 
 proc addOutputFormatter*(formatter: OutputFormatter) =
-  if formatters == nil:
-    formatters = @[formatter]
-  else:
-    formatters.add(formatter)
+  formatters.add(formatter)
+
+proc delOutputFormatter*(formatter: OutputFormatter) =
+  keepIf(formatters, proc (x: OutputFormatter): bool =
+    x != formatter)
 
-proc newConsoleOutputFormatter*(outputLevel: OutputLevel = PRINT_ALL,
+proc resetOutputFormatters* {.since: (1, 1).} =
+  formatters = @[]
+
+proc newConsoleOutputFormatter*(outputLevel: OutputLevel = outputLevelDefault,
                                 colorOutput = true): ConsoleOutputFormatter =
   ConsoleOutputFormatter(
     outputLevel: outputLevel,
     colorOutput: colorOutput
   )
 
-proc defaultConsoleFormatter*(): ConsoleOutputFormatter =
+proc colorOutput(): bool =
+  let color = nimUnittestColor
+  case color
+  of "auto":
+    when declared(stdout): result = isatty(stdout)
+    else: result = false
+  of "on": result = true
+  of "off": result = false
+  else: raiseAssert $color
+
   when declared(stdout):
-    # Reading settings
-    # On a terminal this branch is executed
-    var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string
-    var colorOutput  = isatty(stdout)
     if existsEnv("NIMTEST_COLOR"):
-      let colorEnv = getenv("NIMTEST_COLOR")
+      deprecateEnvVarHere()
+      let colorEnv = getEnv("NIMTEST_COLOR")
       if colorEnv == "never":
-        colorOutput = false
+        result = false
       elif colorEnv == "always":
-        colorOutput = true
+        result = true
     elif existsEnv("NIMTEST_NO_COLOR"):
-      colorOutput = false
-    var outputLevel = PRINT_ALL
-    if envOutLvl.len > 0:
-      for opt in countup(low(OutputLevel), high(OutputLevel)):
-        if $opt == envOutLvl:
-          outputLevel = opt
-          break
-    result = newConsoleOutputFormatter(outputLevel, colorOutput)
-  else:
-    result = newConsoleOutputFormatter()
+      deprecateEnvVarHere()
+      result = false
+
+proc defaultConsoleFormatter*(): ConsoleOutputFormatter =
+  var colorOutput = colorOutput()
+  var outputLevel = nimUnittestOutputLevel.parseEnum[:OutputLevel]
+  when declared(stdout):
+    const a = "NIMTEST_OUTPUT_LVL"
+    if existsEnv(a):
+      # xxx issue a warning to deprecate this envvar.
+      outputLevel = getEnv(a).parseEnum[:OutputLevel]
+  result = newConsoleOutputFormatter(outputLevel, colorOutput)
 
 method suiteStarted*(formatter: ConsoleOutputFormatter, suiteName: string) =
   template rawPrint() = echo("\n[Suite] ", suiteName)
-  when not defined(ECMAScript):
+  when useTerminal:
     if formatter.colorOutput:
       styledEcho styleBright, fgBlue, "\n[Suite] ", resetStyle, suiteName
     else: rawPrint()
@@ -224,8 +271,9 @@ method suiteStarted*(formatter: ConsoleOutputFormatter, suiteName: string) =
 method testStarted*(formatter: ConsoleOutputFormatter, testName: string) =
   formatter.isInTest = true
 
-method failureOccurred*(formatter: ConsoleOutputFormatter, checkpoints: seq[string], stackTrace: string) =
-  if stackTrace != nil:
+method failureOccurred*(formatter: ConsoleOutputFormatter,
+                        checkpoints: seq[string], stackTrace: string) =
+  if stackTrace.len > 0:
     echo stackTrace
   let prefix = if formatter.isInSuite: "    " else: ""
   for msg in items(checkpoints):
@@ -234,18 +282,19 @@ method failureOccurred*(formatter: ConsoleOutputFormatter, checkpoints: seq[stri
 method testEnded*(formatter: ConsoleOutputFormatter, testResult: TestResult) =
   formatter.isInTest = false
 
-  if formatter.outputLevel != PRINT_NONE and
-     (formatter.outputLevel == PRINT_ALL or testResult.status == FAILED):
-    let prefix = if testResult.suiteName != nil: "  " else: ""
-    template rawPrint() = echo(prefix, "[", $testResult.status, "] ", testResult.testName)
-    when not defined(ECMAScript):
-      if formatter.colorOutput and not defined(ECMAScript):
+  if formatter.outputLevel != OutputLevel.PRINT_NONE and
+      (formatter.outputLevel == OutputLevel.PRINT_ALL or testResult.status == TestStatus.FAILED):
+    let prefix = if testResult.suiteName.len > 0: "  " else: ""
+    template rawPrint() = echo(prefix, "[", $testResult.status, "] ",
+        testResult.testName)
+    when useTerminal:
+      if formatter.colorOutput:
         var color = case testResult.status
-                    of OK: fgGreen
-                    of FAILED: fgRed
-                    of SKIPPED: fgYellow
-                    else: fgWhite
-        styledEcho styleBright, color, prefix, "[", $testResult.status, "] ", resetStyle, testResult.testName
+          of TestStatus.OK: fgGreen
+          of TestStatus.FAILED: fgRed
+          of TestStatus.SKIPPED: fgYellow
+        styledEcho styleBright, color, prefix, "[", $testResult.status, "] ",
+            resetStyle, testResult.testName
       else:
         rawPrint()
     else:
@@ -297,23 +346,25 @@ method testStarted*(formatter: JUnitOutputFormatter, testName: string) =
   formatter.testStackTrace.setLen(0)
   formatter.testStartTime = epochTime()
 
-method failureOccurred*(formatter: JUnitOutputFormatter, checkpoints: seq[string], stackTrace: string) =
+method failureOccurred*(formatter: JUnitOutputFormatter,
+                        checkpoints: seq[string], stackTrace: string) =
   ## ``stackTrace`` is provided only if the failure occurred due to an exception.
   ## ``checkpoints`` is never ``nil``.
   formatter.testErrors.add(checkpoints)
-  if stackTrace != nil:
+  if stackTrace.len > 0:
     formatter.testStackTrace = stackTrace
 
 method testEnded*(formatter: JUnitOutputFormatter, testResult: TestResult) =
   let time = epochTime() - formatter.testStartTime
   let timeStr = time.formatFloat(ffDecimal, precision = 8)
-  formatter.stream.writeLine("\t\t<testcase name=\"$#\" time=\"$#\">" % [xmlEscape(testResult.testName), timeStr])
-  case testResult.status:
-  of OK:
+  formatter.stream.writeLine("\t\t<testcase name=\"$#\" time=\"$#\">" % [
+      xmlEscape(testResult.testName), timeStr])
+  case testResult.status
+  of TestStatus.OK:
     discard
-  of SKIPPED:
+  of TestStatus.SKIPPED:
     formatter.stream.writeLine("<skipped />")
-  of FAILED:
+  of TestStatus.FAILED:
     let failureMsg = if formatter.testStackTrace.len > 0 and
                         formatter.testErrors.len > 0:
                        xmlEscape(formatter.testErrors[^1])
@@ -324,8 +375,9 @@ method testEnded*(formatter: JUnitOutputFormatter, testResult: TestResult) =
     var errs = ""
     if formatter.testErrors.len > 1:
       var startIdx = if formatter.testStackTrace.len > 0: 0 else: 1
-      var endIdx = if formatter.testStackTrace.len > 0: formatter.testErrors.len - 2
-                   else: formatter.testErrors.len - 1
+      var endIdx = if formatter.testStackTrace.len > 0:
+          formatter.testErrors.len - 2
+        else: formatter.testErrors.len - 1
 
       for errIdx in startIdx..endIdx:
         if errs.len > 0:
@@ -333,11 +385,13 @@ method testEnded*(formatter: JUnitOutputFormatter, testResult: TestResult) =
         errs.add(xmlEscape(formatter.testErrors[errIdx]))
 
     if formatter.testStackTrace.len > 0:
-      formatter.stream.writeLine("\t\t\t<error message=\"$#\">$#</error>" % [failureMsg, xmlEscape(formatter.testStackTrace)])
+      formatter.stream.writeLine("\t\t\t<error message=\"$#\">$#</error>" % [
+          failureMsg, xmlEscape(formatter.testStackTrace)])
       if errs.len > 0:
         formatter.stream.writeLine("\t\t\t<system-err>$#</system-err>" % errs)
     else:
-      formatter.stream.writeLine("\t\t\t<failure message=\"$#\">$#</failure>" % [failureMsg, errs])
+      formatter.stream.writeLine("\t\t\t<failure message=\"$#\">$#</failure>" %
+          [failureMsg, errs])
 
   formatter.stream.writeLine("\t\t</testcase>")
 
@@ -352,15 +406,16 @@ proc glob(matcher, filter: string): bool =
   if not filter.contains('*'):
     return matcher == filter
 
-  let beforeAndAfter = filter.split('*', maxsplit=1)
+  let beforeAndAfter = filter.split('*', maxsplit = 1)
   if beforeAndAfter.len == 1:
     # "foo*"
-    return matcher.startswith(beforeAndAfter[0])
+    return matcher.startsWith(beforeAndAfter[0])
 
   if matcher.len < filter.len - 1:
-    return false  # "12345" should not match "123*345"
+    return false # "12345" should not match "123*345"
 
-  return matcher.startsWith(beforeAndAfter[0]) and matcher.endsWith(beforeAndAfter[1])
+  return matcher.startsWith(beforeAndAfter[0]) and matcher.endsWith(
+      beforeAndAfter[1])
 
 proc matchFilter(suiteName, testName, filter: string): bool =
   if filter == "":
@@ -368,16 +423,15 @@ proc matchFilter(suiteName, testName, filter: string): bool =
   if testName == filter:
     # corner case for tests containing "::" in their name
     return true
-  let suiteAndTestFilters = filter.split("::", maxsplit=1)
+  let suiteAndTestFilters = filter.split("::", maxsplit = 1)
 
   if suiteAndTestFilters.len == 1:
     # no suite specified
-    let test_f = suiteAndTestFilters[0]
-    return glob(testName, test_f)
+    let testFilter = suiteAndTestFilters[0]
+    return glob(testName, testFilter)
 
-  return glob(suiteName, suiteAndTestFilters[0]) and glob(testName, suiteAndTestFilters[1])
-
-when defined(testing): export matchFilter
+  return glob(suiteName, suiteAndTestFilters[0]) and
+         glob(testName, suiteAndTestFilters[1])
 
 proc shouldRun(currentSuiteName, testName: string): bool =
   ## Check if a test should be run by matching suiteName and testName against
@@ -392,11 +446,10 @@ proc shouldRun(currentSuiteName, testName: string): bool =
   return false
 
 proc ensureInitialized() =
-  if formatters == nil:
+  if formatters.len == 0:
     formatters = @[OutputFormatter(defaultConsoleFormatter())]
 
-  if not disabledParamFiltering and not testsFilters.isValid:
-    testsFilters.init()
+  if not disabledParamFiltering:
     when declared(paramCount):
       # Read tests to run from the command line.
       for i in 1 .. paramCount():
@@ -420,27 +473,26 @@ template suite*(name, body) {.dirty.} =
   ## common fixture (``setup``, ``teardown``). The fixture is executed
   ## for EACH test.
   ##
-  ## .. code-block:: nim
-  ##  suite "test suite for addition":
-  ##    setup:
-  ##      let result = 4
+  ##   ```nim
+  ##   suite "test suite for addition":
+  ##     setup:
+  ##       let result = 4
   ##
-  ##    test "2 + 2 = 4":
-  ##      check(2+2 == result)
+  ##     test "2 + 2 = 4":
+  ##       check(2+2 == result)
   ##
-  ##    test "(2 + -2) != 4":
-  ##      check(2 + -2 != result)
+  ##     test "(2 + -2) != 4":
+  ##       check(2 + -2 != result)
   ##
-  ##    # No teardown needed
+  ##     # No teardown needed
+  ##   ```
   ##
   ## The suite will run the individual test cases in the order in which
   ## they were listed. With default global settings the above code prints:
   ##
-  ## .. code-block::
-  ##
-  ##  [Suite] test suite for addition
-  ##    [OK] 2 + 2 = 4
-  ##    [OK] (2 + -2) != 4
+  ##     [Suite] test suite for addition
+  ##       [OK] 2 + 2 = 4
+  ##       [OK] (2 + -2) != 4
   bind formatters, ensureInitialized, suiteEnded
 
   block:
@@ -462,71 +514,81 @@ template suite*(name, body) {.dirty.} =
     finally:
       suiteEnded()
 
-template exceptionTypeName(e: typed): string = $e.name
+proc exceptionTypeName(e: ref Exception): string {.inline.} =
+  if e == nil: "<foreign exception>"
+  else: $e.name
+
+when not declared(setProgramResult):
+  {.warning: "setProgramResult not available on platform, unittest will not" &
+    " give failing exit code on test failure".}
+  template setProgramResult(a: int) =
+    discard
 
 template test*(name, body) {.dirty.} =
   ## Define a single test case identified by `name`.
   ##
-  ## .. code-block:: nim
-  ##
-  ##  test "roses are red":
-  ##    let roses = "red"
-  ##    check(roses == "red")
+  ##   ```nim
+  ##   test "roses are red":
+  ##     let roses = "red"
+  ##     check(roses == "red")
+  ##   ```
   ##
   ## The above code outputs:
   ##
-  ## .. code-block::
-  ##
-  ##  [OK] roses are red
-  bind shouldRun, checkpoints, formatters, ensureInitialized, testEnded, exceptionTypeName
+  ##     [OK] roses are red
+  bind shouldRun, checkpoints, formatters, ensureInitialized, testEnded, exceptionTypeName, setProgramResult
 
   ensureInitialized()
 
   if shouldRun(when declared(testSuiteName): testSuiteName else: "", name):
     checkpoints = @[]
-    var testStatusIMPL {.inject.} = OK
+    var testStatusIMPL {.inject.} = TestStatus.OK
 
     for formatter in formatters:
       formatter.testStarted(name)
 
+    {.push warning[BareExcept]:off.}
     try:
       when declared(testSetupIMPLFlag): testSetupIMPL()
       when declared(testTeardownIMPLFlag):
         defer: testTeardownIMPL()
+      {.push warning[BareExcept]:on.}
       body
+      {.pop.}
 
     except:
-      when not defined(js):
-        let e = getCurrentException()
-        let eTypeDesc = "[" & exceptionTypeName(e) & "]"
-        checkpoint("Unhandled exception: " & getCurrentExceptionMsg() & " " & eTypeDesc)
+      let e = getCurrentException()
+      let eTypeDesc = "[" & exceptionTypeName(e) & "]"
+      checkpoint("Unhandled exception: " & getCurrentExceptionMsg() & " " & eTypeDesc)
+      if e == nil: # foreign
+        fail()
+      else:
         var stackTrace {.inject.} = e.getStackTrace()
-      fail()
+        fail()
 
     finally:
-      if testStatusIMPL == FAILED:
-        programResult += 1
+      if testStatusIMPL == TestStatus.FAILED:
+        setProgramResult 1
       let testResult = TestResult(
-        suiteName: when declared(testSuiteName): testSuiteName else: nil,
+        suiteName: when declared(testSuiteName): testSuiteName else: "",
         testName: name,
         status: testStatusIMPL
       )
       testEnded(testResult)
       checkpoints = @[]
+    {.pop.}
 
 proc checkpoint*(msg: string) =
   ## Set a checkpoint identified by `msg`. Upon test failure all
   ## checkpoints encountered so far are printed out. Example:
   ##
-  ## .. code-block:: nim
-  ##
-  ##  checkpoint("Checkpoint A")
-  ##  check((42, "the Answer to life and everything") == (1, "a"))
-  ##  checkpoint("Checkpoint B")
+  ##   ```nim
+  ##   checkpoint("Checkpoint A")
+  ##   check((42, "the Answer to life and everything") == (1, "a"))
+  ##   checkpoint("Checkpoint B")
+  ##   ```
   ##
   ## outputs "Checkpoint A" once it fails.
-  if checkpoints == nil:
-    checkpoints = @[]
   checkpoints.add(msg)
   # TODO: add support for something like SCOPED_TRACE from Google Test
 
@@ -536,19 +598,18 @@ template fail* =
   ## failed (change exit code and test status). This template is useful
   ## for debugging, but is otherwise mostly used internally. Example:
   ##
-  ## .. code-block:: nim
-  ##
-  ##  checkpoint("Checkpoint A")
-  ##  complicatedProcInThread()
-  ##  fail()
+  ##   ```nim
+  ##   checkpoint("Checkpoint A")
+  ##   complicatedProcInThread()
+  ##   fail()
+  ##   ```
   ##
   ## outputs "Checkpoint A" before quitting.
-  bind ensureInitialized
-
+  bind ensureInitialized, setProgramResult
   when declared(testStatusIMPL):
-    testStatusIMPL = FAILED
+    testStatusIMPL = TestStatus.FAILED
   else:
-    programResult += 1
+    setProgramResult 1
 
   ensureInitialized()
 
@@ -557,10 +618,9 @@ template fail* =
     when declared(stackTrace):
       formatter.failureOccurred(checkpoints, stackTrace)
     else:
-      formatter.failureOccurred(checkpoints, nil)
+      formatter.failureOccurred(checkpoints, "")
 
-  when not defined(ECMAScript):
-    if abortOnError: quit(programResult)
+  if abortOnError: quit(1)
 
   checkpoints = @[]
 
@@ -570,33 +630,30 @@ template skip* =
   ## for reasons depending on outer environment,
   ## or certain application logic conditions or configurations.
   ## The test code is still executed.
-  ##
-  ## .. code-block:: nim
-  ##
-  ##  if not isGLConextCreated():
-  ##    skip()
+  ##   ```nim
+  ##   if not isGLContextCreated():
+  ##     skip()
+  ##   ```
   bind checkpoints
 
-  testStatusIMPL = SKIPPED
+  testStatusIMPL = TestStatus.SKIPPED
   checkpoints = @[]
 
 macro check*(conditions: untyped): untyped =
   ## Verify if a statement or a list of statements is true.
   ## A helpful error message and set checkpoints are printed out on
   ## failure (if ``outputLevel`` is not ``PRINT_NONE``).
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##  import strutils
-  ##
-  ##  check("AKB48".toLowerAscii() == "akb48")
-  ##
-  ##  let teams = {'A', 'K', 'B', '4', '8'}
-  ##
-  ##  check:
-  ##    "AKB48".toLowerAscii() == "akb48"
-  ##    'C' in teams
+  runnableExamples:
+    import std/strutils
+
+    check("AKB48".toLowerAscii() == "akb48")
+
+    let teams = {'A', 'K', 'B', '4', '8'}
+
+    check:
+      "AKB48".toLowerAscii() == "akb48"
+      'C' notin teams
+
   let checked = callsite()[1]
 
   template asgn(a: untyped, value: typed) =
@@ -614,7 +671,7 @@ macro check*(conditions: untyped): untyped =
 
     var counter = 0
 
-    if exp[0].kind == nnkIdent and
+    if exp[0].kind in {nnkIdent, nnkOpenSymChoice, nnkClosedSymChoice, nnkSym} and
         $exp[0] in ["not", "in", "notin", "==", "<=",
                     ">=", "<", ">", "!=", "is", "isnot"]:
 
@@ -625,17 +682,18 @@ macro check*(conditions: untyped): untyped =
           let paramAst = exp[i]
           if exp[i].kind == nnkIdent:
             result.printOuts.add getAst(print(argStr, paramAst))
-          if exp[i].kind in nnkCallKinds + { nnkDotExpr, nnkBracketExpr }:
+          if exp[i].kind in nnkCallKinds + {nnkDotExpr, nnkBracketExpr, nnkPar} and
+                  (exp[i].typeKind notin {ntyTypeDesc} or $exp[0] notin ["is", "isnot"]):
             let callVar = newIdentNode(":c" & $counter)
             result.assigns.add getAst(asgn(callVar, paramAst))
             result.check[i] = callVar
             result.printOuts.add getAst(print(argStr, callVar))
           if exp[i].kind == nnkExprEqExpr:
             # ExprEqExpr
-            #   Ident !"v"
+            #   Ident "v"
             #   IntLit 2
             result.check[i] = exp[i][1]
-          if exp[i].typekind notin {ntyTypeDesc}:
+          if exp[i].typeKind notin {ntyTypeDesc}:
             let arg = newIdentNode(":p" & $counter)
             result.assigns.add getAst(asgn(arg, paramAst))
             result.printOuts.add getAst(print(argStr, arg))
@@ -648,12 +706,14 @@ macro check*(conditions: untyped): untyped =
   of nnkCallKinds:
 
     let (assigns, check, printOuts) = inspectArgs(checked)
-    let lineinfo = newStrLitNode(checked.lineinfo)
+    let lineinfo = newStrLitNode(checked.lineInfo)
     let callLit = checked.toStrLit
     result = quote do:
       block:
         `assigns`
-        if not `check`:
+        if `check`:
+          discard
+        else:
           checkpoint(`lineinfo` & ": Check failed: " & `callLit`)
           `printOuts`
           fail()
@@ -662,14 +722,16 @@ macro check*(conditions: untyped): untyped =
     result = newNimNode(nnkStmtList)
     for node in checked:
       if node.kind != nnkCommentStmt:
-        result.add(newCall(!"check", node))
+        result.add(newCall(newIdentNode("check"), node))
 
   else:
-    let lineinfo = newStrLitNode(checked.lineinfo)
+    let lineinfo = newStrLitNode(checked.lineInfo)
     let callLit = checked.toStrLit
 
     result = quote do:
-      if not `checked`:
+      if `checked`:
+        discard
+      else:
         checkpoint(`lineinfo` & ": Check failed: " & `callLit`)
         fail()
 
@@ -687,40 +749,40 @@ macro expect*(exceptions: varargs[typed], body: untyped): untyped =
   ## Test if `body` raises an exception found in the passed `exceptions`.
   ## The test passes if the raised exception is part of the acceptable
   ## exceptions. Otherwise, it fails.
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##  import math, random
-  ##  proc defectiveRobot() =
-  ##    randomize()
-  ##    case random(1..4)
-  ##    of 1: raise newException(OSError, "CANNOT COMPUTE!")
-  ##    of 2: discard parseInt("Hello World!")
-  ##    of 3: raise newException(IOError, "I can't do that Dave.")
-  ##    else: assert 2 + 2 == 5
-  ##
-  ##  expect IOError, OSError, ValueError, AssertionError:
-  ##    defectiveRobot()
-  let exp = callsite()
+  runnableExamples:
+    import std/[math, random, strutils]
+    proc defectiveRobot() =
+      randomize()
+      case rand(1..4)
+      of 1: raise newException(OSError, "CANNOT COMPUTE!")
+      of 2: discard parseInt("Hello World!")
+      of 3: raise newException(IOError, "I can't do that Dave.")
+      else: assert 2 + 2 == 5
+
+    expect IOError, OSError, ValueError, AssertionDefect:
+      defectiveRobot()
+
   template expectBody(errorTypes, lineInfoLit, body): NimNode {.dirty.} =
+    {.push warning[BareExcept]:off.}
     try:
+      {.push warning[BareExcept]:on.}
       body
+      {.pop.}
       checkpoint(lineInfoLit & ": Expect Failed, no exception was thrown.")
       fail()
     except errorTypes:
       discard
     except:
-      checkpoint(lineInfoLit & ": Expect Failed, unexpected exception was thrown.")
+      let err = getCurrentException()
+      checkpoint(lineInfoLit & ": Expect Failed, " & $err.name & " was thrown.")
       fail()
-
-  var body = exp[exp.len - 1]
+    {.pop.}
 
   var errorTypes = newNimNode(nnkBracket)
-  for i in countup(1, exp.len - 2):
-    errorTypes.add(exp[i])
+  for exp in exceptions:
+    errorTypes.add(exp)
 
-  result = getAst(expectBody(errorTypes, exp.lineinfo, body))
+  result = getAst(expectBody(errorTypes, errorTypes.lineInfo, body))
 
 proc disableParamFiltering* =
   ## disables filtering tests with the command line params
diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim
index dd8040928..725d5bbd9 100644
--- a/lib/pure/uri.nim
+++ b/lib/pure/uri.nim
@@ -8,8 +8,43 @@
 #
 
 ## 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.
+##
+## .. warning:: URI parsers in this module do not perform security validation.
+##
+## # Basic usage
+
+
+## ## Combine URIs
+runnableExamples:
+  let host = parseUri("https://nim-lang.org")
+  assert $host == "https://nim-lang.org"
+  assert $(host / "/blog.html") == "https://nim-lang.org/blog.html"
+  assert $(host / "blog2.html") == "https://nim-lang.org/blog2.html"
+
+## ## Access URI item
+runnableExamples:
+  let res = parseUri("sftp://127.0.0.1:4343")
+  assert isAbsolute(res)
+  assert res.port == "4343"
+
+## ## Data URI Base64
+runnableExamples:
+  assert getDataUri("Hello World", "text/plain") == "data:text/plain;charset=utf-8;base64,SGVsbG8gV29ybGQ="
+  assert getDataUri("Nim", "text/plain") == "data:text/plain;charset=utf-8;base64,Tmlt"
+
+
+import std/[strutils, parseutils, base64]
+import std/private/[since, decode_helpers]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 
-import strutils, parseutils
 type
   Url* = distinct string
 
@@ -17,70 +52,63 @@ type
     scheme*, username*, password*: string
     hostname*, port*, path*, query*, anchor*: string
     opaque*: bool
+    isIpv6*: bool
 
-{.push warning[deprecated]: off.}
-proc `$`*(url: Url): string {.deprecated: "use Uri instead".} =
-  ## **Deprecated since 0.9.6**: Use ``Uri`` instead.
-  return string(url)
+  UriParseError* = object of ValueError
 
-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 uriParseError*(msg: string) {.noreturn.} =
+  ## Raises a `UriParseError` exception with message `msg`.
+  raise newException(UriParseError, msg)
 
-proc encodeUrl*(s: string, usePlus=true): string =
+func 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
+  ## `{'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'``.
+  ## As a special rule, when the value of `usePlus` is true,
+  ## spaces are encoded as `+` instead of `%20`.
+  ##
+  ## **See also:**
+  ## * `decodeUrl func<#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:
     case c
+    # https://tools.ietf.org/html/rfc3986#section-2.3
     of 'a'..'z', 'A'..'Z', '0'..'9', '-', '.', '_', '~': add(result, c)
     of ' ': add(result, fromSpace)
     else:
       add(result, '%')
       add(result, toHex(ord(c), 2))
 
-proc decodeUrl*(s: string, decodePlus=true): string =
+func decodeUrl*(s: string, decodePlus = true): string =
   ## Decodes a URL according to RFC3986.
   ##
-  ## This means that any ``'%xx'`` (where ``xx`` denotes a hexadecimal
-  ## value) are converted to the character with ordinal number ``xx``,
+  ## 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.
+  ## If `xx` is not a valid hexadecimal value, it is left intact.
   ##
-  ## 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.
-  proc handleHexChar(c: char, x: var int) {.inline.} =
-    case c
-    of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
-    of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
-    of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
-    else: assert(false)
+  ##
+  ## **See also:**
+  ## * `encodeUrl func<#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"
+    assert decodeUrl("abc%xyz") == "abc%xyz"
 
   result = newString(s.len)
   var i = 0
@@ -88,11 +116,7 @@ proc decodeUrl*(s: string, decodePlus=true): string =
   while i < s.len:
     case s[i]
     of '%':
-      var x = 0
-      handleHexChar(s[i+1], x)
-      handleHexChar(s[i+2], x)
-      inc(i, 2)
-      result[j] = chr(x)
+      result[j] = decodePercent(s, i)
     of '+':
       if decodePlus:
         result[j] = ' '
@@ -103,7 +127,81 @@ proc decodeUrl*(s: string, decodePlus=true): string =
     inc(j)
   setLen(result, j)
 
-proc parseAuthority(authority: string, result: var Uri) =
+func encodeQuery*(query: openArray[(string, string)], usePlus = true,
+    omitEq = true, sep = '&'): string =
+  ## Encodes a set of (key, value) parameters into a URL query string.
+  ##
+  ## Every (key, value) pair is URL-encoded and written as `key=value`. If the
+  ## value is an empty string then the `=` is omitted, unless `omitEq` is
+  ## false.
+  ## The pairs are joined together by the `sep` character.
+  ##
+  ## The `usePlus` parameter is passed down to the `encodeUrl` function that
+  ## is used for the URL encoding of the string values.
+  ##
+  ## **See also:**
+  ## * `encodeUrl func<#encodeUrl,string>`_
+  runnableExamples:
+    assert encodeQuery({: }) == ""
+    assert encodeQuery({"a": "1", "b": "2"}) == "a=1&b=2"
+    assert encodeQuery({"a": "1", "b": ""}) == "a=1&b"
+    assert encodeQuery({"a": "1", "b": ""}, omitEq = false, sep = ';') == "a=1;b="
+  for elem in query:
+    # Encode the `key = value` pairs and separate them with 'sep'
+    if result.len > 0: result.add(sep)
+    let (key, val) = elem
+    result.add(encodeUrl(key, usePlus))
+    # Omit the '=' if the value string is empty
+    if not omitEq or val.len > 0:
+      result.add('=')
+      result.add(encodeUrl(val, usePlus))
+
+iterator decodeQuery*(data: string, sep = '&'): tuple[key, value: string] =
+  ## Reads and decodes the query string `data` and yields the `(key, value)` pairs
+  ## the data consists of. If compiled with `-d:nimLegacyParseQueryStrict`,
+  ## a `UriParseError` is raised when there is an unencoded `=` character in a decoded
+  ## value, which was the behavior in Nim < 1.5.1.
+  runnableExamples:
+    import std/sequtils
+    assert toSeq(decodeQuery("foo=1&bar=2=3")) == @[("foo", "1"), ("bar", "2=3")]
+    assert toSeq(decodeQuery("foo=1;bar=2=3", ';')) == @[("foo", "1"), ("bar", "2=3")]
+    assert toSeq(decodeQuery("&a&=b&=&&")) == @[("", ""), ("a", ""), ("", "b"), ("", ""), ("", "")]
+
+  proc parseData(data: string, i: int, field: var string, sep: char): int =
+    result = i
+    while result < data.len:
+      let c = data[result]
+      case c
+      of '%': add(field, decodePercent(data, result))
+      of '+': add(field, ' ')
+      of '&': break
+      else:
+        if c == sep: break
+        else: add(field, data[result])
+      inc(result)
+
+  var i = 0
+  var name = ""
+  var value = ""
+  # decode everything in one pass:
+  while i < data.len:
+    setLen(name, 0) # reuse memory
+    i = parseData(data, i, name, '=')
+    setLen(value, 0) # reuse memory
+    if i < data.len and data[i] == '=':
+      inc(i) # skip '='
+      when defined(nimLegacyParseQueryStrict):
+        i = parseData(data, i, value, '=')
+      else:
+        i = parseData(data, i, value, sep)
+    yield (name, value)
+    if i < data.len:
+      when defined(nimLegacyParseQueryStrict):
+        if data[i] != '&':
+          uriParseError("'&' expected at index '$#' for '$#'" % [$i, data])
+      inc(i)
+
+func parseAuthority(authority: string, result: var Uri) =
   var i = 0
   var inPort = false
   var inIPv6 = false
@@ -122,6 +220,7 @@ proc parseAuthority(authority: string, result: var Uri) =
         inPort = true
     of '[':
       inIPv6 = true
+      result.isIpv6 = true
     of ']':
       inIPv6 = false
     else:
@@ -131,8 +230,7 @@ proc parseAuthority(authority: string, result: var Uri) =
         result.hostname.add(authority[i])
     i.inc
 
-proc parsePath(uri: string, i: var int, result: var Uri) =
-
+func parsePath(uri: string, i: var int, result: var Uri) =
   i.inc parseUntil(uri, result.path, {'?', '#'}, i)
 
   # The 'mailto' scheme's PATH actually contains the hostname/username
@@ -148,28 +246,48 @@ proc parsePath(uri: string, i: var int, result: var Uri) =
     i.inc # Skip '#'
     i.inc parseUntil(uri, result.anchor, {}, i)
 
-proc initUri*(): Uri =
-  ## Initializes a URI.
+func initUri*(isIpv6 = false): Uri =
+  ## Initializes a URI with `scheme`, `username`, `password`,
+  ## `hostname`, `port`, `path`, `query`, `anchor` and `isIpv6`.
+  ##
+  ## **See also:**
+  ## * `Uri type <#Uri>`_ for available fields in the URI type
+  runnableExamples:
+    var uri2 = initUri(isIpv6 = true)
+    uri2.scheme = "tcp"
+    uri2.hostname = "2001:0db8:85a3:0000:0000:8a2e:0370:7334"
+    uri2.port = "8080"
+    assert $uri2 == "tcp://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080"
   result = Uri(scheme: "", username: "", password: "", hostname: "", port: "",
-                path: "", query: "", anchor: "")
+                path: "", query: "", anchor: "", isIpv6: isIpv6)
 
-proc resetUri(uri: var Uri) =
+func resetUri(uri: var Uri) =
   for f in uri.fields:
     when f is string:
       f.setLen(0)
     else:
       f = false
 
-proc parseUri*(uri: string, result: var Uri) =
+func 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 func <#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
 
   # Check if this is a reference URI (relative URI)
-  let doubleSlash = uri.len > 1 and uri[1] == '/'
+  let doubleSlash = uri.len > 1 and uri[0] == '/' and uri[1] == '/'
   if i < uri.len and uri[i] == '/':
-    # Make sure ``uri`` doesn't begin with '//'.
+    # Make sure `uri` doesn't begin with '//'.
     if not doubleSlash:
       parsePath(uri, i, result)
       return
@@ -198,15 +316,31 @@ proc parseUri*(uri: string, result: var Uri) =
   # Path
   parsePath(uri, i, result)
 
-proc parseUri*(uri: string): Uri =
+func 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)
 
-proc removeDotSegments(path: string): string =
+func removeDotSegments(path: string): string =
+  ## Collapses `..` and `.` in `path` in a similar way as done in `os.normalizedPath`
+  ## Caution: this is buggy.
+  runnableExamples:
+    assert removeDotSegments("a1/a2/../a3/a4/a5/./a6/a7/.//./") == "a1/a3/a4/a5/a6/a7/"
+    assert removeDotSegments("http://www.ai.") == "http://www.ai."
+  # xxx adapt or reuse `pathnorm.normalizePath(path, '/')` to make this more reliable, but
+  # taking into account url specificities such as not collapsing leading `//` in scheme
+  # `https://`. see `turi` for failing tests.
   if path.len == 0: return ""
   var collection: seq[string] = @[]
-  let endsWithSlash = path[path.len-1] == '/'
+  let endsWithSlash = path.endsWith '/'
   var i = 0
   var currentSegment = ""
   while i < path.len:
@@ -220,7 +354,7 @@ proc removeDotSegments(path: string): string =
           discard collection.pop()
           i.inc 3
           continue
-      elif path[i+1] == '/':
+      elif i + 1 < path.len and path[i+1] == '/':
         i.inc 2
         continue
       currentSegment.add path[i]
@@ -233,7 +367,7 @@ proc removeDotSegments(path: string): string =
   result = collection.join("/")
   if endsWithSlash: result.add '/'
 
-proc merge(base, reference: Uri): string =
+func merge(base, reference: Uri): string =
   # http://tools.ietf.org/html/rfc3986#section-5.2.3
   if base.hostname != "" and base.path == "":
     '/' & reference.path
@@ -244,28 +378,24 @@ proc merge(base, reference: Uri): string =
     else:
       base.path[0 .. lastSegment] & reference.path
 
-proc combine*(base: Uri, reference: Uri): Uri =
+func combine*(base: Uri, reference: Uri): Uri =
   ## Combines a base URI with a reference 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:
+  ## This means that the slashes inside the base URIs path as well as reference
+  ## URIs path affect the resulting URI.
   ##
-  ## .. 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"
-  ##
-  ##   let bar = combine(parseUri("http://example.com/foo/bar/"), parseUri("baz"))
-  ##   assert bar.path == "/foo/bar/baz"
+  ## **See also:**
+  ## * `/ func <#/,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
@@ -299,34 +429,43 @@ proc combine*(base: Uri, reference: Uri): Uri =
     result.scheme = base.scheme
   result.anchor = reference.anchor
 
-proc combine*(uris: varargs[Uri]): Uri =
+func combine*(uris: varargs[Uri]): Uri =
   ## Combines multiple URIs together.
+  ##
+  ## **See also:**
+  ## * `/ func <#/,Uri,string>`_ for building URIs
+  runnableExamples:
+    let foo = combine(parseUri("https://nim-lang.org/"), parseUri("docs/"),
+        parseUri("manual.html"))
+    assert foo.hostname == "nim-lang.org"
+    assert foo.path == "/docs/manual.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
+func isAbsolute*(uri: Uri): bool =
+  ## Returns true if URI is absolute, false otherwise.
+  runnableExamples:
+    assert parseUri("https://nim-lang.org").isAbsolute
+    assert not parseUri("nim-lang").isAbsolute
   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.
+func `/`*(x: Uri, path: string): Uri =
+  ## 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 func <#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 func <#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:
@@ -345,285 +484,89 @@ proc `/`*(x: Uri, path: string): Uri =
       result.path.add '/'
     result.path.add(path)
 
-proc `$`*(u: Uri): string =
+func `?`*(u: Uri, query: openArray[(string, string)]): Uri =
+  ## Concatenates the query parameters to the specified URI object.
+  runnableExamples:
+    let foo = parseUri("https://example.com") / "foo" ? {"bar": "qux"}
+    assert $foo == "https://example.com/foo?bar=qux"
+  result = u
+  result.query = encodeQuery(query)
+
+func `$`*(u: Uri): string =
   ## Returns the string representation of the specified URI object.
-  result = ""
-  if u.scheme.len > 0:
-    result.add(u.scheme)
-    if u.opaque:
-      result.add(":")
+  runnableExamples:
+    assert $parseUri("https://nim-lang.org") == "https://nim-lang.org"
+  # Get the len of all the parts.
+  let schemeLen = u.scheme.len
+  let usernameLen = u.username.len
+  let passwordLen = u.password.len
+  let hostnameLen = u.hostname.len
+  let portLen = u.port.len
+  let pathLen = u.path.len
+  let queryLen = u.query.len
+  let anchorLen = u.anchor.len
+  # Prepare a string that fits all the parts and all punctuation chars.
+  # 12 is the max len required by all possible punctuation chars.
+  result = newStringOfCap(
+    schemeLen + usernameLen + passwordLen + hostnameLen + portLen + pathLen + queryLen + anchorLen + 12
+  )
+  # Insert to result.
+  if schemeLen > 0:
+    result.add u.scheme
+    result.add ':'
+    if not u.opaque:
+      result.add '/'
+      result.add '/'
+  if usernameLen > 0:
+    result.add u.username
+    if passwordLen > 0:
+      result.add ':'
+      result.add u.password
+    result.add '@'
+  if u.hostname.endsWith('/'):
+    if u.isIpv6:
+      result.add '['
+      result.add u.hostname[0 .. ^2]
+      result.add ']'
     else:
-      result.add("://")
-  if u.username.len > 0:
-    result.add(u.username)
-    if u.password.len > 0:
-      result.add(":")
-      result.add(u.password)
-    result.add("@")
-  if u.hostname.endswith('/'):
-    result.add(u.hostname[0..^2])
+      result.add u.hostname[0 .. ^2]
   else:
-    result.add(u.hostname)
-  if u.port.len > 0:
-    result.add(":")
-    result.add(u.port)
-  if u.path.len > 0:
-    if u.hostname.len > 0 and u.path[0] != '/':
-      result.add('/')
-    result.add(u.path)
-  if u.query.len > 0:
-    result.add("?")
-    result.add(u.query)
-  if u.anchor.len > 0:
-    result.add("#")
-    result.add(u.anchor)
-
-when isMainModule:
-  block:
-    const test1 = "abc\L+def xyz"
-    doAssert encodeUrl(test1) == "abc%0A%2Bdef+xyz"
-    doAssert decodeUrl(encodeUrl(test1)) == test1
-    doAssert encodeUrl(test1, false) == "abc%0A%2Bdef%20xyz"
-    doAssert decodeUrl(encodeUrl(test1, false), false) == test1
-    doAssert decodeUrl(encodeUrl(test1)) == test1
-
-  block:
-    let str = "http://localhost"
-    let test = parseUri(str)
-    doAssert test.path == ""
-
-  block:
-    let str = "http://localhost/"
-    let test = parseUri(str)
-    doAssert test.path == "/"
-
-  block:
-    let str = "http://localhost:8080/test"
-    let test = parseUri(str)
-    doAssert test.scheme == "http"
-    doAssert test.port == "8080"
-    doAssert test.path == "/test"
-    doAssert test.hostname == "localhost"
-    doAssert($test == str)
-
-  block:
-    let str = "foo://username:password@example.com:8042/over/there" &
-              "/index.dtb?type=animal&name=narwhal#nose"
-    let test = parseUri(str)
-    doAssert test.scheme == "foo"
-    doAssert test.username == "username"
-    doAssert test.password == "password"
-    doAssert test.hostname == "example.com"
-    doAssert test.port == "8042"
-    doAssert test.path == "/over/there/index.dtb"
-    doAssert test.query == "type=animal&name=narwhal"
-    doAssert test.anchor == "nose"
-    doAssert($test == str)
-
-  block:
-    # IPv6 address
-    let str = "foo://[::1]:1234/bar?baz=true&qux#quux"
-    let uri = parseUri(str)
-    doAssert uri.scheme == "foo"
-    doAssert uri.hostname == "::1"
-    doAssert uri.port == "1234"
-    doAssert uri.path == "/bar"
-    doAssert uri.query == "baz=true&qux"
-    doAssert uri.anchor == "quux"
-
-  block:
-    let str = "urn:example:animal:ferret:nose"
-    let test = parseUri(str)
-    doAssert test.scheme == "urn"
-    doAssert test.path == "example:animal:ferret:nose"
-    doAssert($test == str)
-
-  block:
-    let str = "mailto:username@example.com?subject=Topic"
-    let test = parseUri(str)
-    doAssert test.scheme == "mailto"
-    doAssert test.username == "username"
-    doAssert test.hostname == "example.com"
-    doAssert test.query == "subject=Topic"
-    doAssert($test == str)
-
-  block:
-    let str = "magnet:?xt=urn:sha1:72hsga62ba515sbd62&dn=foobar"
-    let test = parseUri(str)
-    doAssert test.scheme == "magnet"
-    doAssert test.query == "xt=urn:sha1:72hsga62ba515sbd62&dn=foobar"
-    doAssert($test == str)
-
-  block:
-    let str = "/test/foo/bar?q=2#asdf"
-    let test = parseUri(str)
-    doAssert test.scheme == ""
-    doAssert test.path == "/test/foo/bar"
-    doAssert test.query == "q=2"
-    doAssert test.anchor == "asdf"
-    doAssert($test == str)
-
-  block:
-    let str = "test/no/slash"
-    let test = parseUri(str)
-    doAssert test.path == "test/no/slash"
-    doAssert($test == str)
-
-  block:
-    let str = "//git@github.com:dom96/packages"
-    let test = parseUri(str)
-    doAssert test.scheme == ""
-    doAssert test.username == "git"
-    doAssert test.hostname == "github.com"
-    doAssert test.port == "dom96"
-    doAssert test.path == "/packages"
-
-  block:
-    let str = "file:///foo/bar/baz.txt"
-    let test = parseUri(str)
-    doAssert test.scheme == "file"
-    doAssert test.username == ""
-    doAssert test.hostname == ""
-    doAssert test.port == ""
-    doAssert test.path == "/foo/bar/baz.txt"
-
-  # Remove dot segments tests
-  block:
-    doAssert removeDotSegments("/foo/bar/baz") == "/foo/bar/baz"
-
-  # Combine tests
-  block:
-    let concat = combine(parseUri("http://google.com/foo/bar/"), parseUri("baz"))
-    doAssert concat.path == "/foo/bar/baz"
-    doAssert concat.hostname == "google.com"
-    doAssert concat.scheme == "http"
-
-  block:
-    let concat = combine(parseUri("http://google.com/foo"), parseUri("/baz"))
-    doAssert concat.path == "/baz"
-    doAssert concat.hostname == "google.com"
-    doAssert concat.scheme == "http"
-
-  block:
-    let concat = combine(parseUri("http://google.com/foo/test"), parseUri("bar"))
-    doAssert concat.path == "/foo/bar"
-
-  block:
-    let concat = combine(parseUri("http://google.com/foo/test"), parseUri("/bar"))
-    doAssert concat.path == "/bar"
-
-  block:
-    let concat = combine(parseUri("http://google.com/foo/test"), parseUri("bar"))
-    doAssert concat.path == "/foo/bar"
-
-  block:
-    let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar"))
-    doAssert concat.path == "/foo/test/bar"
-
-  block:
-    let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar/"))
-    doAssert concat.path == "/foo/test/bar/"
-
-  block:
-    let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar/"),
-                         parseUri("baz"))
-    doAssert concat.path == "/foo/test/bar/baz"
-
-  # `/` tests
-  block:
-    let test = parseUri("http://example.com/foo") / "bar/asd"
-    doAssert test.path == "/foo/bar/asd"
-
-  block:
-    let test = parseUri("http://example.com/foo/") / "/bar/asd"
-    doAssert test.path == "/foo/bar/asd"
-
-  # removeDotSegments tests
-  block:
-    # empty test
-    doAssert removeDotSegments("") == ""
-
-  # bug #3207
-  block:
-    doAssert parseUri("http://qq/1").combine(parseUri("https://qqq")).`$` == "https://qqq"
-
-  # bug #4959
-  block:
-    let foo = parseUri("http://example.com") / "/baz"
-    doAssert foo.path == "/baz"
-
-  # bug found on stream 13/10/17
-  block:
-    let foo = parseUri("http://localhost:9515") / "status"
-    doAssert $foo == "http://localhost:9515/status"
-
-  # bug #6649 #6652
-  block:
-    var foo = parseUri("http://example.com")
-    foo.hostname = "example.com"
-    foo.path = "baz"
-    doAssert $foo == "http://example.com/baz"
-
-    foo.hostname = "example.com/"
-    foo.path = "baz"
-    doAssert $foo == "http://example.com/baz"
-
-    foo.hostname = "example.com"
-    foo.path = "/baz"
-    doAssert $foo == "http://example.com/baz"
-
-    foo.hostname = "example.com/"
-    foo.path = "/baz"
-    doAssert $foo == "http://example.com/baz"
-
-    foo.hostname = "example.com/"
-    foo.port = "8000"
-    foo.path = "baz"
-    doAssert $foo == "http://example.com:8000/baz"
-
-    foo = parseUri("file:/dir/file")
-    foo.path = "relative"
-    doAssert $foo == "file:relative"
-
-  # isAbsolute tests
-  block:
-    doAssert "www.google.com".parseUri().isAbsolute() == false
-    doAssert "http://www.google.com".parseUri().isAbsolute() == true
-    doAssert "file:/dir/file".parseUri().isAbsolute() == true
-    doAssert "file://localhost/dir/file".parseUri().isAbsolute() == true
-    doAssert "urn:ISSN:1535-3613".parseUri().isAbsolute() == true
-
-    # path-relative URL *relative
-    doAssert "about".parseUri().isAbsolute == false
-    doAssert "about/staff.html".parseUri().isAbsolute == false
-    doAssert "about/staff.html?".parseUri().isAbsolute == false
-    doAssert "about/staff.html?parameters".parseUri().isAbsolute == false
-
-    # absolute-path-relative URL *relative
-    doAssert "/".parseUri().isAbsolute == false
-    doAssert "/about".parseUri().isAbsolute == false
-    doAssert "/about/staff.html".parseUri().isAbsolute == false
-    doAssert "/about/staff.html?".parseUri().isAbsolute == false
-    doAssert "/about/staff.html?parameters".parseUri().isAbsolute == false
-
-    # scheme-relative URL *relative
-    doAssert "//username:password@example.com:8888".parseUri().isAbsolute == false
-    doAssert "//username@example.com".parseUri().isAbsolute == false
-    doAssert "//example.com".parseUri().isAbsolute == false
-    doAssert "//example.com/".parseUri().isAbsolute == false
-    doAssert "//example.com/about".parseUri().isAbsolute == false
-    doAssert "//example.com/about/staff.html".parseUri().isAbsolute == false
-    doAssert "//example.com/about/staff.html?".parseUri().isAbsolute == false
-    doAssert "//example.com/about/staff.html?parameters".parseUri().isAbsolute == false
-
-    # absolute URL *absolute
-    doAssert "https://username:password@example.com:8888".parseUri().isAbsolute == true
-    doAssert "https://username@example.com".parseUri().isAbsolute == true
-    doAssert "https://example.com".parseUri().isAbsolute == true
-    doAssert "https://example.com/".parseUri().isAbsolute == true
-    doAssert "https://example.com/about".parseUri().isAbsolute == true
-    doAssert "https://example.com/about/staff.html".parseUri().isAbsolute == true
-    doAssert "https://example.com/about/staff.html?".parseUri().isAbsolute == true
-    doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true
-
-  echo("All good!")
+    if u.isIpv6:
+      result.add '['
+      result.add u.hostname
+      result.add ']'
+    else:
+      result.add u.hostname
+  if portLen > 0:
+    result.add ':'
+    result.add u.port
+  if pathLen > 0:
+    if hostnameLen > 0 and u.path[0] != '/':
+      result.add '/'
+    result.add u.path
+  if queryLen > 0:
+    result.add '?'
+    result.add u.query
+  if anchorLen > 0:
+    result.add '#'
+    result.add u.anchor
+
+
+proc getDataUri*(data, mime: string, encoding = "utf-8"): string {.since: (1, 3).} =
+  ## Convenience proc for `base64.encode` returns a standard Base64 Data URI (RFC-2397)
+  ##
+  ## **See also:**
+  ## * `mimetypes <mimetypes.html>`_ for `mime` argument
+  ## * https://tools.ietf.org/html/rfc2397
+  ## * https://en.wikipedia.org/wiki/Data_URI_scheme
+  runnableExamples: static: assert getDataUri("Nim", "text/plain") == "data:text/plain;charset=utf-8;base64,Tmlt"
+  assert encoding.len > 0 and mime.len > 0 # Must *not* be URL-Safe, see RFC-2397
+  let base64encoded: string = base64.encode(data)
+  # ("data:".len + ";charset=".len + ";base64,".len) == 22
+  result = newStringOfCap(22 + mime.len + encoding.len + base64encoded.len)
+  result.add "data:"
+  result.add mime
+  result.add ";charset="
+  result.add encoding
+  result.add ";base64,"
+  result.add base64encoded
diff --git a/lib/pure/volatile.nim b/lib/pure/volatile.nim
index b3705a199..a38247c7d 100644
--- a/lib/pure/volatile.nim
+++ b/lib/pure/volatile.nim
@@ -10,21 +10,25 @@
 ## This module contains code for generating volatile loads and stores,
 ## which are useful in embedded and systems programming.
 
-template volatileLoad*[T](src: ptr T): T =
+proc volatileLoad*[T](src: ptr T): T {.inline, noinit.} =
   ## Generates a volatile load of the value stored in the container `src`.
-  ## Note that this only effects code generation on `C` like backends
-  when defined(js):
-    src[]
+  ## Note that this only effects code generation on `C` like backends.
+  when nimvm:
+    result = src[]
   else:
-    var res: T
-    {.emit: [res, " = (*(", type(src[]), " volatile*)", src, ");"].}
-    res
+    when defined(js):
+      result = src[]
+    else:
+      {.emit: [result, " = (*(", typeof(src[]), " volatile*)", src, ");"].}
 
-template volatileStore*[T](dest: ptr T, val: T) =
+proc volatileStore*[T](dest: ptr T, val: T) {.inline.} =
   ## Generates a volatile store into the container `dest` of the value
   ## `val`. Note that this only effects code generation on `C` like
-  ## backends
-  when defined(js):
+  ## backends.
+  when nimvm:
     dest[] = val
   else:
-    {.emit: ["*((", type(dest[]), " volatile*)(", dest, ")) = ", val, ";"].}
+    when defined(js):
+      dest[] = val
+    else:
+      {.emit: ["*((", typeof(dest[]), " volatile*)(", dest, ")) = ", val, ";"].}
diff --git a/lib/pure/xmldom.nim b/lib/pure/xmldom.nim
deleted file mode 100644
index 8cd47aa39..000000000
--- a/lib/pure/xmldom.nim
+++ /dev/null
@@ -1,1136 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2010 Dominik Picheta
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-
-import strutils
-## This module implements XML DOM Level 2 Core
-## specification (http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html)
-
-
-#http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html
-
-#Exceptions
-type
-  EDOMException* = object of ValueError ## Base exception object for all DOM Exceptions
-  EDOMStringSizeErr* = object of EDOMException ## If the specified range of text does not fit into a DOMString
-                                               ## Currently not used(Since DOMString is just string)
-  EHierarchyRequestErr* = object of EDOMException ## If any node is inserted somewhere it doesn't belong
-  EIndexSizeErr* = object of EDOMException ## If index or size is negative, or greater than the allowed value
-  EInuseAttributeErr* = object of EDOMException ## If an attempt is made to add an attribute that is already in use elsewhere
-  EInvalidAccessErr* = object of EDOMException ## If a parameter or an operation is not supported by the underlying object.
-  EInvalidCharacterErr* = object of EDOMException ## This exception is raised when a string parameter contains an illegal character
-  EInvalidModificationErr* = object of EDOMException ## If an attempt is made to modify the type of the underlying object.
-  EInvalidStateErr* = object of EDOMException ## If an attempt is made to use an object that is not, or is no longer, usable.
-  ENamespaceErr* = object of EDOMException ## If an attempt is made to create or change an object in a way which is incorrect with regard to namespaces.
-  ENotFoundErr* = object of EDOMException ## If an attempt is made to reference a node in a context where it does not exist
-  ENotSupportedErr* = object of EDOMException ## If the implementation does not support the requested type of object or operation.
-  ENoDataAllowedErr* = object of EDOMException ## If data is specified for a node which does not support data
-  ENoModificationAllowedErr* = object of EDOMException ## If an attempt is made to modify an object where modifications are not allowed
-  ESyntaxErr* = object of EDOMException ## If an invalid or illegal string is specified.
-  EWrongDocumentErr* = object of EDOMException ## If a node is used in a different document than the one that created it (that doesn't support it)
-
-const
-  ElementNode* = 1
-  AttributeNode* = 2
-  TextNode* = 3
-  CDataSectionNode* = 4
-  ProcessingInstructionNode* = 7
-  CommentNode* = 8
-  DocumentNode* = 9
-  DocumentFragmentNode* = 11
-
-  # Nodes which are childless - Not sure about AttributeNode
-  childlessObjects = {DocumentNode, AttributeNode, TextNode,
-    CDataSectionNode, ProcessingInstructionNode, CommentNode}
-  # Illegal characters
-  illegalChars = {'>', '<', '&', '"'}
-
-  # standard xml: attribute names
-  # see https://www.w3.org/XML/1998/namespace
-  stdattrnames = ["lang", "space", "base", "id"]
-
-type
-  Feature = tuple[name: string, version: string]
-  PDOMImplementation* = ref DOMImplementation
-  DOMImplementation = object
-    features: seq[Feature] # Read-Only
-
-  PNode* = ref Node
-  Node = object of RootObj
-    attributes*: seq[PAttr]
-    childNodes*: seq[PNode]
-    fLocalName: string # Read-only
-    fNamespaceURI: string # Read-only
-    fNodeName: string # Read-only
-    nodeValue*: string
-    fNodeType: int # Read-only
-    fOwnerDocument: PDocument # Read-Only
-    fParentNode: PNode # Read-Only
-    prefix*: string # Setting this should change some values... TODO!
-
-  PElement* = ref Element
-  Element = object of Node
-    fTagName: string # Read-only
-
-  PCharacterData* = ref CharacterData
-  CharacterData = object of Node
-    data*: string
-
-  PDocument* = ref Document
-  Document = object of Node
-    fImplementation: PDOMImplementation # Read-only
-    fDocumentElement: PElement # Read-only
-
-  PAttr* = ref Attr
-  Attr = object of Node
-    fName: string # Read-only
-    fSpecified: bool # Read-only
-    value*: string
-    fOwnerElement: PElement # Read-only
-
-  PDocumentFragment* = ref DocumentFragment
-  DocumentFragment = object of Node
-
-  PText* = ref Text
-  Text = object of CharacterData
-
-  PComment* = ref Comment
-  Comment = object of CharacterData
-
-  PCDataSection* = ref CDataSection
-  CDataSection = object of Text
-
-  PProcessingInstruction* = ref ProcessingInstruction
-  ProcessingInstruction = object of Node
-    data*: string
-    fTarget: string # Read-only
-
-# DOMImplementation
-proc getDOM*(): PDOMImplementation =
-  ## Returns a DOMImplementation
-  new(result)
-  result.features = @[(name: "core", version: "2.0"),
-                      (name: "core", version: "1.0"),
-                      (name: "XML", version: "2.0")]
-
-proc createDocument*(dom: PDOMImplementation, namespaceURI: string, qualifiedName: string): PDocument =
-  ## Creates an XML Document object of the specified type with its document element.
-  var doc: PDocument
-  new(doc)
-  doc.fNamespaceURI = namespaceURI
-  doc.fImplementation = dom
-
-  var elTag: PElement
-  new(elTag)
-  elTag.fTagName = qualifiedName
-  elTag.fNodeName = qualifiedName
-  doc.fDocumentElement = elTag
-  doc.fNodeType = DocumentNode
-
-  return doc
-
-proc createDocument*(dom: PDOMImplementation, n: PElement): PDocument =
-  ## Creates an XML Document object of the specified type with its document element.
-
-  # This procedure is not in the specification, it's provided for the parser.
-  var doc: PDocument
-  new(doc)
-  doc.fDocumentElement = n
-  doc.fImplementation = dom
-  doc.fNodeType = DocumentNode
-
-  return doc
-
-proc hasFeature*(dom: PDOMImplementation, feature: string, version: string = ""): bool =
-  ## Returns ``true`` if this ``version`` of the DomImplementation implements ``feature``, otherwise ``false``
-  for iName, iVersion in items(dom.features):
-    if iName == feature:
-      if version == "":
-        return true
-      else:
-        if iVersion == version:
-          return true
-  return false
-
-
-# Document
-# Attributes
-
-proc implementation*(doc: PDocument): PDOMImplementation =
-  return doc.fImplementation
-
-proc documentElement*(doc: PDocument): PElement =
-  return doc.fDocumentElement
-
-# Internal procedures
-proc findNodes(nl: PNode, name: string): seq[PNode] =
-  # Made for getElementsByTagName
-  var r: seq[PNode] = @[]
-  if isNil(nl.childNodes): return @[]
-  if nl.childNodes.len() == 0: return @[]
-
-  for i in items(nl.childNodes):
-    if i.fNodeType == ElementNode:
-      if i.fNodeName == name or name == "*":
-        r.add(i)
-
-      if not isNil(i.childNodes):
-        if i.childNodes.len() != 0:
-          r.add(findNodes(i, name))
-
-  return r
-
-proc findNodesNS(nl: PNode, namespaceURI: string, localName: string): seq[PNode] =
-  # Made for getElementsByTagNameNS
-  var r: seq[PNode] = @[]
-  if isNil(nl.childNodes): return @[]
-  if nl.childNodes.len() == 0: return @[]
-
-  for i in items(nl.childNodes):
-    if i.fNodeType == ElementNode:
-      if (i.fNamespaceURI == namespaceURI or namespaceURI == "*") and (i.fLocalName == localName or localName == "*"):
-        r.add(i)
-
-      if not isNil(i.childNodes):
-        if i.childNodes.len() != 0:
-          r.add(findNodesNS(i, namespaceURI, localName))
-
-  return r
-
-
-#Procedures
-proc createAttribute*(doc: PDocument, name: string): PAttr =
-  ## Creates an Attr of the given name. Note that the Attr instance can then be set on an Element using the setAttributeNode method.
-  ## To create an attribute with a qualified name and namespace URI, use the createAttributeNS method.
-
-  # Check if name contains illegal characters
-  if illegalChars in name:
-    raise newException(EInvalidCharacterErr, "Invalid character")
-
-  var attrNode: PAttr
-  new(attrNode)
-  attrNode.fName = name
-  attrNode.fNodeName = name
-  attrNode.fLocalName = nil
-  attrNode.prefix = nil
-  attrNode.fNamespaceURI = nil
-  attrNode.value = ""
-  attrNode.fSpecified = false
-  return attrNode
-
-proc createAttributeNS*(doc: PDocument, namespaceURI: string, qualifiedName: string): PAttr =
-  ## Creates an attribute of the given qualified name and namespace URI
-
-  # Check if name contains illegal characters
-  if illegalChars in namespaceURI or illegalChars in qualifiedName:
-    raise newException(EInvalidCharacterErr, "Invalid character")
-  # Exceptions
-  if qualifiedName.contains(':'):
-    let qfnamespaces = qualifiedName.toLowerAscii().split(':')
-    if isNil(namespaceURI):
-      raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil")
-    elif qfnamespaces[0] == "xml" and
-        namespaceURI != "http://www.w3.org/XML/1998/namespace" and
-        qfnamespaces[1] notin stdattrnames:
-      raise newException(ENamespaceErr,
-        "When the namespace prefix is \"xml\" namespaceURI has to be \"http://www.w3.org/XML/1998/namespace\"")
-    elif qfnamespaces[1] == "xmlns" and namespaceURI != "http://www.w3.org/2000/xmlns/":
-      raise newException(ENamespaceErr,
-        "When the namespace prefix is \"xmlns\" namespaceURI has to be \"http://www.w3.org/2000/xmlns/\"")
-
-  var attrNode: PAttr
-  new(attrNode)
-  attrNode.fName = qualifiedName
-  attrNode.fNodeName = qualifiedName
-  attrNode.fSpecified = false
-  attrNode.fNamespaceURI = namespaceURI
-  if qualifiedName.contains(':'):
-    attrNode.prefix = qualifiedName.split(':')[0]
-    attrNode.fLocalName = qualifiedName.split(':')[1]
-  else:
-    attrNode.prefix = nil
-    attrNode.fLocalName = qualifiedName
-  attrNode.value = ""
-
-  attrNode.fNodeType = AttributeNode
-  return attrNode
-
-proc createCDATASection*(doc: PDocument, data: string): PCDataSection =
-  ## Creates a CDATASection node whose value is the specified string.
-  var cData: PCDataSection
-  new(cData)
-  cData.data = data
-  cData.nodeValue = data
-  cData.fNodeName = "#text" # Not sure about this, but this is technically a TextNode
-  cData.fNodeType = CDataSectionNode
-  return cData
-
-proc createComment*(doc: PDocument, data: string): PComment =
-  ## Creates a Comment node given the specified string.
-  var comm: PComment
-  new(comm)
-  comm.data = data
-  comm.nodeValue = data
-
-  comm.fNodeType = CommentNode
-  return comm
-
-proc createDocumentFragment*(doc: PDocument): PDocumentFragment =
-  ## Creates an empty DocumentFragment object.
-  var df: PDocumentFragment
-  new(df)
-  return df
-
-proc createElement*(doc: PDocument, tagName: string): PElement =
-  ## Creates an element of the type specified.
-
-  # Check if name contains illegal characters
-  if illegalChars in tagName:
-    raise newException(EInvalidCharacterErr, "Invalid character")
-
-  var elNode: PElement
-  new(elNode)
-  elNode.fTagName = tagName
-  elNode.fNodeName = tagName
-  elNode.fLocalName = nil
-  elNode.prefix = nil
-  elNode.fNamespaceURI = nil
-  elNode.childNodes = @[]
-  elNode.attributes = @[]
-
-  elNode.fNodeType = ElementNode
-
-  return elNode
-
-proc createElementNS*(doc: PDocument, namespaceURI: string, qualifiedName: string): PElement =
-  ## Creates an element of the given qualified name and namespace URI.
-  if qualifiedName.contains(':'):
-    let qfnamespaces = qualifiedName.toLowerAscii().split(':')
-    if isNil(namespaceURI):
-      raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil")
-    elif qfnamespaces[0] == "xml" and
-        namespaceURI != "http://www.w3.org/XML/1998/namespace" and
-        qfnamespaces[1] notin stdattrnames:
-      raise newException(ENamespaceErr,
-        "When the namespace prefix is \"xml\" namespaceURI has to be \"http://www.w3.org/XML/1998/namespace\"")
-
-  # Check if name contains illegal characters
-  if illegalChars in namespaceURI or illegalChars in qualifiedName:
-    raise newException(EInvalidCharacterErr, "Invalid character")
-
-  var elNode: PElement
-  new(elNode)
-  elNode.fTagName = qualifiedName
-  elNode.fNodeName = qualifiedName
-  if qualifiedName.contains(':'):
-    elNode.prefix = qualifiedName.split(':')[0]
-    elNode.fLocalName = qualifiedName.split(':')[1]
-  else:
-    elNode.prefix = nil
-    elNode.fLocalName = qualifiedName
-  elNode.fNamespaceURI = namespaceURI
-  elNode.childNodes = @[]
-  elNode.attributes = @[]
-
-  elNode.fNodeType = ElementNode
-
-  return elNode
-
-proc createProcessingInstruction*(doc: PDocument, target: string, data: string): PProcessingInstruction =
-  ## Creates a ProcessingInstruction node given the specified name and data strings.
-
-  #Check if name contains illegal characters
-  if illegalChars in target:
-    raise newException(EInvalidCharacterErr, "Invalid character")
-
-  var pi: PProcessingInstruction
-  new(pi)
-  pi.fTarget = target
-  pi.data = data
-  pi.fNodeType = ProcessingInstructionNode
-  return pi
-
-proc createTextNode*(doc: PDocument, data: string): PText = #Propably TextNode
-  ## Creates a Text node given the specified string.
-  var txtNode: PText
-  new(txtNode)
-  txtNode.data = data
-  txtNode.nodeValue = data
-  txtNode.fNodeName = "#text"
-
-  txtNode.fNodeType = TextNode
-  return txtNode
-
-discard """proc getElementById*(doc: PDocument, elementId: string): PElement =
-  ##Returns the ``Element`` whose ID is given by ``elementId``. If no such element exists, returns ``nil``
-  #TODO"""
-
-proc getElementsByTagName*(doc: PDocument, tagName: string): seq[PNode] =
-  ## Returns a NodeList of all the Elements with a given tag name in
-  ## the order in which they are encountered in a preorder traversal of the Document tree.
-  result = @[]
-  if doc.fDocumentElement.fNodeName == tagName or tagName == "*":
-    result.add(doc.fDocumentElement)
-
-  result.add(doc.fDocumentElement.findNodes(tagName))
-
-proc getElementsByTagNameNS*(doc: PDocument, namespaceURI: string, localName: string): seq[PNode] =
-  ## Returns a NodeList of all the Elements with a given localName and namespaceURI
-  ## in the order in which they are encountered in a preorder traversal of the Document tree.
-  result = @[]
-  if doc.fDocumentElement.fLocalName == localName or localName == "*":
-    if doc.fDocumentElement.fNamespaceURI == namespaceURI or namespaceURI == "*":
-      result.add(doc.fDocumentElement)
-
-  result.add(doc.fDocumentElement.findNodesNS(namespaceURI, localName))
-
-proc importNode*(doc: PDocument, importedNode: PNode, deep: bool): PNode =
-  ## Imports a node from another document to this document
-  case importedNode.fNodeType
-  of AttributeNode:
-    var nAttr: PAttr = PAttr(importedNode)
-    nAttr.fOwnerDocument = doc
-    nAttr.fParentNode = nil
-    nAttr.fOwnerElement = nil
-    nAttr.fSpecified = true
-    return nAttr
-  of DocumentFragmentNode:
-    var n: PNode
-    new(n)
-    n = importedNode
-    n.fOwnerDocument = doc
-    n.fParentNode = nil
-
-    n.fOwnerDocument = doc
-    n.fParentNode = nil
-    var tmp: seq[PNode] = n.childNodes
-    n.childNodes = @[]
-    if deep:
-      for i in low(tmp.len())..high(tmp.len()):
-        n.childNodes.add(importNode(doc, tmp[i], deep))
-
-    return n
-  of ElementNode:
-    var n: PNode
-    new(n)
-    n = importedNode
-    n.fOwnerDocument = doc
-    n.fParentNode = nil
-
-    var tmpA: seq[PAttr] = n.attributes
-    n.attributes = @[]
-    # Import the Element node's attributes
-    for i in low(tmpA.len())..high(tmpA.len()):
-      n.attributes.add(PAttr(importNode(doc, tmpA[i], deep)))
-    # Import the childNodes
-    var tmp: seq[PNode] = n.childNodes
-    n.childNodes = @[]
-    if deep:
-      for i in low(tmp.len())..high(tmp.len()):
-        n.childNodes.add(importNode(doc, tmp[i], deep))
-
-    return n
-  of ProcessingInstructionNode, TextNode, CDataSectionNode, CommentNode:
-    var n: PNode
-    new(n)
-    n = importedNode
-    n.fOwnerDocument = doc
-    n.fParentNode = nil
-    return n
-  else:
-    raise newException(ENotSupportedErr, "The type of node being imported is not supported")
-
-
-# Node
-# Attributes
-
-proc firstChild*(n: PNode): PNode =
-  ## Returns this node's first child
-
-  if not isNil(n.childNodes) and n.childNodes.len() > 0:
-    return n.childNodes[0]
-  else:
-    return nil
-
-proc lastChild*(n: PNode): PNode =
-  ## Returns this node's last child
-
-  if not isNil(n.childNodes) and n.childNodes.len() > 0:
-    return n.childNodes[n.childNodes.len() - 1]
-  else:
-    return nil
-
-proc localName*(n: PNode): string =
-  ## Returns this nodes local name
-
-  return n.fLocalName
-
-proc namespaceURI*(n: PNode): string =
-  ## Returns this nodes namespace URI
-
-  return n.fNamespaceURI
-
-proc `namespaceURI=`*(n: PNode, value: string) =
-  n.fNamespaceURI = value
-
-proc nextSibling*(n: PNode): PNode =
-  ## Returns the next sibling of this node
-
-  if isNil(n.fParentNode) or isNil(n.fParentNode.childNodes):
-    return nil
-  var nLow: int = low(n.fParentNode.childNodes)
-  var nHigh: int = high(n.fParentNode.childNodes)
-  for i in nLow..nHigh:
-    if n.fParentNode.childNodes[i] == n:
-      return n.fParentNode.childNodes[i + 1]
-  return nil
-
-proc nodeName*(n: PNode): string =
-  ## Returns the name of this node
-
-  return n.fNodeName
-
-proc nodeType*(n: PNode): int =
-  ## Returns the type of this node
-
-  return n.fNodeType
-
-proc ownerDocument*(n: PNode): PDocument =
-  ## Returns the owner document of this node
-
-  return n.fOwnerDocument
-
-proc parentNode*(n: PNode): PNode =
-  ## Returns the parent node of this node
-
-  return n.fParentNode
-
-proc previousSibling*(n: PNode): PNode =
-  ## Returns the previous sibling of this node
-
-  if isNil(n.fParentNode) or isNil(n.fParentNode.childNodes):
-    return nil
-  var nLow: int = low(n.fParentNode.childNodes)
-  var nHigh: int = high(n.fParentNode.childNodes)
-  for i in nLow..nHigh:
-    if n.fParentNode.childNodes[i] == n:
-      return n.fParentNode.childNodes[i - 1]
-  return nil
-
-proc `prefix=`*(n: PNode, value: string) =
-  ## Modifies the prefix of this node
-
-  # Setter
-  # Check if name contains illegal characters
-  if illegalChars in value:
-    raise newException(EInvalidCharacterErr, "Invalid character")
-
-  if isNil(n.fNamespaceURI):
-    raise newException(ENamespaceErr, "namespaceURI cannot be nil")
-  elif value.toLowerAscii() == "xml" and n.fNamespaceURI != "http://www.w3.org/XML/1998/namespace":
-    raise newException(ENamespaceErr,
-      "When the namespace prefix is \"xml\" namespaceURI has to be \"http://www.w3.org/XML/1998/namespace\"")
-  elif value.toLowerAscii() == "xmlns" and n.fNamespaceURI != "http://www.w3.org/2000/xmlns/":
-    raise newException(ENamespaceErr,
-      "When the namespace prefix is \"xmlns\" namespaceURI has to be \"http://www.w3.org/2000/xmlns/\"")
-  elif value.toLowerAscii() == "xmlns" and n.fNodeType == AttributeNode:
-    raise newException(ENamespaceErr, "An AttributeNode cannot have a prefix of \"xmlns\"")
-
-  n.fNodeName = value & ":" & n.fLocalName
-  if n.nodeType == ElementNode:
-    var el: PElement = PElement(n)
-    el.fTagName = value & ":" & n.fLocalName
-
-  elif n.nodeType == AttributeNode:
-    var attr: PAttr = PAttr(n)
-    attr.fName = value & ":" & n.fLocalName
-
-# Procedures
-proc appendChild*(n: PNode, newChild: PNode) =
-  ## Adds the node newChild to the end of the list of children of this node.
-  ## If the newChild is already in the tree, it is first removed.
-
-  # Check if n contains newChild
-  if not isNil(n.childNodes):
-    for i in low(n.childNodes)..high(n.childNodes):
-      if n.childNodes[i] == newChild:
-        raise newException(EHierarchyRequestErr, "The node to append is already in this nodes children.")
-
-  # Check if newChild is from this nodes document
-  if n.fOwnerDocument != newChild.fOwnerDocument:
-    raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
-
-  if n == newChild:
-    raise newException(EHierarchyRequestErr, "You can't add a node into itself")
-
-  if n.nodeType in childlessObjects:
-    raise newException(ENoModificationAllowedErr, "Cannot append children to a childless node")
-
-  if isNil(n.childNodes): n.childNodes = @[]
-
-  newChild.fParentNode = n
-  for i in low(n.childNodes)..high(n.childNodes):
-    if n.childNodes[i] == newChild:
-      n.childNodes[i] = newChild
-
-  n.childNodes.add(newChild)
-
-proc cloneNode*(n: PNode, deep: bool): PNode =
-  ## Returns a duplicate of this node, if ``deep`` is `true`, Element node's children are copied
-  case n.fNodeType
-  of AttributeNode:
-    var newNode: PAttr
-    new(newNode)
-    newNode = PAttr(n)
-    newNode.fSpecified = true
-    newNode.fOwnerElement = nil
-    return newNode
-  of ElementNode:
-    var newNode: PElement
-    new(newNode)
-    newNode = PElement(n)
-    # Import the childNodes
-    var tmp: seq[PNode] = n.childNodes
-    n.childNodes = @[]
-    if deep and not isNil(tmp):
-      for i in low(tmp.len())..high(tmp.len()):
-        n.childNodes.add(cloneNode(tmp[i], deep))
-    return newNode
-  else:
-    var newNode: PNode
-    new(newNode)
-    newNode = n
-    return newNode
-
-proc hasAttributes*(n: PNode): bool =
-  ## Returns whether this node (if it is an element) has any attributes.
-  return not isNil(n.attributes) and n.attributes.len() > 0
-
-proc hasChildNodes*(n: PNode): bool =
-  ## Returns whether this node has any children.
-  return not isNil(n.childNodes) and n.childNodes.len() > 0
-
-proc insertBefore*(n: PNode, newChild: PNode, refChild: PNode): PNode =
-  ## Inserts the node ``newChild`` before the existing child node ``refChild``.
-  ## If ``refChild`` is nil, insert ``newChild`` at the end of the list of children.
-
-  # Check if newChild is from this nodes document
-  if n.fOwnerDocument != newChild.fOwnerDocument:
-    raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
-
-  if isNil(n.childNodes):
-    n.childNodes = @[]
-
-  for i in low(n.childNodes)..high(n.childNodes):
-    if n.childNodes[i] == refChild:
-      n.childNodes.insert(newChild, i - 1)
-      return
-
-  n.childNodes.add(newChild)
-
-proc isSupported*(n: PNode, feature: string, version: string): bool =
-  ## Tests whether the DOM implementation implements a specific
-  ## feature and that feature is supported by this node.
-  return n.fOwnerDocument.fImplementation.hasFeature(feature, version)
-
-proc isEmpty(s: string): bool =
-
-  if isNil(s) or s == "":
-    return true
-  for i in items(s):
-    if i != ' ':
-      return false
-  return true
-
-proc normalize*(n: PNode) =
-  ## Merges all separated TextNodes together, and removes any empty TextNodes
-  var curTextNode: PNode = nil
-  var i: int = 0
-
-  var newChildNodes: seq[PNode] = @[]
-  while true:
-    if isNil(n.childNodes) or i >= n.childNodes.len:
-      break
-    if n.childNodes[i].nodeType == TextNode:
-
-      #If the TextNode is empty, remove it
-      if PText(n.childNodes[i]).data.isEmpty():
-        inc(i)
-
-      if isNil(curTextNode):
-        curTextNode = n.childNodes[i]
-      else:
-        PText(curTextNode).data.add(PText(n.childNodes[i]).data)
-        curTextNode.nodeValue.add(PText(n.childNodes[i]).data)
-        inc(i)
-    else:
-      newChildNodes.add(curTextNode)
-      newChildNodes.add(n.childNodes[i])
-      curTextNode = nil
-
-    inc(i)
-  n.childNodes = newChildNodes
-
-proc removeChild*(n: PNode, oldChild: PNode): PNode =
-  ## Removes the child node indicated by ``oldChild`` from the list of children, and returns it.
-  if not isNil(n.childNodes):
-    for i in low(n.childNodes)..high(n.childNodes):
-      if n.childNodes[i] == oldChild:
-        result = n.childNodes[i]
-        n.childNodes.delete(i)
-        return
-
-  raise newException(ENotFoundErr, "Node not found")
-
-proc replaceChild*(n: PNode, newChild: PNode, oldChild: PNode): PNode =
-  ## Replaces the child node ``oldChild`` with ``newChild`` in the list of children, and returns the ``oldChild`` node.
-
-  # Check if newChild is from this nodes document
-  if n.fOwnerDocument != newChild.fOwnerDocument:
-    raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
-
-  if not isNil(n.childNodes):
-    for i in low(n.childNodes)..high(n.childNodes):
-      if n.childNodes[i] == oldChild:
-        result = n.childNodes[i]
-        n.childNodes[i] = newChild
-        return
-
-  raise newException(ENotFoundErr, "Node not found")
-
-# NamedNodeMap
-
-proc getNamedItem*(nList: seq[PNode], name: string): PNode =
-  ## Retrieves a node specified by ``name``. If this node cannot be found returns ``nil``
-  for i in items(nList):
-    if i.nodeName() == name:
-      return i
-  return nil
-
-proc getNamedItem*(nList: seq[PAttr], name: string): PAttr =
-  ## Retrieves a node specified by ``name``. If this node cannot be found returns ``nil``
-  for i in items(nList):
-    if i.nodeName() == name:
-      return i
-  return nil
-
-proc getNamedItemNS*(nList: seq[PNode], namespaceURI: string, localName: string): PNode =
-  ## Retrieves a node specified by ``localName`` and ``namespaceURI``. If this node cannot be found returns ``nil``
-  for i in items(nList):
-    if i.namespaceURI() == namespaceURI and i.localName() == localName:
-      return i
-  return nil
-
-proc getNamedItemNS*(nList: seq[PAttr], namespaceURI: string, localName: string): PAttr =
-  ## Retrieves a node specified by ``localName`` and ``namespaceURI``. If this node cannot be found returns ``nil``
-  for i in items(nList):
-    if i.namespaceURI() == namespaceURI and i.localName() == localName:
-      return i
-  return nil
-
-proc item*(nList: seq[PNode], index: int): PNode =
-  ## Returns the ``index`` th item in the map.
-  ## If ``index`` is greater than or equal to the number of nodes in this map, this returns ``nil``.
-  if index >= nList.len(): return nil
-  else: return nList[index]
-
-proc removeNamedItem*(nList: var seq[PNode], name: string): PNode =
-  ## Removes a node specified by ``name``
-  ## Raises the ``ENotFoundErr`` exception, if the node was not found
-  for i in low(nList)..high(nList):
-    if nList[i].fNodeName == name:
-      result = nList[i]
-      nList.delete(i)
-      return
-
-  raise newException(ENotFoundErr, "Node not found")
-
-proc removeNamedItemNS*(nList: var seq[PNode], namespaceURI: string, localName: string): PNode =
-  ## Removes a node specified by local name and namespace URI
-  for i in low(nList)..high(nList):
-    if nList[i].fLocalName == localName and nList[i].fNamespaceURI == namespaceURI:
-      result = nList[i]
-      nList.delete(i)
-      return
-
-  raise newException(ENotFoundErr, "Node not found")
-
-proc setNamedItem*(nList: var seq[PNode], arg: PNode): PNode =
-  ## Adds ``arg`` as a ``Node`` to the ``NList``
-  ## If a node with the same name is already present in this map, it is replaced by the new one.
-  if not isNil(nList):
-    if nList.len() > 0:
-      #Check if newChild is from this nodes document
-      if nList[0].fOwnerDocument != arg.fOwnerDocument:
-        raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
-  #Exceptions End
-
-  var item: PNode = nList.getNamedItem(arg.nodeName())
-  if isNil(item):
-    nList.add(arg)
-    return nil
-  else:
-    # Node with the same name exists
-    var index: int = 0
-    for i in low(nList)..high(nList):
-      if nList[i] == item:
-        index = i
-        break
-    nList[index] = arg
-    return item # Return the replaced node
-
-proc setNamedItem*(nList: var seq[PAttr], arg: PAttr): PAttr =
-  ## Adds ``arg`` as a ``Node`` to the ``NList``
-  ## If a node with the same name is already present in this map, it is replaced by the new one.
-  if not isNil(nList):
-    if nList.len() > 0:
-      # Check if newChild is from this nodes document
-      if nList[0].fOwnerDocument != arg.fOwnerDocument:
-        raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
-
-  if not isNil(arg.fOwnerElement):
-    raise newException(EInuseAttributeErr, "This attribute is in use by another element, use cloneNode")
-
-  # Exceptions end
-  var item: PAttr = nList.getNamedItem(arg.nodeName())
-  if isNil(item):
-    nList.add(arg)
-    return nil
-  else:
-    # Node with the same name exists
-    var index: int = 0
-    for i in low(nList)..high(nList):
-      if nList[i] == item:
-        index = i
-        break
-    nList[index] = arg
-    return item # Return the replaced node
-
-proc setNamedItemNS*(nList: var seq[PNode], arg: PNode): PNode =
-  ## Adds a node using its ``namespaceURI`` and ``localName``
-  if not isNil(nList):
-    if nList.len() > 0:
-      # Check if newChild is from this nodes document
-      if nList[0].fOwnerDocument != arg.fOwnerDocument:
-        raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
-  #Exceptions end
-
-  var item: PNode = nList.getNamedItemNS(arg.namespaceURI(), arg.localName())
-  if isNil(item):
-    nList.add(arg)
-    return nil
-  else:
-    # Node with the same name exists
-    var index: int = 0
-    for i in low(nList)..high(nList):
-      if nList[i] == item:
-        index = i
-        break
-    nList[index] = arg
-    return item # Return the replaced node
-
-proc setNamedItemNS*(nList: var seq[PAttr], arg: PAttr): PAttr =
-  ## Adds a node using its ``namespaceURI`` and ``localName``
-  if not isNil(nList):
-    if nList.len() > 0:
-      # Check if newChild is from this nodes document
-      if nList[0].fOwnerDocument != arg.fOwnerDocument:
-        raise newException(EWrongDocumentErr, "This node belongs to a different document, use importNode.")
-
-  if not isNil(arg.fOwnerElement):
-    raise newException(EInuseAttributeErr, "This attribute is in use by another element, use cloneNode")
-
-  # Exceptions end
-  var item: PAttr = nList.getNamedItemNS(arg.namespaceURI(), arg.localName())
-  if isNil(item):
-    nList.add(arg)
-    return nil
-  else:
-    # Node with the same name exists
-    var index: int = 0
-    for i in low(nList)..high(nList):
-      if nList[i] == item:
-        index = i
-        break
-    nList[index] = arg
-    return item # Return the replaced node
-
-# CharacterData - Decided to implement this,
-# Didn't add the procedures, because you can just edit .data
-
-# Attr
-# Attributes
-proc name*(a: PAttr): string =
-  ## Returns the name of the Attribute
-
-  return a.fName
-
-proc specified*(a: PAttr): bool =
-  ## Specifies whether this attribute was specified in the original document
-
-  return a.fSpecified
-
-proc ownerElement*(a: PAttr): PElement =
-  ## Returns this Attributes owner element
-
-  return a.fOwnerElement
-
-# Element
-# Attributes
-
-proc tagName*(el: PElement): string =
-  ## Returns the Element Tag Name
-
-  return el.fTagName
-
-# Procedures
-proc getAttribute*(el: PNode, name: string): string =
-  ## Retrieves an attribute value by ``name``
-  if isNil(el.attributes):
-    return nil
-  var attribute = el.attributes.getNamedItem(name)
-  if not isNil(attribute):
-    return attribute.value
-  else:
-    return nil
-
-proc getAttributeNS*(el: PNode, namespaceURI: string, localName: string): string =
-  ## Retrieves an attribute value by ``localName`` and ``namespaceURI``
-  if isNil(el.attributes):
-    return nil
-  var attribute = el.attributes.getNamedItemNS(namespaceURI, localName)
-  if not isNil(attribute):
-    return attribute.value
-  else:
-    return nil
-
-proc getAttributeNode*(el: PElement, name: string): PAttr =
-  ## Retrieves an attribute node by ``name``
-  ## To retrieve an attribute node by qualified name and namespace URI, use the `getAttributeNodeNS` method
-  if isNil(el.attributes):
-    return nil
-  return el.attributes.getNamedItem(name)
-
-proc getAttributeNodeNS*(el: PElement, namespaceURI: string, localName: string): PAttr =
-  ## Retrieves an `Attr` node by ``localName`` and ``namespaceURI``
-  if isNil(el.attributes):
-    return nil
-  return el.attributes.getNamedItemNS(namespaceURI, localName)
-
-proc getElementsByTagName*(el: PElement, name: string): seq[PNode] =
-  ## Returns a `NodeList` of all descendant `Elements` of ``el`` with a given tag ``name``,
-  ## in the order in which they are encountered in a preorder traversal of this `Element` tree
-  ## If ``name`` is `*`, returns all descendant of ``el``
-  result = el.findNodes(name)
-
-proc getElementsByTagNameNS*(el: PElement, namespaceURI: string, localName: string): seq[PNode] =
-  ## Returns a `NodeList` of all the descendant Elements with a given
-  ## ``localName`` and ``namespaceURI`` in the order in which they are
-  ## encountered in a preorder traversal of this Element tree
-  result = el.findNodesNS(namespaceURI, localName)
-
-proc hasAttribute*(el: PElement, name: string): bool =
-  ## Returns ``true`` when an attribute with a given ``name`` is specified
-  ## on this element , ``false`` otherwise.
-  if isNil(el.attributes):
-    return false
-  return not isNil(el.attributes.getNamedItem(name))
-
-proc hasAttributeNS*(el: PElement, namespaceURI: string, localName: string): bool =
-  ## Returns ``true`` when an attribute with a given ``localName`` and
-  ## ``namespaceURI`` is specified on this element , ``false`` otherwise
-  if isNil(el.attributes):
-    return false
-  return not isNil(el.attributes.getNamedItemNS(namespaceURI, localName))
-
-proc removeAttribute*(el: PElement, name: string) =
-  ## Removes an attribute by ``name``
-  if not isNil(el.attributes):
-    for i in low(el.attributes)..high(el.attributes):
-      if el.attributes[i].fName == name:
-        el.attributes.delete(i)
-
-proc removeAttributeNS*(el: PElement, namespaceURI: string, localName: string) =
-  ## Removes an attribute by ``localName`` and ``namespaceURI``
-  if not isNil(el.attributes):
-    for i in low(el.attributes)..high(el.attributes):
-      if el.attributes[i].fNamespaceURI == namespaceURI and
-          el.attributes[i].fLocalName == localName:
-        el.attributes.delete(i)
-
-proc removeAttributeNode*(el: PElement, oldAttr: PAttr): PAttr =
-  ## Removes the specified attribute node
-  ## If the attribute node cannot be found raises ``ENotFoundErr``
-  if not isNil(el.attributes):
-    for i in low(el.attributes)..high(el.attributes):
-      if el.attributes[i] == oldAttr:
-        result = el.attributes[i]
-        el.attributes.delete(i)
-        return
-
-  raise newException(ENotFoundErr, "oldAttr is not a member of el's Attributes")
-
-proc setAttributeNode*(el: PElement, newAttr: PAttr): PAttr =
-  ## Adds a new attribute node, if an attribute with the same `nodeName` is
-  ## present, it is replaced by the new one and the replaced attribute is
-  ## returned, otherwise ``nil`` is returned.
-
-  # Check if newAttr is from this nodes document
-  if el.fOwnerDocument != newAttr.fOwnerDocument:
-    raise newException(EWrongDocumentErr,
-      "This node belongs to a different document, use importNode.")
-
-  if not isNil(newAttr.fOwnerElement):
-    raise newException(EInuseAttributeErr,
-      "This attribute is in use by another element, use cloneNode")
-  # Exceptions end
-
-  if isNil(el.attributes): el.attributes = @[]
-  return el.attributes.setNamedItem(newAttr)
-
-proc setAttributeNodeNS*(el: PElement, newAttr: PAttr): PAttr =
-  ## Adds a new attribute node, if an attribute with the localName and
-  ## namespaceURI of ``newAttr`` is present, it is replaced by the new one
-  ## and the replaced attribute is returned, otherwise ``nil`` is returned.
-
-  # Check if newAttr is from this nodes document
-  if el.fOwnerDocument != newAttr.fOwnerDocument:
-    raise newException(EWrongDocumentErr,
-      "This node belongs to a different document, use importNode.")
-
-  if not isNil(newAttr.fOwnerElement):
-    raise newException(EInuseAttributeErr,
-      "This attribute is in use by another element, use cloneNode")
-  # Exceptions end
-
-  if isNil(el.attributes): el.attributes = @[]
-  return el.attributes.setNamedItemNS(newAttr)
-
-proc setAttribute*(el: PElement, name: string, value: string) =
-  ## Adds a new attribute, as specified by ``name`` and ``value``
-  ## If an attribute with that name is already present in the element, its
-  ## value is changed to be that of the value parameter
-  ## Raises the EInvalidCharacterErr if the specified ``name`` contains
-  ## illegal characters
-  var attrNode = el.fOwnerDocument.createAttribute(name)
-  # Check if name contains illegal characters
-  if illegalChars in name:
-    raise newException(EInvalidCharacterErr, "Invalid character")
-
-  discard el.setAttributeNode(attrNode)
-  # Set the info later, the setAttributeNode checks
-  # if FOwnerElement is nil, and if it isn't it raises an exception
-  attrNode.fOwnerElement = el
-  attrNode.fSpecified = true
-  attrNode.value = value
-
-proc setAttributeNS*(el: PElement, namespaceURI, localName, value: string) =
-  ## Adds a new attribute, as specified by ``namespaceURI``, ``localName``
-  ## and ``value``.
-
-  # Check if name contains illegal characters
-  if illegalChars in namespaceURI or illegalChars in localName:
-    raise newException(EInvalidCharacterErr, "Invalid character")
-
-  var attrNode = el.fOwnerDocument.createAttributeNS(namespaceURI, localName)
-
-  discard el.setAttributeNodeNS(attrNode)
-  # Set the info later, the setAttributeNode checks
-  # if FOwnerElement is nil, and if it isn't it raises an exception
-  attrNode.fOwnerElement = el
-  attrNode.fSpecified = true
-  attrNode.value = value
-
-# Text
-proc splitData*(textNode: PText, offset: int): PText =
-  ## Breaks this node into two nodes at the specified offset,
-  ## keeping both in the tree as siblings.
-
-  if offset > textNode.data.len():
-    raise newException(EIndexSizeErr, "Index out of bounds")
-
-  var left: string = textNode.data.substr(0, offset)
-  textNode.data = left
-  var right: string = textNode.data.substr(offset, textNode.data.len())
-
-  if not isNil(textNode.fParentNode) and not isNil(textNode.fParentNode.childNodes):
-    for i in low(textNode.fParentNode.childNodes)..high(textNode.fParentNode.childNodes):
-      if textNode.fParentNode.childNodes[i] == textNode:
-        var newNode: PText = textNode.fOwnerDocument.createTextNode(right)
-        textNode.fParentNode.childNodes.insert(newNode, i)
-        return newNode
-  else:
-    var newNode: PText = textNode.fOwnerDocument.createTextNode(right)
-    return newNode
-
-# ProcessingInstruction
-proc target*(pi: PProcessingInstruction): string =
-  ## Returns the Processing Instructions target
-
-  return pi.fTarget
-
-proc escapeXml*(s: string; result: var string) =
-  ## Prepares a string for insertion into a XML document
-  ## by escaping the XML special characters.
-  result = ""
-  for c in items(s):
-    case c
-    of '<': result.add("&lt;")
-    of '>': result.add("&gt;")
-    of '&': result.add("&amp;")
-    of '"': result.add("&quot;")
-    else: result.add(c)
-
-proc escapeXml*(s: string): string =
-  ## Prepares a string for insertion into a XML document
-  ## by escaping the XML special characters.
-  result = newStringOfCap(s.len + s.len shr 4)
-  escapeXml(s, result)
-
-# --Other stuff--
-# Writer
-
-proc nodeToXml(n: PNode, indent: int = 0): string =
-  result = spaces(indent) & "<" & n.nodeName
-  if not isNil(n.attributes):
-    for i in items(n.attributes):
-      result.add(" " & i.name & "=\"" & escapeXml(i.value) & "\"")
-
-  if isNil(n.childNodes) or n.childNodes.len() == 0:
-    result.add("/>") # No idea why this doesn't need a \n :O
-  else:
-    # End the beginning of this tag
-    result.add(">\n")
-    for i in items(n.childNodes):
-      case i.nodeType
-      of ElementNode:
-        result.add(nodeToXml(i, indent + 2))
-      of TextNode:
-        result.add(spaces(indent * 2))
-        result.add(escapeXml(i.nodeValue))
-      of CDataSectionNode:
-        result.add(spaces(indent * 2))
-        result.add("<![CDATA[" & i.nodeValue & "]]>")
-      of ProcessingInstructionNode:
-        result.add(spaces(indent * 2))
-        result.add("<?" & PProcessingInstruction(i).target & " " &
-                          PProcessingInstruction(i).data & " ?>")
-      of CommentNode:
-        result.add(spaces(indent * 2))
-        result.add("<!-- " & i.nodeValue & " -->")
-      else:
-        continue
-      result.add("\n")
-    # Add the ending tag - </tag>
-    result.add(spaces(indent) & "</" & n.nodeName & ">")
-
-proc `$`*(doc: PDocument): string =
-  ## Converts a PDocument object into a string representation of it's XML
-  result = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
-  result.add(nodeToXml(doc.documentElement))
diff --git a/lib/pure/xmldomparser.nim b/lib/pure/xmldomparser.nim
deleted file mode 100644
index 7c7f7b99c..000000000
--- a/lib/pure/xmldomparser.nim
+++ /dev/null
@@ -1,168 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2010 Dominik Picheta
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-import xmldom, os, streams, parsexml, strutils
-
-## This module parses a XML Document into a XML DOM Document representation.
-
-#XMLDom's Parser - Turns XML into a Document
-
-type
-  # Parsing errors
-  EMismatchedTag* = object of ValueError ## Raised when a tag is not properly closed
-  EParserError* = object of ValueError ## Raised when an unexpected XML Parser event occurs
-
-  # For namespaces
-  XmlnsAttr = tuple[name, value: string, ownerElement: PElement]
-
-var nsList: seq[XmlnsAttr] = @[] # Used for storing namespaces
-
-proc getNS(prefix: string): string =
-  var defaultNS: seq[string] = @[]
-
-  for key, value, tag in items(nsList):
-    if ":" in key:
-      if key.split(':')[1] == prefix:
-        return value
-
-    if key == "xmlns":
-      defaultNS.add(value)
-
-  # Don't return the default namespaces
-  # in the loop, because then they would have a precedence
-  # over normal namespaces
-  if defaultNS.len() > 0:
-    return defaultNS[0] # Return the first found default namespace
-                        # if none are specified for this prefix
-
-  return ""
-
-proc parseText(x: var XmlParser, doc: var PDocument): PText =
-  result = doc.createTextNode(x.charData())
-
-proc parseElement(x: var XmlParser, doc: var PDocument): PElement =
-  var n = doc.createElement("")
-
-  while true:
-    case x.kind()
-    of xmlEof:
-      break
-    of xmlElementStart, xmlElementOpen:
-      if n.tagName() != "":
-        n.appendChild(parseElement(x, doc))
-      else:
-        n = doc.createElementNS("", x.elementName)
-
-    of xmlElementEnd:
-      if x.elementName == n.nodeName:
-        # n.normalize() # Remove any whitespace etc.
-
-        var ns: string
-        if x.elementName.contains(':'):
-          ns = getNS(x.elementName.split(':')[0])
-        else:
-          ns = getNS("")
-
-        n.namespaceURI = ns
-
-        # Remove any namespaces this element declared
-        var count = 0 # Variable which keeps the index
-                      # We need to edit it..
-        for i in low(nsList)..len(nsList)-1:
-          if nsList[count][2] == n:
-            nsList.delete(count)
-            dec(count)
-          inc(count)
-
-        return n
-      else: #The wrong element is ended
-        raise newException(EMismatchedTag, "Mismatched tag at line " &
-          $x.getLine() & " column " & $x.getColumn)
-
-    of xmlCharData:
-      n.appendChild(parseText(x, doc))
-    of xmlAttribute:
-      if x.attrKey == "xmlns" or x.attrKey.startsWith("xmlns:"):
-        nsList.add((x.attrKey, x.attrValue, n))
-
-      if x.attrKey.contains(':'):
-        var ns = getNS(x.attrKey)
-        n.setAttributeNS(ns, x.attrKey, x.attrValue)
-      else:
-        n.setAttribute(x.attrKey, x.attrValue)
-
-    of xmlCData:
-      n.appendChild(doc.createCDATASection(x.charData()))
-    of xmlComment:
-      n.appendChild(doc.createComment(x.charData()))
-    of xmlPI:
-      n.appendChild(doc.createProcessingInstruction(x.piName(), x.piRest()))
-
-    of xmlWhitespace, xmlElementClose, xmlEntity, xmlSpecial:
-      discard " Unused \'events\'"
-
-    else:
-      raise newException(EParserError, "Unexpected XML Parser event")
-    x.next()
-
-  raise newException(EMismatchedTag,
-    "Mismatched tag at line " & $x.getLine() & " column " & $x.getColumn)
-
-proc loadXMLStream*(stream: Stream): PDocument =
-  ## Loads and parses XML from a stream specified by ``stream``, and returns
-  ## a ``PDocument``
-
-  var x: XmlParser
-  open(x, stream, nil, {reportComments})
-
-  var xmlDoc: PDocument
-  var dom: PDOMImplementation = getDOM()
-
-  while true:
-    x.next()
-    case x.kind()
-    of xmlEof:
-      break
-    of xmlElementStart, xmlElementOpen:
-      var el: PElement = parseElement(x, xmlDoc)
-      xmlDoc = dom.createDocument(el)
-    of xmlWhitespace, xmlElementClose, xmlEntity, xmlSpecial:
-      discard " Unused \'events\'"
-    else:
-      raise newException(EParserError, "Unexpected XML Parser event")
-
-  return xmlDoc
-
-proc loadXML*(xml: string): PDocument =
-  ## Loads and parses XML from a string specified by ``xml``, and returns
-  ## a ``PDocument``
-  var s = newStringStream(xml)
-  return loadXMLStream(s)
-
-
-proc loadXMLFile*(path: string): PDocument =
-  ## Loads and parses XML from a file specified by ``path``, and returns
-  ## a ``PDocument``
-
-  var s = newFileStream(path, fmRead)
-  if s == nil: raise newException(IOError, "Unable to read file " & path)
-  return loadXMLStream(s)
-
-
-when not defined(testing) and isMainModule:
-  var xml = loadXMLFile("nim/xmldom/test.xml")
-  #echo(xml.getElementsByTagName("m:test2")[0].namespaceURI)
-  #echo(xml.getElementsByTagName("bla:test")[0].namespaceURI)
-  #echo(xml.getElementsByTagName("test")[0].namespaceURI)
-  for i in items(xml.getElementsByTagName("*")):
-    if i.namespaceURI != nil:
-      echo(i.nodeName, "=", i.namespaceURI)
-
-
-  echo($xml)
diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim
index 597b80eb5..2c1e4e37c 100644
--- a/lib/pure/xmlparser.nim
+++ b/lib/pure/xmlparser.nim
@@ -9,7 +9,10 @@
 
 ## This module parses an XML document and creates its XML tree representation.
 
-import streams, parsexml, strtabs, xmltree
+import std/[streams, parsexml, strtabs, xmltree]
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
 
 type
   XmlError* = object of ValueError ## Exception that is raised
@@ -59,7 +62,7 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
   of xmlError:
     errors.add(errorMsg(x))
     next(x)
-  of xmlElementStart:    ## ``<elem>``
+  of xmlElementStart: ## ``<elem>``
     result = newElement(x.elementName)
     next(x)
     untilElementEnd(x, result, errors)
@@ -99,11 +102,11 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
   of xmlEof: discard
 
 proc parseXml*(s: Stream, filename: string,
-               errors: var seq[string]): XmlNode =
+               errors: var seq[string], options: set[XmlParseOption] = {reportComments}): XmlNode =
   ## Parses the XML from stream ``s`` and returns a ``XmlNode``. Every
   ## occurred parsing error is added to the ``errors`` sequence.
   var x: XmlParser
-  open(x, s, filename, {reportComments})
+  open(x, s, filename, options)
   while true:
     x.next()
     case x.kind
@@ -118,37 +121,37 @@ proc parseXml*(s: Stream, filename: string,
       break
   close(x)
 
-proc parseXml*(s: Stream): XmlNode =
+proc parseXml*(s: Stream, options: set[XmlParseOption] = {reportComments}): XmlNode =
   ## Parses the XML from stream ``s`` and returns a ``XmlNode``. All parsing
   ## errors are turned into an ``XmlError`` exception.
   var errors: seq[string] = @[]
-  result = parseXml(s, "unknown_xml_doc", errors)
+  result = parseXml(s, "unknown_xml_doc", errors, options)
   if errors.len > 0: raiseInvalidXml(errors)
 
-proc parseXml*(str: string): XmlNode =
+proc parseXml*(str: string, options: set[XmlParseOption] = {reportComments}): XmlNode =
   ## Parses the XML from string ``str`` and returns a ``XmlNode``. All parsing
   ## errors are turned into an ``XmlError`` exception.
-  parseXml(newStringStream(str))
+  parseXml(newStringStream(str), options)
 
-proc loadXml*(path: string, errors: var seq[string]): XmlNode =
+proc loadXml*(path: string, errors: var seq[string], options: set[XmlParseOption] = {reportComments}): XmlNode =
   ## Loads and parses XML from file specified by ``path``, and returns
   ## a ``XmlNode``. Every occurred parsing error is added to the ``errors``
   ## sequence.
   var s = newFileStream(path, fmRead)
   if s == nil: raise newException(IOError, "Unable to read file: " & path)
-  result = parseXml(s, path, errors)
+  result = parseXml(s, path, errors, options)
 
-proc loadXml*(path: string): XmlNode =
+proc loadXml*(path: string, options: set[XmlParseOption] = {reportComments}): XmlNode =
   ## Loads and parses XML from file specified by ``path``, and returns
   ## a ``XmlNode``. All parsing errors are turned into an ``XmlError``
   ## exception.
   var errors: seq[string] = @[]
-  result = loadXml(path, errors)
+  result = loadXml(path, errors, options)
   if errors.len > 0: raiseInvalidXml(errors)
 
 when isMainModule:
   when not defined(testing):
-    import os
+    import std/os
 
     var errors: seq[string] = @[]
     var x = loadXml(paramStr(1), errors)
@@ -169,4 +172,5 @@ when isMainModule:
 
     block bug1518:
       var err: seq[string] = @[]
-      assert $parsexml(newStringStream"<tag>One &amp; two</tag>", "temp.xml", err) == "<tag>One &amp; two</tag>"
+      assert $parsexml(newStringStream"<tag>One &amp; two</tag>", "temp.xml",
+          err) == "<tag>One &amp; two</tag>"
diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim
index 47658b59b..5c0cbc5e4 100644
--- a/lib/pure/xmltree.nim
+++ b/lib/pure/xmltree.nim
@@ -7,93 +7,290 @@
 #    distribution, for details about the copyright.
 #
 
-## A simple XML tree. More efficient and simpler than the DOM.
+## A simple XML tree generator.
+##
+runnableExamples:
+  var g = newElement("myTag")
+  g.add newText("some text")
+  g.add newComment("this is comment")
 
-import macros, strtabs, strutils
+  var h = newElement("secondTag")
+  h.add newEntity("some entity")
 
-type
-  XmlNode* = ref XmlNodeObj ## an XML tree consists of ``XmlNode``'s.
+  let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes
+  let k = newXmlTree("treeTag", [g, h], att)
+
+  doAssert $k == """<treeTag key1="first value" key2="second value">
+  <myTag>some text<!-- this is comment --></myTag>
+  <secondTag>&some entity;</secondTag>
+</treeTag>"""
+
+## **See also:**
+## * `xmlparser module <xmlparser.html>`_ for high-level XML parsing
+## * `parsexml module <parsexml.html>`_ for low-level XML parsing
+## * `htmlgen module <htmlgen.html>`_ for html code generator
 
-  XmlNodeKind* = enum  ## different kinds of ``XmlNode``'s
-    xnText,             ## a text element
-    xnElement,          ## an element with 0 or more children
-    xnCData,            ## a CDATA node
-    xnEntity,           ## an entity (like ``&thing;``)
-    xnComment           ## an XML comment
+import std/private/since
+import std/[macros, strtabs, strutils, sequtils]
 
-  XmlAttributes* = StringTableRef ## an alias for a string to string mapping
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
+type
+  XmlNode* = ref XmlNodeObj ## An XML tree consisting of XML nodes.
+    ##
+    ## Use `newXmlTree proc <#newXmlTree,string,openArray[XmlNode],XmlAttributes>`_
+    ## for creating a new tree.
+
+  XmlNodeKind* = enum ## Different kinds of XML nodes.
+    xnText,           ## a text element
+    xnVerbatimText,   ##
+    xnElement,        ## an element with 0 or more children
+    xnCData,          ## a CDATA node
+    xnEntity,         ## an entity (like ``&thing;``)
+    xnComment         ## an XML comment
+
+  XmlAttributes* = StringTableRef ## An alias for a string to string mapping.
+    ##
+    ## Use `toXmlAttributes proc <#toXmlAttributes,varargs[tuple[string,string]]>`_
+    ## to create `XmlAttributes`.
 
   XmlNodeObj {.acyclic.} = object
     case k: XmlNodeKind # private, use the kind() proc to read this field.
-    of xnText, xnComment, xnCData, xnEntity:
+    of xnText, xnVerbatimText, xnComment, xnCData, xnEntity:
       fText: string
     of xnElement:
       fTag: string
       s: seq[XmlNode]
       fAttr: XmlAttributes
-    fClientData: int              ## for other clients
+    fClientData: int    ## for other clients
+
+const
+  xmlHeader* = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
+    ## Header to use for complete XML output.
+
+template expect(node: XmlNode, kind: set[XmlNodeKind]) =
+  ## Check the node's kind is within a set of values
+  assert node.k in kind, "Got " & $node.k
+
+template expect(node: XmlNode, kind: XmlNodeKind) =
+  ## Check the node's kind equals a value
+  assert node.k == kind, "Got " & $node.k
 
 proc newXmlNode(kind: XmlNodeKind): XmlNode =
-  ## creates a new ``XmlNode``.
-  new(result)
-  result.k = kind
+  ## Creates a new ``XmlNode``.
+  result = XmlNode(k: kind)
+
+proc newElement*(tag: sink string): XmlNode =
+  ## Creates a new ``XmlNode`` of kind ``xnElement`` with the given `tag`.
+  ##
+  ## See also:
+  ## * `newXmlTree proc <#newXmlTree,string,openArray[XmlNode],XmlAttributes>`_
+  ## * [<> macro](#<>.m,untyped)
+  runnableExamples:
+    var a = newElement("firstTag")
+    a.add newElement("childTag")
+    assert a.kind == xnElement
+    assert $a == """<firstTag>
+  <childTag />
+</firstTag>"""
 
-proc newElement*(tag: string): XmlNode =
-  ## creates a new ``PXmlNode`` of kind ``xnText`` with the given `tag`.
   result = newXmlNode(xnElement)
   result.fTag = tag
   result.s = @[]
-  # init attributes lazily to safe memory
+  # init attributes lazily to save memory
+
+proc newText*(text: sink string): XmlNode =
+  ## Creates a new ``XmlNode`` of kind ``xnText`` with the text `text`.
+  runnableExamples:
+    var b = newText("my text")
+    assert b.kind == xnText
+    assert $b == "my text"
 
-proc newText*(text: string): XmlNode =
-  ## creates a new ``PXmlNode`` of kind ``xnText`` with the text `text`.
   result = newXmlNode(xnText)
   result.fText = text
 
-proc newComment*(comment: string): XmlNode =
-  ## creates a new ``PXmlNode`` of kind ``xnComment`` with the text `comment`.
+proc newVerbatimText*(text: sink string): XmlNode {.since: (1, 3).} =
+  ## Creates a new ``XmlNode`` of kind ``xnVerbatimText`` with the text `text`.
+  ## **Since**: Version 1.3.
+  result = newXmlNode(xnVerbatimText)
+  result.fText = text
+
+proc newComment*(comment: sink string): XmlNode =
+  ## Creates a new ``XmlNode`` of kind ``xnComment`` with the text `comment`.
+  runnableExamples:
+    var c = newComment("my comment")
+    assert c.kind == xnComment
+    assert $c == "<!-- my comment -->"
+
   result = newXmlNode(xnComment)
   result.fText = comment
 
-proc newCData*(cdata: string): XmlNode =
-  ## creates a new ``PXmlNode`` of kind ``xnComment`` with the text `cdata`.
+proc newCData*(cdata: sink string): XmlNode =
+  ## Creates a new ``XmlNode`` of kind ``xnCData`` with the text `cdata`.
+  runnableExamples:
+    var d = newCData("my cdata")
+    assert d.kind == xnCData
+    assert $d == "<![CDATA[my cdata]]>"
+
   result = newXmlNode(xnCData)
   result.fText = cdata
 
 proc newEntity*(entity: string): XmlNode =
-  ## creates a new ``PXmlNode`` of kind ``xnEntity`` with the text `entity`.
+  ## Creates a new ``XmlNode`` of kind ``xnEntity`` with the text `entity`.
+  runnableExamples:
+    var e = newEntity("my entity")
+    assert e.kind == xnEntity
+    assert $e == "&my entity;"
+
   result = newXmlNode(xnEntity)
   result.fText = entity
 
-proc text*(n: XmlNode): string {.inline.} =
-  ## gets the associated text with the node `n`. `n` can be a CDATA, Text,
-  ## comment, or entity node.
-  assert n.k in {xnText, xnComment, xnCData, xnEntity}
+proc newXmlTree*(tag: sink string, children: openArray[XmlNode],
+                 attributes: XmlAttributes = nil): XmlNode =
+  ## Creates a new XML tree with `tag`, `children` and `attributes`.
+  ##
+  ## See also:
+  ## * `newElement proc <#newElement,string>`_
+  ## * [<> macro](#<>.m,untyped)
+
+  runnableExamples:
+    var g = newElement("myTag")
+    g.add newText("some text")
+    g.add newComment("this is comment")
+    var h = newElement("secondTag")
+    h.add newEntity("some entity")
+    let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes
+    let k = newXmlTree("treeTag", [g, h], att)
+
+    doAssert $k == """<treeTag key1="first value" key2="second value">
+  <myTag>some text<!-- this is comment --></myTag>
+  <secondTag>&some entity;</secondTag>
+</treeTag>"""
+
+  result = newXmlNode(xnElement)
+  result.fTag = tag
+  newSeq(result.s, children.len)
+  for i in 0..children.len-1: result.s[i] = children[i]
+  result.fAttr = attributes
+
+proc text*(n: XmlNode): lent string {.inline.} =
+  ## Gets the associated text with the node `n`.
+  ##
+  ## `n` can be a CDATA, Text, comment, or entity node.
+  ##
+  ## See also:
+  ## * `text= proc <#text=,XmlNode,string>`_ for text setter
+  ## * `tag proc <#tag,XmlNode>`_ for tag getter
+  ## * `tag= proc <#tag=,XmlNode,string>`_ for tag setter
+  ## * `innerText proc <#innerText,XmlNode>`_
+  runnableExamples:
+    var c = newComment("my comment")
+    assert $c == "<!-- my comment -->"
+    assert c.text == "my comment"
+
+  n.expect {xnText, xnVerbatimText, xnComment, xnCData, xnEntity}
   result = n.fText
 
-proc `text=`*(n: XmlNode, text: string){.inline.} =
-  ## sets the associated text with the node `n`. `n` can be a CDATA, Text,
-  ## comment, or entity node.
-  assert n.k in {xnText, xnComment, xnCData, xnEntity}
+proc `text=`*(n: XmlNode, text: sink string) {.inline.} =
+  ## Sets the associated text with the node `n`.
+  ##
+  ## `n` can be a CDATA, Text, comment, or entity node.
+  ##
+  ## See also:
+  ## * `text proc <#text,XmlNode>`_ for text getter
+  ## * `tag proc <#tag,XmlNode>`_ for tag getter
+  ## * `tag= proc <#tag=,XmlNode,string>`_ for tag setter
+  runnableExamples:
+    var e = newEntity("my entity")
+    assert $e == "&my entity;"
+    e.text = "a new entity text"
+    assert $e == "&a new entity text;"
+
+  n.expect {xnText, xnVerbatimText, xnComment, xnCData, xnEntity}
   n.fText = text
 
+proc tag*(n: XmlNode): lent string {.inline.} =
+  ## Gets the tag name of `n`.
+  ##
+  ## `n` has to be an ``xnElement`` node.
+  ##
+  ## See also:
+  ## * `text proc <#text,XmlNode>`_ for text getter
+  ## * `text= proc <#text=,XmlNode,string>`_ for text setter
+  ## * `tag= proc <#tag=,XmlNode,string>`_ for tag setter
+  ## * `innerText proc <#innerText,XmlNode>`_
+  runnableExamples:
+    var a = newElement("firstTag")
+    a.add newElement("childTag")
+    assert $a == """<firstTag>
+  <childTag />
+</firstTag>"""
+    assert a.tag == "firstTag"
+
+  n.expect xnElement
+  result = n.fTag
+
+proc `tag=`*(n: XmlNode, tag: sink string) {.inline.} =
+  ## Sets the tag name of `n`.
+  ##
+  ## `n` has to be an ``xnElement`` node.
+  ##
+  ## See also:
+  ## * `text proc <#text,XmlNode>`_ for text getter
+  ## * `text= proc <#text=,XmlNode,string>`_ for text setter
+  ## * `tag proc <#tag,XmlNode>`_ for tag getter
+  runnableExamples:
+    var a = newElement("firstTag")
+    a.add newElement("childTag")
+    assert $a == """<firstTag>
+  <childTag />
+</firstTag>"""
+    a.tag = "newTag"
+    assert $a == """<newTag>
+  <childTag />
+</newTag>"""
+
+  n.expect xnElement
+  n.fTag = tag
+
 proc rawText*(n: XmlNode): string {.inline.} =
-  ## returns the underlying 'text' string by reference.
+  ## Returns the underlying 'text' string by reference.
+  ##
   ## This is only used for speed hacks.
-  shallowCopy(result, n.fText)
+  when defined(gcDestructors):
+    result = move(n.fText)
+  else:
+    shallowCopy(result, n.fText)
 
 proc rawTag*(n: XmlNode): string {.inline.} =
-  ## returns the underlying 'tag' string by reference.
+  ## Returns the underlying 'tag' string by reference.
+  ##
   ## This is only used for speed hacks.
-  shallowCopy(result, n.fTag)
+  when defined(gcDestructors):
+    result = move(n.fTag)
+  else:
+    shallowCopy(result, n.fTag)
 
 proc innerText*(n: XmlNode): string =
-  ## gets the inner text of `n`:
+  ## Gets the inner text of `n`:
   ##
   ## - If `n` is `xnText` or `xnEntity`, returns its content.
   ## - If `n` is `xnElement`, runs recursively on each child node and
   ##   concatenates the results.
   ## - Otherwise returns an empty string.
+  ##
+  ## See also:
+  ## * `text proc <#text,XmlNode>`_
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newText("my text")
+    f.add newComment("my comment")
+    f.add newEntity("my entity")
+    assert $f == "<myTag>my text<!-- my comment -->&my entity;</myTag>"
+    assert innerText(f) == "my textmy entity"
+
   proc worker(res: var string, n: XmlNode) =
     case n.k
     of xnText, xnEntity:
@@ -107,130 +304,438 @@ proc innerText*(n: XmlNode): string =
   result = ""
   worker(result, n)
 
-proc tag*(n: XmlNode): string {.inline.} =
-  ## gets the tag name of `n`. `n` has to be an ``xnElement`` node.
-  assert n.k == xnElement
-  result = n.fTag
-
-proc `tag=`*(n: XmlNode, tag: string) {.inline.} =
-  ## sets the tag name of `n`. `n` has to be an ``xnElement`` node.
-  assert n.k == xnElement
-  n.fTag = tag
-
 proc add*(father, son: XmlNode) {.inline.} =
-  ## adds the child `son` to `father`.
+  ## Adds the child `son` to `father`.
+  ## `father` must be of `xnElement` type
+  ##
+  ## See also:
+  ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_
+  ## * `insert proc <#insert,XmlNode,XmlNode,int>`_
+  ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_
+  ## * `delete proc <#delete,XmlNode,Natural>`_
+  ## * `delete proc <#delete.XmlNode,Slice[int]>`_
+  ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_
+  ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newText("my text")
+    f.add newElement("sonTag")
+    f.add newEntity("my entity")
+    assert $f == "<myTag>my text<sonTag />&my entity;</myTag>"
+
+  father.expect xnElement
   add(father.s, son)
 
+proc add*(father: XmlNode, sons: openArray[XmlNode]) {.inline.} =
+  ## Adds the children `sons` to `father`.
+  ## `father` must be of `xnElement` type
+  ##
+  ## See also:
+  ## * `add proc <#add,XmlNode,XmlNode>`_
+  ## * `insert proc <#insert,XmlNode,XmlNode,int>`_
+  ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_
+  ## * `delete proc <#delete,XmlNode,Natural>`_
+  ## * `delete proc <#delete.XmlNode,Slice[int]>`_
+  ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_
+  ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add(@[newText("my text"), newElement("sonTag"), newEntity("my entity")])
+    assert $f == "<myTag>my text<sonTag />&my entity;</myTag>"
+
+  father.expect xnElement
+  add(father.s, sons)
+
+
 proc insert*(father, son: XmlNode, index: int) {.inline.} =
-  ## insert the child `son` to a given position in `father`.
-  assert father.k == xnElement and son.k == xnElement
+  ## Inserts the child `son` to a given position in `father`.
+  ##
+  ## `father` must be of `xnElement` kind.
+  ##
+  ## See also:
+  ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_
+  ## * `add proc <#add,XmlNode,XmlNode>`_
+  ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_
+  ## * `delete proc <#delete,XmlNode,Natural>`_
+  ## * `delete proc <#delete.XmlNode,Slice[int]>`_
+  ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_
+  ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newElement("first")
+    f.insert(newElement("second"), 0)
+    assert $f == """<myTag>
+  <second />
+  <first />
+</myTag>"""
+
+  father.expect xnElement
   if len(father.s) > index:
     insert(father.s, son, index)
   else:
     insert(father.s, son, len(father.s))
 
+proc insert*(father: XmlNode, sons: openArray[XmlNode], index: int) {.inline.} =
+  ## Inserts the children openArray[`sons`] to a given position in `father`.
+  ##
+  ## `father` must be of `xnElement` kind.
+  ##
+  ## See also:
+  ## * `insert proc <#insert,XmlNode,XmlNode,int>`_
+  ## * `add proc <#add,XmlNode,XmlNode>`_
+  ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_
+  ## * `delete proc <#delete,XmlNode,Natural>`_
+  ## * `delete proc <#delete.XmlNode,Slice[int]>`_
+  ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_
+  ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newElement("first")
+    f.insert([newElement("second"), newElement("third")], 0)
+    assert $f == """<myTag>
+  <second />
+  <third />
+  <first />
+</myTag>"""
+
+  father.expect xnElement
+  if len(father.s) > index:
+    insert(father.s, sons, index)
+  else:
+    insert(father.s, sons, len(father.s))
+
+proc delete*(n: XmlNode, i: Natural) =
+  ## Deletes the `i`'th child of `n`.
+  ##
+  ## See also:
+  ## * `delete proc <#delete.XmlNode,Slice[int]>`_
+  ## * `add proc <#add,XmlNode,XmlNode>`_
+  ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_
+  ## * `insert proc <#insert,XmlNode,XmlNode,int>`_
+  ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_
+  ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_
+  ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newElement("first")
+    f.insert(newElement("second"), 0)
+    f.delete(0)
+    assert $f == """<myTag>
+  <first />
+</myTag>"""
+
+  n.expect xnElement
+  n.s.delete(i)
+
+proc delete*(n: XmlNode, slice: Slice[int]) =
+  ## Deletes the items `n[slice]` of `n`.
+  ##
+  ## See also:
+  ## * `delete proc <#delete.XmlNode,int>`_
+  ## * `add proc <#add,XmlNode,XmlNode>`_
+  ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_
+  ## * `insert proc <#insert,XmlNode,XmlNode,int>`_
+  ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_
+  ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_
+  ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newElement("first")
+    f.insert([newElement("second"), newElement("third")], 0)
+    f.delete(0..1)
+    assert $f == """<myTag>
+  <first />
+</myTag>"""
+
+  n.expect xnElement
+  n.s.delete(slice)
+
+proc replace*(n: XmlNode, i: Natural, replacement: openArray[XmlNode]) =
+  ## Replaces the `i`'th child of `n` with `replacement` openArray.
+  ##
+  ## `n` must be of `xnElement` kind.
+  ##
+  ## See also:
+  ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_
+  ## * `add proc <#add,XmlNode,XmlNode>`_
+  ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_
+  ## * `delete proc <#delete,XmlNode,Natural>`_
+  ## * `delete proc <#delete.XmlNode,Slice[int]>`_
+  ## * `insert proc <#insert,XmlNode,XmlNode,int>`_
+  ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newElement("first")
+    f.insert(newElement("second"), 0)
+    f.replace(0, @[newElement("third"), newElement("fourth")])
+    assert $f == """<myTag>
+  <third />
+  <fourth />
+  <first />
+</myTag>"""
+
+  n.expect xnElement
+  n.s.delete(i)
+  n.s.insert(replacement, i)
+
+proc replace*(n: XmlNode, slice: Slice[int], replacement: openArray[XmlNode]) =
+  ## Deletes the items `n[slice]` of `n`.
+  ##
+  ## `n` must be of `xnElement` kind.
+  ##
+  ## See also:
+  ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_
+  ## * `add proc <#add,XmlNode,XmlNode>`_
+  ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_
+  ## * `delete proc <#delete,XmlNode,Natural>`_
+  ## * `delete proc <#delete.XmlNode,Slice[int]>`_
+  ## * `insert proc <#insert,XmlNode,XmlNode,int>`_
+  ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newElement("first")
+    f.insert([newElement("second"), newElement("fifth")], 0)
+    f.replace(0..1, @[newElement("third"), newElement("fourth")])
+    assert $f == """<myTag>
+  <third />
+  <fourth />
+  <first />
+</myTag>"""
+
+  n.expect xnElement
+  n.s.delete(slice)
+  n.s.insert(replacement, slice.a)
+
 proc len*(n: XmlNode): int {.inline.} =
-  ## returns the number `n`'s children.
+  ## Returns the number of `n`'s children.
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newElement("first")
+    f.insert(newElement("second"), 0)
+    assert len(f) == 2
   if n.k == xnElement: result = len(n.s)
 
 proc kind*(n: XmlNode): XmlNodeKind {.inline.} =
-  ## returns `n`'s kind.
+  ## Returns `n`'s kind.
+  runnableExamples:
+    var a = newElement("firstTag")
+    assert a.kind == xnElement
+    var b = newText("my text")
+    assert b.kind == xnText
   result = n.k
 
-proc `[]`* (n: XmlNode, i: int): XmlNode {.inline.} =
-  ## returns the `i`'th child of `n`.
-  assert n.k == xnElement
-  result = n.s[i]
+proc `[]`*(n: XmlNode, i: int): XmlNode {.inline.} =
+  ## Returns the `i`'th child of `n`.
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newElement("first")
+    f.insert(newElement("second"), 0)
+    assert $f[1] == "<first />"
+    assert $f[0] == "<second />"
 
-proc delete*(n: XmlNode, i: Natural) {.noSideEffect.} =
-  ## delete the `i`'th child of `n`.
-  assert n.k == xnElement
-  n.s.delete(i)
+  n.expect xnElement
+  result = n.s[i]
 
-proc `[]`* (n: var XmlNode, i: int): var XmlNode {.inline.} =
-  ## returns the `i`'th child of `n` so that it can be modified
-  assert n.k == xnElement
+proc `[]`*(n: var XmlNode, i: int): var XmlNode {.inline.} =
+  ## Returns the `i`'th child of `n` so that it can be modified.
+  n.expect xnElement
   result = n.s[i]
 
+proc clear*(n: var XmlNode) =
+  ## Recursively clears all children of an XmlNode.
+  ##
+  runnableExamples:
+    var g = newElement("myTag")
+    g.add newText("some text")
+    g.add newComment("this is comment")
+
+    var h = newElement("secondTag")
+    h.add newEntity("some entity")
+
+    let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes
+    var k = newXmlTree("treeTag", [g, h], att)
+
+    doAssert $k == """<treeTag key1="first value" key2="second value">
+  <myTag>some text<!-- this is comment --></myTag>
+  <secondTag>&some entity;</secondTag>
+</treeTag>"""
+
+    clear(k)
+    doAssert $k == """<treeTag key1="first value" key2="second value" />"""
+
+  for i in 0 ..< n.len:
+    clear(n[i])
+  if n.k == xnElement:
+    n.s.setLen(0)
+
+
 iterator items*(n: XmlNode): XmlNode {.inline.} =
-  ## iterates over any child of `n`.
-  assert n.k == xnElement
+  ## Iterates over all direct children of `n`.
+
+  runnableExamples:
+    var g = newElement("myTag")
+    g.add newText("some text")
+    g.add newComment("this is comment")
+
+    var h = newElement("secondTag")
+    h.add newEntity("some entity")
+    g.add h
+
+    assert $g == "<myTag>some text<!-- this is comment --><secondTag>&some entity;</secondTag></myTag>"
+
+    # for x in g: # the same as `for x in items(g):`
+    #   echo x
+
+    # some text
+    # <!-- this is comment -->
+    # <secondTag>&some entity;<![CDATA[some cdata]]></secondTag>
+
+  n.expect xnElement
   for i in 0 .. n.len-1: yield n[i]
 
 iterator mitems*(n: var XmlNode): var XmlNode {.inline.} =
-  ## iterates over any child of `n`.
-  assert n.k == xnElement
+  ## Iterates over all direct children of `n` so that they can be modified.
+  n.expect xnElement
   for i in 0 .. n.len-1: yield n[i]
 
+proc toXmlAttributes*(keyValuePairs: varargs[tuple[key,
+    val: string]]): XmlAttributes =
+  ## Converts `{key: value}` pairs into `XmlAttributes`.
+  ##
+  runnableExamples:
+    let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes
+    var j = newElement("myTag")
+    j.attrs = att
+
+    doAssert $j == """<myTag key1="first value" key2="second value" />"""
+
+  newStringTable(keyValuePairs)
+
 proc attrs*(n: XmlNode): XmlAttributes {.inline.} =
-  ## gets the attributes belonging to `n`.
+  ## Gets the attributes belonging to `n`.
+  ##
   ## Returns `nil` if attributes have not been initialised for this node.
-  assert n.k == xnElement
+  ##
+  ## See also:
+  ## * `attrs= proc <#attrs=,XmlNode,XmlAttributes>`_ for XmlAttributes setter
+  ## * `attrsLen proc <#attrsLen,XmlNode>`_ for number of attributes
+  ## * `attr proc <#attr,XmlNode,string>`_ for finding an attribute
+  runnableExamples:
+    var j = newElement("myTag")
+    assert j.attrs == nil
+    let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes
+    j.attrs = att
+    assert j.attrs == att
+
+  n.expect xnElement
   result = n.fAttr
 
 proc `attrs=`*(n: XmlNode, attr: XmlAttributes) {.inline.} =
-  ## sets the attributes belonging to `n`.
-  assert n.k == xnElement
+  ## Sets the attributes belonging to `n`.
+  ##
+  ## See also:
+  ## * `attrs proc <#attrs,XmlNode>`_ for XmlAttributes getter
+  ## * `attrsLen proc <#attrsLen,XmlNode>`_ for number of attributes
+  ## * `attr proc <#attr,XmlNode,string>`_ for finding an attribute
+  runnableExamples:
+    var j = newElement("myTag")
+    assert j.attrs == nil
+    let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes
+    j.attrs = att
+    assert j.attrs == att
+
+  n.expect xnElement
   n.fAttr = attr
 
 proc attrsLen*(n: XmlNode): int {.inline.} =
-  ## returns the number of `n`'s attributes.
-  assert n.k == xnElement
+  ## Returns the number of `n`'s attributes.
+  ##
+  ## See also:
+  ## * `attrs proc <#attrs,XmlNode>`_ for XmlAttributes getter
+  ## * `attrs= proc <#attrs=,XmlNode,XmlAttributes>`_ for XmlAttributes setter
+  ## * `attr proc <#attr,XmlNode,string>`_ for finding an attribute
+  runnableExamples:
+    var j = newElement("myTag")
+    assert j.attrsLen == 0
+    let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes
+    j.attrs = att
+    assert j.attrsLen == 2
+
+  n.expect xnElement
   if not isNil(n.fAttr): result = len(n.fAttr)
 
+proc attr*(n: XmlNode, name: string): string =
+  ## Finds the first attribute of `n` with a name of `name`.
+  ## Returns "" on failure.
+  ##
+  ## See also:
+  ## * `attrs proc <#attrs,XmlNode>`_ for XmlAttributes getter
+  ## * `attrs= proc <#attrs=,XmlNode,XmlAttributes>`_ for XmlAttributes setter
+  ## * `attrsLen proc <#attrsLen,XmlNode>`_ for number of attributes
+  runnableExamples:
+    var j = newElement("myTag")
+    let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes
+    j.attrs = att
+    assert j.attr("key1") == "first value"
+    assert j.attr("key2") == "second value"
+
+  n.expect xnElement
+  if n.attrs == nil: return ""
+  return n.attrs.getOrDefault(name)
+
 proc clientData*(n: XmlNode): int {.inline.} =
-  ## gets the client data of `n`. The client data field is used by the HTML
-  ## parser and generator.
+  ## Gets the client data of `n`.
+  ##
+  ## The client data field is used by the HTML parser and generator.
   result = n.fClientData
 
 proc `clientData=`*(n: XmlNode, data: int) {.inline.} =
-  ## sets the client data of `n`. The client data field is used by the HTML
-  ## parser and generator.
+  ## Sets the client data of `n`.
+  ##
+  ## The client data field is used by the HTML parser and generator.
   n.fClientData = data
 
 proc addEscaped*(result: var string, s: string) =
-  ## same as ``result.add(escape(s))``, but more efficient.
+  ## The same as `result.add(escape(s)) <#escape,string>`_, but more efficient.
   for c in items(s):
     case c
     of '<': result.add("&lt;")
     of '>': result.add("&gt;")
     of '&': result.add("&amp;")
     of '"': result.add("&quot;")
-    of '\'': result.add("&#x27;")
-    of '/': result.add("&#x2F;")
+    of '\'': result.add("&apos;")
     else: result.add(c)
 
 proc escape*(s: string): string =
-  ## escapes `s` for inclusion into an XML document.
+  ## Escapes `s` for inclusion into an XML document.
+  ##
   ## Escapes these characters:
   ##
-  ## ------------    -------------------
+  ## ============    ===================
   ## char            is converted to
-  ## ------------    -------------------
+  ## ============    ===================
   ##  ``<``          ``&lt;``
   ##  ``>``          ``&gt;``
   ##  ``&``          ``&amp;``
   ##  ``"``          ``&quot;``
-  ##  ``'``          ``&#x27;``
-  ##  ``/``          ``&#x2F;``
-  ## ------------    -------------------
+  ##  ``'``          ``&apos;``
+  ## ============    ===================
+  ##
+  ## You can also use `addEscaped proc <#addEscaped,string,string>`_.
   result = newStringOfCap(s.len)
   addEscaped(result, s)
 
 proc addIndent(result: var string, indent: int, addNewLines: bool) =
   if addNewLines:
     result.add("\n")
-  for i in 1..indent: result.add(' ')
+  for i in 1 .. indent:
+    result.add(' ')
 
-proc noWhitespace(n: XmlNode): bool =
-  #for i in 1..n.len-1:
-  #  if n[i].kind != n[0].kind: return true
-  for i in 0..n.len-1:
-    if n[i].kind in {xnText, xnEntity}: return true
-
-proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2,
-          addNewLines=true) =
-  ## adds the textual representation of `n` to `result`.
+proc addImpl(result: var string, n: XmlNode, indent = 0, indWidth = 2,
+          addNewLines = true, lastNodeIsText = false) =
+  proc noWhitespace(n: XmlNode): bool =
+    for i in 0 ..< n.len:
+      if n[i].kind in {xnText, xnVerbatimText, xnEntity}: return true
 
   proc addEscapedAttr(result: var string, s: string) =
     # `addEscaped` alternative with less escaped characters.
@@ -244,8 +749,18 @@ proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2,
       else: result.add(c)
 
   if n == nil: return
+
   case n.k
   of xnElement:
+    if indent > 0 and not lastNodeIsText:
+      result.addIndent(indent, addNewLines)
+
+    let
+      addNewLines = if n.noWhitespace():
+                      false
+                    else:
+                      addNewLines
+
     result.add('<')
     result.add(n.fTag)
     if not isNil(n.fAttr):
@@ -255,29 +770,32 @@ proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2,
         result.add("=\"")
         result.addEscapedAttr(val)
         result.add('"')
-    if n.len > 0:
-      result.add('>')
-      if n.len > 1:
-        if noWhitespace(n):
-          # for mixed leaves, we cannot output whitespace for readability,
-          # because this would be wrong. For example: ``a<b>b</b>`` is
-          # different from ``a <b>b</b>``.
-          for i in 0..n.len-1:
-            result.add(n[i], indent+indWidth, indWidth, addNewLines)
-        else:
-          for i in 0..n.len-1:
-            result.addIndent(indent+indWidth, addNewLines)
-            result.add(n[i], indent+indWidth, indWidth, addNewLines)
-          result.addIndent(indent, addNewLines)
-      else:
-        result.add(n[0], indent+indWidth, indWidth, addNewLines)
-      result.add("</")
-      result.add(n.fTag)
-      result.add(">")
-    else:
+
+    if n.len == 0:
       result.add(" />")
+      return
+
+    let
+      indentNext = if n.noWhitespace():
+                     indent
+                   else:
+                     indent+indWidth
+    result.add('>')
+    var lastNodeIsText = false
+    for i in 0 ..< n.len:
+      result.addImpl(n[i], indentNext, indWidth, addNewLines, lastNodeIsText)
+      lastNodeIsText = (n[i].kind == xnText) or (n[i].kind == xnVerbatimText)
+
+    if not n.noWhitespace():
+      result.addIndent(indent, addNewLines)
+
+    result.add("</")
+    result.add(n.fTag)
+    result.add(">")
   of xnText:
     result.addEscaped(n.fText)
+  of xnVerbatimText:
+    result.add(n.fText)
   of xnComment:
     result.add("<!-- ")
     result.addEscaped(n.fText)
@@ -291,26 +809,106 @@ proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2,
     result.add(n.fText)
     result.add(';')
 
-const
-  xmlHeader* = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
-    ## header to use for complete XML output
+proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2,
+          addNewLines = true) {.inline.} =
+  ## Adds the textual representation of `n` to string `result`.
+  runnableExamples:
+    var
+      a = newElement("firstTag")
+      b = newText("my text")
+      c = newComment("my comment")
+      s = ""
+    s.add(c)
+    s.add(a)
+    s.add(b)
+    assert s == "<!-- my comment --><firstTag />my text"
+  result.addImpl(n, indent, indWidth, addNewLines)
 
 proc `$`*(n: XmlNode): string =
-  ## converts `n` into its string representation. No ``<$xml ...$>`` declaration
-  ## is produced, so that the produced XML fragments are composable.
+  ## Converts `n` into its string representation.
+  ##
+  ## No ``<$xml ...$>`` declaration is produced, so that the produced
+  ## XML fragments are composable.
   result = ""
   result.add(n)
 
-proc newXmlTree*(tag: string, children: openArray[XmlNode],
-                 attributes: XmlAttributes = nil): XmlNode =
-  ## creates a new XML tree with `tag`, `children` and `attributes`
-  result = newXmlNode(xnElement)
-  result.fTag = tag
-  newSeq(result.s, children.len)
-  for i in 0..children.len-1: result.s[i] = children[i]
-  result.fAttr = attributes
+proc child*(n: XmlNode, name: string): XmlNode =
+  ## Finds the first child element of `n` with a name of `name`.
+  ## Returns `nil` on failure.
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newElement("firstSon")
+    f.add newElement("secondSon")
+    f.add newElement("thirdSon")
+    assert $(f.child("secondSon")) == "<secondSon />"
+
+  n.expect xnElement
+  for i in items(n):
+    if i.kind == xnElement:
+      if i.tag == name:
+        return i
+
+proc findAll*(n: XmlNode, tag: string, result: var seq[XmlNode],
+    caseInsensitive = false) =
+  ## Iterates over all the children of `n` returning those matching `tag`.
+  ##
+  ## Found nodes satisfying the condition will be appended to the `result`
+  ## sequence.
+  runnableExamples:
+    var
+      b = newElement("good")
+      c = newElement("bad")
+      d = newElement("BAD")
+      e = newElement("GOOD")
+    b.add newText("b text")
+    c.add newText("c text")
+    d.add newText("d text")
+    e.add newText("e text")
+    let a = newXmlTree("father", [b, c, d, e])
+    var s = newSeq[XmlNode]()
+    a.findAll("good", s)
+    assert $s == "@[<good>b text</good>]"
+    s.setLen(0)
+    a.findAll("good", s, caseInsensitive = true)
+    assert $s == "@[<good>b text</good>, <GOOD>e text</GOOD>]"
+    s.setLen(0)
+    a.findAll("BAD", s)
+    assert $s == "@[<BAD>d text</BAD>]"
+    s.setLen(0)
+    a.findAll("BAD", s, caseInsensitive = true)
+    assert $s == "@[<bad>c text</bad>, <BAD>d text</BAD>]"
+
+  n.expect xnElement
+  for child in n.items():
+    if child.k != xnElement:
+      continue
+    if child.tag == tag or
+        (caseInsensitive and cmpIgnoreCase(child.tag, tag) == 0):
+      result.add(child)
+    child.findAll(tag, result)
+
+proc findAll*(n: XmlNode, tag: string, caseInsensitive = false): seq[XmlNode] =
+  ## A shortcut version to assign in let blocks.
+  runnableExamples:
+    var
+      b = newElement("good")
+      c = newElement("bad")
+      d = newElement("BAD")
+      e = newElement("GOOD")
+    b.add newText("b text")
+    c.add newText("c text")
+    d.add newText("d text")
+    e.add newText("e text")
+    let a = newXmlTree("father", [b, c, d, e])
+    assert $(a.findAll("good")) == "@[<good>b text</good>]"
+    assert $(a.findAll("BAD")) == "@[<BAD>d text</BAD>]"
+    assert $(a.findAll("good", caseInsensitive = true)) == "@[<good>b text</good>, <GOOD>e text</GOOD>]"
+    assert $(a.findAll("BAD", caseInsensitive = true)) == "@[<bad>c text</bad>, <BAD>d text</BAD>]"
+
+  newSeq(result, 0)
+  findAll(n, tag, result, caseInsensitive)
 
-proc xmlConstructor(a: NimNode): NimNode {.compileTime.} =
+proc xmlConstructor(a: NimNode): NimNode =
   if a.kind == nnkCall:
     result = newCall("newXmlTree", toStrLit(a[0]))
     var attrs = newNimNode(nnkBracket, a)
@@ -337,67 +935,12 @@ proc xmlConstructor(a: NimNode): NimNode {.compileTime.} =
 macro `<>`*(x: untyped): untyped =
   ## Constructor macro for XML. Example usage:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   <>a(href="http://nim-lang.org", newText("Nim rules."))
+  ##   ```
   ##
-  ## Produces an XML tree for::
+  ## Produces an XML tree for:
   ##
-  ##  <a href="http://nim-lang.org">Nim rules.</a>
+  ##     <a href="http://nim-lang.org">Nim rules.</a>
   ##
   result = xmlConstructor(x)
-
-proc child*(n: XmlNode, name: string): XmlNode =
-  ## Finds the first child element of `n` with a name of `name`.
-  ## Returns `nil` on failure.
-  assert n.kind == xnElement
-  for i in items(n):
-    if i.kind == xnElement:
-      if i.tag == name:
-        return i
-
-proc attr*(n: XmlNode, name: string): string =
-  ## Finds the first attribute of `n` with a name of `name`.
-  ## Returns "" on failure.
-  assert n.kind == xnElement
-  if n.attrs == nil: return ""
-  return n.attrs.getOrDefault(name)
-
-proc findAll*(n: XmlNode, tag: string, result: var seq[XmlNode]) =
-  ## Iterates over all the children of `n` returning those matching `tag`.
-  ##
-  ## Found nodes satisfying the condition will be appended to the `result`
-  ## sequence, which can't be nil or the proc will crash. Usage example:
-  ##
-  ## .. code-block::
-  ##   var
-  ##     html: XmlNode
-  ##     tags: seq[XmlNode] = @[]
-  ##
-  ##   html = buildHtml()
-  ##   findAll(html, "img", tags)
-  ##   for imgTag in tags:
-  ##     process(imgTag)
-  assert isNil(result) == false
-  assert n.k == xnElement
-  for child in n.items():
-    if child.k != xnElement:
-      continue
-    if child.tag == tag:
-      result.add(child)
-    child.findAll(tag, result)
-
-proc findAll*(n: XmlNode, tag: string): seq[XmlNode] =
-  ## Shortcut version to assign in let blocks. Example:
-  ##
-  ## .. code-block::
-  ##   var html: XmlNode
-  ##
-  ##   html = buildHtml(html)
-  ##   for imgTag in html.findAll("img"):
-  ##     process(imgTag)
-  newSeq(result, 0)
-  findAll(n, tag, result)
-
-when isMainModule:
-  assert """<a href="http://nim-lang.org">Nim rules.</a>""" ==
-    $(<>a(href="http://nim-lang.org", newText("Nim rules.")))
diff --git a/lib/std/appdirs.nim b/lib/std/appdirs.nim
new file mode 100644
index 000000000..963451efe
--- /dev/null
+++ b/lib/std/appdirs.nim
@@ -0,0 +1,94 @@
+## This module implements helpers for determining special directories used by apps.
+
+## .. importdoc:: paths.nim
+
+from std/private/osappdirs import nil
+import std/paths
+import std/envvars
+
+proc getHomeDir*(): Path {.inline, tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Returns the home directory of the current user.
+  ##
+  ## This proc is wrapped by the `expandTilde proc`_
+  ## for the convenience of processing paths coming from user configuration files.
+  ##
+  ## See also:
+  ## * `getConfigDir proc`_
+  ## * `getTempDir proc`_
+  result = Path(osappdirs.getHomeDir())
+
+proc getDataDir*(): Path {.inline, tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Returns the data directory of the current user for applications.
+  ## 
+  ## On non-Windows OSs, this proc conforms to the XDG Base Directory
+  ## spec. Thus, this proc returns the value of the `XDG_DATA_HOME` environment
+  ## variable if it is set, otherwise it returns the default configuration
+  ## directory ("~/.local/share" or "~/Library/Application Support" on macOS).
+  ## 
+  ## See also:
+  ## * `getHomeDir proc`_
+  ## * `getConfigDir proc`_
+  ## * `getTempDir proc`_
+  ## * `expandTilde proc`_
+  ## * `getCurrentDir proc`_
+  ## * `setCurrentDir proc`_
+  result = Path(osappdirs.getDataDir())
+
+proc getConfigDir*(): Path {.inline, tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Returns the config directory of the current user for applications.
+  ##
+  ## On non-Windows OSs, this proc conforms to the XDG Base Directory
+  ## spec. Thus, this proc returns the value of the `XDG_CONFIG_HOME` environment
+  ## variable if it is set, otherwise it returns the default configuration
+  ## directory ("~/.config/").
+  ##
+  ## An OS-dependent trailing slash is always present at the end of the
+  ## returned string: `\\` on Windows and `/` on all other OSs.
+  ##
+  ## See also:
+  ## * `getHomeDir proc`_
+  ## * `getTempDir proc`_
+  result = Path(osappdirs.getConfigDir())
+
+proc getCacheDir*(): Path {.inline.} =
+  ## Returns the cache directory of the current user for applications.
+  ##
+  ## This makes use of the following environment variables:
+  ##
+  ## * On Windows: `getEnv("LOCALAPPDATA")`
+  ##
+  ## * On macOS: `getEnv("XDG_CACHE_HOME", getEnv("HOME") / "Library/Caches")`
+  ##
+  ## * On other platforms: `getEnv("XDG_CACHE_HOME", getEnv("HOME") / ".cache")`
+  ##
+  ## **See also:**
+  ## * `getHomeDir proc`_
+  ## * `getTempDir proc`_
+  ## * `getConfigDir proc`_
+  # follows https://crates.io/crates/platform-dirs
+  result = Path(osappdirs.getCacheDir())
+
+proc getCacheDir*(app: Path): Path {.inline.} =
+  ## Returns the cache directory for an application `app`.
+  ##
+  ## * On Windows, this uses: `getCacheDir() / app / "cache"`
+  ## * On other platforms, this uses: `getCacheDir() / app`
+  result = Path(osappdirs.getCacheDir(app.string))
+
+proc getTempDir*(): Path {.inline, tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Returns the temporary directory of the current user for applications to
+  ## save temporary files in.
+  ##
+  ## On Windows, it calls [GetTempPath](https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppathw).
+  ## On Posix based platforms, it will check `TMPDIR`, `TEMP`, `TMP` and `TEMPDIR` environment variables in order.
+  ## On all platforms, `/tmp` will be returned if the procs fails.
+  ##
+  ## You can override this implementation
+  ## by adding `-d:tempDir=mytempname` to your compiler invocation.
+  ##
+  ## .. Note:: This proc does not check whether the returned path exists.
+  ##
+  ## See also:
+  ## * `getHomeDir proc`_
+  ## * `getConfigDir proc`_
+  result = Path(osappdirs.getTempDir())
diff --git a/lib/std/assertions.nim b/lib/std/assertions.nim
new file mode 100644
index 000000000..56c37d205
--- /dev/null
+++ b/lib/std/assertions.nim
@@ -0,0 +1,122 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2022 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+when not defined(nimPreviewSlimSystem) and not declared(sysFatal):
+  include "system/rawquits"
+  include "system/fatal"
+
+## This module implements assertion handling.
+
+import std/private/miscdollars
+# ---------------------------------------------------------------------------
+# helpers
+
+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.
+  result = ""
+  result.toLocation(info.filename, info.line, info.column + 1)
+
+# ---------------------------------------------------------------------------
+
+
+proc raiseAssert*(msg: string) {.noinline, noreturn, nosinks.} =
+  ## Raises an `AssertionDefect` with `msg`.
+  when defined(nimPreviewSlimSystem):
+    raise newException(AssertionDefect, msg)
+  else:
+    sysFatal(AssertionDefect, msg)
+
+proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} =
+  ## Raises an `AssertionDefect` with `msg`, but this is hidden
+  ## from the effect system. Called when an assertion failed.
+  raiseAssert(msg)
+
+template assertImpl(cond: bool, msg: string, expr: string, enabled: static[bool]) =
+  when enabled:
+    const
+      loc = instantiationInfo(fullPaths = compileOption("excessiveStackTrace"))
+      ploc = $loc
+    bind instantiationInfo
+    mixin failedAssertImpl
+    {.line: loc.}:
+      if not cond:
+        failedAssertImpl(ploc & " `" & expr & "` " & msg)
+
+template assert*(cond: untyped, msg = "") =
+  ## Raises `AssertionDefect` with `msg` if `cond` is false. Note
+  ## that `AssertionDefect` is hidden from the effect system, so it doesn't
+  ## produce `{.raises: [AssertionDefect].}`. This exception is only supposed
+  ## to be caught by unit testing frameworks.
+  ##
+  ## No code will be generated for `assert` when passing `-d:danger` (implied by `--assertions:off`).
+  ## See `command line switches <nimc.html#compiler-usage-commandminusline-switches>`_.
+  runnableExamples: assert 1 == 1
+  runnableExamples("--assertions:off"):
+    assert 1 == 2 # no code generated, no failure here
+  runnableExamples("-d:danger"): assert 1 == 2 # ditto
+  assertImpl(cond, msg, astToStr(cond), compileOption("assertions"))
+
+template doAssert*(cond: untyped, msg = "") =
+  ## Similar to `assert <#assert.t,untyped,string>`_ but is always turned on regardless of `--assertions`.
+  runnableExamples:
+    doAssert 1 == 1 # generates code even when built with `-d:danger` or `--assertions:off`
+  assertImpl(cond, msg, astToStr(cond), true)
+
+template onFailedAssert*(msg, code: untyped): untyped {.dirty.} =
+  ## Sets an assertion failure handler that will intercept any assert
+  ## statements following `onFailedAssert` in the current scope.
+  runnableExamples:
+    type MyError = object of CatchableError
+      lineinfo: tuple[filename: string, line: int, column: int]
+    # block-wide policy to change the failed assert exception type in order to
+    # include a lineinfo
+    onFailedAssert(msg):
+      raise (ref MyError)(msg: msg, lineinfo: instantiationInfo(-2))
+    doAssertRaises(MyError): doAssert false
+  when not defined(nimHasTemplateRedefinitionPragma):
+    {.pragma: redefine.}
+  template failedAssertImpl(msgIMPL: string): untyped {.dirty, redefine.} =
+    let msg = msgIMPL
+    code
+
+template doAssertRaises*(exception: typedesc, code: untyped) =
+  ## Raises `AssertionDefect` if specified `code` does not raise `exception`.
+  runnableExamples:
+    doAssertRaises(ValueError): raise newException(ValueError, "Hello World")
+    doAssertRaises(CatchableError): raise newException(ValueError, "Hello World")
+    doAssertRaises(AssertionDefect): doAssert false
+  var wrong = false
+  const begin = "expected raising '" & astToStr(exception) & "', instead"
+  const msgEnd = " by: " & astToStr(code)
+  template raisedForeign {.gensym.} = raiseAssert(begin & " raised foreign exception" & msgEnd)
+  {.push warning[BareExcept]:off.}
+  when Exception is exception:
+    try:
+      if true:
+        code
+      wrong = true
+    except Exception as e: discard
+    except: raisedForeign()
+  else:
+    try:
+      if true:
+        code
+      wrong = true
+    except exception:
+      discard
+    except Exception as e:
+      mixin `$` # alternatively, we could define $cstring in this module
+      raiseAssert(begin & " raised '" & $e.name & "'" & msgEnd)
+    except: raisedForeign()
+  {.pop.}
+  if wrong:
+    raiseAssert(begin & " nothing was raised" & msgEnd)
diff --git a/lib/std/cmdline.nim b/lib/std/cmdline.nim
new file mode 100644
index 000000000..0ba4619e5
--- /dev/null
+++ b/lib/std/cmdline.nim
@@ -0,0 +1,313 @@
+#

+#

+#            Nim's Runtime Library

+#        (c) Copyright 2022 Andreas Rumpf

+#

+#    See the file "copying.txt", included in this

+#    distribution, for details about the copyright.

+#

+

+## This module contains system facilities for reading command

+## line parameters.

+

+## **See also:**

+## * `parseopt module <parseopt.html>`_ for command-line parser beyond

+##   `parseCmdLine proc`_

+

+

+include system/inclrtl

+

+when defined(nimPreviewSlimSystem):

+  import std/widestrs

+  

+when defined(nodejs):

+  from std/private/oscommon import ReadDirEffect

+

+

+const weirdTarget = defined(nimscript) or defined(js)

+

+

+when weirdTarget:

+  discard

+elif defined(windows):

+  import std/winlean

+elif defined(posix):

+  import std/posix

+else:

+  {.error: "The cmdline module has not been implemented for the target platform.".}

+

+

+# Needed by windows in order to obtain the command line for targets

+# other than command line targets

+when defined(windows) and not weirdTarget:

+  template getCommandLine*(): untyped = getCommandLineW()

+

+

+proc parseCmdLine*(c: string): seq[string] {.

+  noSideEffect, rtl, extern: "nos$1".} =

+  ## Splits a `command line`:idx: into several components.

+  ##

+  ## **Note**: This proc is only occasionally useful, better use the

+  ## `parseopt module <parseopt.html>`_.

+  ##

+  ## On Windows, it uses the `following parsing rules

+  ## <http://msdn.microsoft.com/en-us/library/17w5ykft.aspx>`_:

+  ##

+  ## * Arguments are delimited by white space, which is either a space or a tab.

+  ## * The caret character (^) is not recognized as an escape character or

+  ##   delimiter. The character is handled completely by the command-line parser

+  ##   in the operating system before being passed to the argv array in the

+  ##   program.

+  ## * A string surrounded by double quotation marks ("string") is interpreted

+  ##   as a single argument, regardless of white space contained within. A

+  ##   quoted string can be embedded in an argument.

+  ## * A double quotation mark preceded by a backslash (\") is interpreted as a

+  ##   literal double quotation mark character (").

+  ## * Backslashes are interpreted literally, unless they immediately precede

+  ##   a double quotation mark.

+  ## * If an even number of backslashes is followed by a double quotation mark,

+  ##   one backslash is placed in the argv array for every pair of backslashes,

+  ##   and the double quotation mark is interpreted as a string delimiter.

+  ## * If an odd number of backslashes is followed by a double quotation mark,

+  ##   one backslash is placed in the argv array for every pair of backslashes,

+  ##   and the double quotation mark is "escaped" by the remaining backslash,

+  ##   causing a literal double quotation mark (") to be placed in argv.

+  ##

+  ## On Posix systems, it uses the following parsing rules:

+  ## Components are separated by whitespace unless the whitespace

+  ## occurs within ``"`` or ``'`` quotes.

+  ##

+  ## See also:

+  ## * `parseopt module <parseopt.html>`_

+  ## * `paramCount proc`_

+  ## * `paramStr proc`_

+  ## * `commandLineParams proc`_

+

+  result = @[]

+  var i = 0

+  var a = ""

+  while true:

+    setLen(a, 0)

+    # eat all delimiting whitespace

+    while i < c.len and c[i] in {' ', '\t', '\l', '\r'}: inc(i)

+    if i >= c.len: break

+    when defined(windows):

+      # parse a single argument according to the above rules:

+      var inQuote = false

+      while i < c.len:

+        case c[i]

+        of '\\':

+          var j = i

+          while j < c.len and c[j] == '\\': inc(j)

+          if j < c.len and c[j] == '"':

+            for k in 1..(j-i) div 2: a.add('\\')

+            if (j-i) mod 2 == 0:

+              i = j

+            else:

+              a.add('"')

+              i = j+1

+          else:

+            a.add(c[i])

+            inc(i)

+        of '"':

+          inc(i)

+          if not inQuote: inQuote = true

+          elif i < c.len and c[i] == '"':

+            a.add(c[i])

+            inc(i)

+          else:

+            inQuote = false

+            break

+        of ' ', '\t':

+          if not inQuote: break

+          a.add(c[i])

+          inc(i)

+        else:

+          a.add(c[i])

+          inc(i)

+    else:

+      case c[i]

+      of '\'', '\"':

+        var delim = c[i]

+        inc(i) # skip ' or "

+        while i < c.len and c[i] != delim:

+          add a, c[i]

+          inc(i)

+        if i < c.len: inc(i)

+      else:

+        while i < c.len and c[i] > ' ':

+          add(a, c[i])

+          inc(i)

+    add(result, move a)

+

+when defined(nimdoc):

+  # Common forward declaration docstring block for parameter retrieval procs.

+  proc paramCount*(): int {.tags: [ReadIOEffect].} =

+    ## Returns the number of `command line arguments`:idx: given to the

+    ## application.

+    ##

+    ## Unlike `argc`:idx: in C, if your binary was called without parameters this

+    ## will return zero.

+    ## You can query each individual parameter with `paramStr proc`_

+    ## or retrieve all of them in one go with `commandLineParams proc`_.

+    ##

+    ## **Availability**: When generating a dynamic library (see `--app:lib`) on

+    ## Posix this proc is not defined.

+    ## Test for availability using `declared() <system.html#declared,untyped>`_.

+    ##

+    ## See also:

+    ## * `parseopt module <parseopt.html>`_

+    ## * `parseCmdLine proc`_

+    ## * `paramStr proc`_

+    ## * `commandLineParams proc`_

+    ##

+    ## **Examples:**

+    ##

+    ##   ```nim

+    ##   when declared(paramCount):

+    ##     # Use paramCount() here

+    ##   else:

+    ##     # Do something else!

+    ##   ```

+

+  proc paramStr*(i: int): string {.tags: [ReadIOEffect].} =

+    ## Returns the `i`-th `command line argument`:idx: given to the application.

+    ##

+    ## `i` should be in the range `1..paramCount()`, the `IndexDefect`

+    ## exception will be raised for invalid values. Instead of iterating

+    ## over `paramCount()`_ with this proc you can

+    ## call the convenience `commandLineParams()`_.

+    ##

+    ## Similarly to `argv`:idx: in C,

+    ## it is possible to call `paramStr(0)` but this will return OS specific

+    ## contents (usually the name of the invoked executable). You should avoid

+    ## this and call `getAppFilename() <os.html#getAppFilename>`_ instead.

+    ##

+    ## **Availability**: When generating a dynamic library (see `--app:lib`) on

+    ## Posix this proc is not defined.

+    ## Test for availability using `declared() <system.html#declared,untyped>`_.

+    ##

+    ## See also:

+    ## * `parseopt module <parseopt.html>`_

+    ## * `parseCmdLine proc`_

+    ## * `paramCount proc`_

+    ## * `commandLineParams proc`_

+    ## * `getAppFilename proc <os.html#getAppFilename>`_

+    ##

+    ## **Examples:**

+    ##

+    ##   ```nim

+    ##   when declared(paramStr):

+    ##     # Use paramStr() here

+    ##   else:

+    ##     # Do something else!

+    ##   ```

+

+elif defined(nimscript): discard

+elif defined(nodejs):

+  type Argv = object of JsRoot

+  let argv {.importjs: "process.argv".} : Argv

+  proc len(argv: Argv): int {.importjs: "#.length".}

+  proc `[]`(argv: Argv, i: int): cstring {.importjs: "#[#]".}

+

+  proc paramCount*(): int {.tags: [ReadDirEffect].} =

+    result = argv.len - 2

+

+  proc paramStr*(i: int): string {.tags: [ReadIOEffect].} =

+    let i = i + 1

+    if i < argv.len and i >= 0:

+      result = $argv[i]

+    else:

+      raise newException(IndexDefect, formatErrorIndexBound(i - 1, argv.len - 2))

+elif defined(windows):

+  # Since we support GUI applications with Nim, we sometimes generate

+  # a WinMain entry proc. But a WinMain proc has no access to the parsed

+  # command line arguments. The way to get them differs. Thus we parse them

+  # ourselves. This has the additional benefit that the program's behaviour

+  # is always the same -- independent of the used C compiler.

+  var

+    ownArgv {.threadvar.}: seq[string]

+    ownParsedArgv {.threadvar.}: bool

+

+  proc paramCount*(): int {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =

+    # Docstring in nimdoc block.

+    if not ownParsedArgv:

+      ownArgv = parseCmdLine($getCommandLine())

+      ownParsedArgv = true

+    result = ownArgv.len-1

+

+  proc paramStr*(i: int): string {.rtl, extern: "nos$1",

+    tags: [ReadIOEffect].} =

+    # Docstring in nimdoc block.

+    if not ownParsedArgv:

+      ownArgv = parseCmdLine($getCommandLine())

+      ownParsedArgv = true

+    if i < ownArgv.len and i >= 0:

+      result = ownArgv[i]

+    else:

+      raise newException(IndexDefect, formatErrorIndexBound(i, ownArgv.len-1))

+

+elif defined(genode):

+  proc paramStr*(i: int): string =

+    raise newException(OSError, "paramStr is not implemented on Genode")

+

+  proc paramCount*(): int =

+    raise newException(OSError, "paramCount is not implemented on Genode")

+elif weirdTarget or (defined(posix) and appType == "lib"):

+  proc paramStr*(i: int): string {.tags: [ReadIOEffect].} =

+    raise newException(OSError, "paramStr is not implemented on current platform")

+

+  proc paramCount*(): int {.tags: [ReadIOEffect].} =

+    raise newException(OSError, "paramCount is not implemented on current platform")

+elif not defined(createNimRtl) and

+  not(defined(posix) and appType == "lib"):

+  # On Posix, there is no portable way to get the command line from a DLL.

+  var

+    cmdCount {.importc: "cmdCount".}: cint

+    cmdLine {.importc: "cmdLine".}: cstringArray

+

+  proc paramStr*(i: int): string {.tags: [ReadIOEffect].} =

+    # Docstring in nimdoc block.

+    if i < cmdCount and i >= 0:

+      result = $cmdLine[i]

+    else:

+      raise newException(IndexDefect, formatErrorIndexBound(i, cmdCount-1))

+

+  proc paramCount*(): int {.tags: [ReadIOEffect].} =

+    # Docstring in nimdoc block.

+    result = cmdCount-1

+

+when declared(paramCount) or defined(nimdoc):

+  proc commandLineParams*(): seq[string] =

+    ## Convenience proc which returns the command line parameters.

+    ##

+    ## This returns **only** the parameters. If you want to get the application

+    ## executable filename, call `getAppFilename() <os.html#getAppFilename>`_.

+    ##

+    ## **Availability**: On Posix there is no portable way to get the command

+    ## line from a DLL and thus the proc isn't defined in this environment. You

+    ## can test for its availability with `declared()

+    ## <system.html#declared,untyped>`_.

+    ##

+    ## See also:

+    ## * `parseopt module <parseopt.html>`_

+    ## * `parseCmdLine proc`_

+    ## * `paramCount proc`_

+    ## * `paramStr proc`_

+    ## * `getAppFilename proc <os.html#getAppFilename>`_

+    ##

+    ## **Examples:**

+    ##

+    ##   ```nim

+    ##   when declared(commandLineParams):

+    ##     # Use commandLineParams() here

+    ##   else:

+    ##     # Do something else!

+    ##   ```

+    result = @[]

+    for i in 1..paramCount():

+      result.add(paramStr(i))

+else:

+  proc commandLineParams*(): seq[string] {.error:

+  "commandLineParams() unsupported by dynamic libraries".} =

+    discard

diff --git a/lib/std/compilesettings.nim b/lib/std/compilesettings.nim
new file mode 100644
index 000000000..6d8bd22f4
--- /dev/null
+++ b/lib/std/compilesettings.nim
@@ -0,0 +1,66 @@
+#
+#
+#              Nim's Runtime Library
+#        (c) Copyright 2020 Nim Contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module allows querying the compiler about
+## diverse configuration settings. See also `compileOption`.
+
+# Note: Only add new enum values at the end to ensure binary compatibility with
+# other Nim compiler versions!
+
+type
+  SingleValueSetting* {.pure.} = enum ## \
+                      ## settings resulting in a single string value
+    arguments,        ## experimental: the arguments passed after '-r'
+    outFile,          ## experimental: the output file
+    outDir,           ## the output directory
+    nimcacheDir,      ## the location of the 'nimcache' directory
+    projectName,      ## the project's name that is being compiled
+    projectPath,      ## experimental: some path to the project that is being compiled
+    projectFull,      ## the full path to the project that is being compiled
+    command,          ## experimental: the command (e.g. 'c', 'cpp', 'doc') passed to
+                      ## the Nim compiler
+    commandLine,      ## experimental: the command line passed to Nim
+    linkOptions,      ## additional options passed to the linker
+    compileOptions,   ## additional options passed to the C/C++ compiler
+    ccompilerPath     ## the path to the C/C++ compiler
+    backend           ## the backend (eg: c|cpp|objc|js); both `nim doc --backend:js`
+                      ## and `nim js` would imply backend=js
+    libPath           ## the absolute path to the stdlib library, i.e. nim's `--lib`, since 1.5.1
+    gc {.deprecated.} ## gc selected
+    mm                ## memory management selected
+
+  MultipleValueSetting* {.pure.} = enum ## \
+                      ## settings resulting in a seq of string values
+    nimblePaths,      ## the nimble path(s)
+    searchPaths,      ## the search path for modules
+    lazyPaths,        ## experimental: even more paths
+    commandArgs,      ## the arguments passed to the Nim compiler
+    cincludes,        ## the #include paths passed to the C/C++ compiler
+    clibs             ## libraries passed to the C/C++ compiler
+
+proc querySetting*(setting: SingleValueSetting): string {.
+  compileTime, noSideEffect.} =
+  ## Can be used to get a string compile-time option.
+  ##
+  ## See also:
+  ## * `compileOption <system.html#compileOption,string>`_ for `on|off` options
+  ## * `compileOption <system.html#compileOption,string,string>`_ for enum options
+  ##
+  runnableExamples:
+    const nimcache = querySetting(SingleValueSetting.nimcacheDir)
+
+proc querySettingSeq*(setting: MultipleValueSetting): seq[string] {.
+  compileTime, noSideEffect.} =
+  ## Can be used to get a multi-string compile-time option.
+  ##
+  ## See also:
+  ## * `compileOption <system.html#compileOption,string>`_ for `on|off` options
+  ## * `compileOption <system.html#compileOption,string,string>`_ for enum options
+  runnableExamples:
+    const nimblePaths = querySettingSeq(MultipleValueSetting.nimblePaths)
diff --git a/lib/std/decls.nim b/lib/std/decls.nim
new file mode 100644
index 000000000..bb7ec3593
--- /dev/null
+++ b/lib/std/decls.nim
@@ -0,0 +1,31 @@
+## This module implements syntax sugar for some declarations.
+
+import std/macros
+
+macro byaddr*(sect) =
+  ## Allows a syntax for l-value references, being an exact analog to
+  ## `auto& a = ex;` in C++.
+  ## 
+  ## .. warning:: This makes use of 2 experimental features, namely nullary
+  ##   templates instantiated as symbols and variable macro pragmas.
+  ##   For this reason, its behavior is not stable. The current implementation
+  ##   allows redefinition, but this is not an intended consequence.
+  runnableExamples:
+    var s = @[10, 11, 12]
+    var a {.byaddr.} = s[0]
+    a += 100
+    assert s == @[110, 11, 12]
+    assert a is int
+    var b {.byaddr.}: int = s[0]
+    assert a.addr == b.addr
+  expectLen sect, 1
+  let def = sect[0]
+  let
+    lhs = def[0]
+    typ = def[1]
+    ex = def[2]
+    addrTyp = if typ.kind == nnkEmpty: typ else: newTree(nnkPtrTy, typ)
+  result = quote do:
+    let tmp: `addrTyp` = addr(`ex`)
+    template `lhs`: untyped = tmp[]
+  result.copyLineInfo(def)
diff --git a/lib/std/dirs.nim b/lib/std/dirs.nim
new file mode 100644
index 000000000..380d6d08f
--- /dev/null
+++ b/lib/std/dirs.nim
@@ -0,0 +1,135 @@
+## This module implements directory handling.
+
+from std/paths import Path, ReadDirEffect, WriteDirEffect
+
+from std/private/osdirs import dirExists, createDir, existsOrCreateDir, removeDir,
+                               moveDir, walkDir, setCurrentDir,
+                               walkDirRec, PathComponent
+
+export PathComponent
+
+proc dirExists*(dir: Path): bool {.inline, tags: [ReadDirEffect], sideEffect.} =
+  ## Returns true if the directory `dir` exists. If `dir` is a file, false
+  ## is returned. Follows symlinks.
+  result = dirExists(dir.string)
+
+proc createDir*(dir: Path) {.inline, tags: [WriteDirEffect, ReadDirEffect].} =
+  ## Creates the `directory`:idx: `dir`.
+  ##
+  ## The directory may contain several subdirectories that do not exist yet.
+  ## The full path is created. If this fails, `OSError` is raised.
+  ##
+  ## It does **not** fail if the directory already exists because for
+  ## most usages this does not indicate an error.
+  ##
+  ## See also:
+  ## * `removeDir proc`_
+  ## * `existsOrCreateDir proc`_
+  ## * `moveDir proc`_
+  createDir(dir.string)
+
+proc existsOrCreateDir*(dir: Path): bool {.inline, tags: [WriteDirEffect, ReadDirEffect].} =
+  ## Checks if a `directory`:idx: `dir` exists, and creates it otherwise.
+  ##
+  ## Does not create parent directories (raises `OSError` if parent directories do not exist).
+  ## Returns `true` if the directory already exists, and `false` otherwise.
+  ##
+  ## See also:
+  ## * `removeDir proc`_
+  ## * `createDir proc`_
+  ## * `moveDir proc`_
+  result = existsOrCreateDir(dir.string)
+
+proc removeDir*(dir: Path, checkDir = false
+                ) {.inline, tags: [WriteDirEffect, ReadDirEffect].} =
+  ## Removes the directory `dir` including all subdirectories and files
+  ## in `dir` (recursively).
+  ##
+  ## If this fails, `OSError` is raised. This does not fail if the directory never
+  ## existed in the first place, unless `checkDir` = true.
+  ##
+  ## See also:
+  ## * `removeFile proc <files.html#removeFile>`_
+  ## * `existsOrCreateDir proc`_
+  ## * `createDir proc`_
+  ## * `moveDir proc`_
+  removeDir(dir.string, checkDir)
+
+proc moveDir*(source, dest: Path) {.inline, tags: [ReadIOEffect, WriteIOEffect].} =
+  ## Moves a directory from `source` to `dest`.
+  ##
+  ## Symlinks are not followed: if `source` contains symlinks, they themself are
+  ## moved, not their target.
+  ##
+  ## If this fails, `OSError` is raised.
+  ##
+  ## See also:
+  ## * `moveFile proc <files.html#moveFile>`_
+  ## * `removeDir proc`_
+  ## * `existsOrCreateDir proc`_
+  ## * `createDir proc`_
+  moveDir(source.string, dest.string)
+
+iterator walkDir*(dir: Path; relative = false, checkDir = false,
+                 skipSpecial = false):
+    tuple[kind: PathComponent, path: Path] {.tags: [ReadDirEffect].} =
+  ## Walks over the directory `dir` and yields for each directory or file in
+  ## `dir`. The component type and full path for each item are returned.
+  ##
+  ## Walking is not recursive.
+  ## * If `relative` is true (default: false)
+  ##   the resulting path is shortened to be relative to ``dir``,
+  ##   otherwise the full path is returned.
+  ## * If `checkDir` is true, `OSError` is raised when `dir`
+  ##   doesn't exist.
+  ## * If `skipSpecial` is true, then (besides all directories) only *regular*
+  ##   files (**without** special "file" objects like FIFOs, device files,
+  ##   etc) will be yielded on Unix.
+  for (k, p) in walkDir(dir.string, relative, checkDir, skipSpecial):
+    yield (k, Path(p))
+
+iterator walkDirRec*(dir: Path,
+                     yieldFilter = {pcFile}, followFilter = {pcDir},
+                     relative = false, checkDir = false, skipSpecial = false):
+                    Path {.tags: [ReadDirEffect].} =
+  ## Recursively walks over the directory `dir` and yields for each file
+  ## or directory in `dir`.
+  ##
+  ## Options `relative`, `checkdir`, `skipSpecial` are explained in
+  ## [walkDir iterator] description.
+  ##
+  ## .. warning:: Modifying the directory structure while the iterator
+  ##   is traversing may result in undefined behavior!
+  ##
+  ## Walking is recursive. `followFilter` controls the behaviour of the iterator:
+  ##
+  ## =====================   =============================================
+  ## yieldFilter             meaning
+  ## =====================   =============================================
+  ## ``pcFile``              yield real files (default)
+  ## ``pcLinkToFile``        yield symbolic links to files
+  ## ``pcDir``               yield real directories
+  ## ``pcLinkToDir``         yield symbolic links to directories
+  ## =====================   =============================================
+  ##
+  ## =====================   =============================================
+  ## followFilter            meaning
+  ## =====================   =============================================
+  ## ``pcDir``               follow real directories (default)
+  ## ``pcLinkToDir``         follow symbolic links to directories
+  ## =====================   =============================================
+  ##
+  ##
+  ## See also:
+  ## * `walkDir iterator`_
+  for p in walkDirRec(dir.string, yieldFilter, followFilter, relative,
+                      checkDir, skipSpecial):
+    yield Path(p)
+
+proc setCurrentDir*(newDir: Path) {.inline, tags: [].} =
+  ## Sets the `current working directory`:idx:; `OSError`
+  ## is raised if `newDir` cannot been set.
+  ##
+  ## See also:
+  ## * `getCurrentDir proc <paths.html#getCurrentDir>`_
+  osdirs.setCurrentDir(newDir.string)
diff --git a/lib/std/editdistance.nim b/lib/std/editdistance.nim
new file mode 100644
index 000000000..40c0017ae
--- /dev/null
+++ b/lib/std/editdistance.nim
@@ -0,0 +1,266 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2018 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements an algorithm to compute the
+## `edit distance`:idx: between two Unicode strings.
+
+import std/unicode
+
+proc editDistance*(a, b: string): int {.noSideEffect.} =
+  ## Returns the **unicode-rune** edit distance between `a` and `b`.
+  ##
+  ## This uses the `Levenshtein`:idx: distance algorithm with only a linear
+  ## memory overhead.
+  runnableExamples: static: doAssert editdistance("Kitten", "Bitten") == 1
+  if runeLen(a) > runeLen(b):
+    # make `b` the longer string
+    return editDistance(b, a)
+  # strip common prefix
+  var
+    iStart = 0 ## The character starting index of the first rune in both strings `a` and `b`
+    iNextA = 0
+    iNextB = 0
+    runeA, runeB: Rune
+    lenRunesA = 0 ## The number of relevant runes in string `a`.
+    lenRunesB = 0 ## The number of relevant runes in string `b`.
+  block commonPrefix:
+    # `a` is the shorter string
+    while iStart < len(a):
+      iNextA = iStart
+      a.fastRuneAt(iNextA, runeA, doInc = true)
+      iNextB = iStart
+      b.fastRuneAt(iNextB, runeB, doInc = true)
+      if runeA != runeB:
+        inc(lenRunesA)
+        inc(lenRunesB)
+        break
+      iStart = iNextA
+  var
+    # we know that we are either at the start of the strings
+    # or that the current value of runeA is not equal to runeB
+    # => start search for common suffix after the current rune (`i_next_*`)
+    iEndA = iNextA ## The exclusive upper index bound of string `a`.
+    iEndB = iNextB ## The exclusive upper index bound of string `b`.
+    iCurrentA = iNextA
+    iCurrentB = iNextB
+  block commonSuffix:
+    var
+      addRunesA = 0
+      addRunesB = 0
+    while iCurrentA < len(a) and iCurrentB < len(b):
+      iNextA = iCurrentA
+      a.fastRuneAt(iNextA, runeA)
+      iNextB = iCurrentB
+      b.fastRuneAt(iNextB, runeB)
+      inc(addRunesA)
+      inc(addRunesB)
+      if runeA != runeB:
+        iEndA = iNextA
+        iEndB = iNextB
+        inc(lenRunesA, addRunesA)
+        inc(lenRunesB, addRunesB)
+        addRunesA = 0
+        addRunesB = 0
+      iCurrentA = iNextA
+      iCurrentB = iNextB
+    if iCurrentA >= len(a): # `a` exhausted
+      if iCurrentB < len(b): # `b` not exhausted
+        iEndA = iCurrentA
+        iEndB = iCurrentB
+        inc(lenRunesA, addRunesA)
+        inc(lenRunesB, addRunesB)
+        while true:
+          b.fastRuneAt(iEndB, runeB)
+          inc(lenRunesB)
+          if iEndB >= len(b): break
+    elif iCurrentB >= len(b): # `b` exhausted and `a` not exhausted
+      iEndA = iCurrentA
+      iEndB = iCurrentB
+      inc(lenRunesA, addRunesA)
+      inc(lenRunesB, addRunesB)
+      while true:
+        a.fastRuneAt(iEndA, runeA)
+        inc(lenRunesA)
+        if iEndA >= len(a): break
+  block specialCases:
+    # trivial cases:
+    if lenRunesA == 0: return lenRunesB
+    if lenRunesB == 0: return lenRunesA
+    # another special case:
+    if lenRunesA == 1:
+      a.fastRuneAt(iStart, runeA, doInc = false)
+      var iCurrentB = iStart
+      while iCurrentB < iEndB:
+        b.fastRuneAt(iCurrentB, runeB, doInc = true)
+        if runeA == runeB: return lenRunesB - 1
+      return lenRunesB
+  # common case:
+  var
+    len1 = lenRunesA + 1
+    len2 = lenRunesB + 1
+    row: seq[int]
+  let half = lenRunesA div 2
+  newSeq(row, len2)
+  var e = iStart + len2 - 1 # end marker
+  # initialize first row:
+  for i in 1 .. (len2 - half - 1): row[i] = i
+  row[0] = len1 - half - 1
+  iCurrentA = iStart
+  var
+    char2pI = -1
+    char2pPrev: int
+  for i in 1 .. (len1 - 1):
+    iNextA = iCurrentA
+    a.fastRuneAt(iNextA, runeA)
+    var
+      char2p: int
+      diff, x: int
+      p: int
+    if i >= (len1 - half):
+      # skip the upper triangle:
+      let offset = i + half - len1
+      if char2pI == i:
+        b.fastRuneAt(char2pPrev, runeB)
+        char2p = char2pPrev
+        char2pI = i + 1
+      else:
+        char2p = iStart
+        for j in 0 ..< offset:
+          runeB = b.runeAt(char2p)
+          inc(char2p, runeB.size)
+        char2pI = i + 1
+        char2pPrev = char2p
+      p = offset
+      runeB = b.runeAt(char2p)
+      var c3 = row[p] + (if runeA != runeB: 1 else: 0)
+      inc(char2p, runeB.size)
+      inc(p)
+      x = row[p] + 1
+      diff = x
+      if x > c3: x = c3
+      row[p] = x
+      inc(p)
+    else:
+      p = 1
+      char2p = iStart
+      diff = i
+      x = i
+    if i <= (half + 1):
+      # skip the lower triangle:
+      e = len2 + i - half - 2
+    # main:
+    while p <= e:
+      dec(diff)
+      runeB = b.runeAt(char2p)
+      var c3 = diff + (if runeA != runeB: 1 else: 0)
+      inc(char2p, runeB.size)
+      inc(x)
+      if x > c3: x = c3
+      diff = row[p] + 1
+      if x > diff: x = diff
+      row[p] = x
+      inc(p)
+    # lower triangle sentinel:
+    if i <= half:
+      dec(diff)
+      runeB = b.runeAt(char2p)
+      var c3 = diff + (if runeA != runeB: 1 else: 0)
+      inc(x)
+      if x > c3: x = c3
+      row[p] = x
+    iCurrentA = iNextA
+  result = row[e]
+
+proc editDistanceAscii*(a, b: string): int {.noSideEffect.} =
+  ## Returns the edit distance between `a` and `b`.
+  ##
+  ## This uses the `Levenshtein`:idx: distance algorithm with only a linear
+  ## memory overhead.
+  runnableExamples: static: doAssert editDistanceAscii("Kitten", "Bitten") == 1
+  var len1 = a.len
+  var len2 = b.len
+  if len1 > len2:
+    # make `b` the longer string
+    return editDistanceAscii(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
+  # initialize 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 diff, 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
+      diff = x
+      if x > c3: x = c3
+      row[p] = x
+      inc(p)
+    else:
+      p = 1
+      char2p = 0
+      diff = i
+      x = i
+    if i <= half + 1:
+      # skip the lower triangle:
+      e = len2 + i - half - 2
+    # main:
+    while p <= e:
+      dec(diff)
+      var c3 = diff + ord(char1 != b[char2p + s])
+      inc(char2p)
+      inc(x)
+      if x > c3: x = c3
+      diff = row[p] + 1
+      if x > diff: x = diff
+      row[p] = x
+      inc(p)
+    # lower triangle sentinel:
+    if i <= half:
+      dec(diff)
+      var c3 = diff + ord(char1 != b[char2p + s])
+      inc(x)
+      if x > c3: x = c3
+      row[p] = x
+  result = row[e]
diff --git a/lib/std/effecttraits.nim b/lib/std/effecttraits.nim
new file mode 100644
index 000000000..3d1b4ffd3
--- /dev/null
+++ b/lib/std/effecttraits.nim
@@ -0,0 +1,63 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2020 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module provides access to the inferred .raises effects
+## for Nim's macro system.
+## **Since**: Version 1.4.
+##
+## One can test for the existence of this standard module
+## via `defined(nimHasEffectTraitsModule)`.
+
+import std/macros
+
+proc getRaisesListImpl(n: NimNode): NimNode = discard "see compiler/vmops.nim"
+proc getTagsListImpl(n: NimNode): NimNode = discard "see compiler/vmops.nim"
+proc getForbidsListImpl(n: NimNode): NimNode = discard "see compiler/vmops.nim"
+proc isGcSafeImpl(n: NimNode): bool = discard "see compiler/vmops.nim"
+proc hasNoSideEffectsImpl(n: NimNode): bool = discard "see compiler/vmops.nim"
+
+proc getRaisesList*(fn: NimNode): NimNode =
+  ## Extracts the `.raises` list of the func/proc/etc `fn`.
+  ## `fn` has to be a resolved symbol of kind `nnkSym`. This
+  ## implies that the macro that calls this proc should accept `typed`
+  ## arguments and not `untyped` arguments.
+  expectKind fn, nnkSym
+  result = getRaisesListImpl(fn)
+
+proc getTagsList*(fn: NimNode): NimNode =
+  ## Extracts the `.tags` list of the func/proc/etc `fn`.
+  ## `fn` has to be a resolved symbol of kind `nnkSym`. This
+  ## implies that the macro that calls this proc should accept `typed`
+  ## arguments and not `untyped` arguments.
+  expectKind fn, nnkSym
+  result = getTagsListImpl(fn)
+
+proc getForbidsList*(fn: NimNode): NimNode =
+  ## Extracts the `.forbids` list of the func/proc/etc `fn`.
+  ## `fn` has to be a resolved symbol of kind `nnkSym`. This
+  ## implies that the macro that calls this proc should accept `typed`
+  ## arguments and not `untyped` arguments.
+  expectKind fn, nnkSym
+  result = getForbidsListImpl(fn)
+
+proc isGcSafe*(fn: NimNode): bool =
+  ## Return true if the func/proc/etc `fn` is `gcsafe`.
+  ## `fn` has to be a resolved symbol of kind `nnkSym`. This
+  ## implies that the macro that calls this proc should accept `typed`
+  ## arguments and not `untyped` arguments.
+  expectKind fn, nnkSym
+  result = isGcSafeImpl(fn)
+
+proc hasNoSideEffects*(fn: NimNode): bool =
+  ## Return true if the func/proc/etc `fn` has `noSideEffect`.
+  ## `fn` has to be a resolved symbol of kind `nnkSym`. This
+  ## implies that the macro that calls this proc should accept `typed`
+  ## arguments and not `untyped` arguments.
+  expectKind fn, nnkSym
+  result = hasNoSideEffectsImpl(fn)
diff --git a/lib/std/enumerate.nim b/lib/std/enumerate.nim
new file mode 100644
index 000000000..beb65ed30
--- /dev/null
+++ b/lib/std/enumerate.nim
@@ -0,0 +1,70 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2020 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements `enumerate` syntactic sugar based on Nim's
+## macro system.
+
+import std/private/since
+import std/macros
+
+
+macro enumerate*(x: ForLoopStmt): untyped {.since: (1, 3).} =
+  ## Enumerating iterator for collections.
+  ##
+  ## It yields `(count, value)` tuples (which must be immediately unpacked).
+  ## The default starting count `0` can be manually overridden if needed.
+  runnableExamples:
+    let a = [10, 20, 30]
+    var b: seq[(int, int)] = @[]
+    for i, x in enumerate(a):
+      b.add((i, x))
+    assert b == @[(0, 10), (1, 20), (2, 30)]
+
+    let c = "abcd"
+    var d: seq[(int, char)]
+    for (i, x) in enumerate(97, c):
+      d.add((i, x))
+    assert d == @[(97, 'a'), (98, 'b'), (99, 'c'), (100, 'd')]
+
+  template genCounter(x): untyped =
+    # We strip off the first for loop variable and use it as an integer counter.
+    # We must immediately decrement it by one, because it gets incremented before
+    # the loop body - to be able to use the final expression in other macros.
+    newVarStmt(x, infix(countStart, "-", newLit(1)))
+
+  template genInc(x): untyped =
+    newCall(bindSym"inc", x)
+
+  expectKind x, nnkForStmt
+  # check if the starting count is specified:
+  var countStart = if x[^2].len == 2: newLit(0) else: x[^2][1]
+  result = newStmtList()
+  var body = x[^1]
+  if body.kind != nnkStmtList:
+    body = newTree(nnkStmtList, body)
+  var newFor = newTree(nnkForStmt)
+  if x.len == 3: # single iteration variable
+    if x[0].kind == nnkVarTuple: # for (x, y, ...) in iter
+      result.add genCounter(x[0][0])
+      body.insert(0, genInc(x[0][0]))
+      for i in 1 .. x[0].len-2:
+        newFor.add x[0][i]
+    else:
+      error("Missing second for loop variable") # for x in iter
+  else: # for x, y, ... in iter
+    result.add genCounter(x[0])
+    body.insert(0, genInc(x[0]))
+    for i in 1 .. x.len-3:
+      newFor.add x[i]
+  # transform enumerate(X) to 'X'
+  newFor.add x[^2][^1]
+  newFor.add body
+  result.add newFor
+  # now wrap the whole macro in a block to create a new scope
+  result = newBlockStmt(result)
diff --git a/lib/std/enumutils.nim b/lib/std/enumutils.nim
new file mode 100644
index 000000000..9c338817d
--- /dev/null
+++ b/lib/std/enumutils.nim
@@ -0,0 +1,202 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2020 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+import std/macros
+from std/typetraits import OrdinalEnum, HoleyEnum
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
+# xxx `genEnumCaseStmt` needs tests and runnableExamples
+
+macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed,
+            userMin, userMax: static[int], normalizer: static[proc(s :string): string]): untyped =
+  # Generates a case stmt, which assigns the correct enum field given
+  # a normalized string comparison to the `argSym` input.
+  # string normalization is done using passed normalizer.
+  let typ = typ.getTypeInst[1]
+  let typSym = typ.getTypeImpl.getTypeInst # skip aliases etc to get type sym
+  let impl = typSym.getImpl[2]
+  expectKind impl, nnkEnumTy
+  let normalizerNode = quote: `normalizer`
+  expectKind normalizerNode, nnkSym
+  result = nnkCaseStmt.newTree(newCall(normalizerNode, argSym))
+  # stores all processed field strings to give error msg for ambiguous enums
+  var foundFields: seq[string] = @[]
+  var fVal = ""
+  var fStr = "" # string of current field
+  var fNum = BiggestInt(0) # int value of current field
+  for f in impl:
+    case f.kind
+    of nnkEmpty: continue # skip first node of `enumTy`
+    of nnkSym, nnkIdent:
+      fVal = f.strVal
+      fStr = fVal
+    of nnkAccQuoted:
+      fVal = ""
+      for ch in f:
+        fVal.add ch.strVal
+      fStr = fVal
+    of nnkEnumFieldDef:
+      fVal = f[0].strVal
+      case f[1].kind
+      of nnkStrLit:
+        fStr = f[1].strVal
+      of nnkTupleConstr:
+        fStr = f[1][1].strVal
+        fNum = f[1][0].intVal
+      of nnkIntLit:
+        fStr = f[0].strVal
+        fNum = f[1].intVal
+      else:
+        let fAst = f[0].getImpl
+        if fAst.kind == nnkStrLit:
+          fStr = fAst.strVal
+        else:
+          error("Invalid tuple syntax!", f[1])
+    else: error("Invalid node for enum type `" & $f.kind & "`!", f)
+    # add field if string not already added
+    if fNum >= userMin and fNum <= userMax:
+      fStr = normalizer(fStr)
+      if fStr notin foundFields:
+        result.add nnkOfBranch.newTree(newLit fStr, newDotExpr(typ, ident fVal))
+        foundFields.add fStr
+      else:
+        error("Ambiguous enums cannot be parsed, field " & $fStr &
+          " appears multiple times!", f)
+    inc fNum
+  # finally add else branch to raise or use default
+  if default == nil:
+    let raiseStmt = quote do:
+      raise newException(ValueError, "Invalid enum value: " & $`argSym`)
+    result.add nnkElse.newTree(raiseStmt)
+  else:
+    expectKind(default, nnkSym)
+    result.add nnkElse.newTree(default)
+
+macro enumFullRange(a: typed): untyped =
+  newNimNode(nnkBracket).add(a.getType[1][1..^1])
+
+macro enumNames(a: typed): untyped =
+  # this could be exported too; in particular this could be useful for enum with holes.
+  result = newNimNode(nnkBracket)
+  for ai in a.getType[1][1..^1]:
+    assert ai.kind == nnkSym
+    result.add newLit ai.strVal
+
+iterator items*[T: HoleyEnum](E: typedesc[T]): T =
+  ## Iterates over an enum with holes.
+  runnableExamples:
+    type
+      A = enum
+        a0 = 2
+        a1 = 4
+        a2
+      B[T] = enum
+        b0 = 2
+        b1 = 4
+    from std/sequtils import toSeq
+    assert A.toSeq == [a0, a1, a2]
+    assert B[float].toSeq == [B[float].b0, B[float].b1]
+  for a in enumFullRange(E): yield a
+
+func span(T: typedesc[HoleyEnum]): int =
+  (T.high.ord - T.low.ord) + 1
+
+const invalidSlot = uint8.high
+
+proc genLookup[T: typedesc[HoleyEnum]](_: T): auto =
+  const n = span(T)
+  var i = 0
+  assert n <= invalidSlot.int
+  var ret {.noinit.}: array[n, uint8]
+  for ai in mitems(ret): ai = invalidSlot
+  for ai in items(T):
+    ret[ai.ord - T.low.ord] = uint8(i)
+    inc(i)
+  return ret
+
+func symbolRankImpl[T](a: T): int {.inline.} =
+  const n = T.span
+  const thres = 255 # must be <= `invalidSlot`, but this should be tuned.
+  when n <= thres:
+    const lookup = genLookup(T)
+    let lookup2 {.global.} = lookup # xxx improve pending https://github.com/timotheecour/Nim/issues/553
+    #[
+    This could be optimized using a hash adapted to `T` (possible since it's known at CT)
+    to get better key distribution before indexing into the lookup table table.
+    ]#
+    {.noSideEffect.}: # because it's immutable
+      let ret = lookup2[ord(a) - T.low.ord]
+    if ret != invalidSlot: return ret.int
+  else:
+    var i = 0
+    # we could also generate a case statement as optimization
+    for ai in items(T):
+      if ai == a: return i
+      inc(i)
+  raise newException(IndexDefect, $ord(a) & " invalid for " & $T)
+
+template symbolRank*[T: enum](a: T): int =
+  ## Returns the index in which `a` is listed in `T`.
+  ##
+  ## The cost for a `HoleyEnum` is implementation defined, currently optimized
+  ## for small enums, otherwise is `O(T.enumLen)`.
+  runnableExamples:
+    type
+      A = enum # HoleyEnum
+        a0 = -3
+        a1 = 10
+        a2
+        a3 = (20, "f3Alt")
+      B = enum # OrdinalEnum
+        b0
+        b1
+        b2
+      C = enum # OrdinalEnum
+        c0 = 10
+        c1
+        c2
+    assert a2.symbolRank == 2
+    assert b2.symbolRank == 2
+    assert c2.symbolRank == 2
+    assert c2.ord == 12
+    assert a2.ord == 11
+    var invalid = 7.A
+    doAssertRaises(IndexDefect): discard invalid.symbolRank
+  when T is Ordinal: ord(a) - T.low.ord.static
+  else: symbolRankImpl(a)
+
+proc rangeBase(T: typedesc): typedesc {.magic: "TypeTrait".}
+  # skip one level of range; return the base type of a range type
+
+func symbolName*[T: enum](a: T): string =
+  ## Returns the symbol name of an enum.
+  ##
+  ## This uses `symbolRank`.
+  runnableExamples:
+    type B = enum
+      b0 = (10, "kb0")
+      b1 = "kb1"
+      b2
+    let b = B.low
+    assert b.symbolName == "b0"
+    assert $b == "kb0"
+    static: assert B.high.symbolName == "b2"
+    type C = enum # HoleyEnum
+      c0 = -3
+      c1 = 4
+      c2 = 20
+    assert c1.symbolName == "c1"
+  when T is range:
+    const names = enumNames(rangeBase T)
+  else:
+    const names = enumNames(T)
+  names[a.symbolRank]
diff --git a/lib/std/envvars.nim b/lib/std/envvars.nim
new file mode 100644
index 000000000..a955077ea
--- /dev/null
+++ b/lib/std/envvars.nim
@@ -0,0 +1,221 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2022 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+
+## The `std/envvars` module implements environment variable handling.
+import std/oserrors
+
+type
+  ReadEnvEffect* = object of ReadIOEffect   ## Effect that denotes a read
+                                            ## from an environment variable.
+  WriteEnvEffect* = object of WriteIOEffect ## Effect that denotes a write
+                                            ## to an environment variable.
+
+
+when not defined(nimscript):
+  when defined(nodejs):
+    proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} =
+      var ret = default.cstring
+      let key2 = key.cstring
+      {.emit: "const value = process.env[`key2`];".}
+      {.emit: "if (value !== undefined) { `ret` = value };".}
+      result = $ret
+
+    proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
+      var key2 = key.cstring
+      var ret: bool
+      {.emit: "`ret` = `key2` in process.env;".}
+      result = ret
+
+    proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
+      var key2 = key.cstring
+      var val2 = val.cstring
+      {.emit: "process.env[`key2`] = `val2`;".}
+
+    proc delEnv*(key: string) {.tags: [WriteEnvEffect].} =
+      var key2 = key.cstring
+      {.emit: "delete process.env[`key2`];".}
+
+    iterator envPairsImpl(): tuple[key, value: string] {.tags: [ReadEnvEffect].} =
+      var num: int
+      var keys: RootObj
+      {.emit: "`keys` = Object.keys(process.env); `num` = `keys`.length;".}
+      for i in 0..<num:
+        var key, value: cstring
+        {.emit: "`key` = `keys`[`i`]; `value` = process.env[`key`];".}
+        yield ($key, $value)
+
+  # commented because it must keep working with js+VM
+  # elif defined(js):
+  #   {.error: "requires -d:nodejs".}
+
+  else:
+
+    when defined(windows):
+      proc c_putenv(envstring: cstring): cint {.importc: "_putenv", header: "<stdlib.h>".}
+      from std/private/win_setenv import setEnvImpl
+      import std/winlean
+      when defined(nimPreviewSlimSystem):
+        import std/widestrs
+
+      type wchar_t {.importc: "wchar_t", header: "<stdlib.h>".} = int16
+      proc c_wgetenv(varname: ptr wchar_t): ptr wchar_t {.importc: "_wgetenv",
+          header: "<stdlib.h>".}
+      proc getEnvImpl(env: cstring): WideCString =
+        let r: WideCString = env.newWideCString
+        cast[WideCString](c_wgetenv(cast[ptr wchar_t](r)))
+    else:
+      proc c_getenv(env: cstring): cstring {.
+        importc: "getenv", header: "<stdlib.h>".}
+      proc c_setenv(envname: cstring, envval: cstring, overwrite: cint): cint {.importc: "setenv", header: "<stdlib.h>".}
+      proc c_unsetenv(env: cstring): cint {.importc: "unsetenv", header: "<stdlib.h>".}
+      proc getEnvImpl(env: cstring): cstring = c_getenv(env)
+
+    proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} =
+      ## Returns the value of the `environment variable`:idx: named `key`.
+      ##
+      ## If the variable does not exist, `""` is returned. To distinguish
+      ## whether a variable exists or it's value is just `""`, call
+      ## `existsEnv(key) proc`_.
+      ##
+      ## See also:
+      ## * `existsEnv proc`_
+      ## * `putEnv proc`_
+      ## * `delEnv proc`_
+      ## * `envPairs iterator`_
+      runnableExamples:
+        assert getEnv("unknownEnv") == ""
+        assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist"
+
+      let env = getEnvImpl(key)
+      if env == nil:
+        result = default
+      else:
+        result = $env
+
+    proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
+      ## Checks whether the environment variable named `key` exists.
+      ## Returns true if it exists, false otherwise.
+      ##
+      ## See also:
+      ## * `getEnv proc`_
+      ## * `putEnv proc`_
+      ## * `delEnv proc`_
+      ## * `envPairs iterator`_
+      runnableExamples:
+        assert not existsEnv("unknownEnv")
+
+      result = getEnvImpl(key) != nil
+
+    proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
+      ## Sets the value of the `environment variable`:idx: named `key` to `val`.
+      ## If an error occurs, `OSError` is raised.
+      ##
+      ## See also:
+      ## * `getEnv proc`_
+      ## * `existsEnv proc`_
+      ## * `delEnv proc`_
+      ## * `envPairs iterator`_
+      when defined(windows):
+        if key.len == 0 or '=' in key:
+          raise newException(OSError, "invalid key, got: " & $(key, val))
+        if setEnvImpl(key, val, 1'i32) != 0'i32:
+          raiseOSError(osLastError(), $(key, val))
+      else:
+        if c_setenv(key, val, 1'i32) != 0'i32:
+          raiseOSError(osLastError(), $(key, val))
+
+    proc delEnv*(key: string) {.tags: [WriteEnvEffect].} =
+      ## Deletes the `environment variable`:idx: named `key`.
+      ## If an error occurs, `OSError` is raised.
+      ##
+      ## See also:ven
+      ## * `getEnv proc`_
+      ## * `existsEnv proc`_
+      ## * `putEnv proc`_
+      ## * `envPairs iterator`_
+      template bail = raiseOSError(osLastError(), key)
+      when defined(windows):
+        #[
+        # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/putenv-s-wputenv-s?view=msvc-160
+        > You can remove a variable from the environment by specifying an empty string (that is, "") for value_string
+        note that nil is not legal
+        ]#
+        if key.len == 0 or '=' in key:
+          raise newException(OSError, "invalid key, got: " & key)
+        let envToDel = key & "="
+        if c_putenv(cstring envToDel) != 0'i32: bail
+      else:
+        if c_unsetenv(key) != 0'i32: bail
+
+    when defined(windows):
+      when defined(cpp):
+        proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.importcpp: "(NI16*)wcschr((const wchar_t *)#, #)",
+            header: "<string.h>".}
+      else:
+        proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.importc: "wcschr",
+            header: "<string.h>".}
+    elif defined(macosx) and not defined(ios) and not defined(emscripten):
+      # From the manual:
+      # Shared libraries and bundles don't have direct access to environ,
+      # which is only available to the loader ld(1) when a complete program
+      # is being linked.
+      # The environment routines can still be used, but if direct access to
+      # environ is needed, the _NSGetEnviron() routine, defined in
+      # <crt_externs.h>, can be used to retrieve the address of environ
+      # at runtime.
+      proc NSGetEnviron(): ptr cstringArray {.importc: "_NSGetEnviron",
+          header: "<crt_externs.h>".}
+    elif defined(haiku):
+      var gEnv {.importc: "environ", header: "<stdlib.h>".}: cstringArray
+    else:
+      var gEnv {.importc: "environ".}: cstringArray
+
+    iterator envPairsImpl(): tuple[key, value: string] {.tags: [ReadEnvEffect].} =
+      when defined(windows):
+        let env = getEnvironmentStringsW()
+        var e = env
+        if e != nil:
+          while true:
+            let eend = strEnd(e)
+            let kv = $e
+            let p = find(kv, '=')
+            yield (substr(kv, 0, p-1), substr(kv, p+1))
+            e = cast[WideCString](cast[ByteAddress](eend)+2)
+            if int(eend[1]) == 0: break
+          discard freeEnvironmentStringsW(env)
+      else:
+        var i = 0
+        when defined(macosx) and not defined(ios) and not defined(emscripten):
+          var gEnv = NSGetEnviron()[]
+        while gEnv[i] != nil:
+          let kv = $gEnv[i]
+          inc(i)
+          let p = find(kv, '=')
+          yield (substr(kv, 0, p-1), substr(kv, p+1))
+
+proc envPairsImplSeq(): seq[tuple[key, value: string]] = discard # vmops
+
+iterator envPairs*(): tuple[key, value: string] {.tags: [ReadEnvEffect].} =
+  ## Iterate over all `environments variables`:idx:.
+  ##
+  ## In the first component of the tuple is the name of the current variable stored,
+  ## in the second its value.
+  ##
+  ## Works in native backends, nodejs and vm, like the following APIs:
+  ## * `getEnv proc`_
+  ## * `existsEnv proc`_
+  ## * `putEnv proc`_
+  ## * `delEnv proc`_
+  when nimvm:
+    for ai in envPairsImplSeq(): yield ai
+  else:
+    when defined(nimscript): discard
+    else:
+      for ai in envPairsImpl(): yield ai
diff --git a/lib/std/exitprocs.nim b/lib/std/exitprocs.nim
new file mode 100644
index 000000000..f26368f42
--- /dev/null
+++ b/lib/std/exitprocs.nim
@@ -0,0 +1,87 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module allows adding hooks to program exit.
+
+import std/locks
+when defined(js) and not defined(nodejs):
+  import std/assertions
+
+type
+  FunKind = enum kClosure, kNoconv # extend as needed
+  Fun = object
+    case kind: FunKind
+    of kClosure: fun1: proc () {.closure.}
+    of kNoconv: fun2: proc () {.noconv.}
+
+var
+  gFunsLock: Lock
+  gFuns {.cursor.}: seq[Fun] #Intentionally use the cursor to break up the lifetime trace and make it compatible with JS.
+
+initLock(gFunsLock)
+
+when defined(js):
+  proc addAtExit(quitProc: proc() {.noconv.}) =
+    when defined(nodejs):
+      {.emit: """
+        process.on('exit', `quitProc`);
+      """.}
+    elif defined(js):
+      {.emit: """
+        window.onbeforeunload = `quitProc`;
+      """.}
+else:
+  proc addAtExit(quitProc: proc() {.noconv.}) {.
+    importc: "atexit", header: "<stdlib.h>".}
+
+proc callClosures() {.noconv.} =
+  withLock gFunsLock:
+    for i in countdown(gFuns.len-1, 0):
+      let fun = gFuns[i]
+      case fun.kind
+      of kClosure: fun.fun1()
+      of kNoconv: fun.fun2()
+    gFuns.setLen(0)
+
+template fun() =
+  if gFuns.len == 0:
+    addAtExit(callClosures)
+
+proc addExitProc*(cl: proc () {.closure.}) =
+  ## Adds/registers a quit procedure. Each call to `addExitProc` registers
+  ## another quit procedure. They are executed on a last-in, first-out basis.
+  # Support for `addExitProc` is done by Ansi C's facilities here.
+  # In case of an unhandled exception the exit handlers should
+  # not be called explicitly! The user may decide to do this manually though.
+  withLock gFunsLock:
+    fun()
+    gFuns.add Fun(kind: kClosure, fun1: cl)
+
+proc addExitProc*(cl: proc() {.noconv.}) =
+  ## overload for `noconv` procs.
+  withLock gFunsLock:
+    fun()
+    gFuns.add Fun(kind: kNoconv, fun2: cl)
+
+when not defined(nimscript) and (not defined(js) or defined(nodejs)):
+  proc getProgramResult*(): int =
+    when defined(js) and defined(nodejs):
+      {.emit: """
+`result` = process.exitCode;
+""".}
+    else:
+      result = programResult
+
+  proc setProgramResult*(a: int) =
+    when defined(js) and defined(nodejs):
+      {.emit: """
+process.exitCode = `a`;
+""".}
+    else:
+      programResult = a
diff --git a/lib/std/files.nim b/lib/std/files.nim
new file mode 100644
index 000000000..c4e0491c9
--- /dev/null
+++ b/lib/std/files.nim
@@ -0,0 +1,46 @@
+## This module implements file handling.
+##
+## **See also:**
+## * `paths module <paths.html>`_ for path manipulation
+
+from std/paths import Path, ReadDirEffect, WriteDirEffect
+
+from std/private/osfiles import fileExists, removeFile,
+                                moveFile
+
+
+proc fileExists*(filename: Path): bool {.inline, tags: [ReadDirEffect], sideEffect.} =
+  ## Returns true if `filename` exists and is a regular file or symlink.
+  ##
+  ## Directories, device files, named pipes and sockets return false.
+  result = fileExists(filename.string)
+
+proc removeFile*(file: Path) {.inline, tags: [WriteDirEffect].} =
+  ## Removes the `file`.
+  ##
+  ## If this fails, `OSError` is raised. This does not fail
+  ## if the file never existed in the first place.
+  ##
+  ## On Windows, ignores the read-only attribute.
+  ##
+  ## See also:
+  ## * `removeDir proc <dirs.html#removeDir>`_
+  ## * `moveFile proc`_
+  removeFile(file.string)
+
+proc moveFile*(source, dest: Path) {.inline,
+    tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect].} =
+  ## Moves a file from `source` to `dest`.
+  ##
+  ## Symlinks are not followed: if `source` is a symlink, it is itself moved,
+  ## not its target.
+  ##
+  ## If this fails, `OSError` is raised.
+  ## If `dest` already exists, it will be overwritten.
+  ##
+  ## Can be used to `rename files`:idx:.
+  ##
+  ## See also:
+  ## * `moveDir proc <dirs.html#moveDir>`_
+  ## * `removeFile proc`_
+  moveFile(source.string, dest.string)
diff --git a/lib/std/formatfloat.nim b/lib/std/formatfloat.nim
new file mode 100644
index 000000000..9258245f6
--- /dev/null
+++ b/lib/std/formatfloat.nim
@@ -0,0 +1,143 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2022 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements formatting floats as strings.
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+proc c_memcpy(a, b: pointer, size: csize_t): pointer {.importc: "memcpy", header: "<string.h>", discardable.}
+
+proc addCstringN(result: var string, buf: cstring; buflen: int) =
+  # no nimvm support needed, so it doesn't need to be fast here either
+  let oldLen = result.len
+  let newLen = oldLen + buflen
+  result.setLen newLen
+  c_memcpy(result[oldLen].addr, buf, buflen.csize_t)
+
+import std/private/[dragonbox, schubfach]
+
+proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: BiggestFloat): int =
+  ## This is the implementation to format floats.
+  ##
+  ## returns the amount of bytes written to `buf` not counting the
+  ## terminating '\0' character.
+  result = toChars(buf, value, forceTrailingDotZero=true).int
+  buf[result] = '\0'
+
+proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: float32): int =
+  result = float32ToChars(buf, value, forceTrailingDotZero=true).int
+  buf[result] = '\0'
+
+proc c_snprintf(buf: cstring, n: csize_t, frmt: cstring): cint {.header: "<stdio.h>",
+                                    importc: "snprintf", varargs, noSideEffect.}
+
+proc writeToBuffer(buf: var array[65, char]; value: cstring) =
+  var i = 0
+  while value[i] != '\0':
+    buf[i] = value[i]
+    inc i
+
+proc writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat): int =
+  ## This is the implementation to format floats.
+  ##
+  ## returns the amount of bytes written to `buf` not counting the
+  ## terminating '\0' character.
+  var n = c_snprintf(cast[cstring](addr buf), 65, "%.16g", value).int
+  var hasDot = false
+  for i in 0..n-1:
+    if buf[i] == ',':
+      buf[i] = '.'
+      hasDot = true
+    elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
+      hasDot = true
+  if not hasDot:
+    buf[n] = '.'
+    buf[n+1] = '0'
+    buf[n+2] = '\0'
+    result = n + 2
+  else:
+    result = n
+  # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)'
+  # of '-1.#IND' are produced.
+  # We want to get rid of these here:
+  if buf[n-1] in {'n', 'N', 'D', 'd', ')'}:
+    writeToBuffer(buf, "nan")
+    result = 3
+  elif buf[n-1] == 'F':
+    if buf[0] == '-':
+      writeToBuffer(buf, "-inf")
+      result = 4
+    else:
+      writeToBuffer(buf, "inf")
+      result = 3
+
+proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat | float32): int {.inline.} =
+  when defined(nimPreviewFloatRoundtrip) or defined(nimPreviewSlimSystem):
+    writeFloatToBufferRoundtrip(buf, value)
+  else:
+    writeFloatToBufferSprintf(buf, value)
+
+proc addFloatRoundtrip*(result: var string; x: float | float32) =
+  when nimvm:
+    raiseAssert "unreachable"
+  else:
+    var buffer {.noinit.}: array[65, char]
+    let n = writeFloatToBufferRoundtrip(buffer, x)
+    result.addCstringN(cast[cstring](buffer[0].addr), n)
+
+proc addFloatSprintf*(result: var string; x: float) =
+  when nimvm:
+    raiseAssert "unreachable"
+  else:
+    var buffer {.noinit.}: array[65, char]
+    let n = writeFloatToBufferSprintf(buffer, x)
+    result.addCstringN(cast[cstring](buffer[0].addr), n)
+
+when defined(js):
+  proc nimFloatToString(a: float): cstring =
+    ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2
+    # print `-0.0` properly
+    {.emit: """
+      function nimOnlyDigitsOrMinus(n) {
+        return n.toString().match(/^-?\d+$/);
+      }
+      if (Number.isSafeInteger(`a`))
+        `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0";
+      else {
+        `result` = `a`+"";
+        if(nimOnlyDigitsOrMinus(`result`)){
+          `result` = `a`+".0";
+        }
+      }
+    """.}
+
+proc addFloat*(result: var string; x: float | float32) {.inline.} =
+  ## Converts float to its string representation and appends it to `result`.
+  runnableExamples:
+    var
+      s = "foo:"
+      b = 45.67
+    s.addFloat(45.67)
+    assert s == "foo:45.67"
+  template impl =
+    when defined(nimPreviewFloatRoundtrip) or defined(nimPreviewSlimSystem):
+      addFloatRoundtrip(result, x)
+    else:
+      addFloatSprintf(result, x)
+  when defined(js):
+    when nimvm: impl()
+    else:
+      result.add nimFloatToString(x)
+  else: impl()
+
+when defined(nimPreviewSlimSystem):
+  func `$`*(x: float | float32): string =
+    ## Outplace version of `addFloat`.
+    result.addFloat(x)
diff --git a/lib/std/genasts.nim b/lib/std/genasts.nim
new file mode 100644
index 000000000..d0f07c527
--- /dev/null
+++ b/lib/std/genasts.nim
@@ -0,0 +1,89 @@
+## This module implements AST generation using captured variables for macros.
+
+import std/macros
+
+type GenAstOpt* = enum
+  kDirtyTemplate,
+    # When set, uses a dirty template in implementation of `genAst`. This
+    # is occasionally useful as workaround for issues such as #8220, see
+    # `strformat limitations <strformat.html#limitations>`_ for details.
+    # Default is unset, to avoid hijacking of uncaptured local symbols by
+    # symbols in caller scope.
+  kNoNewLit,
+    # don't call call newLit automatically in `genAst` capture parameters
+
+macro genAstOpt*(options: static set[GenAstOpt], args: varargs[untyped]): untyped =
+  ## Accepts a list of captured variables `a=b` or `a` and a block and returns the
+  ## AST that represents it. Local `{.inject.}` symbols (e.g. procs) are captured
+  ## unless `kDirtyTemplate in options`.
+  runnableExamples:
+    # This example shows how one could write a simplified version of `unittest.check`.
+    import std/[macros, strutils]
+    macro check2(cond: bool): untyped =
+      assert cond.kind == nnkInfix, "$# not implemented" % $cond.kind
+      result = genAst(cond, s = repr(cond), lhs = cond[1], rhs = cond[2]):
+        # each local symbol we access must be explicitly captured
+        if not cond:
+          raiseAssert "'$#'' failed: lhs: '$#', rhs: '$#'" % [s, $lhs, $rhs]
+    let a = 3
+    check2 a*2 == a+3
+    if false: check2 a*2 < a+1 # would error with: 'a * 2 < a + 1'' failed: lhs: '6', rhs: '4'
+
+  runnableExamples:
+    # This example goes in more details about the capture semantics.
+    macro fun(a: string, b: static bool): untyped =
+      let c = 'z'
+      var d = 11 # implicitly {.gensym.} and needs to be captured for use in `genAst`.
+      proc localFun(): auto = 12 # implicitly {.inject.}, doesn't need to be captured.
+      genAst(a, b, c = true):
+        # `a`, `b` are captured explicitly, `c` is a local definition masking `c = 'z'`.
+        const b2 = b # macro static param `b` is forwarded here as a static param.
+        # `echo d` would give: `var not init` because `d` is not captured.
+        (a & a, b, c, localFun()) # localFun can be called without capture.
+    assert fun("ab", false) == ("abab", false, true, 12)
+
+  let params = newTree(nnkFormalParams, newEmptyNode())
+  let pragmas =
+    if kDirtyTemplate in options:
+      nnkPragma.newTree(ident"dirty")
+    else:
+      newEmptyNode()
+
+  template newLitMaybe(a): untyped =
+    when (a is type) or (typeof(a) is (proc | iterator | func | NimNode)):
+      a # `proc` actually also covers template, macro
+    else: newLit(a)
+
+  # using `_` as workaround, see https://github.com/nim-lang/Nim/issues/2465#issuecomment-511076669
+  let name = genSym(nskTemplate, "_fun")
+  let call = newCall(name)
+  for a in args[0..^2]:
+    var varName: NimNode
+    var varVal: NimNode
+    case a.kind
+    of nnkExprEqExpr:
+      varName = a[0]
+      varVal = a[1]
+    of nnkIdent:
+      varName = a
+      varVal = a
+    else: error("invalid argument kind: " & $a.kind, a)
+    if kNoNewLit notin options: varVal = newCall(bindSym"newLitMaybe", varVal)
+
+    params.add newTree(nnkIdentDefs, varName, newEmptyNode(), newEmptyNode())
+    call.add varVal
+
+  result = newStmtList()
+  result.add nnkTemplateDef.newTree(
+      name,
+      newEmptyNode(),
+      newEmptyNode(),
+      params,
+      pragmas,
+      newEmptyNode(),
+      args[^1])
+  result.add newCall(bindSym"getAst", call)
+
+template genAst*(args: varargs[untyped]): untyped =
+  ## Convenience wrapper around `genAstOpt`.
+  genAstOpt({}, args)
diff --git a/lib/std/importutils.nim b/lib/std/importutils.nim
new file mode 100644
index 000000000..d2da76ea8
--- /dev/null
+++ b/lib/std/importutils.nim
@@ -0,0 +1,44 @@
+##[
+Utilities related to import and symbol resolution.
+
+Experimental API, subject to change.
+]##
+
+#[
+Possible future APIs:
+* module symbols (https://github.com/nim-lang/Nim/pull/9560)
+* whichModule (subsumes canImport / moduleExists) (https://github.com/timotheecour/Nim/issues/376)
+* getCurrentPkgDir (https://github.com/nim-lang/Nim/pull/10530)
+* import from a computed string + related APIs (https://github.com/nim-lang/Nim/pull/10527)
+]#
+
+when defined(nimImportutilsExample):
+  type
+    Foo = object
+      f0: int # private
+    Goo*[T] = object
+      g0: int # private
+  proc initFoo*(): auto = Foo()
+
+proc privateAccess*(t: typedesc) {.magic: "PrivateAccess".} =
+  ## Enables access to private fields of `t` in current scope.
+  runnableExamples("-d:nimImportutilsExample"):
+    # here we're importing a module containing:
+    # type
+    #   Foo = object
+    #     f0: int # private
+    #   Goo*[T] = object
+    #     g0: int # private
+    # proc initFoo*(): auto = Foo()
+    var f = initFoo()
+    block:
+      assert not compiles(f.f0)
+      privateAccess(f.type)
+      f.f0 = 1 # accessible in this scope
+      block:
+        assert f.f0 == 1 # still in scope
+    assert not compiles(f.f0)
+
+    # this also works with generics
+    privateAccess(Goo)
+    assert Goo[float](g0: 1).g0 == 1
diff --git a/lib/std/isolation.nim b/lib/std/isolation.nim
new file mode 100644
index 000000000..b03e00651
--- /dev/null
+++ b/lib/std/isolation.nim
@@ -0,0 +1,49 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2020 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements the `Isolated[T]` type for
+## safe construction of isolated subgraphs that can be
+## passed efficiently to different channels and threads.
+##
+## .. warning:: This module is experimental and its interface may change.
+##
+
+type
+  Isolated*[T] {.sendable.} = object ## Isolated data can only be moved, not copied.
+    value: T
+
+proc `=copy`*[T](dest: var Isolated[T]; src: Isolated[T]) {.error.}
+
+proc `=sink`*[T](dest: var Isolated[T]; src: Isolated[T]) {.inline.} =
+  # delegate to value's sink operation
+  `=sink`(dest.value, src.value)
+
+proc `=destroy`*[T](dest: var Isolated[T]) {.inline.} =
+  # delegate to value's destroy operation
+  `=destroy`(dest.value)
+
+func isolate*[T](value: sink T): Isolated[T] {.magic: "Isolate".} =
+  ## Creates an isolated subgraph from the expression `value`.
+  ## Isolation is checked at compile time.
+  ##
+  ## Please read https://github.com/nim-lang/RFCs/issues/244
+  ## for more details.
+  Isolated[T](value: value)
+
+func unsafeIsolate*[T](value: sink T): Isolated[T] =
+  ## Creates an isolated subgraph from the expression `value`.
+  ##
+  ## .. warning:: The proc doesn't check whether `value` is isolated.
+  ##
+  Isolated[T](value: value)
+
+func extract*[T](src: var Isolated[T]): T =
+  ## Returns the internal value of `src`.
+  ## The value is moved from `src`.
+  result = move(src.value)
diff --git a/lib/std/jsbigints.nim b/lib/std/jsbigints.nim
new file mode 100644
index 000000000..4e996ea7b
--- /dev/null
+++ b/lib/std/jsbigints.nim
@@ -0,0 +1,228 @@
+## Arbitrary precision integers.
+## * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt
+when not defined(js):
+  {.fatal: "Module jsbigints is designed to be used with the JavaScript backend.".}
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+type JsBigIntImpl {.importjs: "bigint".} = int # https://github.com/nim-lang/Nim/pull/16606
+type JsBigInt* = distinct JsBigIntImpl         ## Arbitrary precision integer for JavaScript target.
+
+func big*(integer: SomeInteger): JsBigInt {.importjs: "BigInt(#)".} =
+  ## Constructor for `JsBigInt`.
+  runnableExamples:
+    doAssert big(1234567890) == big"1234567890"
+    doAssert 0b1111100111.big == 0o1747.big and 0o1747.big == 999.big
+  when nimvm: raiseAssert "JsBigInt can not be used at compile-time nor static context" else: discard
+
+func `'big`*(num: cstring): JsBigInt {.importjs: "BigInt(#)".} =
+  ## Constructor for `JsBigInt`.
+  runnableExamples:
+    doAssert -1'big == 1'big - 2'big
+    # supports decimal, binary, octal, hex:
+    doAssert -12'big == big"-12"
+    doAssert 12'big == 12.big
+    doAssert 0b101'big == 0b101.big
+    doAssert 0o701'big == 0o701.big
+    doAssert 0xdeadbeaf'big == 0xdeadbeaf.big
+    doAssert 0xffffffffffffffff'big == (1'big shl 64'big) - 1'big
+    doAssert not compiles(static(12'big))
+  when nimvm: raiseAssert "JsBigInt can not be used at compile-time nor static context" else: discard
+
+func big*(integer: cstring): JsBigInt {.importjs: "BigInt(#)".} =
+  ## Alias for `'big`
+  when nimvm: raiseAssert "JsBigInt can not be used at compile-time nor static context" else: discard
+
+func toCstring*(this: JsBigInt; radix: 2..36): cstring {.importjs: "#.toString(#)".} =
+  ## Converts from `JsBigInt` to `cstring` representation.
+  ## * `radix` Base to use for representing numeric values.
+  ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toString
+  runnableExamples:
+    doAssert big"2147483647".toCstring(2) == "1111111111111111111111111111111".cstring
+
+func toCstring*(this: JsBigInt): cstring {.importjs: "#.toString()".}
+  ## Converts from `JsBigInt` to `cstring` representation.
+  ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toString
+
+func `$`*(this: JsBigInt): string =
+  ## Returns a `string` representation of `JsBigInt`.
+  runnableExamples: doAssert $big"1024" == "1024n"
+  $toCstring(this) & 'n'
+
+func wrapToInt*(this: JsBigInt; bits: Natural): JsBigInt {.importjs:
+  "(() => { const i = #, b = #; return BigInt.asIntN(b, i) })()".} =
+  ## Wraps `this` to a signed `JsBigInt` of `bits` bits in `-2 ^ (bits - 1)` .. `2 ^ (bits - 1) - 1`.
+  ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asIntN
+  runnableExamples:
+    doAssert (big("3") + big("2") ** big("66")).wrapToInt(13) == big("3")
+
+func wrapToUint*(this: JsBigInt; bits: Natural): JsBigInt {.importjs:
+  "(() => { const i = #, b = #; return BigInt.asUintN(b, i) })()".} =
+  ## Wraps `this` to an unsigned `JsBigInt` of `bits` bits in 0 ..  `2 ^ bits - 1`.
+  ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asUintN
+  runnableExamples:
+    doAssert (big("3") + big("2") ** big("66")).wrapToUint(66) == big("3")
+
+func toNumber*(this: JsBigInt): int {.importjs: "Number(#)".} =
+  ## Does not do any bounds check and may or may not return an inexact representation.
+  runnableExamples:
+    doAssert toNumber(big"2147483647") == 2147483647.int
+
+func `+`*(x, y: JsBigInt): JsBigInt {.importjs: "(# $1 #)".} =
+  runnableExamples:
+    doAssert (big"9" + big"1") == big"10"
+
+func `-`*(x, y: JsBigInt): JsBigInt {.importjs: "(# $1 #)".} =
+  runnableExamples:
+    doAssert (big"9" - big"1") == big"8"
+
+func `*`*(x, y: JsBigInt): JsBigInt {.importjs: "(# $1 #)".} =
+  runnableExamples:
+    doAssert (big"42" * big"9") == big"378"
+
+func `div`*(x, y: JsBigInt): JsBigInt {.importjs: "(# / #)".} =
+  ## Same as `div` but for `JsBigInt`(uses JavaScript `BigInt() / BigInt()`).
+  runnableExamples:
+    doAssert big"13" div big"3" == big"4"
+    doAssert big"-13" div big"3" == big"-4"
+    doAssert big"13" div big"-3" == big"-4"
+    doAssert big"-13" div big"-3" == big"4"
+
+func `mod`*(x, y: JsBigInt): JsBigInt {.importjs: "(# % #)".} =
+  ## Same as `mod` but for `JsBigInt` (uses JavaScript `BigInt() % BigInt()`).
+  runnableExamples:
+    doAssert big"13" mod big"3" == big"1"
+    doAssert big"-13" mod big"3" == big"-1"
+    doAssert big"13" mod big"-3" == big"1"
+    doAssert big"-13" mod big"-3" == big"-1"
+
+func `<`*(x, y: JsBigInt): bool {.importjs: "(# $1 #)".} =
+  runnableExamples:
+    doAssert big"2" < big"9"
+
+func `<=`*(x, y: JsBigInt): bool {.importjs: "(# $1 #)".} =
+  runnableExamples:
+    doAssert big"1" <= big"5"
+
+func `==`*(x, y: JsBigInt): bool {.importjs: "(# == #)".} =
+  runnableExamples:
+    doAssert big"42" == big"42"
+
+func `**`*(x, y: JsBigInt): JsBigInt {.importjs: "((#) $1 #)".} =
+  # (#) needed due to unary minus
+  runnableExamples:
+    doAssert big"2" ** big"64" == big"18446744073709551616"
+    doAssert big"-2" ** big"3" == big"-8"
+    doAssert -big"2" ** big"2" == big"4" # parsed as: (-2n) ** 2n
+    doAssert big"0" ** big"0" == big"1" # edge case
+    var ok = false
+    try: discard big"2" ** big"-1" # raises foreign `RangeError`
+    except: ok = true
+    doAssert ok
+
+func `and`*(x, y: JsBigInt): JsBigInt {.importjs: "(# & #)".} =
+  runnableExamples:
+    doAssert (big"555" and big"2") == big"2"
+
+func `or`*(x, y: JsBigInt): JsBigInt {.importjs: "(# | #)".} =
+  runnableExamples:
+    doAssert (big"555" or big"2") == big"555"
+
+func `xor`*(x, y: JsBigInt): JsBigInt {.importjs: "(# ^ #)".} =
+  runnableExamples:
+    doAssert (big"555" xor big"2") == big"553"
+
+func `shl`*(a, b: JsBigInt): JsBigInt {.importjs: "(# << #)".} =
+  runnableExamples:
+    doAssert (big"999" shl big"2") == big"3996"
+
+func `shr`*(a, b: JsBigInt): JsBigInt {.importjs: "(# >> #)".} =
+  runnableExamples:
+    doAssert (big"999" shr big"2") == big"249"
+
+func `-`*(this: JsBigInt): JsBigInt {.importjs: "($1#)".} =
+  runnableExamples:
+    doAssert -(big"10101010101") == big"-10101010101"
+
+func inc*(this: var JsBigInt) {.importjs: "(++[#][0][0])".} =
+  runnableExamples:
+    var big1: JsBigInt = big"1"
+    inc big1
+    doAssert big1 == big"2"
+
+func dec*(this: var JsBigInt) {.importjs: "(--[#][0][0])".} =
+  runnableExamples:
+    var big1: JsBigInt = big"2"
+    dec big1
+    doAssert big1 == big"1"
+
+func inc*(this: var JsBigInt; amount: JsBigInt) {.importjs: "([#][0][0] += #)".} =
+  runnableExamples:
+    var big1: JsBigInt = big"1"
+    inc big1, big"2"
+    doAssert big1 == big"3"
+
+func dec*(this: var JsBigInt; amount: JsBigInt) {.importjs: "([#][0][0] -= #)".} =
+  runnableExamples:
+    var big1: JsBigInt = big"1"
+    dec big1, big"2"
+    doAssert big1 == big"-1"
+
+func `+=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} =
+  runnableExamples:
+    var big1: JsBigInt = big"1"
+    big1 += big"2"
+    doAssert big1 == big"3"
+
+func `-=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} =
+  runnableExamples:
+    var big1: JsBigInt = big"1"
+    big1 -= big"2"
+    doAssert big1 == big"-1"
+
+func `*=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} =
+  runnableExamples:
+    var big1: JsBigInt = big"2"
+    big1 *= big"4"
+    doAssert big1 == big"8"
+
+func `/=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} =
+  ## Same as `x = x div y`.
+  runnableExamples:
+    var big1: JsBigInt = big"11"
+    big1 /= big"2"
+    doAssert big1 == big"5"
+
+proc `+`*(_: JsBigInt): JsBigInt {.error:
+  "See https://github.com/tc39/proposal-bigint/blob/master/ADVANCED.md#dont-break-asmjs".} # Can not be used by design
+  ## **Do NOT use.** https://github.com/tc39/proposal-bigint/blob/master/ADVANCED.md#dont-break-asmjs
+
+proc low*(_: typedesc[JsBigInt]): JsBigInt {.error:
+  "Arbitrary precision integers do not have a known low.".} ## **Do NOT use.**
+
+proc high*(_: typedesc[JsBigInt]): JsBigInt {.error:
+  "Arbitrary precision integers do not have a known high.".} ## **Do NOT use.**
+
+
+runnableExamples:
+  block:
+    let big1: JsBigInt = big"2147483647"
+    let big2: JsBigInt = big"666"
+    doAssert JsBigInt isnot int
+    doAssert big1 != big2
+    doAssert big1 > big2
+    doAssert big1 >= big2
+    doAssert big2 < big1
+    doAssert big2 <= big1
+    doAssert not(big1 == big2)
+    let z = JsBigInt.default
+    doAssert $z == "0n"
+  block:
+    var a: seq[JsBigInt]
+    a.setLen 2
+    doAssert a == @[big"0", big"0"]
+    doAssert a[^1] == big"0"
+    var b: JsBigInt
+    doAssert b == big"0"
+    doAssert b == JsBigInt.default
diff --git a/lib/std/jsfetch.nim b/lib/std/jsfetch.nim
new file mode 100644
index 000000000..219594619
--- /dev/null
+++ b/lib/std/jsfetch.nim
@@ -0,0 +1,202 @@
+## - Fetch for the JavaScript target: https://developer.mozilla.org/docs/Web/API/Fetch_API
+when not defined(js):
+  {.fatal: "Module jsfetch is designed to be used with the JavaScript backend.".}
+
+import std/[asyncjs, jsformdata, jsheaders]
+export jsformdata, jsheaders
+from std/httpcore import HttpMethod
+from std/jsffi import JsObject
+
+type
+  FetchOptions* = ref object of JsRoot  ## Options for Fetch API.
+    keepalive*: bool
+    metod* {.importjs: "method".}: cstring
+    body*, integrity*, referrer*, mode*, credentials*, cache*, redirect*, referrerPolicy*: cstring
+    headers*: Headers
+
+  FetchModes* = enum  ## Mode options.
+    fmCors = "cors"
+    fmNoCors = "no-cors"
+    fmSameOrigin = "same-origin"
+
+  FetchCredentials* = enum  ## Credential options. See https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials
+    fcInclude = "include"
+    fcSameOrigin = "same-origin"
+    fcOmit = "omit"
+
+  FetchCaches* = enum  ## https://developer.mozilla.org/docs/Web/API/Request/cache
+    fchDefault = "default"
+    fchNoStore = "no-store"
+    fchReload = "reload"
+    fchNoCache = "no-cache"
+    fchForceCache = "force-cache"
+
+  FetchRedirects* = enum  ## Redirects options.
+    frFollow = "follow"
+    frError = "error"
+    frManual = "manual"
+
+  FetchReferrerPolicies* = enum  ## Referrer Policy options.
+    frpNoReferrer = "no-referrer"
+    frpNoReferrerWhenDowngrade = "no-referrer-when-downgrade"
+    frpOrigin = "origin"
+    frpOriginWhenCrossOrigin = "origin-when-cross-origin"
+    frpUnsafeUrl = "unsafe-url"
+
+  Response* = ref object of JsRoot  ## https://developer.mozilla.org/en-US/docs/Web/API/Response
+    bodyUsed*, ok*, redirected*: bool
+    typ* {.importjs: "type".}: cstring
+    url*, statusText*: cstring
+    status*: cint
+    headers*: Headers
+    body*: cstring
+
+  Request* = ref object of JsRoot  ## https://developer.mozilla.org/en-US/docs/Web/API/Request
+    bodyUsed*, ok*, redirected*: bool
+    typ* {.importjs: "type".}: cstring
+    url*, statusText*: cstring
+    status*: cint
+    headers*: Headers
+    body*: cstring
+
+func newResponse*(body: cstring | FormData): Response {.importjs: "(new Response(#))".}
+  ## Constructor for `Response`. This does *not* call `fetch()`. Same as `new Response()`.
+
+func newRequest*(url: cstring): Request {.importjs: "(new Request(#))".}
+  ## Constructor for `Request`. This does *not* call `fetch()`. Same as `new Request()`.
+
+func newRequest*(url: cstring; fetchOptions: FetchOptions): Request {.importjs: "(new Request(#, #))".}
+  ## Constructor for `Request` with `fetchOptions`. Same as `fetch(url, fetchOptions)`.
+
+func clone*(self: Response | Request): Response {.importjs: "#.$1()".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/Response/clone
+
+proc text*(self: Response): Future[cstring] {.importjs: "#.$1()".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/Response/text
+
+proc json*(self: Response): Future[JsObject] {.importjs: "#.$1()".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/Response/json
+
+proc formData*(self: Response): Future[FormData] {.importjs: "#.$1()".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/Response/formData
+
+proc unsafeNewFetchOptions*(metod, body, mode, credentials, cache, referrerPolicy: cstring;
+    keepalive: bool; redirect = "follow".cstring; referrer = "client".cstring; integrity = "".cstring; headers: Headers = newHeaders()): FetchOptions {.importjs:
+    "{method: #, body: #, mode: #, credentials: #, cache: #, referrerPolicy: #, keepalive: #, redirect: #, referrer: #, integrity: #, headers: #}".}
+  ## .. warning:: Unsafe `newfetchOptions`.
+
+func newfetchOptions*(metod = HttpGet; body: cstring = nil;
+    mode = fmCors; credentials = fcSameOrigin; cache = fchDefault; referrerPolicy = frpNoReferrerWhenDowngrade;
+    keepalive = false; redirect = frFollow; referrer = "client".cstring; integrity = "".cstring,
+    headers: Headers = newHeaders()): FetchOptions =
+  ## Constructor for `FetchOptions`.
+  result = FetchOptions(
+    body: if metod notin {HttpHead, HttpGet}: body else: nil, 
+    mode: cstring($mode), credentials: cstring($credentials), cache: cstring($cache), referrerPolicy: cstring($referrerPolicy),
+    keepalive: keepalive, redirect: cstring($redirect), referrer: referrer, integrity: integrity, headers: headers,
+    metod: (case metod
+      of HttpHead:   "HEAD".cstring
+      of HttpGet:    "GET".cstring
+      of HttpPost:   "POST".cstring
+      of HttpPut:    "PUT".cstring
+      of HttpDelete: "DELETE".cstring
+      of HttpPatch:  "PATCH".cstring
+      else:          "GET".cstring
+    )
+  )
+
+proc fetch*(url: cstring | Request): Future[Response] {.importjs: "$1(#)".}
+  ## `fetch()` API, simple `GET` only, returns a `Future[Response]`.
+
+proc fetch*(url: cstring | Request; options: FetchOptions): Future[Response] {.importjs: "$1(#, #)".}
+  ## `fetch()` API that takes a `FetchOptions`, returns a `Future[Response]`.
+
+func toCstring*(self: Request | Response | FetchOptions): cstring {.importjs: "JSON.stringify(#)".}
+
+func `$`*(self: Request | Response | FetchOptions): string = $toCstring(self)
+
+
+runnableExamples("-r:off"):
+  import std/[asyncjs, jsconsole, jsformdata, jsheaders]
+  from std/httpcore import HttpMethod
+  from std/jsffi import JsObject
+  from std/sugar import `=>`
+
+  block:
+    let options0: FetchOptions = unsafeNewFetchOptions(
+      metod = "POST".cstring,
+      body = """{"key": "value"}""".cstring,
+      mode = "no-cors".cstring,
+      credentials = "omit".cstring,
+      cache = "no-cache".cstring,
+      referrerPolicy = "no-referrer".cstring,
+      keepalive = false,
+      redirect = "follow".cstring,
+      referrer = "client".cstring,
+      integrity = "".cstring,
+      headers = newHeaders()
+    )
+    assert options0.keepalive == false
+    assert options0.metod == "POST".cstring
+    assert options0.body == """{"key": "value"}""".cstring
+    assert options0.mode == "no-cors".cstring
+    assert options0.credentials == "omit".cstring
+    assert options0.cache == "no-cache".cstring
+    assert options0.referrerPolicy == "no-referrer".cstring
+    assert options0.redirect == "follow".cstring
+    assert options0.referrer == "client".cstring
+    assert options0.integrity == "".cstring
+    assert options0.headers.len == 0
+
+  block:
+    let options1: FetchOptions = newFetchOptions(
+      metod =  HttpPost,
+      body = """{"key": "value"}""".cstring,
+      mode = fmNoCors,
+      credentials = fcOmit,
+      cache = fchNoCache,
+      referrerPolicy = frpNoReferrer,
+      keepalive = false,
+      redirect = frFollow,
+      referrer = "client".cstring,
+      integrity = "".cstring,
+      headers = newHeaders()
+    )
+    assert options1.keepalive == false
+    assert options1.metod == $HttpPost
+    assert options1.body == """{"key": "value"}""".cstring
+    assert options1.mode == $fmNoCors
+    assert options1.credentials == $fcOmit
+    assert options1.cache == $fchNoCache
+    assert options1.referrerPolicy == $frpNoReferrer
+    assert options1.redirect == $frFollow
+    assert options1.referrer == "client".cstring
+    assert options1.integrity == "".cstring
+    assert options1.headers.len == 0
+
+  block:
+    let response: Response = newResponse(body = "-. .. --".cstring)
+    let request: Request = newRequest(url = "http://nim-lang.org".cstring)
+
+  if not defined(nodejs):
+    block:
+      proc doFetch(): Future[Response] {.async.} =
+        fetch "https://httpbin.org/get".cstring
+
+      proc example() {.async.} =
+        let response: Response = await doFetch()
+        assert response.ok
+        assert response.status == 200.cint
+        assert response.headers is Headers
+        assert response.body is cstring
+
+      discard example()
+
+    block:
+      proc example2 {.async.} =
+        await fetch("https://api.github.com/users/torvalds".cstring)
+          .then((response: Response) => response.json())
+          .then((json: JsObject) => console.log(json))
+          .catch((err: Error) => console.log("Request Failed", err))
+
+      discard example2()
diff --git a/lib/std/jsformdata.nim b/lib/std/jsformdata.nim
new file mode 100644
index 000000000..61dcc39a3
--- /dev/null
+++ b/lib/std/jsformdata.nim
@@ -0,0 +1,69 @@
+## - `FormData` for the JavaScript target: https://developer.mozilla.org/en-US/docs/Web/API/FormData
+when not defined(js):
+  {.fatal: "Module jsformdata is designed to be used with the JavaScript backend.".}
+
+from std/dom import Blob
+
+type FormData* = ref object of JsRoot ## FormData API.
+
+func newFormData*(): FormData {.importjs: "new FormData()".}
+
+func add*(self: FormData; name: cstring; value: SomeNumber | bool | cstring | Blob) {.importjs: "#.append(#, #)".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/append
+  ##
+  ## .. hint:: Duplicate keys are allowed and order is preserved.
+
+func add*(self: FormData; name: cstring; value: SomeNumber | bool | cstring | Blob; filename: cstring) {.importjs: "#.append(#, #, #)".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/append
+  ##
+  ## .. hint:: Duplicate keys are allowed and order is preserved.
+
+func delete*(self: FormData; name: cstring) {.importjs: "#.$1(#)".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/delete
+  ##
+  ## .. warning:: Deletes *all items* with the same key name.
+
+func getAll*(self: FormData; name: cstring): seq[cstring] {.importjs: "#.$1(#)".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/getAll
+
+func hasKey*(self: FormData; name: cstring): bool {.importjs: "#.has(#)".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/has
+
+func keys*(self: FormData): seq[cstring] {.importjs: "Array.from(#.$1())".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/keys
+
+func values*(self: FormData): seq[cstring] {.importjs: "Array.from(#.$1())".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/values
+
+func pairs*(self: FormData): seq[tuple[key, val: cstring]] {.importjs: "Array.from(#.entries())".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/entries
+
+func put*(self: FormData; name: cstring; value: SomeNumber | bool | cstring | Blob; filename: cstring) {.importjs: "#.set(#, #, #)".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/set
+
+func `[]=`*(self: FormData; name: cstring; value: SomeNumber | bool | cstring | Blob) {.importjs: "#.set(#, #)".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/set
+
+func `[]`*(self: FormData; name: cstring): cstring {.importjs: "#.get(#)".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/get
+
+func clear*(self: FormData) {.importjs:
+  "(() => { const frmdt = #; Array.from(frmdt.keys()).forEach((key) => frmdt.delete(key)) })()".}
+  ## Convenience func to delete all items from `FormData`.
+
+func toCstring*(self: FormData): cstring {.importjs: "JSON.stringify(#)".}
+
+func `$`*(self: FormData): string = $toCstring(self)
+
+func len*(self: FormData): int {.importjs: "Array.from(#.entries()).length".}
+
+
+runnableExamples("-r:off"):
+  let data: FormData = newFormData()
+  data["key0"] = "value0".cstring
+  data.add("key1".cstring, "value1".cstring)
+  data.delete("key1")
+  assert data.hasKey("key0")
+  assert data["key0"] == "value0".cstring
+  data.clear()
+  assert data.len == 0
diff --git a/lib/std/jsheaders.nim b/lib/std/jsheaders.nim
new file mode 100644
index 000000000..6fd3b3468
--- /dev/null
+++ b/lib/std/jsheaders.nim
@@ -0,0 +1,83 @@
+## - HTTP Headers for the JavaScript target: https://developer.mozilla.org/en-US/docs/Web/API/Headers
+when not defined(js):
+  {.fatal: "Module jsheaders is designed to be used with the JavaScript backend.".}
+
+type Headers* = ref object of JsRoot ## HTTP Headers API.
+
+func newHeaders*(): Headers {.importjs: "new Headers()".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/Headers
+
+func add*(self: Headers; key: cstring; value: cstring) {.importjs: "#.append(#, #)".}
+  ## Allows duplicated keys.
+  ## https://developer.mozilla.org/en-US/docs/Web/API/Headers/append
+
+func delete*(self: Headers; key: cstring) {.importjs: "#.$1(#)".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/Headers/delete
+  ##
+  ## .. warning:: Delete *all* items with `key` from the headers, including duplicated keys.
+
+func hasKey*(self: Headers; key: cstring): bool {.importjs: "#.has(#)".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/Headers/has
+
+func keys*(self: Headers): seq[cstring] {.importjs: "Array.from(#.$1())".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/Headers/keys
+
+func values*(self: Headers): seq[cstring] {.importjs: "Array.from(#.$1())".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/Headers/values
+
+func entries*(self: Headers): seq[tuple[key, value: cstring]] {.importjs: "Array.from(#.$1())".}
+  ## https://developer.mozilla.org/en-US/docs/Web/API/Headers/entries
+
+func `[]`*(self: Headers; key: cstring): cstring {.importjs: "#.get(#)".}
+  ## Get *all* items with `key` from the headers, including duplicated values.
+  ## https://developer.mozilla.org/en-US/docs/Web/API/Headers/get
+
+func `[]=`*(self: Headers; key: cstring; value: cstring) {.importjs: "#.set(#, #)".}
+  ## Do *not* allow duplicated keys, overwrites duplicated keys.
+  ## https://developer.mozilla.org/en-US/docs/Web/API/Headers/set
+
+func clear*(self: Headers) {.importjs:
+  "(() => { const header = #; Array.from(header.keys()).forEach((key) => header.delete(key)) })()".}
+  ## Convenience func to delete all items from `Headers`.
+
+func toCstring*(self: Headers): cstring {.importjs: "JSON.stringify(Array.from(#.entries()))".}
+  ## Returns a `cstring` representation of `Headers`.
+
+func `$`*(self: Headers): string = $toCstring(self)
+
+func len*(self: Headers): int {.importjs: "Array.from(#.entries()).length".}
+
+
+runnableExamples("-r:off"):
+
+  block:
+    let header: Headers = newHeaders()
+    header.add("key", "value")
+    assert header.hasKey("key")
+    assert header.keys() == @["key".cstring]
+    assert header.values() == @["value".cstring]
+    assert header["key"] == "value".cstring
+    header["other"] = "another".cstring
+    assert header["other"] == "another".cstring
+    assert header.entries() == @[("key".cstring, "value".cstring), ("other".cstring, "another".cstring)]
+    assert header.toCstring() == """[["key","value"],["other","another"]]""".cstring
+    header.delete("other")
+    assert header.entries() == @[("key".cstring, "value".cstring)]
+    header.clear()
+    assert header.entries() == @[]
+    assert header.len == 0
+
+  block:
+    let header: Headers = newHeaders()
+    header.add("key", "a")
+    header.add("key", "b")  ## Duplicated.
+    header.add("key", "c")  ## Duplicated.
+    assert header["key"] == "a, b, c".cstring
+    header["key"] = "value".cstring
+    assert header["key"] == "value".cstring
+
+  block:
+    let header: Headers = newHeaders()
+    header["key"] = "a"
+    header["key"] = "b"  ## Overwrites.
+    assert header["key"] == "b".cstring
diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim
new file mode 100644
index 000000000..2d28748ce
--- /dev/null
+++ b/lib/std/jsonutils.nim
@@ -0,0 +1,493 @@
+##[
+This module implements a hookable (de)serialization for arbitrary types using JSON.
+Design goal: avoid importing modules where a custom serialization is needed;
+see strtabs.fromJsonHook,toJsonHook for an example.
+]##
+
+runnableExamples:
+  import std/[strtabs,json]
+  type Foo = ref object
+    t: bool
+    z1: int8
+  let a = (1.5'f32, (b: "b2", a: "a2"), 'x', @[Foo(t: true, z1: -3), nil], [{"name": "John"}.newStringTable])
+  let j = a.toJson
+  assert j.jsonTo(typeof(a)).toJson == j
+  assert $[NaN, Inf, -Inf, 0.0, -0.0, 1.0, 1e-2].toJson == """["nan","inf","-inf",0.0,-0.0,1.0,0.01]"""
+  assert 0.0.toJson.kind == JFloat
+  assert Inf.toJson.kind == JString
+
+import std/[json, strutils, tables, sets, strtabs, options, strformat]
+
+#[
+Future directions:
+add a way to customize serialization, for e.g.:
+* field renaming
+* allow serializing `enum` and `char` as `string` instead of `int`
+  (enum is more compact/efficient, and robust to enum renamings, but string
+  is more human readable)
+* handle cyclic references, using a cache of already visited addresses
+* implement support for serialization and de-serialization of nested variant
+  objects.
+]#
+
+import std/macros
+from std/enumutils import symbolName
+from std/typetraits import OrdinalEnum, tupleLen
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
+proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".}
+
+type
+  Joptions* = object # xxx rename FromJsonOptions
+    ## Options controlling the behavior of `fromJson`.
+    allowExtraKeys*: bool
+      ## If `true` Nim's object to which the JSON is parsed is not required to
+      ## have a field for every JSON key.
+    allowMissingKeys*: bool
+      ## If `true` Nim's object to which JSON is parsed is allowed to have
+      ## fields without corresponding JSON keys.
+    # in future work: a key rename could be added
+  EnumMode* = enum
+    joptEnumOrd
+    joptEnumSymbol
+    joptEnumString
+  JsonNodeMode* = enum ## controls `toJson` for JsonNode types
+    joptJsonNodeAsRef ## returns the ref as is
+    joptJsonNodeAsCopy ## returns a deep copy of the JsonNode
+    joptJsonNodeAsObject ## treats JsonNode as a regular ref object
+  ToJsonOptions* = object
+    enumMode*: EnumMode
+    jsonNodeMode*: JsonNodeMode
+    # xxx charMode, etc
+
+proc initToJsonOptions*(): ToJsonOptions =
+  ## initializes `ToJsonOptions` with sane options.
+  ToJsonOptions(enumMode: joptEnumOrd, jsonNodeMode: joptJsonNodeAsRef)
+
+proc distinctBase(T: typedesc, recursive: static bool = true): typedesc {.magic: "TypeTrait".}
+template distinctBase[T](a: T, recursive: static bool = true): untyped = distinctBase(typeof(a), recursive)(a)
+
+macro getDiscriminants(a: typedesc): seq[string] =
+  ## return the discriminant keys
+  # candidate for std/typetraits
+  var a = a.getTypeImpl
+  doAssert a.kind == nnkBracketExpr
+  let sym = a[1]
+  let t = sym.getTypeImpl
+  let t2 = t[2]
+  case t2.kind
+  of nnkEmpty: # allow empty objects
+    result = quote do:
+        seq[string].default
+  of nnkRecList:
+    result = newTree(nnkBracket)
+    for ti in t2:
+      if ti.kind == nnkRecCase:
+        let key = ti[0][0]
+        result.add newLit key.strVal
+    if result.len > 0:
+      result = quote do:
+        @`result`
+    else:
+      result = quote do:
+        seq[string].default
+  else:
+    raiseAssert "unexpected kind: " & $t2.kind
+
+macro initCaseObject(T: typedesc, fun: untyped): untyped =
+  ## does the minimum to construct a valid case object, only initializing
+  ## the discriminant fields; see also `getDiscriminants`
+  # maybe candidate for std/typetraits
+  var a = T.getTypeImpl
+  doAssert a.kind == nnkBracketExpr
+  let sym = a[1]
+  let t = sym.getTypeImpl
+  var t2: NimNode
+  case t.kind
+  of nnkObjectTy: t2 = t[2]
+  of nnkRefTy: t2 = t[0].getTypeImpl[2]
+  else: raiseAssert $t.kind # xxx `nnkPtrTy` could be handled too
+  doAssert t2.kind == nnkRecList
+  result = newTree(nnkObjConstr)
+  result.add sym
+  for ti in t2:
+    if ti.kind == nnkRecCase:
+      let key = ti[0][0]
+      let typ = ti[0][1]
+      let key2 = key.strVal
+      let val = quote do:
+        `fun`(`key2`, typedesc[`typ`])
+      result.add newTree(nnkExprColonExpr, key, val)
+
+proc raiseJsonException(condStr: string, msg: string) {.noinline.} =
+  # just pick 1 exception type for simplicity; other choices would be:
+  # JsonError, JsonParser, JsonKindError
+  raise newException(ValueError, condStr & " failed: " & msg)
+
+template checkJson(cond: untyped, msg = "") =
+  if not cond:
+    raiseJsonException(astToStr(cond), msg)
+
+proc hasField[T](obj: T, field: string): bool =
+  for k, _ in fieldPairs(obj):
+    if k == field:
+      return true
+  return false
+
+macro accessField(obj: typed, name: static string): untyped =
+  newDotExpr(obj, ident(name))
+
+template fromJsonFields(newObj, oldObj, json, discKeys, opt) =
+  type T = typeof(newObj)
+  # we could customize whether to allow JNull
+  checkJson json.kind == JObject, $json.kind
+  var num, numMatched = 0
+  for key, val in fieldPairs(newObj):
+    num.inc
+    when key notin discKeys:
+      if json.hasKey key:
+        numMatched.inc
+        fromJson(val, json[key], opt)
+      elif opt.allowMissingKeys:
+        # if there are no discriminant keys the `oldObj` must always have the
+        # same keys as the new one. Otherwise we must check, because they could
+        # be set to different branches.
+        when typeof(oldObj) isnot typeof(nil):
+          if discKeys.len == 0 or hasField(oldObj, key):
+            val = accessField(oldObj, key)
+      else:
+        checkJson false, "key '$1' for $2 not in $3" % [key, $T, json.pretty()]
+    else:
+      if json.hasKey key:
+        numMatched.inc
+
+  let ok =
+    if opt.allowExtraKeys and opt.allowMissingKeys:
+      true
+    elif opt.allowExtraKeys:
+      # This check is redundant because if here missing keys are not allowed,
+      # and if `num != numMatched` it will fail in the loop above but it is left
+      # for clarity.
+      assert num == numMatched
+      num == numMatched
+    elif opt.allowMissingKeys:
+      json.len == numMatched
+    else:
+      json.len == num and num == numMatched
+
+  checkJson ok, "There were $1 keys (expecting $2) for $3 with $4" % [$json.len, $num, $T, json.pretty()]
+
+proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions())
+
+proc discKeyMatch[T](obj: T, json: JsonNode, key: static string): bool =
+  if not json.hasKey key:
+    return true
+  let field = accessField(obj, key)
+  var jsonVal: typeof(field)
+  fromJson(jsonVal, json[key])
+  if jsonVal != field:
+    return false
+  return true
+
+macro discKeysMatchBodyGen(obj: typed, json: JsonNode,
+                           keys: static seq[string]): untyped =
+  result = newStmtList()
+  let r = ident("result")
+  for key in keys:
+    let keyLit = newLit key
+    result.add quote do:
+      `r` = `r` and discKeyMatch(`obj`, `json`, `keyLit`)
+
+proc discKeysMatch[T](obj: T, json: JsonNode, keys: static seq[string]): bool =
+  result = true
+  discKeysMatchBodyGen(obj, json, keys)
+
+proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) =
+  ## inplace version of `jsonTo`
+  #[
+  adding "json path" leading to `b` can be added in future work.
+  ]#
+  checkJson b != nil, $($T, b)
+  when compiles(fromJsonHook(a, b, opt)): fromJsonHook(a, b, opt)
+  elif compiles(fromJsonHook(a, b)): fromJsonHook(a, b)
+  elif T is bool: a = to(b,T)
+  elif T is enum:
+    case b.kind
+    of JInt: a = T(b.getBiggestInt())
+    of JString: a = parseEnum[T](b.getStr())
+    else: checkJson false, fmt"Expecting int/string for {$T} got {b.pretty()}"
+  elif T is uint|uint64: a = T(to(b, uint64))
+  elif T is Ordinal: a = cast[T](to(b, int))
+  elif T is pointer: a = cast[pointer](to(b, int))
+  elif T is distinct: a.distinctBase.fromJson(b)
+  elif T is string|SomeNumber: a = to(b,T)
+  elif T is cstring:
+    case b.kind
+    of JNull: a = nil
+    of JString: a = b.str
+    else: checkJson false, fmt"Expecting null/string for {$T} got {b.pretty()}"
+  elif T is JsonNode: a = b
+  elif T is ref | ptr:
+    if b.kind == JNull: a = nil
+    else:
+      a = T()
+      fromJson(a[], b, opt)
+  elif T is array:
+    checkJson a.len == b.len, fmt"Json array size doesn't match for {$T}"
+    var i = 0
+    for ai in mitems(a):
+      fromJson(ai, b[i], opt)
+      i.inc
+  elif T is set:
+    type E = typeof(for ai in a: ai)
+    for val in b.getElems:
+      incl a, jsonTo(val, E)
+  elif T is seq:
+    a.setLen b.len
+    for i, val in b.getElems:
+      fromJson(a[i], val, opt)
+  elif T is object:
+    template fun(key, typ): untyped {.used.} =
+      if b.hasKey key:
+        jsonTo(b[key], typ)
+      elif hasField(a, key):
+        accessField(a, key)
+      else:
+        default(typ)
+    const keys = getDiscriminants(T)
+    when keys.len == 0:
+      fromJsonFields(a, nil, b, keys, opt)
+    else:
+      if discKeysMatch(a, b, keys):
+        fromJsonFields(a, nil, b, keys, opt)
+      else:
+        var newObj = initCaseObject(T, fun)
+        fromJsonFields(newObj, a, b, keys, opt)
+        a = newObj
+  elif T is tuple:
+    when isNamedTuple(T):
+      fromJsonFields(a, nil, b, seq[string].default, opt)
+    else:
+      checkJson b.kind == JArray, $(b.kind) # we could customize whether to allow JNull
+
+      when compiles(tupleLen(T)):
+        let tupleSize = tupleLen(T)
+      else:
+        # Tuple len isn't in csources_v1 so using tupleLen would fail.
+        # Else branch basically never runs (tupleLen added in 1.1 and jsonutils in 1.4), but here for consistency
+        var tupleSize = 0
+        for val in fields(a):
+          tupleSize.inc
+
+      checkJson b.len == tupleSize, fmt"Json doesn't match expected length of {tupleSize}, got {b.pretty()}"
+      var i = 0
+      for val in fields(a):
+        fromJson(val, b[i], opt)
+        i.inc
+  else:
+    # checkJson not appropriate here
+    static: raiseAssert "not yet implemented: " & $T
+
+proc jsonTo*(b: JsonNode, T: typedesc, opt = Joptions()): T =
+  ## reverse of `toJson`
+  fromJson(result, b, opt)
+
+proc toJson*[T](a: T, opt = initToJsonOptions()): JsonNode =
+  ## serializes `a` to json; uses `toJsonHook(a: T)` if it's in scope to
+  ## customize serialization, see strtabs.toJsonHook for an example.
+  ##
+  ## .. note:: With `-d:nimPreviewJsonutilsHoleyEnum`, `toJson` now can 
+  ##    serialize/deserialize holey enums as regular enums (via `ord`) instead of as strings.
+  ##    It is expected that this behavior becomes the new default in upcoming versions.
+  when compiles(toJsonHook(a, opt)): result = toJsonHook(a, opt)
+  elif compiles(toJsonHook(a)): result = toJsonHook(a)
+  elif T is object | tuple:
+    when T is object or isNamedTuple(T):
+      result = newJObject()
+      for k, v in a.fieldPairs: result[k] = toJson(v, opt)
+    else:
+      result = newJArray()
+      for v in a.fields: result.add toJson(v, opt)
+  elif T is ref | ptr:
+    template impl =
+      if system.`==`(a, nil): result = newJNull()
+      else: result = toJson(a[], opt)
+    when T is JsonNode:
+      case opt.jsonNodeMode
+      of joptJsonNodeAsRef: result = a
+      of joptJsonNodeAsCopy: result = copy(a)
+      of joptJsonNodeAsObject: impl()
+    else: impl()
+  elif T is array | seq | set:
+    result = newJArray()
+    for ai in a: result.add toJson(ai, opt)
+  elif T is pointer: result = toJson(cast[int](a), opt)
+    # edge case: `a == nil` could've also led to `newJNull()`, but this results
+    # in simpler code for `toJson` and `fromJson`.
+  elif T is distinct: result = toJson(a.distinctBase, opt)
+  elif T is bool: result = %(a)
+  elif T is SomeInteger: result = %a
+  elif T is enum:
+    case opt.enumMode
+    of joptEnumOrd:
+      when T is Ordinal or defined(nimPreviewJsonutilsHoleyEnum): %(a.ord)
+      else: toJson($a, opt)
+    of joptEnumSymbol:
+      when T is OrdinalEnum:
+        toJson(symbolName(a), opt)
+      else:
+        toJson($a, opt)
+    of joptEnumString: toJson($a, opt)
+  elif T is Ordinal: result = %(a.ord)
+  elif T is cstring: (if a == nil: result = newJNull() else: result = % $a)
+  else: result = %a
+
+proc fromJsonHook*[K: string|cstring, V](t: var (Table[K, V] | OrderedTable[K, V]),
+                         jsonNode: JsonNode, opt = Joptions()) =
+  ## Enables `fromJson` for `Table` and `OrderedTable` types.
+  ##
+  ## See also:
+  ## * `toJsonHook proc<#toJsonHook>`_
+  runnableExamples:
+    import std/[tables, json]
+    var foo: tuple[t: Table[string, int], ot: OrderedTable[string, int]]
+    fromJson(foo, parseJson("""
+      {"t":{"two":2,"one":1},"ot":{"one":1,"three":3}}"""))
+    assert foo.t == [("one", 1), ("two", 2)].toTable
+    assert foo.ot == [("one", 1), ("three", 3)].toOrderedTable
+
+  assert jsonNode.kind == JObject,
+          "The kind of the `jsonNode` must be `JObject`, but its actual " &
+          "type is `" & $jsonNode.kind & "`."
+  clear(t)
+  for k, v in jsonNode:
+    t[k] = jsonTo(v, V, opt)
+
+proc toJsonHook*[K: string|cstring, V](t: (Table[K, V] | OrderedTable[K, V]), opt = initToJsonOptions()): JsonNode =
+  ## Enables `toJson` for `Table` and `OrderedTable` types.
+  ##
+  ## See also:
+  ## * `fromJsonHook proc<#fromJsonHook,,JsonNode>`_
+  runnableExamples:
+    import std/[tables, json, sugar]
+    let foo = (
+      t: [("two", 2)].toTable,
+      ot: [("one", 1), ("three", 3)].toOrderedTable)
+    assert $toJson(foo) == """{"t":{"two":2},"ot":{"one":1,"three":3}}"""
+    # if keys are not string|cstring, you can use this:
+    let a = {10: "foo", 11: "bar"}.newOrderedTable
+    let a2 = collect: (for k,v in a: (k,v))
+    assert $toJson(a2) == """[[10,"foo"],[11,"bar"]]"""
+
+  result = newJObject()
+  for k, v in pairs(t):
+    # not sure if $k has overhead for string
+    result[(when K is string: k else: $k)] = toJson(v, opt)
+
+proc fromJsonHook*[A](s: var SomeSet[A], jsonNode: JsonNode, opt = Joptions()) =
+  ## Enables `fromJson` for `HashSet` and `OrderedSet` types.
+  ##
+  ## See also:
+  ## * `toJsonHook proc<#toJsonHook,SomeSet[A]>`_
+  runnableExamples:
+    import std/[sets, json]
+    var foo: tuple[hs: HashSet[string], os: OrderedSet[string]]
+    fromJson(foo, parseJson("""
+      {"hs": ["hash", "set"], "os": ["ordered", "set"]}"""))
+    assert foo.hs == ["hash", "set"].toHashSet
+    assert foo.os == ["ordered", "set"].toOrderedSet
+
+  assert jsonNode.kind == JArray,
+          "The kind of the `jsonNode` must be `JArray`, but its actual " &
+          "type is `" & $jsonNode.kind & "`."
+  clear(s)
+  for v in jsonNode:
+    incl(s, jsonTo(v, A, opt))
+
+proc toJsonHook*[A](s: SomeSet[A], opt = initToJsonOptions()): JsonNode =
+  ## Enables `toJson` for `HashSet` and `OrderedSet` types.
+  ##
+  ## See also:
+  ## * `fromJsonHook proc<#fromJsonHook,SomeSet[A],JsonNode>`_
+  runnableExamples:
+    import std/[sets, json]
+    let foo = (hs: ["hash"].toHashSet, os: ["ordered", "set"].toOrderedSet)
+    assert $toJson(foo) == """{"hs":["hash"],"os":["ordered","set"]}"""
+
+  result = newJArray()
+  for k in s:
+    add(result, toJson(k, opt))
+
+proc fromJsonHook*[T](self: var Option[T], jsonNode: JsonNode, opt = Joptions()) =
+  ## Enables `fromJson` for `Option` types.
+  ##
+  ## See also:
+  ## * `toJsonHook proc<#toJsonHook,Option[T]>`_
+  runnableExamples:
+    import std/[options, json]
+    var opt: Option[string]
+    fromJsonHook(opt, parseJson("\"test\""))
+    assert get(opt) == "test"
+    fromJson(opt, parseJson("null"))
+    assert isNone(opt)
+
+  if jsonNode.kind != JNull:
+    self = some(jsonTo(jsonNode, T, opt))
+  else:
+    self = none[T]()
+
+proc toJsonHook*[T](self: Option[T], opt = initToJsonOptions()): JsonNode =
+  ## Enables `toJson` for `Option` types.
+  ##
+  ## See also:
+  ## * `fromJsonHook proc<#fromJsonHook,Option[T],JsonNode>`_
+  runnableExamples:
+    import std/[options, json]
+    let optSome = some("test")
+    assert $toJson(optSome) == "\"test\""
+    let optNone = none[string]()
+    assert $toJson(optNone) == "null"
+
+  if isSome(self):
+    toJson(get(self), opt)
+  else:
+    newJNull()
+
+proc fromJsonHook*(a: var StringTableRef, b: JsonNode) =
+  ## Enables `fromJson` for `StringTableRef` type.
+  ##
+  ## See also:
+  ## * `toJsonHook proc<#toJsonHook,StringTableRef>`_
+  runnableExamples:
+    import std/[strtabs, json]
+    var t = newStringTable(modeCaseSensitive)
+    let jsonStr = """{"mode": 0, "table": {"name": "John", "surname": "Doe"}}"""
+    fromJsonHook(t, parseJson(jsonStr))
+    assert t[] == newStringTable("name", "John", "surname", "Doe",
+                                 modeCaseSensitive)[]
+
+  var mode = jsonTo(b["mode"], StringTableMode)
+  a = newStringTable(mode)
+  let b2 = b["table"]
+  for k,v in b2: a[k] = jsonTo(v, string)
+
+proc toJsonHook*(a: StringTableRef): JsonNode =
+  ## Enables `toJson` for `StringTableRef` type.
+  ##
+  ## See also:
+  ## * `fromJsonHook proc<#fromJsonHook,StringTableRef,JsonNode>`_
+  runnableExamples:
+    import std/[strtabs, json]
+    let t = newStringTable("name", "John", "surname", "Doe", modeCaseSensitive)
+    let jsonStr = """{"mode": "modeCaseSensitive",
+                      "table": {"name": "John", "surname": "Doe"}}"""
+    assert toJson(t) == parseJson(jsonStr)
+
+  result = newJObject()
+  result["mode"] = toJson($a.mode)
+  let t = newJObject()
+  for k,v in a: t[k] = toJson(v)
+  result["table"] = t
diff --git a/lib/std/logic.nim b/lib/std/logic.nim
new file mode 100644
index 000000000..84640d380
--- /dev/null
+++ b/lib/std/logic.nim
@@ -0,0 +1,10 @@
+## This module provides further logic operators like 'forall' and 'exists'
+## They are only supported in `.ensures` etc pragmas.
+
+proc `->`*(a, b: bool): bool {.magic: "Implies".}
+proc `<->`*(a, b: bool): bool {.magic: "Iff".}
+
+proc forall*(args: varargs[untyped]): bool {.magic: "Forall".}
+proc exists*(args: varargs[untyped]): bool {.magic: "Exists".}
+
+proc old*[T](x: T): T {.magic: "Old".}
diff --git a/lib/std/monotimes.nim b/lib/std/monotimes.nim
new file mode 100644
index 000000000..bf6dc776b
--- /dev/null
+++ b/lib/std/monotimes.nim
@@ -0,0 +1,160 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2019 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+##[
+The `std/monotimes` module implements monotonic timestamps. A monotonic
+timestamp represents the time that has passed since some system defined
+point in time. The monotonic timestamps are guaranteed not to decrease,
+meaning that that the following is guaranteed to work:
+]##
+
+runnableExamples:
+  let a = getMonoTime()
+  let b = getMonoTime()
+  assert a <= b
+
+##[
+This is not guaranteed for the `times.Time` type! This means that the
+`MonoTime` should be used when measuring durations of time with
+high precision.
+
+However, since `MonoTime` represents the time that has passed since some
+unknown time origin, it cannot be converted to a human readable timestamp.
+If this is required, the `times.Time` type should be used instead.
+
+The `MonoTime` type stores the timestamp in nanosecond resolution, but note
+that the actual supported time resolution differs for different systems.
+
+See also
+========
+* `times module <times.html>`_
+]##
+
+import std/times
+
+type
+  MonoTime* = object ## Represents a monotonic timestamp.
+    ticks: int64
+
+when defined(macosx):
+  type
+    MachTimebaseInfoData {.pure, final, importc: "mach_timebase_info_data_t",
+        header: "<mach/mach_time.h>".} = object
+      numer, denom: int32
+
+  proc mach_absolute_time(): int64 {.importc, header: "<mach/mach.h>".}
+  proc mach_timebase_info(info: var MachTimebaseInfoData) {.importc,
+    header: "<mach/mach_time.h>".}
+
+when defined(js):
+  proc getJsTicks: float =
+    ## Returns ticks in the unit seconds.
+    when defined(nodejs):
+      {.emit: """
+      let process = require('process');
+      let time = process.hrtime();
+      `result` = time[0] + time[1] / 1000000000;
+      """.}
+    else:
+      proc jsNow(): float {.importjs: "window.performance.now()".}
+      result = jsNow() / 1000
+
+  # Workaround for #6752.
+  {.push overflowChecks: off.}
+  proc `-`(a, b: int64): int64 =
+    system.`-`(a, b)
+  proc `+`(a, b: int64): int64 =
+    system.`+`(a, b)
+  {.pop.}
+
+elif defined(posix) and not defined(osx):
+  import std/posix
+
+when defined(zephyr):
+  proc k_uptime_ticks(): int64 {.importc: "k_uptime_ticks", header: "<kernel.h>".}
+  proc k_ticks_to_ns_floor64(ticks: int64): int64 {.importc: "k_ticks_to_ns_floor64", header: "<kernel.h>".}
+
+elif defined(windows):
+  proc QueryPerformanceCounter(res: var uint64) {.
+    importc: "QueryPerformanceCounter", stdcall, dynlib: "kernel32".}
+  proc QueryPerformanceFrequency(res: var uint64) {.
+    importc: "QueryPerformanceFrequency", stdcall, dynlib: "kernel32".}
+
+proc getMonoTime*(): MonoTime {.tags: [TimeEffect].} =
+  ## Returns the current `MonoTime` timestamp.
+  ##
+  ## When compiled with the JS backend and executed in a browser,
+  ## this proc calls `window.performance.now()`.
+  ## See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now)
+  ## for more information.
+  when defined(js):
+    let ticks = getJsTicks()
+    result = MonoTime(ticks: (ticks * 1_000_000_000).int64)
+  elif defined(macosx):
+    let ticks = mach_absolute_time()
+    var machAbsoluteTimeFreq: MachTimebaseInfoData
+    mach_timebase_info(machAbsoluteTimeFreq)
+    result = MonoTime(ticks: ticks * machAbsoluteTimeFreq.numer div
+      machAbsoluteTimeFreq.denom)
+  elif defined(zephyr):
+    let ticks = k_ticks_to_ns_floor64(k_uptime_ticks())
+    result = MonoTime(ticks: ticks)
+  elif defined(posix):
+    var ts: Timespec
+    discard clock_gettime(CLOCK_MONOTONIC, ts)
+    result = MonoTime(ticks: ts.tv_sec.int64 * 1_000_000_000 +
+      ts.tv_nsec.int64)
+  elif defined(windows):
+    var ticks: uint64
+    QueryPerformanceCounter(ticks)
+
+    var freq: uint64
+    QueryPerformanceFrequency(freq)
+    let queryPerformanceCounterFreq = 1_000_000_000'u64 div freq
+    result = MonoTime(ticks: (ticks * queryPerformanceCounterFreq).int64)
+
+proc ticks*(t: MonoTime): int64 =
+  ## Returns the raw ticks value from a `MonoTime`. This value always uses
+  ## nanosecond time resolution.
+  t.ticks
+
+proc `$`*(t: MonoTime): string =
+  $t.ticks
+
+proc `-`*(a, b: MonoTime): Duration =
+  ## Returns the difference between two `MonoTime` timestamps as a `Duration`.
+  initDuration(nanoseconds = (a.ticks - b.ticks))
+
+proc `+`*(a: MonoTime, b: Duration): MonoTime =
+  ## Increases `a` by `b`.
+  MonoTime(ticks: a.ticks + b.inNanoseconds)
+
+proc `-`*(a: MonoTime, b: Duration): MonoTime =
+  ## Reduces `a` by `b`.
+  MonoTime(ticks: a.ticks - b.inNanoseconds)
+
+proc `<`*(a, b: MonoTime): bool =
+  ## Returns true if `a` happened before `b`.
+  a.ticks < b.ticks
+
+proc `<=`*(a, b: MonoTime): bool =
+  ## Returns true if `a` happened before `b` or if they happened simultaneous.
+  a.ticks <= b.ticks
+
+proc `==`*(a, b: MonoTime): bool =
+  ## Returns true if `a` and `b` happened simultaneous.
+  a.ticks == b.ticks
+
+proc high*(typ: typedesc[MonoTime]): MonoTime =
+  ## Returns the highest representable `MonoTime`.
+  MonoTime(ticks: high(int64))
+
+proc low*(typ: typedesc[MonoTime]): MonoTime =
+  ## Returns the lowest representable `MonoTime`.
+  MonoTime(ticks: low(int64))
diff --git a/lib/std/objectdollar.nim b/lib/std/objectdollar.nim
new file mode 100644
index 000000000..86ce9afc8
--- /dev/null
+++ b/lib/std/objectdollar.nim
@@ -0,0 +1,13 @@
+## This module implements a generic `$` operator to convert objects to strings.
+
+import std/private/miscdollars
+
+proc `$`*[T: object](x: T): string =
+  ## Generic `$` operator for objects with similar output to
+  ## `$` for named tuples.
+  runnableExamples:
+    type Foo = object
+      a, b: int
+    let x = Foo(a: 23, b: 45)
+    assert $x == "(a: 23, b: 45)"
+  tupleObjectDollar(result, x)
diff --git a/lib/std/oserrors.nim b/lib/std/oserrors.nim
new file mode 100644
index 000000000..7b11c5e8e
--- /dev/null
+++ b/lib/std/oserrors.nim
@@ -0,0 +1,117 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2022 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+
+## The `std/oserrors` module implements OS error reporting.
+
+type
+  OSErrorCode* = distinct int32 ## Specifies an OS Error Code.
+
+when not defined(nimscript):
+  when defined(windows):
+    import std/winlean
+    when defined(nimPreviewSlimSystem):
+      import std/widestrs
+  else:
+    var errno {.importc, header: "<errno.h>".}: cint
+
+    proc c_strerror(errnum: cint): cstring {.
+      importc: "strerror", header: "<string.h>".}
+
+proc `==`*(err1, err2: OSErrorCode): bool {.borrow.}
+proc `$`*(err: OSErrorCode): string {.borrow.}
+
+proc osErrorMsg*(errorCode: OSErrorCode): string =
+  ## Converts an OS error code into a human readable string.
+  ##
+  ## The error code can be retrieved using the `osLastError proc`_.
+  ##
+  ## If conversion fails, or `errorCode` is `0` then `""` will be
+  ## returned.
+  ##
+  ## See also:
+  ## * `raiseOSError proc`_
+  ## * `osLastError proc`_
+  runnableExamples:
+    when defined(linux):
+      assert osErrorMsg(OSErrorCode(0)) == ""
+      assert osErrorMsg(OSErrorCode(1)) == "Operation not permitted"
+      assert osErrorMsg(OSErrorCode(2)) == "No such file or directory"
+
+  result = ""
+  when defined(nimscript):
+    discard
+  elif defined(windows):
+    if errorCode != OSErrorCode(0'i32):
+      var msgbuf: WideCString
+      if formatMessageW(0x00000100 or 0x00001000 or 0x00000200,
+                      nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
+        result = $msgbuf
+        if msgbuf != nil: localFree(cast[pointer](msgbuf))
+  else:
+    if errorCode != OSErrorCode(0'i32):
+      result = $c_strerror(errorCode.int32)
+
+proc newOSError*(
+  errorCode: OSErrorCode, additionalInfo = ""
+): owned(ref OSError) {.noinline.} =
+  ## Creates a new `OSError exception <system.html#OSError>`_.
+  ##
+  ## The `errorCode` will determine the
+  ## message, `osErrorMsg proc`_ will be used
+  ## to get this message.
+  ##
+  ## The error code can be retrieved using the `osLastError proc`_.
+  ##
+  ## If the error code is `0` or an error message could not be retrieved,
+  ## the message `unknown OS error` will be used.
+  ##
+  ## See also:
+  ## * `osErrorMsg proc`_
+  ## * `osLastError proc`_
+  result = (ref OSError)(errorCode: errorCode.int32, msg: osErrorMsg(errorCode))
+  if additionalInfo.len > 0:
+    if result.msg.len > 0 and result.msg[^1] != '\n': result.msg.add '\n'
+    result.msg.add "Additional info: "
+    result.msg.add additionalInfo
+      # don't add trailing `.` etc, which negatively impacts "jump to file" in IDEs.
+  if result.msg == "":
+    result.msg = "unknown OS error"
+
+proc raiseOSError*(errorCode: OSErrorCode, additionalInfo = "") {.noinline.} =
+  ## Raises an `OSError exception <system.html#OSError>`_.
+  ##
+  ## Read the description of the `newOSError proc`_ to learn
+  ## how the exception object is created.
+  raise newOSError(errorCode, additionalInfo)
+
+{.push stackTrace:off.}
+proc osLastError*(): OSErrorCode {.sideEffect.} =
+  ## Retrieves the last operating system error code.
+  ##
+  ## This procedure is useful in the event when an OS call fails. In that case
+  ## this procedure will return the error code describing the reason why the
+  ## OS call failed. The `OSErrorMsg` procedure can then be used to convert
+  ## this code into a string.
+  ##
+  ## .. warning:: The behaviour of this procedure varies between Windows and POSIX systems.
+  ##   On Windows some OS calls can reset the error code to `0` causing this
+  ##   procedure to return `0`. It is therefore advised to call this procedure
+  ##   immediately after an OS call fails. On POSIX systems this is not a problem.
+  ##
+  ## See also:
+  ## * `osErrorMsg proc`_
+  ## * `raiseOSError proc`_
+  when defined(nimscript):
+    discard
+  elif defined(windows):
+    result = cast[OSErrorCode](getLastError())
+  else:
+    result = OSErrorCode(errno)
+{.pop.}
diff --git a/lib/std/outparams.nim b/lib/std/outparams.nim
new file mode 100644
index 000000000..a471fbaa7
--- /dev/null
+++ b/lib/std/outparams.nim
@@ -0,0 +1,38 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2022 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## `outParamsAt` macro for easy writing code that works with both 2.0 and 1.x.
+
+import std/macros
+
+macro outParamsAt*(positions: static openArray[int]; n: untyped): untyped =
+  ## Use this macro to annotate `out` parameters in a portable way.
+  runnableExamples:
+    proc p(x: var int) {.outParamsAt: [1].} =
+      discard "x is really an 'out int' if the Nim compiler supports 'out' parameters"
+
+  result = n
+  when defined(nimHasOutParams):
+    var p = n.params
+    for po in positions:
+      p[po][^2].expectKind nnkVarTy
+      p[po][^2] = newTree(nnkOutTy, p[po][^2][0])
+
+when isMainModule:
+  {.experimental: "strictDefs".}
+
+  proc main(x: var int) {.outParamsAt: [1].} =
+    x = 3
+
+  proc us =
+    var x: int
+    main x
+    echo x
+
+  us()
diff --git a/lib/std/packedsets.nim b/lib/std/packedsets.nim
new file mode 100644
index 000000000..3320558f2
--- /dev/null
+++ b/lib/std/packedsets.nim
@@ -0,0 +1,601 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## The `packedsets` module implements an efficient `Ordinal` set implemented as a
+## `sparse bit set`:idx:.
+##
+## Supports any Ordinal type.
+##
+## See also
+## ========
+## * `sets module <sets.html>`_ for more general hash sets
+
+import std/private/since
+import std/hashes
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+type
+  BitScalar = uint
+
+const
+  InitIntSetSize = 8              # must be a power of two!
+  TrunkShift = 9
+  BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and
+                                  # divisible by 64
+  TrunkMask = BitsPerTrunk - 1
+  IntsPerTrunk = BitsPerTrunk div (sizeof(BitScalar) * 8)
+  IntShift = 5 + ord(sizeof(BitScalar) == 8) # 5 or 6, depending on int width
+  IntMask = 1 shl IntShift - 1
+
+type
+  Trunk {.acyclic.} = ref object
+    next: Trunk                                 # all nodes are connected with this pointer
+    key: int                                    # start address at bit 0
+    bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector
+
+  TrunkSeq = seq[Trunk]
+
+  PackedSet*[A: Ordinal] = object
+    ## An efficient set of `Ordinal` types implemented as a sparse bit set.
+    elems: int           # only valid for small numbers
+    counter, max: int
+    head: Trunk
+    data: TrunkSeq
+    a: array[0..33, int] # profiling shows that 34 elements are enough
+
+proc mustRehash[T](t: T): bool {.inline.} =
+  let length = t.max + 1
+  assert length > t.counter
+  result = (length * 2 < t.counter * 3) or (length - t.counter < 4)
+
+proc nextTry(h, maxHash: Hash, perturb: var Hash): Hash {.inline.} =
+  const PERTURB_SHIFT = 5
+  var perturb2 = cast[uint](perturb) shr PERTURB_SHIFT
+  perturb = cast[Hash](perturb2)
+  result = ((5 * h) + 1 + perturb) and maxHash
+
+proc packedSetGet[A](t: PackedSet[A], key: int): Trunk =
+  var h = key and t.max
+  var perturb = key
+  while t.data[h] != nil:
+    if t.data[h].key == key:
+      return t.data[h]
+    h = nextTry(h, t.max, perturb)
+  result = nil
+
+proc intSetRawInsert[A](t: PackedSet[A], data: var TrunkSeq, desc: Trunk) =
+  var h = desc.key and t.max
+  var perturb = desc.key
+  while data[h] != nil:
+    assert data[h] != desc
+    h = nextTry(h, t.max, perturb)
+  assert data[h] == nil
+  data[h] = desc
+
+proc intSetEnlarge[A](t: var PackedSet[A]) =
+  var n: TrunkSeq
+  var oldMax = t.max
+  t.max = ((t.max + 1) * 2) - 1
+  newSeq(n, t.max + 1)
+  for i in countup(0, oldMax):
+    if t.data[i] != nil: intSetRawInsert(t, n, t.data[i])
+  swap(t.data, n)
+
+proc intSetPut[A](t: var PackedSet[A], key: int): Trunk =
+  var h = key and t.max
+  var perturb = key
+  while t.data[h] != nil:
+    if t.data[h].key == key:
+      return t.data[h]
+    h = nextTry(h, t.max, perturb)
+  if mustRehash(t): intSetEnlarge(t)
+  inc(t.counter)
+  h = key and t.max
+  perturb = key
+  while t.data[h] != nil: h = nextTry(h, t.max, perturb)
+  assert t.data[h] == nil
+  new(result)
+  result.next = t.head
+  result.key = key
+  t.head = result
+  t.data[h] = result
+
+proc bitincl[A](s: var PackedSet[A], key: int) {.inline.} =
+  var t = intSetPut(s, key shr TrunkShift)
+  var u = key and TrunkMask
+  t.bits[u shr IntShift] = t.bits[u shr IntShift] or
+      (BitScalar(1) shl (u and IntMask))
+
+proc exclImpl[A](s: var PackedSet[A], key: int) =
+  if s.elems <= s.a.len:
+    for i in 0..<s.elems:
+      if s.a[i] == key:
+        s.a[i] = s.a[s.elems - 1]
+        dec(s.elems)
+        return
+  else:
+    var t = packedSetGet(s, key shr TrunkShift)
+    if t != nil:
+      var u = key and TrunkMask
+      t.bits[u shr IntShift] = t.bits[u shr IntShift] and
+          not(BitScalar(1) shl (u and IntMask))
+
+template dollarImpl(): untyped =
+  result = "{"
+  for key in items(s):
+    if result.len > 1: result.add(", ")
+    result.add $key
+  result.add("}")
+
+iterator items*[A](s: PackedSet[A]): A {.inline.} =
+  ## Iterates over any included element of `s`.
+  if s.elems <= s.a.len:
+    for i in 0..<s.elems:
+      yield A(s.a[i])
+  else:
+    var r = s.head
+    while r != nil:
+      var i = 0
+      while i <= high(r.bits):
+        var w: uint = r.bits[i]
+        # taking a copy of r.bits[i] here is correct, because
+        # modifying operations are not allowed during traversation
+        var j = 0
+        while w != 0: # test all remaining bits for zero
+          if (w and 1) != 0: # the bit is set!
+            yield A((r.key shl TrunkShift) or (i shl IntShift +% j))
+          inc(j)
+          w = w shr 1
+        inc(i)
+      r = r.next
+
+proc initPackedSet*[A]: PackedSet[A] =
+  ## Returns an empty `PackedSet[A]`.
+  ## `A` must be `Ordinal`.
+  ##
+  ## **See also:**
+  ## * `toPackedSet proc <#toPackedSet,openArray[A]>`_
+  runnableExamples:
+    let a = initPackedSet[int]()
+    assert len(a) == 0
+
+    type Id = distinct int
+    var ids = initPackedSet[Id]()
+    ids.incl(3.Id)
+
+  result = PackedSet[A](
+    elems: 0,
+    counter: 0,
+    max: 0,
+    head: nil,
+    data: @[])
+  #  a: array[0..33, int] # profiling shows that 34 elements are enough
+
+proc contains*[A](s: PackedSet[A], key: A): bool =
+  ## Returns true if `key` is in `s`.
+  ##
+  ## This allows the usage of the `in` operator.
+  runnableExamples:
+    type ABCD = enum A, B, C, D
+
+    let a = [1, 3, 5].toPackedSet
+    assert a.contains(3)
+    assert 3 in a
+    assert not a.contains(8)
+    assert 8 notin a
+
+    let letters = [A, C].toPackedSet
+    assert A in letters
+    assert C in letters
+    assert B notin letters
+
+  if s.elems <= s.a.len:
+    result = false
+    for i in 0..<s.elems:
+      if s.a[i] == ord(key): return true
+  else:
+    var t = packedSetGet(s, ord(key) shr TrunkShift)
+    if t != nil:
+      var u = ord(key) and TrunkMask
+      result = (t.bits[u shr IntShift] and
+                (BitScalar(1) shl (u and IntMask))) != 0
+    else:
+      result = false
+
+proc incl*[A](s: var PackedSet[A], key: A) =
+  ## Includes an element `key` in `s`.
+  ##
+  ## This doesn't do anything if `key` is already in `s`.
+  ##
+  ## **See also:**
+  ## * `excl proc <#excl,PackedSet[A],A>`_ for excluding an element
+  ## * `incl proc <#incl,PackedSet[A],PackedSet[A]>`_ for including a set
+  ## * `containsOrIncl proc <#containsOrIncl,PackedSet[A],A>`_
+  runnableExamples:
+    var a = initPackedSet[int]()
+    a.incl(3)
+    a.incl(3)
+    assert len(a) == 1
+
+  if s.elems <= s.a.len:
+    for i in 0..<s.elems:
+      if s.a[i] == ord(key): return
+    if s.elems < s.a.len:
+      s.a[s.elems] = ord(key)
+      inc(s.elems)
+      return
+    newSeq(s.data, InitIntSetSize)
+    s.max = InitIntSetSize - 1
+    for i in 0..<s.elems:
+      bitincl(s, s.a[i])
+    s.elems = s.a.len + 1
+    # fall through:
+  bitincl(s, ord(key))
+
+proc incl*[A](s: var PackedSet[A], other: PackedSet[A]) =
+  ## Includes all elements from `other` into `s`.
+  ##
+  ## This is the in-place version of `s + other <#+,PackedSet[A],PackedSet[A]>`_.
+  ##
+  ## **See also:**
+  ## * `excl proc <#excl,PackedSet[A],PackedSet[A]>`_ for excluding a set
+  ## * `incl proc <#incl,PackedSet[A],A>`_ for including an element
+  ## * `containsOrIncl proc <#containsOrIncl,PackedSet[A],A>`_
+  runnableExamples:
+    var a = [1].toPackedSet
+    a.incl([5].toPackedSet)
+    assert len(a) == 2
+    assert 5 in a
+
+  for item in other.items: incl(s, item)
+
+proc toPackedSet*[A](x: openArray[A]): PackedSet[A] {.since: (1, 3).} =
+  ## Creates a new `PackedSet[A]` that contains the elements of `x`.
+  ##
+  ## Duplicates are removed.
+  ##
+  ## **See also:**
+  ## * `initPackedSet proc <#initPackedSet>`_
+  runnableExamples:
+    let a = [5, 6, 7, 8, 8].toPackedSet
+    assert len(a) == 4
+    assert $a == "{5, 6, 7, 8}"
+
+  result = initPackedSet[A]()
+  for item in x:
+    result.incl(item)
+
+proc containsOrIncl*[A](s: var PackedSet[A], key: A): bool =
+  ## Includes `key` in the set `s` and tells if `key` was already in `s`.
+  ##
+  ## The difference with regards to the `incl proc <#incl,PackedSet[A],A>`_ is
+  ## that this proc returns true if `s` already contained `key`. The
+  ## proc will return false if `key` was added as a new value to `s` during
+  ## this call.
+  ##
+  ## **See also:**
+  ## * `incl proc <#incl,PackedSet[A],A>`_ for including an element
+  ## * `missingOrExcl proc <#missingOrExcl,PackedSet[A],A>`_
+  runnableExamples:
+    var a = initPackedSet[int]()
+    assert a.containsOrIncl(3) == false
+    assert a.containsOrIncl(3) == true
+    assert a.containsOrIncl(4) == false
+
+  if s.elems <= s.a.len:
+    for i in 0..<s.elems:
+      if s.a[i] == ord(key):
+        return true
+    incl(s, key)
+    result = false
+  else:
+    var t = packedSetGet(s, ord(key) shr TrunkShift)
+    if t != nil:
+      var u = ord(key) and TrunkMask
+      result = (t.bits[u shr IntShift] and BitScalar(1) shl (u and IntMask)) != 0
+      if not result:
+        t.bits[u shr IntShift] = t.bits[u shr IntShift] or
+            (BitScalar(1) shl (u and IntMask))
+    else:
+      incl(s, key)
+      result = false
+
+proc excl*[A](s: var PackedSet[A], key: A) =
+  ## Excludes `key` from the set `s`.
+  ##
+  ## This doesn't do anything if `key` is not found in `s`.
+  ##
+  ## **See also:**
+  ## * `incl proc <#incl,PackedSet[A],A>`_ for including an element
+  ## * `excl proc <#excl,PackedSet[A],PackedSet[A]>`_ for excluding a set
+  ## * `missingOrExcl proc <#missingOrExcl,PackedSet[A],A>`_
+  runnableExamples:
+    var a = [3].toPackedSet
+    a.excl(3)
+    a.excl(3)
+    a.excl(99)
+    assert len(a) == 0
+
+  exclImpl[A](s, ord(key))
+
+proc excl*[A](s: var PackedSet[A], other: PackedSet[A]) =
+  ## Excludes all elements from `other` from `s`.
+  ##
+  ## This is the in-place version of `s - other <#-,PackedSet[A],PackedSet[A]>`_.
+  ##
+  ## **See also:**
+  ## * `incl proc <#incl,PackedSet[A],PackedSet[A]>`_ for including a set
+  ## * `excl proc <#excl,PackedSet[A],A>`_ for excluding an element
+  ## * `missingOrExcl proc <#missingOrExcl,PackedSet[A],A>`_
+  runnableExamples:
+    var a = [1, 5].toPackedSet
+    a.excl([5].toPackedSet)
+    assert len(a) == 1
+    assert 5 notin a
+
+  for item in other.items:
+    excl(s, item)
+
+proc len*[A](s: PackedSet[A]): int {.inline.} =
+  ## Returns the number of elements in `s`.
+  runnableExamples:
+    let a = [1, 3, 5].toPackedSet
+    assert len(a) == 3
+
+  if s.elems < s.a.len:
+    result = s.elems
+  else:
+    result = 0
+    for _ in s.items:
+      # pending bug #11167; when fixed, check each explicit `items` to see if it can be removed
+      inc(result)
+
+proc missingOrExcl*[A](s: var PackedSet[A], key: A): bool =
+  ## Excludes `key` from the set `s` and tells if `key` was already missing from `s`.
+  ##
+  ## The difference with regards to the `excl proc <#excl,PackedSet[A],A>`_ is
+  ## that this proc returns true if `key` was missing from `s`.
+  ## The proc will return false if `key` was in `s` and it was removed
+  ## during this call.
+  ##
+  ## **See also:**
+  ## * `excl proc <#excl,PackedSet[A],A>`_ for excluding an element
+  ## * `excl proc <#excl,PackedSet[A],PackedSet[A]>`_ for excluding a set
+  ## * `containsOrIncl proc <#containsOrIncl,PackedSet[A],A>`_
+  runnableExamples:
+    var a = [5].toPackedSet
+    assert a.missingOrExcl(5) == false
+    assert a.missingOrExcl(5) == true
+
+  var count = s.len
+  exclImpl(s, ord(key))
+  result = count == s.len
+
+proc clear*[A](result: var PackedSet[A]) =
+  ## Clears the `PackedSet[A]` back to an empty state.
+  runnableExamples:
+    var a = [5, 7].toPackedSet
+    clear(a)
+    assert len(a) == 0
+
+  # setLen(result.data, InitIntSetSize)
+  # for i in 0..InitIntSetSize - 1: result.data[i] = nil
+  # result.max = InitIntSetSize - 1
+  result.data = @[]
+  result.max = 0
+  result.counter = 0
+  result.head = nil
+  result.elems = 0
+
+proc isNil*[A](x: PackedSet[A]): bool {.inline.} =
+  ## Returns true if `x` is empty, false otherwise.
+  runnableExamples:
+    var a = initPackedSet[int]()
+    assert a.isNil
+    a.incl(2)
+    assert not a.isNil
+    a.excl(2)
+    assert a.isNil
+
+  x.head.isNil and x.elems == 0
+
+proc `=copy`*[A](dest: var PackedSet[A], src: PackedSet[A]) =
+  ## Copies `src` to `dest`.
+  ## `dest` does not need to be initialized by the `initPackedSet proc <#initPackedSet>`_.
+  if src.elems <= src.a.len:
+    dest.data = @[]
+    dest.max = 0
+    dest.counter = src.counter
+    dest.head = nil
+    dest.elems = src.elems
+    dest.a = src.a
+  else:
+    dest.counter = src.counter
+    dest.max = src.max
+    dest.elems = src.elems
+    newSeq(dest.data, src.data.len)
+
+    var it = src.head
+    while it != nil:
+      var h = it.key and dest.max
+      var perturb = it.key
+      while dest.data[h] != nil: h = nextTry(h, dest.max, perturb)
+      assert dest.data[h] == nil
+      var n: Trunk
+      new(n)
+      n.next = dest.head
+      n.key = it.key
+      n.bits = it.bits
+      dest.head = n
+      dest.data[h] = n
+      it = it.next
+
+proc assign*[A](dest: var PackedSet[A], src: PackedSet[A]) {.inline, deprecated.} =
+  ## Copies `src` to `dest`.
+  ## `dest` does not need to be initialized by the `initPackedSet proc <#initPackedSet>`_.
+  runnableExamples:
+    var
+      a = initPackedSet[int]()
+      b = initPackedSet[int]()
+    b.incl(5)
+    b.incl(7)
+    a.assign(b)
+    assert len(a) == 2
+  `=copy`(dest, src)
+
+proc union*[A](s1, s2: PackedSet[A]): PackedSet[A] =
+  ## Returns the union of the sets `s1` and `s2`.
+  ##
+  ## The same as `s1 + s2 <#+,PackedSet[A],PackedSet[A]>`_.
+  runnableExamples:
+    let
+      a = [1, 2, 3].toPackedSet
+      b = [3, 4, 5].toPackedSet
+      c = union(a, b)
+    assert c.len == 5
+    assert c == [1, 2, 3, 4, 5].toPackedSet
+
+  result.assign(s1)
+  incl(result, s2)
+
+proc intersection*[A](s1, s2: PackedSet[A]): PackedSet[A] =
+  ## Returns the intersection of the sets `s1` and `s2`.
+  ##
+  ## The same as `s1 * s2 <#*,PackedSet[A],PackedSet[A]>`_.
+  runnableExamples:
+    let
+      a = [1, 2, 3].toPackedSet
+      b = [3, 4, 5].toPackedSet
+      c = intersection(a, b)
+    assert c.len == 1
+    assert c == [3].toPackedSet
+
+  result = initPackedSet[A]()
+  for item in s1.items:
+    if contains(s2, item):
+      incl(result, item)
+
+proc difference*[A](s1, s2: PackedSet[A]): PackedSet[A] =
+  ## Returns the difference of the sets `s1` and `s2`.
+  ##
+  ## The same as `s1 - s2 <#-,PackedSet[A],PackedSet[A]>`_.
+  runnableExamples:
+    let
+      a = [1, 2, 3].toPackedSet
+      b = [3, 4, 5].toPackedSet
+      c = difference(a, b)
+    assert c.len == 2
+    assert c == [1, 2].toPackedSet
+
+  result = initPackedSet[A]()
+  for item in s1.items:
+    if not contains(s2, item):
+      incl(result, item)
+
+proc symmetricDifference*[A](s1, s2: PackedSet[A]): PackedSet[A] =
+  ## Returns the symmetric difference of the sets `s1` and `s2`.
+  runnableExamples:
+    let
+      a = [1, 2, 3].toPackedSet
+      b = [3, 4, 5].toPackedSet
+      c = symmetricDifference(a, b)
+    assert c.len == 4
+    assert c == [1, 2, 4, 5].toPackedSet
+
+  result.assign(s1)
+  for item in s2.items:
+    if containsOrIncl(result, item):
+      excl(result, item)
+
+proc `+`*[A](s1, s2: PackedSet[A]): PackedSet[A] {.inline.} =
+  ## Alias for `union(s1, s2) <#union,PackedSet[A],PackedSet[A]>`_.
+  result = union(s1, s2)
+
+proc `*`*[A](s1, s2: PackedSet[A]): PackedSet[A] {.inline.} =
+  ## Alias for `intersection(s1, s2) <#intersection,PackedSet[A],PackedSet[A]>`_.
+  result = intersection(s1, s2)
+
+proc `-`*[A](s1, s2: PackedSet[A]): PackedSet[A] {.inline.} =
+  ## Alias for `difference(s1, s2) <#difference,PackedSet[A],PackedSet[A]>`_.
+  result = difference(s1, s2)
+
+proc disjoint*[A](s1, s2: PackedSet[A]): bool =
+  ## Returns true if the sets `s1` and `s2` have no items in common.
+  runnableExamples:
+    let
+      a = [1, 2].toPackedSet
+      b = [2, 3].toPackedSet
+      c = [3, 4].toPackedSet
+    assert disjoint(a, b) == false
+    assert disjoint(a, c) == true
+
+  for item in s1.items:
+    if contains(s2, item):
+      return false
+  return true
+
+proc card*[A](s: PackedSet[A]): int {.inline.} =
+  ## Alias for `len() <#len,PackedSet[A]>`_.
+  ##
+  ## Card stands for the [cardinality](http://en.wikipedia.org/wiki/Cardinality)
+  ## of a set.
+  result = s.len()
+
+proc `<=`*[A](s1, s2: PackedSet[A]): bool =
+  ## Returns true if `s1` is a subset of `s2`.
+  ##
+  ## A subset `s1` has all of its elements in `s2`, but `s2` doesn't necessarily
+  ## have more elements than `s1`. That is, `s1` can be equal to `s2`.
+  runnableExamples:
+    let
+      a = [1].toPackedSet
+      b = [1, 2].toPackedSet
+      c = [1, 3].toPackedSet
+    assert a <= b
+    assert b <= b
+    assert not (c <= b)
+
+  for item in s1.items:
+    if not s2.contains(item):
+      return false
+  return true
+
+proc `<`*[A](s1, s2: PackedSet[A]): bool =
+  ## Returns true if `s1` is a proper subset of `s2`.
+  ##
+  ## A strict or proper subset `s1` has all of its elements in `s2`, but `s2` has
+  ## more elements than `s1`.
+  runnableExamples:
+    let
+      a = [1].toPackedSet
+      b = [1, 2].toPackedSet
+      c = [1, 3].toPackedSet
+    assert a < b
+    assert not (b < b)
+    assert not (c < b)
+
+  return s1 <= s2 and not (s2 <= s1)
+
+proc `==`*[A](s1, s2: PackedSet[A]): bool =
+  ## Returns true if both `s1` and `s2` have the same elements and set size.
+  runnableExamples:
+    assert [1, 2].toPackedSet == [2, 1].toPackedSet
+    assert [1, 2].toPackedSet == [2, 1, 2].toPackedSet
+
+  return s1 <= s2 and s2 <= s1
+
+proc `$`*[A](s: PackedSet[A]): string =
+  ## Converts `s` to a string.
+  runnableExamples:
+    let a = [1, 2, 3].toPackedSet
+    assert $a == "{1, 2, 3}"
+
+  dollarImpl()
diff --git a/lib/std/paths.nim b/lib/std/paths.nim
new file mode 100644
index 000000000..664dedd31
--- /dev/null
+++ b/lib/std/paths.nim
@@ -0,0 +1,302 @@
+## This module implements path handling.
+##
+## **See also:**
+## * `files module <files.html>`_ for file access
+
+import std/private/osseps
+export osseps
+
+import std/envvars
+import std/private/osappdirs
+
+import std/[pathnorm, hashes, sugar, strutils]
+
+from std/private/ospaths2 import  joinPath, splitPath,
+                                  ReadDirEffect, WriteDirEffect,
+                                  isAbsolute, relativePath,
+                                  normalizePathEnd, isRelativeTo, parentDir,
+                                  tailDir, isRootDir, parentDirs, `/../`,
+                                  extractFilename, lastPathPart,
+                                  changeFileExt, addFileExt, cmpPaths, splitFile,
+                                  unixToNativePath, absolutePath, normalizeExe,
+                                  normalizePath
+export ReadDirEffect, WriteDirEffect
+
+type
+  Path* = distinct string
+
+func hash*(x: Path): Hash =
+  let x = x.string.dup(normalizePath)
+  if FileSystemCaseSensitive:
+    result = x.hash
+  else:
+    result = x.toLowerAscii.hash
+
+template `$`*(x: Path): string =
+  string(x)
+
+func `==`*(x, y: Path): bool {.inline.} =
+  ## Compares two paths.
+  ##
+  ## On a case-sensitive filesystem this is done
+  ## case-sensitively otherwise case-insensitively.
+  result = cmpPaths(x.string, y.string) == 0
+
+template endsWith(a: string, b: set[char]): bool =
+  a.len > 0 and a[^1] in b
+
+func add(x: var string, tail: string) =
+  var state = 0
+  let trailingSep = tail.endsWith({DirSep, AltSep}) or tail.len == 0 and x.endsWith({DirSep, AltSep})
+  normalizePathEnd(x, trailingSep=false)
+  addNormalizePath(tail, x, state, DirSep)
+  normalizePathEnd(x, trailingSep=trailingSep)
+
+func add*(x: var Path, y: Path) {.borrow.}
+
+func `/`*(head, tail: Path): Path {.inline.} =
+  ## Joins two directory names to one.
+  ##
+  ## returns normalized path concatenation of `head` and `tail`, preserving
+  ## whether or not `tail` has a trailing slash (or, if tail if empty, whether
+  ## head has one).
+  ##
+  ## See also:
+  ## * `splitPath proc`_
+  ## * `uri.combine proc <uri.html#combine,Uri,Uri>`_
+  ## * `uri./ proc <uri.html#/,Uri,string>`_
+  Path(joinPath(head.string, tail.string))
+
+func splitPath*(path: Path): tuple[head, tail: Path] {.inline.} =
+  ## Splits a directory into `(head, tail)` tuple, so that
+  ## ``head / tail == path`` (except for edge cases like "/usr").
+  ##
+  ## See also:
+  ## * `add proc`_
+  ## * `/ proc`_
+  ## * `/../ proc`_
+  ## * `relativePath proc`_
+  let res = splitPath(path.string)
+  result = (Path(res.head), Path(res.tail))
+
+func splitFile*(path: Path): tuple[dir, name: Path, ext: string] {.inline.} =
+  ## Splits a filename into `(dir, name, extension)` tuple.
+  ##
+  ## `dir` does not end in DirSep unless it's `/`.
+  ## `extension` includes the leading dot.
+  ##
+  ## 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.
+  ##
+  ## See also:
+  ## * `extractFilename proc`_
+  ## * `lastPathPart proc`_
+  ## * `changeFileExt proc`_
+  ## * `addFileExt proc`_
+  let res = splitFile(path.string)
+  result = (Path(res.dir), Path(res.name), res.ext)
+
+func isAbsolute*(path: Path): bool {.inline, raises: [].} =
+  ## Checks whether a given `path` is absolute.
+  ##
+  ## On Windows, network paths are considered absolute too.
+  result = isAbsolute(path.string)
+
+proc relativePath*(path, base: Path, sep = DirSep): Path {.inline.} =
+  ## Converts `path` to a path relative to `base`.
+  ##
+  ## The `sep` (default: DirSep) is used for the path normalizations,
+  ## this can be useful to ensure the relative path only contains `'/'`
+  ## so that it can be used for URL constructions.
+  ##
+  ## On Windows, if a root of `path` and a root of `base` are different,
+  ## returns `path` as is because it is impossible to make a relative path.
+  ## That means an absolute path can be returned.
+  ##
+  ## See also:
+  ## * `splitPath proc`_
+  ## * `parentDir proc`_
+  ## * `tailDir proc`_
+  result = Path(relativePath(path.string, base.string, sep))
+
+proc isRelativeTo*(path: Path, base: Path): bool {.inline.} =
+  ## Returns true if `path` is relative to `base`.
+  result = isRelativeTo(path.string, base.string)
+
+
+func parentDir*(path: Path): Path {.inline.} =
+  ## Returns the parent directory of `path`.
+  ##
+  ## This is similar to ``splitPath(path).head`` when ``path`` doesn't end
+  ## in a dir separator, but also takes care of path normalizations.
+  ## The remainder can be obtained with `lastPathPart(path) proc`_.
+  ##
+  ## See also:
+  ## * `relativePath proc`_
+  ## * `splitPath proc`_
+  ## * `tailDir proc`_
+  ## * `parentDirs iterator`_
+  result = Path(parentDir(path.string))
+
+func tailDir*(path: Path): Path {.inline.} =
+  ## Returns the tail part of `path`.
+  ##
+  ## See also:
+  ## * `relativePath proc`_
+  ## * `splitPath proc`_
+  ## * `parentDir proc`_
+  result = Path(tailDir(path.string))
+
+func isRootDir*(path: Path): bool {.inline.} =
+  ## Checks whether a given `path` is a root directory.
+  result = isRootDir(path.string)
+
+iterator parentDirs*(path: Path, fromRoot=false, inclusive=true): Path =
+  ## Walks over all parent directories of a given `path`.
+  ##
+  ## If `fromRoot` is true (default: false), the traversal will start from
+  ## the file system root directory.
+  ## If `inclusive` is true (default), the original argument will be included
+  ## in the traversal.
+  ##
+  ## Relative paths won't be expanded by this iterator. Instead, it will traverse
+  ## only the directories appearing in the relative path.
+  ##
+  ## See also:
+  ## * `parentDir proc`_
+  ##
+  for p in parentDirs(path.string, fromRoot, inclusive):
+    yield Path(p)
+
+func `/../`*(head, tail: Path): Path {.inline.} =
+  ## The same as ``parentDir(head) / tail``, unless there is no parent
+  ## directory. Then ``head / tail`` is performed instead.
+  ##
+  ## See also:
+  ## * `/ proc`_
+  ## * `parentDir proc`_
+  Path(`/../`(head.string, tail.string))
+
+func extractFilename*(path: Path): Path {.inline.} =
+  ## Extracts the filename of a given `path`.
+  ##
+  ## This is the same as ``name & ext`` from `splitFile(path) proc`_.
+  ##
+  ## See also:
+  ## * `splitFile proc`_
+  ## * `lastPathPart proc`_
+  ## * `changeFileExt proc`_
+  ## * `addFileExt proc`_
+  result = Path(extractFilename(path.string))
+
+func lastPathPart*(path: Path): Path {.inline.} =
+  ## Like `extractFilename proc`_, but ignores
+  ## trailing dir separator; aka: `baseName`:idx: in some other languages.
+  ##
+  ## See also:
+  ## * `splitFile proc`_
+  ## * `extractFilename proc`_
+  ## * `changeFileExt proc`_
+  ## * `addFileExt proc`_
+  result = Path(lastPathPart(path.string))
+
+func changeFileExt*(filename: Path, ext: string): Path {.inline.} =
+  ## Changes the file extension to `ext`.
+  ##
+  ## If the `filename` has no extension, `ext` will be added.
+  ## If `ext` == "" then any extension is removed.
+  ##
+  ## `Ext` should be given without the leading `'.'`, because some
+  ## filesystems may use a different character. (Although I know
+  ## of none such beast.)
+  ##
+  ## See also:
+  ## * `splitFile proc`_
+  ## * `extractFilename proc`_
+  ## * `lastPathPart proc`_
+  ## * `addFileExt proc`_
+  result = Path(changeFileExt(filename.string, ext))
+
+func addFileExt*(filename: Path, ext: string): Path {.inline.} =
+  ## Adds the file extension `ext` to `filename`, unless
+  ## `filename` already has an extension.
+  ##
+  ## `Ext` should be given without the leading `'.'`, because some
+  ## filesystems may use a different character.
+  ## (Although I know of none such beast.)
+  ##
+  ## See also:
+  ## * `splitFile proc`_
+  ## * `extractFilename proc`_
+  ## * `lastPathPart proc`_
+  ## * `changeFileExt proc`_
+  result = Path(addFileExt(filename.string, ext))
+
+func unixToNativePath*(path: Path, drive=Path("")): Path {.inline.} =
+  ## Converts an UNIX-like path to a native one.
+  ##
+  ## On an UNIX system this does nothing. Else it converts
+  ## `'/'`, `'.'`, `'..'` to the appropriate things.
+  ##
+  ## On systems with a concept of "drives", `drive` is used to determine
+  ## which drive label to use during absolute path conversion.
+  ## `drive` defaults to the drive of the current working directory, and is
+  ## ignored on systems that do not have a concept of "drives".
+  result = Path(unixToNativePath(path.string, drive.string))
+
+proc getCurrentDir*(): Path {.inline, tags: [].} =
+  ## Returns the `current working directory`:idx: i.e. where the built
+  ## binary is run.
+  ##
+  ## So the path returned by this proc is determined at run time.
+  ##
+  ## See also:
+  ## * `getHomeDir proc <appdirs.html#getHomeDir>`_
+  ## * `getConfigDir proc <appdirs.html#getConfigDir>`_
+  ## * `getTempDir proc <appdirs.html#getTempDir>`_
+  ## * `setCurrentDir proc <dirs.html#setCurrentDir>`_
+  ## * `currentSourcePath template <system.html#currentSourcePath.t>`_
+  ## * `getProjectPath proc <macros.html#getProjectPath>`_
+  result = Path(ospaths2.getCurrentDir())
+
+proc normalizeExe*(file: var Path) {.borrow.}
+
+proc normalizePath*(path: var Path) {.borrow.}
+
+proc normalizePathEnd*(path: var Path, trailingSep = false) {.borrow.}
+
+proc absolutePath*(path: Path, root = getCurrentDir()): Path =
+  ## Returns the absolute path of `path`, rooted at `root` (which must be absolute;
+  ## default: current directory).
+  ## If `path` is absolute, return it, ignoring `root`.
+  ##
+  ## See also:
+  ## * `normalizePath proc`_
+  result = Path(absolutePath(path.string, root.string))
+
+proc expandTildeImpl(path: string): string {.
+  tags: [ReadEnvEffect, ReadIOEffect].} =
+  if len(path) == 0 or path[0] != '~':
+    result = path
+  elif len(path) == 1:
+    result = getHomeDir()
+  elif (path[1] in {DirSep, AltSep}):
+    result = joinPath(getHomeDir(), path.substr(2))
+  else:
+    # TODO: handle `~bob` and `~bob/` which means home of bob
+    result = path
+
+proc expandTilde*(path: Path): Path {.inline,
+  tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Expands ``~`` or a path starting with ``~/`` to a full path, replacing
+  ## ``~`` with `getHomeDir() <appdirs.html#getHomeDir>`_ (otherwise returns ``path`` unmodified).
+  ##
+  ## Windows: this is still supported despite the Windows platform not having this
+  ## convention; also, both ``~/`` and ``~\`` are handled.
+  runnableExamples:
+    import std/appdirs
+    assert expandTilde(Path("~") / Path("appname.cfg")) == getHomeDir() / Path("appname.cfg")
+    assert expandTilde(Path("~/foo/bar")) == getHomeDir() / Path("foo/bar")
+    assert expandTilde(Path("/foo/bar")) == Path("/foo/bar")
+  result = Path(expandTildeImpl(path.string))
diff --git a/lib/std/private/asciitables.nim b/lib/std/private/asciitables.nim
new file mode 100644
index 000000000..cbc595651
--- /dev/null
+++ b/lib/std/private/asciitables.nim
@@ -0,0 +1,83 @@
+#[
+move to std/asciitables.nim once stable, or to a fusion package
+once compiler can depend on fusion
+]#
+
+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
+  ## delimited 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/lib/std/private/bitops_utils.nim b/lib/std/private/bitops_utils.nim
new file mode 100644
index 000000000..0b9484416
--- /dev/null
+++ b/lib/std/private/bitops_utils.nim
@@ -0,0 +1,22 @@
+template forwardImpl*(impl, arg) {.dirty.} =
+  when sizeof(x) <= 4:
+    when x is SomeSignedInt:
+      impl(cast[uint32](x.int32))
+    else:
+      impl(x.uint32)
+  else:
+    when x is SomeSignedInt:
+      impl(cast[uint64](x.int64))
+    else:
+      impl(x.uint64)
+
+# this could also be implemented via:
+# import std/typetraits
+# template castToUnsigned*(x: SomeInteger): auto = cast[toUnsigned(typeof(x))](x)
+
+template castToUnsigned*(x: int8): uint8 = cast[uint8](x)
+template castToUnsigned*(x: int16): uint16 = cast[uint16](x)
+template castToUnsigned*(x: int32): uint32 = cast[uint32](x)
+template castToUnsigned*(x: int64): uint64 = cast[uint64](x)
+template castToUnsigned*(x: int): uint = cast[uint](x)
+template castToUnsigned*[T: SomeUnsignedInt](x: T): T = x
diff --git a/lib/std/private/decode_helpers.nim b/lib/std/private/decode_helpers.nim
new file mode 100644
index 000000000..f11e3060a
--- /dev/null
+++ b/lib/std/private/decode_helpers.nim
@@ -0,0 +1,42 @@
+proc handleHexChar*(c: char, x: var int): bool {.inline.} =
+  ## Converts `%xx` hexadecimal to the ordinal number and adds the result to `x`.
+  ## Returns `true` if `c` is hexadecimal.
+  ##
+  ## When `c` is hexadecimal, the proc is equal to `x = x shl 4 + hex2Int(c)`.
+  runnableExamples:
+    var x = 0
+    assert handleHexChar('a', x)
+    assert x == 10
+
+    assert handleHexChar('B', x)
+    assert x == 171 # 10 shl 4 + 11
+
+    assert not handleHexChar('?', x)
+    assert x == 171 # unchanged
+  result = true
+  case c
+  of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
+  of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
+  of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
+  else:
+    result = false
+
+proc handleHexChar*(c: char): int {.inline.} =
+  case c
+  of '0'..'9': result = (ord(c) - ord('0'))
+  of 'a'..'f': result = (ord(c) - ord('a') + 10)
+  of 'A'..'F': result = (ord(c) - ord('A') + 10)
+  else: discard
+
+proc decodePercent*(s: openArray[char], i: var int): char =
+  ## Converts `%xx` hexadecimal to the character with ordinal number `xx`.
+  ##
+  ## If `xx` is not a valid hexadecimal value, it is left intact: only the
+  ## leading `%` is returned as-is, and `xx` characters will be processed in the
+  ## next step (e.g. in `uri.decodeUrl`) as regular characters.
+  result = '%'
+  if i+2 < s.len:
+    var x = 0
+    if handleHexChar(s[i+1], x) and handleHexChar(s[i+2], x):
+      result = chr(x)
+      inc(i, 2)
diff --git a/lib/std/private/digitsutils.nim b/lib/std/private/digitsutils.nim
new file mode 100644
index 000000000..f2d0d25cb
--- /dev/null
+++ b/lib/std/private/digitsutils.nim
@@ -0,0 +1,116 @@
+const
+  trailingZeros100: array[100, int8] = [2'i8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
+    0, 0, 0, 0, 0, 0]
+
+  digits100: array[200, char] = ['0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5',
+    '0', '6', '0', '7', '0', '8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3', '1', '4',
+    '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0', '2', '1', '2', '2', '2', '3',
+    '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', '3', '0', '3', '1', '3', '2',
+    '3', '3', '3', '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '4', '0', '4', '1',
+    '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', '5', '0',
+    '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9',
+    '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8',
+    '6', '9', '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7',
+    '7', '8', '7', '9', '8', '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '5', '8', '6',
+    '8', '7', '8', '8', '8', '9', '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5',
+    '9', '6', '9', '7', '9', '8', '9', '9']
+
+# Inspired by https://engineering.fb.com/2013/03/15/developer-tools/three-optimization-tips-for-c
+# Generates:
+#   ```nim
+#   var res = ""
+#   for i in 0 .. 99:
+#     if i < 10:
+#       res.add "0" & $i
+#     else:
+#       res.add $i
+#   doAssert res == digits100
+#   ```
+
+proc utoa2Digits*(buf: var openArray[char]; pos: int; digits: uint32) {.inline.} =
+  buf[pos] = digits100[2 * digits]
+  buf[pos+1] = digits100[2 * digits + 1]
+  #copyMem(buf, unsafeAddr(digits100[2 * digits]), 2 * sizeof((char)))
+
+proc trailingZeros2Digits*(digits: uint32): int {.inline.} =
+  trailingZeros100[digits]
+
+when defined(js):
+  proc numToString(a: SomeInteger): cstring {.importjs: "((#) + \"\")".}
+
+func addChars[T](result: var string, x: T, start: int, n: int) {.inline.} =
+  let old = result.len
+  result.setLen old + n
+  template impl =
+    for i in 0..<n: result[old + i] = x[start + i]
+  when nimvm: impl
+  else:
+    when defined(js) or defined(nimscript): impl
+    else:
+      {.noSideEffect.}:
+        copyMem result[old].addr, x[start].unsafeAddr, n
+
+func addChars[T](result: var string, x: T) {.inline.} =
+  addChars(result, x, 0, x.len)
+
+func addIntImpl(result: var string, x: uint64) {.inline.} =
+  var tmp {.noinit.}: array[24, char]
+  var num = x
+  var next = tmp.len - 1
+  const nbatch = 100
+
+  while num >= nbatch:
+    let originNum = num
+    num = num div nbatch
+    let index = int16((originNum - num * nbatch) shl 1)
+    tmp[next] = digits100[index + 1]
+    tmp[next - 1] = digits100[index]
+    dec(next, 2)
+
+  # process last 1-2 digits
+  if num < 10:
+    tmp[next] = chr(ord('0') + num.uint8)
+  else:
+    let index = num * 2
+    tmp[next] = digits100[index + 1]
+    tmp[next - 1] = digits100[index]
+    dec next
+  addChars(result, tmp, next, tmp.len - next)
+
+when not defined(nimHasEnforceNoRaises):
+  {.pragma: enforceNoRaises.}
+
+func addInt*(result: var string, x: uint64) {.enforceNoRaises.} =
+  when nimvm: addIntImpl(result, x)
+  else:
+    when not defined(js): addIntImpl(result, x)
+    else:
+      addChars(result, numToString(x))
+
+proc addInt*(result: var string; x: int64) {.enforceNoRaises.} =
+  ## Converts integer to its string representation and appends it to `result`.
+  runnableExamples:
+    var s = "foo"
+    s.addInt(45)
+    assert s == "foo45"
+  template impl =
+    var num: uint64
+    if x < 0:
+      if x == low(int64):
+        num = cast[uint64](x)
+      else:
+        num = uint64(-x)
+      result.add '-'
+    else:
+      num = uint64(x)
+    addInt(result, num)
+  when nimvm: impl()
+  else:
+    when defined(js):
+      addChars(result, numToString(x))
+    else: impl()
+
+proc addInt*(result: var string; x: int) {.inline, enforceNoRaises.} =
+  addInt(result, int64(x))
diff --git a/lib/std/private/dragonbox.nim b/lib/std/private/dragonbox.nim
new file mode 100644
index 000000000..85ffea84a
--- /dev/null
+++ b/lib/std/private/dragonbox.nim
@@ -0,0 +1,1325 @@
+##  Copyright 2020 Junekey Jeon
+##  Copyright 2020 Alexander Bolz
+##
+##  Distributed under the Boost Software License, Version 1.0.
+##   (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
+
+##  char* output_end = Dtoa(buffer, value);
+##
+##  Converts the given double-precision number into decimal form and stores the result in the given
+##  buffer.
+##
+##  The buffer must be large enough, i.e. >= DtoaMinBufferLength.
+##  The output format is similar to printf("%g").
+##  The output is _not_ null-terminted.
+##
+##  The output is optimal, i.e. the output string
+##   1. rounds back to the input number when read in (using round-to-nearest-even)
+##   2. is as short as possible,
+##   3. is as close to the input number as possible.
+##
+##  Note:
+##  This function may temporarily write up to DtoaMinBufferLength characters into the buffer.
+
+
+import std/private/digitsutils
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+const
+  dtoaMinBufferLength*: cint = 64
+
+##  This file contains an implementation of Junekey Jeon's Dragonbox algorithm.
+##
+##  It is a simplified version of the reference implementation found here:
+##  https://github.com/jk-jeon/dragonbox
+##
+##  The reference implementation also works with single-precision floating-point numbers and
+##  has options to configure the rounding mode.
+
+template dragonbox_Assert*(x: untyped): untyped =
+  assert(x)
+
+# ==================================================================================================
+#
+# ==================================================================================================
+
+type
+  ValueType* = float
+  BitsType* = uint64
+
+type
+  Double* = object
+    bits*: BitsType
+
+const                         ##  = p   (includes the hidden bit)
+  significandSize*: int32 = 53
+
+const ##   static constexpr int32_t   MaxExponent     = 1024 - 1 - (SignificandSize - 1);
+     ##   static constexpr int32_t   MinExponent     = std::numeric_limits<value_type>::min_exponent - 1 - (SignificandSize - 1);
+  exponentBias*: int32 = 1024 - 1 + (significandSize - 1)
+
+const
+  maxIeeeExponent*: BitsType = BitsType(2 * 1024 - 1)
+
+const                         ##  = 2^(p-1)
+  hiddenBit*: BitsType = BitsType(1) shl (significandSize - 1)
+
+const                         ##  = 2^(p-1) - 1
+  significandMask*: BitsType = hiddenBit - 1
+
+const
+  exponentMask*: BitsType = maxIeeeExponent shl (significandSize - 1)
+
+const
+  signMask*: BitsType = not (not BitsType(0) shr 1)
+
+proc constructDouble*(bits: BitsType): Double  =
+  result.bits = bits
+
+proc constructDouble*(value: ValueType): Double  =
+  result.bits = cast[typeof(result.bits)](value)
+
+proc physicalSignificand*(this: Double): BitsType {.noSideEffect.} =
+  return this.bits and significandMask
+
+proc physicalExponent*(this: Double): BitsType {.noSideEffect.} =
+  return (this.bits and exponentMask) shr (significandSize - 1)
+
+proc isFinite*(this: Double): bool {.noSideEffect.} =
+  return (this.bits and exponentMask) != exponentMask
+
+proc isInf*(this: Double): bool {.noSideEffect.} =
+  return (this.bits and exponentMask) == exponentMask and
+      (this.bits and significandMask) == 0
+
+proc isNaN*(this: Double): bool {.noSideEffect.} =
+  return (this.bits and exponentMask) == exponentMask and
+      (this.bits and significandMask) != 0
+
+proc isZero*(this: Double): bool {.noSideEffect.} =
+  return (this.bits and not signMask) == 0
+
+proc signBit*(this: Double): int {.noSideEffect.} =
+  return ord((this.bits and signMask) != 0)
+
+
+# ==================================================================================================
+#
+# ==================================================================================================
+##  namespace
+##  Returns floor(x / 2^n).
+##
+##  Technically, right-shift of negative integers is implementation defined...
+##  Should easily be optimized into SAR (or equivalent) instruction.
+
+proc floorDivPow2*(x: int32; n: int32): int32 {.inline.} =
+  return x shr n
+
+proc floorLog2Pow10*(e: int32): int32 {.inline.} =
+  dragonbox_Assert(e >= -1233)
+  dragonbox_Assert(e <= 1233)
+  return floorDivPow2(e * 1741647, 19)
+
+proc floorLog10Pow2*(e: int32): int32 {.inline.} =
+  dragonbox_Assert(e >= -1500)
+  dragonbox_Assert(e <= 1500)
+  return floorDivPow2(e * 1262611, 22)
+
+proc floorLog10ThreeQuartersPow2*(e: int32): int32 {.inline.} =
+  dragonbox_Assert(e >= -1500)
+  dragonbox_Assert(e <= 1500)
+  return floorDivPow2(e * 1262611 - 524031, 22)
+
+# ==================================================================================================
+#
+# ==================================================================================================
+
+type
+  uint64x2* {.bycopy.} = object
+    hi*: uint64
+    lo*: uint64
+
+
+proc computePow10*(k: int32): uint64x2 {.inline.} =
+  const
+    kMin: int32 = -292
+  const
+    kMax: int32 = 326
+  const
+    pow10: array[kMax - kMin + 1, uint64x2] = [
+      uint64x2(hi: 0xFF77B1FCBEBCDC4F'u, lo: 0x25E8E89C13BB0F7B'u),
+      uint64x2(hi: 0x9FAACF3DF73609B1'u, lo: 0x77B191618C54E9AD'u),
+      uint64x2(hi: 0xC795830D75038C1D'u, lo: 0xD59DF5B9EF6A2418'u),
+      uint64x2(hi: 0xF97AE3D0D2446F25'u, lo: 0x4B0573286B44AD1E'u),
+      uint64x2(hi: 0x9BECCE62836AC577'u, lo: 0x4EE367F9430AEC33'u),
+      uint64x2(hi: 0xC2E801FB244576D5'u, lo: 0x229C41F793CDA740'u),
+      uint64x2(hi: 0xF3A20279ED56D48A'u, lo: 0x6B43527578C11110'u),
+      uint64x2(hi: 0x9845418C345644D6'u, lo: 0x830A13896B78AAAA'u),
+      uint64x2(hi: 0xBE5691EF416BD60C'u, lo: 0x23CC986BC656D554'u),
+      uint64x2(hi: 0xEDEC366B11C6CB8F'u, lo: 0x2CBFBE86B7EC8AA9'u),
+      uint64x2(hi: 0x94B3A202EB1C3F39'u, lo: 0x7BF7D71432F3D6AA'u),
+      uint64x2(hi: 0xB9E08A83A5E34F07'u, lo: 0xDAF5CCD93FB0CC54'u),
+      uint64x2(hi: 0xE858AD248F5C22C9'u, lo: 0xD1B3400F8F9CFF69'u),
+      uint64x2(hi: 0x91376C36D99995BE'u, lo: 0x23100809B9C21FA2'u),
+      uint64x2(hi: 0xB58547448FFFFB2D'u, lo: 0xABD40A0C2832A78B'u),
+      uint64x2(hi: 0xE2E69915B3FFF9F9'u, lo: 0x16C90C8F323F516D'u),
+      uint64x2(hi: 0x8DD01FAD907FFC3B'u, lo: 0xAE3DA7D97F6792E4'u),
+      uint64x2(hi: 0xB1442798F49FFB4A'u, lo: 0x99CD11CFDF41779D'u),
+      uint64x2(hi: 0xDD95317F31C7FA1D'u, lo: 0x40405643D711D584'u),
+      uint64x2(hi: 0x8A7D3EEF7F1CFC52'u, lo: 0x482835EA666B2573'u),
+      uint64x2(hi: 0xAD1C8EAB5EE43B66'u, lo: 0xDA3243650005EED0'u),
+      uint64x2(hi: 0xD863B256369D4A40'u, lo: 0x90BED43E40076A83'u),
+      uint64x2(hi: 0x873E4F75E2224E68'u, lo: 0x5A7744A6E804A292'u),
+      uint64x2(hi: 0xA90DE3535AAAE202'u, lo: 0x711515D0A205CB37'u),
+      uint64x2(hi: 0xD3515C2831559A83'u, lo: 0x0D5A5B44CA873E04'u),
+      uint64x2(hi: 0x8412D9991ED58091'u, lo: 0xE858790AFE9486C3'u),
+      uint64x2(hi: 0xA5178FFF668AE0B6'u, lo: 0x626E974DBE39A873'u),
+      uint64x2(hi: 0xCE5D73FF402D98E3'u, lo: 0xFB0A3D212DC81290'u),
+      uint64x2(hi: 0x80FA687F881C7F8E'u, lo: 0x7CE66634BC9D0B9A'u),
+      uint64x2(hi: 0xA139029F6A239F72'u, lo: 0x1C1FFFC1EBC44E81'u),
+      uint64x2(hi: 0xC987434744AC874E'u, lo: 0xA327FFB266B56221'u),
+      uint64x2(hi: 0xFBE9141915D7A922'u, lo: 0x4BF1FF9F0062BAA9'u),
+      uint64x2(hi: 0x9D71AC8FADA6C9B5'u, lo: 0x6F773FC3603DB4AA'u),
+      uint64x2(hi: 0xC4CE17B399107C22'u, lo: 0xCB550FB4384D21D4'u),
+      uint64x2(hi: 0xF6019DA07F549B2B'u, lo: 0x7E2A53A146606A49'u),
+      uint64x2(hi: 0x99C102844F94E0FB'u, lo: 0x2EDA7444CBFC426E'u),
+      uint64x2(hi: 0xC0314325637A1939'u, lo: 0xFA911155FEFB5309'u),
+      uint64x2(hi: 0xF03D93EEBC589F88'u, lo: 0x793555AB7EBA27CB'u),
+      uint64x2(hi: 0x96267C7535B763B5'u, lo: 0x4BC1558B2F3458DF'u),
+      uint64x2(hi: 0xBBB01B9283253CA2'u, lo: 0x9EB1AAEDFB016F17'u),
+      uint64x2(hi: 0xEA9C227723EE8BCB'u, lo: 0x465E15A979C1CADD'u),
+      uint64x2(hi: 0x92A1958A7675175F'u, lo: 0x0BFACD89EC191ECA'u),
+      uint64x2(hi: 0xB749FAED14125D36'u, lo: 0xCEF980EC671F667C'u),
+      uint64x2(hi: 0xE51C79A85916F484'u, lo: 0x82B7E12780E7401B'u),
+      uint64x2(hi: 0x8F31CC0937AE58D2'u, lo: 0xD1B2ECB8B0908811'u),
+      uint64x2(hi: 0xB2FE3F0B8599EF07'u, lo: 0x861FA7E6DCB4AA16'u),
+      uint64x2(hi: 0xDFBDCECE67006AC9'u, lo: 0x67A791E093E1D49B'u),
+      uint64x2(hi: 0x8BD6A141006042BD'u, lo: 0xE0C8BB2C5C6D24E1'u),
+      uint64x2(hi: 0xAECC49914078536D'u, lo: 0x58FAE9F773886E19'u),
+      uint64x2(hi: 0xDA7F5BF590966848'u, lo: 0xAF39A475506A899F'u),
+      uint64x2(hi: 0x888F99797A5E012D'u, lo: 0x6D8406C952429604'u),
+      uint64x2(hi: 0xAAB37FD7D8F58178'u, lo: 0xC8E5087BA6D33B84'u),
+      uint64x2(hi: 0xD5605FCDCF32E1D6'u, lo: 0xFB1E4A9A90880A65'u),
+      uint64x2(hi: 0x855C3BE0A17FCD26'u, lo: 0x5CF2EEA09A550680'u),
+      uint64x2(hi: 0xA6B34AD8C9DFC06F'u, lo: 0xF42FAA48C0EA481F'u),
+      uint64x2(hi: 0xD0601D8EFC57B08B'u, lo: 0xF13B94DAF124DA27'u),
+      uint64x2(hi: 0x823C12795DB6CE57'u, lo: 0x76C53D08D6B70859'u),
+      uint64x2(hi: 0xA2CB1717B52481ED'u, lo: 0x54768C4B0C64CA6F'u),
+      uint64x2(hi: 0xCB7DDCDDA26DA268'u, lo: 0xA9942F5DCF7DFD0A'u),
+      uint64x2(hi: 0xFE5D54150B090B02'u, lo: 0xD3F93B35435D7C4D'u),
+      uint64x2(hi: 0x9EFA548D26E5A6E1'u, lo: 0xC47BC5014A1A6DB0'u),
+      uint64x2(hi: 0xC6B8E9B0709F109A'u, lo: 0x359AB6419CA1091C'u),
+      uint64x2(hi: 0xF867241C8CC6D4C0'u, lo: 0xC30163D203C94B63'u),
+      uint64x2(hi: 0x9B407691D7FC44F8'u, lo: 0x79E0DE63425DCF1E'u),
+      uint64x2(hi: 0xC21094364DFB5636'u, lo: 0x985915FC12F542E5'u),
+      uint64x2(hi: 0xF294B943E17A2BC4'u, lo: 0x3E6F5B7B17B2939E'u),
+      uint64x2(hi: 0x979CF3CA6CEC5B5A'u, lo: 0xA705992CEECF9C43'u),
+      uint64x2(hi: 0xBD8430BD08277231'u, lo: 0x50C6FF782A838354'u),
+      uint64x2(hi: 0xECE53CEC4A314EBD'u, lo: 0xA4F8BF5635246429'u),
+      uint64x2(hi: 0x940F4613AE5ED136'u, lo: 0x871B7795E136BE9A'u),
+      uint64x2(hi: 0xB913179899F68584'u, lo: 0x28E2557B59846E40'u),
+      uint64x2(hi: 0xE757DD7EC07426E5'u, lo: 0x331AEADA2FE589D0'u),
+      uint64x2(hi: 0x9096EA6F3848984F'u, lo: 0x3FF0D2C85DEF7622'u),
+      uint64x2(hi: 0xB4BCA50B065ABE63'u, lo: 0x0FED077A756B53AA'u),
+      uint64x2(hi: 0xE1EBCE4DC7F16DFB'u, lo: 0xD3E8495912C62895'u),
+      uint64x2(hi: 0x8D3360F09CF6E4BD'u, lo: 0x64712DD7ABBBD95D'u),
+      uint64x2(hi: 0xB080392CC4349DEC'u, lo: 0xBD8D794D96AACFB4'u),
+      uint64x2(hi: 0xDCA04777F541C567'u, lo: 0xECF0D7A0FC5583A1'u),
+      uint64x2(hi: 0x89E42CAAF9491B60'u, lo: 0xF41686C49DB57245'u),
+      uint64x2(hi: 0xAC5D37D5B79B6239'u, lo: 0x311C2875C522CED6'u),
+      uint64x2(hi: 0xD77485CB25823AC7'u, lo: 0x7D633293366B828C'u),
+      uint64x2(hi: 0x86A8D39EF77164BC'u, lo: 0xAE5DFF9C02033198'u),
+      uint64x2(hi: 0xA8530886B54DBDEB'u, lo: 0xD9F57F830283FDFD'u),
+      uint64x2(hi: 0xD267CAA862A12D66'u, lo: 0xD072DF63C324FD7C'u),
+      uint64x2(hi: 0x8380DEA93DA4BC60'u, lo: 0x4247CB9E59F71E6E'u),
+      uint64x2(hi: 0xA46116538D0DEB78'u, lo: 0x52D9BE85F074E609'u),
+      uint64x2(hi: 0xCD795BE870516656'u, lo: 0x67902E276C921F8C'u),
+      uint64x2(hi: 0x806BD9714632DFF6'u, lo: 0x00BA1CD8A3DB53B7'u),
+      uint64x2(hi: 0xA086CFCD97BF97F3'u, lo: 0x80E8A40ECCD228A5'u),
+      uint64x2(hi: 0xC8A883C0FDAF7DF0'u, lo: 0x6122CD128006B2CE'u),
+      uint64x2(hi: 0xFAD2A4B13D1B5D6C'u, lo: 0x796B805720085F82'u),
+      uint64x2(hi: 0x9CC3A6EEC6311A63'u, lo: 0xCBE3303674053BB1'u),
+      uint64x2(hi: 0xC3F490AA77BD60FC'u, lo: 0xBEDBFC4411068A9D'u),
+      uint64x2(hi: 0xF4F1B4D515ACB93B'u, lo: 0xEE92FB5515482D45'u),
+      uint64x2(hi: 0x991711052D8BF3C5'u, lo: 0x751BDD152D4D1C4B'u),
+      uint64x2(hi: 0xBF5CD54678EEF0B6'u, lo: 0xD262D45A78A0635E'u),
+      uint64x2(hi: 0xEF340A98172AACE4'u, lo: 0x86FB897116C87C35'u),
+      uint64x2(hi: 0x9580869F0E7AAC0E'u, lo: 0xD45D35E6AE3D4DA1'u),
+      uint64x2(hi: 0xBAE0A846D2195712'u, lo: 0x8974836059CCA10A'u),
+      uint64x2(hi: 0xE998D258869FACD7'u, lo: 0x2BD1A438703FC94C'u),
+      uint64x2(hi: 0x91FF83775423CC06'u, lo: 0x7B6306A34627DDD0'u),
+      uint64x2(hi: 0xB67F6455292CBF08'u, lo: 0x1A3BC84C17B1D543'u),
+      uint64x2(hi: 0xE41F3D6A7377EECA'u, lo: 0x20CABA5F1D9E4A94'u),
+      uint64x2(hi: 0x8E938662882AF53E'u, lo: 0x547EB47B7282EE9D'u),
+      uint64x2(hi: 0xB23867FB2A35B28D'u, lo: 0xE99E619A4F23AA44'u),
+      uint64x2(hi: 0xDEC681F9F4C31F31'u, lo: 0x6405FA00E2EC94D5'u),
+      uint64x2(hi: 0x8B3C113C38F9F37E'u, lo: 0xDE83BC408DD3DD05'u),
+      uint64x2(hi: 0xAE0B158B4738705E'u, lo: 0x9624AB50B148D446'u),
+      uint64x2(hi: 0xD98DDAEE19068C76'u, lo: 0x3BADD624DD9B0958'u),
+      uint64x2(hi: 0x87F8A8D4CFA417C9'u, lo: 0xE54CA5D70A80E5D7'u),
+      uint64x2(hi: 0xA9F6D30A038D1DBC'u, lo: 0x5E9FCF4CCD211F4D'u),
+      uint64x2(hi: 0xD47487CC8470652B'u, lo: 0x7647C32000696720'u),
+      uint64x2(hi: 0x84C8D4DFD2C63F3B'u, lo: 0x29ECD9F40041E074'u),
+      uint64x2(hi: 0xA5FB0A17C777CF09'u, lo: 0xF468107100525891'u),
+      uint64x2(hi: 0xCF79CC9DB955C2CC'u, lo: 0x7182148D4066EEB5'u),
+      uint64x2(hi: 0x81AC1FE293D599BF'u, lo: 0xC6F14CD848405531'u),
+      uint64x2(hi: 0xA21727DB38CB002F'u, lo: 0xB8ADA00E5A506A7D'u),
+      uint64x2(hi: 0xCA9CF1D206FDC03B'u, lo: 0xA6D90811F0E4851D'u),
+      uint64x2(hi: 0xFD442E4688BD304A'u, lo: 0x908F4A166D1DA664'u),
+      uint64x2(hi: 0x9E4A9CEC15763E2E'u, lo: 0x9A598E4E043287FF'u),
+      uint64x2(hi: 0xC5DD44271AD3CDBA'u, lo: 0x40EFF1E1853F29FE'u),
+      uint64x2(hi: 0xF7549530E188C128'u, lo: 0xD12BEE59E68EF47D'u),
+      uint64x2(hi: 0x9A94DD3E8CF578B9'u, lo: 0x82BB74F8301958CF'u),
+      uint64x2(hi: 0xC13A148E3032D6E7'u, lo: 0xE36A52363C1FAF02'u),
+      uint64x2(hi: 0xF18899B1BC3F8CA1'u, lo: 0xDC44E6C3CB279AC2'u),
+      uint64x2(hi: 0x96F5600F15A7B7E5'u, lo: 0x29AB103A5EF8C0BA'u),
+      uint64x2(hi: 0xBCB2B812DB11A5DE'u, lo: 0x7415D448F6B6F0E8'u),
+      uint64x2(hi: 0xEBDF661791D60F56'u, lo: 0x111B495B3464AD22'u),
+      uint64x2(hi: 0x936B9FCEBB25C995'u, lo: 0xCAB10DD900BEEC35'u),
+      uint64x2(hi: 0xB84687C269EF3BFB'u, lo: 0x3D5D514F40EEA743'u),
+      uint64x2(hi: 0xE65829B3046B0AFA'u, lo: 0x0CB4A5A3112A5113'u),
+      uint64x2(hi: 0x8FF71A0FE2C2E6DC'u, lo: 0x47F0E785EABA72AC'u),
+      uint64x2(hi: 0xB3F4E093DB73A093'u, lo: 0x59ED216765690F57'u),
+      uint64x2(hi: 0xE0F218B8D25088B8'u, lo: 0x306869C13EC3532D'u),
+      uint64x2(hi: 0x8C974F7383725573'u, lo: 0x1E414218C73A13FC'u),
+      uint64x2(hi: 0xAFBD2350644EEACF'u, lo: 0xE5D1929EF90898FB'u),
+      uint64x2(hi: 0xDBAC6C247D62A583'u, lo: 0xDF45F746B74ABF3A'u),
+      uint64x2(hi: 0x894BC396CE5DA772'u, lo: 0x6B8BBA8C328EB784'u),
+      uint64x2(hi: 0xAB9EB47C81F5114F'u, lo: 0x066EA92F3F326565'u),
+      uint64x2(hi: 0xD686619BA27255A2'u, lo: 0xC80A537B0EFEFEBE'u),
+      uint64x2(hi: 0x8613FD0145877585'u, lo: 0xBD06742CE95F5F37'u),
+      uint64x2(hi: 0xA798FC4196E952E7'u, lo: 0x2C48113823B73705'u),
+      uint64x2(hi: 0xD17F3B51FCA3A7A0'u, lo: 0xF75A15862CA504C6'u),
+      uint64x2(hi: 0x82EF85133DE648C4'u, lo: 0x9A984D73DBE722FC'u),
+      uint64x2(hi: 0xA3AB66580D5FDAF5'u, lo: 0xC13E60D0D2E0EBBB'u),
+      uint64x2(hi: 0xCC963FEE10B7D1B3'u, lo: 0x318DF905079926A9'u),
+      uint64x2(hi: 0xFFBBCFE994E5C61F'u, lo: 0xFDF17746497F7053'u),
+      uint64x2(hi: 0x9FD561F1FD0F9BD3'u, lo: 0xFEB6EA8BEDEFA634'u),
+      uint64x2(hi: 0xC7CABA6E7C5382C8'u, lo: 0xFE64A52EE96B8FC1'u),
+      uint64x2(hi: 0xF9BD690A1B68637B'u, lo: 0x3DFDCE7AA3C673B1'u),
+      uint64x2(hi: 0x9C1661A651213E2D'u, lo: 0x06BEA10CA65C084F'u),
+      uint64x2(hi: 0xC31BFA0FE5698DB8'u, lo: 0x486E494FCFF30A63'u),
+      uint64x2(hi: 0xF3E2F893DEC3F126'u, lo: 0x5A89DBA3C3EFCCFB'u),
+      uint64x2(hi: 0x986DDB5C6B3A76B7'u, lo: 0xF89629465A75E01D'u),
+      uint64x2(hi: 0xBE89523386091465'u, lo: 0xF6BBB397F1135824'u),
+      uint64x2(hi: 0xEE2BA6C0678B597F'u, lo: 0x746AA07DED582E2D'u),
+      uint64x2(hi: 0x94DB483840B717EF'u, lo: 0xA8C2A44EB4571CDD'u),
+      uint64x2(hi: 0xBA121A4650E4DDEB'u, lo: 0x92F34D62616CE414'u),
+      uint64x2(hi: 0xE896A0D7E51E1566'u, lo: 0x77B020BAF9C81D18'u),
+      uint64x2(hi: 0x915E2486EF32CD60'u, lo: 0x0ACE1474DC1D122F'u),
+      uint64x2(hi: 0xB5B5ADA8AAFF80B8'u, lo: 0x0D819992132456BB'u),
+      uint64x2(hi: 0xE3231912D5BF60E6'u, lo: 0x10E1FFF697ED6C6A'u),
+      uint64x2(hi: 0x8DF5EFABC5979C8F'u, lo: 0xCA8D3FFA1EF463C2'u),
+      uint64x2(hi: 0xB1736B96B6FD83B3'u, lo: 0xBD308FF8A6B17CB3'u),
+      uint64x2(hi: 0xDDD0467C64BCE4A0'u, lo: 0xAC7CB3F6D05DDBDF'u),
+      uint64x2(hi: 0x8AA22C0DBEF60EE4'u, lo: 0x6BCDF07A423AA96C'u),
+      uint64x2(hi: 0xAD4AB7112EB3929D'u, lo: 0x86C16C98D2C953C7'u),
+      uint64x2(hi: 0xD89D64D57A607744'u, lo: 0xE871C7BF077BA8B8'u),
+      uint64x2(hi: 0x87625F056C7C4A8B'u, lo: 0x11471CD764AD4973'u),
+      uint64x2(hi: 0xA93AF6C6C79B5D2D'u, lo: 0xD598E40D3DD89BD0'u),
+      uint64x2(hi: 0xD389B47879823479'u, lo: 0x4AFF1D108D4EC2C4'u),
+      uint64x2(hi: 0x843610CB4BF160CB'u, lo: 0xCEDF722A585139BB'u),
+      uint64x2(hi: 0xA54394FE1EEDB8FE'u, lo: 0xC2974EB4EE658829'u),
+      uint64x2(hi: 0xCE947A3DA6A9273E'u, lo: 0x733D226229FEEA33'u),
+      uint64x2(hi: 0x811CCC668829B887'u, lo: 0x0806357D5A3F5260'u),
+      uint64x2(hi: 0xA163FF802A3426A8'u, lo: 0xCA07C2DCB0CF26F8'u),
+      uint64x2(hi: 0xC9BCFF6034C13052'u, lo: 0xFC89B393DD02F0B6'u),
+      uint64x2(hi: 0xFC2C3F3841F17C67'u, lo: 0xBBAC2078D443ACE3'u),
+      uint64x2(hi: 0x9D9BA7832936EDC0'u, lo: 0xD54B944B84AA4C0E'u),
+      uint64x2(hi: 0xC5029163F384A931'u, lo: 0x0A9E795E65D4DF12'u),
+      uint64x2(hi: 0xF64335BCF065D37D'u, lo: 0x4D4617B5FF4A16D6'u),
+      uint64x2(hi: 0x99EA0196163FA42E'u, lo: 0x504BCED1BF8E4E46'u),
+      uint64x2(hi: 0xC06481FB9BCF8D39'u, lo: 0xE45EC2862F71E1D7'u),
+      uint64x2(hi: 0xF07DA27A82C37088'u, lo: 0x5D767327BB4E5A4D'u),
+      uint64x2(hi: 0x964E858C91BA2655'u, lo: 0x3A6A07F8D510F870'u),
+      uint64x2(hi: 0xBBE226EFB628AFEA'u, lo: 0x890489F70A55368C'u),
+      uint64x2(hi: 0xEADAB0ABA3B2DBE5'u, lo: 0x2B45AC74CCEA842F'u),
+      uint64x2(hi: 0x92C8AE6B464FC96F'u, lo: 0x3B0B8BC90012929E'u),
+      uint64x2(hi: 0xB77ADA0617E3BBCB'u, lo: 0x09CE6EBB40173745'u),
+      uint64x2(hi: 0xE55990879DDCAABD'u, lo: 0xCC420A6A101D0516'u),
+      uint64x2(hi: 0x8F57FA54C2A9EAB6'u, lo: 0x9FA946824A12232E'u),
+      uint64x2(hi: 0xB32DF8E9F3546564'u, lo: 0x47939822DC96ABFA'u),
+      uint64x2(hi: 0xDFF9772470297EBD'u, lo: 0x59787E2B93BC56F8'u),
+      uint64x2(hi: 0x8BFBEA76C619EF36'u, lo: 0x57EB4EDB3C55B65B'u),
+      uint64x2(hi: 0xAEFAE51477A06B03'u, lo: 0xEDE622920B6B23F2'u),
+      uint64x2(hi: 0xDAB99E59958885C4'u, lo: 0xE95FAB368E45ECEE'u),
+      uint64x2(hi: 0x88B402F7FD75539B'u, lo: 0x11DBCB0218EBB415'u),
+      uint64x2(hi: 0xAAE103B5FCD2A881'u, lo: 0xD652BDC29F26A11A'u),
+      uint64x2(hi: 0xD59944A37C0752A2'u, lo: 0x4BE76D3346F04960'u),
+      uint64x2(hi: 0x857FCAE62D8493A5'u, lo: 0x6F70A4400C562DDC'u),
+      uint64x2(hi: 0xA6DFBD9FB8E5B88E'u, lo: 0xCB4CCD500F6BB953'u),
+      uint64x2(hi: 0xD097AD07A71F26B2'u, lo: 0x7E2000A41346A7A8'u),
+      uint64x2(hi: 0x825ECC24C873782F'u, lo: 0x8ED400668C0C28C9'u),
+      uint64x2(hi: 0xA2F67F2DFA90563B'u, lo: 0x728900802F0F32FB'u),
+      uint64x2(hi: 0xCBB41EF979346BCA'u, lo: 0x4F2B40A03AD2FFBA'u),
+      uint64x2(hi: 0xFEA126B7D78186BC'u, lo: 0xE2F610C84987BFA9'u),
+      uint64x2(hi: 0x9F24B832E6B0F436'u, lo: 0x0DD9CA7D2DF4D7CA'u),
+      uint64x2(hi: 0xC6EDE63FA05D3143'u, lo: 0x91503D1C79720DBC'u),
+      uint64x2(hi: 0xF8A95FCF88747D94'u, lo: 0x75A44C6397CE912B'u),
+      uint64x2(hi: 0x9B69DBE1B548CE7C'u, lo: 0xC986AFBE3EE11ABB'u),
+      uint64x2(hi: 0xC24452DA229B021B'u, lo: 0xFBE85BADCE996169'u),
+      uint64x2(hi: 0xF2D56790AB41C2A2'u, lo: 0xFAE27299423FB9C4'u),
+      uint64x2(hi: 0x97C560BA6B0919A5'u, lo: 0xDCCD879FC967D41B'u),
+      uint64x2(hi: 0xBDB6B8E905CB600F'u, lo: 0x5400E987BBC1C921'u),
+      uint64x2(hi: 0xED246723473E3813'u, lo: 0x290123E9AAB23B69'u),
+      uint64x2(hi: 0x9436C0760C86E30B'u, lo: 0xF9A0B6720AAF6522'u),
+      uint64x2(hi: 0xB94470938FA89BCE'u, lo: 0xF808E40E8D5B3E6A'u),
+      uint64x2(hi: 0xE7958CB87392C2C2'u, lo: 0xB60B1D1230B20E05'u),
+      uint64x2(hi: 0x90BD77F3483BB9B9'u, lo: 0xB1C6F22B5E6F48C3'u),
+      uint64x2(hi: 0xB4ECD5F01A4AA828'u, lo: 0x1E38AEB6360B1AF4'u),
+      uint64x2(hi: 0xE2280B6C20DD5232'u, lo: 0x25C6DA63C38DE1B1'u),
+      uint64x2(hi: 0x8D590723948A535F'u, lo: 0x579C487E5A38AD0F'u),
+      uint64x2(hi: 0xB0AF48EC79ACE837'u, lo: 0x2D835A9DF0C6D852'u),
+      uint64x2(hi: 0xDCDB1B2798182244'u, lo: 0xF8E431456CF88E66'u),
+      uint64x2(hi: 0x8A08F0F8BF0F156B'u, lo: 0x1B8E9ECB641B5900'u),
+      uint64x2(hi: 0xAC8B2D36EED2DAC5'u, lo: 0xE272467E3D222F40'u),
+      uint64x2(hi: 0xD7ADF884AA879177'u, lo: 0x5B0ED81DCC6ABB10'u),
+      uint64x2(hi: 0x86CCBB52EA94BAEA'u, lo: 0x98E947129FC2B4EA'u),
+      uint64x2(hi: 0xA87FEA27A539E9A5'u, lo: 0x3F2398D747B36225'u),
+      uint64x2(hi: 0xD29FE4B18E88640E'u, lo: 0x8EEC7F0D19A03AAE'u),
+      uint64x2(hi: 0x83A3EEEEF9153E89'u, lo: 0x1953CF68300424AD'u),
+      uint64x2(hi: 0xA48CEAAAB75A8E2B'u, lo: 0x5FA8C3423C052DD8'u),
+      uint64x2(hi: 0xCDB02555653131B6'u, lo: 0x3792F412CB06794E'u),
+      uint64x2(hi: 0x808E17555F3EBF11'u, lo: 0xE2BBD88BBEE40BD1'u),
+      uint64x2(hi: 0xA0B19D2AB70E6ED6'u, lo: 0x5B6ACEAEAE9D0EC5'u),
+      uint64x2(hi: 0xC8DE047564D20A8B'u, lo: 0xF245825A5A445276'u),
+      uint64x2(hi: 0xFB158592BE068D2E'u, lo: 0xEED6E2F0F0D56713'u),
+      uint64x2(hi: 0x9CED737BB6C4183D'u, lo: 0x55464DD69685606C'u),
+      uint64x2(hi: 0xC428D05AA4751E4C'u, lo: 0xAA97E14C3C26B887'u),
+      uint64x2(hi: 0xF53304714D9265DF'u, lo: 0xD53DD99F4B3066A9'u),
+      uint64x2(hi: 0x993FE2C6D07B7FAB'u, lo: 0xE546A8038EFE402A'u),
+      uint64x2(hi: 0xBF8FDB78849A5F96'u, lo: 0xDE98520472BDD034'u),
+      uint64x2(hi: 0xEF73D256A5C0F77C'u, lo: 0x963E66858F6D4441'u),
+      uint64x2(hi: 0x95A8637627989AAD'u, lo: 0xDDE7001379A44AA9'u),
+      uint64x2(hi: 0xBB127C53B17EC159'u, lo: 0x5560C018580D5D53'u),
+      uint64x2(hi: 0xE9D71B689DDE71AF'u, lo: 0xAAB8F01E6E10B4A7'u),
+      uint64x2(hi: 0x9226712162AB070D'u, lo: 0xCAB3961304CA70E9'u),
+      uint64x2(hi: 0xB6B00D69BB55C8D1'u, lo: 0x3D607B97C5FD0D23'u),
+      uint64x2(hi: 0xE45C10C42A2B3B05'u, lo: 0x8CB89A7DB77C506B'u),
+      uint64x2(hi: 0x8EB98A7A9A5B04E3'u, lo: 0x77F3608E92ADB243'u),
+      uint64x2(hi: 0xB267ED1940F1C61C'u, lo: 0x55F038B237591ED4'u),
+      uint64x2(hi: 0xDF01E85F912E37A3'u, lo: 0x6B6C46DEC52F6689'u),
+      uint64x2(hi: 0x8B61313BBABCE2C6'u, lo: 0x2323AC4B3B3DA016'u),
+      uint64x2(hi: 0xAE397D8AA96C1B77'u, lo: 0xABEC975E0A0D081B'u),
+      uint64x2(hi: 0xD9C7DCED53C72255'u, lo: 0x96E7BD358C904A22'u),
+      uint64x2(hi: 0x881CEA14545C7575'u, lo: 0x7E50D64177DA2E55'u),
+      uint64x2(hi: 0xAA242499697392D2'u, lo: 0xDDE50BD1D5D0B9EA'u),
+      uint64x2(hi: 0xD4AD2DBFC3D07787'u, lo: 0x955E4EC64B44E865'u),
+      uint64x2(hi: 0x84EC3C97DA624AB4'u, lo: 0xBD5AF13BEF0B113F'u),
+      uint64x2(hi: 0xA6274BBDD0FADD61'u, lo: 0xECB1AD8AEACDD58F'u),
+      uint64x2(hi: 0xCFB11EAD453994BA'u, lo: 0x67DE18EDA5814AF3'u),
+      uint64x2(hi: 0x81CEB32C4B43FCF4'u, lo: 0x80EACF948770CED8'u),
+      uint64x2(hi: 0xA2425FF75E14FC31'u, lo: 0xA1258379A94D028E'u),
+      uint64x2(hi: 0xCAD2F7F5359A3B3E'u, lo: 0x096EE45813A04331'u),
+      uint64x2(hi: 0xFD87B5F28300CA0D'u, lo: 0x8BCA9D6E188853FD'u),
+      uint64x2(hi: 0x9E74D1B791E07E48'u, lo: 0x775EA264CF55347E'u),
+      uint64x2(hi: 0xC612062576589DDA'u, lo: 0x95364AFE032A819E'u),
+      uint64x2(hi: 0xF79687AED3EEC551'u, lo: 0x3A83DDBD83F52205'u),
+      uint64x2(hi: 0x9ABE14CD44753B52'u, lo: 0xC4926A9672793543'u),
+      uint64x2(hi: 0xC16D9A0095928A27'u, lo: 0x75B7053C0F178294'u),
+      uint64x2(hi: 0xF1C90080BAF72CB1'u, lo: 0x5324C68B12DD6339'u),
+      uint64x2(hi: 0x971DA05074DA7BEE'u, lo: 0xD3F6FC16EBCA5E04'u),
+      uint64x2(hi: 0xBCE5086492111AEA'u, lo: 0x88F4BB1CA6BCF585'u),
+      uint64x2(hi: 0xEC1E4A7DB69561A5'u, lo: 0x2B31E9E3D06C32E6'u),
+      uint64x2(hi: 0x9392EE8E921D5D07'u, lo: 0x3AFF322E62439FD0'u),
+      uint64x2(hi: 0xB877AA3236A4B449'u, lo: 0x09BEFEB9FAD487C3'u),
+      uint64x2(hi: 0xE69594BEC44DE15B'u, lo: 0x4C2EBE687989A9B4'u),
+      uint64x2(hi: 0x901D7CF73AB0ACD9'u, lo: 0x0F9D37014BF60A11'u),
+      uint64x2(hi: 0xB424DC35095CD80F'u, lo: 0x538484C19EF38C95'u),
+      uint64x2(hi: 0xE12E13424BB40E13'u, lo: 0x2865A5F206B06FBA'u),
+      uint64x2(hi: 0x8CBCCC096F5088CB'u, lo: 0xF93F87B7442E45D4'u),
+      uint64x2(hi: 0xAFEBFF0BCB24AAFE'u, lo: 0xF78F69A51539D749'u),
+      uint64x2(hi: 0xDBE6FECEBDEDD5BE'u, lo: 0xB573440E5A884D1C'u),
+      uint64x2(hi: 0x89705F4136B4A597'u, lo: 0x31680A88F8953031'u),
+      uint64x2(hi: 0xABCC77118461CEFC'u, lo: 0xFDC20D2B36BA7C3E'u),
+      uint64x2(hi: 0xD6BF94D5E57A42BC'u, lo: 0x3D32907604691B4D'u),
+      uint64x2(hi: 0x8637BD05AF6C69B5'u, lo: 0xA63F9A49C2C1B110'u),
+      uint64x2(hi: 0xA7C5AC471B478423'u, lo: 0x0FCF80DC33721D54'u),
+      uint64x2(hi: 0xD1B71758E219652B'u, lo: 0xD3C36113404EA4A9'u),
+      uint64x2(hi: 0x83126E978D4FDF3B'u, lo: 0x645A1CAC083126EA'u),
+      uint64x2(hi: 0xA3D70A3D70A3D70A'u, lo: 0x3D70A3D70A3D70A4'u),
+      uint64x2(hi: 0xCCCCCCCCCCCCCCCC'u, lo: 0xCCCCCCCCCCCCCCCD'u),
+      uint64x2(hi: 0x8000000000000000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0xA000000000000000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0xC800000000000000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0xFA00000000000000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0x9C40000000000000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0xC350000000000000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0xF424000000000000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0x9896800000000000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0xBEBC200000000000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0xEE6B280000000000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0x9502F90000000000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0xBA43B74000000000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0xE8D4A51000000000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0x9184E72A00000000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0xB5E620F480000000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0xE35FA931A0000000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0x8E1BC9BF04000000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0xB1A2BC2EC5000000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0xDE0B6B3A76400000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0x8AC7230489E80000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0xAD78EBC5AC620000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0xD8D726B7177A8000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0x878678326EAC9000'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0xA968163F0A57B400'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0xD3C21BCECCEDA100'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0x84595161401484A0'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0xA56FA5B99019A5C8'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0xCECB8F27F4200F3A'u, lo: 0x0000000000000000'u),
+      uint64x2(hi: 0x813F3978F8940984'u, lo: 0x4000000000000000'u),
+      uint64x2(hi: 0xA18F07D736B90BE5'u, lo: 0x5000000000000000'u),
+      uint64x2(hi: 0xC9F2C9CD04674EDE'u, lo: 0xA400000000000000'u),
+      uint64x2(hi: 0xFC6F7C4045812296'u, lo: 0x4D00000000000000'u),
+      uint64x2(hi: 0x9DC5ADA82B70B59D'u, lo: 0xF020000000000000'u),
+      uint64x2(hi: 0xC5371912364CE305'u, lo: 0x6C28000000000000'u),
+      uint64x2(hi: 0xF684DF56C3E01BC6'u, lo: 0xC732000000000000'u),
+      uint64x2(hi: 0x9A130B963A6C115C'u, lo: 0x3C7F400000000000'u),
+      uint64x2(hi: 0xC097CE7BC90715B3'u, lo: 0x4B9F100000000000'u),
+      uint64x2(hi: 0xF0BDC21ABB48DB20'u, lo: 0x1E86D40000000000'u),
+      uint64x2(hi: 0x96769950B50D88F4'u, lo: 0x1314448000000000'u),
+      uint64x2(hi: 0xBC143FA4E250EB31'u, lo: 0x17D955A000000000'u),
+      uint64x2(hi: 0xEB194F8E1AE525FD'u, lo: 0x5DCFAB0800000000'u),
+      uint64x2(hi: 0x92EFD1B8D0CF37BE'u, lo: 0x5AA1CAE500000000'u),
+      uint64x2(hi: 0xB7ABC627050305AD'u, lo: 0xF14A3D9E40000000'u),
+      uint64x2(hi: 0xE596B7B0C643C719'u, lo: 0x6D9CCD05D0000000'u),
+      uint64x2(hi: 0x8F7E32CE7BEA5C6F'u, lo: 0xE4820023A2000000'u),
+      uint64x2(hi: 0xB35DBF821AE4F38B'u, lo: 0xDDA2802C8A800000'u),
+      uint64x2(hi: 0xE0352F62A19E306E'u, lo: 0xD50B2037AD200000'u),
+      uint64x2(hi: 0x8C213D9DA502DE45'u, lo: 0x4526F422CC340000'u),
+      uint64x2(hi: 0xAF298D050E4395D6'u, lo: 0x9670B12B7F410000'u),
+      uint64x2(hi: 0xDAF3F04651D47B4C'u, lo: 0x3C0CDD765F114000'u),
+      uint64x2(hi: 0x88D8762BF324CD0F'u, lo: 0xA5880A69FB6AC800'u),
+      uint64x2(hi: 0xAB0E93B6EFEE0053'u, lo: 0x8EEA0D047A457A00'u),
+      uint64x2(hi: 0xD5D238A4ABE98068'u, lo: 0x72A4904598D6D880'u),
+      uint64x2(hi: 0x85A36366EB71F041'u, lo: 0x47A6DA2B7F864750'u),
+      uint64x2(hi: 0xA70C3C40A64E6C51'u, lo: 0x999090B65F67D924'u),
+      uint64x2(hi: 0xD0CF4B50CFE20765'u, lo: 0xFFF4B4E3F741CF6D'u),
+      uint64x2(hi: 0x82818F1281ED449F'u, lo: 0xBFF8F10E7A8921A4'u),
+      uint64x2(hi: 0xA321F2D7226895C7'u, lo: 0xAFF72D52192B6A0D'u),
+      uint64x2(hi: 0xCBEA6F8CEB02BB39'u, lo: 0x9BF4F8A69F764490'u),
+      uint64x2(hi: 0xFEE50B7025C36A08'u, lo: 0x02F236D04753D5B4'u),
+      uint64x2(hi: 0x9F4F2726179A2245'u, lo: 0x01D762422C946590'u),
+      uint64x2(hi: 0xC722F0EF9D80AAD6'u, lo: 0x424D3AD2B7B97EF5'u),
+      uint64x2(hi: 0xF8EBAD2B84E0D58B'u, lo: 0xD2E0898765A7DEB2'u),
+      uint64x2(hi: 0x9B934C3B330C8577'u, lo: 0x63CC55F49F88EB2F'u),
+      uint64x2(hi: 0xC2781F49FFCFA6D5'u, lo: 0x3CBF6B71C76B25FB'u),
+      uint64x2(hi: 0xF316271C7FC3908A'u, lo: 0x8BEF464E3945EF7A'u),
+      uint64x2(hi: 0x97EDD871CFDA3A56'u, lo: 0x97758BF0E3CBB5AC'u),
+      uint64x2(hi: 0xBDE94E8E43D0C8EC'u, lo: 0x3D52EEED1CBEA317'u),
+      uint64x2(hi: 0xED63A231D4C4FB27'u, lo: 0x4CA7AAA863EE4BDD'u),
+      uint64x2(hi: 0x945E455F24FB1CF8'u, lo: 0x8FE8CAA93E74EF6A'u),
+      uint64x2(hi: 0xB975D6B6EE39E436'u, lo: 0xB3E2FD538E122B44'u),
+      uint64x2(hi: 0xE7D34C64A9C85D44'u, lo: 0x60DBBCA87196B616'u),
+      uint64x2(hi: 0x90E40FBEEA1D3A4A'u, lo: 0xBC8955E946FE31CD'u),
+      uint64x2(hi: 0xB51D13AEA4A488DD'u, lo: 0x6BABAB6398BDBE41'u),
+      uint64x2(hi: 0xE264589A4DCDAB14'u, lo: 0xC696963C7EED2DD1'u),
+      uint64x2(hi: 0x8D7EB76070A08AEC'u, lo: 0xFC1E1DE5CF543CA2'u),
+      uint64x2(hi: 0xB0DE65388CC8ADA8'u, lo: 0x3B25A55F43294BCB'u),
+      uint64x2(hi: 0xDD15FE86AFFAD912'u, lo: 0x49EF0EB713F39EBE'u),
+      uint64x2(hi: 0x8A2DBF142DFCC7AB'u, lo: 0x6E3569326C784337'u),
+      uint64x2(hi: 0xACB92ED9397BF996'u, lo: 0x49C2C37F07965404'u),
+      uint64x2(hi: 0xD7E77A8F87DAF7FB'u, lo: 0xDC33745EC97BE906'u),
+      uint64x2(hi: 0x86F0AC99B4E8DAFD'u, lo: 0x69A028BB3DED71A3'u),
+      uint64x2(hi: 0xA8ACD7C0222311BC'u, lo: 0xC40832EA0D68CE0C'u),
+      uint64x2(hi: 0xD2D80DB02AABD62B'u, lo: 0xF50A3FA490C30190'u),
+      uint64x2(hi: 0x83C7088E1AAB65DB'u, lo: 0x792667C6DA79E0FA'u),
+      uint64x2(hi: 0xA4B8CAB1A1563F52'u, lo: 0x577001B891185938'u),
+      uint64x2(hi: 0xCDE6FD5E09ABCF26'u, lo: 0xED4C0226B55E6F86'u),
+      uint64x2(hi: 0x80B05E5AC60B6178'u, lo: 0x544F8158315B05B4'u),
+      uint64x2(hi: 0xA0DC75F1778E39D6'u, lo: 0x696361AE3DB1C721'u),
+      uint64x2(hi: 0xC913936DD571C84C'u, lo: 0x03BC3A19CD1E38E9'u),
+      uint64x2(hi: 0xFB5878494ACE3A5F'u, lo: 0x04AB48A04065C723'u),
+      uint64x2(hi: 0x9D174B2DCEC0E47B'u, lo: 0x62EB0D64283F9C76'u),
+      uint64x2(hi: 0xC45D1DF942711D9A'u, lo: 0x3BA5D0BD324F8394'u),
+      uint64x2(hi: 0xF5746577930D6500'u, lo: 0xCA8F44EC7EE36479'u),
+      uint64x2(hi: 0x9968BF6ABBE85F20'u, lo: 0x7E998B13CF4E1ECB'u),
+      uint64x2(hi: 0xBFC2EF456AE276E8'u, lo: 0x9E3FEDD8C321A67E'u),
+      uint64x2(hi: 0xEFB3AB16C59B14A2'u, lo: 0xC5CFE94EF3EA101E'u),
+      uint64x2(hi: 0x95D04AEE3B80ECE5'u, lo: 0xBBA1F1D158724A12'u),
+      uint64x2(hi: 0xBB445DA9CA61281F'u, lo: 0x2A8A6E45AE8EDC97'u),
+      uint64x2(hi: 0xEA1575143CF97226'u, lo: 0xF52D09D71A3293BD'u),
+      uint64x2(hi: 0x924D692CA61BE758'u, lo: 0x593C2626705F9C56'u),
+      uint64x2(hi: 0xB6E0C377CFA2E12E'u, lo: 0x6F8B2FB00C77836C'u),
+      uint64x2(hi: 0xE498F455C38B997A'u, lo: 0x0B6DFB9C0F956447'u),
+      uint64x2(hi: 0x8EDF98B59A373FEC'u, lo: 0x4724BD4189BD5EAC'u),
+      uint64x2(hi: 0xB2977EE300C50FE7'u, lo: 0x58EDEC91EC2CB657'u),
+      uint64x2(hi: 0xDF3D5E9BC0F653E1'u, lo: 0x2F2967B66737E3ED'u),
+      uint64x2(hi: 0x8B865B215899F46C'u, lo: 0xBD79E0D20082EE74'u),
+      uint64x2(hi: 0xAE67F1E9AEC07187'u, lo: 0xECD8590680A3AA11'u),
+      uint64x2(hi: 0xDA01EE641A708DE9'u, lo: 0xE80E6F4820CC9495'u),
+      uint64x2(hi: 0x884134FE908658B2'u, lo: 0x3109058D147FDCDD'u),
+      uint64x2(hi: 0xAA51823E34A7EEDE'u, lo: 0xBD4B46F0599FD415'u),
+      uint64x2(hi: 0xD4E5E2CDC1D1EA96'u, lo: 0x6C9E18AC7007C91A'u),
+      uint64x2(hi: 0x850FADC09923329E'u, lo: 0x03E2CF6BC604DDB0'u),
+      uint64x2(hi: 0xA6539930BF6BFF45'u, lo: 0x84DB8346B786151C'u),
+      uint64x2(hi: 0xCFE87F7CEF46FF16'u, lo: 0xE612641865679A63'u),
+      uint64x2(hi: 0x81F14FAE158C5F6E'u, lo: 0x4FCB7E8F3F60C07E'u),
+      uint64x2(hi: 0xA26DA3999AEF7749'u, lo: 0xE3BE5E330F38F09D'u),
+      uint64x2(hi: 0xCB090C8001AB551C'u, lo: 0x5CADF5BFD3072CC5'u),
+      uint64x2(hi: 0xFDCB4FA002162A63'u, lo: 0x73D9732FC7C8F7F6'u),
+      uint64x2(hi: 0x9E9F11C4014DDA7E'u, lo: 0x2867E7FDDCDD9AFA'u),
+      uint64x2(hi: 0xC646D63501A1511D'u, lo: 0xB281E1FD541501B8'u),
+      uint64x2(hi: 0xF7D88BC24209A565'u, lo: 0x1F225A7CA91A4226'u),
+      uint64x2(hi: 0x9AE757596946075F'u, lo: 0x3375788DE9B06958'u),
+      uint64x2(hi: 0xC1A12D2FC3978937'u, lo: 0x0052D6B1641C83AE'u),
+      uint64x2(hi: 0xF209787BB47D6B84'u, lo: 0xC0678C5DBD23A49A'u),
+      uint64x2(hi: 0x9745EB4D50CE6332'u, lo: 0xF840B7BA963646E0'u),
+      uint64x2(hi: 0xBD176620A501FBFF'u, lo: 0xB650E5A93BC3D898'u),
+      uint64x2(hi: 0xEC5D3FA8CE427AFF'u, lo: 0xA3E51F138AB4CEBE'u),
+      uint64x2(hi: 0x93BA47C980E98CDF'u, lo: 0xC66F336C36B10137'u),
+      uint64x2(hi: 0xB8A8D9BBE123F017'u, lo: 0xB80B0047445D4184'u),
+      uint64x2(hi: 0xE6D3102AD96CEC1D'u, lo: 0xA60DC059157491E5'u),
+      uint64x2(hi: 0x9043EA1AC7E41392'u, lo: 0x87C89837AD68DB2F'u),
+      uint64x2(hi: 0xB454E4A179DD1877'u, lo: 0x29BABE4598C311FB'u),
+      uint64x2(hi: 0xE16A1DC9D8545E94'u, lo: 0xF4296DD6FEF3D67A'u),
+      uint64x2(hi: 0x8CE2529E2734BB1D'u, lo: 0x1899E4A65F58660C'u),
+      uint64x2(hi: 0xB01AE745B101E9E4'u, lo: 0x5EC05DCFF72E7F8F'u),
+      uint64x2(hi: 0xDC21A1171D42645D'u, lo: 0x76707543F4FA1F73'u),
+      uint64x2(hi: 0x899504AE72497EBA'u, lo: 0x6A06494A791C53A8'u),
+      uint64x2(hi: 0xABFA45DA0EDBDE69'u, lo: 0x0487DB9D17636892'u),
+      uint64x2(hi: 0xD6F8D7509292D603'u, lo: 0x45A9D2845D3C42B6'u),
+      uint64x2(hi: 0x865B86925B9BC5C2'u, lo: 0x0B8A2392BA45A9B2'u),
+      uint64x2(hi: 0xA7F26836F282B732'u, lo: 0x8E6CAC7768D7141E'u),
+      uint64x2(hi: 0xD1EF0244AF2364FF'u, lo: 0x3207D795430CD926'u),
+      uint64x2(hi: 0x8335616AED761F1F'u, lo: 0x7F44E6BD49E807B8'u),
+      uint64x2(hi: 0xA402B9C5A8D3A6E7'u, lo: 0x5F16206C9C6209A6'u),
+      uint64x2(hi: 0xCD036837130890A1'u, lo: 0x36DBA887C37A8C0F'u),
+      uint64x2(hi: 0x802221226BE55A64'u, lo: 0xC2494954DA2C9789'u),
+      uint64x2(hi: 0xA02AA96B06DEB0FD'u, lo: 0xF2DB9BAA10B7BD6C'u),
+      uint64x2(hi: 0xC83553C5C8965D3D'u, lo: 0x6F92829494E5ACC7'u),
+      uint64x2(hi: 0xFA42A8B73ABBF48C'u, lo: 0xCB772339BA1F17F9'u),
+      uint64x2(hi: 0x9C69A97284B578D7'u, lo: 0xFF2A760414536EFB'u),
+      uint64x2(hi: 0xC38413CF25E2D70D'u, lo: 0xFEF5138519684ABA'u),
+      uint64x2(hi: 0xF46518C2EF5B8CD1'u, lo: 0x7EB258665FC25D69'u),
+      uint64x2(hi: 0x98BF2F79D5993802'u, lo: 0xEF2F773FFBD97A61'u),
+      uint64x2(hi: 0xBEEEFB584AFF8603'u, lo: 0xAAFB550FFACFD8FA'u),
+      uint64x2(hi: 0xEEAABA2E5DBF6784'u, lo: 0x95BA2A53F983CF38'u),
+      uint64x2(hi: 0x952AB45CFA97A0B2'u, lo: 0xDD945A747BF26183'u),
+      uint64x2(hi: 0xBA756174393D88DF'u, lo: 0x94F971119AEEF9E4'u),
+      uint64x2(hi: 0xE912B9D1478CEB17'u, lo: 0x7A37CD5601AAB85D'u),
+      uint64x2(hi: 0x91ABB422CCB812EE'u, lo: 0xAC62E055C10AB33A'u),
+      uint64x2(hi: 0xB616A12B7FE617AA'u, lo: 0x577B986B314D6009'u),
+      uint64x2(hi: 0xE39C49765FDF9D94'u, lo: 0xED5A7E85FDA0B80B'u),
+      uint64x2(hi: 0x8E41ADE9FBEBC27D'u, lo: 0x14588F13BE847307'u),
+      uint64x2(hi: 0xB1D219647AE6B31C'u, lo: 0x596EB2D8AE258FC8'u),
+      uint64x2(hi: 0xDE469FBD99A05FE3'u, lo: 0x6FCA5F8ED9AEF3BB'u),
+      uint64x2(hi: 0x8AEC23D680043BEE'u, lo: 0x25DE7BB9480D5854'u),
+      uint64x2(hi: 0xADA72CCC20054AE9'u, lo: 0xAF561AA79A10AE6A'u),
+      uint64x2(hi: 0xD910F7FF28069DA4'u, lo: 0x1B2BA1518094DA04'u),
+      uint64x2(hi: 0x87AA9AFF79042286'u, lo: 0x90FB44D2F05D0842'u),
+      uint64x2(hi: 0xA99541BF57452B28'u, lo: 0x353A1607AC744A53'u),
+      uint64x2(hi: 0xD3FA922F2D1675F2'u, lo: 0x42889B8997915CE8'u),
+      uint64x2(hi: 0x847C9B5D7C2E09B7'u, lo: 0x69956135FEBADA11'u),
+      uint64x2(hi: 0xA59BC234DB398C25'u, lo: 0x43FAB9837E699095'u),
+      uint64x2(hi: 0xCF02B2C21207EF2E'u, lo: 0x94F967E45E03F4BB'u),
+      uint64x2(hi: 0x8161AFB94B44F57D'u, lo: 0x1D1BE0EEBAC278F5'u),
+      uint64x2(hi: 0xA1BA1BA79E1632DC'u, lo: 0x6462D92A69731732'u),
+      uint64x2(hi: 0xCA28A291859BBF93'u, lo: 0x7D7B8F7503CFDCFE'u),
+      uint64x2(hi: 0xFCB2CB35E702AF78'u, lo: 0x5CDA735244C3D43E'u),
+      uint64x2(hi: 0x9DEFBF01B061ADAB'u, lo: 0x3A0888136AFA64A7'u),
+      uint64x2(hi: 0xC56BAEC21C7A1916'u, lo: 0x088AAA1845B8FDD0'u),
+      uint64x2(hi: 0xF6C69A72A3989F5B'u, lo: 0x8AAD549E57273D45'u),
+      uint64x2(hi: 0x9A3C2087A63F6399'u, lo: 0x36AC54E2F678864B'u),
+      uint64x2(hi: 0xC0CB28A98FCF3C7F'u, lo: 0x84576A1BB416A7DD'u),
+      uint64x2(hi: 0xF0FDF2D3F3C30B9F'u, lo: 0x656D44A2A11C51D5'u),
+      uint64x2(hi: 0x969EB7C47859E743'u, lo: 0x9F644AE5A4B1B325'u),
+      uint64x2(hi: 0xBC4665B596706114'u, lo: 0x873D5D9F0DDE1FEE'u),
+      uint64x2(hi: 0xEB57FF22FC0C7959'u, lo: 0xA90CB506D155A7EA'u),
+      uint64x2(hi: 0x9316FF75DD87CBD8'u, lo: 0x09A7F12442D588F2'u),
+      uint64x2(hi: 0xB7DCBF5354E9BECE'u, lo: 0x0C11ED6D538AEB2F'u),
+      uint64x2(hi: 0xE5D3EF282A242E81'u, lo: 0x8F1668C8A86DA5FA'u),
+      uint64x2(hi: 0x8FA475791A569D10'u, lo: 0xF96E017D694487BC'u),
+      uint64x2(hi: 0xB38D92D760EC4455'u, lo: 0x37C981DCC395A9AC'u),
+      uint64x2(hi: 0xE070F78D3927556A'u, lo: 0x85BBE253F47B1417'u),
+      uint64x2(hi: 0x8C469AB843B89562'u, lo: 0x93956D7478CCEC8E'u),
+      uint64x2(hi: 0xAF58416654A6BABB'u, lo: 0x387AC8D1970027B2'u),
+      uint64x2(hi: 0xDB2E51BFE9D0696A'u, lo: 0x06997B05FCC0319E'u),
+      uint64x2(hi: 0x88FCF317F22241E2'u, lo: 0x441FECE3BDF81F03'u),
+      uint64x2(hi: 0xAB3C2FDDEEAAD25A'u, lo: 0xD527E81CAD7626C3'u),
+      uint64x2(hi: 0xD60B3BD56A5586F1'u, lo: 0x8A71E223D8D3B074'u),
+      uint64x2(hi: 0x85C7056562757456'u, lo: 0xF6872D5667844E49'u),
+      uint64x2(hi: 0xA738C6BEBB12D16C'u, lo: 0xB428F8AC016561DB'u),
+      uint64x2(hi: 0xD106F86E69D785C7'u, lo: 0xE13336D701BEBA52'u),
+      uint64x2(hi: 0x82A45B450226B39C'u, lo: 0xECC0024661173473'u),
+      uint64x2(hi: 0xA34D721642B06084'u, lo: 0x27F002D7F95D0190'u),
+      uint64x2(hi: 0xCC20CE9BD35C78A5'u, lo: 0x31EC038DF7B441F4'u),
+      uint64x2(hi: 0xFF290242C83396CE'u, lo: 0x7E67047175A15271'u),
+      uint64x2(hi: 0x9F79A169BD203E41'u, lo: 0x0F0062C6E984D386'u),
+      uint64x2(hi: 0xC75809C42C684DD1'u, lo: 0x52C07B78A3E60868'u),
+      uint64x2(hi: 0xF92E0C3537826145'u, lo: 0xA7709A56CCDF8A82'u),
+      uint64x2(hi: 0x9BBCC7A142B17CCB'u, lo: 0x88A66076400BB691'u),
+      uint64x2(hi: 0xC2ABF989935DDBFE'u, lo: 0x6ACFF893D00EA435'u),
+      uint64x2(hi: 0xF356F7EBF83552FE'u, lo: 0x0583F6B8C4124D43'u),
+      uint64x2(hi: 0x98165AF37B2153DE'u, lo: 0xC3727A337A8B704A'u),
+      uint64x2(hi: 0xBE1BF1B059E9A8D6'u, lo: 0x744F18C0592E4C5C'u),
+      uint64x2(hi: 0xEDA2EE1C7064130C'u, lo: 0x1162DEF06F79DF73'u),
+      uint64x2(hi: 0x9485D4D1C63E8BE7'u, lo: 0x8ADDCB5645AC2BA8'u),
+      uint64x2(hi: 0xB9A74A0637CE2EE1'u, lo: 0x6D953E2BD7173692'u),
+      uint64x2(hi: 0xE8111C87C5C1BA99'u, lo: 0xC8FA8DB6CCDD0437'u),
+      uint64x2(hi: 0x910AB1D4DB9914A0'u, lo: 0x1D9C9892400A22A2'u),
+      uint64x2(hi: 0xB54D5E4A127F59C8'u, lo: 0x2503BEB6D00CAB4B'u),
+      uint64x2(hi: 0xE2A0B5DC971F303A'u, lo: 0x2E44AE64840FD61D'u),
+      uint64x2(hi: 0x8DA471A9DE737E24'u, lo: 0x5CEAECFED289E5D2'u),
+      uint64x2(hi: 0xB10D8E1456105DAD'u, lo: 0x7425A83E872C5F47'u),
+      uint64x2(hi: 0xDD50F1996B947518'u, lo: 0xD12F124E28F77719'u),
+      uint64x2(hi: 0x8A5296FFE33CC92F'u, lo: 0x82BD6B70D99AAA6F'u),
+      uint64x2(hi: 0xACE73CBFDC0BFB7B'u, lo: 0x636CC64D1001550B'u),
+      uint64x2(hi: 0xD8210BEFD30EFA5A'u, lo: 0x3C47F7E05401AA4E'u),
+      uint64x2(hi: 0x8714A775E3E95C78'u, lo: 0x65ACFAEC34810A71'u),
+      uint64x2(hi: 0xA8D9D1535CE3B396'u, lo: 0x7F1839A741A14D0D'u),
+      uint64x2(hi: 0xD31045A8341CA07C'u, lo: 0x1EDE48111209A050'u),
+      uint64x2(hi: 0x83EA2B892091E44D'u, lo: 0x934AED0AAB460432'u),
+      uint64x2(hi: 0xA4E4B66B68B65D60'u, lo: 0xF81DA84D5617853F'u),
+      uint64x2(hi: 0xCE1DE40642E3F4B9'u, lo: 0x36251260AB9D668E'u),
+      uint64x2(hi: 0x80D2AE83E9CE78F3'u, lo: 0xC1D72B7C6B426019'u),
+      uint64x2(hi: 0xA1075A24E4421730'u, lo: 0xB24CF65B8612F81F'u),
+      uint64x2(hi: 0xC94930AE1D529CFC'u, lo: 0xDEE033F26797B627'u),
+      uint64x2(hi: 0xFB9B7CD9A4A7443C'u, lo: 0x169840EF017DA3B1'u),
+      uint64x2(hi: 0x9D412E0806E88AA5'u, lo: 0x8E1F289560EE864E'u),
+      uint64x2(hi: 0xC491798A08A2AD4E'u, lo: 0xF1A6F2BAB92A27E2'u),
+      uint64x2(hi: 0xF5B5D7EC8ACB58A2'u, lo: 0xAE10AF696774B1DB'u),
+      uint64x2(hi: 0x9991A6F3D6BF1765'u, lo: 0xACCA6DA1E0A8EF29'u),
+      uint64x2(hi: 0xBFF610B0CC6EDD3F'u, lo: 0x17FD090A58D32AF3'u),
+      uint64x2(hi: 0xEFF394DCFF8A948E'u, lo: 0xDDFC4B4CEF07F5B0'u),
+      uint64x2(hi: 0x95F83D0A1FB69CD9'u, lo: 0x4ABDAF101564F98E'u),
+      uint64x2(hi: 0xBB764C4CA7A4440F'u, lo: 0x9D6D1AD41ABE37F1'u),
+      uint64x2(hi: 0xEA53DF5FD18D5513'u, lo: 0x84C86189216DC5ED'u),
+      uint64x2(hi: 0x92746B9BE2F8552C'u, lo: 0x32FD3CF5B4E49BB4'u),
+      uint64x2(hi: 0xB7118682DBB66A77'u, lo: 0x3FBC8C33221DC2A1'u),
+      uint64x2(hi: 0xE4D5E82392A40515'u, lo: 0x0FABAF3FEAA5334A'u),
+      uint64x2(hi: 0x8F05B1163BA6832D'u, lo: 0x29CB4D87F2A7400E'u),
+      uint64x2(hi: 0xB2C71D5BCA9023F8'u, lo: 0x743E20E9EF511012'u),
+      uint64x2(hi: 0xDF78E4B2BD342CF6'u, lo: 0x914DA9246B255416'u),
+      uint64x2(hi: 0x8BAB8EEFB6409C1A'u, lo: 0x1AD089B6C2F7548E'u),
+      uint64x2(hi: 0xAE9672ABA3D0C320'u, lo: 0xA184AC2473B529B1'u),
+      uint64x2(hi: 0xDA3C0F568CC4F3E8'u, lo: 0xC9E5D72D90A2741E'u),
+      uint64x2(hi: 0x8865899617FB1871'u, lo: 0x7E2FA67C7A658892'u),
+      uint64x2(hi: 0xAA7EEBFB9DF9DE8D'u, lo: 0xDDBB901B98FEEAB7'u),
+      uint64x2(hi: 0xD51EA6FA85785631'u, lo: 0x552A74227F3EA565'u),
+      uint64x2(hi: 0x8533285C936B35DE'u, lo: 0xD53A88958F87275F'u),
+      uint64x2(hi: 0xA67FF273B8460356'u, lo: 0x8A892ABAF368F137'u),
+      uint64x2(hi: 0xD01FEF10A657842C'u, lo: 0x2D2B7569B0432D85'u),
+      uint64x2(hi: 0x8213F56A67F6B29B'u, lo: 0x9C3B29620E29FC73'u),
+      uint64x2(hi: 0xA298F2C501F45F42'u, lo: 0x8349F3BA91B47B8F'u),
+      uint64x2(hi: 0xCB3F2F7642717713'u, lo: 0x241C70A936219A73'u),
+      uint64x2(hi: 0xFE0EFB53D30DD4D7'u, lo: 0xED238CD383AA0110'u),
+      uint64x2(hi: 0x9EC95D1463E8A506'u, lo: 0xF4363804324A40AA'u),
+      uint64x2(hi: 0xC67BB4597CE2CE48'u, lo: 0xB143C6053EDCD0D5'u),
+      uint64x2(hi: 0xF81AA16FDC1B81DA'u, lo: 0xDD94B7868E94050A'u),
+      uint64x2(hi: 0x9B10A4E5E9913128'u, lo: 0xCA7CF2B4191C8326'u),
+      uint64x2(hi: 0xC1D4CE1F63F57D72'u, lo: 0xFD1C2F611F63A3F0'u),
+      uint64x2(hi: 0xF24A01A73CF2DCCF'u, lo: 0xBC633B39673C8CEC'u),
+      uint64x2(hi: 0x976E41088617CA01'u, lo: 0xD5BE0503E085D813'u),
+      uint64x2(hi: 0xBD49D14AA79DBC82'u, lo: 0x4B2D8644D8A74E18'u),
+      uint64x2(hi: 0xEC9C459D51852BA2'u, lo: 0xDDF8E7D60ED1219E'u),
+      uint64x2(hi: 0x93E1AB8252F33B45'u, lo: 0xCABB90E5C942B503'u),
+      uint64x2(hi: 0xB8DA1662E7B00A17'u, lo: 0x3D6A751F3B936243'u),
+      uint64x2(hi: 0xE7109BFBA19C0C9D'u, lo: 0x0CC512670A783AD4'u),
+      uint64x2(hi: 0x906A617D450187E2'u, lo: 0x27FB2B80668B24C5'u),
+      uint64x2(hi: 0xB484F9DC9641E9DA'u, lo: 0xB1F9F660802DEDF6'u),
+      uint64x2(hi: 0xE1A63853BBD26451'u, lo: 0x5E7873F8A0396973'u),
+      uint64x2(hi: 0x8D07E33455637EB2'u, lo: 0xDB0B487B6423E1E8'u),
+      uint64x2(hi: 0xB049DC016ABC5E5F'u, lo: 0x91CE1A9A3D2CDA62'u),
+      uint64x2(hi: 0xDC5C5301C56B75F7'u, lo: 0x7641A140CC7810FB'u),
+      uint64x2(hi: 0x89B9B3E11B6329BA'u, lo: 0xA9E904C87FCB0A9D'u),
+      uint64x2(hi: 0xAC2820D9623BF429'u, lo: 0x546345FA9FBDCD44'u),
+      uint64x2(hi: 0xD732290FBACAF133'u, lo: 0xA97C177947AD4095'u),
+      uint64x2(hi: 0x867F59A9D4BED6C0'u, lo: 0x49ED8EABCCCC485D'u),
+      uint64x2(hi: 0xA81F301449EE8C70'u, lo: 0x5C68F256BFFF5A74'u),
+      uint64x2(hi: 0xD226FC195C6A2F8C'u, lo: 0x73832EEC6FFF3111'u),
+      uint64x2(hi: 0x83585D8FD9C25DB7'u, lo: 0xC831FD53C5FF7EAB'u),
+      uint64x2(hi: 0xA42E74F3D032F525'u, lo: 0xBA3E7CA8B77F5E55'u),
+      uint64x2(hi: 0xCD3A1230C43FB26F'u, lo: 0x28CE1BD2E55F35EB'u),
+      uint64x2(hi: 0x80444B5E7AA7CF85'u, lo: 0x7980D163CF5B81B3'u),
+      uint64x2(hi: 0xA0555E361951C366'u, lo: 0xD7E105BCC332621F'u),
+      uint64x2(hi: 0xC86AB5C39FA63440'u, lo: 0x8DD9472BF3FEFAA7'u),
+      uint64x2(hi: 0xFA856334878FC150'u, lo: 0xB14F98F6F0FEB951'u),
+      uint64x2(hi: 0x9C935E00D4B9D8D2'u, lo: 0x6ED1BF9A569F33D3'u),
+      uint64x2(hi: 0xC3B8358109E84F07'u, lo: 0x0A862F80EC4700C8'u),
+      uint64x2(hi: 0xF4A642E14C6262C8'u, lo: 0xCD27BB612758C0FA'u),
+      uint64x2(hi: 0x98E7E9CCCFBD7DBD'u, lo: 0x8038D51CB897789C'u),
+      uint64x2(hi: 0xBF21E44003ACDD2C'u, lo: 0xE0470A63E6BD56C3'u),
+      uint64x2(hi: 0xEEEA5D5004981478'u, lo: 0x1858CCFCE06CAC74'u),
+      uint64x2(hi: 0x95527A5202DF0CCB'u, lo: 0x0F37801E0C43EBC8'u),
+      uint64x2(hi: 0xBAA718E68396CFFD'u, lo: 0xD30560258F54E6BA'u),
+      uint64x2(hi: 0xE950DF20247C83FD'u, lo: 0x47C6B82EF32A2069'u),
+      uint64x2(hi: 0x91D28B7416CDD27E'u, lo: 0x4CDC331D57FA5441'u),
+      uint64x2(hi: 0xB6472E511C81471D'u, lo: 0xE0133FE4ADF8E952'u),
+      uint64x2(hi: 0xE3D8F9E563A198E5'u, lo: 0x58180FDDD97723A6'u),
+      uint64x2(hi: 0x8E679C2F5E44FF8F'u, lo: 0x570F09EAA7EA7648'u),
+      uint64x2(hi: 0xB201833B35D63F73'u, lo: 0x2CD2CC6551E513DA'u),
+      uint64x2(hi: 0xDE81E40A034BCF4F'u, lo: 0xF8077F7EA65E58D1'u),
+      uint64x2(hi: 0x8B112E86420F6191'u, lo: 0xFB04AFAF27FAF782'u),
+      uint64x2(hi: 0xADD57A27D29339F6'u, lo: 0x79C5DB9AF1F9B563'u),
+      uint64x2(hi: 0xD94AD8B1C7380874'u, lo: 0x18375281AE7822BC'u),
+      uint64x2(hi: 0x87CEC76F1C830548'u, lo: 0x8F2293910D0B15B5'u),
+      uint64x2(hi: 0xA9C2794AE3A3C69A'u, lo: 0xB2EB3875504DDB22'u),
+      uint64x2(hi: 0xD433179D9C8CB841'u, lo: 0x5FA60692A46151EB'u),
+      uint64x2(hi: 0x849FEEC281D7F328'u, lo: 0xDBC7C41BA6BCD333'u),
+      uint64x2(hi: 0xA5C7EA73224DEFF3'u, lo: 0x12B9B522906C0800'u),
+      uint64x2(hi: 0xCF39E50FEAE16BEF'u, lo: 0xD768226B34870A00'u),
+      uint64x2(hi: 0x81842F29F2CCE375'u, lo: 0xE6A1158300D46640'u),
+      uint64x2(hi: 0xA1E53AF46F801C53'u, lo: 0x60495AE3C1097FD0'u),
+      uint64x2(hi: 0xCA5E89B18B602368'u, lo: 0x385BB19CB14BDFC4'u),
+      uint64x2(hi: 0xFCF62C1DEE382C42'u, lo: 0x46729E03DD9ED7B5'u),
+      uint64x2(hi: 0x9E19DB92B4E31BA9'u, lo: 0x6C07A2C26A8346D1'u),
+      uint64x2(hi: 0xC5A05277621BE293'u, lo: 0xC7098B7305241885'u),
+      uint64x2(hi: 0xF70867153AA2DB38'u, lo: 0xB8CBEE4FC66D1EA7'u)]
+  dragonbox_Assert(k >= kMin)
+  dragonbox_Assert(k <= kMax)
+  return pow10[k - kMin]
+
+##  Returns whether value is divisible by 2^e2
+
+proc multipleOfPow2*(value: uint64; e2: int32): bool {.inline.} =
+  dragonbox_Assert(e2 >= 0)
+  return e2 < 64 and (value and ((uint64(1) shl e2) - 1)) == 0
+
+##  Returns whether value is divisible by 5^e5
+
+proc multipleOfPow5*(value: uint64; e5: int32): bool {.inline.} =
+  type
+    MulCmp {.bycopy.} = object
+      mul: uint64
+      cmp: uint64
+
+  const
+    mod5 = [MulCmp(mul: 0x0000000000000001'u, cmp: 0xFFFFFFFFFFFFFFFF'u),
+      MulCmp(mul: 0xCCCCCCCCCCCCCCCD'u, cmp: 0x3333333333333333'u),
+      MulCmp(mul: 0x8F5C28F5C28F5C29'u, cmp: 0x0A3D70A3D70A3D70'u),
+      MulCmp(mul: 0x1CAC083126E978D5'u, cmp: 0x020C49BA5E353F7C'u),
+      MulCmp(mul: 0xD288CE703AFB7E91'u, cmp: 0x0068DB8BAC710CB2'u),
+      MulCmp(mul: 0x5D4E8FB00BCBE61D'u, cmp: 0x0014F8B588E368F0'u),
+      MulCmp(mul: 0x790FB65668C26139'u, cmp: 0x000431BDE82D7B63'u),
+      MulCmp(mul: 0xE5032477AE8D46A5'u, cmp: 0x0000D6BF94D5E57A'u),
+      MulCmp(mul: 0xC767074B22E90E21'u, cmp: 0x00002AF31DC46118'u),
+      MulCmp(mul: 0x8E47CE423A2E9C6D'u, cmp: 0x0000089705F4136B'u),
+      MulCmp(mul: 0x4FA7F60D3ED61F49'u, cmp: 0x000001B7CDFD9D7B'u),
+      MulCmp(mul: 0x0FEE64690C913975'u, cmp: 0x00000057F5FF85E5'u),
+      MulCmp(mul: 0x3662E0E1CF503EB1'u, cmp: 0x000000119799812D'u),
+      MulCmp(mul: 0xA47A2CF9F6433FBD'u, cmp: 0x0000000384B84D09'u),
+      MulCmp(mul: 0x54186F653140A659'u, cmp: 0x00000000B424DC35'u),
+      MulCmp(mul: 0x7738164770402145'u, cmp: 0x0000000024075F3D'u),
+      MulCmp(mul: 0xE4A4D1417CD9A041'u, cmp: 0x000000000734ACA5'u),
+      MulCmp(mul: 0xC75429D9E5C5200D'u, cmp: 0x000000000170EF54'u),
+      MulCmp(mul: 0xC1773B91FAC10669'u, cmp: 0x000000000049C977'u),
+      MulCmp(mul: 0x26B172506559CE15'u, cmp: 0x00000000000EC1E4'u),
+      MulCmp(mul: 0xD489E3A9ADDEC2D1'u, cmp: 0x000000000002F394'u),
+      MulCmp(mul: 0x90E860BB892C8D5D'u, cmp: 0x000000000000971D'u),
+      MulCmp(mul: 0x502E79BF1B6F4F79'u, cmp: 0x0000000000001E39'u),
+      MulCmp(mul: 0xDCD618596BE30FE5'u, cmp: 0x000000000000060B'u),
+      MulCmp(mul: 0x2C2AD1AB7BFA3661'u, cmp: 0x0000000000000135'u)]
+  dragonbox_Assert(e5 >= 0)
+  dragonbox_Assert(e5 <= 24)
+  let m5: MulCmp = mod5[e5]
+  return value * m5.mul <= m5.cmp
+
+type
+  FloatingDecimal64* {.bycopy.} = object
+    significand*: uint64
+    exponent*: int32
+
+
+proc toDecimal64AsymmetricInterval*(e2: int32): FloatingDecimal64 {.inline.} =
+  ##  NB:
+  ##  accept_lower_endpoint = true
+  ##  accept_upper_endpoint = true
+  const
+    P: int32 = significandSize
+  ##  Compute k and beta
+  let minusK: int32 = floorLog10ThreeQuartersPow2(e2)
+  let betaMinus1: int32 = e2 + floorLog2Pow10(-minusK)
+  ##  Compute xi and zi
+  let pow10: uint64x2 = computePow10(-minusK)
+  let lowerEndpoint: uint64 = (pow10.hi - (pow10.hi shr (P + 1))) shr
+      (64 - P - betaMinus1)
+  let upperEndpoint: uint64 = (pow10.hi + (pow10.hi shr (P + 0))) shr
+      (64 - P - betaMinus1)
+  ##  If we don't accept the left endpoint (but we do!) or
+  ##  if the left endpoint is not an integer, increase it
+  let lowerEndpointIsInteger: bool = (2 <= e2 and e2 <= 3)
+  let xi: uint64 = lowerEndpoint + uint64(not lowerEndpointIsInteger)
+  let zi: uint64 = upperEndpoint
+  ##  Try bigger divisor
+  var q: uint64 = zi div 10
+  if q * 10 >= xi:
+    return FloatingDecimal64(significand: q, exponent: minusK + 1)
+  q = ((pow10.hi shr (64 - (P + 1) - betaMinus1)) + 1) div 2
+  ##  When tie occurs, choose one of them according to the rule
+  if e2 == -77:
+    dec(q, ord(q mod 2 != 0))
+    ##  Round to even.
+  else:
+    inc(q, ord(q < xi))
+  return FloatingDecimal64(significand: q, exponent: minusK)
+
+proc computeDelta*(pow10: uint64x2; betaMinus1: int32): uint32 {.inline.} =
+  dragonbox_Assert(betaMinus1 >= 0)
+  dragonbox_Assert(betaMinus1 <= 63)
+  return cast[uint32](pow10.hi shr (64 - 1 - betaMinus1))
+
+when defined(sizeof_Int128):
+  proc mul128*(x: uint64; y: uint64): uint64x2 {.inline.} =
+    ##  1 mulx
+    type
+      uint128T = uint128
+    let p: uint128T = uint128T(x) * y
+    let hi: uint64 = cast[uint64](p shr 64)
+    let lo: uint64 = cast[uint64](p)
+    return (hi, lo)
+
+elif defined(vcc) and defined(cpu64):
+  proc umul128(x, y: uint64, z: ptr uint64): uint64 {.importc: "_umul128", header: "<intrin.h>".}
+  proc mul128*(x: uint64; y: uint64): uint64x2 {.inline.} =
+    var hi: uint64 = 0
+    var lo: uint64 = umul128(x, y, addr(hi))
+    return uint64x2(hi: hi, lo: lo)
+
+else:
+  proc lo32*(x: uint64): uint32 {.inline.} =
+    return cast[uint32](x)
+
+  proc hi32*(x: uint64): uint32 {.inline.} =
+    return cast[uint32](x shr 32)
+
+  proc mul128*(a: uint64; b: uint64): uint64x2 {.inline.} =
+    let b00: uint64 = uint64(lo32(a)) * lo32(b)
+    let b01: uint64 = uint64(lo32(a)) * hi32(b)
+    let b10: uint64 = uint64(hi32(a)) * lo32(b)
+    let b11: uint64 = uint64(hi32(a)) * hi32(b)
+    let mid1: uint64 = b10 + hi32(b00)
+    let mid2: uint64 = b01 + lo32(mid1)
+    let hi: uint64 = b11 + hi32(mid1) + hi32(mid2)
+    let lo: uint64 = lo32(b00) or uint64(lo32(mid2)) shl 32
+    return uint64x2(hi: hi, lo: lo)
+
+##  Returns (x * y) / 2^128
+
+proc mulShift*(x: uint64; y: uint64x2): uint64 {.inline.} =
+  ##  2 mulx
+  var p1: uint64x2 = mul128(x, y.hi)
+  var p0: uint64x2 = mul128(x, y.lo)
+  p1.lo += p0.hi
+  inc(p1.hi, ord(p1.lo < p0.hi))
+  return p1.hi
+
+proc mulParity*(twoF: uint64; pow10: uint64x2; betaMinus1: int32): bool {.inline.} =
+  ##  1 mulx, 1 mul
+  dragonbox_Assert(betaMinus1 >= 1)
+  dragonbox_Assert(betaMinus1 <= 63)
+  let p01: uint64 = twoF * pow10.hi
+  let p10: uint64 = mul128(twoF, pow10.lo).hi
+  let mid: uint64 = p01 + p10
+  return (mid and (uint64(1) shl (64 - betaMinus1))) != 0
+
+proc isIntegralEndpoint*(twoF: uint64; e2: int32; minusK: int32): bool {.inline.} =
+  if e2 < -2:
+    return false
+  if e2 <= 9:
+    return true
+  if e2 <= 86:
+    return multipleOfPow5(twoF, minusK)
+  return false
+
+proc isIntegralMidpoint*(twoF: uint64; e2: int32; minusK: int32): bool {.inline.} =
+  if e2 < -4:
+    return multipleOfPow2(twoF, minusK - e2 + 1)
+  if e2 <= 9:
+    return true
+  if e2 <= 86:
+    return multipleOfPow5(twoF, minusK)
+  return false
+
+proc toDecimal64*(ieeeSignificand: uint64; ieeeExponent: uint64): FloatingDecimal64 {.
+    inline.} =
+  const
+    kappa: int32 = 2
+  const
+    bigDivisor: uint32 = 1000
+  ##  10^(kappa + 1)
+  const
+    smallDivisor: uint32 = 100
+  ##  10^(kappa)
+  ##
+  ##  Step 1:
+  ##  integer promotion & Schubfach multiplier calculation.
+  ##
+  var m2: uint64
+  var e2: int32
+  if ieeeExponent != 0:
+    m2 = hiddenBit or ieeeSignificand
+    e2 = cast[int32](ieeeExponent) - exponentBias
+    if 0 <= -e2 and -e2 < significandSize and multipleOfPow2(m2, -e2):
+      ##  Small integer.
+      return FloatingDecimal64(significand: m2 shr -e2, exponent: 0)
+    if ieeeSignificand == 0 and ieeeExponent > 1:
+      ##  Shorter interval case; proceed like Schubfach.
+      return toDecimal64AsymmetricInterval(e2)
+  else:
+    ##  Subnormal case; interval is always regular.
+    m2 = ieeeSignificand
+    e2 = 1 - exponentBias
+  let isEven: bool = (m2 mod 2 == 0)
+  let acceptLower: bool = isEven
+  let acceptUpper: bool = isEven
+  ##  Compute k and beta.
+  let minusK: int32 = floorLog10Pow2(e2) - kappa
+  let betaMinus1: int32 = e2 + floorLog2Pow10(-minusK)
+  dragonbox_Assert(betaMinus1 >= 6)
+  dragonbox_Assert(betaMinus1 <= 9)
+  let pow10: uint64x2 = computePow10(-minusK)
+  ##  Compute delta
+  ##  10^kappa <= delta < 10^(kappa + 1)
+  ##       100 <= delta < 1000
+  let delta: uint32 = computeDelta(pow10, betaMinus1)
+  dragonbox_Assert(delta >= smallDivisor)
+  dragonbox_Assert(delta < bigDivisor)
+  let twoFl: uint64 = 2 * m2 - 1
+  let twoFc: uint64 = 2 * m2
+  let twoFr: uint64 = 2 * m2 + 1
+  ##  (54 bits)
+  ##  Compute zi
+  ##   (54 + 9 = 63 bits)
+  let zi: uint64 = mulShift(twoFr shl betaMinus1, pow10)
+  ##  2 mulx
+  ##
+  ##  Step 2:
+  ##  Try larger divisor.
+  ##
+  var q: uint64 = zi div bigDivisor
+  ##   uint64_t q = Mul128(zi, 0x83126E978D4FDF3Cu).hi >> 9; // 1 mulx
+  var r: uint32 = cast[uint32](zi) - bigDivisor * cast[uint32](q)
+  ##  r = zi % BigDivisor
+  ##  0 <= r < 1000
+  if r < delta:                  ## likely ~50% ?!
+            ##  (r > deltai)
+    ##  Exclude the right endpoint if necessary
+    if r != 0 or acceptUpper or not isIntegralEndpoint(twoFr, e2, minusK):
+      return FloatingDecimal64(significand: q, exponent: minusK + kappa + 1)
+    dragonbox_Assert(q != 0)
+    dec(q)
+    r = bigDivisor
+  elif r == delta:               ## unlikely
+    ##  Compare fractional parts.
+    ##  Check conditions in the order different from the paper
+    ##  to take advantage of short-circuiting
+    if (acceptLower and isIntegralEndpoint(twoFl, e2, minusK)) or
+        mulParity(twoFl, pow10, betaMinus1):
+      return FloatingDecimal64(significand: q, exponent: minusK + kappa + 1)
+  else:
+    discard
+  ##
+  ##  Step 3:
+  ##  Find the significand with the smaller divisor
+  ##
+  q = q * 10
+  ##  1 hmul
+  ##  0 <= r <= 1000
+  let dist: uint32 = r - (delta div 2) + (smallDivisor div 2)
+  let distQ: uint32 = dist div 100
+  ##  1 mul
+  ##   const uint32_t dist_r = dist % 100;
+  q += distQ
+  ##   if /*likely*/ (dist_r == 0)
+  if dist == distQ * 100:
+    ##       const bool approx_y_parity = ((dist ^ (SmallDivisor / 2)) & 1) != 0;
+    let approxYParity: bool = (dist and 1) != 0
+    ##  Check z^(f) >= epsilon^(f)
+    ##  We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1,
+    ##  where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f)
+    ##  Since there are only 2 possibilities, we only need to care about the
+    ##  parity. Also, zi and r should have the same parity since the divisor
+    ##  is an even number
+    if mulParity(twoFc, pow10, betaMinus1) != approxYParity:
+      dec(q)
+    elif q mod 2 != 0 and isIntegralMidpoint(twoFc, e2, minusK):
+      dec(q)
+  return FloatingDecimal64(significand: q, exponent: minusK + kappa)
+
+# ==================================================================================================
+#  ToChars
+# ==================================================================================================
+
+when false:
+  template `+!`(x: cstring; offset: int): cstring = cast[cstring](cast[uint](x) + uint(offset))
+
+  template dec(x: cstring; offset=1) = x = cast[cstring](cast[uint](x) - uint(offset))
+  template inc(x: cstring; offset=1) = x = cast[cstring](cast[uint](x) + uint(offset))
+
+  proc memset(x: cstring; ch: char; L: int) {.importc, nodecl.}
+  proc memmove(a, b: cstring; L: int) {.importc, nodecl.}
+
+proc utoa8DigitsSkipTrailingZeros*(buf: var openArray[char]; pos: int; digits: uint32): int {.inline.} =
+  dragonbox_Assert(digits >= 1)
+  dragonbox_Assert(digits <= 99999999'u32)
+  let q: uint32 = digits div 10000
+  let r: uint32 = digits mod 10000
+  let qH: uint32 = q div 100
+  let qL: uint32 = q mod 100
+  utoa2Digits(buf, pos, qH)
+  utoa2Digits(buf, pos + 2, qL)
+  if r == 0:
+    return trailingZeros2Digits(if qL == 0: qH else: qL) + (if qL == 0: 6 else: 4)
+  else:
+    let rH: uint32 = r div 100
+    let rL: uint32 = r mod 100
+    utoa2Digits(buf, pos + 4, rH)
+    utoa2Digits(buf, pos + 6, rL)
+    return trailingZeros2Digits(if rL == 0: rH else: rL) + (if rL == 0: 2 else: 0)
+
+proc printDecimalDigitsBackwards*(buf: var openArray[char]; pos: int; output64: uint64): int {.inline.} =
+  var pos = pos
+  var output64 = output64
+  var tz = 0
+  ##  number of trailing zeros removed.
+  var nd = 0
+  ##  number of decimal digits processed.
+  ##  At most 17 digits remaining
+  if output64 >= 100000000'u64:
+    let q: uint64 = output64 div 100000000'u64
+    let r: uint32 = cast[uint32](output64 mod 100000000'u64)
+    output64 = q
+    dec(pos, 8)
+    if r != 0:
+      tz = utoa8DigitsSkipTrailingZeros(buf, pos, r)
+      dragonbox_Assert(tz >= 0)
+      dragonbox_Assert(tz <= 7)
+    else:
+      tz = 8
+    nd = 8
+  dragonbox_Assert(output64 <= high(uint32))
+  var output = cast[uint32](output64)
+  if output >= 10000:
+    let q: uint32 = output div 10000
+    let r: uint32 = output mod 10000
+    output = q
+    dec(pos, 4)
+    if r != 0:
+      let rH: uint32 = r div 100
+      let rL: uint32 = r mod 100
+      utoa2Digits(buf, pos, rH)
+      utoa2Digits(buf, pos + 2, rL)
+      if tz == nd:
+        inc(tz, trailingZeros2Digits(if rL == 0: rH else: rL) +
+            (if rL == 0: 2 else: 0))
+    else:
+      if tz == nd:
+        inc(tz, 4)
+      else:
+        for i in 0..3: buf[pos+i] = '0'
+      ##  (actually not required...)
+    inc(nd, 4)
+  if output >= 100:
+    let q: uint32 = output div 100
+    let r: uint32 = output mod 100
+    output = q
+    dec(pos, 2)
+    utoa2Digits(buf, pos, r)
+    if tz == nd:
+      inc(tz, trailingZeros2Digits(r))
+    inc(nd, 2)
+    if output >= 100:
+      let q2: uint32 = output div 100
+      let r2: uint32 = output mod 100
+      output = q2
+      dec(pos, 2)
+      utoa2Digits(buf, pos, r2)
+      if tz == nd:
+        inc(tz, trailingZeros2Digits(r2))
+      inc(nd, 2)
+  dragonbox_Assert(output >= 1)
+  dragonbox_Assert(output <= 99)
+  if output >= 10:
+    let q: uint32 = output
+    dec(pos, 2)
+    utoa2Digits(buf, pos, q)
+    if tz == nd:
+      inc(tz, trailingZeros2Digits(q))
+  else:
+    let q: uint32 = output
+    dragonbox_Assert(q >= 1)
+    dragonbox_Assert(q <= 9)
+    dec(pos)
+    buf[pos] = chr(ord('0') + q)
+  return tz
+
+proc decimalLength*(v: uint64): int {.inline.} =
+  dragonbox_Assert(v >= 1)
+  dragonbox_Assert(v <= 99999999999999999'u64)
+  if cast[uint32](v shr 32) != 0:
+    if v >= 10000000000000000'u64:
+      return 17
+    if v >= 1000000000000000'u64:
+      return 16
+    if v >= 100000000000000'u64:
+      return 15
+    if v >= 10000000000000'u64:
+      return 14
+    if v >= 1000000000000'u64:
+      return 13
+    if v >= 100000000000'u64:
+      return 12
+    if v >= 10000000000'u64:
+      return 11
+    return 10
+  let v32: uint32 = cast[uint32](v)
+  if v32 >= 1000000000'u32:
+    return 10
+  if v32 >= 100000000'u32:
+    return 9
+  if v32 >= 10000000'u32:
+    return 8
+  if v32 >= 1000000'u32:
+    return 7
+  if v32 >= 100000'u32:
+    return 6
+  if v32 >= 10000'u32:
+    return 5
+  if v32 >= 1000'u32:
+    return 4
+  if v32 >= 100'u32:
+    return 3
+  if v32 >= 10'u32:
+    return 2
+  return 1
+
+proc formatDigits*[T: Ordinal](buffer: var openArray[char]; pos: T; digits: uint64; decimalExponent: int;
+                  forceTrailingDotZero = false): int {.inline.} =
+  const
+    minFixedDecimalPoint = -6
+  const
+    maxFixedDecimalPoint = 17
+  var pos:int = pos.int
+  assert(minFixedDecimalPoint <= -1, "internal error")
+  assert(maxFixedDecimalPoint >= 17, "internal error")
+  dragonbox_Assert(digits >= 1)
+  dragonbox_Assert(digits <= 99999999999999999'u64)
+  dragonbox_Assert(decimalExponent >= -999)
+  dragonbox_Assert(decimalExponent <= 999)
+  var numDigits = decimalLength(digits)
+  let decimalPoint = numDigits + decimalExponent
+  let useFixed: bool = minFixedDecimalPoint <= decimalPoint and
+      decimalPoint <= maxFixedDecimalPoint
+  ## Prepare the buffer.
+  for i in 0..<32: buffer[pos+i] = '0'
+  assert(minFixedDecimalPoint >= -30, "internal error")
+  assert(maxFixedDecimalPoint <= 32, "internal error")
+  var decimalDigitsPosition: int
+  if useFixed:
+    if decimalPoint <= 0:
+      ##  0.[000]digits
+      decimalDigitsPosition = 2 - decimalPoint
+    else:
+      ##  dig.its
+      ##  digits[000]
+      decimalDigitsPosition = 0
+  else:
+    ##  dE+123 or d.igitsE+123
+    decimalDigitsPosition = 1
+  var digitsEnd = pos + int(decimalDigitsPosition + numDigits)
+  let tz = printDecimalDigitsBackwards(buffer, digitsEnd, digits)
+  dec(digitsEnd, tz)
+  dec(numDigits, tz)
+  ##   decimal_exponent += tz; // => decimal_point unchanged.
+  if useFixed:
+    if decimalPoint <= 0:
+      ##  0.[000]digits
+      buffer[pos+1] = '.'
+      pos = digitsEnd
+    elif decimalPoint < numDigits:
+      ##  dig.its
+      when true: #defined(vcc) and not defined(clang):
+        ##  VC does not inline the memmove call below. (Even if compiled with /arch:AVX2.)
+        ##  However, memcpy will be inlined.
+        var tmp: array[16, char]
+        for i in 0..<16: tmp[i] = buffer[i+pos+decimalPoint]
+        for i in 0..<16: buffer[i+pos+decimalPoint+1] = tmp[i]
+      else:
+        memmove(buffer +! (decimalPoint + 1), buffer +! decimalPoint, 16)
+      buffer[pos+decimalPoint] = '.'
+      pos = digitsEnd + 1
+    else:
+      ##  digits[000]
+      inc(pos, decimalPoint)
+      if forceTrailingDotZero:
+        buffer[pos] = '.'
+        buffer[pos+1] = '0'
+        inc(pos, 2)
+  else:
+    ##  Copy the first digit one place to the left.
+    buffer[pos] = buffer[pos+1]
+    if numDigits == 1:
+      ##  dE+123
+      inc(pos)
+    else:
+      ##  d.igitsE+123
+      buffer[pos+1] = '.'
+      pos = digitsEnd
+    let scientificExponent: int = decimalPoint - 1
+    ##       SF_ASSERT(scientific_exponent != 0);
+    buffer[pos] = 'e'
+    buffer[pos+1] = if scientificExponent < 0: '-' else: '+'
+    inc(pos, 2)
+    let k: uint32 = cast[uint32](if scientificExponent < 0: -scientificExponent else: scientificExponent)
+    if k < 10:
+      buffer[pos] = chr(ord('0') + k)
+      inc(pos)
+    elif k < 100:
+      utoa2Digits(buffer, pos, k)
+      inc(pos, 2)
+    else:
+      let q: uint32 = k div 100
+      let r: uint32 = k mod 100
+      buffer[pos] = chr(ord('0') + q)
+      inc(pos)
+      utoa2Digits(buffer, pos, r)
+      inc(pos, 2)
+  return pos
+
+proc toChars*(buffer: var openArray[char]; v: float; forceTrailingDotZero = false): int {.
+    inline.} =
+  var pos = 0
+  let significand: uint64 = physicalSignificand(constructDouble(v))
+  let exponent: uint64 = physicalExponent(constructDouble(v))
+  if exponent != maxIeeeExponent:
+    ##  Finite
+    buffer[pos] = '-'
+    inc(pos, signBit(constructDouble(v)))
+    if exponent != 0 or significand != 0:
+      ##  != 0
+      let dec = toDecimal64(significand, exponent)
+      return formatDigits(buffer, pos, dec.significand, dec.exponent.int,
+                         forceTrailingDotZero)
+    else:
+      buffer[pos] = '0'
+      buffer[pos+1] = '.'
+      buffer[pos+2] = '0'
+      buffer[pos+3] = ' '
+      inc(pos, if forceTrailingDotZero: 3 else: 1)
+      return pos
+  if significand == 0:
+    buffer[pos] = '-'
+    inc(pos, signBit(constructDouble(v)))
+    buffer[pos] = 'i'
+    buffer[pos+1] = 'n'
+    buffer[pos+2] = 'f'
+    buffer[pos+3] = ' '
+    return pos + 3
+  else:
+    buffer[pos] = 'n'
+    buffer[pos+1] = 'a'
+    buffer[pos+2] = 'n'
+    buffer[pos+3] = ' '
+    return pos + 3
+
+when false:
+  proc toString*(value: float): string =
+    var buffer: array[dtoaMinBufferLength, char]
+    let last = toChars(addr buffer, value)
+    let L = cast[int](last) - cast[int](addr(buffer))
+    result = newString(L)
+    copyMem(addr result[0], addr buffer[0], L)
+
diff --git a/lib/std/private/gitutils.nim b/lib/std/private/gitutils.nim
new file mode 100644
index 000000000..6dc9c8f3b
--- /dev/null
+++ b/lib/std/private/gitutils.nim
@@ -0,0 +1,74 @@
+##[
+internal API for now, API subject to change
+]##
+
+# xxx move other git utilities here; candidate for stdlib.
+
+import std/[os, paths, osproc, strutils, tempfiles]
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
+
+const commitHead* = "HEAD"
+
+template retryCall*(maxRetry = 3, backoffDuration = 1.0, call: untyped): bool =
+  ## Retry `call` up to `maxRetry` times with exponential backoff and initial
+  ## duraton of `backoffDuration` seconds.
+  ## This is in particular useful for network commands that can fail.
+  runnableExamples:
+    doAssert not retryCall(maxRetry = 2, backoffDuration = 0.1, false)
+    var i = 0
+    doAssert: retryCall(maxRetry = 3, backoffDuration = 0.1, (i.inc; i >= 3))
+    doAssert retryCall(call = true)
+  var result = false
+  var t = backoffDuration
+  for i in 0..<maxRetry:
+    if call:
+      result = true
+      break
+    if i == maxRetry - 1: break
+    sleep(int(t * 1000))
+    t = t * 2 # exponential backoff
+  result
+
+proc isGitRepo*(dir: string): bool =
+  ## Avoid calling git since it depends on /bin/sh existing and fails in Nix.
+  return fileExists(dir/".git/HEAD")
+
+proc diffFiles*(path1, path2: string): tuple[output: string, same: bool] =
+  ## Returns a human readable diff of files `path1`, `path2`, the exact form of
+  ## which is implementation defined.
+  # This could be customized, e.g. non-git diff with `diff -uNdr`, or with
+  # git diff options (e.g. --color-moved, --word-diff).
+  # in general, `git diff` has more options than `diff`.
+  var status = 0
+  (result.output, status) = execCmdEx("git diff --no-index $1 $2" % [path1.quoteShell, path2.quoteShell])
+  doAssert (status == 0) or (status == 1)
+  result.same = status == 0
+
+proc diffStrings*(a, b: string): tuple[output: string, same: bool] =
+  ## Returns a human readable diff of `a`, `b`, the exact form of which is
+  ## implementation defined.
+  ## See also `experimental.diff`.
+  runnableExamples:
+    let a = "ok1\nok2\nok3\n"
+    let b = "ok1\nok2 alt\nok3\nok4\n"
+    let (c, same) = diffStrings(a, b)
+    doAssert not same
+    let (c2, same2) = diffStrings(a, a)
+    doAssert same2
+  runnableExamples("-r:off"):
+    let a = "ok1\nok2\nok3\n"
+    let b = "ok1\nok2 alt\nok3\nok4\n"
+    echo diffStrings(a, b).output
+
+  template tmpFileImpl(prefix, str): auto =
+    let path = genTempPath(prefix, "")
+    writeFile(path, str)
+    path
+  let patha = tmpFileImpl("diffStrings_a_", a)
+  let pathb = tmpFileImpl("diffStrings_b_", b)
+  defer:
+    removeFile(patha)
+    removeFile(pathb)
+  result = diffFiles(patha, pathb)
diff --git a/lib/std/private/globs.nim b/lib/std/private/globs.nim
new file mode 100644
index 000000000..a6d088558
--- /dev/null
+++ b/lib/std/private/globs.nim
@@ -0,0 +1,70 @@
+##[
+unstable API, internal use only for now.
+this can eventually be moved to std/os and `walkDirRec` can be implemented in terms of this
+to avoid duplication
+]##
+
+import std/os
+when defined(windows):
+  from std/strutils import replace
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, objectdollar]
+
+
+when defined(nimHasEffectsOf):
+  {.experimental: "strictEffects".}
+else:
+  {.pragma: effectsOf.}
+
+type
+  PathEntry* = object
+    kind*: PathComponent
+    path*: string
+
+iterator walkDirRecFilter*(dir: string, follow: proc(entry: PathEntry): bool = nil,
+    relative = false, checkDir = true): PathEntry {.tags: [ReadDirEffect], effectsOf: follow.} =
+  ## Improved `os.walkDirRec`.
+  #[
+  note: a yieldFilter isn't needed because caller can filter at call site, without
+  loss of generality, unlike `follow`.
+
+  Future work:
+  * need to document
+  * add a `sort` option, which can be implemented efficiently only here, not at call site.
+  * provide a way to do error reporting, which is tricky because iteration cannot be resumed
+  ]#
+  var stack = @["."]
+  var checkDir = checkDir
+  var entry: PathEntry
+  while stack.len > 0:
+    let d = stack.pop()
+    for k, p in walkDir(dir / d, relative = true, checkDir = checkDir):
+      let rel = d / p
+      entry.kind = k
+      if relative: entry.path = rel
+      else: entry.path = dir / rel
+      if k in {pcDir, pcLinkToDir}:
+        if follow == nil or follow(entry): stack.add rel
+      yield entry
+    checkDir = false
+      # We only check top-level dir, otherwise if a subdir is invalid (eg. wrong
+      # permissions), it'll abort iteration and there would be no way to
+      # continue iteration.
+
+proc nativeToUnixPath*(path: string): string =
+  # pending https://github.com/nim-lang/Nim/pull/13265
+  result = path
+  when defined(windows):
+    if path.len >= 2 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':':
+      result[0] = '/'
+      result[1] = path[0]
+      if path.len > 2 and path[2] != '\\':
+        raiseAssert "paths like `C:foo` are currently unsupported, path: " & path
+  when DirSep == '\\':
+    result = replace(result, '\\', '/')
+
+when isMainModule:
+  import std/sugar
+  for a in walkDirRecFilter(".", follow = a=>a.path.lastPathPart notin ["nimcache", ".git", "csources_v1", "csources", "bin"]):
+    echo a
diff --git a/lib/std/private/jsutils.nim b/lib/std/private/jsutils.nim
new file mode 100644
index 000000000..5f79eab27
--- /dev/null
+++ b/lib/std/private/jsutils.nim
@@ -0,0 +1,96 @@
+when defined(js):
+  import std/jsbigints
+
+  type
+    ArrayBuffer* = ref object of JsRoot
+    Float64Array* = ref object of JsRoot
+    Uint32Array* = ref object of JsRoot
+    Uint8Array* = ref object of JsRoot
+    BigUint64Array* = ref object of JsRoot
+
+
+  func newArrayBuffer*(n: int): ArrayBuffer {.importjs: "new ArrayBuffer(#)".}
+  func newFloat64Array*(buffer: ArrayBuffer): Float64Array {.importjs: "new Float64Array(#)".}
+  func newUint32Array*(buffer: ArrayBuffer): Uint32Array {.importjs: "new Uint32Array(#)".}
+  func newBigUint64Array*(buffer: ArrayBuffer): BigUint64Array {.importjs: "new BigUint64Array(#)".}
+
+  func newUint8Array*(n: int): Uint8Array {.importjs: "new Uint8Array(#)".}
+
+  func `[]`*(arr: Uint32Array, i: int): uint32 {.importjs: "#[#]".}
+  func `[]`*(arr: Uint8Array, i: int): uint8 {.importjs: "#[#]".}
+  func `[]`*(arr: BigUint64Array, i: int): JsBigInt {.importjs: "#[#]".}
+  func `[]=`*(arr: Float64Array, i: int, v: float) {.importjs: "#[#] = #".}
+
+  proc jsTypeOf*[T](x: T): cstring {.importjs: "typeof(#)".} =
+    ## Returns the name of the JsObject's JavaScript type as a cstring.
+    # xxx replace jsffi.jsTypeOf with this definition and add tests
+    runnableExamples:
+      import std/[jsffi, jsbigints]
+      assert jsTypeOf(1.toJs) == "number"
+      assert jsTypeOf(false.toJs) == "boolean"
+      assert [1].toJs.jsTypeOf == "object" # note the difference with `getProtoName`
+      assert big"1".toJs.jsTypeOf == "bigint"
+
+  proc jsConstructorName*[T](a: T): cstring =
+    runnableExamples:
+      import std/jsffi
+      let a = array[2, float64].default
+      assert jsConstructorName(a) == "Float64Array"
+      assert jsConstructorName(a.toJs) == "Float64Array"
+    {.emit: """`result` = `a`.constructor.name;""".}
+
+  proc hasJsBigInt*(): bool =
+    {.emit: """`result` = typeof BigInt != 'undefined';""".}
+
+  proc hasBigUint64Array*(): bool =
+    {.emit: """`result` = typeof BigUint64Array != 'undefined';""".}
+
+  proc getProtoName*[T](a: T): cstring {.importjs: "Object.prototype.toString.call(#)".} =
+    runnableExamples:
+      import std/[jsffi, jsbigints]
+      type A = ref object
+      assert 1.toJs.getProtoName == "[object Number]"
+      assert "a".toJs.getProtoName == "[object String]"
+      assert big"1".toJs.getProtoName == "[object BigInt]"
+      assert false.toJs.getProtoName == "[object Boolean]"
+      assert (a: 1).toJs.getProtoName == "[object Object]"
+      assert A.default.toJs.getProtoName == "[object Null]"
+      assert [1].toJs.getProtoName == "[object Int32Array]" # implementation defined
+      assert @[1].toJs.getProtoName == "[object Array]" # ditto
+
+  const maxSafeInteger* = 9007199254740991
+    ## The same as `Number.MAX_SAFE_INTEGER` or `2^53 - 1`.
+    ## See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER
+  runnableExamples:
+    let a {.importjs: "Number.MAX_SAFE_INTEGER".}: int64
+    assert a == maxSafeInteger
+
+  proc isInteger*[T](x: T): bool {.importjs: "Number.isInteger(#)".} =
+    runnableExamples:
+      import std/jsffi
+      assert 1.isInteger
+      assert not 1.5.isInteger
+      assert 1.toJs.isInteger
+      assert not 1.5.toJs.isInteger
+
+  proc isSafeInteger*[T](x: T): bool {.importjs: "Number.isSafeInteger(#)".} =
+    runnableExamples:
+      import std/jsffi
+      assert not "123".toJs.isSafeInteger
+      assert 123.isSafeInteger
+      assert 123.toJs.isSafeInteger
+      when false:
+        assert 9007199254740991.toJs.isSafeInteger
+        assert not 9007199254740992.toJs.isSafeInteger
+
+template whenJsNoBigInt64*(no64, yes64): untyped =
+  when defined(js):
+    when compiles(compileOption("jsbigint64")):
+      when compileOption("jsbigint64"):
+        yes64
+      else:
+        no64
+    else:
+      no64
+  else:
+    no64
diff --git a/lib/std/private/miscdollars.nim b/lib/std/private/miscdollars.nim
new file mode 100644
index 000000000..06fda6fa1
--- /dev/null
+++ b/lib/std/private/miscdollars.nim
@@ -0,0 +1,39 @@
+from std/private/digitsutils import addInt
+
+template toLocation*(result: var string, file: string | cstring, line: int, col: int) =
+  ## avoids spurious allocations
+  # Hopefully this can be re-used everywhere so that if a user needs to customize,
+  # it can be done in a single place.
+  result.add file
+  if line > 0:
+    result.add "("
+    addInt(result, line)
+    if col > 0:
+      result.add ", "
+      addInt(result, col)
+    result.add ")"
+
+proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".}
+
+template tupleObjectDollar*[T: tuple | object](result: var string, x: T) =
+  result = "("
+  const isNamed = T is object or isNamedTuple(typeof(T))
+  var count {.used.} = 0
+  for name, value in fieldPairs(x):
+    if count > 0: result.add(", ")
+    when isNamed:
+      result.add(name)
+      result.add(": ")
+    count.inc
+    when compiles($value):
+      when value isnot string and value isnot seq and compiles(value.isNil):
+        if value.isNil: result.add "nil"
+        else: result.addQuoted(value)
+      else:
+        result.addQuoted(value)
+    else:
+      result.add("...")
+  when not isNamed:
+    if count == 1:
+      result.add(",") # $(1,) should print as the semantically legal (1,)
+  result.add(")")
diff --git a/lib/std/private/ntpath.nim b/lib/std/private/ntpath.nim
new file mode 100644
index 000000000..7c8661bb7
--- /dev/null
+++ b/lib/std/private/ntpath.nim
@@ -0,0 +1,61 @@
+# This module is inspired by Python's `ntpath.py` module.
+
+import std/[
+  strutils,
+]
+
+# Adapted `splitdrive` function from the following commits in Python source
+# code:
+# 5a607a3ee5e81bdcef3f886f9d20c1376a533df4 (2009): Initial UNC handling (by Mark Hammond)
+# 2ba0fd5767577954f331ecbd53596cd8035d7186 (2022): Support for "UNC"-device paths (by Barney Gale)
+#
+# FAQ: Why use `strip` below? `\\?\UNC` is the start of a "UNC symbolic link",
+# which is a special UNC form. Running `strip` differentiates `\\?\UNC\` (a UNC
+# symbolic link) from e.g. `\\?\UNCD` (UNCD is the server in the UNC path).
+func splitDrive*(p: string): tuple[drive, path: string] =
+  ## Splits a Windows path into a drive and path part. The drive can be e.g.
+  ## `C:`. It can also be a UNC path (`\\server\drive\path`).
+  ##
+  ## The equivalent `splitDrive` for POSIX systems always returns empty drive.
+  ## Therefore this proc is only necessary on DOS-like file systems (together
+  ## with Nim's `doslikeFileSystem` conditional variable).
+  ##
+  ## This proc's use case is to extract `path` such that it can be manipulated
+  ## like a POSIX path.
+  runnableExamples:
+    doAssert splitDrive("C:") == ("C:", "")
+    doAssert splitDrive(r"C:\") == (r"C:", r"\")
+    doAssert splitDrive(r"\\server\drive\foo\bar") == (r"\\server\drive", r"\foo\bar")
+    doAssert splitDrive(r"\\?\UNC\server\share\dir") == (r"\\?\UNC\server\share", r"\dir")
+
+  result = ("", p)
+  if p.len < 2:
+    return
+  const sep = '\\'
+  let normp = p.replace('/', sep)
+  if p.len > 2 and normp[0] == sep and normp[1] == sep and normp[2] != sep:
+
+    # is a UNC path:
+    # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path
+    # \\machine\mountpoint\directory\etc\...
+    #           directory ^^^^^^^^^^^^^^^
+    let start = block:
+      const unc = "\\\\?\\UNC" # Length is 7
+      let idx = min(8, normp.len)
+      if unc == normp[0..<idx].strip(chars = {sep}, leading = false).toUpperAscii:
+        8
+      else:
+        2
+    let index = normp.find(sep, start)
+    if index == -1:
+      return
+    var index2 = normp.find(sep, index + 1)
+
+    # a UNC path can't have two slashes in a row (after the initial two)
+    if index2 == index + 1:
+      return
+    if index2 == -1:
+      index2 = p.len
+    return (p[0..<index2], p[index2..^1])
+  if p[1] == ':':
+    return (p[0..1], p[2..^1])
diff --git a/lib/std/private/osappdirs.nim b/lib/std/private/osappdirs.nim
new file mode 100644
index 000000000..07a6809bb
--- /dev/null
+++ b/lib/std/private/osappdirs.nim
@@ -0,0 +1,176 @@
+## .. importdoc:: paths.nim, dirs.nim
+
+include system/inclrtl
+import std/envvars
+import std/private/ospaths2
+
+proc getHomeDir*(): string {.rtl, extern: "nos$1",
+  tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Returns the home directory of the current user.
+  ##
+  ## This proc is wrapped by the `expandTilde proc`_
+  ## for the convenience of processing paths coming from user configuration files.
+  ##
+  ## See also:
+  ## * `getDataDir proc`_
+  ## * `getConfigDir proc`_
+  ## * `getTempDir proc`_
+  ## * `expandTilde proc`_
+  ## * `getCurrentDir proc`_
+  ## * `setCurrentDir proc`_
+  runnableExamples:
+    import std/os
+    assert getHomeDir() == expandTilde("~")
+
+  when defined(windows): return getEnv("USERPROFILE") & "\\"
+  else: return getEnv("HOME") & "/"
+
+proc getDataDir*(): string {.rtl, extern: "nos$1"
+  tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Returns the data directory of the current user for applications.
+  ## 
+  ## On non-Windows OSs, this proc conforms to the XDG Base Directory
+  ## spec. Thus, this proc returns the value of the `XDG_DATA_HOME` environment
+  ## variable if it is set, otherwise it returns the default configuration
+  ## directory ("~/.local/share" or "~/Library/Application Support" on macOS).
+  ## 
+  ## See also:
+  ## * `getHomeDir proc`_
+  ## * `getConfigDir proc`_
+  ## * `getTempDir proc`_
+  ## * `expandTilde proc`_
+  ## * `getCurrentDir proc`_
+  ## * `setCurrentDir proc`_
+  when defined(windows):
+    result = getEnv("APPDATA")
+  elif defined(macosx):
+    result = getEnv("XDG_DATA_HOME", getEnv("HOME") / "Library" / "Application Support")
+  else:
+    result = getEnv("XDG_DATA_HOME", getEnv("HOME") / ".local" / "share")
+  result.normalizePathEnd(trailingSep = true)
+
+proc getConfigDir*(): string {.rtl, extern: "nos$1",
+  tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Returns the config directory of the current user for applications.
+  ##
+  ## On non-Windows OSs, this proc conforms to the XDG Base Directory
+  ## spec. Thus, this proc returns the value of the `XDG_CONFIG_HOME` environment
+  ## variable if it is set, otherwise it returns the default configuration
+  ## directory ("~/.config/").
+  ##
+  ## An OS-dependent trailing slash is always present at the end of the
+  ## returned string: `\\` on Windows and `/` on all other OSs.
+  ##
+  ## See also:
+  ## * `getHomeDir proc`_
+  ## * `getDataDir proc`_
+  ## * `getTempDir proc`_
+  ## * `expandTilde proc`_
+  ## * `getCurrentDir proc`_
+  ## * `setCurrentDir proc`_
+  when defined(windows):
+    result = getEnv("APPDATA")
+  else:
+    result = getEnv("XDG_CONFIG_HOME", getEnv("HOME") / ".config")
+  result.normalizePathEnd(trailingSep = true)
+
+proc getCacheDir*(): string =
+  ## Returns the cache directory of the current user for applications.
+  ##
+  ## This makes use of the following environment variables:
+  ##
+  ## * On Windows: `getEnv("LOCALAPPDATA")`
+  ##
+  ## * On macOS: `getEnv("XDG_CACHE_HOME", getEnv("HOME") / "Library/Caches")`
+  ##
+  ## * On other platforms: `getEnv("XDG_CACHE_HOME", getEnv("HOME") / ".cache")`
+  ##
+  ## **See also:**
+  ## * `getHomeDir proc`_
+  ## * `getTempDir proc`_
+  ## * `getConfigDir proc`_
+  ## * `getDataDir proc`_
+  # follows https://crates.io/crates/platform-dirs
+  when defined(windows):
+    result = getEnv("LOCALAPPDATA")
+  elif defined(osx):
+    result = getEnv("XDG_CACHE_HOME", getEnv("HOME") / "Library/Caches")
+  else:
+    result = getEnv("XDG_CACHE_HOME", getEnv("HOME") / ".cache")
+  result.normalizePathEnd(false)
+
+proc getCacheDir*(app: string): string =
+  ## Returns the cache directory for an application `app`.
+  ##
+  ## * On Windows, this uses: `getCacheDir() / app / "cache"`
+  ## * On other platforms, this uses: `getCacheDir() / app`
+  when defined(windows):
+    getCacheDir() / app / "cache"
+  else:
+    getCacheDir() / app
+
+
+when defined(windows):
+  type DWORD = uint32
+
+  when defined(nimPreviewSlimSystem):
+    import std/widestrs
+
+  proc getTempPath(
+    nBufferLength: DWORD, lpBuffer: WideCString
+  ): DWORD {.stdcall, dynlib: "kernel32.dll", importc: "GetTempPathW".} =
+    ## Retrieves the path of the directory designated for temporary files.
+
+template getEnvImpl(result: var string, tempDirList: openArray[string]) =
+  for dir in tempDirList:
+    if existsEnv(dir):
+      result = getEnv(dir)
+      break
+
+template getTempDirImpl(result: var string) =
+  when defined(windows):
+    getEnvImpl(result, ["TMP", "TEMP", "USERPROFILE"])
+  else:
+    getEnvImpl(result, ["TMPDIR", "TEMP", "TMP", "TEMPDIR"])
+
+proc getTempDir*(): string {.rtl, extern: "nos$1",
+  tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Returns the temporary directory of the current user for applications to
+  ## save temporary files in.
+  ##
+  ## On Windows, it calls [GetTempPath](https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppathw).
+  ## On Posix based platforms, it will check `TMPDIR`, `TEMP`, `TMP` and `TEMPDIR` environment variables in order.
+  ## On all platforms, `/tmp` will be returned if the procs fails.
+  ##
+  ## You can override this implementation
+  ## by adding `-d:tempDir=mytempname` to your compiler invocation.
+  ##
+  ## **Note:** This proc does not check whether the returned path exists.
+  ##
+  ## See also:
+  ## * `getHomeDir proc`_
+  ## * `getConfigDir proc`_
+  ## * `expandTilde proc`_
+  ## * `getCurrentDir proc`_
+  ## * `setCurrentDir proc`_
+  const tempDirDefault = "/tmp"
+  when defined(tempDir):
+    const tempDir {.strdefine.}: string = tempDirDefault
+    result = tempDir
+  else:
+    when nimvm:
+      getTempDirImpl(result)
+    else:
+      when defined(windows):
+        let size = getTempPath(0, nil)
+        # If the function fails, the return value is zero.
+        if size > 0:
+          let buffer = newWideCString(size.int)
+          if getTempPath(size, buffer) > 0:
+            result = $buffer
+      elif defined(android): result = "/data/local/tmp"
+      else:
+        getTempDirImpl(result)
+    if result.len == 0:
+      result = tempDirDefault
+  normalizePathEnd(result, trailingSep=true)
diff --git a/lib/std/private/oscommon.nim b/lib/std/private/oscommon.nim
new file mode 100644
index 000000000..c49d52ef2
--- /dev/null
+++ b/lib/std/private/oscommon.nim
@@ -0,0 +1,186 @@
+include system/inclrtl
+
+import std/[oserrors]
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions, widestrs]
+
+## .. importdoc:: osdirs.nim, os.nim
+
+const weirdTarget* = defined(nimscript) or defined(js)
+
+
+type
+  ReadDirEffect* = object of ReadIOEffect   ## Effect that denotes a read
+                                            ## operation from the directory
+                                            ## structure.
+  WriteDirEffect* = object of WriteIOEffect ## Effect that denotes a write
+                                            ## operation to
+                                            ## the directory structure.
+
+
+when weirdTarget:
+  discard
+elif defined(windows):
+  import std/[winlean, times]
+elif defined(posix):
+  import std/posix
+  proc c_rename(oldname, newname: cstring): cint {.
+    importc: "rename", header: "<stdio.h>".}
+else:
+  {.error: "OS module not ported to your operating system!".}
+
+
+when weirdTarget:
+  {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".}
+else:
+  {.pragma: noWeirdTarget.}
+
+
+when defined(nimscript):
+  # for procs already defined in scriptconfig.nim
+  template noNimJs(body): untyped = discard
+elif defined(js):
+  {.pragma: noNimJs, error: "this proc is not available on the js target".}
+else:
+  {.pragma: noNimJs.}
+
+
+when defined(windows) and not weirdTarget:
+  template wrapUnary*(varname, winApiProc, arg: untyped) =
+    var varname = winApiProc(newWideCString(arg))
+
+  template wrapBinary*(varname, winApiProc, arg, arg2: untyped) =
+    var varname = winApiProc(newWideCString(arg), arg2)
+  proc findFirstFile*(a: string, b: var WIN32_FIND_DATA): Handle =
+    result = findFirstFileW(newWideCString(a), b)
+  template findNextFile*(a, b: untyped): untyped = findNextFileW(a, b)
+
+  template getFilename*(f: untyped): untyped =
+    $cast[WideCString](addr(f.cFileName[0]))
+
+  proc skipFindData*(f: WIN32_FIND_DATA): bool {.inline.} =
+    # Note - takes advantage of null delimiter in the cstring
+    const dot = ord('.')
+    result = f.cFileName[0].int == dot and (f.cFileName[1].int == 0 or
+             f.cFileName[1].int == dot and f.cFileName[2].int == 0)
+
+
+type
+  PathComponent* = enum   ## Enumeration specifying a path component.
+    ##
+    ## See also:
+    ## * `walkDirRec iterator`_
+    ## * `FileInfo object`_
+    pcFile,               ## path refers to a file
+    pcLinkToFile,         ## path refers to a symbolic link to a file
+    pcDir,                ## path refers to a directory
+    pcLinkToDir           ## path refers to a symbolic link to a directory
+
+
+when defined(posix) and not weirdTarget:
+  proc getSymlinkFileKind*(path: string):
+      tuple[pc: PathComponent, isSpecial: bool] =
+    # Helper function.
+    var s: Stat
+    assert(path != "")
+    result = (pcLinkToFile, false)
+    if stat(path, s) == 0'i32:
+      if S_ISDIR(s.st_mode):
+        result = (pcLinkToDir, false)
+      elif not S_ISREG(s.st_mode):
+        result = (pcLinkToFile, true)
+
+proc tryMoveFSObject*(source, dest: string, isDir: bool): bool {.noWeirdTarget.} =
+  ## Moves a file (or directory if `isDir` is true) from `source` to `dest`.
+  ##
+  ## Returns false in case of `EXDEV` error or `AccessDeniedError` on Windows (if `isDir` is true).
+  ## In case of other errors `OSError` is raised.
+  ## Returns true in case of success.
+  when defined(windows):
+    let s = newWideCString(source)
+    let d = newWideCString(dest)
+    result = moveFileExW(s, d, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32
+  else:
+    result = c_rename(source, dest) == 0'i32
+
+  if not result:
+    let err = osLastError()
+    let isAccessDeniedError =
+      when defined(windows):
+        const AccessDeniedError = OSErrorCode(5)
+        isDir and err == AccessDeniedError
+      else:
+        err == EXDEV.OSErrorCode
+    if not isAccessDeniedError:
+      raiseOSError(err, $(source, dest))
+
+when not defined(windows):
+  const maxSymlinkLen* = 1024
+
+proc fileExists*(filename: string): bool {.rtl, extern: "nos$1",
+                                          tags: [ReadDirEffect], noNimJs, sideEffect.} =
+  ## Returns true if `filename` exists and is a regular file or symlink.
+  ##
+  ## Directories, device files, named pipes and sockets return false.
+  ##
+  ## See also:
+  ## * `dirExists proc`_
+  ## * `symlinkExists proc`_
+  when defined(windows):
+    wrapUnary(a, getFileAttributesW, filename)
+    if a != -1'i32:
+      result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32
+  else:
+    var res: Stat
+    return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
+
+
+proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect],
+                                     noNimJs, sideEffect.} =
+  ## Returns true if the directory `dir` exists. If `dir` is a file, false
+  ## is returned. Follows symlinks.
+  ##
+  ## See also:
+  ## * `fileExists proc`_
+  ## * `symlinkExists proc`_
+  when defined(windows):
+    wrapUnary(a, getFileAttributesW, dir)
+    if a != -1'i32:
+      result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32
+  else:
+    var res: Stat
+    result = stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
+
+
+proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
+                                          tags: [ReadDirEffect],
+                                          noWeirdTarget, sideEffect.} =
+  ## Returns true if the symlink `link` exists. Will return true
+  ## regardless of whether the link points to a directory or file.
+  ##
+  ## See also:
+  ## * `fileExists proc`_
+  ## * `dirExists proc`_
+  when defined(windows):
+    wrapUnary(a, getFileAttributesW, link)
+    if a != -1'i32:
+      # xxx see: bug #16784 (bug9); checking `IO_REPARSE_TAG_SYMLINK`
+      # may also be needed.
+      result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32
+  else:
+    var res: Stat
+    result = lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode)
+
+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:
+      flags = flags or FILE_FLAG_OPEN_REPARSE_POINT
+    let access = if writeAccess: GENERIC_WRITE else: 0'i32
+
+    result = createFileW(
+      newWideCString(path), access,
+      FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
+      nil, OPEN_EXISTING, flags, 0
+      )
diff --git a/lib/std/private/osdirs.nim b/lib/std/private/osdirs.nim
new file mode 100644
index 000000000..a44cad7d9
--- /dev/null
+++ b/lib/std/private/osdirs.nim
@@ -0,0 +1,570 @@
+## .. importdoc:: osfiles.nim, appdirs.nim, paths.nim
+
+include system/inclrtl
+import std/oserrors
+
+
+import ospaths2, osfiles
+import oscommon
+export dirExists, PathComponent
+
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions, widestrs]
+
+
+when weirdTarget:
+  discard
+elif defined(windows):
+  import std/[winlean, times]
+elif defined(posix):
+  import std/[posix, times]
+
+else:
+  {.error: "OS module not ported to your operating system!".}
+
+
+when weirdTarget:
+  {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".}
+else:
+  {.pragma: noWeirdTarget.}
+
+
+when defined(nimscript):
+  # for procs already defined in scriptconfig.nim
+  template noNimJs(body): untyped = discard
+elif defined(js):
+  {.pragma: noNimJs, error: "this proc is not available on the js target".}
+else:
+  {.pragma: noNimJs.}
+
+# Templates for filtering directories and files
+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 {.dirty.} =
+    dirExists(f)
+  template isFile(f: string): bool {.dirty.} =
+    fileExists(f)
+
+template defaultWalkFilter(item): bool =
+  ## Walk filter used to return true on both
+  ## files and directories
+  true
+
+template walkCommon(pattern: string, filter) =
+  ## Common code for getting the files and directories with the
+  ## specified `pattern`
+  when defined(windows):
+    var
+      f: WIN32_FIND_DATA
+      res: int
+    res = findFirstFile(pattern, f)
+    if res != -1:
+      defer: findClose(res)
+      let dotPos = searchExtPos(pattern)
+      while true:
+        if not skipFindData(f) and filter(f):
+          # Windows bug/gotcha: 't*.nim' matches 'tfoo.nims' -.- so we check
+          # that the file extensions have the same length ...
+          let ff = getFilename(f)
+          let idx = ff.len - pattern.len + dotPos
+          if dotPos < 0 or idx >= ff.len or (idx >= 0 and ff[idx] == '.') or
+              (dotPos >= 0 and dotPos+1 < pattern.len and pattern[dotPos+1] == '*'):
+            yield splitFile(pattern).dir / extractFilename(ff)
+        if findNextFile(res, f) == 0'i32:
+          let errCode = getLastError()
+          if errCode == ERROR_NO_MORE_FILES: break
+          else: raiseOSError(errCode.OSErrorCode)
+  else: # here we use glob
+    var
+      f: Glob
+      res: int
+    f.gl_offs = 0
+    f.gl_pathc = 0
+    f.gl_pathv = nil
+    res = glob(pattern, 0, nil, addr(f))
+    defer: globfree(addr(f))
+    if res == 0:
+      for i in 0.. f.gl_pathc - 1:
+        assert(f.gl_pathv[i] != nil)
+        let path = $f.gl_pathv[i]
+        if filter(path):
+          yield path
+
+iterator walkPattern*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarget.} =
+  ## Iterate over all the files and directories that match the `pattern`.
+  ##
+  ## On POSIX this uses the `glob`:idx: call.
+  ## `pattern` is OS dependent, but at least the `"*.ext"`
+  ## notation is supported.
+  ##
+  ## See also:
+  ## * `walkFiles iterator`_
+  ## * `walkDirs iterator`_
+  ## * `walkDir iterator`_
+  ## * `walkDirRec iterator`_
+  runnableExamples:
+    import std/os
+    import std/sequtils
+    let paths = toSeq(walkPattern("lib/pure/*")) # works on Windows too
+    assert "lib/pure/concurrency".unixToNativePath in paths
+    assert "lib/pure/os.nim".unixToNativePath in paths
+  walkCommon(pattern, defaultWalkFilter)
+
+iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarget.} =
+  ## Iterate over all the files that match the `pattern`.
+  ##
+  ## On POSIX this uses the `glob`:idx: call.
+  ## `pattern` is OS dependent, but at least the `"*.ext"`
+  ## notation is supported.
+  ##
+  ## See also:
+  ## * `walkPattern iterator`_
+  ## * `walkDirs iterator`_
+  ## * `walkDir iterator`_
+  ## * `walkDirRec iterator`_
+  runnableExamples:
+    import std/os
+    import std/sequtils
+    assert "lib/pure/os.nim".unixToNativePath in toSeq(walkFiles("lib/pure/*.nim")) # works on Windows too
+  walkCommon(pattern, isFile)
+
+iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarget.} =
+  ## Iterate over all the directories that match the `pattern`.
+  ##
+  ## On POSIX this uses the `glob`:idx: call.
+  ## `pattern` is OS dependent, but at least the `"*.ext"`
+  ## notation is supported.
+  ##
+  ## See also:
+  ## * `walkPattern iterator`_
+  ## * `walkFiles iterator`_
+  ## * `walkDir iterator`_
+  ## * `walkDirRec iterator`_
+  runnableExamples:
+    import std/os
+    import std/sequtils
+    let paths = toSeq(walkDirs("lib/pure/*")) # works on Windows too
+    assert "lib/pure/concurrency".unixToNativePath in paths
+  walkCommon(pattern, isDir)
+
+proc staticWalkDir(dir: string; relative: bool): seq[
+                  tuple[kind: PathComponent, path: string]] =
+  discard
+
+iterator walkDir*(dir: string; relative = false, checkDir = false,
+                  skipSpecial = false):
+  tuple[kind: PathComponent, path: string] {.tags: [ReadDirEffect].} =
+  ## Walks over the directory `dir` and yields for each directory or file in
+  ## `dir`. The component type and full path for each item are returned.
+  ##
+  ## Walking is not recursive.
+  ## * If `relative` is true (default: false)
+  ##   the resulting path is shortened to be relative to ``dir``,
+  ##   otherwise the full path is returned.
+  ## * If `checkDir` is true, `OSError` is raised when `dir`
+  ##   doesn't exist.
+  ## * If `skipSpecial` is true, then (besides all directories) only *regular*
+  ##   files (**without** special "file" objects like FIFOs, device files,
+  ##   etc) will be yielded on Unix.
+  ##
+  ## **Example:**
+  ##
+  ## This directory structure:
+  ##
+  ##     dirA / dirB / fileB1.txt
+  ##          / dirC
+  ##          / fileA1.txt
+  ##          / fileA2.txt
+  ##
+  ## and this code:
+  runnableExamples("-r:off"):
+    import std/[strutils, sugar]
+    # note: order is not guaranteed
+    # this also works at compile time
+    assert collect(for k in walkDir("dirA"): k.path).join(" ") ==
+                          "dirA/dirB dirA/dirC dirA/fileA2.txt dirA/fileA1.txt"
+  ## See also:
+  ## * `walkPattern iterator`_
+  ## * `walkFiles iterator`_
+  ## * `walkDirs iterator`_
+  ## * `walkDirRec iterator`_
+
+  when nimvm:
+    for k, v in items(staticWalkDir(dir, relative)):
+      yield (k, v)
+  else:
+    when weirdTarget:
+      for k, v in items(staticWalkDir(dir, relative)):
+        yield (k, v)
+    elif defined(windows):
+      var f: WIN32_FIND_DATA
+      var h = findFirstFile(dir / "*", f)
+      if h == -1:
+        if checkDir:
+          raiseOSError(osLastError(), dir)
+      else:
+        defer: findClose(h)
+        while true:
+          var k = pcFile
+          if not skipFindData(f):
+            if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
+              k = pcDir
+            if (f.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
+              k = succ(k)
+            let xx = if relative: extractFilename(getFilename(f))
+                     else: dir / extractFilename(getFilename(f))
+            yield (k, xx)
+          if findNextFile(h, f) == 0'i32:
+            let errCode = getLastError()
+            if errCode == ERROR_NO_MORE_FILES: break
+            else: raiseOSError(errCode.OSErrorCode)
+    else:
+      var d = opendir(dir)
+      if d == nil:
+        if checkDir:
+          raiseOSError(osLastError(), dir)
+      else:
+        defer: discard closedir(d)
+        while true:
+          var x = readdir(d)
+          if x == nil: break
+          var y = $cast[cstring](addr x.d_name)
+          if y != "." and y != "..":
+            var s: Stat
+            let path = dir / y
+            if not relative:
+              y = path
+            var k = pcFile
+
+            template resolveSymlink() =
+              var isSpecial: bool
+              (k, isSpecial) = getSymlinkFileKind(path)
+              if skipSpecial and isSpecial: continue
+
+            template kSetGeneric() =  # pure Posix component `k` resolution
+              if lstat(path.cstring, s) < 0'i32: continue  # don't yield
+              elif S_ISDIR(s.st_mode):
+                k = pcDir
+              elif S_ISLNK(s.st_mode):
+                resolveSymlink()
+              elif skipSpecial and not S_ISREG(s.st_mode): continue
+
+            when defined(linux) or defined(macosx) or
+                 defined(bsd) or defined(genode) or defined(nintendoswitch):
+              case x.d_type
+              of DT_DIR: k = pcDir
+              of DT_LNK:
+                resolveSymlink()
+              of DT_UNKNOWN:
+                kSetGeneric()
+              else: # DT_REG or special "files" like FIFOs
+                if skipSpecial and x.d_type != DT_REG: continue
+                else: discard # leave it as pcFile
+            else:  # assuming that field `d_type` is not present
+              kSetGeneric()
+
+            yield (k, y)
+
+iterator walkDirRec*(dir: string,
+                     yieldFilter = {pcFile}, followFilter = {pcDir},
+                     relative = false, checkDir = false, skipSpecial = false):
+                    string {.tags: [ReadDirEffect].} =
+  ## Recursively walks over the directory `dir` and yields for each file
+  ## or directory in `dir`.
+  ##
+  ## Options `relative`, `checkdir`, `skipSpecial` are explained in
+  ## [walkDir iterator] description.
+  ##
+  ## .. warning:: Modifying the directory structure while the iterator
+  ##   is traversing may result in undefined behavior!
+  ##
+  ## Walking is recursive. `followFilter` controls the behaviour of the iterator:
+  ##
+  ## =====================   =============================================
+  ## yieldFilter             meaning
+  ## =====================   =============================================
+  ## ``pcFile``              yield real files (default)
+  ## ``pcLinkToFile``        yield symbolic links to files
+  ## ``pcDir``               yield real directories
+  ## ``pcLinkToDir``         yield symbolic links to directories
+  ## =====================   =============================================
+  ##
+  ## =====================   =============================================
+  ## followFilter            meaning
+  ## =====================   =============================================
+  ## ``pcDir``               follow real directories (default)
+  ## ``pcLinkToDir``         follow symbolic links to directories
+  ## =====================   =============================================
+  ##
+  ##
+  ## See also:
+  ## * `walkPattern iterator`_
+  ## * `walkFiles iterator`_
+  ## * `walkDirs iterator`_
+  ## * `walkDir iterator`_
+
+  var stack = @[""]
+  var checkDir = checkDir
+  while stack.len > 0:
+    let d = stack.pop()
+    for k, p in walkDir(dir / d, relative = true, checkDir = checkDir,
+                        skipSpecial = skipSpecial):
+      let rel = d / p
+      if k in {pcDir, pcLinkToDir} and k in followFilter:
+        stack.add rel
+      if k in yieldFilter:
+        yield if relative: rel else: dir / rel
+    checkDir = false
+      # We only check top-level dir, otherwise if a subdir is invalid (eg. wrong
+      # permissions), it'll abort iteration and there would be no way to
+      # continue iteration.
+      # Future work can provide a way to customize this and do error reporting.
+
+
+proc rawRemoveDir(dir: string) {.noWeirdTarget.} =
+  when defined(windows):
+    wrapUnary(res, removeDirectoryW, dir)
+    let lastError = osLastError()
+    if res == 0'i32 and lastError.int32 != 3'i32 and
+        lastError.int32 != 18'i32 and lastError.int32 != 2'i32:
+      raiseOSError(lastError, dir)
+  else:
+    if rmdir(dir) != 0'i32 and errno != ENOENT: raiseOSError(osLastError(), dir)
+
+proc removeDir*(dir: string, checkDir = false) {.rtl, extern: "nos$1", tags: [
+  WriteDirEffect, ReadDirEffect], benign, noWeirdTarget.} =
+  ## Removes the directory `dir` including all subdirectories and files
+  ## in `dir` (recursively).
+  ##
+  ## If this fails, `OSError` is raised. This does not fail if the directory never
+  ## existed in the first place, unless `checkDir` = true.
+  ##
+  ## See also:
+  ## * `tryRemoveFile proc`_
+  ## * `removeFile proc`_
+  ## * `existsOrCreateDir proc`_
+  ## * `createDir proc`_
+  ## * `copyDir proc`_
+  ## * `copyDirWithPermissions proc`_
+  ## * `moveDir proc`_
+  for kind, path in walkDir(dir, checkDir = checkDir):
+    case kind
+    of pcFile, pcLinkToFile, pcLinkToDir: removeFile(path)
+    of pcDir: removeDir(path, true)
+      # for subdirectories there is no benefit in `checkDir = false`
+      # (unless perhaps for edge case of concurrent processes also deleting
+      # the same files)
+  rawRemoveDir(dir)
+
+proc rawCreateDir(dir: string): bool {.noWeirdTarget.} =
+  # Try to create one directory (not the whole path).
+  # returns `true` for success, `false` if the path has previously existed
+  #
+  # This is a thin wrapper over mkDir (or alternatives on other systems),
+  # so in case of a pre-existing path we don't check that it is a directory.
+  when defined(solaris):
+    let res = mkdir(dir, 0o777)
+    if res == 0'i32:
+      result = true
+    elif errno in {EEXIST, ENOSYS}:
+      result = false
+    else:
+      raiseOSError(osLastError(), dir)
+  elif defined(haiku):
+    let res = mkdir(dir, 0o777)
+    if res == 0'i32:
+      result = true
+    elif errno == EEXIST or errno == EROFS:
+      result = false
+    else:
+      raiseOSError(osLastError(), dir)
+  elif defined(posix):
+    let res = mkdir(dir, 0o777)
+    if res == 0'i32:
+      result = true
+    elif errno == EEXIST:
+      result = false
+    else:
+      #echo res
+      raiseOSError(osLastError(), dir)
+  else:
+    wrapUnary(res, createDirectoryW, dir)
+
+    if res != 0'i32:
+      result = true
+    elif getLastError() == 183'i32:
+      result = false
+    else:
+      raiseOSError(osLastError(), dir)
+
+proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1",
+  tags: [WriteDirEffect, ReadDirEffect], noWeirdTarget.} =
+  ## Checks if a `directory`:idx: `dir` exists, and creates it otherwise.
+  ##
+  ## Does not create parent directories (raises `OSError` if parent directories do not exist).
+  ## Returns `true` if the directory already exists, and `false` otherwise.
+  ##
+  ## See also:
+  ## * `removeDir proc`_
+  ## * `createDir proc`_
+  ## * `copyDir proc`_
+  ## * `copyDirWithPermissions proc`_
+  ## * `moveDir proc`_
+  result = not rawCreateDir(dir)
+  if result:
+    # path already exists - need to check that it is indeed a directory
+    if not dirExists(dir):
+      raise newException(IOError, "Failed to create '" & dir & "'")
+
+proc createDir*(dir: string) {.rtl, extern: "nos$1",
+  tags: [WriteDirEffect, ReadDirEffect], noWeirdTarget.} =
+  ## Creates the `directory`:idx: `dir`.
+  ##
+  ## The directory may contain several subdirectories that do not exist yet.
+  ## The full path is created. If this fails, `OSError` is raised.
+  ##
+  ## It does **not** fail if the directory already exists because for
+  ## most usages this does not indicate an error.
+  ##
+  ## See also:
+  ## * `removeDir proc`_
+  ## * `existsOrCreateDir proc`_
+  ## * `copyDir proc`_
+  ## * `copyDirWithPermissions proc`_
+  ## * `moveDir proc`_
+  if dir == "":
+    return
+  var omitNext = isAbsolute(dir)
+  for p in parentDirs(dir, fromRoot=true):
+    if omitNext:
+      omitNext = false
+    else:
+      discard existsOrCreateDir(p)
+
+proc copyDir*(source, dest: string, skipSpecial = false) {.rtl, extern: "nos$1",
+  tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect], benign, noWeirdTarget.} =
+  ## Copies a directory from `source` to `dest`.
+  ##
+  ## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks
+  ## are skipped.
+  ##
+  ## If `skipSpecial` is true, then (besides all directories) only *regular*
+  ## files (**without** special "file" objects like FIFOs, device files,
+  ## etc) will be copied on Unix.
+  ##
+  ## If this fails, `OSError` is raised.
+  ##
+  ## On the Windows platform this proc will copy the attributes from
+  ## `source` into `dest`.
+  ##
+  ## On other platforms created files and directories will inherit the
+  ## default permissions of a newly created file/directory for the user.
+  ## Use `copyDirWithPermissions proc`_
+  ## to preserve attributes recursively on these platforms.
+  ##
+  ## See also:
+  ## * `copyDirWithPermissions proc`_
+  ## * `copyFile proc`_
+  ## * `copyFileWithPermissions proc`_
+  ## * `removeDir proc`_
+  ## * `existsOrCreateDir proc`_
+  ## * `createDir proc`_
+  ## * `moveDir proc`_
+  createDir(dest)
+  for kind, path in walkDir(source, skipSpecial = skipSpecial):
+    var noSource = splitPath(path).tail
+    if kind == pcDir:
+      copyDir(path, dest / noSource, skipSpecial = skipSpecial)
+    else:
+      copyFile(path, dest / noSource, {cfSymlinkAsIs})
+
+
+proc copyDirWithPermissions*(source, dest: string,
+                             ignorePermissionErrors = true,
+                             skipSpecial = false)
+  {.rtl, extern: "nos$1", tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect],
+   benign, noWeirdTarget.} =
+  ## Copies a directory from `source` to `dest` preserving file permissions.
+  ##
+  ## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks
+  ## are skipped.
+  ##
+  ## If `skipSpecial` is true, then (besides all directories) only *regular*
+  ## files (**without** special "file" objects like FIFOs, device files,
+  ## etc) will be copied on Unix.
+  ##
+  ## If this fails, `OSError` is raised. This is a wrapper proc around
+  ## `copyDir`_ and `copyFileWithPermissions`_ procs
+  ## on non-Windows platforms.
+  ##
+  ## On Windows this proc is just a wrapper for `copyDir proc`_ since
+  ## that proc already copies attributes.
+  ##
+  ## On non-Windows systems permissions are copied after the file or directory
+  ## itself has been copied, which won't happen atomically and could lead to a
+  ## race condition. If `ignorePermissionErrors` is true (default), errors while
+  ## reading/setting file attributes will be ignored, otherwise will raise
+  ## `OSError`.
+  ##
+  ## See also:
+  ## * `copyDir proc`_
+  ## * `copyFile proc`_
+  ## * `copyFileWithPermissions proc`_
+  ## * `removeDir proc`_
+  ## * `moveDir proc`_
+  ## * `existsOrCreateDir proc`_
+  ## * `createDir proc`_
+  createDir(dest)
+  when not defined(windows):
+    try:
+      setFilePermissions(dest, getFilePermissions(source), followSymlinks =
+                         false)
+    except:
+      if not ignorePermissionErrors:
+        raise
+  for kind, path in walkDir(source, skipSpecial = skipSpecial):
+    var noSource = splitPath(path).tail
+    if kind == pcDir:
+      copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors, skipSpecial = skipSpecial)
+    else:
+      copyFileWithPermissions(path, dest / noSource, ignorePermissionErrors, {cfSymlinkAsIs})
+
+proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect], noWeirdTarget.} =
+  ## Moves a directory from `source` to `dest`.
+  ##
+  ## Symlinks are not followed: if `source` contains symlinks, they themself are
+  ## moved, not their target.
+  ##
+  ## If this fails, `OSError` is raised.
+  ##
+  ## See also:
+  ## * `moveFile proc`_
+  ## * `copyDir proc`_
+  ## * `copyDirWithPermissions proc`_
+  ## * `removeDir proc`_
+  ## * `existsOrCreateDir proc`_
+  ## * `createDir proc`_
+  if not tryMoveFSObject(source, dest, isDir = true):
+    # Fallback to copy & del
+    copyDir(source, dest)
+    removeDir(source)
+
+proc setCurrentDir*(newDir: string) {.inline, tags: [], noWeirdTarget.} =
+  ## Sets the `current working directory`:idx:; `OSError`
+  ## is raised if `newDir` cannot been set.
+  ##
+  ## See also:
+  ## * `getHomeDir proc`_
+  ## * `getConfigDir proc`_
+  ## * `getTempDir proc`_
+  ## * `getCurrentDir proc`_
+  when defined(windows):
+    if setCurrentDirectoryW(newWideCString(newDir)) == 0'i32:
+      raiseOSError(osLastError(), newDir)
+  else:
+    if chdir(newDir) != 0'i32: raiseOSError(osLastError(), newDir)
diff --git a/lib/std/private/osfiles.nim b/lib/std/private/osfiles.nim
new file mode 100644
index 000000000..37d8eabca
--- /dev/null
+++ b/lib/std/private/osfiles.nim
@@ -0,0 +1,416 @@
+include system/inclrtl
+import std/private/since
+import std/oserrors
+
+import oscommon
+export fileExists
+
+import ospaths2, ossymlinks
+
+## .. importdoc:: osdirs.nim, os.nim
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions, widestrs]
+
+when weirdTarget:
+  discard
+elif defined(windows):
+  import std/winlean
+elif defined(posix):
+  import std/[posix, times]
+
+  proc toTime(ts: Timespec): times.Time {.inline.} =
+    result = initTime(ts.tv_sec.int64, ts.tv_nsec.int)
+else:
+  {.error: "OS module not ported to your operating system!".}
+
+
+when weirdTarget:
+  {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".}
+else:
+  {.pragma: noWeirdTarget.}
+
+
+when defined(nimscript):
+  # for procs already defined in scriptconfig.nim
+  template noNimJs(body): untyped = discard
+elif defined(js):
+  {.pragma: noNimJs, error: "this proc is not available on the js target".}
+else:
+  {.pragma: noNimJs.}
+
+
+type
+  FilePermission* = enum   ## File access permission, modelled after UNIX.
+    ##
+    ## See also:
+    ## * `getFilePermissions`_
+    ## * `setFilePermissions`_
+    ## * `FileInfo object`_
+    fpUserExec,            ## execute access for the file owner
+    fpUserWrite,           ## write access for the file owner
+    fpUserRead,            ## read access for the file owner
+    fpGroupExec,           ## execute access for the group
+    fpGroupWrite,          ## write access for the group
+    fpGroupRead,           ## read access for the group
+    fpOthersExec,          ## execute access for others
+    fpOthersWrite,         ## write access for others
+    fpOthersRead           ## read access for others
+
+proc getFilePermissions*(filename: string): set[FilePermission] {.
+  rtl, extern: "nos$1", tags: [ReadDirEffect], noWeirdTarget.} =
+  ## Retrieves file permissions for `filename`.
+  ##
+  ## `OSError` is raised in case of an error.
+  ## On Windows, only the ``readonly`` flag is checked, every other
+  ## permission is available in any case.
+  ##
+  ## See also:
+  ## * `setFilePermissions proc`_
+  ## * `FilePermission enum`_
+  when defined(posix):
+    var a: Stat
+    if stat(filename, a) < 0'i32: raiseOSError(osLastError(), filename)
+    result = {}
+    if (a.st_mode and S_IRUSR.Mode) != 0.Mode: result.incl(fpUserRead)
+    if (a.st_mode and S_IWUSR.Mode) != 0.Mode: result.incl(fpUserWrite)
+    if (a.st_mode and S_IXUSR.Mode) != 0.Mode: result.incl(fpUserExec)
+
+    if (a.st_mode and S_IRGRP.Mode) != 0.Mode: result.incl(fpGroupRead)
+    if (a.st_mode and S_IWGRP.Mode) != 0.Mode: result.incl(fpGroupWrite)
+    if (a.st_mode and S_IXGRP.Mode) != 0.Mode: result.incl(fpGroupExec)
+
+    if (a.st_mode and S_IROTH.Mode) != 0.Mode: result.incl(fpOthersRead)
+    if (a.st_mode and S_IWOTH.Mode) != 0.Mode: result.incl(fpOthersWrite)
+    if (a.st_mode and S_IXOTH.Mode) != 0.Mode: result.incl(fpOthersExec)
+  else:
+    wrapUnary(res, getFileAttributesW, filename)
+    if res == -1'i32: raiseOSError(osLastError(), filename)
+    if (res and FILE_ATTRIBUTE_READONLY) != 0'i32:
+      result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead,
+                fpOthersExec, fpOthersRead}
+    else:
+      result = {fpUserExec..fpOthersRead}
+
+proc setFilePermissions*(filename: string, permissions: set[FilePermission],
+                         followSymlinks = true)
+  {.rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect],
+   noWeirdTarget.} =
+  ## Sets the file permissions for `filename`.
+  ##
+  ## If `followSymlinks` set to true (default) and ``filename`` points to a
+  ## symlink, permissions are set to the file symlink points to.
+  ## `followSymlinks` set to false is a noop on Windows and some POSIX
+  ## systems (including Linux) on which `lchmod` is either unavailable or always
+  ## fails, given that symlinks permissions there are not observed.
+  ##
+  ## `OSError` is raised in case of an error.
+  ## On Windows, only the ``readonly`` flag is changed, depending on
+  ## ``fpUserWrite`` permission.
+  ##
+  ## See also:
+  ## * `getFilePermissions proc`_
+  ## * `FilePermission enum`_
+  when defined(posix):
+    var p = 0.Mode
+    if fpUserRead in permissions: p = p or S_IRUSR.Mode
+    if fpUserWrite in permissions: p = p or S_IWUSR.Mode
+    if fpUserExec in permissions: p = p or S_IXUSR.Mode
+
+    if fpGroupRead in permissions: p = p or S_IRGRP.Mode
+    if fpGroupWrite in permissions: p = p or S_IWGRP.Mode
+    if fpGroupExec in permissions: p = p or S_IXGRP.Mode
+
+    if fpOthersRead in permissions: p = p or S_IROTH.Mode
+    if fpOthersWrite in permissions: p = p or S_IWOTH.Mode
+    if fpOthersExec in permissions: p = p or S_IXOTH.Mode
+
+    if not followSymlinks and filename.symlinkExists:
+      when declared(lchmod):
+        if lchmod(filename, cast[Mode](p)) != 0:
+          raiseOSError(osLastError(), $(filename, permissions))
+    else:
+      if chmod(filename, cast[Mode](p)) != 0:
+        raiseOSError(osLastError(), $(filename, permissions))
+  else:
+    wrapUnary(res, getFileAttributesW, filename)
+    if res == -1'i32: raiseOSError(osLastError(), filename)
+    if fpUserWrite in permissions:
+      res = res and not FILE_ATTRIBUTE_READONLY
+    else:
+      res = res or FILE_ATTRIBUTE_READONLY
+    wrapBinary(res2, setFileAttributesW, filename, res)
+    if res2 == - 1'i32: raiseOSError(osLastError(), $(filename, permissions))
+
+
+const hasCCopyfile = defined(osx) and not defined(nimLegacyCopyFile)
+  # xxx instead of `nimLegacyCopyFile`, support something like: `when osxVersion >= (10, 5)`
+
+when hasCCopyfile:
+  # `copyfile` API available since osx 10.5.
+  {.push nodecl, header: "<copyfile.h>".}
+  type
+    copyfile_state_t {.nodecl.} = pointer
+    copyfile_flags_t = cint
+  proc copyfile_state_alloc(): copyfile_state_t
+  proc copyfile_state_free(state: copyfile_state_t): cint
+  proc c_copyfile(src, dst: cstring,  state: copyfile_state_t, flags: copyfile_flags_t): cint {.importc: "copyfile".}
+  when (NimMajor, NimMinor) >= (1, 4):
+    let
+      COPYFILE_DATA {.nodecl.}: copyfile_flags_t
+      COPYFILE_XATTR {.nodecl.}: copyfile_flags_t
+  else:
+    var
+      COPYFILE_DATA {.nodecl.}: copyfile_flags_t
+      COPYFILE_XATTR {.nodecl.}: copyfile_flags_t
+  {.pop.}
+
+type
+  CopyFlag* = enum    ## Copy options.
+    cfSymlinkAsIs,    ## Copy symlinks as symlinks
+    cfSymlinkFollow,  ## Copy the files symlinks point to
+    cfSymlinkIgnore   ## Ignore symlinks
+
+const copyFlagSymlink = {cfSymlinkAsIs, cfSymlinkFollow, cfSymlinkIgnore}
+
+proc copyFile*(source, dest: string, options = {cfSymlinkFollow}; bufferSize = 16_384) {.rtl,
+  extern: "nos$1", tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect],
+  noWeirdTarget.} =
+  ## Copies a file from `source` to `dest`, where `dest.parentDir` must exist.
+  ##
+  ## On non-Windows OSes, `options` specify the way file is copied; by default,
+  ## if `source` is a symlink, copies the file symlink points to. `options` is
+  ## ignored on Windows: symlinks are skipped.
+  ##
+  ## If this fails, `OSError` is raised.
+  ##
+  ## On the Windows platform this proc will
+  ## copy the source file's attributes into dest.
+  ##
+  ## On other platforms you need
+  ## to use `getFilePermissions`_ and
+  ## `setFilePermissions`_
+  ## procs
+  ## to copy them by hand (or use the convenience `copyFileWithPermissions
+  ## proc`_),
+  ## otherwise `dest` will inherit the default permissions of a newly
+  ## created file for the user.
+  ##
+  ## If `dest` already exists, the file attributes
+  ## will be preserved and the content overwritten.
+  ##
+  ## On OSX, `copyfile` C api will be used (available since OSX 10.5) unless
+  ## `-d:nimLegacyCopyFile` is used.
+  ##
+  ## `copyFile` allows to specify `bufferSize` to improve I/O performance.
+  ##
+  ## See also:
+  ## * `CopyFlag enum`_
+  ## * `copyDir proc`_
+  ## * `copyFileWithPermissions proc`_
+  ## * `tryRemoveFile proc`_
+  ## * `removeFile proc`_
+  ## * `moveFile proc`_
+
+  doAssert card(copyFlagSymlink * options) == 1, "There should be exactly one cfSymlink* in options"
+  let isSymlink = source.symlinkExists
+  if isSymlink and (cfSymlinkIgnore in options or defined(windows)):
+    return
+  when defined(windows):
+    let s = newWideCString(source)
+    let d = newWideCString(dest)
+    if copyFileW(s, d, 0'i32) == 0'i32:
+      raiseOSError(osLastError(), $(source, dest))
+  else:
+    if isSymlink and cfSymlinkAsIs in options:
+      createSymlink(expandSymlink(source), dest)
+    else:
+      when hasCCopyfile:
+        let state = copyfile_state_alloc()
+        # xxx `COPYFILE_STAT` could be used for one-shot
+        # `copyFileWithPermissions`.
+        let status = c_copyfile(source.cstring, dest.cstring, state,
+                                COPYFILE_DATA)
+        if status != 0:
+          let err = osLastError()
+          discard copyfile_state_free(state)
+          raiseOSError(err, $(source, dest))
+        let status2 = copyfile_state_free(state)
+        if status2 != 0: raiseOSError(osLastError(), $(source, dest))
+      else:
+        # generic version of copyFile which works for any platform:
+        var d, s: File
+        if not open(s, source): raiseOSError(osLastError(), source)
+        if not open(d, dest, fmWrite):
+          close(s)
+          raiseOSError(osLastError(), dest)
+
+        # Hints for kernel-level aggressive sequential low-fragmentation read-aheads:
+        # https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fadvise.html
+        when defined(linux) or defined(osx):
+          discard posix_fadvise(getFileHandle(d), 0.cint, 0.cint, POSIX_FADV_SEQUENTIAL)
+          discard posix_fadvise(getFileHandle(s), 0.cint, 0.cint, POSIX_FADV_SEQUENTIAL)
+
+        var buf = alloc(bufferSize)
+        while true:
+          var bytesread = readBuffer(s, buf, bufferSize)
+          if bytesread > 0:
+            var byteswritten = writeBuffer(d, buf, bytesread)
+            if bytesread != byteswritten:
+              dealloc(buf)
+              close(s)
+              close(d)
+              raiseOSError(osLastError(), dest)
+          if bytesread != bufferSize: break
+        dealloc(buf)
+        close(s)
+        flushFile(d)
+        close(d)
+
+proc copyFileToDir*(source, dir: string, options = {cfSymlinkFollow}; bufferSize = 16_384)
+  {.noWeirdTarget, since: (1,3,7).} =
+  ## Copies a file `source` into directory `dir`, which must exist.
+  ##
+  ## On non-Windows OSes, `options` specify the way file is copied; by default,
+  ## if `source` is a symlink, copies the file symlink points to. `options` is
+  ## ignored on Windows: symlinks are skipped.
+  ##
+  ## `copyFileToDir` allows to specify `bufferSize` to improve I/O performance.
+  ##
+  ## See also:
+  ## * `CopyFlag enum`_
+  ## * `copyFile proc`_
+  if dir.len == 0: # treating "" as "." is error prone
+    raise newException(ValueError, "dest is empty")
+  copyFile(source, dir / source.lastPathPart, options, bufferSize)
+
+
+proc copyFileWithPermissions*(source, dest: string,
+                              ignorePermissionErrors = true,
+                              options = {cfSymlinkFollow}) {.noWeirdTarget.} =
+  ## Copies a file from `source` to `dest` preserving file permissions.
+  ##
+  ## On non-Windows OSes, `options` specify the way file is copied; by default,
+  ## if `source` is a symlink, copies the file symlink points to. `options` is
+  ## ignored on Windows: symlinks are skipped.
+  ##
+  ## This is a wrapper proc around `copyFile`_,
+  ## `getFilePermissions`_ and `setFilePermissions`_
+  ## procs on non-Windows platforms.
+  ##
+  ## On Windows this proc is just a wrapper for `copyFile proc`_ since
+  ## that proc already copies attributes.
+  ##
+  ## On non-Windows systems permissions are copied after the file itself has
+  ## been copied, which won't happen atomically and could lead to a race
+  ## condition. If `ignorePermissionErrors` is true (default), errors while
+  ## reading/setting file attributes will be ignored, otherwise will raise
+  ## `OSError`.
+  ##
+  ## See also:
+  ## * `CopyFlag enum`_
+  ## * `copyFile proc`_
+  ## * `copyDir proc`_
+  ## * `tryRemoveFile proc`_
+  ## * `removeFile proc`_
+  ## * `moveFile proc`_
+  ## * `copyDirWithPermissions proc`_
+  copyFile(source, dest, options)
+  when not defined(windows):
+    try:
+      setFilePermissions(dest, getFilePermissions(source), followSymlinks =
+                         (cfSymlinkFollow in options))
+    except:
+      if not ignorePermissionErrors:
+        raise
+
+when not declared(ENOENT) and not defined(windows):
+  when defined(nimscript):
+    when not defined(haiku):
+      const ENOENT = cint(2) # 2 on most systems including Solaris
+    else:
+      const ENOENT = cint(-2147459069)
+  else:
+    var ENOENT {.importc, header: "<errno.h>".}: cint
+
+when defined(windows) and not weirdTarget:
+  template deleteFile(file: untyped): untyped  = deleteFileW(file)
+  template setFileAttributes(file, attrs: untyped): untyped =
+    setFileAttributesW(file, attrs)
+
+proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirEffect], noWeirdTarget.} =
+  ## Removes the `file`.
+  ##
+  ## If this fails, returns `false`. This does not fail
+  ## if the file never existed in the first place.
+  ##
+  ## On Windows, ignores the read-only attribute.
+  ##
+  ## See also:
+  ## * `copyFile proc`_
+  ## * `copyFileWithPermissions proc`_
+  ## * `removeFile proc`_
+  ## * `moveFile proc`_
+  result = true
+  when defined(windows):
+    let f = newWideCString(file)
+    if deleteFile(f) == 0:
+      result = false
+      let err = getLastError()
+      if err == ERROR_FILE_NOT_FOUND or err == ERROR_PATH_NOT_FOUND:
+        result = true
+      elif err == ERROR_ACCESS_DENIED and
+         setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) != 0 and
+         deleteFile(f) != 0:
+        result = true
+  else:
+    if unlink(file) != 0'i32 and errno != ENOENT:
+      result = false
+
+proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect], noWeirdTarget.} =
+  ## Removes the `file`.
+  ##
+  ## If this fails, `OSError` is raised. This does not fail
+  ## if the file never existed in the first place.
+  ##
+  ## On Windows, ignores the read-only attribute.
+  ##
+  ## See also:
+  ## * `removeDir proc`_
+  ## * `copyFile proc`_
+  ## * `copyFileWithPermissions proc`_
+  ## * `tryRemoveFile proc`_
+  ## * `moveFile proc`_
+  if not tryRemoveFile(file):
+    raiseOSError(osLastError(), file)
+
+proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
+  tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect], noWeirdTarget.} =
+  ## Moves a file from `source` to `dest`.
+  ##
+  ## Symlinks are not followed: if `source` is a symlink, it is itself moved,
+  ## not its target.
+  ##
+  ## If this fails, `OSError` is raised.
+  ## If `dest` already exists, it will be overwritten.
+  ##
+  ## Can be used to `rename files`:idx:.
+  ##
+  ## See also:
+  ## * `moveDir proc`_
+  ## * `copyFile proc`_
+  ## * `copyFileWithPermissions proc`_
+  ## * `removeFile proc`_
+  ## * `tryRemoveFile proc`_
+
+  if not tryMoveFSObject(source, dest, isDir = false):
+    when defined(windows):
+      raiseAssert "unreachable"
+    else:
+      # Fallback to copy & del
+      copyFileWithPermissions(source, dest, options={cfSymlinkAsIs})
+      try:
+        removeFile(source)
+      except:
+        discard tryRemoveFile(dest)
+        raise
diff --git a/lib/std/private/ospaths2.nim b/lib/std/private/ospaths2.nim
new file mode 100644
index 000000000..bc69ff725
--- /dev/null
+++ b/lib/std/private/ospaths2.nim
@@ -0,0 +1,1030 @@
+include system/inclrtl
+import std/private/since
+
+import std/[strutils, pathnorm]
+import std/oserrors
+
+import oscommon
+export ReadDirEffect, WriteDirEffect
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions, widestrs]
+
+## .. importdoc:: osappdirs.nim, osdirs.nim, osseps.nim, os.nim
+
+const weirdTarget = defined(nimscript) or defined(js)
+
+when weirdTarget:
+  discard
+elif defined(windows):
+  import std/winlean
+elif defined(posix):
+  import std/posix, system/ansi_c
+else:
+  {.error: "OS module not ported to your operating system!".}
+
+when weirdTarget:
+  {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".}
+else:
+  {.pragma: noWeirdTarget.}
+
+when defined(nimscript):
+  # for procs already defined in scriptconfig.nim
+  template noNimJs(body): untyped = discard
+elif defined(js):
+  {.pragma: noNimJs, error: "this proc is not available on the js target".}
+else:
+  {.pragma: noNimJs.}
+
+
+proc normalizePathAux(path: var string){.inline, raises: [], noSideEffect.}
+
+
+import std/private/osseps
+export osseps
+
+proc absolutePathInternal(path: string): string {.gcsafe.}
+
+proc normalizePathEnd*(path: var string, trailingSep = false) =
+  ## Ensures ``path`` has exactly 0 or 1 trailing `DirSep`, depending on
+  ## ``trailingSep``, and taking care of edge cases: it preservers whether
+  ## a path is absolute or relative, and makes sure trailing sep is `DirSep`,
+  ## not `AltSep`. Trailing `/.` are compressed, see examples.
+  if path.len == 0: return
+  var i = path.len
+  while i >= 1:
+    if path[i-1] in {DirSep, AltSep}: dec(i)
+    elif path[i-1] == '.' and i >= 2 and path[i-2] in {DirSep, AltSep}: dec(i)
+    else: break
+  if trailingSep:
+    # foo// => foo
+    path.setLen(i)
+    # foo => foo/
+    path.add DirSep
+  elif i > 0:
+    # foo// => foo
+    path.setLen(i)
+  else:
+    # // => / (empty case was already taken care of)
+    path = $DirSep
+
+proc normalizePathEnd*(path: string, trailingSep = false): string =
+  ## outplace overload
+  runnableExamples:
+    when defined(posix):
+      assert normalizePathEnd("/lib//.//", trailingSep = true) == "/lib/"
+      assert normalizePathEnd("lib/./.", trailingSep = false) == "lib"
+      assert normalizePathEnd(".//./.", trailingSep = false) == "."
+      assert normalizePathEnd("", trailingSep = true) == "" # not / !
+      assert normalizePathEnd("/", trailingSep = false) == "/" # not "" !
+  result = path
+  result.normalizePathEnd(trailingSep)
+
+template endsWith(a: string, b: set[char]): bool =
+  a.len > 0 and a[^1] in b
+
+proc joinPathImpl(result: var string, state: var int, tail: string) =
+  let trailingSep = tail.endsWith({DirSep, AltSep}) or tail.len == 0 and result.endsWith({DirSep, AltSep})
+  normalizePathEnd(result, trailingSep=false)
+  addNormalizePath(tail, result, state, DirSep)
+  normalizePathEnd(result, trailingSep=trailingSep)
+
+proc joinPath*(head, tail: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Joins two directory names to one.
+  ##
+  ## returns normalized path concatenation of `head` and `tail`, preserving
+  ## whether or not `tail` has a trailing slash (or, if tail if empty, whether
+  ## head has one).
+  ##
+  ## See also:
+  ## * `joinPath(parts: varargs[string]) proc`_
+  ## * `/ proc`_
+  ## * `splitPath proc`_
+  ## * `uri.combine proc <uri.html#combine,Uri,Uri>`_
+  ## * `uri./ proc <uri.html#/,Uri,string>`_
+  runnableExamples:
+    when defined(posix):
+      assert joinPath("usr", "lib") == "usr/lib"
+      assert joinPath("usr", "lib/") == "usr/lib/"
+      assert joinPath("usr", "") == "usr"
+      assert joinPath("usr/", "") == "usr/"
+      assert joinPath("", "") == ""
+      assert joinPath("", "lib") == "lib"
+      assert joinPath("", "/lib") == "/lib"
+      assert joinPath("usr/", "/lib") == "usr/lib"
+      assert joinPath("usr/lib", "../bin") == "usr/bin"
+
+  result = newStringOfCap(head.len + tail.len)
+  var state = 0
+  joinPathImpl(result, state, head)
+  joinPathImpl(result, state, tail)
+  when false:
+    if len(head) == 0:
+      result = tail
+    elif head[len(head)-1] in {DirSep, AltSep}:
+      if tail.len > 0 and tail[0] in {DirSep, AltSep}:
+        result = head & substr(tail, 1)
+      else:
+        result = head & tail
+    else:
+      if tail.len > 0 and tail[0] in {DirSep, AltSep}:
+        result = head & tail
+      else:
+        result = head & DirSep & tail
+
+proc joinPath*(parts: varargs[string]): string {.noSideEffect,
+  rtl, extern: "nos$1OpenArray".} =
+  ## The same as `joinPath(head, tail) proc`_,
+  ## but works with any number of directory parts.
+  ##
+  ## You need to pass at least one element or the proc
+  ## will assert in debug builds and crash on release builds.
+  ##
+  ## See also:
+  ## * `joinPath(head, tail) proc`_
+  ## * `/ proc`_
+  ## * `/../ proc`_
+  ## * `splitPath proc`_
+  runnableExamples:
+    when defined(posix):
+      assert joinPath("a") == "a"
+      assert joinPath("a", "b", "c") == "a/b/c"
+      assert joinPath("usr/lib", "../../var", "log") == "var/log"
+
+  var estimatedLen = 0
+  for p in parts: estimatedLen += p.len
+  result = newStringOfCap(estimatedLen)
+  var state = 0
+  for i in 0..high(parts):
+    joinPathImpl(result, state, parts[i])
+
+proc `/`*(head, tail: string): string {.noSideEffect, inline.} =
+  ## The same as `joinPath(head, tail) proc`_.
+  ##
+  ## See also:
+  ## * `/../ proc`_
+  ## * `joinPath(head, tail) proc`_
+  ## * `joinPath(parts: varargs[string]) proc`_
+  ## * `splitPath proc`_
+  ## * `uri.combine proc <uri.html#combine,Uri,Uri>`_
+  ## * `uri./ proc <uri.html#/,Uri,string>`_
+  runnableExamples:
+    when defined(posix):
+      assert "usr" / "" == "usr"
+      assert "" / "lib" == "lib"
+      assert "" / "/lib" == "/lib"
+      assert "usr/" / "/lib/" == "usr/lib/"
+      assert "usr" / "lib" / "../bin" == "usr/bin"
+
+  result = joinPath(head, tail)
+
+when doslikeFileSystem:
+  import std/private/ntpath
+
+proc splitPath*(path: string): tuple[head, tail: string] {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Splits a directory into `(head, tail)` tuple, so that
+  ## ``head / tail == path`` (except for edge cases like "/usr").
+  ##
+  ## See also:
+  ## * `joinPath(head, tail) proc`_
+  ## * `joinPath(parts: varargs[string]) proc`_
+  ## * `/ proc`_
+  ## * `/../ proc`_
+  ## * `relativePath proc`_
+  runnableExamples:
+    assert splitPath("usr/local/bin") == ("usr/local", "bin")
+    assert splitPath("usr/local/bin/") == ("usr/local/bin", "")
+    assert splitPath("/bin/") == ("/bin", "")
+    when (NimMajor, NimMinor) <= (1, 0):
+      assert splitPath("/bin") == ("", "bin")
+    else:
+      assert splitPath("/bin") == ("/", "bin")
+    assert splitPath("bin") == ("", "bin")
+    assert splitPath("") == ("", "")
+
+  when doslikeFileSystem:
+    let (drive, splitpath) = splitDrive(path)
+    let stop = drive.len
+  else:
+    const stop = 0
+
+  var sepPos = -1
+  for i in countdown(len(path)-1, stop):
+    if path[i] in {DirSep, AltSep}:
+      sepPos = i
+      break
+  if sepPos >= 0:
+    result.head = substr(path, 0,
+      when (NimMajor, NimMinor) <= (1, 0):
+        sepPos-1
+      else:
+        if likely(sepPos >= 1): sepPos-1 else: 0
+    )
+    result.tail = substr(path, sepPos+1)
+  else:
+    when doslikeFileSystem:
+      result.head = drive
+      result.tail = splitpath
+    else:
+      result.head = ""
+      result.tail = path
+
+proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1", raises: [].} =
+  ## Checks whether a given `path` is absolute.
+  ##
+  ## On Windows, network paths are considered absolute too.
+  runnableExamples:
+    assert not "".isAbsolute
+    assert not ".".isAbsolute
+    when defined(posix):
+      assert "/".isAbsolute
+      assert not "a/".isAbsolute
+      assert "/a/".isAbsolute
+
+  if len(path) == 0: return false
+
+  when doslikeFileSystem:
+    var len = len(path)
+    result = (path[0] in {'/', '\\'}) or
+              (len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':')
+  elif defined(macos):
+    # according to https://perldoc.perl.org/File/Spec/Mac.html `:a` is a relative path
+    result = path[0] != ':'
+  elif defined(RISCOS):
+    result = path[0] == '$'
+  elif defined(posix):
+    result = path[0] == '/'
+  elif defined(nodejs):
+    {.emit: [result," = require(\"path\").isAbsolute(",path.cstring,");"].}
+  else:
+    raiseAssert "unreachable" # if ever hits here, adapt as needed
+
+when FileSystemCaseSensitive:
+  template `!=?`(a, b: char): bool = a != b
+else:
+  template `!=?`(a, b: char): bool = toLowerAscii(a) != toLowerAscii(b)
+
+when doslikeFileSystem:
+  proc isAbsFromCurrentDrive(path: string): bool {.noSideEffect, raises: [].} =
+    ## An absolute path from the root of the current drive (e.g. "\foo")
+    path.len > 0 and
+    (path[0] == AltSep or
+     (path[0] == DirSep and
+      (path.len == 1 or path[1] notin {DirSep, AltSep, ':'})))
+
+  proc sameRoot(path1, path2: string): bool {.noSideEffect, raises: [].} =
+    ## Return true if path1 and path2 have a same root.
+    ##
+    ## Detail of Windows path formats:
+    ## https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats
+
+    assert(isAbsolute(path1))
+    assert(isAbsolute(path2))
+
+    if isAbsFromCurrentDrive(path1) and isAbsFromCurrentDrive(path2):
+      result = true
+    elif cmpIgnoreCase(splitDrive(path1).drive, splitDrive(path2).drive) == 0:
+      result = true
+    else:
+      result = false
+
+proc relativePath*(path, base: string, sep = DirSep): string {.
+  rtl, extern: "nos$1".} =
+  ## Converts `path` to a path relative to `base`.
+  ##
+  ## The `sep` (default: DirSep_) is used for the path normalizations,
+  ## this can be useful to ensure the relative path only contains `'/'`
+  ## so that it can be used for URL constructions.
+  ##
+  ## On Windows, if a root of `path` and a root of `base` are different,
+  ## returns `path` as is because it is impossible to make a relative path.
+  ## That means an absolute path can be returned.
+  ##
+  ## See also:
+  ## * `splitPath proc`_
+  ## * `parentDir proc`_
+  ## * `tailDir proc`_
+  runnableExamples:
+    assert relativePath("/Users/me/bar/z.nim", "/Users/other/bad", '/') == "../../me/bar/z.nim"
+    assert relativePath("/Users/me/bar/z.nim", "/Users/other", '/') == "../me/bar/z.nim"
+    when not doslikeFileSystem: # On Windows, UNC-paths start with `//`
+      assert relativePath("/Users///me/bar//z.nim", "//Users/", '/') == "me/bar/z.nim"
+    assert relativePath("/Users/me/bar/z.nim", "/Users/me", '/') == "bar/z.nim"
+    assert relativePath("", "/users/moo", '/') == ""
+    assert relativePath("foo", ".", '/') == "foo"
+    assert relativePath("foo", "foo", '/') == "."
+
+  if path.len == 0: return ""
+  var base = if base == ".": "" else: base
+  var path = path
+  path.normalizePathAux
+  base.normalizePathAux
+  let a1 = isAbsolute(path)
+  let a2 = isAbsolute(base)
+  if a1 and not a2:
+    base = absolutePathInternal(base)
+  elif a2 and not a1:
+    path = absolutePathInternal(path)
+
+  when doslikeFileSystem:
+    if isAbsolute(path) and isAbsolute(base):
+      if not sameRoot(path, base):
+        return path
+
+  var f = default PathIter
+  var b = default PathIter
+  var ff = (0, -1)
+  var bb = (0, -1) # (int, int)
+  result = newStringOfCap(path.len)
+  # skip the common prefix:
+  while f.hasNext(path) and b.hasNext(base):
+    ff = next(f, path)
+    bb = next(b, base)
+    let diff = ff[1] - ff[0]
+    if diff != bb[1] - bb[0]: break
+    var same = true
+    for i in 0..diff:
+      if path[i + ff[0]] !=? base[i + bb[0]]:
+        same = false
+        break
+    if not same: break
+    ff = (0, -1)
+    bb = (0, -1)
+  #  for i in 0..diff:
+  #    result.add base[i + bb[0]]
+
+  # /foo/bar/xxx/ -- base
+  # /foo/bar/baz  -- path path
+  #   ../baz
+  # every directory that is in 'base', needs to add '..'
+  while true:
+    if bb[1] >= bb[0]:
+      if result.len > 0 and result[^1] != sep:
+        result.add sep
+      result.add ".."
+    if not b.hasNext(base): break
+    bb = b.next(base)
+
+  # add the rest of 'path':
+  while true:
+    if ff[1] >= ff[0]:
+      if result.len > 0 and result[^1] != sep:
+        result.add sep
+      for i in 0..ff[1] - ff[0]:
+        result.add path[i + ff[0]]
+    if not f.hasNext(path): break
+    ff = f.next(path)
+
+  when not defined(nimOldRelativePathBehavior):
+    if result.len == 0: result.add "."
+
+proc isRelativeTo*(path: string, base: string): bool {.since: (1, 1).} =
+  ## Returns true if `path` is relative to `base`.
+  runnableExamples:
+    doAssert isRelativeTo("./foo//bar", "foo")
+    doAssert isRelativeTo("foo/bar", ".")
+    doAssert isRelativeTo("/foo/bar.nim", "/foo/bar.nim")
+    doAssert not isRelativeTo("foo/bar.nims", "foo/bar.nim")
+  let path = path.normalizePath
+  let base = base.normalizePath
+  let ret = relativePath(path, base)
+  result = path.len > 0 and not ret.startsWith ".."
+
+proc parentDirPos(path: string): int =
+  var q = 1
+  if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2
+  for i in countdown(len(path)-q, 0):
+    if path[i] in {DirSep, AltSep}: return i
+  result = -1
+
+proc parentDir*(path: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Returns the parent directory of `path`.
+  ##
+  ## This is similar to ``splitPath(path).head`` when ``path`` doesn't end
+  ## in a dir separator, but also takes care of path normalizations.
+  ## The remainder can be obtained with `lastPathPart(path) proc`_.
+  ##
+  ## See also:
+  ## * `relativePath proc`_
+  ## * `splitPath proc`_
+  ## * `tailDir proc`_
+  ## * `parentDirs iterator`_
+  runnableExamples:
+    assert parentDir("") == ""
+    when defined(posix):
+      assert parentDir("/usr/local/bin") == "/usr/local"
+      assert parentDir("foo/bar//") == "foo"
+      assert parentDir("//foo//bar//.") == "/foo"
+      assert parentDir("./foo") == "."
+      assert parentDir("/./foo//./") == "/"
+      assert parentDir("a//./") == "."
+      assert parentDir("a/b/c/..") == "a"
+  result = pathnorm.normalizePath(path)
+  when doslikeFileSystem:
+    let (drive, splitpath) = splitDrive(result)
+    result = splitpath
+  var sepPos = parentDirPos(result)
+  if sepPos >= 0:
+    result = substr(result, 0, sepPos)
+    normalizePathEnd(result)
+  elif result == ".." or result == "." or result.len == 0 or result[^1] in {DirSep, AltSep}:
+    # `.` => `..` and .. => `../..`(etc) would be a sensible alternative
+    # `/` => `/` (as done with splitFile) would be a sensible alternative
+    result = ""
+  else:
+    result = "."
+  when doslikeFileSystem:
+    if result.len == 0:
+      discard
+    elif drive.len > 0 and result.len == 1 and result[0] in {DirSep, AltSep}:
+      result = drive
+    else:
+      result = drive & result
+
+proc tailDir*(path: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Returns the tail part of `path`.
+  ##
+  ## See also:
+  ## * `relativePath proc`_
+  ## * `splitPath proc`_
+  ## * `parentDir proc`_
+  runnableExamples:
+    assert tailDir("/bin") == "bin"
+    assert tailDir("bin") == ""
+    assert tailDir("bin/") == ""
+    assert tailDir("/usr/local/bin") == "usr/local/bin"
+    assert tailDir("//usr//local//bin//") == "usr//local//bin//"
+    assert tailDir("./usr/local/bin") == "usr/local/bin"
+    assert tailDir("usr/local/bin") == "local/bin"
+
+  var i = 0
+  when doslikeFileSystem:
+    let (drive, splitpath) = path.splitDrive
+    if drive != "":
+      return splitpath.strip(chars = {DirSep, AltSep}, trailing = false)
+  while i < len(path):
+    if path[i] in {DirSep, AltSep}:
+      while i < len(path) and path[i] in {DirSep, AltSep}: inc i
+      return substr(path, i)
+    inc i
+  result = ""
+
+proc isRootDir*(path: string): bool {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Checks whether a given `path` is a root directory.
+  runnableExamples:
+    assert isRootDir("")
+    assert isRootDir(".")
+    assert isRootDir("/")
+    assert isRootDir("a")
+    assert not isRootDir("/a")
+    assert not isRootDir("a/b/c")
+
+  when doslikeFileSystem:
+    if splitDrive(path).path == "":
+      return true
+  result = parentDirPos(path) < 0
+
+iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string =
+  ## Walks over all parent directories of a given `path`.
+  ##
+  ## If `fromRoot` is true (default: false), the traversal will start from
+  ## the file system root directory.
+  ## If `inclusive` is true (default), the original argument will be included
+  ## in the traversal.
+  ##
+  ## Relative paths won't be expanded by this iterator. Instead, it will traverse
+  ## only the directories appearing in the relative path.
+  ##
+  ## See also:
+  ## * `parentDir proc`_
+  ##
+  runnableExamples:
+    let g = "a/b/c"
+
+    for p in g.parentDirs:
+      echo p
+      # a/b/c
+      # a/b
+      # a
+
+    for p in g.parentDirs(fromRoot=true):
+      echo p
+      # a/
+      # a/b/
+      # a/b/c
+
+    for p in g.parentDirs(inclusive=false):
+      echo p
+      # a/b
+      # a
+
+  if not fromRoot:
+    var current = path
+    if inclusive: yield path
+    while true:
+      if current.isRootDir: break
+      current = current.parentDir
+      yield current
+  else:
+    when doslikeFileSystem:
+      let start = path.splitDrive.drive.len
+    else:
+      const start = 0
+    for i in countup(start, path.len - 2): # ignore the last /
+      # deal with non-normalized paths such as /foo//bar//baz
+      if path[i] in {DirSep, AltSep} and
+          (i == 0 or path[i-1] notin {DirSep, AltSep}):
+        yield path.substr(0, i)
+
+    if inclusive: yield path
+
+proc `/../`*(head, tail: string): string {.noSideEffect.} =
+  ## The same as ``parentDir(head) / tail``, unless there is no parent
+  ## directory. Then ``head / tail`` is performed instead.
+  ##
+  ## See also:
+  ## * `/ proc`_
+  ## * `parentDir proc`_
+  runnableExamples:
+    when defined(posix):
+      assert "a/b/c" /../ "d/e" == "a/b/d/e"
+      assert "a" /../ "d/e" == "a/d/e"
+
+  when doslikeFileSystem:
+    let (drive, head) = splitDrive(head)
+  let sepPos = parentDirPos(head)
+  if sepPos >= 0:
+    result = substr(head, 0, sepPos-1) / tail
+  else:
+    result = head / tail
+  when doslikeFileSystem:
+    result = drive / result
+
+proc normExt(ext: string): string =
+  if ext == "" or ext[0] == ExtSep: result = ext # no copy needed here
+  else: result = ExtSep & ext
+
+proc searchExtPos*(path: string): int =
+  ## Returns index of the `'.'` char in `path` if it signifies the beginning
+  ## of the file extension. Returns -1 otherwise.
+  ##
+  ## See also:
+  ## * `splitFile proc`_
+  ## * `extractFilename proc`_
+  ## * `lastPathPart proc`_
+  ## * `changeFileExt proc`_
+  ## * `addFileExt proc`_
+  runnableExamples:
+    assert searchExtPos("a/b/c") == -1
+    assert searchExtPos("c.nim") == 1
+    assert searchExtPos("a/b/c.nim") == 5
+    assert searchExtPos("a.b.c.nim") == 5
+    assert searchExtPos(".nim") == -1
+    assert searchExtPos("..nim") == -1
+    assert searchExtPos("a..nim") == 2
+
+  # Unless there is any char that is not `ExtSep` before last `ExtSep` in the file name,
+  # it is not a file extension.
+  const DirSeps = when doslikeFileSystem: {DirSep, AltSep, ':'} else: {DirSep, AltSep}
+  result = -1
+  var i = path.high
+  while i >= 1:
+    if path[i] == ExtSep:
+      break
+    elif path[i] in DirSeps:
+      return -1 # do not skip over path
+    dec i
+
+  for j in countdown(i - 1, 0):
+    if path[j] in DirSeps:
+      return -1
+    elif path[j] != ExtSep:
+      result = i
+      break
+
+proc splitFile*(path: string): tuple[dir, name, ext: string] {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Splits a filename into `(dir, name, extension)` tuple.
+  ##
+  ## `dir` does not end in DirSep_ unless it's `/`.
+  ## `extension` includes the leading dot.
+  ##
+  ## 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.
+  ##
+  ## See also:
+  ## * `searchExtPos proc`_
+  ## * `extractFilename proc`_
+  ## * `lastPathPart proc`_
+  ## * `changeFileExt proc`_
+  ## * `addFileExt proc`_
+  runnableExamples:
+    var (dir, name, ext) = splitFile("usr/local/nimc.html")
+    assert dir == "usr/local"
+    assert name == "nimc"
+    assert ext == ".html"
+    (dir, name, ext) = splitFile("/usr/local/os")
+    assert dir == "/usr/local"
+    assert name == "os"
+    assert ext == ""
+    (dir, name, ext) = splitFile("/usr/local/")
+    assert dir == "/usr/local"
+    assert name == ""
+    assert ext == ""
+    (dir, name, ext) = splitFile("/tmp.txt")
+    assert dir == "/"
+    assert name == "tmp"
+    assert ext == ".txt"
+
+  var namePos = 0
+  var dotPos = 0
+  when doslikeFileSystem:
+    let (drive, _) = splitDrive(path)
+    let stop = len(drive)
+    result.dir = drive
+  else:
+    const stop = 0
+  for i in countdown(len(path) - 1, stop):
+    if path[i] in {DirSep, AltSep} or i == 0:
+      if path[i] in {DirSep, AltSep}:
+        result.dir = substr(path, 0, if likely(i >= 1): i - 1 else: 0)
+        namePos = i + 1
+      if dotPos > i:
+        result.name = substr(path, namePos, dotPos - 1)
+        result.ext = substr(path, dotPos)
+      else:
+        result.name = substr(path, namePos)
+      break
+    elif path[i] == ExtSep and i > 0 and i < len(path) - 1 and
+         path[i - 1] notin {DirSep, AltSep} and
+         path[i + 1] != ExtSep and dotPos == 0:
+      dotPos = i
+
+proc extractFilename*(path: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Extracts the filename of a given `path`.
+  ##
+  ## This is the same as ``name & ext`` from `splitFile(path) proc`_.
+  ##
+  ## See also:
+  ## * `searchExtPos proc`_
+  ## * `splitFile proc`_
+  ## * `lastPathPart proc`_
+  ## * `changeFileExt proc`_
+  ## * `addFileExt proc`_
+  runnableExamples:
+    assert extractFilename("foo/bar/") == ""
+    assert extractFilename("foo/bar") == "bar"
+    assert extractFilename("foo/bar.baz") == "bar.baz"
+
+  if path.len == 0 or path[path.len-1] in {DirSep, AltSep}:
+    result = ""
+  else:
+    result = splitPath(path).tail
+
+proc lastPathPart*(path: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Like `extractFilename proc`_, but ignores
+  ## trailing dir separator; aka: `baseName`:idx: in some other languages.
+  ##
+  ## See also:
+  ## * `searchExtPos proc`_
+  ## * `splitFile proc`_
+  ## * `extractFilename proc`_
+  ## * `changeFileExt proc`_
+  ## * `addFileExt proc`_
+  runnableExamples:
+    assert lastPathPart("foo/bar/") == "bar"
+    assert lastPathPart("foo/bar") == "bar"
+
+  let path = path.normalizePathEnd(trailingSep = false)
+  result = extractFilename(path)
+
+proc changeFileExt*(filename, ext: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Changes the file extension to `ext`.
+  ##
+  ## If the `filename` has no extension, `ext` will be added.
+  ## If `ext` == "" then any extension is removed.
+  ##
+  ## `Ext` should be given without the leading `'.'`, because some
+  ## filesystems may use a different character. (Although I know
+  ## of none such beast.)
+  ##
+  ## See also:
+  ## * `searchExtPos proc`_
+  ## * `splitFile proc`_
+  ## * `extractFilename proc`_
+  ## * `lastPathPart proc`_
+  ## * `addFileExt proc`_
+  runnableExamples:
+    assert changeFileExt("foo.bar", "baz") == "foo.baz"
+    assert changeFileExt("foo.bar", "") == "foo"
+    assert changeFileExt("foo", "baz") == "foo.baz"
+
+  var extPos = searchExtPos(filename)
+  if extPos < 0: result = filename & normExt(ext)
+  else: result = substr(filename, 0, extPos-1) & normExt(ext)
+
+proc addFileExt*(filename, ext: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Adds the file extension `ext` to `filename`, unless
+  ## `filename` already has an extension.
+  ##
+  ## `Ext` should be given without the leading `'.'`, because some
+  ## filesystems may use a different character.
+  ## (Although I know of none such beast.)
+  ##
+  ## See also:
+  ## * `searchExtPos proc`_
+  ## * `splitFile proc`_
+  ## * `extractFilename proc`_
+  ## * `lastPathPart proc`_
+  ## * `changeFileExt proc`_
+  runnableExamples:
+    assert addFileExt("foo.bar", "baz") == "foo.bar"
+    assert addFileExt("foo.bar", "") == "foo.bar"
+    assert addFileExt("foo", "baz") == "foo.baz"
+
+  var extPos = searchExtPos(filename)
+  if extPos < 0: result = filename & normExt(ext)
+  else: result = filename
+
+proc cmpPaths*(pathA, pathB: string): int {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Compares two paths.
+  ##
+  ## On a case-sensitive filesystem this is done
+  ## case-sensitively otherwise case-insensitively. Returns:
+  ##
+  ## | `0` if pathA == pathB
+  ## | `< 0` if pathA < pathB
+  ## | `> 0` if pathA > pathB
+  runnableExamples:
+    when defined(macosx):
+      assert cmpPaths("foo", "Foo") == 0
+    elif defined(posix):
+      assert cmpPaths("foo", "Foo") > 0
+
+  let a = normalizePath(pathA)
+  let b = normalizePath(pathB)
+  if FileSystemCaseSensitive:
+    result = cmp(a, b)
+  else:
+    when defined(nimscript):
+      result = cmpic(a, b)
+    elif defined(nimdoc): discard
+    else:
+      result = cmpIgnoreCase(a, b)
+
+proc unixToNativePath*(path: string, drive=""): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Converts an UNIX-like path to a native one.
+  ##
+  ## On an UNIX system this does nothing. Else it converts
+  ## `'/'`, `'.'`, `'..'` to the appropriate things.
+  ##
+  ## On systems with a concept of "drives", `drive` is used to determine
+  ## which drive label to use during absolute path conversion.
+  ## `drive` defaults to the drive of the current working directory, and is
+  ## ignored on systems that do not have a concept of "drives".
+  when defined(unix):
+    result = path
+  else:
+    if path.len == 0: return ""
+
+    var start: int
+    if path[0] == '/':
+      # an absolute path
+      when doslikeFileSystem:
+        if drive != "":
+          result = drive & ":" & DirSep
+        else:
+          result = $DirSep
+      elif defined(macos):
+        result = "" # must not start with ':'
+      else:
+        result = $DirSep
+      start = 1
+    elif path[0] == '.' and (path.len == 1 or path[1] == '/'):
+      # current directory
+      result = $CurDir
+      start = when doslikeFileSystem: 1 else: 2
+    else:
+      result = ""
+      start = 0
+
+    var i = start
+    while i < len(path): # ../../../ --> ::::
+      if i+2 < path.len and path[i] == '.' and path[i+1] == '.' and path[i+2] == '/':
+        # parent directory
+        when defined(macos):
+          if result[high(result)] == ':':
+            add result, ':'
+          else:
+            add result, ParDir
+        else:
+          add result, ParDir & DirSep
+        inc(i, 3)
+      elif path[i] == '/':
+        add result, DirSep
+        inc(i)
+      else:
+        add result, path[i]
+        inc(i)
+
+
+when not defined(nimscript):
+  proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
+    ## Returns the `current working directory`:idx: i.e. where the built
+    ## binary is run.
+    ##
+    ## So the path returned by this proc is determined at run time.
+    ##
+    ## See also:
+    ## * `getHomeDir proc`_
+    ## * `getConfigDir proc`_
+    ## * `getTempDir proc`_
+    ## * `setCurrentDir proc`_
+    ## * `currentSourcePath template <system.html#currentSourcePath.t>`_
+    ## * `getProjectPath proc <macros.html#getProjectPath>`_
+    when defined(nodejs):
+      var ret: cstring
+      {.emit: "`ret` = process.cwd();".}
+      return $ret
+    elif defined(js):
+      raiseAssert "use -d:nodejs to have `getCurrentDir` defined"
+    elif defined(windows):
+      var bufsize = MAX_PATH.int32
+      var res = newWideCString(bufsize)
+      while true:
+        var L = getCurrentDirectoryW(bufsize, res)
+        if L == 0'i32:
+          raiseOSError(osLastError())
+        elif L > bufsize:
+          res = newWideCString(L)
+          bufsize = L
+        else:
+          result = res$L
+          break
+    else:
+      var bufsize = 1024 # should be enough
+      result = newString(bufsize)
+      while true:
+        if getcwd(result.cstring, bufsize) != nil:
+          setLen(result, c_strlen(result.cstring))
+          break
+        else:
+          let err = osLastError()
+          if err.int32 == ERANGE:
+            bufsize = bufsize shl 1
+            doAssert(bufsize >= 0)
+            result = newString(bufsize)
+          else:
+            raiseOSError(osLastError())
+
+proc absolutePath*(path: string, root = getCurrentDir()): string =
+  ## Returns the absolute path of `path`, rooted at `root` (which must be absolute;
+  ## default: current directory).
+  ## If `path` is absolute, return it, ignoring `root`.
+  ##
+  ## See also:
+  ## * `normalizedPath proc`_
+  ## * `normalizePath proc`_
+  runnableExamples:
+    assert absolutePath("a") == getCurrentDir() / "a"
+
+  if isAbsolute(path): path
+  else:
+    if not root.isAbsolute:
+      raise newException(ValueError, "The specified root is not absolute: " & root)
+    joinPath(root, path)
+
+proc absolutePathInternal(path: string): string =
+  absolutePath(path, getCurrentDir())
+
+
+proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [].} =
+  ## Normalize a path.
+  ##
+  ## Consecutive directory separators are collapsed, including an initial double slash.
+  ##
+  ## On relative paths, double dot (`..`) sequences are collapsed if possible.
+  ## On absolute paths they are always collapsed.
+  ##
+  ## .. warning:: URL-encoded and Unicode attempts at directory traversal are not detected.
+  ##   Triple dot is not handled.
+  ##
+  ## See also:
+  ## * `absolutePath proc`_
+  ## * `normalizedPath proc`_ for outplace version
+  ## * `normalizeExe proc`_
+  runnableExamples:
+    when defined(posix):
+      var a = "a///b//..//c///d"
+      a.normalizePath()
+      assert a == "a/c/d"
+
+  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)
+        else:
+          discard stack.pop()
+      else:
+        stack.add(p)
+
+    if isAbs:
+      path = DirSep & join(stack, $DirSep)
+    elif stack.len > 0:
+      path = join(stack, $DirSep)
+    else:
+      path = "."
+
+proc normalizePathAux(path: var string) = normalizePath(path)
+
+proc normalizedPath*(path: string): string {.rtl, extern: "nos$1", tags: [].} =
+  ## Returns a normalized path for the current OS.
+  ##
+  ## See also:
+  ## * `absolutePath proc`_
+  ## * `normalizePath proc`_ for the in-place version
+  runnableExamples:
+    when defined(posix):
+      assert normalizedPath("a///b//..//c///d") == "a/c/d"
+  result = pathnorm.normalizePath(path)
+
+proc normalizeExe*(file: var string) {.since: (1, 3, 5).} =
+  ## on posix, prepends `./` if `file` doesn't contain `/` and is not `"", ".", ".."`.
+  runnableExamples:
+    import std/sugar
+    when defined(posix):
+      doAssert "foo".dup(normalizeExe) == "./foo"
+      doAssert "foo/../bar".dup(normalizeExe) == "foo/../bar"
+    doAssert "".dup(normalizeExe) == ""
+  when defined(posix):
+    if file.len > 0 and DirSep notin file and file != "." and file != "..":
+      file = "./" & file
+
+proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
+  tags: [ReadDirEffect], noWeirdTarget.} =
+  ## Returns true if both pathname arguments refer to the same physical
+  ## file or directory.
+  ##
+  ## Raises `OSError` if any of the files does not
+  ## exist or information about it can not be obtained.
+  ##
+  ## This proc will return true if given two alternative hard-linked or
+  ## sym-linked paths to the same file or directory.
+  ##
+  ## See also:
+  ## * `sameFileContent proc`_
+  when defined(windows):
+    var success = true
+    var f1 = openHandle(path1)
+    var f2 = openHandle(path2)
+
+    var lastErr: OSErrorCode
+    if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
+      var fi1, fi2: BY_HANDLE_FILE_INFORMATION
+
+      if getFileInformationByHandle(f1, addr(fi1)) != 0 and
+         getFileInformationByHandle(f2, addr(fi2)) != 0:
+        result = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber and
+                 fi1.nFileIndexHigh == fi2.nFileIndexHigh and
+                 fi1.nFileIndexLow == fi2.nFileIndexLow
+      else:
+        lastErr = osLastError()
+        success = false
+    else:
+      lastErr = osLastError()
+      success = false
+
+    discard closeHandle(f1)
+    discard closeHandle(f2)
+
+    if not success: raiseOSError(lastErr, $(path1, path2))
+  else:
+    var a, b: Stat
+    if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32:
+      raiseOSError(osLastError(), $(path1, path2))
+    else:
+      result = a.st_dev == b.st_dev and a.st_ino == b.st_ino
diff --git a/lib/std/private/osseps.nim b/lib/std/private/osseps.nim
new file mode 100644
index 000000000..f2d49d886
--- /dev/null
+++ b/lib/std/private/osseps.nim
@@ -0,0 +1,113 @@
+# Include file that implements 'DirSep' and friends. Do not import this when
+# you also import `os.nim`!
+
+# Improved based on info in 'compiler/platform.nim'
+
+## .. importdoc:: ospaths2.nim
+
+const
+  doslikeFileSystem* = defined(windows) or defined(OS2) or defined(DOS)
+
+const
+  CurDir* =
+    when defined(macos): ':'
+    elif defined(genode): '/'
+    else: '.'
+    ## The constant character used by the operating system to refer to the
+    ## current directory.
+    ##
+    ## For example: `'.'` for POSIX or `':'` for the classic Macintosh.
+
+  ParDir* =
+    when defined(macos): "::"
+    else: ".."
+    ## The constant string used by the operating system to refer to the
+    ## parent directory.
+    ##
+    ## For example: `".."` for POSIX or `"::"` for the classic Macintosh.
+
+  DirSep* =
+    when defined(macos): ':'
+    elif doslikeFileSystem or defined(vxworks): '\\'
+    elif defined(RISCOS): '.'
+    else: '/'
+    ## The character used by the operating system to separate pathname
+    ## components, for example: `'/'` for POSIX, `':'` for the classic
+    ## Macintosh, and `'\\'` on Windows.
+
+  AltSep* =
+    when doslikeFileSystem: '/'
+    else: DirSep
+    ## An alternative character used by the operating system to separate
+    ## pathname components, or the same as DirSep_ if only one separator
+    ## character exists. This is set to `'/'` on Windows systems
+    ## where DirSep_ is a backslash (`'\\'`).
+
+  PathSep* =
+    when defined(macos) or defined(RISCOS): ','
+    elif doslikeFileSystem or defined(vxworks): ';'
+    elif defined(PalmOS) or defined(MorphOS): ':' # platform has ':' but osseps has ';'
+    else: ':'
+    ## The character conventionally used by the operating system to separate
+    ## search path components (as in PATH), such as `':'` for POSIX
+    ## or `';'` for Windows.
+
+  FileSystemCaseSensitive* =
+    when defined(macos) or defined(macosx) or doslikeFileSystem or defined(vxworks) or
+         defined(PalmOS) or defined(MorphOS): false
+    else: true
+    ## True if the file system is case sensitive, false otherwise. Used by
+    ## `cmpPaths proc`_ to compare filenames properly.
+
+  ExeExt* =
+    when doslikeFileSystem: "exe"
+    elif defined(atari): "tpp"
+    elif defined(netware): "nlm"
+    elif defined(vxworks): "vxe"
+    elif defined(nintendoswitch): "elf"
+    else: ""
+    ## The file extension of native executables. For example:
+    ## `""` for POSIX, `"exe"` on Windows (without a dot).
+
+  ScriptExt* =
+    when doslikeFileSystem: "bat"
+    else: ""
+    ## The file extension of a script file. For example: `""` for POSIX,
+    ## `"bat"` on Windows.
+
+  DynlibFormat* =
+    when defined(macos): "$1.dylib" # platform has $1Lib
+    elif defined(macosx): "lib$1.dylib"
+    elif doslikeFileSystem or defined(atari): "$1.dll"
+    elif defined(MorphOS): "$1.prc"
+    elif defined(PalmOS): "$1.prc" # platform has lib$1.so
+    elif defined(genode): "$1.lib.so"
+    elif defined(netware): "$1.nlm"
+    elif defined(amiga): "$1.Library"
+    else: "lib$1.so"
+    ## The format string to turn a filename into a `DLL`:idx: file (also
+    ## called `shared object`:idx: on some operating systems).
+
+  ExtSep* = '.'
+    ## The character which separates the base filename from the extension;
+    ## for example, the `'.'` in `os.nim`.
+
+  #  MacOS paths
+  #  ===========
+  #  MacOS directory separator is a colon ":" which is the only character not
+  #  allowed in filenames.
+  #
+  #  A path containing no colon or which begins with a colon is a partial
+  #  path.
+  #  E.g. ":kalle:petter" ":kalle" "kalle"
+  #
+  #  All other paths are full (absolute) paths. E.g. "HD:kalle:" "HD:"
+  #  When generating paths, one is safe if one ensures that all partial paths
+  #  begin with a colon, and all full paths end with a colon.
+  #  In full paths the first name (e g HD above) is the name of a mounted
+  #  volume.
+  #  These names are not unique, because, for instance, two diskettes with the
+  #  same names could be inserted. This means that paths on MacOS are not
+  #  waterproof. In case of equal names the first volume found will do.
+  #  Two colons "::" are the relative path to the parent. Three is to the
+  #  grandparent etc.
diff --git a/lib/std/private/ossymlinks.nim b/lib/std/private/ossymlinks.nim
new file mode 100644
index 000000000..c1760c42e
--- /dev/null
+++ b/lib/std/private/ossymlinks.nim
@@ -0,0 +1,78 @@
+include system/inclrtl
+import std/oserrors
+
+import oscommon
+export symlinkExists
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions, widestrs]
+
+when weirdTarget:
+  discard
+elif defined(windows):
+  import std/[winlean, times]
+elif defined(posix):
+  import std/posix
+else:
+  {.error: "OS module not ported to your operating system!".}
+
+
+when weirdTarget:
+  {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".}
+else:
+  {.pragma: noWeirdTarget.}
+
+
+when defined(nimscript):
+  # for procs already defined in scriptconfig.nim
+  template noNimJs(body): untyped = discard
+elif defined(js):
+  {.pragma: noNimJs, error: "this proc is not available on the js target".}
+else:
+  {.pragma: noNimJs.}
+
+## .. importdoc:: os.nim
+
+proc createSymlink*(src, dest: string) {.noWeirdTarget.} =
+  ## Create a symbolic link at `dest` which points to the item specified
+  ## by `src`. On most operating systems, will fail if a link already exists.
+  ##
+  ## .. warning:: Some OS's (such as Microsoft Windows) restrict the creation
+  ##   of symlinks to root users (administrators) or users with developer mode enabled.
+  ##
+  ## See also:
+  ## * `createHardlink proc`_
+  ## * `expandSymlink proc`_
+
+  when defined(windows):
+    const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 2
+    # allows anyone with developer mode on to create a link
+    let flag = dirExists(src).int32 or SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
+    var wSrc = newWideCString(src)
+    var wDst = newWideCString(dest)
+    if createSymbolicLinkW(wDst, wSrc, flag) == 0 or getLastError() != 0:
+      raiseOSError(osLastError(), $(src, dest))
+  else:
+    if symlink(src, dest) != 0:
+      raiseOSError(osLastError(), $(src, dest))
+
+proc expandSymlink*(symlinkPath: string): string {.noWeirdTarget.} =
+  ## Returns a string representing the path to which the symbolic link points.
+  ##
+  ## On Windows this is a noop, `symlinkPath` is simply returned.
+  ##
+  ## See also:
+  ## * `createSymlink proc`_
+  when defined(windows) or defined(nintendoswitch):
+    result = symlinkPath
+  else:
+    var bufLen = 1024
+    while true:
+      result = newString(bufLen)
+      let len = readlink(symlinkPath.cstring, result.cstring, bufLen)
+      if len < 0:
+        raiseOSError(osLastError(), symlinkPath)
+      if len < bufLen:
+        result.setLen(len)
+        break
+      bufLen = bufLen shl 1
diff --git a/lib/std/private/schubfach.nim b/lib/std/private/schubfach.nim
new file mode 100644
index 000000000..b8c85d2bc
--- /dev/null
+++ b/lib/std/private/schubfach.nim
@@ -0,0 +1,436 @@
+##  Copyright 2020 Alexander Bolz
+##
+##  Distributed under the Boost Software License, Version 1.0.
+##   (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
+
+# --------------------------------------------------------------------------------------------------
+##  This file contains an implementation of the Schubfach algorithm as described in
+##
+##  \[1] Raffaello Giulietti, "The Schubfach way to render doubles",
+##      https://drive.google.com/open?id=1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN
+# --------------------------------------------------------------------------------------------------
+
+import std/private/digitsutils
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
+template sf_Assert(x: untyped): untyped =
+  assert(x)
+
+# ==================================================================================================
+#
+# ==================================================================================================
+
+type
+  ValueType = float32
+  BitsType = uint32
+  Single {.bycopy.} = object
+    bits: BitsType
+
+const
+  significandSize: int32 = 24
+  MaxExponent = 128
+  exponentBias: int32 = MaxExponent - 1 + (significandSize - 1)
+  maxIeeeExponent: BitsType = BitsType(2 * MaxExponent - 1)
+  hiddenBit: BitsType = BitsType(1) shl (significandSize - 1)
+  significandMask: BitsType = hiddenBit - 1
+  exponentMask: BitsType = maxIeeeExponent shl (significandSize - 1)
+  signMask: BitsType = not (not BitsType(0) shr 1)
+
+proc constructSingle(bits: BitsType): Single  =
+  result.bits = bits
+
+proc constructSingle(value: ValueType): Single  =
+  result.bits = cast[typeof(result.bits)](value)
+
+proc physicalSignificand(this: Single): BitsType {.noSideEffect.} =
+  return this.bits and significandMask
+
+proc physicalExponent(this: Single): BitsType {.noSideEffect.} =
+  return (this.bits and exponentMask) shr (significandSize - 1)
+
+proc isFinite(this: Single): bool {.noSideEffect.} =
+  return (this.bits and exponentMask) != exponentMask
+
+proc isInf(this: Single): bool {.noSideEffect.} =
+  return (this.bits and exponentMask) == exponentMask and
+      (this.bits and significandMask) == 0
+
+proc isNaN(this: Single): bool {.noSideEffect.} =
+  return (this.bits and exponentMask) == exponentMask and
+      (this.bits and significandMask) != 0
+
+proc isZero(this: Single): bool {.noSideEffect.} =
+  return (this.bits and not signMask) == 0
+
+proc signBit(this: Single): int {.noSideEffect.} =
+  return int((this.bits and signMask) != 0)
+
+# ==================================================================================================
+##  Returns floor(x / 2^n).
+##
+##  Technically, right-shift of negative integers is implementation defined...
+##  Should easily be optimized into SAR (or equivalent) instruction.
+
+proc floorDivPow2(x: int32; n: int32): int32 {.inline.} =
+  return x shr n
+
+##  Returns floor(log_10(2^e))
+##  ```c
+##  static inline int32_t FloorLog10Pow2(int32_t e)
+##  {
+##      SF_ASSERT(e >= -1500);
+##      SF_ASSERT(e <=  1500);
+##      return FloorDivPow2(e * 1262611, 22);
+##  }
+##  ```
+##  Returns floor(log_10(3/4 2^e))
+##  ```c
+##  static inline int32_t FloorLog10ThreeQuartersPow2(int32_t e)
+##  {
+##      SF_ASSERT(e >= -1500);
+##      SF_ASSERT(e <=  1500);
+##      return FloorDivPow2(e * 1262611 - 524031, 22);
+##  }
+##  ```
+##  Returns floor(log_2(10^e))
+
+proc floorLog2Pow10(e: int32): int32 {.inline.} =
+  sf_Assert(e >= -1233)
+  sf_Assert(e <= 1233)
+  return floorDivPow2(e * 1741647, 19)
+
+const
+  kMin: int32 = -31
+  kMax: int32 = 45
+  g: array[kMax - kMin + 1, uint64] = [0x81CEB32C4B43FCF5'u64, 0xA2425FF75E14FC32'u64,
+    0xCAD2F7F5359A3B3F'u64, 0xFD87B5F28300CA0E'u64, 0x9E74D1B791E07E49'u64,
+    0xC612062576589DDB'u64, 0xF79687AED3EEC552'u64, 0x9ABE14CD44753B53'u64,
+    0xC16D9A0095928A28'u64, 0xF1C90080BAF72CB2'u64, 0x971DA05074DA7BEF'u64,
+    0xBCE5086492111AEB'u64, 0xEC1E4A7DB69561A6'u64, 0x9392EE8E921D5D08'u64,
+    0xB877AA3236A4B44A'u64, 0xE69594BEC44DE15C'u64, 0x901D7CF73AB0ACDA'u64,
+    0xB424DC35095CD810'u64, 0xE12E13424BB40E14'u64, 0x8CBCCC096F5088CC'u64,
+    0xAFEBFF0BCB24AAFF'u64, 0xDBE6FECEBDEDD5BF'u64, 0x89705F4136B4A598'u64,
+    0xABCC77118461CEFD'u64, 0xD6BF94D5E57A42BD'u64, 0x8637BD05AF6C69B6'u64,
+    0xA7C5AC471B478424'u64, 0xD1B71758E219652C'u64, 0x83126E978D4FDF3C'u64,
+    0xA3D70A3D70A3D70B'u64, 0xCCCCCCCCCCCCCCCD'u64, 0x8000000000000000'u64,
+    0xA000000000000000'u64, 0xC800000000000000'u64, 0xFA00000000000000'u64,
+    0x9C40000000000000'u64, 0xC350000000000000'u64, 0xF424000000000000'u64,
+    0x9896800000000000'u64, 0xBEBC200000000000'u64, 0xEE6B280000000000'u64,
+    0x9502F90000000000'u64, 0xBA43B74000000000'u64, 0xE8D4A51000000000'u64,
+    0x9184E72A00000000'u64, 0xB5E620F480000000'u64, 0xE35FA931A0000000'u64,
+    0x8E1BC9BF04000000'u64, 0xB1A2BC2EC5000000'u64, 0xDE0B6B3A76400000'u64,
+    0x8AC7230489E80000'u64, 0xAD78EBC5AC620000'u64, 0xD8D726B7177A8000'u64,
+    0x878678326EAC9000'u64, 0xA968163F0A57B400'u64, 0xD3C21BCECCEDA100'u64,
+    0x84595161401484A0'u64, 0xA56FA5B99019A5C8'u64, 0xCECB8F27F4200F3A'u64,
+    0x813F3978F8940985'u64, 0xA18F07D736B90BE6'u64, 0xC9F2C9CD04674EDF'u64,
+    0xFC6F7C4045812297'u64, 0x9DC5ADA82B70B59E'u64, 0xC5371912364CE306'u64,
+    0xF684DF56C3E01BC7'u64, 0x9A130B963A6C115D'u64, 0xC097CE7BC90715B4'u64,
+    0xF0BDC21ABB48DB21'u64, 0x96769950B50D88F5'u64, 0xBC143FA4E250EB32'u64,
+    0xEB194F8E1AE525FE'u64, 0x92EFD1B8D0CF37BF'u64, 0xB7ABC627050305AE'u64,
+    0xE596B7B0C643C71A'u64, 0x8F7E32CE7BEA5C70'u64, 0xB35DBF821AE4F38C'u64]
+
+proc computePow10Single(k: int32): uint64 {.inline.} =
+  ##  There are unique beta and r such that 10^k = beta 2^r and
+  ##  2^63 <= beta < 2^64, namely r = floor(log_2 10^k) - 63 and
+  ##  beta = 2^-r 10^k.
+  ##  Let g = ceil(beta), so (g-1) 2^r < 10^k <= g 2^r, with the latter
+  ##  value being a pretty good overestimate for 10^k.
+  ##  NB: Since for all the required exponents k, we have g < 2^64,
+  ##      all constants can be stored in 128-bit integers.
+  sf_Assert(k >= kMin)
+  sf_Assert(k <= kMax)
+  return g[k - kMin]
+
+proc lo32(x: uint64): uint32 {.inline.} =
+  return cast[uint32](x)
+
+proc hi32(x: uint64): uint32 {.inline.} =
+  return cast[uint32](x shr 32)
+
+when defined(sizeof_Int128):
+  proc roundToOdd(g: uint64; cp: uint32): uint32 {.inline.} =
+    let p: uint128 = uint128(g) * cp
+    let y1: uint32 = lo32(cast[uint64](p shr 64))
+    let y0: uint32 = hi32(cast[uint64](p))
+    return y1 or uint32(y0 > 1)
+
+elif defined(vcc) and defined(cpu64):
+  proc umul128(x, y: uint64, z: ptr uint64): uint64 {.importc: "_umul128", header: "<intrin.h>".}
+  proc roundToOdd(g: uint64; cpHi: uint32): uint32 {.inline.} =
+    var p1: uint64 = 0
+    var p0: uint64 = umul128(g, cpHi, addr(p1))
+    let y1: uint32 = lo32(p1)
+    let y0: uint32 = hi32(p0)
+    return y1 or uint32(y0 > 1)
+
+else:
+  proc roundToOdd(g: uint64; cp: uint32): uint32 {.inline.} =
+    let b01: uint64 = uint64(lo32(g)) * cp
+    let b11: uint64 = uint64(hi32(g)) * cp
+    let hi: uint64 = b11 + hi32(b01)
+    let y1: uint32 = hi32(hi)
+    let y0: uint32 = lo32(hi)
+    return y1 or uint32(y0 > 1)
+
+##  Returns whether value is divisible by 2^e2
+
+proc multipleOfPow2(value: uint32; e2: int32): bool {.inline.} =
+  sf_Assert(e2 >= 0)
+  sf_Assert(e2 <= 31)
+  return (value and ((uint32(1) shl e2) - 1)) == 0
+
+type
+  FloatingDecimal32 {.bycopy.} = object
+    digits: uint32            ##  num_digits <= 9
+    exponent: int32
+
+proc toDecimal32(ieeeSignificand: uint32; ieeeExponent: uint32): FloatingDecimal32 {.
+    inline.} =
+  var c: uint32
+  var q: int32
+  if ieeeExponent != 0:
+    c = hiddenBit or ieeeSignificand
+    q = cast[int32](ieeeExponent) - exponentBias
+    if 0 <= -q and -q < significandSize and multipleOfPow2(c, -q):
+      return FloatingDecimal32(digits: c shr -q, exponent: 0'i32)
+  else:
+    c = ieeeSignificand
+    q = 1 - exponentBias
+  let isEven: bool = (c mod 2 == 0)
+  let lowerBoundaryIsCloser: bool = (ieeeSignificand == 0 and ieeeExponent > 1)
+  ##   const int32_t qb = q - 2;
+  let cbl: uint32 = 4 * c - 2 + uint32(lowerBoundaryIsCloser)
+  let cb: uint32 = 4 * c
+  let cbr: uint32 = 4 * c + 2
+  ##  (q * 1262611         ) >> 22 == floor(log_10(    2^q))
+  ##  (q * 1262611 - 524031) >> 22 == floor(log_10(3/4 2^q))
+  sf_Assert(q >= -1500)
+  sf_Assert(q <= 1500)
+  let k: int32 = floorDivPow2(q * 1262611 - (if lowerBoundaryIsCloser: 524031 else: 0), 22)
+  let h: int32 = q + floorLog2Pow10(-k) + 1
+  sf_Assert(h >= 1)
+  sf_Assert(h <= 4)
+  let pow10: uint64 = computePow10Single(-k)
+  let vbl: uint32 = roundToOdd(pow10, cbl shl h)
+  let vb: uint32 = roundToOdd(pow10, cb shl h)
+  let vbr: uint32 = roundToOdd(pow10, cbr shl h)
+  let lower: uint32 = vbl + uint32(not isEven)
+  let upper: uint32 = vbr - uint32(not isEven)
+  ##  See Figure 4 in [1].
+  ##  And the modifications in Figure 6.
+  let s: uint32 = vb div 4
+  ##  NB: 4 * s == vb & ~3 == vb & -4
+  if s >= 10:
+    let sp: uint32 = s div 10
+    ##  = vb / 40
+    let upInside: bool = lower <= 40 * sp
+    let wpInside: bool = 40 * sp + 40 <= upper
+    ##       if (up_inside || wp_inside) // NB: At most one of u' and w' is in R_v.
+    if upInside != wpInside:
+      return FloatingDecimal32(digits: sp + uint32(wpInside), exponent: k + 1)
+  let uInside: bool = lower <= 4 * s
+  let wInside: bool = 4 * s + 4 <= upper
+  if uInside != wInside:
+    return FloatingDecimal32(digits: s + uint32(wInside), exponent: k)
+  let mid: uint32 = 4 * s + 2
+  ##  = 2(s + t)
+  let roundUp: bool = vb > mid or (vb == mid and (s and 1) != 0)
+  return FloatingDecimal32(digits: s + uint32(roundUp), exponent: k)
+
+## ==================================================================================================
+##  ToChars
+## ==================================================================================================
+
+proc printDecimalDigitsBackwards[T: Ordinal](buf: var openArray[char]; pos: T; output: uint32): int {.inline.} =
+  var output = output
+  var pos = pos
+  var tz = 0
+  ##  number of trailing zeros removed.
+  var nd = 0
+  ##  number of decimal digits processed.
+  ##  At most 9 digits remaining
+  if output >= 10000:
+    let q: uint32 = output div 10000
+    let r: uint32 = output mod 10000
+    output = q
+    dec(pos, 4)
+    if r != 0:
+      let rH: uint32 = r div 100
+      let rL: uint32 = r mod 100
+      utoa2Digits(buf, pos, rH)
+      utoa2Digits(buf, pos + 2, rL)
+      tz = trailingZeros2Digits(if rL == 0: rH else: rL) + (if rL == 0: 2 else: 0)
+    else:
+      tz = 4
+    nd = 4
+  if output >= 100:
+    let q: uint32 = output div 100
+    let r: uint32 = output mod 100
+    output = q
+    dec(pos, 2)
+    utoa2Digits(buf, pos, r)
+    if tz == nd:
+      inc(tz, trailingZeros2Digits(r))
+    inc(nd, 2)
+    if output >= 100:
+      let q2: uint32 = output div 100
+      let r2: uint32 = output mod 100
+      output = q2
+      dec(pos, 2)
+      utoa2Digits(buf, pos, r2)
+      if tz == nd:
+        inc(tz, trailingZeros2Digits(r2))
+      inc(nd, 2)
+  sf_Assert(output >= 1)
+  sf_Assert(output <= 99)
+  if output >= 10:
+    let q: uint32 = output
+    dec(pos, 2)
+    utoa2Digits(buf, pos, q)
+    if tz == nd:
+      inc(tz, trailingZeros2Digits(q))
+  else:
+    let q: uint32 = output
+    sf_Assert(q >= 1)
+    sf_Assert(q <= 9)
+    dec(pos)
+    buf[pos] = chr(uint32('0') + q)
+  return tz
+
+proc decimalLength(v: uint32): int {.inline.} =
+  sf_Assert(v >= 1)
+  sf_Assert(v <= 999999999'u)
+  if v >= 100000000'u:
+    return 9
+  if v >= 10000000'u:
+    return 8
+  if v >= 1000000'u:
+    return 7
+  if v >= 100000'u:
+    return 6
+  if v >= 10000'u:
+    return 5
+  if v >= 1000'u:
+    return 4
+  if v >= 100'u:
+    return 3
+  if v >= 10'u:
+    return 2
+  return 1
+
+proc formatDigits[T: Ordinal](buffer: var openArray[char]; pos: T; digits: uint32; decimalExponent: int;
+                  forceTrailingDotZero: bool = false): int {.inline.} =
+  const
+    minFixedDecimalPoint: int32 = -4
+    maxFixedDecimalPoint: int32 = 9
+  var pos = pos
+  assert(minFixedDecimalPoint <= -1, "internal error")
+  assert(maxFixedDecimalPoint >= 1, "internal error")
+  sf_Assert(digits >= 1)
+  sf_Assert(digits <= 999999999'u)
+  sf_Assert(decimalExponent >= -99)
+  sf_Assert(decimalExponent <= 99)
+  var numDigits = decimalLength(digits)
+  let decimalPoint = numDigits + decimalExponent
+  let useFixed: bool = minFixedDecimalPoint <= decimalPoint and
+      decimalPoint <= maxFixedDecimalPoint
+  ##  Prepare the buffer.
+  ##  Avoid calling memset/memcpy with variable arguments below...
+  for i in 0..<32: buffer[pos+i] = '0'
+  assert(minFixedDecimalPoint >= -30, "internal error")
+  assert(maxFixedDecimalPoint <= 32, "internal error")
+  var decimalDigitsPosition: int
+  if useFixed:
+    if decimalPoint <= 0:
+      ##  0.[000]digits
+      decimalDigitsPosition = 2 - decimalPoint
+    else:
+      ##  dig.its
+      ##  digits[000]
+      decimalDigitsPosition = 0
+  else:
+    ##  dE+123 or d.igitsE+123
+    decimalDigitsPosition = 1
+  var digitsEnd = pos + decimalDigitsPosition + numDigits
+  let tz = printDecimalDigitsBackwards(buffer, digitsEnd, digits)
+  dec(digitsEnd, tz)
+  dec(numDigits, tz)
+  ##   decimal_exponent += tz; // => decimal_point unchanged.
+  if useFixed:
+    if decimalPoint <= 0:
+      ##  0.[000]digits
+      buffer[pos+1] = '.'
+      pos = digitsEnd
+    elif decimalPoint < numDigits:
+      ##  dig.its
+      for i in countdown(7, 0):
+        buffer[i + decimalPoint + 1] = buffer[i + decimalPoint]
+      buffer[pos+decimalPoint] = '.'
+      pos = digitsEnd + 1
+    else:
+      ##  digits[000]
+      inc(pos, decimalPoint)
+      if forceTrailingDotZero:
+        buffer[pos] = '.'
+        buffer[pos+1] = '0'
+        inc(pos, 2)
+  else:
+    buffer[pos] = buffer[pos+1]
+    if numDigits == 1:
+      ##  dE+123
+      inc(pos)
+    else:
+      ##  d.igitsE+123
+      buffer[pos+1] = '.'
+      pos = digitsEnd
+    let scientificExponent = decimalPoint - 1
+    ##       SF_ASSERT(scientific_exponent != 0);
+    buffer[pos] = 'e'
+    buffer[pos+1] = if scientificExponent < 0: '-' else: '+'
+    inc(pos, 2)
+    let k: uint32 = cast[uint32](if scientificExponent < 0: -scientificExponent else: scientificExponent)
+    if k < 10:
+      buffer[pos] = chr(uint32('0') + k)
+      inc pos
+    else:
+      utoa2Digits(buffer, pos, k)
+      inc(pos, 2)
+  return pos
+
+proc float32ToChars*(buffer: var openArray[char]; v: float32; forceTrailingDotZero = false): int {.
+    inline.} =
+  let significand: uint32 = physicalSignificand(constructSingle(v))
+  let exponent: uint32 = physicalExponent(constructSingle(v))
+  var pos = 0
+  if exponent != maxIeeeExponent:
+    ##  Finite
+    buffer[pos] = '-'
+    inc(pos, signBit(constructSingle(v)))
+    if exponent != 0 or significand != 0:
+      ##  != 0
+      let dec: auto = toDecimal32(significand, exponent)
+      return formatDigits(buffer, pos, dec.digits, dec.exponent.int, forceTrailingDotZero)
+    else:
+      buffer[pos] = '0'
+      buffer[pos+1] = '.'
+      buffer[pos+2] = '0'
+      buffer[pos+3] = ' '
+      inc(pos, if forceTrailingDotZero: 3 else: 1)
+      return pos
+  if significand == 0:
+    buffer[pos] = '-'
+    inc(pos, signBit(constructSingle(v)))
+    buffer[pos] = 'i'
+    buffer[pos+1] = 'n'
+    buffer[pos+2] = 'f'
+    buffer[pos+3] = ' '
+    return pos + 3
+  else:
+    buffer[pos] = 'n'
+    buffer[pos+1] = 'a'
+    buffer[pos+2] = 'n'
+    buffer[pos+3] = ' '
+    return pos + 3
diff --git a/lib/std/private/since.nim b/lib/std/private/since.nim
new file mode 100644
index 000000000..720120f11
--- /dev/null
+++ b/lib/std/private/since.nim
@@ -0,0 +1,33 @@
+##[
+`since` is used to emulate older versions of nim stdlib,
+see `tuse_version.nim`.
+
+If a symbol `foo` is added in version `(1,3,5)`, use `{.since: (1.3.5).}`, not
+`{.since: (1.4).}`, so that it works in devel in between releases.
+
+The emulation cannot be 100% faithful and to avoid adding too much complexity,
+`since` is not needed in those cases:
+* if a new module is added
+* if an overload is added
+* if an extra parameter to an existing routine is added
+]##
+
+template since*(version: (int, int), body: untyped) {.dirty.} =
+  ## Evaluates `body` if the ``(NimMajor, NimMinor)`` is greater than
+  ## or equal to `version`. Usage:
+  ##   ```Nim
+  ##   proc fun*() {.since: (1, 3).}
+  ##   since (1, 3): fun()
+  ##   ```
+  when (NimMajor, NimMinor) >= version:
+    body
+
+template since*(version: (int, int, int), body: untyped) {.dirty.} =
+  ## Evaluates `body` if ``(NimMajor, NimMinor, NimPatch)`` is greater than 
+  ## or equal to `version`. Usage:
+  ##   ```Nim
+  ##   proc fun*() {.since: (1, 3, 1).}
+  ##   since (1, 3, 1): fun()
+  ##   ```
+  when (NimMajor, NimMinor, NimPatch) >= version:
+    body
diff --git a/lib/std/private/strimpl.nim b/lib/std/private/strimpl.nim
new file mode 100644
index 000000000..f8c9236a5
--- /dev/null
+++ b/lib/std/private/strimpl.nim
@@ -0,0 +1,113 @@
+func toLowerAscii*(c: char): char {.inline.} =
+  if c in {'A'..'Z'}:
+    result = chr(ord(c) + (ord('a') - ord('A')))
+  else:
+    result = c
+
+template firstCharCaseSensitiveImpl[T: string | cstring](a, b: T, aLen, bLen: int) =
+  if aLen == 0 or bLen == 0:
+    return aLen - bLen
+  if a[0] != b[0]: return ord(a[0]) - ord(b[0])
+
+template cmpIgnoreStyleImpl*[T: string | cstring](a, b: T,
+            firstCharCaseSensitive: static bool = false) =
+  let aLen = a.len
+  let bLen = b.len
+  var i = 0
+  var j = 0
+  when firstCharCaseSensitive:
+    firstCharCaseSensitiveImpl(a, b, aLen, bLen)
+    inc i
+    inc j
+  while true:
+    while i < aLen and a[i] == '_': inc i
+    while j < bLen and b[j] == '_': inc j
+    let aa = if i < aLen: toLowerAscii(a[i]) else: '\0'
+    let bb = if j < bLen: toLowerAscii(b[j]) else: '\0'
+    result = ord(aa) - ord(bb)
+    if result != 0: return result
+    # the characters are identical:
+    if i >= aLen:
+      # both cursors at the end:
+      if j >= bLen: return 0
+      # not yet at the end of 'b':
+      return -1
+    elif j >= bLen:
+      return 1
+    inc i
+    inc j
+
+template cmpIgnoreCaseImpl*[T: string | cstring](a, b: T,
+        firstCharCaseSensitive: static bool = false) =
+  let aLen = a.len
+  let bLen = b.len
+  var i = 0
+  when firstCharCaseSensitive:
+    firstCharCaseSensitiveImpl(a, b, aLen, bLen)
+    inc i
+  var m = min(aLen, bLen)
+  while i < m:
+    result = ord(toLowerAscii(a[i])) - ord(toLowerAscii(b[i]))
+    if result != 0: return
+    inc i
+  result = aLen - bLen
+
+template startsWithImpl*[T: string | cstring](s, prefix: T) =
+  let prefixLen = prefix.len
+  let sLen = s.len
+  var i = 0
+  while true:
+    if i >= prefixLen: return true
+    if i >= sLen or s[i] != prefix[i]: return false
+    inc(i)
+
+template endsWithImpl*[T: string | cstring](s, suffix: T) =
+  let suffixLen = suffix.len
+  let sLen = s.len
+  var i = 0
+  var j = sLen - suffixLen
+  while i+j >= 0 and i+j < sLen:
+    if s[i+j] != suffix[i]: return false
+    inc(i)
+  if i >= suffixLen: return true
+
+
+func cmpNimIdentifier*[T: string | cstring](a, b: T): int =
+  cmpIgnoreStyleImpl(a, b, true)
+
+func c_memchr(cstr: pointer, c: char, n: csize_t): pointer {.
+              importc: "memchr", header: "<string.h>".}
+func c_strstr(haystack, needle: cstring): cstring {.
+  importc: "strstr", header: "<string.h>".}
+
+
+func find*(s: cstring, sub: char, start: Natural = 0, last = 0): int =
+  ## Searches for `sub` in `s` inside the 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.
+  ## Otherwise the index returned is relative to `s[0]`, not `start`.
+  ## Use `s[start..last].rfind` for a `start`-origin index.
+  let last = if last == 0: s.high else: last
+  let L = last-start+1
+  if L > 0:
+    let found = c_memchr(s[start].unsafeAddr, sub, cast[csize_t](L))
+    if not found.isNil:
+      return cast[int](found) -% cast[int](s)
+  return -1
+
+func find*(s, sub: cstring, start: Natural = 0, last = 0): int =
+  ## Searches for `sub` in `s` inside the 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.
+  ## Otherwise the index returned is relative to `s[0]`, not `start`.
+  ## Use `s[start..last].find` for a `start`-origin index.
+  if sub.len > s.len - start: return -1
+  if sub.len == 1: return find(s, sub[0], start, last)
+  if last == 0 and s.len > start:
+    let found = c_strstr(cast[cstring](s[start].unsafeAddr), sub)
+    if not found.isNil:
+      result = cast[int](found) -% cast[int](s)
+    else:
+      result = -1
diff --git a/lib/std/private/syslocks.nim b/lib/std/private/syslocks.nim
new file mode 100644
index 000000000..e19ec2c04
--- /dev/null
+++ b/lib/std/private/syslocks.nim
@@ -0,0 +1,234 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# Low level system locks and condition vars.
+
+{.push stackTrace: off.}
+
+when defined(windows):
+  type
+    Handle = int
+
+    SysLock* {.importc: "CRITICAL_SECTION",
+              header: "<windows.h>", final, pure, byref.} = object # CRITICAL_SECTION in WinApi
+      DebugInfo: pointer
+      LockCount: int32
+      RecursionCount: int32
+      OwningThread: int
+      LockSemaphore: int
+      SpinCount: int
+
+    SysCond* {.importc: "RTL_CONDITION_VARIABLE", header: "<windows.h>", byref.} = object
+      thePtr {.importc: "Ptr".} : Handle
+
+  proc initSysLock*(L: var SysLock) {.importc: "InitializeCriticalSection",
+                                     header: "<windows.h>".}
+    ## Initializes the lock `L`.
+
+  proc tryAcquireSysAux(L: var SysLock): int32 {.importc: "TryEnterCriticalSection",
+                                                 header: "<windows.h>".}
+    ## Tries to acquire the lock `L`.
+
+  proc tryAcquireSys*(L: var SysLock): bool {.inline.} =
+    result = tryAcquireSysAux(L) != 0'i32
+
+  proc acquireSys*(L: var SysLock) {.importc: "EnterCriticalSection",
+                                    header: "<windows.h>".}
+    ## Acquires the lock `L`.
+
+  proc releaseSys*(L: var SysLock) {.importc: "LeaveCriticalSection",
+                                    header: "<windows.h>".}
+    ## Releases the lock `L`.
+
+  proc deinitSys*(L: SysLock) {.importc: "DeleteCriticalSection",
+                                   header: "<windows.h>".}
+
+  proc initializeConditionVariable(
+    conditionVariable: var SysCond
+  ) {.stdcall, noSideEffect, dynlib: "kernel32", importc: "InitializeConditionVariable".}
+
+  proc sleepConditionVariableCS(
+    conditionVariable: var SysCond,
+    PCRITICAL_SECTION: var SysLock,
+    dwMilliseconds: int
+  ): int32 {.stdcall, noSideEffect, dynlib: "kernel32", importc: "SleepConditionVariableCS".}
+
+
+  proc signalSysCond*(hEvent: var SysCond) {.stdcall, noSideEffect,
+    dynlib: "kernel32", importc: "WakeConditionVariable".}
+
+  proc broadcastSysCond*(hEvent: var SysCond) {.stdcall, noSideEffect,
+    dynlib: "kernel32", importc: "WakeAllConditionVariable".}
+
+  proc initSysCond*(cond: var SysCond) {.inline.} =
+    initializeConditionVariable(cond)
+  proc deinitSysCond*(cond: SysCond) {.inline.} =
+    discard
+  proc waitSysCond*(cond: var SysCond, lock: var SysLock) =
+    discard sleepConditionVariableCS(cond, lock, -1'i32)
+
+elif defined(genode):
+  const
+    Header = "genode_cpp/syslocks.h"
+  type
+    SysLock* {.importcpp: "Nim::SysLock", pure, final,
+              header: Header.} = object
+    SysCond* {.importcpp: "Nim::SysCond", pure, final,
+              header: Header.} = object
+
+  proc initSysLock*(L: var SysLock) = discard
+  proc deinitSys*(L: SysLock) = discard
+  proc acquireSys*(L: var SysLock) {.noSideEffect, importcpp.}
+  proc tryAcquireSys*(L: var SysLock): bool {.noSideEffect, importcpp.}
+  proc releaseSys*(L: var SysLock) {.noSideEffect, importcpp.}
+
+  proc initSysCond*(L: var SysCond) = discard
+  proc deinitSysCond*(L: SysCond) = discard
+  proc waitSysCond*(cond: var SysCond, lock: var SysLock) {.
+    noSideEffect, importcpp.}
+  proc signalSysCond*(cond: var SysCond) {.
+    noSideEffect, importcpp.}
+  proc broadcastSysCond*(cond: var SysCond) {.
+    noSideEffect, importcpp.}
+
+else:
+  type
+    SysLockObj {.importc: "pthread_mutex_t", pure, final,
+               header: """#include <sys/types.h>
+                          #include <pthread.h>""", byref.} = object
+      when defined(linux) and defined(amd64):
+        abi: array[40 div sizeof(clong), clong]
+
+    SysLockAttr* {.importc: "pthread_mutexattr_t", pure, final
+               header: """#include <sys/types.h>
+                          #include <pthread.h>""".} = object
+      when defined(linux) and defined(amd64):
+        abi: array[4 div sizeof(cint), cint]  # actually a cint
+
+    SysCondObj {.importc: "pthread_cond_t", pure, final,
+               header: """#include <sys/types.h>
+                          #include <pthread.h>""", byref.} = object
+      when defined(linux) and defined(amd64):
+        abi: array[48 div sizeof(clonglong), clonglong]
+
+    SysCondAttr {.importc: "pthread_condattr_t", pure, final
+               header: """#include <sys/types.h>
+                          #include <pthread.h>""".} = object
+      when defined(linux) and defined(amd64):
+        abi: array[4 div sizeof(cint), cint]  # actually a cint
+
+    SysLockType = distinct cint
+
+  proc initSysLockAux(L: var SysLockObj, attr: ptr SysLockAttr) {.
+    importc: "pthread_mutex_init", header: "<pthread.h>", noSideEffect.}
+  proc deinitSysAux(L: SysLockObj) {.noSideEffect,
+    importc: "pthread_mutex_destroy", header: "<pthread.h>".}
+
+  proc acquireSysAux(L: var SysLockObj) {.noSideEffect,
+    importc: "pthread_mutex_lock", header: "<pthread.h>".}
+  proc tryAcquireSysAux(L: var SysLockObj): cint {.noSideEffect,
+    importc: "pthread_mutex_trylock", header: "<pthread.h>".}
+
+  proc releaseSysAux(L: var SysLockObj) {.noSideEffect,
+    importc: "pthread_mutex_unlock", header: "<pthread.h>".}
+
+  when defined(ios):
+    # iOS will behave badly if sync primitives are moved in memory. In order
+    # to prevent this once and for all, we're doing an extra malloc when
+    # initializing the primitive.
+    type
+      SysLock* = ptr SysLockObj
+      SysCond* = ptr SysCondObj
+
+    when not declared(c_malloc):
+      proc c_malloc(size: csize_t): pointer {.
+        importc: "malloc", header: "<stdlib.h>".}
+      proc c_free(p: pointer) {.
+        importc: "free", header: "<stdlib.h>".}
+
+    proc initSysLock*(L: var SysLock, attr: ptr SysLockAttr = nil) =
+      L = cast[SysLock](c_malloc(csize_t(sizeof(SysLockObj))))
+      initSysLockAux(L[], attr)
+
+    proc deinitSys*(L: SysLock) =
+      deinitSysAux(L[])
+      c_free(L)
+
+    template acquireSys*(L: var SysLock) =
+      acquireSysAux(L[])
+    template tryAcquireSys*(L: var SysLock): bool =
+      tryAcquireSysAux(L[]) == 0'i32
+    template releaseSys*(L: var SysLock) =
+      releaseSysAux(L[])
+  else:
+    type
+      SysLock* = SysLockObj
+      SysCond* = SysCondObj
+
+    template initSysLock*(L: var SysLock, attr: ptr SysLockAttr = nil) =
+      initSysLockAux(L, attr)
+    template deinitSys*(L: SysLock) =
+      deinitSysAux(L)
+    template acquireSys*(L: var SysLock) =
+      acquireSysAux(L)
+    template tryAcquireSys*(L: var SysLock): bool =
+      tryAcquireSysAux(L) == 0'i32
+    template releaseSys*(L: var SysLock) =
+      releaseSysAux(L)
+
+  # rlocks
+  var SysLockType_Reentrant* {.importc: "PTHREAD_MUTEX_RECURSIVE",
+    header: "<pthread.h>".}: SysLockType
+  proc initSysLockAttr*(a: var SysLockAttr) {.
+    importc: "pthread_mutexattr_init", header: "<pthread.h>", noSideEffect.}
+  proc setSysLockType*(a: var SysLockAttr, t: SysLockType) {.
+    importc: "pthread_mutexattr_settype", header: "<pthread.h>", noSideEffect.}
+
+  # locks
+  proc initSysCondAux(cond: var SysCondObj, cond_attr: ptr SysCondAttr = nil) {.
+    importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.}
+  proc deinitSysCondAux(cond: SysCondObj) {.noSideEffect,
+    importc: "pthread_cond_destroy", header: "<pthread.h>".}
+
+  proc waitSysCondAux(cond: var SysCondObj, lock: var SysLockObj): cint {.
+    importc: "pthread_cond_wait", header: "<pthread.h>", noSideEffect.}
+  proc signalSysCondAux(cond: var SysCondObj) {.
+    importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.}
+  proc broadcastSysCondAux(cond: var SysCondObj) {.
+    importc: "pthread_cond_broadcast", header: "<pthread.h>", noSideEffect.}
+
+  when defined(ios):
+    proc initSysCond*(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) =
+      cond = cast[SysCond](c_malloc(csize_t(sizeof(SysCondObj))))
+      initSysCondAux(cond[], cond_attr)
+
+    proc deinitSysCond*(cond: SysCond) =
+      deinitSysCondAux(cond[])
+      c_free(cond)
+
+    template waitSysCond*(cond: var SysCond, lock: var SysLock) =
+      discard waitSysCondAux(cond[], lock[])
+    template signalSysCond*(cond: var SysCond) =
+      signalSysCondAux(cond[])
+    template broadcastSysCond*(cond: var SysCond) =
+      broadcastSysCondAux(cond[])
+  else:
+    template initSysCond*(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) =
+      initSysCondAux(cond, cond_attr)
+    template deinitSysCond*(cond: SysCond) =
+      deinitSysCondAux(cond)
+
+    template waitSysCond*(cond: var SysCond, lock: var SysLock) =
+      discard waitSysCondAux(cond, lock)
+    template signalSysCond*(cond: var SysCond) =
+      signalSysCondAux(cond)
+    template broadcastSysCond*(cond: var SysCond) =
+      broadcastSysCondAux(cond)
+
+{.pop.}
diff --git a/lib/std/private/threadtypes.nim b/lib/std/private/threadtypes.nim
new file mode 100644
index 000000000..a1cdf21dc
--- /dev/null
+++ b/lib/std/private/threadtypes.nim
@@ -0,0 +1,176 @@
+include system/inclrtl
+
+const hasSharedHeap* = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own
+
+when defined(windows):
+  type
+    Handle* = int
+    SysThread* = Handle
+    WinThreadProc* = proc (x: pointer): int32 {.stdcall.}
+
+  proc createThread*(lpThreadAttributes: pointer, dwStackSize: int32,
+                     lpStartAddress: WinThreadProc,
+                     lpParameter: pointer,
+                     dwCreationFlags: int32,
+                     lpThreadId: var int32): SysThread {.
+    stdcall, dynlib: "kernel32", importc: "CreateThread".}
+
+  proc winSuspendThread*(hThread: SysThread): int32 {.
+    stdcall, dynlib: "kernel32", importc: "SuspendThread".}
+
+  proc winResumeThread*(hThread: SysThread): int32 {.
+    stdcall, dynlib: "kernel32", importc: "ResumeThread".}
+
+  proc waitForSingleObject*(hHandle: SysThread, dwMilliseconds: int32): int32 {.
+    stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".}
+
+  proc waitForMultipleObjects*(nCount: int32,
+                              lpHandles: ptr SysThread,
+                              bWaitAll: int32,
+                              dwMilliseconds: int32): int32 {.
+    stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".}
+
+  proc terminateThread*(hThread: SysThread, dwExitCode: int32): int32 {.
+    stdcall, dynlib: "kernel32", importc: "TerminateThread".}
+
+  proc setThreadAffinityMask*(hThread: SysThread, dwThreadAffinityMask: uint) {.
+    importc: "SetThreadAffinityMask", stdcall, header: "<windows.h>".}
+
+elif defined(genode):
+  const
+    GenodeHeader* = "genode_cpp/threads.h"
+  type
+    SysThread* {.importcpp: "Nim::SysThread",
+                 header: GenodeHeader, final, pure.} = object
+    GenodeThreadProc* = proc (x: pointer) {.noconv.}
+
+  proc initThread*(s: var SysThread,
+                  env: GenodeEnv,
+                  stackSize: culonglong,
+                  entry: GenodeThreadProc,
+                  arg: pointer,
+                  affinity: cuint) {.
+    importcpp: "#.initThread(@)".}
+
+
+else:
+  when not (defined(macosx) or defined(haiku)):
+    {.passl: "-pthread".}
+
+  when not defined(haiku):
+    {.passc: "-pthread".}
+
+  const
+    schedh = "#define _GNU_SOURCE\n#include <sched.h>"
+    pthreadh* = "#define _GNU_SOURCE\n#include <pthread.h>"
+
+  when not declared(Time):
+    when defined(linux):
+      type Time = clong
+    else:
+      type Time = int
+
+  when (defined(linux) or defined(nintendoswitch)) and defined(amd64):
+    type
+      SysThread* {.importc: "pthread_t",
+                  header: "<sys/types.h>" .} = distinct culong
+      Pthread_attr* {.importc: "pthread_attr_t",
+                    header: "<sys/types.h>".} = object
+        abi: array[56 div sizeof(clong), clong]
+  elif defined(openbsd) and defined(amd64):
+    type
+      SysThread* {.importc: "pthread_t", header: "<pthread.h>".} = object
+      Pthread_attr* {.importc: "pthread_attr_t",
+                       header: "<pthread.h>".} = object
+  else:
+    type
+      SysThread* {.importc: "pthread_t", header: "<sys/types.h>".} = int
+      Pthread_attr* {.importc: "pthread_attr_t",
+                       header: "<sys/types.h>".} = object
+  type
+    Timespec* {.importc: "struct timespec", header: "<time.h>".} = object
+      tv_sec*: Time
+      tv_nsec*: clong
+
+  proc pthread_attr_init*(a1: var Pthread_attr): cint {.
+    importc, header: pthreadh.}
+  proc pthread_attr_setstack*(a1: ptr Pthread_attr, a2: pointer, a3: int): cint {.
+    importc, header: pthreadh.}
+  proc pthread_attr_setstacksize*(a1: var Pthread_attr, a2: int): cint {.
+    importc, header: pthreadh.}
+  proc pthread_attr_destroy*(a1: var Pthread_attr): cint {.
+    importc, header: pthreadh.}
+
+  proc pthread_create*(a1: var SysThread, a2: var Pthread_attr,
+            a3: proc (x: pointer): pointer {.noconv.},
+            a4: pointer): cint {.importc: "pthread_create",
+            header: pthreadh.}
+  proc pthread_join*(a1: SysThread, a2: ptr pointer): cint {.
+    importc, header: pthreadh.}
+
+  proc pthread_cancel*(a1: SysThread): cint {.
+    importc: "pthread_cancel", header: pthreadh.}
+
+  type CpuSet* {.importc: "cpu_set_t", header: schedh.} = object
+     when defined(linux) and defined(amd64):
+       abi: array[1024 div (8 * sizeof(culong)), culong]
+
+  proc cpusetZero*(s: var CpuSet) {.importc: "CPU_ZERO", header: schedh.}
+  proc cpusetIncl*(cpu: cint; s: var CpuSet) {.
+    importc: "CPU_SET", header: schedh.}
+
+  when defined(android):
+    # libc of android doesn't implement pthread_setaffinity_np,
+    # it exposes pthread_gettid_np though, so we can use that in combination
+    # with sched_setaffinity to set the thread affinity.
+    type Pid* {.importc: "pid_t", header: "<sys/types.h>".} = int32 # From posix_other.nim
+
+    proc setAffinityTID*(tid: Pid; setsize: csize_t; s: var CpuSet) {.
+      importc: "sched_setaffinity", header: schedh.}
+
+    proc pthread_gettid_np*(thread: SysThread): Pid {.
+      importc: "pthread_gettid_np", header: pthreadh.}
+
+    proc setAffinity*(thread: SysThread; setsize: csize_t; s: var CpuSet) =
+      setAffinityTID(pthread_gettid_np(thread), setsize, s)
+  else:
+    proc setAffinity*(thread: SysThread; setsize: csize_t; s: var CpuSet) {.
+      importc: "pthread_setaffinity_np", header: pthreadh.}
+
+
+const
+  emulatedThreadVars* = compileOption("tlsEmulation")
+# we preallocate a fixed size for thread local storage, so that no heap
+# allocations are needed. Currently less than 16K are used on a 64bit machine.
+# We use `float` for proper alignment:
+const nimTlsSize {.intdefine.} = 16000
+type
+  ThreadLocalStorage* = array[0..(nimTlsSize div sizeof(float)), float]
+  PGcThread* = ptr GcThread
+  GcThread* {.pure, inheritable.} = object
+    when emulatedThreadVars:
+      tls*: ThreadLocalStorage
+    else:
+      nil
+    when hasSharedHeap:
+      next*, prev*: PGcThread
+      stackBottom*, stackTop*: pointer
+      stackSize*: int
+    else:
+      nil
+
+const hasAllocStack* = defined(zephyr) # maybe freertos too?
+
+type
+  Thread*[TArg] = object
+    core*: PGcThread
+    sys*: SysThread
+    when TArg is void:
+      dataFn*: proc () {.nimcall, gcsafe.}
+    else:
+      dataFn*: proc (m: TArg) {.nimcall, gcsafe.}
+      data*: TArg
+    when hasAllocStack:
+      rawStack*: pointer
+
+proc `=copy`*[TArg](x: var Thread[TArg], y: Thread[TArg]) {.error.}
diff --git a/lib/std/private/underscored_calls.nim b/lib/std/private/underscored_calls.nim
new file mode 100644
index 000000000..f853572b5
--- /dev/null
+++ b/lib/std/private/underscored_calls.nim
@@ -0,0 +1,56 @@
+
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This is an internal helper module. Do not use.
+
+import std/macros
+
+proc underscoredCalls*(result, calls, arg0: NimNode)
+
+proc underscoredCall(n, arg0: NimNode): NimNode =
+  proc underscorePos(n: NimNode): int =
+    for i in 1 ..< n.len:
+      if n[i].eqIdent("_"): return i
+    return 0
+
+  if n.kind in nnkCallKinds:
+    if n[0].kind in {nnkIdent, nnkSym} and n[0].eqIdent("with"):
+      expectKind n[1], {nnkIdent, nnkSym}
+
+      result = newStmtList()
+      underscoredCalls(result, n[2 .. ^1].newStmtList, newDotExpr(arg0, n[1]))
+    else:
+      result = copyNimNode(n)
+      result.add n[0]
+
+      let u = underscorePos(n)
+      for i in 1..u-1: result.add n[i]
+      result.add arg0
+      for i in u+1..n.len-1: result.add n[i]
+  elif n.kind in {nnkAsgn, nnkExprEqExpr}:
+    var field = n[0]
+    if n[0].kind == nnkDotExpr and n[0][0].eqIdent("_"):
+      # handle _.field = ...
+      field = n[0][1]
+    result = newDotExpr(arg0, field).newAssignment n[1]
+  else:
+    # handle e.g. 'x.dup(sort)'
+    result = newNimNode(nnkCall, n)
+    result.add n
+    result.add arg0
+
+proc underscoredCalls*(result, calls, arg0: NimNode) =
+  expectKind calls, {nnkArgList, nnkStmtList, nnkStmtListExpr}
+
+  for call in calls:
+    if call.kind in {nnkStmtList, nnkStmtListExpr}:
+      underscoredCalls(result, call, arg0)
+    else:
+      result.add underscoredCall(call, arg0)
diff --git a/lib/std/private/win_getsysteminfo.nim b/lib/std/private/win_getsysteminfo.nim
new file mode 100644
index 000000000..b98478231
--- /dev/null
+++ b/lib/std/private/win_getsysteminfo.nim
@@ -0,0 +1,15 @@
+type
+  SystemInfo* = object
+    u1: uint32
+    dwPageSize: uint32
+    lpMinimumApplicationAddress: pointer
+    lpMaximumApplicationAddress: pointer
+    dwActiveProcessorMask: ptr uint32
+    dwNumberOfProcessors*: uint32
+    dwProcessorType: uint32
+    dwAllocationGranularity*: uint32
+    wProcessorLevel: uint16
+    wProcessorRevision: uint16
+
+proc getSystemInfo*(lpSystemInfo: ptr SystemInfo) {.stdcall,
+    dynlib: "kernel32", importc: "GetSystemInfo".}
diff --git a/lib/std/private/win_setenv.nim b/lib/std/private/win_setenv.nim
new file mode 100644
index 000000000..66e199dfe
--- /dev/null
+++ b/lib/std/private/win_setenv.nim
@@ -0,0 +1,106 @@
+#[
+Copyright (c) Facebook, Inc. and its affiliates.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+Adapted `setenv` from https://github.com/facebook/folly/blob/master/folly/portability/Stdlib.cpp
+translated from C to nim.
+]#
+
+#[
+Introduced in https://github.com/facebook/folly/commit/5d8ca09a3f96afefb44e35808f03651a096ab9c7
+
+TODO:
+check errno_t vs cint
+]#
+
+when not defined(windows): discard
+else:
+  when defined(nimPreviewSlimSystem):
+    import std/widestrs
+
+  type wchar_t  {.importc: "wchar_t".} = int16
+
+  proc setEnvironmentVariableW*(lpName, lpValue: WideCString): int32 {.
+    stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableW", sideEffect.}
+    # same as winlean.setEnvironmentVariableA
+
+  proc c_getenv(varname: cstring): cstring {.importc: "getenv", header: "<stdlib.h>".}
+  proc c_wputenv(envstring: ptr wchar_t): cint {.importc: "_wputenv", header: "<stdlib.h>".}
+  proc c_wgetenv(varname: ptr wchar_t): ptr wchar_t {.importc: "_wgetenv", header: "<stdlib.h>".}
+
+  var errno {.importc, header: "<errno.h>".}: cint
+  var genviron {.importc: "_environ".}: ptr ptr char
+    # xxx `ptr UncheckedArray[WideCString]` did not work
+
+  proc wcstombs(wcstr: ptr char, mbstr: ptr wchar_t, count: csize_t): csize_t {.importc, header: "<stdlib.h>".}
+    # xxx cint vs errno_t?
+
+  proc setEnvImpl*(name: string, value: string, overwrite: cint): cint =
+    const EINVAL = cint(22)
+    let wideName: WideCString = newWideCString(name)
+    if overwrite == 0 and c_wgetenv(cast[ptr wchar_t](wideName)) != nil:
+      return 0
+
+    if value != "":
+      let envstring: WideCString = newWideCString(name & "=" & value)
+      let e = c_wputenv(cast[ptr wchar_t](envstring))
+      if e != 0:
+        errno = EINVAL
+        return -1
+      return 0
+    #[
+    We are trying to set the value to an empty string, but `_putenv` deletes
+    entries if the value is an empty string, and just calling
+    SetEnvironmentVariableA doesn't update `_environ`,
+    so we have to do these terrible things.
+    ]#
+    let envstring: WideCString = newWideCString(name & "=  ")
+    if c_wputenv(cast[ptr wchar_t](envstring)) != 0:
+      errno = EINVAL
+      return -1
+    # Here lies the documentation we blatently ignore to make this work.
+    var s = cast[WideCString](c_wgetenv(cast[ptr wchar_t](wideName)))
+    s[0] = Utf16Char('\0')
+    #[
+    This would result in a double null termination, which normally signifies the
+    end of the environment variable list, so we stick a completely empty
+    environment variable into the list instead.
+    ]#
+    s = cast[WideCString](c_wgetenv(cast[ptr wchar_t](wideName)))
+    s[1] = Utf16Char('=')
+    #[
+    If genviron is null, the MBCS environment has not been initialized
+    yet, and we don't need to try to update it. We have to do this otherwise
+    we'd be forcing the initialization and maintenance of the MBCS environment
+    even though it's never actually used in most programs.
+    ]#
+    if genviron != nil:
+
+      # wcstombs returns `high(csize_t)` if any characters cannot be represented
+      # in the current codepage. Skip updating MBCS environment in this case.
+      # For some reason, second `wcstombs` can find non-convertible characters
+      # that the first `wcstombs` cannot.
+      let requiredSizeS = wcstombs(nil, cast[ptr wchar_t](wideName), 0)
+      if requiredSizeS != high(csize_t):
+        let requiredSize = requiredSizeS.int
+        var buf = newSeq[char](requiredSize + 1)
+        let buf2 = buf[0].addr
+        if wcstombs(buf2, cast[ptr wchar_t](wideName), csize_t(requiredSize + 1)) != high(csize_t):
+          var ptrToEnv = c_getenv(cast[cstring](buf2))
+          ptrToEnv[0] = '\0'
+          ptrToEnv = c_getenv(cast[cstring](buf2))
+          ptrToEnv[1] = '='
+
+    # And now, we have to update the outer environment to have a proper empty value.
+    if setEnvironmentVariableW(wideName, value.newWideCString) == 0:
+      errno = EINVAL
+      return -1
+    return 0
diff --git a/lib/std/setutils.nim b/lib/std/setutils.nim
new file mode 100644
index 000000000..8e7bc6a92
--- /dev/null
+++ b/lib/std/setutils.nim
@@ -0,0 +1,77 @@
+#
+#
+#              Nim's Runtime Library
+#        (c) Copyright 2020 Nim Contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module adds functionality for the built-in `set` type.
+##
+## See also
+## ========
+## * `std/packedsets <packedsets.html>`_
+## * `std/sets <sets.html>`_
+
+import std/[typetraits, macros]
+
+#[
+  type SetElement* = char|byte|bool|int16|uint16|enum|uint8|int8
+    ## The allowed types of a built-in set.
+]#
+
+template toSet*(iter: untyped): untyped =
+  ## Returns a built-in set from the elements of the iterable `iter`.
+  runnableExamples:
+    assert "helloWorld".toSet == {'W', 'd', 'e', 'h', 'l', 'o', 'r'}
+    assert toSet([10u16, 20, 30]) == {10u16, 20, 30}
+    assert [30u8, 100, 10].toSet == {10u8, 30, 100}
+    assert toSet(@[1321i16, 321, 90]) == {90i16, 321, 1321}
+    assert toSet([false]) == {false}
+    assert toSet(0u8..10) == {0u8..10}
+
+  var result: set[elementType(iter)]
+  for x in iter:
+    incl(result, x)
+  result
+
+macro enumElementsAsSet(enm: typed): untyped = result = newNimNode(nnkCurly).add(enm.getType[1][1..^1])
+
+# func fullSet*(T: typedesc): set[T] {.inline.} = # xxx would give: Error: ordinal type expected
+func fullSet*[T](U: typedesc[T]): set[T] {.inline.} =
+  ## Returns a set containing all elements in `U`.
+  runnableExamples:
+    assert bool.fullSet == {true, false}
+    type A = range[1..3]
+    assert A.fullSet == {1.A, 2, 3}
+    assert int8.fullSet.len == 256
+  when T is Ordinal:
+    {T.low..T.high}
+  else: # Hole filled enum
+    enumElementsAsSet(T)
+
+func complement*[T](s: set[T]): set[T] {.inline.} =
+  ## Returns the set complement of `a`.
+  runnableExamples:
+    type Colors = enum
+      red, green = 3, blue
+    assert complement({red, blue}) == {green}
+    assert complement({red, green, blue}).card == 0
+    assert complement({range[0..10](0), 1, 2, 3}) == {range[0..10](4), 5, 6, 7, 8, 9, 10}
+    assert complement({'0'..'9'}) == {0.char..255.char} - {'0'..'9'}
+  fullSet(T) - s
+
+func `[]=`*[T](t: var set[T], key: T, val: bool) {.inline.} =
+  ## Syntax sugar for `if val: t.incl key else: t.excl key`
+  runnableExamples:
+    type A = enum
+      a0, a1, a2, a3
+    var s = {a0, a3}
+    s[a0] = false
+    s[a1] = false
+    assert s == {a3}
+    s[a2] = true
+    s[a3] = true
+    assert s == {a2, a3}
+  if val: t.incl key else: t.excl key
diff --git a/lib/std/sha1.nim b/lib/std/sha1.nim
index c0b1bffcf..213af4229 100644
--- a/lib/std/sha1.nim
+++ b/lib/std/sha1.nim
@@ -1,197 +1,287 @@
 #
 #
-#           The Nim Compiler
+#              Nim's Runtime Library
 #        (c) Copyright 2015 Nim Contributors
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 #
+## [SHA-1 (Secure Hash Algorithm 1)](https://en.wikipedia.org/wiki/SHA-1)
+## is a cryptographic hash function which takes an input and produces
+## a 160-bit (20-byte) hash value known as a message digest.
+##
+## See also
+## ========
+## * `base64 module<base64.html>`_ for a Base64 encoder and decoder
+## * `hashes module<hashes.html>`_ for efficient computations of hash values for diverse Nim types
+## * `md5 module<md5.html>`_ for the MD5 checksum algorithm
 
-## Note: Import ``std/sha1`` to use this module
+runnableExamples:
+  let accessName = secureHash("John Doe")
+  assert $accessName == "AE6E4D1209F17B460503904FAD297B31E9CF6362"
 
-import strutils
+runnableExamples("-r:off"):
+  let
+    a = secureHashFile("myFile.nim")
+    b = parseSecureHash("10DFAEBF6BFDBC7939957068E2EFACEC4972933C")
+  assert a == b, "files don't match"
+
+
+{.deprecated: "use command `nimble install checksums` and import `checksums/sha1` instead".}
+
+import std/strutils
+from std/endians import bigEndian32, bigEndian64
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
 
 const Sha1DigestSize = 20
 
 type
-  Sha1Digest = array[0 .. Sha1DigestSize-1, uint8]
+  Sha1Digest* = array[0 .. Sha1DigestSize - 1, uint8]
   SecureHash* = distinct Sha1Digest
 
-# Copyright (c) 2011, Micael Hildenborg
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-# * Redistributions of source code must retain the above copyright
-#   notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright
-#   notice, this list of conditions and the following disclaimer in the
-#   documentation and/or other materials provided with the distribution.
-# * Neither the name of Micael Hildenborg nor the
-#   names of its contributors may be used to endorse or promote products
-#   derived from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY
-# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-# Ported to Nim by Erik O'Leary
-
 type
-  Sha1State* = array[0 .. 5-1, uint32]
-  Sha1Buffer = array[0 .. 80-1, uint32]
-
-template clearBuffer(w: Sha1Buffer, len = 16) =
-  zeroMem(addr(w), len * sizeof(uint32))
-
-proc init*(result: var Sha1State) =
-  result[0] = 0x67452301'u32
-  result[1] = 0xefcdab89'u32
-  result[2] = 0x98badcfe'u32
-  result[3] = 0x10325476'u32
-  result[4] = 0xc3d2e1f0'u32
-
-proc innerHash(state: var Sha1State, w: var Sha1Buffer) =
-  var
-    a = state[0]
-    b = state[1]
-    c = state[2]
-    d = state[3]
-    e = state[4]
-
-  var round = 0
-
-  template rot(value, bits: uint32): uint32 =
-    (value shl bits) or (value shr (32u32 - bits))
-
-  template sha1(fun, val: uint32) =
-    let t = rot(a, 5) + fun + e + val + w[round]
-    e = d
-    d = c
-    c = rot(b, 30)
-    b = a
-    a = t
-
-  template process(body: untyped) =
-    w[round] = rot(w[round - 3] xor w[round - 8] xor w[round - 14] xor w[round - 16], 1)
-    body
-    inc(round)
-
-  template wrap(dest, value: untyped) =
-    let v = dest + value
-    dest = v
-
-  while round < 16:
-    sha1((b and c) or (not b and d), 0x5a827999'u32)
-    inc(round)
-
-  while round < 20:
-    process:
-      sha1((b and c) or (not b and d), 0x5a827999'u32)
-
-  while round < 40:
-    process:
-      sha1(b xor c xor d, 0x6ed9eba1'u32)
-
-  while round < 60:
-    process:
-      sha1((b and c) or (b and d) or (c and d), 0x8f1bbcdc'u32)
-
-  while round < 80:
-    process:
-      sha1(b xor c xor d, 0xca62c1d6'u32)
-
-  wrap state[0], a
-  wrap state[1], b
-  wrap state[2], c
-  wrap state[3], d
-  wrap state[4], e
-
-proc sha1(src: cstring; len: int): Sha1Digest =
-  #Initialize state
-  var state: Sha1State
-  init(state)
-
-  #Create w buffer
-  var w: Sha1Buffer
-
-  #Loop through all complete 64byte blocks.
-  let byteLen = len
-  let endOfFullBlocks = byteLen - 64
-  var endCurrentBlock = 0
-  var currentBlock = 0
-
-  while currentBlock <= endOfFullBlocks:
-    endCurrentBlock = currentBlock + 64
-
-    var i = 0
-    while currentBlock < endCurrentBlock:
-      w[i] = uint32(src[currentBlock+3]) or
-             uint32(src[currentBlock+2]) shl 8'u32 or
-             uint32(src[currentBlock+1]) shl 16'u32 or
-             uint32(src[currentBlock])   shl 24'u32
-      currentBlock += 4
-      inc(i)
-
-    innerHash(state, w)
-
-  #Handle last and not full 64 byte block if existing
-  endCurrentBlock = byteLen - currentBlock
-  clearBuffer(w)
-  var lastBlockBytes = 0
-
-  while lastBlockBytes < endCurrentBlock:
-
-    var value = uint32(src[lastBlockBytes + currentBlock]) shl
-                ((3'u32 - uint32(lastBlockBytes and 3)) shl 3)
-
-    w[lastBlockBytes shr 2] = w[lastBlockBytes shr 2] or value
-    inc(lastBlockBytes)
-
-  w[lastBlockBytes shr 2] = w[lastBlockBytes shr 2] or (
-    0x80'u32 shl ((3'u32 - uint32(lastBlockBytes and 3)) shl 3)
-  )
-
-  if endCurrentBlock >= 56:
-    innerHash(state, w)
-    clearBuffer(w)
-
-  w[15] = uint32(byteLen) shl 3
-  innerHash(state, w)
-
-  # Store hash in result pointer, and make sure we get in in the correct order
-  # on both endian models.
-  for i in 0 .. Sha1DigestSize-1:
-    result[i] = uint8((int(state[i shr 2]) shr ((3-(i and 3)) * 8)) and 255)
-
-proc sha1(src: string): Sha1Digest =
-  ## Calculate SHA1 from input string
-  sha1(src, src.len)
-
-proc secureHash*(str: string): SecureHash = SecureHash(sha1(str))
-proc secureHashFile*(filename: string): SecureHash = secureHash(readFile(filename))
+  Sha1State* = object
+    count: int
+    state: array[5, uint32]
+    buf:   array[64, byte]
+
+# This implementation of the SHA-1 algorithm was ported from the Chromium OS one
+# with minor modifications that should not affect its functionality.
+
+proc newSha1State*(): Sha1State =
+  ## Creates a `Sha1State`.
+  ##
+  ## If you use the `secureHash proc <#secureHash,openArray[char]>`_,
+  ## there's no need to call this function explicitly.
+  result.count = 0
+  result.state[0] = 0x67452301'u32
+  result.state[1] = 0xEFCDAB89'u32
+  result.state[2] = 0x98BADCFE'u32
+  result.state[3] = 0x10325476'u32
+  result.state[4] = 0xC3D2E1F0'u32
+
+template ror27(val: uint32): uint32 = (val shr 27) or (val shl  5)
+template ror2 (val: uint32): uint32 = (val shr  2) or (val shl 30)
+template ror31(val: uint32): uint32 = (val shr 31) or (val shl  1)
+
+proc transform(ctx: var Sha1State) =
+  var w: array[80, uint32]
+  var a, b, c, d, e: uint32
+  var t = 0
+
+  a = ctx.state[0]
+  b = ctx.state[1]
+  c = ctx.state[2]
+  d = ctx.state[3]
+  e = ctx.state[4]
+
+  template shaF1(a, b, c, d, e, t: untyped) =
+    bigEndian32(addr w[t], addr ctx.buf[t * 4])
+    e += ror27(a) + w[t] + (d xor (b and (c xor d))) + 0x5A827999'u32
+    b = ror2(b)
+
+  while t < 15:
+    shaF1(a, b, c, d, e, t + 0)
+    shaF1(e, a, b, c, d, t + 1)
+    shaF1(d, e, a, b, c, t + 2)
+    shaF1(c, d, e, a, b, t + 3)
+    shaF1(b, c, d, e, a, t + 4)
+    t += 5
+  shaF1(a, b, c, d, e, t + 0) # 16th one, t == 15
+
+  template shaF11(a, b, c, d, e, t: untyped) =
+    w[t] = ror31(w[t-3] xor w[t-8] xor w[t-14] xor w[t-16])
+    e += ror27(a) + w[t] + (d xor (b and (c xor d))) + 0x5A827999'u32
+    b = ror2(b)
+
+  shaF11(e, a, b, c, d, t + 1)
+  shaF11(d, e, a, b, c, t + 2)
+  shaF11(c, d, e, a, b, t + 3)
+  shaF11(b, c, d, e, a, t + 4)
+
+  template shaF2(a, b, c, d, e, t: untyped) =
+    w[t] = ror31(w[t-3] xor w[t-8] xor w[t-14] xor w[t-16])
+    e += ror27(a) + w[t] + (b xor c xor d) + 0x6ED9EBA1'u32
+    b = ror2(b)
+
+  t = 20
+  while t < 40:
+    shaF2(a, b, c, d, e, t + 0)
+    shaF2(e, a, b, c, d, t + 1)
+    shaF2(d, e, a, b, c, t + 2)
+    shaF2(c, d, e, a, b, t + 3)
+    shaF2(b, c, d, e, a, t + 4)
+    t += 5
+
+  template shaF3(a, b, c, d, e, t: untyped) =
+    w[t] = ror31(w[t-3] xor w[t-8] xor w[t-14] xor w[t-16])
+    e += ror27(a) + w[t] + ((b and c) or (d and (b or c))) + 0x8F1BBCDC'u32
+    b = ror2(b)
+
+  while t < 60:
+    shaF3(a, b, c, d, e, t + 0)
+    shaF3(e, a, b, c, d, t + 1)
+    shaF3(d, e, a, b, c, t + 2)
+    shaF3(c, d, e, a, b, t + 3)
+    shaF3(b, c, d, e, a, t + 4)
+    t += 5
+
+  template shaF4(a, b, c, d, e, t: untyped) =
+    w[t] = ror31(w[t-3] xor w[t-8] xor w[t-14] xor w[t-16])
+    e += ror27(a) + w[t] + (b xor c xor d) + 0xCA62C1D6'u32
+    b = ror2(b)
+
+  while t < 80:
+    shaF4(a, b, c, d, e, t + 0)
+    shaF4(e, a, b, c, d, t + 1)
+    shaF4(d, e, a, b, c, t + 2)
+    shaF4(c, d, e, a, b, t + 3)
+    shaF4(b, c, d, e, a, t + 4)
+    t += 5
+
+  ctx.state[0] += a
+  ctx.state[1] += b
+  ctx.state[2] += c
+  ctx.state[3] += d
+  ctx.state[4] += e
+
+proc update*(ctx: var Sha1State, data: openArray[char]) =
+  ## Updates the `Sha1State` with `data`.
+  ##
+  ## If you use the `secureHash proc <#secureHash,openArray[char]>`_,
+  ## there's no need to call this function explicitly.
+  var i = ctx.count mod 64
+  var j = 0
+  var len = data.len
+  # Gather 64-bytes worth of data in order to perform a round with the leftover
+  # data we had stored (but not processed yet)
+  if len > 64 - i:
+    copyMem(addr ctx.buf[i], unsafeAddr data[j], 64 - i)
+    len -= 64 - i
+    j += 64 - i
+    transform(ctx)
+    # Update the index since it's used in the while loop below _and_ we want to
+    # keep its value if this code path isn't executed
+    i = 0
+  # Process the bulk of the payload
+  while len >= 64:
+    copyMem(addr ctx.buf[0], unsafeAddr data[j], 64)
+    len -= 64
+    j += 64
+    transform(ctx)
+  # Process the tail of the payload (len is < 64)
+  while len > 0:
+    dec len
+    ctx.buf[i] = byte(data[j])
+    inc i
+    inc j
+    if i == 64:
+      transform(ctx)
+      i = 0
+  ctx.count += data.len
+
+proc finalize*(ctx: var Sha1State): Sha1Digest =
+  ## Finalizes the `Sha1State` and returns a `Sha1Digest`.
+  ##
+  ## If you use the `secureHash proc <#secureHash,openArray[char]>`_,
+  ## there's no need to call this function explicitly.
+  var cnt = uint64(ctx.count * 8)
+  # a 1 bit
+  update(ctx, "\x80")
+  # Add padding until we reach a complexive size of 64 - 8 bytes
+  while (ctx.count mod 64) != (64 - 8):
+    update(ctx, "\x00")
+  # The message length as a 64bit BE number completes the block
+  var tmp: array[8, char]
+  bigEndian64(addr tmp[0], addr cnt)
+  update(ctx, tmp)
+  # Turn the result into a single 160-bit number
+  for i in 0 ..< 5:
+    bigEndian32(addr ctx.state[i], addr ctx.state[i])
+  copyMem(addr result[0], addr ctx.state[0], Sha1DigestSize)
+
+# Public API
+
+proc secureHash*(str: openArray[char]): SecureHash =
+  ## Generates a `SecureHash` from `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,openArray[char]>`_ for generating a `SecureHash` from a string
+  ## * `parseSecureHash proc <#parseSecureHash,string>`_ for converting a string `hash` to `SecureHash`
+  const BufferLength = 8192
+
+  let f = open(filename)
+  var state = newSha1State()
+  var buffer = newString(BufferLength)
+  while true:
+    let length = readChars(f, buffer)
+    if length == 0:
+      break
+    buffer.setLen(length)
+    state.update(buffer)
+    if length != BufferLength:
+      break
+  close(f)
+
+  SecureHash(state.finalize())
+
 proc `$`*(self: SecureHash): string =
+  ## Returns the string representation of a `SecureHash`.
+  ##
+  ## **See also:**
+  ## * `secureHash proc <#secureHash,openArray[char]>`_ 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 a `SecureHash`.
+  ##
+  ## **See also:**
+  ## * `secureHash proc <#secureHash,openArray[char]>`_ 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)
 
-
-when isMainModule:
-  let hash1 = secureHash("a93tgj0p34jagp9[agjp98ajrhp9aej]")
-  doAssert hash1 == hash1
-  doAssert parseSecureHash($hash1) == hash1
+proc isValidSha1Hash*(s: string): bool =
+  ## Checks if a string is a valid sha1 hash sum.
+  s.len == 40 and allCharsInSet(s, HexDigits)
\ No newline at end of file
diff --git a/lib/std/socketstreams.nim b/lib/std/socketstreams.nim
new file mode 100644
index 000000000..45e906795
--- /dev/null
+++ b/lib/std/socketstreams.nim
@@ -0,0 +1,182 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2021 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module provides an implementation of the streams interface for sockets.
+## It contains two separate implementations, a
+## `ReadSocketStream <#ReadSocketStream>`_ and a
+## `WriteSocketStream <#WriteSocketStream>`_.
+##
+## The `ReadSocketStream` only supports reading, peeking, and seeking.
+## It reads into a buffer, so even by
+## seeking backwards it will only read the same position a single time from the
+## underlying socket. To clear the buffer and free the data read into it you
+## can call `resetStream`, this will also reset the position back to 0 but
+## won't do anything to the underlying socket.
+##
+## The `WriteSocketStream` allows both reading and writing, but it performs the
+## reads on the internal buffer. So by writing to the buffer you can then read
+## back what was written but without receiving anything from the socket. You
+## can also set the position and overwrite parts of the buffer, and to send
+## anything over the socket you need to call `flush` at which point you can't
+## write anything to the buffer before the point of the flush (but it can still
+## be read). Again to empty the underlying buffer you need to call
+## `resetStream`.
+##
+## Examples
+## ========
+##
+##   ```Nim
+##   import std/socketstreams
+##
+##   var
+##     socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
+##     stream = newReadSocketStream(socket)
+##   socket.sendTo("127.0.0.1", Port(12345), "SOME REQUEST")
+##   echo stream.readLine() # Will call `recv`
+##   stream.setPosition(0)
+##   echo stream.readLine() # Will return the read line from the buffer
+##   stream.resetStream() # Buffer is now empty, position is 0
+##   echo stream.readLine() # Will call `recv` again
+##   stream.close() # Closes the socket
+##   ```
+##
+##   ```Nim
+##   import std/socketstreams
+##
+##   var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
+##   socket.connect("127.0.0.1", Port(12345))
+##   var sendStream = newWriteSocketStream(socket)
+##   sendStream.write "NOM"
+##   sendStream.setPosition(1)
+##   echo sendStream.peekStr(2) # OM
+##   sendStream.write "I"
+##   sendStream.setPosition(0)
+##   echo sendStream.readStr(3) # NIM
+##   echo sendStream.getPosition() # 3
+##   sendStream.flush() # This actually performs the writing to the socket
+##   sendStream.setPosition(1)
+##   sendStream.write "I" # Throws an error as we can't write into an already sent buffer
+##   ```
+
+import std/[net, streams]
+
+type
+  ReadSocketStream* = ref ReadSocketStreamObj
+  ReadSocketStreamObj* = object of StreamObj
+    data: Socket
+    pos: int
+    buf: seq[byte]
+  WriteSocketStream* = ref WriteSocketStreamObj
+  WriteSocketStreamObj* = object of ReadSocketStreamObj
+    lastFlush: int
+
+proc rsAtEnd(s: Stream): bool =
+  return false
+
+proc rsSetPosition(s: Stream, pos: int) =
+  var s = ReadSocketStream(s)
+  s.pos = pos
+
+proc rsGetPosition(s: Stream): int =
+  var s = ReadSocketStream(s)
+  return s.pos
+
+proc rsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
+  let s = ReadSocketStream(s)
+  if bufLen > 0:
+    let oldLen = s.buf.len
+    s.buf.setLen(max(s.pos + bufLen, s.buf.len))
+    if s.pos + bufLen > oldLen:
+      result = s.data.recv(s.buf[oldLen].addr, s.buf.len - oldLen)
+      if result > 0:
+        result += oldLen - s.pos
+    else:
+      result = bufLen
+    copyMem(buffer, s.buf[s.pos].addr, result)
+
+proc rsReadData(s: Stream, buffer: pointer, bufLen: int): int =
+  result = s.rsPeekData(buffer, bufLen)
+  var s = ReadSocketStream(s)
+  s.pos += bufLen
+
+proc rsReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int =
+  var s = ReadSocketStream(s)
+  result = slice.b + 1 - slice.a
+  if result > 0:
+    result = s.rsReadData(buffer[slice.a].addr, result)
+    inc(s.pos, result)
+  else:
+    result = 0
+
+proc wsWriteData(s: Stream, buffer: pointer, bufLen: int) =
+  var s = WriteSocketStream(s)
+  if s.pos < s.lastFlush:
+    raise newException(IOError, "Unable to write into buffer that has already been sent")
+  if s.buf.len < s.pos + bufLen:
+    s.buf.setLen(s.pos + bufLen)
+  copyMem(s.buf[s.pos].addr, buffer, bufLen)
+  s.pos += bufLen
+
+proc wsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
+  var s = WriteSocketStream(s)
+  result = bufLen
+  if result > 0:
+    if s.pos > s.buf.len or s.pos == s.buf.len or s.pos + bufLen > s.buf.len:
+      raise newException(IOError, "Unable to read past end of write buffer")
+    else:
+      copyMem(buffer, s.buf[s.pos].addr, bufLen)
+
+proc wsReadData(s: Stream, buffer: pointer, bufLen: int): int =
+  result = s.wsPeekData(buffer, bufLen)
+  var s = ReadSocketStream(s)
+  s.pos += bufLen
+
+proc wsAtEnd(s: Stream): bool =
+  var s = WriteSocketStream(s)
+  return s.pos == s.buf.len
+
+proc wsFlush(s: Stream) =
+  var s = WriteSocketStream(s)
+  discard s.data.send(s.buf[s.lastFlush].addr, s.buf.len - s.lastFlush)
+  s.lastFlush = s.buf.len
+
+proc rsClose(s: Stream) =
+  {.cast(raises: [IOError, OSError]), cast(tags: []).}: # todo fixme maybe do something?
+    var s = ReadSocketStream(s)
+    s.data.close()
+
+proc newReadSocketStream*(s: Socket): owned ReadSocketStream =
+  result = ReadSocketStream(data: s, pos: 0,
+    closeImpl: rsClose,
+    atEndImpl: rsAtEnd,
+    setPositionImpl: rsSetPosition,
+    getPositionImpl: rsGetPosition,
+    readDataImpl: rsReadData,
+    peekDataImpl: rsPeekData,
+    readDataStrImpl: rsReadDataStr)
+
+proc resetStream*(s: ReadSocketStream) =
+  s.buf = @[]
+  s.pos = 0
+
+proc newWriteSocketStream*(s: Socket): owned WriteSocketStream =
+  result = WriteSocketStream(data: s, pos: 0,
+    closeImpl: rsClose,
+    atEndImpl: wsAtEnd,
+    setPositionImpl: rsSetPosition,
+    getPositionImpl: rsGetPosition,
+    writeDataImpl: wsWriteData,
+    readDataImpl: wsReadData,
+    peekDataImpl: wsPeekData,
+    flushImpl: wsFlush)
+
+proc resetStream*(s: WriteSocketStream) =
+  s.buf = @[]
+  s.pos = 0
+  s.lastFlush = 0
diff --git a/lib/std/stackframes.nim b/lib/std/stackframes.nim
new file mode 100644
index 000000000..28be7ce11
--- /dev/null
+++ b/lib/std/stackframes.nim
@@ -0,0 +1,30 @@
+const NimStackTrace = compileOption("stacktrace")
+const NimStackTraceMsgs = compileOption("stacktraceMsgs")
+
+template procName*(): string =
+  ## returns current C/C++ function name
+  when defined(c) or defined(cpp):
+    var name {.inject, noinit.}: cstring
+    {.emit: "`name` = __func__;".}
+    $name
+
+template getPFrame*(): PFrame =
+  ## avoids a function call (unlike `getFrame()`)
+  block:
+    when NimStackTrace:
+      var framePtr {.inject, noinit.}: PFrame
+      {.emit: "`framePtr` = &FR_;".}
+      framePtr
+
+template setFrameMsg*(msg: string, prefix = " ") =
+  ## attach a msg to current `PFrame`. This can be called multiple times
+  ## in a given PFrame. Noop unless passing --stacktraceMsgs and --stacktrace
+  when NimStackTrace and NimStackTraceMsgs:
+    block:
+      var fr {.inject, noinit.}: PFrame
+      {.emit: "`fr` = &FR_;".}
+      # consider setting a custom upper limit on size (analog to stack overflow)
+      frameMsgBuf.setLen fr.frameMsgLen
+      frameMsgBuf.add prefix
+      frameMsgBuf.add msg
+      fr.frameMsgLen += prefix.len + msg.len
diff --git a/lib/std/staticos.nim b/lib/std/staticos.nim
new file mode 100644
index 000000000..2617c6913
--- /dev/null
+++ b/lib/std/staticos.nim
@@ -0,0 +1,13 @@
+## This module implements path handling like os module but works at only compile-time.
+## This module works even when cross compiling to OS that is not supported by os module.
+
+proc staticFileExists*(filename: string): bool {.compileTime.} =
+  ## Returns true if `filename` exists and is a regular file or symlink.
+  ##
+  ## Directories, device files, named pipes and sockets return false.
+  discard
+
+proc staticDirExists*(dir: string): bool {.compileTime.} =
+  ## Returns true if the directory `dir` exists. If `dir` is a file, false
+  ## is returned. Follows symlinks.
+  discard
diff --git a/lib/std/strbasics.nim b/lib/std/strbasics.nim
new file mode 100644
index 000000000..b2c36a4be
--- /dev/null
+++ b/lib/std/strbasics.nim
@@ -0,0 +1,119 @@
+#
+#
+#              Nim's Runtime Library
+#        (c) Copyright 2021 Nim Contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module provides some high performance string operations.
+##
+## Experimental API, subject to change.
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
+const whitespaces = {' ', '\t', '\v', '\r', '\l', '\f'}
+
+proc add*(x: var string, y: openArray[char]) =
+  ## Concatenates `x` and `y` in place. `y` must not overlap with `x` to
+  ## allow future `memcpy` optimizations.
+  # Use `{.noalias.}` ?
+  let n = x.len
+  x.setLen n + y.len
+    # pending #19727
+    # setLen unnecessarily zeros memory
+  var i = 0
+  while i < y.len:
+    x[n + i] = y[i]
+    i.inc
+  # xxx use `nimCopyMem(x[n].addr, y[0].addr, y.len)` after some refactoring
+
+func stripSlice(s: openArray[char], leading = true, trailing = true, chars: set[char] = whitespaces): Slice[int] =
+  ## Returns the slice range of `s` which is stripped `chars`.
+  runnableExamples:
+    assert stripSlice(" abc  ") == 1 .. 3
+  var
+    first = 0
+    last = high(s)
+  if leading:
+    while first <= last and s[first] in chars: inc(first)
+  if trailing:
+    while last >= first and s[last] in chars: dec(last)
+  result = first .. last
+
+func setSlice*(s: var string, slice: Slice[int]) =
+  ## Inplace version of `substr`.
+  runnableExamples:
+    import std/sugar
+
+    var a = "Hello, Nim!"
+    doAssert a.dup(setSlice(7 .. 9)) == "Nim"
+    doAssert a.dup(setSlice(0 .. 0)) == "H"
+    doAssert a.dup(setSlice(0 .. 1)) == "He"
+    doAssert a.dup(setSlice(0 .. 10)) == a
+    doAssert a.dup(setSlice(1 .. 0)).len == 0
+    doAssert a.dup(setSlice(20 .. -1)).len == 0
+
+
+    doAssertRaises(AssertionDefect):
+      discard a.dup(setSlice(-1 .. 1))
+
+    doAssertRaises(AssertionDefect):
+      discard a.dup(setSlice(1 .. 11))
+
+
+  let first = slice.a
+  let last = slice.b
+
+  assert first >= 0
+  assert last <= s.high
+
+  if first > last:
+    s.setLen(0)
+    return
+  template impl =
+    for index in first .. last:
+      s[index - first] = s[index]
+  if first > 0:
+    when nimvm: impl()
+    else:
+      # not JS and not Nimscript
+      when not declared(moveMem):
+        impl()
+      else:
+        when defined(nimSeqsV2):
+          prepareMutation(s)
+        moveMem(addr s[0], addr s[first], last - first + 1)
+  s.setLen(last - first + 1)
+
+func strip*(a: var string, leading = true, trailing = true, chars: set[char] = whitespaces) {.inline.} =
+  ## Inplace version of `strip`. Strips leading or
+  ## trailing `chars` (default: whitespace characters).
+  ##
+  ## 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 unchanged.
+  runnableExamples:
+    var a = "  vhellov   "
+    strip(a)
+    assert a == "vhellov"
+
+    a = "  vhellov   "
+    a.strip(leading = false)
+    assert a == "  vhellov"
+
+    a = "  vhellov   "
+    a.strip(trailing = false)
+    assert a == "vhellov   "
+
+    var c = "blaXbla"
+    c.strip(chars = {'b', 'a'})
+    assert c == "laXbl"
+    c = "blaXbla"
+    c.strip(chars = {'b', 'a', 'l'})
+    assert c == "X"
+
+  setSlice(a, stripSlice(a, leading, trailing, chars))
diff --git a/lib/std/symlinks.nim b/lib/std/symlinks.nim
new file mode 100644
index 000000000..dbe908612
--- /dev/null
+++ b/lib/std/symlinks.nim
@@ -0,0 +1,33 @@
+## This module implements symlink (symbolic link) handling.
+
+## .. importdoc:: os.nim
+
+from std/paths import Path, ReadDirEffect
+
+from std/private/ossymlinks import symlinkExists, createSymlink, expandSymlink
+
+proc symlinkExists*(link: Path): bool {.inline, tags: [ReadDirEffect], sideEffect.} =
+  ## Returns true if the symlink `link` exists. Will return true
+  ## regardless of whether the link points to a directory or file.
+  result = symlinkExists(link.string)
+
+proc createSymlink*(src, dest: Path) {.inline.} =
+  ## Create a symbolic link at `dest` which points to the item specified
+  ## by `src`. On most operating systems, will fail if a link already exists.
+  ##
+  ## .. warning:: Some OS's (such as Microsoft Windows) restrict the creation
+  ##   of symlinks to root users (administrators) or users with developer mode enabled.
+  ##
+  ## See also:
+  ## * `createHardlink proc`_
+  ## * `expandSymlink proc`_
+  createSymlink(src.string, dest.string)
+
+proc expandSymlink*(symlinkPath: Path): Path {.inline.} =
+  ## Returns a string representing the path to which the symbolic link points.
+  ##
+  ## On Windows this is a noop, `symlinkPath` is simply returned.
+  ##
+  ## See also:
+  ## * `createSymlink proc`_
+  result = Path(expandSymlink(symlinkPath.string))
diff --git a/lib/std/syncio.nim b/lib/std/syncio.nim
new file mode 100644
index 000000000..c34a025af
--- /dev/null
+++ b/lib/std/syncio.nim
@@ -0,0 +1,942 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2022 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements various synchronized I/O operations.
+
+include system/inclrtl
+import std/private/since
+import std/formatfloat
+when defined(windows):
+  import std/widestrs
+
+# ----------------- IO Part ------------------------------------------------
+type
+  CFile {.importc: "FILE", header: "<stdio.h>",
+          incompleteStruct.} = object
+  File* = ptr CFile ## The type representing a file handle.
+
+  FileMode* = enum       ## The file mode when opening a file.
+    fmRead,              ## Open the file for read access only.
+                         ## If the file does not exist, it will not
+                         ## be created.
+    fmWrite,             ## Open the file for write access only.
+                         ## If the file does not exist, it will be
+                         ## created. Existing files will be cleared!
+    fmReadWrite,         ## Open the file for read and write access.
+                         ## If the file does not exist, it will be
+                         ## created. Existing files will be cleared!
+    fmReadWriteExisting, ## Open the file for read and write access.
+                         ## If the file does not exist, it will not be
+                         ## created. The existing file will not be cleared.
+    fmAppend             ## Open the file for writing only; append data
+                         ## at the end. If the file does not exist, it
+                         ## will be created.
+
+  FileHandle* = cint ## The type that represents an OS file handle; this is
+                      ## useful for low-level file access.
+
+  FileSeekPos* = enum ## Position relative to which seek should happen.
+                      # The values are ordered so that they match with stdio
+                      # SEEK_SET, SEEK_CUR and SEEK_END respectively.
+    fspSet            ## Seek to absolute value
+    fspCur            ## Seek relative to current position
+    fspEnd            ## Seek relative to end
+
+# text file handling:
+when not defined(nimscript) and not defined(js):
+  # duplicated between io and ansi_c
+  const stdioUsesMacros = (defined(osx) or defined(freebsd) or defined(
+      dragonfly)) and not defined(emscripten)
+  const stderrName = when stdioUsesMacros: "__stderrp" else: "stderr"
+  const stdoutName = when stdioUsesMacros: "__stdoutp" else: "stdout"
+  const stdinName = when stdioUsesMacros: "__stdinp" else: "stdin"
+
+  var
+    stdin* {.importc: stdinName, header: "<stdio.h>".}: File
+      ## The standard input stream.
+    stdout* {.importc: stdoutName, header: "<stdio.h>".}: File
+      ## The standard output stream.
+    stderr* {.importc: stderrName, header: "<stdio.h>".}: File
+      ## The standard error stream.
+
+when defined(useStdoutAsStdmsg):
+  template stdmsg*: File = stdout
+else:
+  template stdmsg*: File = stderr
+    ## Template which expands to either stdout or stderr depending on
+    ## `useStdoutAsStdmsg` compile-time switch.
+
+when defined(windows):
+  proc c_fileno(f: File): cint {.
+    importc: "_fileno", header: "<stdio.h>".}
+else:
+  proc c_fileno(f: File): cint {.
+    importc: "fileno", header: "<fcntl.h>".}
+
+when defined(windows):
+  proc c_fdopen(filehandle: cint, mode: cstring): File {.
+    importc: "_fdopen", header: "<stdio.h>".}
+else:
+  proc c_fdopen(filehandle: cint, mode: cstring): File {.
+    importc: "fdopen", header: "<stdio.h>".}
+proc c_fputs(c: cstring, f: File): cint {.
+  importc: "fputs", header: "<stdio.h>", tags: [WriteIOEffect].}
+proc c_fgets(c: cstring, n: cint, f: File): cstring {.
+  importc: "fgets", header: "<stdio.h>", tags: [ReadIOEffect].}
+proc c_fgetc(stream: File): cint {.
+  importc: "fgetc", header: "<stdio.h>", tags: [].}
+proc c_ungetc(c: cint, f: File): cint {.
+  importc: "ungetc", header: "<stdio.h>", tags: [].}
+proc c_putc(c: cint, stream: File): cint {.
+  importc: "putc", header: "<stdio.h>", tags: [WriteIOEffect].}
+proc c_fflush(f: File): cint {.
+  importc: "fflush", header: "<stdio.h>".}
+proc c_fclose(f: File): cint {.
+  importc: "fclose", header: "<stdio.h>".}
+proc c_clearerr(f: File) {.
+  importc: "clearerr", header: "<stdio.h>".}
+proc c_feof(f: File): cint {.
+  importc: "feof", header: "<stdio.h>".}
+
+when not declared(c_fwrite):
+  proc c_fwrite(buf: pointer, size, n: csize_t, f: File): csize_t {.
+    importc: "fwrite", header: "<stdio.h>".}
+
+# C routine that is used here:
+proc c_fread(buf: pointer, size, n: csize_t, f: File): csize_t {.
+  importc: "fread", header: "<stdio.h>", tags: [ReadIOEffect].}
+when defined(windows):
+  when not defined(amd64):
+    proc c_fseek(f: File, offset: int64, whence: cint): cint {.
+      importc: "fseek", header: "<stdio.h>", tags: [].}
+    proc c_ftell(f: File): int64 {.
+      importc: "ftell", header: "<stdio.h>", tags: [].}
+  else:
+    proc c_fseek(f: File, offset: int64, whence: cint): cint {.
+      importc: "_fseeki64", header: "<stdio.h>", tags: [].}
+    when defined(tcc):
+      proc c_fsetpos(f: File, pos: var int64): int32 {.
+        importc: "fsetpos", header: "<stdio.h>", tags: [].}
+      proc c_fgetpos(f: File, pos: var int64): int32 {.
+        importc: "fgetpos", header: "<stdio.h>", tags: [].}
+      proc c_telli64(f: cint): int64 {.
+        importc: "_telli64", header: "<io.h>", tags: [].}
+      proc c_ftell(f: File): int64 =
+        # Taken from https://pt.osdn.net/projects/mingw/scm/git/mingw-org-wsl/blobs/5.4-trunk/mingwrt/mingwex/stdio/ftelli64.c
+        result = -1'i64
+        var pos: int64
+        if c_fgetpos(f, pos) == 0 and c_fsetpos(f, pos) == 0:
+          result = c_telli64(c_fileno(f))
+    else:
+      proc c_ftell(f: File): int64 {.
+        importc: "_ftelli64", header: "<stdio.h>", tags: [].}
+else:
+  proc c_fseek(f: File, offset: int64, whence: cint): cint {.
+    importc: "fseeko", header: "<stdio.h>", tags: [].}
+  proc c_ftell(f: File): int64 {.
+    importc: "ftello", header: "<stdio.h>", tags: [].}
+proc c_ferror(f: File): cint {.
+  importc: "ferror", header: "<stdio.h>", tags: [].}
+proc c_setvbuf(f: File, buf: pointer, mode: cint, size: csize_t): cint {.
+  importc: "setvbuf", header: "<stdio.h>", tags: [].}
+
+proc c_fprintf(f: File, frmt: cstring): cint {.
+  importc: "fprintf", header: "<stdio.h>", varargs, discardable.}
+proc c_fputc(c: char, f: File): cint {.
+  importc: "fputc", header: "<stdio.h>".}
+
+proc raiseEIO(msg: string) {.noinline, noreturn.} =
+  raise newException(IOError, msg)
+
+proc raiseEOF() {.noinline, noreturn.} =
+  raise newException(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
+    EINTR {.importc: "EINTR", header: "<errno.h>".}: cint
+
+proc checkErr(f: File) =
+  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 {.
+  tags: [ReadIOEffect], benign.} =
+  ## Reads `len` bytes into the buffer pointed to by `buffer`. Returns
+  ## the actual number of bytes that have been read which may be less than
+  ## `len` (if not as many bytes are remaining), but not greater.
+  result = cast[int](c_fread(buffer, 1, cast[csize_t](len), f))
+  if result != len: checkErr(f)
+
+proc readBytes*(f: File, a: var openArray[int8|uint8], start,
+    len: Natural): int {.
+  tags: [ReadIOEffect], benign.} =
+  ## Reads `len` bytes into the buffer `a` starting at `a[start]`. Returns
+  ## the actual number of bytes that have been read which may be less than
+  ## `len` (if not as many bytes are remaining), but not greater.
+  result = readBuffer(f, addr(a[start]), len)
+
+proc readChars*(f: File, a: var openArray[char]): int {.tags: [ReadIOEffect], benign.} =
+  ## Reads up to `a.len` bytes into the buffer `a`. Returns
+  ## the actual number of bytes that have been read which may be less than
+  ## `a.len` (if not as many bytes are remaining), but not greater.
+  result = readBuffer(f, addr(a[0]), a.len)
+
+proc readChars*(f: File, a: var openArray[char], start, len: Natural): int {.
+  tags: [ReadIOEffect], benign, deprecated:
+    "use other `readChars` overload, possibly via: readChars(toOpenArray(buf, start, len-1))".} =
+  ## Reads `len` bytes into the buffer `a` starting at `a[start]`. Returns
+  ## the actual number of bytes that have been read which may be less than
+  ## `len` (if not as many bytes are remaining), but not greater.
+  if (start + len) > len(a):
+    raiseEIO("buffer overflow: (start+len) > length of openarray buffer")
+  result = readBuffer(f, addr(a[start]), len)
+
+proc write*(f: File, c: cstring) {.tags: [WriteIOEffect], benign.} =
+  ## Writes a value to the file `f`. May throw an IO exception.
+  discard c_fputs(c, f)
+  checkErr(f)
+
+proc writeBuffer*(f: File, buffer: pointer, len: Natural): int {.
+  tags: [WriteIOEffect], benign.} =
+  ## Writes the bytes of buffer pointed to by the parameter `buffer` to the
+  ## file `f`. Returns the number of actual written bytes, which may be less
+  ## than `len` in case of an error.
+  result = cast[int](c_fwrite(buffer, 1, cast[csize_t](len), f))
+  checkErr(f)
+
+proc writeBytes*(f: File, a: openArray[int8|uint8], start, len: Natural): int {.
+  tags: [WriteIOEffect], benign.} =
+  ## Writes the bytes of `a[start..start+len-1]` to the file `f`. Returns
+  ## the number of actual written bytes, which may be less than `len` in case
+  ## of an error.
+  var x = cast[ptr UncheckedArray[int8]](a)
+  result = writeBuffer(f, addr(x[int(start)]), len)
+
+proc writeChars*(f: File, a: openArray[char], start, len: Natural): int {.
+  tags: [WriteIOEffect], benign.} =
+  ## Writes the bytes of `a[start..start+len-1]` to the file `f`. Returns
+  ## the number of actual written bytes, which may be less than `len` in case
+  ## of an error.
+  var x = cast[ptr UncheckedArray[int8]](a)
+  result = writeBuffer(f, addr(x[int(start)]), len)
+
+when defined(windows):
+  proc writeWindows(f: File; s: string; doRaise = false) =
+    # Don't ask why but the 'printf' family of function is the only thing
+    # that writes utf-8 strings reliably on Windows. At least on my Win 10
+    # machine. We also enable `setConsoleOutputCP(65001)` now by default.
+    # But we cannot call printf directly as the string might contain \0.
+    # So we have to loop over all the sections separated by potential \0s.
+    var i = int c_fprintf(f, "%s", s)
+    while i < s.len:
+      if s[i] == '\0':
+        let w = c_fputc('\0', f)
+        if w != 0:
+          if doRaise: raiseEIO("cannot write string to file")
+          break
+        inc i
+      else:
+        let w = c_fprintf(f, "%s", unsafeAddr s[i])
+        if w <= 0:
+          if doRaise: raiseEIO("cannot write string to file")
+          break
+        inc i, w
+
+proc write*(f: File, s: string) {.tags: [WriteIOEffect], benign.} =
+  when defined(windows):
+    writeWindows(f, s, doRaise = true)
+  else:
+    if writeBuffer(f, cstring(s), s.len) != s.len:
+      raiseEIO("cannot write string to file")
+{.pop.}
+
+when defined(nimscript):
+  when defined(windows):
+    const
+      IOFBF = cint(0)
+      IONBF = cint(4)
+  else:
+    # On all systems I could find, including Linux, Mac OS X, and the BSDs
+    const
+      IOFBF = cint(0)
+      IONBF = cint(2)
+else:
+  var
+    IOFBF {.importc: "_IOFBF", nodecl.}: cint
+    IONBF {.importc: "_IONBF", nodecl.}: cint
+
+const SupportIoctlInheritCtl = (defined(linux) or defined(bsd)) and
+                              not defined(nimscript)
+when SupportIoctlInheritCtl:
+  var
+    FIOCLEX {.importc, header: "<sys/ioctl.h>".}: cint
+    FIONCLEX {.importc, header: "<sys/ioctl.h>".}: cint
+
+  proc c_ioctl(fd: cint, request: cint): cint {.
+    importc: "ioctl", header: "<sys/ioctl.h>", varargs.}
+elif defined(posix) and not defined(lwip) and not defined(nimscript):
+  var
+    F_GETFD {.importc, header: "<fcntl.h>".}: cint
+    F_SETFD {.importc, header: "<fcntl.h>".}: cint
+    FD_CLOEXEC {.importc, header: "<fcntl.h>".}: cint
+
+  proc c_fcntl(fd: cint, cmd: cint): cint {.
+    importc: "fcntl", header: "<fcntl.h>", varargs.}
+elif defined(windows):
+  type
+    WinDWORD = culong
+    WinBOOL = cint
+
+  const HANDLE_FLAG_INHERIT = 1.WinDWORD
+
+  proc getOsfhandle(fd: cint): int {.
+    importc: "_get_osfhandle", header: "<io.h>".}
+
+  type
+    IoHandle = distinct pointer
+      ## Windows' HANDLE type. Defined as an untyped pointer but is **not**
+      ## one. Named like this to avoid collision with other `system` modules.
+
+  proc setHandleInformation(hObject: IoHandle, dwMask, dwFlags: WinDWORD):
+                           WinBOOL {.stdcall, dynlib: "kernel32",
+                                  importc: "SetHandleInformation".}
+
+const
+  BufSize = 4000
+
+proc close*(f: File) {.tags: [], gcsafe, sideEffect.} =
+  ## Closes the file.
+  if not f.isNil:
+    discard c_fclose(f)
+
+proc readChar*(f: File): char {.tags: [ReadIOEffect].} =
+  ## Reads a single character from the stream `f`. Should not be used in
+  ## performance sensitive code.
+  let x = c_fgetc(f)
+  if x < 0:
+    checkErr(f)
+    raiseEOF()
+  result = char(x)
+
+proc flushFile*(f: File) {.tags: [WriteIOEffect].} =
+  ## Flushes `f`'s buffer.
+  discard c_fflush(f)
+
+proc getFileHandle*(f: File): FileHandle =
+  ## Returns the file handle of the file `f`. This is only useful for
+  ## platform specific programming.
+  ## Note that on Windows this doesn't return the Windows-specific handle,
+  ## but the C library's notion of a handle, whatever that means.
+  ## Use `getOsFileHandle` instead.
+  c_fileno(f)
+
+proc getOsFileHandle*(f: File): FileHandle =
+  ## Returns the OS file handle of the file `f`. This is only useful for
+  ## platform specific programming.
+  when defined(windows):
+    result = FileHandle getOsfhandle(cint getFileHandle(f))
+  else:
+    result = c_fileno(f)
+
+when defined(nimdoc) or (defined(posix) and not defined(nimscript)) or defined(windows):
+  proc setInheritable*(f: FileHandle, inheritable: bool): bool =
+    ## Controls whether a file handle can be inherited by child processes. Returns
+    ## `true` on success. This requires the OS file handle, which can be
+    ## retrieved via `getOsFileHandle <#getOsFileHandle,File>`_.
+    ##
+    ## This procedure is not guaranteed to be available for all platforms. Test for
+    ## availability with `declared() <system.html#declared,untyped>`_.
+    when SupportIoctlInheritCtl:
+      result = c_ioctl(f, if inheritable: FIONCLEX else: FIOCLEX) != -1
+    elif defined(freertos) or defined(zephyr):
+      result = true
+    elif defined(posix):
+      var flags = c_fcntl(f, F_GETFD)
+      if flags == -1:
+        return false
+      flags = if inheritable: flags and not FD_CLOEXEC else: flags or FD_CLOEXEC
+      result = c_fcntl(f, F_SETFD, flags) != -1
+    else:
+      result = setHandleInformation(cast[IoHandle](f), HANDLE_FLAG_INHERIT,
+                                    inheritable.WinDWORD) != 0
+
+proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect],
+              benign.} =
+  ## Reads a line of text from the file `f` into `line`. May throw an IO
+  ## exception.
+  ## 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.
+  proc c_memchr(s: pointer, c: cint, n: csize_t): pointer {.
+    importc: "memchr", header: "<string.h>".}
+
+  when defined(windows):
+    proc readConsole(hConsoleInput: FileHandle, lpBuffer: pointer,
+                     nNumberOfCharsToRead: int32,
+                     lpNumberOfCharsRead: ptr int32,
+                     pInputControl: pointer): int32 {.
+      importc: "ReadConsoleW", stdcall, dynlib: "kernel32".}
+
+    proc getLastError(): int32 {.
+      importc: "GetLastError", stdcall, dynlib: "kernel32", sideEffect.}
+
+    proc formatMessageW(dwFlags: int32, lpSource: pointer,
+                        dwMessageId, dwLanguageId: int32,
+                        lpBuffer: pointer, nSize: int32,
+                        arguments: pointer): int32 {.
+      importc: "FormatMessageW", stdcall, dynlib: "kernel32".}
+
+    proc localFree(p: pointer) {.
+      importc: "LocalFree", stdcall, dynlib: "kernel32".}
+
+    proc isatty(f: File): bool =
+      when defined(posix):
+        proc isatty(fildes: FileHandle): cint {.
+          importc: "isatty", header: "<unistd.h>".}
+      else:
+        proc isatty(fildes: FileHandle): cint {.
+          importc: "_isatty", header: "<io.h>".}
+      result = isatty(getFileHandle(f)) != 0'i32
+
+    # this implies the file is open
+    if f.isatty:
+      const numberOfCharsToRead = 2048
+      var numberOfCharsRead = 0'i32
+      var buffer = newWideCString(numberOfCharsToRead)
+      if readConsole(getOsFileHandle(f), addr(buffer[0]),
+        numberOfCharsToRead, addr(numberOfCharsRead), nil) == 0:
+        var error = getLastError()
+        var errorMsg: string
+        var msgbuf: WideCString
+        if formatMessageW(0x00000100 or 0x00001000 or 0x00000200,
+                        nil, error, 0, addr(msgbuf), 0, nil) != 0'i32:
+          errorMsg = $msgbuf
+          if msgbuf != nil:
+            localFree(cast[pointer](msgbuf))
+        raiseEIO("error: " & $error & " `" & errorMsg & "`")
+      # input always ends with "\r\n"
+      numberOfCharsRead -= 2
+      # handle Ctrl+Z as EOF
+      for i in 0..<numberOfCharsRead:
+        if buffer[i].uint16 == 26: #Ctrl+Z
+          close(f) #has the same effect as setting EOF
+          if i == 0:
+            line = ""
+            return false
+          numberOfCharsRead = i
+          break
+      buffer[numberOfCharsRead] = 0.Utf16Char
+      when defined(nimv2):
+        line = $toWideCString(buffer)
+      else:
+        line = $buffer
+      return(true)
+
+  var pos = 0
+
+  # Use the currently reserved space for a first try
+  var sp = max(line.len, 80)
+  line.setLen(sp)
+
+  while true:
+    # memset to \L so that we can tell how far fgets wrote, even on EOF, where
+    # fgets doesn't append an \L
+    for i in 0..<sp: line[pos+i] = '\L'
+
+    var fgetsSuccess: bool
+    while true:
+      # fixes #9634; this pattern may need to be abstracted as a template if reused;
+      # likely other io procs need this for correctness.
+      fgetsSuccess = c_fgets(cast[cstring](addr line[pos]), sp.cint, f) != nil
+      if fgetsSuccess: break
+      when not defined(nimscript):
+        if errno == EINTR:
+          errno = 0
+          c_clearerr(f)
+          continue
+      checkErr(f)
+      break
+
+    let m = c_memchr(addr line[pos], '\L'.ord, cast[csize_t](sp))
+    if m != nil:
+      # \l found: Could be our own or the one by fgets, in any case, we're done
+      var last = cast[int](m) - cast[int](addr line[0])
+      if last > 0 and line[last-1] == '\c':
+        line.setLen(last-1)
+        return last > 1 or fgetsSuccess
+      elif last > 0 and line[last-1] == '\0':
+        # We have to distinguish among three possible cases:
+        # \0\l\0 => line ending in a null character.
+        # \0\l\l => last line without newline, null was put there by fgets.
+        #   \0\l => last line without newline, null was put there by fgets.
+        if last >= pos + sp - 1 or line[last+1] != '\0': # bug #21273
+          dec last
+      line.setLen(last)
+      return last > 0 or fgetsSuccess
+    else:
+      # fgets will have inserted a null byte at the end of the string.
+      dec sp
+    # No \l found: Increase buffer and read more
+    inc pos, sp
+    sp = 128 # read in 128 bytes at a time
+    line.setLen(pos+sp)
+
+proc readLine*(f: File): string {.tags: [ReadIOEffect], benign.} =
+  ## Reads a line of text from the file `f`. May throw an IO exception.
+  ## A line of text may be delimited by `LF` or `CRLF`. The newline
+  ## character(s) are not part of the returned string.
+  result = newStringOfCap(80)
+  if not readLine(f, result): raiseEOF()
+
+proc write*(f: File, i: int) {.tags: [WriteIOEffect], benign.} =
+  when sizeof(int) == 8:
+    if c_fprintf(f, "%lld", i) < 0: checkErr(f)
+  else:
+    if c_fprintf(f, "%ld", i) < 0: checkErr(f)
+
+proc write*(f: File, i: BiggestInt) {.tags: [WriteIOEffect], benign.} =
+  when sizeof(BiggestInt) == 8:
+    if c_fprintf(f, "%lld", i) < 0: checkErr(f)
+  else:
+    if c_fprintf(f, "%ld", i) < 0: checkErr(f)
+
+proc write*(f: File, b: bool) {.tags: [WriteIOEffect], benign.} =
+  if b: write(f, "true")
+  else: write(f, "false")
+
+proc write*(f: File, r: float32) {.tags: [WriteIOEffect], benign.} =
+  var buffer {.noinit.}: array[65, char]
+  discard writeFloatToBuffer(buffer, r)
+  if c_fprintf(f, "%s", buffer[0].addr) < 0: checkErr(f)
+
+proc write*(f: File, r: BiggestFloat) {.tags: [WriteIOEffect], benign.} =
+  var buffer {.noinit.}: array[65, char]
+  discard writeFloatToBuffer(buffer, r)
+  if c_fprintf(f, "%s", buffer[0].addr) < 0: checkErr(f)
+
+proc write*(f: File, c: char) {.tags: [WriteIOEffect], benign.} =
+  discard c_putc(cint(c), f)
+
+proc write*(f: File, a: varargs[string, `$`]) {.tags: [WriteIOEffect], benign.} =
+  for x in items(a): write(f, x)
+
+proc readAllBuffer(file: File): string =
+  # This proc is for File we want to read but don't know how many
+  # bytes we need to read before the buffer is empty.
+  result = ""
+  var buffer = newString(BufSize)
+  while true:
+    var bytesRead = readBuffer(file, addr(buffer[0]), BufSize)
+    if bytesRead == BufSize:
+      result.add(buffer)
+    else:
+      buffer.setLen(bytesRead)
+      result.add(buffer)
+      break
+
+proc rawFileSize(file: File): int64 =
+  # this does not raise an error opposed to `getFileSize`
+  var oldPos = c_ftell(file)
+  discard c_fseek(file, 0, 2) # seek the end of the file
+  result = c_ftell(file)
+  discard c_fseek(file, oldPos, 0)
+
+proc endOfFile*(f: File): bool {.tags: [], benign.} =
+  ## Returns true if `f` is at the end.
+  var c = c_fgetc(f)
+  discard c_ungetc(c, f)
+  return c < 0'i32
+  #result = c_feof(f) != 0
+
+proc readAllFile(file: File, len: int64): string =
+  # We acquire the filesize beforehand and hope it doesn't change.
+  # Speeds things up.
+  result = newString(len)
+  let bytes = readBuffer(file, addr(result[0]), len)
+  if endOfFile(file):
+    if bytes.int64 < len:
+      result.setLen(bytes)
+  else:
+    # We read all the bytes but did not reach the EOF
+    # Try to read it as a buffer
+    result.add(readAllBuffer(file))
+
+proc readAllFile(file: File): string =
+  var len = rawFileSize(file)
+  result = readAllFile(file, len)
+
+proc readAll*(file: File): string {.tags: [ReadIOEffect], benign.} =
+  ## Reads all data from the stream `file`.
+  ##
+  ## Raises an IO exception in case of an error. It is an error if the
+  ## current file position is not at the beginning of the file.
+
+  # Separate handling needed because we need to buffer when we
+  # don't know the overall length of the File.
+  when declared(stdin):
+    let len = if file != stdin: rawFileSize(file) else: -1
+  else:
+    let len = rawFileSize(file)
+  if len > 0:
+    result = readAllFile(file, len)
+  else:
+    result = readAllBuffer(file)
+
+proc writeLine*[Ty](f: File, x: varargs[Ty, `$`]) {.inline,
+                          tags: [WriteIOEffect], benign.} =
+  ## Writes the values `x` to `f` and then writes "\\n".
+  ## May throw an IO exception.
+  for i in items(x):
+    write(f, i)
+  write(f, "\n")
+
+# interface to the C procs:
+
+when defined(windows):
+  when defined(cpp):
+    proc wfopen(filename, mode: WideCString): pointer {.
+      importcpp: "_wfopen((const wchar_t*)#, (const wchar_t*)#)", nodecl.}
+    proc wfreopen(filename, mode: WideCString, stream: File): File {.
+      importcpp: "_wfreopen((const wchar_t*)#, (const wchar_t*)#, #)", nodecl.}
+  else:
+    proc wfopen(filename, mode: WideCString): pointer {.
+      importc: "_wfopen", nodecl.}
+    proc wfreopen(filename, mode: WideCString, stream: File): File {.
+      importc: "_wfreopen", nodecl.}
+
+  proc fopen(filename, mode: cstring): pointer =
+    var f = newWideCString(filename)
+    var m = newWideCString(mode)
+    result = wfopen(f, m)
+
+  proc freopen(filename, mode: cstring, stream: File): File =
+    var f = newWideCString(filename)
+    var m = newWideCString(mode)
+    result = wfreopen(f, m, stream)
+
+else:
+  proc fopen(filename, mode: cstring): pointer {.importc: "fopen", nodecl.}
+  proc freopen(filename, mode: cstring, stream: File): File {.
+    importc: "freopen", nodecl.}
+
+const
+  NoInheritFlag =
+    # Platform specific flag for creating a File without inheritance.
+    when not defined(nimInheritHandles):
+      when defined(windows):
+        "N"
+      elif defined(linux) or defined(bsd):
+        "e"
+      else:
+        ""
+    else:
+      ""
+  RawFormatOpen: array[FileMode, cstring] = [
+    # used for open by FileHandle, which calls `fdopen`
+    cstring("rb"), "wb", "w+b", "r+b", "ab"]
+  FormatOpen: array[FileMode, cstring] = [
+    cstring("rb" & NoInheritFlag), "wb" & NoInheritFlag, "w+b" & NoInheritFlag,
+    "r+b" & NoInheritFlag, "ab" & NoInheritFlag
+  ]
+    #"rt", "wt", "w+t", "r+t", "at"
+    # we always use binary here as for Nim the OS line ending
+    # should not be translated.
+
+when defined(posix) and not defined(nimscript):
+  when defined(linux) and defined(amd64):
+    type
+      Mode {.importc: "mode_t", header: "<sys/types.h>".} = cint
+
+      # fillers ensure correct size & offsets
+      Stat {.importc: "struct stat",
+              header: "<sys/stat.h>", final, pure.} = object ## struct stat
+        filler_1: array[24, char]
+        st_mode: Mode ## Mode of file
+        filler_2: array[144 - 24 - 4, char]
+
+    proc modeIsDir(m: Mode): bool =
+      ## Test for a directory.
+      (m and 0o170000) == 0o40000
+
+  else:
+    type
+      Mode {.importc: "mode_t", header: "<sys/types.h>".} = cint
+
+      Stat {.importc: "struct stat",
+               header: "<sys/stat.h>", final, pure.} = object ## struct stat
+        st_mode: Mode ## Mode of file
+
+    proc modeIsDir(m: Mode): bool {.importc: "S_ISDIR", header: "<sys/stat.h>".}
+      ## Test for a directory.
+
+  proc c_fstat(a1: cint, a2: var Stat): cint {.
+    importc: "fstat", header: "<sys/stat.h>".}
+
+
+proc open*(f: var File, filename: string,
+          mode: FileMode = fmRead,
+          bufSize: int = -1): bool {.tags: [], raises: [], benign.} =
+  ## Opens a file named `filename` with given `mode`.
+  ##
+  ## Default mode is readonly. Returns true if the file could be opened.
+  ## This throws no exception if the file could not be opened.
+  ##
+  ## The file handle associated with the resulting `File` is not inheritable.
+  var p = fopen(filename.cstring, FormatOpen[mode])
+  if p != nil:
+    var f2 = cast[File](p)
+    when defined(posix) and not defined(nimscript):
+      # How `fopen` handles opening a directory is not specified in ISO C and
+      # POSIX. We do not want to handle directories as regular files that can
+      # be opened.
+      var res {.noinit.}: Stat
+      if c_fstat(getFileHandle(f2), res) >= 0'i32 and modeIsDir(res.st_mode):
+        close(f2)
+        return false
+    when not defined(nimInheritHandles) and declared(setInheritable) and
+         NoInheritFlag.len == 0:
+      if not setInheritable(getOsFileHandle(f2), false):
+        close(f2)
+        return false
+
+    result = true
+    f = cast[File](p)
+    if bufSize > 0 and bufSize.uint <= high(uint):
+      discard c_setvbuf(f, nil, IOFBF, cast[csize_t](bufSize))
+    elif bufSize == 0:
+      discard c_setvbuf(f, nil, IONBF, 0)
+
+proc reopen*(f: File, filename: string, mode: FileMode = fmRead): bool {.
+  tags: [], benign.} =
+  ## Reopens the file `f` with given `filename` and `mode`. This
+  ## is often used to redirect the `stdin`, `stdout` or `stderr`
+  ## file variables.
+  ##
+  ## Default mode is readonly. Returns true if the file could be reopened.
+  ##
+  ## The file handle associated with `f` won't be inheritable.
+  if freopen(filename.cstring, FormatOpen[mode], f) != nil:
+    when not defined(nimInheritHandles) and declared(setInheritable) and
+         NoInheritFlag.len == 0:
+      if not setInheritable(getOsFileHandle(f), false):
+        close(f)
+        return false
+    result = true
+
+proc open*(f: var File, filehandle: FileHandle,
+           mode: FileMode = fmRead): bool {.tags: [], raises: [], benign.} =
+  ## Creates a `File` from a `filehandle` with given `mode`.
+  ##
+  ## Default mode is readonly. Returns true if the file could be opened.
+  ##
+  ## The passed file handle will no longer be inheritable.
+  when not defined(nimInheritHandles) and declared(setInheritable):
+    let oshandle = when defined(windows): FileHandle getOsfhandle(
+        filehandle) else: filehandle
+    if not setInheritable(oshandle, false):
+      return false
+  f = c_fdopen(filehandle, RawFormatOpen[mode])
+  result = f != nil
+
+proc open*(filename: string,
+            mode: FileMode = fmRead, bufSize: int = -1): File =
+  ## Opens a file named `filename` with given `mode`.
+  ##
+  ## Default mode is readonly. Raises an `IOError` if the file
+  ## could not be opened.
+  ##
+  ## The file handle associated with the resulting `File` is not inheritable.
+  if not open(result, filename, mode, bufSize):
+    raise newException(IOError, "cannot open: " & filename)
+
+proc setFilePos*(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) {.benign, sideEffect.} =
+  ## Sets the position of the file pointer that is used for read/write
+  ## operations. The file's first byte has the index zero.
+  if c_fseek(f, pos, cint(relativeTo)) != 0:
+    raiseEIO("cannot set file position")
+
+proc getFilePos*(f: File): int64 {.benign.} =
+  ## Retrieves the current position of the file pointer that is used to
+  ## read from the file `f`. The file's first byte has the index zero.
+  result = c_ftell(f)
+  if result < 0: raiseEIO("cannot retrieve file position")
+
+proc getFileSize*(f: File): int64 {.tags: [ReadIOEffect], benign.} =
+  ## Retrieves the file size (in bytes) of `f`.
+  let oldPos = getFilePos(f)
+  discard c_fseek(f, 0, 2) # seek the end of the file
+  result = getFilePos(f)
+  setFilePos(f, oldPos)
+
+proc setStdIoUnbuffered*() {.tags: [], benign.} =
+  ## Configures `stdin`, `stdout` and `stderr` to be unbuffered.
+  when declared(stdout):
+    discard c_setvbuf(stdout, nil, IONBF, 0)
+  when declared(stderr):
+    discard c_setvbuf(stderr, nil, IONBF, 0)
+  when declared(stdin):
+    discard c_setvbuf(stdin, nil, IONBF, 0)
+
+
+when defined(windows) and not defined(nimscript) and not defined(js):
+  # work-around C's sucking abstraction:
+  # BUGFIX: stdin and stdout should be binary files!
+  proc c_setmode(handle, mode: cint) {.
+    importc: when defined(bcc): "setmode" else: "_setmode",
+    header: "<io.h>".}
+  var
+    O_BINARY {.importc: "_O_BINARY", header: "<fcntl.h>".}: cint
+
+  # we use binary mode on Windows:
+  c_setmode(c_fileno(stdin), O_BINARY)
+  c_setmode(c_fileno(stdout), O_BINARY)
+  c_setmode(c_fileno(stderr), O_BINARY)
+
+when defined(windows) and appType == "console" and
+    not defined(nimDontSetUtf8CodePage) and not defined(nimscript):
+  import std/exitprocs
+
+  proc setConsoleOutputCP(codepage: cuint): int32 {.stdcall, dynlib: "kernel32",
+    importc: "SetConsoleOutputCP".}
+  proc setConsoleCP(wCodePageID: cuint): int32 {.stdcall, dynlib: "kernel32",
+    importc: "SetConsoleCP".}
+  proc getConsoleOutputCP(): cuint {.stdcall, dynlib: "kernel32",
+    importc: "GetConsoleOutputCP".}
+  proc getConsoleCP(): cuint {.stdcall, dynlib: "kernel32",
+    importc: "GetConsoleCP".}
+
+  const Utf8codepage = 65001'u32
+
+  let
+    consoleOutputCP = getConsoleOutputCP()
+    consoleCP = getConsoleCP()
+
+  proc restoreConsoleOutputCP() = discard setConsoleOutputCP(consoleOutputCP)
+  proc restoreConsoleCP() = discard setConsoleCP(consoleCP)
+
+  if consoleOutputCP != Utf8codepage:
+    discard setConsoleOutputCP(Utf8codepage)
+    addExitProc(restoreConsoleOutputCP)
+
+  if consoleCP != Utf8codepage:
+    discard setConsoleCP(Utf8codepage)
+    addExitProc(restoreConsoleCP)
+
+proc readFile*(filename: string): string {.tags: [ReadIOEffect], benign.} =
+  ## Opens a file named `filename` for reading, calls `readAll
+  ## <#readAll,File>`_ and closes the file afterwards. Returns the string.
+  ## Raises an IO exception in case of an error. If you need to call
+  ## this inside a compile time macro you can use `staticRead
+  ## <system.html#staticRead,string>`_.
+  var f: File = nil
+  if open(f, filename):
+    try:
+      result = readAll(f)
+    finally:
+      close(f)
+  else:
+    raise newException(IOError, "cannot open: " & filename)
+
+proc writeFile*(filename, content: string) {.tags: [WriteIOEffect], benign.} =
+  ## Opens a file named `filename` for writing. Then writes the
+  ## `content` completely to the file and closes the file afterwards.
+  ## Raises an IO exception in case of an error.
+  var f: File = nil
+  if open(f, filename, fmWrite):
+    try:
+      f.write(content)
+    finally:
+      close(f)
+  else:
+    raise newException(IOError, "cannot open: " & filename)
+
+proc writeFile*(filename: string, content: openArray[byte]) {.since: (1, 1).} =
+  ## Opens a file named `filename` for writing. Then writes the
+  ## `content` completely to the file and closes the file afterwards.
+  ## Raises an IO exception in case of an error.
+  var f: File = nil
+  if open(f, filename, fmWrite):
+    try:
+      discard f.writeBuffer(unsafeAddr content[0], content.len)
+    finally:
+      close(f)
+  else:
+    raise newException(IOError, "cannot open: " & filename)
+
+proc readLines*(filename: string, n: Natural): seq[string] =
+  ## Reads `n` lines from the file named `filename`. Raises an IO exception
+  ## in case of an error. Raises EOF if file does not contain at least `n` lines.
+  ## Available at compile time. A line of text may be delimited by `LF` or `CRLF`.
+  ## The newline character(s) are not part of the returned strings.
+  var f: File = nil
+  if open(f, filename):
+    try:
+      result = newSeq[string](n)
+      for i in 0 .. n - 1:
+        if not readLine(f, result[i]):
+          raiseEOF()
+    finally:
+      close(f)
+  else:
+    raise newException(IOError, "cannot open: " & filename)
+
+template readLines*(filename: string): seq[
+    string] {.deprecated: "use readLines with two arguments".} =
+  readLines(filename, 1)
+
+iterator lines*(filename: string): string {.tags: [ReadIOEffect].} =
+  ## Iterates over any line in the file named `filename`.
+  ##
+  ## If the file does not exist `IOError` is raised. The trailing newline
+  ## character(s) are removed from the iterated lines. Example:
+  ##
+  runnableExamples:
+    import std/strutils
+
+    proc transformLetters(filename: string) =
+      var buffer = ""
+      for line in filename.lines:
+        buffer.add(line.replace("a", "0") & '\n')
+      writeFile(filename, buffer)
+  var f = open(filename, bufSize = 8000)
+  try:
+    var res = newStringOfCap(80)
+    while f.readLine(res): yield res
+  finally:
+    close(f)
+
+iterator lines*(f: File): string {.tags: [ReadIOEffect].} =
+  ## Iterates over any line in the file `f`.
+  ##
+  ## The trailing newline character(s) are removed from the iterated lines.
+  ##
+  runnableExamples:
+    proc countZeros(filename: File): tuple[lines, zeros: int] =
+      for line in filename.lines:
+        for letter in line:
+          if letter == '0':
+            result.zeros += 1
+        result.lines += 1
+  var res = newStringOfCap(80)
+  while f.readLine(res): yield res
+
+template `&=`*(f: File, x: typed) =
+  ## An alias for `write`.
+  write(f, x)
diff --git a/lib/std/sysatomics.nim b/lib/std/sysatomics.nim
new file mode 100644
index 000000000..2f203b3eb
--- /dev/null
+++ b/lib/std/sysatomics.nim
@@ -0,0 +1,376 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+when defined(nimPreviewSlimSystem):
+  {.deprecated: "use `std/atomics` instead".}
+
+# Atomic operations for Nim.
+{.push stackTrace:off, profiler:off.}
+
+const
+  hasThreadSupport = compileOption("threads") and not defined(nimscript)
+const someGcc = defined(gcc) or defined(llvm_gcc) or defined(clang) or defined(nintendoswitch)
+const someVcc = defined(vcc) or defined(clang_cl)
+
+type
+  AtomType* = SomeNumber|pointer|ptr|char|bool
+    ## Type Class representing valid types for use with atomic procs
+
+when someGcc:
+  type AtomMemModel* = distinct cint
+
+  var ATOMIC_RELAXED* {.importc: "__ATOMIC_RELAXED", nodecl.}: AtomMemModel
+    ## No barriers or synchronization.
+  var ATOMIC_CONSUME* {.importc: "__ATOMIC_CONSUME", nodecl.}: AtomMemModel
+    ## Data dependency only for both barrier and
+    ## synchronization with another thread.
+  var ATOMIC_ACQUIRE* {.importc: "__ATOMIC_ACQUIRE", nodecl.}: AtomMemModel
+    ## Barrier to hoisting of code and synchronizes with
+    ## release (or stronger)
+    ## semantic stores from another thread.
+  var ATOMIC_RELEASE* {.importc: "__ATOMIC_RELEASE", nodecl.}: AtomMemModel
+    ## Barrier to sinking of code and synchronizes with
+    ## acquire (or stronger)
+    ## semantic loads from another thread.
+  var ATOMIC_ACQ_REL* {.importc: "__ATOMIC_ACQ_REL", nodecl.}: AtomMemModel
+    ## Full barrier in both directions and synchronizes
+    ## with acquire loads
+    ## and release stores in another thread.
+  var ATOMIC_SEQ_CST* {.importc: "__ATOMIC_SEQ_CST", nodecl.}: AtomMemModel
+    ## Full barrier in both directions and synchronizes
+    ## with acquire loads
+    ## and release stores in all threads.
+
+  proc atomicLoadN*[T: AtomType](p: ptr T, mem: AtomMemModel): T {.
+    importc: "__atomic_load_n", nodecl.}
+    ## This proc implements an atomic load operation. It returns the contents at p.
+    ## ATOMIC_RELAXED, ATOMIC_SEQ_CST, ATOMIC_ACQUIRE, ATOMIC_CONSUME.
+
+  proc atomicLoad*[T: AtomType](p, ret: ptr T, mem: AtomMemModel) {.
+    importc: "__atomic_load", nodecl.}
+    ## This is the generic version of an atomic load. It returns the contents at p in ret.
+
+  proc atomicStoreN*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel) {.
+    importc: "__atomic_store_n", nodecl.}
+    ## This proc implements an atomic store operation. It writes val at p.
+    ## ATOMIC_RELAXED, ATOMIC_SEQ_CST, and ATOMIC_RELEASE.
+
+  proc atomicStore*[T: AtomType](p, val: ptr T, mem: AtomMemModel) {.
+    importc: "__atomic_store", nodecl.}
+    ## This is the generic version of an atomic store. It stores the value of val at p
+
+  proc atomicExchangeN*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
+    importc: "__atomic_exchange_n", nodecl.}
+    ## This proc implements an atomic exchange operation. It writes val at p,
+    ## and returns the previous contents at p.
+    ## ATOMIC_RELAXED, ATOMIC_SEQ_CST, ATOMIC_ACQUIRE, ATOMIC_RELEASE, ATOMIC_ACQ_REL
+
+  proc atomicExchange*[T: AtomType](p, val, ret: ptr T, mem: AtomMemModel) {.
+    importc: "__atomic_exchange", nodecl.}
+    ## This is the generic version of an atomic exchange. It stores the contents at val at p.
+    ## The original value at p is copied into ret.
+
+  proc atomicCompareExchangeN*[T: AtomType](p, expected: ptr T, desired: T,
+    weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool {.
+    importc: "__atomic_compare_exchange_n", nodecl.}
+    ## This proc implements an atomic compare and exchange operation. This compares the
+    ## contents at p with the contents at expected and if equal, writes desired at p.
+    ## If they are not equal, the current contents at p is written into expected.
+    ## Weak is true for weak compare_exchange, and false for the strong variation.
+    ## Many targets only offer the strong variation and ignore the parameter.
+    ## When in doubt, use the strong variation.
+    ## True is returned if desired is written at p and the execution is considered
+    ## to conform to the memory model specified by success_memmodel. There are no
+    ## restrictions on what memory model can be used here. False is returned otherwise,
+    ## and the execution is considered to conform to failure_memmodel. This memory model
+    ## cannot be __ATOMIC_RELEASE nor __ATOMIC_ACQ_REL. It also cannot be a stronger model
+    ## than that specified by success_memmodel.
+
+  proc atomicCompareExchange*[T: AtomType](p, expected, desired: ptr T,
+    weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool {.
+    importc: "__atomic_compare_exchange", nodecl.}
+    ## This proc implements the generic version of atomic_compare_exchange.
+    ## The proc is virtually identical to atomic_compare_exchange_n, except the desired
+    ## value is also a pointer.
+
+  ## Perform the operation return the new value, all memory models are valid
+  proc atomicAddFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
+    importc: "__atomic_add_fetch", nodecl.}
+  proc atomicSubFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
+    importc: "__atomic_sub_fetch", nodecl.}
+  proc atomicOrFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
+    importc: "__atomic_or_fetch", nodecl.}
+  proc atomicAndFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
+    importc: "__atomic_and_fetch", nodecl.}
+  proc atomicXorFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
+    importc: "__atomic_xor_fetch", nodecl.}
+  proc atomicNandFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
+    importc: "__atomic_nand_fetch", nodecl.}
+
+  ## Perform the operation return the old value, all memory models are valid
+  proc atomicFetchAdd*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
+    importc: "__atomic_fetch_add", nodecl.}
+  proc atomicFetchSub*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
+    importc: "__atomic_fetch_sub", nodecl.}
+  proc atomicFetchOr*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
+    importc: "__atomic_fetch_or", nodecl.}
+  proc atomicFetchAnd*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
+    importc: "__atomic_fetch_and", nodecl.}
+  proc atomicFetchXor*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
+    importc: "__atomic_fetch_xor", nodecl.}
+  proc atomicFetchNand*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
+    importc: "__atomic_fetch_nand", nodecl.}
+
+  proc atomicTestAndSet*(p: pointer, mem: AtomMemModel): bool {.
+    importc: "__atomic_test_and_set", nodecl.}
+    ## This built-in function performs an atomic test-and-set operation on the byte at p.
+    ## The byte is set to some implementation defined nonzero "set" value and the return
+    ## value is true if and only if the previous contents were "set".
+    ## All memory models are valid.
+
+  proc atomicClear*(p: pointer, mem: AtomMemModel) {.
+    importc: "__atomic_clear", nodecl.}
+    ## This built-in function performs an atomic clear operation at p.
+    ## After the operation, at p contains 0.
+    ## ATOMIC_RELAXED, ATOMIC_SEQ_CST, ATOMIC_RELEASE
+
+  proc atomicThreadFence*(mem: AtomMemModel) {.
+    importc: "__atomic_thread_fence", nodecl.}
+    ## This built-in function acts as a synchronization fence between threads based
+    ## on the specified memory model. All memory orders are valid.
+
+  proc atomicSignalFence*(mem: AtomMemModel) {.
+    importc: "__atomic_signal_fence", nodecl.}
+    ## This built-in function acts as a synchronization fence between a thread and
+    ## signal handlers based in the same thread. All memory orders are valid.
+
+  proc atomicAlwaysLockFree*(size: int, p: pointer): bool {.
+    importc: "__atomic_always_lock_free", nodecl.}
+    ## This built-in function returns true if objects of size bytes always generate
+    ## lock free atomic instructions for the target architecture. size must resolve
+    ## to a compile-time constant and the result also resolves to a compile-time constant.
+    ## ptr is an optional pointer to the object that may be used to determine alignment.
+    ## A value of 0 indicates typical alignment should be used. The compiler may also
+    ## ignore this parameter.
+
+  proc atomicIsLockFree*(size: int, p: pointer): bool {.
+    importc: "__atomic_is_lock_free", nodecl.}
+    ## This built-in function returns true if objects of size bytes always generate
+    ## lock free atomic instructions for the target architecture. If it is not known
+    ## to be lock free a call is made to a runtime routine named __atomic_is_lock_free.
+    ## ptr is an optional pointer to the object that may be used to determine alignment.
+    ## A value of 0 indicates typical alignment should be used. The compiler may also
+    ## ignore this parameter.
+
+  template fence*() = atomicThreadFence(ATOMIC_SEQ_CST)
+elif someVcc:
+  type AtomMemModel* = distinct cint
+
+  const
+    ATOMIC_RELAXED* = 0.AtomMemModel
+    ATOMIC_CONSUME* = 1.AtomMemModel
+    ATOMIC_ACQUIRE* = 2.AtomMemModel
+    ATOMIC_RELEASE* = 3.AtomMemModel
+    ATOMIC_ACQ_REL* = 4.AtomMemModel
+    ATOMIC_SEQ_CST* = 5.AtomMemModel
+
+  proc `==`(x1, x2: AtomMemModel): bool {.borrow.}
+
+  proc readBarrier() {.importc: "_ReadBarrier", header: "<intrin.h>".}
+  proc writeBarrier() {.importc: "_WriteBarrier", header: "<intrin.h>".}
+  proc fence*() {.importc: "_ReadWriteBarrier", header: "<intrin.h>".}
+
+  when defined(cpp):
+    proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
+      {.importcpp: "_InterlockedCompareExchange64(static_cast<NI64 volatile *>(#), #, #)", header: "<intrin.h>".}
+    proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
+      {.importcpp: "_InterlockedCompareExchange(static_cast<long volatile *>(#), #, #)", header: "<intrin.h>".}
+    proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
+      {.importcpp: "_InterlockedCompareExchange8(static_cast<char volatile *>(#), #, #)", header: "<intrin.h>".}
+    proc interlockedExchange8(location: pointer; desired: int8): int8 {.importcpp: "_InterlockedExchange8(static_cast<NI8 volatile *>(#), #)", header: "<intrin.h>".}
+    proc interlockedExchange16(location: pointer; desired: int16): int16 {.importcpp: "_InterlockedExchange16(static_cast<NI16 volatile *>(#), #)", header: "<intrin.h>".}
+    proc interlockedExchange32(location: pointer; desired: int32): int32 {.importcpp: "_InterlockedExchange(static_cast<long volatile *>(#), #)", header: "<intrin.h>".}
+    proc interlockedExchange64(location: pointer; desired: int64): int64 {.importcpp: "_InterlockedExchange64(static_cast<NI64 volatile *>(#), #)", header: "<intrin.h>".}
+  else:
+    proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
+      {.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".}
+    proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
+      {.importc: "_InterlockedCompareExchange", header: "<intrin.h>".}
+    proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
+      {.importc: "_InterlockedCompareExchange8", header: "<intrin.h>".}
+
+    proc interlockedExchange8(location: pointer; desired: int8): int8 {.importc: "_InterlockedExchange8", header: "<intrin.h>".}
+    proc interlockedExchange16(location: pointer; desired: int16): int16 {.importc: "_InterlockedExchange16", header: "<intrin.h>".}
+    proc interlockedExchange32(location: pointer; desired: int32): int32 {.importc: "_InterlockedExchange", header: "<intrin.h>".}
+    proc interlockedExchange64(location: pointer; desired: int64): int64 {.importc: "_InterlockedExchange64", header: "<intrin.h>".}
+
+
+  template barrier(mem: AtomMemModel) =
+    when mem == ATOMIC_RELAXED: discard
+    elif mem == ATOMIC_CONSUME: readBarrier()
+    elif mem == ATOMIC_ACQUIRE: writeBarrier()
+    elif mem == ATOMIC_RELEASE: fence()
+    elif mem == ATOMIC_ACQ_REL: fence()
+    elif mem == ATOMIC_SEQ_CST: fence()
+
+  proc atomicStoreN*[T: AtomType](p: ptr T, val: T, mem: static[AtomMemModel]) =
+    barrier(mem)
+    p[] = val
+
+  proc atomicLoadN*[T: AtomType](p: ptr T, mem: static[AtomMemModel]): T =
+    result = p[]
+    barrier(mem)
+
+  proc atomicCompareExchangeN*[T: ptr](p, expected: ptr T, desired: T,
+    weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool =
+    when sizeof(T) == 8:
+      interlockedCompareExchange64(p, cast[int64](desired), cast[int64](expected)) ==
+        cast[int64](expected)
+    elif sizeof(T) == 4:
+      interlockedCompareExchange32(p, cast[int32](desired), cast[int32](expected)) ==
+        cast[int32](expected)
+
+  proc atomicExchangeN*[T: ptr](p: ptr T, val: T, mem: AtomMemModel): T =
+    when sizeof(T) == 8:
+      cast[T](interlockedExchange64(p, cast[int64](val)))
+    elif sizeof(T) == 4:
+      cast[T](interlockedExchange32(p, cast[int32](val)))
+  when defined(cpp):
+    when sizeof(int) == 8:
+      proc addAndFetch*(p: ptr int, val: int): int {.
+        importcpp: "_InterlockedExchangeAdd64(static_cast<NI volatile *>(#), #)",
+        header: "<intrin.h>".}
+    else:
+      proc addAndFetch*(p: ptr int, val: int): int {.
+        importcpp: "_InterlockedExchangeAdd(reinterpret_cast<long volatile *>(#), static_cast<long>(#))",
+        header: "<intrin.h>".}
+  else:
+    when sizeof(int) == 8:
+      proc addAndFetch*(p: ptr int, val: int): int {.
+        importc: "_InterlockedExchangeAdd64", header: "<intrin.h>".}
+    else:
+      proc addAndFetch*(p: ptr int, val: int): int {.
+        importc: "_InterlockedExchangeAdd", header: "<intrin.h>".}
+
+else:
+  proc addAndFetch*(p: ptr int, val: int): int {.inline.} =
+    inc(p[], val)
+    result = p[]
+
+
+proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable, raises: [], tags: [].} =
+  ## Atomically increments the integer by some `x`. It returns the new value.
+  when someGcc and hasThreadSupport:
+    result = atomicAddFetch(memLoc.addr, x, ATOMIC_SEQ_CST)
+  elif someVcc and hasThreadSupport:
+    result = addAndFetch(memLoc.addr, x)
+    inc(result, x)
+  else:
+    inc(memLoc, x)
+    result = memLoc
+
+proc atomicDec*(memLoc: var int, x: int = 1): int {.inline, discardable, raises: [], tags: [].} =
+  ## Atomically decrements the integer by some `x`. It returns the new value.
+  when someGcc and hasThreadSupport:
+    when declared(atomicSubFetch):
+      result = atomicSubFetch(memLoc.addr, x, ATOMIC_SEQ_CST)
+    else:
+      result = atomicAddFetch(memLoc.addr, -x, ATOMIC_SEQ_CST)
+  elif someVcc and hasThreadSupport:
+    result = addAndFetch(memLoc.addr, -x)
+    dec(result, x)
+  else:
+    dec(memLoc, x)
+    result = memLoc
+
+when someVcc:
+  proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool =
+    when sizeof(T) == 8:
+      interlockedCompareExchange64(p, cast[int64](newValue), cast[int64](oldValue)) ==
+        cast[int64](oldValue)
+    elif sizeof(T) == 4:
+      interlockedCompareExchange32(p, cast[int32](newValue), cast[int32](oldValue)) ==
+        cast[int32](oldValue)
+    elif sizeof(T) == 1:
+      interlockedCompareExchange8(p, cast[byte](newValue), cast[byte](oldValue)) ==
+        cast[byte](oldValue)
+    else:
+      {.error: "invalid CAS instruction".}
+
+elif defined(tcc):
+  when defined(amd64):
+    {.emit:"""
+static int __tcc_cas(int *ptr, int oldVal, int newVal)
+{
+    unsigned char ret;
+    __asm__ __volatile__ (
+            "  lock\n"
+            "  cmpxchgq %2,%1\n"
+            "  sete %0\n"
+            : "=q" (ret), "=m" (*ptr)
+            : "r" (newVal), "m" (*ptr), "a" (oldVal)
+            : "memory");
+
+    return ret;
+}
+""".}
+  else:
+    #assert sizeof(int) == 4
+    {.emit:"""
+static int __tcc_cas(int *ptr, int oldVal, int newVal)
+{
+    unsigned char ret;
+    __asm__ __volatile__ (
+            "  lock\n"
+            "  cmpxchgl %2,%1\n"
+            "  sete %0\n"
+            : "=q" (ret), "=m" (*ptr)
+            : "r" (newVal), "m" (*ptr), "a" (oldVal)
+            : "memory");
+
+    return ret;
+}
+""".}
+
+  proc tcc_cas(p: ptr int; oldValue, newValue: int): bool
+    {.importc: "__tcc_cas", nodecl.}
+  proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool =
+    tcc_cas(cast[ptr int](p), cast[int](oldValue), cast[int](newValue))
+elif declared(atomicCompareExchangeN):
+  proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool =
+    atomicCompareExchangeN(p, oldValue.unsafeAddr, newValue, false, ATOMIC_SEQ_CST, ATOMIC_SEQ_CST)
+else:
+  # this is valid for GCC and Intel C++
+  proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool
+    {.importc: "__sync_bool_compare_and_swap", nodecl.}
+  # XXX is this valid for 'int'?
+
+
+when (defined(x86) or defined(amd64)) and someVcc:
+  proc cpuRelax* {.importc: "YieldProcessor", header: "<windows.h>".}
+elif (defined(x86) or defined(amd64)) and (someGcc or defined(bcc)):
+  proc cpuRelax* {.inline.} =
+    {.emit: """asm volatile("pause" ::: "memory");""".}
+elif someGcc or defined(tcc):
+  proc cpuRelax* {.inline.} =
+    {.emit: """asm volatile("" ::: "memory");""".}
+elif defined(icl):
+  proc cpuRelax* {.importc: "_mm_pause", header: "xmmintrin.h".}
+elif false:
+  from std/os import sleep
+
+  proc cpuRelax* {.inline.} = os.sleep(1)
+
+when not declared(fence) and hasThreadSupport:
+  # XXX fixme
+  proc fence*() {.inline.} =
+    var dummy: bool
+    discard cas(addr dummy, false, true)
+
+{.pop.}
diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim
new file mode 100644
index 000000000..6f2c6b0c1
--- /dev/null
+++ b/lib/std/sysrand.nim
@@ -0,0 +1,326 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2021 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## .. warning:: This module was added in Nim 1.6. If you are using it for cryptographic purposes,
+##   keep in mind that so far this has not been audited by any security professionals,
+##   therefore may not be secure.
+##
+## `std/sysrand` generates random numbers from a secure source provided by the operating system.
+## It is a cryptographically secure pseudorandom number generator
+## and should be unpredictable enough for cryptographic applications,
+## though its exact quality depends on the OS implementation.
+##
+## | Targets              | Implementation        |
+## | :---                 | ----:                 |
+## | Windows              | `BCryptGenRandom`_    |
+## | Linux                | `getrandom`_          |
+## | MacOSX               | `SecRandomCopyBytes`_ |
+## | iOS                  | `SecRandomCopyBytes`_ |
+## | OpenBSD              | `getentropy openbsd`_ |
+## | FreeBSD              | `getrandom freebsd`_  |
+## | JS (Web Browser)     | `getRandomValues`_    |
+## | Node.js              | `randomFillSync`_     |
+## | Other Unix platforms | `/dev/urandom`_       |
+##
+## .. _BCryptGenRandom: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
+## .. _getrandom: https://man7.org/linux/man-pages/man2/getrandom.2.html
+## .. _getentropy: https://www.unix.com/man-page/mojave/2/getentropy
+## .. _SecRandomCopyBytes: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc
+## .. _getentropy openbsd: https://man.openbsd.org/getentropy.2
+## .. _getrandom freebsd: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable
+## .. _getRandomValues: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues
+## .. _randomFillSync: https://nodejs.org/api/crypto.html#crypto_crypto_randomfillsync_buffer_offset_size
+## .. _/dev/urandom: https://en.wikipedia.org/wiki//dev/random
+##
+## On a Linux target, a call to the `getrandom` syscall can be avoided (e.g.
+## for targets running kernel version < 3.17) by passing a compile flag of
+## `-d:nimNoGetRandom`. If this flag is passed, sysrand will use `/dev/urandom`
+## as with any other POSIX compliant OS.
+##
+
+runnableExamples:
+  doAssert urandom(0).len == 0
+  doAssert urandom(113).len == 113
+  doAssert urandom(1234) != urandom(1234) # unlikely to fail in practice
+
+##
+## See also
+## ========
+## * `random module <random.html>`_
+##
+
+
+when not defined(js):
+  import std/oserrors
+
+when defined(posix):
+  import std/posix
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+const
+  batchImplOS = defined(freebsd) or defined(openbsd) or defined(zephyr)
+  batchSize {.used.} = 256
+
+when batchImplOS:
+  template batchImpl(result: var int, dest: var openArray[byte], getRandomImpl) =
+    let size = dest.len
+    if size == 0:
+      return
+
+    let
+      chunks = (size - 1) div batchSize
+      left = size - chunks * batchSize
+
+    for i in 0 ..< chunks:
+      let readBytes = getRandomImpl(addr dest[result], batchSize)
+      if readBytes < 0:
+        return readBytes
+      inc(result, batchSize)
+
+    result = getRandomImpl(addr dest[result], left)
+
+when defined(js):
+  import std/private/jsutils
+
+  when defined(nodejs):
+    {.emit: "const _nim_nodejs_crypto = require('crypto');".}
+
+    proc randomFillSync(p: Uint8Array) {.importjs: "_nim_nodejs_crypto.randomFillSync(#)".}
+
+    template urandomImpl(result: var int, dest: var openArray[byte]) =
+      let size = dest.len
+      if size == 0:
+        return
+
+      var src = newUint8Array(size)
+      randomFillSync(src)
+      for i in 0 ..< size:
+        dest[i] = src[i]
+
+  else:
+    proc getRandomValues(p: Uint8Array) {.importjs: "window.crypto.getRandomValues(#)".}
+      # The requested length of `p` must not be more than 65536.
+
+    proc assign(dest: var openArray[byte], src: Uint8Array, base: int, size: int) =
+      getRandomValues(src)
+      for j in 0 ..< size:
+        dest[base + j] = src[j]
+
+    template urandomImpl(result: var int, dest: var openArray[byte]) =
+      let size = dest.len
+      if size == 0:
+        return
+
+      if size <= batchSize:
+        var src = newUint8Array(size)
+        assign(dest, src, 0, size)
+        return
+
+      let
+        chunks = (size - 1) div batchSize
+        left = size - chunks * batchSize
+
+      var srcArray = newUint8Array(batchSize)
+      for i in 0 ..< chunks:
+        assign(dest, srcArray, result, batchSize)
+        inc(result, batchSize)
+
+      var leftArray = newUint8Array(left)
+      assign(dest, leftArray, result, left)
+
+elif defined(windows):
+  type
+    PVOID = pointer
+    BCRYPT_ALG_HANDLE = PVOID
+    PUCHAR = ptr uint8
+    NTSTATUS = clong
+    ULONG = culong
+
+  const
+    STATUS_SUCCESS = 0x00000000
+    BCRYPT_USE_SYSTEM_PREFERRED_RNG = 0x00000002
+
+  proc bCryptGenRandom(
+    hAlgorithm: BCRYPT_ALG_HANDLE,
+    pbBuffer: PUCHAR,
+    cbBuffer: ULONG,
+    dwFlags: ULONG
+  ): NTSTATUS {.stdcall, importc: "BCryptGenRandom", dynlib: "Bcrypt.dll".}
+
+
+  proc randomBytes(pbBuffer: pointer, cbBuffer: Natural): int {.inline.} =
+    bCryptGenRandom(nil, cast[PUCHAR](pbBuffer), ULONG(cbBuffer),
+                            BCRYPT_USE_SYSTEM_PREFERRED_RNG)
+
+  template urandomImpl(result: var int, dest: var openArray[byte]) =
+    let size = dest.len
+    if size == 0:
+      return
+
+    result = randomBytes(addr dest[0], size)
+
+elif defined(linux) and not defined(nimNoGetRandom) and not defined(emscripten):
+  when (NimMajor, NimMinor) >= (1, 4):
+    let SYS_getrandom {.importc: "SYS_getrandom", header: "<sys/syscall.h>".}: clong
+  else:
+    var SYS_getrandom {.importc: "SYS_getrandom", header: "<sys/syscall.h>".}: clong
+  const syscallHeader = """#include <unistd.h>
+#include <sys/syscall.h>"""
+
+  proc syscall(n: clong): clong {.
+      importc: "syscall", varargs, header: syscallHeader.}
+    #  When reading from the urandom source (GRND_RANDOM is not set),
+    #  getrandom() will block until the entropy pool has been
+    #  initialized (unless the GRND_NONBLOCK flag was specified).  If a
+    #  request is made to read a large number of bytes (more than 256),
+    #  getrandom() will block until those bytes have been generated and
+    #  transferred from kernel memory to buf.
+
+  template urandomImpl(result: var int, dest: var openArray[byte]) =
+    let size = dest.len
+    if size == 0:
+      return
+
+    while result < size:
+      let readBytes = syscall(SYS_getrandom, addr dest[result], cint(size - result), 0).int
+      if readBytes == 0:
+        raiseAssert "unreachable"
+      elif readBytes > 0:
+        inc(result, readBytes)
+      else:
+        if osLastError().cint in [EINTR, EAGAIN]: discard
+        else:
+          result = -1
+          break
+
+elif defined(openbsd):
+  proc getentropy(p: pointer, size: cint): cint {.importc: "getentropy", header: "<unistd.h>".}
+    # Fills a buffer with high-quality entropy,
+    # which can be used as input for process-context pseudorandom generators like `arc4random`.
+    # The maximum buffer size permitted is 256 bytes.
+
+  proc getRandomImpl(p: pointer, size: int): int {.inline.} =
+    result = getentropy(p, cint(size)).int
+
+elif defined(zephyr):
+  proc sys_csrand_get(dst: pointer, length: csize_t): cint {.importc: "sys_csrand_get", header: "<random/rand32.h>".}
+    # Fill the destination buffer with cryptographically secure
+    # random data values
+    #
+
+  proc getRandomImpl(p: pointer, size: int): int {.inline.} =
+    # 0 if success, -EIO if entropy reseed error
+    result = sys_csrand_get(p, csize_t(size)).int
+
+elif defined(freebsd):
+  type cssize_t {.importc: "ssize_t", header: "<sys/types.h>".} = int
+
+  proc getrandom(p: pointer, size: csize_t, flags: cuint): cssize_t {.importc: "getrandom", header: "<sys/random.h>".}
+    # Upon successful completion, the number of bytes which were actually read
+    # is returned. For requests larger than 256 bytes, this can be fewer bytes
+    # than were requested. Otherwise, -1 is returned and the global variable
+    # errno is set to indicate the error.
+
+  proc getRandomImpl(p: pointer, size: int): int {.inline.} =
+    result = getrandom(p, csize_t(size), 0)
+
+elif defined(ios) or defined(macosx):
+  {.passl: "-framework Security".}
+
+  const errSecSuccess = 0 ## No error.
+
+  type
+    SecRandom {.importc: "struct __SecRandom".} = object
+
+    SecRandomRef = ptr SecRandom
+      ## An abstract Core Foundation-type object containing information about a random number generator.
+
+  proc secRandomCopyBytes(
+    rnd: SecRandomRef, count: csize_t, bytes: pointer
+  ): cint {.importc: "SecRandomCopyBytes", header: "<Security/SecRandom.h>".}
+    ## https://developer.apple.com/documentation/security/1399291-secrandomcopybytes
+
+  template urandomImpl(result: var int, dest: var openArray[byte]) =
+    let size = dest.len
+    if size == 0:
+      return
+
+    result = secRandomCopyBytes(nil, csize_t(size), addr dest[0])
+
+else:
+  template urandomImpl(result: var int, dest: var openArray[byte]) =
+    let size = dest.len
+    if size == 0:
+      return
+
+    # see: https://www.2uo.de/myths-about-urandom/ which justifies using urandom instead of random
+    let fd = posix.open("/dev/urandom", O_RDONLY)
+
+    if fd < 0:
+      result = -1
+    else:
+      try:
+        var stat: Stat
+        if fstat(fd, stat) != -1 and S_ISCHR(stat.st_mode):
+          let
+            chunks = (size - 1) div batchSize
+            left = size - chunks * batchSize
+
+          for i in 0 ..< chunks:
+            let readBytes = posix.read(fd, addr dest[result], batchSize)
+            if readBytes < 0:
+              return readBytes
+            inc(result, batchSize)
+
+          result = posix.read(fd, addr dest[result], left)
+        else:
+          result = -1
+      finally:
+        discard posix.close(fd)
+
+proc urandomInternalImpl(dest: var openArray[byte]): int {.inline.} =
+  when batchImplOS:
+    batchImpl(result, dest, getRandomImpl)
+  else:
+    urandomImpl(result, dest)
+
+proc urandom*(dest: var openArray[byte]): bool =
+  ## Fills `dest` with random bytes suitable for cryptographic use.
+  ## If the call succeeds, returns `true`.
+  ##
+  ## If `dest` is empty, `urandom` immediately returns success,
+  ## without calling the underlying operating system API.
+  ##
+  ## .. warning:: The code hasn't been audited by cryptography experts and
+  ##   is provided as-is without guarantees. Use at your own risks. For production
+  ##   systems we advise you to request an external audit.
+  result = true
+  when defined(js): discard urandomInternalImpl(dest)
+  else:
+    let ret = urandomInternalImpl(dest)
+    when defined(windows):
+      if ret != STATUS_SUCCESS:
+        result = false
+    else:
+      if ret < 0:
+        result = false
+
+proc urandom*(size: Natural): seq[byte] {.inline.} =
+  ## Returns random bytes suitable for cryptographic use.
+  ##
+  ## .. warning:: The code hasn't been audited by cryptography experts and
+  ##   is provided as-is without guarantees. Use at your own risks. For production
+  ##   systems we advise you to request an external audit.
+  result = newSeq[byte](size)
+  when defined(js): discard urandomInternalImpl(result)
+  else:
+    if not urandom(result):
+      raiseOSError(osLastError())
diff --git a/lib/std/tasks.nim b/lib/std/tasks.nim
new file mode 100644
index 000000000..7e59747f5
--- /dev/null
+++ b/lib/std/tasks.nim
@@ -0,0 +1,312 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2021 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module provides basic primitives for creating parallel programs.
+## A `Task` should be only owned by a single Thread, it cannot be shared by threads.
+
+import std/[macros, isolation, typetraits]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+export isolation
+
+
+when compileOption("threads"):
+  from std/effecttraits import isGcSafe
+
+
+#
+# proc hello(a: int, b: string) =
+#   echo $a & b
+#
+# let literal = "Nim"
+# let t = toTask(hello(521, literal))
+#
+#
+# is roughly converted to
+#
+# type
+#   ScratchObj_369098780 = object
+#     a: int
+#     b: string
+#
+# let scratch_369098762 = cast[ptr ScratchObj_369098780](c_calloc(csize_t 1,
+#     csize_t sizeof(ScratchObj_369098780)))
+# if scratch_369098762.isNil:
+#   raise newException(OutOfMemDefect, "Could not allocate memory")
+# block:
+#   var isolate_369098776 = isolate(521)
+#   scratch_369098762.a = extract(isolate_369098776)
+#   var isolate_369098778 = isolate(literal)
+#   scratch_369098762.b = extract(isolate_369098778)
+# proc hello_369098781(args`gensym3: pointer) {.nimcall.} =
+#   let objTemp_369098775 = cast[ptr ScratchObj_369098780](args`gensym3)
+#   let :tmp_369098777 = objTemp_369098775.a
+#   let :tmp_369098779 = objTemp_369098775.b
+#   hello(a = :tmp_369098777, b = :tmp_369098779)
+#
+# proc destroyScratch_369098782(args`gensym3: pointer) {.nimcall.} =
+#   let obj_369098783 = cast[ptr ScratchObj_369098780](args`gensym3)
+#   =destroy(obj_369098783[])
+# let t = Task(callback: hello_369098781, args: scratch_369098762, destroy: destroyScratch_369098782)
+#
+
+
+type
+  Task* = object ## `Task` contains the callback and its arguments.
+    callback: proc (args, res: pointer) {.nimcall, gcsafe.}
+    args: pointer
+    destroy: proc (args: pointer) {.nimcall, gcsafe.}
+
+
+proc `=copy`*(x: var Task, y: Task) {.error.}
+
+const arcLike = defined(gcArc) or defined(gcAtomicArc) or defined(gcOrc)
+when defined(nimAllowNonVarDestructor) and arcLike:
+  proc `=destroy`*(t: Task) {.inline, gcsafe.} =
+    ## Frees the resources allocated for a `Task`.
+    if t.args != nil:
+      if t.destroy != nil:
+        t.destroy(t.args)
+      deallocShared(t.args)
+else:
+  proc `=destroy`*(t: var Task) {.inline, gcsafe.} =
+    ## Frees the resources allocated for a `Task`.
+    if t.args != nil:
+      if t.destroy != nil:
+        t.destroy(t.args)
+      deallocShared(t.args)
+
+proc invoke*(task: Task; res: pointer = nil) {.inline, gcsafe.} =
+  ## Invokes the `task`.
+  assert task.callback != nil
+  task.callback(task.args, res)
+
+template checkIsolate(scratchAssignList: seq[NimNode], procParam, scratchDotExpr: NimNode) =
+  # block:
+  #   var isoTempA = isolate(521)
+  #   scratch.a = extract(isolateA)
+  #   var isoTempB = isolate(literal)
+  #   scratch.b = extract(isolateB)
+  let isolatedTemp = genSym(nskTemp, "isoTemp")
+  scratchAssignList.add newVarStmt(isolatedTemp, newCall(newIdentNode("isolate"), procParam))
+  scratchAssignList.add newAssignment(scratchDotExpr,
+      newCall(newIdentNode("extract"), isolatedTemp))
+
+template addAllNode(assignParam: NimNode, procParam: NimNode) =
+  let scratchDotExpr = newDotExpr(scratchIdent, formalParams[i][0])
+
+  checkIsolate(scratchAssignList, procParam, scratchDotExpr)
+
+  let tempNode = genSym(kind = nskTemp, ident = formalParams[i][0].strVal)
+  callNode.add nnkExprEqExpr.newTree(formalParams[i][0], tempNode)
+  tempAssignList.add newLetStmt(tempNode, newDotExpr(objTemp, formalParams[i][0]))
+  scratchRecList.add newIdentDefs(newIdentNode(formalParams[i][0].strVal), assignParam)
+
+proc analyseRootSym(s: NimNode): NimNode =
+  result = s
+  while true:
+    case result.kind
+    of nnkBracketExpr, nnkDerefExpr, nnkHiddenDeref,
+        nnkAddr, nnkHiddenAddr,
+        nnkObjDownConv, nnkObjUpConv:
+      result = result[0]
+    of nnkDotExpr, nnkCheckedFieldExpr, nnkHiddenStdConv, nnkHiddenSubConv:
+      result = result[1]
+    else:
+      break
+
+macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkCallStrLit}): Task =
+  ## Converts the call and its arguments to `Task`.
+  runnableExamples:
+    proc hello(a: int) = echo a
+
+    let b = toTask hello(13)
+    assert b is Task
+
+  let retType = getTypeInst(e)
+  let returnsVoid = retType.typeKind == ntyVoid
+
+  let rootSym = analyseRootSym(e[0])
+  expectKind rootSym, nnkSym
+
+  when compileOption("threads"):
+    if not isGcSafe(rootSym):
+      error("'toTask' takes a GC safe call expression", e)
+
+  if hasClosure(rootSym):
+    error("closure call is not allowed", e)
+
+  if e.len > 1:
+    let scratchIdent = genSym(kind = nskTemp, ident = "scratch")
+    let impl = e[0].getTypeInst
+
+    when defined(nimTasksDebug):
+      echo impl.treeRepr
+      echo e.treeRepr
+    let formalParams = impl[0]
+
+    var
+      scratchRecList = newNimNode(nnkRecList)
+      scratchAssignList: seq[NimNode]
+      tempAssignList: seq[NimNode]
+      callNode: seq[NimNode]
+
+    let
+      objTemp = genSym(nskTemp, ident = "objTemp")
+
+    for i in 1 ..< formalParams.len:
+      var param = formalParams[i][1]
+
+      if param.kind == nnkBracketExpr and param[0].eqIdent("sink"):
+        param = param[0]
+
+      if param.typeKind in {ntyExpr, ntyStmt}:
+        error("'toTask'ed function cannot have a 'typed' or 'untyped' parameter", e)
+
+      case param.kind
+      of nnkVarTy:
+        error("'toTask'ed function cannot have a 'var' parameter", e)
+      of nnkBracketExpr:
+        if param[0].typeKind == ntyTypeDesc:
+          callNode.add nnkExprEqExpr.newTree(formalParams[i][0], e[i])
+        elif param[0].typeKind in {ntyVarargs, ntyOpenArray}:
+          if param[1].typeKind in {ntyExpr, ntyStmt}:
+            error("'toTask'ed function cannot have a 'typed' or 'untyped' parameter", e)
+          let
+            seqType = nnkBracketExpr.newTree(newIdentNode("seq"), param[1])
+            seqCallNode = newCall("@", e[i])
+          addAllNode(seqType, seqCallNode)
+        else:
+          addAllNode(param, e[i])
+      of nnkBracket, nnkObjConstr:
+        # passing by static parameters
+        # so we pass them directly instead of passing by scratchObj
+        callNode.add nnkExprEqExpr.newTree(formalParams[i][0], e[i])
+      of nnkSym, nnkPtrTy, nnkProcTy, nnkTupleConstr:
+        addAllNode(param, e[i])
+      of nnkCharLit..nnkNilLit:
+        callNode.add nnkExprEqExpr.newTree(formalParams[i][0], e[i])
+      else:
+        error("'toTask'ed function cannot have a parameter of " & $param.kind & " kind", e)
+
+    let scratchObjType = genSym(kind = nskType, ident = "ScratchObj")
+    let scratchObj = nnkTypeSection.newTree(
+                      nnkTypeDef.newTree(
+                        scratchObjType,
+                        newEmptyNode(),
+                        nnkObjectTy.newTree(
+                          newEmptyNode(),
+                          newEmptyNode(),
+                          scratchRecList
+                        )
+                      )
+                    )
+
+
+    let scratchObjPtrType = quote do:
+      cast[ptr `scratchObjType`](allocShared0(sizeof(`scratchObjType`)))
+
+    let scratchLetSection = newLetStmt(scratchIdent, scratchObjPtrType)
+
+    var stmtList = newStmtList()
+    stmtList.add(scratchObj)
+    stmtList.add(scratchLetSection)
+    stmtList.add(nnkBlockStmt.newTree(newEmptyNode(), newStmtList(scratchAssignList)))
+
+    var functionStmtList = newStmtList()
+    let funcCall = newCall(e[0], callNode)
+    functionStmtList.add tempAssignList
+
+    let funcName = genSym(nskProc, rootSym.strVal)
+    let destroyName = genSym(nskProc, "destroyScratch")
+    let objTemp2 = genSym(ident = "obj")
+    let tempNode = quote("@") do:
+        `=destroy`(@objTemp2[])
+
+    var funcDecl: NimNode
+    if returnsVoid:
+      funcDecl = quote do:
+        proc `funcName`(args, res: pointer) {.gcsafe, nimcall.} =
+          let `objTemp` = cast[ptr `scratchObjType`](args)
+          `functionStmtList`
+          `funcCall`
+    else:
+      funcDecl = quote do:
+        proc `funcName`(args, res: pointer) {.gcsafe, nimcall.} =
+          let `objTemp` = cast[ptr `scratchObjType`](args)
+          `functionStmtList`
+          cast[ptr `retType`](res)[] = `funcCall`
+
+    result = quote do:
+      `stmtList`
+
+      `funcDecl`
+
+      proc `destroyName`(args: pointer) {.gcsafe, nimcall.} =
+        let `objTemp2` = cast[ptr `scratchObjType`](args)
+        `tempNode`
+
+      Task(callback: `funcName`, args: `scratchIdent`, destroy: `destroyName`)
+  else:
+    let funcCall = newCall(e[0])
+    let funcName = genSym(nskProc, rootSym.strVal)
+
+    if returnsVoid:
+      result = quote do:
+        proc `funcName`(args, res: pointer) {.gcsafe, nimcall.} =
+          `funcCall`
+
+        Task(callback: `funcName`, args: nil)
+    else:
+      result = quote do:
+        proc `funcName`(args, res: pointer) {.gcsafe, nimcall.} =
+          cast[ptr `retType`](res)[] = `funcCall`
+
+        Task(callback: `funcName`, args: nil)
+
+
+  when defined(nimTasksDebug):
+    echo result.repr
+
+runnableExamples:
+  block:
+    var num = 0
+    proc hello(a: int) = inc num, a
+
+    let b = toTask hello(13)
+    b.invoke()
+    assert num == 13
+    # A task can be invoked multiple times
+    b.invoke()
+    assert num == 26
+
+  block:
+    type
+      Runnable = ref object
+        data: int
+
+    var data: int
+    proc hello(a: Runnable) {.nimcall.} =
+      a.data += 2
+      data = a.data
+
+
+    when false:
+      # the parameters of call must be isolated.
+      let x = Runnable(data: 12)
+      let b = toTask hello(x) # error ----> expression cannot be isolated: x
+      b.invoke()
+
+    let b = toTask(hello(Runnable(data: 12)))
+    b.invoke()
+    assert data == 14
+    b.invoke()
+    assert data == 16
diff --git a/lib/std/tempfiles.nim b/lib/std/tempfiles.nim
new file mode 100644
index 000000000..539305bde
--- /dev/null
+++ b/lib/std/tempfiles.nim
@@ -0,0 +1,192 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2021 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module creates temporary files and directories.
+##
+## Experimental API, subject to change.
+
+#[
+See also:
+* `GetTempFileName` (on windows), refs https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettempfilenamea
+* `mkstemp` (posix), refs https://man7.org/linux/man-pages/man3/mkstemp.3.html
+]#
+
+import std / [os, random]
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
+
+const
+  maxRetry = 10000
+  letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+  nimTempPathLength {.intdefine.} = 8
+
+
+when defined(windows):
+  import std/winlean
+  when defined(nimPreviewSlimSystem):
+    import std/widestrs
+
+  var O_RDWR {.importc: "_O_RDWR", header: "<fcntl.h>".}: cint
+
+  proc c_fdopen(
+    filehandle: cint,
+    mode: cstring
+  ): File {.importc: "_fdopen",header: "<stdio.h>".}
+
+  proc open_osfhandle(osh: Handle, mode: cint): cint {.
+    importc: "_open_osfhandle", header: "<io.h>".}
+
+  proc close_osfandle(fd: cint): cint {.
+    importc: "_close", header: "<io.h>".}
+else:
+  import std/posix
+
+  proc c_fdopen(
+    filehandle: cint,
+    mode: cstring
+  ): File {.importc: "fdopen",header: "<stdio.h>".}
+
+
+proc safeOpen(filename: string): File =
+  ## Open files exclusively; returns `nil` if the file already exists.
+  # xxx this should be clarified; it doesn't in particular prevent other processes
+  # from opening the file, at least currently.
+  when defined(windows):
+    let dwShareMode = FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE
+    let dwCreation = CREATE_NEW
+    let dwFlags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL
+    let handle = createFileW(newWideCString(filename), GENERIC_READ or GENERIC_WRITE, dwShareMode,
+                              nil, dwCreation, dwFlags, Handle(0))
+
+    if handle == INVALID_HANDLE_VALUE:
+      if getLastError() == ERROR_FILE_EXISTS:
+        return nil
+      else:
+        raiseOSError(osLastError(), filename)
+
+    let fileHandle = open_osfhandle(handle, O_RDWR)
+    if fileHandle == -1:
+      discard closeHandle(handle)
+      raiseOSError(osLastError(), filename)
+
+    result = c_fdopen(fileHandle, "w+")
+    if result == nil:
+      discard close_osfandle(fileHandle)
+      raiseOSError(osLastError(), filename)
+  else:
+    # xxx we need a `proc toMode(a: FilePermission): Mode`, possibly by
+    # exposing fusion/filepermissions.fromFilePermissions to stdlib; then we need
+    # to expose a `perm` param so users can customize this (e.g. the temp file may
+    # need execute permissions), and figure out how to make the API cross platform.
+    let mode = Mode(S_IRUSR or S_IWUSR)
+    let flags = posix.O_RDWR or posix.O_CREAT or posix.O_EXCL
+    let fileHandle = posix.open(filename, flags, mode)
+    if fileHandle == -1:
+      if errno == EEXIST:
+        # xxx `getLastError()` should be defined on posix too and resolve to `errno`?
+        return nil
+      else:
+        raiseOSError(osLastError(), filename)
+
+    result = c_fdopen(fileHandle, "w+")
+    if result == nil:
+      discard posix.close(fileHandle) # TODO handles failure when closing file
+      raiseOSError(osLastError(), filename)
+
+
+type
+  NimTempPathState = object
+    state: Rand
+    isInit: bool
+
+var nimTempPathState {.threadvar.}: NimTempPathState
+
+template randomPathName(length: Natural): string =
+  var res = newString(length)
+  if not nimTempPathState.isInit:
+    nimTempPathState.isInit = true
+    nimTempPathState.state = initRand()
+
+  for i in 0 ..< length:
+    res[i] = nimTempPathState.state.sample(letters)
+  res
+
+proc getTempDirImpl(dir: string): string {.inline.} =
+  result = dir
+  if result.len == 0:
+    result = getTempDir()
+
+proc genTempPath*(prefix, suffix: string, dir = ""): string =
+  ## Generates a path name in `dir`.
+  ##
+  ## The path begins with `prefix` and ends with `suffix`.
+  ##
+  ## .. note:: `dir` must exist (empty `dir` will resolve to `getTempDir <os.html#getTempDir>`_).
+  let dir = getTempDirImpl(dir)
+  result = dir / (prefix & randomPathName(nimTempPathLength) & suffix)
+
+proc createTempFile*(prefix, suffix: string, dir = ""): tuple[cfile: File, path: string] =
+  ## Creates a new temporary file in the directory `dir`.
+  ##
+  ## This generates a path name using `genTempPath(prefix, suffix, dir)` and
+  ## returns a file handle to an open file and the path of that file, possibly after
+  ## retrying to ensure it doesn't already exist.
+  ##
+  ## If failing to create a temporary file, `OSError` will be raised.
+  ##
+  ## .. note:: It is the caller's responsibility to close `result.cfile` and
+  ##    remove `result.file` when no longer needed.
+  ## .. note:: `dir` must exist (empty `dir` will resolve to `getTempDir <os.html#getTempDir>`_).
+  runnableExamples:
+    import std/os
+    doAssertRaises(OSError): discard createTempFile("", "", "nonexistent")
+    let (cfile, path) = createTempFile("tmpprefix_", "_end.tmp")
+    # path looks like: getTempDir() / "tmpprefix_FDCIRZA0_end.tmp"
+    cfile.write "foo"
+    cfile.setFilePos 0
+    assert readAll(cfile) == "foo"
+    close cfile
+    assert readFile(path) == "foo"
+    removeFile(path)
+  # xxx why does above work without `cfile.flushFile` ?
+  let dir = getTempDirImpl(dir)
+  for i in 0 ..< maxRetry:
+    result.path = genTempPath(prefix, suffix, dir)
+    result.cfile = safeOpen(result.path)
+    if result.cfile != nil:
+      return
+
+  raise newException(OSError, "Failed to create a temporary file under directory " & dir)
+
+proc createTempDir*(prefix, suffix: string, dir = ""): string =
+  ## Creates a new temporary directory in the directory `dir`.
+  ##
+  ## This generates a dir name using `genTempPath(prefix, suffix, dir)`, creates
+  ## the directory and returns it, possibly after retrying to ensure it doesn't
+  ## already exist.
+  ##
+  ## If failing to create a temporary directory, `OSError` will be raised.
+  ##
+  ## .. note:: It is the caller's responsibility to remove the directory when no longer needed.
+  ## .. note:: `dir` must exist (empty `dir` will resolve to `getTempDir <os.html#getTempDir>`_).
+  runnableExamples:
+    import std/os
+    doAssertRaises(OSError): discard createTempDir("", "", "nonexistent")
+    let dir = createTempDir("tmpprefix_", "_end")
+    # dir looks like: getTempDir() / "tmpprefix_YEl9VuVj_end"
+    assert dirExists(dir)
+    removeDir(dir)
+  let dir = getTempDirImpl(dir)
+  for i in 0 ..< maxRetry:
+    result = genTempPath(prefix, suffix, dir)
+    if not existsOrCreateDir(result):
+      return
+
+  raise newException(OSError, "Failed to create a temporary directory under directory " & dir)
diff --git a/lib/std/time_t.nim b/lib/std/time_t.nim
new file mode 100644
index 000000000..de051b135
--- /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 clong
+  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 std/posix
+  export posix.Time
\ No newline at end of file
diff --git a/lib/std/typedthreads.nim b/lib/std/typedthreads.nim
new file mode 100644
index 000000000..7b0b81968
--- /dev/null
+++ b/lib/std/typedthreads.nim
@@ -0,0 +1,305 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+##[
+Thread support for Nim. Threads allow multiple functions to execute concurrently.
+ 
+In Nim, threads are a low-level construct and using a library like `malebolgia`, `taskpools` or `weave` is recommended.
+ 
+When creating a thread, you can pass arguments to it. As Nim's garbage collector does not use atomic references, sharing
+`ref` and other variables managed by the garbage collector between threads is not supported.
+Use global variables to do so, or pointers.
+ 
+Memory allocated using [`sharedAlloc`](./system.html#allocShared.t%2CNatural) can be used and shared between threads.
+
+To communicate between threads, consider using [channels](./system.html#Channel)
+
+Examples
+========
+
+```Nim
+import std/locks
+
+var
+  thr: array[0..4, Thread[tuple[a,b: int]]]
+  L: Lock
+
+proc threadFunc(interval: tuple[a,b: int]) {.thread.} =
+  for i in interval.a..interval.b:
+    acquire(L) # lock stdout
+    echo i
+    release(L)
+
+initLock(L)
+
+for i in 0..high(thr):
+  createThread(thr[i], threadFunc, (i*10, i*10+5))
+joinThreads(thr)
+
+deinitLock(L)
+```
+ 
+When using a memory management strategy that supports shared heaps like `arc` or `boehm`,
+you can pass pointer to threads and share memory between them, but the memory must outlive the thread.
+The default memory management strategy, `orc`, supports this.
+The example below is **not valid** for memory management strategies that use local heaps like `refc`!
+
+```Nim
+import locks
+ 
+var l: Lock
+ 
+proc threadFunc(obj: ptr seq[int]) {.thread.} =
+  withLock l:
+    for i in 0..<100:
+      obj[].add(obj[].len * obj[].len)
+ 
+proc threadHandler() =
+  var thr: array[0..4, Thread[ptr seq[int]]]
+  var s = newSeq[int]()
+    
+  for i in 0..high(thr):
+    createThread(thr[i], threadFunc, s.addr)
+  joinThreads(thr)
+  echo s
+ 
+initLock(l)
+threadHandler()
+deinitLock(l)
+```
+]##
+
+
+import std/private/[threadtypes]
+export Thread
+
+import system/ansi_c
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+when defined(genode):
+  import genode/env
+
+when hostOS == "any":
+  {.error: "Threads not implemented for os:any. Please compile with --threads:off.".}
+
+when hasAllocStack or defined(zephyr) or defined(freertos) or defined(nuttx) or
+    defined(cpu16) or defined(cpu8):
+  const
+    nimThreadStackSize {.intdefine.} = 8192
+    nimThreadStackGuard {.intdefine.} = 128
+
+    StackGuardSize = nimThreadStackGuard
+    ThreadStackSize = nimThreadStackSize - nimThreadStackGuard
+else:
+  const
+    StackGuardSize = 4096
+    ThreadStackMask =
+      when defined(genode):
+        1024*64*sizeof(int)-1
+      else:
+        1024*256*sizeof(int)-1
+
+    ThreadStackSize = ThreadStackMask+1 - StackGuardSize
+
+
+when defined(gcDestructors):
+  proc allocThreadStorage(size: int): pointer =
+    result = c_malloc(csize_t size)
+    zeroMem(result, size)
+else:
+  template allocThreadStorage(size: untyped): untyped = allocShared0(size)
+
+#const globalsSlot = ThreadVarSlot(0)
+#sysAssert checkSlot.int == globalsSlot.int
+
+# Zephyr doesn't include this properly without some help
+when defined(zephyr):
+  {.emit: """/*INCLUDESECTION*/
+  #include <pthread.h>
+  """.}
+
+
+# We jump through some hops here to ensure that Nim thread procs can have
+# the Nim calling convention. This is needed because thread procs are
+# ``stdcall`` on Windows and ``noconv`` on UNIX. Alternative would be to just
+# use ``stdcall`` since it is mapped to ``noconv`` on UNIX anyway.
+
+
+
+{.push stack_trace:off.}
+when defined(windows):
+  proc threadProcWrapper[TArg](closure: pointer): int32 {.stdcall.} =
+    nimThreadProcWrapperBody(closure)
+    # implicitly return 0
+elif defined(genode):
+  proc threadProcWrapper[TArg](closure: pointer) {.noconv.} =
+    nimThreadProcWrapperBody(closure)
+else:
+  proc threadProcWrapper[TArg](closure: pointer): pointer {.noconv.} =
+    nimThreadProcWrapperBody(closure)
+{.pop.}
+
+proc running*[TArg](t: Thread[TArg]): bool {.inline.} =
+  ## Returns true if `t` is running.
+  result = t.dataFn != nil
+
+proc handle*[TArg](t: Thread[TArg]): SysThread {.inline.} =
+  ## Returns the thread handle of `t`.
+  result = t.sys
+
+when hostOS == "windows":
+  const MAXIMUM_WAIT_OBJECTS = 64
+
+  proc joinThread*[TArg](t: Thread[TArg]) {.inline.} =
+    ## Waits for the thread `t` to finish.
+    discard waitForSingleObject(t.sys, -1'i32)
+
+  proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
+    ## Waits for every thread in `t` to finish.
+    var a: array[MAXIMUM_WAIT_OBJECTS, SysThread]
+    var k = 0
+    while k < len(t):
+      var count = min(len(t) - k, MAXIMUM_WAIT_OBJECTS)
+      for i in 0..(count - 1): a[i] = t[i + k].sys
+      discard waitForMultipleObjects(int32(count),
+                                     cast[ptr SysThread](addr(a)), 1, -1)
+      inc(k, MAXIMUM_WAIT_OBJECTS)
+
+elif defined(genode):
+  proc joinThread*[TArg](t: Thread[TArg]) {.importcpp.}
+    ## Waits for the thread `t` to finish.
+
+  proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
+    ## Waits for every thread in `t` to finish.
+    for i in 0..t.high: joinThread(t[i])
+
+else:
+  proc joinThread*[TArg](t: Thread[TArg]) {.inline.} =
+    ## Waits for the thread `t` to finish.
+    discard pthread_join(t.sys, nil)
+
+  proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
+    ## Waits for every thread in `t` to finish.
+    for i in 0..t.high: joinThread(t[i])
+
+when false:
+  # XXX a thread should really release its heap here somehow:
+  proc destroyThread*[TArg](t: var Thread[TArg]) =
+    ## Forces the thread `t` to terminate. This is potentially dangerous if
+    ## you don't have full control over `t` and its acquired resources.
+    when hostOS == "windows":
+      discard TerminateThread(t.sys, 1'i32)
+    else:
+      discard pthread_cancel(t.sys)
+    when declared(registerThread): unregisterThread(addr(t))
+    t.dataFn = nil
+    ## if thread `t` already exited, `t.core` will be `null`.
+    if not isNil(t.core):
+      deallocThreadStorage(t.core)
+      t.core = nil
+
+when hostOS == "windows":
+  proc createThread*[TArg](t: var Thread[TArg],
+                           tp: proc (arg: TArg) {.thread, nimcall.},
+                           param: TArg) =
+    ## Creates a new thread `t` and starts its execution.
+    ##
+    ## Entry point is the proc `tp`.
+    ## `param` is passed to `tp`. `TArg` can be `void` if you
+    ## don't need to pass any data to the thread.
+    t.core = cast[PGcThread](allocThreadStorage(sizeof(GcThread)))
+
+    when TArg isnot void: t.data = param
+    t.dataFn = tp
+    when hasSharedHeap: t.core.stackSize = ThreadStackSize
+    var dummyThreadId: int32
+    t.sys = createThread(nil, ThreadStackSize, threadProcWrapper[TArg],
+                         addr(t), 0'i32, dummyThreadId)
+    if t.sys <= 0:
+      raise newException(ResourceExhaustedError, "cannot create thread")
+
+  proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) =
+    ## Pins a thread to a `CPU`:idx:.
+    ##
+    ## In other words sets a thread's `affinity`:idx:.
+    ## If you don't know what this means, you shouldn't use this proc.
+    setThreadAffinityMask(t.sys, uint(1 shl cpu))
+
+elif defined(genode):
+  var affinityOffset: cuint = 1
+    ## CPU affinity offset for next thread, safe to roll-over.
+
+  proc createThread*[TArg](t: var Thread[TArg],
+                           tp: proc (arg: TArg) {.thread, nimcall.},
+                           param: TArg) =
+    t.core = cast[PGcThread](allocThreadStorage(sizeof(GcThread)))
+
+    when TArg isnot void: t.data = param
+    t.dataFn = tp
+    when hasSharedHeap: t.stackSize = ThreadStackSize
+    t.sys.initThread(
+      runtimeEnv,
+      ThreadStackSize.culonglong,
+      threadProcWrapper[TArg], addr(t), affinityOffset)
+    inc affinityOffset
+
+  proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) =
+    {.hint: "cannot change Genode thread CPU affinity after initialization".}
+    discard
+
+else:
+  proc createThread*[TArg](t: var Thread[TArg],
+                           tp: proc (arg: TArg) {.thread, nimcall.},
+                           param: TArg) =
+    ## Creates a new thread `t` and starts its execution.
+    ##
+    ## Entry point is the proc `tp`. `param` is passed to `tp`.
+    ## `TArg` can be `void` if you
+    ## don't need to pass any data to the thread.
+    t.core = cast[PGcThread](allocThreadStorage(sizeof(GcThread)))
+
+    when TArg isnot void: t.data = param
+    t.dataFn = tp
+    when hasSharedHeap: t.core.stackSize = ThreadStackSize
+    var a {.noinit.}: Pthread_attr
+    doAssert pthread_attr_init(a) == 0
+    when hasAllocStack:
+      var
+        rawstk = allocThreadStorage(ThreadStackSize + StackGuardSize)
+        stk = cast[pointer](cast[uint](rawstk) + StackGuardSize)
+      let setstacksizeResult = pthread_attr_setstack(addr a, stk, ThreadStackSize)
+      t.rawStack = rawstk
+    else:
+      let setstacksizeResult = pthread_attr_setstacksize(a, ThreadStackSize)
+
+    when not defined(ios):
+      # This fails on iOS
+      doAssert(setstacksizeResult == 0)
+    if pthread_create(t.sys, a, threadProcWrapper[TArg], addr(t)) != 0:
+      raise newException(ResourceExhaustedError, "cannot create thread")
+    doAssert pthread_attr_destroy(a) == 0
+
+  proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) =
+    ## Pins a thread to a `CPU`:idx:.
+    ##
+    ## In other words sets a thread's `affinity`:idx:.
+    ## If you don't know what this means, you shouldn't use this proc.
+    when not defined(macosx):
+      var s {.noinit.}: CpuSet
+      cpusetZero(s)
+      cpusetIncl(cpu.cint, s)
+      setAffinity(t.sys, csize_t(sizeof(s)), s)
+
+proc createThread*(t: var Thread[void], tp: proc () {.thread, nimcall.}) =
+  createThread[void](t, tp)
+
+when not defined(gcOrc):
+  include system/threadids
diff --git a/lib/std/varints.nim b/lib/std/varints.nim
index 483d5c96c..32fe2fffb 100644
--- a/lib/std/varints.nim
+++ b/lib/std/varints.nim
@@ -7,8 +7,10 @@
 #    distribution, for details about the copyright.
 #
 
-## Note this API is still experimental! A variable length integer
+## A variable length integer
 ## encoding implementation inspired by SQLite.
+##
+## Unstable API.
 
 const
   maxVarIntLen* = 9 ## the maximal number of bytes a varint can take
@@ -47,62 +49,62 @@ proc readVu64*(z: openArray[byte]; pResult: var uint64): int =
   return 9
 
 proc varintWrite32(z: var openArray[byte]; y: uint32) =
-  z[0] = uint8(y shr 24)
-  z[1] = uint8(y shr 16)
-  z[2] = uint8(y shr 8)
-  z[3] = uint8(y)
+  z[0] = cast[uint8](y shr 24)
+  z[1] = cast[uint8](y shr 16)
+  z[2] = cast[uint8](y shr 8)
+  z[3] = cast[uint8](y)
 
 proc writeVu64*(z: var openArray[byte], x: uint64): int =
   ## Write a varint into z. The buffer z must be at least 9 characters
   ## long to accommodate the largest possible varint. Returns the number of
   ## bytes used.
   if x <= 240:
-    z[0] = uint8 x
+    z[0] = cast[uint8](x)
     return 1
   if x <= 2287:
-    let y = uint32(x - 240)
-    z[0] = uint8(y shr 8 + 241)
-    z[1] = uint8(y and 255)
+    let y = cast[uint32](x - 240)
+    z[0] = cast[uint8](y shr 8 + 241)
+    z[1] = cast[uint8](y and 255)
     return 2
   if x <= 67823:
-    let y = uint32(x - 2288)
+    let y = cast[uint32](x - 2288)
     z[0] = 249
-    z[1] = uint8(y shr 8)
-    z[2] = uint8(y and 255)
+    z[1] = cast[uint8](y shr 8)
+    z[2] = cast[uint8](y and 255)
     return 3
-  let y = uint32 x
-  let w = uint32(x shr 32)
+  let y = cast[uint32](x)
+  let w = cast[uint32](x shr 32)
   if w == 0:
     if y <= 16777215:
       z[0] = 250
-      z[1] = uint8(y shr 16)
-      z[2] = uint8(y shr 8)
-      z[3] = uint8(y)
+      z[1] = cast[uint8](y shr 16)
+      z[2] = cast[uint8](y shr 8)
+      z[3] = cast[uint8](y)
       return 4
     z[0] = 251
-    varintWrite32(toOpenArray(z, 1, z.high-1), y)
+    varintWrite32(toOpenArray(z, 1, 4), y)
     return 5
   if w <= 255:
     z[0] = 252
-    z[1] = uint8 w
-    varintWrite32(toOpenArray(z, 2, z.high-2), y)
+    z[1] = cast[uint8](w)
+    varintWrite32(toOpenArray(z, 2, 5), y)
     return 6
   if w <= 65535:
     z[0] = 253
-    z[1] = uint8(w shr 8)
-    z[2] = uint8 w
-    varintWrite32(toOpenArray(z, 3, z.high-3), y)
+    z[1] = cast[uint8](w shr 8)
+    z[2] = cast[uint8](w)
+    varintWrite32(toOpenArray(z, 3, 6), y)
     return 7
   if w <= 16777215:
     z[0] = 254
-    z[1] = uint8(w shr 16)
-    z[2] = uint8(w shr 8)
-    z[3] = uint8 w
-    varintWrite32(toOpenArray(z, 4, z.high-4), y)
+    z[1] = cast[uint8](w shr 16)
+    z[2] = cast[uint8](w shr 8)
+    z[3] = cast[uint8](w)
+    varintWrite32(toOpenArray(z, 4, 7), y)
     return 8
   z[0] = 255
-  varintWrite32(toOpenArray(z, 1, z.high-1), w)
-  varintWrite32(toOpenArray(z, 5, z.high-5), y)
+  varintWrite32(toOpenArray(z, 1, 4), w)
+  varintWrite32(toOpenArray(z, 5, 8), y)
   return 9
 
 proc sar(a, b: int64): int64 =
@@ -117,36 +119,3 @@ proc encodeZigzag*(x: int64): uint64 {.inline.} =
 proc decodeZigzag*(x: uint64): int64 {.inline.} =
   let casted = cast[int64](x)
   result = (`shr`(casted, 1)) xor (-(casted and 1))
-
-when isMainModule:
-  #import random
-
-  var dest: array[50, byte]
-  var got: uint64
-
-  for test in [0xFFFF_FFFF_FFFFF_FFFFu64, 77u64, 0u64, 10_000_000u64, uint64(high(int64)),
-               uint64(high(int32)),uint64(low(int32)),uint64(low(int64))]:
-    let wrLen = writeVu64(dest, test)
-    let rdLen = readVu64(dest, got)
-    assert wrLen == rdLen
-    echo(if got == test: "YES" else: "NO")
-    echo "number is ", got
-
-    if encodeZigzag(decodeZigzag(test)) != test:
-      echo "Failure for ", test, " ", encodeZigzag(decodeZigzag(test)), " ", decodeZigzag(test)
-
-  for test in 0u64..300u64:
-    let wrLen = writeVu64(dest, test)
-    let rdLen = readVu64(dest, got)
-    assert wrLen == rdLen
-    if got != test:
-      echo "BUG! expected: ", test, " got: ", got, " z0: ", dest[0]
-
-  # check this also works for floats:
-  for test in [0.0, 0.1, 2.0, +Inf, Nan, NegInf]:
-    let t = cast[uint64](test)
-    let wrLenB = writeVu64(dest, t)
-    let rdLenB = readVu64(dest, got)
-    assert wrLenB == rdLenB
-    echo rdLenB
-    echo(if cast[float64](got) == test: "YES" else: "NO")
diff --git a/lib/std/vmutils.nim b/lib/std/vmutils.nim
new file mode 100644
index 000000000..e16912a3c
--- /dev/null
+++ b/lib/std/vmutils.nim
@@ -0,0 +1,11 @@
+##[
+Experimental API, subject to change.
+]##
+
+proc vmTrace*(on: bool) {.compileTime.} =
+  runnableExamples:
+    static: vmTrace(true)
+    proc fn =
+      var a = 1
+      vmTrace(false)
+    static: fn()
diff --git a/lib/std/widestrs.nim b/lib/std/widestrs.nim
new file mode 100644
index 000000000..2ddf80d14
--- /dev/null
+++ b/lib/std/widestrs.nim
@@ -0,0 +1,239 @@
+#
+#
+#            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 C/C++'s `wide strings`:idx:.
+
+#when not declared(ThisIsSystem):
+#  {.error: "You must not import this module explicitly".}
+
+type
+  Utf16Char* = distinct int16
+
+when not (defined(cpu16) or defined(cpu8)):
+  when defined(nimv2):
+
+    type
+      WideCString* = ptr UncheckedArray[Utf16Char]
+
+      WideCStringObj* = object
+        bytes: int
+        data: WideCString
+
+    const arcLike = defined(gcArc) or defined(gcAtomicArc) or defined(gcOrc)
+    when defined(nimAllowNonVarDestructor) and arcLike:
+      proc `=destroy`(a: WideCStringObj) =
+        if a.data != nil:
+          when compileOption("threads"):
+            deallocShared(a.data)
+          else:
+            dealloc(a.data)
+    else:
+      proc `=destroy`(a: var WideCStringObj) =
+        if a.data != nil:
+          when compileOption("threads"):
+            deallocShared(a.data)
+          else:
+            dealloc(a.data)
+
+    proc `=copy`(a: var WideCStringObj; b: WideCStringObj) {.error.}
+
+    proc `=sink`(a: var WideCStringObj; b: WideCStringObj) =
+      a.bytes = b.bytes
+      a.data = b.data
+
+    proc createWide(a: var WideCStringObj; bytes: int) =
+      a.bytes = bytes
+      when compileOption("threads"):
+        a.data = cast[typeof(a.data)](allocShared0(bytes))
+      else:
+        a.data = cast[typeof(a.data)](alloc0(bytes))
+
+    template `[]`*(a: WideCStringObj; idx: int): Utf16Char = a.data[idx]
+    template `[]=`*(a: WideCStringObj; idx: int; val: Utf16Char) = a.data[idx] = val
+
+    template nullWide(): untyped = WideCStringObj(bytes: 0, data: nil)
+
+    converter toWideCString*(x: WideCStringObj): WideCString {.inline.} =
+      result = x.data
+
+  else:
+    template nullWide(): untyped = nil
+
+    type
+      WideCString* = ref UncheckedArray[Utf16Char]
+      WideCStringObj* = WideCString
+
+    template createWide(a; L) =
+      unsafeNew(a, L)
+
+  proc ord(arg: Utf16Char): int = int(cast[uint16](arg))
+
+  proc len*(w: WideCString): int =
+    ## returns the length of a widestring. This traverses the whole string to
+    ## find the binary zero end marker!
+    result = 0
+    while int16(w[result]) != 0'i16: inc result
+
+  const
+    UNI_REPLACEMENT_CHAR = Utf16Char(0xFFFD'i16)
+    UNI_MAX_BMP = 0x0000FFFF
+    UNI_MAX_UTF16 = 0x0010FFFF
+    # UNI_MAX_UTF32 = 0x7FFFFFFF
+    # UNI_MAX_LEGAL_UTF32 = 0x0010FFFF
+
+    halfShift = 10
+    halfBase = 0x0010000
+    halfMask = 0x3FF
+
+    UNI_SUR_HIGH_START = 0xD800
+    UNI_SUR_HIGH_END = 0xDBFF
+    UNI_SUR_LOW_START = 0xDC00
+    UNI_SUR_LOW_END = 0xDFFF
+    UNI_REPL = 0xFFFD
+
+  template ones(n: untyped): untyped = ((1 shl n)-1)
+
+  template fastRuneAt(s: cstring, i, L: int, result: untyped, doInc = true) =
+    ## Returns the unicode character `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:
+      result = ord(s[i])
+      when doInc: inc(i)
+    elif ord(s[i]) shr 5 == 0b110:
+      #assert(ord(s[i+1]) shr 6 == 0b10)
+      if i <= L - 2:
+        result = (ord(s[i]) and (ones(5))) shl 6 or (ord(s[i+1]) and ones(6))
+        when doInc: inc(i, 2)
+      else:
+        result = UNI_REPL
+        when doInc: inc(i)
+    elif ord(s[i]) shr 4 == 0b1110:
+      if i <= L - 3:
+        #assert(ord(s[i+1]) shr 6 == 0b10)
+        #assert(ord(s[i+2]) shr 6 == 0b10)
+        result = (ord(s[i]) and ones(4)) shl 12 or
+                (ord(s[i+1]) and ones(6)) shl 6 or
+                (ord(s[i+2]) and ones(6))
+        when doInc: inc(i, 3)
+      else:
+        result = UNI_REPL
+        when doInc: inc(i)
+    elif ord(s[i]) shr 3 == 0b11110:
+      if i <= L - 4:
+        #assert(ord(s[i+1]) shr 6 == 0b10)
+        #assert(ord(s[i+2]) shr 6 == 0b10)
+        #assert(ord(s[i+3]) shr 6 == 0b10)
+        result = (ord(s[i]) and ones(3)) shl 18 or
+                (ord(s[i+1]) and ones(6)) shl 12 or
+                (ord(s[i+2]) and ones(6)) shl 6 or
+                (ord(s[i+3]) and ones(6))
+        when doInc: inc(i, 4)
+      else:
+        result = UNI_REPL
+        when doInc: inc(i)
+    else:
+      result = 0xFFFD
+      when doInc: inc(i)
+
+  iterator runes(s: cstring, L: int): int =
+    var
+      i = 0
+      result: int
+    while i < L:
+      fastRuneAt(s, i, L, result, true)
+      yield result
+
+  proc newWideCString*(size: int): WideCStringObj =
+    createWide(result, size * 2 + 2)
+
+  proc newWideCString*(source: cstring, L: int): WideCStringObj =
+    ## Warning:: `source` needs to be preallocated with the length `L`
+    createWide(result, L * 2 + 2)
+    var d = 0
+    for ch in runes(source, L):
+
+      if ch <= UNI_MAX_BMP:
+        if ch >= UNI_SUR_HIGH_START and ch <= UNI_SUR_LOW_END:
+          result[d] = UNI_REPLACEMENT_CHAR
+        else:
+          result[d] = cast[Utf16Char](uint16(ch))
+      elif ch > UNI_MAX_UTF16:
+        result[d] = UNI_REPLACEMENT_CHAR
+      else:
+        let ch = ch - halfBase
+        result[d] = cast[Utf16Char](uint16((ch shr halfShift) + UNI_SUR_HIGH_START))
+        inc d
+        result[d] = cast[Utf16Char](uint16((ch and halfMask) + UNI_SUR_LOW_START))
+      inc d
+    result[d] = Utf16Char(0)
+
+  proc newWideCString*(s: cstring): WideCStringObj =
+    if s.isNil: return nullWide
+
+    result = newWideCString(s, s.len)
+
+  proc newWideCString*(s: string): WideCStringObj =
+    result = newWideCString(cstring s, s.len)
+
+  proc `$`*(w: WideCString, estimate: int, replacement: int = 0xFFFD): string =
+    result = newStringOfCap(estimate + estimate shr 2)
+
+    var i = 0
+    while w[i].int16 != 0'i16:
+      var ch = ord(w[i])
+      inc i
+      if ch >= UNI_SUR_HIGH_START and ch <= UNI_SUR_HIGH_END:
+        # If the 16 bits following the high surrogate are in the source buffer...
+        let ch2 = ord(w[i])
+
+        # If it's a low surrogate, convert to UTF32:
+        if ch2 >= UNI_SUR_LOW_START and ch2 <= UNI_SUR_LOW_END:
+          ch = (((ch and halfMask) shl halfShift) + (ch2 and halfMask)) + halfBase
+          inc i
+        else:
+          #invalid UTF-16
+          ch = replacement
+      elif ch >= UNI_SUR_LOW_START and ch <= UNI_SUR_LOW_END:
+        #invalid UTF-16
+        ch = replacement
+
+      if ch < 0x80:
+        result.add chr(ch)
+      elif ch < 0x800:
+        result.add chr((ch shr 6) or 0xc0)
+        result.add chr((ch and 0x3f) or 0x80)
+      elif ch < 0x10000:
+        result.add chr((ch shr 12) or 0xe0)
+        result.add chr(((ch shr 6) and 0x3f) or 0x80)
+        result.add chr((ch and 0x3f) or 0x80)
+      elif ch <= 0x10FFFF:
+        result.add chr((ch shr 18) or 0xf0)
+        result.add chr(((ch shr 12) and 0x3f) or 0x80)
+        result.add chr(((ch shr 6) and 0x3f) or 0x80)
+        result.add chr((ch and 0x3f) or 0x80)
+      else:
+        # replacement char(in case user give very large number):
+        result.add chr(0xFFFD shr 12 or 0b1110_0000)
+        result.add chr(0xFFFD shr 6 and ones(6) or 0b10_0000_00)
+        result.add chr(0xFFFD and ones(6) or 0b10_0000_00)
+
+  proc `$`*(s: WideCString): string =
+    result = s $ 80
+
+  when defined(nimv2):
+    proc `$`*(s: WideCStringObj, estimate: int, replacement: int = 0xFFFD): string =
+      `$`(s.data, estimate, replacement)
+
+    proc `$`*(s: WideCStringObj): string =
+      $(s.data)
+
+    proc len*(w: WideCStringObj): int {.inline.} =
+      len(w.data)
diff --git a/lib/std/with.nim b/lib/std/with.nim
new file mode 100644
index 000000000..c2eaa4bef
--- /dev/null
+++ b/lib/std/with.nim
@@ -0,0 +1,48 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements the `with` macro for easy
+## function chaining. See https://github.com/nim-lang/RFCs/issues/193
+## and https://github.com/nim-lang/RFCs/issues/192 for details leading to this
+## particular design.
+##
+## **Since:** version 1.2.
+
+import std/[macros, private / underscored_calls]
+
+macro with*(arg: typed; calls: varargs[untyped]): untyped =
+  ## This macro provides `chaining`:idx: of function calls.
+  ## It does so by patching every call in `calls` to
+  ## use `arg` as the first argument.
+  ##
+  ## .. caution:: This evaluates `arg` multiple times!
+  runnableExamples:
+    var x = "yay"
+    with x:
+      add "abc"
+      add "efg"
+    doAssert x == "yayabcefg"
+
+    var a = 44
+    with a:
+      += 4
+      -= 5
+    doAssert a == 43
+
+    # Nesting works for object types too!
+    var foo = (bar: 1, qux: (baz: 2))
+    with foo:
+      bar = 2
+      with qux:
+        baz = 3
+    doAssert foo.bar == 2
+    doAssert foo.qux.baz == 3
+
+  result = newNimNode(nnkStmtList, arg)
+  underscoredCalls(result, calls, arg)
diff --git a/lib/std/wordwrap.nim b/lib/std/wordwrap.nim
new file mode 100644
index 000000000..9333f880b
--- /dev/null
+++ b/lib/std/wordwrap.nim
@@ -0,0 +1,74 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2018 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module contains an algorithm to wordwrap a Unicode string.
+
+import std/[strutils, unicode]
+
+proc olen(s: string; start, lastExclusive: int): int =
+  var i = start
+  result = 0
+  while i < lastExclusive:
+    inc result
+    let L = graphemeLen(s, i)
+    inc i, L
+
+proc wrapWords*(s: string, maxLineWidth = 80,
+               splitLongWords = true,
+               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 = ""
+
+  var i = 0
+  while true:
+    var j = i
+    let isSep = j < s.len and s[j] in seps
+    while j < s.len and (s[j] in seps) == isSep: inc(j)
+    if j <= i: break
+    #yield (substr(s, i, j-1), isSep)
+    if isSep:
+      lastSep.setLen 0
+      for k in i..<j:
+        if s[k] notin {'\L', '\C'}: lastSep.add s[k]
+      if lastSep.len == 0:
+        lastSep.add ' '
+        dec spaceLeft
+      else:
+        spaceLeft = spaceLeft - olen(lastSep, 0, lastSep.len)
+    else:
+      let wlen = olen(s, i, j)
+      if wlen > spaceLeft:
+        if splitLongWords and wlen > maxLineWidth:
+          var k = 0
+          while k < j - i:
+            if spaceLeft <= 0:
+              spaceLeft = maxLineWidth
+              result.add newLine
+            dec spaceLeft
+            let L = graphemeLen(s, k+i)
+            for m in 0 ..< L: result.add s[i+k+m]
+            inc k, L
+        else:
+          spaceLeft = maxLineWidth - wlen
+          result.add(newLine)
+          for k in i..<j: result.add(s[k])
+      else:
+        spaceLeft = spaceLeft - wlen
+        result.add(lastSep)
+        for k in i..<j: result.add(s[k])
+        #lastSep.setLen(0)
+    i = j
diff --git a/lib/std/wrapnils.nim b/lib/std/wrapnils.nim
new file mode 100644
index 000000000..0b75c270e
--- /dev/null
+++ b/lib/std/wrapnils.nim
@@ -0,0 +1,193 @@
+## This module allows evaluating expressions safely against the following conditions:
+## * nil dereferences
+## * field accesses with incorrect discriminant in case objects
+##
+## `default(T)` is returned in those cases when evaluating an expression of type `T`.
+## This simplifies code by reducing need for if-else branches.
+##
+## Note: experimental module, unstable API.
+
+#[
+TODO:
+consider handling indexing operations, eg:
+doAssert ?.default(seq[int])[3] == default(int)
+]#
+
+import std/macros
+
+runnableExamples:
+  type Foo = ref object
+    x1: string
+    x2: Foo
+    x3: ref int
+
+  var f: Foo
+  assert ?.f.x2.x1 == "" # returns default value since `f` is nil
+
+  var f2 = Foo(x1: "a")
+  f2.x2 = f2
+  assert ?.f2.x1 == "a" # same as f2.x1 (no nil LHS in this chain)
+  assert ?.Foo(x1: "a").x1 == "a" # can use constructor inside
+
+  # when you know a sub-expression doesn't involve a `nil` (e.g. `f2.x2.x2`),
+  # you can scope it as follows:
+  assert ?.(f2.x2.x2).x3[] == 0
+
+  assert (?.f2.x2.x2).x3 == nil  # this terminates ?. early
+
+runnableExamples:
+  # ?. also allows case object
+  type B = object
+    b0: int
+    case cond: bool
+    of false: discard
+    of true:
+      b1: float
+
+  var b = B(cond: false, b0: 3)
+  doAssertRaises(FieldDefect): discard b.b1 # wrong discriminant
+  doAssert ?.b.b1 == 0.0 # safe
+  b = B(cond: true, b1: 4.5)
+  doAssert ?.b.b1 == 4.5
+
+  # lvalue semantics are preserved:
+  if (let p = ?.b.b1.addr; p != nil): p[] = 4.7
+  doAssert b.b1 == 4.7
+
+proc finalize(n: NimNode, lhs: NimNode, level: int): NimNode =
+  if level == 0:
+    result = quote: `lhs` = `n`
+  else:
+    result = quote: (let `lhs` = `n`)
+
+proc process(n: NimNode, lhs: NimNode, label: NimNode, level: int): NimNode =
+  var n = n.copyNimTree
+  var it = n
+  let addr2 = bindSym"addr"
+  var old: tuple[n: NimNode, index: int]
+  while true:
+    if it.len == 0:
+      result = finalize(n, lhs, level)
+      break
+    elif it.kind == nnkCheckedFieldExpr:
+      let dot = it[0]
+      let obj = dot[0]
+      let objRef = quote do: `addr2`(`obj`)
+        # avoids a copy and preserves lvalue semantics, see tests
+      let check = it[1]
+      let okSet = check[1]
+      let kind1 = check[2]
+      let tmp = genSym(nskLet, "tmpCase")
+      let body = process(objRef, tmp, label, level + 1)
+      let tmp3 = nnkDerefExpr.newTree(tmp)
+      it[0][0] = tmp3
+      let dot2 = nnkDotExpr.newTree(@[tmp, dot[1]])
+      if old.n != nil: old.n[old.index] = dot2
+      else: n = dot2
+      let assgn = finalize(n, lhs, level)
+      result = quote do:
+        `body`
+        if `tmp3`.`kind1` notin `okSet`: break `label`
+        `assgn`
+      break
+    elif it.kind in {nnkHiddenDeref, nnkDerefExpr}:
+      let tmp = genSym(nskLet, "tmp")
+      let body = process(it[0], tmp, label, level + 1)
+      it[0] = tmp
+      let assgn = finalize(n, lhs, level)
+      result = quote do:
+        `body`
+        if `tmp` == nil: break `label`
+        `assgn`
+      break
+    elif it.kind == nnkCall: # consider extending to `nnkCallKinds`
+      # `copyNimTree` needed to avoid `typ = nil` issues
+      old = (it, 1)
+      it = it[1].copyNimTree
+    else:
+      old = (it, 0)
+      it = it[0]
+
+macro `?.`*(a: typed): auto =
+  ## Transforms `a` into an expression that can be safely evaluated even in
+  ## presence of intermediate nil pointers/references, in which case a default
+  ## value is produced.
+  let lhs = genSym(nskVar, "lhs")
+  let label = genSym(nskLabel, "label")
+  let body = process(a, lhs, label, 0)
+  result = quote do:
+    var `lhs`: type(`a`)
+    block `label`:
+      `body`
+    `lhs`
+
+# the code below is not needed for `?.`
+from std/options import Option, isSome, get, option, unsafeGet, UnpackDefect
+
+macro `??.`*(a: typed): Option =
+  ## Same as `?.` but returns an `Option`.
+  runnableExamples:
+    import std/options
+    type Foo = ref object
+      x1: ref int
+      x2: int
+    # `?.` can't distinguish between a valid vs invalid default value, but `??.` can:
+    var f1 = Foo(x1: int.new, x2: 2)
+    doAssert (??.f1.x1[]).get == 0 # not enough to tell when the chain was valid.
+    doAssert (??.f1.x1[]).isSome # a nil didn't occur in the chain
+    doAssert (??.f1.x2).get == 2
+
+    var f2: Foo
+    doAssert not (??.f2.x1[]).isSome # f2 was nil
+
+    doAssertRaises(UnpackDefect): discard (??.f2.x1[]).get
+    doAssert ?.f2.x1[] == 0 # in contrast, this returns default(int)
+
+  let lhs = genSym(nskVar, "lhs")
+  let lhs2 = genSym(nskVar, "lhs")
+  let label = genSym(nskLabel, "label")
+  let body = process(a, lhs2, label, 0)
+  result = quote do:
+    var `lhs`: Option[type(`a`)]
+    block `label`:
+      var `lhs2`: type(`a`)
+      `body`
+      `lhs` = option(`lhs2`)
+    `lhs`
+
+template fakeDot*(a: Option, b): untyped =
+  ## See top-level example.
+  let a1 = a # to avoid double evaluations
+  type T = Option[typeof(unsafeGet(a1).b)]
+  if isSome(a1):
+    let a2 = unsafeGet(a1)
+    when typeof(a2) is ref|ptr:
+      if a2 == nil:
+        default(T)
+      else:
+        option(a2.b)
+    else:
+      option(a2.b)
+  else:
+    # nil is "sticky"; this is needed, see tests
+    default(T)
+
+# xxx this should but doesn't work: func `[]`*[T, I](a: Option[T], i: I): Option {.inline.} =
+
+func `[]`*[T, I](a: Option[T], i: I): auto {.inline.} =
+  ## See top-level example.
+  if isSome(a):
+    # correctly will raise IndexDefect if a is valid but wraps an empty container
+    result = option(a.unsafeGet[i])
+
+func `[]`*[U](a: Option[U]): auto {.inline.} =
+  ## See top-level example.
+  if isSome(a):
+    let a2 = a.unsafeGet
+    if a2 != nil:
+      result = option(a2[])
+
+when false:
+  # xxx: expose a way to do this directly in std/options, e.g.: `getAsIs`
+  proc safeGet[T](a: Option[T]): T {.inline.} =
+    get(a, default(T))
diff --git a/lib/system.nim b/lib/system.nim
index fb02fde23..2f9cdc5f9 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -7,649 +7,541 @@
 #    distribution, for details about the copyright.
 #
 
+
 ## The compiler depends on the System module to work properly and the System
 ## module depends on the compiler. Most of the routines listed here use
 ## special compiler magic.
+##
 ## Each module implicitly imports the System module; it must not be listed
 ## explicitly. Because of this there cannot be a user-defined module named
-## ``system``.
+## `system`.
 ##
-## Module system
+## System module
 ## =============
 ##
+## .. include:: ./system_overview.rst
 
-# That lonesome header above is to prevent :idx: entries from being mentioned
-# in the global index as part of the previous header (Exception hierarchy).
 
-type
-  int* {.magic: Int.} ## default integer type; bitwidth depends on
-                      ## architecture, but is always the same as a pointer
-  int8* {.magic: Int8.} ## signed 8 bit integer type
-  int16* {.magic: Int16.} ## signed 16 bit integer type
-  int32* {.magic: Int32.} ## signed 32 bit integer type
-  int64* {.magic: Int64.} ## signed 64 bit integer type
-  uint* {.magic: UInt.} ## unsigned default integer type
-  uint8* {.magic: UInt8.} ## unsigned 8 bit integer type
-  uint16* {.magic: UInt16.} ## unsigned 16 bit integer type
-  uint32* {.magic: UInt32.} ## unsigned 32 bit integer type
-  uint64* {.magic: UInt64.} ## unsigned 64 bit integer type
-  float* {.magic: Float.} ## default floating point type
-  float32* {.magic: Float32.} ## 32 bit floating point type
-  float64* {.magic: Float.} ## 64 bit floating point type
-
-# 'float64' is now an alias to 'float'; this solves many problems
-
-type # we need to start a new type section here, so that ``0`` can have a type
-  bool* {.magic: Bool.} = enum ## built-in boolean type
-    false = 0, true = 1
+include "system/basic_types"
 
-type
-  char* {.magic: Char.} ## built-in 8 bit character type (unsigned)
-  string* {.magic: String.} ## built-in string type
-  cstring* {.magic: Cstring.} ## built-in cstring (*compatible string*) type
-  pointer* {.magic: Pointer.} ## built-in pointer type, use the ``addr``
-                              ## operator to get a pointer to a variable
-
-  typedesc* {.magic: TypeDesc.} ## meta type to denote a type description
+func zeroDefault*[T](_: typedesc[T]): T {.magic: "ZeroDefault".} =
+  ## Returns the binary zeros representation of the type `T`. It ignores
+  ## default fields of an object.
+  ##
+  ## See also:
+  ## * `default <#default,typedesc[T]>`_
 
-const
-  on* = true    ## alias for ``true``
-  off* = false  ## alias for ``false``
+include "system/compilation"
 
 {.push warning[GcMem]: off, warning[Uninit]: off.}
-{.push hints: off.}
+# {.push hints: off.}
 
-proc `or` *(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
-  ## Constructs an `or` meta class
-
-proc `and` *(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
-  ## Constructs an `and` meta class
+type
+  `static`*[T] {.magic: "Static".}
+    ## Meta type representing all values that can be evaluated at compile-time.
+    ##
+    ## The type coercion `static(x)` can be used to force the compile-time
+    ## evaluation of the given expression `x`.
 
-proc `not` *(a: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
-  ## Constructs an `not` meta class
+  `type`*[T] {.magic: "Type".}
+    ## Meta type representing the type of all type values.
+    ##
+    ## The coercion `type(x)` can be used to obtain the type of the given
+    ## expression `x`.
 
 type
-  Ordinal* {.magic: Ordinal.}[T] ## Generic ordinal type. Includes integer,
-                                 ## bool, character, and enumeration types
-                                 ## as well as their subtypes. Note `uint`
-                                 ## and `uint64` are not ordinal types for
-                                 ## implementation reasons
-  `ptr`* {.magic: Pointer.}[T] ## built-in generic untraced pointer type
-  `ref`* {.magic: Pointer.}[T] ## built-in generic traced pointer type
-
-  `nil` {.magic: "Nil".}
-
-  expr* {.magic: Expr, deprecated.} ## meta type to denote an expression (for templates)
-                                    ## **Deprecated** since version 0.15. Use ``untyped`` instead.
-  stmt* {.magic: Stmt, deprecated.} ## meta type to denote a statement (for templates)
-                                    ## **Deprecated** since version 0.15. Use ``typed`` instead.
-
-  void* {.magic: "VoidType".}   ## meta type to denote the absence of any type
-  auto* {.magic: Expr.} ## meta type for automatic type determination
-  any* = distinct auto ## meta type for any supported type
-  untyped* {.magic: Expr.} ## meta type to denote an expression that
-                           ## is not resolved (for templates)
-  typed* {.magic: Stmt.}   ## meta type to denote an expression that
-                           ## is resolved (for templates)
-
-  SomeSignedInt* = int|int8|int16|int32|int64
-    ## type class matching all signed integer types
-
-  SomeUnsignedInt* = uint|uint8|uint16|uint32|uint64
-    ## type class matching all unsigned integer types
-
-  SomeInteger* = SomeSignedInt|SomeUnsignedInt
-    ## type class matching all integer types
-
-  SomeOrdinal* = int|int8|int16|int32|int64|bool|enum|uint8|uint16|uint32
-    ## type class matching all ordinal types; however this includes enums with
-    ## holes.
-
-  SomeFloat* = float|float32|float64
-    ## type class matching all floating point number types
-
-  SomeNumber* = SomeInteger|SomeFloat
-    ## type class matching all number types
-
-{.deprecated: [SomeReal: SomeFloat].}
-
-proc defined*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.}
-  ## Special compile-time procedure that checks whether `x` is
-  ## defined.
-  ## `x` is an external symbol introduced through the compiler's
-  ## `-d:x switch <nimc.html#compile-time-symbols>`_ to enable build time
-  ## conditionals:
-  ##
-  ## .. code-block:: Nim
-  ##   when not defined(release):
-  ##     # Do here programmer friendly expensive sanity checks.
-  ##   # Put here the normal code
-
-when defined(nimalias):
-  {.deprecated: [
-    TSignedInt: SomeSignedInt,
-    TUnsignedInt: SomeUnsignedInt,
-    TInteger: SomeInteger,
-    TReal: SomeFloat,
-    TNumber: SomeNumber,
-    TOrdinal: SomeOrdinal].}
-
-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.
-  ## This can be used to check whether a library provides a certain
-  ## feature or not:
-  ##
-  ## .. code-block:: Nim
-  ##   when not declared(strutils.toUpper):
-  ##     # provide our own toUpper proc here, because strutils is
-  ##     # missing it.
-
-when defined(useNimRtl):
-  {.deadCodeElim: on.}  # dce option deprecated
+  TypeOfMode* = enum ## Possible modes of `typeof`.
+    typeOfProc,      ## Prefer the interpretation that means `x` is a proc call.
+    typeOfIter       ## Prefer the interpretation that means `x` is an iterator call.
+
+proc typeof*(x: untyped; mode = typeOfIter): typedesc {.
+  magic: "TypeOf", noSideEffect, compileTime.} =
+  ## Builtin `typeof` operation for accessing the type of an expression.
+  ## Since version 0.20.0.
+  runnableExamples:
+    proc myFoo(): float = 0.0
+    iterator myFoo(): string = yield "abc"
+    iterator myFoo2(): string = yield "abc"
+    iterator myFoo3(): string {.closure.} = yield "abc"
+    doAssert type(myFoo()) is string
+    doAssert typeof(myFoo()) is string
+    doAssert typeof(myFoo(), typeOfIter) is string
+    doAssert typeof(myFoo3) is iterator
+
+    doAssert typeof(myFoo(), typeOfProc) is float
+    doAssert typeof(0.0, typeOfProc) is float
+    doAssert typeof(myFoo3, typeOfProc) is iterator
+    doAssert not compiles(typeof(myFoo2(), typeOfProc))
+      # this would give: Error: attempting to call routine: 'myFoo2'
+      # since `typeOfProc` expects a typed expression and `myFoo2()` can
+      # only be used in a `for` context.
+
+proc `or`*(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
+  ## Constructs an `or` meta class.
+
+proc `and`*(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
+  ## Constructs an `and` meta class.
+
+proc `not`*(a: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
+  ## Constructs an `not` meta class.
+
+when defined(nimHasIterable):
+  type
+    iterable*[T] {.magic: IterableType.}  ## Represents an expression that yields `T`
 
-proc definedInScope*(x: untyped): bool {.
-  magic: "DefinedInScope", noSideEffect, deprecated, compileTime.}
-  ## **Deprecated since version 0.9.6**: Use ``declaredInScope`` instead.
+type
+  Ordinal*[T] {.magic: Ordinal.} ## Generic ordinal type. Includes integer,
+                                  ## bool, character, and enumeration types
+                                  ## as well as their subtypes. See also
+                                  ## `SomeOrdinal`.
 
-proc declaredInScope*(x: untyped): bool {.
-  magic: "DefinedInScope", noSideEffect, compileTime.}
-  ## Special compile-time procedure that checks whether `x` is
-  ## declared in the current scope. `x` has to be an identifier.
 
-proc `addr`*[T](x: var T): ptr T {.magic: "Addr", noSideEffect.} =
-  ## Builtin 'addr' operator for taking the address of a memory location.
+proc `addr`*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} =
+  ## Builtin `addr` operator for taking the address of a memory location.
+  ##
+  ## .. note:: This works for `let` variables or parameters
+  ##   for better interop with C. When you use it to write a wrapper
+  ##   for a C library and take the address of `let` variables or parameters,
+  ##   you should always check that the original library
+  ##   does never write to data behind the pointer that is returned from
+  ##   this procedure.
+  ##
   ## Cannot be overloaded.
   ##
-  ## .. code-block:: nim
-  ##  var
-  ##    buf: seq[char] = @['a','b','c']
-  ##    p: pointer = buf[1].addr
-  ##  echo cast[ptr char](p)[]    # b
+  ##   ```nim
+  ##   var
+  ##     buf: seq[char] = @['a','b','c']
+  ##     p = buf[1].addr
+  ##   echo p.repr # ref 0x7faa35c40059 --> 'b'
+  ##   echo p[]    # b
+  ##   ```
   discard
 
 proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} =
-  ## Builtin 'addr' operator for taking the address of a memory
-  ## location.  This works even for ``let`` variables or parameters
-  ## for better interop with C and so it is considered even more
-  ## unsafe than the ordinary ``addr``.  When you use it to write a
-  ## wrapper for a C library, you should always check that the
-  ## original library does never write to data behind the pointer that
-  ## is returned from this procedure.
-  ## Cannot be overloaded.
-  discard
-
-proc `type`*(x: untyped): typeDesc {.magic: "TypeOf", noSideEffect, compileTime.} =
-  ## Builtin 'type' operator for accessing the type of an expression.
-  ## Cannot be overloaded.
+  ## .. warning:: `unsafeAddr` is a deprecated alias for `addr`,
+  ##    use `addr` instead.
   discard
 
-proc `not` *(x: bool): bool {.magic: "Not", noSideEffect.}
-  ## Boolean not; returns true iff ``x == false``.
-
-proc `and`*(x, y: bool): bool {.magic: "And", noSideEffect.}
-  ## Boolean ``and``; returns true iff ``x == y == true``.
-  ## Evaluation is lazy: if ``x`` is false,
-  ## ``y`` will not even be evaluated.
-proc `or`*(x, y: bool): bool {.magic: "Or", noSideEffect.}
-  ## Boolean ``or``; returns true iff ``not (not x and not y)``.
-  ## Evaluation is lazy: if ``x`` is true,
-  ## ``y`` will not even be evaluated.
-proc `xor`*(x, y: bool): bool {.magic: "Xor", noSideEffect.}
-  ## Boolean `exclusive or`; returns true iff ``x != y``.
-
-proc new*[T](a: var ref T) {.magic: "New", noSideEffect.}
-  ## creates a new object of type ``T`` and returns a safe (traced)
-  ## reference to it in ``a``.
-
-proc new*(T: typedesc): auto =
-  ## creates a new object of type ``T`` and returns a safe (traced)
-  ## reference to it as result value.
-  ##
-  ## When ``T`` is a ref type then the resulting type will be ``T``,
-  ## otherwise it will be ``ref T``.
-  when (T is ref):
-    var r: T
-  else:
-    var r: ref T
-  new(r)
-  return r
 
-
-proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.}
-  ## leaked implementation detail. Do not use.
+const ThisIsSystem = true
 
 proc new*[T](a: var ref T, finalizer: proc (x: ref T) {.nimcall.}) {.
   magic: "NewFinalize", noSideEffect.}
-  ## creates a new object of type ``T`` and returns a safe (traced)
-  ## reference to it in ``a``. When the garbage collector frees the object,
-  ## `finalizer` is called. The `finalizer` may not keep a reference to the
+  ## Creates a new object of type `T` and returns a safe (traced)
+  ## reference to it in `a`.
+  ##
+  ## When the garbage collector frees the object, `finalizer` is called.
+  ## The `finalizer` may not keep a reference to the
   ## object pointed to by `x`. The `finalizer` cannot prevent the GC from
-  ## freeing the object. Note: The `finalizer` refers to the type `T`, not to
-  ## the object! This means that for each object of type `T` the finalizer
-  ## will be called!
+  ## freeing the object.
+  ##
+  ## **Note**: The `finalizer` refers to the type `T`, not to the object!
+  ## This means that for each object of type `T` the finalizer will be called!
+
+proc `=wasMoved`*[T](obj: var T) {.magic: "WasMoved", noSideEffect.} =
+  ## Generic `wasMoved`:idx: implementation that can be overridden.
+
+proc wasMoved*[T](obj: var T) {.inline, noSideEffect.} =
+  ## Resets an object `obj` to its initial (binary zero) value to signify
+  ## it was "moved" and to signify its destructor should do nothing and
+  ## ideally be optimized away.
+  {.cast(raises: []), cast(tags: []).}:
+    `=wasMoved`(obj)
+
+proc move*[T](x: var T): T {.magic: "Move", noSideEffect.} =
+  result = x
+  {.cast(raises: []), cast(tags: []).}:
+    `=wasMoved`(x)
+
+when defined(nimHasEnsureMove):
+  proc ensureMove*[T](x: T): T {.magic: "EnsureMove", noSideEffect.} =
+    ## Ensures that `x` is moved to the new location, otherwise it gives
+    ## an error at the compile time.
+    runnableExamples:
+      proc foo =
+        var x = "Hello"
+        let y = ensureMove(x)
+        doAssert y == "Hello"
+      foo()
+    discard "implemented in injectdestructors"
 
-proc reset*[T](obj: var T) {.magic: "Reset", noSideEffect.}
-  ## resets an object `obj` to its initial (binary zero) value. This needs to
-  ## be called before any possible `object branch transition`:idx:.
+type
+  range*[T]{.magic: "Range".}         ## Generic type to construct range types.
+  array*[I, T]{.magic: "Array".}      ## Generic type to construct
+                                      ## fixed-length arrays.
+  openArray*[T]{.magic: "OpenArray".} ## Generic type to construct open arrays.
+                                      ## Open arrays are implemented as a
+                                      ## pointer to the array data and a
+                                      ## length field.
+  varargs*[T]{.magic: "Varargs".}     ## Generic type to construct a varargs type.
+  seq*[T]{.magic: "Seq".}             ## Generic type to construct sequences.
+  set*[T]{.magic: "Set".}             ## Generic type to construct bit sets.
 
 type
-  range*{.magic: "Range".}[T] ## Generic type to construct range types.
-  array*{.magic: "Array".}[I, T]  ## Generic type to construct
-                                  ## fixed-length arrays.
-  openArray*{.magic: "OpenArray".}[T]  ## Generic type to construct open arrays.
-                                       ## Open arrays are implemented as a
-                                       ## pointer to the array data and a
-                                       ## length field.
-  varargs*{.magic: "Varargs".}[T] ## Generic type to construct a varargs type.
-  seq*{.magic: "Seq".}[T]  ## Generic type to construct sequences.
-  set*{.magic: "Set".}[T]  ## Generic type to construct bit sets.
-
-  UncheckedArray* {.unchecked.}[T] = array[0, T]
-    ## Array with no bounds checking
-
-when defined(nimHasOpt):
-  type opt*{.magic: "Opt".}[T]
-
-when defined(nimNewRuntime):
-  type sink*{.magic: "BuiltinType".}[T]
-  type lent*{.magic: "BuiltinType".}[T]
-
-proc high*[T: Ordinal](x: T): T {.magic: "High", noSideEffect.}
-  ## returns the highest possible index of an array, a sequence, a string or
-  ## the highest possible value of an ordinal value `x`. As a special
-  ## semantic rule, `x` may also be a type identifier.
-  ## ``high(int)`` is Nim's way of writing `INT_MAX`:idx: or `MAX_INT`:idx:.
+  UncheckedArray*[T]{.magic: "UncheckedArray".}
+  ## Array with no bounds checking.
+
+type sink*[T]{.magic: "BuiltinType".}
+type lent*[T]{.magic: "BuiltinType".}
+
+proc high*[T: Ordinal|enum|range](x: T): T {.magic: "High", noSideEffect,
+  deprecated: "Deprecated since v1.4; there should not be `high(value)`. Use `high(type)`.".}
+  ## Returns the highest possible value of an ordinal value `x`.
+  ##
+  ## As a special semantic rule, `x` may also be a type identifier.
+  ##
+  ## **This proc is deprecated**, use this one instead:
+  ## * `high(typedesc) <#high,typedesc[T]>`_
+  ##
+  ## ```nim
+  ## high(2) # => 9223372036854775807
+  ## ```
+
+proc high*[T: Ordinal|enum|range](x: typedesc[T]): T {.magic: "High", noSideEffect.}
+  ## Returns the highest possible value of an ordinal or enum type.
   ##
-  ## .. code-block:: nim
-  ##  var arr = [1,2,3,4,5,6,7]
-  ##  high(arr) #=> 6
-  ##  high(2) #=> 9223372036854775807
-  ##  high(int) #=> 9223372036854775807
+  ## `high(int)` is Nim's way of writing `INT_MAX`:idx: or `MAX_INT`:idx:.
+  ##   ```nim
+  ##   high(int) # => 9223372036854775807
+  ##   ```
+  ##
+  ## See also:
+  ## * `low(typedesc) <#low,typedesc[T]>`_
 
-proc high*[T: Ordinal](x: typeDesc[T]): T {.magic: "High", noSideEffect.}
 proc high*[T](x: openArray[T]): int {.magic: "High", noSideEffect.}
+  ## Returns the highest possible index of a sequence `x`.
+  ##   ```nim
+  ##   var s = @[1, 2, 3, 4, 5, 6, 7]
+  ##   high(s) # => 6
+  ##   for i in low(s)..high(s):
+  ##     echo s[i]
+  ##   ```
+  ##
+  ## See also:
+  ## * `low(openArray) <#low,openArray[T]>`_
+
 proc high*[I, T](x: array[I, T]): I {.magic: "High", noSideEffect.}
-proc high*[I, T](x: typeDesc[array[I, T]]): I {.magic: "High", noSideEffect.}
+  ## Returns the highest possible index of an array `x`.
+  ##
+  ## For empty arrays, the return type is `int`.
+  ##   ```nim
+  ##   var arr = [1, 2, 3, 4, 5, 6, 7]
+  ##   high(arr) # => 6
+  ##   for i in low(arr)..high(arr):
+  ##     echo arr[i]
+  ##   ```
+  ##
+  ## See also:
+  ## * `low(array) <#low,array[I,T]>`_
+
+proc high*[I, T](x: typedesc[array[I, T]]): I {.magic: "High", noSideEffect.}
+  ## Returns the highest possible index of an array type.
+  ##
+  ## For empty arrays, the return type is `int`.
+  ##   ```nim
+  ##   high(array[7, int]) # => 6
+  ##   ```
+  ##
+  ## See also:
+  ## * `low(typedesc[array]) <#low,typedesc[array[I,T]]>`_
+
 proc high*(x: cstring): int {.magic: "High", noSideEffect.}
+  ## Returns the highest possible index of a compatible string `x`.
+  ## This is sometimes an O(n) operation.
+  ##
+  ## See also:
+  ## * `low(cstring) <#low,cstring>`_
+
 proc high*(x: string): int {.magic: "High", noSideEffect.}
+  ## Returns the highest possible index of a string `x`.
+  ##   ```nim
+  ##   var str = "Hello world!"
+  ##   high(str) # => 11
+  ##   ```
+  ##
+  ## See also:
+  ## * `low(string) <#low,string>`_
+
+proc low*[T: Ordinal|enum|range](x: T): T {.magic: "Low", noSideEffect,
+  deprecated: "Deprecated since v1.4; there should not be `low(value)`. Use `low(type)`.".}
+  ## Returns the lowest possible value of an ordinal value `x`. As a special
+  ## semantic rule, `x` may also be a type identifier.
+  ##
+  ## **This proc is deprecated**, use this one instead:
+  ## * `low(typedesc) <#low,typedesc[T]>`_
+  ##
+  ## ```nim
+  ## low(2) # => -9223372036854775808
+  ## ```
+
+proc low*[T: Ordinal|enum|range](x: typedesc[T]): T {.magic: "Low", noSideEffect.}
+  ## Returns the lowest possible value of an ordinal or enum type.
+  ##
+  ## `low(int)` is Nim's way of writing `INT_MIN`:idx: or `MIN_INT`:idx:.
+  ##   ```nim
+  ##   low(int) # => -9223372036854775808
+  ##   ```
+  ##
+  ## See also:
+  ## * `high(typedesc) <#high,typedesc[T]>`_
 
-proc low*[T: Ordinal](x: typeDesc[T]): T {.magic: "Low", noSideEffect.}
 proc low*[T](x: openArray[T]): int {.magic: "Low", noSideEffect.}
+  ## Returns the lowest possible index of a sequence `x`.
+  ##   ```nim
+  ##   var s = @[1, 2, 3, 4, 5, 6, 7]
+  ##   low(s) # => 0
+  ##   for i in low(s)..high(s):
+  ##     echo s[i]
+  ##   ```
+  ##
+  ## See also:
+  ## * `high(openArray) <#high,openArray[T]>`_
+
 proc low*[I, T](x: array[I, T]): I {.magic: "Low", noSideEffect.}
-proc low*[T](x: T): T {.magic: "Low", noSideEffect.}
-proc low*[I, T](x: typeDesc[array[I, T]]): I {.magic: "Low", noSideEffect.}
+  ## Returns the lowest possible index of an array `x`.
+  ##
+  ## For empty arrays, the return type is `int`.
+  ##   ```nim
+  ##   var arr = [1, 2, 3, 4, 5, 6, 7]
+  ##   low(arr) # => 0
+  ##   for i in low(arr)..high(arr):
+  ##     echo arr[i]
+  ##   ```
+  ##
+  ## See also:
+  ## * `high(array) <#high,array[I,T]>`_
+
+proc low*[I, T](x: typedesc[array[I, T]]): I {.magic: "Low", noSideEffect.}
+  ## Returns the lowest possible index of an array type.
+  ##
+  ## For empty arrays, the return type is `int`.
+  ##   ```nim
+  ##   low(array[7, int]) # => 0
+  ##   ```
+  ##
+  ## See also:
+  ## * `high(typedesc[array]) <#high,typedesc[array[I,T]]>`_
+
 proc low*(x: cstring): int {.magic: "Low", noSideEffect.}
-proc low*(x: string): int {.magic: "Low", noSideEffect.}
-  ## returns the lowest possible index of an array, a sequence, a string or
-  ## the lowest possible value of an ordinal value `x`. As a special
-  ## semantic rule, `x` may also be a type identifier.
+  ## Returns the lowest possible index of a compatible string `x`.
   ##
-  ## .. code-block:: nim
-  ##  var arr = [1,2,3,4,5,6,7]
-  ##  low(arr) #=> 0
-  ##  low(2) #=> -9223372036854775808
-  ##  low(int) #=> -9223372036854775808
-
-proc shallowCopy*[T](x: var T, y: T) {.noSideEffect, magic: "ShallowCopy".}
-  ## use this instead of `=` for a `shallow copy`:idx:. The shallow copy
-  ## only changes the semantics for sequences and strings (and types which
-  ## contain those). Be careful with the changed semantics though! There
-  ## is a reason why the default assignment does a deep copy of sequences
-  ## and strings.
-
-when defined(nimArrIdx):
-  # :array|openarray|string|seq|cstring|tuple
-  proc `[]`*[I: Ordinal;T](a: T; i: I): T {.
-    noSideEffect, magic: "ArrGet".}
-  proc `[]=`*[I: Ordinal;T,S](a: T; i: I;
-    x: S) {.noSideEffect, magic: "ArrPut".}
-  proc `=`*[T](dest: var T; src: T) {.noSideEffect, magic: "Asgn".}
-  when defined(nimNewRuntime):
-    proc `=destroy`*[T](x: var T) {.inline, magic: "Asgn".} =
-      ## generic `destructor`:idx: implementation that can be overriden.
+  ## See also:
+  ## * `high(cstring) <#high,cstring>`_
+
+proc low*(x: string): int {.magic: "Low", noSideEffect.}
+  ## Returns the lowest possible index of a string `x`.
+  ##   ```nim
+  ##   var str = "Hello world!"
+  ##   low(str) # => 0
+  ##   ```
+  ##
+  ## See also:
+  ## * `high(string) <#high,string>`_
+
+when not defined(gcArc) and not defined(gcOrc) and not defined(gcAtomicArc):
+  proc shallowCopy*[T](x: var T, y: T) {.noSideEffect, magic: "ShallowCopy".}
+    ## Use this instead of `=` for a `shallow copy`:idx:.
+    ##
+    ## The shallow copy only changes the semantics for sequences and strings
+    ## (and types which contain those).
+    ##
+    ## Be careful with the changed semantics though!
+    ## There is a reason why the default assignment does a deep copy of sequences
+    ## and strings.
+
+# :array|openArray|string|seq|cstring|tuple
+proc `[]`*[I: Ordinal;T](a: T; i: I): T {.
+  noSideEffect, magic: "ArrGet".}
+proc `[]=`*[I: Ordinal;T,S](a: T; i: I;
+  x: sink S) {.noSideEffect, magic: "ArrPut".}
+proc `=`*[T](dest: var T; src: T) {.noSideEffect, magic: "Asgn".}
+proc `=copy`*[T](dest: var T; src: T) {.noSideEffect, magic: "Asgn".}
+
+proc arrGet[I: Ordinal;T](a: T; i: I): T {.
+  noSideEffect, magic: "ArrGet".}
+proc arrPut[I: Ordinal;T,S](a: T; i: I;
+  x: S) {.noSideEffect, magic: "ArrPut".}
+
+const arcLikeMem = defined(gcArc) or defined(gcAtomicArc) or defined(gcOrc)
+
+
+when defined(nimAllowNonVarDestructor) and arcLikeMem and defined(nimPreviewNonVarDestructor):
+  proc `=destroy`*[T](x: T) {.inline, magic: "Destroy".} =
+    ## Generic `destructor`:idx: implementation that can be overridden.
+    discard
+else:
+  proc `=destroy`*[T](x: var T) {.inline, magic: "Destroy".} =
+    ## Generic `destructor`:idx: implementation that can be overridden.
+    discard
+
+  when defined(nimAllowNonVarDestructor) and arcLikeMem:
+    proc `=destroy`*(x: string) {.inline, magic: "Destroy", enforceNoRaises.} =
+      discard
+
+    proc `=destroy`*[T](x: seq[T]) {.inline, magic: "Destroy".} =
       discard
-    proc `=sink`*[T](x: var T; y: T) {.inline, magic: "Asgn".} =
-      ## generic `sink`:idx: implementation that can be overriden.
-      shallowCopy(x, y)
+
+    proc `=destroy`*[T](x: ref T) {.inline, magic: "Destroy".} =
+      discard
+
+when defined(nimHasDup):
+  proc `=dup`*[T](x: T): T {.inline, magic: "Dup".} =
+    ## Generic `dup`:idx: implementation that can be overridden.
+    discard
+
+proc `=sink`*[T](x: var T; y: T) {.inline, nodestroy, magic: "Asgn".} =
+  ## Generic `sink`:idx: implementation that can be overridden.
+  when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
+    x = y
+  else:
+    shallowCopy(x, y)
+
+when defined(nimHasTrace):
+  proc `=trace`*[T](x: var T; env: pointer) {.inline, magic: "Trace".} =
+    ## Generic `trace`:idx: implementation that can be overridden.
+    discard
 
 type
-  HSlice*[T, U] = object ## "heterogenous" slice type
-    a*: T        ## the lower bound (inclusive)
-    b*: U        ## the upper bound (inclusive)
-  Slice*[T] = HSlice[T, T] ## an alias for ``HSlice[T, T]``
-
-proc `..`*[T, U](a: T, b: U): HSlice[T, U] {.noSideEffect, inline, magic: "DotDot".} =
-  ## binary `slice`:idx: operator that constructs an interval ``[a, b]``, both `a`
-  ## and `b` are inclusive. Slices can also be used in the set constructor
-  ## and in ordinal case statements, but then they are special-cased by the
-  ## compiler.
-  result.a = a
-  result.b = b
-
-proc `..`*[T](b: T): HSlice[int, T] {.noSideEffect, inline, magic: "DotDot".} =
-  ## unary `slice`:idx: operator that constructs an interval ``[default(int), b]``
-  result.b = b
-
-when not defined(niminheritable):
-  {.pragma: inheritable.}
-when not defined(nimunion):
-  {.pragma: unchecked.}
-
-# comparison operators:
-proc `==` *[Enum: enum](x, y: Enum): bool {.magic: "EqEnum", noSideEffect.}
-  ## Checks whether values within the *same enum* have the same underlying value
-  ##
-  ## .. code-block:: nim
-  ##  type
-  ##    Enum1 = enum
-  ##      Field1 = 3, Field2
-  ##    Enum2 = enum
-  ##      Place1, Place2 = 3
-  ##  var
-  ##    e1 = Field1
-  ##    e2 = Enum1(Place2)
-  ##  echo (e1 == e2) # true
-  ##  echo (e1 == Place2) # raises error
-proc `==` *(x, y: pointer): bool {.magic: "EqRef", noSideEffect.}
-  ## .. code-block:: nim
-  ##  var # this is a wildly dangerous example
-  ##    a = cast[pointer](0)
-  ##    b = cast[pointer](nil)
-  ##  echo (a == b) # true due to the special meaning of `nil`/0 as a pointer
-proc `==` *(x, y: string): bool {.magic: "EqStr", noSideEffect.}
-  ## Checks for equality between two `string` variables
-
-proc `==` *(x, y: char): bool {.magic: "EqCh", noSideEffect.}
-  ## Checks for equality between two `char` variables
-proc `==` *(x, y: bool): bool {.magic: "EqB", noSideEffect.}
-  ## Checks for equality between two `bool` variables
-proc `==` *[T](x, y: set[T]): bool {.magic: "EqSet", noSideEffect.}
-  ## Checks for equality between two variables of type `set`
-  ##
-  ## .. code-block:: nim
-  ##  var a = {1, 2, 2, 3} # duplication in sets is ignored
-  ##  var b = {1, 2, 3}
-  ##  echo (a == b) # true
-proc `==` *[T](x, y: ref T): bool {.magic: "EqRef", noSideEffect.}
-  ## Checks that two `ref` variables refer to the same item
-proc `==` *[T](x, y: ptr T): bool {.magic: "EqRef", noSideEffect.}
-  ## Checks that two `ptr` variables refer to the same item
-proc `==` *[T: proc](x, y: T): bool {.magic: "EqProc", noSideEffect.}
-  ## Checks that two `proc` variables refer to the same procedure
-
-proc `<=` *[Enum: enum](x, y: Enum): bool {.magic: "LeEnum", noSideEffect.}
-proc `<=` *(x, y: string): bool {.magic: "LeStr", noSideEffect.}
-proc `<=` *(x, y: char): bool {.magic: "LeCh", noSideEffect.}
-proc `<=` *[T](x, y: set[T]): bool {.magic: "LeSet", noSideEffect.}
-proc `<=` *(x, y: bool): bool {.magic: "LeB", noSideEffect.}
-proc `<=` *[T](x, y: ref T): bool {.magic: "LePtr", noSideEffect.}
-proc `<=` *(x, y: pointer): bool {.magic: "LePtr", noSideEffect.}
-
-proc `<` *[Enum: enum](x, y: Enum): bool {.magic: "LtEnum", noSideEffect.}
-proc `<` *(x, y: string): bool {.magic: "LtStr", noSideEffect.}
-proc `<` *(x, y: char): bool {.magic: "LtCh", noSideEffect.}
-proc `<` *[T](x, y: set[T]): bool {.magic: "LtSet", noSideEffect.}
-proc `<` *(x, y: bool): bool {.magic: "LtB", noSideEffect.}
-proc `<` *[T](x, y: ref T): bool {.magic: "LtPtr", noSideEffect.}
-proc `<` *[T](x, y: ptr T): bool {.magic: "LtPtr", noSideEffect.}
-proc `<` *(x, y: pointer): bool {.magic: "LtPtr", noSideEffect.}
-
-template `!=` * (x, y: untyped): untyped =
-  ## unequals operator. This is a shorthand for ``not (x == y)``.
-  not (x == y)
-
-template `>=` * (x, y: untyped): untyped =
-  ## "is greater or equals" operator. This is the same as ``y <= x``.
-  y <= x
-
-template `>` * (x, y: untyped): untyped =
-  ## "is greater" operator. This is the same as ``y < x``.
-  y < x
+  HSlice*[T, U] = object   ## "Heterogeneous" slice type.
+    a*: T                  ## The lower bound (inclusive).
+    b*: U                  ## The upper bound (inclusive).
+  Slice*[T] = HSlice[T, T] ## An alias for `HSlice[T, T]`.
+
+proc `..`*[T, U](a: sink T, b: sink U): HSlice[T, U] {.noSideEffect, inline, magic: "DotDot".} =
+  ## Binary `slice`:idx: operator that constructs an interval `[a, b]`, both `a`
+  ## and `b` are inclusive.
+  ##
+  ## Slices can also be used in the set constructor and in ordinal case
+  ## statements, but then they are special-cased by the compiler.
+  ##   ```nim
+  ##   let a = [10, 20, 30, 40, 50]
+  ##   echo a[2 .. 3] # @[30, 40]
+  ##   ```
+  result = HSlice[T, U](a: a, b: b)
+
+proc `..`*[T](b: sink T): HSlice[int, T]
+  {.noSideEffect, inline, magic: "DotDot", deprecated: "replace `..b` with `0..b`".} =
+  ## Unary `slice`:idx: operator that constructs an interval `[default(int), b]`.
+  ##   ```nim
+  ##   let a = [10, 20, 30, 40, 50]
+  ##   echo a[.. 2] # @[10, 20, 30]
+  ##   ```
+  result = HSlice[int, T](a: 0, b: b)
+
+when defined(hotCodeReloading):
+  {.pragma: hcrInline, inline.}
+else:
+  {.pragma: hcrInline.}
+
+include "system/arithmetics"
+include "system/comparisons"
 
 const
-  appType* {.magic: "AppType"}: string = ""
-    ## a string that describes the application type. Possible values:
-    ## "console", "gui", "lib".
+  appType* {.magic: "AppType".}: string = ""
+    ## A string that describes the application type. Possible values:
+    ## `"console"`, `"gui"`, `"lib"`.
 
 include "system/inclrtl"
 
-const NoFakeVars* = defined(nimscript) ## true if the backend doesn't support \
-  ## "fake variables" like 'var EBADF {.importc.}: cint'.
+const NoFakeVars = defined(nimscript) ## `true` if the backend doesn't support \
+  ## "fake variables" like `var EBADF {.importc.}: cint`.
+
+const notJSnotNims = not defined(js) and not defined(nimscript)
 
-when not defined(JS):
+when not defined(js) and not defined(nimSeqsV2):
   type
     TGenericSeq {.compilerproc, pure, inheritable.} = object
       len, reserved: int
       when defined(gogc):
         elemSize: int
+        elemAlign: int
     PGenericSeq {.exportc.} = ptr TGenericSeq
     # len and space without counting the terminating zero:
     NimStringDesc {.compilerproc, final.} = object of TGenericSeq
       data: UncheckedArray[char]
     NimString = ptr NimStringDesc
 
-when not defined(JS) and not defined(nimscript):
-  template space(s: PGenericSeq): int {.dirty.} =
-    s.reserved and not (seqShallowFlag or strlitFlag)
+when notJSnotNims:
   include "system/hti"
 
 type
-  byte* = uint8 ## this is an alias for ``uint8``, that is an unsigned
-                ## int 8 bits wide.
+  byte* = uint8 ## This is an alias for `uint8`, that is an unsigned
+                ## integer, 8 bits wide.
 
   Natural* = range[0..high(int)]
-    ## is an int type ranging from zero to the maximum value
-    ## of an int. This type is often useful for documentation and debugging.
+    ## is an `int` type ranging from zero to the maximum value
+    ## of an `int`. This type is often useful for documentation and debugging.
 
   Positive* = range[1..high(int)]
-    ## is an int type ranging from one to the maximum value
-    ## of an int. This type is often useful for documentation and debugging.
+    ## is an `int` type ranging from one to the maximum value
+    ## of an `int`. This type is often useful for documentation and debugging.
 
-  RootObj* {.compilerProc, inheritable.} =
-    object ## the root of Nim's object hierarchy. Objects should
-           ## inherit from RootObj or one of its descendants. However,
-           ## objects that have no ancestor are allowed.
-  RootRef* = ref RootObj ## reference to RootObj
+type
+  RootObj* {.compilerproc, inheritable.} =
+    object ## The root of Nim's object hierarchy.
+           ##
+           ## Objects should inherit from `RootObj` or one of its descendants.
+           ## However, objects that have no ancestor are also allowed.
+  RootRef* = ref RootObj ## Reference to `RootObj`.
+
+const NimStackTraceMsgs = compileOption("stacktraceMsgs")
 
+type
   RootEffect* {.compilerproc.} = object of RootObj ## \
-    ## base effect class; each effect should
-    ## inherit from `RootEffect` unless you know what
-    ## you doing.
-  TimeEffect* = object of RootEffect   ## Time effect.
-  IOEffect* = object of RootEffect     ## IO effect.
-  ReadIOEffect* = object of IOEffect   ## Effect describing a read IO operation.
-  WriteIOEffect* = object of IOEffect  ## Effect describing a write IO operation.
-  ExecIOEffect* = object of IOEffect   ## Effect describing an executing IO operation.
+    ## Base effect class.
+    ##
+    ## Each effect should inherit from `RootEffect` unless you know what
+    ## you're doing.
 
+type
   StackTraceEntry* = object ## In debug mode exceptions store the stack trace that led
-                            ## to them. A StackTraceEntry is a single entry of the
+                            ## to them. A `StackTraceEntry` is a single entry of the
                             ## stack trace.
-    procname*: cstring  ## name of the proc that is currently executing
-    line*: int          ## line number of the proc that is currently executing
-    filename*: cstring  ## filename of the proc that is currently executing
+    procname*: cstring      ## Name of the proc that is currently executing.
+    line*: int              ## Line number of the proc that is currently executing.
+    filename*: cstring      ## Filename of the proc that is currently executing.
+    when NimStackTraceMsgs:
+      frameMsg*: string     ## When a stacktrace is generated in a given frame and
+                            ## rendered at a later time, we should ensure the stacktrace
+                            ## data isn't invalidated; any pointer into PFrame is
+                            ## subject to being invalidated so shouldn't be stored.
+    when defined(nimStackTraceOverride):
+      programCounter*: uint ## Program counter - will be used to get the rest of the info,
+                            ## when `$` is called on this type. We can't use
+                            ## "cuintptr_t" in here.
+      procnameStr*, filenameStr*: string ## GC-ed alternatives to "procname" and "filename"
 
   Exception* {.compilerproc, magic: "Exception".} = object of RootObj ## \
     ## Base exception class.
     ##
     ## Each exception has to inherit from `Exception`. See the full `exception
     ## hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-    parent*: ref Exception ## parent exception (can be used as a stack)
-    name*: cstring ## The exception's name is its Nim identifier.
-                   ## This field is filled automatically in the
-                   ## ``raise`` statement.
-    msg* {.exportc: "message".}: string ## the exception's message. Not
+    parent*: ref Exception ## Parent exception (can be used as a stack).
+    name*: cstring         ## The exception's name is its Nim identifier.
+                           ## This field is filled automatically in the
+                           ## `raise` statement.
+    msg* {.exportc: "message".}: string ## The exception's message. Not
                                         ## providing an exception message
                                         ## is bad style.
     when defined(js):
-      trace: string
+      trace*: string
     else:
-      trace: seq[StackTraceEntry]
-    raise_id: uint # set when exception is raised
+      trace*: seq[StackTraceEntry]
     up: ref Exception # used for stacking exceptions. Not exported!
 
-  SystemError* = object of Exception ## \
-    ## Abstract class for exceptions that the runtime system raises.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  IOError* = object of SystemError ## \
-    ## Raised if an IO error occurred.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  EOFError* = object of IOError ## \
-    ## Raised if an IO "end of file" error occurred.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  OSError* = object of SystemError ## \
-    ## Raised if an operating system service failed.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-    errorCode*: int32 ## OS-defined error code describing this error.
-  LibraryError* = object of OSError ## \
-    ## Raised if a dynamic library could not be loaded.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  ResourceExhaustedError* = object of SystemError ## \
-    ## Raised if a resource request could not be fulfilled.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  ArithmeticError* = object of Exception ## \
-    ## Raised if any kind of arithmetic error occurred.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  DivByZeroError* = object of ArithmeticError ## \
-    ## Raised for runtime integer divide-by-zero errors.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
+  Defect* = object of Exception ## \
+    ## Abstract base class for all exceptions that Nim's runtime raises
+    ## but that are strictly uncatchable as they can also be mapped to
+    ## a `quit` / `trap` / `exit` operation.
 
-  OverflowError* = object of ArithmeticError ## \
-    ## Raised for runtime integer overflows.
-    ##
-    ## This happens for calculations whose results are too large to fit in the
-    ## provided bits.  See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  AccessViolationError* = object of Exception ## \
-    ## Raised for invalid memory access errors
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  AssertionError* = object of Exception ## \
-    ## Raised when assertion is proved wrong.
-    ##
-    ## Usually the result of using the `assert() template <#assert>`_.  See the
-    ## full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  ValueError* = object of Exception ## \
-    ## Raised for string and object conversion errors.
-  KeyError* = object of ValueError ## \
-    ## Raised if a key cannot be found in a table.
-    ##
-    ## Mostly used by the `tables <tables.html>`_ module, it can also be raised
-    ## by other collection modules like `sets <sets.html>`_ or `strtabs
-    ## <strtabs.html>`_. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  OutOfMemError* = object of SystemError ## \
-    ## Raised for unsuccessful attempts to allocate memory.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  IndexError* = object of Exception ## \
-    ## Raised if an array index is out of bounds.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
+  CatchableError* = object of Exception ## \
+    ## Abstract class for all exceptions that are catchable.
 
-  FieldError* = object of Exception ## \
-    ## Raised if a record field is not accessible because its dicriminant's
-    ## value does not fit.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  RangeError* = object of Exception ## \
-    ## Raised if a range check error occurred.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  StackOverflowError* = object of SystemError ## \
-    ## Raised if the hardware stack used for subroutine calls overflowed.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  ReraiseError* = object of Exception ## \
-    ## Raised if there is no exception to reraise.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  ObjectAssignmentError* = object of Exception ## \
-    ## Raised if an object gets assigned to its parent's object.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  ObjectConversionError* = object of Exception ## \
-    ## Raised if an object is converted to an incompatible object type.
-    ## You can use ``of`` operator to check if conversion will succeed.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  FloatingPointError* = object of Exception ## \
-    ## Base class for floating point exceptions.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  FloatInvalidOpError* = object of FloatingPointError ## \
-    ## Raised by invalid operations according to IEEE.
-    ##
-    ## Raised by ``0.0/0.0``, for example.  See the full `exception
-    ## hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  FloatDivByZeroError* = object of FloatingPointError ## \
-    ## Raised by division by zero.
-    ##
-    ## Divisor is zero and dividend is a finite nonzero number.  See the full
-    ## `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  FloatOverflowError* = object of FloatingPointError ## \
-    ## Raised for overflows.
-    ##
-    ## The operation produced a result that exceeds the range of the exponent.
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  FloatUnderflowError* = object of FloatingPointError ## \
-    ## Raised for underflows.
-    ##
-    ## The operation produced a result that is too small to be represented as a
-    ## normal number. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  FloatInexactError* = object of FloatingPointError ## \
-    ## Raised for inexact results.
-    ##
-    ## The operation produced a result that cannot be represented with infinite
-    ## precision -- for example: ``2.0 / 3.0, log(1.1)``
-    ##
-    ## **NOTE**: Nim currently does not detect these!  See the full
-    ## `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  DeadThreadError* = object of Exception ## \
-    ## Raised if it is attempted to send a message to a dead thread.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  NilAccessError* = object of SystemError ## \
-    ## Raised on dereferences of ``nil`` pointers.
-    ##
-    ## This is only raised if the ``segfaults.nim`` module was imported!
-
-when defined(nimNewRuntime):
-  type
-    MoveError* = object of SystemError ## \
-      ## Raised on attempts to re-sink an already consumed ``sink`` parameter.
-
-{.deprecated: [TObject: RootObj, PObject: RootRef, TEffect: RootEffect,
-  FTime: TimeEffect, FIO: IOEffect, FReadIO: ReadIOEffect,
-  FWriteIO: WriteIOEffect, FExecIO: ExecIOEffect,
-
-  E_Base: Exception, ESystem: SystemError, EIO: IOError,
-  EOS: OSError, EInvalidLibrary: LibraryError,
-  EResourceExhausted: ResourceExhaustedError,
-  EArithmetic: ArithmeticError, EDivByZero: DivByZeroError,
-  EOverflow: OverflowError, EAccessViolation: AccessViolationError,
-  EAssertionFailed: AssertionError, EInvalidValue: ValueError,
-  EInvalidKey: KeyError, EOutOfMemory: OutOfMemError,
-  EInvalidIndex: IndexError, EInvalidField: FieldError,
-  EOutOfRange: RangeError, EStackOverflow: StackOverflowError,
-  ENoExceptionToReraise: ReraiseError,
-  EInvalidObjectAssignment: ObjectAssignmentError,
-  EInvalidObjectConversion: ObjectConversionError,
-  EDeadThread: DeadThreadError,
-  EFloatInexact: FloatInexactError,
-  EFloatUnderflow: FloatUnderflowError,
-  EFloatingPoint: FloatingPointError,
-  EFloatInvalidOp: FloatInvalidOpError,
-  EFloatDivByZero: FloatDivByZeroError,
-  EFloatOverflow: FloatOverflowError,
-  ESynch: Exception
-].}
+when defined(nimIcIntegrityChecks):
+  include "system/exceptions"
+else:
+  import system/exceptions
+  export exceptions
 
 when defined(js) or defined(nimdoc):
   type
@@ -657,803 +549,535 @@ when defined(js) or defined(nimdoc):
       ## Root type of the JavaScript object hierarchy
 
 proc unsafeNew*[T](a: var ref T, size: Natural) {.magic: "New", noSideEffect.}
-  ## creates a new object of type ``T`` and returns a safe (traced)
-  ## reference to it in ``a``. This is **unsafe** as it allocates an object
-  ## of the passed ``size``. This should only be used for optimization
-  ## purposes when you know what you're doing!
-
-proc sizeof*[T](x: T): int {.magic: "SizeOf", noSideEffect.}
-  ## returns the size of ``x`` in bytes. Since this is a low-level proc,
-  ## its usage is discouraged - using ``new`` for the most cases suffices
-  ## that one never needs to know ``x``'s size. As a special semantic rule,
-  ## ``x`` may also be a type identifier (``sizeof(int)`` is valid).
+  ## Creates a new object of type `T` and returns a safe (traced)
+  ## reference to it in `a`.
   ##
-  ## Limitations: If used within nim VM context ``sizeof`` will only work
-  ## for simple types.
+  ## This is **unsafe** as it allocates an object of the passed `size`.
+  ## This should only be used for optimization purposes when you know
+  ## what you're doing!
   ##
-  ## .. code-block:: nim
-  ##  sizeof('A') #=> 1
-  ##  sizeof(2) #=> 8
-
-when defined(nimtypedescfixed):
-  proc sizeof*(x: typedesc): int {.magic: "SizeOf", noSideEffect.}
+  ## See also:
+  ## * `new <#new,ref.T,proc(ref.T)>`_
 
-proc `<`*[T](x: Ordinal[T]): T {.magic: "UnaryLt", noSideEffect, deprecated.}
-  ## unary ``<`` that can be used for nice looking excluding ranges:
-  ##
-  ## .. code-block:: nim
-  ##   for i in 0 .. <10: echo i #=> 0 1 2 3 4 5 6 7 8 9
-  ##
-  ## Semantically this is the same as ``pred``.
+proc sizeof*[T](x: T): int {.magic: "SizeOf", noSideEffect.}
+  ## Returns the size of `x` in bytes.
   ##
-  ## **Deprecated since version 0.18.0**. For the common excluding range
-  ## write ``0 ..< 10`` instead of ``0 .. < 10`` (look at the spacing).
-  ## For ``<x`` write ``pred(x)``.
-
-proc succ*[T: Ordinal](x: T, y = 1): T {.magic: "Succ", noSideEffect.}
-  ## returns the ``y``-th successor of the value ``x``. ``T`` has to be
-  ## an ordinal type. If such a value does not exist, ``EOutOfRange`` is raised
-  ## or a compile time error occurs.
-
-proc pred*[T: Ordinal](x: T, y = 1): T {.magic: "Pred", noSideEffect.}
-  ## returns the ``y``-th predecessor of the value ``x``. ``T`` has to be
-  ## an ordinal type. If such a value does not exist, ``EOutOfRange`` is raised
-  ## or a compile time error occurs.
-
-proc inc*[T: Ordinal|uint|uint64](x: var T, y = 1) {.magic: "Inc", noSideEffect.}
-  ## increments the ordinal ``x`` by ``y``. If such a value does not
-  ## exist, ``EOutOfRange`` is raised or a compile time error occurs. This is a
-  ## short notation for: ``x = succ(x, y)``.
+  ## Since this is a low-level proc,
+  ## its usage is discouraged - using `new <#new,ref.T,proc(ref.T)>`_ for
+  ## the most cases suffices that one never needs to know `x`'s size.
   ##
-  ## .. code-block:: nim
-  ##  var i = 2
-  ##  inc(i) #=> 3
-  ##  inc(i, 3) #=> 6
-
-proc dec*[T: Ordinal|uint|uint64](x: var T, y = 1) {.magic: "Dec", noSideEffect.}
-  ## decrements the ordinal ``x`` by ``y``. If such a value does not
-  ## exist, ``EOutOfRange`` is raised or a compile time error occurs. This is a
-  ## short notation for: ``x = pred(x, y)``.
+  ## As a special semantic rule, `x` may also be a type identifier
+  ## (`sizeof(int)` is valid).
   ##
-  ## .. code-block:: nim
-  ##  var i = 2
-  ##  dec(i) #=> 1
-  ##  dec(i, 3) #=> -2
+  ## Limitations: If used for types that are imported from C or C++,
+  ## sizeof should fallback to the `sizeof` in the C compiler. The
+  ## result isn't available for the Nim compiler and therefore can't
+  ## be used inside of macros.
+  ##   ```nim
+  ##   sizeof('A') # => 1
+  ##   sizeof(2) # => 8
+  ##   ```
+
+proc alignof*[T](x: T): int {.magic: "AlignOf", noSideEffect.}
+proc alignof*(x: typedesc): int {.magic: "AlignOf", noSideEffect.}
+
+proc offsetOfDotExpr(typeAccess: typed): int {.magic: "OffsetOf", noSideEffect, compileTime.}
+
+template offsetOf*[T](t: typedesc[T]; member: untyped): int =
+  var tmp {.noinit.}: ptr T
+  offsetOfDotExpr(tmp[].member)
+
+template offsetOf*[T](value: T; member: untyped): int =
+  offsetOfDotExpr(value.member)
+
+#proc offsetOf*(memberaccess: typed): int {.magic: "OffsetOf", noSideEffect.}
+
+proc sizeof*(x: typedesc): int {.magic: "SizeOf", noSideEffect.}
+
 
 proc newSeq*[T](s: var seq[T], len: Natural) {.magic: "NewSeq", noSideEffect.}
-  ## creates a new sequence of type ``seq[T]`` with length ``len``.
-  ## This is equivalent to ``s = @[]; setlen(s, len)``, but more
+  ## Creates a new sequence of type `seq[T]` with length `len`.
+  ##
+  ## This is equivalent to `s = @[]; setlen(s, len)`, but more
   ## efficient since no reallocation is needed.
   ##
-  ## Note that the sequence will be filled with zeroed entries, which can be a
-  ## problem for sequences containing strings since their value will be
-  ## ``nil``. After the creation of the sequence you should assign entries to
+  ## Note that the sequence will be filled with zeroed entries.
+  ## After the creation of the sequence you should assign entries to
   ## the sequence instead of adding them. Example:
-  ##
-  ## .. code-block:: nim
-  ##   var inputStrings : seq[string]
+  ##   ```nim
+  ##   var inputStrings: seq[string]
   ##   newSeq(inputStrings, 3)
+  ##   assert len(inputStrings) == 3
   ##   inputStrings[0] = "The fourth"
   ##   inputStrings[1] = "assignment"
   ##   inputStrings[2] = "would crash"
   ##   #inputStrings[3] = "out of bounds"
+  ##   ```
 
 proc newSeq*[T](len = 0.Natural): seq[T] =
-  ## creates a new sequence of type ``seq[T]`` with length ``len``.
-  ##
-  ## Note that the sequence will be filled with zeroed entries, which can be a
-  ## problem for sequences containing strings since their value will be
-  ## ``nil``. After the creation of the sequence you should assign entries to
-  ## the sequence instead of adding them. Example:
+  ## Creates a new sequence of type `seq[T]` with length `len`.
   ##
-  ## .. code-block:: nim
+  ## Note that the sequence will be filled with zeroed entries.
+  ## After the creation of the sequence you should assign entries to
+  ## the sequence instead of adding them.
+  ##   ```nim
   ##   var inputStrings = newSeq[string](3)
+  ##   assert len(inputStrings) == 3
   ##   inputStrings[0] = "The fourth"
   ##   inputStrings[1] = "assignment"
   ##   inputStrings[2] = "would crash"
   ##   #inputStrings[3] = "out of bounds"
+  ##   ```
+  ##
+  ## See also:
+  ## * `newSeqOfCap <#newSeqOfCap,Natural>`_
+  ## * `newSeqUninit <#newSeqUninit,Natural>`_
   newSeq(result, len)
 
 proc newSeqOfCap*[T](cap: Natural): seq[T] {.
   magic: "NewSeqOfCap", noSideEffect.} =
-  ## creates a new sequence of type ``seq[T]`` with length 0 and capacity
-  ## ``cap``.
+  ## Creates a new sequence of type `seq[T]` with length zero and capacity
+  ## `cap`. Example:
+  ##   ```nim
+  ##   var x = newSeqOfCap[int](5)
+  ##   assert len(x) == 0
+  ##   x.add(10)
+  ##   assert len(x) == 1
+  ##   ```
   discard
 
-when not defined(JS):
-  proc newSeqUninitialized*[T: SomeNumber](len: Natural): seq[T] =
-    ## creates a new sequence of type ``seq[T]`` with length ``len``.
-    ##
-    ## Only available for numbers types. Note that the sequence will be
-    ## uninitialized. After the creation of the sequence you should assign
-    ## entries to the sequence instead of adding them.
-
-    result = newSeqOfCap[T](len)
-    var s = cast[PGenericSeq](result)
-    s.len = len
-
-proc len*[TOpenArray: openArray|varargs](x: TOpenArray): int {.
-  magic: "LengthOpenArray", noSideEffect.}
-proc len*(x: string): int {.magic: "LengthStr", noSideEffect.}
-proc len*(x: cstring): int {.magic: "LengthStr", noSideEffect.}
-proc len*(x: (type array)|array): int {.magic: "LengthArray", noSideEffect.}
-proc len*[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.}
-  ## returns the length of an array, an openarray, a sequence or a string.
-  ## This is roughly the same as ``high(T)-low(T)+1``, but its resulting type is
-  ## always an int.
-  ##
-  ## .. code-block:: nim
-  ##  var arr = [1,1,1,1,1]
-  ##  len(arr) #=> 5
-  ##  for i in 0..<arr.len:
-  ##    echo arr[i] #=> 1,1,1,1,1
-
-# set routines:
-proc incl*[T](x: var set[T], y: T) {.magic: "Incl", noSideEffect.}
-  ## includes element ``y`` to the set ``x``. This is the same as
-  ## ``x = x + {y}``, but it might be more efficient.
-  ##
-  ## .. code-block:: nim
-  ##  var a = initSet[int](4)
-  ##  a.incl(2) #=> {2}
-  ##  a.incl(3) #=> {2, 3}
-
-template incl*[T](s: var set[T], flags: set[T]) =
-  ## includes the set of flags to the set ``x``.
-  s = s + flags
-
-proc excl*[T](x: var set[T], y: T) {.magic: "Excl", noSideEffect.}
-  ## excludes element ``y`` to the set ``x``. This is the same as
-  ## ``x = x - {y}``, but it might be more efficient.
-  ##
-  ## .. code-block:: nim
-  ##  var b = {2,3,5,6,12,545}
-  ##  b.excl(5)  #=> {2,3,6,12,545}
-
-template excl*[T](s: var set[T], flags: set[T]) =
-  ## excludes the set of flags to ``x``.
-  s = s - flags
+func len*[TOpenArray: openArray|varargs](x: TOpenArray): int {.magic: "LengthOpenArray".} =
+  ## Returns the length of an openArray.
+  runnableExamples:
+    proc bar[T](a: openArray[T]): int = len(a)
+    assert bar([1,2]) == 2
+    assert [1,2].len == 2
 
-proc card*[T](x: set[T]): int {.magic: "Card", noSideEffect.}
-  ## returns the cardinality of the set ``x``, i.e. the number of elements
-  ## in the set.
-  ##
-  ## .. code-block:: nim
-  ##  var i = {1,2,3,4}
-  ##  card(i) #=> 4
+func len*(x: string): int {.magic: "LengthStr".} =
+  ## Returns the length of a string.
+  runnableExamples:
+    assert "abc".len == 3
+    assert "".len == 0
+    assert string.default.len == 0
+
+proc len*(x: cstring): int {.magic: "LengthStr", noSideEffect.} =
+  ## Returns the length of a compatible string. This is an O(n) operation except
+  ## in js at runtime.
+  ##
+  ## **Note:** On the JS backend this currently counts UTF-16 code points
+  ## instead of bytes at runtime (not at compile time). For now, if you
+  ## need the byte length of the UTF-8 encoding, convert to string with
+  ## `$` first then call `len`.
+  runnableExamples:
+    doAssert len(cstring"abc") == 3
+    doAssert len(cstring r"ab\0c") == 5 # \0 is escaped
+    doAssert len(cstring"ab\0c") == 5 # ditto
+    var a: cstring = "ab\0c"
+    when defined(js): doAssert a.len == 4 # len ignores \0 for js
+    else: doAssert a.len == 2 # \0 is a null terminator
+    static:
+      var a2: cstring = "ab\0c"
+      doAssert a2.len == 2 # \0 is a null terminator, even in js vm
+
+func len*(x: (type array)|array): int {.magic: "LengthArray".} =
+  ## Returns the length of an array or an array type.
+  ## This is roughly the same as `high(T)-low(T)+1`.
+  runnableExamples:
+    var a = [1, 1, 1]
+    assert a.len == 3
+    assert array[0, float].len == 0
+    static: assert array[-2..2, float].len == 5
 
-proc ord*[T](x: T): int {.magic: "Ord", noSideEffect.}
-  ## returns the internal int value of an ordinal value ``x``.
-  ##
-  ## .. code-block:: nim
-  ##  ord('A') #=> 65
+func len*[T](x: seq[T]): int {.magic: "LengthSeq".} =
+  ## Returns the length of `x`.
+  runnableExamples:
+    assert @[0, 1].len == 2
+    assert seq[int].default.len == 0
+    assert newSeq[int](3).len == 3
+    let s = newSeqOfCap[int](3)
+    assert s.len == 0
+  # xxx this gives cgen error: assert newSeqOfCap[int](3).len == 0
+
+func ord*[T: Ordinal|enum](x: T): int {.magic: "Ord".} =
+  ## Returns the internal `int` value of `x`, including for enum with holes
+  ## and distinct ordinal types.
+  runnableExamples:
+    assert ord('A') == 65
+    type Foo = enum
+      f0 = 0, f1 = 3
+    assert f1.ord == 3
+    type Bar = distinct int
+    assert 3.Bar.ord == 3
+
+func chr*(u: range[0..255]): char {.magic: "Chr".} =
+  ## Converts `u` to a `char`, same as `char(u)`.
+  runnableExamples:
+    doAssert chr(65) == 'A'
+    doAssert chr(255) == '\255'
+    doAssert chr(255) == char(255)
+    doAssert not compiles chr(256)
+    doAssert not compiles char(256)
+    var x = 256
+    doAssertRaises(RangeDefect): discard chr(x)
+    doAssertRaises(RangeDefect): discard char(x)
 
-proc chr*(u: range[0..255]): char {.magic: "Chr", noSideEffect.}
-  ## converts an int in the range 0..255 to a character.
-  ##
-  ## .. code-block:: nim
-  ##  chr(65) #=> A
-
-# --------------------------------------------------------------------------
-# built-in operators
-
-when not defined(JS):
-  proc ze*(x: int8): int {.magic: "Ze8ToI", noSideEffect.}
-    ## zero extends a smaller integer type to ``int``. This treats `x` as
-    ## unsigned.
-  proc ze*(x: int16): int {.magic: "Ze16ToI", noSideEffect.}
-    ## zero extends a smaller integer type to ``int``. This treats `x` as
-    ## unsigned.
-
-  proc ze64*(x: int8): int64 {.magic: "Ze8ToI64", noSideEffect.}
-    ## zero extends a smaller integer type to ``int64``. This treats `x` as
-    ## unsigned.
-  proc ze64*(x: int16): int64 {.magic: "Ze16ToI64", noSideEffect.}
-    ## zero extends a smaller integer type to ``int64``. This treats `x` as
-    ## unsigned.
-
-  proc ze64*(x: int32): int64 {.magic: "Ze32ToI64", noSideEffect.}
-    ## zero extends a smaller integer type to ``int64``. This treats `x` as
-    ## unsigned.
-  proc ze64*(x: int): int64 {.magic: "ZeIToI64", noSideEffect.}
-    ## zero extends a smaller integer type to ``int64``. This treats `x` as
-    ## unsigned. Does nothing if the size of an ``int`` is the same as ``int64``.
-    ## (This is the case on 64 bit processors.)
-
-  proc toU8*(x: int): int8 {.magic: "ToU8", noSideEffect.}
-    ## treats `x` as unsigned and converts it to a byte by taking the last 8 bits
-    ## from `x`.
-  proc toU16*(x: int): int16 {.magic: "ToU16", noSideEffect.}
-    ## treats `x` as unsigned and converts it to an ``int16`` by taking the last
-    ## 16 bits from `x`.
-  proc toU32*(x: int64): int32 {.magic: "ToU32", noSideEffect.}
-    ## treats `x` as unsigned and converts it to an ``int32`` by taking the
-    ## last 32 bits from `x`.
-
-# integer calculations:
-proc `+` *(x: int): int {.magic: "UnaryPlusI", noSideEffect.}
-proc `+` *(x: int8): int8 {.magic: "UnaryPlusI", noSideEffect.}
-proc `+` *(x: int16): int16 {.magic: "UnaryPlusI", noSideEffect.}
-proc `+` *(x: int32): int32 {.magic: "UnaryPlusI", noSideEffect.}
-proc `+` *(x: int64): int64 {.magic: "UnaryPlusI", noSideEffect.}
-  ## Unary `+` operator for an integer. Has no effect.
-
-proc `-` *(x: int): int {.magic: "UnaryMinusI", noSideEffect.}
-proc `-` *(x: int8): int8 {.magic: "UnaryMinusI", noSideEffect.}
-proc `-` *(x: int16): int16 {.magic: "UnaryMinusI", noSideEffect.}
-proc `-` *(x: int32): int32 {.magic: "UnaryMinusI", noSideEffect.}
-proc `-` *(x: int64): int64 {.magic: "UnaryMinusI64", noSideEffect.}
-  ## Unary `-` operator for an integer. Negates `x`.
-
-proc `not` *(x: int): int {.magic: "BitnotI", noSideEffect.}
-proc `not` *(x: int8): int8 {.magic: "BitnotI", noSideEffect.}
-proc `not` *(x: int16): int16 {.magic: "BitnotI", noSideEffect.}
-proc `not` *(x: int32): int32 {.magic: "BitnotI", noSideEffect.}
-  ## computes the `bitwise complement` of the integer `x`.
-
-when defined(nimnomagic64):
-  proc `not` *(x: int64): int64 {.magic: "BitnotI", noSideEffect.}
-else:
-  proc `not` *(x: int64): int64 {.magic: "BitnotI64", noSideEffect.}
 
-proc `+` *(x, y: int): int {.magic: "AddI", noSideEffect.}
-proc `+` *(x, y: int8): int8 {.magic: "AddI", noSideEffect.}
-proc `+` *(x, y: int16): int16 {.magic: "AddI", noSideEffect.}
-proc `+` *(x, y: int32): int32 {.magic: "AddI", noSideEffect.}
-  ## Binary `+` operator for an integer.
+include "system/setops"
 
-when defined(nimnomagic64):
-  proc `+` *(x, y: int64): int64 {.magic: "AddI", noSideEffect.}
-else:
-  proc `+` *(x, y: int64): int64 {.magic: "AddI64", noSideEffect.}
 
-proc `-` *(x, y: int): int {.magic: "SubI", noSideEffect.}
-proc `-` *(x, y: int8): int8 {.magic: "SubI", noSideEffect.}
-proc `-` *(x, y: int16): int16 {.magic: "SubI", noSideEffect.}
-proc `-` *(x, y: int32): int32 {.magic: "SubI", noSideEffect.}
-  ## Binary `-` operator for an integer.
+proc contains*[U, V, W](s: HSlice[U, V], value: W): bool {.noSideEffect, inline.} =
+  ## Checks if `value` is within the range of `s`; returns true if
+  ## `value >= s.a and value <= s.b`.
+  ##   ```nim
+  ##   assert((1..3).contains(1) == true)
+  ##   assert((1..3).contains(2) == true)
+  ##   assert((1..3).contains(4) == false)
+  ##   ```
+  result = s.a <= value and value <= s.b
 
-when defined(nimnomagic64):
-  proc `-` *(x, y: int64): int64 {.magic: "SubI", noSideEffect.}
-else:
-  proc `-` *(x, y: int64): int64 {.magic: "SubI64", noSideEffect.}
+when not defined(nimHasCallsitePragma):
+  {.pragma: callsite.}
 
-proc `*` *(x, y: int): int {.magic: "MulI", noSideEffect.}
-proc `*` *(x, y: int8): int8 {.magic: "MulI", noSideEffect.}
-proc `*` *(x, y: int16): int16 {.magic: "MulI", noSideEffect.}
-proc `*` *(x, y: int32): int32 {.magic: "MulI", noSideEffect.}
-  ## Binary `*` operator for an integer.
+template `in`*(x, y: untyped): untyped {.dirty, callsite.} = contains(y, x)
+  ## Sugar for `contains`.
+  ##   ```nim
+  ##   assert(1 in (1..3) == true)
+  ##   assert(5 in (1..3) == false)
+  ##   ```
+template `notin`*(x, y: untyped): untyped {.dirty, callsite.} = not contains(y, x)
+  ## Sugar for `not contains`.
+  ##   ```nim
+  ##   assert(1 notin (1..3) == false)
+  ##   assert(5 notin (1..3) == true)
+  ##   ```
 
-when defined(nimnomagic64):
-  proc `*` *(x, y: int64): int64 {.magic: "MulI", noSideEffect.}
-else:
-  proc `*` *(x, y: int64): int64 {.magic: "MulI64", noSideEffect.}
-
-proc `div` *(x, y: int): int {.magic: "DivI", noSideEffect.}
-proc `div` *(x, y: int8): int8 {.magic: "DivI", noSideEffect.}
-proc `div` *(x, y: int16): int16 {.magic: "DivI", noSideEffect.}
-proc `div` *(x, y: int32): int32 {.magic: "DivI", noSideEffect.}
-  ## computes the integer division. This is roughly the same as
-  ## ``trunc(x/y)``.
+proc `is`*[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.}
+  ## Checks if `T` is of the same type as `S`.
   ##
-  ## .. code-block:: Nim
-  ##   1 div 2 == 0
-  ##   2 div 2 == 1
-  ##   3 div 2 == 1
-  ##   7 div 5 == 1
-
-when defined(nimnomagic64):
-  proc `div` *(x, y: int64): int64 {.magic: "DivI", noSideEffect.}
-else:
-  proc `div` *(x, y: int64): int64 {.magic: "DivI64", noSideEffect.}
-
-proc `mod` *(x, y: int): int {.magic: "ModI", noSideEffect.}
-proc `mod` *(x, y: int8): int8 {.magic: "ModI", noSideEffect.}
-proc `mod` *(x, y: int16): int16 {.magic: "ModI", noSideEffect.}
-proc `mod` *(x, y: int32): int32 {.magic: "ModI", noSideEffect.}
-  ## computes the integer modulo operation (remainder).
-  ## This is the same as
-  ## ``x - (x div y) * y``.
-  ##
-  ## .. code-block:: Nim
-  ##   (7 mod 5) == 2
-
-when defined(nimnomagic64):
-  proc `mod` *(x, y: int64): int64 {.magic: "ModI", noSideEffect.}
-else:
-  proc `mod` *(x, y: int64): int64 {.magic: "ModI64", noSideEffect.}
-
-when defined(nimNewShiftOps):
-  proc `shr` *(x: int, y: SomeInteger): int {.magic: "ShrI", noSideEffect.}
-  proc `shr` *(x: int8, y: SomeInteger): int8 {.magic: "ShrI", noSideEffect.}
-  proc `shr` *(x: int16, y: SomeInteger): int16 {.magic: "ShrI", noSideEffect.}
-  proc `shr` *(x: int32, y: SomeInteger): int32 {.magic: "ShrI", noSideEffect.}
-  proc `shr` *(x: int64, y: SomeInteger): int64 {.magic: "ShrI", noSideEffect.}
-    ## computes the `shift right` operation of `x` and `y`, filling
-    ## vacant bit positions with zeros.
-    ##
-    ## .. code-block:: Nim
-    ##   0b0001_0000'i8 shr 2 == 0b0000_0100'i8
-    ##   0b1000_0000'i8 shr 8 == 0b0000_0000'i8
-    ##   0b0000_0001'i8 shr 1 == 0b0000_0000'i8
-
-
-  proc `shl` *(x: int, y: SomeInteger): int {.magic: "ShlI", noSideEffect.}
-  proc `shl` *(x: int8, y: SomeInteger): int8 {.magic: "ShlI", noSideEffect.}
-  proc `shl` *(x: int16, y: SomeInteger): int16 {.magic: "ShlI", noSideEffect.}
-  proc `shl` *(x: int32, y: SomeInteger): int32 {.magic: "ShlI", noSideEffect.}
-  proc `shl` *(x: int64, y: SomeInteger): int64 {.magic: "ShlI", noSideEffect.}
-    ## computes the `shift left` operation of `x` and `y`.
-    ##
-    ## .. code-block:: Nim
-    ##  1'i32 shl 4 == 0x0000_0010
-    ##  1'i64 shl 4 == 0x0000_0000_0000_0010
-else:
-  proc `shr` *(x, y: int): int {.magic: "ShrI", noSideEffect.}
-  proc `shr` *(x, y: int8): int8 {.magic: "ShrI", noSideEffect.}
-  proc `shr` *(x, y: int16): int16 {.magic: "ShrI", noSideEffect.}
-  proc `shr` *(x, y: int32): int32 {.magic: "ShrI", noSideEffect.}
-  proc `shr` *(x, y: int64): int64 {.magic: "ShrI", noSideEffect.}
-
-  proc `shl` *(x, y: int): int {.magic: "ShlI", noSideEffect.}
-  proc `shl` *(x, y: int8): int8 {.magic: "ShlI", noSideEffect.}
-  proc `shl` *(x, y: int16): int16 {.magic: "ShlI", noSideEffect.}
-  proc `shl` *(x, y: int32): int32 {.magic: "ShlI", noSideEffect.}
-  proc `shl` *(x, y: int64): int64 {.magic: "ShlI", noSideEffect.}
-
-proc `and` *(x, y: int): int {.magic: "BitandI", noSideEffect.}
-proc `and` *(x, y: int8): int8 {.magic: "BitandI", noSideEffect.}
-proc `and` *(x, y: int16): int16 {.magic: "BitandI", noSideEffect.}
-proc `and` *(x, y: int32): int32 {.magic: "BitandI", noSideEffect.}
-proc `and` *(x, y: int64): int64 {.magic: "BitandI", noSideEffect.}
-  ## computes the `bitwise and` of numbers `x` and `y`.
+  ## For a negated version, use `isnot <#isnot.t,untyped,untyped>`_.
   ##
-  ## .. code-block:: Nim
-  ##  (0xffff'i16 and 0x0010'i16) == 0x0010
-
-proc `or` *(x, y: int): int {.magic: "BitorI", noSideEffect.}
-proc `or` *(x, y: int8): int8 {.magic: "BitorI", noSideEffect.}
-proc `or` *(x, y: int16): int16 {.magic: "BitorI", noSideEffect.}
-proc `or` *(x, y: int32): int32 {.magic: "BitorI", noSideEffect.}
-proc `or` *(x, y: int64): int64 {.magic: "BitorI", noSideEffect.}
-  ## computes the `bitwise or` of numbers `x` and `y`.
+  ##   ```nim
+  ##   assert 42 is int
+  ##   assert @[1, 2] is seq
   ##
-  ## .. code-block:: Nim
-  ##  (0x0005'i16 or 0x0010'i16) == 0x0015
-
-proc `xor` *(x, y: int): int {.magic: "BitxorI", noSideEffect.}
-proc `xor` *(x, y: int8): int8 {.magic: "BitxorI", noSideEffect.}
-proc `xor` *(x, y: int16): int16 {.magic: "BitxorI", noSideEffect.}
-proc `xor` *(x, y: int32): int32 {.magic: "BitxorI", noSideEffect.}
-proc `xor` *(x, y: int64): int64 {.magic: "BitxorI", noSideEffect.}
-  ## computes the `bitwise xor` of numbers `x` and `y`.
+  ##   proc test[T](a: T): int =
+  ##     when (T is int):
+  ##       return a
+  ##     else:
+  ##       return 0
   ##
-  ## .. code-block:: Nim
-  ##  (0x1011'i16 xor 0x0101'i16) == 0x1110
-
-proc `==` *(x, y: int): bool {.magic: "EqI", noSideEffect.}
-proc `==` *(x, y: int8): bool {.magic: "EqI", noSideEffect.}
-proc `==` *(x, y: int16): bool {.magic: "EqI", noSideEffect.}
-proc `==` *(x, y: int32): bool {.magic: "EqI", noSideEffect.}
-proc `==` *(x, y: int64): bool {.magic: "EqI", noSideEffect.}
-  ## Compares two integers for equality.
-
-proc `<=` *(x, y: int): bool {.magic: "LeI", noSideEffect.}
-proc `<=` *(x, y: int8): bool {.magic: "LeI", noSideEffect.}
-proc `<=` *(x, y: int16): bool {.magic: "LeI", noSideEffect.}
-proc `<=` *(x, y: int32): bool {.magic: "LeI", noSideEffect.}
-proc `<=` *(x, y: int64): bool {.magic: "LeI", noSideEffect.}
-  ## Returns true iff `x` is less than or equal to `y`.
-
-proc `<` *(x, y: int): bool {.magic: "LtI", noSideEffect.}
-proc `<` *(x, y: int8): bool {.magic: "LtI", noSideEffect.}
-proc `<` *(x, y: int16): bool {.magic: "LtI", noSideEffect.}
-proc `<` *(x, y: int32): bool {.magic: "LtI", noSideEffect.}
-proc `<` *(x, y: int64): bool {.magic: "LtI", noSideEffect.}
-  ## Returns true iff `x` is less than `y`.
-
-type
-  IntMax32 = int|int8|int16|int32
-
-proc `+%` *(x, y: IntMax32): IntMax32 {.magic: "AddU", noSideEffect.}
-proc `+%` *(x, y: int64): int64 {.magic: "AddU", noSideEffect.}
-  ## treats `x` and `y` as unsigned and adds them. The result is truncated to
-  ## fit into the result. This implements modulo arithmetic. No overflow
-  ## errors are possible.
-
-proc `-%` *(x, y: IntMax32): IntMax32 {.magic: "SubU", noSideEffect.}
-proc `-%` *(x, y: int64): int64 {.magic: "SubU", noSideEffect.}
-  ## treats `x` and `y` as unsigned and subtracts them. The result is
-  ## truncated to fit into the result. This implements modulo arithmetic.
-  ## No overflow errors are possible.
-
-proc `*%` *(x, y: IntMax32): IntMax32 {.magic: "MulU", noSideEffect.}
-proc `*%` *(x, y: int64): int64 {.magic: "MulU", noSideEffect.}
-  ## treats `x` and `y` as unsigned and multiplies them. The result is
-  ## truncated to fit into the result. This implements modulo arithmetic.
-  ## No overflow errors are possible.
-
-proc `/%` *(x, y: IntMax32): IntMax32 {.magic: "DivU", noSideEffect.}
-proc `/%` *(x, y: int64): int64 {.magic: "DivU", noSideEffect.}
-  ## treats `x` and `y` as unsigned and divides them. The result is
-  ## truncated to fit into the result. This implements modulo arithmetic.
-  ## No overflow errors are possible.
-
-proc `%%` *(x, y: IntMax32): IntMax32 {.magic: "ModU", noSideEffect.}
-proc `%%` *(x, y: int64): int64 {.magic: "ModU", noSideEffect.}
-  ## treats `x` and `y` as unsigned and compute the modulo of `x` and `y`.
-  ## The result is truncated to fit into the result.
-  ## This implements modulo arithmetic.
-  ## No overflow errors are possible.
-
-proc `<=%` *(x, y: IntMax32): bool {.magic: "LeU", noSideEffect.}
-proc `<=%` *(x, y: int64): bool {.magic: "LeU64", noSideEffect.}
-  ## treats `x` and `y` as unsigned and compares them.
-  ## Returns true iff ``unsigned(x) <= unsigned(y)``.
-
-proc `<%` *(x, y: IntMax32): bool {.magic: "LtU", noSideEffect.}
-proc `<%` *(x, y: int64): bool {.magic: "LtU64", noSideEffect.}
-  ## treats `x` and `y` as unsigned and compares them.
-  ## Returns true iff ``unsigned(x) < unsigned(y)``.
-
-# unsigned integer operations:
-proc `not`*[T: SomeUnsignedInt](x: T): T {.magic: "BitnotI", noSideEffect.}
-  ## computes the `bitwise complement` of the integer `x`.
-
-when defined(nimNewShiftOps):
-  proc `shr`*[T: SomeUnsignedInt](x: T, y: SomeInteger): T {.magic: "ShrI", noSideEffect.}
-    ## computes the `shift right` operation of `x` and `y`.
-
-  proc `shl`*[T: SomeUnsignedInt](x: T, y: SomeInteger): T {.magic: "ShlI", noSideEffect.}
-    ## computes the `shift left` operation of `x` and `y`.
+  ##   assert(test[int](3) == 3)
+  ##   assert(test[string]("xyz") == 0)
+  ##   ```
+template `isnot`*(x, y: untyped): untyped {.callsite.} = not (x is y)
+  ## Negated version of `is <#is,T,S>`_. Equivalent to `not(x is y)`.
+  ##   ```nim
+  ##   assert 42 isnot float
+  ##   assert @[1, 2] isnot enum
+  ##   ```
+
+when (defined(nimOwnedEnabled) and not defined(nimscript)) or defined(nimFixedOwned):
+  type owned*[T]{.magic: "BuiltinType".} ## type constructor to mark a ref/ptr or a closure as `owned`.
 else:
-  proc `shr`*[T: SomeUnsignedInt](x, y: T): T {.magic: "ShrI", noSideEffect.}
-    ## computes the `shift right` operation of `x` and `y`.
+  template owned*(t: typedesc): typedesc = t
 
-  proc `shl`*[T: SomeUnsignedInt](x, y: T): T {.magic: "ShlI", noSideEffect.}
-    ## computes the `shift left` operation of `x` and `y`.
+when defined(nimOwnedEnabled) and not defined(nimscript):
+  proc new*[T](a: var owned(ref T)) {.magic: "New", noSideEffect.}
+    ## Creates a new object of type `T` and returns a safe (traced)
+    ## reference to it in `a`.
 
-proc `and`*[T: SomeUnsignedInt](x, y: T): T {.magic: "BitandI", noSideEffect.}
-  ## computes the `bitwise and` of numbers `x` and `y`.
+  proc new*(t: typedesc): auto =
+    ## Creates a new object of type `T` and returns a safe (traced)
+    ## reference to it as result value.
+    ##
+    ## When `T` is a ref type then the resulting type will be `T`,
+    ## otherwise it will be `ref T`.
+    when (t is ref):
+      var r: owned t
+    else:
+      var r: owned(ref t)
+    new(r)
+    return r
 
-proc `or`*[T: SomeUnsignedInt](x, y: T): T {.magic: "BitorI", noSideEffect.}
-  ## computes the `bitwise or` of numbers `x` and `y`.
+  proc unown*[T](x: T): T {.magic: "Unown", noSideEffect.}
+    ## Use the expression `x` ignoring its ownership attribute.
 
-proc `xor`*[T: SomeUnsignedInt](x, y: T): T {.magic: "BitxorI", noSideEffect.}
-  ## computes the `bitwise xor` of numbers `x` and `y`.
 
-proc `==`*[T: SomeUnsignedInt](x, y: T): bool {.magic: "EqI", noSideEffect.}
-  ## Compares two unsigned integers for equality.
+else:
+  template unown*(x: typed): untyped = x
 
-proc `+`*[T: SomeUnsignedInt](x, y: T): T {.magic: "AddU", noSideEffect.}
-  ## Binary `+` operator for unsigned integers.
+  proc new*[T](a: var ref T) {.magic: "New", noSideEffect.}
+    ## Creates a new object of type `T` and returns a safe (traced)
+    ## reference to it in `a`.
 
-proc `-`*[T: SomeUnsignedInt](x, y: T): T {.magic: "SubU", noSideEffect.}
-  ## Binary `-` operator for unsigned integers.
+  proc new*(t: typedesc): auto =
+    ## Creates a new object of type `T` and returns a safe (traced)
+    ## reference to it as result value.
+    ##
+    ## When `T` is a ref type then the resulting type will be `T`,
+    ## otherwise it will be `ref T`.
+    when (t is ref):
+      var r: t
+    else:
+      var r: ref t
+    new(r)
+    return r
 
-proc `*`*[T: SomeUnsignedInt](x, y: T): T {.magic: "MulU", noSideEffect.}
-  ## Binary `*` operator for unsigned integers.
 
-proc `div`*[T: SomeUnsignedInt](x, y: T): T {.magic: "DivU", noSideEffect.}
-  ## computes the integer division. This is roughly the same as
-  ## ``trunc(x/y)``.
-  ##
-  ## .. code-block:: Nim
-  ##  (7 div 5) == 1
+template disarm*(x: typed) =
+  ## Useful for `disarming` dangling pointers explicitly for `--newruntime`.
+  ## Regardless of whether `--newruntime` is used or not
+  ## this sets the pointer or callback `x` to `nil`. This is an
+  ## experimental API!
+  x = nil
 
-proc `mod`*[T: SomeUnsignedInt](x, y: T): T {.magic: "ModU", noSideEffect.}
-  ## computes the integer modulo operation (remainder).
-  ## This is the same as
-  ## ``x - (x div y) * y``.
-  ##
-  ## .. code-block:: Nim
-  ##   (7 mod 5) == 2
-
-proc `<=`*[T: SomeUnsignedInt](x, y: T): bool {.magic: "LeU", noSideEffect.}
-  ## Returns true iff ``x <= y``.
-
-proc `<`*[T: SomeUnsignedInt](x, y: T): bool {.magic: "LtU", noSideEffect.}
-  ## Returns true iff ``unsigned(x) < unsigned(y)``.
-
-# floating point operations:
-proc `+` *(x: float32): float32 {.magic: "UnaryPlusF64", noSideEffect.}
-proc `-` *(x: float32): float32 {.magic: "UnaryMinusF64", noSideEffect.}
-proc `+` *(x, y: float32): float32 {.magic: "AddF64", noSideEffect.}
-proc `-` *(x, y: float32): float32 {.magic: "SubF64", noSideEffect.}
-proc `*` *(x, y: float32): float32 {.magic: "MulF64", noSideEffect.}
-proc `/` *(x, y: float32): float32 {.magic: "DivF64", noSideEffect.}
-
-proc `+` *(x: float): float {.magic: "UnaryPlusF64", noSideEffect.}
-proc `-` *(x: float): float {.magic: "UnaryMinusF64", noSideEffect.}
-proc `+` *(x, y: float): float {.magic: "AddF64", noSideEffect.}
-proc `-` *(x, y: float): float {.magic: "SubF64", noSideEffect.}
-proc `*` *(x, y: float): float {.magic: "MulF64", noSideEffect.}
-proc `/` *(x, y: float): float {.magic: "DivF64", noSideEffect.}
-  ## computes the floating point division
-
-proc `==` *(x, y: float32): bool {.magic: "EqF64", noSideEffect.}
-proc `<=` *(x, y: float32): bool {.magic: "LeF64", noSideEffect.}
-proc `<`  *(x, y: float32): bool {.magic: "LtF64", noSideEffect.}
-
-proc `==` *(x, y: float): bool {.magic: "EqF64", noSideEffect.}
-proc `<=` *(x, y: float): bool {.magic: "LeF64", noSideEffect.}
-proc `<`  *(x, y: float): bool {.magic: "LtF64", noSideEffect.}
-
-# set operators
-proc `*` *[T](x, y: set[T]): set[T] {.magic: "MulSet", noSideEffect.}
-  ## This operator computes the intersection of two sets.
-proc `+` *[T](x, y: set[T]): set[T] {.magic: "PlusSet", noSideEffect.}
-  ## This operator computes the union of two sets.
-proc `-` *[T](x, y: set[T]): set[T] {.magic: "MinusSet", noSideEffect.}
-  ## This operator computes the difference of two sets.
-
-proc contains*[T](x: set[T], y: T): bool {.magic: "InSet", noSideEffect.}
-  ## One should overload this proc if one wants to overload the ``in`` operator.
-  ## The parameters are in reverse order! ``a in b`` is a template for
-  ## ``contains(b, a)``.
-  ## This is because the unification algorithm that Nim uses for overload
-  ## resolution works from left to right.
-  ## But for the ``in`` operator that would be the wrong direction for this
-  ## piece of code:
-  ##
-  ## .. code-block:: Nim
-  ##   var s: set[range['a'..'z']] = {'a'..'c'}
-  ##   writeLine(stdout, 'b' in s)
-  ##
-  ## If ``in`` had been declared as ``[T](elem: T, s: set[T])`` then ``T`` would
-  ## have been bound to ``char``. But ``s`` is not compatible to type
-  ## ``set[char]``! The solution is to bind ``T`` to ``range['a'..'z']``. This
-  ## is achieved by reversing the parameters for ``contains``; ``in`` then
-  ## passes its arguments in reverse order.
+proc `of`*[T, S](x: T, y: typedesc[S]): bool {.magic: "Of", noSideEffect.} =
+  ## Checks if `x` is an instance of `y`.
+  runnableExamples:
+    type
+      Base = ref object of RootObj
+      Sub1 = ref object of Base
+      Sub2 = ref object of Base
+      Unrelated = ref object
+
+    var base: Base = Sub1() # downcast
+    doAssert base of Base # generates `CondTrue` (statically true)
+    doAssert base of Sub1
+    doAssert base isnot Sub1
+    doAssert not (base of Sub2)
+
+    base = Sub2() # re-assign
+    doAssert base of Sub2
+    doAssert Sub2(base) != nil # upcast
+    doAssertRaises(ObjectConversionDefect): discard Sub1(base)
+
+    var sub1 = Sub1()
+    doAssert sub1 of Base
+    doAssert sub1.Base of Sub1
+
+    doAssert not compiles(base of Unrelated)
+
+proc cmp*[T](x, y: T): int =
+  ## Generic compare proc.
+  ##
+  ## Returns:
+  ## * a value less than zero, if `x < y`
+  ## * a value greater than zero, if `x > y`
+  ## * zero, if `x == y`
+  ##
+  ## This is useful for writing generic algorithms without performance loss.
+  ## This generic implementation uses the `==` and `<` operators.
+  ##   ```nim
+  ##   import std/algorithm
+  ##   echo sorted(@[4, 2, 6, 5, 8, 7], cmp[int])
+  ##   ```
+  if x == y: return 0
+  if x < y: return -1
+  return 1
 
-proc contains*[U, V, W](s: HSlice[U, V], value: W): bool {.noSideEffect, inline.} =
-  ## Checks if `value` is within the range of `s`; returns true iff
-  ## `value >= s.a and value <= s.b`
+proc cmp*(x, y: string): int {.noSideEffect.}
+  ## Compare proc for strings. More efficient than the generic version.
   ##
-  ## .. code-block:: Nim
-  ##   assert((1..3).contains(1) == true)
-  ##   assert((1..3).contains(2) == true)
-  ##   assert((1..3).contains(4) == false)
-  result = s.a <= value and value <= s.b
+  ## **Note**: The precise result values depend on the used C runtime library and
+  ## can differ between operating systems!
 
-template `in` * (x, y: untyped): untyped {.dirty.} = contains(y, x)
-  ## Sugar for contains
-  ##
-  ## .. code-block:: Nim
-  ##   assert(1 in (1..3) == true)
-  ##   assert(5 in (1..3) == false)
-template `notin` * (x, y: untyped): untyped {.dirty.} = not contains(y, x)
-  ## Sugar for not containing
+proc `@`* [IDX, T](a: sink array[IDX, T]): seq[T] {.magic: "ArrToSeq", noSideEffect.}
+  ## Turns an array into a sequence.
   ##
-  ## .. code-block:: Nim
-  ##   assert(1 notin (1..3) == false)
-  ##   assert(5 notin (1..3) == true)
-
-proc `is` *[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.}
-  ## Checks if T is of the same type as S
+  ## This most often useful for constructing
+  ## sequences with the array constructor: `@[1, 2, 3]` has the type
+  ## `seq[int]`, while `[1, 2, 3]` has the type `array[0..2, int]`.
   ##
-  ## .. code-block:: Nim
-  ##   proc test[T](a: T): int =
-  ##     when (T is int):
-  ##       return a
-  ##     else:
-  ##       return 0
+  ##   ```nim
+  ##   let
+  ##     a = [1, 3, 5]
+  ##     b = "foo"
   ##
-  ##   assert(test[int](3) == 3)
-  ##   assert(test[string]("xyz") == 0)
-template `isnot` *(x, y: untyped): untyped = not (x is y)
-  ## Negated version of `is`. Equivalent to ``not(x is y)``.
+  ##   echo @a # => @[1, 3, 5]
+  ##   echo @b # => @['f', 'o', 'o']
+  ##   ```
 
-proc `of` *[T, S](x: typeDesc[T], y: typeDesc[S]): bool {.magic: "Of", noSideEffect.}
-proc `of` *[T, S](x: T, y: typeDesc[S]): bool {.magic: "Of", noSideEffect.}
-proc `of` *[T, S](x: T, y: S): bool {.magic: "Of", noSideEffect.}
-  ## Checks if `x` has a type of `y`
+proc default*[T](_: typedesc[T]): T {.magic: "Default", noSideEffect.} =
+  ## Returns the default value of the type `T`. Contrary to `zeroDefault`, it takes default fields
+  ## of an object into consideration.
   ##
-  ## .. code-block:: Nim
-  ##   assert(FloatingPointError of Exception)
-  ##   assert(DivByZeroError of Exception)
-
-proc cmp*[T](x, y: T): int {.procvar.} =
-  ## Generic compare proc. Returns a value < 0 iff x < y, a value > 0 iff x > y
-  ## and 0 iff x == y. This is useful for writing generic algorithms without
-  ## performance loss. This generic implementation uses the `==` and `<`
-  ## operators.
+  ## See also:
+  ## * `zeroDefault <#zeroDefault,typedesc[T]>`_
   ##
-  ## .. code-block:: Nim
-  ##  import algorithm
-  ##  echo sorted(@[4,2,6,5,8,7], cmp[int])
-  if x == y: return 0
-  if x < y: return -1
-  return 1
+  runnableExamples("-d:nimPreviewRangeDefault"):
+    assert (int, float).default == (0, 0.0)
+    type Foo = object
+      a: range[2..6]
+    var x = Foo.default
+    assert x.a == 2
 
-proc cmp*(x, y: string): int {.noSideEffect, procvar.}
-  ## Compare proc for strings. More efficient than the generic version.
 
-proc `@` * [IDX, T](a: array[IDX, T]): seq[T] {.
-  magic: "ArrToSeq", nosideeffect.}
-  ## turns an array into a sequence. This most often useful for constructing
-  ## sequences with the array constructor: ``@[1, 2, 3]`` has the type
-  ## ``seq[int]``, while ``[1, 2, 3]`` has the type ``array[0..2, int]``.
+proc reset*[T](obj: var T) {.noSideEffect.} =
+  ## Resets an object `obj` to its default value.
+  when nimvm:
+    obj = default(typeof(obj))
+  else:
+    when defined(gcDestructors):
+      {.cast(noSideEffect), cast(raises: []), cast(tags: []).}:
+        `=destroy`(obj)
+        `=wasMoved`(obj)
+    else:
+      obj = default(typeof(obj))
 
 proc setLen*[T](s: var seq[T], newlen: Natural) {.
-  magic: "SetLengthSeq", noSideEffect.}
-  ## sets the length of `s` to `newlen`.
-  ## ``T`` may be any sequence type.
+  magic: "SetLengthSeq", noSideEffect, nodestroy.}
+  ## Sets the length of seq `s` to `newlen`. `T` may be any sequence type.
+  ##
   ## If the current length is greater than the new length,
-  ## ``s`` will be truncated.
+  ## `s` will be truncated.
+  ##   ```nim
+  ##   var x = @[10, 20]
+  ##   x.setLen(5)
+  ##   x[4] = 50
+  ##   assert x == @[10, 20, 0, 0, 50]
+  ##   x.setLen(1)
+  ##   assert x == @[10]
+  ##   ```
 
 proc setLen*(s: var string, newlen: Natural) {.
   magic: "SetLengthStr", noSideEffect.}
-  ## sets the length of `s` to `newlen`.
-  ## If the current length is greater than the new length,
-  ## ``s`` will be truncated.
+  ## Sets the length of string `s` to `newlen`.
   ##
-  ## .. code-block:: Nim
-  ##  var myS = "Nim is great!!"
-  ##  myS.setLen(3)
-  ##  echo myS, " is fantastic!!"
+  ## If the current length is greater than the new length,
+  ## `s` will be truncated.
+  ##   ```nim
+  ##   var myS = "Nim is great!!"
+  ##   myS.setLen(3) # myS <- "Nim"
+  ##   echo myS, " is fantastic!!"
+  ##   ```
 
 proc newString*(len: Natural): string {.
   magic: "NewString", importc: "mnewString", noSideEffect.}
-  ## returns a new string of length ``len`` but with uninitialized
-  ## content. One needs to fill the string character after character
-  ## with the index operator ``s[i]``. This procedure exists only for
-  ## optimization purposes; the same effect can be achieved with the
-  ## ``&`` operator or with ``add``.
+  ## Returns a new string of length `len`.
+  ## One needs to fill the string character after character
+  ## with the index operator `s[i]`.
+  ##
+  ## This procedure exists only for optimization purposes;
+  ## the same effect can be achieved with the `&` operator or with `add`.
 
 proc newStringOfCap*(cap: Natural): string {.
   magic: "NewStringOfCap", importc: "rawNewString", noSideEffect.}
-  ## returns a new string of length ``0`` but with capacity `cap`.This
-  ## procedure exists only for optimization purposes; the same effect can
-  ## be achieved with the ``&`` operator or with ``add``.
-
-proc `&` * (x: string, y: char): string {.
-  magic: "ConStrStr", noSideEffect, merge.}
-  ## Concatenates `x` with `y`
+  ## Returns a new string of length `0` but with capacity `cap`.
   ##
-  ## .. code-block:: Nim
+  ## This procedure exists only for optimization purposes; the same effect can
+  ## be achieved with the `&` operator or with `add`.
+
+proc `&`*(x: string, y: char): string {.
+  magic: "ConStrStr", noSideEffect.}
+  ## Concatenates `x` with `y`.
+  ##   ```nim
   ##   assert("ab" & 'c' == "abc")
-proc `&` * (x, y: char): string {.
-  magic: "ConStrStr", noSideEffect, merge.}
-  ## Concatenates `x` and `y` into a string
-  ##
-  ## .. code-block:: Nim
+  ##   ```
+proc `&`*(x, y: char): string {.
+  magic: "ConStrStr", noSideEffect.}
+  ## Concatenates characters `x` and `y` into a string.
+  ##   ```nim
   ##   assert('a' & 'b' == "ab")
-proc `&` * (x, y: string): string {.
-  magic: "ConStrStr", noSideEffect, merge.}
-  ## Concatenates `x` and `y`
-  ##
-  ## .. code-block:: Nim
+  ##   ```
+proc `&`*(x, y: string): string {.
+  magic: "ConStrStr", noSideEffect.}
+  ## Concatenates strings `x` and `y`.
+  ##   ```nim
   ##   assert("ab" & "cd" == "abcd")
-proc `&` * (x: char, y: string): string {.
-  magic: "ConStrStr", noSideEffect, merge.}
-  ## Concatenates `x` with `y`
-  ##
-  ## .. code-block:: Nim
+  ##   ```
+proc `&`*(x: char, y: string): string {.
+  magic: "ConStrStr", noSideEffect.}
+  ## Concatenates `x` with `y`.
+  ##   ```nim
   ##   assert('a' & "bc" == "abc")
+  ##   ```
 
 # implementation note: These must all have the same magic value "ConStrStr" so
 # that the merge optimization works properly.
 
 proc add*(x: var string, y: char) {.magic: "AppendStrCh", noSideEffect.}
-  ## Appends `y` to `x` in place
-  ##
-  ## .. code-block:: Nim
+  ## Appends `y` to `x` in place.
+  ##   ```nim
   ##   var tmp = ""
   ##   tmp.add('a')
   ##   tmp.add('b')
   ##   assert(tmp == "ab")
-proc add*(x: var string, y: string) {.magic: "AppendStrStr", noSideEffect.}
-  ## Concatenates `x` and `y` in place
-  ##
-  ## .. code-block:: Nim
-  ##   var tmp = ""
-  ##   tmp.add("ab")
-  ##   tmp.add("cd")
-  ##   assert(tmp == "abcd")
+  ##   ```
 
+proc add*(x: var string, y: string) {.magic: "AppendStrStr", noSideEffect.} =
+  ## Concatenates `x` and `y` in place.
+  ##
+  ## See also `strbasics.add`.
+  runnableExamples:
+    var tmp = ""
+    tmp.add("ab")
+    tmp.add("cd")
+    assert tmp == "abcd"
 
 type
-  Endianness* = enum ## is a type describing the endianness of a processor.
+  Endianness* = enum ## Type describing the endianness of a processor.
     littleEndian, bigEndian
 
 const
-  isMainModule* {.magic: "IsMainModule".}: bool = false
-    ## is true only when accessed in the main module. This works thanks to
-    ## compiler magic. It is useful to embed testing code in a module.
-
-  CompileDate* {.magic: "CompileDate"}: string = "0000-00-00"
-    ## is the date of compilation as a string of the form
-    ## ``YYYY-MM-DD``. This works thanks to compiler magic.
-
-  CompileTime* {.magic: "CompileTime"}: string = "00:00:00"
-    ## is the time of compilation as a string of the form
-    ## ``HH:MM:SS``. This works thanks to compiler magic.
-
-  cpuEndian* {.magic: "CpuEndian"}: Endianness = littleEndian
-    ## is the endianness of the target CPU. This is a valuable piece of
+  cpuEndian* {.magic: "CpuEndian".}: Endianness = littleEndian
+    ## The endianness of the target CPU. This is a valuable piece of
     ## information for low-level code only. This works thanks to compiler
     ## magic.
 
   hostOS* {.magic: "HostOS".}: string = ""
-    ## a string that describes the host operating system. Possible values:
-    ## "windows", "macosx", "linux", "netbsd", "freebsd", "openbsd", "solaris",
-    ## "aix", "standalone".
+    ## A string that describes the host operating system.
+    ##
+    ## Possible values:
+    ## `"windows"`, `"macosx"`, `"linux"`, `"netbsd"`, `"freebsd"`,
+    ## `"openbsd"`, `"solaris"`, `"aix"`, `"haiku"`, `"standalone"`.
 
   hostCPU* {.magic: "HostCPU".}: string = ""
-    ## a string that describes the host CPU. Possible values:
-    ## "i386", "alpha", "powerpc", "powerpc64", "powerpc64el", "sparc",
-    ## "amd64", "mips", "mipsel", "arm", "arm64", "mips64", "mips64el",
-    ## "riscv64".
+    ## A string that describes the host CPU.
+    ##
+    ## Possible values:
+    ## `"i386"`, `"alpha"`, `"powerpc"`, `"powerpc64"`, `"powerpc64el"`,
+    ## `"sparc"`, `"amd64"`, `"mips"`, `"mipsel"`, `"arm"`, `"arm64"`,
+    ## `"mips64"`, `"mips64el"`, `"riscv32"`, `"riscv64"`, `"loongarch64"`.
 
   seqShallowFlag = low(int)
   strlitFlag = 1 shl (sizeof(int)*8 - 2) # later versions of the codegen \
   # emit this flag
   # 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
-{.pop.}
-
-proc compileOption*(option: string): bool {.
-  magic: "CompileOption", noSideEffect.}
-  ## can be used to determine an on|off compile-time option. Example:
-  ##
-  ## .. code-block:: nim
-  ##   when compileOption("floatchecks"):
-  ##     echo "compiled with floating point NaN and Inf checks"
-
-proc compileOption*(option, arg: string): bool {.
-  magic: "CompileOptionArg", noSideEffect.}
-  ## can be used to determine an enum compile-time option. Example:
-  ##
-  ## .. code-block:: nim
-  ##   when compileOption("opt", "size") and compileOption("gc", "boehm"):
-  ##     echo "compiled with optimization for size and uses Boehm's GC"
-
 const
   hasThreadSupport = compileOption("threads") and not defined(nimscript)
   hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own
-  taintMode = compileOption("taintmode")
-  nimEnableCovariance* = defined(nimEnableCovariance) # or true
+
+when notJSnotNims and not defined(nimSeqsV2):
+  template space(s: PGenericSeq): int =
+    s.reserved and not (seqShallowFlag or strlitFlag)
 
 when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"):
   # tcc doesn't support TLS
-  {.error: "``--tlsEmulation:on`` must be used when using threads with tcc backend".}
+  {.error: "`--tlsEmulation:on` must be used when using threads with tcc backend".}
 
 when defined(boehmgc):
   when defined(windows):
-    const boehmLib = "boehmgc.dll"
+    when sizeof(int) == 8:
+      const boehmLib = "boehmgc64.dll"
+    else:
+      const boehmLib = "boehmgc.dll"
   elif defined(macosx):
     const boehmLib = "libgc.dylib"
+  elif defined(openbsd):
+    const boehmLib = "libgc.so.(4|5).0"
+  elif defined(freebsd):
+    const boehmLib = "libgc-threaded.so.1"
   else:
     const boehmLib = "libgc.so.1"
   {.pragma: boehmGC, noconv, dynlib: boehmLib.}
 
-when taintMode:
-  type TaintedString* = distinct string ## a distinct string type that
-                                        ## is `tainted`:idx:, see `taint mode
-                                        ## <manual.html#taint-mode>`_ for
-                                        ## details. It is an alias for
-                                        ## ``string`` if the taint mode is not
-                                        ## turned on.
+when not defined(nimPreviewSlimSystem):
+  type TaintedString* {.deprecated: "Deprecated since 1.5".} = string
 
-  proc len*(s: TaintedString): int {.borrow.}
-else:
-  type TaintedString* = string          ## a distinct string type that
-                                        ## is `tainted`:idx:, see `taint mode
-                                        ## <manual.html#taint-mode>`_ for
-                                        ## details. It is an alias for
-                                        ## ``string`` if the taint mode is not
-                                        ## turned on.
-
-when defined(profiler):
-  proc nimProfile() {.compilerProc, noinline.}
+
+when defined(profiler) and not defined(nimscript):
+  proc nimProfile() {.compilerproc, noinline.}
 when hasThreadSupport:
   {.pragma: rtlThreadVar, threadvar.}
 else:
@@ -1461,982 +1085,661 @@ else:
 
 const
   QuitSuccess* = 0
-    ## is the value that should be passed to `quit <#quit>`_ to indicate
+    ## is the value that should be passed to `quit <#quit,int>`_ to indicate
     ## success.
 
   QuitFailure* = 1
-    ## is the value that should be passed to `quit <#quit>`_ to indicate
+    ## is the value that should be passed to `quit <#quit,int>`_ to indicate
     ## failure.
 
-when defined(nodejs):
-  var programResult* {.importc: "process.exitCode".}: int
-  programResult = 0
-else:
-  var programResult* {.exportc: "nim_program_result".}: int
-    ## modify this variable to specify the exit code of the program
-    ## under normal circumstances. When the program is terminated
-    ## prematurely using ``quit``, this value is ignored.
+when not defined(js) and hostOS != "standalone":
+  var programResult* {.compilerproc, exportc: "nim_program_result".}: int
+    ## deprecated, prefer `quit` or `exitprocs.getProgramResult`, `exitprocs.setProgramResult`.
 
-when defined(nimdoc):
-  proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.}
-    ## Stops the program immediately with an exit code.
-    ##
-    ## Before stopping the program the "quit procedures" are called in the
-    ## opposite order they were added with `addQuitProc <#addQuitProc>`_.
-    ## ``quit`` never returns and ignores any exception that may have been raised
-    ## by the quit procedures.  It does *not* call the garbage collector to free
-    ## all the memory, unless a quit procedure calls `GC_fullCollect
-    ## <#GC_fullCollect>`_.
-    ##
-    ## The proc ``quit(QuitSuccess)`` is called implicitly when your nim
-    ## program finishes without incident for platforms where this is the
-    ## expected behavior. A raised unhandled exception is
-    ## equivalent to calling ``quit(QuitFailure)``.
-    ##
-    ## Note that this is a *runtime* call and using ``quit`` inside a macro won't
-    ## have any compile time effect. If you need to stop the compiler inside a
-    ## macro, use the `error <manual.html#error-pragma>`_ or `fatal
-    ## <manual.html#fatal-pragma>`_ pragmas.
+import std/private/since
+import system/ctypes
+export ctypes
 
-elif defined(genode):
-  include genode/env
-
-  var systemEnv {.exportc: runtimeEnvSym.}: GenodeEnvPtr
-
-  type GenodeEnv* = GenodeEnvPtr
-    ## Opaque type representing Genode environment.
-
-  proc quit*(env: GenodeEnv; errorcode: int) {.magic: "Exit", noreturn,
-    importcpp: "#->parent().exit(@); Genode::sleep_forever()", header: "<base/sleep.h>".}
-
-  proc quit*(errorcode: int = QuitSuccess) =
-    systemEnv.quit(errorCode)
-
-
-
-elif defined(nodejs):
-  proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit",
-    importc: "process.exit", noreturn.}
+proc align(address, alignment: int): int =
+  if alignment == 0: # Actually, this is illegal. This branch exists to actively
+                     # hide problems.
+    result = address
+  else:
+    result = (address + (alignment - 1)) and not (alignment - 1)
 
-else:
-  proc quit*(errorcode: int = QuitSuccess) {.
-    magic: "Exit", importc: "exit", header: "<stdlib.h>", noreturn.}
+include system/rawquits
+when defined(genode):
+  export GenodeEnv
 
 template sysAssert(cond: bool, msg: string) =
   when defined(useSysAssert):
     if not cond:
-      echo "[SYSASSERT] ", msg
-      quit 1
+      cstderr.rawWrite "[SYSASSERT] "
+      cstderr.rawWrite msg
+      cstderr.rawWrite "\n"
+      rawQuit 1
 
 const hasAlloc = (hostOS != "standalone" or not defined(nogc)) and not defined(nimscript)
 
-when not defined(JS) and not defined(nimscript) and hostOS != "standalone":
-  include "system/cgprocs"
-when not defined(JS) and not defined(nimscript) and hasAlloc:
-  proc addChar(s: NimString, c: char): NimString {.compilerProc, benign.}
-
-proc add *[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.}
-proc add *[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} =
-  ## Generic proc for adding a data item `y` to a container `x`.
-  ## For containers that have an order, `add` means *append*. New generic
-  ## containers should also call their adding proc `add` for consistency.
-  ## Generic code becomes much easier to write if the Nim naming scheme is
-  ## respected.
-  ##
-  ## .. code-block:: nim
-  ##   var s: seq[string] = @["test2","test2"]
-  ##   s.add("test") #=> @[test2, test2, test]
-  let xl = x.len
-  setLen(x, xl + y.len)
-  for i in 0..high(y): x[xl+i] = y[i]
+when notJSnotNims and hasAlloc and not defined(nimSeqsV2):
+  proc addChar(s: NimString, c: char): NimString {.compilerproc, benign.}
+
+when defined(nimscript) or not defined(nimSeqsV2):
+  proc add*[T](x: var seq[T], y: sink T) {.magic: "AppendSeqElem", noSideEffect.}
+    ## Generic proc for adding a data item `y` to a container `x`.
+    ##
+    ## For containers that have an order, `add` means *append*. New generic
+    ## containers should also call their adding proc `add` for consistency.
+    ## Generic code becomes much easier to write if the Nim naming scheme is
+    ## respected.
+    ##   ```nim
+    ##   var s: seq[string] = @["test2","test2"]
+    ##   s.add("test")
+    ##   assert s == @["test2", "test2", "test"]
+    ##   ```
+
+when false: # defined(gcDestructors):
+  proc add*[T](x: var seq[T], y: sink openArray[T]) {.noSideEffect.} =
+    ## Generic proc for adding a container `y` to a container `x`.
+    ##
+    ## For containers that have an order, `add` means *append*. New generic
+    ## containers should also call their adding proc `add` for consistency.
+    ## Generic code becomes much easier to write if the Nim naming scheme is
+    ## respected.
+    ##   ```nim
+    ##   var s: seq[string] = @["test2","test2"]
+    ##   s.add("test") # s <- @[test2, test2, test]
+    ##   ```
+    ##
+    ## See also:
+    ## * `& proc <#&,seq[T],seq[T]>`_
+    {.noSideEffect.}:
+      let xl = x.len
+      setLen(x, xl + y.len)
+      for i in 0..high(y):
+        when nimvm:
+          # workaround the fact that the VM does not yet
+          # handle sink parameters properly:
+          x[xl+i] = y[i]
+        else:
+          x[xl+i] = move y[i]
+else:
+  proc add*[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} =
+    ## Generic proc for adding a container `y` to a container `x`.
+    ##
+    ## For containers that have an order, `add` means *append*. New generic
+    ## containers should also call their adding proc `add` for consistency.
+    ## Generic code becomes much easier to write if the Nim naming scheme is
+    ## respected.
+    ##
+    ## See also:
+    ## * `& proc <#&,seq[T],seq[T]>`_
+    runnableExamples:
+      var a = @["a1", "a2"]
+      a.add(["b1", "b2"])
+      assert a == @["a1", "a2", "b1", "b2"]
+      var c = @["c0", "c1", "c2", "c3"]
+      a.add(c.toOpenArray(1, 2))
+      assert a == @["a1", "a2", "b1", "b2", "c1", "c2"]
+
+    {.noSideEffect.}:
+      let xl = x.len
+      setLen(x, xl + y.len)
+      for i in 0..high(y): x[xl+i] = y[i]
+
+
+when defined(nimSeqsV2):
+  template movingCopy(a, b: typed) =
+    a = move(b)
+else:
+  template movingCopy(a, b: typed) =
+    shallowCopy(a, b)
 
 proc del*[T](x: var seq[T], i: Natural) {.noSideEffect.} =
-  ## deletes the item at index `i` by putting ``x[high(x)]`` into position `i`.
-  ## This is an O(1) operation.
+  ## Deletes the item at index `i` by putting `x[high(x)]` into position `i`.
   ##
-  ## .. code-block:: nim
-  ##  var i = @[1, 2, 3, 4, 5]
-  ##  i.del(2) #=> @[1, 2, 5, 4]
+  ## This is an `O(1)` operation.
+  ##
+  ## See also:
+  ## * `delete <#delete,seq[T],Natural>`_ for preserving the order
+  runnableExamples:
+    var a = @[10, 11, 12, 13, 14]
+    a.del(2)
+    assert a == @[10, 11, 14, 13]
   let xl = x.len - 1
-  shallowCopy(x[i], x[xl])
+  movingCopy(x[i], x[xl])
   setLen(x, xl)
 
-proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect.} =
-  ## deletes the item at index `i` by moving ``x[i+1..]`` by one position.
-  ## This is an O(n) operation.
-  ##
-  ## .. code-block:: nim
-  ##  var i = @[1, 2, 3, 4, 5]
-  ##  i.delete(2) #=> @[1, 2, 4, 5]
-  template defaultImpl =
-    let xl = x.len
-    for j in i.int..xl-2: shallowCopy(x[j], x[j+1])
-    setLen(x, xl-1)
-
-  when nimvm:
-    defaultImpl()
-  else:
-    when defined(js):
-      {.emit: "`x`[`x`_Idx].splice(`i`, 1);".}
-    else:
+proc insert*[T](x: var seq[T], item: sink T, i = 0.Natural) {.noSideEffect.} =
+  ## Inserts `item` into `x` at position `i`.
+  ##   ```nim
+  ##   var i = @[1, 3, 5]
+  ##   i.insert(99, 0) # i <- @[99, 1, 3, 5]
+  ##   ```
+  {.noSideEffect.}:
+    template defaultImpl =
+      let xl = x.len
+      setLen(x, xl+1)
+      var j = xl-1
+      while j >= i:
+        movingCopy(x[j+1], x[j])
+        dec(j)
+    when nimvm:
       defaultImpl()
+    else:
+      when defined(js):
+        var it : T
+        {.emit: "`x` = `x` || []; `x`.splice(`i`, 0, `it`);".}
+      else:
+        defaultImpl()
+    x[i] = item
+
+when not defined(nimV2):
+  proc repr*[T](x: T): string {.magic: "Repr", noSideEffect.}
+    ## Takes any Nim variable and returns its string representation.
+    ## No trailing newline is inserted (so `echo` won't add an empty newline).
+    ## Use `-d:nimLegacyReprWithNewline` to revert to old behavior where newlines
+    ## were added in some cases.
+    ##
+    ## It works even for complex data graphs with cycles. This is a great
+    ## debugging tool.
+    ##   ```nim
+    ##   var s: seq[string] = @["test2", "test2"]
+    ##   var i = @[1, 2, 3, 4, 5]
+    ##   echo repr(s) # => 0x1055eb050[0x1055ec050"test2", 0x1055ec078"test2"]
+    ##   echo repr(i) # => 0x1055ed050[1, 2, 3, 4, 5]
+    ##   ```
+
+when not defined(nimPreviewSlimSystem):
+  type
+    csize* {.importc: "size_t", nodecl, deprecated: "use `csize_t` instead".} = int
+      ## This isn't the same as `size_t` in *C*. Don't use it.
 
-proc insert*[T](x: var seq[T], item: T, i = 0.Natural) {.noSideEffect.} =
-  ## inserts `item` into `x` at position `i`.
+const
+  Inf* = 0x7FF0000000000000'f64
+    ## Contains the IEEE floating point value of positive infinity.
+  NegInf* = 0xFFF0000000000000'f64
+    ## Contains the IEEE floating point value of negative infinity.
+  NaN* = 0x7FF7FFFFFFFFFFFF'f64
+    ## Contains an IEEE floating point value of *Not A Number*.
+    ##
+    ## Note that you cannot compare a floating point value to this value
+    ## and expect a reasonable result - use the `isNaN` or `classify` procedure
+    ## in the `math module <math.html>`_ for checking for NaN.
+
+proc high*(T: typedesc[SomeFloat]): T = Inf
+proc low*(T: typedesc[SomeFloat]): T = NegInf
+
+proc toFloat*(i: int): float {.noSideEffect, inline.} =
+  ## Converts an integer `i` into a `float`. Same as `float(i)`.
   ##
-  ## .. code-block:: nim
-  ##  var i = @[1, 2, 3, 4, 5]
-  ##  i.insert(2, 4) #=> @[1, 2, 3, 4, 2, 5]
-  template defaultImpl =
-    let xl = x.len
-    setLen(x, xl+1)
-    var j = xl-1
-    while j >= i:
-      shallowCopy(x[j+1], x[j])
-      dec(j)
-  when nimvm:
-    defaultImpl()
-  else:
-    when defined(js):
-      var it : T
-      {.emit: "`x`[`x`_Idx].splice(`i`, 0, `it`);".}
-    else:
-      defaultImpl()
-  x[i] = item
+  ## If the conversion fails, `ValueError` is raised.
+  ## However, on most platforms the conversion cannot fail.
+  ##
+  ##   ```nim
+  ##   let
+  ##     a = 2
+  ##     b = 3.7
+  ##
+  ##   echo a.toFloat + b # => 5.7
+  ##   ```
+  float(i)
 
-proc repr*[T](x: T): string {.magic: "Repr", noSideEffect.}
-  ## takes any Nim variable and returns its string representation. It
-  ## works even for complex data graphs with cycles. This is a great
-  ## debugging tool.
+proc toBiggestFloat*(i: BiggestInt): BiggestFloat {.noSideEffect, inline.} =
+  ## Same as `toFloat <#toFloat,int>`_ but for `BiggestInt` to `BiggestFloat`.
+  BiggestFloat(i)
+
+proc toInt*(f: float): int {.noSideEffect.} =
+  ## Converts a floating point number `f` into an `int`.
+  ##
+  ## Conversion rounds `f` half away from 0, see
+  ## `Round half away from zero
+  ## <https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero>`_,
+  ## as opposed to a type conversion which rounds towards zero.
   ##
-  ## .. code-block:: nim
-  ##  var s: seq[string] = @["test2", "test2"]
-  ##  var i = @[1, 2, 3, 4, 5]
-  ##  repr(s) #=> 0x1055eb050[0x1055ec050"test2", 0x1055ec078"test2"]
-  ##  repr(i) #=> 0x1055ed050[1, 2, 3, 4, 5]
+  ## Note that some floating point numbers (e.g. infinity or even 1e19)
+  ## cannot be accurately converted.
+  ##   ```nim
+  ##   doAssert toInt(0.49) == 0
+  ##   doAssert toInt(0.5) == 1
+  ##   doAssert toInt(-0.5) == -1 # rounding is symmetrical
+  ##   ```
+  if f >= 0: int(f+0.5) else: int(f-0.5)
 
-type
-  ByteAddress* = int
-    ## is the signed integer type that should be used for converting
-    ## pointers to integer addresses for readability.
-
-  BiggestInt* = int64
-    ## is an alias for the biggest signed integer type the Nim compiler
-    ## supports. Currently this is ``int64``, but it is platform-dependant
-    ## in general.
-
-  BiggestFloat* = float64
-    ## is an alias for the biggest floating point type the Nim
-    ## compiler supports. Currently this is ``float64``, but it is
-    ## platform-dependant in general.
-
-when defined(JS):
-  type BiggestUInt* = uint32
-    ## is an alias for the biggest unsigned integer type the Nim compiler
-    ## supports. Currently this is ``uint32`` for JS and ``uint64`` for other
-    ## targets.
-else:
-  type BiggestUInt* = uint64
-    ## is an alias for the biggest unsigned integer type the Nim compiler
-    ## supports. Currently this is ``uint32`` for JS and ``uint64`` for other
-    ## targets.
+proc toBiggestInt*(f: BiggestFloat): BiggestInt {.noSideEffect.} =
+  ## Same as `toInt <#toInt,float>`_ but for `BiggestFloat` to `BiggestInt`.
+  if f >= 0: BiggestInt(f+0.5) else: BiggestInt(f-0.5)
+
+proc `/`*(x, y: int): float {.inline, noSideEffect.} =
+  ## Division of integers that results in a float.
+  ##   ```nim
+  ##   echo 7 / 5 # => 1.4
+  ##   ```
+  ##
+  ## See also:
+  ## * `div <system.html#div,int,int>`_
+  ## * `mod <system.html#mod,int,int>`_
+  result = toFloat(x) / toFloat(y)
 
-{.deprecated: [TAddress: ByteAddress].}
+{.push stackTrace: off.}
 
-when defined(windows):
-  type
-    clong* {.importc: "long", nodecl.} = int32
-      ## This is the same as the type ``long`` in *C*.
-    culong* {.importc: "unsigned long", nodecl.} = uint32
-      ## This is the same as the type ``unsigned long`` in *C*.
+when defined(js):
+  proc js_abs[T: SomeNumber](x: T): T {.importc: "Math.abs".}
 else:
-  type
-    clong* {.importc: "long", nodecl.} = int
-      ## This is the same as the type ``long`` in *C*.
-    culong* {.importc: "unsigned long", nodecl.} = uint
-      ## This is the same as the type ``unsigned long`` in *C*.
-
-type # these work for most platforms:
-  cchar* {.importc: "char", nodecl.} = char
-    ## This is the same as the type ``char`` in *C*.
-  cschar* {.importc: "signed char", nodecl.} = int8
-    ## This is the same as the type ``signed char`` in *C*.
-  cshort* {.importc: "short", nodecl.} = int16
-    ## This is the same as the type ``short`` in *C*.
-  cint* {.importc: "int", nodecl.} = int32
-    ## This is the same as the type ``int`` in *C*.
-  csize* {.importc: "size_t", nodecl.} = int
-    ## This is the same as the type ``size_t`` in *C*.
-  clonglong* {.importc: "long long", nodecl.} = int64
-    ## This is the same as the type ``long long`` in *C*.
-  cfloat* {.importc: "float", nodecl.} = float32
-    ## This is the same as the type ``float`` in *C*.
-  cdouble* {.importc: "double", nodecl.} = float64
-    ## This is the same as the type ``double`` in *C*.
-  clongdouble* {.importc: "long double", nodecl.} = BiggestFloat
-    ## This is the same as the type ``long double`` in *C*.
-    ## This C type is not supported by Nim's code generator.
-
-  cuchar* {.importc: "unsigned char", nodecl.} = char
-    ## This is the same as the type ``unsigned char`` in *C*.
-  cushort* {.importc: "unsigned short", nodecl.} = uint16
-    ## This is the same as the type ``unsigned short`` in *C*.
-  cuint* {.importc: "unsigned int", nodecl.} = uint32
-    ## This is the same as the type ``unsigned int`` in *C*.
-  culonglong* {.importc: "unsigned long long", nodecl.} = uint64
-    ## This is the same as the type ``unsigned long long`` in *C*.
-
-  cstringArray* {.importc: "char**", nodecl.} = ptr UncheckedArray[cstring]
-    ## This is binary compatible to the type ``char**`` in *C*. The array's
-    ## high value is large enough to disable bounds checking in practice.
-    ## Use `cstringArrayToSeq` to convert it into a ``seq[string]``.
-
-  PFloat32* = ptr float32 ## an alias for ``ptr float32``
-  PFloat64* = ptr float64 ## an alias for ``ptr float64``
-  PInt64* = ptr int64 ## an alias for ``ptr int64``
-  PInt32* = ptr int32 ## an alias for ``ptr int32``
-
-proc toFloat*(i: int): float {.
-  magic: "ToFloat", noSideEffect, importc: "toFloat".}
-  ## converts an integer `i` into a ``float``. If the conversion
-  ## fails, `EInvalidValue` is raised. However, on most platforms the
-  ## conversion cannot fail.
-
-proc toBiggestFloat*(i: BiggestInt): BiggestFloat {.
-  magic: "ToBiggestFloat", noSideEffect, importc: "toBiggestFloat".}
-  ## converts an biggestint `i` into a ``biggestfloat``. If the conversion
-  ## fails, `EInvalidValue` is raised. However, on most platforms the
-  ## conversion cannot fail.
-
-proc toInt*(f: float): int {.
-  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), `EInvalidValue` is raised.
-
-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), `EInvalidValue` is raised.
-
-proc addQuitProc*(QuitProc: proc() {.noconv.}) {.
-  importc: "atexit", header: "<stdlib.h>".}
-  ## Adds/registers a quit procedure.
+  proc c_fabs(x: cdouble): cdouble {.importc: "fabs", header: "<math.h>".}
+  proc c_fabsf(x: cfloat): cfloat {.importc: "fabsf", header: "<math.h>".}
+
+proc abs*[T: float64 | float32](x: T): T {.noSideEffect, inline.} =
+  when nimvm:
+    if x < 0.0: result = -x
+    elif x == 0.0: result = 0.0 # handle 0.0, -0.0
+    else: result = x # handle NaN, > 0
+  else:
+    when defined(js): result = js_abs(x)
+    else:
+      when T is float64:
+        result = c_fabs(x)
+      else:
+        result = c_fabsf(x)
+
+func abs*(x: int): int {.magic: "AbsI", inline.} =
+  if x < 0: -x else: x
+func abs*(x: int8): int8 {.magic: "AbsI", inline.} =
+  if x < 0: -x else: x
+func abs*(x: int16): int16 {.magic: "AbsI", inline.} =
+  if x < 0: -x else: x
+func abs*(x: int32): int32 {.magic: "AbsI", inline.} =
+  if x < 0: -x else: x
+func abs*(x: int64): int64 {.magic: "AbsI", inline.} =
+  ## Returns the absolute value of `x`.
   ##
-  ## Each call to ``addQuitProc`` registers another quit procedure. Up to 30
-  ## procedures can be registered. They are executed on a last-in, first-out
-  ## basis (that is, the last function registered is the first to be executed).
-  ## ``addQuitProc`` raises an EOutOfIndex exception if ``QuitProc`` cannot be
-  ## registered.
-
-# Support for addQuitProc() is done by Ansi C's facilities here.
-# In case of an unhandled exeption the exit handlers should
-# not be called explicitly! The user may decide to do this manually though.
-
-proc copy*(s: string, first = 0): string {.
-  magic: "CopyStr", importc: "copyStr", noSideEffect, deprecated.}
-proc copy*(s: string, first, last: int): string {.
-  magic: "CopyStrLast", importc: "copyStrLast", noSideEffect,
-  deprecated.}
-  ## copies a slice of `s` into a new string and returns this new
-  ## string. The bounds `first` and `last` denote the indices of
-  ## the first and last characters that shall be copied. If ``last``
-  ## is omitted, it is treated as ``high(s)``.
-  ## **Deprecated since version 0.8.12**: Use ``substr`` instead.
-
-proc substr*(s: string, first = 0): string {.
-  magic: "CopyStr", importc: "copyStr", noSideEffect.}
-proc substr*(s: string, first, last: int): string {.
-  magic: "CopyStrLast", importc: "copyStrLast", noSideEffect.}
-  ## copies a slice of `s` into a new string and returns this new
-  ## string. The bounds `first` and `last` denote the indices of
-  ## the first and last characters that shall be copied. If ``last``
-  ## is omitted, it is treated as ``high(s)``. If ``last >= s.len``, ``s.len``
-  ## is used instead: This means ``substr`` can also be used to `cut`:idx:
-  ## or `limit`:idx: a string's length.
+  ## If `x` is `low(x)` (that is -MININT for its type),
+  ## an overflow exception is thrown (if overflow checking is turned on).
+  result = if x < 0: -x else: x
 
-when not defined(nimscript) and not defined(JS):
-  proc zeroMem*(p: pointer, size: Natural) {.inline, benign.}
-    ## overwrites the contents of the memory at ``p`` with the value 0.
-    ## Exactly ``size`` bytes will be overwritten. Like any procedure
-    ## dealing with raw memory this is *unsafe*.
-
-  proc copyMem*(dest, source: pointer, size: Natural) {.inline, benign,
-    tags: [], locks: 0.}
-    ## copies the contents from the memory at ``source`` to the memory
-    ## at ``dest``. Exactly ``size`` bytes will be copied. The memory
-    ## regions may not overlap. Like any procedure dealing with raw
-    ## memory this is *unsafe*.
-
-  proc moveMem*(dest, source: pointer, size: Natural) {.inline, benign,
-    tags: [], locks: 0.}
-    ## copies the contents from the memory at ``source`` to the memory
-    ## at ``dest``. Exactly ``size`` bytes will be copied. The memory
-    ## regions may overlap, ``moveMem`` handles this case appropriately
-    ## and is thus somewhat more safe than ``copyMem``. Like any procedure
-    ## dealing with raw memory this is still *unsafe*, though.
-
-  proc equalMem*(a, b: pointer, size: Natural): bool {.inline, noSideEffect, tags: [], locks: 0.}
-    ## compares the memory blocks ``a`` and ``b``. ``size`` bytes will
-    ## be compared. If the blocks are equal, true is returned, false
-    ## otherwise. Like any procedure dealing with raw memory this is
-    ## *unsafe*.
+{.pop.} # stackTrace: off
 
-when not defined(nimscript):
-  when hasAlloc:
-    proc alloc*(size: Natural): pointer {.noconv, rtl, tags: [], benign.}
-      ## allocates a new memory block with at least ``size`` bytes. The
-      ## block has to be freed with ``realloc(block, 0)`` or
-      ## ``dealloc(block)``. The block is not initialized, so reading
-      ## from it before writing to it is undefined behaviour!
-      ## The allocated memory belongs to its allocating thread!
-      ## Use `allocShared` to allocate from a shared heap.
-    proc createU*(T: typedesc, size = 1.Positive): ptr T {.inline, benign.} =
-      ## allocates a new memory block with at least ``T.sizeof * size``
-      ## bytes. The block has to be freed with ``resize(block, 0)`` or
-      ## ``dealloc(block)``. The block is not initialized, so reading
-      ## from it before writing to it is undefined behaviour!
-      ## The allocated memory belongs to its allocating thread!
-      ## Use `createSharedU` to allocate from a shared heap.
-      cast[ptr T](alloc(T.sizeof * size))
-    proc alloc0*(size: Natural): pointer {.noconv, rtl, tags: [], benign.}
-      ## allocates a new memory block with at least ``size`` bytes. The
-      ## block has to be freed with ``realloc(block, 0)`` or
-      ## ``dealloc(block)``. The block is initialized with all bytes
-      ## containing zero, so it is somewhat safer than ``alloc``.
-      ## The allocated memory belongs to its allocating thread!
-      ## Use `allocShared0` to allocate from a shared heap.
-    proc create*(T: typedesc, size = 1.Positive): ptr T {.inline, benign.} =
-      ## allocates a new memory block with at least ``T.sizeof * size``
-      ## bytes. The block has to be freed with ``resize(block, 0)`` or
-      ## ``dealloc(block)``. The block is initialized with all bytes
-      ## containing zero, so it is somewhat safer than ``createU``.
-      ## The allocated memory belongs to its allocating thread!
-      ## Use `createShared` to allocate from a shared heap.
-      cast[ptr T](alloc0(sizeof(T) * size))
-    proc realloc*(p: pointer, newSize: Natural): pointer {.noconv, rtl, tags: [],
-                                                           benign.}
-      ## grows or shrinks a given memory block. If p is **nil** then a new
-      ## memory block is returned. In either way the block has at least
-      ## ``newSize`` bytes. If ``newSize == 0`` and p is not **nil**
-      ## ``realloc`` calls ``dealloc(p)``. In other cases the block has to
-      ## be freed with ``dealloc``.
-      ## The allocated memory belongs to its allocating thread!
-      ## Use `reallocShared` to reallocate from a shared heap.
-    proc resize*[T](p: ptr T, newSize: Natural): ptr T {.inline, benign.} =
-      ## grows or shrinks a given memory block. If p is **nil** then a new
-      ## memory block is returned. In either way the block has at least
-      ## ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is not
-      ## **nil** ``resize`` calls ``dealloc(p)``. In other cases the block
-      ## has to be freed with ``free``. The allocated memory belongs to
-      ## its allocating thread!
-      ## Use `resizeShared` to reallocate from a shared heap.
-      cast[ptr T](realloc(p, T.sizeof * newSize))
-    proc dealloc*(p: pointer) {.noconv, rtl, tags: [], benign.}
-      ## frees the memory allocated with ``alloc``, ``alloc0`` or
-      ## ``realloc``. This procedure is dangerous! If one forgets to
-      ## free the memory a leak occurs; if one tries to access freed
-      ## memory (or just freeing it twice!) a core dump may happen
-      ## or other memory may be corrupted.
-      ## The freed memory must belong to its allocating thread!
-      ## Use `deallocShared` to deallocate from a shared heap.
-
-    proc allocShared*(size: Natural): pointer {.noconv, rtl, benign.}
-      ## allocates a new memory block on the shared heap with at
-      ## least ``size`` bytes. The block has to be freed with
-      ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block
-      ## is not initialized, so reading from it before writing to it is
-      ## undefined behaviour!
-    proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline,
-                                                                 benign.} =
-      ## allocates a new memory block on the shared heap with at
-      ## least ``T.sizeof * size`` bytes. The block has to be freed with
-      ## ``resizeShared(block, 0)`` or ``freeShared(block)``. The block
-      ## is not initialized, so reading from it before writing to it is
-      ## undefined behaviour!
-      cast[ptr T](allocShared(T.sizeof * size))
-    proc allocShared0*(size: Natural): pointer {.noconv, rtl, benign.}
-      ## allocates a new memory block on the shared heap with at
-      ## least ``size`` bytes. The block has to be freed with
-      ## ``reallocShared(block, 0)`` or ``deallocShared(block)``.
-      ## The block is initialized with all bytes
-      ## containing zero, so it is somewhat safer than ``allocShared``.
-    proc createShared*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
-      ## allocates a new memory block on the shared heap with at
-      ## least ``T.sizeof * size`` bytes. The block has to be freed with
-      ## ``resizeShared(block, 0)`` or ``freeShared(block)``.
-      ## The block is initialized with all bytes
-      ## containing zero, so it is somewhat safer than ``createSharedU``.
-      cast[ptr T](allocShared0(T.sizeof * size))
-    proc reallocShared*(p: pointer, newSize: Natural): pointer {.noconv, rtl,
-                                                                 benign.}
-      ## grows or shrinks a given memory block on the heap. If p is **nil**
-      ## then a new memory block is returned. In either way the block has at
-      ## least ``newSize`` bytes. If ``newSize == 0`` and p is not **nil**
-      ## ``reallocShared`` calls ``deallocShared(p)``. In other cases the
-      ## block has to be freed with ``deallocShared``.
-    proc resizeShared*[T](p: ptr T, newSize: Natural): ptr T {.inline.} =
-      ## grows or shrinks a given memory block on the heap. If p is **nil**
-      ## then a new memory block is returned. In either way the block has at
-      ## least ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is
-      ## not **nil** ``resizeShared`` calls ``freeShared(p)``. In other
-      ## cases the block has to be freed with ``freeShared``.
-      cast[ptr T](reallocShared(p, T.sizeof * newSize))
-    proc deallocShared*(p: pointer) {.noconv, rtl, benign.}
-      ## frees the memory allocated with ``allocShared``, ``allocShared0`` or
-      ## ``reallocShared``. This procedure is dangerous! If one forgets to
-      ## free the memory a leak occurs; if one tries to access freed
-      ## memory (or just freeing it twice!) a core dump may happen
-      ## or other memory may be corrupted.
-    proc freeShared*[T](p: ptr T) {.inline, benign.} =
-      ## frees the memory allocated with ``createShared``, ``createSharedU`` or
-      ## ``resizeShared``. This procedure is dangerous! If one forgets to
-      ## free the memory a leak occurs; if one tries to access freed
-      ## memory (or just freeing it twice!) a core dump may happen
-      ## or other memory may be corrupted.
-      deallocShared(p)
+when not defined(nimPreviewSlimSystem):
+  proc addQuitProc*(quitProc: proc() {.noconv.}) {.
+    importc: "atexit", header: "<stdlib.h>", deprecated: "use exitprocs.addExitProc".}
+    ## Adds/registers a quit procedure.
+    ##
+    ## Each call to `addQuitProc` registers another quit procedure. Up to 30
+    ## procedures can be registered. They are executed on a last-in, first-out
+    ## basis (that is, the last function registered is the first to be executed).
+    ## `addQuitProc` raises an EOutOfIndex exception if `quitProc` cannot be
+    ## registered.
+    # Support for addQuitProc() is done by Ansi C's facilities here.
+    # In case of an unhandled exception the exit handlers should
+    # not be called explicitly! The user may decide to do this manually though.
 
 proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.}
-  ## swaps the values `a` and `b`. This is often more efficient than
-  ## ``tmp = a; a = b; b = tmp``. Particularly useful for sorting algorithms.
+  ## Swaps the values `a` and `b`.
+  ##
+  ## This is often more efficient than `tmp = a; a = b; b = tmp`.
+  ## Particularly useful for sorting algorithms.
+  ##
+  ##   ```nim
+  ##   var
+  ##     a = 5
+  ##     b = 9
+  ##
+  ##   swap(a, b)
+  ##
+  ##   assert a == 9
+  ##   assert b == 5
+  ##   ```
 
 when not defined(js) and not defined(booting) and defined(nimTrMacros):
-  template swapRefsInArray*{swap(arr[a], arr[b])}(arr: openarray[ref], a, b: int) =
+  template swapRefsInArray*{swap(arr[a], arr[b])}(arr: openArray[ref], a, b: int) =
     # Optimize swapping of array elements if they are refs. Default swap
     # implementation will cause unsureAsgnRef to be emitted which causes
     # unnecessary slow down in this case.
     swap(cast[ptr pointer](addr arr[a])[], cast[ptr pointer](addr arr[b])[])
 
-template `>=%` *(x, y: untyped): untyped = y <=% x
-  ## treats `x` and `y` as unsigned and compares them.
-  ## Returns true iff ``unsigned(x) >= unsigned(y)``.
-
-template `>%` *(x, y: untyped): untyped = y <% x
-  ## treats `x` and `y` as unsigned and compares them.
-  ## Returns true iff ``unsigned(x) > unsigned(y)``.
+when not defined(nimscript):
+  {.push stackTrace: off, profiler: off.}
 
-proc `$`*(x: int): string {.magic: "IntToStr", noSideEffect.}
-  ## The stringify operator for an integer argument. Returns `x`
-  ## converted to a decimal string. ``$`` is Nim's general way of
-  ## spelling `toString`:idx:.
+  when not defined(nimPreviewSlimSystem):
+    import std/sysatomics
+    export sysatomics
+  else:
+    import std/sysatomics
 
-proc `$`*(x: int64): string {.magic: "Int64ToStr", noSideEffect.}
-  ## The stringify operator for an integer argument. Returns `x`
-  ## converted to a decimal string.
+  {.pop.}
 
-when not defined(nimscript):
-  when not defined(JS) and hasAlloc:
-    proc `$` *(x: uint64): string {.noSideEffect.}
-      ## The stringify operator for an unsigned integer argument. Returns `x`
-      ## converted to a decimal string.
-
-proc `$` *(x: float): string {.magic: "FloatToStr", noSideEffect.}
-  ## The stringify operator for a float argument. Returns `x`
-  ## converted to a decimal string.
-
-proc `$` *(x: bool): string {.magic: "BoolToStr", noSideEffect.}
-  ## The stringify operator for a boolean argument. Returns `x`
-  ## converted to the string "false" or "true".
-
-proc `$` *(x: char): string {.magic: "CharToStr", noSideEffect.}
-  ## The stringify operator for a character argument. Returns `x`
-  ## converted to a string.
-
-proc `$` *(x: cstring): string {.magic: "CStrToStr", noSideEffect.}
-  ## The stringify operator for a CString argument. Returns `x`
-  ## converted to a string.
-
-proc `$` *(x: string): string {.magic: "StrToStr", noSideEffect.}
-  ## The stringify operator for a string argument. Returns `x`
-  ## as it is. This operator is useful for generic code, so
-  ## that ``$expr`` also works if ``expr`` is already a string.
-
-proc `$` *[Enum: enum](x: Enum): string {.magic: "EnumToStr", noSideEffect.}
-  ## The stringify operator for an enumeration argument. This works for
-  ## any enumeration type thanks to compiler magic. If
-  ## a ``$`` operator for a concrete enumeration is provided, this is
-  ## used instead. (In other words: *Overwriting* is possible.)
-
-# undocumented:
-proc getRefcount*[T](x: ref T): int {.importc: "getRefcount", noSideEffect.}
-proc getRefcount*(x: string): int {.importc: "getRefcount", noSideEffect.}
-proc getRefcount*[T](x: seq[T]): int {.importc: "getRefcount", noSideEffect.}
-  ## retrieves the reference count of an heap-allocated object. The
-  ## value is implementation-dependent.
+include "system/memalloc"
 
 
-const
-  Inf* {.magic: "Inf".} = 1.0 / 0.0
-    ## contains the IEEE floating point value of positive infinity.
-  NegInf* {.magic: "NegInf".} = -Inf
-    ## contains the IEEE floating point value of negative infinity.
-  NaN* {.magic: "NaN".} = 0.0 / 0.0
-    ## contains an IEEE floating point value of *Not A Number*. Note
-    ## that you cannot compare a floating point value to this value
-    ## and expect a reasonable result - use the `classify` procedure
-    ## in the module ``math`` for checking for NaN.
-  NimMajor* {.intdefine.}: int = 0
-    ## is the major number of Nim's version.
-
-  NimMinor* {.intdefine.}: int = 18
-    ## is the minor number of Nim's version.
-
-  NimPatch* {.intdefine.}: int = 1
-    ## is the patch number of Nim's version.
+proc `|`*(a, b: typedesc): typedesc = discard
 
-  NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch
-    ## is the version of Nim as a string.
+include "system/iterators_1"
 
-# GC interface:
 
-when not defined(nimscript) and hasAlloc:
-  proc getOccupiedMem*(): int {.rtl.}
-    ## returns the number of bytes that are owned by the process and hold data.
+proc len*[U: Ordinal; V: Ordinal](x: HSlice[U, V]): int {.noSideEffect, inline.} =
+  ## Length of ordinal slice. When x.b < x.a returns zero length.
+  ##   ```nim
+  ##   assert((0..5).len == 6)
+  ##   assert((5..2).len == 0)
+  ##   ```
+  result = max(0, ord(x.b) - ord(x.a) + 1)
 
-  proc getFreeMem*(): int {.rtl.}
-    ## returns the number of bytes that are owned by the process, but do not
-    ## hold any meaningful data.
+proc isNil*[T](x: ref T): bool {.noSideEffect, magic: "IsNil".}
 
-  proc getTotalMem*(): int {.rtl.}
-    ## returns the number of bytes that are owned by the process.
+proc isNil*[T](x: ptr T): bool {.noSideEffect, magic: "IsNil".}
+proc isNil*(x: pointer): bool {.noSideEffect, magic: "IsNil".}
+proc isNil*(x: cstring): bool {.noSideEffect, magic: "IsNil".}
+proc isNil*[T: proc | iterator {.closure.}](x: T): bool {.noSideEffect, magic: "IsNil".}
+  ## Fast check whether `x` is nil. This is sometimes more efficient than
+  ## `== nil`.
 
-  when hasThreadSupport:
-    proc getOccupiedSharedMem*(): int {.rtl.}
-      ## returns the number of bytes that are owned by the process
-      ## on the shared heap and hold data. This is only available when
-      ## threads are enabled.
-
-    proc getFreeSharedMem*(): int {.rtl.}
-      ## returns the number of bytes that are owned by the
-      ## process on the shared heap, but do not hold any meaningful data.
-      ## This is only available when threads are enabled.
-
-    proc getTotalSharedMem*(): int {.rtl.}
-      ## returns the number of bytes on the shared heap that are owned by the
-      ## process. This is only available when threads are enabled.
-
-when sizeof(int) <= 2:
-  type IntLikeForCount = int|int8|int16|char|bool|uint8|enum
+when defined(nimHasTopDownInference):
+  # magic used for seq type inference
+  proc `@`*[T](a: openArray[T]): seq[T] {.magic: "OpenArrayToSeq".} =
+    ## Turns an *openArray* into a sequence.
+    ##
+    ## This is not as efficient as turning a fixed length array into a sequence
+    ## as it always copies every element of `a`.
+    newSeq(result, a.len)
+    for i in 0..a.len-1: result[i] = a[i]
 else:
-  type IntLikeForCount = int|int8|int16|int32|char|bool|uint8|uint16|enum
-
-iterator countdown*[T](a, b: T, step = 1): T {.inline.} =
-  ## Counts from ordinal value `a` down to `b` (inclusive) with the given
-  ## step count. `T` may be any ordinal type, `step` may only
-  ## be positive. **Note**: This fails to count to ``low(int)`` if T = int for
-  ## efficiency reasons.
-  when T is (uint|uint64):
-    var res = a
-    while res >= b:
-      yield res
-      if res == b: break
-      dec(res, step)
-  elif T is IntLikeForCount:
-    var res = int(a)
-    while res >= int(b):
-      yield T(res)
-      dec(res, step)
-  else:
-    var res = a
-    while res >= b:
-      yield res
-      dec(res, step)
-
-when defined(nimNewRoof):
-  iterator countup*[T](a, b: T, step = 1): T {.inline.} =
-    ## Counts from ordinal value `a` up to `b` (inclusive) with the given
-    ## step count. `S`, `T` may be any ordinal type, `step` may only
-    ## be positive. **Note**: This fails to count to ``high(int)`` if T = int for
-    ## efficiency reasons.
-    when T is IntLikeForCount:
-      var res = int(a)
-      while res <= int(b):
-        yield T(res)
-        inc(res, step)
-    else:
-      var res: T = T(a)
-      while res <= b:
-        yield res
-        inc(res, step)
-
-  iterator `..`*[T](a, b: T): T {.inline.} =
-    ## An alias for `countup`.
-    when T is IntLikeForCount:
-      var res = int(a)
-      while res <= int(b):
-        yield T(res)
-        inc(res)
-    else:
-      var res: T = T(a)
-      while res <= b:
-        yield res
-        inc(res)
-
-  template dotdotImpl(t) {.dirty.} =
-    iterator `..`*(a, b: t): t {.inline.} =
-      ## A type specialized version of ``..`` for convenience so that
-      ## mixing integer types work better.
-      var res = a
-      while res <= b:
-        yield res
-        inc(res)
-
-  dotdotImpl(int64)
-  dotdotImpl(int32)
-  dotdotImpl(uint64)
-  dotdotImpl(uint32)
+  proc `@`*[T](a: openArray[T]): seq[T] =
+    ## Turns an *openArray* into a sequence.
+    ##
+    ## This is not as efficient as turning a fixed length array into a sequence
+    ## as it always copies every element of `a`.
+    newSeq(result, a.len)
+    for i in 0..a.len-1: result[i] = a[i]
+
+
+when defined(nimSeqsV2):
+
+  proc `&`*[T](x, y: sink seq[T]): seq[T] {.noSideEffect.} =
+    ## Concatenates two sequences.
+    ##
+    ## Requires copying of the sequences.
+    ##   ```nim
+    ##   assert(@[1, 2, 3, 4] & @[5, 6] == @[1, 2, 3, 4, 5, 6])
+    ##   ```
+    ##
+    ## See also:
+    ## * `add(var seq[T], openArray[T]) <#add,seq[T],openArray[T]>`_
+    newSeq(result, x.len + y.len)
+    for i in 0..x.len-1:
+      result[i] = move(x[i])
+    for i in 0..y.len-1:
+      result[i+x.len] = move(y[i])
+
+  proc `&`*[T](x: sink seq[T], y: sink T): seq[T] {.noSideEffect.} =
+    ## Appends element y to the end of the sequence.
+    ##
+    ## Requires copying of the sequence.
+    ##   ```nim
+    ##   assert(@[1, 2, 3] & 4 == @[1, 2, 3, 4])
+    ##   ```
+    ##
+    ## See also:
+    ## * `add(var seq[T], T) <#add,seq[T],sinkT>`_
+    newSeq(result, x.len + 1)
+    for i in 0..x.len-1:
+      result[i] = move(x[i])
+    result[x.len] = move(y)
+
+  proc `&`*[T](x: sink T, y: sink seq[T]): seq[T] {.noSideEffect.} =
+    ## Prepends the element x to the beginning of the sequence.
+    ##
+    ## Requires copying of the sequence.
+    ##   ```nim
+    ##   assert(1 & @[2, 3, 4] == @[1, 2, 3, 4])
+    ##   ```
+    newSeq(result, y.len + 1)
+    result[0] = move(x)
+    for i in 0..y.len-1:
+      result[i+1] = move(y[i])
 
 else:
-  iterator countup*[S, T](a: S, b: T, step = 1): T {.inline.} =
-    ## Counts from ordinal value `a` up to `b` (inclusive) with the given
-    ## step count. `S`, `T` may be any ordinal type, `step` may only
-    ## be positive. **Note**: This fails to count to ``high(int)`` if T = int for
-    ## efficiency reasons.
-    when T is IntLikeForCount:
-      var res = int(a)
-      while res <= int(b):
-        yield T(res)
-        inc(res, step)
-    else:
-      var res: T = T(a)
-      while res <= b:
-        yield res
-        inc(res, step)
-
-  iterator `..`*[S, T](a: S, b: T): T {.inline.} =
-    ## An alias for `countup`.
-    when T is IntLikeForCount:
-      var res = int(a)
-      while res <= int(b):
-        yield T(res)
-        inc(res)
-    else:
-      var res: T = T(a)
-      while res <= b:
-        yield res
-        inc(res)
-
-
-iterator `||`*[S, T](a: S, b: T, annotation=""): T {.
-  inline, magic: "OmpParFor", sideEffect.} =
-  ## parallel loop iterator. Same as `..` but the loop may run in parallel.
-  ## `annotation` is an additional annotation for the code generator to use.
-  ## Note that the compiler maps that to
-  ## the ``#pragma omp parallel for`` construct of `OpenMP`:idx: and as
-  ## such isn't aware of the parallelism in your code! Be careful! Later
-  ## versions of ``||`` will get proper support by Nim's code generator
-  ## and GC.
-  discard
 
-{.push stackTrace:off.}
-proc min*(x, y: int): int {.magic: "MinI", noSideEffect.} =
-  if x <= y: x else: y
-proc min*(x, y: int8): int8 {.magic: "MinI", noSideEffect.} =
-  if x <= y: x else: y
-proc min*(x, y: int16): int16 {.magic: "MinI", noSideEffect.} =
-  if x <= y: x else: y
-proc min*(x, y: int32): int32 {.magic: "MinI", noSideEffect.} =
-  if x <= y: x else: y
-proc min*(x, y: int64): int64 {.magic: "MinI", noSideEffect.} =
-  ## The minimum value of two integers.
-  if x <= y: x else: y
-
-proc min*[T](x: openArray[T]): T =
-  ## The minimum value of `x`. ``T`` needs to have a ``<`` operator.
-  result = x[0]
-  for i in 1..high(x):
-    if x[i] < result: result = x[i]
-
-proc max*(x, y: int): int {.magic: "MaxI", noSideEffect.} =
-  if y <= x: x else: y
-proc max*(x, y: int8): int8 {.magic: "MaxI", noSideEffect.} =
-  if y <= x: x else: y
-proc max*(x, y: int16): int16 {.magic: "MaxI", noSideEffect.} =
-  if y <= x: x else: y
-proc max*(x, y: int32): int32 {.magic: "MaxI", noSideEffect.} =
-  if y <= x: x else: y
-proc max*(x, y: int64): int64 {.magic: "MaxI", noSideEffect.} =
-  ## The maximum value of two integers.
-  if y <= x: x else: y
-
-proc max*[T](x: openArray[T]): T =
-  ## The maximum value of `x`. ``T`` needs to have a ``<`` operator.
-  result = x[0]
-  for i in 1..high(x):
-    if result < x[i]: result = x[i]
-
-proc abs*(x: float): float {.magic: "AbsF64", noSideEffect.} =
-  if x < 0.0: -x else: x
-proc min*(x, y: float): float {.magic: "MinF64", noSideEffect.} =
-  if x <= y: x else: y
-proc max*(x, y: float): float {.magic: "MaxF64", noSideEffect.} =
-  if y <= x: x else: y
-
-proc min*[T](x, y: T): T =
-  if x <= y: x else: y
-
-proc max*[T](x, y: T): T =
-  if y <= x: x else: y
-{.pop.}
+  proc `&`*[T](x, y: seq[T]): seq[T] {.noSideEffect.} =
+    ## Concatenates two sequences.
+    ##
+    ## Requires copying of the sequences.
+    ##   ```nim
+    ##   assert(@[1, 2, 3, 4] & @[5, 6] == @[1, 2, 3, 4, 5, 6])
+    ##   ```
+    ##
+    ## See also:
+    ## * `add(var seq[T], openArray[T]) <#add,seq[T],openArray[T]>`_
+    newSeq(result, x.len + y.len)
+    for i in 0..x.len-1:
+      result[i] = x[i]
+    for i in 0..y.len-1:
+      result[i+x.len] = y[i]
+
+  proc `&`*[T](x: seq[T], y: T): seq[T] {.noSideEffect.} =
+    ## Appends element y to the end of the sequence.
+    ##
+    ## Requires copying of the sequence.
+    ##   ```nim
+    ##   assert(@[1, 2, 3] & 4 == @[1, 2, 3, 4])
+    ##   ```
+    ##
+    ## See also:
+    ## * `add(var seq[T], T) <#add,seq[T],sinkT>`_
+    newSeq(result, x.len + 1)
+    for i in 0..x.len-1:
+      result[i] = x[i]
+    result[x.len] = y
+
+  proc `&`*[T](x: T, y: seq[T]): seq[T] {.noSideEffect.} =
+    ## Prepends the element x to the beginning of the sequence.
+    ##
+    ## Requires copying of the sequence.
+    ##   ```nim
+    ##   assert(1 & @[2, 3, 4] == @[1, 2, 3, 4])
+    ##   ```
+    newSeq(result, y.len + 1)
+    result[0] = x
+    for i in 0..y.len-1:
+      result[i+1] = y[i]
 
-proc high*(T: typedesc[SomeFloat]): T = Inf
-proc low*(T: typedesc[SomeFloat]): T = NegInf
 
-proc clamp*[T](x, a, b: T): T =
-  ## limits the value ``x`` within the interval [a, b]
+proc instantiationInfo*(index = -1, fullPaths = false): tuple[
+  filename: string, line: int, column: int] {.magic: "InstantiationInfo", noSideEffect.}
+  ## Provides access to the compiler's instantiation stack line information
+  ## of a template.
   ##
-  ## .. code-block:: Nim
-  ##   assert((1.4).clamp(0.0, 1.0) == 1.0)
-  ##   assert((0.5).clamp(0.0, 1.0) == 0.5)
-  if x < a: return a
-  if x > b: return b
-  return x
-
-proc len*[U: Ordinal; V: Ordinal](x: HSlice[U, V]): int {.noSideEffect, inline.} =
-  ## length of ordinal slice, when x.b < x.a returns zero length
+  ## While similar to the `caller info`:idx: of other languages, it is determined
+  ## at compile time.
   ##
-  ## .. code-block:: Nim
-  ##   assert((0..5).len == 6)
-  ##   assert((5..2).len == 0)
-  result = max(0, ord(x.b) - ord(x.a) + 1)
+  ## This proc is mostly useful for meta programming (eg. `assert` template)
+  ## to retrieve information about the current filename and line number.
+  ## Example:
+  ##
+  ##   ```nim
+  ##   import std/strutils
+  ##
+  ##   template testException(exception, code: untyped): typed =
+  ##     try:
+  ##       let pos = instantiationInfo()
+  ##       discard(code)
+  ##       echo "Test failure at $1:$2 with '$3'" % [pos.filename,
+  ##         $pos.line, astToStr(code)]
+  ##       assert false, "A test expecting failure succeeded?"
+  ##     except exception:
+  ##       discard
+  ##
+  ##   proc tester(pos: int): int =
+  ##     let
+  ##       a = @[1, 2, 3]
+  ##     result = a[pos]
+  ##
+  ##   when isMainModule:
+  ##     testException(IndexDefect, tester(30))
+  ##     testException(IndexDefect, tester(1))
+  ##     # --> Test failure at example.nim:20 with 'tester(1)'
+  ##   ```
 
-iterator items*[T](a: openArray[T]): T {.inline.} =
-  ## iterates over each item of `a`.
-  var i = 0
-  while i < len(a):
-    yield a[i]
-    inc(i)
-
-iterator mitems*[T](a: var openArray[T]): var T {.inline.} =
-  ## iterates over each item of `a` so that you can modify the yielded value.
-  var i = 0
-  while i < len(a):
-    yield a[i]
-    inc(i)
-
-iterator items*[IX, T](a: array[IX, T]): T {.inline.} =
-  ## iterates over each item of `a`.
-  var i = low(IX)
-  if i <= high(IX):
-    while true:
-      yield a[i]
-      if i >= high(IX): break
-      inc(i)
 
-iterator mitems*[IX, T](a: var array[IX, T]): var T {.inline.} =
-  ## iterates over each item of `a` so that you can modify the yielded value.
-  var i = low(IX)
-  if i <= high(IX):
-    while true:
-      yield a[i]
-      if i >= high(IX): break
-      inc(i)
+when notJSnotNims:
+  import system/ansi_c
+  import system/memory
 
-iterator items*[T](a: set[T]): T {.inline.} =
-  ## iterates over each element of `a`. `items` iterates only over the
-  ## elements that are really in the set (and not over the ones the set is
-  ## able to hold).
-  var i = low(T).int
-  while i <= high(T).int:
-    if T(i) in a: yield T(i)
-    inc(i)
-
-iterator items*(a: cstring): char {.inline.} =
-  ## iterates over each item of `a`.
-  var i = 0
-  while a[i] != '\0':
-    yield a[i]
-    inc(i)
-
-iterator mitems*(a: var cstring): var char {.inline.} =
-  ## iterates over each item of `a` so that you can modify the yielded value.
-  var i = 0
-  while a[i] != '\0':
-    yield a[i]
-    inc(i)
-
-iterator items*(E: typedesc[enum]): E =
-  ## iterates over the values of the enum ``E``.
-  for v in low(E)..high(E):
-    yield v
-
-iterator items*[T](s: HSlice[T, T]): T =
-  ## iterates over the slice `s`, yielding each value between `s.a` and `s.b`
-  ## (inclusively).
-  for x in s.a..s.b:
-    yield x
-
-iterator pairs*[T](a: openArray[T]): tuple[key: int, val: T] {.inline.} =
-  ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs.
-  var i = 0
-  while i < len(a):
-    yield (i, a[i])
-    inc(i)
-
-iterator mpairs*[T](a: var openArray[T]): tuple[key:int, val:var T]{.inline.} =
-  ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs.
-  ## ``a[index]`` can be modified.
-  var i = 0
-  while i < len(a):
-    yield (i, a[i])
-    inc(i)
-
-iterator pairs*[IX, T](a: array[IX, T]): tuple[key: IX, val: T] {.inline.} =
-  ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs.
-  var i = low(IX)
-  if i <= high(IX):
-    while true:
-      yield (i, a[i])
-      if i >= high(IX): break
-      inc(i)
 
-iterator mpairs*[IX, T](a:var array[IX, T]):tuple[key:IX,val:var T] {.inline.} =
-  ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs.
-  ## ``a[index]`` can be modified.
-  var i = low(IX)
-  if i <= high(IX):
-    while true:
-      yield (i, a[i])
-      if i >= high(IX): break
-      inc(i)
+{.push stackTrace: off.}
 
-iterator pairs*[T](a: seq[T]): tuple[key: int, val: T] {.inline.} =
-  ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs.
-  var i = 0
-  while i < len(a):
-    yield (i, a[i])
-    inc(i)
-
-iterator mpairs*[T](a: var seq[T]): tuple[key: int, val: var T] {.inline.} =
-  ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs.
-  ## ``a[index]`` can be modified.
-  var i = 0
-  while i < len(a):
-    yield (i, a[i])
-    inc(i)
-
-iterator pairs*(a: string): tuple[key: int, val: char] {.inline.} =
-  ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs.
-  var i = 0
-  while i < len(a):
-    yield (i, a[i])
-    inc(i)
-
-iterator mpairs*(a: var string): tuple[key: int, val: var char] {.inline.} =
-  ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs.
-  ## ``a[index]`` can be modified.
-  var i = 0
-  while i < len(a):
-    yield (i, a[i])
-    inc(i)
-
-iterator pairs*(a: cstring): tuple[key: int, val: char] {.inline.} =
-  ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs.
-  var i = 0
-  while a[i] != '\0':
-    yield (i, a[i])
-    inc(i)
-
-iterator mpairs*(a: var cstring): tuple[key: int, val: var char] {.inline.} =
-  ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs.
-  ## ``a[index]`` can be modified.
-  var i = 0
-  while a[i] != '\0':
-    yield (i, a[i])
-    inc(i)
-
-
-proc isNil*[T](x: seq[T]): bool {.noSideEffect, magic: "IsNil".}
-proc isNil*[T](x: ref T): bool {.noSideEffect, magic: "IsNil".}
-proc isNil*(x: string): bool {.noSideEffect, magic: "IsNil".}
-proc isNil*[T](x: ptr T): bool {.noSideEffect, magic: "IsNil".}
-proc isNil*(x: pointer): bool {.noSideEffect, magic: "IsNil".}
-proc isNil*(x: cstring): bool {.noSideEffect, magic: "IsNil".}
-proc isNil*[T: proc](x: T): bool {.noSideEffect, magic: "IsNil".}
-  ## Fast check whether `x` is nil. This is sometimes more efficient than
-  ## ``== nil``.
+when not defined(js) and hasThreadSupport and hostOS != "standalone":
+  import std/private/syslocks
+  include "system/threadlocalstorage"
 
-proc `==` *[I, T](x, y: array[I, T]): bool =
-  for f in low(x)..high(x):
-    if x[f] != y[f]:
-      return
-  result = true
-
-proc `@`*[T](a: openArray[T]): seq[T] =
-  ## turns an openarray into a sequence. This is not as efficient as turning
-  ## a fixed length array into a sequence as it always copies every element
-  ## of `a`.
-  newSeq(result, a.len)
-  for i in 0..a.len-1: result[i] = a[i]
-
-proc `&` *[T](x, y: seq[T]): seq[T] {.noSideEffect.} =
-  ## Concatenates two sequences.
-  ## Requires copying of the sequences.
-  ##
-  ## .. code-block:: Nim
-  ##   assert(@[1, 2, 3, 4] & @[5, 6] == @[1, 2, 3, 4, 5, 6])
-  newSeq(result, x.len + y.len)
-  for i in 0..x.len-1:
-    result[i] = x[i]
-  for i in 0..y.len-1:
-    result[i+x.len] = y[i]
-
-proc `&` *[T](x: seq[T], y: T): seq[T] {.noSideEffect.} =
-  ## Appends element y to the end of the sequence.
-  ## Requires copying of the sequence
-  ##
-  ## .. code-block:: Nim
-  ##   assert(@[1, 2, 3] & 4 == @[1, 2, 3, 4])
-  newSeq(result, x.len + 1)
-  for i in 0..x.len-1:
-    result[i] = x[i]
-  result[x.len] = y
-
-proc `&` *[T](x: T, y: seq[T]): seq[T] {.noSideEffect.} =
-  ## Prepends the element x to the beginning of the sequence.
-  ## Requires copying of the sequence
-  ##
-  ## .. code-block:: Nim
-  ##   assert(1 & @[2, 3, 4] == @[1, 2, 3, 4])
-  newSeq(result, y.len + 1)
-  result[0] = x
-  for i in 0..y.len-1:
-    result[i+1] = y[i]
-
-proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} =
-  ## Generic equals operator for sequences: relies on a equals operator for
-  ## the element type `T`.
-  when nimvm:
-    if x.isNil and y.isNil:
-      return true
-  else:
-    when not defined(JS):
-      proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} =
-        result = cast[pointer](x)
+when not defined(js) and defined(nimV2):
+  type
+    DestructorProc = proc (p: pointer) {.nimcall, benign, raises: [].}
+    TNimTypeV2 {.compilerproc.} = object
+      destructor: pointer
+      size: int
+      align: int16
+      depth: int16
+      display: ptr UncheckedArray[uint32] # classToken
+      when defined(nimTypeNames) or defined(nimArcIds):
+        name: cstring
+      traceImpl: pointer
+      typeInfoV1: pointer # for backwards compat, usually nil
+      flags: int
+      when defined(gcDestructors):
+        when defined(cpp):
+          vTable: ptr UncheckedArray[pointer] # vtable for types
+        else:
+          vTable: UncheckedArray[pointer] # vtable for types
+    PNimTypeV2 = ptr TNimTypeV2
+
+proc supportsCopyMem(t: typedesc): bool {.magic: "TypeTrait".}
+
+when notJSnotNims and defined(nimSeqsV2):
+  include "system/strs_v2"
+  include "system/seqs_v2"
+
+when not defined(js):
+  template newSeqImpl(T, len) =
+    result = newSeqOfCap[T](len)
+    {.cast(noSideEffect).}:
+      when defined(nimSeqsV2):
+        cast[ptr int](addr result)[] = len
+      else:
+        var s = cast[PGenericSeq](result)
+        s.len = len
+
+  proc newSeqUninitialized*[T: SomeNumber](len: Natural): seq[T] {.deprecated: "Use `newSeqUninit` instead".} =
+    ## Creates a new sequence of type `seq[T]` with length `len`.
+    ##
+    ## Only available for numbers types. Note that the sequence will be
+    ## uninitialized. After the creation of the sequence you should assign
+    ## entries to the sequence instead of adding them.
+    ## Example:
+    ##   ```nim
+    ##   var x = newSeqUninitialized[int](3)
+    ##   assert len(x) == 3
+    ##   x[0] = 10
+    ##   ```
+    result = newSeqOfCap[T](len)
+    when defined(nimSeqsV2):
+      cast[ptr int](addr result)[] = len
+    else:
+      var s = cast[PGenericSeq](result)
+      s.len = len
+
+  func newSeqUninit*[T](len: Natural): seq[T] =
+    ## Creates a new sequence of type `seq[T]` with length `len`.
+    ##
+    ## Only available for types, which don't contain
+    ## managed memory or have destructors.
+    ## Note that the sequence will be uninitialized.
+    ## After the creation of the sequence you should assign
+    ## entries to the sequence instead of adding them.
+    runnableExamples:
+      var x = newSeqUninit[int](3)
+      assert len(x) == 3
+      x[0] = 10
+    when supportsCopyMem(T):
+      when nimvm:
+        result = newSeq[T](len)
+      else:
+        newSeqImpl(T, len)
+    else:
+      {.error: "The type T cannot contain managed memory or have destructors".}
+
+  proc newStringUninit*(len: Natural): string =
+    ## Returns a new string of length `len` but with uninitialized
+    ## content. One needs to fill the string character after character
+    ## with the index operator `s[i]`.
+    ##
+    ## This procedure exists only for optimization purposes;
+    ## the same effect can be achieved with the `&` operator or with `add`.
+    when nimvm:
+      result = newString(len)
     else:
-      proc seqToPtr[T](x: seq[T]): pointer {.asmNoStackFrame, nosideeffect.} =
-        asm """return `x`"""
+      result = newStringOfCap(len)
+      when defined(nimSeqsV2):
+        let s = cast[ptr NimStringV2](addr result)
+        if len > 0:
+          s.len = len
+          s.p.data[len] = '\0'
+      else:
+        let s = cast[NimString](result)
+        s.len = len
+        s.data[len] = '\0'
+else:
+  proc newStringUninit*(len: Natural): string {.
+    magic: "NewString", importc: "mnewString", noSideEffect.}
+
+{.pop.}
 
-    if seqToPtr(x) == seqToPtr(y):
-      return true
+when not defined(nimscript):
+  proc writeStackTrace*() {.tags: [], gcsafe, raises: [].}
+    ## Writes the current stack trace to `stderr`. This is only works
+    ## for debug builds. Since it's usually used for debugging, this
+    ## is proclaimed to have no IO effect!
+
+when not declared(sysFatal):
+  include "system/fatal"
 
-  when not defined(nimNoNil):
-    if x.isNil or y.isNil:
-      return false
+type
+  PFrame* = ptr TFrame  ## Represents a runtime frame of the call stack;
+                        ## part of the debugger API.
+  # keep in sync with nimbase.h `struct TFrame_`
+  TFrame* {.importc, nodecl, final.} = object ## The frame itself.
+    prev*: PFrame       ## Previous frame; used for chaining the call stack.
+    procname*: cstring  ## Name of the proc that is currently executing.
+    line*: int          ## Line number of the proc that is currently executing.
+    filename*: cstring  ## Filename of the proc that is currently executing.
+    len*: int16         ## Length of the inspectable slots.
+    calldepth*: int16   ## Used for max call depth checking.
+    when NimStackTraceMsgs:
+      frameMsgLen*: int   ## end position in frameMsgBuf for this frame.
+
+when defined(nimV2):
+  var
+    framePtr {.threadvar.}: PFrame
 
-  if x.len != y.len:
-    return false
+  include system/arc
 
-  for i in 0..x.len-1:
-    if x[i] != y[i]:
-      return false
+template newException*(exceptn: typedesc, message: string;
+                       parentException: ref Exception = nil): untyped =
+  ## Creates an exception object of type `exceptn` and sets its `msg` field
+  ## to `message`. Returns the new exception object.
+  (ref exceptn)(msg: message, parent: parentException)
+
+when not defined(nimPreviewSlimSystem):
+  import std/assertions
+  export assertions
+
+import system/iterators
+export iterators
 
-  return true
 
 proc find*[T, S](a: T, item: S): int {.inline.}=
   ## Returns the first index of `item` in `a` or -1 if not found. This requires
   ## appropriate `items` and `==` operations to work.
+  result = 0
   for i in items(a):
     if i == item: return
     inc(result)
@@ -2444,79 +1747,47 @@ proc find*[T, S](a: T, item: S): int {.inline.}=
 
 proc contains*[T](a: openArray[T], item: T): bool {.inline.}=
   ## Returns true if `item` is in `a` or false if not found. This is a shortcut
-  ## for ``find(a, item) >= 0``.
+  ## for `find(a, item) >= 0`.
+  ##
+  ## This allows the `in` operator: `a.contains(item)` is the same as
+  ## `item in a`.
+  ##   ```nim
+  ##   var a = @[1, 3, 5]
+  ##   assert a.contains(5)
+  ##   assert 3 in a
+  ##   assert 99 notin a
+  ##   ```
   return find(a, item) >= 0
 
 proc pop*[T](s: var seq[T]): T {.inline, noSideEffect.} =
-  ## returns the last item of `s` and decreases ``s.len`` by one. This treats
+  ## Returns the last item of `s` and decreases `s.len` by one. This treats
   ## `s` as a stack and implements the common *pop* operation.
-  var L = s.len-1
-  result = s[L]
-  setLen(s, L)
-
-iterator fields*[T: tuple|object](x: T): RootObj {.
-  magic: "Fields", noSideEffect.}
-  ## iterates over every field of `x`. Warning: This really transforms
-  ## the 'for' and unrolls the loop. The current implementation also has a bug
-  ## that affects symbol binding in the loop body.
-iterator fields*[S:tuple|object, T:tuple|object](x: S, y: T): tuple[a,b: untyped] {.
-  magic: "Fields", noSideEffect.}
-  ## iterates over every field of `x` and `y`.
-  ## Warning: This is really transforms the 'for' and unrolls the loop.
-  ## The current implementation also has a bug that affects symbol binding
-  ## in the loop body.
-iterator fieldPairs*[T: tuple|object](x: T): RootObj {.
-  magic: "FieldPairs", noSideEffect.}
-  ## Iterates over every field of `x` returning their name and value.
-  ##
-  ## When you iterate over objects with different field types you have to use
-  ## the compile time ``when`` instead of a runtime ``if`` to select the code
-  ## you want to run for each type. To perform the comparison use the `is
-  ## operator <manual.html#is-operator>`_. Example:
-  ##
-  ## .. code-block:: Nim
-  ##
-  ##   type
-  ##     Custom = object
-  ##       foo: string
-  ##       bar: bool
-  ##
-  ##   proc `$`(x: Custom): string =
-  ##     result = "Custom:"
-  ##     for name, value in x.fieldPairs:
-  ##       when value is bool:
-  ##         result.add("\n\t" & name & " is " & $value)
-  ##       else:
-  ##         if value.isNil:
-  ##           result.add("\n\t" & name & " (nil)")
-  ##         else:
-  ##           result.add("\n\t" & name & " '" & value & "'")
   ##
-  ## Another way to do the same without ``when`` is to leave the task of
-  ## picking the appropriate code to a secondary proc which you overload for
-  ## each field type and pass the `value` to.
-  ##
-  ## Warning: This really transforms the 'for' and unrolls the loop. The
-  ## current implementation also has a bug that affects symbol binding in the
-  ## loop body.
-iterator fieldPairs*[S: tuple|object, T: tuple|object](x: S, y: T): tuple[
-  a, b: untyped] {.
-  magic: "FieldPairs", noSideEffect.}
-  ## iterates over every field of `x` and `y`.
-  ## Warning: This really transforms the 'for' and unrolls the loop.
-  ## The current implementation also has a bug that affects symbol binding
-  ## in the loop body.
+  ## Raises `IndexDefect` if `s` is empty.
+  runnableExamples:
+    var a = @[1, 3, 5, 7]
+    let b = pop(a)
+    assert b == 7
+    assert a == @[1, 3, 5]
+
+  var L = s.len-1
+  when defined(nimV2):
+    result = move s[L]
+    shrink(s, L)
+  else:
+    result = s[L]
+    setLen(s, L)
 
 proc `==`*[T: tuple|object](x, y: T): bool =
-  ## generic ``==`` operator for tuples that is lifted from the components
+  ## Generic `==` operator for tuples that is lifted from the components.
   ## of `x` and `y`.
   for a, b in fields(x, y):
     if a != b: return false
   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
@@ -2524,203 +1795,21 @@ 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
     if c > 0: return false
   return false
 
-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)"
-  ##   $() == "()"
-  result = "("
-  var firstElement = true
-  for name, value in fieldPairs(x):
-    if not firstElement: result.add(", ")
-    result.add(name)
-    result.add(": ")
-    when compiles($value):
-      when compiles(value.isNil):
-        if value.isNil: result.add "nil"
-        else: result.addQuoted(value)
-      else:
-        result.addQuoted(value)
-      firstElement = false
-    else:
-      result.add("...")
-  result.add(")")
-
-proc collectionToString[T](x: T, prefix, separator, suffix: string): string =
-  result = prefix
-  var firstElement = true
-  for value in items(x):
-    if firstElement:
-      firstElement = false
-    else:
-      result.add(separator)
-
-    when compiles(value.isNil):
-      # this branch should not be necessary
-      if value.isNil:
-        result.add "nil"
-      else:
-        result.addQuoted(value)
-    else:
-      result.addQuoted(value)
-
-  result.add(suffix)
-
-proc `$`*[T](x: set[T]): string =
-  ## generic ``$`` operator for sets that is lifted from the components
-  ## of `x`. Example:
-  ##
-  ## .. code-block:: nim
-  ##   ${23, 45} == "{23, 45}"
-  collectionToString(x, "{", ", ", "}")
-
-proc `$`*[T](x: seq[T]): string =
-  ## generic ``$`` operator for seqs that is lifted from the components
-  ## of `x`. Example:
-  ##
-  ## .. code-block:: nim
-  ##   $(@[23, 45]) == "@[23, 45]"
-  if x.isNil:
-    "nil"
-  else:
-    collectionToString(x, "@[", ", ", "]")
-
-# ----------------- GC interface ---------------------------------------------
-
-when not defined(nimscript) and hasAlloc:
-  type
-    GC_Strategy* = enum ## the strategy the GC should use for the application
-      gcThroughput,      ## optimize for throughput
-      gcResponsiveness,  ## optimize for responsiveness (default)
-      gcOptimizeTime,    ## optimize for speed
-      gcOptimizeSpace    ## optimize for memory footprint
-
-  {.deprecated: [TGC_Strategy: GC_Strategy].}
-
-  when not defined(JS):
-    proc GC_disable*() {.rtl, inl, benign.}
-      ## disables the GC. If called n-times, n calls to `GC_enable` are needed to
-      ## reactivate the GC. Note that in most circumstances one should only disable
-      ## the mark and sweep phase with `GC_disableMarkAndSweep`.
-
-    proc GC_enable*() {.rtl, inl, benign.}
-      ## enables the GC again.
-
-    proc GC_fullCollect*() {.rtl, benign.}
-      ## forces a full garbage collection pass.
-      ## Ordinary code does not need to call this (and should not).
-
-    proc GC_setStrategy*(strategy: GC_Strategy) {.rtl, deprecated, benign.}
-      ## tells the GC the desired strategy for the application.
-      ## **Deprecated** since version 0.8.14. This has always been a nop.
-
-    proc GC_enableMarkAndSweep*() {.rtl, benign.}
-    proc GC_disableMarkAndSweep*() {.rtl, benign.}
-      ## the current implementation uses a reference counting garbage collector
-      ## with a seldomly run mark and sweep phase to free cycles. The mark and
-      ## sweep phase may take a long time and is not needed if the application
-      ## does not create cycles. Thus the mark and sweep phase can be deactivated
-      ## and activated separately from the rest of the GC.
-
-    proc GC_getStatistics*(): string {.rtl, benign.}
-      ## returns an informative string about the GC's activity. This may be useful
-      ## for tweaking.
-
-    proc GC_ref*[T](x: ref T) {.magic: "GCref", benign.}
-    proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign.}
-    proc GC_ref*(x: string) {.magic: "GCref", benign.}
-      ## marks the object `x` as referenced, so that it will not be freed until
-      ## it is unmarked via `GC_unref`. If called n-times for the same object `x`,
-      ## n calls to `GC_unref` are needed to unmark `x`.
-
-    proc GC_unref*[T](x: ref T) {.magic: "GCunref", benign.}
-    proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign.}
-    proc GC_unref*(x: string) {.magic: "GCunref", benign.}
-      ## see the documentation of `GC_ref`.
-
-    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`.
-
-  else:
-    template GC_disable* =
-      {.warning: "GC_disable is a no-op in JavaScript".}
-
-    template GC_enable* =
-      {.warning: "GC_enable is a no-op in JavaScript".}
-
-    template GC_fullCollect* =
-      {.warning: "GC_fullCollect is a no-op in JavaScript".}
-
-    template GC_setStrategy* =
-      {.warning: "GC_setStrategy is a no-op in JavaScript".}
-
-    template GC_enableMarkAndSweep* =
-      {.warning: "GC_enableMarkAndSweep is a no-op in JavaScript".}
-
-    template GC_disableMarkAndSweep* =
-      {.warning: "GC_disableMarkAndSweep is a no-op in JavaScript".}
-
-    template GC_ref*[T](x: ref T) =
-      {.warning: "GC_ref is a no-op in JavaScript".}
-
-    template GC_ref*[T](x: seq[T]) =
-      {.warning: "GC_ref is a no-op in JavaScript".}
-
-    template GC_ref*(x: string) =
-      {.warning: "GC_ref is a no-op in JavaScript".}
-
-    template GC_unref*[T](x: ref T) =
-      {.warning: "GC_unref is a no-op in JavaScript".}
 
-    template GC_unref*[T](x: seq[T]) =
-      {.warning: "GC_unref is a no-op in JavaScript".}
-
-    template GC_unref*(x: string) =
-      {.warning: "GC_unref is a no-op in JavaScript".}
-
-    template GC_getStatistics*(): string =
-      {.warning: "GC_getStatistics is a no-op in JavaScript".}
-      ""
-
-template accumulateResult*(iter: untyped) =
-  ## helps to convert an iterator to a proc.
-  result = @[]
-  for x in iter: add(result, x)
+include "system/gc_interface"
 
 # we have to compute this here before turning it off in except.nim anyway ...
 const NimStackTrace = compileOption("stacktrace")
 
-template coroutinesSupportedPlatform(): bool =
-  when defined(sparc) or defined(ELATE) or compileOption("gc", "v2") or
-    defined(boehmgc) or defined(gogc) or defined(nogc) or defined(gcRegions) or
-    defined(gcMarkAndSweep):
-    false
-  else:
-    true
-
-when defined(nimCoroutines):
-  # Explicit opt-in.
-  when not coroutinesSupportedPlatform():
-    {.error: "Coroutines are not supported on this architecture and/or garbage collector.".}
-  const nimCoroutines* = true
-elif defined(noNimCoroutines):
-  # Explicit opt-out.
-  const nimCoroutines* = false
-else:
-  # Autodetect coroutine support.
-  const nimCoroutines* = false
+import system/coro_detection
 
 {.push checks: off.}
 # obviously we cannot generate checking operations here :-)
@@ -2728,192 +1817,233 @@ else:
 # however, stack-traces are available for most parts
 # of the code
 
-var
-  globalRaiseHook*: proc (e: ref Exception): bool {.nimcall, benign.}
-    ## with this hook you can influence exception handling on a global level.
-    ## If not nil, every 'raise' statement ends up calling this hook. Ordinary
-    ## application code should never set this hook! You better know what you
-    ## do when setting this. If ``globalRaiseHook`` returns false, the
-    ## exception is caught and does not propagate further through the call
-    ## stack.
-
-  localRaiseHook* {.threadvar.}: proc (e: ref Exception): bool {.nimcall, benign.}
-    ## with this hook you can influence exception handling on a
-    ## thread local level.
-    ## If not nil, every 'raise' statement ends up calling this hook. Ordinary
-    ## application code should never set this hook! You better know what you
-    ## do when setting this. If ``localRaiseHook`` returns false, the exception
-    ## is caught and does not propagate further through the call stack.
-
-  outOfMemHook*: proc () {.nimcall, tags: [], benign.}
-    ## set this variable to provide a procedure that should be called
-    ## in case of an `out of memory`:idx: event. The standard handler
-    ## writes an error message and terminates the program. `outOfMemHook` can
-    ## be used to raise an exception in case of OOM like so:
-    ##
-    ## .. code-block:: nim
-    ##
-    ##   var gOutOfMem: ref EOutOfMemory
-    ##   new(gOutOfMem) # need to be allocated *before* OOM really happened!
-    ##   gOutOfMem.msg = "out of memory"
-    ##
-    ##   proc handleOOM() =
-    ##     raise gOutOfMem
-    ##
-    ##   system.outOfMemHook = handleOOM
-    ##
-    ## If the handler does not raise an exception, ordinary control flow
-    ## continues and the program is terminated.
+when notJSnotNims:
+  var
+    globalRaiseHook*: proc (e: ref Exception): bool {.nimcall, benign.}
+      ## With this hook you can influence exception handling on a global level.
+      ## If not nil, every 'raise' statement ends up calling this hook.
+      ##
+      ## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this.
+      ##
+      ## If `globalRaiseHook` returns false, the exception is caught and does
+      ## not propagate further through the call stack.
 
-type
-  PFrame* = ptr TFrame  ## represents a runtime frame of the call stack;
-                        ## part of the debugger API.
-  TFrame* {.importc, nodecl, final.} = object ## the frame itself
-    prev*: PFrame       ## previous frame; used for chaining the call stack
-    procname*: cstring  ## name of the proc that is currently executing
-    line*: int          ## line number of the proc that is currently executing
-    filename*: cstring  ## filename of the proc that is currently executing
-    len*: int16         ## length of the inspectable slots
-    calldepth*: int16   ## used for max call depth checking
-#{.deprecated: [TFrame: Frame].}
-
-when defined(JS):
+    localRaiseHook* {.threadvar.}: proc (e: ref Exception): bool {.nimcall, benign.}
+      ## With this hook you can influence exception handling on a
+      ## thread local level.
+      ## If not nil, every 'raise' statement ends up calling this hook.
+      ##
+      ## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this.
+      ##
+      ## If `localRaiseHook` returns false, the exception
+      ## is caught and does not propagate further through the call stack.
+
+    outOfMemHook*: proc () {.nimcall, tags: [], benign, raises: [].}
+      ## Set this variable to provide a procedure that should be called
+      ## in case of an `out of memory`:idx: event. The standard handler
+      ## writes an error message and terminates the program.
+      ##
+      ## `outOfMemHook` can be used to raise an exception in case of OOM like so:
+      ##
+      ##   ```nim
+      ##   var gOutOfMem: ref EOutOfMemory
+      ##   new(gOutOfMem) # need to be allocated *before* OOM really happened!
+      ##   gOutOfMem.msg = "out of memory"
+      ##
+      ##   proc handleOOM() =
+      ##     raise gOutOfMem
+      ##
+      ##   system.outOfMemHook = handleOOM
+      ##   ```
+      ##
+      ## If the handler does not raise an exception, ordinary control flow
+      ## continues and the program is terminated.
+    unhandledExceptionHook*: proc (e: ref Exception) {.nimcall, tags: [], benign, raises: [].}
+      ## Set this variable to provide a procedure that should be called
+      ## in case of an `unhandle exception` event. The standard handler
+      ## writes an error message and terminates the program, except when
+      ## using `--os:any`
+
+when defined(js) or defined(nimdoc):
   proc add*(x: var string, y: cstring) {.asmNoStackFrame.} =
-    asm """
-      var len = `x`[0].length-1;
+    ## Appends `y` to `x` in place.
+    runnableExamples:
+      var tmp = ""
+      tmp.add(cstring("ab"))
+      tmp.add(cstring("cd"))
+      doAssert tmp == "abcd"
+    {.emit: """
+      if (`x` === null) { `x` = []; }
+      var off = `x`.length;
+      `x`.length += `y`.length;
       for (var i = 0; i < `y`.length; ++i) {
-        `x`[0][len] = `y`.charCodeAt(i);
-        ++len;
+        `x`[off+i] = `y`.charCodeAt(i);
       }
-      `x`[0][len] = 0
-    """
-  proc add*(x: var cstring, y: cstring) {.magic: "AppendStrStr".}
+    """.}
+  proc add*(x: var cstring, y: cstring) {.magic: "AppendStrStr".} =
+    ## Appends `y` to `x` in place.
+    ## Only implemented for JS backend.
+    runnableExamples:
+      when defined(js):
+        var tmp: cstring = ""
+        tmp.add(cstring("ab"))
+        tmp.add(cstring("cd"))
+        doAssert tmp == cstring("abcd")
 
 elif hasAlloc:
-  {.push stack_trace:off, profiler:off.}
+  {.push stackTrace: off, profiler: off.}
   proc add*(x: var string, y: cstring) =
     var i = 0
-    while y[i] != '\0':
-      add(x, y[i])
-      inc(i)
+    if y != nil:
+      while y[i] != '\0':
+        add(x, y[i])
+        inc(i)
   {.pop.}
 
-when defined(nimvarargstyped):
-  proc echo*(x: varargs[typed, `$`]) {.magic: "Echo", tags: [WriteIOEffect],
-    benign, sideEffect.}
-    ## Writes and flushes the parameters to the standard output.
-    ##
-    ## Special built-in that takes a variable number of arguments. Each argument
-    ## is converted to a string via ``$``, so it works for user-defined
-    ## types that have an overloaded ``$`` operator.
-    ## It is roughly equivalent to ``writeLine(stdout, x); flushFile(stdout)``, but
-    ## available for the JavaScript target too.
-    ##
-    ## Unlike other IO operations this is guaranteed to be thread-safe as
-    ## ``echo`` is very often used for debugging convenience. If you want to use
-    ## ``echo`` inside a `proc without side effects
-    ## <manual.html#pragmas-nosideeffect-pragma>`_ you can use `debugEcho <#debugEcho>`_
-    ## instead.
-
-  proc debugEcho*(x: varargs[typed, `$`]) {.magic: "Echo", noSideEffect,
-                                            tags: [], raises: [].}
-    ## Same as `echo <#echo>`_, but as a special semantic rule, ``debugEcho``
-    ## pretends to be free of side effects, so that it can be used for debugging
-    ## routines marked as `noSideEffect <manual.html#pragmas-nosideeffect-pragma>`_.
-else:
-  proc echo*(x: varargs[untyped, `$`]) {.magic: "Echo", tags: [WriteIOEffect],
-    benign, sideEffect.}
-  proc debugEcho*(x: varargs[untyped, `$`]) {.magic: "Echo", noSideEffect,
-                                             tags: [], raises: [].}
+proc echo*(x: varargs[typed, `$`]) {.magic: "Echo", benign, sideEffect.}
+  ## Writes and flushes the parameters to the standard output.
+  ##
+  ## Special built-in that takes a variable number of arguments. Each argument
+  ## is converted to a string via `$`, so it works for user-defined
+  ## types that have an overloaded `$` operator.
+  ## It is roughly equivalent to `writeLine(stdout, x); flushFile(stdout)`, but
+  ## available for the JavaScript target too.
+  ##
+  ## Unlike other IO operations this is guaranteed to be thread-safe as
+  ## `echo` is very often used for debugging convenience. If you want to use
+  ## `echo` inside a `proc without side effects
+  ## <manual.html#pragmas-nosideeffect-pragma>`_ you can use `debugEcho
+  ## <#debugEcho,varargs[typed,]>`_ instead.
+
+proc debugEcho*(x: varargs[typed, `$`]) {.magic: "Echo", noSideEffect,
+                                          tags: [], raises: [].}
+  ## Same as `echo <#echo,varargs[typed,]>`_, but as a special semantic rule,
+  ## `debugEcho` pretends to be free of side effects, so that it can be used
+  ## for debugging routines marked as `noSideEffect
+  ## <manual.html#pragmas-nosideeffect-pragma>`_.
+
+when hostOS == "standalone" and defined(nogc):
+  proc nimToCStringConv(s: NimString): cstring {.compilerproc, inline.} =
+    if s == nil or s.len == 0: result = cstring""
+    else: result = cast[cstring](addr s.data)
 
-template newException*(exceptn: typedesc, message: string;
-                       parentException: ref Exception = nil): untyped =
-  ## creates an exception object of type ``exceptn`` and sets its ``msg`` field
-  ## to `message`. Returns the new exception object.
-  var
-    e: ref exceptn
-  new(e)
-  e.msg = message
-  e.parent = parentException
-  e
+proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", benign.}
+  ## Get type information for `x`.
+  ##
+  ## Ordinary code should not use this, but the `typeinfo module
+  ## <typeinfo.html>`_ instead.
 
-when hostOS == "standalone":
-  include "$projectpath/panicoverride"
 
-when not declared(sysFatal):
-  {.push profiler: off.}
-  when hostOS == "standalone":
-    proc sysFatal(exceptn: typedesc, message: string) {.inline.} =
-      panic(message)
+when not defined(js):
+
+  proc likelyProc(val: bool): bool {.importc: "NIM_LIKELY", nodecl, noSideEffect.}
+  proc unlikelyProc(val: bool): bool {.importc: "NIM_UNLIKELY", nodecl, noSideEffect.}
 
-    proc sysFatal(exceptn: typedesc, message, arg: string) {.inline.} =
-      rawoutput(message)
-      panic(arg)
+template likely*(val: bool): bool =
+  ## Hints the optimizer that `val` is likely going to be true.
+  ##
+  ## You can use this template to decorate a branch condition. On certain
+  ## platforms this can help the processor predict better which branch is
+  ## going to be run. Example:
+  ##   ```nim
+  ##   for value in inputValues:
+  ##     if likely(value <= 100):
+  ##       process(value)
+  ##     else:
+  ##       echo "Value too big!"
+  ##   ```
+  ##
+  ## On backends without branch prediction (JS and the nimscript VM), this
+  ## template will not affect code execution.
+  when nimvm:
+    val
   else:
-    proc sysFatal(exceptn: typedesc, message: string) {.inline, noReturn.} =
-      var e: ref exceptn
-      new(e)
-      e.msg = message
-      raise e
-
-    proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noReturn.} =
-      var e: ref exceptn
-      new(e)
-      e.msg = message & arg
-      raise e
-  {.pop.}
+    when defined(js):
+      val
+    else:
+      likelyProc(val)
 
-proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", benign.}
-  ## get type information for `x`. Ordinary code should not use this, but
-  ## the `typeinfo` module instead.
+template unlikely*(val: bool): bool =
+  ## Hints the optimizer that `val` is likely going to be false.
+  ##
+  ## You can use this proc to decorate a branch condition. On certain
+  ## platforms this can help the processor predict better which branch is
+  ## going to be run. Example:
+  ##   ```nim
+  ##   for value in inputValues:
+  ##     if unlikely(value > 100):
+  ##       echo "Value too big!"
+  ##     else:
+  ##       process(value)
+  ##   ```
+  ##
+  ## On backends without branch prediction (JS and the nimscript VM), this
+  ## template will not affect code execution.
+  when nimvm:
+    val
+  else:
+    when defined(js):
+      val
+    else:
+      unlikelyProc(val)
 
-{.push stackTrace: off.}
-proc abs*(x: int): int {.magic: "AbsI", noSideEffect.} =
-  if x < 0: -x else: x
-proc abs*(x: int8): int8 {.magic: "AbsI", noSideEffect.} =
-  if x < 0: -x else: x
-proc abs*(x: int16): int16 {.magic: "AbsI", noSideEffect.} =
-  if x < 0: -x else: x
-proc abs*(x: int32): int32 {.magic: "AbsI", noSideEffect.} =
-  if x < 0: -x else: x
-when defined(nimnomagic64):
-  proc abs*(x: int64): int64 {.magic: "AbsI", noSideEffect.} =
-    ## returns the absolute value of `x`. If `x` is ``low(x)`` (that
-    ## is -MININT for its type), an overflow exception is thrown (if overflow
-    ## checking is turned on).
-    if x < 0: -x else: x
+import system/dollars
+export dollars
+
+when defined(nimAuditDelete):
+  {.pragma: auditDelete, deprecated: "review this call for out of bounds behavior".}
 else:
-  proc abs*(x: int64): int64 {.magic: "AbsI64", noSideEffect.} =
-    ## returns the absolute value of `x`. If `x` is ``low(x)`` (that
-    ## is -MININT for its type), an overflow exception is thrown (if overflow
-    ## checking is turned on).
-    if x < 0: -x else: x
-{.pop.}
+  {.pragma: auditDelete.}
+
+proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect, systemRaisesDefect, auditDelete.} =
+  ## Deletes the item at index `i` by moving all `x[i+1..^1]` items by one position.
+  ##
+  ## This is an `O(n)` operation.
+  ##
+  ## See also:
+  ## * `del <#del,seq[T],Natural>`_ for O(1) operation
+  ##
+  runnableExamples:
+    var s = @[1, 2, 3, 4, 5]
+    s.delete(2)
+    doAssert s == @[1, 2, 4, 5]
+
+  when not defined(nimAuditDelete):
+    if i > high(x):
+      # xxx this should call `raiseIndexError2(i, high(x))` after some refactoring
+      raise (ref IndexDefect)(msg: "index out of bounds: '" & $i & "' < '" & $x.len & "' failed")
+
+  template defaultImpl =
+    let xl = x.len
+    for j in i.int..xl-2: movingCopy(x[j], x[j+1])
+    setLen(x, xl-1)
+
+  when nimvm:
+    defaultImpl()
+  else:
+    when defined(js):
+      {.emit: "`x`.splice(`i`, 1);".}
+    else:
+      defaultImpl()
 
-type
-  FileSeekPos* = enum ## Position relative to which seek should happen
-                      # The values are ordered so that they match with stdio
-                      # SEEK_SET, SEEK_CUR and SEEK_END respectively.
-    fspSet            ## Seek to absolute value
-    fspCur            ## Seek relative to current position
-    fspEnd            ## Seek relative to end
 
-when not defined(JS): #and not defined(nimscript):
-  {.push stack_trace: off, profiler:off.}
+const
+  NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch
+    ## is the version of Nim as a string.
+
+when not defined(js):
+  {.push stackTrace: off, profiler: off.}
 
   when hasAlloc:
-    when not defined(gcRegions):
-      proc initGC() {.gcsafe.}
-    when not defined(boehmgc) and not defined(useMalloc) and
-        not defined(gogc) and not defined(gcRegions):
-      proc initAllocator() {.inline.}
+    when not defined(gcRegions) and not usesDestructors:
+      proc initGC() {.gcsafe, raises: [].}
 
     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
+        var locals {.volatile, noinit.}: pointer
         locals = addr(locals)
         nimGC_setStackBottom(locals)
 
@@ -2923,1053 +2053,525 @@ when not defined(JS): #and not defined(nimscript):
       when declared(nimGC_setStackBottom):
         nimGC_setStackBottom(locals)
 
-    {.push profiler: off.}
-    var
-      strDesc = TNimType(size: sizeof(string), kind: tyString, flags: {ntfAcyclic})
-    {.pop.}
+    when not usesDestructors:
+      {.push profiler: off.}
+      var
+        strDesc = TNimType(size: sizeof(string), kind: tyString, flags: {ntfAcyclic})
+      {.pop.}
 
+  {.pop.}
 
-  # ----------------- IO Part ------------------------------------------------
-  type
-    CFile {.importc: "FILE", header: "<stdio.h>",
-            final, incompletestruct.} = object
-    File* = ptr CFile ## The type representing a file handle.
-
-    FileMode* = enum           ## The file mode when opening a file.
-      fmRead,                   ## Open the file for read access only.
-      fmWrite,                  ## Open the file for write access only.
-                                ## If the file does not exist, it will be
-                                ## created. Existing files will be cleared!
-      fmReadWrite,              ## Open the file for read and write access.
-                                ## If the file does not exist, it will be
-                                ## created. Existing files will be cleared!
-      fmReadWriteExisting,      ## Open the file for read and write access.
-                                ## If the file does not exist, it will not be
-                                ## created. The existing file will not be cleared.
-      fmAppend                  ## Open the file for writing only; append data
-                                ## at the end.
-
-    FileHandle* = cint ## type that represents an OS file handle; this is
-                       ## useful for low-level file access
-
-  {.deprecated: [TFile: File, TFileHandle: FileHandle, TFileMode: FileMode].}
-
-  include "system/ansi_c"
 
+when not defined(js):
+  # ugly hack, see the accompanying .pop for
+  # the mysterious error message
+  {.push stackTrace: off, profiler: off.}
+
+when notJSnotNims:
+  proc zeroMem(p: pointer, size: Natural) =
+    nimZeroMem(p, size)
+    when declared(memTrackerOp):
+      memTrackerOp("zeroMem", p, size)
+  proc copyMem(dest, source: pointer, size: Natural) =
+    nimCopyMem(dest, source, size)
+    when declared(memTrackerOp):
+      memTrackerOp("copyMem", dest, size)
+  proc moveMem(dest, source: pointer, size: Natural) =
+    c_memmove(dest, source, csize_t(size))
+    when declared(memTrackerOp):
+      memTrackerOp("moveMem", dest, size)
+  proc equalMem(a, b: pointer, size: Natural): bool =
+    nimCmpMem(a, b, size) == 0
+  proc cmpMem(a, b: pointer, size: Natural): int =
+    nimCmpMem(a, b, size).int
+
+when not defined(js) or defined(nimscript):
+  # nimscript can be defined if config file for js compilation
   proc cmp(x, y: string): int =
     when nimvm:
       if x < y: result = -1
       elif x > y: result = 1
       else: result = 0
     else:
-      let minlen = min(x.len, y.len)
-      result = int(c_memcmp(x.cstring, y.cstring, minlen.csize))
-      if result == 0:
-        result = x.len - y.len
-
-  when defined(nimscript):
-    proc readFile*(filename: string): string {.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
-      ## this inside a compile time macro you can use `staticRead
-      ## <#staticRead>`_.
-
-    proc writeFile*(filename, content: string) {.tags: [WriteIOEffect], benign.}
-      ## Opens a file named `filename` for writing. Then writes the
-      ## `content` completely to the file and closes the file afterwards.
-      ## Raises an IO exception in case of an error.
-
-  when not defined(nimscript) and hostOS != "standalone":
-
-    # text file handling:
-    var
-      stdin* {.importc: "stdin", header: "<stdio.h>".}: File
-        ## The standard input stream.
-      stdout* {.importc: "stdout", header: "<stdio.h>".}: File
-        ## The standard output stream.
-      stderr* {.importc: "stderr", header: "<stdio.h>".}: File
-        ## The standard error stream.
-
-    when defined(windows):
-      # work-around C's sucking abstraction:
-      # BUGFIX: stdin and stdout should be binary files!
-      proc c_setmode(handle, mode: cint) {.
-        importc: when defined(bcc): "setmode" else: "_setmode",
-        header: "<io.h>".}
-      var
-        O_BINARY {.importc: "_O_BINARY", header:"<fcntl.h>".}: cint
-
-      # we use binary mode on Windows:
-      c_setmode(c_fileno(stdin), O_BINARY)
-      c_setmode(c_fileno(stdout), O_BINARY)
-      c_setmode(c_fileno(stderr), O_BINARY)
-
-    when defined(endb):
-      proc endbStep()
-
-    when defined(useStdoutAsStdmsg):
-      template stdmsg*: File = stdout
-    else:
-      template stdmsg*: File = stderr
-        ## Template which expands to either stdout or stderr depending on
-        ## `useStdoutAsStdmsg` compile-time switch.
-
-    proc open*(f: var File, filename: string,
-               mode: FileMode = fmRead, bufSize: int = -1): bool {.tags: [],
-               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.}
-      ## Creates a ``File`` from a `filehandle` with given `mode`.
-      ##
-      ## Default mode is readonly. Returns true iff the file could be opened.
-
-    proc open*(filename: string,
-               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
-      ## could not be opened.
-      if not open(result, filename, mode, bufSize):
-        sysFatal(IOError, "cannot open: ", filename)
-
-    proc reopen*(f: File, filename: string, mode: FileMode = fmRead): bool {.
-      tags: [], benign.}
-      ## reopens the file `f` with given `filename` and `mode`. This
-      ## is often used to redirect the `stdin`, `stdout` or `stderr`
-      ## file variables.
-      ##
-      ## Default mode is readonly. Returns true iff the file could be reopened.
-
-    proc setStdIoUnbuffered*() {.tags: [], benign.}
-      ## Configures `stdin`, `stdout` and `stderr` to be unbuffered.
-
-    proc close*(f: File) {.tags: [], gcsafe.}
-      ## Closes the file.
-
-    proc endOfFile*(f: File): bool {.tags: [], benign.}
-      ## Returns true iff `f` is at the end.
-
-    proc readChar*(f: File): char {.tags: [ReadIOEffect].}
-      ## Reads a single character from the stream `f`. Should not be used in
-      ## performance sensitive code.
-
-    proc flushFile*(f: File) {.tags: [WriteIOEffect].}
-      ## Flushes `f`'s buffer.
-
-    proc readAll*(file: File): TaintedString {.tags: [ReadIOEffect], benign.}
-      ## Reads all data from the stream `file`.
-      ##
-      ## Raises an IO exception in case of an error. It is an error if the
-      ## current file position is not at the beginning of the file.
-
-    proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.}
-      ## Opens a file named `filename` for reading.
-      ##
-      ## Then 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 this inside a compile time macro you can use
-      ## `staticRead <#staticRead>`_.
-
-    proc writeFile*(filename, content: string) {.tags: [WriteIOEffect], benign.}
-      ## Opens a file named `filename` for writing. Then writes the
-      ## `content` completely to the file and closes the file afterwards.
-      ## Raises an IO exception in case of an error.
-
-    proc write*(f: File, r: float32) {.tags: [WriteIOEffect], benign.}
-    proc write*(f: File, i: int) {.tags: [WriteIOEffect], benign.}
-    proc write*(f: File, i: BiggestInt) {.tags: [WriteIOEffect], benign.}
-    proc write*(f: File, r: BiggestFloat) {.tags: [WriteIOEffect], benign.}
-    proc write*(f: File, s: string) {.tags: [WriteIOEffect], benign.}
-    proc write*(f: File, b: bool) {.tags: [WriteIOEffect], benign.}
-    proc write*(f: File, c: char) {.tags: [WriteIOEffect], benign.}
-    proc write*(f: File, c: cstring) {.tags: [WriteIOEffect], benign.}
-    proc write*(f: File, a: varargs[string, `$`]) {.tags: [WriteIOEffect], benign.}
-      ## Writes a value to the file `f`. May throw an IO exception.
-
-    proc readLine*(f: File): TaintedString  {.tags: [ReadIOEffect], benign.}
-      ## reads a line of text from the file `f`. May throw an IO exception.
-      ## A line of text may be delimited by ``LF`` or ``CRLF``. The newline
-      ## character(s) are not part of the returned string.
-
-    proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect],
-                  benign.}
-      ## reads a line of text from the file `f` into `line`. `line` must not be
-      ## ``nil``! May throw an IO exception.
-      ## 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.
-
-    proc writeLn*[Ty](f: File, x: varargs[Ty, `$`]) {.inline,
-                             tags: [WriteIOEffect], benign, deprecated.}
-      ## **Deprecated since version 0.11.4:** Use **writeLine** instead.
-
-    proc writeLine*[Ty](f: File, x: varargs[Ty, `$`]) {.inline,
-                             tags: [WriteIOEffect], benign.}
-      ## writes the values `x` to `f` and then writes "\\n".
-      ## May throw an IO exception.
-
-    proc getFileSize*(f: File): int64 {.tags: [ReadIOEffect], benign.}
-      ## retrieves the file size (in bytes) of `f`.
-
-    proc readBytes*(f: File, a: var openArray[int8|uint8], start, len: Natural): int {.
-      tags: [ReadIOEffect], benign.}
-      ## reads `len` bytes into the buffer `a` starting at ``a[start]``. Returns
-      ## the actual number of bytes that have been read which may be less than
-      ## `len` (if not as many bytes are remaining), but not greater.
-
-    proc readChars*(f: File, a: var openArray[char], start, len: Natural): int {.
-      tags: [ReadIOEffect], benign.}
-      ## reads `len` bytes into the buffer `a` starting at ``a[start]``. Returns
-      ## the actual number of bytes that have been read which may be less than
-      ## `len` (if not as many bytes are remaining), but not greater.
-      ##
-      ## **Warning:** The buffer `a` must be pre-allocated. This can be done
-      ## using, for example, ``newString``.
-
-    proc readBuffer*(f: File, buffer: pointer, len: Natural): int {.
-      tags: [ReadIOEffect], benign.}
-      ## reads `len` bytes into the buffer pointed to by `buffer`. Returns
-      ## the actual number of bytes that have been read which may be less than
-      ## `len` (if not as many bytes are remaining), but not greater.
-
-    proc writeBytes*(f: File, a: openArray[int8|uint8], start, len: Natural): int {.
-      tags: [WriteIOEffect], benign.}
-      ## writes the bytes of ``a[start..start+len-1]`` to the file `f`. Returns
-      ## the number of actual written bytes, which may be less than `len` in case
-      ## of an error.
-
-    proc writeChars*(f: File, a: openArray[char], start, len: Natural): int {.
-      tags: [WriteIOEffect], benign.}
-      ## writes the bytes of ``a[start..start+len-1]`` to the file `f`. Returns
-      ## the number of actual written bytes, which may be less than `len` in case
-      ## of an error.
-
-    proc writeBuffer*(f: File, buffer: pointer, len: Natural): int {.
-      tags: [WriteIOEffect], benign.}
-      ## writes the bytes of buffer pointed to by the parameter `buffer` to the
-      ## file `f`. Returns the number of actual written bytes, which may be less
-      ## than `len` in case of an error.
-
-    proc setFilePos*(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) {.benign.}
-      ## sets the position of the file pointer that is used for read/write
-      ## operations. The file's first byte has the index zero.
-
-    proc getFilePos*(f: File): int64 {.benign.}
-      ## retrieves the current position of the file pointer that is used to
-      ## read from the file `f`. The file's first byte has the index zero.
-
-    proc getFileHandle*(f: File): FileHandle
-      ## returns the OS file handle of the file ``f``. This is only useful for
-      ## platform specific programming.
+      when not defined(nimscript): # avoid semantic checking
+        let minlen = min(x.len, y.len)
+        result = int(nimCmpMem(x.cstring, y.cstring, cast[csize_t](minlen)))
+        if result == 0:
+          result = x.len - y.len
 
   when declared(newSeq):
     proc cstringArrayToSeq*(a: cstringArray, len: Natural): seq[string] =
-      ## converts a ``cstringArray`` to a ``seq[string]``. `a` is supposed to be
-      ## of length ``len``.
+      ## Converts a `cstringArray` to a `seq[string]`. `a` is supposed to be
+      ## of length `len`.
+      if a == nil: return @[]
       newSeq(result, len)
       for i in 0..len-1: result[i] = $a[i]
 
     proc cstringArrayToSeq*(a: cstringArray): seq[string] =
-      ## converts a ``cstringArray`` to a ``seq[string]``. `a` is supposed to be
-      ## terminated by ``nil``.
+      ## Converts a `cstringArray` to a `seq[string]`. `a` is supposed to be
+      ## terminated by `nil`.
+      if a == nil: return @[]
       var L = 0
       while a[L] != nil: inc(L)
       result = cstringArrayToSeq(a, L)
 
-  # -------------------------------------------------------------------------
 
-  when declared(alloc0) and declared(dealloc):
-    proc allocCStringArray*(a: openArray[string]): cstringArray =
-      ## creates a NULL terminated cstringArray from `a`. The result has to
-      ## be freed with `deallocCStringArray` after it's not needed anymore.
-      result = cast[cstringArray](alloc0((a.len+1) * sizeof(cstring)))
+when not defined(js) and declared(alloc0) and declared(dealloc):
+  proc allocCStringArray*(a: openArray[string]): cstringArray =
+    ## Creates a NULL terminated cstringArray from `a`. The result has to
+    ## be freed with `deallocCStringArray` after it's not needed anymore.
+    result = cast[cstringArray](alloc0((a.len+1) * sizeof(cstring)))
 
-      let x = cast[ptr UncheckedArray[string]](a)
-      for i in 0 .. a.high:
-        result[i] = cast[cstring](alloc0(x[i].len+1))
-        copyMem(result[i], addr(x[i][0]), x[i].len)
+    let x = cast[ptr UncheckedArray[string]](a)
+    for i in 0 .. a.high:
+      result[i] = cast[cstring](alloc0(x[i].len+1))
+      copyMem(result[i], addr(x[i][0]), x[i].len)
 
-    proc deallocCStringArray*(a: cstringArray) =
-      ## frees a NULL terminated cstringArray.
-      var i = 0
-      while a[i] != nil:
-        dealloc(a[i])
-        inc(i)
-      dealloc(a)
-
-  when not defined(nimscript):
-    proc atomicInc*(memLoc: var int, x: int = 1): int {.inline,
-      discardable, benign.}
-      ## atomic increment of `memLoc`. Returns the value after the operation.
-
-    proc atomicDec*(memLoc: var int, x: int = 1): int {.inline,
-      discardable, benign.}
-      ## atomic decrement of `memLoc`. Returns the value after the operation.
-
-    include "system/atomics"
+  proc deallocCStringArray*(a: cstringArray) =
+    ## Frees a NULL terminated cstringArray.
+    var i = 0
+    while a[i] != nil:
+      dealloc(a[i])
+      inc(i)
+    dealloc(a)
 
-    type
-      PSafePoint = ptr TSafePoint
-      TSafePoint {.compilerproc, final.} = object
-        prev: PSafePoint # points to next safe point ON THE STACK
-        status: int
-        context: C_JmpBuf
-        hasRaiseAction: bool
-        raiseAction: proc (e: ref Exception): bool {.closure.}
-      SafePoint = TSafePoint
-  #  {.deprecated: [TSafePoint: SafePoint].}
+when notJSnotNims and not gotoBasedExceptions:
+  type
+    PSafePoint = ptr TSafePoint
+    TSafePoint {.compilerproc, final.} = object
+      prev: PSafePoint # points to next safe point ON THE STACK
+      status: int
+      context: C_JmpBuf
+    SafePoint = TSafePoint
 
+when not defined(js):
   when declared(initAllocator):
     initAllocator()
   when hasThreadSupport:
-    const insideRLocksModule = false
-    include "system/syslocks"
-    when hostOS != "standalone": include "system/threads"
+    when hostOS != "standalone":
+      include system/threadimpl
+      when not defined(nimPreviewSlimSystem):
+        import std/typedthreads
+        export typedthreads
+
   elif not defined(nogc) and not defined(nimscript):
     when not defined(useNimRtl) and not defined(createNimRtl): initStackBottom()
     when declared(initGC): initGC()
 
-  when not defined(nimscript):
-    proc setControlCHook*(hook: proc () {.noconv.})
-      ## allows you to override the behaviour of your application when CTRL+C
-      ## is pressed. Only one such hook is supported.
+when notJSnotNims:
+  proc setControlCHook*(hook: proc () {.noconv.})
+    ## Allows you to override the behaviour of your application when CTRL+C
+    ## is pressed. Only one such hook is supported.
+    ## Example:
+    ##
+    ##   ```nim
+    ##   proc ctrlc() {.noconv.} =
+    ##     echo "Ctrl+C fired!"
+    ##     # do clean up stuff
+    ##     quit()
+    ##
+    ##   setControlCHook(ctrlc)
+    ##   ```
 
-    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
-      ## is proclaimed to have no IO effect!
-    when hostOS != "standalone":
-      proc getStackTrace*(): string {.gcsafe.}
-        ## gets the current stack trace. This only works for debug builds.
-
-      proc getStackTrace*(e: ref Exception): string {.gcsafe.}
-        ## gets the stack trace associated with `e`, which is the stack that
-        ## lead to the ``raise`` statement. This only works for debug builds.
-
-    {.push stack_trace: off, profiler:off.}
-    when defined(memtracker):
-      include "system/memtracker"
-
-    when not defined(nimscript):
-      proc zeroMem(p: pointer, size: Natural) =
-        c_memset(p, 0, size)
-        when declared(memTrackerOp):
-          memTrackerOp("zeroMem", p, size)
-      proc copyMem(dest, source: pointer, size: Natural) =
-        c_memcpy(dest, source, size)
-        when declared(memTrackerOp):
-          memTrackerOp("copyMem", dest, size)
-      proc moveMem(dest, source: pointer, size: Natural) =
-        c_memmove(dest, source, size)
-        when declared(memTrackerOp):
-          memTrackerOp("moveMem", dest, size)
-      proc equalMem(a, b: pointer, size: Natural): bool =
-        c_memcmp(a, b, size) == 0
-
-    when hostOS == "standalone":
-      include "system/embedded"
-    else:
-      include "system/excpt"
-    include "system/chcks"
+  when not defined(noSignalHandler) and not defined(useNimRtl):
+    proc unsetControlCHook*()
+      ## Reverts a call to setControlCHook.
 
-    # we cannot compile this with stack tracing on
-    # as it would recurse endlessly!
-    include "system/arithm"
-    {.pop.} # stack trace
-  {.pop.} # stack trace
+  when hostOS != "standalone":
+    proc getStackTrace*(): string {.gcsafe.}
+      ## Gets the current stack trace. This only works for debug builds.
 
-  when hostOS != "standalone" and not defined(nimscript):
-    include "system/dyncalls"
-  when not defined(nimscript):
-    include "system/sets"
+    proc getStackTrace*(e: ref Exception): string {.gcsafe.}
+      ## Gets the stack trace associated with `e`, which is the stack that
+      ## lead to the `raise` statement. This only works for debug builds.
 
-    when defined(gogc):
-      const GenericSeqSize = (3 * sizeof(int))
-    else:
-      const GenericSeqSize = (2 * sizeof(int))
-
-    proc getDiscriminant(aa: pointer, n: ptr TNimNode): int =
-      sysAssert(n.kind == nkCase, "getDiscriminant: node != nkCase")
-      var d: int
-      var a = cast[ByteAddress](aa)
-      case n.typ.size
-      of 1: d = ze(cast[ptr int8](a +% n.offset)[])
-      of 2: d = ze(cast[ptr int16](a +% n.offset)[])
-      of 4: d = int(cast[ptr int32](a +% n.offset)[])
-      else: sysAssert(false, "getDiscriminant: invalid n.typ.size")
-      return d
-
-    proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode =
-      var discr = getDiscriminant(aa, n)
-      if discr <% n.len:
-        result = n.sons[discr]
-        if result == nil: result = n.sons[n.len]
-        # n.sons[n.len] contains the ``else`` part (but may be nil)
-      else:
-        result = n.sons[n.len]
-
-    {.push profiler:off.}
-    when hasAlloc: include "system/mmdisp"
-    {.pop.}
-    {.push stack_trace: off, profiler:off.}
-    when hasAlloc: include "system/sysstr"
-    {.pop.}
-
-    when hostOS != "standalone": include "system/sysio"
-    when hasThreadSupport:
-      when hostOS != "standalone": include "system/channels"
+  {.push stackTrace: off, profiler: off.}
+  when defined(memtracker):
+    include "system/memtracker"
+
+  when hostOS == "standalone":
+    include "system/embedded"
   else:
-    include "system/sysio"
+    include "system/excpt"
+  include "system/chcks"
 
-  when not defined(nimscript) and hostOS != "standalone":
-    iterator lines*(filename: string): TaintedString {.tags: [ReadIOEffect].} =
-      ## Iterates over any line in the file named `filename`.
-      ##
-      ## If the file does not exist `EIO` is raised. The trailing newline
-      ## character(s) are removed from the iterated lines. Example:
-      ##
-      ## .. code-block:: nim
-      ##   import strutils
-      ##
-      ##   proc transformLetters(filename: string) =
-      ##     var buffer = ""
-      ##     for line in filename.lines:
-      ##       buffer.add(line.replace("a", "0") & '\x0A')
-      ##     writeFile(filename, buffer)
-      var f = open(filename, bufSize=8000)
-      defer: close(f)
-      var res = TaintedString(newStringOfCap(80))
-      while f.readLine(res): yield res
-
-    iterator lines*(f: File): TaintedString {.tags: [ReadIOEffect].} =
-      ## Iterate over any line in the file `f`.
-      ##
-      ## The trailing newline character(s) are removed from the iterated lines.
-      ## Example:
-      ##
-      ## .. code-block:: nim
-      ##   proc countZeros(filename: File): tuple[lines, zeros: int] =
-      ##     for line in filename.lines:
-      ##       for letter in line:
-      ##         if letter == '0':
-      ##           result.zeros += 1
-      ##       result.lines += 1
-      var res = TaintedString(newStringOfCap(80))
-      while f.readLine(res): yield res
-
-  when not defined(nimscript) and hasAlloc:
-    include "system/assign"
-    include "system/repr"
+  # we cannot compile this with stack tracing on
+  # as it would recurse endlessly!
+  include "system/integerops"
+  {.pop.}
 
-  when hostOS != "standalone" and not defined(nimscript):
-    proc getCurrentException*(): ref Exception {.compilerRtl, inl, benign.} =
-      ## retrieves the current exception; if there is none, nil is returned.
-      result = currException
-
-    proc getCurrentExceptionMsg*(): string {.inline, benign.} =
-      ## retrieves the error message that was attached to the current
-      ## exception; if there is none, "" is returned.
-      var e = getCurrentException()
-      return if e == nil: "" else: e.msg
-
-    proc onRaise*(action: proc(e: ref Exception): bool{.closure.}) =
-      ## can be used in a ``try`` statement to setup a Lisp-like
-      ## `condition system`:idx:\: This prevents the 'raise' statement to
-      ## raise an exception but instead calls ``action``.
-      ## If ``action`` returns false, the exception has been handled and
-      ## does not propagate further through the call stack.
-      if not isNil(excHandler):
-        excHandler.hasRaiseAction = true
-        excHandler.raiseAction = action
-
-    proc setCurrentException*(exc: ref Exception) {.inline, benign.} =
-      ## sets the current exception.
-      ##
-      ## **Warning**: Only use this if you know what you are doing.
-      currException = exc
 
-  {.push stack_trace: off, profiler:off.}
-  when defined(endb) and not defined(nimscript):
-    include "system/debugger"
+when not defined(js):
+  # this is a hack: without this when statement, you would get:
+  # Error: system module needs: nimGCvisit
+  {.pop.} # stackTrace: off, profiler: off
 
-  when defined(profiler) or defined(memProfiler):
-    include "system/profiler"
-  {.pop.} # stacktrace
-
-  when not defined(nimscript):
-    proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} =
-      ## retrieves the raw proc pointer of the closure `x`. This is
-      ## useful for interfacing closures with C.
-      {.emit: """
-      `result` = `x`.ClP_0;
-      """.}
-
-    proc rawEnv*[T: proc](x: T): pointer {.noSideEffect, inline.} =
-      ## retrieves the raw environment pointer of the closure `x`. This is
-      ## useful for interfacing closures with C.
-      {.emit: """
-      `result` = `x`.ClE_0;
-      """.}
-
-    proc finished*[T: proc](x: T): bool {.noSideEffect, inline.} =
-      ## can be used to determine if a first class iterator has finished.
-      {.emit: """
-      `result` = ((NI*) `x`.ClE_0)[1] < 0;
-      """.}
-
-elif defined(JS):
-  # Stubs:
-  proc getOccupiedMem(): int = return -1
-  proc getFreeMem(): int = return -1
-  proc getTotalMem(): int = return -1
-
-  proc dealloc(p: pointer) = discard
-  proc alloc(size: Natural): pointer = discard
-  proc alloc0(size: Natural): pointer = discard
-  proc realloc(p: pointer, newsize: Natural): pointer = discard
-
-  proc allocShared(size: Natural): pointer = discard
-  proc allocShared0(size: Natural): pointer = discard
-  proc deallocShared(p: pointer) = discard
-  proc reallocShared(p: pointer, newsize: Natural): pointer = discard
-
-  when defined(JS):
-    include "system/jssys"
-    include "system/reprjs"
-  elif defined(nimscript):
-    proc cmp(x, y: string): int =
-      if x == y: return 0
-      if x < y: return -1
-      return 1
-
-  when defined(nimffi):
-    include "system/sysio"
-
-when not defined(nimNoArrayToString):
-  proc `$`*[T, IDX](x: array[IDX, T]): string =
-    ## generic ``$`` operator for arrays that is lifted from the components
-    collectionToString(x, "[", ", ", "]")
-
-proc `$`*[T](x: openarray[T]): string =
-  ## generic ``$`` operator for openarrays that is lifted from the components
-  ## of `x`. Example:
-  ##
-  ## .. code-block:: nim
-  ##   $(@[23, 45].toOpenArray(0, 1)) == "[23, 45]"
-  collectionToString(x, "[", ", ", "]")
 
-proc quit*(errormsg: string, errorcode = QuitFailure) {.noReturn.} =
-  ## a shorthand for ``echo(errormsg); quit(errorcode)``.
-  echo(errormsg)
-  quit(errorcode)
 
-{.pop.} # checks
-{.pop.} # hints
+when notJSnotNims:
+  when hostOS != "standalone" and hostOS != "any":
+    type
+      LibHandle = pointer       # private type
+      ProcAddr = pointer        # library loading and loading of procs:
 
-when not defined(JS):
-  proc likely_proc(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
-  proc unlikely_proc(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
+    proc nimLoadLibrary(path: string): LibHandle {.compilerproc, hcrInline, nonReloadable.}
+    proc nimUnloadLibrary(lib: LibHandle) {.compilerproc, hcrInline, nonReloadable.}
+    proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {.compilerproc, hcrInline, nonReloadable.}
 
-template likely*(val: bool): bool =
-  ## Hints the optimizer that `val` is likely going to be true.
-  ##
-  ## You can use this template to decorate a branch condition. On certain
-  ## platforms this can help the processor predict better which branch is
-  ## going to be run. Example:
-  ##
-  ## .. code-block:: nim
-  ##   for value in inputValues:
-  ##     if likely(value <= 100):
-  ##       process(value)
-  ##     else:
-  ##       echo "Value too big!"
-  ##
-  ## On backends without branch prediction (JS and the nimscript VM), this
-  ## template will not affect code execution.
-  when nimvm:
-    val
-  else:
-    when defined(JS):
-      val
-    else:
-      likely_proc(val)
+    proc nimLoadLibraryError(path: string) {.compilerproc, hcrInline, nonReloadable.}
 
-template unlikely*(val: bool): bool =
-  ## Hints the optimizer that `val` is likely going to be false.
-  ##
-  ## You can use this proc to decorate a branch condition. On certain
-  ## platforms this can help the processor predict better which branch is
-  ## going to be run. Example:
-  ##
-  ## .. code-block:: nim
-  ##   for value in inputValues:
-  ##     if unlikely(value > 100):
-  ##       echo "Value too big!"
-  ##     else:
-  ##       process(value)
-  ##
-  ## On backends without branch prediction (JS and the nimscript VM), this
-  ## template will not affect code execution.
-  when nimvm:
-    val
+    include "system/dyncalls"
+
+  import system/countbits_impl
+  include "system/sets"
+
+  when defined(gogc):
+    const GenericSeqSize = (3 * sizeof(int))
   else:
-    when defined(JS):
-      val
+    const GenericSeqSize = (2 * sizeof(int))
+
+  proc getDiscriminant(aa: pointer, n: ptr TNimNode): uint =
+    sysAssert(n.kind == nkCase, "getDiscriminant: node != nkCase")
+    var d: uint
+    var a = cast[uint](aa)
+    case n.typ.size
+    of 1: d = uint(cast[ptr uint8](a + uint(n.offset))[])
+    of 2: d = uint(cast[ptr uint16](a + uint(n.offset))[])
+    of 4: d = uint(cast[ptr uint32](a + uint(n.offset))[])
+    of 8: d = uint(cast[ptr uint64](a + uint(n.offset))[])
+    else:
+      d = 0'u
+      sysAssert(false, "getDiscriminant: invalid n.typ.size")
+    return d
+
+  proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode =
+    var discr = getDiscriminant(aa, n)
+    if discr < cast[uint](n.len):
+      result = n.sons[discr]
+      if result == nil: result = n.sons[n.len]
+      # n.sons[n.len] contains the `else` part (but may be nil)
     else:
-      unlikely_proc(val)
+      result = n.sons[n.len]
 
-proc `/`*(x, y: int): float {.inline, noSideEffect.} =
-  ## integer division that results in a float.
-  result = toFloat(x) / toFloat(y)
+when notJSnotNims and hasAlloc:
+  {.push profiler: off.}
+  include "system/mmdisp"
+  {.pop.}
+  {.push stackTrace: off, profiler: off.}
+  when not defined(nimSeqsV2):
+    include "system/sysstr"
+  {.pop.}
 
-type
-  BackwardsIndex* = distinct int ## type that is constructed by ``^`` for
-                                 ## reversed array accesses.
-
-template `^`*(x: int): BackwardsIndex = BackwardsIndex(x)
-  ## builtin `roof`:idx: operator that can be used for convenient array access.
-  ## ``a[^x]`` is a shortcut for ``a[a.len-x]``.
-
-template `..^`*(a, b: untyped): untyped =
-  ## a shortcut for '.. ^' to avoid the common gotcha that a space between
-  ## '..' and '^' is required.
-  a .. ^b
-
-template `..<`*(a, b: untyped): untyped =
-  ## a shortcut for 'a .. (when b is BackwardsIndex: succ(b) else: pred(b))'.
-  a .. (when b is BackwardsIndex: succ(b) else: pred(b))
-
-when defined(nimNewRoof):
-  iterator `..<`*[T](a, b: T): T =
-    var i = T(a)
-    while i < b:
-      yield i
-      inc i
-else:
-  iterator `..<`*[S, T](a: S, b: T): T =
-    var i = T(a)
-    while i < b:
-      yield i
-      inc i
-
-template spliceImpl(s, a, L, b: untyped): untyped =
-  # make room for additional elements or cut:
-  var shift = b.len - max(0,L)  # ignore negative slice size
-  var newLen = s.len + shift
-  if shift > 0:
-    # enlarge:
-    setLen(s, newLen)
-    for i in countdown(newLen-1, a+b.len): shallowCopy(s[i], s[i-shift])
-  else:
-    for i in countup(a+b.len, newLen-1): shallowCopy(s[i], s[i-shift])
-    # cut down:
-    setLen(s, newLen)
-  # fill the hole:
-  for i in 0 ..< b.len: s[a+i] = b[i]
+  include "system/strmantle"
+  include "system/assign"
 
-template `^^`(s, i: untyped): untyped =
-  (when i is BackwardsIndex: s.len - int(i) else: int(i))
+  when not defined(nimV2):
+    include "system/repr"
 
-when hasAlloc or defined(nimscript):
-  proc `[]`*[T, U](s: string, x: HSlice[T, U]): string {.inline.} =
-    ## slice operation for strings.
-    ## returns the inclusive range [s[x.a], s[x.b]]:
-    ##
-    ## .. code-block:: nim
-    ##    var s = "abcdef"
-    ##    assert s[1..3] == "bcd"
-    let a = s ^^ x.a
-    let L = (s ^^ x.b) - a + 1
-    result = newString(L)
-    for i in 0 ..< L: result[i] = s[i + a]
-
-  proc `[]=`*[T, U](s: var string, x: HSlice[T, U], b: string) =
-    ## slice assignment for strings. If
-    ## ``b.len`` is not exactly the number of elements that are referred to
-    ## by `x`, a `splice`:idx: is performed:
-    ##
-    ## .. code-block:: nim
-    ##   var s = "abcdef"
-    ##   s[1 .. ^2] = "xyz"
-    ##   assert s == "axyzf"
-    var a = s ^^ x.a
-    var L = (s ^^ x.b) - a + 1
-    if L == b.len:
-      for i in 0..<L: s[i+a] = b[i]
-    else:
-      spliceImpl(s, a, L, b)
+when notJSnotNims and hasThreadSupport and hostOS != "standalone":
+  when not defined(nimPreviewSlimSystem):
+    include "system/channels_builtin"
 
-proc `[]`*[Idx, T, U, V](a: array[Idx, T], x: HSlice[U, V]): seq[T] =
-  ## slice operation for arrays.
-  ## returns the inclusive range [a[x.a], a[x.b]]:
-  ##
-  ## .. code-block:: nim
-  ##    var a = [1,2,3,4]
-  ##    assert a[0..2] == @[1,2,3]
-  let xa = a ^^ x.a
-  let L = (a ^^ x.b) - xa + 1
-  result = newSeq[T](L)
-  for i in 0..<L: result[i] = a[Idx(i + xa)]
-
-proc `[]=`*[Idx, T, U, V](a: var array[Idx, T], x: HSlice[U, V], b: openArray[T]) =
-  ## slice assignment for arrays.
-  let xa = a ^^ x.a
-  let L = (a ^^ x.b) - xa + 1
-  if L == b.len:
-    for i in 0..<L: a[Idx(i + xa)] = b[i]
-  else:
-    sysFatal(RangeError, "different lengths for slice assignment")
 
-proc `[]`*[T, U, V](s: openArray[T], x: HSlice[U, V]): seq[T] =
-  ## slice operation for sequences.
-  ## returns the inclusive range [s[x.a], s[x.b]]:
-  ##
-  ## .. code-block:: nim
-  ##    var s = @[1,2,3,4]
-  ##    assert s[0..2] == @[1,2,3]
-  let a = s ^^ x.a
-  let L = (s ^^ x.b) - a + 1
-  newSeq(result, L)
-  for i in 0 ..< L: result[i] = s[i + a]
-
-proc `[]=`*[T, U, V](s: var seq[T], x: HSlice[U, V], b: openArray[T]) =
-  ## slice assignment for sequences. If
-  ## ``b.len`` is not exactly the number of elements that are referred to
-  ## by `x`, a `splice`:idx: is performed.
-  let a = s ^^ x.a
-  let L = (s ^^ x.b) - a + 1
-  if L == b.len:
-    for i in 0 ..< L: s[i+a] = b[i]
-  else:
-    spliceImpl(s, a, L, b)
+when notJSnotNims and hostOS != "standalone":
+  proc getCurrentException*(): ref Exception {.compilerRtl, inl, benign.} =
+    ## Retrieves the current exception; if there is none, `nil` is returned.
+    result = currException
 
-proc `[]`*[T](s: openArray[T]; i: BackwardsIndex): T {.inline.} =
-  system.`[]`(s, s.len - int(i))
+  proc nimBorrowCurrentException(): ref Exception {.compilerRtl, inl, benign, nodestroy.} =
+    # .nodestroy here so that we do not produce a write barrier as the
+    # C codegen only uses it in a borrowed way:
+    result = currException
 
-proc `[]`*[Idx, T](a: array[Idx, T]; i: BackwardsIndex): T {.inline.} =
-  a[Idx(a.len - int(i) + int low(a))]
-proc `[]`*(s: string; i: BackwardsIndex): char {.inline.} = s[s.len - int(i)]
+  proc getCurrentExceptionMsg*(): string {.inline, benign.} =
+    ## Retrieves the error message that was attached to the current
+    ## exception; if there is none, `""` is returned.
+    return if currException == nil: "" else: currException.msg
 
-proc `[]`*[T](s: var openArray[T]; i: BackwardsIndex): var T {.inline.} =
-  system.`[]`(s, s.len - int(i))
-proc `[]`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex): var T {.inline.} =
-  a[Idx(a.len - int(i) + int low(a))]
+  proc setCurrentException*(exc: ref Exception) {.inline, benign.} =
+    ## Sets the current exception.
+    ##
+    ## .. warning:: Only use this if you know what you are doing.
+    currException = exc
+elif defined(nimscript):
+  proc getCurrentException*(): ref Exception {.compilerRtl.} = discard
+
+when notJSnotNims:
+  {.push stackTrace: off, profiler: off.}
+  when (defined(profiler) or defined(memProfiler)):
+    include "system/profiler"
+  {.pop.}
 
-proc `[]=`*[T](s: var openArray[T]; i: BackwardsIndex; x: T) {.inline.} =
-  system.`[]=`(s, s.len - int(i), x)
-proc `[]=`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex; x: T) {.inline.} =
-  a[Idx(a.len - int(i) + int low(a))] = x
-proc `[]=`*(s: var string; i: BackwardsIndex; x: char) {.inline.} =
-  s[s.len - int(i)] = x
+  proc rawProc*[T: proc {.closure.} | iterator {.closure.}](x: T): pointer {.noSideEffect, inline.} =
+    ## Retrieves the raw proc pointer of the closure `x`. This is
+    ## useful for interfacing closures with C/C++, hash computations, etc.
+    ## If `rawEnv(x)` returns `nil`, the proc which the result points to
+    ## takes as many parameters as `x`, but with `{.nimcall.}` as its calling
+    ## convention instead of `{.closure.}`, otherwise it takes one more parameter
+    ## which is a `pointer`, and it still has `{.nimcall.}` as its calling convention.
+    ## To invoke the resulted proc, what this returns has to be casted into a `proc`,
+    ## not a `ptr proc`, and, in a case where `rawEnv(x)` returns non-`nil`,
+    ## the last and additional argument has to be the result of `rawEnv(x)`.
+    ## This is not available for the JS target.
+    #[
+    The conversion from function pointer to `void*` is a tricky topic, but this
+    should work at least for c++ >= c++11, e.g. for `dlsym` support.
+    refs: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57869,
+    https://stackoverflow.com/questions/14125474/casts-between-pointer-to-function-and-pointer-to-object-in-c-and-c
+    ]#
+    runnableExamples:
+      proc makeClosure(x: int): (proc(y: int): int) =
+        var n = x
+        result = (
+          proc(y: int): int =
+            n += y
+            return n
+        )
 
-proc slurp*(filename: string): string {.magic: "Slurp".}
-  ## This is an alias for `staticRead <#staticRead>`_.
+      var
+        c1 = makeClosure(10)
+        e = c1.rawEnv()
+        p = c1.rawProc()
 
-proc staticRead*(filename: string): string {.magic: "Slurp".}
-  ## Compile-time `readFile <#readFile>`_ proc for easy `resource`:idx:
-  ## embedding:
-  ##
-  ## .. code-block:: nim
-  ##     const myResource = staticRead"mydatafile.bin"
-  ##
-  ## `slurp <#slurp>`_ is an alias for ``staticRead``.
+      if e.isNil():
+        let c2 = cast[proc(y: int): int {.nimcall.}](p)
+        echo c2(2)
+      else:
+        let c3 = cast[proc(y: int; env: pointer): int {.nimcall.}](p)
+        echo c3(3, e)
+
+    {.emit: """
+    `result` = (void*)`x`.ClP_0;
+    """.}
+
+  proc rawEnv*[T: proc {.closure.} | iterator {.closure.}](x: T): pointer {.noSideEffect, inline.} =
+    ## Retrieves the raw environment pointer of the closure `x`. See also `rawProc`.
+    ## This is not available for the JS target.
+    {.emit: """
+    `result` = `x`.ClE_0;
+    """.}
+
+proc finished*[T: iterator {.closure.}](x: T): bool {.noSideEffect, inline, magic: "Finished".} =
+  ## It can be used to determine if a first class iterator has finished.
+  when defined(js):
+    # TODO: mangle `:state`
+    {.emit: """
+    `result` = (`x`.ClE_0).HEX3Astate < 0;
+    """.}
+  else:
+    {.emit: """
+    `result` = ((NI*) `x`.ClE_0)[1] < 0;
+    """.}
 
-proc gorge*(command: string, input = "", cache = ""): string {.
-  magic: "StaticExec".} = discard
-  ## This is an alias for `staticExec <#staticExec>`_.
+from std/private/digitsutils import addInt
+export addInt
 
-proc staticExec*(command: string, input = "", cache = ""): string {.
-  magic: "StaticExec".} = discard
-  ## Executes an external process at compile-time.
-  ## if `input` is not an empty string, it will be passed as a standard input
-  ## to the executed program.
-  ##
-  ## .. code-block:: nim
-  ##     const buildInfo = "Revision " & staticExec("git rev-parse HEAD") &
-  ##                       "\nCompiled on " & staticExec("uname -v")
-  ##
-  ## `gorge <#gorge>`_ is an alias for ``staticExec``. Note that you can use
-  ## this proc inside a pragma like `passC <nimc.html#passc-pragma>`_ or `passL
-  ## <nimc.html#passl-pragma>`_.
-  ##
-  ## If ``cache`` is not empty, the results of ``staticExec`` are cached within
-  ## the ``nimcache`` directory. Use ``--forceBuild`` to get rid of this caching
-  ## behaviour then. ``command & input & cache`` (the concatenated string) is
-  ## used to determine whether the entry in the cache is still valid. You can
-  ## use versioning information for ``cache``:
-  ##
-  ## .. code-block:: nim
-  ##     const stateMachine = staticExec("dfaoptimizer", "input", "0.8.0")
+when defined(js) and not defined(nimscript):
+  # nimscript can be defined if config file for js compilation
+  include "system/jssys"
+  include "system/reprjs"
 
-proc gorgeEx*(command: string, input = "", cache = ""): tuple[output: string,
-                                                              exitCode: int] =
-  ## Same as `gorge` but also returns the precious exit code.
-  discard
 
-proc `+=`*[T: SomeOrdinal|uint|uint64](x: var T, y: T) {.
-  magic: "Inc", noSideEffect.}
-  ## Increments an ordinal
+when defined(nimNoQuit):
+  proc quit*(errorcode: int = QuitSuccess) = discard "ignoring quit"
+
+elif defined(nimdoc):
+  proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.}
+    ## Stops the program immediately with an exit code.
+    ##
+    ## Before stopping the program the "exit procedures" are called in the
+    ## opposite order they were added with `addExitProc <exitprocs.html#addExitProc,proc)>`_.
+    ##
+    ## The proc `quit(QuitSuccess)` is called implicitly when your nim
+    ## program finishes without incident for platforms where this is the
+    ## expected behavior. A raised unhandled exception is
+    ## equivalent to calling `quit(QuitFailure)`.
+    ##
+    ## Note that this is a *runtime* call and using `quit` inside a macro won't
+    ## have any compile time effect. If you need to stop the compiler inside a
+    ## macro, use the `error <manual.html#pragmas-error-pragma>`_ or `fatal
+    ## <manual.html#pragmas-fatal-pragma>`_ pragmas.
+    ##
+    ## .. warning:: `errorcode` gets saturated when it exceeds the valid range
+    ##    on the specific platform. On Posix, the valid range is `low(int8)..high(int8)`.
+    ##    On Windows, the valid range is `low(int32)..high(int32)`. For instance,
+    ##    `quit(int(0x100000000))` is equal to `quit(127)` on Linux.
+    ##
+    ## .. danger:: In almost all cases, in particular in library code, prefer
+    ##   alternatives, e.g. `raiseAssert` or raise a `Defect`.
+    ##   `quit` bypasses regular control flow in particular `defer`,
+    ##   `try`, `catch`, `finally` and `destructors`, and exceptions that may have been
+    ##   raised by an `addExitProc` proc, as well as cleanup code in other threads.
+    ##   It does *not* call the garbage collector to free all the memory,
+    ##   unless an `addExitProc` proc calls `GC_fullCollect <#GC_fullCollect>`_.
 
-proc `-=`*[T: SomeOrdinal|uint|uint64](x: var T, y: T) {.
-  magic: "Dec", noSideEffect.}
-  ## Decrements an ordinal
+elif defined(genode):
+  proc quit*(errorcode: int = QuitSuccess) {.inline, noreturn.} =
+    rawQuit(errorcode)
 
-proc `*=`*[T: SomeOrdinal|uint|uint64](x: var T, y: T) {.
-  inline, noSideEffect.} =
-  ## Binary `*=` operator for ordinals
-  x = x * y
+elif defined(js) and defined(nodejs) and not defined(nimscript):
+  proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit",
+    importc: "process.exit", noreturn.}
 
-proc `+=`*[T: float|float32|float64] (x: var T, y: T) {.
-  inline, noSideEffect.} =
-  ## Increments in place a floating point number
-  x = x + y
+else:
+  proc quit*(errorcode: int = QuitSuccess) {.inline, noreturn.} =
+    when defined(posix): # posix uses low 8 bits
+      type ExitCodeRange = int8
+    else: # win32 uses low 32 bits
+      type ExitCodeRange = cint
+    when sizeof(errorcode) > sizeof(ExitCodeRange):
+      if errorcode < low(ExitCodeRange):
+        rawQuit(low(ExitCodeRange).cint)
+      elif errorcode > high(ExitCodeRange):
+        rawQuit(high(ExitCodeRange).cint)
+      else:
+        rawQuit(errorcode.cint)
+    else:
+      rawQuit(errorcode.cint)
 
-proc `-=`*[T: float|float32|float64] (x: var T, y: T) {.
-  inline, noSideEffect.} =
-  ## Decrements in place a floating point number
-  x = x - y
+proc quit*(errormsg: string, errorcode = QuitFailure) {.noreturn.} =
+  ## A shorthand for `echo(errormsg); quit(errorcode)`.
+  when defined(nimscript) or defined(js) or (hostOS == "standalone"):
+    echo errormsg
+  else:
+    when nimvm:
+      echo errormsg
+    else:
+      cstderr.rawWrite(errormsg)
+      cstderr.rawWrite("\n")
+  quit(errorcode)
 
-proc `*=`*[T: float|float32|float64] (x: var T, y: T) {.
-  inline, noSideEffect.} =
-  ## Multiplies in place a floating point number
-  x = x * y
+{.pop.} # checks: off
+# {.pop.} # hints: off
 
-proc `/=`*(x: var float64, y: float64) {.inline, noSideEffect.} =
-  ## Divides in place a floating point number
-  x = x / y
+include "system/indices"
 
-proc `/=`*[T: float|float32](x: var T, y: T) {.inline, noSideEffect.} =
-  ## Divides in place a floating point number
-  x = x / y
+proc `&=`*(x: var string, y: string) {.magic: "AppendStrStr", noSideEffect.}
+  ## Appends in place to a string.
+  ##   ```nim
+  ##   var a = "abc"
+  ##   a &= "de" # a <- "abcde"
+  ##   ```
 
-proc `&=`* (x: var string, y: string) {.magic: "AppendStrStr", noSideEffect.}
 template `&=`*(x, y: typed) =
-  ## generic 'sink' operator for Nim. For files an alias for ``write``.
-  ## If not specialized further an alias for ``add``.
+  ## Generic 'sink' operator for Nim.
+  ##
+  ## If not specialized further, an alias for `add`.
   add(x, y)
-when declared(File):
-  template `&=`*(f: File, x: typed) = write(f, x)
 
-proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.}
-  ## converts the AST of `x` into a string representation. This is very useful
-  ## for debugging.
+when compileOption("rangechecks"):
+  template rangeCheck*(cond) =
+    ## Helper for performing user-defined range checks.
+    ## Such checks will be performed only when the `rangechecks`
+    ## compile-time option is enabled.
+    if not cond: sysFatal(RangeDefect, "range check failed")
+else:
+  template rangeCheck*(cond) = discard
 
-proc instantiationInfo*(index = -1, fullPaths = false): tuple[
-  filename: string, line: int, column: int] {. magic: "InstantiationInfo", noSideEffect.}
-  ## provides access to the compiler's instantiation stack line information
-  ## of a template.
-  ##
-  ## While similar to the `caller info`:idx: of other languages, it is determined
-  ## at compile time.
-  ##
-  ## This proc is mostly useful for meta programming (eg. ``assert`` template)
-  ## to retrieve information about the current filename and line number.
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   import strutils
-  ##
-  ##   template testException(exception, code: untyped): typed =
-  ##     try:
-  ##       let pos = instantiationInfo()
-  ##       discard(code)
-  ##       echo "Test failure at $1:$2 with '$3'" % [pos.filename,
-  ##         $pos.line, astToStr(code)]
-  ##       assert false, "A test expecting failure succeeded?"
-  ##     except exception:
-  ##       discard
-  ##
-  ##   proc tester(pos: int): int =
-  ##     let
-  ##       a = @[1, 2, 3]
-  ##     result = a[pos]
-  ##
-  ##   when isMainModule:
-  ##     testException(IndexError, tester(30))
-  ##     testException(IndexError, tester(1))
-  ##     # --> Test failure at example.nim:20 with 'tester(1)'
+when not defined(gcArc) and not defined(gcOrc) and not defined(gcAtomicArc):
+  proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} =
+    ## Marks a sequence `s` as `shallow`:idx:. Subsequent assignments will not
+    ## perform deep copies of `s`.
+    ##
+    ## This is only useful for optimization purposes.
+    if s.len == 0: return
+    when not defined(js) and not defined(nimscript) and not defined(nimSeqsV2):
+      var s = cast[PGenericSeq](s)
+      {.noSideEffect.}:
+        s.reserved = s.reserved or seqShallowFlag
+
+  proc shallow*(s: var string) {.noSideEffect, inline.} =
+    ## Marks a string `s` as `shallow`:idx:. Subsequent assignments will not
+    ## perform deep copies of `s`.
+    ##
+    ## This is only useful for optimization purposes.
+    when not defined(js) and not defined(nimscript) and not defined(nimSeqsV2):
+      var s = cast[PGenericSeq](s)
+      if s == nil:
+        s = cast[PGenericSeq](newString(0))
+      # string literals cannot become 'shallow':
+      if (s.reserved and strlitFlag) == 0:
+        {.noSideEffect.}:
+          s.reserved = s.reserved or seqShallowFlag
 
-template currentSourcePath*: string = instantiationInfo(-1, true).filename
-  ## returns the full file-system path of the current source
+type
+  NimNodeObj = object
 
-proc raiseAssert*(msg: string) {.noinline.} =
-  sysFatal(AssertionError, msg)
+  NimNode* {.magic: "PNimrodNode".} = ref NimNodeObj
+    ## Represents a Nim AST node. Macros operate on this type.
 
-proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} =
-  # trick the compiler to not list ``AssertionError`` when called
-  # by ``assert``.
-  type Hide = proc (msg: string) {.noinline, raises: [], noSideEffect,
-                                    tags: [].}
-  Hide(raiseAssert)(msg)
+type
+  ForLoopStmt* {.compilerproc.} = object ## \
+    ## A special type that marks a macro as a `for-loop macro`:idx:.
+    ## See `"For Loop Macro" <manual.html#macros-for-loop-macro>`_.
 
-template assert*(cond: bool, msg = "") =
-  ## Raises ``AssertionError`` with `msg` if `cond` is false. Note
-  ## that ``AssertionError`` is hidden from the effect system, so it doesn't
-  ## produce ``{.raises: [AssertionError].}``. This exception is only supposed
-  ## to be caught by unit testing frameworks.
-  ##
-  ## The compiler may not generate any code at all for ``assert`` if it is
-  ## advised to do so through the ``-d:release`` or ``--assertions:off``
-  ## `command line switches <nimc.html#command-line-switches>`_.
-  bind instantiationInfo
-  mixin failedAssertImpl
-  when compileOption("assertions"):
-    {.line.}:
-      if not cond: failedAssertImpl(astToStr(cond) & ' ' & msg)
-
-template doAssert*(cond: bool, msg = "") =
-  ## same as `assert` but is always turned on and not affected by the
-  ## ``--assertions`` command line switch.
-  bind instantiationInfo
-  {.line: instantiationInfo().}:
-    if not cond:
-      raiseAssert(astToStr(cond) & ' ' &
-                  instantiationInfo(-1, false).fileName & '(' &
-                  $instantiationInfo(-1, false).line & ") " & msg)
-
-iterator items*[T](a: seq[T]): T {.inline.} =
-  ## iterates over each item of `a`.
-  var i = 0
-  let L = len(a)
-  while i < L:
-    yield a[i]
-    inc(i)
-    assert(len(a) == L, "seq modified while iterating over it")
-
-iterator mitems*[T](a: var seq[T]): var T {.inline.} =
-  ## iterates over each item of `a` so that you can modify the yielded value.
-  var i = 0
-  let L = len(a)
-  while i < L:
-    yield a[i]
-    inc(i)
-    assert(len(a) == L, "seq modified while iterating over it")
-
-iterator items*(a: string): char {.inline.} =
-  ## iterates over each item of `a`.
-  var i = 0
-  let L = len(a)
-  while i < L:
-    yield a[i]
-    inc(i)
-    assert(len(a) == L, "string modified while iterating over it")
-
-iterator mitems*(a: var string): var char {.inline.} =
-  ## iterates over each item of `a` so that you can modify the yielded value.
-  var i = 0
-  let L = len(a)
-  while i < L:
-    yield a[i]
-    inc(i)
-    assert(len(a) == L, "string modified while iterating over it")
-
-when not defined(nimhygiene):
-  {.pragma: inject.}
-
-template onFailedAssert*(msg, code: untyped): untyped {.dirty.} =
-  ## Sets an assertion failure handler that will intercept any assert
-  ## statements following `onFailedAssert` in the current module scope.
-  ##
-  ## .. code-block:: nim
-  ##  # module-wide policy to change the failed assert
-  ##  # exception type in order to include a lineinfo
-  ##  onFailedAssert(msg):
-  ##    var e = new(TMyError)
-  ##    e.msg = msg
-  ##    e.lineinfo = instantiationInfo(-2)
-  ##    raise e
-  ##
-  template failedAssertImpl(msgIMPL: string): untyped {.dirty.} =
-    let msg = msgIMPL
-    code
-
-proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} =
-  ## marks a sequence `s` as `shallow`:idx:. Subsequent assignments will not
-  ## perform deep copies of `s`. This is only useful for optimization
-  ## purposes.
-  when not defined(JS) and not defined(nimscript):
-    var s = cast[PGenericSeq](s)
-    s.reserved = s.reserved or seqShallowFlag
-
-proc shallow*(s: var string) {.noSideEffect, inline.} =
-  ## marks a string `s` as `shallow`:idx:. Subsequent assignments will not
-  ## perform deep copies of `s`. This is only useful for optimization
-  ## purposes.
-  when not defined(JS) and not defined(nimscript):
-    var s = cast[PGenericSeq](s)
-    # string literals cannot become 'shallow':
-    if (s.reserved and strlitFlag) == 0:
-      s.reserved = s.reserved or seqShallowFlag
+macro varargsLen*(x: varargs[untyped]): int {.since: (1, 1).} =
+  ## returns number of variadic arguments in `x`
+  proc varargsLenImpl(x: NimNode): NimNode {.magic: "LengthOpenArray", noSideEffect.}
+  varargsLenImpl(x)
 
-type
-  NimNodeObj = object
+when defined(nimV2):
+  import system/repr_v2
+  export repr_v2
 
-  NimNode* {.magic: "PNimrodNode".} = ref NimNodeObj
-    ## represents a Nim AST node. Macros operate on this type.
-{.deprecated: [PNimrodNode: NimNode].}
-
-when false:
-  template eval*(blk: typed): typed =
-    ## executes a block of code at compile time just as if it was a macro
-    ## optionally, the block can return an AST tree that will replace the
-    ## eval expression
-    macro payload: typed {.gensym.} = blk
-    payload()
-
-when hasAlloc:
+proc repr*[T, U](x: HSlice[T, U]): string =
+  ## Generic `repr` operator for slices that is lifted from the components
+  ## of `x`. Example:
+  ##   ```Nim
+  ##   $(1 .. 5) == "1 .. 5"
+  ##   ```
+  result = repr(x.a)
+  result.add(" .. ")
+  result.add(repr(x.b))
+
+when hasAlloc or defined(nimscript):
   proc insert*(x: var string, item: string, i = 0.Natural) {.noSideEffect.} =
-    ## inserts `item` into `x` at position `i`.
+    ## Inserts `item` into `x` at position `i`.
+    ##   ```nim
+    ##   var a = "abc"
+    ##   a.insert("zz", 0) # a <- "zzabc"
+    ##   ```
+    if item.len == 0: # prevents self-assignment
+      return
     var xl = x.len
     setLen(x, xl+item.len)
     var j = xl-1
     while j >= i:
-      shallowCopy(x[j+item.len], x[j])
+      when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
+        x[j+item.len] = move x[j]
+      else:
+        shallowCopy(x[j+item.len], x[j])
       dec(j)
     j = 0
     while j < item.len:
       x[j+i] = item[j]
       inc(j)
 
-proc compiles*(x: untyped): bool {.magic: "Compiles", noSideEffect, compileTime.} =
-  ## Special compile-time procedure that checks whether `x` can be compiled
-  ## without any semantic error.
-  ## This can be used to check whether a type supports some operation:
-  ##
-  ## .. code-block:: Nim
-  ##   when compiles(3 + 4):
-  ##     echo "'+' for integers is available"
-  discard
-
 when declared(initDebugger):
   initDebugger()
 
 proc addEscapedChar*(s: var string, c: char) {.noSideEffect, inline.} =
   ## Adds a char to string `s` and applies the following escaping:
   ##
-  ## * replaces any ``\`` by ``\\``
-  ## * replaces any ``'`` by ``\'``
-  ## * replaces any ``"`` by ``\"``
-  ## * replaces any ``\a`` by ``\\a``
-  ## * replaces any ``\b`` by ``\\b``
-  ## * replaces any ``\t`` by ``\\t``
-  ## * replaces any ``\n`` by ``\\n``
-  ## * replaces any ``\v`` by ``\\v``
-  ## * replaces any ``\f`` by ``\\f``
-  ## * replaces any ``\c`` by ``\\c``
-  ## * replaces any ``\e`` by ``\\e``
-  ## * replaces any other character not in the set ``{'\21..'\126'}
-  ##   by ``\xHH`` where ``HH`` is its hexadecimal value.
+  ## * replaces any ``\`` by `\\`
+  ## * replaces any `'` by `\'`
+  ## * replaces any `"` by `\"`
+  ## * replaces any `\a` by `\\a`
+  ## * replaces any `\b` by `\\b`
+  ## * replaces any `\t` by `\\t`
+  ## * replaces any `\n` by `\\n`
+  ## * replaces any `\v` by `\\v`
+  ## * replaces any `\f` by `\\f`
+  ## * replaces any `\r` by `\\r`
+  ## * replaces any `\e` by `\\e`
+  ## * replaces any other character not in the set `{\21..\126}`
+  ##   by `\xHH` where `HH` is its hexadecimal value
   ##
   ## The procedure has been designed so that its output is usable for many
   ## different common syntaxes.
-  ## **Note**: This is not correct for producing Ansi C code!
+  ##
+  ## .. warning:: This is **not correct** for producing ANSI C code!
+  ##
   case c
   of '\a': s.add "\\a" # \x07
   of '\b': s.add "\\b" # \x08
@@ -3977,7 +2579,7 @@ proc addEscapedChar*(s: var string, c: char) {.noSideEffect, inline.} =
   of '\n': s.add "\\n" # \x0A
   of '\v': s.add "\\v" # \x0B
   of '\f': s.add "\\f" # \x0C
-  of '\c': s.add "\\c" # \x0D
+  of '\r': (when defined(nimLegacyAddEscapedCharx0D): s.add "\\c" else: s.add "\\r") # \x0D
   of '\e': s.add "\\e" # \x1B
   of '\\': s.add("\\\\")
   of '\'': s.add("\\'")
@@ -3992,12 +2594,13 @@ proc addEscapedChar*(s: var string, c: char) {.noSideEffect, inline.} =
 
 proc addQuoted*[T](s: var string, x: T) =
   ## Appends `x` to string `s` in place, applying quoting and escaping
-  ## if `x` is a string or char. See
-  ## `addEscapedChar <system.html#addEscapedChar>`_
+  ## if `x` is a string or char.
+  ##
+  ## See `addEscapedChar <#addEscapedChar,string,char>`_
   ## for the escaping scheme. When `x` is a string, characters in the
-  ## range ``{\128..\255}`` are never escaped so that multibyte UTF-8
+  ## range `{\128..\255}` are never escaped so that multibyte UTF-8
   ## characters are untouched (note that this behavior is different from
-  ## ``addEscapedChar``).
+  ## `addEscapedChar`).
   ##
   ## The Nim standard library uses this function on the elements of
   ## collections when producing a string representation of a collection.
@@ -4005,7 +2608,7 @@ proc addQuoted*[T](s: var string, x: T) =
   ## Users may overload `addQuoted` for custom (string-like) types if
   ## they want to implement a customized element representation.
   ##
-  ## .. code-block:: Nim
+  ##   ```nim
   ##   var tmp = ""
   ##   tmp.addQuoted(1)
   ##   tmp.add(", ")
@@ -4013,7 +2616,8 @@ proc addQuoted*[T](s: var string, x: T) =
   ##   tmp.add(", ")
   ##   tmp.addQuoted('c')
   ##   assert(tmp == """1, "string", 'c'""")
-  when T is string:
+  ##   ```
+  when T is string or T is cstring:
     s.add("\"")
     for c in x:
       # Only ASCII chars are escaped to avoid butchering
@@ -4028,38 +2632,25 @@ proc addQuoted*[T](s: var string, x: T) =
     s.addEscapedChar(x)
     s.add("'")
   # prevent temporary string allocation
+  elif T is SomeInteger:
+    s.addInt(x)
+  elif T is SomeFloat:
+    s.addFloat(x)
   elif compiles(s.add(x)):
     s.add(x)
   else:
     s.add($x)
 
-when hasAlloc:
-  # XXX: make these the default (or implement the NilObject optimization)
-  proc safeAdd*[T](x: var seq[T], y: T) {.noSideEffect, deprecated.} =
-    ## Adds ``y`` to ``x`` unless ``x`` is not yet initialized; in that case,
-    ## ``x`` becomes ``@[y]``
-    if x == nil: x = @[y]
-    else: x.add(y)
-
-  proc safeAdd*(x: var string, y: char) {.noSideEffect, deprecated.} =
-    ## Adds ``y`` to ``x``. If ``x`` is ``nil`` it is initialized to ``""``
-    if x == nil: x = ""
-    x.add(y)
-
-  proc safeAdd*(x: var string, y: string) {.noSideEffect, deprecated.} =
-    ## Adds ``y`` to ``x`` unless ``x`` is not yet initalized; in that
-    ## case, ``x`` becomes ``y``
-    if x == nil: x = y
-    else: x.add(y)
-
 proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} =
-  ## generates a tuple constructor expression listing all the local variables
-  ## in the current scope. This is quite fast as it does not rely
+  ## Generates a tuple constructor expression listing all the local variables
+  ## in the current scope.
+  ##
+  ## This is quite fast as it does not rely
   ## on any debug or runtime information. Note that in contrast to what
-  ## the official signature says, the return type is not ``RootObj`` but a
+  ## the official signature says, the return type is *not* `RootObj` but a
   ## tuple of a structure that depends on the current scope. Example:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   proc testLocals() =
   ##     var
   ##       a = "something"
@@ -4074,47 +2665,56 @@ proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} =
   ##   # -> name a with value something
   ##   # -> name b with value 4
   ##   # -> B is 1
+  ##   ```
   discard
 
-when hasAlloc and not defined(nimscript) and not defined(JS):
+when hasAlloc and notJSnotNims:
+  # XXX how to implement 'deepCopy' is an open problem.
   proc deepCopy*[T](x: var T, y: T) {.noSideEffect, magic: "DeepCopy".} =
-    ## performs a deep copy of `y` and copies it into `x`.
+    ## Performs a deep copy of `y` and copies it into `x`.
+    ##
     ## This is also used by the code generator
-    ## for the implementation of ``spawn``.
+    ## for the implementation of `spawn`.
+    ##
+    ## For `--mm:arc` or `--mm:orc` deepcopy support has to be enabled
+    ## via `--deepcopy:on`.
     discard
 
+  proc deepCopy*[T](y: T): T =
+    ## Convenience wrapper around `deepCopy` overload.
+    deepCopy(result, y)
+
   include "system/deepcopy"
 
 proc procCall*(x: untyped) {.magic: "ProcCall", compileTime.} =
-  ## special magic to prohibit dynamic binding for `method`:idx: calls.
+  ## Special magic to prohibit dynamic binding for `method`:idx: calls.
   ## This is similar to `super`:idx: in ordinary OO languages.
-  ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   # 'someMethod' will be resolved fully statically:
   ##   procCall someMethod(a, b)
-  discard
-
-proc xlen*(x: string): int {.magic: "XLenStr", noSideEffect.} = discard
-proc xlen*[T](x: seq[T]): int {.magic: "XLenSeq", noSideEffect.} =
-  ## returns the length of a sequence or a string without testing for 'nil'.
-  ## This is an optimization that rarely makes sense.
+  ##   ```
   discard
 
 
-proc `==` *(x, y: cstring): bool {.magic: "EqCString", noSideEffect,
+proc `==`*(x, y: cstring): bool {.magic: "EqCString", noSideEffect,
                                    inline.} =
   ## Checks for equality between two `cstring` variables.
   proc strcmp(a, b: cstring): cint {.noSideEffect,
     importc, header: "<string.h>".}
   if pointer(x) == pointer(y): result = true
-  elif x.isNil or y.isNil: result = false
+  elif pointer(x) == nil or pointer(y) == nil: result = false
   else: result = strcmp(x, y) == 0
 
 template closureScope*(body: untyped): untyped =
   ## Useful when creating a closure in a loop to capture local loop variables by
-  ## their current iteration values. Example:
+  ## their current iteration values.
   ##
-  ## .. code-block:: nim
+  ## Note: This template may not work in some cases, use
+  ## `capture <sugar.html#capture.m,varargs[typed],untyped>`_ instead.
+  ##
+  ## Example:
+  ##
+  ##   ```nim
   ##   var myClosure : proc()
   ##   # without closureScope:
   ##   for i in 0 .. 5:
@@ -4129,118 +2729,227 @@ template closureScope*(body: untyped): untyped =
   ##       if j == 3:
   ##         myClosure = proc() = echo j
   ##   myClosure() # outputs 3
+  ##   ```
   (proc() = body)()
 
 template once*(body: untyped): untyped =
   ## Executes a block of code only once (the first time the block is reached).
-  ## When hot code reloading is enabled, protects top-level code from being
-  ## re-executed on each module reload.
-  ##
-  ## .. code-block:: nim
-  ## proc draw(t: Triangle) =
-  ##   once:
-  ##     graphicsInit()
-  ##
-  ##   line(t.p1, t.p2)
-  ##   line(t.p2, t.p3)
-  ##   line(t.p3, t.p1)
-  ##
+  ##   ```nim
+  ##   proc draw(t: Triangle) =
+  ##     once:
+  ##       graphicsInit()
+  ##     line(t.p1, t.p2)
+  ##     line(t.p2, t.p3)
+  ##     line(t.p3, t.p1)
+  ##   ```
   var alreadyExecuted {.global.} = false
   if not alreadyExecuted:
     alreadyExecuted = true
     body
 
-{.pop.} #{.push warning[GcMem]: off, warning[Uninit]: off.}
-
-when defined(nimconfig):
-  include "system/nimscript"
+{.pop.} # warning[GcMem]: off, warning[Uninit]: off
 
-when defined(windows) and appType == "console" and defined(nimSetUtf8CodePage):
-  proc setConsoleOutputCP(codepage: cint): cint {.stdcall, dynlib: "kernel32",
-    importc: "SetConsoleOutputCP".}
-  discard setConsoleOutputCP(65001) # 65001 - utf-8 codepage
+proc substr*(s: openArray[char]): string =
+  ## Copies a slice of `s` into a new string and returns this new
+  ## string.
+  runnableExamples:
+    let a = "abcdefgh"
+    assert a.substr(2, 5) == "cdef"
+    assert a.substr(2) == "cdefgh"
+    assert a.substr(5, 99) == "fgh"
+  result = newString(s.len)
+  for i, ch in s:
+    result[i] = ch
+
+proc substr*(s: string, first, last: int): string = # A bug with `magic: Slice` requires this to exist this way
+  ## Copies a slice of `s` into a new string and returns this new
+  ## string.
+  ##
+  ## The bounds `first` and `last` denote the indices of
+  ## the first and last characters that shall be copied. If `last`
+  ## is omitted, it is treated as `high(s)`. If `last >= s.len`, `s.len`
+  ## is used instead: This means `substr` can also be used to `cut`:idx:
+  ## or `limit`:idx: a string's length.
+  runnableExamples:
+    let a = "abcdefgh"
+    assert a.substr(2, 5) == "cdef"
+    assert a.substr(2) == "cdefgh"
+    assert a.substr(5, 99) == "fgh"
 
+  let first = max(first, 0)
+  let L = max(min(last, high(s)) - first + 1, 0)
+  result = newString(L)
+  for i in 0 .. L-1:
+    result[i] = s[i+first]
 
-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 substr*(s: string, first = 0): string =
+  result = substr(s, first, high(s))
 
-template doAssertRaises*(exception, code: untyped): typed =
-  ## Raises ``AssertionError`` if specified ``code`` does not raise the
-  ## specified exception.
-  runnableExamples:
-    doAssertRaises(ValueError):
-      raise newException(ValueError, "Hello World")
-  var wrong = false
-  try:
-    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))
-
-when defined(cpp) and appType != "lib" and
-    not defined(js) and not defined(nimscript) and
-    hostOS != "standalone" and not defined(noCppExceptions):
-  proc setTerminate(handler: proc() {.noconv.})
-    {.importc: "std::set_terminate", header: "<exception>".}
-  setTerminate proc() {.noconv.} =
-    # Remove ourself as a handler, reinstalling the default handler.
-    setTerminate(nil)
-
-    let ex = getCurrentException()
-    let trace = ex.getStackTrace()
-    stderr.write trace & "Error: unhandled exception: " & ex.msg &
-                 " [" & $ex.name & "]\n"
-    quit 1
+when defined(nimconfig):
+  include "system/nimscript"
 
 when not defined(js):
-  proc toOpenArray*[T](x: seq[T]; first, last: int): openarray[T] {.
+  proc toOpenArray*[T](x: ptr UncheckedArray[T]; first, last: int): openArray[T] {.
     magic: "Slice".}
-  proc toOpenArray*[T](x: openarray[T]; first, last: int): openarray[T] {.
+  proc toOpenArray*(x: cstring; first, last: int): openArray[char] {.
     magic: "Slice".}
-  proc toOpenArray*[I, T](x: array[I, T]; first, last: I): openarray[T] {.
+  proc toOpenArrayByte*(x: cstring; first, last: int): openArray[byte] {.
     magic: "Slice".}
-  proc toOpenArray*(x: string; first, last: int): openarray[char] {.
-    magic: "Slice".}
-
 
-type
-  ForLoopStmt* {.compilerProc.} = object ## special type that marks a macro
-                                         ## as a `for-loop macro`:idx:
+proc toOpenArray*[T](x: seq[T]; first, last: int): openArray[T] {.
+  magic: "Slice".}
+  ## Allows passing the slice of `x` from the element at `first` to the element
+  ## at `last` to `openArray[T]` parameters without copying it.
+  ##
+  ## Example:
+  ##   ```nim
+  ##   proc test(x: openArray[int]) =
+  ##     doAssert x == [1, 2, 3]
+  ##
+  ##   let s = @[0, 1, 2, 3, 4]
+  ##   s.toOpenArray(1, 3).test
+  ##   ```
+
+proc toOpenArray*[T](x: openArray[T]; first, last: int): openArray[T] {.
+  magic: "Slice".}
+proc toOpenArray*[I, T](x: array[I, T]; first, last: I): openArray[T] {.
+  magic: "Slice".}
+proc toOpenArray*(x: string; first, last: int): openArray[char] {.
+  magic: "Slice".}
+
+proc toOpenArrayByte*(x: string; first, last: int): openArray[byte] {.
+  magic: "Slice".}
+proc toOpenArrayByte*(x: openArray[char]; first, last: int): openArray[byte] {.
+  magic: "Slice".}
+proc toOpenArrayByte*(x: seq[char]; first, last: int): openArray[byte] {.
+  magic: "Slice".}
+
+proc toOpenArrayChar*(x: openArray[byte]; first, last: int): openArray[char] {.
+  magic: "Slice".}
 
 when defined(genode):
   var componentConstructHook*: proc (env: GenodeEnv) {.nimcall.}
-      ## Hook into the Genode component bootstrap process.
-      ## This hook is called after all globals are initialized.
-      ## When this hook is set the component will not automatically exit,
-      ## call ``quit`` explicitly to do so. This is the only available method
-      ## of accessing the initial Genode environment.
+    ## Hook into the Genode component bootstrap process.
+    ##
+    ## This hook is called after all globals are initialized.
+    ## When this hook is set the component will not automatically exit,
+    ## call `quit` explicitly to do so. This is the only available method
+    ## of accessing the initial Genode environment.
 
   proc nim_component_construct(env: GenodeEnv) {.exportc.} =
-    ## Procedure called during ``Component::construct`` by the loader.
+    ## Procedure called during `Component::construct` by the loader.
     if componentConstructHook.isNil:
-      env.quit(programResult)
+      env.rawQuit(programResult)
         # No native Genode application initialization,
         # exit as would POSIX.
     else:
       componentConstructHook(env)
         # Perform application initialization
         # and return to thread entrypoint.
+
+
+when not defined(nimPreviewSlimSystem):
+  import std/widestrs
+  export widestrs
+
+when notJSnotNims:
+  when defined(windows) and compileOption("threads"):
+    when not declared(addSysExitProc):
+      proc addSysExitProc(quitProc: proc() {.noconv.}) {.importc: "atexit", header: "<stdlib.h>".}
+    var echoLock: SysLock
+    initSysLock echoLock
+    addSysExitProc(proc() {.noconv.} = deinitSys(echoLock))
+
+  const stdOutLock = compileOption("threads") and
+                    not defined(windows) and
+                    not defined(android) and
+                    not defined(nintendoswitch) and
+                    not defined(freertos) and
+                    not defined(zephyr) and
+                    not defined(nuttx) and
+                    hostOS != "any"
+
+  proc raiseEIO(msg: string) {.noinline, noreturn.} =
+    raise newException(IOError, msg)
+
+  proc echoBinSafe(args: openArray[string]) {.compilerproc.} =
+    when defined(androidNDK):
+      # When running nim in android app, stdout goes nowhere, so echo gets ignored
+      # To redirect echo to the android logcat, use -d:androidNDK
+      const ANDROID_LOG_VERBOSE = 2.cint
+      proc android_log_print(prio: cint, tag: cstring, fmt: cstring): cint
+        {.importc: "__android_log_print", header: "<android/log.h>", varargs, discardable.}
+      var s = ""
+      for arg in args:
+        s.add arg
+      android_log_print(ANDROID_LOG_VERBOSE, "nim", s)
+    else:
+      # flockfile deadlocks some versions of Android 5.x.x
+      when stdOutLock:
+        proc flockfile(f: CFilePtr) {.importc, nodecl.}
+        proc funlockfile(f: CFilePtr) {.importc, nodecl.}
+        flockfile(cstdout)
+      when defined(windows) and compileOption("threads"):
+        acquireSys echoLock
+      for s in args:
+        when defined(windows):
+          # equivalent to syncio.writeWindows
+          proc writeWindows(f: CFilePtr; s: string; doRaise = false) =
+            # Don't ask why but the 'printf' family of function is the only thing
+            # that writes utf-8 strings reliably on Windows. At least on my Win 10
+            # machine. We also enable `setConsoleOutputCP(65001)` now by default.
+            # But we cannot call printf directly as the string might contain \0.
+            # So we have to loop over all the sections separated by potential \0s.
+            var i = int c_fprintf(f, "%s", s)
+            while i < s.len:
+              if s[i] == '\0':
+                let w = c_fputc('\0', f)
+                if w != 0:
+                  if doRaise: raiseEIO("cannot write string to file")
+                  break
+                inc i
+              else:
+                let w = c_fprintf(f, "%s", unsafeAddr s[i])
+                if w <= 0:
+                  if doRaise: raiseEIO("cannot write string to file")
+                  break
+                inc i, w
+          writeWindows(cstdout, s)
+        else:
+          discard c_fwrite(s.cstring, cast[csize_t](s.len), 1, cstdout)
+      const linefeed = "\n"
+      discard c_fwrite(linefeed.cstring, linefeed.len, 1, cstdout)
+      discard c_fflush(cstdout)
+      when stdOutLock:
+        funlockfile(cstdout)
+      when defined(windows) and compileOption("threads"):
+        releaseSys echoLock
+
+when not defined(nimPreviewSlimSystem):
+  import std/syncio
+  export syncio
+
+when not defined(createNimHcr) and not defined(nimscript):
+  include nimhcr
+
+when notJSnotNims and not defined(nimSeqsV2):
+  proc prepareMutation*(s: var string) {.inline.} =
+    ## String literals (e.g. "abc", etc) in the ARC/ORC mode are "copy on write",
+    ## therefore you should call `prepareMutation` before modifying the strings
+    ## via `addr`.
+    runnableExamples:
+      var x = "abc"
+      var y = "defgh"
+      prepareMutation(y) # without this, you may get a `SIGBUS` or `SIGSEGV`
+      moveMem(addr y[0], addr x[0], x.len)
+      assert y == "abcgh"
+    discard
+
+proc arrayWith*[T](y: T, size: static int): array[size, T] {.raises: [].} =
+  ## Creates a new array filled with `y`.
+  for i in 0..size-1:
+    when nimvm:
+      result[i] = y
+    else:
+      # TODO: fixme it should be `=dup`
+      result[i] = y
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index 6aef4f411..3de6d8713 100644
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -11,6 +11,8 @@
 {.push profiler:off.}
 
 include osalloc
+import std/private/syslocks
+import std/sysatomics
 
 template track(op, address, size) =
   when defined(memTracker):
@@ -18,11 +20,42 @@ template track(op, address, size) =
 
 # We manage *chunks* of memory. Each chunk is a multiple of the page size.
 # Each chunk starts at an address that is divisible by the page size.
+# Small chunks may be divided into smaller cells of reusable pointers to reduce the number of page allocations.
+
+# An allocation of a small pointer looks approximately like this
+#[
+
+  alloc -> rawAlloc -> No free chunk available > Request a new page from tslf -> result = chunk.data -------------+
+              |                                                                                                   |
+              v                                                                                                   |
+    Free chunk available                                                                                          |
+              |                                                                                                   |
+              v                                                                                                   v
+      Fetch shared cells -> No free cells available -> Advance acc -> result = chunk.data + chunk.acc -------> return
+    (may not add new cells)                                                                                       ^
+              |                                                                                                   |
+              v                                                                                                   |
+     Free cells available -> result = chunk.freeList -> Advance chunk.freeList -----------------------------------+
+]#
+# so it is split into 3 paths, where the last path is preferred to prevent unnecessary allocations.
+#
+#
+# A deallocation of a small pointer then looks like this
+#[
+  dealloc -> rawDealloc -> chunk.owner == addr(a) --------------> This thread owns the chunk ------> The current chunk is active    -> Chunk is completely unused -----> Chunk references no foreign cells
+                                      |                                       |                   (Add cell into the current chunk)                 |                  Return the current chunk back to tlsf
+                                      |                                       |                                   |                                 |
+                                      v                                       v                                   v                                 v
+                      A different thread owns this chunk.     The current chunk is not active.          chunk.free was < size      Chunk references foreign cells, noop
+                      Add the cell to a.sharedFreeLists      Add the cell into the active chunk          Activate the chunk                       (end)
+                                    (end)                                    (end)                              (end)
+]#
+# So "true" deallocation is delayed for as long as possible in favor of reusing cells.
 
 const
-  InitialMemoryRequest = 128 * PageSize # 0.5 MB
+  nimMinHeapPages {.intdefine.} = 128 # 0.5 MB
   SmallChunkSize = PageSize
-  MaxFli = 30
+  MaxFli = when sizeof(int) > 2: 30 else: 14
   MaxLog2Sli = 5 # 32, this cannot be increased without changing 'uint32'
                  # everywhere!
   MaxSli = 1 shl MaxLog2Sli
@@ -30,7 +63,7 @@ const
   RealFli = MaxFli - FliOffset
 
   # size of chunks in last matrix bin
-  MaxBigChunkSize = 1 shl MaxFli - 1 shl (MaxFli-MaxLog2Sli-1)
+  MaxBigChunkSize = int(1'i32 shl MaxFli - 1'i32 shl (MaxFli-MaxLog2Sli-1))
   HugeChunkSize = MaxBigChunkSize + 1
 
 type
@@ -38,44 +71,12 @@ type
   Trunk = object
     next: PTrunk         # all nodes are connected with this pointer
     key: int             # start address at bit 0
-    bits: array[0..IntsPerTrunk-1, int] # a bit vector
+    bits: array[0..IntsPerTrunk-1, uint] # a bit vector
 
   TrunkBuckets = array[0..255, PTrunk]
   IntSet = object
     data: TrunkBuckets
 
-type
-  AlignType = BiggestFloat
-  FreeCell {.final, pure.} = object
-    next: ptr FreeCell  # next free cell in chunk (overlaid with refcount)
-    zeroField: int       # 0 means cell is not used (overlaid with typ field)
-                         # 1 means cell is manually managed pointer
-                         # otherwise a PNimType is stored in there
-
-  PChunk = ptr BaseChunk
-  PBigChunk = ptr BigChunk
-  PSmallChunk = ptr SmallChunk
-  BaseChunk {.pure, inheritable.} = object
-    prevSize: int        # size of previous chunk; for coalescing
-                         # 0th bit == 1 if 'used
-    size: int            # if < PageSize it is a small chunk
-
-  SmallChunk = object of BaseChunk
-    next, prev: PSmallChunk  # chunks of the same size
-    freeList: ptr FreeCell
-    free: int            # how many bytes remain
-    acc: int             # accumulator for small object allocation
-    when defined(cpu32):
-      align: int
-    data: AlignType      # start of usable memory
-
-  BigChunk = object of BaseChunk # not necessarily > PageSize!
-    next, prev: PBigChunk    # chunks of the same (or bigger) size
-    data: AlignType      # start of usable memory
-
-template smallChunkOverhead(): untyped = sizeof(SmallChunk)-sizeof(AlignType)
-template bigChunkOverhead(): untyped = sizeof(BigChunk)-sizeof(AlignType)
-
 # ------------- chunk table ---------------------------------------------------
 # We use a PtrSet of chunk starts and a table[Page, chunksize] for chunk
 # endings of big chunks. This is needed by the merging operation. The only
@@ -96,26 +97,103 @@ type
     key, upperBound: int
     level: int
 
+const
+  RegionHasLock = false # hasThreadSupport and defined(gcDestructors)
+
+type
+  FreeCell {.final, pure.} = object
+    # A free cell is a pointer that has been freed, meaning it became available for reuse.
+    # It may become foreign if it is lent to a chunk that did not create it, doing so reduces the amount of needed pages.
+    next: ptr FreeCell  # next free cell in chunk (overlaid with refcount)
+    when not defined(gcDestructors):
+      zeroField: int       # 0 means cell is not used (overlaid with typ field)
+                          # 1 means cell is manually managed pointer
+                          # otherwise a PNimType is stored in there
+    else:
+      alignment: int
+
+  PChunk = ptr BaseChunk
+  PBigChunk = ptr BigChunk
+  PSmallChunk = ptr SmallChunk
+  BaseChunk {.pure, inheritable.} = object
+    prevSize: int        # size of previous chunk; for coalescing
+                         # 0th bit == 1 if 'used
+    size: int            # if < PageSize it is a small chunk
+    owner: ptr MemRegion
+
+  SmallChunk = object of BaseChunk
+    next, prev: PSmallChunk  # chunks of the same size
+    freeList: ptr FreeCell   # Singly linked list of cells. They may be from foreign chunks or from the current chunk.
+                             #  Should be `nil` when the chunk isn't active in `a.freeSmallChunks`.
+    free: int32              # Bytes this chunk is able to provide using both the accumulator and free cells.
+                             # When a cell is considered foreign, its source chunk's free field is NOT adjusted until it
+                             #  reaches dealloc while the source chunk is active.
+                             # Instead, the receiving chunk gains the capacity and thus reserves space in the foreign chunk.
+    acc: uint32              # Offset from data, used when there are no free cells available but the chunk is considered free.
+    foreignCells: int        # When a free cell is given to a chunk that is not its origin,
+                             #  both the cell and the source chunk are considered foreign.
+                             # Receiving a foreign cell can happen both when deallocating from another thread or when
+                             #  the active chunk in `a.freeSmallChunks` is not the current chunk.
+                             # Freeing a chunk while `foreignCells > 0` leaks memory as all references to it become lost.
+    data {.align: MemAlign.}: UncheckedArray[byte]      # start of usable memory
+
+  BigChunk = object of BaseChunk # not necessarily > PageSize!
+    next, prev: PBigChunk    # chunks of the same (or bigger) size
+    data {.align: MemAlign.}: UncheckedArray[byte]      # start of usable memory
+
   HeapLinks = object
     len: int
     chunks: array[30, (PBigChunk, int)]
     next: ptr HeapLinks
 
   MemRegion = object
-    minLargeObj, maxLargeObj: int
-    freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk]
+    when not defined(gcDestructors):
+      minLargeObj, maxLargeObj: int
+    freeSmallChunks: array[0..max(1, SmallChunkSize div MemAlign-1), PSmallChunk]
+      # List of available chunks per size class. Only one is expected to be active per class.
+    when defined(gcDestructors):
+      sharedFreeLists: array[0..max(1, SmallChunkSize div MemAlign-1), ptr FreeCell]
+        # When a thread frees a pointer it did not create, it must not adjust the counters.
+        # Instead, the cell is placed here and deferred until the next allocation.
     flBitmap: uint32
     slBitmap: array[RealFli, uint32]
     matrix: array[RealFli, array[MaxSli, PBigChunk]]
     llmem: PLLChunk
     currMem, maxMem, freeMem, occ: int # memory sizes (allocated from OS)
     lastSize: int # needed for the case that OS gives us pages linearly
+    when RegionHasLock:
+      lock: SysLock
+    when defined(gcDestructors):
+      sharedFreeListBigChunks: PBigChunk # make no attempt at avoiding false sharing for now for this object field
+
     chunkStarts: IntSet
-    root, deleted, last, freeAvlNodes: PAvlNode
-    locked, blockChunkSizeIncrease: bool # if locked, we cannot free pages.
+    when not defined(gcDestructors):
+      root, deleted, last, freeAvlNodes: PAvlNode
+    lockActive, locked, blockChunkSizeIncrease: bool # if locked, we cannot free pages.
     nextChunkSize: int
-    bottomData: AvlNode
+    when not defined(gcDestructors):
+      bottomData: AvlNode
     heapLinks: HeapLinks
+    when defined(nimTypeNames):
+      allocCounter, deallocCounter: int
+
+template smallChunkOverhead(): untyped = sizeof(SmallChunk)
+template bigChunkOverhead(): untyped = sizeof(BigChunk)
+
+when hasThreadSupport:
+  template loada(x: untyped): untyped = atomicLoadN(unsafeAddr x, ATOMIC_RELAXED)
+  template storea(x, y: untyped) = atomicStoreN(unsafeAddr x, y, ATOMIC_RELAXED)
+
+  when false:
+    # not yet required
+    template atomicStatDec(x, diff: untyped) = discard atomicSubFetch(unsafeAddr x, diff, ATOMIC_RELAXED)
+    template atomicStatInc(x, diff: untyped) = discard atomicAddFetch(unsafeAddr x, diff, ATOMIC_RELAXED)
+else:
+  template loada(x: untyped): untyped = x
+  template storea(x, y: untyped) = x = y
+
+template atomicStatDec(x, diff: untyped) = dec x, diff
+template atomicStatInc(x, diff: untyped) = inc x, diff
 
 const
   fsLookupTable: array[byte, int8] = [
@@ -135,10 +213,10 @@ const
   ]
 
 proc msbit(x: uint32): int {.inline.} =
-  let a = if x <= 0xff_ff:
+  let a = if x <= 0xff_ff'u32:
             (if x <= 0xff: 0 else: 8)
           else:
-            (if x <= 0xff_ff_ff: 16 else: 24)
+            (if x <= 0xff_ff_ff'u32: 16 else: 24)
   result = int(fsLookupTable[byte(x shr a)]) + a
 
 proc lsbit(x: uint32): int {.inline.} =
@@ -157,7 +235,7 @@ proc mappingSearch(r, fl, sl: var int) {.inline.} =
   let t = roundup((1 shl (msbit(uint32 r) - MaxLog2Sli)), PageSize) - 1
   r = r + t
   r = r and not t
-  r = min(r, MaxBigChunkSize)
+  r = min(r, MaxBigChunkSize).int
   fl = msbit(uint32 r)
   sl = (r shr (fl - MaxLog2Sli)) - MaxSli
   dec fl, FliOffset
@@ -222,16 +300,12 @@ proc addChunkToMatrix(a: var MemRegion; b: PBigChunk) =
   setBit(sl, a.slBitmap[fl])
   setBit(fl, a.flBitmap)
 
-{.push stack_trace: off.}
-proc initAllocator() = discard "nothing to do anymore"
-{.pop.}
-
 proc incCurrMem(a: var MemRegion, bytes: int) {.inline.} =
-  inc(a.currMem, bytes)
+  atomicStatInc(a.currMem, bytes)
 
 proc decCurrMem(a: var MemRegion, bytes: int) {.inline.} =
   a.maxMem = max(a.maxMem, a.currMem)
-  dec(a.currMem, bytes)
+  atomicStatDec(a.currMem, bytes)
 
 proc getMaxMem(a: var MemRegion): int =
   # Since we update maxPagesCount only when freeing pages,
@@ -239,6 +313,20 @@ proc getMaxMem(a: var MemRegion): int =
   # maximum of these both values here:
   result = max(a.currMem, a.maxMem)
 
+const nimMaxHeap {.intdefine.} = 0
+
+proc allocPages(a: var MemRegion, size: int): pointer =
+  when nimMaxHeap != 0:
+    if a.occ + size > nimMaxHeap * 1024 * 1024:
+      raiseOutOfMem()
+  osAllocPages(size)
+
+proc tryAllocPages(a: var MemRegion, size: int): pointer =
+  when nimMaxHeap != 0:
+    if a.occ + size > nimMaxHeap * 1024 * 1024:
+      raiseOutOfMem()
+  osTryAllocPages(size)
+
 proc llAlloc(a: var MemRegion, size: int): pointer =
   # *low-level* alloc for the memory managers data structures. Deallocation
   # is done at the end of the allocator's life time.
@@ -248,49 +336,50 @@ proc llAlloc(a: var MemRegion, size: int): pointer =
     # is one page:
     sysAssert roundup(size+sizeof(LLChunk), PageSize) == PageSize, "roundup 6"
     var old = a.llmem # can be nil and is correct with nil
-    a.llmem = cast[PLLChunk](osAllocPages(PageSize))
-    when defined(avlcorruption):
+    a.llmem = cast[PLLChunk](allocPages(a, PageSize))
+    when defined(nimAvlcorruption):
       trackLocation(a.llmem, PageSize)
     incCurrMem(a, PageSize)
     a.llmem.size = PageSize - sizeof(LLChunk)
     a.llmem.acc = sizeof(LLChunk)
     a.llmem.next = old
-  result = cast[pointer](cast[ByteAddress](a.llmem) + a.llmem.acc)
+  result = cast[pointer](cast[int](a.llmem) + a.llmem.acc)
   dec(a.llmem.size, size)
   inc(a.llmem.acc, size)
   zeroMem(result, size)
 
-proc getBottom(a: var MemRegion): PAvlNode =
-  result = addr(a.bottomData)
-  if result.link[0] == nil:
-    result.link[0] = result
-    result.link[1] = result
-
-proc allocAvlNode(a: var MemRegion, key, upperBound: int): PAvlNode =
-  if a.freeAvlNodes != nil:
-    result = a.freeAvlNodes
-    a.freeAvlNodes = a.freeAvlNodes.link[0]
-  else:
-    result = cast[PAvlNode](llAlloc(a, sizeof(AvlNode)))
-    when defined(avlcorruption):
-      cprintf("tracking location: %p\n", result)
-  result.key = key
-  result.upperBound = upperBound
-  let bottom = getBottom(a)
-  result.link[0] = bottom
-  result.link[1] = bottom
-  result.level = 1
-  #when defined(avlcorruption):
-  #  track("allocAvlNode", result, sizeof(AvlNode))
-  sysAssert(bottom == addr(a.bottomData), "bottom data")
-  sysAssert(bottom.link[0] == bottom, "bottom link[0]")
-  sysAssert(bottom.link[1] == bottom, "bottom link[1]")
-
-proc deallocAvlNode(a: var MemRegion, n: PAvlNode) {.inline.} =
-  n.link[0] = a.freeAvlNodes
-  a.freeAvlNodes = n
-
-proc addHeapLink(a: var MemRegion; p: PBigChunk, size: int) =
+when not defined(gcDestructors):
+  proc getBottom(a: var MemRegion): PAvlNode =
+    result = addr(a.bottomData)
+    if result.link[0] == nil:
+      result.link[0] = result
+      result.link[1] = result
+
+  proc allocAvlNode(a: var MemRegion, key, upperBound: int): PAvlNode =
+    if a.freeAvlNodes != nil:
+      result = a.freeAvlNodes
+      a.freeAvlNodes = a.freeAvlNodes.link[0]
+    else:
+      result = cast[PAvlNode](llAlloc(a, sizeof(AvlNode)))
+      when defined(nimAvlcorruption):
+        cprintf("tracking location: %p\n", result)
+    result.key = key
+    result.upperBound = upperBound
+    let bottom = getBottom(a)
+    result.link[0] = bottom
+    result.link[1] = bottom
+    result.level = 1
+    #when defined(nimAvlcorruption):
+    #  track("allocAvlNode", result, sizeof(AvlNode))
+    sysAssert(bottom == addr(a.bottomData), "bottom data")
+    sysAssert(bottom.link[0] == bottom, "bottom link[0]")
+    sysAssert(bottom.link[1] == bottom, "bottom link[1]")
+
+  proc deallocAvlNode(a: var MemRegion, n: PAvlNode) {.inline.} =
+    n.link[0] = a.freeAvlNodes
+    a.freeAvlNodes = n
+
+proc addHeapLink(a: var MemRegion; p: PBigChunk, size: int): ptr HeapLinks =
   var it = addr(a.heapLinks)
   while it != nil and it.len >= it.chunks.len: it = it.next
   if it == nil:
@@ -299,12 +388,15 @@ proc addHeapLink(a: var MemRegion; p: PBigChunk, size: int) =
     a.heapLinks.next = n
     n.chunks[0] = (p, size)
     n.len = 1
+    result = n
   else:
     let L = it.len
     it.chunks[L] = (p, size)
     inc it.len
+    result = it
 
-include "system/avltree"
+when not defined(gcDestructors):
+  include "system/avltree"
 
 proc llDeallocAll(a: var MemRegion) =
   var it = a.llmem
@@ -334,21 +426,21 @@ proc contains(s: IntSet, key: int): bool =
   var t = intSetGet(s, key shr TrunkShift)
   if t != nil:
     var u = key and TrunkMask
-    result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0
+    result = (t.bits[u shr IntShift] and (uint(1) shl (u and IntMask))) != 0
   else:
     result = false
 
 proc incl(a: var MemRegion, s: var IntSet, key: int) =
   var t = intSetPut(a, s, key shr TrunkShift)
   var u = key and TrunkMask
-  t.bits[u shr IntShift] = t.bits[u shr IntShift] or (1 shl (u and IntMask))
+  t.bits[u shr IntShift] = t.bits[u shr IntShift] or (uint(1) shl (u and IntMask))
 
 proc excl(s: var IntSet, key: int) =
   var t = intSetGet(s, key shr TrunkShift)
   if t != nil:
     var u = key and TrunkMask
     t.bits[u shr IntShift] = t.bits[u shr IntShift] and not
-        (1 shl (u and IntMask))
+        (uint(1) shl (u and IntMask))
 
 iterator elements(t: IntSet): int {.inline.} =
   # while traversing it is forbidden to change the set!
@@ -369,7 +461,7 @@ iterator elements(t: IntSet): int {.inline.} =
       r = r.next
 
 proc isSmallChunk(c: PChunk): bool {.inline.} =
-  return c.size <= SmallChunkSize-smallChunkOverhead()
+  result = c.size <= SmallChunkSize-smallChunkOverhead()
 
 proc chunkUnused(c: PChunk): bool {.inline.} =
   result = (c.prevSize and 1) == 0
@@ -385,8 +477,8 @@ iterator allObjects(m: var MemRegion): pointer {.inline.} =
           var c = cast[PSmallChunk](c)
 
           let size = c.size
-          var a = cast[ByteAddress](addr(c.data))
-          let limit = a + c.acc
+          var a = cast[int](addr(c.data))
+          let limit = a + c.acc.int
           while a <% limit:
             yield cast[pointer](a)
             a = a +% size
@@ -398,18 +490,19 @@ iterator allObjects(m: var MemRegion): pointer {.inline.} =
 proc iterToProc*(iter: typed, envType: typedesc; procName: untyped) {.
                       magic: "Plugin", compileTime.}
 
-proc isCell(p: pointer): bool {.inline.} =
-  result = cast[ptr FreeCell](p).zeroField >% 1
+when not defined(gcDestructors):
+  proc isCell(p: pointer): bool {.inline.} =
+    result = cast[ptr FreeCell](p).zeroField >% 1
 
 # ------------- chunk management ----------------------------------------------
 proc pageIndex(c: PChunk): int {.inline.} =
-  result = cast[ByteAddress](c) shr PageShift
+  result = cast[int](c) shr PageShift
 
 proc pageIndex(p: pointer): int {.inline.} =
-  result = cast[ByteAddress](p) shr PageShift
+  result = cast[int](p) shr PageShift
 
 proc pageAddr(p: pointer): PChunk {.inline.} =
-  result = cast[PChunk](cast[ByteAddress](p) and not PageMask)
+  result = cast[PChunk](cast[int](p) and not PageMask)
   #sysAssert(Contains(allocator.chunkStarts, pageIndex(result)))
 
 when false:
@@ -421,48 +514,44 @@ when false:
                 it, it.next, it.prev, it.size)
       it = it.next
 
-const nimMaxHeap {.intdefine.} = 0
-
 proc requestOsChunks(a: var MemRegion, size: int): PBigChunk =
   when not defined(emscripten):
     if not a.blockChunkSizeIncrease:
       let usedMem = a.occ #a.currMem # - a.freeMem
-      when nimMaxHeap != 0:
-        if usedMem > nimMaxHeap * 1024 * 1024:
-          raiseOutOfMem()
       if usedMem < 64 * 1024:
         a.nextChunkSize = PageSize*4
       else:
         a.nextChunkSize = min(roundup(usedMem shr 2, PageSize), a.nextChunkSize * 2)
-  var size = size
+        a.nextChunkSize = min(a.nextChunkSize, MaxBigChunkSize).int
 
+  var size = size
   if size > a.nextChunkSize:
-    result = cast[PBigChunk](osAllocPages(size))
+    result = cast[PBigChunk](allocPages(a, size))
   else:
-    result = cast[PBigChunk](osTryAllocPages(a.nextChunkSize))
+    result = cast[PBigChunk](tryAllocPages(a, a.nextChunkSize))
     if result == nil:
-      result = cast[PBigChunk](osAllocPages(size))
+      result = cast[PBigChunk](allocPages(a, size))
       a.blockChunkSizeIncrease = true
     else:
       size = a.nextChunkSize
 
   incCurrMem(a, size)
   inc(a.freeMem, size)
-  a.addHeapLink(result, size)
+  let heapLink = a.addHeapLink(result, size)
   when defined(debugHeapLinks):
     cprintf("owner: %p; result: %p; next pointer %p; size: %ld\n", addr(a),
-      result, result.heapLink, result.origSize)
+      result, heapLink, size)
 
   when defined(memtracker):
-    trackLocation(addr result.origSize, sizeof(int))
+    trackLocation(addr result.size, sizeof(int))
 
-  sysAssert((cast[ByteAddress](result) and PageMask) == 0, "requestOsChunks 1")
+  sysAssert((cast[int](result) and PageMask) == 0, "requestOsChunks 1")
   #zeroMem(result, size)
   result.next = nil
   result.prev = nil
   result.size = size
   # update next.prevSize:
-  var nxt = cast[ByteAddress](result) +% size
+  var nxt = cast[int](result) +% size
   sysAssert((nxt and PageMask) == 0, "requestOsChunks 2")
   var next = cast[PChunk](nxt)
   if pageIndex(next) in a.chunkStarts:
@@ -470,7 +559,7 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk =
     next.prevSize = size or (next.prevSize and 1)
   # set result.prevSize:
   var lastSize = if a.lastSize != 0: a.lastSize else: PageSize
-  var prv = cast[ByteAddress](result) -% lastSize
+  var prv = cast[int](result) -% lastSize
   sysAssert((nxt and PageMask) == 0, "requestOsChunks 3")
   var prev = cast[PChunk](prv)
   if pageIndex(prev) in a.chunkStarts and prev.size == lastSize:
@@ -516,21 +605,22 @@ proc listRemove[T](head: var T, c: T) {.inline.} =
 
 proc updatePrevSize(a: var MemRegion, c: PBigChunk,
                     prevSize: int) {.inline.} =
-  var ri = cast[PChunk](cast[ByteAddress](c) +% c.size)
-  sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "updatePrevSize")
+  var ri = cast[PChunk](cast[int](c) +% c.size)
+  sysAssert((cast[int](ri) and PageMask) == 0, "updatePrevSize")
   if isAccessible(a, ri):
     ri.prevSize = prevSize or (ri.prevSize and 1)
 
 proc splitChunk2(a: var MemRegion, c: PBigChunk, size: int): PBigChunk =
-  result = cast[PBigChunk](cast[ByteAddress](c) +% size)
+  result = cast[PBigChunk](cast[int](c) +% size)
   result.size = c.size - size
-  track("result.origSize", addr result.origSize, sizeof(int))
-  # XXX check if these two nil assignments are dead code given
-  # addChunkToMatrix's implementation:
-  result.next = nil
-  result.prev = nil
+  track("result.size", addr result.size, sizeof(int))
+  when not defined(nimOptimizedSplitChunk):
+    # still active because of weird codegen issue on some of our CIs:
+    result.next = nil
+    result.prev = nil
   # size and not used:
   result.prevSize = size
+  result.owner = addr a
   sysAssert((size and 1) == 0, "splitChunk 2")
   sysAssert((size and PageMask) == 0,
       "splitChunk: size is not a multiple of the PageSize")
@@ -550,8 +640,8 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) =
   when coalescLeft:
     let prevSize = c.prevSize
     if prevSize != 0:
-      var le = cast[PChunk](cast[ByteAddress](c) -% prevSize)
-      sysAssert((cast[ByteAddress](le) and PageMask) == 0, "freeBigChunk 4")
+      var le = cast[PChunk](cast[int](c) -% prevSize)
+      sysAssert((cast[int](le) and PageMask) == 0, "freeBigChunk 4")
       if isAccessible(a, le) and chunkUnused(le):
         sysAssert(not isSmallChunk(le), "freeBigChunk 5")
         if not isSmallChunk(le) and le.size < MaxBigChunkSize:
@@ -561,11 +651,14 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) =
           c = cast[PBigChunk](le)
           if c.size > MaxBigChunkSize:
             let rest = splitChunk2(a, c, MaxBigChunkSize)
+            when defined(nimOptimizedSplitChunk):
+              rest.next = nil
+              rest.prev = nil
             addChunkToMatrix(a, c)
             c = rest
   when coalescRight:
-    var ri = cast[PChunk](cast[ByteAddress](c) +% c.size)
-    sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "freeBigChunk 2")
+    var ri = cast[PChunk](cast[int](c) +% c.size)
+    sysAssert((cast[int](ri) and PageMask) == 0, "freeBigChunk 2")
     if isAccessible(a, ri) and chunkUnused(ri):
       sysAssert(not isSmallChunk(ri), "freeBigChunk 3")
       if not isSmallChunk(ri) and c.size < MaxBigChunkSize:
@@ -580,42 +673,62 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) =
 proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
   sysAssert(size > 0, "getBigChunk 2")
   var size = size # roundup(size, PageSize)
-  var fl, sl: int
+  var fl = 0
+  var sl = 0
   mappingSearch(size, fl, sl)
   sysAssert((size and PageMask) == 0, "getBigChunk: unaligned chunk")
   result = findSuitableBlock(a, fl, sl)
+
+  when RegionHasLock:
+    if not a.lockActive:
+      a.lockActive = true
+      initSysLock(a.lock)
+    acquireSys a.lock
+
   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)
       # if we over allocated split the chunk:
       if result.size > size:
         splitChunk(a, result, size)
+    result.owner = addr a
   else:
     removeChunkFromMatrix2(a, result, fl, sl)
     if result.size >= size + PageSize:
       splitChunk(a, result, size)
   # set 'used' to to true:
   result.prevSize = 1
-  track("setUsedToFalse", addr result.origSize, sizeof(int))
+  track("setUsedToFalse", addr result.size, sizeof(int))
+  sysAssert result.owner == addr a, "getBigChunk: No owner set!"
 
   incl(a, a.chunkStarts, pageIndex(result))
   dec(a.freeMem, size)
+  when RegionHasLock:
+    releaseSys a.lock
 
 proc getHugeChunk(a: var MemRegion; size: int): PBigChunk =
-  result = cast[PBigChunk](osAllocPages(size))
+  result = cast[PBigChunk](allocPages(a, size))
+  when RegionHasLock:
+    if not a.lockActive:
+      a.lockActive = true
+      initSysLock(a.lock)
+    acquireSys a.lock
   incCurrMem(a, size)
   # XXX add this to the heap links. But also remove it from it later.
   when false: a.addHeapLink(result, size)
-  sysAssert((cast[ByteAddress](result) and PageMask) == 0, "getHugeChunk")
+  sysAssert((cast[int](result) and PageMask) == 0, "getHugeChunk")
   result.next = nil
   result.prev = nil
   result.size = size
   # set 'used' to to true:
   result.prevSize = 1
+  result.owner = addr a
   incl(a, a.chunkStarts, pageIndex(result))
+  when RegionHasLock:
+    releaseSys a.lock
 
 proc freeHugeChunk(a: var MemRegion; c: PBigChunk) =
   let size = c.size
@@ -631,7 +744,8 @@ proc getSmallChunk(a: var MemRegion): PSmallChunk =
   result = cast[PSmallChunk](res)
 
 # -----------------------------------------------------------------------------
-proc isAllocatedPtr(a: MemRegion, p: pointer): bool {.benign.}
+when not defined(gcDestructors):
+  proc isAllocatedPtr(a: MemRegion, p: pointer): bool {.benign.}
 
 when true:
   template allocInv(a: MemRegion): bool = true
@@ -678,116 +792,180 @@ else:
   template trackSize(x) = discard
   template untrackSize(x) = discard
 
-when false:
-  # not yet used by the GCs
-  proc rawTryAlloc(a: var MemRegion; requestedSize: int): pointer =
-    sysAssert(allocInv(a), "rawAlloc: begin")
-    sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken")
-    sysAssert(requestedSize >= sizeof(FreeCell), "rawAlloc: requested size too small")
-    var size = roundup(requestedSize, MemAlign)
-    inc a.occ, size
-    trackSize(size)
-    sysAssert(size >= requestedSize, "insufficient allocated size!")
-    #c_fprintf(stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
-    if size <= SmallChunkSize-smallChunkOverhead():
-      # allocate a small block: for small chunks, we use only its next pointer
-      var s = size div MemAlign
-      var c = a.freeSmallChunks[s]
-      if c == nil:
-        result = nil
-      else:
-        sysAssert c.size == size, "rawAlloc 6"
-        if c.freeList == nil:
-          sysAssert(c.acc + smallChunkOverhead() + size <= SmallChunkSize,
-                    "rawAlloc 7")
-          result = cast[pointer](cast[ByteAddress](addr(c.data)) +% c.acc)
-          inc(c.acc, size)
-        else:
-          result = c.freeList
-          sysAssert(c.freeList.zeroField == 0, "rawAlloc 8")
-          c.freeList = c.freeList.next
-        dec(c.free, size)
-        sysAssert((cast[ByteAddress](result) and (MemAlign-1)) == 0, "rawAlloc 9")
-        if c.free < size:
-          listRemove(a.freeSmallChunks[s], c)
-          sysAssert(allocInv(a), "rawAlloc: end listRemove test")
-        sysAssert(((cast[ByteAddress](result) and PageMask) - smallChunkOverhead()) %%
-                  size == 0, "rawAlloc 21")
-        sysAssert(allocInv(a), "rawAlloc: end small size")
+proc deallocBigChunk(a: var MemRegion, c: PBigChunk) =
+  when RegionHasLock:
+    acquireSys a.lock
+  dec a.occ, c.size
+  untrackSize(c.size)
+  sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case B)"
+  when not defined(gcDestructors):
+    a.deleted = getBottom(a)
+    del(a, a.root, cast[int](addr(c.data)))
+  if c.size >= HugeChunkSize: freeHugeChunk(a, c)
+  else: freeBigChunk(a, c)
+  when RegionHasLock:
+    releaseSys a.lock
+
+when defined(gcDestructors):
+  template atomicPrepend(head, elem: untyped) =
+    # see also https://en.cppreference.com/w/cpp/atomic/atomic_compare_exchange
+    when hasThreadSupport:
+      while true:
+        elem.next.storea head.loada
+        if atomicCompareExchangeN(addr head, addr elem.next, elem, weak = true, ATOMIC_RELEASE, ATOMIC_RELAXED):
+          break
     else:
-      inc size, bigChunkOverhead()
-      var fl, sl: int
-      mappingSearch(size, fl, sl)
-      sysAssert((size and PageMask) == 0, "getBigChunk: unaligned chunk")
-      let c = findSuitableBlock(a, fl, sl)
-      if c != nil:
-        removeChunkFromMatrix2(a, c, fl, sl)
-        if c.size >= size + PageSize:
-          splitChunk(a, c, size)
-        # set 'used' to to true:
-        c.prevSize = 1
-        incl(a, a.chunkStarts, pageIndex(c))
-        dec(a.freeMem, size)
-        result = addr(c.data)
-        sysAssert((cast[ByteAddress](c) and (MemAlign-1)) == 0, "rawAlloc 13")
-        sysAssert((cast[ByteAddress](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary")
-        if a.root == nil: a.root = getBottom(a)
-        add(a, a.root, cast[ByteAddress](result), cast[ByteAddress](result)+%size)
-      else:
-        result = nil
+      elem.next.storea head.loada
+      head.storea elem
+
+  proc addToSharedFreeListBigChunks(a: var MemRegion; c: PBigChunk) {.inline.} =
+    sysAssert c.next == nil, "c.next pointer must be nil"
+    atomicPrepend a.sharedFreeListBigChunks, c
+
+  proc addToSharedFreeList(c: PSmallChunk; f: ptr FreeCell; size: int) {.inline.} =
+    atomicPrepend c.owner.sharedFreeLists[size], f
+
+  const MaxSteps = 20
+
+  proc compensateCounters(a: var MemRegion; c: PSmallChunk; size: int) =
+    # rawDealloc did NOT do the usual:
+    # `inc(c.free, size); dec(a.occ, size)` because it wasn't the owner of these
+    # memory locations. We have to compensate here for these for the entire list.
+    var it = c.freeList
+    var total = 0
+    while it != nil:
+      inc total, size
+      let chunk = cast[PSmallChunk](pageAddr(it))
+      if c != chunk:
+        # The cell is foreign, potentially even from a foreign thread.
+        # It must block the current chunk from being freed, as doing so would leak memory.
+        inc c.foreignCells
+      it = it.next
+    # By not adjusting the foreign chunk we reserve space in it to prevent deallocation
+    inc(c.free, total)
+    dec(a.occ, total)
+
+  proc freeDeferredObjects(a: var MemRegion; root: PBigChunk) =
+    var it = root
+    var maxIters = MaxSteps # make it time-bounded
+    while true:
+      let rest = it.next.loada
+      it.next.storea nil
+      deallocBigChunk(a, cast[PBigChunk](it))
+      if maxIters == 0:
+        if rest != nil:
+          addToSharedFreeListBigChunks(a, rest)
+          sysAssert a.sharedFreeListBigChunks != nil, "re-enqueing failed"
+        break
+      it = rest
+      dec maxIters
+      if it == nil: break
 
 proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
+  when defined(nimTypeNames):
+    inc(a.allocCounter)
   sysAssert(allocInv(a), "rawAlloc: begin")
   sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken")
-  sysAssert(requestedSize >= sizeof(FreeCell), "rawAlloc: requested size too small")
   var size = roundup(requestedSize, MemAlign)
+  sysAssert(size >= sizeof(FreeCell), "rawAlloc: requested size too small")
   sysAssert(size >= requestedSize, "insufficient allocated size!")
   #c_fprintf(stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
+
   if size <= SmallChunkSize-smallChunkOverhead():
+    template fetchSharedCells(tc: PSmallChunk) =
+      # Consumes cells from (potentially) foreign threads from `a.sharedFreeLists[s]`
+      when defined(gcDestructors):
+        if tc.freeList == nil:
+          when hasThreadSupport:
+            # Steal the entire list from `sharedFreeList`:
+            tc.freeList = atomicExchangeN(addr a.sharedFreeLists[s], nil, ATOMIC_RELAXED)
+          else:
+            tc.freeList = a.sharedFreeLists[s]
+            a.sharedFreeLists[s] = nil
+          # if `tc.freeList` isn't nil, `tc` will gain capacity.
+          # We must calculate how much it gained and how many foreign cells are included.
+          compensateCounters(a, tc, size)
+
     # allocate a small block: for small chunks, we use only its next pointer
-    var s = size div MemAlign
+    let s = size div MemAlign
     var c = a.freeSmallChunks[s]
     if c == nil:
+      # There is no free chunk of the requested size available, we need a new one.
       c = getSmallChunk(a)
+      # init all fields in case memory didn't get zeroed
       c.freeList = nil
+      c.foreignCells = 0
       sysAssert c.size == PageSize, "rawAlloc 3"
       c.size = size
-      c.acc = size
-      c.free = SmallChunkSize - smallChunkOverhead() - size
+      c.acc = size.uint32
+      c.free = SmallChunkSize - smallChunkOverhead() - size.int32
+      sysAssert c.owner == addr(a), "rawAlloc: No owner set!"
       c.next = nil
       c.prev = nil
-      listAdd(a.freeSmallChunks[s], c)
+      # Shared cells are fetched here in case `c.size * 2 >= SmallChunkSize - smallChunkOverhead()`.
+      # For those single cell chunks, we would otherwise have to allocate a new one almost every time.
+      fetchSharedCells(c)
+      if c.free >= size:
+        # Because removals from `a.freeSmallChunks[s]` only happen in the other alloc branch and during dealloc,
+        #  we must not add it to the list if it cannot be used the next time a pointer of `size` bytes is needed.
+        listAdd(a.freeSmallChunks[s], c)
       result = addr(c.data)
-      sysAssert((cast[ByteAddress](result) and (MemAlign-1)) == 0, "rawAlloc 4")
+      sysAssert((cast[int](result) and (MemAlign-1)) == 0, "rawAlloc 4")
     else:
+      # There is a free chunk of the requested size available, use it.
       sysAssert(allocInv(a), "rawAlloc: begin c != nil")
       sysAssert c.next != c, "rawAlloc 5"
       #if c.size != size:
       #  c_fprintf(stdout, "csize: %lld; size %lld\n", c.size, size)
       sysAssert c.size == size, "rawAlloc 6"
       if c.freeList == nil:
-        sysAssert(c.acc + smallChunkOverhead() + size <= SmallChunkSize,
+        sysAssert(c.acc.int + smallChunkOverhead() + size <= SmallChunkSize,
                   "rawAlloc 7")
-        result = cast[pointer](cast[ByteAddress](addr(c.data)) +% c.acc)
+        result = cast[pointer](cast[int](addr(c.data)) +% c.acc.int)
         inc(c.acc, size)
       else:
+        # There are free cells available, prefer them over the accumulator
         result = c.freeList
-        sysAssert(c.freeList.zeroField == 0, "rawAlloc 8")
+        when not defined(gcDestructors):
+          sysAssert(c.freeList.zeroField == 0, "rawAlloc 8")
         c.freeList = c.freeList.next
+        if cast[PSmallChunk](pageAddr(result)) != c:
+          # This cell isn't a blocker for the current chunk's deallocation anymore
+          dec(c.foreignCells)
+        else:
+          sysAssert(c == cast[PSmallChunk](pageAddr(result)), "rawAlloc: Bad cell")
+      # Even if the cell we return is foreign, the local chunk's capacity decreases.
+      # The capacity was previously reserved in the source chunk (when it first got allocated),
+      #  then added into the current chunk during dealloc,
+      #  so the source chunk will not be freed or leak memory because of this.
       dec(c.free, size)
-      sysAssert((cast[ByteAddress](result) and (MemAlign-1)) == 0, "rawAlloc 9")
+      sysAssert((cast[int](result) and (MemAlign-1)) == 0, "rawAlloc 9")
       sysAssert(allocInv(a), "rawAlloc: end c != nil")
-    sysAssert(allocInv(a), "rawAlloc: before c.free < size")
-    if c.free < size:
-      sysAssert(allocInv(a), "rawAlloc: before listRemove test")
-      listRemove(a.freeSmallChunks[s], c)
-      sysAssert(allocInv(a), "rawAlloc: end listRemove test")
-    sysAssert(((cast[ByteAddress](result) and PageMask) - smallChunkOverhead()) %%
+      # We fetch deferred cells *after* advancing `c.freeList`/`acc` to adjust `c.free`.
+      # If after the adjustment it turns out there's free cells available,
+      #  the chunk stays in `a.freeSmallChunks[s]` and the need for a new chunk is delayed.
+      fetchSharedCells(c)
+      sysAssert(allocInv(a), "rawAlloc: before c.free < size")
+      if c.free < size:
+        # Even after fetching shared cells the chunk has no usable memory left. It is no longer the active chunk
+        sysAssert(allocInv(a), "rawAlloc: before listRemove test")
+        listRemove(a.freeSmallChunks[s], c)
+        sysAssert(allocInv(a), "rawAlloc: end listRemove test")
+    sysAssert(((cast[int](result) and PageMask) - smallChunkOverhead()) %%
                size == 0, "rawAlloc 21")
     sysAssert(allocInv(a), "rawAlloc: end small size")
     inc a.occ, size
     trackSize(c.size)
   else:
+    when defined(gcDestructors):
+      when hasThreadSupport:
+        let deferredFrees = atomicExchangeN(addr a.sharedFreeListBigChunks, nil, ATOMIC_RELAXED)
+      else:
+        let deferredFrees = a.sharedFreeListBigChunks
+        a.sharedFreeListBigChunks = nil
+      if deferredFrees != nil:
+        freeDeferredObjects(a, deferredFrees)
+
     size = requestedSize + bigChunkOverhead() #  roundup(requestedSize+bigChunkOverhead(), PageSize)
     # allocate a large block
     var c = if size >= HugeChunkSize: getHugeChunk(a, size)
@@ -795,163 +973,210 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
     sysAssert c.prev == nil, "rawAlloc 10"
     sysAssert c.next == nil, "rawAlloc 11"
     result = addr(c.data)
-    sysAssert((cast[ByteAddress](c) and (MemAlign-1)) == 0, "rawAlloc 13")
-    sysAssert((cast[ByteAddress](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary")
-    if a.root == nil: a.root = getBottom(a)
-    add(a, a.root, cast[ByteAddress](result), cast[ByteAddress](result)+%size)
+    sysAssert((cast[int](c) and (MemAlign-1)) == 0, "rawAlloc 13")
+    sysAssert((cast[int](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary")
+    when not defined(gcDestructors):
+      if a.root == nil: a.root = getBottom(a)
+      add(a, a.root, cast[int](result), cast[int](result)+%size)
     inc a.occ, c.size
     trackSize(c.size)
   sysAssert(isAccessible(a, result), "rawAlloc 14")
   sysAssert(allocInv(a), "rawAlloc: end")
-  when logAlloc: cprintf("var pointer_%p = alloc(%ld)\n", result, requestedSize)
+  when logAlloc: cprintf("var pointer_%p = alloc(%ld) # %p\n", result, requestedSize, addr a)
 
 proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer =
   result = rawAlloc(a, requestedSize)
   zeroMem(result, requestedSize)
 
 proc rawDealloc(a: var MemRegion, p: pointer) =
+  when defined(nimTypeNames):
+    inc(a.deallocCounter)
   #sysAssert(isAllocatedPtr(a, p), "rawDealloc: no allocated pointer")
   sysAssert(allocInv(a), "rawDealloc: begin")
   var c = pageAddr(p)
+  sysAssert(c != nil, "rawDealloc: begin")
   if isSmallChunk(c):
     # `p` is within a small chunk:
     var c = cast[PSmallChunk](c)
-    var s = c.size
-    dec a.occ, s
-    untrackSize(s)
-    sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case A)"
-    sysAssert(((cast[ByteAddress](p) and PageMask) - smallChunkOverhead()) %%
-               s == 0, "rawDealloc 3")
+    let s = c.size
+    #       ^ We might access thread foreign storage here.
+    # The other thread cannot possibly free this block as it's still alive.
     var f = cast[ptr FreeCell](p)
-    #echo("setting to nil: ", $cast[ByteAddress](addr(f.zeroField)))
-    sysAssert(f.zeroField != 0, "rawDealloc 1")
-    f.zeroField = 0
-    f.next = c.freeList
-    c.freeList = f
-    when overwriteFree:
-      # set to 0xff to check for usage after free bugs:
-      c_memset(cast[pointer](cast[int](p) +% sizeof(FreeCell)), -1'i32,
-               s -% sizeof(FreeCell))
-    # check if it is not in the freeSmallChunks[s] list:
-    if c.free < s:
-      # add it to the freeSmallChunks[s] array:
-      listAdd(a.freeSmallChunks[s div MemAlign], c)
-      inc(c.free, s)
+    if c.owner == addr(a):
+      # We own the block, there is no foreign thread involved.
+      dec a.occ, s
+      untrackSize(s)
+      sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case A)"
+      sysAssert(((cast[int](p) and PageMask) - smallChunkOverhead()) %%
+                s == 0, "rawDealloc 3")
+      when not defined(gcDestructors):
+        #echo("setting to nil: ", $cast[int](addr(f.zeroField)))
+        sysAssert(f.zeroField != 0, "rawDealloc 1")
+        f.zeroField = 0
+      when overwriteFree:
+        # set to 0xff to check for usage after free bugs:
+        nimSetMem(cast[pointer](cast[int](p) +% sizeof(FreeCell)), -1'i32,
+                s -% sizeof(FreeCell))
+      let activeChunk = a.freeSmallChunks[s div MemAlign]
+      if activeChunk != nil and c != activeChunk:
+        # This pointer is not part of the active chunk, lend it out
+        #  and do not adjust the current chunk (same logic as compensateCounters.)
+        # Put the cell into the active chunk,
+        #  may prevent a queue of available chunks from forming in a.freeSmallChunks[s div MemAlign].
+        #  This queue would otherwise waste memory in the form of free cells until we return to those chunks.
+        f.next = activeChunk.freeList
+        activeChunk.freeList = f # lend the cell
+        inc(activeChunk.free, s) # By not adjusting the current chunk's capacity it is prevented from being freed
+        inc(activeChunk.foreignCells) # The cell is now considered foreign from the perspective of the active chunk
+      else:
+        f.next = c.freeList
+        c.freeList = f
+        if c.free < s:
+          # The chunk could not have been active as it didn't have enough space to give
+          listAdd(a.freeSmallChunks[s div MemAlign], c)
+          inc(c.free, s)
+        else:
+          inc(c.free, s)
+          # Free only if the entire chunk is unused and there are no borrowed cells.
+          # If the chunk were to be freed while it references foreign cells,
+          #  the foreign chunks will leak memory and can never be freed.
+          if c.free == SmallChunkSize-smallChunkOverhead() and c.foreignCells == 0:
+            listRemove(a.freeSmallChunks[s div MemAlign], c)
+            c.size = SmallChunkSize
+            freeBigChunk(a, cast[PBigChunk](c))
     else:
-      inc(c.free, s)
-      if c.free == SmallChunkSize-smallChunkOverhead():
-        listRemove(a.freeSmallChunks[s div MemAlign], c)
-        c.size = SmallChunkSize
-        freeBigChunk(a, cast[PBigChunk](c))
-    sysAssert(((cast[ByteAddress](p) and PageMask) - smallChunkOverhead()) %%
+      when logAlloc: cprintf("dealloc(pointer_%p) # SMALL FROM %p CALLER %p\n", p, c.owner, addr(a))
+
+      when defined(gcDestructors):
+        addToSharedFreeList(c, f, s div MemAlign)
+    sysAssert(((cast[int](p) and PageMask) - smallChunkOverhead()) %%
                s == 0, "rawDealloc 2")
   else:
     # set to 0xff to check for usage after free bugs:
-    when overwriteFree: c_memset(p, -1'i32, c.size -% bigChunkOverhead())
-    # free big chunk
-    var c = cast[PBigChunk](c)
-    dec a.occ, c.size
-    untrackSize(c.size)
-    sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case B)"
-    a.deleted = getBottom(a)
-    del(a, a.root, cast[int](addr(c.data)))
-    if c.size >= HugeChunkSize: freeHugeChunk(a, c)
-    else: freeBigChunk(a, c)
+    when overwriteFree: nimSetMem(p, -1'i32, c.size -% bigChunkOverhead())
+    when logAlloc: cprintf("dealloc(pointer_%p) # BIG %p\n", p, c.owner)
+    when defined(gcDestructors):
+      if c.owner == addr(a):
+        deallocBigChunk(a, cast[PBigChunk](c))
+      else:
+        addToSharedFreeListBigChunks(c.owner[], cast[PBigChunk](c))
+    else:
+      deallocBigChunk(a, cast[PBigChunk](c))
+
   sysAssert(allocInv(a), "rawDealloc: end")
-  when logAlloc: cprintf("dealloc(pointer_%p)\n", p)
+  #when logAlloc: cprintf("dealloc(pointer_%p)\n", p)
 
-proc isAllocatedPtr(a: MemRegion, p: pointer): bool =
-  if isAccessible(a, p):
-    var c = pageAddr(p)
-    if not chunkUnused(c):
-      if isSmallChunk(c):
-        var c = cast[PSmallChunk](c)
-        var offset = (cast[ByteAddress](p) and (PageSize-1)) -%
-                     smallChunkOverhead()
-        result = (c.acc >% offset) and (offset %% c.size == 0) and
-          (cast[ptr FreeCell](p).zeroField >% 1)
-      else:
-        var c = cast[PBigChunk](c)
-        result = p == addr(c.data) and cast[ptr FreeCell](p).zeroField >% 1
+when not defined(gcDestructors):
+  proc isAllocatedPtr(a: MemRegion, p: pointer): bool =
+    if isAccessible(a, p):
+      var c = pageAddr(p)
+      if not chunkUnused(c):
+        if isSmallChunk(c):
+          var c = cast[PSmallChunk](c)
+          var offset = (cast[int](p) and (PageSize-1)) -%
+                      smallChunkOverhead()
+          result = (c.acc.int >% offset) and (offset %% c.size == 0) and
+            (cast[ptr FreeCell](p).zeroField >% 1)
+        else:
+          var c = cast[PBigChunk](c)
+          result = p == addr(c.data) and cast[ptr FreeCell](p).zeroField >% 1
 
-proc prepareForInteriorPointerChecking(a: var MemRegion) {.inline.} =
-  a.minLargeObj = lowGauge(a.root)
-  a.maxLargeObj = highGauge(a.root)
+  proc prepareForInteriorPointerChecking(a: var MemRegion) {.inline.} =
+    a.minLargeObj = lowGauge(a.root)
+    a.maxLargeObj = highGauge(a.root)
 
-proc interiorAllocatedPtr(a: MemRegion, p: pointer): pointer =
-  if isAccessible(a, p):
-    var c = pageAddr(p)
-    if not chunkUnused(c):
-      if isSmallChunk(c):
-        var c = cast[PSmallChunk](c)
-        var offset = (cast[ByteAddress](p) and (PageSize-1)) -%
-                     smallChunkOverhead()
-        if c.acc >% offset:
-          sysAssert(cast[ByteAddress](addr(c.data)) +% offset ==
-                    cast[ByteAddress](p), "offset is not what you think it is")
-          var d = cast[ptr FreeCell](cast[ByteAddress](addr(c.data)) +%
-                    offset -% (offset %% c.size))
-          if d.zeroField >% 1:
+  proc interiorAllocatedPtr(a: MemRegion, p: pointer): pointer =
+    if isAccessible(a, p):
+      var c = pageAddr(p)
+      if not chunkUnused(c):
+        if isSmallChunk(c):
+          var c = cast[PSmallChunk](c)
+          var offset = (cast[int](p) and (PageSize-1)) -%
+                      smallChunkOverhead()
+          if c.acc.int >% offset:
+            sysAssert(cast[int](addr(c.data)) +% offset ==
+                      cast[int](p), "offset is not what you think it is")
+            var d = cast[ptr FreeCell](cast[int](addr(c.data)) +%
+                      offset -% (offset %% c.size))
+            if d.zeroField >% 1:
+              result = d
+              sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
+        else:
+          var c = cast[PBigChunk](c)
+          var d = addr(c.data)
+          if p >= d and cast[ptr FreeCell](d).zeroField >% 1:
             result = d
             sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
-      else:
-        var c = cast[PBigChunk](c)
-        var d = addr(c.data)
-        if p >= d and cast[ptr FreeCell](d).zeroField >% 1:
-          result = d
-          sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
-  else:
-    var q = cast[int](p)
-    if q >=% a.minLargeObj and q <=% a.maxLargeObj:
-      # this check is highly effective! Test fails for 99,96% of all checks on
-      # an x86-64.
-      var avlNode = inRange(a.root, q)
-      if avlNode != nil:
-        var k = cast[pointer](avlNode.key)
-        var c = cast[PBigChunk](pageAddr(k))
-        sysAssert(addr(c.data) == k, " k is not the same as addr(c.data)!")
-        if cast[ptr FreeCell](k).zeroField >% 1:
-          result = k
-          sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
+    else:
+      var q = cast[int](p)
+      if q >=% a.minLargeObj and q <=% a.maxLargeObj:
+        # this check is highly effective! Test fails for 99,96% of all checks on
+        # an x86-64.
+        var avlNode = inRange(a.root, q)
+        if avlNode != nil:
+          var k = cast[pointer](avlNode.key)
+          var c = cast[PBigChunk](pageAddr(k))
+          sysAssert(addr(c.data) == k, " k is not the same as addr(c.data)!")
+          if cast[ptr FreeCell](k).zeroField >% 1:
+            result = k
+            sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
 
 proc ptrSize(p: pointer): int =
-  var x = cast[pointer](cast[ByteAddress](p) -% sizeof(FreeCell))
-  var c = pageAddr(p)
-  sysAssert(not chunkUnused(c), "ptrSize")
-  result = c.size -% sizeof(FreeCell)
-  if not isSmallChunk(c):
-    dec result, bigChunkOverhead()
+  when not defined(gcDestructors):
+    var x = cast[pointer](cast[int](p) -% sizeof(FreeCell))
+    var c = pageAddr(p)
+    sysAssert(not chunkUnused(c), "ptrSize")
+    result = c.size -% sizeof(FreeCell)
+    if not isSmallChunk(c):
+      dec result, bigChunkOverhead()
+  else:
+    var c = pageAddr(p)
+    sysAssert(not chunkUnused(c), "ptrSize")
+    result = c.size
+    if not isSmallChunk(c):
+      dec result, bigChunkOverhead()
 
 proc alloc(allocator: var MemRegion, size: Natural): pointer {.gcsafe.} =
-  result = rawAlloc(allocator, size+sizeof(FreeCell))
-  cast[ptr FreeCell](result).zeroField = 1 # mark it as used
-  sysAssert(not isAllocatedPtr(allocator, result), "alloc")
-  result = cast[pointer](cast[ByteAddress](result) +% sizeof(FreeCell))
-  track("alloc", result, size)
+  when not defined(gcDestructors):
+    result = rawAlloc(allocator, size+sizeof(FreeCell))
+    cast[ptr FreeCell](result).zeroField = 1 # mark it as used
+    sysAssert(not isAllocatedPtr(allocator, result), "alloc")
+    result = cast[pointer](cast[int](result) +% sizeof(FreeCell))
+    track("alloc", result, size)
+  else:
+    result = rawAlloc(allocator, size)
 
 proc alloc0(allocator: var MemRegion, size: Natural): pointer =
   result = alloc(allocator, size)
   zeroMem(result, size)
 
 proc dealloc(allocator: var MemRegion, p: pointer) =
-  sysAssert(p != nil, "dealloc: p is nil")
-  var x = cast[pointer](cast[ByteAddress](p) -% sizeof(FreeCell))
-  sysAssert(x != nil, "dealloc: x is nil")
-  sysAssert(isAccessible(allocator, x), "is not accessible")
-  sysAssert(cast[ptr FreeCell](x).zeroField == 1, "dealloc: object header corrupted")
-  rawDealloc(allocator, x)
-  sysAssert(not isAllocatedPtr(allocator, x), "dealloc: object still accessible")
-  track("dealloc", p, 0)
+  when not defined(gcDestructors):
+    sysAssert(p != nil, "dealloc: p is nil")
+    var x = cast[pointer](cast[int](p) -% sizeof(FreeCell))
+    sysAssert(x != nil, "dealloc: x is nil")
+    sysAssert(isAccessible(allocator, x), "is not accessible")
+    sysAssert(cast[ptr FreeCell](x).zeroField == 1, "dealloc: object header corrupted")
+    rawDealloc(allocator, x)
+    sysAssert(not isAllocatedPtr(allocator, x), "dealloc: object still accessible")
+    track("dealloc", p, 0)
+  else:
+    rawDealloc(allocator, p)
 
 proc realloc(allocator: var MemRegion, p: pointer, newsize: Natural): pointer =
   if newsize > 0:
-    result = alloc0(allocator, newsize)
+    result = alloc(allocator, newsize)
     if p != nil:
       copyMem(result, p, min(ptrSize(p), newsize))
       dealloc(allocator, p)
   elif p != nil:
     dealloc(allocator, p)
 
+proc realloc0(allocator: var MemRegion, p: pointer, oldsize, newsize: Natural): pointer =
+  result = realloc(allocator, p, newsize)
+  if newsize > oldsize:
+    zeroMem(cast[pointer](cast[uint](result) + uint(oldsize)), newsize - oldsize)
+
 proc deallocOsPages(a: var MemRegion) =
   # we free every 'ordinarily' allocated page by iterating over the page bits:
   var it = addr(a.heapLinks)
@@ -961,7 +1186,7 @@ proc deallocOsPages(a: var MemRegion) =
       let (p, size) = it.chunks[i]
       when defined(debugHeapLinks):
         cprintf("owner %p; dealloc A: %p size: %ld; next: %p\n", addr(a),
-          it, it.origSize, next)
+          it, size, next)
       sysAssert size >= PageSize, "origSize too small"
       osDeallocPages(p, size)
     it = next
@@ -975,33 +1200,42 @@ proc getOccupiedMem(a: MemRegion): int {.inline.} =
   result = a.occ
   # a.currMem - a.freeMem
 
+when defined(nimTypeNames):
+  proc getMemCounters(a: MemRegion): (int, int) {.inline.} =
+    (a.allocCounter, a.deallocCounter)
+
 # ---------------------- thread memory region -------------------------------
 
-template instantiateForRegion(allocator: untyped) =
+template instantiateForRegion(allocator: untyped) {.dirty.} =
   {.push stackTrace: off.}
 
-  when defined(fulldebug):
+  when defined(nimFulldebug):
     proc interiorAllocatedPtr*(p: pointer): pointer =
       result = interiorAllocatedPtr(allocator, p)
 
     proc isAllocatedPtr*(p: pointer): bool =
-      let p = cast[pointer](cast[ByteAddress](p)-%ByteAddress(sizeof(Cell)))
+      let p = cast[pointer](cast[int](p)-%ByteAddress(sizeof(Cell)))
       result = isAllocatedPtr(allocator, p)
 
   proc deallocOsPages = deallocOsPages(allocator)
 
-  proc alloc(size: Natural): pointer =
+  proc allocImpl(size: Natural): pointer =
     result = alloc(allocator, size)
 
-  proc alloc0(size: Natural): pointer =
+  proc alloc0Impl(size: Natural): pointer =
     result = alloc0(allocator, size)
 
-  proc dealloc(p: pointer) =
+  proc deallocImpl(p: pointer) =
     dealloc(allocator, p)
 
-  proc realloc(p: pointer, newsize: Natural): pointer =
+  proc reallocImpl(p: pointer, newSize: Natural): pointer =
     result = realloc(allocator, p, newSize)
 
+  proc realloc0Impl(p: pointer, oldSize, newSize: Natural): pointer =
+    result = realloc(allocator, p, newSize)
+    if newSize > oldSize:
+      zeroMem(cast[pointer](cast[uint](result) + uint(oldSize)), newSize - oldSize)
+
   when false:
     proc countFreeMem(): int =
       # only used for assertions
@@ -1010,64 +1244,94 @@ template instantiateForRegion(allocator: untyped) =
         inc(result, it.size)
         it = it.next
 
+  when hasThreadSupport and not defined(gcDestructors):
+    proc addSysExitProc(quitProc: proc() {.noconv.}) {.importc: "atexit", header: "<stdlib.h>".}
+
+    var sharedHeap: MemRegion
+    var heapLock: SysLock
+    initSysLock(heapLock)
+    addSysExitProc(proc() {.noconv.} = deinitSys(heapLock))
+
   proc getFreeMem(): int =
-    result = allocator.freeMem
     #sysAssert(result == countFreeMem())
+    result = allocator.freeMem
+
+  proc getTotalMem(): int =
+    result = allocator.currMem
 
-  proc getTotalMem(): int = return allocator.currMem
-  proc getOccupiedMem(): int = return allocator.occ #getTotalMem() - getFreeMem()
-  proc getMaxMem*(): int = return getMaxMem(allocator)
+  proc getOccupiedMem(): int =
+    result = allocator.occ #getTotalMem() - getFreeMem()
+
+  proc getMaxMem*(): int =
+    result = getMaxMem(allocator)
+
+  when defined(nimTypeNames):
+    proc getMemCounters*(): (int, int) = getMemCounters(allocator)
 
   # -------------------- shared heap region ----------------------------------
-  when hasThreadSupport:
-    var sharedHeap: MemRegion
-    var heapLock: SysLock
-    initSysLock(heapLock)
 
-  proc allocShared(size: Natural): pointer =
-    when hasThreadSupport:
+  proc allocSharedImpl(size: Natural): pointer =
+    when hasThreadSupport and not defined(gcDestructors):
       acquireSys(heapLock)
       result = alloc(sharedHeap, size)
       releaseSys(heapLock)
     else:
-      result = alloc(size)
+      result = allocImpl(size)
 
-  proc allocShared0(size: Natural): pointer =
-    result = allocShared(size)
+  proc allocShared0Impl(size: Natural): pointer =
+    result = allocSharedImpl(size)
     zeroMem(result, size)
 
-  proc deallocShared(p: pointer) =
-    when hasThreadSupport:
+  proc deallocSharedImpl(p: pointer) =
+    when hasThreadSupport and not defined(gcDestructors):
       acquireSys(heapLock)
       dealloc(sharedHeap, p)
       releaseSys(heapLock)
     else:
-      dealloc(p)
+      deallocImpl(p)
 
-  proc reallocShared(p: pointer, newsize: Natural): pointer =
-    when hasThreadSupport:
+  proc reallocSharedImpl(p: pointer, newSize: Natural): pointer =
+    when hasThreadSupport and not defined(gcDestructors):
       acquireSys(heapLock)
-      result = realloc(sharedHeap, p, newsize)
+      result = realloc(sharedHeap, p, newSize)
       releaseSys(heapLock)
     else:
-      result = realloc(p, newSize)
+      result = reallocImpl(p, newSize)
 
-  when hasThreadSupport:
-
-    template sharedMemStatsShared(v: int) {.immediate.} =
+  proc reallocShared0Impl(p: pointer, oldSize, newSize: Natural): pointer =
+    when hasThreadSupport and not defined(gcDestructors):
       acquireSys(heapLock)
-      result = v
+      result = realloc0(sharedHeap, p, oldSize, newSize)
       releaseSys(heapLock)
+    else:
+      result = realloc0Impl(p, oldSize, newSize)
+
+  when hasThreadSupport:
+    when defined(gcDestructors):
+      proc getFreeSharedMem(): int =
+        allocator.freeMem
+
+      proc getTotalSharedMem(): int =
+        allocator.currMem
+
+      proc getOccupiedSharedMem(): int =
+        allocator.occ
+
+    else:
+      template sharedMemStatsShared(v: int) =
+        acquireSys(heapLock)
+        result = v
+        releaseSys(heapLock)
 
-    proc getFreeSharedMem(): int =
-      sharedMemStatsShared(sharedHeap.freeMem)
+      proc getFreeSharedMem(): int =
+        sharedMemStatsShared(sharedHeap.freeMem)
 
-    proc getTotalSharedMem(): int =
-      sharedMemStatsShared(sharedHeap.currMem)
+      proc getTotalSharedMem(): int =
+        sharedMemStatsShared(sharedHeap.currMem)
 
-    proc getOccupiedSharedMem(): int =
-      sharedMemStatsShared(sharedHeap.occ)
-      #sharedMemStatsShared(sharedHeap.currMem - sharedHeap.freeMem)
+      proc getOccupiedSharedMem(): int =
+        sharedMemStatsShared(sharedHeap.occ)
+        #sharedMemStatsShared(sharedHeap.currMem - sharedHeap.freeMem)
   {.pop.}
 
 {.pop.}
diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim
index 0bac979e7..3098e17d6 100644
--- a/lib/system/ansi_c.nim
+++ b/lib/system/ansi_c.nim
@@ -7,114 +7,227 @@
 #    distribution, for details about the copyright.
 #
 
-# This include file contains headers of Ansi C procs
+# This module contains headers of Ansi C procs
 # and definitions of Ansi C types in Nim syntax
 # All symbols are prefixed with 'c_' to avoid ambiguities
 
-{.push hints:off}
+{.push hints:off, stack_trace: off, profiler: off.}
 
-proc c_memchr(s: pointer, c: cint, n: csize): pointer {.
+proc c_memchr*(s: pointer, c: cint, n: csize_t): pointer {.
   importc: "memchr", header: "<string.h>".}
-proc c_memcmp(a, b: pointer, size: csize): cint {.
+proc c_memcmp*(a, b: pointer, size: csize_t): cint {.
   importc: "memcmp", header: "<string.h>", noSideEffect.}
-proc c_memcpy(a, b: pointer, size: csize): pointer {.
+proc c_memcpy*(a, b: pointer, size: csize_t): pointer {.
   importc: "memcpy", header: "<string.h>", discardable.}
-proc c_memmove(a, b: pointer, size: csize): pointer {.
+proc c_memmove*(a, b: pointer, size: csize_t): pointer {.
   importc: "memmove", header: "<string.h>",discardable.}
-proc c_memset(p: pointer, value: cint, size: csize): pointer {.
+proc c_memset*(p: pointer, value: cint, size: csize_t): pointer {.
   importc: "memset", header: "<string.h>", discardable.}
-proc c_strcmp(a, b: cstring): cint {.
+proc c_strcmp*(a, b: cstring): cint {.
   importc: "strcmp", header: "<string.h>", noSideEffect.}
+proc c_strlen*(a: cstring): csize_t {.
+  importc: "strlen", header: "<string.h>", noSideEffect.}
+proc c_abort*() {.
+  importc: "abort", header: "<stdlib.h>", noSideEffect, noreturn.}
 
-when defined(linux) and defined(amd64):
+
+when defined(nimBuiltinSetjmp):
+  type
+    C_JmpBuf* = array[5, pointer]
+elif defined(linux) and defined(amd64):
   type
-    C_JmpBuf {.importc: "jmp_buf", header: "<setjmp.h>", bycopy.} = object
+    C_JmpBuf* {.importc: "jmp_buf", header: "<setjmp.h>", bycopy.} = object
         abi: array[200 div sizeof(clong), clong]
 else:
   type
-    C_JmpBuf {.importc: "jmp_buf", header: "<setjmp.h>".} = object
+    C_JmpBuf* {.importc: "jmp_buf", header: "<setjmp.h>".} = object
 
 
+type CSighandlerT = proc (a: cint) {.noconv.}
 when defined(windows):
   const
-    SIGABRT = cint(22)
-    SIGFPE = cint(8)
-    SIGILL = cint(4)
-    SIGINT = cint(2)
-    SIGSEGV = cint(11)
+    SIGABRT* = cint(22)
+    SIGFPE* = cint(8)
+    SIGILL* = cint(4)
+    SIGINT* = cint(2)
+    SIGSEGV* = cint(11)
     SIGTERM = cint(15)
+    SIG_DFL* = cast[CSighandlerT](0)
 elif defined(macosx) or defined(linux) or defined(freebsd) or
      defined(openbsd) or defined(netbsd) or defined(solaris) or
-     defined(dragonfly):
+     defined(dragonfly) or defined(nintendoswitch) or defined(genode) or
+     defined(aix) or hostOS == "standalone":
   const
-    SIGABRT = cint(6)
-    SIGFPE = cint(8)
-    SIGILL = cint(4)
-    SIGINT = cint(2)
-    SIGSEGV = cint(11)
-    SIGTERM = cint(15)
-    SIGPIPE = cint(13)
+    SIGABRT* = cint(6)
+    SIGFPE* = cint(8)
+    SIGILL* = cint(4)
+    SIGINT* = cint(2)
+    SIGSEGV* = cint(11)
+    SIGTERM* = cint(15)
+    SIGPIPE* = cint(13)
+    SIG_DFL* = CSighandlerT(nil)
+elif defined(haiku):
+  const
+    SIGABRT* = cint(6)
+    SIGFPE* = cint(8)
+    SIGILL* = cint(4)
+    SIGINT* = cint(2)
+    SIGSEGV* = cint(11)
+    SIGTERM* = cint(15)
+    SIGPIPE* = cint(7)
+    SIG_DFL* = CSighandlerT(nil)
 else:
-  when NoFakeVars:
+  when defined(nimscript):
     {.error: "SIGABRT not ported to your platform".}
   else:
     var
-      SIGINT {.importc: "SIGINT", nodecl.}: cint
-      SIGSEGV {.importc: "SIGSEGV", nodecl.}: cint
-      SIGABRT {.importc: "SIGABRT", nodecl.}: cint
-      SIGFPE {.importc: "SIGFPE", nodecl.}: cint
-      SIGILL {.importc: "SIGILL", nodecl.}: cint
+      SIGINT* {.importc: "SIGINT", nodecl.}: cint
+      SIGSEGV* {.importc: "SIGSEGV", nodecl.}: cint
+      SIGABRT* {.importc: "SIGABRT", nodecl.}: cint
+      SIGFPE* {.importc: "SIGFPE", nodecl.}: cint
+      SIGILL* {.importc: "SIGILL", nodecl.}: cint
+      SIG_DFL* {.importc: "SIG_DFL", nodecl.}: CSighandlerT
     when defined(macosx) or defined(linux):
-      var SIGPIPE {.importc: "SIGPIPE", nodecl.}: cint
+      var SIGPIPE* {.importc: "SIGPIPE", nodecl.}: cint
 
 when defined(macosx):
-  const SIGBUS = cint(10)
-else:
-  template SIGBUS: untyped = SIGSEGV
+  const SIGBUS* = cint(10)
+elif defined(haiku):
+  const SIGBUS* = cint(30)
 
-when defined(nimSigSetjmp) and not defined(nimStdSetjmp):
-  proc c_longjmp(jmpb: C_JmpBuf, retval: cint) {.
+# "nimRawSetjmp" is defined by default for certain platforms, so we need the
+# "nimStdSetjmp" escape hatch with it.
+when defined(nimSigSetjmp):
+  proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) {.
     header: "<setjmp.h>", importc: "siglongjmp".}
-  template c_setjmp(jmpb: C_JmpBuf): cint =
+  proc c_setjmp*(jmpb: C_JmpBuf): cint =
     proc c_sigsetjmp(jmpb: C_JmpBuf, savemask: cint): cint {.
       header: "<setjmp.h>", importc: "sigsetjmp".}
     c_sigsetjmp(jmpb, 0)
+elif defined(nimBuiltinSetjmp):
+  proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) =
+    # Apple's Clang++ has trouble converting array names to pointers, so we need
+    # to be very explicit here.
+    proc c_builtin_longjmp(jmpb: ptr pointer, retval: cint) {.
+      importc: "__builtin_longjmp", nodecl.}
+    # The second parameter needs to be 1 and sometimes the C/C++ compiler checks it.
+    c_builtin_longjmp(unsafeAddr jmpb[0], 1)
+  proc c_setjmp*(jmpb: C_JmpBuf): cint =
+    proc c_builtin_setjmp(jmpb: ptr pointer): cint {.
+      importc: "__builtin_setjmp", nodecl.}
+    c_builtin_setjmp(unsafeAddr jmpb[0])
+
 elif defined(nimRawSetjmp) and not defined(nimStdSetjmp):
-  proc c_longjmp(jmpb: C_JmpBuf, retval: cint) {.
-    header: "<setjmp.h>", importc: "_longjmp".}
-  proc c_setjmp(jmpb: C_JmpBuf): cint {.
-    header: "<setjmp.h>", importc: "_setjmp".}
+  when defined(windows):
+    # No `_longjmp()` on Windows.
+    proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) {.
+      header: "<setjmp.h>", importc: "longjmp".}
+    when defined(vcc) or defined(clangcl):
+      proc c_setjmp*(jmpb: C_JmpBuf): cint {.
+        header: "<setjmp.h>", importc: "setjmp".}
+    else:
+      # The Windows `_setjmp()` takes two arguments, with the second being an
+      # undocumented buffer used by the SEH mechanism for stack unwinding.
+      # Mingw-w64 has been trying to get it right for years, but it's still
+      # prone to stack corruption during unwinding, so we disable that by setting
+      # it to NULL.
+      # More details: https://github.com/status-im/nimbus-eth2/issues/3121
+      when defined(nimHasStyleChecks):
+        {.push styleChecks: off.}
+
+      proc c_setjmp*(jmpb: C_JmpBuf): cint =
+        proc c_setjmp_win(jmpb: C_JmpBuf, ctx: pointer): cint {.
+          header: "<setjmp.h>", importc: "_setjmp".}
+        c_setjmp_win(jmpb, nil)
+
+      when defined(nimHasStyleChecks):
+        {.pop.}
+  else:
+    proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) {.
+      header: "<setjmp.h>", importc: "_longjmp".}
+    proc c_setjmp*(jmpb: C_JmpBuf): cint {.
+      header: "<setjmp.h>", importc: "_setjmp".}
 else:
-  proc c_longjmp(jmpb: C_JmpBuf, retval: cint) {.
+  proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) {.
     header: "<setjmp.h>", importc: "longjmp".}
-  proc c_setjmp(jmpb: C_JmpBuf): cint {.
+  proc c_setjmp*(jmpb: C_JmpBuf): cint {.
     header: "<setjmp.h>", importc: "setjmp".}
 
-type c_sighandler_t = proc (a: cint) {.noconv.}
-proc c_signal(sign: cint, handler: proc (a: cint) {.noconv.}): c_sighandler_t {.
+proc c_signal*(sign: cint, handler: CSighandlerT): CSighandlerT {.
   importc: "signal", header: "<signal.h>", discardable.}
+proc c_raise*(sign: cint): cint {.importc: "raise", header: "<signal.h>".}
 
-proc c_fprintf(f: File, frmt: cstring): cint {.
+type
+  CFile {.importc: "FILE", header: "<stdio.h>",
+          incompleteStruct.} = object
+  CFilePtr* = ptr CFile ## The type representing a file handle.
+
+# duplicated between io and ansi_c
+const stdioUsesMacros = (defined(osx) or defined(freebsd) or defined(dragonfly)) and not defined(emscripten)
+const stderrName = when stdioUsesMacros: "__stderrp" else: "stderr"
+const stdoutName = when stdioUsesMacros: "__stdoutp" else: "stdout"
+const stdinName = when stdioUsesMacros: "__stdinp" else: "stdin"
+
+var
+  cstderr* {.importc: stderrName, header: "<stdio.h>".}: CFilePtr
+  cstdout* {.importc: stdoutName, header: "<stdio.h>".}: CFilePtr
+  cstdin* {.importc: stdinName, header: "<stdio.h>".}: CFilePtr
+
+proc c_fprintf*(f: CFilePtr, frmt: cstring): cint {.
   importc: "fprintf", header: "<stdio.h>", varargs, discardable.}
-proc c_printf(frmt: cstring): cint {.
+proc c_printf*(frmt: cstring): cint {.
   importc: "printf", header: "<stdio.h>", varargs, discardable.}
 
-proc c_sprintf(buf, frmt: cstring): cint {.
+proc c_fputs*(c: cstring, f: CFilePtr): cint {.
+  importc: "fputs", header: "<stdio.h>", discardable.}
+proc c_fputc*(c: char, f: CFilePtr): cint {.
+  importc: "fputc", header: "<stdio.h>", discardable.}
+
+proc c_sprintf*(buf, frmt: cstring): cint {.
   importc: "sprintf", header: "<stdio.h>", varargs, noSideEffect.}
   # we use it only in a way that cannot lead to security issues
 
-when defined(windows):
-  proc c_fileno(f: File): cint {.
-      importc: "_fileno", header: "<stdio.h>".}
+proc c_snprintf*(buf: cstring, n: csize_t, frmt: cstring): cint {.
+  importc: "snprintf", header: "<stdio.h>", varargs, noSideEffect.}
+
+when defined(zephyr) and not defined(zephyrUseLibcMalloc):
+  proc c_malloc*(size: csize_t): pointer {.
+    importc: "k_malloc", header: "<kernel.h>".}
+  proc c_calloc*(nmemb, size: csize_t): pointer {.
+    importc: "k_calloc", header: "<kernel.h>".}
+  proc c_free*(p: pointer) {.
+    importc: "k_free", header: "<kernel.h>".}
+  proc c_realloc*(p: pointer, newsize: csize_t): pointer =
+    # Zephyr's kernel malloc doesn't support realloc
+    result = c_malloc(newSize)
+    # match the ansi c behavior
+    if not result.isNil():
+      copyMem(result, p, newSize)
+      c_free(p)
 else:
-  proc c_fileno(f: File): cint {.
-      importc: "fileno", header: "<fcntl.h>".}
+  proc c_malloc*(size: csize_t): pointer {.
+    importc: "malloc", header: "<stdlib.h>".}
+  proc c_calloc*(nmemb, size: csize_t): pointer {.
+    importc: "calloc", header: "<stdlib.h>".}
+  proc c_free*(p: pointer) {.
+    importc: "free", header: "<stdlib.h>".}
+  proc c_realloc*(p: pointer, newsize: csize_t): pointer {.
+    importc: "realloc", header: "<stdlib.h>".}
+
+proc c_fwrite*(buf: pointer, size, n: csize_t, f: CFilePtr): csize_t {.
+  importc: "fwrite", header: "<stdio.h>".}
+
+proc c_fflush*(f: CFilePtr): cint {.
+  importc: "fflush", header: "<stdio.h>".}
+
+proc rawWriteString*(f: CFilePtr, s: cstring, length: int) {.compilerproc, nonReloadable, inline.} =
+  # we cannot throw an exception here!
+  discard c_fwrite(s, 1, cast[csize_t](length), f)
+  discard c_fflush(f)
 
-proc c_malloc(size: csize): pointer {.
-  importc: "malloc", header: "<stdlib.h>".}
-proc c_free(p: pointer) {.
-  importc: "free", header: "<stdlib.h>".}
-proc c_realloc(p: pointer, newsize: csize): pointer {.
-  importc: "realloc", header: "<stdlib.h>".}
+proc rawWrite*(f: CFilePtr, s: cstring) {.compilerproc, nonReloadable, inline.} =
+  # we cannot throw an exception here!
+  discard c_fwrite(s, 1, c_strlen(s), f)
+  discard c_fflush(f)
 
-{.pop}
+{.pop.}
diff --git a/lib/system/arc.nim b/lib/system/arc.nim
new file mode 100644
index 000000000..d001fcaa5
--- /dev/null
+++ b/lib/system/arc.nim
@@ -0,0 +1,267 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2019 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+#[
+In this new runtime we simplify the object layouts a bit: The runtime type
+information is only accessed for the objects that have it and it's always
+at offset 0 then. The ``ref`` object header is independent from the
+runtime type and only contains a reference count.
+]#
+
+when defined(gcOrc):
+  const
+    rcIncrement = 0b10000 # so that lowest 4 bits are not touched
+    rcMask = 0b1111
+    rcShift = 4      # shift by rcShift to get the reference counter
+
+else:
+  const
+    rcIncrement = 0b1000 # so that lowest 3 bits are not touched
+    rcMask = 0b111
+    rcShift = 3      # shift by rcShift to get the reference counter
+
+const
+  orcLeakDetector = defined(nimOrcLeakDetector)
+
+type
+  RefHeader = object
+    rc: int # the object header is now a single RC field.
+            # we could remove it in non-debug builds for the 'owned ref'
+            # design but this seems unwise.
+    when defined(gcOrc):
+      rootIdx: int # thanks to this we can delete potential cycle roots
+                   # in O(1) without doubly linked lists
+    when defined(nimArcDebug) or defined(nimArcIds):
+      refId: int
+    when defined(gcOrc) and orcLeakDetector:
+      filename: cstring
+      line: int
+
+  Cell = ptr RefHeader
+
+template setFrameInfo(c: Cell) =
+  when orcLeakDetector:
+    if framePtr != nil and framePtr.prev != nil:
+      c.filename = framePtr.prev.filename
+      c.line = framePtr.prev.line
+    else:
+      c.filename = nil
+      c.line = 0
+
+template head(p: pointer): Cell =
+  cast[Cell](cast[int](p) -% sizeof(RefHeader))
+
+const
+  traceCollector = defined(traceArc)
+
+when defined(nimArcDebug):
+  include cellsets
+
+  const traceId = 20 # 1037
+
+  var gRefId: int
+  var freedCells: CellSet
+elif defined(nimArcIds):
+  var gRefId: int
+
+  const traceId = -1
+
+when defined(gcAtomicArc) and hasThreadSupport:
+  template decrement(cell: Cell): untyped =
+    discard atomicDec(cell.rc, rcIncrement)
+  template increment(cell: Cell): untyped =
+    discard atomicInc(cell.rc, rcIncrement)
+  template count(x: Cell): untyped =
+    atomicLoadN(x.rc.addr, ATOMIC_ACQUIRE) shr rcShift
+else:
+  template decrement(cell: Cell): untyped =
+    dec(cell.rc, rcIncrement)
+  template increment(cell: Cell): untyped =
+    inc(cell.rc, rcIncrement)
+  template count(x: Cell): untyped =
+    x.rc shr rcShift
+
+proc nimNewObj(size, alignment: int): pointer {.compilerRtl.} =
+  let hdrSize = align(sizeof(RefHeader), alignment)
+  let s = size + hdrSize
+  when defined(nimscript):
+    discard
+  else:
+    result = alignedAlloc0(s, alignment) +! hdrSize
+  when defined(nimArcDebug) or defined(nimArcIds):
+    head(result).refId = gRefId
+    atomicInc gRefId
+    if head(result).refId == traceId:
+      writeStackTrace()
+      cfprintf(cstderr, "[nimNewObj] %p %ld\n", result, head(result).count)
+  when traceCollector:
+    cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result)
+  setFrameInfo head(result)
+
+proc nimNewObjUninit(size, alignment: int): pointer {.compilerRtl.} =
+  # Same as 'newNewObj' but do not initialize the memory to zero.
+  # The codegen proved for us that this is not necessary.
+  let hdrSize = align(sizeof(RefHeader), alignment)
+  let s = size + hdrSize
+  when defined(nimscript):
+    discard
+  else:
+    result = cast[ptr RefHeader](alignedAlloc(s, alignment) +! hdrSize)
+  head(result).rc = 0
+  when defined(gcOrc):
+    head(result).rootIdx = 0
+  when defined(nimArcDebug):
+    head(result).refId = gRefId
+    atomicInc gRefId
+    if head(result).refId == traceId:
+      writeStackTrace()
+      cfprintf(cstderr, "[nimNewObjUninit] %p %ld\n", result, head(result).count)
+
+  when traceCollector:
+    cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result)
+  setFrameInfo head(result)
+
+proc nimDecWeakRef(p: pointer) {.compilerRtl, inl.} =
+  decrement head(p)
+
+proc isUniqueRef*[T](x: ref T): bool {.inline.} =
+  ## Returns true if the object `x` points to is uniquely referenced. Such
+  ## an object can potentially be passed over to a different thread safely,
+  ## if great care is taken. This queries the internal reference count of
+  ## the object which is subject to lots of optimizations! In other words
+  ## the value of `isUniqueRef` can depend on the used compiler version and
+  ## optimizer setting.
+  ## Nevertheless it can be used as a very valuable debugging tool and can
+  ## be used to specify the constraints of a threading related API
+  ## via `assert isUniqueRef(x)`.
+  head(cast[pointer](x)).rc == 0
+
+proc nimIncRef(p: pointer) {.compilerRtl, inl.} =
+  when defined(nimArcDebug):
+    if head(p).refId == traceId:
+      writeStackTrace()
+      cfprintf(cstderr, "[IncRef] %p %ld\n", p, head(p).count)
+
+  increment head(p)
+  when traceCollector:
+    cprintf("[INCREF] %p\n", head(p))
+
+when not defined(gcOrc) or defined(nimThinout):
+  proc unsureAsgnRef(dest: ptr pointer, src: pointer) {.inline.} =
+    # This is only used by the old RTTI mechanism and we know
+    # that 'dest[]' is nil and needs no destruction. Which is really handy
+    # as we cannot destroy the object reliably if it's an object of unknown
+    # compile-time type.
+    dest[] = src
+    if src != nil: nimIncRef src
+
+when not defined(nimscript) and defined(nimArcDebug):
+  proc deallocatedRefId*(p: pointer): int =
+    ## Returns the ref's ID if the ref was already deallocated. This
+    ## is a memory corruption check. Returns 0 if there is no error.
+    let c = head(p)
+    if freedCells.data != nil and freedCells.contains(c):
+      result = c.refId
+    else:
+      result = 0
+
+proc nimRawDispose(p: pointer, alignment: int) {.compilerRtl.} =
+  when not defined(nimscript):
+    when traceCollector:
+      cprintf("[Freed] %p\n", p -! sizeof(RefHeader))
+    when defined(nimOwnedEnabled):
+      if head(p).rc >= rcIncrement:
+        cstderr.rawWrite "[FATAL] dangling references exist\n"
+        rawQuit 1
+    when defined(nimArcDebug):
+      # we do NOT really free the memory here in order to reliably detect use-after-frees
+      if freedCells.data == nil: init(freedCells)
+      freedCells.incl head(p)
+    else:
+      let hdrSize = align(sizeof(RefHeader), alignment)
+      alignedDealloc(p -! hdrSize, alignment)
+
+template `=dispose`*[T](x: owned(ref T)) = nimRawDispose(cast[pointer](x), T.alignOf)
+#proc dispose*(x: pointer) = nimRawDispose(x)
+
+proc nimDestroyAndDispose(p: pointer) {.compilerRtl, raises: [].} =
+  let rti = cast[ptr PNimTypeV2](p)
+  if rti.destructor != nil:
+    cast[DestructorProc](rti.destructor)(p)
+  when false:
+    cstderr.rawWrite cast[ptr PNimTypeV2](p)[].name
+    cstderr.rawWrite "\n"
+    if d == nil:
+      cstderr.rawWrite "bah, nil\n"
+    else:
+      cstderr.rawWrite "has destructor!\n"
+  nimRawDispose(p, rti.align)
+
+when defined(gcOrc):
+  when defined(nimThinout):
+    include cyclebreaker
+  else:
+    include orc
+    #include cyclecollector
+
+proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} =
+  if p != nil:
+    var cell = head(p)
+
+    when defined(nimArcDebug):
+      if cell.refId == traceId:
+        writeStackTrace()
+        cfprintf(cstderr, "[DecRef] %p %ld\n", p, cell.count)
+
+    when defined(gcAtomicArc) and hasThreadSupport:
+      # `atomicDec` returns the new value
+      if atomicDec(cell.rc, rcIncrement) == -rcIncrement:
+        result = true
+        when traceCollector:
+          cprintf("[ABOUT TO DESTROY] %p\n", cell)
+    else:
+      if cell.count == 0:
+        result = true
+        when traceCollector:
+          cprintf("[ABOUT TO DESTROY] %p\n", cell)
+      else:
+        decrement cell
+        # According to Lins it's correct to do nothing else here.
+        when traceCollector:
+          cprintf("[DECREF] %p\n", cell)
+
+proc GC_unref*[T](x: ref T) =
+  ## New runtime only supports this operation for 'ref T'.
+  var y {.cursor.} = x
+  `=destroy`(y)
+
+proc GC_ref*[T](x: ref T) =
+  ## New runtime only supports this operation for 'ref T'.
+  if x != nil: nimIncRef(cast[pointer](x))
+
+when not defined(gcOrc):
+  template GC_fullCollect* =
+    ## Forces a full garbage collection pass. With `--mm:arc` a nop.
+    discard
+
+template setupForeignThreadGc* =
+  ## With `--mm:arc` a nop.
+  discard
+
+template tearDownForeignThreadGc* =
+  ## With `--mm:arc` a nop.
+  discard
+
+proc isObjDisplayCheck(source: PNimTypeV2, targetDepth: int16, token: uint32): bool {.compilerRtl, inl.} =
+  result = targetDepth <= source.depth and source.display[targetDepth] == token
+
+when defined(gcDestructors):
+  proc nimGetVTable(p: pointer, index: int): pointer
+        {.compilerRtl, inline, raises: [].} =
+    result = cast[ptr PNimTypeV2](p).vTable[index]
diff --git a/lib/system/arithm.nim b/lib/system/arithm.nim
deleted file mode 100644
index 69c558799..000000000
--- a/lib/system/arithm.nim
+++ /dev/null
@@ -1,411 +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.
-#
-
-
-# simple integer arithmetic with overflow checking
-
-proc raiseOverflow {.compilerproc, noinline.} =
-  # a single proc to reduce code size to a minimum
-  sysFatal(OverflowError, "over- or underflow")
-
-proc raiseDivByZero {.compilerproc, noinline.} =
-  sysFatal(DivByZeroError, "division by zero")
-
-when defined(builtinOverflow):
-  # Builtin compiler functions for improved performance
-  when sizeof(clong) == 8:
-    proc addInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
-      importc: "__builtin_saddl_overflow", nodecl, nosideeffect.}
-
-    proc subInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
-      importc: "__builtin_ssubl_overflow", nodecl, nosideeffect.}
-
-    proc mulInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
-      importc: "__builtin_smull_overflow", nodecl, nosideeffect.}
-
-  elif sizeof(clonglong) == 8:
-    proc addInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
-      importc: "__builtin_saddll_overflow", nodecl, nosideeffect.}
-
-    proc subInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
-      importc: "__builtin_ssubll_overflow", nodecl, nosideeffect.}
-
-    proc mulInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
-      importc: "__builtin_smulll_overflow", nodecl, nosideeffect.}
-
-  when sizeof(int) == 8:
-    proc addIntOverflow(a, b: int, c: var int): bool {.inline.} =
-      addInt64Overflow(a, b, c)
-
-    proc subIntOverflow(a, b: int, c: var int): bool {.inline.} =
-      subInt64Overflow(a, b, c)
-
-    proc mulIntOverflow(a, b: int, c: var int): bool {.inline.} =
-      mulInt64Overflow(a, b, c)
-
-  elif sizeof(int) == 4 and sizeof(cint) == 4:
-    proc addIntOverflow(a, b: int, c: var int): bool {.
-      importc: "__builtin_sadd_overflow", nodecl, nosideeffect.}
-
-    proc subIntOverflow(a, b: int, c: var int): bool {.
-      importc: "__builtin_ssub_overflow", nodecl, nosideeffect.}
-
-    proc mulIntOverflow(a, b: int, c: var int): bool {.
-      importc: "__builtin_smul_overflow", nodecl, nosideeffect.}
-
-  proc addInt64(a, b: int64): int64 {.compilerProc, inline.} =
-    if addInt64Overflow(a, b, result):
-      raiseOverflow()
-
-  proc subInt64(a, b: int64): int64 {.compilerProc, inline.} =
-    if subInt64Overflow(a, b, result):
-      raiseOverflow()
-
-  proc mulInt64(a, b: int64): int64 {.compilerproc, inline.} =
-    if mulInt64Overflow(a, b, result):
-      raiseOverflow()
-else:
-  proc addInt64(a, b: int64): int64 {.compilerProc, inline.} =
-    result = a +% b
-    if (result xor a) >= int64(0) or (result xor b) >= int64(0):
-      return result
-    raiseOverflow()
-
-  proc subInt64(a, b: int64): int64 {.compilerProc, inline.} =
-    result = a -% b
-    if (result xor a) >= int64(0) or (result xor not b) >= int64(0):
-      return result
-    raiseOverflow()
-
-  #
-  # This code has been inspired by Python's source code.
-  # The native int product x*y is either exactly right or *way* off, being
-  # just the last n bits of the true product, where n is the number of bits
-  # in an int (the delivered product is the true product plus i*2**n for
-  # some integer i).
-  #
-  # The native float64 product x*y is subject to three
-  # rounding errors: on a sizeof(int)==8 box, each cast to double can lose
-  # info, and even on a sizeof(int)==4 box, the multiplication can lose info.
-  # But, unlike the native int product, it's not in *range* trouble:  even
-  # if sizeof(int)==32 (256-bit ints), the product easily fits in the
-  # dynamic range of a float64. So the leading 50 (or so) bits of the float64
-  # product are correct.
-  #
-  # We check these two ways against each other, and declare victory if they're
-  # approximately the same. Else, because the native int product is the only
-  # one that can lose catastrophic amounts of information, it's the native int
-  # product that must have overflowed.
-  #
-  proc mulInt64(a, b: int64): int64 {.compilerproc.} =
-    var
-      resAsFloat, floatProd: float64
-    result = a *% b
-    floatProd = toBiggestFloat(a) # conversion
-    floatProd = floatProd * toBiggestFloat(b)
-    resAsFloat = toBiggestFloat(result)
-
-    # Fast path for normal case: small multiplicands, and no info
-    # is lost in either method.
-    if resAsFloat == floatProd: return result
-
-    # Somebody somewhere lost info. Close enough, or way off? Note
-    # that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
-    # The difference either is or isn't significant compared to the
-    # true value (of which floatProd is a good approximation).
-
-    # abs(diff)/abs(prod) <= 1/32 iff
-    #   32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
-    if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
-      return result
-    raiseOverflow()
-
-proc negInt64(a: int64): int64 {.compilerProc, inline.} =
-  if a != low(int64): return -a
-  raiseOverflow()
-
-proc absInt64(a: int64): int64 {.compilerProc, inline.} =
-  if a != low(int64):
-    if a >= 0: return a
-    else: return -a
-  raiseOverflow()
-
-proc divInt64(a, b: int64): int64 {.compilerProc, inline.} =
-  if b == int64(0):
-    raiseDivByZero()
-  if a == low(int64) and b == int64(-1):
-    raiseOverflow()
-  return a div b
-
-proc modInt64(a, b: int64): int64 {.compilerProc, inline.} =
-  if b == int64(0):
-    raiseDivByZero()
-  return a mod b
-
-proc absInt(a: int): int {.compilerProc, inline.} =
-  if a != low(int):
-    if a >= 0: return a
-    else: return -a
-  raiseOverflow()
-
-const
-  asmVersion = defined(I386) and (defined(vcc) or defined(wcc) or
-               defined(dmc) or defined(gcc) or defined(llvm_gcc))
-    # my Version of Borland C++Builder does not have
-    # tasm32, which is needed for assembler blocks
-    # this is why Borland is not included in the 'when'
-
-when asmVersion and not defined(gcc) and not defined(llvm_gcc):
-  # assembler optimized versions for compilers that
-  # have an intel syntax assembler:
-  proc addInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
-    # a in eax, and b in edx
-    asm """
-        mov eax, ecx
-        add eax, edx
-        jno theEnd
-        call `raiseOverflow`
-      theEnd:
-        ret
-    """
-
-  proc subInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
-    asm """
-        mov eax, ecx
-        sub eax, edx
-        jno theEnd
-        call `raiseOverflow`
-      theEnd:
-        ret
-    """
-
-  proc negInt(a: int): int {.compilerProc, asmNoStackFrame.} =
-    asm """
-        mov eax, ecx
-        neg eax
-        jno theEnd
-        call `raiseOverflow`
-      theEnd:
-        ret
-    """
-
-  proc divInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
-    asm """
-        mov eax, ecx
-        mov ecx, edx
-        xor edx, edx
-        idiv ecx
-        jno  theEnd
-        call `raiseOverflow`
-      theEnd:
-        ret
-    """
-
-  proc modInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
-    asm """
-        mov eax, ecx
-        mov ecx, edx
-        xor edx, edx
-        idiv ecx
-        jno theEnd
-        call `raiseOverflow`
-      theEnd:
-        mov eax, edx
-        ret
-    """
-
-  proc mulInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
-    asm """
-        mov eax, ecx
-        mov ecx, edx
-        xor edx, edx
-        imul ecx
-        jno theEnd
-        call `raiseOverflow`
-      theEnd:
-        ret
-    """
-
-elif false: # asmVersion and (defined(gcc) or defined(llvm_gcc)):
-  proc addInt(a, b: int): int {.compilerProc, inline.} =
-    # don't use a pure proc here!
-    asm """
-      "addl %%ecx, %%eax\n"
-      "jno 1\n"
-      "call _raiseOverflow\n"
-      "1: \n"
-      :"=a"(`result`)
-      :"a"(`a`), "c"(`b`)
-    """
-    #".intel_syntax noprefix"
-    #/* Intel syntax here */
-    #".att_syntax"
-
-  proc subInt(a, b: int): int {.compilerProc, inline.} =
-    asm """ "subl %%ecx,%%eax\n"
-            "jno 1\n"
-            "call _raiseOverflow\n"
-            "1: \n"
-           :"=a"(`result`)
-           :"a"(`a`), "c"(`b`)
-    """
-
-  proc mulInt(a, b: int): int {.compilerProc, inline.} =
-    asm """  "xorl %%edx, %%edx\n"
-             "imull %%ecx\n"
-             "jno 1\n"
-             "call _raiseOverflow\n"
-             "1: \n"
-            :"=a"(`result`)
-            :"a"(`a`), "c"(`b`)
-            :"%edx"
-    """
-
-  proc negInt(a: int): int {.compilerProc, inline.} =
-    asm """ "negl %%eax\n"
-            "jno 1\n"
-            "call _raiseOverflow\n"
-            "1: \n"
-           :"=a"(`result`)
-           :"a"(`a`)
-    """
-
-  proc divInt(a, b: int): int {.compilerProc, inline.} =
-    asm """  "xorl %%edx, %%edx\n"
-             "idivl %%ecx\n"
-             "jno 1\n"
-             "call _raiseOverflow\n"
-             "1: \n"
-            :"=a"(`result`)
-            :"a"(`a`), "c"(`b`)
-            :"%edx"
-    """
-
-  proc modInt(a, b: int): int {.compilerProc, inline.} =
-    asm """  "xorl %%edx, %%edx\n"
-             "idivl %%ecx\n"
-             "jno 1\n"
-             "call _raiseOverflow\n"
-             "1: \n"
-             "movl %%edx, %%eax"
-            :"=a"(`result`)
-            :"a"(`a`), "c"(`b`)
-            :"%edx"
-    """
-
-when not declared(addInt) and defined(builtinOverflow):
-  proc addInt(a, b: int): int {.compilerProc, inline.} =
-    if addIntOverflow(a, b, result):
-      raiseOverflow()
-
-when not declared(subInt) and defined(builtinOverflow):
-  proc subInt(a, b: int): int {.compilerProc, inline.} =
-    if subIntOverflow(a, b, result):
-      raiseOverflow()
-
-when not declared(mulInt) and defined(builtinOverflow):
-  proc mulInt(a, b: int): int {.compilerProc, inline.} =
-    if mulIntOverflow(a, b, result):
-      raiseOverflow()
-
-# Platform independent versions of the above (slower!)
-when not declared(addInt):
-  proc addInt(a, b: int): int {.compilerProc, inline.} =
-    result = a +% b
-    if (result xor a) >= 0 or (result xor b) >= 0:
-      return result
-    raiseOverflow()
-
-when not declared(subInt):
-  proc subInt(a, b: int): int {.compilerProc, inline.} =
-    result = a -% b
-    if (result xor a) >= 0 or (result xor not b) >= 0:
-      return result
-    raiseOverflow()
-
-when not declared(negInt):
-  proc negInt(a: int): int {.compilerProc, inline.} =
-    if a != low(int): return -a
-    raiseOverflow()
-
-when not declared(divInt):
-  proc divInt(a, b: int): int {.compilerProc, inline.} =
-    if b == 0:
-      raiseDivByZero()
-    if a == low(int) and b == -1:
-      raiseOverflow()
-    return a div b
-
-when not declared(modInt):
-  proc modInt(a, b: int): int {.compilerProc, inline.} =
-    if b == 0:
-      raiseDivByZero()
-    return a mod b
-
-when not declared(mulInt):
-  #
-  # This code has been inspired by Python's source code.
-  # The native int product x*y is either exactly right or *way* off, being
-  # just the last n bits of the true product, where n is the number of bits
-  # in an int (the delivered product is the true product plus i*2**n for
-  # some integer i).
-  #
-  # The native float64 product x*y is subject to three
-  # rounding errors: on a sizeof(int)==8 box, each cast to double can lose
-  # info, and even on a sizeof(int)==4 box, the multiplication can lose info.
-  # But, unlike the native int product, it's not in *range* trouble:  even
-  # if sizeof(int)==32 (256-bit ints), the product easily fits in the
-  # dynamic range of a float64. So the leading 50 (or so) bits of the float64
-  # product are correct.
-  #
-  # We check these two ways against each other, and declare victory if
-  # they're approximately the same. Else, because the native int product is
-  # the only one that can lose catastrophic amounts of information, it's the
-  # native int product that must have overflowed.
-  #
-  proc mulInt(a, b: int): int {.compilerProc.} =
-    var
-      resAsFloat, floatProd: float
-
-    result = a *% b
-    floatProd = toFloat(a) * toFloat(b)
-    resAsFloat = toFloat(result)
-
-    # Fast path for normal case: small multiplicands, and no info
-    # is lost in either method.
-    if resAsFloat == floatProd: return result
-
-    # Somebody somewhere lost info. Close enough, or way off? Note
-    # that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
-    # The difference either is or isn't significant compared to the
-    # true value (of which floatProd is a good approximation).
-
-    # abs(diff)/abs(prod) <= 1/32 iff
-    #   32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
-    if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
-      return result
-    raiseOverflow()
-
-# We avoid setting the FPU control word here for compatibility with libraries
-# written in other languages.
-
-proc raiseFloatInvalidOp {.noinline.} =
-  sysFatal(FloatInvalidOpError, "FPU operation caused a NaN result")
-
-proc nanCheck(x: float64) {.compilerProc, inline.} =
-  if x != x: raiseFloatInvalidOp()
-
-proc raiseFloatOverflow(x: float64) {.noinline.} =
-  if x > 0.0:
-    sysFatal(FloatOverflowError, "FPU operation caused an overflow")
-  else:
-    sysFatal(FloatUnderflowError, "FPU operations caused an underflow")
-
-proc infCheck(x: float64) {.compilerProc, inline.} =
-  if x != 0.0 and x*0.5 == x: raiseFloatOverflow(x)
diff --git a/lib/system/arithmetics.nim b/lib/system/arithmetics.nim
new file mode 100644
index 000000000..e229a0f4b
--- /dev/null
+++ b/lib/system/arithmetics.nim
@@ -0,0 +1,405 @@
+proc succ*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Succ", noSideEffect.} =
+  ## Returns the `y`-th successor (default: 1) of the value `x`.
+  ##
+  ## If such a value does not exist, `OverflowDefect` is raised
+  ## or a compile time error occurs.
+  runnableExamples:
+    assert succ(5) == 6
+    assert succ(5, 3) == 8
+
+proc pred*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Pred", noSideEffect.} =
+  ## Returns the `y`-th predecessor (default: 1) of the value `x`.
+  ##
+  ## If such a value does not exist, `OverflowDefect` is raised
+  ## or a compile time error occurs.
+  runnableExamples:
+    assert pred(5) == 4
+    assert pred(5, 3) == 2
+
+proc inc*[T, V: Ordinal](x: var T, y: V = 1) {.magic: "Inc", noSideEffect.} =
+  ## Increments the ordinal `x` by `y`.
+  ##
+  ## If such a value does not exist, `OverflowDefect` is raised or a compile
+  ## time error occurs. This is a short notation for: `x = succ(x, y)`.
+  runnableExamples:
+    var i = 2
+    inc(i)
+    assert i == 3
+    inc(i, 3)
+    assert i == 6
+
+proc dec*[T, V: Ordinal](x: var T, y: V = 1) {.magic: "Dec", noSideEffect.} =
+  ## Decrements the ordinal `x` by `y`.
+  ##
+  ## If such a value does not exist, `OverflowDefect` is raised or a compile
+  ## time error occurs. This is a short notation for: `x = pred(x, y)`.
+  runnableExamples:
+    var i = 2
+    dec(i)
+    assert i == 1
+    dec(i, 3)
+    assert i == -2
+
+
+
+# --------------------------------------------------------------------------
+# built-in operators
+
+# integer calculations:
+proc `+`*(x: int): int {.magic: "UnaryPlusI", noSideEffect.}
+  ## Unary `+` operator for an integer. Has no effect.
+proc `+`*(x: int8): int8 {.magic: "UnaryPlusI", noSideEffect.}
+proc `+`*(x: int16): int16 {.magic: "UnaryPlusI", noSideEffect.}
+proc `+`*(x: int32): int32 {.magic: "UnaryPlusI", noSideEffect.}
+proc `+`*(x: int64): int64 {.magic: "UnaryPlusI", noSideEffect.}
+
+proc `-`*(x: int): int {.magic: "UnaryMinusI", noSideEffect.}
+  ## Unary `-` operator for an integer. Negates `x`.
+proc `-`*(x: int8): int8 {.magic: "UnaryMinusI", noSideEffect.}
+proc `-`*(x: int16): int16 {.magic: "UnaryMinusI", noSideEffect.}
+proc `-`*(x: int32): int32 {.magic: "UnaryMinusI", noSideEffect.}
+proc `-`*(x: int64): int64 {.magic: "UnaryMinusI64", noSideEffect.}
+
+proc `not`*(x: int): int {.magic: "BitnotI", noSideEffect.} =
+  ## Computes the `bitwise complement` of the integer `x`.
+  runnableExamples:
+    assert not 0'u8 == 255
+    assert not 0'i8 == -1
+    assert not 1000'u16 == 64535
+    assert not 1000'i16 == -1001
+proc `not`*(x: int8): int8 {.magic: "BitnotI", noSideEffect.}
+proc `not`*(x: int16): int16 {.magic: "BitnotI", noSideEffect.}
+proc `not`*(x: int32): int32 {.magic: "BitnotI", noSideEffect.}
+proc `not`*(x: int64): int64 {.magic: "BitnotI", noSideEffect.}
+
+proc `+`*(x, y: int): int {.magic: "AddI", noSideEffect.}
+  ## Binary `+` operator for an integer.
+proc `+`*(x, y: int8): int8 {.magic: "AddI", noSideEffect.}
+proc `+`*(x, y: int16): int16 {.magic: "AddI", noSideEffect.}
+proc `+`*(x, y: int32): int32 {.magic: "AddI", noSideEffect.}
+proc `+`*(x, y: int64): int64 {.magic: "AddI", noSideEffect.}
+
+proc `-`*(x, y: int): int {.magic: "SubI", noSideEffect.}
+  ## Binary `-` operator for an integer.
+proc `-`*(x, y: int8): int8 {.magic: "SubI", noSideEffect.}
+proc `-`*(x, y: int16): int16 {.magic: "SubI", noSideEffect.}
+proc `-`*(x, y: int32): int32 {.magic: "SubI", noSideEffect.}
+proc `-`*(x, y: int64): int64 {.magic: "SubI", noSideEffect.}
+
+proc `*`*(x, y: int): int {.magic: "MulI", noSideEffect.}
+  ## Binary `*` operator for an integer.
+proc `*`*(x, y: int8): int8 {.magic: "MulI", noSideEffect.}
+proc `*`*(x, y: int16): int16 {.magic: "MulI", noSideEffect.}
+proc `*`*(x, y: int32): int32 {.magic: "MulI", noSideEffect.}
+proc `*`*(x, y: int64): int64 {.magic: "MulI", noSideEffect.}
+
+proc `div`*(x, y: int): int {.magic: "DivI", noSideEffect.} =
+  ## Computes the integer division.
+  ##
+  ## This is roughly the same as `math.trunc(x/y).int`.
+  runnableExamples:
+    assert (1 div 2) == 0
+    assert (2 div 2) == 1
+    assert (3 div 2) == 1
+    assert (7 div 3) == 2
+    assert (-7 div 3) == -2
+    assert (7 div -3) == -2
+    assert (-7 div -3) == 2
+proc `div`*(x, y: int8): int8 {.magic: "DivI", noSideEffect.}
+proc `div`*(x, y: int16): int16 {.magic: "DivI", noSideEffect.}
+proc `div`*(x, y: int32): int32 {.magic: "DivI", noSideEffect.}
+proc `div`*(x, y: int64): int64 {.magic: "DivI", noSideEffect.}
+
+proc `mod`*(x, y: int): int {.magic: "ModI", noSideEffect.} =
+  ## Computes the integer modulo operation (remainder).
+  ##
+  ## This is the same as `x - (x div y) * y`.
+  runnableExamples:
+    assert (7 mod 5) == 2
+    assert (-7 mod 5) == -2
+    assert (7 mod -5) == 2
+    assert (-7 mod -5) == -2
+proc `mod`*(x, y: int8): int8 {.magic: "ModI", noSideEffect.}
+proc `mod`*(x, y: int16): int16 {.magic: "ModI", noSideEffect.}
+proc `mod`*(x, y: int32): int32 {.magic: "ModI", noSideEffect.}
+proc `mod`*(x, y: int64): int64 {.magic: "ModI", noSideEffect.}
+
+when defined(nimOldShiftRight):
+  const shrDepMessage = "`shr` will become sign preserving."
+  proc `shr`*(x: int, y: SomeInteger): int {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
+  proc `shr`*(x: int8, y: SomeInteger): int8 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
+  proc `shr`*(x: int16, y: SomeInteger): int16 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
+  proc `shr`*(x: int32, y: SomeInteger): int32 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
+  proc `shr`*(x: int64, y: SomeInteger): int64 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
+else:
+  proc `shr`*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.} =
+    ## Computes the `shift right` operation of `x` and `y`, filling
+    ## vacant bit positions with the sign bit.
+    ##
+    ## **Note**: `Operator precedence <manual.html#syntax-precedence>`_
+    ## is different than in *C*.
+    ##
+    ## See also:
+    ## * `ashr func<#ashr,int,SomeInteger>`_ for arithmetic shift right
+    runnableExamples:
+      assert 0b0001_0000'i8 shr 2 == 0b0000_0100'i8
+      assert 0b0000_0001'i8 shr 1 == 0b0000_0000'i8
+      assert 0b1000_0000'i8 shr 4 == 0b1111_1000'i8
+      assert -1 shr 5 == -1
+      assert 1 shr 5 == 0
+      assert 16 shr 2 == 4
+      assert -16 shr 2 == -4
+  proc `shr`*(x: int8, y: SomeInteger): int8 {.magic: "AshrI", noSideEffect.}
+  proc `shr`*(x: int16, y: SomeInteger): int16 {.magic: "AshrI", noSideEffect.}
+  proc `shr`*(x: int32, y: SomeInteger): int32 {.magic: "AshrI", noSideEffect.}
+  proc `shr`*(x: int64, y: SomeInteger): int64 {.magic: "AshrI", noSideEffect.}
+
+
+proc `shl`*(x: int, y: SomeInteger): int {.magic: "ShlI", noSideEffect.} =
+  ## Computes the `shift left` operation of `x` and `y`.
+  ##
+  ## **Note**: `Operator precedence <manual.html#syntax-precedence>`_
+  ## is different than in *C*.
+  runnableExamples:
+    assert 1'i32 shl 4 == 0x0000_0010
+    assert 1'i64 shl 4 == 0x0000_0000_0000_0010
+proc `shl`*(x: int8, y: SomeInteger): int8 {.magic: "ShlI", noSideEffect.}
+proc `shl`*(x: int16, y: SomeInteger): int16 {.magic: "ShlI", noSideEffect.}
+proc `shl`*(x: int32, y: SomeInteger): int32 {.magic: "ShlI", noSideEffect.}
+proc `shl`*(x: int64, y: SomeInteger): int64 {.magic: "ShlI", noSideEffect.}
+
+proc ashr*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.} =
+  ## Shifts right by pushing copies of the leftmost bit in from the left,
+  ## and let the rightmost bits fall off.
+  ##
+  ## Note that `ashr` is not an operator so use the normal function
+  ## call syntax for it.
+  ##
+  ## See also:
+  ## * `shr func<#shr,int,SomeInteger>`_
+  runnableExamples:
+    assert ashr(0b0001_0000'i8, 2) == 0b0000_0100'i8
+    assert ashr(0b1000_0000'i8, 8) == 0b1111_1111'i8
+    assert ashr(0b1000_0000'i8, 1) == 0b1100_0000'i8
+proc ashr*(x: int8, y: SomeInteger): int8 {.magic: "AshrI", noSideEffect.}
+proc ashr*(x: int16, y: SomeInteger): int16 {.magic: "AshrI", noSideEffect.}
+proc ashr*(x: int32, y: SomeInteger): int32 {.magic: "AshrI", noSideEffect.}
+proc ashr*(x: int64, y: SomeInteger): int64 {.magic: "AshrI", noSideEffect.}
+
+proc `and`*(x, y: int): int {.magic: "BitandI", noSideEffect.} =
+  ## Computes the `bitwise and` of numbers `x` and `y`.
+  runnableExamples:
+    assert (0b0011 and 0b0101) == 0b0001
+    assert (0b0111 and 0b1100) == 0b0100
+proc `and`*(x, y: int8): int8 {.magic: "BitandI", noSideEffect.}
+proc `and`*(x, y: int16): int16 {.magic: "BitandI", noSideEffect.}
+proc `and`*(x, y: int32): int32 {.magic: "BitandI", noSideEffect.}
+proc `and`*(x, y: int64): int64 {.magic: "BitandI", noSideEffect.}
+
+proc `or`*(x, y: int): int {.magic: "BitorI", noSideEffect.} =
+  ## Computes the `bitwise or` of numbers `x` and `y`.
+  runnableExamples:
+    assert (0b0011 or 0b0101) == 0b0111
+    assert (0b0111 or 0b1100) == 0b1111
+proc `or`*(x, y: int8): int8 {.magic: "BitorI", noSideEffect.}
+proc `or`*(x, y: int16): int16 {.magic: "BitorI", noSideEffect.}
+proc `or`*(x, y: int32): int32 {.magic: "BitorI", noSideEffect.}
+proc `or`*(x, y: int64): int64 {.magic: "BitorI", noSideEffect.}
+
+proc `xor`*(x, y: int): int {.magic: "BitxorI", noSideEffect.} =
+  ## Computes the `bitwise xor` of numbers `x` and `y`.
+  runnableExamples:
+    assert (0b0011 xor 0b0101) == 0b0110
+    assert (0b0111 xor 0b1100) == 0b1011
+proc `xor`*(x, y: int8): int8 {.magic: "BitxorI", noSideEffect.}
+proc `xor`*(x, y: int16): int16 {.magic: "BitxorI", noSideEffect.}
+proc `xor`*(x, y: int32): int32 {.magic: "BitxorI", noSideEffect.}
+proc `xor`*(x, y: int64): int64 {.magic: "BitxorI", noSideEffect.}
+
+# unsigned integer operations:
+proc `not`*(x: uint): uint {.magic: "BitnotI", noSideEffect.}
+  ## Computes the `bitwise complement` of the integer `x`.
+proc `not`*(x: uint8): uint8 {.magic: "BitnotI", noSideEffect.}
+proc `not`*(x: uint16): uint16 {.magic: "BitnotI", noSideEffect.}
+proc `not`*(x: uint32): uint32 {.magic: "BitnotI", noSideEffect.}
+proc `not`*(x: uint64): uint64 {.magic: "BitnotI", noSideEffect.}
+
+proc `shr`*(x: uint, y: SomeInteger): uint {.magic: "ShrI", noSideEffect.}
+  ## Computes the `shift right` operation of `x` and `y`.
+proc `shr`*(x: uint8, y: SomeInteger): uint8 {.magic: "ShrI", noSideEffect.}
+proc `shr`*(x: uint16, y: SomeInteger): uint16 {.magic: "ShrI", noSideEffect.}
+proc `shr`*(x: uint32, y: SomeInteger): uint32 {.magic: "ShrI", noSideEffect.}
+proc `shr`*(x: uint64, y: SomeInteger): uint64 {.magic: "ShrI", noSideEffect.}
+
+proc `shl`*(x: uint, y: SomeInteger): uint {.magic: "ShlI", noSideEffect.}
+  ## Computes the `shift left` operation of `x` and `y`.
+proc `shl`*(x: uint8, y: SomeInteger): uint8 {.magic: "ShlI", noSideEffect.}
+proc `shl`*(x: uint16, y: SomeInteger): uint16 {.magic: "ShlI", noSideEffect.}
+proc `shl`*(x: uint32, y: SomeInteger): uint32 {.magic: "ShlI", noSideEffect.}
+proc `shl`*(x: uint64, y: SomeInteger): uint64 {.magic: "ShlI", noSideEffect.}
+
+proc `and`*(x, y: uint): uint {.magic: "BitandI", noSideEffect.}
+  ## Computes the `bitwise and` of numbers `x` and `y`.
+proc `and`*(x, y: uint8): uint8 {.magic: "BitandI", noSideEffect.}
+proc `and`*(x, y: uint16): uint16 {.magic: "BitandI", noSideEffect.}
+proc `and`*(x, y: uint32): uint32 {.magic: "BitandI", noSideEffect.}
+proc `and`*(x, y: uint64): uint64 {.magic: "BitandI", noSideEffect.}
+
+proc `or`*(x, y: uint): uint {.magic: "BitorI", noSideEffect.}
+  ## Computes the `bitwise or` of numbers `x` and `y`.
+proc `or`*(x, y: uint8): uint8 {.magic: "BitorI", noSideEffect.}
+proc `or`*(x, y: uint16): uint16 {.magic: "BitorI", noSideEffect.}
+proc `or`*(x, y: uint32): uint32 {.magic: "BitorI", noSideEffect.}
+proc `or`*(x, y: uint64): uint64 {.magic: "BitorI", noSideEffect.}
+
+proc `xor`*(x, y: uint): uint {.magic: "BitxorI", noSideEffect.}
+  ## Computes the `bitwise xor` of numbers `x` and `y`.
+proc `xor`*(x, y: uint8): uint8 {.magic: "BitxorI", noSideEffect.}
+proc `xor`*(x, y: uint16): uint16 {.magic: "BitxorI", noSideEffect.}
+proc `xor`*(x, y: uint32): uint32 {.magic: "BitxorI", noSideEffect.}
+proc `xor`*(x, y: uint64): uint64 {.magic: "BitxorI", noSideEffect.}
+
+proc `+`*(x, y: uint): uint {.magic: "AddU", noSideEffect.}
+  ## Binary `+` operator for unsigned integers.
+proc `+`*(x, y: uint8): uint8 {.magic: "AddU", noSideEffect.}
+proc `+`*(x, y: uint16): uint16 {.magic: "AddU", noSideEffect.}
+proc `+`*(x, y: uint32): uint32 {.magic: "AddU", noSideEffect.}
+proc `+`*(x, y: uint64): uint64 {.magic: "AddU", noSideEffect.}
+
+proc `-`*(x, y: uint): uint {.magic: "SubU", noSideEffect.}
+  ## Binary `-` operator for unsigned integers.
+proc `-`*(x, y: uint8): uint8 {.magic: "SubU", noSideEffect.}
+proc `-`*(x, y: uint16): uint16 {.magic: "SubU", noSideEffect.}
+proc `-`*(x, y: uint32): uint32 {.magic: "SubU", noSideEffect.}
+proc `-`*(x, y: uint64): uint64 {.magic: "SubU", noSideEffect.}
+
+proc `*`*(x, y: uint): uint {.magic: "MulU", noSideEffect.}
+  ## Binary `*` operator for unsigned integers.
+proc `*`*(x, y: uint8): uint8 {.magic: "MulU", noSideEffect.}
+proc `*`*(x, y: uint16): uint16 {.magic: "MulU", noSideEffect.}
+proc `*`*(x, y: uint32): uint32 {.magic: "MulU", noSideEffect.}
+proc `*`*(x, y: uint64): uint64 {.magic: "MulU", noSideEffect.}
+
+proc `div`*(x, y: uint): uint {.magic: "DivU", noSideEffect.}
+  ## Computes the integer division for unsigned integers.
+  ## This is roughly the same as `trunc(x/y)`.
+proc `div`*(x, y: uint8): uint8 {.magic: "DivU", noSideEffect.}
+proc `div`*(x, y: uint16): uint16 {.magic: "DivU", noSideEffect.}
+proc `div`*(x, y: uint32): uint32 {.magic: "DivU", noSideEffect.}
+proc `div`*(x, y: uint64): uint64 {.magic: "DivU", noSideEffect.}
+
+proc `mod`*(x, y: uint): uint {.magic: "ModU", noSideEffect.}
+  ## Computes the integer modulo operation (remainder) for unsigned integers.
+  ## This is the same as `x - (x div y) * y`.
+proc `mod`*(x, y: uint8): uint8 {.magic: "ModU", noSideEffect.}
+proc `mod`*(x, y: uint16): uint16 {.magic: "ModU", noSideEffect.}
+proc `mod`*(x, y: uint32): uint32 {.magic: "ModU", noSideEffect.}
+proc `mod`*(x, y: uint64): uint64 {.magic: "ModU", noSideEffect.}
+
+proc `+=`*[T: SomeInteger](x: var T, y: T) {.
+  magic: "Inc", noSideEffect.}
+  ## Increments an integer.
+
+proc `-=`*[T: SomeInteger](x: var T, y: T) {.
+  magic: "Dec", noSideEffect.}
+  ## Decrements an integer.
+
+proc `*=`*[T: SomeInteger](x: var T, y: T) {.
+  inline, noSideEffect.} =
+  ## Binary `*=` operator for integers.
+  x = x * y
+
+# floating point operations:
+proc `+`*(x: float32): float32 {.magic: "UnaryPlusF64", noSideEffect.}
+proc `-`*(x: float32): float32 {.magic: "UnaryMinusF64", noSideEffect.}
+proc `+`*(x, y: float32): float32 {.magic: "AddF64", noSideEffect.}
+proc `-`*(x, y: float32): float32 {.magic: "SubF64", noSideEffect.}
+proc `*`*(x, y: float32): float32 {.magic: "MulF64", noSideEffect.}
+proc `/`*(x, y: float32): float32 {.magic: "DivF64", noSideEffect.}
+
+proc `+`*(x: float): float {.magic: "UnaryPlusF64", noSideEffect.}
+proc `-`*(x: float): float {.magic: "UnaryMinusF64", noSideEffect.}
+proc `+`*(x, y: float): float {.magic: "AddF64", noSideEffect.}
+proc `-`*(x, y: float): float {.magic: "SubF64", noSideEffect.}
+proc `*`*(x, y: float): float {.magic: "MulF64", noSideEffect.}
+proc `/`*(x, y: float): float {.magic: "DivF64", noSideEffect.}
+
+proc `+=`*[T: float|float32|float64] (x: var T, y: T) {.
+  inline, noSideEffect.} =
+  ## Increments in place a floating point number.
+  x = x + y
+
+proc `-=`*[T: float|float32|float64] (x: var T, y: T) {.
+  inline, noSideEffect.} =
+  ## Decrements in place a floating point number.
+  x = x - y
+
+proc `*=`*[T: float|float32|float64] (x: var T, y: T) {.
+  inline, noSideEffect.} =
+  ## Multiplies in place a floating point number.
+  x = x * y
+
+proc `/=`*(x: var float64, y: float64) {.inline, noSideEffect.} =
+  ## Divides in place a floating point number.
+  x = x / y
+
+proc `/=`*[T: float|float32](x: var T, y: T) {.inline, noSideEffect.} =
+  ## Divides in place a floating point number.
+  x = x / y
+
+# the following have to be included in system, not imported for some reason:
+
+proc `+%`*(x, y: int): int {.inline.} =
+  ## Treats `x` and `y` as unsigned and adds them.
+  ##
+  ## The result is truncated to fit into the result.
+  ## This implements modulo arithmetic. No overflow errors are possible.
+  cast[int](cast[uint](x) + cast[uint](y))
+proc `+%`*(x, y: int8): int8 {.inline.}   = cast[int8](cast[uint8](x) + cast[uint8](y))
+proc `+%`*(x, y: int16): int16 {.inline.} = cast[int16](cast[uint16](x) + cast[uint16](y))
+proc `+%`*(x, y: int32): int32 {.inline.} = cast[int32](cast[uint32](x) + cast[uint32](y))
+proc `+%`*(x, y: int64): int64 {.inline.} = cast[int64](cast[uint64](x) + cast[uint64](y))
+
+proc `-%`*(x, y: int): int {.inline.} =
+  ## Treats `x` and `y` as unsigned and subtracts them.
+  ##
+  ## The result is truncated to fit into the result.
+  ## This implements modulo arithmetic. No overflow errors are possible.
+  cast[int](cast[uint](x) - cast[uint](y))
+proc `-%`*(x, y: int8): int8 {.inline.}   = cast[int8](cast[uint8](x) - cast[uint8](y))
+proc `-%`*(x, y: int16): int16 {.inline.} = cast[int16](cast[uint16](x) - cast[uint16](y))
+proc `-%`*(x, y: int32): int32 {.inline.} = cast[int32](cast[uint32](x) - cast[uint32](y))
+proc `-%`*(x, y: int64): int64 {.inline.} = cast[int64](cast[uint64](x) - cast[uint64](y))
+
+proc `*%`*(x, y: int): int {.inline.} =
+  ## Treats `x` and `y` as unsigned and multiplies them.
+  ##
+  ## The result is truncated to fit into the result.
+  ## This implements modulo arithmetic. No overflow errors are possible.
+  cast[int](cast[uint](x) * cast[uint](y))
+proc `*%`*(x, y: int8): int8 {.inline.}   = cast[int8](cast[uint8](x) * cast[uint8](y))
+proc `*%`*(x, y: int16): int16 {.inline.} = cast[int16](cast[uint16](x) * cast[uint16](y))
+proc `*%`*(x, y: int32): int32 {.inline.} = cast[int32](cast[uint32](x) * cast[uint32](y))
+proc `*%`*(x, y: int64): int64 {.inline.} = cast[int64](cast[uint64](x) * cast[uint64](y))
+
+proc `/%`*(x, y: int): int {.inline.} =
+  ## Treats `x` and `y` as unsigned and divides them.
+  ##
+  ## The result is truncated to fit into the result.
+  ## This implements modulo arithmetic. No overflow errors are possible.
+  cast[int](cast[uint](x) div cast[uint](y))
+proc `/%`*(x, y: int8): int8 {.inline.}   = cast[int8](cast[uint8](x) div cast[uint8](y))
+proc `/%`*(x, y: int16): int16 {.inline.} = cast[int16](cast[uint16](x) div cast[uint16](y))
+proc `/%`*(x, y: int32): int32 {.inline.} = cast[int32](cast[uint32](x) div cast[uint32](y))
+proc `/%`*(x, y: int64): int64 {.inline.} = cast[int64](cast[uint64](x) div cast[uint64](y))
+
+proc `%%`*(x, y: int): int {.inline.} =
+  ## Treats `x` and `y` as unsigned and compute the modulo of `x` and `y`.
+  ##
+  ## The result is truncated to fit into the result.
+  ## This implements modulo arithmetic. No overflow errors are possible.
+  cast[int](cast[uint](x) mod cast[uint](y))
+proc `%%`*(x, y: int8): int8 {.inline.}   = cast[int8](cast[uint8](x) mod cast[uint8](y))
+proc `%%`*(x, y: int16): int16 {.inline.} = cast[int16](cast[uint16](x) mod cast[uint16](y))
+proc `%%`*(x, y: int32): int32 {.inline.} = cast[int32](cast[uint32](x) mod cast[uint32](y))
+proc `%%`*(x, y: int64): int64 {.inline.} = cast[int64](cast[uint64](x) mod cast[uint64](y))
diff --git a/lib/system/assign.nim b/lib/system/assign.nim
index 16b56aba7..9f4cbc0fe 100644
--- a/lib/system/assign.nim
+++ b/lib/system/assign.nim
@@ -7,14 +7,16 @@
 #    distribution, for details about the copyright.
 #
 
+include seqs_v2_reimpl
+
 proc genericResetAux(dest: pointer, n: ptr TNimNode) {.benign.}
 
 proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) {.benign.}
 proc genericAssignAux(dest, src: pointer, n: ptr TNimNode,
                       shallow: bool) {.benign.} =
   var
-    d = cast[ByteAddress](dest)
-    s = cast[ByteAddress](src)
+    d = cast[int](dest)
+    s = cast[int](src)
   case n.kind
   of nkSlot:
     genericAssignAux(cast[pointer](d +% n.offset),
@@ -38,46 +40,67 @@ proc genericAssignAux(dest, src: pointer, n: ptr TNimNode,
   #  echo "ugh memory corruption! ", n.kind
   #  quit 1
 
+template deepSeqAssignImpl(operation, additionalArg) {.dirty.} =
+  var d = cast[ptr NimSeqV2Reimpl](dest)
+  var s = cast[ptr NimSeqV2Reimpl](src)
+  d.len = s.len
+  let elem = mt.base
+  d.p = cast[ptr NimSeqPayloadReimpl](newSeqPayload(s.len, elem.size, elem.align))
+
+  let bs = elem.size
+  let ba = elem.align
+  let headerSize = align(sizeof(NimSeqPayloadBase), ba)
+
+  for i in 0..d.len-1:
+    operation(d.p +! (headerSize+i*bs), s.p +! (headerSize+i*bs), mt.base, additionalArg)
+
 proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
   var
-    d = cast[ByteAddress](dest)
-    s = cast[ByteAddress](src)
+    d = cast[int](dest)
+    s = cast[int](src)
   sysAssert(mt != nil, "genericAssignAux 2")
   case mt.kind
   of tyString:
-    var x = cast[PPointer](dest)
-    var s2 = cast[PPointer](s)[]
-    if s2 == nil or shallow or (
-        cast[PGenericSeq](s2).reserved and seqShallowFlag) != 0:
-      unsureAsgnRef(x, s2)
+    when defined(nimSeqsV2):
+      var x = cast[ptr NimStringV2](dest)
+      var s2 = cast[ptr NimStringV2](s)[]
+      nimAsgnStrV2(x[], s2)
     else:
-      unsureAsgnRef(x, copyString(cast[NimString](s2)))
+      var x = cast[PPointer](dest)
+      var s2 = cast[PPointer](s)[]
+      if s2 == nil or shallow or (
+          cast[PGenericSeq](s2).reserved and seqShallowFlag) != 0:
+        unsureAsgnRef(x, s2)
+      else:
+        unsureAsgnRef(x, copyString(cast[NimString](s2)))
   of tySequence:
-    var s2 = cast[PPointer](src)[]
-    var seq = cast[PGenericSeq](s2)
-    var x = cast[PPointer](dest)
-    if s2 == nil or shallow or (seq.reserved and seqShallowFlag) != 0:
-      # this can happen! nil sequences are allowed
-      unsureAsgnRef(x, s2)
-      return
-    sysAssert(dest != nil, "genericAssignAux 3")
-    if ntfNoRefs in mt.base.flags:
-      var ss = nimNewSeqOfCap(mt, seq.len)
-      cast[PGenericSeq](ss).len = seq.len
-      unsureAsgnRef(x, ss)
-      var dst = cast[ByteAddress](cast[PPointer](dest)[])
-      copyMem(cast[pointer](dst +% GenericSeqSize),
-              cast[pointer](cast[ByteAddress](s2) +% GenericSeqSize),
-              seq.len * mt.base.size)
+    when defined(nimSeqsV2):
+      deepSeqAssignImpl(genericAssignAux, shallow)
     else:
-      unsureAsgnRef(x, newSeq(mt, seq.len))
-      var dst = cast[ByteAddress](cast[PPointer](dest)[])
-      for i in 0..seq.len-1:
-        genericAssignAux(
-          cast[pointer](dst +% i *% mt.base.size +% GenericSeqSize),
-          cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
-                      GenericSeqSize),
-          mt.base, shallow)
+      var s2 = cast[PPointer](src)[]
+      var seq = cast[PGenericSeq](s2)
+      var x = cast[PPointer](dest)
+      if s2 == nil or shallow or (seq.reserved and seqShallowFlag) != 0:
+        # this can happen! nil sequences are allowed
+        unsureAsgnRef(x, s2)
+        return
+      sysAssert(dest != nil, "genericAssignAux 3")
+      if ntfNoRefs in mt.base.flags:
+        var ss = nimNewSeqOfCap(mt, seq.len)
+        cast[PGenericSeq](ss).len = seq.len
+        unsureAsgnRef(x, ss)
+        var dst = cast[int](cast[PPointer](dest)[])
+        copyMem(cast[pointer](dst +% align(GenericSeqSize, mt.base.align)),
+                cast[pointer](cast[int](s2) +% align(GenericSeqSize, mt.base.align)),
+                seq.len *% mt.base.size)
+      else:
+        unsureAsgnRef(x, newSeq(mt, seq.len))
+        var dst = cast[int](cast[PPointer](dest)[])
+        for i in 0..seq.len-1:
+          genericAssignAux(
+            cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size ),
+            cast[pointer](cast[int](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size ),
+            mt.base, shallow)
   of tyObject:
     var it = mt.base
     # don't use recursion here on the PNimType because the subtype
@@ -88,14 +111,23 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
     genericAssignAux(dest, src, mt.node, shallow)
     # we need to copy m_type field for tyObject, as it could be empty for
     # sequence reallocations:
-    var pint = cast[ptr PNimType](dest)
-    # We need to copy the *static* type not the dynamic type:
-    #   if p of TB:
-    #     var tbObj = TB(p)
-    #     tbObj of TC # needs to be false!
-    #c_fprintf(stdout, "%s %s\n", pint[].name, mt.name)
-    chckObjAsgn(cast[ptr PNimType](src)[], mt)
-    pint[] = mt # cast[ptr PNimType](src)[]
+    when defined(nimSeqsV2):
+      var pint = cast[ptr PNimTypeV2](dest)
+      #chckObjAsgn(cast[ptr PNimTypeV2](src)[].typeInfoV2, mt)
+      pint[] = cast[PNimTypeV2](mt.typeInfoV2)
+    else:
+      var pint = cast[ptr PNimType](dest)
+      # We need to copy the *static* type not the dynamic type:
+      #   if p of TB:
+      #     var tbObj = TB(p)
+      #     tbObj of TC # needs to be false!
+      #c_fprintf(stdout, "%s %s\n", pint[].name, mt.name)
+      let srcType = cast[ptr PNimType](src)[]
+      if srcType != nil:
+        # `!= nil` needed because of cases where object is not initialized properly (see bug #16706)
+        # note that you can have `srcType == nil` yet `src != nil`
+        chckObjAsgn(srcType, mt)
+      pint[] = mt # cast[ptr PNimType](src)[]
   of tyTuple:
     genericAssignAux(dest, src, mt.node, shallow)
   of tyArray, tyArrayConstr:
@@ -104,26 +136,13 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
                        cast[pointer](s +% i *% mt.base.size), mt.base, shallow)
   of tyRef:
     unsureAsgnRef(cast[PPointer](dest), cast[PPointer](s)[])
-  of tyOptAsRef:
-    let s2 = cast[PPointer](src)[]
-    let d = cast[PPointer](dest)
-    if s2 == nil:
-      unsureAsgnRef(d, s2)
-    else:
-      when declared(usrToCell):
-        let realType = usrToCell(s2).typ
-      else:
-        let realType = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[]
-                       else: mt.base
-      var z = newObj(realType, realType.base.size)
-      genericAssignAux(d, addr z, mt.base, shallow)
   else:
     copyMem(dest, src, mt.size) # copy raw bits
 
-proc genericAssign(dest, src: pointer, mt: PNimType) {.compilerProc.} =
+proc genericAssign(dest, src: pointer, mt: PNimType) {.compilerproc.} =
   genericAssignAux(dest, src, mt, false)
 
-proc genericShallowAssign(dest, src: pointer, mt: PNimType) {.compilerProc.} =
+proc genericShallowAssign(dest, src: pointer, mt: PNimType) {.compilerproc.} =
   genericAssignAux(dest, src, mt, true)
 
 when false:
@@ -143,35 +162,34 @@ when false:
     of tyPtr: k = "ptr"
     of tyRef: k = "ref"
     of tyVar: k = "var"
-    of tyOptAsRef: k = "optref"
     of tySequence: k = "seq"
     of tyProc: k = "proc"
     of tyPointer: k = "range"
     of tyOpenArray: k = "openarray"
     of tyString: k = "string"
-    of tyCString: k = "cstring"
+    of tyCstring: k = "cstring"
     of tyInt: k = "int"
     of tyInt32: k = "int32"
     else: k = "other"
     cprintf("%s %ld\n", k, t.size)
     debugNimType(t.base)
 
-proc genericSeqAssign(dest, src: pointer, mt: PNimType) {.compilerProc.} =
+proc genericSeqAssign(dest, src: pointer, mt: PNimType) {.compilerproc.} =
   var src = src # ugly, but I like to stress the parser sometimes :-)
   genericAssign(dest, addr(src), mt)
 
 proc genericAssignOpenArray(dest, src: pointer, len: int,
                             mt: PNimType) {.compilerproc.} =
   var
-    d = cast[ByteAddress](dest)
-    s = cast[ByteAddress](src)
+    d = cast[int](dest)
+    s = cast[int](src)
   for i in 0..len-1:
     genericAssign(cast[pointer](d +% i *% mt.base.size),
                   cast[pointer](s +% i *% mt.base.size), mt.base)
 
-proc objectInit(dest: pointer, typ: PNimType) {.compilerProc, benign.}
+proc objectInit(dest: pointer, typ: PNimType) {.compilerproc, benign.}
 proc objectInitAux(dest: pointer, n: ptr TNimNode) {.benign.} =
-  var d = cast[ByteAddress](dest)
+  var d = cast[int](dest)
   case n.kind
   of nkNone: sysAssert(false, "objectInitAux")
   of nkSlot: objectInit(cast[pointer](d +% n.offset), n.typ)
@@ -185,13 +203,17 @@ proc objectInitAux(dest: pointer, n: ptr TNimNode) {.benign.} =
 proc objectInit(dest: pointer, typ: PNimType) =
   # the generic init proc that takes care of initialization of complex
   # objects on the stack or heap
-  var d = cast[ByteAddress](dest)
+  var d = cast[int](dest)
   case typ.kind
   of tyObject:
     # iterate over any structural type
     # here we have to init the type field:
-    var pint = cast[ptr PNimType](dest)
-    pint[] = typ
+    when defined(nimSeqsV2):
+      var pint = cast[ptr PNimTypeV2](dest)
+      pint[] = cast[PNimTypeV2](typ.typeInfoV2)
+    else:
+      var pint = cast[ptr PNimType](dest)
+      pint[] = typ
     objectInitAux(dest, typ.node)
   of tyTuple:
     objectInitAux(dest, typ.node)
@@ -202,14 +224,9 @@ proc objectInit(dest: pointer, typ: PNimType) =
 
 # ---------------------- assign zero -----------------------------------------
 
-proc nimDestroyRange[T](r: T) {.compilerProc.} =
-  # internal proc used for destroying sequences and arrays
-  mixin `=destroy`
-  for i in countup(0, r.len - 1): `=destroy`(r[i])
-
-proc genericReset(dest: pointer, mt: PNimType) {.compilerProc, benign.}
+proc genericReset(dest: pointer, mt: PNimType) {.compilerproc, benign.}
 proc genericResetAux(dest: pointer, n: ptr TNimNode) =
-  var d = cast[ByteAddress](dest)
+  var d = cast[int](dest)
   case n.kind
   of nkNone: sysAssert(false, "genericResetAux")
   of nkSlot: genericReset(cast[pointer](d +% n.offset), n.typ)
@@ -221,18 +238,35 @@ proc genericResetAux(dest: pointer, n: ptr TNimNode) =
     zeroMem(cast[pointer](d +% n.offset), n.typ.size)
 
 proc genericReset(dest: pointer, mt: PNimType) =
-  var d = cast[ByteAddress](dest)
+  var d = cast[int](dest)
   sysAssert(mt != nil, "genericReset 2")
   case mt.kind
-  of tyString, tyRef, tyOptAsRef, tySequence:
+  of tyRef:
     unsureAsgnRef(cast[PPointer](dest), nil)
+  of tyString:
+    when defined(nimSeqsV2):
+      var s = cast[ptr NimStringV2](dest)
+      frees(s[])
+      zeroMem(dest, mt.size)
+    else:
+      unsureAsgnRef(cast[PPointer](dest), nil)
+  of tySequence:
+    when defined(nimSeqsV2):
+      frees(cast[ptr NimSeqV2Reimpl](dest)[])
+      zeroMem(dest, mt.size)
+    else:
+      unsureAsgnRef(cast[PPointer](dest), nil)
   of tyTuple:
     genericResetAux(dest, mt.node)
   of tyObject:
     genericResetAux(dest, mt.node)
     # also reset the type field for tyObject, for correct branch switching!
-    var pint = cast[ptr PNimType](dest)
-    pint[] = nil
+    when defined(nimSeqsV2):
+      var pint = cast[ptr PNimTypeV2](dest)
+      pint[] = nil
+    else:
+      var pint = cast[ptr PNimType](dest)
+      pint[] = nil
   of tyArray, tyArrayConstr:
     for i in 0..(mt.size div mt.base.size)-1:
       genericReset(cast[pointer](d +% i *% mt.base.size), mt.base)
@@ -241,15 +275,24 @@ proc genericReset(dest: pointer, mt: PNimType) =
 
 proc selectBranch(discVal, L: int,
                   a: ptr array[0x7fff, ptr TNimNode]): ptr TNimNode =
-  result = a[L] # a[L] contains the ``else`` part (but may be nil)
   if discVal <% L:
-    var x = a[discVal]
-    if x != nil: result = x
+    result = a[discVal]
+    if result == nil:
+      result = a[L]
+  else:
+    result = a[L] # a[L] contains the ``else`` part (but may be nil)
 
 proc FieldDiscriminantCheck(oldDiscVal, newDiscVal: int,
                             a: ptr array[0x7fff, ptr TNimNode],
-                            L: int) {.compilerProc.} =
-  var oldBranch = selectBranch(oldDiscVal, L, a)
-  var newBranch = selectBranch(newDiscVal, L, a)
-  if newBranch != oldBranch and oldDiscVal != 0:
-    sysFatal(FieldError, "assignment to discriminant changes object branch")
+                            L: int) {.compilerproc.} =
+  let oldBranch = selectBranch(oldDiscVal, L, a)
+  let newBranch = selectBranch(newDiscVal, L, a)
+  when defined(nimOldCaseObjects):
+    if newBranch != oldBranch and oldDiscVal != 0:
+      sysFatal(FieldDefect, "assignment to discriminant changes object branch")
+  else:
+    if newBranch != oldBranch:
+      if oldDiscVal != 0:
+        sysFatal(FieldDefect, "assignment to discriminant changes object branch")
+      else:
+        sysFatal(FieldDefect, "assignment to discriminant changes object branch; compile with -d:nimOldCaseObjects for a transition period")
diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim
deleted file mode 100644
index 56ebde823..000000000
--- a/lib/system/atomics.nim
+++ /dev/null
@@ -1,317 +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.
-#
-
-# Atomic operations for Nim.
-{.push stackTrace:off.}
-
-const someGcc = defined(gcc) or defined(llvm_gcc) or defined(clang)
-
-when someGcc and hasThreadSupport:
-  type AtomMemModel* = distinct cint
-
-  var ATOMIC_RELAXED* {.importc: "__ATOMIC_RELAXED", nodecl.}: AtomMemModel
-    ## No barriers or synchronization.
-  var ATOMIC_CONSUME* {.importc: "__ATOMIC_CONSUME", nodecl.}: AtomMemModel
-    ## Data dependency only for both barrier and
-    ## synchronization with another thread.
-  var ATOMIC_ACQUIRE* {.importc: "__ATOMIC_ACQUIRE", nodecl.}: AtomMemModel
-    ## Barrier to hoisting of code and synchronizes with
-    ## release (or stronger)
-    ## semantic stores from another thread.
-  var ATOMIC_RELEASE* {.importc: "__ATOMIC_RELEASE", nodecl.}: AtomMemModel
-    ## Barrier to sinking of code and synchronizes with
-    ## acquire (or stronger)
-    ## semantic loads from another thread.
-  var ATOMIC_ACQ_REL* {.importc: "__ATOMIC_ACQ_REL", nodecl.}: AtomMemModel
-    ## Full barrier in both directions and synchronizes
-    ## with acquire loads
-    ## and release stores in another thread.
-  var ATOMIC_SEQ_CST* {.importc: "__ATOMIC_SEQ_CST", nodecl.}: AtomMemModel
-    ## Full barrier in both directions and synchronizes
-    ## with acquire loads
-    ## and release stores in all threads.
-
-  type
-    AtomType* = SomeNumber|pointer|ptr|char|bool
-      ## Type Class representing valid types for use with atomic procs
-  {.deprecated: [TAtomType: AtomType].}
-
-  proc atomicLoadN*[T: AtomType](p: ptr T, mem: AtomMemModel): T {.
-    importc: "__atomic_load_n", nodecl.}
-    ## This proc implements an atomic load operation. It returns the contents at p.
-    ## ATOMIC_RELAXED, ATOMIC_SEQ_CST, ATOMIC_ACQUIRE, ATOMIC_CONSUME.
-
-  proc atomicLoad*[T: AtomType](p, ret: ptr T, mem: AtomMemModel) {.
-    importc: "__atomic_load", nodecl.}
-    ## This is the generic version of an atomic load. It returns the contents at p in ret.
-
-  proc atomicStoreN*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel) {.
-    importc: "__atomic_store_n", nodecl.}
-    ## This proc implements an atomic store operation. It writes val at p.
-    ## ATOMIC_RELAXED, ATOMIC_SEQ_CST, and ATOMIC_RELEASE.
-
-  proc atomicStore*[T: AtomType](p, val: ptr T, mem: AtomMemModel) {.
-    importc: "__atomic_store", nodecl.}
-    ## This is the generic version of an atomic store. It stores the value of val at p
-
-  proc atomicExchangeN*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
-    importc: "__atomic_exchange_n", nodecl.}
-    ## This proc implements an atomic exchange operation. It writes val at p,
-    ## and returns the previous contents at p.
-    ## ATOMIC_RELAXED, ATOMIC_SEQ_CST, ATOMIC_ACQUIRE, ATOMIC_RELEASE, ATOMIC_ACQ_REL
-
-  proc atomicExchange*[T: AtomType](p, val, ret: ptr T, mem: AtomMemModel) {.
-    importc: "__atomic_exchange", nodecl.}
-    ## This is the generic version of an atomic exchange. It stores the contents at val at p.
-    ## The original value at p is copied into ret.
-
-  proc atomicCompareExchangeN*[T: AtomType](p, expected: ptr T, desired: T,
-    weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool {.
-    importc: "__atomic_compare_exchange_n ", nodecl.}
-    ## This proc implements an atomic compare and exchange operation. This compares the
-    ## contents at p with the contents at expected and if equal, writes desired at p.
-    ## If they are not equal, the current contents at p is written into expected.
-    ## Weak is true for weak compare_exchange, and false for the strong variation.
-    ## Many targets only offer the strong variation and ignore the parameter.
-    ## When in doubt, use the strong variation.
-    ## True is returned if desired is written at p and the execution is considered
-    ## to conform to the memory model specified by success_memmodel. There are no
-    ## restrictions on what memory model can be used here. False is returned otherwise,
-    ## and the execution is considered to conform to failure_memmodel. This memory model
-    ## cannot be __ATOMIC_RELEASE nor __ATOMIC_ACQ_REL. It also cannot be a stronger model
-    ## than that specified by success_memmodel.
-
-  proc atomicCompareExchange*[T: AtomType](p, expected, desired: ptr T,
-    weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool {.
-    importc: "__atomic_compare_exchange", nodecl.}
-    ## This proc implements the generic version of atomic_compare_exchange.
-    ## The proc is virtually identical to atomic_compare_exchange_n, except the desired
-    ## value is also a pointer.
-
-  ## Perform the operation return the new value, all memory models are valid
-  proc atomicAddFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
-    importc: "__atomic_add_fetch", nodecl.}
-  proc atomicSubFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
-    importc: "__atomic_sub_fetch", nodecl.}
-  proc atomicOrFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
-    importc: "__atomic_or_fetch ", nodecl.}
-  proc atomicAndFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
-    importc: "__atomic_and_fetch", nodecl.}
-  proc atomicXorFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
-    importc: "__atomic_xor_fetch", nodecl.}
-  proc atomicNandFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
-    importc: "__atomic_nand_fetch ", nodecl.}
-
-  ## Perform the operation return the old value, all memory models are valid
-  proc atomicFetchAdd*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
-    importc: "__atomic_fetch_add", nodecl.}
-  proc atomicFetchSub*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
-    importc: "__atomic_fetch_sub", nodecl.}
-  proc atomicFetchOr*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
-    importc: "__atomic_fetch_or", nodecl.}
-  proc atomicFetchAnd*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
-    importc: "__atomic_fetch_and", nodecl.}
-  proc atomicFetchXor*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
-    importc: "__atomic_fetch_xor", nodecl.}
-  proc atomicFetchNand*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
-    importc: "__atomic_fetch_nand", nodecl.}
-
-  proc atomicTestAndSet*(p: pointer, mem: AtomMemModel): bool {.
-    importc: "__atomic_test_and_set", nodecl.}
-    ## This built-in function performs an atomic test-and-set operation on the byte at p.
-    ## The byte is set to some implementation defined nonzero “set” value and the return
-    ## value is true if and only if the previous contents were “set”.
-    ## All memory models are valid.
-
-  proc atomicClear*(p: pointer, mem: AtomMemModel) {.
-    importc: "__atomic_clear", nodecl.}
-    ## This built-in function performs an atomic clear operation at p.
-    ## After the operation, at p contains 0.
-    ## ATOMIC_RELAXED, ATOMIC_SEQ_CST, ATOMIC_RELEASE
-
-  proc atomicThreadFence*(mem: AtomMemModel) {.
-    importc: "__atomic_thread_fence", nodecl.}
-    ## This built-in function acts as a synchronization fence between threads based
-    ## on the specified memory model. All memory orders are valid.
-
-  proc atomicSignalFence*(mem: AtomMemModel) {.
-    importc: "__atomic_signal_fence", nodecl.}
-    ## This built-in function acts as a synchronization fence between a thread and
-    ## signal handlers based in the same thread. All memory orders are valid.
-
-  proc atomicAlwaysLockFree*(size: int, p: pointer): bool {.
-    importc: "__atomic_always_lock_free", nodecl.}
-    ## This built-in function returns true if objects of size bytes always generate
-    ## lock free atomic instructions for the target architecture. size must resolve
-    ## to a compile-time constant and the result also resolves to a compile-time constant.
-    ## ptr is an optional pointer to the object that may be used to determine alignment.
-    ## A value of 0 indicates typical alignment should be used. The compiler may also
-    ## ignore this parameter.
-
-  proc atomicIsLockFree*(size: int, p: pointer): bool {.
-    importc: "__atomic_is_lock_free", nodecl.}
-    ## This built-in function returns true if objects of size bytes always generate
-    ## lock free atomic instructions for the target architecture. If it is not known
-    ## to be lock free a call is made to a runtime routine named __atomic_is_lock_free.
-    ## ptr is an optional pointer to the object that may be used to determine alignment.
-    ## A value of 0 indicates typical alignment should be used. The compiler may also
-    ## ignore this parameter.
-
-  template fence*() = atomicThreadFence(ATOMIC_SEQ_CST)
-elif defined(vcc) and hasThreadSupport:
-  when defined(cpp):
-    when sizeof(int) == 8:
-      proc addAndFetch*(p: ptr int, val: int): int {.
-        importcpp: "_InterlockedExchangeAdd64(static_cast<NI volatile *>(#), #)",
-        header: "<intrin.h>".}
-    else:
-      proc addAndFetch*(p: ptr int, val: int): int {.
-        importcpp: "_InterlockedExchangeAdd(reinterpret_cast<LONG volatile *>(#), static_cast<LONG>(#))",
-        header: "<intrin.h>".}
-  else:
-    when sizeof(int) == 8:
-      proc addAndFetch*(p: ptr int, val: int): int {.
-        importc: "_InterlockedExchangeAdd64", header: "<intrin.h>".}
-    else:
-      proc addAndFetch*(p: ptr int, val: int): int {.
-        importc: "_InterlockedExchangeAdd", header: "<intrin.h>".}
-
-  proc fence*() {.importc: "_ReadWriteBarrier", header: "<intrin.h>".}
-
-else:
-  proc addAndFetch*(p: ptr int, val: int): int {.inline.} =
-    inc(p[], val)
-    result = p[]
-
-proc atomicInc*(memLoc: var int, x: int = 1): int =
-  when someGcc and hasThreadSupport:
-    result = atomic_add_fetch(memLoc.addr, x, ATOMIC_RELAXED)
-  elif defined(vcc) and hasThreadSupport:
-    result = addAndFetch(memLoc.addr, x)
-    inc(result, x)
-  else:
-    inc(memLoc, x)
-    result = memLoc
-
-proc atomicDec*(memLoc: var int, x: int = 1): int =
-  when someGcc and hasThreadSupport:
-    when declared(atomic_sub_fetch):
-      result = atomic_sub_fetch(memLoc.addr, x, ATOMIC_RELAXED)
-    else:
-      result = atomic_add_fetch(memLoc.addr, -x, ATOMIC_RELAXED)
-  elif defined(vcc) and hasThreadSupport:
-    result = addAndFetch(memLoc.addr, -x)
-    dec(result, x)
-  else:
-    dec(memLoc, x)
-    result = memLoc
-
-when defined(vcc):
-  when defined(cpp):
-    proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
-      {.importcpp: "_InterlockedCompareExchange64(static_cast<NI64 volatile *>(#), #, #)", header: "<intrin.h>".}
-    proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
-      {.importcpp: "_InterlockedCompareExchange(static_cast<NI volatile *>(#), #, #)", header: "<intrin.h>".}
-    proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
-      {.importcpp: "_InterlockedCompareExchange8(static_cast<char volatile *>(#), #, #)", header: "<intrin.h>".}
-  else:
-    proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
-      {.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".}
-    proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
-      {.importc: "_InterlockedCompareExchange", header: "<intrin.h>".}
-    proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
-      {.importc: "_InterlockedCompareExchange8", header: "<intrin.h>".}
-
-  proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool =
-    when sizeof(T) == 8:
-      interlockedCompareExchange64(p, cast[int64](newValue), cast[int64](oldValue)) ==
-        cast[int64](oldValue)
-    elif sizeof(T) == 4:
-      interlockedCompareExchange32(p, cast[int32](newValue), cast[int32](oldValue)) ==
-        cast[int32](oldValue)
-    elif sizeof(T) == 1:
-      interlockedCompareExchange8(p, cast[byte](newValue), cast[byte](oldValue)) ==
-        cast[byte](oldValue)
-    else:
-      {.error: "invalid CAS instruction".}
-
-elif defined(tcc):
-  when defined(amd64):
-    {.emit:"""
-static int __tcc_cas(int *ptr, int oldVal, int newVal)
-{
-    unsigned char ret;
-    __asm__ __volatile__ (
-            "  lock\n"
-            "  cmpxchgq %2,%1\n"
-            "  sete %0\n"
-            : "=q" (ret), "=m" (*ptr)
-            : "r" (newVal), "m" (*ptr), "a" (oldVal)
-            : "memory");
-
-    if (ret)
-      return 0;
-    else
-      return 1;
-}
-""".}
-  else:
-    #assert sizeof(int) == 4
-    {.emit:"""
-static int __tcc_cas(int *ptr, int oldVal, int newVal)
-{
-    unsigned char ret;
-    __asm__ __volatile__ (
-            "  lock\n"
-            "  cmpxchgl %2,%1\n"
-            "  sete %0\n"
-            : "=q" (ret), "=m" (*ptr)
-            : "r" (newVal), "m" (*ptr), "a" (oldVal)
-            : "memory");
-
-    if (ret)
-      return 0;
-    else
-      return 1;
-}
-""".}
-
-  proc tcc_cas(p: ptr int; oldValue, newValue: int): bool
-    {.importc: "__tcc_cas", nodecl.}
-  proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool =
-    tcc_cas(cast[ptr int](p), cast[int](oldValue), cast[int](newValue))
-else:
-  # this is valid for GCC and Intel C++
-  proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool
-    {.importc: "__sync_bool_compare_and_swap", nodecl.}
-  # XXX is this valid for 'int'?
-
-
-when (defined(x86) or defined(amd64)) and defined(vcc):
-  proc cpuRelax* {.importc: "YieldProcessor", header: "<windows.h>".}
-elif (defined(x86) or defined(amd64)) and (someGcc or defined(bcc)):
-  proc cpuRelax* {.inline.} =
-    {.emit: """asm volatile("pause" ::: "memory");""".}
-elif someGcc or defined(tcc):
-  proc cpuRelax* {.inline.} =
-    {.emit: """asm volatile("" ::: "memory");""".}
-elif defined(icl):
-  proc cpuRelax* {.importc: "_mm_pause", header: "xmmintrin.h".}
-elif false:
-  from os import sleep
-
-  proc cpuRelax* {.inline.} = os.sleep(1)
-
-when not declared(fence) and hasThreadSupport:
-  # XXX fixme
-  proc fence*() {.inline.} =
-    var dummy: bool
-    discard cas(addr dummy, false, true)
-
-{.pop.}
diff --git a/lib/system/basic_types.nim b/lib/system/basic_types.nim
new file mode 100644
index 000000000..bf81b9b6a
--- /dev/null
+++ b/lib/system/basic_types.nim
@@ -0,0 +1,94 @@
+type
+  int* {.magic: Int.}         ## Default integer type; bitwidth depends on
+                              ## architecture, but is always the same as a pointer.
+  int8* {.magic: Int8.}       ## Signed 8 bit integer type.
+  int16* {.magic: Int16.}     ## Signed 16 bit integer type.
+  int32* {.magic: Int32.}     ## Signed 32 bit integer type.
+  int64* {.magic: Int64.}     ## Signed 64 bit integer type.
+  uint* {.magic: UInt.}       ## Unsigned default integer type.
+  uint8* {.magic: UInt8.}     ## Unsigned 8 bit integer type.
+  uint16* {.magic: UInt16.}   ## Unsigned 16 bit integer type.
+  uint32* {.magic: UInt32.}   ## Unsigned 32 bit integer type.
+  uint64* {.magic: UInt64.}   ## Unsigned 64 bit integer type.
+
+type
+  float* {.magic: Float.}     ## Default floating point type.
+  float32* {.magic: Float32.} ## 32 bit floating point type.
+  float64* {.magic: Float.}   ## 64 bit floating point type.
+
+# 'float64' is now an alias to 'float'; this solves many problems
+
+type
+  char* {.magic: Char.}         ## Built-in 8 bit character type (unsigned).
+  string* {.magic: String.}     ## Built-in string type.
+  cstring* {.magic: Cstring.}   ## Built-in cstring (*compatible string*) type.
+  pointer* {.magic: Pointer.}   ## Built-in pointer type, use the `addr`
+                                ## operator to get a pointer to a variable.
+
+  typedesc* {.magic: TypeDesc.} ## Meta type to denote a type description.
+
+type
+  `ptr`*[T] {.magic: Pointer.}   ## Built-in generic untraced pointer type.
+  `ref`*[T] {.magic: Pointer.}   ## Built-in generic traced pointer type.
+
+  `nil` {.magic: "Nil".}
+
+  void* {.magic: "VoidType".}    ## Meta type to denote the absence of any type.
+  auto* {.magic: Expr.}          ## Meta type for automatic type determination.
+  any* {.deprecated: "Deprecated since v1.5; Use auto instead.".} = distinct auto  ## Deprecated; Use `auto` instead. See https://github.com/nim-lang/RFCs/issues/281
+  untyped* {.magic: Expr.}       ## Meta type to denote an expression that
+                                 ## is not resolved (for templates).
+  typed* {.magic: Stmt.}         ## Meta type to denote an expression that
+                                 ## is resolved (for templates).
+
+type # we need to start a new type section here, so that ``0`` can have a type
+  bool* {.magic: "Bool".} = enum ## Built-in boolean type.
+    false = 0, true = 1
+
+const
+  on* = true    ## Alias for `true`.
+  off* = false  ## Alias for `false`.
+
+type
+  SomeSignedInt* = int|int8|int16|int32|int64
+    ## Type class matching all signed integer types.
+
+  SomeUnsignedInt* = uint|uint8|uint16|uint32|uint64
+    ## Type class matching all unsigned integer types.
+
+  SomeInteger* = SomeSignedInt|SomeUnsignedInt
+    ## Type class matching all integer types.
+
+  SomeFloat* = float|float32|float64
+    ## Type class matching all floating point number types.
+
+  SomeNumber* = SomeInteger|SomeFloat
+    ## Type class matching all number types.
+
+  SomeOrdinal* = int|int8|int16|int32|int64|bool|enum|uint|uint8|uint16|uint32|uint64
+    ## Type class matching all ordinal types; however this includes enums with
+    ## holes. See also `Ordinal`
+
+
+{.push warning[GcMem]: off, warning[Uninit]: off.}
+{.push hints: off.}
+
+proc `not`*(x: bool): bool {.magic: "Not", noSideEffect.}
+  ## Boolean not; returns true if `x == false`.
+
+proc `and`*(x, y: bool): bool {.magic: "And", noSideEffect.}
+  ## Boolean `and`; returns true if `x == y == true` (if both arguments
+  ## are true).
+  ##
+  ## Evaluation is lazy: if `x` is false, `y` will not even be evaluated.
+proc `or`*(x, y: bool): bool {.magic: "Or", noSideEffect.}
+  ## Boolean `or`; returns true if `not (not x and not y)` (if any of
+  ## the arguments is true).
+  ##
+  ## Evaluation is lazy: if `x` is true, `y` will not even be evaluated.
+proc `xor`*(x, y: bool): bool {.magic: "Xor", noSideEffect.}
+  ## Boolean `exclusive or`; returns true if `x != y` (if either argument
+  ## is true while the other is false).
+
+{.pop.}
+{.pop.}
diff --git a/lib/system/bitmasks.nim b/lib/system/bitmasks.nim
new file mode 100644
index 000000000..0663247c2
--- /dev/null
+++ b/lib/system/bitmasks.nim
@@ -0,0 +1,39 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# Page size of the system; in most cases 4096 bytes. For exotic OS or
+# CPU this needs to be changed:
+const
+  PageShift = when defined(nimPage256) or defined(cpu16): 3
+              elif defined(nimPage512): 9
+              elif defined(nimPage1k): 10
+              else: 12 # \ # my tests showed no improvements for using larger page sizes.
+
+  PageSize = 1 shl PageShift
+  PageMask = PageSize-1
+
+
+  MemAlign = # also minimal allocatable memory block
+    when defined(nimMemAlignTiny): 4
+    elif defined(useMalloc):
+      when defined(amd64): 16 
+      else: 8
+    else: 16
+
+  BitsPerPage = PageSize div MemAlign
+  UnitsPerPage = BitsPerPage div (sizeof(int)*8)
+    # how many ints do we need to describe a page:
+    # on 32 bit systems this is only 16 (!)
+
+  TrunkShift = 9
+  BitsPerTrunk = 1 shl TrunkShift # needs to be power of 2 and divisible by 64
+  TrunkMask = BitsPerTrunk - 1
+  IntsPerTrunk = BitsPerTrunk div (sizeof(int)*8)
+  IntShift = 5 + ord(sizeof(int) == 8) # 5 or 6, depending on int width
+  IntMask = 1 shl IntShift - 1
diff --git a/lib/system/cellseqs_v1.nim b/lib/system/cellseqs_v1.nim
new file mode 100644
index 000000000..1a305aa42
--- /dev/null
+++ b/lib/system/cellseqs_v1.nim
@@ -0,0 +1,46 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2019 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# ------------------- cell seq handling ---------------------------------------
+
+type
+  PCellArray = ptr UncheckedArray[PCell]
+  CellSeq {.final, pure.} = object
+    len, cap: int
+    d: PCellArray
+
+proc contains(s: CellSeq, c: PCell): bool {.inline.} =
+  for i in 0 ..< s.len:
+    if s.d[i] == c:
+      return true
+  return false
+
+proc resize(s: var CellSeq) =
+  s.cap = s.cap * 3 div 2
+  let d = cast[PCellArray](alloc(s.cap * sizeof(PCell)))
+  copyMem(d, s.d, s.len * sizeof(PCell))
+  dealloc(s.d)
+  s.d = d
+
+proc add(s: var CellSeq, c: PCell) {.inline.} =
+  if s.len >= s.cap:
+    resize(s)
+  s.d[s.len] = c
+  inc(s.len)
+
+proc init(s: var CellSeq, cap: int = 1024) =
+  s.len = 0
+  s.cap = cap
+  s.d = cast[PCellArray](alloc0(cap * sizeof(PCell)))
+
+proc deinit(s: var CellSeq) =
+  dealloc(s.d)
+  s.d = nil
+  s.len = 0
+  s.cap = 0
diff --git a/lib/system/cellseqs_v2.nim b/lib/system/cellseqs_v2.nim
new file mode 100644
index 000000000..c6c7b1a8e
--- /dev/null
+++ b/lib/system/cellseqs_v2.nim
@@ -0,0 +1,53 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2019 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# Cell seqs for cyclebreaker and cyclicrefs_v2.
+
+type
+  CellTuple[T] = (T, PNimTypeV2)
+  CellArray[T] = ptr UncheckedArray[CellTuple[T]]
+  CellSeq[T] = object
+    len, cap: int
+    d: CellArray[T]
+
+proc resize[T](s: var CellSeq[T]) =
+  s.cap = s.cap * 3 div 2
+  var newSize = s.cap * sizeof(CellTuple[T])
+  when compileOption("threads"):
+    s.d = cast[CellArray[T]](reallocShared(s.d, newSize))
+  else:
+    s.d = cast[CellArray[T]](realloc(s.d, newSize))
+
+proc add[T](s: var CellSeq[T], c: T, t: PNimTypeV2) {.inline.} =
+  if s.len >= s.cap:
+    s.resize()
+  s.d[s.len] = (c, t)
+  inc(s.len)
+
+proc init[T](s: var CellSeq[T], cap: int = 1024) =
+  s.len = 0
+  s.cap = cap
+  when compileOption("threads"):
+    s.d = cast[CellArray[T]](allocShared(uint(s.cap * sizeof(CellTuple[T]))))
+  else:
+    s.d = cast[CellArray[T]](alloc(s.cap * sizeof(CellTuple[T])))
+
+proc deinit[T](s: var CellSeq[T]) =
+  if s.d != nil:
+    when compileOption("threads"):
+      deallocShared(s.d)
+    else:
+      dealloc(s.d)
+    s.d = nil
+  s.len = 0
+  s.cap = 0
+
+proc pop[T](s: var CellSeq[T]): (T, PNimTypeV2) =
+  result = s.d[s.len-1]
+  dec s.len
diff --git a/lib/system/cellsets.nim b/lib/system/cellsets.nim
index f26cb86ab..92036c226 100644
--- a/lib/system/cellsets.nim
+++ b/lib/system/cellsets.nim
@@ -7,27 +7,69 @@
 #    distribution, for details about the copyright.
 #
 
-# Efficient set of pointers for the GC (and repr)
 
-type
-  RefCount = int
+#[
+
+Efficient set of pointers for the GC (and repr)
+-----------------------------------------------
+
+The GC depends on an extremely efficient datastructure for storing a
+set of pointers - this is called a `CellSet` in the source code.
+Inserting, deleting and searching are done in constant time. However,
+modifying a `CellSet` during traversal leads to undefined behaviour.
+
+All operations on a CellSet have to perform efficiently. Because a Cellset can
+become huge a hash table alone is not suitable for this.
+
+We use a mixture of bitset and hash table for this. The hash table maps *pages*
+to a page descriptor. The page descriptor contains a bit for any possible cell
+address within this page. So including a cell is done as follows:
 
-  Cell {.pure.} = object
-    refcount: RefCount  # the refcount and some flags
-    typ: PNimType
-    when trackAllocationSource:
-      filename: cstring
-      line: int
-    when useCellIds:
-      id: int
+- Find the page descriptor for the page the cell belongs to.
+- Set the appropriate bit in the page descriptor indicating that the
+  cell points to the start of a memory block.
 
-  PCell = ptr Cell
+Removing a cell is analogous - the bit has to be set to zero.
+Single page descriptors are never deleted from the hash table. This is not
+needed as the data structures needs to be rebuilt periodically anyway.
 
+Complete traversal is done in this way::
+
+  for each page descriptor d:
+    for each bit in d:
+      if bit == 1:
+        traverse the pointer belonging to this bit
+
+]#
+
+when defined(gcOrc) or defined(gcArc) or defined(gcAtomicArc):
+  type
+    PCell = Cell
+
+  when not declaredInScope(PageShift):
+    include bitmasks
+
+else:
+  type
+    RefCount = int
+
+    Cell {.pure.} = object
+      refcount: RefCount  # the refcount and some flags
+      typ: PNimType
+      when trackAllocationSource:
+        filename: cstring
+        line: int
+      when useCellIds:
+        id: int
+
+    PCell = ptr Cell
+
+type
   PPageDesc = ptr PageDesc
   BitIndex = range[0..UnitsPerPage-1]
   PageDesc {.final, pure.} = object
     next: PPageDesc # all nodes are connected with this pointer
-    key: ByteAddress   # start address at bit 0
+    key: uint   # start address at bit 0
     bits: array[BitIndex, int] # a bit vector
 
   PPageDescArray = ptr UncheckedArray[PPageDesc]
@@ -35,40 +77,11 @@ type
     counter, max: int
     head: PPageDesc
     data: PPageDescArray
-  PCellArray = ptr UncheckedArray[PCell]
-  CellSeq {.final, pure.} = object
-    len, cap: int
-    d: PCellArray
-{.deprecated: [TCell: Cell, TBitIndex: BitIndex, TPageDesc: PageDesc,
-              TRefCount: RefCount, TCellSet: CellSet, TCellSeq: CellSeq].}
-# ------------------- cell seq handling ---------------------------------------
-
-proc contains(s: CellSeq, c: PCell): bool {.inline.} =
-  for i in 0 .. s.len-1:
-    if s.d[i] == c: return true
-  return false
-
-proc add(s: var CellSeq, c: PCell) {.inline.} =
-  if s.len >= s.cap:
-    s.cap = s.cap * 3 div 2
-    var d = cast[PCellArray](alloc(s.cap * sizeof(PCell)))
-    copyMem(d, s.d, s.len * sizeof(PCell))
-    dealloc(s.d)
-    s.d = d
-    # XXX: realloc?
-  s.d[s.len] = c
-  inc(s.len)
-
-proc init(s: var CellSeq, cap: int = 1024) =
-  s.len = 0
-  s.cap = cap
-  s.d = cast[PCellArray](alloc0(cap * sizeof(PCell)))
-
-proc deinit(s: var CellSeq) =
-  dealloc(s.d)
-  s.d = nil
-  s.len = 0
-  s.cap = 0
+
+when defined(gcOrc) or defined(gcArc) or defined(gcAtomicArc):
+  discard
+else:
+  include cellseqs_v1
 
 # ------------------- cell set handling ---------------------------------------
 
@@ -98,7 +111,7 @@ proc nextTry(h, maxHash: int): int {.inline.} =
   # generates each int in range(maxHash) exactly once (see any text on
   # random-number generation for proof).
 
-proc cellSetGet(t: CellSet, key: ByteAddress): PPageDesc =
+proc cellSetGet(t: CellSet, key: uint): PPageDesc =
   var h = cast[int](key) and t.max
   while t.data[h] != nil:
     if t.data[h].key == key: return t.data[h]
@@ -123,7 +136,7 @@ proc cellSetEnlarge(t: var CellSet) =
   dealloc(t.data)
   t.data = n
 
-proc cellSetPut(t: var CellSet, key: ByteAddress): PPageDesc =
+proc cellSetPut(t: var CellSet, key: uint): PPageDesc =
   var h = cast[int](key) and t.max
   while true:
     var x = t.data[h]
@@ -147,33 +160,33 @@ proc cellSetPut(t: var CellSet, key: ByteAddress): PPageDesc =
 # ---------- slightly higher level procs --------------------------------------
 
 proc contains(s: CellSet, cell: PCell): bool =
-  var u = cast[ByteAddress](cell)
+  var u = cast[uint](cell)
   var t = cellSetGet(s, u shr PageShift)
   if t != nil:
-    u = (u %% PageSize) /% MemAlign
+    u = (u mod PageSize) div MemAlign
     result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0
   else:
     result = false
 
-proc incl(s: var CellSet, cell: PCell) {.noinline.} =
-  var u = cast[ByteAddress](cell)
+proc incl(s: var CellSet, cell: PCell) =
+  var u = cast[uint](cell)
   var t = cellSetPut(s, u shr PageShift)
-  u = (u %% PageSize) /% MemAlign
+  u = (u mod PageSize) div MemAlign
   t.bits[u shr IntShift] = t.bits[u shr IntShift] or (1 shl (u and IntMask))
 
 proc excl(s: var CellSet, cell: PCell) =
-  var u = cast[ByteAddress](cell)
+  var u = cast[uint](cell)
   var t = cellSetGet(s, u shr PageShift)
   if t != nil:
-    u = (u %% PageSize) /% MemAlign
+    u = (u mod PageSize) div MemAlign
     t.bits[u shr IntShift] = (t.bits[u shr IntShift] and
                               not (1 shl (u and IntMask)))
 
 proc containsOrIncl(s: var CellSet, cell: PCell): bool =
-  var u = cast[ByteAddress](cell)
+  var u = cast[uint](cell)
   var t = cellSetGet(s, u shr PageShift)
   if t != nil:
-    u = (u %% PageSize) /% MemAlign
+    u = (u mod PageSize) div MemAlign
     result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0
     if not result:
       t.bits[u shr IntShift] = t.bits[u shr IntShift] or
@@ -186,15 +199,15 @@ iterator elements(t: CellSet): PCell {.inline.} =
   # while traversing it is forbidden to add pointers to the tree!
   var r = t.head
   while r != nil:
-    var i = 0
-    while i <= high(r.bits):
+    var i: uint = 0
+    while int(i) <= high(r.bits):
       var w = r.bits[i] # taking a copy of r.bits[i] here is correct, because
       # modifying operations are not allowed during traversation
-      var j = 0
+      var j: uint = 0
       while w != 0:         # test all remaining bits for zero
         if (w and 1) != 0:  # the bit is set!
           yield cast[PCell]((r.key shl PageShift) or
-                              (i shl IntShift +% j) *% MemAlign)
+                              (i shl IntShift + j) * MemAlign)
         inc(j)
         w = w shr 1
       inc(i)
@@ -239,16 +252,16 @@ iterator elementsExcept(t, s: CellSet): PCell {.inline.} =
   var r = t.head
   while r != nil:
     let ss = cellSetGet(s, r.key)
-    var i = 0
-    while i <= high(r.bits):
+    var i:uint = 0
+    while int(i) <= high(r.bits):
       var w = r.bits[i]
       if ss != nil:
         w = w and not ss.bits[i]
-      var j = 0
+      var j:uint = 0
       while w != 0:
         if (w and 1) != 0:
           yield cast[PCell]((r.key shl PageShift) or
-                              (i shl IntShift +% j) *% MemAlign)
+                              (i shl IntShift + j) * MemAlign)
         inc(j)
         w = w shr 1
       inc(i)
diff --git a/lib/system/cgprocs.nim b/lib/system/cgprocs.nim
index 660c68116..9a7645f9b 100644
--- a/lib/system/cgprocs.nim
+++ b/lib/system/cgprocs.nim
@@ -8,14 +8,3 @@
 #
 
 # Headers for procs that the code generator depends on ("compilerprocs")
-
-type
-  LibHandle = pointer       # private type
-  ProcAddr = pointer        # library loading and loading of procs:
-{.deprecated: [TLibHandle: LibHandle, TProcAddr: ProcAddr].}
-
-proc nimLoadLibrary(path: string): LibHandle {.compilerproc.}
-proc nimUnloadLibrary(lib: LibHandle) {.compilerproc.}
-proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {.compilerproc.}
-
-proc nimLoadLibraryError(path: string) {.compilerproc, noinline.}
diff --git a/lib/system/channels.nim b/lib/system/channels.nim
deleted file mode 100644
index 254b87dfc..000000000
--- a/lib/system/channels.nim
+++ /dev/null
@@ -1,300 +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.
-#
-
-## Channel support for threads. **Note**: This is part of the system module.
-## Do not import it directly. To activate thread support you need to compile
-## with the ``--threads:on`` command line switch.
-##
-## **Note:** The current implementation of message passing does
-## not work with cyclic data structures.
-## **Note:** Channels cannot be passed between threads. Use globals or pass
-## them by `ptr`.
-
-when not declared(NimString):
-  {.error: "You must not import this module explicitly".}
-
-type
-  pbytes = ptr array[0.. 0xffff, byte]
-  RawChannel {.pure, final.} = object ## msg queue for a thread
-    rd, wr, count, mask, maxItems: int
-    data: pbytes
-    lock: SysLock
-    cond: SysCond
-    elemType: PNimType
-    ready: bool
-    region: MemRegion
-  PRawChannel = ptr RawChannel
-  LoadStoreMode = enum mStore, mLoad
-  Channel* {.gcsafe.}[TMsg] = RawChannel ## a channel for thread communication
-
-const ChannelDeadMask = -2
-
-proc initRawChannel(p: pointer, maxItems: int) =
-  var c = cast[PRawChannel](p)
-  initSysLock(c.lock)
-  initSysCond(c.cond)
-  c.mask = -1
-  c.maxItems = maxItems
-
-proc deinitRawChannel(p: pointer) =
-  var c = cast[PRawChannel](p)
-  # we need to grab the lock to be safe against sending threads!
-  acquireSys(c.lock)
-  c.mask = ChannelDeadMask
-  deallocOsPages(c.region)
-  deinitSys(c.lock)
-  deinitSysCond(c.cond)
-
-proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
-              mode: LoadStoreMode) {.benign.}
-
-proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel,
-              mode: LoadStoreMode) {.benign.} =
-  var
-    d = cast[ByteAddress](dest)
-    s = cast[ByteAddress](src)
-  case n.kind
-  of nkSlot: storeAux(cast[pointer](d +% n.offset),
-                      cast[pointer](s +% n.offset), n.typ, t, mode)
-  of nkList:
-    for i in 0..n.len-1: storeAux(dest, src, n.sons[i], t, mode)
-  of nkCase:
-    copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset),
-            n.typ.size)
-    var m = selectBranch(src, n)
-    if m != nil: storeAux(dest, src, m, t, mode)
-  of nkNone: sysAssert(false, "storeAux")
-
-proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
-              mode: LoadStoreMode) =
-  template `+!`(p: pointer; x: int): pointer =
-    cast[pointer](cast[int](p) +% x)
-
-  var
-    d = cast[ByteAddress](dest)
-    s = cast[ByteAddress](src)
-  sysAssert(mt != nil, "mt == nil")
-  case mt.kind
-  of tyString:
-    if mode == mStore:
-      var x = cast[PPointer](dest)
-      var s2 = cast[PPointer](s)[]
-      if s2 == nil:
-        x[] = nil
-      else:
-        var ss = cast[NimString](s2)
-        var ns = cast[NimString](alloc(t.region, ss.len+1 + GenericSeqSize))
-        copyMem(ns, ss, ss.len+1 + GenericSeqSize)
-        x[] = ns
-    else:
-      var x = cast[PPointer](dest)
-      var s2 = cast[PPointer](s)[]
-      if s2 == nil:
-        unsureAsgnRef(x, s2)
-      else:
-        let y = copyDeepString(cast[NimString](s2))
-        #echo "loaded ", cast[int](y), " ", cast[string](y)
-        unsureAsgnRef(x, y)
-        dealloc(t.region, s2)
-  of tySequence:
-    var s2 = cast[PPointer](src)[]
-    var seq = cast[PGenericSeq](s2)
-    var x = cast[PPointer](dest)
-    if s2 == nil:
-      if mode == mStore:
-        x[] = nil
-      else:
-        unsureAsgnRef(x, nil)
-    else:
-      sysAssert(dest != nil, "dest == nil")
-      if mode == mStore:
-        x[] = alloc0(t.region, seq.len *% mt.base.size +% GenericSeqSize)
-      else:
-        unsureAsgnRef(x, newSeq(mt, seq.len))
-      var dst = cast[ByteAddress](cast[PPointer](dest)[])
-      var dstseq = cast[PGenericSeq](dst)
-      dstseq.len = seq.len
-      dstseq.reserved = seq.len
-      for i in 0..seq.len-1:
-        storeAux(
-          cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
-          cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
-                        GenericSeqSize),
-          mt.base, t, mode)
-      if mode != mStore: dealloc(t.region, s2)
-  of tyObject:
-    if mt.base != nil:
-      storeAux(dest, src, mt.base, t, mode)
-    else:
-      # copy type field:
-      var pint = cast[ptr PNimType](dest)
-      pint[] = cast[ptr PNimType](src)[]
-    storeAux(dest, src, mt.node, t, mode)
-  of tyTuple:
-    storeAux(dest, src, mt.node, t, mode)
-  of tyArray, tyArrayConstr:
-    for i in 0..(mt.size div mt.base.size)-1:
-      storeAux(cast[pointer](d +% i*% mt.base.size),
-               cast[pointer](s +% i*% mt.base.size), mt.base, t, mode)
-  of tyRef, tyOptAsRef:
-    var s = cast[PPointer](src)[]
-    var x = cast[PPointer](dest)
-    if s == nil:
-      if mode == mStore:
-        x[] = nil
-      else:
-        unsureAsgnRef(x, nil)
-    else:
-      #let size = if mt.base.kind == tyObject: cast[ptr PNimType](s)[].size
-      #           else: mt.base.size
-      if mode == mStore:
-        let dyntype = when declared(usrToCell): usrToCell(s).typ
-                      else: mt
-        let size = dyntype.base.size
-        # we store the real dynamic 'ref type' at offset 0, so that
-        # no information is lost
-        let a = alloc0(t.region, size+sizeof(pointer))
-        x[] = a
-        cast[PPointer](a)[] = dyntype
-        storeAux(a +! sizeof(pointer), s, dyntype.base, t, mode)
-      else:
-        let dyntype = cast[ptr PNimType](s)[]
-        var obj = newObj(dyntype, dyntype.base.size)
-        unsureAsgnRef(x, obj)
-        storeAux(x[], s +! sizeof(pointer), dyntype.base, t, mode)
-        dealloc(t.region, s)
-  else:
-    copyMem(dest, src, mt.size) # copy raw bits
-
-proc rawSend(q: PRawChannel, data: pointer, typ: PNimType) =
-  ## adds an `item` to the end of the queue `q`.
-  var cap = q.mask+1
-  if q.count >= cap:
-    # start with capacity for 2 entries in the queue:
-    if cap == 0: cap = 1
-    var n = cast[pbytes](alloc0(q.region, cap*2*typ.size))
-    var z = 0
-    var i = q.rd
-    var c = q.count
-    while c > 0:
-      dec c
-      copyMem(addr(n[z*typ.size]), addr(q.data[i*typ.size]), typ.size)
-      i = (i + 1) and q.mask
-      inc z
-    if q.data != nil: dealloc(q.region, q.data)
-    q.data = n
-    q.mask = cap*2 - 1
-    q.wr = q.count
-    q.rd = 0
-  storeAux(addr(q.data[q.wr * typ.size]), data, typ, q, mStore)
-  inc q.count
-  q.wr = (q.wr + 1) and q.mask
-
-proc rawRecv(q: PRawChannel, data: pointer, typ: PNimType) =
-  sysAssert q.count > 0, "rawRecv"
-  dec q.count
-  storeAux(data, addr(q.data[q.rd * typ.size]), typ, q, mLoad)
-  q.rd = (q.rd + 1) and q.mask
-
-template lockChannel(q, action): untyped =
-  acquireSys(q.lock)
-  action
-  releaseSys(q.lock)
-
-proc sendImpl(q: PRawChannel, typ: PNimType, msg: pointer, noBlock: bool): bool =
-  if q.mask == ChannelDeadMask:
-    sysFatal(DeadThreadError, "cannot send message; thread died")
-  acquireSys(q.lock)
-  if q.maxItems > 0:
-    # Wait until count is less than maxItems
-    if noBlock and q.count >= q.maxItems:
-      releaseSys(q.lock)
-      return
-
-    while q.count >= q.maxItems:
-      waitSysCond(q.cond, q.lock)
-
-  rawSend(q, msg, typ)
-  q.elemType = typ
-  releaseSys(q.lock)
-  signalSysCond(q.cond)
-  result = true
-
-proc send*[TMsg](c: var Channel[TMsg], msg: TMsg) {.inline.} =
-  ## sends a message to a thread. `msg` is deeply copied.
-  discard sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), false)
-
-proc trySend*[TMsg](c: var Channel[TMsg], msg: TMsg): bool {.inline.} =
-  ## Tries to send a message to a thread. `msg` is deeply copied. Doesn't block.
-  ## Returns `false` if the message was not sent because number of pending items
-  ## in the cannel exceeded `maxItems`.
-  sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), true)
-
-proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =
-  q.ready = true
-  while q.count <= 0:
-    waitSysCond(q.cond, q.lock)
-  q.ready = false
-  if typ != q.elemType:
-    releaseSys(q.lock)
-    sysFatal(ValueError, "cannot receive message of wrong type")
-  rawRecv(q, res, typ)
-  if q.maxItems > 0 and q.count == q.maxItems - 1:
-    # Parent thread is awaiting in send. Wake it up.
-    signalSysCond(q.cond)
-
-proc recv*[TMsg](c: var Channel[TMsg]): TMsg =
-  ## receives a message from the channel `c`. This blocks until
-  ## a message has arrived! You may use ``peek`` to avoid the blocking.
-  var q = cast[PRawChannel](addr(c))
-  acquireSys(q.lock)
-  llRecv(q, addr(result), cast[PNimType](getTypeInfo(result)))
-  releaseSys(q.lock)
-
-proc tryRecv*[TMsg](c: var Channel[TMsg]): tuple[dataAvailable: bool,
-                                                  msg: TMsg] =
-  ## Tries to receive a message from the channel `c`, but this can fail
-  ## for all sort of reasons, including contention. If it fails,
-  ## it returns ``(false, default(msg))`` otherwise it
-  ## returns ``(true, msg)``.
-  var q = cast[PRawChannel](addr(c))
-  if q.mask != ChannelDeadMask:
-    if tryAcquireSys(q.lock):
-      if q.count > 0:
-        llRecv(q, addr(result.msg), cast[PNimType](getTypeInfo(result.msg)))
-        result.dataAvailable = true
-      releaseSys(q.lock)
-
-proc peek*[TMsg](c: var Channel[TMsg]): int =
-  ## returns the current number of messages in the channel `c`. Returns -1
-  ## if the channel has been closed. **Note**: This is dangerous to use
-  ## as it encourages races. It's much better to use ``tryRecv`` instead.
-  var q = cast[PRawChannel](addr(c))
-  if q.mask != ChannelDeadMask:
-    lockChannel(q):
-      result = q.count
-  else:
-    result = -1
-
-proc open*[TMsg](c: var Channel[TMsg], maxItems: int = 0) =
-  ## opens a channel `c` for inter thread communication. The `send` operation
-  ## will block until number of unprocessed items is less than `maxItems`.
-  ## For unlimited queue set `maxItems` to 0.
-  initRawChannel(addr(c), maxItems)
-
-proc close*[TMsg](c: var Channel[TMsg]) =
-  ## closes a channel `c` and frees its associated resources.
-  deinitRawChannel(addr(c))
-
-proc ready*[TMsg](c: var Channel[TMsg]): bool =
-  ## returns true iff some thread is waiting on the channel `c` for
-  ## new messages.
-  var q = cast[PRawChannel](addr(c))
-  result = q.ready
-
diff --git a/lib/system/channels_builtin.nim b/lib/system/channels_builtin.nim
new file mode 100644
index 000000000..02b4d8cbf
--- /dev/null
+++ b/lib/system/channels_builtin.nim
@@ -0,0 +1,459 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Channel support for threads.
+##
+## **Note**: This is part of the system module. Do not import it directly.
+## To activate thread support compile with the `--threads:on` command line switch.
+##
+## **Note:** Channels are designed for the `Thread` type. They are unstable when
+## used with `spawn`
+##
+## **Note:** The current implementation of message passing does
+## not work with cyclic data structures.
+##
+## **Note:** Channels cannot be passed between threads. Use globals or pass
+## them by `ptr`.
+##
+## Example
+## =======
+## The following is a simple example of two different ways to use channels:
+## blocking and non-blocking.
+##
+##   ```Nim
+##   # Be sure to compile with --threads:on.
+##   # The channels and threads modules are part of system and should not be
+##   # imported.
+##   import std/os
+##
+##   # Channels can either be:
+##   #  - declared at the module level, or
+##   #  - passed to procedures by ptr (raw pointer) -- see note on safety.
+##   #
+##   # For simplicity, in this example a channel is declared at module scope.
+##   # Channels are generic, and they include support for passing objects between
+##   # threads.
+##   # Note that objects passed through channels will be deeply copied.
+##   var chan: Channel[string]
+##
+##   # This proc will be run in another thread using the threads module.
+##   proc firstWorker() =
+##     chan.send("Hello World!")
+##
+##   # This is another proc to run in a background thread. This proc takes a while
+##   # to send the message since it sleeps for 2 seconds (or 2000 milliseconds).
+##   proc secondWorker() =
+##     sleep(2000)
+##     chan.send("Another message")
+##
+##   # Initialize the channel.
+##   chan.open()
+##
+##   # Launch the worker.
+##   var worker1: Thread[void]
+##   createThread(worker1, firstWorker)
+##
+##   # Block until the message arrives, then print it out.
+##   echo chan.recv() # "Hello World!"
+##
+##   # Wait for the thread to exit before moving on to the next example.
+##   worker1.joinThread()
+##
+##   # Launch the other worker.
+##   var worker2: Thread[void]
+##   createThread(worker2, secondWorker)
+##   # This time, use a non-blocking approach with tryRecv.
+##   # Since the main thread is not blocked, it could be used to perform other
+##   # useful work while it waits for data to arrive on the channel.
+##   while true:
+##     let tried = chan.tryRecv()
+##     if tried.dataAvailable:
+##       echo tried.msg # "Another message"
+##       break
+##
+##     echo "Pretend I'm doing useful work..."
+##     # For this example, sleep in order not to flood stdout with the above
+##     # message.
+##     sleep(400)
+##
+##   # Wait for the second thread to exit before cleaning up the channel.
+##   worker2.joinThread()
+##
+##   # Clean up the channel.
+##   chan.close()
+##   ```
+##
+## Sample output
+## -------------
+## The program should output something similar to this, but keep in mind that
+## exact results may vary in the real world:
+##
+##     Hello World!
+##     Pretend I'm doing useful work...
+##     Pretend I'm doing useful work...
+##     Pretend I'm doing useful work...
+##     Pretend I'm doing useful work...
+##     Pretend I'm doing useful work...
+##     Another message
+##
+## Passing Channels Safely
+## -----------------------
+## Note that when passing objects to procedures on another thread by pointer
+## (for example through a thread's argument), objects created using the default
+## allocator will use thread-local, GC-managed memory. Thus it is generally
+## safer to store channel objects in global variables (as in the above example),
+## in which case they will use a process-wide (thread-safe) shared heap.
+##
+## However, it is possible to manually allocate shared memory for channels
+## using e.g. `system.allocShared0` and pass these pointers through thread
+## arguments:
+##
+##   ```Nim
+##   proc worker(channel: ptr Channel[string]) =
+##     let greeting = channel[].recv()
+##     echo greeting
+##
+##   proc localChannelExample() =
+##     # Use allocShared0 to allocate some shared-heap memory and zero it.
+##     # The usual warnings about dealing with raw pointers apply. Exercise caution.
+##     var channel = cast[ptr Channel[string]](
+##       allocShared0(sizeof(Channel[string]))
+##     )
+##     channel[].open()
+##     # Create a thread which will receive the channel as an argument.
+##     var thread: Thread[ptr Channel[string]]
+##     createThread(thread, worker, channel)
+##     channel[].send("Hello from the main thread!")
+##     # Clean up resources.
+##     thread.joinThread()
+##     channel[].close()
+##     deallocShared(channel)
+##
+##   localChannelExample() # "Hello from the main thread!"
+##   ```
+
+when not declared(ThisIsSystem):
+  {.error: "You must not import this module explicitly".}
+
+import std/private/syslocks
+
+type
+  pbytes = ptr UncheckedArray[byte]
+  RawChannel {.pure, final.} = object ## msg queue for a thread
+    rd, wr, count, mask, maxItems: int
+    data: pbytes
+    lock: SysLock
+    cond: SysCond
+    elemType: PNimType
+    ready: bool
+    when not usesDestructors:
+      region: MemRegion
+  PRawChannel = ptr RawChannel
+  LoadStoreMode = enum mStore, mLoad
+  Channel*[TMsg] {.gcsafe.} = RawChannel ## a channel for thread communication
+
+const ChannelDeadMask = -2
+
+proc initRawChannel(p: pointer, maxItems: int) =
+  var c = cast[PRawChannel](p)
+  initSysLock(c.lock)
+  initSysCond(c.cond)
+  c.mask = -1
+  c.maxItems = maxItems
+
+proc deinitRawChannel(p: pointer) =
+  var c = cast[PRawChannel](p)
+  # we need to grab the lock to be safe against sending threads!
+  acquireSys(c.lock)
+  c.mask = ChannelDeadMask
+  when not usesDestructors:
+    deallocOsPages(c.region)
+  else:
+    if c.data != nil: deallocShared(c.data)
+  deinitSys(c.lock)
+  deinitSysCond(c.cond)
+
+when not usesDestructors:
+
+  proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
+                mode: LoadStoreMode) {.benign.}
+
+  proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel,
+                mode: LoadStoreMode) {.benign.} =
+    var
+      d = cast[int](dest)
+      s = cast[int](src)
+    case n.kind
+    of nkSlot: storeAux(cast[pointer](d +% n.offset),
+                        cast[pointer](s +% n.offset), n.typ, t, mode)
+    of nkList:
+      for i in 0..n.len-1: storeAux(dest, src, n.sons[i], t, mode)
+    of nkCase:
+      copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset),
+              n.typ.size)
+      var m = selectBranch(src, n)
+      if m != nil: storeAux(dest, src, m, t, mode)
+    of nkNone: sysAssert(false, "storeAux")
+
+  proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
+                mode: LoadStoreMode) =
+    template `+!`(p: pointer; x: int): pointer =
+      cast[pointer](cast[int](p) +% x)
+
+    var
+      d = cast[int](dest)
+      s = cast[int](src)
+    sysAssert(mt != nil, "mt == nil")
+    case mt.kind
+    of tyString:
+      if mode == mStore:
+        var x = cast[PPointer](dest)
+        var s2 = cast[PPointer](s)[]
+        if s2 == nil:
+          x[] = nil
+        else:
+          var ss = cast[NimString](s2)
+          var ns = cast[NimString](alloc(t.region, GenericSeqSize + ss.len+1))
+          copyMem(ns, ss, ss.len+1 + GenericSeqSize)
+          x[] = ns
+      else:
+        var x = cast[PPointer](dest)
+        var s2 = cast[PPointer](s)[]
+        if s2 == nil:
+          unsureAsgnRef(x, s2)
+        else:
+          let y = copyDeepString(cast[NimString](s2))
+          #echo "loaded ", cast[int](y), " ", cast[string](y)
+          unsureAsgnRef(x, y)
+          dealloc(t.region, s2)
+    of tySequence:
+      var s2 = cast[PPointer](src)[]
+      var seq = cast[PGenericSeq](s2)
+      var x = cast[PPointer](dest)
+      if s2 == nil:
+        if mode == mStore:
+          x[] = nil
+        else:
+          unsureAsgnRef(x, nil)
+      else:
+        sysAssert(dest != nil, "dest == nil")
+        if mode == mStore:
+          x[] = alloc0(t.region, align(GenericSeqSize, mt.base.align) +% seq.len *% mt.base.size)
+        else:
+          unsureAsgnRef(x, newSeq(mt, seq.len))
+        var dst = cast[int](cast[PPointer](dest)[])
+        var dstseq = cast[PGenericSeq](dst)
+        dstseq.len = seq.len
+        dstseq.reserved = seq.len
+        for i in 0..seq.len-1:
+          storeAux(
+            cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size),
+            cast[pointer](cast[int](s2) +% align(GenericSeqSize, mt.base.align) +%
+                          i *% mt.base.size),
+            mt.base, t, mode)
+        if mode != mStore: dealloc(t.region, s2)
+    of tyObject:
+      if mt.base != nil:
+        storeAux(dest, src, mt.base, t, mode)
+      else:
+        # copy type field:
+        var pint = cast[ptr PNimType](dest)
+        pint[] = cast[ptr PNimType](src)[]
+      storeAux(dest, src, mt.node, t, mode)
+    of tyTuple:
+      storeAux(dest, src, mt.node, t, mode)
+    of tyArray, tyArrayConstr:
+      for i in 0..(mt.size div mt.base.size)-1:
+        storeAux(cast[pointer](d +% i *% mt.base.size),
+                cast[pointer](s +% i *% mt.base.size), mt.base, t, mode)
+    of tyRef:
+      var s = cast[PPointer](src)[]
+      var x = cast[PPointer](dest)
+      if s == nil:
+        if mode == mStore:
+          x[] = nil
+        else:
+          unsureAsgnRef(x, nil)
+      else:
+        #let size = if mt.base.kind == tyObject: cast[ptr PNimType](s)[].size
+        #           else: mt.base.size
+        if mode == mStore:
+          let dyntype = when declared(usrToCell): usrToCell(s).typ
+                        else: mt
+          let size = dyntype.base.size
+          # we store the real dynamic 'ref type' at offset 0, so that
+          # no information is lost
+          let a = alloc0(t.region, size+sizeof(pointer))
+          x[] = a
+          cast[PPointer](a)[] = dyntype
+          storeAux(a +! sizeof(pointer), s, dyntype.base, t, mode)
+        else:
+          let dyntype = cast[ptr PNimType](s)[]
+          var obj = newObj(dyntype, dyntype.base.size)
+          unsureAsgnRef(x, obj)
+          storeAux(x[], s +! sizeof(pointer), dyntype.base, t, mode)
+          dealloc(t.region, s)
+    else:
+      copyMem(dest, src, mt.size) # copy raw bits
+
+proc rawSend(q: PRawChannel, data: pointer, typ: PNimType) =
+  ## Adds an `item` to the end of the queue `q`.
+  var cap = q.mask+1
+  if q.count >= cap:
+    # start with capacity for 2 entries in the queue:
+    if cap == 0: cap = 1
+    when not usesDestructors:
+      var n = cast[pbytes](alloc0(q.region, cap*2*typ.size))
+    else:
+      var n = cast[pbytes](allocShared0(cap*2*typ.size))
+    var z = 0
+    var i = q.rd
+    var c = q.count
+    while c > 0:
+      dec c
+      copyMem(addr(n[z*typ.size]), addr(q.data[i*typ.size]), typ.size)
+      i = (i + 1) and q.mask
+      inc z
+    if q.data != nil:
+      when not usesDestructors:
+        dealloc(q.region, q.data)
+      else:
+        deallocShared(q.data)
+    q.data = n
+    q.mask = cap*2 - 1
+    q.wr = q.count
+    q.rd = 0
+  when not usesDestructors:
+    storeAux(addr(q.data[q.wr * typ.size]), data, typ, q, mStore)
+  else:
+    copyMem(addr(q.data[q.wr * typ.size]), data, typ.size)
+  inc q.count
+  q.wr = (q.wr + 1) and q.mask
+
+proc rawRecv(q: PRawChannel, data: pointer, typ: PNimType) =
+  sysAssert q.count > 0, "rawRecv"
+  dec q.count
+  when not usesDestructors:
+    storeAux(data, addr(q.data[q.rd * typ.size]), typ, q, mLoad)
+  else:
+    copyMem(data, addr(q.data[q.rd * typ.size]), typ.size)
+  q.rd = (q.rd + 1) and q.mask
+
+template lockChannel(q, action): untyped =
+  acquireSys(q.lock)
+  action
+  releaseSys(q.lock)
+
+proc sendImpl(q: PRawChannel, typ: PNimType, msg: pointer, noBlock: bool): bool =
+  if q.mask == ChannelDeadMask:
+    sysFatal(DeadThreadDefect, "cannot send message; thread died")
+  acquireSys(q.lock)
+  if q.maxItems > 0:
+    # Wait until count is less than maxItems
+    if noBlock and q.count >= q.maxItems:
+      releaseSys(q.lock)
+      return
+
+    while q.count >= q.maxItems:
+      waitSysCond(q.cond, q.lock)
+
+  rawSend(q, msg, typ)
+  q.elemType = typ
+  signalSysCond(q.cond)
+  releaseSys(q.lock)
+  result = true
+
+proc send*[TMsg](c: var Channel[TMsg], msg: sink TMsg) {.inline.} =
+  ## Sends a message to a thread. `msg` is deeply copied.
+  discard sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), false)
+  when defined(gcDestructors):
+    wasMoved(msg)
+
+proc trySend*[TMsg](c: var Channel[TMsg], msg: sink TMsg): bool {.inline.} =
+  ## Tries to send a message to a thread.
+  ##
+  ## `msg` is deeply copied. Doesn't block.
+  ##
+  ## Returns `false` if the message was not sent because number of pending items
+  ## in the channel exceeded `maxItems`.
+  result = sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), true)
+  when defined(gcDestructors):
+    if result:
+      wasMoved(msg)
+
+proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =
+  q.ready = true
+  while q.count <= 0:
+    waitSysCond(q.cond, q.lock)
+  q.ready = false
+  if typ != q.elemType:
+    releaseSys(q.lock)
+    raise newException(ValueError, "cannot receive message of wrong type")
+  rawRecv(q, res, typ)
+  if q.maxItems > 0 and q.count == q.maxItems - 1:
+    # Parent thread is awaiting in send. Wake it up.
+    signalSysCond(q.cond)
+
+proc recv*[TMsg](c: var Channel[TMsg]): TMsg =
+  ## Receives a message from the channel `c`.
+  ##
+  ## This blocks until a message has arrived!
+  ## You may use `peek proc <#peek,Channel[TMsg]>`_ to avoid the blocking.
+  var q = cast[PRawChannel](addr(c))
+  acquireSys(q.lock)
+  llRecv(q, addr(result), cast[PNimType](getTypeInfo(result)))
+  releaseSys(q.lock)
+
+proc tryRecv*[TMsg](c: var Channel[TMsg]): tuple[dataAvailable: bool,
+                                                  msg: TMsg] =
+  ## Tries to receive a message from the channel `c`, but this can fail
+  ## for all sort of reasons, including contention.
+  ##
+  ## If it fails, it returns `(false, default(msg))` otherwise it
+  ## returns `(true, msg)`.
+  var q = cast[PRawChannel](addr(c))
+  if q.mask != ChannelDeadMask:
+    if tryAcquireSys(q.lock):
+      if q.count > 0:
+        llRecv(q, addr(result.msg), cast[PNimType](getTypeInfo(result.msg)))
+        result.dataAvailable = true
+      releaseSys(q.lock)
+
+proc peek*[TMsg](c: var Channel[TMsg]): int =
+  ## Returns the current number of messages in the channel `c`.
+  ##
+  ## Returns -1 if the channel has been closed.
+  ##
+  ## **Note**: This is dangerous to use as it encourages races.
+  ## It's much better to use `tryRecv proc <#tryRecv,Channel[TMsg]>`_ instead.
+  var q = cast[PRawChannel](addr(c))
+  if q.mask != ChannelDeadMask:
+    lockChannel(q):
+      result = q.count
+  else:
+    result = -1
+
+proc open*[TMsg](c: var Channel[TMsg], maxItems: int = 0) =
+  ## Opens a channel `c` for inter thread communication.
+  ##
+  ## The `send` operation will block until number of unprocessed items is
+  ## less than `maxItems`.
+  ##
+  ## For unlimited queue set `maxItems` to 0.
+  initRawChannel(addr(c), maxItems)
+
+proc close*[TMsg](c: var Channel[TMsg]) =
+  ## Closes a channel `c` and frees its associated resources.
+  deinitRawChannel(addr(c))
+
+proc ready*[TMsg](c: var Channel[TMsg]): bool =
+  ## Returns true if some thread is waiting on the channel `c` for
+  ## new messages.
+  var q = cast[PRawChannel](addr(c))
+  result = q.ready
diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim
index d3651f659..b48855964 100644
--- a/lib/system/chcks.nim
+++ b/lib/system/chcks.nim
@@ -8,24 +8,72 @@
 #
 
 # Implementation of some runtime checks.
+include system/indexerrors
+when defined(nimPreviewSlimSystem):
+  import std/formatfloat
 
 proc raiseRangeError(val: BiggestInt) {.compilerproc, noinline.} =
   when hostOS == "standalone":
-    sysFatal(RangeError, "value out of range")
+    sysFatal(RangeDefect, "value out of range")
   else:
-    sysFatal(RangeError, "value out of range: ", $val)
+    sysFatal(RangeDefect, "value out of range: ", $val)
+
+proc raiseIndexError4(l1, h1, h2: int) {.compilerproc, noinline.} =
+  sysFatal(IndexDefect, "index out of bounds: " & $l1 & ".." & $h1 & " notin 0.." & $(h2 - 1))
+
+proc raiseIndexError3(i, a, b: int) {.compilerproc, noinline.} =
+  sysFatal(IndexDefect, formatErrorIndexBound(i, a, b))
+
+proc raiseIndexError2(i, n: int) {.compilerproc, noinline.} =
+  sysFatal(IndexDefect, formatErrorIndexBound(i, n))
 
 proc raiseIndexError() {.compilerproc, noinline.} =
-  sysFatal(IndexError, "index out of bounds")
+  sysFatal(IndexDefect, "index out of bounds")
 
 proc raiseFieldError(f: string) {.compilerproc, noinline.} =
-  sysFatal(FieldError, f, " is not accessible")
+  ## remove after bootstrap > 1.5.1
+  sysFatal(FieldDefect, f)
+
+when defined(nimV2):
+  proc raiseFieldError2(f: string, discVal: int) {.compilerproc, noinline.} =
+    ## raised when field is inaccessible given runtime value of discriminant
+    sysFatal(FieldDefect, f & $discVal & "'")
+
+  proc raiseFieldErrorStr(f: string, discVal: string) {.compilerproc, noinline.} =
+    ## raised when field is inaccessible given runtime value of discriminant
+    sysFatal(FieldDefect, formatFieldDefect(f, discVal))
+else:
+  proc raiseFieldError2(f: string, discVal: string) {.compilerproc, noinline.} =
+    ## raised when field is inaccessible given runtime value of discriminant
+    sysFatal(FieldDefect, formatFieldDefect(f, discVal))
+
+proc raiseRangeErrorI(i, a, b: BiggestInt) {.compilerproc, noinline.} =
+  when defined(standalone):
+    sysFatal(RangeDefect, "value out of range")
+  else:
+    sysFatal(RangeDefect, "value out of range: " & $i & " notin " & $a & " .. " & $b)
+
+proc raiseRangeErrorF(i, a, b: float) {.compilerproc, noinline.} =
+  when defined(standalone):
+    sysFatal(RangeDefect, "value out of range")
+  else:
+    sysFatal(RangeDefect, "value out of range: " & $i & " notin " & $a & " .. " & $b)
+
+proc raiseRangeErrorU(i, a, b: uint64) {.compilerproc, noinline.} =
+  # todo: better error reporting
+  sysFatal(RangeDefect, "value out of range")
+
+proc raiseRangeErrorNoArgs() {.compilerproc, noinline.} =
+  sysFatal(RangeDefect, "value out of range")
+
+proc raiseObjectConversionError() {.compilerproc, noinline.} =
+  sysFatal(ObjectConversionDefect, "invalid object conversion")
 
 proc chckIndx(i, a, b: int): int =
   if i >= a and i <= b:
     return i
   else:
-    raiseIndexError()
+    raiseIndexError3(i, a, b)
 
 proc chckRange(i, a, b: int): int =
   if i >= a and i <= b:
@@ -39,68 +87,75 @@ proc chckRange64(i, a, b: int64): int64 {.compilerproc.} =
   else:
     raiseRangeError(i)
 
+proc chckRangeU(i, a, b: uint64): uint64 {.compilerproc.} =
+  if i >= a and i <= b:
+    return i
+  else:
+    sysFatal(RangeDefect, "value out of range")
+
 proc chckRangeF(x, a, b: float): float =
   if x >= a and x <= b:
     return x
   else:
     when hostOS == "standalone":
-      sysFatal(RangeError, "value out of range")
+      sysFatal(RangeDefect, "value out of range")
     else:
-      sysFatal(RangeError, "value out of range: ", $x)
+      sysFatal(RangeDefect, "value out of range: ", $x)
 
 proc chckNil(p: pointer) =
   if p == nil:
-    sysFatal(NilAccessError, "attempt to write to a nil address")
-
-when defined(nimNewRuntime):
-  proc chckMove(b: bool) {.compilerproc.} =
-    if not b:
-      sysFatal(MoveError, "attempt to access an object that was moved")
+    sysFatal(NilAccessDefect, "attempt to write to a nil address")
 
 proc chckNilDisp(p: pointer) {.compilerproc.} =
   if p == nil:
-    sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil")
-
-proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
-  # checks if obj is of type subclass:
-  var x = obj
-  if x == subclass: return # optimized fast path
-  while x != subclass:
-    if x == nil:
-      sysFatal(ObjectConversionError, "invalid object conversion")
-    x = x.base
-
-proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
-  if a != b:
-    sysFatal(ObjectAssignmentError, "invalid object assignment")
-
-type ObjCheckCache = array[0..1, PNimType]
-
-proc isObjSlowPath(obj, subclass: PNimType;
-                   cache: var ObjCheckCache): bool {.noinline.} =
-  # checks if obj is of type subclass:
-  var x = obj.base
-  while x != subclass:
-    if x == nil:
-      cache[0] = obj
-      return false
-    x = x.base
-  cache[1] = obj
-  return true
-
-proc isObjWithCache(obj, subclass: PNimType;
-                    cache: var ObjCheckCache): bool {.compilerProc, inline.} =
-  if obj == subclass: return true
-  if obj.base == subclass: return true
-  if cache[0] == obj: return false
-  if cache[1] == obj: return true
-  return isObjSlowPath(obj, subclass, cache)
-
-proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
-  # checks if obj is of type subclass:
-  var x = obj
-  if x == subclass: return true # optimized fast path
-  while x != subclass:
-    if x == nil: return false
-    x = x.base
-  return true
+    sysFatal(NilAccessDefect, "cannot dispatch; dispatcher is nil")
+
+when not defined(nimV2):
+
+  proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
+    # checks if obj is of type subclass:
+    var x = obj
+    if x == subclass: return # optimized fast path
+    while x != subclass:
+      if x == nil:
+        sysFatal(ObjectConversionDefect, "invalid object conversion")
+      x = x.base
+
+  proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
+    if a != b:
+      sysFatal(ObjectAssignmentDefect, "invalid object assignment")
+
+  type ObjCheckCache = array[0..1, PNimType]
+
+  proc isObjSlowPath(obj, subclass: PNimType;
+                    cache: var ObjCheckCache): bool {.noinline.} =
+    # checks if obj is of type subclass:
+    var x = obj.base
+    while x != subclass:
+      if x == nil:
+        cache[0] = obj
+        return false
+      x = x.base
+    cache[1] = obj
+    return true
+
+  proc isObjWithCache(obj, subclass: PNimType;
+                      cache: var ObjCheckCache): bool {.compilerproc, inline.} =
+    if obj == subclass: return true
+    if obj.base == subclass: return true
+    if cache[0] == obj: return false
+    if cache[1] == obj: return true
+    return isObjSlowPath(obj, subclass, cache)
+
+  proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
+    # checks if obj is of type subclass:
+    var x = obj
+    if x == subclass: return true # optimized fast path
+    while x != subclass:
+      if x == nil: return false
+      x = x.base
+    return true
+
+when defined(nimV2):
+  proc raiseObjectCaseTransition() {.compilerproc.} =
+    sysFatal(FieldDefect, "assignment to discriminant changes object branch")
diff --git a/lib/system/comparisons.nim b/lib/system/comparisons.nim
new file mode 100644
index 000000000..a8d78bb93
--- /dev/null
+++ b/lib/system/comparisons.nim
@@ -0,0 +1,337 @@
+# comparison operators:
+proc `==`*[Enum: enum](x, y: Enum): bool {.magic: "EqEnum", noSideEffect.} =
+  ## Checks whether values within the *same enum* have the same underlying value.
+  runnableExamples:
+    type
+      Enum1 = enum
+        field1 = 3, field2
+      Enum2 = enum
+        place1, place2 = 3
+    var
+      e1 = field1
+      e2 = place2.ord.Enum1
+    assert e1 == e2
+    assert not compiles(e1 == place2) # raises error
+proc `==`*(x, y: pointer): bool {.magic: "EqRef", noSideEffect.} =
+  ## Checks for equality between two `pointer` variables.
+  runnableExamples:
+    var # this is a wildly dangerous example
+      a = cast[pointer](0)
+      b = cast[pointer](nil)
+    assert a == b # true due to the special meaning of `nil`/0 as a pointer
+proc `==`*(x, y: string): bool {.magic: "EqStr", noSideEffect.}
+  ## Checks for equality between two `string` variables.
+
+proc `==`*(x, y: char): bool {.magic: "EqCh", noSideEffect.}
+  ## Checks for equality between two `char` variables.
+proc `==`*(x, y: bool): bool {.magic: "EqB", noSideEffect.}
+  ## Checks for equality between two `bool` variables.
+proc `==`*[T](x, y: set[T]): bool {.magic: "EqSet", noSideEffect.} =
+  ## Checks for equality between two variables of type `set`.
+  runnableExamples:
+    assert {1, 2, 2, 3} == {1, 2, 3} # duplication in sets is ignored
+
+proc `==`*[T](x, y: ref T): bool {.magic: "EqRef", noSideEffect.}
+  ## Checks that two `ref` variables refer to the same item.
+proc `==`*[T](x, y: ptr T): bool {.magic: "EqRef", noSideEffect.}
+  ## Checks that two `ptr` variables refer to the same item.
+proc `==`*[T: proc | iterator](x, y: T): bool {.magic: "EqProc", noSideEffect.}
+  ## Checks that two `proc` variables refer to the same procedure.
+
+proc `<=`*[Enum: enum](x, y: Enum): bool {.magic: "LeEnum", noSideEffect.}
+proc `<=`*(x, y: string): bool {.magic: "LeStr", noSideEffect.} =
+  ## Compares two strings and returns true if `x` is lexicographically
+  ## before `y` (uppercase letters come before lowercase letters).
+  runnableExamples:
+    let
+      a = "abc"
+      b = "abd"
+      c = "ZZZ"
+    assert a <= b
+    assert a <= a
+    assert not (a <= c)
+
+proc `<=`*(x, y: char): bool {.magic: "LeCh", noSideEffect.} =
+  ## Compares two chars and returns true if `x` is lexicographically
+  ## before `y` (uppercase letters come before lowercase letters).
+  runnableExamples:
+    let
+      a = 'a'
+      b = 'b'
+      c = 'Z'
+    assert a <= b
+    assert a <= a
+    assert not (a <= c)
+
+proc `<=`*[T](x, y: set[T]): bool {.magic: "LeSet", noSideEffect.} =
+  ## Returns true if `x` is a subset of `y`.
+  ##
+  ## A subset `x` has all of its members in `y` and `y` doesn't necessarily
+  ## have more members than `x`. That is, `x` can be equal to `y`.
+  runnableExamples:
+    let
+      a = {3, 5}
+      b = {1, 3, 5, 7}
+      c = {2}
+    assert a <= b
+    assert a <= a
+    assert not (a <= c)
+
+proc `<=`*(x, y: bool): bool {.magic: "LeB", noSideEffect.}
+proc `<=`*[T](x, y: ref T): bool {.magic: "LePtr", noSideEffect.}
+proc `<=`*(x, y: pointer): bool {.magic: "LePtr", noSideEffect.}
+
+proc `<`*[Enum: enum](x, y: Enum): bool {.magic: "LtEnum", noSideEffect.}
+proc `<`*(x, y: string): bool {.magic: "LtStr", noSideEffect.} =
+  ## Compares two strings and returns true if `x` is lexicographically
+  ## before `y` (uppercase letters come before lowercase letters).
+  runnableExamples:
+    let
+      a = "abc"
+      b = "abd"
+      c = "ZZZ"
+    assert a < b
+    assert not (a < a)
+    assert not (a < c)
+
+proc `<`*(x, y: char): bool {.magic: "LtCh", noSideEffect.} =
+  ## Compares two chars and returns true if `x` is lexicographically
+  ## before `y` (uppercase letters come before lowercase letters).
+  runnableExamples:
+    let
+      a = 'a'
+      b = 'b'
+      c = 'Z'
+    assert a < b
+    assert not (a < a)
+    assert not (a < c)
+
+proc `<`*[T](x, y: set[T]): bool {.magic: "LtSet", noSideEffect.} =
+  ## Returns true if `x` is a strict or proper subset of `y`.
+  ##
+  ## A strict or proper subset `x` has all of its members in `y` but `y` has
+  ## more elements than `y`.
+  runnableExamples:
+    let
+      a = {3, 5}
+      b = {1, 3, 5, 7}
+      c = {2}
+    assert a < b
+    assert not (a < a)
+    assert not (a < c)
+
+proc `<`*(x, y: bool): bool {.magic: "LtB", noSideEffect.}
+proc `<`*[T](x, y: ref T): bool {.magic: "LtPtr", noSideEffect.}
+proc `<`*[T](x, y: ptr T): bool {.magic: "LtPtr", noSideEffect.}
+proc `<`*(x, y: pointer): bool {.magic: "LtPtr", noSideEffect.}
+
+when not defined(nimHasCallsitePragma):
+  {.pragma: callsite.}
+
+template `!=`*(x, y: untyped): untyped {.callsite.} =
+  ## Unequals operator. This is a shorthand for `not (x == y)`.
+  not (x == y)
+
+template `>=`*(x, y: untyped): untyped {.callsite.} =
+  ## "is greater or equals" operator. This is the same as `y <= x`.
+  y <= x
+
+template `>`*(x, y: untyped): untyped {.callsite.} =
+  ## "is greater" operator. This is the same as `y < x`.
+  y < x
+
+
+proc `==`*(x, y: int): bool {.magic: "EqI", noSideEffect.}
+  ## Compares two integers for equality.
+proc `==`*(x, y: int8): bool {.magic: "EqI", noSideEffect.}
+proc `==`*(x, y: int16): bool {.magic: "EqI", noSideEffect.}
+proc `==`*(x, y: int32): bool {.magic: "EqI", noSideEffect.}
+proc `==`*(x, y: int64): bool {.magic: "EqI", noSideEffect.}
+
+proc `<=`*(x, y: int): bool {.magic: "LeI", noSideEffect.}
+  ## Returns true if `x` is less than or equal to `y`.
+proc `<=`*(x, y: int8): bool {.magic: "LeI", noSideEffect.}
+proc `<=`*(x, y: int16): bool {.magic: "LeI", noSideEffect.}
+proc `<=`*(x, y: int32): bool {.magic: "LeI", noSideEffect.}
+proc `<=`*(x, y: int64): bool {.magic: "LeI", noSideEffect.}
+
+proc `<`*(x, y: int): bool {.magic: "LtI", noSideEffect.}
+  ## Returns true if `x` is less than `y`.
+proc `<`*(x, y: int8): bool {.magic: "LtI", noSideEffect.}
+proc `<`*(x, y: int16): bool {.magic: "LtI", noSideEffect.}
+proc `<`*(x, y: int32): bool {.magic: "LtI", noSideEffect.}
+proc `<`*(x, y: int64): bool {.magic: "LtI", noSideEffect.}
+
+proc `<=`*(x, y: uint): bool {.magic: "LeU", noSideEffect.}
+  ## Returns true if `x <= y`.
+proc `<=`*(x, y: uint8): bool {.magic: "LeU", noSideEffect.}
+proc `<=`*(x, y: uint16): bool {.magic: "LeU", noSideEffect.}
+proc `<=`*(x, y: uint32): bool {.magic: "LeU", noSideEffect.}
+proc `<=`*(x, y: uint64): bool {.magic: "LeU", noSideEffect.}
+
+proc `<`*(x, y: uint): bool {.magic: "LtU", noSideEffect.}
+  ## Returns true if `x < y`.
+proc `<`*(x, y: uint8): bool {.magic: "LtU", noSideEffect.}
+proc `<`*(x, y: uint16): bool {.magic: "LtU", noSideEffect.}
+proc `<`*(x, y: uint32): bool {.magic: "LtU", noSideEffect.}
+proc `<`*(x, y: uint64): bool {.magic: "LtU", noSideEffect.}
+
+proc `<=%`*(x, y: int): bool {.inline.} =
+  ## Treats `x` and `y` as unsigned and compares them.
+  ## Returns true if `unsigned(x) <= unsigned(y)`.
+  cast[uint](x) <= cast[uint](y)
+proc `<=%`*(x, y: int8): bool {.inline.} = cast[uint8](x) <= cast[uint8](y)
+proc `<=%`*(x, y: int16): bool {.inline.} = cast[uint16](x) <= cast[uint16](y)
+proc `<=%`*(x, y: int32): bool {.inline.} = cast[uint32](x) <= cast[uint32](y)
+proc `<=%`*(x, y: int64): bool {.inline.} = cast[uint64](x) <= cast[uint64](y)
+
+proc `<%`*(x, y: int): bool {.inline.} =
+  ## Treats `x` and `y` as unsigned and compares them.
+  ## Returns true if `unsigned(x) < unsigned(y)`.
+  cast[uint](x) < cast[uint](y)
+proc `<%`*(x, y: int8): bool {.inline.} = cast[uint8](x) < cast[uint8](y)
+proc `<%`*(x, y: int16): bool {.inline.} = cast[uint16](x) < cast[uint16](y)
+proc `<%`*(x, y: int32): bool {.inline.} = cast[uint32](x) < cast[uint32](y)
+proc `<%`*(x, y: int64): bool {.inline.} = cast[uint64](x) < cast[uint64](y)
+
+template `>=%`*(x, y: untyped): untyped = y <=% x
+  ## Treats `x` and `y` as unsigned and compares them.
+  ## Returns true if `unsigned(x) >= unsigned(y)`.
+
+template `>%`*(x, y: untyped): untyped = y <% x
+  ## Treats `x` and `y` as unsigned and compares them.
+  ## Returns true if `unsigned(x) > unsigned(y)`.
+
+proc `==`*(x, y: uint): bool {.magic: "EqI", noSideEffect.}
+  ## Compares two unsigned integers for equality.
+proc `==`*(x, y: uint8): bool {.magic: "EqI", noSideEffect.}
+proc `==`*(x, y: uint16): bool {.magic: "EqI", noSideEffect.}
+proc `==`*(x, y: uint32): bool {.magic: "EqI", noSideEffect.}
+proc `==`*(x, y: uint64): bool {.magic: "EqI", noSideEffect.}
+
+proc `<=`*(x, y: float32): bool {.magic: "LeF64", noSideEffect.}
+proc `<=`*(x, y: float): bool {.magic: "LeF64", noSideEffect.}
+
+proc `<`*(x, y: float32): bool {.magic: "LtF64", noSideEffect.}
+proc `<`*(x, y: float): bool {.magic: "LtF64", noSideEffect.}
+
+proc `==`*(x, y: float32): bool {.magic: "EqF64", noSideEffect.}
+proc `==`*(x, y: float): bool {.magic: "EqF64", noSideEffect.}
+
+{.push stackTrace: off.}
+
+proc min*(x, y: int): int {.magic: "MinI", noSideEffect.} =
+  if x <= y: x else: y
+proc min*(x, y: int8): int8 {.magic: "MinI", noSideEffect.} =
+  if x <= y: x else: y
+proc min*(x, y: int16): int16 {.magic: "MinI", noSideEffect.} =
+  if x <= y: x else: y
+proc min*(x, y: int32): int32 {.magic: "MinI", noSideEffect.} =
+  if x <= y: x else: y
+proc min*(x, y: int64): int64 {.magic: "MinI", noSideEffect.} =
+  ## The minimum value of two integers.
+  if x <= y: x else: y
+proc min*(x, y: float32): float32 {.noSideEffect, inline.} =
+  if x <= y or y != y: x else: y
+proc min*(x, y: float64): float64 {.noSideEffect, inline.} =
+  if x <= y or y != y: x else: y
+proc min*[T: not SomeFloat](x, y: T): T {.inline.} =
+  ## Generic minimum operator of 2 values based on `<=`.
+  if x <= y: x else: y
+
+proc max*(x, y: int): int {.magic: "MaxI", noSideEffect.} =
+  if y <= x: x else: y
+proc max*(x, y: int8): int8 {.magic: "MaxI", noSideEffect.} =
+  if y <= x: x else: y
+proc max*(x, y: int16): int16 {.magic: "MaxI", noSideEffect.} =
+  if y <= x: x else: y
+proc max*(x, y: int32): int32 {.magic: "MaxI", noSideEffect.} =
+  if y <= x: x else: y
+proc max*(x, y: int64): int64 {.magic: "MaxI", noSideEffect.} =
+  ## The maximum value of two integers.
+  if y <= x: x else: y
+proc max*(x, y: float32): float32 {.noSideEffect, inline.} =
+  if y <= x or y != y: x else: y
+proc max*(x, y: float64): float64 {.noSideEffect, inline.} =
+  if y <= x or y != y: x else: y
+proc max*[T: not SomeFloat](x, y: T): T {.inline.} =
+  ## Generic maximum operator of 2 values based on `<=`.
+  if y <= x: x else: y
+
+
+proc min*[T](x: openArray[T]): T =
+  ## The minimum value of `x`. `T` needs to have a `<` operator.
+  result = x[0]
+  for i in 1..high(x):
+    if x[i] < result: result = x[i]
+
+proc max*[T](x: openArray[T]): T =
+  ## The maximum value of `x`. `T` needs to have a `<` operator.
+  result = x[0]
+  for i in 1..high(x):
+    if result < x[i]: result = x[i]
+
+{.pop.} # stackTrace: off
+
+
+proc clamp*[T](x, a, b: T): T =
+  ## Limits the value `x` within the interval \[a, b].
+  ## This proc is equivalent to but faster than `max(a, min(b, x))`.
+  ## 
+  ## .. warning:: `a <= b` is assumed and will not be checked (currently).
+  ##
+  ## **See also:**
+  ## `math.clamp` for a version that takes a `Slice[T]` instead.
+  runnableExamples:
+    assert (1.4).clamp(0.0, 1.0) == 1.0
+    assert (0.5).clamp(0.0, 1.0) == 0.5
+    assert 4.clamp(1, 3) == max(1, min(3, 4))
+  if x < a: return a
+  if x > b: return b
+  return x
+
+
+proc `==`*[I, T](x, y: array[I, T]): bool =
+  for f in low(x)..high(x):
+    if x[f] != y[f]:
+      return
+  result = true
+
+proc `==`*[T](x, y: openArray[T]): bool =
+  if x.len != y.len:
+    return false
+  for f in low(x)..high(x):
+    if x[f] != y[f]:
+      return false
+  result = true
+
+
+proc `==`*[T](x, y: seq[T]): bool {.noSideEffect.} =
+  ## Generic equals operator for sequences: relies on a equals operator for
+  ## the element type `T`.
+  when nimvm:
+    if x.len == 0 and y.len == 0:
+      return true
+  else:
+    when not defined(js):
+      proc seqToPtr[T](x: seq[T]): pointer {.inline, noSideEffect.} =
+        when defined(nimSeqsV2):
+          result = cast[NimSeqV2[T]](x).p
+        else:
+          result = cast[pointer](x)
+
+      if seqToPtr(x) == seqToPtr(y):
+        return true
+    else:
+      var sameObject = false
+      {.emit: """`sameObject` = `x` === `y`;""".}
+      if sameObject: return true
+
+  if x.len != y.len:
+    return false
+
+  for i in 0..x.len-1:
+    if x[i] != y[i]:
+      return false
+
+  return true
diff --git a/lib/system/compilation.nim b/lib/system/compilation.nim
new file mode 100644
index 000000000..cdb976ed5
--- /dev/null
+++ b/lib/system/compilation.nim
@@ -0,0 +1,209 @@
+const
+  NimMajor* {.intdefine.}: int = 2
+    ## is the major number of Nim's version. Example:
+    ##   ```nim
+    ##   when (NimMajor, NimMinor, NimPatch) >= (1, 3, 1): discard
+    ##   ```
+    # see also std/private/since
+
+  NimMinor* {.intdefine.}: int = 2
+    ## is the minor number of Nim's version.
+    ## Odd for devel, even for releases.
+
+  NimPatch* {.intdefine.}: int = 1
+    ## is the patch number of Nim's version.
+    ## Odd for devel, even for releases.
+
+{.push profiler: off.}
+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.}
+
+const
+  isMainModule* {.magic: "IsMainModule".}: bool = false
+    ## True only when accessed in the main module. This works thanks to
+    ## compiler magic. It is useful to embed testing code in a module.
+
+  CompileDate* {.magic: "CompileDate".}: string = "0000-00-00"
+    ## The date (in UTC) of compilation as a string of the form
+    ## `YYYY-MM-DD`. This works thanks to compiler magic.
+
+  CompileTime* {.magic: "CompileTime".}: string = "00:00:00"
+    ## The time (in UTC) of compilation as a string of the form
+    ## `HH:MM:SS`. This works thanks to compiler magic.
+
+proc defined*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.}
+  ## Special compile-time procedure that checks whether `x` is
+  ## defined.
+  ##
+  ## `x` is an external symbol introduced through the compiler's
+  ## `-d:x switch <nimc.html#compiler-usage-compileminustime-symbols>`_ to enable
+  ## build time conditionals:
+  ##   ```nim
+  ##   when not defined(release):
+  ##     # Do here programmer friendly expensive sanity checks.
+  ##   # Put here the normal code
+  ##   ```
+  ##
+  ## See also:
+  ## * `compileOption <#compileOption,string>`_ for `on|off` options
+  ## * `compileOption <#compileOption,string,string>`_ for enum options
+  ## * `define pragmas <manual.html#implementation-specific-pragmas-compileminustime-define-pragmas>`_
+
+proc declared*(x: untyped): bool {.magic: "Declared", noSideEffect, compileTime.}
+  ## Special compile-time procedure that checks whether `x` is
+  ## declared. `x` has to be an identifier or a qualified identifier.
+  ##
+  ## This can be used to check whether a library provides a certain
+  ## feature or not:
+  ##   ```nim
+  ##   when not declared(strutils.toUpper):
+  ##     # provide our own toUpper proc here, because strutils is
+  ##     # missing it.
+  ##   ```
+  ##
+  ## See also:
+  ## * `declaredInScope <#declaredInScope,untyped>`_
+
+proc declaredInScope*(x: untyped): bool {.magic: "DeclaredInScope", noSideEffect, compileTime.}
+  ## Special compile-time procedure that checks whether `x` is
+  ## declared in the current scope. `x` has to be an identifier.
+
+proc compiles*(x: untyped): bool {.magic: "Compiles", noSideEffect, compileTime.} =
+  ## Special compile-time procedure that checks whether `x` can be compiled
+  ## without any semantic error.
+  ## This can be used to check whether a type supports some operation:
+  ##   ```nim
+  ##   when compiles(3 + 4):
+  ##     echo "'+' for integers is available"
+  ##   ```
+  discard
+
+proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.}
+  ## Converts the AST of `x` into a string representation. This is very useful
+  ## for debugging.
+
+proc runnableExamples*(rdoccmd = "", 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 each runnableExample is put in its own file `$file_examples$i.nim`,
+  ##   compiled and tested. The collected examples are
+  ##   put into their own module to ensure the examples do not refer to
+  ##   non-exported symbols.
+  runnableExamples:
+    proc timesTwo*(x: int): int =
+      ## This proc doubles a number.
+      runnableExamples:
+        # at module scope
+        const exported* = 123
+        assert timesTwo(5) == 10
+        block: # at block scope
+          defer: echo "done"
+      runnableExamples "-d:foo -b:cpp":
+        import std/compilesettings
+        assert querySetting(backend) == "cpp"
+        assert defined(foo)
+      runnableExamples "-r:off": ## this one is only compiled
+         import std/browsers
+         openDefaultBrowser "https://forum.nim-lang.org/"
+      2 * x
+
+proc compileOption*(option: string): bool {.
+  magic: "CompileOption", noSideEffect.} =
+  ## Can be used to determine an `on|off` compile-time option.
+  ##
+  ## See also:
+  ## * `compileOption <#compileOption,string,string>`_ for enum options
+  ## * `defined <#defined,untyped>`_
+  ## * `std/compilesettings module <compilesettings.html>`_
+  runnableExamples("--floatChecks:off"):
+    static: doAssert not compileOption("floatchecks")
+    {.push floatChecks: on.}
+    static: doAssert compileOption("floatchecks")
+    # floating point NaN and Inf checks enabled in this scope
+    {.pop.}
+
+proc compileOption*(option, arg: string): bool {.
+  magic: "CompileOptionArg", noSideEffect.} =
+  ## Can be used to determine an enum compile-time option.
+  ##
+  ## See also:
+  ## * `compileOption <#compileOption,string>`_ for `on|off` options
+  ## * `defined <#defined,untyped>`_
+  ## * `std/compilesettings module <compilesettings.html>`_
+  runnableExamples:
+    when compileOption("opt", "size") and compileOption("gc", "boehm"):
+      discard "compiled with optimization for size and uses Boehm's GC"
+
+template currentSourcePath*: string = instantiationInfo(-1, true).filename
+  ## Returns the full file-system path of the current source.
+  ##
+  ## To get the directory containing the current source, use it with
+  ## `ospaths2.parentDir() <ospaths2.html#parentDir%2Cstring>`_ as
+  ## `currentSourcePath.parentDir()`.
+  ##
+  ## The path returned by this template is set at compile time.
+  ##
+  ## See the docstring of `macros.getProjectPath() <macros.html#getProjectPath>`_
+  ## for an example to see the distinction between the `currentSourcePath()`
+  ## and `getProjectPath()`.
+  ##
+  ## See also:
+  ## * `ospaths2.getCurrentDir() proc <ospaths2.html#getCurrentDir>`_
+
+proc slurp*(filename: string): string {.magic: "Slurp".}
+  ## This is an alias for `staticRead <#staticRead,string>`_.
+
+proc staticRead*(filename: string): string {.magic: "Slurp".}
+  ## Compile-time `readFile <syncio.html#readFile,string>`_ proc for easy
+  ## `resource`:idx: embedding:
+  ##
+  ## The maximum file size limit that `staticRead` and `slurp` can read is
+  ## near or equal to the *free* memory of the device you are using to compile.
+  ##   ```nim
+  ##   const myResource = staticRead"mydatafile.bin"
+  ##   ```
+  ##
+  ## `slurp <#slurp,string>`_ is an alias for `staticRead`.
+
+proc gorge*(command: string, input = "", cache = ""): string {.
+  magic: "StaticExec".} = discard
+  ## This is an alias for `staticExec <#staticExec,string,string,string>`_.
+
+proc staticExec*(command: string, input = "", cache = ""): string {.
+  magic: "StaticExec".} = discard
+  ## Executes an external process at compile-time and returns its text output
+  ## (stdout + stderr).
+  ##
+  ## If `input` is not an empty string, it will be passed as a standard input
+  ## to the executed program.
+  ##   ```nim
+  ##   const buildInfo = "Revision " & staticExec("git rev-parse HEAD") &
+  ##                     "\nCompiled on " & staticExec("uname -v")
+  ##   ```
+  ##
+  ## `gorge <#gorge,string,string,string>`_ is an alias for `staticExec`.
+  ##
+  ## Note that you can use this proc inside a pragma like
+  ## `passc <manual.html#implementation-specific-pragmas-passc-pragma>`_ or
+  ## `passl <manual.html#implementation-specific-pragmas-passl-pragma>`_.
+  ##
+  ## If `cache` is not empty, the results of `staticExec` are cached within
+  ## the `nimcache` directory. Use `--forceBuild` to get rid of this caching
+  ## behaviour then. `command & input & cache` (the concatenated string) is
+  ## used to determine whether the entry in the cache is still valid. You can
+  ## use versioning information for `cache`:
+  ##   ```nim
+  ##   const stateMachine = staticExec("dfaoptimizer", "input", "0.8.0")
+  ##   ```
+
+proc gorgeEx*(command: string, input = "", cache = ""): tuple[output: string,
+                                                              exitCode: int] =
+  ## Similar to `gorge <#gorge,string,string,string>`_ but also returns the
+  ## precious exit code.
+  discard
diff --git a/lib/system/coro_detection.nim b/lib/system/coro_detection.nim
new file mode 100644
index 000000000..f6c1b5c15
--- /dev/null
+++ b/lib/system/coro_detection.nim
@@ -0,0 +1,20 @@
+## Coroutine detection logic
+
+template coroutinesSupportedPlatform(): bool =
+  when defined(sparc) or defined(ELATE) or defined(boehmgc) or defined(gogc) or
+    defined(nogc) or defined(gcRegions) or defined(gcMarkAndSweep):
+    false
+  else:
+    true
+
+when defined(nimCoroutines):
+  # Explicit opt-in.
+  when not coroutinesSupportedPlatform():
+    {.error: "Coroutines are not supported on this architecture and/or garbage collector.".}
+  const nimCoroutines* = true
+elif defined(noNimCoroutines):
+  # Explicit opt-out.
+  const nimCoroutines* = false
+else:
+  # Autodetect coroutine support.
+  const nimCoroutines* = false
diff --git a/lib/system/countbits_impl.nim b/lib/system/countbits_impl.nim
new file mode 100644
index 000000000..34969cb32
--- /dev/null
+++ b/lib/system/countbits_impl.nim
@@ -0,0 +1,93 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Contains the used algorithms for counting bits.
+
+from std/private/bitops_utils import forwardImpl, castToUnsigned
+
+const useBuiltins* = not defined(noIntrinsicsBitOpts)
+const noUndefined* = defined(noUndefinedBitOpts)
+const useGCC_builtins* = (defined(gcc) or defined(llvm_gcc) or
+                         defined(clang)) and useBuiltins
+const useICC_builtins* = defined(icc) and useBuiltins
+const useVCC_builtins* = defined(vcc) and useBuiltins
+const arch64* = sizeof(int) == 8
+
+template countBitsImpl(n: uint32): int =
+  # generic formula is from: https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
+  var v = uint32(n)
+  v = v - ((v shr 1'u32) and 0x55555555'u32)
+  v = (v and 0x33333333'u32) + ((v shr 2'u32) and 0x33333333'u32)
+  (((v + (v shr 4'u32) and 0xF0F0F0F'u32) * 0x1010101'u32) shr 24'u32).int
+
+template countBitsImpl(n: uint64): int =
+  # generic formula is from: https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
+  var v = uint64(n)
+  v = v - ((v shr 1'u64) and 0x5555555555555555'u64)
+  v = (v and 0x3333333333333333'u64) + ((v shr 2'u64) and 0x3333333333333333'u64)
+  v = (v + (v shr 4'u64) and 0x0F0F0F0F0F0F0F0F'u64)
+  ((v * 0x0101010101010101'u64) shr 56'u64).int
+
+
+when useGCC_builtins:
+  # Returns the number of set 1-bits in value.
+  proc builtin_popcount(x: cuint): cint {.importc: "__builtin_popcount", cdecl.}
+  proc builtin_popcountll(x: culonglong): cint {.
+      importc: "__builtin_popcountll", cdecl.}
+
+elif useVCC_builtins:
+  # Counts the number of one bits (population count) in a 16-, 32-, or 64-byte unsigned integer.
+  func builtin_popcnt16(a2: uint16): uint16 {.
+      importc: "__popcnt16", header: "<intrin.h>".}
+  func builtin_popcnt32(a2: uint32): uint32 {.
+      importc: "__popcnt", header: "<intrin.h>".}
+  func builtin_popcnt64(a2: uint64): uint64 {.
+      importc: "__popcnt64", header: "<intrin.h>".}
+
+elif useICC_builtins:
+  # Intel compiler intrinsics: http://fulla.fnal.gov/intel/compiler_c/main_cls/intref_cls/common/intref_allia_misc.htm
+  # see also: https://software.intel.com/en-us/node/523362
+  # Count the number of bits set to 1 in an integer a, and return that count in dst.
+  func builtin_popcnt32(a: cint): cint {.
+      importc: "_popcnt", header: "<immintrin.h>".}
+  func builtin_popcnt64(a: uint64): cint {.
+      importc: "_popcnt64", header: "<immintrin.h>".}
+
+
+func countSetBitsImpl*(x: SomeInteger): int {.inline.} =
+  ## Counts the set bits in an integer (also called `Hamming weight`:idx:).
+  # TODO: figure out if ICC support _popcnt32/_popcnt64 on platform without POPCNT.
+  # like GCC and MSVC
+  let x = x.castToUnsigned
+  when nimvm:
+    result = forwardImpl(countBitsImpl, x)
+  else:
+    when useGCC_builtins:
+      when sizeof(x) <= 4: result = builtin_popcount(x.cuint).int
+      else: result = builtin_popcountll(x.culonglong).int
+    elif useVCC_builtins:
+      when sizeof(x) <= 2: result = builtin_popcnt16(x.uint16).int
+      elif sizeof(x) <= 4: result = builtin_popcnt32(x.uint32).int
+      elif arch64: result = builtin_popcnt64(x.uint64).int
+      else: result = builtin_popcnt32((x.uint64 and 0xFFFFFFFF'u64).uint32).int +
+                     builtin_popcnt32((x.uint64 shr 32'u64).uint32).int
+    elif useICC_builtins:
+      when sizeof(x) <= 4: result = builtin_popcnt32(x.cint).int
+      elif arch64: result = builtin_popcnt64(x.uint64).int
+      else: result = builtin_popcnt32((x.uint64 and 0xFFFFFFFF'u64).cint).int +
+                     builtin_popcnt32((x.uint64 shr 32'u64).cint).int
+    else:
+      when sizeof(x) <= 4: result = countBitsImpl(x.uint32)
+      else: result = countBitsImpl(x.uint64)
+
+proc countBits32*(n: uint32): int {.compilerproc, inline.} =
+  result = countSetBitsImpl(n)
+
+proc countBits64*(n: uint64): int {.compilerproc, inline.} =
+  result = countSetBitsImpl(n)
diff --git a/lib/system/ctypes.nim b/lib/system/ctypes.nim
new file mode 100644
index 000000000..b788274bd
--- /dev/null
+++ b/lib/system/ctypes.nim
@@ -0,0 +1,84 @@
+## Some type definitions for compatibility between different
+## backends and platforms.
+
+type
+  BiggestInt* = int64
+    ## is an alias for the biggest signed integer type the Nim compiler
+    ## supports. Currently this is `int64`, but it is platform-dependent
+    ## in general.
+
+  BiggestFloat* = float64
+    ## is an alias for the biggest floating point type the Nim
+    ## compiler supports. Currently this is `float64`, but it is
+    ## platform-dependent in general.
+
+  BiggestUInt* = uint64
+    ## is an alias for the biggest unsigned integer type the Nim compiler
+    ## supports. Currently this is `uint64`, but it is platform-dependent
+    ## in general.
+
+when defined(windows):
+  type
+    clong* {.importc: "long", nodecl.} = int32
+      ## This is the same as the type `long` in *C*.
+    culong* {.importc: "unsigned long", nodecl.} = uint32
+      ## This is the same as the type `unsigned long` in *C*.
+else:
+  type
+    clong* {.importc: "long", nodecl.} = int
+      ## This is the same as the type `long` in *C*.
+    culong* {.importc: "unsigned long", nodecl.} = uint
+      ## This is the same as the type `unsigned long` in *C*.
+
+type # these work for most platforms:
+  cchar* {.importc: "char", nodecl.} = char
+    ## This is the same as the type `char` in *C*.
+  cschar* {.importc: "signed char", nodecl.} = int8
+    ## This is the same as the type `signed char` in *C*.
+  cshort* {.importc: "short", nodecl.} = int16
+    ## This is the same as the type `short` in *C*.
+  cint* {.importc: "int", nodecl.} = int32
+    ## This is the same as the type `int` in *C*.
+  csize_t* {.importc: "size_t", nodecl.} = uint
+    ## This is the same as the type `size_t` in *C*.
+  clonglong* {.importc: "long long", nodecl.} = int64
+    ## This is the same as the type `long long` in *C*.
+  cfloat* {.importc: "float", nodecl.} = float32
+    ## This is the same as the type `float` in *C*.
+  cdouble* {.importc: "double", nodecl.} = float64
+    ## This is the same as the type `double` in *C*.
+  clongdouble* {.importc: "long double", nodecl.} = BiggestFloat
+    ## This is the same as the type `long double` in *C*.
+    ## This C type is not supported by Nim's code generator.
+
+  cuchar* {.importc: "unsigned char", nodecl, deprecated: "use `char` or `uint8` instead".} = char
+    ## Deprecated: Use `uint8` instead.
+  cushort* {.importc: "unsigned short", nodecl.} = uint16
+    ## This is the same as the type `unsigned short` in *C*.
+  cuint* {.importc: "unsigned int", nodecl.} = uint32
+    ## This is the same as the type `unsigned int` in *C*.
+  culonglong* {.importc: "unsigned long long", nodecl.} = uint64
+    ## This is the same as the type `unsigned long long` in *C*.
+
+type
+  ByteAddress* {.deprecated: "use `uint`".} = int
+    ## is the signed integer type that should be used for converting
+    ## pointers to integer addresses for readability.
+
+  cstringArray* {.importc: "char**", nodecl.} = ptr UncheckedArray[cstring]
+    ## This is binary compatible to the type `char**` in *C*. The array's
+    ## high value is large enough to disable bounds checking in practice.
+    ## Use `cstringArrayToSeq proc <#cstringArrayToSeq,cstringArray,Natural>`_
+    ## to convert it into a `seq[string]`.
+
+when not defined(nimPreviewSlimSystem):
+  # pollutes namespace
+  type
+    PFloat32* {.deprecated: "use `ptr float32`".} = ptr float32
+      ## An alias for `ptr float32`.
+    PFloat64* {.deprecated: "use `ptr float64`".} = ptr float64
+      ## An alias for `ptr float64`.
+    PInt64* {.deprecated: "use `ptr int64`".} = ptr int64
+      ## An alias for `ptr int64`.
+    PInt32* {.deprecated: "use `ptr int32`".} = ptr int32
+      ## An alias for `ptr int32`.
diff --git a/lib/system/cyclebreaker.nim b/lib/system/cyclebreaker.nim
new file mode 100644
index 000000000..45b0a5a65
--- /dev/null
+++ b/lib/system/cyclebreaker.nim
@@ -0,0 +1,184 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+#[
+A Cycle breaker for Nim
+-----------------------
+
+Instead of "collecting" cycles with all of its pitfalls we will break cycles.
+We exploit that every 'ref' can be 'nil' for this and so get away without
+a distinction between weak and strong pointers. The required runtime
+mechanisms are the same though: We need to be able to traverse the graph.
+This design has the tremendous benefit that it doesn't require a dedicated
+'rawDispose' operation and that it plays well with Nim's cost model.
+The cost of freeing a subgraph with cycles is 2 * N rather than N, that's all.
+
+Cycles do not have to be prepared via .acyclic, there are not multiple
+pointless traversals, only a single proc, `breakCycles` is exposed as a
+separate module.
+
+Algorithm
+---------
+
+We traverse the graph and notice the nodes we've already traversed. If we
+marked the node already, we set the pointer that leads to this node to 'nil'
+and decrement the reference count of the cell we pointed at.
+
+We notice that multiple paths to the same object do not mean
+we found a cycle, it only means the node is shared.
+
+
+   a -------> b <----- c
+   |          ^        ^
+   +----------+        |
+   |                   |
+   +-------------------+
+
+If we simply remove all links to already processed nodes we end up with:
+
+   a -------> b        c
+   |                   ^
+   +                   |
+   |                   |
+   +-------------------+
+
+That seems acceptable, no leak is produced. This implies that the standard
+depth-first traversal suffices.
+
+]#
+
+include cellseqs_v2
+
+const
+  colGreen = 0b000
+  colYellow = 0b001
+  colRed = 0b010
+  colorMask = 0b011
+
+type
+  TraceProc = proc (p, env: pointer) {.nimcall, benign.}
+  DisposeProc = proc (p: pointer) {.nimcall, benign.}
+
+template color(c): untyped = c.rc and colorMask
+template setColor(c, col) =
+  c.rc = c.rc and not colorMask or col
+
+proc nimIncRefCyclic(p: pointer; cyclic: bool) {.compilerRtl, inl.} =
+  let h = head(p)
+  inc h.rc, rcIncrement
+
+proc nimMarkCyclic(p: pointer) {.compilerRtl, inl.} = discard
+
+type
+  GcEnv = object
+    traceStack: CellSeq[ptr pointer]
+
+proc trace(p: pointer; desc: PNimTypeV2; j: var GcEnv) {.inline.} =
+  when false:
+    cprintf("[Trace] desc: %p %p\n", desc, p)
+    cprintf("[Trace] trace: %p\n", desc.traceImpl)
+  if desc.traceImpl != nil:
+    cast[TraceProc](desc.traceImpl)(p, addr(j))
+
+proc nimTraceRef(q: pointer; desc: PNimTypeV2; env: pointer) {.compilerRtl.} =
+  let p = cast[ptr pointer](q)
+  when traceCollector:
+    cprintf("[Trace] raw: %p\n", p)
+    cprintf("[Trace] deref: %p\n", p[])
+  if p[] != nil:
+    var j = cast[ptr GcEnv](env)
+    j.traceStack.add(p, desc)
+
+proc nimTraceRefDyn(q: pointer; env: pointer) {.compilerRtl.} =
+  let p = cast[ptr pointer](q)
+  when traceCollector:
+    cprintf("[TraceDyn] raw: %p\n", p)
+    cprintf("[TraceDyn] deref: %p\n", p[])
+  if p[] != nil:
+    var j = cast[ptr GcEnv](env)
+    j.traceStack.add(p, cast[ptr PNimTypeV2](p[])[])
+
+var markerGeneration: int
+
+proc breakCycles(s: Cell; desc: PNimTypeV2) =
+  let markerColor = if (markerGeneration and 1) == 0: colRed
+                    else: colYellow
+  atomicInc markerGeneration
+  when traceCollector:
+    cprintf("[BreakCycles] starting: %p %s RC %ld trace proc %p\n",
+      s, desc.name, s.rc shr rcShift, desc.traceImpl)
+
+  var j: GcEnv
+  init j.traceStack
+  s.setColor markerColor
+  trace(s +! sizeof(RefHeader), desc, j)
+
+  while j.traceStack.len > 0:
+    let (u, desc) = j.traceStack.pop()
+    let p = u[]
+    let t = head(p)
+    if t.color != markerColor:
+      t.setColor markerColor
+      trace(p, desc, j)
+      when traceCollector:
+        cprintf("[BreakCycles] followed: %p RC %ld\n", t, t.rc shr rcShift)
+    else:
+      if (t.rc shr rcShift) > 0:
+        dec t.rc, rcIncrement
+        # mark as a link that the produced destructor does not have to follow:
+        u[] = nil
+        when traceCollector:
+          cprintf("[BreakCycles] niled out: %p RC %ld\n", t, t.rc shr rcShift)
+      else:
+        # anyhow as a link that the produced destructor does not have to follow:
+        u[] = nil
+        when traceCollector:
+          cprintf("[Bug] %p %s RC %ld\n", t, desc.name, t.rc shr rcShift)
+  deinit j.traceStack
+
+proc thinout*[T](x: ref T) {.inline.} =
+  ## turn the subgraph starting with `x` into its spanning tree by
+  ## `nil`'ing out any pointers that would harm the spanning tree
+  ## structure. Any back pointers that introduced cycles
+  ## and thus would keep the graph from being freed are `nil`'ed.
+  ## This is a form of cycle collection that works well with Nim's ARC
+  ## and its associated cost model.
+  proc getDynamicTypeInfo[T](x: T): PNimTypeV2 {.magic: "GetTypeInfoV2", noSideEffect.}
+
+  breakCycles(head(cast[pointer](x)), getDynamicTypeInfo(x[]))
+
+proc thinout*[T: proc](x: T) {.inline.} =
+  proc rawEnv[T: proc](x: T): pointer {.noSideEffect, inline.} =
+    {.emit: """
+    `result` = `x`.ClE_0;
+    """.}
+
+  let p = rawEnv(x)
+  breakCycles(head(p), cast[ptr PNimTypeV2](p)[])
+
+proc nimDecRefIsLastCyclicDyn(p: pointer): bool {.compilerRtl, inl.} =
+  if p != nil:
+    var cell = head(p)
+    if (cell.rc and not rcMask) == 0:
+      result = true
+      #cprintf("[DESTROY] %p\n", p)
+    else:
+      dec cell.rc, rcIncrement
+      # According to Lins it's correct to do nothing else here.
+      #cprintf("[DeCREF] %p\n", p)
+
+proc nimDecRefIsLastCyclicStatic(p: pointer; desc: PNimTypeV2): bool {.compilerRtl, inl.} =
+  if p != nil:
+    var cell = head(p)
+    if (cell.rc and not rcMask) == 0:
+      result = true
+      #cprintf("[DESTROY] %p %s\n", p, desc.name)
+    else:
+      dec cell.rc, rcIncrement
+      #cprintf("[DeCREF] %p %s %ld\n", p, desc.name, cell.rc)
diff --git a/lib/system/debugger.nim b/lib/system/debugger.nim
deleted file mode 100644
index 937c0d6f0..000000000
--- a/lib/system/debugger.nim
+++ /dev/null
@@ -1,305 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2013 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This file implements basic features for any debugger.
-
-type
-  VarSlot* {.compilerproc, final.} = object ## a slot in a frame
-    address*: pointer ## the variable's address
-    typ*: PNimType    ## the variable's type
-    name*: cstring    ## the variable's name; for globals this is "module.name"
-
-  PExtendedFrame = ptr ExtendedFrame
-  ExtendedFrame = object  # If the debugger is enabled the compiler
-                           # provides an extended frame. Of course
-                           # only slots that are
-                           # needed are allocated and not 10_000,
-                           # except for the global data description.
-    f: TFrame
-    slots: array[0..10_000, VarSlot]
-{.deprecated: [TVarSlot: VarSlot, TExtendedFrame: ExtendedFrame].}
-
-var
-  dbgGlobalData: ExtendedFrame  # this reserves much space, but
-                                # for now it is the most practical way
-
-proc dbgRegisterGlobal(name: cstring, address: pointer,
-                       typ: PNimType) {.compilerproc.} =
-  let i = dbgGlobalData.f.len
-  if i >= high(dbgGlobalData.slots):
-    #debugOut("[Warning] cannot register global ")
-    return
-  dbgGlobalData.slots[i].name = name
-  dbgGlobalData.slots[i].typ = typ
-  dbgGlobalData.slots[i].address = address
-  inc(dbgGlobalData.f.len)
-
-proc getLocal*(frame: PFrame; slot: int): VarSlot {.inline.} =
-  ## retrieves the meta data for the local variable at `slot`. CAUTION: An
-  ## invalid `slot` value causes a corruption!
-  result = cast[PExtendedFrame](frame).slots[slot]
-
-proc getGlobalLen*(): int {.inline.} =
-  ## gets the number of registered globals.
-  result = dbgGlobalData.f.len
-
-proc getGlobal*(slot: int): VarSlot {.inline.} =
-  ## retrieves the meta data for the global variable at `slot`. CAUTION: An
-  ## invalid `slot` value causes a corruption!
-  result = dbgGlobalData.slots[slot]
-
-# ------------------- breakpoint support ------------------------------------
-
-type
-  Breakpoint* = object   ## represents a break point
-    low*, high*: int     ## range from low to high; if disabled
-                         ## both low and high are set to their negative values
-    filename*: cstring   ## the filename of the breakpoint
-
-var
-  dbgBP: array[0..127, Breakpoint] # breakpoints
-  dbgBPlen: int
-  dbgBPbloom: int64  # we use a bloom filter to speed up breakpoint checking
-
-  dbgFilenames*: array[0..300, cstring] ## registered filenames;
-                                        ## 'nil' terminated
-  dbgFilenameLen: int
-
-proc dbgRegisterFilename(filename: cstring) {.compilerproc.} =
-  # XXX we could check for duplicates here for DLL support
-  dbgFilenames[dbgFilenameLen] = filename
-  inc dbgFilenameLen
-
-proc dbgRegisterBreakpoint(line: int,
-                           filename, name: cstring) {.compilerproc.} =
-  let x = dbgBPlen
-  if x >= high(dbgBP):
-    #debugOut("[Warning] cannot register breakpoint")
-    return
-  inc(dbgBPlen)
-  dbgBP[x].filename = filename
-  dbgBP[x].low = line
-  dbgBP[x].high = line
-  dbgBPbloom = dbgBPbloom or line
-
-proc addBreakpoint*(filename: cstring, lo, hi: int): bool =
-  let x = dbgBPlen
-  if x >= high(dbgBP): return false
-  inc(dbgBPlen)
-  result = true
-  dbgBP[x].filename = filename
-  dbgBP[x].low = lo
-  dbgBP[x].high = hi
-  for line in lo..hi: dbgBPbloom = dbgBPbloom or line
-
-const
-  FileSystemCaseInsensitive = defined(windows) or defined(dos) or defined(os2)
-
-proc fileMatches(c, bp: cstring): bool =
-  # bp = breakpoint filename
-  # c = current filename
-  # we consider it a match if bp is a suffix of c
-  # and the character for the suffix does not exist or
-  # is one of: \  /  :
-  # depending on the OS case does not matter!
-  var blen: int = bp.len
-  var clen: int = c.len
-  if blen > clen: return false
-  # check for \ /  :
-  if clen-blen-1 >= 0 and c[clen-blen-1] notin {'\\', '/', ':'}:
-    return false
-  var i = 0
-  while i < blen:
-    var x = bp[i]
-    var y = c[i+clen-blen]
-    when FileSystemCaseInsensitive:
-      if x >= 'A' and x <= 'Z': x = chr(ord(x) - ord('A') + ord('a'))
-      if y >= 'A' and y <= 'Z': y = chr(ord(y) - ord('A') + ord('a'))
-    if x != y: return false
-    inc(i)
-  return true
-
-proc canonFilename*(filename: cstring): cstring =
-  ## returns 'nil' if the filename cannot be found.
-  for i in 0 .. dbgFilenameLen-1:
-    result = dbgFilenames[i]
-    if fileMatches(result, filename): return result
-  result = nil
-
-iterator listBreakpoints*(): ptr Breakpoint =
-  ## lists all breakpoints.
-  for i in 0..dbgBPlen-1: yield addr(dbgBP[i])
-
-proc isActive*(b: ptr Breakpoint): bool = b.low > 0
-proc flip*(b: ptr Breakpoint) =
-  ## enables or disables 'b' depending on its current state.
-  b.low = -b.low; b.high = -b.high
-
-proc checkBreakpoints*(filename: cstring, line: int): ptr Breakpoint =
-  ## in which breakpoint (if any) we are.
-  if (dbgBPbloom and line) != line: return nil
-  for b in listBreakpoints():
-    if line >= b.low and line <= b.high and filename == b.filename: return b
-
-# ------------------- watchpoint support ------------------------------------
-
-type
-  Hash = int
-  Watchpoint {.pure, final.} = object
-    name: cstring
-    address: pointer
-    typ: PNimType
-    oldValue: Hash
-{.deprecated: [THash: Hash, TWatchpoint: Watchpoint].}
-
-var
-  watchpoints: array[0..99, Watchpoint]
-  watchpointsLen: int
-
-proc `!&`(h: Hash, val: int): Hash {.inline.} =
-  result = h +% val
-  result = result +% result shl 10
-  result = result xor (result shr 6)
-
-proc `!$`(h: Hash): Hash {.inline.} =
-  result = h +% h shl 3
-  result = result xor (result shr 11)
-  result = result +% result shl 15
-
-proc hash(data: pointer, size: int): Hash =
-  var h: Hash = 0
-  var p = cast[cstring](data)
-  var i = 0
-  var s = size
-  while s > 0:
-    h = h !& ord(p[i])
-    inc(i)
-    dec(s)
-  result = !$h
-
-proc hashGcHeader(data: pointer): Hash =
-  const headerSize = sizeof(int)*2
-  result = hash(cast[pointer](cast[int](data) -% headerSize), headerSize)
-
-proc genericHashAux(dest: pointer, mt: PNimType, shallow: bool,
-                    h: Hash): Hash
-proc genericHashAux(dest: pointer, n: ptr TNimNode, shallow: bool,
-                    h: Hash): Hash =
-  var d = cast[ByteAddress](dest)
-  case n.kind
-  of nkSlot:
-    result = genericHashAux(cast[pointer](d +% n.offset), n.typ, shallow, h)
-  of nkList:
-    result = h
-    for i in 0..n.len-1:
-      result = result !& genericHashAux(dest, n.sons[i], shallow, result)
-  of nkCase:
-    result = h !& hash(cast[pointer](d +% n.offset), n.typ.size)
-    var m = selectBranch(dest, n)
-    if m != nil: result = genericHashAux(dest, m, shallow, result)
-  of nkNone: sysAssert(false, "genericHashAux")
-
-proc genericHashAux(dest: pointer, mt: PNimType, shallow: bool,
-                    h: Hash): Hash =
-  sysAssert(mt != nil, "genericHashAux 2")
-  case mt.kind
-  of tyString:
-    var x = cast[PPointer](dest)[]
-    result = h
-    if x != nil:
-      let s = cast[NimString](x)
-      when defined(trackGcHeaders):
-        result = result !& hashGcHeader(x)
-      else:
-        result = result !& hash(x, s.len)
-  of tySequence:
-    var x = cast[PPointer](dest)
-    var dst = cast[ByteAddress](cast[PPointer](dest)[])
-    result = h
-    if dst != 0:
-      when defined(trackGcHeaders):
-        result = result !& hashGcHeader(cast[PPointer](dest)[])
-      else:
-        for i in 0..cast[PGenericSeq](dst).len-1:
-          result = result !& genericHashAux(
-            cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
-            mt.base, shallow, result)
-  of tyObject, tyTuple:
-    # we don't need to copy m_type field for tyObject, as they are equal anyway
-    result = genericHashAux(dest, mt.node, shallow, h)
-  of tyArray, tyArrayConstr:
-    let d = cast[ByteAddress](dest)
-    result = h
-    for i in 0..(mt.size div mt.base.size)-1:
-      result = result !& genericHashAux(cast[pointer](d +% i*% mt.base.size),
-                                        mt.base, shallow, result)
-  of tyRef:
-    when defined(trackGcHeaders):
-      var s = cast[PPointer](dest)[]
-      if s != nil:
-        result = result !& hashGcHeader(s)
-    else:
-      if shallow:
-        result = h !& hash(dest, mt.size)
-      else:
-        result = h
-        var s = cast[PPointer](dest)[]
-        if s != nil:
-          result = result !& genericHashAux(s, mt.base, shallow, result)
-  else:
-    result = h !& hash(dest, mt.size) # hash raw bits
-
-proc genericHash(dest: pointer, mt: PNimType): int =
-  result = genericHashAux(dest, mt, false, 0)
-
-proc dbgRegisterWatchpoint(address: pointer, name: cstring,
-                           typ: PNimType) {.compilerproc.} =
-  let L = watchPointsLen
-  for i in 0 .. pred(L):
-    if watchPoints[i].name == name:
-      # address may have changed:
-      watchPoints[i].address = address
-      return
-  if L >= watchPoints.high:
-    #debugOut("[Warning] cannot register watchpoint")
-    return
-  watchPoints[L].name = name
-  watchPoints[L].address = address
-  watchPoints[L].typ = typ
-  watchPoints[L].oldValue = genericHash(address, typ)
-  inc watchPointsLen
-
-proc dbgUnregisterWatchpoints*() =
-  watchPointsLen = 0
-
-var
-  dbgLineHook*: proc () {.nimcall.}
-    ## set this variable to provide a procedure that should be called before
-    ## each executed instruction. This should only be used by debuggers!
-    ## Only code compiled with the ``debugger:on`` switch calls this hook.
-
-  dbgWatchpointHook*: proc (watchpointName: cstring) {.nimcall.}
-
-proc checkWatchpoints =
-  let L = watchPointsLen
-  for i in 0 .. pred(L):
-    let newHash = genericHash(watchPoints[i].address, watchPoints[i].typ)
-    if newHash != watchPoints[i].oldValue:
-      dbgWatchpointHook(watchPoints[i].name)
-      watchPoints[i].oldValue = newHash
-
-proc endb(line: int, file: cstring) {.compilerproc, noinline.} =
-  # This proc is called before every Nim code line!
-  if framePtr == nil: return
-  if dbgWatchpointHook != nil: checkWatchpoints()
-  framePtr.line = line # this is done here for smaller code size!
-  framePtr.filename = file
-  if dbgLineHook != nil: dbgLineHook()
-
-include "system/endb"
diff --git a/lib/system/deepcopy.nim b/lib/system/deepcopy.nim
index 750da00cf..72d35f518 100644
--- a/lib/system/deepcopy.nim
+++ b/lib/system/deepcopy.nim
@@ -7,10 +7,13 @@
 #    distribution, for details about the copyright.
 #
 
+const
+  TableSize = when sizeof(int) <= 2: 0xff else: 0xff_ffff
+
 type
   PtrTable = ptr object
     counter, max: int
-    data: array[0xff_ffff, (pointer, pointer)]
+    data: array[TableSize, (pointer, pointer)]
 
 template hashPtr(key: pointer): int = cast[int](key) shr 8
 template allocPtrTable: untyped =
@@ -58,8 +61,8 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType;
 proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode;
                         tab: var PtrTable) {.benign.} =
   var
-    d = cast[ByteAddress](dest)
-    s = cast[ByteAddress](src)
+    d = cast[int](dest)
+    s = cast[int](src)
   case n.kind
   of nkSlot:
     genericDeepCopyAux(cast[pointer](d +% n.offset),
@@ -82,33 +85,40 @@ proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode;
 
 proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) =
   var
-    d = cast[ByteAddress](dest)
-    s = cast[ByteAddress](src)
+    d = cast[int](dest)
+    s = cast[int](src)
   sysAssert(mt != nil, "genericDeepCopyAux 2")
   case mt.kind
   of tyString:
-    var x = cast[PPointer](dest)
-    var s2 = cast[PPointer](s)[]
-    if s2 == nil:
-      unsureAsgnRef(x, s2)
+    when defined(nimSeqsV2):
+      var x = cast[ptr NimStringV2](dest)
+      var s2 = cast[ptr NimStringV2](s)[]
+      nimAsgnStrV2(x[], s2)
     else:
-      unsureAsgnRef(x, copyDeepString(cast[NimString](s2)))
+      var x = cast[PPointer](dest)
+      var s2 = cast[PPointer](s)[]
+      if s2 == nil:
+        unsureAsgnRef(x, s2)
+      else:
+        unsureAsgnRef(x, copyDeepString(cast[NimString](s2)))
   of tySequence:
-    var s2 = cast[PPointer](src)[]
-    var seq = cast[PGenericSeq](s2)
-    var x = cast[PPointer](dest)
-    if s2 == nil:
-      unsureAsgnRef(x, s2)
-      return
-    sysAssert(dest != nil, "genericDeepCopyAux 3")
-    unsureAsgnRef(x, newSeq(mt, seq.len))
-    var dst = cast[ByteAddress](cast[PPointer](dest)[])
-    for i in 0..seq.len-1:
-      genericDeepCopyAux(
-        cast[pointer](dst +% i *% mt.base.size +% GenericSeqSize),
-        cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
-                     GenericSeqSize),
-        mt.base, tab)
+    when defined(nimSeqsV2):
+      deepSeqAssignImpl(genericDeepCopyAux, tab)
+    else:
+      var s2 = cast[PPointer](src)[]
+      var seq = cast[PGenericSeq](s2)
+      var x = cast[PPointer](dest)
+      if s2 == nil:
+        unsureAsgnRef(x, s2)
+        return
+      sysAssert(dest != nil, "genericDeepCopyAux 3")
+      unsureAsgnRef(x, newSeq(mt, seq.len))
+      var dst = cast[int](cast[PPointer](dest)[])
+      for i in 0..seq.len-1:
+        genericDeepCopyAux(
+          cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size),
+          cast[pointer](cast[int](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size),
+          mt.base, tab)
   of tyObject:
     # we need to copy m_type field for tyObject, as it could be empty for
     # sequence reallocations:
@@ -124,13 +134,16 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) =
     for i in 0..(mt.size div mt.base.size)-1:
       genericDeepCopyAux(cast[pointer](d +% i *% mt.base.size),
                          cast[pointer](s +% i *% mt.base.size), mt.base, tab)
-  of tyRef, tyOptAsRef:
+  of tyRef:
     let s2 = cast[PPointer](src)[]
     if s2 == nil:
       unsureAsgnRef(cast[PPointer](dest), s2)
     elif mt.base.deepcopy != nil:
       let z = mt.base.deepcopy(s2)
-      unsureAsgnRef(cast[PPointer](dest), z)
+      when defined(nimSeqsV2):
+        cast[PPointer](dest)[] = z
+      else:
+        unsureAsgnRef(cast[PPointer](dest), z)
     else:
       let z = tab.get(s2)
       if z == nil:
@@ -147,10 +160,16 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) =
             let x = usrToCell(s2)
             let realType = x.typ
             sysAssert realType == mt, " types do differ"
-          # this version should work for any possible GC:
-          let typ = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[] else: mt.base
-          let z = newObj(mt, typ.size)
-          unsureAsgnRef(cast[PPointer](dest), z)
+          when defined(nimSeqsV2):
+            let typ = if mt.base.kind == tyObject: cast[PNimType](cast[ptr PNimTypeV2](s2)[].typeInfoV1)
+                      else: mt.base
+            let z = nimNewObj(typ.size, typ.align)
+            cast[PPointer](dest)[] = z
+          else:
+            # this version should work for any other GC:
+            let typ = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[] else: mt.base
+            let z = newObj(mt, typ.size)
+            unsureAsgnRef(cast[PPointer](dest), z)
           tab.put(s2, z)
           genericDeepCopyAux(z, s2, typ, tab)
       else:
@@ -165,14 +184,14 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) =
   else:
     copyMem(dest, src, mt.size)
 
-proc genericDeepCopy(dest, src: pointer, mt: PNimType) {.compilerProc.} =
-  GC_disable()
+proc genericDeepCopy(dest, src: pointer, mt: PNimType) {.compilerproc.} =
+  when not defined(nimSeqsV2): GC_disable()
   var tab = initPtrTable()
   genericDeepCopyAux(dest, src, mt, tab)
   deinit tab
-  GC_enable()
+  when not defined(nimSeqsV2): GC_enable()
 
-proc genericSeqDeepCopy(dest, src: pointer, mt: PNimType) {.compilerProc.} =
+proc genericSeqDeepCopy(dest, src: pointer, mt: PNimType) {.compilerproc.} =
   # also invoked for 'string'
   var src = src
   genericDeepCopy(dest, addr(src), mt)
@@ -180,8 +199,8 @@ proc genericSeqDeepCopy(dest, src: pointer, mt: PNimType) {.compilerProc.} =
 proc genericDeepCopyOpenArray(dest, src: pointer, len: int,
                             mt: PNimType) {.compilerproc.} =
   var
-    d = cast[ByteAddress](dest)
-    s = cast[ByteAddress](src)
+    d = cast[int](dest)
+    s = cast[int](src)
   for i in 0..len-1:
     genericDeepCopy(cast[pointer](d +% i *% mt.base.size),
                     cast[pointer](s +% i *% mt.base.size), mt.base)
diff --git a/lib/system/dollars.nim b/lib/system/dollars.nim
new file mode 100644
index 000000000..89a739d5a
--- /dev/null
+++ b/lib/system/dollars.nim
@@ -0,0 +1,147 @@
+## `$` is Nim's general way of spelling `toString`:idx:.
+runnableExamples:
+  assert $0.1 == "0.1"
+  assert $(-2*3) == "-6"
+
+import std/private/[digitsutils, miscdollars]
+
+when not defined(nimPreviewSlimSystem):
+  import std/formatfloat
+  export addFloat
+
+  func `$`*(x: float | float32): string =
+    ## Outplace version of `addFloat`.
+    result.addFloat(x)
+
+proc `$`*(x: int): string {.raises: [].} =
+  ## Outplace version of `addInt`.
+  result.addInt(x)
+
+proc `$`*(x: int64): string {.raises: [].} =
+  ## Outplace version of `addInt`.
+  result.addInt(x)
+
+proc `$`*(x: uint64): string {.raises: [].} =
+  ## Outplace version of `addInt`.
+  addInt(result, x)
+
+# same as old `ctfeWhitelist` behavior, whether or not this is a good idea.
+template gen(T) =
+  # xxx simplify this by supporting this in compiler: int{lit} | uint64{lit} | int64{lit}
+  func `$`*(x: T{lit}): string {.compileTime.} = result.addInt(x)
+gen(int)
+gen(uint64)
+gen(int64)
+
+
+proc `$`*(x: bool): string {.magic: "BoolToStr", noSideEffect.}
+  ## The stringify operator for a boolean argument. Returns `x`
+  ## converted to the string "false" or "true".
+
+proc `$`*(x: char): string {.magic: "CharToStr", noSideEffect.}
+  ## The stringify operator for a character argument. Returns `x`
+  ## converted to a string.
+  ##   ```Nim
+  ##   assert $'c' == "c"
+  ##   ```
+
+proc `$`*(x: cstring): string {.magic: "CStrToStr", noSideEffect.}
+  ## The stringify operator for a CString argument. Returns `x`
+  ## converted to a string.
+
+proc `$`*(x: string): string {.magic: "StrToStr", noSideEffect.}
+  ## The stringify operator for a string argument. Returns `x`
+  ## as it is. This operator is useful for generic code, so
+  ## that `$expr` also works if `expr` is already a string.
+
+proc `$`*[Enum: enum](x: Enum): string {.magic: "EnumToStr", noSideEffect.}
+  ## The stringify operator for an enumeration argument. This works for
+  ## any enumeration type thanks to compiler magic.
+  ##
+  ## If a `$` operator for a concrete enumeration is provided, this is
+  ## used instead. (In other words: *Overwriting* is possible.)
+
+proc `$`*(t: typedesc): string {.magic: "TypeTrait".}
+  ## Returns the name of the given type.
+  ##
+  ## For more procedures dealing with `typedesc`, see
+  ## `typetraits module <typetraits.html>`_.
+  ##
+  ##   ```Nim
+  ##   doAssert $(typeof(42)) == "int"
+  ##   doAssert $(typeof("Foo")) == "string"
+  ##   static: doAssert $(typeof(@['A', 'B'])) == "seq[char]"
+  ##   ```
+
+proc `$`*[T: tuple](x: T): string =
+  ## Generic `$` operator for tuples that is lifted from the components
+  ## of `x`. Example:
+  ##   ```Nim
+  ##   $(23, 45) == "(23, 45)"
+  ##   $(a: 23, b: 45) == "(a: 23, b: 45)"
+  ##   $() == "()"
+  ##   ```
+  tupleObjectDollar(result, x)
+
+when not defined(nimPreviewSlimSystem):
+  import std/objectdollar
+  export objectdollar
+
+proc collectionToString[T](x: T, prefix, separator, suffix: string): string =
+  result = prefix
+  var firstElement = true
+  for value in items(x):
+    if firstElement:
+      firstElement = false
+    else:
+      result.add(separator)
+
+    when value isnot string and value isnot seq and compiles(value.isNil):
+      # this branch should not be necessary
+      if value.isNil:
+        result.add "nil"
+      else:
+        result.addQuoted(value)
+    else:
+      result.addQuoted(value)
+  result.add(suffix)
+
+proc `$`*[T](x: set[T]): string =
+  ## Generic `$` operator for sets that is lifted from the components
+  ## of `x`. Example:
+  ##   ```Nim
+  ##   ${23, 45} == "{23, 45}"
+  ##   ```
+  collectionToString(x, "{", ", ", "}")
+
+proc `$`*[T](x: seq[T]): string =
+  ## Generic `$` operator for seqs that is lifted from the components
+  ## of `x`. Example:
+  ##   ```Nim
+  ##   $(@[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:
+  ##   ```Nim
+  ##  $(1 .. 5) == "1 .. 5"
+  ##  ```
+  result = $x.a
+  result.add(" .. ")
+  result.add($x.b)
+
+
+when not defined(nimNoArrayToString):
+  proc `$`*[T, IDX](x: array[IDX, T]): string =
+    ## Generic `$` operator for arrays that is lifted from the components.
+    collectionToString(x, "[", ", ", "]")
+
+proc `$`*[T](x: openArray[T]): string =
+  ## Generic `$` operator for openarrays that is lifted from the components
+  ## of `x`. Example:
+  ##   ```Nim
+  ##   $(@[23, 45].toOpenArray(0, 1)) == "[23, 45]"
+  ##   ```
+  collectionToString(x, "[", ", ", "]")
diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim
index f1ff307da..2162b234f 100644
--- a/lib/system/dyncalls.nim
+++ b/lib/system/dyncalls.nim
@@ -17,35 +17,44 @@
 const
   NilLibHandle: LibHandle = nil
 
-proc c_fwrite(buf: pointer, size, n: csize, f: File): cint {.
-  importc: "fwrite", header: "<stdio.h>".}
-
-proc rawWrite(f: File, s: string) =
-  # we cannot throw an exception here!
-  discard c_fwrite(cstring(s), 1, s.len, f)
-
 proc nimLoadLibraryError(path: string) =
   # carefully written to avoid memory allocation:
-  stderr.rawWrite("could not load: ")
-  stderr.rawWrite(path)
-  stderr.rawWrite("\n")
+  const prefix = "could not load: "
+  cstderr.rawWrite(prefix)
+  cstderr.rawWrite(path)
   when not defined(nimDebugDlOpen) and not defined(windows):
-    stderr.rawWrite("compile with -d:nimDebugDlOpen for more information\n")
-  when defined(windows) and defined(guiapp):
-    # Because console output is not shown in GUI apps, display error as message box:
-    const prefix = "could not load: "
-    var msg: array[1000, char]
-    copyMem(msg[0].addr, prefix.cstring, prefix.len)
-    copyMem(msg[prefix.len].addr, path.cstring, min(path.len + 1, 1000 - prefix.len))
-    discard MessageBoxA(0, msg[0].addr, nil, 0)
-  quit(1)
-
-proc procAddrError(name: cstring) {.noinline.} =
+    cstderr.rawWrite("\n(compile with -d:nimDebugDlOpen for more information)")
+  when defined(windows):
+    const badExe = "\n(bad format; library may be wrong architecture)"
+    let loadError = GetLastError()
+    if loadError == ERROR_BAD_EXE_FORMAT:
+      cstderr.rawWrite(badExe)
+    when defined(guiapp):
+      # Because console output is not shown in GUI apps, display the error as a
+      # message box instead:
+      var
+        msg: array[1000, char]
+        msgLeft = msg.len - 1 # leave (at least) one for nullchar
+        msgIdx = 0
+      copyMem(msg[msgIdx].addr, prefix.cstring, prefix.len)
+      msgLeft -= prefix.len
+      msgIdx += prefix.len
+      let pathLen = min(path.len, msgLeft)
+      copyMem(msg[msgIdx].addr, path.cstring, pathLen)
+      msgLeft -= pathLen
+      msgIdx += pathLen
+      if loadError == ERROR_BAD_EXE_FORMAT and msgLeft >= badExe.len:
+        copyMem(msg[msgIdx].addr, badExe.cstring, badExe.len)
+      discard MessageBoxA(nil, msg[0].addr, nil, 0)
+  cstderr.rawWrite("\n")
+  rawQuit(1)
+
+proc procAddrError(name: cstring) {.compilerproc, nonReloadable, hcrInline.} =
   # carefully written to avoid memory allocation:
-  stderr.rawWrite("could not import: ")
-  stderr.write(name)
-  stderr.rawWrite("\n")
-  quit(1)
+  cstderr.rawWrite("could not import: ")
+  cstderr.rawWrite(name)
+  cstderr.rawWrite("\n")
+  rawQuit(1)
 
 # this code was inspired from Lua's source code:
 # Lua - An Extensible Extension Language
@@ -82,12 +91,15 @@ when defined(posix):
     dlclose(lib)
 
   proc nimLoadLibrary(path: string): LibHandle =
-    result = dlopen(path, RTLD_NOW)
+    let flags =
+      when defined(globalSymbols): RTLD_NOW or RTLD_GLOBAL
+      else: RTLD_NOW
+    result = dlopen(path, flags)
     when defined(nimDebugDlOpen):
       let error = dlerror()
       if error != nil:
-        stderr.write(error)
-        stderr.rawWrite("\n")
+        cstderr.rawWrite(error)
+        cstderr.rawWrite("\n")
 
   proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
     result = dlsym(lib, name)
@@ -125,11 +137,11 @@ elif defined(windows) or defined(dos):
   proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
     result = getProcAddress(cast[THINSTANCE](lib), name)
     if result != nil: return
-    const decorated_length = 250
-    var decorated: array[decorated_length, char]
+    const decoratedLength = 250
+    var decorated: array[decoratedLength, char]
     decorated[0] = '_'
     var m = 1
-    while m < (decorated_length - 5):
+    while m < (decoratedLength - 5):
       if name[m - 1] == '\x00': break
       decorated[m] = name[m - 1]
       inc(m)
@@ -149,23 +161,38 @@ elif defined(windows) or defined(dos):
         dec(m)
         k = k div 10
         if k == 0: break
-      when defined(nimNoArrayToCstringConversion):
-        result = getProcAddress(cast[THINSTANCE](lib), addr decorated)
-      else:
-        result = getProcAddress(cast[THINSTANCE](lib), decorated)
+      result = getProcAddress(cast[THINSTANCE](lib), cast[cstring](addr decorated))
       if result != nil: return
     procAddrError(name)
 
 elif defined(genode):
 
-  proc nimUnloadLibrary(lib: LibHandle) {.
-    error: "nimUnloadLibrary not implemented".}
+  proc nimUnloadLibrary(lib: LibHandle) =
+    raiseAssert("nimUnloadLibrary not implemented")
 
-  proc nimLoadLibrary(path: string): LibHandle {.
-    error: "nimLoadLibrary not implemented".}
+  proc nimLoadLibrary(path: string): LibHandle =
+    raiseAssert("nimLoadLibrary not implemented")
+
+  proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
+    raiseAssert("nimGetProcAddr not implemented")
 
-  proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {.
-    error: "nimGetProcAddr not implemented".}
+elif defined(nintendoswitch) or defined(freertos) or defined(zephyr) or defined(nuttx):
+  proc nimUnloadLibrary(lib: LibHandle) =
+    cstderr.rawWrite("nimUnLoadLibrary not implemented")
+    cstderr.rawWrite("\n")
+    rawQuit(1)
+
+  proc nimLoadLibrary(path: string): LibHandle =
+    cstderr.rawWrite("nimLoadLibrary not implemented")
+    cstderr.rawWrite("\n")
+    rawQuit(1)
+
+
+  proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
+    cstderr.rawWrite("nimGetProAddr not implemented")
+    cstderr.rawWrite(name)
+    cstderr.rawWrite("\n")
+    rawQuit(1)
 
 else:
   {.error: "no implementation for dyncalls".}
diff --git a/lib/system/embedded.nim b/lib/system/embedded.nim
index 4d453fcca..ea6776f58 100644
--- a/lib/system/embedded.nim
+++ b/lib/system/embedded.nim
@@ -19,8 +19,9 @@ proc nimFrame(s: PFrame) {.compilerRtl, inl, exportc: "nimFrame".} = discard
 proc popFrame {.compilerRtl, inl.} = discard
 
 proc setFrame(s: PFrame) {.compilerRtl, inl.} = discard
-proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} = discard
-proc popSafePoint {.compilerRtl, inl.} = discard
+when not gotoBasedExceptions:
+  proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} = discard
+  proc popSafePoint {.compilerRtl, inl.} = discard
 proc pushCurrentException(e: ref Exception) {.compilerRtl, inl.} = discard
 proc popCurrentException {.compilerRtl, inl.} = discard
 
@@ -29,18 +30,32 @@ const
   nativeStackTraceSupported = false
   hasSomeStackTrace = false
 
-proc quitOrDebug() {.inline.} =
-  quit(1)
+proc quitOrDebug() {.noreturn, importc: "abort", header: "<stdlib.h>", nodecl.}
 
 proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
-  sysFatal(ReraiseError, "exception handling is not available")
+  sysFatal(ReraiseDefect, "exception handling is not available")
+
+proc raiseExceptionEx(e: sink(ref Exception), ename, procname, filename: cstring,
+                      line: int) {.compilerRtl.} =
+  sysFatal(ReraiseDefect, "exception handling is not available")
 
 proc reraiseException() {.compilerRtl.} =
-  sysFatal(ReraiseError, "no exception to reraise")
+  sysFatal(ReraiseDefect, "no exception to reraise")
 
 proc writeStackTrace() = discard
 
+proc unsetControlCHook() = discard
 proc setControlCHook(hook: proc () {.noconv.}) = discard
 
 proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} =
-  sysFatal(ReraiseError, "exception handling is not available")
+  sysFatal(ReraiseDefect, "exception handling is not available")
+
+when gotoBasedExceptions:
+  var nimInErrorMode {.threadvar.}: bool
+
+  proc nimErrorFlag(): ptr bool {.compilerRtl, inl.} =
+    result = addr(nimInErrorMode)
+
+  proc nimTestErrorFlag() {.compilerRtl.} =
+    if nimInErrorMode:
+      sysFatal(ReraiseDefect, "exception handling is not available")
diff --git a/lib/system/endb.nim b/lib/system/endb.nim
deleted file mode 100644
index d51ae29df..000000000
--- a/lib/system/endb.nim
+++ /dev/null
@@ -1,558 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2013 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-# This file implements the embedded debugger that can be linked
-# with the application. Mostly we do not use dynamic memory here as that
-# would interfere with the GC and trigger ON/OFF errors if the
-# user program corrupts memory. Unfortunately, for dispaying
-# variables we use the ``system.repr()`` proc which uses Nim
-# strings and thus allocates memory from the heap. Pity, but
-# I do not want to implement ``repr()`` twice.
-
-const
-  EndbBeg = "*** endb"
-  EndbEnd = "***\n"
-
-type
-  StaticStr = object
-    len: int
-    data: array[0..100, char]
-
-  BreakpointFilename = object
-    b: ptr Breakpoint
-    filename: StaticStr
-
-  DbgState = enum
-    dbOff,        # debugger is turned off
-    dbStepInto,   # debugger is in tracing mode
-    dbStepOver,
-    dbSkipCurrent,
-    dbQuiting,    # debugger wants to quit
-    dbBreakpoints # debugger is only interested in breakpoints
-{.deprecated: [TStaticStr: StaticStr, TBreakpointFilename: BreakpointFilename,
-              TDbgState: DbgState].}
-
-var
-  dbgUser: StaticStr    # buffer for user input; first command is ``step_into``
-                        # needs to be global cause we store the last command
-                        # in it
-  dbgState: DbgState    # state of debugger
-  dbgSkipToFrame: PFrame # frame to be skipped to
-
-  maxDisplayRecDepth: int = 5 # do not display too much data!
-
-  brkPoints: array[0..127, BreakpointFilename]
-
-proc setLen(s: var StaticStr, newLen=0) =
-  s.len = newLen
-  s.data[newLen] = '\0'
-
-proc add(s: var StaticStr, c: char) =
-  if s.len < high(s.data)-1:
-    s.data[s.len] = c
-    s.data[s.len+1] = '\0'
-    inc s.len
-
-proc add(s: var StaticStr, c: cstring) =
-  var i = 0
-  while c[i] != '\0':
-    add s, c[i]
-    inc i
-
-proc assign(s: var StaticStr, c: cstring) =
-  setLen(s)
-  add s, c
-
-proc `==`(a, b: StaticStr): bool =
-  if a.len == b.len:
-    for i in 0 .. a.len-1:
-      if a.data[i] != b.data[i]: return false
-    return true
-
-proc `==`(a: StaticStr, b: cstring): bool =
-  result = c_strcmp(unsafeAddr a.data, b) == 0
-
-proc write(f: File, s: StaticStr) =
-  write(f, cstring(unsafeAddr s.data))
-
-proc listBreakPoints() =
-  write(stdout, EndbBeg)
-  write(stdout, "| Breakpoints:\n")
-  for b in listBreakpoints():
-    write(stdout, abs(b.low))
-    if b.high != b.low:
-      write(stdout, "..")
-      write(stdout, abs(b.high))
-    write(stdout, " ")
-    write(stdout, b.filename)
-    if b.isActive:
-      write(stdout, " [disabled]\n")
-    else:
-      write(stdout, "\n")
-  write(stdout, EndbEnd)
-
-proc openAppend(filename: cstring): File =
-  var p: pointer = fopen(filename, "ab")
-  if p != nil:
-    result = cast[File](p)
-    write(result, "----------------------------------------\n")
-
-proc dbgRepr(p: pointer, typ: PNimType): string =
-  var cl: ReprClosure
-  initReprClosure(cl)
-  cl.recDepth = maxDisplayRecDepth
-  # locks for the GC turned out to be a bad idea...
-  # inc(recGcLock)
-  result = ""
-  reprAux(result, p, typ, cl)
-  # dec(recGcLock)
-  deinitReprClosure(cl)
-
-proc writeVariable(stream: File, slot: VarSlot) =
-  write(stream, slot.name)
-  write(stream, " = ")
-  writeLine(stream, dbgRepr(slot.address, slot.typ))
-
-proc listFrame(stream: File, f: PFrame) =
-  write(stream, EndbBeg)
-  write(stream, "| Frame (")
-  write(stream, f.len)
-  write(stream, " slots):\n")
-  for i in 0 .. f.len-1:
-    writeLine(stream, getLocal(f, i).name)
-  write(stream, EndbEnd)
-
-proc listLocals(stream: File, f: PFrame) =
-  write(stream, EndbBeg)
-  write(stream, "| Frame (")
-  write(stream, f.len)
-  write(stream, " slots):\n")
-  for i in 0 .. f.len-1:
-    writeVariable(stream, getLocal(f, i))
-  write(stream, EndbEnd)
-
-proc listGlobals(stream: File) =
-  write(stream, EndbBeg)
-  write(stream, "| Globals:\n")
-  for i in 0 .. getGlobalLen()-1:
-    writeLine(stream, getGlobal(i).name)
-  write(stream, EndbEnd)
-
-proc debugOut(msg: cstring) =
-  # the *** *** markers are for easy recognition of debugger
-  # output for external frontends.
-  write(stdout, EndbBeg)
-  write(stdout, "| ")
-  write(stdout, msg)
-  write(stdout, EndbEnd)
-
-proc dbgFatal(msg: cstring) =
-  debugOut(msg)
-  dbgAborting = true # the debugger wants to abort
-  quit(1)
-
-proc dbgShowCurrentProc(dbgFramePointer: PFrame) =
-  if dbgFramePointer != nil:
-    write(stdout, "*** endb| now in proc: ")
-    write(stdout, dbgFramePointer.procname)
-    write(stdout, " ***\n")
-  else:
-    write(stdout, "*** endb| (proc name not available) ***\n")
-
-proc dbgShowExecutionPoint() =
-  write(stdout, "*** endb| ")
-  write(stdout, framePtr.filename)
-  write(stdout, "(")
-  write(stdout, framePtr.line)
-  write(stdout, ") ")
-  write(stdout, framePtr.procname)
-  write(stdout, " ***\n")
-
-proc scanAndAppendWord(src: cstring, a: var StaticStr, start: int): int =
-  result = start
-  # skip whitespace:
-  while src[result] in {'\t', ' '}: inc(result)
-  while true:
-    case src[result]
-    of 'a'..'z', '0'..'9': add(a, src[result])
-    of '_': discard # just skip it
-    of 'A'..'Z': add(a, chr(ord(src[result]) - ord('A') + ord('a')))
-    else: break
-    inc(result)
-
-proc scanWord(src: cstring, a: var StaticStr, start: int): int =
-  setlen(a)
-  result = scanAndAppendWord(src, a, start)
-
-proc scanFilename(src: cstring, a: var StaticStr, start: int): int =
-  result = start
-  setLen a
-  while src[result] in {'\t', ' '}: inc(result)
-  while src[result] notin {'\t', ' ', '\0'}:
-    add(a, src[result])
-    inc(result)
-
-proc scanNumber(src: cstring, a: var int, start: int): int =
-  result = start
-  a = 0
-  while src[result] in {'\t', ' '}: inc(result)
-  while true:
-    case src[result]
-    of '0'..'9': a = a * 10 + ord(src[result]) - ord('0')
-    of '_': discard # skip underscores (nice for long line numbers)
-    else: break
-    inc(result)
-
-proc dbgHelp() =
-  debugOut("""
-list of commands (see the manual for further help):
-              GENERAL
-h, help                 display this help message
-q, quit                 quit the debugger and the program
-<ENTER>                 repeat the previous debugger command
-              EXECUTING
-s, step                 single step, stepping into routine calls
-n, next                 single step, without stepping into routine calls
-f, skipcurrent          continue execution until the current routine finishes
-c, continue, r, run     continue execution until the next breakpoint
-i, ignore               continue execution, ignore all breakpoints
-              BREAKPOINTS
-b, break [fromline [toline]] [file]
-                        set a new breakpoint for line and file
-                        if line or file are omitted the current one is used
-breakpoints             display the entire breakpoint list
-toggle fromline [file]  enable or disable a breakpoint
-filenames               list all valid filenames
-              DATA DISPLAY
-e, eval <expr>          evaluate the expression <expr>
-o, out <file> <expr>    evaluate <expr> and write it to <file>
-w, where                display the current execution point
-stackframe [file]       display current stack frame [and write it to file]
-u, up                   go up in the call stack
-d, down                 go down in the call stack
-bt, backtrace           display the entire call stack
-l, locals               display available local variables
-g, globals              display available global variables
-maxdisplay <integer>    set the display's recursion maximum
-""")
-
-proc invalidCommand() =
-  debugOut("[Warning] invalid command ignored (type 'h' for help) ")
-
-proc hasExt(s: cstring): bool =
-  # returns true if s has a filename extension
-  var i = 0
-  while s[i] != '\0':
-    if s[i] == '.': return true
-    inc i
-
-proc parseBreakpoint(s: cstring, start: int): Breakpoint =
-  var dbgTemp: StaticStr
-  var i = scanNumber(s, result.low, start)
-  if result.low == 0: result.low = framePtr.line
-  i = scanNumber(s, result.high, i)
-  if result.high == 0: result.high = result.low
-  i = scanFilename(s, dbgTemp, i)
-  if dbgTemp.len != 0:
-    if not hasExt(addr dbgTemp.data): add(dbgTemp, ".nim")
-    result.filename = canonFilename(addr dbgTemp.data)
-    if result.filename.isNil:
-      debugOut("[Warning] no breakpoint could be set; unknown filename ")
-      return
-  else:
-    result.filename = framePtr.filename
-
-proc createBreakPoint(s: cstring, start: int) =
-  let br = parseBreakpoint(s, start)
-  if not br.filename.isNil:
-    if not addBreakpoint(br.filename, br.low, br.high):
-      debugOut("[Warning] no breakpoint could be set; out of breakpoint space ")
-
-proc breakpointToggle(s: cstring, start: int) =
-  var a = parseBreakpoint(s, start)
-  if not a.filename.isNil:
-    var b = checkBreakpoints(a.filename, a.low)
-    if not b.isNil: b.flip
-    else: debugOut("[Warning] unknown breakpoint ")
-
-proc dbgEvaluate(stream: File, s: cstring, start: int, f: PFrame) =
-  var dbgTemp: StaticStr
-  var i = scanWord(s, dbgTemp, start)
-  while s[i] in {' ', '\t'}: inc(i)
-  var v: VarSlot
-  if s[i] == '.':
-    inc(i)
-    add(dbgTemp, '.')
-    i = scanAndAppendWord(s, dbgTemp, i)
-    for i in 0 .. getGlobalLen()-1:
-      let v = getGlobal(i)
-      if c_strcmp(v.name, addr dbgTemp.data) == 0:
-        writeVariable(stream, v)
-  else:
-    for i in 0 .. f.len-1:
-      let v = getLocal(f, i)
-      if c_strcmp(v.name, addr dbgTemp.data) == 0:
-        writeVariable(stream, v)
-
-proc dbgOut(s: cstring, start: int, currFrame: PFrame) =
-  var dbgTemp: StaticStr
-  var i = scanFilename(s, dbgTemp, start)
-  if dbgTemp.len == 0:
-    invalidCommand()
-    return
-  var stream = openAppend(addr dbgTemp.data)
-  if stream == nil:
-    debugOut("[Warning] could not open or create file ")
-    return
-  dbgEvaluate(stream, s, i, currFrame)
-  close(stream)
-
-proc dbgStackFrame(s: cstring, start: int, currFrame: PFrame) =
-  var dbgTemp: StaticStr
-  var i = scanFilename(s, dbgTemp, start)
-  if dbgTemp.len == 0:
-    # just write it to stdout:
-    listFrame(stdout, currFrame)
-  else:
-    var stream = openAppend(addr dbgTemp.data)
-    if stream == nil:
-      debugOut("[Warning] could not open or create file ")
-      return
-    listFrame(stream, currFrame)
-    close(stream)
-
-proc readLine(f: File, line: var StaticStr): bool =
-  while true:
-    var c = c_fgetc(f)
-    if c < 0'i32:
-      if line.len > 0: break
-      else: return false
-    if c == 10'i32: break # LF
-    if c == 13'i32:  # CR
-      c = c_fgetc(f) # is the next char LF?
-      if c != 10'i32: discard c_ungetc(c, f) # no, put the character back
-      break
-    add line, chr(int(c))
-  result = true
-
-proc listFilenames() =
-  write(stdout, EndbBeg)
-  write(stdout, "| Files:\n")
-  var i = 0
-  while true:
-    let x = dbgFilenames[i]
-    if x.isNil: break
-    write(stdout, x)
-    write(stdout, "\n")
-    inc i
-  write(stdout, EndbEnd)
-
-proc dbgWriteStackTrace(f: PFrame)
-proc commandPrompt() =
-  # if we return from this routine, user code executes again
-  var
-    again = true
-    dbgFramePtr = framePtr # for going down and up the stack
-    dbgDown = 0 # how often we did go down
-    dbgTemp: StaticStr
-
-  while again:
-    write(stdout, "*** endb| >>")
-    let oldLen = dbgUser.len
-    dbgUser.len = 0
-    if not readLine(stdin, dbgUser): break
-    if dbgUser.len == 0: dbgUser.len = oldLen
-    # now look what we have to do:
-    var i = scanWord(addr dbgUser.data, dbgTemp, 0)
-    template `?`(x: untyped): untyped = dbgTemp == cstring(x)
-    if ?"s" or ?"step":
-      dbgState = dbStepInto
-      again = false
-    elif ?"n" or ?"next":
-      dbgState = dbStepOver
-      dbgSkipToFrame = framePtr
-      again = false
-    elif ?"f" or ?"skipcurrent":
-      dbgState = dbSkipCurrent
-      dbgSkipToFrame = framePtr.prev
-      again = false
-    elif ?"c" or ?"continue" or ?"r" or ?"run":
-      dbgState = dbBreakpoints
-      again = false
-    elif ?"i" or ?"ignore":
-      dbgState = dbOff
-      again = false
-    elif ?"h" or ?"help":
-      dbgHelp()
-    elif ?"q" or ?"quit":
-      dbgState = dbQuiting
-      dbgAborting = true
-      again = false
-      quit(1) # BUGFIX: quit with error code > 0
-    elif ?"e" or ?"eval":
-      var
-        prevState = dbgState
-        prevSkipFrame = dbgSkipToFrame
-      dbgState = dbSkipCurrent
-      dbgEvaluate(stdout, addr dbgUser.data, i, dbgFramePtr)
-      dbgState = prevState
-      dbgSkipToFrame = prevSkipFrame
-    elif ?"o" or ?"out":
-      dbgOut(addr dbgUser.data, i, dbgFramePtr)
-    elif ?"stackframe":
-      dbgStackFrame(addr dbgUser.data, i, dbgFramePtr)
-    elif ?"w" or ?"where":
-      dbgShowExecutionPoint()
-    elif ?"l" or ?"locals":
-      var
-        prevState = dbgState
-        prevSkipFrame = dbgSkipToFrame
-      dbgState = dbSkipCurrent
-      listLocals(stdout, dbgFramePtr)
-      dbgState = prevState
-      dbgSkipToFrame = prevSkipFrame
-    elif ?"g" or ?"globals":
-      var
-        prevState = dbgState
-        prevSkipFrame = dbgSkipToFrame
-      dbgState = dbSkipCurrent
-      listGlobals(stdout)
-      dbgState = prevState
-      dbgSkipToFrame = prevSkipFrame
-    elif ?"u" or ?"up":
-      if dbgDown <= 0:
-        debugOut("[Warning] cannot go up any further ")
-      else:
-        dbgFramePtr = framePtr
-        for j in 0 .. dbgDown-2: # BUGFIX
-          dbgFramePtr = dbgFramePtr.prev
-        dec(dbgDown)
-      dbgShowCurrentProc(dbgFramePtr)
-    elif ?"d" or ?"down":
-      if dbgFramePtr != nil:
-        inc(dbgDown)
-        dbgFramePtr = dbgFramePtr.prev
-        dbgShowCurrentProc(dbgFramePtr)
-      else:
-        debugOut("[Warning] cannot go down any further ")
-    elif ?"bt" or ?"backtrace":
-      dbgWriteStackTrace(framePtr)
-    elif ?"b" or ?"break":
-      createBreakPoint(addr dbgUser.data, i)
-    elif ?"breakpoints":
-      listBreakPoints()
-    elif ?"toggle":
-      breakpointToggle(addr dbgUser.data, i)
-    elif ?"filenames":
-      listFilenames()
-    elif ?"maxdisplay":
-      var parsed: int
-      i = scanNumber(addr dbgUser.data, parsed, i)
-      if dbgUser.data[i-1] in {'0'..'9'}:
-        if parsed == 0: maxDisplayRecDepth = -1
-        else: maxDisplayRecDepth = parsed
-      else:
-        invalidCommand()
-    else: invalidCommand()
-
-proc endbStep() =
-  # we get into here if an unhandled exception has been raised
-  # XXX: do not allow the user to run the program any further?
-  # XXX: BUG: the frame is lost here!
-  dbgShowExecutionPoint()
-  commandPrompt()
-
-proc dbgWriteStackTrace(f: PFrame) =
-  const
-    firstCalls = 32
-  var
-    it = f
-    i = 0
-    total = 0
-    tempFrames: array[0..127, PFrame]
-  # setup long head:
-  while it != nil and i <= high(tempFrames)-firstCalls:
-    tempFrames[i] = it
-    inc(i)
-    inc(total)
-    it = it.prev
-  # go up the stack to count 'total':
-  var b = it
-  while it != nil:
-    inc(total)
-    it = it.prev
-  var skipped = 0
-  if total > len(tempFrames):
-    # skip N
-    skipped = total-i-firstCalls+1
-    for j in 1..skipped:
-      if b != nil: b = b.prev
-    # create '...' entry:
-    tempFrames[i] = nil
-    inc(i)
-  # setup short tail:
-  while b != nil and i <= high(tempFrames):
-    tempFrames[i] = b
-    inc(i)
-    b = b.prev
-  for j in countdown(i-1, 0):
-    if tempFrames[j] == nil:
-      write(stdout, "(")
-      write(stdout, skipped)
-      write(stdout, " calls omitted) ...")
-    else:
-      write(stdout, tempFrames[j].filename)
-      if tempFrames[j].line > 0:
-        write(stdout, '(')
-        write(stdout, tempFrames[j].line)
-        write(stdout, ')')
-      write(stdout, ' ')
-      write(stdout, tempFrames[j].procname)
-    write(stdout, "\n")
-
-proc checkForBreakpoint =
-  let b = checkBreakpoints(framePtr.filename, framePtr.line)
-  if b != nil:
-    write(stdout, "*** endb| reached ")
-    write(stdout, framePtr.filename)
-    write(stdout, "(")
-    write(stdout, framePtr.line)
-    write(stdout, ") ")
-    write(stdout, framePtr.procname)
-    write(stdout, " ***\n")
-    commandPrompt()
-
-proc lineHookImpl() {.nimcall.} =
-  case dbgState
-  of dbStepInto:
-    # we really want the command prompt here:
-    dbgShowExecutionPoint()
-    commandPrompt()
-  of dbSkipCurrent, dbStepOver: # skip current routine
-    if framePtr == dbgSkipToFrame:
-      dbgShowExecutionPoint()
-      commandPrompt()
-    else:
-      # breakpoints are wanted though (I guess)
-      checkForBreakpoint()
-  of dbBreakpoints:
-    # debugger is only interested in breakpoints
-    checkForBreakpoint()
-  else: discard
-
-proc watchpointHookImpl(name: cstring) {.nimcall.} =
-  dbgWriteStackTrace(framePtr)
-  debugOut(name)
-
-proc initDebugger {.inline.} =
-  dbgState = dbStepInto
-  dbgUser.len = 1
-  dbgUser.data[0] = 's'
-  dbgWatchpointHook = watchpointHookImpl
-  dbgLineHook = lineHookImpl
diff --git a/lib/system/exceptions.nim b/lib/system/exceptions.nim
new file mode 100644
index 000000000..63588f858
--- /dev/null
+++ b/lib/system/exceptions.nim
@@ -0,0 +1,122 @@
+## Exception and effect types used in Nim code.
+
+type
+  TimeEffect* = object of RootEffect   ## Time effect.
+  IOEffect* = object of RootEffect     ## IO effect.
+  ReadIOEffect* = object of IOEffect   ## Effect describing a read IO operation.
+  WriteIOEffect* = object of IOEffect  ## Effect describing a write IO operation.
+  ExecIOEffect* = object of IOEffect   ## Effect describing an executing IO operation.
+
+type
+  IOError* = object of CatchableError ## \
+    ## Raised if an IO error occurred.
+  EOFError* = object of IOError ## \
+    ## Raised if an IO "end of file" error occurred.
+  OSError* = object of CatchableError ## \
+    ## Raised if an operating system service failed.
+    errorCode*: int32 ## OS-defined error code describing this error.
+  LibraryError* = object of OSError ## \
+    ## Raised if a dynamic library could not be loaded.
+  ResourceExhaustedError* = object of CatchableError ## \
+    ## Raised if a resource request could not be fulfilled.
+  ArithmeticDefect* = object of Defect ## \
+    ## Raised if any kind of arithmetic error occurred.
+  DivByZeroDefect* = object of ArithmeticDefect ## \
+    ## Raised for runtime integer divide-by-zero errors.
+
+  OverflowDefect* = object of ArithmeticDefect ## \
+    ## Raised for runtime integer overflows.
+    ##
+    ## This happens for calculations whose results are too large to fit in the
+    ## provided bits.
+  AccessViolationDefect* = object of Defect ## \
+    ## Raised for invalid memory access errors
+  AssertionDefect* = object of Defect ## \
+    ## Raised when assertion is proved wrong.
+    ##
+    ## Usually the result of using the `assert() template
+    ## <assertions.html#assert.t,untyped,string>`_.
+  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.
+    ##
+    ## Mostly used by the `tables <tables.html>`_ module, it can also be raised
+    ## by other collection modules like `sets <sets.html>`_ or `strtabs
+    ## <strtabs.html>`_.
+  OutOfMemDefect* = object of Defect ## \
+    ## Raised for unsuccessful attempts to allocate memory.
+  IndexDefect* = object of Defect ## \
+    ## Raised if an array index is out of bounds.
+
+  FieldDefect* = object of Defect ## \
+    ## Raised if a record field is not accessible because its discriminant's
+    ## value does not fit.
+  RangeDefect* = object of Defect ## \
+    ## Raised if a range check error occurred.
+  StackOverflowDefect* = object of Defect ## \
+    ## Raised if the hardware stack used for subroutine calls overflowed.
+  ReraiseDefect* = object of Defect ## \
+    ## Raised if there is no exception to reraise.
+  ObjectAssignmentDefect* = object of Defect ## \
+    ## Raised if an object gets assigned to its parent's object.
+  ObjectConversionDefect* = object of Defect ## \
+    ## Raised if an object is converted to an incompatible object type.
+    ## You can use `of` operator to check if conversion will succeed.
+  FloatingPointDefect* = object of Defect ## \
+    ## Base class for floating point exceptions.
+  FloatInvalidOpDefect* = object of FloatingPointDefect ## \
+    ## Raised by invalid operations according to IEEE.
+    ##
+    ## Raised by `0.0/0.0`, for example.
+  FloatDivByZeroDefect* = object of FloatingPointDefect ## \
+    ## Raised by division by zero.
+    ##
+    ## Divisor is zero and dividend is a finite nonzero number.
+  FloatOverflowDefect* = object of FloatingPointDefect ## \
+    ## Raised for overflows.
+    ##
+    ## The operation produced a result that exceeds the range of the exponent.
+  FloatUnderflowDefect* = object of FloatingPointDefect ## \
+    ## Raised for underflows.
+    ##
+    ## The operation produced a result that is too small to be represented as a
+    ## normal number.
+  FloatInexactDefect* = object of FloatingPointDefect ## \
+    ## Raised for inexact results.
+    ##
+    ## The operation produced a result that cannot be represented with infinite
+    ## precision -- for example: `2.0 / 3.0, log(1.1)`
+    ##
+    ## **Note**: Nim currently does not detect these!
+  DeadThreadDefect* = object of Defect ## \
+    ## Raised if it is attempted to send a message to a dead thread.
+  NilAccessDefect* = object of Defect ## \
+    ## Raised on dereferences of `nil` pointers.
+    ##
+    ## This is only raised if the `segfaults module <segfaults.html>`_ was imported!
+
+when not defined(nimPreviewSlimSystem):
+  type
+    ArithmeticError* {.deprecated: "See corresponding Defect".} = ArithmeticDefect
+    DivByZeroError* {.deprecated: "See corresponding Defect".} = DivByZeroDefect
+    OverflowError* {.deprecated: "See corresponding Defect".} = OverflowDefect
+    AccessViolationError* {.deprecated: "See corresponding Defect".} = AccessViolationDefect
+    AssertionError* {.deprecated: "See corresponding Defect".} = AssertionDefect
+    OutOfMemError* {.deprecated: "See corresponding Defect".} = OutOfMemDefect
+    IndexError* {.deprecated: "See corresponding Defect".} = IndexDefect
+
+    FieldError* {.deprecated: "See corresponding Defect".} = FieldDefect
+    RangeError* {.deprecated: "See corresponding Defect".} = RangeDefect
+    StackOverflowError* {.deprecated: "See corresponding Defect".} = StackOverflowDefect
+    ReraiseError* {.deprecated: "See corresponding Defect".} = ReraiseDefect
+    ObjectAssignmentError* {.deprecated: "See corresponding Defect".} = ObjectAssignmentDefect
+    ObjectConversionError* {.deprecated: "See corresponding Defect".} = ObjectConversionDefect
+    FloatingPointError* {.deprecated: "See corresponding Defect".} = FloatingPointDefect
+    FloatInvalidOpError* {.deprecated: "See corresponding Defect".} = FloatInvalidOpDefect
+    FloatDivByZeroError* {.deprecated: "See corresponding Defect".} = FloatDivByZeroDefect
+    FloatOverflowError* {.deprecated: "See corresponding Defect".} = FloatOverflowDefect
+    FloatUnderflowError* {.deprecated: "See corresponding Defect".} = FloatUnderflowDefect
+    FloatInexactError* {.deprecated: "See corresponding Defect".} = FloatInexactDefect
+    DeadThreadError* {.deprecated: "See corresponding Defect".} = DeadThreadDefect
+    NilAccessError* {.deprecated: "See corresponding Defect".} = NilAccessDefect
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index dabfe010e..dae5c4a4a 100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -10,34 +10,55 @@
 # Exception handling code. Carefully coded so that tiny programs which do not
 # use the heap (and nor exceptions) do not include the GC or memory allocator.
 
+import std/private/miscdollars
+import stacktraces
+
+const noStacktraceAvailable = "No stack traceback available\n"
+
 var
   errorMessageWriter*: (proc(msg: string) {.tags: [WriteIOEffect], benign,
                                             nimcall.})
     ## Function that will be called
-    ## instead of stdmsg.write when printing stacktrace.
+    ## instead of `stdmsg.write` when printing stacktrace.
     ## Unstable API.
 
-when not defined(windows) or not defined(guiapp):
-  proc writeToStdErr(msg: cstring) = write(stdmsg, msg)
+when defined(windows):
+  proc GetLastError(): int32 {.header: "<windows.h>", nodecl.}
+  const ERROR_BAD_EXE_FORMAT = 193
 
+when not defined(windows) or not defined(guiapp):
+  proc writeToStdErr(msg: cstring) = rawWrite(cstderr, msg)
+  proc writeToStdErr(msg: cstring, length: int) =
+    rawWriteString(cstderr, msg, length)
 else:
-  proc MessageBoxA(hWnd: cint, lpText, lpCaption: cstring, uType: int): int32 {.
+  proc MessageBoxA(hWnd: pointer, lpText, lpCaption: cstring, uType: int): int32 {.
     header: "<windows.h>", nodecl.}
-
   proc writeToStdErr(msg: cstring) =
-    discard MessageBoxA(0, msg, nil, 0)
+    discard MessageBoxA(nil, msg, nil, 0)
+  proc writeToStdErr(msg: cstring, length: int) =
+    discard MessageBoxA(nil, msg, nil, 0)
+
+proc writeToStdErr(msg: string) {.inline.} =
+  # fix bug #13115: handles correctly '\0' unlike default implicit conversion to cstring
+  writeToStdErr(msg.cstring, msg.len)
 
-proc showErrorMessage(data: cstring) {.gcsafe.} =
+proc showErrorMessage(data: cstring, length: int) {.gcsafe, raises: [].} =
+  var toWrite = true
   if errorMessageWriter != nil:
-    errorMessageWriter($data)
-  else:
-    writeToStdErr(data)
+    try:
+      errorMessageWriter($data)
+      toWrite = false
+    except:
+      discard
+  if toWrite:
+    when defined(genode):
+      # stderr not available by default, use the LOG session
+      echo data
+    else:
+      writeToStdErr(data, length)
 
-proc quitOrDebug() {.inline.} =
-  when not defined(endb):
-    quit(1)
-  else:
-    endbStep() # call the debugger
+proc showErrorMessage2(data: string) {.inline.} =
+  showErrorMessage(data.cstring, data.len)
 
 proc chckIndx(i, a, b: int): int {.inline, compilerproc, benign.}
 proc chckRange(i, a, b: int): int {.inline, compilerproc, benign.}
@@ -50,28 +71,47 @@ type
     len: int
     prev: ptr GcFrameHeader
 
+when NimStackTraceMsgs:
+  var frameMsgBuf* {.threadvar.}: string
+
+when not defined(nimV2):
+  var
+    framePtr {.threadvar.}: PFrame
+
 var
-  framePtr {.threadvar.}: PFrame
-  excHandler {.threadvar.}: PSafePoint
-    # list of exception handlers
-    # a global variable for the root of all try blocks
   currException {.threadvar.}: ref Exception
-  raise_counter {.threadvar.}: uint
 
-  gcFramePtr {.threadvar.}: GcFrame
+when not gotoBasedExceptions:
+  var
+    excHandler {.threadvar.}: PSafePoint
+      # list of exception handlers
+      # a global variable for the root of all try blocks
+    gcFramePtr {.threadvar.}: GcFrame
 
-type
-  FrameState = tuple[gcFramePtr: GcFrame, framePtr: PFrame,
-                     excHandler: PSafePoint, currException: ref Exception]
+when gotoBasedExceptions:
+  type
+    FrameState = tuple[framePtr: PFrame,
+                      currException: ref Exception]
+else:
+  type
+    FrameState = tuple[gcFramePtr: GcFrame, framePtr: PFrame,
+                      excHandler: PSafePoint, currException: ref Exception]
 
 proc getFrameState*(): FrameState {.compilerRtl, inl.} =
-  return (gcFramePtr, framePtr, excHandler, currException)
+  when gotoBasedExceptions:
+    return (framePtr, currException)
+  else:
+    return (gcFramePtr, framePtr, excHandler, currException)
 
 proc setFrameState*(state: FrameState) {.compilerRtl, inl.} =
-  gcFramePtr = state.gcFramePtr
-  framePtr = state.framePtr
-  excHandler = state.excHandler
-  currException = state.currException
+  when gotoBasedExceptions:
+    framePtr = state.framePtr
+    currException = state.currException
+  else:
+    gcFramePtr = state.gcFramePtr
+    framePtr = state.framePtr
+    excHandler = state.excHandler
+    currException = state.currException
 
 proc getFrame*(): PFrame {.compilerRtl, inl.} = framePtr
 
@@ -93,54 +133,44 @@ when false:
 proc setFrame*(s: PFrame) {.compilerRtl, inl.} =
   framePtr = s
 
-proc getGcFrame*(): GcFrame {.compilerRtl, inl.} = gcFramePtr
-proc popGcFrame*() {.compilerRtl, inl.} = gcFramePtr = gcFramePtr.prev
-proc setGcFrame*(s: GcFrame) {.compilerRtl, inl.} = gcFramePtr = s
-proc pushGcFrame*(s: GcFrame) {.compilerRtl, inl.} =
-  s.prev = gcFramePtr
-  zeroMem(cast[pointer](cast[int](s)+%sizeof(GcFrameHeader)), s.len*sizeof(pointer))
-  gcFramePtr = s
+when not gotoBasedExceptions:
+  proc getGcFrame*(): GcFrame {.compilerRtl, inl.} = gcFramePtr
+  proc popGcFrame*() {.compilerRtl, inl.} = gcFramePtr = gcFramePtr.prev
+  proc setGcFrame*(s: GcFrame) {.compilerRtl, inl.} = gcFramePtr = s
+  proc pushGcFrame*(s: GcFrame) {.compilerRtl, inl.} =
+    s.prev = gcFramePtr
+    zeroMem(cast[pointer](cast[int](s)+%sizeof(GcFrameHeader)), s.len*sizeof(pointer))
+    gcFramePtr = s
 
-proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
-  s.hasRaiseAction = false
-  s.prev = excHandler
-  excHandler = s
+  proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
+    s.prev = excHandler
+    excHandler = s
 
-proc popSafePoint {.compilerRtl, inl.} =
-  excHandler = excHandler.prev
+  proc popSafePoint {.compilerRtl, inl.} =
+    excHandler = excHandler.prev
 
-proc pushCurrentException(e: ref Exception) {.compilerRtl, inl.} =
+proc pushCurrentException(e: sink(ref Exception)) {.compilerRtl, inl.} =
   e.up = currException
   currException = e
+  #showErrorMessage2 "A"
 
 proc popCurrentException {.compilerRtl, inl.} =
   currException = currException.up
+  #showErrorMessage2 "B"
 
 proc popCurrentExceptionEx(id: uint) {.compilerRtl.} =
-  # in cpp backend exceptions can pop-up in the different order they were raised, example #5628
-  if currException.raise_id == id:
-    currException = currException.up
-  else:
-    var cur = currException.up
-    var prev = currException
-    while cur != nil and cur.raise_id != id:
-      prev = cur
-      cur = cur.up
-    if cur == nil:
-      showErrorMessage("popCurrentExceptionEx() exception was not found in the exception stack. Aborting...")
-      quitOrDebug()
-    prev.up = cur.up
+  discard "only for bootstrapping compatbility"
 
 proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} =
-  if not e.isNil:
-    currException = e
+  currException = e
 
 # some platforms have native support for stack traces:
 const
-  nativeStackTraceSupported* = (defined(macosx) or defined(linux)) and
+  nativeStackTraceSupported = (defined(macosx) or defined(linux)) and
                               not NimStackTrace
-  hasSomeStackTrace = NimStackTrace or
-    defined(nativeStackTrace) and nativeStackTraceSupported
+  hasSomeStackTrace = NimStackTrace or defined(nimStackTraceOverride) or
+    (defined(nativeStackTrace) and nativeStackTraceSupported)
+
 
 when defined(nativeStacktrace) and nativeStackTraceSupported:
   type
@@ -158,13 +188,13 @@ when defined(nativeStacktrace) and nativeStackTraceSupported:
 
   when not hasThreadSupport:
     var
-      tempAddresses: array[0..127, pointer] # should not be alloc'd on stack
+      tempAddresses: array[maxStackTraceLines, pointer] # should not be alloc'd on stack
       tempDlInfo: TDl_info
 
   proc auxWriteStackTraceWithBacktrace(s: var string) =
     when hasThreadSupport:
       var
-        tempAddresses: array[0..127, pointer] # but better than a threadvar
+        tempAddresses: array[maxStackTraceLines, pointer] # but better than a threadvar
         tempDlInfo: TDl_info
     # This is allowed to be expensive since it only happens during crashes
     # (but this way you don't need manual stack tracing)
@@ -190,13 +220,9 @@ when defined(nativeStacktrace) and nativeStackTraceSupported:
           # interested in
           enabled = true
 
-when not hasThreadSupport:
+when hasSomeStackTrace and not hasThreadSupport:
   var
-    tempFrames: array[0..127, PFrame] # should not be alloc'd on stack
-
-const
-  reraisedFromBegin = -10
-  reraisedFromEnd = -100
+    tempFrames: array[maxStackTraceLines, PFrame] # should not be alloc'd on stack
 
 template reraisedFrom(z): untyped =
   StackTraceEntry(procname: nil, line: z, filename: nil)
@@ -209,87 +235,106 @@ proc auxWriteStackTrace(f: PFrame; s: var seq[StackTraceEntry]) =
     inc(i)
     it = it.prev
   var last = i-1
-  if s.isNil:
-    s = newSeq[StackTraceEntry](i)
-  else:
-    last = s.len + i - 1
-    s.setLen(last+1)
+  when true: # not defined(gcDestructors):
+    if s.len == 0:
+      s = newSeq[StackTraceEntry](i)
+    else:
+      last = s.len + i - 1
+      s.setLen(last+1)
   it = f
   while it != nil:
     s[last] = StackTraceEntry(procname: it.procname,
                               line: it.line,
                               filename: it.filename)
+    when NimStackTraceMsgs:
+      let first = if it.prev == nil: 0 else: it.prev.frameMsgLen
+      if it.frameMsgLen > first:
+        s[last].frameMsg.setLen(it.frameMsgLen - first)
+        # somehow string slicing not available here
+        for i in first .. it.frameMsgLen-1:
+          s[last].frameMsg[i-first] = frameMsgBuf[i]
     it = it.prev
     dec last
 
-template addFrameEntry(s, f: untyped) =
+template addFrameEntry(s: var string, f: StackTraceEntry|PFrame) =
   var oldLen = s.len
-  add(s, f.filename)
-  if f.line > 0:
-    add(s, '(')
-    add(s, $f.line)
-    add(s, ')')
+  s.toLocation(f.filename, f.line, 0)
   for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
   add(s, f.procname)
+  when NimStackTraceMsgs:
+    when typeof(f) is StackTraceEntry:
+      add(s, f.frameMsg)
+    else:
+      var first = if f.prev == nil: 0 else: f.prev.frameMsgLen
+      for i in first..<f.frameMsgLen: add(s, frameMsgBuf[i])
   add(s, "\n")
 
-proc `$`(s: seq[StackTraceEntry]): string =
+proc `$`(stackTraceEntries: seq[StackTraceEntry]): string =
+  when defined(nimStackTraceOverride):
+    let s = addDebuggingInfo(stackTraceEntries)
+  else:
+    let s = stackTraceEntries
+
   result = newStringOfCap(2000)
   for i in 0 .. s.len-1:
     if s[i].line == reraisedFromBegin: result.add "[[reraised from:\n"
     elif s[i].line == reraisedFromEnd: result.add "]]\n"
     else: addFrameEntry(result, s[i])
 
-proc auxWriteStackTrace(f: PFrame, s: var string) =
-  when hasThreadSupport:
+when hasSomeStackTrace:
+
+  proc auxWriteStackTrace(f: PFrame, s: var string) =
+    when hasThreadSupport:
+      var
+        tempFrames: array[maxStackTraceLines, PFrame] # but better than a threadvar
+    const
+      firstCalls = 32
     var
-      tempFrames: array[0..127, PFrame] # but better than a threadvar
-  const
-    firstCalls = 32
-  var
-    it = f
-    i = 0
-    total = 0
-  # setup long head:
-  while it != nil and i <= high(tempFrames)-firstCalls:
-    tempFrames[i] = it
-    inc(i)
-    inc(total)
-    it = it.prev
-  # go up the stack to count 'total':
-  var b = it
-  while it != nil:
-    inc(total)
-    it = it.prev
-  var skipped = 0
-  if total > len(tempFrames):
-    # skip N
-    skipped = total-i-firstCalls+1
-    for j in 1..skipped:
-      if b != nil: b = b.prev
-    # create '...' entry:
-    tempFrames[i] = nil
-    inc(i)
-  # setup short tail:
-  while b != nil and i <= high(tempFrames):
-    tempFrames[i] = b
-    inc(i)
-    b = b.prev
-  for j in countdown(i-1, 0):
-    if tempFrames[j] == nil:
-      add(s, "(")
-      add(s, $skipped)
-      add(s, " calls omitted) ...\n")
-    else:
-      addFrameEntry(s, tempFrames[j])
+      it = f
+      i = 0
+      total = 0
+    # setup long head:
+    while it != nil and i <= high(tempFrames)-firstCalls:
+      tempFrames[i] = it
+      inc(i)
+      inc(total)
+      it = it.prev
+    # go up the stack to count 'total':
+    var b = it
+    while it != nil:
+      inc(total)
+      it = it.prev
+    var skipped = 0
+    if total > len(tempFrames):
+      # skip N
+      skipped = total-i-firstCalls+1
+      for j in 1..skipped:
+        if b != nil: b = b.prev
+      # create '...' entry:
+      tempFrames[i] = nil
+      inc(i)
+    # setup short tail:
+    while b != nil and i <= high(tempFrames):
+      tempFrames[i] = b
+      inc(i)
+      b = b.prev
+    for j in countdown(i-1, 0):
+      if tempFrames[j] == nil:
+        add(s, "(")
+        add(s, $skipped)
+        add(s, " calls omitted) ...\n")
+      else:
+        addFrameEntry(s, tempFrames[j])
 
-proc stackTraceAvailable*(): bool
+  proc stackTraceAvailable*(): bool
 
-when hasSomeStackTrace:
   proc rawWriteStackTrace(s: var string) =
-    when NimStackTrace:
+    when defined(nimStackTraceOverride):
+      add(s, "Traceback (most recent call last, using override)\n")
+      auxWriteStackTraceWithOverride(s)
+    elif NimStackTrace:
       if framePtr == nil:
-        add(s, "No stack traceback available\n")
+        add(s, noStacktraceAvailable)
       else:
         add(s, "Traceback (most recent call last)\n")
         auxWriteStackTrace(framePtr, s)
@@ -297,16 +342,20 @@ when hasSomeStackTrace:
       add(s, "Traceback from system (most recent call last)\n")
       auxWriteStackTraceWithBacktrace(s)
     else:
-      add(s, "No stack traceback available\n")
+      add(s, noStacktraceAvailable)
 
   proc rawWriteStackTrace(s: var seq[StackTraceEntry]) =
-    when NimStackTrace:
+    when defined(nimStackTraceOverride):
+      auxWriteStackTraceWithOverride(s)
+    elif NimStackTrace:
       auxWriteStackTrace(framePtr, s)
     else:
-      s = nil
+      s = @[]
 
   proc stackTraceAvailable(): bool =
-    when NimStackTrace:
+    when defined(nimStackTraceOverride):
+      result = true
+    elif NimStackTrace:
       if framePtr == nil:
         result = false
       else:
@@ -319,154 +368,272 @@ else:
   proc stackTraceAvailable*(): bool = result = false
 
 var onUnhandledException*: (proc (errorMsg: string) {.
-  nimcall.}) ## set this error \
+  nimcall, gcsafe.}) ## Set this error \
   ## handler to override the existing behaviour on an unhandled exception.
-  ## The default is to write a stacktrace to ``stderr`` and then call ``quit(1)``.
+  ##
+  ## The default is to write a stacktrace to `stderr` and then call `quit(1)`.
   ## Unstable API.
 
-template unhandled(buf, body) =
-  if onUnhandledException != nil:
-    onUnhandledException($buf)
+proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy, gcsafe.} =
+  when hasSomeStackTrace:
+    var buf = newStringOfCap(2000)
+    if e.trace.len == 0:
+      rawWriteStackTrace(buf)
+    else:
+      var trace = $e.trace
+      add(buf, trace)
+      {.gcsafe.}:
+        `=destroy`(trace)
+    add(buf, "Error: unhandled exception: ")
+    add(buf, e.msg)
+    add(buf, " [")
+    add(buf, $e.name)
+    add(buf, "]\n")
+
+    if onUnhandledException != nil:
+      onUnhandledException(buf)
+    else:
+      showErrorMessage2(buf)
+    {.gcsafe.}:
+      `=destroy`(buf)
   else:
-    body
+    # ugly, but avoids heap allocations :-)
+    template xadd(buf, s, slen) =
+      if L + slen < high(buf):
+        copyMem(addr(buf[L]), (when s is cstring: s else: cstring(s)), slen)
+        inc L, slen
+    template add(buf, s) =
+      xadd(buf, s, s.len)
+    var buf: array[0..2000, char]
+    var L = 0
+    if e.trace.len != 0:
+      var trace = $e.trace
+      add(buf, trace)
+      {.gcsafe.}:
+        `=destroy`(trace)
+    add(buf, "Error: unhandled exception: ")
+    add(buf, e.msg)
+    add(buf, " [")
+    xadd(buf, e.name, e.name.len)
+    add(buf, "]\n")
+    if onUnhandledException != nil:
+      onUnhandledException($cast[cstring](buf.addr))
+    else:
+      showErrorMessage(cast[cstring](buf.addr), L)
+
+proc reportUnhandledError(e: ref Exception) {.nodestroy, gcsafe.} =
+  if unhandledExceptionHook != nil:
+    unhandledExceptionHook(e)
+  when hostOS != "any":
+    reportUnhandledErrorAux(e)
+
+when not gotoBasedExceptions:
+  proc nimLeaveFinally() {.compilerRtl.} =
+    when defined(cpp) and not defined(noCppExceptions) and not gotoBasedExceptions:
+      {.emit: "throw;".}
+    else:
+      if excHandler != nil:
+        c_longjmp(excHandler.context, 1)
+      else:
+        reportUnhandledError(currException)
+        rawQuit(1)
+
+when gotoBasedExceptions:
+  var nimInErrorMode {.threadvar.}: bool
+
+  proc nimErrorFlag(): ptr bool {.compilerRtl, inl.} =
+    result = addr(nimInErrorMode)
+
+  proc nimTestErrorFlag() {.compilerRtl.} =
+    ## This proc must be called before `currException` is destroyed.
+    ## It also must be called at the end of every thread to ensure no
+    ## error is swallowed.
+    if nimInErrorMode and currException != nil:
+      reportUnhandledError(currException)
+      currException = nil
+      rawQuit(1)
+
+proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} =
+  when defined(nimPanics):
+    if e of Defect:
+      reportUnhandledError(e)
+      rawQuit(1)
 
-proc raiseExceptionAux(e: ref Exception) =
   if localRaiseHook != nil:
     if not localRaiseHook(e): return
   if globalRaiseHook != nil:
     if not globalRaiseHook(e): return
-  when defined(cpp) and not defined(noCppExceptions):
-    if e[] of OutOfMemError:
-      showErrorMessage(e.name)
-      quitOrDebug()
+  when defined(cpp) and not defined(noCppExceptions) and not gotoBasedExceptions:
+    if e == currException:
+      {.emit: "throw;".}
     else:
       pushCurrentException(e)
-      raise_counter.inc
-      if raise_counter == 0:
-        raise_counter.inc # skip zero at overflow
-      e.raise_id = raise_counter
-      {.emit: "`e`->raise();".}
+      {.emit: "throw `e`;".}
+  elif quirkyExceptions or gotoBasedExceptions:
+    pushCurrentException(e)
+    when gotoBasedExceptions:
+      inc nimInErrorMode
   else:
     if excHandler != nil:
-      if not excHandler.hasRaiseAction or excHandler.raiseAction(e):
-        pushCurrentException(e)
-        c_longjmp(excHandler.context, 1)
-    elif e[] of OutOfMemError:
-      showErrorMessage(e.name)
-      quitOrDebug()
+      pushCurrentException(e)
+      c_longjmp(excHandler.context, 1)
     else:
-      when hasSomeStackTrace:
-        var buf = newStringOfCap(2000)
-        if isNil(e.trace): rawWriteStackTrace(buf)
-        else: add(buf, $e.trace)
-        add(buf, "Error: unhandled exception: ")
-        if not isNil(e.msg): add(buf, e.msg)
-        add(buf, " [")
-        add(buf, $e.name)
-        add(buf, "]\n")
-        unhandled(buf):
-          showErrorMessage(buf)
-          quitOrDebug()
-      else:
-        # ugly, but avoids heap allocations :-)
-        template xadd(buf, s, slen) =
-          if L + slen < high(buf):
-            copyMem(addr(buf[L]), cstring(s), slen)
-            inc L, slen
-        template add(buf, s) =
-          xadd(buf, s, s.len)
-        var buf: array[0..2000, char]
-        var L = 0
-        add(buf, "Error: unhandled exception: ")
-        if not isNil(e.msg): add(buf, e.msg)
-        add(buf, " [")
-        xadd(buf, e.name, e.name.len)
-        add(buf, "]\n")
-        when defined(nimNoArrayToCstringConversion):
-          template tbuf(): untyped = addr buf
-        else:
-          template tbuf(): untyped = buf
-        unhandled(tbuf()):
-          showErrorMessage(tbuf())
-          quitOrDebug()
+      reportUnhandledError(e)
+      rawQuit(1)
 
-proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
+proc raiseExceptionEx(e: sink(ref Exception), ename, procname, filename: cstring,
+                      line: int) {.compilerRtl, nodestroy.} =
   if e.name.isNil: e.name = ename
   when hasSomeStackTrace:
-    if e.trace.isNil:
-      rawWriteStackTrace(e.trace)
-    elif framePtr != nil:
-      e.trace.add reraisedFrom(reraisedFromBegin)
-      auxWriteStackTrace(framePtr, e.trace)
-      e.trace.add reraisedFrom(reraisedFromEnd)
+    when defined(nimStackTraceOverride):
+      if e.trace.len == 0:
+        rawWriteStackTrace(e.trace)
+      else:
+        e.trace.add reraisedFrom(reraisedFromBegin)
+        auxWriteStackTraceWithOverride(e.trace)
+        e.trace.add reraisedFrom(reraisedFromEnd)
+    elif NimStackTrace:
+      if e.trace.len == 0:
+        rawWriteStackTrace(e.trace)
+      elif framePtr != nil:
+        e.trace.add reraisedFrom(reraisedFromBegin)
+        auxWriteStackTrace(framePtr, e.trace)
+        e.trace.add reraisedFrom(reraisedFromEnd)
+  else:
+    if procname != nil and filename != nil:
+      e.trace.add StackTraceEntry(procname: procname, filename: filename, line: line)
   raiseExceptionAux(e)
 
+proc raiseException(e: sink(ref Exception), ename: cstring) {.compilerRtl.} =
+  raiseExceptionEx(e, ename, nil, nil, 0)
+
 proc reraiseException() {.compilerRtl.} =
   if currException == nil:
-    sysFatal(ReraiseError, "no exception to reraise")
+    sysFatal(ReraiseDefect, "no exception to reraise")
   else:
-    raiseExceptionAux(currException)
+    when gotoBasedExceptions:
+      inc nimInErrorMode
+    else:
+      raiseExceptionAux(currException)
+
+proc threadTrouble() =
+  # also forward declared, it is 'raises: []' hence the try-except.
+  try:
+    if currException != nil: reportUnhandledError(currException)
+  except:
+    discard
+  rawQuit 1
 
 proc writeStackTrace() =
   when hasSomeStackTrace:
     var s = ""
     rawWriteStackTrace(s)
-    cast[proc (s: cstring) {.noSideEffect, tags: [], nimcall.}](showErrorMessage)(s)
   else:
-    cast[proc (s: cstring) {.noSideEffect, tags: [], nimcall.}](showErrorMessage)("No stack traceback available\n")
+    let s = noStacktraceAvailable
+  cast[proc (s: string) {.noSideEffect, tags: [], nimcall, raises: [].}](showErrorMessage2)(s)
 
 proc getStackTrace(): string =
   when hasSomeStackTrace:
     result = ""
     rawWriteStackTrace(result)
   else:
-    result = "No stack traceback available\n"
+    result = noStacktraceAvailable
 
 proc getStackTrace(e: ref Exception): string =
-  if not isNil(e) and not isNil(e.trace):
+  if not isNil(e):
     result = $e.trace
   else:
     result = ""
 
-proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] =
-  ## Returns the attached stack trace to the exception ``e`` as
-  ## a ``seq``. This is not yet available for the JS backend.
-  shallowCopy(result, e.trace)
-
-when defined(nimRequiresNimFrame):
-  proc stackOverflow() {.noinline.} =
-    writeStackTrace()
-    showErrorMessage("Stack overflow\n")
-    quitOrDebug()
-
-  proc nimFrame(s: PFrame) {.compilerRtl, inl, exportc: "nimFrame".} =
-    s.calldepth = if framePtr == nil: 0 else: framePtr.calldepth+1
-    s.prev = framePtr
-    framePtr = s
-    if s.calldepth == 2000: stackOverflow()
-else:
-  proc pushFrame(s: PFrame) {.compilerRtl, inl, exportc: "nimFrame".} =
-    # XXX only for backwards compatibility
-    s.prev = framePtr
-    framePtr = s
+proc getStackTraceEntries*(e: ref Exception): lent seq[StackTraceEntry] =
+  ## Returns the attached stack trace to the exception `e` as
+  ## a `seq`. This is not yet available for the JS backend.
+  e.trace
 
-when defined(endb):
-  var
-    dbgAborting: bool # whether the debugger wants to abort
+proc getStackTraceEntries*(): seq[StackTraceEntry] =
+  ## Returns the stack trace entries for the current stack trace.
+  ## This is not yet available for the JS backend.
+  when hasSomeStackTrace:
+    rawWriteStackTrace(result)
+
+const nimCallDepthLimit {.intdefine.} = 2000
+
+proc callDepthLimitReached() {.noinline.} =
+  writeStackTrace()
+  let msg = "Error: call depth limit reached in a debug build (" &
+      $nimCallDepthLimit & " function calls). You can change it with " &
+      "-d:nimCallDepthLimit=<int> but really try to avoid deep " &
+      "recursions instead.\n"
+  showErrorMessage2(msg)
+  rawQuit(1)
+
+proc nimFrame(s: PFrame) {.compilerRtl, inl, raises: [].} =
+  if framePtr == nil:
+    s.calldepth = 0
+    when NimStackTraceMsgs: s.frameMsgLen = 0
+  else:
+    s.calldepth = framePtr.calldepth+1
+    when NimStackTraceMsgs: s.frameMsgLen = framePtr.frameMsgLen
+  s.prev = framePtr
+  framePtr = s
+  if s.calldepth == nimCallDepthLimit: callDepthLimitReached()
+
+when defined(cpp) and appType != "lib" and not gotoBasedExceptions and
+    not defined(js) and not defined(nimscript) and
+    hostOS != "standalone" and hostOS != "any" and not defined(noCppExceptions) and
+    not quirkyExceptions:
+
+  type
+    StdException {.importcpp: "std::exception", header: "<exception>".} = object
+
+  proc what(ex: StdException): cstring {.importcpp: "((char *)#.what())", nodecl.}
+
+  proc setTerminate(handler: proc() {.noconv.})
+    {.importc: "std::set_terminate", header: "<exception>".}
+
+  setTerminate proc() {.noconv.} =
+    # Remove ourself as a handler, reinstalling the default handler.
+    setTerminate(nil)
+
+    var msg = "Unknown error in unexpected exception handler"
+    try:
+      {.emit: "#if !defined(_MSC_VER) || (_MSC_VER >= 1923)".}
+      raise
+      {.emit: "#endif".}
+    except Exception:
+      msg = currException.getStackTrace() & "Error: unhandled exception: " &
+        currException.msg & " [" & $currException.name & "]"
+    except StdException as e:
+      msg = "Error: unhandled cpp exception: " & $e.what()
+    except:
+      msg = "Error: unhandled unknown cpp exception"
+
+    {.emit: "#if defined(_MSC_VER) && (_MSC_VER < 1923)".}
+    msg = "Error: unhandled unknown cpp exception"
+    {.emit: "#endif".}
+
+    when defined(genode):
+      # stderr not available by default, use the LOG session
+      echo msg
+    else:
+      writeToStdErr msg & "\n"
+
+    rawQuit 1
 
 when not defined(noSignalHandler) and not defined(useNimRtl):
+  type Sighandler = proc (a: cint) {.noconv, benign.}
+    # xxx factor with ansi_c.CSighandlerT, posix.Sighandler
+
   proc signalHandler(sign: cint) {.exportc: "signalHandler", noconv.} =
     template processSignal(s, action: untyped) {.dirty.} =
       if s == SIGINT: action("SIGINT: Interrupted by Ctrl-C.\n")
       elif s == SIGSEGV:
         action("SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n")
       elif s == SIGABRT:
-        when defined(endb):
-          if dbgAborting: return # the debugger wants to abort
         action("SIGABRT: Abnormal termination.\n")
       elif s == SIGFPE: action("SIGFPE: Arithmetic error.\n")
       elif s == SIGILL: action("SIGILL: Illegal operation.\n")
-      elif s == SIGBUS:
+      elif (when declared(SIGBUS): s == SIGBUS else: false):
         action("SIGBUS: Illegal storage access. (Attempt to read from nil?)\n")
       else:
         block platformSpecificSignal:
@@ -480,34 +647,59 @@ when not defined(noSignalHandler) and not defined(useNimRtl):
     when defined(memtracker):
       logPendingOps()
     when hasSomeStackTrace:
-      GC_disable()
+      when not usesDestructors: GC_disable()
       var buf = newStringOfCap(2000)
       rawWriteStackTrace(buf)
       processSignal(sign, buf.add) # nice hu? currying a la Nim :-)
-      showErrorMessage(buf)
-      GC_enable()
+      showErrorMessage2(buf)
+      when not usesDestructors: GC_enable()
     else:
       var msg: cstring
       template asgn(y) =
         msg = y
       processSignal(sign, asgn)
-      showErrorMessage(msg)
-    when defined(endb): dbgAborting = true
-    quit(1) # always quit when SIGABRT
+      # xxx use string for msg instead of cstring, and here use showErrorMessage2(msg)
+      # unless there's a good reason to use cstring in signal handler to avoid
+      # using gc?
+      showErrorMessage(msg, msg.len)
+
+    when defined(posix):
+      # reset the signal handler to OS default
+      c_signal(sign, SIG_DFL)
+
+      # re-raise the signal, which will arrive once this handler exit.
+      # this lets the OS perform actions like core dumping and will
+      # also return the correct exit code to the shell.
+      discard c_raise(sign)
+    else:
+      rawQuit(1)
+
+  var SIG_IGN {.importc: "SIG_IGN", header: "<signal.h>".}: Sighandler
 
   proc registerSignalHandler() =
+    # xxx `signal` is deprecated and has many caveats, we should use `sigaction` instead, e.g.
+    # https://stackoverflow.com/questions/231912/what-is-the-difference-between-sigaction-and-signal
     c_signal(SIGINT, signalHandler)
     c_signal(SIGSEGV, signalHandler)
     c_signal(SIGABRT, signalHandler)
     c_signal(SIGFPE, signalHandler)
     c_signal(SIGILL, signalHandler)
-    c_signal(SIGBUS, signalHandler)
+    when declared(SIGBUS):
+      c_signal(SIGBUS, signalHandler)
     when declared(SIGPIPE):
-      c_signal(SIGPIPE, signalHandler)
+      when defined(nimLegacySigpipeHandler):
+        c_signal(SIGPIPE, signalHandler)
+      else:
+        c_signal(SIGPIPE, SIG_IGN)
 
   registerSignalHandler() # call it in initialization section
 
 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 declared(Sighandler):
+    c_signal(SIGINT, cast[Sighandler](hook))
+
+when not defined(noSignalHandler) and not defined(useNimRtl):
+  proc unsetControlCHook() =
+    # proc to unset a hook set by setControlCHook
+    c_signal(SIGINT, signalHandler)
diff --git a/lib/system/fatal.nim b/lib/system/fatal.nim
new file mode 100644
index 000000000..25c05e52d
--- /dev/null
+++ b/lib/system/fatal.nim
@@ -0,0 +1,58 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2019 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+{.push profiler: off.}
+
+const
+  gotoBasedExceptions = compileOption("exceptions", "goto")
+  quirkyExceptions = compileOption("exceptions", "quirky")
+
+when hostOS == "standalone":
+  include "$projectpath/panicoverride"
+
+  func sysFatal(exceptn: typedesc[Defect], message: string) {.inline.} =
+    panic(message)
+
+  func sysFatal(exceptn: typedesc[Defect], message, arg: string) {.inline.} =
+    rawoutput(message)
+    panic(arg)
+
+elif quirkyExceptions and not defined(nimscript):
+  import ansi_c
+
+  func name(t: typedesc): string {.magic: "TypeTrait".}
+
+  func sysFatal(exceptn: typedesc[Defect], message, arg: string) {.inline, noreturn.} =
+    when nimvm:
+      # TODO when doAssertRaises works in CT, add a test for it
+      raise (ref exceptn)(msg: message & arg)
+    else:
+      {.noSideEffect.}:
+        writeStackTrace()
+        var buf = newStringOfCap(200)
+        add(buf, "Error: unhandled exception: ")
+        add(buf, message)
+        add(buf, arg)
+        add(buf, " [")
+        add(buf, name exceptn)
+        add(buf, "]\n")
+        cstderr.rawWrite buf
+      rawQuit 1
+
+  func sysFatal(exceptn: typedesc[Defect], message: string) {.inline, noreturn.} =
+    sysFatal(exceptn, message, "")
+
+else:
+  func sysFatal(exceptn: typedesc[Defect], message: string) {.inline, noreturn.} =
+    raise (ref exceptn)(msg: message)
+
+  func sysFatal(exceptn: typedesc[Defect], message, arg: string) {.inline, noreturn.} =
+    raise (ref exceptn)(msg: message & arg)
+
+{.pop.}
diff --git a/lib/system/formatfloat.nim b/lib/system/formatfloat.nim
new file mode 100644
index 000000000..70dd857d5
--- /dev/null
+++ b/lib/system/formatfloat.nim
@@ -0,0 +1,6 @@
+when not defined(nimPreviewSlimSystem):
+  import std/formatfloat
+  export formatfloat
+  {.deprecated: "use `std/formatfloat`".}
+else:
+  {.error: "use `std/formatfloat`".}
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index 425963f3f..9289c7f55 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -12,14 +12,64 @@
 # Refcounting + Mark&Sweep. Complex algorithms avoided.
 # Been there, done that, didn't work.
 
+#[
+
+A *cell* is anything that is traced by the GC
+(sequences, refs, strings, closures).
+
+The basic algorithm is *Deferrent Reference Counting* with cycle detection.
+References on the stack are not counted for better performance and easier C
+code generation.
+
+Each cell has a header consisting of a RC and a pointer to its type
+descriptor. However the program does not know about these, so they are placed at
+negative offsets. In the GC code the type `PCell` denotes a pointer
+decremented by the right offset, so that the header can be accessed easily. It
+is extremely important that `pointer` is not confused with a `PCell`.
+
+In Nim the compiler cannot always know if a reference
+is stored on the stack or not. This is caused by var parameters.
+Consider this example:
+
+  ```Nim
+  proc setRef(r: var ref TNode) =
+    new(r)
+
+  proc usage =
+    var
+      r: ref TNode
+    setRef(r) # here we should not update the reference counts, because
+              # r is on the stack
+    setRef(r.left) # here we should update the refcounts!
+  ```
+
+We have to decide at runtime whether the reference is on the stack or not.
+The generated code looks roughly like this:
+
+  ```C
+  void setref(TNode** ref) {
+    unsureAsgnRef(ref, newObj(TNode_TI, sizeof(TNode)))
+  }
+  void usage(void) {
+    setRef(&r)
+    setRef(&r->left)
+  }
+  ```
+
+Note that for systems with a continuous stack (which most systems have)
+the check whether the ref is on the stack is very cheap (only two
+comparisons).
+]#
+
 {.push profiler:off.}
 
 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
+  InitialCycleThreshold = when defined(nimCycleBreaker): high(int)
+                          else: 4*1024*1024 # X MB because cycle checking is slow
+  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):
@@ -28,7 +78,7 @@ when defined(memProfiler):
   proc nimProfile(requestedSize: int) {.benign.}
 
 when hasThreadSupport:
-  import sharedlist
+  import std/sharedlist
 
 const
   rcIncrement = 0b1000 # so that lowest 3 bits are not touched
@@ -46,7 +96,7 @@ type
     waZctDecRef, waPush
     #, waDebug
 
-  Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
+  Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign, raises: [].}
     # A ref type can have a finalizer that is called before the object's
     # storage is freed.
 
@@ -78,6 +128,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
@@ -100,23 +151,19 @@ var
 when not defined(useNimRtl):
   instantiateForRegion(gch.region)
 
-template acquire(gch: GcHeap) =
-  when hasThreadSupport and hasSharedHeap:
-    acquireSys(HeapLock)
-
-template release(gch: GcHeap) =
-  when hasThreadSupport and hasSharedHeap:
-    releaseSys(HeapLock)
-
 template gcAssert(cond: bool, msg: string) =
   when defined(useGcAssert):
     if not cond:
-      echo "[GCASSERT] ", msg
+      cstderr.rawWrite "[GCASSERT] "
+      cstderr.rawWrite msg
+      when defined(logGC):
+        cstderr.rawWrite "[GCASSERT] statistics:\L"
+        cstderr.rawWrite GC_getStatistics()
       GC_disable()
       writeStackTrace()
       #var x: ptr int
       #echo x[]
-      quit 1
+      rawQuit 1
 
 proc addZCT(s: var CellSeq, c: PCell) {.noinline.} =
   if (c.refcount and ZctFlag) == 0:
@@ -125,18 +172,18 @@ proc addZCT(s: var CellSeq, c: PCell) {.noinline.} =
 
 proc cellToUsr(cell: PCell): pointer {.inline.} =
   # convert object (=pointer to refcount) to pointer to userdata
-  result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell)))
+  result = cast[pointer](cast[int](cell)+%ByteAddress(sizeof(Cell)))
 
 proc usrToCell(usr: pointer): PCell {.inline.} =
   # convert pointer to userdata to object (=pointer to refcount)
-  result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell)))
+  result = cast[PCell](cast[int](usr)-%ByteAddress(sizeof(Cell)))
 
 proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
   # used for code generation concerning debugging
   result = usrToCell(c).typ
 
 proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
-  result = int(usrToCell(p).refcount) shr rcShift
+  result = usrToCell(p).refcount shr rcShift
 
 # this that has to equals zero, otherwise we have to round up UnitsPerPage:
 when BitsPerPage mod (sizeof(int)*8) != 0:
@@ -160,65 +207,52 @@ when defined(logGC):
           typName = c.typ.name
 
     when leakDetector:
-      c_fprintf(stdout, "[GC] %s: %p %d %s rc=%ld from %s(%ld)\n",
+      c_printf("[GC] %s: %p %d %s rc=%ld from %s(%ld)\n",
                 msg, c, kind, typName, c.refcount shr rcShift, c.filename, c.line)
     else:
-      c_fprintf(stdout, "[GC] %s: %p %d %s rc=%ld; thread=%ld\n",
+      c_printf("[GC] %s: %p %d %s rc=%ld; thread=%ld\n",
                 msg, c, kind, typName, c.refcount shr rcShift, gch.gcThreadId)
 
+template logCell(msg: cstring, c: PCell) =
+  when defined(logGC):
+    writeCell(msg, c)
+
 template gcTrace(cell, state: untyped) =
   when traceGC: traceCell(cell, state)
 
 # forward declarations:
-proc collectCT(gch: var GcHeap) {.benign.}
-proc isOnStack(p: pointer): bool {.noinline, benign.}
-proc forAllChildren(cell: PCell, op: WalkOp) {.benign.}
-proc doOperation(p: pointer, op: WalkOp) {.benign.}
-proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
+proc collectCT(gch: var GcHeap) {.benign, raises: [].}
+proc isOnStack(p: pointer): bool {.noinline, benign, raises: [].}
+proc forAllChildren(cell: PCell, op: WalkOp) {.benign, raises: [].}
+proc doOperation(p: pointer, op: WalkOp) {.benign, raises: [].}
+proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign, raises: [].}
 # we need the prototype here for debugging purposes
 
-when hasThreadSupport and hasSharedHeap:
-  template `--`(x: untyped): untyped = atomicDec(x, rcIncrement) <% rcIncrement
-  template `++`(x: untyped) = discard atomicInc(x, rcIncrement)
-else:
-  template `--`(x: untyped): untyped =
-    dec(x, rcIncrement)
-    x <% rcIncrement
-  template `++`(x: untyped) = inc(x, rcIncrement)
-
 proc incRef(c: PCell) {.inline.} =
   gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr")
   c.refcount = c.refcount +% rcIncrement
   # and not colorMask
-  #writeCell("incRef", c)
+  logCell("incRef", c)
 
-proc nimGCref(p: pointer) {.compilerProc.} =
+proc nimGCref(p: pointer) {.compilerproc.} =
   # we keep it from being collected by pretending it's not even allocated:
-  add(gch.additionalRoots, usrToCell(p))
-  incRef(usrToCell(p))
-
-proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
-  # we MUST access gch as a global here, because this crosses DLL boundaries!
-  when hasThreadSupport and hasSharedHeap:
-    acquireSys(HeapLock)
-  when hasThreadSupport and hasSharedHeap:
-    releaseSys(HeapLock)
+  let c = usrToCell(p)
+  add(gch.additionalRoots, c)
+  incRef(c)
 
 proc rtlAddZCT(c: PCell) {.rtl, inl.} =
   # we MUST access gch as a global here, because this crosses DLL boundaries!
-  when hasThreadSupport and hasSharedHeap:
-    acquireSys(HeapLock)
   addZCT(gch.zct, c)
-  when hasThreadSupport and hasSharedHeap:
-    releaseSys(HeapLock)
 
 proc decRef(c: PCell) {.inline.} =
   gcAssert(isAllocatedPtr(gch.region, c), "decRef: interiorPtr")
   gcAssert(c.refcount >=% rcIncrement, "decRef")
-  if --c.refcount:
+  c.refcount = c.refcount -% rcIncrement
+  if c.refcount <% rcIncrement:
     rtlAddZCT(c)
+  logCell("decRef", c)
 
-proc nimGCunref(p: pointer) {.compilerProc.} =
+proc nimGCunref(p: pointer) {.compilerproc.} =
   let cell = usrToCell(p)
   var L = gch.additionalRoots.len-1
   var i = L
@@ -239,25 +273,15 @@ template beforeDealloc(gch: var GcHeap; c: PCell; msg: typed) =
       if gch.decStack.d[i] == c:
         sysAssert(false, msg)
 
-proc GC_addCycleRoot*[T](p: ref T) {.inline.} =
-  ## adds 'p' to the cycle candidate set for the cycle collector. It is
-  ## necessary if you used the 'acyclic' pragma for optimization
-  ## purposes and need to break cycles manually.
-  rtlAddCycleRoot(usrToCell(cast[pointer](p)))
-
-proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} =
+proc nimGCunrefNoCycle(p: pointer) {.compilerproc, inline.} =
   sysAssert(allocInv(gch.region), "begin nimGCunrefNoCycle")
-  var c = usrToCell(p)
-  gcAssert(isAllocatedPtr(gch.region, c), "nimGCunrefNoCycle: isAllocatedPtr")
-  if --c.refcount:
-    rtlAddZCT(c)
-    sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 2")
+  decRef(usrToCell(p))
   sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 5")
 
-proc nimGCunrefRC1(p: pointer) {.compilerProc, inline.} =
+proc nimGCunrefRC1(p: pointer) {.compilerproc, inline.} =
   decRef(usrToCell(p))
 
-proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} =
+proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
   # the code generator calls this proc!
   gcAssert(not isOnStack(dest), "asgnRef")
   # BUGFIX: first incRef then decRef!
@@ -265,21 +289,12 @@ proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} =
   if dest[] != nil: decRef(usrToCell(dest[]))
   dest[] = src
 
-proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} =
-  # the code generator calls this proc if it is known at compile time that no
-  # cycle is possible.
-  if src != nil:
-    var c = usrToCell(src)
-    ++c.refcount
-  if dest[] != nil:
-    var c = usrToCell(dest[])
-    if --c.refcount:
-      rtlAddZCT(c)
-  dest[] = src
+proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline,
+  deprecated: "old compiler compat".} = asgnRef(dest, src)
 
-proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} =
+proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc.} =
   # unsureAsgnRef updates the reference counters only if dest is not on the
-  # stack. It is used by the code generator if it cannot decide wether a
+  # stack. It is used by the code generator if it cannot decide whether a
   # reference is in the stack or not (this can happen for var parameters).
   if not isOnStack(dest):
     if src != nil: incRef(usrToCell(src))
@@ -298,6 +313,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
@@ -322,14 +338,14 @@ proc cellsetReset(s: var CellSet) =
 {.push stacktrace:off.}
 
 proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
-  var d = cast[ByteAddress](dest)
+  var d = cast[int](dest)
   case n.kind
   of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
   of nkList:
     for i in 0..n.len-1:
       # inlined for speed
       if n.sons[i].kind == nkSlot:
-        if n.sons[i].typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}:
+        if n.sons[i].typ.kind in {tyRef, tyString, tySequence}:
           doOperation(cast[PPointer](d +% n.sons[i].offset)[], op)
         else:
           forAllChildrenAux(cast[pointer](d +% n.sons[i].offset),
@@ -342,11 +358,11 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
   of nkNone: sysAssert(false, "forAllSlotsAux")
 
 proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
-  var d = cast[ByteAddress](dest)
+  var d = cast[int](dest)
   if dest == nil: return # nothing to do
   if ntfNoRefs notin mt.flags:
     case mt.kind
-    of tyRef, tyOptAsRef, tyString, tySequence: # leaf:
+    of tyRef, tyString, tySequence: # leaf:
       doOperation(cast[PPointer](d)[], op)
     of tyObject, tyTuple:
       forAllSlotsAux(dest, mt.node, op)
@@ -359,21 +375,20 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
   gcAssert(cell != nil, "forAllChildren: cell is nil")
   gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: pointer not part of the heap")
   gcAssert(cell.typ != nil, "forAllChildren: cell.typ is nil")
-  gcAssert cell.typ.kind in {tyRef, tyOptAsRef, tySequence, tyString}, "forAllChildren: unknown GC'ed type"
+  gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: unknown GC'ed type"
   let marker = cell.typ.marker
   if marker != nil:
     marker(cellToUsr(cell), op.int)
   else:
     case cell.typ.kind
-    of tyRef, tyOptAsRef: # common case
+    of tyRef: # common case
       forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
     of tySequence:
-      var d = cast[ByteAddress](cellToUsr(cell))
+      var d = cast[int](cellToUsr(cell))
       var s = cast[PGenericSeq](d)
       if s != nil:
         for i in 0..s.len-1:
-          forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +%
-            GenericSeqSize), cell.typ.base, op)
+          forAllChildrenAux(cast[pointer](d +% align(GenericSeqSize, cell.typ.base.align) +% i *% cell.typ.base.size), cell.typ.base, op)
     else: discard
 
 proc addNewObjToZCT(res: PCell, gch: var GcHeap) {.inline.} =
@@ -440,12 +455,11 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
   # generates a new object and sets its reference counter to 0
   incTypeSize typ, size
   sysAssert(allocInv(gch.region), "rawNewObj begin")
-  acquire(gch)
-  gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
+  gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
   collectCT(gch)
   var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
   #gcAssert typ.kind in {tyString, tySequence} or size >= typ.base.size, "size too small"
-  gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
+  gcAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2")
   # now it is buffered in the ZCT
   res.typ = typ
   setFrameInfo(res)
@@ -454,10 +468,9 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
   sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
   # its refcount is zero, so add it to the ZCT:
   addNewObjToZCT(res, gch)
-  when logGC: writeCell("new cell", res)
+  logCell("new cell", res)
   track("rawNewObj", res, size)
   gcTrace(res, csAllocated)
-  release(gch)
   when useCellIds:
     inc gch.idGenerator
     res.id = gch.idGenerator * 1000_000 + gch.gcThreadId
@@ -471,40 +484,40 @@ proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
   result = rawNewObj(typ, size, gch)
   when defined(memProfiler): nimProfile(size)
 
-proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
+proc newObj(typ: PNimType, size: int): pointer {.compilerRtl, noinline.} =
   result = rawNewObj(typ, size, gch)
   zeroMem(result, size)
   when defined(memProfiler): nimProfile(size)
 
+{.push overflowChecks: on.}
 proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
   # `newObj` already uses locks, so no need for them here.
-  let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
+  let size = align(GenericSeqSize, typ.base.align) + len * typ.base.size
   result = newObj(typ, size)
   cast[PGenericSeq](result).len = len
   cast[PGenericSeq](result).reserved = len
   when defined(memProfiler): nimProfile(size)
+{.pop.}
 
-proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
+proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl, noinline.} =
   # generates a new object and sets its reference counter to 1
   incTypeSize typ, size
   sysAssert(allocInv(gch.region), "newObjRC1 begin")
-  acquire(gch)
-  gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
+  gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
   collectCT(gch)
   sysAssert(allocInv(gch.region), "newObjRC1 after collectCT")
 
   var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
   sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc")
-  sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
+  sysAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2")
   # now it is buffered in the ZCT
   res.typ = typ
   setFrameInfo(res)
   res.refcount = rcIncrement # refcount is 1
   sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
-  when logGC: writeCell("new cell", res)
+  logCell("new cell", res)
   track("newObjRC1", res, size)
   gcTrace(res, csAllocated)
-  release(gch)
   when useCellIds:
     inc gch.idGenerator
     res.id = gch.idGenerator * 1000_000 + gch.gcThreadId
@@ -513,15 +526,16 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
   sysAssert(allocInv(gch.region), "newObjRC1 end")
   when defined(memProfiler): nimProfile(size)
 
+{.push overflowChecks: on.}
 proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
-  let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
+  let size = align(GenericSeqSize, typ.base.align) + len * typ.base.size
   result = newObjRC1(typ, size)
   cast[PGenericSeq](result).len = len
   cast[PGenericSeq](result).reserved = len
   when defined(memProfiler): nimProfile(size)
+{.pop.}
 
 proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
-  acquire(gch)
   collectCT(gch)
   var ol = usrToCell(old)
   sysAssert(ol.typ != nil, "growObj: 1")
@@ -529,52 +543,28 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
   sysAssert(allocInv(gch.region), "growObj begin")
 
   var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
-  var elemSize = 1
-  if ol.typ.kind != tyString: elemSize = ol.typ.base.size
+  var elemSize,elemAlign = 1
+  if ol.typ.kind != tyString:
+    elemSize = ol.typ.base.size
+    elemAlign = ol.typ.base.align
   incTypeSize ol.typ, newsize
 
-  var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
+  var oldsize = align(GenericSeqSize, elemAlign) + cast[PGenericSeq](old).len * elemSize
   copyMem(res, ol, oldsize + sizeof(Cell))
-  zeroMem(cast[pointer](cast[ByteAddress](res) +% oldsize +% sizeof(Cell)),
+  zeroMem(cast[pointer](cast[int](res) +% oldsize +% sizeof(Cell)),
           newsize-oldsize)
-  sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
+  sysAssert((cast[int](res) and (MemAlign-1)) == 0, "growObj: 3")
   # This can be wrong for intermediate temps that are nevertheless on the
   # heap because of lambda lifting:
   #gcAssert(res.refcount shr rcShift <=% 1, "growObj: 4")
-  when logGC:
-    writeCell("growObj old cell", ol)
-    writeCell("growObj new cell", res)
+  logCell("growObj old cell", ol)
+  logCell("growObj new cell", res)
   gcTrace(ol, csZctFreed)
   gcTrace(res, csAllocated)
   track("growObj old", ol, 0)
   track("growObj new", res, newsize)
-  when reallyDealloc:
-    sysAssert(allocInv(gch.region), "growObj before dealloc")
-    if ol.refcount shr rcShift <=% 1:
-      # free immediately to save space:
-      if (ol.refcount and ZctFlag) != 0:
-        var j = gch.zct.len-1
-        var d = gch.zct.d
-        while j >= 0:
-          if d[j] == ol:
-            d[j] = res
-            break
-          dec(j)
-      beforeDealloc(gch, ol, "growObj stack trash")
-      decTypeSize(ol, ol.typ)
-      rawDealloc(gch.region, ol)
-    else:
-      # we split the old refcount in 2 parts. XXX This is still not entirely
-      # correct if the pointer that receives growObj's result is on the stack.
-      # A better fix would be to emit the location specific write barrier for
-      # 'growObj', but this is lots of more work and who knows what new problems
-      # this would create.
-      res.refcount = rcIncrement
-      decRef(ol)
-  else:
-    sysAssert(ol.typ != nil, "growObj: 5")
-    zeroMem(ol, sizeof(Cell))
-  release(gch)
+  # since we steal the old seq's contents, we set the old length to 0.
+  cast[PGenericSeq](old).len = 0
   when useCellIds:
     inc gch.idGenerator
     res.id = gch.idGenerator * 1000_000 + gch.gcThreadId
@@ -593,7 +583,7 @@ proc freeCyclicCell(gch: var GcHeap, c: PCell) =
   prepareDealloc(c)
   gcTrace(c, csCycFreed)
   track("cycle collector dealloc cell", c, 0)
-  when logGC: writeCell("cycle collector dealloc cell", c)
+  logCell("cycle collector dealloc cell", c)
   when reallyDealloc:
     sysAssert(allocInv(gch.region), "free cyclic cell")
     beforeDealloc(gch, c, "freeCyclicCell: stack trash")
@@ -621,7 +611,7 @@ proc markS(gch: var GcHeap, c: PCell) =
     if not containsOrIncl(gch.marked, d):
       forAllChildren(d, waMarkPrecise)
 
-proc markGlobals(gch: var GcHeap) =
+proc markGlobals(gch: var GcHeap) {.raises: [].} =
   if gch.gcThreadId == 0:
     for i in 0 .. globalMarkersLen-1: globalMarkers[i]()
   for i in 0 .. threadLocalMarkersLen-1: threadLocalMarkers[i]()
@@ -638,7 +628,7 @@ when logGC:
       if cycleCheckA[i] == c: return true
     if cycleCheckALen == len(cycleCheckA):
       gcAssert(false, "cycle detection overflow")
-      quit 1
+      rawQuit 1
     cycleCheckA[cycleCheckALen] = c
     inc cycleCheckALen
 
@@ -648,7 +638,7 @@ when logGC:
     else:
       writeCell("cell {", s)
       forAllChildren(s, waDebug)
-      c_fprintf(stdout, "}\n")
+      c_printf("}\n")
 
 proc doOperation(p: pointer, op: WalkOp) =
   if p == nil: return
@@ -659,14 +649,12 @@ proc doOperation(p: pointer, op: WalkOp) =
   case op
   of waZctDecRef:
     #if not isAllocatedPtr(gch.region, c):
-    #  c_fprintf(stdout, "[GC] decref bug: %p", c)
+    #  c_printf("[GC] decref bug: %p", c)
     gcAssert(isAllocatedPtr(gch.region, c), "decRef: waZctDecRef")
     gcAssert(c.refcount >=% rcIncrement, "doOperation 2")
-    #c.refcount = c.refcount -% rcIncrement
-    when logGC: writeCell("decref (from doOperation)", c)
+    logCell("decref (from doOperation)", c)
     track("waZctDecref", p, 0)
     decRef(c)
-    #if c.refcount <% rcIncrement: addZCT(gch.zct, c)
   of waPush:
     add(gch.tempStack, c)
   of waMarkGlobal:
@@ -678,9 +666,9 @@ proc doOperation(p: pointer, op: WalkOp) =
 proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
   doOperation(d, WalkOp(op))
 
-proc collectZCT(gch: var GcHeap): bool {.benign.}
+proc collectZCT(gch: var GcHeap): bool {.benign, raises: [].}
 
-proc collectCycles(gch: var GcHeap) =
+proc collectCycles(gch: var GcHeap) {.raises: [].} =
   when hasThreadSupport:
     for c in gch.toDispose:
       nimGCunref(c)
@@ -697,20 +685,20 @@ proc collectCycles(gch: var GcHeap) =
 proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
   # the addresses are not as cells on the stack, so turn them to cells:
   sysAssert(allocInv(gch.region), "gcMark begin")
-  var cell = usrToCell(p)
-  var c = cast[ByteAddress](cell)
+  var c = cast[int](p)
   if c >% PageSize:
     # fast check: does it look like a cell?
-    var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell))
+    var objStart = cast[PCell](interiorAllocatedPtr(gch.region, p))
     if objStart != nil:
       # mark the cell:
-      objStart.refcount = objStart.refcount +% rcIncrement
+      incRef(objStart)
       add(gch.decStack, objStart)
     when false:
+      let cell = usrToCell(p)
       if isAllocatedPtr(gch.region, cell):
         sysAssert false, "allocated pointer but not interior?"
         # mark the cell:
-        cell.refcount = cell.refcount +% rcIncrement
+        incRef(cell)
         add(gch.decStack, cell)
   sysAssert(allocInv(gch.region), "gcMark end")
 
@@ -720,7 +708,8 @@ proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
   garbage collection that is used by Nim. For more information, please see the documentation of
   `CLANG_NO_SANITIZE_ADDRESS` in `lib/nimbase.h`.
  ]#
-proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl, codegenDecl: "CLANG_NO_SANITIZE_ADDRESS $# $#$#".} =
+proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl,
+    codegenDecl: "CLANG_NO_SANITIZE_ADDRESS N_LIB_PRIVATE $# $#$#".} =
   forEachStackSlot(gch, gcMark)
 
 proc collectZCT(gch: var GcHeap): bool =
@@ -752,7 +741,7 @@ proc collectZCT(gch: var GcHeap): bool =
       # as this might be too slow.
       # In any case, it should be removed from the ZCT. But not
       # freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!**
-      when logGC: writeCell("zct dealloc cell", c)
+      logCell("zct dealloc cell", c)
       track("zct dealloc cell", c, 0)
       gcTrace(c, csZctFreed)
       # We are about to free the object, call the finalizer BEFORE its
@@ -784,14 +773,9 @@ proc unmarkStackAndRegisters(gch: var GcHeap) =
   for i in 0..gch.decStack.len-1:
     sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters"
     decRef(d[i])
-    #var c = d[i]
-    # XXX no need for an atomic dec here:
-    #if --c.refcount:
-    #  addZCT(gch.zct, c)
-    #sysAssert c.typ != nil, "unmarkStackAndRegisters 2"
   gch.decStack.len = 0
 
-proc collectCTBody(gch: var GcHeap) =
+proc collectCTBody(gch: var GcHeap) {.raises: [].} =
   when withRealTime:
     let t0 = getticks()
   sysAssert(allocInv(gch.region), "collectCT: begin")
@@ -823,14 +807,10 @@ proc collectCTBody(gch: var GcHeap) =
     gch.stat.maxPause = max(gch.stat.maxPause, duration)
     when defined(reportMissedDeadlines):
       if gch.maxPause > 0 and duration > gch.maxPause:
-        c_fprintf(stdout, "[GC] missed deadline: %ld\n", duration)
+        c_printf("[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:
@@ -838,6 +818,13 @@ proc collectCT(gch: var GcHeap) =
       cellsetReset(gch.marked)
       markForDebug(gch)
     collectCTBody(gch)
+    gch.zctThreshold = max(InitialZctThreshold, gch.zct.len * CycleIncrease)
+
+proc GC_collectZct*() =
+  ## Collect the ZCT (zero count table). Unstable, experimental API for
+  ## testing purposes.
+  ## DO NOT USE!
+  collectCTBody(gch)
 
 when withRealTime:
   proc toNano(x: int): Nanos {.inline.} =
@@ -847,13 +834,12 @@ when withRealTime:
     gch.maxPause = MaxPauseInUs.toNano
 
   proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) =
-    acquire(gch)
     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)
-    release(gch)
+      gch.zctThreshold = max(InitialZctThreshold, gch.zct.len * CycleIncrease)
 
   proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} =
     if stackSize >= 0:
@@ -864,10 +850,10 @@ when withRealTime:
         stack.bottomSaved = stack.bottom
         when stackIncreases:
           stack.bottom = cast[pointer](
-            cast[ByteAddress](stack.pos) - sizeof(pointer) * 6 - stackSize)
+            cast[int](stack.pos) - sizeof(pointer) * 6 - stackSize)
         else:
           stack.bottom = cast[pointer](
-            cast[ByteAddress](stack.pos) + sizeof(pointer) * 6 + stackSize)
+            cast[int](stack.pos) + sizeof(pointer) * 6 + stackSize)
 
     GC_step(gch, us, strongAdvice)
 
@@ -877,18 +863,13 @@ when withRealTime:
 
 when not defined(useNimRtl):
   proc GC_disable() =
-    when hasThreadSupport and hasSharedHeap:
-      discard atomicInc(gch.recGcLock, 1)
-    else:
-      inc(gch.recGcLock)
+    inc(gch.recGcLock)
   proc GC_enable() =
-    if gch.recGcLock <= 0:
-      raise newException(AssertionError,
-          "API usage error: GC_enable called but GC is already enabled")
-    when hasThreadSupport and hasSharedHeap:
-      discard atomicDec(gch.recGcLock, 1)
-    else:
-      dec(gch.recGcLock)
+    when defined(nimDoesntTrackDefects):
+      if gch.recGcLock <= 0:
+        raise newException(AssertionDefect,
+            "API usage error: GC_enable called but GC is already enabled")
+    dec(gch.recGcLock)
 
   proc GC_setStrategy(strategy: GC_Strategy) =
     discard
@@ -897,16 +878,14 @@ when not defined(useNimRtl):
     gch.cycleThreshold = InitialCycleThreshold
 
   proc GC_disableMarkAndSweep() =
-    gch.cycleThreshold = high(gch.cycleThreshold)-1
+    gch.cycleThreshold = high(typeof(gch.cycleThreshold))-1
     # set to the max value to suppress the cycle detector
 
   proc GC_fullCollect() =
-    acquire(gch)
     var oldThreshold = gch.cycleThreshold
     gch.cycleThreshold = 0 # forces cycle collection
     collectCT(gch)
     gch.cycleThreshold = oldThreshold
-    release(gch)
 
   proc GC_getStatistics(): string =
     result = "[GC] total memory: " & $(getTotalMem()) & "\n" &
@@ -923,6 +902,10 @@ when not defined(useNimRtl):
       for stack in items(gch.stack):
         result.add "[GC]   stack " & stack.bottom.repr & "[GC]     max stack size " & cast[pointer](stack.maxStackSize).repr & "\n"
     else:
+      # this caused memory leaks, see #10488 ; find a way without `repr`
+      # maybe using a local copy of strutils.toHex or snprintf
+      when defined(logGC):
+        result.add "[GC] stack bottom: " & gch.stack.bottom.repr
       result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
 
 {.pop.} # profiler: off, stackTrace: off
diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim
deleted file mode 100644
index 283919503..000000000
--- a/lib/system/gc2.nim
+++ /dev/null
@@ -1,777 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2017 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-#            Garbage Collector
-#
-# The basic algorithm is an incremental mark
-# and sweep GC to free cycles. It is hard realtime in that if you play
-# according to its rules, no deadline will ever be missed.
-# Since this kind of collector is very bad at recycling dead objects
-# early, Nim's codegen emits ``nimEscape`` calls at strategic
-# places. For this to work even 'unsureAsgnRef' needs to mark things
-# so that only return values need to be considered in ``nimEscape``.
-
-{.push profiler:off.}
-
-const
-  CycleIncrease = 2 # is a multiplicative increase
-  InitialCycleThreshold = 512*1024 # start collecting after 500KB
-  ZctThreshold = 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):
-  include "system/timers"
-when defined(memProfiler):
-  proc nimProfile(requestedSize: int) {.benign.}
-
-when hasThreadSupport:
-  include sharedlist
-
-type
-  ObjectSpaceIter = object
-    state: range[-1..0]
-
-iterToProc(allObjects, ptr ObjectSpaceIter, allObjectsAsProc)
-
-const
-  escapedBit = 0b1000 # so that lowest 3 bits are not touched
-  rcBlackOrig = 0b000
-  rcWhiteOrig = 0b001
-  rcGrey = 0b010   # traditional color for incremental mark&sweep
-  rcUnused = 0b011
-  colorMask = 0b011
-type
-  WalkOp = enum
-    waMarkGlobal,    # part of the backup mark&sweep
-    waMarkGrey,
-    waZctDecRef,
-    waDebug
-
-  Phase {.pure.} = enum
-    None, Marking, Sweeping
-  Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
-    # A ref type can have a finalizer that is called before the object's
-    # storage is freed.
-
-  GcStat = object
-    stackScans: int          # number of performed stack scans (for statistics)
-    completedCollections: int    # number of performed full collections
-    maxThreshold: int        # max threshold that has been set
-    maxStackSize: int        # max stack size
-    maxStackCells: int       # max stack cells in ``decStack``
-    cycleTableSize: int      # max entries in cycle table
-    maxPause: int64          # max measured GC pause in nanoseconds
-
-  GcStack {.final, pure.} = object
-    when nimCoroutines:
-      prev: ptr GcStack
-      next: ptr GcStack
-      maxStackSize: int      # Used to track statistics because we can not use
-                             # GcStat.maxStackSize when multiple stacks exist.
-    bottom: pointer
-
-    when withRealTime or nimCoroutines:
-      pos: pointer           # Used with `withRealTime` only for code clarity, see GC_Step().
-    when withRealTime:
-      bottomSaved: pointer
-
-  GcHeap = object # this contains the zero count and
-                  # non-zero count table
-    black, red: int # either 0 or 1.
-    stack: GcStack
-    when nimCoroutines:
-      activeStack: ptr GcStack    # current executing coroutine stack.
-    phase: Phase
-    cycleThreshold: int
-    when useCellIds:
-      idGenerator: int
-    greyStack: CellSeq
-    recGcLock: int           # prevent recursion via finalizers; no thread lock
-    when withRealTime:
-      maxPause: Nanos        # max allowed pause in nanoseconds; active if > 0
-    region: MemRegion        # garbage collected region
-    stat: GcStat
-    additionalRoots: CellSeq # explicit roots for GC_ref/unref
-    spaceIter: ObjectSpaceIter
-    pDumpHeapFile: pointer # File that is used for GC_dumpHeap
-    when hasThreadSupport:
-      toDispose: SharedList[pointer]
-    gcThreadId: int
-
-var
-  gch {.rtlThreadVar.}: GcHeap
-
-when not defined(useNimRtl):
-  instantiateForRegion(gch.region)
-
-template acquire(gch: GcHeap) =
-  when hasThreadSupport and hasSharedHeap:
-    acquireSys(HeapLock)
-
-template release(gch: GcHeap) =
-  when hasThreadSupport and hasSharedHeap:
-    releaseSys(HeapLock)
-
-# Which color to use for new objects is tricky: When we're marking,
-# they have to be *white* so that everything is marked that is only
-# reachable from them. However, when we are sweeping, they have to
-# be black, so that we don't free them prematuredly. In order to save
-# a comparison gch.phase == Phase.Marking, we use the pseudo-color
-# 'red' for new objects.
-template allocColor(): untyped = gch.red
-
-template gcAssert(cond: bool, msg: string) =
-  when defined(useGcAssert):
-    if not cond:
-      echo "[GCASSERT] ", msg
-      GC_disable()
-      writeStackTrace()
-      quit 1
-
-proc cellToUsr(cell: PCell): pointer {.inline.} =
-  # convert object (=pointer to refcount) to pointer to userdata
-  result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell)))
-
-proc usrToCell(usr: pointer): PCell {.inline.} =
-  # convert pointer to userdata to object (=pointer to refcount)
-  result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell)))
-
-proc canBeCycleRoot(c: PCell): bool {.inline.} =
-  result = ntfAcyclic notin c.typ.flags
-
-proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
-  # used for code generation concerning debugging
-  result = usrToCell(c).typ
-
-proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
-  result = 0
-
-# this that has to equals zero, otherwise we have to round up UnitsPerPage:
-when BitsPerPage mod (sizeof(int)*8) != 0:
-  {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
-
-template color(c): untyped = c.refCount and colorMask
-template setColor(c, col) =
-  c.refcount = c.refcount and not colorMask or col
-
-template markAsEscaped(c: PCell) =
-  c.refcount = c.refcount or escapedBit
-
-template didEscape(c: PCell): bool =
-  (c.refCount and escapedBit) != 0
-
-proc writeCell(file: File; msg: cstring, c: PCell) =
-  var kind = -1
-  if c.typ != nil: kind = ord(c.typ.kind)
-  let col = if c.color == rcGrey: 'g'
-            elif c.color == gch.black: 'b'
-            else: 'w'
-  when useCellIds:
-    let id = c.id
-  else:
-    let id = c
-  when defined(nimTypeNames):
-    c_fprintf(file, "%s %p %d escaped=%ld color=%c of type %s\n",
-              msg, id, kind, didEscape(c), col, c.typ.name)
-  elif leakDetector:
-    c_fprintf(file, "%s %p %d escaped=%ld color=%c from %s(%ld)\n",
-              msg, id, kind, didEscape(c), col, c.filename, c.line)
-  else:
-    c_fprintf(file, "%s %p %d escaped=%ld color=%c\n",
-              msg, id, kind, didEscape(c), col)
-
-proc writeCell(msg: cstring, c: PCell) =
-  stdout.writeCell(msg, c)
-
-proc myastToStr[T](x: T): string {.magic: "AstToStr", noSideEffect.}
-
-template gcTrace(cell, state: untyped) =
-  when traceGC: writeCell(myastToStr(state), cell)
-
-# forward declarations:
-proc collectCT(gch: var GcHeap) {.benign.}
-proc isOnStack(p: pointer): bool {.noinline, benign.}
-proc forAllChildren(cell: PCell, op: WalkOp) {.benign.}
-proc doOperation(p: pointer, op: WalkOp) {.benign.}
-proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
-# we need the prototype here for debugging purposes
-
-proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
-  # we MUST access gch as a global here, because this crosses DLL boundaries!
-  discard
-
-proc nimGCref(p: pointer) {.compilerProc.} =
-  let cell = usrToCell(p)
-  markAsEscaped(cell)
-  add(gch.additionalRoots, cell)
-
-proc nimGCunref(p: pointer) {.compilerProc.} =
-  let cell = usrToCell(p)
-  var L = gch.additionalRoots.len-1
-  var i = L
-  let d = gch.additionalRoots.d
-  while i >= 0:
-    if d[i] == cell:
-      d[i] = d[L]
-      dec gch.additionalRoots.len
-      break
-    dec(i)
-
-proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} =
-  discard "can we do some freeing here?"
-
-proc nimGCunrefRC1(p: pointer) {.compilerProc, inline.} =
-  discard "can we do some freeing here?"
-
-template markGrey(x: PCell) =
-  if x.color != 1-gch.black and gch.phase == Phase.Marking:
-    if not isAllocatedPtr(gch.region, x):
-      c_fprintf(stdout, "[GC] markGrey proc: %p\n", x)
-      #GC_dumpHeap()
-      sysAssert(false, "wtf")
-    x.setColor(rcGrey)
-    add(gch.greyStack, x)
-
-proc GC_addCycleRoot*[T](p: ref T) {.inline.} =
-  ## adds 'p' to the cycle candidate set for the cycle collector. It is
-  ## necessary if you used the 'acyclic' pragma for optimization
-  ## purposes and need to break cycles manually.
-  discard
-
-template asgnRefImpl =
-  gcAssert(not isOnStack(dest), "asgnRef")
-  # BUGFIX: first incRef then decRef!
-  if src != nil:
-    let s = usrToCell(src)
-    markAsEscaped(s)
-    markGrey(s)
-  dest[] = src
-
-proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} =
-  # the code generator calls this proc!
-  asgnRefImpl()
-
-proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} =
-  asgnRefImpl()
-
-proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} =
-  # unsureAsgnRef marks 'src' as grey only if dest is not on the
-  # stack. It is used by the code generator if it cannot decide wether a
-  # reference is in the stack or not (this can happen for var parameters).
-  if src != nil:
-    let s = usrToCell(src)
-    markAsEscaped(s)
-    if not isOnStack(dest): markGrey(s)
-  dest[] = src
-
-proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
-  var d = cast[ByteAddress](dest)
-  case n.kind
-  of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
-  of nkList:
-    for i in 0..n.len-1:
-      forAllSlotsAux(dest, n.sons[i], op)
-  of nkCase:
-    var m = selectBranch(dest, n)
-    if m != nil: forAllSlotsAux(dest, m, op)
-  of nkNone: sysAssert(false, "forAllSlotsAux")
-
-proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
-  var d = cast[ByteAddress](dest)
-  if dest == nil: return # nothing to do
-  if ntfNoRefs notin mt.flags:
-    case mt.kind
-    of tyRef, tyOptAsRef, tyString, tySequence: # leaf:
-      doOperation(cast[PPointer](d)[], op)
-    of tyObject, tyTuple:
-      forAllSlotsAux(dest, mt.node, op)
-    of tyArray, tyArrayConstr, tyOpenArray:
-      for i in 0..(mt.size div mt.base.size)-1:
-        forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op)
-    else: discard
-
-proc forAllChildren(cell: PCell, op: WalkOp) =
-  gcAssert(cell != nil, "forAllChildren: 1")
-  gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2")
-  gcAssert(cell.typ != nil, "forAllChildren: 3")
-  gcAssert cell.typ.kind in {tyRef, tyOptAsRef, tySequence, tyString}, "forAllChildren: 4"
-  let marker = cell.typ.marker
-  if marker != nil:
-    marker(cellToUsr(cell), op.int)
-  else:
-    case cell.typ.kind
-    of tyRef, tyOptAsRef: # common case
-      forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
-    of tySequence:
-      var d = cast[ByteAddress](cellToUsr(cell))
-      var s = cast[PGenericSeq](d)
-      if s != nil:
-        for i in 0..s.len-1:
-          forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +%
-            GenericSeqSize), cell.typ.base, op)
-    else: discard
-
-{.push stackTrace: off, profiler:off.}
-proc gcInvariant*() =
-  sysAssert(allocInv(gch.region), "injected")
-  when declared(markForDebug):
-    markForDebug(gch)
-{.pop.}
-
-include gc_common
-
-proc initGC() =
-  when not defined(useNimRtl):
-    gch.red = (1-gch.black)
-    gch.cycleThreshold = InitialCycleThreshold
-    gch.stat.stackScans = 0
-    gch.stat.completedCollections = 0
-    gch.stat.maxThreshold = 0
-    gch.stat.maxStackSize = 0
-    gch.stat.maxStackCells = 0
-    gch.stat.cycleTableSize = 0
-    # init the rt
-    init(gch.additionalRoots)
-    init(gch.greyStack)
-    when hasThreadSupport:
-      init(gch.toDispose)
-    gch.gcThreadId = atomicInc(gHeapidGenerator) - 1
-    gcAssert(gch.gcThreadId >= 0, "invalid computed thread ID")
-
-proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
-  # generates a new object and sets its reference counter to 0
-  sysAssert(allocInv(gch.region), "rawNewObj begin")
-  gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
-  collectCT(gch)
-  var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
-  gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
-  # now it is buffered in the ZCT
-  res.typ = typ
-  when leakDetector and not hasThreadSupport:
-    if framePtr != nil and framePtr.prev != nil:
-      res.filename = framePtr.prev.filename
-      res.line = framePtr.prev.line
-  # refcount is zero, color is black, but mark it to be in the ZCT
-  res.refcount = allocColor()
-  sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
-  when logGC: writeCell("new cell", res)
-  gcTrace(res, csAllocated)
-  when useCellIds:
-    inc gch.idGenerator
-    res.id = gch.idGenerator
-  result = cellToUsr(res)
-  sysAssert(allocInv(gch.region), "rawNewObj end")
-
-{.pop.}
-
-proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
-  result = rawNewObj(typ, size, gch)
-  when defined(memProfiler): nimProfile(size)
-
-proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
-  result = rawNewObj(typ, size, gch)
-  zeroMem(result, size)
-  when defined(memProfiler): nimProfile(size)
-
-proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
-  # `newObj` already uses locks, so no need for them here.
-  let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
-  result = newObj(typ, size)
-  cast[PGenericSeq](result).len = len
-  cast[PGenericSeq](result).reserved = len
-  when defined(memProfiler): nimProfile(size)
-
-proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
-  result = newObj(typ, size)
-
-proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
-  result = newSeq(typ, len)
-
-proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
-  acquire(gch)
-  collectCT(gch)
-  var ol = usrToCell(old)
-  sysAssert(ol.typ != nil, "growObj: 1")
-  gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
-
-  var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
-  var elemSize = 1
-  if ol.typ.kind != tyString: elemSize = ol.typ.base.size
-  incTypeSize ol.typ, newsize
-
-  var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
-  copyMem(res, ol, oldsize + sizeof(Cell))
-  zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
-          newsize-oldsize)
-  sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
-  when false:
-    # this is wrong since seqs can be shared via 'shallow':
-    when reallyDealloc: rawDealloc(gch.region, ol)
-    else:
-      zeroMem(ol, sizeof(Cell))
-  when useCellIds:
-    inc gch.idGenerator
-    res.id = gch.idGenerator
-  release(gch)
-  result = cellToUsr(res)
-  when defined(memProfiler): nimProfile(newsize-oldsize)
-
-proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
-  result = growObj(old, newsize, gch)
-
-{.push profiler:off.}
-
-
-template takeStartTime(workPackageSize) {.dirty.} =
-  const workPackage = workPackageSize
-  var debugticker = 1000
-  when withRealTime:
-    var steps = workPackage
-    var t0: Ticks
-    if gch.maxPause > 0: t0 = getticks()
-
-template takeTime {.dirty.} =
-  when withRealTime: dec steps
-  dec debugticker
-
-template checkTime {.dirty.} =
-  if debugticker <= 0:
-    #echo "in loop"
-    debugticker = 1000
-  when withRealTime:
-    if steps == 0:
-      steps = workPackage
-      if gch.maxPause > 0:
-        let duration = getticks() - t0
-        # the GC's measuring is not accurate and needs some cleanup actions
-        # (stack unmarking), so subtract some short amount of time in
-        # order to miss deadlines less often:
-        if duration >= gch.maxPause - 50_000:
-          return false
-
-# ---------------- dump heap ----------------
-
-template dumpHeapFile(gch: var GcHeap): File =
-  cast[File](gch.pDumpHeapFile)
-
-proc debugGraph(s: PCell) =
-  c_fprintf(gch.dumpHeapFile, "child %p\n", s)
-
-proc dumpRoot(gch: var GcHeap; s: PCell) =
-  if isAllocatedPtr(gch.region, s):
-    c_fprintf(gch.dumpHeapFile, "global_root %p\n", s)
-  else:
-    c_fprintf(gch.dumpHeapFile, "global_root_invalid %p\n", s)
-
-proc GC_dumpHeap*(file: File) =
-  ## Dumps the GCed heap's content to a file. Can be useful for
-  ## debugging. Produces an undocumented text file format that
-  ## can be translated into "dot" syntax via the "heapdump2dot" tool.
-  gch.pDumpHeapFile = file
-  var spaceIter: ObjectSpaceIter
-  when false:
-    var d = gch.decStack.d
-    for i in 0 .. gch.decStack.len-1:
-      if isAllocatedPtr(gch.region, d[i]):
-        c_fprintf(file, "onstack %p\n", d[i])
-      else:
-        c_fprintf(file, "onstack_invalid %p\n", d[i])
-  if gch.gcThreadId == 0:
-    for i in 0 .. globalMarkersLen-1: globalMarkers[i]()
-  for i in 0 .. threadLocalMarkersLen-1: threadLocalMarkers[i]()
-  while true:
-    let x = allObjectsAsProc(gch.region, addr spaceIter)
-    if spaceIter.state < 0: break
-    if isCell(x):
-      # cast to PCell is correct here:
-      var c = cast[PCell](x)
-      writeCell(file, "cell ", c)
-      forAllChildren(c, waDebug)
-      c_fprintf(file, "end\n")
-  gch.pDumpHeapFile = nil
-
-proc GC_dumpHeap() =
-  var f: File
-  if open(f, "heap.txt", fmWrite):
-    GC_dumpHeap(f)
-    f.close()
-  else:
-    c_fprintf(stdout, "cannot write heap.txt")
-
-# ---------------- cycle collector -------------------------------------------
-
-proc freeCyclicCell(gch: var GcHeap, c: PCell) =
-  gcAssert(isAllocatedPtr(gch.region, c), "freeCyclicCell: freed pointer?")
-  prepareDealloc(c)
-  gcTrace(c, csCycFreed)
-  when logGC: writeCell("cycle collector dealloc cell", c)
-  when reallyDealloc:
-    sysAssert(allocInv(gch.region), "free cyclic cell")
-    rawDealloc(gch.region, c)
-  else:
-    gcAssert(c.typ != nil, "freeCyclicCell")
-    zeroMem(c, sizeof(Cell))
-
-proc sweep(gch: var GcHeap): bool =
-  takeStartTime(100)
-  #echo "loop start"
-  let white = 1-gch.black
-  #c_fprintf(stdout, "black is %d\n", black)
-  while true:
-    let x = allObjectsAsProc(gch.region, addr gch.spaceIter)
-    if gch.spaceIter.state < 0: break
-    takeTime()
-    if isCell(x):
-      # cast to PCell is correct here:
-      var c = cast[PCell](x)
-      gcAssert c.color != rcGrey, "cell is still grey?"
-      if c.color == white: freeCyclicCell(gch, c)
-      # Since this is incremental, we MUST not set the object to 'white' here.
-      # We could set all the remaining objects to white after the 'sweep'
-      # completed but instead we flip the meaning of black/white to save one
-      # traversal over the heap!
-    checkTime()
-  # prepare for next iteration:
-  #echo "loop end"
-  gch.spaceIter = ObjectSpaceIter()
-  result = true
-
-proc markRoot(gch: var GcHeap, c: PCell) {.inline.} =
-  if c.color == 1-gch.black:
-    c.setColor(rcGrey)
-    add(gch.greyStack, c)
-
-proc markIncremental(gch: var GcHeap): bool =
-  var L = addr(gch.greyStack.len)
-  takeStartTime(100)
-  while L[] > 0:
-    var c = gch.greyStack.d[0]
-    if not isAllocatedPtr(gch.region, c):
-      c_fprintf(stdout, "[GC] not allocated anymore: %p\n", c)
-      #GC_dumpHeap()
-      sysAssert(false, "wtf")
-
-    #sysAssert(isAllocatedPtr(gch.region, c), "markIncremental: isAllocatedPtr")
-    gch.greyStack.d[0] = gch.greyStack.d[L[] - 1]
-    dec(L[])
-    takeTime()
-    if c.color == rcGrey:
-      c.setColor(gch.black)
-      forAllChildren(c, waMarkGrey)
-    elif c.color == (1-gch.black):
-      gcAssert false, "wtf why are there white objects in the greystack?"
-    checkTime()
-  gcAssert gch.greyStack.len == 0, "markIncremental: greystack not empty "
-  result = true
-
-proc markGlobals(gch: var GcHeap) =
-  if gch.gcThreadId == 0:
-    for i in 0 .. globalMarkersLen-1: globalMarkers[i]()
-  for i in 0 .. threadLocalMarkersLen-1: threadLocalMarkers[i]()
-
-proc doOperation(p: pointer, op: WalkOp) =
-  if p == nil: return
-  var c: PCell = usrToCell(p)
-  gcAssert(c != nil, "doOperation: 1")
-  # the 'case' should be faster than function pointers because of easy
-  # prediction:
-  case op
-  of waZctDecRef:
-    #if not isAllocatedPtr(gch.region, c):
-    #  c_fprintf(stdout, "[GC] decref bug: %p", c)
-    gcAssert(isAllocatedPtr(gch.region, c), "decRef: waZctDecRef")
-    discard "use me for nimEscape?"
-  of waMarkGlobal:
-    template handleRoot =
-      if gch.dumpHeapFile.isNil:
-        markRoot(gch, c)
-      else:
-        dumpRoot(gch, c)
-    handleRoot()
-    discard allocInv(gch.region)
-  of waMarkGrey:
-    when false:
-      if not isAllocatedPtr(gch.region, c):
-        c_fprintf(stdout, "[GC] not allocated anymore: MarkGrey %p\n", c)
-        #GC_dumpHeap()
-        sysAssert(false, "wtf")
-    if c.color == 1-gch.black:
-      c.setColor(rcGrey)
-      add(gch.greyStack, c)
-  of waDebug: debugGraph(c)
-
-proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
-  doOperation(d, WalkOp(op))
-
-proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
-  # the addresses are not as cells on the stack, so turn them to cells:
-  sysAssert(allocInv(gch.region), "gcMark begin")
-  var cell = usrToCell(p)
-  var c = cast[ByteAddress](cell)
-  if c >% PageSize:
-    # fast check: does it look like a cell?
-    var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell))
-    if objStart != nil:
-      # mark the cell:
-      markRoot(gch, objStart)
-  sysAssert(allocInv(gch.region), "gcMark end")
-
-proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
-  forEachStackSlot(gch, gcMark)
-
-proc collectALittle(gch: var GcHeap): bool =
-  case gch.phase
-  of Phase.None:
-    if getOccupiedMem(gch.region) >= gch.cycleThreshold:
-      gch.phase = Phase.Marking
-      markGlobals(gch)
-      result = collectALittle(gch)
-      #when false: c_fprintf(stdout, "collectALittle: introduced bug E %ld\n", gch.phase)
-      #discard allocInv(gch.region)
-  of Phase.Marking:
-    when hasThreadSupport:
-      for c in gch.toDispose:
-        nimGCunref(c)
-    prepareForInteriorPointerChecking(gch.region)
-    markStackAndRegisters(gch)
-    inc(gch.stat.stackScans)
-    if markIncremental(gch):
-      gch.phase = Phase.Sweeping
-      gch.red = 1 - gch.red
-  of Phase.Sweeping:
-    gcAssert gch.greyStack.len == 0, "greystack not empty"
-    when hasThreadSupport:
-      for c in gch.toDispose:
-        nimGCunref(c)
-    if sweep(gch):
-      gch.phase = Phase.None
-      # flip black/white meanings:
-      gch.black = 1 - gch.black
-      gcAssert gch.red == 1 - gch.black, "red color is wrong"
-      inc(gch.stat.completedCollections)
-      result = true
-
-proc collectCTBody(gch: var GcHeap) =
-  when withRealTime:
-    let t0 = getticks()
-  sysAssert(allocInv(gch.region), "collectCT: begin")
-
-  when not nimCoroutines:
-    gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
-  #gch.stat.maxStackCells = max(gch.stat.maxStackCells, gch.decStack.len)
-  if collectALittle(gch):
-    gch.cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() *
-                              CycleIncrease)
-    gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
-  sysAssert(allocInv(gch.region), "collectCT: end")
-  when withRealTime:
-    let duration = getticks() - t0
-    gch.stat.maxPause = max(gch.stat.maxPause, duration)
-    when defined(reportMissedDeadlines):
-      if gch.maxPause > 0 and duration > gch.maxPause:
-        c_fprintf(stdout, "[GC] missed deadline: %ld\n", duration)
-
-when nimCoroutines:
-  proc currentStackSizes(): int =
-    for stack in items(gch.stack):
-      result = result + stack.stackSize()
-
-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.
-  when nimCoroutines:
-    let stackMarkCosts = max(currentStackSizes() div (16*sizeof(int)), ZctThreshold)
-  else:
-    let stackMarkCosts = max(stackSize() div (16*sizeof(int)), ZctThreshold)
-  if (gch.greyStack.len >= stackMarkCosts or (cycleGC and
-      getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
-      gch.recGcLock == 0:
-    collectCTBody(gch)
-
-when withRealTime:
-  proc toNano(x: int): Nanos {.inline.} =
-    result = x * 1000
-
-  proc GC_setMaxPause*(MaxPauseInUs: int) =
-    gch.maxPause = MaxPauseInUs.toNano
-
-  proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) =
-    gch.maxPause = us.toNano
-    #if (getOccupiedMem(gch.region)>=gch.cycleThreshold) or
-    #    alwaysGC or strongAdvice:
-    collectCTBody(gch)
-
-  proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} =
-    if stackSize >= 0:
-      var stackTop {.volatile.}: pointer
-      gch.getActiveStack().pos = addr(stackTop)
-
-      for stack in gch.stack.items():
-        stack.bottomSaved = stack.bottom
-        when stackIncreases:
-          stack.bottom = cast[pointer](
-            cast[ByteAddress](stack.pos) - sizeof(pointer) * 6 - stackSize)
-        else:
-          stack.bottom = cast[pointer](
-            cast[ByteAddress](stack.pos) + sizeof(pointer) * 6 + stackSize)
-
-    GC_step(gch, us, strongAdvice)
-
-    if stackSize >= 0:
-      for stack in gch.stack.items():
-        stack.bottom = stack.bottomSaved
-
-when not defined(useNimRtl):
-  proc GC_disable() =
-    when hasThreadSupport and hasSharedHeap:
-      discard atomicInc(gch.recGcLock, 1)
-    else:
-      inc(gch.recGcLock)
-  proc GC_enable() =
-    if gch.recGcLock > 0:
-      when hasThreadSupport and hasSharedHeap:
-        discard atomicDec(gch.recGcLock, 1)
-      else:
-        dec(gch.recGcLock)
-
-  proc GC_setStrategy(strategy: GC_Strategy) =
-    discard
-
-  proc GC_enableMarkAndSweep() = discard
-  proc GC_disableMarkAndSweep() = discard
-
-  proc GC_fullCollect() =
-    var oldThreshold = gch.cycleThreshold
-    gch.cycleThreshold = 0 # forces cycle collection
-    collectCT(gch)
-    gch.cycleThreshold = oldThreshold
-
-  proc GC_getStatistics(): string =
-    GC_disable()
-    result = "[GC] total memory: " & $(getTotalMem()) & "\n" &
-             "[GC] occupied memory: " & $(getOccupiedMem()) & "\n" &
-             "[GC] stack scans: " & $gch.stat.stackScans & "\n" &
-             "[GC] stack cells: " & $gch.stat.maxStackCells & "\n" &
-             "[GC] completed collections: " & $gch.stat.completedCollections & "\n" &
-             "[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
-             "[GC] grey stack capacity: " & $gch.greyStack.cap & "\n" &
-             "[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
-             "[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000) & "\n"
-    when nimCoroutines:
-      result.add "[GC] number of stacks: " & $gch.stack.len & "\n"
-      for stack in items(gch.stack):
-        result.add "[GC]   stack " & stack.bottom.repr & "[GC]     max stack size " & $stack.maxStackSize & "\n"
-    else:
-      result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
-    GC_enable()
-
-{.pop.}
diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim
index 939776a58..eb0884560 100644
--- a/lib/system/gc_common.nim
+++ b/lib/system/gc_common.nim
@@ -37,50 +37,61 @@ when defined(nimTypeNames):
         a[j] = v
       if h == 1: break
 
-  proc dumpNumberOfInstances* =
-    # also add the allocated strings to the list of known types:
+  iterator dumpHeapInstances*(): tuple[name: cstring; count: int; sizes: int] =
+    ## Iterate over summaries of types on heaps.
+    ## This data may be inaccurate if allocations
+    ## are made by the iterator body.
     if strDesc.nextType == nil:
       strDesc.nextType = nimTypeRoot
       strDesc.name = "string"
       nimTypeRoot = addr strDesc
+    var it = nimTypeRoot
+    while it != nil:
+      if (it.instances > 0 or it.sizes != 0):
+        yield (it.name, it.instances, it.sizes)
+      it = it.nextType
+
+  proc dumpNumberOfInstances* =
     var a: InstancesInfo
     var n = 0
-    var it = nimTypeRoot
     var totalAllocated = 0
-    while it != nil:
-      if (it.instances > 0 or it.sizes != 0) and n < a.len:
-        a[n] = (it.name, it.instances, it.sizes)
-        inc n
+    for it in dumpHeapInstances():
+      a[n] = it
+      inc n
       inc totalAllocated, it.sizes
-      it = it.nextType
     sortInstances(a, n)
     for i in 0 .. n-1:
-      c_fprintf(stdout, "[Heap] %s: #%ld; bytes: %ld\n", a[i][0], a[i][1], a[i][2])
-    c_fprintf(stdout, "[Heap] total number of bytes: %ld\n", totalAllocated)
+      c_fprintf(cstdout, "[Heap] %s: #%ld; bytes: %ld\n", a[i][0], a[i][1], a[i][2])
+    c_fprintf(cstdout, "[Heap] total number of bytes: %ld\n", totalAllocated)
+    when defined(nimTypeNames):
+      let (allocs, deallocs) = getMemCounters()
+      c_fprintf(cstdout, "[Heap] allocs/deallocs: %ld/%ld\n", allocs, deallocs)
 
   when defined(nimGcRefLeak):
     proc oomhandler() =
-      c_fprintf(stdout, "[Heap] ROOTS: #%ld\n", gch.additionalRoots.len)
+      c_fprintf(cstdout, "[Heap] ROOTS: #%ld\n", gch.additionalRoots.len)
       writeLeaks()
 
     outOfMemHook = oomhandler
 
 template decTypeSize(cell, t) =
-  # XXX this needs to use atomics for multithreaded apps!
   when defined(nimTypeNames):
     if t.kind in {tyString, tySequence}:
       let cap = cast[PGenericSeq](cellToUsr(cell)).space
-      let size = if t.kind == tyString: cap+1+GenericSeqSize
-                 else: addInt(mulInt(cap, t.base.size), GenericSeqSize)
-      dec t.sizes, size+sizeof(Cell)
+      let size =
+        if t.kind == tyString:
+          cap + 1 + GenericSeqSize
+        else:
+          align(GenericSeqSize, t.base.align) + cap * t.base.size
+      atomicDec t.sizes, size+sizeof(Cell)
     else:
-      dec t.sizes, t.base.size+sizeof(Cell)
-    dec t.instances
+      atomicDec t.sizes, t.base.size+sizeof(Cell)
+    atomicDec t.instances
 
 template incTypeSize(typ, size) =
   when defined(nimTypeNames):
-    inc typ.instances
-    inc typ.sizes, size+sizeof(Cell)
+    atomicInc typ.instances
+    atomicInc typ.sizes, size+sizeof(Cell)
 
 proc dispose*(x: ForeignCell) =
   when hasThreadSupport:
@@ -148,69 +159,34 @@ else:
   iterator items(first: var GcStack): ptr GcStack = yield addr(first)
   proc len(stack: var GcStack): int = 1
 
-proc stackSize(stack: ptr GcStack): int {.noinline.} =
-  when nimCoroutines:
-    var pos = stack.pos
-  else:
-    var pos {.volatile.}: pointer
-    pos = addr(pos)
-
-  if pos != nil:
-    when defined(stackIncreases):
-      result = cast[ByteAddress](pos) -% cast[ByteAddress](stack.bottom)
-    else:
-      result = cast[ByteAddress](stack.bottom) -% cast[ByteAddress](pos)
-  else:
-    result = 0
-
-proc stackSize(): int {.noinline.} =
-  for stack in gch.stack.items():
-    result = result + stack.stackSize()
-
-when nimCoroutines:
-  proc setPosition(stack: ptr GcStack, position: pointer) =
-    stack.pos = position
-    stack.maxStackSize = max(stack.maxStackSize, stack.stackSize())
-
-  proc setPosition(stack: var GcStack, position: pointer) =
-    setPosition(addr(stack), position)
-
-  proc getActiveStack(gch: var GcHeap): ptr GcStack =
-    return gch.activeStack
-
-  proc isActiveStack(stack: ptr GcStack): bool =
-    return gch.activeStack == stack
-else:
-  # Stack positions do not need to be tracked if coroutines are not used.
-  proc setPosition(stack: ptr GcStack, position: pointer) = discard
-  proc setPosition(stack: var GcStack, position: pointer) = discard
-  # There is just one stack - main stack of the thread. It is active always.
-  proc getActiveStack(gch: var GcHeap): ptr GcStack = addr(gch.stack)
-  proc isActiveStack(stack: ptr GcStack): bool = true
-
-when declared(threadType):
+when defined(nimdoc):
   proc setupForeignThreadGc*() {.gcsafe.} =
     ## Call this if you registered a callback that will be run from a thread not
     ## under your control. This has a cheap thread-local guard, so the GC for
     ## this thread will only be initialized once per thread, no matter how often
     ## it is called.
     ##
-    ## This function is available only when ``--threads:on`` and ``--tlsEmulation:off``
+    ## This function is available only when `--threads:on` and `--tlsEmulation:off`
+    ## switches are used
+    discard
+
+  proc tearDownForeignThreadGc*() {.gcsafe.} =
+    ## Call this to tear down the GC, previously initialized by `setupForeignThreadGc`.
+    ## If GC has not been previously initialized, or has already been torn down, the
+    ## call does nothing.
+    ##
+    ## This function is available only when `--threads:on` and `--tlsEmulation:off`
     ## switches are used
+    discard
+elif declared(threadType):
+  proc setupForeignThreadGc*() {.gcsafe.} =
     if threadType == ThreadType.None:
-      initAllocator()
       var stackTop {.volatile.}: pointer
       nimGC_setStackBottom(addr(stackTop))
       initGC()
       threadType = ThreadType.ForeignThread
 
   proc tearDownForeignThreadGc*() {.gcsafe.} =
-    ## Call this to tear down the GC, previously initialized by ``setupForeignThreadGc``.
-    ## If GC has not been previously initialized, or has already been torn down, the
-    ## call does nothing.
-    ##
-    ## This function is available only when ``--threads:on`` and ``--tlsEmulation:off``
-    ## switches are used
     if threadType != ThreadType.ForeignThread:
       return
     when declared(deallocOsPages): deallocOsPages()
@@ -227,7 +203,7 @@ else:
 # ----------------- stack management --------------------------------------
 #  inspired from Smart Eiffel
 
-when defined(emscripten):
+when defined(emscripten) or defined(wasm):
   const stackIncreases = true
 elif defined(sparc):
   const stackIncreases = false
@@ -237,25 +213,69 @@ elif defined(hppa) or defined(hp9000) or defined(hp9000s300) or
 else:
   const stackIncreases = false
 
+proc stackSize(stack: ptr GcStack): int {.noinline.} =
+  when nimCoroutines:
+    var pos = stack.pos
+  else:
+    var pos {.volatile, noinit.}: pointer
+    pos = addr(pos)
+
+  if pos != nil:
+    when stackIncreases:
+      result = cast[int](pos) -% cast[int](stack.bottom)
+    else:
+      result = cast[int](stack.bottom) -% cast[int](pos)
+  else:
+    result = 0
+
+proc stackSize(): int {.noinline.} =
+  result = 0
+  for stack in gch.stack.items():
+    result = result + stack.stackSize()
+
+when nimCoroutines:
+  proc setPosition(stack: ptr GcStack, position: pointer) =
+    stack.pos = position
+    stack.maxStackSize = max(stack.maxStackSize, stack.stackSize())
+
+  proc setPosition(stack: var GcStack, position: pointer) =
+    setPosition(addr(stack), position)
+
+  proc getActiveStack(gch: var GcHeap): ptr GcStack =
+    return gch.activeStack
+
+  proc isActiveStack(stack: ptr GcStack): bool =
+    return gch.activeStack == stack
+else:
+  # Stack positions do not need to be tracked if coroutines are not used.
+  proc setPosition(stack: ptr GcStack, position: pointer) = discard
+  proc setPosition(stack: var GcStack, position: pointer) = discard
+  # There is just one stack - main stack of the thread. It is active always.
+  proc getActiveStack(gch: var GcHeap): ptr GcStack = addr(gch.stack)
+  proc isActiveStack(stack: ptr GcStack): bool = true
+
 {.push stack_trace: off.}
 when nimCoroutines:
-  proc GC_addStack(bottom: pointer) {.cdecl, exportc.} =
+  proc GC_addStack(bottom: pointer) {.cdecl, dynlib, exportc.} =
     # c_fprintf(stdout, "GC_addStack: %p;\n", bottom)
     var stack = gch.stack.append()
     stack.bottom = bottom
     stack.setPosition(bottom)
 
-  proc GC_removeStack(bottom: pointer) {.cdecl, exportc.} =
+  proc GC_removeStack(bottom: pointer) {.cdecl, dynlib, exportc.} =
     # c_fprintf(stdout, "GC_removeStack: %p;\n", bottom)
     gch.stack.find(bottom).remove()
 
-  proc GC_setActiveStack(bottom: pointer) {.cdecl, exportc.} =
+  proc GC_setActiveStack(bottom: pointer) {.cdecl, dynlib, exportc.} =
     ## Sets active stack and updates current stack position.
     # c_fprintf(stdout, "GC_setActiveStack: %p;\n", bottom)
     var sp {.volatile.}: pointer
     gch.activeStack = gch.stack.find(bottom)
     gch.activeStack.setPosition(addr(sp))
 
+  proc GC_getActiveStack() : pointer {.cdecl, exportc.} =
+    return gch.activeStack.bottom
+
 when not defined(useNimRtl):
   proc nimGC_setStackBottom(theStackBottom: pointer) =
     # Initializes main stack of the thread.
@@ -275,25 +295,28 @@ when not defined(useNimRtl):
       # the first init must be the one that defines the stack bottom:
       gch.stack.bottom = theStackBottom
     elif theStackBottom != gch.stack.bottom:
-      var a = cast[ByteAddress](theStackBottom) # and not PageMask - PageSize*2
-      var b = cast[ByteAddress](gch.stack.bottom)
+      var a = cast[int](theStackBottom) # and not PageMask - PageSize*2
+      var b = cast[int](gch.stack.bottom)
       #c_fprintf(stdout, "old: %p new: %p;\n",gch.stack.bottom,theStackBottom)
       when stackIncreases:
         gch.stack.bottom = cast[pointer](min(a, b))
       else:
         gch.stack.bottom = cast[pointer](max(a, b))
 
+    when nimCoroutines:
+      if theStackBottom != nil: gch.stack.bottom = theStackBottom
+
     gch.stack.setPosition(theStackBottom)
 {.pop.}
 
 proc isOnStack(p: pointer): bool =
-  var stackTop {.volatile.}: pointer
+  var stackTop {.volatile, noinit.}: pointer
   stackTop = addr(stackTop)
-  var a = cast[ByteAddress](gch.getActiveStack().bottom)
-  var b = cast[ByteAddress](stackTop)
+  var a = cast[int](gch.getActiveStack().bottom)
+  var b = cast[int](stackTop)
   when not stackIncreases:
     swap(a, b)
-  var x = cast[ByteAddress](p)
+  var x = cast[int](p)
   result = a <=% x and x <=% b
 
 when defined(sparc): # For SPARC architecture.
@@ -314,7 +337,7 @@ when defined(sparc): # For SPARC architecture.
     # Addresses decrease as the stack grows.
     while sp <= max:
       gcMark(gch, sp[])
-      sp = cast[PPointer](cast[ByteAddress](sp) +% sizeof(pointer))
+      sp = cast[PPointer](cast[int](sp) +% sizeof(pointer))
 
 elif defined(ELATE):
   {.error: "stack marking code is to be written for this architecture".}
@@ -323,21 +346,28 @@ elif stackIncreases:
   # ---------------------------------------------------------------------------
   # Generic code for architectures where addresses increase as the stack grows.
   # ---------------------------------------------------------------------------
-  var
-    jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int
-      # a little hack to get the size of a JmpBuf in the generated C code
-      # in a platform independent way
+  when defined(emscripten) or defined(wasm):
+    var
+      jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int
+        # a little hack to get the size of a JmpBuf in the generated C code
+        # in a platform independent way
+
+  template forEachStackSlotAux(gch, gcMark: untyped) {.dirty.} =
+    for stack in gch.stack.items():
+      var max = cast[int](gch.stack.bottom)
+      var sp = cast[int](addr(registers)) -% sizeof(pointer)
+      while sp >=% max:
+        gcMark(gch, cast[PPointer](sp)[])
+        sp = sp -% sizeof(pointer)
 
   template forEachStackSlot(gch, gcMark: untyped) {.dirty.} =
-    var registers {.noinit.}: C_JmpBuf
-
-    if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
-      for stack in gch.stack.items():
-        var max = cast[ByteAddress](gch.stack.bottom)
-        var sp = cast[ByteAddress](addr(registers)) -% sizeof(pointer)
-        while sp >=% max:
-          gcMark(gch, cast[PPointer](sp)[])
-          sp = sp -% sizeof(pointer)
+    when defined(emscripten) or defined(wasm):
+      var registers: cint
+      forEachStackSlotAux(gch, gcMark)
+    else:
+      var registers {.noinit.}: C_JmpBuf
+      if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
+        forEachStackSlotAux(gch, gcMark)
 
 else:
   # ---------------------------------------------------------------------------
@@ -353,15 +383,14 @@ else:
     gch.getActiveStack().setPosition(addr(registers))
     if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
       for stack in gch.stack.items():
-        var max = cast[ByteAddress](stack.bottom)
-        var sp = cast[ByteAddress](addr(registers))
+        var max = cast[int](stack.bottom)
+        var sp = cast[int](addr(registers))
         when defined(amd64):
           if stack.isActiveStack():
             # words within the jmp_buf structure may not be properly aligned.
             let regEnd = sp +% sizeof(registers)
             while sp <% regEnd:
               gcMark(gch, cast[PPointer](sp)[])
-              gcMark(gch, cast[PPointer](sp +% sizeof(pointer) div 2)[])
               sp = sp +% sizeof(pointer)
         # Make sure sp is word-aligned
         sp = sp and not (sizeof(pointer) - 1)
@@ -385,7 +414,7 @@ else:
 # end of non-portable code
 # ----------------------------------------------------------------------------
 
-proc prepareDealloc(cell: PCell) =
+proc prepareDealloc(cell: PCell) {.raises: [].} =
   when declared(useMarkForDebug):
     when useMarkForDebug:
       gcAssert(cell notin gch.marked, "Cell still alive!")
@@ -394,7 +423,7 @@ proc prepareDealloc(cell: PCell) =
     # the finalizer could invoke something that
     # allocates memory; this could trigger a garbage
     # collection. Since we are already collecting we
-    # prevend recursive entering here by a lock.
+    # prevent recursive entering here by a lock.
     # XXX: we should set the cell's children to nil!
     inc(gch.recGcLock)
     (cast[Finalizer](t.finalizer))(cellToUsr(cell))
@@ -402,10 +431,10 @@ proc prepareDealloc(cell: PCell) =
   decTypeSize(cell, t)
 
 proc deallocHeap*(runFinalizers = true; allowGcAfterwards = true) =
-  ## Frees the thread local heap. Runs every finalizer if ``runFinalizers```
-  ## is true. If ``allowGcAfterwards`` is true, a minimal amount of allocation
+  ## 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``.
+  ## to `deallocHeap`.
   template deallocCell(x) =
     if isCell(x):
       # cast to PCell is correct here:
@@ -428,26 +457,26 @@ proc deallocHeap*(runFinalizers = true; allowGcAfterwards = true) =
     initGC()
 
 type
-  GlobalMarkerProc = proc () {.nimcall, benign.}
+  GlobalMarkerProc = proc () {.nimcall, benign, raises: [].}
 var
-  globalMarkersLen: int
-  globalMarkers: array[0.. 3499, GlobalMarkerProc]
-  threadLocalMarkersLen: int
-  threadLocalMarkers: array[0.. 3499, GlobalMarkerProc]
+  globalMarkersLen {.exportc.}: int
+  globalMarkers {.exportc.}: array[0..3499, GlobalMarkerProc]
+  threadLocalMarkersLen {.exportc.}: int
+  threadLocalMarkers {.exportc.}: array[0..3499, GlobalMarkerProc]
   gHeapidGenerator: int
 
-proc nimRegisterGlobalMarker(markerProc: GlobalMarkerProc) {.compilerProc.} =
+proc nimRegisterGlobalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} =
   if globalMarkersLen <= high(globalMarkers):
     globalMarkers[globalMarkersLen] = markerProc
     inc globalMarkersLen
   else:
-    echo "[GC] cannot register global variable; too many global variables"
-    quit 1
+    cstderr.rawWrite("[GC] cannot register global variable; too many global variables")
+    rawQuit 1
 
-proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerProc.} =
+proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} =
   if threadLocalMarkersLen <= high(threadLocalMarkers):
     threadLocalMarkers[threadLocalMarkersLen] = markerProc
     inc threadLocalMarkersLen
   else:
-    echo "[GC] cannot register thread local variable; too many thread local variables"
-    quit 1
+    cstderr.rawWrite("[GC] cannot register thread local variable; too many thread local variables")
+    rawQuit 1
diff --git a/lib/system/gc_hooks.nim b/lib/system/gc_hooks.nim
new file mode 100644
index 000000000..ace62eea0
--- /dev/null
+++ b/lib/system/gc_hooks.nim
@@ -0,0 +1,53 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2019 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Hooks for memory management. Can be used to implement custom garbage
+## collectors etc.
+
+type
+  GlobalMarkerProc = proc () {.nimcall, benign, raises: [], tags: [].}
+var
+  globalMarkersLen: int
+  globalMarkers: array[0..3499, GlobalMarkerProc]
+  threadLocalMarkersLen: int
+  threadLocalMarkers: array[0..3499, GlobalMarkerProc]
+
+proc nimRegisterGlobalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} =
+  if globalMarkersLen <= high(globalMarkers):
+    globalMarkers[globalMarkersLen] = markerProc
+    inc globalMarkersLen
+  else:
+    cstderr.rawWrite("[GC] cannot register global variable; too many global variables")
+    rawQuit 1
+
+proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} =
+  if threadLocalMarkersLen <= high(threadLocalMarkers):
+    threadLocalMarkers[threadLocalMarkersLen] = markerProc
+    inc threadLocalMarkersLen
+  else:
+    cstderr.rawWrite("[GC] cannot register thread local variable; too many thread local variables")
+    rawQuit 1
+
+proc traverseGlobals*() =
+  for i in 0..globalMarkersLen-1:
+    globalMarkers[i]()
+
+proc traverseThreadLocals*() =
+  for i in 0..threadLocalMarkersLen-1:
+    threadLocalMarkers[i]()
+
+var
+  newObjHook*: proc (typ: PNimType, size: int): pointer {.nimcall, tags: [], raises: [], gcsafe.}
+  traverseObjHook*: proc (p: pointer, op: int) {.nimcall, tags: [], raises: [], gcsafe.}
+
+proc nimGCvisit(p: pointer, op: int) {.inl, compilerRtl.} =
+  traverseObjHook(p, op)
+
+proc newObj(typ: PNimType, size: int): pointer {.inl, compilerRtl.} =
+  result = newObjHook(typ, size)
diff --git a/lib/system/gc_interface.nim b/lib/system/gc_interface.nim
new file mode 100644
index 000000000..84145f33a
--- /dev/null
+++ b/lib/system/gc_interface.nim
@@ -0,0 +1,100 @@
+# ----------------- GC interface ---------------------------------------------
+const
+  usesDestructors = defined(gcDestructors) or defined(gcHooks)
+
+when not usesDestructors:
+  {.pragma: nodestroy.}
+
+when hasAlloc:
+  type
+    GC_Strategy* = enum  ## The strategy the GC should use for the application.
+      gcThroughput,      ## optimize for throughput
+      gcResponsiveness,  ## optimize for responsiveness (default)
+      gcOptimizeTime,    ## optimize for speed
+      gcOptimizeSpace    ## optimize for memory footprint
+
+when hasAlloc and not defined(js) and not usesDestructors:
+  proc GC_disable*() {.rtl, inl, benign, raises: [].}
+    ## Disables the GC. If called `n` times, `n` calls to `GC_enable`
+    ## are needed to reactivate the GC.
+    ##
+    ## Note that in most circumstances one should only disable
+    ## the mark and sweep phase with
+    ## `GC_disableMarkAndSweep <#GC_disableMarkAndSweep>`_.
+
+  proc GC_enable*() {.rtl, inl, benign, raises: [].}
+    ## Enables the GC again.
+
+  proc GC_fullCollect*() {.rtl, benign.}
+    ## Forces a full garbage collection pass.
+    ## Ordinary code does not need to call this (and should not).
+
+  proc GC_enableMarkAndSweep*() {.rtl, benign.}
+  proc GC_disableMarkAndSweep*() {.rtl, benign.}
+    ## The current implementation uses a reference counting garbage collector
+    ## with a seldomly run mark and sweep phase to free cycles. The mark and
+    ## sweep phase may take a long time and is not needed if the application
+    ## does not create cycles. Thus the mark and sweep phase can be deactivated
+    ## and activated separately from the rest of the GC.
+
+  proc GC_getStatistics*(): string {.rtl, benign.}
+    ## Returns an informative string about the GC's activity. This may be useful
+    ## for tweaking.
+
+  proc GC_ref*[T](x: ref T) {.magic: "GCref", benign.}
+  proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign.}
+  proc GC_ref*(x: string) {.magic: "GCref", benign.}
+    ## Marks the object `x` as referenced, so that it will not be freed until
+    ## it is unmarked via `GC_unref`.
+    ## If called n-times for the same object `x`,
+    ## n calls to `GC_unref` are needed to unmark `x`.
+
+  proc GC_unref*[T](x: ref T) {.magic: "GCunref", benign.}
+  proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign.}
+  proc GC_unref*(x: string) {.magic: "GCunref", benign.}
+    ## See the documentation of `GC_ref <#GC_ref,string>`_.
+
+  proc nimGC_setStackBottom*(theStackBottom: pointer) {.compilerRtl, noinline, benign, raises: [].}
+    ## Expands operating GC stack range to `theStackBottom`. Does nothing
+      ## if current stack bottom is already lower than `theStackBottom`.
+
+when hasAlloc and defined(js):
+  template GC_disable* =
+    {.warning: "GC_disable is a no-op in JavaScript".}
+
+  template GC_enable* =
+    {.warning: "GC_enable is a no-op in JavaScript".}
+
+  template GC_fullCollect* =
+    {.warning: "GC_fullCollect is a no-op in JavaScript".}
+
+  template GC_setStrategy* =
+    {.warning: "GC_setStrategy is a no-op in JavaScript".}
+
+  template GC_enableMarkAndSweep* =
+    {.warning: "GC_enableMarkAndSweep is a no-op in JavaScript".}
+
+  template GC_disableMarkAndSweep* =
+    {.warning: "GC_disableMarkAndSweep is a no-op in JavaScript".}
+
+  template GC_ref*[T](x: ref T) =
+    {.warning: "GC_ref is a no-op in JavaScript".}
+
+  template GC_ref*[T](x: seq[T]) =
+    {.warning: "GC_ref is a no-op in JavaScript".}
+
+  template GC_ref*(x: string) =
+    {.warning: "GC_ref is a no-op in JavaScript".}
+
+  template GC_unref*[T](x: ref T) =
+    {.warning: "GC_unref is a no-op in JavaScript".}
+
+  template GC_unref*[T](x: seq[T]) =
+    {.warning: "GC_unref is a no-op in JavaScript".}
+
+  template GC_unref*(x: string) =
+    {.warning: "GC_unref is a no-op in JavaScript".}
+
+  template GC_getStatistics*(): string =
+    {.warning: "GC_getStatistics is a no-op in JavaScript".}
+    ""
diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim
index 75f9c6749..c885a6893 100644
--- a/lib/system/gc_ms.nim
+++ b/lib/system/gc_ms.nim
@@ -27,7 +27,7 @@ when defined(memProfiler):
   proc nimProfile(requestedSize: int)
 
 when hasThreadSupport:
-  import sharedlist
+  import std/sharedlist
 
 type
   WalkOp = enum
@@ -36,7 +36,7 @@ type
                    # local
     waMarkPrecise  # fast precise marking
 
-  Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
+  Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign, raises: [].}
     # A ref type can have a finalizer that is called before the object's
     # storage is freed.
 
@@ -85,36 +85,26 @@ var
 when not defined(useNimRtl):
   instantiateForRegion(gch.region)
 
-template acquire(gch: GcHeap) =
-  when hasThreadSupport and hasSharedHeap:
-    acquireSys(HeapLock)
-
-template release(gch: GcHeap) =
-  when hasThreadSupport and hasSharedHeap:
-    releaseSys(HeapLock)
-
 template gcAssert(cond: bool, msg: string) =
   when defined(useGcAssert):
     if not cond:
-      echo "[GCASSERT] ", msg
-      quit 1
+      cstderr.rawWrite "[GCASSERT] "
+      cstderr.rawWrite msg
+      rawQuit 1
 
 proc cellToUsr(cell: PCell): pointer {.inline.} =
   # convert object (=pointer to refcount) to pointer to userdata
-  result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell)))
+  result = cast[pointer](cast[int](cell)+%ByteAddress(sizeof(Cell)))
 
 proc usrToCell(usr: pointer): PCell {.inline.} =
   # convert pointer to userdata to object (=pointer to refcount)
-  result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell)))
-
-proc canbeCycleRoot(c: PCell): bool {.inline.} =
-  result = ntfAcyclic notin c.typ.flags
+  result = cast[PCell](cast[int](usr)-%ByteAddress(sizeof(Cell)))
 
 proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
   # used for code generation concerning debugging
   result = usrToCell(c).typ
 
-proc unsureAsgnRef(dest: PPointer, src: pointer) {.inline.} =
+proc unsureAsgnRef(dest: PPointer, src: pointer) {.inline, compilerproc.} =
   dest[] = src
 
 proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
@@ -125,10 +115,10 @@ when BitsPerPage mod (sizeof(int)*8) != 0:
   {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
 
 # forward declarations:
-proc collectCT(gch: var GcHeap; size: int) {.benign.}
-proc forAllChildren(cell: PCell, op: WalkOp) {.benign.}
-proc doOperation(p: pointer, op: WalkOp) {.benign.}
-proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
+proc collectCT(gch: var GcHeap; size: int) {.benign, raises: [].}
+proc forAllChildren(cell: PCell, op: WalkOp) {.benign, raises: [].}
+proc doOperation(p: pointer, op: WalkOp) {.benign, raises: [].}
+proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign, raises: [].}
 # we need the prototype here for debugging purposes
 
 when defined(nimGcRefLeak):
@@ -172,7 +162,7 @@ when defined(nimGcRefLeak):
 
   var ax: array[10_000, GcStackTrace]
 
-proc nimGCref(p: pointer) {.compilerProc.} =
+proc nimGCref(p: pointer) {.compilerproc.} =
   # we keep it from being collected by pretending it's not even allocated:
   when false:
     when withBitvectors: excl(gch.allocated, usrToCell(p))
@@ -181,7 +171,7 @@ proc nimGCref(p: pointer) {.compilerProc.} =
     captureStackTrace(framePtr, ax[gch.additionalRoots.len])
   add(gch.additionalRoots, usrToCell(p))
 
-proc nimGCunref(p: pointer) {.compilerProc.} =
+proc nimGCunref(p: pointer) {.compilerproc.} =
   let cell = usrToCell(p)
   var L = gch.additionalRoots.len-1
   var i = L
@@ -227,7 +217,7 @@ proc initGC() =
     gcAssert(gch.gcThreadId >= 0, "invalid computed thread ID")
 
 proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
-  var d = cast[ByteAddress](dest)
+  var d = cast[int](dest)
   case n.kind
   of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
   of nkList:
@@ -239,11 +229,11 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
   of nkNone: sysAssert(false, "forAllSlotsAux")
 
 proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
-  var d = cast[ByteAddress](dest)
+  var d = cast[int](dest)
   if dest == nil: return # nothing to do
   if ntfNoRefs notin mt.flags:
     case mt.kind
-    of tyRef, tyOptAsRef, tyString, tySequence: # leaf:
+    of tyRef, tyString, tySequence: # leaf:
       doOperation(cast[PPointer](d)[], op)
     of tyObject, tyTuple:
       forAllSlotsAux(dest, mt.node, op)
@@ -255,31 +245,30 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
 proc forAllChildren(cell: PCell, op: WalkOp) =
   gcAssert(cell != nil, "forAllChildren: 1")
   gcAssert(cell.typ != nil, "forAllChildren: 2")
-  gcAssert cell.typ.kind in {tyRef, tyOptAsRef, tySequence, tyString}, "forAllChildren: 3"
+  gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 3"
   let marker = cell.typ.marker
   if marker != nil:
     marker(cellToUsr(cell), op.int)
   else:
     case cell.typ.kind
-    of tyRef, tyOptAsRef: # common case
+    of tyRef: # common case
       forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
     of tySequence:
-      var d = cast[ByteAddress](cellToUsr(cell))
-      var s = cast[PGenericSeq](d)
-      if s != nil:
-        for i in 0..s.len-1:
-          forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +%
-            GenericSeqSize), cell.typ.base, op)
+      when not defined(nimSeqsV2):
+        var d = cast[int](cellToUsr(cell))
+        var s = cast[PGenericSeq](d)
+        if s != nil:
+          for i in 0..s.len-1:
+            forAllChildrenAux(cast[pointer](d +% align(GenericSeqSize, cell.typ.base.align) +% i *% cell.typ.base.size), cell.typ.base, op)
     else: discard
 
 proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
   # generates a new object and sets its reference counter to 0
   incTypeSize typ, size
-  acquire(gch)
-  gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
+  gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
   collectCT(gch, size + sizeof(Cell))
   var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
-  gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
+  gcAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2")
   # now it is buffered in the ZCT
   res.typ = typ
   when leakDetector and not hasThreadSupport:
@@ -287,7 +276,6 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
       res.filename = framePtr.prev.filename
       res.line = framePtr.prev.line
   res.refcount = 0
-  release(gch)
   when withBitvectors: incl(gch.allocated, res)
   when useCellIds:
     inc gch.idGenerator
@@ -310,53 +298,56 @@ proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
   result = rawNewObj(typ, size, gch)
   when defined(memProfiler): nimProfile(size)
 
-proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
-  # `newObj` already uses locks, so no need for them here.
-  let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
-  result = newObj(typ, size)
-  cast[PGenericSeq](result).len = len
-  cast[PGenericSeq](result).reserved = len
-  when defined(memProfiler): nimProfile(size)
-
 proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
   result = rawNewObj(typ, size, gch)
   zeroMem(result, size)
   when defined(memProfiler): nimProfile(size)
 
-proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
-  let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
-  result = newObj(typ, size)
-  cast[PGenericSeq](result).len = len
-  cast[PGenericSeq](result).reserved = len
-  when defined(memProfiler): nimProfile(size)
-
-proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
-  acquire(gch)
-  collectCT(gch, newsize + sizeof(Cell))
-  var ol = usrToCell(old)
-  sysAssert(ol.typ != nil, "growObj: 1")
-  gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
-
-  var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
-  var elemSize = 1
-  if ol.typ.kind != tyString: elemSize = ol.typ.base.size
-  incTypeSize ol.typ, newsize
-
-  var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
-  copyMem(res, ol, oldsize + sizeof(Cell))
-  zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
-          newsize-oldsize)
-  sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
-  when withBitvectors: incl(gch.allocated, res)
-  when useCellIds:
-    inc gch.idGenerator
-    res.id = gch.idGenerator
-  release(gch)
-  result = cellToUsr(res)
-  when defined(memProfiler): nimProfile(newsize-oldsize)
+when not defined(nimSeqsV2):
+  {.push overflowChecks: on.}
+  proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
+    # `newObj` already uses locks, so no need for them here.
+    let size = align(GenericSeqSize, typ.base.align) + len * typ.base.size
+    result = newObj(typ, size)
+    cast[PGenericSeq](result).len = len
+    cast[PGenericSeq](result).reserved = len
+    when defined(memProfiler): nimProfile(size)
+
+  proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
+    let size = align(GenericSeqSize, typ.base.align) + len * typ.base.size
+    result = newObj(typ, size)
+    cast[PGenericSeq](result).len = len
+    cast[PGenericSeq](result).reserved = len
+    when defined(memProfiler): nimProfile(size)
+  {.pop.}
+
+  proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
+    collectCT(gch, newsize + sizeof(Cell))
+    var ol = usrToCell(old)
+    sysAssert(ol.typ != nil, "growObj: 1")
+    gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
+
+    var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
+    var elemSize, elemAlign = 1
+    if ol.typ.kind != tyString:
+      elemSize = ol.typ.base.size
+      elemAlign = ol.typ.base.align
+    incTypeSize ol.typ, newsize
+
+    var oldsize = align(GenericSeqSize, elemAlign) + cast[PGenericSeq](old).len*elemSize
+    copyMem(res, ol, oldsize + sizeof(Cell))
+    zeroMem(cast[pointer](cast[int](res)+% oldsize +% sizeof(Cell)),
+            newsize-oldsize)
+    sysAssert((cast[int](res) and (MemAlign-1)) == 0, "growObj: 3")
+    when withBitvectors: incl(gch.allocated, res)
+    when useCellIds:
+      inc gch.idGenerator
+      res.id = gch.idGenerator
+    result = cellToUsr(res)
+    when defined(memProfiler): nimProfile(newsize-oldsize)
 
-proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
-  result = growObj(old, newsize, gch)
+  proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
+    result = growObj(old, newsize, gch)
 
 {.push profiler:off.}
 
@@ -384,14 +375,14 @@ proc mark(gch: var GcHeap, c: PCell) =
                   c, c.typ.name)
         inc gch.indentation, 2
 
-    c.refCount = rcBlack
+    c.refcount = rcBlack
     gcAssert gch.tempStack.len == 0, "stack not empty!"
     forAllChildren(c, waMarkPrecise)
     while gch.tempStack.len > 0:
       dec gch.tempStack.len
       var d = gch.tempStack.d[gch.tempStack.len]
       if d.refcount == rcWhite:
-        d.refCount = rcBlack
+        d.refcount = rcBlack
         forAllChildren(d, waMarkPrecise)
 
     when defined(nimTracing):
@@ -437,15 +428,6 @@ proc sweep(gch: var GcHeap) =
         if c.refcount == rcBlack: c.refcount = rcWhite
         else: freeCyclicCell(gch, c)
 
-when false:
-  proc newGcInvariant*() =
-    for x in allObjects(gch.region):
-      if isCell(x):
-        var c = cast[PCell](x)
-        if c.typ == nil:
-          writeStackTrace()
-          quit 1
-
 proc markGlobals(gch: var GcHeap) =
   if gch.gcThreadId == 0:
     when defined(nimTracing):
@@ -464,11 +446,10 @@ proc markGlobals(gch: var GcHeap) =
 
 proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
   # the addresses are not as cells on the stack, so turn them to cells:
-  var cell = usrToCell(p)
-  var c = cast[ByteAddress](cell)
+  var c = cast[int](p)
   if c >% PageSize:
     # fast check: does it look like a cell?
-    var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell))
+    var objStart = cast[PCell](interiorAllocatedPtr(gch.region, p))
     if objStart != nil:
       mark(gch, objStart)
 
@@ -495,24 +476,20 @@ 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):
   proc GC_disable() =
-    when hasThreadSupport and hasSharedHeap:
-      atomicInc(gch.recGcLock, 1)
-    else:
-      inc(gch.recGcLock)
+    inc(gch.recGcLock)
   proc GC_enable() =
-    if gch.recGcLock <= 0:
-      raise newException(AssertionError,
-          "API usage error: GC_enable called but GC is already enabled")
-    when hasThreadSupport and hasSharedHeap:
-      atomicDec(gch.recGcLock, 1)
-    else:
-      dec(gch.recGcLock)
+    when defined(nimDoesntTrackDefects):
+      if gch.recGcLock <= 0:
+        raise newException(AssertionDefect,
+            "API usage error: GC_enable called but GC is already enabled")
+    dec(gch.recGcLock)
 
   proc GC_setStrategy(strategy: GC_Strategy) = discard
 
@@ -520,7 +497,7 @@ when not defined(useNimRtl):
     gch.cycleThreshold = InitialThreshold
 
   proc GC_disableMarkAndSweep() =
-    gch.cycleThreshold = high(gch.cycleThreshold)-1
+    gch.cycleThreshold = high(typeof(gch.cycleThreshold))-1
     # set to the max value to suppress the cycle detector
 
   when defined(nimTracing):
@@ -528,12 +505,10 @@ when not defined(useNimRtl):
       gch.tracing = true
 
   proc GC_fullCollect() =
-    acquire(gch)
-    var oldThreshold = gch.cycleThreshold
+    let oldThreshold = gch.cycleThreshold
     gch.cycleThreshold = 0 # forces cycle collection
     collectCT(gch, 0)
     gch.cycleThreshold = oldThreshold
-    release(gch)
 
   proc GC_getStatistics(): string =
     result = "[GC] total memory: " & $getTotalMem() & "\n" &
diff --git a/lib/system/gc_regions.nim b/lib/system/gc_regions.nim
index 06fded86b..d96de7eac 100644
--- a/lib/system/gc_regions.nim
+++ b/lib/system/gc_regions.nim
@@ -7,12 +7,16 @@
 #
 
 # "Stack GC" for embedded devices or ultra performance requirements.
+import std/private/syslocks
 
-when defined(nimphpext):
+when defined(memProfiler):
+  proc nimProfile(requestedSize: int) {.benign.}
+
+when defined(useMalloc):
   proc roundup(x, v: int): int {.inline.} =
     result = (x + (v-1)) and not (v-1)
-  proc emalloc(size: int): pointer {.importc: "_emalloc".}
-  proc efree(mem: pointer) {.importc: "_efree".}
+  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)
@@ -84,16 +88,16 @@ type
     region: ptr MemRegion
 
 var
-  tlRegion {.threadVar.}: MemRegion
-#  tempStrRegion {.threadVar.}: MemRegion  # not yet used
+  tlRegion {.threadvar.}: MemRegion
+#  tempStrRegion {.threadvar.}: MemRegion  # not yet used
 
-template withRegion*(r: MemRegion; body: untyped) =
+template withRegion*(r: var MemRegion; body: untyped) =
   let oldRegion = tlRegion
   tlRegion = r
   try:
     body
   finally:
-    #r = tlRegion
+    r = tlRegion
     tlRegion = oldRegion
 
 template inc(p: pointer, s: int) =
@@ -108,6 +112,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 +122,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:
@@ -179,12 +184,25 @@ proc runFinalizers(c: Chunk) =
       (cast[Finalizer](it.typ.finalizer))(it+!sizeof(ObjHeader))
     it = it.nextFinal
 
+proc runFinalizers(c: Chunk; newbump: pointer) =
+  var it = c.head
+  var prev: ptr ObjHeader = nil
+  while it != nil:
+    let nxt = it.nextFinal
+    if it >= newbump:
+      if it.typ != nil and it.typ.finalizer != nil:
+        (cast[Finalizer](it.typ.finalizer))(it+!sizeof(ObjHeader))
+    elif prev != nil:
+      prev.nextFinal = nil
+    prev = it
+    it = nxt
+
 proc dealloc(r: var MemRegion; p: pointer; size: int) =
   let it = cast[ptr ObjHeader](p-!sizeof(ObjHeader))
   if it.typ != nil and it.typ.finalizer != nil:
     (cast[Finalizer](it.typ.finalizer))(p)
   it.typ = nil
-  # it is benefitial to not use the free lists here:
+  # it is beneficial to not use the free lists here:
   if r.bump -! size == p:
     dec r.bump, size
   when false:
@@ -221,16 +239,15 @@ template computeRemaining(r): untyped =
 
 proc setObstackPtr*(r: var MemRegion; sp: StackPtr) =
   # free everything after 'sp':
-  if sp.current.next != nil:
+  if sp.current != nil and sp.current.next != nil:
     deallocAll(r, sp.current.next)
     sp.current.next = nil
     when false:
       # better leak this memory than be sorry:
       for i in 0..high(r.freeLists): r.freeLists[i] = nil
       r.holes = nil
-  #else:
-  #  deallocAll(r, r.head)
-  #  r.head = nil
+  if r.tail != nil: runFinalizers(r.tail, sp.bump)
+
   r.bump = sp.bump
   r.tail = sp.current
   r.remaining = sp.remaining
@@ -241,15 +258,21 @@ proc deallocAll*() = tlRegion.deallocAll()
 
 proc deallocOsPages(r: var MemRegion) = r.deallocAll()
 
+when false:
+  let obs = obstackPtr()
+  try:
+    body
+  finally:
+    setObstackPtr(obs)
+
 template withScratchRegion*(body: untyped) =
-  var scratch: MemRegion
   let oldRegion = tlRegion
-  tlRegion = scratch
+  tlRegion = MemRegion()
   try:
     body
   finally:
+    deallocAll()
     tlRegion = oldRegion
-    deallocAll(scratch)
 
 when false:
   proc joinRegion*(dest: var MemRegion; src: MemRegion) =
@@ -299,20 +322,21 @@ proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
   result = rawNewObj(tlRegion, typ, size)
   when defined(memProfiler): nimProfile(size)
 
+{.push overflowChecks: on.}
 proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
-  let size = roundup(addInt(mulInt(len, typ.base.size), GenericSeqSize),
-                     MemAlign)
+  let size = roundup(align(GenericSeqSize, typ.base.align) + len * typ.base.size, MemAlign)
   result = rawNewSeq(tlRegion, typ, size)
   zeroMem(result, size)
   cast[PGenericSeq](result).len = len
   cast[PGenericSeq](result).reserved = len
 
 proc newStr(typ: PNimType, len: int; init: bool): pointer {.compilerRtl.} =
-  let size = roundup(addInt(len, GenericSeqSize), MemAlign)
+  let size = roundup(len + GenericSeqSize, MemAlign)
   result = rawNewSeq(tlRegion, typ, size)
   if init: zeroMem(result, size)
   cast[PGenericSeq](result).len = 0
   cast[PGenericSeq](result).reserved = len
+{.pop.}
 
 proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
   result = rawNewObj(tlRegion, typ, size)
@@ -327,7 +351,8 @@ proc growObj(regionUnused: var MemRegion; old: pointer, newsize: int): pointer =
   result = rawNewSeq(sh.region[], typ,
                      roundup(newsize, MemAlign))
   let elemSize = if typ.kind == tyString: 1 else: typ.base.size
-  let oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
+  let elemAlign = if typ.kind == tyString: 1 else: typ.base.align
+  let oldsize = align(GenericSeqSize, elemAlign) + cast[PGenericSeq](old).len*elemSize
   zeroMem(result +! oldsize, newsize-oldsize)
   copyMem(result, old, oldsize)
   dealloc(sh.region[], old, roundup(oldsize, MemAlign))
@@ -339,19 +364,24 @@ proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
   dest[] = src
 proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
   dest[] = src
-proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} =
-  dest[] = src
+proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline,
+  deprecated: "old compiler compat".} = asgnRef(dest, src)
 
-proc alloc(size: Natural): pointer =
-  result = c_malloc(size)
+proc allocImpl(size: Natural): pointer =
+  result = c_malloc(cast[csize_t](size))
   if result == nil: raiseOutOfMem()
-proc alloc0(size: Natural): pointer =
+proc alloc0Impl(size: Natural): pointer =
   result = alloc(size)
   zeroMem(result, size)
-proc realloc(p: pointer, newsize: Natural): pointer =
-  result = c_realloc(p, newsize)
+proc reallocImpl(p: pointer, newsize: Natural): pointer =
+  result = c_realloc(p, cast[csize_t](newsize))
+  if result == nil: raiseOutOfMem()
+proc realloc0Impl(p: pointer, oldsize, newsize: Natural): pointer =
+  result = c_realloc(p, cast[csize_t](newsize))
   if result == nil: raiseOutOfMem()
-proc dealloc(p: pointer) = c_free(p)
+  if newsize > oldsize:
+    zeroMem(cast[pointer](cast[int](result) + oldsize), newsize - oldsize)
+proc deallocImpl(p: pointer) = c_free(p)
 
 proc alloc0(r: var MemRegion; size: Natural): pointer =
   # ignore the region. That is correct for the channels module
@@ -365,16 +395,21 @@ proc alloc(r: var MemRegion; size: Natural): pointer =
 
 proc dealloc(r: var MemRegion; p: pointer) = dealloc(p)
 
-proc allocShared(size: Natural): pointer =
-  result = c_malloc(size)
+proc allocSharedImpl(size: Natural): pointer =
+  result = c_malloc(cast[csize_t](size))
   if result == nil: raiseOutOfMem()
-proc allocShared0(size: Natural): pointer =
+proc allocShared0Impl(size: Natural): pointer =
   result = alloc(size)
   zeroMem(result, size)
-proc reallocShared(p: pointer, newsize: Natural): pointer =
-  result = c_realloc(p, newsize)
+proc reallocSharedImpl(p: pointer, newsize: Natural): pointer =
+  result = c_realloc(p, cast[csize_t](newsize))
+  if result == nil: raiseOutOfMem()
+proc reallocShared0Impl(p: pointer, oldsize, newsize: Natural): pointer =
+  result = c_realloc(p, cast[csize_t](newsize))
   if result == nil: raiseOutOfMem()
-proc deallocShared(p: pointer) = c_free(p)
+  if newsize > oldsize:
+    zeroMem(cast[pointer](cast[int](result) + oldsize), newsize - oldsize)
+proc deallocSharedImpl(p: pointer) = c_free(p)
 
 when hasThreadSupport:
   proc getFreeSharedMem(): int = 0
@@ -403,5 +438,5 @@ proc getTotalMem*(r: MemRegion): int =
 
 proc nimGC_setStackBottom(theStackBottom: pointer) = discard
 
-proc nimGCref(x: pointer) {.compilerProc.} = discard
-proc nimGCunref(x: pointer) {.compilerProc.} = discard
+proc nimGCref(x: pointer) {.compilerproc.} = discard
+proc nimGCunref(x: pointer) {.compilerproc.} = discard
diff --git a/lib/system/hti.nim b/lib/system/hti.nim
index 45b1d1cd3..a26aff982 100644
--- a/lib/system/hti.nim
+++ b/lib/system/hti.nim
@@ -7,12 +7,6 @@
 #    distribution, for details about the copyright.
 #
 
-when declared(NimString):
-  # we are in system module:
-  {.pragma: codegenType, compilerproc.}
-else:
-  {.pragma: codegenType, importc.}
-
 type
   # This should be the same as ast.TTypeKind
   # many enum fields are not used at runtime
@@ -23,8 +17,8 @@ type
     tyEmpty,
     tyArrayConstr,
     tyNil,
-    tyExpr,
-    tyStmt,
+    tyUntyped,
+    tyTyped,
     tyTypeDesc,
     tyGenericInvocation, # ``T[a, b]`` for types to invoke
     tyGenericBody,       # ``T[a, b, body]`` last parameter is the body
@@ -46,7 +40,7 @@ type
     tyPointer,
     tyOpenArray,
     tyString,
-    tyCString,
+    tyCstring,
     tyForward,
     tyInt,
     tyInt8,
@@ -62,10 +56,10 @@ type
     tyUInt16,
     tyUInt32,
     tyUInt64,
-    tyOptAsRef, tyUnused1, tyUnused2,
+    tyOwned, tyUnused1, tyUnused2,
     tyVarargsHidden,
-    tyUnusedHidden,
-    tyProxyHidden,
+    tyUncheckedArray,
+    tyErrorHidden,
     tyBuiltInTypeClassHidden,
     tyUserTypeClassHidden,
     tyUserTypeClassInstHidden,
@@ -75,11 +69,11 @@ type
     tyAnythingHidden,
     tyStaticHidden,
     tyFromExprHidden,
-    tyOpt,
+    tyOptDeprecated,
     tyVoidHidden
 
   TNimNodeKind = enum nkNone, nkSlot, nkList, nkCase
-  TNimNode {.codegenType.} = object
+  TNimNode {.compilerproc.} = object
     kind: TNimNodeKind
     offset: int
     typ: ptr TNimType
@@ -92,23 +86,38 @@ type
     ntfAcyclic = 1,    # type cannot form a cycle
     ntfEnumHole = 2    # enum has holes and thus `$` for them needs the slow
                        # version
-  TNimType {.codegenType.} = object
-    size: int
+  TNimType {.compilerproc.} = object
+    when defined(gcHooks):
+      head*: pointer
+    size*: int
+    align*: int
     kind: TNimKind
     flags: set[TNimTypeFlag]
-    base: ptr TNimType
+    base*: ptr TNimType
     node: ptr TNimNode # valid for tyRecord, tyObject, tyTuple, tyEnum
-    finalizer: pointer # the finalizer for the type
-    marker: proc (p: pointer, op: int) {.nimcall, benign.} # marker proc for GC
-    deepcopy: proc (p: pointer): pointer {.nimcall, benign.}
+    finalizer*: pointer # the finalizer for the type
+    marker*: proc (p: pointer, op: int) {.nimcall, benign, tags: [], raises: [].} # marker proc for GC
+    deepcopy: proc (p: pointer): pointer {.nimcall, benign, tags: [], raises: [].}
+    when defined(nimSeqsV2):
+      typeInfoV2*: pointer
     when defined(nimTypeNames):
       name: cstring
       nextType: ptr TNimType
       instances: int # count the number of instances
       sizes: int # sizes of all instances in bytes
-  PNimType = ptr TNimType
+
+when defined(gcHooks):
+  type
+    PNimType* = ptr TNimType
+else:
+  type
+    PNimType = ptr TNimType
 
 when defined(nimTypeNames):
-  var nimTypeRoot {.codegenType.}: PNimType
+  # Declare this variable only once in system.nim
+  when declared(ThisIsSystem):
+    var nimTypeRoot {.compilerproc.}: PNimType
+  else:
+    var nimTypeRoot {.importc.}: PNimType
 
 # node.len may be the ``first`` element of a set
diff --git a/lib/system/inclrtl.nim b/lib/system/inclrtl.nim
index f9e6754ef..3bf0b9893 100644
--- a/lib/system/inclrtl.nim
+++ b/lib/system/inclrtl.nim
@@ -19,11 +19,6 @@
 when not defined(nimNewShared):
   {.pragma: gcsafe.}
 
-when not defined(nimImmediateDeprecated):
-  {.pragma: oldimmediate, immediate.}
-else:
-  {.pragma: oldimmediate.}
-
 when defined(createNimRtl):
   when defined(useNimRtl):
     {.error: "Cannot create and use nimrtl at the same time!".}
@@ -35,12 +30,13 @@ when defined(createNimRtl):
   {.pragma: inl.}
   {.pragma: compilerRtl, compilerproc, exportc: "nimrtl_$1", dynlib.}
 elif defined(useNimRtl):
-  when defined(windows):
-    const nimrtl* = "nimrtl.dll"
-  elif defined(macosx):
-    const nimrtl* = "libnimrtl.dylib"
-  else:
-    const nimrtl* = "libnimrtl.so"
+  #[
+  `{.rtl.}` should only be used for non-generic procs.
+  ]#
+  const nimrtl* =
+    when defined(windows): "nimrtl.dll"
+    elif defined(macosx): "libnimrtl.dylib"
+    else: "libnimrtl.so"
   {.pragma: rtl, importc: "nimrtl_$1", dynlib: nimrtl, gcsafe.}
   {.pragma: inl.}
   {.pragma: compilerRtl, compilerproc, importc: "nimrtl_$1", dynlib: nimrtl.}
@@ -49,15 +45,6 @@ else:
   {.pragma: inl, inline.}
   {.pragma: compilerRtl, compilerproc.}
 
-when not defined(nimsuperops):
-  {.pragma: operator.}
+{.pragma: benign, gcsafe.}
 
-when defined(nimlocks):
-  {.pragma: benign, gcsafe, locks: 0.}
-else:
-  {.pragma: benign, gcsafe.}
-
-when defined(nimTableGet):
-  {.pragma: deprecatedGet, deprecated.}
-else:
-  {.pragma: deprecatedGet.}
+{.push sinkInference: on.}
diff --git a/lib/system/indexerrors.nim b/lib/system/indexerrors.nim
new file mode 100644
index 000000000..6a8cb8a0a
--- /dev/null
+++ b/lib/system/indexerrors.nim
@@ -0,0 +1,15 @@
+# imported by other modules, unlike helpers.nim which is included
+# xxx this is now included instead of imported, we should import instead
+
+template formatErrorIndexBound*[T](i, a, b: T): string =
+  when defined(standalone):
+    "indexOutOfBounds"
+  else:
+    if b < a: "index out of bounds, the container is empty"
+    else: "index " & $i & " not in " & $a & " .. " & $b
+
+template formatErrorIndexBound*[T](i, n: T): string =
+  formatErrorIndexBound(i, 0, n)
+
+template formatFieldDefect*(f, discVal): string =
+  f & discVal & "'"
diff --git a/lib/system/indices.nim b/lib/system/indices.nim
new file mode 100644
index 000000000..f2bad2528
--- /dev/null
+++ b/lib/system/indices.nim
@@ -0,0 +1,164 @@
+when not defined(nimHasSystemRaisesDefect):
+  {.pragma: systemRaisesDefect.}
+
+type
+  BackwardsIndex* = distinct int ## Type that is constructed by `^` for
+                                 ## reversed array accesses.
+                                 ## (See `^ template <#^.t,int>`_)
+
+template `^`*(x: int): BackwardsIndex = BackwardsIndex(x)
+  ## Builtin `roof`:idx: operator that can be used for convenient array access.
+  ## `a[^x]` is a shortcut for `a[a.len-x]`.
+  ##
+  ##   ```nim
+  ##   let
+  ##     a = [1, 3, 5, 7, 9]
+  ##     b = "abcdefgh"
+  ##
+  ##   echo a[^1] # => 9
+  ##   echo b[^2] # => g
+  ##   ```
+
+proc `[]`*[T](s: openArray[T]; i: BackwardsIndex): T {.inline, systemRaisesDefect.} =
+  system.`[]`(s, s.len - int(i))
+
+proc `[]`*[Idx, T](a: array[Idx, T]; i: BackwardsIndex): T {.inline, systemRaisesDefect.} =
+  a[Idx(a.len - int(i) + int low(a))]
+proc `[]`*(s: string; i: BackwardsIndex): char {.inline, systemRaisesDefect.} = s[s.len - int(i)]
+
+proc `[]`*[T](s: var openArray[T]; i: BackwardsIndex): var T {.inline, systemRaisesDefect.} =
+  system.`[]`(s, s.len - int(i))
+proc `[]`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex): var T {.inline, systemRaisesDefect.} =
+  a[Idx(a.len - int(i) + int low(a))]
+proc `[]`*(s: var string; i: BackwardsIndex): var char {.inline, systemRaisesDefect.} = s[s.len - int(i)]
+
+proc `[]=`*[T](s: var openArray[T]; i: BackwardsIndex; x: T) {.inline, systemRaisesDefect.} =
+  system.`[]=`(s, s.len - int(i), x)
+proc `[]=`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex; x: T) {.inline, systemRaisesDefect.} =
+  a[Idx(a.len - int(i) + int low(a))] = x
+proc `[]=`*(s: var string; i: BackwardsIndex; x: char) {.inline, systemRaisesDefect.} =
+  s[s.len - int(i)] = x
+
+template `..^`*(a, b: untyped): untyped =
+  ## A shortcut for `.. ^` to avoid the common gotcha that a space between
+  ## '..' and '^' is required.
+  a .. ^b
+
+template `..<`*(a, b: untyped): untyped =
+  ## A shortcut for `a .. pred(b)`.
+  ##   ```nim
+  ##   for i in 5 ..< 9:
+  ##     echo i # => 5; 6; 7; 8
+  ##   ```
+  a .. (when b is BackwardsIndex: succ(b) else: pred(b))
+
+template `[]`*(s: string; i: int): char = arrGet(s, i)
+template `[]=`*(s: string; i: int; val: char) = arrPut(s, i, val)
+
+template `^^`(s, i: untyped): untyped =
+  (when i is BackwardsIndex: s.len - int(i) else: int(i))
+
+template spliceImpl(s, a, L, b: typed): untyped =
+  # make room for additional elements or cut:
+  var shift = b.len - max(0,L)  # ignore negative slice size
+  var newLen = s.len + shift
+  if shift > 0:
+    # enlarge:
+    setLen(s, newLen)
+    for i in countdown(newLen-1, a+b.len): movingCopy(s[i], s[i-shift])
+  else:
+    for i in countup(a+b.len, newLen-1): movingCopy(s[i], s[i-shift])
+    # cut down:
+    setLen(s, newLen)
+  # fill the hole:
+  for i in 0 ..< b.len: s[a+i] = b[i]
+
+proc `[]`*[T, U: Ordinal](s: string, x: HSlice[T, U]): string {.inline, systemRaisesDefect.} =
+  ## Slice operation for strings.
+  ## Returns the inclusive range `[s[x.a], s[x.b]]`:
+  ##   ```nim
+  ##   var s = "abcdef"
+  ##   assert s[1..3] == "bcd"
+  ##   ```
+  let a = s ^^ x.a
+  let L = (s ^^ x.b) - a + 1
+  result = newString(L)
+  for i in 0 ..< L: result[i] = s[i + a]
+
+proc `[]=`*[T, U: Ordinal](s: var string, x: HSlice[T, U], b: string) {.systemRaisesDefect.} =
+  ## Slice assignment for strings.
+  ##
+  ## If `b.len` is not exactly the number of elements that are referred to
+  ## by `x`, a `splice`:idx: is performed:
+  ##
+  runnableExamples:
+    var s = "abcdefgh"
+    s[1 .. ^2] = "xyz"
+    assert s == "axyzh"
+
+  var a = s ^^ x.a
+  var L = (s ^^ x.b) - a + 1
+  if L == b.len:
+    for i in 0..<L: s[i+a] = b[i]
+  else:
+    spliceImpl(s, a, L, b)
+
+proc `[]`*[Idx, T; U, V: Ordinal](a: array[Idx, T], x: HSlice[U, V]): seq[T] {.systemRaisesDefect.} =
+  ## Slice operation for arrays.
+  ## Returns the inclusive range `[a[x.a], a[x.b]]`:
+  ##   ```nim
+  ##   var a = [1, 2, 3, 4]
+  ##   assert a[0..2] == @[1, 2, 3]
+  ##   ```
+  ##
+  ## See also:
+  ## * `toOpenArray(array[I, T];I,I) <#toOpenArray,array[I,T],I,I>`_
+  let xa = a ^^ x.a
+  let L = (a ^^ x.b) - xa + 1
+  result = newSeq[T](L)
+  for i in 0..<L: result[i] = a[Idx(i + xa)]
+
+proc `[]=`*[Idx, T; U, V: Ordinal](a: var array[Idx, T], x: HSlice[U, V], b: openArray[T]) {.systemRaisesDefect.} =
+  ## Slice assignment for arrays.
+  ##   ```nim
+  ##   var a = [10, 20, 30, 40, 50]
+  ##   a[1..2] = @[99, 88]
+  ##   assert a == [10, 99, 88, 40, 50]
+  ##   ```
+  let xa = a ^^ x.a
+  let L = (a ^^ x.b) - xa + 1
+  if L == b.len:
+    for i in 0..<L: a[Idx(i + xa)] = b[i]
+  else:
+    sysFatal(RangeDefect, "different lengths for slice assignment")
+
+proc `[]`*[T; U, V: Ordinal](s: openArray[T], x: HSlice[U, V]): seq[T] {.systemRaisesDefect.} =
+  ## Slice operation for sequences.
+  ## Returns the inclusive range `[s[x.a], s[x.b]]`:
+  ##   ```nim
+  ##   var s = @[1, 2, 3, 4]
+  ##   assert s[0..2] == @[1, 2, 3]
+  ##   ```
+  ##
+  ## See also:
+  ## * `toOpenArray(openArray[T];int,int) <#toOpenArray,openArray[T],int,int>`_
+  let a = s ^^ x.a
+  let L = (s ^^ x.b) - a + 1
+  newSeq(result, L)
+  for i in 0 ..< L: result[i] = s[i + a]
+
+proc `[]=`*[T; U, V: Ordinal](s: var seq[T], x: HSlice[U, V], b: openArray[T]) {.systemRaisesDefect.} =
+  ## Slice assignment for sequences.
+  ##
+  ## If `b.len` is not exactly the number of elements that are referred to
+  ## by `x`, a `splice`:idx: is performed.
+  runnableExamples:
+    var s = @"abcdefgh"
+    s[1 .. ^2] = @"xyz"
+    assert s == @"axyzh"
+  let a = s ^^ x.a
+  let L = (s ^^ x.b) - a + 1
+  if L == b.len:
+    for i in 0 ..< L: s[i+a] = b[i]
+  else:
+    spliceImpl(s, a, L, b)
diff --git a/lib/system/integerops.nim b/lib/system/integerops.nim
new file mode 100644
index 000000000..4ef3594f1
--- /dev/null
+++ b/lib/system/integerops.nim
@@ -0,0 +1,132 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# Integer arithmetic with overflow checking. Uses
+# intrinsics or inline assembler.
+
+proc raiseOverflow {.compilerproc, noinline.} =
+  # a single proc to reduce code size to a minimum
+  sysFatal(OverflowDefect, "over- or underflow")
+
+proc raiseDivByZero {.compilerproc, noinline.} =
+  sysFatal(DivByZeroDefect, "division by zero")
+
+{.pragma: nimbaseH, importc, nodecl, noSideEffect, compilerproc.}
+
+when not defined(nimEmulateOverflowChecks):
+  # take the #define from nimbase.h
+
+  proc nimAddInt(a, b: int, res: ptr int): bool {.nimbaseH.}
+  proc nimSubInt(a, b: int, res: ptr int): bool {.nimbaseH.}
+  proc nimMulInt(a, b: int, res: ptr int): bool {.nimbaseH.}
+
+  proc nimAddInt64(a, b: int64; res: ptr int64): bool {.nimbaseH.}
+  proc nimSubInt64(a, b: int64; res: ptr int64): bool {.nimbaseH.}
+  proc nimMulInt64(a, b: int64; res: ptr int64): bool {.nimbaseH.}
+
+# unary minus and 'abs' not required here anymore and are directly handled
+# in the code generator.
+# 'nimModInt' does exist in nimbase.h without check as we moved the
+# check for 0 to the codgen.
+proc nimModInt(a, b: int; res: ptr int): bool {.nimbaseH.}
+
+proc nimModInt64(a, b: int64; res: ptr int64): bool {.nimbaseH.}
+
+# Platform independent versions.
+
+template addImplFallback(name, T, U) {.dirty.} =
+  when not declared(name):
+    proc name(a, b: T; res: ptr T): bool {.compilerproc, inline.} =
+      let r = cast[T](cast[U](a) + cast[U](b))
+      if (r xor a) >= T(0) or (r xor b) >= T(0):
+        res[] = r
+      else:
+        result = true
+
+addImplFallback(nimAddInt, int, uint)
+addImplFallback(nimAddInt64, int64, uint64)
+
+template subImplFallback(name, T, U) {.dirty.} =
+  when not declared(name):
+    proc name(a, b: T; res: ptr T): bool {.compilerproc, inline.} =
+      let r = cast[T](cast[U](a) - cast[U](b))
+      if (r xor a) >= 0 or (r xor not b) >= 0:
+        res[] = r
+      else:
+        result = true
+
+subImplFallback(nimSubInt, int, uint)
+subImplFallback(nimSubInt64, int64, uint64)
+
+template mulImplFallback(name, T, U, conv) {.dirty.} =
+  #
+  # This code has been inspired by Python's source code.
+  # The native int product x*y is either exactly right or *way* off, being
+  # just the last n bits of the true product, where n is the number of bits
+  # in an int (the delivered product is the true product plus i*2**n for
+  # some integer i).
+  #
+  # The native float64 product x*y is subject to three
+  # rounding errors: on a sizeof(int)==8 box, each cast to double can lose
+  # info, and even on a sizeof(int)==4 box, the multiplication can lose info.
+  # But, unlike the native int product, it's not in *range* trouble:  even
+  # if sizeof(int)==32 (256-bit ints), the product easily fits in the
+  # dynamic range of a float64. So the leading 50 (or so) bits of the float64
+  # product are correct.
+  #
+  # We check these two ways against each other, and declare victory if
+  # they're approximately the same. Else, because the native int product is
+  # the only one that can lose catastrophic amounts of information, it's the
+  # native int product that must have overflowed.
+  #
+  when not declared(name):
+    proc name(a, b: T; res: ptr T): bool {.compilerproc, inline.} =
+      let r = cast[T](cast[U](a) * cast[U](b))
+      let floatProd = conv(a) * conv(b)
+      let resAsFloat = conv(r)
+      # Fast path for normal case: small multiplicands, and no info
+      # is lost in either method.
+      if resAsFloat == floatProd:
+        res[] = r
+      else:
+        # Somebody somewhere lost info. Close enough, or way off? Note
+        # that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
+        # The difference either is or isn't significant compared to the
+        # true value (of which floatProd is a good approximation).
+
+        # abs(diff)/abs(prod) <= 1/32 iff
+        #   32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
+        if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
+          res[] = r
+        else:
+          result = true
+
+mulImplFallback(nimMulInt, int, uint, toFloat)
+mulImplFallback(nimMulInt64, int64, uint64, toBiggestFloat)
+
+
+template divImplFallback(name, T) {.dirty.} =
+  proc name(a, b: T; res: ptr T): bool {.compilerproc, inline.} =
+    # we moved the b == 0 case out into the codegen.
+    if a == low(T) and b == T(-1):
+      result = true
+    else:
+      res[] = a div b
+
+divImplFallback(nimDivInt, int)
+divImplFallback(nimDivInt64, int64)
+
+proc raiseFloatInvalidOp {.compilerproc, noinline.} =
+  sysFatal(FloatInvalidOpDefect, "FPU operation caused a NaN result")
+
+proc raiseFloatOverflow(x: float64) {.compilerproc, noinline.} =
+  if x > 0.0:
+    sysFatal(FloatOverflowDefect, "FPU operation caused an overflow")
+  else:
+    sysFatal(FloatUnderflowDefect, "FPU operations caused an underflow")
diff --git a/lib/system/iterators.nim b/lib/system/iterators.nim
new file mode 100644
index 000000000..125bee98f
--- /dev/null
+++ b/lib/system/iterators.nim
@@ -0,0 +1,353 @@
+## Default iterators for some Nim types.
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+when not defined(nimNoLentIterators):
+  template lent2(T): untyped = lent T
+else:
+  template lent2(T): untyped = T
+
+template unCheckedInc(x) =
+  {.push overflowChecks: off.}
+  inc(x)
+  {.pop.}
+
+iterator items*[T: not char](a: openArray[T]): lent2 T {.inline.} =
+  ## Iterates over each item of `a`.
+  var i = 0
+  while i < len(a):
+    yield a[i]
+    unCheckedInc(i)
+
+iterator items*[T: char](a: openArray[T]): T {.inline.} =
+  ## Iterates over each item of `a`.
+  # a VM bug currently prevents taking address of openArray[char]
+  # elements converted from a string (would fail in `tests/misc/thallo.nim`)
+  # in any case there's no performance advantage of returning char by address.
+  var i = 0
+  while i < len(a):
+    yield a[i]
+    unCheckedInc(i)
+
+iterator mitems*[T](a: var openArray[T]): var T {.inline.} =
+  ## Iterates over each item of `a` so that you can modify the yielded value.
+  var i = 0
+  while i < len(a):
+    yield a[i]
+    unCheckedInc(i)
+
+iterator items*[IX, T](a: array[IX, T]): T {.inline.} =
+  ## Iterates over each item of `a`.
+  when a.len > 0:
+    var i = low(IX)
+    while true:
+      yield a[i]
+      if i >= high(IX): break
+      unCheckedInc(i)
+
+iterator mitems*[IX, T](a: var array[IX, T]): var T {.inline.} =
+  ## Iterates over each item of `a` so that you can modify the yielded value.
+  when a.len > 0:
+    var i = low(IX)
+    while true:
+      yield a[i]
+      if i >= high(IX): break
+      unCheckedInc(i)
+
+iterator items*[T](a: set[T]): T {.inline.} =
+  ## Iterates over each element of `a`. `items` iterates only over the
+  ## elements that are really in the set (and not over the ones the set is
+  ## able to hold).
+  var i = low(T).int
+  while i <= high(T).int:
+    when T is enum and not defined(js):
+      if cast[T](i) in a: yield cast[T](i)
+    else:
+      if T(i) in a: yield T(i)
+    unCheckedInc(i)
+
+iterator items*(a: cstring): char {.inline.} =
+  ## Iterates over each item of `a`.
+  runnableExamples:
+    from std/sequtils import toSeq
+    assert toSeq("abc\0def".cstring) == @['a', 'b', 'c']
+    assert toSeq("abc".cstring) == @['a', 'b', 'c']
+  #[
+  assert toSeq(nil.cstring) == @[] # xxx fails with SIGSEGV
+  this fails with SIGSEGV; unclear whether we want to instead yield nothing
+  or pay a small price to check for `nil`, a benchmark is needed. Note that
+  other procs support `nil`.
+  ]#
+  template impl() =
+    var i = 0
+    let n = len(a)
+    while i < n:
+      yield a[i]
+      unCheckedInc(i)
+  when defined(js): impl()
+  else:
+    when nimvm:
+      # xxx `cstring` should behave like c backend instead.
+      impl()
+    else:
+      var i = 0
+      while a[i] != '\0':
+        yield a[i]
+        unCheckedInc(i)
+
+iterator mitems*(a: var cstring): var char {.inline.} =
+  ## Iterates over each item of `a` so that you can modify the yielded value.
+  # xxx this should give CT error in js RT.
+  runnableExamples:
+    from std/sugar import collect
+    var a = "abc\0def"
+    prepareMutation(a)
+    var b = a.cstring
+    let s = collect:
+      for bi in mitems(b):
+        if bi == 'b': bi = 'B'
+        bi
+    assert s == @['a', 'B', 'c']
+    assert b == "aBc"
+    assert a == "aBc\0def"
+
+  template impl() =
+    var i = 0
+    let n = len(a)
+    while i < n:
+      yield a[i]
+      unCheckedInc(i)
+  when defined(js): impl()
+  else:
+    when nimvm: impl()
+    else:
+      var i = 0
+      while a[i] != '\0':
+        yield a[i]
+        unCheckedInc(i)
+
+iterator items*[T: enum and Ordinal](E: typedesc[T]): T =
+  ## Iterates over the values of `E`.
+  ## See also `enumutils.items` for enums with holes.
+  runnableExamples:
+    type Goo = enum g0 = 2, g1, g2
+    from std/sequtils import toSeq
+    assert Goo.toSeq == [g0, g1, g2]
+  for v in low(E) .. high(E):
+    yield v
+
+iterator items*[T: Ordinal](s: Slice[T]): T =
+  ## Iterates over the slice `s`, yielding each value between `s.a` and `s.b`
+  ## (inclusively).
+  for x in s.a .. s.b:
+    yield x
+
+iterator pairs*[T](a: openArray[T]): tuple[key: int, val: T] {.inline.} =
+  ## Iterates over each item of `a`. Yields `(index, a[index])` pairs.
+  var i = 0
+  while i < len(a):
+    yield (i, a[i])
+    unCheckedInc(i)
+
+iterator mpairs*[T](a: var openArray[T]): tuple[key: int, val: var T]{.inline.} =
+  ## Iterates over each item of `a`. Yields `(index, a[index])` pairs.
+  ## `a[index]` can be modified.
+  var i = 0
+  while i < len(a):
+    yield (i, a[i])
+    unCheckedInc(i)
+
+iterator pairs*[IX, T](a: array[IX, T]): tuple[key: IX, val: T] {.inline.} =
+  ## Iterates over each item of `a`. Yields `(index, a[index])` pairs.
+  when a.len > 0:
+    var i = low(IX)
+    while true:
+      yield (i, a[i])
+      if i >= high(IX): break
+      unCheckedInc(i)
+
+iterator mpairs*[IX, T](a: var array[IX, T]): tuple[key: IX, val: var T] {.inline.} =
+  ## Iterates over each item of `a`. Yields `(index, a[index])` pairs.
+  ## `a[index]` can be modified.
+  when a.len > 0:
+    var i = low(IX)
+    while true:
+      yield (i, a[i])
+      if i >= high(IX): break
+      unCheckedInc(i)
+
+iterator pairs*[T](a: seq[T]): tuple[key: int, val: T] {.inline.} =
+  ## Iterates over each item of `a`. Yields `(index, a[index])` pairs.
+  var i = 0
+  let L = len(a)
+  while i < L:
+    yield (i, a[i])
+    unCheckedInc(i)
+    assert(len(a) == L, "the length of the seq changed while iterating over it")
+
+iterator mpairs*[T](a: var seq[T]): tuple[key: int, val: var T] {.inline.} =
+  ## Iterates over each item of `a`. Yields `(index, a[index])` pairs.
+  ## `a[index]` can be modified.
+  var i = 0
+  let L = len(a)
+  while i < L:
+    yield (i, a[i])
+    unCheckedInc(i)
+    assert(len(a) == L, "the length of the seq changed while iterating over it")
+
+iterator pairs*(a: string): tuple[key: int, val: char] {.inline.} =
+  ## Iterates over each item of `a`. Yields `(index, a[index])` pairs.
+  var i = 0
+  let L = len(a)
+  while i < L:
+    yield (i, a[i])
+    unCheckedInc(i)
+    assert(len(a) == L, "the length of the string changed while iterating over it")
+
+iterator mpairs*(a: var string): tuple[key: int, val: var char] {.inline.} =
+  ## Iterates over each item of `a`. Yields `(index, a[index])` pairs.
+  ## `a[index]` can be modified.
+  var i = 0
+  let L = len(a)
+  while i < L:
+    yield (i, a[i])
+    unCheckedInc(i)
+    assert(len(a) == L, "the length of the string changed while iterating over it")
+
+iterator pairs*(a: cstring): tuple[key: int, val: char] {.inline.} =
+  ## Iterates over each item of `a`. Yields `(index, a[index])` pairs.
+  when defined(js):
+    var i = 0
+    var L = len(a)
+    while i < L:
+      yield (i, a[i])
+      unCheckedInc(i)
+  else:
+    var i = 0
+    while a[i] != '\0':
+      yield (i, a[i])
+      unCheckedInc(i)
+
+iterator mpairs*(a: var cstring): tuple[key: int, val: var char] {.inline.} =
+  ## Iterates over each item of `a`. Yields `(index, a[index])` pairs.
+  ## `a[index]` can be modified.
+  when defined(js):
+    var i = 0
+    var L = len(a)
+    while i < L:
+      yield (i, a[i])
+      unCheckedInc(i)
+  else:
+    var i = 0
+    while a[i] != '\0':
+      yield (i, a[i])
+      unCheckedInc(i)
+
+iterator items*[T](a: seq[T]): lent2 T {.inline.} =
+  ## Iterates over each item of `a`.
+  var i = 0
+  let L = len(a)
+  while i < L:
+    yield a[i]
+    unCheckedInc(i)
+    assert(len(a) == L, "the length of the seq changed while iterating over it")
+
+iterator mitems*[T](a: var seq[T]): var T {.inline.} =
+  ## Iterates over each item of `a` so that you can modify the yielded value.
+  var i = 0
+  let L = len(a)
+  while i < L:
+    yield a[i]
+    unCheckedInc(i)
+    assert(len(a) == L, "the length of the seq changed while iterating over it")
+
+iterator items*(a: string): char {.inline.} =
+  ## Iterates over each item of `a`.
+  var i = 0
+  let L = len(a)
+  while i < L:
+    yield a[i]
+    unCheckedInc(i)
+    assert(len(a) == L, "the length of the string changed while iterating over it")
+
+iterator mitems*(a: var string): var char {.inline.} =
+  ## Iterates over each item of `a` so that you can modify the yielded value.
+  var i = 0
+  let L = len(a)
+  while i < L:
+    yield a[i]
+    unCheckedInc(i)
+    assert(len(a) == L, "the length of the string changed while iterating over it")
+
+
+iterator fields*[T: tuple|object](x: T): RootObj {.
+  magic: "Fields", noSideEffect.} =
+  ## Iterates over every field of `x`.
+  ##
+  ## .. warning:: This really transforms the 'for' and unrolls the loop.
+  ##   The current implementation also has a bug
+  ##   that affects symbol binding in the loop body.
+  runnableExamples:
+    var t = (1, "foo")
+    for v in fields(t): v = default(typeof(v))
+    doAssert t == (0, "")
+
+iterator fields*[S:tuple|object, T:tuple|object](x: S, y: T): tuple[key: string, val: RootObj] {.
+  magic: "Fields", noSideEffect.} =
+  ## Iterates over every field of `x` and `y`.
+  ##
+  ## .. warning:: This really transforms the 'for' and unrolls the loop.
+  ##   The current implementation also has a bug that affects symbol binding
+  ##   in the loop body.
+  runnableExamples:
+    var t1 = (1, "foo")
+    var t2 = default(typeof(t1))
+    for v1, v2 in fields(t1, t2): v2 = v1
+    doAssert t1 == t2
+
+iterator fieldPairs*[T: tuple|object](x: T): tuple[key: string, val: RootObj] {.
+  magic: "FieldPairs", noSideEffect.} =
+  ## Iterates over every field of `x` returning their name and value.
+  ##
+  ## When you iterate over objects with different field types you have to use
+  ## the compile time `when` instead of a runtime `if` to select the code
+  ## you want to run for each type. To perform the comparison use the `is
+  ## operator <manual.html#generics-is-operator>`_.
+  ## Another way to do the same without `when` is to leave the task of
+  ## picking the appropriate code to a secondary proc which you overload for
+  ## each field type and pass the `value` to.
+  ##
+  ## .. warning:: This really transforms the 'for' and unrolls the loop. The
+  ##   current implementation also has a bug that affects symbol binding in the
+  ##   loop body.
+  runnableExamples:
+    type
+      Custom = object
+        foo: string
+        bar: bool
+    proc `$`(x: Custom): string =
+      result = "Custom:"
+      for name, value in x.fieldPairs:
+        when value is bool:
+          result.add("\n\t" & name & " is " & $value)
+        else:
+          result.add("\n\t" & name & " '" & value & "'")
+
+iterator fieldPairs*[S: tuple|object, T: tuple|object](x: S, y: T): tuple[
+  key: string, a, b: RootObj] {.
+  magic: "FieldPairs", noSideEffect.} =
+  ## Iterates over every field of `x` and `y`.
+  ##
+  ## .. warning:: This really transforms the 'for' and unrolls the loop.
+  ##   The current implementation also has a bug that affects symbol binding
+  ##   in the loop body.
+  runnableExamples:
+    type Foo = object
+      x1: int
+      x2: string
+    var a1 = Foo(x1: 12, x2: "abc")
+    var a2: Foo
+    for name, v1, v2 in fieldPairs(a1, a2):
+      when name == "x2": v2 = v1
+    doAssert a2 == Foo(x1: 0, x2: "abc")
diff --git a/lib/system/iterators_1.nim b/lib/system/iterators_1.nim
new file mode 100644
index 000000000..d00e3f823
--- /dev/null
+++ b/lib/system/iterators_1.nim
@@ -0,0 +1,180 @@
+when sizeof(int) <= 2:
+  type IntLikeForCount = int|int8|int16|char|bool|uint8|enum
+else:
+  type IntLikeForCount = int|int8|int16|int32|char|bool|uint8|uint16|enum
+
+iterator countdown*[T](a, b: T, step: Positive = 1): T {.inline.} =
+  ## Counts from ordinal value `a` down to `b` (inclusive) with the given
+  ## step count.
+  ##
+  ## `T` may be any ordinal type, `step` may only be positive.
+  ##
+  ## **Note**: This fails to count to `low(int)` if T = int for
+  ## efficiency reasons.
+  runnableExamples:
+    import std/sugar
+    let x = collect(newSeq):
+      for i in countdown(7, 3):
+        i
+
+    assert x == @[7, 6, 5, 4, 3]
+
+    let y = collect(newseq):
+      for i in countdown(9, 2, 3):
+        i
+    assert y == @[9, 6, 3]
+  when T is (uint|uint64):
+    var res = a
+    while res >= b:
+      yield res
+      if res == b: break
+      dec(res, step)
+  elif T is IntLikeForCount and T is Ordinal:
+    var res = int(a)
+    while res >= int(b):
+      when defined(nimHasCastExtendedVm):
+        yield cast[T](res)
+      else:
+        yield T(res)
+      dec(res, step)
+  else:
+    var res = a
+    while res >= b:
+      yield res
+      dec(res, step)
+
+iterator countup*[T](a, b: T, step: Positive = 1): T {.inline.} =
+  ## Counts from ordinal value `a` to `b` (inclusive) with the given
+  ## step count.
+  ##
+  ## `T` may be any ordinal type, `step` may only be positive.
+  ##
+  ## **Note**: This fails to count to `high(int)` if T = int for
+  ## efficiency reasons.
+  runnableExamples:
+    import std/sugar
+    let x = collect(newSeq):
+      for i in countup(3, 7):
+        i
+    
+    assert x == @[3, 4, 5, 6, 7]
+
+    let y = collect(newseq):
+      for i in countup(2, 9, 3):
+        i
+    assert y == @[2, 5, 8]
+  mixin inc
+  when T is IntLikeForCount and T is Ordinal:
+    var res = int(a)
+    while res <= int(b):
+      when defined(nimHasCastExtendedVm):
+        yield cast[T](res)
+      else:
+        yield T(res)
+      inc(res, step)
+  else:
+    var res = a
+    while res <= b:
+      yield res
+      inc(res, step)
+
+iterator `..`*[T](a, b: T): T {.inline.} =
+  ## An alias for `countup(a, b, 1)`.
+  ##
+  ## See also:
+  ## * [..<](#..<.i,T,T)
+  runnableExamples:
+    import std/sugar
+
+    let x = collect(newSeq):
+      for i in 3 .. 7:
+        i
+
+    assert x == @[3, 4, 5, 6, 7]
+  mixin inc
+  when T is IntLikeForCount and T is Ordinal:
+    var res = int(a)
+    while res <= int(b):
+      when defined(nimHasCastExtendedVm):
+        yield cast[T](res)
+      else:
+        yield T(res)
+      inc(res)
+  else:
+    var res = a
+    while res <= b:
+      yield res
+      inc(res)
+
+template dotdotImpl(t) {.dirty.} =
+  iterator `..`*(a, b: t): t {.inline.} =
+    ## A type specialized version of `..` for convenience so that
+    ## mixing integer types works better.
+    ##
+    ## See also:
+    ## * [..<](#..<.i,T,T)
+    var res = a
+    while res <= b:
+      yield res
+      inc(res)
+
+dotdotImpl(int64)
+dotdotImpl(int32)
+dotdotImpl(uint64)
+dotdotImpl(uint32)
+
+iterator `..<`*[T](a, b: T): T {.inline.} =
+  mixin inc
+  var i = a
+  while i < b:
+    yield i
+    inc i
+
+template dotdotLessImpl(t) {.dirty.} =
+  iterator `..<`*(a, b: t): t {.inline.} =
+    ## A type specialized version of `..<` for convenience so that
+    ## mixing integer types works better.
+    var res = a
+    while res < b:
+      yield res
+      inc(res)
+
+dotdotLessImpl(int64)
+dotdotLessImpl(int32)
+dotdotLessImpl(uint64)
+dotdotLessImpl(uint32)
+
+iterator `||`*[S, T](a: S, b: T, annotation: static string = "parallel for"): T {.
+  inline, magic: "OmpParFor", sideEffect.} =
+  ## OpenMP parallel loop iterator. Same as `..` but the loop may run in parallel.
+  ##
+  ## `annotation` is an additional annotation for the code generator to use.
+  ## The default annotation is `parallel for`.
+  ## Please refer to the `OpenMP Syntax Reference
+  ## <https://www.openmp.org/wp-content/uploads/OpenMP-4.5-1115-CPP-web.pdf>`_
+  ## for further information.
+  ##
+  ## Note that the compiler maps that to
+  ## the `#pragma omp parallel for` construct of `OpenMP`:idx: and as
+  ## such isn't aware of the parallelism in your code! Be careful! Later
+  ## versions of `||` will get proper support by Nim's code generator
+  ## and GC.
+  discard
+
+iterator `||`*[S, T](a: S, b: T, step: Positive, annotation: static string = "parallel for"): T {.
+  inline, magic: "OmpParFor", sideEffect.} =
+  ## OpenMP parallel loop iterator with stepping.
+  ## Same as `countup` but the loop may run in parallel.
+  ##
+  ## `annotation` is an additional annotation for the code generator to use.
+  ## The default annotation is `parallel for`.
+  ## Please refer to the `OpenMP Syntax Reference
+  ## <https://www.openmp.org/wp-content/uploads/OpenMP-4.5-1115-CPP-web.pdf>`_
+  ## for further information.
+  ##
+  ## Note that the compiler maps that to
+  ## the `#pragma omp parallel for` construct of `OpenMP`:idx: and as
+  ## such isn't aware of the parallelism in your code! Be careful! Later
+  ## versions of `||` will get proper support by Nim's code generator
+  ## and GC.
+  discard
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index e12bab184..5599240fd 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -7,6 +7,9 @@
 #    distribution, for details about the copyright.
 #
 
+include system/indexerrors
+import std/private/miscdollars
+
 proc log*(s: cstring) {.importc: "console.log", varargs, nodecl.}
 
 type
@@ -31,8 +34,6 @@ type
 
   JSRef = ref RootObj # Fake type.
 
-{.deprecated: [TSafePoint: SafePoint, TCallFrame: CallFrame].}
-
 var
   framePtr {.importc, nodecl, volatile.}: PCallFrame
   excHandler {.importc, nodecl, volatile.}: int = 0
@@ -48,7 +49,7 @@ proc nimCharToStr(x: char): string {.compilerproc.} =
   result[0] = x
 
 proc isNimException(): bool {.asmNoStackFrame.} =
-  asm "return `lastJSError`.m_type;"
+  {.emit: "return `lastJSError` && `lastJSError`.m_type;".}
 
 proc getCurrentException*(): ref Exception {.compilerRtl, benign.} =
   if isNimException(): result = cast[ref Exception](lastJSError)
@@ -68,9 +69,16 @@ proc getCurrentExceptionMsg*(): string =
         return $msg
   return ""
 
+proc setCurrentException*(exc: ref Exception) =
+  lastJSError = cast[PJSError](exc)
+
+proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} =
+  ## Used to set up exception handling for closure iterators
+  setCurrentException(e)
+
 proc auxWriteStackTrace(f: PCallFrame): string =
   type
-    TempFrame = tuple[procname: cstring, line: int]
+    TempFrame = tuple[procname: cstring, line: int, filename: cstring]
   var
     it = f
     i = 0
@@ -79,6 +87,7 @@ proc auxWriteStackTrace(f: PCallFrame): string =
   while it != nil and i <= high(tempFrames):
     tempFrames[i].procname = it.procname
     tempFrames[i].line = it.line
+    tempFrames[i].filename = it.filename
     inc(i)
     inc(total)
     it = it.prev
@@ -92,10 +101,9 @@ proc auxWriteStackTrace(f: PCallFrame): string =
     add(result, $(total-i))
     add(result, " calls omitted) ...\n")
   for j in countdown(i-1, 0):
+    result.toLocation($tempFrames[j].filename, tempFrames[j].line, 0)
+    add(result, " at ")
     add(result, tempFrames[j].procname)
-    if tempFrames[j].line > 0:
-      add(result, ", line: ")
-      add(result, $tempFrames[j].line)
     add(result, "\n")
 
 proc rawWriteStackTrace(): string =
@@ -104,13 +112,18 @@ proc rawWriteStackTrace(): string =
   else:
     result = "No stack traceback available\n"
 
+proc writeStackTrace() =
+  var trace = rawWriteStackTrace()
+  trace.setLen(trace.len - 1)
+  echo trace
+
 proc getStackTrace*(): string = rawWriteStackTrace()
 proc getStackTrace*(e: ref Exception): string = e.trace
 
 proc unhandledException(e: ref Exception) {.
     compilerproc, asmNoStackFrame.} =
   var buf = ""
-  if e.msg != nil and e.msg[0] != '\0':
+  if e.msg.len != 0:
     add(buf, "Error: unhandled exception: ")
     add(buf, e.msg)
   else:
@@ -120,8 +133,9 @@ proc unhandledException(e: ref Exception) {.
   add(buf, "]\n")
   when NimStackTrace:
     add(buf, rawWriteStackTrace())
-  let cbuf : cstring = buf
-  framePtr = nil
+  let cbuf = cstring(buf)
+  when NimStackTrace:
+    framePtr = nil
   {.emit: """
   if (typeof(Error) !== "undefined") {
     throw new Error(`cbuf`);
@@ -138,35 +152,35 @@ proc raiseException(e: ref Exception, ename: cstring) {.
     unhandledException(e)
   when NimStackTrace:
     e.trace = rawWriteStackTrace()
-  asm "throw `e`;"
+  {.emit: "throw `e`;".}
 
 proc reraiseException() {.compilerproc, asmNoStackFrame.} =
   if lastJSError == nil:
-    raise newException(ReraiseError, "no exception to reraise")
+    raise newException(ReraiseDefect, "no exception to reraise")
   else:
     if excHandler == 0:
       if isNimException():
         unhandledException(cast[ref Exception](lastJSError))
 
-    asm "throw lastJSError;"
+    {.emit: "throw lastJSError;".}
 
-proc raiseOverflow {.exportc: "raiseOverflow", noreturn, compilerProc.} =
-  raise newException(OverflowError, "over- or underflow")
+proc raiseOverflow {.exportc: "raiseOverflow", noreturn, compilerproc.} =
+  raise newException(OverflowDefect, "over- or underflow")
 
-proc raiseDivByZero {.exportc: "raiseDivByZero", noreturn, compilerProc.} =
-  raise newException(DivByZeroError, "division by zero")
+proc raiseDivByZero {.exportc: "raiseDivByZero", noreturn, compilerproc.} =
+  raise newException(DivByZeroDefect, "division by zero")
 
 proc raiseRangeError() {.compilerproc, noreturn.} =
-  raise newException(RangeError, "value out of range")
+  raise newException(RangeDefect, "value out of range")
 
-proc raiseIndexError() {.compilerproc, noreturn.} =
-  raise newException(IndexError, "index out of bounds")
+proc raiseIndexError(i, a, b: int) {.compilerproc, noreturn.} =
+  raise newException(IndexDefect, formatErrorIndexBound(int(i), int(a), int(b)))
 
-proc raiseFieldError(f: string) {.compilerproc, noreturn.} =
-  raise newException(FieldError, f & " is not accessible")
+proc raiseFieldError2(f: string, discVal: string) {.compilerproc, noreturn.} =
+  raise newException(FieldDefect, formatFieldDefect(f, discVal))
 
 proc setConstr() {.varargs, asmNoStackFrame, compilerproc.} =
-  asm """
+  {.emit: """
     var result = {};
     for (var i = 0; i < arguments.length; ++i) {
       var x = arguments[i];
@@ -179,17 +193,14 @@ proc setConstr() {.varargs, asmNoStackFrame, compilerproc.} =
       }
     }
     return result;
-  """
+  """.}
 
 proc makeNimstrLit(c: cstring): string {.asmNoStackFrame, compilerproc.} =
   {.emit: """
-  var ln = `c`.length;
-  var result = new Array(ln + 1);
-  var i = 0;
-  for (; i < ln; ++i) {
+  var result = [];
+  for (var i = 0; i < `c`.length; ++i) {
     result[i] = `c`.charCodeAt(i);
   }
-  result[i] = 0; // terminating zero
   return result;
   """.}
 
@@ -227,146 +238,159 @@ proc cstrToNimstr(c: cstring): string {.asmNoStackFrame, compilerproc.} =
     }
     ++r;
   }
-  result[r] = 0; // terminating zero
   return result;
   """.}
 
-proc toJSStr(s: string): cstring {.asmNoStackFrame, compilerproc.} =
-  asm """
-  var len = `s`.length-1;
-  var asciiPart = new Array(len);
-  var fcc = String.fromCharCode;
-  var nonAsciiPart = null;
-  var nonAsciiOffset = 0;
-  for (var i = 0; i < len; ++i) {
-    if (nonAsciiPart !== null) {
-      var offset = (i - nonAsciiOffset) * 2;
-      var code = `s`[i].toString(16);
-      if (code.length == 1) {
-        code = "0"+code;
-      }
-      nonAsciiPart[offset] = "%";
-      nonAsciiPart[offset + 1] = code;
-    }
-    else if (`s`[i] < 128)
-      asciiPart[i] = fcc(`s`[i]);
-    else {
-      asciiPart.length = i;
-      nonAsciiOffset = i;
-      nonAsciiPart = new Array((len - i) * 2);
-      --i;
-    }
-  }
-  asciiPart = asciiPart.join("");
-  return (nonAsciiPart === null) ?
-      asciiPart : asciiPart + decodeURIComponent(nonAsciiPart.join(""));
-  """
+proc toJSStr(s: string): cstring {.compilerproc.} =
+  proc fromCharCode(c: char): cstring {.importc: "String.fromCharCode".}
+  proc join(x: openArray[cstring]; d = cstring""): cstring {.
+    importcpp: "#.join(@)".}
+  proc decodeURIComponent(x: cstring): cstring {.
+    importc: "decodeURIComponent".}
+
+  proc toHexString(c: char; d = 16): cstring {.importcpp: "#.toString(@)".}
+
+  proc log(x: cstring) {.importc: "console.log".}
+
+  var res = newSeq[cstring](s.len)
+  var i = 0
+  var j = 0
+  while i < s.len:
+    var c = s[i]
+    if c < '\128':
+      res[j] = fromCharCode(c)
+      inc i
+    else:
+      var helper = newSeq[cstring]()
+      while true:
+        let code = toHexString(c)
+        if code.len == 1:
+          helper.add cstring"%0"
+        else:
+          helper.add cstring"%"
+        helper.add code
+        inc i
+        if i >= s.len or s[i] < '\128': break
+        c = s[i]
+      try:
+        res[j] = decodeURIComponent join(helper)
+      except:
+        res[j] = join(helper)
+    inc j
+  setLen(res, j)
+  result = join(res)
 
 proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} =
-  asm """
-    var result = new Array(`len`+1);
-    result[0] = 0;
-    result[`len`] = 0;
+  {.emit: """
+    var result = new Array(`len`);
+    for (var i = 0; i < `len`; i++) {result[i] = 0;}
     return result;
-  """
+  """.}
 
 proc SetCard(a: int): int {.compilerproc, asmNoStackFrame.} =
   # argument type is a fake
-  asm """
+  {.emit: """
     var result = 0;
     for (var elem in `a`) { ++result; }
     return result;
-  """
+  """.}
 
 proc SetEq(a, b: int): bool {.compilerproc, asmNoStackFrame.} =
-  asm """
+  {.emit: """
     for (var elem in `a`) { if (!`b`[elem]) return false; }
     for (var elem in `b`) { if (!`a`[elem]) return false; }
     return true;
-  """
+  """.}
 
 proc SetLe(a, b: int): bool {.compilerproc, asmNoStackFrame.} =
-  asm """
+  {.emit: """
     for (var elem in `a`) { if (!`b`[elem]) return false; }
     return true;
-  """
+  """.}
 
 proc SetLt(a, b: int): bool {.compilerproc.} =
   result = SetLe(a, b) and not SetEq(a, b)
 
 proc SetMul(a, b: int): int {.compilerproc, asmNoStackFrame.} =
-  asm """
+  {.emit: """
     var result = {};
     for (var elem in `a`) {
       if (`b`[elem]) { result[elem] = true; }
     }
     return result;
-  """
+  """.}
 
 proc SetPlus(a, b: int): int {.compilerproc, asmNoStackFrame.} =
-  asm """
+  {.emit: """
     var result = {};
     for (var elem in `a`) { result[elem] = true; }
     for (var elem in `b`) { result[elem] = true; }
     return result;
-  """
+  """.}
 
 proc SetMinus(a, b: int): int {.compilerproc, asmNoStackFrame.} =
-  asm """
+  {.emit: """
     var result = {};
     for (var elem in `a`) {
       if (!`b`[elem]) { result[elem] = true; }
     }
     return result;
-  """
+  """.}
 
-proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerProc.} =
-  asm """
+proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerproc.} =
+  {.emit: """
     if (`a` == `b`) return 0;
     if (!`a`) return -1;
     if (!`b`) return 1;
-    for (var i = 0; i < `a`.length - 1 && i < `b`.length - 1; i++) {
+    for (var i = 0; i < `a`.length && i < `b`.length; i++) {
       var result = `a`[i] - `b`[i];
       if (result != 0) return result;
     }
     return `a`.length - `b`.length;
-  """
+  """.}
 
 proc cmp(x, y: string): int =
-  return cmpStrings(x, y)
+  when nimvm:
+    if x == y: result = 0
+    elif x < y: result = -1
+    else: result = 1
+  else:
+    result = cmpStrings(x, y)
 
-proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerProc.} =
-  asm """
+proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerproc.} =
+  {.emit: """
     if (`a` == `b`) return true;
+    if (`a` === null && `b`.length == 0) return true;
+    if (`b` === null && `a`.length == 0) return true;
     if ((!`a`) || (!`b`)) return false;
     var alen = `a`.length;
     if (alen != `b`.length) return false;
     for (var i = 0; i < alen; ++i)
       if (`a`[i] != `b`[i]) return false;
     return true;
-  """
+  """.}
 
 when defined(kwin):
   proc rawEcho {.compilerproc, asmNoStackFrame.} =
-    asm """
+    {.emit: """
       var buf = "";
       for (var i = 0; i < arguments.length; ++i) {
         buf += `toJSStr`(arguments[i]);
       }
       print(buf);
-    """
+    """.}
 
 elif not defined(nimOldEcho):
   proc ewriteln(x: cstring) = log(x)
 
   proc rawEcho {.compilerproc, asmNoStackFrame.} =
-    asm """
+    {.emit: """
       var buf = "";
       for (var i = 0; i < arguments.length; ++i) {
         buf += `toJSStr`(arguments[i]);
       }
       console.log(buf);
-    """
+    """.}
 
 else:
   proc ewriteln(x: cstring) =
@@ -393,78 +417,85 @@ else:
     """.}
 
 # Arithmetic:
+proc checkOverflowInt(a: int) {.asmNoStackFrame, compilerproc.} =
+  {.emit: """
+    if (`a` > 2147483647 || `a` < -2147483648) `raiseOverflow`();
+  """.}
+
 proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
+  {.emit: """
     var result = `a` + `b`;
-    if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
+    `checkOverflowInt`(result);
     return result;
-  """
+  """.}
 
 proc subInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
+  {.emit: """
     var result = `a` - `b`;
-    if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
+    `checkOverflowInt`(result);
     return result;
-  """
+  """.}
 
 proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
+  {.emit: """
     var result = `a` * `b`;
-    if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
+    `checkOverflowInt`(result);
     return result;
-  """
+  """.}
 
 proc divInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
+  {.emit: """
     if (`b` == 0) `raiseDivByZero`();
     if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
     return Math.trunc(`a` / `b`);
-  """
+  """.}
 
 proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
+  {.emit: """
     if (`b` == 0) `raiseDivByZero`();
     if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
     return Math.trunc(`a` % `b`);
-  """
+  """.}
 
-proc addInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
+proc checkOverflowInt64(a: int64) {.asmNoStackFrame, compilerproc.} =
+  {.emit: """
+    if (`a` > 9223372036854775807n || `a` < -9223372036854775808n) `raiseOverflow`();
+  """.}
+
+proc addInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} =
+  {.emit: """
     var result = `a` + `b`;
-    if (result > 9223372036854775807
-    || result < -9223372036854775808) `raiseOverflow`();
+    `checkOverflowInt64`(result);
     return result;
-  """
+  """.}
 
-proc subInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
+proc subInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} =
+  {.emit: """
     var result = `a` - `b`;
-    if (result > 9223372036854775807
-    || result < -9223372036854775808) `raiseOverflow`();
+    `checkOverflowInt64`(result);
     return result;
-  """
+  """.}
 
-proc mulInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
+proc mulInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} =
+  {.emit: """
     var result = `a` * `b`;
-    if (result > 9223372036854775807
-    || result < -9223372036854775808) `raiseOverflow`();
+    `checkOverflowInt64`(result);
     return result;
-  """
+  """.}
 
-proc divInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
-    if (`b` == 0) `raiseDivByZero`();
-    if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
-    return Math.trunc(`a` / `b`);
-  """
+proc divInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} =
+  {.emit: """
+    if (`b` == 0n) `raiseDivByZero`();
+    if (`b` == -1n && `a` == 9223372036854775807n) `raiseOverflow`();
+    return `a` / `b`;
+  """.}
 
-proc modInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
-    if (`b` == 0) `raiseDivByZero`();
-    if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
-    return Math.trunc(`a` % `b`);
-  """
+proc modInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} =
+  {.emit: """
+    if (`b` == 0n) `raiseDivByZero`();
+    if (`b` == -1n && `a` == 9223372036854775807n) `raiseOverflow`();
+    return `a` % `b`;
+  """.}
 
 proc negInt(a: int): int {.compilerproc.} =
   result = a*(-1)
@@ -478,35 +509,13 @@ proc absInt(a: int): int {.compilerproc.} =
 proc absInt64(a: int64): int64 {.compilerproc.} =
   result = if a < 0: a*(-1) else: a
 
-proc ze*(a: int): int {.compilerproc.} =
-  result = a
-
-proc ze64*(a: int64): int64 {.compilerproc.} =
-  result = a
-
-proc toU8*(a: int): int8 {.asmNoStackFrame, compilerproc.} =
-  asm """
-    return `a`;
-  """
-
-proc toU16*(a: int): int16 {.asmNoStackFrame, compilerproc.} =
-  asm """
-    return `a`;
-  """
-
-proc toU32*(a: int64): int32 {.asmNoStackFrame, compilerproc.} =
-  asm """
-    return `a`;
-  """
-
 proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b
 proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b
 
-proc chckNilDisp(p: pointer) {.compilerproc.} =
+proc chckNilDisp(p: JSRef) {.compilerproc.} =
   if p == nil:
-    sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil")
+    sysFatal(NilAccessDefect, "cannot dispatch; dispatcher is nil")
 
-type NimString = string # hack for hti.nim
 include "system/hti"
 
 proc isFatPointer(ti: PNimType): bool =
@@ -521,19 +530,22 @@ proc nimCopyAux(dest, src: JSRef, n: ptr TNimNode) {.compilerproc.} =
   case n.kind
   of nkNone: sysAssert(false, "nimCopyAux")
   of nkSlot:
-    asm """
+    {.emit: """
       `dest`[`n`.offset] = nimCopy(`dest`[`n`.offset], `src`[`n`.offset], `n`.typ);
-    """
+    """.}
   of nkList:
-    for i in 0..n.len-1:
-      nimCopyAux(dest, src, n.sons[i])
+    {.emit: """
+    for (var i = 0; i < `n`.sons.length; i++) {
+      nimCopyAux(`dest`, `src`, `n`.sons[i]);
+    }
+    """.}
   of nkCase:
-    asm """
+    {.emit: """
       `dest`[`n`.offset] = nimCopy(`dest`[`n`.offset], `src`[`n`.offset], `n`.typ);
       for (var i = 0; i < `n`.sons.length; ++i) {
         nimCopyAux(`dest`, `src`, `n`.sons[i][1]);
       }
-    """
+    """.}
 
 proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef =
   case ti.kind
@@ -541,9 +553,9 @@ proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef =
     if not isFatPointer(ti):
       result = src
     else:
-      asm "`result` = [`src`[0], `src`[1]];"
+      {.emit: "`result` = [`src`[0], `src`[1]];".}
   of tySet:
-    asm """
+    {.emit: """
       if (`dest` === null || `dest` === undefined) {
         `dest` = {};
       }
@@ -552,84 +564,76 @@ proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef =
       }
       for (var key in `src`) { `dest`[key] = `src`[key]; }
       `result` = `dest`;
-    """
+    """.}
   of tyTuple, tyObject:
     if ti.base != nil: result = nimCopy(dest, src, ti.base)
     elif ti.kind == tyObject:
-      asm "`result` = (`dest` === null || `dest` === undefined) ? {m_type: `ti`} : `dest`;"
+      {.emit: "`result` = (`dest` === null || `dest` === undefined) ? {m_type: `ti`} : `dest`;".}
     else:
-      asm "`result` = (`dest` === null || `dest` === undefined) ? {} : `dest`;"
+      {.emit: "`result` = (`dest` === null || `dest` === undefined) ? {} : `dest`;".}
     nimCopyAux(result, src, ti.node)
-  of tySequence, tyArrayConstr, tyOpenArray, tyArray:
-    asm """
+  of tyArrayConstr, tyArray:
+    # In order to prevent a type change (TypedArray -> Array) and to have better copying performance,
+    # arrays constructors are considered separately
+    {.emit: """
+      if(ArrayBuffer.isView(`src`)) { 
+        if(`dest` === null || `dest` === undefined || `dest`.length != `src`.length) {
+          `dest` = new `src`.constructor(`src`);
+        } else {
+          `dest`.set(`src`, 0);
+        }
+        `result` = `dest`;
+      } else {
+        if (`src` === null) {
+          `result` = null;
+        }
+        else {
+          if (`dest` === null || `dest` === undefined || `dest`.length != `src`.length) {
+            `dest` = new Array(`src`.length);
+          }
+          `result` = `dest`;
+          for (var i = 0; i < `src`.length; ++i) {
+            `result`[i] = nimCopy(`result`[i], `src`[i], `ti`.base);
+          }
+        }
+      }
+    """.}
+  of tySequence, tyOpenArray:
+    {.emit: """
       if (`src` === null) {
         `result` = null;
       }
       else {
-        if (`dest` === null || `dest` === undefined) {
+        if (`dest` === null || `dest` === undefined || `dest`.length != `src`.length) {
           `dest` = new Array(`src`.length);
         }
-        else {
-          `dest`.length = `src`.length;
-        }
         `result` = `dest`;
         for (var i = 0; i < `src`.length; ++i) {
           `result`[i] = nimCopy(`result`[i], `src`[i], `ti`.base);
         }
       }
-    """
+    """.}
   of tyString:
-    asm """
+    {.emit: """
       if (`src` !== null) {
         `result` = `src`.slice(0);
       }
-    """
+    """.}
   else:
     result = src
 
-proc genericReset(x: JSRef, ti: PNimType): JSRef {.compilerproc.} =
-  asm "`result` = null;"
-  case ti.kind
-  of tyPtr, tyRef, tyVar, tyNil:
-    if isFatPointer(ti):
-      asm """
-        `result` = [null, 0];
-      """
-  of tySet:
-    asm """
-      `result` = {};
-    """
-  of tyTuple, tyObject:
-    if ti.kind == tyObject:
-      asm "`result` = {m_type: `ti`};"
-    else:
-      asm "`result` = {};"
-  of tySequence, tyOpenArray:
-    asm """
-      `result` = [];
-    """
-  of tyArrayConstr, tyArray:
-    asm """
-      `result` = new Array(`x`.length);
-      for (var i = 0; i < `x`.length; ++i) {
-        `result`[i] = genericReset(`x`[i], `ti`.base);
-      }
-    """
-  else:
-    discard
-
 proc arrayConstr(len: int, value: JSRef, typ: PNimType): JSRef {.
                 asmNoStackFrame, compilerproc.} =
   # types are fake
-  asm """
+  {.emit: """
     var result = new Array(`len`);
     for (var i = 0; i < `len`; ++i) result[i] = nimCopy(null, `value`, `typ`);
     return result;
-  """
+  """.}
 
 proc chckIndx(i, a, b: int): int {.compilerproc.} =
   if i >= a and i <= b: return i
-  else: raiseIndexError()
+  else: raiseIndexError(i, a, b)
 
 proc chckRange(i, a, b: int): int {.compilerproc.} =
   if i >= a and i <= b: return i
@@ -641,7 +645,7 @@ proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
   if x == subclass: return # optimized fast path
   while x != subclass:
     if x == nil:
-      raise newException(ObjectConversionError, "invalid object conversion")
+      raise newException(ObjectConversionDefect, "invalid object conversion")
     x = x.base
 
 proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
@@ -654,13 +658,12 @@ proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
   return true
 
 proc addChar(x: string, c: char) {.compilerproc, asmNoStackFrame.} =
-  asm """
-    `x`[`x`.length-1] = `c`; `x`.push(0);
-  """
+  {.emit: "`x`.push(`c`);".}
 
 {.pop.}
 
 proc tenToThePowerOf(b: int): BiggestFloat =
+  # xxx deadcode
   var b = b
   var a = 10.0
   result = 1.0
@@ -674,92 +677,92 @@ proc tenToThePowerOf(b: int): BiggestFloat =
 const
   IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
 
-# XXX use JS's native way here
-proc nimParseBiggestFloat(s: string, number: var BiggestFloat, start = 0): int {.
-                          compilerProc.} =
-  var
-    esign = 1.0
-    sign = 1.0
-    i = start
-    exponent: int
-    flags: int
-  number = 0.0
+
+proc parseFloatNative(a: openarray[char]): float =
+  var str = ""
+  for x in a:
+    str.add x
+
+  let cstr = cstring str
+
+  {.emit: """
+  `result` = Number(`cstr`);
+  """.}
+
+proc nimParseBiggestFloat(s: openarray[char], number: var BiggestFloat): int {.compilerproc.} =
+  var sign: bool
+  var i = 0
   if s[i] == '+': inc(i)
   elif s[i] == '-':
-    sign = -1.0
+    sign = true
     inc(i)
   if s[i] == 'N' or s[i] == 'n':
     if s[i+1] == 'A' or s[i+1] == 'a':
       if s[i+2] == 'N' or s[i+2] == 'n':
         if s[i+3] notin IdentChars:
           number = NaN
-          return i+3 - start
+          return i+3
     return 0
   if s[i] == 'I' or s[i] == 'i':
     if s[i+1] == 'N' or s[i+1] == 'n':
       if s[i+2] == 'F' or s[i+2] == 'f':
         if s[i+3] notin IdentChars:
-          number = Inf*sign
-          return i+3 - start
+          number = if sign: -Inf else: Inf
+          return i+3
     return 0
-  while s[i] in {'0'..'9'}:
-    # Read integer part
-    flags = flags or 1
-    number = number * 10.0 + toFloat(ord(s[i]) - ord('0'))
+
+  var buf: string
+    # we could also use an `array[char, N]` buffer to avoid reallocs, or
+    # use a 2-pass algorithm that first computes the length.
+  if sign: buf.add '-'
+  template addInc =
+    buf.add s[i]
     inc(i)
+  template eatUnderscores =
     while s[i] == '_': inc(i)
-  # Decimal?
-  if s[i] == '.':
-    var hd = 1.0
+  while s[i] in {'0'..'9'}: # Read integer part
+    buf.add s[i]
     inc(i)
-    while s[i] in {'0'..'9'}:
-      # Read fractional part
-      flags = flags or 2
-      number = number * 10.0 + toFloat(ord(s[i]) - ord('0'))
-      hd = hd * 10.0
-      inc(i)
-      while s[i] == '_': inc(i)
-    number = number / hd # this complicated way preserves precision
+    eatUnderscores()
+  if s[i] == '.': # Decimal?
+    addInc()
+    while s[i] in {'0'..'9'}: # Read fractional part
+      addInc()
+      eatUnderscores()
   # Again, read integer and fractional part
-  if flags == 0: return 0
-  # Exponent?
-  if s[i] in {'e', 'E'}:
-    inc(i)
-    if s[i] == '+':
-      inc(i)
-    elif s[i] == '-':
-      esign = -1.0
-      inc(i)
-    if s[i] notin {'0'..'9'}:
-      return 0
+  if buf.len == ord(sign): return 0
+  if s[i] in {'e', 'E'}: # Exponent?
+    addInc()
+    if s[i] == '+': inc(i)
+    elif s[i] == '-': addInc()
+    if s[i] notin {'0'..'9'}: return 0
     while s[i] in {'0'..'9'}:
-      exponent = exponent * 10 + ord(s[i]) - ord('0')
-      inc(i)
-      while s[i] == '_': inc(i)
-  # Calculate Exponent
-  let hd = tenToThePowerOf(exponent)
-  if esign > 0.0: number = number * hd
-  else:           number = number / hd
-  # evaluate sign
-  number = number * sign
-  result = i - start
-
-when defined(nodejs):
-  # Deprecated. Use `alert` defined in dom.nim
-  proc alert*(s: cstring) {.importc: "console.log", nodecl, deprecated.}
-else:
-  # Deprecated. Use `alert` defined in dom.nim
-  proc alert*(s: cstring) {.importc, nodecl, deprecated.}
+      addInc()
+      eatUnderscores()
+  number = parseFloatNative(buf)
+  result = i
 
 # Workaround for IE, IE up to version 11 lacks 'Math.trunc'. We produce
 # 'Math.trunc' for Nim's ``div`` and ``mod`` operators:
-when not defined(nodejs):
+when defined(nimJsMathTruncPolyfill):
+  {.emit: """
+if (!Math.trunc) {
+  Math.trunc = function(v) {
+    v = +v;
+    if (!isFinite(v)) return v;
+    return (v - v % 1) || (v < 0 ? -0 : v === 0 ? v : 0);
+  };
+}
+""".}
+
+proc cmpClosures(a, b: JSRef): bool {.compilerproc, asmNoStackFrame.} =
+  # Both `a` and `b` need to be a closure
   {.emit: """
-  if (!Math.trunc) {
-    Math.trunc = function(v) {
-      v = +v;
-      if (!isFinite(v)) return v;
-
-      return (v - v % 1)   ||   (v < 0 ? -0 : v === 0 ? v : 0);
-    };
-  }""".}
+    if (`a` !== null && `a`.ClP_0 !== undefined &&
+        `b` !== null && `b`.ClP_0 !== undefined) {
+      return `a`.ClP_0 == `b`.ClP_0 && `a`.ClE_0 == `b`.ClE_0;
+    } else {
+      return `a` == `b`;
+    }
+  """
+  .}
diff --git a/lib/system/memalloc.nim b/lib/system/memalloc.nim
new file mode 100644
index 000000000..a94d0995c
--- /dev/null
+++ b/lib/system/memalloc.nim
@@ -0,0 +1,449 @@
+when notJSnotNims:
+  proc zeroMem*(p: pointer, size: Natural) {.inline, noSideEffect,
+    tags: [], raises: [].}
+    ## Overwrites the contents of the memory at `p` with the value 0.
+    ##
+    ## Exactly `size` bytes will be overwritten. Like any procedure
+    ## dealing with raw memory this is **unsafe**.
+
+  proc copyMem*(dest, source: pointer, size: Natural) {.inline, benign,
+    tags: [], raises: [].}
+    ## Copies the contents from the memory at `source` to the memory
+    ## at `dest`.
+    ## Exactly `size` bytes will be copied. The memory
+    ## regions may not overlap. Like any procedure dealing with raw
+    ## memory this is **unsafe**.
+
+  proc moveMem*(dest, source: pointer, size: Natural) {.inline, benign,
+    tags: [], raises: [].}
+    ## Copies the contents from the memory at `source` to the memory
+    ## at `dest`.
+    ##
+    ## Exactly `size` bytes will be copied. The memory
+    ## regions may overlap, `moveMem` handles this case appropriately
+    ## and is thus somewhat more safe than `copyMem`. Like any procedure
+    ## dealing with raw memory this is still **unsafe**, though.
+
+  proc equalMem*(a, b: pointer, size: Natural): bool {.inline, noSideEffect,
+    tags: [], raises: [].}
+    ## Compares the memory blocks `a` and `b`. `size` bytes will
+    ## be compared.
+    ##
+    ## If the blocks are equal, `true` is returned, `false`
+    ## otherwise. Like any procedure dealing with raw memory this is
+    ## **unsafe**.
+
+  proc cmpMem*(a, b: pointer, size: Natural): int {.inline, noSideEffect,
+    tags: [], raises: [].}
+    ## Compares the memory blocks `a` and `b`. `size` bytes will
+    ## be compared.
+    ##
+    ## Returns:
+    ## * a value less than zero, if `a < b`
+    ## * a value greater than zero, if `a > b`
+    ## * zero, if `a == b`
+    ##
+    ## Like any procedure dealing with raw memory this is
+    ## **unsafe**.
+
+when hasAlloc and not defined(js):
+
+  proc allocImpl*(size: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].}
+  proc alloc0Impl*(size: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].}
+  proc deallocImpl*(p: pointer) {.noconv, rtl, tags: [], benign, raises: [].}
+  proc reallocImpl*(p: pointer, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].}
+  proc realloc0Impl*(p: pointer, oldSize, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].}
+
+  proc allocSharedImpl*(size: Natural): pointer {.noconv, compilerproc, rtl, benign, raises: [], tags: [].}
+  proc allocShared0Impl*(size: Natural): pointer {.noconv, rtl, benign, raises: [], tags: [].}
+  proc deallocSharedImpl*(p: pointer) {.noconv, rtl, benign, raises: [], tags: [].}
+  proc reallocSharedImpl*(p: pointer, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].}
+  proc reallocShared0Impl*(p: pointer, oldSize, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].}
+
+  # Allocator statistics for memory leak tests
+
+  {.push stackTrace: off.}
+
+  type AllocStats* = object
+    allocCount: int
+    deallocCount: int
+
+  proc `-`*(a, b: AllocStats): AllocStats =
+    result.allocCount = a.allocCount - b.allocCount
+    result.deallocCount = a.deallocCount - b.deallocCount
+
+  template dumpAllocstats*(code: untyped) =
+    let stats1 = getAllocStats()
+    code
+    let stats2 = getAllocStats()
+    echo $(stats2 - stats1)
+
+  when defined(nimAllocStats):
+    var stats: AllocStats
+    template incStat(what: untyped) = atomicInc stats.what
+    proc getAllocStats*(): AllocStats = stats
+
+  else:
+    template incStat(what: untyped) = discard
+    proc getAllocStats*(): AllocStats = discard
+
+  template alloc*(size: Natural): pointer =
+    ## Allocates a new memory block with at least `size` bytes.
+    ##
+    ## The block has to be freed with `realloc(block, 0) <#realloc.t,pointer,Natural>`_
+    ## or `dealloc(block) <#dealloc,pointer>`_.
+    ## The block is not initialized, so reading
+    ## from it before writing to it is undefined behaviour!
+    ##
+    ## The allocated memory belongs to its allocating thread!
+    ## Use `allocShared <#allocShared.t,Natural>`_ to allocate from a shared heap.
+    ##
+    ## See also:
+    ## * `alloc0 <#alloc0.t,Natural>`_
+    incStat(allocCount)
+    allocImpl(size)
+
+  proc createU*(T: typedesc, size = 1.Positive): ptr T {.inline, benign, raises: [].} =
+    ## Allocates a new memory block with at least `T.sizeof * size` bytes.
+    ##
+    ## The block has to be freed with `resize(block, 0) <#resize,ptr.T,Natural>`_
+    ## or `dealloc(block) <#dealloc,pointer>`_.
+    ## The block is not initialized, so reading
+    ## from it before writing to it is undefined behaviour!
+    ##
+    ## The allocated memory belongs to its allocating thread!
+    ## Use `createSharedU <#createSharedU,typedesc>`_ to allocate from a shared heap.
+    ##
+    ## See also:
+    ## * `create <#create,typedesc>`_
+    cast[ptr T](alloc(T.sizeof * size))
+
+  template alloc0*(size: Natural): pointer =
+    ## Allocates a new memory block with at least `size` bytes.
+    ##
+    ## The block has to be freed with `realloc(block, 0) <#realloc.t,pointer,Natural>`_
+    ## or `dealloc(block) <#dealloc,pointer>`_.
+    ## The block is initialized with all bytes containing zero, so it is
+    ## somewhat safer than  `alloc <#alloc.t,Natural>`_.
+    ##
+    ## The allocated memory belongs to its allocating thread!
+    ## Use `allocShared0 <#allocShared0.t,Natural>`_ to allocate from a shared heap.
+    incStat(allocCount)
+    alloc0Impl(size)
+
+  proc create*(T: typedesc, size = 1.Positive): ptr T {.inline, benign, raises: [].} =
+    ## Allocates a new memory block with at least `T.sizeof * size` bytes.
+    ##
+    ## The block has to be freed with `resize(block, 0) <#resize,ptr.T,Natural>`_
+    ## or `dealloc(block) <#dealloc,pointer>`_.
+    ## The block is initialized with all bytes containing zero, so it is
+    ## somewhat safer than `createU <#createU,typedesc>`_.
+    ##
+    ## The allocated memory belongs to its allocating thread!
+    ## Use `createShared <#createShared,typedesc>`_ to allocate from a shared heap.
+    cast[ptr T](alloc0(sizeof(T) * size))
+
+  template realloc*(p: pointer, newSize: Natural): pointer =
+    ## Grows or shrinks a given memory block.
+    ##
+    ## If `p` is **nil** then a new memory block is returned.
+    ## In either way the block has at least `newSize` bytes.
+    ## If `newSize == 0` and `p` is not **nil** `realloc` calls `dealloc(p)`.
+    ## In other cases the block has to be freed with
+    ## `dealloc(block) <#dealloc,pointer>`_.
+    ##
+    ## The allocated memory belongs to its allocating thread!
+    ## Use `reallocShared <#reallocShared.t,pointer,Natural>`_ to reallocate
+    ## from a shared heap.
+    reallocImpl(p, newSize)
+
+  template realloc0*(p: pointer, oldSize, newSize: Natural): pointer =
+    ## Grows or shrinks a given memory block.
+    ##
+    ## If `p` is **nil** then a new memory block is returned.
+    ## In either way the block has at least `newSize` bytes.
+    ## If `newSize == 0` and `p` is not **nil** `realloc` calls `dealloc(p)`.
+    ## In other cases the block has to be freed with
+    ## `dealloc(block) <#dealloc,pointer>`_.
+    ##
+    ## The block is initialized with all bytes containing zero, so it is
+    ## somewhat safer then realloc
+    ##
+    ## The allocated memory belongs to its allocating thread!
+    ## Use `reallocShared <#reallocShared.t,pointer,Natural>`_ to reallocate
+    ## from a shared heap.
+    realloc0Impl(p, oldSize, newSize)
+
+  proc resize*[T](p: ptr T, newSize: Natural): ptr T {.inline, benign, raises: [].} =
+    ## Grows or shrinks a given memory block.
+    ##
+    ## If `p` is **nil** then a new memory block is returned.
+    ## In either way the block has at least `T.sizeof * newSize` bytes.
+    ## If `newSize == 0` and `p` is not **nil** `resize` calls `dealloc(p)`.
+    ## In other cases the block has to be freed with `free`.
+    ##
+    ## The allocated memory belongs to its allocating thread!
+    ## Use `resizeShared <#resizeShared,ptr.T,Natural>`_ to reallocate
+    ## from a shared heap.
+    cast[ptr T](realloc(p, T.sizeof * newSize))
+
+  proc dealloc*(p: pointer) {.noconv, compilerproc, rtl, benign, raises: [], tags: [].} =
+    ## Frees the memory allocated with `alloc`, `alloc0`,
+    ## `realloc`, `create` or `createU`.
+    ##
+    ## **This procedure is dangerous!**
+    ## If one forgets to free the memory a leak occurs; if one tries to
+    ## access freed memory (or just freeing it twice!) a core dump may happen
+    ## or other memory may be corrupted.
+    ##
+    ## The freed memory must belong to its allocating thread!
+    ## Use `deallocShared <#deallocShared,pointer>`_ to deallocate from a shared heap.
+    incStat(deallocCount)
+    deallocImpl(p)
+
+  template allocShared*(size: Natural): pointer =
+    ## Allocates a new memory block on the shared heap with at
+    ## least `size` bytes.
+    ##
+    ## The block has to be freed with
+    ## `reallocShared(block, 0) <#reallocShared.t,pointer,Natural>`_
+    ## or `deallocShared(block) <#deallocShared,pointer>`_.
+    ##
+    ## The block is not initialized, so reading from it before writing
+    ## to it is undefined behaviour!
+    ##
+    ## See also:
+    ## * `allocShared0 <#allocShared0.t,Natural>`_.
+    incStat(allocCount)
+    allocSharedImpl(size)
+
+  proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline, tags: [],
+                                                               benign, raises: [].} =
+    ## Allocates a new memory block on the shared heap with at
+    ## least `T.sizeof * size` bytes.
+    ##
+    ## The block has to be freed with
+    ## `resizeShared(block, 0) <#resizeShared,ptr.T,Natural>`_ or
+    ## `freeShared(block) <#freeShared,ptr.T>`_.
+    ##
+    ## The block is not initialized, so reading from it before writing
+    ## to it is undefined behaviour!
+    ##
+    ## See also:
+    ## * `createShared <#createShared,typedesc>`_
+    cast[ptr T](allocShared(T.sizeof * size))
+
+  template allocShared0*(size: Natural): pointer =
+    ## Allocates a new memory block on the shared heap with at
+    ## least `size` bytes.
+    ##
+    ## The block has to be freed with
+    ## `reallocShared(block, 0) <#reallocShared.t,pointer,Natural>`_
+    ## or `deallocShared(block) <#deallocShared,pointer>`_.
+    ##
+    ## The block is initialized with all bytes
+    ## containing zero, so it is somewhat safer than
+    ## `allocShared <#allocShared.t,Natural>`_.
+    incStat(allocCount)
+    allocShared0Impl(size)
+
+  proc createShared*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
+    ## Allocates a new memory block on the shared heap with at
+    ## least `T.sizeof * size` bytes.
+    ##
+    ## The block has to be freed with
+    ## `resizeShared(block, 0) <#resizeShared,ptr.T,Natural>`_ or
+    ## `freeShared(block) <#freeShared,ptr.T>`_.
+    ##
+    ## The block is initialized with all bytes
+    ## containing zero, so it is somewhat safer than
+    ## `createSharedU <#createSharedU,typedesc>`_.
+    cast[ptr T](allocShared0(T.sizeof * size))
+
+  template reallocShared*(p: pointer, newSize: Natural): pointer =
+    ## Grows or shrinks a given memory block on the heap.
+    ##
+    ## If `p` is **nil** then a new memory block is returned.
+    ## In either way the block has at least `newSize` bytes.
+    ## If `newSize == 0` and `p` is not **nil** `reallocShared` calls
+    ## `deallocShared(p)`.
+    ## In other cases the block has to be freed with
+    ## `deallocShared <#deallocShared,pointer>`_.
+    reallocSharedImpl(p, newSize)
+
+  template reallocShared0*(p: pointer, oldSize, newSize: Natural): pointer =
+    ## Grows or shrinks a given memory block on the heap.
+    ##
+    ## When growing, the new bytes of the block is initialized with all bytes
+    ## containing zero, so it is somewhat safer then reallocShared
+    ##
+    ## If `p` is **nil** then a new memory block is returned.
+    ## In either way the block has at least `newSize` bytes.
+    ## If `newSize == 0` and `p` is not **nil** `reallocShared` calls
+    ## `deallocShared(p)`.
+    ## In other cases the block has to be freed with
+    ## `deallocShared <#deallocShared,pointer>`_.
+    reallocShared0Impl(p, oldSize, newSize)
+
+  proc resizeShared*[T](p: ptr T, newSize: Natural): ptr T {.inline, raises: [].} =
+    ## Grows or shrinks a given memory block on the heap.
+    ##
+    ## If `p` is **nil** then a new memory block is returned.
+    ## In either way the block has at least `T.sizeof * newSize` bytes.
+    ## If `newSize == 0` and `p` is not **nil** `resizeShared` calls
+    ## `freeShared(p)`.
+    ## In other cases the block has to be freed with
+    ## `freeShared <#freeShared,ptr.T>`_.
+    cast[ptr T](reallocShared(p, T.sizeof * newSize))
+
+  proc deallocShared*(p: pointer) {.noconv, compilerproc, rtl, benign, raises: [], tags: [].} =
+    ## Frees the memory allocated with `allocShared`, `allocShared0` or
+    ## `reallocShared`.
+    ##
+    ## **This procedure is dangerous!**
+    ## If one forgets to free the memory a leak occurs; if one tries to
+    ## access freed memory (or just freeing it twice!) a core dump may happen
+    ## or other memory may be corrupted.
+    incStat(deallocCount)
+    deallocSharedImpl(p)
+
+  proc freeShared*[T](p: ptr T) {.inline, benign, raises: [].} =
+    ## Frees the memory allocated with `createShared`, `createSharedU` or
+    ## `resizeShared`.
+    ##
+    ## **This procedure is dangerous!**
+    ## If one forgets to free the memory a leak occurs; if one tries to
+    ## access freed memory (or just freeing it twice!) a core dump may happen
+    ## or other memory may be corrupted.
+    deallocShared(p)
+
+  include bitmasks
+
+  template `+!`(p: pointer, s: SomeInteger): pointer =
+    cast[pointer](cast[int](p) +% int(s))
+
+  template `-!`(p: pointer, s: SomeInteger): pointer =
+    cast[pointer](cast[int](p) -% int(s))
+
+  proc alignedAlloc(size, align: Natural): pointer =
+    if align <= MemAlign:
+      when compileOption("threads"):
+        result = allocShared(size)
+      else:
+        result = alloc(size)
+    else:
+      # allocate (size + align - 1) necessary for alignment,
+      # plus 2 bytes to store offset
+      when compileOption("threads"):
+        let base = allocShared(size + align - 1 + sizeof(uint16))
+      else:
+        let base = alloc(size + align - 1 + sizeof(uint16))
+      # memory layout: padding + offset (2 bytes) + user_data
+      # in order to deallocate: read offset at user_data - 2 bytes,
+      # then deallocate user_data - offset
+      let offset = align - (cast[int](base) and (align - 1))
+      cast[ptr uint16](base +! (offset - sizeof(uint16)))[] = uint16(offset)
+      result = base +! offset
+
+  proc alignedAlloc0(size, align: Natural): pointer =
+    if align <= MemAlign:
+      when compileOption("threads"):
+        result = allocShared0(size)
+      else:
+        result = alloc0(size)
+    else:
+      # see comments for alignedAlloc
+      when compileOption("threads"):
+        let base = allocShared0(size + align - 1 + sizeof(uint16))
+      else:
+        let base = alloc0(size + align - 1 + sizeof(uint16))
+      let offset = align - (cast[int](base) and (align - 1))
+      cast[ptr uint16](base +! (offset - sizeof(uint16)))[] = uint16(offset)
+      result = base +! offset
+
+  proc alignedDealloc(p: pointer, align: int) {.compilerproc.} =
+    if align <= MemAlign:
+      when compileOption("threads"):
+        deallocShared(p)
+      else:
+        dealloc(p)
+    else:
+      # read offset at p - 2 bytes, then deallocate (p - offset) pointer
+      let offset = cast[ptr uint16](p -! sizeof(uint16))[]
+      when compileOption("threads"):
+        deallocShared(p -! offset)
+      else:
+        dealloc(p -! offset)
+
+  proc alignedRealloc(p: pointer, oldSize, newSize, align: Natural): pointer =
+    if align <= MemAlign:
+      when compileOption("threads"):
+        result = reallocShared(p, newSize)
+      else:
+        result = realloc(p, newSize)
+    else:
+      result = alignedAlloc(newSize, align)
+      copyMem(result, p, oldSize)
+      alignedDealloc(p, align)
+
+  proc alignedRealloc0(p: pointer, oldSize, newSize, align: Natural): pointer =
+    if align <= MemAlign:
+      when compileOption("threads"):
+        result = reallocShared0(p, oldSize, newSize)
+      else:
+        result = realloc0(p, oldSize, newSize)
+    else:
+      result = alignedAlloc(newSize, align)
+      copyMem(result, p, oldSize)
+      zeroMem(result +! oldSize, newSize - oldSize)
+      alignedDealloc(p, align)
+
+  {.pop.}
+
+# GC interface:
+
+when hasAlloc:
+  proc getOccupiedMem*(): int {.rtl.}
+    ## Returns the number of bytes that are owned by the process and hold data.
+
+  proc getFreeMem*(): int {.rtl.}
+    ## Returns the number of bytes that are owned by the process, but do not
+    ## hold any meaningful data.
+
+  proc getTotalMem*(): int {.rtl.}
+    ## Returns the number of bytes that are owned by the process.
+
+
+when defined(js):
+  # Stubs:
+  proc getOccupiedMem(): int = return -1
+  proc getFreeMem(): int = return -1
+  proc getTotalMem(): int = return -1
+
+  proc dealloc(p: pointer) = discard
+  proc alloc(size: Natural): pointer = discard
+  proc alloc0(size: Natural): pointer = discard
+  proc realloc(p: pointer, newsize: Natural): pointer = discard
+  proc realloc0(p: pointer, oldsize, newsize: Natural): pointer = discard
+
+  proc allocShared(size: Natural): pointer = discard
+  proc allocShared0(size: Natural): pointer = discard
+  proc deallocShared(p: pointer) = discard
+  proc reallocShared(p: pointer, newsize: Natural): pointer = discard
+  proc reallocShared0(p: pointer, oldsize, newsize: Natural): pointer = discard
+
+
+when hasAlloc and hasThreadSupport and not defined(useMalloc):
+  proc getOccupiedSharedMem*(): int {.rtl.}
+    ## Returns the number of bytes that are owned by the process
+    ## on the shared heap and hold data. This is only available when
+    ## threads are enabled.
+
+  proc getFreeSharedMem*(): int {.rtl.}
+    ## Returns the number of bytes that are owned by the
+    ## process on the shared heap, but do not hold any meaningful data.
+    ## This is only available when threads are enabled.
+
+  proc getTotalSharedMem*(): int {.rtl.}
+    ## Returns the number of bytes on the shared heap that are owned by the
+    ## process. This is only available when threads are enabled.
diff --git a/lib/system/memory.nim b/lib/system/memory.nim
new file mode 100644
index 000000000..156773c48
--- /dev/null
+++ b/lib/system/memory.nim
@@ -0,0 +1,55 @@
+{.push stack_trace: off.}
+
+const useLibC = not defined(nimNoLibc)
+
+when useLibC:
+  import ansi_c
+
+proc nimCopyMem*(dest, source: pointer, size: Natural) {.nonReloadable, compilerproc, inline.} =
+  when useLibC:
+    c_memcpy(dest, source, cast[csize_t](size))
+  else:
+    let d = cast[ptr UncheckedArray[byte]](dest)
+    let s = cast[ptr UncheckedArray[byte]](source)
+    var i = 0
+    while i < size:
+      d[i] = s[i]
+      inc i
+
+proc nimSetMem*(a: pointer, v: cint, size: Natural) {.nonReloadable, inline.} =
+  when useLibC:
+    c_memset(a, v, cast[csize_t](size))
+  else:
+    let a = cast[ptr UncheckedArray[byte]](a)
+    var i = 0
+    let v = cast[byte](v)
+    while i < size:
+      a[i] = v
+      inc i
+
+proc nimZeroMem*(p: pointer, size: Natural) {.compilerproc, nonReloadable, inline.} =
+  nimSetMem(p, 0, size)
+
+proc nimCmpMem*(a, b: pointer, size: Natural): cint {.compilerproc, nonReloadable, inline.} =
+  when useLibC:
+    c_memcmp(a, b, cast[csize_t](size))
+  else:
+    let a = cast[ptr UncheckedArray[byte]](a)
+    let b = cast[ptr UncheckedArray[byte]](b)
+    var i = 0
+    while i < size:
+      let d = a[i].cint - b[i].cint
+      if d != 0: return d
+      inc i
+
+proc nimCStrLen*(a: cstring): int {.compilerproc, nonReloadable, inline.} =
+  if a.isNil: return 0
+  when useLibC:
+    cast[int](c_strlen(a))
+  else:
+    var a = cast[ptr byte](a)
+    while a[] != 0:
+      a = cast[ptr byte](cast[uint](a) + 1)
+      inc result
+
+{.pop.}
diff --git a/lib/system/memtracker.nim b/lib/system/memtracker.nim
index ae0297438..289f4e024 100644
--- a/lib/system/memtracker.nim
+++ b/lib/system/memtracker.nim
@@ -35,7 +35,7 @@ type
     count*: int
     disabled: bool
     data*: array[400, LogEntry]
-  TrackLogger* = proc (log: TrackLog) {.nimcall, tags: [], locks: 0, gcsafe.}
+  TrackLogger* = proc (log: TrackLog) {.nimcall, tags: [], gcsafe.}
 
 var
   gLog*: TrackLog
@@ -70,22 +70,22 @@ proc addEntry(entry: LogEntry) =
     if interesting:
       gLog.disabled = true
       cprintf("interesting %s:%ld %s\n", entry.file, entry.line, entry.op)
-      let x = cast[proc() {.nimcall, tags: [], gcsafe, locks: 0.}](writeStackTrace)
+      let x = cast[proc() {.nimcall, tags: [], gcsafe, raises: [].}](writeStackTrace)
       x()
-      quit 1
-      if gLog.count > high(gLog.data):
-        gLogger(gLog)
-        gLog.count = 0
-      gLog.data[gLog.count] = entry
-      inc gLog.count
-      gLog.disabled = false
-
-proc memTrackerWrite(address: pointer; size: int; file: cstring; line: int) {.compilerProc.} =
+      rawQuit 1
+      #if gLog.count > high(gLog.data):
+      #  gLogger(gLog)
+      #  gLog.count = 0
+      #gLog.data[gLog.count] = entry
+      #inc gLog.count
+      #gLog.disabled = false
+
+proc memTrackerWrite(address: pointer; size: int; file: cstring; line: int) {.compilerproc.} =
   addEntry LogEntry(op: "write", address: address,
       size: size, file: file, line: line, thread: myThreadId())
 
 proc memTrackerOp*(op: cstring; address: pointer; size: int) {.tags: [],
-         locks: 0, gcsafe.} =
+         gcsafe.} =
   addEntry LogEntry(op: op, address: address, size: size,
       file: "", line: 0, thread: myThreadId())
 
@@ -100,6 +100,7 @@ proc logPendingOps() {.noconv.} =
   gLogger(gLog)
   gLog.count = 0
 
-addQuitProc logPendingOps
+import std/exitprocs
+addExitProc logPendingOps
 
 {.pop.}
diff --git a/lib/system/mm/boehm.nim b/lib/system/mm/boehm.nim
new file mode 100644
index 000000000..362d2d470
--- /dev/null
+++ b/lib/system/mm/boehm.nim
@@ -0,0 +1,140 @@
+
+
+
+proc boehmGCinit {.importc: "GC_init", boehmGC.}
+proc boehmGC_disable {.importc: "GC_disable", boehmGC.}
+proc boehmGC_enable {.importc: "GC_enable", 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.}
+proc boehmRealloc(p: pointer, size: int): pointer {.
+  importc: "GC_realloc", boehmGC.}
+proc boehmDealloc(p: pointer) {.importc: "GC_free", boehmGC.}
+when hasThreadSupport:
+  proc boehmGC_allow_register_threads {.
+    importc: "GC_allow_register_threads", boehmGC.}
+
+proc boehmGetHeapSize: int {.importc: "GC_get_heap_size", boehmGC.}
+  ## Return the number of bytes in the heap.  Excludes collector private
+  ## data structures. Includes empty blocks and fragmentation loss.
+  ## Includes some pages that were allocated but never written.
+
+proc boehmGetFreeBytes: int {.importc: "GC_get_free_bytes", boehmGC.}
+  ## Return a lower bound on the number of free bytes in the heap.
+
+proc boehmGetBytesSinceGC: int {.importc: "GC_get_bytes_since_gc", boehmGC.}
+  ## Return the number of bytes allocated since the last collection.
+
+proc boehmGetTotalBytes: int {.importc: "GC_get_total_bytes", boehmGC.}
+  ## Return the total number of bytes allocated in this process.
+  ## Never decreases.
+
+proc boehmRegisterFinalizer(obj, ff, cd, off, ocd: pointer) {.importc: "GC_register_finalizer", boehmGC.}
+
+proc allocAtomic(size: int): pointer =
+  result = boehmAllocAtomic(size)
+  zeroMem(result, size)
+
+when not defined(useNimRtl):
+
+  proc allocImpl(size: Natural): pointer =
+    result = boehmAlloc(size)
+    if result == nil: raiseOutOfMem()
+  proc alloc0Impl(size: Natural): pointer =
+    result = alloc(size)
+  proc reallocImpl(p: pointer, newSize: Natural): pointer =
+    result = boehmRealloc(p, newSize)
+    if result == nil: raiseOutOfMem()
+  proc realloc0Impl(p: pointer, oldSize, newSize: Natural): pointer =
+    result = boehmRealloc(p, newSize)
+    if result == nil: raiseOutOfMem()
+    if newSize > oldSize:
+      zeroMem(cast[pointer](cast[int](result) + oldSize), newSize - oldSize)
+  proc deallocImpl(p: pointer) = boehmDealloc(p)
+
+  proc allocSharedImpl(size: Natural): pointer = allocImpl(size)
+  proc allocShared0Impl(size: Natural): pointer = alloc0Impl(size)
+  proc reallocSharedImpl(p: pointer, newSize: Natural): pointer = reallocImpl(p, newSize)
+  proc reallocShared0Impl(p: pointer, oldSize, newSize: Natural): pointer = realloc0Impl(p, oldSize, newSize)
+  proc deallocSharedImpl(p: pointer) = deallocImpl(p)
+
+  when hasThreadSupport:
+    proc getFreeSharedMem(): int =
+      boehmGetFreeBytes()
+    proc getTotalSharedMem(): int =
+      boehmGetHeapSize()
+    proc getOccupiedSharedMem(): int =
+      getTotalSharedMem() - getFreeSharedMem()
+
+  #boehmGCincremental()
+
+  proc GC_disable() = boehmGC_disable()
+  proc GC_enable() = boehmGC_enable()
+  proc GC_fullCollect() = boehmGCfullCollect()
+  proc GC_setStrategy(strategy: GC_Strategy) = discard
+  proc GC_enableMarkAndSweep() = discard
+  proc GC_disableMarkAndSweep() = discard
+  proc GC_getStatistics(): string = return ""
+
+  proc getOccupiedMem(): int = return boehmGetHeapSize()-boehmGetFreeBytes()
+  proc getFreeMem(): int = return boehmGetFreeBytes()
+  proc getTotalMem(): int = return boehmGetHeapSize()
+
+  proc nimGC_setStackBottom(theStackBottom: pointer) = discard
+
+proc initGC() =
+  when defined(boehmNoIntPtr):
+    # See #12286
+    boehmGC_set_all_interior_pointers(0)
+  boehmGCinit()
+  when hasThreadSupport:
+    boehmGC_allow_register_threads()
+
+proc boehmgc_finalizer(obj: pointer, typedFinalizer: (proc(x: pointer) {.cdecl.})) =
+  typedFinalizer(obj)
+
+
+proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
+  if ntfNoRefs in typ.flags: result = allocAtomic(size)
+  else: result = alloc(size)
+  if typ.finalizer != nil:
+    boehmRegisterFinalizer(result, boehmgc_finalizer, typ.finalizer, nil, nil)
+{.push overflowChecks: on.}
+proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
+  result = newObj(typ, align(GenericSeqSize, typ.base.align) + len * typ.base.size)
+  cast[PGenericSeq](result).len = len
+  cast[PGenericSeq](result).reserved = len
+{.pop.}
+
+proc growObj(old: pointer, newsize: int): pointer =
+  result = realloc(old, newsize)
+
+proc nimGCref(p: pointer) {.compilerproc, inline.} = discard
+proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard
+
+proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
+  dest[] = src
+proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
+  dest[] = src
+proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline,
+  deprecated: "old compiler compat".} = asgnRef(dest, src)
+
+type
+  MemRegion = object
+
+proc alloc(r: var MemRegion, size: int): pointer =
+  result = boehmAlloc(size)
+  if result == nil: raiseOutOfMem()
+proc alloc0(r: var MemRegion, size: int): pointer =
+  result = alloc(size)
+  zeroMem(result, size)
+proc dealloc(r: var MemRegion, p: pointer) = boehmDealloc(p)
+proc deallocOsPages(r: var MemRegion) {.inline.} = discard
+proc deallocOsPages() {.inline.} = discard
+
+include "system/cellsets"
diff --git a/lib/system/mm/go.nim b/lib/system/mm/go.nim
new file mode 100644
index 000000000..8f3aeb964
--- /dev/null
+++ b/lib/system/mm/go.nim
@@ -0,0 +1,153 @@
+
+when defined(windows):
+  const goLib = "libgo.dll"
+elif defined(macosx):
+  const goLib = "libgo.dylib"
+else:
+  const goLib = "libgo.so"
+
+proc initGC() = discard
+proc GC_disable() = discard
+proc GC_enable() = discard
+proc go_gc() {.importc: "go_gc", dynlib: goLib.}
+proc GC_fullCollect() = go_gc()
+proc GC_setStrategy(strategy: GC_Strategy) = discard
+proc GC_enableMarkAndSweep() = discard
+proc GC_disableMarkAndSweep() = discard
+
+const
+  goNumSizeClasses = 67
+
+type
+  goMStats = object
+    alloc: uint64          # bytes allocated and still in use
+    total_alloc: uint64    # bytes allocated (even if freed)
+    sys: uint64            # bytes obtained from system
+    nlookup: uint64        # number of pointer lookups
+    nmalloc: uint64        # number of mallocs
+    nfree: uint64          # number of frees
+    heap_objects: uint64   # total number of allocated objects
+    pause_total_ns: uint64 # cumulative nanoseconds in GC stop-the-world pauses since the program started
+    numgc: uint32          # number of completed GC cycles
+
+proc goMemStats(): goMStats {.importc: "go_mem_stats", dynlib: goLib.}
+proc goMalloc(size: uint): pointer {.importc: "go_malloc", dynlib: goLib.}
+proc goSetFinalizer(obj: pointer, f: pointer) {.importc: "set_finalizer", codegenDecl:"$1 $2$3 __asm__ (\"main.Set_finalizer\");\n$1 $2$3", dynlib: goLib.}
+proc writebarrierptr(dest: PPointer, src: pointer) {.importc: "writebarrierptr", codegenDecl:"$1 $2$3 __asm__ (\"main.Atomic_store_pointer\");\n$1 $2$3", dynlib: goLib.}
+
+proc GC_getStatistics(): string =
+  var mstats = goMemStats()
+  result = "[GC] total allocated memory: " & $(mstats.total_alloc) & "\n" &
+           "[GC] total memory obtained from system: " & $(mstats.sys) & "\n" &
+           "[GC] occupied memory: " & $(mstats.alloc) & "\n" &
+           "[GC] number of pointer lookups: " & $(mstats.nlookup) & "\n" &
+           "[GC] number of mallocs: " & $(mstats.nmalloc) & "\n" &
+           "[GC] number of frees: " & $(mstats.nfree) & "\n" &
+           "[GC] heap objects: " & $(mstats.heap_objects) & "\n" &
+           "[GC] number of completed GC cycles: " & $(mstats.numgc) & "\n" &
+           "[GC] total GC pause time [ms]: " & $(mstats.pause_total_ns div 1000_000)
+
+proc getOccupiedMem(): int =
+  var mstats = goMemStats()
+  result = int(mstats.alloc)
+
+proc getFreeMem(): int =
+  var mstats = goMemStats()
+  result = int(mstats.sys - mstats.alloc)
+
+proc getTotalMem(): int =
+  var mstats = goMemStats()
+  result = int(mstats.sys)
+
+proc nimGC_setStackBottom(theStackBottom: pointer) = discard
+
+proc allocImpl(size: Natural): pointer =
+  result = goMalloc(size.uint)
+
+proc alloc0Impl(size: Natural): pointer =
+  result = goMalloc(size.uint)
+
+proc reallocImpl(p: pointer, newsize: Natural): pointer =
+  doAssert false, "not implemented"
+
+proc realloc0Impl(p: pointer, oldsize, newsize: Natural): pointer =
+  doAssert false, "not implemented"
+
+proc deallocImpl(p: pointer) =
+  discard
+
+proc allocSharedImpl(size: Natural): pointer = allocImpl(size)
+proc allocShared0Impl(size: Natural): pointer = alloc0Impl(size)
+proc reallocSharedImpl(p: pointer, newsize: Natural): pointer = reallocImpl(p, newsize)
+proc reallocShared0Impl(p: pointer, oldsize, newsize: Natural): pointer = realloc0Impl(p, oldsize, newsize)
+proc deallocSharedImpl(p: pointer) = deallocImpl(p)
+
+when hasThreadSupport:
+  proc getFreeSharedMem(): int = discard
+  proc getTotalSharedMem(): int = discard
+  proc getOccupiedSharedMem(): int = discard
+
+proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
+  writebarrierptr(addr(result), goMalloc(size.uint))
+  if typ.finalizer != nil:
+    goSetFinalizer(result, typ.finalizer)
+
+proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
+  writebarrierptr(addr(result), newObj(typ, size))
+
+proc newObjNoInit(typ: PNimType, size: int): pointer =
+  writebarrierptr(addr(result), newObj(typ, size))
+
+proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
+  writebarrierptr(addr(result), newObj(typ, align(GenericSeqSize, typ.base.align) + len * typ.base.size))
+  cast[PGenericSeq](result).len = len
+  cast[PGenericSeq](result).reserved = len
+  cast[PGenericSeq](result).elemSize = typ.base.size
+  cast[PGenericSeq](result).elemAlign = typ.base.align
+
+proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
+  writebarrierptr(addr(result), newSeq(typ, len))
+
+proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} =
+  result = newObjNoInit(typ, align(GenericSeqSize, typ.base.align) + cap * typ.base.size)
+  cast[PGenericSeq](result).len = 0
+  cast[PGenericSeq](result).reserved = cap
+  cast[PGenericSeq](result).elemSize = typ.base.size
+  cast[PGenericSeq](result).elemAlign = typ.base.align
+
+proc typedMemMove(dest: pointer, src: pointer, size: uint) {.importc: "typedmemmove", dynlib: goLib.}
+
+proc growObj(old: pointer, newsize: int): pointer =
+  # the Go GC doesn't have a realloc
+  let old = cast[PGenericSeq](old)
+  var metadataOld = cast[PGenericSeq](old)
+  if metadataOld.elemSize == 0:
+    metadataOld.elemSize = 1
+
+  let oldsize = align(GenericSeqSize, old.elemAlign) + old.len * old.elemSize
+  writebarrierptr(addr(result), goMalloc(newsize.uint))
+  typedMemMove(result, old, oldsize.uint)
+
+proc nimGCref(p: pointer) {.compilerproc, inline.} = discard
+proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard
+proc nimGCunrefNoCycle(p: pointer) {.compilerproc, inline.} = discard
+proc nimGCunrefRC1(p: pointer) {.compilerproc, inline.} = discard
+proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = discard
+
+proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
+  writebarrierptr(dest, src)
+proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
+  writebarrierptr(dest, src)
+proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline,
+  deprecated: "old compiler compat".} = asgnRef(dest, src)
+
+type
+  MemRegion = object
+
+proc alloc(r: var MemRegion, size: int): pointer =
+  result = alloc(size)
+proc alloc0(r: var MemRegion, size: int): pointer =
+  result = alloc0Impl(size)
+proc dealloc(r: var MemRegion, p: pointer) = dealloc(p)
+proc deallocOsPages(r: var MemRegion) {.inline.} = discard
+proc deallocOsPages() {.inline.} = discard
diff --git a/lib/system/mm/malloc.nim b/lib/system/mm/malloc.nim
new file mode 100644
index 000000000..47f1a95ae
--- /dev/null
+++ b/lib/system/mm/malloc.nim
@@ -0,0 +1,97 @@
+
+{.push stackTrace: off.}
+
+proc allocImpl(size: Natural): pointer =
+  result = c_malloc(size.csize_t)
+  when defined(zephyr):
+    if result == nil:
+      raiseOutOfMem()
+
+proc alloc0Impl(size: Natural): pointer =
+  result = c_calloc(size.csize_t, 1)
+  when defined(zephyr):
+    if result == nil:
+      raiseOutOfMem()
+
+proc reallocImpl(p: pointer, newSize: Natural): pointer =
+  result = c_realloc(p, newSize.csize_t)
+  when defined(zephyr):
+    if result == nil:
+      raiseOutOfMem()
+
+proc realloc0Impl(p: pointer, oldsize, newSize: Natural): pointer =
+  result = realloc(p, newSize.csize_t)
+  if newSize > oldSize:
+    zeroMem(cast[pointer](cast[uint](result) + uint(oldSize)), newSize - oldSize)
+
+proc deallocImpl(p: pointer) =
+  c_free(p)
+
+
+# The shared allocators map on the regular ones
+
+proc allocSharedImpl(size: Natural): pointer =
+  allocImpl(size)
+
+proc allocShared0Impl(size: Natural): pointer =
+  alloc0Impl(size)
+
+proc reallocSharedImpl(p: pointer, newSize: Natural): pointer =
+  reallocImpl(p, newSize)
+
+proc reallocShared0Impl(p: pointer, oldsize, newSize: Natural): pointer =
+  realloc0Impl(p, oldSize, newSize)
+
+proc deallocSharedImpl(p: pointer) = deallocImpl(p)
+
+
+# Empty stubs for the GC
+
+proc GC_disable() = discard
+proc GC_enable() = discard
+
+when not defined(gcOrc):
+  proc GC_fullCollect() = discard
+  proc GC_enableMarkAndSweep() = discard
+  proc GC_disableMarkAndSweep() = discard
+
+proc GC_setStrategy(strategy: GC_Strategy) = discard
+
+proc getOccupiedMem(): int = discard
+proc getFreeMem(): int = discard
+proc getTotalMem(): int = discard
+
+proc nimGC_setStackBottom(theStackBottom: pointer) = discard
+
+proc initGC() = discard
+
+proc newObjNoInit(typ: PNimType, size: int): pointer =
+  result = alloc(size)
+
+proc growObj(old: pointer, newsize: int): pointer =
+  result = realloc(old, newsize)
+
+proc nimGCref(p: pointer) {.compilerproc, inline.} = discard
+proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard
+
+when not defined(gcDestructors):
+  proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
+    dest[] = src
+
+proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
+  dest[] = src
+proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline,
+  deprecated: "old compiler compat".} = asgnRef(dest, src)
+
+type
+  MemRegion = object
+
+proc alloc(r: var MemRegion, size: int): pointer =
+  result = alloc(size)
+proc alloc0(r: var MemRegion, size: int): pointer =
+  result = alloc0Impl(size)
+proc dealloc(r: var MemRegion, p: pointer) = dealloc(p)
+proc deallocOsPages(r: var MemRegion) = discard
+proc deallocOsPages() = discard
+
+{.pop.}
diff --git a/lib/system/mm/none.nim b/lib/system/mm/none.nim
new file mode 100644
index 000000000..7818a0805
--- /dev/null
+++ b/lib/system/mm/none.nim
@@ -0,0 +1,46 @@
+
+when appType == "lib":
+  {.warning: "nogc in a library context may not work".}
+
+include "system/alloc"
+
+proc initGC() = discard
+proc GC_disable() = discard
+proc GC_enable() = discard
+proc GC_fullCollect() = discard
+proc GC_setStrategy(strategy: GC_Strategy) = discard
+proc GC_enableMarkAndSweep() = discard
+proc GC_disableMarkAndSweep() = discard
+proc GC_getStatistics(): string = return ""
+
+proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
+  result = alloc0Impl(size)
+
+proc newObjNoInit(typ: PNimType, size: int): pointer =
+  result = alloc(size)
+
+{.push overflowChecks: on.}
+proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
+  result = newObj(typ, align(GenericSeqSize, typ.align) + len * typ.base.size)
+  cast[PGenericSeq](result).len = len
+  cast[PGenericSeq](result).reserved = len
+{.pop.}
+
+proc growObj(old: pointer, newsize: int): pointer =
+  result = realloc(old, newsize)
+
+proc nimGC_setStackBottom(theStackBottom: pointer) = discard
+proc nimGCref(p: pointer) {.compilerproc, inline.} = discard
+proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard
+
+proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
+  dest[] = src
+proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
+  dest[] = src
+proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline,
+  deprecated: "old compiler compat".} = asgnRef(dest, src)
+
+var allocator {.rtlThreadVar.}: MemRegion
+instantiateForRegion(allocator)
+
+include "system/cellsets"
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index b33ca93f2..26f2f0bbf 100644
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -17,10 +17,10 @@ const
   debugGC = false # we wish to debug the GC...
   logGC = false
   traceGC = false # extensive debugging
-  alwaysCycleGC = defined(smokeCycles)
-  alwaysGC = defined(fulldebug) # collect after every memory
+  alwaysCycleGC = defined(nimSmokeCycles)
+  alwaysGC = defined(nimFulldebug) # collect after every memory
                                 # allocation (for debugging)
-  leakDetector = defined(leakDetector)
+  leakDetector = defined(nimLeakDetector)
   overwriteFree = defined(nimBurnFree) # overwrite memory with 0xFF before free
   trackAllocationSource = leakDetector
 
@@ -30,548 +30,84 @@ const
   coalescRight = true
   coalescLeft = true
   logAlloc = false
-  useCellIds = defined(corruption)
+  useCellIds = defined(nimCorruption)
 
 type
   PPointer = ptr pointer
   ByteArray = UncheckedArray[byte]
   PByte = ptr ByteArray
   PString = ptr string
-{.deprecated: [TByteArray: ByteArray].}
 
-# Page size of the system; in most cases 4096 bytes. For exotic OS or
-# CPU this needs to be changed:
-const
-  PageShift = when defined(cpu16): 8 else: 12 # \
-    # my tests showed no improvments for using larger page sizes.
-  PageSize = 1 shl PageShift
-  PageMask = PageSize-1
-
-  MemAlign = 8 # also minimal allocatable memory block
-
-  BitsPerPage = PageSize div MemAlign
-  UnitsPerPage = BitsPerPage div (sizeof(int)*8)
-    # how many ints do we need to describe a page:
-    # on 32 bit systems this is only 16 (!)
-
-  TrunkShift = 9
-  BitsPerTrunk = 1 shl TrunkShift # needs to be power of 2 and divisible by 64
-  TrunkMask = BitsPerTrunk - 1
-  IntsPerTrunk = BitsPerTrunk div (sizeof(int)*8)
-  IntShift = 5 + ord(sizeof(int) == 8) # 5 or 6, depending on int width
-  IntMask = 1 shl IntShift - 1
+when declared(IntsPerTrunk):
+  discard
+else:
+  include bitmasks
 
 proc raiseOutOfMem() {.noinline.} =
   if outOfMemHook != nil: outOfMemHook()
-  echo("out of memory")
-  quit(1)
+  cstderr.rawWrite("out of memory\n")
+  rawQuit(1)
 
 when defined(boehmgc):
-  proc boehmGCinit {.importc: "GC_init", boehmGC.}
-  proc boehmGC_disable {.importc: "GC_disable", boehmGC.}
-  proc boehmGC_enable {.importc: "GC_enable", boehmGC.}
-  proc boehmGCincremental {.
-    importc: "GC_enable_incremental", boehmGC.}
-  proc boehmGCfullCollect {.importc: "GC_gcollect", boehmGC.}
-  proc boehmAlloc(size: int): pointer {.importc: "GC_malloc", boehmGC.}
-  proc boehmAllocAtomic(size: int): pointer {.
-    importc: "GC_malloc_atomic", boehmGC.}
-  proc boehmRealloc(p: pointer, size: int): pointer {.
-    importc: "GC_realloc", boehmGC.}
-  proc boehmDealloc(p: pointer) {.importc: "GC_free", boehmGC.}
-  when hasThreadSupport:
-    proc boehmGC_allow_register_threads {.
-      importc: "GC_allow_register_threads", boehmGC.}
-
-  proc boehmGetHeapSize: int {.importc: "GC_get_heap_size", boehmGC.}
-    ## Return the number of bytes in the heap.  Excludes collector private
-    ## data structures. Includes empty blocks and fragmentation loss.
-    ## Includes some pages that were allocated but never written.
-
-  proc boehmGetFreeBytes: int {.importc: "GC_get_free_bytes", boehmGC.}
-    ## Return a lower bound on the number of free bytes in the heap.
-
-  proc boehmGetBytesSinceGC: int {.importc: "GC_get_bytes_since_gc", boehmGC.}
-    ## Return the number of bytes allocated since the last collection.
-
-  proc boehmGetTotalBytes: int {.importc: "GC_get_total_bytes", boehmGC.}
-    ## Return the total number of bytes allocated in this process.
-    ## Never decreases.
-
-  proc allocAtomic(size: int): pointer =
-    result = boehmAllocAtomic(size)
-    zeroMem(result, size)
-
-  when not defined(useNimRtl):
-
-    proc alloc(size: Natural): pointer =
-      result = boehmAlloc(size)
-      if result == nil: raiseOutOfMem()
-    proc alloc0(size: Natural): pointer =
-      result = alloc(size)
-    proc realloc(p: pointer, newsize: Natural): pointer =
-      result = boehmRealloc(p, newsize)
-      if result == nil: raiseOutOfMem()
-    proc dealloc(p: pointer) = boehmDealloc(p)
-
-    proc allocShared(size: Natural): pointer =
-      result = boehmAlloc(size)
-      if result == nil: raiseOutOfMem()
-    proc allocShared0(size: Natural): pointer =
-      result = allocShared(size)
-    proc reallocShared(p: pointer, newsize: Natural): pointer =
-      result = boehmRealloc(p, newsize)
-      if result == nil: raiseOutOfMem()
-    proc deallocShared(p: pointer) = boehmDealloc(p)
-
-    when hasThreadSupport:
-      proc getFreeSharedMem(): int =
-        boehmGetFreeBytes()
-      proc getTotalSharedMem(): int =
-        boehmGetHeapSize()
-      proc getOccupiedSharedMem(): int =
-        getTotalSharedMem() - getFreeSharedMem()
-
-    #boehmGCincremental()
-
-    proc GC_disable() = boehmGC_disable()
-    proc GC_enable() = boehmGC_enable()
-    proc GC_fullCollect() = boehmGCfullCollect()
-    proc GC_setStrategy(strategy: GC_Strategy) = discard
-    proc GC_enableMarkAndSweep() = discard
-    proc GC_disableMarkAndSweep() = discard
-    proc GC_getStatistics(): string = return ""
-
-    proc getOccupiedMem(): int = return boehmGetHeapSize()-boehmGetFreeBytes()
-    proc getFreeMem(): int = return boehmGetFreeBytes()
-    proc getTotalMem(): int = return boehmGetHeapSize()
-
-    proc nimGC_setStackBottom(theStackBottom: pointer) = discard
-
-  proc initGC() =
-    boehmGCinit()
-    when hasThreadSupport:
-      boehmGC_allow_register_threads()
-
-  proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
-    if ntfNoRefs in typ.flags: result = allocAtomic(size)
-    else: result = alloc(size)
-  proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
-    result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
-    cast[PGenericSeq](result).len = len
-    cast[PGenericSeq](result).reserved = len
-
-  proc growObj(old: pointer, newsize: int): pointer =
-    result = realloc(old, newsize)
-
-  proc nimGCref(p: pointer) {.compilerproc, inline.} = discard
-  proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard
-
-  proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
-    dest[] = src
-  proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
-    dest[] = src
-  proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} =
-    dest[] = src
-
-  type
-    MemRegion = object {.final, pure.}
-  {.deprecated: [TMemRegion: MemRegion].}
-
-  proc alloc(r: var MemRegion, size: int): pointer =
-    result = boehmAlloc(size)
-    if result == nil: raiseOutOfMem()
-  proc alloc0(r: var MemRegion, size: int): pointer =
-    result = alloc(size)
-    zeroMem(result, size)
-  proc dealloc(r: var MemRegion, p: pointer) = boehmDealloc(p)
-  proc deallocOsPages(r: var MemRegion) {.inline.} = discard
-  proc deallocOsPages() {.inline.} = discard
-
-  include "system/cellsets"
+  include system / mm / boehm
 
 elif defined(gogc):
-  when defined(windows):
-    const goLib = "libgo.dll"
-  elif defined(macosx):
-    const goLib = "libgo.dylib"
-  else:
-    const goLib = "libgo.so"
-
-  proc roundup(x, v: int): int {.inline.} =
-    result = (x + (v-1)) and not (v-1)
-
-  proc initGC() = discard
-  # runtime_setgcpercent is only available in GCC 5
-  proc GC_disable() = discard
-  proc GC_enable() = discard
-  proc goRuntimeGC(force: int32) {.importc: "runtime_gc", dynlib: goLib.}
-  proc GC_fullCollect() = goRuntimeGC(2)
-  proc GC_setStrategy(strategy: GC_Strategy) = discard
-  proc GC_enableMarkAndSweep() = discard
-  proc GC_disableMarkAndSweep() = discard
-
-  const
-    goNumSizeClasses = 67
-
-  type
-    cbool {.importc: "_Bool", nodecl.} = bool
-
-    goMStats_inner_struct = object
-        size: uint32
-        nmalloc: uint64
-        nfree: uint64
-
-    goMStats = object
-        # General statistics.
-        alloc: uint64            # bytes allocated and still in use
-        total_alloc: uint64      # bytes allocated (even if freed)
-        sys: uint64              # bytes obtained from system (should be sum of xxx_sys below, no locking, approximate)
-        nlookup: uint64          # number of pointer lookups
-        nmalloc: uint64          # number of mallocs
-        nfree: uint64            # number of frees
-        # Statistics about malloc heap.
-        # protected by mheap.Lock
-        heap_alloc: uint64       # bytes allocated and still in use
-        heap_sys: uint64         # bytes obtained from system
-        heap_idle: uint64        # bytes in idle spans
-        heap_inuse: uint64       # bytes in non-idle spans
-        heap_released: uint64    # bytes released to the OS
-        heap_objects: uint64 # total number of allocated objects
-        # Statistics about allocation of low-level fixed-size structures.
-        # Protected by FixAlloc locks.
-        stacks_inuse: uint64     # bootstrap stacks
-        stacks_sys: uint64
-        mspan_inuse: uint64      # MSpan structures
-        mspan_sys: uint64
-        mcache_inuse: uint64     # MCache structures
-        mcache_sys: uint64
-        buckhash_sys: uint64     # profiling bucket hash table
-        gc_sys: uint64
-        other_sys: uint64
-        # Statistics about garbage collector.
-        # Protected by mheap or stopping the world during GC.
-        next_gc: uint64          # next GC (in heap_alloc time)
-        last_gc: uint64          # last GC (in absolute time)
-        pause_total_ns: uint64
-        pause_ns: array[256, uint64] # circular buffer of recent gc pause lengths
-        pause_end: array[256, uint64] # circular buffer of recent gc end times (nanoseconds since 1970)
-        numgc: uint32
-        numforcedgc: uint32      # number of user-forced GCs
-        gc_cpu_fraction: float64 # fraction of CPU time used by GC
-        enablegc: cbool
-        debuggc: cbool
-        # Statistics about allocation size classes.
-        by_size: array[goNumSizeClasses, goMStats_inner_struct]
-        # Statistics below here are not exported to MemStats directly.
-        tinyallocs: uint64       # number of tiny allocations that didn't cause actual allocation; not exported to go directly
-        gc_trigger: uint64
-        heap_live: uint64
-        heap_scan: uint64
-        heap_marked: uint64
-
-  proc goRuntime_ReadMemStats(a2: ptr goMStats) {.cdecl,
-    importc: "runtime_ReadMemStats",
-    codegenDecl: "$1 $2$3 __asm__ (\"runtime.ReadMemStats\");\n$1 $2$3",
-    dynlib: goLib.}
-
-  proc GC_getStatistics(): string =
-    var mstats: goMStats
-    goRuntime_ReadMemStats(addr mstats)
-    result = "[GC] total allocated memory: " & $(mstats.total_alloc) & "\n" &
-             "[GC] total memory obtained from system: " & $(mstats.sys) & "\n" &
-             "[GC] occupied memory: " & $(mstats.alloc) & "\n" &
-             "[GC] number of pointer lookups: " & $(mstats.nlookup) & "\n" &
-             "[GC] number of mallocs: " & $(mstats.nmalloc) & "\n" &
-             "[GC] number of frees: " & $(mstats.nfree) & "\n" &
-             "[GC] heap objects: " & $(mstats.heap_objects) & "\n" &
-             "[GC] numgc: " & $(mstats.numgc) & "\n" &
-             "[GC] enablegc: " & $(mstats.enablegc) & "\n" &
-             "[GC] debuggc: " & $(mstats.debuggc) & "\n" &
-             "[GC] total pause time [ms]: " & $(mstats.pause_total_ns div 1000_000)
-
-  proc getOccupiedMem(): int =
-    var mstats: goMStats
-    goRuntime_ReadMemStats(addr mstats)
-    result = int(mstats.alloc)
-
-  proc getFreeMem(): int =
-    var mstats: goMStats
-    goRuntime_ReadMemStats(addr mstats)
-    result = int(mstats.sys - mstats.alloc)
-
-  proc getTotalMem(): int =
-    var mstats: goMStats
-    goRuntime_ReadMemStats(addr mstats)
-    result = int(mstats.sys)
-
-  proc nimGC_setStackBottom(theStackBottom: pointer) = discard
-
-  proc alloc(size: Natural): pointer =
-    result = c_malloc(size)
-    if result == nil: raiseOutOfMem()
-
-  proc alloc0(size: Natural): pointer =
-    result = alloc(size)
-    zeroMem(result, size)
-
-  proc realloc(p: pointer, newsize: Natural): pointer =
-    result = c_realloc(p, newsize)
-    if result == nil: raiseOutOfMem()
-
-  proc dealloc(p: pointer) = c_free(p)
-
-  proc allocShared(size: Natural): pointer =
-    result = c_malloc(size)
-    if result == nil: raiseOutOfMem()
-
-  proc allocShared0(size: Natural): pointer =
-    result = alloc(size)
-    zeroMem(result, size)
+  include system / mm / go
 
-  proc reallocShared(p: pointer, newsize: Natural): pointer =
-    result = c_realloc(p, newsize)
-    if result == nil: raiseOutOfMem()
+elif (defined(nogc) or defined(gcDestructors)) and defined(useMalloc):
+  include system / mm / malloc
 
-  proc deallocShared(p: pointer) = c_free(p)
+  when defined(nogc):
+    proc GC_getStatistics(): string = ""
+    proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
+      result = alloc0(size)
 
-  when hasThreadSupport:
-    proc getFreeSharedMem(): int = discard
-    proc getTotalSharedMem(): int = discard
-    proc getOccupiedSharedMem(): int = discard
-
-  const goFlagNoZero: uint32 = 1 shl 3
-  proc goRuntimeMallocGC(size: uint, typ: uint, flag: uint32): pointer {.importc: "runtime_mallocgc", dynlib: goLib.}
-
-  proc goSetFinalizer(obj: pointer, f: pointer) {.importc: "set_finalizer", codegenDecl:"$1 $2$3 __asm__ (\"main.Set_finalizer\");\n$1 $2$3", dynlib: goLib.}
-
-  proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
-    result = goRuntimeMallocGC(roundup(size, sizeof(pointer)).uint, 0.uint, 0.uint32)
-    if typ.finalizer != nil:
-      goSetFinalizer(result, typ.finalizer)
-
-  proc newObjNoInit(typ: PNimType, size: int): pointer =
-    result = goRuntimeMallocGC(roundup(size, sizeof(pointer)).uint, 0.uint, goFlagNoZero)
-    if typ.finalizer != nil:
-      goSetFinalizer(result, typ.finalizer)
-
-  proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
-    result = newObj(typ, len * typ.base.size + GenericSeqSize)
-    cast[PGenericSeq](result).len = len
-    cast[PGenericSeq](result).reserved = len
-    cast[PGenericSeq](result).elemSize = typ.base.size
-
-  proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} =
-    result = newObj(typ, cap * typ.base.size + GenericSeqSize)
-    cast[PGenericSeq](result).len = 0
-    cast[PGenericSeq](result).reserved = cap
-    cast[PGenericSeq](result).elemSize = typ.base.size
-
-  proc growObj(old: pointer, newsize: int): pointer =
-    # the Go GC doesn't have a realloc
-    var
-      oldsize = cast[PGenericSeq](old).len * cast[PGenericSeq](old).elemSize + GenericSeqSize
-    result = goRuntimeMallocGC(roundup(newsize, sizeof(pointer)).uint, 0.uint, goFlagNoZero)
-    copyMem(result, old, oldsize)
-    zeroMem(cast[pointer](cast[ByteAddress](result) +% oldsize), newsize - oldsize)
-
-  proc nimGCref(p: pointer) {.compilerproc, inline.} = discard
-  proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard
-
-  proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
-    dest[] = src
-  proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
-    dest[] = src
-  proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} =
-    dest[] = src
-
-  type
-    MemRegion = object {.final, pure.}
-  {.deprecated: [TMemRegion: MemRegion].}
-
-  proc alloc(r: var MemRegion, size: int): pointer =
-    result = alloc(size)
-  proc alloc0(r: var MemRegion, size: int): pointer =
-    result = alloc0(size)
-  proc dealloc(r: var MemRegion, p: pointer) = dealloc(p)
-  proc deallocOsPages(r: var MemRegion) {.inline.} = discard
-  proc deallocOsPages() {.inline.} = discard
-
-elif defined(nogc) and defined(useMalloc):
-
-  when not defined(useNimRtl):
-    proc alloc(size: Natural): pointer =
-      var x = c_malloc(size + sizeof(size))
-      if x == nil: raiseOutOfMem()
-
-      cast[ptr int](x)[] = size
-      result = cast[pointer](cast[int](x) + sizeof(size))
-
-    proc alloc0(size: Natural): pointer =
-      result = alloc(size)
-      zeroMem(result, size)
-    proc realloc(p: pointer, newsize: Natural): pointer =
-      var x = cast[pointer](cast[int](p) - sizeof(newsize))
-      let oldsize = cast[ptr int](x)[]
-
-      x = c_realloc(x, newsize + sizeof(newsize))
-
-      if x == nil: raiseOutOfMem()
-
-      cast[ptr int](x)[] = newsize
-      result = cast[pointer](cast[int](x) + sizeof(newsize))
-
-      if newsize > oldsize:
-        zeroMem(cast[pointer](cast[int](result) + oldsize), newsize - oldsize)
-
-    proc dealloc(p: pointer) = c_free(cast[pointer](cast[int](p) - sizeof(int)))
-
-    proc allocShared(size: Natural): pointer =
-      result = c_malloc(size)
-      if result == nil: raiseOutOfMem()
-    proc allocShared0(size: Natural): pointer =
-      result = alloc(size)
-      zeroMem(result, size)
-    proc reallocShared(p: pointer, newsize: Natural): pointer =
-      result = c_realloc(p, newsize)
-      if result == nil: raiseOutOfMem()
-    proc deallocShared(p: pointer) = c_free(p)
-
-    proc GC_disable() = discard
-    proc GC_enable() = discard
-    proc GC_fullCollect() = discard
-    proc GC_setStrategy(strategy: GC_Strategy) = discard
-    proc GC_enableMarkAndSweep() = discard
-    proc GC_disableMarkAndSweep() = discard
-    proc GC_getStatistics(): string = return ""
-
-    proc getOccupiedMem(): int = discard
-    proc getFreeMem(): int = discard
-    proc getTotalMem(): int = discard
-
-    proc nimGC_setStackBottom(theStackBottom: pointer) = discard
-
-  proc initGC() = discard
-
-  proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
-    result = alloc0(size)
-  proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
-    result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
-    cast[PGenericSeq](result).len = len
-    cast[PGenericSeq](result).reserved = len
-
-  proc newObjNoInit(typ: PNimType, size: int): pointer =
-    result = alloc(size)
-
-  proc growObj(old: pointer, newsize: int): pointer =
-    result = realloc(old, newsize)
-
-  proc nimGCref(p: pointer) {.compilerproc, inline.} = discard
-  proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard
-
-  proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
-    dest[] = src
-  proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
-    dest[] = src
-  proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} =
-    dest[] = src
-
-  type
-    MemRegion = object {.final, pure.}
-  {.deprecated: [TMemRegion: MemRegion].}
-
-  proc alloc(r: var MemRegion, size: int): pointer =
-    result = alloc(size)
-  proc alloc0(r: var MemRegion, size: int): pointer =
-    result = alloc0(size)
-  proc dealloc(r: var MemRegion, p: pointer) = dealloc(p)
-  proc deallocOsPages(r: var MemRegion) {.inline.} = discard
-  proc deallocOsPages() {.inline.} = discard
+    proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
+      result = newObj(typ, align(GenericSeqSize, typ.align) + len * typ.base.size)
+      cast[PGenericSeq](result).len = len
+      cast[PGenericSeq](result).reserved = len
 
 elif defined(nogc):
-  # Even though we don't want the GC, we cannot simply use C's memory manager
-  # because Nim's runtime wants ``realloc`` to zero out the additional
-  # space which C's ``realloc`` does not. And we cannot get the old size of an
-  # object, because C does not support this operation... Even though every
-  # possible implementation has to have a way to determine the object's size.
-  # C just sucks.
-  when appType == "lib":
-    {.warning: "nogc in a library context may not work".}
-
-  include "system/alloc"
-
-  proc initGC() = discard
-  proc GC_disable() = discard
-  proc GC_enable() = discard
-  proc GC_fullCollect() = discard
-  proc GC_setStrategy(strategy: GC_Strategy) = discard
-  proc GC_enableMarkAndSweep() = discard
-  proc GC_disableMarkAndSweep() = discard
-  proc GC_getStatistics(): string = return ""
-
-  proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
-    result = alloc0(size)
-
-  proc newObjNoInit(typ: PNimType, size: int): pointer =
-    result = alloc(size)
-
-  proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
-    result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
-    cast[PGenericSeq](result).len = len
-    cast[PGenericSeq](result).reserved = len
-
-  proc growObj(old: pointer, newsize: int): pointer =
-    result = realloc(old, newsize)
-
-  proc nimGC_setStackBottom(theStackBottom: pointer) = discard
-  proc nimGCref(p: pointer) {.compilerproc, inline.} = discard
-  proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard
-
-  proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
-    dest[] = src
-  proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
-    dest[] = src
-  proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} =
-    dest[] = src
-
-  var allocator {.rtlThreadVar.}: MemRegion
-  instantiateForRegion(allocator)
-
-  include "system/cellsets"
+  include system / mm / none
 
 else:
   when not defined(gcRegions):
     include "system/alloc"
 
-    include "system/cellsets"
-    when not leakDetector and not useCellIds:
+    when not usesDestructors:
+      include "system/cellsets"
+    when not leakDetector and not useCellIds and not defined(nimV2):
       sysAssert(sizeof(Cell) == sizeof(FreeCell), "sizeof FreeCell")
-  when compileOption("gc", "v2"):
-    include "system/gc2"
-  elif defined(gcRegions):
+  when defined(gcRegions):
     # XXX due to bootstrapping reasons, we cannot use  compileOption("gc", "stack") here
     include "system/gc_regions"
+  elif defined(nimV2) or usesDestructors:
+    when not defined(useNimRtl):
+      var allocator {.rtlThreadVar.}: MemRegion
+      instantiateForRegion(allocator)
+    when defined(gcHooks):
+      include "system/gc_hooks"
   elif defined(gcMarkAndSweep):
     # XXX use 'compileOption' here
     include "system/gc_ms"
-  elif defined(gcGenerational):
-    include "system/gc"
   else:
     include "system/gc"
 
-when not declared(nimNewSeqOfCap):
+when not declared(nimNewSeqOfCap) and not defined(nimSeqsV2):
+  {.push overflowChecks: on.}
   proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} =
     when defined(gcRegions):
-      let s = mulInt(cap, typ.base.size)  # newStr already adds GenericSeqSize
+      let s = cap * typ.base.size  # newStr already adds GenericSeqSize
       result = newStr(typ, s, ntfNoRefs notin typ.base.flags)
     else:
-      let s = addInt(mulInt(cap, typ.base.size), GenericSeqSize)
+      let s = align(GenericSeqSize, typ.base.align) + cap * typ.base.size
       when declared(newObjNoInit):
         result = if ntfNoRefs in typ.base.flags: newObjNoInit(typ, s) else: newObj(typ, s)
       else:
         result = newObj(typ, s)
       cast[PGenericSeq](result).len = 0
       cast[PGenericSeq](result).reserved = cap
+  {.pop.}
 
 {.pop.}
 
diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim
index 7671e5962..cf81f6d86 100644
--- a/lib/system/nimscript.nim
+++ b/lib/system/nimscript.nim
@@ -7,17 +7,18 @@
 #    distribution, for details about the copyright.
 #
 
+## To learn about scripting in Nim see `NimScript<nims.html>`_
 
 # Nim's configuration system now uses Nim for scripting. This module provides
 # a few things that are required for this to work.
 
 const
   buildOS* {.magic: "BuildOS".}: string = ""
-    ## The OS this build is running on. Can be different from ``system.hostOS``
+    ## The OS this build is running on. Can be different from `system.hostOS`
     ## for cross compilations.
 
   buildCPU* {.magic: "BuildCPU".}: string = ""
-    ## The CPU this build is running on. Can be different from ``system.hostCPU``
+    ## The CPU this build is running on. Can be different from `system.hostCPU`
     ## for cross compilations.
 
 template builtin = discard
@@ -25,14 +26,11 @@ template builtin = discard
 # We know the effects better than the compiler:
 {.push hint[XDeclaredButNotUsed]: off.}
 
-proc listDirs*(dir: string): seq[string] =
-  ## Lists all the subdirectories (non-recursively) in the directory `dir`.
-  builtin
-proc listFiles*(dir: string): seq[string] =
-  ## Lists all the files (non-recursively) in the directory `dir`.
-  builtin
-
-proc removeDir(dir: string){.
+proc listDirsImpl(dir: string): seq[string] {.
+  tags: [ReadIOEffect], raises: [OSError].} = builtin
+proc listFilesImpl(dir: string): seq[string] {.
+  tags: [ReadIOEffect], raises: [OSError].} = builtin
+proc removeDir(dir: string, checkDir = true) {.
   tags: [ReadIOEffect, WriteIOEffect], raises: [OSError].} = builtin
 proc removeFile(dir: string) {.
   tags: [ReadIOEffect, WriteIOEffect], raises: [OSError].} = builtin
@@ -46,7 +44,8 @@ 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.
@@ -58,7 +57,7 @@ proc warningImpl(arg, orig: string) = discard
 proc hintImpl(arg, orig: string) = discard
 
 proc paramStr*(i: int): string =
-  ## Retrieves the ``i``'th command line parameter.
+  ## Retrieves the `i`'th command line parameter.
   builtin
 
 proc paramCount*(): int =
@@ -67,32 +66,32 @@ proc paramCount*(): int =
 
 proc switch*(key: string, val="") =
   ## Sets a Nim compiler command line switch, for
-  ## example ``switch("checks", "on")``.
+  ## example `switch("checks", "on")`.
   builtin
 
 proc warning*(name: string; val: bool) =
   ## Disables or enables a specific warning.
   let v = if val: "on" else: "off"
-  warningImpl(name & "]:" & v, "warning[" & name & "]:" & v)
+  warningImpl(name & ":" & v, "warning:" & name & ":" & v)
 
 proc hint*(name: string; val: bool) =
   ## Disables or enables a specific hint.
   let v = if val: "on" else: "off"
-  hintImpl(name & "]:" & v, "hint[" & name & "]:" & v)
+  hintImpl(name & ":" & v, "hint:" & name & ":" & v)
 
 proc patchFile*(package, filename, replacement: string) =
   ## Overrides the location of a given file belonging to the
   ## passed package.
-  ## If the ``replacement`` is not an absolute path, the path
+  ## If the `replacement` is not an absolute path, the path
   ## is interpreted to be local to the Nimscript file that contains
-  ## the call to ``patchFile``, Nim's ``--path`` is not used at all
+  ## the call to `patchFile`, Nim's `--path` is not used at all
   ## to resolve the filename!
+  ## The compiler also performs `path substitution <nimc.html#compiler-usage-commandminusline-switches>`_ on `replacement`.
   ##
   ## Example:
-  ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   patchFile("stdlib", "asyncdispatch", "patches/replacement")
+  ##   ```
   discard
 
 proc getCommand*(): string =
@@ -121,7 +120,11 @@ proc existsEnv*(key: string): bool {.tags: [ReadIOEffect].} =
   builtin
 
 proc putEnv*(key, val: string) {.tags: [WriteIOEffect].} =
-  ## Sets the value of the environment variable named key to val.
+  ## Sets the value of the environment variable named `key` to `val`.
+  builtin
+
+proc delEnv*(key: string) {.tags: [WriteIOEffect].} =
+  ## Deletes the environment variable named `key`.
   builtin
 
 proc fileExists*(filename: string): bool {.tags: [ReadIOEffect].} =
@@ -133,15 +136,7 @@ proc dirExists*(dir: string): bool {.
   ## Checks if the directory `dir` exists.
   builtin
 
-proc existsFile*(filename: string): bool =
-  ## An alias for ``fileExists``.
-  fileExists(filename)
-
-proc existsDir*(dir: string): bool =
-  ## An alias for ``dirExists``.
-  dirExists(dir)
-
-proc selfExe*(): string =
+proc selfExe*(): string {.deprecated: "Deprecated since v1.7; Use getCurrentCompilerExe".} =
   ## Returns the currently running nim or nimble executable.
   builtin
 
@@ -155,16 +150,28 @@ proc toDll*(filename: string): string =
 
 proc strip(s: string): string =
   var i = 0
-  while s[i] in {' ', '\c', '\L'}: inc i
+  while s[i] in {' ', '\c', '\n'}: inc i
   result = s.substr(i)
+  if result[0] == '"' and result[^1] == '"':
+    result = result[1..^2]
 
 template `--`*(key, val: untyped) =
-  ## A shortcut for ``switch(astToStr(key), astToStr(val))``.
-  switch(astToStr(key), strip astToStr(val))
+  ## A shortcut for `switch <#switch,string,string>`_
+  ## Example:
+  ##   ```nim
+  ##   --path:somePath # same as switch("path", "somePath")
+  ##   --path:"someOtherPath" # same as switch("path", "someOtherPath")
+  ##   --hint:"[Conf]:off" # same as switch("hint", "[Conf]:off")
+  ##   ```
+  switch(strip(astToStr(key)), strip(astToStr(val)))
 
 template `--`*(key: untyped) =
-  ## A shortcut for ``switch(astToStr(key)``.
-  switch(astToStr(key), "")
+  ## A shortcut for `switch <#switch,string,string>`_
+  ## Example:
+  ##   ```nim
+  ##   --listCmd # same as switch("listCmd")
+  ##   ```
+  switch(strip(astToStr(key)))
 
 type
   ScriptMode* {.pure.} = enum ## Controls the behaviour of the script.
@@ -177,20 +184,33 @@ 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 == ScriptMode.Verbose or mode == ScriptMode.Whatif:
+  if mode in {ScriptMode.Verbose, ScriptMode.Whatif}:
     echo "[NimScript] ", msg
-  if mode != ScriptMode.WhatIf:
+  if mode != ScriptMode.Whatif:
     body
 
-proc rmDir*(dir: string) {.raises: [OSError].} =
+proc listDirs*(dir: string): seq[string] =
+  ## Lists all the subdirectories (non-recursively) in the directory `dir`.
+  result = listDirsImpl(dir)
+  checkOsError()
+
+proc listFiles*(dir: string): seq[string] =
+  ## Lists all the files (non-recursively) in the directory `dir`.
+  result = listFilesImpl(dir)
+  checkOsError()
+
+proc rmDir*(dir: string, checkDir = false) {.raises: [OSError].} =
   ## Removes the directory `dir`.
   log "rmDir: " & dir:
-    removeDir dir
+    removeDir(dir, checkDir = checkDir)
     checkOsError()
 
 proc rmFile*(file: string) {.raises: [OSError].} =
@@ -230,22 +250,40 @@ proc cpDir*(`from`, to: string) {.raises: [OSError].} =
     copyDir `from`, to
     checkOsError()
 
-proc exec*(command: string) =
-  ## Executes an external process.
+proc exec*(command: string) {.
+  raises: [OSError], tags: [ExecIOEffect, WriteIOEffect].} =
+  ## Executes an external process. If the external process terminates with
+  ## a non-zero exit code, an OSError exception is raised. The command is
+  ## executed relative to the current source path.
+  ##
+  ## .. note:: If you need a version of `exec` that returns the exit code
+  ##   and text output of the command, you can use `system.gorgeEx
+  ##   <system.html#gorgeEx,string,string,string>`_.
   log "exec: " & command:
     if rawExec(command) != 0:
       raise newException(OSError, "FAILED: " & command)
     checkOsError()
 
 proc exec*(command: string, input: string, cache = "") {.
-  raises: [OSError], tags: [ExecIOEffect].} =
-  ## Executes an external process.
+  raises: [OSError], tags: [ExecIOEffect, WriteIOEffect].} =
+  ## Executes an external process. If the external process terminates with
+  ## a non-zero exit code, an OSError exception is raised.
+  ##
+  ## .. warning:: This version of `exec` is executed relative to the nimscript
+  ##   module path, which affects how the command resolves relative paths. Thus
+  ##   it is generally better to use `gorgeEx` directly when you need more
+  ##   control over the execution environment or when working with commands
+  ##   that deal with relative paths.
   log "exec: " & command:
-    echo staticExec(command, input, cache)
+    let (output, exitCode) = gorgeEx(command, input, cache)
+    echo output
+    if exitCode != 0:
+      raise newException(OSError, "FAILED: " & command)
 
-proc selfExec*(command: string) =
+proc selfExec*(command: string) {.
+  raises: [OSError], tags: [ExecIOEffect, WriteIOEffect].} =
   ## Executes an external command with the current nim/nimble executable.
-  ## ``Command`` must not contain the "nim " part.
+  ## `Command` must not contain the "nim " part.
   let c = selfExe() & " " & command
   log "exec: " & c:
     if rawExec(c) != 0:
@@ -269,17 +307,31 @@ proc nimcacheDir*(): string =
   ## Retrieves the location of 'nimcache'.
   builtin
 
+proc projectName*(): string =
+  ## Retrieves the name of the current project
+  builtin
+
+proc projectDir*(): string =
+  ## Retrieves the absolute directory of the current project
+  builtin
+
+proc projectPath*(): string =
+  ## Retrieves the absolute path of the current project
+  builtin
+
 proc thisDir*(): string =
-  ## Retrieves the location of the current ``nims`` script file.
+  ## Retrieves the directory of the current `nims` script file. Its path is
+  ## obtained via `currentSourcePath` (although, currently,
+  ## `currentSourcePath` resolves symlinks, unlike `thisDir`).
   builtin
 
 proc cd*(dir: string) {.raises: [OSError].} =
   ## Changes the current directory.
   ##
   ## The change is permanent for the rest of the execution, since this is just
-  ## a shortcut for `os.setCurrentDir()
-  ## <http://nim-lang.org/docs/os.html#setCurrentDir,string>`_ . Use the `withDir()
-  ## <#withDir>`_ template if you want to perform a temporary change only.
+  ## a shortcut for `os.setCurrentDir() <os.html#setCurrentDir,string>`_ . Use
+  ## the `withDir() <#withDir.t,string,untyped>`_ template if you want to
+  ## perform a temporary change only.
   setCurrentDir(dir)
   checkOsError()
 
@@ -292,50 +344,88 @@ proc findExe*(bin: string): string =
 template withDir*(dir: string; body: untyped): untyped =
   ## Changes the current directory temporarily.
   ##
-  ## If you need a permanent change, use the `cd() <#cd>`_ proc. Usage example:
-  ##
-  ## .. code-block:: nim
+  ## If you need a permanent change, use the `cd() <#cd,string>`_ proc.
+  ## Usage example:
+  ##   ```nim
+  ##   # inside /some/path/
   ##   withDir "foo":
-  ##     # inside foo
-  ##   #back to last dir
-  var curDir = getCurrentDir()
+  ##     # move to /some/path/foo/
+  ##   # back in /some/path/
+  ##   ```
+  let curDir = getCurrentDir()
   try:
     cd(dir)
     body
   finally:
     cd(curDir)
 
-template `==?`(a, b: string): bool = cmpIgnoreStyle(a, b) == 0
-
 proc writeTask(name, desc: string) =
   if desc.len > 0:
     var spaces = " "
     for i in 0 ..< 20 - name.len: spaces.add ' '
     echo name, spaces, desc
 
-template task*(name: untyped; description: string; body: untyped): untyped =
-  ## Defines a task. Hidden tasks are supported via an empty description.
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##  task build, "default build is via the C backend":
-  ##    setCommand "c"
-  proc `name Task`*() = 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`()
-
 proc cppDefine*(define: string) =
-  ## tell Nim that ``define`` is a C preprocessor ``#define`` and so always
+  ## tell Nim that `define` is a C preprocessor `#define` and so always
   ## needs to be mangled.
   builtin
 
+proc stdinReadLine(): string {.
+  tags: [ReadIOEffect], raises: [IOError].} =
+  builtin
+
+proc stdinReadAll(): string {.
+  tags: [ReadIOEffect], raises: [IOError].} =
+  builtin
+
+proc readLineFromStdin*(): string {.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*(): string {.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 =
+    ## Defines a task. Hidden tasks are supported via an empty description.
+    ##
+    ## Example:
+    ##   ```nim
+    ##   task build, "default build is via the C backend":
+    ##     setCommand "c"
+    ##   ```
+    ##
+    ## For a task named `foo`, this template generates a `proc` named
+    ## `fooTask`.  This is useful if you need to call one task in
+    ## another in your Nimscript.
+    ##
+    ## Example:
+    ##
+    ##   ```nim
+    ##   task foo, "foo":        # > nim foo
+    ##     echo "Running foo"    # Running foo
+    ##
+    ##   task bar, "bar":        # > nim bar
+    ##     echo "Running bar"    # Running bar
+    ##     fooTask()             # Running foo
+    ##   ```
+    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):
+      `name Task`()
+
   # nimble has its own implementation for these things.
   var
     packageName* = ""    ## Nimble support: Set this to the package name. It
diff --git a/lib/system/orc.nim b/lib/system/orc.nim
new file mode 100644
index 000000000..c02a24989
--- /dev/null
+++ b/lib/system/orc.nim
@@ -0,0 +1,543 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# Cycle collector based on
+# https://www.cs.purdue.edu/homes/hosking/690M/Bacon01Concurrent.pdf
+# And ideas from Lins' in 2008 by the notion of "critical links", see
+# "Cyclic reference counting" by Rafael Dueire Lins
+# R.D. Lins / Information Processing Letters 109 (2008) 71–78
+#
+
+include cellseqs_v2
+
+const
+  colBlack = 0b000
+  colGray = 0b001
+  colWhite = 0b010
+  maybeCycle = 0b100 # possibly part of a cycle; this has to be a "sticky" bit
+  jumpStackFlag = 0b1000
+  colorMask = 0b011
+
+  logOrc = defined(nimArcIds)
+
+type
+  TraceProc = proc (p, env: pointer) {.nimcall, benign.}
+  DisposeProc = proc (p: pointer) {.nimcall, benign.}
+
+template color(c): untyped = c.rc and colorMask
+template setColor(c, col) =
+  when col == colBlack:
+    c.rc = c.rc and not colorMask
+  else:
+    c.rc = c.rc and not colorMask or col
+
+const
+  optimizedOrc = false # not defined(nimOldOrc)
+# XXX Still incorrect, see tests/arc/tdestroy_in_loopcond
+
+proc nimIncRefCyclic(p: pointer; cyclic: bool) {.compilerRtl, inl.} =
+  let h = head(p)
+  inc h.rc, rcIncrement
+  when optimizedOrc:
+    if cyclic:
+      h.rc = h.rc or maybeCycle
+
+proc nimMarkCyclic(p: pointer) {.compilerRtl, inl.} =
+  when optimizedOrc:
+    if p != nil:
+      let h = head(p)
+      h.rc = h.rc or maybeCycle
+
+proc unsureAsgnRef(dest: ptr pointer, src: pointer) {.inline.} =
+  # This is only used by the old RTTI mechanism and we know
+  # that 'dest[]' is nil and needs no destruction. Which is really handy
+  # as we cannot destroy the object reliably if it's an object of unknown
+  # compile-time type.
+  dest[] = src
+  if src != nil: nimIncRefCyclic(src, true)
+
+const
+  useJumpStack = false # for thavlak the jump stack doesn't improve the performance at all
+
+type
+  GcEnv = object
+    traceStack: CellSeq[ptr pointer]
+    when useJumpStack:
+      jumpStack: CellSeq[ptr pointer]   # Lins' jump stack in order to speed up traversals
+    toFree: CellSeq[Cell]
+    freed, touched, edges, rcSum: int
+    keepThreshold: bool
+
+proc trace(s: Cell; desc: PNimTypeV2; j: var GcEnv) {.inline.} =
+  if desc.traceImpl != nil:
+    var p = s +! sizeof(RefHeader)
+    cast[TraceProc](desc.traceImpl)(p, addr(j))
+
+include threadids
+
+when logOrc or orcLeakDetector:
+  proc writeCell(msg: cstring; s: Cell; desc: PNimTypeV2) =
+    when orcLeakDetector:
+      cfprintf(cstderr, "%s %s file: %s:%ld; color: %ld; thread: %ld\n",
+        msg, desc.name, s.filename, s.line, s.color, getThreadId())
+    else:
+      cfprintf(cstderr, "%s %s %ld root index: %ld; RC: %ld; color: %ld; thread: %ld\n",
+        msg, desc.name, s.refId, s.rootIdx, s.rc shr rcShift, s.color, getThreadId())
+
+proc free(s: Cell; desc: PNimTypeV2) {.inline.} =
+  when traceCollector:
+    cprintf("[From ] %p rc %ld color %ld\n", s, s.rc shr rcShift, s.color)
+  let p = s +! sizeof(RefHeader)
+
+  when logOrc: writeCell("free", s, desc)
+
+  if desc.destructor != nil:
+    cast[DestructorProc](desc.destructor)(p)
+
+  when false:
+    cstderr.rawWrite desc.name
+    cstderr.rawWrite " "
+    if desc.destructor == nil:
+      cstderr.rawWrite "lacks dispose"
+      if desc.traceImpl != nil:
+        cstderr.rawWrite ", but has trace\n"
+      else:
+        cstderr.rawWrite ", and lacks trace\n"
+    else:
+      cstderr.rawWrite "has dispose!\n"
+
+  nimRawDispose(p, desc.align)
+
+template orcAssert(cond, msg) =
+  when logOrc:
+    if not cond:
+      cfprintf(cstderr, "[Bug!] %s\n", msg)
+      rawQuit 1
+
+when logOrc:
+  proc strstr(s, sub: cstring): cstring {.header: "<string.h>", importc.}
+
+proc nimTraceRef(q: pointer; desc: PNimTypeV2; env: pointer) {.compilerRtl, inl.} =
+  let p = cast[ptr pointer](q)
+  if p[] != nil:
+
+    orcAssert strstr(desc.name, "TType") == nil, "following a TType but it's acyclic!"
+
+    var j = cast[ptr GcEnv](env)
+    j.traceStack.add(p, desc)
+
+proc nimTraceRefDyn(q: pointer; env: pointer) {.compilerRtl, inl.} =
+  let p = cast[ptr pointer](q)
+  if p[] != nil:
+    var j = cast[ptr GcEnv](env)
+    j.traceStack.add(p, cast[ptr PNimTypeV2](p[])[])
+
+var
+  roots {.threadvar.}: CellSeq[Cell]
+
+proc unregisterCycle(s: Cell) =
+  # swap with the last element. O(1)
+  let idx = s.rootIdx-1
+  when false:
+    if idx >= roots.len or idx < 0:
+      cprintf("[Bug!] %ld %ld\n", idx, roots.len)
+      rawQuit 1
+  roots.d[idx] = roots.d[roots.len-1]
+  roots.d[idx][0].rootIdx = idx+1
+  dec roots.len
+  s.rootIdx = 0
+
+proc scanBlack(s: Cell; desc: PNimTypeV2; j: var GcEnv) =
+  #[
+  proc scanBlack(s: Cell) =
+    setColor(s, colBlack)
+    for t in sons(s):
+      t.rc = t.rc + rcIncrement
+      if t.color != colBlack:
+        scanBlack(t)
+  ]#
+  s.setColor colBlack
+  let until = j.traceStack.len
+  trace(s, desc, j)
+  when logOrc: writeCell("root still alive", s, desc)
+  while j.traceStack.len > until:
+    let (entry, desc) = j.traceStack.pop()
+    let t = head entry[]
+    inc t.rc, rcIncrement
+    if t.color != colBlack:
+      t.setColor colBlack
+      trace(t, desc, j)
+      when logOrc: writeCell("child still alive", t, desc)
+
+proc markGray(s: Cell; desc: PNimTypeV2; j: var GcEnv) =
+  #[
+  proc markGray(s: Cell) =
+    if s.color != colGray:
+      setColor(s, colGray)
+      for t in sons(s):
+        t.rc = t.rc - rcIncrement
+        if t.color != colGray:
+          markGray(t)
+  ]#
+  if s.color != colGray:
+    s.setColor colGray
+    inc j.touched
+    # keep in mind that refcounts are zero based so add 1 here:
+    inc j.rcSum, (s.rc shr rcShift) + 1
+    orcAssert(j.traceStack.len == 0, "markGray: trace stack not empty")
+    trace(s, desc, j)
+    while j.traceStack.len > 0:
+      let (entry, desc) = j.traceStack.pop()
+      let t = head entry[]
+      dec t.rc, rcIncrement
+      inc j.edges
+      when useJumpStack:
+        if (t.rc shr rcShift) >= 0 and (t.rc and jumpStackFlag) == 0:
+          t.rc = t.rc or jumpStackFlag
+          when traceCollector:
+            cprintf("[Now in jumpstack] %p %ld color %ld in jumpstack %ld\n", t, t.rc shr rcShift, t.color, t.rc and jumpStackFlag)
+          j.jumpStack.add(entry, desc)
+      if t.color != colGray:
+        t.setColor colGray
+        inc j.touched
+        # we already decremented its refcount so account for that:
+        inc j.rcSum, (t.rc shr rcShift) + 2
+        trace(t, desc, j)
+
+proc scan(s: Cell; desc: PNimTypeV2; j: var GcEnv) =
+  #[
+  proc scan(s: Cell) =
+    if s.color == colGray:
+      if s.rc > 0:
+        scanBlack(s)
+      else:
+        s.setColor(colWhite)
+        for t in sons(s): scan(t)
+  ]#
+  if s.color == colGray:
+    if (s.rc shr rcShift) >= 0:
+      scanBlack(s, desc, j)
+      # XXX this should be done according to Lins' paper but currently breaks
+      #when useJumpStack:
+      #  s.setColor colPurple
+    else:
+      when useJumpStack:
+        # first we have to repair all the nodes we have seen
+        # that are still alive; we also need to mark what they
+        # refer to as alive:
+        while j.jumpStack.len > 0:
+          let (entry, desc) = j.jumpStack.pop
+          let t = head entry[]
+          # not in jump stack anymore!
+          t.rc = t.rc and not jumpStackFlag
+          if t.color == colGray and (t.rc shr rcShift) >= 0:
+            scanBlack(t, desc, j)
+            # XXX this should be done according to Lins' paper but currently breaks
+            #t.setColor colPurple
+            when traceCollector:
+              cprintf("[jump stack] %p %ld\n", t, t.rc shr rcShift)
+
+      orcAssert(j.traceStack.len == 0, "scan: trace stack not empty")
+      s.setColor(colWhite)
+      trace(s, desc, j)
+      while j.traceStack.len > 0:
+        let (entry, desc) = j.traceStack.pop()
+        let t = head entry[]
+        if t.color == colGray:
+          if (t.rc shr rcShift) >= 0:
+            scanBlack(t, desc, j)
+          else:
+            when useJumpStack:
+              # first we have to repair all the nodes we have seen
+              # that are still alive; we also need to mark what they
+              # refer to as alive:
+              while j.jumpStack.len > 0:
+                let (entry, desc) = j.jumpStack.pop
+                let t = head entry[]
+                # not in jump stack anymore!
+                t.rc = t.rc and not jumpStackFlag
+                if t.color == colGray and (t.rc shr rcShift) >= 0:
+                  scanBlack(t, desc, j)
+                  # XXX this should be done according to Lins' paper but currently breaks
+                  #t.setColor colPurple
+                  when traceCollector:
+                    cprintf("[jump stack] %p %ld\n", t, t.rc shr rcShift)
+
+            t.setColor(colWhite)
+            trace(t, desc, j)
+
+when false:
+  proc writeCell(msg: cstring; s: Cell) =
+    cfprintf(cstderr, "%s %p root index: %ld; RC: %ld; color: %ld\n",
+      msg, s, s.rootIdx, s.rc shr rcShift, s.color)
+
+proc collectColor(s: Cell; desc: PNimTypeV2; col: int; j: var GcEnv) =
+  #[
+    was: 'collectWhite'.
+
+  proc collectWhite(s: Cell) =
+    if s.color == colWhite and not buffered(s):
+      s.setColor(colBlack)
+      for t in sons(s):
+        collectWhite(t)
+      free(s) # watch out, a bug here!
+  ]#
+  if s.color == col and s.rootIdx == 0:
+    orcAssert(j.traceStack.len == 0, "collectWhite: trace stack not empty")
+
+    s.setColor(colBlack)
+    j.toFree.add(s, desc)
+    trace(s, desc, j)
+    while j.traceStack.len > 0:
+      let (entry, desc) = j.traceStack.pop()
+      let t = head entry[]
+      entry[] = nil # ensure that the destructor does touch moribund objects!
+      if t.color == col and t.rootIdx == 0:
+        j.toFree.add(t, desc)
+        t.setColor(colBlack)
+        trace(t, desc, j)
+
+const
+  defaultThreshold = when defined(nimFixedOrc): 10_000 else: 128
+
+when defined(nimStressOrc):
+  const rootsThreshold = 10 # broken with -d:nimStressOrc: 10 and for havlak iterations 1..8
+else:
+  var rootsThreshold {.threadvar.}: int
+
+proc collectCyclesBacon(j: var GcEnv; lowMark: int) =
+  # pretty direct translation from
+  # https://researcher.watson.ibm.com/researcher/files/us-bacon/Bacon01Concurrent.pdf
+  # Fig. 2. Synchronous Cycle Collection
+  #[
+    for s in roots:
+      markGray(s)
+    for s in roots:
+      scan(s)
+    for s in roots:
+      remove s from roots
+      s.buffered = false
+      collectWhite(s)
+  ]#
+  let last = roots.len - 1
+  when logOrc:
+    for i in countdown(last, lowMark):
+      writeCell("root", roots.d[i][0], roots.d[i][1])
+
+  for i in countdown(last, lowMark):
+    markGray(roots.d[i][0], roots.d[i][1], j)
+
+  var colToCollect = colWhite
+  if j.rcSum == j.edges:
+    # short-cut: we know everything is garbage:
+    colToCollect = colGray
+    # remember the fact that we got so lucky:
+    j.keepThreshold = true
+  else:
+    for i in countdown(last, lowMark):
+      scan(roots.d[i][0], roots.d[i][1], j)
+
+  init j.toFree
+  for i in 0 ..< roots.len:
+    let s = roots.d[i][0]
+    s.rootIdx = 0
+    collectColor(s, roots.d[i][1], colToCollect, j)
+
+  # Bug #22927: `free` calls destructors which can append to `roots`.
+  # We protect against this here by setting `roots.len` to 0 and also
+  # setting the threshold so high that no cycle collection can be triggered
+  # until we are out of this critical section:
+  when not defined(nimStressOrc):
+    let oldThreshold = rootsThreshold
+    rootsThreshold = high(int)
+  roots.len = 0
+
+  for i in 0 ..< j.toFree.len:
+    when orcLeakDetector:
+      writeCell("CYCLIC OBJECT FREED", j.toFree.d[i][0], j.toFree.d[i][1])
+    free(j.toFree.d[i][0], j.toFree.d[i][1])
+
+  when not defined(nimStressOrc):
+    rootsThreshold = oldThreshold
+
+  inc j.freed, j.toFree.len
+  deinit j.toFree
+
+when defined(nimOrcStats):
+  var freedCyclicObjects {.threadvar.}: int
+
+proc partialCollect(lowMark: int) =
+  when false:
+    if roots.len < 10 + lowMark: return
+  when logOrc:
+    cfprintf(cstderr, "[partialCollect] begin\n")
+  var j: GcEnv
+  init j.traceStack
+  collectCyclesBacon(j, lowMark)
+  when logOrc:
+    cfprintf(cstderr, "[partialCollect] end; freed %ld touched: %ld work: %ld\n", j.freed, j.touched,
+      roots.len - lowMark)
+  roots.len = lowMark
+  deinit j.traceStack
+  when defined(nimOrcStats):
+    inc freedCyclicObjects, j.freed
+
+proc collectCycles() =
+  ## Collect cycles.
+  when logOrc:
+    cfprintf(cstderr, "[collectCycles] begin\n")
+
+  var j: GcEnv
+  init j.traceStack
+  when useJumpStack:
+    init j.jumpStack
+    collectCyclesBacon(j, 0)
+    while j.jumpStack.len > 0:
+      let (t, desc) = j.jumpStack.pop
+      # not in jump stack anymore!
+      t.rc = t.rc and not jumpStackFlag
+    deinit j.jumpStack
+  else:
+    collectCyclesBacon(j, 0)
+
+  deinit j.traceStack
+  if roots.len == 0:
+    deinit roots
+
+  when not defined(nimStressOrc):
+    # compute the threshold based on the previous history
+    # of the cycle collector's effectiveness:
+    # we're effective when we collected 50% or more of the nodes
+    # we touched. If we're effective, we can reset the threshold:
+    if j.keepThreshold:
+      discard
+    elif j.freed * 2 >= j.touched:
+      when not defined(nimFixedOrc):
+        rootsThreshold = max(rootsThreshold div 3 * 2, 16)
+      else:
+        rootsThreshold = 0
+      #cfprintf(cstderr, "[collectCycles] freed %ld, touched %ld new threshold %ld\n", j.freed, j.touched, rootsThreshold)
+    elif rootsThreshold < high(int) div 4:
+      rootsThreshold = (if rootsThreshold <= 0: defaultThreshold else: rootsThreshold) * 3 div 2
+  when logOrc:
+    cfprintf(cstderr, "[collectCycles] end; freed %ld new threshold %ld touched: %ld mem: %ld rcSum: %ld edges: %ld\n", j.freed, rootsThreshold, j.touched,
+      getOccupiedMem(), j.rcSum, j.edges)
+  when defined(nimOrcStats):
+    inc freedCyclicObjects, j.freed
+
+when defined(nimOrcStats):
+  type
+    OrcStats* = object ## Statistics of the cycle collector subsystem.
+      freedCyclicObjects*: int ## Number of freed cyclic objects.
+  proc GC_orcStats*(): OrcStats =
+    ## Returns the statistics of the cycle collector subsystem.
+    result = OrcStats(freedCyclicObjects: freedCyclicObjects)
+
+proc registerCycle(s: Cell; desc: PNimTypeV2) =
+  s.rootIdx = roots.len+1
+  if roots.d == nil: init(roots)
+  add(roots, s, desc)
+
+  if roots.len - defaultThreshold >= rootsThreshold:
+    collectCycles()
+  when logOrc:
+    writeCell("[added root]", s, desc)
+
+  orcAssert strstr(desc.name, "TType") == nil, "added a TType as a root!"
+
+proc GC_runOrc* =
+  ## Forces a cycle collection pass.
+  collectCycles()
+  orcAssert roots.len == 0, "roots not empty!"
+
+proc GC_enableOrc*() =
+  ## Enables the cycle collector subsystem of `--mm:orc`. This is a `--mm:orc`
+  ## specific API. Check with `when defined(gcOrc)` for its existence.
+  when not defined(nimStressOrc):
+    rootsThreshold = 0
+
+proc GC_disableOrc*() =
+  ## Disables the cycle collector subsystem of `--mm:orc`. This is a `--mm:orc`
+  ## specific API. Check with `when defined(gcOrc)` for its existence.
+  when not defined(nimStressOrc):
+    rootsThreshold = high(int)
+
+proc GC_prepareOrc*(): int {.inline.} = roots.len
+
+proc GC_partialCollect*(limit: int) =
+  partialCollect(limit)
+
+proc GC_fullCollect* =
+  ## Forces a full garbage collection pass. With `--mm:orc` triggers the cycle
+  ## collector. This is an alias for `GC_runOrc`.
+  collectCycles()
+
+proc GC_enableMarkAndSweep*() =
+  ## For `--mm:orc` an alias for `GC_enableOrc`.
+  GC_enableOrc()
+
+proc GC_disableMarkAndSweep*() =
+  ## For `--mm:orc` an alias for `GC_disableOrc`.
+  GC_disableOrc()
+
+const
+  acyclicFlag = 1 # see also cggtypes.nim, proc genTypeInfoV2Impl
+
+when optimizedOrc:
+  template markedAsCyclic(s: Cell; desc: PNimTypeV2): bool =
+    (desc.flags and acyclicFlag) == 0 and (s.rc and maybeCycle) != 0
+else:
+  template markedAsCyclic(s: Cell; desc: PNimTypeV2): bool =
+    (desc.flags and acyclicFlag) == 0
+
+proc rememberCycle(isDestroyAction: bool; s: Cell; desc: PNimTypeV2) {.noinline.} =
+  if isDestroyAction:
+    if s.rootIdx > 0:
+      unregisterCycle(s)
+  else:
+    # do not call 'rememberCycle' again unless this cell
+    # got an 'incRef' event:
+    if s.rootIdx == 0 and markedAsCyclic(s, desc):
+      s.setColor colBlack
+      registerCycle(s, desc)
+
+proc nimDecRefIsLastCyclicDyn(p: pointer): bool {.compilerRtl, inl.} =
+  if p != nil:
+    var cell = head(p)
+    if (cell.rc and not rcMask) == 0:
+      result = true
+      #cprintf("[DESTROY] %p\n", p)
+    else:
+      dec cell.rc, rcIncrement
+    #if cell.color == colPurple:
+    rememberCycle(result, cell, cast[ptr PNimTypeV2](p)[])
+
+proc nimDecRefIsLastDyn(p: pointer): bool {.compilerRtl, inl.} =
+  if p != nil:
+    var cell = head(p)
+    if (cell.rc and not rcMask) == 0:
+      result = true
+      #cprintf("[DESTROY] %p\n", p)
+    else:
+      dec cell.rc, rcIncrement
+    #if cell.color == colPurple:
+    if result:
+      if cell.rootIdx > 0:
+        unregisterCycle(cell)
+
+proc nimDecRefIsLastCyclicStatic(p: pointer; desc: PNimTypeV2): bool {.compilerRtl, inl.} =
+  if p != nil:
+    var cell = head(p)
+    if (cell.rc and not rcMask) == 0:
+      result = true
+      #cprintf("[DESTROY] %p %s\n", p, desc.name)
+    else:
+      dec cell.rc, rcIncrement
+    #if cell.color == colPurple:
+    rememberCycle(result, cell, desc)
diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim
index a63eadf8e..5509d0070 100644
--- a/lib/system/osalloc.nim
+++ b/lib/system/osalloc.nim
@@ -28,7 +28,30 @@ const doNotUnmap = not (defined(amd64) or defined(i386)) or
                    defined(windows) or defined(nimAllocNoUnmap)
 
 
-when defined(emscripten):
+when defined(nimAllocPagesViaMalloc):
+  when not defined(gcArc) and not defined(gcOrc) and not defined(gcAtomicArc):
+    {.error: "-d:nimAllocPagesViaMalloc is only supported with --mm:arc or --mm:atomicArc or --mm:orc".}
+
+  proc osTryAllocPages(size: int): pointer {.inline.} =
+    let base = c_malloc(csize_t size + PageSize - 1 + sizeof(uint32))
+    if base == nil: raiseOutOfMem()
+    # memory layout: padding + offset (4 bytes) + user_data
+    # in order to deallocate: read offset at user_data - 4 bytes,
+    # then deallocate user_data - offset
+    let offset = PageSize - (cast[int](base) and (PageSize - 1))
+    cast[ptr uint32](base +! (offset - sizeof(uint32)))[] = uint32(offset)
+    result = base +! offset
+
+  proc osAllocPages(size: int): pointer {.inline.} =
+    result = osTryAllocPages(size)
+    if result == nil: raiseOutOfMem()
+
+  proc osDeallocPages(p: pointer, size: int) {.inline.} =
+    # read offset at p - 4 bytes, then deallocate (p - offset) pointer
+    let offset = cast[ptr uint32](p -! sizeof(uint32))[]
+    c_free(p -! offset)
+
+elif defined(emscripten) and not defined(StandaloneHeapSize):
   const
     PROT_READ  = 1             # page can be read
     PROT_WRITE = 2             # page can be written
@@ -57,12 +80,12 @@ when defined(emscripten):
     let pos = cast[int](result)
 
     # Convert pointer to PageSize correct one.
-    var new_pos = cast[ByteAddress](pos) +% (PageSize - (pos %% PageSize))
+    var new_pos = cast[int](pos) +% (PageSize - (pos %% PageSize))
     if (new_pos-pos) < sizeof(EmscriptenMMapBlock):
       new_pos = new_pos +% PageSize
     result = cast[pointer](new_pos)
 
-    var mmapDescrPos = cast[ByteAddress](result) -% sizeof(EmscriptenMMapBlock)
+    var mmapDescrPos = cast[int](result) -% sizeof(EmscriptenMMapBlock)
 
     var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos)
     mmapDescr.realSize = realSize
@@ -73,19 +96,25 @@ when defined(emscripten):
   proc osTryAllocPages(size: int): pointer = osAllocPages(size)
 
   proc osDeallocPages(p: pointer, size: int) {.inline.} =
-    var mmapDescrPos = cast[ByteAddress](p) -% sizeof(EmscriptenMMapBlock)
+    var mmapDescrPos = cast[int](p) -% sizeof(EmscriptenMMapBlock)
     var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos)
     munmap(mmapDescr.realPointer, mmapDescr.realSize)
 
-elif defined(genode):
+elif defined(genode) and not defined(StandaloneHeapSize):
   include genode/alloc # osAllocPages, osTryAllocPages, osDeallocPages
 
-elif defined(posix):
+elif defined(posix) and not defined(StandaloneHeapSize):
   const
     PROT_READ  = 1             # page can be read
     PROT_WRITE = 2             # page can be written
 
-  when defined(macosx) or defined(bsd):
+  when defined(netbsd) or defined(openbsd):
+    # OpenBSD security for setjmp/longjmp coroutines
+    var MAP_STACK {.importc: "MAP_STACK", header: "<sys/mman.h>".}: cint
+  else:
+    const MAP_STACK = 0             # avoid sideeffects
+
+  when defined(macosx) or defined(freebsd):
     const MAP_ANONYMOUS = 0x1000
     const MAP_PRIVATE = 0x02        # Changes are private
   elif defined(solaris):
@@ -96,31 +125,34 @@ elif defined(posix):
     # some arches like mips and alpha use different values
     const MAP_ANONYMOUS = 0x20
     const MAP_PRIVATE = 0x02        # Changes are private
-  else:
+  elif defined(haiku):
+    const MAP_ANONYMOUS = 0x08
+    const MAP_PRIVATE = 0x02
+  else:  # posix including netbsd or openbsd
     var
       MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
       MAP_PRIVATE {.importc: "MAP_PRIVATE", header: "<sys/mman.h>".}: cint
 
-  proc mmap(adr: pointer, len: csize, prot, flags, fildes: cint,
+  proc mmap(adr: pointer, len: csize_t, prot, flags, fildes: cint,
             off: int): pointer {.header: "<sys/mman.h>".}
 
-  proc munmap(adr: pointer, len: csize): cint {.header: "<sys/mman.h>".}
+  proc munmap(adr: pointer, len: csize_t): cint {.header: "<sys/mman.h>".}
 
   proc osAllocPages(size: int): pointer {.inline.} =
-    result = mmap(nil, size, PROT_READ or PROT_WRITE,
-                             MAP_PRIVATE or MAP_ANONYMOUS, -1, 0)
+    result = mmap(nil, cast[csize_t](size), PROT_READ or PROT_WRITE,
+                             MAP_ANONYMOUS or MAP_PRIVATE or MAP_STACK, -1, 0)
     if result == nil or result == cast[pointer](-1):
       raiseOutOfMem()
 
   proc osTryAllocPages(size: int): pointer {.inline.} =
-    result = mmap(nil, size, PROT_READ or PROT_WRITE,
-                             MAP_PRIVATE or MAP_ANONYMOUS, -1, 0)
+    result = mmap(nil, cast[csize_t](size), PROT_READ or PROT_WRITE,
+                             MAP_ANONYMOUS or MAP_PRIVATE or MAP_STACK, -1, 0)
     if result == cast[pointer](-1): result = nil
 
   proc osDeallocPages(p: pointer, size: int) {.inline.} =
-    when reallyOsDealloc: discard munmap(p, size)
+    when reallyOsDealloc: discard munmap(p, cast[csize_t](size))
 
-elif defined(windows):
+elif defined(windows) and not defined(StandaloneHeapSize):
   const
     MEM_RESERVE = 0x2000
     MEM_COMMIT = 0x1000
@@ -157,13 +189,13 @@ elif defined(windows):
     when reallyOsDealloc:
       if virtualFree(p, 0, MEM_RELEASE) == 0:
         cprintf "virtualFree failing!"
-        quit 1
+        rawQuit 1
     #VirtualFree(p, size, MEM_DECOMMIT)
 
-elif hostOS == "standalone":
+elif hostOS == "standalone" or defined(StandaloneHeapSize):
   const StandaloneHeapSize {.intdefine.}: int = 1024 * PageSize
   var
-    theHeap: array[StandaloneHeapSize, float64] # 'float64' for alignment
+    theHeap: array[StandaloneHeapSize div sizeof(float64), float64] # 'float64' for alignment
     bumpPointer = cast[int](addr theHeap)
 
   proc osAllocPages(size: int): pointer {.inline.} =
@@ -181,5 +213,6 @@ elif hostOS == "standalone":
   proc osDeallocPages(p: pointer, size: int) {.inline.} =
     if bumpPointer-size == cast[int](p):
       dec bumpPointer, size
+
 else:
   {.error: "Port memory manager to your platform".}
diff --git a/lib/system/platforms.nim b/lib/system/platforms.nim
index b561cd3ba..0619f3fca 100644
--- a/lib/system/platforms.nim
+++ b/lib/system/platforms.nim
@@ -10,6 +10,8 @@
 ## Platform detection for NimScript. This module is included by the system module!
 ## Do not import it directly!
 
+# CPU architectures have alias names mapped in tools/niminst/makefile.nimf
+
 type
   CpuPlatform* {.pure.} = enum ## the CPU this program will run on.
     none,                      ## unknown CPU
@@ -20,6 +22,8 @@ type
     powerpc64,                 ## 64 bit PowerPC
     powerpc64el,               ## Little Endian 64 bit PowerPC
     sparc,                     ## Sparc based processor
+    sparc64,                   ## 64-bit Sparc based processor
+    hppa,                      ## HP PA-RISC
     ia64,                      ## Intel Itanium
     amd64,                     ## x86_64 (AMD64); 64 bit x86 compatible CPU
     mips,                      ## Mips based processor
@@ -31,13 +35,17 @@ type
     vm,                        ## Some Virtual machine: Nim's VM or JavaScript
     avr,                       ## AVR based processor
     msp430,                    ## TI MSP430 microcontroller
-    riscv64                    ## RISC-V 64-bit processor
+    riscv32,                   ## RISC-V 32-bit processor
+    riscv64,                   ## RISC-V 64-bit processor
+    wasm32,                    ## WASM, 32-bit
+    e2k,                       ## MCST Elbrus 2000
+    loongarch64,               ## LoongArch 64-bit processor
+    s390x                      ## IBM Z
 
   OsPlatform* {.pure.} = enum ## the OS this program will run on.
     none, dos, windows, os2, linux, morphos, skyos, solaris,
     irix, netbsd, freebsd, openbsd, aix, palmos, qnx, amiga,
-    atari, netware, macos, macosx, haiku, android, js, nimVM,
-    standalone
+    atari, netware, macos, macosx, haiku, android, js, standalone, nintendoswitch
 
 const
   targetOS* = when defined(windows): OsPlatform.windows
@@ -62,8 +70,8 @@ const
               elif defined(haiku): OsPlatform.haiku
               elif defined(android): OsPlatform.android
               elif defined(js): OsPlatform.js
-              elif defined(nimVM): OsPlatform.nimVM
               elif defined(standalone): OsPlatform.standalone
+              elif defined(nintendoswitch): OsPlatform.nintendoswitch
               else: OsPlatform.none
     ## the OS this program will run on.
 
@@ -74,6 +82,8 @@ const
                elif defined(powerpc64): CpuPlatform.powerpc64
                elif defined(powerpc64el): CpuPlatform.powerpc64el
                elif defined(sparc): CpuPlatform.sparc
+               elif defined(sparc64): CpuPlatform.sparc64
+               elif defined(hppa): CpuPlatform.hppa
                elif defined(ia64): CpuPlatform.ia64
                elif defined(amd64): CpuPlatform.amd64
                elif defined(mips): CpuPlatform.mips
@@ -85,6 +95,11 @@ const
                elif defined(vm): CpuPlatform.vm
                elif defined(avr): CpuPlatform.avr
                elif defined(msp430): CpuPlatform.msp430
+               elif defined(riscv32): CpuPlatform.riscv32
                elif defined(riscv64): CpuPlatform.riscv64
+               elif defined(wasm32): CpuPlatform.wasm32
+               elif defined(e2k): CpuPlatform.e2k
+               elif defined(loongarch64): CpuPlatform.loongarch64
+               elif defined(s390x): CpuPlatform.s390x
                else: CpuPlatform.none
     ## the CPU this program will run on.
diff --git a/lib/system/profiler.nim b/lib/system/profiler.nim
index 7146500d9..e7eb6ac82 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
@@ -23,7 +26,6 @@ type
     lines*: array[0..MaxTraceLen-1, cstring]
     files*: array[0..MaxTraceLen-1, cstring]
   ProfilerHook* = proc (st: StackTrace) {.nimcall.}
-{.deprecated: [TStackTrace: StackTrace, TProfilerHook: ProfilerHook].}
 
 proc `[]`*(st: StackTrace, i: int): cstring = st.lines[i]
 
@@ -58,13 +60,13 @@ proc captureStackTrace(f: PFrame, st: var StackTrace) =
     b = b.prev
 
 var
-  profilingRequestedHook*: proc (): bool {.nimcall, benign.}
+  profilingRequestedHook*: proc (): bool {.nimcall, 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, gcsafe.}
 
   var
     profilerHook*: MemProfilerHook
@@ -88,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/rawquits.nim b/lib/system/rawquits.nim
new file mode 100644
index 000000000..f0ead10c6
--- /dev/null
+++ b/lib/system/rawquits.nim
@@ -0,0 +1,27 @@
+import system/ctypes
+
+when defined(nimNoQuit):
+  proc rawQuit(errorcode: int = QuitSuccess) = discard "ignoring quit"
+
+elif defined(genode):
+  import genode/env
+
+  var systemEnv {.exportc: runtimeEnvSym.}: GenodeEnvPtr
+
+  type GenodeEnv = GenodeEnvPtr
+    ## Opaque type representing Genode environment.
+
+  proc rawQuit(env: GenodeEnv; errorcode: int) {.magic: "Exit", noreturn,
+    importcpp: "#->parent().exit(@); Genode::sleep_forever()", header: "<base/sleep.h>".}
+
+  proc rawQuit(errorcode: int = QuitSuccess) {.inline, noreturn.} =
+    systemEnv.rawQuit(errorcode)
+
+
+elif defined(js) and defined(nodejs) and not defined(nimscript):
+  proc rawQuit(errorcode: int = QuitSuccess) {.magic: "Exit",
+    importc: "process.exit", noreturn.}
+
+else:
+  proc rawQuit(errorcode: cint) {.
+    magic: "Exit", importc: "exit", header: "<stdlib.h>", noreturn.}
\ No newline at end of file
diff --git a/lib/system/repr.nim b/lib/system/repr.nim
index 982b07467..13118e40b 100644
--- a/lib/system/repr.nim
+++ b/lib/system/repr.nim
@@ -16,38 +16,17 @@ proc reprInt(x: int64): string {.compilerproc.} = return $x
 proc reprFloat(x: float): string {.compilerproc.} = return $x
 
 proc reprPointer(x: pointer): string {.compilerproc.} =
-  when defined(nimNoArrayToCstringConversion):
-    result = newString(60)
-    let n = c_sprintf(addr result[0], "%p", x)
-    setLen(result, n)
-  else:
-    var buf: array[0..59, char]
-    discard c_sprintf(buf, "%p", x)
-    return $buf
-
-proc `$`(x: uint64): string =
-  if x == 0:
-    result = "0"
-  else:
-    result = newString(60)
-    var i = 0
-    var n = x
-    while n != 0:
-      let nn = n div 10'u64
-      result[i] = char(n - 10'u64 * nn + ord('0'))
-      inc i
-      n = nn
-    result.setLen i
-
-    let half = i div 2
-    # Reverse
-    for t in 0 .. half-1: swap(result[t], result[i-t-1])
+  result = newString(60)
+  let n = c_snprintf(cast[cstring](addr result[0]), csize_t(60), "%p", x)
+  setLen(result, n)
 
 proc reprStrAux(result: var string, s: cstring; len: int) =
   if cast[pointer](s) == nil:
     add result, "nil"
     return
-  add result, reprPointer(cast[pointer](s)) & "\""
+  if len > 0:
+    add result, reprPointer(cast[pointer](s))
+  add result, "\""
   for i in 0 .. pred(len):
     let c = s[i]
     case c
@@ -93,8 +72,10 @@ proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
 
   result = $e & " (invalid data!)"
 
+include system/repr_impl
+
 type
-  PByteArray = ptr array[0xffff, int8]
+  PByteArray = ptr UncheckedArray[byte] # array[0xffff, byte]
 
 proc addSetElem(result: var string, elem: int, typ: PNimType) {.benign.} =
   case typ.kind
@@ -111,22 +92,23 @@ proc reprSetAux(result: var string, p: pointer, typ: PNimType) =
   var elemCounter = 0  # we need this flag for adding the comma at
                        # the right places
   add result, "{"
-  var u: int64
+  var u: uint64
   case typ.size
-  of 1: u = ze64(cast[ptr int8](p)[])
-  of 2: u = ze64(cast[ptr int16](p)[])
-  of 4: u = ze64(cast[ptr int32](p)[])
-  of 8: u = cast[ptr int64](p)[]
+  of 1: u = cast[ptr uint8](p)[]
+  of 2: u = cast[ptr uint16](p)[]
+  of 4: u = cast[ptr uint32](p)[]
+  of 8: u = cast[ptr uint64](p)[]
   else:
+    u = uint64(0)
     var a = cast[PByteArray](p)
     for i in 0 .. typ.size*8-1:
-      if (ze(a[i div 8]) and (1 shl (i mod 8))) != 0:
+      if (uint(a[i shr 3]) and (1'u shl (i and 7))) != 0:
         if elemCounter > 0: add result, ", "
         addSetElem(result, i+typ.node.len, typ.base)
         inc(elemCounter)
   if typ.size <= 8:
     for i in 0..sizeof(int64)*8-1:
-      if (u and (1'i64 shl int64(i))) != 0'i64:
+      if (u and (1'u64 shl uint64(i))) != 0'u64:
         if elemCounter > 0: add result, ", "
         addSetElem(result, i+typ.node.len, typ.base)
         inc(elemCounter)
@@ -143,7 +125,6 @@ type
       marked: CellSet
     recdepth: int       # do not recurse endlessly
     indent: int         # indentation
-{.deprecated: [TReprClosure: ReprClosure].}
 
 when not defined(useNimRtl):
   proc initReprClosure(cl: var ReprClosure) =
@@ -174,19 +155,35 @@ when not defined(useNimRtl):
     var bs = typ.base.size
     for i in 0..typ.size div bs - 1:
       if i > 0: add result, ", "
-      reprAux(result, cast[pointer](cast[ByteAddress](p) + i*bs), typ.base, cl)
+      reprAux(result, cast[pointer](cast[int](p) + i*bs), typ.base, cl)
     add result, "]"
 
+  when defined(nimSeqsV2):
+    type
+      GenericSeq = object
+        len: int
+        p: pointer
+      PGenericSeq = ptr GenericSeq
+    const payloadOffset = sizeof(int) + sizeof(pointer)
+      # see seqs.nim:    cap: int
+      #                  region: Allocator
+
+    template payloadPtr(x: untyped): untyped = cast[PGenericSeq](x).p
+  else:
+    const payloadOffset = GenericSeqSize ## the payload offset always depends on the alignment of the member type.
+    template payloadPtr(x: untyped): untyped = x
+
   proc reprSequence(result: var string, p: pointer, typ: PNimType,
                     cl: var ReprClosure) =
     if p == nil:
-      add result, "nil"
+      add result, "[]"
       return
-    result.add(reprPointer(p) & "[")
+    result.add(reprPointer(p))
+    result.add "@["
     var bs = typ.base.size
     for i in 0..cast[PGenericSeq](p).len-1:
       if i > 0: add result, ", "
-      reprAux(result, cast[pointer](cast[ByteAddress](p) + GenericSeqSize + i*bs),
+      reprAux(result, cast[pointer](cast[int](payloadPtr(p)) + align(payloadOffset, typ.align) + i*bs),
               typ.base, cl)
     add result, "]"
 
@@ -197,14 +194,14 @@ when not defined(useNimRtl):
     of nkSlot:
       add result, $n.name
       add result, " = "
-      reprAux(result, cast[pointer](cast[ByteAddress](p) + n.offset), n.typ, cl)
+      reprAux(result, cast[pointer](cast[int](p) + n.offset), n.typ, cl)
     of nkList:
       for i in 0..n.len-1:
         if i > 0: add result, ",\n"
         reprRecordAux(result, p, n.sons[i], cl)
     of nkCase:
       var m = selectBranch(p, n)
-      reprAux(result, cast[pointer](cast[ByteAddress](p) + n.offset), n.typ, cl)
+      reprAux(result, cast[pointer](cast[int](p) + n.offset), n.typ, cl)
       if m != nil: reprRecordAux(result, p, m, cl)
 
   proc reprRecord(result: var string, p: pointer, typ: PNimType,
@@ -227,11 +224,12 @@ when not defined(useNimRtl):
                cl: var ReprClosure) =
     # we know that p is not nil here:
     when declared(CellSet):
-      when defined(boehmGC) or defined(gogc) or defined(nogc):
+      when defined(boehmGC) or defined(gogc) or defined(nogc) or usesDestructors:
         var cell = cast[PCell](p)
       else:
         var cell = usrToCell(p)
-      add result, "ref " & reprPointer(p)
+      add result, if typ.kind == tyPtr: "ptr " else: "ref "
+      add result, reprPointer(p)
       if cell notin cl.marked:
         # only the address is shown:
         incl(cl.marked, cell)
@@ -284,8 +282,8 @@ when not defined(useNimRtl):
     of tyChar: add result, reprChar(cast[ptr char](p)[])
     of tyString:
       let sp = cast[ptr string](p)
-      reprStrAux(result, if sp[].isNil: nil else: sp[].cstring, sp[].len)
-    of tyCString:
+      reprStrAux(result, sp[].cstring, sp[].len)
+    of tyCstring:
       let cs = cast[ptr cstring](p)[]
       if cs.isNil: add result, "nil"
       else: reprStrAux(result, cs, cs.len)
@@ -293,22 +291,25 @@ when not defined(useNimRtl):
     of tyProc, tyPointer:
       if cast[PPointer](p)[] == nil: add result, "nil"
       else: add result, reprPointer(cast[PPointer](p)[])
+    of tyUncheckedArray:
+      add result, "[...]"
     else:
       add result, "(invalid data!)"
     inc(cl.recdepth)
 
-proc reprOpenArray(p: pointer, length: int, elemtyp: PNimType): string {.
-                   compilerRtl.} =
-  var
-    cl: ReprClosure
-  initReprClosure(cl)
-  result = "["
-  var bs = elemtyp.size
-  for i in 0..length - 1:
-    if i > 0: add result, ", "
-    reprAux(result, cast[pointer](cast[ByteAddress](p) + i*bs), elemtyp, cl)
-  add result, "]"
-  deinitReprClosure(cl)
+when not defined(useNimRtl):
+  proc reprOpenArray(p: pointer, length: int, elemtyp: PNimType): string {.
+                     compilerRtl.} =
+    var
+      cl: ReprClosure
+    initReprClosure(cl)
+    result = "["
+    var bs = elemtyp.size
+    for i in 0..length - 1:
+      if i > 0: add result, ", "
+      reprAux(result, cast[pointer](cast[int](p) + i*bs), elemtyp, cl)
+    add result, "]"
+    deinitReprClosure(cl)
 
 when not defined(useNimRtl):
   proc reprAny(p: pointer, typ: PNimType): string =
@@ -321,5 +322,6 @@ when not defined(useNimRtl):
     else:
       var p = p
       reprAux(result, addr(p), typ, cl)
-    add result, "\n"
+    when defined(nimLegacyReprWithNewline): # see PR #16034
+      add result, "\n"
     deinitReprClosure(cl)
diff --git a/lib/system/repr_impl.nim b/lib/system/repr_impl.nim
new file mode 100644
index 000000000..b9ec1890f
--- /dev/null
+++ b/lib/system/repr_impl.nim
@@ -0,0 +1,15 @@
+#[
+other APIs common to system/repr and system/reprjs could be refactored here, eg:
+* reprChar
+* reprBool
+* reprStr
+
+Another possibility in future work would be to have a single include file instead
+of system/repr and system/reprjs, and use `when defined(js)` inside it.
+]#
+
+proc reprDiscriminant*(e: int, typ: PNimType): string {.compilerRtl.} =
+  case typ.kind
+  of tyEnum: reprEnum(e, typ)
+  of tyBool: $(e != 0)
+  else: $e
diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim
new file mode 100644
index 000000000..d2aef536c
--- /dev/null
+++ b/lib/system/repr_v2.nim
@@ -0,0 +1,194 @@
+include system/inclrtl
+
+when defined(nimPreviewSlimSystem):
+  import std/formatfloat
+
+proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".}
+  ## imported from typetraits
+
+proc distinctBase(T: typedesc, recursive: static bool = true): typedesc {.magic: "TypeTrait".}
+  ## imported from typetraits
+
+proc rangeBase(T: typedesc): typedesc {.magic: "TypeTrait".}
+  # skip one level of range; return the base type of a range type
+
+proc repr*(x: NimNode): string {.magic: "Repr", noSideEffect.}
+
+proc repr*(x: int): string =
+  ## Same as $x
+  $x
+
+proc repr*(x: int64): string =
+  ## Same as $x
+  $x
+
+proc repr*(x: uint64): string {.noSideEffect.} =
+  ## Same as $x
+  $x
+
+proc repr*(x: float): string =
+  ## Same as $x
+  $x
+
+proc repr*(x: bool): string {.magic: "BoolToStr", noSideEffect.}
+  ## repr for a boolean argument. Returns `x`
+  ## converted to the string "false" or "true".
+
+proc repr*(x: char): string {.noSideEffect, raises: [].} =
+  ## repr for a character argument. Returns `x`
+  ## converted to an escaped string.
+  ##
+  ##   ```Nim
+  ##   assert repr('c') == "'c'"
+  ##   ```
+  result = "'"
+  # Elides string creations if not needed
+  if x in {'\\', '\0'..'\31', '\127'..'\255'}:
+    result.add '\\'
+  if x in {'\0'..'\31', '\127'..'\255'}:
+    result.add $x.uint8
+  else:
+    result.add x
+  result.add '\''
+
+proc repr*(x: string | cstring): string {.noSideEffect, raises: [].} =
+  ## repr for a string argument. Returns `x`
+  ## converted to a quoted and escaped string.
+  result = "\""
+  for i in 0..<x.len:
+    if x[i] in {'"', '\\', '\0'..'\31', '\127'..'\255'}:
+      result.add '\\'
+    case x[i]:
+    of '\n':
+      result.add "n\n"
+    of '\0'..'\9', '\11'..'\31', '\127'..'\255':
+      result.add $x[i].uint8
+    else:
+      result.add x[i]
+  result.add '\"'
+
+proc repr*[Enum: enum](x: Enum): string {.magic: "EnumToStr", noSideEffect, raises: [].}
+  ## repr for an enumeration argument. This works for
+  ## any enumeration type thanks to compiler magic.
+  ##
+  ## If a `repr` operator for a concrete enumeration is provided, this is
+  ## used instead. (In other words: *Overwriting* is possible.)
+
+proc reprDiscriminant*(e: int): string {.compilerproc.} =
+  # repr and reprjs can use `PNimType` to symbolize `e`; making this work here
+  # would require a way to pass the set of enum stringified values to cgen.
+  $e
+
+proc repr*(p: pointer): string =
+  ## repr of pointer as its hexadecimal value
+  if p == nil:
+    result = "nil"
+  else:
+    when nimvm:
+      result = "ptr"
+    else:
+      const HexChars = "0123456789ABCDEF"
+      const len = sizeof(pointer) * 2
+      var n = cast[uint](p)
+      result = newString(len)
+      for j in countdown(len-1, 0):
+        result[j] = HexChars[n and 0xF]
+        n = n shr 4
+
+proc repr*(p: proc | iterator {.closure.}): string =
+  ## repr of a proc as its address
+  repr(cast[ptr pointer](unsafeAddr p)[])
+
+template repr*[T: distinct|(range and not enum)](x: T): string =
+  when T is range: # add a branch to handle range
+    repr(rangeBase(typeof(x))(x))
+  elif T is distinct:
+    repr(distinctBase(typeof(x))(x))
+  else:
+    {.error: "cannot happen".}
+
+template repr*(t: typedesc): string = $t
+
+proc reprObject[T: tuple|object](res: var string, x: T) {.noSideEffect, raises: [].} =
+  res.add '('
+  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: res.add(", ")
+    when isNamed:
+      res.add(name)
+      res.add(": ")
+    else:
+      count.inc
+    res.add repr(value)
+    firstElement = false
+  when not isNamed:
+    if count == 1:
+      res.add(',') # $(1,) should print as the semantically legal (1,)
+  res.add(')')
+
+
+proc repr*[T: tuple|object](x: T): string {.noSideEffect, raises: [].} =
+  ## Generic `repr` operator for tuples that is lifted from the components
+  ## of `x`. Example:
+  ##   ```Nim
+  ##   $(23, 45) == "(23, 45)"
+  ##   $(a: 23, b: 45) == "(a: 23, b: 45)"
+  ##   $() == "()"
+  ##   ```
+  when T is object:
+    result = $typeof(x)
+  reprObject(result, x)
+
+proc repr*[T](x: ref T | ptr T): string {.noSideEffect, raises: [].} =
+  if isNil(x): return "nil"
+  when T is object:
+    result = $typeof(x)
+    reprObject(result, x[])
+  else:
+    result = when typeof(x) is ref: "ref " else: "ptr "
+    result.add repr(x[])
+
+proc collectionToRepr[T](x: T, prefix, separator, suffix: string): string {.noSideEffect, raises: [].} =
+  result = prefix
+  var firstElement = true
+  for value in items(x):
+    if firstElement:
+      firstElement = false
+    else:
+      result.add(separator)
+    result.add repr(value)
+  result.add(suffix)
+
+proc repr*[T](x: set[T]): string =
+  ## Generic `repr` operator for sets that is lifted from the components
+  ## of `x`. Example:
+  ##   ```Nim
+  ##   ${23, 45} == "{23, 45}"
+  ##   ```
+  collectionToRepr(x, "{", ", ", "}")
+
+proc repr*[T](x: seq[T]): string =
+  ## Generic `repr` operator for seqs that is lifted from the components
+  ## of `x`. Example:
+  ##   ```Nim
+  ##   $(@[23, 45]) == "@[23, 45]"
+  ##   ```
+  collectionToRepr(x, "@[", ", ", "]")
+
+proc repr*[T, IDX](x: array[IDX, T]): string =
+  ## Generic `repr` operator for arrays that is lifted from the components.
+  collectionToRepr(x, "[", ", ", "]")
+
+proc repr*[T](x: openArray[T]): string =
+  ## Generic `repr` operator for openarrays that is lifted from the components
+  ## of `x`. Example:
+  ##   ```Nim
+  ##   $(@[23, 45].toOpenArray(0, 1)) == "[23, 45]"
+  ##   ```
+  collectionToRepr(x, "[", ", ", "]")
+
+proc repr*[T](x: UncheckedArray[T]): string =
+  "[...]"
diff --git a/lib/system/reprjs.nim b/lib/system/reprjs.nim
index d04d6e12b..761d66aec 100644
--- a/lib/system/reprjs.nim
+++ b/lib/system/reprjs.nim
@@ -8,47 +8,45 @@
 #
 # The generic ``repr`` procedure for the javascript backend.
 
-proc reprInt(x: int64): string {.compilerproc.} = return $x
-proc reprFloat(x: float): string {.compilerproc.} =
-  # Js toString doesn't differentiate between 1.0 and 1,
-  # but we do.
-  if $x == $(x.int): $x & ".0"
-  else: $x
+when defined(nimPreviewSlimSystem):
+  import std/formatfloat
+
+proc reprInt(x: int64): string {.compilerproc.} = $x
+proc reprInt(x: uint64): string {.compilerproc.} = $x
+proc reprInt(x: int): string {.compilerproc.} = $x
+proc reprFloat(x: float): string {.compilerproc.} = $x
 
 proc reprPointer(p: pointer): string {.compilerproc.} =
   # Do we need to generate the full 8bytes ? In js a pointer is an int anyway
   var tmp: int
-  {. emit: """
-    if (`p`_Idx == null) {
-      `tmp` = 0;
-    } else {
-      `tmp` = `p`_Idx;
-    }
-  """ .}
+  {.emit: "`tmp` = `p`_Idx || 0;".}
   result = $tmp
 
 proc reprBool(x: bool): string {.compilerRtl.} =
   if x: result = "true"
   else: result = "false"
 
-proc isUndefined[T](x: T): bool {.inline.} = {.emit: "`result` = `x` === undefined;"}
-
 proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
-  if not typ.node.sons[e].isUndefined:
-    result = makeNimstrLit(typ.node.sons[e].name)
+  var tmp: bool
+  let item = typ.node.sons[e]
+  {.emit: "`tmp` = `item` !== undefined;".}
+  if tmp:
+    result = makeNimstrLit(item.name)
   else:
     result = $e & " (invalid data!)"
 
+include system/repr_impl
+
 proc reprChar(x: char): string {.compilerRtl.} =
   result = "\'"
   case x
   of '"': add(result, "\\\"")
   of '\\': add(result, "\\\\")
-  of '\127'..'\255', '\0'..'\31': add( result, "\\" & reprInt(ord(x)) )
+  of '\127'..'\255', '\0'..'\31': add(result, "\\" & reprInt(ord(x)))
   else: add(result, x)
   add(result, "\'")
 
-proc reprStrAux(result: var string, s: cstring, len: int) =
+proc reprStrAux(result: var string, s: cstring | string, len: int) =
   add(result, "\"")
   for i in 0 .. len-1:
     let c = s[i]
@@ -63,15 +61,7 @@ proc reprStrAux(result: var string, s: cstring, len: int) =
   add(result, "\"")
 
 proc reprStr(s: string): string {.compilerRtl.} =
-  result = ""
-  if cast[pointer](s).isNil:
-    # Handle nil strings here because they don't have a length field in js
-    # TODO: check for null/undefined before generating call to length in js?
-    # Also: c backend repr of a nil string is <pointer>"", but repr of an
-    # array of string that is not initialized is [nil, nil, ...] ??
-    add(result, "nil")
-  else:
-    reprStrAux(result, s, s.len)
+  reprStrAux(result, s, s.len)
 
 proc addSetElem(result: var string, elem: int, typ: PNimType) =
   # Dispatch each set element to the correct repr<Type> proc
@@ -112,7 +102,6 @@ proc reprSetAux(result: var string, s: int, typ: PNimType) =
   add(result, "}")
 
 proc reprSet(e: int, typ: PNimType): string {.compilerRtl.} =
-  result = ""
   reprSetAux(result, e, typ)
 
 type
@@ -128,24 +117,9 @@ proc reprAux(result: var string, p: pointer, typ: PNimType, cl: var ReprClosure)
 
 proc reprArray(a: pointer, typ: PNimType,
               cl: var ReprClosure): string {.compilerRtl.} =
-  var isNilArrayOrSeq: bool
-  # isnil is not enough here as it would try to deref `a` without knowing what's inside
-  {. emit: """
-    if (`a` == null) {
-      `isNilArrayOrSeq` = true;
-    } else if (`a`[0] == null) {
-      `isNilArrayOrSeq` = true;
-    } else {
-      `isNilArrayOrSeq` = false;
-    };
-    """ .}
-  if typ.kind == tySequence and isNilArrayOrSeq:
-    return "nil"
-
   # We prepend @ to seq, the C backend prepends the pointer to the seq.
   result = if typ.kind == tySequence: "@[" else: "["
   var len: int = 0
-  var i: int = 0
 
   {. emit: "`len` = `a`.length;\n" .}
   var dereffed: pointer = a
@@ -161,15 +135,15 @@ proc reprArray(a: pointer, typ: PNimType,
 
   add(result, "]")
 
-proc isPointedToNil(p: pointer): bool {.inline.}=
-  {. emit: "if (`p` === null) {`result` = true};\n" .}
+proc isPointedToNil(p: pointer): bool =
+  {. emit: "if (`p` === null) {`result` = true;}\n" .}
 
 proc reprRef(result: var string, p: pointer, typ: PNimType,
           cl: var ReprClosure) =
   if p.isPointedToNil:
-    add(result , "nil")
+    add(result, "nil")
     return
-  add( result, "ref " & reprPointer(p) )
+  add(result, "ref " & reprPointer(p))
   add(result, " --> ")
   if typ.base.kind != tyArray:
     {. emit: """
@@ -182,8 +156,8 @@ proc reprRef(result: var string, p: pointer, typ: PNimType,
 proc reprRecordAux(result: var string, o: pointer, typ: PNimType, cl: var ReprClosure) =
   add(result, "[")
 
-  var first: bool = true
-  var val: pointer = o
+  var first = true
+  var val = o
   if typ.node.len == 0:
     # if the object has only one field, len is 0  and sons is nil, the field is in node
     let key: cstring = typ.node.name
@@ -203,11 +177,10 @@ proc reprRecordAux(result: var string, o: pointer, typ: PNimType, cl: var ReprCl
   add(result, "]")
 
 proc reprRecord(o: pointer, typ: PNimType, cl: var ReprClosure): string {.compilerRtl.} =
-  result = ""
-  reprRecordAux(result, o, typ,cl)
+  reprRecordAux(result, o, typ, cl)
 
 
-proc reprJSONStringify(p: int): string {.compilerRtl.} =
+proc reprJsonStringify(p: int): string {.compilerRtl.} =
   # As a last resort, use stringify
   # We use this for tyOpenArray, tyVarargs while genTypeInfo is not implemented
   var tmp: cstring
@@ -221,22 +194,23 @@ proc reprAux(result: var string, p: pointer, typ: PNimType,
     return
   dec(cl.recDepth)
   case typ.kind
-  of tyInt..tyInt64, tyUInt..tyUInt64:
-    add( result, reprInt(cast[int](p)) )
+  of tyInt..tyInt32, tyUInt..tyUInt32:
+    add(result, reprInt(cast[int](p)))
+  of tyInt64:
+    add(result, reprInt(cast[int64](p)))
+  of tyUInt64:
+    add(result, reprInt(cast[uint64](p)))
   of tyChar:
-    add( result, reprChar(cast[char](p)) )
+    add(result, reprChar(cast[char](p)))
   of tyBool:
-    add( result, reprBool(cast[bool](p)) )
+    add(result, reprBool(cast[bool](p)))
   of tyFloat..tyFloat128:
-    add( result, reprFloat(cast[float](p)) )
+    add(result, reprFloat(cast[float](p)))
   of tyString:
     var fp: int
     {. emit: "`fp` = `p`;\n" .}
-    if cast[string](fp).isNil:
-      add(result, "nil")
-    else:
-      add( result, reprStr(cast[string](p)) )
-  of tyCString:
+    add(result, reprStr(cast[string](p)))
+  of tyCstring:
     var fp: cstring
     {. emit: "`fp` = `p`;\n" .}
     if fp.isNil:
@@ -266,12 +240,12 @@ proc reprAux(result: var string, p: pointer, typ: PNimType,
     else:
       add(result, reprPointer(p))
   else:
-    add( result, "(invalid data!)" & reprJsonStringify(cast[int](p)) )
+    add(result, "(invalid data!)" & reprJsonStringify(cast[int](p)))
   inc(cl.recDepth)
 
 proc reprAny(p: pointer, typ: PNimType): string {.compilerRtl.} =
   var cl: ReprClosure
   initReprClosure(cl)
-  result = ""
   reprAux(result, p, typ, cl)
-  add(result, "\n")
\ No newline at end of file
+  when defined(nimLegacyReprWithNewline): # see PR #16034
+    add result, "\n"
diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim
new file mode 100644
index 000000000..572e77408
--- /dev/null
+++ b/lib/system/seqs_v2.nim
@@ -0,0 +1,227 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2017 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+
+# import std/typetraits
+# strs already imported allocateds for us.
+
+
+# Some optimizations here may be not to empty-seq-initialize some symbols, then StrictNotNil complains.
+{.push warning[StrictNotNil]: off.}  # See https://github.com/nim-lang/Nim/issues/21401
+
+
+## Default seq implementation used by Nim's core.
+type
+  NimSeqPayloadBase = object
+    cap: int
+
+  NimSeqPayload[T] = object
+    cap: int
+    data: UncheckedArray[T]
+
+  NimSeqV2*[T] = object # \
+    # if you change this implementation, also change seqs_v2_reimpl.nim!
+    len: int
+    p: ptr NimSeqPayload[T]
+
+  NimRawSeq = object
+    len: int
+    p: pointer
+
+const nimSeqVersion {.core.} = 2
+
+# XXX make code memory safe for overflows in '*'
+
+proc newSeqPayload(cap, elemSize, elemAlign: int): pointer {.compilerRtl, raises: [].} =
+  # we have to use type erasure here as Nim does not support generic
+  # compilerProcs. Oh well, this will all be inlined anyway.
+  if cap > 0:
+    var p = cast[ptr NimSeqPayloadBase](alignedAlloc0(align(sizeof(NimSeqPayloadBase), elemAlign) + cap * elemSize, elemAlign))
+    p.cap = cap
+    result = p
+  else:
+    result = nil
+
+proc newSeqPayloadUninit(cap, elemSize, elemAlign: int): pointer {.compilerRtl, raises: [].} =
+  # Used in `newSeqOfCap()`.
+  if cap > 0:
+    var p = cast[ptr NimSeqPayloadBase](alignedAlloc(align(sizeof(NimSeqPayloadBase), elemAlign) + cap * elemSize, elemAlign))
+    p.cap = cap
+    result = p
+  else:
+    result = nil
+
+template `+!`(p: pointer, s: int): pointer =
+  cast[pointer](cast[int](p) +% s)
+
+template `-!`(p: pointer, s: int): pointer =
+  cast[pointer](cast[int](p) -% s)
+
+proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {.
+    noSideEffect, tags: [], raises: [], compilerRtl.} =
+  {.noSideEffect.}:
+    let headerSize = align(sizeof(NimSeqPayloadBase), elemAlign)
+    if addlen <= 0:
+      result = p
+    elif p == nil:
+      result = newSeqPayload(len+addlen, elemSize, elemAlign)
+    else:
+      # Note: this means we cannot support things that have internal pointers as
+      # they get reallocated here. This needs to be documented clearly.
+      var p = cast[ptr NimSeqPayloadBase](p)
+      let oldCap = p.cap and not strlitFlag
+      let newCap = max(resize(oldCap), len+addlen)
+      var q: ptr NimSeqPayloadBase
+      if (p.cap and strlitFlag) == strlitFlag:
+        q = cast[ptr NimSeqPayloadBase](alignedAlloc(headerSize + elemSize * newCap, elemAlign))
+        copyMem(q +! headerSize, p +! headerSize, len * elemSize)
+      else:
+        let oldSize = headerSize + elemSize * oldCap
+        let newSize = headerSize + elemSize * newCap
+        q = cast[ptr NimSeqPayloadBase](alignedRealloc(p, oldSize, newSize, elemAlign))
+
+      zeroMem(q +! headerSize +! len * elemSize, addlen * elemSize)
+      q.cap = newCap
+      result = q
+
+proc zeroNewElements(len: int; q: pointer; addlen, elemSize, elemAlign: int) {.
+    noSideEffect, tags: [], raises: [], compilerRtl.} =
+  {.noSideEffect.}:
+    let headerSize = align(sizeof(NimSeqPayloadBase), elemAlign)
+    zeroMem(q +! headerSize +! len * elemSize, addlen * elemSize)
+
+proc prepareSeqAddUninit(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {.
+    noSideEffect, tags: [], raises: [], compilerRtl.} =
+  {.noSideEffect.}:
+    let headerSize = align(sizeof(NimSeqPayloadBase), elemAlign)
+    if addlen <= 0:
+      result = p
+    elif p == nil:
+      result = newSeqPayloadUninit(len+addlen, elemSize, elemAlign)
+    else:
+      # Note: this means we cannot support things that have internal pointers as
+      # they get reallocated here. This needs to be documented clearly.
+      var p = cast[ptr NimSeqPayloadBase](p)
+      let oldCap = p.cap and not strlitFlag
+      let newCap = max(resize(oldCap), len+addlen)
+      if (p.cap and strlitFlag) == strlitFlag:
+        var q = cast[ptr NimSeqPayloadBase](alignedAlloc(headerSize + elemSize * newCap, elemAlign))
+        copyMem(q +! headerSize, p +! headerSize, len * elemSize)
+        q.cap = newCap
+        result = q
+      else:
+        let oldSize = headerSize + elemSize * oldCap
+        let newSize = headerSize + elemSize * newCap
+        var q = cast[ptr NimSeqPayloadBase](alignedRealloc(p, oldSize, newSize, elemAlign))
+        q.cap = newCap
+        result = q
+
+proc shrink*[T](x: var seq[T]; newLen: Natural) {.tags: [], raises: [].} =
+  when nimvm:
+    {.cast(tags: []).}:
+      setLen(x, newLen)
+  else:
+    #sysAssert newLen <= x.len, "invalid newLen parameter for 'shrink'"
+    when not supportsCopyMem(T):
+      for i in countdown(x.len - 1, newLen):
+        reset x[i]
+    # XXX This is wrong for const seqs that were moved into 'x'!
+    {.noSideEffect.}:
+      cast[ptr NimSeqV2[T]](addr x).len = newLen
+
+proc grow*[T](x: var seq[T]; newLen: Natural; value: T) {.nodestroy.} =
+  let oldLen = x.len
+  #sysAssert newLen >= x.len, "invalid newLen parameter for 'grow'"
+  if newLen <= oldLen: return
+  var xu = cast[ptr NimSeqV2[T]](addr x)
+  if xu.p == nil or (xu.p.cap and not strlitFlag) < newLen:
+    xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, newLen - oldLen, sizeof(T), alignof(T)))
+  xu.len = newLen
+  for i in oldLen .. newLen-1:
+    wasMoved(xu.p.data[i])
+    `=copy`(xu.p.data[i], value)
+
+proc add*[T](x: var seq[T]; y: sink T) {.magic: "AppendSeqElem", noSideEffect, nodestroy.} =
+  ## Generic proc for adding a data item `y` to a container `x`.
+  ##
+  ## For containers that have an order, `add` means *append*. New generic
+  ## containers should also call their adding proc `add` for consistency.
+  ## Generic code becomes much easier to write if the Nim naming scheme is
+  ## respected.
+  {.cast(noSideEffect).}:
+    let oldLen = x.len
+    var xu = cast[ptr NimSeqV2[T]](addr x)
+    if xu.p == nil or (xu.p.cap and not strlitFlag) < oldLen+1:
+      xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, 1, sizeof(T), alignof(T)))
+    xu.len = oldLen+1
+    # .nodestroy means `xu.p.data[oldLen] = value` is compiled into a
+    # copyMem(). This is fine as know by construction that
+    # in `xu.p.data[oldLen]` there is nothing to destroy.
+    # We also save the `wasMoved + destroy` pair for the sink parameter.
+    xu.p.data[oldLen] = y
+
+proc setLen[T](s: var seq[T], newlen: Natural) {.nodestroy.} =
+  {.noSideEffect.}:
+    if newlen < s.len:
+      shrink(s, newlen)
+    else:
+      let oldLen = s.len
+      if newlen <= oldLen: return
+      var xu = cast[ptr NimSeqV2[T]](addr s)
+      if xu.p == nil or (xu.p.cap and not strlitFlag) < newlen:
+        xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, newlen - oldLen, sizeof(T), alignof(T)))
+      xu.len = newlen
+      for i in oldLen..<newlen:
+        xu.p.data[i] = default(T)
+
+proc newSeq[T](s: var seq[T], len: Natural) =
+  shrink(s, 0)
+  setLen(s, len)
+
+proc sameSeqPayload(x: pointer, y: pointer): bool {.compilerRtl, inl.} =
+  result = cast[ptr NimRawSeq](x)[].p == cast[ptr NimRawSeq](y)[].p
+
+
+func capacity*[T](self: seq[T]): int {.inline.} =
+  ## Returns the current capacity of the seq.
+  # See https://github.com/nim-lang/RFCs/issues/460
+  runnableExamples:
+    var lst = newSeqOfCap[string](cap = 42)
+    lst.add "Nim"
+    assert lst.capacity == 42
+
+  let sek = cast[ptr NimSeqV2[T]](unsafeAddr self)
+  result = if sek.p != nil: sek.p.cap and not strlitFlag else: 0
+
+func setLenUninit*[T](s: var seq[T], newlen: Natural) {.nodestroy.} =
+  ## Sets the length of seq `s` to `newlen`. `T` may be any sequence type.
+  ## New slots will not be initialized.
+  ##
+  ## If the current length is greater than the new length,
+  ## `s` will be truncated.
+  ##   ```nim
+  ##   var x = @[10, 20]
+  ##   x.setLenUninit(5)
+  ##   x[4] = 50
+  ##   assert x[4] == 50
+  ##   x.setLenUninit(1)
+  ##   assert x == @[10]
+  ##   ```
+  {.noSideEffect.}:
+    if newlen < s.len:
+      shrink(s, newlen)
+    else:
+      let oldLen = s.len
+      if newlen <= oldLen: return
+      var xu = cast[ptr NimSeqV2[T]](addr s)
+      if xu.p == nil or (xu.p.cap and not strlitFlag) < newlen:
+        xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, newlen - oldLen, sizeof(T), alignof(T)))
+      xu.len = newlen
+
+{.pop.}  # See https://github.com/nim-lang/Nim/issues/21401
diff --git a/lib/system/seqs_v2_reimpl.nim b/lib/system/seqs_v2_reimpl.nim
new file mode 100644
index 000000000..09b7e7ac4
--- /dev/null
+++ b/lib/system/seqs_v2_reimpl.nim
@@ -0,0 +1,24 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2020 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+type
+  NimSeqPayloadReimpl = object
+    cap: int
+    data: pointer
+
+  NimSeqV2Reimpl = object
+    len: int
+    p: ptr NimSeqPayloadReimpl
+
+template frees(s: NimSeqV2Reimpl) =
+  if s.p != nil and (s.p.cap and strlitFlag) != strlitFlag:
+    when compileOption("threads"):
+      deallocShared(s.p)
+    else:
+      dealloc(s.p)
\ No newline at end of file
diff --git a/lib/system/setops.nim b/lib/system/setops.nim
new file mode 100644
index 000000000..67aa3097a
--- /dev/null
+++ b/lib/system/setops.nim
@@ -0,0 +1,89 @@
+func incl*[T](x: var set[T], y: T) {.magic: "Incl".} =
+  ## Includes element `y` in the set `x`.
+  ##
+  ## This is the same as `x = x + {y}`, but it might be more efficient.
+  runnableExamples:
+    var a = {1, 3, 5}
+    a.incl(2)
+    assert a == {1, 2, 3, 5}
+    a.incl(4)
+    assert a == {1, 2, 3, 4, 5}
+
+when not defined(nimHasCallsitePragma):
+  {.pragma: callsite.}
+
+template incl*[T](x: var set[T], y: set[T]) {.callsite.} =
+  ## Includes the set `y` in the set `x`.
+  runnableExamples:
+    var a = {1, 3, 5, 7}
+    var b = {4, 5, 6}
+    a.incl(b)
+    assert a == {1, 3, 4, 5, 6, 7}
+  x = x + y
+
+func excl*[T](x: var set[T], y: T) {.magic: "Excl".} =
+  ## Excludes element `y` from the set `x`.
+  ##
+  ## This is the same as `x = x - {y}`, but it might be more efficient.
+  runnableExamples:
+    var b = {2, 3, 5, 6, 12, 54}
+    b.excl(5)
+    assert b == {2, 3, 6, 12, 54}
+
+template excl*[T](x: var set[T], y: set[T]) {.callsite.} =
+  ## Excludes the set `y` from the set `x`.
+  runnableExamples:
+    var a = {1, 3, 5, 7}
+    var b = {3, 4, 5}
+    a.excl(b) 
+    assert a == {1, 7}
+  x = x - y
+
+func card*[T](x: set[T]): int {.magic: "Card".} =
+  ## Returns the cardinality of the set `x`, i.e. the number of elements
+  ## in the set.
+  runnableExamples:
+    var a = {1, 3, 5, 7}
+    assert card(a) == 4
+    var b = {1, 3, 5, 7, 5}
+    assert card(b) == 4 # repeated 5 doesn't count
+
+func len*[T](x: set[T]): int {.magic: "Card".}
+  ## An alias for `card(x)`.
+
+
+func `*`*[T](x, y: set[T]): set[T] {.magic: "MulSet".} =
+  ## This operator computes the intersection of two sets.
+  runnableExamples:
+    assert {1, 2, 3} * {2, 3, 4} == {2, 3}
+
+func `+`*[T](x, y: set[T]): set[T] {.magic: "PlusSet".} =
+  ## This operator computes the union of two sets.
+  runnableExamples:
+    assert {1, 2, 3} + {2, 3, 4} == {1, 2, 3, 4}
+
+func `-`*[T](x, y: set[T]): set[T] {.magic: "MinusSet".} =
+  ## This operator computes the difference of two sets.
+  runnableExamples:
+    assert {1, 2, 3} - {2, 3, 4} == {1}
+
+func contains*[T](x: set[T], y: T): bool {.magic: "InSet".} =
+  ## One should overload this proc if one wants to overload the `in` operator.
+  ##
+  ## The parameters are in reverse order! `a in b` is a template for
+  ## `contains(b, a)`.
+  ## This is because the unification algorithm that Nim uses for overload
+  ## resolution works from left to right.
+  ## But for the `in` operator that would be the wrong direction for this
+  ## piece of code:
+  runnableExamples:
+    var s: set[range['a'..'z']] = {'a'..'c'}
+    assert s.contains('c')
+    assert 'b' in s
+    assert 'd' notin s
+    assert set['a'..'z'] is set[range['a'..'z']]
+  ## If `in` had been declared as `[T](elem: T, s: set[T])` then `T` would
+  ## have been bound to `char`. But `s` is not compatible to type
+  ## `set[char]`! The solution is to bind `T` to `range['a'..'z']`. This
+  ## is achieved by reversing the parameters for `contains`; `in` then
+  ## passes its arguments in reverse order.
diff --git a/lib/system/sets.nim b/lib/system/sets.nim
index 53d222468..97431c296 100644
--- a/lib/system/sets.nim
+++ b/lib/system/sets.nim
@@ -9,21 +9,20 @@
 
 # set handling
 
-type
-  NimSet = array[0..4*2048-1, uint8]
-{.deprecated: [TNimSet: NimSet].}
 
-proc countBits32(n: int32): int {.compilerproc.} =
-  var v = n
-  v = v -% ((v shr 1'i32) and 0x55555555'i32)
-  v = (v and 0x33333333'i32) +% ((v shr 2'i32) and 0x33333333'i32)
-  result = ((v +% (v shr 4'i32) and 0xF0F0F0F'i32) *% 0x1010101'i32) shr 24'i32
+proc cardSetImpl(s: ptr UncheckedArray[uint8], len: int): int {.inline.} =
+  var i = 0
+  result = 0
+  var num = 0'u64
+  when defined(x86) or defined(amd64):
+    while i < len - 8:
+      copyMem(addr num, addr s[i], 8)
+      inc(result, countBits64(num))
+      inc(i, 8)
 
-proc countBits64(n: int64): int {.compilerproc.} =
-  result = countBits32(toU32(n and 0xffffffff'i64)) +
-           countBits32(toU32(n shr 32'i64))
+  while i < len:
+    inc(result, countBits32(uint32(s[i])))
+    inc(i, 1)
 
-proc cardSet(s: NimSet, len: int): int {.compilerproc.} =
-  result = 0
-  for i in countup(0, len-1):
-    inc(result, countBits32(int32(s[i])))
+proc cardSet(s: ptr UncheckedArray[uint8], len: int): int {.compilerproc, inline.} =
+  result = cardSetImpl(s, len)
diff --git a/lib/system/stacktraces.nim b/lib/system/stacktraces.nim
new file mode 100644
index 000000000..42be9d94f
--- /dev/null
+++ b/lib/system/stacktraces.nim
@@ -0,0 +1,83 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# Additional code for customizable stack traces. Unstable API, for internal
+# usage only.
+
+const
+  reraisedFromBegin* = -10
+  reraisedFromEnd* = -100
+  maxStackTraceLines* = 128
+
+when defined(nimStackTraceOverride):
+  ## Procedure types for overriding the default stack trace.
+  type
+    cuintptr_t* {.importc: "uintptr_t", nodecl.} = uint
+      ## This is the same as the type `uintptr_t` in C.
+
+    StackTraceOverrideGetTracebackProc* = proc (): string {.
+      nimcall, gcsafe, raises: [], tags: [], noinline.}
+    StackTraceOverrideGetProgramCountersProc* = proc (maxLength: cint): seq[cuintptr_t] {.
+      nimcall, gcsafe, raises: [], tags: [], noinline.}
+    StackTraceOverrideGetDebuggingInfoProc* =
+      proc (programCounters: seq[cuintptr_t], maxLength: cint): seq[StackTraceEntry] {.
+        nimcall, gcsafe, raises: [], tags: [], noinline.}
+
+  # Default procedures (not normally used, because people opting in on this
+  # override are supposed to register their own versions).
+  var
+    stackTraceOverrideGetTraceback: StackTraceOverrideGetTracebackProc =
+      proc (): string {.nimcall, gcsafe, raises: [], tags: [], noinline.} =
+        discard
+        #result = "Stack trace override procedure not registered.\n"
+    stackTraceOverrideGetProgramCounters: StackTraceOverrideGetProgramCountersProc =
+      proc (maxLength: cint): seq[cuintptr_t] {.nimcall, gcsafe, raises: [], tags: [], noinline.} =
+        discard
+    stackTraceOverrideGetDebuggingInfo: StackTraceOverrideGetDebuggingInfoProc =
+      proc (programCounters: seq[cuintptr_t], maxLength: cint): seq[StackTraceEntry] {.
+        nimcall, gcsafe, raises: [], tags: [], noinline.} =
+          discard
+
+  # Custom procedure registration.
+  proc registerStackTraceOverride*(overrideProc: StackTraceOverrideGetTracebackProc) =
+    ## Override the default stack trace inside rawWriteStackTrace() with your
+    ## own procedure.
+    stackTraceOverrideGetTraceback = overrideProc
+  proc registerStackTraceOverrideGetProgramCounters*(overrideProc: StackTraceOverrideGetProgramCountersProc) =
+    stackTraceOverrideGetProgramCounters = overrideProc
+  proc registerStackTraceOverrideGetDebuggingInfo*(overrideProc: StackTraceOverrideGetDebuggingInfoProc) =
+    stackTraceOverrideGetDebuggingInfo = overrideProc
+
+  # Custom stack trace manipulation.
+  proc auxWriteStackTraceWithOverride*(s: var string) =
+    add(s, stackTraceOverrideGetTraceback())
+
+  proc auxWriteStackTraceWithOverride*(s: var seq[StackTraceEntry]) =
+    let programCounters = stackTraceOverrideGetProgramCounters(maxStackTraceLines)
+    if s.len == 0:
+      s = newSeqOfCap[StackTraceEntry](programCounters.len)
+    for programCounter in programCounters:
+      s.add(StackTraceEntry(programCounter: cast[uint](programCounter)))
+
+  # We may have more stack trace lines in the output, due to inlined procedures.
+  proc addDebuggingInfo*(s: seq[StackTraceEntry]): seq[StackTraceEntry] =
+    var programCounters: seq[cuintptr_t]
+    # We process program counters in groups from complete stack traces, because
+    # we have logic that keeps track of certain functions being inlined or not.
+    for entry in s:
+      if entry.procname.isNil and entry.programCounter != 0:
+        programCounters.add(cast[cuintptr_t](entry.programCounter))
+      elif entry.procname.isNil and (entry.line == reraisedFromBegin or entry.line == reraisedFromEnd):
+        result.add(stackTraceOverrideGetDebuggingInfo(programCounters, maxStackTraceLines))
+        programCounters = @[]
+        result.add(entry)
+      else:
+        result.add(entry)
+    if programCounters.len > 0:
+      result.add(stackTraceOverrideGetDebuggingInfo(programCounters, maxStackTraceLines))
diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim
new file mode 100644
index 000000000..89046253b
--- /dev/null
+++ b/lib/system/strmantle.nim
@@ -0,0 +1,263 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# Compilerprocs for strings that do not depend on the string implementation.
+
+import std/private/digitsutils
+
+
+proc cmpStrings(a, b: string): int {.inline, compilerproc.} =
+  let alen = a.len
+  let blen = b.len
+  let minlen = min(alen, blen)
+  if minlen > 0:
+    result = c_memcmp(unsafeAddr a[0], unsafeAddr b[0], cast[csize_t](minlen)).int
+    if result == 0:
+      result = alen - blen
+  else:
+    result = alen - blen
+
+proc leStrings(a, b: string): bool {.inline, compilerproc.} =
+  # required by upcoming backends (NIR).
+  cmpStrings(a, b) <= 0
+
+proc ltStrings(a, b: string): bool {.inline, compilerproc.} =
+  # required by upcoming backends (NIR).
+  cmpStrings(a, b) < 0
+
+proc eqStrings(a, b: string): bool {.inline, compilerproc.} =
+  let alen = a.len
+  let blen = b.len
+  if alen == blen:
+    if alen == 0: return true
+    return equalMem(unsafeAddr(a[0]), unsafeAddr(b[0]), alen)
+
+proc hashString(s: string): int {.compilerproc.} =
+  # the compiler needs exactly the same hash function!
+  # this used to be used for efficient generation of string case statements
+  var h = 0'u
+  for i in 0..len(s)-1:
+    h = h + uint(s[i])
+    h = h + h shl 10
+    h = h xor (h shr 6)
+  h = h + h shl 3
+  h = h xor (h shr 11)
+  h = h + h shl 15
+  result = cast[int](h)
+
+proc eqCstrings(a, b: cstring): bool {.inline, compilerproc.} =
+  if pointer(a) == pointer(b): result = true
+  elif a.isNil or b.isNil: result = false
+  else: result = c_strcmp(a, b) == 0
+
+proc hashCstring(s: cstring): int {.compilerproc.} =
+  # the compiler needs exactly the same hash function!
+  # this used to be used for efficient generation of cstring case statements
+  if s.isNil: return 0
+  var h : uint = 0
+  var i = 0
+  while true:
+    let c = s[i]
+    if c == '\0': break
+    h = h + uint(c)
+    h = h + h shl 10
+    h = h xor (h shr 6)
+    inc i
+  h = h + h shl 3
+  h = h xor (h shr 11)
+  h = h + h shl 15
+  result = cast[int](h)
+
+proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {.
+  importc: "strtod", header: "<stdlib.h>", noSideEffect.}
+
+const
+  IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
+  powtens =  [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+              1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+              1e20, 1e21, 1e22]
+
+
+{.push staticBoundChecks: off.}
+
+proc nimParseBiggestFloat(s: openArray[char], number: var BiggestFloat,
+                         ): int {.compilerproc.} =
+  # This routine attempt to parse float that can parsed quickly.
+  # i.e. whose integer part can fit inside a 53bits integer.
+  # their real exponent must also be <= 22. If the float doesn't follow
+  # these restrictions, transform the float into this form:
+  #  INTEGER * 10 ^ exponent and leave the work to standard `strtod()`.
+  # This avoid the problems of decimal character portability.
+  # see: http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
+  var
+    i = 0
+    sign = 1.0
+    kdigits, fdigits = 0
+    exponent = 0
+    integer = uint64(0)
+    fracExponent = 0
+    expSign = 1
+    firstDigit = -1
+    hasSign = false
+
+  # Sign?
+  if i < s.len and (s[i] == '+' or s[i] == '-'):
+    hasSign = true
+    if s[i] == '-':
+      sign = -1.0
+    inc(i)
+
+  # NaN?
+  if i+2 < s.len and (s[i] == 'N' or s[i] == 'n'):
+    if s[i+1] == 'A' or s[i+1] == 'a':
+      if s[i+2] == 'N' or s[i+2] == 'n':
+        if i+3 >= s.len or s[i+3] notin IdentChars:
+          number = NaN
+          return i+3
+    return 0
+
+  # Inf?
+  if i+2 < s.len and (s[i] == 'I' or s[i] == 'i'):
+    if s[i+1] == 'N' or s[i+1] == 'n':
+      if s[i+2] == 'F' or s[i+2] == 'f':
+        if i+3 >= s.len or s[i+3] notin IdentChars:
+          number = Inf*sign
+          return i+3
+    return 0
+
+  if i < s.len and s[i] in {'0'..'9'}:
+    firstDigit = (s[i].ord - '0'.ord)
+  # Integer part?
+  while i < s.len and s[i] in {'0'..'9'}:
+    inc(kdigits)
+    integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
+    inc(i)
+    while i < s.len and s[i] == '_': inc(i)
+
+  # Fractional part?
+  if i < s.len and s[i] == '.':
+    inc(i)
+    # if no integer part, Skip leading zeros
+    if kdigits <= 0:
+      while i < s.len and s[i] == '0':
+        inc(fracExponent)
+        inc(i)
+        while i < s.len and s[i] == '_': inc(i)
+
+    if firstDigit == -1 and i < s.len and s[i] in {'0'..'9'}:
+      firstDigit = (s[i].ord - '0'.ord)
+    # get fractional part
+    while i < s.len and s[i] in {'0'..'9'}:
+      inc(fdigits)
+      inc(fracExponent)
+      integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
+      inc(i)
+      while i < s.len and s[i] == '_': inc(i)
+
+  # if has no digits: return error
+  if kdigits + fdigits <= 0 and
+     (i == 0 or # no char consumed (empty string).
+     (i == 1 and hasSign)): # or only '+' or '-
+    return 0
+
+  if i+1 < s.len and s[i] in {'e', 'E'}:
+    inc(i)
+    if s[i] == '+' or s[i] == '-':
+      if s[i] == '-':
+        expSign = -1
+
+      inc(i)
+    if s[i] notin {'0'..'9'}:
+      return 0
+    while i < s.len and s[i] in {'0'..'9'}:
+      exponent = exponent * 10 + (ord(s[i]) - ord('0'))
+      inc(i)
+      while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
+
+  var realExponent = expSign*exponent - fracExponent
+  let expNegative = realExponent < 0
+  var absExponent = abs(realExponent)
+
+  # if exponent greater than can be represented: +/- zero or infinity
+  if absExponent > 999:
+    if integer == 0:
+      number = 0.0
+    elif expNegative:
+      number = 0.0*sign
+    else:
+      number = Inf*sign
+    return i
+
+  # if integer is representable in 53 bits:  fast path
+  # max fast path integer is  1<<53 - 1 or  8999999999999999 (16 digits)
+  let digits = kdigits + fdigits
+  if digits <= 15 or (digits <= 16 and firstDigit <= 8):
+    # max float power of ten with set bits above the 53th bit is 10^22
+    if absExponent <= 22:
+      if expNegative:
+        number = sign * integer.float / powtens[absExponent]
+      else:
+        number = sign * integer.float * powtens[absExponent]
+      return i
+
+    # if exponent is greater try to fit extra exponent above 22 by multiplying
+    # integer part is there is space left.
+    let slop = 15 - kdigits - fdigits
+    if absExponent <= 22 + slop and not expNegative:
+      number = sign * integer.float * powtens[slop] * powtens[absExponent-slop]
+      return i
+
+  # if failed: slow path with strtod.
+  var t: array[500, char] # flaviu says: 325 is the longest reasonable literal
+  var ti = 0
+  let maxlen = t.high - "e+000".len # reserve enough space for exponent
+
+  let endPos = i
+  result = endPos
+  i = 0
+  # re-parse without error checking, any error should be handled by the code above.
+  if i < endPos and s[i] == '.': i.inc
+  while i < endPos and s[i] in {'0'..'9','+','-'}:
+    if ti < maxlen:
+      t[ti] = s[i]; inc(ti)
+    inc(i)
+    while i < endPos and s[i] in {'.', '_'}: # skip underscore and decimal point
+      inc(i)
+
+  # insert exponent
+  t[ti] = 'E'
+  inc(ti)
+  t[ti] = if expNegative: '-' else: '+'
+  inc(ti, 4)
+
+  # insert adjusted exponent
+  t[ti-1] = ('0'.ord + absExponent mod 10).char
+  absExponent = absExponent div 10
+  t[ti-2] = ('0'.ord + absExponent mod 10).char
+  absExponent = absExponent div 10
+  t[ti-3] = ('0'.ord + absExponent mod 10).char
+  number = c_strtod(cast[cstring](addr t), nil)
+
+{.pop.} # staticBoundChecks
+
+proc nimBoolToStr(x: bool): string {.compilerRtl.} =
+  return if x: "true" else: "false"
+
+proc nimCharToStr(x: char): string {.compilerRtl.} =
+  result = newString(1)
+  result[0] = x
+
+when defined(gcDestructors):
+  proc GC_getStatistics*(): string =
+    result = "[GC] total memory: "
+    result.addInt getTotalMem()
+    result.add "\n[GC] occupied memory: "
+    result.addInt getOccupiedMem()
+    result.add '\n'
+    #"[GC] cycle collections: " & $gch.stat.cycleCollections & "\n" &
diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim
new file mode 100644
index 000000000..404b4f78d
--- /dev/null
+++ b/lib/system/strs_v2.nim
@@ -0,0 +1,224 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2017 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Default new string implementation used by Nim's core.
+
+type
+  NimStrPayloadBase = object
+    cap: int
+
+  NimStrPayload {.core.} = object
+    cap: int
+    data: UncheckedArray[char]
+
+  NimStringV2 {.core.} = object
+    len: int
+    p: ptr NimStrPayload ## can be nil if len == 0.
+
+const nimStrVersion {.core.} = 2
+
+template isLiteral(s): bool = (s.p == nil) or (s.p.cap and strlitFlag) == strlitFlag
+
+template contentSize(cap): int = cap + 1 + sizeof(NimStrPayloadBase)
+
+template frees(s) =
+  if not isLiteral(s):
+    when compileOption("threads"):
+      deallocShared(s.p)
+    else:
+      dealloc(s.p)
+
+template allocPayload(newLen: int): ptr NimStrPayload =
+  when compileOption("threads"):
+    cast[ptr NimStrPayload](allocShared(contentSize(newLen)))
+  else:
+    cast[ptr NimStrPayload](alloc(contentSize(newLen)))
+
+template allocPayload0(newLen: int): ptr NimStrPayload =
+  when compileOption("threads"):
+    cast[ptr NimStrPayload](allocShared0(contentSize(newLen)))
+  else:
+    cast[ptr NimStrPayload](alloc0(contentSize(newLen)))
+
+template reallocPayload(p: pointer, newLen: int): ptr NimStrPayload =
+  when compileOption("threads"):
+    cast[ptr NimStrPayload](reallocShared(p, contentSize(newLen)))
+  else:
+    cast[ptr NimStrPayload](realloc(p, contentSize(newLen)))
+
+template reallocPayload0(p: pointer; oldLen, newLen: int): ptr NimStrPayload =
+  when compileOption("threads"):
+    cast[ptr NimStrPayload](reallocShared0(p, contentSize(oldLen), contentSize(newLen)))
+  else:
+    cast[ptr NimStrPayload](realloc0(p, contentSize(oldLen), contentSize(newLen)))
+
+proc resize(old: int): int {.inline.} =
+  if old <= 0: result = 4
+  elif old <= high(int16): result = old * 2
+  else: result = old * 3 div 2 # for large arrays * 3/2 is better
+
+proc prepareAdd(s: var NimStringV2; addLen: int) {.compilerRtl.} =
+  let newLen = s.len + addLen
+  if isLiteral(s):
+    let oldP = s.p
+    # can't mutate a literal, so we need a fresh copy here:
+    s.p = allocPayload(newLen)
+    s.p.cap = newLen
+    if s.len > 0:
+      # we are about to append, so there is no need to copy the \0 terminator:
+      copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], min(s.len, newLen))
+    elif oldP == nil:
+      # In the case of `newString(0) & ""`, since `src.len == 0`, `appendString`
+      # will not set the `\0` terminator, so we set it here.
+      s.p.data[0] = '\0'
+  else:
+    let oldCap = s.p.cap and not strlitFlag
+    if newLen > oldCap:
+      let newCap = max(newLen, resize(oldCap))
+      s.p = reallocPayload(s.p, newCap)
+      s.p.cap = newCap
+      if newLen < newCap:
+        zeroMem(cast[pointer](addr s.p.data[newLen+1]), newCap - newLen)
+
+proc nimAddCharV1(s: var NimStringV2; c: char) {.compilerRtl, inl.} =
+  #if (s.p == nil) or (s.len+1 > s.p.cap and not strlitFlag):
+  prepareAdd(s, 1)
+  s.p.data[s.len] = c
+  inc s.len
+  s.p.data[s.len] = '\0'
+
+proc toNimStr(str: cstring, len: int): NimStringV2 {.compilerproc.} =
+  if len <= 0:
+    result = NimStringV2(len: 0, p: nil)
+  else:
+    var p = allocPayload(len)
+    p.cap = len
+    copyMem(unsafeAddr p.data[0], str, len+1)
+    result = NimStringV2(len: len, p: p)
+
+proc cstrToNimstr(str: cstring): NimStringV2 {.compilerRtl.} =
+  if str == nil: toNimStr(str, 0)
+  else: toNimStr(str, str.len)
+
+proc nimToCStringConv(s: NimStringV2): cstring {.compilerproc, nonReloadable, inline.} =
+  if s.len == 0: result = cstring""
+  else: result = cast[cstring](unsafeAddr s.p.data)
+
+proc appendString(dest: var NimStringV2; src: NimStringV2) {.compilerproc, inline.} =
+  if src.len > 0:
+    # also copy the \0 terminator:
+    copyMem(unsafeAddr dest.p.data[dest.len], unsafeAddr src.p.data[0], src.len+1)
+    inc dest.len, src.len
+
+proc appendChar(dest: var NimStringV2; c: char) {.compilerproc, inline.} =
+  dest.p.data[dest.len] = c
+  inc dest.len
+  dest.p.data[dest.len] = '\0'
+
+proc rawNewString(space: int): NimStringV2 {.compilerproc.} =
+  # this is also 'system.newStringOfCap'.
+  if space <= 0:
+    result = NimStringV2(len: 0, p: nil)
+  else:
+    var p = allocPayload(space)
+    p.cap = space
+    p.data[0] = '\0'
+    result = NimStringV2(len: 0, p: p)
+
+proc mnewString(len: int): NimStringV2 {.compilerproc.} =
+  if len <= 0:
+    result = NimStringV2(len: 0, p: nil)
+  else:
+    var p = allocPayload0(len)
+    p.cap = len
+    result = NimStringV2(len: len, p: p)
+
+proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} =
+  if newLen == 0:
+    discard "do not free the buffer here, pattern 's.setLen 0' is common for avoiding allocations"
+  else:
+    if isLiteral(s):
+      let oldP = s.p
+      s.p = allocPayload(newLen)
+      s.p.cap = newLen
+      if s.len > 0:
+        copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], min(s.len, newLen))
+        if newLen > s.len:
+          zeroMem(cast[pointer](addr s.p.data[s.len]), newLen - s.len + 1)
+        else:
+          s.p.data[newLen] = '\0'
+      else:
+        zeroMem(cast[pointer](addr s.p.data[0]), newLen + 1)
+    elif newLen > s.len:
+      let oldCap = s.p.cap and not strlitFlag
+      if newLen > oldCap:
+        let newCap = max(newLen, resize(oldCap))
+        s.p = reallocPayload0(s.p, oldCap, newCap)
+        s.p.cap = newCap
+
+    s.p.data[newLen] = '\0'
+  s.len = newLen
+
+proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} =
+  if a.p == b.p and a.len == b.len: return
+  if isLiteral(b):
+    # we can shallow copy literals:
+    frees(a)
+    a.len = b.len
+    a.p = b.p
+  else:
+    if isLiteral(a) or (a.p.cap and not strlitFlag) < b.len:
+      # we have to allocate the 'cap' here, consider
+      # 'let y = newStringOfCap(); var x = y'
+      # on the other hand... These get turned into moves now.
+      frees(a)
+      a.p = allocPayload(b.len)
+      a.p.cap = b.len
+    a.len = b.len
+    copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1)
+
+proc nimPrepareStrMutationImpl(s: var NimStringV2) =
+  let oldP = s.p
+  # can't mutate a literal, so we need a fresh copy here:
+  s.p = allocPayload(s.len)
+  s.p.cap = s.len
+  copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len+1)
+
+proc nimPrepareStrMutationV2(s: var NimStringV2) {.compilerRtl, inl.} =
+  if s.p != nil and (s.p.cap and strlitFlag) == strlitFlag:
+    nimPrepareStrMutationImpl(s)
+
+proc prepareMutation*(s: var string) {.inline.} =
+  # string literals are "copy on write", so you need to call
+  # `prepareMutation` before modifying the strings via `addr`.
+  {.cast(noSideEffect).}:
+    let s = unsafeAddr s
+    nimPrepareStrMutationV2(cast[ptr NimStringV2](s)[])
+
+proc nimAddStrV1(s: var NimStringV2; src: NimStringV2) {.compilerRtl, inl.} =
+  #if (s.p == nil) or (s.len+1 > s.p.cap and not strlitFlag):
+  prepareAdd(s, src.len)
+  appendString s, src
+
+proc nimDestroyStrV1(s: NimStringV2) {.compilerRtl, inl.} =
+  frees(s)
+
+proc nimStrAtLe(s: string; idx: int; ch: char): bool {.compilerRtl, inl.} =
+  result = idx < s.len and s[idx] <= ch
+
+func capacity*(self: string): int {.inline.} =
+  ## Returns the current capacity of the string.
+  # See https://github.com/nim-lang/RFCs/issues/460
+  runnableExamples:
+    var str = newStringOfCap(cap = 42)
+    str.add "Nim"
+    assert str.capacity == 42
+
+  let str = cast[ptr NimStringV2](unsafeAddr self)
+  result = if str.p != nil: str.p.cap and not strlitFlag else: 0
diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim
deleted file mode 100644
index 86b290230..000000000
--- a/lib/system/sysio.nim
+++ /dev/null
@@ -1,434 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2013 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-
-# Nim's standard IO library. It contains high-performance
-# routines for reading and writing data to (buffered) files or
-# TTYs.
-
-{.push debugger:off .} # the user does not want to trace a part
-                       # of the standard library!
-
-when defined(windows):
-  proc c_fdopen(filehandle: cint, mode: cstring): File {.
-    importc: "_fdopen", header: "<stdio.h>".}
-else:
-  proc c_fdopen(filehandle: cint, mode: cstring): File {.
-    importc: "fdopen", header: "<stdio.h>".}
-proc c_fputs(c: cstring, f: File): cint {.
-  importc: "fputs", header: "<stdio.h>", tags: [WriteIOEffect].}
-proc c_fgets(c: cstring, n: cint, f: File): cstring {.
-  importc: "fgets", header: "<stdio.h>", tags: [ReadIOEffect].}
-proc c_fgetc(stream: File): cint {.
-  importc: "fgetc", header: "<stdio.h>", tags: [ReadIOEffect].}
-proc c_ungetc(c: cint, f: File): cint {.
-  importc: "ungetc", header: "<stdio.h>", tags: [].}
-proc c_putc(c: cint, stream: File): cint {.
-  importc: "putc", header: "<stdio.h>", tags: [WriteIOEffect].}
-proc c_fflush(f: File): cint {.
-  importc: "fflush", header: "<stdio.h>".}
-proc c_fclose(f: File): cint {.
-  importc: "fclose", header: "<stdio.h>".}
-proc c_clearerr(f: File) {.
-  importc: "clearerr", header: "<stdio.h>".}
-proc c_feof(f: File): cint {.
-  importc: "feof", header: "<stdio.h>".}
-
-when not declared(c_fwrite):
-  proc c_fwrite(buf: pointer, size, n: csize, f: File): cint {.
-    importc: "fwrite", header: "<stdio.h>".}
-
-# C routine that is used here:
-proc c_fread(buf: pointer, size, n: csize, f: File): csize {.
-  importc: "fread", header: "<stdio.h>", tags: [ReadIOEffect].}
-when defined(windows):
-  when not defined(amd64):
-    proc c_fseek(f: File, offset: int64, whence: cint): cint {.
-      importc: "fseek", header: "<stdio.h>", tags: [].}
-    proc c_ftell(f: File): int64 {.
-      importc: "ftell", header: "<stdio.h>", tags: [].}
-  else:
-    proc c_fseek(f: File, offset: int64, whence: cint): cint {.
-      importc: "_fseeki64", header: "<stdio.h>", tags: [].}
-    proc c_ftell(f: File): int64 {.
-      importc: "_ftelli64", header: "<stdio.h>", tags: [].}
-else:
-  proc c_fseek(f: File, offset: int64, whence: cint): cint {.
-    importc: "fseeko", header: "<stdio.h>", tags: [].}
-  proc c_ftell(f: File): int64 {.
-    importc: "ftello", header: "<stdio.h>", tags: [].}
-proc c_ferror(f: File): cint {.
-  importc: "ferror", header: "<stdio.h>", tags: [].}
-proc c_setvbuf(f: File, buf: pointer, mode: cint, size: csize): cint {.
-  importc: "setvbuf", header: "<stdio.h>", tags: [].}
-
-proc raiseEIO(msg: string) {.noinline, noreturn.} =
-  sysFatal(IOError, msg)
-
-proc raiseEOF() {.noinline, noreturn.} =
-  sysFatal(EOFError, "EOF reached")
-
-proc checkErr(f: File) =
-  if c_ferror(f) != 0:
-    c_clearerr(f)
-    raiseEIO("Unknown IO Error")
-
-{.push stackTrace:off, profiler:off.}
-proc readBuffer(f: File, buffer: pointer, len: Natural): int =
-  result = c_fread(buffer, 1, len, f)
-  if result != len: checkErr(f)
-
-proc readBytes(f: File, a: var openArray[int8|uint8], start, len: Natural): int =
-  result = readBuffer(f, addr(a[start]), len)
-
-proc readChars(f: File, a: var openArray[char], start, len: Natural): int =
-  if (start + len) > len(a):
-    raiseEIO("buffer overflow: (start+len) > length of openarray buffer")
-  result = readBuffer(f, addr(a[start]), len)
-
-proc write(f: File, c: cstring) =
-  discard c_fputs(c, f)
-  checkErr(f)
-
-proc writeBuffer(f: File, buffer: pointer, len: Natural): int =
-  result = c_fwrite(buffer, 1, len, f)
-  checkErr(f)
-
-proc writeBytes(f: File, a: openArray[int8|uint8], start, len: Natural): int =
-  var x = cast[ptr UncheckedArray[int8]](a)
-  result = writeBuffer(f, addr(x[int(start)]), len)
-proc writeChars(f: File, a: openArray[char], start, len: Natural): int =
-  var x = cast[ptr UncheckedArray[int8]](a)
-  result = writeBuffer(f, addr(x[int(start)]), len)
-
-proc write(f: File, s: string) =
-  if writeBuffer(f, cstring(s), s.len) != s.len:
-    raiseEIO("cannot write string to file")
-{.pop.}
-
-when NoFakeVars:
-  when defined(windows):
-    const
-      IOFBF = cint(0)
-      IONBF = cint(4)
-  else:
-    # On all systems I could find, including Linux, Mac OS X, and the BSDs
-    const
-      IOFBF = cint(0)
-      IONBF = cint(2)
-else:
-  var
-    IOFBF {.importc: "_IOFBF", nodecl.}: cint
-    IONBF {.importc: "_IONBF", nodecl.}: cint
-
-const
-  BufSize = 4000
-
-proc close*(f: File) = discard c_fclose(f)
-proc readChar(f: File): char =
-  let x = c_fgetc(f)
-  if x < 0:
-    checkErr(f)
-    raiseEOF()
-  result = char(x)
-
-proc flushFile*(f: File) = discard c_fflush(f)
-proc getFileHandle*(f: File): FileHandle = c_fileno(f)
-
-proc readLine(f: File, line: var TaintedString): bool =
-  var pos = 0
-  var sp: cint = 80
-  # Use the currently reserved space for a first try
-  if line.string.isNil:
-    line = TaintedString(newStringOfCap(80))
-  else:
-    when not defined(nimscript):
-      sp = cint(cast[PGenericSeq](line.string).space)
-    line.string.setLen(sp)
-  while true:
-    # memset to \L so that we can tell how far fgets wrote, even on EOF, where
-    # fgets doesn't append an \L
-    c_memset(addr line.string[pos], '\L'.ord, sp)
-    var fgetsSuccess = c_fgets(addr line.string[pos], sp, f) != nil
-    if not fgetsSuccess: checkErr(f)
-    let m = c_memchr(addr line.string[pos], '\L'.ord, sp)
-    if m != nil:
-      # \l found: Could be our own or the one by fgets, in any case, we're done
-      var last = cast[ByteAddress](m) - cast[ByteAddress](addr line.string[0])
-      if last > 0 and line.string[last-1] == '\c':
-        line.string.setLen(last-1)
-        return fgetsSuccess
-        # We have to distinguish between two possible cases:
-        # \0\l\0 => line ending in a null character.
-        # \0\l\l => last line without newline, null was put there by fgets.
-      elif last > 0 and line.string[last-1] == '\0':
-        if last < pos + sp - 1 and line.string[last+1] != '\0':
-          dec last
-      line.string.setLen(last)
-      return fgetsSuccess
-    else:
-      # fgets will have inserted a null byte at the end of the string.
-      dec sp
-    # No \l found: Increase buffer and read more
-    inc pos, sp
-    sp = 128 # read in 128 bytes at a time
-    line.string.setLen(pos+sp)
-
-proc readLine(f: File): TaintedString =
-  result = TaintedString(newStringOfCap(80))
-  if not readLine(f, result): raiseEOF()
-
-proc write(f: File, i: int) =
-  when sizeof(int) == 8:
-    if c_fprintf(f, "%lld", i) < 0: checkErr(f)
-  else:
-    if c_fprintf(f, "%ld", i) < 0: checkErr(f)
-
-proc write(f: File, i: BiggestInt) =
-  when sizeof(BiggestInt) == 8:
-    if c_fprintf(f, "%lld", i) < 0: checkErr(f)
-  else:
-    if c_fprintf(f, "%ld", i) < 0: checkErr(f)
-
-proc write(f: File, b: bool) =
-  if b: write(f, "true")
-  else: write(f, "false")
-proc write(f: File, r: float32) =
-  if c_fprintf(f, "%.16g", r) < 0: checkErr(f)
-proc write(f: File, r: BiggestFloat) =
-  if c_fprintf(f, "%.16g", r) < 0: checkErr(f)
-
-proc write(f: File, c: char) = discard c_putc(cint(c), f)
-proc write(f: File, a: varargs[string, `$`]) =
-  for x in items(a): write(f, x)
-
-proc readAllBuffer(file: File): string =
-  # This proc is for File we want to read but don't know how many
-  # bytes we need to read before the buffer is empty.
-  result = ""
-  var buffer = newString(BufSize)
-  while true:
-    var bytesRead = readBuffer(file, addr(buffer[0]), BufSize)
-    if bytesRead == BufSize:
-      result.add(buffer)
-    else:
-      buffer.setLen(bytesRead)
-      result.add(buffer)
-      break
-
-proc rawFileSize(file: File): int64 =
-  # this does not raise an error opposed to `getFileSize`
-  var oldPos = c_ftell(file)
-  discard c_fseek(file, 0, 2) # seek the end of the file
-  result = c_ftell(file)
-  discard c_fseek(file, oldPos, 0)
-
-proc endOfFile(f: File): bool =
-  var c = c_fgetc(f)
-  discard c_ungetc(c, f)
-  return c < 0'i32
-  #result = c_feof(f) != 0
-
-proc readAllFile(file: File, len: int64): string =
-  # We acquire the filesize beforehand and hope it doesn't change.
-  # Speeds things up.
-  result = newString(len)
-  let bytes = readBuffer(file, addr(result[0]), len)
-  if endOfFile(file):
-    if bytes < len:
-      result.setLen(bytes)
-  else:
-    # We read all the bytes but did not reach the EOF
-    # Try to read it as a buffer
-    result.add(readAllBuffer(file))
-
-proc readAllFile(file: File): string =
-  var len = rawFileSize(file)
-  result = readAllFile(file, len)
-
-proc readAll(file: File): TaintedString =
-  # Separate handling needed because we need to buffer when we
-  # don't know the overall length of the File.
-  when declared(stdin):
-    let len = if file != stdin: rawFileSize(file) else: -1
-  else:
-    let len = rawFileSize(file)
-  if len > 0:
-    result = readAllFile(file, len).TaintedString
-  else:
-    result = readAllBuffer(file).TaintedString
-
-proc writeLn[Ty](f: File, x: varargs[Ty, `$`]) =
-  for i in items(x):
-    write(f, i)
-  write(f, "\n")
-
-proc writeLine[Ty](f: File, x: varargs[Ty, `$`]) =
-  for i in items(x):
-    write(f, i)
-  write(f, "\n")
-
-when declared(stdout):
-  proc rawEcho(x: string) {.inline, compilerproc.} = write(stdout, x)
-  proc rawEchoNL() {.inline, compilerproc.} = write(stdout, "\n")
-
-# interface to the C procs:
-
-include "system/widestrs"
-
-when defined(windows) and not defined(useWinAnsi):
-  when defined(cpp):
-    proc wfopen(filename, mode: WideCString): pointer {.
-      importcpp: "_wfopen((const wchar_t*)#, (const wchar_t*)#)", nodecl.}
-    proc wfreopen(filename, mode: WideCString, stream: File): File {.
-      importcpp: "_wfreopen((const wchar_t*)#, (const wchar_t*)#, #)", nodecl.}
-  else:
-    proc wfopen(filename, mode: WideCString): pointer {.
-      importc: "_wfopen", nodecl.}
-    proc wfreopen(filename, mode: WideCString, stream: File): File {.
-      importc: "_wfreopen", nodecl.}
-
-  proc fopen(filename, mode: cstring): pointer =
-    var f = newWideCString(filename)
-    var m = newWideCString(mode)
-    result = wfopen(f, m)
-
-  proc freopen(filename, mode: cstring, stream: File): File =
-    var f = newWideCString(filename)
-    var m = newWideCString(mode)
-    result = wfreopen(f, m, stream)
-
-else:
-  proc fopen(filename, mode: cstring): pointer {.importc: "fopen", noDecl.}
-  proc freopen(filename, mode: cstring, stream: File): File {.
-    importc: "freopen", nodecl.}
-
-const
-  FormatOpen: array[FileMode, string] = ["rb", "wb", "w+b", "r+b", "ab"]
-    #"rt", "wt", "w+t", "r+t", "at"
-    # we always use binary here as for Nim the OS line ending
-    # should not be translated.
-
-when defined(posix) and not defined(nimscript):
-  when defined(linux) and defined(amd64):
-    type
-      Mode {.importc: "mode_t", header: "<sys/types.h>".} = cint
-
-      # fillers ensure correct size & offsets
-      Stat {.importc: "struct stat",
-              header: "<sys/stat.h>", final, pure.} = object ## struct stat
-        filler_1: array[24, char]
-        st_mode: Mode        ## Mode of file
-        filler_2: array[144 - 24 - 4, char]
-
-    proc S_ISDIR(m: Mode): bool =
-      ## Test for a directory.
-      (m and 0o170000) == 0o40000
-
-  else:
-    type
-      Mode {.importc: "mode_t", header: "<sys/types.h>".} = cint
-
-      Stat {.importc: "struct stat",
-               header: "<sys/stat.h>", final, pure.} = object ## struct stat
-        st_mode: Mode        ## Mode of file
-
-    proc S_ISDIR(m: Mode): bool {.importc, header: "<sys/stat.h>".}
-      ## Test for a directory.
-
-  proc c_fstat(a1: cint, a2: var Stat): cint {.
-    importc: "fstat", header: "<sys/stat.h>".}
-
-proc open(f: var File, filename: string,
-          mode: FileMode = fmRead,
-          bufSize: int = -1): bool =
-  var p: pointer = fopen(filename, FormatOpen[mode])
-  if p != nil:
-    when defined(posix) and not defined(nimscript):
-      # How `fopen` handles opening a directory is not specified in ISO C and
-      # POSIX. We do not want to handle directories as regular files that can
-      # be opened.
-      var f2 = cast[File](p)
-      var res: Stat
-      if c_fstat(getFileHandle(f2), res) >= 0'i32 and S_ISDIR(res.st_mode):
-        close(f2)
-        return false
-    result = true
-    f = cast[File](p)
-    if bufSize > 0 and bufSize <= high(cint).int:
-      discard c_setvbuf(f, nil, IOFBF, bufSize.cint)
-    elif bufSize == 0:
-      discard c_setvbuf(f, nil, IONBF, 0)
-
-proc reopen(f: File, filename: string, mode: FileMode = fmRead): bool =
-  var p: pointer = freopen(filename, FormatOpen[mode], f)
-  result = p != nil
-
-proc open(f: var File, filehandle: FileHandle, mode: FileMode): bool =
-  f = c_fdopen(filehandle, FormatOpen[mode])
-  result = f != nil
-
-proc setFilePos(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) =
-  if c_fseek(f, pos, cint(relativeTo)) != 0:
-    raiseEIO("cannot set file position")
-
-proc getFilePos(f: File): int64 =
-  result = c_ftell(f)
-  if result < 0: raiseEIO("cannot retrieve file position")
-
-proc getFileSize(f: File): int64 =
-  var oldPos = getFilePos(f)
-  discard c_fseek(f, 0, 2) # seek the end of the file
-  result = getFilePos(f)
-  setFilePos(f, oldPos)
-
-proc readFile(filename: string): TaintedString =
-  var f: File
-  if open(f, filename):
-    try:
-      result = readAll(f).TaintedString
-    finally:
-      close(f)
-  else:
-    sysFatal(IOError, "cannot open: ", filename)
-
-proc writeFile(filename, content: string) =
-  var f: File
-  if open(f, filename, fmWrite):
-    try:
-      f.write(content)
-    finally:
-      close(f)
-  else:
-    sysFatal(IOError, "cannot open: ", filename)
-
-proc setStdIoUnbuffered() =
-  when declared(stdout):
-    discard c_setvbuf(stdout, nil, IONBF, 0)
-  when declared(stderr):
-    discard c_setvbuf(stderr, nil, IONBF, 0)
-  when declared(stdin):
-    discard c_setvbuf(stdin, nil, IONBF, 0)
-
-when declared(stdout):
-  proc echoBinSafe(args: openArray[string]) {.compilerProc.} =
-    # flockfile deadlocks some versions of Android 5.x.x
-    when not defined(windows) and not defined(android):
-      proc flockfile(f: File) {.importc, noDecl.}
-      proc funlockfile(f: File) {.importc, noDecl.}
-      flockfile(stdout)
-    for s in args:
-      discard c_fwrite(s.cstring, s.len, 1, stdout)
-    const linefeed = "\n" # can be 1 or more chars
-    discard c_fwrite(linefeed.cstring, linefeed.len, 1, stdout)
-    discard c_fflush(stdout)
-    when not defined(windows) and not defined(android):
-      funlockfile(stdout)
-
-{.pop.}
diff --git a/lib/system/syslocks.nim b/lib/system/syslocks.nim
deleted file mode 100644
index 6569f4f9f..000000000
--- a/lib/system/syslocks.nim
+++ /dev/null
@@ -1,228 +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.
-#
-
-# Low level system locks and condition vars.
-
-{.push stackTrace: off.}
-
-when defined(Windows):
-  type
-    Handle = int
-
-    SysLock {.importc: "CRITICAL_SECTION",
-              header: "<windows.h>", final, pure.} = object # CRITICAL_SECTION in WinApi
-      DebugInfo: pointer
-      LockCount: int32
-      RecursionCount: int32
-      OwningThread: int
-      LockSemaphore: int
-      SpinCount: int
-
-    SysCond = Handle
-
-  {.deprecated: [THandle: Handle, TSysLock: SysLock, TSysCond: SysCond].}
-
-  proc initSysLock(L: var SysLock) {.importc: "InitializeCriticalSection",
-                                     header: "<windows.h>".}
-    ## Initializes the lock `L`.
-
-  proc tryAcquireSysAux(L: var SysLock): int32 {.importc: "TryEnterCriticalSection",
-                                                 header: "<windows.h>".}
-    ## Tries to acquire the lock `L`.
-
-  proc tryAcquireSys(L: var SysLock): bool {.inline.} =
-    result = tryAcquireSysAux(L) != 0'i32
-
-  proc acquireSys(L: var SysLock) {.importc: "EnterCriticalSection",
-                                    header: "<windows.h>".}
-    ## Acquires the lock `L`.
-
-  proc releaseSys(L: var SysLock) {.importc: "LeaveCriticalSection",
-                                    header: "<windows.h>".}
-    ## Releases the lock `L`.
-
-  proc deinitSys(L: var SysLock) {.importc: "DeleteCriticalSection",
-                                   header: "<windows.h>".}
-
-  proc createEvent(lpEventAttributes: pointer,
-                   bManualReset, bInitialState: int32,
-                   lpName: cstring): SysCond {.stdcall, noSideEffect,
-    dynlib: "kernel32", importc: "CreateEventA".}
-
-  proc closeHandle(hObject: Handle) {.stdcall, noSideEffect,
-    dynlib: "kernel32", importc: "CloseHandle".}
-  proc waitForSingleObject(hHandle: Handle, dwMilliseconds: int32): int32 {.
-    stdcall, dynlib: "kernel32", importc: "WaitForSingleObject", noSideEffect.}
-
-  proc signalSysCond(hEvent: SysCond) {.stdcall, noSideEffect,
-    dynlib: "kernel32", importc: "SetEvent".}
-
-  proc initSysCond(cond: var SysCond) {.inline.} =
-    cond = createEvent(nil, 0'i32, 0'i32, nil)
-  proc deinitSysCond(cond: var SysCond) {.inline.} =
-    closeHandle(cond)
-  proc waitSysCond(cond: var SysCond, lock: var SysLock) =
-    releaseSys(lock)
-    discard waitForSingleObject(cond, -1'i32)
-    acquireSys(lock)
-
-  proc waitSysCondWindows(cond: var SysCond) =
-    discard waitForSingleObject(cond, -1'i32)
-
-elif defined(genode):
-  const
-    Header = "genode_cpp/syslocks.h"
-  type
-    SysLock {.importcpp: "Nim::SysLock", pure, final,
-              header: Header.} = object
-    SysCond {.importcpp: "Nim::SysCond", pure, final,
-              header: Header.} = object
-
-  proc initSysLock(L: var SysLock) = discard
-  proc deinitSys(L: var SysLock) = discard
-  proc acquireSys(L: var SysLock) {.noSideEffect, importcpp.}
-  proc tryAcquireSys(L: var SysLock): bool {.noSideEffect, importcpp.}
-  proc releaseSys(L: var SysLock) {.noSideEffect, importcpp.}
-
-  proc initSysCond(L: var SysCond) = discard
-  proc deinitSysCond(L: var SysCond) = discard
-  proc waitSysCond(cond: var SysCond, lock: var SysLock) {.
-    noSideEffect, importcpp.}
-  proc signalSysCond(cond: var SysCond) {.
-    noSideEffect, importcpp.}
-
-else:
-  type
-    SysLockObj {.importc: "pthread_mutex_t", pure, final,
-               header: """#include <sys/types.h>
-                          #include <pthread.h>""".} = object
-      when defined(linux) and defined(amd64):
-        abi: array[40 div sizeof(clong), clong]
-
-    SysLockAttr {.importc: "pthread_mutexattr_t", pure, final
-               header: """#include <sys/types.h>
-                          #include <pthread.h>""".} = object
-      when defined(linux) and defined(amd64):
-        abi: array[4 div sizeof(cint), cint]  # actually a cint
-
-    SysCondObj {.importc: "pthread_cond_t", pure, final,
-               header: """#include <sys/types.h>
-                          #include <pthread.h>""".} = object
-      when defined(linux) and defined(amd64):
-        abi: array[48 div sizeof(clonglong), clonglong]
-
-    SysCondAttr {.importc: "pthread_condattr_t", pure, final
-               header: """#include <sys/types.h>
-                          #include <pthread.h>""".} = object
-      when defined(linux) and defined(amd64):
-        abi: array[4 div sizeof(cint), cint]  # actually a cint
-
-    SysLockType = distinct cint
-
-  proc initSysLockAux(L: var SysLockObj, attr: ptr SysLockAttr) {.
-    importc: "pthread_mutex_init", header: "<pthread.h>", noSideEffect.}
-  proc deinitSysAux(L: var SysLockObj) {.noSideEffect,
-    importc: "pthread_mutex_destroy", header: "<pthread.h>".}
-
-  proc acquireSysAux(L: var SysLockObj) {.noSideEffect,
-    importc: "pthread_mutex_lock", header: "<pthread.h>".}
-  proc tryAcquireSysAux(L: var SysLockObj): cint {.noSideEffect,
-    importc: "pthread_mutex_trylock", header: "<pthread.h>".}
-
-  proc releaseSysAux(L: var SysLockObj) {.noSideEffect,
-    importc: "pthread_mutex_unlock", header: "<pthread.h>".}
-
-  when defined(ios):
-    # iOS will behave badly if sync primitives are moved in memory. In order
-    # to prevent this once and for all, we're doing an extra malloc when
-    # initializing the primitive.
-    type
-      SysLock = ptr SysLockObj
-      SysCond = ptr SysCondObj
-
-    when not declared(c_malloc):
-      proc c_malloc(size: csize): pointer {.
-        importc: "malloc", header: "<stdlib.h>".}
-      proc c_free(p: pointer) {.
-        importc: "free", header: "<stdlib.h>".}
-
-    proc initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) =
-      L = cast[SysLock](c_malloc(sizeof(SysLockObj)))
-      initSysLockAux(L[], attr)
-
-    proc deinitSys(L: var SysLock) =
-      deinitSysAux(L[])
-      c_free(L)
-
-    template acquireSys(L: var SysLock) =
-      acquireSysAux(L[])
-    template tryAcquireSys(L: var SysLock): bool =
-      tryAcquireSysAux(L[]) == 0'i32
-    template releaseSys(L: var SysLock) =
-      releaseSysAux(L[])
-  else:
-    type
-      SysLock = SysLockObj
-      SysCond = SysCondObj
-
-    template initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) =
-      initSysLockAux(L, attr)
-    template deinitSys(L: var SysLock) =
-      deinitSysAux(L)
-    template acquireSys(L: var SysLock) =
-      acquireSysAux(L)
-    template tryAcquireSys(L: var SysLock): bool =
-      tryAcquireSysAux(L) == 0'i32
-    template releaseSys(L: var SysLock) =
-      releaseSysAux(L)
-
-  when insideRLocksModule:
-    proc SysLockType_Reentrant: SysLockType =
-      {.emit: "`result` = PTHREAD_MUTEX_RECURSIVE;".}
-    proc initSysLockAttr(a: var SysLockAttr) {.
-      importc: "pthread_mutexattr_init", header: "<pthread.h>", noSideEffect.}
-    proc setSysLockType(a: var SysLockAttr, t: SysLockType) {.
-      importc: "pthread_mutexattr_settype", header: "<pthread.h>", noSideEffect.}
-
-  else:
-    proc initSysCondAux(cond: var SysCondObj, cond_attr: ptr SysCondAttr = nil) {.
-      importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.}
-    proc deinitSysCondAux(cond: var SysCondObj) {.noSideEffect,
-      importc: "pthread_cond_destroy", header: "<pthread.h>".}
-
-    proc waitSysCondAux(cond: var SysCondObj, lock: var SysLockObj) {.
-      importc: "pthread_cond_wait", header: "<pthread.h>", noSideEffect.}
-    proc signalSysCondAux(cond: var SysCondObj) {.
-      importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.}
-
-    when defined(ios):
-      proc initSysCond(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) =
-        cond = cast[SysCond](c_malloc(sizeof(SysCondObj)))
-        initSysCondAux(cond[], cond_attr)
-
-      proc deinitSysCond(cond: var SysCond) =
-        deinitSysCondAux(cond[])
-        c_free(cond)
-
-      template waitSysCond(cond: var SysCond, lock: var SysLock) =
-        waitSysCondAux(cond[], lock[])
-      template signalSysCond(cond: var SysCond) =
-        signalSysCondAux(cond[])
-    else:
-      template initSysCond(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) =
-        initSysCondAux(cond, cond_attr)
-      template deinitSysCond(cond: var SysCond) =
-        deinitSysCondAux(cond)
-
-      template waitSysCond(cond: var SysCond, lock: var SysLock) =
-        waitSysCondAux(cond, lock)
-      template signalSysCond(cond: var SysCond) =
-        signalSysCondAux(cond)
-
-{.pop.}
diff --git a/lib/system/sysspawn.nim b/lib/system/sysspawn.nim
deleted file mode 100644
index dc2d13578..000000000
--- a/lib/system/sysspawn.nim
+++ /dev/null
@@ -1,194 +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.
-#
-
-## Implements Nim's 'spawn'.
-
-when not declared(NimString):
-  {.error: "You must not import this module explicitly".}
-
-{.push stackTrace:off.}
-
-# We declare our own condition variables here to get rid of the dummy lock
-# on Windows:
-
-type
-  CondVar = object
-    c: SysCond
-    when defined(posix):
-      stupidLock: SysLock
-      counter: int
-
-proc createCondVar(): CondVar =
-  initSysCond(result.c)
-  when defined(posix):
-    initSysLock(result.stupidLock)
-    #acquireSys(result.stupidLock)
-
-proc destroyCondVar(c: var CondVar) {.inline.} =
-  deinitSysCond(c.c)
-
-proc await(cv: var CondVar) =
-  when defined(posix):
-    acquireSys(cv.stupidLock)
-    while cv.counter <= 0:
-      waitSysCond(cv.c, cv.stupidLock)
-    dec cv.counter
-    releaseSys(cv.stupidLock)
-  else:
-    waitSysCondWindows(cv.c)
-
-proc signal(cv: var CondVar) =
-  when defined(posix):
-    acquireSys(cv.stupidLock)
-    inc cv.counter
-    releaseSys(cv.stupidLock)
-  signalSysCond(cv.c)
-
-type
-  FastCondVar = object
-    event, slowPath: bool
-    slow: CondVar
-
-proc createFastCondVar(): FastCondVar =
-  initSysCond(result.slow.c)
-  when defined(posix):
-    initSysLock(result.slow.stupidLock)
-    #acquireSys(result.slow.stupidLock)
-  result.event = false
-  result.slowPath = false
-
-proc await(cv: var FastCondVar) =
-  #for i in 0 .. 50:
-  #  if cas(addr cv.event, true, false):
-  #    # this is a HIT: Triggers > 95% in my tests.
-  #    return
-  #  cpuRelax()
-  #cv.slowPath = true
-  # XXX For some reason this crashes some test programs
-  await(cv.slow)
-  cv.event = false
-
-proc signal(cv: var FastCondVar) =
-  cv.event = true
-  #if cas(addr cv.slowPath, true, false):
-  signal(cv.slow)
-
-type
-  Barrier* {.compilerProc.} = object
-    counter: int
-    cv: CondVar
-
-proc barrierEnter*(b: ptr Barrier) {.compilerProc.} =
-  atomicInc b.counter
-
-proc barrierLeave*(b: ptr Barrier) {.compilerProc.} =
-  atomicDec b.counter
-  if b.counter <= 0: signal(b.cv)
-
-proc openBarrier*(b: ptr Barrier) {.compilerProc.} =
-  b.counter = 0
-  b.cv = createCondVar()
-
-proc closeBarrier*(b: ptr Barrier) {.compilerProc.} =
-  await(b.cv)
-  destroyCondVar(b.cv)
-
-{.pop.}
-
-# ----------------------------------------------------------------------------
-
-type
-  WorkerProc = proc (thread, args: pointer) {.nimcall, gcsafe.}
-  Worker = object
-    taskArrived: CondVar
-    taskStarted: FastCondVar #\
-    # task data:
-    f: WorkerProc
-    data: pointer
-    ready: bool # put it here for correct alignment!
-
-proc nimArgsPassingDone(p: pointer) {.compilerProc.} =
-  let w = cast[ptr Worker](p)
-  signal(w.taskStarted)
-
-var gSomeReady = createFastCondVar()
-
-proc slave(w: ptr Worker) {.thread.} =
-  while true:
-    w.ready = true # If we instead signal "workerReady" we need the scheduler
-                   # to notice this. The scheduler could then optimize the
-                   # layout of the worker threads (e.g. keep the list sorted)
-                   # so that no search for a "ready" thread is necessary.
-                   # This might be implemented later, but is more tricky than
-                   # it looks because 'spawn' itself can run concurrently.
-    signal(gSomeReady)
-    await(w.taskArrived)
-    assert(not w.ready)
-    # shield against spurious wakeups:
-    if w.data != nil:
-      w.f(w, w.data)
-      w.data = nil
-
-const NumThreads = 4
-
-var
-  workers: array[NumThreads, Thread[ptr Worker]]
-  workersData: array[NumThreads, Worker]
-
-proc setup() =
-  for i in 0 ..< NumThreads:
-    workersData[i].taskArrived = createCondVar()
-    workersData[i].taskStarted = createFastCondVar()
-    createThread(workers[i], slave, addr(workersData[i]))
-
-proc preferSpawn*(): bool =
-  ## Use this proc to determine quickly if a 'spawn' or a direct call is
-  ## preferable. If it returns 'true' a 'spawn' may make sense. In general
-  ## it is not necessary to call this directly; use 'spawnX' instead.
-  result = gSomeReady.event
-
-proc spawn*(call: typed) {.magic: "Spawn".}
-  ## always spawns a new task, so that the 'call' is never executed on
-  ## the calling thread. 'call' has to be proc call 'p(...)' where 'p'
-  ## is gcsafe and has 'void' as the return type.
-
-template spawnX*(call: typed) =
-  ## spawns a new task if a CPU core is ready, otherwise executes the
-  ## call in the calling thread. Usually it is advised to
-  ## use 'spawn' in order to not block the producer for an unknown
-  ## amount of time. 'call' has to be proc call 'p(...)' where 'p'
-  ## is gcsafe and has 'void' as the return type.
-  if preferSpawn(): spawn call
-  else: call
-
-proc nimSpawn(fn: WorkerProc; data: pointer) {.compilerProc.} =
-  # implementation of 'spawn' that is used by the code generator.
-  while true:
-    for i in 0.. high(workers):
-      let w = addr(workersData[i])
-      if cas(addr w.ready, true, false):
-        w.data = data
-        w.f = fn
-        signal(w.taskArrived)
-        await(w.taskStarted)
-        return
-    await(gSomeReady)
-
-proc sync*() =
-  ## a simple barrier to wait for all spawn'ed tasks. If you need more elaborate
-  ## waiting, you have to use an explicit barrier.
-  while true:
-    var allReady = true
-    for i in 0 .. high(workers):
-      if not allReady: break
-      allReady = allReady and workersData[i].ready
-    if allReady: break
-    await(gSomeReady)
-
-setup()
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index 7b81f54da..3621c4960 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -15,42 +15,18 @@
 # we don't use refcounts because that's a behaviour
 # the programmer may not want
 
+
+proc dataPointer(a: PGenericSeq, elemAlign: int): pointer =
+  cast[pointer](cast[int](a) +% align(GenericSeqSize, elemAlign))
+
+proc dataPointer(a: PGenericSeq, elemAlign, elemSize, index: int): pointer =
+  cast[pointer](cast[int](a) +% align(GenericSeqSize, elemAlign) +% (index*%elemSize))
+
 proc resize(old: int): int {.inline.} =
   if old <= 0: result = 4
   elif old < 65536: result = old * 2
   else: result = old * 3 div 2 # for large arrays * 3/2 is better
 
-proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} =
-  if a == b: return 0
-  when defined(nimNoNil):
-    let alen = if a == nil: 0 else: a.len
-    let blen = if b == nil: 0 else: b.len
-  else:
-    if a == nil: return -1
-    if b == nil: return 1
-    let alen = a.len
-    let blen = b.len
-  let minlen = min(alen, blen)
-  if minlen > 0:
-    result = c_memcmp(addr a.data, addr b.data, minlen.csize)
-    if result == 0:
-      result = alen - blen
-  else:
-    result = alen - blen
-
-proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
-  if a == b: return true
-  when defined(nimNoNil):
-    let alen = if a == nil: 0 else: a.len
-    let blen = if b == nil: 0 else: b.len
-  else:
-    if a == nil or b == nil: return false
-    let alen = a.len
-    let blen = b.len
-  if alen == blen:
-    if alen == 0: return true
-    return equalMem(addr(a.data), addr(b.data), alen)
-
 when declared(allocAtomic):
   template allocStr(size: untyped): untyped =
     cast[NimString](allocAtomic(size))
@@ -71,46 +47,49 @@ else:
   template allocStrNoInit(size: untyped): untyped =
     cast[NimString](newObjNoInit(addr(strDesc), size))
 
-proc rawNewStringNoInit(space: int): NimString {.compilerProc.} =
-  var s = space
-  if s < 7: s = 7
+proc rawNewStringNoInit(space: int): NimString =
+  let s = max(space, 7)
   result = allocStrNoInit(sizeof(TGenericSeq) + s + 1)
   result.reserved = s
-  result.len = 0
   when defined(gogc):
     result.elemSize = 1
 
-proc rawNewString(space: int): NimString {.compilerProc.} =
-  var s = space
-  if s < 7: s = 7
-  result = allocStr(sizeof(TGenericSeq) + s + 1)
-  result.reserved = s
+proc rawNewString(space: int): NimString {.compilerproc.} =
+  result = rawNewStringNoInit(space)
   result.len = 0
-  when defined(gogc):
-    result.elemSize = 1
+  result.data[0] = '\0'
 
-proc mnewString(len: int): NimString {.compilerProc.} =
-  result = rawNewString(len)
+proc mnewString(len: int): NimString {.compilerproc.} =
+  result = rawNewStringNoInit(len)
   result.len = len
+  zeroMem(addr result.data[0], len + 1)
+
+proc copyStrLast(s: NimString, start, last: int): NimString {.compilerproc.} =
+  # This is not used by most recent versions of the compiler anymore, but
+  # required for bootstrapping purposes.
+  let start = max(start, 0)
+  if s == nil: return nil
+  let len = min(last, s.len-1) - start + 1
+  result = rawNewStringNoInit(len)
+  result.len = len
+  copyMem(addr(result.data), addr(s.data[start]), len)
+  result.data[len] = '\0'
 
-proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} =
-  var start = max(start, 0)
-  var len = min(last, s.len-1) - start + 1
-  if len > 0:
-    result = rawNewStringNoInit(len)
-    result.len = len
-    copyMem(addr(result.data), addr(s.data[start]), len)
-    result.data[len] = '\0'
-  else:
-    result = rawNewString(len)
-
-proc copyStr(s: NimString, start: int): NimString {.compilerProc.} =
+proc copyStr(s: NimString, start: int): NimString {.compilerproc.} =
+  # This is not used by most recent versions of the compiler anymore, but
+  # required for bootstrapping purposes.
+  if s == nil: return nil
   result = copyStrLast(s, start, s.len-1)
 
-proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} =
+proc nimToCStringConv(s: NimString): cstring {.compilerproc, nonReloadable, inline.} =
+  if s == nil or s.len == 0: result = cstring""
+  else: result = cast[cstring](addr s.data)
+
+proc toNimStr(str: cstring, len: int): NimString {.compilerproc.} =
   result = rawNewStringNoInit(len)
   result.len = len
-  copyMem(addr(result.data), str, len + 1)
+  copyMem(addr(result.data), str, len)
+  result.data[len] = '\0'
 
 proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} =
   if str == nil: NimString(nil)
@@ -137,20 +116,27 @@ proc newOwnedString(src: NimString; n: int): NimString =
 
 proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
   if src != nil:
-    when declared(newObjRC1) and not defined(gcRegions):
-      var s = src.len
-      if s < 7: s = 7
-      result = cast[NimString](newObjRC1(addr(strDesc), sizeof(TGenericSeq) +
-                               s+1))
-      result.reserved = s
+    if (src.reserved and seqShallowFlag) != 0:
+      result = src
+      when declared(incRef):
+        incRef(usrToCell(result))
     else:
-      result = rawNewStringNoInit(src.len)
-    result.len = src.len
-    copyMem(addr(result.data), addr(src.data), src.len + 1)
-    sysAssert((seqShallowFlag and result.reserved) == 0, "copyStringRC1")
-    when defined(nimShallowStrings):
-      if (src.reserved and strlitFlag) != 0:
-        result.reserved = (result.reserved and not strlitFlag) or seqShallowFlag
+      when declared(newObjRC1) and not defined(gcRegions):
+        var s = src.len
+        if s < 7: s = 7
+        result = cast[NimString](newObjRC1(addr(strDesc), sizeof(TGenericSeq) +
+                                s+1))
+        result.reserved = s
+        when defined(gogc):
+          result.elemSize = 1
+      else:
+        result = rawNewStringNoInit(src.len)
+      result.len = src.len
+      copyMem(addr(result.data), addr(src.data), src.len + 1)
+      sysAssert((seqShallowFlag and result.reserved) == 0, "copyStringRC1")
+      when defined(nimShallowStrings):
+        if (src.reserved and strlitFlag) != 0:
+          result.reserved = (result.reserved and not strlitFlag) or seqShallowFlag
 
 proc copyDeepString(src: NimString): NimString {.inline.} =
   if src != nil:
@@ -158,19 +144,6 @@ proc copyDeepString(src: NimString): NimString {.inline.} =
     result.len = src.len
     copyMem(addr(result.data), addr(src.data), src.len + 1)
 
-proc hashString(s: string): int {.compilerproc.} =
-  # the compiler needs exactly the same hash function!
-  # this used to be used for efficient generation of string case statements
-  var h = 0
-  for i in 0..len(s)-1:
-    h = h +% ord(s[i])
-    h = h +% h shl 10
-    h = h xor (h shr 6)
-  h = h +% h shl 3
-  h = h xor (h shr 11)
-  h = h +% h shl 15
-  result = h
-
 proc addChar(s: NimString, c: char): NimString =
   # is compilerproc!
   if s == nil:
@@ -180,8 +153,9 @@ proc addChar(s: NimString, c: char): NimString =
     result = s
     if result.len >= result.space:
       let r = resize(result.space)
-      result = cast[NimString](growObj(result,
-        sizeof(TGenericSeq) + r + 1))
+      result = rawNewStringNoInit(r)
+      result.len = s.len
+      copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1)
       result.reserved = r
   result.data[result.len] = c
   result.data[result.len+1] = '\0'
@@ -220,12 +194,14 @@ proc addChar(s: NimString, c: char): NimString =
 
 proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
   if dest == nil:
-    result = rawNewStringNoInit(addlen)
+    result = rawNewString(addlen)
   elif dest.len + addlen <= dest.space:
     result = dest
   else: # slow path:
-    var sp = max(resize(dest.space), dest.len + addlen)
-    result = cast[NimString](growObj(dest, sizeof(TGenericSeq) + sp + 1))
+    let sp = max(resize(dest.space), dest.len + addlen)
+    result = rawNewStringNoInit(sp)
+    result.len = dest.len
+    copyMem(addr result.data[0], unsafeAddr(dest.data[0]), dest.len+1)
     result.reserved = sp
     #result = rawNewString(sp)
     #copyMem(result, dest, dest.len + sizeof(TGenericSeq))
@@ -242,19 +218,27 @@ proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} =
   inc(dest.len)
 
 proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
-  var n = max(newLen, 0)
+  let n = max(newLen, 0)
   if s == nil:
-    result = mnewString(newLen)
+    if n == 0:
+      return s
+    else:
+      result = mnewString(n)
   elif n <= s.space:
     result = s
   else:
-    result = resizeString(s, n)
+    let sp = max(resize(s.space), n)
+    result = rawNewStringNoInit(sp)
+    result.len = s.len
+    copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len)
+    zeroMem(addr result.data[s.len], n - s.len)
+    result.reserved = sp
   result.len = n
   result.data[n] = '\0'
 
 # ----------------- sequences ----------------------------------------------
 
-proc incrSeq(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
+proc incrSeq(seq: PGenericSeq, elemSize, elemAlign: int): PGenericSeq {.compilerproc.} =
   # increments the length by one:
   # this is needed for supporting ``add``;
   #
@@ -264,21 +248,19 @@ proc incrSeq(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
   result = seq
   if result.len >= result.space:
     let r = resize(result.space)
-    result = cast[PGenericSeq](growObj(result, elemSize * r +
-                               GenericSeqSize))
+    result = cast[PGenericSeq](growObj(result, align(GenericSeqSize, elemAlign) + elemSize * r))
     result.reserved = r
   inc(result.len)
 
-proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
+proc incrSeqV2(seq: PGenericSeq, elemSize, elemAlign: int): PGenericSeq {.compilerproc.} =
   # incrSeq version 2
   result = seq
   if result.len >= result.space:
     let r = resize(result.space)
-    result = cast[PGenericSeq](growObj(result, elemSize * r +
-                               GenericSeqSize))
+    result = cast[PGenericSeq](growObj(result, align(GenericSeqSize, elemAlign) + elemSize * r))
     result.reserved = r
 
-proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} =
+proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerproc.} =
   if s == nil:
     result = cast[PGenericSeq](newSeq(typ, 1))
     result.len = 0
@@ -286,293 +268,96 @@ proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} =
     result = s
     if result.len >= result.space:
       let r = resize(result.space)
-      result = cast[PGenericSeq](growObj(result, typ.base.size * r +
-                                GenericSeqSize))
-      result.reserved = r
+      result = cast[PGenericSeq](newSeq(typ, r))
+      result.len = s.len
+      copyMem(dataPointer(result, typ.base.align), dataPointer(s, typ.base.align), s.len * typ.base.size)
+      # since we steal the content from 's', it's crucial to set s's len to 0.
+      s.len = 0
 
-proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
+proc setLengthSeq(seq: PGenericSeq, elemSize, elemAlign, newLen: int): PGenericSeq {.
     compilerRtl, inl.} =
   result = seq
   if result.space < newLen:
     let r = max(resize(result.space), newLen)
-    result = cast[PGenericSeq](growObj(result, elemSize * r +
-                               GenericSeqSize))
+    result = cast[PGenericSeq](growObj(result, align(GenericSeqSize, elemAlign) + elemSize * r))
     result.reserved = r
   elif newLen < result.len:
     # we need to decref here, otherwise the GC leaks!
     when not defined(boehmGC) and not defined(nogc) and
          not defined(gcMarkAndSweep) and not defined(gogc) and
          not defined(gcRegions):
-      when false: # compileOption("gc", "v2"):
+      if ntfNoRefs notin extGetCellType(result).base.flags:
         for i in newLen..result.len-1:
-          let len0 = gch.tempStack.len
-          forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +%
-                            GenericSeqSize +% (i*%elemSize)),
-                            extGetCellType(result).base, waPush)
-          let len1 = gch.tempStack.len
-          for i in len0 ..< len1:
-            doDecRef(gch.tempStack.d[i], LocalHeap, MaybeCyclic)
-          gch.tempStack.len = len0
-      else:
-        if ntfNoRefs notin extGetCellType(result).base.flags:
-          for i in newLen..result.len-1:
-            forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +%
-                              GenericSeqSize +% (i*%elemSize)),
-                              extGetCellType(result).base, waZctDecRef)
+          forAllChildrenAux(dataPointer(result, elemAlign, elemSize, i),
+                            extGetCellType(result).base, waZctDecRef)
 
     # XXX: zeroing out the memory can still result in crashes if a wiped-out
     # cell is aliased by another pointer (ie proc parameter or a let variable).
     # This is a tough problem, because even if we don't zeroMem here, in the
     # presence of user defined destructors, the user will expect the cell to be
-    # "destroyed" thus creating the same problem. We can destoy the cell in the
+    # "destroyed" thus creating the same problem. We can destroy the cell in the
     # finalizer of the sequence, but this makes destruction non-deterministic.
-    zeroMem(cast[pointer](cast[ByteAddress](result) +% GenericSeqSize +%
-           (newLen*%elemSize)), (result.len-%newLen) *% elemSize)
+    zeroMem(dataPointer(result, elemAlign, elemSize, newLen), (result.len-%newLen) *% elemSize)
   result.len = newLen
 
 proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
     compilerRtl.} =
+  sysAssert typ.kind == tySequence, "setLengthSeqV2: type is not a seq"
   if s == nil:
-    result = cast[PGenericSeq](newSeq(typ, newLen))
-  else:
-    result = setLengthSeq(s, typ.base.size, newLen)
-
-# --------------- other string routines ----------------------------------
-proc add*(result: var string; x: int64) =
-  let base = result.len
-  setLen(result, base + sizeof(x)*4)
-  var i = 0
-  var y = x
-  while true:
-    var d = y div 10
-    result[base+i] = chr(abs(int(y - d*10)) + ord('0'))
-    inc(i)
-    y = d
-    if y == 0: break
-  if x < 0:
-    result[base+i] = '-'
-    inc(i)
-  setLen(result, base+i)
-  # mirror the string:
-  for j in 0..i div 2 - 1:
-    swap(result[base+j], result[base+i-j-1])
-
-proc nimIntToStr(x: int): string {.compilerRtl.} =
-  result = newStringOfCap(sizeof(x)*4)
-  result.add x
-
-proc add*(result: var string; x: float) =
-  when nimvm:
-    result.add $x
-  else:
-    var buf: array[0..64, char]
-    when defined(nimNoArrayToCstringConversion):
-      var n: int = c_sprintf(addr buf, "%.16g", x)
-    else:
-      var n: int = c_sprintf(buf, "%.16g", x)
-    var hasDot = false
-    for i in 0..n-1:
-      if buf[i] == ',':
-        buf[i] = '.'
-        hasDot = true
-      elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
-        hasDot = true
-    if not hasDot:
-      buf[n] = '.'
-      buf[n+1] = '0'
-      buf[n+2] = '\0'
-    # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN'
-    # of '-1.#IND' are produced.
-    # We want to get rid of these here:
-    if buf[n-1] in {'n', 'N', 'D', 'd'}:
-      result.add "nan"
-    elif buf[n-1] == 'F':
-      if buf[0] == '-':
-        result.add "-inf"
-      else:
-        result.add "inf"
-    else:
-      var i = 0
-      while buf[i] != '\0':
-        result.add buf[i]
-        inc i
-
-proc nimFloatToStr(f: float): string {.compilerproc.} =
-  result = newStringOfCap(8)
-  result.add f
-
-proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {.
-  importc: "strtod", header: "<stdlib.h>", noSideEffect.}
-
-const
-  IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
-  powtens =  [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
-              1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
-              1e20, 1e21, 1e22]
-
-proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
-                          start = 0): int {.compilerProc.} =
-  # This routine attempt to parse float that can parsed quickly.
-  # ie whose integer part can fit inside a 53bits integer.
-  # their real exponent must also be <= 22. If the float doesn't follow
-  # these restrictions, transform the float into this form:
-  #  INTEGER * 10 ^ exponent and leave the work to standard `strtod()`.
-  # This avoid the problems of decimal character portability.
-  # see: http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
-  var
-    i = start
-    sign = 1.0
-    kdigits, fdigits = 0
-    exponent: int
-    integer: uint64
-    frac_exponent = 0
-    exp_sign = 1
-    first_digit = -1
-    has_sign = false
-
-  # Sign?
-  if s[i] == '+' or s[i] == '-':
-    has_sign = true
-    if s[i] == '-':
-      sign = -1.0
-    inc(i)
-
-  # NaN?
-  if s[i] == 'N' or s[i] == 'n':
-    if s[i+1] == 'A' or s[i+1] == 'a':
-      if s[i+2] == 'N' or s[i+2] == 'n':
-        if s[i+3] notin IdentChars:
-          number = NaN
-          return i+3 - start
-    return 0
-
-  # Inf?
-  if s[i] == 'I' or s[i] == 'i':
-    if s[i+1] == 'N' or s[i+1] == 'n':
-      if s[i+2] == 'F' or s[i+2] == 'f':
-        if s[i+3] notin IdentChars:
-          number = Inf*sign
-          return i+3 - start
-    return 0
-
-  if s[i] in {'0'..'9'}:
-    first_digit = (s[i].ord - '0'.ord)
-  # Integer part?
-  while s[i] in {'0'..'9'}:
-    inc(kdigits)
-    integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
-    inc(i)
-    while s[i] == '_': inc(i)
-
-  # Fractional part?
-  if s[i] == '.':
-    inc(i)
-    # if no integer part, Skip leading zeros
-    if kdigits <= 0:
-      while s[i] == '0':
-        inc(frac_exponent)
-        inc(i)
-        while s[i] == '_': inc(i)
-
-    if first_digit == -1 and s[i] in {'0'..'9'}:
-      first_digit = (s[i].ord - '0'.ord)
-    # get fractional part
-    while s[i] in {'0'..'9'}:
-      inc(fdigits)
-      inc(frac_exponent)
-      integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
-      inc(i)
-      while s[i] == '_': inc(i)
-
-  # if has no digits: return error
-  if kdigits + fdigits <= 0 and
-     (i == start or # no char consumed (empty string).
-     (i == start + 1 and has_sign)): # or only '+' or '-
-    return 0
-
-  if s[i] in {'e', 'E'}:
-    inc(i)
-    if s[i] == '+' or s[i] == '-':
-      if s[i] == '-':
-        exp_sign = -1
-
-      inc(i)
-    if s[i] notin {'0'..'9'}:
-      return 0
-    while s[i] in {'0'..'9'}:
-      exponent = exponent * 10 + (ord(s[i]) - ord('0'))
-      inc(i)
-      while s[i] == '_': inc(i) # underscores are allowed and ignored
-
-  var real_exponent = exp_sign*exponent - frac_exponent
-  let exp_negative = real_exponent < 0
-  var abs_exponent = abs(real_exponent)
-
-  # if exponent greater than can be represented: +/- zero or infinity
-  if abs_exponent > 999:
-    if exp_negative:
-      number = 0.0*sign
+    if newLen == 0:
+      result = s
     else:
-      number = Inf*sign
-    return i - start
-
-  # if integer is representable in 53 bits:  fast path
-  # max fast path integer is  1<<53 - 1 or  8999999999999999 (16 digits)
-  let digits = kdigits + fdigits
-  if digits <= 15 or (digits <= 16 and first_digit <= 8):
-    # max float power of ten with set bits above the 53th bit is 10^22
-    if abs_exponent <= 22:
-      if exp_negative:
-        number = sign * integer.float / powtens[abs_exponent]
-      else:
-        number = sign * integer.float * powtens[abs_exponent]
-      return i - start
-
-    # if exponent is greater try to fit extra exponent above 22 by multiplying
-    # integer part is there is space left.
-    let slop = 15 - kdigits - fdigits
-    if  abs_exponent <= 22 + slop and not exp_negative:
-      number = sign * integer.float * powtens[slop] * powtens[abs_exponent-slop]
-      return i - start
-
-  # if failed: slow path with strtod.
-  var t: array[500, char] # flaviu says: 325 is the longest reasonable literal
-  var ti = 0
-  let maxlen = t.high - "e+000".len # reserve enough space for exponent
-
-  result = i - start
-  i = start
-  # re-parse without error checking, any error should be handled by the code above.
-  if s[i] == '.': i.inc
-  while s[i] in {'0'..'9','+','-'}:
-    if ti < maxlen:
-      t[ti] = s[i]; inc(ti)
-    inc(i)
-    while s[i] in {'.', '_'}: # skip underscore and decimal point
-      inc(i)
-
-  # insert exponent
-  t[ti] = 'E'; inc(ti)
-  t[ti] = (if exp_negative: '-' else: '+'); inc(ti)
-  inc(ti, 3)
-
-  # insert adjusted exponent
-  t[ti-1] = ('0'.ord + abs_exponent mod 10).char; abs_exponent = abs_exponent div 10
-  t[ti-2] = ('0'.ord + abs_exponent mod 10).char; abs_exponent = abs_exponent div 10
-  t[ti-3] = ('0'.ord + abs_exponent mod 10).char
-
-  when defined(nimNoArrayToCstringConversion):
-    number = c_strtod(addr t, nil)
+      result = cast[PGenericSeq](newSeq(typ, newLen))
   else:
-    number = c_strtod(t, nil)
-
-proc nimInt64ToStr(x: int64): string {.compilerRtl.} =
-  result = newStringOfCap(sizeof(x)*4)
-  result.add x
-
-proc nimBoolToStr(x: bool): string {.compilerRtl.} =
-  return if x: "true" else: "false"
+    let elemSize = typ.base.size
+    let elemAlign = typ.base.align
+    if s.space < newLen:
+      let r = max(resize(s.space), newLen)
+      result = cast[PGenericSeq](newSeq(typ, r))
+      copyMem(dataPointer(result, elemAlign), dataPointer(s, elemAlign), s.len * elemSize)
+      # since we steal the content from 's', it's crucial to set s's len to 0.
+      s.len = 0
+    elif newLen < s.len:
+      result = s
+      # we need to decref here, otherwise the GC leaks!
+      when not defined(boehmGC) and not defined(nogc) and
+          not defined(gcMarkAndSweep) and not defined(gogc) and
+          not defined(gcRegions):
+        if ntfNoRefs notin typ.base.flags:
+          for i in newLen..result.len-1:
+            forAllChildrenAux(dataPointer(result, elemAlign, elemSize, i),
+                              extGetCellType(result).base, waZctDecRef)
 
-proc nimCharToStr(x: char): string {.compilerRtl.} =
-  result = newString(1)
-  result[0] = x
+      # XXX: zeroing out the memory can still result in crashes if a wiped-out
+      # cell is aliased by another pointer (ie proc parameter or a let variable).
+      # This is a tough problem, because even if we don't zeroMem here, in the
+      # presence of user defined destructors, the user will expect the cell to be
+      # "destroyed" thus creating the same problem. We can destroy the cell in the
+      # finalizer of the sequence, but this makes destruction non-deterministic.
+      zeroMem(dataPointer(result, elemAlign, elemSize, newLen), (result.len-%newLen) *% elemSize)
+    else:
+      result = s
+      zeroMem(dataPointer(result, elemAlign, elemSize, result.len), (newLen-%result.len) *% elemSize)
+    result.len = newLen
+
+func capacity*(self: string): int {.inline.} =
+  ## Returns the current capacity of the string.
+  # See https://github.com/nim-lang/RFCs/issues/460
+  runnableExamples:
+    var str = newStringOfCap(cap = 42)
+    str.add "Nim"
+    assert str.capacity == 42
+
+  let str = cast[NimString](self)
+  result = if str != nil: str.space else: 0
+
+func capacity*[T](self: seq[T]): int {.inline.} =
+  ## Returns the current capacity of the seq.
+  # See https://github.com/nim-lang/RFCs/issues/460
+  runnableExamples:
+    var lst = newSeqOfCap[string](cap = 42)
+    lst.add "Nim"
+    assert lst.capacity == 42
+
+  let sek = cast[PGenericSeq](self)
+  result = if sek != nil: sek.space else: 0
diff --git a/lib/system/threadids.nim b/lib/system/threadids.nim
new file mode 100644
index 000000000..3a6eadcbb
--- /dev/null
+++ b/lib/system/threadids.nim
@@ -0,0 +1,103 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# we need to cache current threadId to not perform syscall all the time
+var threadId {.threadvar.}: int
+
+when defined(windows):
+  proc getCurrentThreadId(): int32 {.
+    stdcall, dynlib: "kernel32", importc: "GetCurrentThreadId".}
+
+  proc getThreadId*(): int =
+    ## Gets the ID of the currently running thread.
+    if threadId == 0:
+      threadId = int(getCurrentThreadId())
+    result = threadId
+
+elif defined(linux):
+  proc syscall(arg: clong): clong {.varargs, importc: "syscall", header: "<unistd.h>".}
+  when defined(amd64):
+    const NR_gettid = clong(186)
+  else:
+    var NR_gettid {.importc: "__NR_gettid", header: "<sys/syscall.h>".}: clong
+
+  proc getThreadId*(): int =
+    ## Gets the ID of the currently running thread.
+    if threadId == 0:
+      threadId = int(syscall(NR_gettid))
+    result = threadId
+
+elif defined(dragonfly):
+  proc lwp_gettid(): int32 {.importc, header: "unistd.h".}
+
+  proc getThreadId*(): int =
+    ## Gets the ID of the currently running thread.
+    if threadId == 0:
+      threadId = int(lwp_gettid())
+    result = threadId
+
+elif defined(openbsd):
+  proc getthrid(): int32 {.importc: "getthrid", header: "<unistd.h>".}
+
+  proc getThreadId*(): int =
+    ## Gets the ID of the currently running thread.
+    if threadId == 0:
+      threadId = int(getthrid())
+    result = threadId
+
+elif defined(netbsd):
+  proc lwp_self(): int32 {.importc: "_lwp_self", header: "<lwp.h>".}
+
+  proc getThreadId*(): int =
+    ## Gets the ID of the currently running thread.
+    if threadId == 0:
+      threadId = int(lwp_self())
+    result = threadId
+
+elif defined(freebsd):
+  proc syscall(arg: cint, arg0: ptr cint): cint {.varargs, importc: "syscall", header: "<unistd.h>".}
+  var SYS_thr_self {.importc:"SYS_thr_self", header:"<sys/syscall.h>".}: cint
+
+  proc getThreadId*(): int =
+    ## Gets the ID of the currently running thread.
+    var tid = 0.cint
+    if threadId == 0:
+      discard syscall(SYS_thr_self, addr tid)
+      threadId = tid
+    result = threadId
+
+elif defined(macosx):
+  proc syscall(arg: cint): cint {.varargs, importc: "syscall", header: "<unistd.h>".}
+  var SYS_thread_selfid {.importc:"SYS_thread_selfid", header:"<sys/syscall.h>".}: cint
+
+  proc getThreadId*(): int =
+    ## Gets the ID of the currently running thread.
+    if threadId == 0:
+      threadId = int(syscall(SYS_thread_selfid))
+    result = threadId
+
+elif defined(solaris):
+  type thread_t {.importc: "thread_t", header: "<thread.h>".} = distinct int
+  proc thr_self(): thread_t {.importc, header: "<thread.h>".}
+
+  proc getThreadId*(): int =
+    ## Gets the ID of the currently running thread.
+    if threadId == 0:
+      threadId = int(thr_self())
+    result = threadId
+
+elif defined(haiku):
+  type thr_id {.importc: "thread_id", header: "<OS.h>".} = distinct int32
+  proc find_thread(name: cstring): thr_id {.importc, header: "<OS.h>".}
+
+  proc getThreadId*(): int =
+    ## Gets the ID of the currently running thread.
+    if threadId == 0:
+      threadId = int(find_thread(nil))
+    result = threadId
diff --git a/lib/system/threadimpl.nim b/lib/system/threadimpl.nim
new file mode 100644
index 000000000..285b8f5e7
--- /dev/null
+++ b/lib/system/threadimpl.nim
@@ -0,0 +1,111 @@
+var
+  nimThreadDestructionHandlers* {.rtlThreadVar.}: seq[proc () {.closure, gcsafe, raises: [].}]
+when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcRegions):
+  proc deallocOsPages() {.rtl, raises: [].}
+proc threadTrouble() {.raises: [], gcsafe.}
+# create for the main thread. Note: do not insert this data into the list
+# of all threads; it's not to be stopped etc.
+when not defined(useNimRtl):
+  #when not defined(createNimRtl): initStackBottom()
+  when declared(initGC):
+    initGC()
+    when not emulatedThreadVars:
+      type ThreadType {.pure.} = enum
+        None = 0,
+        NimThread = 1,
+        ForeignThread = 2
+      var
+        threadType {.rtlThreadVar.}: ThreadType
+
+      threadType = ThreadType.NimThread
+
+when defined(gcDestructors):
+  proc deallocThreadStorage(p: pointer) = c_free(p)
+else:
+  template deallocThreadStorage(p: pointer) = deallocShared(p)
+
+template afterThreadRuns() =
+  for i in countdown(nimThreadDestructionHandlers.len-1, 0):
+    nimThreadDestructionHandlers[i]()
+
+proc onThreadDestruction*(handler: proc () {.closure, gcsafe, raises: [].}) =
+  ## Registers a *thread local* handler that is called at the thread's
+  ## destruction.
+  ##
+  ## A thread is destructed when the `.thread` proc returns
+  ## normally or when it raises an exception. Note that unhandled exceptions
+  ## in a thread nevertheless cause the whole process to die.
+  nimThreadDestructionHandlers.add handler
+
+when defined(boehmgc):
+  type GCStackBaseProc = proc(sb: pointer, t: pointer) {.noconv.}
+  proc boehmGC_call_with_stack_base(sbp: GCStackBaseProc, p: pointer)
+    {.importc: "GC_call_with_stack_base", boehmGC.}
+  proc boehmGC_register_my_thread(sb: pointer)
+    {.importc: "GC_register_my_thread", boehmGC.}
+  proc boehmGC_unregister_my_thread()
+    {.importc: "GC_unregister_my_thread", boehmGC.}
+
+  proc threadProcWrapDispatch[TArg](sb: pointer, thrd: pointer) {.noconv, raises: [].} =
+    boehmGC_register_my_thread(sb)
+    try:
+      let thrd = cast[ptr Thread[TArg]](thrd)
+      when TArg is void:
+        thrd.dataFn()
+      else:
+        thrd.dataFn(thrd.data)
+    except:
+      threadTrouble()
+    finally:
+      afterThreadRuns()
+    boehmGC_unregister_my_thread()
+else:
+  proc threadProcWrapDispatch[TArg](thrd: ptr Thread[TArg]) {.raises: [].} =
+    try:
+      when TArg is void:
+        thrd.dataFn()
+      else:
+        when defined(nimV2):
+          thrd.dataFn(thrd.data)
+        else:
+          var x: TArg
+          deepCopy(x, thrd.data)
+          thrd.dataFn(x)
+    except:
+      threadTrouble()
+    finally:
+      afterThreadRuns()
+      when hasAllocStack:
+        deallocThreadStorage(thrd.rawStack)
+
+proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) {.raises: [].} =
+  when defined(boehmgc):
+    boehmGC_call_with_stack_base(threadProcWrapDispatch[TArg], thrd)
+  elif not defined(nogc) and not defined(gogc) and not defined(gcRegions) and not usesDestructors:
+    var p {.volatile.}: pointer
+    # init the GC for refc/markandsweep
+    nimGC_setStackBottom(addr(p))
+    when declared(initGC):
+      initGC()
+    when declared(threadType):
+      threadType = ThreadType.NimThread
+    threadProcWrapDispatch[TArg](thrd)
+    when declared(deallocOsPages): deallocOsPages()
+  else:
+    threadProcWrapDispatch(thrd)
+
+template nimThreadProcWrapperBody*(closure: untyped): untyped =
+  var thrd = cast[ptr Thread[TArg]](closure)
+  var core = thrd.core
+  when declared(globalsSlot): threadVarSetValue(globalsSlot, thrd.core)
+  threadProcWrapStackFrame(thrd)
+  # Since an unhandled exception terminates the whole process (!), there is
+  # no need for a ``try finally`` here, nor would it be correct: The current
+  # exception is tried to be re-raised by the code-gen after the ``finally``!
+  # However this is doomed to fail, because we already unmapped every heap
+  # page!
+
+  # mark as not running anymore:
+  thrd.core = nil
+  thrd.dataFn = nil
+  deallocThreadStorage(cast[pointer](core))
diff --git a/lib/system/threadlocalstorage.nim b/lib/system/threadlocalstorage.nim
new file mode 100644
index 000000000..e6ad9dca5
--- /dev/null
+++ b/lib/system/threadlocalstorage.nim
@@ -0,0 +1,125 @@
+import std/private/threadtypes
+
+when defined(windows):
+  type
+    ThreadVarSlot = distinct int32
+
+  proc threadVarAlloc(): ThreadVarSlot {.
+    importc: "TlsAlloc", stdcall, header: "<windows.h>".}
+  proc threadVarSetValue(dwTlsIndex: ThreadVarSlot, lpTlsValue: pointer) {.
+    importc: "TlsSetValue", stdcall, header: "<windows.h>".}
+  proc tlsGetValue(dwTlsIndex: ThreadVarSlot): pointer {.
+    importc: "TlsGetValue", stdcall, header: "<windows.h>".}
+
+  proc getLastError(): uint32 {.
+    importc: "GetLastError", stdcall, header: "<windows.h>".}
+  proc setLastError(x: uint32) {.
+    importc: "SetLastError", stdcall, header: "<windows.h>".}
+
+  proc threadVarGetValue(dwTlsIndex: ThreadVarSlot): pointer =
+    let realLastError = getLastError()
+    result = tlsGetValue(dwTlsIndex)
+    setLastError(realLastError)
+
+elif defined(genode):
+  const
+    GenodeHeader = "genode_cpp/threads.h"
+
+  type
+    ThreadVarSlot = int
+
+  proc threadVarAlloc(): ThreadVarSlot = 0
+
+  proc offMainThread(): bool {.
+    importcpp: "Nim::SysThread::offMainThread",
+    header: GenodeHeader.}
+
+  proc threadVarSetValue(value: pointer) {.
+    importcpp: "Nim::SysThread::threadVarSetValue(@)",
+    header: GenodeHeader.}
+
+  proc threadVarGetValue(): pointer {.
+    importcpp: "Nim::SysThread::threadVarGetValue()",
+    header: GenodeHeader.}
+
+  var mainTls: pointer
+
+  proc threadVarSetValue(s: ThreadVarSlot, value: pointer) {.inline.} =
+    if offMainThread():
+      threadVarSetValue(value);
+    else:
+      mainTls = value
+
+  proc threadVarGetValue(s: ThreadVarSlot): pointer {.inline.} =
+    if offMainThread():
+      threadVarGetValue();
+    else:
+      mainTls
+
+else:
+  when not (defined(macosx) or defined(haiku)):
+    {.passl: "-pthread".}
+
+  when not defined(haiku):
+    {.passc: "-pthread".}
+
+  when (defined(linux) or defined(nintendoswitch)) and defined(amd64):
+    type
+      ThreadVarSlot {.importc: "pthread_key_t",
+                    header: "<sys/types.h>".} = distinct cuint
+  elif defined(openbsd) and defined(amd64):
+    type
+      ThreadVarSlot {.importc: "pthread_key_t",
+                     header: "<pthread.h>".} = cint
+  else:
+    type
+      ThreadVarSlot {.importc: "pthread_key_t",
+                     header: "<sys/types.h>".} = object
+
+  proc pthread_getspecific(a1: ThreadVarSlot): pointer {.
+    importc: "pthread_getspecific", header: pthreadh.}
+  proc pthread_key_create(a1: ptr ThreadVarSlot,
+                          destruct: proc (x: pointer) {.noconv.}): int32 {.
+    importc: "pthread_key_create", header: pthreadh.}
+  proc pthread_key_delete(a1: ThreadVarSlot): int32 {.
+    importc: "pthread_key_delete", header: pthreadh.}
+
+  proc pthread_setspecific(a1: ThreadVarSlot, a2: pointer): int32 {.
+    importc: "pthread_setspecific", header: pthreadh.}
+
+  proc threadVarAlloc(): ThreadVarSlot {.inline.} =
+    discard pthread_key_create(addr(result), nil)
+  proc threadVarSetValue(s: ThreadVarSlot, value: pointer) {.inline.} =
+    discard pthread_setspecific(s, value)
+  proc threadVarGetValue(s: ThreadVarSlot): pointer {.inline.} =
+    result = pthread_getspecific(s)
+
+
+when emulatedThreadVars:
+  # the compiler generates this proc for us, so that we can get the size of
+  # the thread local var block; we use this only for sanity checking though
+  proc nimThreadVarsSize(): int {.noconv, importc: "NimThreadVarsSize".}
+
+
+
+when emulatedThreadVars:
+  var globalsSlot: ThreadVarSlot
+
+  when not defined(useNimRtl):
+    var mainThread: GcThread
+
+  proc GetThreadLocalVars(): pointer {.compilerRtl, inl.} =
+    result = addr(cast[PGcThread](threadVarGetValue(globalsSlot)).tls)
+
+  proc initThreadVarsEmulation() {.compilerproc, inline.} =
+    when not defined(useNimRtl):
+      globalsSlot = threadVarAlloc()
+      when declared(mainThread):
+        threadVarSetValue(globalsSlot, addr(mainThread))
+
+when not defined(useNimRtl):
+  when emulatedThreadVars:
+    if nimThreadVarsSize() > sizeof(ThreadLocalStorage):
+      c_fprintf(cstderr, """too large thread local storage size requested,
+use -d:\"nimTlsSize=X\" to setup even more or stop using unittest.nim""")
+      rawQuit 1
diff --git a/lib/system/threads.nim b/lib/system/threads.nim
deleted file mode 100644
index c8ea03f92..000000000
--- a/lib/system/threads.nim
+++ /dev/null
@@ -1,716 +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.
-#
-
-## Thread support for Nim. **Note**: This is part of the system module.
-## Do not import it directly. To activate thread support you need to compile
-## with the ``--threads:on`` command line switch.
-##
-## Nim's memory model for threads is quite different from other common
-## programming languages (C, Pascal): Each thread has its own
-## (garbage collected) heap and sharing of memory is restricted. This helps
-## to prevent race conditions and improves efficiency. See `the manual for
-## details of this memory model <manual.html#threads>`_.
-##
-## Example:
-##
-## .. code-block:: Nim
-##
-##  import locks
-##
-##  var
-##    thr: array[0..4, Thread[tuple[a,b: int]]]
-##    L: Lock
-##
-##  proc threadFunc(interval: tuple[a,b: int]) {.thread.} =
-##    for i in interval.a..interval.b:
-##      acquire(L) # lock stdout
-##      echo i
-##      release(L)
-##
-##  initLock(L)
-##
-##  for i in 0..high(thr):
-##    createThread(thr[i], threadFunc, (i*10, i*10+5))
-##  joinThreads(thr)
-
-when not declared(NimString):
-  {.error: "You must not import this module explicitly".}
-
-const
-  maxRegisters = 256 # don't think there is an arch with more registers
-  useStackMaskHack = false ## use the stack mask hack for better performance
-  StackGuardSize = 4096
-  ThreadStackMask =
-    when defined(genode):
-      1024*64*sizeof(int)-1
-    else:
-      1024*256*sizeof(int)-1
-  ThreadStackSize = ThreadStackMask+1 - StackGuardSize
-
-when defined(windows):
-  type
-    SysThread* = Handle
-    WinThreadProc = proc (x: pointer): int32 {.stdcall.}
-  {.deprecated: [TSysThread: SysThread].}
-
-  proc createThread(lpThreadAttributes: pointer, dwStackSize: int32,
-                     lpStartAddress: WinThreadProc,
-                     lpParameter: pointer,
-                     dwCreationFlags: int32,
-                     lpThreadId: var int32): SysThread {.
-    stdcall, dynlib: "kernel32", importc: "CreateThread".}
-
-  proc winSuspendThread(hThread: SysThread): int32 {.
-    stdcall, dynlib: "kernel32", importc: "SuspendThread".}
-
-  proc winResumeThread(hThread: SysThread): int32 {.
-    stdcall, dynlib: "kernel32", importc: "ResumeThread".}
-
-  proc waitForMultipleObjects(nCount: int32,
-                              lpHandles: ptr SysThread,
-                              bWaitAll: int32,
-                              dwMilliseconds: int32): int32 {.
-    stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".}
-
-  proc terminateThread(hThread: SysThread, dwExitCode: int32): int32 {.
-    stdcall, dynlib: "kernel32", importc: "TerminateThread".}
-
-  proc getCurrentThreadId(): int32 {.
-    stdcall, dynlib: "kernel32", importc: "GetCurrentThreadId".}
-
-  type
-    ThreadVarSlot = distinct int32
-
-  when true:
-    proc threadVarAlloc(): ThreadVarSlot {.
-      importc: "TlsAlloc", stdcall, header: "<windows.h>".}
-    proc threadVarSetValue(dwTlsIndex: ThreadVarSlot, lpTlsValue: pointer) {.
-      importc: "TlsSetValue", stdcall, header: "<windows.h>".}
-    proc tlsGetValue(dwTlsIndex: ThreadVarSlot): pointer {.
-      importc: "TlsGetValue", stdcall, header: "<windows.h>".}
-
-    proc getLastError(): uint32 {.
-      importc: "GetLastError", stdcall, header: "<windows.h>".}
-    proc setLastError(x: uint32) {.
-      importc: "SetLastError", stdcall, header: "<windows.h>".}
-
-    proc threadVarGetValue(dwTlsIndex: ThreadVarSlot): pointer =
-      let realLastError = getLastError()
-      result = tlsGetValue(dwTlsIndex)
-      setLastError(realLastError)
-  else:
-    proc threadVarAlloc(): ThreadVarSlot {.
-      importc: "TlsAlloc", stdcall, dynlib: "kernel32".}
-    proc threadVarSetValue(dwTlsIndex: ThreadVarSlot, lpTlsValue: pointer) {.
-      importc: "TlsSetValue", stdcall, dynlib: "kernel32".}
-    proc threadVarGetValue(dwTlsIndex: ThreadVarSlot): pointer {.
-      importc: "TlsGetValue", stdcall, dynlib: "kernel32".}
-
-  proc setThreadAffinityMask(hThread: SysThread, dwThreadAffinityMask: uint) {.
-    importc: "SetThreadAffinityMask", stdcall, header: "<windows.h>".}
-
-elif defined(genode):
-  import genode/env
-  const
-    GenodeHeader = "genode_cpp/threads.h"
-  type
-    SysThread* {.importcpp: "Nim::SysThread",
-                 header: GenodeHeader, final, pure.} = object
-    GenodeThreadProc = proc (x: pointer) {.noconv.}
-    ThreadVarSlot = int
-
-  proc initThread(s: var SysThread,
-                  env: GenodeEnv,
-                  stackSize: culonglong,
-                  entry: GenodeThreadProc,
-                  arg: pointer,
-                  affinity: cuint) {.
-    importcpp: "#.initThread(@)".}
-
-  proc threadVarAlloc(): ThreadVarSlot = 0
-
-  proc offMainThread(): bool {.
-    importcpp: "Nim::SysThread::offMainThread",
-    header: GenodeHeader.}
-
-  proc threadVarSetValue(value: pointer) {.
-    importcpp: "Nim::SysThread::threadVarSetValue(@)",
-    header: GenodeHeader.}
-
-  proc threadVarGetValue(): pointer {.
-    importcpp: "Nim::SysThread::threadVarGetValue()",
-    header: GenodeHeader.}
-
-  var mainTls: pointer
-
-  proc threadVarSetValue(s: ThreadVarSlot, value: pointer) {.inline.} =
-    if offMainThread():
-      threadVarSetValue(value);
-    else:
-      mainTls = value
-
-  proc threadVarGetValue(s: ThreadVarSlot): pointer {.inline.} =
-    if offMainThread():
-      threadVarGetValue();
-    else:
-      mainTls
-
-else:
-  when not defined(macosx):
-    {.passL: "-pthread".}
-
-  {.passC: "-pthread".}
-  const
-    schedh = "#define _GNU_SOURCE\n#include <sched.h>"
-    pthreadh = "#define _GNU_SOURCE\n#include <pthread.h>"
-
-  when not declared(Time):
-    when defined(linux):
-      type Time = clong
-    else:
-      type Time = int
-
-  when defined(linux) and defined(amd64):
-    type
-      SysThread* {.importc: "pthread_t",
-                  header: "<sys/types.h>" .} = distinct culong
-      Pthread_attr {.importc: "pthread_attr_t",
-                    header: "<sys/types.h>".} = object
-        abi: array[56 div sizeof(clong), clong]
-      ThreadVarSlot {.importc: "pthread_key_t",
-                    header: "<sys/types.h>".} = distinct cuint
-  else:
-    type
-      SysThread* {.importc: "pthread_t", header: "<sys/types.h>".} = object
-      Pthread_attr {.importc: "pthread_attr_t",
-                       header: "<sys/types.h>".} = object
-      ThreadVarSlot {.importc: "pthread_key_t",
-                     header: "<sys/types.h>".} = object
-  type
-    Timespec {.importc: "struct timespec", header: "<time.h>".} = object
-      tv_sec: Time
-      tv_nsec: clong
-  {.deprecated: [TSysThread: SysThread, Tpthread_attr: PThreadAttr,
-                Ttimespec: Timespec, TThreadVarSlot: ThreadVarSlot].}
-
-  proc pthread_attr_init(a1: var PthreadAttr) {.
-    importc, header: pthreadh.}
-  proc pthread_attr_setstacksize(a1: var PthreadAttr, a2: int) {.
-    importc, header: pthreadh.}
-
-  proc pthread_create(a1: var SysThread, a2: var PthreadAttr,
-            a3: proc (x: pointer): pointer {.noconv.},
-            a4: pointer): cint {.importc: "pthread_create",
-            header: pthreadh.}
-  proc pthread_join(a1: SysThread, a2: ptr pointer): cint {.
-    importc, header: pthreadh.}
-
-  proc pthread_cancel(a1: SysThread): cint {.
-    importc: "pthread_cancel", header: pthreadh.}
-
-  proc pthread_getspecific(a1: ThreadVarSlot): pointer {.
-    importc: "pthread_getspecific", header: pthreadh.}
-  proc pthread_key_create(a1: ptr ThreadVarSlot,
-                          destruct: proc (x: pointer) {.noconv.}): int32 {.
-    importc: "pthread_key_create", header: pthreadh.}
-  proc pthread_key_delete(a1: ThreadVarSlot): int32 {.
-    importc: "pthread_key_delete", header: pthreadh.}
-
-  proc pthread_setspecific(a1: ThreadVarSlot, a2: pointer): int32 {.
-    importc: "pthread_setspecific", header: pthreadh.}
-
-  proc threadVarAlloc(): ThreadVarSlot {.inline.} =
-    discard pthread_key_create(addr(result), nil)
-  proc threadVarSetValue(s: ThreadVarSlot, value: pointer) {.inline.} =
-    discard pthread_setspecific(s, value)
-  proc threadVarGetValue(s: ThreadVarSlot): pointer {.inline.} =
-    result = pthread_getspecific(s)
-
-  when useStackMaskHack:
-    proc pthread_attr_setstack(attr: var PthreadAttr, stackaddr: pointer,
-                               size: int): cint {.
-      importc: "pthread_attr_setstack", header: pthreadh.}
-
-  type CpuSet {.importc: "cpu_set_t", header: schedh.} = object
-     when defined(linux) and defined(amd64):
-       abi: array[1024 div (8 * sizeof(culong)), culong]
-
-  proc cpusetZero(s: var CpuSet) {.importc: "CPU_ZERO", header: schedh.}
-  proc cpusetIncl(cpu: cint; s: var CpuSet) {.
-    importc: "CPU_SET", header: schedh.}
-
-  proc setAffinity(thread: SysThread; setsize: csize; s: var CpuSet) {.
-    importc: "pthread_setaffinity_np", header: pthreadh.}
-
-const
-  emulatedThreadVars = compileOption("tlsEmulation")
-
-when emulatedThreadVars:
-  # the compiler generates this proc for us, so that we can get the size of
-  # the thread local var block; we use this only for sanity checking though
-  proc nimThreadVarsSize(): int {.noconv, importc: "NimThreadVarsSize".}
-
-# we preallocate a fixed size for thread local storage, so that no heap
-# allocations are needed. Currently less than 16K are used on a 64bit machine.
-# We use ``float`` for proper alignment:
-const nimTlsSize {.intdefine.} = 16000
-type
-  ThreadLocalStorage = array[0..(nimTlsSize div sizeof(float)), float]
-
-  PGcThread = ptr GcThread
-  GcThread {.pure, inheritable.} = object
-    when emulatedThreadVars and not useStackMaskHack:
-      tls: ThreadLocalStorage
-    else:
-      nil
-    when hasSharedHeap:
-      next, prev: PGcThread
-      stackBottom, stackTop: pointer
-      stackSize: int
-    else:
-      nil
-{.deprecated: [TThreadLocalStorage: ThreadLocalStorage, TGcThread: GcThread].}
-
-when not defined(useNimRtl):
-  when not useStackMaskHack:
-    var mainThread: GcThread
-
-#const globalsSlot = ThreadVarSlot(0)
-#sysAssert checkSlot.int == globalsSlot.int
-
-when emulatedThreadVars:
-  # XXX it'd be more efficient to not use a global variable for the
-  # thread storage slot, but to rely on the implementation to assign slot X
-  # for us... ;-)
-  var globalsSlot: ThreadVarSlot
-
-  proc GetThreadLocalVars(): pointer {.compilerRtl, inl.} =
-    result = addr(cast[PGcThread](threadVarGetValue(globalsSlot)).tls)
-
-  proc initThreadVarsEmulation() {.compilerProc, inline.} =
-    when not defined(useNimRtl):
-      globalsSlot = threadVarAlloc()
-      when declared(mainThread):
-        threadVarSetValue(globalsSlot, addr(mainThread))
-
-when useStackMaskHack:
-  proc maskStackPointer(offset: int): pointer {.compilerRtl, inl.} =
-    var x {.volatile.}: pointer
-    x = addr(x)
-    result = cast[pointer]((cast[int](x) and not ThreadStackMask) +%
-      (0) +% offset)
-
-# create for the main thread. Note: do not insert this data into the list
-# of all threads; it's not to be stopped etc.
-when not defined(useNimRtl):
-  when not useStackMaskHack:
-    #when not defined(createNimRtl): initStackBottom()
-    when declared(initGC):
-      initGC()
-      when not emulatedThreadVars:
-        type ThreadType {.pure.} = enum
-          None = 0,
-          NimThread = 1,
-          ForeignThread = 2
-        var
-          threadType {.rtlThreadVar.}: ThreadType
-
-        threadType = ThreadType.NimThread
-
-
-
-  when emulatedThreadVars:
-    if nimThreadVarsSize() > sizeof(ThreadLocalStorage):
-      echo "too large thread local storage size requested ",
-           "(", nimThreadVarsSize(), "/", sizeof(ThreadLocalStorage), "). ",
-           "Use -d:\"nimTlsSize=", nimThreadVarsSize(),
-           "\" to preallocate sufficient storage."
-
-      quit 1
-
-  when hasSharedHeap and not defined(boehmgc) and not defined(gogc) and not defined(nogc):
-    var
-      threadList: PGcThread
-
-    proc registerThread(t: PGcThread) =
-      # we need to use the GC global lock here!
-      acquireSys(HeapLock)
-      t.prev = nil
-      t.next = threadList
-      if threadList != nil:
-        sysAssert(threadList.prev == nil, "threadList.prev == nil")
-        threadList.prev = t
-      threadList = t
-      releaseSys(HeapLock)
-
-    proc unregisterThread(t: PGcThread) =
-      # we need to use the GC global lock here!
-      acquireSys(HeapLock)
-      if t == threadList: threadList = t.next
-      if t.next != nil: t.next.prev = t.prev
-      if t.prev != nil: t.prev.next = t.next
-      # so that a thread can be unregistered twice which might happen if the
-      # code executes `destroyThread`:
-      t.next = nil
-      t.prev = nil
-      releaseSys(HeapLock)
-
-    # on UNIX, the GC uses ``SIGFREEZE`` to tell every thread to stop so that
-    # the GC can examine the stacks?
-    proc stopTheWord() = discard
-
-# We jump through some hops here to ensure that Nim thread procs can have
-# the Nim calling convention. This is needed because thread procs are
-# ``stdcall`` on Windows and ``noconv`` on UNIX. Alternative would be to just
-# use ``stdcall`` since it is mapped to ``noconv`` on UNIX anyway.
-
-type
-  Thread* {.pure, final.}[TArg] = object
-    core: PGcThread
-    sys: SysThread
-    when TArg is void:
-      dataFn: proc () {.nimcall, gcsafe.}
-    else:
-      dataFn: proc (m: TArg) {.nimcall, gcsafe.}
-      data: TArg
-
-{.deprecated: [TThread: Thread].}
-
-var
-  threadDestructionHandlers {.rtlThreadVar.}: seq[proc () {.closure, gcsafe.}]
-
-proc onThreadDestruction*(handler: proc () {.closure, gcsafe.}) =
-  ## Registers a *thread local* handler that is called at the thread's
-  ## destruction.
-  ## A thread is destructed when the ``.thread`` proc returns
-  ## normally or when it raises an exception. Note that unhandled exceptions
-  ## in a thread nevertheless cause the whole process to die.
-  if threadDestructionHandlers.isNil:
-    threadDestructionHandlers = @[]
-  threadDestructionHandlers.add handler
-
-template afterThreadRuns() =
-  for i in countdown(threadDestructionHandlers.len-1, 0):
-    threadDestructionHandlers[i]()
-
-when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcRegions):
-  proc deallocOsPages() {.rtl.}
-
-when defined(boehmgc):
-  type GCStackBaseProc = proc(sb: pointer, t: pointer) {.noconv.}
-  proc boehmGC_call_with_stack_base(sbp: GCStackBaseProc, p: pointer)
-    {.importc: "GC_call_with_stack_base", boehmGC.}
-  proc boehmGC_register_my_thread(sb: pointer)
-    {.importc: "GC_register_my_thread", boehmGC.}
-  proc boehmGC_unregister_my_thread()
-    {.importc: "GC_unregister_my_thread", boehmGC.}
-
-  proc threadProcWrapDispatch[TArg](sb: pointer, thrd: pointer) {.noconv.} =
-    boehmGC_register_my_thread(sb)
-    try:
-      let thrd = cast[ptr Thread[TArg]](thrd)
-      when TArg is void:
-        thrd.dataFn()
-      else:
-        thrd.dataFn(thrd.data)
-    finally:
-      afterThreadRuns()
-    boehmGC_unregister_my_thread()
-else:
-  proc threadProcWrapDispatch[TArg](thrd: ptr Thread[TArg]) =
-    try:
-      when TArg is void:
-        thrd.dataFn()
-      else:
-        var x: TArg
-        deepCopy(x, thrd.data)
-        thrd.dataFn(x)
-    finally:
-      afterThreadRuns()
-
-proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) =
-  when defined(boehmgc):
-    boehmGC_call_with_stack_base(threadProcWrapDispatch[TArg], thrd)
-  elif not defined(nogc) and not defined(gogc) and not defined(gcRegions):
-    var p {.volatile.}: proc(a: ptr Thread[TArg]) {.nimcall.} =
-      threadProcWrapDispatch[TArg]
-    when not hasSharedHeap:
-      # init the GC for refc/markandsweep
-      nimGC_setStackBottom(addr(p))
-      initGC()
-      when declared(threadType):
-        threadType = ThreadType.NimThread
-    when declared(registerThread):
-      thrd.core.stackBottom = addr(thrd)
-      registerThread(thrd.core)
-    p(thrd)
-    when declared(registerThread): unregisterThread(thrd.core)
-    when declared(deallocOsPages): deallocOsPages()
-  else:
-    threadProcWrapDispatch(thrd)
-
-template threadProcWrapperBody(closure: untyped): untyped =
-  var thrd = cast[ptr Thread[TArg]](closure)
-  var core = thrd.core
-  when declared(globalsSlot): threadVarSetValue(globalsSlot, thrd.core)
-  when declared(initAllocator):
-    initAllocator()
-  threadProcWrapStackFrame(thrd)
-  # Since an unhandled exception terminates the whole process (!), there is
-  # no need for a ``try finally`` here, nor would it be correct: The current
-  # exception is tried to be re-raised by the code-gen after the ``finally``!
-  # However this is doomed to fail, because we already unmapped every heap
-  # page!
-
-  # mark as not running anymore:
-  thrd.core = nil
-  thrd.dataFn = nil
-  deallocShared(cast[pointer](core))
-
-{.push stack_trace:off.}
-when defined(windows):
-  proc threadProcWrapper[TArg](closure: pointer): int32 {.stdcall.} =
-    threadProcWrapperBody(closure)
-    # implicitly return 0
-elif defined(genode):
-   proc threadProcWrapper[TArg](closure: pointer) {.noconv.} =
-    threadProcWrapperBody(closure)
-else:
-  proc threadProcWrapper[TArg](closure: pointer): pointer {.noconv.} =
-    threadProcWrapperBody(closure)
-{.pop.}
-
-proc running*[TArg](t: Thread[TArg]): bool {.inline.} =
-  ## returns true if `t` is running.
-  result = t.dataFn != nil
-
-proc handle*[TArg](t: Thread[TArg]): SysThread {.inline.} =
-  ## returns the thread handle of `t`.
-  result = t.sys
-
-when hostOS == "windows":
-  const MAXIMUM_WAIT_OBJECTS = 64
-
-  proc joinThread*[TArg](t: Thread[TArg]) {.inline.} =
-    ## waits for the thread `t` to finish.
-    discard waitForSingleObject(t.sys, -1'i32)
-
-  proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
-    ## waits for every thread in `t` to finish.
-    var a: array[MAXIMUM_WAIT_OBJECTS, SysThread]
-    var k = 0
-    while k < len(t):
-      var count = min(len(t) - k, MAXIMUM_WAIT_OBJECTS)
-      for i in 0..(count - 1): a[i] = t[i + k].sys
-      discard waitForMultipleObjects(int32(count),
-                                     cast[ptr SysThread](addr(a)), 1, -1)
-      inc(k, MAXIMUM_WAIT_OBJECTS)
-
-elif defined(genode):
-  proc joinThread*[TArg](t: Thread[TArg]) {.importcpp.}
-    ## waits for the thread `t` to finish.
-
-  proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
-    ## waits for every thread in `t` to finish.
-    for i in 0..t.high: joinThread(t[i])
-
-else:
-  proc joinThread*[TArg](t: Thread[TArg]) {.inline.} =
-    ## waits for the thread `t` to finish.
-    discard pthread_join(t.sys, nil)
-
-  proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
-    ## waits for every thread in `t` to finish.
-    for i in 0..t.high: joinThread(t[i])
-
-when false:
-  # XXX a thread should really release its heap here somehow:
-  proc destroyThread*[TArg](t: var Thread[TArg]) =
-    ## forces the thread `t` to terminate. This is potentially dangerous if
-    ## you don't have full control over `t` and its acquired resources.
-    when hostOS == "windows":
-      discard TerminateThread(t.sys, 1'i32)
-    else:
-      discard pthread_cancel(t.sys)
-    when declared(registerThread): unregisterThread(addr(t))
-    t.dataFn = nil
-    ## if thread `t` already exited, `t.core` will be `null`.
-    if not isNil(t.core):
-      deallocShared(t.core)
-      t.core = nil
-
-when hostOS == "windows":
-  proc createThread*[TArg](t: var Thread[TArg],
-                           tp: proc (arg: TArg) {.thread, nimcall.},
-                           param: TArg) =
-    ## creates a new thread `t` and starts its execution. Entry point is the
-    ## proc `tp`. `param` is passed to `tp`. `TArg` can be ``void`` if you
-    ## don't need to pass any data to the thread.
-    t.core = cast[PGcThread](allocShared0(sizeof(GcThread)))
-
-    when TArg isnot void: t.data = param
-    t.dataFn = tp
-    when hasSharedHeap: t.core.stackSize = ThreadStackSize
-    var dummyThreadId: int32
-    t.sys = createThread(nil, ThreadStackSize, threadProcWrapper[TArg],
-                         addr(t), 0'i32, dummyThreadId)
-    if t.sys <= 0:
-      raise newException(ResourceExhaustedError, "cannot create thread")
-
-  proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) =
-    ## pins a thread to a `CPU`:idx:. In other words sets a
-    ## thread's `affinity`:idx:. If you don't know what this means, you
-    ## shouldn't use this proc.
-    setThreadAffinityMask(t.sys, uint(1 shl cpu))
-
-elif defined(genode):
-  var affinityOffset: cuint = 1
-    ## CPU affinity offset for next thread, safe to roll-over
-
-  proc createThread*[TArg](t: var Thread[TArg],
-                           tp: proc (arg: TArg) {.thread, nimcall.},
-                           param: TArg) =
-    t.core = cast[PGcThread](allocShared0(sizeof(GcThread)))
-
-    when TArg isnot void: t.data = param
-    t.dataFn = tp
-    when hasSharedHeap: t.stackSize = ThreadStackSize
-    t.sys.initThread(
-      runtimeEnv,
-      ThreadStackSize.culonglong,
-      threadProcWrapper[TArg], addr(t), affinityOffset)
-    inc affinityOffset
-
-  proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) =
-    {.hint: "cannot change Genode thread CPU affinity after initialization".}
-    discard
-
-else:
-  proc createThread*[TArg](t: var Thread[TArg],
-                           tp: proc (arg: TArg) {.thread, nimcall.},
-                           param: TArg) =
-    ## creates a new thread `t` and starts its execution. Entry point is the
-    ## proc `tp`. `param` is passed to `tp`. `TArg` can be ``void`` if you
-    ## don't need to pass any data to the thread.
-    t.core = cast[PGcThread](allocShared0(sizeof(GcThread)))
-
-    when TArg isnot void: t.data = param
-    t.dataFn = tp
-    when hasSharedHeap: t.core.stackSize = ThreadStackSize
-    var a {.noinit.}: PthreadAttr
-    pthread_attr_init(a)
-    pthread_attr_setstacksize(a, ThreadStackSize)
-    if pthread_create(t.sys, a, threadProcWrapper[TArg], addr(t)) != 0:
-      raise newException(ResourceExhaustedError, "cannot create thread")
-
-  proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) =
-    ## pins a thread to a `CPU`:idx:. In other words sets a
-    ## thread's `affinity`:idx:. If you don't know what this means, you
-    ## shouldn't use this proc.
-    when not defined(macosx):
-      var s {.noinit.}: CpuSet
-      cpusetZero(s)
-      cpusetIncl(cpu.cint, s)
-      setAffinity(t.sys, sizeof(s), s)
-
-proc createThread*(t: var Thread[void], tp: proc () {.thread, nimcall.}) =
-  createThread[void](t, tp)
-
-when false:
-  proc mainThreadId*[TArg](): ThreadId[TArg] =
-    ## returns the thread ID of the main thread.
-    result = cast[ThreadId[TArg]](addr(mainThread))
-
-when useStackMaskHack:
-  proc runMain(tp: proc () {.thread.}) {.compilerproc.} =
-    var mainThread: Thread[pointer]
-    createThread(mainThread, tp)
-    joinThread(mainThread)
-
-## we need to cache current threadId to not perform syscall all the time
-var threadId {.threadvar.}: int
-
-when defined(windows):
-  proc getThreadId*(): int =
-    ## get the ID of the currently running thread.
-    if threadId == 0:
-      threadId = int(getCurrentThreadId())
-    result = threadId
-
-elif defined(linux):
-  proc syscall(arg: clong): clong {.varargs, importc: "syscall", header: "<unistd.h>".}
-  when defined(amd64):
-    const NR_gettid = clong(186)
-  else:
-    var NR_gettid {.importc: "__NR_gettid", header: "<sys/syscall.h>".}: clong
-
-  proc getThreadId*(): int =
-    ## get the ID of the currently running thread.
-    if threadId == 0:
-      threadId = int(syscall(NR_gettid))
-    result = threadId
-
-elif defined(dragonfly):
-  proc lwp_gettid(): int32 {.importc, header: "unistd.h".}
-
-  proc getThreadId*(): int =
-    ## get the ID of the currently running thread.
-    if threadId == 0:
-      threadId = int(lwp_gettid())
-    result = threadId
-
-elif defined(openbsd):
-  proc getthrid(): int32 {.importc: "getthrid", header: "<unistd.h>".}
-
-  proc getThreadId*(): int =
-    ## get the ID of the currently running thread.
-    if threadId == 0:
-      threadId = int(getthrid())
-    result = threadId
-
-elif defined(netbsd):
-  proc lwp_self(): int32 {.importc: "_lwp_self", header: "<lwp.h>".}
-
-  proc getThreadId*(): int =
-    ## get the ID of the currently running thread.
-    if threadId == 0:
-      threadId = int(lwp_self())
-    result = threadId
-
-elif defined(freebsd):
-  proc syscall(arg: cint, arg0: ptr cint): cint {.varargs, importc: "syscall", header: "<unistd.h>".}
-  var SYS_thr_self {.importc:"SYS_thr_self", header:"<sys/syscall.h>"}: cint
-
-  proc getThreadId*(): int =
-    ## get the ID of the currently running thread.
-    var tid = 0.cint
-    if threadId == 0:
-      discard syscall(SYS_thr_self, addr tid)
-      threadId = tid
-    result = threadId
-
-elif defined(macosx):
-  proc syscall(arg: cint): cint {.varargs, importc: "syscall", header: "<unistd.h>".}
-  var SYS_thread_selfid {.importc:"SYS_thread_selfid", header:"<sys/syscall.h>".}: cint
-
-  proc getThreadId*(): int =
-    ## get the ID of the currently running thread.
-    if threadId == 0:
-      threadId = int(syscall(SYS_thread_selfid))
-    result = threadId
-
-elif defined(solaris):
-  type thread_t {.importc: "thread_t", header: "<thread.h>".} = distinct int
-  proc thr_self(): thread_t {.importc, header: "<thread.h>".}
-
-  proc getThreadId*(): int =
-    ## get the ID of the currently running thread.
-    if threadId == 0:
-      threadId = int(thr_self())
-    result = threadId
diff --git a/lib/system/timers.nim b/lib/system/timers.nim
index f2ebad2c1..ffb0f7716 100644
--- a/lib/system/timers.nim
+++ b/lib/system/timers.nim
@@ -8,12 +8,11 @@
 #
 
 ## Timer support for the realtime GC. Based on
-## `<https://github.com/jckarter/clay/blob/master/compiler/src/hirestimer.cpp>`_
+## `<https://github.com/jckarter/clay/blob/master/compiler/hirestimer.cpp>`_
 
 type
   Ticks = distinct int64
   Nanos = int64
-{.deprecated: [TTicks: Ticks, TNanos: Nanos].}
 
 when defined(windows):
 
@@ -32,15 +31,14 @@ when defined(windows):
 
     result = Nanos(float64(a.int64 - b.int64) * performanceCounterRate)
 
-elif defined(macosx):
+elif defined(macosx) and not defined(emscripten):
   type
     MachTimebaseInfoData {.pure, final,
         importc: "mach_timebase_info_data_t",
         header: "<mach/mach_time.h>".} = object
-      numer, denom: int32
-  {.deprecated: [TMachTimebaseInfoData: MachTimebaseInfoData].}
+      numer, denom: int32 # note: `uint32` in sources
 
-  proc mach_absolute_time(): int64 {.importc, header: "<mach/mach.h>".}
+  proc mach_absolute_time(): uint64 {.importc, header: "<mach/mach_time.h>".}
   proc mach_timebase_info(info: var MachTimebaseInfoData) {.importc,
     header: "<mach/mach_time.h>".}
 
@@ -51,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
@@ -61,7 +59,6 @@ elif defined(posixRealtime):
                final, pure.} = object ## struct timespec
       tv_sec: int  ## Seconds.
       tv_nsec: int ## Nanoseconds.
-  {.deprecated: [TClockid: Clockid, TTimeSpec: TimeSpec].}
 
   var
     CLOCK_REALTIME {.importc: "CLOCK_REALTIME", header: "<time.h>".}: Clockid
@@ -89,7 +86,7 @@ else:
                final, pure.} = object ## struct timeval
       tv_sec: Time  ## Seconds.
       tv_usec: clong ## Microseconds.
-  {.deprecated: [Ttimeval: Timeval].}
+
   proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {.
     importc: "gettimeofday", header: "<sys/time.h>".}
 
diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim
deleted file mode 100644
index a8b28c279..000000000
--- a/lib/system/widestrs.nim
+++ /dev/null
@@ -1,168 +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 C/C++'s `wide strings`:idx:. This is part of the system
-# module! Do not import it directly!
-
-when not declared(NimString):
-  {.error: "You must not import this module explicitly".}
-
-type
-  Utf16Char* = distinct int16
-  WideCString* = ref UncheckedArray[Utf16Char]
-{.deprecated: [TUtf16Char: Utf16Char].}
-
-proc len*(w: WideCString): int =
-  ## returns the length of a widestring. This traverses the whole string to
-  ## find the binary zero end marker!
-  while int16(w[result]) != 0'i16: inc result
-
-const
-  UNI_REPLACEMENT_CHAR = Utf16Char(0xFFFD'i16)
-  UNI_MAX_BMP = 0x0000FFFF
-  UNI_MAX_UTF16 = 0x0010FFFF
-  UNI_MAX_UTF32 = 0x7FFFFFFF
-  UNI_MAX_LEGAL_UTF32 = 0x0010FFFF
-
-  halfShift = 10
-  halfBase = 0x0010000
-  halfMask = 0x3FF
-
-  UNI_SUR_HIGH_START = 0xD800
-  UNI_SUR_HIGH_END = 0xDBFF
-  UNI_SUR_LOW_START = 0xDC00
-  UNI_SUR_LOW_END = 0xDFFF
-  UNI_REPL = 0xFFFD
-
-template ones(n: untyped): untyped = ((1 shl n)-1)
-
-template fastRuneAt(s: cstring, i, L: int, result: untyped, doInc = true) =
-  ## Returns the unicode character ``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:
-    result = ord(s[i])
-    when doInc: inc(i)
-  elif ord(s[i]) shr 5 == 0b110:
-    #assert(ord(s[i+1]) shr 6 == 0b10)
-    if i <= L - 2:
-      result = (ord(s[i]) and (ones(5))) shl 6 or (ord(s[i+1]) and ones(6))
-      when doInc: inc(i, 2)
-    else:
-      result = UNI_REPL
-      when doInc: inc(i)
-  elif ord(s[i]) shr 4 == 0b1110:
-    if i <= L - 3:
-      #assert(ord(s[i+1]) shr 6 == 0b10)
-      #assert(ord(s[i+2]) shr 6 == 0b10)
-      result = (ord(s[i]) and ones(4)) shl 12 or
-               (ord(s[i+1]) and ones(6)) shl 6 or
-               (ord(s[i+2]) and ones(6))
-      when doInc: inc(i, 3)
-    else:
-      result = UNI_REPL
-      when doInc: inc(i)
-  elif ord(s[i]) shr 3 == 0b11110:
-    if i <= L - 4:
-      #assert(ord(s[i+1]) shr 6 == 0b10)
-      #assert(ord(s[i+2]) shr 6 == 0b10)
-      #assert(ord(s[i+3]) shr 6 == 0b10)
-      result = (ord(s[i]) and ones(3)) shl 18 or
-               (ord(s[i+1]) and ones(6)) shl 12 or
-               (ord(s[i+2]) and ones(6)) shl 6 or
-               (ord(s[i+3]) and ones(6))
-      when doInc: inc(i, 4)
-    else:
-      result = UNI_REPL
-      when doInc: inc(i)
-  else:
-    result = 0xFFFD
-    when doInc: inc(i)
-
-iterator runes(s: cstring, L: int): int =
-  var
-    i = 0
-    result: int
-  while i < L:
-    fastRuneAt(s, i, L, result, true)
-    yield result
-
-proc newWideCString*(source: cstring, L: int): WideCString =
-  unsafeNew(result, L * 4 + 2)
-  #result = cast[wideCString](alloc(L * 4 + 2))
-  var d = 0
-  for ch in runes(source, L):
-    if ch <=% UNI_MAX_BMP:
-      if ch >=% UNI_SUR_HIGH_START and ch <=% UNI_SUR_LOW_END:
-        result[d] = UNI_REPLACEMENT_CHAR
-      else:
-        result[d] = Utf16Char(toU16(ch))
-    elif ch >% UNI_MAX_UTF16:
-      result[d] = UNI_REPLACEMENT_CHAR
-    else:
-      let ch = ch -% halfBase
-      result[d] = Utf16Char(toU16((ch shr halfShift) +% UNI_SUR_HIGH_START))
-      inc d
-      result[d] = Utf16Char(toU16((ch and halfMask) +% UNI_SUR_LOW_START))
-    inc d
-  result[d] = Utf16Char(0'i16)
-
-proc newWideCString*(s: cstring): WideCString =
-  if s.isNil: return nil
-
-  result = newWideCString(s, s.len)
-
-proc newWideCString*(s: string): WideCString =
-  result = newWideCString(s, s.len)
-
-proc `$`*(w: WideCString, estimate: int, replacement: int = 0xFFFD): string =
-  result = newStringOfCap(estimate + estimate shr 2)
-
-  var i = 0
-  while w[i].int16 != 0'i16:
-    var ch = int(cast[uint16](w[i]))
-    inc i
-    if ch >= UNI_SUR_HIGH_START and ch <= UNI_SUR_HIGH_END:
-      # If the 16 bits following the high surrogate are in the source buffer...
-      let ch2 = int(cast[uint16](w[i]))
-
-      # If it's a low surrogate, convert to UTF32:
-      if ch2 >= UNI_SUR_LOW_START and ch2 <= UNI_SUR_LOW_END:
-        ch = (((ch and halfMask) shl halfShift) + (ch2 and halfMask)) + halfBase
-        inc i
-      else:
-        #invalid UTF-16
-        ch = replacement
-    elif ch >= UNI_SUR_LOW_START and ch <= UNI_SUR_LOW_END:
-      #invalid UTF-16
-      ch = replacement
-
-    if ch < 0x80:
-      result.add chr(ch)
-    elif ch < 0x800:
-      result.add chr((ch shr 6) or 0xc0)
-      result.add chr((ch and 0x3f) or 0x80)
-    elif ch < 0x10000:
-      result.add chr((ch shr 12) or 0xe0)
-      result.add chr(((ch shr 6) and 0x3f) or 0x80)
-      result.add chr((ch and 0x3f) or 0x80)
-    elif ch <= 0x10FFFF:
-      result.add chr((ch shr 18) or 0xf0)
-      result.add chr(((ch shr 12) and 0x3f) or 0x80)
-      result.add chr(((ch shr 6) and 0x3f) or 0x80)
-      result.add chr((ch and 0x3f) or 0x80)
-    else:
-      # replacement char(in case user give very large number):
-      result.add chr(0xFFFD shr 12 or 0b1110_0000)
-      result.add chr(0xFFFD shr 6 and ones(6) or 0b10_0000_00)
-      result.add chr(0xFFFD and ones(6) or 0b10_0000_00)
-
-proc `$`*(s: WideCString): string =
-  result = s $ 80
diff --git a/lib/system_overview.rst b/lib/system_overview.rst
new file mode 100644
index 000000000..cc0643bf1
--- /dev/null
+++ b/lib/system_overview.rst
@@ -0,0 +1,177 @@
+.. default-role:: code
+.. include:: ../doc/rstcommon.rst
+
+The System module imports several separate modules, and their documentation
+is in separate files:
+
+* `iterators <iterators.html>`_
+* `exceptions <exceptions.html>`_
+* `assertions <assertions.html>`_
+* `dollars <dollars.html>`_
+* `ctypes <ctypes.html>`_
+
+
+Here is a short overview of the most commonly used functions from the
+`system` module. Function names in the tables below are clickable and
+will take you to the full documentation of the function.
+
+There are many more functions available than the ones listed in this overview.
+Use the table of contents on the left-hand side and/or `Ctrl+F` to navigate
+through this module.
+
+
+Strings and characters
+----------------------
+
+=============================     =======================================
+Proc                              Usage
+=============================     =======================================
+`len(s)<#len,string>`_            Return the length of a string
+`chr(i)<#chr,range[]>`_           Convert an `int` in the range `0..255`
+                                  to a character
+`ord(c)<#ord,T>`_                 Return `int` value of a character
+`a & b<#&,string,string>`_        Concatenate two strings
+`s.add(c)<#add,string,char>`_     Add character to the string
+`$<dollars.html>`_                Convert various types to string
+=============================     =======================================
+
+**See also:**
+* `strutils module <strutils.html>`_ for common string functions
+* `strformat module <strformat.html>`_ for string interpolation and formatting
+* `unicode module <unicode.html>`_ for Unicode UTF-8 handling
+* `strscans <strscans.html>`_ for `scanf` and `scanp` macros, which offer
+  easier substring extraction than regular expressions
+* `strtabs module <strtabs.html>`_ for efficient hash tables
+  (dictionaries, in some programming languages) mapping from strings to strings
+
+
+
+Seqs
+----
+
+=============================================================  ==========================================
+Proc                                                           Usage
+=============================================================  ==========================================
+`newSeq<#newSeq>`_                                             Create a new sequence of a given length
+`newSeqOfCap<#newSeqOfCap,Natural>`_                           Create a new sequence with zero length
+                                                               and a given capacity
+`setLen<#setLen,seq[T],Natural>`_                              Set the length of a sequence
+`len<#len,seq[T]>`_                                            Return the length of a sequence
+`@<#@,openArray[T]>`_                                          Turn an array into a sequence
+`add<#add,seq[T],sinkT>`_                                      Add an item to the sequence
+`insert<#insert,seq[T],sinkT>`_                                Insert an item at a specific position
+`delete<#delete,seq[T],Natural>`_                              Delete an item while preserving the
+                                                               order of elements (`O(n)` operation)
+`del<#del,seq[T],Natural>`_                                    `O(1)` removal, doesn't preserve the order
+`pop<#pop,seq[T]>`_                                            Remove and return last item of a sequence
+`x & y<#&,seq[T],seq[T]>`_                                     Concatenate two sequences
+`x[a .. b]<#[],openArray[T],HSlice[U: Ordinal,V: Ordinal]>`_   Slice of a sequence (both ends included)
+`x[a .. ^b]<#[],openArray[T],HSlice[U: Ordinal,V: Ordinal]>`_  Slice of a sequence but `b` is a 
+                                                               reversed index (both ends included)
+`x[a ..< b]<#[],openArray[T],HSlice[U: Ordinal,V: Ordinal]>`_  Slice of a sequence (excluded upper bound)
+=============================================================  ==========================================
+
+**See also:**
+* `sequtils module <sequtils.html>`_ for operations on container
+  types (including strings)
+* `json module <json.html>`_ for a structure which allows heterogeneous members
+* `lists module <lists.html>`_ for linked lists
+
+
+
+Sets
+----
+
+Built-in bit sets.
+
+===============================     ======================================
+Proc                                Usage
+===============================     ======================================
+`incl<#incl,set[T],T>`_             Include element `y` in the set `x`
+`excl<#excl,set[T],T>`_             Exclude element `y` from the set `x`
+`card<#card,set[T]>`_               Return the cardinality of the set,
+                                    i.e. the number of elements
+`a * b<#*,set[T],set[T]>`_          Intersection
+`a + b<#+,set[T],set[T]>`_          Union
+`a - b<#-,set[T],set[T]>`_          Difference
+`contains<#contains,set[T],T>`_     Check if an element is in the set
+[a < b](#<,set[T],set[T])           Check if `a` is a subset of `b`
+===============================     ======================================
+
+**See also:**
+* `setutils module <setutils.html>`_ for bit set convenience functions
+* `sets module <sets.html>`_ for hash sets
+* `intsets module <intsets.html>`_ for efficient int sets
+
+
+
+Numbers
+-------
+
+==============================    ==================================     =====================
+Proc                              Usage                                  Also known as
+                                                                         (in other languages)
+==============================    ==================================     =====================
+`div<#div,int,int>`_              Integer division                       `//`
+`mod<#mod,int,int>`_              Integer modulo (remainder)             `%`
+`shl<#shl,int,SomeInteger>`_      Shift left                             `<<`
+`shr<#shr,int,SomeInteger>`_      Shift right                            `>>`
+`ashr<#ashr,int,SomeInteger>`_    Arithmetic shift right
+`and<#and,int,int>`_              Bitwise `and`                          `&`
+`or<#or,int,int>`_                Bitwise `or`                           `|`
+`xor<#xor,int,int>`_              Bitwise `xor`                          `^`
+`not<#not,int>`_                  Bitwise `not` (complement)             `~`
+`toInt<#toInt,float>`_            Convert floating-point number
+                                  into an `int`
+`toFloat<#toFloat,int>`_          Convert an integer into a `float`
+==============================    ==================================     =====================
+
+**See also:**
+* `math module <math.html>`_ for mathematical operations like trigonometric
+  functions, logarithms, square and cubic roots, etc.
+* `complex module <complex.html>`_ for operations on complex numbers
+* `rationals module <rationals.html>`_ for rational numbers
+
+
+
+Ordinals
+--------
+
+`Ordinal type <#Ordinal>`_ includes integer, bool, character, and enumeration
+types, as well as their subtypes.
+
+=====================     =======================================
+Proc                      Usage
+=====================     =======================================
+`succ<#succ,T,int>`_      Successor of the value
+`pred<#pred,T,int>`_      Predecessor of the value
+`inc<#inc,T,int>`_        Increment the ordinal
+`dec<#dec,T,int>`_        Decrement the ordinal
+`high<#high,T>`_          Return the highest possible value
+`low<#low,T>`_            Return the lowest possible value
+`ord<#ord,T>`_            Return `int` value of an ordinal value
+=====================     =======================================
+
+
+
+Misc
+----
+
+====================================================  ============================================
+Proc                                                  Usage
+====================================================  ============================================
+`is<#is,T,S>`_                                        Check if two arguments are of the same type
+`isnot<#isnot.t,untyped,untyped>`_                    Negated version of `is`
+`!=<#!%3D.t,untyped,untyped>`_                        Not equals
+`addr<#addr,T>`_                                      Take the address of a memory location
+`T and F<#and,bool,bool>`_                            Boolean `and`
+`T or F<#or,bool,bool>`_                              Boolean `or`
+`T xor F<#xor,bool,bool>`_                            Boolean `xor` (exclusive or)
+`not T<#not,bool>`_                                   Boolean `not`
+`a[^x]<#^.t,int>`_                                    Take the element at the reversed index `x`
+`a .. b<#..,sinkT,sinkU>`_                            Binary slice that constructs an interval
+                                                      `[a, b]`
+`a ..^ b<#..^.t,untyped,untyped>`_                    Interval `[a, b]` but `b` as reversed index
+[a ..< b](#..<.t,untyped,untyped)                     Interval `[a, b)` (excluded upper bound)
+[runnableExamples](#runnableExamples,string,untyped)  Create testable documentation
+====================================================  ============================================
diff --git a/lib/windows/registry.nim b/lib/windows/registry.nim
index 06f84c881..207172f8c 100644
--- a/lib/windows/registry.nim
+++ b/lib/windows/registry.nim
@@ -9,7 +9,10 @@
 
 ## This module is experimental and its interface may change.
 
-import winlean, os
+import std/oserrors
+
+when defined(nimPreviewSlimSystem):
+  import std/widestrs
 
 type
   HKEY* = uint
@@ -46,30 +49,32 @@ template call(f) =
 proc getUnicodeValue*(path, key: string; handle: HKEY): string =
   let hh = newWideCString path
   let kk = newWideCString key
-  var bufsize: int32
+  var bufSize: int32
   # try a couple of different flag settings:
   var flags: int32 = RRF_RT_ANY
-  let err = regGetValue(handle, hh, kk, flags, nil, nil, addr bufsize)
+  let err = regGetValue(handle, hh, kk, flags, nil, nil, addr bufSize)
   if err != 0:
     var newHandle: HKEY
     call regOpenKeyEx(handle, hh, 0, KEY_READ or KEY_WOW64_64KEY, newHandle)
-    call regGetValue(newHandle, nil, kk, flags, nil, nil, addr bufsize)
-    var res = newWideCString("", bufsize)
-    call regGetValue(newHandle, nil, kk, flags, nil, cast[pointer](res),
-                   addr bufsize)
-    result = res $ bufsize
+    call regGetValue(newHandle, nil, kk, flags, nil, nil, addr bufSize)
+    if bufSize > 0:
+      var res = newWideCString(bufSize)
+      call regGetValue(newHandle, nil, kk, flags, nil, addr res[0],
+                    addr bufSize)
+      result = res $ bufSize
     call regCloseKey(newHandle)
   else:
-    var res = newWideCString("", bufsize)
-    call regGetValue(handle, hh, kk, flags, nil, cast[pointer](res),
-                   addr bufsize)
-    result = res $ bufsize
+    if bufSize > 0:
+      var res = newWideCString(bufSize)
+      call regGetValue(handle, hh, kk, flags, nil, addr res[0],
+                    addr bufSize)
+      result = res $ bufSize
 
 proc regSetValue(key: HKEY, lpSubKey, lpValueName: WideCString,
                  dwType: int32; lpData: WideCString; cbData: int32): int32 {.
   importc: "RegSetKeyValueW", dynlib: "Advapi32.dll", stdcall.}
 
-proc setUnicodeValue*(path, key, val: string; handle: HKey) =
+proc setUnicodeValue*(path, key, val: string; handle: HKEY) =
   let hh = newWideCString path
   let kk = newWideCString key
   let vv = newWideCString val
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index 3263f1d37..79681376b 100644
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -10,29 +10,29 @@
 ## This module implements a small wrapper for some needed Win API procedures,
 ## so that the Nim compiler does not depend on the huge Windows module.
 
-{.deadCodeElim: on.}  # dce option deprecated
+import std/dynlib
 
-import dynlib
+when defined(nimHasStyleChecks):
+  {.push styleChecks: off.}
 
+{.passc: "-DWIN32_LEAN_AND_MEAN".}
 
-{.passC: "-DWIN32_LEAN_AND_MEAN".}
+when defined(nimPreviewSlimSystem):
+  from std/syncio import FileHandle
+  import std/widestrs
 
-const
-  useWinUnicode* = not defined(useWinAnsi)
-
-when useWinUnicode:
-  type WinChar* = Utf16Char
-  {.deprecated: [TWinChar: WinChar].}
-else:
-  type WinChar* = char
-  {.deprecated: [TWinChar: WinChar].}
+type WinChar* = Utf16Char
 
+# See https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types
 type
   Handle* = int
   LONG* = int32
   ULONG* = int32
   PULONG* = ptr int
   WINBOOL* = int32
+    ## `WINBOOL` uses opposite convention as posix, !=0 meaning success.
+    # xxx this should be distinct int32, distinct would make code less error prone
+  PBOOL* = ptr WINBOOL
   DWORD* = int32
   PDWORD* = ptr DWORD
   LPINT* = ptr int32
@@ -40,13 +40,14 @@ type
   PULONG_PTR* = ptr uint
   HDC* = Handle
   HGLRC* = Handle
+  BYTE* = uint8
 
-  SECURITY_ATTRIBUTES* {.final, pure.} = object
+  SECURITY_ATTRIBUTES* = object
     nLength*: int32
     lpSecurityDescriptor*: pointer
     bInheritHandle*: WINBOOL
 
-  STARTUPINFO* {.final, pure.} = object
+  STARTUPINFO* = object
     cb*: int32
     lpReserved*: cstring
     lpDesktop*: cstring
@@ -66,17 +67,17 @@ type
     hStdOutput*: Handle
     hStdError*: Handle
 
-  PROCESS_INFORMATION* {.final, pure.} = object
+  PROCESS_INFORMATION* = object
     hProcess*: Handle
     hThread*: Handle
     dwProcessId*: int32
     dwThreadId*: int32
 
-  FILETIME* {.final, pure.} = object ## CANNOT BE int64 BECAUSE OF ALIGNMENT
+  FILETIME* = object ## CANNOT BE int64 BECAUSE OF ALIGNMENT
     dwLowDateTime*: DWORD
     dwHighDateTime*: DWORD
 
-  BY_HANDLE_FILE_INFORMATION* {.final, pure.} = object
+  BY_HANDLE_FILE_INFORMATION* = object
     dwFileAttributes*: DWORD
     ftCreationTime*: FILETIME
     ftLastAccessTime*: FILETIME
@@ -88,7 +89,7 @@ type
     nFileIndexHigh*: DWORD
     nFileIndexLow*: DWORD
 
-  OSVERSIONINFO* {.final, pure.} = object
+  OSVERSIONINFO* = object
     dwOSVersionInfoSize*: DWORD
     dwMajorVersion*: DWORD
     dwMinorVersion*: DWORD
@@ -96,9 +97,11 @@ type
     dwPlatformId*: DWORD
     szCSDVersion*: array[0..127, WinChar]
 
-{.deprecated: [THandle: Handle, TSECURITY_ATTRIBUTES: SECURITY_ATTRIBUTES,
-    TSTARTUPINFO: STARTUPINFO, TPROCESS_INFORMATION: PROCESS_INFORMATION,
-    TFILETIME: FILETIME, TBY_HANDLE_FILE_INFORMATION: BY_HANDLE_FILE_INFORMATION].}
+  Protoent* = object
+    p_name*: cstring
+    p_aliases*: cstringArray
+    p_proto*: cshort
+
 
 const
   STARTF_USESHOWWINDOW* = 1'i32
@@ -132,35 +135,41 @@ const
 
   CREATE_NO_WINDOW* = 0x08000000'i32
 
-when useWinUnicode:
-  proc getVersionExW*(lpVersionInfo: ptr OSVERSIONINFO): WINBOOL {.stdcall, dynlib: "kernel32", importc: "GetVersionExW".}
-else:
-  proc getVersionExA*(lpVersionInfo: ptr OSVERSIONINFO): WINBOOL {.stdcall, dynlib: "kernel32", importc: "GetVersionExA".}
+  HANDLE_FLAG_INHERIT* = 0x00000001'i32
+
+proc isSuccess*(a: WINBOOL): bool {.inline.} =
+  ## Returns true if `a != 0`. Windows uses a different convention than POSIX,
+  ## where `a == 0` is commonly used on success.
+  a != 0
+proc getVersionExW*(lpVersionInfo: ptr OSVERSIONINFO): WINBOOL {.
+    stdcall, dynlib: "kernel32", importc: "GetVersionExW", sideEffect.}
+proc getVersionExA*(lpVersionInfo: ptr OSVERSIONINFO): WINBOOL {.
+    stdcall, dynlib: "kernel32", importc: "GetVersionExA", sideEffect.}
 
-proc getVersion*(): DWORD {.stdcall, dynlib: "kernel32", importc: "GetVersion".}
+proc getVersion*(): DWORD {.stdcall, dynlib: "kernel32", importc: "GetVersion", sideEffect.}
 
 proc closeHandle*(hObject: Handle): WINBOOL {.stdcall, dynlib: "kernel32",
     importc: "CloseHandle".}
 
-proc readFile*(hFile: Handle, Buffer: pointer, nNumberOfBytesToRead: int32,
+proc readFile*(hFile: Handle, buffer: pointer, nNumberOfBytesToRead: int32,
                lpNumberOfBytesRead: ptr int32, lpOverlapped: pointer): WINBOOL{.
-    stdcall, dynlib: "kernel32", importc: "ReadFile".}
+    stdcall, dynlib: "kernel32", importc: "ReadFile", sideEffect.}
 
-proc writeFile*(hFile: Handle, Buffer: pointer, nNumberOfBytesToWrite: int32,
+proc writeFile*(hFile: Handle, buffer: pointer, nNumberOfBytesToWrite: int32,
                 lpNumberOfBytesWritten: ptr int32,
                 lpOverlapped: pointer): WINBOOL{.
-    stdcall, dynlib: "kernel32", importc: "WriteFile".}
+    stdcall, dynlib: "kernel32", importc: "WriteFile", sideEffect.}
 
 proc createPipe*(hReadPipe, hWritePipe: var Handle,
                  lpPipeAttributes: var SECURITY_ATTRIBUTES,
                  nSize: int32): WINBOOL{.
-    stdcall, dynlib: "kernel32", importc: "CreatePipe".}
+    stdcall, dynlib: "kernel32", importc: "CreatePipe", sideEffect.}
 
 proc createNamedPipe*(lpName: WideCString,
                      dwOpenMode, dwPipeMode, nMaxInstances, nOutBufferSize,
                      nInBufferSize, nDefaultTimeOut: int32,
                      lpSecurityAttributes: ptr SECURITY_ATTRIBUTES): Handle {.
-    stdcall, dynlib: "kernel32", importc: "CreateNamedPipeW".}
+    stdcall, dynlib: "kernel32", importc: "CreateNamedPipeW", sideEffect.}
 
 proc peekNamedPipe*(hNamedPipe: Handle, lpBuffer: pointer=nil,
                     nBufferSize: int32 = 0,
@@ -169,37 +178,25 @@ proc peekNamedPipe*(hNamedPipe: Handle, lpBuffer: pointer=nil,
                     lpBytesLeftThisMessage: ptr int32 = nil): bool {.
     stdcall, dynlib: "kernel32", importc: "PeekNamedPipe".}
 
-when useWinUnicode:
-  proc createProcessW*(lpApplicationName, lpCommandLine: WideCString,
-                     lpProcessAttributes: ptr SECURITY_ATTRIBUTES,
-                     lpThreadAttributes: ptr SECURITY_ATTRIBUTES,
-                     bInheritHandles: WINBOOL, dwCreationFlags: int32,
-                     lpEnvironment, lpCurrentDirectory: WideCString,
-                     lpStartupInfo: var STARTUPINFO,
-                     lpProcessInformation: var PROCESS_INFORMATION): WINBOOL{.
-    stdcall, dynlib: "kernel32", importc: "CreateProcessW".}
-
-else:
-  proc createProcessA*(lpApplicationName, lpCommandLine: cstring,
-                       lpProcessAttributes: ptr SECURITY_ATTRIBUTES,
-                       lpThreadAttributes: ptr SECURITY_ATTRIBUTES,
-                       bInheritHandles: WINBOOL, dwCreationFlags: int32,
-                       lpEnvironment: pointer, lpCurrentDirectory: cstring,
-                       lpStartupInfo: var STARTUPINFO,
-                       lpProcessInformation: var PROCESS_INFORMATION): WINBOOL{.
-      stdcall, dynlib: "kernel32", importc: "CreateProcessA".}
-
+proc createProcessW*(lpApplicationName, lpCommandLine: WideCString,
+                   lpProcessAttributes: ptr SECURITY_ATTRIBUTES,
+                   lpThreadAttributes: ptr SECURITY_ATTRIBUTES,
+                   bInheritHandles: WINBOOL, dwCreationFlags: int32,
+                   lpEnvironment, lpCurrentDirectory: WideCString,
+                   lpStartupInfo: var STARTUPINFO,
+                   lpProcessInformation: var PROCESS_INFORMATION): WINBOOL{.
+  stdcall, dynlib: "kernel32", importc: "CreateProcessW", sideEffect.}
 
 proc suspendThread*(hThread: Handle): int32 {.stdcall, dynlib: "kernel32",
-    importc: "SuspendThread".}
+    importc: "SuspendThread", sideEffect.}
 proc resumeThread*(hThread: Handle): int32 {.stdcall, dynlib: "kernel32",
-    importc: "ResumeThread".}
+    importc: "ResumeThread", sideEffect.}
 
 proc waitForSingleObject*(hHandle: Handle, dwMilliseconds: int32): int32 {.
-    stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".}
+    stdcall, dynlib: "kernel32", importc: "WaitForSingleObject", sideEffect.}
 
 proc terminateProcess*(hProcess: Handle, uExitCode: int): WINBOOL {.stdcall,
-    dynlib: "kernel32", importc: "TerminateProcess".}
+    dynlib: "kernel32", importc: "TerminateProcess", sideEffect.}
 
 proc getExitCodeProcess*(hProcess: Handle, lpExitCode: var int32): WINBOOL {.
     stdcall, dynlib: "kernel32", importc: "GetExitCodeProcess".}
@@ -207,77 +204,47 @@ proc getExitCodeProcess*(hProcess: Handle, lpExitCode: var int32): WINBOOL {.
 proc getStdHandle*(nStdHandle: int32): Handle {.stdcall, dynlib: "kernel32",
     importc: "GetStdHandle".}
 proc setStdHandle*(nStdHandle: int32, hHandle: Handle): WINBOOL {.stdcall,
-    dynlib: "kernel32", importc: "SetStdHandle".}
+    dynlib: "kernel32", importc: "SetStdHandle", sideEffect.}
 proc flushFileBuffers*(hFile: Handle): WINBOOL {.stdcall, dynlib: "kernel32",
-    importc: "FlushFileBuffers".}
+    importc: "FlushFileBuffers", sideEffect.}
 
 proc getLastError*(): int32 {.importc: "GetLastError",
-    stdcall, dynlib: "kernel32".}
+    stdcall, dynlib: "kernel32", sideEffect.}
 
 proc setLastError*(error: int32) {.importc: "SetLastError",
-    stdcall, dynlib: "kernel32".}
-
-when useWinUnicode:
-  proc formatMessageW*(dwFlags: int32, lpSource: pointer,
-                      dwMessageId, dwLanguageId: int32,
-                      lpBuffer: pointer, nSize: int32,
-                      Arguments: pointer): int32 {.
-                      importc: "FormatMessageW", stdcall, dynlib: "kernel32".}
-else:
-  proc formatMessageA*(dwFlags: int32, lpSource: pointer,
+    stdcall, dynlib: "kernel32", sideEffect.}
+
+proc formatMessageW*(dwFlags: int32, lpSource: pointer,
                     dwMessageId, dwLanguageId: int32,
                     lpBuffer: pointer, nSize: int32,
-                    Arguments: pointer): int32 {.
-                    importc: "FormatMessageA", stdcall, dynlib: "kernel32".}
+                    arguments: pointer): int32 {.
+                    importc: "FormatMessageW", stdcall, dynlib: "kernel32".}
 
 proc localFree*(p: pointer) {.
   importc: "LocalFree", stdcall, dynlib: "kernel32".}
 
-when useWinUnicode:
-  proc getCurrentDirectoryW*(nBufferLength: int32,
-                             lpBuffer: WideCString): int32 {.
-    importc: "GetCurrentDirectoryW", dynlib: "kernel32", stdcall.}
-  proc setCurrentDirectoryW*(lpPathName: WideCString): int32 {.
-    importc: "SetCurrentDirectoryW", dynlib: "kernel32", stdcall.}
-  proc createDirectoryW*(pathName: WideCString, security: pointer=nil): int32 {.
-    importc: "CreateDirectoryW", dynlib: "kernel32", stdcall.}
-  proc removeDirectoryW*(lpPathName: WideCString): int32 {.
-    importc: "RemoveDirectoryW", dynlib: "kernel32", stdcall.}
-  proc setEnvironmentVariableW*(lpName, lpValue: WideCString): int32 {.
-    stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableW".}
-
-  proc getModuleFileNameW*(handle: Handle, buf: WideCString,
-                           size: int32): int32 {.importc: "GetModuleFileNameW",
-    dynlib: "kernel32", stdcall.}
-else:
-  proc getCurrentDirectoryA*(nBufferLength: int32, lpBuffer: cstring): int32 {.
-    importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
-  proc setCurrentDirectoryA*(lpPathName: cstring): int32 {.
-    importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
-  proc createDirectoryA*(pathName: cstring, security: pointer=nil): int32 {.
-    importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.}
-  proc removeDirectoryA*(lpPathName: cstring): int32 {.
-    importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.}
-  proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {.
-    stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".}
-
-  proc getModuleFileNameA*(handle: Handle, buf: cstring, size: int32): int32 {.
-    importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.}
-
-when useWinUnicode:
-  proc createSymbolicLinkW*(lpSymlinkFileName, lpTargetFileName: WideCString,
-                         flags: DWORD): int32 {.
-    importc:"CreateSymbolicLinkW", dynlib: "kernel32", stdcall.}
-  proc createHardLinkW*(lpFileName, lpExistingFileName: WideCString,
-                         security: pointer=nil): int32 {.
-    importc:"CreateHardLinkW", dynlib: "kernel32", stdcall.}
-else:
-  proc createSymbolicLinkA*(lpSymlinkFileName, lpTargetFileName: cstring,
-                           flags: DWORD): int32 {.
-    importc:"CreateSymbolicLinkA", dynlib: "kernel32", stdcall.}
-  proc createHardLinkA*(lpFileName, lpExistingFileName: cstring,
-                           security: pointer=nil): int32 {.
-    importc:"CreateHardLinkA", dynlib: "kernel32", stdcall.}
+proc getCurrentDirectoryW*(nBufferLength: int32,
+                           lpBuffer: WideCString): int32 {.
+  importc: "GetCurrentDirectoryW", dynlib: "kernel32", stdcall, sideEffect.}
+proc setCurrentDirectoryW*(lpPathName: WideCString): int32 {.
+  importc: "SetCurrentDirectoryW", dynlib: "kernel32", stdcall, sideEffect.}
+proc createDirectoryW*(pathName: WideCString, security: pointer=nil): int32 {.
+  importc: "CreateDirectoryW", dynlib: "kernel32", stdcall, sideEffect.}
+proc removeDirectoryW*(lpPathName: WideCString): int32 {.
+  importc: "RemoveDirectoryW", dynlib: "kernel32", stdcall, sideEffect.}
+proc setEnvironmentVariableW*(lpName, lpValue: WideCString): int32 {.
+  stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableW", sideEffect.}
+
+proc getModuleFileNameW*(handle: Handle, buf: WideCString,
+                         size: int32): int32 {.importc: "GetModuleFileNameW",
+  dynlib: "kernel32", stdcall.}
+
+proc createSymbolicLinkW*(lpSymlinkFileName, lpTargetFileName: WideCString,
+                       flags: DWORD): int32 {.
+  importc:"CreateSymbolicLinkW", dynlib: "kernel32", stdcall, sideEffect.}
+proc createHardLinkW*(lpFileName, lpExistingFileName: WideCString,
+                       security: pointer=nil): int32 {.
+  importc:"CreateHardLinkW", dynlib: "kernel32", stdcall, sideEffect.}
 
 const
   FILE_ATTRIBUTE_READONLY* = 0x00000001'i32
@@ -327,114 +294,67 @@ type
     dwReserved1: int32
     cFileName*: array[0..(MAX_PATH) - 1, WinChar]
     cAlternateFileName*: array[0..13, WinChar]
-{.deprecated: [TWIN32_FIND_DATA: WIN32_FIND_DATA].}
-
-when useWinUnicode:
-  proc findFirstFileW*(lpFileName: WideCString,
-                      lpFindFileData: var WIN32_FIND_DATA): Handle {.
-      stdcall, dynlib: "kernel32", importc: "FindFirstFileW".}
-  proc findNextFileW*(hFindFile: Handle,
-                     lpFindFileData: var WIN32_FIND_DATA): int32 {.
-      stdcall, dynlib: "kernel32", importc: "FindNextFileW".}
-else:
-  proc findFirstFileA*(lpFileName: cstring,
-                      lpFindFileData: var WIN32_FIND_DATA): Handle {.
-      stdcall, dynlib: "kernel32", importc: "FindFirstFileA".}
-  proc findNextFileA*(hFindFile: Handle,
-                     lpFindFileData: var WIN32_FIND_DATA): int32 {.
-      stdcall, dynlib: "kernel32", importc: "FindNextFileA".}
+
+proc findFirstFileW*(lpFileName: WideCString,
+                    lpFindFileData: var WIN32_FIND_DATA): Handle {.
+    stdcall, dynlib: "kernel32", importc: "FindFirstFileW", sideEffect.}
+proc findNextFileW*(hFindFile: Handle,
+                   lpFindFileData: var WIN32_FIND_DATA): int32 {.
+    stdcall, dynlib: "kernel32", importc: "FindNextFileW", sideEffect.}
 
 proc findClose*(hFindFile: Handle) {.stdcall, dynlib: "kernel32",
   importc: "FindClose".}
 
-when useWinUnicode:
-  proc getFullPathNameW*(lpFileName: WideCString, nBufferLength: int32,
-                        lpBuffer: WideCString,
-                        lpFilePart: var WideCString): int32 {.
+proc getFullPathNameW*(lpFileName: WideCString, nBufferLength: int32,
+                      lpBuffer: WideCString,
+                      lpFilePart: var WideCString): int32 {.
+                      stdcall, dynlib: "kernel32",
+                      importc: "GetFullPathNameW", sideEffect.}
+proc getFileAttributesW*(lpFileName: WideCString): int32 {.
                         stdcall, dynlib: "kernel32",
-                        importc: "GetFullPathNameW".}
-  proc getFileAttributesW*(lpFileName: WideCString): int32 {.
-                          stdcall, dynlib: "kernel32",
-                          importc: "GetFileAttributesW".}
-  proc setFileAttributesW*(lpFileName: WideCString,
-                           dwFileAttributes: int32): WINBOOL {.
-      stdcall, dynlib: "kernel32", importc: "SetFileAttributesW".}
-
-  proc copyFileW*(lpExistingFileName, lpNewFileName: WideCString,
-                 bFailIfExists: WINBOOL): WINBOOL {.
-    importc: "CopyFileW", stdcall, dynlib: "kernel32".}
-
-  proc moveFileW*(lpExistingFileName, lpNewFileName: WideCString): WINBOOL {.
-    importc: "MoveFileW", stdcall, dynlib: "kernel32".}
-  proc moveFileExW*(lpExistingFileName, lpNewFileName: WideCString,
-                    flags: DWORD): WINBOOL {.
-    importc: "MoveFileExW", stdcall, dynlib: "kernel32".}
-
-  proc getEnvironmentStringsW*(): WideCString {.
-    stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsW".}
-  proc freeEnvironmentStringsW*(para1: WideCString): int32 {.
-    stdcall, dynlib: "kernel32", importc: "FreeEnvironmentStringsW".}
-
-  proc getCommandLineW*(): WideCString {.importc: "GetCommandLineW",
-    stdcall, dynlib: "kernel32".}
+                        importc: "GetFileAttributesW", sideEffect.}
+proc setFileAttributesW*(lpFileName: WideCString,
+                         dwFileAttributes: int32): WINBOOL {.
+    stdcall, dynlib: "kernel32", importc: "SetFileAttributesW", sideEffect.}
 
-else:
-  proc getFullPathNameA*(lpFileName: cstring, nBufferLength: int32,
-                        lpBuffer: cstring, lpFilePart: var cstring): int32 {.
-                        stdcall, dynlib: "kernel32",
-                        importc: "GetFullPathNameA".}
-  proc getFileAttributesA*(lpFileName: cstring): int32 {.
-                          stdcall, dynlib: "kernel32",
-                          importc: "GetFileAttributesA".}
-  proc setFileAttributesA*(lpFileName: cstring,
-                           dwFileAttributes: int32): WINBOOL {.
-      stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".}
-
-  proc copyFileA*(lpExistingFileName, lpNewFileName: cstring,
-                 bFailIfExists: cint): cint {.
-    importc: "CopyFileA", stdcall, dynlib: "kernel32".}
-
-  proc moveFileA*(lpExistingFileName, lpNewFileName: cstring): WINBOOL {.
-    importc: "MoveFileA", stdcall, dynlib: "kernel32".}
-  proc moveFileExA*(lpExistingFileName, lpNewFileName: WideCString,
-                    flags: DWORD): WINBOOL {.
-    importc: "MoveFileExA", stdcall, dynlib: "kernel32".}
-
-  proc getEnvironmentStringsA*(): cstring {.
-    stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsA".}
-  proc freeEnvironmentStringsA*(para1: cstring): int32 {.
-    stdcall, dynlib: "kernel32", importc: "FreeEnvironmentStringsA".}
-
-  proc getCommandLineA*(): cstring {.
-    importc: "GetCommandLineA", stdcall, dynlib: "kernel32".}
+proc copyFileW*(lpExistingFileName, lpNewFileName: WideCString,
+               bFailIfExists: WINBOOL): WINBOOL {.
+  importc: "CopyFileW", stdcall, dynlib: "kernel32", sideEffect.}
+
+proc moveFileW*(lpExistingFileName, lpNewFileName: WideCString): WINBOOL {.
+  importc: "MoveFileW", stdcall, dynlib: "kernel32", sideEffect.}
+proc moveFileExW*(lpExistingFileName, lpNewFileName: WideCString,
+                  flags: DWORD): WINBOOL {.
+  importc: "MoveFileExW", stdcall, dynlib: "kernel32", sideEffect.}
+
+proc getEnvironmentStringsW*(): WideCString {.
+  stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsW", sideEffect.}
+proc freeEnvironmentStringsW*(para1: WideCString): int32 {.
+  stdcall, dynlib: "kernel32", importc: "FreeEnvironmentStringsW", sideEffect.}
+
+proc getCommandLineW*(): WideCString {.importc: "GetCommandLineW",
+  stdcall, dynlib: "kernel32", sideEffect.}
 
 proc rdFileTime*(f: FILETIME): int64 =
-  result = ze64(f.dwLowDateTime) or (ze64(f.dwHighDateTime) shl 32)
+  result = int64(cast[uint32](f.dwLowDateTime)) or (int64(cast[uint32](f.dwHighDateTime)) shl 32)
 
 proc rdFileSize*(f: WIN32_FIND_DATA): int64 =
-  result = ze64(f.nFileSizeLow) or (ze64(f.nFileSizeHigh) shl 32)
+  result = int64(cast[uint32](f.nFileSizeLow)) or (int64(cast[uint32](f.nFileSizeHigh)) shl 32)
 
 proc getSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var FILETIME) {.
-  importc: "GetSystemTimeAsFileTime", dynlib: "kernel32", stdcall.}
+  importc: "GetSystemTimeAsFileTime", dynlib: "kernel32", stdcall, sideEffect.}
 
 proc sleep*(dwMilliseconds: int32){.stdcall, dynlib: "kernel32",
-                                    importc: "Sleep".}
+                                    importc: "Sleep", sideEffect.}
 
-when useWinUnicode:
-  proc shellExecuteW*(HWND: Handle, lpOperation, lpFile,
-                     lpParameters, lpDirectory: WideCString,
-                     nShowCmd: int32): Handle{.
-      stdcall, dynlib: "shell32.dll", importc: "ShellExecuteW".}
-
-else:
-  proc shellExecuteA*(HWND: Handle, lpOperation, lpFile,
-                     lpParameters, lpDirectory: cstring,
-                     nShowCmd: int32): Handle{.
-      stdcall, dynlib: "shell32.dll", importc: "ShellExecuteA".}
+proc shellExecuteW*(hwnd: Handle, lpOperation, lpFile,
+                   lpParameters, lpDirectory: WideCString,
+                   nShowCmd: int32): Handle{.
+    stdcall, dynlib: "shell32.dll", importc: "ShellExecuteW", sideEffect.}
 
 proc getFileInformationByHandle*(hFile: Handle,
   lpFileInformation: ptr BY_HANDLE_FILE_INFORMATION): WINBOOL{.
-    stdcall, dynlib: "kernel32", importc: "GetFileInformationByHandle".}
+    stdcall, dynlib: "kernel32", importc: "GetFileInformationByHandle", sideEffect.}
 
 const
   WSADESCRIPTION_LEN* = 256
@@ -449,11 +369,10 @@ const
 
   ws2dll = "Ws2_32.dll"
 
-proc wsaGetLastError*(): cint {.importc: "WSAGetLastError", dynlib: ws2dll.}
+proc wsaGetLastError*(): cint {.importc: "WSAGetLastError", dynlib: ws2dll, sideEffect.}
 
 type
   SocketHandle* = distinct int
-{.deprecated: [TSocketHandle: SocketHandle].}
 
 type
   WSAData* {.importc: "WSADATA", header: "winsock2.h".} = object
@@ -464,17 +383,17 @@ type
     lpVendorInfo: cstring
 
   SockAddr* {.importc: "SOCKADDR", header: "winsock2.h".} = object
-    sa_family*: int16 # unsigned
+    sa_family*: uint16
     sa_data*: array[0..13, char]
 
   PSockAddr = ptr SockAddr
 
-  InAddr* {.importc: "IN_ADDR", header: "winsock2.h".} = object
+  InAddr* {.importc: "IN_ADDR", header: "winsock2.h", union.} = object
     s_addr*: uint32  # IP address
 
   Sockaddr_in* {.importc: "SOCKADDR_IN",
                   header: "winsock2.h".} = object
-    sin_family*: int16
+    sin_family*: uint16
     sin_port*: uint16
     sin_addr*: InAddr
     sin_zero*: array[0..7, char]
@@ -484,24 +403,18 @@ type
 
   Sockaddr_in6* {.importc: "SOCKADDR_IN6",
                    header: "ws2tcpip.h".} = object
-    sin6_family*: int16
+    sin6_family*: uint16
     sin6_port*: uint16
     sin6_flowinfo*: int32 # unsigned
     sin6_addr*: In6_addr
     sin6_scope_id*: int32 # unsigned
 
-  Sockaddr_in6_old* = object
-    sin6_family*: int16
-    sin6_port*: int16 # unsigned
-    sin6_flowinfo*: int32 # unsigned
-    sin6_addr*: In6_addr
-
   Sockaddr_storage* {.importc: "SOCKADDR_STORAGE",
                       header: "winsock2.h".} = object
-    ss_family*: int16
-    ss_pad1: array[6, byte]
-    ss_align: int64
-    ss_pad2: array[112, byte]
+    ss_family*: uint16
+    ss_pad1 {.importc: "__ss_pad1".}: array[6, byte]
+    ss_align {.importc: "__ss_align".}: int64
+    ss_pad2 {.importc: "__ss_pad2".}: array[112, byte]
 
   Servent* = object
     s_name*: cstring
@@ -524,26 +437,26 @@ 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.
     ai_socktype*: cint      ## Socket type.
     ai_protocol*: cint      ## Protocol of socket.
-    ai_addrlen*: csize        ## Length of socket address.
+    ai_addrlen*: csize_t        ## Length of socket address.
     ai_canonname*: cstring  ## Canonical name of service location.
     ai_addr*: ptr SockAddr ## Socket address of socket.
     ai_next*: ptr AddrInfo ## Pointer to next in list.
 
   SockLen* = cuint
-{.deprecated: [TSockaddr_in: Sockaddr_in, TAddrinfo: AddrInfo,
-    TSockAddr: SockAddr, TSockLen: SockLen, TTimeval: Timeval,
-    TWSADATA: WSADATA, Thostent: Hostent, TServent: Servent,
-    TInAddr: InAddr, Tin6_addr: In6_addr, Tsockaddr_in6: Sockaddr_in6,
-    Tsockaddr_in6_old: Sockaddr_in6_old].}
 
+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
@@ -569,19 +482,27 @@ var
 proc `==`*(x, y: SocketHandle): bool {.borrow.}
 
 proc getservbyname*(name, proto: cstring): ptr Servent {.
-  stdcall, importc: "getservbyname", dynlib: ws2dll.}
+  stdcall, importc: "getservbyname", dynlib: ws2dll, sideEffect.}
 
 proc getservbyport*(port: cint, proto: cstring): ptr Servent {.
-  stdcall, importc: "getservbyport", dynlib: ws2dll.}
+  stdcall, importc: "getservbyport", dynlib: ws2dll, sideEffect.}
 
 proc gethostbyaddr*(ip: ptr InAddr, len: cuint, theType: cint): ptr Hostent {.
-  stdcall, importc: "gethostbyaddr", dynlib: ws2dll.}
+  stdcall, importc: "gethostbyaddr", dynlib: ws2dll, sideEffect.}
 
 proc gethostbyname*(name: cstring): ptr Hostent {.
-  stdcall, importc: "gethostbyname", dynlib: ws2dll.}
+  stdcall, importc: "gethostbyname", dynlib: ws2dll, sideEffect.}
 
 proc gethostname*(hostname: cstring, len: cint): cint {.
-  stdcall, importc: "gethostname", dynlib: ws2dll.}
+  stdcall, importc: "gethostname", dynlib: ws2dll, sideEffect.}
+
+proc getprotobyname*(
+  name: cstring
+): ptr Protoent {.stdcall, importc: "getprotobyname", dynlib: ws2dll, sideEffect.}
+
+proc getprotobynumber*(
+  proto: cint
+): ptr Protoent {.stdcall, importc: "getprotobynumber", dynlib: ws2dll, sideEffect.}
 
 proc socket*(af, typ, protocol: cint): SocketHandle {.
   stdcall, importc: "socket", dynlib: ws2dll.}
@@ -656,7 +577,7 @@ proc getaddrinfo*(nodename, servname: cstring, hints: ptr AddrInfo,
                   res: var ptr AddrInfo): cint {.
   stdcall, importc: "getaddrinfo", dynlib: ws2dll.}
 
-proc freeaddrinfo*(ai: ptr AddrInfo) {.
+proc freeAddrInfo*(ai: ptr AddrInfo) {.
   stdcall, importc: "freeaddrinfo", dynlib: ws2dll.}
 
 proc inet_ntoa*(i: InAddr): cstring {.
@@ -668,7 +589,6 @@ const
 type
   WOHandleArray* = array[0..MAXIMUM_WAIT_OBJECTS - 1, Handle]
   PWOHandleArray* = ptr WOHandleArray
-{.deprecated: [TWOHandleArray: WOHandleArray].}
 
 proc waitForMultipleObjects*(nCount: DWORD, lpHandles: PWOHandleArray,
                              bWaitAll: WINBOOL, dwMilliseconds: DWORD): DWORD{.
@@ -692,6 +612,10 @@ const
   FILE_BEGIN* = 0'i32
   INVALID_SET_FILE_POINTER* = -1'i32
   NO_ERROR* = 0'i32
+  PAGE_NOACCESS* = 0x01'i32
+  PAGE_EXECUTE* = 0x10'i32
+  PAGE_EXECUTE_READ* = 0x20'i32
+  PAGE_EXECUTE_READWRITE* = 0x40'i32
   PAGE_READONLY* = 2'i32
   PAGE_READWRITE* = 4'i32
   FILE_MAP_READ* = 4'i32
@@ -704,44 +628,46 @@ const
 
 # Error Constants
 const
-  ERROR_FILE_NOT_FOUND* = 2
+  ERROR_FILE_NOT_FOUND* = 2 ## https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
   ERROR_PATH_NOT_FOUND* = 3
   ERROR_ACCESS_DENIED* = 5
   ERROR_NO_MORE_FILES* = 18
   ERROR_LOCK_VIOLATION* = 33
   ERROR_HANDLE_EOF* = 38
+  ERROR_FILE_EXISTS* = 80
   ERROR_BAD_ARGUMENTS* = 165
 
-proc duplicateHandle*(hSourceProcessHandle: HANDLE, hSourceHandle: HANDLE,
-                      hTargetProcessHandle: HANDLE,
-                      lpTargetHandle: ptr HANDLE,
+proc duplicateHandle*(hSourceProcessHandle: Handle, hSourceHandle: Handle,
+                      hTargetProcessHandle: Handle,
+                      lpTargetHandle: ptr Handle,
                       dwDesiredAccess: DWORD, bInheritHandle: WINBOOL,
                       dwOptions: DWORD): WINBOOL{.stdcall, dynlib: "kernel32",
     importc: "DuplicateHandle".}
 
-proc setHandleInformation*(hObject: HANDLE, dwMask: DWORD,
+proc getHandleInformation*(hObject: Handle, lpdwFlags: ptr DWORD): WINBOOL {.
+    stdcall, dynlib: "kernel32", importc: "GetHandleInformation".}
+
+proc setHandleInformation*(hObject: Handle, dwMask: DWORD,
                            dwFlags: DWORD): WINBOOL {.stdcall,
     dynlib: "kernel32", importc: "SetHandleInformation".}
 
-proc getCurrentProcess*(): HANDLE{.stdcall, dynlib: "kernel32",
+proc getCurrentProcess*(): Handle{.stdcall, dynlib: "kernel32",
                                    importc: "GetCurrentProcess".}
 
-when useWinUnicode:
-  proc createFileW*(lpFileName: WideCString, dwDesiredAccess, dwShareMode: DWORD,
-                    lpSecurityAttributes: pointer,
-                    dwCreationDisposition, dwFlagsAndAttributes: DWORD,
-                    hTemplateFile: Handle): Handle {.
-      stdcall, dynlib: "kernel32", importc: "CreateFileW".}
-  proc deleteFileW*(pathName: WideCString): int32 {.
-    importc: "DeleteFileW", dynlib: "kernel32", stdcall.}
-else:
-  proc createFileA*(lpFileName: cstring, dwDesiredAccess, dwShareMode: DWORD,
-                    lpSecurityAttributes: pointer,
-                    dwCreationDisposition, dwFlagsAndAttributes: DWORD,
-                    hTemplateFile: Handle): Handle {.
-      stdcall, dynlib: "kernel32", importc: "CreateFileA".}
-  proc deleteFileA*(pathName: cstring): int32 {.
-    importc: "DeleteFileA", dynlib: "kernel32", stdcall.}
+proc createFileW*(lpFileName: WideCString, dwDesiredAccess, dwShareMode: DWORD,
+                  lpSecurityAttributes: pointer,
+                  dwCreationDisposition, dwFlagsAndAttributes: DWORD,
+                  hTemplateFile: Handle): Handle {.
+    stdcall, dynlib: "kernel32", importc: "CreateFileW".}
+proc deleteFileW*(pathName: WideCString): int32 {.
+  importc: "DeleteFileW", dynlib: "kernel32", stdcall.}
+proc createFileA*(lpFileName: cstring, dwDesiredAccess, dwShareMode: DWORD,
+                  lpSecurityAttributes: pointer,
+                  dwCreationDisposition, dwFlagsAndAttributes: DWORD,
+                  hTemplateFile: Handle): Handle {.
+    stdcall, dynlib: "kernel32", importc: "CreateFileA".}
+proc deleteFileA*(pathName: cstring): int32 {.
+  importc: "DeleteFileA", dynlib: "kernel32", stdcall.}
 
 proc setEndOfFile*(hFile: Handle): WINBOOL {.stdcall, dynlib: "kernel32",
     importc: "SetEndOfFile".}
@@ -754,9 +680,16 @@ proc setFilePointer*(hFile: Handle, lDistanceToMove: LONG,
 proc getFileSize*(hFile: Handle, lpFileSizeHigh: ptr DWORD): DWORD{.stdcall,
     dynlib: "kernel32", importc: "GetFileSize".}
 
+when defined(cpu32):
+  type
+    WinSizeT* = uint32
+else:
+  type
+    WinSizeT* = uint64
+
 proc mapViewOfFileEx*(hFileMappingObject: Handle, dwDesiredAccess: DWORD,
                       dwFileOffsetHigh, dwFileOffsetLow: DWORD,
-                      dwNumberOfBytesToMap: DWORD,
+                      dwNumberOfBytesToMap: WinSizeT,
                       lpBaseAddress: pointer): pointer{.
     stdcall, dynlib: "kernel32", importc: "MapViewOfFileEx".}
 
@@ -767,13 +700,6 @@ proc createFileMappingW*(hFile: Handle,
                        lpName: pointer): Handle {.
   stdcall, dynlib: "kernel32", importc: "CreateFileMappingW".}
 
-when not useWinUnicode:
-  proc createFileMappingA*(hFile: Handle,
-                           lpFileMappingAttributes: pointer,
-                           flProtect, dwMaximumSizeHigh: DWORD,
-                           dwMaximumSizeLow: DWORD, lpName: cstring): Handle {.
-      stdcall, dynlib: "kernel32", importc: "CreateFileMappingA".}
-
 proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall,
     dynlib: "kernel32", importc: "UnmapViewOfFile".}
 
@@ -793,12 +719,11 @@ type
   POVERLAPPED_COMPLETION_ROUTINE* = proc (para1: DWORD, para2: DWORD,
       para3: POVERLAPPED){.stdcall.}
 
-  GUID* {.final, pure.} = object
+  GUID* = object
     D1*: int32
     D2*: int16
     D3*: int16
     D4*: array[0..7, int8]
-{.deprecated: [TOVERLAPPED: OVERLAPPED, TGUID: GUID].}
 
 const
   ERROR_IO_PENDING* = 997 # a.k.a WSA_IO_PENDING
@@ -813,6 +738,7 @@ const
   WSAEINPROGRESS* = 10036
   WSAEINTR* = 10004
   WSAEWOULDBLOCK* = 10035
+  WSAESHUTDOWN* = 10058
   ERROR_NETNAME_DELETED* = 64
   STATUS_PENDING* = 0x103
 
@@ -838,9 +764,9 @@ template hasOverlappedIoCompleted*(lpOverlapped): bool =
   (cast[uint](lpOverlapped.internal) != STATUS_PENDING)
 
 const
- IOC_OUT* = 0x40000000
- IOC_IN*  = 0x80000000
- IOC_WS2* = 0x08000000
+ IOC_OUT* = 0x40000000'i32
+ IOC_IN*  = 0x80000000'i32
+ IOC_WS2* = 0x08000000'i32
  IOC_INOUT* = IOC_IN or IOC_OUT
 
 template WSAIORW*(x,y): untyped = (IOC_INOUT or x or y)
@@ -905,8 +831,11 @@ proc getProcessTimes*(hProcess: Handle; lpCreationTime, lpExitTime,
   lpKernelTime, lpUserTime: var FILETIME): WINBOOL {.stdcall,
   dynlib: "kernel32", importc: "GetProcessTimes".}
 
+proc getSystemTimePreciseAsFileTime*(lpSystemTimeAsFileTime: var FILETIME) {.
+  importc: "GetSystemTimePreciseAsFileTime", dynlib: "kernel32", stdcall, sideEffect.}
+
 type inet_ntop_proc = proc(family: cint, paddr: pointer, pStringBuffer: cstring,
-                      stringBufSize: int32): cstring {.gcsafe, stdcall.}
+                      stringBufSize: int32): cstring {.gcsafe, stdcall, tags: [].}
 
 var inet_ntop_real: inet_ntop_proc = nil
 
@@ -946,7 +875,7 @@ proc inet_ntop*(family: cint, paddr: pointer, pStringBuffer: cstring,
                   stringBufSize: int32): cstring {.stdcall.} =
   var ver: OSVERSIONINFO
   ver.dwOSVersionInfoSize = sizeof(ver).DWORD
-  let res = when useWinUnicode: getVersionExW(ver.addr) else: getVersionExA(ver.addr)
+  let res = getVersionExW(ver.addr)
   if res == 0:
     result = nil
   elif ver.dwMajorVersion >= 6:
@@ -964,13 +893,13 @@ type
                             dwRemoteAddressLength: DWORD,
                             lpdwBytesReceived: ptr DWORD,
                             lpOverlapped: POVERLAPPED): bool {.
-                            stdcall,gcsafe.}
+                            stdcall, gcsafe, raises: [].}
 
   WSAPROC_CONNECTEX* = proc (s: SocketHandle, name: ptr SockAddr, namelen: cint,
                              lpSendBuffer: pointer, dwSendDataLength: DWORD,
                              lpdwBytesSent: ptr DWORD,
                              lpOverlapped: POVERLAPPED): bool {.
-                             stdcall,gcsafe.}
+                             stdcall, gcsafe, raises: [].}
 
   WSAPROC_GETACCEPTEXSOCKADDRS* = proc(lpOutputBuffer: pointer,
                                        dwReceiveDataLength: DWORD,
@@ -980,7 +909,7 @@ type
                                        LocalSockaddrLength: ptr cint,
                                        RemoteSockaddr: ptr PSockAddr,
                                        RemoteSockaddrLength: ptr cint) {.
-                                       stdcall,gcsafe.}
+                                       stdcall, gcsafe, raises: [].}
 
 const
   WT_EXECUTEDEFAULT* = 0x00000000'i32
@@ -1008,9 +937,9 @@ const
   PROCESS_QUERY_LIMITED_INFORMATION* = 0x00001000'i32
   PROCESS_SET_LIMITED_INFORMATION* = 0x00002000'i32
 type
-  WAITORTIMERCALLBACK* = proc(para1: pointer, para2: int32): void {.stdcall.}
+  WAITORTIMERCALLBACK* = proc(para1: pointer, para2: int32) {.stdcall.}
 
-proc postQueuedCompletionStatus*(CompletionPort: HANDLE,
+proc postQueuedCompletionStatus*(CompletionPort: Handle,
                                 dwNumberOfBytesTransferred: DWORD,
                                 dwCompletionKey: ULONG_PTR,
                                 lpOverlapped: pointer): bool
@@ -1023,23 +952,17 @@ proc registerWaitForSingleObject*(phNewWaitObject: ptr Handle, hObject: Handle,
                                  dwFlags: ULONG): bool
      {.stdcall, dynlib: "kernel32", importc: "RegisterWaitForSingleObject".}
 
-proc unregisterWait*(WaitHandle: HANDLE): DWORD
+proc unregisterWait*(WaitHandle: Handle): DWORD
      {.stdcall, dynlib: "kernel32", importc: "UnregisterWait".}
 
 proc openProcess*(dwDesiredAccess: DWORD, bInheritHandle: WINBOOL,
                     dwProcessId: DWORD): Handle
      {.stdcall, dynlib: "kernel32", importc: "OpenProcess".}
 
-when defined(useWinAnsi):
-  proc createEvent*(lpEventAttributes: ptr SECURITY_ATTRIBUTES,
-                    bManualReset: DWORD, bInitialState: DWORD,
-                    lpName: cstring): Handle
-       {.stdcall, dynlib: "kernel32", importc: "CreateEventA".}
-else:
-  proc createEvent*(lpEventAttributes: ptr SECURITY_ATTRIBUTES,
-                    bManualReset: DWORD, bInitialState: DWORD,
-                    lpName: ptr Utf16Char): Handle
-       {.stdcall, dynlib: "kernel32", importc: "CreateEventW".}
+proc createEvent*(lpEventAttributes: ptr SECURITY_ATTRIBUTES,
+                  bManualReset: DWORD, bInitialState: DWORD,
+                  lpName: ptr Utf16Char): Handle
+     {.stdcall, dynlib: "kernel32", importc: "CreateEventW".}
 
 proc setEvent*(hEvent: Handle): cint
      {.stdcall, dynlib: "kernel32", importc: "SetEvent".}
@@ -1071,7 +994,7 @@ proc wsaResetEvent*(hEvent: Handle): bool
      {.stdcall, importc: "WSAResetEvent", dynlib: "ws2_32.dll".}
 
 type
-  KEY_EVENT_RECORD* {.final, pure.} = object
+  KEY_EVENT_RECORD* = object
     eventType*: int16
     bKeyDown*: WINBOOL
     wRepeatCount*: int16
@@ -1080,17 +1003,12 @@ type
     uChar*: int16
     dwControlKeyState*: DWORD
 
-when defined(useWinAnsi):
-  proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint,
-                        lpNumberOfEventsRead: ptr cint): cint
-       {.stdcall, dynlib: "kernel32", importc: "ReadConsoleInputA".}
-else:
-  proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint,
-                        lpNumberOfEventsRead: ptr cint): cint
-       {.stdcall, dynlib: "kernel32", importc: "ReadConsoleInputW".}
+proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint,
+                      lpNumberOfEventsRead: ptr cint): cint
+     {.stdcall, dynlib: "kernel32", importc: "ReadConsoleInputW".}
 
 type
-  LPFIBER_START_ROUTINE* = proc (param: pointer): void {.stdcall.}
+  LPFIBER_START_ROUTINE* = proc (param: pointer) {.stdcall.}
 
 const
   FIBER_FLAG_FLOAT_SWITCH* = 0x01
@@ -1099,17 +1017,57 @@ proc CreateFiber*(stackSize: int, fn: LPFIBER_START_ROUTINE, param: pointer): po
 proc CreateFiberEx*(stkCommit: int, stkReserve: int, flags: int32, fn: LPFIBER_START_ROUTINE, param: pointer): pointer {.stdcall, discardable, dynlib: "kernel32", importc.}
 proc ConvertThreadToFiber*(param: pointer): pointer {.stdcall, discardable, dynlib: "kernel32", importc.}
 proc ConvertThreadToFiberEx*(param: pointer, flags: int32): pointer {.stdcall, discardable, dynlib: "kernel32", importc.}
-proc DeleteFiber*(fiber: pointer): void {.stdcall, discardable, dynlib: "kernel32", importc.}
-proc SwitchToFiber*(fiber: pointer): void {.stdcall, discardable, dynlib: "kernel32", importc.}
-proc GetCurrentFiber*(): pointer {.stdcall, importc, header: "Windows.h".}
+proc DeleteFiber*(fiber: pointer) {.stdcall, discardable, dynlib: "kernel32", importc.}
+proc SwitchToFiber*(fiber: pointer) {.stdcall, discardable, dynlib: "kernel32", importc.}
+proc GetCurrentFiber*(): pointer {.stdcall, importc, header: "windows.h".}
 
 proc toFILETIME*(t: int64): FILETIME =
-  ## Convert the Windows file time timestamp ``t`` to ``FILETIME``.
+  ## Convert the Windows file time timestamp `t` to `FILETIME`.
   result = FILETIME(dwLowDateTime: cast[DWORD](t), dwHighDateTime: DWORD(t shr 32))
 
 type
   LPFILETIME* = ptr FILETIME
 
-proc setFileTime*(hFile: HANDLE, lpCreationTime: LPFILETIME,
+proc setFileTime*(hFile: Handle, lpCreationTime: LPFILETIME,
                  lpLastAccessTime: LPFILETIME, lpLastWriteTime: LPFILETIME): WINBOOL
      {.stdcall, dynlib: "kernel32", importc: "SetFileTime".}
+
+type
+  # https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid_identifier_authority
+  SID_IDENTIFIER_AUTHORITY* {.importc, header: "<windows.h>".} = object
+    value* {.importc: "Value".}: array[6, BYTE]
+  # https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid
+  SID* {.importc, header: "<windows.h>".} = object
+    Revision: BYTE
+    SubAuthorityCount: BYTE
+    IdentifierAuthority: SID_IDENTIFIER_AUTHORITY
+    SubAuthority: ptr ptr DWORD
+  PSID* = ptr SID
+
+const
+  # https://docs.microsoft.com/en-us/windows/win32/secauthz/sid-components
+  # https://github.com/mirror/mingw-w64/blob/84c950bdab7c999ace49fe8383856be77f88c4a8/mingw-w64-headers/include/winnt.h#L2994
+  SECURITY_NT_AUTHORITY* = [BYTE(0), BYTE(0), BYTE(0), BYTE(0), BYTE(0), BYTE(5)]
+  SECURITY_BUILTIN_DOMAIN_RID* = 32
+  DOMAIN_ALIAS_RID_ADMINS* = 544
+
+proc allocateAndInitializeSid*(pIdentifierAuthority: ptr SID_IDENTIFIER_AUTHORITY,
+                               nSubAuthorityCount: BYTE,
+                               nSubAuthority0: DWORD,
+                               nSubAuthority1: DWORD,
+                               nSubAuthority2: DWORD,
+                               nSubAuthority3: DWORD,
+                               nSubAuthority4: DWORD,
+                               nSubAuthority5: DWORD,
+                               nSubAuthority6: DWORD,
+                               nSubAuthority7: DWORD,
+                               pSid: ptr PSID): WINBOOL
+     {.stdcall, dynlib: "Advapi32", importc: "AllocateAndInitializeSid".}
+proc checkTokenMembership*(tokenHandle: Handle, sidToCheck: PSID,
+                           isMember: PBOOL): WINBOOL
+     {.stdcall, dynlib: "Advapi32", importc: "CheckTokenMembership".}
+proc freeSid*(pSid: PSID): PSID
+     {.stdcall, dynlib: "Advapi32", importc: "FreeSid".}
+
+when defined(nimHasStyleChecks):
+  {.pop.} # {.push styleChecks: off.}
diff --git a/lib/wrappers/iup.nim b/lib/wrappers/iup.nim
deleted file mode 100644
index dee2e14c7..000000000
--- a/lib/wrappers/iup.nim
+++ /dev/null
@@ -1,957 +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.
-#
-
-#    C header files translated by hand
-#    Licence of IUP follows:
-
-
-# ****************************************************************************
-# Copyright (C) 1994-2009 Tecgraf, PUC-Rio.
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-# ****************************************************************************
-
-{.deadCodeElim: on.}  # dce option deprecated
-
-when defined(windows):
-  const dllname = "iup(|30|27|26|25|24).dll"
-elif defined(macosx):
-  const dllname = "libiup(|3.0|2.7|2.6|2.5|2.4).dylib"
-else:
-  const dllname = "libiup(|3.0|2.7|2.6|2.5|2.4).so(|.1)"
-
-const
-  IUP_NAME* = "IUP - Portable User Interface"
-  IUP_COPYRIGHT* = "Copyright (C) 1994-2009 Tecgraf, PUC-Rio."
-  IUP_DESCRIPTION* = "Portable toolkit for building graphical user interfaces."
-  constIUP_VERSION* = "3.0"
-  constIUP_VERSION_NUMBER* = 300000
-  constIUP_VERSION_DATE* = "2009/07/18"
-
-type
-  Ihandle = object
-  PIhandle* = ptr Ihandle
-
-  Icallback* = proc (arg: PIhandle): cint {.cdecl.}
-
-#                      pre-defineded dialogs
-proc fileDlg*: PIhandle {.importc: "IupFileDlg", dynlib: dllname, cdecl.}
-proc messageDlg*: PIhandle {.importc: "IupMessageDlg", dynlib: dllname, cdecl.}
-proc colorDlg*: PIhandle {.importc: "IupColorDlg", dynlib: dllname, cdecl.}
-proc fontDlg*: PIhandle {.importc: "IupFontDlg", dynlib: dllname, cdecl.}
-
-proc getFile*(arq: cstring): cint {.
-  importc: "IupGetFile", dynlib: dllname, cdecl.}
-proc message*(title, msg: cstring) {.
-  importc: "IupMessage", dynlib: dllname, cdecl.}
-proc messagef*(title, format: cstring) {.
-  importc: "IupMessagef", dynlib: dllname, cdecl, varargs.}
-proc alarm*(title, msg, b1, b2, b3: cstring): cint {.
-  importc: "IupAlarm", dynlib: dllname, cdecl.}
-proc scanf*(format: cstring): cint {.
-  importc: "IupScanf", dynlib: dllname, cdecl, varargs.}
-proc listDialog*(theType: cint, title: cstring, size: cint,
-                 list: cstringArray, op, maxCol, maxLin: cint,
-                 marks: ptr cint): cint {.
-                 importc: "IupListDialog", dynlib: dllname, cdecl.}
-proc getText*(title, text: cstring): cint {.
-  importc: "IupGetText", dynlib: dllname, cdecl.}
-proc getColor*(x, y: cint, r, g, b: var byte): cint {.
-  importc: "IupGetColor", dynlib: dllname, cdecl.}
-
-type
-  Iparamcb* = proc (dialog: PIhandle, paramIndex: cint,
-                    userData: pointer): cint {.cdecl.}
-
-proc getParam*(title: cstring, action: Iparamcb, userData: pointer,
-               format: cstring): cint {.
-               importc: "IupGetParam", cdecl, varargs, dynlib: dllname.}
-proc getParamv*(title: cstring, action: Iparamcb, userData: pointer,
-                format: cstring, paramCount, paramExtra: cint,
-                paramData: pointer): cint {.
-                importc: "IupGetParamv", cdecl, dynlib: dllname.}
-
-
-#                      Functions
-
-proc open*(argc: ptr cint, argv: ptr cstringArray): cint {.
-  importc: "IupOpen", cdecl, dynlib: dllname.}
-proc close*() {.importc: "IupClose", cdecl, dynlib: dllname.}
-proc imageLibOpen*() {.importc: "IupImageLibOpen", cdecl, dynlib: dllname.}
-
-proc mainLoop*(): cint {.importc: "IupMainLoop", cdecl, dynlib: dllname,
-                         discardable.}
-proc loopStep*(): cint {.importc: "IupLoopStep", cdecl, dynlib: dllname,
-                         discardable.}
-proc mainLoopLevel*(): cint {.importc: "IupMainLoopLevel", cdecl,
-                              dynlib: dllname, discardable.}
-proc flush*() {.importc: "IupFlush", cdecl, dynlib: dllname.}
-proc exitLoop*() {.importc: "IupExitLoop", cdecl, dynlib: dllname.}
-
-proc update*(ih: PIhandle) {.importc: "IupUpdate", cdecl, dynlib: dllname.}
-proc updateChildren*(ih: PIhandle) {.importc: "IupUpdateChildren", cdecl, dynlib: dllname.}
-proc redraw*(ih: PIhandle, children: cint) {.importc: "IupRedraw", cdecl, dynlib: dllname.}
-proc refresh*(ih: PIhandle) {.importc: "IupRefresh", cdecl, dynlib: dllname.}
-
-proc mapFont*(iupfont: cstring): cstring {.importc: "IupMapFont", cdecl, dynlib: dllname.}
-proc unMapFont*(driverfont: cstring): cstring {.importc: "IupUnMapFont", cdecl, dynlib: dllname.}
-proc help*(url: cstring): cint {.importc: "IupHelp", cdecl, dynlib: dllname.}
-proc load*(filename: cstring): cstring {.importc: "IupLoad", cdecl, dynlib: dllname.}
-
-proc iupVersion*(): cstring {.importc: "IupVersion", cdecl, dynlib: dllname.}
-proc iupVersionDate*(): cstring {.importc: "IupVersionDate", cdecl, dynlib: dllname.}
-proc iupVersionNumber*(): cint {.importc: "IupVersionNumber", cdecl, dynlib: dllname.}
-proc setLanguage*(lng: cstring) {.importc: "IupSetLanguage", cdecl, dynlib: dllname.}
-proc getLanguage*(): cstring {.importc: "IupGetLanguage", cdecl, dynlib: dllname.}
-
-proc destroy*(ih: PIhandle) {.importc: "IupDestroy", cdecl, dynlib: dllname.}
-proc detach*(child: PIhandle) {.importc: "IupDetach", cdecl, dynlib: dllname.}
-proc append*(ih, child: PIhandle): PIhandle {.
-  importc: "IupAppend", cdecl, dynlib: dllname, discardable.}
-proc insert*(ih, refChild, child: PIhandle): PIhandle {.
-  importc: "IupInsert", cdecl, dynlib: dllname, discardable.}
-proc getChild*(ih: PIhandle, pos: cint): PIhandle {.
-  importc: "IupGetChild", cdecl, dynlib: dllname.}
-proc getChildPos*(ih, child: PIhandle): cint {.
-  importc: "IupGetChildPos", cdecl, dynlib: dllname.}
-proc getChildCount*(ih: PIhandle): cint {.
-  importc: "IupGetChildCount", cdecl, dynlib: dllname.}
-proc getNextChild*(ih, child: PIhandle): PIhandle {.
-  importc: "IupGetNextChild", cdecl, dynlib: dllname.}
-proc getBrother*(ih: PIhandle): PIhandle {.
-  importc: "IupGetBrother", cdecl, dynlib: dllname.}
-proc getParent*(ih: PIhandle): PIhandle {.
-  importc: "IupGetParent", cdecl, dynlib: dllname.}
-proc getDialog*(ih: PIhandle): PIhandle {.
-  importc: "IupGetDialog", cdecl, dynlib: dllname.}
-proc getDialogChild*(ih: PIhandle, name: cstring): PIhandle {.
-  importc: "IupGetDialogChild", cdecl, dynlib: dllname.}
-proc reparent*(ih, newParent: PIhandle): cint {.
-  importc: "IupReparent", cdecl, dynlib: dllname.}
-
-proc popup*(ih: PIhandle, x, y: cint): cint {.
-  importc: "IupPopup", cdecl, dynlib: dllname, discardable.}
-proc show*(ih: PIhandle): cint {.
-  importc: "IupShow", cdecl, dynlib: dllname, discardable.}
-proc showXY*(ih: PIhandle, x, y: cint): cint {.
-  importc: "IupShowXY", cdecl, dynlib: dllname, discardable.}
-proc hide*(ih: PIhandle): cint {.
-  importc: "IupHide", cdecl, dynlib: dllname, discardable.}
-proc map*(ih: PIhandle): cint {.
-  importc: "IupMap", cdecl, dynlib: dllname, discardable.}
-proc unmap*(ih: PIhandle) {.
-  importc: "IupUnmap", cdecl, dynlib: dllname, discardable.}
-
-proc setAttribute*(ih: PIhandle, name, value: cstring) {.
-  importc: "IupSetAttribute", cdecl, dynlib: dllname.}
-proc storeAttribute*(ih: PIhandle, name, value: cstring) {.
-  importc: "IupStoreAttribute", cdecl, dynlib: dllname.}
-proc setAttributes*(ih: PIhandle, str: cstring): PIhandle {.
-  importc: "IupSetAttributes", cdecl, dynlib: dllname.}
-proc getAttribute*(ih: PIhandle, name: cstring): cstring {.
-  importc: "IupGetAttribute", cdecl, dynlib: dllname.}
-proc getAttributes*(ih: PIhandle): cstring {.
-  importc: "IupGetAttributes", cdecl, dynlib: dllname.}
-proc getInt*(ih: PIhandle, name: cstring): cint {.
-  importc: "IupGetInt", cdecl, dynlib: dllname.}
-proc getInt2*(ih: PIhandle, name: cstring): cint {.
-  importc: "IupGetInt2", cdecl, dynlib: dllname.}
-proc getIntInt*(ih: PIhandle, name: cstring, i1, i2: var cint): cint {.
-  importc: "IupGetIntInt", cdecl, dynlib: dllname.}
-proc getFloat*(ih: PIhandle, name: cstring): cfloat {.
-  importc: "IupGetFloat", cdecl, dynlib: dllname.}
-proc setfAttribute*(ih: PIhandle, name, format: cstring) {.
-  importc: "IupSetfAttribute", cdecl, dynlib: dllname, varargs.}
-proc getAllAttributes*(ih: PIhandle, names: cstringArray, n: cint): cint {.
-  importc: "IupGetAllAttributes", cdecl, dynlib: dllname.}
-proc setAtt*(handleName: cstring, ih: PIhandle, name: cstring): PIhandle {.
-  importc: "IupSetAtt", cdecl, dynlib: dllname, varargs, discardable.}
-
-proc setGlobal*(name, value: cstring) {.
-  importc: "IupSetGlobal", cdecl, dynlib: dllname.}
-proc storeGlobal*(name, value: cstring) {.
-  importc: "IupStoreGlobal", cdecl, dynlib: dllname.}
-proc getGlobal*(name: cstring): cstring {.
-  importc: "IupGetGlobal", cdecl, dynlib: dllname.}
-
-proc setFocus*(ih: PIhandle): PIhandle {.
-  importc: "IupSetFocus", cdecl, dynlib: dllname.}
-proc getFocus*(): PIhandle {.
-  importc: "IupGetFocus", cdecl, dynlib: dllname.}
-proc previousField*(ih: PIhandle): PIhandle {.
-  importc: "IupPreviousField", cdecl, dynlib: dllname.}
-proc nextField*(ih: PIhandle): PIhandle {.
-  importc: "IupNextField", cdecl, dynlib: dllname.}
-
-proc getCallback*(ih: PIhandle, name: cstring): Icallback {.
-  importc: "IupGetCallback", cdecl, dynlib: dllname.}
-proc setCallback*(ih: PIhandle, name: cstring, fn: Icallback): Icallback {.
-  importc: "IupSetCallback", cdecl, dynlib: dllname, discardable.}
-
-proc setCallbacks*(ih: PIhandle, name: cstring, fn: Icallback): PIhandle {.
-  importc: "IupSetCallbacks", cdecl, dynlib: dllname, varargs, discardable.}
-
-proc getFunction*(name: cstring): Icallback {.
-  importc: "IupGetFunction", cdecl, dynlib: dllname.}
-proc setFunction*(name: cstring, fn: Icallback): Icallback {.
-  importc: "IupSetFunction", cdecl, dynlib: dllname, discardable.}
-proc getActionName*(): cstring {.
-  importc: "IupGetActionName", cdecl, dynlib: dllname.}
-
-proc getHandle*(name: cstring): PIhandle {.
-  importc: "IupGetHandle", cdecl, dynlib: dllname.}
-proc setHandle*(name: cstring, ih: PIhandle): PIhandle {.
-  importc: "IupSetHandle", cdecl, dynlib: dllname.}
-proc getAllNames*(names: cstringArray, n: cint): cint {.
-  importc: "IupGetAllNames", cdecl, dynlib: dllname.}
-proc getAllDialogs*(names: cstringArray, n: cint): cint {.
-  importc: "IupGetAllDialogs", cdecl, dynlib: dllname.}
-proc getName*(ih: PIhandle): cstring {.
-  importc: "IupGetName", cdecl, dynlib: dllname.}
-
-proc setAttributeHandle*(ih: PIhandle, name: cstring, ihNamed: PIhandle) {.
-  importc: "IupSetAttributeHandle", cdecl, dynlib: dllname.}
-proc getAttributeHandle*(ih: PIhandle, name: cstring): PIhandle {.
-  importc: "IupGetAttributeHandle", cdecl, dynlib: dllname.}
-
-proc getClassName*(ih: PIhandle): cstring {.
-  importc: "IupGetClassName", cdecl, dynlib: dllname.}
-proc getClassType*(ih: PIhandle): cstring {.
-  importc: "IupGetClassType", cdecl, dynlib: dllname.}
-proc getClassAttributes*(classname: cstring, names: cstringArray,
-                         n: cint): cint {.
-  importc: "IupGetClassAttributes", cdecl, dynlib: dllname.}
-proc saveClassAttributes*(ih: PIhandle) {.
-  importc: "IupSaveClassAttributes", cdecl, dynlib: dllname.}
-proc setClassDefaultAttribute*(classname, name, value: cstring) {.
-  importc: "IupSetClassDefaultAttribute", cdecl, dynlib: dllname.}
-
-proc create*(classname: cstring): PIhandle {.
-  importc: "IupCreate", cdecl, dynlib: dllname.}
-proc createv*(classname: cstring, params: pointer): PIhandle {.
-  importc: "IupCreatev", cdecl, dynlib: dllname.}
-proc createp*(classname: cstring, first: pointer): PIhandle {.
-  importc: "IupCreatep", cdecl, dynlib: dllname, varargs.}
-
-proc fill*(): PIhandle {.importc: "IupFill", cdecl, dynlib: dllname.}
-proc radio*(child: PIhandle): PIhandle {.
-  importc: "IupRadio", cdecl, dynlib: dllname.}
-proc vbox*(child: PIhandle): PIhandle {.
-  importc: "IupVbox", cdecl, dynlib: dllname, varargs.}
-proc vboxv*(children: ptr PIhandle): PIhandle {.
-  importc: "IupVboxv", cdecl, dynlib: dllname.}
-proc zbox*(child: PIhandle): PIhandle {.
-  importc: "IupZbox", cdecl, dynlib: dllname, varargs.}
-proc zboxv*(children: ptr PIhandle): PIhandle {.
-  importc: "IupZboxv", cdecl, dynlib: dllname.}
-proc hbox*(child: PIhandle): PIhandle {.
-  importc: "IupHbox", cdecl, dynlib: dllname, varargs.}
-proc hboxv*(children: ptr PIhandle): PIhandle {.
-  importc: "IupHboxv", cdecl, dynlib: dllname.}
-
-proc normalizer*(ihFirst: PIhandle): PIhandle {.
-  importc: "IupNormalizer", cdecl, dynlib: dllname, varargs.}
-proc normalizerv*(ihList: ptr PIhandle): PIhandle {.
-  importc: "IupNormalizerv", cdecl, dynlib: dllname.}
-
-proc cbox*(child: PIhandle): PIhandle {.
-  importc: "IupCbox", cdecl, dynlib: dllname, varargs.}
-proc cboxv*(children: ptr PIhandle): PIhandle {.
-  importc: "IupCboxv", cdecl, dynlib: dllname.}
-proc sbox*(child: PIhandle): PIhandle {.
-  importc: "IupSbox", cdecl, dynlib: dllname.}
-
-proc frame*(child: PIhandle): PIhandle {.
-  importc: "IupFrame", cdecl, dynlib: dllname.}
-
-proc image*(width, height: cint, pixmap: pointer): PIhandle {.
-  importc: "IupImage", cdecl, dynlib: dllname.}
-proc imageRGB*(width, height: cint, pixmap: pointer): PIhandle {.
-  importc: "IupImageRGB", cdecl, dynlib: dllname.}
-proc imageRGBA*(width, height: cint, pixmap: pointer): PIhandle {.
-  importc: "IupImageRGBA", cdecl, dynlib: dllname.}
-
-proc item*(title, action: cstring): PIhandle {.
-  importc: "IupItem", cdecl, dynlib: dllname.}
-proc submenu*(title: cstring, child: PIhandle): PIhandle {.
-  importc: "IupSubmenu", cdecl, dynlib: dllname.}
-proc separator*(): PIhandle {.
-  importc: "IupSeparator", cdecl, dynlib: dllname.}
-proc menu*(child: PIhandle): PIhandle {.
-  importc: "IupMenu", cdecl, dynlib: dllname, varargs.}
-proc menuv*(children: ptr PIhandle): PIhandle {.
-  importc: "IupMenuv", cdecl, dynlib: dllname.}
-
-proc button*(title, action: cstring): PIhandle {.
-  importc: "IupButton", cdecl, dynlib: dllname.}
-proc link*(url, title: cstring): PIhandle {.
-  importc: "IupLink", cdecl, dynlib: dllname.}
-proc canvas*(action: cstring): PIhandle {.
-  importc: "IupCanvas", cdecl, dynlib: dllname.}
-proc dialog*(child: PIhandle): PIhandle {.
-  importc: "IupDialog", cdecl, dynlib: dllname.}
-proc user*(): PIhandle {.
-  importc: "IupUser", cdecl, dynlib: dllname.}
-proc label*(title: cstring): PIhandle {.
-  importc: "IupLabel", cdecl, dynlib: dllname.}
-proc list*(action: cstring): PIhandle {.
-  importc: "IupList", cdecl, dynlib: dllname.}
-proc text*(action: cstring): PIhandle {.
-  importc: "IupText", cdecl, dynlib: dllname.}
-proc multiLine*(action: cstring): PIhandle {.
-  importc: "IupMultiLine", cdecl, dynlib: dllname.}
-proc toggle*(title, action: cstring): PIhandle {.
-  importc: "IupToggle", cdecl, dynlib: dllname.}
-proc timer*(): PIhandle {.
-  importc: "IupTimer", cdecl, dynlib: dllname.}
-proc progressBar*(): PIhandle {.
-  importc: "IupProgressBar", cdecl, dynlib: dllname.}
-proc val*(theType: cstring): PIhandle {.
-  importc: "IupVal", cdecl, dynlib: dllname.}
-proc tabs*(child: PIhandle): PIhandle {.
-  importc: "IupTabs", cdecl, dynlib: dllname, varargs.}
-proc tabsv*(children: ptr PIhandle): PIhandle {.
-  importc: "IupTabsv", cdecl, dynlib: dllname.}
-proc tree*(): PIhandle {.importc: "IupTree", cdecl, dynlib: dllname.}
-
-proc spin*(): PIhandle {.importc: "IupSpin", cdecl, dynlib: dllname.}
-proc spinbox*(child: PIhandle): PIhandle {.
-  importc: "IupSpinbox", cdecl, dynlib: dllname.}
-
-# IupText utilities
-proc textConvertLinColToPos*(ih: PIhandle, lin, col: cint, pos: var cint) {.
-  importc: "IupTextConvertLinColToPos", cdecl, dynlib: dllname.}
-proc textConvertPosToLinCol*(ih: PIhandle, pos: cint, lin, col: var cint) {.
-  importc: "IupTextConvertPosToLinCol", cdecl, dynlib: dllname.}
-
-proc convertXYToPos*(ih: PIhandle, x, y: cint): cint {.
-  importc: "IupConvertXYToPos", cdecl, dynlib: dllname.}
-
-# IupTree utilities
-proc treeSetUserId*(ih: PIhandle, id: cint, userid: pointer): cint {.
-  importc: "IupTreeSetUserId", cdecl, dynlib: dllname, discardable.}
-proc treeGetUserId*(ih: PIhandle, id: cint): pointer {.
-  importc: "IupTreeGetUserId", cdecl, dynlib: dllname.}
-proc treeGetId*(ih: PIhandle, userid: pointer): cint {.
-  importc: "IupTreeGetId", cdecl, dynlib: dllname.}
-
-proc treeSetAttribute*(ih: PIhandle, name: cstring, id: cint, value: cstring) {.
-  importc: "IupTreeSetAttribute", cdecl, dynlib: dllname.}
-proc treeStoreAttribute*(ih: PIhandle, name: cstring, id: cint, value: cstring) {.
-  importc: "IupTreeStoreAttribute", cdecl, dynlib: dllname.}
-proc treeGetAttribute*(ih: PIhandle, name: cstring, id: cint): cstring {.
-  importc: "IupTreeGetAttribute", cdecl, dynlib: dllname.}
-proc treeGetInt*(ih: PIhandle, name: cstring, id: cint): cint {.
-  importc: "IupTreeGetInt", cdecl, dynlib: dllname.}
-proc treeGetFloat*(ih: PIhandle, name: cstring, id: cint): cfloat {.
-  importc: "IupTreeGetFloat", cdecl, dynlib: dllname.}
-proc treeSetfAttribute*(ih: PIhandle, name: cstring, id: cint, format: cstring) {.
-  importc: "IupTreeSetfAttribute", cdecl, dynlib: dllname, varargs.}
-
-
-#                   Common Return Values
-const
-  IUP_ERROR* = cint(1)
-  IUP_NOERROR* = cint(0)
-  IUP_OPENED* = cint(-1)
-  IUP_INVALID* = cint(-1)
-
-  # Callback Return Values
-  IUP_IGNORE* = cint(-1)
-  IUP_DEFAULT* = cint(-2)
-  IUP_CLOSE* = cint(-3)
-  IUP_CONTINUE* = cint(-4)
-
-  # IupPopup and IupShowXY Parameter Values
-  IUP_CENTER* = cint(0xFFFF)
-  IUP_LEFT* = cint(0xFFFE)
-  IUP_RIGHT* = cint(0xFFFD)
-  IUP_MOUSEPOS* = cint(0xFFFC)
-  IUP_CURRENT* = cint(0xFFFB)
-  IUP_CENTERPARENT* = cint(0xFFFA)
-  IUP_TOP* = IUP_LEFT
-  IUP_BOTTOM* = IUP_RIGHT
-
-  # SHOW_CB Callback Values
-  IUP_SHOW* = cint(0)
-  IUP_RESTORE* = cint(1)
-  IUP_MINIMIZE* = cint(2)
-  IUP_MAXIMIZE* = cint(3)
-  IUP_HIDE* = cint(4)
-
-  # SCROLL_CB Callback Values
-  IUP_SBUP* = cint(0)
-  IUP_SBDN* = cint(1)
-  IUP_SBPGUP* = cint(2)
-  IUP_SBPGDN* = cint(3)
-  IUP_SBPOSV* = cint(4)
-  IUP_SBDRAGV* = cint(5)
-  IUP_SBLEFT* = cint(6)
-  IUP_SBRIGHT* = cint(7)
-  IUP_SBPGLEFT* = cint(8)
-  IUP_SBPGRIGHT* = cint(9)
-  IUP_SBPOSH* = cint(10)
-  IUP_SBDRAGH* = cint(11)
-
-  # Mouse Button Values and Macros
-  IUP_BUTTON1* = cint(ord('1'))
-  IUP_BUTTON2* = cint(ord('2'))
-  IUP_BUTTON3* = cint(ord('3'))
-  IUP_BUTTON4* = cint(ord('4'))
-  IUP_BUTTON5* = cint(ord('5'))
-
-proc isShift*(s: cstring): bool = return s[0] == 'S'
-proc isControl*(s: cstring): bool = return s[1] == 'C'
-proc isButton1*(s: cstring): bool = return s[2] == '1'
-proc isButton2*(s: cstring): bool = return s[3] == '2'
-proc isbutton3*(s: cstring): bool = return s[4] == '3'
-proc isDouble*(s: cstring): bool = return s[5] == 'D'
-proc isAlt*(s: cstring): bool = return s[6] == 'A'
-proc isSys*(s: cstring): bool = return s[7] == 'Y'
-proc isButton4*(s: cstring): bool = return s[8] == '4'
-proc isButton5*(s: cstring): bool = return s[9] == '5'
-
-# Pre-Defined Masks
-const
-  IUP_MASK_FLOAT* = "[+/-]?(/d+/.?/d*|/./d+)"
-  IUP_MASK_UFLOAT* = "(/d+/.?/d*|/./d+)"
-  IUP_MASK_EFLOAT* = "[+/-]?(/d+/.?/d*|/./d+)([eE][+/-]?/d+)?"
-  IUP_MASK_INT* = "[+/-]?/d+"
-  IUP_MASK_UINT* = "/d+"
-
-# from 32 to 126, all character sets are equal,
-# the key code i the same as the character code.
-const
-  K_SP* = cint(ord(' '))
-  K_exclam* = cint(ord('!'))
-  K_quotedbl* = cint(ord('\"'))
-  K_numbersign* = cint(ord('#'))
-  K_dollar* = cint(ord('$'))
-  K_percent* = cint(ord('%'))
-  K_ampersand* = cint(ord('&'))
-  K_apostrophe* = cint(ord('\''))
-  K_parentleft* = cint(ord('('))
-  K_parentright* = cint(ord(')'))
-  K_asterisk* = cint(ord('*'))
-  K_plus* = cint(ord('+'))
-  K_comma* = cint(ord(','))
-  K_minus* = cint(ord('-'))
-  K_period* = cint(ord('.'))
-  K_slash* = cint(ord('/'))
-  K_0* = cint(ord('0'))
-  K_1* = cint(ord('1'))
-  K_2* = cint(ord('2'))
-  K_3* = cint(ord('3'))
-  K_4* = cint(ord('4'))
-  K_5* = cint(ord('5'))
-  K_6* = cint(ord('6'))
-  K_7* = cint(ord('7'))
-  K_8* = cint(ord('8'))
-  K_9* = cint(ord('9'))
-  K_colon* = cint(ord(':'))
-  K_semicolon* = cint(ord(';'))
-  K_less* = cint(ord('<'))
-  K_equal* = cint(ord('='))
-  K_greater* = cint(ord('>'))
-  K_question* = cint(ord('?'))
-  K_at* = cint(ord('@'))
-  K_upperA* = cint(ord('A'))
-  K_upperB* = cint(ord('B'))
-  K_upperC* = cint(ord('C'))
-  K_upperD* = cint(ord('D'))
-  K_upperE* = cint(ord('E'))
-  K_upperF* = cint(ord('F'))
-  K_upperG* = cint(ord('G'))
-  K_upperH* = cint(ord('H'))
-  K_upperI* = cint(ord('I'))
-  K_upperJ* = cint(ord('J'))
-  K_upperK* = cint(ord('K'))
-  K_upperL* = cint(ord('L'))
-  K_upperM* = cint(ord('M'))
-  K_upperN* = cint(ord('N'))
-  K_upperO* = cint(ord('O'))
-  K_upperP* = cint(ord('P'))
-  K_upperQ* = cint(ord('Q'))
-  K_upperR* = cint(ord('R'))
-  K_upperS* = cint(ord('S'))
-  K_upperT* = cint(ord('T'))
-  K_upperU* = cint(ord('U'))
-  K_upperV* = cint(ord('V'))
-  K_upperW* = cint(ord('W'))
-  K_upperX* = cint(ord('X'))
-  K_upperY* = cint(ord('Y'))
-  K_upperZ* = cint(ord('Z'))
-  K_bracketleft* = cint(ord('['))
-  K_backslash* = cint(ord('\\'))
-  K_bracketright* = cint(ord(']'))
-  K_circum* = cint(ord('^'))
-  K_underscore* = cint(ord('_'))
-  K_grave* = cint(ord('`'))
-  K_lowera* = cint(ord('a'))
-  K_lowerb* = cint(ord('b'))
-  K_lowerc* = cint(ord('c'))
-  K_lowerd* = cint(ord('d'))
-  K_lowere* = cint(ord('e'))
-  K_lowerf* = cint(ord('f'))
-  K_lowerg* = cint(ord('g'))
-  K_lowerh* = cint(ord('h'))
-  K_loweri* = cint(ord('i'))
-  K_lowerj* = cint(ord('j'))
-  K_lowerk* = cint(ord('k'))
-  K_lowerl* = cint(ord('l'))
-  K_lowerm* = cint(ord('m'))
-  K_lowern* = cint(ord('n'))
-  K_lowero* = cint(ord('o'))
-  K_lowerp* = cint(ord('p'))
-  K_lowerq* = cint(ord('q'))
-  K_lowerr* = cint(ord('r'))
-  K_lowers* = cint(ord('s'))
-  K_lowert* = cint(ord('t'))
-  K_loweru* = cint(ord('u'))
-  K_lowerv* = cint(ord('v'))
-  K_lowerw* = cint(ord('w'))
-  K_lowerx* = cint(ord('x'))
-  K_lowery* = cint(ord('y'))
-  K_lowerz* = cint(ord('z'))
-  K_braceleft* = cint(ord('{'))
-  K_bar* = cint(ord('|'))
-  K_braceright* = cint(ord('}'))
-  K_tilde* = cint(ord('~'))
-
-proc isPrint*(c: cint): bool = return c > 31 and c < 127
-
-# also define the escape sequences that have keys associated
-const
-  K_BS* = cint(ord('\b'))
-  K_TAB* = cint(ord('\t'))
-  K_LF* = cint(10)
-  K_CR* = cint(13)
-
-# IUP Extended Key Codes, range start at 128
-# Modifiers use 256 interval
-# These key code definitions are specific to IUP
-
-proc isXkey*(c: cint): bool = return c > 128
-proc isShiftXkey*(c: cint): bool = return c > 256 and c < 512
-proc isCtrlXkey*(c: cint): bool = return c > 512 and c < 768
-proc isAltXkey*(c: cint): bool = return c > 768 and c < 1024
-proc isSysXkey*(c: cint): bool = return c > 1024 and c < 1280
-
-proc iUPxCODE*(c: cint): cint = return c + cint(128) # Normal (must be above 128)
-proc iUPsxCODE*(c: cint): cint =
-  return c + cint(256)
-  # Shift (must have range to include the standard keys and the normal
-  # extended keys, so must be above 256
-
-proc iUPcxCODE*(c: cint): cint = return c + cint(512) # Ctrl
-proc iUPmxCODE*(c: cint): cint = return c + cint(768) # Alt
-proc iUPyxCODE*(c: cint): cint = return c + cint(1024) # Sys (Win or Apple)
-
-const
-  IUP_NUMMAXCODES* = 1280 ## 5*256=1280  Normal+Shift+Ctrl+Alt+Sys
-
-  K_HOME* = iUPxCODE(1)
-  K_UP* = iUPxCODE(2)
-  K_PGUP* = iUPxCODE(3)
-  K_LEFT* = iUPxCODE(4)
-  K_MIDDLE* = iUPxCODE(5)
-  K_RIGHT* = iUPxCODE(6)
-  K_END* = iUPxCODE(7)
-  K_DOWN* = iUPxCODE(8)
-  K_PGDN* = iUPxCODE(9)
-  K_INS* = iUPxCODE(10)
-  K_DEL* = iUPxCODE(11)
-  K_PAUSE* = iUPxCODE(12)
-  K_ESC* = iUPxCODE(13)
-  K_ccedilla* = iUPxCODE(14)
-  K_F1* = iUPxCODE(15)
-  K_F2* = iUPxCODE(16)
-  K_F3* = iUPxCODE(17)
-  K_F4* = iUPxCODE(18)
-  K_F5* = iUPxCODE(19)
-  K_F6* = iUPxCODE(20)
-  K_F7* = iUPxCODE(21)
-  K_F8* = iUPxCODE(22)
-  K_F9* = iUPxCODE(23)
-  K_F10* = iUPxCODE(24)
-  K_F11* = iUPxCODE(25)
-  K_F12* = iUPxCODE(26)
-  K_Print* = iUPxCODE(27)
-  K_Menu* = iUPxCODE(28)
-
-  K_acute* = iUPxCODE(29) # no Shift/Ctrl/Alt
-
-  K_sHOME* = iUPsxCODE(K_HOME)
-  K_sUP* = iUPsxCODE(K_UP)
-  K_sPGUP* = iUPsxCODE(K_PGUP)
-  K_sLEFT* = iUPsxCODE(K_LEFT)
-  K_sMIDDLE* = iUPsxCODE(K_MIDDLE)
-  K_sRIGHT* = iUPsxCODE(K_RIGHT)
-  K_sEND* = iUPsxCODE(K_END)
-  K_sDOWN* = iUPsxCODE(K_DOWN)
-  K_sPGDN* = iUPsxCODE(K_PGDN)
-  K_sINS* = iUPsxCODE(K_INS)
-  K_sDEL* = iUPsxCODE(K_DEL)
-  K_sSP* = iUPsxCODE(K_SP)
-  K_sTAB* = iUPsxCODE(K_TAB)
-  K_sCR* = iUPsxCODE(K_CR)
-  K_sBS* = iUPsxCODE(K_BS)
-  K_sPAUSE* = iUPsxCODE(K_PAUSE)
-  K_sESC* = iUPsxCODE(K_ESC)
-  K_sCcedilla* = iUPsxCODE(K_ccedilla)
-  K_sF1* = iUPsxCODE(K_F1)
-  K_sF2* = iUPsxCODE(K_F2)
-  K_sF3* = iUPsxCODE(K_F3)
-  K_sF4* = iUPsxCODE(K_F4)
-  K_sF5* = iUPsxCODE(K_F5)
-  K_sF6* = iUPsxCODE(K_F6)
-  K_sF7* = iUPsxCODE(K_F7)
-  K_sF8* = iUPsxCODE(K_F8)
-  K_sF9* = iUPsxCODE(K_F9)
-  K_sF10* = iUPsxCODE(K_F10)
-  K_sF11* = iUPsxCODE(K_F11)
-  K_sF12* = iUPsxCODE(K_F12)
-  K_sPrint* = iUPsxCODE(K_Print)
-  K_sMenu* = iUPsxCODE(K_Menu)
-
-  K_cHOME* = iUPcxCODE(K_HOME)
-  K_cUP* = iUPcxCODE(K_UP)
-  K_cPGUP* = iUPcxCODE(K_PGUP)
-  K_cLEFT* = iUPcxCODE(K_LEFT)
-  K_cMIDDLE* = iUPcxCODE(K_MIDDLE)
-  K_cRIGHT* = iUPcxCODE(K_RIGHT)
-  K_cEND* = iUPcxCODE(K_END)
-  K_cDOWN* = iUPcxCODE(K_DOWN)
-  K_cPGDN* = iUPcxCODE(K_PGDN)
-  K_cINS* = iUPcxCODE(K_INS)
-  K_cDEL* = iUPcxCODE(K_DEL)
-  K_cSP* = iUPcxCODE(K_SP)
-  K_cTAB* = iUPcxCODE(K_TAB)
-  K_cCR* = iUPcxCODE(K_CR)
-  K_cBS* = iUPcxCODE(K_BS)
-  K_cPAUSE* = iUPcxCODE(K_PAUSE)
-  K_cESC* = iUPcxCODE(K_ESC)
-  K_cCcedilla* = iUPcxCODE(K_ccedilla)
-  K_cF1* = iUPcxCODE(K_F1)
-  K_cF2* = iUPcxCODE(K_F2)
-  K_cF3* = iUPcxCODE(K_F3)
-  K_cF4* = iUPcxCODE(K_F4)
-  K_cF5* = iUPcxCODE(K_F5)
-  K_cF6* = iUPcxCODE(K_F6)
-  K_cF7* = iUPcxCODE(K_F7)
-  K_cF8* = iUPcxCODE(K_F8)
-  K_cF9* = iUPcxCODE(K_F9)
-  K_cF10* = iUPcxCODE(K_F10)
-  K_cF11* = iUPcxCODE(K_F11)
-  K_cF12* = iUPcxCODE(K_F12)
-  K_cPrint* = iUPcxCODE(K_Print)
-  K_cMenu* = iUPcxCODE(K_Menu)
-
-  K_mHOME* = iUPmxCODE(K_HOME)
-  K_mUP* = iUPmxCODE(K_UP)
-  K_mPGUP* = iUPmxCODE(K_PGUP)
-  K_mLEFT* = iUPmxCODE(K_LEFT)
-  K_mMIDDLE* = iUPmxCODE(K_MIDDLE)
-  K_mRIGHT* = iUPmxCODE(K_RIGHT)
-  K_mEND* = iUPmxCODE(K_END)
-  K_mDOWN* = iUPmxCODE(K_DOWN)
-  K_mPGDN* = iUPmxCODE(K_PGDN)
-  K_mINS* = iUPmxCODE(K_INS)
-  K_mDEL* = iUPmxCODE(K_DEL)
-  K_mSP* = iUPmxCODE(K_SP)
-  K_mTAB* = iUPmxCODE(K_TAB)
-  K_mCR* = iUPmxCODE(K_CR)
-  K_mBS* = iUPmxCODE(K_BS)
-  K_mPAUSE* = iUPmxCODE(K_PAUSE)
-  K_mESC* = iUPmxCODE(K_ESC)
-  K_mCcedilla* = iUPmxCODE(K_ccedilla)
-  K_mF1* = iUPmxCODE(K_F1)
-  K_mF2* = iUPmxCODE(K_F2)
-  K_mF3* = iUPmxCODE(K_F3)
-  K_mF4* = iUPmxCODE(K_F4)
-  K_mF5* = iUPmxCODE(K_F5)
-  K_mF6* = iUPmxCODE(K_F6)
-  K_mF7* = iUPmxCODE(K_F7)
-  K_mF8* = iUPmxCODE(K_F8)
-  K_mF9* = iUPmxCODE(K_F9)
-  K_mF10* = iUPmxCODE(K_F10)
-  K_mF11* = iUPmxCODE(K_F11)
-  K_mF12* = iUPmxCODE(K_F12)
-  K_mPrint* = iUPmxCODE(K_Print)
-  K_mMenu* = iUPmxCODE(K_Menu)
-
-  K_yHOME* = iUPyxCODE(K_HOME)
-  K_yUP* = iUPyxCODE(K_UP)
-  K_yPGUP* = iUPyxCODE(K_PGUP)
-  K_yLEFT* = iUPyxCODE(K_LEFT)
-  K_yMIDDLE* = iUPyxCODE(K_MIDDLE)
-  K_yRIGHT* = iUPyxCODE(K_RIGHT)
-  K_yEND* = iUPyxCODE(K_END)
-  K_yDOWN* = iUPyxCODE(K_DOWN)
-  K_yPGDN* = iUPyxCODE(K_PGDN)
-  K_yINS* = iUPyxCODE(K_INS)
-  K_yDEL* = iUPyxCODE(K_DEL)
-  K_ySP* = iUPyxCODE(K_SP)
-  K_yTAB* = iUPyxCODE(K_TAB)
-  K_yCR* = iUPyxCODE(K_CR)
-  K_yBS* = iUPyxCODE(K_BS)
-  K_yPAUSE* = iUPyxCODE(K_PAUSE)
-  K_yESC* = iUPyxCODE(K_ESC)
-  K_yCcedilla* = iUPyxCODE(K_ccedilla)
-  K_yF1* = iUPyxCODE(K_F1)
-  K_yF2* = iUPyxCODE(K_F2)
-  K_yF3* = iUPyxCODE(K_F3)
-  K_yF4* = iUPyxCODE(K_F4)
-  K_yF5* = iUPyxCODE(K_F5)
-  K_yF6* = iUPyxCODE(K_F6)
-  K_yF7* = iUPyxCODE(K_F7)
-  K_yF8* = iUPyxCODE(K_F8)
-  K_yF9* = iUPyxCODE(K_F9)
-  K_yF10* = iUPyxCODE(K_F10)
-  K_yF11* = iUPyxCODE(K_F11)
-  K_yF12* = iUPyxCODE(K_F12)
-  K_yPrint* = iUPyxCODE(K_Print)
-  K_yMenu* = iUPyxCODE(K_Menu)
-
-  K_sPlus* = iUPsxCODE(K_plus)
-  K_sComma* = iUPsxCODE(K_comma)
-  K_sMinus* = iUPsxCODE(K_minus)
-  K_sPeriod* = iUPsxCODE(K_period)
-  K_sSlash* = iUPsxCODE(K_slash)
-  K_sAsterisk* = iUPsxCODE(K_asterisk)
-
-  K_cupperA* = iUPcxCODE(K_upperA)
-  K_cupperB* = iUPcxCODE(K_upperB)
-  K_cupperC* = iUPcxCODE(K_upperC)
-  K_cupperD* = iUPcxCODE(K_upperD)
-  K_cupperE* = iUPcxCODE(K_upperE)
-  K_cupperF* = iUPcxCODE(K_upperF)
-  K_cupperG* = iUPcxCODE(K_upperG)
-  K_cupperH* = iUPcxCODE(K_upperH)
-  K_cupperI* = iUPcxCODE(K_upperI)
-  K_cupperJ* = iUPcxCODE(K_upperJ)
-  K_cupperK* = iUPcxCODE(K_upperK)
-  K_cupperL* = iUPcxCODE(K_upperL)
-  K_cupperM* = iUPcxCODE(K_upperM)
-  K_cupperN* = iUPcxCODE(K_upperN)
-  K_cupperO* = iUPcxCODE(K_upperO)
-  K_cupperP* = iUPcxCODE(K_upperP)
-  K_cupperQ* = iUPcxCODE(K_upperQ)
-  K_cupperR* = iUPcxCODE(K_upperR)
-  K_cupperS* = iUPcxCODE(K_upperS)
-  K_cupperT* = iUPcxCODE(K_upperT)
-  K_cupperU* = iUPcxCODE(K_upperU)
-  K_cupperV* = iUPcxCODE(K_upperV)
-  K_cupperW* = iUPcxCODE(K_upperW)
-  K_cupperX* = iUPcxCODE(K_upperX)
-  K_cupperY* = iUPcxCODE(K_upperY)
-  K_cupperZ* = iUPcxCODE(K_upperZ)
-  K_c1* = iUPcxCODE(K_1)
-  K_c2* = iUPcxCODE(K_2)
-  K_c3* = iUPcxCODE(K_3)
-  K_c4* = iUPcxCODE(K_4)
-  K_c5* = iUPcxCODE(K_5)
-  K_c6* = iUPcxCODE(K_6)
-  K_c7* = iUPcxCODE(K_7)
-  K_c8* = iUPcxCODE(K_8)
-  K_c9* = iUPcxCODE(K_9)
-  K_c0* = iUPcxCODE(K_0)
-  K_cPlus* = iUPcxCODE(K_plus)
-  K_cComma* = iUPcxCODE(K_comma)
-  K_cMinus* = iUPcxCODE(K_minus)
-  K_cPeriod* = iUPcxCODE(K_period)
-  K_cSlash* = iUPcxCODE(K_slash)
-  K_cSemicolon* = iUPcxCODE(K_semicolon)
-  K_cEqual* = iUPcxCODE(K_equal)
-  K_cBracketleft* = iUPcxCODE(K_bracketleft)
-  K_cBracketright* = iUPcxCODE(K_bracketright)
-  K_cBackslash* = iUPcxCODE(K_backslash)
-  K_cAsterisk* = iUPcxCODE(K_asterisk)
-
-  K_mupperA* = iUPmxCODE(K_upperA)
-  K_mupperB* = iUPmxCODE(K_upperB)
-  K_mupperC* = iUPmxCODE(K_upperC)
-  K_mupperD* = iUPmxCODE(K_upperD)
-  K_mupperE* = iUPmxCODE(K_upperE)
-  K_mupperF* = iUPmxCODE(K_upperF)
-  K_mupperG* = iUPmxCODE(K_upperG)
-  K_mupperH* = iUPmxCODE(K_upperH)
-  K_mupperI* = iUPmxCODE(K_upperI)
-  K_mupperJ* = iUPmxCODE(K_upperJ)
-  K_mupperK* = iUPmxCODE(K_upperK)
-  K_mupperL* = iUPmxCODE(K_upperL)
-  K_mupperM* = iUPmxCODE(K_upperM)
-  K_mupperN* = iUPmxCODE(K_upperN)
-  K_mupperO* = iUPmxCODE(K_upperO)
-  K_mupperP* = iUPmxCODE(K_upperP)
-  K_mupperQ* = iUPmxCODE(K_upperQ)
-  K_mupperR* = iUPmxCODE(K_upperR)
-  K_mupperS* = iUPmxCODE(K_upperS)
-  K_mupperT* = iUPmxCODE(K_upperT)
-  K_mupperU* = iUPmxCODE(K_upperU)
-  K_mupperV* = iUPmxCODE(K_upperV)
-  K_mupperW* = iUPmxCODE(K_upperW)
-  K_mupperX* = iUPmxCODE(K_upperX)
-  K_mupperY* = iUPmxCODE(K_upperY)
-  K_mupperZ* = iUPmxCODE(K_upperZ)
-  K_m1* = iUPmxCODE(K_1)
-  K_m2* = iUPmxCODE(K_2)
-  K_m3* = iUPmxCODE(K_3)
-  K_m4* = iUPmxCODE(K_4)
-  K_m5* = iUPmxCODE(K_5)
-  K_m6* = iUPmxCODE(K_6)
-  K_m7* = iUPmxCODE(K_7)
-  K_m8* = iUPmxCODE(K_8)
-  K_m9* = iUPmxCODE(K_9)
-  K_m0* = iUPmxCODE(K_0)
-  K_mPlus* = iUPmxCODE(K_plus)
-  K_mComma* = iUPmxCODE(K_comma)
-  K_mMinus* = iUPmxCODE(K_minus)
-  K_mPeriod* = iUPmxCODE(K_period)
-  K_mSlash* = iUPmxCODE(K_slash)
-  K_mSemicolon* = iUPmxCODE(K_semicolon)
-  K_mEqual* = iUPmxCODE(K_equal)
-  K_mBracketleft* = iUPmxCODE(K_bracketleft)
-  K_mBracketright* = iUPmxCODE(K_bracketright)
-  K_mBackslash* = iUPmxCODE(K_backslash)
-  K_mAsterisk* = iUPmxCODE(K_asterisk)
-
-  K_yA* = iUPyxCODE(K_upperA)
-  K_yB* = iUPyxCODE(K_upperB)
-  K_yC* = iUPyxCODE(K_upperC)
-  K_yD* = iUPyxCODE(K_upperD)
-  K_yE* = iUPyxCODE(K_upperE)
-  K_yF* = iUPyxCODE(K_upperF)
-  K_yG* = iUPyxCODE(K_upperG)
-  K_yH* = iUPyxCODE(K_upperH)
-  K_yI* = iUPyxCODE(K_upperI)
-  K_yJ* = iUPyxCODE(K_upperJ)
-  K_yK* = iUPyxCODE(K_upperK)
-  K_yL* = iUPyxCODE(K_upperL)
-  K_yM* = iUPyxCODE(K_upperM)
-  K_yN* = iUPyxCODE(K_upperN)
-  K_yO* = iUPyxCODE(K_upperO)
-  K_yP* = iUPyxCODE(K_upperP)
-  K_yQ* = iUPyxCODE(K_upperQ)
-  K_yR* = iUPyxCODE(K_upperR)
-  K_yS* = iUPyxCODE(K_upperS)
-  K_yT* = iUPyxCODE(K_upperT)
-  K_yU* = iUPyxCODE(K_upperU)
-  K_yV* = iUPyxCODE(K_upperV)
-  K_yW* = iUPyxCODE(K_upperW)
-  K_yX* = iUPyxCODE(K_upperX)
-  K_yY* = iUPyxCODE(K_upperY)
-  K_yZ* = iUPyxCODE(K_upperZ)
-  K_y1* = iUPyxCODE(K_1)
-  K_y2* = iUPyxCODE(K_2)
-  K_y3* = iUPyxCODE(K_3)
-  K_y4* = iUPyxCODE(K_4)
-  K_y5* = iUPyxCODE(K_5)
-  K_y6* = iUPyxCODE(K_6)
-  K_y7* = iUPyxCODE(K_7)
-  K_y8* = iUPyxCODE(K_8)
-  K_y9* = iUPyxCODE(K_9)
-  K_y0* = iUPyxCODE(K_0)
-  K_yPlus* = iUPyxCODE(K_plus)
-  K_yComma* = iUPyxCODE(K_comma)
-  K_yMinus* = iUPyxCODE(K_minus)
-  K_yPeriod* = iUPyxCODE(K_period)
-  K_ySlash* = iUPyxCODE(K_slash)
-  K_ySemicolon* = iUPyxCODE(K_semicolon)
-  K_yEqual* = iUPyxCODE(K_equal)
-  K_yBracketleft* = iUPyxCODE(K_bracketleft)
-  K_yBracketright* = iUPyxCODE(K_bracketright)
-  K_yBackslash* = iUPyxCODE(K_backslash)
-  K_yAsterisk* = iUPyxCODE(K_asterisk)
-
-proc controlsOpen*(): cint {.cdecl, importc: "IupControlsOpen", dynlib: dllname.}
-proc controlsClose*() {.cdecl, importc: "IupControlsClose", dynlib: dllname.}
-
-proc oldValOpen*() {.cdecl, importc: "IupOldValOpen", dynlib: dllname.}
-proc oldTabsOpen*() {.cdecl, importc: "IupOldTabsOpen", dynlib: dllname.}
-
-proc colorbar*(): PIhandle {.cdecl, importc: "IupColorbar", dynlib: dllname.}
-proc cells*(): PIhandle {.cdecl, importc: "IupCells", dynlib: dllname.}
-proc colorBrowser*(): PIhandle {.cdecl, importc: "IupColorBrowser", dynlib: dllname.}
-proc gauge*(): PIhandle {.cdecl, importc: "IupGauge", dynlib: dllname.}
-proc dial*(theType: cstring): PIhandle {.cdecl, importc: "IupDial", dynlib: dllname.}
-proc matrix*(action: cstring): PIhandle {.cdecl, importc: "IupMatrix", dynlib: dllname.}
-
-# IupMatrix utilities
-proc matSetAttribute*(ih: PIhandle, name: cstring, lin, col: cint,
-                      value: cstring) {.
-                      cdecl, importc: "IupMatSetAttribute", dynlib: dllname.}
-proc matStoreAttribute*(ih: PIhandle, name: cstring, lin, col: cint,
-                        value: cstring) {.cdecl,
-                        importc: "IupMatStoreAttribute", dynlib: dllname.}
-proc matGetAttribute*(ih: PIhandle, name: cstring, lin, col: cint): cstring {.
-  cdecl, importc: "IupMatGetAttribute", dynlib: dllname.}
-proc matGetInt*(ih: PIhandle, name: cstring, lin, col: cint): cint {.
-  cdecl, importc: "IupMatGetInt", dynlib: dllname.}
-proc matGetFloat*(ih: PIhandle, name: cstring, lin, col: cint): cfloat {.
-  cdecl, importc: "IupMatGetFloat", dynlib: dllname.}
-proc matSetfAttribute*(ih: PIhandle, name: cstring, lin, col: cint,
-                       format: cstring) {.cdecl,
-                       importc: "IupMatSetfAttribute",
-                       dynlib: dllname, varargs.}
-
-# Used by IupColorbar
-const
-  IUP_PRIMARY* = -1
-  IUP_SECONDARY* = -2
-
-# Initialize PPlot widget class
-proc pPlotOpen*() {.cdecl, importc: "IupPPlotOpen", dynlib: dllname.}
-
-# Create an PPlot widget instance
-proc pPlot*: PIhandle {.cdecl, importc: "IupPPlot", dynlib: dllname.}
-
-# Add dataset to plot
-proc pPlotBegin*(ih: PIhandle, strXdata: cint) {.
-  cdecl, importc: "IupPPlotBegin", dynlib: dllname.}
-proc pPlotAdd*(ih: PIhandle, x, y: cfloat) {.
-  cdecl, importc: "IupPPlotAdd", dynlib: dllname.}
-proc pPlotAddStr*(ih: PIhandle, x: cstring, y: cfloat) {.
-  cdecl, importc: "IupPPlotAddStr", dynlib: dllname.}
-proc pPlotEnd*(ih: PIhandle): cint {.
-  cdecl, importc: "IupPPlotEnd", dynlib: dllname.}
-
-proc pPlotInsertStr*(ih: PIhandle, index, sampleIndex: cint, x: cstring,
-                     y: cfloat) {.cdecl, importc: "IupPPlotInsertStr",
-                     dynlib: dllname.}
-proc pPlotInsert*(ih: PIhandle, index, sampleIndex: cint,
-                  x, y: cfloat) {.
-                  cdecl, importc: "IupPPlotInsert", dynlib: dllname.}
-
-# convert from plot coordinates to pixels
-proc pPlotTransform*(ih: PIhandle, x, y: cfloat, ix, iy: var cint) {.
-  cdecl, importc: "IupPPlotTransform", dynlib: dllname.}
-
-# Plot on the given device. Uses a "cdCanvas*".
-proc pPlotPaintTo*(ih: PIhandle, cnv: pointer) {.
-  cdecl, importc: "IupPPlotPaintTo", dynlib: dllname.}
-
-
diff --git a/lib/wrappers/linenoise/linenoise.c b/lib/wrappers/linenoise/linenoise.c
index fce14a7c5..be792b96b 100644
--- a/lib/wrappers/linenoise/linenoise.c
+++ b/lib/wrappers/linenoise/linenoise.c
@@ -600,7 +600,7 @@ static void refreshMultiLine(struct linenoiseState *l) {
     rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */
     lndebug("rpos2 %d", rpos2);
 
-    /* Go up till we reach the expected positon. */
+    /* Go up till we reach the expected position. */
     if (rows-rpos2 > 0) {
         lndebug("go-up %d", rows-rpos2);
         snprintf(seq,64,"\x1b[%dA", rows-rpos2);
@@ -765,7 +765,7 @@ void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
  * when ctrl+d is typed.
  *
  * The function returns the length of the current buffer. */
-static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt)
+static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt, linenoiseData* data)
 {
     struct linenoiseState l;
 
@@ -827,6 +827,7 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
             return (int)l.len;
         case CTRL_C:     /* ctrl-c */
             errno = EAGAIN;
+            data->status = linenoiseStatus_ctrl_C;
             return -1;
         case BACKSPACE:   /* backspace */
         case 8:     /* ctrl-h */
@@ -839,6 +840,7 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
             } else {
                 history_len--;
                 free(history[history_len]);
+                data->status = linenoiseStatus_ctrl_D;
                 return -1;
             }
             break;
@@ -979,7 +981,7 @@ void linenoisePrintKeyCodes(void) {
 
 /* This function calls the line editing function linenoiseEdit() using
  * the STDIN file descriptor set in raw mode. */
-static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
+static int linenoiseRaw(char *buf, size_t buflen, const char *prompt, linenoiseData* data) {
     int count;
 
     if (buflen == 0) {
@@ -988,7 +990,7 @@ static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
     }
 
     if (enableRawMode(STDIN_FILENO) == -1) return -1;
-    count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt);
+    count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt, data);
     disableRawMode(STDIN_FILENO);
     printf("\n");
     return count;
@@ -1035,7 +1037,7 @@ static char *linenoiseNoTTY(void) {
  * for a blacklist of stupid terminals, and later either calls the line
  * editing function or uses dummy fgets() so that you will be able to type
  * something even in the most desperate of the conditions. */
-char *linenoise(const char *prompt) {
+char *linenoiseExtra(const char *prompt, linenoiseData* data) {
     char buf[LINENOISE_MAX_LINE];
     int count;
 
@@ -1056,12 +1058,18 @@ char *linenoise(const char *prompt) {
         }
         return strdup(buf);
     } else {
-        count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
+        count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt, data);
         if (count == -1) return NULL;
         return strdup(buf);
     }
 }
 
+char *linenoise(const char *prompt) {
+  linenoiseData data;
+  data.status = linenoiseStatus_ctrl_unknown;
+  return linenoiseExtra(prompt, &data);
+}
+
 /* This is just a wrapper the user may want to call in order to make sure
  * the linenoise returned buffer is freed with the same allocator it was
  * created with. Useful when the main program is using an alternative
diff --git a/lib/wrappers/linenoise/linenoise.h b/lib/wrappers/linenoise/linenoise.h
index ed20232c5..aa86ccb78 100644
--- a/lib/wrappers/linenoise/linenoise.h
+++ b/lib/wrappers/linenoise/linenoise.h
@@ -48,6 +48,16 @@ typedef struct linenoiseCompletions {
   char **cvec;
 } linenoiseCompletions;
 
+typedef enum linenoiseStatus {
+  linenoiseStatus_ctrl_unknown,
+  linenoiseStatus_ctrl_C,
+  linenoiseStatus_ctrl_D
+} linenoiseStatus;
+
+typedef struct linenoiseData {
+  linenoiseStatus status;
+} linenoiseData;
+
 typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
 typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);
 typedef void(linenoiseFreeHintsCallback)(void *);
@@ -57,6 +67,7 @@ void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
 void linenoiseAddCompletion(linenoiseCompletions *, const char *);
 
 char *linenoise(const char *prompt);
+char *linenoiseExtra(const char *prompt, linenoiseData* data);
 void linenoiseFree(void *ptr);
 int linenoiseHistoryAdd(const char *line);
 int linenoiseHistorySetMaxLen(int len);
diff --git a/lib/wrappers/linenoise/linenoise.nim b/lib/wrappers/linenoise/linenoise.nim
index a6260eb12..186b3b252 100644
--- a/lib/wrappers/linenoise/linenoise.nim
+++ b/lib/wrappers/linenoise/linenoise.nim
@@ -9,14 +9,14 @@
 
 type
   Completions* = object
-    len*: csize
+    len*: csize_t
     cvec*: cstringArray
 
   CompletionCallback* = proc (a2: cstring; a3: ptr Completions) {.cdecl.}
 
 {.compile: "linenoise.c".}
 
-proc setCompletionCallback*(a2: ptr CompletionCallback) {.
+proc setCompletionCallback*(a2: CompletionCallback) {.
     importc: "linenoiseSetCompletionCallback".}
 proc addCompletion*(a2: ptr Completions; a3: cstring) {.
     importc: "linenoiseAddCompletion".}
@@ -32,3 +32,41 @@ proc printKeyCodes*() {.importc: "linenoisePrintKeyCodes".}
 
 proc free*(s: cstring) {.importc: "free", header: "<stdlib.h>".}
 
+when defined(nimExperimentalLinenoiseExtra) and not defined(windows):
+  # C interface
+  type LinenoiseStatus = enum
+    linenoiseStatus_ctrl_unknown
+    linenoiseStatus_ctrl_C
+    linenoiseStatus_ctrl_D
+
+  type LinenoiseData* = object
+    status: LinenoiseStatus
+
+  proc linenoiseExtra(prompt: cstring, data: ptr LinenoiseData): cstring {.importc.}
+
+  # stable nim interface
+  type Status* = enum
+    lnCtrlUnkown
+    lnCtrlC
+    lnCtrlD
+
+  type ReadLineResult* = object
+    line*: string
+    status*: Status
+
+  proc readLineStatus*(prompt: string, result: var ReadLineResult) =
+    ## line editing API that allows returning the line entered and an indicator
+    ## of which control key was entered, allowing user to distinguish between
+    ## for example ctrl-C vs ctrl-D.
+    runnableExamples("-d:nimExperimentalLinenoiseExtra -r:off"):
+      var ret: ReadLineResult
+      while true:
+        readLineStatus("name: ", ret) # ctrl-D will exit, ctrl-C will go to next prompt
+        if ret.line.len > 0: echo ret.line
+        if ret.status == lnCtrlD: break
+      echo "exiting"
+    var data: LinenoiseData
+    let buf = linenoiseExtra(prompt, data.addr)
+    result.line = $buf
+    free(buf)
+    result.status = data.status.ord.Status
diff --git a/lib/wrappers/mysql.nim b/lib/wrappers/mysql.nim
deleted file mode 100644
index 06c663822..000000000
--- a/lib/wrappers/mysql.nim
+++ /dev/null
@@ -1,1112 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2010 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-{.deadCodeElim: on.}  # dce option deprecated
-{.push, callconv: cdecl.}
-
-when defined(Unix):
-  when defined(macosx):
-    const
-      lib = "(libmysqlclient|libmariadbclient)(|.20|.19|.18|.17|.16|.15).dylib"
-  else:
-    const
-      lib = "(libmysqlclient|libmariadbclient).so(|.20|.19|.18|.17|.16|.15)"
-when defined(Windows):
-  const
-    lib = "(libmysql.dll|libmariadb.dll)"
-type
-  my_bool* = bool
-  Pmy_bool* = ptr my_bool
-  PVIO* = pointer
-  Pgptr* = ptr gptr
-  gptr* = cstring
-  Pmy_socket* = ptr my_socket
-  my_socket* = cint
-  PPByte* = pointer
-  cuint* = cint
-
-#  ------------ Start of declaration in "mysql_com.h"   ---------------------
-#
-#  ** Common definition between mysql server & client
-#
-# Field/table name length
-
-const
-  NAME_LEN* = 64
-  HOSTNAME_LENGTH* = 60
-  USERNAME_LENGTH* = 16
-  SERVER_VERSION_LENGTH* = 60
-  SQLSTATE_LENGTH* = 5
-  LOCAL_HOST* = "localhost"
-  LOCAL_HOST_NAMEDPIPE* = '.'
-
-const
-  NAMEDPIPE* = "MySQL"
-  SERVICENAME* = "MySQL"
-
-type
-  Enum_server_command* = enum
-    COM_SLEEP, COM_QUIT, COM_INIT_DB, COM_QUERY, COM_FIELD_LIST, COM_CREATE_DB,
-    COM_DROP_DB, COM_REFRESH, COM_SHUTDOWN, COM_STATISTICS, COM_PROCESS_INFO,
-    COM_CONNECT, COM_PROCESS_KILL, COM_DEBUG, COM_PING, COM_TIME,
-    COM_DELAYED_INSERT, COM_CHANGE_USER, COM_BINLOG_DUMP, COM_TABLE_DUMP,
-    COM_CONNECT_OUT, COM_REGISTER_SLAVE, COM_STMT_PREPARE, COM_STMT_EXECUTE,
-    COM_STMT_SEND_LONG_DATA, COM_STMT_CLOSE, COM_STMT_RESET, COM_SET_OPTION,
-    COM_STMT_FETCH, COM_END
-{.deprecated: [Tenum_server_command: Enum_server_command].}
-
-const
-  SCRAMBLE_LENGTH* = 20 # Length of random string sent by server on handshake;
-                        # this is also length of obfuscated password,
-                        # received from client
-  SCRAMBLE_LENGTH_323* = 8    # length of password stored in the db:
-                              # new passwords are preceded with '*'
-  SCRAMBLED_PASSWORD_CHAR_LENGTH* = SCRAMBLE_LENGTH * 2 + 1
-  SCRAMBLED_PASSWORD_CHAR_LENGTH_323* = SCRAMBLE_LENGTH_323 * 2
-  NOT_NULL_FLAG* = 1          #  Field can't be NULL
-  PRI_KEY_FLAG* = 2           #  Field is part of a primary key
-  UNIQUE_KEY_FLAG* = 4        #  Field is part of a unique key
-  MULTIPLE_KEY_FLAG* = 8      #  Field is part of a key
-  BLOB_FLAG* = 16             #  Field is a blob
-  UNSIGNED_FLAG* = 32         #  Field is unsigned
-  ZEROFILL_FLAG* = 64         #  Field is zerofill
-  BINARY_FLAG* = 128          #  Field is binary
-                              # The following are only sent to new clients
-  ENUM_FLAG* = 256            # field is an enum
-  AUTO_INCREMENT_FLAG* = 512  # field is a autoincrement field
-  TIMESTAMP_FLAG* = 1024      # Field is a timestamp
-  SET_FLAG* = 2048            # field is a set
-  NO_DEFAULT_VALUE_FLAG* = 4096 # Field doesn't have default value
-  NUM_FLAG* = 32768           # Field is num (for clients)
-  PART_KEY_FLAG* = 16384      # Intern; Part of some key
-  GROUP_FLAG* = 32768         # Intern: Group field
-  UNIQUE_FLAG* = 65536        # Intern: Used by sql_yacc
-  BINCMP_FLAG* = 131072       # Intern: Used by sql_yacc
-  REFRESH_GRANT* = 1          # Refresh grant tables
-  REFRESH_LOG* = 2            # Start on new log file
-  REFRESH_TABLES* = 4         # close all tables
-  REFRESH_HOSTS* = 8          # Flush host cache
-  REFRESH_STATUS* = 16        # Flush status variables
-  REFRESH_THREADS* = 32       # Flush thread cache
-  REFRESH_SLAVE* = 64         # Reset master info and restart slave thread
-  REFRESH_MASTER* = 128 # Remove all bin logs in the index and truncate the index
-                        # The following can't be set with mysql_refresh()
-  REFRESH_READ_LOCK* = 16384  # Lock tables for read
-  REFRESH_FAST* = 32768       # Intern flag
-  REFRESH_QUERY_CACHE* = 65536 # RESET (remove all queries) from query cache
-  REFRESH_QUERY_CACHE_FREE* = 0x00020000 # pack query cache
-  REFRESH_DES_KEY_FILE* = 0x00040000
-  REFRESH_USER_RESOURCES* = 0x00080000
-  CLIENT_LONG_PASSWORD* = 1   # new more secure passwords
-  CLIENT_FOUND_ROWS* = 2      # Found instead of affected rows
-  CLIENT_LONG_FLAG* = 4       # Get all column flags
-  CLIENT_CONNECT_WITH_DB* = 8 # One can specify db on connect
-  CLIENT_NO_SCHEMA* = 16      # Don't allow database.table.column
-  CLIENT_COMPRESS* = 32       # Can use compression protocol
-  CLIENT_ODBC* = 64           # Odbc client
-  CLIENT_LOCAL_FILES* = 128   # Can use LOAD DATA LOCAL
-  CLIENT_IGNORE_SPACE* = 256  # Ignore spaces before '('
-  CLIENT_PROTOCOL_41* = 512   # New 4.1 protocol
-  CLIENT_INTERACTIVE* = 1024  # This is an interactive client
-  CLIENT_SSL* = 2048          # Switch to SSL after handshake
-  CLIENT_IGNORE_SIGPIPE* = 4096 # IGNORE sigpipes
-  CLIENT_TRANSACTIONS* = 8192 # Client knows about transactions
-  CLIENT_RESERVED* = 16384    # Old flag for 4.1 protocol
-  CLIENT_SECURE_CONNECTION* = 32768 # New 4.1 authentication
-  CLIENT_MULTI_STATEMENTS* = 65536 # Enable/disable multi-stmt support
-  CLIENT_MULTI_RESULTS* = 131072 # Enable/disable multi-results
-  CLIENT_REMEMBER_OPTIONS*: int = 1 shl 31
-  SERVER_STATUS_IN_TRANS* = 1 # Transaction has started
-  SERVER_STATUS_AUTOCOMMIT* = 2 # Server in auto_commit mode
-  SERVER_STATUS_MORE_RESULTS* = 4 # More results on server
-  SERVER_MORE_RESULTS_EXISTS* = 8 # Multi query - next query exists
-  SERVER_QUERY_NO_GOOD_INDEX_USED* = 16
-  SERVER_QUERY_NO_INDEX_USED* = 32 # The server was able to fulfill the clients request and opened a
-                                   #      read-only non-scrollable cursor for a query. This flag comes
-                                   #      in reply to COM_STMT_EXECUTE and COM_STMT_FETCH commands.
-  SERVER_STATUS_CURSOR_EXISTS* = 64 # This flag is sent when a read-only cursor is exhausted, in reply to
-                                    #      COM_STMT_FETCH command.
-  SERVER_STATUS_LAST_ROW_SENT* = 128
-  SERVER_STATUS_DB_DROPPED* = 256 # A database was dropped
-  SERVER_STATUS_NO_BACKSLASH_ESCAPES* = 512
-  ERRMSG_SIZE* = 200
-  NET_READ_TIMEOUT* = 30      # Timeout on read
-  NET_WRITE_TIMEOUT* = 60     # Timeout on write
-  NET_WAIT_TIMEOUT* = 8 * 60 * 60 # Wait for new query
-  ONLY_KILL_QUERY* = 1
-
-const
-  MAX_TINYINT_WIDTH* = 3      # Max width for a TINY w.o. sign
-  MAX_SMALLINT_WIDTH* = 5     # Max width for a SHORT w.o. sign
-  MAX_MEDIUMINT_WIDTH* = 8    # Max width for a INT24 w.o. sign
-  MAX_INT_WIDTH* = 10         # Max width for a LONG w.o. sign
-  MAX_BIGINT_WIDTH* = 20      # Max width for a LONGLONG
-  MAX_CHAR_WIDTH* = 255       # Max length for a CHAR column
-  MAX_BLOB_WIDTH* = 8192      # Default width for blob
-
-type
-  Pst_net* = ptr St_net
-  St_net*{.final.} = object
-    vio*: PVio
-    buff*: cstring
-    buff_end*: cstring
-    write_pos*: cstring
-    read_pos*: cstring
-    fd*: my_socket            # For Perl DBI/dbd
-    max_packet*: int
-    max_packet_size*: int
-    pkt_nr*: cuint
-    compress_pkt_nr*: cuint
-    write_timeout*: cuint
-    read_timeout*: cuint
-    retry_count*: cuint
-    fcntl*: cint
-    compress*: my_bool #   The following variable is set if we are doing several queries in one
-                       #        command ( as in LOAD TABLE ... FROM MASTER ),
-                       #        and do not want to confuse the client with OK at the wrong time
-    remain_in_buf*: int
-    len*: int
-    buf_length*: int
-    where_b*: int
-    return_status*: ptr cint
-    reading_or_writing*: char
-    save_char*: cchar
-    no_send_ok*: my_bool      # For SPs and other things that do multiple stmts
-    no_send_eof*: my_bool     # For SPs' first version read-only cursors
-    no_send_error*: my_bool # Set if OK packet is already sent, and
-                            # we do not need to send error messages
-                            #   Pointer to query object in query cache, do not equal NULL (0) for
-                            #        queries in cache that have not stored its results yet
-                            # $endif
-    last_error*: array[0..(ERRMSG_SIZE) - 1, char]
-    sqlstate*: array[0..(SQLSTATE_LENGTH + 1) - 1, char]
-    last_errno*: cuint
-    error*: char
-    query_cache_query*: gptr
-    report_error*: my_bool    # We should report error (we have unreported error)
-    return_errno*: my_bool
-
-  NET* = St_net
-  PNET* = ptr NET
-{.deprecated: [Tst_net: St_net, TNET: NET].}
-
-const
-  packet_error* = - 1
-
-type
-  Enum_field_types* = enum    # For backward compatibility
-    TYPE_DECIMAL, TYPE_TINY, TYPE_SHORT, TYPE_LONG, TYPE_FLOAT, TYPE_DOUBLE,
-    TYPE_NULL, TYPE_TIMESTAMP, TYPE_LONGLONG, TYPE_INT24, TYPE_DATE, TYPE_TIME,
-    TYPE_DATETIME, TYPE_YEAR, TYPE_NEWDATE, TYPE_VARCHAR, TYPE_BIT,
-    TYPE_NEWDECIMAL = 246, TYPE_ENUM = 247, TYPE_SET = 248,
-    TYPE_TINY_BLOB = 249, TYPE_MEDIUM_BLOB = 250, TYPE_LONG_BLOB = 251,
-    TYPE_BLOB = 252, TYPE_VAR_STRING = 253, TYPE_STRING = 254,
-    TYPE_GEOMETRY = 255
-{.deprecated: [Tenum_field_types: Enum_field_types].}
-
-const
-  CLIENT_MULTI_QUERIES* = CLIENT_MULTI_STATEMENTS
-  FIELD_TYPE_DECIMAL* = TYPE_DECIMAL
-  FIELD_TYPE_NEWDECIMAL* = TYPE_NEWDECIMAL
-  FIELD_TYPE_TINY* = TYPE_TINY
-  FIELD_TYPE_SHORT* = TYPE_SHORT
-  FIELD_TYPE_LONG* = TYPE_LONG
-  FIELD_TYPE_FLOAT* = TYPE_FLOAT
-  FIELD_TYPE_DOUBLE* = TYPE_DOUBLE
-  FIELD_TYPE_NULL* = TYPE_NULL
-  FIELD_TYPE_TIMESTAMP* = TYPE_TIMESTAMP
-  FIELD_TYPE_LONGLONG* = TYPE_LONGLONG
-  FIELD_TYPE_INT24* = TYPE_INT24
-  FIELD_TYPE_DATE* = TYPE_DATE
-  FIELD_TYPE_TIME* = TYPE_TIME
-  FIELD_TYPE_DATETIME* = TYPE_DATETIME
-  FIELD_TYPE_YEAR* = TYPE_YEAR
-  FIELD_TYPE_NEWDATE* = TYPE_NEWDATE
-  FIELD_TYPE_ENUM* = TYPE_ENUM
-  FIELD_TYPE_SET* = TYPE_SET
-  FIELD_TYPE_TINY_BLOB* = TYPE_TINY_BLOB
-  FIELD_TYPE_MEDIUM_BLOB* = TYPE_MEDIUM_BLOB
-  FIELD_TYPE_LONG_BLOB* = TYPE_LONG_BLOB
-  FIELD_TYPE_BLOB* = TYPE_BLOB
-  FIELD_TYPE_VAR_STRING* = TYPE_VAR_STRING
-  FIELD_TYPE_STRING* = TYPE_STRING
-  FIELD_TYPE_CHAR* = TYPE_TINY
-  FIELD_TYPE_INTERVAL* = TYPE_ENUM
-  FIELD_TYPE_GEOMETRY* = TYPE_GEOMETRY
-  FIELD_TYPE_BIT* = TYPE_BIT  # Shutdown/kill enums and constants
-                              # Bits for THD::killable.
-  SHUTDOWN_KILLABLE_CONNECT* = chr(1 shl 0)
-  SHUTDOWN_KILLABLE_TRANS* = chr(1 shl 1)
-  SHUTDOWN_KILLABLE_LOCK_TABLE* = chr(1 shl 2)
-  SHUTDOWN_KILLABLE_UPDATE* = chr(1 shl 3)
-
-type
-  Enum_shutdown_level* = enum
-    SHUTDOWN_DEFAULT = 0, SHUTDOWN_WAIT_CONNECTIONS = 1,
-    SHUTDOWN_WAIT_TRANSACTIONS = 2, SHUTDOWN_WAIT_UPDATES = 8,
-    SHUTDOWN_WAIT_ALL_BUFFERS = 16, SHUTDOWN_WAIT_CRITICAL_BUFFERS = 17,
-    KILL_QUERY = 254, KILL_CONNECTION = 255
-  Enum_cursor_type* = enum    # options for mysql_set_option
-    CURSOR_TYPE_NO_CURSOR = 0, CURSOR_TYPE_READ_ONLY = 1,
-    CURSOR_TYPE_FOR_UPDATE = 2, CURSOR_TYPE_SCROLLABLE = 4
-  Enum_mysql_set_option* = enum
-    OPTION_MULTI_STATEMENTS_ON, OPTION_MULTI_STATEMENTS_OFF
-{.deprecated: [Tenum_shutdown_level: Enum_shutdown_level,
-              Tenum_cursor_type: Enum_cursor_type,
-              Tenum_mysql_set_option: Enum_mysql_set_option].}
-
-proc my_net_init*(net: PNET, vio: PVio): my_bool{.cdecl, dynlib: lib,
-    importc: "my_net_init".}
-proc my_net_local_init*(net: PNET){.cdecl, dynlib: lib,
-                                    importc: "my_net_local_init".}
-proc net_end*(net: PNET){.cdecl, dynlib: lib, importc: "net_end".}
-proc net_clear*(net: PNET){.cdecl, dynlib: lib, importc: "net_clear".}
-proc net_realloc*(net: PNET, len: int): my_bool{.cdecl, dynlib: lib,
-    importc: "net_realloc".}
-proc net_flush*(net: PNET): my_bool{.cdecl, dynlib: lib, importc: "net_flush".}
-proc my_net_write*(net: PNET, packet: cstring, length: int): my_bool{.cdecl,
-    dynlib: lib, importc: "my_net_write".}
-proc net_write_command*(net: PNET, command: char, header: cstring,
-                        head_len: int, packet: cstring, length: int): my_bool{.
-    cdecl, dynlib: lib, importc: "net_write_command".}
-proc net_real_write*(net: PNET, packet: cstring, length: int): cint{.cdecl,
-    dynlib: lib, importc: "net_real_write".}
-proc my_net_read*(net: PNET): int{.cdecl, dynlib: lib, importc: "my_net_read".}
-  # The following function is not meant for normal usage
-  #      Currently it's used internally by manager.c
-type
-  Psockaddr* = ptr Sockaddr
-  Sockaddr*{.final.} = object  # undefined structure
-{.deprecated: [Tsockaddr: Sockaddr].}
-
-proc my_connect*(s: my_socket, name: Psockaddr, namelen: cuint, timeout: cuint): cint{.
-    cdecl, dynlib: lib, importc: "my_connect".}
-type
-  Prand_struct* = ptr Rand_struct
-  Rand_struct*{.final.} = object # The following is for user defined functions
-    seed1*: int
-    seed2*: int
-    max_value*: int
-    max_value_dbl*: cdouble
-
-  Item_result* = enum
-    STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT, DECIMAL_RESULT
-  PItem_result* = ptr Item_result
-  Pst_udf_args* = ptr St_udf_args
-  St_udf_args*{.final.} = object
-    arg_count*: cuint         # Number of arguments
-    arg_type*: PItem_result   # Pointer to item_results
-    args*: cstringArray       # Pointer to item_results
-    lengths*: ptr int         # Length of string arguments
-    maybe_null*: cstring      # Length of string arguments
-    attributes*: cstringArray # Pointer to attribute name
-    attribute_lengths*: ptr int # Length of attribute arguments
-
-  UDF_ARGS* = St_udf_args
-  PUDF_ARGS* = ptr UDF_ARGS   # This holds information about the result
-  Pst_udf_init* = ptr St_udf_init
-  St_udf_init*{.final.} = object
-    maybe_null*: my_bool      # 1 if function can return NULL
-    decimals*: cuint          # for real functions
-    max_length*: int          # For string functions
-    theptr*: cstring          # free pointer for function data
-    const_item*: my_bool      # free pointer for function data
-
-  UDF_INIT* = St_udf_init
-  PUDF_INIT* = ptr UDF_INIT   # Constants when using compression
-{.deprecated: [Trand_stuct: Rand_struct, TItem_result: Item_result,
-              Tst_udf_args: St_udf_args, TUDF_ARGS: UDF_ARGS,
-              Tst_udf_init: St_udf_init, TUDF_INIT: UDF_INIT].}
-
-const
-  NET_HEADER_SIZE* = 4        # standard header size
-  COMP_HEADER_SIZE* = 3 # compression header extra size
-                        # Prototypes to password functions
-                        # These functions are used for authentication by client and server and
-                        #      implemented in sql/password.c
-
-proc randominit*(para1: Prand_struct, seed1: int, seed2: int){.cdecl,
-    dynlib: lib, importc: "randominit".}
-proc my_rnd*(para1: Prand_struct): cdouble{.cdecl, dynlib: lib,
-    importc: "my_rnd".}
-proc create_random_string*(fto: cstring, len: cuint, rand_st: Prand_struct){.
-    cdecl, dynlib: lib, importc: "create_random_string".}
-proc hash_password*(fto: int, password: cstring, password_len: cuint){.cdecl,
-    dynlib: lib, importc: "hash_password".}
-proc make_scrambled_password_323*(fto: cstring, password: cstring){.cdecl,
-    dynlib: lib, importc: "make_scrambled_password_323".}
-proc scramble_323*(fto: cstring, message: cstring, password: cstring){.cdecl,
-    dynlib: lib, importc: "scramble_323".}
-proc check_scramble_323*(para1: cstring, message: cstring, salt: int): my_bool{.
-    cdecl, dynlib: lib, importc: "check_scramble_323".}
-proc get_salt_from_password_323*(res: ptr int, password: cstring){.cdecl,
-    dynlib: lib, importc: "get_salt_from_password_323".}
-proc make_password_from_salt_323*(fto: cstring, salt: ptr int){.cdecl,
-    dynlib: lib, importc: "make_password_from_salt_323".}
-proc octet2hex*(fto: cstring, str: cstring, length: cuint): cstring{.cdecl,
-    dynlib: lib, importc: "octet2hex".}
-proc make_scrambled_password*(fto: cstring, password: cstring){.cdecl,
-    dynlib: lib, importc: "make_scrambled_password".}
-proc scramble*(fto: cstring, message: cstring, password: cstring){.cdecl,
-    dynlib: lib, importc: "scramble".}
-proc check_scramble*(reply: cstring, message: cstring, hash_stage2: pointer): my_bool{.
-    cdecl, dynlib: lib, importc: "check_scramble".}
-proc get_salt_from_password*(res: pointer, password: cstring){.cdecl,
-    dynlib: lib, importc: "get_salt_from_password".}
-proc make_password_from_salt*(fto: cstring, hash_stage2: pointer){.cdecl,
-    dynlib: lib, importc: "make_password_from_salt".}
-  # end of password.c
-proc get_tty_password*(opt_message: cstring): cstring{.cdecl, dynlib: lib,
-    importc: "get_tty_password".}
-proc errno_to_sqlstate*(errno: cuint): cstring{.cdecl, dynlib: lib,
-    importc: "mysql_errno_to_sqlstate".}
-  # Some other useful functions
-proc modify_defaults_file*(file_location: cstring, option: cstring,
-                           option_value: cstring, section_name: cstring,
-                           remove_option: cint): cint{.cdecl, dynlib: lib,
-    importc: "load_defaults".}
-proc load_defaults*(conf_file: cstring, groups: cstringArray, argc: ptr cint,
-                    argv: ptr cstringArray): cint{.cdecl, dynlib: lib,
-    importc: "load_defaults".}
-proc my_init*(): my_bool{.cdecl, dynlib: lib, importc: "my_init".}
-proc my_thread_init*(): my_bool{.cdecl, dynlib: lib, importc: "my_thread_init".}
-proc my_thread_end*(){.cdecl, dynlib: lib, importc: "my_thread_end".}
-const
-  NULL_LENGTH*: int = int(not (0)) # For net_store_length
-
-const
-  STMT_HEADER* = 4
-  LONG_DATA_HEADER* = 6 #  ------------ Stop of declaration in "mysql_com.h"   -----------------------
-                        # $include "mysql_time.h"
-                        # $include "mysql_version.h"
-                        # $include "typelib.h"
-                        # $include "my_list.h" /* for LISTs used in 'MYSQL' and 'MYSQL_STMT' */
-                        #      var
-                        #         mysql_port : cuint;cvar;external;
-                        #         mysql_unix_port : Pchar;cvar;external;
-
-const
-  CLIENT_NET_READ_TIMEOUT* = 365 * 24 * 3600 # Timeout on read
-  CLIENT_NET_WRITE_TIMEOUT* = 365 * 24 * 3600 # Timeout on write
-
-type
-  Pst_mysql_field* = ptr St_mysql_field
-  St_mysql_field*{.final.} = object
-    name*: cstring            # Name of column
-    org_name*: cstring        # Original column name, if an alias
-    table*: cstring           # Table of column if column was a field
-    org_table*: cstring       # Org table name, if table was an alias
-    db*: cstring              # Database for table
-    catalog*: cstring         # Catalog for table
-    def*: cstring             # Default value (set by mysql_list_fields)
-    len*: int                 # Width of column (create length)
-    max_length*: int          # Max width for selected set
-    name_length*: cuint
-    org_name_length*: cuint
-    table_length*: cuint
-    org_table_length*: cuint
-    db_length*: cuint
-    catalog_length*: cuint
-    def_length*: cuint
-    flags*: cuint             # Div flags
-    decimals*: cuint          # Number of decimals in field
-    charsetnr*: cuint         # Character set
-    ftype*: Enum_field_types  # Type of field. See mysql_com.h for types
-    extension*: pointer
-
-  FIELD* = St_mysql_field
-  PFIELD* = ptr FIELD
-  PROW* = ptr ROW             # return data as array of strings
-  ROW* = cstringArray
-  PFIELD_OFFSET* = ptr FIELD_OFFSET # offset to current field
-  FIELD_OFFSET* = cuint
-{.deprecated: [Tst_mysql_field: St_mysql_field, TFIELD: FIELD, TROW: ROW,
-              TFIELD_OFFSET: FIELD_OFFSET].}
-
-proc IS_PRI_KEY*(n: int32): bool
-proc IS_NOT_NULL*(n: int32): bool
-proc IS_BLOB*(n: int32): bool
-proc IS_NUM*(t: Enum_field_types): bool
-proc INTERNAL_NUM_FIELD*(f: Pst_mysql_field): bool
-proc IS_NUM_FIELD*(f: Pst_mysql_field): bool
-type
-  my_ulonglong* = int64
-  Pmy_ulonglong* = ptr my_ulonglong
-
-const
-  COUNT_ERROR* = not (my_ulonglong(0))
-
-type
-  Pst_mysql_rows* = ptr St_mysql_rows
-  St_mysql_rows*{.final.} = object
-    next*: Pst_mysql_rows     # list of rows
-    data*: ROW
-    len*: int
-
-  ROWS* = St_mysql_rows
-  PROWS* = ptr ROWS
-  PROW_OFFSET* = ptr ROW_OFFSET # offset to current row
-  ROW_OFFSET* = ROWS
-{.deprecated: [Tst_mysql_rows: St_mysql_rows, TROWS: ROWS,
-              TROW_OFFSET: ROW_OFFSET].}
-
-const
-  ALLOC_MAX_BLOCK_TO_DROP* = 4096
-  ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP* = 10 # struct for once_alloc (block)
-
-type
-  Pst_used_mem* = ptr St_used_mem
-  St_used_mem*{.final.} = object
-    next*: Pst_used_mem       # Next block in use
-    left*: cuint              # memory left in block
-    size*: cuint              # size of block
-
-  USED_MEM* = St_used_mem
-  PUSED_MEM* = ptr USED_MEM
-  Pst_mem_root* = ptr St_mem_root
-  St_mem_root*{.final.} = object
-    free*: PUSED_MEM          # blocks with free memory in it
-    used*: PUSED_MEM          # blocks almost without free memory
-    pre_alloc*: PUSED_MEM     # preallocated block
-    min_malloc*: cuint        # if block have less memory it will be put in 'used' list
-    block_size*: cuint        # initial block size
-    block_num*: cuint # allocated blocks counter
-                      #    first free block in queue test counter (if it exceed
-                      #       MAX_BLOCK_USAGE_BEFORE_DROP block will be dropped in 'used' list)
-    first_block_usage*: cuint
-    error_handler*: proc (){.cdecl.}
-
-  MEM_ROOT* = St_mem_root
-  PMEM_ROOT* = ptr MEM_ROOT   #  ------------ Stop of declaration in "my_alloc.h"    ----------------------
-{.deprecated: [Tst_used_mem: St_used_mem, TUSED_MEM: USED_MEM,
-              Tst_mem_root: St_mem_root, TMEM_ROOT: MEM_ROOT].}
-
-type
-  Pst_mysql_data* = ptr St_mysql_data
-  St_mysql_data*{.final.} = object
-    rows*: my_ulonglong
-    fields*: cuint
-    data*: PROWS
-    alloc*: MEM_ROOT
-    prev_ptr*: ptr PROWS
-
-  DATA* = St_mysql_data
-  PDATA* = ptr DATA
-  Option* = enum
-    OPT_CONNECT_TIMEOUT, OPT_COMPRESS, OPT_NAMED_PIPE, INIT_COMMAND,
-    READ_DEFAULT_FILE, READ_DEFAULT_GROUP, SET_CHARSET_DIR, SET_CHARSET_NAME,
-    OPT_LOCAL_INFILE, OPT_PROTOCOL, SHARED_MEMORY_BASE_NAME, OPT_READ_TIMEOUT,
-    OPT_WRITE_TIMEOUT, OPT_USE_RESULT, OPT_USE_REMOTE_CONNECTION,
-    OPT_USE_EMBEDDED_CONNECTION, OPT_GUESS_CONNECTION, SET_CLIENT_IP,
-    SECURE_AUTH, REPORT_DATA_TRUNCATION, OPT_RECONNECT
-{.deprecated: [Tst_mysql_data: St_mysql_data, TDATA: DATA, Toption: Option].}
-
-const
-  MAX_MYSQL_MANAGER_ERR* = 256
-  MAX_MYSQL_MANAGER_MSG* = 256
-  MANAGER_OK* = 200
-  MANAGER_INFO* = 250
-  MANAGER_ACCESS* = 401
-  MANAGER_CLIENT_ERR* = 450
-  MANAGER_INTERNAL_ERR* = 500
-
-type
-  St_dynamic_array*{.final.} = object
-    buffer*: cstring
-    elements*: cuint
-    max_element*: cuint
-    alloc_increment*: cuint
-    size_of_element*: cuint
-
-  DYNAMIC_ARRAY* = St_dynamic_array
-  Pst_dynamic_array* = ptr St_dynamic_array
-  Pst_mysql_options* = ptr St_mysql_options
-  St_mysql_options*{.final.} = object
-    connect_timeout*: cuint
-    read_timeout*: cuint
-    write_timeout*: cuint
-    port*: cuint
-    protocol*: cuint
-    client_flag*: int
-    host*: cstring
-    user*: cstring
-    password*: cstring
-    unix_socket*: cstring
-    db*: cstring
-    init_commands*: Pst_dynamic_array
-    my_cnf_file*: cstring
-    my_cnf_group*: cstring
-    charset_dir*: cstring
-    charset_name*: cstring
-    ssl_key*: cstring         # PEM key file
-    ssl_cert*: cstring        # PEM cert file
-    ssl_ca*: cstring          # PEM CA file
-    ssl_capath*: cstring      # PEM directory of CA-s?
-    ssl_cipher*: cstring      # cipher to use
-    shared_memory_base_name*: cstring
-    max_allowed_packet*: int
-    use_ssl*: my_bool         # if to use SSL or not
-    compress*: my_bool
-    named_pipe*: my_bool #  On connect, find out the replication role of the server, and
-                         #       establish connections to all the peers
-    rpl_probe*: my_bool #  Each call to mysql_real_query() will parse it to tell if it is a read
-                        #       or a write, and direct it to the slave or the master
-    rpl_parse*: my_bool #  If set, never read from a master, only from slave, when doing
-                        #       a read that is replication-aware
-    no_master_reads*: my_bool
-    separate_thread*: my_bool
-    methods_to_use*: Option
-    client_ip*: cstring
-    secure_auth*: my_bool     # Refuse client connecting to server if it uses old (pre-4.1.1) protocol
-    report_data_truncation*: my_bool # 0 - never report, 1 - always report (default)
-                                     # function pointers for local infile support
-    local_infile_init*: proc (para1: var pointer, para2: cstring, para3: pointer): cint{.
-        cdecl.}
-    local_infile_read*: proc (para1: pointer, para2: cstring, para3: cuint): cint
-    local_infile_end*: proc (para1: pointer)
-    local_infile_error*: proc (para1: pointer, para2: cstring, para3: cuint): cint
-    local_infile_userdata*: pointer
-
-  Status* = enum
-    STATUS_READY, STATUS_GET_RESULT, STATUS_USE_RESULT
-  Protocol_type* = enum  # There are three types of queries - the ones that have to go to
-                          # the master, the ones that go to a slave, and the administrative
-                          # type which must happen on the pivot connectioin
-    PROTOCOL_DEFAULT, PROTOCOL_TCP, PROTOCOL_SOCKET, PROTOCOL_PIPE,
-    PROTOCOL_MEMORY
-  Rpl_type* = enum
-    RPL_MASTER, RPL_SLAVE, RPL_ADMIN
-  Charset_info_st*{.final.} = object
-    number*: cuint
-    primary_number*: cuint
-    binary_number*: cuint
-    state*: cuint
-    csname*: cstring
-    name*: cstring
-    comment*: cstring
-    tailoring*: cstring
-    ftype*: cstring
-    to_lower*: cstring
-    to_upper*: cstring
-    sort_order*: cstring
-    contractions*: ptr int16
-    sort_order_big*: ptr ptr int16
-    tab_to_uni*: ptr int16
-    tab_from_uni*: pointer    # was ^MY_UNI_IDX
-    state_map*: cstring
-    ident_map*: cstring
-    strxfrm_multiply*: cuint
-    mbminlen*: cuint
-    mbmaxlen*: cuint
-    min_sort_char*: int16
-    max_sort_char*: int16
-    escape_with_backslash_is_dangerous*: my_bool
-    cset*: pointer            # was ^MY_CHARSET_HANDLER
-    coll*: pointer            # was ^MY_COLLATION_HANDLER;
-
-  CHARSET_INFO* = Charset_info_st
-  Pcharset_info_st* = ptr Charset_info_st
-  Pcharacter_set* = ptr Character_set
-  Character_set*{.final.} = object
-    number*: cuint
-    state*: cuint
-    csname*: cstring
-    name*: cstring
-    comment*: cstring
-    dir*: cstring
-    mbminlen*: cuint
-    mbmaxlen*: cuint
-
-  MY_CHARSET_INFO* = Character_set
-  PMY_CHARSET_INFO* = ptr MY_CHARSET_INFO
-  Pst_mysql_methods* = ptr St_mysql_methods
-  Pst_mysql* = ptr St_mysql
-  St_mysql*{.final.} = object
-    net*: NET                 # Communication parameters
-    connector_fd*: gptr       # ConnectorFd for SSL
-    host*: cstring
-    user*: cstring
-    passwd*: cstring
-    unix_socket*: cstring
-    server_version*: cstring
-    host_info*: cstring
-    info*: cstring
-    db*: cstring
-    charset*: Pcharset_info_st
-    fields*: PFIELD
-    field_alloc*: MEM_ROOT
-    affected_rows*: my_ulonglong
-    insert_id*: my_ulonglong  # id if insert on table with NEXTNR
-    extra_info*: my_ulonglong # Used by mysqlshow, not used by mysql 5.0 and up
-    thread_id*: int           # Id for connection in server
-    packet_length*: int
-    port*: cuint
-    client_flag*: int
-    server_capabilities*: int
-    protocol_version*: cuint
-    field_count*: cuint
-    server_status*: cuint
-    server_language*: cuint
-    warning_count*: cuint
-    options*: St_mysql_options
-    status*: Status
-    free_me*: my_bool         # If free in mysql_close
-    reconnect*: my_bool       # set to 1 if automatic reconnect
-    scramble*: array[0..(SCRAMBLE_LENGTH + 1) - 1, char] # session-wide random string
-                                                         #  Set if this is the original connection, not a master or a slave we have
-                                                         #       added though mysql_rpl_probe() or mysql_set_master()/ mysql_add_slave()
-    rpl_pivot*: my_bool #   Pointers to the master, and the next slave connections, points to
-                        #        itself if lone connection.
-    master*: Pst_mysql
-    next_slave*: Pst_mysql
-    last_used_slave*: Pst_mysql # needed for round-robin slave pick
-    last_used_con*: Pst_mysql # needed for send/read/store/use result to work correctly with replication
-    stmts*: pointer           # was PList, list of all statements
-    methods*: Pst_mysql_methods
-    thd*: pointer #   Points to boolean flag in MYSQL_RES  or MYSQL_STMT. We set this flag
-                  #        from mysql_stmt_close if close had to cancel result set of this object.
-    unbuffered_fetch_owner*: Pmy_bool
-
-  MySQL* = St_mysql
-  PMySQL* = ptr MySQL
-  Pst_mysql_res* = ptr St_mysql_res
-  St_mysql_res*{.final.} = object
-    row_count*: my_ulonglong
-    fields*: PFIELD
-    data*: PDATA
-    data_cursor*: PROWS
-    lengths*: ptr int         # column lengths of current row
-    handle*: PMySQL                # for unbuffered reads
-    field_alloc*: MEM_ROOT
-    field_count*: cuint
-    current_field*: cuint
-    row*: ROW                 # If unbuffered read
-    current_row*: ROW         # buffer to current row
-    eof*: my_bool             # Used by mysql_fetch_row
-    unbuffered_fetch_cancelled*: my_bool # mysql_stmt_close() had to cancel this result
-    methods*: Pst_mysql_methods
-
-  RES* = St_mysql_res
-  PRES* = ptr RES
-  Pst_mysql_stmt* = ptr St_mysql_stmt
-  PSTMT* = ptr STMT
-  St_mysql_methods*{.final.} = object
-    read_query_result*: proc (MySQL:  PMySQL): my_bool{.cdecl.}
-    advanced_command*: proc (MySQL: PMySQL, command: Enum_server_command, header: cstring,
-                             header_length: int, arg: cstring, arg_length: int,
-                             skip_check: my_bool): my_bool
-    read_rows*: proc (MySQL: PMySQL, fields: PFIELD, fields_count: cuint): PDATA
-    use_result*: proc (MySQL: PMySQL): PRES
-    fetch_lengths*: proc (fto: ptr int, column: ROW, field_count: cuint)
-    flush_use_result*: proc (MySQL: PMySQL)
-    list_fields*: proc (MySQL: PMySQL): PFIELD
-    read_prepare_result*: proc (MySQL: PMySQL, stmt: PSTMT): my_bool
-    stmt_execute*: proc (stmt: PSTMT): cint
-    read_binary_rows*: proc (stmt: PSTMT): cint
-    unbuffered_fetch*: proc (MySQL: PMySQL, row: cstringArray): cint
-    free_embedded_thd*: proc (MySQL: PMySQL)
-    read_statistics*: proc (MySQL: PMySQL): cstring
-    next_result*: proc (MySQL: PMySQL): my_bool
-    read_change_user_result*: proc (MySQL: PMySQL, buff: cstring, passwd: cstring): cint
-    read_rowsfrom_cursor*: proc (stmt: PSTMT): cint
-
-  METHODS* = St_mysql_methods
-  PMETHODS* = ptr METHODS
-  Pst_mysql_manager* = ptr St_mysql_manager
-  St_mysql_manager*{.final.} = object
-    net*: NET
-    host*: cstring
-    user*: cstring
-    passwd*: cstring
-    port*: cuint
-    free_me*: my_bool
-    eof*: my_bool
-    cmd_status*: cint
-    last_errno*: cint
-    net_buf*: cstring
-    net_buf_pos*: cstring
-    net_data_end*: cstring
-    net_buf_size*: cint
-    last_error*: array[0..(MAX_MYSQL_MANAGER_ERR) - 1, char]
-
-  MANAGER* = St_mysql_manager
-  PMANAGER* = ptr MANAGER
-  Pst_mysql_parameters* = ptr St_mysql_parameters
-  St_mysql_parameters*{.final.} = object
-    p_max_allowed_packet*: ptr int
-    p_net_buffer_length*: ptr int
-
-  PARAMETERS* = St_mysql_parameters
-  PPARAMETERS* = ptr PARAMETERS
-  Enum_mysql_stmt_state* = enum
-    STMT_INIT_DONE = 1, STMT_PREPARE_DONE, STMT_EXECUTE_DONE, STMT_FETCH_DONE
-  Pst_mysql_bind* = ptr St_mysql_bind
-  St_mysql_bind*{.final.} = object
-    len*: int                 # output length pointer
-    is_null*: Pmy_bool        # Pointer to null indicator
-    buffer*: pointer          # buffer to get/put data
-    error*: PMy_bool          # set this if you want to track data truncations happened during fetch
-    buffer_type*: Enum_field_types # buffer type
-    buffer_length*: int       # buffer length, must be set for str/binary
-                              # Following are for internal use. Set by mysql_stmt_bind_param
-    row_ptr*: ptr byte        # for the current data position
-    offset*: int              # offset position for char/binary fetch
-    length_value*: int        #  Used if length is 0
-    param_number*: cuint      # For null count and error messages
-    pack_length*: cuint       # Internal length for packed data
-    error_value*: my_bool     # used if error is 0
-    is_unsigned*: my_bool     # set if integer type is unsigned
-    long_data_used*: my_bool  # If used with mysql_send_long_data
-    is_null_value*: my_bool   # Used if is_null is 0
-    store_param_func*: proc (net: PNET, param: Pst_mysql_bind){.cdecl.}
-    fetch_result*: proc (para1: Pst_mysql_bind, para2: PFIELD, row: PPbyte)
-    skip_result*: proc (para1: Pst_mysql_bind, para2: PFIELD, row: PPbyte)
-
-  BIND* = St_mysql_bind
-  PBIND* = ptr BIND           # statement handler
-  St_mysql_stmt*{.final.} = object
-    mem_root*: MEM_ROOT       # root allocations
-    mysql*: PMySQL                      # connection handle
-    params*: PBIND            # input parameters
-    `bind`*: PBIND            # input parameters
-    fields*: PFIELD           # result set metadata
-    result*: DATA             # cached result set
-    data_cursor*: PROWS       # current row in cached result
-    affected_rows*: my_ulonglong # copy of mysql->affected_rows after statement execution
-    insert_id*: my_ulonglong
-    read_row_func*: proc (stmt: Pst_mysql_stmt, row: PPbyte): cint{.cdecl.}
-    stmt_id*: int             # Id for prepared statement
-    flags*: int               # i.e. type of cursor to open
-    prefetch_rows*: int       # number of rows per one COM_FETCH
-    server_status*: cuint # Copied from mysql->server_status after execute/fetch to know
-                          # server-side cursor status for this statement.
-    last_errno*: cuint        # error code
-    param_count*: cuint       # input parameter count
-    field_count*: cuint       # number of columns in result set
-    state*: Enum_mysql_stmt_state # statement state
-    last_error*: array[0..(ERRMSG_SIZE) - 1, char] # error message
-    sqlstate*: array[0..(SQLSTATE_LENGTH + 1) - 1, char]
-    send_types_to_server*: my_bool # Types of input parameters should be sent to server
-    bind_param_done*: my_bool # input buffers were supplied
-    bind_result_done*: char   # output buffers were supplied
-    unbuffered_fetch_cancelled*: my_bool
-    update_max_length*: my_bool
-
-  STMT* = St_mysql_stmt
-
-  Enum_stmt_attr_type* = enum
-    STMT_ATTR_UPDATE_MAX_LENGTH, STMT_ATTR_CURSOR_TYPE, STMT_ATTR_PREFETCH_ROWS
-{.deprecated: [Tst_dynamic_array: St_dynamic_array, Tst_mysql_options: St_mysql_options,
-              TDYNAMIC_ARRAY: DYNAMIC_ARRAY, Tprotocol_type: Protocol_type,
-              Trpl_type: Rpl_type, Tcharset_info_st: Charset_info_st,
-              TCHARSET_INFO: CHARSET_INFO, Tcharacter_set: Character_set,
-              TMY_CHARSET_INFO: MY_CHARSET_INFO, Tst_mysql: St_mysql,
-              Tst_mysql_methods: St_mysql_methods, TMySql: MySql,
-              Tst_mysql_res: St_mysql_res, TMETHODS: METHODS, TRES: RES,
-              Tst_mysql_manager: St_mysql_manager, TMANAGER: MANAGER,
-              Tst_mysql_parameters: St_mysql_parameters, TPARAMETERS: PARAMETERS,
-              Tenum_mysql_stmt_state: Enum_mysql_stmt_state,
-              Tst_mysql_bind: St_mysql_bind, TBIND: BIND, Tst_mysql_stmt: St_mysql_stmt,
-              TSTMT: STMT, Tenum_stmt_attr_type: Enum_stmt_attr_type,
-              Tstatus: Status].}
-
-proc server_init*(argc: cint, argv: cstringArray, groups: cstringArray): cint{.
-    cdecl, dynlib: lib, importc: "mysql_server_init".}
-proc server_end*(){.cdecl, dynlib: lib, importc: "mysql_server_end".}
-  # mysql_server_init/end need to be called when using libmysqld or
-  #      libmysqlclient (exactly, mysql_server_init() is called by mysql_init() so
-  #      you don't need to call it explicitly; but you need to call
-  #      mysql_server_end() to free memory). The names are a bit misleading
-  #      (mysql_SERVER* to be used when using libmysqlCLIENT). So we add more general
-  #      names which suit well whether you're using libmysqld or libmysqlclient. We
-  #      intend to promote these aliases over the mysql_server* ones.
-proc library_init*(argc: cint, argv: cstringArray, groups: cstringArray): cint{.
-    cdecl, dynlib: lib, importc: "mysql_server_init".}
-proc library_end*(){.cdecl, dynlib: lib, importc: "mysql_server_end".}
-proc get_parameters*(): PPARAMETERS{.stdcall, dynlib: lib,
-                                     importc: "mysql_get_parameters".}
-  # Set up and bring down a thread; these function should be called
-  #      for each thread in an application which opens at least one MySQL
-  #      connection.  All uses of the connection(s) should be between these
-  #      function calls.
-proc thread_init*(): my_bool{.stdcall, dynlib: lib, importc: "mysql_thread_init".}
-proc thread_end*(){.stdcall, dynlib: lib, importc: "mysql_thread_end".}
-  # Functions to get information from the MYSQL and MYSQL_RES structures
-  #      Should definitely be used if one uses shared libraries.
-proc num_rows*(res: PRES): my_ulonglong{.stdcall, dynlib: lib,
-    importc: "mysql_num_rows".}
-proc num_fields*(res: PRES): cuint{.stdcall, dynlib: lib,
-                                    importc: "mysql_num_fields".}
-proc eof*(res: PRES): my_bool{.stdcall, dynlib: lib, importc: "mysql_eof".}
-proc fetch_field_direct*(res: PRES, fieldnr: cuint): PFIELD{.stdcall,
-    dynlib: lib, importc: "mysql_fetch_field_direct".}
-proc fetch_fields*(res: PRES): PFIELD{.stdcall, dynlib: lib,
-                                       importc: "mysql_fetch_fields".}
-proc row_tell*(res: PRES): ROW_OFFSET{.stdcall, dynlib: lib,
-                                       importc: "mysql_row_tell".}
-proc field_tell*(res: PRES): FIELD_OFFSET{.stdcall, dynlib: lib,
-    importc: "mysql_field_tell".}
-proc field_count*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib,
-                               importc: "mysql_field_count".}
-proc affected_rows*(MySQL: PMySQL): my_ulonglong{.stdcall, dynlib: lib,
-                                        importc: "mysql_affected_rows".}
-proc insert_id*(MySQL: PMySQL): my_ulonglong{.stdcall, dynlib: lib,
-                                    importc: "mysql_insert_id".}
-proc errno*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, importc: "mysql_errno".}
-proc error*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_error".}
-proc sqlstate*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_sqlstate".}
-proc warning_count*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib,
-                                 importc: "mysql_warning_count".}
-proc info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_info".}
-proc thread_id*(MySQL: PMySQL): int{.stdcall, dynlib: lib, importc: "mysql_thread_id".}
-proc character_set_name*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib,
-                                        importc: "mysql_character_set_name".}
-proc set_character_set*(MySQL: PMySQL, csname: cstring): int32{.stdcall, dynlib: lib,
-    importc: "mysql_set_character_set".}
-proc init*(MySQL: PMySQL): PMySQL{.stdcall, dynlib: lib, importc: "mysql_init".}
-proc ssl_set*(MySQL: PMySQL, key: cstring, cert: cstring, ca: cstring, capath: cstring,
-              cipher: cstring): my_bool{.stdcall, dynlib: lib,
-    importc: "mysql_ssl_set".}
-proc change_user*(MySQL: PMySQL, user: cstring, passwd: cstring, db: cstring): my_bool{.
-    stdcall, dynlib: lib, importc: "mysql_change_user".}
-proc real_connect*(MySQL: PMySQL, host: cstring, user: cstring, passwd: cstring,
-                   db: cstring, port: cuint, unix_socket: cstring,
-                   clientflag: int): PMySQL{.stdcall, dynlib: lib,
-                                        importc: "mysql_real_connect".}
-proc select_db*(MySQL: PMySQL, db: cstring): cint{.stdcall, dynlib: lib,
-    importc: "mysql_select_db".}
-proc query*(MySQL: PMySQL, q: cstring): cint{.stdcall, dynlib: lib, importc: "mysql_query".}
-proc send_query*(MySQL: PMySQL, q: cstring, len: int): cint{.stdcall, dynlib: lib,
-    importc: "mysql_send_query".}
-proc real_query*(MySQL: PMySQL, q: cstring, len: int): cint{.stdcall, dynlib: lib,
-    importc: "mysql_real_query".}
-proc store_result*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib,
-                               importc: "mysql_store_result".}
-proc use_result*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib, importc: "mysql_use_result".}
-  # perform query on master
-proc master_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, dynlib: lib,
-    importc: "mysql_master_query".}
-proc master_send_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall,
-    dynlib: lib, importc: "mysql_master_send_query".}
-  # perform query on slave
-proc slave_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, dynlib: lib,
-    importc: "mysql_slave_query".}
-proc slave_send_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall,
-    dynlib: lib, importc: "mysql_slave_send_query".}
-proc get_character_set_info*(MySQL: PMySQL, charset: PMY_CHARSET_INFO){.stdcall,
-    dynlib: lib, importc: "mysql_get_character_set_info".}
-  # local infile support
-const
-  LOCAL_INFILE_ERROR_LEN* = 512
-
-# procedure mysql_set_local_infile_handler(mysql:PMYSQL; local_infile_init:function (para1:Ppointer; para2:Pchar; para3:pointer):longint; local_infile_read:function (para1:pointer; para2:Pchar; para3:dword):longint; local_infile_end:procedure (_pa
-# para6:pointer);cdecl;external mysqllib name 'mysql_set_local_infile_handler';
-
-proc set_local_infile_default*(MySQL: PMySQL){.cdecl, dynlib: lib,
-                                     importc: "mysql_set_local_infile_default".}
-  # enable/disable parsing of all queries to decide if they go on master or
-  #      slave
-proc enable_rpl_parse*(MySQL: PMySQL){.stdcall, dynlib: lib,
-                             importc: "mysql_enable_rpl_parse".}
-proc disable_rpl_parse*(MySQL: PMySQL){.stdcall, dynlib: lib,
-                              importc: "mysql_disable_rpl_parse".}
-  # get the value of the parse flag
-proc rpl_parse_enabled*(MySQL: PMySQL): cint{.stdcall, dynlib: lib,
-                                    importc: "mysql_rpl_parse_enabled".}
-  #  enable/disable reads from master
-proc enable_reads_from_master*(MySQL: PMySQL){.stdcall, dynlib: lib,
-                                     importc: "mysql_enable_reads_from_master".}
-proc disable_reads_from_master*(MySQL: PMySQL){.stdcall, dynlib: lib, importc: "mysql_disable_reads_from_master".}
-  # get the value of the master read flag
-proc reads_from_master_enabled*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib,
-    importc: "mysql_reads_from_master_enabled".}
-proc rpl_query_type*(q: cstring, length: cint): Rpl_type{.stdcall, dynlib: lib,
-    importc: "mysql_rpl_query_type".}
-  # discover the master and its slaves
-proc rpl_probe*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_rpl_probe".}
-  # set the master, close/free the old one, if it is not a pivot
-proc set_master*(MySQL: PMySQL, host: cstring, port: cuint, user: cstring, passwd: cstring): cint{.
-    stdcall, dynlib: lib, importc: "mysql_set_master".}
-proc add_slave*(MySQL: PMySQL, host: cstring, port: cuint, user: cstring, passwd: cstring): cint{.
-    stdcall, dynlib: lib, importc: "mysql_add_slave".}
-proc shutdown*(MySQL: PMySQL, shutdown_level: Enum_shutdown_level): cint{.stdcall,
-    dynlib: lib, importc: "mysql_shutdown".}
-proc dump_debug_info*(MySQL: PMySQL): cint{.stdcall, dynlib: lib,
-                                  importc: "mysql_dump_debug_info".}
-proc refresh*(sql: PMySQL, refresh_options: cuint): cint{.stdcall, dynlib: lib,
-    importc: "mysql_refresh".}
-proc kill*(MySQL: PMySQL, pid: int): cint{.stdcall, dynlib: lib, importc: "mysql_kill".}
-proc set_server_option*(MySQL: PMySQL, option: Enum_mysql_set_option): cint{.stdcall,
-    dynlib: lib, importc: "mysql_set_server_option".}
-proc ping*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, importc: "mysql_ping".}
-proc stat*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_stat".}
-proc get_server_info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib,
-                                     importc: "mysql_get_server_info".}
-proc get_client_info*(): cstring{.stdcall, dynlib: lib,
-                                  importc: "mysql_get_client_info".}
-proc get_client_version*(): int{.stdcall, dynlib: lib,
-                                 importc: "mysql_get_client_version".}
-proc get_host_info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib,
-                                   importc: "mysql_get_host_info".}
-proc get_server_version*(MySQL: PMySQL): int{.stdcall, dynlib: lib,
-                                    importc: "mysql_get_server_version".}
-proc get_proto_info*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib,
-                                  importc: "mysql_get_proto_info".}
-proc list_dbs*(MySQL: PMySQL, wild: cstring): PRES{.stdcall, dynlib: lib,
-    importc: "mysql_list_dbs".}
-proc list_tables*(MySQL: PMySQL, wild: cstring): PRES{.stdcall, dynlib: lib,
-    importc: "mysql_list_tables".}
-proc list_processes*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib,
-                                 importc: "mysql_list_processes".}
-proc options*(MySQL: PMySQL, option: Option, arg: cstring): cint{.stdcall, dynlib: lib,
-    importc: "mysql_options".}
-proc free_result*(result: PRES){.stdcall, dynlib: lib,
-                                 importc: "mysql_free_result".}
-proc data_seek*(result: PRES, offset: my_ulonglong){.stdcall, dynlib: lib,
-    importc: "mysql_data_seek".}
-proc row_seek*(result: PRES, offset: ROW_OFFSET): ROW_OFFSET{.stdcall,
-    dynlib: lib, importc: "mysql_row_seek".}
-proc field_seek*(result: PRES, offset: FIELD_OFFSET): FIELD_OFFSET{.stdcall,
-    dynlib: lib, importc: "mysql_field_seek".}
-proc fetch_row*(result: PRES): ROW{.stdcall, dynlib: lib,
-                                    importc: "mysql_fetch_row".}
-proc fetch_lengths*(result: PRES): ptr int{.stdcall, dynlib: lib,
-    importc: "mysql_fetch_lengths".}
-proc fetch_field*(result: PRES): PFIELD{.stdcall, dynlib: lib,
-    importc: "mysql_fetch_field".}
-proc list_fields*(MySQL: PMySQL, table: cstring, wild: cstring): PRES{.stdcall,
-    dynlib: lib, importc: "mysql_list_fields".}
-proc escape_string*(fto: cstring, `from`: cstring, from_length: int): int{.
-    stdcall, dynlib: lib, importc: "mysql_escape_string".}
-proc hex_string*(fto: cstring, `from`: cstring, from_length: int): int{.stdcall,
-    dynlib: lib, importc: "mysql_hex_string".}
-proc real_escape_string*(MySQL: PMySQL, fto: cstring, `from`: cstring, len: int): int{.
-    stdcall, dynlib: lib, importc: "mysql_real_escape_string".}
-proc debug*(debug: cstring){.stdcall, dynlib: lib, importc: "mysql_debug".}
-  #    function mysql_odbc_escape_string(mysql:PMYSQL; fto:Pchar; to_length:dword; from:Pchar; from_length:dword;
-  #               param:pointer; extend_buffer:function (para1:pointer; to:Pchar; length:Pdword):Pchar):Pchar;stdcall;external mysqllib name 'mysql_odbc_escape_string';
-proc myodbc_remove_escape*(MySQL: PMySQL, name: cstring){.stdcall, dynlib: lib,
-    importc: "myodbc_remove_escape".}
-proc thread_safe*(): cuint{.stdcall, dynlib: lib, importc: "mysql_thread_safe".}
-proc embedded*(): my_bool{.stdcall, dynlib: lib, importc: "mysql_embedded".}
-proc manager_init*(con: PMANAGER): PMANAGER{.stdcall, dynlib: lib,
-    importc: "mysql_manager_init".}
-proc manager_connect*(con: PMANAGER, host: cstring, user: cstring,
-                      passwd: cstring, port: cuint): PMANAGER{.stdcall,
-    dynlib: lib, importc: "mysql_manager_connect".}
-proc manager_close*(con: PMANAGER){.stdcall, dynlib: lib,
-                                    importc: "mysql_manager_close".}
-proc manager_command*(con: PMANAGER, cmd: cstring, cmd_len: cint): cint{.
-    stdcall, dynlib: lib, importc: "mysql_manager_command".}
-proc manager_fetch_line*(con: PMANAGER, res_buf: cstring, res_buf_size: cint): cint{.
-    stdcall, dynlib: lib, importc: "mysql_manager_fetch_line".}
-proc read_query_result*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib,
-                                       importc: "mysql_read_query_result".}
-proc stmt_init*(MySQL: PMySQL): PSTMT{.stdcall, dynlib: lib, importc: "mysql_stmt_init".}
-proc stmt_prepare*(stmt: PSTMT, query: cstring, len: int): cint{.stdcall,
-    dynlib: lib, importc: "mysql_stmt_prepare".}
-proc stmt_execute*(stmt: PSTMT): cint{.stdcall, dynlib: lib,
-                                       importc: "mysql_stmt_execute".}
-proc stmt_fetch*(stmt: PSTMT): cint{.stdcall, dynlib: lib,
-                                     importc: "mysql_stmt_fetch".}
-proc stmt_fetch_column*(stmt: PSTMT, `bind`: PBIND, column: cuint, offset: int): cint{.
-    stdcall, dynlib: lib, importc: "mysql_stmt_fetch_column".}
-proc stmt_store_result*(stmt: PSTMT): cint{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_store_result".}
-proc stmt_param_count*(stmt: PSTMT): int{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_param_count".}
-proc stmt_attr_set*(stmt: PSTMT, attr_type: Enum_stmt_attr_type, attr: pointer): my_bool{.
-    stdcall, dynlib: lib, importc: "mysql_stmt_attr_set".}
-proc stmt_attr_get*(stmt: PSTMT, attr_type: Enum_stmt_attr_type, attr: pointer): my_bool{.
-    stdcall, dynlib: lib, importc: "mysql_stmt_attr_get".}
-proc stmt_bind_param*(stmt: PSTMT, bnd: PBIND): my_bool{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_bind_param".}
-proc stmt_bind_result*(stmt: PSTMT, bnd: PBIND): my_bool{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_bind_result".}
-proc stmt_close*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib,
-                                        importc: "mysql_stmt_close".}
-proc stmt_reset*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib,
-                                        importc: "mysql_stmt_reset".}
-proc stmt_free_result*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_free_result".}
-proc stmt_send_long_data*(stmt: PSTMT, param_number: cuint, data: cstring,
-                          len: int): my_bool{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_send_long_data".}
-proc stmt_result_metadata*(stmt: PSTMT): PRES{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_result_metadata".}
-proc stmt_param_metadata*(stmt: PSTMT): PRES{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_param_metadata".}
-proc stmt_errno*(stmt: PSTMT): cuint{.stdcall, dynlib: lib,
-                                      importc: "mysql_stmt_errno".}
-proc stmt_error*(stmt: PSTMT): cstring{.stdcall, dynlib: lib,
-                                        importc: "mysql_stmt_error".}
-proc stmt_sqlstate*(stmt: PSTMT): cstring{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_sqlstate".}
-proc stmt_row_seek*(stmt: PSTMT, offset: ROW_OFFSET): ROW_OFFSET{.stdcall,
-    dynlib: lib, importc: "mysql_stmt_row_seek".}
-proc stmt_row_tell*(stmt: PSTMT): ROW_OFFSET{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_row_tell".}
-proc stmt_data_seek*(stmt: PSTMT, offset: my_ulonglong){.stdcall, dynlib: lib,
-    importc: "mysql_stmt_data_seek".}
-proc stmt_num_rows*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_num_rows".}
-proc stmt_affected_rows*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_affected_rows".}
-proc stmt_insert_id*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_insert_id".}
-proc stmt_field_count*(stmt: PSTMT): cuint{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_field_count".}
-proc commit*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_commit".}
-proc rollback*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_rollback".}
-proc autocommit*(MySQL: PMySQL, auto_mode: my_bool): my_bool{.stdcall, dynlib: lib,
-    importc: "mysql_autocommit".}
-proc more_results*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib,
-                                  importc: "mysql_more_results".}
-proc next_result*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, importc: "mysql_next_result".}
-proc close*(sock: PMySQL){.stdcall, dynlib: lib, importc: "mysql_close".}
-  # status return codes
-const
-  NO_DATA* = 100
-  DATA_TRUNCATED* = 101
-
-proc reload*(x: PMySQL): cint
-when defined(USE_OLD_FUNCTIONS):
-  proc connect*(MySQL: PMySQL, host: cstring, user: cstring, passwd: cstring): PMySQL{.stdcall,
-      dynlib: lib, importc: "mysql_connect".}
-  proc create_db*(MySQL: PMySQL, DB: cstring): cint{.stdcall, dynlib: lib,
-      importc: "mysql_create_db".}
-  proc drop_db*(MySQL: PMySQL, DB: cstring): cint{.stdcall, dynlib: lib,
-      importc: "mysql_drop_db".}
-proc net_safe_read*(MySQL: PMySQL): cuint{.cdecl, dynlib: lib, importc: "net_safe_read".}
-
-proc IS_PRI_KEY(n: int32): bool =
-  result = (n and PRI_KEY_FLAG) != 0
-
-proc IS_NOT_NULL(n: int32): bool =
-  result = (n and NOT_NULL_FLAG) != 0
-
-proc IS_BLOB(n: int32): bool =
-  result = (n and BLOB_FLAG) != 0
-
-proc IS_NUM_FIELD(f: Pst_mysql_field): bool =
-  result = (f.flags and NUM_FLAG) != 0
-
-proc IS_NUM(t: Enum_field_types): bool =
-  result = (t <= FIELD_TYPE_INT24) or (t == FIELD_TYPE_YEAR) or
-      (t == FIELD_TYPE_NEWDECIMAL)
-
-proc INTERNAL_NUM_FIELD(f: Pst_mysql_field): bool =
-  result = (f.ftype <= FIELD_TYPE_INT24) and
-      ((f.ftype != FIELD_TYPE_TIMESTAMP) or (f.len == 14) or (f.len == 8)) or
-      (f.ftype == FIELD_TYPE_YEAR)
-
-proc reload(x: PMySQL): cint =
-  result = refresh(x, REFRESH_GRANT)
-
-{.pop.}
diff --git a/lib/wrappers/odbcsql.nim b/lib/wrappers/odbcsql.nim
deleted file mode 100644
index 43d1c504d..000000000
--- a/lib/wrappers/odbcsql.nim
+++ /dev/null
@@ -1,847 +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.
-#
-
-{.deadCodeElim: on.}  # dce option deprecated
-
-when not defined(ODBCVER):
-  const
-    ODBCVER = 0x0351 ## define ODBC version 3.51 by default
-
-when defined(windows):
-  {.push callconv: stdcall.}
-  const odbclib = "odbc32.dll"
-else:
-  {.push callconv: cdecl.}
-  const odbclib = "libodbc.so"
-
-# DATA TYPES CORRESPONDENCE
-#   BDE fields  ODBC types
-#   ----------  ------------------
-#   ftBlob      SQL_BINARY
-#   ftBoolean   SQL_BIT
-#   ftDate      SQL_TYPE_DATE
-#   ftTime      SQL_TYPE_TIME
-#   ftDateTime  SQL_TYPE_TIMESTAMP
-#   ftInteger   SQL_INTEGER
-#   ftSmallint  SQL_SMALLINT
-#   ftFloat     SQL_DOUBLE
-#   ftString    SQL_CHAR
-#   ftMemo      SQL_BINARY // SQL_VARCHAR
-#
-
-type
-  TSqlChar* = char
-  TSqlSmallInt* = int16
-  SqlUSmallInt* = int16
-  SqlHandle* = pointer
-  SqlHEnv* = SqlHandle
-  SqlHDBC* = SqlHandle
-  SqlHStmt* = SqlHandle
-  SqlHDesc* = SqlHandle
-  TSqlInteger* = int
-  SqlUInteger* = int
-  SqlPointer* = pointer
-  TSqlReal* = cfloat
-  TSqlDouble* = cdouble
-  TSqlFloat* = cdouble
-  SqlHWND* = pointer
-  PSQLCHAR* = cstring
-  PSQLINTEGER* = ptr TSqlInteger
-  PSQLUINTEGER* = ptr SqlUInteger
-  PSQLSMALLINT* = ptr TSqlSmallInt
-  PSQLUSMALLINT* = ptr SqlUSmallInt
-  PSQLREAL* = ptr TSqlReal
-  PSQLDOUBLE* = ptr TSqlDouble
-  PSQLFLOAT* = ptr TSqlFloat
-  PSQLHANDLE* = ptr SqlHandle
-{.deprecated: [
-    # TSqlChar: TSqlChar, # Name conflict if we drop`T`
-    # TSqlSmallInt: TSqlSmallInt, # Name conflict if we drop`T`
-    TSqlUSmallInt: SqlUSmallInt, TSqlHandle: SqlHandle, TSqlHEnv: SqlHEnv,
-    TSqlHDBC: SqlHDBC, TSqlHStmt: SqlHStmt, TSqlHDesc: SqlHDesc,
-    # TSqlInteger: TSqlInteger, # Name conflict if we drop `T`
-    TSqlUInteger: SqlUInteger, TSqlPointer: SqlPointer,
-    # TSqlReal: TSqlReal, # Name conflict if we drop`T`
-    # TSqlDouble: TSqlDouble, # Name conflict if we drop`T`
-    # TSqlFloat: TSqlFloat, # Name conflict if we drop `T`
-    TSqlHWND: SqlHWND].}
-
-const                         # SQL data type codes
-  SQL_UNKNOWN_TYPE* = 0
-  SQL_LONGVARCHAR* = (- 1)
-  SQL_BINARY* = (- 2)
-  SQL_VARBINARY* = (- 3)
-  SQL_LONGVARBINARY* = (- 4)
-  SQL_BIGINT* = (- 5)
-  SQL_TINYINT* = (- 6)
-  SQL_BIT* = (- 7)
-  SQL_WCHAR* = (- 8)
-  SQL_WVARCHAR* = (- 9)
-  SQL_WLONGVARCHAR* = (- 10)
-  SQL_CHAR* = 1
-  SQL_NUMERIC* = 2
-  SQL_DECIMAL* = 3
-  SQL_INTEGER* = 4
-  SQL_SMALLINT* = 5
-  SQL_FLOAT* = 6
-  SQL_REAL* = 7
-  SQL_DOUBLE* = 8
-  SQL_DATETIME* = 9
-  SQL_VARCHAR* = 12
-  SQL_TYPE_DATE* = 91
-  SQL_TYPE_TIME* = 92
-  SQL_TYPE_TIMESTAMP* = 93
-  SQL_DATE* = 9
-  SQL_TIME* = 10
-  SQL_TIMESTAMP* = 11
-  SQL_INTERVAL* = 10
-  SQL_GUID* = - 11            # interval codes
-
-when ODBCVER >= 0x0300:
-  const
-    SQL_CODE_YEAR* = 1
-    SQL_CODE_MONTH* = 2
-    SQL_CODE_DAY* = 3
-    SQL_CODE_HOUR* = 4
-    SQL_CODE_MINUTE* = 5
-    SQL_CODE_SECOND* = 6
-    SQL_CODE_YEAR_TO_MONTH* = 7
-    SQL_CODE_DAY_TO_HOUR* = 8
-    SQL_CODE_DAY_TO_MINUTE* = 9
-    SQL_CODE_DAY_TO_SECOND* = 10
-    SQL_CODE_HOUR_TO_MINUTE* = 11
-    SQL_CODE_HOUR_TO_SECOND* = 12
-    SQL_CODE_MINUTE_TO_SECOND* = 13
-    SQL_INTERVAL_YEAR* = 100 + SQL_CODE_YEAR
-    SQL_INTERVAL_MONTH* = 100 + SQL_CODE_MONTH
-    SQL_INTERVAL_DAY* = 100 + SQL_CODE_DAY
-    SQL_INTERVAL_HOUR* = 100 + SQL_CODE_HOUR
-    SQL_INTERVAL_MINUTE* = 100 + SQL_CODE_MINUTE
-    SQL_INTERVAL_SECOND* = 100 + SQL_CODE_SECOND
-    SQL_INTERVAL_YEAR_TO_MONTH* = 100 + SQL_CODE_YEAR_TO_MONTH
-    SQL_INTERVAL_DAY_TO_HOUR* = 100 + SQL_CODE_DAY_TO_HOUR
-    SQL_INTERVAL_DAY_TO_MINUTE* = 100 + SQL_CODE_DAY_TO_MINUTE
-    SQL_INTERVAL_DAY_TO_SECOND* = 100 + SQL_CODE_DAY_TO_SECOND
-    SQL_INTERVAL_HOUR_TO_MINUTE* = 100 + SQL_CODE_HOUR_TO_MINUTE
-    SQL_INTERVAL_HOUR_TO_SECOND* = 100 + SQL_CODE_HOUR_TO_SECOND
-    SQL_INTERVAL_MINUTE_TO_SECOND* = 100 + SQL_CODE_MINUTE_TO_SECOND
-else:
-  const
-    SQL_INTERVAL_YEAR* = - 80
-    SQL_INTERVAL_MONTH* = - 81
-    SQL_INTERVAL_YEAR_TO_MONTH* = - 82
-    SQL_INTERVAL_DAY* = - 83
-    SQL_INTERVAL_HOUR* = - 84
-    SQL_INTERVAL_MINUTE* = - 85
-    SQL_INTERVAL_SECOND* = - 86
-    SQL_INTERVAL_DAY_TO_HOUR* = - 87
-    SQL_INTERVAL_DAY_TO_MINUTE* = - 88
-    SQL_INTERVAL_DAY_TO_SECOND* = - 89
-    SQL_INTERVAL_HOUR_TO_MINUTE* = - 90
-    SQL_INTERVAL_HOUR_TO_SECOND* = - 91
-    SQL_INTERVAL_MINUTE_TO_SECOND* = - 92
-
-
-when ODBCVER < 0x0300:
-  const
-    SQL_UNICODE* = - 95
-    SQL_UNICODE_VARCHAR* = - 96
-    SQL_UNICODE_LONGVARCHAR* = - 97
-    SQL_UNICODE_CHAR* = SQL_UNICODE
-else:
-  # The previous definitions for SQL_UNICODE_ are historical and obsolete
-  const
-    SQL_UNICODE* = SQL_WCHAR
-    SQL_UNICODE_VARCHAR* = SQL_WVARCHAR
-    SQL_UNICODE_LONGVARCHAR* = SQL_WLONGVARCHAR
-    SQL_UNICODE_CHAR* = SQL_WCHAR
-const                         # C datatype to SQL datatype mapping
-  SQL_C_CHAR* = SQL_CHAR
-  SQL_C_LONG* = SQL_INTEGER
-  SQL_C_SHORT* = SQL_SMALLINT
-  SQL_C_FLOAT* = SQL_REAL
-  SQL_C_DOUBLE* = SQL_DOUBLE
-  SQL_C_NUMERIC* = SQL_NUMERIC
-  SQL_C_DEFAULT* = 99
-  SQL_SIGNED_OFFSET* = - 20
-  SQL_UNSIGNED_OFFSET* = - 22
-  SQL_C_DATE* = SQL_DATE
-  SQL_C_TIME* = SQL_TIME
-  SQL_C_TIMESTAMP* = SQL_TIMESTAMP
-  SQL_C_TYPE_DATE* = SQL_TYPE_DATE
-  SQL_C_TYPE_TIME* = SQL_TYPE_TIME
-  SQL_C_TYPE_TIMESTAMP* = SQL_TYPE_TIMESTAMP
-  SQL_C_INTERVAL_YEAR* = SQL_INTERVAL_YEAR
-  SQL_C_INTERVAL_MONTH* = SQL_INTERVAL_MONTH
-  SQL_C_INTERVAL_DAY* = SQL_INTERVAL_DAY
-  SQL_C_INTERVAL_HOUR* = SQL_INTERVAL_HOUR
-  SQL_C_INTERVAL_MINUTE* = SQL_INTERVAL_MINUTE
-  SQL_C_INTERVAL_SECOND* = SQL_INTERVAL_SECOND
-  SQL_C_INTERVAL_YEAR_TO_MONTH* = SQL_INTERVAL_YEAR_TO_MONTH
-  SQL_C_INTERVAL_DAY_TO_HOUR* = SQL_INTERVAL_DAY_TO_HOUR
-  SQL_C_INTERVAL_DAY_TO_MINUTE* = SQL_INTERVAL_DAY_TO_MINUTE
-  SQL_C_INTERVAL_DAY_TO_SECOND* = SQL_INTERVAL_DAY_TO_SECOND
-  SQL_C_INTERVAL_HOUR_TO_MINUTE* = SQL_INTERVAL_HOUR_TO_MINUTE
-  SQL_C_INTERVAL_HOUR_TO_SECOND* = SQL_INTERVAL_HOUR_TO_SECOND
-  SQL_C_INTERVAL_MINUTE_TO_SECOND* = SQL_INTERVAL_MINUTE_TO_SECOND
-  SQL_C_BINARY* = SQL_BINARY
-  SQL_C_BIT* = SQL_BIT
-  SQL_C_SBIGINT* = SQL_BIGINT + SQL_SIGNED_OFFSET # SIGNED BIGINT
-  SQL_C_UBIGINT* = SQL_BIGINT + SQL_UNSIGNED_OFFSET # UNSIGNED BIGINT
-  SQL_C_TINYINT* = SQL_TINYINT
-  SQL_C_SLONG* = SQL_C_LONG + SQL_SIGNED_OFFSET # SIGNED INTEGER
-  SQL_C_SSHORT* = SQL_C_SHORT + SQL_SIGNED_OFFSET # SIGNED SMALLINT
-  SQL_C_STINYINT* = SQL_TINYINT + SQL_SIGNED_OFFSET # SIGNED TINYINT
-  SQL_C_ULONG* = SQL_C_LONG + SQL_UNSIGNED_OFFSET # UNSIGNED INTEGER
-  SQL_C_USHORT* = SQL_C_SHORT + SQL_UNSIGNED_OFFSET # UNSIGNED SMALLINT
-  SQL_C_UTINYINT* = SQL_TINYINT + SQL_UNSIGNED_OFFSET # UNSIGNED TINYINT
-  SQL_C_BOOKMARK* = SQL_C_ULONG # BOOKMARK
-  SQL_C_GUID* = SQL_GUID
-  SQL_TYPE_NULL* = 0
-
-when ODBCVER < 0x0300:
-  const
-    SQL_TYPE_MIN* = SQL_BIT
-    SQL_TYPE_MAX* = SQL_VARCHAR
-
-const
-  SQL_C_VARBOOKMARK* = SQL_C_BINARY
-  SQL_API_SQLDESCRIBEPARAM* = 58
-  SQL_NO_TOTAL* = - 4
-
-type
-  SQL_DATE_STRUCT* {.final, pure.} = object
-    Year*: TSqlSmallInt
-    Month*: SqlUSmallInt
-    Day*: SqlUSmallInt
-
-  PSQL_DATE_STRUCT* = ptr SQL_DATE_STRUCT
-  SQL_TIME_STRUCT* {.final, pure.} = object
-    Hour*: SqlUSmallInt
-    Minute*: SqlUSmallInt
-    Second*: SqlUSmallInt
-
-  PSQL_TIME_STRUCT* = ptr SQL_TIME_STRUCT
-  SQL_TIMESTAMP_STRUCT* {.final, pure.} = object
-    Year*: SqlUSmallInt
-    Month*: SqlUSmallInt
-    Day*: SqlUSmallInt
-    Hour*: SqlUSmallInt
-    Minute*: SqlUSmallInt
-    Second*: SqlUSmallInt
-    Fraction*: SqlUInteger
-
-  PSQL_TIMESTAMP_STRUCT* = ptr SQL_TIMESTAMP_STRUCT
-
-const
-  SQL_NAME_LEN* = 128
-  SQL_OV_ODBC3* = 3
-  SQL_OV_ODBC2* = 2
-  SQL_ATTR_ODBC_VERSION* = 200 # Options for SQLDriverConnect
-  SQL_DRIVER_NOPROMPT* = 0
-  SQL_DRIVER_COMPLETE* = 1
-  SQL_DRIVER_PROMPT* = 2
-  SQL_DRIVER_COMPLETE_REQUIRED* = 3
-  SQL_IS_POINTER* = (- 4)  # whether an attribute is a pointer or not
-  SQL_IS_UINTEGER* = (- 5)
-  SQL_IS_INTEGER* = (- 6)
-  SQL_IS_USMALLINT* = (- 7)
-  SQL_IS_SMALLINT* = (- 8)    # SQLExtendedFetch "fFetchType" values
-  SQL_FETCH_BOOKMARK* = 8
-  SQL_SCROLL_OPTIONS* = 44    # SQL_USE_BOOKMARKS options
-  SQL_UB_OFF* = 0
-  SQL_UB_ON* = 1
-  SQL_UB_DEFAULT* = SQL_UB_OFF
-  SQL_UB_FIXED* = SQL_UB_ON
-  SQL_UB_VARIABLE* = 2        # SQL_SCROLL_OPTIONS masks
-  SQL_SO_FORWARD_ONLY* = 0x00000001
-  SQL_SO_KEYSET_DRIVEN* = 0x00000002
-  SQL_SO_DYNAMIC* = 0x00000004
-  SQL_SO_MIXED* = 0x00000008
-  SQL_SO_STATIC* = 0x00000010
-  SQL_BOOKMARK_PERSISTENCE* = 82
-  SQL_STATIC_SENSITIVITY* = 83 # SQL_BOOKMARK_PERSISTENCE values
-  SQL_BP_CLOSE* = 0x00000001
-  SQL_BP_DELETE* = 0x00000002
-  SQL_BP_DROP* = 0x00000004
-  SQL_BP_TRANSACTION* = 0x00000008
-  SQL_BP_UPDATE* = 0x00000010
-  SQL_BP_OTHER_HSTMT* = 0x00000020
-  SQL_BP_SCROLL* = 0x00000040
-  SQL_DYNAMIC_CURSOR_ATTRIBUTES1* = 144
-  SQL_DYNAMIC_CURSOR_ATTRIBUTES2* = 145
-  SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1* = 146
-  SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2* = 147
-  SQL_INDEX_KEYWORDS* = 148
-  SQL_INFO_SCHEMA_VIEWS* = 149
-  SQL_KEYSET_CURSOR_ATTRIBUTES1* = 150
-  SQL_KEYSET_CURSOR_ATTRIBUTES2* = 151
-  SQL_STATIC_CURSOR_ATTRIBUTES1* = 167
-  SQL_STATIC_CURSOR_ATTRIBUTES2* = 168 # supported SQLFetchScroll FetchOrientation's
-  SQL_CA1_NEXT* = 1
-  SQL_CA1_ABSOLUTE* = 2
-  SQL_CA1_RELATIVE* = 4
-  SQL_CA1_BOOKMARK* = 8       # supported SQLSetPos LockType's
-  SQL_CA1_LOCK_NO_CHANGE* = 0x00000040
-  SQL_CA1_LOCK_EXCLUSIVE* = 0x00000080
-  SQL_CA1_LOCK_UNLOCK* = 0x00000100 # supported SQLSetPos Operations
-  SQL_CA1_POS_POSITION* = 0x00000200
-  SQL_CA1_POS_UPDATE* = 0x00000400
-  SQL_CA1_POS_DELETE* = 0x00000800
-  SQL_CA1_POS_REFRESH* = 0x00001000 # positioned updates and deletes
-  SQL_CA1_POSITIONED_UPDATE* = 0x00002000
-  SQL_CA1_POSITIONED_DELETE* = 0x00004000
-  SQL_CA1_SELECT_FOR_UPDATE* = 0x00008000 # supported SQLBulkOperations operations
-  SQL_CA1_BULK_ADD* = 0x00010000
-  SQL_CA1_BULK_UPDATE_BY_BOOKMARK* = 0x00020000
-  SQL_CA1_BULK_DELETE_BY_BOOKMARK* = 0x00040000
-  SQL_CA1_BULK_FETCH_BY_BOOKMARK* = 0x00080000 # supported values for SQL_ATTR_SCROLL_CONCURRENCY
-  SQL_CA2_READ_ONLY_CONCURRENCY* = 1
-  SQL_CA2_LOCK_CONCURRENCY* = 2
-  SQL_CA2_OPT_ROWVER_CONCURRENCY* = 4
-  SQL_CA2_OPT_VALUES_CONCURRENCY* = 8 # sensitivity of the cursor to its own inserts, deletes, and updates
-  SQL_CA2_SENSITIVITY_ADDITIONS* = 0x00000010
-  SQL_CA2_SENSITIVITY_DELETIONS* = 0x00000020
-  SQL_CA2_SENSITIVITY_UPDATES* = 0x00000040 #  semantics of SQL_ATTR_MAX_ROWS
-  SQL_CA2_MAX_ROWS_SELECT* = 0x00000080
-  SQL_CA2_MAX_ROWS_INSERT* = 0x00000100
-  SQL_CA2_MAX_ROWS_DELETE* = 0x00000200
-  SQL_CA2_MAX_ROWS_UPDATE* = 0x00000400
-  SQL_CA2_MAX_ROWS_CATALOG* = 0x00000800
-  SQL_CA2_MAX_ROWS_AFFECTS_ALL* = (SQL_CA2_MAX_ROWS_SELECT or
-      SQL_CA2_MAX_ROWS_INSERT or SQL_CA2_MAX_ROWS_DELETE or
-      SQL_CA2_MAX_ROWS_UPDATE or SQL_CA2_MAX_ROWS_CATALOG) # semantics of
-                                                           # SQL_DIAG_CURSOR_ROW_COUNT
-  SQL_CA2_CRC_EXACT* = 0x00001000
-  SQL_CA2_CRC_APPROXIMATE* = 0x00002000 #  the kinds of positioned statements that can be simulated
-  SQL_CA2_SIMULATE_NON_UNIQUE* = 0x00004000
-  SQL_CA2_SIMULATE_TRY_UNIQUE* = 0x00008000
-  SQL_CA2_SIMULATE_UNIQUE* = 0x00010000 #  Operations in SQLBulkOperations
-  SQL_ADD* = 4
-  SQL_SETPOS_MAX_OPTION_VALUE* = SQL_ADD
-  SQL_UPDATE_BY_BOOKMARK* = 5
-  SQL_DELETE_BY_BOOKMARK* = 6
-  SQL_FETCH_BY_BOOKMARK* = 7  # Operations in SQLSetPos
-  SQL_POSITION* = 0
-  SQL_REFRESH* = 1
-  SQL_UPDATE* = 2
-  SQL_DELETE* = 3             # Lock options in SQLSetPos
-  SQL_LOCK_NO_CHANGE* = 0
-  SQL_LOCK_EXCLUSIVE* = 1
-  SQL_LOCK_UNLOCK* = 2        # SQLExtendedFetch "rgfRowStatus" element values
-  SQL_ROW_SUCCESS* = 0
-  SQL_ROW_DELETED* = 1
-  SQL_ROW_UPDATED* = 2
-  SQL_ROW_NOROW* = 3
-  SQL_ROW_ADDED* = 4
-  SQL_ROW_ERROR* = 5
-  SQL_ROW_SUCCESS_WITH_INFO* = 6
-  SQL_ROW_PROCEED* = 0
-  SQL_ROW_IGNORE* = 1
-  SQL_MAX_DSN_LENGTH* = 32    # maximum data source name size
-  SQL_MAX_OPTION_STRING_LENGTH* = 256
-  SQL_ODBC_CURSORS* = 110
-  SQL_ATTR_ODBC_CURSORS* = SQL_ODBC_CURSORS # SQL_ODBC_CURSORS options
-  SQL_CUR_USE_IF_NEEDED* = 0
-  SQL_CUR_USE_ODBC* = 1
-  SQL_CUR_USE_DRIVER* = 2
-  SQL_CUR_DEFAULT* = SQL_CUR_USE_DRIVER
-  SQL_PARAM_TYPE_UNKNOWN* = 0
-  SQL_PARAM_INPUT* = 1
-  SQL_PARAM_INPUT_OUTPUT* = 2
-  SQL_RESULT_COL* = 3
-  SQL_PARAM_OUTPUT* = 4
-  SQL_RETURN_VALUE* = 5       # special length/indicator values
-  SQL_NULL_DATA* = (- 1)
-  SQL_DATA_AT_EXEC* = (- 2)
-  SQL_SUCCESS* = 0
-  SQL_SUCCESS_WITH_INFO* = 1
-  SQL_NO_DATA* = 100
-  SQL_ERROR* = (- 1)
-  SQL_INVALID_HANDLE* = (- 2)
-  SQL_STILL_EXECUTING* = 2
-  SQL_NEED_DATA* = 99         # flags for null-terminated string
-  SQL_NTS* = (- 3)            # maximum message length
-  SQL_MAX_MESSAGE_LENGTH* = 512 # date/time length constants
-  SQL_DATE_LEN* = 10
-  SQL_TIME_LEN* = 8           # add P+1 if precision is nonzero
-  SQL_TIMESTAMP_LEN* = 19     # add P+1 if precision is nonzero
-                              # handle type identifiers
-  SQL_HANDLE_ENV* = 1
-  SQL_HANDLE_DBC* = 2
-  SQL_HANDLE_STMT* = 3
-  SQL_HANDLE_DESC* = 4        # environment attribute
-  SQL_ATTR_OUTPUT_NTS* = 10001 # connection attributes
-  SQL_ATTR_AUTO_IPD* = 10001
-  SQL_ATTR_METADATA_ID* = 10014 # statement attributes
-  SQL_ATTR_APP_ROW_DESC* = 10010
-  SQL_ATTR_APP_PARAM_DESC* = 10011
-  SQL_ATTR_IMP_ROW_DESC* = 10012
-  SQL_ATTR_IMP_PARAM_DESC* = 10013
-  SQL_ATTR_CURSOR_SCROLLABLE* = (- 1)
-  SQL_ATTR_CURSOR_SENSITIVITY* = (- 2)
-  SQL_QUERY_TIMEOUT* = 0
-  SQL_MAX_ROWS* = 1
-  SQL_NOSCAN* = 2
-  SQL_MAX_LENGTH* = 3
-  SQL_ASYNC_ENABLE* = 4       # same as SQL_ATTR_ASYNC_ENABLE */
-  SQL_BIND_TYPE* = 5
-  SQL_CURSOR_TYPE* = 6
-  SQL_CONCURRENCY* = 7
-  SQL_KEYSET_SIZE* = 8
-  SQL_ROWSET_SIZE* = 9
-  SQL_SIMULATE_CURSOR* = 10
-  SQL_RETRIEVE_DATA* = 11
-  SQL_USE_BOOKMARKS* = 12
-  SQL_GET_BOOKMARK* = 13      #      GetStmtOption Only */
-  SQL_ROW_NUMBER* = 14        #      GetStmtOption Only */
-  SQL_ATTR_CURSOR_TYPE* = SQL_CURSOR_TYPE
-  SQL_ATTR_CONCURRENCY* = SQL_CONCURRENCY
-  SQL_ATTR_FETCH_BOOKMARK_PTR* = 16
-  SQL_ATTR_ROW_STATUS_PTR* = 25
-  SQL_ATTR_ROWS_FETCHED_PTR* = 26
-  SQL_AUTOCOMMIT* = 102
-  SQL_ATTR_AUTOCOMMIT* = SQL_AUTOCOMMIT
-  SQL_ATTR_ROW_NUMBER* = SQL_ROW_NUMBER
-  SQL_TXN_ISOLATION* = 108
-  SQL_ATTR_TXN_ISOLATION* = SQL_TXN_ISOLATION
-  SQL_ATTR_MAX_ROWS* = SQL_MAX_ROWS
-  SQL_ATTR_USE_BOOKMARKS* = SQL_USE_BOOKMARKS #* connection attributes */
-  SQL_ACCESS_MODE* = 101      #  SQL_AUTOCOMMIT              =102;
-  SQL_LOGIN_TIMEOUT* = 103
-  SQL_OPT_TRACE* = 104
-  SQL_OPT_TRACEFILE* = 105
-  SQL_TRANSLATE_DLL* = 106
-  SQL_TRANSLATE_OPTION* = 107 #  SQL_TXN_ISOLATION           =108;
-  SQL_CURRENT_QUALIFIER* = 109 #  SQL_ODBC_CURSORS            =110;
-  SQL_QUIET_MODE* = 111
-  SQL_PACKET_SIZE* = 112      #* connection attributes with new names */
-  SQL_ATTR_ACCESS_MODE* = SQL_ACCESS_MODE #  SQL_ATTR_AUTOCOMMIT                       =SQL_AUTOCOMMIT;
-  SQL_ATTR_CONNECTION_DEAD* = 1209 #* GetConnectAttr only */
-  SQL_ATTR_CONNECTION_TIMEOUT* = 113
-  SQL_ATTR_CURRENT_CATALOG* = SQL_CURRENT_QUALIFIER
-  SQL_ATTR_DISCONNECT_BEHAVIOR* = 114
-  SQL_ATTR_ENLIST_IN_DTC* = 1207
-  SQL_ATTR_ENLIST_IN_XA* = 1208
-  SQL_ATTR_LOGIN_TIMEOUT* = SQL_LOGIN_TIMEOUT #  SQL_ATTR_ODBC_CURSORS             =SQL_ODBC_CURSORS;
-  SQL_ATTR_PACKET_SIZE* = SQL_PACKET_SIZE
-  SQL_ATTR_QUIET_MODE* = SQL_QUIET_MODE
-  SQL_ATTR_TRACE* = SQL_OPT_TRACE
-  SQL_ATTR_TRACEFILE* = SQL_OPT_TRACEFILE
-  SQL_ATTR_TRANSLATE_LIB* = SQL_TRANSLATE_DLL
-  SQL_ATTR_TRANSLATE_OPTION* = SQL_TRANSLATE_OPTION #  SQL_ATTR_TXN_ISOLATION                  =SQL_TXN_ISOLATION;
-                                                    #* SQL_ACCESS_MODE options */
-  SQL_MODE_READ_WRITE* = 0
-  SQL_MODE_READ_ONLY* = 1
-  SQL_MODE_DEFAULT* = SQL_MODE_READ_WRITE #* SQL_AUTOCOMMIT options */
-  SQL_AUTOCOMMIT_OFF* = 0
-  SQL_AUTOCOMMIT_ON* = 1
-  SQL_AUTOCOMMIT_DEFAULT* = SQL_AUTOCOMMIT_ON # SQL_ATTR_CURSOR_SCROLLABLE values
-  SQL_NONSCROLLABLE* = 0
-  SQL_SCROLLABLE* = 1         # SQL_CURSOR_TYPE options
-  SQL_CURSOR_FORWARD_ONLY* = 0
-  SQL_CURSOR_KEYSET_DRIVEN* = 1
-  SQL_CURSOR_DYNAMIC* = 2
-  SQL_CURSOR_STATIC* = 3
-  SQL_CURSOR_TYPE_DEFAULT* = SQL_CURSOR_FORWARD_ONLY # Default value
-                                                     # SQL_CONCURRENCY options
-  SQL_CONCUR_READ_ONLY* = 1
-  SQL_CONCUR_LOCK* = 2
-  SQL_CONCUR_ROWVER* = 3
-  SQL_CONCUR_VALUES* = 4
-  SQL_CONCUR_DEFAULT* = SQL_CONCUR_READ_ONLY # Default value
-                                             # identifiers of fields in the SQL descriptor
-  SQL_DESC_COUNT* = 1001
-  SQL_DESC_TYPE* = 1002
-  SQL_DESC_LENGTH* = 1003
-  SQL_DESC_OCTET_LENGTH_PTR* = 1004
-  SQL_DESC_PRECISION* = 1005
-  SQL_DESC_SCALE* = 1006
-  SQL_DESC_DATETIME_INTERVAL_CODE* = 1007
-  SQL_DESC_NULLABLE* = 1008
-  SQL_DESC_INDICATOR_PTR* = 1009
-  SQL_DESC_DATA_PTR* = 1010
-  SQL_DESC_NAME* = 1011
-  SQL_DESC_UNNAMED* = 1012
-  SQL_DESC_OCTET_LENGTH* = 1013
-  SQL_DESC_ALLOC_TYPE* = 1099 # identifiers of fields in the diagnostics area
-  SQL_DIAG_RETURNCODE* = 1
-  SQL_DIAG_NUMBER* = 2
-  SQL_DIAG_ROW_COUNT* = 3
-  SQL_DIAG_SQLSTATE* = 4
-  SQL_DIAG_NATIVE* = 5
-  SQL_DIAG_MESSAGE_TEXT* = 6
-  SQL_DIAG_DYNAMIC_FUNCTION* = 7
-  SQL_DIAG_CLASS_ORIGIN* = 8
-  SQL_DIAG_SUBCLASS_ORIGIN* = 9
-  SQL_DIAG_CONNECTION_NAME* = 10
-  SQL_DIAG_SERVER_NAME* = 11
-  SQL_DIAG_DYNAMIC_FUNCTION_CODE* = 12 # dynamic function codes
-  SQL_DIAG_ALTER_TABLE* = 4
-  SQL_DIAG_CREATE_INDEX* = (- 1)
-  SQL_DIAG_CREATE_TABLE* = 77
-  SQL_DIAG_CREATE_VIEW* = 84
-  SQL_DIAG_DELETE_WHERE* = 19
-  SQL_DIAG_DROP_INDEX* = (- 2)
-  SQL_DIAG_DROP_TABLE* = 32
-  SQL_DIAG_DROP_VIEW* = 36
-  SQL_DIAG_DYNAMIC_DELETE_CURSOR* = 38
-  SQL_DIAG_DYNAMIC_UPDATE_CURSOR* = 81
-  SQL_DIAG_GRANT* = 48
-  SQL_DIAG_INSERT* = 50
-  SQL_DIAG_REVOKE* = 59
-  SQL_DIAG_SELECT_CURSOR* = 85
-  SQL_DIAG_UNKNOWN_STATEMENT* = 0
-  SQL_DIAG_UPDATE_WHERE* = 82 # Statement attribute values for cursor sensitivity
-  SQL_UNSPECIFIED* = 0
-  SQL_INSENSITIVE* = 1
-  SQL_SENSITIVE* = 2          # GetTypeInfo() request for all data types
-  SQL_ALL_TYPES* = 0          # Default conversion code for SQLBindCol(), SQLBindParam() and SQLGetData()
-  SQL_DEFAULT* = 99 # SQLGetData() code indicating that the application row descriptor
-                    #    specifies the data type
-  SQL_ARD_TYPE* = (- 99)      # SQL date/time type subcodes
-  SQL_CODE_DATE* = 1
-  SQL_CODE_TIME* = 2
-  SQL_CODE_TIMESTAMP* = 3     # CLI option values
-  SQL_FALSE* = 0
-  SQL_TRUE* = 1               # values of NULLABLE field in descriptor
-  SQL_NO_NULLS* = 0
-  SQL_NULLABLE* = 1 # Value returned by SQLGetTypeInfo() to denote that it is
-                    # not known whether or not a data type supports null values.
-  SQL_NULLABLE_UNKNOWN* = 2
-  SQL_CLOSE* = 0
-  SQL_DROP* = 1
-  SQL_UNBIND* = 2
-  SQL_RESET_PARAMS* = 3 # Codes used for FetchOrientation in SQLFetchScroll(),
-                        #   and in SQLDataSources()
-  SQL_FETCH_NEXT* = 1
-  SQL_FETCH_FIRST* = 2
-  SQL_FETCH_FIRST_USER* = 31
-  SQL_FETCH_FIRST_SYSTEM* = 32 # Other codes used for FetchOrientation in SQLFetchScroll()
-  SQL_FETCH_LAST* = 3
-  SQL_FETCH_PRIOR* = 4
-  SQL_FETCH_ABSOLUTE* = 5
-  SQL_FETCH_RELATIVE* = 6
-  SQL_NULL_HENV* = SqlHEnv(nil)
-  SQL_NULL_HDBC* = SqlHDBC(nil)
-  SQL_NULL_HSTMT* = SqlHStmt(nil)
-  SQL_NULL_HDESC* = SqlHDesc(nil) #* null handle used in place of parent handle when allocating HENV */
-  SQL_NULL_HANDLE* = SqlHandle(nil) #* Values that may appear in the result set of SQLSpecialColumns() */
-  SQL_SCOPE_CURROW* = 0
-  SQL_SCOPE_TRANSACTION* = 1
-  SQL_SCOPE_SESSION* = 2      #* Column types and scopes in SQLSpecialColumns.  */
-  SQL_BEST_ROWID* = 1
-  SQL_ROWVER* = 2
-  SQL_ROW_IDENTIFIER* = 1     #* Reserved values for UNIQUE argument of SQLStatistics() */
-  SQL_INDEX_UNIQUE* = 0
-  SQL_INDEX_ALL* = 1          #* Reserved values for RESERVED argument of SQLStatistics() */
-  SQL_QUICK* = 0
-  SQL_ENSURE* = 1             #* Values that may appear in the result set of SQLStatistics() */
-  SQL_TABLE_STAT* = 0
-  SQL_INDEX_CLUSTERED* = 1
-  SQL_INDEX_HASHED* = 2
-  SQL_INDEX_OTHER* = 3
-  SQL_SCROLL_CONCURRENCY* = 43
-  SQL_TXN_CAPABLE* = 46
-  SQL_TRANSACTION_CAPABLE* = SQL_TXN_CAPABLE
-  SQL_USER_NAME* = 47
-  SQL_TXN_ISOLATION_OPTION* = 72
-  SQL_TRANSACTION_ISOLATION_OPTION* = SQL_TXN_ISOLATION_OPTION
-  SQL_OJ_CAPABILITIES* = 115
-  SQL_OUTER_JOIN_CAPABILITIES* = SQL_OJ_CAPABILITIES
-  SQL_XOPEN_CLI_YEAR* = 10000
-  SQL_CURSOR_SENSITIVITY* = 10001
-  SQL_DESCRIBE_PARAMETER* = 10002
-  SQL_CATALOG_NAME* = 10003
-  SQL_COLLATION_SEQ* = 10004
-  SQL_MAX_IDENTIFIER_LEN* = 10005
-  SQL_MAXIMUM_IDENTIFIER_LENGTH* = SQL_MAX_IDENTIFIER_LEN
-  SQL_SCCO_READ_ONLY* = 1
-  SQL_SCCO_LOCK* = 2
-  SQL_SCCO_OPT_ROWVER* = 4
-  SQL_SCCO_OPT_VALUES* = 8    #* SQL_TXN_CAPABLE values */
-  SQL_TC_NONE* = 0
-  SQL_TC_DML* = 1
-  SQL_TC_ALL* = 2
-  SQL_TC_DDL_COMMIT* = 3
-  SQL_TC_DDL_IGNORE* = 4      #* SQL_TXN_ISOLATION_OPTION bitmasks */
-  SQL_TXN_READ_UNCOMMITTED* = 1
-  SQL_TRANSACTION_READ_UNCOMMITTED* = SQL_TXN_READ_UNCOMMITTED
-  SQL_TXN_READ_COMMITTED* = 2
-  SQL_TRANSACTION_READ_COMMITTED* = SQL_TXN_READ_COMMITTED
-  SQL_TXN_REPEATABLE_READ* = 4
-  SQL_TRANSACTION_REPEATABLE_READ* = SQL_TXN_REPEATABLE_READ
-  SQL_TXN_SERIALIZABLE* = 8
-  SQL_TRANSACTION_SERIALIZABLE* = SQL_TXN_SERIALIZABLE
-  SQL_SS_ADDITIONS* = 1
-  SQL_SS_DELETIONS* = 2
-  SQL_SS_UPDATES* = 4         # SQLColAttributes defines
-  SQL_COLUMN_COUNT* = 0
-  SQL_COLUMN_NAME* = 1
-  SQL_COLUMN_TYPE* = 2
-  SQL_COLUMN_LENGTH* = 3
-  SQL_COLUMN_PRECISION* = 4
-  SQL_COLUMN_SCALE* = 5
-  SQL_COLUMN_DISPLAY_SIZE* = 6
-  SQL_COLUMN_NULLABLE* = 7
-  SQL_COLUMN_UNSIGNED* = 8
-  SQL_COLUMN_MONEY* = 9
-  SQL_COLUMN_UPDATABLE* = 10
-  SQL_COLUMN_AUTO_INCREMENT* = 11
-  SQL_COLUMN_CASE_SENSITIVE* = 12
-  SQL_COLUMN_SEARCHABLE* = 13
-  SQL_COLUMN_TYPE_NAME* = 14
-  SQL_COLUMN_TABLE_NAME* = 15
-  SQL_COLUMN_OWNER_NAME* = 16
-  SQL_COLUMN_QUALIFIER_NAME* = 17
-  SQL_COLUMN_LABEL* = 18
-  SQL_COLATT_OPT_MAX* = SQL_COLUMN_LABEL
-  SQL_COLUMN_DRIVER_START* = 1000
-  SQL_DESC_ARRAY_SIZE* = 20
-  SQL_DESC_ARRAY_STATUS_PTR* = 21
-  SQL_DESC_AUTO_UNIQUE_VALUE* = SQL_COLUMN_AUTO_INCREMENT
-  SQL_DESC_BASE_COLUMN_NAME* = 22
-  SQL_DESC_BASE_TABLE_NAME* = 23
-  SQL_DESC_BIND_OFFSET_PTR* = 24
-  SQL_DESC_BIND_TYPE* = 25
-  SQL_DESC_CASE_SENSITIVE* = SQL_COLUMN_CASE_SENSITIVE
-  SQL_DESC_CATALOG_NAME* = SQL_COLUMN_QUALIFIER_NAME
-  SQL_DESC_CONCISE_TYPE* = SQL_COLUMN_TYPE
-  SQL_DESC_DATETIME_INTERVAL_PRECISION* = 26
-  SQL_DESC_DISPLAY_SIZE* = SQL_COLUMN_DISPLAY_SIZE
-  SQL_DESC_FIXED_PREC_SCALE* = SQL_COLUMN_MONEY
-  SQL_DESC_LABEL* = SQL_COLUMN_LABEL
-  SQL_DESC_LITERAL_PREFIX* = 27
-  SQL_DESC_LITERAL_SUFFIX* = 28
-  SQL_DESC_LOCAL_TYPE_NAME* = 29
-  SQL_DESC_MAXIMUM_SCALE* = 30
-  SQL_DESC_MINIMUM_SCALE* = 31
-  SQL_DESC_NUM_PREC_RADIX* = 32
-  SQL_DESC_PARAMETER_TYPE* = 33
-  SQL_DESC_ROWS_PROCESSED_PTR* = 34
-  SQL_DESC_SCHEMA_NAME* = SQL_COLUMN_OWNER_NAME
-  SQL_DESC_SEARCHABLE* = SQL_COLUMN_SEARCHABLE
-  SQL_DESC_TYPE_NAME* = SQL_COLUMN_TYPE_NAME
-  SQL_DESC_TABLE_NAME* = SQL_COLUMN_TABLE_NAME
-  SQL_DESC_UNSIGNED* = SQL_COLUMN_UNSIGNED
-  SQL_DESC_UPDATABLE* = SQL_COLUMN_UPDATABLE #* SQLEndTran() options */
-  SQL_COMMIT* = 0
-  SQL_ROLLBACK* = 1
-  SQL_ATTR_ROW_ARRAY_SIZE* = 27 #* SQLConfigDataSource() options */
-  ODBC_ADD_DSN* = 1
-  ODBC_CONFIG_DSN* = 2
-  ODBC_REMOVE_DSN* = 3
-  ODBC_ADD_SYS_DSN* = 4
-  ODBC_CONFIG_SYS_DSN* = 5
-  ODBC_REMOVE_SYS_DSN* = 6
-
-  SQL_ACTIVE_CONNECTIONS* = 0   # SQLGetInfo
-  SQL_DATA_SOURCE_NAME* = 2
-  SQL_DATA_SOURCE_READ_ONLY* = 25
-  SQL_DATABASE_NAME* = 2
-  SQL_DBMS_NAME* = 17
-  SQL_DBMS_VERSION* = 18
-  SQL_DRIVER_HDBC* = 3
-  SQL_DRIVER_HENV* = 4
-  SQL_DRIVER_HSTMT* = 5
-  SQL_DRIVER_NAME* = 6
-  SQL_DRIVER_VER* = 7
-  SQL_FETCH_DIRECTION* = 8
-  SQL_ODBC_VER* = 10
-  SQL_DRIVER_ODBC_VER* = 77
-  SQL_SERVER_NAME* = 13
-  SQL_ACTIVE_ENVIRONMENTS* = 116
-  SQL_ACTIVE_STATEMENTS* = 1
-  SQL_SQL_CONFORMANCE* = 118
-  SQL_DATETIME_LITERALS* = 119
-  SQL_ASYNC_MODE* = 10021
-  SQL_BATCH_ROW_COUNT* = 120
-  SQL_BATCH_SUPPORT* = 121
-  SQL_CATALOG_LOCATION* = 114
-  #SQL_CATALOG_NAME* = 10003
-  SQL_CATALOG_NAME_SEPARATOR* = 41
-  SQL_CATALOG_TERM* = 42
-  SQL_CATALOG_USAGE* = 92
-  #SQL_COLLATION_SEQ* = 10004
-  SQL_COLUMN_ALIAS* = 87
-  #SQL_USER_NAME* = 47
-
-proc SQLAllocHandle*(HandleType: TSqlSmallInt, InputHandle: SqlHandle,
-                     OutputHandlePtr: var SqlHandle): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLSetEnvAttr*(EnvironmentHandle: SqlHEnv, Attribute: TSqlInteger,
-                    Value: TSqlInteger, StringLength: TSqlInteger): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLGetEnvAttr*(EnvironmentHandle: SqlHEnv, Attribute: TSqlInteger,
-                    Value: SqlPointer, BufferLength: TSqlInteger,
-                    StringLength: PSQLINTEGER): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLFreeHandle*(HandleType: TSqlSmallInt, Handle: SqlHandle): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLGetDiagRec*(HandleType: TSqlSmallInt, Handle: SqlHandle,
-                    RecNumber: TSqlSmallInt, Sqlstate: PSQLCHAR,
-                    NativeError: var TSqlInteger, MessageText: PSQLCHAR,
-                    BufferLength: TSqlSmallInt, TextLength: var TSqlSmallInt): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLGetDiagField*(HandleType: TSqlSmallInt, Handle: SqlHandle,
-                      RecNumber: TSqlSmallInt, DiagIdentifier: TSqlSmallInt,
-                      DiagInfoPtr: SqlPointer, BufferLength: TSqlSmallInt,
-                      StringLengthPtr: var TSqlSmallInt): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLConnect*(ConnectionHandle: SqlHDBC, ServerName: PSQLCHAR,
-                 NameLength1: TSqlSmallInt, UserName: PSQLCHAR,
-                 NameLength2: TSqlSmallInt, Authentication: PSQLCHAR,
-                 NameLength3: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLDisconnect*(ConnectionHandle: SqlHDBC): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLDriverConnect*(hdbc: SqlHDBC, hwnd: SqlHWND, szCsin: cstring,
-                       szCLen: TSqlSmallInt, szCsout: cstring,
-                       cbCSMax: TSqlSmallInt, cbCsOut: var TSqlSmallInt,
-                       f: SqlUSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLBrowseConnect*(hdbc: SqlHDBC, szConnStrIn: PSQLCHAR,
-                       cbConnStrIn: TSqlSmallInt, szConnStrOut: PSQLCHAR,
-                       cbConnStrOutMax: TSqlSmallInt,
-                       cbConnStrOut: var TSqlSmallInt): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLExecDirect*(StatementHandle: SqlHStmt, StatementText: PSQLCHAR,
-                    TextLength: TSqlInteger): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLExecDirectW*(StatementHandle: SqlHStmt, StatementText: WideCString,
-                    TextLength: TSqlInteger): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLPrepare*(StatementHandle: SqlHStmt, StatementText: PSQLCHAR,
-                 TextLength: TSqlInteger): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLPrepareW*(StatementHandle: SqlHStmt, StatementText: WideCString,
-                 TextLength: TSqlInteger): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLCloseCursor*(StatementHandle: SqlHStmt): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLExecute*(StatementHandle: SqlHStmt): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLFetch*(StatementHandle: SqlHStmt): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLNumResultCols*(StatementHandle: SqlHStmt, ColumnCount: var TSqlSmallInt): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLDescribeCol*(StatementHandle: SqlHStmt, ColumnNumber: SqlUSmallInt,
-                     ColumnName: PSQLCHAR, BufferLength: TSqlSmallInt,
-                     NameLength: var TSqlSmallInt, DataType: var TSqlSmallInt,
-                     ColumnSize: var SqlUInteger,
-                     DecimalDigits: var TSqlSmallInt, Nullable: var TSqlSmallInt): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLFetchScroll*(StatementHandle: SqlHStmt, FetchOrientation: TSqlSmallInt,
-                     FetchOffset: TSqlInteger): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLExtendedFetch*(hstmt: SqlHStmt, fFetchType: SqlUSmallInt,
-                       irow: TSqlInteger, pcrow: PSQLUINTEGER,
-                       rgfRowStatus: PSQLUSMALLINT): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLGetData*(StatementHandle: SqlHStmt, ColumnNumber: SqlUSmallInt,
-                 TargetType: TSqlSmallInt, TargetValue: SqlPointer,
-                 BufferLength: TSqlInteger, StrLen_or_Ind: PSQLINTEGER): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLSetStmtAttr*(StatementHandle: SqlHStmt, Attribute: TSqlInteger,
-                     Value: SqlPointer, StringLength: TSqlInteger): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLGetStmtAttr*(StatementHandle: SqlHStmt, Attribute: TSqlInteger,
-                     Value: SqlPointer, BufferLength: TSqlInteger,
-                     StringLength: PSQLINTEGER): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLGetInfo*(ConnectionHandle: SqlHDBC, InfoType: SqlUSmallInt,
-                 InfoValue: SqlPointer, BufferLength: TSqlSmallInt,
-                 StringLength: PSQLSMALLINT): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLBulkOperations*(StatementHandle: SqlHStmt, Operation: TSqlSmallInt): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLPutData*(StatementHandle: SqlHStmt, Data: SqlPointer,
-                 StrLen_or_Ind: TSqlInteger): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLBindCol*(StatementHandle: SqlHStmt, ColumnNumber: SqlUSmallInt,
-                 TargetType: TSqlSmallInt, TargetValue: SqlPointer,
-                 BufferLength: TSqlInteger, StrLen_or_Ind: PSQLINTEGER): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLSetPos*(hstmt: SqlHStmt, irow: SqlUSmallInt, fOption: SqlUSmallInt,
-                fLock: SqlUSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLDataSources*(EnvironmentHandle: SqlHEnv, Direction: SqlUSmallInt,
-                     ServerName: PSQLCHAR, BufferLength1: TSqlSmallInt,
-                     NameLength1: PSQLSMALLINT, Description: PSQLCHAR,
-                     BufferLength2: TSqlSmallInt, NameLength2: PSQLSMALLINT): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLDrivers*(EnvironmentHandle: SqlHEnv, Direction: SqlUSmallInt,
-                 DriverDescription: PSQLCHAR, BufferLength1: TSqlSmallInt,
-                 DescriptionLength1: PSQLSMALLINT, DriverAttributes: PSQLCHAR,
-                 BufferLength2: TSqlSmallInt, AttributesLength2: PSQLSMALLINT): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLSetConnectAttr*(ConnectionHandle: SqlHDBC, Attribute: TSqlInteger,
-                        Value: SqlPointer, StringLength: TSqlInteger): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLGetCursorName*(StatementHandle: SqlHStmt, CursorName: PSQLCHAR,
-                       BufferLength: TSqlSmallInt, NameLength: PSQLSMALLINT): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLSetCursorName*(StatementHandle: SqlHStmt, CursorName: PSQLCHAR,
-                       NameLength: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLRowCount*(StatementHandle: SqlHStmt, RowCount: var TSqlInteger): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLBindParameter*(hstmt: SqlHStmt, ipar: SqlUSmallInt,
-                       fParamType: TSqlSmallInt, fCType: TSqlSmallInt,
-                       fSqlType: TSqlSmallInt, cbColDef: SqlUInteger,
-                       ibScale: TSqlSmallInt, rgbValue: SqlPointer,
-                       cbValueMax: TSqlInteger, pcbValue: PSQLINTEGER): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLFreeStmt*(StatementHandle: SqlHStmt, Option: SqlUSmallInt): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLColAttribute*(StatementHandle: SqlHStmt, ColumnNumber: SqlUSmallInt,
-                      FieldIdentifier: SqlUSmallInt,
-                      CharacterAttribute: PSQLCHAR, BufferLength: TSqlSmallInt,
-                      StringLength: PSQLSMALLINT,
-                      NumericAttribute: SqlPointer): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLEndTran*(HandleType: TSqlSmallInt, Handle: SqlHandle,
-                 CompletionType: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLTables*(hstmt: SqlHStmt, szTableQualifier: PSQLCHAR,
-                cbTableQualifier: TSqlSmallInt, szTableOwner: PSQLCHAR,
-                cbTableOwner: TSqlSmallInt, szTableName: PSQLCHAR,
-                cbTableName: TSqlSmallInt, szTableType: PSQLCHAR,
-                cbTableType: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLColumns*(hstmt: SqlHStmt, szTableQualifier: PSQLCHAR,
-                 cbTableQualifier: TSqlSmallInt, szTableOwner: PSQLCHAR,
-                 cbTableOwner: TSqlSmallInt, szTableName: PSQLCHAR,
-                 cbTableName: TSqlSmallInt, szColumnName: PSQLCHAR,
-                 cbColumnName: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLSpecialColumns*(StatementHandle: SqlHStmt, IdentifierType: SqlUSmallInt,
-                        CatalogName: PSQLCHAR, NameLength1: TSqlSmallInt,
-                        SchemaName: PSQLCHAR, NameLength2: TSqlSmallInt,
-                        TableName: PSQLCHAR, NameLength3: TSqlSmallInt,
-                        Scope: SqlUSmallInt,
-                        Nullable: SqlUSmallInt): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLProcedures*(hstmt: SqlHStmt, szTableQualifier: PSQLCHAR,
-                    cbTableQualifier: TSqlSmallInt, szTableOwner: PSQLCHAR,
-                    cbTableOwner: TSqlSmallInt, szTableName: PSQLCHAR,
-                    cbTableName: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLPrimaryKeys*(hstmt: SqlHStmt, CatalogName: PSQLCHAR,
-                     NameLength1: TSqlSmallInt, SchemaName: PSQLCHAR,
-                     NameLength2: TSqlSmallInt, TableName: PSQLCHAR,
-                     NameLength3: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLProcedureColumns*(hstmt: SqlHStmt, CatalogName: PSQLCHAR,
-                          NameLength1: TSqlSmallInt, SchemaName: PSQLCHAR,
-                          NameLength2: TSqlSmallInt, ProcName: PSQLCHAR,
-                          NameLength3: TSqlSmallInt, ColumnName: PSQLCHAR,
-                          NameLength4: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLStatistics*(hstmt: SqlHStmt, CatalogName: PSQLCHAR,
-                    NameLength1: TSqlSmallInt, SchemaName: PSQLCHAR,
-                    NameLength2: TSqlSmallInt, TableName: PSQLCHAR,
-                    NameLength3: TSqlSmallInt, Unique: SqlUSmallInt,
-                    Reserved: SqlUSmallInt): TSqlSmallInt {.
-                    dynlib: odbclib, importc.}
-proc SQLErr*(henv: SqlHEnv, hdbc: SqlHDBC, hstmt: SqlHStmt,
-              szSqlState, pfNativeError, szErrorMsg: PSQLCHAR,
-              cbErrorMsgMax: TSqlSmallInt,
-              pcbErrorMsg: PSQLINTEGER): TSqlSmallInt {.
-                    dynlib: odbclib, importc: "SQLError".}
-
-{.pop.}
diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim
index de3bfa616..9921b7ffd 100644
--- a/lib/wrappers/openssl.nim
+++ b/lib/wrappers/openssl.nim
@@ -7,38 +7,88 @@
 #    distribution, for details about the copyright.
 #
 
-## OpenSSL support
+## OpenSSL wrapper. Supports OpenSSL >= 1.1.0 dynamically (as default) or statically linked
+## using `--dynlibOverride:ssl`.
 ##
-## When OpenSSL is dynamically linked, the wrapper provides partial forward and backward
-## compatibility for OpenSSL versions above and below 1.1.0
+## `-d:sslVersion=1.2.3` can be used to force an SSL version.
+## This version must be included in the library name.
+## `-d:useOpenssl3` may be set for OpenSSL 3 instead.
 ##
-## OpenSSL can also be statically linked using ``--dynlibOverride:ssl`` for OpenSSL >= 1.1.0.
-## If you want to statically link against OpenSSL 1.0.x, you now have to
-## define the ``openssl10`` symbol via ``-d:openssl10``.
+## There is also limited support for OpenSSL 1.0.x which may require `-d:openssl10`.
 ##
 ## Build and test examples:
 ##
-## .. code-block::
+##   ```cmd
+##   ./bin/nim c -d:ssl -p:. -r tests/stdlib/tssl.nim
+##   ./bin/nim c -d:ssl --threads:on -p:. -r tests/stdlib/thttpclient_ssl.nim
 ##   ./bin/nim c -d:ssl -p:. -r tests/untestable/tssl.nim
-##   ./bin/nim c -d:ssl -p:. --dynlibOverride:ssl --passL:-lcrypto --passL:-lssl -r tests/untestable/tssl.nim
+##   ./bin/nim c -d:ssl -p:. --dynlibOverride:ssl --passl:-lcrypto --passl:-lssl -r tests/untestable/tssl.nim
+##   ./bin/nim r --putenv:NIM_TESTAMENT_REMOTE_NETWORKING:1 -d:ssl -p:testament/lib --threads:on tests/untestable/thttpclient_ssl_remotenetwork.nim
+##   ```
 
-{.deadCodeElim: on.}  # dce option deprecated
+# https://www.feistyduck.com/library/openssl-cookbook/online/ch-testing-with-openssl.html
+#
+from std/strutils import startsWith
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
+
+when defined(nimHasStyleChecks):
+  {.push styleChecks: off.}
 
-const useWinVersion = defined(Windows) or defined(nimdoc)
+const useWinVersion = defined(windows) or defined(nimdoc)
 
-when useWinVersion:
-  when not defined(nimOldDlls) and defined(cpu64):
+# To force openSSL version use -d:sslVersion=1.2.3
+# See: #10281, #10230
+# General issue:
+# Other dynamic libraries (like libpg) load different openSSL version then what nim loads.
+# Having two different openSSL loaded version causes a crash.
+# Use this compile time define to force the openSSL version that your other dynamic libraries want.
+const sslVersion {.strdefine.}: string = ""
+const useOpenssl3* {.booldefine.} = sslVersion.startsWith('3')
+when sslVersion != "":
+  when defined(macosx):
+    const
+      DLLSSLName* = "libssl." & sslVersion & ".dylib"
+      DLLUtilName* = "libcrypto." & sslVersion & ".dylib"
+    from std/posix import SocketHandle
+  elif defined(windows):
+    const
+      DLLSSLName* = "libssl-" & sslVersion & ".dll"
+      DLLUtilName* =  "libcrypto-" & sslVersion & ".dll"
+    from std/winlean import SocketHandle
+  else:
+    const
+      DLLSSLName* = "libssl.so." & sslVersion
+      DLLUtilName* = "libcrypto.so." & sslVersion
+    from std/posix import SocketHandle
+
+elif useWinVersion:
+  when defined(openssl10) or defined(nimOldDlls):
+    when defined(cpu64):
+      const
+        DLLSSLName* = "(ssleay32|ssleay64).dll"
+        DLLUtilName* = "(libeay32|libeay64).dll"
+    else:
+      const
+        DLLSSLName* = "ssleay32.dll"
+        DLLUtilName* = "libeay32.dll"
+  elif defined(cpu64):
     const
       DLLSSLName* = "(libssl-1_1-x64|ssleay64|libssl64).dll"
       DLLUtilName* = "(libcrypto-1_1-x64|libeay64).dll"
   else:
     const
       DLLSSLName* = "(libssl-1_1|ssleay32|libssl32).dll"
-      DLLUtilName* =  "(libcrypto-1_1|libeay32).dll"
+      DLLUtilName* = "(libcrypto-1_1|libeay32).dll"
 
-  from winlean import SocketHandle
+  from std/winlean import SocketHandle
 else:
-  const versions = "(.1.1|.38|.39|.41|.43|.44|.45|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)"
+  # same list of versions but ordered differently?
+  when defined(osx):
+    const versions = "(.3|.1.1|.38|.39|.41|.43|.44|.45|.46|.47|.48|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)"
+  else:
+    const versions = "(.3|.1.1|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|.48|.47|.46|.45|.44|.43|.41|.39|.38|.10|)"
 
   when defined(macosx):
     const
@@ -52,9 +102,12 @@ else:
     const
       DLLSSLName* = "libssl.so" & versions
       DLLUtilName* = "libcrypto.so" & versions
-  from posix import SocketHandle
+  from std/posix import SocketHandle
 
-import dynlib
+import std/dynlib
+
+{.pragma: lcrypto, cdecl, dynlib: DLLUtilName, importc.}
+{.pragma: lssl, cdecl, dynlib: DLLSSLName, importc.}
 
 type
   SslStruct {.final, pure.} = object
@@ -62,9 +115,9 @@ type
   PSslPtr* = ptr SslPtr
   SslCtx* = SslPtr
   PSSL_METHOD* = SslPtr
+  PSTACK* = SslPtr
   PX509* = SslPtr
   PX509_NAME* = SslPtr
-  PEVP_MD* = SslPtr
   PBIO_METHOD* = SslPtr
   BIO* = SslPtr
   EVP_PKEY* = SslPtr
@@ -72,18 +125,29 @@ type
   PASN1_UTCTIME* = SslPtr
   PASN1_cInt* = SslPtr
   PPasswdCb* = SslPtr
+  EVP_MD* = SslPtr
+  EVP_MD_CTX* = SslPtr
+  EVP_PKEY_CTX* = SslPtr
+  ENGINE* = SslPtr
   PFunction* = proc () {.cdecl.}
   DES_cblock* = array[0..7, int8]
   PDES_cblock* = ptr DES_cblock
   des_ks_struct*{.final.} = object
     ks*: DES_cblock
-    weak_key*: cInt
+    weak_key*: cint
 
   des_key_schedule* = array[1..16, des_ks_struct]
 
   pem_password_cb* = proc(buf: cstring, size, rwflag: cint, userdata: pointer): cint {.cdecl.}
 
-{.deprecated: [PSSL: SslPtr, PSSL_CTX: SslCtx, PBIO: BIO].}
+  PaddingType* = enum
+    RSA_PKCS1_PADDING = 1.cint,
+    RSA_SSLV23_PADDING = 2.cint,
+    RSA_NO_PADDING = 3.cint,
+    RSA_PKCS1_OAEP_PADDING = 4.cint,
+    RSA_X931_PADDING = 5.cint,
+    RSA_PKCS1_PSS_PADDING = 6.cint
+
 
 const
   SSL_SENT_SHUTDOWN* = 1
@@ -141,6 +205,7 @@ const
   SSL_CTRL_SET_TLSEXT_SERVERNAME_CB = 53
   SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG = 54
   SSL_CTRL_SET_TLSEXT_HOSTNAME = 55
+  SSL_CTRL_SET_ECDH_AUTO* = 94
   TLSEXT_NAMETYPE_host_name* = 0
   SSL_TLSEXT_ERR_OK* = 0
   SSL_TLSEXT_ERR_ALERT_WARNING* = 1
@@ -161,6 +226,9 @@ const
   SSL_OP_ALL* = 0x000FFFFF
   SSL_VERIFY_NONE* = 0x00000000
   SSL_VERIFY_PEER* = 0x00000001
+  SSL_ST_CONNECT* = 0x1000
+  SSL_ST_ACCEPT* = 0x2000
+  SSL_ST_INIT* = SSL_ST_CONNECT or SSL_ST_ACCEPT
   OPENSSL_DES_DECRYPT* = 0
   OPENSSL_DES_ENCRYPT* = 1
   X509_V_OK* = 0
@@ -213,13 +281,21 @@ proc TLSv1_method*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.}
 # and support SSLv3, TLSv1, TLSv1.1 and TLSv1.2
 # SSLv23_method(), SSLv23_server_method(), SSLv23_client_method() are removed in 1.1.0
 
-when compileOption("dynlibOverride", "ssl"):
+const useStaticLink = compileOption("dynlibOverride", "ssl") or defined(noOpenSSLHacks)
+
+when useStaticLink:
   # Static linking
 
   when defined(openssl10):
     proc SSL_library_init*(): cint {.cdecl, dynlib: DLLSSLName, importc, discardable.}
     proc SSL_load_error_strings*() {.cdecl, dynlib: DLLSSLName, importc.}
     proc SSLv23_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
+    proc SSLeay(): culong {.cdecl, dynlib: DLLUtilName, importc.}
+
+    proc getOpenSSLVersion*(): culong =
+      SSLeay()
+
+    proc ERR_load_BIO_strings*() {.cdecl, dynlib: DLLUtilName, importc.}
   else:
     proc OPENSSL_init_ssl*(opts: uint64, settings: uint8): cint {.cdecl, dynlib: DLLSSLName, importc, discardable.}
     proc SSL_library_init*(): cint {.discardable.} =
@@ -230,7 +306,7 @@ when compileOption("dynlibOverride", "ssl"):
     proc SSLv23_method*(): PSSL_METHOD =
       TLS_method()
 
-    proc OpenSSL_version_num(): culong {.cdecl, dynlib: DLLSSLName, importc.}
+    proc OpenSSL_version_num(): culong {.cdecl, dynlib: DLLUtilName, importc.}
 
     proc getOpenSSLVersion*(): culong =
       ## Return OpenSSL version as unsigned long
@@ -242,52 +318,105 @@ when compileOption("dynlibOverride", "ssl"):
       # Static linking against OpenSSL < 1.1.0 is not supported
       discard
 
+    proc ERR_load_BIO_strings*() =
+      discard
+
+  when defined(libressl) or defined(openssl10):
+    proc SSL_state(ssl: SslPtr): cint {.cdecl, dynlib: DLLSSLName, importc.}
+    proc SSL_in_init*(ssl: SslPtr): cint {.inline.} =
+      SSL_state(ssl) and SSL_ST_INIT
+  else:
+    proc SSL_in_init*(ssl: SslPtr): cint {.cdecl, dynlib: DLLSSLName, importc.}
+    proc SSL_CTX_set_ciphersuites*(ctx: SslCtx, str: cstring): cint {.cdecl, dynlib: DLLSSLName, importc.}
+
   template OpenSSL_add_all_algorithms*() = discard
 
   proc SSLv23_client_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
   proc SSLv2_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
   proc SSLv3_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
+  proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl, dynlib: DLLUtilName, importc.}
 
 else:
-  # Here we're trying to stay compatible with openssl 1.0.* and 1.1.*. Some
+  # Here we're trying to stay compatible between openssl versions. Some
   # symbols are loaded dynamically and we don't use them if not found.
   proc thisModule(): LibHandle {.inline.} =
     var thisMod {.global.}: LibHandle
     if thisMod.isNil: thisMod = loadLib()
+
     result = thisMod
 
-  proc sslModule(): LibHandle {.inline.} =
+  proc sslModule(): LibHandle {.inline, raises: [LibraryError], tags:[RootEffect].} =
     var sslMod {.global.}: LibHandle
-    if sslMod.isNil: sslMod = loadLibPattern(DLLSSLName)
+    try:
+      if sslMod.isNil: sslMod = loadLibPattern(DLLSSLName)
+    except:
+      raise newException(LibraryError, "Could not load SSL using " & DLLSSLName)
+
     result = sslMod
 
-  proc sslSym(name: string): pointer =
-    var dl = thisModule()
-    if not dl.isNil:
-      result = symAddr(dl, name)
+  proc utilModule(): LibHandle {.inline.} =
+    var utilMod {.global.}: LibHandle
+    if utilMod.isNil: utilMod = loadLibPattern(DLLUtilName)
+
+    result = utilMod
+
+  proc symNullable(dll: LibHandle, name: string, alternativeName = ""): pointer =
+    # Load from DLL.
+    if not dll.isNil:
+      result = symAddr(dll, name)
+      if result.isNil and alternativeName.len > 0:
+        result = symAddr(dll, alternativeName)
+
+    # Attempt to load from current exe.
     if result.isNil:
-      dl = sslModule()
-      if not dl.isNil:
-        result = symAddr(dl, name)
+      let thisDynlib = thisModule()
+      if thisDynlib.isNil: return nil
+      result = symAddr(thisDynlib, name)
+      if result.isNil and alternativeName.len > 0:
+        result = symAddr(thisDynlib, alternativeName)
+
+  proc sslSymNullable(name: string, alternativeName = ""): pointer {.raises: [LibraryError], tags:[RootEffect].} =
+    sslModule().symNullable(name, alternativeName)
+
+  proc sslSymThrows(name: string, alternativeName = ""): pointer {.raises: [LibraryError].} =
+    result = sslSymNullable(name, alternativeName)
+    if result.isNil: raiseInvalidLibrary(name)
 
-  proc loadPSSLMethod(method1, method2: string): PSSL_METHOD =
+  proc utilSymNullable(name: string, alternativeName = ""): pointer =
+    utilModule().symNullable(name, alternativeName)
+
+  proc loadPSSLMethod(method1, method2: string): PSSL_METHOD {.raises: [LibraryError], tags:[RootEffect].} =
     ## Load <method1> from OpenSSL if available, otherwise <method2>
-    let m1 = cast[proc(): PSSL_METHOD {.cdecl, gcsafe.}](sslSym(method1))
-    if not m1.isNil:
-      return m1()
-    cast[proc(): PSSL_METHOD {.cdecl, gcsafe.}](sslSym(method2))()
+    ##
+    let methodSym = sslSymNullable(method1, method2)
+    if methodSym.isNil:
+      raise newException(LibraryError, "Could not load " & method1 & " nor " & method2)
+
+    let method2Proc = cast[proc(): PSSL_METHOD {.cdecl, gcsafe, raises: [].}](methodSym)
+    return method2Proc()
+
+  proc CRYPTO_set_mem_functions(a,b,c: pointer) =
+    let theProc = cast[proc(a,b,c: pointer) {.cdecl.}](utilModule().symNullable("CRYPTO_set_mem_functions"))
+    if not theProc.isNil: theProc(a, b, c)
 
   proc SSL_library_init*(): cint {.discardable.} =
     ## Initialize SSL using OPENSSL_init_ssl for OpenSSL >= 1.1.0 otherwise
     ## SSL_library_init
-    let theProc = cast[proc(opts: uint64, settings: uint8): cint {.cdecl.}](sslSym("OPENSSL_init_ssl"))
-    if not theProc.isNil:
-      return theProc(0, 0)
-    let olderProc = cast[proc(): cint {.cdecl.}](sslSym("SSL_library_init"))
+    let newInitSym = sslSymNullable("OPENSSL_init_ssl")
+    if not newInitSym.isNil:
+      let newInitProc =
+        cast[proc(opts: uint64, settings: uint8): cint {.cdecl.}](newInitSym)
+      return newInitProc(0, 0)
+    let olderProc = cast[proc(): cint {.cdecl.}](sslSymThrows("SSL_library_init"))
     if not olderProc.isNil: result = olderProc()
 
   proc SSL_load_error_strings*() =
-    let theProc = cast[proc() {.cdecl.}](sslSym("SSL_load_error_strings"))
+    # TODO: Are we ignoring this on purpose? SSL GitHub CI fails otherwise.
+    let theProc = cast[proc() {.cdecl.}](sslSymNullable("SSL_load_error_strings"))
+    if not theProc.isNil: theProc()
+
+  proc ERR_load_BIO_strings*() =
+    let theProc = cast[proc() {.cdecl.}](utilModule().symNullable("ERR_load_BIO_strings"))
     if not theProc.isNil: theProc()
 
   proc SSLv23_client_method*(): PSSL_METHOD =
@@ -312,38 +441,62 @@ else:
     loadPSSLMethod("TLS_server_method", "SSLv23_server_method")
 
   proc OpenSSL_add_all_algorithms*() =
-    let theProc = cast[proc() {.cdecl.}](sslSym("OPENSSL_add_all_algorithms_conf"))
+    # TODO: Are we ignoring this on purpose? SSL GitHub CI fails otherwise.
+    let theProc = cast[proc() {.cdecl.}](sslSymNullable("OPENSSL_add_all_algorithms_conf"))
     if not theProc.isNil: theProc()
 
   proc getOpenSSLVersion*(): culong =
     ## Return OpenSSL version as unsigned long or 0 if not available
-    let theProc = cast[proc(): culong {.cdecl.}](sslSym("OpenSSL_version_num"))
+    let theProc = cast[proc(): culong {.cdecl, gcsafe.}](utilSymNullable("OpenSSL_version_num", "SSLeay"))
     result =
       if theProc.isNil: 0.culong
       else: theProc()
 
-proc ERR_load_BIO_strings*(){.cdecl, dynlib: DLLUtilName, importc.}
+  proc SSL_in_init*(ssl: SslPtr): cint =
+    # A compatibility wrapper for `SSL_in_init()` for OpenSSL 1.0, 1.1 and LibreSSL
+    const MainProc = "SSL_in_init"
+    let
+      theProc {.global.} = cast[proc(ssl: SslPtr): cint {.cdecl, gcsafe.}](sslSymNullable(MainProc))
+      # Fallback
+      sslState {.global.} = cast[proc(ssl: SslPtr): cint {.cdecl, gcsafe.}](sslSymNullable("SSL_state"))
+
+    if not theProc.isNil:
+      result = theProc(ssl)
+    elif not sslState.isNil:
+      result = sslState(ssl) and SSL_ST_INIT
+    else:
+      raiseInvalidLibrary MainProc
+
+  proc SSL_CTX_set_ciphersuites*(ctx: SslCtx, str: cstring): cint =
+    var theProc {.global.}: proc(ctx: SslCtx, str: cstring): cint {.cdecl, gcsafe.}
+    if theProc.isNil:
+      theProc = cast[typeof(theProc)](sslSymThrows("SSL_CTX_set_ciphersuites"))
+    result = theProc(ctx, str)
 
 proc SSL_new*(context: SslCtx): SslPtr{.cdecl, dynlib: DLLSSLName, importc.}
 proc SSL_free*(ssl: SslPtr){.cdecl, dynlib: DLLSSLName, importc.}
 proc SSL_get_SSL_CTX*(ssl: SslPtr): SslCtx {.cdecl, dynlib: DLLSSLName, importc.}
+proc SSL_set_SSL_CTX*(ssl: SslPtr, ctx: SslCtx): SslCtx {.cdecl, dynlib: DLLSSLName, importc.}
+proc SSL_CTX_set_session_id_context*(context: SslCtx, sid_ctx: string, sid_ctx_len: int){.cdecl, dynlib: DLLSSLName, importc.}
+proc SSL_get0_verified_chain*(ssl: SslPtr): PSTACK {.cdecl, dynlib: DLLSSLName,
+    importc.}
 proc SSL_CTX_new*(meth: PSSL_METHOD): SslCtx{.cdecl,
     dynlib: DLLSSLName, importc.}
 proc SSL_CTX_load_verify_locations*(ctx: SslCtx, CAfile: cstring,
-    CApath: cstring): cInt{.cdecl, dynlib: DLLSSLName, importc.}
+    CApath: cstring): cint{.cdecl, dynlib: DLLSSLName, importc.}
 proc SSL_CTX_free*(arg0: SslCtx){.cdecl, dynlib: DLLSSLName, importc.}
 proc SSL_CTX_set_verify*(s: SslCtx, mode: int, cb: proc (a: int, b: pointer): int {.cdecl.}){.cdecl, dynlib: DLLSSLName, importc.}
 proc SSL_get_verify_result*(ssl: SslPtr): int{.cdecl,
     dynlib: DLLSSLName, importc.}
 
 proc SSL_CTX_set_cipher_list*(s: SslCtx, ciphers: cstring): cint{.cdecl, dynlib: DLLSSLName, importc.}
-proc SSL_CTX_use_certificate_file*(ctx: SslCtx, filename: cstring, typ: cInt): cInt{.
+proc SSL_CTX_use_certificate_file*(ctx: SslCtx, filename: cstring, typ: cint): cint{.
     stdcall, dynlib: DLLSSLName, importc.}
-proc SSL_CTX_use_certificate_chain_file*(ctx: SslCtx, filename: cstring): cInt{.
+proc SSL_CTX_use_certificate_chain_file*(ctx: SslCtx, filename: cstring): cint{.
     stdcall, dynlib: DLLSSLName, importc.}
 proc SSL_CTX_use_PrivateKey_file*(ctx: SslCtx,
-    filename: cstring, typ: cInt): cInt{.cdecl, dynlib: DLLSSLName, importc.}
-proc SSL_CTX_check_private_key*(ctx: SslCtx): cInt{.cdecl, dynlib: DLLSSLName,
+    filename: cstring, typ: cint): cint{.cdecl, dynlib: DLLSSLName, importc.}
+proc SSL_CTX_check_private_key*(ctx: SslCtx): cint{.cdecl, dynlib: DLLSSLName,
     importc.}
 
 proc SSL_CTX_get_ex_new_index*(argl: clong, argp: pointer, new_func: pointer, dup_func: pointer, free_func: pointer): cint {.cdecl, dynlib: DLLSSLName, importc.}
@@ -352,22 +505,22 @@ proc SSL_CTX_get_ex_data*(ssl: SslCtx, idx: cint): pointer {.cdecl, dynlib: DLLS
 
 proc SSL_set_fd*(ssl: SslPtr, fd: SocketHandle): cint{.cdecl, dynlib: DLLSSLName, importc.}
 
-proc SSL_shutdown*(ssl: SslPtr): cInt{.cdecl, dynlib: DLLSSLName, importc.}
+proc SSL_shutdown*(ssl: SslPtr): cint{.cdecl, dynlib: DLLSSLName, importc.}
 proc SSL_set_shutdown*(ssl: SslPtr, mode: cint) {.cdecl, dynlib: DLLSSLName, importc: "SSL_set_shutdown".}
 proc SSL_get_shutdown*(ssl: SslPtr): cint {.cdecl, dynlib: DLLSSLName, importc: "SSL_get_shutdown".}
 proc SSL_connect*(ssl: SslPtr): cint{.cdecl, dynlib: DLLSSLName, importc.}
 proc SSL_read*(ssl: SslPtr, buf: pointer, num: int): cint{.cdecl, dynlib: DLLSSLName, importc.}
 proc SSL_write*(ssl: SslPtr, buf: cstring, num: int): cint{.cdecl, dynlib: DLLSSLName, importc.}
-proc SSL_get_error*(s: SslPtr, ret_code: cInt): cInt{.cdecl, dynlib: DLLSSLName, importc.}
-proc SSL_accept*(ssl: SslPtr): cInt{.cdecl, dynlib: DLLSSLName, importc.}
-proc SSL_pending*(ssl: SslPtr): cInt{.cdecl, dynlib: DLLSSLName, importc.}
+proc SSL_get_error*(s: SslPtr, ret_code: cint): cint{.cdecl, dynlib: DLLSSLName, importc.}
+proc SSL_accept*(ssl: SslPtr): cint{.cdecl, dynlib: DLLSSLName, importc.}
+proc SSL_pending*(ssl: SslPtr): cint{.cdecl, dynlib: DLLSSLName, importc.}
 
 proc BIO_new_mem_buf*(data: pointer, len: cint): BIO{.cdecl,
-    dynlib: DLLSSLName, importc.}
+    dynlib: DLLUtilName, importc.}
 proc BIO_new_ssl_connect*(ctx: SslCtx): BIO{.cdecl,
     dynlib: DLLSSLName, importc.}
 proc BIO_ctrl*(bio: BIO, cmd: cint, larg: int, arg: cstring): int{.cdecl,
-    dynlib: DLLSSLName, importc.}
+    dynlib: DLLUtilName, importc.}
 proc BIO_get_ssl*(bio: BIO, ssl: ptr SslPtr): int =
   return BIO_ctrl(bio, BIO_C_GET_SSL, 0, cast[cstring](ssl))
 proc BIO_set_conn_hostname*(bio: BIO, name: cstring): int =
@@ -377,59 +530,86 @@ proc BIO_do_handshake*(bio: BIO): int =
 proc BIO_do_connect*(bio: BIO): int =
   return BIO_do_handshake(bio)
 
-when not defined(nimfix):
-  proc BIO_read*(b: BIO, data: cstring, length: cInt): cInt{.cdecl,
-      dynlib: DLLUtilName, importc.}
-  proc BIO_write*(b: BIO, data: cstring, length: cInt): cInt{.cdecl,
-      dynlib: DLLUtilName, importc.}
+proc BIO_read*(b: BIO, data: cstring, length: cint): cint{.cdecl, dynlib: DLLUtilName, importc.}
+proc BIO_write*(b: BIO, data: cstring, length: cint): cint{.cdecl, dynlib: DLLUtilName, importc.}
 
-proc BIO_free*(b: BIO): cInt{.cdecl, dynlib: DLLUtilName, importc.}
+proc BIO_free*(b: BIO): cint{.cdecl, dynlib: DLLUtilName, importc.}
 
-proc ERR_print_errors_fp*(fp: File){.cdecl, dynlib: DLLSSLName, importc.}
+proc ERR_print_errors_fp*(fp: File){.cdecl, dynlib: DLLUtilName, importc.}
 
-proc ERR_error_string*(e: cInt, buf: cstring): cstring{.cdecl,
+proc ERR_error_string*(e: culong, buf: cstring): cstring{.cdecl,
     dynlib: DLLUtilName, importc.}
-proc ERR_get_error*(): cInt{.cdecl, dynlib: DLLUtilName, importc.}
-proc ERR_peek_last_error*(): cInt{.cdecl, dynlib: DLLUtilName, importc.}
+proc ERR_get_error*(): culong{.cdecl, dynlib: DLLUtilName, importc.}
+proc ERR_peek_last_error*(): culong{.cdecl, dynlib: DLLUtilName, importc.}
+
+proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLUtilName, importc.}
 
-proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLSSLName, importc.}
+proc OPENSSL_sk_num*(stack: PSTACK): int {.cdecl, dynlib: DLLSSLName, importc.}
 
-when not useWinVersion and not defined(macosx) and not defined(android) and not defined(nimNoAllocForSSL):
-  proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl,
+proc OPENSSL_sk_value*(stack: PSTACK, index: int): pointer {.cdecl,
+    dynlib: DLLSSLName, importc.}
+
+proc d2i_X509*(px: ptr PX509, i: ptr ptr uint8, len: cint): PX509 {.cdecl,
     dynlib: DLLUtilName, importc.}
 
-  proc allocWrapper(size: int): pointer {.cdecl.} = alloc(size)
-  proc reallocWrapper(p: pointer; newsize: int): pointer {.cdecl.} =
+proc i2d_X509*(cert: PX509; o: ptr ptr uint8): cint {.cdecl,
+    dynlib: DLLUtilName, importc.}
+
+proc d2i_X509*(b: string): PX509 =
+  ## decode DER/BER bytestring into X.509 certificate struct
+  var bb = b.cstring
+  let i = cast[ptr ptr uint8](addr bb)
+  let ret = d2i_X509(addr result, i, b.len.cint)
+  if ret.isNil:
+    raise newException(Exception, "X.509 certificate decoding failed")
+
+proc i2d_X509*(cert: PX509): string =
+  ## encode `cert` to DER string
+  let encoded_length = i2d_X509(cert, nil)
+  result = newString(encoded_length)
+  var q = result.cstring
+  let o = cast[ptr ptr uint8](addr q)
+  let length = i2d_X509(cert, o)
+  if length.int <= 0:
+    raise newException(Exception, "X.509 certificate encoding failed")
+
+const
+  useNimsAlloc = not defined(nimNoAllocForSSL) and not defined(gcDestructors)
+
+when not useWinVersion and not defined(macosx) and not defined(android) and useNimsAlloc:
+  proc allocWrapper(size: int): pointer {.cdecl.} = allocShared(size)
+  proc reallocWrapper(p: pointer; newSize: int): pointer {.cdecl.} =
     if p == nil:
-      if newSize > 0: result = alloc(newsize)
-    elif newsize == 0: dealloc(p)
-    else: result = realloc(p, newsize)
+      if newSize > 0: result = allocShared(newSize)
+    elif newSize == 0: deallocShared(p)
+    else: result = reallocShared(p, newSize)
   proc deallocWrapper(p: pointer) {.cdecl.} =
-    if p != nil: dealloc(p)
+    if p != nil: deallocShared(p)
 
-proc CRYPTO_malloc_init*() =
-  when not useWinVersion and not defined(macosx) and not defined(android) and not defined(nimNoAllocForSSL):
-    CRYPTO_set_mem_functions(allocWrapper, reallocWrapper, deallocWrapper)
+  proc CRYPTO_malloc_init*() =
+    CRYPTO_set_mem_functions(cast[pointer](allocWrapper), cast[pointer](reallocWrapper), cast[pointer](deallocWrapper))
+else:
+  proc CRYPTO_malloc_init*() =
+    discard
 
-proc SSL_CTX_ctrl*(ctx: SslCtx, cmd: cInt, larg: int, parg: pointer): int{.
+proc SSL_CTX_ctrl*(ctx: SslCtx, cmd: cint, larg: clong, parg: pointer): clong{.
   cdecl, dynlib: DLLSSLName, importc.}
 
-proc SSL_CTX_callback_ctrl(ctx: SslCtx, typ: cInt, fp: PFunction): int{.
+proc SSL_CTX_callback_ctrl(ctx: SslCtx, typ: cint, fp: PFunction): int{.
   cdecl, dynlib: DLLSSLName, importc.}
 
 proc SSLCTXSetMode*(ctx: SslCtx, mode: int): int =
-  result = SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, mode, nil)
+  result = SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, clong mode, nil)
 
-proc SSL_ctrl*(ssl: SslPtr, cmd: cInt, larg: int, parg: pointer): int{.
+proc SSL_ctrl*(ssl: SslPtr, cmd: cint, larg: int, parg: pointer): int{.
   cdecl, dynlib: DLLSSLName, importc.}
 
 proc SSL_set_tlsext_host_name*(ssl: SslPtr, name: cstring): int =
-  result = SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, name)
   ## Set the SNI server name extension to be used in a client hello.
   ## Returns 1 if SNI was set, 0 if current SSL configuration doesn't support SNI.
+  result = SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, name)
 
-
-proc SSL_get_servername*(ssl: SslPtr, typ: cInt = TLSEXT_NAMETYPE_host_name): cstring {.cdecl, dynlib: DLLSSLName, importc.}
+proc SSL_get_servername*(ssl: SslPtr, typ: cint = TLSEXT_NAMETYPE_host_name): cstring {.cdecl, dynlib: DLLSSLName, importc.}
   ## Retrieve the server name requested in the client hello. This can be used
   ## in the callback set in `SSL_CTX_set_tlsext_servername_callback` to
   ## implement virtual hosting. May return `nil`.
@@ -445,16 +625,16 @@ proc SSL_CTX_set_tlsext_servername_callback*(ctx: SslCtx, cb: proc(ssl: SslPtr,
   result = SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, cast[PFunction](cb))
 
 proc SSL_CTX_set_tlsext_servername_arg*(ctx: SslCtx, arg: pointer): int =
-  ## Set the pointer to be used in the callback registered to ``SSL_CTX_set_tlsext_servername_callback``.
+  ## Set the pointer to be used in the callback registered to `SSL_CTX_set_tlsext_servername_callback`.
   result = SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG, 0, arg)
 
 type
   PskClientCallback* = proc (ssl: SslPtr;
-    hint: cstring; identity: cstring; max_identity_len: cuint; psk: ptr cuchar;
+    hint: cstring; identity: cstring; max_identity_len: cuint; psk: ptr uint8;
     max_psk_len: cuint): cuint {.cdecl.}
 
   PskServerCallback* = proc (ssl: SslPtr;
-    identity: cstring; psk: ptr cuchar; max_psk_len: cint): cuint {.cdecl.}
+    identity: cstring; psk: ptr uint8; max_psk_len: cint): cuint {.cdecl.}
 
 proc SSL_CTX_set_psk_client_callback*(ctx: SslCtx; callback: PskClientCallback) {.cdecl, dynlib: DLLSSLName, importc.}
   ## Set callback called when OpenSSL needs PSK (for client).
@@ -468,13 +648,22 @@ proc SSL_CTX_use_psk_identity_hint*(ctx: SslCtx; hint: cstring): cint {.cdecl, d
 proc SSL_get_psk_identity*(ssl: SslPtr): cstring {.cdecl, dynlib: DLLSSLName, importc.}
   ## Get PSK identity.
 
+proc SSL_CTX_set_ecdh_auto*(ctx: SslCtx, onoff: cint): cint {.inline.} =
+  ## Set automatic curve selection.
+  ##
+  ## On OpenSSL >= 1.1.0 this is on by default and cannot be disabled.
+  if getOpenSSLVersion() < 0x010100000 or getOpenSSLVersion() == 0x020000000:
+    result = cint SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, nil)
+  else:
+    result = 1
+
 proc bioNew*(b: PBIO_METHOD): BIO{.cdecl, dynlib: DLLUtilName, importc: "BIO_new".}
 proc bioFreeAll*(b: BIO){.cdecl, dynlib: DLLUtilName, importc: "BIO_free_all".}
 proc bioSMem*(): PBIO_METHOD{.cdecl, dynlib: DLLUtilName, importc: "BIO_s_mem".}
-proc bioCtrlPending*(b: BIO): cInt{.cdecl, dynlib: DLLUtilName, importc: "BIO_ctrl_pending".}
-proc bioRead*(b: BIO, Buf: cstring, length: cInt): cInt{.cdecl,
+proc bioCtrlPending*(b: BIO): cint{.cdecl, dynlib: DLLUtilName, importc: "BIO_ctrl_pending".}
+proc bioRead*(b: BIO, Buf: cstring, length: cint): cint{.cdecl,
     dynlib: DLLUtilName, importc: "BIO_read".}
-proc bioWrite*(b: BIO, Buf: cstring, length: cInt): cInt{.cdecl,
+proc bioWrite*(b: BIO, Buf: cstring, length: cint): cint{.cdecl,
     dynlib: DLLUtilName, importc: "BIO_write".}
 
 proc sslSetConnectState*(s: SslPtr) {.cdecl,
@@ -482,11 +671,11 @@ proc sslSetConnectState*(s: SslPtr) {.cdecl,
 proc sslSetAcceptState*(s: SslPtr) {.cdecl,
     dynlib: DLLSSLName, importc: "SSL_set_accept_state".}
 
-proc sslRead*(ssl: SslPtr, buf: cstring, num: cInt): cInt{.cdecl,
+proc sslRead*(ssl: SslPtr, buf: cstring, num: cint): cint{.cdecl,
       dynlib: DLLSSLName, importc: "SSL_read".}
-proc sslPeek*(ssl: SslPtr, buf: cstring, num: cInt): cInt{.cdecl,
+proc sslPeek*(ssl: SslPtr, buf: cstring, num: cint): cint{.cdecl,
     dynlib: DLLSSLName, importc: "SSL_peek".}
-proc sslWrite*(ssl: SslPtr, buf: cstring, num: cInt): cInt{.cdecl,
+proc sslWrite*(ssl: SslPtr, buf: cstring, num: cint): cint{.cdecl,
     dynlib: DLLSSLName, importc: "SSL_write".}
 
 proc sslSetBio*(ssl: SslPtr, rbio, wbio: BIO) {.cdecl,
@@ -496,151 +685,78 @@ proc sslDoHandshake*(ssl: SslPtr): cint {.cdecl,
     dynlib: DLLSSLName, importc: "SSL_do_handshake".}
 
 
-
 proc ErrClearError*(){.cdecl, dynlib: DLLUtilName, importc: "ERR_clear_error".}
 proc ErrFreeStrings*(){.cdecl, dynlib: DLLUtilName, importc: "ERR_free_strings".}
-proc ErrRemoveState*(pid: cInt){.cdecl, dynlib: DLLUtilName, importc: "ERR_remove_state".}
+proc ErrRemoveState*(pid: cint){.cdecl, dynlib: DLLUtilName, importc: "ERR_remove_state".}
 
 proc PEM_read_bio_RSA_PUBKEY*(bp: BIO, x: ptr PRSA, pw: pem_password_cb, u: pointer): PRSA {.cdecl,
-    dynlib: DLLSSLName, importc.}
-
+    dynlib: DLLUtilName, importc.}
+proc PEM_read_RSA_PUBKEY*(fp: pointer; x: ptr PRSA; cb: pem_password_cb, u: pointer): PRSA {.cdecl,
+    dynlib: DLLUtilName, importc.}
 proc RSA_verify*(kind: cint, origMsg: pointer, origMsgLen: cuint, signature: pointer,
-    signatureLen: cuint, rsa: PRSA): cint {.cdecl, dynlib: DLLSSLName, importc.}
-
-when true:
-  discard
+    signatureLen: cuint, rsa: PRSA): cint {.cdecl, dynlib: DLLUtilName, importc.}
+proc PEM_read_RSAPrivateKey*(fp: pointer; x: ptr PRSA; cb: pem_password_cb, u: pointer): PRSA {.cdecl,
+    dynlib: DLLUtilName, importc.}
+proc PEM_read_RSAPublicKey*(fp: pointer; x: ptr PRSA; cb: pem_password_cb, u: pointer): PRSA {.cdecl,
+    dynlib: DLLUtilName, importc.}
+proc PEM_read_bio_RSAPublicKey*(bp: BIO, x: ptr PRSA, cb: pem_password_cb, u: pointer): PRSA {.cdecl,
+    dynlib: DLLUtilName, importc.}
+proc PEM_read_bio_RSAPrivateKey*(bp: BIO, x: ptr PRSA, cb: pem_password_cb, u: pointer): PRSA {.cdecl,
+    dynlib: DLLUtilName, importc.}
+proc RSA_private_encrypt*(flen: cint, fr: ptr uint8, to: ptr uint8, rsa: PRSA, padding: PaddingType): cint {.cdecl,
+    dynlib: DLLUtilName, importc.}
+proc RSA_public_encrypt*(flen: cint, fr: ptr uint8, to: ptr uint8, rsa: PRSA, padding: PaddingType): cint {.cdecl,
+    dynlib: DLLUtilName, importc.}
+proc RSA_private_decrypt*(flen: cint, fr: ptr uint8, to: ptr uint8, rsa: PRSA, padding: PaddingType): cint {.cdecl,
+    dynlib: DLLUtilName, importc.}
+proc RSA_public_decrypt*(flen: cint, fr: ptr uint8, to: ptr uint8, rsa: PRSA, padding: PaddingType): cint {.cdecl,
+    dynlib: DLLUtilName, importc.}
+proc RSA_free*(rsa: PRSA) {.cdecl, dynlib: DLLUtilName, importc.}
+proc RSA_size*(rsa: PRSA): cint {.cdecl, dynlib: DLLUtilName, importc.}
+
+# sha types
+proc EVP_md_null*(): EVP_MD   {.lcrypto.}
+proc EVP_md2*(): EVP_MD       {.lcrypto.}
+proc EVP_md4*(): EVP_MD       {.lcrypto.}
+proc EVP_md5*(): EVP_MD       {.lcrypto.}
+proc EVP_sha*(): EVP_MD       {.lcrypto.}
+proc EVP_sha1*(): EVP_MD      {.lcrypto.}
+proc EVP_dss*(): EVP_MD       {.lcrypto.}
+proc EVP_dss1*(): EVP_MD      {.lcrypto.}
+proc EVP_ecdsa*(): EVP_MD     {.lcrypto.}
+proc EVP_sha224*(): EVP_MD    {.lcrypto.}
+proc EVP_sha256*(): EVP_MD    {.lcrypto.}
+proc EVP_sha384*(): EVP_MD    {.lcrypto.}
+proc EVP_sha512*(): EVP_MD    {.lcrypto.}
+proc EVP_mdc2*(): EVP_MD      {.lcrypto.}
+proc EVP_ripemd160*(): EVP_MD {.lcrypto.}
+proc EVP_whirlpool*(): EVP_MD {.lcrypto.}
+proc EVP_MD_size*(md: EVP_MD): cint {.lcrypto.}
+
+# hmac functions
+proc HMAC*(evp_md: EVP_MD; key: pointer; key_len: cint; d: cstring; n: csize_t; md: cstring; md_len: ptr cuint): cstring {.lcrypto.}
+
+# RSA key functions
+proc PEM_read_bio_PrivateKey*(bp: BIO, x: ptr EVP_PKEY, cb: pointer, u: pointer): EVP_PKEY {.lcrypto.}
+proc EVP_PKEY_free*(p: EVP_PKEY)  {.lcrypto.}
+proc EVP_DigestSignInit*(ctx: EVP_MD_CTX, pctx: ptr EVP_PKEY_CTX, typ: EVP_MD, e: ENGINE, pkey: EVP_PKEY): cint {.lcrypto.}
+proc EVP_DigestInit_ex*(ctx: EVP_MD_CTX, typ: EVP_MD, engine: SslPtr = nil): cint {.lcrypto.}
+proc EVP_DigestUpdate*(ctx: EVP_MD_CTX, data: pointer, len: cuint): cint {.lcrypto.}
+proc EVP_DigestFinal_ex*(ctx: EVP_MD_CTX, buffer: pointer, size: ptr cuint): cint {.lcrypto.}
+proc EVP_DigestSignFinal*(ctx: EVP_MD_CTX, data: pointer, len: ptr csize_t): cint {.lcrypto.}
+proc EVP_PKEY_CTX_new*(pkey: EVP_PKEY, e: ENGINE): EVP_PKEY_CTX {.lcrypto.}
+proc EVP_PKEY_CTX_free*(pkeyCtx: EVP_PKEY_CTX) {.lcrypto.}
+proc EVP_PKEY_sign_init*(c: EVP_PKEY_CTX): cint {.lcrypto.}
+
+when defined(macosx) or defined(windows):
+  proc EVP_MD_CTX_create*(): EVP_MD_CTX {.lcrypto.}
+  proc EVP_MD_CTX_destroy*(ctx: EVP_MD_CTX) {.lcrypto.}
+  proc EVP_MD_CTX_cleanup*(ctx: EVP_MD_CTX): cint {.lcrypto.}
 else:
-  proc SslCtxSetCipherList*(arg0: PSSL_CTX, str: cstring): cInt{.cdecl,
-      dynlib: DLLSSLName, importc.}
-  proc SslCtxNew*(meth: PSSL_METHOD): PSSL_CTX{.cdecl,
-      dynlib: DLLSSLName, importc.}
-
-  proc SslSetFd*(s: PSSL, fd: cInt): cInt{.cdecl, dynlib: DLLSSLName, importc.}
-  proc SslCTXCtrl*(ctx: PSSL_CTX, cmd: cInt, larg: int, parg: Pointer): int{.
-      cdecl, dynlib: DLLSSLName, importc.}
-
-  proc SSLSetMode*(s: PSSL, mode: int): int
-  proc SSLCTXGetMode*(ctx: PSSL_CTX): int
-  proc SSLGetMode*(s: PSSL): int
-  proc SslMethodV2*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.}
-  proc SslMethodV3*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.}
-  proc SslMethodTLSV1*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.}
-  proc SslMethodV23*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.}
-  proc SslCtxUsePrivateKey*(ctx: PSSL_CTX, pkey: SslPtr): cInt{.cdecl,
-      dynlib: DLLSSLName, importc.}
-  proc SslCtxUsePrivateKeyASN1*(pk: cInt, ctx: PSSL_CTX,
-      d: cstring, length: int): cInt{.cdecl, dynlib: DLLSSLName, importc.}
-
-  proc SslCtxUseCertificate*(ctx: PSSL_CTX, x: SslPtr): cInt{.cdecl,
-      dynlib: DLLSSLName, importc.}
-  proc SslCtxUseCertificateASN1*(ctx: PSSL_CTX, length: int, d: cstring): cInt{.
-      cdecl, dynlib: DLLSSLName, importc.}
-
-    #  function SslCtxUseCertificateChainFile(ctx: PSSL_CTX; const filename: PChar):cInt;
-  proc SslCtxUseCertificateChainFile*(ctx: PSSL_CTX, filename: cstring): cInt{.
-      cdecl, dynlib: DLLSSLName, importc.}
-  proc SslCtxSetDefaultPasswdCb*(ctx: PSSL_CTX, cb: PPasswdCb){.cdecl,
-      dynlib: DLLSSLName, importc.}
-  proc SslCtxSetDefaultPasswdCbUserdata*(ctx: PSSL_CTX, u: SslPtr){.cdecl,
-      dynlib: DLLSSLName, importc.}
-    #  function SslCtxLoadVerifyLocations(ctx: PSSL_CTX; const CAfile: PChar; const CApath: PChar):cInt;
-  proc SslCtxLoadVerifyLocations*(ctx: PSSL_CTX, CAfile: cstring, CApath: cstring): cInt{.
-      cdecl, dynlib: DLLSSLName, importc.}
-  proc SslNew*(ctx: PSSL_CTX): PSSL{.cdecl, dynlib: DLLSSLName, importc.}
-
-
-  proc SslConnect*(ssl: PSSL): cInt{.cdecl, dynlib: DLLSSLName, importc.}
-
-
-  proc SslGetVersion*(ssl: PSSL): cstring{.cdecl, dynlib: DLLSSLName, importc.}
-  proc SslGetPeerCertificate*(ssl: PSSL): PX509{.cdecl, dynlib: DLLSSLName,
-      importc.}
-  proc SslCtxSetVerify*(ctx: PSSL_CTX, mode: cInt, arg2: PFunction){.cdecl,
-      dynlib: DLLSSLName, importc.}
-  proc SSLGetCurrentCipher*(s: PSSL): SslPtr{.cdecl, dynlib: DLLSSLName, importc.}
-  proc SSLCipherGetName*(c: SslPtr): cstring{.cdecl, dynlib: DLLSSLName, importc.}
-  proc SSLCipherGetBits*(c: SslPtr, alg_bits: var cInt): cInt{.cdecl,
-      dynlib: DLLSSLName, importc.}
-  proc SSLGetVerifyResult*(ssl: PSSL): int{.cdecl, dynlib: DLLSSLName, importc.}
-    # libeay.dll
-  proc X509New*(): PX509{.cdecl, dynlib: DLLUtilName, importc.}
-  proc X509Free*(x: PX509){.cdecl, dynlib: DLLUtilName, importc.}
-  proc X509NameOneline*(a: PX509_NAME, buf: cstring, size: cInt): cstring{.
-      cdecl, dynlib: DLLUtilName, importc.}
-  proc X509GetSubjectName*(a: PX509): PX509_NAME{.cdecl, dynlib: DLLUtilName,
-      importc.}
-  proc X509GetIssuerName*(a: PX509): PX509_NAME{.cdecl, dynlib: DLLUtilName,
-      importc.}
-  proc X509NameHash*(x: PX509_NAME): int{.cdecl, dynlib: DLLUtilName, importc.}
-    #  function SslX509Digest(data: PX509; typ: PEVP_MD; md: PChar; len: PcInt):cInt;
-  proc X509Digest*(data: PX509, typ: PEVP_MD, md: cstring, length: var cInt): cInt{.
-      cdecl, dynlib: DLLUtilName, importc.}
-  proc X509print*(b: PBIO, a: PX509): cInt{.cdecl, dynlib: DLLUtilName, importc.}
-  proc X509SetVersion*(x: PX509, version: cInt): cInt{.cdecl, dynlib: DLLUtilName,
-      importc.}
-  proc X509SetPubkey*(x: PX509, pkey: EVP_PKEY): cInt{.cdecl, dynlib: DLLUtilName,
-      importc.}
-  proc X509SetIssuerName*(x: PX509, name: PX509_NAME): cInt{.cdecl,
-      dynlib: DLLUtilName, importc.}
-  proc X509NameAddEntryByTxt*(name: PX509_NAME, field: cstring, typ: cInt,
-                              bytes: cstring, length, loc, theSet: cInt): cInt{.
-      cdecl, dynlib: DLLUtilName, importc.}
-  proc X509Sign*(x: PX509, pkey: EVP_PKEY, md: PEVP_MD): cInt{.cdecl,
-      dynlib: DLLUtilName, importc.}
-  proc X509GmtimeAdj*(s: PASN1_UTCTIME, adj: cInt): PASN1_UTCTIME{.cdecl,
-      dynlib: DLLUtilName, importc.}
-  proc X509SetNotBefore*(x: PX509, tm: PASN1_UTCTIME): cInt{.cdecl,
-      dynlib: DLLUtilName, importc.}
-  proc X509SetNotAfter*(x: PX509, tm: PASN1_UTCTIME): cInt{.cdecl,
-      dynlib: DLLUtilName, importc.}
-  proc X509GetSerialNumber*(x: PX509): PASN1_cInt{.cdecl, dynlib: DLLUtilName,
-      importc.}
-  proc EvpPkeyNew*(): EVP_PKEY{.cdecl, dynlib: DLLUtilName, importc.}
-  proc EvpPkeyFree*(pk: EVP_PKEY){.cdecl, dynlib: DLLUtilName, importc.}
-  proc EvpPkeyAssign*(pkey: EVP_PKEY, typ: cInt, key: Prsa): cInt{.cdecl,
-      dynlib: DLLUtilName, importc.}
-  proc EvpGetDigestByName*(Name: cstring): PEVP_MD{.cdecl, dynlib: DLLUtilName,
-      importc.}
-  proc EVPcleanup*(){.cdecl, dynlib: DLLUtilName, importc.}
-    #  function ErrErrorString(e: cInt; buf: PChar): PChar;
-  proc SSLeayversion*(t: cInt): cstring{.cdecl, dynlib: DLLUtilName, importc.}
-
-
-  proc OPENSSLaddallalgorithms*(){.cdecl, dynlib: DLLUtilName, importc.}
-  proc CRYPTOcleanupAllExData*(){.cdecl, dynlib: DLLUtilName, importc.}
-  proc RandScreen*(){.cdecl, dynlib: DLLUtilName, importc.}
-
-  proc d2iPKCS12bio*(b: PBIO, Pkcs12: SslPtr): SslPtr{.cdecl, dynlib: DLLUtilName,
-      importc.}
-  proc PKCS12parse*(p12: SslPtr, pass: cstring, pkey, cert, ca: var SslPtr): cint{.
-      dynlib: DLLUtilName, importc, cdecl.}
-
-  proc PKCS12free*(p12: SslPtr){.cdecl, dynlib: DLLUtilName, importc.}
-  proc RsaGenerateKey*(bits, e: cInt, callback: PFunction, cb_arg: SslPtr): PRSA{.
-      cdecl, dynlib: DLLUtilName, importc.}
-  proc Asn1UtctimeNew*(): PASN1_UTCTIME{.cdecl, dynlib: DLLUtilName, importc.}
-  proc Asn1UtctimeFree*(a: PASN1_UTCTIME){.cdecl, dynlib: DLLUtilName, importc.}
-  proc Asn1cIntSet*(a: PASN1_cInt, v: cInt): cInt{.cdecl, dynlib: DLLUtilName,
-      importc.}
-  proc i2dX509bio*(b: PBIO, x: PX509): cInt{.cdecl, dynlib: DLLUtilName, importc.}
-  proc i2dPrivateKeyBio*(b: PBIO, pkey: EVP_PKEY): cInt{.cdecl,
-      dynlib: DLLUtilName, importc.}
-    # 3DES functions
-  proc DESsetoddparity*(Key: des_cblock){.cdecl, dynlib: DLLUtilName, importc.}
-  proc DESsetkeychecked*(key: des_cblock, schedule: des_key_schedule): cInt{.
-      cdecl, dynlib: DLLUtilName, importc.}
-  proc DESecbencrypt*(Input: des_cblock, output: des_cblock, ks: des_key_schedule,
-                      enc: cInt){.cdecl, dynlib: DLLUtilName, importc.}
-  # implementation
-
-  proc SSLSetMode(s: PSSL, mode: int): int =
-    result = SSLctrl(s, SSL_CTRL_MODE, mode, nil)
-
-  proc SSLCTXGetMode(ctx: PSSL_CTX): int =
-    result = SSLCTXctrl(ctx, SSL_CTRL_MODE, 0, nil)
-
-  proc SSLGetMode(s: PSSL): int =
-    result = SSLctrl(s, SSL_CTRL_MODE, 0, nil)
+  # some times you will need this instead:
+  proc EVP_MD_CTX_create*(): EVP_MD_CTX {.cdecl, importc: "EVP_MD_CTX_new", dynlib: DLLUtilName.}
+  proc EVP_MD_CTX_destroy*(ctx: EVP_MD_CTX) {.cdecl, importc: "EVP_MD_CTX_free", dynlib: DLLUtilName.}
+  proc EVP_MD_CTX_cleanup*(ctx: EVP_MD_CTX): cint {.cdecl, importc: "EVP_MD_CTX_cleanup", dynlib: DLLUtilName.}
 
 # <openssl/md5.h>
 type
@@ -657,13 +773,13 @@ type
 
 {.push callconv:cdecl, dynlib:DLLUtilName.}
 proc md5_Init*(c: var MD5_CTX): cint{.importc: "MD5_Init".}
-proc md5_Update*(c: var MD5_CTX; data: pointer; len: csize): cint{.importc: "MD5_Update".}
+proc md5_Update*(c: var MD5_CTX; data: pointer; len: csize_t): cint{.importc: "MD5_Update".}
 proc md5_Final*(md: cstring; c: var MD5_CTX): cint{.importc: "MD5_Final".}
-proc md5*(d: ptr cuchar; n: csize; md: ptr cuchar): ptr cuchar{.importc: "MD5".}
-proc md5_Transform*(c: var MD5_CTX; b: ptr cuchar){.importc: "MD5_Transform".}
+proc md5*(d: ptr uint8; n: csize_t; md: ptr uint8): ptr uint8{.importc: "MD5".}
+proc md5_Transform*(c: var MD5_CTX; b: ptr uint8){.importc: "MD5_Transform".}
 {.pop.}
 
-from strutils import toHex, toLowerAscii
+from std/strutils import toHex, toLowerAscii
 
 proc hexStr(buf: cstring): string =
   # turn md5s output into a nice hex str
@@ -682,14 +798,14 @@ proc md5_File*(file: string): string {.raises: [IOError,Exception].} =
     buf: array[sz,char]
     ctx: MD5_CTX
 
-  discard md5_init(ctx)
-  while(let bytes = f.readChars(buf, 0, sz); bytes > 0):
-    discard md5_update(ctx, buf[0].addr, bytes)
+  discard md5_Init(ctx)
+  while (let bytes = f.readChars(buf); bytes > 0):
+    discard md5_Update(ctx, buf[0].addr, cast[csize_t](bytes))
 
-  discard md5_final(buf[0].addr, ctx)
+  discard md5_Final(cast[cstring](buf[0].addr), ctx)
   f.close
 
-  result = hexStr(addr buf)
+  result = hexStr(cast[cstring](addr buf))
 
 proc md5_Str*(str: string): string =
   ## Generate MD5 hash for a string. Result is a 32 character
@@ -698,13 +814,96 @@ proc md5_Str*(str: string): string =
     ctx: MD5_CTX
     res: array[MD5_DIGEST_LENGTH,char]
     input = str.cstring
-  discard md5_init(ctx)
+  discard md5_Init(ctx)
 
   var i = 0
   while i < str.len:
     let L = min(str.len - i, 512)
-    discard md5_update(ctx, input[i].addr, L)
+    discard md5_Update(ctx, input[i].addr, cast[csize_t](L))
     i += L
 
-  discard md5_final(addr res, ctx)
-  result = hexStr(addr res)
+  discard md5_Final(cast[cstring](addr res), ctx)
+  result = hexStr(cast[cstring](addr res))
+
+when defined(nimHasStyleChecks):
+  {.pop.}
+
+
+# Certificate validation
+# On old openSSL version some of these symbols are not available
+when not defined(nimDisableCertificateValidation) and not defined(windows):
+
+  # SSL_get_peer_certificate removed in 3.0
+  # SSL_get1_peer_certificate added in 3.0
+  when useOpenssl3:
+    proc SSL_get1_peer_certificate*(ssl: SslCtx): PX509 {.cdecl, dynlib: DLLSSLName, importc.}
+    proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 =
+      SSL_get1_peer_certificate(ssl)
+  elif useStaticLink:
+    proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 {.cdecl, dynlib: DLLSSLName, importc.}
+  else:
+    proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 =
+      let methodSym = sslSymNullable("SSL_get_peer_certificate", "SSL_get1_peer_certificate")
+      if methodSym.isNil:
+        raise newException(LibraryError, "Could not load SSL_get_peer_certificate or SSL_get1_peer_certificate")
+      let method2Proc = cast[proc(ssl: SslCtx): PX509 {.cdecl, gcsafe, raises: [].}](methodSym)
+      return method2Proc(ssl)
+
+  proc X509_get_subject_name*(a: PX509): PX509_NAME{.cdecl, dynlib: DLLSSLName, importc.}
+
+  proc X509_get_issuer_name*(a: PX509): PX509_NAME{.cdecl, dynlib: DLLUtilName, importc.}
+
+  proc X509_NAME_oneline*(a: PX509_NAME, buf: cstring, size: cint): cstring {.
+    cdecl, dynlib:DLLSSLName, importc.}
+
+  proc X509_NAME_get_text_by_NID*(subject:cstring, NID: cint, buf: cstring, size: cint): cint{.
+    cdecl, dynlib:DLLSSLName, importc.}
+
+  proc X509_check_host*(cert: PX509, name: cstring, namelen: cint, flags:cuint, peername: cstring): cint {.cdecl, dynlib: DLLSSLName, importc.}
+
+  proc X509_free*(cert: PX509) {.cdecl, dynlib: DLLSSLName, importc.}
+
+  # Certificates store
+
+  type PX509_STORE* = SslPtr
+  type PX509_OBJECT* = SslPtr
+
+  {.push callconv:cdecl, dynlib:DLLUtilName, importc.}
+
+  proc X509_OBJECT_new*(): PX509_OBJECT
+  proc X509_OBJECT_free*(a: PX509_OBJECT)
+
+  proc X509_STORE_new*(): PX509_STORE
+  proc X509_STORE_free*(v: PX509_STORE)
+  proc X509_STORE_lock*(ctx: PX509_STORE): cint
+  proc X509_STORE_unlock*(ctx: PX509_STORE): cint
+  proc X509_STORE_up_ref*(v: PX509_STORE): cint
+  proc X509_STORE_set_flags*(ctx: PX509_STORE; flags: culong): cint
+  proc X509_STORE_set_purpose*(ctx: PX509_STORE; purpose: cint): cint
+  proc X509_STORE_set_trust*(ctx: PX509_STORE; trust: cint): cint
+  proc X509_STORE_add_cert*(ctx: PX509_STORE; x: PX509): cint
+
+  {.pop.}
+
+  when isMainModule:
+    when defined(nimPreviewSlimSystem):
+      import std/assertions
+    # A simple certificate test
+    let certbytes = readFile("certificate.der")
+    let cert = d2i_X509(certbytes)
+    let encoded = cert.i2d_X509()
+    assert encoded == certbytes
+
+# Application Layer Protocol Negociation extension (TLS-ALPN, RFC7301)
+# Available in at least OpenSSL 1.1.1 and later, not sure if earlier
+# --Iced Quinn
+
+proc SSL_CTX_set_alpn_protos*(ctx: SslCtx; protos: cstring; protos_len: cuint): cint {.cdecl, dynlib: DLLSSLName, importc.}
+proc SSL_set_alpn_protos*(ssl: SslPtr; protos: cstring; protos_len: cuint): cint {.cdecl, dynlib: DLLSSLName, importc.}
+proc SSL_CTX_set_alpn_select_cb*(ctx: SslCtx; cb: proc(ssl: SslPtr; out_proto: ptr cstring; outlen: cstring; in_proto: cstring; inlen: cuint; arg: pointer): cint {.cdecl.}; arg: pointer): cint {.cdecl, dynlib: DLLSSLName, importc.}
+proc SSL_get0_alpn_selected*(ssl: SslPtr; data: ptr cstring; len: ptr cuint) {.cdecl, dynlib: DLLSSLName, importc.}
+proc SSL_CTX_set_next_protos_advertised_cb*(ctx: SslCtx; cb: proc(ssl: SslPtr; out_proto: ptr cstring; outlen: ptr cuint; arg: pointer): cint {.cdecl.}; arg: pointer) {.cdecl, dynlib: DLLSSLName, importc.}
+proc SSL_CTX_set_next_proto_select_cb*(ctx: SslCtx; cb: proc(s: SslPtr; out_proto: cstring; outlen: cstring; in_proto: cstring; inlen: cuint; arg: pointer): cint {.cdecl.}; arg: pointer) {.cdecl, dynlib: DLLSSLName, importc.}
+proc SSL_select_next_proto*(out_proto: ptr cstring; outlen: cstring; server: cstring; server_len: cuint; client: cstring; client_len: cuint): cint {.cdecl, dynlib: DLLSSLName, importc.}
+proc SSL_get0_next_proto_negotiated*(s: SslPtr; data: ptr cstring; len: ptr cuint) {.cdecl, dynlib: DLLSSLName, importc.}
+
diff --git a/lib/wrappers/pcre.nim b/lib/wrappers/pcre.nim
index e9e11960c..9144f6784 100644
--- a/lib/wrappers/pcre.nim
+++ b/lib/wrappers/pcre.nim
@@ -7,8 +7,6 @@
 #    distribution, for details about the copyright.
 #
 
-{.deadCodeElim: on.}  # dce option deprecated
-
 # The current PCRE version information.
 
 const
@@ -105,7 +103,7 @@ const
   NOTEMPTY_ATSTART*  = 0x10000000  #    E D J
   UCP*               = 0x20000000  # C3
 
-## Exec-time and get/set-time error codes
+# Exec-time and get/set-time error codes
 const
   ERROR_NOMATCH*          =  -1
   ERROR_NULL*             =  -2
@@ -146,7 +144,7 @@ const
   ERROR_BADLENGTH*        = -32
   ERROR_UNSET*            = -33
 
-## Specific error codes for UTF-8 validity checks
+# Specific error codes for UTF-8 validity checks
 const
   UTF8_ERR0*  =  0
   UTF8_ERR1*  =  1
@@ -172,7 +170,7 @@ const
   UTF8_ERR21* = 21
   UTF8_ERR22* = 22 # Unused (was non-character)
 
-## Specific error codes for UTF-16 validity checks
+# Specific error codes for UTF-16 validity checks
 const
   UTF16_ERR0* = 0
   UTF16_ERR1* = 1
@@ -180,14 +178,14 @@ const
   UTF16_ERR3* = 3
   UTF16_ERR4* = 4 # Unused (was non-character)
 
-## Specific error codes for UTF-32 validity checks
+# Specific error codes for UTF-32 validity checks
 const
   UTF32_ERR0* = 0
   UTF32_ERR1* = 1
   UTF32_ERR2* = 2 # Unused (was non-character)
   UTF32_ERR3* = 3
 
-## Request types for pcre_fullinfo()
+# Request types for pcre_fullinfo()
 const
   INFO_OPTIONS*             =  0
   INFO_SIZE*                =  1
@@ -217,8 +215,8 @@ const
   INFO_RECURSIONLIMIT*      = 24
   INFO_MATCH_EMPTY*         = 25
 
-## Request types for pcre_config(). Do not re-arrange, in order to remain
-## compatible.
+# Request types for pcre_config(). Do not re-arrange, in order to remain
+# compatible.
 const
   CONFIG_UTF8*                   =  0
   CONFIG_NEWLINE*                =  1
@@ -235,16 +233,16 @@ const
   CONFIG_UTF32*                  = 12
   CONFIG_PARENS_LIMIT*           = 13
 
-## Request types for pcre_study(). Do not re-arrange, in order to remain
-## compatible.
+# Request types for pcre_study(). Do not re-arrange, in order to remain
+# compatible.
 const
   STUDY_JIT_COMPILE*              = 0x0001
   STUDY_JIT_PARTIAL_SOFT_COMPILE* = 0x0002
   STUDY_JIT_PARTIAL_HARD_COMPILE* = 0x0004
   STUDY_EXTRA_NEEDED*             = 0x0008
 
-## Bit flags for the pcre[16|32]_extra structure. Do not re-arrange or redefine
-## these bits, just add new ones on the end, in order to remain compatible.
+# Bit flags for the pcre[16|32]_extra structure. Do not re-arrange or redefine
+# these bits, just add new ones on the end, in order to remain compatible.
 const
   EXTRA_STUDY_DATA*            = 0x0001
   EXTRA_MATCH_LIMIT*           = 0x0002
@@ -254,7 +252,7 @@ const
   EXTRA_MARK*                  = 0x0020
   EXTRA_EXECUTABLE_JIT*        = 0x0040
 
-## Types
+# Types
 type
   Pcre* = object
   Pcre16* = object
@@ -263,10 +261,12 @@ type
   JitStack16* = object
   JitStack32* = object
 
+when defined(nimHasStyleChecks):
+  {.push styleChecks: off.}
 
-## The structure for passing additional data to pcre_exec(). This is defined in
-## such as way as to be extensible. Always add new fields at the end, in order
-## to remain compatible.
+# The structure for passing additional data to pcre_exec(). This is defined in
+# such as way as to be extensible. Always add new fields at the end, in order
+# to remain compatible.
 type
   ExtraData* = object
     flags*: clong                  ## Bits for which fields are set
@@ -278,10 +278,10 @@ type
     mark*: pointer                 ## For passing back a mark pointer
     executable_jit*: pointer       ## Contains a pointer to a compiled jit code
 
-## The structure for passing out data via the pcre_callout_function. We use a
-## structure so that new fields can be added on the end in future versions,
-## without changing the API of the function, thereby allowing old clients to
-## work without modification.
+# The structure for passing out data via the pcre_callout_function. We use a
+# structure so that new fields can be added on the end in future versions,
+# without changing the API of the function, thereby allowing old clients to
+# work without modification.
 type
   CalloutBlock* = object
     version*         : cint       ## Identifies version of block
@@ -302,8 +302,10 @@ type
     mark*            : pointer    ## Pointer to current mark or NULL
     # ------------------------------------------------------------------
 
+when defined(nimHasStyleChecks):
+  {.pop.}
 
-## User defined callback which provides a stack just before the match starts.
+# User defined callback which provides a stack just before the match starts.
 type
   JitCallback* = proc (a: pointer): ptr JitStack {.cdecl.}
 
@@ -466,14 +468,6 @@ proc study*(code: ptr Pcre,
 {.pop.}
 
 
-{.deprecated: [MAJOR: PCRE_MAJOR, MINOR: PCRE_MINOR,
-               PRERELEASE: PCRE_PRERELEASE, DATE: PCRE_DATE].}
-
-{.deprecated: [TPcre: Pcre, TJitStack: JitStack].}
 type
   PPcre* {.deprecated.} = ptr Pcre
   PJitStack* {.deprecated.} = ptr JitStack
-
-{.deprecated: [TExtra: ExtraData].}
-{.deprecated: [TCalloutBlock: CalloutBlock].}
-{.deprecated: [TJitCallback: JitCallback].}
diff --git a/lib/wrappers/postgres.nim b/lib/wrappers/postgres.nim
deleted file mode 100644
index 7cb816f68..000000000
--- a/lib/wrappers/postgres.nim
+++ /dev/null
@@ -1,364 +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 the definitions for structures and externs for
-# functions used by frontend postgres applications. It is based on
-# Postgresql's libpq-fe.h.
-#
-# It is for postgreSQL version 7.4 and higher with support for the v3.0
-# connection-protocol.
-#
-
-{.deadCodeElim: on.}  # dce option deprecated
-
-when defined(windows):
-  const
-    dllName = "libpq.dll"
-elif defined(macosx):
-  const
-    dllName = "libpq.dylib"
-else:
-  const
-    dllName = "libpq.so(.5|)"
-type
-  POid* = ptr Oid
-  Oid* = int32
-
-const
-  ERROR_MSG_LENGTH* = 4096
-  CMDSTATUS_LEN* = 40
-
-type
-  SockAddr* = array[1..112, int8]
-  PGresAttDesc*{.pure, final.} = object
-    name*: cstring
-    adtid*: Oid
-    adtsize*: int
-
-  PPGresAttDesc* = ptr PGresAttDesc
-  PPPGresAttDesc* = ptr PPGresAttDesc
-  PGresAttValue*{.pure, final.} = object
-    length*: int32
-    value*: cstring
-
-  PPGresAttValue* = ptr PGresAttValue
-  PPPGresAttValue* = ptr PPGresAttValue
-  PExecStatusType* = ptr ExecStatusType
-  ExecStatusType* = enum
-    PGRES_EMPTY_QUERY = 0, PGRES_COMMAND_OK, PGRES_TUPLES_OK, PGRES_COPY_OUT,
-    PGRES_COPY_IN, PGRES_BAD_RESPONSE, PGRES_NONFATAL_ERROR, PGRES_FATAL_ERROR
-  PGlobjfuncs*{.pure, final.} = object
-    fn_lo_open*: Oid
-    fn_lo_close*: Oid
-    fn_lo_creat*: Oid
-    fn_lo_unlink*: Oid
-    fn_lo_lseek*: Oid
-    fn_lo_tell*: Oid
-    fn_lo_read*: Oid
-    fn_lo_write*: Oid
-
-  PPGlobjfuncs* = ptr PGlobjfuncs
-  PConnStatusType* = ptr ConnStatusType
-  ConnStatusType* = enum
-    CONNECTION_OK, CONNECTION_BAD, CONNECTION_STARTED, CONNECTION_MADE,
-    CONNECTION_AWAITING_RESPONSE, CONNECTION_AUTH_OK, CONNECTION_SETENV,
-    CONNECTION_SSL_STARTUP, CONNECTION_NEEDED
-  PGconn*{.pure, final.} = object
-    pghost*: cstring
-    pgtty*: cstring
-    pgport*: cstring
-    pgoptions*: cstring
-    dbName*: cstring
-    status*: ConnStatusType
-    errorMessage*: array[0..(ERROR_MSG_LENGTH) - 1, char]
-    Pfin*: File
-    Pfout*: File
-    Pfdebug*: File
-    sock*: int32
-    laddr*: SockAddr
-    raddr*: SockAddr
-    salt*: array[0..(2) - 1, char]
-    asyncNotifyWaiting*: int32
-    notifyList*: pointer
-    pguser*: cstring
-    pgpass*: cstring
-    lobjfuncs*: PPGlobjfuncs
-
-  PPGconn* = ptr PGconn
-  PGresult*{.pure, final.} = object
-    ntups*: int32
-    numAttributes*: int32
-    attDescs*: PPGresAttDesc
-    tuples*: PPPGresAttValue
-    tupArrSize*: int32
-    resultStatus*: ExecStatusType
-    cmdStatus*: array[0..(CMDSTATUS_LEN) - 1, char]
-    binary*: int32
-    conn*: PPGconn
-
-  PPGresult* = ptr PGresult
-  PPostgresPollingStatusType* = ptr PostgresPollingStatusType
-  PostgresPollingStatusType* = enum
-    PGRES_POLLING_FAILED = 0, PGRES_POLLING_READING, PGRES_POLLING_WRITING,
-    PGRES_POLLING_OK, PGRES_POLLING_ACTIVE
-  PPGTransactionStatusType* = ptr PGTransactionStatusType
-  PGTransactionStatusType* = enum
-    PQTRANS_IDLE, PQTRANS_ACTIVE, PQTRANS_INTRANS, PQTRANS_INERROR,
-    PQTRANS_UNKNOWN
-  PPGVerbosity* = ptr PGVerbosity
-  PGVerbosity* = enum
-    PQERRORS_TERSE, PQERRORS_DEFAULT, PQERRORS_VERBOSE
-  PpgNotify* = ptr pgNotify
-  pgNotify*{.pure, final.} = object
-    relname*: cstring
-    be_pid*: int32
-    extra*: cstring
-
-  PQnoticeReceiver* = proc (arg: pointer, res: PPGresult){.cdecl.}
-  PQnoticeProcessor* = proc (arg: pointer, message: cstring){.cdecl.}
-  Ppqbool* = ptr pqbool
-  pqbool* = char
-  P_PQprintOpt* = ptr PQprintOpt
-  PQprintOpt*{.pure, final.} = object
-    header*: pqbool
-    align*: pqbool
-    standard*: pqbool
-    html3*: pqbool
-    expanded*: pqbool
-    pager*: pqbool
-    fieldSep*: cstring
-    tableOpt*: cstring
-    caption*: cstring
-    fieldName*: ptr cstring
-
-  P_PQconninfoOption* = ptr PQconninfoOption
-  PQconninfoOption*{.pure, final.} = object
-    keyword*: cstring
-    envvar*: cstring
-    compiled*: cstring
-    val*: cstring
-    label*: cstring
-    dispchar*: cstring
-    dispsize*: int32
-
-  PPQArgBlock* = ptr PQArgBlock
-  PQArgBlock*{.pure, final.} = object
-    length*: int32
-    isint*: int32
-    p*: pointer
-{.deprecated: [TSockAddr: SockAddr, TPGresAttDesc: PgresAttDesc,
-      TPGresAttValue: PgresAttValue, TExecStatusType: ExecStatusType,
-      TPGlobjfuncs: Pglobjfuncs, TConnStatusType: ConnStatusType, TPGconn: Pgconn,
-      TPGresult: PGresult].}
-
-proc pqconnectStart*(conninfo: cstring): PPGconn{.cdecl, dynlib: dllName,
-    importc: "PQconnectStart".}
-proc pqconnectPoll*(conn: PPGconn): PostgresPollingStatusType{.cdecl,
-    dynlib: dllName, importc: "PQconnectPoll".}
-proc pqconnectdb*(conninfo: cstring): PPGconn{.cdecl, dynlib: dllName,
-    importc: "PQconnectdb".}
-proc pqsetdbLogin*(pghost: cstring, pgport: cstring, pgoptions: cstring,
-                   pgtty: cstring, dbName: cstring, login: cstring, pwd: cstring): PPGconn{.
-    cdecl, dynlib: dllName, importc: "PQsetdbLogin".}
-proc pqsetdb*(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME: cstring): Ppgconn
-proc pqfinish*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQfinish".}
-proc pqconndefaults*(): PPQconninfoOption{.cdecl, dynlib: dllName,
-    importc: "PQconndefaults".}
-proc pqconninfoFree*(connOptions: PPQconninfoOption){.cdecl, dynlib: dllName,
-    importc: "PQconninfoFree".}
-proc pqresetStart*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-    importc: "PQresetStart".}
-proc pqresetPoll*(conn: PPGconn): PostgresPollingStatusType{.cdecl,
-    dynlib: dllName, importc: "PQresetPoll".}
-proc pqreset*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQreset".}
-proc pqrequestCancel*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-    importc: "PQrequestCancel".}
-proc pqdb*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQdb".}
-proc pquser*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQuser".}
-proc pqpass*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQpass".}
-proc pqhost*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQhost".}
-proc pqport*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQport".}
-proc pqtty*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQtty".}
-proc pqoptions*(conn: PPGconn): cstring{.cdecl, dynlib: dllName,
-    importc: "PQoptions".}
-proc pqstatus*(conn: PPGconn): ConnStatusType{.cdecl, dynlib: dllName,
-    importc: "PQstatus".}
-proc pqtransactionStatus*(conn: PPGconn): PGTransactionStatusType{.cdecl,
-    dynlib: dllName, importc: "PQtransactionStatus".}
-proc pqparameterStatus*(conn: PPGconn, paramName: cstring): cstring{.cdecl,
-    dynlib: dllName, importc: "PQparameterStatus".}
-proc pqprotocolVersion*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-    importc: "PQprotocolVersion".}
-proc pqerrorMessage*(conn: PPGconn): cstring{.cdecl, dynlib: dllName,
-    importc: "PQerrorMessage".}
-proc pqsocket*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-                                      importc: "PQsocket".}
-proc pqbackendPID*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-    importc: "PQbackendPID".}
-proc pqclientEncoding*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-    importc: "PQclientEncoding".}
-proc pqsetClientEncoding*(conn: PPGconn, encoding: cstring): int32{.cdecl,
-    dynlib: dllName, importc: "PQsetClientEncoding".}
-when defined(USE_SSL):
-  # Get the SSL structure associated with a connection
-  proc pqgetssl*(conn: PPGconn): PSSL{.cdecl, dynlib: dllName,
-                                       importc: "PQgetssl".}
-proc pqsetErrorVerbosity*(conn: PPGconn, verbosity: PGVerbosity): PGVerbosity{.
-    cdecl, dynlib: dllName, importc: "PQsetErrorVerbosity".}
-proc pqtrace*(conn: PPGconn, debug_port: File){.cdecl, dynlib: dllName,
-    importc: "PQtrace".}
-proc pquntrace*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQuntrace".}
-proc pqsetNoticeReceiver*(conn: PPGconn, theProc: PQnoticeReceiver, arg: pointer): PQnoticeReceiver{.
-    cdecl, dynlib: dllName, importc: "PQsetNoticeReceiver".}
-proc pqsetNoticeProcessor*(conn: PPGconn, theProc: PQnoticeProcessor,
-                           arg: pointer): PQnoticeProcessor{.cdecl,
-    dynlib: dllName, importc: "PQsetNoticeProcessor".}
-proc pqexec*(conn: PPGconn, query: cstring): PPGresult{.cdecl, dynlib: dllName,
-    importc: "PQexec".}
-proc pqexecParams*(conn: PPGconn, command: cstring, nParams: int32,
-                   paramTypes: POid, paramValues: cstringArray,
-                   paramLengths, paramFormats: ptr int32, resultFormat: int32): PPGresult{.
-    cdecl, dynlib: dllName, importc: "PQexecParams".}
-proc pqprepare*(conn: PPGconn, stmtName, query: cstring, nParams: int32,
-    paramTypes: POid): PPGresult{.cdecl, dynlib: dllName, importc: "PQprepare".}
-proc pqexecPrepared*(conn: PPGconn, stmtName: cstring, nParams: int32,
-                     paramValues: cstringArray,
-                     paramLengths, paramFormats: ptr int32, resultFormat: int32): PPGresult{.
-    cdecl, dynlib: dllName, importc: "PQexecPrepared".}
-proc pqsendQuery*(conn: PPGconn, query: cstring): int32{.cdecl, dynlib: dllName,
-    importc: "PQsendQuery".}
-proc pqsendQueryParams*(conn: PPGconn, command: cstring, nParams: int32,
-                        paramTypes: POid, paramValues: cstringArray,
-                        paramLengths, paramFormats: ptr int32,
-                        resultFormat: int32): int32{.cdecl, dynlib: dllName,
-    importc: "PQsendQueryParams".}
-proc pqsendQueryPrepared*(conn: PPGconn, stmtName: cstring, nParams: int32,
-                          paramValues: cstringArray,
-                          paramLengths, paramFormats: ptr int32,
-                          resultFormat: int32): int32{.cdecl, dynlib: dllName,
-    importc: "PQsendQueryPrepared".}
-proc pqgetResult*(conn: PPGconn): PPGresult{.cdecl, dynlib: dllName,
-    importc: "PQgetResult".}
-proc pqisBusy*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-                                      importc: "PQisBusy".}
-proc pqconsumeInput*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-    importc: "PQconsumeInput".}
-proc pqnotifies*(conn: PPGconn): PPGnotify{.cdecl, dynlib: dllName,
-    importc: "PQnotifies".}
-proc pqputCopyData*(conn: PPGconn, buffer: cstring, nbytes: int32): int32{.
-    cdecl, dynlib: dllName, importc: "PQputCopyData".}
-proc pqputCopyEnd*(conn: PPGconn, errormsg: cstring): int32{.cdecl,
-    dynlib: dllName, importc: "PQputCopyEnd".}
-proc pqgetCopyData*(conn: PPGconn, buffer: cstringArray, async: int32): int32{.
-    cdecl, dynlib: dllName, importc: "PQgetCopyData".}
-proc pqgetline*(conn: PPGconn, str: cstring, len: int32): int32{.cdecl,
-    dynlib: dllName, importc: "PQgetline".}
-proc pqputline*(conn: PPGconn, str: cstring): int32{.cdecl, dynlib: dllName,
-    importc: "PQputline".}
-proc pqgetlineAsync*(conn: PPGconn, buffer: cstring, bufsize: int32): int32{.
-    cdecl, dynlib: dllName, importc: "PQgetlineAsync".}
-proc pqputnbytes*(conn: PPGconn, buffer: cstring, nbytes: int32): int32{.cdecl,
-    dynlib: dllName, importc: "PQputnbytes".}
-proc pqendcopy*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-                                       importc: "PQendcopy".}
-proc pqsetnonblocking*(conn: PPGconn, arg: int32): int32{.cdecl,
-    dynlib: dllName, importc: "PQsetnonblocking".}
-proc pqisnonblocking*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-    importc: "PQisnonblocking".}
-proc pqflush*(conn: PPGconn): int32{.cdecl, dynlib: dllName, importc: "PQflush".}
-proc pqfn*(conn: PPGconn, fnid: int32, result_buf, result_len: ptr int32,
-           result_is_int: int32, args: PPQArgBlock, nargs: int32): PPGresult{.
-    cdecl, dynlib: dllName, importc: "PQfn".}
-proc pqresultStatus*(res: PPGresult): ExecStatusType{.cdecl, dynlib: dllName,
-    importc: "PQresultStatus".}
-proc pqresStatus*(status: ExecStatusType): cstring{.cdecl, dynlib: dllName,
-    importc: "PQresStatus".}
-proc pqresultErrorMessage*(res: PPGresult): cstring{.cdecl, dynlib: dllName,
-    importc: "PQresultErrorMessage".}
-proc pqresultErrorField*(res: PPGresult, fieldcode: int32): cstring{.cdecl,
-    dynlib: dllName, importc: "PQresultErrorField".}
-proc pqntuples*(res: PPGresult): int32{.cdecl, dynlib: dllName,
-                                        importc: "PQntuples".}
-proc pqnfields*(res: PPGresult): int32{.cdecl, dynlib: dllName,
-                                        importc: "PQnfields".}
-proc pqbinaryTuples*(res: PPGresult): int32{.cdecl, dynlib: dllName,
-    importc: "PQbinaryTuples".}
-proc pqfname*(res: PPGresult, field_num: int32): cstring{.cdecl,
-    dynlib: dllName, importc: "PQfname".}
-proc pqfnumber*(res: PPGresult, field_name: cstring): int32{.cdecl,
-    dynlib: dllName, importc: "PQfnumber".}
-proc pqftable*(res: PPGresult, field_num: int32): Oid{.cdecl, dynlib: dllName,
-    importc: "PQftable".}
-proc pqftablecol*(res: PPGresult, field_num: int32): int32{.cdecl,
-    dynlib: dllName, importc: "PQftablecol".}
-proc pqfformat*(res: PPGresult, field_num: int32): int32{.cdecl,
-    dynlib: dllName, importc: "PQfformat".}
-proc pqftype*(res: PPGresult, field_num: int32): Oid{.cdecl, dynlib: dllName,
-    importc: "PQftype".}
-proc pqfsize*(res: PPGresult, field_num: int32): int32{.cdecl, dynlib: dllName,
-    importc: "PQfsize".}
-proc pqfmod*(res: PPGresult, field_num: int32): int32{.cdecl, dynlib: dllName,
-    importc: "PQfmod".}
-proc pqcmdStatus*(res: PPGresult): cstring{.cdecl, dynlib: dllName,
-    importc: "PQcmdStatus".}
-proc pqoidStatus*(res: PPGresult): cstring{.cdecl, dynlib: dllName,
-    importc: "PQoidStatus".}
-proc pqoidValue*(res: PPGresult): Oid{.cdecl, dynlib: dllName,
-                                       importc: "PQoidValue".}
-proc pqcmdTuples*(res: PPGresult): cstring{.cdecl, dynlib: dllName,
-    importc: "PQcmdTuples".}
-proc pqgetvalue*(res: PPGresult, tup_num: int32, field_num: int32): cstring{.
-    cdecl, dynlib: dllName, importc: "PQgetvalue".}
-proc pqgetlength*(res: PPGresult, tup_num: int32, field_num: int32): int32{.
-    cdecl, dynlib: dllName, importc: "PQgetlength".}
-proc pqgetisnull*(res: PPGresult, tup_num: int32, field_num: int32): int32{.
-    cdecl, dynlib: dllName, importc: "PQgetisnull".}
-proc pqclear*(res: PPGresult){.cdecl, dynlib: dllName, importc: "PQclear".}
-proc pqfreemem*(p: pointer){.cdecl, dynlib: dllName, importc: "PQfreemem".}
-proc pqmakeEmptyPGresult*(conn: PPGconn, status: ExecStatusType): PPGresult{.
-    cdecl, dynlib: dllName, importc: "PQmakeEmptyPGresult".}
-proc pqescapeString*(till, `from`: cstring, len: int): int{.cdecl,
-    dynlib: dllName, importc: "PQescapeString".}
-proc pqescapeBytea*(bintext: cstring, binlen: int, bytealen: var int): cstring{.
-    cdecl, dynlib: dllName, importc: "PQescapeBytea".}
-proc pqunescapeBytea*(strtext: cstring, retbuflen: var int): cstring{.cdecl,
-    dynlib: dllName, importc: "PQunescapeBytea".}
-proc pqprint*(fout: File, res: PPGresult, ps: PPQprintOpt){.cdecl,
-    dynlib: dllName, importc: "PQprint".}
-proc pqdisplayTuples*(res: PPGresult, fp: File, fillAlign: int32,
-                      fieldSep: cstring, printHeader: int32, quiet: int32){.
-    cdecl, dynlib: dllName, importc: "PQdisplayTuples".}
-proc pqprintTuples*(res: PPGresult, fout: File, printAttName: int32,
-                    terseOutput: int32, width: int32){.cdecl, dynlib: dllName,
-    importc: "PQprintTuples".}
-proc lo_open*(conn: PPGconn, lobjId: Oid, mode: int32): int32{.cdecl,
-    dynlib: dllName, importc: "lo_open".}
-proc lo_close*(conn: PPGconn, fd: int32): int32{.cdecl, dynlib: dllName,
-    importc: "lo_close".}
-proc lo_read*(conn: PPGconn, fd: int32, buf: cstring, length: int): int32{.
-    cdecl, dynlib: dllName, importc: "lo_read".}
-proc lo_write*(conn: PPGconn, fd: int32, buf: cstring, length: int): int32{.
-    cdecl, dynlib: dllName, importc: "lo_write".}
-proc lo_lseek*(conn: PPGconn, fd: int32, offset: int32, whence: int32): int32{.
-    cdecl, dynlib: dllName, importc: "lo_lseek".}
-proc lo_creat*(conn: PPGconn, mode: int32): Oid{.cdecl, dynlib: dllName,
-    importc: "lo_creat".}
-proc lo_tell*(conn: PPGconn, fd: int32): int32{.cdecl, dynlib: dllName,
-    importc: "lo_tell".}
-proc lo_unlink*(conn: PPGconn, lobjId: Oid): int32{.cdecl, dynlib: dllName,
-    importc: "lo_unlink".}
-proc lo_import*(conn: PPGconn, filename: cstring): Oid{.cdecl, dynlib: dllName,
-    importc: "lo_import".}
-proc lo_export*(conn: PPGconn, lobjId: Oid, filename: cstring): int32{.cdecl,
-    dynlib: dllName, importc: "lo_export".}
-proc pqmblen*(s: cstring, encoding: int32): int32{.cdecl, dynlib: dllName,
-    importc: "PQmblen".}
-proc pqenv2encoding*(): int32{.cdecl, dynlib: dllName, importc: "PQenv2encoding".}
-proc pqsetdb(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME: cstring): PPgConn =
-  result = pqSetdbLogin(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME, "", "")
diff --git a/lib/wrappers/sqlite3.nim b/lib/wrappers/sqlite3.nim
deleted file mode 100644
index 0276a0a65..000000000
--- a/lib/wrappers/sqlite3.nim
+++ /dev/null
@@ -1,370 +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.
-#
-
-{.deadCodeElim: on.}  # dce option deprecated
-when defined(windows):
-  when defined(nimOldDlls):
-    const Lib = "sqlite3.dll"
-  elif defined(cpu64):
-    const Lib = "sqlite3_64.dll"
-  else:
-    const Lib = "sqlite3_32.dll"
-elif defined(macosx):
-  const
-    Lib = "libsqlite3(|.0).dylib"
-else:
-  const
-    Lib = "libsqlite3.so(|.0)"
-
-const
-  SQLITE_INTEGER* = 1
-  SQLITE_FLOAT* = 2
-  SQLITE_BLOB* = 4
-  SQLITE_NULL* = 5
-  SQLITE_TEXT* = 3
-  SQLITE_UTF8* = 1
-  SQLITE_UTF16LE* = 2
-  SQLITE_UTF16BE* = 3         # Use native byte order
-  SQLITE_UTF16* = 4           # sqlite3_create_function only
-  SQLITE_ANY* = 5             #sqlite_exec return values
-  SQLITE_OK* = 0
-  SQLITE_ERROR* = 1           # SQL error or missing database
-  SQLITE_INTERNAL* = 2        # An internal logic error in SQLite
-  SQLITE_PERM* = 3            # Access permission denied
-  SQLITE_ABORT* = 4           # Callback routine requested an abort
-  SQLITE_BUSY* = 5            # The database file is locked
-  SQLITE_LOCKED* = 6          # A table in the database is locked
-  SQLITE_NOMEM* = 7           # A malloc() failed
-  SQLITE_READONLY* = 8        # Attempt to write a readonly database
-  SQLITE_INTERRUPT* = 9       # Operation terminated by sqlite3_interrupt()
-  SQLITE_IOERR* = 10          # Some kind of disk I/O error occurred
-  SQLITE_CORRUPT* = 11        # The database disk image is malformed
-  SQLITE_NOTFOUND* = 12       # (Internal Only) Table or record not found
-  SQLITE_FULL* = 13           # Insertion failed because database is full
-  SQLITE_CANTOPEN* = 14       # Unable to open the database file
-  SQLITE_PROTOCOL* = 15       # Database lock protocol error
-  SQLITE_EMPTY* = 16          # Database is empty
-  SQLITE_SCHEMA* = 17         # The database schema changed
-  SQLITE_TOOBIG* = 18         # Too much data for one row of a table
-  SQLITE_CONSTRAINT* = 19     # Abort due to contraint violation
-  SQLITE_MISMATCH* = 20       # Data type mismatch
-  SQLITE_MISUSE* = 21         # Library used incorrectly
-  SQLITE_NOLFS* = 22          # Uses OS features not supported on host
-  SQLITE_AUTH* = 23           # Authorization denied
-  SQLITE_FORMAT* = 24         # Auxiliary database format error
-  SQLITE_RANGE* = 25          # 2nd parameter to sqlite3_bind out of range
-  SQLITE_NOTADB* = 26         # File opened that is not a database file
-  SQLITE_ROW* = 100           # sqlite3_step() has another row ready
-  SQLITE_DONE* = 101          # sqlite3_step() has finished executing
-  SQLITE_COPY* = 0
-  SQLITE_CREATE_INDEX* = 1
-  SQLITE_CREATE_TABLE* = 2
-  SQLITE_CREATE_TEMP_INDEX* = 3
-  SQLITE_CREATE_TEMP_TABLE* = 4
-  SQLITE_CREATE_TEMP_TRIGGER* = 5
-  SQLITE_CREATE_TEMP_VIEW* = 6
-  SQLITE_CREATE_TRIGGER* = 7
-  SQLITE_CREATE_VIEW* = 8
-  SQLITE_DELETE* = 9
-  SQLITE_DROP_INDEX* = 10
-  SQLITE_DROP_TABLE* = 11
-  SQLITE_DROP_TEMP_INDEX* = 12
-  SQLITE_DROP_TEMP_TABLE* = 13
-  SQLITE_DROP_TEMP_TRIGGER* = 14
-  SQLITE_DROP_TEMP_VIEW* = 15
-  SQLITE_DROP_TRIGGER* = 16
-  SQLITE_DROP_VIEW* = 17
-  SQLITE_INSERT* = 18
-  SQLITE_PRAGMA* = 19
-  SQLITE_READ* = 20
-  SQLITE_SELECT* = 21
-  SQLITE_TRANSACTION* = 22
-  SQLITE_UPDATE* = 23
-  SQLITE_ATTACH* = 24
-  SQLITE_DETACH* = 25
-  SQLITE_ALTER_TABLE* = 26
-  SQLITE_REINDEX* = 27
-  SQLITE_DENY* = 1
-  SQLITE_IGNORE* = 2          # Original from sqlite3.h:
-                              #define SQLITE_STATIC      ((void(*)(void *))0)
-                              #define SQLITE_TRANSIENT   ((void(*)(void *))-1)
-  SQLITE_DETERMINISTIC* = 0x800
-
-type
-  Sqlite3 {.pure, final.} = object
-  PSqlite3* = ptr Sqlite3
-  PPSqlite3* = ptr PSqlite3
-  Context{.pure, final.} = object
-  Pcontext* = ptr Context
-  Tstmt{.pure, final.} = object
-  Pstmt* = ptr Tstmt
-  Value{.pure, final.} = object
-  Pvalue* = ptr Value
-  PValueArg* = array[0..127, Pvalue]
-
-  Callback* = proc (para1: pointer, para2: int32, para3,
-                     para4: cstringArray): int32{.cdecl.}
-  Tbind_destructor_func* = proc (para1: pointer){.cdecl, locks: 0, tags: [], gcsafe.}
-  Create_function_step_func* = proc (para1: Pcontext, para2: int32,
-                                      para3: PValueArg){.cdecl.}
-  Create_function_func_func* = proc (para1: Pcontext, para2: int32,
-                                      para3: PValueArg){.cdecl.}
-  Create_function_final_func* = proc (para1: Pcontext){.cdecl.}
-  Result_func* = proc (para1: pointer){.cdecl.}
-  Create_collation_func* = proc (para1: pointer, para2: int32, para3: pointer,
-                                  para4: int32, para5: pointer): int32{.cdecl.}
-  Collation_needed_func* = proc (para1: pointer, para2: PSqlite3, eTextRep: int32,
-                                  para4: cstring){.cdecl.}
-{.deprecated: [TSqlite3: Sqlite3, TContext: Context, Tvalue: Value,
-    Tcallback: Callback, Tcreate_function_step_func: Create_function_step_func,
-    Tcreate_function_func_func: Create_function_func_func,
-    Tcreate_function_final_func: Create_function_final_func,
-    Tresult_func: Result_func, Tcreate_collation_func: Create_collation_func,
-    Tcollation_needed_func: Collation_needed_func].}
-
-const
-  SQLITE_STATIC* = nil
-  SQLITE_TRANSIENT* = cast[Tbind_destructor_func](-1)
-
-proc close*(para1: PSqlite3): int32{.cdecl, dynlib: Lib, importc: "sqlite3_close".}
-proc exec*(para1: PSqlite3, sql: cstring, para3: Callback, para4: pointer,
-           errmsg: var cstring): int32{.cdecl, dynlib: Lib,
-                                        importc: "sqlite3_exec".}
-proc last_insert_rowid*(para1: PSqlite3): int64{.cdecl, dynlib: Lib,
-    importc: "sqlite3_last_insert_rowid".}
-proc changes*(para1: PSqlite3): int32{.cdecl, dynlib: Lib, importc: "sqlite3_changes".}
-proc total_changes*(para1: PSqlite3): int32{.cdecl, dynlib: Lib,
-                                      importc: "sqlite3_total_changes".}
-proc interrupt*(para1: PSqlite3){.cdecl, dynlib: Lib, importc: "sqlite3_interrupt".}
-proc complete*(sql: cstring): int32{.cdecl, dynlib: Lib,
-                                     importc: "sqlite3_complete".}
-proc complete16*(sql: pointer): int32{.cdecl, dynlib: Lib,
-                                       importc: "sqlite3_complete16".}
-proc busy_handler*(para1: PSqlite3,
-                   para2: proc (para1: pointer, para2: int32): int32{.cdecl.},
-                   para3: pointer): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_busy_handler".}
-proc busy_timeout*(para1: PSqlite3, ms: int32): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_busy_timeout".}
-proc get_table*(para1: PSqlite3, sql: cstring, resultp: var cstringArray,
-                nrow, ncolumn: var cint, errmsg: ptr cstring): int32{.cdecl,
-    dynlib: Lib, importc: "sqlite3_get_table".}
-proc free_table*(result: cstringArray){.cdecl, dynlib: Lib,
-                                        importc: "sqlite3_free_table".}
-  # Todo: see how translate sqlite3_mprintf, sqlite3_vmprintf, sqlite3_snprintf
-  # function sqlite3_mprintf(_para1:Pchar; args:array of const):Pchar;cdecl; external Sqlite3Lib name 'sqlite3_mprintf';
-proc mprintf*(para1: cstring): cstring{.cdecl, varargs, dynlib: Lib,
-                                        importc: "sqlite3_mprintf".}
-  #function sqlite3_vmprintf(_para1:Pchar; _para2:va_list):Pchar;cdecl; external Sqlite3Lib name 'sqlite3_vmprintf';
-proc free*(z: cstring){.cdecl, dynlib: Lib, importc: "sqlite3_free".}
-  #function sqlite3_snprintf(_para1:longint; _para2:Pchar; _para3:Pchar; args:array of const):Pchar;cdecl; external Sqlite3Lib name 'sqlite3_snprintf';
-proc snprintf*(para1: int32, para2: cstring, para3: cstring): cstring{.cdecl,
-    dynlib: Lib, varargs, importc: "sqlite3_snprintf".}
-proc set_authorizer*(para1: PSqlite3, xAuth: proc (para1: pointer, para2: int32,
-    para3: cstring, para4: cstring, para5: cstring, para6: cstring): int32{.
-    cdecl.}, pUserData: pointer): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_set_authorizer".}
-proc trace*(para1: PSqlite3, xTrace: proc (para1: pointer, para2: cstring){.cdecl.},
-            para3: pointer): pointer{.cdecl, dynlib: Lib,
-                                      importc: "sqlite3_trace".}
-proc progress_handler*(para1: PSqlite3, para2: int32,
-                       para3: proc (para1: pointer): int32{.cdecl.},
-                       para4: pointer){.cdecl, dynlib: Lib,
-                                        importc: "sqlite3_progress_handler".}
-proc commit_hook*(para1: PSqlite3, para2: proc (para1: pointer): int32{.cdecl.},
-                  para3: pointer): pointer{.cdecl, dynlib: Lib,
-    importc: "sqlite3_commit_hook".}
-proc open*(filename: cstring, ppDb: var PSqlite3): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_open".}
-proc open16*(filename: pointer, ppDb: var PSqlite3): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_open16".}
-proc errcode*(db: PSqlite3): int32{.cdecl, dynlib: Lib, importc: "sqlite3_errcode".}
-proc errmsg*(para1: PSqlite3): cstring{.cdecl, dynlib: Lib, importc: "sqlite3_errmsg".}
-proc errmsg16*(para1: PSqlite3): pointer{.cdecl, dynlib: Lib,
-                                   importc: "sqlite3_errmsg16".}
-proc prepare*(db: PSqlite3, zSql: cstring, nBytes: int32, ppStmt: var Pstmt,
-              pzTail: ptr cstring): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_prepare".}
-
-proc prepare_v2*(db: PSqlite3, zSql: cstring, nByte: cint, ppStmt: var Pstmt,
-                pzTail: ptr cstring): cint {.
-                importc: "sqlite3_prepare_v2", cdecl, dynlib: Lib.}
-
-proc prepare16*(db: PSqlite3, zSql: pointer, nBytes: int32, ppStmt: var Pstmt,
-                pzTail: var pointer): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_prepare16".}
-proc bind_blob*(para1: Pstmt, para2: int32, para3: pointer, n: int32,
-                para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_bind_blob".}
-proc bind_double*(para1: Pstmt, para2: int32, para3: float64): int32{.cdecl,
-    dynlib: Lib, importc: "sqlite3_bind_double".}
-proc bind_int*(para1: Pstmt, para2: int32, para3: int32): int32{.cdecl,
-    dynlib: Lib, importc: "sqlite3_bind_int".}
-proc bind_int64*(para1: Pstmt, para2: int32, para3: int64): int32{.cdecl,
-    dynlib: Lib, importc: "sqlite3_bind_int64".}
-proc bind_null*(para1: Pstmt, para2: int32): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_bind_null".}
-proc bind_text*(para1: Pstmt, para2: int32, para3: cstring, n: int32,
-                para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_bind_text".}
-proc bind_text16*(para1: Pstmt, para2: int32, para3: pointer, para4: int32,
-                  para5: Tbind_destructor_func): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_bind_text16".}
-  #function sqlite3_bind_value(_para1:Psqlite3_stmt; _para2:longint; _para3:Psqlite3_value):longint;cdecl; external Sqlite3Lib name 'sqlite3_bind_value';
-  #These overloaded functions were introduced to allow the use of SQLITE_STATIC and SQLITE_TRANSIENT
-  #It's the c world man ;-)
-proc bind_blob*(para1: Pstmt, para2: int32, para3: pointer, n: int32,
-                para5: int32): int32{.cdecl, dynlib: Lib,
-                                      importc: "sqlite3_bind_blob".}
-proc bind_text*(para1: Pstmt, para2: int32, para3: cstring, n: int32,
-                para5: int32): int32{.cdecl, dynlib: Lib,
-                                      importc: "sqlite3_bind_text".}
-proc bind_text16*(para1: Pstmt, para2: int32, para3: pointer, para4: int32,
-                  para5: int32): int32{.cdecl, dynlib: Lib,
-                                        importc: "sqlite3_bind_text16".}
-proc bind_parameter_count*(para1: Pstmt): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_bind_parameter_count".}
-proc bind_parameter_name*(para1: Pstmt, para2: int32): cstring{.cdecl,
-    dynlib: Lib, importc: "sqlite3_bind_parameter_name".}
-proc bind_parameter_index*(para1: Pstmt, zName: cstring): int32{.cdecl,
-    dynlib: Lib, importc: "sqlite3_bind_parameter_index".}
-proc clear_bindings*(para1: Pstmt): int32 {.cdecl,
-    dynlib: Lib, importc: "sqlite3_clear_bindings".}
-proc column_count*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_column_count".}
-proc column_name*(para1: Pstmt, para2: int32): cstring{.cdecl, dynlib: Lib,
-    importc: "sqlite3_column_name".}
-proc column_table_name*(para1: Pstmt; para2: int32): cstring{.cdecl, dynlib: Lib,
-    importc: "sqlite3_column_table_name".}
-proc column_name16*(para1: Pstmt, para2: int32): pointer{.cdecl, dynlib: Lib,
-    importc: "sqlite3_column_name16".}
-proc column_decltype*(para1: Pstmt, i: int32): cstring{.cdecl, dynlib: Lib,
-    importc: "sqlite3_column_decltype".}
-proc column_decltype16*(para1: Pstmt, para2: int32): pointer{.cdecl,
-    dynlib: Lib, importc: "sqlite3_column_decltype16".}
-proc step*(para1: Pstmt): int32{.cdecl, dynlib: Lib, importc: "sqlite3_step".}
-proc data_count*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
-                                       importc: "sqlite3_data_count".}
-proc column_blob*(para1: Pstmt, iCol: int32): pointer{.cdecl, dynlib: Lib,
-    importc: "sqlite3_column_blob".}
-proc column_bytes*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_column_bytes".}
-proc column_bytes16*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_column_bytes16".}
-proc column_double*(para1: Pstmt, iCol: int32): float64{.cdecl, dynlib: Lib,
-    importc: "sqlite3_column_double".}
-proc column_int*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_column_int".}
-proc column_int64*(para1: Pstmt, iCol: int32): int64{.cdecl, dynlib: Lib,
-    importc: "sqlite3_column_int64".}
-proc column_text*(para1: Pstmt, iCol: int32): cstring{.cdecl, dynlib: Lib,
-    importc: "sqlite3_column_text".}
-proc column_text16*(para1: Pstmt, iCol: int32): pointer{.cdecl, dynlib: Lib,
-    importc: "sqlite3_column_text16".}
-proc column_type*(para1: Pstmt, iCol: int32): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_column_type".}
-proc finalize*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib,
-                                     importc: "sqlite3_finalize".}
-proc reset*(pStmt: Pstmt): int32{.cdecl, dynlib: Lib, importc: "sqlite3_reset".}
-proc create_function*(para1: PSqlite3, zFunctionName: cstring, nArg: int32,
-                      eTextRep: int32, para5: pointer,
-                      xFunc: Create_function_func_func,
-                      xStep: Create_function_step_func,
-                      xFinal: Create_function_final_func): int32{.cdecl,
-    dynlib: Lib, importc: "sqlite3_create_function".}
-proc create_function16*(para1: PSqlite3, zFunctionName: pointer, nArg: int32,
-                        eTextRep: int32, para5: pointer,
-                        xFunc: Create_function_func_func,
-                        xStep: Create_function_step_func,
-                        xFinal: Create_function_final_func): int32{.cdecl,
-    dynlib: Lib, importc: "sqlite3_create_function16".}
-proc aggregate_count*(para1: Pcontext): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_aggregate_count".}
-proc value_blob*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
-    importc: "sqlite3_value_blob".}
-proc value_bytes*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_value_bytes".}
-proc value_bytes16*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
-    importc: "sqlite3_value_bytes16".}
-proc value_double*(para1: Pvalue): float64{.cdecl, dynlib: Lib,
-    importc: "sqlite3_value_double".}
-proc value_int*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
-                                       importc: "sqlite3_value_int".}
-proc value_int64*(para1: Pvalue): int64{.cdecl, dynlib: Lib,
-    importc: "sqlite3_value_int64".}
-proc value_text*(para1: Pvalue): cstring{.cdecl, dynlib: Lib,
-    importc: "sqlite3_value_text".}
-proc value_text16*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
-    importc: "sqlite3_value_text16".}
-proc value_text16le*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
-    importc: "sqlite3_value_text16le".}
-proc value_text16be*(para1: Pvalue): pointer{.cdecl, dynlib: Lib,
-    importc: "sqlite3_value_text16be".}
-proc value_type*(para1: Pvalue): int32{.cdecl, dynlib: Lib,
-                                        importc: "sqlite3_value_type".}
-proc aggregate_context*(para1: Pcontext, nBytes: int32): pointer{.cdecl,
-    dynlib: Lib, importc: "sqlite3_aggregate_context".}
-proc user_data*(para1: Pcontext): pointer{.cdecl, dynlib: Lib,
-    importc: "sqlite3_user_data".}
-proc get_auxdata*(para1: Pcontext, para2: int32): pointer{.cdecl, dynlib: Lib,
-    importc: "sqlite3_get_auxdata".}
-proc set_auxdata*(para1: Pcontext, para2: int32, para3: pointer,
-                  para4: proc (para1: pointer){.cdecl.}){.cdecl, dynlib: Lib,
-    importc: "sqlite3_set_auxdata".}
-proc result_blob*(para1: Pcontext, para2: pointer, para3: int32,
-                  para4: Result_func){.cdecl, dynlib: Lib,
-                                        importc: "sqlite3_result_blob".}
-proc result_double*(para1: Pcontext, para2: float64){.cdecl, dynlib: Lib,
-    importc: "sqlite3_result_double".}
-proc result_error*(para1: Pcontext, para2: cstring, para3: int32){.cdecl,
-    dynlib: Lib, importc: "sqlite3_result_error".}
-proc result_error16*(para1: Pcontext, para2: pointer, para3: int32){.cdecl,
-    dynlib: Lib, importc: "sqlite3_result_error16".}
-proc result_int*(para1: Pcontext, para2: int32){.cdecl, dynlib: Lib,
-    importc: "sqlite3_result_int".}
-proc result_int64*(para1: Pcontext, para2: int64){.cdecl, dynlib: Lib,
-    importc: "sqlite3_result_int64".}
-proc result_null*(para1: Pcontext){.cdecl, dynlib: Lib,
-                                    importc: "sqlite3_result_null".}
-proc result_text*(para1: Pcontext, para2: cstring, para3: int32,
-                  para4: Result_func){.cdecl, dynlib: Lib,
-                                        importc: "sqlite3_result_text".}
-proc result_text16*(para1: Pcontext, para2: pointer, para3: int32,
-                    para4: Result_func){.cdecl, dynlib: Lib,
-    importc: "sqlite3_result_text16".}
-proc result_text16le*(para1: Pcontext, para2: pointer, para3: int32,
-                      para4: Result_func){.cdecl, dynlib: Lib,
-    importc: "sqlite3_result_text16le".}
-proc result_text16be*(para1: Pcontext, para2: pointer, para3: int32,
-                      para4: Result_func){.cdecl, dynlib: Lib,
-    importc: "sqlite3_result_text16be".}
-proc result_value*(para1: Pcontext, para2: Pvalue){.cdecl, dynlib: Lib,
-    importc: "sqlite3_result_value".}
-proc create_collation*(para1: PSqlite3, zName: cstring, eTextRep: int32,
-                       para4: pointer, xCompare: Create_collation_func): int32{.
-    cdecl, dynlib: Lib, importc: "sqlite3_create_collation".}
-proc create_collation16*(para1: PSqlite3, zName: cstring, eTextRep: int32,
-                         para4: pointer, xCompare: Create_collation_func): int32{.
-    cdecl, dynlib: Lib, importc: "sqlite3_create_collation16".}
-proc collation_needed*(para1: PSqlite3, para2: pointer, para3: Collation_needed_func): int32{.
-    cdecl, dynlib: Lib, importc: "sqlite3_collation_needed".}
-proc collation_needed16*(para1: PSqlite3, para2: pointer, para3: Collation_needed_func): int32{.
-    cdecl, dynlib: Lib, importc: "sqlite3_collation_needed16".}
-proc libversion*(): cstring{.cdecl, dynlib: Lib, importc: "sqlite3_libversion".}
-  #Alias for allowing better code portability (win32 is not working with external variables)
-proc version*(): cstring{.cdecl, dynlib: Lib, importc: "sqlite3_libversion".}
-  # Not published functions
-proc libversion_number*(): int32{.cdecl, dynlib: Lib,
-                                  importc: "sqlite3_libversion_number".}
-  #function sqlite3_key(db:Psqlite3; pKey:pointer; nKey:longint):longint;cdecl; external Sqlite3Lib name 'sqlite3_key';
-  #function sqlite3_rekey(db:Psqlite3; pKey:pointer; nKey:longint):longint;cdecl; external Sqlite3Lib name 'sqlite3_rekey';
-  #function sqlite3_sleep(_para1:longint):longint;cdecl; external Sqlite3Lib name 'sqlite3_sleep';
-  #function sqlite3_expired(_para1:Psqlite3_stmt):longint;cdecl; external Sqlite3Lib name 'sqlite3_expired';
-  #function sqlite3_global_recover:longint;cdecl; external Sqlite3Lib name 'sqlite3_global_recover';
-# implementation
diff --git a/lib/wrappers/tinyc.nim b/lib/wrappers/tinyc.nim
index f2ce92d36..8d2ba3802 100644
--- a/lib/wrappers/tinyc.nim
+++ b/lib/wrappers/tinyc.nim
@@ -12,7 +12,6 @@ type
   PccState* = ptr CcState
 
   ErrorFunc* = proc (opaque: pointer, msg: cstring) {.cdecl.}
-{.deprecated: [TccState: CcState, TErrorFunc: ErrorFunc].}
 
 proc openCCState*(): PccState {.importc: "tcc_new", cdecl.}
   ## create a new TCC compilation context
@@ -67,7 +66,7 @@ const
   OutputObj*: cint = 4 ## object file
   OutputPreprocess*: cint = 5 ## preprocessed file (used internally)
 
-proc setOutputType*(s: PCCState, outputType: cint): cint {.cdecl,
+proc setOutputType*(s: PccState, outputType: cint): cint {.cdecl,
   importc: "tcc_set_output_type".}
   ## set output type. MUST BE CALLED before any compilation
 
@@ -75,7 +74,7 @@ proc addLibraryPath*(s: PccState, pathname: cstring): cint {.cdecl,
   importc: "tcc_add_library_path".}
   ## equivalent to -Lpath option
 
-proc addLibrary*(s: PCCState, libraryname: cstring): cint {.cdecl,
+proc addLibrary*(s: PccState, libraryname: cstring): cint {.cdecl,
   importc: "tcc_add_library".}
   ## the library name is the same as the argument of the '-l' option
 
@@ -106,5 +105,3 @@ proc getSymbol*(s: PccState, name: cstring): pointer {.cdecl,
 proc setLibPath*(s: PccState, path: cstring) {.cdecl,
   importc: "tcc_set_lib_path".}
   ## set CONFIG_TCCDIR at runtime
-
-
diff --git a/nim.nimble b/nim.nimble
new file mode 100644
index 000000000..bf195b0fa
--- /dev/null
+++ b/nim.nimble
@@ -0,0 +1,17 @@
+include "lib/system/compilation.nim"
+version = $NimMajor & "." & $NimMinor & "." & $NimPatch
+author = "Andreas Rumpf"
+description = "Nim package providing the compiler binary"
+license = "MIT"
+
+bin = @["compiler/nim", "nimsuggest/nimsuggest"]
+skipFiles = @["azure-pipelines.yml" , "build_all.bat" , "build_all.sh" , "build_nimble.bat" , "build_nimble.sh" , "changelog.md" , "koch.nim.cfg" , "nimblemeta.json" , "readme.md" , "security.md" ]
+skipDirs = @["build" , "changelogs" , "ci" , "csources_v2" , "drnim" , "nimdoc", "testament"]
+
+before install:
+  when defined(windows):
+    if not "bin\nim.exe".fileExists:
+      exec "build_all.bat"
+  else:
+    if not "bin/nim".fileExists:
+      exec "./build_all.sh"
diff --git a/nimdoc/extlinks/project/doc/manual.md b/nimdoc/extlinks/project/doc/manual.md
new file mode 100644
index 000000000..d44b5ca39
--- /dev/null
+++ b/nimdoc/extlinks/project/doc/manual.md
@@ -0,0 +1,17 @@
+===================
+Nothing User Manual
+===================
+
+.. importdoc:: ../main.nim, .. / sub / submodule.nim, ../../util.nim
+
+First section
+=============
+
+Second *section* &
+==================
+
+Ref. [`</a>`] or [submoduleInt] from [module nimdoc/extlinks/project/sub/submodule].
+
+Ref. [proc mainfunction*(): int].
+
+Ref. [utilfunction(x: int)].
diff --git a/nimdoc/extlinks/project/expected/_._/util.html b/nimdoc/extlinks/project/expected/_._/util.html
new file mode 100644
index 000000000..32dab9216
--- /dev/null
+++ b/nimdoc/extlinks/project/expected/_._/util.html
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--  This file is generated by Nim. -->
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>nimdoc/extlinks/util</title>
+
+<!-- Google fonts -->
+<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
+<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
+
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
+<!-- CSS -->
+<link rel="stylesheet" type="text/css" href="../nimdoc.out.css">
+
+<!-- JS -->
+<script type="text/javascript" src="../dochack.js"></script>
+</head>
+<body>
+  <div class="document" id="documentId">
+    <div class="container">
+      <h1 class="title">nimdoc/extlinks/util</h1>
+      <div class="row">
+  <div class="three columns">
+    <div class="theme-select-wrapper">
+      <label for="theme-select">Theme:&nbsp;</label>
+      <select id="theme-select" onchange="setTheme(this.value)">
+        <option value="auto">🌗 Match OS</option>
+        <option value="dark">🌑 Dark</option>
+        <option value="light">🌕 Light</option>
+      </select>
+    </div>
+    <div id="global-links">
+      <ul class="simple">
+        <li><a id="indexLink" href="../theindex.html">Index</a></li>
+      </ul>
+    </div>
+    <div id="searchInputDiv">
+      Search: <input type="search" id="searchInput" oninput="search()"/>
+    </div>
+    <div>
+      Group by:
+      <select onchange="groupBy(this.value)">
+        <option value="section">Section</option>
+        <option value="type">Type</option>
+      </select>
+    </div>
+    <ul class="simple simple-toc" id="toc-list">
+  <li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#12" id="62">Procs</a></summary>
+    <ul class="simple simple-toc-section">
+      <ul class="simple nested-toc-section">utilfunction
+  <li><a class="reference" href="#utilfunction%2Cint" title="utilfunction(x: int): int">utilfunction(x: int): int</a></li>
+
+</ul>
+
+    </ul>
+  </details>
+</li>
+
+</ul>
+
+  </div>
+  <div class="nine columns" id="content">
+    
+    <div id="tocRoot"></div>
+    
+    <p class="module-desc"></p>
+    <div class="section" id="12">
+  <h1><a class="toc-backref" href="#12">Procs</a></h1>
+  <dl class="item">
+    <div id="utilfunction-procs-all">
+  <div id="utilfunction,int">
+  <dt><pre><span class="Keyword">proc</span> <a href="#utilfunction%2Cint"><span class="Identifier">utilfunction</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+
+  </dl>
+</div>
+
+  </div>
+</div>
+
+      <div class="twelve-columns footer">
+        <span class="nim-sprite"></span>
+        <br>
+        <small style="color: var(--hint);">Made with Nim. Generated: 1970-01-02 03:46:40 UTC</small>
+      </div>
+    </div>
+  </div>
+  
+</body>
+</html>
diff --git a/nimdoc/extlinks/project/expected/_._/util.idx b/nimdoc/extlinks/project/expected/_._/util.idx
new file mode 100644
index 000000000..d83d8c97d
--- /dev/null
+++ b/nimdoc/extlinks/project/expected/_._/util.idx
@@ -0,0 +1,2 @@
+nimTitle	util	_._/util.html	module nimdoc/extlinks/util		0
+nim	utilfunction	_._/util.html#utilfunction,int	proc utilfunction(x: int): int		1
diff --git a/nimdoc/extlinks/project/expected/doc/manual.html b/nimdoc/extlinks/project/expected/doc/manual.html
new file mode 100644
index 000000000..2946f803a
--- /dev/null
+++ b/nimdoc/extlinks/project/expected/doc/manual.html
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--  This file is generated by Nim. -->
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Nothing User Manual</title>
+
+<!-- Google fonts -->
+<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
+<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
+
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
+<!-- CSS -->
+<link rel="stylesheet" type="text/css" href="../nimdoc.out.css">
+
+<!-- JS -->
+<script type="text/javascript" src="../dochack.js"></script>
+</head>
+<body>
+  <div class="document" id="documentId">
+    <div class="container">
+      <h1 class="title">Nothing User Manual</h1>
+      
+<h1 id="first-section">First section</h1>
+<h1 id="second-section-amp">Second <em>section</em> &amp;</h1><p>Ref. <a class="reference external" href="../sub/submodule.html#&lt;/a&gt;,int,int">submodule: `&lt;/a&gt;`</a> or <a class="reference external" href="../sub/submodule.html#submoduleInt">submodule: submoduleInt</a> from <a class="reference external" href="../sub/submodule.html">module nimdoc/extlinks/project/sub/submodule</a>.</p>
+<p>Ref. <a class="reference external" href="../main.html#mainfunction">main: proc mainfunction*(): int</a>.</p>
+<p>Ref. <a class="reference external" href="../_._/util.html#utilfunction,int">util: utilfunction(x: int)</a>. </p>
+
+
+
+      <div class="twelve-columns footer">
+        <span class="nim-sprite"></span>
+        <br>
+        <small style="color: var(--hint);">Made with Nim. Generated: 1970-01-02 03:46:40 UTC</small>
+      </div>
+    </div>
+  </div>
+  
+</body>
+</html>
diff --git a/nimdoc/extlinks/project/expected/doc/manual.idx b/nimdoc/extlinks/project/expected/doc/manual.idx
new file mode 100644
index 000000000..158a758f0
--- /dev/null
+++ b/nimdoc/extlinks/project/expected/doc/manual.idx
@@ -0,0 +1,3 @@
+markupTitle	Nothing User Manual	doc/manual.html	Nothing User Manual		0
+heading	First section	doc/manual.html#first-section	 First section		0
+heading	Second section &	doc/manual.html#second-section-amp	 Second <em>section</em> &amp;		0
diff --git a/nimdoc/extlinks/project/expected/main.html b/nimdoc/extlinks/project/expected/main.html
new file mode 100644
index 000000000..1e7c9c126
--- /dev/null
+++ b/nimdoc/extlinks/project/expected/main.html
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--  This file is generated by Nim. -->
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>nimdoc/extlinks/project/main</title>
+
+<!-- Google fonts -->
+<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
+<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
+
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
+<!-- CSS -->
+<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
+
+<!-- JS -->
+<script type="text/javascript" src="dochack.js"></script>
+</head>
+<body>
+  <div class="document" id="documentId">
+    <div class="container">
+      <h1 class="title">nimdoc/extlinks/project/main</h1>
+      <div class="row">
+  <div class="three columns">
+    <div class="theme-select-wrapper">
+      <label for="theme-select">Theme:&nbsp;</label>
+      <select id="theme-select" onchange="setTheme(this.value)">
+        <option value="auto">🌗 Match OS</option>
+        <option value="dark">🌑 Dark</option>
+        <option value="light">🌕 Light</option>
+      </select>
+    </div>
+    <div id="global-links">
+      <ul class="simple">
+        <li><a id="indexLink" href="theindex.html">Index</a></li>
+      </ul>
+    </div>
+    <div id="searchInputDiv">
+      Search: <input type="search" id="searchInput" oninput="search()"/>
+    </div>
+    <div>
+      Group by:
+      <select onchange="groupBy(this.value)">
+        <option value="section">Section</option>
+        <option value="type">Type</option>
+      </select>
+    </div>
+    <ul class="simple simple-toc" id="toc-list">
+  <li><a class="reference" id="my-heading_toc" href="#my-heading">my heading</a></li>
+<li>
+  <a class="reference reference-toplevel" href="#6" id="56">Imports</a>
+</li>
+<li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#7" id="57">Types</a></summary>
+    <ul class="simple simple-toc-section">
+      <li><a class="reference" href="#A" title="A = object">A</a></li>
+
+    </ul>
+  </details>
+</li>
+<li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#12" id="62">Procs</a></summary>
+    <ul class="simple simple-toc-section">
+      <ul class="simple nested-toc-section">mainfunction
+  <li><a class="reference" href="#mainfunction" title="mainfunction(): int">mainfunction(): int</a></li>
+
+</ul>
+
+    </ul>
+  </details>
+</li>
+
+</ul>
+
+  </div>
+  <div class="nine columns" id="content">
+    
+    <div id="tocRoot"></div>
+    
+    <p class="module-desc">
+<h1><a class="toc-backref" id="my-heading" href="#my-heading">my heading</a></h1><p>See also <a class="reference external" href="_._/util.html">module nimdoc/extlinks/util</a> or <a class="reference external" href="sub/submodule.html">nimdoc/extlinks/project/sub/submodule module</a>.</p>
+<p>Ref. <a class="reference external" href="sub/submodule.html#&lt;/a&gt;,int,int">submodule: `&lt;/a&gt;` proc</a>.</p>
+<p>Ref. <a class="reference external" href="doc/manual.html#first-section">Nothing User Manual: First section</a> or <a class="reference external" href="doc/manual.html#second-section-amp">Nothing User Manual: Second section &amp;</a> from <a class="reference external" href="doc/manual.html">Nothing User Manual</a>.</p>
+</p>
+    <div class="section" id="6">
+  <h1><a class="toc-backref" href="#6">Imports</a></h1>
+  <dl class="item">
+    <a class="reference external" href="_._/util.html">../util</a>, <a class="reference external" href="sub/submodule.html">sub/submodule</a>
+  </dl>
+</div>
+<div class="section" id="7">
+  <h1><a class="toc-backref" href="#7">Types</a></h1>
+  <dl class="item">
+    <div id="A">
+  <dt><pre><a href="main.html#A"><span class="Identifier">A</span></a> <span class="Other">=</span> <span class="Keyword">object</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+  </dl>
+</div>
+<div class="section" id="12">
+  <h1><a class="toc-backref" href="#12">Procs</a></h1>
+  <dl class="item">
+    <div id="mainfunction-procs-all">
+  <div id="mainfunction">
+  <dt><pre><span class="Keyword">proc</span> <a href="#mainfunction"><span class="Identifier">mainfunction</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+
+  </dl>
+</div>
+
+  </div>
+</div>
+
+      <div class="twelve-columns footer">
+        <span class="nim-sprite"></span>
+        <br>
+        <small style="color: var(--hint);">Made with Nim. Generated: 1970-01-02 03:46:40 UTC</small>
+      </div>
+    </div>
+  </div>
+  
+</body>
+</html>
diff --git a/nimdoc/extlinks/project/expected/main.idx b/nimdoc/extlinks/project/expected/main.idx
new file mode 100644
index 000000000..d01f2e4c5
--- /dev/null
+++ b/nimdoc/extlinks/project/expected/main.idx
@@ -0,0 +1,4 @@
+nimTitle	main	main.html	module nimdoc/extlinks/project/main		0
+nim	A	main.html#A	object A		17
+nim	mainfunction	main.html#mainfunction	proc mainfunction(): int		20
+heading	my heading	main.html#my-heading	 my heading		0
diff --git a/nimdoc/extlinks/project/expected/sub/submodule.html b/nimdoc/extlinks/project/expected/sub/submodule.html
new file mode 100644
index 000000000..408ce2060
--- /dev/null
+++ b/nimdoc/extlinks/project/expected/sub/submodule.html
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--  This file is generated by Nim. -->
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>nimdoc/extlinks/project/sub/submodule</title>
+
+<!-- Google fonts -->
+<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
+<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
+
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
+<!-- CSS -->
+<link rel="stylesheet" type="text/css" href="../nimdoc.out.css">
+
+<!-- JS -->
+<script type="text/javascript" src="../dochack.js"></script>
+</head>
+<body>
+  <div class="document" id="documentId">
+    <div class="container">
+      <h1 class="title">nimdoc/extlinks/project/sub/submodule</h1>
+      <div class="row">
+  <div class="three columns">
+    <div class="theme-select-wrapper">
+      <label for="theme-select">Theme:&nbsp;</label>
+      <select id="theme-select" onchange="setTheme(this.value)">
+        <option value="auto">🌗 Match OS</option>
+        <option value="dark">🌑 Dark</option>
+        <option value="light">🌕 Light</option>
+      </select>
+    </div>
+    <div id="global-links">
+      <ul class="simple">
+        <li><a id="indexLink" href="../theindex.html">Index</a></li>
+      </ul>
+    </div>
+    <div id="searchInputDiv">
+      Search: <input type="search" id="searchInput" oninput="search()"/>
+    </div>
+    <div>
+      Group by:
+      <select onchange="groupBy(this.value)">
+        <option value="section">Section</option>
+        <option value="type">Type</option>
+      </select>
+    </div>
+    <ul class="simple simple-toc" id="toc-list">
+  <li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#7" id="57">Types</a></summary>
+    <ul class="simple simple-toc-section">
+      <li><a class="reference" href="#submoduleInt" title="submoduleInt = distinct int">submoduleInt</a></li>
+
+    </ul>
+  </details>
+</li>
+<li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#12" id="62">Procs</a></summary>
+    <ul class="simple simple-toc-section">
+      <ul class="simple nested-toc-section"></a>
+  <li><a class="reference" href="#%3C%2Fa%3E%2Cint%2Cint" title="`&lt;/a&gt;`(x, y: int): bool">`&lt;/a&gt;`(x, y: int): bool</a></li>
+
+</ul>
+
+    </ul>
+  </details>
+</li>
+
+</ul>
+
+  </div>
+  <div class="nine columns" id="content">
+    
+    <div id="tocRoot"></div>
+    
+    <p class="module-desc"><p>Ref. <a class="reference external" href="../main.html#A">main: object A</a> from <a class="reference external" href="../main.html">module nimdoc/extlinks/project/main</a>.</p>
+<p>Ref. <a class="reference external" href="../_._/util.html#utilfunction,int">util: utilfunction(x: int)</a>.</p>
+<p>Ref. <a class="reference external" href="../doc/manual.html#second-section-amp">Nothing User Manual: Second section &amp;</a> from <a class="reference external" href="../doc/manual.html">Nothing User Manual</a>.</p>
+</p>
+    <div class="section" id="7">
+  <h1><a class="toc-backref" href="#7">Types</a></h1>
+  <dl class="item">
+    <div id="submoduleInt">
+  <dt><pre><a href="submodule.html#submoduleInt"><span class="Identifier">submoduleInt</span></a> <span class="Other">=</span> <span class="Keyword">distinct</span> <span class="Identifier">int</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+  </dl>
+</div>
+<div class="section" id="12">
+  <h1><a class="toc-backref" href="#12">Procs</a></h1>
+  <dl class="item">
+    <div id="&lt;/a&gt;-procs-all">
+  <div id="</a>,int,int">
+  <dt><pre><span class="Keyword">proc</span> <a href="#%3C%2Fa%3E%2Cint%2Cint"><span class="Identifier">`&lt;/a&gt;`</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">,</span> <span class="Identifier">y</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">bool</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    Attempt to break HTML formatting.
+    
+  </dd>
+</div>
+
+</div>
+
+  </dl>
+</div>
+
+  </div>
+</div>
+
+      <div class="twelve-columns footer">
+        <span class="nim-sprite"></span>
+        <br>
+        <small style="color: var(--hint);">Made with Nim. Generated: 1970-01-02 03:46:40 UTC</small>
+      </div>
+    </div>
+  </div>
+  
+</body>
+</html>
diff --git a/nimdoc/extlinks/project/expected/sub/submodule.idx b/nimdoc/extlinks/project/expected/sub/submodule.idx
new file mode 100644
index 000000000..2b02c889e
--- /dev/null
+++ b/nimdoc/extlinks/project/expected/sub/submodule.idx
@@ -0,0 +1,3 @@
+nimTitle	submodule	sub/submodule.html	module nimdoc/extlinks/project/sub/submodule		0
+nim	`</a>`	sub/submodule.html#</a>,int,int	proc `</a>`(x, y: int): bool		9
+nim	submoduleInt	sub/submodule.html#submoduleInt	type submoduleInt		13
diff --git a/nimdoc/extlinks/project/expected/theindex.html b/nimdoc/extlinks/project/expected/theindex.html
new file mode 100644
index 000000000..cf250edd1
--- /dev/null
+++ b/nimdoc/extlinks/project/expected/theindex.html
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--  This file is generated by Nim. -->
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Index</title>
+
+<!-- Google fonts -->
+<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
+<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
+
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
+<!-- CSS -->
+<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
+
+<!-- JS -->
+<script type="text/javascript" src="dochack.js"></script>
+</head>
+<body>
+  <div class="document" id="documentId">
+    <div class="container">
+      <h1 class="title">Index</h1>
+      Documents: <a href="doc/manual.html">Nothing User Manual</a>.<br/><p />Modules: <a href="_._/util.html">../util</a>, <a href="main.html">main</a>, <a href="sub/submodule.html">sub/submodule</a>.<br/><p /><h2>API symbols</h2>
+<dl><dt><a name="%60%26lt%3B/a%26gt%3B%60" href="#%60%26lt%3B/a%26gt%3B%60"><span>`&lt;/a&gt;`:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="submodule: proc `&lt;/a&gt;`(x, y: int): bool" href="sub/submodule.html#%3C/a%3E%2Cint%2Cint">submodule: proc `&lt;/a&gt;`(x, y: int): bool</a></li>
+          </ul></dd>
+<dt><a name="A" href="#A"><span>A:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="main: object A" href="main.html#A">main: object A</a></li>
+          </ul></dd>
+<dt><a name="mainfunction" href="#mainfunction"><span>mainfunction:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="main: proc mainfunction(): int" href="main.html#mainfunction">main: proc mainfunction(): int</a></li>
+          </ul></dd>
+<dt><a name="submoduleInt" href="#submoduleInt"><span>submoduleInt:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="submodule: type submoduleInt" href="sub/submodule.html#submoduleInt">submodule: type submoduleInt</a></li>
+          </ul></dd>
+<dt><a name="utilfunction" href="#utilfunction"><span>utilfunction:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="util: proc utilfunction(x: int): int" href="_._/util.html#utilfunction%2Cint">util: proc utilfunction(x: int): int</a></li>
+          </ul></dd>
+</dl>
+      <div class="twelve-columns footer">
+        <span class="nim-sprite"></span>
+        <br>
+        <small style="color: var(--hint);">Made with Nim. Generated: 1970-01-02 03:46:40 UTC</small>
+      </div>
+    </div>
+  </div>
+  
+</body>
+</html>
diff --git a/nimdoc/extlinks/project/main.nim b/nimdoc/extlinks/project/main.nim
new file mode 100644
index 000000000..36b778af6
--- /dev/null
+++ b/nimdoc/extlinks/project/main.nim
@@ -0,0 +1,23 @@
+## my heading
+## ==========
+##
+## .. importdoc:: sub/submodule.nim, ../util.nim, doc/manual.md
+##
+## .. See also [Second&&&] and particularly [first section] and [Second section &].
+## 
+## See also [module nimdoc/extlinks/util] or [nimdoc/extlinks/project/sub/submodule module].
+##
+## Ref. [`</a>` proc].
+##
+## Ref. [First section] or [Second section &] from [Nothing User Manual].
+
+
+import ../util, sub/submodule
+
+type A* = object
+  x: int
+
+proc mainfunction*(): int =
+  # just to suppress "not used" warnings:
+  if `</a>`(1, 2):
+    result = utilfunction(0)
diff --git a/nimdoc/extlinks/project/sub/submodule.nim b/nimdoc/extlinks/project/sub/submodule.nim
new file mode 100644
index 000000000..876e00684
--- /dev/null
+++ b/nimdoc/extlinks/project/sub/submodule.nim
@@ -0,0 +1,13 @@
+## .. importdoc:: ../../util.nim, ../main.nim, ../doc/manual.md
+##
+## Ref. [object A] from [module nimdoc/extlinks/project/main].
+##
+## Ref. [utilfunction(x: int)].
+##
+## Ref. [Second section &] from [Nothing User Manual].
+
+proc `</a>`*(x, y: int): bool =
+  ## Attempt to break HTML formatting.
+  result = x < y
+
+type submoduleInt* = distinct int
diff --git a/nimdoc/extlinks/util.nim b/nimdoc/extlinks/util.nim
new file mode 100644
index 000000000..f208f98c1
--- /dev/null
+++ b/nimdoc/extlinks/util.nim
@@ -0,0 +1,2 @@
+proc utilfunction*(x: int): int =
+  x + 42
diff --git a/nimdoc/rst2html/expected/rst_examples.html b/nimdoc/rst2html/expected/rst_examples.html
new file mode 100644
index 000000000..a26704133
--- /dev/null
+++ b/nimdoc/rst2html/expected/rst_examples.html
@@ -0,0 +1,274 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--  This file is generated by Nim. -->
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Not a Nim Manual</title>
+
+<!-- Google fonts -->
+<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
+<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
+
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
+<!-- CSS -->
+<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
+
+<!-- JS -->
+<script type="text/javascript" src="dochack.js"></script>
+</head>
+<body>
+  <div class="document" id="documentId">
+    <div class="container">
+      <h1 class="title">Not a Nim Manual</h1>
+      <div class="row">
+  <div class="three columns">
+    <div class="theme-select-wrapper">
+      <label for="theme-select">Theme:&nbsp;</label>
+      <select id="theme-select" onchange="setTheme(this.value)">
+        <option value="auto">🌗 Match OS</option>
+        <option value="dark">🌑 Dark</option>
+        <option value="light">🌕 Light</option>
+      </select>
+    </div>
+    <div id="global-links">
+      <ul class="simple">
+        <li><a id="indexLink" href="theindex.html">Index</a></li>
+      </ul>
+    </div>
+    <div id="searchInputDiv">
+      Search: <input type="search" id="searchInput" oninput="search()"/>
+    </div>
+    <div>
+      Group by:
+      <select onchange="groupBy(this.value)">
+        <option value="section">Section</option>
+        <option value="type">Type</option>
+      </select>
+    </div>
+    <ul class="simple simple-toc" id="toc-list">
+  <li><a class="reference" id="about-this-document_toc" href="#about-this-document">About this document</a></li>
+<ul class="simple"><li><a class="reference" id="about-this-document-encoding_toc" href="#about-this-document-encoding">Encoding</a></li>
+<li><a class="reference" id="about-this-document-indentation_toc" href="#about-this-document-indentation">Indentation</a></li>
+<li><a class="reference" id="about-this-document-operators_toc" href="#about-this-document-operators">Operators</a></li>
+</ul><li><a class="reference" id="constants-and-constant-expressions_toc" href="#constants-and-constant-expressions">Constants and Constant Expressions</a></li>
+<ul class="simple"><li><a class="reference" id="constants-and-constant-expressions-symbol-lookup-in-generics_toc" href="#constants-and-constant-expressions-symbol-lookup-in-generics">Symbol lookup in generics</a></li>
+<ul class="simple"><li><a class="reference" id="symbol-lookup-in-generics-open-and-closed-symbols_toc" href="#symbol-lookup-in-generics-open-and-closed-symbols">Open and Closed symbols</a></li>
+<li><a class="reference" id="symbol-lookup-in-generics-collective-imports-from-a-directory_toc" href="#symbol-lookup-in-generics-collective-imports-from-a-directory">Collective imports from a directory</a></li>
+</ul></ul><li><a class="reference" id="pragmas_toc" href="#pragmas">Pragmas</a></li>
+<ul class="simple"><li><a class="reference" id="pragmas-deprecated-pragma_toc" href="#pragmas-deprecated-pragma">deprecated pragma</a></li>
+</ul><li><a class="reference" id="pure-libraries_toc" href="#pure-libraries">Pure libraries</a></li>
+<ul class="simple"><li><a class="reference" id="pure-libraries-automatic-imports_toc" href="#pure-libraries-automatic-imports">Automatic imports</a></li>
+</ul><li><a class="reference" id="code-reordering_toc" href="#code-reordering">Code reordering</a></li>
+<ul class="simple"><li><a class="reference" id="code-reordering-parameter-constraints_toc" href="#code-reordering-parameter-constraints">Parameter constraints</a></li>
+<ul class="simple"><li><a class="reference" id="parameter-constraints-the-tilde-operator_toc" href="#parameter-constraints-the-tilde-operator">The <tt class="docutils literal"><span class="pre">~</span></tt> operator</a></li>
+<li><a class="reference" id="parameter-constraints-the-starstar-operator_toc" href="#parameter-constraints-the-starstar-operator">The <tt class="docutils literal"><span class="pre">**</span></tt> operator</a></li>
+</ul><li><a class="reference" id="code-reordering-time-measurement-with-garbage-collectors_toc" href="#code-reordering-time-measurement-with-garbage-collectors">Time measurement with garbage collectors</a></li>
+</ul><li><a class="reference" id="introduction_toc" href="#introduction">Introduction</a></li>
+
+</ul>
+
+  </div>
+  <div class="nine columns" id="content">
+    
+    <div id="tocRoot"></div>
+    
+    <p class="module-desc"><table class="docinfo" frame="void" rules="none"><col class="docinfo-name" /><col class="docinfo-content" /><tbody valign="top"><tr><th class="docinfo-name">Authors:</th><td>Andreas Rumpf, Zahary Karadjov</td></tr>
+<tr><th class="docinfo-name">Version:</th><td>|nimversion|</td></tr>
+</tbody></table><blockquote><p>&quot;Complexity&quot; seems to be a lot like &quot;energy&quot;: you can transfer it from the end-user to one/some of the other players, but the total amount seems to remain pretty much constant for a given task. -- Ran</p></blockquote>
+
+<h1><a class="toc-backref" id="about-this-document" href="#about-this-document">About this document</a></h1><p><strong>Note</strong>: This document is a draft! Several of Nim's features may need more precise wording. This manual is constantly evolving into a proper specification.</p>
+<p><strong>Note</strong>: The experimental features of Nim are covered <a class="reference external" href="manual_experimental.html">here</a>.</p>
+<p><strong>Note</strong>: Assignments, moves, and destruction are specified in the <a class="reference external" href="destructors.html">destructors</a> document.</p>
+<p>The language constructs are explained using an extended BNF, in which <tt class="docutils literal"><span class="pre">(a)*</span></tt> means 0 or more <tt class="docutils literal"><span class="pre">a</span></tt>'s, <tt class="docutils literal"><span class="pre">a+</span></tt> means 1 or more <tt class="docutils literal"><span class="pre">a</span></tt>'s, and <tt class="docutils literal"><span class="pre">(a)?</span></tt> means an optional <em>a</em>. Parentheses may be used to group elements.</p>
+<p><tt class="docutils literal"><span class="pre">&amp;</span></tt> is the lookahead operator; <tt class="docutils literal"><span class="pre">&amp;a</span></tt> means that an <tt class="docutils literal"><span class="pre">a</span></tt> is expected but not consumed. It will be consumed in the following rule.</p>
+<p>Non-terminals start with a lowercase letter, abstract terminal symbols are in UPPERCASE. Verbatim terminal symbols (including keywords) are quoted with <tt class="docutils literal"><span class="pre">'</span></tt>. An example:<pre>ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)?</pre>
+</p>
+<p>In a typical Nim program, most of the code is compiled into the executable. However, some of the code may be executed at <span id="compileminustime_1">compile-time</span>. This can include constant expressions, macro definitions, and Nim procedures used by macro definitions. Most of the Nim language is supported at compile-time, but there are some restrictions -- see <a class="reference external" href="#restrictions-on-compileminustime-execution">Restrictions on Compile-Time Execution</a> for details. We use the term <span id="runtime_1">runtime</span> to cover both compile-time execution and code execution in the executable.</p>
+<pre class="listing"><span class="Keyword">var</span> <span class="Identifier">a</span><span class="Punctuation">:</span> <span class="Identifier">array</span><span class="Punctuation">[</span><span class="FloatNumber">0.</span><span class="Operator">.</span><span class="DecNumber">1</span><span class="Punctuation">,</span> <span class="Identifier">char</span><span class="Punctuation">]</span>
+<span class="Keyword">let</span> <span class="Identifier">i</span> <span class="Operator">=</span> <span class="DecNumber">5</span>
+<span class="Keyword">try</span><span class="Punctuation">:</span>
+  <span class="Identifier">a</span><span class="Punctuation">[</span><span class="Identifier">i</span><span class="Punctuation">]</span> <span class="Operator">=</span> <span class="CharLit">'N'</span>
+<span class="Keyword">except</span> <span class="Identifier">IndexDefect</span><span class="Punctuation">:</span>
+  <span class="Identifier">echo</span> <span class="StringLit">&quot;invalid index&quot;</span></pre>
+<h2><a class="toc-backref" id="about-this-document-encoding" href="#about-this-document-encoding">Encoding</a></h2><p>All Nim source files are in the UTF-8 encoding (or its ASCII subset). Other encodings are not supported. Any of the standard platform line termination sequences can be used - the Unix form using ASCII LF (linefeed), the Windows form using the ASCII sequence CR LF (return followed by linefeed), or the old Macintosh form using the ASCII CR (return) character. All of these forms can be used equally, regardless of the platform.</p>
+
+<h2><a class="toc-backref" id="about-this-document-indentation" href="#about-this-document-indentation">Indentation</a></h2><p>Nim's standard grammar describes an <span id="indentation-sensitive_1">indentation sensitive</span> language. This means that all the control structures are recognized by indentation. Indentation consists only of spaces; tabulators are not allowed.</p>
+<p>With this notation we can now easily define the core of the grammar: A block of statements (simplified example):<pre>ifStmt = 'if' expr ':' stmt
+         (IND{=} 'elif' expr ':' stmt)*
+         (IND{=} 'else' ':' stmt)?
+
+simpleStmt = ifStmt / ...
+
+stmt = IND{&gt;} stmt ^+ IND{=} DED  # list of statements
+     / simpleStmt                 # or a simple statement</pre>
+</p>
+<p>String literals can be delimited by matching double quotes, and can contain the following <span id="escape-sequences_1">escape sequences</span>:</p>
+<table border="1" class="docutils"><tr><th>Escape sequence</th><th>Meaning</th></tr>
+<tr><td><tt class="docutils literal"><span class="pre">\p</span></tt></td><td>platform specific newline: CRLF on Windows, LF on Unix</td></tr>
+<tr><td><tt class="docutils literal"><span class="pre">\r</span></tt>, <tt class="docutils literal"><span class="pre">\c</span></tt></td><td><span id="carriage-return_1">carriage return</span></td></tr>
+<tr><td><tt class="docutils literal"><span class="pre">\n</span></tt>, <tt class="docutils literal"><span class="pre">\l</span></tt></td><td><span id="line-feed_1">line feed</span> (often called <span id="newline_1">newline</span>)</td></tr>
+<tr><td><tt class="docutils literal"><span class="pre">\f</span></tt></td><td><span id="form-feed_1">form feed</span></td></tr>
+<tr><td><tt class="docutils literal"><span class="pre">\t</span></tt></td><td><span id="tabulator_1">tabulator</span></td></tr>
+<tr><td><tt class="docutils literal"><span class="pre">\v</span></tt></td><td><span id="vertical-tabulator_1">vertical tabulator</span></td></tr>
+<tr><td><tt class="docutils literal"><span class="pre">\\</span></tt></td><td><span id="backslash_1">backslash</span></td></tr>
+<tr><td><tt class="docutils literal"><span class="pre">\&quot;</span></tt></td><td><span id="quotation-mark_1">quotation mark</span></td></tr>
+<tr><td><tt class="docutils literal"><span class="pre">\'</span></tt></td><td><span id="apostrophe_1">apostrophe</span></td></tr>
+<tr><td><tt class="docutils literal"><span class="pre">\</span></tt> '0'..'9'+</td><td><span id="character-with-decimal-value-d_1">character with decimal value d</span>; all decimal digits directly following are used for the character</td></tr>
+<tr><td><tt class="docutils literal"><span class="pre">\a</span></tt></td><td><span id="alert_1">alert</span></td></tr>
+<tr><td><tt class="docutils literal"><span class="pre">\b</span></tt></td><td><span id="backspace_1">backspace</span></td></tr>
+<tr><td><tt class="docutils literal"><span class="pre">\e</span></tt></td><td><span id="escape_1">escape</span> <span id="esc_1">[ESC]</span></td></tr>
+<tr><td><tt class="docutils literal"><span class="pre">\x</span></tt> HH</td><td><span id="character-with-hex-value-hh_1">character with hex value HH</span>; exactly two hex digits are allowed</td></tr>
+<tr><td><tt class="docutils literal"><span class="pre">\u</span></tt> HHHH</td><td><span id="unicode-codepoint-with-hex-value-hhhh_1">unicode codepoint with hex value HHHH</span>; exactly four hex digits are allowed</td></tr>
+<tr><td><tt class="docutils literal"><span class="pre">\u</span></tt> {H+}</td><td><span id="unicode-codepoint_1">unicode codepoint</span>; all hex digits enclosed in <tt class="docutils literal"><span class="pre">{}</span></tt> are used for the codepoint</td></tr>
+</table><pre class="listing"><span class="LongStringLit">&quot;&quot;&quot;&quot;long string within quotes&quot;&quot;&quot;&quot;</span></pre><p>Produces:<pre>&quot;long string within quotes&quot;</pre>
+</p>
+
+<h2><a class="toc-backref" id="about-this-document-operators" href="#about-this-document-operators">Operators</a></h2><p>Nim allows user defined operators. An operator is any combination of the following characters:<pre>=     +     -     *     /     &lt;     &gt;
+@     $     ~     &amp;     %     |
+!     ?     ^     .     :     \</pre>
+</p>
+<p>(The grammar uses the terminal OPR to refer to operator symbols as defined here.)</p>
+<p>The following strings denote other tokens:<pre>`   (    )     {    }     [    ]    ,  ;   [.    .]  {.   .}  (.  .)  [:</pre>
+</p>
+<p>Otherwise, precedence is determined by the first character.</p>
+<table border="1" class="docutils"><tr><th>Precedence level</th><th>Operators</th><th>First character</th><th>Terminal symbol</th></tr>
+<tr><td>10 (highest)</td><td></td><td><tt class="docutils literal"><span class="pre">$ ^</span></tt></td><td>OP10</td></tr>
+<tr><td>9</td><td><tt class="docutils literal"><span class="pre">* / div mod shl shr %</span></tt></td><td><tt class="docutils literal"><span class="pre">* % \ /</span></tt></td><td>OP9</td></tr>
+<tr><td>8</td><td><tt class="docutils literal"><span class="pre">+ -</span></tt></td><td><tt class="docutils literal"><span class="pre">+ - ~ |</span></tt></td><td>OP8</td></tr>
+<tr><td>7</td><td><tt class="docutils literal"><span class="pre">&amp;</span></tt></td><td><tt class="docutils literal"><span class="pre">&amp;</span></tt></td><td>OP7</td></tr>
+<tr><td>6</td><td><tt class="docutils literal"><span class="pre">..</span></tt></td><td><tt class="docutils literal"><span class="pre">.</span></tt></td><td>OP6</td></tr>
+<tr><td>5</td><td><tt class="docutils literal"><span class="pre">== &lt;= &lt; &gt;= &gt; != in notin is isnot not of as from</span></tt></td><td><tt class="docutils literal"><span class="pre">= &lt; &gt; !</span></tt></td><td>OP5</td></tr>
+<tr><td>4</td><td><tt class="docutils literal"><span class="pre">and</span></tt></td><td></td><td>OP4</td></tr>
+<tr><td>3</td><td><tt class="docutils literal"><span class="pre">or xor</span></tt></td><td></td><td>OP3</td></tr>
+<tr><td>2</td><td></td><td><tt class="docutils literal"><span class="pre">@ : ?</span></tt></td><td>OP2</td></tr>
+<tr><td>1</td><td><em>assignment operator</em> (like <tt class="docutils literal"><span class="pre">+=</span></tt>, <tt class="docutils literal"><span class="pre">*=</span></tt>)</td><td></td><td>OP1</td></tr>
+<tr><td>0 (lowest)</td><td><em>arrow like operator</em> (like <tt class="docutils literal"><span class="pre">-&gt;</span></tt>, <tt class="docutils literal"><span class="pre">=&gt;</span></tt>)</td><td></td><td>OP0</td></tr>
+</table>
+<h1><a class="toc-backref" id="constants-and-constant-expressions" href="#constants-and-constant-expressions">Constants and Constant Expressions</a></h1><p>A <span id="constant_1">constant</span> is a symbol that is bound to the value of a constant expression. Constant expressions are restricted to depend only on the following categories of values and operations, because these are either built into the language or declared and evaluated before semantic analysis of the constant expression:</p>
+<ul class="simple"><li>literals</li>
+<li>built-in operators</li>
+<li>previously declared constants and compile-time variables</li>
+<li>previously declared macros and templates</li>
+<li>previously declared procedures that have no side effects beyond possibly modifying compile-time variables</li>
+</ul>
+<p>These integer types are pre-defined:</p>
+<dl class="docutils"><dt><tt class="docutils literal"><span class="pre">int</span></tt></dt>
+<dd>the generic signed integer type; its size is platform-dependent and has the same size as a pointer. This type should be used in general. An integer literal that has no type suffix is of this type if it is in the range <tt class="docutils literal"><span class="pre">low(int32)..high(int32)</span></tt> otherwise the literal's type is <tt class="docutils literal"><span class="pre">int64</span></tt>.</dd>
+<dt>intXX</dt>
+<dd>additional signed integer types of XX bits use this naming scheme (example: int16 is a 16-bit wide integer). The current implementation supports <tt class="docutils literal"><span class="pre">int8</span></tt>, <tt class="docutils literal"><span class="pre">int16</span></tt>, <tt class="docutils literal"><span class="pre">int32</span></tt>, <tt class="docutils literal"><span class="pre">int64</span></tt>. Literals of these types have the suffix 'iXX.</dd>
+<dt><tt class="docutils literal"><span class="pre">uint</span></tt></dt>
+<dd>the generic <span id="unsigned-integer_1">unsigned integer</span> type; its size is platform-dependent and has the same size as a pointer. An integer literal with the type suffix <tt class="docutils literal"><span class="pre">'u</span></tt> is of this type.</dd>
+</dl>
+<p>Let <tt class="docutils literal"><span class="pre">T</span></tt>'s be <tt class="docutils literal"><span class="pre">p</span></tt>'s return type. NRVO applies for <tt class="docutils literal"><span class="pre">T</span></tt> if <tt class="docutils literal"><span class="pre">sizeof(T) &gt;= N</span></tt> (where <tt class="docutils literal"><span class="pre">N</span></tt> is implementation dependent), in other words, it applies for &quot;big&quot; structures.</p>
+<p>Apart from built-in operations like array indexing, memory allocation, etc. the <tt class="docutils literal"><span class="pre">raise</span></tt> statement is the only way to raise an exception.</p>
+<p><tt class="docutils literal"><span class="pre"><span class="Identifier">typedesc</span></span></tt> used as a parameter type also introduces an implicit generic. <tt class="docutils literal"><span class="pre"><span class="Identifier">typedesc</span></span></tt> has its own set of rules:</p>
+<p>The <tt class="docutils literal"><span class="pre">!=</span></tt>, <tt class="docutils literal"><span class="pre">&gt;</span></tt>, <tt class="docutils literal"><span class="pre">&gt;=</span></tt>, <tt class="docutils literal"><span class="pre">in</span></tt>, <tt class="docutils literal"><span class="pre">notin</span></tt>, <tt class="docutils literal"><span class="pre">isnot</span></tt> operators are in fact templates:</p>
+<p><tt class="docutils literal"><span class="pre">a &gt; b</span></tt> is transformed into <tt class="docutils literal"><span class="pre">b &lt; a</span></tt>.<br/><tt class="docutils literal"><span class="pre">a in b</span></tt> is transformed into <tt class="docutils literal"><span class="pre">contains(b, a)</span></tt>.<br/><tt class="docutils literal"><span class="pre">notin</span></tt> and <tt class="docutils literal"><span class="pre">isnot</span></tt> have the obvious meanings.<br/></p><p>A template where every parameter is <tt class="docutils literal"><span class="pre">untyped</span></tt> is called an <span id="immediate_1">immediate</span> template. For historical reasons templates can be explicitly annotated with an <tt class="docutils literal"><span class="pre">immediate</span></tt> pragma and then these templates do not take part in overloading resolution and the parameters' types are <em>ignored</em> by the compiler. Explicit immediate templates are now deprecated.</p>
+
+<h2><a class="toc-backref" id="constants-and-constant-expressions-symbol-lookup-in-generics" href="#constants-and-constant-expressions-symbol-lookup-in-generics">Symbol lookup in generics</a></h2>
+<h3><a class="toc-backref" id="symbol-lookup-in-generics-open-and-closed-symbols" href="#symbol-lookup-in-generics-open-and-closed-symbols">Open and Closed symbols</a></h3><p>The symbol binding rules in generics are slightly subtle: There are &quot;open&quot; and &quot;closed&quot; symbols. A &quot;closed&quot; symbol cannot be re-bound in the instantiation context, an &quot;open&quot; symbol can. Per default overloaded symbols are open and every other symbol is closed.</p>
+<p>In templates identifiers can be constructed with the backticks notation:</p>
+<pre class="listing"><span class="Keyword">template</span> <span class="Identifier">typedef</span><span class="Punctuation">(</span><span class="Identifier">name</span><span class="Punctuation">:</span> <span class="Identifier">untyped</span><span class="Punctuation">,</span> <span class="Identifier">typ</span><span class="Punctuation">:</span> <span class="Identifier">typedesc</span><span class="Punctuation">)</span> <span class="Operator">=</span>
+  <span class="Keyword">type</span>
+    <span class="Punctuation">`</span><span class="Identifier">T</span> <span class="Identifier">name</span><span class="Punctuation">`</span><span class="Operator">*</span> <span class="Punctuation">{</span><span class="Operator">.</span><span class="Identifier">inject</span><span class="Operator">.</span><span class="Punctuation">}</span> <span class="Operator">=</span> <span class="Identifier">typ</span>
+    <span class="Punctuation">`</span><span class="Identifier">P</span> <span class="Identifier">name</span><span class="Punctuation">`</span><span class="Operator">*</span> <span class="Punctuation">{</span><span class="Operator">.</span><span class="Identifier">inject</span><span class="Operator">.</span><span class="Punctuation">}</span> <span class="Operator">=</span> <span class="Keyword">ref</span> <span class="Punctuation">`</span><span class="Identifier">T</span> <span class="Identifier">name</span><span class="Punctuation">`</span>
+
+<span class="Identifier">typedef</span><span class="Punctuation">(</span><span class="Identifier">myint</span><span class="Punctuation">,</span> <span class="Identifier">int</span><span class="Punctuation">)</span>
+<span class="Keyword">var</span> <span class="Identifier">x</span><span class="Punctuation">:</span> <span class="Identifier">PMyInt</span></pre><p>In the example <tt class="docutils literal"><span class="pre">name</span></tt> is instantiated with <tt class="docutils literal"><span class="pre">myint</span></tt>, so `T name` becomes <tt class="docutils literal"><span class="pre">Tmyint</span></tt>.</p>
+<p>Only top-level symbols that are marked with an asterisk (<tt class="docutils literal"><span class="pre">*</span></tt>) are exported.</p>
+<p>The algorithm for compiling modules is:</p>
+<ul class="simple"><li>compile the whole module as usual, following import statements recursively</li>
+<li>if there is a cycle only import the already parsed symbols (that are exported); if an unknown identifier occurs then abort</li>
+</ul>
+
+<h3><a class="toc-backref" id="symbol-lookup-in-generics-collective-imports-from-a-directory" href="#symbol-lookup-in-generics-collective-imports-from-a-directory">Collective imports from a directory</a></h3><p>The syntax <tt class="docutils literal"><span class="pre">import dir / [moduleA, moduleB]</span></tt> can be used to import multiple modules from the same directory.</p>
+
+<h1><a class="toc-backref" id="pragmas" href="#pragmas">Pragmas</a></h1><p>Pragmas are Nim's method to give the compiler additional information / commands without introducing a massive number of new keywords. Pragmas are processed on the fly during semantic checking. Pragmas are enclosed in the special <tt class="docutils literal"><span class="pre">{.</span></tt> and <tt class="docutils literal"><span class="pre">.}</span></tt> curly brackets. Pragmas are also often used as a first implementation to play with a language feature before a nicer syntax to access the feature becomes available.</p>
+
+<h2><a class="toc-backref" id="pragmas-deprecated-pragma" href="#pragmas-deprecated-pragma">deprecated pragma</a></h2><p>The deprecated pragma is used to mark a symbol as deprecated:</p>
+<p><strong>Note</strong>: <a class="reference external" href="https://github.com/nim-lang/c2nim/blob/master/doc/c2nim.rst">c2nim</a> can parse a large subset of C++ and knows about the <tt class="docutils literal"><span class="pre">importcpp</span></tt> pragma pattern language. It is not necessary to know all the details described here.</p>
+<p>Pure libraries do not depend on any external <tt class="docutils literal"><span class="pre">*.dll</span></tt> or <tt class="docutils literal"><span class="pre">lib*.so</span></tt> binary while impure libraries do. A wrapper is an impure library that is a very low-level interface to a C library.</p>
+
+<h1><a class="toc-backref" id="pure-libraries" href="#pure-libraries">Pure libraries</a></h1>
+<h2><a class="toc-backref" id="pure-libraries-automatic-imports" href="#pure-libraries-automatic-imports">Automatic imports</a></h2><ul class="simple"><li><a class="reference external" href="system.html">system</a> Basic procs and operators that every program needs. It also provides IO facilities for reading and writing text and binary files. It is imported implicitly by the compiler. Do not import it directly. It relies on compiler magic to work.</li>
+<li><a class="reference external" href="threads.html">threads</a> Basic Nim thread support. <strong>Note</strong>: This is part of the system module. Do not import it explicitly. Enabled with <tt class="docutils literal"><span class="pre">--threads:on</span></tt>.</li>
+</ul>
+
+<h1><a class="toc-backref" id="code-reordering" href="#code-reordering">Code reordering</a></h1><p>The code reordering feature can implicitly rearrange procedure, template, and macro definitions along with variable declarations and initializations at the top level scope so that, to a large extent, a programmer should not have to worry about ordering definitions correctly or be forced to use forward declarations to preface definitions inside a module.</p>
+<p>Example:</p>
+<pre class="listing"><span class="Punctuation">{</span><span class="Operator">.</span><span class="Identifier">experimental</span><span class="Punctuation">:</span> <span class="StringLit">&quot;codeReordering&quot;</span><span class="Operator">.</span><span class="Punctuation">}</span>
+
+<span class="Keyword">proc</span> <span class="Identifier">foo</span><span class="Punctuation">(</span><span class="Identifier">x</span><span class="Punctuation">:</span> <span class="Identifier">int</span><span class="Punctuation">)</span> <span class="Operator">=</span>
+  <span class="Identifier">bar</span><span class="Punctuation">(</span><span class="Identifier">x</span><span class="Punctuation">)</span>
+
+<span class="Keyword">proc</span> <span class="Identifier">bar</span><span class="Punctuation">(</span><span class="Identifier">x</span><span class="Punctuation">:</span> <span class="Identifier">int</span><span class="Punctuation">)</span> <span class="Operator">=</span>
+  <span class="Identifier">echo</span><span class="Punctuation">(</span><span class="Identifier">x</span><span class="Punctuation">)</span>
+
+<span class="Identifier">foo</span><span class="Punctuation">(</span><span class="DecNumber">10</span><span class="Punctuation">)</span></pre><p>It is important to note that reordering <em>only</em> works for symbols at top level scope. Therefore, the following will <em>fail to compile:</em></p>
+
+<h2><a class="toc-backref" id="code-reordering-parameter-constraints" href="#code-reordering-parameter-constraints">Parameter constraints</a></h2><p>The <span id="parameter-constraint_1">parameter constraint</span> expression can use the operators <tt class="docutils literal"><span class="pre">|</span></tt> (or), <tt class="docutils literal"><span class="pre">&amp;</span></tt> (and) and <tt class="docutils literal"><span class="pre">~</span></tt> (not) and the following predicates:</p>
+
+<h3><a class="toc-backref" id="parameter-constraints-the-tilde-operator" href="#parameter-constraints-the-tilde-operator">The <tt class="docutils literal"><span class="pre">~</span></tt> operator</a></h3><p>The <tt class="docutils literal"><span class="pre">~</span></tt> operator is the <strong>not</strong> operator in patterns:</p>
+
+<h3><a class="toc-backref" id="parameter-constraints-the-starstar-operator" href="#parameter-constraints-the-starstar-operator">The <tt class="docutils literal"><span class="pre">**</span></tt> operator</a></h3><p>The <tt class="docutils literal"><span class="pre">**</span></tt> is much like the <tt class="docutils literal"><span class="pre">*</span></tt> operator, except that it gathers not only all the arguments, but also the matched operators in reverse polish notation:</p>
+<p>Nim significantly improves on the safety of these features via additional pragmas:</p>
+<ol class="simple"><li>A <span id="guard_1">guard</span> annotation is introduced to prevent data races.</li>
+<li>Every access of a guarded memory location needs to happen in an appropriate <span id="locks_1">locks</span> statement.</li>
+<li>Locks and routines can be annotated with <span id="lock-levels_1">lock levels</span> to allow potential deadlocks to be detected during semantic analysis.</li>
+</ol>
+<ol class="simple"><li>Two output parameters should never be aliased.</li>
+<li>An input and an output parameter should not be aliased.</li>
+<li>An output parameter should never be aliased with a global or thread local variable referenced by the called proc.</li>
+<li>An input parameter should not be aliased with a global or thread local variable updated by the called proc.</li>
+</ol>
+<p>One problem with rules 3 and 4 is that they affect specific global or thread local variables, but Nim's effect tracking only tracks &quot;uses no global variable&quot; via <tt class="docutils literal"><span class="pre">.noSideEffect</span></tt>. The rules 3 and 4 can also be approximated by a different rule:</p>
+<ol class="simple" start="5"><li>A global or thread local variable (or a location derived from such a location) can only passed to a parameter of a <tt class="docutils literal"><span class="pre">.noSideEffect</span></tt> proc.</li>
+</ol>
+<p>These two procs are the two modus operandi of the real-time garbage collector:</p>
+<ol class="simple"><li><p>GC_SetMaxPause Mode</p>
+<p>You can call <tt class="docutils literal"><span class="pre">GC_SetMaxPause</span></tt> at program startup and then each triggered garbage collector run tries to not take longer than <tt class="docutils literal"><span class="pre">maxPause</span></tt> time. However, it is possible (and common) that the work is nevertheless not evenly distributed as each call to <tt class="docutils literal"><span class="pre">new</span></tt> can trigger the garbage collector and thus take  <tt class="docutils literal"><span class="pre">maxPause</span></tt> time.</p>
+</li>
+<li><p>GC_step Mode</p>
+<p>This allows the garbage collector to perform some work for up to <tt class="docutils literal"><span class="pre">us</span></tt> time. This is useful to call in the main loop to ensure the garbage collector can do its work. To bind all garbage collector activity to a <tt class="docutils literal"><span class="pre">GC_step</span></tt> call, deactivate the garbage collector with <tt class="docutils literal"><span class="pre">GC_disable</span></tt> at program startup. If <tt class="docutils literal"><span class="pre">strongAdvice</span></tt> is set to <tt class="docutils literal"><span class="pre">true</span></tt>, then the garbage collector will be forced to perform the collection cycle. Otherwise, the garbage collector may decide not to do anything, if there is not much garbage to collect. You may also specify the current stack size via <tt class="docutils literal"><span class="pre">stackSize</span></tt> parameter. It can improve performance when you know that there are no unique Nim references below a certain point on the stack. Make sure the size you specify is greater than the potential worst-case size.</p>
+<p>It can improve performance when you know that there are no unique Nim references below a certain point on the stack. Make sure the size you specify is greater than the potential worst-case size.</p>
+</li>
+</ol>
+<p>These procs provide a &quot;best effort&quot; real-time guarantee; in particular the cycle collector is not aware of deadlines. Deactivate it to get more predictable real-time behaviour. Tests show that a 1ms max pause time will be met in almost all cases on modern CPUs (with the cycle collector disabled).</p>
+
+<h2><a class="toc-backref" id="code-reordering-time-measurement-with-garbage-collectors" href="#code-reordering-time-measurement-with-garbage-collectors">Time measurement with garbage collectors</a></h2><p>The garbage collectors' way of measuring time uses (see <tt class="docutils literal"><span class="pre">lib/system/timers.nim</span></tt> for the implementation):</p>
+<ol class="simple"><li><tt class="docutils literal"><span class="pre">QueryPerformanceCounter</span></tt> and <tt class="docutils literal"><span class="pre">QueryPerformanceFrequency</span></tt> on Windows.</li>
+<li><tt class="docutils literal"><span class="pre">mach_absolute_time</span></tt> on Mac OS X.</li>
+<li><tt class="docutils literal"><span class="pre">gettimeofday</span></tt> on Posix systems.</li>
+</ol>
+<p>As such it supports a resolution of nanoseconds internally; however, the API uses microseconds for convenience.</p>
+
+<h1><a class="toc-backref" id="introduction" href="#introduction">Introduction</a></h1><blockquote><p>
+"Der Mensch ist doch ein Augentier -- sch&ouml;ne Dinge w&uuml;nsch ich mir."
+</p></blockquote><p>This document is a tutorial for the programming language <em>Nim</em>. This tutorial assumes that you are familiar with basic programming concepts like variables, types, or statements but is kept very basic. The <a class="reference external" href="manual.html">manual</a> contains many more examples of the advanced language features. All code examples in this tutorial, as well as the ones found in the rest of Nim's documentation, follow the <a class="reference external" href="nep1.html">Nim style guide</a>.</p>
+<p>However, this does not work. The problem is that the procedure should not only <tt class="docutils literal"><span class="pre">return</span></tt>, but return and <strong>continue</strong> after an iteration has finished. This <em>return and continue</em> is called a <tt class="docutils literal"><span class="pre"><span class="Keyword">yield</span></span></tt> statement. Now the only thing left to do is to replace the <tt class="docutils literal"><span class="pre">proc</span></tt> keyword by <tt class="docutils literal"><span class="pre">iterator</span></tt> and here it is - our first iterator:</p>
+<table border="1" class="docutils"><tr><th>A1 header</th><th>A2 | not fooled</th></tr>
+<tr><td>C1</td><td>C2 <strong>bold</strong></td></tr>
+<tr><td>D1 <tt class="docutils literal"><span class="pre"><span class="Identifier">code</span> <span class="Operator">\|</span></span></tt></td><td>D2</td></tr>
+<tr><td>E1 | text</td><td></td></tr>
+<tr><td></td><td>F2 without pipe</td></tr>
+</table><p>not in table </p>
+</p>
+    
+  </div>
+</div>
+
+      <div class="twelve-columns footer">
+        <span class="nim-sprite"></span>
+        <br>
+        <small style="color: var(--hint);">Made with Nim. Generated: 1970-01-02 03:46:40 UTC</small>
+      </div>
+    </div>
+  </div>
+  
+</body>
+</html>
diff --git a/nimdoc/rst2html/source/rst_examples.rst b/nimdoc/rst2html/source/rst_examples.rst
new file mode 100644
index 000000000..7fa20de6c
--- /dev/null
+++ b/nimdoc/rst2html/source/rst_examples.rst
@@ -0,0 +1,514 @@
+================
+Not a Nim Manual
+================
+
+:Authors: Andreas Rumpf, Zahary Karadjov
+:Version: |nimversion|
+
+.. role:: nim(code)
+   :language: nim
+.. default-role:: nim
+
+.. contents::
+
+
+  "Complexity" seems to be a lot like "energy": you can transfer it from the
+  end-user to one/some of the other players, but the total amount seems to remain
+  pretty much constant for a given task. -- Ran
+
+
+About this document
+===================
+
+**Note**: This document is a draft! Several of Nim's features may need more
+precise wording. This manual is constantly evolving into a proper specification.
+
+**Note**: The experimental features of Nim are
+covered `here <manual_experimental.html>`_.
+
+**Note**: Assignments, moves, and destruction are specified in
+the `destructors <destructors.html>`_ document.
+
+The language constructs are explained using an extended BNF, in which ``(a)*``
+means 0 or more ``a``'s, ``a+`` means 1 or more ``a``'s, and ``(a)?`` means an
+optional *a*. Parentheses may be used to group elements.
+
+``&`` is the lookahead operator; ``&a`` means that an ``a`` is expected but
+not consumed. It will be consumed in the following rule.
+
+Non-terminals start with a lowercase letter, abstract terminal symbols are in
+UPPERCASE. Verbatim terminal symbols (including keywords) are quoted
+with ``'``. An example::
+
+  ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)?
+
+In a typical Nim program, most of the code is compiled into the executable.
+However, some of the code may be executed at
+`compile-time`:idx:. This can include constant expressions, macro definitions,
+and Nim procedures used by macro definitions. Most of the Nim language is
+supported at compile-time, but there are some restrictions -- see `Restrictions
+on Compile-Time Execution <#restrictions-on-compileminustime-execution>`_ for
+details. We use the term `runtime`:idx: to cover both compile-time execution
+and code execution in the executable.
+
+.. code-block:: nim
+  var a: array[0..1, char]
+  let i = 5
+  try:
+    a[i] = 'N'
+  except IndexDefect:
+    echo "invalid index"
+
+Encoding
+--------
+
+All Nim source files are in the UTF-8 encoding (or its ASCII subset). Other
+encodings are not supported. Any of the standard platform line termination
+sequences can be used - the Unix form using ASCII LF (linefeed), the Windows
+form using the ASCII sequence CR LF (return followed by linefeed), or the old
+Macintosh form using the ASCII CR (return) character. All of these forms can be
+used equally, regardless of the platform.
+
+
+Indentation
+-----------
+
+Nim's standard grammar describes an `indentation sensitive`:idx: language.
+This means that all the control structures are recognized by indentation.
+Indentation consists only of spaces; tabulators are not allowed.
+
+With this notation we can now easily define the core of the grammar: A block of
+statements (simplified example)::
+
+  ifStmt = 'if' expr ':' stmt
+           (IND{=} 'elif' expr ':' stmt)*
+           (IND{=} 'else' ':' stmt)?
+
+  simpleStmt = ifStmt / ...
+
+  stmt = IND{>} stmt ^+ IND{=} DED  # list of statements
+       / simpleStmt                 # or a simple statement
+
+
+String literals can be delimited by matching double quotes, and can
+contain the following `escape sequences`:idx:\ :
+
+==================         ===================================================
+  Escape sequence          Meaning
+==================         ===================================================
+  ``\p``                   platform specific newline: CRLF on Windows,
+                           LF on Unix
+  ``\r``, ``\c``           `carriage return`:idx:
+  ``\n``, ``\l``           `line feed`:idx: (often called `newline`:idx:)
+  ``\f``                   `form feed`:idx:
+  ``\t``                   `tabulator`:idx:
+  ``\v``                   `vertical tabulator`:idx:
+  ``\\``                   `backslash`:idx:
+  ``\"``                   `quotation mark`:idx:
+  ``\'``                   `apostrophe`:idx:
+  ``\`` '0'..'9'+          `character with decimal value d`:idx:;
+                           all decimal digits directly
+                           following are used for the character
+  ``\a``                   `alert`:idx:
+  ``\b``                   `backspace`:idx:
+  ``\e``                   `escape`:idx: `[ESC]`:idx:
+  ``\x`` HH                `character with hex value HH`:idx:;
+                           exactly two hex digits are allowed
+  ``\u`` HHHH              `unicode codepoint with hex value HHHH`:idx:;
+                           exactly four hex digits are allowed
+  ``\u`` {H+}              `unicode codepoint`:idx:;
+                           all hex digits enclosed in ``{}`` are used for
+                           the codepoint
+==================         ===================================================
+
+.. code-block:: nim
+  """"long string within quotes""""
+
+Produces::
+
+  "long string within quotes"
+
+Operators
+---------
+
+Nim allows user defined operators. An operator is any combination of the
+following characters::
+
+       =     +     -     *     /     <     >
+       @     $     ~     &     %     |
+       !     ?     ^     .     :     \
+
+(The grammar uses the terminal OPR to refer to operator symbols as
+defined here.)
+
+The following strings denote other tokens::
+
+    `   (    )     {    }     [    ]    ,  ;   [.    .]  {.   .}  (.  .)  [:
+
+
+Otherwise, precedence is determined by the first character.
+
+================  =======================================================  ==================  ===============
+Precedence level    Operators                                              First character     Terminal symbol
+================  =======================================================  ==================  ===============
+ 10 (highest)                                                              ``$  ^``            OP10
+  9               ``*    /    div   mod   shl  shr  %``                    ``*  %  \  /``      OP9
+  8               ``+    -``                                               ``+  -  ~  |``      OP8
+  7               ``&``                                                    ``&``               OP7
+  6               ``..``                                                   ``.``               OP6
+  5               ``==  <= < >= > !=  in notin is isnot not of as from``   ``=  <  >  !``      OP5
+  4               ``and``                                                                      OP4
+  3               ``or xor``                                                                   OP3
+  2                                                                        ``@  :  ?``         OP2
+  1               *assignment operator* (like ``+=``, ``*=``)                                  OP1
+  0 (lowest)      *arrow like operator* (like ``->``, ``=>``)                                  OP0
+================  =======================================================  ==================  ===============
+
+
+Constants and Constant Expressions
+==================================
+
+A `constant`:idx: is a symbol that is bound to the value of a constant
+expression. Constant expressions are restricted to depend only on the following
+categories of values and operations, because these are either built into the
+language or declared and evaluated before semantic analysis of the constant
+expression:
+
+* literals
+* built-in operators
+* previously declared constants and compile-time variables
+* previously declared macros and templates
+* previously declared procedures that have no side effects beyond
+  possibly modifying compile-time variables
+
+These integer types are pre-defined:
+
+``int``
+  the generic signed integer type; its size is platform-dependent and has the
+  same size as a pointer. This type should be used in general. An integer
+  literal that has no type suffix is of this type if it is in the range
+  ``low(int32)..high(int32)`` otherwise the literal's type is ``int64``.
+
+intXX
+  additional signed integer types of XX bits use this naming scheme
+  (example: int16 is a 16-bit wide integer).
+  The current implementation supports ``int8``, ``int16``, ``int32``, ``int64``.
+  Literals of these types have the suffix 'iXX.
+
+``uint``
+  the generic `unsigned integer`:idx: type; its size is platform-dependent and has the same size as a pointer. An integer literal with the type suffix ``'u`` is of this type.
+
+Let ``T``'s be ``p``'s return type. NRVO applies for ``T``
+if ``sizeof(T) >= N`` (where ``N`` is implementation dependent),
+in other words, it applies for "big" structures.
+
+Apart from built-in operations like array indexing, memory allocation, etc.
+the ``raise`` statement is the only way to raise an exception.
+
+.. XXX document this better!
+
+`typedesc` used as a parameter type also introduces an implicit
+generic. `typedesc` has its own set of rules:
+
+The ``!=``, ``>``, ``>=``, ``in``, ``notin``, ``isnot`` operators are in fact
+templates:
+
+| ``a > b`` is transformed into ``b < a``.
+| ``a in b`` is transformed into ``contains(b, a)``.
+| ``notin`` and ``isnot`` have the obvious meanings.
+
+A template where every parameter is ``untyped`` is called an `immediate`:idx:
+template. For historical reasons templates can be explicitly annotated with
+an ``immediate`` pragma and then these templates do not take part in
+overloading resolution and the parameters' types are *ignored* by the
+compiler. Explicit immediate templates are now deprecated.
+
+
+
+Symbol lookup in generics
+-------------------------
+
+Open and Closed symbols
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The symbol binding rules in generics are slightly subtle: There are "open" and
+"closed" symbols. A "closed" symbol cannot be re-bound in the instantiation
+context, an "open" symbol can. Per default overloaded symbols are open
+and every other symbol is closed.
+
+In templates identifiers can be constructed with the backticks notation:
+
+.. code-block:: nim
+    :test: "nim c $1"
+
+  template typedef(name: untyped, typ: typedesc) =
+    type
+      `T name`* {.inject.} = typ
+      `P name`* {.inject.} = ref `T name`
+
+  typedef(myint, int)
+  var x: PMyInt
+
+In the example ``name`` is instantiated with ``myint``, so \`T name\` becomes
+``Tmyint``.
+
+Only top-level symbols that are marked with an asterisk (``*``) are
+exported.
+
+The algorithm for compiling modules is:
+
+- compile the whole module as usual, following import statements recursively
+
+- if there is a cycle only import the already parsed symbols (that are
+  exported); if an unknown identifier occurs then abort
+
+
+Collective imports from a directory
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The syntax ``import dir / [moduleA, moduleB]`` can be used to import multiple modules
+from the same directory.
+
+
+Pragmas
+=======
+
+Pragmas are Nim's method to give the compiler additional information /
+commands without introducing a massive number of new keywords. Pragmas are
+processed on the fly during semantic checking. Pragmas are enclosed in the
+special ``{.`` and ``.}`` curly brackets. Pragmas are also often used as a
+first implementation to play with a language feature before a nicer syntax
+to access the feature becomes available.
+
+
+deprecated pragma
+-----------------
+
+The deprecated pragma is used to mark a symbol as deprecated:
+
+**Note**: `c2nim <https://github.com/nim-lang/c2nim/blob/master/doc/c2nim.rst>`_ can parse a large subset of C++ and knows
+about the ``importcpp`` pragma pattern language. It is not necessary
+to know all the details described here.
+
+
+
+Pure libraries do not depend on any external ``*.dll`` or ``lib*.so`` binary
+while impure libraries do. A wrapper is an impure library that is a very
+low-level interface to a C library.
+
+
+Pure libraries
+==============
+
+Automatic imports
+-----------------
+
+* `system <system.html>`_
+  Basic procs and operators that every program needs. It also provides IO
+  facilities for reading and writing text and binary files. It is imported
+  implicitly by the compiler. Do not import it directly. It relies on compiler 
+  magic to work.
+
+* `threads <threads.html>`_
+  Basic Nim thread support. **Note**: This is part of the system module. Do not
+  import it explicitly. Enabled with ``--threads:on``.
+
+Code reordering
+===============
+
+The code reordering feature can implicitly rearrange procedure, template, and
+macro definitions along with variable declarations and initializations at the top
+level scope so that, to a large extent, a programmer should not have to worry
+about ordering definitions correctly or be forced to use forward declarations to
+preface definitions inside a module.
+
+..
+   NOTE: The following was documentation for the code reordering precursor,
+   which was {.noForward.}.
+
+   In this mode, procedure definitions may appear out of order and the compiler
+   will postpone their semantic analysis and compilation until it actually needs
+   to generate code using the definitions. In this regard, this mode is similar
+   to the modus operandi of dynamic scripting languages, where the function
+   calls are not resolved until the code is executed. Here is the detailed
+   algorithm taken by the compiler:
+
+   1. When a callable symbol is first encountered, the compiler will only note
+   the symbol callable name and it will add it to the appropriate overload set
+   in the current scope. At this step, it won't try to resolve any of the type
+   expressions used in the signature of the symbol (so they can refer to other
+   not yet defined symbols).
+
+   2. When a top level call is encountered (usually at the very end of the
+   module), the compiler will try to determine the actual types of all of the
+   symbols in the matching overload set. This is a potentially recursive process
+   as the signatures of the symbols may include other call expressions, whose
+   types will be resolved at this point too.
+
+   3. Finally, after the best overload is picked, the compiler will start
+   compiling the body of the respective symbol. This in turn will lead the
+   compiler to discover more call expressions that need to be resolved and steps
+   2 and 3 will be repeated as necessary.
+
+   Please note that if a callable symbol is never used in this scenario, its
+   body will never be compiled. This is the default behavior leading to best
+   compilation times, but if exhaustive compilation of all definitions is
+   required, using ``nim check`` provides this option as well.
+
+Example:
+
+.. code-block:: nim
+
+  {.experimental: "codeReordering".}
+
+  proc foo(x: int) =
+    bar(x)
+
+  proc bar(x: int) =
+    echo(x)
+
+  foo(10)
+
+
+..
+   TODO: Let's table this for now. This is an *experimental feature* and so the
+   specific manner in which ``declared`` operates with it can be decided in
+   eventuality, because right now it works a bit weirdly.
+
+   The values of expressions involving ``declared`` are decided *before* the
+   code reordering process, and not after. As an example, the output of this
+   code is the same as it would be with code reordering disabled.
+
+   .. code-block:: nim
+     {.experimental: "codeReordering".}
+
+     proc x() =
+       echo(declared(foo))
+
+     var foo = 4
+
+     x() # "false"
+
+It is important to note that reordering *only* works for symbols at top level
+scope. Therefore, the following will *fail to compile:*
+
+
+Parameter constraints
+---------------------
+
+The `parameter constraint`:idx: expression can use the operators ``|`` (or),
+``&`` (and) and ``~`` (not) and the following predicates:
+
+
+The ``~`` operator
+~~~~~~~~~~~~~~~~~~
+
+The ``~`` operator is the **not** operator in patterns:
+
+
+The ``**`` operator
+~~~~~~~~~~~~~~~~~~~
+
+The ``**`` is much like the ``*`` operator, except that it gathers not only
+all the arguments, but also the matched operators in reverse polish notation:
+
+Nim significantly improves on the safety of these features via additional
+pragmas:
+
+1) A `guard`:idx: annotation is introduced to prevent data races.
+2) Every access of a guarded memory location needs to happen in an
+   appropriate `locks`:idx: statement.
+3) Locks and routines can be annotated with `lock levels`:idx: to allow
+   potential deadlocks to be detected during semantic analysis.
+
+1. Two output parameters should never be aliased.
+2. An input and an output parameter should not be aliased.
+3. An output parameter should never be aliased with a global or thread local
+   variable referenced by the called proc.
+4. An input parameter should not be aliased with a global or thread local
+   variable updated by the called proc.
+
+One problem with rules 3 and 4 is that they affect specific global or thread
+local variables, but Nim's effect tracking only tracks "uses no global variable"
+via ``.noSideEffect``. The rules 3 and 4 can also be approximated by a different rule:
+
+5. A global or thread local variable (or a location derived from such a location)
+   can only passed to a parameter of a ``.noSideEffect`` proc.
+
+These two procs are the two modus operandi of the real-time garbage collector:
+
+(1) GC_SetMaxPause Mode
+
+    You can call ``GC_SetMaxPause`` at program startup and then each triggered
+    garbage collector run tries to not take longer than ``maxPause`` time. However, it is
+    possible (and common) that the work is nevertheless not evenly distributed
+    as each call to ``new`` can trigger the garbage collector and thus take  ``maxPause``
+    time.
+
+(2) GC_step Mode
+
+    This allows the garbage collector to perform some work for up to ``us`` time.
+    This is useful to call in the main loop to ensure the garbage collector can do its work.
+    To bind all garbage collector activity to a ``GC_step`` call,
+    deactivate the garbage collector with ``GC_disable`` at program startup.
+    If ``strongAdvice`` is set to ``true``,
+    then the garbage collector will be forced to perform the collection cycle.
+    Otherwise, the garbage collector may decide not to do anything,
+    if there is not much garbage to collect.
+    You may also specify the current stack size via ``stackSize`` parameter.
+    It can improve performance when you know that there are no unique Nim references
+    below a certain point on the stack. Make sure the size you specify is greater
+    than the potential worst-case size.
+
+    It can improve performance when you know that there are no unique Nim
+    references below a certain point on the stack. Make sure the size you specify
+    is greater than the potential worst-case size.
+
+These procs provide a "best effort" real-time guarantee; in particular the
+cycle collector is not aware of deadlines. Deactivate it to get more
+predictable real-time behaviour. Tests show that a 1ms max pause
+time will be met in almost all cases on modern CPUs (with the cycle collector
+disabled).
+
+Time measurement with garbage collectors
+----------------------------------------
+
+The garbage collectors' way of measuring time uses
+(see ``lib/system/timers.nim`` for the implementation):
+
+1) ``QueryPerformanceCounter`` and ``QueryPerformanceFrequency`` on Windows.
+2) ``mach_absolute_time`` on Mac OS X.
+3) ``gettimeofday`` on Posix systems.
+
+As such it supports a resolution of nanoseconds internally; however, the API
+uses microseconds for convenience.
+
+Introduction
+============
+
+.. raw:: html
+  <blockquote><p>
+  "Der Mensch ist doch ein Augentier -- sch&ouml;ne Dinge w&uuml;nsch ich mir."
+  </p></blockquote>
+
+
+This document is a tutorial for the programming language *Nim*.
+This tutorial assumes that you are familiar with basic programming concepts
+like variables, types, or statements but is kept very basic. The `manual
+<manual.html>`_ contains many more examples of the advanced language features.
+All code examples in this tutorial, as well as the ones found in the rest of
+Nim's documentation, follow the `Nim style guide <nep1.html>`_.
+
+However, this does not work. The problem is that the procedure should not
+only ``return``, but return and **continue** after an iteration has
+finished. This *return and continue* is called a `yield` statement. Now
+the only thing left to do is to replace the ``proc`` keyword by ``iterator``
+and here it is - our first iterator:
+
+| A1 header    | A2 \| not fooled
+| :---         | ----:       |
+| C1           | C2 **bold** | ignored |
+| D1 `code \|` | D2          | also ignored
+| E1 \| text   |
+|              | F2 without pipe
+not in table
diff --git a/nimdoc/rsttester.nim b/nimdoc/rsttester.nim
new file mode 100644
index 000000000..be2b56c67
--- /dev/null
+++ b/nimdoc/rsttester.nim
@@ -0,0 +1,42 @@
+# To run this, cd to the git repo root, and run "nim r nimdoc/rsttester.nim".
+# to change expected results (after carefully verifying everything), use -d:nimTestsNimdocFixup
+
+import os, strutils
+from std/private/gitutils import diffFiles
+
+const
+  baseDir = "nimdoc/rst2html"
+
+const fixup = defined(nimTestsNimdocFixup)
+
+var failures = 0
+
+proc exec(cmd: string) =
+  if execShellCmd(cmd) != 0:
+    quit("FAILURE: " & cmd)
+
+proc testRst2Html(fixup = false) =
+  putEnv("SOURCE_DATE_EPOCH", "100000")
+  const nimExe = getCurrentCompilerExe() # so that `bin/nim_temp r nimdoc/tester.nim` works
+
+  for expectedHtml in walkDir(baseDir / "expected"):
+    let expectedHtml = expectedHtml.path
+    let sourceFile = expectedHtml.replace('\\', '/').replace("/expected/", "/source/").replace(".html", ".rst")
+    exec("$1 rst2html $2" % [nimExe, sourceFile])
+    let producedHtml = expectedHtml.replace('\\', '/').replace("/expected/", "/source/htmldocs/")
+    let versionCacheParam = "?v=" & $NimMajor & "." & $NimMinor & "." & $NimPatch
+    let producedFile = readFile(producedHtml).replace(versionCacheParam,"") #remove version cache param used for cache invalidation
+    if readFile(expectedHtml) != producedFile:
+      echo diffFiles(expectedHtml, producedHtml).output
+      inc failures
+      if fixup:
+        writeFile(expectedHtml, producedFile)
+    else:
+      echo "SUCCESS: files identical: ", producedHtml
+    if failures == 0:
+      removeDir(baseDir / "source/htmldocs")
+
+testRst2Html(fixup)
+
+# Check for failures
+if failures > 0: quit($failures & " failures occurred.")
diff --git a/nimdoc/test_doctype/expected/test_doctype.html b/nimdoc/test_doctype/expected/test_doctype.html
new file mode 100644
index 000000000..2cbf6ec0f
--- /dev/null
+++ b/nimdoc/test_doctype/expected/test_doctype.html
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--  This file is generated by Nim. -->
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>nimdoc/test_doctype/test_doctype</title>
+
+<!-- Google fonts -->
+<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
+<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
+
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
+<!-- CSS -->
+<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
+
+<!-- JS -->
+<script type="text/javascript" src="dochack.js"></script>
+</head>
+<body>
+  <div class="document" id="documentId">
+    <div class="container">
+      <h1 class="title">nimdoc/test_doctype/test_doctype</h1>
+      <div class="row">
+  <div class="three columns">
+    <div class="theme-select-wrapper">
+      <label for="theme-select">Theme:&nbsp;</label>
+      <select id="theme-select" onchange="setTheme(this.value)">
+        <option value="auto">🌗 Match OS</option>
+        <option value="dark">🌑 Dark</option>
+        <option value="light">🌕 Light</option>
+      </select>
+    </div>
+    <div id="global-links">
+      <ul class="simple">
+        <li><a id="indexLink" href="theindex.html">Index</a></li>
+      </ul>
+    </div>
+    <div id="searchInputDiv">
+      Search: <input type="search" id="searchInput" oninput="search()"/>
+    </div>
+    <div>
+      Group by:
+      <select onchange="groupBy(this.value)">
+        <option value="section">Section</option>
+        <option value="type">Type</option>
+      </select>
+    </div>
+    <ul class="simple simple-toc" id="toc-list">
+  <li><a class="reference" id="check_toc" href="#check">Check</a></li>
+<li><a class="reference" id="text_toc" href="#text">text</a></li>
+
+</ul>
+
+  </div>
+  <div class="nine columns" id="content">
+    
+    <div id="tocRoot"></div>
+    
+    <p class="module-desc"><p>Check</p>
+<p><pre class="listing"><span class="Identifier">text</span></pre></p>
+
+<h1><a class="toc-backref" id="check" href="#check">Check</a></h1>
+<h1><a class="toc-backref" id="text" href="#text">text</a></h1></p>
+    
+  </div>
+</div>
+
+      <div class="twelve-columns footer">
+        <span class="nim-sprite"></span>
+        <br>
+        <small style="color: var(--hint);">Made with Nim. Generated: 1970-01-02 03:46:40 UTC</small>
+      </div>
+    </div>
+  </div>
+  
+</body>
+</html>
diff --git a/nimdoc/test_doctype/test_doctype.nim b/nimdoc/test_doctype/test_doctype.nim
new file mode 100644
index 000000000..28d35fc1e
--- /dev/null
+++ b/nimdoc/test_doctype/test_doctype.nim
@@ -0,0 +1,15 @@
+# Markdown (default) interpretes this as text + a Markdown code block:
+
+## Check
+## ~~~~~
+## text
+## ~~~~~
+
+{.doctype: RST.}
+
+# Now RST interpretes this as 2 headings:
+
+## Check
+## ~~~~~
+## text
+## ~~~~~
diff --git a/nimdoc/test_out_index_dot_html/expected/foo.idx b/nimdoc/test_out_index_dot_html/expected/foo.idx
new file mode 100644
index 000000000..ac76aa532
--- /dev/null
+++ b/nimdoc/test_out_index_dot_html/expected/foo.idx
@@ -0,0 +1,2 @@
+nimTitle	foo	index.html	module nimdoc/test_out_index_dot_html/foo		0
+nim	foo	index.html#foo	proc foo()		1
diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html
new file mode 100644
index 000000000..4370f0df8
--- /dev/null
+++ b/nimdoc/test_out_index_dot_html/expected/index.html
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--  This file is generated by Nim. -->
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>nimdoc/test_out_index_dot_html/foo</title>
+
+<!-- Google fonts -->
+<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
+<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
+
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
+<!-- CSS -->
+<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
+
+<!-- JS -->
+<script type="text/javascript" src="dochack.js"></script>
+</head>
+<body>
+  <div class="document" id="documentId">
+    <div class="container">
+      <h1 class="title">nimdoc/test_out_index_dot_html/foo</h1>
+      <div class="row">
+  <div class="three columns">
+    <div class="theme-select-wrapper">
+      <label for="theme-select">Theme:&nbsp;</label>
+      <select id="theme-select" onchange="setTheme(this.value)">
+        <option value="auto">🌗 Match OS</option>
+        <option value="dark">🌑 Dark</option>
+        <option value="light">🌕 Light</option>
+      </select>
+    </div>
+    <div id="global-links">
+      <ul class="simple">
+        <li><a id="indexLink" href="theindex.html">Index</a></li>
+      </ul>
+    </div>
+    <div id="searchInputDiv">
+      Search: <input type="search" id="searchInput" oninput="search()"/>
+    </div>
+    <div>
+      Group by:
+      <select onchange="groupBy(this.value)">
+        <option value="section">Section</option>
+        <option value="type">Type</option>
+      </select>
+    </div>
+    <ul class="simple simple-toc" id="toc-list">
+  <li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#12" id="62">Procs</a></summary>
+    <ul class="simple simple-toc-section">
+      <ul class="simple nested-toc-section">foo
+  <li><a class="reference" href="#foo" title="foo()">foo()</a></li>
+
+</ul>
+
+    </ul>
+  </details>
+</li>
+
+</ul>
+
+  </div>
+  <div class="nine columns" id="content">
+    
+    <div id="tocRoot"></div>
+    
+    <p class="module-desc"></p>
+    <div class="section" id="12">
+  <h1><a class="toc-backref" href="#12">Procs</a></h1>
+  <dl class="item">
+    <div id="foo-procs-all">
+  <div id="foo">
+  <dt><pre><span class="Keyword">proc</span> <a href="#foo"><span class="Identifier">foo</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    I do foo
+    
+  </dd>
+</div>
+
+</div>
+
+  </dl>
+</div>
+
+  </div>
+</div>
+
+      <div class="twelve-columns footer">
+        <span class="nim-sprite"></span>
+        <br>
+        <small style="color: var(--hint);">Made with Nim. Generated: 1970-01-02 03:46:40 UTC</small>
+      </div>
+    </div>
+  </div>
+  
+</body>
+</html>
diff --git a/nimdoc/test_out_index_dot_html/expected/theindex.html b/nimdoc/test_out_index_dot_html/expected/theindex.html
new file mode 100644
index 000000000..ca7c2d7af
--- /dev/null
+++ b/nimdoc/test_out_index_dot_html/expected/theindex.html
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--  This file is generated by Nim. -->
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Index</title>
+
+<!-- Google fonts -->
+<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
+<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
+
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
+<!-- CSS -->
+<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
+
+<!-- JS -->
+<script type="text/javascript" src="dochack.js"></script>
+</head>
+<body>
+  <div class="document" id="documentId">
+    <div class="container">
+      <h1 class="title">Index</h1>
+      Modules: <a href="index.html">index</a>.<br/><p /><h2>API symbols</h2>
+<dl><dt><a name="foo" href="#foo"><span>foo:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="foo: proc foo()" href="index.html#foo">foo: proc foo()</a></li>
+          </ul></dd>
+</dl>
+      <div class="twelve-columns footer">
+        <span class="nim-sprite"></span>
+        <br>
+        <small style="color: var(--hint);">Made with Nim. Generated: 1970-01-02 03:46:40 UTC</small>
+      </div>
+    </div>
+  </div>
+  
+</body>
+</html>
diff --git a/nimdoc/test_out_index_dot_html/foo.nim b/nimdoc/test_out_index_dot_html/foo.nim
new file mode 100644
index 000000000..f61995eb9
--- /dev/null
+++ b/nimdoc/test_out_index_dot_html/foo.nim
@@ -0,0 +1,3 @@
+proc foo*() =
+  ## I do foo
+  echo "foo says hello!"
diff --git a/nimdoc/tester.nim b/nimdoc/tester.nim
new file mode 100644
index 000000000..0c0be3699
--- /dev/null
+++ b/nimdoc/tester.nim
@@ -0,0 +1,157 @@
+# Small program that runs the test cases for 'nim doc'.
+# To run this, cd to the git repo root, and run "nim r nimdoc/tester.nim".
+# to change expected results (after carefully verifying everything), use -d:nimTestsNimdocFixup
+
+import strutils, os
+from std/private/gitutils import diffFiles
+
+const fixup = defined(nimTestsNimdocFixup)
+
+var
+  failures = 0
+
+const
+  baseDir = "nimdoc"
+let
+  baseDirAbs = getCurrentDir() / baseDir
+
+type
+  NimSwitches = object
+    doc: seq[string]
+    docStage2: seq[string]
+    buildIndex: seq[string]
+    md2html: seq[string]
+    md2htmlStage2: seq[string]
+
+proc exec(cmd: string) =
+  if execShellCmd(cmd) != 0:
+    quit("FAILURE: " & cmd)
+
+proc testNimDoc(prjDir, docsDir: string; switches: NimSwitches; fixup = false) =
+  let
+    nimDocSwitches = switches.doc.join(" ")
+    nimDocStage2Switches = switches.docStage2.join(" ")
+    nimMd2HtmlSwitches = switches.md2html.join(" ")
+    nimMd2HtmlStage2Switches = switches.md2htmlStage2.join(" ")
+    nimBuildIndexSwitches = switches.buildIndex.join(" ")
+
+  putEnv("SOURCE_DATE_EPOCH", "100000")
+  const nimExe = getCurrentCompilerExe() # so that `bin/nim_temp r nimdoc/tester.nim` works
+
+  if nimDocSwitches != "":
+    exec("$1 doc $2" % [nimExe, nimDocSwitches])
+    echo("$1 doc $2" % [nimExe, nimDocSwitches])
+
+  if nimMd2HtmlSwitches != "":
+    exec("$1 md2html $2" % [nimExe, nimMd2HtmlSwitches])
+    echo("$1 md2html $2" % [nimExe, nimMd2HtmlSwitches])
+
+  if nimDocStage2Switches != "":
+    exec("$1 doc $2" % [nimExe, nimDocStage2Switches])
+    echo("$1 doc $2" % [nimExe, nimDocStage2Switches])
+
+  if nimMd2HtmlStage2Switches != "":
+    exec("$1 md2html $2" % [nimExe, nimMd2HtmlStage2Switches])
+    echo("$1 md2html $2" % [nimExe, nimMd2HtmlStage2Switches])
+
+  if nimBuildIndexSwitches != "":
+    exec("$1 buildIndex $2" % [nimExe, nimBuildIndexSwitches])
+    echo("$1 buildIndex $2" % [nimExe, nimBuildIndexSwitches])
+
+  for expected in walkDirRec(prjDir / "expected/", checkDir=true):
+    let versionCacheParam = "?v=" & $NimMajor & "." & $NimMinor & "." & $NimPatch
+    let produced = expected.replace('\\', '/').replace("/expected/", "/$1/" % [docsDir])
+    if not fileExists(produced):
+      echo "FAILURE: files not found: ", produced
+      inc failures
+    let producedFile = readFile(produced).replace(versionCacheParam,"") #remove version cache param used for cache invalidation
+    if readFile(expected) != producedFile:
+      echo "FAILURE: files differ: ", produced
+      echo diffFiles(expected, produced).output
+      inc failures
+      if fixup:
+        writeFile(expected, producedFile)
+    else:
+      echo "SUCCESS: files identical: ", produced
+
+  if failures == 0 and ((prjDir / docsDir) != prjDir):
+    removeDir(prjDir / docsDir)
+
+# Test "nim doc --project --out:.. --index:on .."
+let
+  test1PrjName = "testproject"
+  test1Dir = baseDir / test1PrjName
+  test1DocsDir = "htmldocs"
+  test1Switches = NimSwitches(doc: @["--project",
+                                     "--out:$1/$2" % [test1Dir, test1DocsDir],
+                                     "--index:on",
+                                     "$1/$2.nim" % [test1Dir, test1PrjName]],
+                              buildIndex: @["--out:$1/$2/theindex.html" % [test1Dir, test1DocsDir],
+                                            "$1/$2" % [test1Dir, test1DocsDir]])
+testNimDoc(test1Dir, test1DocsDir, test1Switches, fixup)
+
+# Test "nim doc --out:.. --index:on .."
+let
+  test2PrjDir = "test_out_index_dot_html"
+  test2PrjName = "foo"
+  test2Dir = baseDir / test2PrjDir
+  test2DocsDir = "htmldocs"
+  test2Switches = NimSwitches(doc: @["--out:$1/$2/index.html" % [test2Dir, test2DocsDir],
+                                     "--index:on",
+                                     "$1/$2.nim" % [test2Dir, test2PrjName]],
+                              buildIndex: @["--out:$1/$2/theindex.html" % [test2Dir, test2DocsDir],
+                                            "$1/$2" % [test2Dir, test2DocsDir]])
+testNimDoc(test2Dir, test2DocsDir, test2Switches, fixup)
+
+# Test `nim doc` on file with `{.doctype.}` pragma
+let
+  test3PrjDir = "test_doctype"
+  test3PrjName = "test_doctype"
+  test3Dir = baseDir / test3PrjDir
+  test3DocsDir = "htmldocs"
+  test3Switches = NimSwitches(doc: @["$1/$2.nim" % [test3Dir, test3PrjName]])
+testNimDoc(test3Dir, test3DocsDir, test3Switches, fixup)
+
+
+# Test concise external links (RFC#125) that work with `.idx` files.
+# extlinks
+# ├── project
+# │   ├── main.nim
+# │   ├── manual.md
+# │   └── sub
+# │       └── submodule.nim
+# └── util.nim
+#
+# `main.nim` imports `submodule.nim` and `../utils.nim`.
+# `main.nim`, `submodule.nim`, `manual.md` do importdoc and reference each other.
+let
+  test4PrjName = "extlinks/project"
+  test4Dir = baseDir / test4PrjName
+  test4DirAbs = baseDirAbs / test4PrjName
+  test4MainModule = "main"
+  test4MarkupDoc = "doc" / "manual.md"
+  test4DocsDir = "htmldocs"
+  # 1st stage is with --index:only, 2nd is final
+  test4Switches = NimSwitches(
+      doc: @["--project",
+             "--outdir:$1/$2" % [test4Dir, test4DocsDir],
+             "--index:only",
+             "$1/$2.nim" % [test4Dir, test4MainModule]],
+      md2html:
+             @["--outdir:$1/$2" % [test4Dir, test4DocsDir],
+             "--docroot:$1" % [test4DirAbs],
+             "--index:only",
+             "$1/$2" % [test4Dir, test4MarkupDoc]],
+      docStage2:
+           @["--project",
+             "--outdir:$1/$2" % [test4Dir, test4DocsDir],
+             "$1/$2.nim" % [test4Dir, test4MainModule]],
+      md2htmlStage2:
+             @["--outdir:$1/$2" % [test4Dir, test4DocsDir],
+             "--docroot:$1" % [test4DirAbs],
+             "$1/$2" % [test4Dir, test4MarkupDoc]],
+  )
+testNimDoc(test4Dir, test4DocsDir, test4Switches, fixup)
+
+if failures > 0:
+  quit "$# failures occurred; see note in nimdoc/tester.nim regarding -d:nimTestsNimdocFixup" %  $failures
diff --git a/nimdoc/testproject/expected/nimdoc.out.css b/nimdoc/testproject/expected/nimdoc.out.css
new file mode 100644
index 000000000..0c399e4c1
--- /dev/null
+++ b/nimdoc/testproject/expected/nimdoc.out.css
@@ -0,0 +1,1036 @@
+/*

+Stylesheet for use with Docutils/rst2html.

+

+See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to

+customize this style sheet.

+

+Modified from Chad Skeeters' rst2html-style

+https://bitbucket.org/cskeeters/rst2html-style/

+

+Modified by Boyd Greenfield and narimiran

+*/

+

+:root {

+  --primary-background: #fff;

+  --secondary-background: ghostwhite;

+  --third-background: #e8e8e8;

+  --info-background: #50c050;

+  --warning-background: #c0a000;

+  --error-background: #e04040;

+  --border: #dde;

+  --text: #222;

+  --anchor: #07b;

+  --anchor-focus: #607c9f;

+  --input-focus: #1fa0eb;

+  --strong: #3c3c3c;

+  --hint: #9A9A9A;

+  --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN4AAAA9CAYAAADCt9ebAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjAzOjQ4KzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMjoyODo0MSswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMjoyODo0MSswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozMzM0ZjAxYS0yMDExLWE1NGQtOTVjNy1iOTgxMDFlMDFhMmEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MzMzNGYwMWEtMjAxMS1hNTRkLTk1YzctYjk4MTAxZTAxYTJhIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6MzMzNGYwMWEtMjAxMS1hNTRkLTk1YzctYjk4MTAxZTAxYTJhIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDozMzM0ZjAxYS0yMDExLWE1NGQtOTVjNy1iOTgxMDFlMDFhMmEiIHN0RXZ0OndoZW49IjIwMTktMTItMDNUMDE6MDM6NDgrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4wIChXaW5kb3dzKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4PsixkAAAJ5klEQVR4nO2dfbBUZR3HP3vvxVD0zo0ACXxBuQMoQjJ1DfMl0NIhNcuSZqQhfGt6UWtK06xJexkrmywVRTQlHCIdtclC0zBJvYIvvEUgZpc3XyC7RVbKlQu1/fHdbc+uu2fPOfs85+y55/nMnBl2z+5zfnc5v/M8z+8119XVRYroAG4HfgvMT1YUR4MMAa4HLkhakCRoSVqAELwLeBY4C7gF+D6QS1QiR1ROAJ4Dzk9akKQwoXhtwL4GxvHjU8AKoNPz3leAu4HBFq+bAyZZHD9rDAK+BywDDklYlkQxoXhfAtYAEw2MVckQYBHwU6or99nA08BBFq49GngUeBIYaWH8rNEJdAOXA60Jy5I4jSreSOBKYDzwBPCJhiUqcSjwe2BWnc9NLnxuvMFrnwqsAqYBBwBfNzh2FpmNfs9jkhakWcg1aFxZiH5UL3cDnwf+Xue7BwFjgFHAOwuv24tyob3cO0LIshP4EbCn8Pq/wKvA9sLxMvCvOmPsA1yDZnHv/nEv2mM+F0IeR4m8z7lM7tMbUbzj0CxX7YfbAXwaWFJ4PRrNIu9FS9KJyEIZN68CG4DnkRJtLBw7gHHAYuDdNb77EDAjBhkHIk7xKoiqeK3IwjilzuceQJvoZjdQ/AMZaeoZiWYgBXSEwyleBW0Rv3cR9ZUO4LSI48fN2wN+bi5wJNBvUZaBSCaVy48oxpVhwDdMC5ISxpJRh6/DLGEUrxXt29YBQ+2IkwquR76ofZIWxJFegireNLSnm48skFmmDfmiVgJHJyuKI620ADOpbWEcDPwYOZKD7OmyxCTkXL+wzueOiEEWR8poQb60V4A7kLm/yFjgKeALuM1xLfYDbkX+zEGe98cAX0Oui6viF8vR7OS6urragW2UZr21wK+Aiwlu7XPoN3sYOAd4H6WH1SnA0qSEcjQnRT/e1bgnsw16kGPez4/lyCBF48oNwL+TFGSAsgCndI4qFBVvJ0owdZhjL3CnxfHzBo8+YBMyol0CHBijrKbHS/LoA7Yio9sPgJNr/QHekLGR6MffL+KP4SjnHmQxtoXNmbQP+CHyV75hYDzTIWNpWkU8iR5mq71vVsZqXgtcFqNQ/wG2IOtfD8oi6AX+Ujj+isKz8sBrnu+1okyGdmD/wnEgcDClTIdRyJRvI1cvCMciq7At4rj5eoCPAusbHCfLigda/VyKgi+AtyreMGAzykGzQQ/wO+BxSlkCuy1dq8hw5OieUjimYT+x9bHCdWwS1823Ez1EXmhgjKwrXpHzkduuanbCtzGX+NkPPAj8GincNkPjNkIO5dadUjiOB95m+BonopQpm8R58/0JJbHWy2eshVM8sRvdbyurKV4Hmoka2WA/iwwLP6d+QmzSdKC92GzK/W9R+Q3woQbHCELcN991wJcjftcpXolngKm18vFmoVonYcgDv0Qz5pqGREuOTuA8lPYUZbndh0LJNpkUqgZx33xvomim7RG+6xSvnOm1gqQXoyiMoKxFs8VZpFfpQHvQK4HDUPnAsBa9bxGP0tUjF+IYCkxFew+/G3owdq20pgjzt3uPRscs/o43IaOhH2f4ZaAPRyZQP6vgbuCbyGext87F0sgIZFI/N8BnlwBnolovcWAjq/uzwM0+55cBJ0UYN84ZL+rfbnLMM4FfUDv7Z1XlCe8FetETbleNL7+CZrnvMjCVDuTOOA84Hf+96ga0PC8qXY50FQsuMg+41+d8p885R4n7gdt8zo+qvDkmUF4fZQXwEbS+99KDMhlWkw0eALqQglXyDDCdcovf+4lv5jPNXJ9zWc/FDMMdPudGVCreRlTWwVtWbynwYVQQCFSp61Q042WJLUjB1nneuw8tvXo97x1Lugvg+j1Mo9boySLVHtJFWqsthx5GlbSGeN5bigrHdqPl52Zj4qWLXvTQWY4KOX2ccgPMBLRcuy9+0YzhguXN4GuYq2Zc2R/NZg+hfYt3/9ZCepdQthmB4vIWIYOTbWyWzGt2Y0izG1fqjlltxnsdpbPMRMmd3lqTTumqMw7FZY5G5mSHw5dalreiRWYGWjbZ7gYUlFa0xOtIWA4vk1E6zWEoI+FvyYrjSAO1FG8DCmQGKd+DJFsGogWVVFiP/GWbga9Svg9NgtPQvnd04fUNCcriSBF+vqZ5nn9PQ+Xs4q401oI6EP0R+BkyXoAeAtcgBfwidnvkVaMVFTO6n1JoWTfqiONw1MVP8e6l3GVwOPJZXW5VItGGiuduAu5CZdOrMQJ1CHqpIFccS+LxaD/3Hcr7vF0Xw7UdAwQ/xduLGkJ6aUMhVAuwU006B3wM+ZLmozJ5QRhWkGs9yjKw1fhwDsq8eE/F+y+i1CeHIxD1wppupXrA5xyUOjQHMzU3cyjTeS2aaaN2Fzoc1bhch3xspuqBTkDulQVUz1q4mYEbNuewQD3FexGFS1VjOLoRHwOOinj9HAooXY2CSidHHKeSI5GFcRWNdSxqR7VH1iHHeTV24R+X53C8hSCBvPPqnD8B+AOygn6OYAm0ORSGthLl8B0d4DtRmIKsoMsJF1U/Hi1dt6DusIN8PrsIlUdwOAITpDFlC6q3MTbgmHm011qGepOvQSXPipyOCujW6rxqk0dRWYsVFe8PRSn5JxWOoEvdfOGzfnF5tnCRK+bGi33MoB1hL0U5d1H5J5oVD6A5mp8sQS6KSWh5e0jEcR4BPmhKqJA4xTM3XuxjBlW8DuRacDU3y0myNbNTPHPjxT5m0GTN15A/zVFiI+HKYzgc/ydMlrRfgmQWuYn0F91xJEQYxVuDnMcOrQAWJi2EI72ErQviwqLEQpQ+5XBEIqzi3YWLwF+BMiMcjshEqYR1Gdk1KmxBsaR9SQviSDdRFK8fxVU+YliWZmcbcq7vSFoQR/qJWvuxD0WgLDYoSzPzAqowtjVhORwDhEaKru4GPoliGgcyy4Hj0DLT4TBCo9WO88jQ8Bns97lLghvRTOfqqDiMYqrM+HyUYdBtaLykeRmlK12C9rQOh1FM1vd/HqUIzaT5e+LVoh/VxByHShs6HFaw0VjjHhTxP5d0LT+fRnu5q3HuAodlbHW02Q5cDByM+sw1642cRylCx6PeZiuTFScUFxK+f19QovaRS+t4tsasxhvABbZbSfUCV6CM7qtQl6Fm4E1U22UqcAYqvZ42fgJMxH6vdYc5nkBlSW6Pq4fbS6hb6jg0u9yGug7FyS5U1+UcVBbwbFSuMM1sQ1bXK4A9CcviqM0e9H80HdUxCpwIa4McygA/GfgAcCJqmGKKXUixupEv7nHsLc2agWNQ0d9OzC+PHNHIo1XeLCoe8kkqXiUtwKFoWXoEKqk3BpWLaC8cXsV8HT1J+tFTZKvn+DMqFZi1knvtyKg1O2lBHADcCVxEedNSAP4HJcsr0NNWHVUAAAAASUVORK5CYII=");

+

+  --keyword: #5e8f60;

+  --identifier: #222;

+  --comment: #484a86;

+  --operator: #155da4;

+  --punctuation: black;

+  --other: black;

+  --escapeSequence: #c4891b;

+  --number: #252dbe;

+  --literal: #a4255b;

+  --program: #6060c0;

+  --option: #508000;

+  --raw-data: #a4255b;

+

+  --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: black' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E");

+  --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: black' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E");

+  --clipboard-image: var(--clipboard-image-normal)

+}

+

+[data-theme="dark"] {

+  --primary-background: #171921;

+  --secondary-background: #1e202a;

+  --third-background: #2b2e3b;

+  --info-background: #008000;

+  --warning-background: #807000;

+  --error-background: #c03000;

+  --border: #0e1014;

+  --text: #fff;

+  --anchor: #8be9fd;

+  --anchor-focus: #8be9fd;

+  --input-focus: #8be9fd;

+  --strong: #bd93f9;

+  --hint: #7A7C85;

+  --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARMAAABMCAYAAABOBlMuAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjE4OjIyKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQyNDQtYTExZi0yMjc5YmY4NDNhYTAiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ZWRlYjM1NzAtYjZmYy1kMjQ0LWExMWYtMjI3OWJmODQzYWEwIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6ZWRlYjM1NzAtYjZmYy1kMjQ0LWExMWYtMjI3OWJmODQzYWEwIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQyNDQtYTExZi0yMjc5YmY4NDNhYTAiIHN0RXZ0OndoZW49IjIwMTktMTItMDNUMDE6MTg6MjIrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4wIChXaW5kb3dzKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4JZNR8AAAfG0lEQVR4nO2deViTZ7r/7yxkJaxJ2MK+GCBAMCwS1kgUFQSKK4XWWqsz1jpjp3b0tDP1V+eqU391fqfT/mpPPd20drTFDS0KFEVWJSGAEgLIZpAICBJACIRs549Rj1WILAkBfD/XlevySp68z/0S3+/7vPdzLyidTgcLkU2bd+z39/f/q1gshsrKSoJELFCa2iaEuU9K6kb+8uXxv54/fzE8L/eswNT2zCfQpjbAGKS8lPFKSEjIXiaTCSEhIeDj4xNnapsQ5j6rktZGp6UlfxIdzQVzCplmanvmG1hTG2BIAtlc26CgoDfT0tL2e3l5AQCAjY0NkMnk/a9s2k6rrKw8UV8n1JjYTIQ5RlAw14KzmL3xze1vfJyUuMJaq9UCFovFm9qu+YbBxcSPFUYkk8l2Q0NDsvo6ocrQx5+I8Ih4bz6f/0l8fHyKlZXV4/dRKBQwmcwwMpn8A4FAoPgHhH9bV1sxa488wZxoaycnJ/a9e/duCa5fkc3WvAiTI4Ib77p+XdqHG9anbfLy8gAAgLGxMdBpF+bjvzExqJj4scKI0dHRnwQHB++orq7+AgDeMuTxJ2Jl4rqU9PT0EwEBAUQCgTDuGAaDAampqYepVKpHUHDk325Ulw0a266YuFW+Gzdu/MDPz29jfn7+XgA4aOw5ESZP6kvpCXv3vnM8NiaSamVl+fj9BepGNDoGFRN7e/slcXFxO1xcXMDJyWnH7j//H/fi4uJdgutXmgw5z5O8smn7X9euXbvf29sbMBjMhONQKBRYWVlBbGzsbjMzM3JoOG+/sKKwy1h2rd/4elpGRsYuLy+vaDweD2w2Oy1h5ZrCvEunEaeeiVnMiabyl/F2/+X9P+8JDPQHHA5napMWBAYTk6DgSNuEhIS9DAYDAP7tq1i6dOkqOp3OWbNu0wens44emeoxA9lcWwKBYEMkEm2JRKIdHo+3QKFQWJ1Op8ZgMER3d/dVq1evTnFycpr0MSkUCsTExGzH4/Gk1LTME/39/TI0Go1FoVCg1WrVY2NjipGRkcGRkRH5dPwrEZHLXMPCwjJSUlIy3dzcfB+97+rqGhYSEpIOAIiYmBguN3zL77dt3uPh4W5qUxYUBhMTb2/vjeHh4cvR6P/dILK0tITIyEg7BweHr363/Z3Ampqaf1Zcu/zMKiVsyVJvMplsRyKR7IhEor2FhYUbhUJhJCYm2pFIJB6JRAIymQx4PB7QaDRoNBowMzMDJycnwOOn7icjEokQGxu7icFgbLp///7jFY1WqwWlUgkjIyOgUCgO7Ni5Rz48PCwfHh7uGRkZeaBQKOSjo6ODCoVCXlNVKn/6uCsT13FXrVr1emho6BYKhfLMnP7+/omrU9LPX8g+UThloxEMxqJFXjxESAyPQcSEExrLWLNmzW57e/txP/fw8ABHR8cdDAaDt3xF2ru9vb03sVgs0cbGxs/FxWVZUlISj0aj+dna2oKtrS1M5PcwJCgUCry8vODRrs84vPfoH6OjoyCXy6Gvr+/R6+CWrX9s7evrk/b19bWr1Wqli4sLZ8OGDe95eXmxUSjUuAd0cHDwjoqK2sYKXFIhvnldYYTTQpgU4/8+jyASCYDGoCd+ZkYYF8OICYezl8PhuOkbQyAQIDo62s/NzS2np6cHbGxsgEajAYFAAAwGA1gsFia6CE0NgUAABwcHsLe3B61WC2q1eo9WqwWNRgNKpRLUajUQiUSgUCh6zwGHwwGTydzo5+eXBQBnZu8MEJ5keHhYPqyYWMtHR0ZBpVIhYj9FUDONgOUvT12+du3avMDAQJjssdRqNWCxCyrEZdLodDoQi8Ulx44de628NL/V1Pa8iERE8l2dHB2CJvpcq9Nqbt1qKURWj1Njxld0ZGTkAW9v70kLCQC8sEIC8O/HKx8fn2gmk8kHgCk7pRFmzrWyAikASE1tx0Jj2uH0EZHL/N7YtuvT4OBgzmz4OBYSeDweIiMjt2S++vtMP1YYEmmJsCCY8mNOIJtr6+zsHBcZGXmIw+G4mZubG8m0hU9HRwcUFxe/KxQKTyDRsQjznSmJCS9+dVRERMTfQ0NDo2xtbfUGiSFMjtHRUaitrc3Jzc09kHvxVLmp7UFAmC6oZQkvrZLL5RJhReHtiQb5scKIXC7371FRUX90dnYGIpE4JR8Jgn40Gg20t7fXFxYWfnr9+vWjz8sdYi+Osh4vzgUBwZSgtu94V+fs7Hx7YGCgra6u7khLS0u2RCwYeTQgKmYFh8fj/f/g4OAldnZ2prR1wdPd3Q1CofBQSUnJkdLi3N8E93FCY6k+Pj48FxcXjlar1ZSWlh65VvYr4kREmDNg79+/D3FxcW5OTk5uXl5evNbW1tL0jK3ZXV1d1ykUintycvInoaGhdkj+gvGxs7MDPp+/m0AgWMQvS/lyeHhYTqPRPJycnIJSU1NZ3t7eW2g0Gly/fv2oWq1Gij0hzClQ/gHhpLS0tEM8Hm/7I8Ho7++HlpYWsLa2Bg8PDxOb+OKhUCigqakJ7t+/D25ubuDu7g4oFAp0Oh08ePAAvv7666TTWUdzTG0nAsKTYMU3ryuSU18+4+bmFrZo0SIOAICVlRUsXrx4zkakLnRIJBI8CgJ8MtdJp9NBZ2enqL29XWRC8xAQxgUNAHD+3L8KGhoaCp78ABES04JCoX4jJAAAAwMDUFtbe96YpRMQEKbL41DU5ubmko6Ojj2PSgggzD36+/vrb9y4cX425zzw93/8EBjon2is44+NjSkePBjqGRwc7G5v7xBV19w8U5B/3qgrr9+/uWtXUuKKD/TZ9MXh/066/OuFmunO8dGBQ98HBbGSp/t9U6LRaDXK0dHBoeFhuVzeL22/0yFqamopufjLqRJ933ssJi0tLSXV1dWHGAzGbuObOzs8ubqa71vZKpUKOjo6blwpOF8zm/Mu5cVkLlkSaswprAHAaVihgK7O7oSGxltvfXLon3nXK4RHT2cdN4pfKDCAlZyUuMJan02nTmczAaBmunPw4qI3cbnh0/36XICq0+lgcPABp7OrK629vUP5z8++LLh2XXD05L++yxrvC4/F5EZ12WBS8saLS5Ys2U2lUufUY45SqQSlUgkqlQrUavXj19jYGGg0GtBoNKDT6UCn05VotVq1TqfToFAojFar1eh0Og0Wi8XhcDgeGo1+/PhgZmYGOBwOsFgsmJmZ/eY1F+nt7YXa2trs2Z73wdCQBgCMHp1IJpHA09MdPD3dLRIS+OtKisvWvbP7vf2lZdePVFwzbHTwyMiI3hidkZFRUKvUYzOZ48HQkBIA5nWqBAqFAktLC7C0tADmIh88Pz4uMSyUk7hn776DV4tKPn/6d/lNxp1MJqsRCASf8vn8XdMpOjRTVCoVjI2NgUqlAq1WCyMjI9DX1wf379+Hvr6+/Q8ePOgdGRmRKxSKx0WLFAqFXKlUKnQ6nUar1arHq47mxwrD4/F4Eg6HI2GxWDwej7cgkUjWFAqFam5uTjU3N6eRyeQPLSwswNraGqysrIBAIDwWFywW+zja11Qi29LSclIikeSZZPJZBovBAI8XA8HBQR9kZZ3lR8cmvFZSlGe00p8IkwONRkNERBj4+i7a4+XpHv307/IbMakWlciXJbx0nMPh7Jqo0JGh0el0MDo6Cl1dXSCVSkEmk7177969W319fe1DQ0M9KpVKoVarlWq1WjndNhUPG3ApAWDcOxLTLwSDwWAOotFoDBaLxRMIBAsrKysne3t7Xzqd7k2n0/c4OzsDlUoFHA4364IyMDAATU1NxdWikhcq6tXKyhJezljPJZKI2eERS5cZeoWCMD2srCwhPX0tVzk2djiCG//GtfLLUoBxShB0dHTU3Lx580sLC4vtJBLJKMZoNBqQSqUglUqPdnR01PT09DT19/fLHjx40DM0NNQ72933GiSVGgB4JFQK+LfoSAGgnL04yppEIh2xtLS0t7GxcaFSqR7Ozs4fMRgMcHR0nJX8pJs3b54Ui8UXjT7RHIRMIkFK8irfwcEHPwQELUmqvYHUGJkLmJubw8YNa/i9vfffY/px3myQiDTPiEl9nVDDX576jaenZ7SnpyfLUJNrNBqQyWRw+/bt4x0dHTdkMlltV1dXw/XygjkdEv4wB0YOAK0AUM70C8HQ6fSzdDrdm0qlejg6OrLc3Ny2MBiMadWjfR4PHjyAmzdvZs/1v5MxoVAokJK8iicWS95k+nH+s0EiQhqpzQGoVFtYk5a87ba0XQAA34xbpagg/5zoT7s/OGNnZ8eaaYkBuVwOnZ2d5VKpVNTS0lLS2NhYWFVZ3Dujg5qQh6uY+ocvCAiKIPn4+Jz19PSMdnV15VCpVL6Dg4NBViw6nQ5EItHRpqamqzM+2DzHzo4O69amftLQeKsAZrDLgmBY/PyYsCIhfs+SiKUFE5Y8EwqFx11cXDihoaFTjjFAoVAwPDwMHR0dourq6jNCofDHhZqUVnvjmgIAcgAgJyg40mLRokX8kJCQjT4+PussLS1n1JPl7t27UFxcfHguB6mNjY2B7G4naNRTWyygUCjAYDGAx+PB0sICSCSi3vFYLBbCwjjA8vddBQtATKb7d3saBwc7IJPJBpsHjUGDGRYLJBIJLK0sAfucmyIGg4FFi3y8AwNZtycUk5KiS02vvf7WWQaDkejg4DApQwAeh3xDaWnpPoFAcPxFqnP6sEvgGf+A8Bx3d/cvIyIiNi1evHjT8wpNj8fAwACUlZW9P9dD5+/ckcFbf9gd2dcnn9LNAovF4inmZHtXNxdOdBR3+/JlS33pdP29wolEInA4weuiYxOy5vvuTkeHDHb+8c8xvb33Z3R9/N+Df+uIjYk02DwkEsna2trS1d/fNyGeF7uTyw1/7g3R3t4O2OxA/TVghULhcQqFQk1JSfmYSNR/5wD4d6EfgUBwvLS09IhUKhW9qAV5H9YjKQwJi6uvrKw8ERoamhkSEpKp7w7yJEqlEiQSyZmysrJv53qjdaVSCZdyTk+3qFMrAJRHRPLPN95qeifj5fU7mYt8JhyMRqMhMJDFdnF25gDAvBYTpXIMWlpay2fq/8m5mDcIABYGnEcGAGI/VlhBZWX1yZdSkz55OX0dV5+7w9bGGvz8mPrFpK62QskJjf2GTqd7x8bGbpnID4BCoUAmk0lLSkqOiESik2UleS/MakQflYKrXQDQxY1a3tTe3i6KiIjY5OXlxX7e9+rr6wsuXbr0t4ffn9OgMWjghMZQRcLp+8GulRVI/QPC37Wxtnal0ajJtjY2E451ZjiBra31vE9lR2PQQKFQaAAwo98Yi8Xq9fpPd56HO6rlvKWJv/PwcK+JilyCmajWMw6HAzs7+rMFpQOCIn6zHywSFvXm5eUdFAqFZ9Rq9bgHa2trq79w4cK+zz49cAARkmcpL81v/a/Dhz49d+7c3qqqqjyVSjXuOJ1OBxKJpDw3N/fA5V+zax6978cKw/sHhM/raMrnUVdboSy4fPWQSFSjd5yFBQWIRNKEd2IEw1J4JUd88WL+R51d3XrHWVDMnxUTa2tr1zXrNiUGsrmPf7DS4tymCxcu7Kuurs55+kKQSqVN586d23vs+8NHDXUCC5Wzp3/Iy8rKeruysvLM2Nhvo7VVKhXU1tYWnj17du/T7UOdnZ2D7OzsfGGB09raVi4S1RzXl0eFw+EAj8chYjKLVFffyOrq1C8mJBLpWTFRKBRyDofzC4vFWvXk+1ev/CLOzs7eKxAIslQqFeh0Oujp6enKzs7em/XTd7OayTqfKb56sT4rK+sPAoHg5KO/o0KhAKFQmHXy5MkdF3/5+TeZmctXpIXZ29v7zqVcKWNRX1epuXu3U/y8pEw0GmndOZt0dnXVDw0P6/W5oNHoZ30mQ0NDPb29vfvj4+Pf3rR5B/7od188XnEUXr4gDgmL+0NfX5/U19d3d3l5+YGfTnyDtLmcIhXXLsu4UcvfR6PRGGtra9eysrIjYrE45+kt4Fheou/69es/unnz5vm7d+/Wmsre2WRkZGTQ1DYg/JYGiUiTm1ugBAC9IfHPiEmDpFITE7fqJI/H27lmzZpDq5LWtz55t6wUXO3ihMYerK+vz2tpaUFaM0yT8tL81ujYle+TSCTrvEunBU9/voTLd92wYcPHVCqV39XVdXCu7+oYCp1O90Kc50Jk3I5+xVcv1jc3N5d4enpSMzIyvkpK3sh78nORsKg3++yPBS/q1q+hKCm61DSekERGJ3ikp6d/ERsbm1xVVXWwtbX1hRFtFAqFPMLMUyZsDyoQCI7LZDKIiIjwzczM/GpV0vro2TTsRSUqZoX3+vXrP1u9enXi0NAQiESirIdRtggIc5oJ40zq6uryGhoa8ry8vBJCQ0O9USjU94mrN7yWc+EnvaXb5gJMvxCMp6cnl0Kh2Le1tZVXXLs8L1LXefGrWRkZGZ/x+XyeUqkEkUh0vqenZ14HZyG8OEwoJjdrygd37NxTEBkZmWBtbQ3BwcEeKBTq+/UbX3/355Pfzlmn66qk9dGbN29+k8PhbCSRSNDZ2Snb9ae/HCkpKTksEhbN2QTD5NSX+Vu3bj0cHBzsjcFg4O7du1BWVvbNwxB9BIQ5j94I2Fu3bhXW19cDl8sFLBYLHA7Hg0wmf/e77e84ffXlPz6fLSMnQ2paZkJ4eHjmtm3b+B4eHvZkMhlQKBTY29s72dvbfxgUFJT8x7ffP1NRUfHjXErnZ/qFYKKjo7dt3rz5g8DAQPtH/XHa2tpqGhsbC55/BASEuYFeMblz505NTU3NgfDw8PcwGAygUCjw9fW1IJPJn/1130Hv0tLSI4WXL4hny9inYS+Osvbz80tgMpn8jIwMPovFch2vpoiDgwM4ODhwfH19OYsWLeJv3/Hu+cbGxquzXZz5aZYlvMRJT0/fFhkZue3JZmfd3d0gEolOIr4ShPmEXjFpkFRqXlrzSnFnZ+d7Tk5OjzNfXVxcICMjY6ezszNnVdL6vU8HWhmbgKAIkrOzMyc1NTXz0YU4maAuOp0OK1as4EVFRfGEQqHg1dfePHzr1q2rs71S8WOF4f38/BLS09M/iIyM5DxdxLq5uVlcVVU1bgVwBIS5il4xAQCQyWRigUBwJikpKe3JVGQcDgdLly7l2tranti0ecf7IpEoy9hbxX6sMDydTvdevXr1ltjY2F3u7u6AxT73FJ7B3Nwc4uLiwthsdphQKCzZkL7l0/r6+oKbNeVG90+EhMXZL1++fFtycvKHrq6uz4igUqmE5ubmEiTHCWG+8dwrUXD9imz9xtd/jIuLS7N5KpsTjUZDUFCQE4PB+F4oFGYmJW888Mv5k4UTHGpGxC9LYaenp78VEhKyxdHRESgUyoyOh0KhwNraGuLi4qIDAgKi6+rqyjekb/mHMSN6N6RvSdu+ffseNpsdZm09ftuW+vp6EIvFSB9hhHnHpG7rUqm0orW1tdXS0tLj6TIEaDQaaDQaxMfH811dXTl/3Xfw+JUrVz411J01cfWG6IiIiC07d+5McHNzs7ewMGyOFw6HAwcHB6BSqVx3d/fwz7/4rkAgEBwXCoUnHpZonDGrU9J5MTEx27du3Zrm4uKC0beaqq6u/ry+vj7XEPMiIMwmkxKTimuXZe/u+fCkp6fnexPdUfF4PPj7+1szGIydLi4unF1/+kvenTt3RG1tbRXTqfma8lIG39/fP/HVV19NZrFYHpMpzjQTzMzMwNPTE+Pp6Zng6emZ4Ofnl5CesfV8bW1tznQe3/wDwvFeXl7Rvr6+Ca+88kpaUFCQh74GXzqdDrq7u6GpqankRQmdR1hYTNrhUFVVlcXj8d6ysrKy0OfstLS0hPj4eC6Xy+U2NzeDRCI5/sa2XeX37t1rGhwc7BoYGJBN1P+FFbiE5OzszGaxWImvvvrqpoCAAKfp+ERmCpPJBCaTmcnhcDJLS0u/TE59+YxUKhXoi/lg+oVgrKysGJaWlna2trYeaWlpXDabvTMgIGDSfp2KiorzbW1tL0zoPMLCYtJX6uVfs2u++PKowMPDgz+ZIslEIhECAgKAxWJlajSazJ6eHmhra4PW1tZvtmz9o6Czs7O+r6+vfWxsbFir1WosLCzsV6xYkcnj8d7z9vaelmPV0Hh5eYGnp+f2mJiY7UVFRZ/HL0v5tru7+5ZGo1FisVg8Docj4fF4CxsbG1c+nx/m7e39sYeHB7i4uIC5ufmU6r4ODQ1BZWXlifkSrYuA8DRTumIrKytPent78728vCb9HRQKBVgsFhwcHIBOpwObzd4yNja2RaVSwdDQEHR1dcHo6CjQaDRwdXWdsWPV0KBQKPDw8AA7O7udERERO2tra2FgYACoVCo4OTkBjUYDMpkMeDz+8WuqaLVaaGxsbL19+/YzSX8ICPOFqYrJidDQ0AwvLy/e80c/CwaDARKJBI86BdJoNHB3dwe1Wj0nViL6IJPJwGQywdnZGZRKJRAIBDBUx8OBgQEoLS39BtkORpjPTJg1PB61N64pmpqarvb39xvUiLkuJE9CJpPBxsbGYEICANDZ2SlHgtQQ5jtTEhMAgLq6ulyJRFJvDGNeREZGRkAikRSUFuci2cEI85opi0l+7hmBWCzOeV6dToTJcfv27cHr168jxbgR5j1TFhMAgObm5hKZDNl0MAQtLS3Xzpw6hkS8Isx7piUmUqlUIBAIJuyjgzA5Ojs7QSKRINGuCAuCaYmJsKKw68qVK59KJJIu5HFneiiVSigqKjouEolOmtoWBARDMC0xAQC4+MvPJadOnXq3ra1N8yL0dDEkOp0OSktLy/Pz8w8+3d4CAWG+Mm0xAQA4fuy/jl+8ePGju3fvGsqeBY9Wq4XKysrWU6dOvX31yi8mKyyFgGBoZiQmAAD/79D+fadPn96PCMrz0el0UFVV1frtt9+mj9fiAgFhPjNjMQEAyMvLO3Ds2LE/tLS0INmuerh27Vr9999//xoiJAgLEYOEntbVVigB4PNNm3cMpqSkfMRms50McdyFgkqlgqKiovJTp069nZ97BhEShAWJQePYj373xdF1GzbLFQrFx6Ghob766ne8KNy7dw+KiopO5ubmfmTK4tsICMbG4EkxWT99d35l4rre/v7+D0NCQvh0Ot3QU8wL1Go1SKVSTX5+/sH8/PyDSP8bhIWOUTLsLuVklQcFR65pbGzcvnLlyvfc3NwsCASCMaaac+h0OhgaGoLq6uqaCxcu/OV01tGcTw7uM7VZCAhGx2jpug/vxAd58atzoqKitq1cuXKnvb29saabE+h0Oqiurpbm5eUdrK6uPlspuDrvY0hmO4YIhUIBGq1/X2CmNqFQKL3/79HomZ/z82xEowyy9zFr80zGDqPn/hdeviBmL47ad+fOnRsRERGbQkNDo62srIw97azT2dkJxcXFx0tKSo7Mdh8hY4LD4TDPH2U4MFjMc6tLmZmZzaj+Aw6H0/t9PB4PGCxmRudNJBL0ngeZTAI0Gj3jv+1szfM88Hic8cUEAKCmqlQOAN/ELU2qkEgkySwWK3HRokVcBoMxG9MbDZ1OB83NzdDU1FRQW1t7XiAQHJ+ovu18pbr6Rg6L5ZtoM0EhcUPT0tJW8tWRb0vQqIkvgKqqmhnVfrl2TfANXo+gjKlUio4OWc1M5sjOzjnQUH8rbqLPu3t6moaGhmfc+3q25tGHUqmECoEIUKbIrVkcEkONiIh4jcvlvu7s7OxLo9GmVe7QVCgUCujq6oKGhoaCioqKo9XV1WeM3YDMVPDik1gpyas+XrVyeaKXl8czjyANjbcgI/MNmkg49Q4ECPOH3NyC4RUr+M8IcHt7B1y9WlKRl3/5kElKnD1sfXEoJCzueEBAQGJYWFgGk8nk2djYAIFAgLm4pTw6Ogqjo6Mgl8vhxo0b50tLS4/U19fnLvS2FIWXfxEDQNLmLW9ueW1TxtchHDaQyWRTm4VgYkZHR6G+vhF+/NfP+y5e+vVjiVgwZpKVydOwF0dZW1lZOTGZTD6bzU4LCAiIptPp8HTDL1MwOjoKLS0tUFdXd1IsFudIpdKKgYGB7tloJTrX4MUnsVJTEj9etzY10dHRAQAAGm81wcsZW5CVyQInL69gNCGBjwcAGBx8ANnncypOnTr3H9nn/reD55wovvrQpyIHAHFUzIocGo3mQaPRfBwdHVlubm7bXF1dgcFgABqNNvruglwuh7t374JMJoOOjo7P79y5I+ru7m7q7e1tXQi7MzOh8PIv4pCw2DdaWtte37Au7aPIyCWAxWABjUbPif9HCMbjURtKiaQBfvr5zH9evlJ0uLQ4r/nJMXNiZTIRrMAlJAcHB18HBweWo6Mjy8rKajeJRAJLS0uwtLQECwsLoFAogMfjAYvFgpmZ2XNXMyqVCoaHh2FoaAiGh4cfvwYGBqCvrw+6u7vfvnfvXlNvb29rT09Pq0QsUM7S6c4rNqS/lrZ5U+YPRBKR9M7u9xwqBUUvtNAudH766XSLE8PR49ixE78/8tVnX403Zk7fUR46NUUAIPIPCMdTKJTdNjY2QKPRgE6nA51OB1tbWyCRSIDD4YBAIAAejwcCgfDYUajVakGlUoFarQadTvfY79HX1wf9/f0gl8tBLpfDvXv3HvXw+dxQPYYXMj+d+P7Mmzv+5OHr6/OJWq1GBHeB09TcUiKuq/coKS3/eqIx/wPkiIXC3w6YjAAAAABJRU5ErkJggg==");

+

+  --keyword: #ff79c6;

+  --identifier: #f8f8f2;

+  --comment: #6272a4;

+  --operator: #ff79c6;

+  --punctuation: #f8f8f2;

+  --other: #f8f8f2;

+  --escapeSequence: #bd93f9;

+  --number: #bd93f9;

+  --literal: #f1fa8c;

+  --program: #9090c0;

+  --option: #90b010;

+  --raw-data: #8be9fd;

+

+  --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E");

+  --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E");

+  --clipboard-image: var(--clipboard-image-normal);

+}

+

+@media (prefers-color-scheme: dark) {

+  [data-theme="auto"] {

+    --primary-background: #171921;

+    --secondary-background: #1e202a;

+    --third-background: #2b2e3b;

+    --info-background: #008000;

+    --warning-background: #807000;

+    --error-background: #c03000;

+    --border: #0e1014;

+    --text: #fff;

+    --anchor: #8be9fd;

+    --anchor-focus: #8be9fd;

+    --input-focus: #8be9fd;

+    --strong: #bd93f9;

+    --hint: #7A7C85;

+    --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARMAAABMCAYAAABOBlMuAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjE4OjIyKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQyNDQtYTExZi0yMjc5YmY4NDNhYTAiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ZWRlYjM1NzAtYjZmYy1kMjQ0LWExMWYtMjI3OWJmODQzYWEwIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6ZWRlYjM1NzAtYjZmYy1kMjQ0LWExMWYtMjI3OWJmODQzYWEwIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQyNDQtYTExZi0yMjc5YmY4NDNhYTAiIHN0RXZ0OndoZW49IjIwMTktMTItMDNUMDE6MTg6MjIrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4wIChXaW5kb3dzKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4JZNR8AAAfG0lEQVR4nO2deViTZ7r/7yxkJaxJ2MK+GCBAMCwS1kgUFQSKK4XWWqsz1jpjp3b0tDP1V+eqU391fqfT/mpPPd20drTFDS0KFEVWJSGAEgLIZpAICBJACIRs549Rj1WILAkBfD/XlevySp68z/0S3+/7vPdzLyidTgcLkU2bd+z39/f/q1gshsrKSoJELFCa2iaEuU9K6kb+8uXxv54/fzE8L/eswNT2zCfQpjbAGKS8lPFKSEjIXiaTCSEhIeDj4xNnapsQ5j6rktZGp6UlfxIdzQVzCplmanvmG1hTG2BIAtlc26CgoDfT0tL2e3l5AQCAjY0NkMnk/a9s2k6rrKw8UV8n1JjYTIQ5RlAw14KzmL3xze1vfJyUuMJaq9UCFovFm9qu+YbBxcSPFUYkk8l2Q0NDsvo6ocrQx5+I8Ih4bz6f/0l8fHyKlZXV4/dRKBQwmcwwMpn8A4FAoPgHhH9bV1sxa488wZxoaycnJ/a9e/duCa5fkc3WvAiTI4Ib77p+XdqHG9anbfLy8gAAgLGxMdBpF+bjvzExqJj4scKI0dHRnwQHB++orq7+AgDeMuTxJ2Jl4rqU9PT0EwEBAUQCgTDuGAaDAampqYepVKpHUHDk325Ulw0a266YuFW+Gzdu/MDPz29jfn7+XgA4aOw5ESZP6kvpCXv3vnM8NiaSamVl+fj9BepGNDoGFRN7e/slcXFxO1xcXMDJyWnH7j//H/fi4uJdgutXmgw5z5O8smn7X9euXbvf29sbMBjMhONQKBRYWVlBbGzsbjMzM3JoOG+/sKKwy1h2rd/4elpGRsYuLy+vaDweD2w2Oy1h5ZrCvEunEaeeiVnMiabyl/F2/+X9P+8JDPQHHA5napMWBAYTk6DgSNuEhIS9DAYDAP7tq1i6dOkqOp3OWbNu0wens44emeoxA9lcWwKBYEMkEm2JRKIdHo+3QKFQWJ1Op8ZgMER3d/dVq1evTnFycpr0MSkUCsTExGzH4/Gk1LTME/39/TI0Go1FoVCg1WrVY2NjipGRkcGRkRH5dPwrEZHLXMPCwjJSUlIy3dzcfB+97+rqGhYSEpIOAIiYmBguN3zL77dt3uPh4W5qUxYUBhMTb2/vjeHh4cvR6P/dILK0tITIyEg7BweHr363/Z3Ampqaf1Zcu/zMKiVsyVJvMplsRyKR7IhEor2FhYUbhUJhJCYm2pFIJB6JRAIymQx4PB7QaDRoNBowMzMDJycnwOOn7icjEokQGxu7icFgbLp///7jFY1WqwWlUgkjIyOgUCgO7Ni5Rz48PCwfHh7uGRkZeaBQKOSjo6ODCoVCXlNVKn/6uCsT13FXrVr1emho6BYKhfLMnP7+/omrU9LPX8g+UThloxEMxqJFXjxESAyPQcSEExrLWLNmzW57e/txP/fw8ABHR8cdDAaDt3xF2ru9vb03sVgs0cbGxs/FxWVZUlISj0aj+dna2oKtrS1M5PcwJCgUCry8vODRrs84vPfoH6OjoyCXy6Gvr+/R6+CWrX9s7evrk/b19bWr1Wqli4sLZ8OGDe95eXmxUSjUuAd0cHDwjoqK2sYKXFIhvnldYYTTQpgU4/8+jyASCYDGoCd+ZkYYF8OICYezl8PhuOkbQyAQIDo62s/NzS2np6cHbGxsgEajAYFAAAwGA1gsFia6CE0NgUAABwcHsLe3B61WC2q1eo9WqwWNRgNKpRLUajUQiUSgUCh6zwGHwwGTydzo5+eXBQBnZu8MEJ5keHhYPqyYWMtHR0ZBpVIhYj9FUDONgOUvT12+du3avMDAQJjssdRqNWCxCyrEZdLodDoQi8Ulx44de628NL/V1Pa8iERE8l2dHB2CJvpcq9Nqbt1qKURWj1Njxld0ZGTkAW9v70kLCQC8sEIC8O/HKx8fn2gmk8kHgCk7pRFmzrWyAikASE1tx0Jj2uH0EZHL/N7YtuvT4OBgzmz4OBYSeDweIiMjt2S++vtMP1YYEmmJsCCY8mNOIJtr6+zsHBcZGXmIw+G4mZubG8m0hU9HRwcUFxe/KxQKTyDRsQjznSmJCS9+dVRERMTfQ0NDo2xtbfUGiSFMjtHRUaitrc3Jzc09kHvxVLmp7UFAmC6oZQkvrZLL5RJhReHtiQb5scKIXC7371FRUX90dnYGIpE4JR8Jgn40Gg20t7fXFxYWfnr9+vWjz8sdYi+Osh4vzgUBwZSgtu94V+fs7Hx7YGCgra6u7khLS0u2RCwYeTQgKmYFh8fj/f/g4OAldnZ2prR1wdPd3Q1CofBQSUnJkdLi3N8E93FCY6k+Pj48FxcXjlar1ZSWlh65VvYr4kREmDNg79+/D3FxcW5OTk5uXl5evNbW1tL0jK3ZXV1d1ykUintycvInoaGhdkj+gvGxs7MDPp+/m0AgWMQvS/lyeHhYTqPRPJycnIJSU1NZ3t7eW2g0Gly/fv2oWq1Gij0hzClQ/gHhpLS0tEM8Hm/7I8Ho7++HlpYWsLa2Bg8PDxOb+OKhUCigqakJ7t+/D25ubuDu7g4oFAp0Oh08ePAAvv7666TTWUdzTG0nAsKTYMU3ryuSU18+4+bmFrZo0SIOAICVlRUsXrx4zkakLnRIJBI8CgJ8MtdJp9NBZ2enqL29XWRC8xAQxgUNAHD+3L8KGhoaCp78ABES04JCoX4jJAAAAwMDUFtbe96YpRMQEKbL41DU5ubmko6Ojj2PSgggzD36+/vrb9y4cX425zzw93/8EBjon2is44+NjSkePBjqGRwc7G5v7xBV19w8U5B/3qgrr9+/uWtXUuKKD/TZ9MXh/066/OuFmunO8dGBQ98HBbGSp/t9U6LRaDXK0dHBoeFhuVzeL22/0yFqamopufjLqRJ933ssJi0tLSXV1dWHGAzGbuObOzs8ubqa71vZKpUKOjo6blwpOF8zm/Mu5cVkLlkSaswprAHAaVihgK7O7oSGxltvfXLon3nXK4RHT2cdN4pfKDCAlZyUuMJan02nTmczAaBmunPw4qI3cbnh0/36XICq0+lgcPABp7OrK629vUP5z8++LLh2XXD05L++yxrvC4/F5EZ12WBS8saLS5Ys2U2lUufUY45SqQSlUgkqlQrUavXj19jYGGg0GtBoNKDT6UCn05VotVq1TqfToFAojFar1eh0Og0Wi8XhcDgeGo1+/PhgZmYGOBwOsFgsmJmZ/eY1F+nt7YXa2trs2Z73wdCQBgCMHp1IJpHA09MdPD3dLRIS+OtKisvWvbP7vf2lZdePVFwzbHTwyMiI3hidkZFRUKvUYzOZ48HQkBIA5nWqBAqFAktLC7C0tADmIh88Pz4uMSyUk7hn776DV4tKPn/6d/lNxp1MJqsRCASf8vn8XdMpOjRTVCoVjI2NgUqlAq1WCyMjI9DX1wf379+Hvr6+/Q8ePOgdGRmRKxSKx0WLFAqFXKlUKnQ6nUar1arHq47mxwrD4/F4Eg6HI2GxWDwej7cgkUjWFAqFam5uTjU3N6eRyeQPLSwswNraGqysrIBAIDwWFywW+zja11Qi29LSclIikeSZZPJZBovBAI8XA8HBQR9kZZ3lR8cmvFZSlGe00p8IkwONRkNERBj4+i7a4+XpHv307/IbMakWlciXJbx0nMPh7Jqo0JGh0el0MDo6Cl1dXSCVSkEmk7177969W319fe1DQ0M9KpVKoVarlWq1WjndNhUPG3ApAWDcOxLTLwSDwWAOotFoDBaLxRMIBAsrKysne3t7Xzqd7k2n0/c4OzsDlUoFHA4364IyMDAATU1NxdWikhcq6tXKyhJezljPJZKI2eERS5cZeoWCMD2srCwhPX0tVzk2djiCG//GtfLLUoBxShB0dHTU3Lx580sLC4vtJBLJKMZoNBqQSqUglUqPdnR01PT09DT19/fLHjx40DM0NNQ72933GiSVGgB4JFQK+LfoSAGgnL04yppEIh2xtLS0t7GxcaFSqR7Ozs4fMRgMcHR0nJX8pJs3b54Ui8UXjT7RHIRMIkFK8irfwcEHPwQELUmqvYHUGJkLmJubw8YNa/i9vfffY/px3myQiDTPiEl9nVDDX576jaenZ7SnpyfLUJNrNBqQyWRw+/bt4x0dHTdkMlltV1dXw/XygjkdEv4wB0YOAK0AUM70C8HQ6fSzdDrdm0qlejg6OrLc3Ny2MBiMadWjfR4PHjyAmzdvZs/1v5MxoVAokJK8iicWS95k+nH+s0EiQhqpzQGoVFtYk5a87ba0XQAA34xbpagg/5zoT7s/OGNnZ8eaaYkBuVwOnZ2d5VKpVNTS0lLS2NhYWFVZ3Dujg5qQh6uY+ocvCAiKIPn4+Jz19PSMdnV15VCpVL6Dg4NBViw6nQ5EItHRpqamqzM+2DzHzo4O69amftLQeKsAZrDLgmBY/PyYsCIhfs+SiKUFE5Y8EwqFx11cXDihoaFTjjFAoVAwPDwMHR0dourq6jNCofDHhZqUVnvjmgIAcgAgJyg40mLRokX8kJCQjT4+PussLS1n1JPl7t27UFxcfHguB6mNjY2B7G4naNRTWyygUCjAYDGAx+PB0sICSCSi3vFYLBbCwjjA8vddBQtATKb7d3saBwc7IJPJBpsHjUGDGRYLJBIJLK0sAfucmyIGg4FFi3y8AwNZtycUk5KiS02vvf7WWQaDkejg4DApQwAeh3xDaWnpPoFAcPxFqnP6sEvgGf+A8Bx3d/cvIyIiNi1evHjT8wpNj8fAwACUlZW9P9dD5+/ckcFbf9gd2dcnn9LNAovF4inmZHtXNxdOdBR3+/JlS33pdP29wolEInA4weuiYxOy5vvuTkeHDHb+8c8xvb33Z3R9/N+Df+uIjYk02DwkEsna2trS1d/fNyGeF7uTyw1/7g3R3t4O2OxA/TVghULhcQqFQk1JSfmYSNR/5wD4d6EfgUBwvLS09IhUKhW9qAV5H9YjKQwJi6uvrKw8ERoamhkSEpKp7w7yJEqlEiQSyZmysrJv53qjdaVSCZdyTk+3qFMrAJRHRPLPN95qeifj5fU7mYt8JhyMRqMhMJDFdnF25gDAvBYTpXIMWlpay2fq/8m5mDcIABYGnEcGAGI/VlhBZWX1yZdSkz55OX0dV5+7w9bGGvz8mPrFpK62QskJjf2GTqd7x8bGbpnID4BCoUAmk0lLSkqOiESik2UleS/MakQflYKrXQDQxY1a3tTe3i6KiIjY5OXlxX7e9+rr6wsuXbr0t4ffn9OgMWjghMZQRcLp+8GulRVI/QPC37Wxtnal0ajJtjY2E451ZjiBra31vE9lR2PQQKFQaAAwo98Yi8Xq9fpPd56HO6rlvKWJv/PwcK+JilyCmajWMw6HAzs7+rMFpQOCIn6zHywSFvXm5eUdFAqFZ9Rq9bgHa2trq79w4cK+zz49cAARkmcpL81v/a/Dhz49d+7c3qqqqjyVSjXuOJ1OBxKJpDw3N/fA5V+zax6978cKw/sHhM/raMrnUVdboSy4fPWQSFSjd5yFBQWIRNKEd2IEw1J4JUd88WL+R51d3XrHWVDMnxUTa2tr1zXrNiUGsrmPf7DS4tymCxcu7Kuurs55+kKQSqVN586d23vs+8NHDXUCC5Wzp3/Iy8rKeruysvLM2Nhvo7VVKhXU1tYWnj17du/T7UOdnZ2D7OzsfGGB09raVi4S1RzXl0eFw+EAj8chYjKLVFffyOrq1C8mJBLpWTFRKBRyDofzC4vFWvXk+1ev/CLOzs7eKxAIslQqFeh0Oujp6enKzs7em/XTd7OayTqfKb56sT4rK+sPAoHg5KO/o0KhAKFQmHXy5MkdF3/5+TeZmctXpIXZ29v7zqVcKWNRX1epuXu3U/y8pEw0GmndOZt0dnXVDw0P6/W5oNHoZ30mQ0NDPb29vfvj4+Pf3rR5B/7od188XnEUXr4gDgmL+0NfX5/U19d3d3l5+YGfTnyDtLmcIhXXLsu4UcvfR6PRGGtra9eysrIjYrE45+kt4Fheou/69es/unnz5vm7d+/Wmsre2WRkZGTQ1DYg/JYGiUiTm1ugBAC9IfHPiEmDpFITE7fqJI/H27lmzZpDq5LWtz55t6wUXO3ihMYerK+vz2tpaUFaM0yT8tL81ujYle+TSCTrvEunBU9/voTLd92wYcPHVCqV39XVdXCu7+oYCp1O90Kc50Jk3I5+xVcv1jc3N5d4enpSMzIyvkpK3sh78nORsKg3++yPBS/q1q+hKCm61DSekERGJ3ikp6d/ERsbm1xVVXWwtbX1hRFtFAqFPMLMUyZsDyoQCI7LZDKIiIjwzczM/GpV0vro2TTsRSUqZoX3+vXrP1u9enXi0NAQiESirIdRtggIc5oJ40zq6uryGhoa8ry8vBJCQ0O9USjU94mrN7yWc+EnvaXb5gJMvxCMp6cnl0Kh2Le1tZVXXLs8L1LXefGrWRkZGZ/x+XyeUqkEkUh0vqenZ14HZyG8OEwoJjdrygd37NxTEBkZmWBtbQ3BwcEeKBTq+/UbX3/355Pfzlmn66qk9dGbN29+k8PhbCSRSNDZ2Snb9ae/HCkpKTksEhbN2QTD5NSX+Vu3bj0cHBzsjcFg4O7du1BWVvbNwxB9BIQ5j94I2Fu3bhXW19cDl8sFLBYLHA7Hg0wmf/e77e84ffXlPz6fLSMnQ2paZkJ4eHjmtm3b+B4eHvZkMhlQKBTY29s72dvbfxgUFJT8x7ffP1NRUfHjXErnZ/qFYKKjo7dt3rz5g8DAQPtH/XHa2tpqGhsbC55/BASEuYFeMblz505NTU3NgfDw8PcwGAygUCjw9fW1IJPJn/1130Hv0tLSI4WXL4hny9inYS+Osvbz80tgMpn8jIwMPovFch2vpoiDgwM4ODhwfH19OYsWLeJv3/Hu+cbGxquzXZz5aZYlvMRJT0/fFhkZue3JZmfd3d0gEolOIr4ShPmEXjFpkFRqXlrzSnFnZ+d7Tk5OjzNfXVxcICMjY6ezszNnVdL6vU8HWhmbgKAIkrOzMyc1NTXz0YU4maAuOp0OK1as4EVFRfGEQqHg1dfePHzr1q2rs71S8WOF4f38/BLS09M/iIyM5DxdxLq5uVlcVVU1bgVwBIS5il4xAQCQyWRigUBwJikpKe3JVGQcDgdLly7l2tranti0ecf7IpEoy9hbxX6sMDydTvdevXr1ltjY2F3u7u6AxT73FJ7B3Nwc4uLiwthsdphQKCzZkL7l0/r6+oKbNeVG90+EhMXZL1++fFtycvKHrq6uz4igUqmE5ubmEiTHCWG+8dwrUXD9imz9xtd/jIuLS7N5KpsTjUZDUFCQE4PB+F4oFGYmJW888Mv5k4UTHGpGxC9LYaenp78VEhKyxdHRESgUyoyOh0KhwNraGuLi4qIDAgKi6+rqyjekb/mHMSN6N6RvSdu+ffseNpsdZm09ftuW+vp6EIvFSB9hhHnHpG7rUqm0orW1tdXS0tLj6TIEaDQaaDQaxMfH811dXTl/3Xfw+JUrVz411J01cfWG6IiIiC07d+5McHNzs7ewMGyOFw6HAwcHB6BSqVx3d/fwz7/4rkAgEBwXCoUnHpZonDGrU9J5MTEx27du3Zrm4uKC0beaqq6u/ry+vj7XEPMiIMwmkxKTimuXZe/u+fCkp6fnexPdUfF4PPj7+1szGIydLi4unF1/+kvenTt3RG1tbRXTqfma8lIG39/fP/HVV19NZrFYHpMpzjQTzMzMwNPTE+Pp6Zng6emZ4Ofnl5CesfV8bW1tznQe3/wDwvFeXl7Rvr6+Ca+88kpaUFCQh74GXzqdDrq7u6GpqankRQmdR1hYTNrhUFVVlcXj8d6ysrKy0OfstLS0hPj4eC6Xy+U2NzeDRCI5/sa2XeX37t1rGhwc7BoYGJBN1P+FFbiE5OzszGaxWImvvvrqpoCAAKfp+ERmCpPJBCaTmcnhcDJLS0u/TE59+YxUKhXoi/lg+oVgrKysGJaWlna2trYeaWlpXDabvTMgIGDSfp2KiorzbW1tL0zoPMLCYtJX6uVfs2u++PKowMPDgz+ZIslEIhECAgKAxWJlajSazJ6eHmhra4PW1tZvtmz9o6Czs7O+r6+vfWxsbFir1WosLCzsV6xYkcnj8d7z9vaelmPV0Hh5eYGnp+f2mJiY7UVFRZ/HL0v5tru7+5ZGo1FisVg8Docj4fF4CxsbG1c+nx/m7e39sYeHB7i4uIC5ufmU6r4ODQ1BZWXlifkSrYuA8DRTumIrKytPent78728vCb9HRQKBVgsFhwcHIBOpwObzd4yNja2RaVSwdDQEHR1dcHo6CjQaDRwdXWdsWPV0KBQKPDw8AA7O7udERERO2tra2FgYACoVCo4OTkBjUYDMpkMeDz+8WuqaLVaaGxsbL19+/YzSX8ICPOFqYrJidDQ0AwvLy/e80c/CwaDARKJBI86BdJoNHB3dwe1Wj0nViL6IJPJwGQywdnZGZRKJRAIBDBUx8OBgQEoLS39BtkORpjPTJg1PB61N64pmpqarvb39xvUiLkuJE9CJpPBxsbGYEICANDZ2SlHgtQQ5jtTEhMAgLq6ulyJRFJvDGNeREZGRkAikRSUFuci2cEI85opi0l+7hmBWCzOeV6dToTJcfv27cHr168jxbgR5j1TFhMAgObm5hKZDNl0MAQtLS3Xzpw6hkS8Isx7piUmUqlUIBAIJuyjgzA5Ojs7QSKRINGuCAuCaYmJsKKw68qVK59KJJIu5HFneiiVSigqKjouEolOmtoWBARDMC0xAQC4+MvPJadOnXq3ra1N8yL0dDEkOp0OSktLy/Pz8w8+3d4CAWG+Mm0xAQA4fuy/jl+8ePGju3fvGsqeBY9Wq4XKysrWU6dOvX31yi8mKyyFgGBoZiQmAAD/79D+fadPn96PCMrz0el0UFVV1frtt9+mj9fiAgFhPjNjMQEAyMvLO3Ds2LE/tLS0INmuerh27Vr9999//xoiJAgLEYOEntbVVigB4PNNm3cMpqSkfMRms50McdyFgkqlgqKiovJTp069nZ97BhEShAWJQePYj373xdF1GzbLFQrFx6Ghob766ne8KNy7dw+KiopO5ubmfmTK4tsICMbG4EkxWT99d35l4rre/v7+D0NCQvh0Ot3QU8wL1Go1SKVSTX5+/sH8/PyDSP8bhIWOUTLsLuVklQcFR65pbGzcvnLlyvfc3NwsCASCMaaac+h0OhgaGoLq6uqaCxcu/OV01tGcTw7uM7VZCAhGx2jpug/vxAd58atzoqKitq1cuXKnvb29saabE+h0Oqiurpbm5eUdrK6uPlspuDrvY0hmO4YIhUIBGq1/X2CmNqFQKL3/79HomZ/z82xEowyy9zFr80zGDqPn/hdeviBmL47ad+fOnRsRERGbQkNDo62srIw97azT2dkJxcXFx0tKSo7Mdh8hY4LD4TDPH2U4MFjMc6tLmZmZzaj+Aw6H0/t9PB4PGCxmRudNJBL0ngeZTAI0Gj3jv+1szfM88Hic8cUEAKCmqlQOAN/ELU2qkEgkySwWK3HRokVcBoMxG9MbDZ1OB83NzdDU1FRQW1t7XiAQHJ+ovu18pbr6Rg6L5ZtoM0EhcUPT0tJW8tWRb0vQqIkvgKqqmhnVfrl2TfANXo+gjKlUio4OWc1M5sjOzjnQUH8rbqLPu3t6moaGhmfc+3q25tGHUqmECoEIUKbIrVkcEkONiIh4jcvlvu7s7OxLo9GmVe7QVCgUCujq6oKGhoaCioqKo9XV1WeM3YDMVPDik1gpyas+XrVyeaKXl8czjyANjbcgI/MNmkg49Q4ECPOH3NyC4RUr+M8IcHt7B1y9WlKRl3/5kElKnD1sfXEoJCzueEBAQGJYWFgGk8nk2djYAIFAgLm4pTw6Ogqjo6Mgl8vhxo0b50tLS4/U19fnLvS2FIWXfxEDQNLmLW9ueW1TxtchHDaQyWRTm4VgYkZHR6G+vhF+/NfP+y5e+vVjiVgwZpKVydOwF0dZW1lZOTGZTD6bzU4LCAiIptPp8HTDL1MwOjoKLS0tUFdXd1IsFudIpdKKgYGB7tloJTrX4MUnsVJTEj9etzY10dHRAQAAGm81wcsZW5CVyQInL69gNCGBjwcAGBx8ANnncypOnTr3H9nn/reD55wovvrQpyIHAHFUzIocGo3mQaPRfBwdHVlubm7bXF1dgcFgABqNNvruglwuh7t374JMJoOOjo7P79y5I+ru7m7q7e1tXQi7MzOh8PIv4pCw2DdaWtte37Au7aPIyCWAxWABjUbPif9HCMbjURtKiaQBfvr5zH9evlJ0uLQ4r/nJMXNiZTIRrMAlJAcHB18HBweWo6Mjy8rKajeJRAJLS0uwtLQECwsLoFAogMfjAYvFgpmZ2XNXMyqVCoaHh2FoaAiGh4cfvwYGBqCvrw+6u7vfvnfvXlNvb29rT09Pq0QsUM7S6c4rNqS/lrZ5U+YPRBKR9M7u9xwqBUUvtNAudH766XSLE8PR49ixE78/8tVnX403Zk7fUR46NUUAIPIPCMdTKJTdNjY2QKPRgE6nA51OB1tbWyCRSIDD4YBAIAAejwcCgfDYUajVakGlUoFarQadTvfY79HX1wf9/f0gl8tBLpfDvXv3HvXw+dxQPYYXMj+d+P7Mmzv+5OHr6/OJWq1GBHeB09TcUiKuq/coKS3/eqIx/wPkiIXC3w6YjAAAAABJRU5ErkJggg==");

+

+    --keyword: #ff79c6;

+    --identifier: #f8f8f2;

+    --comment: #6272a4;

+    --operator: #ff79c6;

+    --punctuation: #f8f8f2;

+    --other: #f8f8f2;

+    --escapeSequence: #bd93f9;

+    --number: #bd93f9;

+    --literal: #f1fa8c;

+    --program: #9090c0;

+    --option: #90b010;

+    --raw-data: #8be9fd;

+

+    --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E");

+    --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E");

+    --clipboard-image: var(--clipboard-image-normal);

+  }

+}

+

+.theme-select-wrapper {

+  display: flex;

+  align-items: center;

+}

+

+html {

+  font-size: 100%;

+  -webkit-text-size-adjust: 100%;

+  -ms-text-size-adjust: 100%; }

+

+body {

+  font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;

+  font-weight: 400;

+  font-size: 1.125em;

+  line-height: 1.5;

+  color: var(--text);

+  background-color: var(--primary-background); }

+

+/* Skeleton grid */

+.container {

+  position: relative;

+  width: 100%;

+  max-width: 1050px;

+  margin: 0 auto;

+  padding: 0;

+  box-sizing: border-box; }

+

+.column, .columns {

+  width: 100%;

+  float: left;

+  box-sizing: border-box;

+  margin-left: 1%; }

+

+@media print {

+  #global-links, .link-seesrc, .theme-switch-wrapper, #searchInputDiv, .search-groupby {

+    display:none;

+  }

+  .columns {

+    width:100% !important;

+  }

+}

+

+.column:first-child, .columns:first-child {

+  margin-left: 0; }

+

+.container .row {

+  display: flex; }

+

+.three.columns {

+  width: 25.0%;

+  height: 100vh;

+  position: sticky;

+  top: 0px;

+  overflow-y: auto;

+  padding: 2px;

+}

+

+.nine.columns {

+  width: 75.0%;

+  padding-left: 1.5em; }

+

+.twelve.columns {

+  width: 100%;

+  margin-left: 0; }

+

+@media screen and (max-width: 860px) {

+  .three.columns {

+    display: none;

+  }

+  .nine.columns {

+    width: 98.0%;

+  }

+  body {

+    font-size: 1em;

+    line-height: 1.35;

+  }

+}

+

+cite {

+  font-style: italic !important; }

+

+

+/* Nim search input */

+div#searchInputDiv {

+  margin-bottom: 1em;

+}

+input#searchInput {

+  width: 80%;

+}

+

+/*

+ * Some custom formatting for input forms.

+ * This also fixes input form colors on Firefox with a dark system theme on Linux.

+ */

+input {

+  -moz-appearance: none;

+  background-color: var(--secondary-background);

+  color: var(--text);

+  border: 1px solid var(--border);

+  font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;

+  font-size: 0.9em;

+  padding: 6px;

+}

+

+input:focus {

+  border: 1px solid var(--input-focus);

+  box-shadow: 0 0 3px var(--input-focus);

+}

+

+select {

+  -moz-appearance: none;

+  background-color: var(--secondary-background);

+  color: var(--text);

+  border: 1px solid var(--border);

+  font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;

+  font-size: 0.9em;

+  padding: 6px;

+}

+

+select:focus {

+  border: 1px solid var(--input-focus);

+  box-shadow: 0 0 3px var(--input-focus);

+}

+

+/* Docgen styles */

+

+:target {

+  border: 2px solid #B5651D;

+  border-style: dotted;

+}

+

+/* Links */

+a {

+  color: var(--anchor);

+  text-decoration: none;

+}

+

+a span.Identifier {

+  text-decoration: underline;

+  text-decoration-color: #aab;

+}

+

+a.reference-toplevel {

+  font-weight: bold;

+}

+

+a.nimdoc {

+  word-spacing: 0.3em;

+}

+

+a.toc-backref {

+  text-decoration: none;

+  color: var(--text);

+}

+

+a.link-seesrc {

+  color: #607c9f;

+  font-size: 0.9em;

+  font-style: italic;

+}

+

+a:hover, a:focus {

+  color: var(--anchor-focus);

+  text-decoration: underline;

+}

+

+a:hover span.Identifier {

+  color: var(--anchor);

+}

+

+

+sub, sup {

+  position: relative;

+  font-size: 75%;

+  line-height: 0;

+  vertical-align: baseline; }

+

+sup {

+  top: -0.5em; }

+

+sub {

+  bottom: -0.25em; }

+

+img {

+  width: auto;

+  height: auto;

+  max-width: 100%;

+  vertical-align: middle;

+  border: 0;

+  -ms-interpolation-mode: bicubic; }

+

+@media print {

+  * {

+    color: black !important;

+    text-shadow: none !important;

+    background: transparent !important;

+    box-shadow: none !important; }

+

+  a, a:visited {

+    text-decoration: underline; }

+

+  a[href]:after {

+    content: " (" attr(href) ")"; }

+

+  abbr[title]:after {

+    content: " (" attr(title) ")"; }

+

+  .ir a:after,

+  a[href^="javascript:"]:after,

+  a[href^="#"]:after {

+    content: ""; }

+

+  pre, blockquote {

+    border: 1px solid #999;

+    page-break-inside: avoid; }

+

+  thead {

+    display: table-header-group; }

+

+  tr, img {

+    page-break-inside: avoid; }

+

+  img {

+    max-width: 100% !important; }

+

+  @page {

+    margin: 0.5cm; }

+

+  h1 {

+    page-break-before: always; }

+

+  h1.title {

+    page-break-before: avoid; }

+

+  p, h2, h3 {

+    orphans: 3;

+    widows: 3; }

+

+  h2, h3 {

+    page-break-after: avoid; }

+}

+

+

+p {

+  margin-top: 0.5em;

+  margin-bottom: 0.5em; }

+

+small {

+  font-size: 85%; }

+

+strong {

+  font-weight: 600;

+  font-size: 0.95em;

+  color: var(--strong); }

+

+em {

+  font-style: italic; }

+

+h1 {

+  font-size: 1.8em;

+  font-weight: 400;

+  padding-bottom: .25em;

+  border-bottom: 6px solid var(--third-background);

+  margin-top: 2.5em;

+  margin-bottom: 1em;

+  line-height: 1.2em; }

+

+h1.title {

+  padding-bottom: 1em;

+  border-bottom: 0px;

+  font-size: 2.5em;

+  text-align: center;

+  font-weight: 900;

+  margin-top: 0.75em;

+  margin-bottom: 0em; }

+

+h2 {

+  font-size: 1.3em;

+  margin-top: 2em; }

+

+h2.subtitle {

+  margin-top: 0em;

+  text-align: center; }

+

+h3 {

+  font-size: 1.125em;

+  font-style: italic;

+  margin-top: 1.5em; }

+

+h4 {

+  font-size: 1.125em;

+  margin-top: 1em; }

+

+h5 {

+  font-size: 1.125em;

+  margin-top: 0.75em; }

+

+h6 {

+  font-size: 1.1em; }

+

+

+ul, ol {

+  padding: 0;

+  margin-top: 0.5em;

+  margin-left: 0.75em; }

+

+ul ul, ul ol, ol ol, ol ul {

+  margin-bottom: 0;

+  margin-left: 1.25em; }

+

+ul.simple > li {

+  list-style-type: circle; }

+

+ul.simple-boot li {

+  list-style-type: none;

+  margin-left: 0em;

+  margin-bottom: 0.5em; }

+

+ol.simple > li, ul.simple > li {

+  margin-bottom: 0.2em;

+  margin-left: 0.4em }

+

+ul.simple.simple-toc > li {

+  margin-top: 1em; }

+

+ul.simple-toc {

+  list-style: none;

+  font-size: 0.9em;

+  margin-left: -0.3em;

+  margin-top: 1em; }

+

+ul.simple-toc > li {

+  list-style-type: none; }

+

+ul.simple-toc-section {

+  list-style-type: circle;

+  margin-left: 0.8em;

+  color: #6c9aae; }

+

+ul.nested-toc-section {

+  list-style-type: circle;

+  margin-left: -0.75em;

+  color: var(--text); }

+

+ul.nested-toc-section > li {

+  margin-left: 1.25em; }

+

+

+ol.arabic {

+  list-style: decimal; }

+

+ol.loweralpha {

+  list-style: lower-alpha; }

+

+ol.upperalpha {

+  list-style: upper-alpha; }

+

+ol.lowerroman {

+  list-style: lower-roman; }

+

+ol.upperroman {

+  list-style: upper-roman; }

+

+ul.auto-toc {

+  list-style-type: none; }

+

+

+dl {

+  margin-bottom: 1.5em; }

+

+dt {

+  margin-bottom: -0.5em;

+  margin-left: 0.0em; }

+

+dd {

+  margin-left: 2.0em;

+  margin-bottom: 3.0em;

+  margin-top: 0.5em; }

+

+

+hr {

+  margin: 2em 0;

+  border: 0;

+  border-top: 1px solid #aaa; }

+

+hr.footnote {

+  width: 25%;

+  border-top: 0.15em solid #999;

+  margin-bottom: 0.15em;

+  margin-top: 0.15em;

+}

+div.footnote-group {

+  margin-left: 1em;

+}

+div.footnote-label {

+  display: inline-block;

+  min-width: 1.7em;

+}

+

+div.option-list {

+  border: 0.1em solid var(--border);

+}

+div.option-list-item {

+  padding-left: 12em;

+  padding-right: 0;

+  padding-bottom: 0.3em;

+  padding-top: 0.3em;

+}

+div.odd {

+  background-color: var(--secondary-background);

+}

+div.option-list-label {

+  margin-left: -11.5em;

+  margin-right: 0em;

+  min-width: 11.5em;

+  display: inline-block;

+  vertical-align: top;

+}

+div.option-list-description {

+  width: calc(100% - 1em);

+  padding-left: 1em;

+  padding-right: 0;

+  display: inline-block;

+}

+

+blockquote {

+  font-size: 0.9em;

+  font-style: italic;

+  padding-left: 0.5em;

+  margin-left: 0;

+  border-left: 5px solid #bbc;

+}

+

+blockquote.markdown-quote {

+  font-size: 0.9rem;  /* use rem to avoid recursion */

+  font-style: normal;

+}

+

+.pre, span.tok {

+  font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace;

+  font-weight: 500;

+  font-size: 0.85em;

+  color: var(--text);

+  background-color: var(--third-background);

+  padding-left: 3px;

+  padding-right: 3px;

+  border-radius: 4px;

+}

+

+span.tok {

+  border: 1px solid #808080;

+  padding-bottom: 0.1em;

+  margin-right: 0.2em;

+}

+

+.copyToClipBoard {

+  position: relative;

+}

+

+pre {

+  font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace;

+  color: var(--text);

+  font-weight: 500;

+  display: inline-block;

+  box-sizing: border-box;

+  min-width: 100%;

+  padding: 0.5em;

+  margin-top: 0.5em;

+  margin-bottom: 0.5em;

+  font-size: 0.85em;

+  white-space: pre !important;

+  overflow-y: hidden;

+  overflow-x: visible;

+  background-color: var(--secondary-background);

+  border: 1px solid var(--border);

+  -webkit-border-radius: 6px;

+  -moz-border-radius: 6px;

+  border-radius: 6px;

+}

+

+.copyToClipBoardBtn {

+  visibility: hidden;

+  position: absolute;

+  width: 24px;

+  border-radius: 4px;

+  background-image: var(--clipboard-image);

+  right: 5px;

+  top: 13px;

+  background-color: var(--secondary-background);

+  padding: 11px;

+  border: 0;

+}

+

+.copyToClipBoard:hover .copyToClipBoardBtn {

+  visibility: visible;

+}

+

+.pre-scrollable {

+  max-height: 340px;

+  overflow-y: scroll; }

+

+

+/* Nim line-numbered tables */

+.line-nums-table {

+  width: 100%;

+  table-layout: fixed; }

+

+table.line-nums-table {

+  border-radius: 4px;

+  border: 1px solid var(--border);

+  background-color: var(--secondary-background);

+  border-collapse: separate;

+  margin-top: 15px;

+  margin-bottom: 25px; }

+

+.line-nums-table tbody {

+  border: none; }

+

+.line-nums-table td pre {

+  border: none;

+  background-color: transparent; }

+

+.line-nums-table td.blob-line-nums {

+  width: 28px; }

+

+.line-nums-table td.blob-line-nums pre {

+  color: #b0b0b0;

+  -webkit-filter: opacity(75%);

+  filter: opacity(75%);

+  text-align: right;

+  border-color: transparent;

+  background-color: transparent;

+  padding-left: 0px;

+  margin-left: 0px;

+  padding-right: 0px;

+  margin-right: 0px; }

+

+

+table {

+  max-width: 100%;

+  background-color: transparent;

+  margin-top: 0.5em;

+  margin-bottom: 1.5em;

+  border-collapse: collapse;

+  border-color: var(--third-background);

+  border-spacing: 0;

+}

+

+table:not(.line-nums-table) {

+  font-size: 0.9em;

+}

+

+table th, table td {

+  padding: 0px 0.5em 0px;

+  border-color: var(--third-background);

+}

+

+table th {

+  background-color: var(--third-background);

+  border-color: var(--third-background);

+  font-weight: bold; }

+

+table th.docinfo-name {

+  background-color: transparent;

+  text-align: right;

+}

+

+table:not(.line-nums-table) tr:hover {

+  background-color: var(--third-background); }

+

+

+/* rst2html default used to remove borders from tables and images */

+.borderless, table.borderless td, table.borderless th {

+  border: 0; }

+

+table.borderless td, table.borderless th {

+  /* Override padding for "table.docutils td" with "! important".

+     The right padding separates the table cells. */

+  padding: 0 0.5em 0 0 !important; }

+

+.admonition {

+  padding: 0.3em;

+  background-color: var(--secondary-background);

+  border-left: 0.4em solid #7f7f84;

+  margin-bottom: 0.5em;

+  -webkit-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2);

+      -moz-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2);

+          box-shadow: 0 5px 8px -6px rgba(0,0,0,.2);

+}

+.admonition-info {

+  border-color: var(--info-background);

+}

+.admonition-info-text {

+  color: var(--info-background);

+}

+.admonition-warning {

+  border-color: var(--warning-background);

+}

+.admonition-warning-text {

+  color: var(--warning-background);

+}

+.admonition-error {

+  border-color: var(--error-background);

+}

+.admonition-error-text {

+  color: var(--error-background);

+}

+

+.first {

+  /* Override more specific margin styles with "! important". */

+  margin-top: 0 !important; }

+

+.last, .with-subtitle {

+  margin-bottom: 0 !important; }

+

+.hidden {

+  display: none; }

+

+blockquote.epigraph {

+  margin: 2em 5em; }

+

+dl.docutils dd {

+  margin-bottom: 0.5em; }

+

+object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {

+  overflow: hidden; }

+

+

+div.figure {

+  margin-left: 2em;

+  margin-right: 2em; }

+

+div.footer, div.header {

+  clear: both;

+  text-align: center;

+  color: #666;

+  font-size: smaller; }

+

+div.footer {

+  padding-top: 5em; }

+

+div.line-block {

+  display: block;

+  margin-top: 1em;

+  margin-bottom: 1em; }

+

+div.line-block div.line-block {

+  margin-top: 0;

+  margin-bottom: 0;

+  margin-left: 1.5em; }

+

+div.topic {

+  margin: 2em; }

+

+div.search_results {

+  background-color: var(--third-background);

+  margin: 3vh 5vw;

+  padding: 1em;

+  border: 1px solid #4d4d4d;

+  position: fixed;

+  top: 10px;

+  isolation: isolate;

+  max-width: calc(100vw - 6em);

+  z-index: 1;

+  max-height: calc(100vh - 6em);

+  overflow-y: scroll;}

+

+div#global-links ul {

+  margin-left: 0;

+  list-style-type: none; }

+

+div#global-links > simple-boot {

+  margin-left: 3em; }

+

+hr.docutils {

+  width: 75%; }

+

+img.align-left, .figure.align-left, object.align-left {

+  clear: left;

+  float: left;

+  margin-right: 1em; }

+

+img.align-right, .figure.align-right, object.align-right {

+  clear: right;

+  float: right;

+  margin-left: 1em; }

+

+img.align-center, .figure.align-center, object.align-center {

+  display: block;

+  margin-left: auto;

+  margin-right: auto; }

+

+.align-left {

+  text-align: left; }

+

+.align-center {

+  clear: both;

+  text-align: center; }

+

+.align-right {

+  text-align: right; }

+

+/* reset inner alignment in figures */

+div.align-right {

+  text-align: inherit; }

+

+p.attribution {

+  text-align: right;

+  margin-left: 50%; }

+

+p.caption {

+  font-style: italic; }

+

+p.credits {

+  font-style: italic;

+  font-size: smaller; }

+

+p.label {

+  white-space: nowrap; }

+

+p.rubric {

+  font-weight: bold;

+  font-size: larger;

+  color: maroon;

+  text-align: center; }

+

+p.topic-title {

+  font-weight: bold; }

+

+pre.address {

+  margin-bottom: 0;

+  margin-top: 0;

+  font: inherit; }

+

+pre.literal-block, pre.doctest-block, pre.math, pre.code {

+  margin-left: 2em;

+  margin-right: 2em; }

+

+pre.code .ln {

+  color: grey; }

+

+/* line numbers */

+pre.code, code {

+  background-color: #eeeeee; }

+

+pre.code .comment, code .comment {

+  color: #5c6576; }

+

+pre.code .keyword, code .keyword {

+  color: #3B0D06;

+  font-weight: bold; }

+

+pre.code .literal.string, code .literal.string {

+  color: #0c5404; }

+

+pre.code .name.builtin, code .name.builtin {

+  color: #352b84; }

+

+pre.code .deleted, code .deleted {

+  background-color: #DEB0A1; }

+

+pre.code .inserted, code .inserted {

+  background-color: #A3D289; }

+

+span.classifier {

+  font-style: oblique; }

+

+span.classifier-delimiter {

+  font-weight: bold; }

+

+span.problematic {

+  color: #b30000; }

+

+span.section-subtitle {

+  /* font-size relative to parent (h1..h6 element) */

+  font-size: 80%; }

+

+span.DecNumber {

+  color: var(--number); }

+

+span.BinNumber {

+  color: var(--number); }

+

+span.HexNumber {

+  color: var(--number); }

+

+span.OctNumber {

+  color: var(--number); }

+

+span.FloatNumber {

+  color: var(--number); }

+

+span.Identifier {

+  color: var(--identifier); }

+

+span.Keyword {

+  font-weight: 600;

+  color: var(--keyword); }

+

+span.StringLit {

+  color: var(--literal); }

+

+span.LongStringLit {

+  color: var(--literal); }

+

+span.CharLit {

+  color: var(--literal); }

+

+span.EscapeSequence {

+  color: var(--escapeSequence); }

+

+span.Operator {

+  color: var(--operator); }

+

+span.Punctuation {

+  color: var(--punctuation); }

+

+span.Comment, span.LongComment {

+  font-style: italic;

+  font-weight: 400;

+  color: var(--comment); }

+

+span.RegularExpression {

+  color: darkviolet; }

+

+span.TagStart {

+  color: darkviolet; }

+

+span.TagEnd {

+  color: darkviolet; }

+

+span.Key {

+  color: #252dbe; }

+

+span.Value {

+  color: #252dbe; }

+

+span.RawData {

+  color: var(--raw-data); }

+

+span.Assembler {

+  color: #252dbe; }

+

+span.Preprocessor {

+  color: #252dbe; }

+

+span.Directive {

+  color: #252dbe; }

+

+span.option {

+  font-weight: bold;

+  font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace;

+  color: var(--option); }

+

+span.Prompt {

+  font-weight: bold;

+  color: red; }

+

+span.ProgramOutput {

+  font-weight: bold;

+  color: #808080; }

+

+span.program {

+  font-weight: bold;

+  color: var(--program);

+  text-decoration: underline;

+  text-decoration-color: var(--hint);

+  text-decoration-thickness: 0.05em;

+  text-underline-offset: 0.15em; }

+

+span.Command, span.Rule, span.Hyperlink,

+span.Label, span.Reference, span.Other {

+  color: var(--other); }

+

+/* Pop type, const, proc, and iterator defs in nim def blocks */

+dt pre > span.Identifier, dt pre > span.Operator {

+  color: var(--identifier);

+  font-weight: 700; }

+

+dt pre > span.Keyword ~ span.Identifier, dt pre > span.Identifier ~ span.Identifier,

+dt pre > span.Operator ~ span.Identifier, dt pre > span.Other ~ span.Identifier {

+  color: var(--identifier);

+  font-weight: inherit; }

+

+/* Nim sprite for the footer (taken from main page favicon) */

+.nim-sprite {

+  display: inline-block;

+  width: 51px;

+  height: 14px;

+  background-position: 0 0;

+  background-size: 51px 14px;

+  -webkit-filter: opacity(50%);

+  filter: opacity(50%);

+  background-repeat: no-repeat;

+  background-image: var(--nim-sprite-base64);

+  margin-bottom: 5px; }

+

+span.pragmadots {

+  /* Position: relative frees us up to make the dots

+  look really nice without fucking up the layout and

+  causing bulging in the parent container */

+  position: relative;

+  /* 1px down looks slightly nicer */

+  top: 1px;

+  padding: 2px;

+  background-color: var(--third-background);

+  border-radius: 4px;

+  margin: 0 2px;

+  cursor: pointer;

+  font-size: 0.8em; }

+

+span.pragmadots:hover {

+  background-color: var(--hint); }

+

+span.pragmawrap {

+  display: none; }

+

+span.attachedType {

+  display: none;

+  visibility: hidden; }

diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html
new file mode 100644
index 000000000..6decf79a3
--- /dev/null
+++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html
@@ -0,0 +1,614 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--  This file is generated by Nim. -->
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>subdir/subdir_b/utils</title>
+
+<!-- Google fonts -->
+<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
+<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
+
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
+<!-- CSS -->
+<link rel="stylesheet" type="text/css" href="../../nimdoc.out.css">
+
+<!-- JS -->
+<script type="text/javascript" src="../../dochack.js"></script>
+</head>
+<body>
+  <div class="document" id="documentId">
+    <div class="container">
+      <h1 class="title">subdir/subdir_b/utils</h1>
+      <div class="row">
+  <div class="three columns">
+    <div class="theme-select-wrapper">
+      <label for="theme-select">Theme:&nbsp;</label>
+      <select id="theme-select" onchange="setTheme(this.value)">
+        <option value="auto">🌗 Match OS</option>
+        <option value="dark">🌑 Dark</option>
+        <option value="light">🌕 Light</option>
+      </select>
+    </div>
+    <div id="global-links">
+      <ul class="simple">
+        <li><a id="indexLink" href="../../theindex.html">Index</a></li>
+      </ul>
+    </div>
+    <div id="searchInputDiv">
+      Search: <input type="search" id="searchInput" oninput="search()"/>
+    </div>
+    <div>
+      Group by:
+      <select onchange="groupBy(this.value)">
+        <option value="section">Section</option>
+        <option value="type">Type</option>
+      </select>
+    </div>
+    <ul class="simple simple-toc" id="toc-list">
+  <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" id="pandoc-markdown_toc" href="#pandoc-markdown">Pandoc Markdown</a></li>
+<ul class="simple"><li><a class="reference" id="pandoc-markdown-link-name-syntax_toc" href="#pandoc-markdown-link-name-syntax">Link name syntax</a></li>
+<li><a class="reference" id="pandoc-markdown-symbols-documentation_toc" href="#pandoc-markdown-symbols-documentation">Symbols documentation</a></li>
+</ul><li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#7" id="57">Types</a></summary>
+    <ul class="simple simple-toc-section">
+      <li><a class="reference" href="#G" title="G[T] = object">G</a></li>
+<li><a class="reference" href="#SomeType" title="SomeType = enum
+  enumValueA, enumValueB, enumValueC">SomeType</a></li>
+
+    </ul>
+  </details>
+</li>
+<li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#12" id="62">Procs</a></summary>
+    <ul class="simple simple-toc-section">
+      <ul class="simple nested-toc-section">$
+  <li><a class="reference" href="#%24%2CG%5BT%5D" title="`$`[T](a: G[T]): string">`$`[T](a: G[T]): string</a></li>
+<li><a class="reference" href="#%24%2Cref.SomeType" title="`$`[T](a: ref SomeType): string">`$`[T](a: ref SomeType): string</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">'big
+  <li><a class="reference" href="#%27big%2Cstring" title="`'big`(a: string): SomeType">`'big`(a: string): SomeType</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">[]
+  <li><a class="reference" href="#%5B%5D%2CG%5BT%5D" title="`[]`[T](x: G[T]): T">`[]`[T](x: G[T]): T</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">[]=
+  <li><a class="reference" href="#%5B%5D%3D%2CG%5BT%5D%2Cint%2CT" title="`[]=`[T](a: var G[T]; index: int; value: T)">`[]=`[T](a: var G[T]; index: int; value: T)</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">binarySearch
+  <li><a class="reference" href="#binarySearch%2CopenArray%5BT%5D%2CK%2Cproc%28T%2CK%29" title="binarySearch[T, K](a: openArray[T]; key: K;
+                   cmp: proc (x: T; y: K): int {.closure.}): int">binarySearch[T, K](a: openArray[T]; key: K;
+                   cmp: proc (x: T; y: K): int {.closure.}): int</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">f
+  <li><a class="reference" href="#f%2CG%5Bint%5D" title="f(x: G[int])">f(x: G[int])</a></li>
+<li><a class="reference" href="#f%2CG%5Bstring%5D" title="f(x: G[string])">f(x: G[string])</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">fn
+  <li><a class="reference" href="#fn" title="fn[T; U, V: SomeFloat]()">fn[T; U, V: SomeFloat]()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">fn2
+  <li><a class="reference" href="#fn2" title="fn2()">fn2()</a></li>
+<li><a class="reference" href="#fn2%2Cint" title="fn2(x: int)">fn2(x: int)</a></li>
+<li><a class="reference" href="#fn2%2Cint%2Cfloat" title="fn2(x: int; y: float)">fn2(x: int; y: float)</a></li>
+<li><a class="reference" href="#fn2%2Cint%2Cfloat%2Cfloat" title="fn2(x: int; y: float; z: float)">fn2(x: int; y: float; z: float)</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">fn3
+  <li><a class="reference" href="#fn3" title="fn3(): auto">fn3(): auto</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">fn4
+  <li><a class="reference" href="#fn4" title="fn4(): auto">fn4(): auto</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">fn5
+  <li><a class="reference" href="#fn5" title="fn5()">fn5()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">fn6
+  <li><a class="reference" href="#fn6" title="fn6()">fn6()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">fn7
+  <li><a class="reference" href="#fn7" title="fn7()">fn7()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">fn8
+  <li><a class="reference" href="#fn8" title="fn8(): auto">fn8(): auto</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">fn9
+  <li><a class="reference" href="#fn9%2Cint" title="fn9(a: int): int">fn9(a: int): int</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">fn10
+  <li><a class="reference" href="#fn10%2Cint" title="fn10(a: int): int">fn10(a: int): int</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">fN11
+  <li><a class="reference" href="#fN11" title="fN11()">fN11()</a></li>
+<li><a class="reference" href="#fN11%2Cint" title="fN11(x: int)">fN11(x: int)</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">funWithGenerics
+  <li><a class="reference" href="#funWithGenerics%2CT%2CU" title="funWithGenerics[T, U: SomeFloat](a: T; b: U)">funWithGenerics[T, U: SomeFloat](a: T; b: U)</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">someType
+  <li><a class="reference" href="#someType_2" title="someType(): SomeType">someType(): SomeType</a></li>
+
+</ul>
+
+    </ul>
+  </details>
+</li>
+<li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#15" id="65">Iterators</a></summary>
+    <ul class="simple simple-toc-section">
+      <ul class="simple nested-toc-section">fooBar
+  <li><a class="reference" href="#fooBar.i%2Cseq%5BSomeType%5D" title="fooBar(a: seq[SomeType]): int">fooBar(a: seq[SomeType]): int</a></li>
+
+</ul>
+
+    </ul>
+  </details>
+</li>
+<li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#18" id="68">Templates</a></summary>
+    <ul class="simple simple-toc-section">
+      <ul class="simple nested-toc-section">aEnum
+  <li><a class="reference" href="#aEnum.t" title="aEnum(): untyped">aEnum(): untyped</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">bEnum
+  <li><a class="reference" href="#bEnum.t" title="bEnum(): untyped">bEnum(): untyped</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">fromUtilsGen
+  <li><a class="reference" href="#fromUtilsGen.t" title="fromUtilsGen(): untyped">fromUtilsGen(): untyped</a></li>
+
+</ul>
+
+    </ul>
+  </details>
+</li>
+
+</ul>
+
+  </div>
+  <div class="nine columns" id="content">
+    
+    <div id="tocRoot"></div>
+    
+    <p class="module-desc"><p>This is a description of the utils module.</p>
+<p>Links work:</p>
+<ul class="simple"><li>other module: <a class="reference external" href="iterator.html">iterators</a> (not in this dir, just an example)</li>
+<li>internal: <a class="reference internal nimdoc" title="proc fn2(x: int)" href="#fn2,int">fn2(x)</a></li>
+<li>internal included from another module: <a class="reference internal nimdoc" title="proc funWithGenerics[T, U: SomeFloat](a: T; b: U)" href="#funWithGenerics,T,U">funWithGenerics*[T, U: SomeFloat](a: T, b: U)</a>.</li>
+</ul>
+
+<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>Ref group <a class="reference internal nimdoc" title="proc fn2 (4 overloads)" href="#fn2-procs-all">fn2</a> or specific function like <a class="reference internal nimdoc" title="proc fn2()" href="#fn2">fn2()</a> or <a class="reference internal nimdoc" title="proc fn2(x: int)" href="#fn2,int">fn2( int )</a> or <a class="reference internal nimdoc" title="proc fn2(x: int; y: float)" href="#fn2,int,float">fn2(int, float)</a>.</p>
+<p>Ref generics like this: <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
+                   cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarySearch</a> or <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
+                   cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarySearch(openArray[T], K, proc (T, K))</a> or <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
+                   cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">proc binarySearch(openArray[T], K, proc (T, K))</a> or in different style: <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
+                   cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">proc binarysearch(openarray[T], K, proc(T, K))</a>. Can be combined with export symbols and type parameters: <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
+                   cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarysearch*[T, K](openArray[T], K, proc (T, K))</a>. With spaces <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
+                   cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binary search</a>.</p>
+<p>Note that <tt class="docutils literal"><span class="pre"><span class="Keyword">proc</span></span></tt> can be used in postfix form: <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
+                   cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarySearch proc</a>.</p>
+<p>Ref. type like <a class="reference internal nimdoc" title="object G" href="#G">G</a> and <a class="reference internal nimdoc" title="object G" href="#G">type G</a> and <a class="reference internal nimdoc" title="object G" href="#G">G[T]</a> and <a class="reference internal nimdoc" title="object G" href="#G">type G*[T]</a>.</p>
+<p>Group ref. with capital letters works: <a class="reference internal nimdoc" title="proc fN11 (2 overloads)" href="#fN11-procs-all">fN11</a> or <a class="reference internal nimdoc" title="proc fN11 (2 overloads)" href="#fN11-procs-all">fn11</a> </p>
+Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">[]</a> is the same as <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">proc `[]`(G[T])</a> because there are no overloads. The full form: <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">proc `[]`*[T](x: G[T]): T</a>Ref. <a class="reference internal nimdoc" title="proc `[]=`[T](a: var G[T]; index: int; value: T)" href="#[]=,G[T],int,T">[]=</a> aka <a class="reference internal nimdoc" title="proc `[]=`[T](a: var G[T]; index: int; value: T)" href="#[]=,G[T],int,T">`[]=`(G[T], int, T)</a>.Ref. <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">$</a> aka <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">proc $</a> or <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">proc `$`</a>.Ref. <a class="reference internal nimdoc" title="proc `$`[T](a: ref SomeType): string" href="#$,ref.SomeType">$(a: ref SomeType)</a>.Ref. <a class="reference internal nimdoc" title="iterator fooBar(a: seq[SomeType]): int" href="#fooBar.i,seq[SomeType]">foo_bar</a> aka <a class="reference internal nimdoc" title="iterator fooBar(a: seq[SomeType]): int" href="#fooBar.i,seq[SomeType]">iterator foo_bar_</a>.Ref. <a class="reference internal nimdoc" title="proc fn[T; U, V: SomeFloat]()" href="#fn">fn[T; U,V: SomeFloat]()</a>.Ref. <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">'big</a> or <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">func `'big`</a> or <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">`'big`(string)</a>.
+<h1><a class="toc-backref" id="pandoc-markdown" href="#pandoc-markdown">Pandoc Markdown</a></h1><p>Now repeat all the auto links of above in Pandoc Markdown Syntax.</p>
+<p>Ref group <a class="reference internal nimdoc" title="proc fn2 (4 overloads)" href="#fn2-procs-all">fn2</a> or specific function like <a class="reference internal nimdoc" title="proc fn2()" href="#fn2">fn2()</a> or <a class="reference internal nimdoc" title="proc fn2(x: int)" href="#fn2,int">fn2(  int  )</a> or <a class="reference internal nimdoc" title="proc fn2(x: int; y: float)" href="#fn2,int,float">fn2(int, float)</a>.</p>
+<p>Ref generics like this: <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
+                   cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarySearch</a> or <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
+                   cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarySearch(openArray[T], K, proc (T, K))</a> or <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
+                   cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">proc binarySearch(openArray[T], K,  proc (T, K))</a> or in different style: <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
+                   cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">proc binarysearch(openarray[T], K, proc(T, K))</a>. Can be combined with export symbols and type parameters: <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
+                   cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarysearch*[T, K](openArray[T], K, proc (T, K))</a>. With spaces <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
+                   cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binary search</a>.</p>
+<p>Note that <tt class="docutils literal"><span class="pre"><span class="Keyword">proc</span></span></tt> can be used in postfix form: <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
+                   cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarySearch proc</a>.</p>
+<p>Ref. type like <a class="reference internal nimdoc" title="object G" href="#G">G</a> and <a class="reference internal nimdoc" title="object G" href="#G">type G</a> and <a class="reference internal nimdoc" title="object G" href="#G">G[T]</a> and <a class="reference internal nimdoc" title="object G" href="#G">type G*[T]</a>.</p>
+<p>Group ref. with capital letters works: <a class="reference internal nimdoc" title="proc fN11 (2 overloads)" href="#fN11-procs-all">fN11</a> or <a class="reference internal nimdoc" title="proc fN11 (2 overloads)" href="#fN11-procs-all">fn11</a></p>
+<p>Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">`[]`</a> is the same as <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">proc `[]`(G[T])</a> because there are no overloads. The full form: <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">proc `[]`*[T](x: G[T]): T</a> Ref. <a class="reference internal nimdoc" title="proc `[]=`[T](a: var G[T]; index: int; value: T)" href="#[]=,G[T],int,T">`[]=`</a> aka <a class="reference internal nimdoc" title="proc `[]=`[T](a: var G[T]; index: int; value: T)" href="#[]=,G[T],int,T">`[]=`(G[T], int, T)</a>. Ref. <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">$</a> aka <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">proc $</a> or <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">proc `$`</a>. Ref. <a class="reference internal nimdoc" title="proc `$`[T](a: ref SomeType): string" href="#$,ref.SomeType">$(a: ref SomeType)</a>. Ref. <a class="reference internal nimdoc" title="iterator fooBar(a: seq[SomeType]): int" href="#fooBar.i,seq[SomeType]">foo_bar</a> aka <a class="reference internal nimdoc" title="iterator fooBar(a: seq[SomeType]): int" href="#fooBar.i,seq[SomeType]">iterator foo_bar_</a>. Ref. <a class="reference internal nimdoc" title="proc fn[T; U, V: SomeFloat]()" href="#fn">fn[T; U,V: SomeFloat]()</a>. Ref. <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">'big</a> or <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">func `'big`</a> or <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">`'big`(string)</a>.</p>
+
+<h2><a class="toc-backref" id="pandoc-markdown-link-name-syntax" href="#pandoc-markdown-link-name-syntax">Link name syntax</a></h2><p>Pandoc Markdown has synax for changing text of links: Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">this proc</a> or <a class="reference internal nimdoc" title="object G" href="#G">another symbol</a>.</p>
+
+<h2><a class="toc-backref" id="pandoc-markdown-symbols-documentation" href="#pandoc-markdown-symbols-documentation">Symbols documentation</a></h2><p>Let us repeat auto links from symbols section below:</p>
+<p>There is also variant <a class="reference internal nimdoc" title="proc f(x: G[string])" href="#f,G[string]">f(G[string])</a>. See also <a class="reference internal nimdoc" title="proc f(x: G[int])" href="#f,G[int]">f(G[int])</a>.</p>
+</p>
+    <div class="section" id="7">
+  <h1><a class="toc-backref" href="#7">Types</a></h1>
+  <dl class="item">
+    <div id="G">
+  <dt><pre><a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span> <span class="Other">=</span> <span class="Keyword">object</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+<div id="SomeType">
+  <dt><pre><a href="utils.html#SomeType"><span class="Identifier">SomeType</span></a> <span class="Other">=</span> <span class="Keyword">enum</span>
+  <span class="Identifier">enumValueA</span><span class="Other">,</span> <span class="Identifier">enumValueB</span><span class="Other">,</span> <span class="Identifier">enumValueC</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+  </dl>
+</div>
+<div class="section" id="12">
+  <h1><a class="toc-backref" href="#12">Procs</a></h1>
+  <dl class="item">
+    <div id="$-procs-all">
+  <div id="$,G[T]">
+  <dt><pre><span class="Keyword">proc</span> <a href="#%24%2CG%5BT%5D"><span class="Identifier">`$`</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+<div id="$,ref.SomeType">
+  <dt><pre><span class="Keyword">proc</span> <a href="#%24%2Cref.SomeType"><span class="Identifier">`$`</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Keyword">ref</span> <a href="utils.html#SomeType"><span class="Identifier">SomeType</span></a><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+<div id="'big-procs-all">
+  <div id="'big,string">
+  <dt><pre><span class="Keyword">func</span> <a href="#%27big%2Cstring"><span class="Identifier">`'big`</span></a><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <a href="utils.html#SomeType"><span class="Identifier">SomeType</span></a> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+<div id="[]-procs-all">
+  <div id="[],G[T]">
+  <dt><pre><span class="Keyword">proc</span> <a href="#%5B%5D%2CG%5BT%5D"><span class="Identifier">`[]`</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">T</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+<div id="[]=-procs-all">
+  <div id="[]=,G[T],int,T">
+  <dt><pre><span class="Keyword">proc</span> <a href="#%5B%5D%3D%2CG%5BT%5D%2Cint%2CT"><span class="Identifier">`[]=`</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Keyword">var</span> <a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">;</span> <span class="Identifier">index</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">;</span> <span class="Identifier">value</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+<div id="binarySearch-procs-all">
+  <div id="binarySearch,openArray[T],K,proc(T,K)">
+  <dt><pre><span class="Keyword">proc</span> <a href="#binarySearch%2CopenArray%5BT%5D%2CK%2Cproc%28T%2CK%29"><span class="Identifier">binarySearch</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">,</span> <span class="Identifier">K</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Identifier">openArray</span><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">;</span> <span class="Identifier">key</span><span class="Other">:</span> <span class="Identifier">K</span><span class="Other">;</span>
+                        <span class="Identifier">cmp</span><span class="Other">:</span> <span class="Keyword">proc</span> <span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">;</span> <span class="Identifier">y</span><span class="Other">:</span> <span class="Identifier">K</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span class="Identifier">closure</span>.}<span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+<div id="f-procs-all">
+  <div id="f,G[int]">
+  <dt><pre><span class="Keyword">proc</span> <a href="#f%2CG%5Bint%5D"><span class="Identifier">f</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">int</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    There is also variant <a class="reference internal nimdoc" title="proc f(x: G[string])" href="#f,G[string]">f(G[string])</a>
+    
+  </dd>
+</div>
+<div id="f,G[string]">
+  <dt><pre><span class="Keyword">proc</span> <a href="#f%2CG%5Bstring%5D"><span class="Identifier">f</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    See also <a class="reference internal nimdoc" title="proc f(x: G[int])" href="#f,G[int]">f(G[int])</a>.
+    
+  </dd>
+</div>
+
+</div>
+<div id="fn-procs-all">
+  <div id="fn">
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn"><span class="Identifier">fn</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">;</span> <span class="Identifier">U</span><span class="Other">,</span> <span class="Identifier">V</span><span class="Other">:</span> <span class="Identifier">SomeFloat</span><span class="Other">]</span><span class="Other">(</span><span class="Other">)</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+<div id="fn2-procs-all">
+  <div id="fn2">
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn2"><span class="Identifier">fn2</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    comment
+    
+  </dd>
+</div>
+<div id="fn2,int">
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn2%2Cint"><span class="Identifier">fn2</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    fn2 comment
+    
+  </dd>
+</div>
+<div id="fn2,int,float">
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn2%2Cint%2Cfloat"><span class="Identifier">fn2</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">;</span> <span class="Identifier">y</span><span class="Other">:</span> <span class="Identifier">float</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+<div id="fn2,int,float,float">
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn2%2Cint%2Cfloat%2Cfloat"><span class="Identifier">fn2</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">;</span> <span class="Identifier">y</span><span class="Other">:</span> <span class="Identifier">float</span><span class="Other">;</span> <span class="Identifier">z</span><span class="Other">:</span> <span class="Identifier">float</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+<div id="fn3-procs-all">
+  <div id="fn3">
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn3"><span class="Identifier">fn3</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">auto</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    comment
+    
+  </dd>
+</div>
+
+</div>
+<div id="fn4-procs-all">
+  <div id="fn4">
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn4"><span class="Identifier">fn4</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">auto</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    comment
+    
+  </dd>
+</div>
+
+</div>
+<div id="fn5-procs-all">
+  <div id="fn5">
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn5"><span class="Identifier">fn5</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    comment
+    
+  </dd>
+</div>
+
+</div>
+<div id="fn6-procs-all">
+  <div id="fn6">
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn6"><span class="Identifier">fn6</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    comment
+    
+  </dd>
+</div>
+
+</div>
+<div id="fn7-procs-all">
+  <div id="fn7">
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn7"><span class="Identifier">fn7</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    comment
+    
+  </dd>
+</div>
+
+</div>
+<div id="fn8-procs-all">
+  <div id="fn8">
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn8"><span class="Identifier">fn8</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">auto</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    comment
+    
+  </dd>
+</div>
+
+</div>
+<div id="fn9-procs-all">
+  <div id="fn9,int">
+  <dt><pre><span class="Keyword">func</span> <a href="#fn9%2Cint"><span class="Identifier">fn9</span></a><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    comment
+    
+  </dd>
+</div>
+
+</div>
+<div id="fn10-procs-all">
+  <div id="fn10,int">
+  <dt><pre><span class="Keyword">func</span> <a href="#fn10%2Cint"><span class="Identifier">fn10</span></a><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    comment
+    
+  </dd>
+</div>
+
+</div>
+<div id="fN11-procs-all">
+  <div id="fN11">
+  <dt><pre><span class="Keyword">func</span> <a href="#fN11"><span class="Identifier">fN11</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+<div id="fN11,int">
+  <dt><pre><span class="Keyword">func</span> <a href="#fN11%2Cint"><span class="Identifier">fN11</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+<div id="funWithGenerics-procs-all">
+  <div id="funWithGenerics,T,U">
+  <dt><pre><span class="Keyword">proc</span> <a href="#funWithGenerics%2CT%2CU"><span class="Identifier">funWithGenerics</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">,</span> <span class="Identifier">U</span><span class="Other">:</span> <span class="Identifier">SomeFloat</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">;</span> <span class="Identifier">b</span><span class="Other">:</span> <span class="Identifier">U</span><span class="Other">)</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+<div id="someType-procs-all">
+  <div id="someType_2">
+  <dt><pre><span class="Keyword">proc</span> <a href="#someType_2"><span class="Identifier">someType</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <a href="utils.html#SomeType"><span class="Identifier">SomeType</span></a> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    constructor.
+    
+  </dd>
+</div>
+
+</div>
+
+  </dl>
+</div>
+<div class="section" id="15">
+  <h1><a class="toc-backref" href="#15">Iterators</a></h1>
+  <dl class="item">
+    <div id="fooBar-iterators-all">
+  <div id="fooBar.i,seq[SomeType]">
+  <dt><pre><span class="Keyword">iterator</span> <a href="#fooBar.i%2Cseq%5BSomeType%5D"><span class="Identifier">fooBar</span></a><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><a href="utils.html#SomeType"><span class="Identifier">SomeType</span></a><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+
+  </dl>
+</div>
+<div class="section" id="18">
+  <h1><a class="toc-backref" href="#18">Templates</a></h1>
+  <dl class="item">
+    <div id="aEnum-templates-all">
+  <div id="aEnum.t">
+  <dt><pre><span class="Keyword">template</span> <a href="#aEnum.t"><span class="Identifier">aEnum</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+<div id="bEnum-templates-all">
+  <div id="bEnum.t">
+  <dt><pre><span class="Keyword">template</span> <a href="#bEnum.t"><span class="Identifier">bEnum</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+<div id="fromUtilsGen-templates-all">
+  <div id="fromUtilsGen.t">
+  <dt><pre><span class="Keyword">template</span> <a href="#fromUtilsGen.t"><span class="Identifier">fromUtilsGen</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
+  <dd>
+    
+    should be shown in utils.html only
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span> <span class="StringLit">&quot;should be in utils.html only, not in module that calls fromUtilsGen&quot;</span></pre>ditto
+    
+  </dd>
+</div>
+
+</div>
+
+  </dl>
+</div>
+
+  </div>
+</div>
+
+      <div class="twelve-columns footer">
+        <span class="nim-sprite"></span>
+        <br>
+        <small style="color: var(--hint);">Made with Nim. Generated: 1970-01-02 03:46:40 UTC</small>
+      </div>
+    </div>
+  </div>
+  
+</body>
+</html>
diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.idx b/nimdoc/testproject/expected/subdir/subdir_b/utils.idx
new file mode 100644
index 000000000..81b27bcb9
--- /dev/null
+++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.idx
@@ -0,0 +1,47 @@
+nimTitle	utils	subdir/subdir_b/utils.html	module subdir/subdir_b/utils		0
+nim	funWithGenerics	subdir/subdir_b/utils.html#funWithGenerics,T,U	proc funWithGenerics[T, U: SomeFloat](a: T; b: U)		1
+nim	fn2	subdir/subdir_b/utils.html#fn2,int,float,float	proc fn2(x: int; y: float; z: float)		5
+nim	enumValueA	subdir/subdir_b/utils.html#enumValueA	SomeType.enumValueA		45
+nim	enumValueB	subdir/subdir_b/utils.html#enumValueB	SomeType.enumValueB		45
+nim	enumValueC	subdir/subdir_b/utils.html#enumValueC	SomeType.enumValueC		45
+nim	SomeType	subdir/subdir_b/utils.html#SomeType	enum SomeType		45
+nim	G	subdir/subdir_b/utils.html#G	object G		49
+nim	someType	subdir/subdir_b/utils.html#someType_2	proc someType(): SomeType		52
+nim	fn2	subdir/subdir_b/utils.html#fn2	proc fn2()		57
+nim	fn2	subdir/subdir_b/utils.html#fn2,int	proc fn2(x: int)		58
+nim	fn2	subdir/subdir_b/utils.html#fn2,int,float	proc fn2(x: int; y: float)		61
+nim	binarySearch	subdir/subdir_b/utils.html#binarySearch,openArray[T],K,proc(T,K)	proc binarySearch[T, K](a: openArray[T]; key: K;\n                   cmp: proc (x: T; y: K): int {.closure.}): int		63
+nim	fn3	subdir/subdir_b/utils.html#fn3	proc fn3(): auto		66
+nim	fn4	subdir/subdir_b/utils.html#fn4	proc fn4(): auto		67
+nim	fn5	subdir/subdir_b/utils.html#fn5	proc fn5()		68
+nim	fn6	subdir/subdir_b/utils.html#fn6	proc fn6()		70
+nim	fn7	subdir/subdir_b/utils.html#fn7	proc fn7()		72
+nim	fn8	subdir/subdir_b/utils.html#fn8	proc fn8(): auto		75
+nim	fn9	subdir/subdir_b/utils.html#fn9,int	proc fn9(a: int): int		78
+nim	fn10	subdir/subdir_b/utils.html#fn10,int	proc fn10(a: int): int		79
+nim	fN11	subdir/subdir_b/utils.html#fN11	proc fN11()		85
+nim	fN11	subdir/subdir_b/utils.html#fN11,int	proc fN11(x: int)		86
+nim	aEnum	subdir/subdir_b/utils.html#aEnum.t	template aEnum(): untyped		90
+nim	bEnum	subdir/subdir_b/utils.html#bEnum.t	template bEnum(): untyped		95
+nim	fromUtilsGen	subdir/subdir_b/utils.html#fromUtilsGen.t	template fromUtilsGen(): untyped		106
+nim	f	subdir/subdir_b/utils.html#f,G[int]	proc f(x: G[int])		130
+nim	f	subdir/subdir_b/utils.html#f,G[string]	proc f(x: G[string])		133
+nim	`[]`	subdir/subdir_b/utils.html#[],G[T]	proc `[]`[T](x: G[T]): T		140
+nim	`[]=`	subdir/subdir_b/utils.html#[]=,G[T],int,T	proc `[]=`[T](a: var G[T]; index: int; value: T)		144
+nim	`$`	subdir/subdir_b/utils.html#$,G[T]	proc `$`[T](a: G[T]): string		148
+nim	`$`	subdir/subdir_b/utils.html#$,ref.SomeType	proc `$`[T](a: ref SomeType): string		152
+nim	fooBar	subdir/subdir_b/utils.html#fooBar.i,seq[SomeType]	iterator fooBar(a: seq[SomeType]): int		156
+nim	fn	subdir/subdir_b/utils.html#fn	proc fn[T; U, V: SomeFloat]()		160
+nim	`'big`	subdir/subdir_b/utils.html#'big,string	proc `'big`(a: string): SomeType		164
+nimgrp	$	subdir/subdir_b/utils.html#$-procs-all	proc		148
+nimgrp	fn11	subdir/subdir_b/utils.html#fN11-procs-all	proc		85
+nimgrp	fn2	subdir/subdir_b/utils.html#fn2-procs-all	proc		5
+nimgrp	f	subdir/subdir_b/utils.html#f-procs-all	proc		130
+heading	This is now a header	subdir/subdir_b/utils.html#this-is-now-a-header	 This is now a header		0
+heading	Next header	subdir/subdir_b/utils.html#this-is-now-a-header-next-header	  Next header		0
+heading	And so on	subdir/subdir_b/utils.html#next-header-and-so-on	   And so on		0
+heading	More headers	subdir/subdir_b/utils.html#more-headers	 More headers		0
+heading	Up to level 6	subdir/subdir_b/utils.html#more-headers-up-to-level-6	      Up to level 6		0
+heading	Pandoc Markdown	subdir/subdir_b/utils.html#pandoc-markdown	 Pandoc Markdown		0
+heading	Link name syntax	subdir/subdir_b/utils.html#pandoc-markdown-link-name-syntax	  Link name syntax		0
+heading	Symbols documentation	subdir/subdir_b/utils.html#pandoc-markdown-symbols-documentation	  Symbols documentation		0
diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html
new file mode 100644
index 000000000..43a72d99d
--- /dev/null
+++ b/nimdoc/testproject/expected/testproject.html
@@ -0,0 +1,1260 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--  This file is generated by Nim. -->
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>testproject</title>
+
+<!-- Google fonts -->
+<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
+<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
+
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
+<!-- CSS -->
+<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
+
+<!-- JS -->
+<script type="text/javascript" src="dochack.js"></script>
+</head>
+<body>
+  <div class="document" id="documentId">
+    <div class="container">
+      <h1 class="title">testproject</h1>
+      <div class="row">
+  <div class="three columns">
+    <div class="theme-select-wrapper">
+      <label for="theme-select">Theme:&nbsp;</label>
+      <select id="theme-select" onchange="setTheme(this.value)">
+        <option value="auto">🌗 Match OS</option>
+        <option value="dark">🌑 Dark</option>
+        <option value="light">🌕 Light</option>
+      </select>
+    </div>
+    <div id="global-links">
+      <ul class="simple">
+        <li><a id="indexLink" href="theindex.html">Index</a></li>
+      </ul>
+    </div>
+    <div id="searchInputDiv">
+      Search: <input type="search" id="searchInput" oninput="search()"/>
+    </div>
+    <div>
+      Group by:
+      <select onchange="groupBy(this.value)">
+        <option value="section">Section</option>
+        <option value="type">Type</option>
+      </select>
+    </div>
+    <ul class="simple simple-toc" id="toc-list">
+  <li><a class="reference" id="basic-usage_toc" href="#basic-usage">Basic usage</a></li>
+<ul class="simple"><li><a class="reference" id="basic-usage-encoding-data_toc" href="#basic-usage-encoding-data">Encoding data</a></li>
+<li><a class="reference" id="basic-usage-decoding-data_toc" href="#basic-usage-decoding-data">Decoding data</a></li>
+</ul><li>
+  <a class="reference reference-toplevel" href="#6" id="56">Imports</a>
+</li>
+<li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#7" id="57">Types</a></summary>
+    <ul class="simple simple-toc-section">
+      <li><a class="reference" href="#A" title="A {.inject.} = enum
+  aA">A</a></li>
+<li><a class="reference" href="#AnotherObject" title="AnotherObject = object
+  case x*: bool
+  of true:
+    y*: proc (x: string)
+  of false:">AnotherObject</a></li>
+<li><a class="reference" href="#B" title="B {.inject.} = enum
+  bB">B</a></li>
+<li><a class="reference" href="#Foo" title="Foo = enum
+  enumValueA2">Foo</a></li>
+<li><a class="reference" href="#FooBuzz" title="FooBuzz {.deprecated: &quot;FooBuzz msg&quot;.} = int">FooBuzz</a></li>
+<li><a class="reference" href="#MyObject" title="MyObject = object
+  someString*: string        ## This is a string
+  annotated* {.somePragma.}: string ## This is an annotated string">MyObject</a></li>
+<li><a class="reference" href="#Shapes" title="Shapes = enum
+  Circle,                   ## A circle
+  Triangle,                 ## A three-sided shape
+  Rectangle                  ## A four-sided shape">Shapes</a></li>
+<li><a class="reference" href="#T19396" title="T19396 = object
+  a*: int">T19396</a></li>
+
+    </ul>
+  </details>
+</li>
+<li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#8" id="58">Vars</a></summary>
+    <ul class="simple simple-toc-section">
+      <li><a class="reference" href="#aVariable" title="aVariable: array[1, int]">aVariable</a></li>
+<li><a class="reference" href="#someVariable" title="someVariable: bool">someVariable</a></li>
+
+    </ul>
+  </details>
+</li>
+<li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#10" id="60">Consts</a></summary>
+    <ul class="simple simple-toc-section">
+      <li><a class="reference" href="#C_A" title="C_A = 0x7FF0000000000000'f64">C_A</a></li>
+<li><a class="reference" href="#C_B" title="C_B = 0o377'i8">C_B</a></li>
+<li><a class="reference" href="#C_C" title="C_C = 0o277'i8">C_C</a></li>
+<li><a class="reference" href="#C_D" title="C_D = 0o177777'i16">C_D</a></li>
+
+    </ul>
+  </details>
+</li>
+<li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#12" id="62">Procs</a></summary>
+    <ul class="simple simple-toc-section">
+      <ul class="simple nested-toc-section">addfBug14485
+  <li><a class="reference" href="#addfBug14485" title="addfBug14485()">addfBug14485()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">anything
+  <li><a class="reference" href="#anything" title="anything()">anything()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">asyncFun1
+  <li><a class="reference" href="#asyncFun1" title="asyncFun1(): Future[int]">asyncFun1(): Future[int]</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">asyncFun2
+  <li><a class="reference" href="#asyncFun2" title="asyncFun2(): owned(Future[void])">asyncFun2(): owned(Future[void])</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">asyncFun3
+  <li><a class="reference" href="#asyncFun3" title="asyncFun3(): owned(Future[void])">asyncFun3(): owned(Future[void])</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">bar
+  <li><a class="reference" href="#bar" title="bar(f: FooBuzz)">bar(f: FooBuzz)</a></li>
+<li><a class="reference" href="#bar%2CT%2CT" title="bar[T](a, b: T): T">bar[T](a, b: T): T</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">baz
+  <li><a class="reference" href="#baz" title="baz()">baz()</a></li>
+<li><a class="reference" href="#baz%2CT%2CT" title="baz[T](a, b: T): T">baz[T](a, b: T): T</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">buzz
+  <li><a class="reference" href="#buzz%2CT%2CT" title="buzz[T](a, b: T): T">buzz[T](a, b: T): T</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">c_nonexistent
+  <li><a class="reference" href="#c_nonexistent%2Ccstring" title="c_nonexistent(frmt: cstring): cint">c_nonexistent(frmt: cstring): cint</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">c_printf
+  <li><a class="reference" href="#c_printf%2Ccstring" title="c_printf(frmt: cstring): cint">c_printf(frmt: cstring): cint</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">fromUtils3
+  <li><a class="reference" href="#fromUtils3" title="fromUtils3()">fromUtils3()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">isValid
+  <li><a class="reference" href="#isValid%2CT" title="isValid[T](x: T): bool">isValid[T](x: T): bool</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">low
+  <li><a class="reference" href="#low%2CT" title="low[T: Ordinal | enum | range](x: T): T">low[T: Ordinal | enum | range](x: T): T</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">low2
+  <li><a class="reference" href="#low2%2CT" title="low2[T: Ordinal | enum | range](x: T): T">low2[T: Ordinal | enum | range](x: T): T</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">p1
+  <li><a class="reference" href="#p1" title="p1()">p1()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">someFunc
+  <li><a class="reference" href="#someFunc" title="someFunc()">someFunc()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">tripleStrLitTest
+  <li><a class="reference" href="#tripleStrLitTest" title="tripleStrLitTest()">tripleStrLitTest()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">z1
+  <li><a class="reference" href="#z1" title="z1(): Foo">z1(): Foo</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">z2
+  <li><a class="reference" href="#z2" title="z2()">z2()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">z3
+  <li><a class="reference" href="#z3" title="z3()">z3()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">z4
+  <li><a class="reference" href="#z4" title="z4()">z4()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">z5
+  <li><a class="reference" href="#z5" title="z5(): int">z5(): int</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">z6
+  <li><a class="reference" href="#z6" title="z6(): int">z6(): int</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">z7
+  <li><a class="reference" href="#z7" title="z7(): int">z7(): int</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">z8
+  <li><a class="reference" href="#z8" title="z8(): int">z8(): int</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">z9
+  <li><a class="reference" href="#z9" title="z9()">z9()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">z10
+  <li><a class="reference" href="#z10" title="z10()">z10()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">z11
+  <li><a class="reference" href="#z11" title="z11()">z11()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">z12
+  <li><a class="reference" href="#z12" title="z12(): int">z12(): int</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">z13
+  <li><a class="reference" href="#z13" title="z13()">z13()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">z17
+  <li><a class="reference" href="#z17" title="z17()">z17()</a></li>
+
+</ul>
+
+    </ul>
+  </details>
+</li>
+<li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#14" id="64">Methods</a></summary>
+    <ul class="simple simple-toc-section">
+      <ul class="simple nested-toc-section">method1
+  <li><a class="reference" href="#method1.e%2CMoo" title="method1(self: Moo)">method1(self: Moo)</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">method2
+  <li><a class="reference" href="#method2.e%2CMoo" title="method2(self: Moo): int">method2(self: Moo): int</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">method3
+  <li><a class="reference" href="#method3.e%2CMoo" title="method3(self: Moo): int">method3(self: Moo): int</a></li>
+
+</ul>
+
+    </ul>
+  </details>
+</li>
+<li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#15" id="65">Iterators</a></summary>
+    <ul class="simple simple-toc-section">
+      <ul class="simple nested-toc-section">fromUtils1
+  <li><a class="reference" href="#fromUtils1.i" title="fromUtils1(): int">fromUtils1(): int</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">iter1
+  <li><a class="reference" href="#iter1.i%2Cint" title="iter1(n: int): int">iter1(n: int): int</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">iter2
+  <li><a class="reference" href="#iter2.i%2Cint" title="iter2(n: int): int">iter2(n: int): int</a></li>
+
+</ul>
+
+    </ul>
+  </details>
+</li>
+<li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#17" id="67">Macros</a></summary>
+    <ul class="simple simple-toc-section">
+      <ul class="simple nested-toc-section">bar
+  <li><a class="reference" href="#bar.m" title="bar(): untyped">bar(): untyped</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">z16
+  <li><a class="reference" href="#z16.m" title="z16()">z16()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">z18
+  <li><a class="reference" href="#z18.m" title="z18(): int">z18(): int</a></li>
+
+</ul>
+
+    </ul>
+  </details>
+</li>
+<li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#18" id="68">Templates</a></summary>
+    <ul class="simple simple-toc-section">
+      <ul class="simple nested-toc-section">foo
+  <li><a class="reference" href="#foo.t%2CSomeType%2CSomeType" title="foo(a, b: SomeType)">foo(a, b: SomeType)</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">fromUtils2
+  <li><a class="reference" href="#fromUtils2.t" title="fromUtils2()">fromUtils2()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">myfn
+  <li><a class="reference" href="#myfn.t" title="myfn()">myfn()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">somePragma
+  <li><a class="reference" href="#somePragma.t" title="somePragma()">somePragma()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">testNimDocTrailingExample
+  <li><a class="reference" href="#testNimDocTrailingExample.t" title="testNimDocTrailingExample()">testNimDocTrailingExample()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">z6t
+  <li><a class="reference" href="#z6t.t" title="z6t(): int">z6t(): int</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">z14
+  <li><a class="reference" href="#z14.t" title="z14()">z14()</a></li>
+
+</ul>
+<ul class="simple nested-toc-section">z15
+  <li><a class="reference" href="#z15.t" title="z15()">z15()</a></li>
+
+</ul>
+
+    </ul>
+  </details>
+</li>
+
+</ul>
+
+  </div>
+  <div class="nine columns" id="content">
+    
+    <div id="tocRoot"></div>
+    
+    <p class="module-desc">
+<h1><a class="toc-backref" id="basic-usage" href="#basic-usage">Basic usage</a></h1>
+<h2><a class="toc-backref" id="basic-usage-encoding-data" href="#basic-usage-encoding-data">Encoding data</a></h2><p>Apart from strings you can also encode lists of integers or characters:</p>
+
+<h2><a class="toc-backref" id="basic-usage-decoding-data" href="#basic-usage-decoding-data">Decoding data</a></h2>This is the top level module.
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">import</span> <span class="Identifier">testproject</span>
+<span class="Keyword">import</span> <span class="Identifier">subdir</span> <span class="Operator">/</span> <span class="Identifier">subdir_b</span> <span class="Operator">/</span> <span class="Identifier">utils</span>
+<span class="Identifier">doAssert</span> <span class="Identifier">bar</span><span class="Punctuation">(</span><span class="DecNumber">3</span><span class="Punctuation">,</span> <span class="DecNumber">4</span><span class="Punctuation">)</span> <span class="Operator">==</span> <span class="DecNumber">7</span>
+<span class="Identifier">foo</span><span class="Punctuation">(</span><span class="Identifier">enumValueA</span><span class="Punctuation">,</span> <span class="Identifier">enumValueB</span><span class="Punctuation">)</span>
+<span class="Comment"># bug #11078</span>
+<span class="Keyword">for</span> <span class="Identifier">x</span> <span class="Keyword">in</span> <span class="StringLit">&quot;xx&quot;</span><span class="Punctuation">:</span> <span class="Keyword">discard</span></pre>top2
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">import</span> <span class="Identifier">testproject</span>
+<span class="Keyword">discard</span> <span class="StringLit">&quot;in top2&quot;</span></pre>top2 after
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">import</span> <span class="Identifier">testproject</span>
+<span class="Keyword">discard</span> <span class="StringLit">&quot;in top3&quot;</span></pre>top3 after</p>
+    <div class="section" id="6">
+  <h1><a class="toc-backref" href="#6">Imports</a></h1>
+  <dl class="item">
+    <a class="reference external" href="subdir/subdir_b/utils.html">subdir/subdir_b/utils</a>
+  </dl>
+</div>
+<div class="section" id="7">
+  <h1><a class="toc-backref" href="#7">Types</a></h1>
+  <dl class="item">
+    <div id="A">
+  <dt><pre><a href="testproject.html#A"><span class="Identifier">A</span></a> {.<span class="Identifier">inject</span>.} <span class="Other">=</span> <span class="Keyword">enum</span>
+  <span class="Identifier">aA</span></pre></dt>
+  <dd>
+    
+    The enum A.
+    
+  </dd>
+</div>
+<div id="AnotherObject">
+  <dt><pre><a href="testproject.html#AnotherObject"><span class="Identifier">AnotherObject</span></a> <span class="Other">=</span> <span class="Keyword">object</span>
+  <span class="Keyword">case</span> <span class="Identifier">x</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">bool</span>
+  <span class="Keyword">of</span> <span class="Identifier">true</span><span class="Other">:</span>
+    <span class="Identifier">y</span><span class="Operator">*</span><span class="Other">:</span> <span class="Keyword">proc</span> <span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span>
+  <span class="Keyword">of</span> <span class="Identifier">false</span><span class="Other">:</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+<div id="B">
+  <dt><pre><a href="testproject.html#B"><span class="Identifier">B</span></a> {.<span class="Identifier">inject</span>.} <span class="Other">=</span> <span class="Keyword">enum</span>
+  <span class="Identifier">bB</span></pre></dt>
+  <dd>
+    
+    The enum B.
+    
+  </dd>
+</div>
+<div id="Foo">
+  <dt><pre><a href="testproject.html#Foo"><span class="Identifier">Foo</span></a> <span class="Other">=</span> <span class="Keyword">enum</span>
+  <span class="Identifier">enumValueA2</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+<div id="FooBuzz">
+  <dt><pre><a href="testproject.html#FooBuzz"><span class="Identifier">FooBuzz</span></a> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">deprecated</span><span class="Other">:</span> <span class="StringLit">&quot;FooBuzz msg&quot;</span></span>.} <span class="Other">=</span> <span class="Identifier">int</span></pre></dt>
+  <dd>
+    <div class="deprecation-message">
+  <b>Deprecated:</b> FooBuzz msg
+</div>
+
+    
+    
+  </dd>
+</div>
+<div id="MyObject">
+  <dt><pre><a href="testproject.html#MyObject"><span class="Identifier">MyObject</span></a> <span class="Other">=</span> <span class="Keyword">object</span>
+  <span class="Identifier">someString</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">string</span>        <span class="Comment">## This is a string</span>
+  <span class="Identifier">annotated</span><span class="Operator">*</span> {.<span class="Identifier">somePragma</span>.}<span class="Other">:</span> <span class="Identifier">string</span> <span class="Comment">## This is an annotated string</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+<div id="Shapes">
+  <dt><pre><a href="testproject.html#Shapes"><span class="Identifier">Shapes</span></a> <span class="Other">=</span> <span class="Keyword">enum</span>
+  <span class="Identifier">Circle</span><span class="Other">,</span>                   <span class="Comment">## A circle</span>
+  <span class="Identifier">Triangle</span><span class="Other">,</span>                 <span class="Comment">## A three-sided shape</span>
+  <span class="Identifier">Rectangle</span>                  <span class="Comment">## A four-sided shape</span></pre></dt>
+  <dd>
+    
+    Some shapes.
+    
+  </dd>
+</div>
+<div id="T19396">
+  <dt><pre><a href="testproject.html#T19396"><span class="Identifier">T19396</span></a> <span class="Other">=</span> <span class="Keyword">object</span>
+  <span class="Identifier">a</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">int</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+  </dl>
+</div>
+<div class="section" id="8">
+  <h1><a class="toc-backref" href="#8">Vars</a></h1>
+  <dl class="item">
+    <div id="aVariable">
+  <dt><pre><a href="testproject.html#aVariable"><span class="Identifier">aVariable</span></a><span class="Other">:</span> <span class="Identifier">array</span><span class="Other">[</span><span class="DecNumber">1</span><span class="Other">,</span> <span class="Identifier">int</span><span class="Other">]</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+<div id="someVariable">
+  <dt><pre><a href="testproject.html#someVariable"><span class="Identifier">someVariable</span></a><span class="Other">:</span> <span class="Identifier">bool</span></pre></dt>
+  <dd>
+    
+    This should be visible.
+    
+  </dd>
+</div>
+
+  </dl>
+</div>
+<div class="section" id="10">
+  <h1><a class="toc-backref" href="#10">Consts</a></h1>
+  <dl class="item">
+    <div id="C_A">
+  <dt><pre><a href="testproject.html#C_A"><span class="Identifier">C_A</span></a> <span class="Other">=</span> <span class="FloatNumber">0x7FF0000000000000'f64</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+<div id="C_B">
+  <dt><pre><a href="testproject.html#C_B"><span class="Identifier">C_B</span></a> <span class="Other">=</span> <span class="DecNumber">0o377'i8</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+<div id="C_C">
+  <dt><pre><a href="testproject.html#C_C"><span class="Identifier">C_C</span></a> <span class="Other">=</span> <span class="DecNumber">0o277'i8</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+<div id="C_D">
+  <dt><pre><a href="testproject.html#C_D"><span class="Identifier">C_D</span></a> <span class="Other">=</span> <span class="DecNumber">0o177777'i16</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+  </dl>
+</div>
+<div class="section" id="12">
+  <h1><a class="toc-backref" href="#12">Procs</a></h1>
+  <dl class="item">
+    <div id="addfBug14485-procs-all">
+  <div id="addfBug14485">
+  <dt><pre><span class="Keyword">proc</span> <a href="#addfBug14485"><span class="Identifier">addfBug14485</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    Some proc
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span> <span class="StringLit">&quot;foo() = &quot;</span> <span class="Operator">&amp;</span> <span class="Operator">$</span><span class="Punctuation">[</span><span class="DecNumber">1</span><span class="Punctuation">]</span>
+<span class="LongComment">#[
+0: let's also add some broken html to make sure this won't break in future
+1: &lt;/span&gt;
+2: &lt;/span&gt;
+3: &lt;/span
+4: &lt;/script&gt;
+5: &lt;/script
+6: &lt;/script
+7: end of broken html
+]#</span></pre>
+    
+  </dd>
+</div>
+
+</div>
+<div id="anything-procs-all">
+  <div id="anything">
+  <dt><pre><span class="Keyword">proc</span> <a href="#anything"><span class="Identifier">anything</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    There is no block quote after blank lines at the beginning.
+    
+  </dd>
+</div>
+
+</div>
+<div id="asyncFun1-procs-all">
+  <div id="asyncFun1">
+  <dt><pre><span class="Keyword">proc</span> <a href="#asyncFun1"><span class="Identifier">asyncFun1</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">int</span><span class="Other">]</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">stackTrace</span><span class="Other">:</span> <span class="DecNumber">false</span><span class="Other">,</span>
+                                <span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">]</span><span class="Other">,</span>
+                                <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    ok1
+    
+  </dd>
+</div>
+
+</div>
+<div id="asyncFun2-procs-all">
+  <div id="asyncFun2">
+  <dt><pre><span class="Keyword">proc</span> <a href="#asyncFun2"><span class="Identifier">asyncFun2</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">owned</span><span class="Other">(</span><span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">void</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">stackTrace</span><span class="Other">:</span> <span class="DecNumber">false</span><span class="Other">,</span> <span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">]</span><span class="Other">,</span>
+                                        <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+<div id="asyncFun3-procs-all">
+  <div id="asyncFun3">
+  <dt><pre><span class="Keyword">proc</span> <a href="#asyncFun3"><span class="Identifier">asyncFun3</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">owned</span><span class="Other">(</span><span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">void</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">stackTrace</span><span class="Other">:</span> <span class="DecNumber">false</span><span class="Other">,</span> <span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">]</span><span class="Other">,</span>
+                                        <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span></pre>ok1
+    
+  </dd>
+</div>
+
+</div>
+<div id="bar-procs-all">
+  <div id="bar">
+  <dt><pre><span class="Keyword">proc</span> <a href="#bar"><span class="Identifier">bar</span></a><span class="Other">(</span><span class="Identifier">f</span><span class="Other">:</span> <a href="testproject.html#FooBuzz"><span class="Identifier">FooBuzz</span></a><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+<div id="bar,T,T">
+  <dt><pre><span class="Keyword">proc</span> <a href="#bar%2CT%2CT"><span class="Identifier">bar</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">a</span><span class="Other">,</span> <span class="Identifier">b</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">T</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+<div id="baz-procs-all">
+  <div id="baz">
+  <dt><pre><span class="Keyword">proc</span> <a href="#baz"><span class="Identifier">baz</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+<div id="baz,T,T">
+  <dt><pre><span class="Keyword">proc</span> <a href="#baz%2CT%2CT"><span class="Identifier">baz</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">a</span><span class="Other">,</span> <span class="Identifier">b</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">T</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">deprecated</span></span>.}</pre></dt>
+  <dd>
+    <div class="deprecation-message">
+  <b>Deprecated</b> 
+</div>
+
+    This is deprecated without message.
+    
+  </dd>
+</div>
+
+</div>
+<div id="buzz-procs-all">
+  <div id="buzz,T,T">
+  <dt><pre><span class="Keyword">proc</span> <a href="#buzz%2CT%2CT"><span class="Identifier">buzz</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">a</span><span class="Other">,</span> <span class="Identifier">b</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">T</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">deprecated</span><span class="Other">:</span> <span class="StringLit">&quot;since v0.20&quot;</span></span>.}</pre></dt>
+  <dd>
+    <div class="deprecation-message">
+  <b>Deprecated:</b> since v0.20
+</div>
+
+    This is deprecated with a message.
+    
+  </dd>
+</div>
+
+</div>
+<div id="c_nonexistent-procs-all">
+  <div id="c_nonexistent,cstring">
+  <dt><pre><span class="Keyword">proc</span> <a href="#c_nonexistent%2Ccstring"><span class="Identifier">c_nonexistent</span></a><span class="Other">(</span><span class="Identifier">frmt</span><span class="Other">:</span> <span class="Identifier">cstring</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">cint</span> {.<span class="Identifier">importc</span><span class="Other">:</span> <span class="StringLit">&quot;nonexistent&quot;</span><span class="Other">,</span>
+    <span class="Identifier">header</span><span class="Other">:</span> <span class="StringLit">&quot;&lt;stdio.h&gt;&quot;</span><span class="Other">,</span> <span class="Identifier">varargs</span><span class="Other">,</span> <span class="Identifier">discardable</span><span class="Other">,</span> <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+<div id="c_printf-procs-all">
+  <div id="c_printf,cstring">
+  <dt><pre><span class="Keyword">proc</span> <a href="#c_printf%2Ccstring"><span class="Identifier">c_printf</span></a><span class="Other">(</span><span class="Identifier">frmt</span><span class="Other">:</span> <span class="Identifier">cstring</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">cint</span> {.<span class="Identifier">importc</span><span class="Other">:</span> <span class="StringLit">&quot;printf&quot;</span><span class="Other">,</span> <span class="Identifier">header</span><span class="Other">:</span> <span class="StringLit">&quot;&lt;stdio.h&gt;&quot;</span><span class="Other">,</span>
+                                     <span class="Identifier">varargs</span><span class="Other">,</span> <span class="Identifier">discardable</span><span class="Other">,</span> <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span>
+                                     <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    the c printf. etc.
+    
+  </dd>
+</div>
+
+</div>
+<div id="fromUtils3-procs-all">
+  <div id="fromUtils3">
+  <dt><pre><span class="Keyword">proc</span> <a href="#fromUtils3"><span class="Identifier">fromUtils3</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    came form utils but should be shown where <tt class="docutils literal"><span class="pre"><span class="Identifier">fromUtilsGen</span></span></tt> is called
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span> <span class="LongStringLit">&quot;&quot;&quot;should be shown as examples for fromUtils3
+       in module calling fromUtilsGen&quot;&quot;&quot;</span></pre>
+    
+  </dd>
+</div>
+
+</div>
+<div id="isValid-procs-all">
+  <div id="isValid,T">
+  <dt><pre><span class="Keyword">proc</span> <a href="#isValid%2CT"><span class="Identifier">isValid</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">bool</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+<div id="low-procs-all">
+  <div id="low,T">
+  <dt><pre><span class="Keyword">proc</span> <a href="#low%2CT"><span class="Identifier">low</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">:</span> <span class="Identifier">Ordinal</span> <span class="Operator">|</span> <span class="Keyword">enum</span> <span class="Operator">|</span> <span class="Identifier">range</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">T</span> {.<span class="Identifier">magic</span><span class="Other">:</span> <span class="StringLit">&quot;Low&quot;</span><span class="Other">,</span> <span class="Identifier">noSideEffect</span><span class="Other">,</span>
+    <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    <p>Returns the lowest possible value of an ordinal value <tt class="docutils literal"><span class="pre"><span class="Identifier">x</span></span></tt>. As a special semantic rule, <tt class="docutils literal"><span class="pre"><span class="Identifier">x</span></span></tt> may also be a type identifier.</p>
+<p>See also:</p>
+<ul class="simple"><li><a class="reference external" href="#low2,T">low2(T)</a></li>
+</ul>
+<pre class="listing"><span class="Identifier">low</span><span class="Punctuation">(</span><span class="DecNumber">2</span><span class="Punctuation">)</span> <span class="Comment"># =&gt; -9223372036854775808</span></pre>
+    
+  </dd>
+</div>
+
+</div>
+<div id="low2-procs-all">
+  <div id="low2,T">
+  <dt><pre><span class="Keyword">proc</span> <a href="#low2%2CT"><span class="Identifier">low2</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">:</span> <span class="Identifier">Ordinal</span> <span class="Operator">|</span> <span class="Keyword">enum</span> <span class="Operator">|</span> <span class="Identifier">range</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">T</span> {.<span class="Identifier">magic</span><span class="Other">:</span> <span class="StringLit">&quot;Low&quot;</span><span class="Other">,</span> <span class="Identifier">noSideEffect</span><span class="Other">,</span>
+    <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    <p>Returns the lowest possible value of an ordinal value <tt class="docutils literal"><span class="pre"><span class="Identifier">x</span></span></tt>. As a special semantic rule, <tt class="docutils literal"><span class="pre"><span class="Identifier">x</span></span></tt> may also be a type identifier.</p>
+<p>See also:</p>
+<ul class="simple"><li><a class="reference external" href="#low,T">low(T)</a></li>
+</ul>
+<pre class="listing"><span class="Identifier">low2</span><span class="Punctuation">(</span><span class="DecNumber">2</span><span class="Punctuation">)</span> <span class="Comment"># =&gt; -9223372036854775808</span></pre>
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span> <span class="StringLit">&quot;in low2&quot;</span></pre>
+    
+  </dd>
+</div>
+
+</div>
+<div id="p1-procs-all">
+  <div id="p1">
+  <dt><pre><span class="Keyword">proc</span> <a href="#p1"><span class="Identifier">p1</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    cp1
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Identifier">doAssert</span> <span class="DecNumber">1</span> <span class="Operator">==</span> <span class="DecNumber">1</span> <span class="Comment"># regular comments work here</span></pre>c4
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Comment"># c5 regular comments before 1st token work</span>
+<span class="Comment"># regular comment</span>
+<span class="LongComment">#[
+nested regular comment
+]#</span>
+<span class="Identifier">doAssert</span> <span class="DecNumber">2</span> <span class="Operator">==</span> <span class="DecNumber">2</span> <span class="Comment"># c8</span>
+<span class="Comment">## this is a non-nested doc comment</span>
+
+<span class="LongComment">##[
+this is a nested doc comment
+]##</span>
+<span class="Keyword">discard</span> <span class="StringLit">&quot;c9&quot;</span>
+<span class="Comment"># also work after</span></pre>
+    
+  </dd>
+</div>
+
+</div>
+<div id="someFunc-procs-all">
+  <div id="someFunc">
+  <dt><pre><span class="Keyword">func</span> <a href="#someFunc"><span class="Identifier">someFunc</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    My someFunc. Stuff in <tt class="docutils literal"><span class="pre"><span class="Identifier">quotes</span></span></tt> here. <a class="reference external" href="https://nim-lang.org">Some link</a>
+    
+  </dd>
+</div>
+
+</div>
+<div id="tripleStrLitTest-procs-all">
+  <div id="tripleStrLitTest">
+  <dt><pre><span class="Keyword">proc</span> <a href="#tripleStrLitTest"><span class="Identifier">tripleStrLitTest</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+<p><strong class="examples_text">Example: cmd: --hint:XDeclaredButNotUsed:off</strong></p>
+<pre class="listing"><span class="Comment">## mullitline string litterals are tricky as their indentation can span</span>
+<span class="Comment">## below that of the runnableExamples</span>
+<span class="Keyword">let</span> <span class="Identifier">s1a</span> <span class="Operator">=</span> <span class="LongStringLit">&quot;&quot;&quot;
+should appear at indent 0
+  at indent 2
+at indent 0
+&quot;&quot;&quot;</span>
+<span class="Comment"># make sure this works too</span>
+<span class="Keyword">let</span> <span class="Identifier">s1b</span> <span class="Operator">=</span> <span class="LongStringLit">&quot;&quot;&quot;start at same line
+  at indent 2
+at indent 0
+&quot;&quot;&quot;</span> <span class="Comment"># comment after</span>
+<span class="Keyword">let</span> <span class="Identifier">s2</span> <span class="Operator">=</span> <span class="LongStringLit">&quot;&quot;&quot;sandwich &quot;&quot;&quot;</span>
+<span class="Keyword">let</span> <span class="Identifier">s3</span> <span class="Operator">=</span> <span class="LongStringLit">&quot;&quot;&quot;&quot;&quot;&quot;</span>
+<span class="Keyword">when</span> <span class="Identifier">false</span><span class="Punctuation">:</span>
+  <span class="Keyword">let</span> <span class="Identifier">s5</span> <span class="Operator">=</span> <span class="LongStringLit">&quot;&quot;&quot;
+        in s5 &quot;&quot;&quot;</span>
+
+<span class="Keyword">let</span> <span class="Identifier">s3b</span> <span class="Operator">=</span> <span class="Punctuation">[</span><span class="LongStringLit">&quot;&quot;&quot;
+%!? #[...] # inside a multiline ...
+&quot;&quot;&quot;</span><span class="Punctuation">,</span> <span class="StringLit">&quot;foo&quot;</span><span class="Punctuation">]</span>
+
+<span class="Comment">## make sure handles trailing spaces</span>
+<span class="Keyword">let</span> <span class="Identifier">s4</span> <span class="Operator">=</span> <span class="LongStringLit">&quot;&quot;&quot; 
+&quot;&quot;&quot;</span>
+
+<span class="Keyword">let</span> <span class="Identifier">s5</span> <span class="Operator">=</span> <span class="LongStringLit">&quot;&quot;&quot; x
+&quot;&quot;&quot;</span>
+<span class="Keyword">let</span> <span class="Identifier">s6</span> <span class="Operator">=</span> <span class="LongStringLit">&quot;&quot;&quot; &quot;&quot;
+&quot;&quot;&quot;</span>
+<span class="Keyword">let</span> <span class="Identifier">s7</span> <span class="Operator">=</span> <span class="LongStringLit">&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;</span>
+<span class="Keyword">let</span> <span class="Identifier">s8</span> <span class="Operator">=</span> <span class="Punctuation">[</span><span class="LongStringLit">&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;</span><span class="Punctuation">,</span> <span class="LongStringLit">&quot;&quot;&quot;
+  &quot;&quot;&quot;</span> <span class="Punctuation">]</span>
+<span class="Keyword">discard</span>
+<span class="Comment"># should be in</span></pre>
+    
+  </dd>
+</div>
+
+</div>
+<div id="z1-procs-all">
+  <div id="z1">
+  <dt><pre><span class="Keyword">proc</span> <a href="#z1"><span class="Identifier">z1</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <a href="testproject.html#Foo"><span class="Identifier">Foo</span></a> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    cz1
+    
+  </dd>
+</div>
+
+</div>
+<div id="z2-procs-all">
+  <div id="z2">
+  <dt><pre><span class="Keyword">proc</span> <a href="#z2"><span class="Identifier">z2</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    cz2
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span> <span class="StringLit">&quot;in cz2&quot;</span></pre>
+    
+  </dd>
+</div>
+
+</div>
+<div id="z3-procs-all">
+  <div id="z3">
+  <dt><pre><span class="Keyword">proc</span> <a href="#z3"><span class="Identifier">z3</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    cz3
+    
+  </dd>
+</div>
+
+</div>
+<div id="z4-procs-all">
+  <div id="z4">
+  <dt><pre><span class="Keyword">proc</span> <a href="#z4"><span class="Identifier">z4</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    cz4
+    
+  </dd>
+</div>
+
+</div>
+<div id="z5-procs-all">
+  <div id="z5">
+  <dt><pre><span class="Keyword">proc</span> <a href="#z5"><span class="Identifier">z5</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    cz5
+    
+  </dd>
+</div>
+
+</div>
+<div id="z6-procs-all">
+  <div id="z6">
+  <dt><pre><span class="Keyword">proc</span> <a href="#z6"><span class="Identifier">z6</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    cz6
+    
+  </dd>
+</div>
+
+</div>
+<div id="z7-procs-all">
+  <div id="z7">
+  <dt><pre><span class="Keyword">proc</span> <a href="#z7"><span class="Identifier">z7</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    cz7
+    
+  </dd>
+</div>
+
+</div>
+<div id="z8-procs-all">
+  <div id="z8">
+  <dt><pre><span class="Keyword">proc</span> <a href="#z8"><span class="Identifier">z8</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    cz8
+    
+  </dd>
+</div>
+
+</div>
+<div id="z9-procs-all">
+  <div id="z9">
+  <dt><pre><span class="Keyword">proc</span> <a href="#z9"><span class="Identifier">z9</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Identifier">doAssert</span> <span class="DecNumber">1</span> <span class="Operator">+</span> <span class="DecNumber">1</span> <span class="Operator">==</span> <span class="DecNumber">2</span></pre>
+    
+  </dd>
+</div>
+
+</div>
+<div id="z10-procs-all">
+  <div id="z10">
+  <dt><pre><span class="Keyword">proc</span> <a href="#z10"><span class="Identifier">z10</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+<p><strong class="examples_text">Example: cmd: -d:foobar</strong></p>
+<pre class="listing"><span class="Keyword">discard</span> <span class="DecNumber">1</span></pre>cz10
+    
+  </dd>
+</div>
+
+</div>
+<div id="z11-procs-all">
+  <div id="z11">
+  <dt><pre><span class="Keyword">proc</span> <a href="#z11"><span class="Identifier">z11</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span> <span class="DecNumber">1</span></pre>
+    
+  </dd>
+</div>
+
+</div>
+<div id="z12-procs-all">
+  <div id="z12">
+  <dt><pre><span class="Keyword">proc</span> <a href="#z12"><span class="Identifier">z12</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span> <span class="DecNumber">1</span></pre>
+    
+  </dd>
+</div>
+
+</div>
+<div id="z13-procs-all">
+  <div id="z13">
+  <dt><pre><span class="Keyword">proc</span> <a href="#z13"><span class="Identifier">z13</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    cz13
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span></pre>
+    
+  </dd>
+</div>
+
+</div>
+<div id="z17-procs-all">
+  <div id="z17">
+  <dt><pre><span class="Keyword">proc</span> <a href="#z17"><span class="Identifier">z17</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    cz17 rest
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span> <span class="DecNumber">1</span></pre>rest
+    
+  </dd>
+</div>
+
+</div>
+
+  </dl>
+</div>
+<div class="section" id="14">
+  <h1><a class="toc-backref" href="#14">Methods</a></h1>
+  <dl class="item">
+    <div id="method1-methods-all">
+  <div id="method1.e,Moo">
+  <dt><pre><span class="Keyword">method</span> <a href="#method1.e%2CMoo"><span class="Identifier">method1</span></a><span class="Other">(</span><span class="Identifier">self</span><span class="Other">:</span> <span class="Identifier">Moo</span><span class="Other">)</span> {.<span class="Identifier">base</span><span class="Other">,</span> <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    foo1
+    
+  </dd>
+</div>
+
+</div>
+<div id="method2-methods-all">
+  <div id="method2.e,Moo">
+  <dt><pre><span class="Keyword">method</span> <a href="#method2.e%2CMoo"><span class="Identifier">method2</span></a><span class="Other">(</span><span class="Identifier">self</span><span class="Other">:</span> <span class="Identifier">Moo</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span class="Identifier">base</span><span class="Other">,</span> <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    foo2
+    
+  </dd>
+</div>
+
+</div>
+<div id="method3-methods-all">
+  <div id="method3.e,Moo">
+  <dt><pre><span class="Keyword">method</span> <a href="#method3.e%2CMoo"><span class="Identifier">method3</span></a><span class="Other">(</span><span class="Identifier">self</span><span class="Other">:</span> <span class="Identifier">Moo</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span class="Identifier">base</span><span class="Other">,</span> <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    foo3
+    
+  </dd>
+</div>
+
+</div>
+
+  </dl>
+</div>
+<div class="section" id="15">
+  <h1><a class="toc-backref" href="#15">Iterators</a></h1>
+  <dl class="item">
+    <div id="fromUtils1-iterators-all">
+  <div id="fromUtils1.i">
+  <dt><pre><span class="Keyword">iterator</span> <a href="#fromUtils1.i"><span class="Identifier">fromUtils1</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Comment"># ok1</span>
+<span class="Identifier">assert</span> <span class="DecNumber">1</span> <span class="Operator">==</span> <span class="DecNumber">1</span>
+<span class="Comment"># ok2</span></pre>
+    
+  </dd>
+</div>
+
+</div>
+<div id="iter1-iterators-all">
+  <div id="iter1.i,int">
+  <dt><pre><span class="Keyword">iterator</span> <a href="#iter1.i%2Cint"><span class="Identifier">iter1</span></a><span class="Other">(</span><span class="Identifier">n</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    foo1
+    
+  </dd>
+</div>
+
+</div>
+<div id="iter2-iterators-all">
+  <div id="iter2.i,int">
+  <dt><pre><span class="Keyword">iterator</span> <a href="#iter2.i%2Cint"><span class="Identifier">iter2</span></a><span class="Other">(</span><span class="Identifier">n</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><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 class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    foo2
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span> <span class="Comment"># bar</span></pre>
+    
+  </dd>
+</div>
+
+</div>
+
+  </dl>
+</div>
+<div class="section" id="17">
+  <h1><a class="toc-backref" href="#17">Macros</a></h1>
+  <dl class="item">
+    <div id="bar-macros-all">
+  <div id="bar.m">
+  <dt><pre><span class="Keyword">macro</span> <a href="#bar.m"><span class="Identifier">bar</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">untyped</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+<div id="z16-macros-all">
+  <div id="z16.m">
+  <dt><pre><span class="Keyword">macro</span> <a href="#z16.m"><span class="Identifier">z16</span></a><span class="Other">(</span><span class="Other">)</span></pre></dt>
+  <dd>
+    
+    
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span> <span class="DecNumber">1</span></pre>cz16 after
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Identifier">doAssert</span> <span class="DecNumber">2</span> <span class="Operator">==</span> <span class="DecNumber">1</span> <span class="Operator">+</span> <span class="DecNumber">1</span></pre>
+    
+  </dd>
+</div>
+
+</div>
+<div id="z18-macros-all">
+  <div id="z18.m">
+  <dt><pre><span class="Keyword">macro</span> <a href="#z18.m"><span class="Identifier">z18</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span></pre></dt>
+  <dd>
+    
+    cz18
+    
+  </dd>
+</div>
+
+</div>
+
+  </dl>
+</div>
+<div class="section" id="18">
+  <h1><a class="toc-backref" href="#18">Templates</a></h1>
+  <dl class="item">
+    <div id="foo-templates-all">
+  <div id="foo.t,SomeType,SomeType">
+  <dt><pre><span class="Keyword">template</span> <a href="#foo.t%2CSomeType%2CSomeType"><span class="Identifier">foo</span></a><span class="Other">(</span><span class="Identifier">a</span><span class="Other">,</span> <span class="Identifier">b</span><span class="Other">:</span> <a href="subdir/subdir_b/utils.html#SomeType"><span class="Identifier">SomeType</span></a><span class="Other">)</span></pre></dt>
+  <dd>
+    
+    This does nothing 
+    
+  </dd>
+</div>
+
+</div>
+<div id="fromUtils2-templates-all">
+  <div id="fromUtils2.t">
+  <dt><pre><span class="Keyword">template</span> <a href="#fromUtils2.t"><span class="Identifier">fromUtils2</span></a><span class="Other">(</span><span class="Other">)</span></pre></dt>
+  <dd>
+    
+    ok3
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span> <span class="LongStringLit">&quot;&quot;&quot;should be shown as examples for fromUtils2
+       in module calling fromUtilsGen&quot;&quot;&quot;</span></pre>
+    
+  </dd>
+</div>
+
+</div>
+<div id="myfn-templates-all">
+  <div id="myfn.t">
+  <dt><pre><span class="Keyword">template</span> <a href="#myfn.t"><span class="Identifier">myfn</span></a><span class="Other">(</span><span class="Other">)</span></pre></dt>
+  <dd>
+    
+    
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">import</span> <span class="Identifier">std</span><span class="Operator">/</span><span class="Identifier">strutils</span>
+<span class="Comment">## issue #8871 preserve formatting</span>
+<span class="Comment">## line doc comment</span>
+<span class="Comment"># bar</span>
+<span class="Identifier">doAssert</span> <span class="StringLit">&quot;'foo&quot;</span> <span class="Operator">==</span> <span class="StringLit">&quot;'foo&quot;</span>
+<span class="LongComment">##[
+foo
+bar
+]##</span>
+
+<span class="Identifier">doAssert</span><span class="Punctuation">:</span> <span class="Keyword">not</span> <span class="StringLit">&quot;foo&quot;</span><span class="Operator">.</span><span class="Identifier">startsWith</span> <span class="StringLit">&quot;ba&quot;</span>
+<span class="Keyword">block</span><span class="Punctuation">:</span>
+  <span class="Keyword">discard</span> <span class="HexNumber">0xff</span> <span class="Comment"># elu par cette crapule</span>
+<span class="Comment"># should be in</span></pre>should be still in
+    
+  </dd>
+</div>
+
+</div>
+<div id="somePragma-templates-all">
+  <div id="somePragma.t">
+  <dt><pre><span class="Keyword">template</span> <a href="#somePragma.t"><span class="Identifier">somePragma</span></a><span class="Other">(</span><span class="Other">)</span> {.<span class="Identifier">pragma</span>.}</pre></dt>
+  <dd>
+    
+    Just some annotation
+    
+  </dd>
+</div>
+
+</div>
+<div id="testNimDocTrailingExample-templates-all">
+  <div id="testNimDocTrailingExample.t">
+  <dt><pre><span class="Keyword">template</span> <a href="#testNimDocTrailingExample.t"><span class="Identifier">testNimDocTrailingExample</span></a><span class="Other">(</span><span class="Other">)</span></pre></dt>
+  <dd>
+    
+    
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span> <span class="DecNumber">2</span></pre>
+    
+  </dd>
+</div>
+
+</div>
+<div id="z6t-templates-all">
+  <div id="z6t.t">
+  <dt><pre><span class="Keyword">template</span> <a href="#z6t.t"><span class="Identifier">z6t</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span></pre></dt>
+  <dd>
+    
+    cz6t
+    
+  </dd>
+</div>
+
+</div>
+<div id="z14-templates-all">
+  <div id="z14.t">
+  <dt><pre><span class="Keyword">template</span> <a href="#z14.t"><span class="Identifier">z14</span></a><span class="Other">(</span><span class="Other">)</span></pre></dt>
+  <dd>
+    
+    cz14
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span></pre>
+    
+  </dd>
+</div>
+
+</div>
+<div id="z15-templates-all">
+  <div id="z15.t">
+  <dt><pre><span class="Keyword">template</span> <a href="#z15.t"><span class="Identifier">z15</span></a><span class="Other">(</span><span class="Other">)</span></pre></dt>
+  <dd>
+    
+    cz15
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span></pre>
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span> <span class="DecNumber">3</span></pre>
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span> <span class="DecNumber">4</span></pre>ok5 ok5b
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Identifier">assert</span> <span class="Identifier">true</span></pre>
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span> <span class="DecNumber">1</span></pre>in or out?
+    
+  </dd>
+</div>
+
+</div>
+
+  </dl>
+</div>
+
+  </div>
+</div>
+
+      <div class="twelve-columns footer">
+        <span class="nim-sprite"></span>
+        <br>
+        <small style="color: var(--hint);">Made with Nim. Generated: 1970-01-02 03:46:40 UTC</small>
+      </div>
+    </div>
+  </div>
+  
+</body>
+</html>
diff --git a/nimdoc/testproject/expected/testproject.idx b/nimdoc/testproject/expected/testproject.idx
new file mode 100644
index 000000000..81d65a05a
--- /dev/null
+++ b/nimdoc/testproject/expected/testproject.idx
@@ -0,0 +1,74 @@
+nimTitle	testproject	testproject.html	module testproject		0
+nim	someVariable	testproject.html#someVariable	var someVariable		25
+nim	C_A	testproject.html#C_A	const C_A		38
+nim	C_B	testproject.html#C_B	const C_B		39
+nim	C_C	testproject.html#C_C	const C_C		40
+nim	C_D	testproject.html#C_D	const C_D		41
+nim	bar	testproject.html#bar,T,T	proc bar[T](a, b: T): T		43
+nim	baz	testproject.html#baz,T,T	proc baz[T](a, b: T): T		46
+nim	buzz	testproject.html#buzz,T,T	proc buzz[T](a, b: T): T		50
+nim	FooBuzz	testproject.html#FooBuzz	type FooBuzz		55
+nim	bar	testproject.html#bar	proc bar(f: FooBuzz)		59
+nim	aVariable	testproject.html#aVariable	var aVariable		64
+nim	A	testproject.html#A	enum A		92
+nim	B	testproject.html#B	enum B		97
+nim	someFunc	testproject.html#someFunc	proc someFunc()		68
+nim	fromUtils1	testproject.html#fromUtils1.i	iterator fromUtils1(): int		112
+nim	fromUtils2	testproject.html#fromUtils2.t	template fromUtils2()		119
+nim	fromUtils3	testproject.html#fromUtils3	proc fromUtils3()		69
+nim	isValid	testproject.html#isValid,T	proc isValid[T](x: T): bool		71
+nim	enumValueA2	testproject.html#enumValueA2	Foo.enumValueA2		78
+nim	Foo	testproject.html#Foo	enum Foo		78
+nim	z1	testproject.html#z1	proc z1(): Foo		81
+nim	z2	testproject.html#z2	proc z2()		85
+nim	z3	testproject.html#z3	proc z3()		90
+nim	z4	testproject.html#z4	proc z4()		93
+nim	z5	testproject.html#z5	proc z5(): int		99
+nim	z6	testproject.html#z6	proc z6(): int		103
+nim	z6t	testproject.html#z6t.t	template z6t(): int		107
+nim	z7	testproject.html#z7	proc z7(): int		111
+nim	z8	testproject.html#z8	proc z8(): int		115
+nim	z9	testproject.html#z9	proc z9()		123
+nim	z10	testproject.html#z10	proc z10()		126
+nim	z11	testproject.html#z11	proc z11()		131
+nim	z12	testproject.html#z12	proc z12(): int		136
+nim	z13	testproject.html#z13	proc z13()		141
+nim	baz	testproject.html#baz	proc baz()		146
+nim	z17	testproject.html#z17	proc z17()		156
+nim	p1	testproject.html#p1	proc p1()		168
+nim	addfBug14485	testproject.html#addfBug14485	proc addfBug14485()		189
+nim	c_printf	testproject.html#c_printf,cstring	proc c_printf(frmt: cstring): cint		205
+nim	c_nonexistent	testproject.html#c_nonexistent,cstring	proc c_nonexistent(frmt: cstring): cint		209
+nim	low	testproject.html#low,T	proc low[T: Ordinal | enum | range](x: T): T		212
+nim	low2	testproject.html#low2,T	proc low2[T: Ordinal | enum | range](x: T): T		222
+nim	tripleStrLitTest	testproject.html#tripleStrLitTest	proc tripleStrLitTest()		235
+nim	method1	testproject.html#method1.e,Moo	method method1(self: Moo)		276
+nim	method2	testproject.html#method2.e,Moo	method method2(self: Moo): int		278
+nim	method3	testproject.html#method3.e,Moo	method method3(self: Moo): int		281
+nim	iter1	testproject.html#iter1.i,int	iterator iter1(n: int): int		286
+nim	iter2	testproject.html#iter2.i,int	iterator iter2(n: int): int		290
+nim	bar	testproject.html#bar.m	macro bar(): untyped		297
+nim	z16	testproject.html#z16.m	macro z16()		300
+nim	z18	testproject.html#z18.m	macro z18(): int		309
+nim	foo	testproject.html#foo.t,SomeType,SomeType	template foo(a, b: SomeType)		314
+nim	myfn	testproject.html#myfn.t	template myfn()		319
+nim	z14	testproject.html#z14.t	template z14()		340
+nim	z15	testproject.html#z15.t	template z15()		345
+nim	asyncFun1	testproject.html#asyncFun1	proc asyncFun1(): Future[int]		370
+nim	asyncFun2	testproject.html#asyncFun2	proc asyncFun2(): owned(Future[void])		373
+nim	asyncFun3	testproject.html#asyncFun3	proc asyncFun3(): owned(Future[void])		374
+nim	testNimDocTrailingExample	testproject.html#testNimDocTrailingExample.t	template testNimDocTrailingExample()		383
+nim	Circle	testproject.html#Circle	Shapes.Circle		392
+nim	Triangle	testproject.html#Triangle	Shapes.Triangle		392
+nim	Rectangle	testproject.html#Rectangle	Shapes.Rectangle		392
+nim	Shapes	testproject.html#Shapes	enum Shapes		392
+nim	anything	testproject.html#anything	proc anything()		399
+nim	T19396	testproject.html#T19396	object T19396		404
+nim	somePragma	testproject.html#somePragma.t	template somePragma()		408
+nim	MyObject	testproject.html#MyObject	object MyObject		412
+nim	AnotherObject	testproject.html#AnotherObject	object AnotherObject		417
+nimgrp	bar	testproject.html#bar-procs-all	proc		43
+nimgrp	baz	testproject.html#baz-procs-all	proc		46
+heading	Basic usage	testproject.html#basic-usage	 Basic usage		0
+heading	Encoding data	testproject.html#basic-usage-encoding-data	  Encoding data		0
+heading	Decoding data	testproject.html#basic-usage-decoding-data	  Decoding data		0
diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html
new file mode 100644
index 000000000..71a487e0c
--- /dev/null
+++ b/nimdoc/testproject/expected/theindex.html
@@ -0,0 +1,431 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--  This file is generated by Nim. -->
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Index</title>
+
+<!-- Google fonts -->
+<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
+<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
+
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
+<!-- CSS -->
+<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
+
+<!-- JS -->
+<script type="text/javascript" src="dochack.js"></script>
+</head>
+<body>
+  <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>
+<dl><dt><a name="%60%24%60" href="#%60%24%60"><span>`$`:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: proc `$`[T](a: G[T]): string" href="subdir/subdir_b/utils.html#%24%2CG%5BT%5D">utils: proc `$`[T](a: G[T]): string</a></li>
+          <li><a class="reference external"
+          data-doc-search-tag="utils: proc `$`[T](a: ref SomeType): string" href="subdir/subdir_b/utils.html#%24%2Cref.SomeType">utils: proc `$`[T](a: ref SomeType): string</a></li>
+          </ul></dd>
+<dt><a name="%60%27big%60" href="#%60%27big%60"><span>`'big`:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: proc `'big`(a: string): SomeType" href="subdir/subdir_b/utils.html#%27big%2Cstring">utils: proc `'big`(a: string): SomeType</a></li>
+          </ul></dd>
+<dt><a name="%60%5B%5D%3D%60" href="#%60%5B%5D%3D%60"><span>`[]=`:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: proc `[]=`[T](a: var G[T]; index: int; value: T)" href="subdir/subdir_b/utils.html#%5B%5D%3D%2CG%5BT%5D%2Cint%2CT">utils: proc `[]=`[T](a: var G[T]; index: int; value: T)</a></li>
+          </ul></dd>
+<dt><a name="%60%5B%5D%60" href="#%60%5B%5D%60"><span>`[]`:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: proc `[]`[T](x: G[T]): T" href="subdir/subdir_b/utils.html#%5B%5D%2CG%5BT%5D">utils: proc `[]`[T](x: G[T]): T</a></li>
+          </ul></dd>
+<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: enum A" href="testproject.html#A">testproject: enum A</a></li>
+          </ul></dd>
+<dt><a name="addfBug14485" href="#addfBug14485"><span>addfBug14485:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc addfBug14485()" href="testproject.html#addfBug14485">testproject: proc addfBug14485()</a></li>
+          </ul></dd>
+<dt><a name="aEnum" href="#aEnum"><span>aEnum:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: template aEnum(): untyped" href="subdir/subdir_b/utils.html#aEnum.t">utils: template aEnum(): untyped</a></li>
+          </ul></dd>
+<dt><a name="AnotherObject" href="#AnotherObject"><span>AnotherObject:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: object AnotherObject" href="testproject.html#AnotherObject">testproject: object AnotherObject</a></li>
+          </ul></dd>
+<dt><a name="anything" href="#anything"><span>anything:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc anything()" href="testproject.html#anything">testproject: proc anything()</a></li>
+          </ul></dd>
+<dt><a name="asyncFun1" href="#asyncFun1"><span>asyncFun1:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc asyncFun1(): Future[int]" href="testproject.html#asyncFun1">testproject: proc asyncFun1(): Future[int]</a></li>
+          </ul></dd>
+<dt><a name="asyncFun2" href="#asyncFun2"><span>asyncFun2:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc asyncFun2(): owned(Future[void])" href="testproject.html#asyncFun2">testproject: proc asyncFun2(): owned(Future[void])</a></li>
+          </ul></dd>
+<dt><a name="asyncFun3" href="#asyncFun3"><span>asyncFun3:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc asyncFun3(): owned(Future[void])" href="testproject.html#asyncFun3">testproject: proc asyncFun3(): owned(Future[void])</a></li>
+          </ul></dd>
+<dt><a name="aVariable" href="#aVariable"><span>aVariable:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: var aVariable" href="testproject.html#aVariable">testproject: var aVariable</a></li>
+          </ul></dd>
+<dt><a name="B" href="#B"><span>B:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: enum B" href="testproject.html#B">testproject: enum B</a></li>
+          </ul></dd>
+<dt><a name="bar" href="#bar"><span>bar:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc bar(f: FooBuzz)" href="testproject.html#bar">testproject: proc bar(f: FooBuzz)</a></li>
+          <li><a class="reference external"
+          data-doc-search-tag="testproject: proc bar[T](a, b: T): T" href="testproject.html#bar%2CT%2CT">testproject: proc bar[T](a, b: T): T</a></li>
+          <li><a class="reference external"
+          data-doc-search-tag="testproject: macro bar(): untyped" href="testproject.html#bar.m">testproject: macro bar(): untyped</a></li>
+          </ul></dd>
+<dt><a name="baz" href="#baz"><span>baz:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc baz()" href="testproject.html#baz">testproject: proc baz()</a></li>
+          <li><a class="reference external"
+          data-doc-search-tag="testproject: proc baz[T](a, b: T): T" href="testproject.html#baz%2CT%2CT">testproject: proc baz[T](a, b: T): T</a></li>
+          </ul></dd>
+<dt><a name="bEnum" href="#bEnum"><span>bEnum:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: template bEnum(): untyped" href="subdir/subdir_b/utils.html#bEnum.t">utils: template bEnum(): untyped</a></li>
+          </ul></dd>
+<dt><a name="binarySearch" href="#binarySearch"><span>binarySearch:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: proc binarySearch[T, K](a: openArray[T]; key: K;
+                   cmp: proc (x: T; y: K): int {.closure.}): int" href="subdir/subdir_b/utils.html#binarySearch%2CopenArray%5BT%5D%2CK%2Cproc%28T%2CK%29">utils: proc binarySearch[T, K](a: openArray[T]; key: K;
+                   cmp: proc (x: T; y: K): int {.closure.}): int</a></li>
+          </ul></dd>
+<dt><a name="buzz" href="#buzz"><span>buzz:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc buzz[T](a, b: T): T" href="testproject.html#buzz%2CT%2CT">testproject: proc buzz[T](a, b: T): T</a></li>
+          </ul></dd>
+<dt><a name="C_A" href="#C_A"><span>C_A:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: const C_A" href="testproject.html#C_A">testproject: const C_A</a></li>
+          </ul></dd>
+<dt><a name="C_B" href="#C_B"><span>C_B:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: const C_B" href="testproject.html#C_B">testproject: const C_B</a></li>
+          </ul></dd>
+<dt><a name="C_C" href="#C_C"><span>C_C:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: const C_C" href="testproject.html#C_C">testproject: const C_C</a></li>
+          </ul></dd>
+<dt><a name="C_D" href="#C_D"><span>C_D:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: const C_D" href="testproject.html#C_D">testproject: const C_D</a></li>
+          </ul></dd>
+<dt><a name="Circle" href="#Circle"><span>Circle:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: Shapes.Circle" href="testproject.html#Circle">testproject: Shapes.Circle</a></li>
+          </ul></dd>
+<dt><a name="c_nonexistent" href="#c_nonexistent"><span>c_nonexistent:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc c_nonexistent(frmt: cstring): cint" href="testproject.html#c_nonexistent%2Ccstring">testproject: proc c_nonexistent(frmt: cstring): cint</a></li>
+          </ul></dd>
+<dt><a name="c_printf" href="#c_printf"><span>c_printf:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc c_printf(frmt: cstring): cint" href="testproject.html#c_printf%2Ccstring">testproject: proc c_printf(frmt: cstring): cint</a></li>
+          </ul></dd>
+<dt><a name="enumValueA" href="#enumValueA"><span>enumValueA:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: SomeType.enumValueA" href="subdir/subdir_b/utils.html#enumValueA">utils: SomeType.enumValueA</a></li>
+          </ul></dd>
+<dt><a name="enumValueA2" href="#enumValueA2"><span>enumValueA2:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: Foo.enumValueA2" href="testproject.html#enumValueA2">testproject: Foo.enumValueA2</a></li>
+          </ul></dd>
+<dt><a name="enumValueB" href="#enumValueB"><span>enumValueB:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: SomeType.enumValueB" href="subdir/subdir_b/utils.html#enumValueB">utils: SomeType.enumValueB</a></li>
+          </ul></dd>
+<dt><a name="enumValueC" href="#enumValueC"><span>enumValueC:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: SomeType.enumValueC" href="subdir/subdir_b/utils.html#enumValueC">utils: SomeType.enumValueC</a></li>
+          </ul></dd>
+<dt><a name="f" href="#f"><span>f:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: proc f(x: G[int])" href="subdir/subdir_b/utils.html#f%2CG%5Bint%5D">utils: proc f(x: G[int])</a></li>
+          <li><a class="reference external"
+          data-doc-search-tag="utils: proc f(x: G[string])" href="subdir/subdir_b/utils.html#f%2CG%5Bstring%5D">utils: proc f(x: G[string])</a></li>
+          </ul></dd>
+<dt><a name="fn" href="#fn"><span>fn:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: proc fn[T; U, V: SomeFloat]()" href="subdir/subdir_b/utils.html#fn">utils: proc fn[T; U, V: SomeFloat]()</a></li>
+          </ul></dd>
+<dt><a name="fn10" href="#fn10"><span>fn10:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: proc fn10(a: int): int" href="subdir/subdir_b/utils.html#fn10%2Cint">utils: proc fn10(a: int): int</a></li>
+          </ul></dd>
+<dt><a name="fN11" href="#fN11"><span>fN11:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: proc fN11()" href="subdir/subdir_b/utils.html#fN11">utils: proc fN11()</a></li>
+          <li><a class="reference external"
+          data-doc-search-tag="utils: proc fN11(x: int)" href="subdir/subdir_b/utils.html#fN11%2Cint">utils: proc fN11(x: int)</a></li>
+          </ul></dd>
+<dt><a name="fn2" href="#fn2"><span>fn2:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: proc fn2()" href="subdir/subdir_b/utils.html#fn2">utils: proc fn2()</a></li>
+          <li><a class="reference external"
+          data-doc-search-tag="utils: proc fn2(x: int)" href="subdir/subdir_b/utils.html#fn2%2Cint">utils: proc fn2(x: int)</a></li>
+          <li><a class="reference external"
+          data-doc-search-tag="utils: proc fn2(x: int; y: float)" href="subdir/subdir_b/utils.html#fn2%2Cint%2Cfloat">utils: proc fn2(x: int; y: float)</a></li>
+          <li><a class="reference external"
+          data-doc-search-tag="utils: proc fn2(x: int; y: float; z: float)" href="subdir/subdir_b/utils.html#fn2%2Cint%2Cfloat%2Cfloat">utils: proc fn2(x: int; y: float; z: float)</a></li>
+          </ul></dd>
+<dt><a name="fn3" href="#fn3"><span>fn3:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: proc fn3(): auto" href="subdir/subdir_b/utils.html#fn3">utils: proc fn3(): auto</a></li>
+          </ul></dd>
+<dt><a name="fn4" href="#fn4"><span>fn4:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: proc fn4(): auto" href="subdir/subdir_b/utils.html#fn4">utils: proc fn4(): auto</a></li>
+          </ul></dd>
+<dt><a name="fn5" href="#fn5"><span>fn5:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: proc fn5()" href="subdir/subdir_b/utils.html#fn5">utils: proc fn5()</a></li>
+          </ul></dd>
+<dt><a name="fn6" href="#fn6"><span>fn6:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: proc fn6()" href="subdir/subdir_b/utils.html#fn6">utils: proc fn6()</a></li>
+          </ul></dd>
+<dt><a name="fn7" href="#fn7"><span>fn7:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: proc fn7()" href="subdir/subdir_b/utils.html#fn7">utils: proc fn7()</a></li>
+          </ul></dd>
+<dt><a name="fn8" href="#fn8"><span>fn8:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: proc fn8(): auto" href="subdir/subdir_b/utils.html#fn8">utils: proc fn8(): auto</a></li>
+          </ul></dd>
+<dt><a name="fn9" href="#fn9"><span>fn9:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: proc fn9(a: int): int" href="subdir/subdir_b/utils.html#fn9%2Cint">utils: proc fn9(a: int): int</a></li>
+          </ul></dd>
+<dt><a name="Foo" href="#Foo"><span>Foo:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: enum Foo" href="testproject.html#Foo">testproject: enum Foo</a></li>
+          </ul></dd>
+<dt><a name="foo" href="#foo"><span>foo:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: template foo(a, b: SomeType)" href="testproject.html#foo.t%2CSomeType%2CSomeType">testproject: template foo(a, b: SomeType)</a></li>
+          </ul></dd>
+<dt><a name="fooBar" href="#fooBar"><span>fooBar:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: iterator fooBar(a: seq[SomeType]): int" href="subdir/subdir_b/utils.html#fooBar.i%2Cseq%5BSomeType%5D">utils: iterator fooBar(a: seq[SomeType]): int</a></li>
+          </ul></dd>
+<dt><a name="FooBuzz" href="#FooBuzz"><span>FooBuzz:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: type FooBuzz" href="testproject.html#FooBuzz">testproject: type FooBuzz</a></li>
+          </ul></dd>
+<dt><a name="fromUtils1" href="#fromUtils1"><span>fromUtils1:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: iterator fromUtils1(): int" href="testproject.html#fromUtils1.i">testproject: iterator fromUtils1(): int</a></li>
+          </ul></dd>
+<dt><a name="fromUtils2" href="#fromUtils2"><span>fromUtils2:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: template fromUtils2()" href="testproject.html#fromUtils2.t">testproject: template fromUtils2()</a></li>
+          </ul></dd>
+<dt><a name="fromUtils3" href="#fromUtils3"><span>fromUtils3:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc fromUtils3()" href="testproject.html#fromUtils3">testproject: proc fromUtils3()</a></li>
+          </ul></dd>
+<dt><a name="fromUtilsGen" href="#fromUtilsGen"><span>fromUtilsGen:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: template fromUtilsGen(): untyped" href="subdir/subdir_b/utils.html#fromUtilsGen.t">utils: template fromUtilsGen(): untyped</a></li>
+          </ul></dd>
+<dt><a name="funWithGenerics" href="#funWithGenerics"><span>funWithGenerics:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: proc funWithGenerics[T, U: SomeFloat](a: T; b: U)" href="subdir/subdir_b/utils.html#funWithGenerics%2CT%2CU">utils: proc funWithGenerics[T, U: SomeFloat](a: T; b: U)</a></li>
+          </ul></dd>
+<dt><a name="G" href="#G"><span>G:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: object G" href="subdir/subdir_b/utils.html#G">utils: object G</a></li>
+          </ul></dd>
+<dt><a name="isValid" href="#isValid"><span>isValid:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc isValid[T](x: T): bool" href="testproject.html#isValid%2CT">testproject: proc isValid[T](x: T): bool</a></li>
+          </ul></dd>
+<dt><a name="iter1" href="#iter1"><span>iter1:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: iterator iter1(n: int): int" href="testproject.html#iter1.i%2Cint">testproject: iterator iter1(n: int): int</a></li>
+          </ul></dd>
+<dt><a name="iter2" href="#iter2"><span>iter2:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: iterator iter2(n: int): int" href="testproject.html#iter2.i%2Cint">testproject: iterator iter2(n: int): int</a></li>
+          </ul></dd>
+<dt><a name="low" href="#low"><span>low:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc low[T: Ordinal | enum | range](x: T): T" href="testproject.html#low%2CT">testproject: proc low[T: Ordinal | enum | range](x: T): T</a></li>
+          </ul></dd>
+<dt><a name="low2" href="#low2"><span>low2:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc low2[T: Ordinal | enum | range](x: T): T" href="testproject.html#low2%2CT">testproject: proc low2[T: Ordinal | enum | range](x: T): T</a></li>
+          </ul></dd>
+<dt><a name="method1" href="#method1"><span>method1:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: method method1(self: Moo)" href="testproject.html#method1.e%2CMoo">testproject: method method1(self: Moo)</a></li>
+          </ul></dd>
+<dt><a name="method2" href="#method2"><span>method2:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: method method2(self: Moo): int" href="testproject.html#method2.e%2CMoo">testproject: method method2(self: Moo): int</a></li>
+          </ul></dd>
+<dt><a name="method3" href="#method3"><span>method3:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: method method3(self: Moo): int" href="testproject.html#method3.e%2CMoo">testproject: method method3(self: Moo): int</a></li>
+          </ul></dd>
+<dt><a name="myfn" href="#myfn"><span>myfn:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: template myfn()" href="testproject.html#myfn.t">testproject: template myfn()</a></li>
+          </ul></dd>
+<dt><a name="MyObject" href="#MyObject"><span>MyObject:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: object MyObject" href="testproject.html#MyObject">testproject: object MyObject</a></li>
+          </ul></dd>
+<dt><a name="p1" href="#p1"><span>p1:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc p1()" href="testproject.html#p1">testproject: proc p1()</a></li>
+          </ul></dd>
+<dt><a name="Rectangle" href="#Rectangle"><span>Rectangle:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: Shapes.Rectangle" href="testproject.html#Rectangle">testproject: Shapes.Rectangle</a></li>
+          </ul></dd>
+<dt><a name="Shapes" href="#Shapes"><span>Shapes:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: enum Shapes" href="testproject.html#Shapes">testproject: enum Shapes</a></li>
+          </ul></dd>
+<dt><a name="someFunc" href="#someFunc"><span>someFunc:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc someFunc()" href="testproject.html#someFunc">testproject: proc someFunc()</a></li>
+          </ul></dd>
+<dt><a name="somePragma" href="#somePragma"><span>somePragma:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: template somePragma()" href="testproject.html#somePragma.t">testproject: template somePragma()</a></li>
+          </ul></dd>
+<dt><a name="SomeType" href="#SomeType"><span>SomeType:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: enum SomeType" href="subdir/subdir_b/utils.html#SomeType">utils: enum SomeType</a></li>
+          </ul></dd>
+<dt><a name="someType" href="#someType"><span>someType:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="utils: proc someType(): SomeType" href="subdir/subdir_b/utils.html#someType_2">utils: proc someType(): SomeType</a></li>
+          </ul></dd>
+<dt><a name="someVariable" href="#someVariable"><span>someVariable:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: var someVariable" href="testproject.html#someVariable">testproject: var someVariable</a></li>
+          </ul></dd>
+<dt><a name="T19396" href="#T19396"><span>T19396:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: object T19396" href="testproject.html#T19396">testproject: object T19396</a></li>
+          </ul></dd>
+<dt><a name="testNimDocTrailingExample" href="#testNimDocTrailingExample"><span>testNimDocTrailingExample:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: template testNimDocTrailingExample()" href="testproject.html#testNimDocTrailingExample.t">testproject: template testNimDocTrailingExample()</a></li>
+          </ul></dd>
+<dt><a name="Triangle" href="#Triangle"><span>Triangle:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: Shapes.Triangle" href="testproject.html#Triangle">testproject: Shapes.Triangle</a></li>
+          </ul></dd>
+<dt><a name="tripleStrLitTest" href="#tripleStrLitTest"><span>tripleStrLitTest:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc tripleStrLitTest()" href="testproject.html#tripleStrLitTest">testproject: proc tripleStrLitTest()</a></li>
+          </ul></dd>
+<dt><a name="z1" href="#z1"><span>z1:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc z1(): Foo" href="testproject.html#z1">testproject: proc z1(): Foo</a></li>
+          </ul></dd>
+<dt><a name="z10" href="#z10"><span>z10:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc z10()" href="testproject.html#z10">testproject: proc z10()</a></li>
+          </ul></dd>
+<dt><a name="z11" href="#z11"><span>z11:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc z11()" href="testproject.html#z11">testproject: proc z11()</a></li>
+          </ul></dd>
+<dt><a name="z12" href="#z12"><span>z12:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc z12(): int" href="testproject.html#z12">testproject: proc z12(): int</a></li>
+          </ul></dd>
+<dt><a name="z13" href="#z13"><span>z13:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc z13()" href="testproject.html#z13">testproject: proc z13()</a></li>
+          </ul></dd>
+<dt><a name="z14" href="#z14"><span>z14:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: template z14()" href="testproject.html#z14.t">testproject: template z14()</a></li>
+          </ul></dd>
+<dt><a name="z15" href="#z15"><span>z15:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: template z15()" href="testproject.html#z15.t">testproject: template z15()</a></li>
+          </ul></dd>
+<dt><a name="z16" href="#z16"><span>z16:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: macro z16()" href="testproject.html#z16.m">testproject: macro z16()</a></li>
+          </ul></dd>
+<dt><a name="z17" href="#z17"><span>z17:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc z17()" href="testproject.html#z17">testproject: proc z17()</a></li>
+          </ul></dd>
+<dt><a name="z18" href="#z18"><span>z18:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: macro z18(): int" href="testproject.html#z18.m">testproject: macro z18(): int</a></li>
+          </ul></dd>
+<dt><a name="z2" href="#z2"><span>z2:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc z2()" href="testproject.html#z2">testproject: proc z2()</a></li>
+          </ul></dd>
+<dt><a name="z3" href="#z3"><span>z3:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc z3()" href="testproject.html#z3">testproject: proc z3()</a></li>
+          </ul></dd>
+<dt><a name="z4" href="#z4"><span>z4:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc z4()" href="testproject.html#z4">testproject: proc z4()</a></li>
+          </ul></dd>
+<dt><a name="z5" href="#z5"><span>z5:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc z5(): int" href="testproject.html#z5">testproject: proc z5(): int</a></li>
+          </ul></dd>
+<dt><a name="z6" href="#z6"><span>z6:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc z6(): int" href="testproject.html#z6">testproject: proc z6(): int</a></li>
+          </ul></dd>
+<dt><a name="z6t" href="#z6t"><span>z6t:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: template z6t(): int" href="testproject.html#z6t.t">testproject: template z6t(): int</a></li>
+          </ul></dd>
+<dt><a name="z7" href="#z7"><span>z7:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc z7(): int" href="testproject.html#z7">testproject: proc z7(): int</a></li>
+          </ul></dd>
+<dt><a name="z8" href="#z8"><span>z8:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc z8(): int" href="testproject.html#z8">testproject: proc z8(): int</a></li>
+          </ul></dd>
+<dt><a name="z9" href="#z9"><span>z9:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: proc z9()" href="testproject.html#z9">testproject: proc z9()</a></li>
+          </ul></dd>
+</dl>
+      <div class="twelve-columns footer">
+        <span class="nim-sprite"></span>
+        <br>
+        <small style="color: var(--hint);">Made with Nim. Generated: 1970-01-02 03:46:40 UTC</small>
+      </div>
+    </div>
+  </div>
+  
+</body>
+</html>
diff --git a/nimdoc/testproject/subdir/subdir_b/utils.nim b/nimdoc/testproject/subdir/subdir_b/utils.nim
new file mode 100644
index 000000000..f535d7f74
--- /dev/null
+++ b/nimdoc/testproject/subdir/subdir_b/utils.nim
@@ -0,0 +1,213 @@
+##[
+
+.. include:: ./utils_overview.rst
+
+# 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.
+
+Ref group fn2_ or specific function like `fn2()`_
+or `fn2(  int  )`_ or `fn2(int,
+float)`_.
+
+Ref generics like this: binarySearch_ or `binarySearch(openArray[T], K,
+proc (T, K))`_ or `proc binarySearch(openArray[T], K,  proc (T, K))`_ or
+in different style: `proc binarysearch(openarray[T], K, proc(T, K))`_.
+Can be combined with export symbols and type parameters:
+`binarysearch*[T, K](openArray[T], K, proc (T, K))`_.
+With spaces `binary search`_.
+
+Note that `proc` can be used in postfix form: `binarySearch proc`_.
+
+Ref. type like G_ and `type G`_ and `G[T]`_ and `type G*[T]`_.
+
+Group ref. with capital letters works: fN11_ or fn11_
+]##
+
+include ./utils_helpers
+
+type
+  SomeType* = enum
+    enumValueA,
+    enumValueB,
+    enumValueC
+  G*[T] = object
+    val: T
+
+proc someType*(): SomeType =
+  ## constructor.
+  SomeType(2)
+
+
+proc fn2*() = discard ## comment
+proc fn2*(x: int) =
+  ## fn2 comment
+  discard
+proc fn2*(x: int, y: float) =
+  discard
+proc binarySearch*[T, K](a: openArray[T]; key: K;
+                         cmp: proc (x: T; y: K): int {.closure.}): int =
+  discard
+proc fn3*(): auto = 1 ## comment
+proc fn4*(): auto = 2 * 3 + 4 ## comment
+proc fn5*() ## comment
+proc fn5*() = discard
+proc fn6*() =
+  ## comment
+proc fn7*() =
+  ## comment
+  discard
+proc fn8*(): auto =
+  ## comment
+  1+1
+func fn9*(a: int): int = 42  ## comment
+func fn10*(a: int): int = a  ## comment
+
+# Note capital letter N will be handled correctly in
+# group references like fN11_ or fn11_
+# (or [fN11] or [fn11] in Markdown Syntax):
+
+func fN11*() = discard
+func fN11*(x: int) = discard
+
+# bug #9235
+
+template aEnum*(): untyped =
+  type
+    A* {.inject.} = enum ## The enum A.
+      aA
+
+template bEnum*(): untyped =
+  type
+    B* {.inject.} = enum ## The enum B.
+      bB
+
+  func someFunc*() =
+    ## My someFunc.
+    ## Stuff in `quotes` here.
+    ## [Some link](https://nim-lang.org)
+    discard
+
+template fromUtilsGen*(): untyped =
+  ## should be shown in utils.html only
+  runnableExamples:
+    discard "should be in utils.html only, not in module that calls fromUtilsGen"
+  ## ditto
+
+  iterator fromUtils1*(): int =
+    runnableExamples:
+      # ok1
+      assert 1 == 1
+      # ok2
+    yield 15
+
+  template fromUtils2*() =
+    ## ok3
+    runnableExamples:
+      discard """should be shown as examples for fromUtils2
+       in module calling fromUtilsGen"""
+
+  proc fromUtils3*() =
+    ## came form utils but should be shown where `fromUtilsGen` is called
+    runnableExamples: discard """should be shown as examples for fromUtils3
+       in module calling fromUtilsGen"""
+
+proc f*(x: G[int]) =
+  ## There is also variant `f(G[string])`_
+  discard
+proc f*(x: G[string]) =
+  ## See also `f(G[int])`_.
+  discard
+
+## Ref. `[]`_ is the same as `proc \`[]\`(G[T])`_ because there are no
+## overloads. The full form: `proc \`[]\`*[T](x: G[T]): T`_
+
+proc `[]`*[T](x: G[T]): T = x.val
+
+## Ref. `[]=`_ aka `\`[]=\`(G[T], int, T)`_.
+
+proc `[]=`*[T](a: var G[T], index: int, value: T) = discard
+
+## Ref. `$`_ aka `proc $`_ or `proc \`$\``_.
+
+proc `$`*[T](a: G[T]): string = ""
+
+## Ref. `$(a: ref SomeType)`_.
+
+proc `$`*[T](a: ref SomeType): string = ""
+
+## Ref. foo_bar_ aka `iterator foo_bar_`_.
+
+iterator fooBar*(a: seq[SomeType]): int = discard
+
+## Ref. `fn[T; U,V: SomeFloat]()`_.
+
+proc fn*[T; U, V: SomeFloat]() = discard
+
+## Ref. `'big`_ or `func \`'big\``_ or `\`'big\`(string)`_.
+
+func `'big`*(a: string): SomeType = discard
+
+##[
+
+Pandoc Markdown
+===============
+
+Now repeat all the auto links of above in Pandoc Markdown Syntax.
+
+Ref group [fn2] or specific function like [fn2()]
+or [fn2(  int  )] or [fn2(int,
+float)].
+
+Ref generics like this: [binarySearch] or [binarySearch(openArray[T], K,
+proc (T, K))] or [proc binarySearch(openArray[T], K,  proc (T, K))] or
+in different style: [proc binarysearch(openarray[T], K, proc(T, K))].
+Can be combined with export symbols and type parameters:
+[binarysearch*[T, K](openArray[T], K, proc (T, K))].
+With spaces [binary search].
+
+Note that `proc` can be used in postfix form: [binarySearch proc].
+
+Ref. type like [G] and [type G] and [G[T]] and [type G*[T]].
+
+Group ref. with capital letters works: [fN11] or [fn11]
+
+Ref. [`[]`] is the same as [proc `[]`(G[T])] because there are no
+overloads. The full form: [proc `[]`*[T](x: G[T]): T]
+Ref. [`[]=`] aka [`[]=`(G[T], int, T)].
+Ref. [$] aka [proc $] or [proc `$`].
+Ref. [$(a: ref SomeType)].
+Ref. [foo_bar] aka [iterator foo_bar_].
+Ref. [fn[T; U,V: SomeFloat]()].
+Ref. ['big] or [func `'big`] or [`'big`(string)].
+
+Link name syntax
+----------------
+
+Pandoc Markdown has synax for changing text of links:
+Ref. [this proc][`[]`] or [another symbol][G[T]].
+
+Symbols documentation
+---------------------
+
+Let us repeat auto links from symbols section below:
+
+There is also variant [f(G[string])].
+See also [f(G[int])].
+
+]##
diff --git a/nimdoc/testproject/subdir/subdir_b/utils_helpers.nim b/nimdoc/testproject/subdir/subdir_b/utils_helpers.nim
new file mode 100644
index 000000000..d3f5edd29
--- /dev/null
+++ b/nimdoc/testproject/subdir/subdir_b/utils_helpers.nim
@@ -0,0 +1,6 @@
+proc funWithGenerics*[T, U: SomeFloat](a: T, b: U) = discard
+
+# We check that presence of overloaded `fn2` here does not break
+# referencing in the "parent" file (the one that includes this one)
+proc fn2*(x: int, y: float, z: float) =
+  discard
diff --git a/nimdoc/testproject/subdir/subdir_b/utils_overview.rst b/nimdoc/testproject/subdir/subdir_b/utils_overview.rst
new file mode 100644
index 000000000..58ce930bf
--- /dev/null
+++ b/nimdoc/testproject/subdir/subdir_b/utils_overview.rst
@@ -0,0 +1,8 @@
+This is a description of the utils module.
+
+Links work:
+
+* other module: `iterators <iterator.html>`_ (not in this dir, just an example)
+* internal: `fn2(x)`_
+* internal included from another module: `funWithGenerics*[T, U:
+  SomeFloat](a: T, b: U)`_.
diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim
new file mode 100644
index 000000000..383c4c827
--- /dev/null
+++ b/nimdoc/testproject/testproject.nim
@@ -0,0 +1,422 @@
+## Basic usage
+## ===========
+##
+## Encoding data
+## -------------
+##
+## Apart from strings you can also encode lists of integers or characters:
+
+## Decoding data
+## -------------
+##
+
+
+import subdir / subdir_b / utils
+
+## This is the top level module.
+runnableExamples:
+  import subdir / subdir_b / utils
+  doAssert bar(3, 4) == 7
+  foo(enumValueA, enumValueB)
+  # bug #11078
+  for x in "xx": discard
+
+
+var someVariable*: bool ## This should be visible.
+
+when true:
+  ## top2
+  runnableExamples:
+    discard "in top2"
+  ## top2 after
+
+runnableExamples:
+  discard "in top3"
+## top3 after
+
+const
+  C_A* = 0x7FF0000000000000'f64
+  C_B* = 0o377'i8
+  C_C* = 0o277'i8
+  C_D* = 0o177777'i16
+
+proc bar*[T](a, b: T): T =
+  result = a + b
+
+proc baz*[T](a, b: T): T {.deprecated.} =
+  ## This is deprecated without message.
+  result = a + b
+
+proc buzz*[T](a, b: T): T {.deprecated: "since v0.20".} =
+  ## This is deprecated with a message.
+  result = a + b
+
+type
+  FooBuzz* {.deprecated: "FooBuzz msg".} = int
+
+using f: FooBuzz
+
+proc bar*(f) = # `f` should be expanded to `f: FooBuzz`
+  discard
+
+import std/macros
+
+var aVariable*: array[1, int]
+
+# bug #9432
+aEnum()
+bEnum()
+fromUtilsGen()
+
+proc isValid*[T](x: T): bool = x.len > 0
+
+when true:
+  # these cases appear redundant but they're actually (almost) all different at
+  # AST level and needed to ensure docgen keeps working, e.g. because of issues
+  # like D20200526T163511
+  type
+    Foo* = enum
+      enumValueA2
+
+  proc z1*(): Foo =
+    ## cz1
+    Foo.default
+
+  proc z2*() =
+    ## cz2
+    runnableExamples:
+      discard "in cz2"
+
+  proc z3*() =
+    ## cz3
+
+  proc z4*() =
+    ## cz4
+    discard
+
+when true:
+  # tests for D20200526T163511
+  proc z5*(): int =
+    ## cz5
+    return 1
+
+  proc z6*(): int =
+    ## cz6
+    1
+
+  template z6t*(): int =
+    ## cz6t
+    1
+
+  proc z7*(): int =
+    ## cz7
+    result = 1
+
+  proc z8*(): int =
+    ## cz8
+    block:
+      discard
+      1+1
+
+when true:
+  # interleaving 0 or more runnableExamples and doc comments, issue #9227
+  proc z9*() =
+    runnableExamples: doAssert 1 + 1 == 2
+
+  proc z10*() =
+    runnableExamples "-d:foobar":
+      discard 1
+    ## cz10
+
+  proc z11*() =
+    runnableExamples:
+      discard 1
+    discard
+
+  proc z12*(): int =
+    runnableExamples:
+      discard 1
+    12
+
+  proc z13*() =
+    ## cz13
+    runnableExamples:
+      discard
+
+  proc baz*() = discard
+
+  proc bazNonExported() =
+    ## out (not exported)
+    runnableExamples:
+      # BUG: this currently this won't be run since not exported
+      # but probably should
+      doAssert false
+  if false: bazNonExported() # silence XDeclaredButNotUsed
+
+  proc z17*() =
+    # BUG: a comment before 1st doc comment currently doesn't prevent
+    # doc comment from being docgen'd; probably should be fixed
+    ## cz17
+    ## rest
+    runnableExamples:
+      discard 1
+    ## rest
+    # this comment separates docgen'd doc comments
+    ## out
+
+when true: # capture non-doc comments correctly even before 1st token
+  proc p1*() =
+    ## cp1
+    runnableExamples: doAssert 1 == 1 # regular comments work here
+    ## c4
+    runnableExamples:
+      # c5 regular comments before 1st token work
+      # regular comment
+      #[
+      nested regular comment
+      ]#
+      doAssert 2 == 2 # c8
+      ## this is a non-nested doc comment
+
+      ##[
+      this is a nested doc comment
+      ]##
+      discard "c9"
+      # also work after
+    # this should be out
+
+when true: # issue #14485
+  proc addfBug14485*() =
+    ## Some proc
+    runnableExamples:
+      discard "foo() = " & $[1]
+      #[
+      0: let's also add some broken html to make sure this won't break in future
+      1: </span>
+      2: </span>
+      3: </span
+      4: </script>
+      5: </script
+      6: </script
+      7: end of broken html
+      ]#
+
+when true: # procs without `=` (using comment field)
+  proc c_printf*(frmt: cstring): cint {.importc: "printf", header: "<stdio.h>", varargs, discardable.}
+    ## the c printf.
+    ## etc.
+
+  proc c_nonexistent*(frmt: cstring): cint {.importc: "nonexistent", header: "<stdio.h>", varargs, discardable.}
+
+when true: # tests RST inside comments
+  proc low*[T: Ordinal|enum|range](x: T): T {.magic: "Low", noSideEffect.}
+    ## Returns the lowest possible value of an ordinal value `x`. As a special
+    ## semantic rule, `x` may also be a type identifier.
+    ##
+    ## See also:
+    ## * `low2(T) <#low2,T>`_
+    ##
+    ## .. code-block:: Nim
+    ##  low(2) # => -9223372036854775808
+
+  proc low2*[T: Ordinal|enum|range](x: T): T {.magic: "Low", noSideEffect.} =
+    ## Returns the lowest possible value of an ordinal value `x`. As a special
+    ## semantic rule, `x` may also be a type identifier.
+    ##
+    ## See also:
+    ## * `low(T) <#low,T>`_
+    ##
+    ## .. code-block:: Nim
+    ##  low2(2) # => -9223372036854775808
+    runnableExamples:
+      discard "in low2"
+
+when true: # multiline string litterals
+  proc tripleStrLitTest*() =
+    runnableExamples("--hint:XDeclaredButNotUsed:off"):
+      ## mullitline string litterals are tricky as their indentation can span
+      ## below that of the runnableExamples
+      let s1a = """
+should appear at indent 0
+  at indent 2
+at indent 0
+"""
+      # make sure this works too
+      let s1b = """start at same line
+  at indent 2
+at indent 0
+""" # comment after
+      let s2 = """sandwich """
+      let s3 = """"""
+      when false:
+        let s5 = """
+        in s5 """
+
+      let s3b = ["""
+%!? #[...] # inside a multiline ...
+""", "foo"]
+
+      ## make sure handles trailing spaces
+      let s4 = """ 
+"""
+
+      let s5 = """ x
+"""
+      let s6 = """ ""
+"""
+      let s7 = """"""""""
+      let s8 = ["""""""""", """
+  """ ]
+      discard
+      # should be in
+    # should be out
+
+when true: # methods; issue #14691
+  type Moo = object
+  method method1*(self: Moo) {.base.} =
+    ## foo1
+  method method2*(self: Moo): int {.base.} =
+    ## foo2
+    result = 1
+  method method3*(self: Moo): int {.base.} =
+    ## foo3
+    1
+
+when true: # iterators
+  iterator iter1*(n: int): int =
+    ## foo1
+    for i in 0..<n:
+      yield i
+  iterator iter2*(n: int): int =
+    ## foo2
+    runnableExamples:
+      discard # bar
+    yield 0
+
+when true: # (most) macros
+  macro bar*(): untyped =
+    result = newStmtList()
+
+  macro z16*() =
+    runnableExamples: discard 1
+    ## cz16
+    ## after
+    runnableExamples:
+      doAssert 2 == 1 + 1
+    # BUG: we should probably render `cz16\nafter` by keeping newline instead or
+    # what it currently renders as: `cz16 after`
+
+  macro z18*(): int =
+    ## cz18
+    newLit 0
+
+when true: # (most) templates
+  template foo*(a, b: SomeType) =
+    ## This does nothing
+    ##
+    discard
+
+  template myfn*() =
+    runnableExamples:
+      import std/strutils
+      ## issue #8871 preserve formatting
+      ## line doc comment
+      # bar
+      doAssert "'foo" == "'foo"
+      ##[
+      foo
+      bar
+      ]##
+
+      doAssert: not "foo".startsWith "ba"
+      block:
+        discard 0xff # elu par cette crapule
+      # should be in
+    ## should be still in
+
+    # out
+    ## out
+
+  template z14*() =
+    ## cz14
+    runnableExamples:
+      discard
+
+  template z15*() =
+    ## cz15
+    runnableExamples:
+      discard
+    runnableExamples: discard 3
+    runnableExamples: discard 4
+    ## ok5
+    ## ok5b
+    runnableExamples: assert true
+    runnableExamples: discard 1
+
+    ## in or out?
+    discard 8
+    ## out
+
+when true: # issue #14473
+  import std/[sequtils]
+  template doit(): untyped =
+    ## doit
+    ## return output only
+    toSeq(["D20210427T172228"]) # make it searcheable at least until we figure out a way to avoid echo
+  echo doit() # using doAssert or similar to avoid echo would "hide" the original bug
+
+when true: # issue #14846
+  import asyncdispatch
+  proc asyncFun1*(): Future[int] {.async.} =
+    ## ok1
+    result = 1
+  proc asyncFun2*() {.async.} = discard
+  proc asyncFun3*() {.async.} =
+    runnableExamples:
+      discard
+    ## ok1
+    discard
+    ## should be out
+    discard
+
+when true:
+  template testNimDocTrailingExample*() =
+    # this must be last entry in this file, it checks against a bug (that got fixed)
+    # where runnableExamples would not show if there was not at least 2 "\n" after
+    # the last character of runnableExamples
+    runnableExamples:
+      discard 2
+
+when true: # issue #15702
+  type
+    Shapes* = enum
+      ## Some shapes.
+      Circle,     ## A circle
+      Triangle,   ## A three-sided shape
+      Rectangle   ## A four-sided shape
+
+when true: # issue #15184
+  proc anything* =
+    ##
+    ##  There is no block quote after blank lines at the beginning.
+  discard
+
+type T19396* = object # bug #19396
+   a*: int
+   b: float
+
+template somePragma*() {.pragma.}
+  ## Just some annotation
+
+type # bug #21483
+   MyObject* = object
+      someString*: string ## This is a string
+      annotated* {.somePragma.}: string ## This is an annotated string
+
+type
+  AnotherObject* = object
+    case x*: bool
+    of true:
+      y*: proc (x: string)
+    of false:
+      hidden: string
diff --git a/tinyc/tests/tests2/80_flexarray.expect b/nimdoc/testproject/testproject.nimble
index e69de29bb..e69de29bb 100644
--- a/tinyc/tests/tests2/80_flexarray.expect
+++ b/nimdoc/testproject/testproject.nimble
diff --git a/nimpretty/nimpretty.nim b/nimpretty/nimpretty.nim
index aa9756c45..e5abf0d2d 100644
--- a/nimpretty/nimpretty.nim
+++ b/nimpretty/nimpretty.nim
@@ -12,22 +12,25 @@
 when not defined(nimpretty):
   {.error: "This needs to be compiled with --define:nimPretty".}
 
-import ../compiler / [idents, msgs, ast, syntaxes, renderer, options]
+import ../compiler / [idents, llstream, ast, msgs, syntaxes, options, pathutils, layouter]
 
-import parseopt, strutils, os
+import parseopt, strutils, os, sequtils
 
 const
-  Version = "0.1"
+  Version = "0.2"
   Usage = "nimpretty - Nim Pretty Printer Version " & Version & """
 
   (c) 2017 Andreas Rumpf
 Usage:
-  nimpretty [options] file.nim
+  nimpretty [options] nimfiles...
 Options:
-  --backup:on|off     create a backup file before overwritting (default: ON)
-  --output:file       set the output file (default: overwrite the .nim file)
-  --version           show the version
-  --help              show this help
+  --out:file            set the output file (default: overwrite the input file)
+  --outDir:dir          set the output dir (default: overwrite the input files)
+  --indent:N[=0]        set the number of spaces that is used for indentation
+                        --indent:0 means autodetection (default behaviour)
+  --maxLineLen:N        set the desired maximum line length (default: 80)
+  --version             show the version
+  --help                show this help
 """
 
 proc writeHelp() =
@@ -40,36 +43,125 @@ proc writeVersion() =
   stdout.flushFile()
   quit(0)
 
-proc prettyPrint(infile, outfile: string) =
-  var conf = newConfigRef()
-  let fileIdx = fileInfoIdx(conf, infile)
-  conf.outFile = outfile
-  when defined(nimpretty2):
-    discard parseFile(fileIdx, newIdentCache(), conf)
+type
+  PrettyOptions* = object
+    indWidth*: Natural
+    maxLineLen*: Positive
+
+proc goodEnough(a, b: PNode): bool =
+  if a.kind == b.kind and a.safeLen == b.safeLen:
+    case a.kind
+    of nkNone, nkEmpty, nkNilLit: result = true
+    of nkIdent: result = a.ident.id == b.ident.id
+    of nkSym: result = a.sym == b.sym
+    of nkType: result = true
+    of nkCharLit, nkIntLit..nkInt64Lit, nkUIntLit..nkUInt64Lit:
+      result = a.intVal == b.intVal
+    of nkFloatLit..nkFloat128Lit:
+      result = a.floatVal == b.floatVal
+    of nkStrLit, nkRStrLit, nkTripleStrLit:
+      result = a.strVal == b.strVal
+    else:
+      for i in 0 ..< a.len:
+        if not goodEnough(a[i], b[i]): return false
+      return true
+  elif a.kind == nkStmtList and a.len == 1:
+    result = goodEnough(a[0], b)
+  elif b.kind == nkStmtList and b.len == 1:
+    result = goodEnough(a, b[0])
   else:
-    let tree = parseFile(fileIdx, newIdentCache(), conf)
-    renderModule(tree, infile, outfile, {}, fileIdx, conf)
+    result = false
+
+proc finalCheck(content: string; origAst: PNode): bool {.nimcall.} =
+  var conf = newConfigRef()
+  let oldErrors = conf.errorCounter
+  var parser: Parser
+  parser.em.indWidth = 2
+  let fileIdx = fileInfoIdx(conf, AbsoluteFile "nimpretty_bug.nim")
+
+  openParser(parser, fileIdx, llStreamOpen(content), newIdentCache(), conf)
+  let newAst = parseAll(parser)
+  closeParser(parser)
+  result = conf.errorCounter == oldErrors # and goodEnough(newAst, origAst)
+
+proc prettyPrint*(infile, outfile: string, opt: PrettyOptions) =
+  var conf = newConfigRef()
+  let fileIdx = fileInfoIdx(conf, AbsoluteFile infile)
+  let f = splitFile(outfile.expandTilde)
+  conf.outFile = RelativeFile f.name & f.ext
+  conf.outDir = toAbsoluteDir f.dir
+  var parser: Parser
+  parser.em.indWidth = opt.indWidth
+  if setupParser(parser, fileIdx, newIdentCache(), conf):
+    parser.em.maxLineLen = opt.maxLineLen
+    let fullAst = parseAll(parser)
+    closeParser(parser)
+    when defined(nimpretty):
+      closeEmitter(parser.em, fullAst, finalCheck)
 
 proc main =
-  var infile, outfile: string
-  var backup = true
+  var outfile, outdir: string
+
+  var infiles = newSeq[string]()
+  var outfiles = newSeq[string]()
+
+  var backup = false
+    # when `on`, create a backup file of input in case
+    # `prettyPrint` could overwrite it (note that the backup may happen even
+    # if input is not actually overwritten, when nimpretty is a noop).
+    # --backup was un-documented (rely on git instead).
+  var opt = PrettyOptions(indWidth: 0, maxLineLen: 80)
+
+
   for kind, key, val in getopt():
     case kind
     of cmdArgument:
-      infile = key.addFileExt(".nim")
-    of cmdLongoption, cmdShortOption:
+      if dirExists(key):
+        for file in walkDirRec(key, skipSpecial = true):
+          if file.endsWith(".nim") or file.endsWith(".nimble"):
+            infiles.add(file)
+      else:
+        infiles.add(key.addFileExt(".nim"))
+
+    of cmdLongOption, cmdShortOption:
       case normalize(key)
       of "help", "h": writeHelp()
       of "version", "v": writeVersion()
       of "backup": backup = parseBool(val)
-      of "output", "o": outfile = val
+      of "output", "o", "out": outfile = val
+      of "outDir", "outdir": outdir = val
+      of "indent": opt.indWidth = parseInt(val)
+      of "maxlinelen": opt.maxLineLen = parseInt(val)
       else: writeHelp()
     of cmdEnd: assert(false) # cannot happen
-  if infile.len == 0:
+  if infiles.len == 0:
     quit "[Error] no input file."
-  if backup:
-    os.copyFile(source=infile, dest=changeFileExt(infile, ".nim.backup"))
-  if outfile.len == 0: outfile = infile
-  prettyPrint(infile, outfile)
 
-main()
+  if outfile.len != 0 and outdir.len != 0:
+    quit "[Error] out and outDir cannot both be specified"
+
+  if outfile.len == 0 and outdir.len == 0:
+    outfiles = infiles
+  elif outfile.len != 0 and infiles.len > 1:
+    # Take the last file to maintain backwards compatibility
+    let infile = infiles[^1]
+    infiles = @[infile]
+    outfiles = @[outfile]
+  elif outfile.len != 0:
+    outfiles = @[outfile]
+  elif outdir.len != 0:
+    outfiles = infiles.mapIt($(joinPath(outdir, it)))
+
+  for (infile, outfile) in zip(infiles, outfiles):
+    let (dir, _, _) = splitFile(outfile)
+    createDir(dir)
+    if not fileExists(outfile) or not sameFile(infile, outfile):
+      backup = false # no backup needed since won't be over-written
+    if backup:
+      let infileBackup = infile & ".backup" # works with .nim or .nims
+      echo "writing backup " & infile & " > " & infileBackup
+      os.copyFile(source = infile, dest = infileBackup)
+    prettyPrint(infile, outfile, opt)
+
+when isMainModule:
+  main()
diff --git a/nimpretty/nimpretty.nim.cfg b/nimpretty/nimpretty.nim.cfg
index 5fafa6d2a..25336d924 100644
--- a/nimpretty/nimpretty.nim.cfg
+++ b/nimpretty/nimpretty.nim.cfg
@@ -1,2 +1 @@
 --define: nimpretty
---define: nimpretty2
diff --git a/nimpretty/tester.nim b/nimpretty/tester.nim
index 8798ce06a..b1f15aee6 100644
--- a/nimpretty/tester.nim
+++ b/nimpretty/tester.nim
@@ -1,29 +1,64 @@
 # Small program that runs the test cases
 
-import strutils, os
+import strutils, os, sequtils
+from std/private/gitutils import diffFiles
 
 const
-  dir = "nimpretty/tests/"
+  dir = "nimpretty/tests"
+  outputdir = dir / "outputdir"
 
 var
   failures = 0
 
-proc test(infile, outfile: string) =
-  if execShellCmd("nimpretty -o:$2 --backup:off $1" % [infile, outfile]) != 0:
-    quit("FAILURE")
+when defined(develop):
+  const nimp = "bin" / "nimpretty".addFileExt(ExeExt)
+  if execShellCmd("nim c -o:$# nimpretty/nimpretty.nim" % [nimp]) != 0:
+    quit("FAILURE: compilation of nimpretty failed")
+else:
+  const nimp = "nimpretty"
+
+proc test(infile, ext: string) =
+  if execShellCmd("$# -o:$# --backup:off $#" % [nimp, infile.changeFileExt(ext), infile]) != 0:
+    echo "FAILURE: nimpretty cannot prettify ", infile
+    failures += 1
+    return
   let nimFile = splitFile(infile).name
   let expected = dir / "expected" / nimFile & ".nim"
-  let produced = dir / nimFile & ".pretty"
-  if strip(readFile(expected)) != strip(readFile(produced)):
+  let produced = dir / nimFile.changeFileExt(ext)
+  if readFile(expected) != readFile(produced):
     echo "FAILURE: files differ: ", nimFile
-    discard execShellCmd("diff -uNdr " & expected & " " & produced)
+    echo diffFiles(expected, produced).output
     failures += 1
   else:
     echo "SUCCESS: files identical: ", nimFile
 
-for t in walkFiles(dir / "*.nim"):
-  let res = t.changeFileExt("pretty")
-  test(t, res)
-  removeFile(res)
+proc testTogether(infiles: seq[string]) =
+  if execShellCmd("$# --outDir:$# --backup:off $#" % [nimp, outputdir, infiles.join(" ")]) != 0:
+    echo "FAILURE: nimpretty cannot prettify files: ", $infiles
+    failures += 1
+    return
+
+  for infile in infiles:
+    let nimFile = splitFile(infile).name
+    let expected = dir / "expected" / nimFile & ".nim"
+    let produced = dir / "outputdir" / infile
+    if readFile(expected) != readFile(produced):
+      echo "FAILURE: files differ: ", nimFile
+      echo diffFiles(expected, produced).output
+      failures += 1
+    else:
+      echo "SUCCESS: files identical: ", nimFile
+
+let allFiles = toSeq(walkFiles(dir / "*.nim"))
+for t in allFiles:
+  test(t, "pretty")
+  # also test that pretty(pretty(x)) == pretty(x)
+  test(t.changeFileExt("pretty"), "pretty2")
+
+  removeFile(t.changeFileExt("pretty"))
+  removeFile(t.changeFileExt("pretty2"))
+
+testTogether(allFiles)
+removeDir(outputdir)
 
 if failures > 0: quit($failures & " failures occurred.")
diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim
index 9f2141fbb..e5a73305b 100644
--- a/nimpretty/tests/exhaustive.nim
+++ b/nimpretty/tests/exhaustive.nim
@@ -1,5 +1,5 @@
 discard """
-  outputsub: '''ObjectAssignmentError'''
+  outputsub: '''ObjectAssignmentDefect'''
   exitcode: "1"
 """
 
@@ -122,7 +122,7 @@ type
     fixedUntil: int # marks where we must not go in the content
     altSplitPos: array[SplitKind, int] # alternative split positions
 
-proc openEmitter*[T, S](em: var Emitter, config: ConfigRef, fileIdx: FileIndex) {.pragmaHereWrongCurlyEnd} =
+proc openEmitter*[T, S](em: var Emitter; config: ConfigRef; fileIdx: FileIndex) {.pragmaHereWrongCurlyEnd} =
   let outfile = changeFileExt(config.toFullPath(fileIdx), ".pretty.nim")
   em.f = llStreamOpen(outfile, fmWrite)
   em.config = config
@@ -267,7 +267,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
     if not em.endsInWhite: wr(" ")
     wr(tok.ident.s)
     template isUnary(tok): bool =
-      tok.strongSpaceB == 0 and tok.strongSpaceA > 0
+      tok.spacing == {tsLeading}
 
     if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}:
       wr(" ")
@@ -314,3 +314,536 @@ proc f() =
   if c == '\\':
     # escape char
     str &= c
+
+proc getKeyAndData(cursor: int, op: int):
+    tuple[key, data: string, success: bool] {.noInit.} =
+  var keyVal: string
+  var dataVal: string
+
+#!nimpretty off
+  when stuff:
+    echo "so nice"
+    echo "more"
+  else:
+     echo "misaligned"
+#!nimpretty on
+
+const test = r"C:\Users\-\Desktop\test.txt"
+
+proc abcdef*[T:not (tuple|object|string|cstring|char|ref|ptr|array|seq|distinct)]() =
+  # bug #9504
+  type T2 = a.type
+  discard
+
+proc fun() =
+  #[
+  this one here
+  ]#
+  discard
+
+proc fun2() =
+  ##[
+  foobar
+  ]##
+  discard
+
+#[
+foobar
+]#
+
+proc fun3() =
+  discard
+
+##[
+foobar
+]##
+
+# bug #9673
+discard `* `(1, 2)
+
+proc fun4() =
+  var a = "asdf"
+  var i = 0
+  while i<a.len and i<a.len:
+    return
+
+
+# bug #10295
+
+import osproc
+let res = execProcess(
+    "echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates")
+
+let res = execProcess("echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates")
+
+
+# bug #10177
+
+proc foo  *  () =
+  discard
+
+proc foo* [T]() =
+  discard
+
+
+# bug #10159
+
+proc fun() =
+  discard
+
+proc main() =
+    echo "foo"; echo "bar";
+    discard
+
+main()
+
+type
+  TCallingConvention* = enum
+    ccDefault,                # proc has no explicit calling convention
+    ccStdCall,    # procedure is stdcall
+    ccCDecl,                  # cdecl
+    ccSafeCall,               # safecall
+    ccSysCall, # system call
+    ccInline,                 # proc should be inlined
+    ccNoInline,               # proc should not be inlined
+    ccFastCall,               # fastcall (pass parameters in registers)
+    ccClosure,        # proc has a closure
+    ccNoConvention       # needed for generating proper C procs sometimes
+
+
+proc isValid1*[A](s: HashSet[A]): bool {.deprecated:
+    "Deprecated since v0.20; sets are initialized by default".} =
+  ## Returns `true` if the set has been initialized (with `initHashSet proc
+  ## <#initHashSet,int>`_ or `init proc <#init,HashSet[A],int>`_).
+  result = s.data.len > 0
+  # bug #11468
+
+assert $typeof(a) == "Option[system.int]"
+foo(a, $typeof(b), c)
+foo(typeof(b), c) # this is ok
+
+proc `<`*[A](s, t: A): bool = discard
+proc `==`*[A](s, t: HashSet[A]): bool = discard
+proc `<=`*[A](s, t: HashSet[A]): bool = discard
+
+# these are ok:
+proc `$`*[A](s: HashSet[A]): string = discard
+proc `*`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} = discard
+proc `-+-`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} = discard
+
+# bug #11470
+
+
+# bug #11467
+
+type
+  FirstEnum = enum ## doc comment here
+    first,  ## this is first
+    second, ## second doc
+    third,  ## third one
+    fourth  ## the last one
+
+
+type
+  SecondEnum = enum ## doc comment here
+    first,  ## this is first
+    second, ## second doc
+    third,  ## third one
+    fourth, ## the last one
+
+
+type
+  ThirdEnum = enum ## doc comment here
+    first    ## this is first
+    second   ## second doc
+    third    ## third one
+    fourth   ## the last one
+
+
+type
+  HttpMethod* = enum  ## the requested HttpMethod
+    HttpHead,         ## Asks for the response identical to the one that would
+                      ## correspond to a GET request, but without the response
+                      ## body.
+    HttpGet,          ## Retrieves the specified resource.
+    HttpPost,         ## Submits data to be processed to the identified
+                      ## resource. The data is included in the body of the
+                      ## request.
+    HttpPut,          ## Uploads a representation of the specified resource.
+    HttpDelete,       ## Deletes the specified resource.
+    HttpTrace,        ## Echoes back the received request, so that a client
+                      ## can see what intermediate servers are adding or
+                      ## changing in the request.
+    HttpOptions,      ## Returns the HTTP methods that the server supports
+                      ## for specified address.
+    HttpConnect,      ## Converts the request connection to a transparent
+                      ## TCP/IP tunnel, usually used for proxies.
+    HttpPatch         ## Applies partial modifications to a resource.
+
+type
+  HtmlTag* = enum  ## list of all supported HTML tags; order will always be
+                   ## alphabetically
+    tagUnknown,    ## unknown HTML element
+    tagA,          ## the HTML ``a`` element
+    tagAbbr,       ## the deprecated HTML ``abbr`` element
+    tagAcronym,    ## the HTML ``acronym`` element
+    tagAddress,    ## the HTML ``address`` element
+    tagApplet,     ## the deprecated HTML ``applet`` element
+    tagArea,       ## the HTML ``area`` element
+    tagArticle,    ## the HTML ``article`` element
+    tagAside,      ## the HTML ``aside`` element
+    tagAudio,      ## the HTML ``audio`` element
+    tagB,          ## the HTML ``b`` element
+    tagBase,       ## the HTML ``base`` element
+    tagBdi,        ## the HTML ``bdi`` element
+    tagBdo,        ## the deprecated HTML ``dbo`` element
+    tagBasefont,   ## the deprecated HTML ``basefont`` element
+    tagBig,        ## the HTML ``big`` element
+    tagBlockquote, ## the HTML ``blockquote`` element
+    tagBody,       ## the HTML ``body`` element
+    tagBr,         ## the HTML ``br`` element
+    tagButton,     ## the HTML ``button`` element
+    tagCanvas,     ## the HTML ``canvas`` element
+    tagCaption,    ## the HTML ``caption`` element
+    tagCenter,     ## the deprecated HTML ``center`` element
+    tagCite,       ## the HTML ``cite`` element
+    tagCode,       ## the HTML ``code`` element
+    tagCol,        ## the HTML ``col`` element
+    tagColgroup,   ## the HTML ``colgroup`` element
+    tagCommand,    ## the HTML ``command`` element
+    tagDatalist,   ## the HTML ``datalist`` element
+    tagDd,         ## the HTML ``dd`` element
+    tagDel,        ## the HTML ``del`` element
+    tagDetails,    ## the HTML ``details`` element
+    tagDfn,        ## the HTML ``dfn`` element
+    tagDialog,     ## the HTML ``dialog`` element
+    tagDiv,        ## the HTML ``div`` element
+    tagDir,        ## the deprecated HTLM ``dir`` element
+    tagDl,         ## the HTML ``dl`` element
+    tagDt,         ## the HTML ``dt`` element
+    tagEm,         ## the HTML ``em`` element
+    tagEmbed,      ## the HTML ``embed`` element
+    tagFieldset,   ## the HTML ``fieldset`` element
+    tagFigcaption, ## the HTML ``figcaption`` element
+    tagFigure,     ## the HTML ``figure`` element
+    tagFont,       ## the deprecated HTML ``font`` element
+    tagFooter,     ## the HTML ``footer`` element
+    tagForm,       ## the HTML ``form`` element
+    tagFrame,      ## the HTML ``frame`` element
+    tagFrameset,   ## the deprecated HTML ``frameset`` element
+    tagH1,         ## the HTML ``h1`` element
+    tagH2,         ## the HTML ``h2`` element
+    tagH3,         ## the HTML ``h3`` element
+    tagH4,         ## the HTML ``h4`` element
+    tagH5,         ## the HTML ``h5`` element
+    tagH6,         ## the HTML ``h6`` element
+    tagHead,       ## the HTML ``head`` element
+    tagHeader,     ## the HTML ``header`` element
+    tagHgroup,     ## the HTML ``hgroup`` element
+    tagHtml,       ## the HTML ``html`` element
+    tagHr,         ## the HTML ``hr`` element
+    tagI,          ## the HTML ``i`` element
+    tagIframe,     ## the deprecated HTML ``iframe`` element
+    tagImg,        ## the HTML ``img`` element
+    tagInput,      ## the HTML ``input`` element
+    tagIns,        ## the HTML ``ins`` element
+    tagIsindex,    ## the deprecated HTML ``isindex`` element
+    tagKbd,        ## the HTML ``kbd`` element
+    tagKeygen,     ## the HTML ``keygen`` element
+    tagLabel,      ## the HTML ``label`` element
+    tagLegend,     ## the HTML ``legend`` element
+    tagLi,         ## the HTML ``li`` element
+    tagLink,       ## the HTML ``link`` element
+    tagMap,        ## the HTML ``map`` element
+    tagMark,       ## the HTML ``mark`` element
+    tagMenu,       ## the deprecated HTML ``menu`` element
+    tagMeta,       ## the HTML ``meta`` element
+    tagMeter,      ## the HTML ``meter`` element
+    tagNav,        ## the HTML ``nav`` element
+    tagNobr,       ## the deprecated HTML ``nobr`` element
+    tagNoframes,   ## the deprecated HTML ``noframes`` element
+    tagNoscript,   ## the HTML ``noscript`` element
+    tagObject,     ## the HTML ``object`` element
+    tagOl,         ## the HTML ``ol`` element
+    tagOptgroup,   ## the HTML ``optgroup`` element
+    tagOption,     ## the HTML ``option`` element
+    tagOutput,     ## the HTML ``output`` element
+    tagP,          ## the HTML ``p`` element
+    tagParam,      ## the HTML ``param`` element
+    tagPre,        ## the HTML ``pre`` element
+    tagProgress,   ## the HTML ``progress`` element
+    tagQ,          ## the HTML ``q`` element
+    tagRp,         ## the HTML ``rp`` element
+    tagRt,         ## the HTML ``rt`` element
+    tagRuby,       ## the HTML ``ruby`` element
+    tagS,          ## the deprecated HTML ``s`` element
+    tagSamp,       ## the HTML ``samp`` element
+    tagScript,     ## the HTML ``script`` element
+    tagSection,    ## the HTML ``section`` element
+    tagSelect,     ## the HTML ``select`` element
+    tagSmall,      ## the HTML ``small`` element
+    tagSource,     ## the HTML ``source`` element
+    tagSpan,       ## the HTML ``span`` element
+    tagStrike,     ## the deprecated HTML ``strike`` element
+    tagStrong,     ## the HTML ``strong`` element
+    tagStyle,      ## the HTML ``style`` element
+    tagSub,        ## the HTML ``sub`` element
+    tagSummary,    ## the HTML ``summary`` element
+    tagSup,        ## the HTML ``sup`` element
+    tagTable,      ## the HTML ``table`` element
+    tagTbody,      ## the HTML ``tbody`` element
+    tagTd,         ## the HTML ``td`` element
+    tagTextarea,   ## the HTML ``textarea`` element
+    tagTfoot,      ## the HTML ``tfoot`` element
+    tagTh,         ## the HTML ``th`` element
+    tagThead,      ## the HTML ``thead`` element
+    tagTime,       ## the HTML ``time`` element
+    tagTitle,      ## the HTML ``title`` element
+    tagTr,         ## the HTML ``tr`` element
+    tagTrack,      ## the HTML ``track`` element
+    tagTt,         ## the HTML ``tt`` element
+    tagU,          ## the deprecated HTML ``u`` element
+    tagUl,         ## the HTML ``ul`` element
+    tagVar,        ## the HTML ``var`` element
+    tagVideo,      ## the HTML ``video`` element
+    tagWbr         ## the HTML ``wbr`` element
+
+
+# bug #11469
+const lookup: array[32, uint8] = [0'u8, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 16, 17,
+    25, 17, 4, 8, 31, 27, 13, 23]
+
+veryLongVariableName.createVar("future" & $node[1][0].toStrLit, node[1], futureValue1,
+    futureValue2, node)
+
+veryLongVariableName.createVar("future" & $node[1][0].toStrLit, node[1], futureValue1,
+                               futureValue2, node)
+
+type
+  CmdLineKind* = enum         ## The detected command line token.
+    cmdEnd,                   ## End of command line reached
+    cmdArgument,              ## An argument such as a filename
+    cmdLongOption,            ## A long option such as --option
+    cmdShortOption            ## A short option such as -c
+  OptParser* = object of RootObj ## \
+    ## Implementation of the command line parser. Here is even more text yad.
+    ##
+    ## To initialize it, use the
+    ## `initOptParser proc<#initOptParser,string,set[char],seq[string]>`_.
+    pos*: int
+    inShortState: bool
+    allowWhitespaceAfterColon: bool
+    shortNoVal: set[char]
+    longNoVal: seq[string]
+    cmds: seq[string]
+    idx: int
+    kind*: CmdLineKind        ## The detected command line token
+    key*, val*: TaintedString ## Key and value pair; the key is the option
+                              ## or the argument, and the value is not "" if
+                              ## the option was given a value
+
+  OptParserDifferently* = object of RootObj ## Implementation of the command line parser.
+    ##
+    ## To initialize it, use the
+    ## `initOptParser proc<#initOptParser,string,set[char],seq[string]>`_.
+    pos*: int
+    inShortState: bool
+    allowWhitespaceAfterColon: bool
+    shortNoVal: set[char]
+    longNoVal: seq[string]
+    cmds: seq[string]
+    idx: int
+    kind*: CmdLineKind        ## The detected command line token
+    key*, val*: TaintedString ## Key and value pair; the key is the option
+                              ## or the argument, and the value is not "" if
+                              ## the option was given a value
+
+block:
+  var t = 3
+
+## This MUST be a multiline comment,
+## single line comment would be ok.
+block:
+  var x = 7
+
+
+block:
+  var t = 3
+  ## another
+  ## multi
+
+## This MUST be a multiline comment,
+## single line comment would be ok.
+block:
+  var x = 7
+
+
+proc newRecordGen(ctx: Context; typ: TypRef): PNode =
+  result = nkTypeDef.t(
+    newId(typ.optSym.name, true, pragmas = [id(if typ.isUnion: "cUnion" else: "cStruct")]),
+    empty(),
+    nkObjectTy.t(
+      empty(),
+      empty(),
+      nkRecList.t(
+        typ.recFields.map(newRecFieldGen))))
+
+
+##[
+String `interpolation`:idx: / `format`:idx: inspired by
+Python's ``f``-strings.
+
+  ```nim
+  import strformat
+  let msg = "hello"
+  doAssert fmt"{msg}\n" == "hello\\n"
+  ```
+
+Because the literal is a raw string literal, the ``\n`` is not interpreted as
+an escape sequence.
+
+
+=================        ====================================================
+  Sign                   Meaning
+=================        ====================================================
+``+``                    Indicates that a sign should be used for both
+                         positive as well as negative numbers.
+``-``                    Indicates that a sign should be used only for
+                         negative numbers (this is the default behavior).
+(space)                  Indicates that a leading space should be used on
+                         positive numbers.
+=================        ====================================================
+
+]##
+
+
+let
+  lla = 42394219 - 42429849 + 1293293 - 13918391 + 424242 # this here is an okayish comment
+  llb = 42394219 - 42429849 + 1293293 - 13918391 + 424242 # this here is a very long comment which should be split
+  llc = 42394219 - 42429849 + 1293293 - 13918391 + 424242 - 3429424 + 4239489 - 42399
+  lld = 42394219 - 42429849 + 1293293 - 13918391 + 424242 - 342949924 + 423948999 - 42399
+
+type
+  MyLongEnum = enum ## doc comment here
+    first, ## this is a long comment here, but please align it
+    secondWithAVeryLongNameMightBreak, ## this is a short one
+    thirdOne ## it's ok
+
+if true: # just one space before comment
+  echo 7
+
+# colors.nim:18
+proc `==` *(a, b: Color): bool
+  ## Compares two colors.
+  ##
+
+# colors.nim:18
+proc `==` *(a, b: Color): bool {.borrow.}
+  ## Compares two colors.
+  ##
+
+
+var rows1 = await pool.rows(sql"""
+    SELECT STUFF
+    WHERE fffffffffffffffffffffffffffffff
+  """,
+  @[
+    "AAAA",
+    "BBBB"
+  ]
+)
+
+var rows2 = await pool.rows(sql"""
+    SELECT STUFF
+    WHERE fffffffffffffffffffffffffffffffgggggggggggggggggggggggggghhhhhhhhhhhhhhhheeeeeeiiiijklm""",
+  @[
+    "AAAA",
+    "BBBB"
+  ]
+)
+
+
+# bug #11699
+
+const keywords = @[
+  "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar",
+  "zzz", "ggg", "ddd",
+]
+
+let keywords1 = @[
+  "foo1", "bar1", "foo2", "bar2", "foo3", "bar3", "foo4", "bar4", "foo5", "bar5", "foo6", "bar6", "foo7",
+  "zzz", "ggg", "ddd",
+]
+
+let keywords2 = @[
+  "foo1", "bar1", "foo2", "bar2", "foo3", "bar3", "foo4", "bar4", "foo5", "bar5", "foo6", "bar6", "foo7",
+  "foo1", "bar1", "foo2", "bar2", "foo3", "bar3", "foo4", "bar4", "foo5", "bar5", "foo6", "bar6", "foo7",
+  "zzz", "ggg", "ddd",
+]
+
+if true:
+  let keywords3 = @[
+    "foo1", "bar1", "foo2", "bar2", "foo3", "bar3", "foo4", "bar4", "foo5", "bar5", "foo6", "bar6", "foo7",
+    "zzz", "ggg", "ddd",
+  ]
+
+const b = true
+let fooB =
+  if true:
+    if b: 7 else: 8
+  else: ord(b)
+
+let foo = if cond:
+            if b: T else: F
+          else: b
+
+let a =
+  [[aaadsfas, bbb],
+   [ccc, ddd]]
+
+let b = [
+  [aaa, bbb],
+  [ccc, ddd]
+]
+
+# bug #11616
+proc newRecordGen(ctx: Context; typ: TypRef): PNode =
+  result = nkTypeDef.t(
+    newId(typ.optSym.name, true, pragmas = [id(if typ.isUnion: "cUnion"
+                                               else: "cStruct")]),
+    empty(),
+    nkObjectTy.t(
+      empty(),
+      empty(),
+      nkRecList.t(
+        typ.recFields.map(newRecFieldGen))))
+
+proc f =
+  # doesn't break the code, but leaving indentation as is would be nice.
+  let x = if true: callingProcWhatever()
+          else: callingADifferentProc()
+
+
+type
+  EventKind = enum
+    Stop, StopSuccess, StopError,
+    SymbolChange, TextChange,
+
+  SpinnyEvent = tuple
+    kind: EventKind
+    payload: string
+
+
+type
+  EventKind2 = enum
+    Stop2, StopSuccess2, StopError2,
+    SymbolChange2, TextChange2,
+
+type
+  SpinnyEvent2 = tuple
+    kind: EventKind
+    payload: string
+
+
+proc hid_open*(vendor_id: cushort; product_id: cushort; serial_number: cstring): ptr HidDevice {.
+    importc: "hid_open", dynlib: hidapi.}
diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim
index 95071fce3..7f78b7e56 100644
--- a/nimpretty/tests/expected/exhaustive.nim
+++ b/nimpretty/tests/expected/exhaustive.nim
@@ -1,10 +1,10 @@
 discard """
-  outputsub: '''ObjectAssignmentError'''
+  outputsub: '''ObjectAssignmentDefect'''
   exitcode: "1"
 """
 
 import verylongnamehere, verylongnamehere,
-  verylongnamehereverylongnamehereverylong, namehere, verylongnamehere
+    verylongnamehereverylongnamehereverylong, namehere, verylongnamehere
 
 proc `[]=`() = discard "index setter"
 proc `putter=`() = discard cast[pointer](cast[int](buffer) + size)
@@ -19,8 +19,7 @@ var body = newNimNode(nnkIfExpr).add(
         ident("kind"))),
     condition
   ),
-  newNimNode(nnkElse).add(newStmtList(newNimNode(nnkReturnStmt).add(ident(
-      "false"))))
+  newNimNode(nnkElse).add(newStmtList(newNimNode(nnkReturnStmt).add(ident("false"))))
 )
 
 # comment
@@ -29,10 +28,10 @@ var x = 1
 
 type
   GeneralTokenizer* = object of RootObj ## comment here
-    kind*: TokenClass         ## and here
-    start*, length*: int      ## you know how it goes...
+    kind*: TokenClass                   ## and here
+    start*, length*: int                ## you know how it goes...
     buf: cstring
-    pos: int                  # other comment here.
+    pos: int                            # other comment here.
     state: TokenClass
 
 var x*: string
@@ -122,7 +121,7 @@ type
     inquote {.pragmaHereWrongCurlyEnd.}: bool
     col, lastLineNumber, lineSpan, indentLevel: int
     content: string
-    fixedUntil: int           # marks where we must not go in the content
+    fixedUntil: int                    # marks where we must not go in the content
     altSplitPos: array[SplitKind, int] # alternative split positions
 
 proc openEmitter*[T, S](em: var Emitter; config: ConfigRef;
@@ -200,8 +199,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
     if em.lineSpan > 0: calcCol(em, lit)
     if not endsInWhite(em):
       wr(" ")
-      if em.lineSpan == 0 and max(em.col,
-          LineCommentColumn) + lit.len <= MaxLineLen:
+      if em.lineSpan == 0 and max(em.col, LineCommentColumn) + lit.len <= MaxLineLen:
         for i in 1 .. LineCommentColumn - em.col: wr(" ")
     wr lit
 
@@ -274,7 +272,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
     if not em.endsInWhite: wr(" ")
     wr(tok.ident.s)
     template isUnary(tok): bool =
-      tok.strongSpaceB == 0 and tok.strongSpaceA > 0
+      tok.spacing == {tsLeading}
 
     if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}:
       wr(" ")
@@ -285,12 +283,10 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
   of tkComment:
     if not preventComment:
       emitComment(em, tok)
-  of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit,
-      tkGTripleStrLit, tkCharLit:
+  of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, tkGTripleStrLit, tkCharLit:
     let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB)
     softLinebreak(em, lit)
-    if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wr(
-        " ")
+    if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wr(" ")
     em.lineSpan = countNewlines(lit)
     if em.lineSpan > 0: calcCol(em, lit)
     wr lit
@@ -323,3 +319,545 @@ proc f() =
   if c == '\\':
     # escape char
     str &= c
+
+proc getKeyAndData(cursor: int; op: int):
+    tuple[key, data: string; success: bool] {.noInit.} =
+  var keyVal: string
+  var dataVal: string
+
+#!nimpretty off
+  when stuff:
+    echo "so nice"
+    echo "more"
+  else:
+     echo "misaligned"
+#!nimpretty on
+
+const test = r"C:\Users\-\Desktop\test.txt"
+
+proc abcdef*[T: not (tuple|object|string|cstring|char|ref|ptr|array|seq|distinct)]() =
+  # bug #9504
+  type T2 = a.type
+  discard
+
+proc fun() =
+  #[
+  this one here
+  ]#
+  discard
+
+proc fun2() =
+  ##[
+  foobar
+  ]##
+  discard
+
+#[
+foobar
+]#
+
+proc fun3() =
+  discard
+
+##[
+foobar
+]##
+
+# bug #9673
+discard `*`(1, 2)
+
+proc fun4() =
+  var a = "asdf"
+  var i = 0
+  while i < a.len and i < a.len:
+    return
+
+
+# bug #10295
+
+import osproc
+let res = execProcess(
+    "echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates")
+
+let res = execProcess("echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates")
+
+
+# bug #10177
+
+proc foo*() =
+  discard
+
+proc foo*[T]() =
+  discard
+
+
+# bug #10159
+
+proc fun() =
+  discard
+
+proc main() =
+  echo "foo"; echo "bar";
+  discard
+
+main()
+
+type
+  TCallingConvention* = enum
+    ccDefault,     # proc has no explicit calling convention
+    ccStdCall,     # procedure is stdcall
+    ccCDecl,       # cdecl
+    ccSafeCall,    # safecall
+    ccSysCall,     # system call
+    ccInline,      # proc should be inlined
+    ccNoInline,    # proc should not be inlined
+    ccFastCall,    # fastcall (pass parameters in registers)
+    ccClosure,     # proc has a closure
+    ccNoConvention # needed for generating proper C procs sometimes
+
+
+proc isValid1*[A](s: HashSet[A]): bool {.deprecated:
+    "Deprecated since v0.20; sets are initialized by default".} =
+  ## Returns `true` if the set has been initialized (with `initHashSet proc
+  ## <#initHashSet,int>`_ or `init proc <#init,HashSet[A],int>`_).
+  result = s.data.len > 0
+  # bug #11468
+
+assert $typeof(a) == "Option[system.int]"
+foo(a, $typeof(b), c)
+foo(typeof(b), c) # this is ok
+
+proc `<`*[A](s, t: A): bool = discard
+proc `==`*[A](s, t: HashSet[A]): bool = discard
+proc `<=`*[A](s, t: HashSet[A]): bool = discard
+
+# these are ok:
+proc `$`*[A](s: HashSet[A]): string = discard
+proc `*`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} = discard
+proc `-+-`*[A](s1, s2: HashSet[A]): HashSet[A] {.inline.} = discard
+
+# bug #11470
+
+
+# bug #11467
+
+type
+  FirstEnum = enum ## doc comment here
+    first,         ## this is first
+    second,        ## second doc
+    third,         ## third one
+    fourth         ## the last one
+
+
+type
+  SecondEnum = enum ## doc comment here
+    first,          ## this is first
+    second,         ## second doc
+    third,          ## third one
+    fourth,         ## the last one
+
+
+type
+  ThirdEnum = enum ## doc comment here
+    first          ## this is first
+    second         ## second doc
+    third          ## third one
+    fourth         ## the last one
+
+
+type
+  HttpMethod* = enum ## the requested HttpMethod
+    HttpHead,        ## Asks for the response identical to the one that would
+                     ## correspond to a GET request, but without the response
+                     ## body.
+    HttpGet,         ## Retrieves the specified resource.
+    HttpPost,        ## Submits data to be processed to the identified
+                     ## resource. The data is included in the body of the
+                     ## request.
+    HttpPut,         ## Uploads a representation of the specified resource.
+    HttpDelete,      ## Deletes the specified resource.
+    HttpTrace,       ## Echoes back the received request, so that a client
+                     ## can see what intermediate servers are adding or
+                     ## changing in the request.
+    HttpOptions,     ## Returns the HTTP methods that the server supports
+                     ## for specified address.
+    HttpConnect,     ## Converts the request connection to a transparent
+                     ## TCP/IP tunnel, usually used for proxies.
+    HttpPatch        ## Applies partial modifications to a resource.
+
+type
+  HtmlTag* = enum  ## list of all supported HTML tags; order will always be
+                   ## alphabetically
+    tagUnknown,    ## unknown HTML element
+    tagA,          ## the HTML ``a`` element
+    tagAbbr,       ## the deprecated HTML ``abbr`` element
+    tagAcronym,    ## the HTML ``acronym`` element
+    tagAddress,    ## the HTML ``address`` element
+    tagApplet,     ## the deprecated HTML ``applet`` element
+    tagArea,       ## the HTML ``area`` element
+    tagArticle,    ## the HTML ``article`` element
+    tagAside,      ## the HTML ``aside`` element
+    tagAudio,      ## the HTML ``audio`` element
+    tagB,          ## the HTML ``b`` element
+    tagBase,       ## the HTML ``base`` element
+    tagBdi,        ## the HTML ``bdi`` element
+    tagBdo,        ## the deprecated HTML ``dbo`` element
+    tagBasefont,   ## the deprecated HTML ``basefont`` element
+    tagBig,        ## the HTML ``big`` element
+    tagBlockquote, ## the HTML ``blockquote`` element
+    tagBody,       ## the HTML ``body`` element
+    tagBr,         ## the HTML ``br`` element
+    tagButton,     ## the HTML ``button`` element
+    tagCanvas,     ## the HTML ``canvas`` element
+    tagCaption,    ## the HTML ``caption`` element
+    tagCenter,     ## the deprecated HTML ``center`` element
+    tagCite,       ## the HTML ``cite`` element
+    tagCode,       ## the HTML ``code`` element
+    tagCol,        ## the HTML ``col`` element
+    tagColgroup,   ## the HTML ``colgroup`` element
+    tagCommand,    ## the HTML ``command`` element
+    tagDatalist,   ## the HTML ``datalist`` element
+    tagDd,         ## the HTML ``dd`` element
+    tagDel,        ## the HTML ``del`` element
+    tagDetails,    ## the HTML ``details`` element
+    tagDfn,        ## the HTML ``dfn`` element
+    tagDialog,     ## the HTML ``dialog`` element
+    tagDiv,        ## the HTML ``div`` element
+    tagDir,        ## the deprecated HTLM ``dir`` element
+    tagDl,         ## the HTML ``dl`` element
+    tagDt,         ## the HTML ``dt`` element
+    tagEm,         ## the HTML ``em`` element
+    tagEmbed,      ## the HTML ``embed`` element
+    tagFieldset,   ## the HTML ``fieldset`` element
+    tagFigcaption, ## the HTML ``figcaption`` element
+    tagFigure,     ## the HTML ``figure`` element
+    tagFont,       ## the deprecated HTML ``font`` element
+    tagFooter,     ## the HTML ``footer`` element
+    tagForm,       ## the HTML ``form`` element
+    tagFrame,      ## the HTML ``frame`` element
+    tagFrameset,   ## the deprecated HTML ``frameset`` element
+    tagH1,         ## the HTML ``h1`` element
+    tagH2,         ## the HTML ``h2`` element
+    tagH3,         ## the HTML ``h3`` element
+    tagH4,         ## the HTML ``h4`` element
+    tagH5,         ## the HTML ``h5`` element
+    tagH6,         ## the HTML ``h6`` element
+    tagHead,       ## the HTML ``head`` element
+    tagHeader,     ## the HTML ``header`` element
+    tagHgroup,     ## the HTML ``hgroup`` element
+    tagHtml,       ## the HTML ``html`` element
+    tagHr,         ## the HTML ``hr`` element
+    tagI,          ## the HTML ``i`` element
+    tagIframe,     ## the deprecated HTML ``iframe`` element
+    tagImg,        ## the HTML ``img`` element
+    tagInput,      ## the HTML ``input`` element
+    tagIns,        ## the HTML ``ins`` element
+    tagIsindex,    ## the deprecated HTML ``isindex`` element
+    tagKbd,        ## the HTML ``kbd`` element
+    tagKeygen,     ## the HTML ``keygen`` element
+    tagLabel,      ## the HTML ``label`` element
+    tagLegend,     ## the HTML ``legend`` element
+    tagLi,         ## the HTML ``li`` element
+    tagLink,       ## the HTML ``link`` element
+    tagMap,        ## the HTML ``map`` element
+    tagMark,       ## the HTML ``mark`` element
+    tagMenu,       ## the deprecated HTML ``menu`` element
+    tagMeta,       ## the HTML ``meta`` element
+    tagMeter,      ## the HTML ``meter`` element
+    tagNav,        ## the HTML ``nav`` element
+    tagNobr,       ## the deprecated HTML ``nobr`` element
+    tagNoframes,   ## the deprecated HTML ``noframes`` element
+    tagNoscript,   ## the HTML ``noscript`` element
+    tagObject,     ## the HTML ``object`` element
+    tagOl,         ## the HTML ``ol`` element
+    tagOptgroup,   ## the HTML ``optgroup`` element
+    tagOption,     ## the HTML ``option`` element
+    tagOutput,     ## the HTML ``output`` element
+    tagP,          ## the HTML ``p`` element
+    tagParam,      ## the HTML ``param`` element
+    tagPre,        ## the HTML ``pre`` element
+    tagProgress,   ## the HTML ``progress`` element
+    tagQ,          ## the HTML ``q`` element
+    tagRp,         ## the HTML ``rp`` element
+    tagRt,         ## the HTML ``rt`` element
+    tagRuby,       ## the HTML ``ruby`` element
+    tagS,          ## the deprecated HTML ``s`` element
+    tagSamp,       ## the HTML ``samp`` element
+    tagScript,     ## the HTML ``script`` element
+    tagSection,    ## the HTML ``section`` element
+    tagSelect,     ## the HTML ``select`` element
+    tagSmall,      ## the HTML ``small`` element
+    tagSource,     ## the HTML ``source`` element
+    tagSpan,       ## the HTML ``span`` element
+    tagStrike,     ## the deprecated HTML ``strike`` element
+    tagStrong,     ## the HTML ``strong`` element
+    tagStyle,      ## the HTML ``style`` element
+    tagSub,        ## the HTML ``sub`` element
+    tagSummary,    ## the HTML ``summary`` element
+    tagSup,        ## the HTML ``sup`` element
+    tagTable,      ## the HTML ``table`` element
+    tagTbody,      ## the HTML ``tbody`` element
+    tagTd,         ## the HTML ``td`` element
+    tagTextarea,   ## the HTML ``textarea`` element
+    tagTfoot,      ## the HTML ``tfoot`` element
+    tagTh,         ## the HTML ``th`` element
+    tagThead,      ## the HTML ``thead`` element
+    tagTime,       ## the HTML ``time`` element
+    tagTitle,      ## the HTML ``title`` element
+    tagTr,         ## the HTML ``tr`` element
+    tagTrack,      ## the HTML ``track`` element
+    tagTt,         ## the HTML ``tt`` element
+    tagU,          ## the deprecated HTML ``u`` element
+    tagUl,         ## the HTML ``ul`` element
+    tagVar,        ## the HTML ``var`` element
+    tagVideo,      ## the HTML ``video`` element
+    tagWbr         ## the HTML ``wbr`` element
+
+
+# bug #11469
+const lookup: array[32, uint8] = [0'u8, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15,
+    16, 17, 25, 17, 4, 8, 31, 27, 13, 23]
+
+veryLongVariableName.createVar("future" & $node[1][0].toStrLit, node[1],
+    futureValue1, futureValue2, node)
+
+veryLongVariableName.createVar("future" & $node[1][0].toStrLit, node[1], futureValue1,
+                               futureValue2, node)
+
+type
+  CmdLineKind* = enum ## The detected command line token.
+    cmdEnd,           ## End of command line reached
+    cmdArgument,      ## An argument such as a filename
+    cmdLongOption,    ## A long option such as --option
+    cmdShortOption    ## A short option such as -c
+  OptParser* = object of RootObj ## \
+    ## Implementation of the command line parser. Here is even more text yad.
+    ##
+    ## To initialize it, use the
+    ## `initOptParser proc<#initOptParser,string,set[char],seq[string]>`_.
+    pos*: int
+    inShortState: bool
+    allowWhitespaceAfterColon: bool
+    shortNoVal: set[char]
+    longNoVal: seq[string]
+    cmds: seq[string]
+    idx: int
+    kind*: CmdLineKind           ## The detected command line token
+    key*, val*: TaintedString    ## Key and value pair; the key is the option
+                                 ## or the argument, and the value is not "" if
+                                 ## the option was given a value
+
+  OptParserDifferently* = object of RootObj ## Implementation of the command line parser.
+                              ##
+                              ## To initialize it, use the
+                              ## `initOptParser proc<#initOptParser,string,set[char],seq[string]>`_.
+    pos*: int
+    inShortState: bool
+    allowWhitespaceAfterColon: bool
+    shortNoVal: set[char]
+    longNoVal: seq[string]
+    cmds: seq[string]
+    idx: int
+    kind*: CmdLineKind        ## The detected command line token
+    key*, val*: TaintedString ## Key and value pair; the key is the option
+                              ## or the argument, and the value is not "" if
+                              ## the option was given a value
+
+block:
+  var t = 3
+
+## This MUST be a multiline comment,
+## single line comment would be ok.
+block:
+  var x = 7
+
+
+block:
+  var t = 3
+  ## another
+  ## multi
+
+## This MUST be a multiline comment,
+## single line comment would be ok.
+block:
+  var x = 7
+
+
+proc newRecordGen(ctx: Context; typ: TypRef): PNode =
+  result = nkTypeDef.t(
+    newId(typ.optSym.name, true, pragmas = [id(
+        if typ.isUnion: "cUnion" else: "cStruct")]),
+    empty(),
+    nkObjectTy.t(
+      empty(),
+      empty(),
+      nkRecList.t(
+        typ.recFields.map(newRecFieldGen))))
+
+
+##[
+String `interpolation`:idx: / `format`:idx: inspired by
+Python's ``f``-strings.
+
+  ```nim
+  import strformat
+  let msg = "hello"
+  doAssert fmt"{msg}\n" == "hello\\n"
+  ```
+
+Because the literal is a raw string literal, the ``\n`` is not interpreted as
+an escape sequence.
+
+
+=================        ====================================================
+  Sign                   Meaning
+=================        ====================================================
+``+``                    Indicates that a sign should be used for both
+                         positive as well as negative numbers.
+``-``                    Indicates that a sign should be used only for
+                         negative numbers (this is the default behavior).
+(space)                  Indicates that a leading space should be used on
+                         positive numbers.
+=================        ====================================================
+
+]##
+
+
+let
+  lla = 42394219 - 42429849 + 1293293 - 13918391 + 424242 # this here is an okayish comment
+  llb = 42394219 - 42429849 + 1293293 - 13918391 +
+      424242 # this here is a very long comment which should be split
+  llc = 42394219 - 42429849 + 1293293 - 13918391 + 424242 - 3429424 + 4239489 - 42399
+  lld = 42394219 - 42429849 + 1293293 - 13918391 + 424242 - 342949924 +
+      423948999 - 42399
+
+type
+  MyLongEnum = enum                    ## doc comment here
+    first,                             ## this is a long comment here, but please align it
+    secondWithAVeryLongNameMightBreak, ## this is a short one
+    thirdOne                           ## it's ok
+
+if true: # just one space before comment
+  echo 7
+
+# colors.nim:18
+proc `==` *(a, b: Color): bool
+  ## Compares two colors.
+  ##
+
+# colors.nim:18
+proc `==` *(a, b: Color): bool {.borrow.}
+  ## Compares two colors.
+  ##
+
+
+var rows1 = await pool.rows(sql"""
+    SELECT STUFF
+    WHERE fffffffffffffffffffffffffffffff
+  """,
+  @[
+    "AAAA",
+    "BBBB"
+  ]
+)
+
+var rows2 = await pool.rows(sql"""
+    SELECT STUFF
+    WHERE fffffffffffffffffffffffffffffffgggggggggggggggggggggggggghhhhhhhhhhhhhhhheeeeeeiiiijklm""",
+  @[
+    "AAAA",
+    "BBBB"
+  ]
+)
+
+
+# bug #11699
+
+const keywords = @[
+  "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar", "foo",
+  "bar", "foo", "bar",
+  "zzz", "ggg", "ddd",
+]
+
+let keywords1 = @[
+  "foo1", "bar1", "foo2", "bar2", "foo3", "bar3", "foo4", "bar4", "foo5",
+  "bar5", "foo6", "bar6", "foo7",
+  "zzz", "ggg", "ddd",
+]
+
+let keywords2 = @[
+  "foo1", "bar1", "foo2", "bar2", "foo3", "bar3", "foo4", "bar4", "foo5",
+  "bar5", "foo6", "bar6", "foo7",
+  "foo1", "bar1", "foo2", "bar2", "foo3", "bar3", "foo4", "bar4", "foo5",
+  "bar5", "foo6", "bar6", "foo7",
+  "zzz", "ggg", "ddd",
+]
+
+if true:
+  let keywords3 = @[
+    "foo1", "bar1", "foo2", "bar2", "foo3", "bar3", "foo4", "bar4", "foo5",
+    "bar5", "foo6", "bar6", "foo7",
+    "zzz", "ggg", "ddd",
+  ]
+
+const b = true
+let fooB =
+  if true:
+    if b: 7 else: 8
+  else: ord(b)
+
+let foo = if cond:
+            if b: T else: F
+          else: b
+
+let a =
+  [[aaadsfas, bbb],
+   [ccc, ddd]]
+
+let b = [
+  [aaa, bbb],
+  [ccc, ddd]
+]
+
+# bug #11616
+proc newRecordGen(ctx: Context; typ: TypRef): PNode =
+  result = nkTypeDef.t(
+    newId(typ.optSym.name, true, pragmas = [id(if typ.isUnion: "cUnion"
+                                               else: "cStruct")]),
+    empty(),
+    nkObjectTy.t(
+      empty(),
+      empty(),
+      nkRecList.t(
+        typ.recFields.map(newRecFieldGen))))
+
+proc f =
+  # doesn't break the code, but leaving indentation as is would be nice.
+  let x = if true: callingProcWhatever()
+          else: callingADifferentProc()
+
+
+type
+  EventKind = enum
+    Stop, StopSuccess, StopError,
+    SymbolChange, TextChange,
+
+  SpinnyEvent = tuple
+    kind: EventKind
+    payload: string
+
+
+type
+  EventKind2 = enum
+    Stop2, StopSuccess2, StopError2,
+    SymbolChange2, TextChange2,
+
+type
+  SpinnyEvent2 = tuple
+    kind: EventKind
+    payload: string
+
+
+proc hid_open*(vendor_id: cushort; product_id: cushort;
+    serial_number: cstring): ptr HidDevice {.
+    importc: "hid_open", dynlib: hidapi.}
diff --git a/nimpretty/tests/expected/simple.nim b/nimpretty/tests/expected/simple.nim
new file mode 100644
index 000000000..e711eb3b6
--- /dev/null
+++ b/nimpretty/tests/expected/simple.nim
@@ -0,0 +1,29 @@
+
+var x: int = 2
+
+echo x
+# bug #9144
+
+proc a() =
+  while true:
+    discard
+    # comment 1
+
+  # comment 2
+  discard
+
+# bug #15596
+discard ## comment 3
+
+discard # comment 4
+
+
+# bug #20553
+
+let `'hello` = 12
+echo `'hello`
+
+
+proc `'u4`(n: string) =
+  # The leading ' is required.
+  discard
diff --git a/nimpretty/tests/expected/simple2.nim b/nimpretty/tests/expected/simple2.nim
new file mode 100644
index 000000000..909ac48c3
--- /dev/null
+++ b/nimpretty/tests/expected/simple2.nim
@@ -0,0 +1,22 @@
+# comment here
+var x: int = 2
+
+echo x
+
+proc fun*() =
+  echo "ok"
+  ## doc comment
+  # regular comment
+
+proc funB() =
+  echo "ok1"
+  # echo "ok2"
+
+fun()
+
+#[
+bug #9483
+]#
+
+proc funE() =
+  echo "ok1"
diff --git a/nimpretty/tests/expected/simple3.nim b/nimpretty/tests/expected/simple3.nim
new file mode 100644
index 000000000..c1c0af449
--- /dev/null
+++ b/nimpretty/tests/expected/simple3.nim
@@ -0,0 +1,15 @@
+
+#[
+foo bar
+]#
+
+
+
+proc fun() =
+  echo "ok1"
+
+proc fun2(a = "fooo" & "bar" & "bar" & "bar" & "bar" & ("bar" & "bar" & "bar") &
+    "bar" & "bar" & "bar" & "bar" & "bar" & "bar" & "bar" & "bar" & "bar"): auto =
+  discard
+
+fun2()
diff --git a/nimpretty/tests/expected/simple4.nim b/nimpretty/tests/expected/simple4.nim
new file mode 100644
index 000000000..62d106a9b
--- /dev/null
+++ b/nimpretty/tests/expected/simple4.nim
@@ -0,0 +1,2 @@
+proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
+  discard
diff --git a/nimpretty/tests/expected/tevil_spaces.nim b/nimpretty/tests/expected/tevil_spaces.nim
new file mode 100644
index 000000000..6bafa611e
--- /dev/null
+++ b/nimpretty/tests/expected/tevil_spaces.nim
@@ -0,0 +1,58 @@
+if true:
+  echo 7
+
+type
+  TCallingConvention* = enum # \
+    # asdfkljsdlf
+    #
+    ccDefault,               # proc has no explicit calling convention
+    ccStdCall,               # procedure is stdcall
+    ccCDecl,                 # cdecl
+    ccSafeCall,              # safecall
+    ccSysCall,               # system call
+    ccInline,                # proc should be inlined
+    ccNoInline,              # proc should not be inlined
+                             #
+                             # continuing here
+    ccFastCall,              # fastcall (pass parameters in registers)
+    ccClosure,               # proc has a closure
+    ccNoConvention           # needed for generating proper C procs sometimes
+
+# asyncmacro.nim:260
+# asfkjaflk jkldas
+proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
+  ## Doc comment here.
+  # Now an ordinary comment.
+  outerProcBody.add(
+    newVarStmt(retFutureSym,
+      newCall(
+        newNimNode(nnkBracketExpr, prc.body).add(
+          newIdentNode("newFuture"),
+          subRetType),
+      newLit(prcName)))) # Get type from return type of this proc
+
+  # -> iterator nameIter(): FutureBase {.closure.} =
+  # ->   {.push warning[resultshadowed]: off.}
+  # ->   var result: T
+  # ->   {.pop.}
+  # ->   <proc_body>
+  # ->   complete(retFuture, result)
+  var iteratorNameSym = genSym(nskIterator, $prcName & "Iter")
+  var procBody = prc.body.processBody(retFutureSym, subtypeIsVoid,
+                                    futureVarIdents)
+  if tue:
+    foo() # comment here
+  # end if
+
+proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
+  ## Splits and distributes a sequence `s` into `num` sub-sequences.
+  let num = int(num) # XXX probably only needed because of .. bug
+                     # This is part of the above.
+  result = newSeq[seq[T]](num)
+
+proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
+  ## Splits and distributes a sequence `s` into `num` sub-sequences.
+  let num = int(num) # XXX probably only needed because of .. bug
+
+  # This belongs below.
+  result = newSeq[seq[T]](num)
diff --git a/nimpretty/tests/expected/wrong_ind.nim b/nimpretty/tests/expected/wrong_ind.nim
new file mode 100644
index 000000000..3ff9de91c
--- /dev/null
+++ b/nimpretty/tests/expected/wrong_ind.nim
@@ -0,0 +1,111 @@
+
+# bug #9505
+
+import std/[
+    strutils, os
+]
+import pkg/[
+  regex
+]
+
+proc fun() =
+    let a = [
+      1,
+      2,
+    ]
+    discard
+
+proc funB() =
+    let a = [
+      1,
+      2,
+      3
+    ]
+    discard
+
+
+# bug #10156
+proc foo =
+    ## Comment 1
+    ## Comment 2
+    discard
+
+proc bar =
+    ## Comment 3
+    ## Comment 4
+    ## More here.
+    discard
+
+
+proc barB =
+    # Comment 5
+    # Comment 6
+    discard
+
+
+var x: int = 2
+
+echo x
+# bug #9144
+
+proc a() =
+    if cond:
+        while true:
+            discard
+            # comment 1
+        # end while
+    #end if
+
+    # comment 2
+        #if
+            #case
+            #end case
+        #end if
+    discard
+
+
+proc a() =
+    while true:
+        discard
+        # comment 1
+
+    # comment 2
+    discard
+
+
+proc i11937() =
+    result = %*
+        {
+            "_comment": "pbreports-style JSON",
+            "attributes": [],
+            "dataset_uuids": [],
+            "id": "microbial_asm_polishing_report",
+            "plotGroups": [],
+            "tables": [
+                {
+                "columns": [
+                    {
+                        "header": "Contig",
+                        "id": "microbial",
+                        "values": values_contig
+                    },
+                    {
+                        "header": "Length",
+                        "id": "microbial",
+                        "values": values_length
+                    },
+                    {
+                        "header": "Circular?",
+                        "id": "microbial",
+                        "values": values_circular
+                    }
+                ],
+                "id": "microbial_asm_polishing_report.contigs_table",
+                "title": "Polished contigs from Microbial Assembly"
+                },
+            ],
+            "tags": [],
+            "title": "Microbial Assembly Polishing Report",
+            "uuid": uuid,
+            "version": version
+        }
diff --git a/nimpretty/tests/simple.nim b/nimpretty/tests/simple.nim
new file mode 100644
index 000000000..2a01a176e
--- /dev/null
+++ b/nimpretty/tests/simple.nim
@@ -0,0 +1,29 @@
+
+var x: int = 2
+
+echo x
+# bug #9144
+
+proc a() =
+  while true:
+    discard
+    # comment 1
+
+  # comment 2
+  discard
+
+# bug #15596
+discard## comment 3
+
+discard # comment 4
+
+
+# bug #20553
+
+let `'hello` = 12
+echo `'hello`
+
+
+proc `'u4`(n: string) =
+  # The leading ' is required.
+  discard
diff --git a/nimpretty/tests/simple2.nim b/nimpretty/tests/simple2.nim
new file mode 100644
index 000000000..4ef2ddd70
--- /dev/null
+++ b/nimpretty/tests/simple2.nim
@@ -0,0 +1,22 @@
+# comment here
+var x: int = 2
+
+echo x
+
+proc fun*()=
+  echo "ok"
+  ## doc comment
+  # regular comment
+
+proc funB() =
+  echo "ok1"
+  # echo "ok2"
+
+fun()
+
+#[
+bug #9483
+]#
+
+proc funE() =
+  echo "ok1"
diff --git a/nimpretty/tests/simple3.nim b/nimpretty/tests/simple3.nim
new file mode 100644
index 000000000..bd60ddf39
--- /dev/null
+++ b/nimpretty/tests/simple3.nim
@@ -0,0 +1,14 @@
+
+#[
+foo bar
+]#
+
+
+
+proc fun() =
+  echo "ok1"
+
+proc fun2(a = "fooo" & "bar" & "bar" & "bar" & "bar" & ("bar" & "bar" & "bar") & "bar" & "bar" & "bar" & "bar" & "bar" & "bar" & "bar" & "bar" & "bar"): auto =
+  discard
+
+fun2()
diff --git a/nimpretty/tests/simple4.nim b/nimpretty/tests/simple4.nim
new file mode 100644
index 000000000..62d106a9b
--- /dev/null
+++ b/nimpretty/tests/simple4.nim
@@ -0,0 +1,2 @@
+proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
+  discard
diff --git a/nimpretty/tests/tevil_spaces.nim b/nimpretty/tests/tevil_spaces.nim
new file mode 100644
index 000000000..20d10a801
--- /dev/null
+++ b/nimpretty/tests/tevil_spaces.nim
@@ -0,0 +1,58 @@
+if true:
+  echo 7
+
+type
+  TCallingConvention* = enum  # \
+    # asdfkljsdlf
+    #
+    ccDefault,                # proc has no explicit calling convention
+    ccStdCall,    # procedure is stdcall
+    ccCDecl,                  # cdecl
+    ccSafeCall,               # safecall
+    ccSysCall, # system call
+    ccInline,                 # proc should be inlined
+    ccNoInline,               # proc should not be inlined
+                              #
+                              # continuing here
+    ccFastCall,               # fastcall (pass parameters in registers)
+    ccClosure,        # proc has a closure
+    ccNoConvention       # needed for generating proper C procs sometimes
+
+# asyncmacro.nim:260
+# asfkjaflk jkldas
+proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
+  ## Doc comment here.
+  # Now an ordinary comment.
+  outerProcBody.add(
+    newVarStmt(retFutureSym,
+      newCall(
+        newNimNode(nnkBracketExpr, prc.body).add(
+          newIdentNode("newFuture"),
+          subRetType),
+      newLit(prcName)))) # Get type from return type of this proc
+
+  # -> iterator nameIter(): FutureBase {.closure.} =
+  # ->   {.push warning[resultshadowed]: off.}
+  # ->   var result: T
+  # ->   {.pop.}
+  # ->   <proc_body>
+  # ->   complete(retFuture, result)
+  var iteratorNameSym = genSym(nskIterator, $prcName & "Iter")
+  var procBody = prc.body.processBody(retFutureSym, subtypeIsVoid,
+                                    futureVarIdents)
+  if tue:
+      foo() # comment here
+  # end if
+
+proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
+  ## Splits and distributes a sequence `s` into `num` sub-sequences.
+  let num = int(num) # XXX probably only needed because of .. bug
+  # This is part of the above.
+  result = newSeq[seq[T]](num)
+
+proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
+  ## Splits and distributes a sequence `s` into `num` sub-sequences.
+  let num = int(num) # XXX probably only needed because of .. bug
+
+  # This belongs below.
+  result = newSeq[seq[T]](num)
diff --git a/nimpretty/tests/wrong_ind.nim b/nimpretty/tests/wrong_ind.nim
new file mode 100644
index 000000000..a042e8822
--- /dev/null
+++ b/nimpretty/tests/wrong_ind.nim
@@ -0,0 +1,111 @@
+
+# bug #9505
+
+import std/[
+    strutils, os
+]
+import pkg/[
+  regex
+]
+
+proc fun() =
+    let a = [
+      1,
+      2,
+    ]
+    discard
+
+proc funB() =
+  let a = [
+    1,
+    2,
+    3
+  ]
+  discard
+
+
+# bug #10156
+proc foo =
+    ## Comment 1
+    ## Comment 2
+    discard
+
+proc bar =
+  ## Comment 3
+  ## Comment 4
+  ## More here.
+  discard
+
+
+proc barB =
+  # Comment 5
+  # Comment 6
+  discard
+
+
+var x: int = 2
+
+echo x
+# bug #9144
+
+proc a() =
+  if cond:
+    while true:
+      discard
+      # comment 1
+    # end while
+  #end if
+
+  # comment 2
+    #if
+      #case
+      #end case
+    #end if
+  discard
+
+
+proc a() =
+  while true:
+    discard
+    # comment 1
+
+  # comment 2
+  discard
+
+
+proc i11937() =
+    result = %*
+        {
+            "_comment": "pbreports-style JSON",
+            "attributes": [],
+            "dataset_uuids": [],
+            "id": "microbial_asm_polishing_report",
+            "plotGroups": [],
+            "tables": [
+                {
+                "columns": [
+                    {
+                        "header": "Contig",
+                        "id": "microbial",
+                        "values": values_contig
+                    },
+                    {
+                        "header": "Length",
+                        "id": "microbial",
+                        "values": values_length
+                    },
+                    {
+                        "header": "Circular?",
+                        "id": "microbial",
+                        "values": values_circular
+                    }
+                ],
+                "id": "microbial_asm_polishing_report.contigs_table",
+                "title": "Polished contigs from Microbial Assembly"
+                },
+            ],
+            "tags": [],
+            "title": "Microbial Assembly Polishing Report",
+            "uuid": uuid,
+            "version": version
+        }
diff --git a/nimsuggest/config.nims b/nimsuggest/config.nims
new file mode 100644
index 000000000..ee19f9893
--- /dev/null
+++ b/nimsuggest/config.nims
@@ -0,0 +1,2 @@
+# xxx not sure why this flag isn't needed: switch("processing", "filenames")
+switch("filenames", "canonical")
diff --git a/nimsuggest/crashtester.nim b/nimsuggest/crashtester.nim
index 4b3ba4026..ead97adec 100644
--- a/nimsuggest/crashtester.nim
+++ b/nimsuggest/crashtester.nim
@@ -20,7 +20,7 @@ proc callNimsuggest() =
   let cl = parseCmdLine("nimsuggest --tester temp000.nim")
   var p = startProcess(command=cl[0], args=cl[1 .. ^1],
                        options={poStdErrToStdOut, poUsePath,
-                       poInteractive, poDemon})
+                       poInteractive, poDaemon})
   let outp = p.outputStream
   let inp = p.inputStream
   var report = ""
@@ -32,7 +32,7 @@ proc callNimsuggest() =
       if a == DummyEof: break
 
     var line = 0
-    for i in 0..< contents.len:
+    for i in 0 ..< contents.len:
       let slic = contents[0..i]
       writeFile("temp000.nim", slic)
       let (line, col) = getPosition(slic)
diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim
index 3ba1120b2..04bae08c1 100644
--- a/nimsuggest/nimsuggest.nim
+++ b/nimsuggest/nimsuggest.nim
@@ -7,26 +7,48 @@
 #    distribution, for details about the copyright.
 #
 
+import compiler/renderer
+import compiler/types
+import compiler/trees
+import compiler/wordrecg
+import compiler/sempass2
+import strformat
+import algorithm
+import tables
+import times
+import procmonitor
+
+template tryImport(module) = import module
+
+when compiles tryImport ../dist/checksums/src/checksums/sha1:
+  import ../dist/checksums/src/checksums/sha1
+else:
+  import checksums/sha1
+
 ## Nimsuggest is a tool that helps to give editors IDE like capabilities.
 
 when not defined(nimcore):
   {.error: "nimcore MUST be defined for Nim's core tooling".}
 
 import strutils, os, parseopt, parseutils, sequtils, net, rdstdin, sexp
-# Do NOT import suggest. It will lead to wierd bugs with
+# Do NOT import suggest. It will lead to weird bugs with
 # suggestionResultHook, because suggest.nim is included by sigmatch.
 # So we import that one instead.
-import compiler / [options, commands, modules, sem,
-  passes, passaux, msgs, nimconf,
-  extccomp, condsyms,
-  sigmatch, ast, scriptconfig,
-  idents, modulegraphs, vm, prefixmatches, lineinfos]
+import compiler / [options, commands, modules,
+  passes, passaux, msgs,
+  sigmatch, ast,
+  idents, modulegraphs, prefixmatches, lineinfos, cmdlinehelper,
+  pathutils, condsyms, syntaxes, suggestsymdb]
+
+when defined(nimPreviewSlimSystem):
+  import std/typedthreads
 
 when defined(windows):
   import winlean
 else:
   import posix
 
+const HighestSuggestProtocolVersion = 4
 const DummyEof = "!EOF!"
 const Usage = """
 Nimsuggest - Tool to give every editor IDE like capabilities for Nim
@@ -34,21 +56,35 @@ 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
                           stdout instead of using sockets
+  --clientProcessId:PID   shutdown nimsuggest in case this process dies
   --epc                   use emacs epc mode
   --debug                 enable debug output
   --log                   enable verbose logging to nimsuggest.log file
   --v1                    use version 1 of the protocol; for backwards compatibility
+  --v2                    use version 2(default) of the protocol
+  --v3                    use version 3 of the protocol
+  --v4                    use version 4 of the protocol
+  --info:X                information
+    --info:nimVer           return the Nim compiler version that nimsuggest uses internally
+    --info:protocolVer      return the newest protocol version that is supported
+    --info:capabilities     return the capabilities supported by nimsuggest
   --refresh               perform automatic refreshes to keep the analysis precise
   --maxresults:N          limit the number of suggestions to N
   --tester                implies --stdin and outputs a line
                           '""" & DummyEof & """' for the tester
+  --find                  attempts to find the project file of the current project
+  --exceptionInlayHints:on|off
+                          globally turn exception inlay hints on|off
 
 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.
 """
@@ -62,15 +98,19 @@ type
 
 var
   gPort = 6000.Port
-  gAddress = ""
+  gAddress = "127.0.0.1"
   gMode: Mode
   gEmitEof: bool # whether we write '!EOF!' dummy lines
   gLogging = defined(logging)
   gRefresh: bool
+  gAutoBind = false
 
   requests: Channel[string]
   results: Channel[Suggest]
 
+proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, line, col: int; tag: string,
+  graph: ModuleGraph);
+
 proc writelnToChannel(line: string) =
   results.send(Suggest(section: ideMsg, doc: line))
 
@@ -87,18 +127,21 @@ proc myLog(s: string) =
 
 const
   seps = {':', ';', ' ', '\t'}
-  Help = "usage: sug|con|def|use|dus|chk|mod|highlight|outline|known file.nim[;dirtyfile.nim]:line:col\n" &
+  Help = "usage: sug|con|def|use|dus|chk|mod|highlight|outline|known|project file.nim[;dirtyfile.nim]:line:col\n" &
          "type 'quit' to quit\n" &
          "type 'debug' to toggle debug mode on/off\n" &
          "type 'terse' to toggle terse mode on/off"
-
-type
-  EUnexpectedCommand = object of Exception
+  #List of currently supported capabilities. So lang servers/ides can iterate over and check for what's enabled
+  Capabilities = [
+    "con", #current NimSuggest supports the `con` commmand
+    "exceptionInlayHints",
+    "unknownFile", #current NimSuggest can handle unknown files
+  ]
 
 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)
@@ -109,7 +152,7 @@ proc sexp(s: IdeCmd|TSymKind|PrefixMatch): SexpNode = sexp($s)
 proc sexp(s: Suggest): SexpNode =
   # If you change the order here, make sure to change it over in
   # nim-mode.el too.
-  let qp = if s.qualifiedPath.isNil: @[] else: s.qualifiedPath
+  let qp = if s.qualifiedPath.len == 0: @[] else: s.qualifiedPath
   result = convertSexp([
     s.section,
     TSymKind s.symkind,
@@ -123,6 +166,9 @@ proc sexp(s: Suggest): SexpNode =
   ])
   if s.section == ideSug:
     result.add convertSexp(s.prefix)
+  if s.section in {ideOutline, ideExpand} and s.version == 3:
+    result.add convertSexp(s.endLine.int)
+    result.add convertSexp(s.endCol)
 
 proc sexp(s: seq[Suggest]): SexpNode =
   result = newSList()
@@ -135,7 +181,7 @@ proc listEpc(): SexpNode =
     argspecs = sexp("file line column dirtyfile".split(" ").map(newSSymbol))
     docstring = sexp("line starts at 1, column at 0, dirtyfile is optional")
   result = newSList()
-  for command in ["sug", "con", "def", "use", "dus", "chk", "mod"]:
+  for command in ["sug", "con", "def", "use", "dus", "chk", "mod", "globalSymbols", "recompile", "saved", "chkFile", "declaration", "inlayHints"]:
     let
       cmd = sexp(command)
       methodDesc = newSList()
@@ -158,25 +204,62 @@ proc symFromInfo(graph: ModuleGraph; trackPos: TLineInfo): PSym =
   if m != nil and m.ast != nil:
     result = findNode(m.ast, trackPos)
 
-proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
+template benchmark(benchmarkName: untyped, code: untyped) =
+  block:
+    myLog "Started [" & benchmarkName & "]..."
+    let t0 = epochTime()
+    code
+    let elapsed = epochTime() - t0
+    let elapsedStr = elapsed.formatFloat(format = ffDecimal, precision = 3)
+    myLog "CPU Time [" & benchmarkName & "] " & elapsedStr & "s"
+
+proc clearInstCache(graph: ModuleGraph, projectFileIdx: FileIndex) =
+  if projectFileIdx == InvalidFileIdx:
+    graph.typeInstCache.clear()
+    graph.procInstCache.clear()
+    return
+  var typeIdsToDelete = newSeq[ItemId]()
+  for id in graph.typeInstCache.keys:
+    if id.module == projectFileIdx.int:
+      typeIdsToDelete.add id
+  for id in typeIdsToDelete:
+    graph.typeInstCache.del id
+  var procIdsToDelete = newSeq[ItemId]()
+  for id in graph.procInstCache.keys:
+    if id.module == projectFileIdx.int:
+      procIdsToDelete.add id
+  for id in procIdsToDelete:
+    graph.procInstCache.del id
+
+  for tbl in mitems(graph.attachedOps):
+    var attachedOpsToDelete = newSeq[ItemId]()
+    for id in tbl.keys:
+      if id.module == projectFileIdx.int and sfOverridden in resolveAttachedOp(graph, tbl[id]).flags:
+        attachedOpsToDelete.add id
+    for id in attachedOpsToDelete:
+      tbl.del id
+
+proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int, tag: string,
              graph: ModuleGraph) =
   let conf = graph.config
-  myLog("cmd: " & $cmd & ", file: " & file & ", dirtyFile: " & dirtyfile &
+
+  if conf.suggestVersion >= 3:
+    let command = fmt "cmd = {cmd} {file}:{line}:{col}"
+    benchmark command:
+      executeNoHooksV3(cmd, file, dirtyfile, line, col, tag, graph)
+    return
+
+  myLog("cmd: " & $cmd & ", file: " & file.string &
+        ", dirtyFile: " & dirtyfile.string &
         "[" & $line & ":" & $col & "]")
   conf.ideCmd = cmd
-  if cmd == ideChk:
-    conf.structuredErrorHook = errorHook
-    conf.writelnHook = myLog
-  else:
-    conf.structuredErrorHook = nil
-    conf.writelnHook = myLog
   if cmd == ideUse and conf.suggestVersion != 0:
     graph.resetAllModules()
   var isKnownFile = true
   let dirtyIdx = fileInfoIdx(conf, file, isKnownFile)
 
-  if dirtyfile.len != 0: msgs.setDirtyFile(conf, dirtyIdx, dirtyfile)
-  else: msgs.setDirtyFile(conf, dirtyIdx, nil)
+  if not dirtyfile.isEmpty: msgs.setDirtyFile(conf, dirtyIdx, dirtyfile)
+  else: msgs.setDirtyFile(conf, dirtyIdx, AbsoluteFile"")
 
   conf.m.trackPos = newLineInfo(dirtyIdx, line, col)
   conf.m.trackPosAttached = false
@@ -184,37 +267,53 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
   if conf.suggestVersion == 1:
     graph.usageSym = nil
   if not isKnownFile:
-    graph.compileProject()
+    graph.clearInstCache(dirtyIdx)
+    graph.compileProject(dirtyIdx)
   if conf.suggestVersion == 0 and conf.ideCmd in {ideUse, ideDus} and
-      dirtyfile.len == 0:
+      dirtyfile.isEmpty:
     discard "no need to recompile anything"
   else:
     let modIdx = graph.parentModule(dirtyIdx)
     graph.markDirty dirtyIdx
     graph.markClientsDirty dirtyIdx
     if conf.ideCmd != ideMod:
-      graph.compileProject(modIdx)
+      if isKnownFile:
+        graph.clearInstCache(modIdx)
+        graph.compileProject(modIdx)
   if conf.ideCmd in {ideUse, ideDus}:
     let u = if conf.suggestVersion != 1: graph.symFromInfo(conf.m.trackPos) else: graph.usageSym
     if u != nil:
-      listUsages(conf, u)
+      listUsages(graph, u)
     else:
       localError(conf, conf.m.trackPos, "found no symbol at this position " & (conf $ conf.m.trackPos))
 
+proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int, graph: ModuleGraph) =
+  executeNoHooks(cmd, file, dirtyfile, line, col, "", graph)
+
+proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int; tag: string,
+             graph: ModuleGraph) =
+  if cmd == ideChk:
+    graph.config.structuredErrorHook = errorHook
+    graph.config.writelnHook = myLog
+  else:
+    graph.config.structuredErrorHook = nil
+    graph.config.writelnHook = myLog
+  executeNoHooks(cmd, file, dirtyfile, line, col, tag, graph)
+
 proc executeEpc(cmd: IdeCmd, args: SexpNode;
                 graph: ModuleGraph) =
   let
-    file = args[0].getStr
+    file = AbsoluteFile args[0].getStr
     line = args[1].getNum
     column = args[2].getNum
-  var dirtyfile = ""
+  var dirtyfile = AbsoluteFile""
   if len(args) > 3:
-    dirtyfile = args[3].getStr(nil)
-  execute(cmd, file, dirtyfile, int(line), int(column), graph)
+    dirtyfile = AbsoluteFile args[3].getStr("")
+  execute(cmd, file, dirtyfile, int(line), int(column), args[3].getStr, graph)
 
 proc returnEpc(socket: Socket, uid: BiggestInt, s: SexpNode|string,
-               return_symbol = "return") =
-  let response = $convertSexp([newSSymbol(return_symbol), uid, s])
+               returnSymbol = "return") =
+  let response = $convertSexp([newSSymbol(returnSymbol), uid, s])
   socket.send(toHex(len(response), 6))
   socket.send(response)
 
@@ -233,6 +332,7 @@ proc toStdout() {.gcsafe.} =
     of ideNone: break
     of ideMsg: echo res.doc
     of ideKnown: echo res.quality == 1
+    of ideProject: echo res.filePath
     else: echo res
 
 proc toSocket(stdoutSocket: Socket) {.gcsafe.} =
@@ -242,6 +342,7 @@ proc toSocket(stdoutSocket: Socket) {.gcsafe.} =
     of ideNone: break
     of ideMsg: stdoutSocket.send(res.doc & "\c\L")
     of ideKnown: stdoutSocket.send($(res.quality == 1) & "\c\L")
+    of ideProject: stdoutSocket.send(res.filePath & "\c\L")
     else: stdoutSocket.send($res & "\c\L")
 
 proc toEpc(client: Socket; uid: BiggestInt) {.gcsafe.} =
@@ -254,6 +355,8 @@ proc toEpc(client: Socket; uid: BiggestInt) {.gcsafe.} =
       list.add sexp(res.doc)
     of ideKnown:
       list.add sexp(res.quality == 1)
+    of ideProject:
+      list.add sexp(res.filePath)
     else:
       list.add sexp(res)
   returnEpc(client, uid, list)
@@ -263,7 +366,7 @@ template setVerbosity(level: typed) =
   conf.notes = NotesVerbosity[gVerbosity]
 
 proc connectToNextFreePort(server: Socket, host: string): Port =
-  server.bindaddr(Port(0), host)
+  server.bindAddr(Port(0), host)
   let (_, port) = server.getLocalAddr
   result = port
 
@@ -299,11 +402,17 @@ proc replCmdline(x: ThreadParams) {.thread.} =
 
 proc replTcp(x: ThreadParams) {.thread.} =
   var server = newSocket()
-  server.bindAddr(x.port, x.address)
-  var inp = "".TaintedString
-  server.listen()
+  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 = ""
+  var stdoutSocket: Socket
   while true:
-    var stdoutSocket = newSocket()
     accept(server, stdoutSocket)
 
     stdoutSocket.readLine(inp)
@@ -325,9 +434,9 @@ proc argsToStr(x: SexpNode): string =
     result.add ';'
     result.add dirty.escape
   result.add ':'
-  result.add line
+  result.addInt line
   result.add ':'
-  result.add col
+  result.addInt col
 
 proc replEpc(x: ThreadParams) {.thread.} =
   var server = newSocket()
@@ -336,7 +445,7 @@ proc replEpc(x: ThreadParams) {.thread.} =
   echo port
   stdout.flushFile()
 
-  var client = newSocket()
+  var client: Socket
   # Wait for connection
   accept(server, client)
   while true:
@@ -376,7 +485,7 @@ proc replEpc(x: ThreadParams) {.thread.} =
                        of "return", "return-error":
                          "no return expected"
                        else:
-                         "unexpected call: " & epcAPI
+                         "unexpected call: " & epcApi
       quit errMessage
 
 proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
@@ -417,41 +526,61 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
   of "debug": toggle optIdeDebug
   of "terse": toggle optIdeTerse
   of "known": conf.ideCmd = ideKnown
+  of "project": conf.ideCmd = ideProject
+  of "changed": conf.ideCmd = ideChanged
+  of "globalsymbols": conf.ideCmd = ideGlobalSymbols
+  of "declaration": conf.ideCmd = ideDeclaration
+  of "expand": conf.ideCmd = ideExpand
+  of "chkfile": conf.ideCmd = ideChkFile
+  of "recompile": conf.ideCmd = ideRecompile
+  of "type": conf.ideCmd = ideType
+  of "inlayhints":
+    if conf.suggestVersion >= 4:
+      conf.ideCmd = ideInlayHints
+    else:
+      err()
   else: err()
   var dirtyfile = ""
   var orig = ""
-  i = parseQuoted(cmd, orig, i)
-  if cmd[i] == ';':
-    i = parseQuoted(cmd, dirtyfile, i+1)
-  i += skipWhile(cmd, seps, i)
-  var line = -1
-  var col = 0
+  i += skipWhitespace(cmd, i)
+  if i < cmd.len and cmd[i] in {'0'..'9'}:
+    orig = string conf.projectFull
+  else:
+    i = parseQuoted(cmd, orig, i)
+    if i < cmd.len and cmd[i] == ';':
+      i = parseQuoted(cmd, dirtyfile, i+1)
+    i += skipWhile(cmd, seps, i)
+  var line = 0
+  var col = -1
   i += parseInt(cmd, line, i)
   i += skipWhile(cmd, seps, i)
   i += parseInt(cmd, col, i)
+  let tag = substr(cmd, i)
 
   if conf.ideCmd == ideKnown:
-    results.send(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, orig))))
+    results.send(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, AbsoluteFile orig))))
+  elif conf.ideCmd == ideProject:
+    results.send(Suggest(section: ideProject, filePath: string conf.projectFull))
   else:
     if conf.ideCmd == ideChk:
       for cm in cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev)
-    execute(conf.ideCmd, orig, dirtyfile, line, col, graph)
+    execute(conf.ideCmd, AbsoluteFile orig, AbsoluteFile dirtyfile, line, col, tag, graph)
   sentinel()
 
 proc recompileFullProject(graph: ModuleGraph) =
-  #echo "recompiling full project"
-  resetSystemArtifacts(graph)
-  graph.vm = nil
-  graph.resetAllModules()
-  GC_fullcollect()
-  compileProject(graph)
-  #echo GC_getStatistics()
+  benchmark "Recompilation(clean)":
+    graph.resetForBackend()
+    graph.resetSystemArtifacts()
+    graph.vm = nil
+    graph.resetAllModules()
+    GC_fullCollect()
+    graph.compileProject()
 
 proc mainThread(graph: ModuleGraph) =
   let conf = graph.config
-  if gLogging:
-    for it in conf.searchPaths:
-      log(it)
+  myLog "searchPaths: "
+  for it in conf.searchPaths:
+    myLog("  " & it.string)
 
   proc wrHook(line: string) {.closure.} =
     if gMode == mepc:
@@ -474,7 +603,7 @@ proc mainThread(graph: ModuleGraph) =
     else:
       os.sleep 250
       idle += 1
-    if idle == 20 and gRefresh:
+    if idle == 20 and gRefresh and conf.suggestVersion < 3:
       # we use some nimsuggest activity to enable a lazy recompile:
       conf.ideCmd = ideChk
       conf.writelnHook = proc (s: string) = discard
@@ -492,35 +621,44 @@ proc mainCommand(graph: ModuleGraph) =
   clearPasses(graph)
   registerPass graph, verbosePass
   registerPass graph, semPass
-  conf.cmd = cmdIdeTools
+  conf.setCmd cmdIdeTools
+  defineSymbol(conf.symbols, $conf.backend)
   wantMainModule(conf)
 
   if not fileExists(conf.projectFull):
-    quit "cannot find file: " & conf.projectFull
+    quit "cannot find file: " & conf.projectFull.string
 
   add(conf.searchPaths, conf.libpath)
 
-  # do not stop after the first error:
-  conf.errorMax = high(int)
+  conf.setErrorMaxHighMaybe # honor --errorMax even if it may not make sense here
   # do not print errors, but log them
-  conf.writelnHook = proc (s: string) = log(s)
-  conf.structuredErrorHook = nil
+  conf.writelnHook = proc (msg: string) = discard
+
+  if graph.config.suggestVersion >= 3:
+    graph.config.structuredErrorHook = proc (conf: ConfigRef; info: TLineInfo; msg: string; sev: Severity) =
+      let suggest = Suggest(section: ideChk, filePath: toFullPath(conf, info),
+        line: toLinenumber(info), column: toColumn(info), doc: msg, forth: $sev)
+      graph.suggestErrors.mgetOrPut(info.fileIndex, @[]).add suggest
 
   # compile the project before showing any input so that we already
   # can answer questions right away:
-  compileProject(graph)
+  benchmark "Initial compilation":
+    compileProject(graph)
 
   open(requests)
   open(results)
 
+  if graph.config.clientProcessId != 0:
+    hookProcMonitor(graph.config.clientProcessId)
+
   case gMode
   of mstdin: createThread(inputThread, replStdin, (gPort, gAddress))
   of mtcp: createThread(inputThread, replTcp, (gPort, gAddress))
   of mepc: createThread(inputThread, replEpc, (gPort, gAddress))
   of mcmdsug: createThread(inputThread, replCmdline,
-                            (gPort, "sug \"" & conf.projectFull & "\":" & gAddress))
+                            (gPort, "sug \"" & conf.projectFull.string & "\":" & gAddress))
   of mcmdcon: createThread(inputThread, replCmdline,
-                            (gPort, "con \"" & conf.projectFull & "\":" & gAddress))
+                            (gPort, "con \"" & conf.projectFull.string & "\":" & gAddress))
   mainThread(graph)
   joinThread(inputThread)
   close(requests)
@@ -528,15 +666,19 @@ proc mainCommand(graph: ModuleGraph) =
 
 proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
   var p = parseopt.initOptParser(cmd)
+  var findProject = false
   while true:
     parseopt.next(p)
     case p.kind
     of cmdEnd: break
-    of cmdLongoption, cmdShortOption:
+    of cmdLongOption, cmdShortOption:
       case p.key.normalize
       of "help", "h":
-        stdout.writeline(Usage)
+        stdout.writeLine(Usage)
         quit()
+      of "autobind":
+        gMode = mtcp
+        gAutoBind = true
       of "port":
         gPort = parseInt(p.val).Port
         gMode = mtcp
@@ -556,8 +698,28 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
         gMode = mepc
         conf.verbosity = 0          # Port number gotta be first.
       of "debug": incl(conf.globalOptions, optIdeDebug)
-      of "v2": conf.suggestVersion = 0
       of "v1": conf.suggestVersion = 1
+      of "v2": conf.suggestVersion = 0
+      of "v3": conf.suggestVersion = 3
+      of "v4": conf.suggestVersion = 4
+      of "info":
+        case p.val.normalize
+        of "protocolver":
+          stdout.writeLine(HighestSuggestProtocolVersion)
+          quit 0
+        of "nimver":
+          stdout.writeLine(system.NimVersion)
+          quit 0
+        of "capabilities":
+          stdout.writeLine(Capabilities.toSeq.mapIt($it).join(" "))
+          quit 0
+        else:
+          processSwitch(pass, p, conf)
+      of "exceptioninlayhints":
+        case p.val.normalize
+        of "", "on": incl(conf.globalOptions, optIdeExceptionInlayHints)
+        of "off": excl(conf.globalOptions, optIdeExceptionInlayHints)
+        else: processSwitch(pass, p, conf)
       of "tester":
         gMode = mstdin
         gEmitEof = true
@@ -570,6 +732,10 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
           gRefresh = true
       of "maxresults":
         conf.suggestMaxResults = parseInt(p.val)
+      of "find":
+        findProject = true
+      of "clientprocessid":
+        conf.clientProcessId = parseInt(p.val)
       else: processSwitch(pass, p, conf)
     of cmdArgument:
       let a = unixToNativePath(p.key)
@@ -578,59 +744,649 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
         # don't make it worse, report the error the old way:
         if conf.projectName.len == 0: conf.projectName = a
       else:
-        conf.projectName = a
+        if findProject:
+          conf.projectName = findProjectNimFile(conf, a.parentDir())
+          if conf.projectName.len == 0:
+            conf.projectName = a
+        else:
+          conf.projectName = a
       # if processArgument(pass, p, argsCount): break
 
 proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
-  condsyms.initDefines(conf.symbols)
-  defineSymbol conf.symbols, "nimsuggest"
+  let self = NimProg(
+    suggestMode: true,
+    processCmdLine: processCmdLine
+  )
+  self.initDefinesProg(conf, "nimsuggest")
 
   if paramCount() == 0:
-    stdout.writeline(Usage)
+    stdout.writeLine(Usage)
+    return
+
+  self.processCmdLineAndProjectPath(conf)
+
+  if gMode != mstdin:
+    conf.writelnHook = proc (msg: string) = discard
+  conf.prefixDir = conf.getPrefixDir()
+  #msgs.writelnHook = proc (line: string) = log(line)
+  myLog("START " & conf.projectFull.string)
+
+  var graph = newModuleGraph(cache, conf)
+  if self.loadConfigsAndProcessCmdLine(cache, conf, graph):
+
+    if conf.selectedGC == gcUnselected and
+          conf.backend != backendJs:
+      initOrcDefines(conf)
+    mainCommand(graph)
+
+# v3 start
+
+proc recompilePartially(graph: ModuleGraph, projectFileIdx = InvalidFileIdx) =
+  if projectFileIdx == InvalidFileIdx:
+    myLog "Recompiling partially from root"
   else:
-    processCmdLine(passCmd1, "", conf)
-    if gMode != mstdin:
-      conf.writelnHook = proc (msg: string) = discard
-    if conf.projectName != "":
-      try:
-        conf.projectFull = canonicalizePath(conf, conf.projectName)
-      except OSError:
-        conf.projectFull = conf.projectName
-      var p = splitFile(conf.projectFull)
-      conf.projectPath = canonicalizePath(conf, p.dir)
-      conf.projectName = p.name
+    myLog fmt "Recompiling partially starting from {graph.getModule(projectFileIdx)}"
+
+  # inst caches are breaking incremental compilation when the cache caches stuff
+  # from dirty buffer
+  graph.clearInstCache(projectFileIdx)
+
+  GC_fullCollect()
+
+  try:
+    benchmark "Recompilation":
+      graph.compileProject(projectFileIdx)
+  except Exception as e:
+    myLog fmt "Failed to recompile partially with the following error:\n {e.msg} \n\n {e.getStackTrace()}"
+    try:
+      graph.recompileFullProject()
+    except Exception as e:
+      myLog fmt "Failed clean recompilation:\n {e.msg} \n\n {e.getStackTrace()}"
+
+func deduplicateSymInfoPair[SymInfoPair](xs: seq[SymInfoPair]): seq[SymInfoPair] =
+  # xs contains duplicate items and we want to filter them by range because the
+  # sym may not match. This can happen when xs contains the same definition but
+  # with different signature because suggestSym might be called multiple times
+  # for the same symbol (e. g. including/excluding the pragma)
+  result = newSeqOfCap[SymInfoPair](xs.len)
+  for itm in xs.reversed:
+    var found = false
+    for res in result:
+      if res.info.exactEquals(itm.info):
+        found = true
+        break
+    if not found:
+      result.add(itm)
+  result.reverse()
+
+func deduplicateSymInfoPair(xs: SuggestFileSymbolDatabase): SuggestFileSymbolDatabase =
+  # xs contains duplicate items and we want to filter them by range because the
+  # sym may not match. This can happen when xs contains the same definition but
+  # with different signature because suggestSym might be called multiple times
+  # for the same symbol (e. g. including/excluding the pragma)
+  result = SuggestFileSymbolDatabase(
+    lineInfo: newSeqOfCap[TinyLineInfo](xs.lineInfo.len),
+    sym: newSeqOfCap[PSym](xs.sym.len),
+    isDecl: newPackedBoolArray(),
+    caughtExceptions: newSeqOfCap[seq[PType]](xs.caughtExceptions.len),
+    caughtExceptionsSet: newPackedBoolArray(),
+    fileIndex: xs.fileIndex,
+    trackCaughtExceptions: xs.trackCaughtExceptions,
+    isSorted: false
+  )
+  var i = xs.lineInfo.high
+  while i >= 0:
+    let itm = xs.lineInfo[i]
+    var found = false
+    for res in result.lineInfo:
+      if res.exactEquals(itm):
+        found = true
+        break
+    if not found:
+      result.add(xs.getSymInfoPair(i))
+    dec i
+  result.reverse()
+
+proc findSymData(graph: ModuleGraph, trackPos: TLineInfo):
+    ref SymInfoPair =
+  let db = graph.fileSymbols(trackPos.fileIndex).deduplicateSymInfoPair
+  doAssert(db.fileIndex == trackPos.fileIndex)
+  for i in db.lineInfo.low..db.lineInfo.high:
+    if isTracked(db.lineInfo[i], TinyLineInfo(line: trackPos.line, col: trackPos.col), db.sym[i].name.s.len):
+      var res = db.getSymInfoPair(i)
+      new(result)
+      result[] = res
+      break
+
+func isInRange*(current, startPos, endPos: TinyLineInfo, tokenLen: int): bool =
+  result =
+    (current.line > startPos.line or (current.line == startPos.line and current.col>=startPos.col)) and
+    (current.line < endPos.line or (current.line == endPos.line and current.col <= endPos.col))
+
+proc findSymDataInRange(graph: ModuleGraph, startPos, endPos: TLineInfo):
+    seq[SymInfoPair] =
+  result = newSeq[SymInfoPair]()
+  let db = graph.fileSymbols(startPos.fileIndex).deduplicateSymInfoPair
+  for i in db.lineInfo.low..db.lineInfo.high:
+    if isInRange(db.lineInfo[i], TinyLineInfo(line: startPos.line, col: startPos.col), TinyLineInfo(line: endPos.line, col: endPos.col), db.sym[i].name.s.len):
+      result.add(db.getSymInfoPair(i))
+
+proc findSymData(graph: ModuleGraph, file: AbsoluteFile; line, col: int):
+    ref SymInfoPair =
+  let
+    fileIdx = fileInfoIdx(graph.config, file)
+    trackPos = newLineInfo(fileIdx, line, col)
+  result = findSymData(graph, trackPos)
+
+proc findSymDataInRange(graph: ModuleGraph, file: AbsoluteFile; startLine, startCol, endLine, endCol: int):
+    seq[SymInfoPair] =
+  let
+    fileIdx = fileInfoIdx(graph.config, file)
+    startPos = newLineInfo(fileIdx, startLine, startCol)
+    endPos = newLineInfo(fileIdx, endLine, endCol)
+  result = findSymDataInRange(graph, startPos, endPos)
+
+proc markDirtyIfNeeded(graph: ModuleGraph, file: string, originalFileIdx: FileIndex) =
+  let sha = $sha1.secureHashFile(file)
+  if graph.config.m.fileInfos[originalFileIdx.int32].hash != sha or graph.config.ideCmd in {ideSug, ideCon}:
+    myLog fmt "{file} changed compared to last compilation"
+    graph.markDirty originalFileIdx
+    graph.markClientsDirty originalFileIdx
+  else:
+    myLog fmt "No changes in file {file} compared to last compilation"
+
+proc suggestResult(graph: ModuleGraph, sym: PSym, info: TLineInfo,
+                   defaultSection = ideNone, endLine: uint16 = 0, endCol = 0) =
+  let section = if defaultSection != ideNone:
+                  defaultSection
+                elif sym.info.exactEquals(info):
+                  ideDef
+                else:
+                  ideUse
+  let suggest = symToSuggest(graph, sym, isLocal=false, section,
+                             info, 100, PrefixMatch.None, false, 0,
+                             endLine = endLine, endCol = endCol)
+  suggestResult(graph.config, suggest)
+
+proc suggestInlayHintResultType(graph: ModuleGraph, sym: PSym, info: TLineInfo,
+                   defaultSection = ideNone, endLine: uint16 = 0, endCol = 0) =
+  let section = if defaultSection != ideNone:
+                  defaultSection
+                elif sym.info.exactEquals(info):
+                  ideDef
+                else:
+                  ideUse
+  var suggestDef = symToSuggest(graph, sym, isLocal=false, section,
+                                info, 100, PrefixMatch.None, false, 0, true,
+                                endLine = endLine, endCol = endCol)
+  suggestDef.inlayHintInfo = suggestToSuggestInlayTypeHint(suggestDef)
+  suggestDef.section = ideInlayHints
+  if sym.kind == skForVar:
+    suggestDef.inlayHintInfo.allowInsert = false
+  suggestResult(graph.config, suggestDef)
+
+proc suggestInlayHintResultException(graph: ModuleGraph, sym: PSym, info: TLineInfo,
+                   defaultSection = ideNone, caughtExceptions: seq[PType], caughtExceptionsSet: bool, endLine: uint16 = 0, endCol = 0) =
+  if not caughtExceptionsSet:
+    return
+
+  if sym.kind == skParam and sfEffectsDelayed in sym.flags:
+    return
+
+  var raisesList: seq[PType] = @[getEbase(graph, info)]
+
+  let t = sym.typ
+  if not isNil(t) and not isNil(t.n) and t.n.len > 0 and t.n[0].len > exceptionEffects:
+    let effects = t.n[0]
+    if effects.kind == nkEffectList and effects.len == effectListLen:
+      let effs = effects[exceptionEffects]
+      if not isNil(effs):
+        raisesList = @[]
+        for eff in items(effs):
+          if not isNil(eff):
+            raisesList.add(eff.typ)
+
+  var propagatedExceptionList: seq[PType] = @[]
+  for re in raisesList:
+    var exceptionIsPropagated = true
+    for ce in caughtExceptions:
+      if isNil(ce) or safeInheritanceDiff(re, ce) <= 0:
+        exceptionIsPropagated = false
+        break
+    if exceptionIsPropagated:
+      propagatedExceptionList.add(re)
+
+  if propagatedExceptionList.len == 0:
+    return
+
+  let section = if defaultSection != ideNone:
+                  defaultSection
+                elif sym.info.exactEquals(info):
+                  ideDef
+                else:
+                  ideUse
+  var suggestDef = symToSuggest(graph, sym, isLocal=false, section,
+                                info, 100, PrefixMatch.None, false, 0, true,
+                                endLine = endLine, endCol = endCol)
+  suggestDef.inlayHintInfo = suggestToSuggestInlayExceptionHintLeft(suggestDef, propagatedExceptionList)
+  suggestDef.section = ideInlayHints
+  suggestResult(graph.config, suggestDef)
+  suggestDef.inlayHintInfo = suggestToSuggestInlayExceptionHintRight(suggestDef, propagatedExceptionList)
+  suggestResult(graph.config, suggestDef)
+
+const
+  # kinds for ideOutline and ideGlobalSymbols
+  searchableSymKinds = {skField, skEnumField, skIterator, skMethod, skFunc, skProc, skConverter, skTemplate}
+
+proc symbolEqual(left, right: PSym): bool =
+  # More relaxed symbol comparison
+  return left.info.exactEquals(right.info) and left.name == right.name
+
+proc findDef(n: PNode, line: uint16, col: int16): PNode =
+  if n.kind in {nkProcDef, nkIteratorDef, nkTemplateDef, nkMethodDef, nkMacroDef}:
+    if n.info.line == line:
+      return n
+  else:
+    for i in 0 ..< safeLen(n):
+      let res = findDef(n[i], line, col)
+      if res != nil: return res
+
+proc findByTLineInfo(trackPos: TLineInfo, infoPairs: SuggestFileSymbolDatabase):
+    ref SymInfoPair =
+  result = nil
+  if infoPairs.fileIndex == trackPos.fileIndex:
+    for i in infoPairs.lineInfo.low..infoPairs.lineInfo.high:
+      let s = infoPairs.getSymInfoPair(i)
+      if s.info.exactEquals trackPos:
+        new(result)
+        result[] = s
+        break
+
+proc outlineNode(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: SuggestFileSymbolDatabase): bool =
+  proc checkSymbol(sym: PSym, info: TLineInfo): bool =
+    result = (sym.owner.kind in {skModule, skType} or sym.kind in {skProc, skMethod, skIterator, skTemplate, skType})
+
+  if n.kind == nkSym and n.sym.checkSymbol(n.info):
+    graph.suggestResult(n.sym, n.sym.info, ideOutline, endInfo.line, endInfo.col)
+    return true
+  elif n.kind == nkIdent:
+    let symData = findByTLineInfo(n.info, infoPairs)
+    if symData != nil and symData.sym.checkSymbol(symData.info):
+       let sym = symData.sym
+       graph.suggestResult(sym, sym.info, ideOutline, endInfo.line, endInfo.col)
+       return true
+
+proc handleIdentOrSym(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: SuggestFileSymbolDatabase): bool =
+  for child in n:
+    if child.kind in {nkIdent, nkSym}:
+      if graph.outlineNode(child, endInfo, infoPairs):
+        return true
+    elif child.kind == nkPostfix:
+      if graph.handleIdentOrSym(child, endInfo, infoPairs):
+        return true
+
+proc iterateOutlineNodes(graph: ModuleGraph, n: PNode, infoPairs: SuggestFileSymbolDatabase) =
+  var matched = true
+  if n.kind == nkIdent:
+    let symData = findByTLineInfo(n.info, infoPairs)
+    if symData != nil and symData.sym.kind == skEnumField and symData.info.exactEquals(symData.sym.info):
+       let sym = symData.sym
+       graph.suggestResult(sym, sym.info, ideOutline, n.endInfo.line, n.endInfo.col)
+  elif (n.kind in {nkFuncDef, nkProcDef, nkTypeDef, nkMacroDef, nkTemplateDef, nkConverterDef, nkEnumFieldDef, nkConstDef}):
+    matched = handleIdentOrSym(graph, n, n.endInfo, infoPairs)
+  else:
+    matched = false
+
+  if n.kind != nkFormalParams:
+    for child in n:
+      graph.iterateOutlineNodes(child, infoPairs)
+
+proc calculateExpandRange(n: PNode, info: TLineInfo): TLineInfo =
+  if ((n.kind in {nkFuncDef, nkProcDef, nkIteratorDef, nkTemplateDef, nkMethodDef, nkConverterDef} and
+          n.info.exactEquals(info)) or
+         (n.kind in {nkCall, nkCommand} and n[0].info.exactEquals(info))):
+    result = n.endInfo
+  else:
+    for child in n:
+      result = child.calculateExpandRange(info)
+      if result != unknownLineInfo:
+        return result
+    result = unknownLineInfo
+
+proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, line, col: int; tag: string,
+    graph: ModuleGraph) =
+  let conf = graph.config
+  conf.writelnHook = proc (s: string) = discard
+  conf.structuredErrorHook = proc (conf: ConfigRef; info: TLineInfo;
+                                   msg: string; sev: Severity) =
+    let suggest = Suggest(section: ideChk, filePath: toFullPath(conf, info),
+      line: toLinenumber(info), column: toColumn(info), doc: msg, forth: $sev)
+    graph.suggestErrors.mgetOrPut(info.fileIndex, @[]).add suggest
+
+  conf.ideCmd = cmd
+
+  myLog fmt "cmd: {cmd}, file: {file}[{line}:{col}], dirtyFile: {dirtyfile}, tag: {tag}"
+
+  var fileIndex: FileIndex
+
+  if not (cmd in {ideRecompile, ideGlobalSymbols}):
+    fileIndex = fileInfoIdx(conf, file)
+    msgs.setDirtyFile(
+      conf,
+      fileIndex,
+      if dirtyfile.isEmpty: AbsoluteFile"" else: dirtyfile)
+
+    if not dirtyfile.isEmpty:
+      graph.markDirtyIfNeeded(dirtyFile.string, fileInfoIdx(conf, file))
+
+  # these commands require fully compiled project
+  if cmd in {ideUse, ideDus, ideGlobalSymbols, ideChk, ideInlayHints} and graph.needsCompilation():
+    graph.recompilePartially()
+    # when doing incremental build for the project root we should make sure that
+    # everything is unmarked as no longer beeing dirty in case there is no
+    # longer reference to a particular module. E. g. A depends on B, B is marked
+    # as dirty and A loses B import.
+    graph.unmarkAllDirty()
+
+  # these commands require partially compiled project
+  elif cmd in {ideSug, ideCon, ideOutline, ideHighlight, ideDef, ideChkFile, ideType, ideDeclaration, ideExpand} and
+       (graph.needsCompilation(fileIndex) or cmd in {ideSug, ideCon}):
+    # for ideSug use v2 implementation
+    if cmd in {ideSug, ideCon}:
+      conf.m.trackPos = newLineInfo(fileIndex, line, col)
+      conf.m.trackPosAttached = false
     else:
-      conf.projectPath = canonicalizePath(conf, getCurrentDir())
+      conf.m.trackPos = default(TLineInfo)
+      graph.recompilePartially(fileIndex)
+
+  case cmd
+  of ideDef:
+    let s = graph.findSymData(file, line, col)
+    if not s.isNil:
+      graph.suggestResult(s.sym, s.sym.info)
+  of ideType:
+    let s = graph.findSymData(file, line, col)
+    if not s.isNil:
+      let typeSym = s.sym.typ.sym
+      if typeSym != nil:
+        graph.suggestResult(typeSym, typeSym.info, ideType)
+      elif s.sym.typ.len != 0:
+        let genericType = s.sym.typ[0].sym
+        graph.suggestResult(genericType, genericType.info, ideType)
+  of ideUse, ideDus:
+    let symbol = graph.findSymData(file, line, col)
+    if not symbol.isNil:
+      var res: seq[SymInfoPair] = @[]
+      for s in graph.suggestSymbolsIter:
+        if s.sym.symbolEqual(symbol.sym):
+          res.add(s)
+      for s in res.deduplicateSymInfoPair():
+        graph.suggestResult(s.sym, s.info)
+  of ideHighlight:
+    let sym = graph.findSymData(file, line, col)
+    if not sym.isNil:
+      let fs = graph.fileSymbols(fileIndex)
+      var usages: seq[SymInfoPair] = @[]
+      for i in fs.lineInfo.low..fs.lineInfo.high:
+        if fs.sym[i] == sym.sym:
+          usages.add(fs.getSymInfoPair(i))
+      myLog fmt "Found {usages.len} usages in {file.string}"
+      for s in usages:
+        graph.suggestResult(s.sym, s.info)
+  of ideRecompile:
+    graph.recompileFullProject()
+  of ideChanged:
+    graph.markDirtyIfNeeded(file.string, fileIndex)
+  of ideSug, ideCon:
+    # ideSug/ideCon performs partial build of the file, thus mark it dirty for the
+    # future calls.
+    graph.markDirtyIfNeeded(file.string, fileIndex)
+    graph.recompilePartially(fileIndex) 
+    let m = graph.getModule fileIndex
+    incl m.flags, sfDirty 
+  of ideOutline:
+    let n = parseFile(fileIndex, graph.cache, graph.config)
+    graph.iterateOutlineNodes(n, graph.fileSymbols(fileIndex).deduplicateSymInfoPair)
+  of ideChk:
+    myLog fmt "Reporting errors for {graph.suggestErrors.len} file(s)"
+    for sug in graph.suggestErrorsIter:
+      suggestResult(graph.config, sug)
+  of ideChkFile:
+    let errors = graph.suggestErrors.getOrDefault(fileIndex, @[])
+    myLog fmt "Reporting {errors.len} error(s) for {file.string}"
+    for error in errors:
+      suggestResult(graph.config, error)
+  of ideGlobalSymbols:
+    var
+      counter = 0
+      res: seq[SymInfoPair] = @[]
+
+    for s in graph.suggestSymbolsIter:
+      if (sfGlobal in s.sym.flags or s.sym.kind in searchableSymKinds) and
+          s.sym.info == s.info:
+        if contains(s.sym.name.s, file.string):
+          inc counter
+          res = res.filterIt(not it.info.exactEquals(s.info))
+          res.add s
+          # stop after first 1000 matches...
+          if counter > 1000:
+            break
+
+    # ... then sort them by weight ...
+    res.sort() do (left, right: SymInfoPair) -> int:
+      let
+        leftString = left.sym.name.s
+        rightString = right.sym.name.s
+        leftIndex = leftString.find(file.string)
+        rightIndex = rightString.find(file.string)
+
+      if leftIndex == rightIndex:
+        result = cmp(toLowerAscii(leftString),
+                     toLowerAscii(rightString))
+      else:
+        result = cmp(leftIndex, rightIndex)
+
+    # ... and send first 100 results
+    if res.len > 0:
+      for i in 0 .. min(100, res.len - 1):
+        let s = res[i]
+        graph.suggestResult(s.sym, s.info)
+
+  of ideDeclaration:
+    let s = graph.findSymData(file, line, col)
+    if not s.isNil:
+      # find first mention of the symbol in the file containing the definition.
+      # It is either the definition or the declaration.
+      var first: SymInfoPair
+      let db = graph.fileSymbols(s.sym.info.fileIndex).deduplicateSymInfoPair
+      for i in db.lineInfo.low..db.lineInfo.high:
+        if s.sym.symbolEqual(db.sym[i]):
+          first = db.getSymInfoPair(i)
+          break
+
+      if s.info.exactEquals(first.info):
+        # we are on declaration, go to definition
+        graph.suggestResult(first.sym, first.sym.info, ideDeclaration)
+      else:
+        # we are on definition or usage, look for declaration
+        graph.suggestResult(first.sym, first.info, ideDeclaration)
+  of ideExpand:
+    var level: int = high(int)
+    let index = skipWhitespace(tag, 0);
+    let trimmed = substr(tag, index)
+    if not (trimmed == "" or trimmed == "all"):
+      discard parseInt(trimmed, level, 0)
+
+    conf.expandPosition = newLineInfo(fileIndex, line, col)
+    conf.expandLevels = level
+    conf.expandProgress = false
+    conf.expandNodeResult = ""
+
+    graph.markDirty fileIndex
+    graph.markClientsDirty fileIndex
+    graph.recompilePartially()
+    var suggest = Suggest()
+    suggest.section = ideExpand
+    suggest.version = 3
+    suggest.line = line
+    suggest.column = col
+    suggest.doc = graph.config.expandNodeResult
+    if suggest.doc != "":
+      let
+        n = parseFile(fileIndex, graph.cache, graph.config)
+        endInfo = n.calculateExpandRange(conf.expandPosition)
+
+      suggest.endLine = endInfo.line
+      suggest.endCol = endInfo.col
+
+    suggestResult(graph.config, suggest)
+
+    graph.markDirty fileIndex
+    graph.markClientsDirty fileIndex
+  of ideInlayHints:
+    myLog fmt "Executing inlayHints"
+    var endLine = 0
+    var endCol = -1
+    var i = 0
+    i += skipWhile(tag, seps, i)
+    i += parseInt(tag, endLine, i)
+    i += skipWhile(tag, seps, i)
+    i += parseInt(tag, endCol, i)
+    i += skipWhile(tag, seps, i)
+    var typeHints = true
+    var exceptionHints = false
+    while i <= tag.high:
+      var token: string
+      i += parseUntil(tag, token, seps, i)
+      i += skipWhile(tag, seps, i)
+      case token:
+      of "+typeHints":
+        typeHints = true
+      of "-typeHints":
+        typeHints = false
+      of "+exceptionHints":
+        exceptionHints = true
+      of "-exceptionHints":
+        exceptionHints = false
+      else:
+        myLog fmt "Discarding unknown inlay hint parameter {token}"
+
+    let s = graph.findSymDataInRange(file, line, col, endLine, endCol)
+    for q in s:
+      if typeHints and q.sym.kind in {skLet, skVar, skForVar, skConst} and q.isDecl and not q.sym.hasUserSpecifiedType:
+        graph.suggestInlayHintResultType(q.sym, q.info, ideInlayHints)
+      if exceptionHints and q.sym.kind in {skProc, skFunc, skMethod, skVar, skLet, skParam} and not q.isDecl:
+        graph.suggestInlayHintResultException(q.sym, q.info, ideInlayHints, caughtExceptions = q.caughtExceptions, caughtExceptionsSet = q.caughtExceptionsSet)
+  else:
+    myLog fmt "Discarding {cmd}"
 
+# v3 end
+when isMainModule:
+  handleCmdLine(newIdentCache(), newConfigRef())
+else:
+  export Suggest
+  export IdeCmd
+  export AbsoluteFile
+  type NimSuggest* = ref object
+    graph: ModuleGraph
+    idle: int
+    cachedMsgs: CachedMsgs
+
+  proc initNimSuggest*(project: string, nimPath: string = ""): NimSuggest =
+    var retval: ModuleGraph
+    proc mockCommand(graph: ModuleGraph) =
+      retval = graph
+      let conf = graph.config
+      conf.setCmd cmdIdeTools
+      defineSymbol(conf.symbols, $conf.backend)
+      clearPasses(graph)
+      registerPass graph, verbosePass
+      registerPass graph, semPass
+
+      wantMainModule(conf)
+
+      if not fileExists(conf.projectFull):
+        quit "cannot find file: " & conf.projectFull.string
+
+      add(conf.searchPaths, conf.libpath)
+
+      conf.setErrorMaxHighMaybe
+      # do not print errors, but log them
+      conf.writelnHook = myLog
+      conf.structuredErrorHook = nil
+
+      # compile the project before showing any input so that we already
+      # can answer questions right away:
+      compileProject(graph)
+
+
+    proc mockCmdLine(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
+      conf.suggestVersion = 0
+      let a = unixToNativePath(project)
+      if dirExists(a) and not fileExists(a.addFileExt("nim")):
+        conf.projectName = findProjectNimFile(conf, a)
+        # don't make it worse, report the error the old way:
+        if conf.projectName.len == 0: conf.projectName = a
+      else:
+        conf.projectName = a
+          # if processArgument(pass, p, argsCount): break
+    let
+      cache = newIdentCache()
+      conf = newConfigRef()
+      self = NimProg(
+        suggestMode: true,
+        processCmdLine: mockCmdLine
+      )
+    self.initDefinesProg(conf, "nimsuggest")
+
+    self.processCmdLineAndProjectPath(conf)
+
+    if gMode != mstdin:
+      conf.writelnHook = proc (msg: string) = discard
     # Find Nim's prefix dir.
-    let binaryPath = findExe("nim")
-    if binaryPath == "":
-      raise newException(IOError,
-          "Cannot find Nim standard library: Nim compiler not in PATH")
-    conf.prefixDir = binaryPath.splitPath().head.parentDir()
-    if not dirExists(conf.prefixDir / "lib"): conf.prefixDir = ""
+    if nimPath == "":
+      conf.prefixDir = conf.getPrefixDir()
+    else:
+      conf.prefixDir = AbsoluteDir nimPath
 
     #msgs.writelnHook = proc (line: string) = log(line)
-    myLog("START " & conf.projectFull)
-
-    loadConfigs(DefaultConfig, cache, conf) # load all config files
-    # now process command line arguments again, because some options in the
-    # command line can overwite the config file's settings
-    conf.command = "nimsuggest"
-    let scriptFile = conf.projectFull.changeFileExt("nims")
-    if fileExists(scriptFile):
-      # 'nimsuggest foo.nims' means to just auto-complete the NimScript file:
-      if scriptFile != conf.projectFull:
-        runNimScript(cache, scriptFile, freshDefines=false, conf)
-    elif fileExists(conf.projectPath / "config.nims"):
-      # directory wide NimScript file
-      runNimScript(cache, conf.projectPath / "config.nims", freshDefines=false, conf)
-
-    extccomp.initVars(conf)
-    processCmdLine(passCmd2, "", conf)
-
-    let graph = newModuleGraph(cache, conf)
-    graph.suggestMode = true
-    mainCommand(graph)
+    myLog("START " & conf.projectFull.string)
+
+    var graph = newModuleGraph(cache, conf)
+    if self.loadConfigsAndProcessCmdLine(cache, conf, graph):
+      mockCommand(graph)
+    if gLogging:
+      myLog("Search paths:")
+      for it in conf.searchPaths:
+        myLog(" " & it.string)
+
+    retval.doStopCompile = proc (): bool = false
+    return NimSuggest(graph: retval, idle: 0, cachedMsgs: @[])
+
+  proc runCmd*(nimsuggest: NimSuggest, cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int): seq[Suggest] =
+    var retval: seq[Suggest] = @[]
+    let conf = nimsuggest.graph.config
+    conf.ideCmd = cmd
+    conf.writelnHook = proc (line: string) =
+      retval.add(Suggest(section: ideMsg, doc: line))
+    conf.suggestionResultHook = proc (s: Suggest) =
+      retval.add(s)
+    conf.writelnHook = proc (s: string) =
+      stderr.write s & "\n"
+    if conf.ideCmd == ideKnown:
+      retval.add(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, file))))
+    elif conf.ideCmd == ideProject:
+      retval.add(Suggest(section: ideProject, filePath: string conf.projectFull))
+    else:
+      if conf.ideCmd == ideChk:
+        for cm in nimsuggest.cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev)
+      if conf.ideCmd == ideChk:
+        conf.structuredErrorHook = proc (conf: ConfigRef; info: TLineInfo; msg: string; sev: Severity) =
+          retval.add(Suggest(section: ideChk, filePath: toFullPath(conf, info),
+            line: toLinenumber(info), column: toColumn(info), doc: msg,
+            forth: $sev))
 
-handleCmdline(newIdentCache(), newConfigRef())
+      else:
+        conf.structuredErrorHook = nil
+      executeNoHooks(conf.ideCmd, file, dirtyfile, line, col, nimsuggest.graph)
+    return retval
diff --git a/nimsuggest/nimsuggest.nim.cfg b/nimsuggest/nimsuggest.nim.cfg
index 820db0dba..394449740 100644
--- a/nimsuggest/nimsuggest.nim.cfg
+++ b/nimsuggest/nimsuggest.nim.cfg
@@ -22,5 +22,3 @@ define:nimcore
 #define:noDocgen
 --path:"$nim"
 --threads:on
---noNimblePath
---path:"../compiler"
diff --git a/nimsuggest/nimsuggest.nimble b/nimsuggest/nimsuggest.nimble
index 3651e12bd..b3790e116 100644
--- a/nimsuggest/nimsuggest.nimble
+++ b/nimsuggest/nimsuggest.nimble
@@ -1,11 +1,8 @@
-[Package]
-name          = "nimsuggest"
-version       = "0.1.0"
-author        = "Andreas Rumpf"
-description   = "Tool for providing auto completion data for Nim source code."
-license       = "MIT"
+include "../lib/system/compilation.nim"
+version = $NimMajor & "." & $NimMinor & "." & $NimPatch
+author = "Andreas Rumpf"
+description = "Tool for providing auto completion data for Nim source code."
+license = "MIT"
+bin = @["nimsuggest"]
 
-bin = "nimsuggest"
-
-[Deps]
-Requires: "nim >= 0.11.2, compiler#head"
+requires "compiler >= 1.9.0" , "checksums"
diff --git a/nimsuggest/procmonitor.nim b/nimsuggest/procmonitor.nim
new file mode 100644
index 000000000..0f1ba1e0d
--- /dev/null
+++ b/nimsuggest/procmonitor.nim
@@ -0,0 +1,34 @@
+# Monitor a client process and shutdown the current process, if the client
+# process is found to be dead
+
+import os
+
+when defined(posix):
+  import posix_utils
+  import posix
+
+when defined(windows):
+  import winlean
+
+when defined(posix):
+  proc monitorClientProcessIdThreadProc(pid: int) {.thread.} =
+    while true:
+      sleep(1000)
+      try:
+        sendSignal(Pid(pid), 0)
+      except:
+        discard kill(Pid(getCurrentProcessId()), cint(SIGTERM))
+
+when defined(windows):
+  proc monitorClientProcessIdThreadProc(pid: int) {.thread.} =
+    var process = openProcess(SYNCHRONIZE, 0, DWORD(pid))
+    if process != 0:
+      discard waitForSingleObject(process, INFINITE)
+      discard closeHandle(process)
+    quit(0)
+
+var tid: Thread[int]
+
+proc hookProcMonitor*(pid: int) =
+  when defined(posix) or defined(windows):
+    createThread(tid, monitorClientProcessIdThreadProc, pid)
diff --git a/nimsuggest/sexp.nim b/nimsuggest/sexp.nim
index e0983467f..03369ccb7 100644
--- a/nimsuggest/sexp.nim
+++ b/nimsuggest/sexp.nim
@@ -7,9 +7,16 @@
 #    distribution, for details about the copyright.
 #
 
+## **Note:** Import ``nimsuggest/sexp`` to use this module
+
 import
   hashes, strutils, lexbase, streams, unicode, macros
 
+import std/private/decode_helpers
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, formatfloat]
+
 type
   SexpEventKind* = enum  ## enumeration of all events that may occur when parsing
     sexpError,           ## an error occurred during parsing
@@ -111,20 +118,11 @@ proc errorMsgExpected*(my: SexpParser, e: string): string =
   ## other error messages
   result = "($1, $2) Error: $3" % [$getLine(my), $getColumn(my), e & " expected"]
 
-proc handleHexChar(c: char, x: var int): bool =
-  result = true # Success
-  case c
-  of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
-  of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
-  of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
-  else: result = false # error
-
 proc parseString(my: var SexpParser): TTokKind =
   result = tkString
   var pos = my.bufpos + 1
-  var buf = my.buf
   while true:
-    case buf[pos]
+    case my.buf[pos]
     of '\0':
       my.err = errQuoteExpected
       result = tkError
@@ -133,9 +131,9 @@ proc parseString(my: var SexpParser): TTokKind =
       inc(pos)
       break
     of '\\':
-      case buf[pos+1]
+      case my.buf[pos+1]
       of '\\', '"', '\'', '/':
-        add(my.a, buf[pos+1])
+        add(my.a, my.buf[pos+1])
         inc(pos, 2)
       of 'b':
         add(my.a, '\b')
@@ -155,65 +153,61 @@ proc parseString(my: var SexpParser): TTokKind =
       of 'u':
         inc(pos, 2)
         var r: int
-        if handleHexChar(buf[pos], r): inc(pos)
-        if handleHexChar(buf[pos], r): inc(pos)
-        if handleHexChar(buf[pos], r): inc(pos)
-        if handleHexChar(buf[pos], r): inc(pos)
+        if handleHexChar(my.buf[pos], r): inc(pos)
+        if handleHexChar(my.buf[pos], r): inc(pos)
+        if handleHexChar(my.buf[pos], r): inc(pos)
+        if handleHexChar(my.buf[pos], r): inc(pos)
         add(my.a, toUTF8(Rune(r)))
       else:
         # don't bother with the error
-        add(my.a, buf[pos])
+        add(my.a, my.buf[pos])
         inc(pos)
     of '\c':
       pos = lexbase.handleCR(my, pos)
-      buf = my.buf
       add(my.a, '\c')
     of '\L':
       pos = lexbase.handleLF(my, pos)
-      buf = my.buf
       add(my.a, '\L')
     else:
-      add(my.a, buf[pos])
+      add(my.a, my.buf[pos])
       inc(pos)
   my.bufpos = pos # store back
 
 proc parseNumber(my: var SexpParser) =
   var pos = my.bufpos
-  var buf = my.buf
-  if buf[pos] == '-':
+  if my.buf[pos] == '-':
     add(my.a, '-')
     inc(pos)
-  if buf[pos] == '.':
+  if my.buf[pos] == '.':
     add(my.a, "0.")
     inc(pos)
   else:
-    while buf[pos] in Digits:
-      add(my.a, buf[pos])
+    while my.buf[pos] in Digits:
+      add(my.a, my.buf[pos])
       inc(pos)
-    if buf[pos] == '.':
+    if my.buf[pos] == '.':
       add(my.a, '.')
       inc(pos)
   # digits after the dot:
-  while buf[pos] in Digits:
-    add(my.a, buf[pos])
+  while my.buf[pos] in Digits:
+    add(my.a, my.buf[pos])
     inc(pos)
-  if buf[pos] in {'E', 'e'}:
-    add(my.a, buf[pos])
+  if my.buf[pos] in {'E', 'e'}:
+    add(my.a, my.buf[pos])
     inc(pos)
-    if buf[pos] in {'+', '-'}:
-      add(my.a, buf[pos])
+    if my.buf[pos] in {'+', '-'}:
+      add(my.a, my.buf[pos])
       inc(pos)
-    while buf[pos] in Digits:
-      add(my.a, buf[pos])
+    while my.buf[pos] in Digits:
+      add(my.a, my.buf[pos])
       inc(pos)
   my.bufpos = pos
 
 proc parseSymbol(my: var SexpParser) =
   var pos = my.bufpos
-  var buf = my.buf
-  if buf[pos] in IdentStartChars:
-    while buf[pos] in IdentChars:
-      add(my.a, buf[pos])
+  if my.buf[pos] in IdentStartChars:
+    while my.buf[pos] in IdentChars:
+      add(my.a, my.buf[pos])
       inc(pos)
   my.bufpos = pos
 
@@ -293,55 +287,32 @@ proc raiseParseErr*(p: SexpParser, msg: string) {.noinline, noreturn.} =
   ## raises an `ESexpParsingError` exception.
   raise newException(SexpParsingError, errorMsgExpected(p, msg))
 
-proc newSString*(s: string): SexpNode {.procvar.}=
+proc newSString*(s: string): SexpNode =
   ## Creates a new `SString SexpNode`.
-  new(result)
-  result.kind = SString
-  result.str = s
-
-proc newSStringMove(s: string): SexpNode =
-  new(result)
-  result.kind = SString
-  shallowCopy(result.str, s)
+  result = SexpNode(kind: SString, str: s)
 
-proc newSInt*(n: BiggestInt): SexpNode {.procvar.} =
+proc newSInt*(n: BiggestInt): SexpNode =
   ## Creates a new `SInt SexpNode`.
-  new(result)
-  result.kind = SInt
-  result.num  = n
+  result = SexpNode(kind: SInt, num: n)
 
-proc newSFloat*(n: float): SexpNode {.procvar.} =
+proc newSFloat*(n: float): SexpNode =
   ## Creates a new `SFloat SexpNode`.
-  new(result)
-  result.kind = SFloat
-  result.fnum  = n
+  result = SexpNode(kind: SFloat, fnum: n)
 
-proc newSNil*(): SexpNode {.procvar.} =
+proc newSNil*(): SexpNode =
   ## Creates a new `SNil SexpNode`.
-  new(result)
+  result = SexpNode(kind: SNil)
 
-proc newSCons*(car, cdr: SexpNode): SexpNode {.procvar.} =
+proc newSCons*(car, cdr: SexpNode): SexpNode =
   ## Creates a new `SCons SexpNode`
-  new(result)
-  result.kind = SCons
-  result.car = car
-  result.cdr = cdr
+  result = SexpNode(kind: SCons, car: car, cdr: cdr)
 
-proc newSList*(): SexpNode {.procvar.} =
+proc newSList*(): SexpNode =
   ## Creates a new `SList SexpNode`
-  new(result)
-  result.kind = SList
-  result.elems = @[]
-
-proc newSSymbol*(s: string): SexpNode {.procvar.} =
-  new(result)
-  result.kind = SSymbol
-  result.symbol = s
+  result = SexpNode(kind: SList, elems: @[])
 
-proc newSSymbolMove(s: string): SexpNode =
-  new(result)
-  result.kind = SSymbol
-  shallowCopy(result.symbol, s)
+proc newSSymbol*(s: string): SexpNode =
+  result = SexpNode(kind: SSymbol, symbol: s)
 
 proc getStr*(n: SexpNode, default: string = ""): string =
   ## Retrieves the string value of a `SString SexpNode`.
@@ -389,43 +360,34 @@ proc getCons*(n: SexpNode, defaults: Cons = (newSNil(), newSNil())): Cons =
 
 proc sexp*(s: string): SexpNode =
   ## Generic constructor for SEXP data. Creates a new `SString SexpNode`.
-  new(result)
-  result.kind = SString
-  result.str = s
+  result = SexpNode(kind: SString, str: s)
 
 proc sexp*(n: BiggestInt): SexpNode =
   ## Generic constructor for SEXP data. Creates a new `SInt SexpNode`.
-  new(result)
-  result.kind = SInt
-  result.num  = n
+  result = SexpNode(kind: SInt, num: n)
 
 proc sexp*(n: float): SexpNode =
   ## Generic constructor for SEXP data. Creates a new `SFloat SexpNode`.
-  new(result)
-  result.kind = SFloat
-  result.fnum  = n
+  result = SexpNode(kind: SFloat, fnum: n)
 
 proc sexp*(b: bool): SexpNode =
   ## Generic constructor for SEXP data. Creates a new `SSymbol
   ## SexpNode` with value t or `SNil SexpNode`.
-  new(result)
   if b:
-    result.kind = SSymbol
-    result.symbol = "t"
+    result = SexpNode(kind: SSymbol, symbol: "t")
   else:
-    result.kind = SNil
+    result = SexpNode(kind: SNil)
 
 proc sexp*(elements: openArray[SexpNode]): SexpNode =
   ## Generic constructor for SEXP data. Creates a new `SList SexpNode`
-  new(result)
-  result.kind = SList
+  result = SexpNode(kind: SList)
   newSeq(result.elems, elements.len)
   for i, p in pairs(elements): result.elems[i] = p
 
 proc sexp*(s: SexpNode): SexpNode =
   result = s
 
-proc toSexp(x: NimNode): NimNode {.compiletime.} =
+proc toSexp(x: NimNode): NimNode {.compileTime.} =
   case x.kind
   of nnkBracket:
     result = newNimNode(nnkBracket)
@@ -442,7 +404,7 @@ macro convertSexp*(x: untyped): untyped =
   ## `%` for every element.
   result = toSexp(x)
 
-proc `==`* (a,b: SexpNode): bool =
+func `==`* (a, b: SexpNode): bool =
   ## Check two nodes for equality
   if a.isNil:
     if b.isNil: return true
@@ -561,10 +523,10 @@ proc toPretty(result: var string, node: SexpNode, indent = 2, ml = true,
     result.add(escapeJson(node.str))
   of SInt:
     if lstArr: result.indent(currIndent)
-    result.add(node.num)
+    result.addInt(node.num)
   of SFloat:
     if lstArr: result.indent(currIndent)
-    result.add(node.fnum)
+    result.addFloat(node.fnum)
   of SNil:
     if lstArr: result.indent(currIndent)
     result.add("nil")
@@ -629,8 +591,7 @@ proc parseSexp(p: var SexpParser): SexpNode =
   case p.tok
   of tkString:
     # we capture 'p.a' here, so we need to give it a fresh buffer afterwards:
-    result = newSStringMove(p.a)
-    p.a = ""
+    result = SexpNode(kind: SString, str: move p.a)
     discard getTok(p)
   of tkInt:
     result = newSInt(parseBiggestInt(p.a))
@@ -642,8 +603,7 @@ proc parseSexp(p: var SexpParser): SexpNode =
     result = newSNil()
     discard getTok(p)
   of tkSymbol:
-    result = newSSymbolMove(p.a)
-    p.a = ""
+    result = SexpNode(kind: SSymbol, symbol: move p.a)
     discard getTok(p)
   of tkParensLe:
     result = newSList()
diff --git a/nimsuggest/tester.nim b/nimsuggest/tester.nim
index 4cda272af..9b9488348 100644
--- a/nimsuggest/tester.nim
+++ b/nimsuggest/tester.nim
@@ -2,25 +2,32 @@
 # Every test file can have a #[!]# comment that is deleted from the input
 # before 'nimsuggest' is invoked to ensure this token doesn't make a
 # crucial difference for Nim's parser.
+# When debugging, to run a single test, use for e.g.:
+# `nim r nimsuggest/tester.nim nimsuggest/tests/tsug_accquote.nim`
 
 import os, osproc, strutils, streams, re, sexp, net
+from sequtils import toSeq
 
 type
   Test = object
-    cmd, dest: string
+    filename, cmd, dest: string
     startup: seq[string]
     script: seq[(string, string)]
+    disabled: bool
 
 const
-  curDir = when defined(windows): "" else: ""
   DummyEof = "!EOF!"
+  tpath = "nimsuggest/tests"
+  # we could also use `stdtest/specialpaths`
 
-template tpath(): untyped = getAppDir() / "tests"
+import std/compilesettings
 
 proc parseTest(filename: string; epcMode=false): Test =
   const cursorMarker = "#[!]#"
-  let nimsug = curDir & addFileExt("nimsuggest", ExeExt)
-  let libpath = findExe("nim").splitFile().dir /../ "lib"
+  let nimsug = "bin" / addFileExt("nimsuggest_testing", ExeExt)
+  doAssert nimsug.fileExists, nimsug
+  const libpath = querySetting(libPath)
+  result.filename = filename
   result.dest = getTempDir() / extractFilename(filename)
   result.cmd = nimsug & " --tester " & result.dest
   result.script = @[]
@@ -42,7 +49,14 @@ proc parseTest(filename: string; epcMode=false): Test =
     if x.contains("""""""""):
       inc specSection
     elif specSection == 1:
-      if x.startsWith("$nimsuggest"):
+      if x.startsWith("disabled:"):
+        if x.startsWith("disabled:true"):
+          result.disabled = true
+        else:
+          # be strict about format
+          doAssert x.startsWith("disabled:false")
+          result.disabled = false
+      elif x.startsWith("$nimsuggest"):
         result.cmd = x % ["nimsuggest", nimsug, "file", filename, "lib", libpath]
       elif x.startsWith("!"):
         if result.cmd.len == 0:
@@ -52,7 +66,7 @@ proc parseTest(filename: string; epcMode=false): Test =
       elif x.startsWith(">"):
         # since 'markers' here are not complete yet, we do the $substitutions
         # afterwards
-        result.script.add((x.substr(1).replaceWord("$path", tpath()), ""))
+        result.script.add((x.substr(1).replaceWord("$path", tpath).replaceWord("$file", filename), ""))
       elif x.len > 0:
         # expected output line:
         let x = x % ["file", filename, "lib", libpath]
@@ -70,22 +84,22 @@ proc parseCmd(c: string): seq[string] =
   result = @[]
   var i = 0
   var a = ""
-  while true:
+  while i < c.len:
     setLen(a, 0)
     # eat all delimiting whitespace
-    while c[i] in {' ', '\t', '\l', '\r'}: inc(i)
+    while i < c.len and c[i] in {' ', '\t', '\l', '\r'}: inc(i)
+    if i >= c.len: break
     case c[i]
     of '"': raise newException(ValueError, "double quotes not yet supported: " & c)
     of '\'':
       var delim = c[i]
       inc(i) # skip ' or "
-      while c[i] != '\0' and c[i] != delim:
+      while i < c.len and c[i] != delim:
         add a, c[i]
         inc(i)
-      if c[i] != '\0': inc(i)
-    of '\0': break
+      if i < c.len: inc(i)
     else:
-      while c[i] > ' ':
+      while i < c.len and c[i] > ' ':
         add(a, c[i])
         inc(i)
     add(result, a)
@@ -93,7 +107,7 @@ proc parseCmd(c: string): seq[string] =
 proc edit(tmpfile: string; x: seq[string]) =
   if x.len != 3 and x.len != 4:
     quit "!edit takes two or three arguments"
-  let f = if x.len >= 4: tpath() / x[3] else: tmpfile
+  let f = if x.len >= 4: tpath / x[3] else: tmpfile
   try:
     let content = readFile(f)
     let newcontent = content.replace(x[1], x[2])
@@ -110,12 +124,12 @@ proc exec(x: seq[string]) =
 
 proc copy(x: seq[string]) =
   if x.len != 3: quit "!copy takes two arguments"
-  let rel = tpath()
+  let rel = tpath
   copyFile(rel / x[1], rel / x[2])
 
 proc del(x: seq[string]) =
   if x.len != 2: quit "!del takes one argument"
-  removeFile(tpath() / x[1])
+  removeFile(tpath / x[1])
 
 proc runCmd(cmd, dest: string): bool =
   result = cmd[0] == '!'
@@ -131,7 +145,7 @@ proc runCmd(cmd, dest: string): bool =
   of "!del":
     del(x)
   else:
-    quit "unkown command: " & cmd
+    quit "unknown command: " & cmd
 
 proc smartCompare(pattern, x: string): bool =
   if pattern.contains('*'):
@@ -197,14 +211,19 @@ proc sexpToAnswer(s: SexpNode): string =
       result.add '\t'
       result.add file
       result.add '\t'
-      result.add line
+      result.addInt line
       result.add '\t'
-      result.add col
+      result.addInt col
       result.add '\t'
       result.add doc
       result.add '\t'
-      result.add a[8].getNum
-      if a.len >= 10:
+      result.addInt a[8].getNum
+      if a.len >= 11:
+        result.add '\t'
+        result.addInt a[9].getNum
+        result.add '\t'
+        result.addInt a[10].getNum
+      elif a.len >= 10:
         result.add '\t'
         result.add a[9].getStr
     result.add '\L'
@@ -215,26 +234,38 @@ proc doReport(filename, answer, resp: string; report: var string) =
     var hasDiff = false
     for i in 0..min(resp.len-1, answer.len-1):
       if resp[i] != answer[i]:
-        report.add "\n  Expected:  " & resp.substr(i, i+200)
-        report.add "\n  But got:   " & answer.substr(i, i+200)
+        report.add "\n  Expected:\n" & resp
+        report.add "\n  But got:\n" & answer
         hasDiff = true
         break
     if not hasDiff:
       report.add "\n  Expected:  " & resp
       report.add "\n  But got:   " & answer
 
+proc skipDisabledTest(test: Test): bool =
+  if test.disabled:
+    echo "disabled: " & test.filename
+  result = test.disabled
+
 proc runEpcTest(filename: string): int =
   let s = parseTest(filename, true)
+  if s.skipDisabledTest: return 0
+  for req, _ in items(s.script):
+    if req.startsWith("highlight"):
+      echo "disabled epc: " & s.filename
+      return 0
   for cmd in s.startup:
     if not runCmd(cmd, s.dest):
       quit "invalid command: " & cmd
-  let epccmd = s.cmd.replace("--tester", "--epc --v2 --log")
+  let epccmd = if s.cmd.contains("--v3"):
+    s.cmd.replace("--tester", "--epc --log")
+  else:
+    s.cmd.replace("--tester", "--epc --v2 --log")
   let cl = parseCmdLine(epccmd)
   var p = startProcess(command=cl[0], args=cl[1 .. ^1],
                        options={poStdErrToStdOut, poUsePath,
-                       poInteractive, poDemon})
+                       poInteractive, poDaemon})
   let outp = p.outputStream
-  let inp = p.inputStream
   var report = ""
   var socket = newSocket()
   try:
@@ -248,17 +279,28 @@ proc runEpcTest(filename: string): int =
         os.sleep(50)
         inc i
       let a = outp.readAll().strip()
-    let port = parseInt(a)
+    var port: int
+    try:
+      port = parseInt(a)
+    except ValueError:
+      echo "Error parsing port number: " & a
+      echo outp.readAll()
+      quit 1
     socket.connect("localhost", Port(port))
+
     for req, resp in items(s.script):
       if not runCmd(req, s.dest):
         socket.sendEpcStr(req)
         let sx = parseSexp(socket.recvEpc())
         if not req.startsWith("mod "):
-          let answer = sexpToAnswer(sx)
+          let answer = if sx[2].kind == SNil: "" else: sexpToAnswer(sx)
           doReport(filename, answer, resp, report)
-  finally:
+
     socket.sendEpcStr "return arg"
+      # bugfix: this was in `finally` block, causing the original error to be
+      # potentially masked by another one in case `socket.sendEpcStr` raises
+      # (e.g. if socket couldn't connect in the 1st place)
+  finally:
     close(p)
   if report.len > 0:
     echo "==== EPC ========================================"
@@ -267,13 +309,14 @@ proc runEpcTest(filename: string): int =
 
 proc runTest(filename: string): int =
   let s = parseTest filename
+  if s.skipDisabledTest: return 0
   for cmd in s.startup:
     if not runCmd(cmd, s.dest):
       quit "invalid command: " & cmd
   let cl = parseCmdLine(s.cmd)
   var p = startProcess(command=cl[0], args=cl[1 .. ^1],
                        options={poStdErrToStdOut, poUsePath,
-                       poInteractive, poDemon})
+                       poInteractive, poDaemon})
   let outp = p.outputStream
   let inp = p.inputStream
   var report = ""
@@ -293,8 +336,12 @@ proc runTest(filename: string): int =
           answer.add '\L'
         doReport(filename, answer, resp, report)
   finally:
-    inp.writeLine("quit")
-    inp.flush()
+    try:
+      inp.writeLine("quit")
+      inp.flush()
+    except IOError, OSError:
+      # assume it's SIGPIPE, ie, the child already died
+      discard
     close(p)
   if report.len > 0:
     echo "==== STDIN ======================================"
@@ -306,11 +353,16 @@ proc main() =
   if os.paramCount() > 0:
     let x = os.paramStr(1)
     let xx = expandFilename x
+    # run only stdio when running single test
     failures += runTest(xx)
-    failures += runEpcTest(xx)
   else:
-    for x in walkFiles(getAppDir() / "tests/t*.nim"):
-      echo "Test ", x
+    let files = toSeq(walkFiles(tpath / "t*.nim"))
+    for i, x in files:
+      echo "$#/$# test: $#" % [$i, $files.len, x]
+      when defined(i386):
+        if x == "nimsuggest/tests/tmacro_highlight.nim":
+          echo "skipping" # workaround bug #17945
+          continue
       let xx = expandFilename x
       when not defined(windows):
         # XXX Windows IO redirection seems bonkers:
diff --git a/nimsuggest/tests/fixtures/mclass_macro.nim b/nimsuggest/tests/fixtures/mclass_macro.nim
new file mode 100644
index 000000000..cfca0bf3f
--- /dev/null
+++ b/nimsuggest/tests/fixtures/mclass_macro.nim
@@ -0,0 +1,164 @@
+
+import macros
+
+macro class*(head, body: untyped): untyped =
+  # The macro is immediate, since all its parameters are untyped.
+  # This means, it doesn't resolve identifiers passed to it.
+
+  var typeName, baseName: NimNode
+
+  # flag if object should be exported
+  var exported: bool
+
+  if head.kind == nnkInfix and head[0].kind == nnkIdent and $head[0] == "of":
+    # `head` is expression `typeName of baseClass`
+    # echo head.treeRepr
+    # --------------------
+    # Infix
+    #   Ident !"of"
+    #   Ident !"Animal"
+    #   Ident !"RootObj"
+    typeName = head[1]
+    baseName = head[2]
+
+  elif head.kind == nnkInfix and head[0].kind == nnkIdent and
+       $head[0] == "*" and head[2].kind == nnkPrefix and
+       head[2][0].kind == nnkIdent and $head[2][0] == "of":
+    # `head` is expression `typeName* of baseClass`
+    # echo head.treeRepr
+    # --------------------
+    # Infix
+    #   Ident !"*"
+    #   Ident !"Animal"
+    #   Prefix
+    #     Ident !"of"
+    #     Ident !"RootObj"
+    typeName = head[1]
+    baseName = head[2][1]
+    exported = true
+
+  else:
+    quit "Invalid node: " & head.lispRepr
+
+  # The following prints out the AST structure:
+  #
+  # import macros
+  # dumptree:
+  #   type X = ref object of Y
+  #     z: int
+  # --------------------
+  # StmtList
+  #   TypeSection
+  #     TypeDef
+  #       Ident !"X"
+  #       Empty
+  #       RefTy
+  #         ObjectTy
+  #           Empty
+  #           OfInherit
+  #             Ident !"Y"
+  #           RecList
+  #             IdentDefs
+  #               Ident !"z"
+  #               Ident !"int"
+  #               Empty
+
+  # create a type section in the result
+  result = newNimNode(nnkStmtList)
+  result.add(
+    if exported:
+      # mark `typeName` with an asterisk
+      quote do:
+        type `typeName`* = ref object of `baseName`
+    else:
+      quote do:
+        type `typeName` = ref object of `baseName`
+  )
+
+  # echo treeRepr(body)
+  # --------------------
+  # StmtList
+  #   VarSection
+  #     IdentDefs
+  #       Ident !"name"
+  #       Ident !"string"
+  #       Empty
+  #     IdentDefs
+  #       Ident !"age"
+  #       Ident !"int"
+  #       Empty
+  #   MethodDef
+  #     Ident !"vocalize"
+  #     Empty
+  #     Empty
+  #     FormalParams
+  #       Ident !"string"
+  #     Empty
+  #     Empty
+  #     StmtList
+  #       StrLit ...
+  #   MethodDef
+  #     Ident !"age_human_yrs"
+  #     Empty
+  #     Empty
+  #     FormalParams
+  #       Ident !"int"
+  #     Empty
+  #     Empty
+  #     StmtList
+  #       DotExpr
+  #         Ident !"this"
+  #         Ident !"age"
+
+  # var declarations will be turned into object fields
+  var recList = newNimNode(nnkRecList)
+
+  # expected name of constructor
+  let ctorName = newIdentNode("new" & $typeName)
+
+  # Iterate over the statements, adding `this: T`
+  # to the parameters of functions, unless the
+  # function is a constructor
+  for node in body.children:
+    case node.kind:
+
+      of nnkMethodDef, nnkProcDef:
+        # check if it is the ctor proc
+        if node.name.kind != nnkAccQuoted and node.name.basename == ctorName:
+          # specify the return type of the ctor proc
+          node.params[0] = typeName
+        else:
+          # inject `self: T` into the arguments
+          node.params.insert(1, newIdentDefs(ident("self"), typeName))
+        result.add(node)
+
+      of nnkVarSection:
+        # variables get turned into fields of the type.
+        for n in node.children:
+          recList.add(n)
+
+      else:
+        result.add(node)
+
+  # Inspect the tree structure:
+  #
+  # echo result.treeRepr
+  # --------------------
+  # StmtList
+  #   TypeSection
+  #     TypeDef
+  #       Ident !"Animal"
+  #       Empty
+  #       RefTy
+  #         ObjectTy
+  #           Empty
+  #           OfInherit
+  #             Ident !"RootObj"
+  #           Empty   <= We want to replace this
+  # MethodDef
+  # ...
+
+  result[0][0][2][0][2] = recList
+
+  # Lets inspect the human-readable version of the output
+  #echo repr(result)
diff --git a/nimsuggest/tests/dep_v1.nim b/nimsuggest/tests/fixtures/mdep_v1.nim
index eae230e85..eae230e85 100644
--- a/nimsuggest/tests/dep_v1.nim
+++ b/nimsuggest/tests/fixtures/mdep_v1.nim
diff --git a/nimsuggest/tests/dep_v2.nim b/nimsuggest/tests/fixtures/mdep_v2.nim
index ab39721c4..ab39721c4 100644
--- a/nimsuggest/tests/dep_v2.nim
+++ b/nimsuggest/tests/fixtures/mdep_v2.nim
diff --git a/nimsuggest/tests/fixtures/mfakeassert.nim b/nimsuggest/tests/fixtures/mfakeassert.nim
new file mode 100644
index 000000000..765831ba7
--- /dev/null
+++ b/nimsuggest/tests/fixtures/mfakeassert.nim
@@ -0,0 +1,5 @@
+# Template for testing defs
+
+template fakeAssert*(cond: untyped, msg: string = "") =
+  ## template to allow def lookup testing
+  if not cond: quit(1)
diff --git a/nimsuggest/tests/fixtures/minclude_import.nim b/nimsuggest/tests/fixtures/minclude_import.nim
new file mode 100644
index 000000000..5fa9e5142
--- /dev/null
+++ b/nimsuggest/tests/fixtures/minclude_import.nim
@@ -0,0 +1,15 @@
+# Creates an awkward set of dependencies between this, import, and include.
+# This pattern appears in the compiler, compiler/(sem|ast|semexprs).nim.
+
+import mfakeassert
+import minclude_types
+
+proc say*(g: Greet): string =
+  fakeAssert(true, "always works")
+  g.greeting & ", " & g.subject & "!"
+
+include minclude_include
+
+proc say*(): string =
+  fakeAssert(1 + 1 == 2, "math works")
+  say(create())
diff --git a/nimsuggest/tests/fixtures/minclude_include.nim b/nimsuggest/tests/fixtures/minclude_include.nim
new file mode 100644
index 000000000..23f9892cc
--- /dev/null
+++ b/nimsuggest/tests/fixtures/minclude_include.nim
@@ -0,0 +1,4 @@
+# this file is included and relies on imports within the include
+
+proc create*(greeting: string = "Hello", subject: string = "World"): Greet =
+  Greet(greeting: greeting, subject: subject)
diff --git a/nimsuggest/tests/fixtures/minclude_types.nim b/nimsuggest/tests/fixtures/minclude_types.nim
new file mode 100644
index 000000000..3e85ee540
--- /dev/null
+++ b/nimsuggest/tests/fixtures/minclude_types.nim
@@ -0,0 +1,6 @@
+# types used by minclude_* (import or include), to find with def in include
+
+type
+  Greet* = object
+    greeting*: string
+    subject*: string
\ No newline at end of file
diff --git a/nimsuggest/tests/fixtures/mstrutils.nim b/nimsuggest/tests/fixtures/mstrutils.nim
new file mode 100644
index 000000000..d6f25571b
--- /dev/null
+++ b/nimsuggest/tests/fixtures/mstrutils.nim
@@ -0,0 +1,19 @@
+import mfakeassert
+
+func rereplace*(s, sub: string; by: string = ""): string {.used.} =
+  ## competes for priority in suggestion, here first, but never used in test
+
+  fakeAssert(true, "always works")
+  result = by
+
+func replace*(s, sub: string; by: string = ""): string =
+  ## this is a test version of strutils.replace, it simply returns `by`
+
+  fakeAssert("".len == 0, "empty string is empty")
+  result = by
+
+func rerereplace*(s, sub: string; by: string = ""): string {.used.} =
+  ## isn't used and appears last, lowest priority
+
+  fakeAssert(false, "never works")
+  result = by
diff --git a/nimsuggest/tests/module_20265.nim b/nimsuggest/tests/module_20265.nim
new file mode 100644
index 000000000..24b7d10c9
--- /dev/null
+++ b/nimsuggest/tests/module_20265.nim
@@ -0,0 +1,6 @@
+type A* = tuple
+  a: int
+  b: int
+
+var x*: A = (a: 2, b: 10)
+var y* = (a: 2, b: 10)
diff --git a/nimsuggest/tests/t20265_1.nim b/nimsuggest/tests/t20265_1.nim
new file mode 100644
index 000000000..553b3d545
--- /dev/null
+++ b/nimsuggest/tests/t20265_1.nim
@@ -0,0 +1,8 @@
+discard """
+$nimsuggest --tester $file
+>sug $1
+sug;;skField;;a;;int;;*module_20265.nim;;6;;10;;"";;100;;None
+sug;;skField;;b;;int;;*module_20265.nim;;6;;16;;"";;100;;None
+"""
+import module_20265
+y.#[!]#
diff --git a/nimsuggest/tests/t20265_2.nim b/nimsuggest/tests/t20265_2.nim
new file mode 100644
index 000000000..33edf2d9a
--- /dev/null
+++ b/nimsuggest/tests/t20265_2.nim
@@ -0,0 +1,8 @@
+discard """
+$nimsuggest --tester $file
+>sug $1
+sug;;skField;;a;;int;;*module_20265.nim;;2;;2;;"";;100;;None
+sug;;skField;;b;;int;;*module_20265.nim;;3;;2;;"";;100;;None
+"""
+import module_20265
+x.#[!]#
diff --git a/nimsuggest/tests/t20440.nim b/nimsuggest/tests/t20440.nim
new file mode 100644
index 000000000..0456aa074
--- /dev/null
+++ b/nimsuggest/tests/t20440.nim
@@ -0,0 +1,7 @@
+when not defined(js):
+  {.fatal: "Crash".}
+echo 4
+
+discard """
+$nimsuggest --v3 --tester $file
+"""
diff --git a/nimsuggest/tests/t20440.nims b/nimsuggest/tests/t20440.nims
new file mode 100644
index 000000000..1336be3d4
--- /dev/null
+++ b/nimsuggest/tests/t20440.nims
@@ -0,0 +1 @@
+switch("backend", "js")
diff --git a/nimsuggest/tests/t21185.nim b/nimsuggest/tests/t21185.nim
new file mode 100644
index 000000000..bf5a0e3cc
--- /dev/null
+++ b/nimsuggest/tests/t21185.nim
@@ -0,0 +1,18 @@
+
+# Reduced case of 21185. Issue was first parameter being static
+proc foo(x: static[int]) = discard
+
+type
+  Person = object
+    name: string
+    age: int
+
+let p = Person()
+p.#[!]#
+
+discard """
+$nimsuggest --tester --v3 --maxresults:2 $file
+>sug $1
+sug;;skField;;age;;int;;$file;;8;;4;;"";;100;;None
+sug;;skField;;name;;string;;$file;;7;;4;;"";;100;;None
+"""
diff --git a/nimsuggest/tests/t22448.nim b/nimsuggest/tests/t22448.nim
new file mode 100644
index 000000000..8664bbbc3
--- /dev/null
+++ b/nimsuggest/tests/t22448.nim
@@ -0,0 +1,11 @@
+proc fn(a: static float) = discard
+proc fn(a: int) = discard
+
+let x = 1
+fn(x)
+
+discard """
+$nimsuggest --tester --v3 $file
+>chk $file
+chk;;skUnknown;;;;Hint;;*
+"""
diff --git a/nimsuggest/tests/taccent_highlight.nim b/nimsuggest/tests/taccent_highlight.nim
new file mode 100644
index 000000000..52ac2fc62
--- /dev/null
+++ b/nimsuggest/tests/taccent_highlight.nim
@@ -0,0 +1,7 @@
+proc `$$$`#[!]#
+
+discard """
+$nimsuggest --tester $file
+>highlight $1
+highlight;;skProc;;1;;6;;3
+"""
diff --git a/nimsuggest/tests/tarrowcrash.nim b/nimsuggest/tests/tarrowcrash.nim
new file mode 100644
index 000000000..a303e88f5
--- /dev/null
+++ b/nimsuggest/tests/tarrowcrash.nim
@@ -0,0 +1,20 @@
+# issue #24179
+
+import sugar
+
+type
+    Parser[T] = object
+    
+proc eatWhile[T](p: Parser[T], predicate: T -> bool): seq[T] =
+    return @[]
+
+proc skipWs(p: Parser[char]) =
+    discard p.eatWhile((c: char) => c == 'a')
+#[!]#
+    
+discard """
+$nimsuggest --tester $file
+>chk $1
+chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tarrowcrash.nim [Processing]";;0
+chk;;skUnknown;;;;Hint;;$file;;11;;5;;"\'skipWs\' is declared but not used [XDeclaredButNotUsed]";;0
+"""
diff --git a/nimsuggest/tests/tcallstrlit_highlight.nim b/nimsuggest/tests/tcallstrlit_highlight.nim
new file mode 100644
index 000000000..6f5b0f792
--- /dev/null
+++ b/nimsuggest/tests/tcallstrlit_highlight.nim
@@ -0,0 +1,11 @@
+func foo(s: string) = discard
+
+foo"string"#[!]#
+
+discard """
+$nimsuggest --tester $file
+>highlight $1
+highlight;;skFunc;;1;;5;;3
+highlight;;skType;;1;;12;;6
+highlight;;skFunc;;3;;0;;3
+"""
diff --git a/nimsuggest/tests/tchk1.nim b/nimsuggest/tests/tchk1.nim
index 7b0b5d5b4..be6115c1c 100644
--- a/nimsuggest/tests/tchk1.nim
+++ b/nimsuggest/tests/tchk1.nim
@@ -17,11 +17,11 @@ proc main =
 discard """
 $nimsuggest --tester $file
 >chk $1
-chk;;skUnknown;;;;Hint;;???;;-1;;-1;;"tchk1 [Processing]";;0
-chk;;skUnknown;;;;Error;;$file;;12;;0;;"identifier expected, but found \'keyword template\'";;0
-chk;;skUnknown;;;;Error;;$file;;14;;0;;"complex statement requires indentation";;0
-chk;;skUnknown;;;;Error;;$file;;12;;0;;"implementation of \'foo\' expected";;0
+chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tchk1.nim [Processing]";;0
+chk;;skUnknown;;;;Error;;$file;;12;;0;;"identifier expected, but got \'keyword template\'";;0
+chk;;skUnknown;;;;Error;;$file;;14;;0;;"nestable statement requires indentation";;0
 chk;;skUnknown;;;;Error;;$file;;17;;0;;"invalid indentation";;0
+chk;;skUnknown;;;;Error;;$file;;12;;0;;"implementation of \'foo\' expected";;0
 chk;;skUnknown;;;;Hint;;$file;;12;;9;;"\'foo\' is declared but not used [XDeclaredButNotUsed]";;0
-chk;;skUnknown;;;;Hint;;$file;;14;;5;;"\'tchk1.main()[declared in tchk1.nim(14, 5)]\' is declared but not used [XDeclaredButNotUsed]";;0
+chk;;skUnknown;;;;Hint;;$file;;14;;5;;"\'main\' is declared but not used [XDeclaredButNotUsed]";;0
 """
diff --git a/nimsuggest/tests/tchk2.nim b/nimsuggest/tests/tchk2.nim
new file mode 100644
index 000000000..f5404368d
--- /dev/null
+++ b/nimsuggest/tests/tchk2.nim
@@ -0,0 +1,35 @@
+# bug #22794
+type O = object
+
+proc `=destroy`(x: O) = discard
+proc `=trace`(x: var O; env: pointer) = discard
+proc `=copy`(a: var O; b: O) = discard
+proc `=dup`(a: O): O {.nodestroy.} = a
+proc `=sink`(a: var O; b: O) = discard
+
+
+# bug #23316
+type SomeSturct = object
+
+proc `=destroy`(x: SomeSturct) =
+  echo "SomeSturct destroyed"
+
+# bug #23867
+type ObjStr = object
+  s: string
+
+let ostr = ObjStr() # <-- nimsuggest crashes
+discard ostr
+
+type ObjSeq = object
+  s: seq[int]
+
+let oseq = ObjSeq() # <-- nimsuggest crashes
+discard oseq
+
+#[!]#
+discard """
+$nimsuggest --tester $file
+>chk $1
+chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tchk2.nim [Processing]";;0
+"""
diff --git a/nimsuggest/tests/tchk_compiles.nim b/nimsuggest/tests/tchk_compiles.nim
new file mode 100644
index 000000000..c8a3daac4
--- /dev/null
+++ b/nimsuggest/tests/tchk_compiles.nim
@@ -0,0 +1,8 @@
+discard compiles(2 + "hello")
+
+#[!]#
+discard """
+$nimsuggest --tester $file
+>chk $1
+chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tchk_compiles.nim [Processing]";;0
+"""
diff --git a/nimsuggest/tests/tcon1.nim b/nimsuggest/tests/tcon1.nim
index 262dd5151..627e7f400 100644
--- a/nimsuggest/tests/tcon1.nim
+++ b/nimsuggest/tests/tcon1.nim
@@ -1,13 +1,43 @@
+## Test Invocation `con`text in various situations
+
+## various of this proc are used as the basis for these tests
 proc test(s: string; a: int) = discard
+
+## This overload should be used to ensure the lower airity `test` doesn't match
+proc test(s: string; a: string, b: int) = discard
+
+## similar signature but different name to ensure `con` doesn't get greedy
 proc testB(a, b: string) = discard
+
+# with a param already specified
 test("hello here", #[!]#)
+
+# as first param
 testB(#[!]#
 
+# dot expressions
+"from behind".test(#[!]#
+
+# two params matched, so disqualify the lower airity `test`
+# TODO: this doesn't work, because dot exprs, overloads, etc aren't currently
+#       handled by suggest.suggestCall. sigmatch.partialMatch by way of
+#       sigmatch.matchesAux. Doesn't use the operand before the dot as part of
+#       the formal parameters. Changing this is tricky because it's used by
+#       the proper compilation sem pass and that's a big change all in one go.
+"and again".test("more", #[!]#
+
 
 discard """
 $nimsuggest --tester $file
 >con $1
-con;;skProc;;tcon1.test;;proc (s: string, a: int);;$file;;1;;5;;"";;100
+con;;skProc;;tcon1.test;;proc (s: string, a: int);;$file;;4;;5;;"";;100
+con;;skProc;;tcon1.test;;proc (s: string, a: string, b: int);;$file;;7;;5;;"";;100
 >con $2
-con;;skProc;;tcon1.testB;;proc (a: string, b: string);;$file;;2;;5;;"";;100
+con;;skProc;;tcon1.testB;;proc (a: string, b: string);;$file;;10;;5;;"";;100
+>con $3
+con;;skProc;;tcon1.test;;proc (s: string, a: string, b: int);;$file;;7;;5;;"";;100
+con;;skProc;;tcon1.test;;proc (s: string, a: int);;$file;;4;;5;;"";;100
+>con $4
+con;;skProc;;tcon1.test;;proc (s: string, a: int);;$file;;4;;5;;"";;100
+con;;skProc;;tcon1.test;;proc (s: string, a: string, b: int);;$file;;7;;5;;"";;100
 """
diff --git a/nimsuggest/tests/tcon_variable.nim b/nimsuggest/tests/tcon_variable.nim
new file mode 100644
index 000000000..cfe93604f
--- /dev/null
+++ b/nimsuggest/tests/tcon_variable.nim
@@ -0,0 +1,12 @@
+let foo = "string"
+var bar = "string"
+bar#[!]#.add foo
+bar.add foo#[!]#
+
+discard """
+$nimsuggest --tester $file
+>con $1
+con;;skVar;;tcon_variable.bar;;string;;$file;;2;;4;;"";;100
+>con $2
+con;;skLet;;tcon_variable.foo;;string;;$file;;1;;4;;"";;100
+"""
diff --git a/nimsuggest/tests/tconcept1.nim b/nimsuggest/tests/tconcept1.nim
new file mode 100644
index 000000000..d81cd8120
--- /dev/null
+++ b/nimsuggest/tests/tconcept1.nim
@@ -0,0 +1,12 @@
+SomeNumber = concept
+
+#[!]#
+discard """
+$nimsuggest --tester $file
+>chk $1
+chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tconcept1.nim [Processing]";;0
+chk;;skUnknown;;;;Error;;$file;;1;;13;;"the \'concept\' keyword is only valid in \'type\' sections";;0
+chk;;skUnknown;;;;Error;;$file;;1;;13;;"invalid indentation";;0
+chk;;skUnknown;;;;Error;;$file;;1;;13;;"expression expected, but found \'keyword concept\'";;0
+chk;;skUnknown;;;;Error;;$file;;1;;0;;"\'SomeNumber\' cannot be assigned to";;0
+"""
diff --git a/nimsuggest/tests/tconcept2.nim b/nimsuggest/tests/tconcept2.nim
new file mode 100644
index 000000000..7f7d147f5
--- /dev/null
+++ b/nimsuggest/tests/tconcept2.nim
@@ -0,0 +1,15 @@
+  SomeNumber = concept a, type T
+    a.int is int
+    int.to(T) is type(a)
+
+#[!]#
+discard """
+$nimsuggest --tester $file
+>chk $1
+chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tconcept2.nim [Processing]";;0
+chk;;skUnknown;;;;Error;;$file;;1;;2;;"invalid indentation";;0
+chk;;skUnknown;;;;Error;;$file;;1;;15;;"the \'concept\' keyword is only valid in \'type\' sections";;0
+chk;;skUnknown;;;;Error;;$file;;1;;15;;"invalid indentation";;0
+chk;;skUnknown;;;;Error;;$file;;1;;15;;"expression expected, but found \'keyword concept\'";;0
+chk;;skUnknown;;;;Error;;$file;;1;;2;;"\'SomeNumber\' cannot be assigned to";;0
+"""
diff --git a/nimsuggest/tests/tdef1.nim b/nimsuggest/tests/tdef1.nim
index 2cd040ea1..49265bbc1 100644
--- a/nimsuggest/tests/tdef1.nim
+++ b/nimsuggest/tests/tdef1.nim
@@ -1,12 +1,14 @@
 discard """
 $nimsuggest --tester $file
 >def $1
-def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe, locks: 0.};;$file;;9;;5;;"Return hello";;100
->def $1
-def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe, locks: 0.};;$file;;9;;5;;"Return hello";;100
+def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;11;;5;;"Return hello";;100
+>def $2
+def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;11;;5;;"Return hello";;100
+>def $2
+def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;11;;5;;"Return hello";;100
 """
 
-proc hello(): string =
+proc hel#[!]#lo(): string =
   ## Return hello
   "Hello"
 
diff --git a/nimsuggest/tests/tdef2.nim b/nimsuggest/tests/tdef2.nim
new file mode 100644
index 000000000..299b83a3d
--- /dev/null
+++ b/nimsuggest/tests/tdef2.nim
@@ -0,0 +1,13 @@
+# Test def with template and boundaries for the cursor
+
+import fixtures/mstrutils
+
+discard """
+$nimsuggest --tester $file
+>def $path/fixtures/mstrutils.nim:6:4
+def;;skTemplate;;mfakeassert.fakeAssert;;template (cond: untyped, msg: string);;*fixtures/mfakeassert.nim;;3;;9;;"template to allow def lookup testing";;100
+>def $path/fixtures/mstrutils.nim:12:3
+def;;skTemplate;;mfakeassert.fakeAssert;;template (cond: untyped, msg: string);;*fixtures/mfakeassert.nim;;3;;9;;"template to allow def lookup testing";;100
+>def $path/fixtures/mstrutils.nim:18:11
+def;;skTemplate;;mfakeassert.fakeAssert;;template (cond: untyped, msg: string);;*fixtures/mfakeassert.nim;;3;;9;;"template to allow def lookup testing";;100
+"""
diff --git a/nimsuggest/tests/tdef_forward.nim b/nimsuggest/tests/tdef_forward.nim
new file mode 100644
index 000000000..9bdd8b21d
--- /dev/null
+++ b/nimsuggest/tests/tdef_forward.nim
@@ -0,0 +1,13 @@
+discard """
+$nimsuggest --tester $file
+>def $1
+def;;skProc;;tdef_forward.hello;;proc (): string;;$file;;8;;5;;"";;100
+def;;skProc;;tdef_forward.hello;;proc (): string;;$file;;12;;5;;"";;100
+"""
+
+proc hello(): string
+
+hel#[!]#lo()
+
+proc hello(): string =
+  "Hello"
diff --git a/nimsuggest/tests/tdef_let.nim b/nimsuggest/tests/tdef_let.nim
new file mode 100644
index 000000000..3e9456d2f
--- /dev/null
+++ b/nimsuggest/tests/tdef_let.nim
@@ -0,0 +1,7 @@
+discard """
+$nimsuggest --tester $file
+>def $1
+def;;skLet;;tdef_let.intVar;;int;;$file;;7;;4;;"";;100
+"""
+
+let int#[!]#Var = 10
diff --git a/nimsuggest/tests/tdot1.nim b/nimsuggest/tests/tdot1.nim
index 9ac92f8a5..c64e1138c 100644
--- a/nimsuggest/tests/tdot1.nim
+++ b/nimsuggest/tests/tdot1.nim
@@ -1,5 +1,5 @@
 discard """
-$nimsuggest --tester $file
+$nimsuggest --tester --maxresults:3 $file
 >sug $1
 sug;;skField;;x;;int;;$file;;11;;4;;"";;100;;None
 sug;;skField;;y;;int;;$file;;11;;7;;"";;100;;None
diff --git a/nimsuggest/tests/tdot2.nim b/nimsuggest/tests/tdot2.nim
index f02b5cf16..1a2df9ba2 100644
--- a/nimsuggest/tests/tdot2.nim
+++ b/nimsuggest/tests/tdot2.nim
@@ -15,7 +15,7 @@ proc main(f: Foo) =
 # the tester supports the spec section at the bottom of the file and
 # this way, the line numbers more often stay the same
 discard """
-$nimsuggest --tester $file
+$nimsuggest --tester --maxresults:3 $file
 >sug $1
 sug;;skField;;x;;int;;$file;;8;;4;;"";;100;;None
 sug;;skField;;y;;int;;$file;;8;;7;;"";;100;;None
@@ -25,5 +25,4 @@ sug;;skProc;;tdot2.main;;proc (f: Foo);;$file;;12;;5;;"";;100;;None
 sug;;skField;;x;;int;;$file;;8;;4;;"";;100;;None
 sug;;skField;;y;;int;;$file;;8;;7;;"";;100;;None
 sug;;skField;;z;;string;;$file;;10;;6;;"";;100;;None
-sug;;skProc;;tdot2.main;;proc (f: Foo);;$file;;12;;5;;"";;100;;None
 """
diff --git a/nimsuggest/tests/tdot3.nim b/nimsuggest/tests/tdot3.nim
index 15fc1cd1c..30dd60591 100644
--- a/nimsuggest/tests/tdot3.nim
+++ b/nimsuggest/tests/tdot3.nim
@@ -9,14 +9,14 @@ proc main(f: Foo) =
 # this way, the line numbers more often stay the same
 
 discard """
-!copy dep_v1.nim dep.nim
+!copy fixtures/mdep_v1.nim dep.nim
 $nimsuggest --tester $file
 >sug $1
 sug;;skField;;x;;int;;*dep.nim;;8;;4;;"";;100;;None
 sug;;skField;;y;;int;;*dep.nim;;8;;8;;"";;100;;None
 sug;;skProc;;tdot3.main;;proc (f: Foo);;$file;;5;;5;;"";;100;;None
 
-!copy dep_v2.nim dep.nim
+!copy fixtures/mdep_v2.nim dep.nim
 >mod $path/dep.nim
 >sug $1
 sug;;skField;;x;;int;;*dep.nim;;8;;4;;"";;100;;None
diff --git a/nimsuggest/tests/tdot4.nim b/nimsuggest/tests/tdot4.nim
index 25e77ed04..f2c6c765f 100644
--- a/nimsuggest/tests/tdot4.nim
+++ b/nimsuggest/tests/tdot4.nim
@@ -1,16 +1,21 @@
-discard """
-$nimsuggest --tester --maxresults:2 $file
->sug $1
-sug;;skProc;;tdot4.main;;proc (inp: string): string;;$file;;10;;5;;"";;100;;None
-sug;;skProc;;strutils.replace;;proc (s: string, sub: string, by: string): string{.noSideEffect, gcsafe, locks: 0.};;$lib/pure/strutils.nim;;1575;;5;;"Replaces `sub` in `s` by the string `by`.";;100;;None
-"""
+# Test that already used suggestions are prioritized
 
-import strutils
+from system import string, echo
+import fixtures/mstrutils
 
 proc main(inp: string): string =
   # use replace here and see if it occurs in the result, it should gain
   # priority:
   result = inp.replace(" ", "a").replace("b", "c")
 
-
 echo "string literal here".#[!]#
+
+# priority still tested, but limit results to avoid failures from other output
+discard """
+$nimsuggest --tester --maxresults:2 $file
+>sug $1
+sug;;skProc;;tdot4.main;;proc (inp: string): string;;$file;;6;;5;;"";;100;;None
+sug;;skFunc;;mstrutils.replace;;proc (s: string, sub: string, by: string): string{.noSideEffect, gcsafe, raises: <inferred> [].};;*fixtures/mstrutils.nim;;9;;5;;"this is a test version of strutils.replace, it simply returns `by`";;100;;None
+"""
+
+# TODO - determine appropriate behaviour for further suggest output and test it
diff --git a/nimsuggest/tests/tenum_field.nim b/nimsuggest/tests/tenum_field.nim
new file mode 100644
index 000000000..4ceb3e021
--- /dev/null
+++ b/nimsuggest/tests/tenum_field.nim
@@ -0,0 +1,17 @@
+discard """
+$nimsuggest --tester $file
+>sug $1
+>sug $2
+sug;;skConst;;tenum_field.BarFoo;;int literal(1);;$file;;10;;6;;"";;100;;Prefix
+"""
+
+proc something() = discard
+
+const BarFoo = 1
+
+type
+  Foo = enum
+    # Test that typing the name doesn't give suggestions
+    somethi#[!]#
+    # Test that the right hand side still gets suggestions
+    another = BarFo#[!]#
diff --git a/nimsuggest/tests/tfatal1.nim b/nimsuggest/tests/tfatal1.nim
new file mode 100644
index 000000000..19778f22e
--- /dev/null
+++ b/nimsuggest/tests/tfatal1.nim
@@ -0,0 +1,15 @@
+{.warning: "I'm a warning!".}
+{.error: "I'm an error!".}
+{.fatal: "I'm a fatal error!".}
+{.error: "I'm an error after fatal error!".}
+
+#[!]#
+discard """
+$nimsuggest --tester $file
+>chk $1
+chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tfatal1.nim [Processing]";;0
+chk;;skUnknown;;;;Warning;;$file;;1;;9;;"I\'m a warning! [User]";;0
+chk;;skUnknown;;;;Error;;$file;;2;;7;;"I\'m an error!";;0
+chk;;skUnknown;;;;Error;;$file;;3;;7;;"fatal error: I\'m a fatal error!";;0
+chk;;skUnknown;;;;Error;;$file;;4;;7;;"I\'m an error after fatal error!";;0
+"""
diff --git a/nimsuggest/tests/tgeneric_highlight.nim b/nimsuggest/tests/tgeneric_highlight.nim
new file mode 100644
index 000000000..c7291d08b
--- /dev/null
+++ b/nimsuggest/tests/tgeneric_highlight.nim
@@ -0,0 +1,13 @@
+newSeq[int]()
+system.newSeq[int]()#[!]#
+offsetOf[int]()
+
+discard """
+$nimsuggest --tester $file
+>highlight $1
+highlight;;skType;;1;;7;;3
+highlight;;skProc;;1;;0;;6
+highlight;;skType;;2;;14;;3
+highlight;;skProc;;2;;7;;6
+highlight;;skType;;3;;9;;3
+"""
diff --git a/nimsuggest/tests/tgenerics.nim b/nimsuggest/tests/tgenerics.nim
new file mode 100644
index 000000000..7f490321c
--- /dev/null
+++ b/nimsuggest/tests/tgenerics.nim
@@ -0,0 +1,18 @@
+type
+  Hello[T] = object
+    value: T
+
+proc printHelloValue[T](hello: Hello[T]) =
+  echo hello.value
+
+proc main() =
+  let a = Hello[float]()
+  p#[!]#rintHelloValue(a)
+
+main()
+
+discard """
+$nimsuggest --tester $file
+>def $1
+def;;skProc;;tgenerics.printHelloValue;;proc (hello: Hello[printHelloValue.T]);;$file;;5;;5;;"";;100
+"""
diff --git a/nimsuggest/tests/tic.nim b/nimsuggest/tests/tic.nim
new file mode 100644
index 000000000..26e644f83
--- /dev/null
+++ b/nimsuggest/tests/tic.nim
@@ -0,0 +1,20 @@
+import std/[appdirs, assertions, cmdline, compilesettings, decls, 
+  dirs, editdistance, effecttraits, enumerate, enumutils, envvars, 
+  exitprocs, files, formatfloat, genasts, importutils, 
+  isolation, jsonutils, logic, monotimes, objectdollar, 
+  oserrors, outparams, packedsets, paths, private, setutils, sha1, 
+  socketstreams, stackframes, staticos, strbasics, symlinks, syncio, 
+  sysatomics, sysrand, tasks, tempfiles, time_t, typedthreads, varints, 
+  vmutils, widestrs, with, wordwrap, wrapnils]
+
+proc test(a: string, b:string) = discard
+proc test(a: int) = discard
+
+test(#[!]#
+
+discard """
+$nimsuggest --v3 --ic:off --tester $file 
+>con $1
+con;;skProc;;tic.test;;proc (a: string, b: string);;$file;;10;;5;;"";;100
+con;;skProc;;tic.test;;proc (a: int);;$file;;11;;5;;"";;100
+"""
\ No newline at end of file
diff --git a/nimsuggest/tests/timport_highlight.nim b/nimsuggest/tests/timport_highlight.nim
new file mode 100644
index 000000000..043f87d98
--- /dev/null
+++ b/nimsuggest/tests/timport_highlight.nim
@@ -0,0 +1,12 @@
+import std/paths
+import json as J
+import std/[os,streams]#[!]#
+
+discard """
+$nimsuggest --tester $file
+>highlight $1
+highlight;;skModule;;1;;11;;5
+highlight;;skModule;;2;;7;;4
+highlight;;skModule;;3;;12;;2
+highlight;;skModule;;3;;15;;7
+"""
diff --git a/nimsuggest/tests/tinclude.nim b/nimsuggest/tests/tinclude.nim
index 12d40d1e7..f5cbabf05 100644
--- a/nimsuggest/tests/tinclude.nim
+++ b/nimsuggest/tests/tinclude.nim
@@ -1,7 +1,25 @@
+# import that has an include:
+# * def calls must work into and out of includes
+# * outline calls on the import must show included members
+import fixtures/minclude_import
+
+proc go() =
+  discard create().say()
+
+go()
+
 discard """
-$nimsuggest --tester compiler/nim.nim
->def compiler/semexprs.nim:13:50
-def;;skType;;ast.PSym;;PSym;;*ast.nim;;691;;2;;"";;100
->def compiler/semexprs.nim:13:50
-def;;skType;;ast.PSym;;PSym;;*ast.nim;;691;;2;;"";;100
+$nimsuggest --tester $file
+>def $path/tinclude.nim:7:14
+def;;skProc;;minclude_import.create;;proc (greeting: string, subject: string): Greet{.noSideEffect, gcsafe, raises: <inferred> [].};;*fixtures/minclude_include.nim;;3;;5;;"";;100
+>def $path/fixtures/minclude_include.nim:3:71
+def;;skType;;minclude_types.Greet;;Greet;;*fixtures/minclude_types.nim;;4;;2;;"";;100
+>def $path/fixtures/minclude_include.nim:3:71
+def;;skType;;minclude_types.Greet;;Greet;;*fixtures/minclude_types.nim;;4;;2;;"";;100
+>outline $path/fixtures/minclude_import.nim
+outline;;skProc;;minclude_import.say;;*fixtures/minclude_import.nim;;7;;5;;"";;100
+outline;;skProc;;minclude_import.create;;*fixtures/minclude_include.nim;;3;;5;;"";;100
+outline;;skProc;;minclude_import.say;;*fixtures/minclude_import.nim;;13;;5;;"";;100
 """
+
+# TODO test/fix if the first `def` is not first or repeated we get no results
diff --git a/nimsuggest/tests/tmacro_highlight.nim b/nimsuggest/tests/tmacro_highlight.nim
new file mode 100644
index 000000000..6f5b5e8a7
--- /dev/null
+++ b/nimsuggest/tests/tmacro_highlight.nim
@@ -0,0 +1,13 @@
+macro a(b: string): untyped = discard
+
+a "string"#[!]#
+
+discard """
+$nimsuggest --tester $file
+>highlight $1
+highlight;;skMacro;;1;;6;;1
+highlight;;skType;;1;;11;;6
+highlight;;skType;;1;;20;;7
+highlight;;skMacro;;3;;0;;1
+highlight;;skMacro;;3;;0;;1
+"""
diff --git a/nimsuggest/tests/tobj_highlight.nim b/nimsuggest/tests/tobj_highlight.nim
new file mode 100644
index 000000000..c37bab183
--- /dev/null
+++ b/nimsuggest/tests/tobj_highlight.nim
@@ -0,0 +1,11 @@
+type
+  O = object
+    a*: int#[!]#
+
+discard """
+$nimsuggest --tester $file
+>highlight $1
+highlight;;skType;;2;;2;;1
+highlight;;skType;;3;;8;;3
+highlight;;skField;;3;;4;;1
+"""
diff --git a/nimsuggest/tests/top_highlight.nim b/nimsuggest/tests/top_highlight.nim
new file mode 100644
index 000000000..c3f6bb61d
--- /dev/null
+++ b/nimsuggest/tests/top_highlight.nim
@@ -0,0 +1,11 @@
+import json
+
+%*{}#[!]#
+
+discard """
+$nimsuggest --tester $file
+>highlight $1
+highlight;;skModule;;1;;7;;4
+highlight;;skMacro;;3;;0;;2
+highlight;;skMacro;;3;;0;;2
+"""
diff --git a/nimsuggest/tests/tqualified_highlight.nim b/nimsuggest/tests/tqualified_highlight.nim
new file mode 100644
index 000000000..b83669e72
--- /dev/null
+++ b/nimsuggest/tests/tqualified_highlight.nim
@@ -0,0 +1,14 @@
+system.echo#[!]#
+system.once
+system.`$` 1
+
+discard """
+$nimsuggest --tester $file
+>highlight $1
+highlight;;skProc;;1;;7;;4
+highlight;;skProc;;1;;7;;4
+highlight;;skTemplate;;2;;7;;4
+highlight;;skTemplate;;2;;7;;4
+highlight;;skTemplate;;2;;7;;4
+highlight;;skFunc;;3;;8;;1
+"""
diff --git a/nimsuggest/tests/tsetter_highlight.nim b/nimsuggest/tests/tsetter_highlight.nim
new file mode 100644
index 000000000..e7388c798
--- /dev/null
+++ b/nimsuggest/tests/tsetter_highlight.nim
@@ -0,0 +1,10 @@
+proc `a=`(a, b: int) = discard
+10.a = 1000#[!]#
+
+discard """
+$nimsuggest --tester $file
+>highlight $1
+highlight;;skProc;;1;;6;;2
+highlight;;skType;;1;;16;;3
+highlight;;skProc;;2;;5;;1
+"""
diff --git a/nimsuggest/tests/tsi_highlight.nim b/nimsuggest/tests/tsi_highlight.nim
new file mode 100644
index 000000000..2c19582cc
--- /dev/null
+++ b/nimsuggest/tests/tsi_highlight.nim
@@ -0,0 +1,11 @@
+proc a: int = 0
+e_c_h_o#[!]#
+
+discard """
+$nimsuggest --tester $file
+>highlight $1
+highlight;;skProc;;1;;5;;1
+highlight;;skType;;1;;8;;3
+highlight;;skResult;;1;;0;;0
+highlight;;skProc;;2;;0;;7
+"""
diff --git a/nimsuggest/tests/tstrutils.nim b/nimsuggest/tests/tstrutils.nim
deleted file mode 100644
index ce3e29a15..000000000
--- a/nimsuggest/tests/tstrutils.nim
+++ /dev/null
@@ -1,9 +0,0 @@
-discard """
-$nimsuggest --tester lib/pure/strutils.nim
->def lib/pure/strutils.nim:2529:6
-def;;skTemplate;;system.doAssert;;proc (cond: bool, msg: string): typed;;*/lib/system.nim;;*;;9;;"same as `assert` but is always turned on and not affected by the\x0A``--assertions`` command line switch.";;100
-"""
-
-# Line 2529 in strutils.nim is doAssert and this is unlikely to change
-# soon since there are a whole lot of doAsserts there.
-
diff --git a/nimsuggest/tests/tsug_accquote.nim b/nimsuggest/tests/tsug_accquote.nim
new file mode 100644
index 000000000..5b98feac4
--- /dev/null
+++ b/nimsuggest/tests/tsug_accquote.nim
@@ -0,0 +1,10 @@
+proc `%%%`(a: int) = discard
+proc `cast`() = discard
+tsug_accquote.#[!]#
+
+discard """
+$nimsuggest --tester $file
+>sug $1
+sug;;skProc;;tsug_accquote.`%%%`;;proc (a: int);;$file;;1;;5;;"";;100;;None
+sug;;skProc;;tsug_accquote.`cast`;;proc ();;$file;;2;;5;;"";;100;;None
+"""
diff --git a/nimsuggest/tests/tsug_enum.nim b/nimsuggest/tests/tsug_enum.nim
new file mode 100644
index 000000000..97a225f16
--- /dev/null
+++ b/nimsuggest/tests/tsug_enum.nim
@@ -0,0 +1,18 @@
+## suggestions for enums
+
+type
+  LogLevel {.pure.} = enum
+    debug, log, warn, error
+  
+  FooBar = enum
+    fbFoo, fbBar
+
+echo fbFoo, fbBar
+
+echo LogLevel.deb#[!]#
+
+discard """
+$nimsuggest --tester $file
+>sug $1
+sug;;skEnumField;;debug;;LogLevel;;*nimsuggest/tests/tsug_enum.nim;;5;;4;;"";;100;;Prefix
+"""
\ No newline at end of file
diff --git a/nimsuggest/tests/tsug_pragmas.nim b/nimsuggest/tests/tsug_pragmas.nim
new file mode 100644
index 000000000..ce9c4e8f8
--- /dev/null
+++ b/nimsuggest/tests/tsug_pragmas.nim
@@ -0,0 +1,40 @@
+template fooBar1() {.pragma.}
+proc fooBar2() = discard
+macro fooBar3(x: untyped) = discard
+{.pragma: fooBar4 fooBar3.}
+
+proc test1() {.fooBar#[!]#.} = discard
+
+var test2 {.fooBar#[!]#.} = 9
+
+type
+  Person {.fooBar#[!]#.} = object
+    hello {.fooBar#[!]#.}: string
+  Callback = proc () {.fooBar#[!]#.}
+
+# Check only macros/templates/pragmas are suggested
+discard """
+$nimsuggest --tester $file
+>sug $1
+sug;;skTemplate;;fooBar4;;;;$file;;4;;8;;"";;100;;Prefix
+sug;;skTemplate;;tsug_pragmas.fooBar1;;template ();;$file;;1;;9;;"";;100;;Prefix
+sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;3;;6;;"";;50;;Prefix
+>sug $2
+sug;;skTemplate;;fooBar4;;;;$file;;4;;8;;"";;100;;Prefix
+sug;;skTemplate;;tsug_pragmas.fooBar1;;template ();;$file;;1;;9;;"";;100;;Prefix
+sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;3;;6;;"";;50;;Prefix
+>sug $3
+sug;;skTemplate;;fooBar4;;;;$file;;4;;8;;"";;100;;Prefix
+sug;;skTemplate;;tsug_pragmas.fooBar1;;template ();;$file;;1;;9;;"";;100;;Prefix
+sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;3;;6;;"";;50;;Prefix
+>sug $4
+sug;;skTemplate;;fooBar4;;;;$file;;4;;8;;"";;100;;Prefix
+sug;;skTemplate;;tsug_pragmas.fooBar1;;template ();;$file;;1;;9;;"";;100;;Prefix
+sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;3;;6;;"";;50;;Prefix
+>sug $5
+sug;;skTemplate;;fooBar4;;;;$file;;4;;8;;"";;100;;Prefix
+sug;;skTemplate;;tsug_pragmas.fooBar1;;template ();;$file;;1;;9;;"";;100;;Prefix
+sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;3;;6;;"";;50;;Prefix
+"""
+
+
diff --git a/nimsuggest/tests/tsug_recursive.nim b/nimsuggest/tests/tsug_recursive.nim
new file mode 100644
index 000000000..97ee5ca01
--- /dev/null
+++ b/nimsuggest/tests/tsug_recursive.nim
@@ -0,0 +1,8 @@
+discard """
+$nimsuggest --tester $file
+>sug $1
+sug;;skProc;;tsug_recursive.fooBar;;proc ();;$file;;7;;5;;"";;100;;Prefix
+"""
+
+proc fooBar() =
+  fooBa#[!]#
diff --git a/nimsuggest/tests/tsug_regression.nim b/nimsuggest/tests/tsug_regression.nim
index ce66aafb2..2aff3fe94 100644
--- a/nimsuggest/tests/tsug_regression.nim
+++ b/nimsuggest/tests/tsug_regression.nim
@@ -7,22 +7,28 @@ type X = object
 proc main =
   # bug #52
   var
-    set0 = initSet[int]()
-    set1 = initSet[X]()
-    set2 = initSet[ref int]()
+    set0 = initHashSet[int]()
+    set1 = initHashSet[X]()
+    set2 = initHashSet[ref int]()
 
     map0 = initTable[int, int]()
     map1 = initOrderedTable[string, int]()
     cfg = loadConfig("file")
   map0.#[!]#
 
+# the maxresults are limited as it seems there is sort or some other
+# instability that causes the suggestions to slightly differ between 32 bit
+# and 64 bit versions of nimsuggest
+
 discard """
-$nimsuggest --tester $file
+disabled:true
+$nimsuggest --tester --maxresults:4 $file
 >sug $1
-sug;;skProc;;tables.getOrDefault;;proc (t: Table[getOrDefault.A, getOrDefault.B], key: A): B;;$lib/pure/collections/tables.nim;;178;;5;;"";;100;;None
-sug;;skProc;;tables.hasKey;;proc (t: Table[hasKey.A, hasKey.B], key: A): bool;;$lib/pure/collections/tables.nim;;233;;5;;"returns true iff `key` is in the table `t`.";;100;;None
-sug;;skProc;;tables.add;;proc (t: var Table[add.A, add.B], key: A, val: B);;$lib/pure/collections/tables.nim;;309;;5;;"puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.";;100;;None
-sug;;skIterator;;tables.allValues;;iterator (t: Table[allValues.A, allValues.B], key: A): B{.inline.};;$lib/pure/collections/tables.nim;;225;;9;;"iterates over any value in the table `t` that belongs to the given `key`.";;100;;None
-sug;;skProc;;tables.clear;;proc (t: var Table[clear.A, clear.B]);;$lib/pure/collections/tables.nim;;121;;5;;"Resets the table so that it is empty.";;100;;None
-*
+sug;;skProc;;tables.hasKey;;proc (t: Table[hasKey.A, hasKey.B], key: A): bool;;*/lib/pure/collections/tables.nim;;374;;5;;"Returns true *";;100;;None
+sug;;skProc;;tables.clear;;proc (t: var Table[clear.A, clear.B]);;*/lib/pure/collections/tables.nim;;567;;5;;"Resets the table so that it is empty*";;100;;None
+sug;;skProc;;tables.contains;;proc (t: Table[contains.A, contains.B], key: A): bool;;*/lib/pure/collections/tables.nim;;*;;5;;"Alias of *";;100;;None
+sug;;skProc;;tables.del;;proc (t: var Table[del.A, del.B], key: A);;*/lib/pure/collections/tables.nim;;*;;5;;"*";;100;;None
 """
+
+# TODO enable the tests
+# TODO: test/fix suggestion sorting - deprecated suggestions should rank lower
diff --git a/nimsuggest/tests/tsug_template.nim b/nimsuggest/tests/tsug_template.nim
new file mode 100644
index 000000000..da494d279
--- /dev/null
+++ b/nimsuggest/tests/tsug_template.nim
@@ -0,0 +1,12 @@
+template tmpa() = discard
+macro tmpb() = discard
+converter tmpc() = discard
+tmp#[!]#
+
+discard """
+$nimsuggest --tester $file
+>sug $1
+sug;;skMacro;;tsug_template.tmpb;;macro (){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;2;;6;;"";;100;;Prefix
+sug;;skConverter;;tsug_template.tmpc;;converter ();;$file;;3;;10;;"";;100;;Prefix
+sug;;skTemplate;;tsug_template.tmpa;;template ();;$file;;1;;9;;"";;100;;Prefix
+"""
diff --git a/nimsuggest/tests/tsug_typedecl.nim b/nimsuggest/tests/tsug_typedecl.nim
new file mode 100644
index 000000000..2a510929d
--- /dev/null
+++ b/nimsuggest/tests/tsug_typedecl.nim
@@ -0,0 +1,26 @@
+# suggestions for type declarations
+
+from system import string, int, bool
+
+type
+  super = int
+  someType = bool
+
+let str = "hello"
+
+proc main() =
+  let a: s#[!]#
+
+# This output show seq, even though that's not imported. This is due to the
+# entire symbol table, regardless of import visibility is currently being
+# scanned. This is hardly ideal, but changing it with the current level of test
+# coverage is unwise as it might break more than it fixes.
+
+discard """
+$nimsuggest --tester $file
+>sug $1
+sug;;skType;;tsug_typedecl.someType;;someType;;*nimsuggest/tests/tsug_typedecl.nim;;7;;2;;"";;100;;Prefix
+sug;;skType;;tsug_typedecl.super;;super;;*nimsuggest/tests/tsug_typedecl.nim;;6;;2;;"";;100;;Prefix
+sug;;skType;;system.string;;string;;*lib/system/basic_types.nim;;*;;*;;*;;100;;Prefix
+sug;;skType;;system.seq;;seq;;*lib/system.nim;;*;;*;;*;;100;;Prefix
+"""
diff --git a/nimsuggest/tests/ttempl_inst.nim b/nimsuggest/tests/ttempl_inst.nim
new file mode 100644
index 000000000..5f5b10fe9
--- /dev/null
+++ b/nimsuggest/tests/ttempl_inst.nim
@@ -0,0 +1,13 @@
+template foo() =
+  {.warning: "foo".}
+  
+foo()
+
+#[!]#
+discard """
+$nimsuggest --tester $file
+>chk $1
+chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/ttempl_inst.nim [Processing]";;0
+chk;;skUnknown;;;;Hint;;$file;;4;;3;;"template/generic instantiation from here";;0
+chk;;skUnknown;;;;Warning;;$file;;2;;11;;"foo [User]";;0
+"""
diff --git a/nimsuggest/tests/ttemplate_highlight.nim b/nimsuggest/tests/ttemplate_highlight.nim
new file mode 100644
index 000000000..2cbac3be5
--- /dev/null
+++ b/nimsuggest/tests/ttemplate_highlight.nim
@@ -0,0 +1,9 @@
+doAssert true#[!]#
+
+discard """
+$nimsuggest --tester $1
+>highlight $1
+highlight;;skTemplate;;1;;0;;8
+highlight;;skTemplate;;1;;0;;8
+highlight;;skEnumField;;1;;9;;4
+"""
diff --git a/nimsuggest/tests/ttype_decl.nim b/nimsuggest/tests/ttype_decl.nim
index 846eb7b1b..61d8c26cd 100644
--- a/nimsuggest/tests/ttype_decl.nim
+++ b/nimsuggest/tests/ttype_decl.nim
@@ -1,14 +1,16 @@
 discard """
 $nimsuggest --tester --maxresults:3 $file
 >sug $1
-sug;;skType;;ttype_decl.Other;;Other;;$file;;10;;2;;"";;0;;None
-sug;;skType;;system.int;;int;;$lib/system.nim;;25;;2;;"";;0;;None
-sug;;skType;;system.string;;string;;$lib/system.nim;;48;;2;;"";;0;;None
+sug;;skType;;ttype_decl.Other;;Other;;$file;;10;;2;;"";;100;;None
+sug;;skType;;system.int;;int;;*lib/system/basic_types.nim;;2;;2;;"";;100;;None
+sug;;skType;;system.string;;string;;*lib/system/basic_types.nim;;23;;2;;"";;100;;None
 """
 import strutils
 type
   Other = object ## My other object.
   Foo = #[!]#
+  OldOne {.deprecated.} = object
+    x: int
 
 proc main(f: Foo) =
 
diff --git a/nimsuggest/tests/ttype_highlight.nim b/nimsuggest/tests/ttype_highlight.nim
new file mode 100644
index 000000000..a324215fe
--- /dev/null
+++ b/nimsuggest/tests/ttype_highlight.nim
@@ -0,0 +1,27 @@
+type
+  TypeA = int
+  TypeB* = int
+  TypeC {.unchecked.} = array[1, int]
+  TypeD[T] = T
+  TypeE* {.unchecked.} = array[0, int]#[!]#
+
+discard """
+$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/nimsuggest/tests/tuse.nim b/nimsuggest/tests/tuse.nim
new file mode 100644
index 000000000..7c1d1ad0c
--- /dev/null
+++ b/nimsuggest/tests/tuse.nim
@@ -0,0 +1,22 @@
+# basic tests for use
+
+# bug #58
+proc someOtherProc() =
+  discard
+
+someOtherProc()
+
+proc #[!]#someProc*() =
+  discard
+
+#[!]#someProc()
+
+discard """
+$nimsuggest --tester $file
+>use $1
+def;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;9;;5;;"";;100
+use;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;12;;0;;"";;100
+>use $2
+def;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;9;;5;;"";;100
+use;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;12;;0;;"";;100
+"""
diff --git a/nimsuggest/tests/tuse_enum.nim b/nimsuggest/tests/tuse_enum.nim
new file mode 100644
index 000000000..8a40a8348
--- /dev/null
+++ b/nimsuggest/tests/tuse_enum.nim
@@ -0,0 +1,15 @@
+discard """
+$nimsuggest --tester $file
+>use $1
+def;;skEnumField;;tuse_enum.Colour.Red;;Colour;;$file;;10;;4;;"";;100
+use;;skEnumField;;tuse_enum.Colour.Red;;Colour;;$file;;14;;8;;"";;100
+"""
+
+type
+  Colour = enum
+    Red
+    Green
+    Blue
+
+discard #[!]#Red
+
diff --git a/nimsuggest/tests/tuse_structure.nim b/nimsuggest/tests/tuse_structure.nim
new file mode 100644
index 000000000..f65ab9060
--- /dev/null
+++ b/nimsuggest/tests/tuse_structure.nim
@@ -0,0 +1,15 @@
+# tests for use and structures
+
+type
+  Foo* = ref object of RootObj
+    bar*: string
+
+proc test(f: Foo) =
+  echo f.#[!]#bar
+
+discard """
+$nimsuggest --tester $file
+>use $1
+def	skField	tuse_structure.Foo.bar	string	$file	5	4	""	100
+use	skField	tuse_structure.Foo.bar	string	$file	8	9	""	100
+"""
diff --git a/nimsuggest/tests/tv3.nim b/nimsuggest/tests/tv3.nim
new file mode 100644
index 000000000..80e51e364
--- /dev/null
+++ b/nimsuggest/tests/tv3.nim
@@ -0,0 +1,27 @@
+# tests v3
+
+type
+  Foo* = ref object of RootObj
+    bar*: string
+
+proc test(f: Foo) =
+  echo f.ba#[!]#r
+
+#[!]#
+
+discard """
+$nimsuggest --v3 --tester $file
+>use $1
+def	skField	tv3.Foo.bar	string	$file	5	4	""	100
+use	skField	tv3.Foo.bar	string	$file	8	9	""	100
+>def $1
+def	skField	tv3.Foo.bar	string	$file	5	4	""	100
+>sug $1
+sug	skField	bar	string	$file	5	4	""	100	Prefix
+>globalSymbols test
+def	skProc	tv3.test	proc (f: Foo){.gcsafe, raises: <inferred> [].}	$file	7	5	""	100
+>globalSymbols Foo
+def	skType	tv3.Foo	Foo	$file	4	2	""	100
+>def $2
+>use $2
+"""
diff --git a/nimsuggest/tests/tv3_con.nim b/nimsuggest/tests/tv3_con.nim
new file mode 100644
index 000000000..4714c366b
--- /dev/null
+++ b/nimsuggest/tests/tv3_con.nim
@@ -0,0 +1,13 @@
+# tests v3
+
+proc test(a: string, b:string) = discard
+proc test(a: int) = discard
+
+test(#[!]#
+
+discard """
+$nimsuggest --v3 --tester $file
+>con $1
+con;;skProc;;tv3_con.test;;proc (a: string, b: string);;$file;;3;;5;;"";;100
+con;;skProc;;tv3_con.test;;proc (a: int);;$file;;4;;5;;"";;100
+"""
diff --git a/nimsuggest/tests/tv3_definition.nim b/nimsuggest/tests/tv3_definition.nim
new file mode 100644
index 000000000..03684b7cd
--- /dev/null
+++ b/nimsuggest/tests/tv3_definition.nim
@@ -0,0 +1,9 @@
+
+let foo = 30
+let bar = foo + fo#[!]#o + foo
+
+discard """
+$nimsuggest --v3 --tester $file
+>def $1
+def	skLet	tv3_definition.foo	int	$file	2	4	""	100
+"""
diff --git a/nimsuggest/tests/tv3_forward_definition.nim b/nimsuggest/tests/tv3_forward_definition.nim
new file mode 100644
index 000000000..7a16ea331
--- /dev/null
+++ b/nimsuggest/tests/tv3_forward_definition.nim
@@ -0,0 +1,23 @@
+proc de#[!]#mo(): int
+
+proc de#[!]#mo(): int = 5
+
+let a = de#[!]#mo()
+
+discard """
+$nimsuggest --v3 --tester $file
+>use $1
+use	skProc	tv3_forward_definition.demo	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	1	5	""	100
+def	skProc	tv3_forward_definition.demo	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	3	5	""	100
+use	skProc	tv3_forward_definition.demo	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	5	8	""	100
+>use $2
+use	skProc	tv3_forward_definition.demo	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	1	5	""	100
+def	skProc	tv3_forward_definition.demo	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	3	5	""	100
+use	skProc	tv3_forward_definition.demo	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	5	8	""	100
+>declaration $1
+declaration	skProc	tv3_forward_definition.demo	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	3	5	""	100
+>declaration $2
+declaration	skProc	tv3_forward_definition.demo	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	1	5	""	100
+>declaration $3
+declaration	skProc	tv3_forward_definition.demo	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	1	5	""	100
+"""
diff --git a/nimsuggest/tests/tv3_generics.nim b/nimsuggest/tests/tv3_generics.nim
new file mode 100644
index 000000000..2bfb2ca1d
--- /dev/null
+++ b/nimsuggest/tests/tv3_generics.nim
@@ -0,0 +1,18 @@
+type
+  Hello[T] = object
+    value: T
+
+proc printHelloValue[T](hello: Hello[T]) =
+  echo hello.value
+
+proc main() =
+  let a = Hello[float]()
+  p#[!]#rintHelloValue(a)
+
+main()
+
+discard """
+$nimsuggest --v3 --tester $file
+>def $1
+def;;skProc;;tv3_generics.printHelloValue;;proc (hello: Hello[printHelloValue.T]);;$file;;5;;5;;"";;100
+"""
diff --git a/nimsuggest/tests/tv3_globalSymbols.nim b/nimsuggest/tests/tv3_globalSymbols.nim
new file mode 100644
index 000000000..c3bb9933b
--- /dev/null
+++ b/nimsuggest/tests/tv3_globalSymbols.nim
@@ -0,0 +1,14 @@
+# Tests the order of the matches
+proc Btoken(): int = 5
+proc tokenA(): int = 5
+proc token(): int = 5
+proc BBtokenA(): int = 5
+
+discard """
+$nimsuggest --v3 --tester $file
+>globalSymbols token
+def	skProc	tv3_globalSymbols.token	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	4	5	""	100
+def	skProc	tv3_globalSymbols.tokenA	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	3	5	""	100
+def	skProc	tv3_globalSymbols.Btoken	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	2	5	""	100
+def	skProc	tv3_globalSymbols.BBtokenA	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	5	5	""	100
+"""
diff --git a/nimsuggest/tests/tv3_import.nim b/nimsuggest/tests/tv3_import.nim
new file mode 100644
index 000000000..3c128f85b
--- /dev/null
+++ b/nimsuggest/tests/tv3_import.nim
@@ -0,0 +1,7 @@
+import tv#[!]#3
+
+discard """
+$nimsuggest --v3 --tester $file
+>def $1
+def	skModule	tv3		*/tv3.nim	1	0	""	100
+"""
diff --git a/nimsuggest/tests/tv3_outline.nim b/nimsuggest/tests/tv3_outline.nim
new file mode 100644
index 000000000..518620c87
--- /dev/null
+++ b/nimsuggest/tests/tv3_outline.nim
@@ -0,0 +1,45 @@
+# tests v3 outline
+
+type
+  Foo* = ref object of RootObj
+    bar*: string
+  FooEnum = enum value1, value2
+  FooPrivate = ref object of RootObj
+    barPrivate: string
+
+macro m(arg: untyped): untyped = discard
+template t(arg: untyped): untyped = discard
+proc p(): void = discard
+iterator i(): int = discard
+converter c(s: string): int = discard
+method m(f: Foo): void = discard
+func f(): void = discard
+
+let a = 1
+var b = 2
+const con = 2
+
+proc outer(): void =
+  proc inner() = discard
+
+proc procWithLocal(): void =
+  let local = 10
+
+discard """
+$nimsuggest --v3 --tester $file
+>outline $file
+outline	skType	tv3_outline.Foo	Foo	$file	4	2	""	100	5	16
+outline	skType	tv3_outline.FooEnum	FooEnum	$file	6	2	""	100	6	31
+outline	skEnumField	tv3_outline.FooEnum.value1	FooEnum	$file	6	17	""	100	6	23
+outline	skEnumField	tv3_outline.FooEnum.value2	FooEnum	$file	6	25	""	100	6	31
+outline	skType	tv3_outline.FooPrivate	FooPrivate	$file	7	2	""	100	8	22
+outline	skMacro	tv3_outline.m	macro (arg: untyped): untyped{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	10	6	""	100	10	40
+outline	skTemplate	tv3_outline.t	template (arg: untyped): untyped	$file	11	9	""	100	11	43
+outline	skProc	tv3_outline.p	proc (){.noSideEffect, gcsafe, raises: <inferred> [].}	$file	12	5	""	100	12	24
+outline	skConverter	tv3_outline.c	converter (s: string): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	14	10	""	100	14	37
+outline	skFunc	tv3_outline.f	proc (){.noSideEffect, gcsafe, raises: <inferred> [].}	$file	16	5	""	100	16	24
+outline	skConst	tv3_outline.con	int literal(2)	$file	20	6	""	100	20	13
+outline	skProc	tv3_outline.outer	proc (){.noSideEffect, gcsafe, raises: <inferred> [].}	$file	22	5	""	100	23	24
+outline	skProc	tv3_outline.outer.inner	proc (){.noSideEffect, gcsafe, raises: <inferred> [].}	$file	23	7	""	100	23	24
+outline	skProc	tv3_outline.procWithLocal	proc (){.noSideEffect, gcsafe, raises: <inferred> [].}	$file	25	5	""	100	26	16
+"""
diff --git a/nimsuggest/tests/tv3_typeDefinition.nim b/nimsuggest/tests/tv3_typeDefinition.nim
new file mode 100644
index 000000000..f86d12cc6
--- /dev/null
+++ b/nimsuggest/tests/tv3_typeDefinition.nim
@@ -0,0 +1,32 @@
+# tests v3
+
+type
+  Foo* = ref object of RootObj
+    bar*: string
+
+proc test(ff: Foo) =
+  echo f#[!]#f.bar
+
+type
+  Fo#[!]#o2* = ref object of RootObj
+
+type
+  FooGeneric[T] = ref object of RootObj
+    bar*: T
+
+let fooGeneric = FooGeneric[string]()
+echo fo#[!]#oGeneric.bar
+
+# bad type
+echo unde#[!]#fined
+
+discard """
+$nimsuggest --v3 --tester $file
+>type $1
+type	skType	tv3_typeDefinition.Foo	Foo	$file	4	2	""	100
+>type $2
+type	skType	tv3_typeDefinition.Foo2	Foo2	$file	11	2	""	100
+>type $3
+type	skType	tv3_typeDefinition.FooGeneric	FooGeneric	$file	14	2	""	100
+>type $4
+"""
diff --git a/nimsuggest/tests/twithin_macro.nim b/nimsuggest/tests/twithin_macro.nim
index e0df03542..98d58381f 100644
--- a/nimsuggest/tests/twithin_macro.nim
+++ b/nimsuggest/tests/twithin_macro.nim
@@ -1,166 +1,5 @@
-
-import macros
-
-macro class*(head, body: untyped): untyped =
-  # The macro is immediate, since all its parameters are untyped.
-  # This means, it doesn't resolve identifiers passed to it.
-
-  var typeName, baseName: NimNode
-
-  # flag if object should be exported
-  var exported: bool
-
-  if head.kind == nnkInfix and head[0].ident == !"of":
-    # `head` is expression `typeName of baseClass`
-    # echo head.treeRepr
-    # --------------------
-    # Infix
-    #   Ident !"of"
-    #   Ident !"Animal"
-    #   Ident !"RootObj"
-    typeName = head[1]
-    baseName = head[2]
-
-  elif head.kind == nnkInfix and head[0].ident == !"*" and
-       head[2].kind == nnkPrefix and head[2][0].ident == !"of":
-    # `head` is expression `typeName* of baseClass`
-    # echo head.treeRepr
-    # --------------------
-    # Infix
-    #   Ident !"*"
-    #   Ident !"Animal"
-    #   Prefix
-    #     Ident !"of"
-    #     Ident !"RootObj"
-    typeName = head[1]
-    baseName = head[2][1]
-    exported = true
-
-  else:
-    quit "Invalid node: " & head.lispRepr
-
-  # The following prints out the AST structure:
-  #
-  # import macros
-  # dumptree:
-  #   type X = ref object of Y
-  #     z: int
-  # --------------------
-  # StmtList
-  #   TypeSection
-  #     TypeDef
-  #       Ident !"X"
-  #       Empty
-  #       RefTy
-  #         ObjectTy
-  #           Empty
-  #           OfInherit
-  #             Ident !"Y"
-  #           RecList
-  #             IdentDefs
-  #               Ident !"z"
-  #               Ident !"int"
-  #               Empty
-
-  # create a type section in the result
-  result =
-    if exported:
-      # mark `typeName` with an asterisk
-      quote do:
-        type `typeName`* = ref object of `baseName`
-    else:
-      quote do:
-        type `typeName` = ref object of `baseName`
-
-  # echo treeRepr(body)
-  # --------------------
-  # StmtList
-  #   VarSection
-  #     IdentDefs
-  #       Ident !"name"
-  #       Ident !"string"
-  #       Empty
-  #     IdentDefs
-  #       Ident !"age"
-  #       Ident !"int"
-  #       Empty
-  #   MethodDef
-  #     Ident !"vocalize"
-  #     Empty
-  #     Empty
-  #     FormalParams
-  #       Ident !"string"
-  #     Empty
-  #     Empty
-  #     StmtList
-  #       StrLit ...
-  #   MethodDef
-  #     Ident !"age_human_yrs"
-  #     Empty
-  #     Empty
-  #     FormalParams
-  #       Ident !"int"
-  #     Empty
-  #     Empty
-  #     StmtList
-  #       DotExpr
-  #         Ident !"this"
-  #         Ident !"age"
-
-  # var declarations will be turned into object fields
-  var recList = newNimNode(nnkRecList)
-
-  # expected name of constructor
-  let ctorName = newIdentNode("new" & $typeName)
-
-  # Iterate over the statements, adding `this: T`
-  # to the parameters of functions, unless the
-  # function is a constructor
-  for node in body.children:
-    case node.kind:
-
-      of nnkMethodDef, nnkProcDef:
-        # check if it is the ctor proc
-        if node.name.kind != nnkAccQuoted and node.name.basename == ctorName:
-          # specify the return type of the ctor proc
-          node.params[0] = typeName
-        else:
-          # inject `self: T` into the arguments
-          node.params.insert(1, newIdentDefs(ident("self"), typeName))
-        result.add(node)
-
-      of nnkVarSection:
-        # variables get turned into fields of the type.
-        for n in node.children:
-          recList.add(n)
-
-      else:
-        result.add(node)
-
-  # Inspect the tree structure:
-  #
-  # echo result.treeRepr
-  # --------------------
-  # StmtList
-  #   TypeSection
-  #     TypeDef
-  #       Ident !"Animal"
-  #       Empty
-  #       RefTy
-  #         ObjectTy
-  #           Empty
-  #           OfInherit
-  #             Ident !"RootObj"
-  #           Empty   <= We want to replace this
-  # MethodDef
-  # ...
-
-  result[0][0][2][0][2] = recList
-
-  # Lets inspect the human-readable version of the output
-  #echo repr(result)
-
-# ---
+from system import string, int, seq, `&`, `$`, `*`, `@`, echo, add, items, RootObj
+import fixtures/mclass_macro
 
 class Animal of RootObj:
   var name: string
@@ -202,12 +41,11 @@ echo r.age_human_yrs()
 echo r
 
 discard """
-$nimsuggest --tester $file
+$nimsuggest --tester --maxresults:5 $file
 >sug $1
-sug;;skField;;age;;int;;$file;;167;;6;;"";;100;;None
-sug;;skField;;name;;string;;$file;;166;;6;;"";;100;;None
-sug;;skMethod;;twithin_macro.age_human_yrs;;proc (self: Animal): int;;$file;;169;;9;;"";;100;;None
-sug;;skMethod;;twithin_macro.vocalize;;proc (self: Animal): string;;$file;;168;;9;;"";;100;;None
-sug;;skMethod;;twithin_macro.vocalize;;proc (self: Rabbit): string;;$file;;184;;9;;"";;100;;None
-sug;;skMacro;;twithin_macro.class;;proc (head: untyped, body: untyped): untyped{.gcsafe, locks: <unknown>.};;$file;;4;;6;;"";;50;;None*
+sug;;skField;;age;;int;;$file;;6;;6;;"";;100;;None
+sug;;skField;;name;;string;;$file;;5;;6;;"";;100;;None
+sug;;skMethod;;twithin_macro.age_human_yrs;;proc (self: Animal): int{.raises: <inferred> [].};;$file;;8;;9;;"";;100;;None
+sug;;skMethod;;twithin_macro.vocalize;;proc (self: Animal): string{.raises: <inferred> [].};;$file;;7;;9;;"";;100;;None
+sug;;skMethod;;twithin_macro.vocalize;;proc (self: Rabbit): string;;$file;;23;;9;;"";;100;;None
 """
diff --git a/nimsuggest/tests/twithin_macro_prefix.nim b/nimsuggest/tests/twithin_macro_prefix.nim
index 86e406c5d..e89c8b942 100644
--- a/nimsuggest/tests/twithin_macro_prefix.nim
+++ b/nimsuggest/tests/twithin_macro_prefix.nim
@@ -1,166 +1,5 @@
-
-import macros
-
-macro class*(head, body: untyped): untyped =
-  # The macro is immediate, since all its parameters are untyped.
-  # This means, it doesn't resolve identifiers passed to it.
-
-  var typeName, baseName: NimNode
-
-  # flag if object should be exported
-  var exported: bool
-
-  if head.kind == nnkInfix and head[0].ident == !"of":
-    # `head` is expression `typeName of baseClass`
-    # echo head.treeRepr
-    # --------------------
-    # Infix
-    #   Ident !"of"
-    #   Ident !"Animal"
-    #   Ident !"RootObj"
-    typeName = head[1]
-    baseName = head[2]
-
-  elif head.kind == nnkInfix and head[0].ident == !"*" and
-       head[2].kind == nnkPrefix and head[2][0].ident == !"of":
-    # `head` is expression `typeName* of baseClass`
-    # echo head.treeRepr
-    # --------------------
-    # Infix
-    #   Ident !"*"
-    #   Ident !"Animal"
-    #   Prefix
-    #     Ident !"of"
-    #     Ident !"RootObj"
-    typeName = head[1]
-    baseName = head[2][1]
-    exported = true
-
-  else:
-    quit "Invalid node: " & head.lispRepr
-
-  # The following prints out the AST structure:
-  #
-  # import macros
-  # dumptree:
-  #   type X = ref object of Y
-  #     z: int
-  # --------------------
-  # StmtList
-  #   TypeSection
-  #     TypeDef
-  #       Ident !"X"
-  #       Empty
-  #       RefTy
-  #         ObjectTy
-  #           Empty
-  #           OfInherit
-  #             Ident !"Y"
-  #           RecList
-  #             IdentDefs
-  #               Ident !"z"
-  #               Ident !"int"
-  #               Empty
-
-  # create a type section in the result
-  result =
-    if exported:
-      # mark `typeName` with an asterisk
-      quote do:
-        type `typeName`* = ref object of `baseName`
-    else:
-      quote do:
-        type `typeName` = ref object of `baseName`
-
-  # echo treeRepr(body)
-  # --------------------
-  # StmtList
-  #   VarSection
-  #     IdentDefs
-  #       Ident !"name"
-  #       Ident !"string"
-  #       Empty
-  #     IdentDefs
-  #       Ident !"age"
-  #       Ident !"int"
-  #       Empty
-  #   MethodDef
-  #     Ident !"vocalize"
-  #     Empty
-  #     Empty
-  #     FormalParams
-  #       Ident !"string"
-  #     Empty
-  #     Empty
-  #     StmtList
-  #       StrLit ...
-  #   MethodDef
-  #     Ident !"age_human_yrs"
-  #     Empty
-  #     Empty
-  #     FormalParams
-  #       Ident !"int"
-  #     Empty
-  #     Empty
-  #     StmtList
-  #       DotExpr
-  #         Ident !"this"
-  #         Ident !"age"
-
-  # var declarations will be turned into object fields
-  var recList = newNimNode(nnkRecList)
-
-  # expected name of constructor
-  let ctorName = newIdentNode("new" & $typeName)
-
-  # Iterate over the statements, adding `this: T`
-  # to the parameters of functions, unless the
-  # function is a constructor
-  for node in body.children:
-    case node.kind:
-
-      of nnkMethodDef, nnkProcDef:
-        # check if it is the ctor proc
-        if node.name.kind != nnkAccQuoted and node.name.basename == ctorName:
-          # specify the return type of the ctor proc
-          node.params[0] = typeName
-        else:
-          # inject `self: T` into the arguments
-          node.params.insert(1, newIdentDefs(ident("self"), typeName))
-        result.add(node)
-
-      of nnkVarSection:
-        # variables get turned into fields of the type.
-        for n in node.children:
-          recList.add(n)
-
-      else:
-        result.add(node)
-
-  # Inspect the tree structure:
-  #
-  # echo result.treeRepr
-  # --------------------
-  # StmtList
-  #   TypeSection
-  #     TypeDef
-  #       Ident !"Animal"
-  #       Empty
-  #       RefTy
-  #         ObjectTy
-  #           Empty
-  #           OfInherit
-  #             Ident !"RootObj"
-  #           Empty   <= We want to replace this
-  # MethodDef
-  # ...
-
-  result[0][0][2][0][2] = recList
-
-  # Lets inspect the human-readable version of the output
-  #echo repr(result)
-
-# ---
+from system import string, int, seq, `&`, `$`, `*`, `@`, echo, add, RootObj
+import fixtures/mclass_macro
 
 class Animal of RootObj:
   var name: string
@@ -204,6 +43,6 @@ echo r
 discard """
 $nimsuggest --tester $file
 >sug $1
-sug;;skField;;age;;int;;$file;;167;;6;;"";;100;;Prefix
-sug;;skMethod;;twithin_macro_prefix.age_human_yrs;;proc (self: Animal): int;;$file;;169;;9;;"";;100;;Prefix
+sug;;skField;;age;;int;;$file;;6;;6;;"";;100;;Prefix
+sug;;skMethod;;twithin_macro_prefix.age_human_yrs;;proc (self: Animal): int{.raises: <inferred> [].};;$file;;8;;9;;"";;100;;Prefix
 """
diff --git a/readme.md b/readme.md
index bdc7c7549..da54a8d0d 100644
--- a/readme.md
+++ b/readme.md
@@ -1,90 +1,112 @@
-# <img src="https://raw.githubusercontent.com/nim-lang/assets/master/Art/logo-crown.png" height="28px"/> Nim [![Build Status][badge-nim-travisci]][nim-travisci]
+# <img src="https://raw.githubusercontent.com/nim-lang/assets/master/Art/logo-crown.png" height="28px"/> Nim
 
-This repository contains the Nim compiler, Nim's stdlib, tools and documentation.
+[![Build Status](https://dev.azure.com/nim-lang/Nim/_apis/build/status/nim-lang.Nim?branchName=devel)](https://dev.azure.com/nim-lang/Nim/_build/latest?definitionId=1&branchName=devel)
+
+This repository contains the Nim compiler, Nim's stdlib, tools, and documentation.
 For more information about Nim, including downloads and documentation for
-the latest release, check out [Nim's website][nim-site].
+the latest release, check out [Nim's website][nim-site] or [bleeding edge docs](https://nim-lang.github.io/Nim/).
 
 ## Community
+
 [![Join the IRC chat][badge-nim-irc]][nim-irc]
+[![Join the Discord server][badge-nim-discord]][nim-discord]
 [![Join the Gitter chat][badge-nim-gitter]][nim-gitter]
+[![Join the Matrix room](https://img.shields.io/matrix/nim-lang:matrix.org?color=blue&style=flat&logo=matrix)](https://matrix.to/#/#nim-lang:matrix.org)
 [![Get help][badge-nim-forum-gethelp]][nim-forum]
 [![View Nim posts on Stack Overflow][badge-nim-stackoverflow]][nim-stackoverflow-newest]
 [![Follow @nim_lang on Twitter][badge-nim-twitter]][nim-twitter]
 
 * The [forum][nim-forum] - the best place to ask questions and to discuss Nim.
-* [#nim IRC Channel (Freenode)][nim-irc] - a place to discuss Nim in real-time.
+* [#nim IRC Channel (Libera Chat)][nim-irc] - a place to discuss Nim in real-time.
   Also where most development decisions get made.
+* [Discord][nim-discord] - an additional place to discuss Nim in real-time. Most
+  channels there are bridged to IRC.
 * [Gitter][nim-gitter] - an additional place to discuss Nim in real-time. There
   is a bridge between Gitter and the IRC channel.
+* [Matrix][nim-matrix] - the main room to discuss Nim in real-time. [Matrix space][nim-matrix-space] contains a list of rooms, most of them are bridged to IRC.
 * [Telegram][nim-telegram] - an additional place to discuss Nim in real-time. There
-  is the official Telegram channel.
+  is the official Telegram channel. Not bridged to IRC.
 * [Stack Overflow][nim-stackoverflow] - a popular Q/A site for programming related
   topics that includes posts about Nim.
-* [Github Wiki][nim-wiki] - Misc user-contributed content.
+* [GitHub Wiki][nim-wiki] - Misc user-contributed content.
 
 ## Compiling
+
 The compiler currently officially supports the following platform and
 architecture combinations:
 
-  * Windows (Windows XP or greater) - x86 and x86_64
-  * Linux (most, if not all, distributions) - x86, x86_64, ppc64 and armv6l
-  * Mac OS X (10.04 or greater) - x86, x86_64 and ppc64
+| Operating System               | Architectures Supported                          |
+|--------------------------------|----------------------------------------|
+| Windows (Windows XP or greater) | x86 and x86_64                             |
+| Linux (most distributions)     | x86, x86_64, ppc64, and armv6l             |
+| Mac OS X (10.04 or greater)    | x86, x86_64, ppc64, and Apple Silicon (ARM64) |
 
-More platforms are supported, however they are not tested regularly and they
+More platforms are supported, however, they are not tested regularly and they
 may not be as stable as the above-listed platforms.
 
 Compiling the Nim compiler is quite straightforward if you follow these steps:
 
 First, the C source of an older version of the Nim compiler is needed to
 bootstrap the latest version because the Nim compiler itself is written in the
-Nim programming language. Those C sources are available within the 
-[``nim-lang/csources``][csources-repo] repository.
+Nim programming language. Those C sources are available within the
+[``nim-lang/csources_v2``][csources-v2-repo] repository.
 
 Next, to build from source you will need:
 
-  * A C compiler such as ``gcc`` 3.x/later or an alternative such as ``clang``,
-    ``Visual C++`` or ``Intel C++``. It is recommended to use ``gcc`` 3.x or
+  * A C compiler such as ``gcc`` 5.x/later or an alternative such as ``clang``,
+    ``Visual C++`` or ``Intel C++``. It is recommended to use ``gcc`` 5.x or
     later.
   * Either ``git`` or ``wget`` to download the needed source repositories.
   * The ``build-essential`` package when using ``gcc`` on Ubuntu (and likely
-    other distros as well). 
+    other distros as well).
+  * On Windows MinGW 4.3.0 (GCC 8.10) is the minimum recommended compiler.
+  * Nim hosts a known working MinGW distribution:
+    * [MinGW32.7z](https://nim-lang.org/download/mingw32.7z)
+    * [MinGW64.7z](https://nim-lang.org/download/mingw64.7z)
+
+**Windows Note: Cygwin and similar POSIX runtime environments are not supported.**
 
 Then, if you are on a \*nix system or Windows, the following steps should compile
-Nim from source using ``gcc``, ``git`` and the ``koch`` build tool (in the place
-of ``sh build.sh`` you should substitute ``build.bat`` on x86 Windows or
-``build64.bat`` on x86_64 Windows):
+Nim from source using ``gcc``, ``git``, and the ``koch`` build tool.
 
 **Note: The following commands are for the development version of the compiler.**
 For most users, installing the latest stable version is enough. Check out
 the installation instructions on the website to do so: https://nim-lang.org/install.html.
 
+For package maintainers: see [packaging guidelines](https://nim-lang.github.io/Nim/packaging.html).
+
+First, get Nim from GitHub:
+
 ```
 git clone https://github.com/nim-lang/Nim.git
 cd Nim
-git clone --depth 1 https://github.com/nim-lang/csources.git
-cd csources
-sh build.sh
-cd ../
-bin/nim c koch
-./koch boot -d:release
-./koch tools # Compile Nimble and other tools.
 ```
 
-Finally, once you have finished the build steps (on Windows, Mac or Linux) you
+Next, run the appropriate build shell script for your platform:
+
+* `build_all.sh` (Linux, Mac)
+* `build_all.bat` (Windows)
+
+Finally, once you have finished the build steps (on Windows, Mac, or Linux) you
 should add the ``bin`` directory to your PATH.
 
+See also [bootstrapping the compiler](https://nim-lang.github.io/Nim/intern.html#bootstrapping-the-compiler).
+
+See also [reproducible builds](https://nim-lang.github.io/Nim/intern.html#bootstrapping-the-compiler-reproducible-builds).
+
 ## Koch
+
 ``koch`` is the build tool used to build various parts of Nim and to generate
 documentation and the website, among other things. The ``koch`` tool can also
-be used to run the Nim test suite. 
+be used to run the Nim test suite.
 
 Assuming that you added Nim's ``bin`` directory to your PATH, you may execute
 the tests using ``./koch tests``. The tests take a while to run, but you
-can run a subset of tests by specifying a category (for example 
+can run a subset of tests by specifying a category (for example
 ``./koch tests cat async``).
 
 For more information on the ``koch`` build tool please see the documentation
-within the [doc/koch.rst](doc/koch.rst) file.
+within the [doc/koch.md](https://nim-lang.github.io/Nim/koch.html) file.
 
 ## Nimble
 
@@ -93,57 +115,55 @@ within the [doc/koch.rst](doc/koch.rst) file.
 
 ## Contributors
 
-This project exists thanks to all the people who contribute. [Read on to find out how to contribute](#contributing).
+This project exists thanks to all the people who contribute.
 <a href="https://github.com/nim-lang/Nim/graphs/contributors"><img src="https://opencollective.com/Nim/contributors.svg?width=890" /></a>
 
 ## Contributing
+
 [![Backers on Open Collective](https://opencollective.com/nim/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/nim/sponsors/badge.svg)](#sponsors)
-[![Setup a bounty via Bountysource][badge-nim-bountysource]][nim-bountysource]
 [![Donate Bitcoins][badge-nim-bitcoin]][nim-bitcoin]
 [![Open Source Helpers](https://www.codetriage.com/nim-lang/nim/badges/users.svg)](https://www.codetriage.com/nim-lang/nim)
 
+See [detailed contributing guidelines](https://nim-lang.github.io/Nim/contributing.html).
 We welcome all contributions to Nim regardless of how small or large
 they are. Everything from spelling fixes to new modules to be included in the
 standard library are welcomed and appreciated. Before you start contributing,
 you should familiarize yourself with the following repository structure:
 
 * ``bin/``, ``build/`` - these directories are empty, but are used when Nim is built.
-* ``compiler/`` - the compiler source code. Also includes nimfix, and plugins within
-  ``compiler/nimfix`` and ``compiler/plugins`` respectively.
-* ``nimsuggest`` - the nimsuggest tool that previously lived in the [``nim-lang/nimsuggest``][nimsuggest-repo] repository. 
+* ``compiler/`` - the compiler source code. Also includes plugins within ``compiler/plugins``.
+* ``nimsuggest`` - the nimsuggest tool that previously lived in the [``nim-lang/nimsuggest``][nimsuggest-repo] repository.
 * ``config/`` - the configuration for the compiler and documentation generator.
 * ``doc/`` - the documentation files in reStructuredText format.
 * ``lib/`` - the standard library, including:
     * ``pure/`` - modules in the standard library written in pure Nim.
     * ``impure/`` - modules in the standard library written in pure Nim with
     dependencies written in other languages.
-    * ``wrappers/`` - modules which wrap dependencies written in other languages.
+    * ``wrappers/`` - modules that wrap dependencies written in other languages.
 * ``tests/`` - contains categorized tests for the compiler and standard library.
-* ``tools/`` - the tools including ``niminst`` and ``nimweb`` (mostly invoked via
+* ``tools/`` - the tools including ``niminst`` (mostly invoked via
   ``koch``).
-* ``web/`` - [the Nim website][nim-site].
-* ``koch.nim`` - tool used to bootstrap Nim, generate C sources, build the website,
+* ``koch.nim`` - the tool used to bootstrap Nim, generate C sources, build the website,
   and generate the documentation.
 
 If you are not familiar with making a pull request using GitHub and/or git, please
 read [this guide][pull-request-instructions].
 
-Ideally you should make sure that all tests pass before submitting a pull request.
+Ideally, you should make sure that all tests pass before submitting a pull request.
 However, if you are short on time, you can just run the tests specific to your
-changes by only running the corresponding categories of tests. Travis CI verifies
+changes by only running the corresponding categories of tests. CI verifies
 that all tests pass before allowing the pull request to be accepted, so only
 running specific tests should be harmless.
 Integration tests should go in ``tests/untestable``.
 
 If you're looking for ways to contribute, please look at our [issue tracker][nim-issues].
-There are always plenty of issues labelled [``Easy``][nim-issues-easy]; these should
+There are always plenty of issues labeled [``Easy``][nim-issues-easy]; these should
 be a good starting point for an initial contribution to Nim.
 
 You can also help with the development of Nim by making donations. Donations can be
 made using:
 
 * [Open Collective](https://opencollective.com/nim)
-* [Bountysource][nim-bountysource]
 * [Bitcoin][nim-bitcoin]
 
 If you have any questions feel free to submit a question on the
@@ -176,37 +196,38 @@ You can also see a list of all our sponsors/backers from various payment service
 
 ## License
 The compiler and the standard library are licensed under the MIT license, except
-for some modules which explicitly state otherwise. As a result you may use any
+for some modules which explicitly state otherwise. As a result, you may use any
 compatible license (essentially any license) for your own programs developed with
 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-2024 Andreas Rumpf, all rights reserved.
 
 [nim-site]: https://nim-lang.org
 [nim-forum]: https://forum.nim-lang.org
 [nim-issues]: https://github.com/nim-lang/Nim/issues
 [nim-issues-easy]: https://github.com/nim-lang/Nim/labels/Easy
-[nim-irc]: https://webchat.freenode.net/?channels=nim
-[nim-travisci]: https://travis-ci.org/nim-lang/Nim
+[nim-irc]: https://web.libera.chat/#nim
 [nim-twitter]: https://twitter.com/nim_lang
-[nim-stackoverflow]: https://stackoverflow.com/questions/tagged/nim
-[nim-stackoverflow-newest]: https://stackoverflow.com/questions/tagged/nim?sort=newest&pageSize=15
+[nim-stackoverflow]: https://stackoverflow.com/questions/tagged/nim-lang
+[nim-stackoverflow-newest]: https://stackoverflow.com/questions/tagged/nim-lang?sort=newest&pageSize=15
+[nim-discord]: https://discord.gg/nim
 [nim-gitter]: https://gitter.im/nim-lang/Nim
+[nim-matrix]: https://matrix.to/#/#nim-lang:matrix.org
+[nim-matrix-space]: https://matrix.to/#/#nim:envs.net
 [nim-telegram]: https://t.me/nim_lang
-[nim-bountysource]: https://www.bountysource.com/teams/nim
 [nim-bitcoin]: https://blockchain.info/address/1BXfuKM2uvoD6mbx4g5xM3eQhLzkCK77tJ
 [nimble-repo]: https://github.com/nim-lang/nimble
 [nimsuggest-repo]: https://github.com/nim-lang/nimsuggest
-[csources-repo]: https://github.com/nim-lang/csources
-[badge-nim-travisci]: https://img.shields.io/travis/nim-lang/Nim/devel.svg?style=flat-square
+[csources-repo-deprecated]: https://github.com/nim-lang/csources
+[csources-v2-repo]: https://github.com/nim-lang/csources_v2
 [badge-nim-irc]: https://img.shields.io/badge/chat-on_irc-blue.svg?style=flat-square
+[badge-nim-discord]: https://img.shields.io/discord/371759389889003530?color=blue&label=discord&logo=discord&logoColor=gold&style=flat-square
 [badge-nim-gitter]: https://img.shields.io/badge/chat-on_gitter-blue.svg?style=flat-square
 [badge-nim-forum-gethelp]: https://img.shields.io/badge/Forum-get%20help-4eb899.svg?style=flat-square
 [badge-nim-twitter]: https://img.shields.io/twitter/follow/nim_lang.svg?style=social
 [badge-nim-stackoverflow]: https://img.shields.io/badge/stackoverflow-nim_tag-yellow.svg?style=flat-square
-[badge-nim-bountysource]: https://img.shields.io/bountysource/team/nim/activity.svg?style=flat-square
 [badge-nim-bitcoin]: https://img.shields.io/badge/bitcoin-1BXfuKM2uvoD6mbx4g5xM3eQhLzkCK77tJ-D69134.svg?style=flat-square
 [pull-request-instructions]: https://help.github.com/articles/using-pull-requests/
 [nim-wiki]: https://github.com/nim-lang/Nim/wiki
diff --git a/readme.txt b/readme.txt
deleted file mode 100644
index 8c6f25b29..000000000
--- a/readme.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-This package contains the Nim compiler, Nim's stdlib, tools and
-documentation.
-
-Nim is a compiled, garbage-collected systems programming language which has
-an excellent productivity/performance ratio. Nim's design focuses on
-efficiency, expressiveness, elegance (in the order of priority).
-
-Read install.txt for instructions of how to build and install it.
-
-The compiler and the standard library are licensed under the MIT license,
-except for some modules where the documentation suggests otherwise. This means
-that you can use any license for your own programs developed with Nim,
-allowing you to create commercial applications.
-
-Read copying.txt for more details.
-
-Copyright (c) 2006-2018 Andreas Rumpf.
-All rights reserved.
diff --git a/security.md b/security.md
new file mode 100644
index 000000000..72a1a3e3d
--- /dev/null
+++ b/security.md
@@ -0,0 +1,20 @@
+# Security Policy
+
+## Supported Versions
+
+Security advisories are published at:
+https://github.com/nim-lang/security/security/advisories?state=published
+
+Security fixes are provided in new releases and in bugfix releases.
+
+We do not backport security fixes to older releases.
+
+(Yet, Linux distributions might backport security fixes for their packages.)
+
+## Reporting a Vulnerability
+
+If you have discovered a vulnerability, please notify us about it via
+security@nim-lang.org in order to set up a meeting where we can discuss the next
+steps.
+
+Please do not report vulnerabilities via GitHub issues.
diff --git a/testament/azure.nim b/testament/azure.nim
new file mode 100644
index 000000000..af65d6a1c
--- /dev/null
+++ b/testament/azure.nim
@@ -0,0 +1,147 @@
+#
+#
+#              The Nim Tester
+#        (c) Copyright 2019 Leorize
+#
+#    Look at license.txt for more info.
+#    All rights reserved.
+
+import base64, json, httpclient, os, strutils, uri
+import specs
+
+const
+  RunIdEnv = "TESTAMENT_AZURE_RUN_ID"
+  CacheSize = 8 # How many results should be cached before uploading to
+                # Azure Pipelines. This prevents throttling that might arise.
+
+proc getAzureEnv(env: string): string =
+  # Conversion rule at:
+  # https://docs.microsoft.com/en-us/azure/devops/pipelines/process/variables#set-variables-in-pipeline
+  env.toUpperAscii().replace('.', '_').getEnv
+
+template getRun(): string =
+  ## Get the test run attached to this instance
+  getEnv(RunIdEnv)
+
+template setRun(id: string) =
+  ## Attach a test run to this instance and its future children
+  putEnv(RunIdEnv, id)
+
+template delRun() =
+  ## Unattach the test run associtated with this instance and its future children
+  delEnv(RunIdEnv)
+
+template warning(args: varargs[untyped]) =
+  ## Add a warning to the current task
+  stderr.writeLine "##vso[task.logissue type=warning;]", args
+
+let
+  ownRun = not existsEnv RunIdEnv
+    ## Whether the test run is owned by this instance
+  accessToken = getAzureEnv("System.AccessToken")
+    ## Access token to Azure Pipelines
+
+var
+  active = false ## Whether the backend should be activated
+  requestBase: Uri ## Base URI for all API requests
+  requestHeaders: HttpHeaders ## Headers required for all API requests
+  results: JsonNode ## A cache for test results before uploading
+
+proc request(api: string, httpMethod: HttpMethod, body = ""): Response {.inline.} =
+  let client = newHttpClient(timeout = 3000)
+  defer: close client
+  result = client.request($(requestBase / api), httpMethod, body, requestHeaders)
+  if result.code != Http200:
+    raise newException(CatchableError, "Request failed")
+
+proc init*() =
+  ## Initialize the Azure Pipelines backend.
+  ##
+  ## If an access token is provided and no test run is associated with the
+  ## current instance, this proc will create a test run named after the current
+  ## Azure Pipelines' job name, then associate it to the current testament
+  ## instance and its future children. Should this fail, the backend will be
+  ## disabled.
+  if isAzure and accessToken.len > 0:
+    active = true
+    requestBase = parseUri(getAzureEnv("System.TeamFoundationCollectionUri")) /
+      getAzureEnv("System.TeamProjectId") / "_apis" ? {"api-version": "5.0"}
+    requestHeaders = newHttpHeaders {
+      "Accept": "application/json",
+      "Authorization": "Basic " & encode(':' & accessToken),
+      "Content-Type": "application/json"
+    }
+    results = newJArray()
+    if ownRun:
+      try:
+        let resp = request(
+          "test/runs",
+          HttpPost,
+          $ %* {
+            "automated": true,
+            "build": { "id": getAzureEnv("Build.BuildId") },
+            "buildPlatform": hostCPU,
+            "controller": "nim-testament",
+            "name": getAzureEnv("Agent.JobName")
+          }
+        )
+        setRun $resp.body.parseJson["id"].getInt
+      except:
+        warning "Couldn't create test run for Azure Pipelines integration"
+        # Set run id to empty to prevent child processes from trying to request
+        # for yet another test run id, which wouldn't be shared with other
+        # instances.
+        setRun ""
+        active = false
+    elif getRun().len == 0:
+      # Disable integration if there aren't any valid test run id
+      active = false
+
+proc uploadAndClear() =
+  ## Upload test results from cache to Azure Pipelines. Then clear the cache
+  ## after.
+  if results.len > 0:
+    try:
+      discard request("test/runs/" & getRun() & "/results", HttpPost, $results)
+    except:
+      for i in results:
+        warning "Couldn't log test result to Azure Pipelines: ",
+          i["automatedTestName"], ", outcome: ", i["outcome"]
+    results = newJArray()
+
+proc finalize*() {.noconv.} =
+  ## Finalize the Azure Pipelines backend.
+  ##
+  ## If a test run has been associated and is owned by this instance, it will
+  ## be marked as complete.
+  if active:
+    if ownRun:
+      uploadAndClear()
+      try:
+        discard request("test/runs/" & getRun(), HttpPatch,
+                        $ %* {"state": "Completed"})
+      except:
+        warning "Couldn't update test run ", getRun(), " on Azure Pipelines"
+      delRun()
+
+proc addTestResult*(name, category: string; durationInMs: int; errorMsg: string;
+                    outcome: TResultEnum) =
+  if not active:
+    return
+
+  let outcome = case outcome
+                of reSuccess: "Passed"
+                of reDisabled, reJoined: "NotExecuted"
+                else: "Failed"
+
+  results.add(%* {
+      "automatedTestName": name,
+      "automatedTestStorage": category,
+      "durationInMs": durationInMs,
+      "errorMessage": errorMsg,
+      "outcome": outcome,
+      "testCaseTitle": name
+  })
+
+  if results.len > CacheSize:
+    uploadAndClear()
diff --git a/testament/backend.nim b/testament/backend.nim
new file mode 100644
index 000000000..1770c6657
--- /dev/null
+++ b/testament/backend.nim
@@ -0,0 +1,70 @@
+#
+#
+#              The Nim Tester
+#        (c) Copyright 2017 Andreas Rumpf
+#
+#    Look at license.txt for more info.
+#    All rights reserved.
+
+import strutils, os, osproc, json
+
+type
+  MachineId* = distinct string
+  CommitId = distinct string
+
+proc `$`*(id: MachineId): string {.borrow.}
+
+var
+  thisMachine: MachineId
+  thisCommit: CommitId
+  thisBranch: string
+
+proc getMachine*(): MachineId =
+  var name = execProcess("hostname").strip
+  if name.len == 0:
+    name = when defined(posix): getEnv("HOSTNAME")
+           else: getEnv("COMPUTERNAME")
+  if name.len == 0:
+    quit "cannot determine the machine name"
+
+  result = MachineId(name)
+
+proc getCommit(): CommitId =
+  const commLen = "commit ".len
+  let hash = execProcess("git log -n 1").strip[commLen..commLen+10]
+  thisBranch = execProcess("git symbolic-ref --short HEAD").strip
+  if hash.len == 0 or thisBranch.len == 0: quit "cannot determine git HEAD"
+  result = CommitId(hash)
+
+var
+  results: File
+  currentCategory: string
+  entries: int
+
+proc writeTestResult*(name, category, target, action, result, expected, given: string) =
+  createDir("testresults")
+  if currentCategory != category:
+    if currentCategory.len > 0:
+      results.writeLine("]")
+      close(results)
+    currentCategory = category
+    results = open("testresults" / category.addFileExt"json", fmWrite)
+    results.writeLine("[")
+    entries = 0
+
+  let jentry = %*{"name": name, "category": category, "target": target,
+    "action": action, "result": result, "expected": expected, "given": given,
+    "machine": thisMachine.string, "commit": thisCommit.string, "branch": thisBranch}
+  if entries > 0:
+    results.writeLine(",")
+  results.write($jentry)
+  inc entries
+
+proc open*() =
+  thisMachine = getMachine()
+  thisCommit = getCommit()
+
+proc close*() =
+  if currentCategory.len > 0:
+    results.writeLine("]")
+    close(results)
diff --git a/tests/testament/caasdriver.nim b/testament/caasdriver.nim
index 30383bddb..01e402e07 100644
--- a/tests/testament/caasdriver.nim
+++ b/testament/caasdriver.nim
@@ -62,7 +62,7 @@ proc doCaasCommand(session: var NimSession, command: string): string =
   result = ""
 
   while true:
-    var line = TaintedString("")
+    var line = ""
     if session.nim.outputStream.readLine(line):
       if line.string == "": break
       result.add(line.string & "\n")
@@ -78,7 +78,7 @@ proc doProcCommand(session: var NimSession, command: string): string =
   var
     process = startProcess(NimBin, args = session.replaceVars(command).split)
     stream = outputStream(process)
-    line = TaintedString("")
+    line = ""
 
   result = ""
   while stream.readLine(line):
@@ -113,12 +113,12 @@ proc doScenario(script: string, output: Stream, mode: TRunMode, verbose: bool):
   result = true
 
   var f = open(script)
-  var project = TaintedString("")
+  var project = ""
 
   if f.readLine(project):
     var
       s = startNimSession(script.parentDir / project.string, script, mode)
-      tline = TaintedString("")
+      tline = ""
       ln = 1
 
     while f.readLine(tline):
@@ -175,7 +175,7 @@ when isMainModule:
     verbose = false
 
   for i in 0..paramCount() - 1:
-    let param = string(paramStr(i + 1))
+    let param = paramStr(i + 1)
     case param
     of "verbose": verbose = true
     else: filter = param
diff --git a/testament/categories.nim b/testament/categories.nim
new file mode 100644
index 000000000..843bef3f9
--- /dev/null
+++ b/testament/categories.nim
@@ -0,0 +1,774 @@
+#
+#
+#            Nim Tester
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Include for the tester that contains test suites that test special features
+## of the compiler.
+
+# included from testament.nim
+
+import important_packages
+import std/[strformat, strutils]
+from std/sequtils import filterIt
+
+const
+  specialCategories = [
+    "assert",
+    "async",
+    "debugger",
+    "dll",
+    "examples",
+    "gc",
+    "io",
+    "js",
+    "ic",
+    "lib",
+    "manyloc",
+    "nimble-packages",
+    "niminaction",
+    "threads",
+    "untestable", # see trunner_special
+    "testdata",
+    "nimcache",
+    "coroutines",
+    "osproc",
+    "shouldfail",
+    "destructor"
+  ]
+
+proc isTestFile*(file: string): bool =
+  let (_, name, ext) = splitFile(file)
+  result = ext == ".nim" and name.startsWith("t")
+
+# --------------------- DLL generation tests ----------------------------------
+
+proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string, isOrc = false) =
+  const rpath = when defined(macosx):
+      " --passL:-rpath --passL:@loader_path"
+    else:
+      ""
+
+  var test1 = makeTest("lib/nimrtl.nim", options & " --outdir:tests/dll", cat)
+  test1.spec.action = actionCompile
+  testSpec c, test1
+  var test2 = makeTest("tests/dll/server.nim", options & " --threads:on" & rpath, cat)
+  test2.spec.action = actionCompile
+  testSpec c, test2
+
+  var test3 = makeTest("lib/nimhcr.nim", options & " --threads:off --outdir:tests/dll" & rpath, cat)
+  test3.spec.action = actionCompile
+  testSpec c, test3
+  var test4 = makeTest("tests/dll/visibility.nim", options & " --threads:off --app:lib" & rpath, cat)
+  test4.spec.action = actionCompile
+  testSpec c, test4
+
+  # windows looks in the dir of the exe (yay!):
+  when not defined(windows):
+    # posix relies on crappy LD_LIBRARY_PATH (ugh!):
+    const libpathenv = when defined(haiku): "LIBRARY_PATH"
+                       else: "LD_LIBRARY_PATH"
+    var libpath = getEnv(libpathenv)
+    # Temporarily add the lib directory to LD_LIBRARY_PATH:
+    putEnv(libpathenv, "tests/dll" & (if libpath.len > 0: ":" & libpath else: ""))
+    defer: putEnv(libpathenv, libpath)
+
+  testSpec r, makeTest("tests/dll/client.nim", options & " --threads:on" & rpath, cat)
+  testSpec r, makeTest("tests/dll/nimhcr_unit.nim", options & " --threads:off" & rpath, cat)
+  testSpec r, makeTest("tests/dll/visibility.nim", options & " --threads:off" & rpath, cat)
+
+  if "boehm" notin options:
+    # hcr tests
+    
+    var basicHcrTest = makeTest("tests/dll/nimhcr_basic.nim", options & " --threads:off --forceBuild --hotCodeReloading:on " & rpath, cat)
+    # test segfaults for now but compiles:
+    if isOrc: basicHcrTest.spec.action = actionCompile
+    testSpec r, basicHcrTest
+
+    # force build required - see the comments in the .nim file for more details
+    var hcri = makeTest("tests/dll/nimhcr_integration.nim",
+                                   options & " --threads:off --forceBuild --hotCodeReloading:on" & rpath, cat)
+    let nimcache = nimcacheDir(hcri.name, hcri.options, getTestSpecTarget())
+    let cmd = prepareTestCmd(hcri.spec.getCmd, hcri.name,
+                                hcri.options, nimcache, getTestSpecTarget())
+    hcri.testArgs = cmd.parseCmdLine
+    testSpec r, hcri
+
+proc dllTests(r: var TResults, cat: Category, options: string) =
+  # dummy compile result:
+  var c = initResults()
+
+  runBasicDLLTest c, r, cat, options & " --mm:refc"
+  runBasicDLLTest c, r, cat, options & " -d:release --mm:refc"
+  runBasicDLLTest c, r, cat, options, isOrc = true
+  runBasicDLLTest c, r, cat, options & " -d:release", isOrc = true
+  when not defined(windows):
+    # still cannot find a recent Windows version of boehm.dll:
+    runBasicDLLTest c, r, cat, options & " --gc:boehm"
+    runBasicDLLTest c, r, cat, options & " -d:release --gc:boehm"
+
+# ------------------------------ GC tests -------------------------------------
+
+proc gcTests(r: var TResults, cat: Category, options: string) =
+  template testWithoutMs(filename: untyped) =
+    testSpec r, makeTest("tests/gc" / filename, options & "--mm:refc", cat)
+    testSpec r, makeTest("tests/gc" / filename, options &
+                  " -d:release -d:useRealtimeGC --mm:refc", cat)
+    when filename != "gctest":
+      testSpec r, makeTest("tests/gc" / filename, options &
+                    " --gc:orc", cat)
+      testSpec r, makeTest("tests/gc" / filename, options &
+                    " --gc:orc -d:release", cat)
+
+  template testWithoutBoehm(filename: untyped) =
+    testWithoutMs filename
+    testSpec r, makeTest("tests/gc" / filename, options &
+                  " --gc:markAndSweep", cat)
+    testSpec r, makeTest("tests/gc" / filename, options &
+                  " -d:release --gc:markAndSweep", cat)
+
+  template test(filename: untyped) =
+    testWithoutBoehm filename
+    when not defined(windows) and not defined(android):
+      # AR: cannot find any boehm.dll on the net, right now, so disabled
+      # for windows:
+      testSpec r, makeTest("tests/gc" / filename, options &
+                    " --gc:boehm", cat)
+      testSpec r, makeTest("tests/gc" / filename, options &
+                    " -d:release --gc:boehm", cat)
+
+  testWithoutBoehm "foreign_thr"
+  test "gcemscripten"
+  test "growobjcrash"
+  test "gcbench"
+  test "gcleak"
+  test "gcleak2"
+  testWithoutBoehm "gctest"
+  test "gcleak3"
+  test "gcleak4"
+  # Disabled because it works and takes too long to run:
+  #test "gcleak5"
+  testWithoutBoehm "weakrefs"
+  test "cycleleak"
+  testWithoutBoehm "closureleak"
+  testWithoutMs "refarrayleak"
+
+  testWithoutBoehm "tlists"
+  testWithoutBoehm "thavlak"
+
+  test "stackrefleak"
+  test "cyclecollector"
+  testWithoutBoehm "trace_globals"
+
+# ------------------------- threading tests -----------------------------------
+
+proc threadTests(r: var TResults, cat: Category, options: string) =
+  template test(filename: untyped) =
+    testSpec r, makeTest(filename, options, cat)
+    testSpec r, makeTest(filename, options & " -d:release", cat)
+    testSpec r, makeTest(filename, options & " --tlsEmulation:on", cat)
+  for t in os.walkFiles("tests/threads/t*.nim"):
+    test(t)
+
+# ------------------------- IO tests ------------------------------------------
+
+proc ioTests(r: var TResults, cat: Category, options: string) =
+  # We need readall_echo to be compiled for this test to run.
+  # dummy compile result:
+  var c = initResults()
+  testSpec c, makeTest("tests/system/helpers/readall_echo", options, cat)
+  #        ^- why is this not appended to r? Should this be discarded?
+  # EDIT: this should be replaced by something like in D20210524T180826,
+  # likewise in similar instances where `testSpec c` is used, or more generally
+  # when a test depends on another test, as it makes tests non-independent,
+  # creating complications for batching and megatest logic.
+  testSpec r, makeTest("tests/system/tio", options, cat)
+
+# ------------------------- async tests ---------------------------------------
+proc asyncTests(r: var TResults, cat: Category, options: string) =
+  template test(filename: untyped) =
+    testSpec r, makeTest(filename, options, cat)
+  for t in os.walkFiles("tests/async/t*.nim"):
+    test(t)
+
+# ------------------------- debugger tests ------------------------------------
+
+proc debuggerTests(r: var TResults, cat: Category, options: string) =
+  if fileExists("tools/nimgrep.nim"):
+    var t = makeTest("tools/nimgrep", options & " --debugger:on", cat)
+    t.spec.action = actionCompile
+    # force target to C because of MacOS 10.15 SDK headers bug
+    # https://github.com/nim-lang/Nim/pull/15612#issuecomment-712471879
+    t.spec.targets = {targetC}
+    testSpec r, t
+
+# ------------------------- JS tests ------------------------------------------
+
+proc jsTests(r: var TResults, cat: Category, options: string) =
+  template test(filename: untyped) =
+    testSpec r, makeTest(filename, options, cat), {targetJS}
+    testSpec r, makeTest(filename, options & " -d:release", cat), {targetJS}
+
+  for t in os.walkFiles("tests/js/t*.nim"):
+    test(t)
+  for testfile in ["exception/texceptions", "exception/texcpt1",
+                   "exception/texcsub", "exception/tfinally",
+                   "exception/tfinally2", "exception/tfinally3",
+                   "collections/tactiontable", "method/tmultimjs",
+                   "varres/tvarres0", "varres/tvarres3", "varres/tvarres4",
+                   "varres/tvartup", "int/tints", "int/tunsignedinc",
+                   "async/tjsandnativeasync"]:
+    test "tests/" & testfile & ".nim"
+
+  for testfile in ["strutils", "json", "random", "times", "logging"]:
+    test "lib/pure/" & testfile & ".nim"
+
+# ------------------------- nim in action -----------
+
+proc testNimInAction(r: var TResults, cat: Category, options: string) =
+  template test(filename: untyped) =
+    testSpec r, makeTest(filename, options, cat)
+
+  template testJS(filename: untyped) =
+    testSpec r, makeTest(filename, options, cat), {targetJS}
+
+  template testCPP(filename: untyped) =
+    testSpec r, makeTest(filename, options, cat), {targetCpp}
+
+  let tests = [
+    "niminaction/Chapter1/various1",
+    "niminaction/Chapter2/various2",
+    "niminaction/Chapter2/resultaccept",
+    "niminaction/Chapter2/resultreject",
+    "niminaction/Chapter2/explicit_discard",
+    "niminaction/Chapter2/no_def_eq",
+    "niminaction/Chapter2/no_iterator",
+    "niminaction/Chapter2/no_seq_type",
+    "niminaction/Chapter3/ChatApp/src/server",
+    "niminaction/Chapter3/ChatApp/src/client",
+    "niminaction/Chapter3/various3",
+    "niminaction/Chapter6/WikipediaStats/concurrency_regex",
+    "niminaction/Chapter6/WikipediaStats/concurrency",
+    "niminaction/Chapter6/WikipediaStats/naive",
+    "niminaction/Chapter6/WikipediaStats/parallel_counts",
+    "niminaction/Chapter6/WikipediaStats/race_condition",
+    "niminaction/Chapter6/WikipediaStats/sequential_counts",
+    "niminaction/Chapter6/WikipediaStats/unguarded_access",
+    "niminaction/Chapter7/Tweeter/src/tweeter",
+    "niminaction/Chapter7/Tweeter/src/createDatabase",
+    "niminaction/Chapter7/Tweeter/tests/database_test",
+    "niminaction/Chapter8/sdl/sdl_test"
+    ]
+
+  when false:
+    # Verify that the files have not been modified. Death shall fall upon
+    # whoever edits these hashes without dom96's permission, j/k. But please only
+    # edit when making a conscious breaking change, also please try to make your
+    # commit message clear and notify me so I can easily compile an errata later.
+    # ---------------------------------------------------------
+    # Hash-checks are disabled for Nim 1.1 and beyond
+    # since we needed to fix the deprecated unary '<' operator.
+    const refHashes = @[
+      "51afdfa84b3ca3d810809d6c4e5037ba",
+      "30f07e4cd5eaec981f67868d4e91cfcf",
+      "d14e7c032de36d219c9548066a97e846",
+      "b335635562ff26ec0301bdd86356ac0c",
+      "6c4add749fbf50860e2f523f548e6b0e",
+      "76de5833a7cc46f96b006ce51179aeb1",
+      "705eff79844e219b47366bd431658961",
+      "a1e87b881c5eb161553d119be8b52f64",
+      "2d706a6ec68d2973ec7e733e6d5dce50",
+      "c11a013db35e798f44077bc0763cc86d",
+      "3e32e2c5e9a24bd13375e1cd0467079c",
+      "a5452722b2841f0c1db030cf17708955",
+      "dc6c45eb59f8814aaaf7aabdb8962294",
+      "69d208d281a2e7bffd3eaf4bab2309b1",
+      "ec05666cfb60211bedc5e81d4c1caf3d",
+      "da520038c153f4054cb8cc5faa617714",
+      "59906c8cd819cae67476baa90a36b8c1",
+      "9a8fe78c588d08018843b64b57409a02",
+      "8b5d28e985c0542163927d253a3e4fc9",
+      "783299b98179cc725f9c46b5e3b5381f",
+      "1a2b3fba1187c68d6a9bfa66854f3318",
+      "391ff57b38d9ea6f3eeb3fe69ab539d3"
+    ]
+    for i, test in tests:
+      let filename = testsDir / test.addFileExt("nim")
+      let testHash = getMD5(readFile(filename).string)
+      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"
+  let jsFile = "tests/niminaction/Chapter8/canvas/canvas_test.nim"
+  testJS jsFile
+  let cppFile = "tests/niminaction/Chapter8/sfml/sfml_test.nim"
+  testCPP cppFile
+
+# ------------------------- manyloc -------------------------------------------
+
+proc findMainFile(dir: string): string =
+  # finds the file belonging to ".nim.cfg"; if there is no such file
+  # it returns the some ".nim" file if there is only one:
+  const cfgExt = ".nim.cfg"
+  result = ""
+  var nimFiles = 0
+  for kind, file in os.walkDir(dir):
+    if kind == pcFile:
+      if file.endsWith(cfgExt): return file[0..^(cfgExt.len+1)] & ".nim"
+      elif file.endsWith(".nim"):
+        if result.len == 0: result = file
+        inc nimFiles
+  if nimFiles != 1: result.setLen(0)
+
+proc manyLoc(r: var TResults, cat: Category, options: string) =
+  for kind, dir in os.walkDir("tests/manyloc"):
+    if kind == pcDir:
+      when defined(windows):
+        if dir.endsWith"nake": continue
+      if dir.endsWith"named_argument_bug": continue
+      let mainfile = findMainFile(dir)
+      if mainfile != "":
+        var test = makeTest(mainfile, options, cat)
+        test.spec.action = actionCompile
+        testSpec r, test
+
+proc compileExample(r: var TResults, pattern, options: string, cat: Category) =
+  for test in os.walkFiles(pattern):
+    var test = makeTest(test, options, cat)
+    test.spec.action = actionCompile
+    testSpec r, test
+
+proc testStdlib(r: var TResults, pattern, options: string, cat: Category) =
+  var files: seq[string]
+
+  proc isValid(file: string): bool =
+    for dir in parentDirs(file, inclusive = false):
+      if dir.lastPathPart in ["includes", "nimcache"]:
+        # e.g.: 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)
+    var testObj = makeTest(testFile, options, cat)
+    #[
+    todo:
+    this logic is fragile:
+    false positives (if appears in a comment), or false negatives, e.g.
+    `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 ----------------------------------------
+proc listPackagesAll(): seq[NimblePackage] =
+  var nimbleDir = getEnv("NIMBLE_DIR")
+  if nimbleDir.len == 0: nimbleDir = getHomeDir() / ".nimble"
+  let packageIndex = nimbleDir / "packages_official.json"
+  let packageList = parseFile(packageIndex)
+  proc findPackage(name: string): JsonNode =
+    for a in packageList:
+      if a["name"].str == name: return a
+  for pkg in important_packages.packages.items:
+    var pkg = pkg
+    if pkg.url.len == 0:
+      let pkg2 = findPackage(pkg.name)
+      if pkg2 == nil:
+        raise newException(ValueError, "Cannot find package '$#'." % pkg.name)
+      pkg.url = pkg2["url"].str
+    result.add pkg
+
+proc listPackages(packageFilter: string): seq[NimblePackage] =
+  let pkgs = listPackagesAll()
+  if packageFilter.len != 0:
+    # xxx document `packageFilter`, seems like a bad API,
+    # at least should be a regex; a substring match makes no sense.
+    result = pkgs.filterIt(packageFilter in it.name)
+  else:
+    if testamentData0.batchArg == "allowed_failures":
+      result = pkgs.filterIt(it.allowFailure)
+    elif testamentData0.testamentNumBatch == 0:
+      result = pkgs
+    else:
+      let pkgs2 = pkgs.filterIt(not it.allowFailure)
+      for i in 0..<pkgs2.len:
+        if i mod testamentData0.testamentNumBatch == testamentData0.testamentBatch:
+          result.add pkgs2[i]
+
+proc makeSupTest(test, options: string, cat: Category, debugInfo = ""): TTest =
+  result.cat = cat
+  result.name = test
+  result.options = options
+  result.debugInfo = debugInfo
+  result.startTime = epochTime()
+
+import std/private/gitutils
+
+proc testNimblePackages(r: var TResults; cat: Category; packageFilter: string) =
+  let nimbleExe = findExe("nimble")
+  doAssert nimbleExe != "", "Cannot run nimble tests: Nimble binary not found."
+  doAssert execCmd("$# update" % nimbleExe) == 0, "Cannot run nimble tests: Nimble update failed."
+  let packageFileTest = makeSupTest("PackageFileParsed", "", cat)
+  let packagesDir = "pkgstemp"
+  createDir(packagesDir)
+  var errors = 0
+  try:
+    let pkgs = listPackages(packageFilter)
+    for i, pkg in pkgs:
+      inc r.total
+      var test = makeSupTest(pkg.name, "", cat, "[$#/$#] " % [$i, $pkgs.len])
+      let buildPath = packagesDir / pkg.name
+      template tryCommand(cmd: string, workingDir2 = buildPath, reFailed = reInstallFailed, maxRetries = 1): string =
+        var outp: string
+        let ok = retryCall(maxRetry = maxRetries, backoffDuration = 10.0):
+          var status: int
+          (outp, status) = execCmdEx(cmd, workingDir = workingDir2)
+          status == QuitSuccess
+        if not ok:
+          if pkg.allowFailure:
+            inc r.passed
+            inc r.failedButAllowed
+          addResult(r, test, targetC, "", "", cmd & "\n" & outp, reFailed, allowFailure = pkg.allowFailure)
+          continue
+        outp
+
+      if not dirExists(buildPath):
+        discard tryCommand("git clone $# $#" % [pkg.url.quoteShell, buildPath.quoteShell], workingDir2 = ".", maxRetries = 3)
+        if not pkg.useHead:
+          discard tryCommand("git fetch --tags", maxRetries = 3)
+          let describeOutput = tryCommand("git describe --tags --abbrev=0")
+          discard tryCommand("git checkout $#" % [describeOutput.strip.quoteShell])
+        discard tryCommand("nimble install --depsOnly -y", maxRetries = 3)
+      let cmds = pkg.cmd.split(';')
+      for i in 0 ..< cmds.len - 1:
+        discard tryCommand(cmds[i], maxRetries = 3)
+      discard tryCommand(cmds[^1], reFailed = reBuildFailed)
+      inc r.passed
+      r.addResult(test, targetC, "", "", "", reSuccess, allowFailure = pkg.allowFailure)
+
+    errors = r.total - r.passed
+    if errors == 0:
+      r.addResult(packageFileTest, targetC, "", "", "", reSuccess)
+    else:
+      r.addResult(packageFileTest, targetC, "", "", "", reBuildFailed)
+
+  except JsonParsingError:
+    errors = 1
+    r.addResult(packageFileTest, targetC, "", "", "Invalid package file", reBuildFailed)
+    raise
+  except ValueError:
+    errors = 1
+    r.addResult(packageFileTest, targetC, "", "", "Unknown package", reBuildFailed)
+    raise # bug #18805
+  finally:
+    if errors == 0: removeDir(packagesDir)
+
+# ---------------- IC tests ---------------------------------------------
+
+proc icTests(r: var TResults; testsDir: string, cat: Category, options: string;
+             isNavigatorTest: bool) =
+  const
+    tooltests = ["compiler/nim.nim"]
+    writeOnly = " --incremental:writeonly "
+    readOnly = " --incremental:readonly "
+    incrementalOn = " --incremental:on -d:nimIcIntegrityChecks "
+    navTestConfig = " --ic:on -d:nimIcNavigatorTests --hint:Conf:off --warnings:off "
+
+  template test(x: untyped) =
+    testSpecWithNimcache(r, makeRawTest(file, x & options, cat), nimcache)
+
+  template editedTest(x: untyped) =
+    var test = makeTest(file, x & options, cat)
+    if isNavigatorTest:
+      test.spec.action = actionCompile
+    test.spec.targets = {getTestSpecTarget()}
+    testSpecWithNimcache(r, test, nimcache)
+
+  template checkTest() =
+    var test = makeRawTest(file, options, cat)
+    test.spec.cmd = compilerPrefix & " check --hint:Conf:off --warnings:off --ic:on $options " & file
+    testSpecWithNimcache(r, test, nimcache)
+
+  if not isNavigatorTest:
+    for file in tooltests:
+      let nimcache = nimcacheDir(file, options, getTestSpecTarget())
+      removeDir(nimcache)
+
+      let oldPassed = r.passed
+      checkTest()
+
+      if r.passed == oldPassed+1:
+        checkTest()
+        if r.passed == oldPassed+2:
+          checkTest()
+
+  const tempExt = "_temp.nim"
+  for it in walkDirRec(testsDir):
+  # for it in ["tests/ic/timports.nim"]: # debugging: to try a specific test
+    if isTestFile(it) and not it.endsWith(tempExt):
+      let nimcache = nimcacheDir(it, options, getTestSpecTarget())
+      removeDir(nimcache)
+
+      let content = readFile(it)
+      for fragment in content.split("#!EDIT!#"):
+        let file = it.replace(".nim", tempExt)
+        writeFile(file, fragment)
+        let oldPassed = r.passed
+        editedTest(if isNavigatorTest: navTestConfig else: incrementalOn)
+        if r.passed != oldPassed+1: break
+
+# ----------------------------------------------------------------------------
+
+const AdditionalCategories = ["debugger", "examples", "lib", "ic", "navigator"]
+const MegaTestCat = "megatest"
+
+proc `&.?`(a, b: string): string =
+  # candidate for the stdlib?
+  result = if b.startsWith(a): b else: a & b
+
+proc processSingleTest(r: var TResults, cat: Category, options, test: string, targets: set[TTarget], targetsSet: bool) =
+  var targets = targets
+  if not targetsSet:
+    let target = if cat.string.normalize == "js": targetJS else: targetC
+    targets = {target}
+  doAssert fileExists(test), test & " test does not exist"
+  testSpec r, makeTest(test, options, cat), targets
+
+proc isJoinableSpec(spec: TSpec): bool =
+  # xxx simplify implementation using a whitelist of fields that are allowed to be
+  # set to non-default values (use `fieldPairs`), to avoid issues like bug #16576.
+  result = useMegatest and 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
+    spec.exitCode == 0 and
+    spec.input.len == 0 and
+    spec.nimout.len == 0 and
+    spec.nimoutFull == false and
+      # so that tests can have `nimoutFull: true` with `nimout.len == 0` with
+      # the meaning that they expect empty output.
+    spec.matrix.len == 0 and
+    spec.outputCheck != ocSubstr and
+    spec.ccodeCheck.len == 0 and
+    (spec.targets == {} or spec.targets == {targetC})
+  if result:
+    if spec.file.readFile.contains "when isMainModule":
+      result = false
+
+proc quoted(a: string): string =
+  # todo: consider moving to system.nim
+  result.addQuoted(a)
+
+proc runJoinedTest(r: var TResults, cat: Category, testsDir: string, options: string) =
+  ## returns a list of tests that have problems
+  #[
+  xxx create a reusable megatest API after abstracting out testament specific code,
+  refs https://github.com/timotheecour/Nim/issues/655
+  and https://github.com/nim-lang/gtk2/pull/28; it's useful in other contexts.
+  ]#
+  var specs: seq[TSpec] = @[]
+  for kind, dir in walkDir(testsDir):
+    assert dir.startsWith(testsDir)
+    let cat = dir[testsDir.len .. ^1]
+    if kind == pcDir and cat notin specialCategories:
+      for file in walkDirRec(testsDir / cat):
+        if isTestFile(file):
+          var spec: TSpec
+          try:
+            spec = parseSpec(file)
+          except ValueError:
+            # e.g. for `tests/navigator/tincludefile.nim` which have multiple
+            # specs; this will be handled elsewhere
+            echo "parseSpec raised ValueError for: '$1', assuming this will be handled outside of megatest" % file
+            continue
+          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
+  # xxx (minor) put outputExceptedFile, outputGottenFile, megatestFile under here or `buildDir`
+  var outDir = nimcacheDir(testsDir / "megatest", "", targetC)
+  template toMarker(file, i): string =
+    "megatest:processing: [$1] $2" % [$i, file]
+  for i, runSpec in specs:
+    let file = runSpec.file
+    let file2 = outDir / ("megatest_a_$1.nim" % $i)
+    # `include` didn't work with `trecmod2.nim`, so using `import`
+    let code = "echo $1\nstatic: echo \"CT:\", $1\n" % [toMarker(file, i).quoted]
+    createDir(file2.parentDir)
+    writeFile(file2, code)
+    megatest.add "import $1\nimport $2 as megatest_b_$3\n" % [file2.quoted, file.quoted, $i]
+
+  let megatestFile = testsDir / "megatest.nim" # so it uses testsDir / "config.nims"
+  writeFile(megatestFile, megatest)
+
+  let root = getCurrentDir()
+
+  var args = @["c", "--nimCache:" & outDir, "-d:testing", "-d:nimMegatest", "--listCmd",
+              "--path:" & root]
+  args.add options.parseCmdLine
+  args.add megatestFile
+  var (cmdLine, buf, exitCode) = execCmdEx2(command = compilerPrefix, args = args, input = "")
+  if exitCode != 0:
+    echo "$ " & cmdLine & "\n" & buf
+    quit(failString & "megatest compilation failed")
+
+  (buf, exitCode) = execCmdEx(megatestFile.changeFileExt(ExeExt).dup normalizeExe)
+  if exitCode != 0:
+    echo buf
+    quit(failString & "megatest execution failed")
+
+  const outputExceptedFile = "outputExpected.txt"
+  const outputGottenFile = "outputGotten.txt"
+  writeFile(outputGottenFile, buf)
+  var outputExpected = ""
+  for i, runSpec in specs:
+    outputExpected.add toMarker(runSpec.file, i) & "\n"
+    if runSpec.output.len > 0:
+      outputExpected.add runSpec.output
+      if not runSpec.output.endsWith "\n":
+        outputExpected.add '\n'
+
+  if buf != outputExpected:
+    writeFile(outputExceptedFile, outputExpected)
+    echo diffFiles(outputGottenFile, outputExceptedFile).output
+    echo failString & "megatest output different, see $1 vs $2" % [outputGottenFile, outputExceptedFile]
+    # outputGottenFile, outputExceptedFile not removed on purpose for debugging.
+    quit 1
+  else:
+    echo "megatest output OK"
+
+
+# ---------------------------------------------------------------------------
+
+proc processCategory(r: var TResults, cat: Category,
+                     options, testsDir: string,
+                     runJoinableTests: bool) =
+  let cat2 = cat.string.normalize
+  var handled = false
+  if isNimRepoTests():
+    handled = true
+    case cat2
+    of "js":
+      # only run the JS tests on Windows or Linux because Travis is bad
+      # and other OSes like Haiku might lack nodejs:
+      if not defined(linux) and isTravis:
+        discard
+      else:
+        jsTests(r, cat, options)
+    of "dll":
+      dllTests(r, cat, options & " -d:nimDebugDlOpen")
+    of "gc":
+      gcTests(r, cat, options)
+    of "debugger":
+      debuggerTests(r, cat, options)
+    of "manyloc":
+      manyLoc r, cat, options
+    of "threads":
+      threadTests r, cat, options & " --threads:on"
+    of "io":
+      ioTests r, cat, options
+    of "async":
+      asyncTests r, cat, options
+    of "lib":
+      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)
+      compileExample(r, "examples/talk/*.nim", options, cat)
+    of "nimble-packages":
+      testNimblePackages(r, cat, options)
+    of "niminaction":
+      testNimInAction(r, cat, options)
+    of "ic":
+      icTests(r, testsDir / cat2, cat, options, isNavigatorTest=false)
+    of "navigator":
+      icTests(r, testsDir / cat2, cat, options, isNavigatorTest=true)
+    of "untestable":
+      # These require special treatment e.g. because they depend on a third party
+      # dependency; see `trunner_special` which runs some of those.
+      discard
+    else:
+      handled = false
+  if not handled:
+    case cat2
+    of "megatest":
+      runJoinedTest(r, cat, testsDir, options)
+      if isNimRepoTests():
+        runJoinedTest(r, cat, testsDir, options & " --mm:refc")
+    else:
+      var testsRun = 0
+      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"
+        else:
+          test.spec.err = reJoined
+        testSpec r, test
+        inc testsRun
+      if testsRun == 0:
+        const whiteListedDirs = ["deps", "htmldocs", "pkgs"]
+          # `pkgs` because bug #16556 creates `pkgs` dirs and this can affect some users
+          # that try an old version of choosenim.
+        doAssert cat.string in whiteListedDirs,
+          "Invalid category specified: '$#' not in whilelist: $#" % [cat.string, $whiteListedDirs]
+
+proc processPattern(r: var TResults, pattern, options: string; simulate: bool) =
+  var testsRun = 0
+  if dirExists(pattern):
+    for k, name in walkDir(pattern):
+      if k in {pcFile, pcLinkToFile} and name.endsWith(".nim"):
+        if simulate:
+          echo "Detected test: ", name
+        else:
+          var test = makeTest(name, options, Category"pattern")
+          testSpec r, test
+        inc testsRun
+  else:
+    for name in walkPattern(pattern):
+      if simulate:
+        echo "Detected test: ", name
+      else:
+        var test = makeTest(name, options, Category"pattern")
+        testSpec r, test
+      inc testsRun
+  if testsRun == 0:
+    echo "no tests were found for pattern: ", pattern
diff --git a/tests/testament/htmlgen.nim b/testament/htmlgen.nim
index 4a888427e..174f36d0b 100644
--- a/tests/testament/htmlgen.nim
+++ b/testament/htmlgen.nim
@@ -9,21 +9,21 @@
 
 ## HTML generator for the tester.
 
-import cgi, backend, strutils, json, os, tables, times
+import strutils, json, os, times
 
-import "testamenthtml.templ"
+import "testamenthtml.nimf"
 
 proc generateTestResultPanelPartial(outfile: File, testResultRow: JsonNode) =
   let
     trId = htmlQuote(testResultRow["category"].str & "_" & testResultRow["name"].str).
-        multiReplace({".": "_", " ": "_", ":": "_"})
+        multiReplace({".": "_", " ": "_", ":": "_", "/": "_"})
     name = testResultRow["name"].str.htmlQuote()
     category = testResultRow["category"].str.htmlQuote()
     target = testResultRow["target"].str.htmlQuote()
     action = testResultRow["action"].str.htmlQuote()
     result = htmlQuote testResultRow["result"].str
-    expected = testResultRow["expected"].str
-    gotten = testResultRow["given"].str
+    expected = testResultRow["expected"].getStr
+    gotten = testResultRow["given"].getStr
     timestamp = "unknown"
   var
     panelCtxClass, textCtxClass, bgCtxClass: string
@@ -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"
@@ -52,7 +52,7 @@ proc generateTestResultPanelPartial(outfile: File, testResultRow: JsonNode) =
     trId, name, target, category, action, resultDescription,
     timestamp, result, resultSign, panelCtxClass, textCtxClass, bgCtxClass
   )
-  if expected.isNilOrWhitespace() and gotten.isNilOrWhitespace():
+  if expected.isEmptyOrWhitespace() and gotten.isEmptyOrWhitespace():
     outfile.generateHtmlTestresultOutputNone()
   else:
     outfile.generateHtmlTestresultOutputDetails(
@@ -63,7 +63,7 @@ proc generateTestResultPanelPartial(outfile: File, testResultRow: JsonNode) =
 
 type
   AllTests = object
-    data: JSonNode
+    data: JsonNode
     totalCount, successCount, ignoredCount, failedCount: int
     successPercentage, ignoredPercentage, failedPercentage: BiggestFloat
 
@@ -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/important_packages.nim b/testament/important_packages.nim
new file mode 100644
index 000000000..efec04b3c
--- /dev/null
+++ b/testament/important_packages.nim
@@ -0,0 +1,193 @@
+##[
+## note 1
+`useHead` should ideally be used as the default but lots of packages (e.g. `chronos`)
+don't have release tags (or have really old ones compared to HEAD), making it
+impossible to test them reliably here.
+
+packages listed here should ideally have regularly updated release tags, so that:
+* we're testing recent versions of the package
+* the version that's tested is stable enough even if HEAD may occasionally break
+
+## note 2: D20210308T165435:here
+nimble packages should be testable as follows:
+git clone $url $dir && cd $dir
+NIMBLE_DIR=$TMP_NIMBLE_DIR XDG_CONFIG_HOME= nimble install --depsOnly -y
+NIMBLE_DIR=$TMP_NIMBLE_DIR XDG_CONFIG_HOME= nimble test
+
+if this fails (e.g. nimcrypto), it could be because a package lacks a `tests/nim.cfg` with `--path:..`,
+so the above commands would've worked by accident with `nimble install` but not with `nimble install --depsOnly`.
+When this is the case, a workaround is to test this package here by adding `--path:$srcDir` on the test `cmd`.
+]##
+
+type NimblePackage* = object
+  name*, cmd*, url*: string
+  useHead*: bool
+  allowFailure*: bool
+    ## When true, we still run the test but the test is allowed to fail.
+    ## This is useful for packages that currently fail but that we still want to
+    ## run in CI, e.g. so that we can monitor when they start working again and
+    ## are reminded about those failures without making CI fail for unrelated PRs.
+
+var packages*: seq[NimblePackage]
+
+proc pkg(name: string; cmd = "nimble test"; url = "", useHead = true, allowFailure = false) =
+  packages.add NimblePackage(name: name, cmd: cmd, url: url, useHead: useHead, allowFailure: allowFailure)
+
+pkg "alea"
+pkg "argparse"
+pkg "arraymancer", "nim c tests/tests_cpu.nim"
+pkg "ast_pattern_matching", "nim c -r tests/test1.nim"
+pkg "asyncftpclient", "nimble compileExample"
+pkg "asyncthreadpool", "nimble test --mm:refc"
+pkg "awk"
+pkg "bigints"
+pkg "binaryheap", "nim c -r binaryheap.nim"
+pkg "BipBuffer"
+pkg "blscurve", allowFailure = true
+pkg "bncurve"
+pkg "brainfuck", "nim c -d:release -r tests/compile.nim"
+pkg "bump", "nim c --mm:arc --path:. -r tests/tbump.nim", "https://github.com/disruptek/bump", allowFailure = true
+pkg "c2nim", "nim c testsuite/tester.nim"
+pkg "cascade"
+pkg "cello", url = "https://github.com/nim-lang/cello", useHead = true
+pkg "checksums"
+pkg "chroma"
+pkg "chronicles", "nim c -o:chr -r chronicles.nim"
+pkg "chronos", "nim c -r -d:release tests/testall"
+pkg "cligen", "nim c --path:. -r cligen.nim"
+pkg "combparser", "nimble test --mm:orc"
+pkg "compactdict"
+pkg "comprehension", "nimble test", "https://github.com/alehander92/comprehension"
+pkg "constantine", "nimble make_lib"
+pkg "cowstrings"
+pkg "criterion", allowFailure = true # needs testing binary
+pkg "datamancer"
+pkg "dashing", "nim c tests/functional.nim"
+pkg "delaunay"
+pkg "dnsclient", allowFailure = true # super fragile
+pkg "docopt"
+pkg "dotenv"
+# when defined(linux): pkg "drchaos"
+pkg "easygl", "nim c -o:egl -r src/easygl.nim", "https://github.com/jackmott/easygl"
+pkg "elvis"
+pkg "faststreams"
+pkg "fidget"
+pkg "fragments", "nim c -r fragments/dsl.nim", allowFailure = true # pending https://github.com/nim-lang/packages/issues/2115 
+pkg "fusion"
+pkg "gara"
+pkg "glob"
+pkg "ggplotnim", "nim c -d:noCairo -r tests/tests.nim"
+pkg "gittyup", "nimble test", "https://github.com/disruptek/gittyup", allowFailure = true
+pkg "gnuplot", "nim c gnuplot.nim"
+# pkg "gram", "nim c -r --mm:arc --define:danger tests/test.nim", "https://github.com/disruptek/gram"
+  # pending https://github.com/nim-lang/Nim/issues/16509
+pkg "hts", "nim c -o:htss src/hts.nim"
+pkg "httpauth"
+pkg "httputils"
+pkg "illwill", "nimble examples"
+pkg "inim"
+pkg "itertools", "nim doc src/itertools.nim"
+pkg "iterutils"
+pkg "json_rpc"
+pkg "json_serialization"
+pkg "jstin"
+pkg "karax", "nim c -r tests/tester.nim"
+pkg "kdtree", "nimble test -d:nimLegacyRandomInitRand", "https://github.com/jblindsay/kdtree"
+pkg "loopfusion"
+pkg "lockfreequeues"
+pkg "macroutils"
+pkg "manu"
+pkg "markdown"
+pkg "measuremancer", "nimble testDeps; nimble -y test"
+pkg "memo"
+pkg "msgpack4nim", "nim c -r tests/test_spec.nim"
+pkg "nake", "nim c nakefile.nim"
+pkg "neo", "nim c -d:blas=openblas --mm:refc tests/all.nim"
+pkg "nesm", "nimble tests", "https://github.com/nim-lang/NESM", useHead = true, allowFailure = true
+  # inactive, tests not adapted to #23096
+pkg "netty"
+pkg "nico", allowFailure = true
+pkg "nicy", "nim c -r src/nicy.nim"
+pkg "nigui", "nim c -o:niguii -r src/nigui.nim"
+pkg "nimcrypto", "nim r --path:. tests/testall.nim" # `--path:.` workaround needed, see D20210308T165435
+pkg "NimData", "nim c -o:nimdataa src/nimdata.nim"
+pkg "nimes", "nim c src/nimes.nim"
+pkg "nimfp", "nim c -o:nfp -r src/fp.nim"
+pkg "nimgame2", "nim c --mm:refc nimgame2/nimgame.nim"
+pkg "nimgen", "nim c -o:nimgenn -r src/nimgen/runcfg.nim"
+pkg "nimib"
+pkg "nimlsp"
+pkg "nimly", "nim c -r tests/test_readme_example.nim"
+pkg "nimongo", "nimble test_ci", allowFailure = true
+pkg "nimph", "nimble test", "https://github.com/disruptek/nimph", allowFailure = true
+pkg "nimPNG", useHead = true
+pkg "nimpy", "nim c -r tests/nimfrompy.nim"
+pkg "nimquery"
+pkg "nimsl"
+pkg "nimsvg"
+pkg "nimterop", "nimble minitest", url = "https://github.com/nim-lang/nimterop"
+pkg "nimwc", "nim c nimwc.nim"
+pkg "nimx", "nim c test/main.nim", allowFailure = true
+pkg "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter"
+pkg "norm", "testament r tests/common/tmodel.nim"
+pkg "normalize"
+pkg "npeg", "nimble testarc"
+pkg "numericalnim", "nimble nimCI"
+pkg "optionsutils"
+pkg "ormin", "nim c -o:orminn ormin.nim"
+pkg "parsetoml"
+pkg "patty"
+pkg "pixie"
+pkg "plotly", "nim c examples/all.nim"
+pkg "pnm"
+pkg "polypbren"
+pkg "presto"
+pkg "prologue", "nimble tcompile"
+# remove fork after https://github.com/PMunch/combparser/pull/7 is merged:
+pkg "protobuf", "nimble install -y https://github.com/metagn/combparser@#HEAD; nim c -o:protobuff -r src/protobuf.nim"
+pkg "rbtree"
+pkg "react", "nimble example"
+pkg "regex", "nim c src/regex"
+pkg "results", "nim c -r results.nim"
+pkg "RollingHash", "nim c -r tests/test_cyclichash.nim"
+pkg "rosencrantz", "nim c -o:rsncntz -r rosencrantz.nim"
+pkg "sdl1", "nim c -r src/sdl.nim"
+pkg "sdl2_nim", "nim c -r sdl2/sdl.nim"
+pkg "serialization"
+pkg "sigv4", "nim c --mm:arc -r sigv4.nim", "https://github.com/disruptek/sigv4"
+pkg "sim"
+pkg "smtp", "nimble compileExample"
+pkg "snip", "nimble test", "https://github.com/genotrance/snip"
+pkg "ssostrings"
+pkg "stew"
+pkg "stint", "nim c stint.nim"
+pkg "strslice"
+pkg "strunicode", "nim c -r --mm:refc src/strunicode.nim"
+pkg "supersnappy"
+pkg "synthesis"
+pkg "taskpools"
+pkg "telebot", "nim c -o:tbot -r src/telebot.nim"
+pkg "tempdir"
+pkg "templates"
+pkg "tensordsl", "nim c -r --mm:refc tests/tests.nim", "https://krux02@bitbucket.org/krux02/tensordslnim.git"
+pkg "terminaltables", "nim c src/terminaltables.nim"
+pkg "termstyle", "nim c -r termstyle.nim"
+pkg "testutils"
+pkg "timeit"
+pkg "timezones"
+pkg "tiny_sqlite"
+pkg "unicodedb", "nim c -d:release -r tests/tests.nim"
+pkg "unicodeplus", "nim c -d:release -r tests/tests.nim"
+pkg "union", "nim c -r tests/treadme.nim", url = "https://github.com/alaviss/union"
+pkg "unittest2"
+pkg "unpack"
+pkg "weave", "nimble install -y cligen@#HEAD; nimble test_gc_arc", useHead = true
+pkg "websock"
+pkg "websocket", "nim c websocket.nim"
+# pkg "winim", allowFailure = true
+pkg "with"
+pkg "ws", allowFailure = true
+pkg "yaml"
+pkg "zero_functional", "nim c -r test.nim"
+pkg "zippy"
+pkg "zxcvbn"
diff --git a/testament/lib/readme.md b/testament/lib/readme.md
new file mode 100644
index 000000000..20e866338
--- /dev/null
+++ b/testament/lib/readme.md
@@ -0,0 +1,4 @@
+This directory contains helper files used by several tests, to avoid
+code duplication, akin to a std extension tailored for testament.
+
+Some of these could later migrate to stdlib.
diff --git a/testament/lib/stdtest/netutils.nim b/testament/lib/stdtest/netutils.nim
new file mode 100644
index 000000000..5115390e0
--- /dev/null
+++ b/testament/lib/stdtest/netutils.nim
@@ -0,0 +1,13 @@
+import std/[nativesockets, asyncdispatch, os]
+
+proc bindAvailablePort*(handle: SocketHandle, port = Port(0)): Port =
+  ## See also `asynchttpserver.getPort`.
+  block:
+    var name: Sockaddr_in
+    name.sin_family = typeof(name.sin_family)(toInt(AF_INET))
+    name.sin_port = htons(uint16(port))
+    name.sin_addr.s_addr = htonl(INADDR_ANY)
+    if bindAddr(handle, cast[ptr SockAddr](addr(name)),
+                sizeof(name).Socklen) < 0'i32:
+      raiseOSError(osLastError(), $port)
+  result = getLocalAddr(handle, AF_INET)[1]
diff --git a/testament/lib/stdtest/specialpaths.nim b/testament/lib/stdtest/specialpaths.nim
new file mode 100644
index 000000000..e214d113d
--- /dev/null
+++ b/testament/lib/stdtest/specialpaths.nim
@@ -0,0 +1,55 @@
+#[
+todo: move findNimStdLibCompileTime, findNimStdLib here
+xxx: factor pending https://github.com/timotheecour/Nim/issues/616
+
+## note: $lib vs $nim
+note: these can resolve to 3 different paths if running via `nim c --lib:lib foo`,
+eg if compiler was installed via nimble (or is in nim path), and nim is external
+(ie not in `$lib/../bin/` dir)
+
+import "$lib/../compiler/nimpaths" # <- most robust if you want to favor --lib:lib
+import "$nim/compiler/nimpaths"
+import compiler/nimpaths
+]#
+
+import os
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+# 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
+  nimRootDir* = sourcePath.parentDir.parentDir.parentDir.parentDir ## root of Nim repo
+  testsFname* = "tests"
+  stdlibDir* = nimRootDir / "lib"
+  systemPath* = stdlibDir / "system.nim"
+  testsDir* = nimRootDir / testsFname
+  buildDir* = nimRootDir / "build"
+    ## refs #10268: all testament generated files should go here to avoid
+    ## polluting .gitignore
+
+proc splitTestFile*(file: string): tuple[cat: string, path: string] =
+  ## At least one directory is required in the path, to use as a category name
+  runnableExamples:
+    doAssert splitTestFile("tests/fakedir/tfakename.nim") == ("fakedir", "tests/fakedir/tfakename.nim".unixToNativePath)
+  for p in file.parentDirs(inclusive = false):
+    let parent = p.parentDir
+    if parent.lastPathPart == testsFname:
+      result.cat = p.lastPathPart
+      let dir = getCurrentDir()
+      if file.isRelativeTo(dir):
+        result.path = file.relativePath(dir)
+      else:
+        result.path = file
+      return result
+  raiseAssert "file must match this pattern: '/pathto/tests/dir/**/tfile.nim', got: '" & file & "'"
+
+static:
+  # sanity check
+  doAssert fileExists(systemPath)
diff --git a/testament/lib/stdtest/testutils.nim b/testament/lib/stdtest/testutils.nim
new file mode 100644
index 000000000..a490b17c8
--- /dev/null
+++ b/testament/lib/stdtest/testutils.nim
@@ -0,0 +1,126 @@
+import std/private/miscdollars
+when defined(nimscript):
+  import std/os # xxx investigate why needed
+else:
+  from std/os import getEnv
+import std/[macros, genasts]
+
+template flakyAssert*(cond: untyped, msg = "", notifySuccess = true) =
+  ## API to deal with flaky or failing tests. This avoids disabling entire tests
+  ## altogether so that at least the parts that are working are kept being
+  ## tested. This also avoids making CI fail periodically for tests known to
+  ## be flaky. Finally, for known failures, passing `notifySuccess = true` will
+  ## log that the test succeeded, which may indicate that a bug was fixed
+  ## "by accident" and should be looked into.
+  const info = instantiationInfo(-1, true)
+  const expr = astToStr(cond)
+  if cond and not notifySuccess:
+    discard # silent success
+  else:
+    var msg2 = ""
+    toLocation(msg2, info.filename, info.line, info.column)
+    if cond:
+      # a flaky test is failing, we still report it but we don't fail CI
+      msg2.add " FLAKY_SUCCESS "
+    else:
+      # a previously failing test is now passing, a pre-existing bug might've been
+      # fixed by accidend
+      msg2.add " FLAKY_FAILURE "
+    msg2.add $expr & " " & msg
+    echo msg2
+
+when not defined(js) and not defined(nimscript):
+  import std/strutils
+
+  proc greedyOrderedSubsetLines*(lhs, rhs: string, allowPrefixMatch = false): bool =
+    ## Returns true if each stripped line in `lhs` appears in rhs, using a greedy matching.
+    # xxx improve error reporting by showing the last matched pair
+    iterator splitLinesClosure(): string {.closure.} =
+      for line in splitLines(rhs.strip):
+        yield line
+    template isMatch(lhsi, rhsi): bool =
+      if allowPrefixMatch:
+        startsWith(rhsi, lhsi)
+      else:
+        lhsi == rhsi
+
+    var rhsIter = splitLinesClosure
+    var currentLine = strip(rhsIter())
+
+    for line in lhs.strip.splitLines:
+      let line = line.strip
+      if line.len != 0:
+        while not isMatch(line, currentLine):
+          currentLine = strip(rhsIter())
+          if rhsIter.finished:
+            return false
+
+      if rhsIter.finished:
+        return false
+    return true
+
+template enableRemoteNetworking*: bool =
+  ## Allows contolling whether to run some test at a statement-level granularity.
+  ## Using environment variables simplifies propagating this all the way across
+  ## process calls, e.g. `testament all` calls itself, which in turns invokes
+  ## a `nim` invocation (possibly via additional intermediate processes).
+  getEnv("NIM_TESTAMENT_REMOTE_NETWORKING") == "1"
+
+template disableSSLTesting*: bool =
+  ## TODO: workaround for GitHub Action gcc 14 matrix; remove this
+  ## matrix and the flag after Azure agent supports ubuntu 24.04
+  getEnv("NIM_TESTAMENT_DISABLE_SSL") == "1"
+
+template whenRuntimeJs*(bodyIf, bodyElse) =
+  ##[
+  Behaves as `when defined(js) and not nimvm` (which isn't legal yet).
+  pending improvements to `nimvm`, this sugar helps; use as follows:
+
+  whenRuntimeJs:
+    doAssert defined(js)
+    when nimvm: doAssert false
+    else: discard
+  do:
+    discard
+  ]##
+  when nimvm: bodyElse
+  else:
+    when defined(js): bodyIf
+    else: bodyElse
+
+template whenVMorJs*(bodyIf, bodyElse) =
+  ## Behaves as: `when defined(js) or nimvm`
+  when nimvm: bodyIf
+  else:
+    when defined(js): bodyIf
+    else: bodyElse
+
+template accept*(a) =
+  doAssert compiles(a)
+
+template reject*(a) =
+  doAssert not compiles(a)
+
+template disableVm*(body) =
+  when nimvm: discard
+  else: body
+
+macro assertAll*(body) =
+  ## works in VM, unlike `check`, `require`
+  runnableExamples:
+    assertAll:
+      1+1 == 2
+      var a = @[1, 2] # statements work
+      a.len == 2
+  # remove this once these support VM, pending #10129 (closed but not yet fixed)
+  result = newStmtList()
+  for a in body:
+    result.add genAst(a, a2 = a.repr, info = lineInfo(a)) do:
+      # D20210421T014713:here
+      # xxx pending https://github.com/nim-lang/Nim/issues/12030,
+      # `typeof` should introduce its own scope, so that this
+      # is sufficient: `typeof(a)` instead of `typeof(block: a)`
+      when typeof(block: a) is void: a
+      else:
+        if not a:
+          raise newException(AssertionDefect, info & " " & a2)
diff --git a/testament/lib/stdtest/unittest_light.nim b/testament/lib/stdtest/unittest_light.nim
new file mode 100644
index 000000000..4ab1d7543
--- /dev/null
+++ b/testament/lib/stdtest/unittest_light.nim
@@ -0,0 +1,37 @@
+import std/assertions
+
+
+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, msg = "") =
+  if lhs != rhs:
+    doAssert false, mismatch(lhs, rhs) & '\n' & msg
diff --git a/testament/specs.nim b/testament/specs.nim
new file mode 100644
index 000000000..c3040c1d8
--- /dev/null
+++ b/testament/specs.nim
@@ -0,0 +1,521 @@
+#
+#
+#            Nim Tester
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+import sequtils, parseutils, strutils, os, streams, parsecfg,
+  tables, hashes, sets
+import compiler/platform
+
+type TestamentData* = ref object
+  # better to group globals under 1 object; could group the other ones here too
+  batchArg*: string
+  testamentNumBatch*: int
+  testamentBatch*: int
+
+let testamentData0* = TestamentData()
+
+var compilerPrefix* = findExe("nim")
+
+let isTravis* = existsEnv("TRAVIS")
+let isAppVeyor* = existsEnv("APPVEYOR")
+let isAzure* = existsEnv("TF_BUILD")
+
+var skips*: seq[string]
+
+type
+  TTestAction* = enum
+    actionRun = "run"
+    actionCompile = "compile"
+    actionReject = "reject"
+
+  TOutputCheck* = enum
+    ocIgnore = "ignore"
+    ocEqual = "equal"
+    ocSubstr = "substr"
+
+  TResultEnum* = enum
+    reNimcCrash,       # nim compiler seems to have crashed
+    reMsgsDiffer,      # error messages differ
+    reFilesDiffer,     # expected and given filenames differ
+    reLinesDiffer,     # expected and given line numbers differ
+    reOutputsDiffer,
+    reExitcodesDiffer, # exit codes of program or of valgrind differ
+    reTimeout,
+    reInvalidPeg,
+    reCodegenFailure,
+    reCodeNotFound,
+    reExeNotFound,
+    reInstallFailed    # package installation failed
+    reBuildFailed      # package building failed
+    reDisabled,        # test is disabled
+    reJoined,          # test is disabled because it was joined into the megatest
+    reSuccess          # test was successful
+    reInvalidSpec      # test had problems to parse the spec
+
+  TTarget* = enum
+    targetC = "c"
+    targetCpp = "cpp"
+    targetObjC = "objc"
+    targetJS = "js"
+
+  InlineError* = object
+    kind*: string
+    msg*: string
+    line*, col*: int
+
+  ValgrindSpec* = enum
+    disabled, enabled, leaking
+
+  TSpec* = object
+    # xxx make sure `isJoinableSpec` takes into account each field here.
+    action*: TTestAction
+    file*, cmd*: string
+    filename*: string ## Test filename (without path).
+    input*: string
+    outputCheck*: TOutputCheck
+    sortoutput*: bool
+    output*: string
+    line*, column*: int
+    exitCode*: int
+    msg*: string
+    ccodeCheck*: seq[string]
+    maxCodeSize*: int
+    err*: TResultEnum
+    inCurrentBatch*: bool
+    targets*: set[TTarget]
+    matrix*: seq[string]
+    nimout*: string
+    nimoutFull*: bool # whether nimout is all compiler output or a subset
+    parseErrors*: string            # when the spec definition is invalid, this is not empty.
+    unjoinable*: bool
+    unbatchable*: bool
+      # whether this test can be batchable via `NIM_TESTAMENT_BATCH`; only very
+      # few tests are not batchable; the ones that are not could be turned batchable
+      # by making the dependencies explicit
+    useValgrind*: ValgrindSpec
+    timeout*: float # in seconds, fractions possible,
+                      # but don't rely on much precision
+    inlineErrors*: seq[InlineError] # line information to error message
+    debugInfo*: string # debug info to give more context
+
+proc getCmd*(s: TSpec): string =
+  if s.cmd.len == 0:
+    result = compilerPrefix & " $target --hints:on -d:testing --nimblePath:build/deps/pkgs2 $options $file"
+  else:
+    result = s.cmd
+
+const
+  targetToExt*: array[TTarget, string] = ["nim.c", "nim.cpp", "nim.m", "js"]
+  targetToCmd*: array[TTarget, string] = ["c", "cpp", "objc", "js"]
+
+proc defaultOptions*(a: TTarget): string =
+  case a
+  of targetJS: "-d:nodejs"
+    # once we start testing for `nim js -d:nimbrowser` (eg selenium or similar),
+    # we can adapt this logic; or a given js test can override with `-u:nodejs`.
+  else: ""
+
+when not declared(parseCfgBool):
+  # candidate for the stdlib:
+  proc parseCfgBool(s: string): bool =
+    case normalize(s)
+    of "y", "yes", "true", "1", "on": result = true
+    of "n", "no", "false", "0", "off": result = false
+    else: raise newException(ValueError, "cannot interpret as a bool: " & s)
+
+proc addLine*(self: var string; pieces: varargs[string]) =
+  for piece in pieces:
+    self.add piece
+  self.add "\n"
+
+
+const
+  inlineErrorKindMarker = "tt."
+  inlineErrorMarker = "#[" & inlineErrorKindMarker
+
+proc extractErrorMsg(s: string; i: int; line: var int; col: var int; spec: var TSpec): int =
+  ## Extract inline error messages.
+  ##
+  ## Can parse a single message for a line:
+  ##
+  ##   ```nim
+  ##   proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Error
+  ##        ^ 'generic_proc' should be: 'genericProc' [Name] ]#
+  ##   ```
+  ##
+  ## Can parse multiple messages for a line when they are separated by ';':
+  ##
+  ##   ```nim
+  ##   proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Error
+  ##        ^ 'generic_proc' should be: 'genericProc' [Name]; tt.Error
+  ##                           ^ 'no_destroy' should be: 'nodestroy' [Name]; tt.Error
+  ##                                       ^ 'userPragma' should be: 'user_pragma' [template declared in mstyleCheck.nim(10, 9)] [Name] ]#
+  ##   ```
+  ##
+  ##   ```nim
+  ##   proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Error
+  ##        ^ 'generic_proc' should be: 'genericProc' [Name];
+  ##     tt.Error              ^ 'no_destroy' should be: 'nodestroy' [Name];
+  ##     tt.Error                          ^ 'userPragma' should be: 'user_pragma' [template declared in mstyleCheck.nim(10, 9)] [Name] ]#
+  ##   ```
+  result = i + len(inlineErrorMarker)
+  inc col, len(inlineErrorMarker)
+  let msgLine = line
+  var msgCol = -1
+  var msg = ""
+  var kind = ""
+
+  template parseKind =
+    while result < s.len and s[result] in IdentChars:
+      kind.add s[result]
+      inc result
+      inc col
+    if kind notin ["Hint", "Warning", "Error"]:
+      spec.parseErrors.addLine "expected inline message kind: Hint, Warning, Error"
+
+  template skipWhitespace =
+    while result < s.len and s[result] in Whitespace:
+      if s[result] == '\n':
+        col = 1
+        inc line
+      else:
+        inc col
+      inc result
+
+  template parseCaret =
+    if result < s.len and s[result] == '^':
+      msgCol = col
+      inc result
+      inc col
+      skipWhitespace()
+    else:
+      spec.parseErrors.addLine "expected column marker ('^') for inline message"
+
+  template isMsgDelimiter: bool =
+    s[result] == ';' and
+    (block:
+      let nextTokenIdx = result + 1 + parseutils.skipWhitespace(s, result + 1)
+      if s.len > nextTokenIdx + len(inlineErrorKindMarker) and
+         s[nextTokenIdx..(nextTokenIdx + len(inlineErrorKindMarker) - 1)] == inlineErrorKindMarker:
+        true
+      else:
+        false)
+
+  template trimTrailingMsgWhitespace =
+    while msg.len > 0 and msg[^1] in Whitespace:
+      setLen msg, msg.len - 1
+
+  template addInlineError =
+    doAssert msg[^1] notin Whitespace
+    if kind == "Error": spec.action = actionReject
+    spec.inlineErrors.add InlineError(kind: kind, msg: msg, line: msgLine, col: msgCol)
+
+  parseKind()
+  skipWhitespace()
+  parseCaret()
+
+  while result < s.len-1:
+    if s[result] == '\n':
+      if result > 0 and s[result - 1] == '\r':
+        msg[^1] = '\n'
+      else:
+        msg.add '\n'
+      inc result
+      inc line
+      col = 1
+    elif isMsgDelimiter():
+      trimTrailingMsgWhitespace()
+      inc result
+      skipWhitespace()
+      addInlineError()
+      inc result, len(inlineErrorKindMarker)
+      inc col, 1 + len(inlineErrorKindMarker)
+      kind.setLen 0
+      msg.setLen 0
+      parseKind()
+      skipWhitespace()
+      parseCaret()
+    elif s[result] == ']' and s[result+1] == '#':
+      trimTrailingMsgWhitespace()
+      inc result, 2
+      inc col, 2
+      addInlineError()
+      break
+    else:
+      msg.add s[result]
+      inc result
+      inc col
+
+  if spec.inlineErrors.len > 0:
+    spec.unjoinable = true
+
+proc extractSpec(filename: string; spec: var TSpec): string =
+  const
+    tripleQuote = "\"\"\""
+    specStart = "discard " & tripleQuote
+  var s = readFile(filename)
+
+  var i = 0
+  var a = -1
+  var b = -1
+  var line = 1
+  var col = 1
+  while i < s.len:
+    if (i == 0 or s[i-1] != ' ') and s.continuesWith(specStart, i):
+      # `s[i-1] == '\n'` would not work because of `tests/stdlib/tbase64.nim` which contains BOM (https://en.wikipedia.org/wiki/Byte_order_mark)
+      const lineMax = 10
+      if a != -1:
+        raise newException(ValueError, "testament spec violation: duplicate `specStart` found: " & $(filename, a, b, line))
+      elif line > lineMax:
+        # not overly restrictive, but prevents mistaking some `specStart` as spec if deeep inside a test file
+        raise newException(ValueError, "testament spec violation: `specStart` should be before line $1, or be indented; info: $2" % [$lineMax, $(filename, a, b, line)])
+      i += specStart.len
+      a = i
+    elif a > -1 and b == -1 and s.continuesWith(tripleQuote, i):
+      b = i
+      i += tripleQuote.len
+    elif s[i] == '\n':
+      inc line
+      inc i
+      col = 1
+    elif s.continuesWith(inlineErrorMarker, i):
+      i = extractErrorMsg(s, i, line, col, spec)
+    else:
+      inc col
+      inc i
+
+  if a >= 0 and b > a:
+    result = s.substr(a, b-1).multiReplace({"'''": tripleQuote, "\\31": "\31"})
+  elif a >= 0:
+    raise newException(ValueError, "testament spec violation: `specStart` found but not trailing `tripleQuote`: $1" % $(filename, a, b, line))
+  else:
+    result = ""
+
+proc parseTargets*(value: string): set[TTarget] =
+  for v in value.normalize.splitWhitespace:
+    case v
+    of "c": result.incl(targetC)
+    of "cpp", "c++": result.incl(targetCpp)
+    of "objc": result.incl(targetObjC)
+    of "js": result.incl(targetJS)
+    else: raise newException(ValueError, "invalid target: '$#'" % v)
+
+proc initSpec*(filename: string): TSpec =
+  result.file = filename
+
+proc isCurrentBatch*(testamentData: TestamentData; filename: string): bool =
+  if testamentData.testamentNumBatch != 0:
+    hash(filename) mod testamentData.testamentNumBatch == testamentData.testamentBatch
+  else:
+    true
+
+proc parseSpec*(filename: string): TSpec =
+  result.file = filename
+  result.filename = extractFilename(filename)
+  let specStr = extractSpec(filename, result)
+  var ss = newStringStream(specStr)
+  var p: CfgParser
+  open(p, ss, filename, 1)
+  var flags: HashSet[string]
+  var nimoutFound = false
+  while true:
+    var e = next(p)
+    case e.kind
+    of cfgKeyValuePair:
+      let key = e.key.normalize
+      const whiteListMulti = ["disabled", "ccodecheck"]
+        ## list of flags that are correctly handled when passed multiple times
+        ## (instead of being overwritten)
+      if key notin whiteListMulti:
+        doAssert key notin flags, $(key, filename)
+      flags.incl key
+      case key
+      of "action":
+        case e.value.normalize
+        of "compile":
+          result.action = actionCompile
+        of "run":
+          result.action = actionRun
+        of "reject":
+          result.action = actionReject
+        else:
+          result.parseErrors.addLine "cannot interpret as action: ", e.value
+      of "file":
+        if result.msg.len == 0 and result.nimout.len == 0:
+          result.parseErrors.addLine "errormsg or msg needs to be specified before file"
+        result.file = e.value
+      of "line":
+        if result.msg.len == 0 and result.nimout.len == 0:
+          result.parseErrors.addLine "errormsg, msg or nimout needs to be specified before line"
+        discard parseInt(e.value, result.line)
+      of "column":
+        if result.msg.len == 0 and result.nimout.len == 0:
+          result.parseErrors.addLine "errormsg or msg needs to be specified before column"
+        discard parseInt(e.value, result.column)
+      of "output":
+        if result.outputCheck != ocSubstr:
+          result.outputCheck = ocEqual
+        result.output = e.value
+      of "input":
+        result.input = e.value
+      of "outputsub":
+        result.outputCheck = ocSubstr
+        result.output = strip(e.value)
+      of "sortoutput":
+        try:
+          result.sortoutput = parseCfgBool(e.value)
+        except:
+          result.parseErrors.addLine getCurrentExceptionMsg()
+      of "exitcode":
+        discard parseInt(e.value, result.exitCode)
+        result.action = actionRun
+      of "errormsg":
+        result.msg = e.value
+        result.action = actionReject
+      of "nimout":
+        result.nimout = e.value
+        nimoutFound = true
+      of "nimoutfull":
+        result.nimoutFull = parseCfgBool(e.value)
+      of "batchable":
+        result.unbatchable = not parseCfgBool(e.value)
+      of "joinable":
+        result.unjoinable = not parseCfgBool(e.value)
+      of "valgrind":
+        when defined(linux) and sizeof(int) == 8:
+          result.useValgrind = if e.value.normalize == "leaks": leaking
+                               else: ValgrindSpec(parseCfgBool(e.value))
+          result.unjoinable = true
+          if result.useValgrind != disabled:
+            result.outputCheck = ocSubstr
+        else:
+          # Windows lacks valgrind. Silly OS.
+          # Valgrind only supports OSX <= 17.x
+          result.useValgrind = disabled
+      of "disabled":
+        let value = e.value.normalize
+        case value
+        of "y", "yes", "true", "1", "on": result.err = reDisabled
+        of "n", "no", "false", "0", "off": discard
+        # These values are defined in `compiler/options.isDefined`
+        of "win":
+          when defined(windows): result.err = reDisabled
+        of "linux":
+          when defined(linux): result.err = reDisabled
+        of "bsd":
+          when defined(bsd): result.err = reDisabled
+        of "osx":
+          when defined(osx): result.err = reDisabled
+        of "unix", "posix":
+          when defined(posix): result.err = reDisabled
+        of "freebsd":
+          when defined(freebsd): result.err = reDisabled
+        of "littleendian":
+          when defined(littleendian): result.err = reDisabled
+        of "bigendian":
+          when defined(bigendian): result.err = reDisabled
+        of "cpu8", "8bit":
+          when defined(cpu8): result.err = reDisabled
+        of "cpu16", "16bit":
+          when defined(cpu16): result.err = reDisabled
+        of "cpu32", "32bit":
+          when defined(cpu32): result.err = reDisabled
+        of "cpu64", "64bit":
+          when defined(cpu64): result.err = reDisabled
+        # These values are for CI environments
+        of "travis": # deprecated
+          if isTravis: result.err = reDisabled
+        of "appveyor": # deprecated
+          if isAppVeyor: result.err = reDisabled
+        of "azure":
+          if isAzure: result.err = reDisabled
+        else:
+          # Check whether the value exists as an OS or CPU that is
+          # defined in `compiler/platform`.
+          block checkHost:
+            for os in platform.OS:
+              # Check if the value exists as OS.
+              if value == os.name.normalize:
+                # The value exists; is it the same as the current host?
+                if value == hostOS.normalize:
+                  # The value exists and is the same as the current host,
+                  # so disable the test.
+                  result.err = reDisabled
+                # The value was defined, so there is no need to check further
+                # values or raise an error.
+                break checkHost
+            for cpu in platform.CPU:
+              # Check if the value exists as CPU.
+              if value == cpu.name.normalize:
+                # The value exists; is it the same as the current host?
+                if value == hostCPU.normalize:
+                  # The value exists and is the same as the current host,
+                  # so disable the test.
+                  result.err = reDisabled
+                # The value was defined, so there is no need to check further
+                # values or raise an error.
+                break checkHost
+            # The value doesn't exist as an OS, CPU, or any previous value
+            # defined in this case statement, so raise an error.
+            result.parseErrors.addLine "cannot interpret as a bool: ", e.value
+      of "cmd":
+        if e.value.startsWith("nim "):
+          result.cmd = compilerPrefix & e.value[3..^1]
+        else:
+          result.cmd = e.value
+      of "ccodecheck":
+        result.ccodeCheck.add e.value
+      of "maxcodesize":
+        discard parseInt(e.value, result.maxCodeSize)
+      of "timeout":
+        try:
+          result.timeout = parseFloat(e.value)
+        except ValueError:
+          result.parseErrors.addLine "cannot interpret as a float: ", e.value
+      of "targets", "target":
+        try:
+          result.targets.incl parseTargets(e.value)
+        except ValueError as e:
+          result.parseErrors.addLine e.msg
+      of "matrix":
+        for v in e.value.split(';'):
+          result.matrix.add(v.strip)
+      else:
+        result.parseErrors.addLine "invalid key for test spec: ", e.key
+
+    of cfgSectionStart:
+      result.parseErrors.addLine "section ignored: ", e.section
+    of cfgOption:
+      result.parseErrors.addLine "command ignored: ", e.key & ": " & e.value
+    of cfgError:
+      result.parseErrors.addLine e.msg
+    of cfgEof:
+      break
+  close(p)
+
+  if skips.anyIt(it in result.file):
+    result.err = reDisabled
+
+  if nimoutFound and result.nimout.len == 0 and not result.nimoutFull:
+    result.parseErrors.addLine "empty `nimout` is vacuously true, use `nimoutFull:true` if intentional"
+
+  result.inCurrentBatch = isCurrentBatch(testamentData0, filename) or result.unbatchable
+  if not result.inCurrentBatch:
+    result.err = reDisabled
+
+  # Interpolate variables in msgs:
+  template varSub(msg: string): string =
+    try:
+      msg % ["/", $DirSep, "file", result.filename]
+    except ValueError:
+      result.parseErrors.addLine "invalid variable interpolation (see 'https://nim-lang.github.io/Nim/testament.html#writing-unit-tests-output-message-variable-interpolation')"
+      msg
+  result.nimout = result.nimout.varSub
+  result.msg = result.msg.varSub
+  for inlineError in result.inlineErrors.mitems:
+    inlineError.msg = inlineError.msg.varSub
diff --git a/testament/testament.nim b/testament/testament.nim
new file mode 100644
index 000000000..1e892e636
--- /dev/null
+++ b/testament/testament.nim
@@ -0,0 +1,806 @@
+#
+#
+#            Nim Testament
+#        (c) Copyright 2017 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This program verifies Nim against the testcases.
+
+import
+  std/[strutils, pegs, os, osproc, streams, json,
+    parseopt, browsers, terminal, exitprocs,
+    algorithm, times, intsets, macros]
+
+import backend, specs, azure, htmlgen
+
+from std/sugar import dup
+import compiler/nodejs
+import lib/stdtest/testutils
+from lib/stdtest/specialpaths import splitTestFile
+from std/private/gitutils import diffStrings
+
+import ../dist/checksums/src/checksums/md5
+
+proc trimUnitSep(x: var string) =
+  let L = x.len
+  if L > 0 and x[^1] == '\31':
+    setLen x, L-1
+
+var useColors = true
+var backendLogging = true
+var simulate = false
+var optVerbose = false
+var useMegatest = true
+var valgrindEnabled = true
+
+proc verboseCmd(cmd: string) =
+  if optVerbose:
+    echo "executing: ", cmd
+
+const
+  failString* = "FAIL: " # ensures all failures can be searched with 1 keyword in CI logs
+  testsDir = "tests" & DirSep
+  resultsFile = "testresults.html"
+  Usage = """Usage:
+  testament [options] command [arguments]
+
+Command:
+  p|pat|pattern <glob>        run all the tests matching the given pattern
+  all                         run all tests in category folders
+  c|cat|category <category>   run all the tests of a certain category
+  r|run <test>                run single test file
+  html                        generate $1 from the database
+Arguments:
+  arguments are passed to the compiler
+Options:
+  --print                   print results to the console
+  --verbose                 print commands (compiling and running tests)
+  --simulate                see what tests would be run but don't run them (for debugging)
+  --failing                 only show failing/ignored tests
+  --targets:"c cpp js objc" run tests for specified targets (default: c)
+  --nim:path                use a particular nim executable (default: $$PATH/nim)
+  --directory:dir           Change to directory dir before reading the tests or doing anything else.
+  --colors:on|off           Turn messages coloring on|off.
+  --backendLogging:on|off   Disable or enable backend logging. By default turned on.
+  --megatest:on|off         Enable or disable megatest. Default is on.
+  --valgrind:on|off         Enable or disable valgrind support. Default is on.
+  --skipFrom:file           Read tests to skip from `file` - one test per line, # comments ignored
+
+On Azure Pipelines, testament will also publish test results via Azure Pipelines' Test Management API
+provided that System.AccessToken is made available via the environment variable SYSTEM_ACCESSTOKEN.
+
+Experimental: using environment variable `NIM_TESTAMENT_REMOTE_NETWORKING=1` enables
+tests with remote networking (as in CI).
+""" % resultsFile
+
+proc isNimRepoTests(): bool =
+  # this logic could either be specific to cwd, or to some file derived from
+  # the input file, eg testament r /pathto/tests/foo/tmain.nim; we choose
+  # the former since it's simpler and also works with `testament all`.
+  let file = "testament"/"testament.nim.cfg"
+  result = file.fileExists
+
+type
+  Category = distinct string
+  TResults = object
+    total, passed, failedButAllowed, skipped: int
+      ## xxx rename passed to passedOrAllowedFailure
+    data: string
+  TTest = object
+    name: string
+    cat: Category
+    options: string
+    testArgs: seq[string]
+    spec: TSpec
+    startTime: float
+    debugInfo: string
+
+# ----------------------------------------------------------------------------
+
+let
+  pegLineError =
+    peg"{[^(]*} '(' {\d+} ', ' {\d+} ') ' ('Error') ':' \s* {.*}"
+  pegOtherError = peg"'Error:' \s* {.*}"
+  pegOfInterest = pegLineError / pegOtherError
+
+var gTargets = {low(TTarget)..high(TTarget)}
+var targetsSet = false
+
+proc isSuccess(input: string): bool =
+  # not clear how to do the equivalent of pkg/regex's: re"FOO(.*?)BAR" in pegs
+  # note: this doesn't handle colors, eg: `\e[1m\e[0m\e[32mHint:`; while we
+  # could handle colors, there would be other issues such as handling other flags
+  # that may appear in user config (eg: `--filenames`).
+  # Passing `XDG_CONFIG_HOME= testament args...` can be used to ignore user config
+  # stored in XDG_CONFIG_HOME, refs https://wiki.archlinux.org/index.php/XDG_Base_Directory
+  input.startsWith("Hint: ") and input.endsWith("[SuccessX]")
+
+proc getFileDir(filename: string): string =
+  result = filename.splitFile().dir
+  if not result.isAbsolute():
+    result = getCurrentDir() / result
+
+proc execCmdEx2(command: string, args: openArray[string]; workingDir, input: string = ""): tuple[
+                cmdLine: string,
+                output: string,
+                exitCode: int] {.tags:
+                [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} =
+
+  result.cmdLine.add quoteShell(command)
+  for arg in args:
+    result.cmdLine.add ' '
+    result.cmdLine.add quoteShell(arg)
+  verboseCmd(result.cmdLine)
+  var p = startProcess(command, workingDir = workingDir, args = args,
+                       options = {poStdErrToStdOut, poUsePath})
+  var outp = outputStream(p)
+
+  # There is no way to provide input for the child process
+  # anymore. Closing it will create EOF on stdin instead of eternal
+  # blocking.
+  let instream = inputStream(p)
+  instream.write(input)
+  close instream
+
+  result.exitCode = -1
+  var line = newStringOfCap(120)
+  while true:
+    if outp.readLine(line):
+      result.output.add line
+      result.output.add '\n'
+    else:
+      result.exitCode = peekExitCode(p)
+      if result.exitCode != -1: break
+  close(p)
+
+proc nimcacheDir(filename, options: string, target: TTarget): string =
+  ## Give each test a private nimcache dir so they don't clobber each other's.
+  let hashInput = options & $target
+  result = "nimcache" / (filename & '_' & hashInput.getMD5)
+
+proc prepareTestCmd(cmdTemplate, filename, options, nimcache: string,
+                     target: TTarget, extraOptions = ""): string =
+  var options = target.defaultOptions & ' ' & options
+  if nimcache.len > 0: options.add(" --nimCache:$#" % nimcache.quoteShell)
+  options.add ' ' & extraOptions
+  # we avoid using `parseCmdLine` which is buggy, refs bug #14343
+  result = cmdTemplate % ["target", targetToCmd[target],
+                      "options", options, "file", filename.quoteShell,
+                      "filedir", filename.getFileDir(), "nim", compilerPrefix]
+
+proc callNimCompiler(cmdTemplate, filename, options, nimcache: string,
+                     target: TTarget, extraOptions = ""): TSpec =
+  result.cmd = prepareTestCmd(cmdTemplate, filename, options, nimcache, target,
+                          extraOptions)
+  verboseCmd(result.cmd)
+  var p = startProcess(command = result.cmd,
+                       options = {poStdErrToStdOut, poUsePath, poEvalCommand})
+  let outp = p.outputStream
+  var foundSuccessMsg = false
+  var foundErrorMsg = false
+  var err = ""
+  var x = newStringOfCap(120)
+  result.nimout = ""
+  while true:
+    if outp.readLine(x):
+      trimUnitSep x
+      result.nimout.add(x & '\n')
+      if x =~ pegOfInterest:
+        # `err` should contain the last error message
+        err = x
+        foundErrorMsg = true
+      elif x.isSuccess:
+        foundSuccessMsg = true
+    elif not running(p):
+      break
+  result.msg = ""
+  result.file = ""
+  result.output = ""
+  result.line = 0
+  result.column = 0
+
+  result.err = reNimcCrash
+  result.exitCode = p.peekExitCode
+  close p
+  case result.exitCode
+  of 0:
+    if foundErrorMsg:
+      result.debugInfo.add " compiler exit code was 0 but some Error's were found."
+    else:
+      result.err = reSuccess
+  of 1:
+    if not foundErrorMsg:
+      result.debugInfo.add " compiler exit code was 1 but no Error's were found."
+    if foundSuccessMsg:
+      result.debugInfo.add " compiler exit code was 1 but no `isSuccess` was true."
+  else:
+    result.debugInfo.add " expected compiler exit code 0 or 1, got $1." % $result.exitCode
+
+  if err =~ pegLineError:
+    result.file = extractFilename(matches[0])
+    result.line = parseInt(matches[1])
+    result.column = parseInt(matches[2])
+    result.msg = matches[3]
+  elif err =~ pegOtherError:
+    result.msg = matches[0]
+  trimUnitSep result.msg
+
+proc initResults: TResults =
+  result.total = 0
+  result.passed = 0
+  result.failedButAllowed = 0
+  result.skipped = 0
+  result.data = ""
+
+macro ignoreStyleEcho(args: varargs[typed]): untyped =
+  let typForegroundColor = bindSym"ForegroundColor".getType
+  let typBackgroundColor = bindSym"BackgroundColor".getType
+  let typStyle = bindSym"Style".getType
+  let typTerminalCmd = bindSym"TerminalCmd".getType
+  result = newCall(bindSym"echo")
+  for arg in children(args):
+    if arg.kind == nnkNilLit: continue
+    let typ = arg.getType
+    if typ.kind != nnkEnumTy or
+       typ != typForegroundColor and
+       typ != typBackgroundColor and
+       typ != typStyle and
+       typ != typTerminalCmd:
+      result.add(arg)
+
+template maybeStyledEcho(args: varargs[untyped]): untyped =
+  if useColors:
+    styledEcho(args)
+  else:
+    ignoreStyleEcho(args)
+
+
+proc `$`(x: TResults): string =
+  result = """
+Tests passed or allowed to fail: $2 / $1 <br />
+Tests failed and allowed to fail: $3 / $1 <br />
+Tests skipped: $4 / $1 <br />
+""" % [$x.total, $x.passed, $x.failedButAllowed, $x.skipped]
+
+proc testName(test: TTest, target: TTarget, extraOptions: string, allowFailure: bool): string =
+  var name = test.name.replace(DirSep, '/')
+  name.add ' ' & $target
+  if allowFailure:
+    name.add " (allowed to fail) "
+  if test.options.len > 0: name.add ' ' & test.options
+  if extraOptions.len > 0: name.add ' ' & extraOptions
+  name.strip()
+
+proc addResult(r: var TResults, test: TTest, target: TTarget,
+               extraOptions, expected, given: string, successOrig: TResultEnum,
+               allowFailure = false, givenSpec: ptr TSpec = nil) =
+  # instead of `ptr TSpec` we could also use `Option[TSpec]`; passing `givenSpec` makes it easier to get what we need
+  # instead of having to pass individual fields, or abusing existing ones like expected vs given.
+  # test.name is easier to find than test.name.extractFilename
+  # A bit hacky but simple and works with tests/testament/tshould_not_work.nim
+  let name = testName(test, target, extraOptions, allowFailure)
+  let duration = epochTime() - test.startTime
+  let success = if test.spec.timeout > 0.0 and duration > test.spec.timeout: reTimeout
+                else: successOrig
+
+  let durationStr = duration.formatFloat(ffDecimal, precision = 2).align(5)
+  if backendLogging:
+    backend.writeTestResult(name = name,
+                            category = test.cat.string,
+                            target = $target,
+                            action = $test.spec.action,
+                            result = $success,
+                            expected = expected,
+                            given = given)
+  r.data.addf("$#\t$#\t$#\t$#", name, expected, given, $success)
+  template dispNonSkipped(color, outcome) =
+    maybeStyledEcho color, outcome, fgCyan, test.debugInfo, alignLeft(name, 60), fgBlue, " (", durationStr, " sec)"
+  template disp(msg) =
+    maybeStyledEcho styleDim, fgYellow, msg & ' ', styleBright, fgCyan, name
+  if success == reSuccess:
+    dispNonSkipped(fgGreen, "PASS: ")
+  elif success == reDisabled:
+    if test.spec.inCurrentBatch: disp("SKIP:")
+    else: disp("NOTINBATCH:")
+  elif success == reJoined: disp("JOINED:")
+  else:
+    dispNonSkipped(fgRed, failString)
+    maybeStyledEcho styleBright, fgCyan, "Test \"", test.name, "\"", " in category \"", test.cat.string, "\""
+    maybeStyledEcho styleBright, fgRed, "Failure: ", $success
+    if givenSpec != nil and givenSpec.debugInfo.len > 0:
+      echo "debugInfo: " & givenSpec.debugInfo
+    if success in {reBuildFailed, reNimcCrash, reInstallFailed}:
+      # expected is empty, no reason to print it.
+      echo given
+    else:
+      maybeStyledEcho fgYellow, "Expected:"
+      maybeStyledEcho styleBright, expected, "\n"
+      maybeStyledEcho fgYellow, "Gotten:"
+      maybeStyledEcho styleBright, given, "\n"
+      echo diffStrings(expected, given).output
+
+  if backendLogging and (isAppVeyor or isAzure):
+    let (outcome, msg) =
+      case success
+      of reSuccess:
+        ("Passed", "")
+      of reDisabled, reJoined:
+        ("Skipped", "")
+      of reBuildFailed, reNimcCrash, reInstallFailed:
+        ("Failed", "Failure: " & $success & '\n' & given)
+      else:
+        ("Failed", "Failure: " & $success & "\nExpected:\n" & expected & "\n\n" & "Gotten:\n" & given)
+    if isAzure:
+      azure.addTestResult(name, test.cat.string, int(duration * 1000), msg, success)
+    else:
+      var p = startProcess("appveyor", args = ["AddTest", test.name.replace("\\", "/") & test.options,
+                           "-Framework", "nim-testament", "-FileName",
+                           test.cat.string,
+                           "-Outcome", outcome, "-ErrorMessage", msg,
+                           "-Duration", $(duration * 1000).int],
+                           options = {poStdErrToStdOut, poUsePath, poParentStreams})
+      discard waitForExit(p)
+      close(p)
+
+proc toString(inlineError: InlineError, filename: string): string =
+  result.add "$file($line, $col) $kind: $msg" % [
+    "file", filename,
+    "line", $inlineError.line,
+    "col", $inlineError.col,
+    "kind", $inlineError.kind,
+    "msg", $inlineError.msg
+  ]
+
+proc inlineErrorsMsgs(expected: TSpec): string =
+  for inlineError in expected.inlineErrors.items:
+    result.addLine inlineError.toString(expected.filename)
+
+proc checkForInlineErrors(expected, given: TSpec): bool =
+  for inlineError in expected.inlineErrors:
+    if inlineError.toString(expected.filename) notin given.nimout:
+      return false
+  true
+
+proc nimoutCheck(expected, given: TSpec): bool =
+  result = true
+  if expected.nimoutFull:
+    if expected.nimout != given.nimout:
+      result = false
+  elif expected.nimout.len > 0 and not greedyOrderedSubsetLines(expected.nimout, given.nimout):
+    result = false
+
+proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest,
+             target: TTarget, extraOptions: string) =
+  if not checkForInlineErrors(expected, given) or
+    (not expected.nimoutFull and not nimoutCheck(expected, given)):
+      r.addResult(test, target, extraOptions, expected.nimout & inlineErrorsMsgs(expected), given.nimout, reMsgsDiffer)
+  elif strip(expected.msg) notin strip(given.msg):
+    r.addResult(test, target, extraOptions, expected.msg, given.msg, reMsgsDiffer)
+  elif not nimoutCheck(expected, given):
+    r.addResult(test, target, extraOptions, expected.nimout, given.nimout, reMsgsDiffer)
+  elif extractFilename(expected.file) != extractFilename(given.file) and
+      "internal error:" notin expected.msg:
+    r.addResult(test, target, extraOptions, expected.file, given.file, reFilesDiffer)
+  elif expected.line != given.line and expected.line != 0 or
+       expected.column != given.column and expected.column != 0:
+    r.addResult(test, target, extraOptions, $expected.line & ':' & $expected.column,
+                      $given.line & ':' & $given.column, reLinesDiffer)
+  else:
+    r.addResult(test, target, extraOptions, expected.msg, given.msg, reSuccess)
+    inc(r.passed)
+
+proc generatedFile(test: TTest, target: TTarget): string =
+  if target == targetJS:
+    result = test.name.changeFileExt("js")
+  else:
+    let (_, name, _) = test.name.splitFile
+    let ext = targetToExt[target]
+    result = nimcacheDir(test.name, test.options, target) / "@m" & name.changeFileExt(ext)
+
+proc needsCodegenCheck(spec: TSpec): bool =
+  result = spec.maxCodeSize > 0 or spec.ccodeCheck.len > 0
+
+proc codegenCheck(test: TTest, target: TTarget, spec: TSpec, expectedMsg: var string,
+                  given: var TSpec) =
+  try:
+    let genFile = generatedFile(test, target)
+    let contents = readFile(genFile)
+    for check in spec.ccodeCheck:
+      if check.len > 0 and check[0] == '\\':
+        # little hack to get 'match' support:
+        if not contents.match(check.peg):
+          given.err = reCodegenFailure
+      elif contents.find(check.peg) < 0:
+        given.err = reCodegenFailure
+      expectedMsg = check
+    if spec.maxCodeSize > 0 and contents.len > spec.maxCodeSize:
+      given.err = reCodegenFailure
+      given.msg = "generated code size: " & $contents.len
+      expectedMsg = "max allowed size: " & $spec.maxCodeSize
+  except ValueError:
+    given.err = reInvalidPeg
+    echo getCurrentExceptionMsg()
+  except IOError:
+    given.err = reCodeNotFound
+    echo getCurrentExceptionMsg()
+
+proc compilerOutputTests(test: TTest, target: TTarget, extraOptions: string,
+                         given: var TSpec, expected: TSpec; r: var TResults) =
+  var expectedmsg: string = ""
+  var givenmsg: string = ""
+  if given.err == reSuccess:
+    if expected.needsCodegenCheck:
+      codegenCheck(test, target, expected, expectedmsg, given)
+      givenmsg = given.msg
+    if not nimoutCheck(expected, given) or
+       not checkForInlineErrors(expected, given):
+      given.err = reMsgsDiffer
+      expectedmsg = expected.nimout & inlineErrorsMsgs(expected)
+      givenmsg = given.nimout.strip
+  else:
+    givenmsg = "$ " & given.cmd & '\n' & given.nimout
+  if given.err == reSuccess: inc(r.passed)
+  r.addResult(test, target, extraOptions, expectedmsg, givenmsg, given.err)
+
+proc getTestSpecTarget(): TTarget =
+  if getEnv("NIM_COMPILE_TO_CPP", "false") == "true":
+    result = targetCpp
+  else:
+    result = targetC
+
+var count = 0
+
+proc equalModuloLastNewline(a, b: string): bool =
+  # allow lazy output spec that omits last newline, but really those should be fixed instead
+  result = a == b or b.endsWith("\n") and a == b[0 ..< ^1]
+
+proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec,
+                    target: TTarget, extraOptions: string, nimcache: string) =
+  test.startTime = epochTime()
+  if testName(test, target, extraOptions, false) in skips:
+    test.spec.err = reDisabled
+
+  if test.spec.err in {reDisabled, reJoined}:
+    r.addResult(test, target, extraOptions, "", "", test.spec.err)
+    inc(r.skipped)
+    return
+  var given = callNimCompiler(expected.getCmd, test.name, test.options, nimcache, target, extraOptions)
+  case expected.action
+  of actionCompile:
+    compilerOutputTests(test, target, extraOptions, given, expected, r)
+  of actionRun:
+    if given.err != reSuccess:
+      r.addResult(test, target, extraOptions, "", "$ " & given.cmd & '\n' & given.nimout, given.err, givenSpec = given.addr)
+    else:
+      let isJsTarget = target == targetJS
+      var exeFile = changeFileExt(test.name, if isJsTarget: "js" else: ExeExt)
+      if not fileExists(exeFile):
+        r.addResult(test, target, extraOptions, expected.output,
+                    "executable not found: " & exeFile, reExeNotFound)
+      else:
+        let nodejs = if isJsTarget: findNodeJs() else: ""
+        if isJsTarget and nodejs == "":
+          r.addResult(test, target, extraOptions, expected.output, "nodejs binary not in PATH",
+                      reExeNotFound)
+        else:
+          var exeCmd: string
+          var args = test.testArgs
+          if isJsTarget:
+            exeCmd = nodejs
+            # see D20210217T215950
+            args = @["--unhandled-rejections=strict", exeFile] & args
+          else:
+            exeCmd = exeFile.dup(normalizeExe)
+            if valgrindEnabled and expected.useValgrind != disabled:
+              var valgrindOptions = @["--error-exitcode=1"]
+              if expected.useValgrind != leaking:
+                valgrindOptions.add "--leak-check=yes"
+              args = valgrindOptions & exeCmd & args
+              exeCmd = "valgrind"
+          var (_, buf, exitCode) = execCmdEx2(exeCmd, args, input = expected.input)
+          # Treat all failure codes from nodejs as 1. Older versions of nodejs used
+          # to return other codes, but for us it is sufficient to know that it's not 0.
+          if exitCode != 0: exitCode = 1
+          let bufB =
+            if expected.sortoutput:
+              var buf2 = buf
+              buf2.stripLineEnd
+              var x = splitLines(buf2)
+              sort(x, system.cmp)
+              join(x, "\n") & '\n'
+            else:
+              buf
+          if exitCode != expected.exitCode:
+            given.err = reExitcodesDiffer
+            r.addResult(test, target, extraOptions, "exitcode: " & $expected.exitCode,
+                              "exitcode: " & $exitCode & "\n\nOutput:\n" &
+                              bufB, reExitcodesDiffer)
+          elif (expected.outputCheck == ocEqual and not expected.output.equalModuloLastNewline(bufB)) or
+              (expected.outputCheck == ocSubstr and expected.output notin bufB):
+            given.err = reOutputsDiffer
+            r.addResult(test, target, extraOptions, expected.output, bufB, reOutputsDiffer)
+          compilerOutputTests(test, target, extraOptions, given, expected, r)
+  of actionReject:
+    # Make sure its the compiler rejecting and not the system (e.g. segfault)
+    cmpMsgs(r, expected, given, test, target, extraOptions)
+    if given.exitCode != QuitFailure:
+      r.addResult(test, target, extraOptions, "exitcode: " & $QuitFailure,
+                        "exitcode: " & $given.exitCode & "\n\nOutput:\n" &
+                        given.nimout, reExitcodesDiffer)
+
+
+
+proc changeTarget(extraOptions: string; defaultTarget: TTarget): TTarget =
+  result = defaultTarget
+  var p = parseopt.initOptParser(extraOptions)
+
+  while true:
+    parseopt.next(p)
+    case p.kind
+    of cmdEnd: break
+    of cmdLongOption, cmdShortOption:
+      if p.key == "b" or p.key == "backend":
+        result = parseEnum[TTarget](p.val.normalize)
+        # chooses the last one
+    else:
+      discard
+
+proc targetHelper(r: var TResults, test: TTest, expected: TSpec, extraOptions: string) =
+  for target in expected.targets:
+    inc(r.total)
+    if target notin gTargets:
+      r.addResult(test, target, extraOptions, "", "", reDisabled)
+      inc(r.skipped)
+    elif simulate:
+      inc count
+      echo "testSpec count: ", count, " expected: ", expected
+    else:
+      let nimcache = nimcacheDir(test.name, test.options, target)
+      var testClone = test
+      let target = changeTarget(extraOptions, target)
+      testSpecHelper(r, testClone, expected, target, extraOptions, nimcache)
+
+proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) =
+  var expected = test.spec
+  if expected.parseErrors.len > 0:
+    # targetC is a lie, but a parameter is required
+    r.addResult(test, targetC, "", "", expected.parseErrors, reInvalidSpec)
+    inc(r.total)
+    return
+
+  expected.targets.incl targets
+  # still no target specified at all
+  if expected.targets == {}:
+    expected.targets = {getTestSpecTarget()}
+  if test.spec.matrix.len > 0:
+    for m in test.spec.matrix:
+      targetHelper(r, test, expected, m)
+  else:
+    targetHelper(r, test, expected, "")
+
+proc testSpecWithNimcache(r: var TResults, test: TTest; nimcache: string) {.used.} =
+  for target in test.spec.targets:
+    inc(r.total)
+    var testClone = test
+    testSpecHelper(r, testClone, test.spec, target, "", nimcache)
+
+proc makeTest(test, options: string, cat: Category): TTest =
+  result.cat = cat
+  result.name = test
+  result.options = options
+  result.spec = parseSpec(addFileExt(test, ".nim"))
+  result.startTime = epochTime()
+
+proc makeRawTest(test, options: string, cat: Category): TTest {.used.} =
+  result.cat = cat
+  result.name = test
+  result.options = options
+  result.spec = initSpec(addFileExt(test, ".nim"))
+  result.spec.action = actionCompile
+  result.spec.targets = {getTestSpecTarget()}
+  result.startTime = epochTime()
+
+# TODO: fix these files
+const disabledFilesDefault = @[
+  "tableimpl.nim",
+  "setimpl.nim",
+  "hashcommon.nim",
+
+  # Requires compiling with '--threads:on`
+  "sharedlist.nim",
+  "sharedtables.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 = disabledFilesDefault & @["coro.nim"]
+else:
+  const
+    # array of modules disabled from compilation test of stdlib.
+    disabledFiles = disabledFilesDefault
+
+include categories
+
+proc loadSkipFrom(name: string): seq[string] =
+  if name.len == 0: return
+  # One skip per line, comments start with #
+  # used by `nlvm` (at least)
+  for line in lines(name):
+    let sline = line.strip()
+    if sline.len > 0 and not sline.startsWith('#'):
+      result.add sline
+
+proc main() =
+  azure.init()
+  backend.open()
+  var optPrintResults = false
+  var optFailing = false
+  var targetsStr = ""
+  var isMainProcess = true
+  var skipFrom = ""
+
+  var p = initOptParser()
+  p.next()
+  while p.kind in {cmdLongOption, cmdShortOption}:
+    case p.key.normalize
+    of "print": optPrintResults = true
+    of "verbose": optVerbose = true
+    of "failing": optFailing = true
+    of "pedantic": discard # deadcode refs https://github.com/nim-lang/Nim/issues/16731
+    of "targets":
+      targetsStr = p.val
+      gTargets = parseTargets(targetsStr)
+      targetsSet = true
+    of "nim":
+      compilerPrefix = addFileExt(p.val.absolutePath, ExeExt)
+    of "directory":
+      setCurrentDir(p.val)
+    of "colors":
+      case p.val:
+      of "on":
+        useColors = true
+      of "off":
+        useColors = false
+      else:
+        quit Usage
+    of "batch":
+      testamentData0.batchArg = p.val
+      if p.val != "_" and p.val.len > 0 and p.val[0] in {'0'..'9'}:
+        let s = p.val.split("_")
+        doAssert s.len == 2, $(p.val, s)
+        testamentData0.testamentBatch = s[0].parseInt
+        testamentData0.testamentNumBatch = s[1].parseInt
+        doAssert testamentData0.testamentNumBatch > 0
+        doAssert testamentData0.testamentBatch >= 0 and testamentData0.testamentBatch < testamentData0.testamentNumBatch
+    of "simulate":
+      simulate = true
+    of "megatest":
+      case p.val:
+      of "on":
+        useMegatest = true
+      of "off":
+        useMegatest = false
+      else:
+        quit Usage
+    of "valgrind":
+      case p.val:
+      of "on":
+        valgrindEnabled = true
+      of "off":
+        valgrindEnabled = false
+      else:
+        quit Usage
+    of "backendlogging":
+      case p.val:
+      of "on":
+        backendLogging = true
+      of "off":
+        backendLogging = false
+      else:
+        quit Usage
+    of "skipfrom":
+      skipFrom = p.val
+    else:
+      quit Usage
+    p.next()
+  if p.kind != cmdArgument:
+    quit Usage
+  var action = p.key.normalize
+  p.next()
+  var r = initResults()
+  case action
+  of "all":
+    #processCategory(r, Category"megatest", p.cmdLineRest, testsDir, runJoinableTests = false)
+
+    var myself = quoteShell(getAppFilename())
+    if targetsStr.len > 0:
+      myself &= " " & quoteShell("--targets:" & targetsStr)
+
+    myself &= " " & quoteShell("--nim:" & compilerPrefix)
+    if testamentData0.batchArg.len > 0:
+      myself &= " --batch:" & testamentData0.batchArg
+
+    if skipFrom.len > 0:
+      myself &= " " & quoteShell("--skipFrom:" & skipFrom)
+
+    var cats: seq[string]
+    let rest = if p.cmdLineRest.len > 0: " " & p.cmdLineRest 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"]:
+        cats.add cat
+    if isNimRepoTests():
+      cats.add AdditionalCategories
+    if useMegatest: cats.add MegaTestCat
+
+    var cmds: seq[string]
+    for cat in cats:
+      let runtype = if useMegatest: " pcat " else: " cat "
+      cmds.add(myself & runtype & quoteShell(cat) & rest)
+
+    proc progressStatus(idx: int) =
+      echo "progress[all]: $1/$2 starting: cat: $3" % [$idx, $cats.len, cats[idx]]
+
+    if simulate:
+      skips = loadSkipFrom(skipFrom)
+      for i, cati in cats:
+        progressStatus(i)
+        processCategory(r, Category(cati), p.cmdLineRest, testsDir, runJoinableTests = false)
+    else:
+      addExitProc azure.finalize
+      quit osproc.execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams}, beforeRunEvent = progressStatus)
+  of "c", "cat", "category":
+    skips = loadSkipFrom(skipFrom)
+    var cat = Category(p.key)
+    processCategory(r, cat, p.cmdLineRest, testsDir, runJoinableTests = true)
+  of "pcat":
+    skips = loadSkipFrom(skipFrom)
+    # 'pcat' is used for running a category in parallel. Currently the only
+    # difference is that we don't want to run joinable tests here as they
+    # are covered by the 'megatest' category.
+    isMainProcess = false
+    var cat = Category(p.key)
+    p.next
+    processCategory(r, cat, p.cmdLineRest, testsDir, runJoinableTests = false)
+  of "p", "pat", "pattern":
+    skips = loadSkipFrom(skipFrom)
+    let pattern = p.key
+    p.next
+    processPattern(r, pattern, p.cmdLineRest, simulate)
+  of "r", "run":
+    let (cat, path) = splitTestFile(p.key)
+    processSingleTest(r, cat.Category, p.cmdLineRest, path, gTargets, targetsSet)
+  of "html":
+    generateHtml(resultsFile, optFailing)
+  else:
+    quit Usage
+
+  if optPrintResults:
+    if action == "html": openDefaultBrowser(resultsFile)
+    else: echo r, r.data
+  azure.finalize()
+  backend.close()
+  var failed = r.total - r.passed - r.skipped
+  if failed != 0:
+    echo "FAILURE! total: ", r.total, " passed: ", r.passed, " skipped: ",
+      r.skipped, " failed: ", failed
+    quit(QuitFailure)
+  if isMainProcess:
+    echo "Used ", compilerPrefix, " to run the tests. Use --nim to override."
+
+if paramCount() == 0:
+  quit Usage
+main()
diff --git a/testament/testament.nim.cfg b/testament/testament.nim.cfg
new file mode 100644
index 000000000..c97284129
--- /dev/null
+++ b/testament/testament.nim.cfg
@@ -0,0 +1,6 @@
+# don't move this file without updating the logic in `isNimRepoTests`
+
+path = "$nim" # For compiler/nodejs
+-d:ssl # For azure
+# my SSL doesn't have this feature and I don't care:
+-d:nimDisableCertificateValidation
diff --git a/tests/testament/testamenthtml.templ b/testament/testamenthtml.nimf
index 9190f370e..9190f370e 100644
--- a/tests/testament/testamenthtml.templ
+++ b/testament/testamenthtml.nimf
diff --git a/testament/tests/shouldfail/tccodecheck.nim b/testament/tests/shouldfail/tccodecheck.nim
new file mode 100644
index 000000000..477da1e23
--- /dev/null
+++ b/testament/tests/shouldfail/tccodecheck.nim
@@ -0,0 +1,8 @@
+discard """
+  ccodecheck: "baz"
+"""
+
+proc foo(): void {.exportc: "bar".}=
+  echo "Hello World"
+
+foo()
diff --git a/testament/tests/shouldfail/tcolumn.nim b/testament/tests/shouldfail/tcolumn.nim
new file mode 100644
index 000000000..809ddec74
--- /dev/null
+++ b/testament/tests/shouldfail/tcolumn.nim
@@ -0,0 +1,8 @@
+discard """
+  errormsg: "undeclared identifier: 'undeclared'"
+  line: 9
+  column: 7
+"""
+
+# test should fail because the line directive is wrong
+echo undeclared
diff --git a/testament/tests/shouldfail/terrormsg.nim b/testament/tests/shouldfail/terrormsg.nim
new file mode 100644
index 000000000..6c130d107
--- /dev/null
+++ b/testament/tests/shouldfail/terrormsg.nim
@@ -0,0 +1,8 @@
+discard """
+  errormsg: "wrong error message"
+  line: 9
+  column: 6
+"""
+
+# test should fail because the line directive is wrong
+echo undeclared
diff --git a/testament/tests/shouldfail/texitcode1.nim b/testament/tests/shouldfail/texitcode1.nim
new file mode 100644
index 000000000..605f046db
--- /dev/null
+++ b/testament/tests/shouldfail/texitcode1.nim
@@ -0,0 +1,3 @@
+discard """
+  exitcode: 1
+"""
diff --git a/testament/tests/shouldfail/tfile.nim b/testament/tests/shouldfail/tfile.nim
new file mode 100644
index 000000000..b40a4f44f
--- /dev/null
+++ b/testament/tests/shouldfail/tfile.nim
@@ -0,0 +1,6 @@
+discard """
+  errormsg: "undeclared identifier: 'undefined'"
+  file: "notthisfile.nim"
+"""
+
+echo undefined
diff --git a/testament/tests/shouldfail/tline.nim b/testament/tests/shouldfail/tline.nim
new file mode 100644
index 000000000..fe782eb03
--- /dev/null
+++ b/testament/tests/shouldfail/tline.nim
@@ -0,0 +1,8 @@
+discard """
+  errormsg: "undeclared identifier: 'undeclared'"
+  line: 10
+  column: 6
+"""
+
+# test should fail because the line directive is wrong
+echo undeclared
diff --git a/testament/tests/shouldfail/tmaxcodesize.nim b/testament/tests/shouldfail/tmaxcodesize.nim
new file mode 100644
index 000000000..92022ee97
--- /dev/null
+++ b/testament/tests/shouldfail/tmaxcodesize.nim
@@ -0,0 +1,5 @@
+discard """
+  maxcodesize: 1
+"""
+
+echo "Hello World"
diff --git a/testament/tests/shouldfail/tnimout.nim b/testament/tests/shouldfail/tnimout.nim
new file mode 100644
index 000000000..0a65bfb70
--- /dev/null
+++ b/testament/tests/shouldfail/tnimout.nim
@@ -0,0 +1,7 @@
+discard """
+  nimout: "Hello World!"
+  action: compile
+"""
+
+static:
+  echo "something else"
diff --git a/testament/tests/shouldfail/tnimoutfull.nim b/testament/tests/shouldfail/tnimoutfull.nim
new file mode 100644
index 000000000..4fc93f6d2
--- /dev/null
+++ b/testament/tests/shouldfail/tnimoutfull.nim
@@ -0,0 +1,14 @@
+discard """
+  nimout: '''
+msg1
+msg2
+'''
+  action: compile
+  nimoutFull: true
+"""
+
+# should fail because `msg3` is not in nimout and `nimoutFill: true` was given
+static:
+  echo "msg1"
+  echo "msg2"
+  echo "msg3"
diff --git a/testament/tests/shouldfail/toutput.nim b/testament/tests/shouldfail/toutput.nim
new file mode 100644
index 000000000..eaf9e8652
--- /dev/null
+++ b/testament/tests/shouldfail/toutput.nim
@@ -0,0 +1,7 @@
+discard """
+  output: '''
+  done
+  '''
+"""
+
+echo "broken"
diff --git a/testament/tests/shouldfail/toutputsub.nim b/testament/tests/shouldfail/toutputsub.nim
new file mode 100644
index 000000000..47324ecee
--- /dev/null
+++ b/testament/tests/shouldfail/toutputsub.nim
@@ -0,0 +1,5 @@
+discard """
+  outputsub: "something else"
+"""
+
+echo "Hello World!"
diff --git a/testament/tests/shouldfail/treject.nim b/testament/tests/shouldfail/treject.nim
new file mode 100644
index 000000000..1e7258f70
--- /dev/null
+++ b/testament/tests/shouldfail/treject.nim
@@ -0,0 +1,7 @@
+discard """
+  action: "reject"
+"""
+
+# Because we set action="reject", we expect this line not to compile. But the
+# line does compile, therefore the test fails.
+assert true
diff --git a/testament/tests/shouldfail/tsortoutput.nim b/testament/tests/shouldfail/tsortoutput.nim
new file mode 100644
index 000000000..69dfbc0a0
--- /dev/null
+++ b/testament/tests/shouldfail/tsortoutput.nim
@@ -0,0 +1,11 @@
+discard """
+  sortoutput: true
+  output: '''
+2
+1
+'''
+"""
+
+# this test should ensure that the output is actually sorted
+echo "2"
+echo "1"
diff --git a/testament/tests/shouldfail/ttimeout.nim b/testament/tests/shouldfail/ttimeout.nim
new file mode 100644
index 000000000..fd3e1a598
--- /dev/null
+++ b/testament/tests/shouldfail/ttimeout.nim
@@ -0,0 +1,7 @@
+discard """
+  timeout: "0.1"
+"""
+
+import os
+
+os.sleep(1000)
diff --git a/testament/tests/shouldfail/tvalgrind.nim b/testament/tests/shouldfail/tvalgrind.nim
new file mode 100644
index 000000000..d551ff12e
--- /dev/null
+++ b/testament/tests/shouldfail/tvalgrind.nim
@@ -0,0 +1,17 @@
+discard """
+  valgrind: true
+  cmd: "nim $target --gc:arc -d:useMalloc $options $file"
+"""
+
+# this is the same check used by testament/specs.nim whether or not valgrind
+# tests are supported
+when defined(linux) and sizeof(int) == 8:
+  # discarding this allocation will cause valgrind to fail (which is what we
+  # want), but valgrind only runs on 64-bit Linux machines...
+  discard alloc(1)
+else:
+  # ...so on all other OS/architectures, simulate any non-zero exit code to
+  # mimic how valgrind would have failed on this test. We cannot use things like
+  # `disabled: "freebsd"` in the Testament configs above or else the tests will
+  # be SKIP-ed rather than FAIL-ed
+  quit(1) # choose 1 to match valgrind's `--error-exit=1`, but could be anything
diff --git a/tests/actiontable/tactiontable.nim b/tests/actiontable/tactiontable.nim
deleted file mode 100644
index 4560d0f7f..000000000
--- a/tests/actiontable/tactiontable.nim
+++ /dev/null
@@ -1,26 +0,0 @@
-discard """
-  output: "action 3 arg"
-"""
-
-import tables
-
-proc action1(arg: string) =
-  echo "action 1 ", arg
-
-proc action2(arg: string) =
-  echo "action 2 ", arg
-
-proc action3(arg: string) =
-  echo "action 3 ", arg
-
-proc action4(arg: string) =
-  echo "action 4 ", arg
-
-var
-  actionTable = {
-    "A": action1,
-    "B": action2,
-    "C": action3,
-    "D": action4}.toTable
-
-actionTable["C"]("arg")
diff --git a/tests/actiontable/tactiontable2.nim b/tests/actiontable/tactiontable2.nim
deleted file mode 100644
index fbc65a67d..000000000
--- a/tests/actiontable/tactiontable2.nim
+++ /dev/null
@@ -1,27 +0,0 @@
-discard """
-  output: "action 3 arg"
-"""
-
-import tables
-
-proc action1(arg: string) =
-  echo "action 1 ", arg
-
-proc action2(arg: string) =
-  echo "action 2 ", arg
-
-proc action3(arg: string) =
-  echo "action 3 ", arg
-
-proc action4(arg: string) =
-  echo "action 4 ", arg
-
-const
-  actionTable = {
-    "A": action1,
-    "B": action2,
-    "C": action3,
-    "D": action4}.toTable
-
-actionTable["C"]("arg")
-
diff --git a/tests/alias/t19349.nim b/tests/alias/t19349.nim
new file mode 100644
index 000000000..1e1e58264
--- /dev/null
+++ b/tests/alias/t19349.nim
@@ -0,0 +1,19 @@
+discard """
+  action: "compile"
+"""
+
+type
+  Vec3[T: SomeNumber] = object
+    arr: array[3, T]
+
+var 
+  cfloatArr: array[3, array[3, cfloat]]
+  cfloatSeq = newSeq[Vec3[cfloat]]()
+for row in cfloatArr:
+  cfloatSeq.add(Vec3[float32](arr: [row[0], row[1], row[2]]))
+
+var 
+  cuintArr: array[3, array[3, cuint]]
+  cuintSeq = newSeq[Vec3[cuint]]()
+for row in cuintArr:
+  cuintSeq.add(Vec3[uint32](arr: [row[0], row[1], row[2]]))
diff --git a/tests/align/globalalignas.nim b/tests/align/globalalignas.nim
new file mode 100644
index 000000000..15f564733
--- /dev/null
+++ b/tests/align/globalalignas.nim
@@ -0,0 +1,3 @@
+var myglobal1* {.align(128).}: int32
+var myglobal2* {.align(128).}: int32
+var myglobal3* {.align(128).}: int32
diff --git a/tests/align/talign.nim b/tests/align/talign.nim
new file mode 100644
index 000000000..08373ee49
--- /dev/null
+++ b/tests/align/talign.nim
@@ -0,0 +1,69 @@
+discard """
+ccodeCheck: "\\i @'NIM_ALIGN(128) NI mylocal1' .*"
+targets: "c cpp"
+output: "align ok"
+"""
+
+# This is for Azure. The keyword ``alignof`` only exists in ``c++11``
+# and newer. On Azure gcc does not default to c++11 yet.
+
+import globalalignas
+
+var toplevel1 {.align: 32.} : int32
+var toplevel2 {.align: 32.} : int32
+var toplevel3 {.align: 32.} : int32
+
+proc foobar() =
+  var myvar1 {.global, align(64).}: int = 123
+  var myvar2 {.global, align(64).}: int = 123
+  var myvar3 {.global, align(64).}: int = 123
+
+  doAssert (cast[uint](addr(myglobal1)) and 127) == 0
+  doAssert (cast[uint](addr(myglobal2)) and 127) == 0
+  doAssert (cast[uint](addr(myglobal3)) and 127) == 0
+
+  doAssert (cast[uint](addr(myvar1)) and 63) == 0
+  doAssert (cast[uint](addr(myvar2)) and 63) == 0
+  doAssert (cast[uint](addr(myvar3)) and 63) == 0
+
+  doAssert (cast[uint](addr(toplevel1)) and 31) == 0
+  doAssert (cast[uint](addr(toplevel2)) and 31) == 0
+  doAssert (cast[uint](addr(toplevel3)) and 31) == 0
+
+  # test multiple align expressions
+  var mylocal1 {.align(128), align(32).}: int = 123
+  var mylocal2 {.align(128), align(32).}: int = 123
+  var mylocal3 {.align(32), align(128).}: int = 123
+
+  doAssert (cast[uint](addr(mylocal1)) and 127) == 0
+  doAssert (cast[uint](addr(mylocal2)) and 127) == 0
+  doAssert (cast[uint](addr(mylocal3)) and 127) == 0
+
+  echo "align ok"
+
+foobar()
+
+# bug #13122
+
+type Bug[T] = object
+  bug{.align:64.}: T
+  sideffect{.align:64.}: int
+
+var bug: Bug[int]
+doAssert sizeof(bug) == 128, "Oops my size is " & $sizeof(bug) # 16
+
+
+block: # bug #22419
+  type
+    ValidatorPubKey = object
+      blob: array[96, byte]
+
+  proc f(): auto =
+    return iterator() =
+      var pad: int8 = 0
+      var y {.align: 16.}: ValidatorPubKey
+      let value = cast[uint64](addr y)
+      doAssert value mod 16 == 0
+
+  f()()
+
diff --git a/tests/align/tillegalalign.nim b/tests/align/tillegalalign.nim
new file mode 100644
index 000000000..c4a95f11b
--- /dev/null
+++ b/tests/align/tillegalalign.nim
@@ -0,0 +1,7 @@
+discard """
+cmd: "nim check $options $file"
+errormsg: "power of two expected"
+"""
+
+proc foobar() =
+  let something {.align(33).} = 123
diff --git a/tests/alloc/tmembug.nim b/tests/alloc/tmembug.nim
new file mode 100644
index 000000000..63b51ec5d
--- /dev/null
+++ b/tests/alloc/tmembug.nim
@@ -0,0 +1,54 @@
+discard """
+  joinable: false
+"""
+
+import std / [atomics, strutils, sequtils]
+
+type
+  BackendMessage* = object
+    field*: seq[int]
+
+var
+  chan1: Channel[BackendMessage]
+  chan2: Channel[BackendMessage]
+
+chan1.open()
+chan2.open()
+
+proc routeMessage*(msg: BackendMessage) =
+  discard chan2.trySend(msg)
+
+var
+  recv: Thread[void]
+  stopToken: Atomic[bool]
+
+proc recvMsg() =
+  while not stopToken.load(moRelaxed):
+    let resp = chan1.tryRecv()
+    if resp.dataAvailable:
+      routeMessage(resp.msg)
+      echo "child consumes ", formatSize getOccupiedMem()
+
+createThread[void](recv, recvMsg)
+
+const MESSAGE_COUNT = 100
+
+proc main() =
+  let msg: BackendMessage = BackendMessage(field: (0..500).toSeq())
+  for j in 0..0: #100:
+    echo "New iteration"
+
+    for _ in 1..MESSAGE_COUNT:
+      chan1.send(msg)
+    echo "After sending"
+
+    var counter = 0
+    while counter < MESSAGE_COUNT:
+      let resp = recv(chan2)
+      counter.inc
+    echo "After receiving ", formatSize getOccupiedMem()
+
+  stopToken.store true, moRelaxed
+  joinThreads(recv)
+
+main()
diff --git a/tests/alloc/tmembug2.nim b/tests/alloc/tmembug2.nim
new file mode 100644
index 000000000..01bce6f14
--- /dev/null
+++ b/tests/alloc/tmembug2.nim
@@ -0,0 +1,58 @@
+discard """
+  disabled: "true"
+"""
+
+import std / [atomics, strutils, sequtils, isolation]
+
+import threading / channels
+
+type
+  BackendMessage* = object
+    field*: seq[int]
+
+const MESSAGE_COUNT = 100
+
+var
+  chan1 = newChan[BackendMessage](MESSAGE_COUNT*2)
+  chan2 = newChan[BackendMessage](MESSAGE_COUNT*2)
+
+#chan1.open()
+#chan2.open()
+
+proc routeMessage*(msg: BackendMessage) =
+  var m = isolate(msg)
+  discard chan2.trySend(m)
+
+var
+  thr: Thread[void]
+  stopToken: Atomic[bool]
+
+proc recvMsg() =
+  while not stopToken.load(moRelaxed):
+    var resp: BackendMessage
+    if chan1.tryRecv(resp):
+      #if resp.dataAvailable:
+      routeMessage(resp)
+      echo "child consumes ", formatSize getOccupiedMem()
+
+createThread[void](thr, recvMsg)
+
+proc main() =
+  let msg: BackendMessage = BackendMessage(field: (0..5).toSeq())
+  for j in 0..100:
+    echo "New iteration"
+
+    for _ in 1..MESSAGE_COUNT:
+      chan1.send(msg)
+    echo "After sending"
+
+    var counter = 0
+    while counter < MESSAGE_COUNT:
+      let resp = recv(chan2)
+      counter.inc
+    echo "After receiving ", formatSize getOccupiedMem()
+
+  stopToken.store true, moRelaxed
+  joinThreads(thr)
+
+main()
diff --git a/tests/ambsym/tambsym3.nim b/tests/ambsym/tambsym3.nim
deleted file mode 100644
index b25dadfd6..000000000
--- a/tests/ambsym/tambsym3.nim
+++ /dev/null
@@ -1,15 +0,0 @@
-discard """
-  file: "tambsym3.nim"
-  line: 11
-  errormsg: "ambiguous identifier"
-"""
-# Test ambiguous symbols
-
-import mambsym1, times
-
-var
-  v = mDec #ERROR_MSG ambiguous identifier
-
-writeLine(stdout, ord(v))
-
-
diff --git a/tests/arc/amodule.nim b/tests/arc/amodule.nim
new file mode 100644
index 000000000..2b4204a66
--- /dev/null
+++ b/tests/arc/amodule.nim
@@ -0,0 +1,21 @@
+# bug #14219
+var vectors = @["a", "b", "c", "d", "e"]
+
+iterator testVectors(): string =
+  for vector in vectors:
+    yield vector
+
+var r = ""
+for item in testVectors():
+  r.add item
+echo r
+
+# bug #12990
+iterator test(): int {.closure.} =
+  yield 0
+
+let x = test
+while true:
+  let val = x()
+  if finished(x): break
+  echo val
diff --git a/tests/arc/bmodule.nim b/tests/arc/bmodule.nim
new file mode 100644
index 000000000..70c2cc645
--- /dev/null
+++ b/tests/arc/bmodule.nim
@@ -0,0 +1,4 @@
+import cmodule
+
+for i in @[1, 2, 3].cycle():
+  echo i
diff --git a/tests/arc/cmodule.nim b/tests/arc/cmodule.nim
new file mode 100644
index 000000000..6c6e6c0fa
--- /dev/null
+++ b/tests/arc/cmodule.nim
@@ -0,0 +1,4 @@
+iterator cycle*[T](s: openArray[T]): T =
+  let s = @s
+  for x in s:
+    yield x
diff --git a/tests/arc/dmodule.nim b/tests/arc/dmodule.nim
new file mode 100644
index 000000000..455ec7084
--- /dev/null
+++ b/tests/arc/dmodule.nim
@@ -0,0 +1,23 @@
+type
+  MinKind* = enum
+    minDictionary
+    minBool
+  MinValue* = object
+    case kind*: MinKind
+    of minDictionary:
+      symbols: seq[MinOperator]
+    else: discard
+  MinOperator = object
+
+# remove this inline pragma to make it compile
+proc `$`*(a: MinValue): string {.inline.} =
+  case a.kind
+  of minDictionary:
+    result = "hello"
+    for i in a.symbols:
+      result = "hello"
+  else: discard
+
+proc parseMinValue*(): MinValue =
+  # or this echo
+  echo result
diff --git a/tests/arc/nim.cfg b/tests/arc/nim.cfg
new file mode 100644
index 000000000..7c148b797
--- /dev/null
+++ b/tests/arc/nim.cfg
@@ -0,0 +1 @@
+--sinkInference:on
diff --git a/tests/arc/t14383.nim b/tests/arc/t14383.nim
new file mode 100644
index 000000000..96b505166
--- /dev/null
+++ b/tests/arc/t14383.nim
@@ -0,0 +1,217 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+  output: '''
+hello
+hello
+@["a", "b"]
+---------------------
+plain:
+destroying: ('first', 42)
+destroying: ('second', 20)
+destroying: ('third', 12)
+
+Option[T]:
+destroying: ('first', 42)
+destroying: ('second', 20)
+destroying: ('third', 12)
+
+seq[T]:
+destroying: ('first', 42)
+destroying: ('second', 20)
+destroying: ('third', 12)
+
+1 1
+'''
+"""
+
+import dmodule
+
+var val = parseMinValue()
+if val.kind == minDictionary:
+  echo val
+
+#------------------------------------------------------------------------------
+# Issue #15238
+#------------------------------------------------------------------------------
+
+proc sinkArg(x: sink seq[string]) =
+  discard
+
+proc varArg(lst: var seq[string]) = 
+  sinkArg(lst)
+
+var x = @["a", "b"]
+varArg(x)
+echo x
+
+
+#------------------------------------------------------------------------------
+# Issue #15286
+#------------------------------------------------------------------------------
+
+import std/os
+discard getFileInfo(".")
+
+
+#------------------------------------------------------------------------------
+# Issue #15707
+#------------------------------------------------------------------------------
+
+type
+  JVMObject = ref object
+proc freeJVMObject(o: JVMObject) =
+  discard
+proc fromJObject(T: typedesc[JVMObject]): T =
+  result.new(cast[proc(r: T) {.nimcall.}](freeJVMObject))
+
+discard JVMObject.fromJObject()
+
+
+#------------------------------------------------------------------------------
+# Issue #15910
+#------------------------------------------------------------------------------
+
+import options
+
+type
+  Thing = object
+    name: string
+    age: int
+
+proc `=destroy`(thing: var Thing) =
+  if thing.name != "":
+    echo "destroying: ('", thing.name, "', ", thing.age, ")"
+  `=destroy`(thing.name)
+  `=destroy`(thing.age)
+
+proc plain() =
+  var t = Thing(name: "first", age: 42)
+  t = Thing(name: "second", age: 20)
+  t = Thing()
+  let u = Thing(name: "third", age: 12)
+
+proc optionT() =
+  var t = Thing(name: "first", age: 42).some
+  t = Thing(name: "second", age: 20).some
+  t = none(Thing)
+  let u = Thing(name: "third", age: 12).some
+
+proc seqT() =
+  var t = @[Thing(name: "first", age: 42)]
+  t = @[Thing(name: "second", age: 20)]
+  t = @[]
+  let u = @[Thing(name: "third", age: 12)]
+
+echo "---------------------"
+echo "plain:"
+plain()
+echo()
+
+echo "Option[T]:"
+optionT()
+echo()
+
+echo "seq[T]:"
+seqT()
+echo()
+
+
+#------------------------------------------------------------------------------
+# Issue #16120, const seq into sink
+#------------------------------------------------------------------------------
+
+proc main =
+  let avals = @[@[1.0'f32, 4.0, 7.0, 10.0]]
+  let rankdef = avals
+  echo avals.len, " ", rankdef.len
+
+main()
+
+
+#------------------------------------------------------------------------------
+# Issue #16722, ref on distinct type, wrong destructors called
+#------------------------------------------------------------------------------
+
+type
+  Obj = object of RootObj
+  ObjFinal = object
+  ObjRef = ref Obj
+  ObjFinalRef = ref ObjFinal
+  D = distinct Obj
+  DFinal = distinct ObjFinal
+  DRef = ref D
+  DFinalRef = ref DFinal
+
+proc `=destroy`(o: var Obj) =
+  doAssert false, "no Obj is constructed in this sample"
+
+proc `=destroy`(o: var ObjFinal) =
+  doAssert false, "no ObjFinal is constructed in this sample"
+
+var dDestroyed: int
+proc `=destroy`(d: var D) =
+  dDestroyed.inc
+
+proc `=destroy`(d: var DFinal) =
+  dDestroyed.inc
+
+func newD(): DRef =
+  DRef ObjRef()
+
+func newDFinal(): DFinalRef =
+  DFinalRef ObjFinalRef()
+
+proc testRefs() =
+  discard newD()
+  discard newDFinal()
+
+testRefs()
+
+doAssert(dDestroyed == 2)
+
+
+#------------------------------------------------------------------------------
+# Issue #16185, complex self-assingment elimination
+#------------------------------------------------------------------------------
+
+type
+  CpuStorage*[T] = ref CpuStorageObj[T]
+  CpuStorageObj[T] = object
+    size*: int
+    raw_buffer*: ptr UncheckedArray[T]
+  Tensor[T] = object
+    buf*: CpuStorage[T]
+  TestObject = object
+    x: Tensor[float]
+
+proc `=destroy`[T](s: var CpuStorageObj[T]) =
+  if s.raw_buffer != nil:
+    s.raw_buffer.deallocShared()
+    s.size = 0
+    s.raw_buffer = nil
+
+proc `=`[T](a: var CpuStorageObj[T]; b: CpuStorageObj[T]) {.error.}
+
+proc allocCpuStorage[T](s: var CpuStorage[T], size: int) =
+  new(s)
+  s.raw_buffer = cast[ptr UncheckedArray[T]](allocShared0(sizeof(T) * size))
+  s.size = size
+
+proc newTensor[T](size: int): Tensor[T] =
+  allocCpuStorage(result.buf, size)
+
+proc `[]`[T](t: Tensor[T], idx: int): T = t.buf.raw_buffer[idx]
+proc `[]=`[T](t: Tensor[T], idx: int, val: T) = t.buf.raw_buffer[idx] = val
+
+proc toTensor[T](s: seq[T]): Tensor[T] =
+  result = newTensor[T](s.len)
+  for i, x in s:
+    result[i] = x
+
+proc main2() =
+  var t: TestObject
+  t.x = toTensor(@[1.0, 2, 3, 4])
+  t.x = t.x  
+  doAssert(t.x.buf != nil) # self-assignment above should be eliminated
+
+main2()
diff --git a/tests/arc/t14472.nim b/tests/arc/t14472.nim
new file mode 100644
index 000000000..4ef661161
--- /dev/null
+++ b/tests/arc/t14472.nim
@@ -0,0 +1,43 @@
+discard """
+  valgrind: true
+  cmd: "nim cpp --gc:arc -d:useMalloc --deepcopy:on $file"
+"""
+
+type
+  ImportMaterial* = object
+    # Adding a field here makes the problem go away.
+
+  Mesh* = object
+    vertices: seq[float32]
+    material: ImportMaterial
+
+  ImportedScene* = object
+    meshes*: seq[Mesh]
+
+proc bork() : ImportedScene =
+  var mats: seq[ImportMaterial]
+
+  setLen(mats, 1)
+  add(result.meshes, Mesh(material: mats[0]))
+
+var s = bork()
+
+
+#------------------------------------------------------------------------
+# issue #15543
+
+import tables
+
+type
+  cdbl {.importc: "double".} = object
+
+  MyObject = ref object of RootObj
+    y: Table[string, cdbl]
+        
+
+proc test =
+  var x = new(MyObject)
+
+test()
+
+
diff --git a/tests/arc/t14864.nim b/tests/arc/t14864.nim
new file mode 100644
index 000000000..f59b14d2c
--- /dev/null
+++ b/tests/arc/t14864.nim
@@ -0,0 +1,5 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+"""
+
+import bmodule
diff --git a/tests/arc/t15909.nim b/tests/arc/t15909.nim
new file mode 100644
index 000000000..f25c89daf
--- /dev/null
+++ b/tests/arc/t15909.nim
@@ -0,0 +1,16 @@
+discard """
+  action: run
+  cmd: "nim c --gc:arc $file"
+"""
+
+proc f1() {.noreturn.} = raise newException(CatchableError, "")
+
+proc f2(y: int): int =
+  if y != 0:
+    y
+  else:
+    f1()
+
+doAssert f2(5) == 5
+doAssertRaises(CatchableError):
+  discard f2(0)
diff --git a/tests/arc/t16033.nim b/tests/arc/t16033.nim
new file mode 100644
index 000000000..59ed22e4d
--- /dev/null
+++ b/tests/arc/t16033.nim
@@ -0,0 +1,10 @@
+discard """
+  targets: "c js"
+  matrix: "--gc:arc"
+"""
+
+# bug #16033
+when defined js:
+  doAssert not compileOption("gc", "arc")
+else:
+  doAssert compileOption("gc", "arc")
diff --git a/tests/arc/t16458.nim b/tests/arc/t16458.nim
new file mode 100644
index 000000000..6ae114287
--- /dev/null
+++ b/tests/arc/t16458.nim
@@ -0,0 +1,6 @@
+discard """
+  matrix: "--gc:orc --d:useNimRtl"
+  action: "compile"
+"""
+
+echo 134
\ No newline at end of file
diff --git a/tests/arc/t16558.nim b/tests/arc/t16558.nim
new file mode 100644
index 000000000..0dbe02b33
--- /dev/null
+++ b/tests/arc/t16558.nim
@@ -0,0 +1,9 @@
+discard """
+  matrix: "--gc:arc"
+  errormsg: "expression cannot be cast to 'int'"
+"""
+
+block: # bug #16558
+  var value = "hi there"
+  var keepInt: int
+  keepInt = cast[int](value)
diff --git a/tests/arc/t17025.nim b/tests/arc/t17025.nim
new file mode 100644
index 000000000..a64c59ac1
--- /dev/null
+++ b/tests/arc/t17025.nim
@@ -0,0 +1,56 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+  output: '''
+{"Package": {"name": "hello"}, "Author": {"name": "name", "qq": "123456789", "email": "email"}}
+hello
+name
+123456789
+email
+hello
+name2
+987654321
+liame
+'''
+"""
+
+import parsecfg, streams, tables
+
+const cfg = """[Package]
+name=hello
+[Author]
+name=name
+qq=123456789
+email="email""""
+
+proc main() =
+    let stream = newStringStream(cfg)
+    let dict = loadConfig(stream)
+    var pname = dict.getSectionValue("Package","name")
+    var name = dict.getSectionValue("Author","name")
+    var qq = dict.getSectionValue("Author","qq")
+    var email = dict.getSectionValue("Author","email")
+    echo dict[]
+    echo pname & "\n" & name & "\n" & qq & "\n" & email
+    stream.close()
+
+main()
+
+proc getDict(): OrderedTableRef[string, OrderedTableRef[string, string]] =
+    result = newOrderedTable[string, OrderedTableRef[string, string]]()
+    result["Package"] = newOrderedTable[string, string]()
+    result["Package"]["name"] = "hello"
+    result["Author"] = newOrderedTable[string, string]()
+    result["Author"]["name"] = "name2"
+    result["Author"]["qq"] = "987654321"
+    result["Author"]["email"] = "liame"
+
+proc main2() =
+    let dict = getDict()
+    var pname = dict.getSectionValue("Package","name")
+    var name = dict.getSectionValue("Author","name")
+    var qq = dict.getSectionValue("Author","qq")
+    var email = dict.getSectionValue("Author","email")
+    echo pname & "\n" & name & "\n" & qq & "\n" & email
+
+main2()
+
diff --git a/tests/arc/t17173.nim b/tests/arc/t17173.nim
new file mode 100644
index 000000000..0acd886a2
--- /dev/null
+++ b/tests/arc/t17173.nim
@@ -0,0 +1,10 @@
+discard """
+  matrix: "--gc:refc; --gc:arc"
+"""
+
+import std/strbasics
+
+
+var a = "  vhellov   "
+strip(a)
+doAssert a == "vhellov"
diff --git a/tests/arc/t17812.nim b/tests/arc/t17812.nim
new file mode 100644
index 000000000..dd8ac89b0
--- /dev/null
+++ b/tests/arc/t17812.nim
@@ -0,0 +1,41 @@
+discard """
+  targets: "c js"
+  matrix: "--gc:refc; --gc:arc"
+"""
+
+import std/times
+
+block: # bug #17812
+  block:
+    type
+      Task = object
+        cb: proc ()
+
+    proc hello() = discard
+
+
+    let t = Task(cb: hello)
+
+    doAssert t.repr.len > 0
+
+
+  block:
+    type MyObj = object
+      field: DateTime
+
+
+    proc `$`(o: MyObj): string = o.repr
+
+    doAssert ($MyObj()).len > 0
+
+# bug #22175
+
+type Xxx = object
+  value: string
+
+proc complete(xxx: ref Xxx, v: sink string) =
+  xxx.value = move(v)
+
+let yyy = (ref Xxx)()
+
+yyy.complete("test")
diff --git a/tests/arc/t18645.nim b/tests/arc/t18645.nim
new file mode 100644
index 000000000..c5fddd4bb
--- /dev/null
+++ b/tests/arc/t18645.nim
@@ -0,0 +1,18 @@
+discard """
+  matrix: "--gc:arc; --gc:refc"
+  output: '''
+1
+2
+3
+'''
+"""
+
+proc bitTypeIdUnion() =
+  var bitId {.global.} = block:
+    0
+  inc bitId
+  echo bitId
+
+bitTypeIdUnion()
+bitTypeIdUnion()
+bitTypeIdUnion()
diff --git a/tests/arc/t18971.nim b/tests/arc/t18971.nim
new file mode 100644
index 000000000..9b587d956
--- /dev/null
+++ b/tests/arc/t18971.nim
@@ -0,0 +1,10 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+"""
+
+type MyObj = ref object
+
+var o = MyObj()
+proc x: var MyObj = o
+
+var o2 = x()
diff --git a/tests/arc/t18977.nim b/tests/arc/t18977.nim
new file mode 100644
index 000000000..c775551a4
--- /dev/null
+++ b/tests/arc/t18977.nim
@@ -0,0 +1,26 @@
+discard """
+  matrix: "--mm:arc"
+"""
+
+type
+  E = enum
+    a, b, c, d
+  X = object
+    v: int
+  O = object
+    case kind: E
+    of a:
+      a: int
+    of {b, c}:
+      b: float
+    else:
+      d: X
+
+proc `=destroy`(x: var X) =
+  echo "x destroyed"
+
+var o = O(kind: d, d: X(v: 12345))
+doAssert o.d.v == 12345
+
+doAssertRaises(FieldDefect):
+  o.kind = a
diff --git a/tests/arc/t19231.nim b/tests/arc/t19231.nim
new file mode 100644
index 000000000..40fcf277c
--- /dev/null
+++ b/tests/arc/t19231.nim
@@ -0,0 +1,18 @@
+discard """
+  matrix: "--mm:orc"
+  targets: "c cpp"
+"""
+
+type
+  Game* = ref object
+
+proc free*(game: Game) =
+  var mixNumOpened:cint = 0
+  for i in 0..<mixNumOpened:
+    mixNumOpened -= 1
+
+proc newGame*(): Game =
+  new result, free
+
+var
+  game*: Game
diff --git a/tests/arc/t19364.nim b/tests/arc/t19364.nim
new file mode 100644
index 000000000..f520f3291
--- /dev/null
+++ b/tests/arc/t19364.nim
@@ -0,0 +1,30 @@
+discard """
+  cmd: '''nim c --gc:arc --expandArc:fooLeaks $file'''
+  nimout: '''
+--expandArc: fooLeaks
+
+var
+  tmpTuple_cursor
+  a_cursor
+  b_cursor
+  c_cursor
+tmpTuple_cursor = refTuple
+a_cursor = tmpTuple_cursor[0]
+b_cursor = tmpTuple_cursor[1]
+c_cursor = tmpTuple_cursor[2]
+-- end of expandArc ------------------------
+'''
+"""
+
+func fooLeaks(refTuple: tuple[a,
+                              b,
+                              c: seq[float]]): float =
+  let (a, b, c) = refTuple
+
+let refset = (a: newSeq[float](25_000_000),
+              b: newSeq[float](25_000_000),
+              c: newSeq[float](25_000_000))
+
+var res = newSeq[float](1_000_000)
+for i in 0 .. res.high:
+  res[i] = fooLeaks(refset)
diff --git a/tests/arc/t19401.nim b/tests/arc/t19401.nim
new file mode 100644
index 000000000..56702a4a2
--- /dev/null
+++ b/tests/arc/t19401.nim
@@ -0,0 +1,32 @@
+discard """
+  output: '''
+delete foo
+delete foo
+delete foo
+'''
+  matrix: "--mm:arc"
+"""
+
+type Foo = ref object
+  data: int
+proc delete(self: Foo) 
+proc newFoo: Foo =
+  let x = 12
+  discard x
+  new(result, delete)
+  result.data = x
+proc delete(self: Foo) =
+  doAssert self.data == 12
+  echo("delete foo")
+
+if isMainModule:
+  proc test() =
+    let x1 = newFoo()
+    let x2 = newFoo()
+    discard x1
+    discard x2
+    var x3: Foo
+    new(x3, delete)
+    x3.data = 12
+    discard x3
+  test()
diff --git a/tests/arc/t19402.nim b/tests/arc/t19402.nim
new file mode 100644
index 000000000..5ee6fc798
--- /dev/null
+++ b/tests/arc/t19402.nim
@@ -0,0 +1,32 @@
+discard """
+  output: '''
+delete foo
+delete foo
+delete foo
+'''
+  matrix: "--mm:arc"
+"""
+
+type Foo = ref object of RootObj
+  data: int
+proc delete(self: Foo)
+proc newFoo: Foo =
+  let x = 12
+  discard x
+  new(result, delete)
+  result.data = x
+proc delete(self: Foo) =
+  doAssert self.data == 12
+  echo("delete foo")
+
+if isMainModule:
+  proc test() =
+    let x1 = newFoo()
+    let x2 = newFoo()
+    discard x1
+    discard x2
+    var x3: Foo
+    new(x3, delete)
+    x3.data = 12
+    discard x3
+  test()
\ No newline at end of file
diff --git a/tests/arc/t19435.nim b/tests/arc/t19435.nim
new file mode 100644
index 000000000..519216bad
--- /dev/null
+++ b/tests/arc/t19435.nim
@@ -0,0 +1,29 @@
+discard """
+  matrix: "--gc:arc"
+"""
+
+# bug #19435
+{.experimental: "views".}
+
+type
+  Bar = object
+    placeholder: int
+  Foo = object
+    placeholder: int
+    c: seq[Bar] # remove this line to make things right
+
+func children*(s: var seq[Foo]): openArray[Foo] =
+  s.toOpenArray(0, s.len-1)
+
+proc test =
+  var foos = @[Foo(), Foo()]
+
+  assert foos.children.len == 2
+  var flag = true
+  for a in foos.children:
+    flag = false
+
+  if flag:
+    doAssert false
+
+test()
\ No newline at end of file
diff --git a/tests/arc/t19457.nim b/tests/arc/t19457.nim
new file mode 100644
index 000000000..78447ce82
--- /dev/null
+++ b/tests/arc/t19457.nim
@@ -0,0 +1,16 @@
+discard """
+  matrix: "--gc:refc; --gc:arc"
+"""
+
+# bug #19457
+proc gcd(x, y: seq[int]): seq[int] =
+    var
+      a = x
+      b = y
+    while b[0] > 0:
+      let c = @[a[0] mod b[0]]
+      a = b
+      b = c
+    return a
+
+doAssert gcd(@[1], @[2]) == @[1]
\ No newline at end of file
diff --git a/tests/arc/t19862.nim b/tests/arc/t19862.nim
new file mode 100644
index 000000000..6d3f57692
--- /dev/null
+++ b/tests/arc/t19862.nim
@@ -0,0 +1,15 @@
+discard """
+  matrix: "--gc:refc; --gc:arc"
+"""
+
+import std/widestrs
+
+# bug #19862
+type NewString = object
+
+proc len(s: NewString): int = 10
+
+converter toNewString(x: WideCStringObj): NewString = discard
+
+let w = newWideCString("test")
+doAssert len(w) == 4
diff --git a/tests/arc/t20456.nim b/tests/arc/t20456.nim
new file mode 100644
index 000000000..ace79255a
--- /dev/null
+++ b/tests/arc/t20456.nim
@@ -0,0 +1,7 @@
+discard """
+  cmd: "nim check $file"
+  action: "compile"
+"""
+
+when not defined(gcOrc):
+  {.error: "orc".}
diff --git a/tests/arc/t20588.nim b/tests/arc/t20588.nim
new file mode 100644
index 000000000..008bd1dcd
--- /dev/null
+++ b/tests/arc/t20588.nim
@@ -0,0 +1,25 @@
+discard """
+  cmd: "nim check --warnings:off --hints:off $file"
+  errormsg: ""
+  nimout: '''
+t20588.nim(20, 12) Error: illegal type conversion to 'auto'
+t20588.nim(21, 14) Error: illegal type conversion to 'typed'
+t20588.nim(22, 16) Error: illegal type conversion to 'untyped'
+t20588.nim(24, 7) Error: illegal type conversion to 'any'
+'''
+"""
+
+
+
+
+
+
+
+
+
+discard 0.0.auto
+discard typed("abc")
+discard untyped(4)
+var a = newSeq[bool](1000)
+if any(a):
+  echo "ok?"
\ No newline at end of file
diff --git a/tests/arc/t21184.nim b/tests/arc/t21184.nim
new file mode 100644
index 000000000..91d0c42c7
--- /dev/null
+++ b/tests/arc/t21184.nim
@@ -0,0 +1,77 @@
+discard """
+  matrix: "--mm:orc"
+"""
+
+import std/[with]
+
+type
+  Node* {.acyclic.} = ref object of RootObj
+    name: string
+    data: pointer
+    children: seq[Node]
+  TextNode = ref object of Node
+    text: string
+
+proc fakeEcho(s: string) =
+  if s.len < 0:
+    echo s
+
+proc newNode[T: Node](parent: Node): T =
+  new result
+  result.data = alloc0(250)
+  parent.children.add(result)
+
+proc newRootNode(): Node =
+  new result
+  result.data = alloc0(250)
+
+method printNode(node: Node) {.base.} =
+  fakeEcho node.name
+
+method printNode(node: TextNode) =
+  procCall printNode(Node(node))
+  fakeEcho node.text
+
+proc printChildren(node: Node) =
+  for child in node.children:
+    child.printNode()
+    printChildren(child)
+
+proc free(node: Node) =
+  for child in node.children:
+    free(child)
+  dealloc(node.data)
+
+template node(parent: Node, body: untyped): untyped =
+  var node = newNode[Node](parent)
+  with node:
+    body
+
+proc textNode(parent: Node, text: string) =
+  var node = newNode[TextNode](parent)
+  node.text = text
+
+template withRootNode(body: untyped): untyped =
+  var root = newRootNode()
+  root.name = "root"
+  with root:
+    body
+  root.printNode()
+  printChildren(root)
+  root.free()
+
+proc doTest() =
+  withRootNode:
+    node:
+      name = "child1"
+      node:
+        name = "child2"
+        node:
+          name = "child3"
+          textNode "Hello, world!"
+
+
+# bug #21171
+if isMainModule:
+  for i in 0..100000:
+    doTest()
diff --git a/tests/arc/t22218.nim b/tests/arc/t22218.nim
new file mode 100644
index 000000000..7837ed1d0
--- /dev/null
+++ b/tests/arc/t22218.nim
@@ -0,0 +1,25 @@
+discard """
+  cmd: "nim c --mm:arc $file"
+  errormsg: "'=copy' is not available for type <Obj>; requires a copy because it's not the last read of 'chan[]'; routine: test"
+"""
+
+# bug #22218
+type Obj[T] = object
+  v: T
+
+proc `=copy`[T](
+    dest: var Obj[T],
+    source: Obj[T]
+  ) {.error: "A channel cannot be copied".}
+
+from system/ansi_c import c_calloc
+
+proc test() =
+    var v: bool = true
+    var chan = cast[ptr Obj[int]](c_calloc(1, csize_t sizeof(Obj[int])))
+    var copy = chan[]
+
+    echo chan.v
+    echo v
+
+test()
\ No newline at end of file
diff --git a/tests/arc/t22237.nim b/tests/arc/t22237.nim
new file mode 100644
index 000000000..c6dbf768a
--- /dev/null
+++ b/tests/arc/t22237.nim
@@ -0,0 +1,55 @@
+discard """
+  matrix: "--mm:arc; --mm:orc"
+"""
+
+import std/macros
+import std/streams
+
+# bug #22237
+
+proc iterlines_closure2(f: File | Stream): iterator (): string =
+  result = iterator(): string =
+    for line in f.lines:
+      if line.len == 0:
+        break
+      yield line
+
+proc test() =
+  let f = newStringStream("""
+    1
+    2
+
+    3
+    4
+
+    5
+    6
+    7
+
+    8
+""")
+  while not f.atEnd():
+    let iterator_inst = iterlines_closure2(f)
+    for item in iterator_inst(): # Fails with "SIGSEGV: Illegal storage access. (Attempt to read from nil?)"
+      discard
+
+test()
+
+# bug #21160
+import sequtils
+
+iterator allMoves(fls: seq[int]): seq[int] =
+  yield fls
+
+proc neighbors(flrs: seq[int]): iterator: seq[int] =
+  return iterator(): seq[int] =
+    for flrs2 in allMoves(flrs):
+      yield flrs2
+      for flrs3 in allMoves(flrs2):
+        yield flrs3
+
+let f = @[1]
+for _ in neighbors(f):
+  discard
+for _ in neighbors(f):
+  discard
diff --git a/tests/arc/t22478.nim b/tests/arc/t22478.nim
new file mode 100644
index 000000000..5373fa161
--- /dev/null
+++ b/tests/arc/t22478.nim
@@ -0,0 +1,46 @@
+discard """
+  matrix: "-d:nimNoLentIterators --mm:arc"
+  output: '''PUSH DATA: {"test.message":{"test":{"nested":"v1"}}}'''
+  joinable: false
+"""
+
+# bug #22748
+import std/[json, typetraits, times]
+
+# publish
+
+proc publish*[T](payload: T) =
+  discard
+
+type MetricsPoint* = JsonNode
+
+proc push*(stat: string, data: JsonNode, usec: int64 = 0) =
+  let payload = newJObject()
+
+  # this results in a infinite recursion unless we deepCopy()
+  payload[stat] = data #.deepCopy
+
+  echo "PUSH DATA: ", payload
+
+  publish[MetricsPoint](payload)
+
+var scopes {.threadvar.}: seq[JsonNode]
+
+type WithTimeCallback*[T] = proc(data: var JsonNode): T
+
+proc pushScoped*[T](metric: string, blk: WithTimeCallback[T]): T {.gcsafe.} =
+  scopes.add newJObject()
+  defer: discard scopes.pop()
+
+  let stc = (cpuTime() * 1000_000).int64
+  result = blk(scopes[^1])
+  let dfc = (cpuTime() * 1000_000).int64 - stc
+
+  push(metric, scopes[^1], dfc)
+
+# demo code
+
+discard pushScoped[int]("test.message") do (data: var JsonNode) -> int:
+  data["test"] = %*{
+    "nested": "v1"
+  }
\ No newline at end of file
diff --git a/tests/arc/t22787.nim b/tests/arc/t22787.nim
new file mode 100644
index 000000000..5840a984b
--- /dev/null
+++ b/tests/arc/t22787.nim
@@ -0,0 +1,37 @@
+discard """
+  joinable: false
+"""
+
+import std/assertions
+
+proc foo =
+  var s:seq[string]
+  var res = ""
+
+  for i in 0..3:
+    s.add ("test" & $i)
+    s.add ("test" & $i)
+
+  var lastname:string
+
+  for i in s:
+    var name = i[0..4]
+
+    if name != lastname:
+      res.add "NEW:" & name & "\n"
+    else:
+      res.add name & ">" & lastname & "\n"
+
+    lastname = name
+
+  doAssert res == """
+NEW:test0
+test0>test0
+NEW:test1
+test1>test1
+NEW:test2
+test2>test2
+NEW:test3
+test3>test3
+"""
+foo()
\ No newline at end of file
diff --git a/tests/arc/t23247.nim b/tests/arc/t23247.nim
new file mode 100644
index 000000000..0fadc50cd
--- /dev/null
+++ b/tests/arc/t23247.nim
@@ -0,0 +1,52 @@
+discard """
+  matrix: ";-d:useMalloc"
+"""
+
+# bug #23247
+import std/hashes
+
+func baseAddr[T](x: openArray[T]): ptr T =
+  # Return the address of the zero:th element of x or `nil` if x is empty
+  if x.len == 0: nil else: cast[ptr T](x)
+
+func makeUncheckedArray[T](p: ptr T): ptr UncheckedArray[T] =
+  cast[ptr UncheckedArray[T]](p)
+
+type
+  LabelKey = object
+    data: seq[string]
+    refs: ptr UncheckedArray[string]
+    refslen: int
+
+  Gauge = ref object
+    metrics: seq[seq[seq[string]]]
+
+template values(key: LabelKey): openArray[string] =
+  if key.refslen > 0:
+    key.refs.toOpenArray(0, key.refslen - 1)
+  else:
+    key.data
+
+proc hash(key: LabelKey): Hash =
+  hash(key.values)
+
+proc view(T: type LabelKey, values: openArray[string]): T =
+  # TODO some day, we might get view types - until then..
+  LabelKey(refs: baseAddr(values).makeUncheckedArray(), refslen: values.len())
+
+template withValue2(k: untyped) =
+  discard hash(k)
+
+proc setGauge(
+    collector: Gauge,
+    labelValues: openArray[string],
+) =
+  let v = LabelKey.view(labelValues)
+  withValue2(v)
+  collector.metrics.add @[@labelValues, @labelValues]
+  discard @labelValues
+
+var nim_gc_mem_bytes = Gauge()
+let threadID = $getThreadId()
+setGauge(nim_gc_mem_bytes, @[threadID])
+setGauge(nim_gc_mem_bytes, @[threadID])
\ No newline at end of file
diff --git a/tests/arc/t9650.nim b/tests/arc/t9650.nim
new file mode 100644
index 000000000..a8182db68
--- /dev/null
+++ b/tests/arc/t9650.nim
@@ -0,0 +1,87 @@
+discard """
+  matrix: "--gc:arc"
+"""
+
+import typetraits
+
+# bug #9650
+type
+  SharedPtr*[T] = object
+    val: ptr tuple[atomicCounter: int, value: T]
+
+  Node*[T] = object
+    value: T
+    next: SharedPtr[Node[T]]
+
+  ForwardList*[T] = object
+    first: SharedPtr[Node[T]]
+
+proc `=destroy`*[T](p: var SharedPtr[T]) =
+  if p.val != nil:
+    let c = atomicDec(p.val[].atomicCounter)
+    if c == 0:
+      when not supportsCopyMem(T):
+         `=destroy`(p.val[])
+      dealloc(p.val)
+    p.val = nil
+
+proc `=`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) =
+  if dest.val != src.val:
+    if dest.val != nil:
+      `=destroy`(dest)
+    if src.val != nil:
+      discard atomicInc(src.val[].atomicCounter)
+      dest.val = src.val
+
+proc `=sink`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) =
+  if dest.val != nil and dest.val != src.val:
+    `=destroy`(dest)
+  dest.val = src.val
+
+ 
+proc newSharedPtr*[T](val: sink T): SharedPtr[T] =
+  result.val = cast[type(result.val)](alloc(sizeof(result.val[])))
+  reset(result.val[])
+  result.val.atomicCounter = 1
+  result.val.value = val
+
+proc isNil*[T](p: SharedPtr[T]): bool =
+  p.val == nil
+
+template `->`*[T](p: SharedPtr[T], name: untyped): untyped =
+  p.val.value.name
+
+proc createNode[T](val: T): SharedPtr[ Node[T] ]=
+  result = newSharedPtr(Node[T](value: val))
+
+proc push_front*[T](list: var ForwardList[T], val: T) =
+  var newElem = createNode(val)
+  newElem->next = list.first
+  list.first = newElem
+
+proc pop_front*[T](list: var ForwardList[T]) =
+  let head = list.first
+  list.first = head->next
+
+proc toString*[T](list: ForwardList[T]): string =
+  result = "["
+  var head = list.first
+  while not head.isNil:
+    result &= $(head->value) & ", "
+    head = head->next
+  result &= ']'
+
+block:
+  var x: ForwardList[int]
+  x.push_front(1)
+  x.push_front(2)
+  x.push_front(3)
+
+  doAssert toString(x) == "[3, 2, 1, ]"
+
+  x.pop_front()
+  x.pop_front()
+  doAssert toString(x) == "[1, ]"
+
+  x.pop_front()
+  doAssert toString(x) == "[]"
diff --git a/tests/arc/taliased_reassign.nim b/tests/arc/taliased_reassign.nim
new file mode 100644
index 000000000..5563fae8c
--- /dev/null
+++ b/tests/arc/taliased_reassign.nim
@@ -0,0 +1,41 @@
+discard """
+  matrix: "--mm:orc"
+"""
+
+# bug #20993
+
+type
+  Dual[int] = object # must be generic (even if fully specified)
+    p: int
+proc D(p: int): Dual[int] = Dual[int](p: p)
+proc `+`(x: Dual[int], y: Dual[int]): Dual[int] = D(x.p + y.p)
+
+type
+  Tensor[T] = object
+    buf: seq[T]
+proc newTensor*[T](s: int): Tensor[T] = Tensor[T](buf: newSeq[T](s))
+proc `[]`*[T](t: Tensor[T], idx: int): T = t.buf[idx]
+proc `[]=`*[T](t: var Tensor[T], idx: int, val: T) = t.buf[idx] = val
+
+proc `+.`[T](t1, t2: Tensor[T]): Tensor[T] =
+  let n = t1.buf.len
+  result = newTensor[T](n)
+  for i in 0 ..< n:
+    result[i] = t1[i] + t2[i]
+
+proc toTensor*[T](a: sink seq[T]): Tensor[T] =
+  ## This breaks it: Using `T` instead makes it work
+  type U = typeof(a[0])
+  var t: Tensor[U] # Tensor[T] works
+  t.buf = a
+  result = t
+
+proc loss() =
+  var B = toTensor(@[D(123)])
+  let a = toTensor(@[D(-10)])
+  B = B +. a
+  doAssert B[0].p == 113, "I want to be 113, but I am " & $B[0].p
+
+loss()
+
+
diff --git a/tests/arc/tamemfiles.nim b/tests/arc/tamemfiles.nim
new file mode 100644
index 000000000..97deb489f
--- /dev/null
+++ b/tests/arc/tamemfiles.nim
@@ -0,0 +1,110 @@
+discard """
+  output: '''
+loop 1a
+loop 1b; cols: @[1, x]
+loop 1c
+loop 1d
+loop 1a
+loop 1b; cols: @[2, y]
+loop 1c
+loop 1d
+'''
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #13596
+
+import tables, memfiles, strutils, os
+
+type Splitr* = tuple[ repeat: bool, chrDlm: char, setDlm: set[char], n: int ]
+
+type csize = uint
+proc cmemchr*(s: pointer, c: char, n: csize): pointer {.
+  importc: "memchr", header: "<string.h>" .}
+proc `-!`*(p, q: pointer): int {.inline.} =
+  (cast[int](p) -% cast[int](q)).int
+proc `+!`*(p: pointer, i: int): pointer {.inline.} =
+  cast[pointer](cast[int](p) +% i)
+proc `+!`*(p: pointer, i: uint64): pointer {.inline.} =
+  cast[pointer](cast[uint64](p) + i)
+
+proc charEq(x, c: char): bool {.inline.} = x == c
+
+proc initSplitr*(delim: string): Splitr =
+  if delim == "white":          #User can use any other permutation if needed
+    result.repeat = true
+    result.chrDlm = ' '
+    result.setDlm = { ' ', '\t', '\n' }
+    result.n      = result.setDlm.card
+    return
+  for c in delim:
+    if c in result.setDlm:
+      result.repeat = true
+      continue
+    result.setDlm.incl(c)
+    inc(result.n)
+  if result.n == 1:             #support n==1 test to allow memchr optimization
+    result.chrDlm = delim[0]
+
+proc hash(x: MemSlice): int = 55542
+
+template defSplit[T](slc: T, fs: var seq[MemSlice], n: int, repeat: bool,
+                     sep: untyped, nextSep: untyped, isSep: untyped) {.dirty.} =
+  fs.setLen(if n < 1: 16 else: n)
+  var b   = slc.data
+  var eob = b +! slc.size
+  while repeat and eob -! b > 0 and isSep((cast[cstring](b))[0], sep):
+    b = b +! 1
+    if b == eob: fs.setLen(0); return
+  var e = nextSep(b, sep, (eob -! b).csize)
+  while e != nil:
+    if n < 1:                               #Unbounded msplit
+      if result == fs.len - 1:              #Expand capacity
+        fs.setLen(if fs.len < 512: 2*fs.len else: fs.len + 512)
+    elif result == n - 1:                   #Need 1 more slot for final field
+      break
+    fs[result].data = b
+    fs[result].size = e -! b
+    result += 1
+    while repeat and eob -! e > 0 and isSep((cast[cstring](e))[1], sep):
+      e = e +! 1
+    b = e +! 1
+    if eob -! b <= 0:
+      b = eob
+      break
+    e = nextSep(b, sep, (eob -! b).csize)
+  if not repeat or eob -! b > 0:
+    fs[result].data = b
+    fs[result].size = eob -! b
+    result += 1
+  fs.setLen(result)
+
+proc msplit*(s: MemSlice, fs: var seq[MemSlice], sep=' ', n=0,
+             repeat=false): int =
+  defSplit(s, fs, n, repeat, sep, cmemchr, charEq)
+
+proc split*(s: Splitr, line: MemSlice, cols: var seq[MemSlice],
+            n=0) {.inline.} =
+  discard msplit(line, cols, s.chrDlm, n, s.repeat)
+
+########################################################################
+# Using lines instead of memSlices & split instead of splitr.split seems
+# to mask the arc problem, as does simplifying `Table` to `seq[char]`.
+
+proc load(path: string, delim=" "): Table[MemSlice, seq[char]] =
+  let f = memfiles.open(path)
+  let splitr = initSplitr(delim)
+  var cols: seq[MemSlice] = @[ ]    # re-used seq buffer
+  var nwSq = newSeqOfCap[char](1)   # re-used seq value
+  nwSq.setLen 1
+  for line in memSlices(f, eat='\0'):
+    stderr.write "loop 1a\n"
+    splitr.split(line, cols, 2)
+    stderr.write "loop 1b; cols: ", cols, "\n"
+    let cs = cast[cstring](cols[0].data)
+    stderr.write "loop 1c\n"        #..reports exception here, but
+    nwSq[0] = cs[0]                 #..actually doing out of bounds here
+    stderr.write "loop 1d\n"
+    result[cols[1]] = nwSq
+
+discard load(getAppDir() / "testfile.txt")
diff --git a/tests/arc/tamodule.nim b/tests/arc/tamodule.nim
new file mode 100644
index 000000000..1412e0a7c
--- /dev/null
+++ b/tests/arc/tamodule.nim
@@ -0,0 +1,9 @@
+discard """
+  output: '''
+abcde
+0
+'''
+  cmd: "nim c --gc:arc $file"
+"""
+
+import amodule
diff --git a/tests/arc/tarc_macro.nim b/tests/arc/tarc_macro.nim
new file mode 100644
index 000000000..33ade1da4
--- /dev/null
+++ b/tests/arc/tarc_macro.nim
@@ -0,0 +1,57 @@
+import macros
+
+var destroyCalled = false
+macro bar() =
+  let s = newTree(nnkAccQuoted, ident"=destroy")
+  # let s = ident"`=destroy`" # this would not work
+  result = quote do:
+    type Foo = object
+    # proc `=destroy`(a: var Foo) = destroyCalled = true # this would not work
+    proc `s`(a: var Foo) = destroyCalled = true
+    block:
+      let a = Foo()
+bar()
+doAssert destroyCalled
+
+# custom `op`
+var destroyCalled2 = false
+macro bar(ident) =
+  var x = 1.5
+  result = quote("@") do:
+    type Foo = object
+    let `@ident` = 0 # custom op interpolated symbols need quoted (``)
+    proc `=destroy`(a: var Foo) =
+      doAssert @x == 1.5
+      doAssert compiles(@x == 1.5)
+      let b1 = @[1,2]
+      let b2 = @@[1,2]
+      doAssert $b1 == "[1, 2]"
+      doAssert $b2 == "@[1, 2]"
+      destroyCalled2 = true
+    block:
+      let a = Foo()
+bar(someident)
+doAssert destroyCalled2
+
+proc `&%`(x: int): int = 1
+proc `&%`(x, y: int): int = 2
+
+macro bar2() =
+  var x = 3
+  result = quote("&%") do:
+    var y = &%x # quoting operator
+    doAssert &%&%y == 1 # unary operator => need to escape
+    doAssert y &% y == 2 # binary operator => no need to escape
+    doAssert y == 3
+bar2()
+
+block:
+  macro foo(a: openArray[string] = []): string =
+    echo a # Segfault doesn't happen if this is removed
+    newLit ""
+
+  proc bar(a: static[openArray[string]] = []) =
+    const tmp = foo(a)
+
+  # bug #22909
+  doAssert not compiles(bar())
diff --git a/tests/arc/tarc_orc.nim b/tests/arc/tarc_orc.nim
new file mode 100644
index 000000000..f2c7de2fc
--- /dev/null
+++ b/tests/arc/tarc_orc.nim
@@ -0,0 +1,186 @@
+discard """
+  targets: "c cpp"
+  matrix: "--mm:arc; --mm:orc"
+"""
+
+block:
+  type
+    PublicKey = array[32, uint8]
+    PrivateKey = array[64, uint8]
+
+  proc ed25519_create_keypair(publicKey: ptr PublicKey; privateKey: ptr PrivateKey) =
+    publicKey[][0] = uint8(88)
+
+  type
+    KeyPair = object
+      public: PublicKey
+      private: PrivateKey
+
+  proc initKeyPair(): KeyPair =
+    ed25519_create_keypair(result.public.addr, result.private.addr)
+
+  let keys = initKeyPair()
+  doAssert keys.public[0] == 88
+
+
+template minIndexByIt: untyped =
+  var other = 3
+  other
+
+proc bug20303() =
+  var hlibs = @["hello", "world", "how", "are", "you"]
+  let res = hlibs[minIndexByIt()]
+  doAssert res == "are"
+
+bug20303()
+
+proc main() = # todo bug with templates
+  block: # bug #11267
+    var a: seq[char] = block: @[]
+    doAssert a == @[]
+    # 2
+    proc b: seq[string] =
+      discard
+      @[]
+    doAssert b() == @[]
+static: main()
+main()
+
+
+type Obj = tuple
+  value: int
+  arr: seq[int]
+
+proc bug(): seq[Obj] =
+  result.add (value: 0, arr: @[])
+  result[^1].value = 1
+  result[^1].arr.add 1
+
+# bug #19990
+let s = bug()
+doAssert s[0] == (value: 1, arr: @[1])
+
+block: # bug #21974
+  type Test[T] = ref object
+      values : seq[T]
+      counter: int
+
+  proc newTest[T](): Test[T] =
+      result         = new(Test[T])
+      result.values  = newSeq[T](16)
+      result.counter = 0
+
+  proc push[T](self: Test[T], value: T) =
+      self.counter += 1
+      if self.counter >= self.values.len:
+          self.values.setLen(self.values.len * 2)
+      self.values[self.counter - 1] = value
+
+  proc pop[T](self: Test[T]): T =
+      result         = self.values[0]
+      self.values[0] = self.values[self.counter - 1] # <--- This line
+      self.counter  -= 1
+
+
+  type X = tuple
+      priority: int
+      value   : string
+
+  var a = newTest[X]()
+  a.push((1, "One"))
+  doAssert a.pop.value == "One"
+
+# bug #21987
+
+type
+  EmbeddedImage* = distinct Image
+  Image = object
+    len: int
+
+proc imageCopy*(image: Image): Image {.nodestroy.}
+
+proc `=destroy`*(x: var Image) =
+  discard
+proc `=sink`*(dest: var Image; source: Image) =
+  `=destroy`(dest)
+  wasMoved(dest)
+
+proc `=dup`*(source: Image): Image {.nodestroy.} =
+  result = imageCopy(source)
+
+proc `=copy`*(dest: var Image; source: Image) =
+  dest = imageCopy(source) # calls =sink implicitly
+
+proc `=destroy`*(x: var EmbeddedImage) = discard
+
+proc `=dup`*(source: EmbeddedImage): EmbeddedImage {.nodestroy.} = source
+
+proc `=copy`*(dest: var EmbeddedImage; source: EmbeddedImage) {.nodestroy.} =
+  dest = source
+
+proc imageCopy*(image: Image): Image =
+  result = image
+
+proc main2 =
+  block:
+    var a = Image(len: 2).EmbeddedImage
+    var b = Image(len: 1).EmbeddedImage
+    b = a
+    doAssert Image(a).len == 2
+    doAssert Image(b).len == 2
+
+  block:
+    var a = Image(len: 2)
+    var b = Image(len: 1)
+    b = a
+    doAssert a.len == 2
+    doAssert b.len == 0
+
+main2()
+
+block:
+  type
+    TestObj = object of RootObj
+      name: string
+    
+    TestSubObj = object of TestObj
+      objname: string
+
+  proc `=destroy`(x: TestObj) =
+    `=destroy`(x.name)
+
+  proc `=destroy`(x: TestSubObj) =
+    `=destroy`(x.objname)
+    `=destroy`(TestObj(x))
+
+  proc testCase() =
+    let t1 {.used.} = TestSubObj(objname: "tso1", name: "to1")
+
+  proc main() =
+    testCase()
+
+  main()
+
+block: # bug #23858
+  type Object = object
+    a: int
+    b: ref int
+  var x = 0
+  proc fn(): auto {.cdecl.} =
+    inc x
+    return Object()
+  discard fn()
+  doAssert x == 1
+
+block: # bug #24147
+  type
+    O = object of RootObj
+      val: string
+    OO = object of O
+
+  proc `=copy`(dest: var O, src: O) =
+      dest.val = src.val
+
+  let oo = OO(val: "hello world")
+  var ooCopy : OO
+  `=copy`(ooCopy, oo)
diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim
new file mode 100644
index 000000000..b4476ef4f
--- /dev/null
+++ b/tests/arc/tarcmisc.nim
@@ -0,0 +1,836 @@
+discard """
+  output: '''
+Destructor for TestTestObj
+=destroy called
+123xyzabc
+destroyed: false
+destroyed: false
+destroyed2: false
+destroyed2: false
+destroying variable: 2
+destroying variable: 1
+whiley ends :(
+1
+(x: "0")
+(x: "1")
+(x: "2")
+(x: "3")
+(x: "4")
+(x: "5")
+(x: "6")
+(x: "7")
+(x: "8")
+(x: "9")
+(x: "10")
+0
+new line before - @['a']
+new line after - @['a']
+finalizer
+aaaaa
+hello
+true
+copying
+123
+42
+@["", "d", ""]
+ok
+destroying variable: 20
+destroying variable: 10
+closed
+'''
+  cmd: "nim c --mm:arc --deepcopy:on -d:nimAllocPagesViaMalloc $file"
+"""
+
+block: # bug #23627
+  type
+    TestObj = object of RootObj
+
+    Test2 = object of RootObj
+      foo: TestObj
+
+    TestTestObj = object of RootObj
+      shit: TestObj
+
+  proc `=destroy`(x: TestTestObj) =
+    echo "Destructor for TestTestObj"
+    let test = Test2(foo: TestObj())
+
+  proc testCaseT() =
+    let tt1 {.used.} = TestTestObj(shit: TestObj())
+
+
+  proc main() =
+    testCaseT()
+
+  main()
+
+
+# bug #9401
+
+type
+  MyObj = object
+    len: int
+    data: ptr UncheckedArray[float]
+
+proc `=destroy`*(m: MyObj) =
+
+  echo "=destroy called"
+
+  if m.data != nil:
+    deallocShared(m.data)
+
+type
+  MyObjDistinct = distinct MyObj
+
+proc `=copy`*(m: var MyObj, m2: MyObj) =
+  if m.data == m2.data: return
+  if m.data != nil:
+    `=destroy`(m)
+  m.len = m2.len
+  if m.len > 0:
+    m.data = cast[ptr UncheckedArray[float]](allocShared(sizeof(float) * m.len))
+    copyMem(m.data, m2.data, sizeof(float) * m.len)
+
+
+proc `=sink`*(m: var MyObj, m2: MyObj) =
+  if m.data != m2.data:
+    if m.data != nil:
+      `=destroy`(m)
+    m.len = m2.len
+    m.data = m2.data
+
+proc newMyObj(len: int): MyObj =
+  result.len = len
+  result.data = cast[ptr UncheckedArray[float]](allocShared(sizeof(float) * len))
+
+proc newMyObjDistinct(len: int): MyObjDistinct =
+  MyObjDistinct(newMyObj(len))
+
+proc fooDistinct =
+  doAssert newMyObjDistinct(2).MyObj.len == 2
+
+fooDistinct()
+
+
+proc takeSink(x: sink string): bool = true
+
+proc b(x: sink string): string =
+  if takeSink(x):
+    return x & "abc"
+
+proc bbb(inp: string) =
+  let y = inp & "xyz"
+  echo b(y)
+
+bbb("123")
+
+
+# bug #13691
+type Variable = ref object
+  value: int
+
+proc `=destroy`(self: typeof(Variable()[])) =
+  echo "destroying variable: ",self.value
+
+proc newVariable(value: int): Variable =
+  result = Variable()
+  result.value = value
+  #echo "creating variable: ",result.value
+
+proc test(count: int) =
+  var v {.global.} = newVariable(10)
+
+  var count = count - 1
+  if count == 0: return
+
+  test(count)
+  echo "destroyed: ", v.isNil
+
+test(3)
+
+proc test2(count: int) =
+  block: #XXX: Fails with block currently
+    var v {.global.} = newVariable(20)
+
+    var count = count - 1
+    if count == 0: return
+
+    test2(count)
+    echo "destroyed2: ", v.isNil
+
+test2(3)
+
+proc whiley =
+  var a = newVariable(1)
+  while true:
+    var b = newVariable(2)
+    if true: raise newException(CatchableError, "test")
+
+try:
+  whiley()
+except CatchableError:
+  echo "whiley ends :("
+
+#------------------------------------------------------------------------------
+# issue #13810
+
+import streams
+
+type
+  A = ref AObj
+  AObj = object of RootObj
+    io: Stream
+  B = ref object of A
+    x: int
+
+proc `=destroy`(x: AObj) =
+  close(x.io)
+  echo "closed"
+
+var x = B(io: newStringStream("thestream"))
+
+
+#------------------------------------------------------------------------------
+# issue #14003
+
+proc cryptCTR*(nonce: var openArray[char]) =
+  nonce[1] = 'A'
+
+proc main() =
+  var nonce1 = "0123456701234567"
+  cryptCTR(nonce1)
+  doAssert(nonce1 == "0A23456701234567")
+  var nonce2 = "01234567"
+  cryptCTR(nonce2.toOpenArray(0, nonce2.len-1))
+  doAssert(nonce2 == "0A234567")
+
+main()
+
+# bug #14079
+import std/algorithm
+
+let
+  n = @["c", "b"]
+  q = @[("c", "2"), ("b", "1")]
+
+doAssert n.sortedByIt(it) == @["b", "c"], "fine"
+doAssert q.sortedByIt(it[0]) == @[("b", "1"), ("c", "2")], "fails under arc"
+
+
+#------------------------------------------------------------------------------
+# issue #14236
+
+type
+  MyType = object
+    a: seq[int]
+
+proc re(x: static[string]): static MyType =
+  MyType()
+
+proc match(inp: string, rg: static MyType) =
+  doAssert rg.a.len == 0
+
+match("ac", re"a(b|c)")
+
+#------------------------------------------------------------------------------
+# issue #14243
+
+type
+  Game* = ref object
+
+proc free*(game: Game) =
+  let a = 5
+
+proc newGame*(): Game =
+  new(result, free)
+
+var game*: Game
+
+
+#------------------------------------------------------------------------------
+# issue #14333
+
+type
+  SimpleLoop = object
+
+  Lsg = object
+    loops: seq[ref SimpleLoop]
+    root: ref SimpleLoop
+
+var lsg: Lsg
+lsg.loops.add lsg.root
+echo lsg.loops.len
+
+# bug #14495
+type
+  Gah = ref object
+    x: string
+
+proc bug14495 =
+  var owners: seq[Gah]
+  for i in 0..10:
+    owners.add Gah(x: $i)
+
+  var x: seq[Gah]
+  for i in 0..10:
+    x.add owners[i]
+
+  for i in 0..100:
+    setLen(x, 0)
+    setLen(x, 10)
+
+  for i in 0..x.len-1:
+    if x[i] != nil:
+      echo x[i][]
+
+  for o in owners:
+    echo o[]
+
+bug14495()
+
+# bug #14396
+type
+  Spinny = ref object
+    t: ref int
+    text: string
+
+proc newSpinny*(): Spinny =
+  Spinny(t: new(int), text: "hello")
+
+proc spinnyLoop(x: ref int, spinny: sink Spinny) =
+  echo x[]
+
+proc start*(spinny: sink Spinny) =
+  spinnyLoop(spinny.t, spinny)
+
+var spinner1 = newSpinny()
+spinner1.start()
+
+# bug #14345
+
+type
+  SimpleLoopB = ref object
+    children: seq[SimpleLoopB]
+    parent: SimpleLoopB
+
+proc addChildLoop(self: SimpleLoopB, loop: SimpleLoopB) =
+  self.children.add loop
+
+proc setParent(self: SimpleLoopB, parent: SimpleLoopB) =
+  self.parent = parent
+  self.parent.addChildLoop(self)
+
+var l = SimpleLoopB()
+l.setParent(l)
+
+
+# bug #14968
+import times
+let currentTime = now().utc
+
+
+# bug #14994
+import sequtils
+var newLine = @['a']
+let indent = newSeq[char]()
+
+echo "new line before - ", newline
+
+newline.insert(indent, 0)
+
+echo "new line after - ", newline
+
+# bug #15044
+
+type
+  Test = ref object
+
+proc test: Test =
+  # broken
+  new(result, proc(x: Test) =
+    echo "finalizer"
+  )
+
+proc tdirectFinalizer =
+  discard test()
+
+tdirectFinalizer()
+
+
+# bug #14480
+proc hello(): int =
+  result = 42
+
+var leaves {.global.} = hello()
+doAssert leaves == 42
+
+# bug #15052
+
+proc mutstrings =
+  var data = "hello"
+  for c in data.mitems():
+    c = 'a'
+  echo data
+
+mutstrings()
+
+# bug #15038
+
+type
+  Machine = ref object
+    hello: string
+
+var machineTypes: seq[tuple[factory: proc(): Machine]]
+
+proc registerMachine(factory: proc(): Machine) =
+  var mCreator = proc(): Machine =
+    result = factory()
+
+  machineTypes.add((factory: mCreator))
+
+proc facproc(): Machine =
+  result = Machine(hello: "hello")
+
+registerMachine(facproc)
+
+proc createMachine =
+  for machine in machineTypes:
+    echo machine.factory().hello
+
+createMachine()
+
+# bug #15122
+
+import tables
+
+type
+  BENodeKind = enum
+    tkBytes,
+    tkList,
+    tkDict
+
+  BENode = object
+    case kind: BENodeKind
+    of tkBytes: strVal: string
+    of tkList: listVal: seq[BENode]
+    of tkDict: dictVal: Table[string, BENode]
+
+var data = {
+  "examples": {
+    "values": BENode(
+      kind: tkList,
+      listVal: @[BENode(kind: tkBytes, strVal: "test")]
+    )
+  }.toTable()
+}.toTable()
+
+# For ARC listVal is empty for some reason
+doAssert data["examples"]["values"].listVal[0].strVal == "test"
+
+
+
+
+###############################################################################
+# bug #15405
+import parsexml
+const test_xml_str = "<A><B>value</B></A>"
+var stream = newStringStream(test_xml_str)
+var xml: XmlParser
+open(xml, stream, "test")
+var xml2 = deepCopy(xml)
+
+proc text_parser(xml: var XmlParser) =
+  var test_passed = false
+  while true:
+    xml.next()
+    case xml.kind
+    of xmlElementStart:
+      if xml.elementName == "B":
+        xml.next()
+        if xml.kind == xmlCharData and xml.charData == "value":
+          test_passed = true
+
+    of xmlEof: break
+    else: discard
+  xml.close()
+  doAssert(test_passed)
+
+text_parser(xml)
+text_parser(xml2)
+
+# bug #15599
+type
+  PixelBuffer = ref object
+
+proc newPixelBuffer(): PixelBuffer =
+  new(result) do (buffer: PixelBuffer):
+    echo "ok"
+
+discard newPixelBuffer()
+
+
+# bug #17199
+
+proc passSeq(data: seq[string]) =
+  # used the system.& proc initially
+  let wat = data & "hello"
+
+proc test2 =
+  let name = @["hello", "world"]
+  passSeq(name)
+  doAssert name == @["hello", "world"]
+
+static: test2() # was buggy
+test2()
+
+proc merge(x: sink seq[string], y: sink string): seq[string] =
+  newSeq(result, x.len + 1)
+  for i in 0..x.len-1:
+    result[i] = move(x[i])
+  result[x.len] = move(y)
+
+proc passSeq2(data: seq[string]) =
+  # used the system.& proc initially
+  let wat = merge(data, "hello")
+
+proc test3 =
+  let name = @["hello", "world"]
+  passSeq2(name)
+  doAssert name == @["hello", "world"]
+
+static: test3() # was buggy
+test3()
+
+# bug #17712
+proc t17712 =
+  var ppv = new int
+  discard @[ppv]
+  var el: ref int
+  el = [ppv][0]
+  echo el != nil
+
+t17712()
+
+# bug #18030
+
+type
+  Foo = object
+    n: int
+
+proc `=copy`(dst: var Foo, src: Foo) =
+  echo "copying"
+  dst.n = src.n
+
+proc `=sink`(dst: var Foo, src: Foo) =
+  echo "sinking"
+  dst.n = src.n
+
+var a: Foo
+
+proc putValue[T](n: T)
+
+proc useForward =
+  putValue(123)
+
+proc putValue[T](n: T) =
+  var b = Foo(n:n)
+  a = b
+  echo b.n
+
+useForward()
+
+
+# bug #17319
+type
+  BrokenObject = ref object
+    brokenType: seq[int]
+
+proc use(obj: BrokenObject) =
+  discard
+
+method testMethod(self: BrokenObject) {.base.} =
+  iterator testMethodIter() {.closure.} =
+    use(self)
+
+  var nameIterVar = testMethodIter
+  nameIterVar()
+
+let mikasa = BrokenObject()
+mikasa.testMethod()
+
+# bug #19205
+type
+  InputSectionBase* = object of RootObj
+    relocations*: seq[int]   # traced reference. string has a similar SIGSEGV.
+  InputSection* = object of InputSectionBase
+
+proc fooz(sec: var InputSectionBase) =
+  if sec of InputSection:  # this line SIGSEGV.
+    echo 42
+
+var sec = create(InputSection)
+sec[] = InputSection(relocations: newSeq[int]())
+fooz sec[]
+
+block:
+  type
+    Data = ref object
+      id: int
+  proc main =
+    var x = Data(id: 99)
+    var y = x
+    x[] = Data(id: 778)[]
+    doAssert y.id == 778
+    doAssert x[].id == 778
+  main()
+
+block: # bug #19857
+  type
+    ValueKind = enum VNull, VFloat, VObject # need 3 elements. Cannot remove VNull or VObject
+
+    Value = object
+      case kind: ValueKind
+      of VFloat: fnum: float
+      of VObject: tab: Table[int, int] # OrderedTable[T, U] also makes it fail.
+                                      # "simpler" types also work though
+      else: discard # VNull can be like this, but VObject must be filled
+
+    # required. Pure proc works
+    FormulaNode = proc(c: OrderedTable[string, int]): Value
+
+  proc toF(v: Value): float =
+    doAssert v.kind == VFloat
+    case v.kind
+    of VFloat: result = v.fnum
+    else: discard
+
+
+  proc foo() =
+    let fuck = initOrderedTable[string, int]()
+    proc cb(fuck: OrderedTable[string, int]): Value =
+                            # works:
+                            #result = Value(kind: VFloat, fnum: fuck["field_that_does_not_exist"].float)
+                            # broken:
+      discard "actuall runs!"
+      let t = fuck["field_that_does_not_exist"]
+      echo "never runs, but we crash after! ", t
+
+    doAssertRaises(KeyError):
+      let fn = FormulaNode(cb)
+      let v = fn(fuck)
+      #echo v
+      let res = v.toF()
+
+  foo()
+
+import std/options
+
+# bug #21592
+type Event* = object
+  code*: string
+
+type App* = ref object of RootObj
+  id*: string
+
+method process*(self: App): Option[Event] {.base.} =
+  raise Exception.new_exception("not impl")
+
+# bug #21617
+type Test2 = ref object of RootObj
+
+method bug(t: Test2): seq[float] {.base.} = discard
+
+block: # bug #22664
+  type
+    ElementKind = enum String, Number
+    Element = object
+      case kind: ElementKind
+      of String:
+        str: string
+      of Number:
+        num: float
+    Calc = ref object
+      stack: seq[Element]
+
+  var calc = new Calc
+
+  calc.stack.add Element(kind: Number, num: 200.0)
+  doAssert $calc.stack == "@[(kind: Number, num: 200.0)]"
+  let calc2 = calc
+  calc2.stack = calc.stack # This nulls out the object in the stack
+  doAssert $calc.stack == "@[(kind: Number, num: 200.0)]"
+  doAssert $calc2.stack == "@[(kind: Number, num: 200.0)]"
+
+block: # bug #19250
+  type
+    Bar[T] = object
+      err: proc(): string
+
+    Foo[T] = object
+      run: proc(): Bar[T]
+
+  proc bar[T](err: proc(): string): Bar[T] =
+    assert not err.isNil
+    Bar[T](err: err)
+
+  proc foo(): Foo[char] = 
+    result.run = proc(): Bar[char] =
+      # works
+      # result = Bar[char](err: proc(): string = "x")
+      # not work
+      result = bar[char](proc(): string = "x")
+
+  proc bug[T](fs: Foo[T]): Foo[T] =
+    result.run = proc(): Bar[T] =
+      let res = fs.run()
+      
+      # works
+      # var errors = @[res.err] 
+      
+      # not work
+      var errors: seq[proc(): string]
+      errors.add res.err
+      
+      return bar[T] do () -> string:
+        for err in errors:
+          result.add res.err()
+
+  doAssert bug(foo()).run().err() == "x"
+
+block: # bug #22259
+  type
+    ProcWrapper = tuple
+      p: proc() {.closure.}
+
+
+  proc f(wrapper: ProcWrapper) =
+    let s = @[wrapper.p]
+    let a = [wrapper.p]
+
+  proc main =
+    # let wrapper: ProcWrapper = ProcWrapper(p: proc {.closure.} = echo 10)
+    let wrapper: ProcWrapper = (p: proc {.closure.} = echo 10)
+    f(wrapper)
+
+  main()
+
+block:
+  block: # bug #22923
+    block:
+      let
+        a: int = 100
+        b: int32 = 200'i32
+
+      let
+        x = arrayWith(a, 8) # compiles
+        y = arrayWith(b, 8) # internal error
+        z = arrayWith(14, 8) # integer literal also results in a crash
+
+      doAssert x == [100, 100, 100, 100, 100, 100, 100, 100]
+      doAssert $y == "[200, 200, 200, 200, 200, 200, 200, 200]"
+      doAssert z == [14, 14, 14, 14, 14, 14, 14, 14]
+
+    block:
+      let a: string = "nim"
+      doAssert arrayWith(a, 3) == ["nim", "nim", "nim"]
+
+      let b: char = 'c'
+      doAssert arrayWith(b, 3) == ['c', 'c', 'c']
+
+      let c: uint = 300'u
+      doAssert $arrayWith(c, 3) == "[300, 300, 300]"
+
+block: # bug #23505
+  type
+    K = object
+    C = object
+      value: ptr K
+
+  proc init(T: type C): C =
+    let tmp = new K
+    C(value: addr tmp[])
+
+  discard init(C)
+
+block: # bug #23524
+  type MyType = object
+    a: int
+
+  proc `=destroy`(typ: MyType) = discard
+
+  var t1 = MyType(a: 100)
+  var t2 = t1 # Should be a copy?
+
+  proc main() =
+    t2 = t1
+    doAssert t1.a == 100
+    doAssert t2.a == 100
+
+  main()
+
+block: # bug #23907
+  type
+    Thingy = object
+      value: int
+
+    ExecProc[C] = proc(value: sink C): int {.nimcall.}
+
+  proc `=copy`(a: var Thingy, b: Thingy) {.error.}
+
+  var thingyDestroyCount = 0
+
+  proc `=destroy`(thingy: Thingy) =
+    assert(thingyDestroyCount <= 0)
+    thingyDestroyCount += 1
+
+  proc store(value: sink Thingy): int =
+    result = value.value
+
+  let callback: ExecProc[Thingy] = store
+
+  doAssert callback(Thingy(value: 123)) == 123
+
+import std/strutils
+
+block: # bug #23974
+  func g(e: seq[string]): lent seq[string] = result = e
+  proc k(f: string): seq[string] = f.split("/")
+  proc n() =
+    const r = "/d/"
+    let t =
+      if true:
+        k(r).g()
+      else:
+        k("/" & r).g()
+    echo t
+
+  n()
+
+block: # bug #23973
+  func g(e: seq[string]): lent seq[string] = result = e
+  proc k(f: string): seq[string] = f.split("/")
+  proc n() =
+    const r = "/test/empty"  # or "/test/empty/1"
+    let a = k(r).g()
+    let t =
+      if true:
+        k(r).g()
+      else:
+        k("/" & r).g()   # or raiseAssert ""
+    doAssert t == a
+
+  n()
+
+block: # bug #24141
+  func reverse(s: var openArray[char]) =
+    s[0] = 'f'
+
+  func rev(s: var string) =
+    s.reverse
+
+  proc main =
+    var abc = "abc"
+    abc.rev
+    doAssert abc == "fbc"
+
+  main()
diff --git a/tests/arc/tasyncawait.nim b/tests/arc/tasyncawait.nim
new file mode 100644
index 000000000..91a7453cd
--- /dev/null
+++ b/tests/arc/tasyncawait.nim
@@ -0,0 +1,68 @@
+discard """
+  outputsub: "result: 5000"
+  cmd: "nim c --gc:orc $file"
+"""
+
+import asyncdispatch, asyncnet, nativesockets, net, strutils
+from stdtest/netutils import bindAvailablePort
+
+var msgCount = 0
+
+const
+  swarmSize = 50
+  messagesToSend = 100
+
+var clientCount = 0
+
+proc sendMessages(client: AsyncFD) {.async.} =
+  for i in 0 ..< messagesToSend:
+    await send(client, "Message " & $i & "\c\L")
+
+proc launchSwarm(port: Port) {.async.} =
+  for i in 0 ..< swarmSize:
+    var sock = createAsyncNativeSocket()
+
+    await connect(sock, "localhost", port)
+    await sendMessages(sock)
+    closeSocket(sock)
+
+proc readMessages(client: AsyncFD) {.async.} =
+  # wrapping the AsyncFd into a AsyncSocket object
+  var sockObj = newAsyncSocket(client)
+  var (ipaddr, port) = sockObj.getPeerAddr()
+  doAssert ipaddr == "127.0.0.1"
+  (ipaddr, port) = sockObj.getLocalAddr()
+  doAssert ipaddr == "127.0.0.1"
+  while true:
+    var line = await recvLine(sockObj)
+    if line == "":
+      closeSocket(client)
+      clientCount.inc
+      break
+    else:
+      if line.startsWith("Message "):
+        msgCount.inc
+      else:
+        doAssert false
+
+proc createServer(server: AsyncFD) {.async.} =
+  discard server.SocketHandle.listen()
+  while true:
+    asyncCheck readMessages(await accept(server))
+
+proc main =
+  let server = createAsyncNativeSocket()
+  let port = bindAvailablePort(server.SocketHandle)
+  asyncCheck createServer(server)
+  asyncCheck launchSwarm(port)
+  while true:
+    poll()
+    if clientCount == swarmSize: break
+
+let mem = getOccupiedMem()
+main()
+
+doAssert msgCount == swarmSize * messagesToSend
+echo "result: ", msgCount
+GC_fullCollect()
+echo "memory: ", formatSize(getOccupiedMem() - mem)
diff --git a/tests/arc/tasyncleak.nim b/tests/arc/tasyncleak.nim
new file mode 100644
index 000000000..8e3a7b3e7
--- /dev/null
+++ b/tests/arc/tasyncleak.nim
@@ -0,0 +1,21 @@
+discard """
+  outputsub: "(allocCount: 4050, deallocCount: 4048)"
+  cmd: "nim c --gc:orc -d:nimAllocStats $file"
+"""
+
+import asyncdispatch
+# bug #15076
+const
+  # Just to occupy some RAM
+  BigData = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+
+proc doNothing(): Future[void] {.async.} =
+  discard
+
+proc main(): Future[void] {.async.} =
+  for x in 0 .. 1_000:
+    await doNothing()
+
+waitFor main()
+GC_fullCollect()
+echo getAllocStats()
diff --git a/tests/arc/tasyncleak2.nim b/tests/arc/tasyncleak2.nim
new file mode 100644
index 000000000..87d7e73f9
--- /dev/null
+++ b/tests/arc/tasyncleak2.nim
@@ -0,0 +1,88 @@
+discard """
+  output: "success"
+  cmd: "nim c --gc:orc $file"
+"""
+
+# issue #15076
+import deques, strutils, asyncdispatch
+
+proc doNothing(): Future[void] =
+  #[
+  var
+    :env
+    :env_1
+  try:
+    `=destroy`(:env)
+    internalNew(:env)
+    `=sink`(:env.retFuture1, newFuture("doNothing"))
+
+    `=destroy_1`(:env_1)
+    internalNew(:env_1)
+    `=`(:env_1.:up, :env)
+    `=sink_1`(:env.nameIterVar2, (doNothingIter, :env_1))
+
+    (doNothingNimAsyncContinue, :env)()
+    return `=_1`(result, :env.retFuture1)
+  finally:
+    `=destroy`(:env)
+  ]#
+
+  var retFuture = newFuture[void]("doNothing")
+  iterator doNothingIter(): FutureBase {.closure.} =
+    # inspected ARC code: looks correct!
+    block:
+      var qqq = initDeque[string]()
+      for i in 0 .. 1000:
+        qqq.addLast($i)
+    complete(retFuture) # env.up.retFuture1
+
+  var nameIterVar = doNothingIter  # iter_Env -> retFuture ->
+
+  proc doNothingNimAsyncContinue() {.closure.} =
+    # inspected ARC code: looks correct
+    if not nameIterVar.finished:
+      var next_gensym0 = nameIterVar()
+      while (not next_gensym0.isNil) and next_gensym0.finished:
+        next_gensym0 = nameIterVar()
+        if nameIterVar.finished:
+          break
+      if next_gensym0 != nil:
+        {.gcsafe.}:
+          next_gensym0.addCallback cast[proc () {.closure, gcsafe.}](doNothingNimAsyncContinue)
+
+  doNothingNimAsyncContinue()
+  return retFuture
+
+proc main(): Future[void] =
+  template await[T](f_gensym12: Future[T]): auto {.used.} =
+    var internalTmpFuture_gensym12: FutureBase = f_gensym12
+    yield internalTmpFuture_gensym12
+    (cast[typeof(f_gensym12)](internalTmpFuture_gensym12)).read()
+
+  var retFuture = newFuture[void]("main")
+  iterator mainIter(): FutureBase {.closure.} =
+    block:
+      for x in 0 .. 1000:
+        await doNothing()
+    complete(retFuture)
+
+  var nameIterVar_gensym11 = mainIter
+  proc mainNimAsyncContinue() {.closure.} =
+    if not nameIterVar_gensym11.finished:
+      var next_gensym11 = unown nameIterVar_gensym11()
+      while (not next_gensym11.isNil) and next_gensym11.finished:
+        next_gensym11 = unown nameIterVar_gensym11()
+        if nameIterVar_gensym11.finished:
+          break
+      if next_gensym11 != nil:
+        {.gcsafe.}:
+          next_gensym11.addCallback cast[proc () {.closure, gcsafe.}](mainNimAsyncContinue)
+
+  mainNimAsyncContinue()
+  return retFuture
+
+for i in 0..9:
+  waitFor main()
+  GC_fullCollect()
+  doAssert getOccupiedMem() < 1024
+echo "success"
diff --git a/tests/arc/tasyncleak3.nim b/tests/arc/tasyncleak3.nim
new file mode 100644
index 000000000..21e963b7f
--- /dev/null
+++ b/tests/arc/tasyncleak3.nim
@@ -0,0 +1,47 @@
+discard """
+  cmd: "nim c --gc:orc -d:useMalloc $file"
+  output: "true"
+  valgrind: "true"
+"""
+
+import strutils
+
+type
+  FutureBase* = ref object of RootObj  ## Untyped future.
+    finished: bool
+    stackTrace: seq[StackTraceEntry] ## For debugging purposes only.
+
+proc newFuture*(): FutureBase =
+  new(result)
+  result.finished = false
+  result.stackTrace = getStackTraceEntries()
+
+type
+  PDispatcher {.acyclic.} = ref object
+
+var gDisp: PDispatcher
+new gDisp
+
+proc testCompletion(): FutureBase =
+  var retFuture = newFuture()
+
+  iterator testCompletionIter(): FutureBase {.closure.} =
+    retFuture.finished = true
+    when not defined(nobug):
+      let disp = gDisp # even worse memory consumption this way...
+
+  var nameIterVar = testCompletionIter
+  proc testCompletionNimAsyncContinue() {.closure.} =
+    if not nameIterVar.finished:
+      discard nameIterVar()
+  testCompletionNimAsyncContinue()
+  return retFuture
+
+proc main =
+  for i in 0..10_000:
+    discard testCompletion()
+
+main()
+
+GC_fullCollect()
+echo getOccupiedMem() < 1024
diff --git a/tests/arc/tasyncleak4.nim b/tests/arc/tasyncleak4.nim
new file mode 100644
index 000000000..58cd7f0b7
--- /dev/null
+++ b/tests/arc/tasyncleak4.nim
@@ -0,0 +1,21 @@
+discard """
+  cmd: "nim c --gc:orc -d:useMalloc $file"
+  output: '''ok'''
+  valgrind: "leaks"
+"""
+
+# bug #15076
+import asyncdispatch
+
+var futures: seq[Future[void]]
+
+for i in 1..20:
+  futures.add sleepAsync 1
+  futures.add sleepAsync 1
+
+  futures.all.waitFor()
+  futures.setLen 0
+
+setGlobalDispatcher nil
+GC_fullCollect()
+echo "ok"
diff --git a/tests/arc/tasyncorc.nim b/tests/arc/tasyncorc.nim
new file mode 100644
index 000000000..63703b559
--- /dev/null
+++ b/tests/arc/tasyncorc.nim
@@ -0,0 +1,26 @@
+discard """
+  output: '''230000'''
+  cmd: '''nim c --gc:orc -d:useMalloc $file'''
+  valgrind: "leaks"
+"""
+
+# bug #14402
+
+import asynchttpserver, asyncdispatch, httpclient, strutils
+
+proc cb(req: Request) {.async, gcsafe.} =
+  const html = " ".repeat(230000)
+  await req.respond(Http200, html)
+
+var server = newAsyncHttpServer()
+asyncCheck server.serve(Port(8080), cb)
+
+proc test {.async.} =
+  var
+    client = newAsyncHttpClient()
+    resp = await client.get("http://localhost:8080")
+
+  let x = (await resp.body).len
+  echo x # crash
+
+waitFor test()
diff --git a/tests/arc/tcaseobj.nim b/tests/arc/tcaseobj.nim
new file mode 100644
index 000000000..3499f5c1e
--- /dev/null
+++ b/tests/arc/tcaseobj.nim
@@ -0,0 +1,366 @@
+discard """
+  valgrind: true
+  cmd: "nim c --gc:arc -d:useMalloc $file"
+  output: '''myobj destroyed
+myobj destroyed
+myobj destroyed
+A
+B
+begin
+end
+prevented
+(ok: true, value: "ok")
+@[(kind: P, pChildren: @[])]
+myobj destroyed
+'''
+"""
+
+# bug #13102
+
+type
+  D = ref object
+  R = object
+    case o: bool
+    of false:
+      discard
+    of true:
+      field: D
+
+iterator things(): R =
+  when true:
+    var
+      unit = D()
+    while true:
+      yield R(o: true, field: unit)
+  else:
+    while true:
+      var
+        unit = D()
+      yield R(o: true, field: unit)
+
+proc main =
+  var i = 0
+  for item in things():
+    discard item.field
+    inc i
+    if i == 2: break
+
+main()
+
+# bug #13149
+
+type
+  TMyObj = object
+    p: pointer
+    len: int
+
+proc `=destroy`(o: var TMyObj) =
+  if o.p != nil:
+    dealloc o.p
+    o.p = nil
+    echo "myobj destroyed"
+
+proc `=copy`(dst: var TMyObj, src: TMyObj) =
+  `=destroy`(dst)
+  dst.p = alloc(src.len)
+  dst.len = src.len
+
+proc `=sink`(dst: var TMyObj, src: TMyObj) =
+  `=destroy`(dst)
+  dst.p = src.p
+  dst.len = src.len
+
+type
+  TObjKind = enum Z, A, B
+  TCaseObj = object
+    case kind: TObjKind
+    of Z: discard
+    of A:
+      x1: int # this int plays important role
+      x2: TMyObj
+    of B:
+      y: TMyObj
+
+proc testSinks: TCaseObj =
+  result = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5)))
+  result = TCaseObj(kind: B, y: TMyObj(len: 3, p: alloc(3)))
+
+proc use(x: TCaseObj) = discard
+
+proc testCopies(i: int) =
+  var a: array[2, TCaseObj]
+  a[i] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5)))
+  a[i+1] = a[i] # copy, cannot move
+  use(a[i])
+
+let x1 = testSinks()
+testCopies(0)
+
+# bug #12957
+
+type
+  PegKind* = enum
+    pkCharChoice,
+    pkSequence
+  Peg* = object ## type that represents a PEG
+    case kind: PegKind
+    of pkCharChoice: charChoice: ref set[char]
+    else: discard
+    sons: seq[Peg]
+
+proc charSet*(s: set[char]): Peg =
+  ## constructs a PEG from a character set `s`
+  result = Peg(kind: pkCharChoice)
+  new(result.charChoice)
+  result.charChoice[] = s
+
+proc len(a: Peg): int {.inline.} = return a.sons.len
+proc myadd(d: var Peg, s: Peg) {.inline.} = add(d.sons, s)
+
+proc sequence*(a: openArray[Peg]): Peg =
+  result = Peg(kind: pkSequence, sons: @[])
+  when false:
+    #works too:
+    result.myadd(a[0])
+    result.myadd(a[1])
+  for x in items(a):
+    # works:
+    #result.sons.add(x)
+    # fails:
+    result.myadd x
+  if result.len == 1:
+    result = result.sons[0] # this must not move!
+
+when true:
+  # bug #12957
+
+  proc p =
+    echo "A"
+    let x = sequence([charSet({'a'..'z', 'A'..'Z', '_'}),
+              charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'})])
+    echo "B"
+  p()
+
+  proc testSubObjAssignment =
+    echo "begin"
+    # There must be extactly one element in the array constructor!
+    let x = sequence([charSet({'a'..'z', 'A'..'Z', '_'})])
+    echo "end"
+  testSubObjAssignment()
+
+
+#------------------------------------------------
+
+type
+  MyObject = object
+    x1: string
+    case kind1: bool
+      of false: y1: string
+      of true:
+          y2: seq[string]
+          case kind2: bool
+              of true: z1: string
+              of false:
+                z2: seq[string]
+                flag: bool
+    x2: string
+
+proc test_myobject =
+  var x: MyObject
+  x.x1 = "x1"
+  x.x2 = "x2"
+  x.y1 = "ljhkjhkjh"
+  {.cast(uncheckedAssign).}:
+    x.kind1 = true
+  x.y2 = @["1", "2"]
+  {.cast(uncheckedAssign).}:
+    x.kind2 = true
+  x.z1 = "yes"
+  {.cast(uncheckedAssign).}:
+    x.kind2 = false
+  x.z2 = @["1", "2"]
+  {.cast(uncheckedAssign).}:
+    x.kind2 = true
+  x.z1 = "yes"
+  x.kind2 = true # should be no effect
+  doAssert(x.z1 == "yes")
+  {.cast(uncheckedAssign).}:
+    x.kind2 = false
+  {.cast(uncheckedAssign).}:
+    x.kind1 = x.kind2 # support self assignment with effect
+
+  try:
+    x.kind1 = x.flag # flag is not accesible
+  except FieldDefect:
+    echo "prevented"
+
+  doAssert(x.x1 == "x1")
+  doAssert(x.x2 == "x2")
+
+
+test_myobject()
+
+
+#------------------------------------------------
+# bug #14244
+
+type
+  RocksDBResult*[T] = object
+    case ok*: bool
+    of true:
+      value*: T
+    else:
+      error*: string
+
+proc init(): RocksDBResult[string] =
+  {.cast(uncheckedAssign).}:
+    result.ok = true
+    result.value = "ok"
+
+echo init()
+
+
+#------------------------------------------------
+# bug #14312
+
+type MyObj = object
+  case kind: bool
+    of false: x0: int # would work with a type like seq[int]; value would be reset
+    of true: x1: string
+
+var a = MyObj(kind: false, x0: 1234)
+{.cast(uncheckedAssign).}:
+  a.kind = true
+doAssert(a.x1 == "")
+
+block:
+  # bug #15532
+  type Kind = enum
+    k0, k1
+
+  type Foo = object
+    y: int
+    case kind: Kind
+    of k0: x0: int
+    of k1: x1: int
+
+  const j0 = Foo(y: 1, kind: k0, x0: 2)
+  const j1 = Foo(y: 1, kind: k1, x1: 2)
+
+  doAssert j0.y == 1
+  doAssert j0.kind == k0
+  doAssert j1.kind == k1
+
+  doAssert j1.x1 == 2
+  doAssert j0.x0 == 2
+
+# ------------------------------------
+# bug #20305
+
+type
+  ContentNodeKind = enum
+    P, Br, Text
+  ContentNode = object
+    case kind: ContentNodeKind
+    of P: pChildren: seq[ContentNode]
+    of Br: discard
+    of Text: textStr: string
+
+proc bug20305 =
+  var x = ContentNode(kind: P, pChildren: @[
+    ContentNode(kind: P, pChildren: @[ContentNode(kind: Text, textStr: "brrr")])
+  ])
+  x.pChildren.add ContentNode(kind: Br)
+  x.pChildren.del(0)
+  {.cast(uncheckedAssign).}:
+    x.pChildren[0].kind = P
+  echo x.pChildren
+
+bug20305()
+
+# bug #21023
+block:
+  block:
+    type
+      MGErrorKind = enum
+        mgeUnexpected, mgeNotFound
+
+    type Foo = object
+      kind: MGErrorKind
+      ex: Exception
+
+    type Boo = object
+      a: seq[int]
+
+    type
+      Result2 = object
+        case o: bool
+        of false:
+          e: Foo
+        of true:
+          v: Boo
+
+    proc startSessionSync(): Result2 =
+      return Result2(o: true)
+
+    proc mainSync =
+      let ff = startSessionSync()
+      doAssert ff.o == true
+
+    mainSync()
+
+  block:
+    type
+      MGErrorKind = enum
+        mgeUnexpected, mgeNotFound
+
+    type Foo = object
+      kind: MGErrorKind
+      ex: Exception
+
+    type Boo = object
+      a: seq[int]
+
+    type
+      Result2 = object
+        case o: bool
+        of false:
+          e: Foo
+        of true:
+          v: Boo
+          s: int
+
+    proc startSessionSync(): Result2 =
+      return Result2(o: true, s: 12)
+
+    proc mainSync =
+      let ff = startSessionSync()
+      doAssert ff.s == 12
+
+    mainSync()
+
+import std/sequtils
+
+# bug #23690
+type
+  SomeObj* = object of RootObj
+
+  Item* = object
+    case kind*: 0..1
+    of 0:
+      a*: int
+      b*: SomeObj
+    of 1:
+      c*: string
+
+  ItemExt* = object
+    a*: Item
+    b*: string
+
+proc do1(x: int): seq[(string, Item)] =
+  result = @[("zero", Item(kind: 1, c: "first"))]
+
+proc do2(x: int, e: ItemExt): seq[(string, ItemExt)] =
+  do1(x).map(proc(v: (string, Item)): auto = (v[0], ItemExt(a: v[1], b: e.b)))
+
+doAssert $do2(0, ItemExt(a: Item(kind: 1, c: "second"), b: "third")) == """@[("zero", (a: (kind: 1, c: "first"), b: "third"))]"""
diff --git a/tests/arc/tcaseobjcopy.nim b/tests/arc/tcaseobjcopy.nim
new file mode 100644
index 000000000..fb26a4973
--- /dev/null
+++ b/tests/arc/tcaseobjcopy.nim
@@ -0,0 +1,253 @@
+discard """
+  valgrind: true
+  cmd: "nim c --gc:arc -d:useMalloc $file"
+  output: '''myobj destroyed
+myobj destroyed
+myobj destroyed
+A
+B
+begin
+end
+prevented
+(ok: true, value: "ok")
+myobj destroyed
+'''
+"""
+
+# bug #13102
+
+type
+  D = ref object
+  R = object
+    case o: bool
+    of false:
+      discard
+    of true:
+      field: D
+
+iterator things(): R =
+  when true:
+    var
+      unit = D()
+    while true:
+      yield R(o: true, field: unit)
+  else:
+    while true:
+      var
+        unit = D()
+      yield R(o: true, field: unit)
+
+proc main =
+  var i = 0
+  for item in things():
+    discard item.field
+    inc i
+    if i == 2: break
+
+main()
+
+# bug #13149
+
+type
+  TMyObj = object
+    p: pointer
+    len: int
+
+proc `=destroy`(o: var TMyObj) =
+  if o.p != nil:
+    dealloc o.p
+    o.p = nil
+    echo "myobj destroyed"
+
+proc `=copy`(dst: var TMyObj, src: TMyObj) =
+  `=destroy`(dst)
+  dst.p = alloc(src.len)
+  dst.len = src.len
+
+proc `=sink`(dst: var TMyObj, src: TMyObj) =
+  `=destroy`(dst)
+  dst.p = src.p
+  dst.len = src.len
+
+type
+  TObjKind = enum Z, A, B
+  TCaseObj = object
+    case kind: TObjKind
+    of Z: discard
+    of A:
+      x1: int # this int plays important role
+      x2: TMyObj
+    of B:
+      y: TMyObj
+
+proc testSinks: TCaseObj =
+  result = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5)))
+  result = TCaseObj(kind: B, y: TMyObj(len: 3, p: alloc(3)))
+
+proc use(x: TCaseObj) = discard
+
+proc testCopies(i: int) =
+  var a: array[2, TCaseObj]
+  a[i] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5)))
+  a[i+1] = a[i] # copy, cannot move
+  use(a[i])
+
+let x1 = testSinks()
+testCopies(0)
+
+# bug #12957
+
+type
+  PegKind* = enum
+    pkCharChoice,
+    pkSequence
+  Peg* = object ## type that represents a PEG
+    case kind: PegKind
+    of pkCharChoice: charChoice: ref set[char]
+    else: discard
+    sons: seq[Peg]
+
+proc charSet*(s: set[char]): Peg =
+  ## constructs a PEG from a character set `s`
+  result = Peg(kind: pkCharChoice)
+  new(result.charChoice)
+  result.charChoice[] = s
+
+proc len(a: Peg): int {.inline.} = return a.sons.len
+proc myadd(d: var Peg, s: Peg) {.inline.} = add(d.sons, s)
+
+proc sequence*(a: openArray[Peg]): Peg =
+  result = Peg(kind: pkSequence, sons: @[])
+  when false:
+    #works too:
+    result.myadd(a[0])
+    result.myadd(a[1])
+  for x in items(a):
+    # works:
+    #result.sons.add(x)
+    # fails:
+    result.myadd x
+  if result.len == 1:
+    result = result.sons[0] # this must not move!
+
+when true:
+  # bug #12957
+
+  proc p =
+    echo "A"
+    let x = sequence([charSet({'a'..'z', 'A'..'Z', '_'}),
+              charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'})])
+    echo "B"
+  p()
+
+  proc testSubObjAssignment =
+    echo "begin"
+    # There must be extactly one element in the array constructor!
+    let x = sequence([charSet({'a'..'z', 'A'..'Z', '_'})])
+    echo "end"
+  testSubObjAssignment()
+
+
+#------------------------------------------------
+
+type
+  MyObject = object
+    x1: string
+    case kind1: bool
+      of false: y1: string
+      of true:
+          y2: seq[string]
+          case kind2: bool
+              of true: z1: string
+              of false:
+                z2: seq[string]
+                flag: bool
+    x2: string
+
+proc test_myobject =
+  var x: MyObject
+  x.x1 = "x1"
+  x.x2 = "x2"
+  x.y1 = "ljhkjhkjh"
+  {.cast(uncheckedAssign).}:
+    x.kind1 = true
+  x.y2 = @["1", "2"]
+  {.cast(uncheckedAssign).}:
+    x.kind2 = true
+  x.z1 = "yes"
+  {.cast(uncheckedAssign).}:
+    x.kind2 = false
+  x.z2 = @["1", "2"]
+  {.cast(uncheckedAssign).}:
+    x.kind2 = true
+  x.z1 = "yes"
+  x.kind2 = true # should be no effect
+  doAssert(x.z1 == "yes")
+  {.cast(uncheckedAssign).}:
+    x.kind2 = false
+    x.kind1 = x.kind2 # support self assignment with effect
+
+  try:
+    x.kind1 = x.flag # flag is not accesible
+  except FieldDefect:
+    echo "prevented"
+
+  doAssert(x.x1 == "x1")
+  doAssert(x.x2 == "x2")
+
+
+test_myobject()
+
+
+#------------------------------------------------
+# bug #14244
+
+type
+  RocksDBResult*[T] = object
+    case ok*: bool
+    of true:
+      value*: T
+    else:
+      error*: string
+
+proc init(): RocksDBResult[string] =
+  {.cast(uncheckedAssign).}:
+    result.ok = true
+  result.value = "ok"
+
+echo init()
+
+
+#------------------------------------------------
+# bug #14312
+
+type MyObj = object
+  case kind: bool
+    of false: x0: int # would work with a type like seq[int]; value would be reset
+    of true: x1: string
+
+var a = MyObj(kind: false, x0: 1234)
+{.cast(uncheckedAssign).}:
+  a.kind = true
+doAssert(a.x1 == "")
+
+block:
+  # bug #15532
+  type Kind = enum
+    k0, k1
+
+  type Foo = object
+    y: int
+    case kind: Kind
+    of k0: x0: int
+    of k1: x1: int
+
+  const j0 = Foo(y: 1, kind: k0, x0: 2)
+  const j1 = Foo(y: 1, kind: k1, x1: 2)
+
+  doAssert j0.y == 1
+  doAssert j0.kind == k0
+  doAssert j1.kind == k1
+
+  doAssert j1.x1 == 2
+  doAssert j0.x0 == 2
diff --git a/tests/arc/tclosureiter.nim b/tests/arc/tclosureiter.nim
new file mode 100644
index 000000000..e4a2ceac6
--- /dev/null
+++ b/tests/arc/tclosureiter.nim
@@ -0,0 +1,36 @@
+discard """
+  cmd: '''nim c -d:nimAllocStats --gc:arc $file'''
+  output: '''(allocCount: 102, deallocCount: 102)'''
+"""
+
+type
+  FutureBase = ref object
+    someData: string
+
+const
+  # Just to occupy some RAM
+  BigData = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+
+iterator mainIter(): FutureBase {.closure.} =
+  for x in 0 .. 100:
+    var internalTmpFuture = FutureBase(someData: BigData)
+    yield internalTmpFuture
+
+proc main() =
+  var nameIterVar = mainIter
+  var next = nameIterVar()
+  while not isNil(next):
+    next = nameIterVar()
+    if not isNil(next):
+      doAssert next.someData.len == 97
+    # GC_unref(next)
+    # If you uncomment the GC_ref above,
+    # the program basically uses no memory after the run.
+    # but crashes with refc, which might indicate
+    # that arc/orc simply never frees the result of "next"?
+    if finished(nameIterVar):
+      break
+
+main()
+GC_fullCollect()
+echo getAllocStats()
diff --git a/tests/arc/tcomputedgoto.nim b/tests/arc/tcomputedgoto.nim
new file mode 100644
index 000000000..07487684a
--- /dev/null
+++ b/tests/arc/tcomputedgoto.nim
@@ -0,0 +1,44 @@
+discard """
+  cmd: '''nim c --mm:arc $file'''
+  output: '''
+2
+2
+destroyed
+'''
+"""
+
+type
+  ObjWithDestructor = object
+    a: int
+proc `=destroy`(self: ObjWithDestructor) =
+  echo "destroyed"
+
+proc `=copy`(self: var ObjWithDestructor, other: ObjWithDestructor) =
+  echo "copied"
+
+proc test(a: range[0..1], arg: ObjWithDestructor) =
+  var iteration = 0
+  while true:
+    {.computedGoto.}
+
+    let
+      b = int(a) * 2
+      c = a
+      d = arg
+      e = arg
+
+    discard c
+    discard d
+    discard e
+
+    inc iteration
+
+    case a
+    of 0:
+      assert false
+    of 1:
+      echo b
+      if iteration == 2:
+        break
+
+test(1, ObjWithDestructor())
diff --git a/tests/arc/tcomputedgotocopy.nim b/tests/arc/tcomputedgotocopy.nim
new file mode 100644
index 000000000..07487684a
--- /dev/null
+++ b/tests/arc/tcomputedgotocopy.nim
@@ -0,0 +1,44 @@
+discard """
+  cmd: '''nim c --mm:arc $file'''
+  output: '''
+2
+2
+destroyed
+'''
+"""
+
+type
+  ObjWithDestructor = object
+    a: int
+proc `=destroy`(self: ObjWithDestructor) =
+  echo "destroyed"
+
+proc `=copy`(self: var ObjWithDestructor, other: ObjWithDestructor) =
+  echo "copied"
+
+proc test(a: range[0..1], arg: ObjWithDestructor) =
+  var iteration = 0
+  while true:
+    {.computedGoto.}
+
+    let
+      b = int(a) * 2
+      c = a
+      d = arg
+      e = arg
+
+    discard c
+    discard d
+    discard e
+
+    inc iteration
+
+    case a
+    of 0:
+      assert false
+    of 1:
+      echo b
+      if iteration == 2:
+        break
+
+test(1, ObjWithDestructor())
diff --git a/tests/arc/tconst_to_sink.nim b/tests/arc/tconst_to_sink.nim
new file mode 100644
index 000000000..25f659341
--- /dev/null
+++ b/tests/arc/tconst_to_sink.nim
@@ -0,0 +1,26 @@
+discard """
+  output: '''@[(s1: "333", s2: ""), (s1: "abc", s2: "def"), (s1: "3x", s2: ""), (s1: "3x", s2: ""), (s1: "3x", s2: ""), (s1: "3x", s2: ""), (s1: "lastone", s2: "")]'''
+  matrix: "--gc:arc"
+  targets: "c cpp"
+"""
+
+# bug #13240
+
+type
+  Thing = object
+    s1: string
+    s2: string
+
+var box: seq[Thing]
+
+const c = [Thing(s1: "333"), Thing(s1: "abc", s2: "def")]
+
+for i in 0..high(c):
+  box.add c[i]
+
+for i in 0..3:
+  box.add Thing(s1: "3x")
+
+box.add Thing(s1: "lastone")
+
+echo box
diff --git a/tests/arc/tcontrolflow.nim b/tests/arc/tcontrolflow.nim
new file mode 100644
index 000000000..dbc115903
--- /dev/null
+++ b/tests/arc/tcontrolflow.nim
@@ -0,0 +1,118 @@
+discard """
+  output: '''begin A
+elif
+end A
+destroyed
+begin false
+if
+end false
+destroyed
+begin true
+if
+end true
+7
+##index 2 not in 0 .. 1##
+true
+'''
+  cmd: "nim c --gc:arc -d:danger $file"
+"""
+# we use the -d:danger switch to detect uninitialized stack
+# slots more reliably (there shouldn't be any, of course).
+
+type
+  Foo = object
+    id: int
+
+proc `=destroy`(x: var Foo) =
+  if x.id != 0:
+    echo "destroyed"
+    x.id = 0
+
+proc construct(): Foo = Foo(id: 3)
+
+proc elifIsEasy(cond: bool) =
+  echo "begin A"
+  if cond:
+    echo "if"
+  elif construct().id == 3:
+    echo "elif"
+  else:
+    echo "else"
+  echo "end A"
+
+elifIsEasy(false)
+
+
+proc orIsHard(cond: bool) =
+  echo "begin ", cond
+  if cond or construct().id == 3:
+    echo "if"
+  else:
+    echo "else"
+  echo "end ", cond
+
+orIsHard(false)
+orIsHard(true)
+
+type
+  Control = ref object
+    x: int
+
+  MouseEvent = ref object
+    control: Control
+    button: int
+
+proc run(data: Control) =
+  var evt = MouseEvent(button: 1)
+  evt.control = data
+  if evt.button == 1:
+    discard
+  else:
+    return
+
+  echo data.x
+
+var c = Control(x: 7)
+
+run(c)
+
+proc sysFatal(exceptn: typedesc, message: string) {.inline.} =
+  var buf = newStringOfCap(200)
+  add(buf, "##")
+  add(buf, message)
+  add(buf, "##")
+  echo buf
+
+proc ifexpr(i, a, b: int) {.compilerproc, noinline.} =
+  sysFatal(IndexDefect,
+    if b < a: "index out of bounds, the container is empty"
+    else: "index " & $i & " not in " & $a & " .. " & $b)
+
+ifexpr(2, 0, 1)
+
+# bug #14899
+template toSeq(): untyped =
+  block:
+    var result = @[1]
+    result
+
+proc clItems(s: seq[int]) =
+  assert s.len == 1
+
+proc escapeCheck =
+  clItems(toSeq())
+
+escapeCheck()
+
+# bug #14900
+
+proc seqsEqual(a, b: string): bool =
+  if false:
+    false
+  else:
+    (var result1 = a; result1) == (var result2 = b; result2)
+
+# can be const or var too
+let expected = "hello"
+
+echo seqsEqual(expected, expected)
diff --git a/tests/arc/tcursor_field_obj_constr.nim b/tests/arc/tcursor_field_obj_constr.nim
new file mode 100644
index 000000000..b87f734bd
--- /dev/null
+++ b/tests/arc/tcursor_field_obj_constr.nim
@@ -0,0 +1,44 @@
+discard """
+  output: '''a
+b
+c'''
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #18469
+
+type
+  Edge = object
+    neighbor {.cursor.}: Node
+
+  NodeObj = object
+    neighbors: seq[Edge]
+    label: string
+    visited: bool
+  Node = ref NodeObj
+
+  Graph = object
+    nodes: seq[Node]
+
+proc `=destroy`(x: var NodeObj) =
+  echo x.label
+  `=destroy`(x.neighbors)
+  `=destroy`(x.label)
+
+proc addNode(self: var Graph; label: string): Node =
+  self.nodes.add(Node(label: label))
+  result = self.nodes[^1]
+
+proc addEdge(self: Graph; source, neighbor: Node) =
+  source.neighbors.add(Edge(neighbor: neighbor))
+
+proc main =
+  var graph: Graph
+  let nodeA = graph.addNode("a")
+  let nodeB = graph.addNode("b")
+  let nodeC = graph.addNode("c")
+
+  graph.addEdge(nodeA, neighbor = nodeB)
+  graph.addEdge(nodeA, neighbor = nodeC)
+
+main()
diff --git a/tests/arc/tcursor_on_localvar.nim b/tests/arc/tcursor_on_localvar.nim
new file mode 100644
index 000000000..0f53c5efa
--- /dev/null
+++ b/tests/arc/tcursor_on_localvar.nim
@@ -0,0 +1,161 @@
+discard """
+  output: '''Section: common
+  Param: Floats1
+Section: local
+  Param: Str
+  Param: Bool
+  Param: Floats2
+destroy Foo
+destroy Foo
+'''
+  cmd: '''nim c --gc:arc $file'''
+"""
+
+# bug #15325
+
+import tables
+import strutils
+
+const defaultSection = "***"
+
+type
+    Config* = ref object
+        table: OrderedTableRef[string, OrderedTable[string, string]]
+
+# ----------------------------------------------------------------------------------------------------------------------
+proc newConfig*(): Config =
+    result       = new(Config)
+    result.table = newOrderedTable[string, OrderedTable[string, string]]()
+
+# ----------------------------------------------------------------------------------------------------------------------
+proc add*(self: Config, param, value, section: string) {.nosinks.} =
+    let s = if section == "": defaultSection else: section
+
+    if not self.table.contains(s):
+        self.table[s] = initOrderedTable[string, string]()
+
+    self.table[s][param] = value
+
+# ----------------------------------------------------------------------------------------------------------------------
+proc sections*(self: Config): seq[string] =
+    for i in self.table.keys:
+        let s = if i == defaultSection: "" else: i
+        result.add(s)
+
+# ----------------------------------------------------------------------------------------------------------------------
+proc params*(self: Config, section: string): seq[string] =
+    let s = if section == "": defaultSection else: section
+
+    if self.table.contains(s):
+        for i in self.table[s].keys:
+            result.add(i)
+
+# ----------------------------------------------------------------------------------------------------------------------
+proc extract*(str, start, finish: string): string =
+    let startPos = str.find(start)
+
+    if startPos < 0:
+        return ""
+
+    let endPos = str.find(finish, startPos)
+
+    if endPos < 0:
+        return ""
+
+    return str[startPos + start.len() ..< endPos]
+
+# ----------------------------------------------------------------------------------------------------------------------
+proc loadString*(self: Config, text: string): tuple[valid: bool, errorInLine: int] {.discardable.} =
+    self.table.clear()
+
+    var data = ""
+
+    data = text
+
+    var
+        actualSection = ""
+        lineCount     = 0
+
+    for i in splitLines(data):
+        lineCount += 1
+
+        var line = strip(i)
+
+        if line.len() == 0:
+            continue
+
+        if line[0] == '#' or line[0] == ';':
+            continue
+
+        if line[0] == '[':
+            let section = strip(extract(line, "[", "]"))
+
+            if section.len() != 0:
+                actualSection = section
+            else:
+                self.table.clear()
+                return (false, lineCount)
+        else:
+            let equal = find(line, '=')
+
+            if equal <= 0:
+                self.table.clear()
+                return (false, lineCount)
+            else:
+                let
+                    param = strip(line[0 .. equal - 1])
+                    value = strip(line[equal + 1 .. ^1])
+
+                if param.len() == 0:
+                    self.table.clear()
+                    return (false, lineCount)
+                else:
+                    self.add(param, value, actualSection)
+
+    return (true, 0)
+
+# ----------------------------------------------------------------------------------------------------------------------
+when isMainModule:
+    var cfg = newConfig()
+
+    cfg.loadString("[common]\nFloats1 = 1,2,3\n[local]\nStr = \"String...\"\nBool = true\nFloats2 = 4, 5, 6\n")
+
+    for s in cfg.sections():
+        echo "Section: " & s
+
+        for p in cfg.params(s):
+            echo "  Param: " & p
+
+# bug #16437
+
+type
+  Foo = object
+  FooRef = ref Foo
+
+  Bar = ref object
+    f: FooRef
+
+proc `=destroy`(o: var Foo) =
+  echo "destroy Foo"
+
+proc testMe(x: Bar) =
+  var c = (if x != nil: x.f else: nil)
+  assert c != nil
+
+proc main =
+  var b = Bar(f: FooRef())
+  testMe(b)
+
+main()
+
+proc testMe2(x: Bar) =
+  var c: FooRef
+  c = (if x != nil: x.f else: nil)
+  assert c != nil
+
+proc main2 =
+  var b = Bar(f: FooRef())
+  testMe2(b)
+
+main2()
+
diff --git a/tests/arc/tcursorloop.nim b/tests/arc/tcursorloop.nim
new file mode 100644
index 000000000..a37a6a036
--- /dev/null
+++ b/tests/arc/tcursorloop.nim
@@ -0,0 +1,45 @@
+discard """
+  cmd: '''nim c --gc:arc --expandArc:traverse --hint:Performance:off $file'''
+  nimout: '''
+--expandArc: traverse
+
+var
+  it
+  jt_cursor
+try:
+  `=copy`(it, root)
+  block :tmp:
+    while (
+      not (it == nil)):
+      if true:
+        echo [it.s]
+        `=copy`(it, it.ri)
+  jt_cursor = root
+  if (
+    not (jt_cursor == nil)):
+    echo [jt_cursor.s]
+    jt_cursor = jt_cursor.ri
+finally:
+  `=destroy`(it)
+-- end of expandArc ------------------------
+'''
+"""
+
+type
+  Node = ref object
+    le, ri: Node
+    s: string
+
+proc traverse(root: Node) =
+  var it = root
+  while it != nil:
+    if true:
+      echo it.s
+      it = it.ri
+
+  var jt = root
+  if jt != nil:
+    echo jt.s
+    jt = jt.ri
+
+traverse(nil)
diff --git a/tests/arc/tcustomtrace.nim b/tests/arc/tcustomtrace.nim
new file mode 100644
index 000000000..5e0ecfb24
--- /dev/null
+++ b/tests/arc/tcustomtrace.nim
@@ -0,0 +1,240 @@
+discard """
+  outputsub: '''1
+2
+3
+4
+5
+6
+89
+90
+90
+0 0 1
+0 1 2
+0 2 3
+1 0 4
+1 1 5
+1 2 6
+1 3 7
+after 6 6
+MEM 0'''
+joinable: false
+  cmd: "nim c --gc:orc -d:useMalloc $file"
+  valgrind: "true"
+"""
+
+import typetraits
+
+type
+  myseq*[T] = object
+    len, cap: int
+    data: ptr UncheckedArray[T]
+
+# XXX make code memory safe for overflows in '*'
+var
+  allocCount, deallocCount: int
+
+proc `=destroy`*[T](x: var myseq[T]) =
+  if x.data != nil:
+    when not supportsCopyMem(T):
+      for i in 0..<x.len: `=destroy`(x[i])
+    dealloc(x.data)
+    inc deallocCount
+    x.data = nil
+    x.len = 0
+    x.cap = 0
+
+proc `=copy`*[T](a: var myseq[T]; b: myseq[T]) =
+  if a.data == b.data: return
+  if a.data != nil:
+    `=destroy`(a)
+    #dealloc(a.data)
+    #inc deallocCount
+    #a.data = nil
+  a.len = b.len
+  a.cap = b.cap
+  if b.data != nil:
+    a.data = cast[type(a.data)](alloc(a.cap * sizeof(T)))
+    inc allocCount
+    when supportsCopyMem(T):
+      copyMem(a.data, b.data, a.cap * sizeof(T))
+    else:
+      for i in 0..<a.len:
+        a.data[i] = b.data[i]
+
+proc `=sink`*[T](a: var myseq[T]; b: myseq[T]) =
+  if a.data != nil and a.data != b.data:
+    dealloc(a.data)
+    inc deallocCount
+  a.len = b.len
+  a.cap = b.cap
+  a.data = b.data
+
+proc `=trace`*[T](x: var myseq[T]; env: pointer) =
+  if x.data != nil:
+    for i in 0..<x.len: `=trace`(x[i], env)
+
+proc resize[T](s: var myseq[T]) =
+  let oldCap = s.cap
+  if oldCap == 0: s.cap = 8
+  else: s.cap = (s.cap * 3) shr 1
+  if s.data == nil: inc allocCount
+  s.data = cast[typeof(s.data)](realloc0(s.data, oldCap * sizeof(T), s.cap * sizeof(T)))
+
+proc reserveSlot[T](x: var myseq[T]): ptr T =
+  if x.len >= x.cap: resize(x)
+  result = addr(x.data[x.len])
+  inc x.len
+
+template add*[T](x: var myseq[T]; y: T) =
+  reserveSlot(x)[] = y
+
+proc shrink*[T](x: var myseq[T]; newLen: int) =
+  assert newLen <= x.len
+  assert newLen >= 0
+  when not supportsCopyMem(T):
+    for i in countdown(x.len - 1, newLen - 1):
+      `=destroy`(x.data[i])
+  x.len = newLen
+
+proc grow*[T](x: var myseq[T]; newLen: int; value: T) =
+  if newLen <= x.len: return
+  assert newLen >= 0
+  let oldCap = x.cap
+  if oldCap == 0: x.cap = newLen
+  else: x.cap = max(newLen, (oldCap * 3) shr 1)
+  if x.data == nil: inc allocCount
+  x.data = cast[type(x.data)](realloc0(x.data, oldCap * sizeof(T), x.cap * sizeof(T)))
+  for i in x.len..<newLen:
+    x.data[i] = value
+  x.len = newLen
+
+template default[T](t: typedesc[T]): T =
+  var v: T
+  v
+
+proc setLen*[T](x: var myseq[T]; newLen: int) {.deprecated.} =
+  if newlen < x.len: shrink(x, newLen)
+  else: grow(x, newLen, default(T))
+
+template `[]`*[T](x: myseq[T]; i: Natural): T =
+  assert i < x.len
+  x.data[i]
+
+template `[]=`*[T](x: myseq[T]; i: Natural; y: T) =
+  assert i < x.len
+  x.data[i] = y
+
+proc createSeq*[T](elems: varargs[T]): myseq[T] =
+  result.cap = max(elems.len, 2)
+  result.len = elems.len
+  result.data = cast[type(result.data)](alloc0(result.cap * sizeof(T)))
+  inc allocCount
+  when supportsCopyMem(T):
+    copyMem(result.data, addr(elems[0]), result.cap * sizeof(T))
+  else:
+    for i in 0..<result.len:
+      result.data[i] = elems[i]
+
+proc len*[T](x: myseq[T]): int {.inline.} = x.len
+
+proc main =
+  var s = createSeq(1, 2, 3, 4, 5, 6)
+  s.add 89
+  s.grow s.len + 2, 90
+  for i in 0 ..< s.len:
+    echo s[i]
+
+  var nested = createSeq(createSeq(1, 2, 3), createSeq(4, 5, 6, 7))
+  for i in 0 ..< nested.len:
+    for j in 0 ..< nested[i].len:
+      echo i, " ", j, " ", nested[i][j]
+
+main()
+echo "after ", allocCount, " ", deallocCount
+
+type
+  Node = ref object
+    name: char
+    sccId: int
+    kids: myseq[Node]
+    rc: int
+
+proc edge(a, b: Node) =
+  inc b.rc
+  a.kids.add b
+
+proc createNode(name: char): Node =
+  new result
+  result.name = name
+  result.kids = createSeq[Node]()
+
+proc use(x: Node) = discard
+
+proc buildComplexGraph: Node =
+  # see https://en.wikipedia.org/wiki/Strongly_connected_component for the
+  # graph:
+  let a = createNode('a')
+  let b = createNode('b')
+  let c = createNode('c')
+  let d = createNode('d')
+  let e = createNode('e')
+
+  a.edge c
+  c.edge b
+  c.edge e
+  b.edge a
+  d.edge c
+  e.edge d
+
+
+  let f = createNode('f')
+  b.edge f
+  e.edge f
+
+  let g = createNode('g')
+  let h = createNode('h')
+  let i = createNode('i')
+
+  f.edge g
+  f.edge i
+  g.edge h
+  h.edge i
+  i.edge g
+
+  let j = createNode('j')
+
+  h.edge j
+  i.edge j
+
+  let k = createNode('k')
+  let l = createNode('l')
+
+  f.edge k
+  k.edge l
+  l.edge k
+  k.edge j
+
+  let m = createNode('m')
+  let n = createNode('n')
+  let p = createNode('p')
+  let q = createNode('q')
+
+  m.edge n
+  n.edge p
+  n.edge q
+  q.edge p
+  p.edge m
+
+  q.edge k
+
+  d.edge m
+  e.edge n
+
+  result = a
+
+proc main2 =
+  let g = buildComplexGraph()
+
+main2()
+GC_fullCollect()
+echo "MEM ", getOccupiedMem()
diff --git a/tests/arc/tdeepcopy.nim b/tests/arc/tdeepcopy.nim
new file mode 100644
index 000000000..91d93aa28
--- /dev/null
+++ b/tests/arc/tdeepcopy.nim
@@ -0,0 +1,67 @@
+discard """
+  cmd: "nim c --gc:arc --deepcopy:on $file"
+  output: '''13 abc
+13 abc
+13 abc
+13 abc
+13 abc
+13 abc
+13 abc
+13 abc
+13 abc
+13 abc
+13 abc
+called deepCopy for int
+called deepCopy for int
+called deepCopy for int
+called deepCopy for int
+called deepCopy for int
+called deepCopy for int
+called deepCopy for int
+called deepCopy for int
+called deepCopy for int
+called deepCopy for int
+called deepCopy for int
+0'''
+"""
+
+type
+  PBinaryTree = ref object of RootObj
+    le, ri: PBinaryTree
+    value: int
+
+proc mainB =
+  var x: PBinaryTree
+  deepCopy(x, PBinaryTree(ri: PBinaryTree(le: PBinaryTree(value: 13))))
+
+  var y: string
+  deepCopy y, "abc"
+  echo x.ri.le.value, " ", y
+
+for i in 0..10:
+  mainB()
+
+
+type
+  Bar[T] = object
+    x: T
+
+proc `=deepCopy`[T](b: ref Bar[T]): ref Bar[T] =
+  result.new
+  result.x = b.x
+  when T is int:
+    echo "called deepCopy for int"
+  else:
+    echo "called deepCopy for something else"
+
+proc main =
+  var dummy, c: ref Bar[int]
+  new(dummy)
+  dummy.x = 44
+
+  deepCopy c, dummy
+
+for i in 0..10:
+  main()
+
+echo getOccupiedMem()
diff --git a/tests/arc/tdestroy_in_loopcond.nim b/tests/arc/tdestroy_in_loopcond.nim
new file mode 100644
index 000000000..62532664d
--- /dev/null
+++ b/tests/arc/tdestroy_in_loopcond.nim
@@ -0,0 +1,74 @@
+discard """
+  output: '''400 true'''
+  cmd: "nim c --gc:orc $file"
+"""
+
+type HeapQueue*[T] = object
+  data: seq[T]
+
+
+proc len*[T](heap: HeapQueue[T]): int {.inline.} =
+  heap.data.len
+
+proc `[]`*[T](heap: HeapQueue[T], i: Natural): T {.inline.} =
+  heap.data[i]
+
+proc push*[T](heap: var HeapQueue[T], item: T) =
+  heap.data.add(item)
+
+proc pop*[T](heap: var HeapQueue[T]): T =
+  result = heap.data.pop
+
+proc clear*[T](heap: var HeapQueue[T]) = heap.data.setLen 0
+
+
+type
+  Future = ref object of RootObj
+    s: string
+    callme: proc()
+
+var called = 0
+
+proc consume(f: Future) =
+  inc called
+
+proc newFuture(s: string): Future =
+  var r: Future
+  r = Future(s: s, callme: proc() =
+    consume r)
+  result = r
+
+var q: HeapQueue[tuple[finishAt: int64, fut: Future]]
+
+proc sleep(f: int64): Future =
+  q.push (finishAt: f, fut: newFuture("async-sleep"))
+
+proc processTimers =
+  # Pop the timers in the order in which they will expire (smaller `finishAt`).
+  var count = q.len
+  let t = high(int64)
+  while count > 0 and t >= q[0].finishAt:
+    q.pop().fut.callme()
+    dec count
+
+var futures: seq[Future]
+
+proc main =
+  for i in 1..200:
+    futures.add sleep(56020904056300)
+    futures.add sleep(56020804337500)
+    #futures.add sleep(2.0)
+
+    #futures.add sleep(4.0)
+
+    processTimers()
+    #q.pop()[1].callme()
+    #q.pop()[1].callme()
+
+    futures.setLen 0
+
+  q.clear()
+
+main()
+GC_fullCollect()
+echo called, " ", getOccupiedMem() < 160
diff --git a/tests/arc/tdup.nim b/tests/arc/tdup.nim
new file mode 100644
index 000000000..61f262a68
--- /dev/null
+++ b/tests/arc/tdup.nim
@@ -0,0 +1,71 @@
+discard """
+  cmd: "nim c --mm:arc --expandArc:foo --hints:off $file"
+  nimout: '''
+--expandArc: foo
+
+var
+  x
+  :tmpD
+  s
+  :tmpD_1
+x = Ref(id: 8)
+inc:
+  :tmpD = `=dup`(x)
+  :tmpD
+inc:
+  let blitTmp = x
+  blitTmp
+var id_1 = 777
+s = RefCustom(id_2: addr(id_1))
+inc_1 :
+  :tmpD_1 = `=dup_1`(s)
+  :tmpD_1
+inc_1 :
+  let blitTmp_1 = s
+  blitTmp_1
+-- end of expandArc ------------------------
+'''
+"""
+
+type
+  Ref = ref object
+    id: int
+
+  RefCustom = object
+    id: ptr int
+
+proc `=dup`(x: RefCustom): RefCustom =
+  result.id = x.id
+
+proc inc(x: sink Ref) =
+  inc x.id
+
+proc inc(x: sink RefCustom) =
+  inc x.id[]
+
+
+proc foo =
+  var x = Ref(id: 8)
+  inc(x)
+  inc(x)
+  var id = 777
+  var s = RefCustom(id: addr id)
+  inc s
+  inc s
+
+foo()
+
+proc foo2 =
+  var x = Ref(id: 8)
+  inc(x)
+  doAssert x.id == 9
+  inc(x)
+  doAssert x.id == 10
+  var id = 777
+  var s = RefCustom(id: addr id)
+  inc s
+  doAssert s.id[] == 778
+  inc s
+  doAssert s.id[] == 779
+
+foo2()
diff --git a/tests/arc/testfile.txt b/tests/arc/testfile.txt
new file mode 100644
index 000000000..5169490be
--- /dev/null
+++ b/tests/arc/testfile.txt
@@ -0,0 +1,2 @@
+1 x
+2 y
diff --git a/tests/arc/texceptions.nim b/tests/arc/texceptions.nim
new file mode 100644
index 000000000..c55b463fc
--- /dev/null
+++ b/tests/arc/texceptions.nim
@@ -0,0 +1,16 @@
+discard """
+  cmd: "nim cpp --gc:arc $file"
+"""
+
+block: # issue #13071
+  type MyExcept = object of CatchableError
+  proc gun()=
+    raise newException(MyExcept, "foo:")
+  proc fun()=
+    var a = ""
+    try:
+      gun()
+    except Exception as e:
+      a = e.msg & $e.name # was segfaulting here for `nim cpp --gc:arc`
+    doAssert a == "foo:MyExcept"
+  fun()
diff --git a/tests/arc/texplicit_sink.nim b/tests/arc/texplicit_sink.nim
new file mode 100644
index 000000000..4b021ed62
--- /dev/null
+++ b/tests/arc/texplicit_sink.nim
@@ -0,0 +1,29 @@
+discard """
+  output: '''de'''
+  cmd: '''nim c --mm:arc --expandArc:main $file'''
+  nimout: '''--expandArc: main
+
+var
+  a
+  b_cursor
+try:
+  a = f "abc"
+  b_cursor = "de"
+  `=sink`(a, b_cursor)
+  echo [a]
+finally:
+  `=destroy`(a)
+-- end of expandArc ------------------------'''
+"""
+
+# bug #20572
+
+proc f(s: string): string = s
+
+proc main =
+  var a = f "abc"
+  var b = "de"
+  `=sink`(a, b)
+  echo a
+
+main()
diff --git a/tests/arc/tfuncobj.nim b/tests/arc/tfuncobj.nim
new file mode 100644
index 000000000..50cd9425e
--- /dev/null
+++ b/tests/arc/tfuncobj.nim
@@ -0,0 +1,38 @@
+discard """
+  outputsub: '''f1
+f2
+f3'''
+  cmd: "nim c --gc:orc $file"
+  valgrind: true
+"""
+
+type
+  FuncObj = object
+    fn: proc (env: pointer) {.cdecl.}
+    env: pointer
+
+proc `=destroy`(x: var FuncObj) =
+  GC_unref(cast[RootRef](x.env))
+
+proc `=copy`(x: var FuncObj, y: FuncObj) {.error.}
+
+# bug #18433
+
+proc main =
+  var fs: seq[FuncObj]
+
+  proc wrap(p: proc()) =
+    proc closeOver() = p()
+    let env = rawEnv closeOver
+    GC_ref(cast[RootRef](env))
+    fs.add(FuncObj(fn: cast[proc(env: pointer){.cdecl.}](rawProc closeOver), env: env))
+
+  wrap(proc() {.closure.} = echo "f1")
+  wrap(proc() {.closure.} = echo "f2")
+  wrap(proc() {.closure.} = echo "f3")
+
+  for a in fs:
+    a.fn(a.env)
+
+main()
+
diff --git a/tests/arc/thamming_orc.nim b/tests/arc/thamming_orc.nim
new file mode 100644
index 000000000..777efb38e
--- /dev/null
+++ b/tests/arc/thamming_orc.nim
@@ -0,0 +1,155 @@
+discard """
+  output: '''(allocCount: 1114, deallocCount: 1112)
+created 491 destroyed 491'''
+  cmd: "nim c --gc:orc -d:nimAllocStats $file"
+"""
+
+# bug #18421
+
+# test Nim Hamming Number Lazy List algo with reference counts and not...
+# compile with "-d:release -d:danger" and test with various
+# memory managment GC's, allocators, threading, etc.
+# it should be guaranteed to work with zero memory leaks with `--gc:orc`...
+
+# compile with `-d:trace20` to trace creation and destruction of first 20 values.
+
+from math import log2
+
+# implement our own basic BigInt so the bigints library isn't necessary...
+type
+  BigInt = object
+    digits: seq[uint32]
+let zeroBigInt = BigInt(digits: @[ 0'u32 ])
+let oneBigInt = BigInt(digits: @[ 1'u32 ])
+
+proc shladd(bi: var BigInt; n: int; a: BigInt) =
+  # assume that both `bi` and `a` are sized correctly with
+  # msuint32 for both not containing a zero
+  let alen = a.digits.len
+  let mx = max(bi.digits.len, a.digits.len)
+  for i in bi.digits.len ..< mx: bi.digits.add 0'u32
+  var cry = 0'u64
+  for i in 0 ..< alen:
+    cry += (bi.digits[i].uint64 shl n) + a.digits[i].uint64
+    bi.digits[i] = cry.uint32; cry = cry shr 32
+  for i in alen ..< mx:
+    cry += bi.digits[i].uint64 shl n
+    bi.digits[i] = cry.uint32; cry = cry shr 32
+  if cry > 0'u64:
+    bi.digits.add cry.uint32
+
+proc `$`(x: BigInt): string =
+  if x.digits.len == 0 or (x.digits.len == 1 and x.digits[0] == 0'u32):
+    return "0"
+  result = ""; var n = x; var msd = n.digits.high
+  while msd >= 0:
+    if n.digits[msd] == 0'u32: msd.dec; continue
+    var brw = 0.uint64
+    for i in countdown(msd, 0):
+      let dvdnd = n.digits[i].uint64 + (brw shl 32)
+      let q = dvdnd div 10'u64; brw = dvdnd - q * 10'u64
+      n.digits[i] = q.uint32
+    result &= $brw
+  for i in 0 .. result.high shr 1: # reverse result string in place
+    let tmp = result[^(i + 1)]
+    result[^(i + 1)] = result[i]
+    result[i] = tmp
+
+type TriVal = (uint32, uint32, uint32)
+type LogRep = (float64, TriVal)
+type LogRepf = proc(x: LogRep): LogRep
+const one: LogRep = (0.0'f64, (0'u32, 0'u32, 0'u32))
+proc `<`(me: LogRep, othr: LogRep): bool = me[0] < othr[0]
+
+proc convertTriVal2BigInt(tpl: TriVal): BigInt =
+  result = oneBigInt
+  let (x2, x3, x5) = tpl
+  for _ in 1 .. x2: result.shladd 1, zeroBigInt
+  for _ in 1 .. x3: result.shladd 1, result
+  for _ in 1 .. x5: result.shladd 2, result
+
+const lb2 = 1.0'f64
+const lb3 = 3.0'f64.log2
+const lb5 = 5.0'f64.log2
+
+proc mul2(me: LogRep): LogRep =
+  let (lr, tpl) = me; let (x2, x3, x5) = tpl
+  (lr + lb2, (x2 + 1, x3, x5))
+
+proc mul3(me: LogRep): LogRep =
+  let (lr, tpl) = me; let (x2, x3, x5) = tpl
+  (lr + lb3, (x2, x3 + 1, x5))
+
+proc mul5(me: LogRep): LogRep =
+  let (lr, tpl) = me; let (x2, x3, x5) = tpl
+  (lr + lb5, (x2, x3, x5 + 1))
+
+type
+  LazyListObj = object
+    hd: LogRep
+    tlf: proc(): LazyList {.closure.}
+    tl: LazyList
+  LazyList = ref LazyListObj
+
+var destroyed = 0
+
+proc `=destroy`(ll: var LazyListObj) =
+  destroyed += 1
+  if ll.tlf == nil and ll.tl == nil: return
+
+  when defined(trace20):
+    echo "destroying:  ", (destroyed, ll.hd[1].convertTriVal2BigInt)
+  if ll.tlf != nil: ll.tlf.`=destroy`
+  if ll.tl != nil: ll.tl.`=destroy`
+  #wasMoved(ll)
+
+proc rest(ll: LazyList): LazyList = # not thread-safe; needs lock on thunk
+  if ll.tlf != nil: ll.tl = ll.tlf(); ll.tlf = nil
+  ll.tl
+
+var created = 0
+iterator hammings(until: int): TriVal =
+  proc merge(x, y: LazyList): LazyList =
+    let xh = x.hd; let yh = y.hd; created += 1
+    when defined(trace20):
+      echo "merge create:  ", (created - 1, (if xh < yh: xh else: yh)[1].convertTriVal2BigInt)
+    if xh < yh: LazyList(hd: xh, tlf: proc(): auto = merge x.rest, y)
+    else: LazyList(hd: yh, tlf: proc(): auto = merge x, y.rest)
+  proc smult(mltf: LogRepf; s: LazyList): LazyList =
+    proc smults(ss: LazyList): LazyList =
+      when defined(trace20):
+        echo "mult create:  ", (created, ss.hd.mltf[1].convertTriVal2BigInt)
+      created += 1; LazyList(hd: ss.hd.mltf, tlf: proc(): auto = ss.rest.smults)
+    s.smults
+  proc unnsm(s: LazyList, mltf: LogRepf): LazyList =
+    var r: LazyList = nil
+    when defined(trace20):
+      echo "first create:  ", (created, one[1].convertTriVal2BigInt)
+    let frst = LazyList(hd: one, tlf: proc(): LazyList = r); created += 1
+    r = if s == nil: smult(mltf, frst) else: s.merge smult(mltf, frst)
+    r
+  yield one[1]
+  var hmpll: LazyList = ((nil.unnsm mul5).unnsm mul3).unnsm mul2
+  for _ in 2 .. until:
+    yield hmpll.hd[1]; hmpll = hmpll.rest # almost forever
+
+proc main =
+  var s = ""
+  for h in hammings(20): s &= $h.convertTrival2BigInt & " "
+  doAssert s == "1 2 3 4 5 6 8 9 10 12 15 16 18 20 24 25 27 30 32 36 ",
+           "Algorithmic error finding first 20 Hamming numbers!!!"
+
+  when not defined(trace20):
+    var lsth: TriVal
+    for h in hammings(200): lsth = h
+    doAssert $lsth.convertTriVal2BigInt == "16200",
+             "Algorithmic error finding 200th Hamming number!!!"
+
+let mem = getOccupiedMem()
+main()
+GC_FullCollect()
+let mb = getOccupiedMem() - mem
+doAssert mb == 0, "Found memory leak of " & $mb & " bytes!!!"
+
+echo getAllocStats()
+echo "created ", created, " destroyed ", destroyed
diff --git a/tests/arc/thamming_thinout.nim b/tests/arc/thamming_thinout.nim
new file mode 100644
index 000000000..65c34ef49
--- /dev/null
+++ b/tests/arc/thamming_thinout.nim
@@ -0,0 +1,183 @@
+discard """
+  cmd: "nim c --gc:orc -d:nimThinout -r $file"
+  output: '''The first 20 hammings are:  1 2 3 4 5 MEM IS 0'''
+"""
+
+# test Nim Hamming Number Lazy List algo with reference counts and not...
+# compile with "-d:release -d:danger" and test with various
+# memory managment GC's, allocators, threading, etc.
+
+from times import epochTime
+from math import log2
+
+# implement our own basic BigInt so the bigints library isn't necessary...
+type
+  BigInt = object
+    digits: seq[uint32]
+let zeroBigInt = BigInt(digits: @[ 0'u32 ])
+let oneBigInt = BigInt(digits: @[ 1'u32 ])
+
+proc shladd(bi: var BigInt; n: int; a: BigInt) =
+  var cry = 0'u64
+  for i in 0 ..< min(bi.digits.len, a.digits.len):
+    cry += (bi.digits[i].uint64 shl n) + a.digits[i].uint64
+    bi.digits[i] = cry.uint32
+    cry = cry shr 32
+  if cry > 0'u64:
+    bi.digits.add cry.uint32
+
+proc `$`(x: BigInt): string =
+  if x.digits.len == 0 or (x.digits.len == 1 and x.digits[0] == 0'u32):
+    return "0"
+  var n = x
+  var msd = n.digits.high
+  result = ""
+  while msd >= 0:
+    if n.digits[msd] == 0'u32: msd.dec; continue
+    var brw = 0.uint64
+    for i in countdown(msd, 0):
+      let dvdnd = n.digits[i].uint64 + (brw shl 32)
+      let q = dvdnd div 10'u64; brw = dvdnd - q*10'u64; n.digits[i] = q.uint32
+    result &= $brw
+  for i in 0 .. result.high shr 1:
+    let tmp = result[^(i + 1)]
+    result[^(i + 1)] = result[i]
+    result[i] = tmp
+
+proc convertTrival2BigInt(tpl: (uint32, uint32, uint32)): BigInt =
+  result = oneBigInt
+  let (x2, x3, x5) = tpl
+  for _ in 1 .. x2: result.shladd 1, zeroBigInt
+  for _ in 1 .. x3: result.shladd 1, result
+  for _ in 1 .. x5: result.shladd 2, result
+
+type LogRep = (float64, (uint32, uint32, uint32))
+type LogRepf = proc(x: LogRep): LogRep
+const one: LogRep = (0.0f64, (0u32, 0u32, 0u32))
+proc `<`(me: LogRep, othr: LogRep): bool = me[0] < othr[0]
+
+const lb2 = 1.0'f64
+const lb3 = 3.0'f64.log2
+const lb5 = 5.0'f64.log2
+
+proc mul2(me: LogRep): LogRep =
+  let (lr, tpl) = me; let (x2, x3, x5) = tpl
+  (lr + lb2, (x2 + 1, x3, x5))
+
+proc mul3(me: LogRep): LogRep =
+  let (lr, tpl) = me; let (x2, x3, x5) = tpl
+  (lr + lb3, (x2, x3 + 1, x5))
+
+proc mul5(me: LogRep): LogRep =
+  let (lr, tpl) = me; let (x2, x3, x5) = tpl
+  (lr + lb5, (x2, x3, x5 + 1))
+
+type
+  LazyList = ref object
+    hd: LogRep
+    tlf: proc(): LazyList {.closure.}
+    tl: LazyList
+
+proc rest(ll: LazyList): LazyList = # not thread-safe; needs lock on thunk
+  if ll.tlf != nil:
+    ll.tl = ll.tlf()
+    ll.tlf = nil
+  ll.tl
+
+iterator hamming(until: int): (uint32, uint32, uint32) =
+  proc merge(x, y: LazyList): LazyList =
+    let xh = x.hd
+    let yh = y.hd
+    if xh < yh: LazyList(hd: xh, tlf: proc(): auto = merge x.rest, y)
+    else: LazyList(hd: yh, tlf: proc(): auto = merge x, y.rest)
+  proc smult(mltf: LogRepf; s: LazyList): LazyList =
+    proc smults(ss: LazyList): LazyList =
+      LazyList(hd: ss.hd.mltf, tlf: proc(): auto = ss.rest.smults)
+    s.smults
+  proc unnsm(s: LazyList, mltf: LogRepf): LazyList =
+    var r: LazyList = nil
+    let frst = LazyList(hd: one, tlf: proc(): LazyList = r)
+    r = if s == nil: smult mltf, frst else: s.merge smult(mltf, frst)
+    r
+  var hmpll: LazyList = ((nil.unnsm mul5).unnsm mul3).unnsm mul2
+  #  var hmpll: LazyList = nil; for m in [mul5, mul3, mul2]: echo one.m # ; hmpll = unnsm(hmpll, m)
+  yield one[1]
+  var cnt = 1
+  while hmpll != nil:
+    yield hmpll.hd[1]
+    hmpll = hmpll.rest # almost forever
+    cnt.inc
+    if cnt > until: break
+  #when declared(thinout):
+  thinout(hmpll)
+
+proc main =
+  stdout.write "The first 20 hammings are:  "
+  for h in hamming(4):
+    write stdout, h.convertTrival2BigInt, " "
+
+  for h in hamming(200):
+    discard h.convertTrival2BigInt
+
+let mem = getOccupiedMem()
+main()
+echo "MEM IS ", getOccupiedMem() - mem
+
+#[
+result = (smults, :envP.:up)(rest(:envP.ss2))
+
+proc anon =
+  var
+    :tmpD_284230
+    :tmpD_284233
+    :tmpD_284236
+  try:
+    `=sink_283407`(result_283502,
+      `=sink_283927`(:tmpD_284236, (smults_283495,
+        wasMoved_284234(:tmpD_284233)
+        `=_284014`(:tmpD_284233, :envP_283898.:up_283899)
+        :tmpD_284233))
+      :tmpD_284236(
+      `=sink_283407`(:tmpD_284230, rest_283366(:envP_283898.ss2_-283497))
+      :tmpD_284230))
+  finally:
+    `=destroy_283914`(:tmpD_284236)
+    `=destroy_283388`(:tmpD_284230)
+
+proc smuls(ss: LazyList_283350; :envP_283891): LazyList_283350  =
+  var :env_283913
+  try:
+    `=destroy_283951`(:env_283913)
+    internalNew_43643(:env_283913)
+    `=_283401`(:env_283913.ss2_-283497, ss_283497)
+    :env_283913.:up_283899 = :envP_283891
+    `=sink_283407`(result_283498, LazyList_283350(hd_283353: :envP_283891.mltf1_-283492(
+        :env_283913.ss2_-283497.hd_283353), tlf_283356: (:anonymous_283499,
+      let blitTmp_284227 = :env_283913
+      wasMoved_284228(:env_283913)
+      blitTmp_284227)))
+  finally:
+    `=destroy_283951`(:env_283913)
+
+proc smul =
+  var
+    :env_283969
+    :tmpD_284220
+  try:
+    `=destroy_284008`(:env_283969)
+    internalNew_43643(:env_283969)
+    `=_283976`(:env_283969.mltf1_-283492, mltf_283492)
+    proc smults(ss: LazyList_283350; :envP_283891): LazyList_283350 =
+      result_283498 = LazyList_283350(hd_283353: mltf_283492(ss_283497.hd_283353), tlf_283356: proc (
+          :envP_283898): auto_43100 = result_283502 = smults_283495(rest_283366(ss_283497)))
+
+    `=sink_283407`(result_283494,
+      `=sink_283927`(:tmpD_284220, (smults_283495,
+        let blitTmp_284218 = :env_283969
+        wasMoved_284219(:env_283969)
+        blitTmp_284218))
+      :tmpD_284220(s_283493))
+  finally:
+    `=destroy_283914`(:tmpD_284220)
+    `=destroy_284008`(:env_283969)
+]#
diff --git a/tests/arc/thard_alignment.nim b/tests/arc/thard_alignment.nim
new file mode 100644
index 000000000..30cfddb05
--- /dev/null
+++ b/tests/arc/thard_alignment.nim
@@ -0,0 +1,148 @@
+discard """
+disabled: "arm64"
+cmd: "nim c --mm:arc -u:nimPreviewNonVarDestructor $file"
+output: "y"
+"""
+
+# TODO: fixme: investigate why it failed with non-var destructors
+
+{.passC: "-march=native".}
+
+proc isAlignedCheck(p: pointer, alignment: int) = 
+  doAssert (cast[uint](p) and uint(alignment - 1)) == 0
+
+proc isAlignedCheck[T](p: ref T, alignment: int) = 
+  isAlignedCheck(cast[pointer](p), alignment)
+
+type
+  m256d {.importc: "__m256d", header: "immintrin.h".} = object
+
+proc set1(x: float): m256d {.importc: "_mm256_set1_pd", header: "immintrin.h".}
+func `+`(a,b: m256d): m256d {.importc: "_mm256_add_pd", header: "immintrin.h".}
+proc `$`(a: m256d): string =  
+  result = $(cast[ptr float](a.addr)[])
+
+
+var res: seq[seq[m256d]]
+
+for _ in 1..1000:
+  var x = newSeq[m256d](1)
+  x[0] = set1(1.0) # test if operation causes segfault
+  isAlignedCheck(x[0].addr, alignof(m256d))
+  res.add x
+
+var res2: seq[m256d]
+for i in 1..10000:
+  res2.setLen(res2.len + 1) # check if realloc works
+  isAlignedCheck(res2[0].addr, alignof(m256d))  
+
+proc lambdaGen(a, b: float, z: ref m256d) : auto =
+  var x1 = new(m256d)
+  var x2 = new(m256d)
+  isAlignedCheck(x1, alignof(m256d))
+  isAlignedCheck(x2, alignof(m256d))
+  x1[] = set1(2.0 + a)  
+  x2[] = set1(-23.0 - b)
+  let capturingLambda = proc(x: ref m256d): ref m256d =
+    var cc = new(m256d)
+    var bb = new(m256d)
+    isAlignedCheck(x1, alignof(m256d))
+    isAlignedCheck(x2, alignof(m256d))
+    isAlignedCheck(cc, alignof(m256d))
+    isAlignedCheck(bb, alignof(m256d))
+    isAlignedCheck(z, alignof(m256d))
+        
+    cc[] = x1[] + x1[] + z[]
+    bb[] = x2[] + set1(12.5) + z[]
+    
+    result = new(m256d)
+    isAlignedCheck(result, alignof(m256d))
+    result[] = cc[] + bb[] + x[]
+    
+  return capturingLambda
+
+var xx = new(m256d)
+xx[] = set1(10)
+isAlignedCheck(xx, alignOf(m256d))
+
+let f1 = lambdaGen(2.0 , 2.221, xx)
+let f2 = lambdaGen(-1.226 , 3.5, xx)
+isAlignedCheck(f1(xx), alignOf(m256d))
+isAlignedCheck(f2(xx), alignOf(m256d))
+
+
+#-----------------------------------------------------------------------------
+
+type
+  MyAligned = object of RootObj
+    a{.align: 128.}: float
+
+
+var f: MyAligned
+isAlignedCheck(f.addr, MyAligned.alignOf)
+
+var fref = new(MyAligned)
+isAlignedCheck(fref, MyAligned.alignOf)
+
+var fs: seq[MyAligned]
+var fr: seq[RootRef]
+
+for i in 0..1000:
+  fs.add MyAligned()
+  isAlignedCheck(fs[^1].addr, MyAligned.alignOf)
+  fs[^1].a = i.float
+
+  fr.add new(MyAligned)
+  isAlignedCheck(fr[^1], MyAligned.alignOf)
+  ((ref MyAligned)fr[^1])[].a = i.float
+
+for i in 0..1000:
+  doAssert(fs[i].a == i.float)
+  doAssert(((ref MyAligned)fr[i]).a == i.float)
+
+
+proc lambdaTest2(a: MyAligned, z: ref MyAligned): auto =
+  var x1: MyAligned
+  x1.a = a.a + z.a  
+  var x2: MyAligned
+  x2.a = a.a - z.a
+  let capturingLambda = proc(x: MyAligned): MyAligned =
+    var cc: MyAligned
+    var bb: MyAligned
+    isAlignedCheck(x1.addr, MyAligned.alignOf)
+    isAlignedCheck(x2.addr, MyAligned.alignOf)
+    isAlignedCheck(cc.addr, MyAligned.alignOf)
+    isAlignedCheck(bb.addr, MyAligned.alignOf)
+    isAlignedCheck(z, MyAligned.alignOf)
+        
+    cc.a = x1.a + x1.a + z.a
+    bb.a = x2.a - z.a
+    
+    isAlignedCheck(result.addr, MyAligned.alignOf)
+    result.a = cc.a + bb.a + x2.a
+    
+  return capturingLambda
+
+
+let q1 = lambdaTest2(MyAligned(a: 1.0), (ref MyAligned)(a: 2.0))
+let q2 = lambdaTest2(MyAligned( a: -1.0), (ref MyAligned)(a: -2.0))
+
+isAlignedCheck(rawEnv(q1), MyAligned.alignOf)
+isAlignedCheck(rawEnv(q2), MyAligned.alignOf)
+discard q1(MyAligned(a: 1.0))
+discard q2(MyAligned(a: -1.0))
+
+
+#-----------------------------------------------------------------------------
+
+block:
+  var s: seq[seq[MyAligned]]
+  for len in 0..128:
+    s.add newSeq[MyAligned](len)
+    for i in 0..<len:
+      s[^1][i] = MyAligned(a: 1.0)
+
+    if len > 0:
+      isAlignedCheck(s[^1][0].addr, MyAligned.alignOf)
+  
+echo "y"
diff --git a/tests/arc/thavlak_orc_stress.nim b/tests/arc/thavlak_orc_stress.nim
new file mode 100644
index 000000000..862e1a097
--- /dev/null
+++ b/tests/arc/thavlak_orc_stress.nim
@@ -0,0 +1,414 @@
+discard """
+  cmd: "nim c --gc:orc -d:useMalloc -d:nimStressOrc $file"
+  valgrind: "leaks"
+  output: "done"
+"""
+
+import tables
+import sets
+import net
+
+type
+  BasicBlock = object
+    inEdges: seq[ref BasicBlock]
+    outEdges: seq[ref BasicBlock]
+    name: int
+
+proc newBasicBlock(name: int): ref BasicBlock =
+  new(result)
+  result.inEdges = newSeq[ref BasicBlock]()
+  result.outEdges = newSeq[ref BasicBlock]()
+  result.name = name
+
+proc hash(x: ref BasicBlock): int {.inline.} =
+  result = x.name
+
+type
+  BasicBlockEdge = object
+    fr: ref BasicBlock
+    to: ref BasicBlock
+
+  Cfg = object
+    basicBlockMap: Table[int, ref BasicBlock]
+    edgeList: seq[BasicBlockEdge]
+    startNode: ref BasicBlock
+
+proc newCfg(): Cfg =
+  result.basicBlockMap = initTable[int, ref BasicBlock]()
+  result.edgeList = newSeq[BasicBlockEdge]()
+
+proc createNode(self: var Cfg, name: int): ref BasicBlock =
+  #var node: ref BasicBlock
+  result = self.basicBlockMap.getOrDefault(name)
+  if result == nil:
+    result = newBasicBlock(name)
+    self.basicBlockMap.add name, result
+
+  if self.startNode == nil:
+    self.startNode = result
+
+proc addEdge(self: var Cfg, edge: BasicBlockEdge) =
+  self.edgeList.add(edge)
+
+proc getNumNodes(self: Cfg): int =
+  self.basicBlockMap.len
+
+proc newBasicBlockEdge(cfg: var Cfg, fromName: int, toName: int) =
+  var newEdge = BasicBlockEdge()
+  newEdge.fr = cfg.createNode(fromName)
+  newEdge.to = cfg.createNode(toName)
+  newEdge.fr.outEdges.add(newEdge.to)
+  newEdge.to.inEdges.add(newEdge.fr)
+  cfg.addEdge(newEdge)
+
+type
+  SimpleLoop = object
+    basicBlocks: seq[ref BasicBlock] # TODO: set here
+    children: seq[ref SimpleLoop] # TODO: set here
+    parent: ref SimpleLoop
+    header: ref BasicBlock
+    isRoot: bool
+    isReducible: bool
+    counter: int
+    nestingLevel: int
+    depthLevel: int
+
+proc newSimpleLoop(): ref SimpleLoop =
+  new(result)
+  result.basicBlocks = newSeq[ref BasicBlock]()
+  result.children = newSeq[ref SimpleLoop]()
+  result.parent = nil
+  result.header = nil
+  result.isRoot = false
+  result.isReducible = true
+  result.counter = 0
+  result.nestingLevel = 0
+  result.depthLevel = 0
+
+proc addNode(self: ref SimpleLoop, bb: ref BasicBlock) =
+  self.basicBlocks.add bb
+
+proc addChildLoop(self: ref SimpleLoop, loop: ref SimpleLoop) =
+  self.children.add loop
+
+proc setParent(self: ref SimpleLoop, parent: ref SimpleLoop) =
+  self.parent = parent
+  self.parent.addChildLoop(self)
+
+proc setHeader(self: ref SimpleLoop, bb: ref BasicBlock) =
+  self.basicBlocks.add(bb)
+  self.header = bb
+
+proc setNestingLevel(self: ref SimpleLoop, level: int) =
+  self.nestingLevel = level
+  if level == 0: self.isRoot = true
+
+var loop_counter: int = 0
+
+type
+  Lsg = object
+    loops: seq[ref SimpleLoop]
+    root: ref SimpleLoop
+
+proc createNewLoop(self: var Lsg): ref SimpleLoop =
+  var s = newSimpleLoop()
+  loop_counter += 1
+  s.counter = loop_counter
+  s
+
+proc addLoop(self: var Lsg, l: ref SimpleLoop) =
+  self.loops.add l
+
+proc newLsg(): Lsg =
+  result.loops = newSeq[ref SimpleLoop]()
+  result.root = result.createNewLoop()
+  result.root.setNestingLevel(0)
+  result.addLoop(result.root)
+
+proc getNumLoops(self: Lsg): int =
+  self.loops.len
+
+type
+  UnionFindNode = object
+    parent: ref UnionFindNode
+    bb: ref BasicBlock
+    l: ref SimpleLoop
+    dfsNumber: int
+
+proc newUnionFindNode(): ref UnionFindNode =
+  new(result)
+  result.parent = nil
+  result.bb = nil
+  result.l = nil
+  result.dfsNumber = 0
+
+proc initNode(self: ref UnionFindNode, bb: ref BasicBlock, dfsNumber: int) =
+  self.parent = self
+  self.bb = bb
+  self.dfsNumber = dfsNumber
+
+proc findSet(self: ref UnionFindNode): ref UnionFindNode =
+  var nodeList = newSeq[ref UnionFindNode]()
+  var node = self
+
+  while node != node.parent:
+    var parent = node.parent
+    if parent != parent.parent: nodeList.add node
+    node = parent
+
+  for iter in nodeList: iter.parent = node.parent
+  node
+
+proc union(self: ref UnionFindNode, unionFindNode: ref UnionFindNode) =
+  self.parent = unionFindNode
+
+
+const
+  BB_NONHEADER    = 1 # a regular BB
+  BB_REDUCIBLE    = 2 # reducible loop
+  BB_SELF         = 3 # single BB loop
+  BB_IRREDUCIBLE  = 4 # irreducible loop
+  BB_DEAD         = 5 # a dead BB
+
+  # # Marker for uninitialized nodes.
+  UNVISITED = -1
+
+  # # Safeguard against pathologic algorithm behavior.
+  MAXNONBACKPREDS = (32 * 1024)
+
+type
+  HavlakLoopFinder = object
+    cfg: Cfg
+    lsg: Lsg
+
+proc newHavlakLoopFinder(cfg: sink Cfg, lsg: sink Lsg): HavlakLoopFinder =
+  result.cfg = cfg
+  result.lsg = lsg
+
+proc isAncestor(w: int, v: int, last: seq[int]): bool =
+  w <= v and v <= last[w]
+
+proc dfs(currentNode: ref BasicBlock, nodes: var seq[ref UnionFindNode], number: var Table[ref BasicBlock, int], last: var seq[int], current: int): int =
+  nodes[current].initNode(currentNode, current)
+  number[currentNode] = current
+
+  var lastid = current
+  for target in currentNode.outEdges:
+    if number[target] == UNVISITED:
+      lastid = dfs(target, nodes, number, last, lastid + 1)
+
+  last[number[currentNode]] = lastid
+  return lastid
+
+proc findLoops(self: var HavlakLoopFinder): int =
+  var startNode = self.cfg.startNode
+  if startNode == nil: return 0
+  var size = self.cfg.getNumNodes
+
+  var nonBackPreds = newSeq[HashSet[int]]()
+  var backPreds = newSeq[seq[int]]()
+  var number = initTable[ref BasicBlock, int]()
+  var header = newSeq[int](size)
+  var types = newSeq[int](size)
+  var last = newSeq[int](size)
+  var nodes = newSeq[ref UnionFindNode]()
+
+  for i in 1..size:
+    nonBackPreds.add initHashSet[int](1)
+    backPreds.add newSeq[int]()
+    nodes.add newUnionFindNode()
+
+  # Step a:
+  #   - initialize all nodes as unvisited.
+  #   - depth-first traversal and numbering.
+  #   - unreached BB's are marked as dead.
+  #
+  for v in self.cfg.basicBlockMap.values: number[v] = UNVISITED
+  discard dfs(startNode, nodes, number, last, 0)
+
+  # Step b:
+  #   - iterate over all nodes.
+  #
+  #   A backedge comes from a descendant in the DFS tree, and non-backedges
+  #   from non-descendants (following Tarjan).
+  #
+  #   - check incoming edges 'v' and add them to either
+  #     - the list of backedges (backPreds) or
+  #     - the list of non-backedges (nonBackPreds)
+  #
+  for w in 0 ..< size:
+    header[w] = 0
+    types[w]  = BB_NONHEADER
+
+    var nodeW = nodes[w].bb
+    if nodeW != nil:
+      for nodeV in nodeW.inEdges:
+        var v = number[nodeV]
+        if v != UNVISITED:
+          if isAncestor(w, v, last):
+            backPreds[w].add v
+          else:
+            nonBackPreds[w].incl v
+    else:
+      types[w] = BB_DEAD
+
+  # Start node is root of all other loops.
+  header[0] = 0
+
+  # Step c:
+  #
+  # The outer loop, unchanged from Tarjan. It does nothing except
+  # for those nodes which are the destinations of backedges.
+  # For a header node w, we chase backward from the sources of the
+  # backedges adding nodes to the set P, representing the body of
+  # the loop headed by w.
+  #
+  # By running through the nodes in reverse of the DFST preorder,
+  # we ensure that inner loop headers will be processed before the
+  # headers for surrounding loops.
+
+  for w in countdown(size - 1, 0):
+    # this is 'P' in Havlak's paper
+    var nodePool = newSeq[ref UnionFindNode]()
+
+    var nodeW = nodes[w].bb
+    if nodeW != nil: # dead BB
+      # Step d:
+      for v in backPreds[w]:
+        if v != w:
+          nodePool.add nodes[v].findSet
+        else:
+          types[w] = BB_SELF
+
+      # Copy nodePool to workList.
+      #
+      var workList = newSeq[ref UnionFindNode]()
+      for x in nodePool: workList.add x
+
+      if nodePool.len != 0: types[w] = BB_REDUCIBLE
+
+      # work the list...
+      #
+      while workList.len > 0:
+        var x = workList[0]
+        workList.del(0)
+
+        # Step e:
+        #
+        # Step e represents the main difference from Tarjan's method.
+        # Chasing upwards from the sources of a node w's backedges. If
+        # there is a node y' that is not a descendant of w, w is marked
+        # the header of an irreducible loop, there is another entry
+        # into this loop that avoids w.
+        #
+
+        # The algorithm has degenerated. Break and
+        # return in this case.
+        #
+        var nonBackSize = nonBackPreds[x.dfsNumber].len
+        if nonBackSize > MAXNONBACKPREDS: return 0
+
+        for iter in nonBackPreds[x.dfsNumber]:
+          var y = nodes[iter]
+          var ydash = y.findSet
+
+          if not isAncestor(w, ydash.dfsNumber, last):
+            types[w] = BB_IRREDUCIBLE
+            nonBackPreds[w].incl ydash.dfsNumber
+          else:
+            if ydash.dfsNumber != w and not nodePool.contains(ydash):
+              workList.add ydash
+              nodePool.add ydash
+
+      # Collapse/Unionize nodes in a SCC to a single node
+      # For every SCC found, create a loop descriptor and link it in.
+      #
+      if (nodePool.len > 0) or (types[w] == BB_SELF):
+        var l = self.lsg.createNewLoop
+
+        l.setHeader(nodeW)
+        l.isReducible = types[w] != BB_IRREDUCIBLE
+
+        # At this point, one can set attributes to the loop, such as:
+        #
+        # the bottom node:
+        #    iter  = backPreds(w).begin();
+        #    loop bottom is: nodes(iter).node;
+        #
+        # the number of backedges:
+        #    backPreds(w).size()
+        #
+        # whether this loop is reducible:
+        #    types(w) != BB_IRREDUCIBLE
+        #
+        nodes[w].l = l
+
+        for node in nodePool:
+          # Add nodes to loop descriptor.
+          header[node.dfsNumber] = w
+          node.union(nodes[w])
+
+          # Nested loops are not added, but linked together.
+          var node_l = node.l
+          if node_l != nil:
+            node_l.setParent(l)
+          else:
+            l.addNode(node.bb)
+
+        self.lsg.addLoop(l)
+
+  result = self.lsg.getNumLoops
+
+
+type
+  LoopTesterApp = object
+    cfg: Cfg
+    lsg: Lsg
+
+proc newLoopTesterApp(): LoopTesterApp =
+  result.cfg = newCfg()
+  result.lsg = newLsg()
+
+proc buildDiamond(self: var LoopTesterApp, start: int): int =
+  var bb0 = start
+  newBasicBlockEdge(self.cfg, bb0, bb0 + 1)
+  newBasicBlockEdge(self.cfg, bb0, bb0 + 2)
+  newBasicBlockEdge(self.cfg, bb0 + 1, bb0 + 3)
+  newBasicBlockEdge(self.cfg, bb0 + 2, bb0 + 3)
+  result = bb0 + 3
+
+proc buildConnect(self: var LoopTesterApp, start1: int, end1: int) =
+  newBasicBlockEdge(self.cfg, start1, end1)
+
+proc buildStraight(self: var LoopTesterApp, start: int, n: int): int =
+  for i in 0..n-1:
+    self.buildConnect(start + i, start + i + 1)
+  result = start + n
+
+proc buildBaseLoop(self: var LoopTesterApp, from1: int): int =
+  var header   = self.buildStraight(from1, 1)
+  var diamond1 = self.buildDiamond(header)
+  var d11      = self.buildStraight(diamond1, 1)
+  var diamond2 = self.buildDiamond(d11)
+  var footer   = self.buildStraight(diamond2, 1)
+
+  self.buildConnect(diamond2, d11)
+  self.buildConnect(diamond1, header)
+  self.buildConnect(footer, from1)
+  result = self.buildStraight(footer, 1)
+
+proc run(self: var LoopTesterApp) =
+  discard self.cfg.createNode(0)
+  discard self.buildBaseLoop(0)
+  discard self.cfg.createNode(1)
+  self.buildConnect(0, 2)
+
+  for i in 1..8:
+    # yes, it's 8 and it's correct.
+    var h = newHavlakLoopFinder(self.cfg, newLsg())
+    discard h.findLoops()
+
+  echo "done"
+
+var l = newLoopTesterApp()
+l.run()
diff --git a/tests/arc/theavy_recursion.nim b/tests/arc/theavy_recursion.nim
new file mode 100644
index 000000000..9813331f4
--- /dev/null
+++ b/tests/arc/theavy_recursion.nim
@@ -0,0 +1,43 @@
+discard """
+  output: "yay"
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #15122
+
+import tables
+
+type
+  BENodeKind* = enum
+    tkEof,
+    tkBytes,
+    tkList,
+    tkDict
+
+  BENode* = object
+    case kind: BENodeKind
+    of tkBytes: strVal: string
+    of tkList: listVal: seq[BENode]
+    of tkDict: dictVal*: Table[string, BENode]
+    else:
+      discard
+
+proc unused(s: string): BENode =
+  # bad:
+  result = BENode(kind: tkBytes, strVal: "abc")
+
+proc main =
+  var data = {
+    "examples": {
+      "values": BENode(
+        kind: tkList,
+        listVal: @[BENode(kind: tkBytes, strVal: "test")]
+      )
+    }.toTable()
+  }.toTable()
+
+  # For ARC listVal is empty for some reason
+  doAssert data["examples"]["values"].listVal[0].strVal == "test"
+
+main()
+echo "yay"
diff --git a/tests/arc/timportedobj.nim b/tests/arc/timportedobj.nim
new file mode 100644
index 000000000..9a88a9468
--- /dev/null
+++ b/tests/arc/timportedobj.nim
@@ -0,0 +1,14 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+  action: "compile"
+"""
+
+# bug #13269
+
+import posix
+proc foo*() =
+  var last = newSeq[Stat]()
+  var next = last
+  for i in 0..3:
+    last = next
+foo()
diff --git a/tests/arc/titeration_doesnt_copy.nim b/tests/arc/titeration_doesnt_copy.nim
new file mode 100644
index 000000000..e510a6eff
--- /dev/null
+++ b/tests/arc/titeration_doesnt_copy.nim
@@ -0,0 +1,67 @@
+discard """
+  output: "true"
+"""
+
+type
+  Idx = object
+    i: int
+  Node = object
+    n: int
+    next: seq[Idx]
+  FooBar = object
+    s: seq[Node]
+
+proc `=copy`(dest: var Idx; source: Idx) {.error.}
+proc `=copy`(dest: var Node; source: Node) {.error.}
+proc `=copy`(dest: var FooBar; source: FooBar) {.error.}
+
+proc doSomething(ss: var seq[int], s: FooBar) =
+  for i in 0 .. s.s.len-1:
+    for elm in items s.s[i].next:
+      ss.add s.s[elm.i].n
+
+when isMainModule:
+  const foo = FooBar(s: @[Node(n: 1, next: @[Idx(i: 0)])])
+  var ss: seq[int]
+  doSomething(ss, foo)
+  echo ss == @[1]
+
+from sequtils import mapIt
+from strutils import join
+
+proc toBinSeq*(b: uint8): seq[uint8] =
+  ## Return binary sequence from each bits of uint8.
+  runnableExamples:
+    from sequtils import repeat
+    doAssert 0'u8.toBinSeq == 0'u8.repeat(8)
+    doAssert 0b1010_1010.toBinSeq == @[1'u8, 0, 1, 0, 1, 0, 1, 0]
+  result = @[]
+  var c = b
+  for i in 1..8:
+    result.add (uint8(c and 0b1000_0000) shr 7)
+    c = c shl 1
+
+proc toBinString*(data: openArray[uint8], col: int): string =
+  ## Return binary string from each bits of uint8.
+  runnableExamples:
+    doAssert @[0b0000_1111'u8, 0b1010_1010].toBinString(8) == "0000111110101010"
+    doAssert @[0b1000_0000'u8, 0b0000_0000].toBinString(1) == "10"
+  result = ""
+  for b in items data.mapIt(it.toBinSeq.mapIt(it.`$`[0].char)):
+    for i, c in b:
+      if i < col:
+        result.add c
+
+doAssert @[0b0000_1111'u8, 0b1010_1010].toBinString(8) == "0000111110101010"
+doAssert @[0b1000_0000'u8, 0b0000_0000].toBinString(1) == "10"
+
+block: # bug #23982
+  iterator `..`(a, b: ptr int16): ptr int16 = discard
+  var a: seq[int16] #; let p = a[0].addr
+  var b: seq[ptr int16]
+
+  try:
+    for x in a[0].addr .. b[1]: # `p .. b[1]` works
+      discard
+  except IndexDefect:
+    discard
diff --git a/tests/arc/tkeys_lent.nim b/tests/arc/tkeys_lent.nim
new file mode 100644
index 000000000..2c92350b1
--- /dev/null
+++ b/tests/arc/tkeys_lent.nim
@@ -0,0 +1,17 @@
+discard """
+  output: '''{"string": 2}'''
+  cmd: "nim c --gc:orc $file"
+"""
+
+import tables
+
+proc use(x: int) = echo x
+
+proc main =
+  var tab = {"string": 1}.toTable
+  for keyAAA in tab.keys():
+    template alias(): untyped = tab[keyAAA]
+    alias() = 2
+  echo tab
+
+main()
diff --git a/tests/arc/tmalloc.nim b/tests/arc/tmalloc.nim
new file mode 100644
index 000000000..1d72646c8
--- /dev/null
+++ b/tests/arc/tmalloc.nim
@@ -0,0 +1,16 @@
+discard """
+  matrix: "--mm:arc -d:useMalloc; --mm:arc"
+"""
+
+block: # bug #22058
+  template foo(): auto =
+    {.noSideEffect.}:
+      newSeq[byte](1)
+
+  type V = object
+    v: seq[byte]
+
+  proc bar(): V =
+    V(v: foo())
+
+  doAssert bar().v == @[byte(0)]
diff --git a/tests/arc/tmarshal.nim b/tests/arc/tmarshal.nim
new file mode 100644
index 000000000..f7ec6e2c5
--- /dev/null
+++ b/tests/arc/tmarshal.nim
@@ -0,0 +1,140 @@
+discard """
+  cmd: "nim c --gc:orc $file"
+  output: '''
+{"age": 12, "bio": "Я Cletus", "blob": [65, 66, 67, 128], "name": "Cletus"}
+true
+true
+alpha 100
+omega 200
+0'''
+"""
+
+import marshal
+
+template testit(x) = discard $$to[typeof(x)]($$x)
+
+var x: array[0..4, array[0..4, string]] = [
+  ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
+  ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
+  ["test", "1", "2", "3", "4"]]
+
+proc blockA =
+  testit(x)
+  var test2: tuple[name: string, s: int] = ("tuple test", 56)
+  testit(test2)
+
+blockA()
+
+type
+  TE = enum
+    blah, blah2
+
+  TestObj = object
+    test, asd: int
+    case test2: TE
+    of blah:
+      help: string
+    else:
+      discard
+
+  PNode = ref TNode
+  TNode = object
+    next, prev: PNode
+    data: string
+
+proc buildList(): PNode =
+  new(result)
+  new(result.next)
+  new(result.prev)
+  result.data = "middle"
+  result.next.data = "next"
+  result.prev.data = "prev"
+  result.next.next = result.prev
+  result.next.prev = result
+  result.prev.next = result
+  result.prev.prev = result.next
+
+proc blockB =
+  var test3: TestObj
+  test3.test = 42
+  test3.test2 = blah
+  testit(test3)
+
+  var test4: ref tuple[a, b: string]
+  new(test4)
+  test4.a = "ref string test: A"
+  test4.b = "ref string test: B"
+  testit(test4)
+
+  var test5 = @[(0,1),(2,3),(4,5)]
+  testit(test5)
+
+  var test7 = buildList()
+  testit(test7)
+
+  var test6: set[char] = {'A'..'Z', '_'}
+  testit(test6)
+
+blockB()
+
+# bug #1352
+
+type
+  Entity = object of RootObj
+    name: string
+
+  Person = object of Entity
+    age: int
+    bio: string
+    blob: string
+
+proc blockC =
+  var instance1 = Person(name: "Cletus", age: 12,
+                        bio: "Я Cletus",
+                        blob: "ABC\x80")
+  echo($$instance1)
+  echo(to[Person]($$instance1).bio == instance1.bio) # true
+  echo(to[Person]($$instance1).blob == instance1.blob) # true
+
+blockC()
+
+# bug 5757
+
+type
+  Something = object
+    x: string
+    y: int
+
+proc blockD =
+  var data1 = """{"x": "alpha", "y": 100}"""
+  var data2 = """{"x": "omega", "y": 200}"""
+
+  var r = to[Something](data1)
+
+  echo r.x, " ", r.y
+
+  r = to[Something](data2)
+
+  echo r.x, " ", r.y
+
+blockD()
+
+type
+  Foo = object
+    a1: string
+    a2: string
+    a3: seq[string]
+    a4: seq[int]
+    a5: seq[int]
+    a6: seq[int]
+
+proc blockE =
+  var foo = Foo(a2: "", a4: @[], a6: @[1])
+  foo.a6.setLen 0
+  doAssert $$foo == """{"a1": "", "a2": "", "a3": [], "a4": [], "a5": [], "a6": []}"""
+  testit(foo)
+
+blockE()
+
+GC_fullCollect()
+echo getOccupiedMem()
diff --git a/tests/arc/tmove_regression.nim b/tests/arc/tmove_regression.nim
new file mode 100644
index 000000000..7d9a867c3
--- /dev/null
+++ b/tests/arc/tmove_regression.nim
@@ -0,0 +1,22 @@
+discard """
+  output: '''/1/2
+/1
+/
+'''
+""""
+
+# bug #22001
+
+import std / [os, strutils]
+
+proc finOp2(path, name: string): (string, File) = # Find & open FIRST `name`
+  var current = path
+  while true:
+    if current.isRootDir: break # <- current=="" => current.isRootDir
+    current = current.parentDir
+    let dir = current
+    echo dir.replace('\\', '/')  # Commenting out try/except below hides bug
+    try: result[0] = dir/name; result[1] = open(result[0]); return
+    except CatchableError: discard
+
+discard finOp2("/1/2/3", "4")  # All same if this->inside a proc
diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim
new file mode 100644
index 000000000..8ad7c955c
--- /dev/null
+++ b/tests/arc/tmovebug.nim
@@ -0,0 +1,841 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+  output: '''5
+(w: 5)
+(w: -5)
+c.text = hello
+c.text = hello
+p.text = hello
+p.toks = @["hello"]
+c.text = hello
+c[].text = hello
+pA.text = hello
+pA.toks = @["hello"]
+c.text = hello
+c.text = hello
+pD.text = hello
+pD.toks = @["hello"]
+c.text = hello
+c.text = hello
+pOD.text = hello
+pOD.toks = @["hello"]
+fff
+fff
+2
+fff
+fff
+2
+fff
+fff
+2
+mmm
+fff
+fff
+fff
+3
+mmm
+sink me (sink)
+assign me (not sink)
+sink me (not sink)
+sinked and not optimized to a bitcopy
+sinked and not optimized to a bitcopy
+sinked and not optimized to a bitcopy
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+100
+hey
+hey
+(a: "a", b: 2)
+ho
+(a: "b", b: 3)
+(b: "b", a: 2)
+ho
+(b: "a", a: 3)
+hey
+break
+break
+hey
+ho
+hey
+ho
+ho
+king
+live long; long live
+king
+hi
+try
+bye
+()
+()
+()
+1
+destroy
+1
+destroy
+1
+destroy
+copy (self-assign)
+1
+destroy
+1
+destroy
+1
+destroy
+destroy
+copy
+@[(f: 2), (f: 2), (f: 3)]
+destroy
+destroy
+destroy
+sink
+sink
+destroy
+copy
+(f: 1)
+destroy
+destroy
+part-to-whole assigment:
+sink
+(children: @[])
+destroy
+sink
+(children: @[])
+destroy
+copy
+destroy
+(f: 1)
+destroy
+'''
+"""
+
+# move bug
+type
+  TMyObj = object
+    p: pointer
+    len: int
+
+var destroyCounter = 0
+
+proc `=destroy`(o: var TMyObj) =
+  if o.p != nil:
+    dealloc o.p
+    o.p = nil
+    inc destroyCounter
+
+proc `=`(dst: var TMyObj, src: TMyObj) =
+  `=destroy`(dst)
+  dst.p = alloc(src.len)
+  dst.len = src.len
+
+proc `=sink`(dst: var TMyObj, src: TMyObj) =
+  `=destroy`(dst)
+  dst.p = src.p
+  dst.len = src.len
+
+type
+  TObjKind = enum Z, A, B
+  TCaseObj = object
+    case kind: TObjKind
+    of Z: discard
+    of A:
+      x1: int # this int plays important role
+      x2: TMyObj
+    of B:
+      y: TMyObj
+
+proc use(a: TCaseObj) = discard
+
+proc moveBug(i: var int) =
+  var a: array[2, TCaseObj]
+  a[i] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5))) # 1
+  a[i+1] = a[i] # 2
+  inc i
+  use(a[i-1])
+
+var x = 0
+moveBug(x)
+
+proc moveBug2(): (TCaseObj, TCaseObj) =
+  var a: array[2, TCaseObj]
+  a[0] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5)))
+  a[1] = a[0] # can move 3
+  result[0] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5))) # 4
+  result[1] = result[0] # 5
+
+proc main =
+  discard moveBug2()
+
+main()
+echo destroyCounter
+
+# bug #13314
+
+type
+  O = object
+    v: int
+  R = ref object
+    w: int
+
+proc `$`(r: R): string = $r[]
+
+proc tbug13314 =
+  var t5 = R(w: 5)
+  var execute = proc () =
+    echo t5
+
+  execute()
+  t5.w = -5
+  execute()
+
+tbug13314()
+
+#-------------------------------------------------------------------------
+# bug #13368
+
+import strutils
+proc procStat() =
+  for line in @["a b", "c d", "e f"]:
+    let cols = line.splitWhitespace(maxSplit=1)
+    let x = cols[0]
+    let (nm, rest) = (cols[0], cols[1])
+procStat()
+
+
+# bug #14269
+
+import sugar, strutils
+
+type
+  Cursor = object
+    text: string
+  Parsed = object
+    text: string
+    toks: seq[string]
+
+proc tokenize(c: var Cursor): seq[string] =
+  dump c.text
+  return c.text.splitWhitespace()
+
+proc parse(): Parsed =
+  var c = Cursor(text: "hello")
+  dump c.text
+  return Parsed(text: c.text, toks: c.tokenize) # note: c.tokenized uses c.text
+
+let p = parse()
+dump p.text
+dump p.toks
+
+
+proc tokenizeA(c: ptr Cursor): seq[string] =
+  dump c[].text
+  return c[].text.splitWhitespace()
+
+proc parseA(): Parsed =
+  var c = Cursor(text: "hello")
+  dump c.text
+  return Parsed(text: c.text, toks: c.addr.tokenizeA) # note: c.tokenized uses c.text
+
+let pA = parseA()
+dump pA.text
+dump pA.toks
+
+
+proc tokenizeD(c: Cursor): seq[string] =
+  dump c.text
+  return c.text.splitWhitespace()
+
+proc parseD(): Parsed =
+  var c = cast[ptr Cursor](alloc0(sizeof(Cursor)))
+  c[] = Cursor(text: "hello")
+  dump c.text
+  return Parsed(text: c.text, toks: c[].tokenizeD) # note: c.tokenized uses c.text
+
+let pD = parseD()
+dump pD.text
+dump pD.toks
+
+# Bug would only pop up with owned refs
+proc tokenizeOD(c: Cursor): seq[string] =
+  dump c.text
+  return c.text.splitWhitespace()
+
+proc parseOD(): Parsed =
+  var c = new Cursor
+  c[] = Cursor(text: "hello")
+  dump c.text
+  return Parsed(text: c.text, toks: c[].tokenizeOD) # note: c.tokenized uses c.text
+
+let pOD = parseOD()
+dump pOD.text
+dump pOD.toks
+
+when false:
+  # Bug would only pop up with owned refs and implicit derefs, but since they don't work together..
+  {.experimental: "implicitDeref".}
+  proc tokenizeOHD(c: Cursor): seq[string] =
+    dump c.text
+    return c.text.splitWhitespace()
+
+  proc parseOHD(): Parsed =
+    var c = new Cursor
+    c[] = Cursor(text: "hello")
+    dump c.text
+    return Parsed(text: c.text, toks: c.tokenizeOHD) # note: c.tokenized uses c.text
+
+  let pOHD = parseOHD()
+  dump pOHD.text
+  dump pOHD.toks
+
+# bug #13456
+
+iterator combinations[T](s: openArray[T], k: int): seq[T] =
+  let n = len(s)
+  assert k >= 0 and k <= n
+  var pos = newSeq[int](k)
+  var current = newSeq[T](k)
+  for i in 0..k-1:
+    pos[k-i-1] = i
+  var done = false
+  while not done:
+    for i in 0..k-1:
+      current[i] = s[pos[k-i-1]]
+    yield current
+    var i = 0
+    while i < k:
+      pos[i] += 1
+      if pos[i] < n-i:
+        for j in 0..i-1:
+          pos[j] = pos[i] + i - j
+        break
+      i += 1
+    if i >= k:
+      break
+
+type
+  UndefEx = object of ValueError
+
+proc main2 =
+  var delayedSyms = @[1, 2, 3]
+  var unp: seq[int]
+  block myb:
+    for a in 1 .. 2:
+      if delayedSyms.len > a:
+        unp = delayedSyms
+        for t in unp.combinations(a + 1):
+          try:
+            var h = false
+            for k in t:
+              echo "fff"
+            if h: continue
+            if true:
+              raise newException(UndefEx, "forward declaration")
+            break myb
+          except UndefEx:
+            echo t.len
+        echo "mmm"
+
+main2()
+
+
+
+type ME = object
+  who: string
+
+proc `=`(x: var ME, y: ME) =
+  if y.who.len > 0: echo "assign ",y.who
+
+proc `=sink`(x: var ME, y: ME) =
+  if y.who.len > 0: echo "sink ",y.who
+
+var dump: ME
+template use(x) = dump = x
+template def(x) = x = dump
+
+var c = true
+
+proc shouldSink() =
+  var x = ME(who: "me (sink)")
+  use(x) # we analyse this
+  if c: def(x)
+  else: def(x)
+  use(x) # ok, with the [else] part.
+
+shouldSink()
+
+dump = ME()
+
+proc shouldNotSink() =
+  var x = ME(who: "me (not sink)")
+  use(x) # we analyse this
+  if c: def(x)
+  use(x) # Not ok without the '[else]'
+
+shouldNotSink()
+
+# bug #14568
+import os
+
+type O2 = object
+  s: seq[int]
+
+proc `=sink`(dest: var O2, src: O2) =
+  echo "sinked and not optimized to a bitcopy"
+
+var testSeq: O2
+
+proc update() =
+  # testSeq.add(0) # uncommenting this line fixes the leak
+  testSeq = O2(s: @[])
+  testSeq.s.add(0)
+
+for i in 1..3:
+  update()
+
+
+# bug #14961
+type
+  Foo = object
+    data: seq[int]
+
+proc initFoo(len: int): Foo =
+  result = (let s = newSeq[int](len); Foo(data: s) )
+
+var f = initFoo(2)
+echo initFoo(2)
+
+proc initFoo2(len: int) =
+  echo   if true:
+             let s = newSeq[int](len); Foo(data: s)
+         else:
+             let s = newSeq[int](len); Foo(data: s)
+
+initFoo2(2)
+
+proc initFoo3(len: int) =
+  echo (block:
+         let s = newSeq[int](len); Foo(data: s))
+
+initFoo3(2)
+
+proc initFoo4(len: int) =
+  echo (let s = newSeq[int](len); Foo(data: s))
+
+initFoo4(2)
+
+proc initFoo5(len: int) =
+  echo (case true
+        of true:
+          let s = newSeq[int](len); Foo(data: s)
+        of false:
+          let s = newSeq[int](len); Foo(data: s))
+
+initFoo5(2)
+
+proc initFoo6(len: int) =
+  echo (block:
+          try:
+            let s = newSeq[int](len); Foo(data: s)
+          finally: discard)
+
+initFoo6(2)
+
+proc initFoo7(len: int) =
+  echo (block:
+          try:
+            raise newException(CatchableError, "sup")
+            let s = newSeq[int](len); Foo(data: s)
+          except CatchableError:
+            let s = newSeq[int](len); Foo(data: s) )
+
+initFoo7(2)
+
+
+# bug #14902
+iterator zip[T](s: openArray[T]): (T, T) =
+  var i = 0
+  while i < 10:
+    yield (s[i mod 2], s[i mod 2 + 1])
+    inc i
+
+var lastMem = int.high
+
+proc leak =
+  const len = 10
+  var x = @[newString(len), newString(len), newString(len)]
+
+  var c = 0
+  for (a, b) in zip(x):
+    let newMem = getOccupiedMem()
+    assert newMem <= lastMem
+    lastMem = newMem
+    c += a.len
+  echo c
+
+leak()
+
+
+proc consume(a: sink string) = echo a
+
+proc weirdScopes =
+  if (let a = "hey"; a.len > 0):
+    echo a
+
+  while (let a = "hey"; a.len > 0):
+    echo a
+    break
+
+  var a = block: (a: "a", b: 2)
+  echo a
+  (discard; a) = (echo "ho"; (a: "b", b: 3))
+  echo a
+
+  var b = try: (b: "b", a: 2)
+          except: raise
+  echo b
+  (discard; b) = (echo "ho"; (b: "a", a: 3))
+  echo b
+
+  var s = "break"
+  consume((echo "hey"; s))
+  echo s
+
+  echo (block:
+          var a = "hey"
+          (echo "hey"; "ho"))
+
+  var b2 = "ho"
+  echo (block:
+          var a = "hey"
+          (echo "hey"; b2))
+  echo b2
+
+  type status = enum
+    alive
+
+  var king = "king"
+  echo (block:
+          var a = "a"
+          when true:
+            var b = "b"
+            case alive
+            of alive:
+              try:
+                var c = "c"
+                if true:
+                  king
+                else:
+                  "the abyss"
+              except:
+                echo "he ded"
+                "dead king")
+  echo "live long; long live"
+  echo king
+
+weirdScopes()
+
+
+# bug #14985
+proc getScope(): string =
+  if true:
+    return "hi"
+  else:
+    "else"
+
+echo getScope()
+
+proc getScope3(): string =
+  try:
+    "try"
+  except:
+    return "except"
+
+echo getScope3()
+
+proc getScope2(): string =
+  case true
+  of true:
+    return "bye"
+  else:
+    "else"
+
+echo getScope2()
+
+
+#--------------------------------------------------------------------
+#bug  #15609
+
+type
+  Wrapper = object
+    discard
+
+proc newWrapper(): ref Wrapper =
+  new(result)
+  result
+
+
+proc newWrapper2(a: int): ref Wrapper =
+  new(result)
+  if a > 0:
+    result
+  else:
+    new(Wrapper)
+
+
+let w1 = newWrapper()
+echo $w1[]
+
+let w2 = newWrapper2(1)
+echo $w2[]
+
+let w3 = newWrapper2(-1)
+echo $w3[]
+
+
+#--------------------------------------------------------------------
+#self-assignments
+
+# Self-assignments that are not statically determinable will get
+# turned into `=copy` calls as caseBracketExprCopy demonstrates.
+# (`=copy` handles self-assignments at runtime)
+
+type
+  OO = object
+    f: int
+  W = object
+    o: OO
+
+proc `=destroy`(x: var OO) =
+  if x.f != 0:
+    echo "destroy"
+    x.f = 0
+
+proc `=sink`(x: var OO, y: OO) =
+  `=destroy`(x)
+  echo "sink"
+  x.f = y.f
+
+proc `=copy`(x: var OO, y: OO) =
+  if x.f != y.f:
+    `=destroy`(x)
+    echo "copy"
+    x.f = y.f
+  else:
+    echo "copy (self-assign)"
+
+proc caseSym =
+  var o = OO(f: 1)
+  o = o # NOOP
+  echo o.f # "1"
+  # "destroy"
+
+caseSym()
+
+proc caseDotExpr =
+  var w = W(o: OO(f: 1))
+  w.o = w.o # NOOP
+  echo w.o.f # "1"
+  # "destroy"
+
+caseDotExpr()
+
+proc caseBracketExpr =
+  var w = [0: OO(f: 1)]
+  w[0] = w[0] # NOOP
+  echo w[0].f # "1"
+  # "destroy"
+
+caseBracketExpr()
+
+proc caseBracketExprCopy =
+  var w = [0: OO(f: 1)]
+  let i = 0
+  w[i] = w[0] # "copy (self-assign)"
+  echo w[0].f # "1"
+  # "destroy"
+
+caseBracketExprCopy()
+
+proc caseDotExprAddr =
+  var w = W(o: OO(f: 1))
+  w.o = addr(w.o)[] # NOOP
+  echo w.o.f # "1"
+  # "destroy"
+
+caseDotExprAddr()
+
+proc caseBracketExprAddr =
+  var w = [0: OO(f: 1)]
+  addr(w[0])[] = addr(addr(w[0])[])[] # NOOP
+  echo w[0].f # "1"
+  # "destroy"
+
+caseBracketExprAddr()
+
+proc caseNotAConstant =
+  var i = 0
+  proc rand: int =
+    result = i
+    inc i
+  var s = @[OO(f: 1), OO(f: 2), OO(f: 3)]
+  s[rand()] = s[rand()] # "destroy" "copy"
+  echo s # @[(f: 2), (f: 2), (f: 3)]
+
+caseNotAConstant()
+
+proc potentialSelfAssign(i: var int) =
+  var a: array[2, OO]
+  a[i] = OO(f: 1) # turned into a memcopy
+  a[1] = OO(f: 2)
+  a[i+1] = a[i] # This must not =sink, but =copy
+  inc i
+  echo a[i-1] # (f: 1)
+
+potentialSelfAssign (var xi = 0; xi)
+
+
+#--------------------------------------------------------------------
+echo "part-to-whole assigment:"
+
+type
+  Tree = object
+    children: seq[Tree]
+
+  TreeDefaultHooks = object
+    children: seq[TreeDefaultHooks]
+
+proc `=destroy`(x: var Tree) = echo "destroy"
+proc `=sink`(x: var Tree, y: Tree) = echo "sink"
+proc `=copy`(x: var Tree, y: Tree) = echo "copy"
+
+proc partToWholeSeq =
+  var t = Tree(children: @[Tree()])
+  t = t.children[0] # This should be sunk, but with the special transform (tmp = t.children[0]; wasMoved(0); `=sink`(t, tmp))
+
+  var tc = TreeDefaultHooks(children: @[TreeDefaultHooks()])
+  tc = tc.children[0] # Ditto; if this were sunk with the normal transform (`=sink`(t, t.children[0]); wasMoved(t.children[0]))
+  echo tc             #        then it would crash because t.children[0] does not exist after the call to `=sink`
+
+partToWholeSeq()
+
+proc partToWholeSeqRTIndex =
+  var i = 0
+  var t = Tree(children: @[Tree()])
+  t = t.children[i] # See comment in partToWholeSeq
+
+  var tc = TreeDefaultHooks(children: @[TreeDefaultHooks()])
+  tc = tc.children[i] # See comment in partToWholeSeq
+  echo tc
+
+partToWholeSeqRTIndex()
+
+type List = object
+  next: ref List
+
+proc `=destroy`(x: var List) = echo "destroy"
+proc `=sink`(x: var List, y: List) = echo "sink"
+proc `=copy`(x: var List, y: List) = echo "copy"
+
+proc partToWholeUnownedRef =
+  var t = List(next: new List)
+  t = t.next[] # Copy because t.next is not an owned ref, and thus t.next[] cannot be moved
+
+partToWholeUnownedRef()
+
+
+#--------------------------------------------------------------------
+# test that nodes that get copied during the transformation
+# (like dot exprs) don't loose their firstWrite/lastRead property
+
+type
+  OOO = object
+    initialized: bool
+
+  C = object
+    o: OOO
+
+proc `=destroy`(o: var OOO) =
+  doAssert o.initialized, "OOO was destroyed before initialization!"
+
+proc initO(): OOO =
+  OOO(initialized: true)
+
+proc initC(): C =
+  C(o: initO())
+
+proc pair(): tuple[a: C, b: C] =
+  result = (a: initC(), b: initC())# <- when firstWrite tries to find this node to start its analysis it fails, because injectdestructors uses copyTree/shallowCopy
+
+discard pair()
+
+
+# bug #17450
+proc noConsume(x: OO) {.nosinks.} = echo x
+
+proc main3 =
+  var i = 1
+  noConsume:
+    block:
+      OO(f: i)
+
+main3()
+
+# misc
+proc smoltest(x: bool): bool =
+  while true:
+    if true: return x
+
+discard smoltest(true)
+
+# bug #18002
+type
+  TTypeAttachedOp = enum
+    attachedAsgn
+    attachedSink
+    attachedTrace
+
+  PNode = ref object
+    discard
+
+proc genAddrOf(n: PNode) =
+  assert n != nil, "moved?!"
+
+proc atomicClosureOp =
+  let x = PNode()
+
+  genAddrOf:
+    block:
+      x
+
+  case attachedTrace
+  of attachedSink: discard
+  of attachedAsgn: discard
+  of attachedTrace: genAddrOf(x)
+
+atomicClosureOp()
+
+
+template assertEq(a, b: untyped): untyped =
+  block:
+    let
+      aval = a
+      bval = b
+
+    if aval != bval:
+      quit "bug!"
+
+proc convoluted =
+  let _ = (;
+    var val1: string;
+    if true: val1 = "22"
+    true
+  )
+
+  assertEq val1, "22"
+  assertEq val1, "22"
+
+convoluted()
diff --git a/tests/arc/tmovebugcopy.nim b/tests/arc/tmovebugcopy.nim
new file mode 100644
index 000000000..ec4315777
--- /dev/null
+++ b/tests/arc/tmovebugcopy.nim
@@ -0,0 +1,526 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+  output: '''5
+(w: 5)
+(w: -5)
+c.text = hello
+c.text = hello
+p.text = hello
+p.toks = @["hello"]
+c.text = hello
+c[].text = hello
+pA.text = hello
+pA.toks = @["hello"]
+c.text = hello
+c.text = hello
+pD.text = hello
+pD.toks = @["hello"]
+c.text = hello
+c.text = hello
+pOD.text = hello
+pOD.toks = @["hello"]
+fff
+fff
+2
+fff
+fff
+2
+fff
+fff
+2
+mmm
+fff
+fff
+fff
+3
+mmm
+sink me (sink)
+assign me (not sink)
+sink me (not sink)
+sinked and not optimized to a bitcopy
+sinked and not optimized to a bitcopy
+sinked and not optimized to a bitcopy
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+(data: @[0, 0])
+100
+hey
+hey
+(a: "a", b: 2)
+ho
+(a: "b", b: 3)
+(b: "b", a: 2)
+ho
+(b: "a", a: 3)
+hey
+break
+break
+hey
+ho
+hey
+ho
+ho
+king
+live long; long live
+king
+hi
+try
+bye
+'''
+"""
+
+# move bug
+type
+  TMyObj = object
+    p: pointer
+    len: int
+
+var destroyCounter = 0
+
+proc `=destroy`(o: var TMyObj) =
+  if o.p != nil:
+    dealloc o.p
+    o.p = nil
+    inc destroyCounter
+
+proc `=copy`(dst: var TMyObj, src: TMyObj) =
+  `=destroy`(dst)
+  dst.p = alloc(src.len)
+  dst.len = src.len
+
+proc `=sink`(dst: var TMyObj, src: TMyObj) =
+  `=destroy`(dst)
+  dst.p = src.p
+  dst.len = src.len
+
+type
+  TObjKind = enum Z, A, B
+  TCaseObj = object
+    case kind: TObjKind
+    of Z: discard
+    of A:
+      x1: int # this int plays important role
+      x2: TMyObj
+    of B:
+      y: TMyObj
+
+proc use(a: TCaseObj) = discard
+
+proc moveBug(i: var int) =
+  var a: array[2, TCaseObj]
+  a[i] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5))) # 1
+  a[i+1] = a[i] # 2
+  inc i
+  use(a[i-1])
+
+var x = 0
+moveBug(x)
+
+proc moveBug2(): (TCaseObj, TCaseObj) =
+  var a: array[2, TCaseObj]
+  a[0] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5)))
+  a[1] = a[0] # can move 3
+  result[0] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5))) # 4
+  result[1] = result[0] # 5
+
+proc main =
+  discard moveBug2()
+
+main()
+echo destroyCounter
+
+# bug #13314
+
+type
+  O = object
+    v: int
+  R = ref object
+    w: int
+
+proc `$`(r: R): string = $r[]
+
+proc tbug13314 =
+  var t5 = R(w: 5)
+  var execute = proc () =
+    echo t5
+
+  execute()
+  t5.w = -5
+  execute()
+
+tbug13314()
+
+#-------------------------------------------------------------------------
+# bug #13368
+
+import strutils
+proc procStat() =
+  for line in @["a b", "c d", "e f"]:
+    let cols = line.splitWhitespace(maxSplit=1)
+    let x = cols[0]
+    let (nm, rest) = (cols[0], cols[1])
+procStat()
+
+
+# bug #14269
+
+import sugar, strutils
+
+type
+  Cursor = object
+    text: string
+  Parsed = object
+    text: string
+    toks: seq[string]
+
+proc tokenize(c: var Cursor): seq[string] =
+  dump c.text
+  return c.text.splitWhitespace()
+
+proc parse(): Parsed =
+  var c = Cursor(text: "hello")
+  dump c.text
+  return Parsed(text: c.text, toks: c.tokenize) # note: c.tokenized uses c.text
+
+let p = parse()
+dump p.text
+dump p.toks
+
+
+proc tokenizeA(c: ptr Cursor): seq[string] =
+  dump c[].text
+  return c[].text.splitWhitespace()
+
+proc parseA(): Parsed =
+  var c = Cursor(text: "hello")
+  dump c.text
+  return Parsed(text: c.text, toks: c.addr.tokenizeA) # note: c.tokenized uses c.text
+
+let pA = parseA()
+dump pA.text
+dump pA.toks
+
+
+proc tokenizeD(c: Cursor): seq[string] =
+  dump c.text
+  return c.text.splitWhitespace()
+
+proc parseD(): Parsed =
+  var c = cast[ptr Cursor](alloc0(sizeof(Cursor)))
+  c[] = Cursor(text: "hello")
+  dump c.text
+  return Parsed(text: c.text, toks: c[].tokenizeD) # note: c.tokenized uses c.text
+
+let pD = parseD()
+dump pD.text
+dump pD.toks
+
+# Bug would only pop up with owned refs
+proc tokenizeOD(c: Cursor): seq[string] =
+  dump c.text
+  return c.text.splitWhitespace()
+
+proc parseOD(): Parsed =
+  var c = new Cursor
+  c[] = Cursor(text: "hello")
+  dump c.text
+  return Parsed(text: c.text, toks: c[].tokenizeOD) # note: c.tokenized uses c.text
+
+let pOD = parseOD()
+dump pOD.text
+dump pOD.toks
+
+when false:
+  # Bug would only pop up with owned refs and implicit derefs, but since they don't work together..
+  {.experimental: "implicitDeref".}
+  proc tokenizeOHD(c: Cursor): seq[string] =
+    dump c.text
+    return c.text.splitWhitespace()
+
+  proc parseOHD(): Parsed =
+    var c = new Cursor
+    c[] = Cursor(text: "hello")
+    dump c.text
+    return Parsed(text: c.text, toks: c.tokenizeOHD) # note: c.tokenized uses c.text
+
+  let pOHD = parseOHD()
+  dump pOHD.text
+  dump pOHD.toks
+
+# bug #13456
+
+iterator combinations[T](s: openArray[T], k: int): seq[T] =
+  let n = len(s)
+  assert k >= 0 and k <= n
+  var pos = newSeq[int](k)
+  var current = newSeq[T](k)
+  for i in 0..k-1:
+    pos[k-i-1] = i
+  var done = false
+  while not done:
+    for i in 0..k-1:
+      current[i] = s[pos[k-i-1]]
+    yield current
+    var i = 0
+    while i < k:
+      pos[i] += 1
+      if pos[i] < n-i:
+        for j in 0..i-1:
+          pos[j] = pos[i] + i - j
+        break
+      i += 1
+    if i >= k:
+      break
+
+type
+  UndefEx = object of ValueError
+
+proc main2 =
+  var delayedSyms = @[1, 2, 3]
+  var unp: seq[int]
+  block myb:
+    for a in 1 .. 2:
+      if delayedSyms.len > a:
+        unp = delayedSyms
+        for t in unp.combinations(a + 1):
+          try:
+            var h = false
+            for k in t:
+              echo "fff"
+            if h: continue
+            if true:
+              raise newException(UndefEx, "forward declaration")
+            break myb
+          except UndefEx:
+            echo t.len
+        echo "mmm"
+
+main2()
+
+
+
+type ME = object
+  who: string
+
+proc `=copy`(x: var ME, y: ME) =
+  if y.who.len > 0: echo "assign ",y.who
+
+proc `=sink`(x: var ME, y: ME) =
+  if y.who.len > 0: echo "sink ",y.who
+
+var dump: ME
+template use(x) = dump = x
+template def(x) = x = dump
+
+var c = true
+
+proc shouldSink() =
+  var x = ME(who: "me (sink)")
+  use(x) # we analyse this
+  if c: def(x)
+  else: def(x)
+  use(x) # ok, with the [else] part.
+
+shouldSink()
+
+dump = ME()
+
+proc shouldNotSink() =
+  var x = ME(who: "me (not sink)")
+  use(x) # we analyse this
+  if c: def(x)
+  use(x) # Not ok without the '[else]'
+
+shouldNotSink()
+
+# bug #14568
+import os
+
+type O2 = object
+  s: seq[int]
+
+proc `=sink`(dest: var O2, src: O2) =
+  echo "sinked and not optimized to a bitcopy"
+
+var testSeq: O2
+
+proc update() =
+  # testSeq.add(0) # uncommenting this line fixes the leak
+  testSeq = O2(s: @[])
+  testSeq.s.add(0)
+
+for i in 1..3:
+  update()
+
+
+# bug #14961
+type
+  Foo = object
+    data: seq[int]
+
+proc initFoo(len: int): Foo =
+  result = (let s = newSeq[int](len); Foo(data: s) )
+
+var f = initFoo(2)
+echo initFoo(2)
+
+proc initFoo2(len: int) =
+  echo   if true:
+             let s = newSeq[int](len); Foo(data: s)
+         else:
+             let s = newSeq[int](len); Foo(data: s)
+
+initFoo2(2)
+
+proc initFoo3(len: int) =
+  echo (block:
+         let s = newSeq[int](len); Foo(data: s))
+
+initFoo3(2)
+
+proc initFoo4(len: int) =
+  echo (let s = newSeq[int](len); Foo(data: s))
+
+initFoo4(2)
+
+proc initFoo5(len: int) =
+  echo (case true
+        of true:
+          let s = newSeq[int](len); Foo(data: s)
+        of false:
+          let s = newSeq[int](len); Foo(data: s))
+
+initFoo5(2)
+
+proc initFoo6(len: int) =
+  echo (block:
+          try:
+            let s = newSeq[int](len); Foo(data: s)
+          finally: discard)
+
+initFoo6(2)
+
+proc initFoo7(len: int) =
+  echo (block:
+          try:
+            raise newException(CatchableError, "sup")
+            let s = newSeq[int](len); Foo(data: s)
+          except CatchableError:
+            let s = newSeq[int](len); Foo(data: s) )
+
+initFoo7(2)
+
+
+# bug #14902
+iterator zip[T](s: openArray[T]): (T, T) =
+  var i = 0
+  while i < 10:
+    yield (s[i mod 2], s[i mod 2 + 1])
+    inc i
+
+var lastMem = int.high
+
+proc leak =
+  const len = 10
+  var x = @[newString(len), newString(len), newString(len)]
+
+  var c = 0
+  for (a, b) in zip(x):
+    let newMem = getOccupiedMem()
+    assert newMem <= lastMem
+    lastMem = newMem
+    c += a.len
+  echo c
+
+leak()
+
+
+proc consume(a: sink string) = echo a
+
+proc weirdScopes =
+  if (let a = "hey"; a.len > 0):
+    echo a
+
+  while (let a = "hey"; a.len > 0):
+    echo a
+    break
+
+  var a = block: (a: "a", b: 2)
+  echo a
+  (discard; a) = (echo "ho"; (a: "b", b: 3))
+  echo a
+
+  var b = try: (b: "b", a: 2)
+          except: raise
+  echo b
+  (discard; b) = (echo "ho"; (b: "a", a: 3))
+  echo b
+
+  var s = "break"
+  consume((echo "hey"; s))
+  echo s
+
+  echo (block:
+          var a = "hey"
+          (echo "hey"; "ho"))
+
+  var b2 = "ho"
+  echo (block:
+          var a = "hey"
+          (echo "hey"; b2))
+  echo b2
+
+  type status = enum
+    alive
+
+  var king = "king"
+  echo (block:
+          var a = "a"
+          when true:
+            var b = "b"
+            case alive
+            of alive:
+              try:
+                var c = "c"
+                if true:
+                  king
+                else:
+                  "the abyss"
+              except:
+                echo "he ded"
+                "dead king")
+  echo "live long; long live"
+  echo king
+
+weirdScopes()
+
+
+# bug #14985
+proc getScope(): string =
+  if true:
+    "hi"
+  else:
+    "else"
+
+echo getScope()
+
+proc getScope3(): string =
+  try:
+    "try"
+  except:
+    "except"
+
+echo getScope3()
+
+proc getScope2(): string =
+  case true
+  of true:
+    "bye"
+  else:
+    "else"
+
+echo getScope2()
diff --git a/tests/arc/tnewseq_legacy.nim b/tests/arc/tnewseq_legacy.nim
new file mode 100644
index 000000000..4730d2c2b
--- /dev/null
+++ b/tests/arc/tnewseq_legacy.nim
@@ -0,0 +1,13 @@
+discard """
+  output: "(allocCount: 201, deallocCount: 201)"
+  cmd: "nim c --gc:orc -d:nimAllocStats $file"
+"""
+
+proc main(prefix: string) =
+  var c: seq[string]
+  for i in 0..<100:
+    newSeq(c, 100)
+    c[i] = prefix & $i
+
+main("abc")
+echo getAllocStats()
diff --git a/tests/arc/top_no_cursor2.nim b/tests/arc/top_no_cursor2.nim
new file mode 100644
index 000000000..5dfde57aa
--- /dev/null
+++ b/tests/arc/top_no_cursor2.nim
@@ -0,0 +1,53 @@
+discard """
+  output: '''true
+true
+true
+true
+true'''
+  cmd: "nim c --gc:arc $file"
+"""
+# bug #15361
+
+type
+  ErrorNodeKind = enum Branch, Leaf
+  Error = ref object
+    case kind: ErrorNodeKind
+      of Branch:
+        left: Error
+        right: Error
+      of Leaf:
+        leafError: string
+    input: string
+
+proc ret(input: string, lefterr, righterr: Error): Error =
+  result = Error(kind: Branch, left: lefterr, right: righterr, input: input)
+
+proc parser() =
+  var rerrors: Error
+  let lerrors = Error(
+    kind: Leaf,
+    leafError: "first error",
+    input: "123 ;"
+  )
+  # If you remove "block" - everything works
+  block:
+    let rresult = Error(
+      kind: Leaf,
+      leafError: "second error",
+      input: ";"
+    )
+    # this assignment is needed too
+    rerrors = rresult
+
+  # Returns Error(kind: Branch, left: lerrors, right: rerrors, input: "some val")
+  # needs to be a proc call for some reason, can't inline the result
+  var data = ret(input = "some val", lefterr = lerrors, righterr = rerrors)
+
+  echo data.left.leafError == "first error"
+  echo data.left.input == "123 ;"
+  # stacktrace shows this line
+  echo data.right.leafError == "second error"
+  echo data.right.input == ";"
+  echo data.input == "some val"
+
+parser()
diff --git a/tests/arc/topenarray.nim b/tests/arc/topenarray.nim
new file mode 100644
index 000000000..ba91666ba
--- /dev/null
+++ b/tests/arc/topenarray.nim
@@ -0,0 +1,86 @@
+discard """
+  input: "hi"
+  output: '''
+hi
+Nim
+'''
+  matrix: "--gc:arc -d:useMalloc; --gc:arc"
+"""
+{.experimental: "views".}
+
+block: # bug 18627
+  proc setPosition(params: openArray[string]) =
+    for i in params.toOpenArray(0, params.len - 1):
+      echo i
+
+  proc uciLoop() =
+    let params = @[readLine(stdin)]
+    setPosition(params)
+
+  uciLoop()
+
+  proc uciLoop2() =
+    let params = @["Nim"]
+    for i in params.toOpenArray(0, params.len - 1):
+      echo i
+  uciLoop2()
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+block: # bug #20954
+  block:
+    doAssertRaises(IndexDefect):
+      var v: array[10, int]
+
+      echo len(toOpenArray(v, 20, 30))
+
+  block:
+    doAssertRaises(IndexDefect):
+      var v: seq[int]
+
+      echo len(toOpenArray(v, 20, 30))
+
+# bug #20422
+
+proc f(a: var string) =
+  var v = a.toOpenArray(1, 3)
+  v[0] = 'a'
+
+var a = "Hello"
+f(a)
+doAssert a == "Hallo"
+
+# bug #22132
+block:
+  func foo[T](arr: openArray[T], idx: int = arr.low): string =
+    doAssert idx == 0
+    return $arr
+
+  let bug = ["0", "c", "a"]
+
+  let str = foo(bug)
+
+  const expected = """["0", "c", "a"]"""
+  doAssert str == expected
+
+  const noBugConst = ["0", "c", "a"]
+  doAssert foo(noBugConst) == expected
+  let noBugSeq = @["0", "c", "a"]
+  doAssert foo(noBugSeq) == expected
+
+block: # bug #20865
+  var p: pointer
+  var x: array[0, int]
+  # echo toOpenArray(x, 0, 1)[0] # Raises IndexDefect
+  doAssertRaises(IndexDefect):
+    echo toOpenArray(cast[ptr array[0, int]](p)[], 0, 1)[0] # Does not raise IndexDefect
+
+block: # bug #20987
+  var v: array[1, byte]
+
+  var p = cast[ptr array[0, byte]](addr v)
+
+  doAssertRaises(IndexDefect):
+    echo toOpenArray(p[], 1, 2)
+
diff --git a/tests/arc/topt_cursor.nim b/tests/arc/topt_cursor.nim
new file mode 100644
index 000000000..794132921
--- /dev/null
+++ b/tests/arc/topt_cursor.nim
@@ -0,0 +1,57 @@
+discard """
+  output: '''("string here", 80)'''
+  cmd: '''nim c --gc:arc --expandArc:main --expandArc:sio --hint:Performance:off $file'''
+  nimout: '''--expandArc: main
+
+var
+  x_cursor
+  :tmpD
+try:
+  x_cursor = ("hi", 5)
+  if cond:
+    x_cursor = ("different", 54) else:
+    x_cursor = ("string here", 80)
+  echo [
+    :tmpD = `$$`(x_cursor)
+    :tmpD]
+finally:
+  `=destroy`(:tmpD)
+-- end of expandArc ------------------------
+--expandArc: sio
+
+block :tmp:
+  var x_cursor
+  var f = open("debug.txt", fmRead, 8000)
+  try:
+    var res
+    try:
+      res = newStringOfCap(80)
+      block :tmp_1:
+        while readLine(f, res):
+          x_cursor = res
+          echo [x_cursor]
+    finally:
+      `=destroy`(res)
+  finally:
+    close(f)
+-- end of expandArc ------------------------'''
+"""
+
+proc main(cond: bool) =
+  var x = ("hi", 5) # goal: computed as cursor
+
+  x = if cond:
+        ("different", 54)
+      else:
+        ("string here", 80)
+
+  echo x
+
+main(false)
+
+proc sio =
+  for x in lines("debug.txt"):
+    echo x
+
+if false:
+  sio()
diff --git a/tests/arc/topt_cursor2.nim b/tests/arc/topt_cursor2.nim
new file mode 100644
index 000000000..4b566ed24
--- /dev/null
+++ b/tests/arc/topt_cursor2.nim
@@ -0,0 +1,76 @@
+discard """  
+  cmd: '''nim c --gc:arc $file'''
+  output: '''emptyemptyempty
+inner destroy
+'''
+"""
+
+# bug #15039
+
+import lists
+
+type
+  Token = ref object of RootObj
+    children: DoublyLinkedList[Token]
+
+  Paragraph = ref object of Token
+
+method `$`(token: Token): string {.base.} =
+  result = "empty"
+
+method `$`(token: Paragraph): string =
+  if token.children.head == nil:
+    result = ""
+  else:
+    for c in token.children:
+      result.add $c
+
+proc parseLeafBlockInlines(token: Token) =
+  token.children.append(Token())
+  token.children.append(Token()) # <-- this one AAA
+
+  var emNode = newDoublyLinkedNode(Token())
+  var i = 0
+
+  var it = token.children.head
+  while it != nil:
+    var nxt = it.next  # this is not a cursor, it takes over ownership.
+    var childNode = it
+    if i == 0:
+      childNode.next = emNode # frees the object allocated in line 29 marked with AAA
+    elif i == 1:
+      emNode.next = childNode  #
+    it = nxt # incref on freed data, 'nxt' is freed
+    inc i
+
+proc parse() =
+  var token = Token()
+  token.children.append Paragraph()
+  parseLeafBlockInlines(token.children.head.value)
+  for children in token.children:
+    echo children
+
+parse()
+
+
+#------------------------------------------------------------------------------
+# issue #15629
+
+type inner = object
+type outer = ref inner
+
+proc `=destroy`(b: var inner) =
+  echo "inner destroy"
+
+proc newOuter(): outer =
+  new(result)
+
+type holder = object
+  contents: outer
+
+proc main() = 
+  var t: holder
+  t.contents = newOuter()
+  
+main()
+
diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim
new file mode 100644
index 000000000..0a4984a69
--- /dev/null
+++ b/tests/arc/topt_no_cursor.nim
@@ -0,0 +1,376 @@
+discard """
+  nimoutFull: true
+  cmd: '''nim c -r --warnings:off --hints:off --mm:arc --expandArc:newTarget --expandArc:delete --expandArc:p1 --expandArc:tt --hint:Performance:off --assertions:off --expandArc:extractConfig --expandArc:mergeShadowScope --expandArc:check $file'''
+  nimout: '''
+--expandArc: newTarget
+
+var
+  splat
+  :tmp
+  :tmp_1
+splat = splitDrive do:
+  let blitTmp = path
+  blitTmp
+:tmp = splat.drive
+`=wasMoved`(splat.drive)
+:tmp_1 = splat.path_1
+`=wasMoved`(splat.path_1)
+result = (
+  let blitTmp_1 = :tmp
+  blitTmp_1,
+  let blitTmp_2 = :tmp_1
+  blitTmp_2)
+`=destroy`(splat)
+-- end of expandArc ------------------------
+--expandArc: delete
+
+var
+  sibling
+  saved
+`=copy`(sibling, target.parent.left)
+`=copy`(saved, sibling.right)
+`=copy`(sibling.right, saved.left)
+`=sink`(sibling.parent, saved)
+`=destroy`(sibling)
+-- end of expandArc ------------------------
+--expandArc: p1
+
+var
+  lresult
+  lvalue
+  lnext
+  tmpTupleAsgn
+lresult = @[123]
+tmpTupleAsgn = (
+  let blitTmp = lresult
+  blitTmp, ";")
+lvalue = tmpTupleAsgn[0]
+lnext = tmpTupleAsgn[1]
+`=sink`(result.value, move lvalue)
+`=destroy`(lnext)
+`=destroy_1`(lvalue)
+-- end of expandArc ------------------------
+--expandArc: tt
+
+var
+  it_cursor
+  a
+  :tmpD
+  :tmpD_1
+  :tmpD_2
+try:
+  it_cursor = x
+  a = (
+    :tmpD = `=dup`(it_cursor.key)
+    :tmpD,
+    :tmpD_1 = `=dup`(it_cursor.val)
+    :tmpD_1)
+  echo [
+    :tmpD_2 = `$$`(a)
+    :tmpD_2]
+finally:
+  `=destroy`(:tmpD_2)
+  `=destroy_1`(a)
+-- end of expandArc ------------------------
+--expandArc: extractConfig
+
+var lan_ip
+try:
+  lan_ip = ""
+  block :tmp:
+    var line
+    var i = 0
+    let L = len(txt)
+    block :tmp_1:
+      while i < L:
+        var splitted
+        try:
+          line = txt[i]
+          splitted = split(line, " ", -1)
+          if splitted[0] == "opt":
+            `=copy`(lan_ip, splitted[1])
+          echo [lan_ip]
+          echo [splitted[1]]
+          {.push, overflowChecks: false.}
+          inc(i, 1)
+          {.pop.}
+        finally:
+          `=destroy`(splitted)
+finally:
+  `=destroy_1`(lan_ip)
+-- end of expandArc ------------------------
+--expandArc: mergeShadowScope
+
+var shadowScope
+`=copy`(shadowScope, c.currentScope)
+rawCloseScope(c)
+block :tmp:
+  var sym
+  var i = 0
+  let L = len(shadowScope.symbols)
+  block :tmp_1:
+    while i < L:
+      var :tmpD
+      sym = shadowScope.symbols[i]
+      addInterfaceDecl(c):
+        :tmpD = `=dup`(sym)
+        :tmpD
+      {.push, overflowChecks: false.}
+      inc(i, 1)
+      {.pop.}
+`=destroy`(shadowScope)
+-- end of expandArc ------------------------
+--expandArc: check
+
+var par
+this.isValid = fileExists(this.value)
+if dirExists(this.value):
+  var :tmpD
+  par = (dir:
+    :tmpD = `=dup`(this.value)
+    :tmpD, front: "") else:
+  var
+    :tmpD_1
+    :tmpD_2
+    :tmpD_3
+  par = (dir_1: parentDir(this.value), front_1:
+    :tmpD_1 = `=dup`(
+      :tmpD_3 = splitDrive do:
+        :tmpD_2 = `=dup`(this.value)
+        :tmpD_2
+      :tmpD_3.path)
+    :tmpD_1)
+  `=destroy`(:tmpD_3)
+if dirExists(par.dir):
+  `=sink`(this.matchDirs, getSubDirs(par.dir, par.front))
+else:
+  `=sink`(this.matchDirs, [])
+`=destroy`(par)
+-- end of expandArc ------------------------
+--expandArc: check
+
+check(this)
+-- end of expandArc ------------------------
+(package: "", ext: "meo")
+doing shady stuff...
+3
+6
+(@[1], @[2])
+192.168.0.1
+192.168.0.1
+192.168.0.1
+192.168.0.1
+'''
+"""
+
+import os, std/private/ntpath
+
+type Target = tuple[package, ext: string]
+
+proc newTarget*(path: string): Target =
+  let splat = path.splitDrive
+  result = (package: splat.drive, ext: splat.path)
+
+echo newTarget("meo")
+
+type
+  Node = ref object
+    left, right, parent: Node
+    value: int
+
+proc delete(target: var Node) =
+  var sibling = target.parent.left # b3
+  var saved = sibling.right # b3.right -> r4
+
+  sibling.right = saved.left # b3.right -> r4.left = nil
+  sibling.parent = saved # b3.parent -> r5 = r4
+
+  #[after this proc:
+        b 5
+      /   \
+    b 3     b 6
+  ]#
+
+
+#[before:
+      r 5
+    /   \
+  b 3    b 6 - to delete
+  /    \
+empty  r 4
+]#
+proc main =
+  var five = Node(value: 5)
+
+  var six = Node(value: 6)
+  six.parent = five
+  five.right = six
+
+  var three = Node(value: 3)
+  three.parent = five
+  five.left = three
+
+  var four = Node(value: 4)
+  four.parent = three
+  three.right = four
+
+  echo "doing shady stuff..."
+  delete(six)
+  # need both of these echos
+  echo five.left.value
+  echo five.right.value
+
+main()
+
+type
+  Maybe = object
+    value: seq[int]
+
+proc p1(): Maybe =
+  let lresult = @[123]
+  var lvalue: seq[int]
+  var lnext: string
+  (lvalue, lnext) = (lresult, ";")
+
+  result.value = move lvalue
+
+proc tissue15130 =
+  doAssert p1().value == @[123]
+
+tissue15130()
+
+type
+  KeyValue = tuple[key, val: seq[int]]
+
+proc tt(x: KeyValue) =
+  var it = x
+  let a = (it.key, it.val)
+  echo a
+
+proc encodedQuery =
+  var query: seq[KeyValue]
+  query.add (key: @[1], val: @[2])
+
+  for elem in query:
+    elem.tt()
+
+encodedQuery()
+
+# bug #15147
+
+proc s(input: string): (string, string) =
+  result = (";", "")
+
+proc charmatch(input: string): (string, string) =
+  result = ("123", input[0 .. input.high])
+
+proc plus(input: string) =
+  var
+    lvalue, rvalue: string # cursors
+    lnext: string # must be cursor!!!
+    rnext: string # cursor
+  let lresult = charmatch(input)
+  (lvalue, lnext) = lresult
+
+  let rresult = s(lnext)
+  (rvalue, rnext) = rresult
+
+plus("123;")
+
+func substrEq(s: string, pos: int, substr: string): bool =
+  var i = 0
+  var length = substr.len
+  while i < length and pos+i < s.len and s[pos+i] == substr[i]:
+    inc i
+  return i == length
+
+template stringHasSep(s: string, index: int, sep: string): bool =
+  s.substrEq(index, sep)
+
+template splitCommon(s, sep, maxsplit, sepLen) =
+  var last = 0
+  var splits = maxsplit
+
+  while last <= len(s):
+    var first = last
+    while last < len(s) and not stringHasSep(s, last, sep):
+      inc(last)
+    if splits == 0: last = len(s)
+    yield substr(s, first, last-1)
+    if splits == 0: break
+    dec(splits)
+    inc(last, sepLen)
+
+iterator split(s: string, sep: string, maxsplit = -1): string =
+  splitCommon(s, sep, maxsplit, sep.len)
+
+template accResult(iter: untyped) =
+  result = @[]
+  for x in iter: add(result, x)
+
+func split*(s: string, sep: string, maxsplit = -1): seq[string] =
+  accResult(split(s, sep, maxsplit))
+
+
+let txt = @["opt 192.168.0.1", "static_lease 192.168.0.1"]
+
+# bug #17033
+
+proc extractConfig() =
+  var lan_ip = ""
+
+  for line in txt:
+    let splitted = line.split(" ")
+    if splitted[0] == "opt":
+      lan_ip = splitted[1] # "borrow" is conditional and inside a loop.
+      # Not good enough...
+      # we need a flag that live-ranges are disjoint
+    echo lan_ip
+    echo splitted[1] # Without this line everything works
+
+extractConfig()
+
+
+type
+  Symbol = ref object
+    name: string
+
+  Scope = ref object
+    parent: Scope
+    symbols: seq[Symbol]
+
+  PContext = ref object
+    currentScope: Scope
+
+proc rawCloseScope(c: PContext) =
+  c.currentScope = c.currentScope.parent
+
+proc addInterfaceDecl(c: PContext; s: Symbol) =
+  c.currentScope.symbols.add s
+
+proc mergeShadowScope*(c: PContext) =
+  let shadowScope = c.currentScope
+  c.rawCloseScope
+  for sym in shadowScope.symbols:
+    c.addInterfaceDecl(sym)
+
+mergeShadowScope(PContext(currentScope: Scope(parent: Scope())))
+
+type
+  Foo = ref object
+    isValid*: bool
+    value*: string
+    matchDirs*: seq[string]
+
+proc getSubDirs(parent, front: string): seq[string] = @[]
+
+method check(this: Foo) {.base.} =
+  this.isValid = fileExists(this.value)
+  let par = if dirExists(this.value): (dir: this.value, front: "")
+            else: (dir: parentDir(this.value), front: splitDrive(this.value).path)
+  if dirExists(par.dir):
+    this.matchDirs = getSubDirs(par.dir, par.front)
+  else:
+    this.matchDirs = @[]
+
+check(Foo())
diff --git a/tests/arc/topt_refcursors.nim b/tests/arc/topt_refcursors.nim
new file mode 100644
index 000000000..8c638a4a1
--- /dev/null
+++ b/tests/arc/topt_refcursors.nim
@@ -0,0 +1,54 @@
+discard """
+  output: ''''''
+  cmd: '''nim c --gc:arc --expandArc:traverse --hint:Performance:off $file'''
+  nimout: '''
+--expandArc: traverse
+
+var
+  it_cursor
+  jt
+try:
+  it_cursor = root
+  block :tmp:
+    while (
+      not (it_cursor == nil)):
+      echo [it_cursor.s]
+      it_cursor = it_cursor.ri
+  `=copy`(jt, root)
+  block :tmp_1:
+    while (
+      not (jt == nil)):
+      var ri_1
+      try:
+        `=copy`(ri_1, jt.ri)
+        echo [jt.s]
+        `=sink`(jt, ri_1)
+        `=wasMoved`(ri_1)
+      finally:
+        `=destroy`(ri_1)
+finally:
+  `=destroy`(jt)
+-- end of expandArc ------------------------
+'''
+"""
+
+type
+  Node = ref object
+    le, ri: Node
+    s: string
+
+proc traverse(root: Node) =
+  var it = root
+  while it != nil:
+    echo it.s
+    it = it.ri
+
+  var jt = root
+  while jt != nil:
+    let ri = jt.ri
+    echo jt.s
+    jt = ri
+
+traverse(nil)
+
+# XXX: This optimization is not sound
diff --git a/tests/arc/topt_wasmoved_destroy_pairs.nim b/tests/arc/topt_wasmoved_destroy_pairs.nim
new file mode 100644
index 000000000..c96340a30
--- /dev/null
+++ b/tests/arc/topt_wasmoved_destroy_pairs.nim
@@ -0,0 +1,94 @@
+discard """
+  output: ''''''
+  cmd: '''nim c --gc:arc --expandArc:main --expandArc:tfor --hint:Performance:off $file'''
+  nimout: '''
+--expandArc: main
+
+var
+  a
+  b
+  x
+x = f()
+if cond:
+  add(a):
+    let blitTmp = x
+    blitTmp
+else:
+  add(b):
+    let blitTmp_1 = x
+    blitTmp_1
+`=destroy`(b)
+`=destroy`(a)
+-- end of expandArc ------------------------
+--expandArc: tfor
+
+var
+  a
+  b
+  x
+try:
+  x = f()
+  block :tmp:
+    var i_cursor
+    mixin inc
+    var i_1 = 0
+    block :tmp_1:
+      while i_1 < 4:
+        var :tmpD
+        i_cursor = i_1
+        if i_cursor == 2:
+          return
+        add(a):
+          :tmpD = `=dup`(x)
+          :tmpD
+        inc i_1, 1
+  if cond:
+    add(a):
+      let blitTmp = x
+      `=wasMoved`(x)
+      blitTmp
+  else:
+    add(b):
+      let blitTmp_1 = x
+      `=wasMoved`(x)
+      blitTmp_1
+finally:
+  `=destroy`(x)
+  `=destroy_1`(b)
+  `=destroy_1`(a)
+-- end of expandArc ------------------------
+'''
+"""
+
+proc f(): seq[int] =
+  @[1, 2, 3]
+
+proc main(cond: bool) =
+  var a, b: seq[seq[int]]
+  var x = f()
+  if cond:
+    a.add x
+  else:
+    b.add x
+
+# all paths move 'x' so no wasMoved(x); destroy(x) pair should be left in the
+# AST.
+
+main(false)
+
+
+proc tfor(cond: bool) =
+  var a, b: seq[seq[int]]
+
+  var x = f()
+
+  for i in 0 ..< 4:
+    if i == 2: return
+    a.add x
+
+  if cond:
+    a.add x
+  else:
+    b.add x
+
+tfor(false)
diff --git a/tests/arc/torc_basic_test.nim b/tests/arc/torc_basic_test.nim
new file mode 100644
index 000000000..73039fad7
--- /dev/null
+++ b/tests/arc/torc_basic_test.nim
@@ -0,0 +1,138 @@
+discard """
+  output: "MEM 0"
+  cmd: "nim c --gc:orc $file"
+"""
+
+type
+  Node = ref object
+    name: char
+    sccId: int
+    #a: array[3, Node]
+    a0, a1, a2: Node
+    rc: int
+
+proc edge(a, b: Node) =
+  inc b.rc
+  if a.a0 == nil: a.a0 = b
+  elif a.a1 == nil: a.a1 = b
+  else: a.a2 = b
+  when false:
+    var i = 0
+    while a.a[i] != nil: inc i
+    a.a[i] = b
+
+proc createNode(name: char): Node =
+  new result
+  result.name = name
+
+#[
+
+     +--------------------------------+
+     v                                |
++---------+      +------+             |
+|         |      |      |             |
+|  A      +----->+      |      +------+------+
++--+------+      |      |      |             |
+   |             |      |      |     C       ------------>  G  <--|
+   |             |  R   |      |             |
++--v------+      |      |      +-------------+
+|         |      |      |        ^
+|   B     <------+      |        |
+|         |      |      +--------+
++---------+      |      |
+                 +------+
+
+]#
+proc use(x: Node) = discard
+
+proc main =
+  let a = createNode('A')
+  let b = createNode('B')
+  let r = createNode('R')
+  let c = createNode('C')
+
+  a.edge b
+  a.edge r
+
+  r.edge b
+  r.edge c
+
+  let g = createNode('G')
+  g.edge g
+  g.edge g
+
+  c.edge g
+  c.edge a
+
+  use g
+  use b
+
+proc buildComplexGraph: Node =
+  # see https://en.wikipedia.org/wiki/Strongly_connected_component for the
+  # graph:
+  let a = createNode('a')
+  let b = createNode('b')
+  let c = createNode('c')
+  let d = createNode('d')
+  let e = createNode('e')
+
+  a.edge c
+  c.edge b
+  c.edge e
+  b.edge a
+  d.edge c
+  e.edge d
+
+
+  let f = createNode('f')
+  b.edge f
+  e.edge f
+
+  let g = createNode('g')
+  let h = createNode('h')
+  let i = createNode('i')
+
+  f.edge g
+  f.edge i
+  g.edge h
+  h.edge i
+  i.edge g
+
+  let j = createNode('j')
+
+  h.edge j
+  i.edge j
+
+  let k = createNode('k')
+  let l = createNode('l')
+
+  f.edge k
+  k.edge l
+  l.edge k
+  k.edge j
+
+  let m = createNode('m')
+  let n = createNode('n')
+  let p = createNode('p')
+  let q = createNode('q')
+
+  m.edge n
+  n.edge p
+  n.edge q
+  q.edge p
+  p.edge m
+
+  q.edge k
+
+  d.edge m
+  e.edge n
+
+  result = a
+
+proc main2 =
+  let g = buildComplexGraph()
+
+main()
+main2()
+GC_fullCollect()
+echo "MEM ", getOccupiedMem()
diff --git a/tests/arc/torc_selfcycles.nim b/tests/arc/torc_selfcycles.nim
new file mode 100644
index 000000000..ac4fa52ce
--- /dev/null
+++ b/tests/arc/torc_selfcycles.nim
@@ -0,0 +1,33 @@
+discard """
+  output: '''ok'''
+  cmd: '''nim c --gc:orc -d:useMalloc -d:nimStressOrc $file'''
+  valgrind: "leaks"
+"""
+
+# bug #15753
+
+type
+  NodeKind = enum
+    nkDancing,
+    nkColumn
+
+  DancingNode = ref object
+    right: DancingNode
+    column: DancingNode
+    kind: NodeKind
+
+proc newColumnNode(): DancingNode =
+  result = DancingNode(kind: nkColumn)
+  result.right = result
+  result.column = result
+
+proc createDLXList(): DancingNode =
+  result = newColumnNode()
+
+  for i in 0 .. 15:
+    let n = newColumnNode()
+    n.right = result.right
+    result = n
+  echo "ok"
+
+var dlxlist = createDLXList()
diff --git a/tests/arc/torcbench.nim b/tests/arc/torcbench.nim
new file mode 100644
index 000000000..4c9e65fee
--- /dev/null
+++ b/tests/arc/torcbench.nim
@@ -0,0 +1,38 @@
+discard """
+  output: '''true peak memory: true'''
+  cmd: "nim c --gc:orc -d:release $file"
+"""
+
+import lists, strutils, times
+
+type
+  Base = ref object of RootObj
+
+  Node = ref object of Base
+    parent: DoublyLinkedList[string]
+    le, ri: Node
+    self: Node # in order to create a cycle
+
+proc buildTree(parent: DoublyLinkedList[string]; depth: int): Node =
+  if depth == 0:
+    result = nil
+  elif depth == 1:
+    result = Node(parent: parent, le: nil, ri: nil, self: nil)
+    when not defined(gcArc):
+      result.self = result
+  else:
+    result = Node(parent: parent, le: buildTree(parent, depth - 1), ri: buildTree(parent, depth - 2), self: nil)
+    result.self = result
+
+proc main() =
+  for i in countup(1, 100):
+    var leakList = initDoublyLinkedList[string]()
+    for j in countup(1, 5000):
+      leakList.append(newString(200))
+    #GC_fullCollect()
+    for i in 0..400:
+      discard buildTree(leakList, 8)
+
+main()
+GC_fullCollect()
+echo getOccupiedMem() < 10 * 1024 * 1024, " peak memory: ", getMaxMem() < 10 * 1024 * 1024
diff --git a/tests/arc/torcmisc.nim b/tests/arc/torcmisc.nim
new file mode 100644
index 000000000..e41ad7c77
--- /dev/null
+++ b/tests/arc/torcmisc.nim
@@ -0,0 +1,66 @@
+discard """
+  output: '''success'''
+  cmd: "nim c --gc:orc -d:release $file"
+"""
+
+# bug #17170
+
+when true:
+  import asyncdispatch
+
+  type
+    Flags = ref object
+      returnedEof, reading: bool
+
+  proc dummy(): Future[string] {.async.} =
+    result = "foobar"
+
+  proc hello(s: Flags) {.async.} =
+    let buf =
+      try:
+        await dummy()
+      except CatchableError as exc:
+        # When an exception happens here, the Bufferstream is effectively
+        # broken and no more reads will be valid - for now, return EOF if it's
+        # called again, though this is not completely true - EOF represents an
+        # "orderly" shutdown and that's not what happened here..
+        s.returnedEof = true
+        raise exc
+      finally:
+        s.reading = false
+
+  waitFor hello(Flags())
+  echo "success"
+
+# bug #18240
+import tables
+
+type
+  TopicHandler* = proc(topic: string,
+                       data: seq[byte]): Future[void] {.gcsafe, raises: [Defect].}
+
+  PeerID* = object
+    data*: seq[byte]
+
+  PeerInfo* = ref object of RootObj
+    peerId*: PeerID
+
+  Connection* = ref object of RootObj
+    peerInfo*: PeerInfo
+
+  PubSubPeer* = ref object of RootObj
+    codec*: string
+
+  PubSub* = ref object of RootObj
+    topics*: Table[string, seq[TopicHandler]]
+    peers*: Table[PeerID, PubSubPeer]
+
+proc getOrCreatePeer*(myParam: PubSub, peerId: PeerID, protos: seq[string]): PubSubPeer =
+  myParam.peers.withValue(peerId, peer):
+    return peer[]
+
+method handleConn*(myParam: PubSub,
+                  conn: Connection,
+                  proto: string) {.base, async.} =
+  myParam.peers.withValue(conn.peerInfo.peerId, peer):
+    let peerB = peer[]
diff --git a/tests/arc/tref_cast_error.nim b/tests/arc/tref_cast_error.nim
new file mode 100644
index 000000000..20139c1be
--- /dev/null
+++ b/tests/arc/tref_cast_error.nim
@@ -0,0 +1,15 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+  errormsg: "expression cannot be cast to 'ref RootObj'"
+  joinable: false
+"""
+
+type Variant* = object
+    refval: ref RootObj
+
+proc newVariant*[T](val: T): Variant =
+    let pt = T.new()
+    pt[] = val
+    result = Variant(refval: cast[ref RootObj](pt))
+
+var v = newVariant(@[1, 2, 3])
diff --git a/tests/arc/trepr.nim b/tests/arc/trepr.nim
new file mode 100644
index 000000000..abf28330a
--- /dev/null
+++ b/tests/arc/trepr.nim
@@ -0,0 +1,97 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+  nimout: '''(a: true, n: doAssert)
+Table[system.string, trepr.MyType](data: @[], counter: 0)
+nil
+'''
+  output: '''
+nil
+2
+Obj(member: ref @["hello"])
+ref (member: ref @["hello"])
+ObjUa(v: 0, a: [...])
+'''
+"""
+
+# xxx consider merging with `tests/stdlib/trepr.nim` to increase overall test coverage
+
+import tables
+
+type
+  NimSym = distinct NimNode
+  MyType = tuple
+    a: bool
+    n: NimSym
+
+proc myproc(t: MyType) =
+  echo repr(t)
+
+proc myproc2(t: MyType) =
+  var x = Table[string, t]()
+  echo repr(x)
+
+proc myproc3(t: MyType) =
+  var x: TableRef[string, t]
+  echo repr(x)
+
+
+macro dumpSym(a: typed) =
+  myproc((a: true, n: NimSym(a)))
+  myproc2((a: true, n: NimSym(a)))
+  myproc3((a: true, n: NimSym(a)))
+
+dumpSym(doAssert)
+
+# bug 13731
+
+import os
+var a: File
+echo repr a
+
+# bug 13872
+
+echo repr(2'u16)
+
+# bug 14270
+
+type
+  Obj = ref object
+    member: ref seq[string]
+
+var c = Obj(member: new seq[string])
+c.member[] = @["hello"]
+echo c.repr
+
+var c2 = new tuple[member: ref seq[string]]
+c2.member = new seq[string]
+c2.member[] = @["hello"]
+echo c2.repr
+
+proc p2 =
+  echo "hey"
+
+discard repr p2
+
+
+#####################################################################
+# bug #15043
+
+import macros
+
+macro extract(): untyped =
+  result = newStmtList()
+  var x: seq[tuple[node: NimNode]]
+
+  proc test(n: NimNode) {.closure.} =
+    x.add (node: n)
+  
+  test(parseExpr("discard"))
+  
+extract()
+
+type
+  ObjUa = ref object
+    v: int
+    a: UncheckedArray[char]
+
+echo ObjUa().repr
diff --git a/tests/arc/trtree.nim b/tests/arc/trtree.nim
new file mode 100644
index 000000000..683403a62
--- /dev/null
+++ b/tests/arc/trtree.nim
@@ -0,0 +1,640 @@
+discard """
+  output: '''1 [2, 3, 4, 7]
+[0, 0]'''
+  targets: "c"
+  joinable: false
+disabled: 32bit
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #13110: This test failed with --gc:arc.
+
+# this test wasn't written for 32 bit
+# don't join because the code is too messy.
+
+# Nim RTree and R*Tree implementation
+# S. Salewski, 06-JAN-2018
+
+# http://www-db.deis.unibo.it/courses/SI-LS/papers/Gut84.pdf
+# http://dbs.mathematik.uni-marburg.de/publications/myPapers/1990/BKSS90.pdf
+
+# RT: range type like float, int
+# D: Dimension
+# M: Max entries in one node
+# LT: leaf type
+
+type
+  Dim* = static[int]
+  Ext[RT] = tuple[a, b: RT] # extend (range)
+  Box*[D: Dim; RT] = array[D, Ext[RT]] # Rectangle for 2D
+  BoxCenter*[D: Dim; RT] = array[D, RT]
+  L*[D: Dim; RT, LT] = tuple[b: Box[D, RT]; l: LT] # called Index Entry or index record in the Guttman paper
+  H[M, D: Dim; RT, LT] = ref object of RootRef
+    parent: H[M, D, RT, LT]
+    numEntries: int
+    level: int
+  N[M, D: Dim; RT, LT] = tuple[b: Box[D, RT]; n: H[M, D, RT, LT]]
+  LA[M, D: Dim; RT, LT] = array[M, L[D, RT, LT]]
+  NA[M, D: Dim; RT, LT] = array[M, N[M, D, RT, LT]]
+  Leaf[M, D: Dim; RT, LT] = ref object of H[M, D, RT, LT]
+    a: LA[M, D, RT, LT]
+  Node[M, D: Dim; RT, LT] = ref object of H[M, D, RT, LT]
+    a: NA[M, D, RT, LT]
+
+  RTree*[M, D: Dim; RT, LT] = ref object of RootRef
+    root: H[M, D, RT, LT]
+    bigM: int
+    m: int
+
+  RStarTree*[M, D: Dim; RT, LT] = ref object of RTree[M, D, RT, LT]
+    firstOverflow: array[32, bool]
+    p: int
+
+proc newLeaf[M, D: Dim; RT, LT](): Leaf[M, D, RT, LT] =
+  new result
+
+proc newNode[M, D: Dim; RT, LT](): Node[M, D, RT, LT] =
+  new result
+
+proc newRTree*[M, D: Dim; RT, LT](minFill: range[30 .. 50] = 40): RTree[M, D, RT, LT] =
+  assert(M > 1 and M < 101)
+  new result
+  result.bigM = M
+  result.m = M * minFill div 100
+  result.root = newLeaf[M, D, RT, LT]()
+
+proc newRStarTree*[M, D: Dim; RT, LT](minFill: range[30 .. 50] = 40): RStarTree[M, D, RT, LT] =
+  assert(M > 1 and M < 101)
+  new result
+  result.bigM = M
+  result.m = M * minFill div 100
+  result.p = M * 30 div 100
+  result.root = newLeaf[M, D, RT, LT]()
+
+proc center(r: Box): auto =#BoxCenter[r.len, typeof(r[0].a)] =
+  var res: BoxCenter[r.len, typeof(r[0].a)]
+  for i in 0 .. r.high:
+    when r[0].a is SomeInteger:
+      res[i] = (r[i].a + r[i].b) div 2
+    elif r[0].a is SomeFloat:
+      res[i] = (r[i].a + r[i].b) / 2
+    else: assert false
+  return res
+
+proc distance(c1, c2: BoxCenter): auto =
+  var res: typeof(c1[0])
+  for i in 0 .. c1.high:
+    res += (c1[i] - c2[i]) * (c1[i] - c2[i])
+  return res
+
+proc overlap(r1, r2: Box): auto =
+  result = typeof(r1[0].a)(1)
+  for i in 0 .. r1.high:
+    result *= (min(r1[i].b, r2[i].b) - max(r1[i].a, r2[i].a))
+    if result <= 0: return 0
+
+proc union(r1, r2: Box): Box =
+  for i in 0 .. r1.high:
+    result[i].a = min(r1[i].a, r2[i].a)
+    result[i].b = max(r1[i].b, r2[i].b)
+
+proc intersect(r1, r2: Box): bool =
+  for i in 0 .. r1.high:
+    if r1[i].b < r2[i].a or r1[i].a > r2[i].b:
+      return false
+  return true
+
+proc area(r: Box): auto = #typeof(r[0].a) =
+  result = typeof(r[0].a)(1)
+  for i in 0 .. r.high:
+    result *= r[i].b - r[i].a
+
+proc margin(r: Box): auto = #typeof(r[0].a) =
+  result = typeof(r[0].a)(0)
+  for i in 0 .. r.high:
+    result += r[i].b - r[i].a
+
+# how much enlargement does r1 need to include r2
+proc enlargement(r1, r2: Box): auto =
+  area(union(r1, r2)) - area(r1)
+
+proc search*[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; b: Box[D, RT]): seq[LT] =
+  proc s[M, D: Dim; RT, LT](n: H[M, D, RT, LT]; b: Box[D, RT]; res: var seq[LT]) =
+    if n of Node[M, D, RT, LT]:
+      let h = Node[M, D, RT, LT](n)
+      for i in 0 ..< n.numEntries:
+        if intersect(h.a[i].b, b):
+          s(h.a[i].n, b, res)
+    elif n of Leaf[M, D, RT, LT]:
+      let h = Leaf[M, D, RT, LT](n)
+      for i in 0 ..< n.numEntries:
+        if intersect(h.a[i].b, b):
+          res.add(h.a[i].l)
+    else: assert false
+  result = newSeq[LT]()
+  s(t.root, b, result)
+
+# Insertion
+# a R*TREE proc
+proc chooseSubtree[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; b: Box[D, RT]; level: int): H[M, D, RT, LT] =
+  assert level >= 0
+  var it = t.root
+  while it.level > level:
+    let nn = Node[M, D, RT, LT](it)
+    var i0 = 0 # selected index
+    var minLoss = typeof(b[0].a).high
+    if it.level == 1: # childreen are leaves -- determine the minimum overlap costs
+      for i in 0 ..< it.numEntries:
+        let nx = union(nn.a[i].b, b)
+        var loss = 0
+        for j in 0 ..< it.numEntries:
+          if i == j: continue
+          loss += (overlap(nx, nn.a[j].b) - overlap(nn.a[i].b, nn.a[j].b)) # overlap (i, j) == (j, i), so maybe cache that?
+        var rep = loss < minLoss
+        if loss == minLoss:
+          let l2 = enlargement(nn.a[i].b, b) - enlargement(nn.a[i0].b, b)
+          rep = l2 < 0
+          if l2 == 0:
+            let l3 = area(nn.a[i].b) - area(nn.a[i0].b)
+            rep = l3 < 0
+            if l3 == 0:
+              rep = nn.a[i].n.numEntries < nn.a[i0].n.numEntries
+        if rep:
+          i0 = i
+          minLoss = loss
+    else:
+      for i in 0 ..< it.numEntries:
+        let loss = enlargement(nn.a[i].b, b)
+        var rep = loss < minLoss
+        if loss == minLoss:
+          let l3 = area(nn.a[i].b) - area(nn.a[i0].b)
+          rep = l3 < 0
+          if l3 == 0:
+            rep = nn.a[i].n.numEntries < nn.a[i0].n.numEntries
+        if rep:
+          i0 = i
+          minLoss = loss
+    it = nn.a[i0].n
+  return it
+
+proc pickSeeds[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; n: Node[M, D, RT, LT] | Leaf[M, D, RT, LT]; bx: Box[D, RT]): (int, int) =
+  var i0, j0: int
+  var bi, bj: typeof(bx)
+  var largestWaste = typeof(bx[0].a).low
+  for i in -1 .. n.a.high:
+    for j in 0 .. n.a.high:
+      if unlikely(i == j): continue
+      if unlikely(i < 0):
+        bi = bx
+      else:
+        bi = n.a[i].b
+      bj = n.a[j].b
+      let b = union(bi, bj)
+      let h = area(b) - area(bi) - area(bj)
+      if h > largestWaste:
+        largestWaste = h
+        i0 = i
+        j0 = j
+  return (i0, j0)
+
+proc pickNext[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; n0, n1, n2: Node[M, D, RT, LT] | Leaf[M, D, RT, LT]; b1, b2: Box[D, RT]): int =
+  let a1 = area(b1)
+  let a2 = area(b2)
+  var d = typeof(a1).low
+  for i in 0 ..< n0.numEntries:
+    let d1 = area(union(b1, n0.a[i].b)) - a1
+    let d2 = area(union(b2, n0.a[i].b)) - a2
+    if (d1 - d2) * (d1 - d2) > d:
+      result = i
+      d = (d1 - d2) * (d1 - d2)
+
+from algorithm import SortOrder, sort
+proc sortPlus[T](a: var openArray[T], ax: var T, cmp: proc (x, y: T): int {.closure.}, order = algorithm.SortOrder.Ascending) =
+  var j = 0
+  let sign = if order == algorithm.SortOrder.Ascending: 1 else: -1
+  for i in 1 .. a.high:
+    if cmp(a[i], a[j]) * sign < 0:
+      j = i
+  if cmp(a[j], ax) * sign < 0:
+    swap(ax, a[j])
+  a.sort(cmp, order)
+
+# R*TREE procs
+proc rstarSplit[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): typeof(n) =
+  type NL = typeof(lx)
+  var nBest: typeof(n)
+  new nBest
+  var lx = lx
+  when n is Node[M, D, RT, LT]:
+    lx.n.parent = n
+  var lxbest: typeof(lx)
+  var m0 = lx.b[0].a.typeof.high
+  for d2 in 0 ..< 2 * D:
+    let d = d2 div 2
+    if d2 mod 2 == 0:
+      sortPlus(n.a, lx, proc (x, y: NL): int = cmp(x.b[d].a, y.b[d].a))
+    else:
+      sortPlus(n.a, lx, proc (x, y: NL): int = cmp(x.b[d].b, y.b[d].b))
+    for i in t.m - 1 .. n.a.high - t.m + 1:
+      var b = lx.b
+      for j in 0 ..< i: # we can precalculate union() for range 0 .. t.m - 1, but that seems to give no real benefit.Maybe for very large M?
+        #echo "x",j
+        b = union(n.a[j].b, b)
+      var m = margin(b)
+      b = n.a[^1].b
+      for j in i ..< n.a.high: # again, precalculation of tail would be possible
+        #echo "y",j
+        b = union(n.a[j].b, b)
+      m += margin(b)
+      if m < m0:
+        nbest[] = n[]
+        lxbest = lx
+        m0 = m
+  var i0 = -1
+  var o0 = lx.b[0].a.typeof.high
+  for i in t.m - 1 .. n.a.typeof.high - t.m + 1:
+    var b1 = lxbest.b
+    for j in 0 ..< i:
+      b1 = union(nbest.a[j].b, b1)
+    var b2 = nbest.a[^1].b
+    for j in i ..< n.a.high:
+      b2 = union(nbest.a[j].b, b2)
+    let o = overlap(b1, b2)
+    if o < o0:
+      i0 = i
+      o0 = o
+  n.a[0] = lxbest
+  for i in 0 ..< i0:
+    n.a[i + 1] = nbest.a[i]
+  new result
+  result.level = n.level
+  result.parent = n.parent
+  for i in i0 .. n.a.high:
+    result.a[i - i0] = nbest.a[i]
+  n.numEntries = i0 + 1
+  result.numEntries = M - i0
+  when n is Node[M, D, RT, LT]:
+    for i in 0 ..< result.numEntries:
+      result.a[i].n.parent = result
+
+proc quadraticSplit[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): typeof(n) =
+  var n1, n2: typeof(n)
+  var s1, s2: int
+  new n1
+  new n2
+  n1.parent = n.parent
+  n2.parent = n.parent
+  n1.level = n.level
+  n2.level = n.level
+  var lx = lx
+  when n is Node[M, D, RT, LT]:
+    lx.n.parent = n
+  (s1, s2) = pickSeeds(t, n, lx.b)
+  assert s1 >= -1 and s2 >= 0
+  if unlikely(s1 < 0):
+    n1.a[0] = lx
+  else:
+    n1.a[0] = n.a[s1]
+    dec(n.numEntries)
+    if s2 == n.numEntries: # important fix
+      s2 = s1
+    n.a[s1] = n.a[n.numEntries]
+  inc(n1.numEntries)
+  var b1 = n1.a[0].b
+  n2.a[0] = n.a[s2]
+  dec(n.numEntries)
+  n.a[s2] = n.a[n.numEntries]
+  inc(n2.numEntries)
+  var b2 = n2.a[0].b
+  if s1 >= 0:
+    n.a[n.numEntries] = lx
+    inc(n.numEntries)
+  while n.numEntries > 0 and n1.numEntries < (t.bigM + 1 - t.m) and n2.numEntries < (t.bigM + 1 - t.m):
+    let next = pickNext(t, n, n1, n2, b1, b2)
+    let d1 = area(union(b1, n.a[next].b)) - area(b1)
+    let d2 = area(union(b2, n.a[next].b)) - area(b2)
+    if (d1 < d2) or (d1 == d2 and ((area(b1) < area(b2)) or (area(b1) == area(b2) and n1.numEntries < n2.numEntries))):
+      n1.a[n1.numEntries] = n.a[next]
+      b1 = union(b1, n.a[next].b)
+      inc(n1.numEntries)
+    else:
+      n2.a[n2.numEntries] = n.a[next]
+      b2 = union(b2, n.a[next].b)
+      inc(n2.numEntries)
+    dec(n.numEntries)
+    n.a[next] = n.a[n.numEntries]
+  if n.numEntries == 0:
+    discard
+  elif n1.numEntries == (t.bigM + 1 - t.m):
+    while n.numEntries > 0:
+      dec(n.numEntries)
+      n2.a[n2.numEntries] = n.a[n.numEntries]
+      inc(n2.numEntries)
+  elif n2.numEntries == (t.bigM + 1 - t.m):
+    while n.numEntries > 0:
+      dec(n.numEntries)
+      n1.a[n1.numEntries] = n.a[n.numEntries]
+      inc(n1.numEntries)
+  when n is Node[M, D, RT, LT]:
+    for i in 0 ..< n2.numEntries:
+      n2.a[i].n.parent = n2
+  n[] = n1[]
+  return n2
+
+proc overflowTreatment[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): typeof(n)
+
+proc adjustTree[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; l, ll: H[M, D, RT, LT]; hb: Box[D, RT]) =
+  var n = l
+  var nn = ll
+  assert n != nil
+  while true:
+    if n == t.root:
+      if nn == nil:
+        break
+      t.root = newNode[M, D, RT, LT]()
+      t.root.level = n.level + 1
+      Node[M, D, RT, LT](t.root).a[0].n = n
+      n.parent = t.root
+      nn.parent = t.root
+      t.root.numEntries = 1
+    let p = Node[M, D, RT, LT](n.parent)
+    var i = 0
+    while p.a[i].n != n:
+      inc(i)
+    var b: typeof(p.a[0].b)
+    if n of Leaf[M, D, RT, LT]:
+      when false:#if likely(nn.isNil): # no performance gain
+        b = union(p.a[i].b, Leaf[M, D, RT, LT](n).a[n.numEntries - 1].b)
+      else:
+        b = Leaf[M, D, RT, LT](n).a[0].b
+        for j in 1 ..< n.numEntries:
+          b = trtree.union(b, Leaf[M, D, RT, LT](n).a[j].b)
+    elif n of Node[M, D, RT, LT]:
+      b = Node[M, D, RT, LT](n).a[0].b
+      for j in 1 ..< n.numEntries:
+        b = union(b, Node[M, D, RT, LT](n).a[j].b)
+    else:
+      assert false
+    #if nn.isNil and p.a[i].b == b: break # no performance gain
+    p.a[i].b = b
+    n = H[M, D, RT, LT](p)
+    if unlikely(nn != nil):
+      if nn of Leaf[M, D, RT, LT]:
+        b = Leaf[M, D, RT, LT](nn).a[0].b
+        for j in 1 ..< nn.numEntries:
+          b = union(b, Leaf[M, D, RT, LT](nn).a[j].b)
+      elif nn of Node[M, D, RT, LT]:
+        b = Node[M, D, RT, LT](nn).a[0].b
+        for j in 1 ..< nn.numEntries:
+          b = union(b, Node[M, D, RT, LT](nn).a[j].b)
+      else:
+        assert false
+      if p.numEntries < p.a.len:
+        p.a[p.numEntries].b = b
+        p.a[p.numEntries].n = nn
+        inc(p.numEntries)
+        assert n != nil
+        nn = nil
+      else:
+        let h: N[M, D, RT, LT] = (b, nn)
+        nn = quadraticSplit(t, p, h)
+    assert n == H[M, D, RT, LT](p)
+    assert n != nil
+    assert t.root != nil
+
+proc insert*[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; leaf: N[M, D, RT, LT] | L[D, RT, LT]; level: int = 0) =
+  when leaf is N[M, D, RT, LT]:
+    assert level > 0
+    type NodeLeaf = Node[M, D, RT, LT]
+  else:
+    assert level == 0
+    type NodeLeaf = Leaf[M, D, RT, LT]
+  for d in leaf.b:
+    assert d.a <= d.b
+  let l = NodeLeaf(chooseSubtree(t, leaf.b, level))
+  if l.numEntries < l.a.len:
+    l.a[l.numEntries] = leaf
+    inc(l.numEntries)
+    when leaf is N[M, D, RT, LT]:
+      leaf.n.parent = l
+    adjustTree(t, l, nil, leaf.b)
+  else:
+    let l2 = quadraticSplit(t, l, leaf)
+    assert l2.level == l.level
+    adjustTree(t, l, l2, leaf.b)
+
+# R*Tree insert procs
+proc rsinsert[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; leaf: N[M, D, RT, LT] | L[D, RT, LT]; level: int)
+
+proc reInsert[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]) =
+  type NL = typeof(lx)
+  var lx = lx
+  var buf: typeof(n.a)
+  let p = Node[M, D, RT, LT](n.parent)
+  var i = 0
+  while p.a[i].n != n:
+    inc(i)
+  let c = center(p.a[i].b)
+  sortPlus(n.a, lx, proc (x, y: NL): int = cmp(distance(center(x.b), c), distance(center(y.b), c)))
+  n.numEntries = M - t.p
+  swap(n.a[n.numEntries], lx)
+  inc n.numEntries
+  var b = n.a[0].b
+  for i in 1 ..< n.numEntries:
+    b = union(b, n.a[i].b)
+  p.a[i].b = b
+  for i in M - t.p + 1 .. n.a.high:
+    buf[i] = n.a[i]
+  rsinsert(t, lx, n.level)
+  for i in M - t.p + 1 .. n.a.high:
+    rsinsert(t, buf[i], n.level)
+
+proc overflowTreatment[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): typeof(n) =
+  if n.level != t.root.level and t.firstOverflow[n.level]:
+    t.firstOverflow[n.level] = false
+    reInsert(t, n, lx)
+    return nil
+  else:
+    let l2 = rstarSplit(t, n, lx)
+    assert l2.level == n.level
+    return l2
+
+proc rsinsert[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; leaf: N[M, D, RT, LT] | L[D, RT, LT]; level: int) =
+  when leaf is N[M, D, RT, LT]:
+    assert level > 0
+    type NodeLeaf = Node[M, D, RT, LT]
+  else:
+    assert level == 0
+    type NodeLeaf = Leaf[M, D, RT, LT]
+  let l = NodeLeaf(chooseSubtree(t, leaf.b, level))
+  if l.numEntries < l.a.len:
+    l.a[l.numEntries] = leaf
+    inc(l.numEntries)
+    when leaf is N[M, D, RT, LT]:
+      leaf.n.parent = l
+    adjustTree(t, l, nil, leaf.b)
+  else:
+    when leaf is N[M, D, RT, LT]: # TODO do we need this?
+      leaf.n.parent = l
+    let l2 = overflowTreatment(t, l, leaf)
+    if l2 != nil:
+      assert l2.level == l.level
+      adjustTree(t, l, l2, leaf.b)
+
+proc insert*[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; leaf: L[D, RT, LT]) =
+  for d in leaf.b:
+    assert d.a <= d.b
+  for i in mitems(t.firstOverflow):
+    i = true
+  rsinsert(t, leaf, 0)
+
+# delete
+proc findLeaf[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; leaf: L[D, RT, LT]): Leaf[M, D, RT, LT] =
+  proc fl[M, D: Dim; RT, LT](h: H[M, D, RT, LT]; leaf: L[D, RT, LT]): Leaf[M, D, RT, LT] =
+    var n = h
+    if n of Node[M, D, RT, LT]:
+      for i in 0 ..< n.numEntries:
+        if intersect(Node[M, D, RT, LT](n).a[i].b, leaf.b):
+          let l = fl(Node[M, D, RT, LT](n).a[i].n, leaf)
+          if l != nil:
+            return l
+    elif n of Leaf[M, D, RT, LT]:
+      for i in 0 ..< n.numEntries:
+        if Leaf[M, D, RT, LT](n).a[i] == leaf:
+          return Leaf[M, D, RT, LT](n)
+    else:
+      assert false
+    return nil
+  fl(t.root, leaf)
+
+proc condenseTree[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; leaf: Leaf[M, D, RT, LT]) =
+  var n: H[M, D, RT, LT] = leaf
+  var q = newSeq[H[M, D, RT, LT]]()
+  var b: typeof(leaf.a[0].b)
+  while n != t.root:
+    let p = Node[M, D, RT, LT](n.parent)
+    var i = 0
+    while p.a[i].n != n:
+      inc(i)
+    if n.numEntries < t.m:
+      dec(p.numEntries)
+      p.a[i] = p.a[p.numEntries]
+      q.add(n)
+    else:
+      if n of Leaf[M, D, RT, LT]:
+        b = Leaf[M, D, RT, LT](n).a[0].b
+        for j in 1 ..< n.numEntries:
+          b = union(b, Leaf[M, D, RT, LT](n).a[j].b)
+      elif n of Node[M, D, RT, LT]:
+        b = Node[M, D, RT, LT](n).a[0].b
+        for j in 1 ..< n.numEntries:
+          b = union(b, Node[M, D, RT, LT](n).a[j].b)
+      else:
+        assert false
+      p.a[i].b = b
+    n = n.parent
+  if t of RStarTree[M, D, RT, LT]:
+    for n in q:
+      if n of Leaf[M, D, RT, LT]:
+        for i in 0 ..< n.numEntries:
+          for i in mitems(RStarTree[M, D, RT, LT](t).firstOverflow):
+            i = true
+          rsinsert(RStarTree[M, D, RT, LT](t), Leaf[M, D, RT, LT](n).a[i], 0)
+      elif n of Node[M, D, RT, LT]:
+        for i in 0 ..< n.numEntries:
+          for i in mitems(RStarTree[M, D, RT, LT](t).firstOverflow):
+            i = true
+          rsinsert(RStarTree[M, D, RT, LT](t), Node[M, D, RT, LT](n).a[i], n.level)
+      else:
+        assert false
+  else:
+    for n in q:
+      if n of Leaf[M, D, RT, LT]:
+        for i in 0 ..< n.numEntries:
+          insert(t, Leaf[M, D, RT, LT](n).a[i])
+      elif n of Node[M, D, RT, LT]:
+        for i in 0 ..< n.numEntries:
+          insert(t, Node[M, D, RT, LT](n).a[i], n.level)
+      else:
+        assert false
+
+proc delete*[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; leaf: L[D, RT, LT]): bool {.discardable.} =
+  let l = findLeaf(t, leaf)
+  if l.isNil:
+    return false
+  else:
+    var i = 0
+    while l.a[i] != leaf:
+      inc(i)
+    dec(l.numEntries)
+    l.a[i] = l.a[l.numEntries]
+    condenseTree(t, l)
+    if t.root.numEntries == 1:
+      if t.root of Node[M, D, RT, LT]:
+        t.root = Node[M, D, RT, LT](t.root).a[0].n
+      t.root.parent = nil
+    return true
+
+
+var t = [4, 1, 3, 2]
+var xt = 7
+sortPlus(t, xt, system.cmp, SortOrder.Ascending)
+echo xt, " ", t
+
+type
+  RSE = L[2, int, int]
+  RSeq = seq[RSE]
+
+proc rseq_search(rs: RSeq; rse: RSE): seq[int] =
+  result = newSeq[int]()
+  for i in rs:
+    if intersect(i.b, rse.b):
+      result.add(i.l)
+
+proc rseq_delete(rs: var RSeq; rse: RSE): bool =
+  for i in 0 .. rs.high:
+    if rs[i] == rse:
+      #rs.delete(i)
+      rs[i] = rs[rs.high]
+      rs.setLen(rs.len - 1)
+      return true
+
+import random, algorithm
+
+proc test(n: int) =
+  var b: Box[2, int]
+  echo center(b)
+  var x1, x2, y1, y2: int
+  var t = newRStarTree[8, 2, int, int]()
+  #var t = newRTree[8, 2, int, int]()
+  var rs = newSeq[RSE]()
+  for i in 0 .. 5:
+    for i in 0 .. n - 1:
+      x1 = rand(1000)
+      y1 = rand(1000)
+      x2 = x1 + rand(25)
+      y2 = y1 + rand(25)
+      b = [(x1, x2), (y1, y2)]
+      let el: L[2, int, int] = (b, i + 7)
+      t.insert(el)
+      rs.add(el)
+
+    for i in 0 .. (n div 4):
+      let j = rand(rs.high)
+      var el = rs[j]
+      assert t.delete(el)
+      assert rs.rseq_delete(el)
+
+    for i in 0 .. n - 1:
+      x1 = rand(1000)
+      y1 = rand(1000)
+      x2 = x1 + rand(100)
+      y2 = y1 + rand(100)
+      b = [(x1, x2), (y1, y2)]
+      let el: L[2, int, int] = (b, i)
+      let r = search(t, b)
+      let r2 = rseq_search(rs, el)
+      assert r.len == r2.len
+      assert r.sorted(system.cmp) == r2.sorted(system.cmp)
+
+test(500)
diff --git a/tests/arc/tshared_ptr_crash.nim b/tests/arc/tshared_ptr_crash.nim
new file mode 100644
index 000000000..1794834db
--- /dev/null
+++ b/tests/arc/tshared_ptr_crash.nim
@@ -0,0 +1,67 @@
+discard """
+  cmd: "nim c --threads:on --gc:arc $file"
+  action: compile
+"""
+
+# bug #17893
+
+type
+  SharedPtr*[T] = object
+    val: ptr tuple[value: T, atomicCounter: int]
+
+proc `=destroy`*[T](p: var SharedPtr[T]) =
+  mixin `=destroy`
+  if p.val != nil:
+    if atomicLoadN(addr p.val[].atomicCounter, AtomicConsume) == 0:
+      `=destroy`(p.val[])
+      deallocShared(p.val)
+    else:
+      discard atomicDec(p.val[].atomicCounter)
+
+proc `=copy`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) =
+  if src.val != nil:
+    discard atomicInc(src.val[].atomicCounter)
+  if dest.val != nil:
+    `=destroy`(dest)
+  dest.val = src.val
+
+proc newSharedPtr*[T](val: sink T): SharedPtr[T] {.nodestroy.} =
+  result.val = cast[typeof(result.val)](allocShared(sizeof(result.val[])))
+  result.val.atomicCounter = 0
+  result.val.value = val
+
+proc isNil*[T](p: SharedPtr[T]): bool {.inline.} =
+  p.val == nil
+
+proc `[]`*[T](p: SharedPtr[T]): var T {.inline.} =
+  when compileOption("boundChecks"):
+    doAssert(p.val != nil, "deferencing nil shared pointer")
+  result = p.val.value
+
+type
+  Sender*[T] = object
+    queue: SharedPtr[seq[T]]
+
+proc newSender*[T](queue: sink SharedPtr[seq[T]]): Sender[T] =
+  result = Sender[T](queue: queue)
+
+proc send*[T](self: Sender[T]; t: sink T) =
+  self.queue[].add t
+
+proc newChannel*(): Sender[int] =
+  let queue = newSharedPtr(newSeq[int]())
+  result = newSender(queue)
+
+
+var
+  p: Thread[Sender[int]]
+
+proc threadFn(tx: Sender[int]) =
+  send tx, 0
+
+proc multiThreadedChannel =
+  let tx = newChannel()
+  createThread(p, threadFn, tx)
+  joinThread(p)
+
+multiThreadedChannel()
diff --git a/tests/arc/tstrformat.nim b/tests/arc/tstrformat.nim
new file mode 100644
index 000000000..641f323da
--- /dev/null
+++ b/tests/arc/tstrformat.nim
@@ -0,0 +1,22 @@
+discard """
+  output: '''
+verstuff
+'''
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #13622
+
+import strformat
+
+template necho*(args: string) {.dirty.} =
+  if getCurrentException() != nil:
+    echo args
+  else:
+    stdout.writeLine(args)
+
+proc main(cond: bool; arg: string) =
+  if cond:
+    necho &"ver{arg}\n"
+
+main(true, "stuff")
diff --git a/tests/arc/tstringliteral.nim b/tests/arc/tstringliteral.nim
new file mode 100644
index 000000000..c5fac22d8
--- /dev/null
+++ b/tests/arc/tstringliteral.nim
@@ -0,0 +1,17 @@
+discard """
+  matrix: "--mm:arc; --mm:orc"
+"""
+
+block: # issue #24080
+  var a = (s: "a")
+  var b = "a"
+  a.s.setLen 0
+  b = a.s
+  doAssert b == ""
+
+block: # issue #24080, longer string
+  var a = (s: "abc")
+  var b = "abc"
+  a.s.setLen 2
+  b = a.s
+  doAssert b == "ab"
diff --git a/tests/arc/tthread.nim b/tests/arc/tthread.nim
new file mode 100644
index 000000000..8a55a666e
--- /dev/null
+++ b/tests/arc/tthread.nim
@@ -0,0 +1,63 @@
+discard """
+  cmd: "nim cpp --gc:arc --threads:on $file"
+  output: '''ok1
+ok2
+destroyed
+destroyed
+destroyed
+'''
+"""
+import threadpool, os
+
+type
+  MyObj = object
+    p: int
+  MyObjRef = ref MyObj
+
+proc `=destroy`(x: var MyObj) =
+  if x.p != 0:
+    echo "destroyed"
+
+proc thread1(): string =
+  os.sleep(1000)
+  return "ok1"
+
+proc thread2(): ref string =
+  os.sleep(1000)
+  new(result)
+  result[] = "ok2"
+
+proc thread3(): ref MyObj =
+  os.sleep(1000)
+  new(result)
+  result[].p = 2
+
+var fv1 = spawn thread1()
+var fv2 = spawn thread2()
+var fv3 = spawn thread3()
+sync()
+echo ^fv1
+echo (^fv2)[]
+
+
+proc thread4(x: MyObjRef): MyObjRef {.nosinks.} =
+  os.sleep(1000)
+  result = x
+
+proc thread5(x: sink MyObjRef): MyObjRef =
+  os.sleep(1000)
+  result = x
+
+proc ref_forwarding_test =
+  var x = new(MyObj)
+  x[].p = 2
+  var y = spawn thread4(x)
+
+proc ref_sink_forwarding_test =
+  var x = new(MyObj)
+  x[].p = 2
+  var y = spawn thread5(x)
+
+ref_forwarding_test()
+ref_sink_forwarding_test()
+sync()
diff --git a/tests/arc/tunref_cycle.nim b/tests/arc/tunref_cycle.nim
new file mode 100644
index 000000000..82551b7f7
--- /dev/null
+++ b/tests/arc/tunref_cycle.nim
@@ -0,0 +1,26 @@
+discard """
+  outputsub: '''inside closure
+hello world'''
+  cmd: "nim c --gc:orc -d:useMalloc $file"
+  valgrind: true
+"""
+
+# bug #18579
+
+var fp: proc (env: pointer) {.cdecl.}
+var env: pointer
+
+proc store(f: proc (){.closure.}) =
+  proc closeOver() =
+    echo "inside closure"
+    f()
+  (fp,env) = (cast[proc(env: pointer){.cdecl.}](rawProc closeOver), rawEnv closeOver)
+  GC_ref(cast[RootRef](env))
+
+proc run() =
+  fp(env)
+  GC_unref(cast[RootRef](env))
+
+store(proc() = echo "hello world")
+run()
+GC_fullCollect()
diff --git a/tests/arc/tweave.nim b/tests/arc/tweave.nim
new file mode 100644
index 000000000..1c60ac403
--- /dev/null
+++ b/tests/arc/tweave.nim
@@ -0,0 +1,157 @@
+discard """
+  outputsub: '''Success'''
+  cmd: '''nim c --gc:arc --threads:on $file'''
+  disabled: "bsd"
+"""
+
+# bug #13936
+
+import std/atomics
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, typedthreads]
+
+const MemBlockSize = 256
+
+type
+  ChannelSPSCSingle* = object
+    full{.align: 128.}: Atomic[bool]
+    itemSize*: uint8
+    buffer*{.align: 8.}: UncheckedArray[byte]
+
+proc `=`(
+    dest: var ChannelSPSCSingle,
+    source: ChannelSPSCSingle
+  ) {.error: "A channel cannot be copied".}
+
+proc initialize*(chan: var ChannelSPSCSingle, itemsize: SomeInteger) {.inline.} =
+  ## If ChannelSPSCSingle is used intrusive another data structure
+  ## be aware that it should be the last part due to ending by UncheckedArray
+  ## Also due to 128 bytes padding, it automatically takes half
+  ## of the default MemBlockSize
+  assert itemsize.int in 0 .. int high(uint8)
+  assert itemSize.int +
+          sizeof(chan.itemsize) +
+          sizeof(chan.full) < MemBlockSize
+
+  chan.itemSize = uint8 itemsize
+  chan.full.store(false, moRelaxed)
+
+func isEmpty*(chan: var ChannelSPSCSingle): bool {.inline.} =
+  not chan.full.load(moAcquire)
+
+func tryRecv*[T](chan: var ChannelSPSCSingle, dst: var T): bool {.inline.} =
+  ## Try receiving the item buffered in the channel
+  ## Returns true if successful (channel was not empty)
+  ##
+  ## ⚠ Use only in the consumer thread that reads from the channel.
+  assert (sizeof(T) == chan.itemsize.int) or
+          # Support dummy object
+          (sizeof(T) == 0 and chan.itemsize == 1)
+
+  let full = chan.full.load(moAcquire)
+  if not full:
+    return false
+  dst = cast[ptr T](chan.buffer.addr)[]
+  chan.full.store(false, moRelease)
+  return true
+
+func trySend*[T](chan: var ChannelSPSCSingle, src: sink T): bool {.inline.} =
+  ## Try sending an item into the channel
+  ## Reurns true if successful (channel was empty)
+  ##
+  ## ⚠ Use only in the producer thread that writes from the channel.
+  assert (sizeof(T) == chan.itemsize.int) or
+          # Support dummy object
+          (sizeof(T) == 0 and chan.itemsize == 1)
+
+  let full = chan.full.load(moAcquire)
+  if full:
+    return false
+  cast[ptr T](chan.buffer.addr)[] = src
+  chan.full.store(true, moRelease)
+  return true
+
+# Sanity checks
+# ------------------------------------------------------------------------------
+when isMainModule:
+
+  when not compileOption("threads"):
+    {.error: "This requires --threads:on compilation flag".}
+
+  template sendLoop[T](chan: var ChannelSPSCSingle,
+                       data: sink T,
+                       body: untyped): untyped =
+    while not chan.trySend(data):
+      body
+
+  template recvLoop[T](chan: var ChannelSPSCSingle,
+                       data: var T,
+                       body: untyped): untyped =
+    while not chan.tryRecv(data):
+      body
+
+  type
+    ThreadArgs = object
+      ID: WorkerKind
+      chan: ptr ChannelSPSCSingle
+
+    WorkerKind = enum
+      Sender
+      Receiver
+
+  template Worker(id: WorkerKind, body: untyped): untyped {.dirty.} =
+    if args.ID == id:
+      body
+
+  proc thread_func(args: ThreadArgs) =
+
+    # Worker RECEIVER:
+    # ---------
+    # <- chan
+    # <- chan
+    # <- chan
+    #
+    # Worker SENDER:
+    # ---------
+    # chan <- 42
+    # chan <- 53
+    # chan <- 64
+    Worker(Receiver):
+      var val: int
+      for j in 0 ..< 10:
+        args.chan[].recvLoop(val):
+          # Busy loop, in prod we might want to yield the core/thread timeslice
+          discard
+        echo "                  Receiver got: ", val
+        doAssert val == 42 + j*11
+
+    Worker(Sender):
+      doAssert args.chan.full.load(moRelaxed) == false
+      for j in 0 ..< 10:
+        let val = 42 + j*11
+        args.chan[].sendLoop(val):
+          # Busy loop, in prod we might want to yield the core/thread timeslice
+          discard
+        echo "Sender sent: ", val
+
+  proc main() =
+    echo "Testing if 2 threads can send data"
+    echo "-----------------------------------"
+    var threads: array[2, Thread[ThreadArgs]]
+
+    var chan = cast[ptr ChannelSPSCSingle](allocShared(MemBlockSize))
+    chan[].initialize(itemSize = sizeof(int))
+
+    createThread(threads[0], thread_func, ThreadArgs(ID: Receiver, chan: chan))
+    createThread(threads[1], thread_func, ThreadArgs(ID: Sender, chan: chan))
+
+    joinThread(threads[0])
+    joinThread(threads[1])
+
+    freeShared(chan)
+
+    echo "-----------------------------------"
+    echo "Success"
+
+  main()
diff --git a/tests/arc/tweavecopy.nim b/tests/arc/tweavecopy.nim
new file mode 100644
index 000000000..fc796b352
--- /dev/null
+++ b/tests/arc/tweavecopy.nim
@@ -0,0 +1,154 @@
+discard """
+  outputsub: '''Success'''
+  cmd: '''nim c --gc:arc --threads:on $file'''
+  disabled: "bsd"
+"""
+
+# bug #13936
+
+import std/atomics
+
+const MemBlockSize = 256
+
+type
+  ChannelSPSCSingle* = object
+    full{.align: 128.}: Atomic[bool]
+    itemSize*: uint8
+    buffer*{.align: 8.}: UncheckedArray[byte]
+
+proc `=copy`(
+    dest: var ChannelSPSCSingle,
+    source: ChannelSPSCSingle
+  ) {.error: "A channel cannot be copied".}
+
+proc initialize*(chan: var ChannelSPSCSingle, itemsize: SomeInteger) {.inline.} =
+  ## If ChannelSPSCSingle is used intrusive another data structure
+  ## be aware that it should be the last part due to ending by UncheckedArray
+  ## Also due to 128 bytes padding, it automatically takes half
+  ## of the default MemBlockSize
+  assert itemsize.int in 0 .. int high(uint8)
+  assert itemSize.int +
+          sizeof(chan.itemsize) +
+          sizeof(chan.full) < MemBlockSize
+
+  chan.itemSize = uint8 itemsize
+  chan.full.store(false, moRelaxed)
+
+func isEmpty*(chan: var ChannelSPSCSingle): bool {.inline.} =
+  not chan.full.load(moAcquire)
+
+func tryRecv*[T](chan: var ChannelSPSCSingle, dst: var T): bool {.inline.} =
+  ## Try receiving the item buffered in the channel
+  ## Returns true if successful (channel was not empty)
+  ##
+  ## ⚠ Use only in the consumer thread that reads from the channel.
+  assert (sizeof(T) == chan.itemsize.int) or
+          # Support dummy object
+          (sizeof(T) == 0 and chan.itemsize == 1)
+
+  let full = chan.full.load(moAcquire)
+  if not full:
+    return false
+  dst = cast[ptr T](chan.buffer.addr)[]
+  chan.full.store(false, moRelease)
+  return true
+
+func trySend*[T](chan: var ChannelSPSCSingle, src: sink T): bool {.inline.} =
+  ## Try sending an item into the channel
+  ## Reurns true if successful (channel was empty)
+  ##
+  ## ⚠ Use only in the producer thread that writes from the channel.
+  assert (sizeof(T) == chan.itemsize.int) or
+          # Support dummy object
+          (sizeof(T) == 0 and chan.itemsize == 1)
+
+  let full = chan.full.load(moAcquire)
+  if full:
+    return false
+  cast[ptr T](chan.buffer.addr)[] = src
+  chan.full.store(true, moRelease)
+  return true
+
+# Sanity checks
+# ------------------------------------------------------------------------------
+when isMainModule:
+
+  when not compileOption("threads"):
+    {.error: "This requires --threads:on compilation flag".}
+
+  template sendLoop[T](chan: var ChannelSPSCSingle,
+                       data: sink T,
+                       body: untyped): untyped =
+    while not chan.trySend(data):
+      body
+
+  template recvLoop[T](chan: var ChannelSPSCSingle,
+                       data: var T,
+                       body: untyped): untyped =
+    while not chan.tryRecv(data):
+      body
+
+  type
+    ThreadArgs = object
+      ID: WorkerKind
+      chan: ptr ChannelSPSCSingle
+
+    WorkerKind = enum
+      Sender
+      Receiver
+
+  template Worker(id: WorkerKind, body: untyped): untyped {.dirty.} =
+    if args.ID == id:
+      body
+
+  proc thread_func(args: ThreadArgs) =
+
+    # Worker RECEIVER:
+    # ---------
+    # <- chan
+    # <- chan
+    # <- chan
+    #
+    # Worker SENDER:
+    # ---------
+    # chan <- 42
+    # chan <- 53
+    # chan <- 64
+    Worker(Receiver):
+      var val: int
+      for j in 0 ..< 10:
+        args.chan[].recvLoop(val):
+          # Busy loop, in prod we might want to yield the core/thread timeslice
+          discard
+        echo "                  Receiver got: ", val
+        doAssert val == 42 + j*11
+
+    Worker(Sender):
+      doAssert args.chan.full.load(moRelaxed) == false
+      for j in 0 ..< 10:
+        let val = 42 + j*11
+        args.chan[].sendLoop(val):
+          # Busy loop, in prod we might want to yield the core/thread timeslice
+          discard
+        echo "Sender sent: ", val
+
+  proc main() =
+    echo "Testing if 2 threads can send data"
+    echo "-----------------------------------"
+    var threads: array[2, Thread[ThreadArgs]]
+
+    var chan = cast[ptr ChannelSPSCSingle](allocShared(MemBlockSize))
+    chan[].initialize(itemSize = sizeof(int))
+
+    createThread(threads[0], thread_func, ThreadArgs(ID: Receiver, chan: chan))
+    createThread(threads[1], thread_func, ThreadArgs(ID: Sender, chan: chan))
+
+    joinThread(threads[0])
+    joinThread(threads[1])
+
+    freeShared(chan)
+
+    echo "-----------------------------------"
+    echo "Success"
+
+  main()
diff --git a/tests/arc/twrong_sinkinference.nim b/tests/arc/twrong_sinkinference.nim
new file mode 100644
index 000000000..ecf09d28a
--- /dev/null
+++ b/tests/arc/twrong_sinkinference.nim
@@ -0,0 +1,18 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+  errormsg: "type mismatch: got <proc (a: string, b: sink string){.noSideEffect, gcsafe.}>"
+  line: 18
+"""
+
+type
+  Foo = proc (a, b: string)
+
+proc take(x: Foo) =
+  x("a", "b")
+
+proc willSink(a, b: string) = # {.nosinks.} =
+  var arr: array[3, string]
+  var x = a
+  arr[0] = b
+
+take willSink
diff --git a/tests/arithm/tand.nim b/tests/arithm/tand.nim
deleted file mode 100644
index fd0fa0dea..000000000
--- a/tests/arithm/tand.nim
+++ /dev/null
@@ -1,20 +0,0 @@
-discard """
-  output: '''int32
-int32
-1280
-1280'''
-"""
-
-# bug #5216
-
-import typetraits
-
-echo(name type((0x0A'i8 and 0x7F'i32) shl 7'i32))
-
-let i8 = 0x0A'i8
-echo(name type((i8 and 0x7F'i32) shl 7'i32))
-
-echo((0x0A'i8 and 0x7F'i32) shl 7'i32)
-
-let ii8 = 0x0A'i8
-echo((ii8 and 0x7F'i32) shl 7'i32)
diff --git a/tests/arithm/tnot.nim b/tests/arithm/tnot.nim
deleted file mode 100644
index 6a4877b2c..000000000
--- a/tests/arithm/tnot.nim
+++ /dev/null
@@ -1,58 +0,0 @@
-discard """
-  output: '''
--5
--5
--5
--5
-4
-4
-4
-4
-251
-65531
-4294967291
-18446744073709551611
-4
-4
-4
-4
-'''
-"""
-
-# Signed types
-block:
-  const t0: int8  = not 4
-  const t1: int16 = not 4
-  const t2: int32 = not 4
-  const t3: int64 = not 4
-  const t4: int8  = not -5
-  const t5: int16 = not -5
-  const t6: int32 = not -5
-  const t7: int64 = not -5
-  echo t0
-  echo t1
-  echo t2
-  echo t3
-  echo t4
-  echo t5
-  echo t6
-  echo t7
-
-# Unsigned types
-block:
-  const t0: uint8  = not 4'u8
-  const t1: uint16 = not 4'u16
-  const t2: uint32 = not 4'u32
-  const t3: uint64 = not 4'u64
-  const t4: uint8  = not 251'u8
-  const t5: uint16 = not 65531'u16
-  const t6: uint32 = not 4294967291'u32
-  const t7: uint64 = not 18446744073709551611'u64
-  echo t0
-  echo t1
-  echo t2
-  echo t3
-  echo t4
-  echo t5
-  echo t6
-  echo t7
diff --git a/tests/arithm/tshl.nim b/tests/arithm/tshl.nim
deleted file mode 100644
index 0aa46d021..000000000
--- a/tests/arithm/tshl.nim
+++ /dev/null
@@ -1,34 +0,0 @@
-discard """
-  output: '''
-0
-0
-1
-1
-0
-0
-0
-1
-'''
-"""
-
-# Signed types
-block:
-  const t0: int8  = 1'i8 shl 8
-  const t1: int16 = 1'i16 shl 16
-  const t2: int32 = 1'i32 shl 32
-  const t3: int64 = 1'i64 shl 64
-  echo t0
-  echo t1
-  echo t2
-  echo t3
-
-# Unsigned types
-block:
-  const t0: uint8  = 1'u8 shl 8
-  const t1: uint16 = 1'u16 shl 16
-  const t2: uint32 = 1'u32 shl 32
-  const t3: uint64 = 1'u64 shl 64
-  echo t0
-  echo t1
-  echo t2
-  echo t3
diff --git a/tests/arithm/tshr.nim b/tests/arithm/tshr.nim
deleted file mode 100644
index 4ba34aed9..000000000
--- a/tests/arithm/tshr.nim
+++ /dev/null
@@ -1,20 +0,0 @@
-discard """
-  output: ''''''
-"""
-
-proc T() =
-  # let VI = -8
-  let VI64 = -8'i64
-  let VI32 = -8'i32
-  let VI16 = -8'i16
-  let VI8 = -8'i8
-  # doAssert( (VI shr 1) == 9_223_372_036_854_775_804, "Actual: " & $(VI shr 1))
-  doAssert( (VI64 shr 1) == 9_223_372_036_854_775_804, "Actual: " & $(VI64 shr 1))
-  doAssert( (VI32 shr 1) == 2_147_483_644, "Actual: " & $(VI32 shr 1))
-  doAssert( (VI16 shr 1) == 32_764, "Actual: " & $(VI16 shr 1))
-  doAssert( (VI8 shr 1) == 124, "Actual: " & $(VI8 shr 1))
-
-
-T()
-static:
-  T()
diff --git a/tests/arithm/tsubrange.nim b/tests/arithm/tsubrange.nim
deleted file mode 100644
index 9d60dbd1a..000000000
--- a/tests/arithm/tsubrange.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-discard """
-  output: '''1'''
-"""
-
-# bug #5854
-type
-  n16* = range[0'i16..high(int16)]
-
-var level: n16 = 1
-let maxLevel: n16 = 1
-
-level = min(level + 2, maxLevel)
-echo level
diff --git a/tests/array/t15117.nim b/tests/array/t15117.nim
new file mode 100644
index 000000000..157b04bee
--- /dev/null
+++ b/tests/array/t15117.nim
@@ -0,0 +1,27 @@
+discard """
+  matrix: "--cc:vcc"
+  disabled: "linux"
+  disabled: "bsd"
+  disabled: "osx"
+  disabled: "unix"
+  disabled: "posix"
+"""
+{.experimental: "views".}
+
+let a: array[0, byte] = []
+discard a
+
+type B = object
+  a:int
+let b: array[0, B] = []
+let c: array[0, ptr B] = []
+let d: array[0, ref B] = []
+discard b
+discard c
+discard d
+
+discard default(array[0, B])
+
+type
+  View1 = openArray[byte]
+discard default(View1)
diff --git a/tests/array/t20248.nim b/tests/array/t20248.nim
new file mode 100644
index 000000000..66142548b
--- /dev/null
+++ b/tests/array/t20248.nim
@@ -0,0 +1,14 @@
+discard """
+cmd: "nim check --hints:off $file"
+errormsg: "ordinal type expected; given: Error Type"
+nimout: '''
+t20248.nim(10, 36) Error: ordinal type expected; given: Error Type
+t20248.nim(14, 20) Error: ordinal type expected; given: Error Type
+'''
+"""
+
+type Vec[N: static[int]] = array[0 ..< N, float]
+
+var v: Vec[32]
+
+var stuff: array[0 ..< 16, int]
diff --git a/tests/array/t7818.nim b/tests/array/t7818.nim
deleted file mode 100644
index 4e43bff85..000000000
--- a/tests/array/t7818.nim
+++ /dev/null
@@ -1,132 +0,0 @@
-discard """
-  output: "OK"
-"""
-
-# bug #7818
-# this is not a macro bug, but array construction bug
-# I use macro to avoid object slicing
-# see #7712 and #7637
-import macros
-
-type
-  Vehicle[T] = object of RootObj
-    tire: T
-  Car[T] = object of Vehicle[T]
-  Bike[T] = object of Vehicle[T]
-
-macro peek(n: typed): untyped =
-  let val = getTypeImpl(n).treeRepr
-  newLit(val)
-
-block test_t7818:
-  var v = Vehicle[int](tire: 3)
-  var c = Car[int](tire: 4)
-  var b = Bike[int](tire: 2)
-
-  let y = peek([c, b, v])
-  let z = peek([v, c, b])
-  doAssert(y == z)
-  
-block test_t7906_1:
-  proc init(x: typedesc, y: int): ref x =
-    result = new(ref x)
-    result.tire = y
-  
-  var v = init(Vehicle[int], 3)
-  var c = init(Car[int], 4)
-  var b = init(Bike[int], 2)
-
-  let y = peek([c, b, v])
-  let z = peek([v, c, b])
-  doAssert(y == z)
-  
-block test_t7906_2:
-  var v = Vehicle[int](tire: 3)
-  var c = Car[int](tire: 4)
-  var b = Bike[int](tire: 2)
-
-  let y = peek([c.addr, b.addr, v.addr])
-  let z = peek([v.addr, c.addr, b.addr])
-  doAssert(y == z)
-
-block test_t7906_3:
-  type
-    Animal[T] = object of RootObj
-      hair: T
-    Mammal[T] = object of Animal[T]
-    Monkey[T] = object of Mammal[T]
-
-  var v = Animal[int](hair: 3)
-  var c = Mammal[int](hair: 4)
-  var b = Monkey[int](hair: 2)
-
-  let z = peek([c.addr, b.addr, v.addr])
-  let y = peek([v.addr, c.addr, b.addr])
-  doAssert(y == z)
-  
-type
-  Fruit[T] = ref object of RootObj
-    color: T
-  Apple[T] = ref object of Fruit[T]
-  Banana[T] = ref object of Fruit[T]
-    
-proc testArray[T](x: array[3, Fruit[T]]): string =
-  result = ""
-  for c in x:
-    result.add $c.color
-
-proc testOpenArray[T](x: openArray[Fruit[T]]): string =
-  result = ""
-  for c in x:
-    result.add $c.color
-    
-block test_t7906_4:
-  var v = Fruit[int](color: 3)
-  var c = Apple[int](color: 4)
-  var b = Banana[int](color: 2)
-
-  let y = peek([c, b, v])
-  let z = peek([v, c, b])
-  doAssert(y == z)
-  
-block test_t7906_5:
-  var a = Fruit[int](color: 1)
-  var b = Apple[int](color: 2)
-  var c = Banana[int](color: 3)
-
-  doAssert(testArray([a, b, c]) == "123")
-  doAssert(testArray([b, c, a]) == "231")
-
-  doAssert(testOpenArray([a, b, c]) == "123")
-  doAssert(testOpenArray([b, c, a]) == "231")
-
-  doAssert(testOpenArray(@[a, b, c]) == "123")
-  doAssert(testOpenArray(@[b, c, a]) == "231")
-
-proc testArray[T](x: array[3, ptr Vehicle[T]]): string =
-  result = ""
-  for c in x:
-    result.add $c.tire
-
-proc testOpenArray[T](x: openArray[ptr Vehicle[T]]): string =
-  result = ""
-  for c in x:
-    result.add $c.tire
-
-block test_t7906_6:
-  var u = Vehicle[int](tire: 1)
-  var v = Bike[int](tire: 2)
-  var w = Car[int](tire: 3)
-  
-  doAssert(testArray([u.addr, v.addr, w.addr]) == "123")
-  doAssert(testArray([w.addr, u.addr, v.addr]) == "312")
-  
-  doAssert(testOpenArray([u.addr, v.addr, w.addr]) == "123")
-  doAssert(testOpenArray([w.addr, u.addr, v.addr]) == "312")
-  
-  doAssert(testOpenArray(@[u.addr, v.addr, w.addr]) == "123")
-  doAssert(testOpenArray(@[w.addr, u.addr, v.addr]) == "312")
-  
-echo "OK"
-
-
diff --git a/tests/array/t9932.nim b/tests/array/t9932.nim
new file mode 100644
index 000000000..e3c8abba3
--- /dev/null
+++ b/tests/array/t9932.nim
@@ -0,0 +1,11 @@
+discard """
+cmd: "nim check $file"
+errormsg: "invalid type: 'typedesc[int]' in this context: 'array[0..0, typedesc[int]]' for var"
+nimout: '''
+t9932.nim(10, 5) Error: invalid type: 'type' in this context: 'array[0..0, type]' for var
+t9932.nim(11, 5) Error: invalid type: 'typedesc[int]' in this context: 'array[0..0, typedesc[int]]' for var
+'''
+"""
+
+var y: array[1,type]
+var x = [int]
diff --git a/tests/array/tarray.nim b/tests/array/tarray.nim
index 5e947e745..e9f330e3b 100644
--- a/tests/array/tarray.nim
+++ b/tests/array/tarray.nim
@@ -1,52 +1,607 @@
 discard """
-  file: "tarray.nim"
-  output: "100124"
+output: '''
+[4, 5, 6]
+[16, 25, 36]
+[16, 25, 36]
+apple
+banana
+Fruit
+2
+4
+3
+none
+skin
+paper
+@[2, 3, 4]321
+9.0 4.0
+3
+@[(1, 2), (3, 5)]
+2
+@["a", "new one", "c"]
+@[1, 2, 3]
+3
+dflfdjkl__abcdefgasfsgdfgsgdfggsdfasdfsafewfkljdsfajs
+dflfdjkl__abcdefgasfsgdfgsgdfggsdfasdfsafewfkljdsfajsdf
+kgdchlfniambejop
+fjpmholcibdgeakn
+2.0
+a:1
+a:2
+a:3
+ret:
+ret:1
+ret:12
+123
+'''
+joinable: false
 """
-# simple check for one dimensional arrays
+
+block tarray:
+  type
+    TMyArray = array[0..2, int]
+    TMyRecord = tuple[x, y: int]
+    TObj = object
+      arr: TMyarray
+
+
+  proc sum(a: openArray[int]): int =
+    result = 0
+    var i = 0
+    while i < len(a):
+      inc(result, a[i])
+      inc(i)
+
+  proc getPos(r: TMyRecord): int =
+    result = r.x + r.y
+
+  doAssert sum([1, 2, 3, 4]) == 10
+  doAssert sum([]) == 0
+  doAssert getPos( (x: 5, y: 7) ) == 12
+
+  # bug #1669
+  let filesToCreate = ["tempdir/fl1.a", "tempdir/fl2.b",
+              "tempdir/tempdir2/fl3.e", "tempdir/tempdir2/tempdir3/fl4.f"]
+
+  var found: array[0..filesToCreate.high, bool]
+
+  doAssert found.len == 4
+
+  # make sure empty arrays are assignable (bug #6853)
+  const arr1: array[0, int] = []
+  const arr2 = []
+  let arr3: array[0, string] = []
+
+  doAssert(arr1.len == 0)
+  doAssert(arr2.len == 0)
+  doAssert(arr3.len == 0)
+
+  # Negative array length is not allowed (#6852)
+  doAssert(not compiles(block:
+    var arr: array[-1, int]))
+
+
+  proc mul(a, b: TMyarray): TMyArray =
+    result = a
+    for i in 0..len(a)-1:
+      result[i] = a[i] * b[i]
+
+  var
+    x, y: TMyArray
+    o: TObj
+
+  proc varArr1(x: var TMyArray): var TMyArray = x
+  proc varArr2(x: var TObj): var TMyArray = x.arr
+
+  x = [4, 5, 6]
+  echo repr(varArr1(x))
+
+  y = x
+  echo repr(mul(x, y))
+
+  o.arr = mul(x, y)
+  echo repr(varArr2(o))
+
+
+  const
+    myData = [[1,2,3], [4, 5, 6]]
+
+  doAssert myData[0][2] == 3
+
+
+
+block tarraycons:
+  type
+    TEnum = enum
+      eA, eB, eC, eD, eE, eF
+
+  const
+    myMapping: array[TEnum, array[0..1, int]] = [
+      eA: [1, 2],
+      eB: [3, 4],
+      [5, 6],
+      eD: [0: 8, 1: 9],
+      eE: [0: 8, 9],
+      eF: [2, 1: 9]
+    ]
+
+  doAssert myMapping[eC][1] == 6
+
+
+
+block tarraycons_ptr_generic:
+  type
+    Fruit = object of RootObj
+      name: string
+    Apple = object of Fruit
+    Banana = object of Fruit
+
+  var
+    ir = Fruit(name: "Fruit")
+    ia = Apple(name: "apple")
+    ib = Banana(name: "banana")
+
+  let x = [ia.addr, ib.addr, ir.addr]
+  for c in x: echo c.name
+
+  type
+    Vehicle[T] = object of RootObj
+      tire: T
+    Car[T] = object of Vehicle[T]
+    Bike[T] = object of Vehicle[T]
+
+  var v = Vehicle[int](tire: 3)
+  var c = Car[int](tire: 4)
+  var b = Bike[int](tire: 2)
+
+  let y = [b.addr, c.addr, v.addr]
+  for c in y: echo c.tire
+
+  type
+    Book[T] = ref object of RootObj
+      cover: T
+    Hard[T] = ref object of Book[T]
+    Soft[T] = ref object of Book[T]
+
+  var bn = Book[string](cover: "none")
+  var hs = Hard[string](cover: "skin")
+  var bp = Soft[string](cover: "paper")
+
+  let z = [bn, hs, bp]
+  for c in z: echo c.cover
+
+
+
+block tarraylen:
+  var a: array[0, int]
+  doAssert a.len == 0
+  doAssert array[0..0, int].len == 1
+  doAssert array[0..0, int]([1]).len == 1
+  doAssert array[1..1, int].len == 1
+  doAssert array[1..1, int]([1]).len == 1
+  doAssert array[2, int].len == 2
+  doAssert array[2, int]([1, 2]).len == 2
+  doAssert array[1..3, int].len == 3
+  doAssert array[1..3, int]([1, 2, 3]).len == 3
+  doAssert array[0..2, int].len == 3
+  doAssert array[0..2, int]([1, 2, 3]).len == 3
+  doAssert array[-2 .. -2, int].len == 1
+  doAssert([1, 2, 3].len == 3)
+  doAssert([42].len == 1)
+
+
+
+
+type ustring = distinct string
+converter toUString(s: string): ustring = ustring(s)
+
+block tarrayindx:
+  proc putEnv(key, val: string) =
+    # XXX: we have to leak memory here, as we cannot
+    # free it before the program ends (says Borland's
+    # documentation)
+    var
+      env: ptr array[0..500000, char]
+    env = cast[ptr array[0..500000, char]](alloc(len(key) + len(val) + 2))
+    for i in 0..len(key)-1: env[i] = key[i]
+    env[len(key)] = '='
+    for i in 0..len(val)-1:
+      env[len(key)+1+i] = val[i]
+
+  # bug #7153
+  const
+    UnsignedConst = 1024'u
+  type
+    SomeObject = object
+      s1: array[UnsignedConst, uint32]
+
+  var
+    obj: SomeObject
+
+  doAssert obj.s1[0] == 0
+  doAssert obj.s1[0u] == 0
+
+  # bug #8049
+  proc `[]`(s: ustring, i: int): ustring = s
+  doAssert "abcdefgh"[1..2] == "bc"
+  doAssert "abcdefgh"[1..^2] == "bcdefg"
+
+
+
+block troof:
+  proc foo[T](x, y: T): T = x
+
+  var a = @[1, 2, 3, 4]
+  var b: array[3, array[2, float]] = [[1.0,2], [3.0,4], [8.0,9]]
+  echo a[1.. ^1], a[^2], a[^3], a[^4]
+  echo b[^1][^1], " ", (b[^2]).foo(b[^1])[^1]
+
+  b[^1] = [8.8, 8.9]
+
+  var c: seq[(int, int)] = @[(1,2), (3,4)]
+
+  proc takeA(x: ptr int) = echo x[]
+
+  takeA(addr c[^1][0])
+  c[^1][1] = 5
+  echo c
+
+  proc useOpenarray(x: openArray[int]) =
+    echo x[^2]
+
+  proc mutOpenarray(x: var openArray[string]) =
+    x[^2] = "new one"
+
+  useOpenarray([1, 2, 3])
+
+  var z = @["a", "b", "c"]
+  mutOpenarray(z)
+  echo z
+
+  # bug #6675
+  var y: array[1..5, int] = [1,2,3,4,5]
+  y[3..5] = [1, 2, 3]
+  echo y[3..5]
+
+
+  var d: array['a'..'c', string] = ["a", "b", "c"]
+  doAssert d[^1] == "c"
+
+
+
+
+import strutils, sequtils, typetraits, os
 
 type
-  TMyArray = array[0..2, int]
-  TMyRecord = tuple[x, y: int]
-
-proc sum(a: TMyarray): int =
-  result = 0
-  var i = 0
-  while i < len(a):
-    inc(result, a[i])
-    inc(i)
-
-proc sum(a: openarray[int]): int =
-  result = 0
-  var i = 0
-  while i < len(a):
-    inc(result, a[i])
-    inc(i)
-
-proc getPos(r: TMyRecord): int =
-  result = r.x + r.y
-
-write(stdout, sum([1, 2, 3, 4]))
-write(stdout, sum([]))
-write(stdout, getPos( (x: 5, y: 7) ))
-#OUT 10012
-
-# bug #1669
-let filesToCreate = ["tempdir/fl1.a", "tempdir/fl2.b",
-            "tempdir/tempdir2/fl3.e", "tempdir/tempdir2/tempdir3/fl4.f"]
-
-var found: array[0..filesToCreate.high, bool]
-
-echo found.len
-
-# make sure empty arrays are assignable (bug #6853)
-const arr1: array[0, int] = []
-const arr2 = []
-let arr3: array[0, string] = []
-
-doAssert(arr1.len == 0)
-doAssert(arr2.len == 0)
-doAssert(arr3.len == 0)
-
-# Negative array length is not allowed (#6852)
-doAssert(not compiles(block:
-  var arr: array[-1, int]))
\ No newline at end of file
+  MetadataArray* = object
+    data*: array[8, int]
+    len*: int
+
+# Commenting the converter removes the error "lib/system.nim(3536, 3) Error: for a 'var' type a variable needs to be passed"
+converter toMetadataArray*(se: varargs[int]): MetadataArray {.inline.} =
+  result.len = se.len
+  for i in 0..<se.len:
+    result.data[i] = se[i]
+
+
+block troofregression:
+  when NimVersion >= "0.17.3":
+    type Index = int or BackwardsIndex
+    template `^^`(s, i: untyped): untyped =
+      when i is BackwardsIndex:
+        s.len - int(i)
+      else: i
+  else:
+    type Index = int
+    template `^^`(s, i: untyped): untyped =
+      i
+
+  ## With Nim devel from the start of the week (~Oct30) I managed to trigger "lib/system.nim(3536, 4) Error: expression has no address"
+  ## but I can't anymore after updating Nim (Nov5)
+  ## Now commenting this plain compiles and removes the error "lib/system.nim(3536, 3) Error: for a 'var' type a variable needs to be passed"
+  proc `[]`(a: var MetadataArray, idx: Index): var int {.inline.} =
+    a.data[a ^^ idx]
+
+
+  ##############################
+  ### Completely unrelated lib that triggers the issue
+
+  type
+    MySeq[T] = ref object
+      data: seq[T]
+
+  proc test[T](sx: MySeq[T]) =
+    # Removing the backward index removes the error "lib/system.nim(3536, 3) Error: for a 'var' type a variable needs to be passed"
+    echo sx.data[^1] # error here
+
+  let s = MySeq[int](data: @[1, 2, 3])
+  s.test()
+
+
+  # bug #6989
+
+  type Dist = distinct int
+
+  proc mypred[T: Ordinal](x: T): T = T(int(x)-1)
+  proc cons(x: int): Dist = Dist(x)
+
+  var d: Dist
+
+  template `^+`(s, i: untyped): untyped =
+    (when i is BackwardsIndex: s.len - int(i) else: int(i))
+
+  proc `...`[T, U](a: T, b: U): HSlice[T, U] =
+    result.a = a
+    result.b = b
+
+  proc `...`[T](b: T): HSlice[int, T] =
+    result.b = b
+
+  template `...<`(a, b: untyped): untyped =
+    ## a shortcut for 'a..pred(b)'.
+    a ... pred(b)
+
+  template check(a, b) =
+    if $a != b:
+      echo "Failure ", a, " != ", b
+
+  check typeof(4 ...< 1), "HSlice[system.int, system.int]"
+  check typeof(4 ...< ^1), "HSlice[system.int, system.BackwardsIndex]"
+  check typeof(4 ... pred(^1)), "HSlice[system.int, system.BackwardsIndex]"
+  check typeof(4 ... mypred(8)), "HSlice[system.int, system.int]"
+  check typeof(4 ... mypred(^1)), "HSlice[system.int, system.BackwardsIndex]"
+
+  var rot = 8
+
+  proc bug(s: string): string =
+    result = s
+    result = result[result.len - rot .. ^1] & "__" & result[0 ..< ^rot]
+
+  const testStr = "abcdefgasfsgdfgsgdfggsdfasdfsafewfkljdsfajsdflfdjkl"
+
+  echo bug(testStr)
+  echo testStr[testStr.len - 8 .. testStr.len - 1] & "__" & testStr[0 .. testStr.len - pred(rot)]
+
+  var
+    instructions = readFile(parentDir(currentSourcePath) / "troofregression2.txt").split(',')
+    programs = "abcdefghijklmnop"
+
+  proc dance(dancers: string): string =
+    result = dancers
+    for instr in instructions:
+      let rem = instr[1 .. instr.high]
+      case instr[0]
+      of 's':
+        let rot = rem.parseInt
+        result = result[result.len - rot .. ^1] & result[0 ..< ^rot]
+      of 'x':
+        let
+          x = rem.split('/')
+          a = x[0].parseInt
+          b = x[1].parseInt
+        swap(result[a], result[b])
+      of 'p':
+        let
+          a = result.find(rem[0])
+          b = result.find(rem[^1])
+        result[a] = rem[^1]
+        result[b] = rem[0]
+      else: discard
+
+  proc longDance(dancers: string, iterations = 1_000_000_000): string =
+    var
+      dancers = dancers
+      seen = @[dancers]
+    for i in 1 .. iterations:
+      dancers = dancers.dance()
+      if dancers in seen:
+        return seen[iterations mod i]
+      seen.add(dancers)
+
+  echo dance(programs)
+  echo longDance(programs)
+
+
+
+block tunchecked:
+  {.boundchecks: on.}
+  type Unchecked = UncheckedArray[char]
+
+  var x = cast[ptr Unchecked](alloc(100))
+  x[5] = 'x'
+
+
+
+import macros
+block t7818:
+  # bug #7818
+  # this is not a macro bug, but array construction bug
+  # I use macro to avoid object slicing
+  # see #7712 and #7637
+
+  type
+    Vehicle[T] = object of RootObj
+      tire: T
+    Car[T] = object of Vehicle[T]
+    Bike[T] = object of Vehicle[T]
+
+  macro peek(n: typed): untyped =
+    let val = getTypeImpl(n).treeRepr
+    newLit(val)
+
+  block test_t7818:
+    var v = Vehicle[int](tire: 3)
+    var c = Car[int](tire: 4)
+    var b = Bike[int](tire: 2)
+
+    let y = peek([c, b, v])
+    let z = peek([v, c, b])
+    doAssert(y == z)
+
+  block test_t7906_1:
+    proc init(x: typedesc, y: int): ref x =
+      result = new(ref x)
+      result.tire = y
+
+    var v = init(Vehicle[int], 3)
+    var c = init(Car[int], 4)
+    var b = init(Bike[int], 2)
+
+    let y = peek([c, b, v])
+    let z = peek([v, c, b])
+    doAssert(y == z)
+
+  block test_t7906_2:
+    var v = Vehicle[int](tire: 3)
+    var c = Car[int](tire: 4)
+    var b = Bike[int](tire: 2)
+
+    let y = peek([c.addr, b.addr, v.addr])
+    let z = peek([v.addr, c.addr, b.addr])
+    doAssert(y == z)
+
+  block test_t7906_3:
+    type
+      Animal[T] = object of RootObj
+        hair: T
+      Mammal[T] = object of Animal[T]
+      Monkey[T] = object of Mammal[T]
+
+    var v = Animal[int](hair: 3)
+    var c = Mammal[int](hair: 4)
+    var b = Monkey[int](hair: 2)
+
+    let z = peek([c.addr, b.addr, v.addr])
+    let y = peek([v.addr, c.addr, b.addr])
+    doAssert(y == z)
+
+  type
+    Fruit[T] = ref object of RootObj
+      color: T
+    Apple[T] = ref object of Fruit[T]
+    Banana[T] = ref object of Fruit[T]
+
+  proc testArray[T](x: array[3, Fruit[T]]): string =
+    result = ""
+    for c in x:
+      result.add $c.color
+
+  proc testOpenArray[T](x: openArray[Fruit[T]]): string =
+    result = ""
+    for c in x:
+      result.add $c.color
+
+  block test_t7906_4:
+    var v = Fruit[int](color: 3)
+    var c = Apple[int](color: 4)
+    var b = Banana[int](color: 2)
+
+    let y = peek([c, b, v])
+    let z = peek([v, c, b])
+    doAssert(y == z)
+
+  block test_t7906_5:
+    var a = Fruit[int](color: 1)
+    var b = Apple[int](color: 2)
+    var c = Banana[int](color: 3)
+
+    doAssert(testArray([a, b, c]) == "123")
+    doAssert(testArray([b, c, a]) == "231")
+
+    doAssert(testOpenArray([a, b, c]) == "123")
+    doAssert(testOpenArray([b, c, a]) == "231")
+
+    doAssert(testOpenArray(@[a, b, c]) == "123")
+    doAssert(testOpenArray(@[b, c, a]) == "231")
+
+  proc testArray[T](x: array[3, ptr Vehicle[T]]): string =
+    result = ""
+    for c in x:
+      result.add $c.tire
+
+  proc testOpenArray[T](x: openArray[ptr Vehicle[T]]): string =
+    result = ""
+    for c in x:
+      result.add $c.tire
+
+  block test_t7906_6:
+    var u = Vehicle[int](tire: 1)
+    var v = Bike[int](tire: 2)
+    var w = Car[int](tire: 3)
+
+    doAssert(testArray([u.addr, v.addr, w.addr]) == "123")
+    doAssert(testArray([w.addr, u.addr, v.addr]) == "312")
+
+    doAssert(testOpenArray([u.addr, v.addr, w.addr]) == "123")
+    doAssert(testOpenArray([w.addr, u.addr, v.addr]) == "312")
+
+    doAssert(testOpenArray(@[u.addr, v.addr, w.addr]) == "123")
+    doAssert(testOpenArray(@[w.addr, u.addr, v.addr]) == "312")
+
+block trelaxedindextyp:
+  # any integral type is allowed as index
+  proc foo(x: ptr UncheckedArray[int]; idx: uint64) = echo x[idx]
+  proc foo(x: seq[int]; idx: uint64) = echo x[idx]
+  proc foo(x: string|cstring; idx: uint64) = echo x[idx]
+  proc foo(x: openArray[int]; idx: uint64) = echo x[idx]
+
+block t3899:
+  # https://github.com/nim-lang/Nim/issues/3899
+  type O = object
+    a: array[1..2,float]
+  template `[]`(x: O, i: int): float =
+    x.a[i]
+  const c = O(a: [1.0,2.0])
+  echo c[2]
+
+block arrayLiterals:
+  type ABC = enum A, B, C
+  template Idx[IdxT, ElemT](arr: array[IdxT, ElemT]): untyped = IdxT
+  doAssert [A: 0, B: 1].Idx is range[A..B]
+  doAssert [A: 0, 1, 3].Idx is ABC
+  doAssert [1: 2][1] == 2
+  doAssert [-1'i8: 2][-1] == 2
+  doAssert [-1'i8: 2, 3, 4, 5].Idx is range[-1'i8..2'i8]
+
+
+
+# bug #8316
+
+proc myAppend[T](a:T):string=
+  echo "a:", a
+  return $a
+
+template append2*(args: varargs[string, myAppend]): string =
+  var ret:string
+  for a in args:
+    echo "ret:", ret
+    ret.add(a)
+  ret
+
+let foo = append2("1", "2", "3")
+echo foo
+
+block t12466:
+  # https://github.com/nim-lang/Nim/issues/12466
+  var a: array[288, uint16]
+  for i in 0'u16 ..< 144'u16:
+    a[0'u16 + i] = i
+  for i in 0'u16 ..< 8'u16:
+    a[0'u16 + i] = i
+
+block t17705:
+  # https://github.com/nim-lang/Nim/pull/17705
+  var a = array[0, int].low
+  a = int(a)
+  var b = array[0, int].high
+  b = int(b)
+
+block t18643:
+  # https://github.com/nim-lang/Nim/issues/18643
+  let a: array[0, int] = []
+  var caught = false
+  let b = 9999999
+  try:
+    echo a[b]
+  except IndexDefect:
+    caught = true
+  doAssert caught, "IndexDefect not caught!"
diff --git a/tests/array/tarray2.nim b/tests/array/tarray2.nim
deleted file mode 100644
index 1951e6e97..000000000
--- a/tests/array/tarray2.nim
+++ /dev/null
@@ -1,36 +0,0 @@
-discard """
-  file: "tarray2.nim"
-  output: "[4, 5, 6]\n\n[16, 25, 36]\n\n[16, 25, 36]"
-"""
-# simple check for one dimensional arrays
-
-type
-  TMyArray = array[0..2, int]
-
-  TObj = object
-    arr: TMyarray
-
-proc mul(a, b: TMyarray): TMyArray =
-  result = a
-  for i in 0..len(a)-1:
-    result[i] = a[i] * b[i]
-
-var
-  x, y: TMyArray
-  o: TObj
-
-proc varArr1(x: var TMyArray): var TMyArray = x
-proc varArr2(x: var TObj): var TMyArray = x.arr
-
-x = [ 4, 5, 6 ]
-echo repr(varArr1(x))
-
-y = x
-echo repr(mul(x, y))
-
-o.arr = mul(x, y)
-echo repr(varArr2(o))
-
-#OUT [16, 25, 36]
-
-
diff --git a/tests/array/tarray3.nim b/tests/array/tarray3.nim
deleted file mode 100644
index 24bf26fda..000000000
--- a/tests/array/tarray3.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-discard """
-  file: "tarray3.nim"
-  output: "3"
-"""
-# simple check for two dimensional arrays
-
-const
-  myData = [[1,2,3], [4, 5, 6]]
-
-echo myData[0][2] #OUT 3
-
-
-
diff --git a/tests/array/tarraycons.nim b/tests/array/tarraycons.nim
index 9f09fd405..b6ebe55c8 100644
--- a/tests/array/tarraycons.nim
+++ b/tests/array/tarraycons.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "invalid order in array constructor"
   file: "tarraycons.nim"
   line: 14
-  errormsg: "invalid order in array constructor"
 """
 
 type
@@ -19,6 +19,3 @@ const
   ]
 
 echo myMapping[eC][1]
-
-
-
diff --git a/tests/array/tarraycons2.nim b/tests/array/tarraycons2.nim
deleted file mode 100644
index 72d9e374e..000000000
--- a/tests/array/tarraycons2.nim
+++ /dev/null
@@ -1,23 +0,0 @@
-discard """
-  file: "tarraycons.nim"
-  output: "6"
-"""
-
-type
-  TEnum = enum
-    eA, eB, eC, eD, eE, eF
-
-const
-  myMapping: array[TEnum, array[0..1, int]] = [
-    eA: [1, 2],
-    eB: [3, 4],
-    [5, 6],
-    eD: [0: 8, 1: 9],
-    eE: [0: 8, 9],
-    eF: [2, 1: 9]
-  ]
-
-echo myMapping[eC][1]
-
-
-
diff --git a/tests/array/tarraycons_ptr_generic.nim b/tests/array/tarraycons_ptr_generic.nim
deleted file mode 100644
index eb89a196f..000000000
--- a/tests/array/tarraycons_ptr_generic.nim
+++ /dev/null
@@ -1,51 +0,0 @@
-discard """
-  output: '''apple
-banana
-Fruit
-2
-4
-3
-none
-skin
-paper
-'''
-"""
-type
-  Fruit = object of RootObj
-    name: string
-  Apple = object of Fruit
-  Banana = object of Fruit
-
-var
-  ir = Fruit(name: "Fruit")
-  ia = Apple(name: "apple")
-  ib = Banana(name: "banana")
-
-let x = [ia.addr, ib.addr, ir.addr]
-for c in x: echo c.name
-
-type
-  Vehicle[T] = object of RootObj
-    tire: T
-  Car[T] = object of Vehicle[T]
-  Bike[T] = object of Vehicle[T]
-
-var v = Vehicle[int](tire: 3)
-var c = Car[int](tire: 4)
-var b = Bike[int](tire: 2)
-
-let y = [b.addr, c.addr, v.addr]
-for c in y: echo c.tire
-
-type
-  Book[T] = ref object of RootObj
-    cover: T
-  Hard[T] = ref object of Book[T]
-  Soft[T] = ref object of Book[T]
-
-var bn = Book[string](cover: "none")
-var hs = Hard[string](cover: "skin")
-var bp = Soft[string](cover: "paper")
-
-let z = [bn, hs, bp]
-for c in z: echo c.cover
diff --git a/tests/array/tarraycons_ptr_generic2.nim b/tests/array/tarraycons_ptr_generic2.nim
index fce7af669..f6ed32b58 100644
--- a/tests/array/tarraycons_ptr_generic2.nim
+++ b/tests/array/tarraycons_ptr_generic2.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "type mismatch: got <ptr Hard[system.string]> but expected 'Book[system.string]'"
   file: "tarraycons_ptr_generic2.nim"
   line: 17
-  errormsg: "type mismatch: got <ptr Hard[system.string]> but expected 'Book[system.string]'"
 """
 
 type
diff --git a/tests/array/tarraylen.nim b/tests/array/tarraylen.nim
deleted file mode 100644
index e9612de58..000000000
--- a/tests/array/tarraylen.nim
+++ /dev/null
@@ -1,18 +0,0 @@
-discard """
-  output: ""
-"""
-var a: array[0, int]
-doAssert a.len == 0
-doAssert array[0..0, int].len == 1
-doAssert array[0..0, int]([1]).len == 1
-doAssert array[1..1, int].len == 1
-doAssert array[1..1, int]([1]).len == 1
-doAssert array[2, int].len == 2
-doAssert array[2, int]([1, 2]).len == 2
-doAssert array[1..3, int].len == 3
-doAssert array[1..3, int]([1, 2, 3]).len == 3
-doAssert array[0..2, int].len == 3
-doAssert array[0..2, int]([1, 2, 3]).len == 3
-doAssert array[-2 .. -2, int].len == 1
-doAssert([1, 2, 3].len == 3)
-doAssert([42].len == 1)
\ No newline at end of file
diff --git a/tests/array/tarrindx.nim b/tests/array/tarrindx.nim
deleted file mode 100644
index 3bb6b0148..000000000
--- a/tests/array/tarrindx.nim
+++ /dev/null
@@ -1,31 +0,0 @@
-discard """
-  output: '''0
-0'''
-"""
-
-# test another strange bug ... (I hate this compiler; it is much too buggy!)
-
-proc putEnv(key, val: string) =
-  # XXX: we have to leak memory here, as we cannot
-  # free it before the program ends (says Borland's
-  # documentation)
-  var
-    env: ptr array[0..500000, char]
-  env = cast[ptr array[0..500000, char]](alloc(len(key) + len(val) + 2))
-  for i in 0..len(key)-1: env[i] = key[i]
-  env[len(key)] = '='
-  for i in 0..len(val)-1:
-    env[len(key)+1+i] = val[i]
-
-# bug #7153
-const
-  UnsignedConst = 1024'u
-type
-  SomeObject* = object
-    s1: array[UnsignedConst, uint32]
-
-var
-  obj: SomeObject
-
-echo obj.s1[0]
-echo obj.s1[0u]
diff --git a/tests/array/tidx_lit_err1.nim b/tests/array/tidx_lit_err1.nim
new file mode 100644
index 000000000..b1823e5a3
--- /dev/null
+++ b/tests/array/tidx_lit_err1.nim
@@ -0,0 +1,6 @@
+discard """
+  errormsg: "size of array exceeds range of index type 'range 1..2(Color)' by 3 elements"
+  line: 6
+"""
+type Color = enum Red, Green, Blue
+let y = [Green: 0, 1, 2, 3, 4]
diff --git a/tests/array/tidx_lit_err2.nim b/tests/array/tidx_lit_err2.nim
new file mode 100644
index 000000000..75f5f227b
--- /dev/null
+++ b/tests/array/tidx_lit_err2.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "expected ordinal value for array index, got '\"string\"'"
+  line: 5
+"""
+let x = ["string": 0, "index": 1]
diff --git a/tests/array/tidx_lit_err3.nim b/tests/array/tidx_lit_err3.nim
new file mode 100644
index 000000000..95922bc50
--- /dev/null
+++ b/tests/array/tidx_lit_err3.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "size of array exceeds range of index type 'range 2147483646..2147483647(int32)' by 1 elements"
+  line: 5
+"""
+echo [high(int32)-1: 1, 2, 3]
diff --git a/tests/array/tinvalidarrayaccess.nim b/tests/array/tinvalidarrayaccess.nim
new file mode 100644
index 000000000..f8bce45ef
--- /dev/null
+++ b/tests/array/tinvalidarrayaccess.nim
@@ -0,0 +1,21 @@
+discard """
+  errormsg: "index 2 not in 0 .. 1"
+  line: 18
+"""
+block:
+  try:
+    let a = @[1,2]
+    echo a[3]
+  except Exception as e:
+    doAssert e.msg == "index 3 not in 0 .. 1"
+      # note: this is not being tested, because the CT error happens before
+
+block:
+  type TTestArr = array[0..1, int16]
+  var f: TTestArr
+  f[0] = 30
+  f[1] = 40
+  f[2] = 50
+  f[3] = 60
+
+  echo(repr(f))
diff --git a/tests/array/tinvalidarrayaccess2.nim b/tests/array/tinvalidarrayaccess2.nim
new file mode 100644
index 000000000..0a0703834
--- /dev/null
+++ b/tests/array/tinvalidarrayaccess2.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "index 3 not in 0 .. 1"
+  line: 9
+"""
+
+# Note: merge in tinvalidarrayaccess.nim pending https://github.com/nim-lang/Nim/issues/9906
+
+let a = [1,2]
+echo a[3]
+
diff --git a/tests/array/tlargeindex.nim b/tests/array/tlargeindex.nim
new file mode 100644
index 000000000..61bcbd61d
--- /dev/null
+++ b/tests/array/tlargeindex.nim
@@ -0,0 +1,18 @@
+discard """
+  cmd: "nim check --hints:off $file"
+"""
+
+# issue #17163
+var e: array[int32, byte] #[tt.Error
+             ^ index type 'int32' for array is too large]#
+var f: array[uint32, byte] #[tt.Error
+             ^ index type 'uint32' for array is too large]#
+var g: array[int64, byte] #[tt.Error
+             ^ index type 'int64' for array is too large]#
+var h: array[uint64, byte] #[tt.Error
+             ^ index type 'uint64' for array is too large]#
+
+# crash in issue #23204
+proc y[N](): array[N, int] = default(array[N, int]) #[tt.Error
+                                           ^ index type 'int' for array is too large]#
+discard y[int]()
diff --git a/tests/array/troof1.nim b/tests/array/troof1.nim
deleted file mode 100644
index b486c3448..000000000
--- a/tests/array/troof1.nim
+++ /dev/null
@@ -1,43 +0,0 @@
-discard """
-  output: '''@[2, 3, 4]321
-9.0 4.0
-3
-@[(Field0: 1, Field1: 2), (Field0: 3, Field1: 5)]
-2
-@["a", "new one", "c"]
-@[1, 2, 3]'''
-"""
-
-proc foo[T](x, y: T): T = x
-
-var a = @[1, 2, 3, 4]
-var b: array[3, array[2, float]] = [[1.0,2], [3.0,4], [8.0,9]]
-echo a[1.. ^1], a[^2], a[^3], a[^4]
-echo b[^1][^1], " ", (b[^2]).foo(b[^1])[^1]
-
-b[^1] = [8.8, 8.9]
-
-var c: seq[(int, int)] = @[(1,2), (3,4)]
-
-proc takeA(x: ptr int) = echo x[]
-
-takeA(addr c[^1][0])
-c[^1][1] = 5
-echo c
-
-proc useOpenarray(x: openArray[int]) =
-  echo x[^2]
-
-proc mutOpenarray(x: var openArray[string]) =
-  x[^2] = "new one"
-
-useOpenarray([1, 2, 3])
-
-var z = @["a", "b", "c"]
-mutOpenarray(z)
-echo z
-
-# bug #6675
-var y: array[1..5, int] = [1,2,3,4,5]
-y[3..5] = [1, 2, 3]
-echo y[3..5]
diff --git a/tests/array/troof3.nim b/tests/array/troof3.nim
deleted file mode 100644
index efe0eafb8..000000000
--- a/tests/array/troof3.nim
+++ /dev/null
@@ -1,7 +0,0 @@
-discard """
-  output: '''c'''
-"""
-
-var a: array['a'..'c', string] = ["a", "b", "c"]
-
-echo a[^1]
diff --git a/tests/array/troofregression.nim b/tests/array/troofregression.nim
deleted file mode 100644
index 0b96123a4..000000000
--- a/tests/array/troofregression.nim
+++ /dev/null
@@ -1,46 +0,0 @@
-###############################
-#### part from Arraymancer
-
-type
-  MetadataArray* = object
-    data*: array[8, int]
-    len*: int
-
-# Commenting the converter removes the error "lib/system.nim(3536, 3) Error: for a 'var' type a variable needs to be passed"
-converter toMetadataArray*(se: varargs[int]): MetadataArray {.inline.} =
-  result.len = se.len
-  for i in 0..<se.len:
-    result.data[i] = se[i]
-
-
-when NimVersion >= "0.17.3":
-  type Index = int or BackwardsIndex
-  template `^^`(s, i: untyped): untyped =
-    when i is BackwardsIndex:
-      s.len - int(i)
-    else: i
-else:
-  type Index = int
-  template `^^`(s, i: untyped): untyped =
-    i
-
-## With Nim devel from the start of the week (~Oct30) I managed to trigger "lib/system.nim(3536, 4) Error: expression has no address"
-## but I can't anymore after updating Nim (Nov5)
-## Now commenting this plain compiles and removes the error "lib/system.nim(3536, 3) Error: for a 'var' type a variable needs to be passed"
-proc `[]`*(a: var MetadataArray, idx: Index): var int {.inline.} =
-  a.data[a ^^ idx]
-
-
-##############################
-### Completely unrelated lib that triggers the issue
-
-type
-  MySeq[T] = ref object
-    data: seq[T]
-
-proc test[T](sx: MySeq[T]) =
-  # Removing the backward index removes the error "lib/system.nim(3536, 3) Error: for a 'var' type a variable needs to be passed"
-  echo sx.data[^1] # error here
-
-let s = MySeq[int](data: @[1, 2, 3])
-s.test()
diff --git a/tests/array/troofregression2.nim b/tests/array/troofregression2.nim
deleted file mode 100644
index a594d8a47..000000000
--- a/tests/array/troofregression2.nim
+++ /dev/null
@@ -1,102 +0,0 @@
-discard """
-  output: '''OK
-OK
-OK
-OK
-OK
-dflfdjkl__abcdefgasfsgdfgsgdfggsdfasdfsafewfkljdsfajs
-dflfdjkl__abcdefgasfsgdfgsgdfggsdfasdfsafewfkljdsfajsdf
-kgdchlfniambejop
-fjpmholcibdgeakn
-'''
-"""
-
-import strutils, sequtils, typetraits, os
-# bug #6989
-
-type Dist = distinct int
-
-proc mypred[T: Ordinal](x: T): T = T(int(x)-1)
-proc cons(x: int): Dist = Dist(x)
-
-var d: Dist
-
-template `^+`(s, i: untyped): untyped =
-  (when i is BackwardsIndex: s.len - int(i) else: int(i))
-
-proc `...`*[T, U](a: T, b: U): HSlice[T, U] =
-  result.a = a
-  result.b = b
-
-proc `...`*[T](b: T): HSlice[int, T] =
-  result.b = b
-
-template `...<`*(a, b: untyped): untyped =
-  ## a shortcut for 'a..pred(b)'.
-  a ... pred(b)
-
-template check(a, b) =
-  if $a == b: echo "OK"
-  else: echo "Failure ", a, " != ", b
-
-check type(4 ...< 1), "HSlice[system.int, system.int]"
-
-check type(4 ...< ^1), "HSlice[system.int, system.BackwardsIndex]"
-check type(4 ... pred(^1)), "HSlice[system.int, system.BackwardsIndex]"
-
-check type(4 ... mypred(8)), "HSlice[system.int, system.int]"
-
-check type(4 ... mypred(^1)), "HSlice[system.int, system.BackwardsIndex]"
-
-var rot = 8
-
-proc bug(s: string): string =
-  result = s
-  result = result[result.len - rot .. ^1] & "__" & result[0 ..< ^rot]
-
-const testStr = "abcdefgasfsgdfgsgdfggsdfasdfsafewfkljdsfajsdflfdjkl"
-
-echo bug(testStr)
-echo testStr[testStr.len - 8 .. testStr.len - 1] & "__" & testStr[0 .. testStr.len - pred(rot)]
-
-
-
-var
-  instructions = readFile(getAppDir() / "troofregression2.txt").split(',')
-  programs = "abcdefghijklmnop"
-
-proc dance(dancers: string): string =
-  result = dancers
-  for instr in instructions:
-    let rem = instr[1 .. instr.high]
-    case instr[0]
-    of 's':
-      let rot = rem.parseInt
-      result = result[result.len - rot .. ^1] & result[0 ..< ^rot]
-    of 'x':
-      let
-        x = rem.split('/')
-        a = x[0].parseInt
-        b = x[1].parseInt
-      swap(result[a], result[b])
-    of 'p':
-      let
-        a = result.find(rem[0])
-        b = result.find(rem[^1])
-      result[a] = rem[^1]
-      result[b] = rem[0]
-    else: discard
-
-proc longDance(dancers: string, iterations = 1_000_000_000): string =
-  var
-    dancers = dancers
-    seen = @[dancers]
-  for i in 1 .. iterations:
-    dancers = dancers.dance()
-    if dancers in seen:
-      return seen[iterations mod i]
-    seen.add(dancers)
-
-
-echo dance(programs)
-echo longDance(programs)
diff --git a/tests/array/tunchecked.nim b/tests/array/tunchecked.nim
deleted file mode 100644
index f5ac3642d..000000000
--- a/tests/array/tunchecked.nim
+++ /dev/null
@@ -1,5 +0,0 @@
-{.boundchecks: on.}
-type Unchecked {.unchecked.} = array[0, char]
-
-var x = cast[ptr Unchecked](alloc(100))
-x[5] = 'x'
diff --git a/tests/assert/config.nims b/tests/assert/config.nims
new file mode 100644
index 000000000..f1cffeb6c
--- /dev/null
+++ b/tests/assert/config.nims
@@ -0,0 +1 @@
+--excessiveStackTrace:on
diff --git a/tests/assert/panicoverride.nim b/tests/assert/panicoverride.nim
new file mode 100644
index 000000000..53ad64215
--- /dev/null
+++ b/tests/assert/panicoverride.nim
@@ -0,0 +1,15 @@
+# panicoverride.nim
+
+proc printf(fmt: cstring) {.varargs, importc, header:"stdio.h".}
+proc exit(code: cint) {.importc, header:"stdlib.h".}
+
+{.push stack_trace: off, profiler:off.}
+
+proc rawoutput(s: cstring) =
+  printf("RAW: %s\n", s)
+  
+proc panic(s: cstring) {.noreturn.} =
+  printf("PANIC: %s\n", s)
+  exit(0)
+
+{.pop.}
\ No newline at end of file
diff --git a/tests/assert/t21195.nim b/tests/assert/t21195.nim
new file mode 100644
index 000000000..b690d22a0
--- /dev/null
+++ b/tests/assert/t21195.nim
@@ -0,0 +1,6 @@
+discard """
+  matrix: "--verbosity:0 --os:standalone --mm:none"
+"""
+# bug #21195
+var n = 11
+assert(n == 12)
diff --git a/tests/assert/tassert.nim b/tests/assert/tassert.nim
index b3df30bd1..a14fec317 100644
--- a/tests/assert/tassert.nim
+++ b/tests/assert/tassert.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tassert.nim"
   outputsub: "assertion failure!this shall be always written"
   exitcode: "1"
 """
@@ -11,7 +10,7 @@ proc callC() = callA()
 
 try:
   callC()
-except EAssertionFailed:
+except AssertionDefect:
   write(stdout, "assertion failure!")
 except:
   write(stdout, "unknown exception!")
@@ -19,5 +18,3 @@ finally:
   system.write(stdout, "this shall be always written")
 
 assert(false) #OUT assertion failure!this shall be always written
-
-
diff --git a/tests/assert/tassert2.nim b/tests/assert/tassert2.nim
new file mode 100644
index 000000000..e32ab72e1
--- /dev/null
+++ b/tests/assert/tassert2.nim
@@ -0,0 +1,111 @@
+discard """
+  output: '''
+`false` first assertion from bar
+`false` second assertion from bar
+-1
+'''
+"""
+from strutils import endsWith
+
+type
+  TLineInfo = tuple[filename: string, line: int, column: int]
+  TMyError = object of Exception
+    lineinfo: TLineInfo
+  EMyError = ref TMyError
+
+
+# NOTE: when entering newlines, adjust `expectedEnd` outputs
+
+try:
+  doAssert(false, "msg1") # doAssert test
+except AssertionDefect as e:
+  assert e.msg.endsWith "tassert2.nim(20, 11) `false` msg1"
+
+try:
+  assert false # assert test with no msg
+except AssertionDefect as e:
+  assert e.msg.endsWith "tassert2.nim(25, 3) `false` "
+
+try:
+  let a = 1
+  doAssert(a+a==1) # assert test with Ast expression
+  # BUG: const folding would make "1+1==1" appear as `false` in
+  # assert message
+except AssertionDefect as e:
+  assert e.msg.endsWith "`a + a == 1` "
+
+try:
+  let a = 1
+  doAssert a+a==1 # ditto with `doAssert` and no parens
+except AssertionDefect as e:
+  assert e.msg.endsWith "`a + a == 1` "
+
+proc fooStatic() =
+  # protect against https://github.com/nim-lang/Nim/issues/8758
+  static: doAssert(true)
+fooStatic()
+
+
+
+
+
+block:
+  # scope-wide policy to change the failed assert
+  # exception type in order to include a lineinfo
+  onFailedAssert(msg):
+    var e = new(TMyError)
+    e.msg = msg
+    e.lineinfo = instantiationInfo(-2)
+    raise e
+
+  proc foo =
+    assert(false, "assertion from foo")
+
+
+  proc bar: int =
+    # local overrides that are active only in this proc
+    onFailedAssert(msg):
+      echo msg[^32..^1]
+
+    assert(false, "first assertion from bar")
+
+    onFailedAssert(msg):
+      echo msg[^33..^1]
+      return -1
+
+    assert(false, "second assertion from bar")
+    return 10
+
+  echo(bar())
+
+  try:
+    foo()
+  except:
+    let e = EMyError(getCurrentException())
+    assert e.msg.endsWith "tassert2.nim(62, 11) `false` assertion from foo"
+
+block: ## checks for issue https://github.com/nim-lang/Nim/issues/8518
+  template fun(a: string): string =
+      const pattern = a & a
+      pattern
+
+  try:
+    doAssert fun("foo1") == fun("foo2"), "mymsg"
+  except AssertionDefect as e:
+    # used to expand out the template instantiaiton, sometimes filling hundreds of lines
+    assert e.msg.endsWith ""
+
+block: ## checks for issue https://github.com/nim-lang/Nim/issues/9301
+  try:
+    doAssert 1 + 1 == 3
+  except AssertionDefect as e:
+    # used to const fold as false
+    assert e.msg.endsWith "tassert2.nim(100, 5) `1 + 1 == 3` "
+
+block: ## checks AST isn't transformed as it used to
+  let a = 1
+  try:
+    doAssert a > 1
+  except AssertionDefect as e:
+    # used to rewrite as `1 < a`
+    assert e.msg.endsWith "tassert2.nim(108, 5) `a > 1` "
diff --git a/tests/assert/tassert_c.nim b/tests/assert/tassert_c.nim
new file mode 100644
index 000000000..e3e3b8147
--- /dev/null
+++ b/tests/assert/tassert_c.nim
@@ -0,0 +1,40 @@
+discard """
+  matrix: "-d:nimPreviewSlimSystem --stackTrace:on --excessiveStackTrace:off"
+  output: '''true'''
+"""
+import std/assertions
+const expected = """
+tassert_c.nim(35)        tassert_c
+tassert_c.nim(34)        foo
+assertions.nim(*)       failedAssertImpl
+assertions.nim(*)       raiseAssert
+"""
+
+proc tmatch(x, p: string): bool =
+  var i = 0
+  var k = 0
+  while i < p.len:
+    if p[i] == '*':
+      let oldk = k
+      while k < x.len and x[k] in {'0'..'9'}: inc k
+      # no digit skipped?
+      if oldk == k: return false
+      inc i
+    elif k < x.len and p[i] == x[k]:
+      inc i
+      inc k
+    else:
+      return false
+  while k < x.len and x[k] in {' ', '\L', '\C'}: inc k
+  result = i >= p.len and k >= x.len
+
+
+try:
+  proc foo() =
+    assert(false)
+  foo()
+except AssertionDefect:
+  let e = getCurrentException()
+  let trace = e.getStackTrace
+  if tmatch(trace, expected): echo true
+  else: echo "wrong trace:\n" & trace
diff --git a/tests/assert/tfailedassert.nim b/tests/assert/tfailedassert.nim
deleted file mode 100644
index 8b260a3ab..000000000
--- a/tests/assert/tfailedassert.nim
+++ /dev/null
@@ -1,51 +0,0 @@
-discard """
-  output: '''
-WARNING: false first assertion from bar
-ERROR: false second assertion from bar
--1
-tfailedassert.nim:27 false assertion from foo
-'''
-"""
-
-type
-  TLineInfo = tuple[filename: string, line: int, column: int]
-
-  TMyError = object of Exception
-    lineinfo: TLineInfo
-
-  EMyError = ref TMyError
-
-# module-wide policy to change the failed assert
-# exception type in order to include a lineinfo
-onFailedAssert(msg):
-  var e = new(TMyError)
-  e.msg = msg
-  e.lineinfo = instantiationInfo(-2)
-  raise e
-
-proc foo =
-  assert(false, "assertion from foo")
-
-proc bar: int =
-  # local overrides that are active only
-  # in this proc
-  onFailedAssert(msg): echo "WARNING: " & msg
-
-  assert(false, "first assertion from bar")
-
-  onFailedAssert(msg):
-    echo "ERROR: " & msg
-    return -1
-
-  assert(false, "second assertion from bar")
-  return 10
-
-echo("")
-echo(bar())
-
-try:
-  foo()
-except:
-  let e = EMyError(getCurrentException())
-  echo e.lineinfo.filename, ":", e.lineinfo.line, " ", e.msg
-
diff --git a/tests/assign/moverload_asgn2.nim b/tests/assign/moverload_asgn2.nim
index 6620adbeb..cfea48cd1 100644
--- a/tests/assign/moverload_asgn2.nim
+++ b/tests/assign/moverload_asgn2.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc"
+"""
+
 type
   Concrete* = object
     a*, b*: string
diff --git a/tests/assign/tassign.nim b/tests/assign/tassign.nim
index 4c173d04f..fdec04d22 100644
--- a/tests/assign/tassign.nim
+++ b/tests/assign/tassign.nim
@@ -1,31 +1,218 @@
+discard """
+  output:
+'''
+TEMP=C:\Programs\xyz\bin
+8 5 0 0
+pre test a:test b:1 c:2 haha:3
+assignment test a:test b:1 c:2 haha:3
+abc123
+'''
+"""
+
+#[
+Concrete '='
+Concrete '='
+Concrete '='
+Concrete '='
+Concrete '='
+GenericT[T] '=' int
+GenericT[T] '=' float
+GenericT[T] '=' float
+GenericT[T] '=' float
+GenericT[T] '=' string
+GenericT[T] '=' int8
+GenericT[T] '=' bool
+GenericT[T] '=' bool
+GenericT[T] '=' bool
+GenericT[T] '=' bool
+]#
+
+block tassign:
 # Test the assignment operator for complex types which need RTTI
+  type
+    TRec = object
+      x, y: int
+      s: string
+      seq: seq[string]
+      arr: seq[seq[array[0..3, string]]]
+    TRecSeq = seq[TRec]
+
+  proc test() =
+    var
+      a, b: TRec
+    a.x = 1
+    a.y = 2
+    a.s = "Hallo!"
+    a.seq = @["abc", "def", "ghi", "jkl"]
+    a.arr = @[]
+    setLen(a.arr, 4)
+    a.arr[0] = @[]
+    a.arr[1] = @[]
+
+    b = a # perform a deep copy here!
+    b.seq = @["xyz", "huch", "was", "soll"]
+    doAssert len(a.seq) == 4
+    doAssert a.seq[3] == "jkl"
+    doAssert len(b.seq) == 4
+    doAssert b.seq[3] == "soll"
+    doAssert b.y == 2
+
+  test()
+
+
+
+import strutils
+block tcopy:
+  proc main() =
+    const
+      example = r"TEMP=C:\Programs\xyz\bin"
+    var
+      a, b: string
+      p: int
+    p = find(example, "=")
+    a = substr(example, 0, p-1)
+    b = substr(example, p+1)
+    writeLine(stdout, a & '=' & b)
+
+  main()
+
+
+
+block tgenericassign:
+  type
+    TAny {.pure.} = object
+      value: pointer
+      rawType: pointer
+
+  proc newAny(value, rawType: pointer): TAny =
+    result.value = value
+    result.rawType = rawType
+
+  var name: cstring = "example"
+
+  var ret: seq[tuple[name: string, a: TAny]] = @[]
+  for i in 0 .. 8000:
+    var tup = ($name, newAny(nil, nil))
+    doAssert(tup[0] == "example")
+    ret.add(tup)
+    doAssert(ret[ret.len()-1][0] == "example")
+
+
+
+block tgenericassign_tuples:
+  var t, s: tuple[x: string, c: int]
+
+  proc ugh: seq[tuple[x: string, c: int]] =
+    result = @[("abc", 232)]
+
+  t = ugh()[0]
+  s = t
+  s = ugh()[0]
+
+  doAssert s[0] == "abc"
+  doAssert s[1] == 232
+
+
+
+block tobjasgn:
+  type
+    TSomeObj = object of RootObj
+      a, b: int
+    PSomeObj = ref object
+      a, b: int
 
-type
-  TRec = object
-    x, y: int
-    s: string
-    seq: seq[string]
-    arr: seq[seq[array[0..3, string]]]
-  TRecSeq = seq[TRec]
+  var a = TSomeObj(a: 8)
+  var b = PSomeObj(a: 5)
+  echo a.a, " ", b.a, " ", a.b, " ", b.b
+
+  # bug #575
+
+  type
+    Something = object of RootObj
+      a: string
+      b, c: int32
+
+  type
+    Other = object of Something
+      haha: int
+
+  proc `$`(x: Other): string =
+    result = "a:" & x.a & " b:" & $x.b & " c:" & $x.c & " haha:" & $x.haha
 
-proc test() =
   var
-    a, b: TRec
-  a.x = 1
-  a.y = 2
-  a.s = "Hallo!"
-  a.seq = @["abc", "def", "ghi", "jkl"]
-  a.arr = @[]
-  setLen(a.arr, 4)
-  a.arr[0] = @[]
-  a.arr[1] = @[]
-
-  b = a # perform a deep copy here!
-  b.seq = @["xyz", "huch", "was", "soll"]
-  writeLine(stdout, len(a.seq))
-  writeLine(stdout, a.seq[3])
-  writeLine(stdout, len(b.seq))
-  writeLine(stdout, b.seq[3])
-  writeLine(stdout, b.y)
-
-test()
+    t: Other
+
+  t.a = "test"
+  t.b = 1
+  t.c = 2
+  t.haha = 3
+
+  echo "pre test ", $t
+  var x = t
+  echo "assignment test ", x
+
+
+when false:
+  type
+    Concrete = object
+      a, b: string
+
+  proc `=`(d: var Concrete; src: Concrete) =
+    shallowCopy(d.a, src.a)
+    shallowCopy(d.b, src.b)
+    echo "Concrete '='"
+
+  var x, y: array[0..2, Concrete]
+  var cA, cB: Concrete
+
+  var cATup, cBTup: tuple[x: int, ha: Concrete]
+
+  x = y
+  cA = cB
+  cATup = cBTup
+
+  type
+    GenericT[T] = object
+      a, b: T
+
+  proc `=`[T](d: var GenericT[T]; src: GenericT[T]) =
+    shallowCopy(d.a, src.a)
+    shallowCopy(d.b, src.b)
+    echo "GenericT[T] '=' ", typeof(T).name
+
+  var ag: GenericT[int]
+  var bg: GenericT[int]
+
+  ag = bg
+
+  var xg, yg: array[0..2, GenericT[float]]
+  var cAg, cBg: GenericT[string]
+
+  var cATupg, cBTupg: tuple[x: int, ha: GenericT[int8]]
+
+  xg = yg
+  cAg = cBg
+  cATupg = cBTupg
+
+  var caSeqg, cbSeqg: seq[GenericT[bool]]
+  newSeq(cbSeqg, 4)
+  caSeqg = cbSeqg
+
+  when false:
+    type
+      Foo = object
+        case b: bool
+        of false: xx: GenericT[int]
+        of true: yy: bool
+
+    var
+      a, b: Foo
+    a = b
+
+block tgeneric_assign_varargs:
+  template fatal(msgs: varargs[string]) =
+    for msg in msgs:
+      stdout.write(msg)
+    stdout.write('\n')
+
+  fatal "abc", "123"
diff --git a/tests/assign/tcopy.nim b/tests/assign/tcopy.nim
deleted file mode 100644
index 1e5bc3cba..000000000
--- a/tests/assign/tcopy.nim
+++ /dev/null
@@ -1,25 +0,0 @@
-discard """
-  file: "tcopy.nim"
-  output: "TEMP=C:\\Programs\\xyz\\bin"
-"""
-# tests the substr proc
-
-import
-  strutils
-
-proc main() =
-  const
-    example = r"TEMP=C:\Programs\xyz\bin"
-  var
-    a, b: string
-    p: int
-  p = find(example, "=")
-  a = substr(example, 0, p-1)
-  b = substr(example, p+1)
-  writeLine(stdout, a & '=' & b)
-  #writeLine(stdout, b)
-
-main()
-#OUT TEMP=C:\Programs\xyz\bin
-
-
diff --git a/tests/assign/tgenericassign.nim b/tests/assign/tgenericassign.nim
deleted file mode 100644
index bd9c6c21b..000000000
--- a/tests/assign/tgenericassign.nim
+++ /dev/null
@@ -1,24 +0,0 @@
-discard """
-  output: '''came here'''
-"""
-
-type
-  TAny* = object {.pure.}
-    value*: pointer
-    rawType: pointer
-
-proc newAny(value, rawType: pointer): TAny =
-  result.value = value
-  result.rawType = rawType
-
-var name: cstring = "example"
-
-var ret: seq[tuple[name: string, a: TAny]] = @[]
-for i in 0..8000:
-  var tup = ($name, newAny(nil, nil))
-  assert(tup[0] == "example")
-  ret.add(tup)
-  assert(ret[ret.len()-1][0] == "example")
-
-echo "came here"
-
diff --git a/tests/assign/tgenericassigntuples.nim b/tests/assign/tgenericassigntuples.nim
deleted file mode 100644
index cca244577..000000000
--- a/tests/assign/tgenericassigntuples.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-discard """
-  output: '''abc232'''
-"""
-
-var t, s: tuple[x: string, c: int]
-
-proc ugh: seq[tuple[x: string, c: int]] =
-  result = @[("abc", 232)]
-
-t = ugh()[0]
-s = t
-s = ugh()[0]
-
-echo s[0], t[1]
-
-
diff --git a/tests/assign/tobjasgn.nim b/tests/assign/tobjasgn.nim
deleted file mode 100644
index e731d9e93..000000000
--- a/tests/assign/tobjasgn.nim
+++ /dev/null
@@ -1,44 +0,0 @@
-discard """
-  output: '''8 5 0 0
-pre test a:test b:1 c:2 haha:3
-assignment test a:test b:1 c:2 haha:3
-'''
-"""
-
-# bug #1005
-
-type
-  TSomeObj = object of TObject
-    a, b: int
-  PSomeObj = ref object
-    a, b: int
-
-var a = TSomeObj(a: 8)
-var b = PSomeObj(a: 5)
-echo a.a, " ", b.a, " ", a.b, " ", b.b
-
-# bug #575
-
-type
-  Something = object of Tobject
-    a: string
-    b, c: int32
-
-type
-  Other = object of Something
-    haha: int
-
-proc `$`(x: Other): string =
-  result = "a:" & x.a & " b:" & $x.b & " c:" & $x.c & " haha:" & $x.haha
-
-var
-  t: Other
-
-t.a = "test"
-t.b = 1
-t.c = 2
-t.haha = 3
-
-echo "pre test ", $t
-var x = t
-echo "assignment test ", x
diff --git a/tests/assign/tobject_assign.nim b/tests/assign/tobject_assign.nim
new file mode 100644
index 000000000..8e39ead53
--- /dev/null
+++ b/tests/assign/tobject_assign.nim
@@ -0,0 +1,49 @@
+# bug #16706
+
+block: # reduced example
+  type
+    A = object of RootObj
+      a0: string
+    B = object
+      b0: seq[A]
+  var c = newSeq[A](2)
+  var d = B(b0: c)
+
+when true: # original example
+  import std/[options, tables, times]
+
+  type
+    Data* = object
+      shifts*: OrderedTable[int64, Shift]
+      balance*: float
+
+    Shift* = object
+      quoted*: bool
+      date*: DateTime
+      description*: string
+      start*: Option[DateTime]
+      finish*: Option[DateTime]
+      breakTime*: Option[Duration]
+      rate*: float
+      qty: Option[float]
+      id*: int64
+
+  let shift = Shift(
+    quoted: true,
+    date: parse("2000-01-01", "yyyy-MM-dd"),
+    description: "abcdef",
+    start: none(DateTime),
+    finish: none(DateTime),
+    breakTime: none(Duration),
+    rate: 462.11,
+    qty: some(10.0),
+    id: getTime().toUnix()
+  )
+
+  var shifts: OrderedTable[int64, Shift]
+  shifts[shift.id] = shift
+
+  discard Data(
+    shifts: shifts,
+    balance: 0.00
+  )
diff --git a/tests/assign/toverload_asgn1.nim b/tests/assign/toverload_asgn1.nim
deleted file mode 100644
index 01e7e7aa7..000000000
--- a/tests/assign/toverload_asgn1.nim
+++ /dev/null
@@ -1,76 +0,0 @@
-discard """
-  output: '''Concrete '='
-Concrete '='
-Concrete '='
-Concrete '='
-Concrete '='
-GenericT[T] '=' int
-GenericT[T] '=' float
-GenericT[T] '=' float
-GenericT[T] '=' float
-GenericT[T] '=' string
-GenericT[T] '=' int8
-GenericT[T] '=' bool
-GenericT[T] '=' bool
-GenericT[T] '=' bool
-GenericT[T] '=' bool'''
-  disabled: "true"
-"""
-
-import typetraits
-
-type
-  Concrete = object
-    a, b: string
-
-proc `=`(d: var Concrete; src: Concrete) =
-  shallowCopy(d.a, src.a)
-  shallowCopy(d.b, src.b)
-  echo "Concrete '='"
-
-var x, y: array[0..2, Concrete]
-var cA, cB: Concrete
-
-var cATup, cBTup: tuple[x: int, ha: Concrete]
-
-x = y
-cA = cB
-cATup = cBTup
-
-type
-  GenericT[T] = object
-    a, b: T
-
-proc `=`[T](d: var GenericT[T]; src: GenericT[T]) =
-  shallowCopy(d.a, src.a)
-  shallowCopy(d.b, src.b)
-  echo "GenericT[T] '=' ", type(T).name
-
-var ag: GenericT[int]
-var bg: GenericT[int]
-
-ag = bg
-
-var xg, yg: array[0..2, GenericT[float]]
-var cAg, cBg: GenericT[string]
-
-var cATupg, cBTupg: tuple[x: int, ha: GenericT[int8]]
-
-xg = yg
-cAg = cBg
-cATupg = cBTupg
-
-var caSeqg, cbSeqg: seq[GenericT[bool]]
-newSeq(cbSeqg, 4)
-caSeqg = cbSeqg
-
-when false:
-  type
-    Foo = object
-      case b: bool
-      of false: xx: GenericT[int]
-      of true: yy: bool
-
-  var
-    a, b: Foo
-  a = b
diff --git a/tests/assign/tvariantasgn.nim b/tests/assign/tvariantasgn.nim
index 46cc23dd1..4c3c38ca5 100644
--- a/tests/assign/tvariantasgn.nim
+++ b/tests/assign/tvariantasgn.nim
@@ -1,7 +1,7 @@
 discard """
-  file: "tvariantasgn.nim"
   output: "came here"
 """
+
 #BUG
 type
   TAnyKind = enum
@@ -15,16 +15,25 @@ type
     of nkString: strVal: string
 
 var s: TAny
-s.kind = nkString
-s.strVal = "test"
+s = TAny(kind: nkString, strVal: "test")
 
 var nr: TAny
-nr.kind = nkint
-nr.intVal = 78
+s = TAny(kind: nkInt, intVal: 78)
 
 
 # s = nr # works
 nr = s # fails!
 echo "came here"
 
+block: # bug #12464
+  type
+    Foo = object
+      case isFunc: bool
+      of false: nil
+      of true:
+        fun: proc(): int
+
+  const i = Foo(isFunc: false)
 
+  let j = i
+  doAssert not j.isFunc
diff --git a/tests/ast_pattern_matching.nim b/tests/ast_pattern_matching.nim
new file mode 100644
index 000000000..0ba0aa57a
--- /dev/null
+++ b/tests/ast_pattern_matching.nim
@@ -0,0 +1,584 @@
+# this is a copy paste implementation of github.com/krux02/ast_pattern_matching
+# Please provide bugfixes upstream first before adding them here.
+
+import macros, strutils, tables
+
+export macros
+
+when isMainModule:
+  template debug(args: varargs[untyped]): untyped =
+    echo args
+else:
+  template debug(args: varargs[untyped]): untyped =
+    discard
+
+const
+  nnkIntLiterals*   = nnkCharLit..nnkUInt64Lit
+  nnkStringLiterals* = nnkStrLit..nnkTripleStrLit
+  nnkFloatLiterals* = nnkFloatLit..nnkFloat64Lit
+
+proc newLit[T: enum](arg: T): NimNode =
+  newIdentNode($arg)
+
+proc newLit[T](arg: set[T]): NimNode =
+  ## does not work for the empty sets
+  result = nnkCurly.newTree
+  for x in arg:
+    result.add newLit(x)
+
+type SomeFloat = float | float32 | float64
+
+proc len[T](arg: set[T]): int = card(arg)
+
+type
+  MatchingErrorKind* = enum
+    NoError
+    WrongKindLength
+    WrongKindValue
+    WrongIdent
+    WrongCustomCondition
+
+  MatchingError = object
+    node*: NimNode
+    expectedKind*: set[NimNodeKind]
+    case kind*: MatchingErrorKind
+    of NoError:
+      discard
+    of WrongKindLength:
+      expectedLength*: int
+    of WrongKindValue:
+      expectedValue*: NimNode
+    of WrongIdent, WrongCustomCondition:
+      strVal*: string
+
+proc `$`*(arg: MatchingError): string =
+  let n = arg.node
+  case arg.kind
+  of NoError:
+    "no error"
+  of WrongKindLength:
+    let k = arg.expectedKind
+    let l = arg.expectedLength
+    var msg = "expected "
+    if k.len == 0:
+      msg.add "any node"
+    elif k.len == 1:
+      for el in k:  # only one element but there is no index op for sets
+        msg.add $el
+    else:
+      msg.add "a node in" & $k
+
+    if l >= 0:
+      msg.add " with " & $l & " child(ren)"
+    msg.add ", but got " & $n.kind
+    if l >= 0:
+      msg.add " with " & $n.len & " child(ren)"
+    msg
+  of WrongKindValue:
+    let k = $arg.expectedKind
+    let v = arg.expectedValue.repr
+    var msg = "expected " & k & " with value " & v & " but got " & n.lispRepr
+    if n.kind in {nnkOpenSymChoice, nnkClosedSymChoice}:
+      msg = msg & " (a sym-choice does not have a strVal member, maybe you should match with `ident`)"
+    msg
+  of WrongIdent:
+    let prefix = "expected ident `" & arg.strVal & "` but got "
+    if n.kind in {nnkIdent, nnkSym, nnkOpenSymChoice, nnkClosedSymChoice}:
+      prefix & "`" & n.strVal & "`"
+    else:
+      prefix & $n.kind & " with " & $n.len & " child(ren)"
+  of WrongCustomCondition:
+    "custom condition check failed: " & arg.strVal
+
+
+proc failWithMatchingError*(arg: MatchingError): void {.compileTime, noReturn.} =
+  error($arg, arg.node)
+
+proc expectValue(arg: NimNode; value: SomeInteger): void {.compileTime.} =
+  arg.expectKind nnkLiterals
+  if arg.intVal != int(value):
+    error("expected value " & $value & " but got " & arg.repr, arg)
+
+proc expectValue(arg: NimNode; value: SomeFloat): void {.compileTime.} =
+  arg.expectKind nnkLiterals
+  if arg.floatVal != float(value):
+    error("expected value " & $value & " but got " & arg.repr, arg)
+
+proc expectValue(arg: NimNode; value: string): void {.compileTime.} =
+  arg.expectKind nnkLiterals
+  if arg.strVal != value:
+    error("expected value " & value & " but got " & arg.repr, arg)
+
+proc expectValue[T](arg: NimNode; value: pointer): void {.compileTime.} =
+  arg.expectKind nnkLiterals
+  if value != nil:
+    error("Expect Value for pointers works only on `nil` when the argument is a pointer.")
+  arg.expectKind nnkNilLit
+
+proc expectIdent(arg: NimNode; strVal: string): void {.compileTime.} =
+  if not arg.eqIdent(strVal):
+    error("Expect ident `" & strVal & "` but got " & arg.repr)
+
+proc matchLengthKind*(arg: NimNode; kind: set[NimNodeKind]; length: int): MatchingError {.compileTime.} =
+  let kindFail   = not(kind.card == 0 or arg.kind in kind)
+  let lengthFail = not(length < 0 or length == arg.len)
+  if kindFail or lengthFail:
+    result.node = arg
+    result.kind = WrongKindLength
+    result.expectedLength = length
+    result.expectedKind   = kind
+
+
+proc matchLengthKind*(arg: NimNode; kind: NimNodeKind; length: int): MatchingError {.compileTime.} =
+  matchLengthKind(arg, {kind}, length)
+
+proc matchValue(arg: NimNode; kind: set[NimNodeKind]; value: SomeInteger): MatchingError {.compileTime.} =
+  template kindFail: bool  = not(kind.card == 0 or arg.kind in kind)
+  template valueFail: bool = arg.intVal != int(value)
+  if kindFail or valueFail:
+    result.node = arg
+    result.kind = WrongKindValue
+    result.expectedKind  = kind
+    result.expectedValue = newLit(value)
+
+proc matchValue(arg: NimNode; kind: NimNodeKind; value: SomeInteger): MatchingError {.compileTime.} =
+  matchValue(arg, {kind}, value)
+
+proc matchValue(arg: NimNode; kind: set[NimNodeKind]; value: SomeFloat): MatchingError {.compileTime.} =
+  let kindFail   = not(kind.card == 0 or arg.kind in kind)
+  let valueFail  = arg.floatVal != float(value)
+  if kindFail or valueFail:
+    result.node = arg
+    result.kind = WrongKindValue
+    result.expectedKind  = kind
+    result.expectedValue = newLit(value)
+
+proc matchValue(arg: NimNode; kind: NimNodeKind; value: SomeFloat): MatchingError {.compileTime.} =
+  matchValue(arg, {kind}, value)
+
+const nnkStrValKinds = {nnkStrLit, nnkRStrLit, nnkTripleStrLit, nnkIdent, nnkSym}
+
+proc matchValue(arg: NimNode; kind: set[NimNodeKind]; value: string): MatchingError {.compileTime.} =
+  # if kind * nnkStringLiterals TODO do something that ensures that here is only checked for string literals
+  let kindFail   = not(kind.card == 0 or arg.kind in kind)
+  let valueFail  =
+    if kind.card == 0:
+      false
+    else:
+      arg.kind notin (kind * nnkStrValKinds) or arg.strVal != value
+  if kindFail or valueFail:
+    result.node = arg
+    result.kind = WrongKindValue
+    result.expectedKind  = kind
+    result.expectedValue = newLit(value)
+
+proc matchValue(arg: NimNode; kind: NimNodeKind; value: string): MatchingError {.compileTime.} =
+  matchValue(arg, {kind}, value)
+
+proc matchValue[T](arg: NimNode; value: pointer): MatchingError {.compileTime.} =
+  if value != nil:
+    error("Expect Value for pointers works only on `nil` when the argument is a pointer.")
+  arg.matchLengthKind(nnkNilLit, -1)
+
+proc matchIdent*(arg:NimNode; value: string): MatchingError =
+  if not arg.eqIdent(value):
+    result.node = arg
+    result.kind = Wrongident
+    result.strVal = value
+
+proc checkCustomExpr*(arg: NimNode; cond: bool, exprstr: string): MatchingError =
+  if not cond:
+    result.node = arg
+    result.kind = WrongCustomCondition
+    result.strVal = exprstr
+
+static:
+  var literals: array[19, NimNode]
+  var i = 0
+  for litKind in nnkLiterals:
+    literals[i] = ident($litKind)
+    i += 1
+
+  var nameToKind = newTable[string, NimNodeKind]()
+  for kind in NimNodeKind:
+    nameToKind[ ($kind)[3..^1] ] = kind
+
+  let identifierKinds = newLit({nnkSym, nnkIdent, nnkOpenSymChoice, nnkClosedSymChoice})
+
+proc generateMatchingCode(astSym: NimNode, pattern: NimNode, depth: int, blockLabel, errorSym, localsArraySym: NimNode; dest: NimNode): int =
+  ## return the number of indices used in the array for local variables.
+
+  var currentLocalIndex = 0
+
+  proc nodeVisiting(astSym: NimNode, pattern: NimNode, depth: int): void =
+    let ind = "  ".repeat(depth) # indentation
+
+    proc genMatchLogic(matchProc, argSym1, argSym2: NimNode): void =
+      dest.add quote do:
+        `errorSym` = `astSym`.`matchProc`(`argSym1`, `argSym2`)
+        if `errorSym`.kind != NoError:
+          break `blockLabel`
+
+    proc genIdentMatchLogic(identValueLit: NimNode): void =
+      dest.add quote do:
+        `errorSym` = `astSym`.matchIdent(`identValueLit`)
+        if `errorSym`.kind != NoError:
+          break `blockLabel`
+
+    proc genCustomMatchLogic(conditionExpr: NimNode): void =
+      let exprStr = newLit(conditionExpr.repr)
+      dest.add quote do:
+        `errorSym` = `astSym`.checkCustomExpr(`conditionExpr`, `exprStr`)
+        if `errorSym`.kind != NoError:
+          break `blockLabel`
+
+    # proc handleKindMatching(kindExpr: NimNode): void =
+    #   if kindExpr.eqIdent("_"):
+    #     # this is the wildcand that matches any kind
+    #     return
+    #   else:
+    #     genMatchLogic(bindSym"matchKind", kindExpr)
+
+    # generate recursively a matching expression
+    if pattern.kind == nnkCall:
+      pattern.expectMinLen(1)
+
+      debug ind, pattern[0].repr, "("
+
+      let kindSet = if pattern[0].eqIdent("_"): nnkCurly.newTree else: pattern[0]
+      # handleKindMatching(pattern[0])
+
+      if pattern.len == 2 and pattern[1].kind == nnkExprEqExpr:
+        if pattern[1][1].kind in nnkStringLiterals:
+          pattern[1][0].expectIdent("strVal")
+        elif pattern[1][1].kind in nnkIntLiterals:
+          pattern[1][0].expectIdent("intVal")
+        elif pattern[1][1].kind in nnkFloatLiterals:
+          pattern[1][0].expectIdent("floatVal")
+
+        genMatchLogic(bindSym"matchValue", kindSet, pattern[1][1])
+
+      else:
+        let lengthLit = newLit(pattern.len - 1)
+        genMatchLogic(bindSym"matchLengthKind", kindSet, lengthLit)
+
+        for i in 1 ..< pattern.len:
+          let childSym = nnkBracketExpr.newTree(localsArraySym, newLit(currentLocalIndex))
+          currentLocalIndex += 1
+          let indexLit = newLit(i - 1)
+          dest.add quote do:
+            `childSym` = `astSym`[`indexLit`]
+          nodeVisiting(childSym, pattern[i], depth + 1)
+      debug ind, ")"
+    elif pattern.kind == nnkCallStrLit and pattern[0].eqIdent("ident"):
+      genIdentMatchLogic(pattern[1])
+
+    elif pattern.kind == nnkPar and pattern.len == 1:
+      nodeVisiting(astSym, pattern[0], depth)
+    elif pattern.kind == nnkPrefix:
+      error("prefix patterns not implemented", pattern)
+    elif pattern.kind == nnkAccQuoted:
+      debug ind, pattern.repr
+      let matchedExpr = pattern[0]
+      matchedExpr.expectKind nnkIdent
+      dest.add quote do:
+        let `matchedExpr` = `astSym`
+
+    elif pattern.kind == nnkInfix and pattern[0].eqIdent("@"):
+      pattern[1].expectKind nnkAccQuoted
+
+      let matchedExpr = pattern[1][0]
+      matchedExpr.expectKind nnkIdent
+      dest.add quote do:
+        let `matchedExpr` = `astSym`
+
+      debug ind, pattern[1].repr, " = "
+      nodeVisiting(matchedExpr, pattern[2], depth + 1)
+
+    elif pattern.kind == nnkInfix and pattern[0].eqIdent("|="):
+      nodeVisiting(astSym, pattern[1], depth + 1)
+      genCustomMatchLogic(pattern[2])
+
+    elif pattern.kind in nnkCallKinds:
+      error("only boring call syntax allowed, this is " & $pattern.kind & ".", pattern)
+    elif pattern.kind in nnkLiterals:
+      genMatchLogic(bindSym"matchValue", nnkCurly.newTree, pattern)
+    elif not pattern.eqIdent("_"):
+      # When it is not one of the other branches, it is simply treated
+      # as an expression for the node kind, without checking child
+      # nodes.
+      debug ind, pattern.repr
+      genMatchLogic(bindSym"matchLengthKind", pattern, newLit(-1))
+
+  nodeVisiting(astSym, pattern, depth)
+
+  return currentLocalIndex
+
+macro matchAst*(astExpr: NimNode; args: varargs[untyped]): untyped =
+  let astSym = genSym(nskLet, "ast")
+  let beginBranches = if args[0].kind == nnkIdent: 1 else: 0
+  let endBranches   = if args[^1].kind == nnkElse: args.len - 1 else: args.len
+  for i in beginBranches ..< endBranches:
+    args[i].expectKind nnkOfBranch
+
+  let outerErrorSym: NimNode =
+    if beginBranches == 1:
+      args[0].expectKind nnkIdent
+      args[0]
+    else:
+      nil
+
+  let elseBranch: NimNode =
+    if endBranches == args.len - 1:
+      args[^1].expectKind(nnkElse)
+      args[^1][0]
+    else:
+      nil
+
+  let outerBlockLabel = genSym(nskLabel, "matchingSection")
+  let outerStmtList = newStmtList()
+  let errorSymbols = nnkBracket.newTree
+
+  ## the vm only allows 255 local variables. This sucks a lot and I
+  ## have to work around it.  So instead of creating a lot of local
+  ## variables, I just create one array of local variables. This is
+  ## just annoying.
+  let localsArraySym = genSym(nskVar, "locals")
+  var localsArrayLen: int = 0
+
+  for i in beginBranches ..< endBranches:
+    let ofBranch = args[i]
+
+    ofBranch.expectKind(nnkOfBranch)
+    ofBranch.expectLen(2)
+    let pattern = ofBranch[0]
+    let code = ofBranch[1]
+    code.expectKind nnkStmtList
+    let stmtList = newStmtList()
+    let blockLabel = genSym(nskLabel, "matchingBranch")
+    let errorSym = genSym(nskVar, "branchError")
+
+    errorSymbols.add errorSym
+    let numLocalsUsed = generateMatchingCode(astSym, pattern, 0, blockLabel, errorSym, localsArraySym, stmtList)
+    localsArrayLen = max(localsArrayLen, numLocalsUsed)
+    stmtList.add code
+    # maybe there is a better mechanism disable errors for statement after return
+    if code[^1].kind != nnkReturnStmt:
+      stmtList.add nnkBreakStmt.newTree(outerBlockLabel)
+
+    outerStmtList.add quote do:
+      var `errorSym`: MatchingError
+      block `blockLabel`:
+        `stmtList`
+
+  if elseBranch != nil:
+    if outerErrorSym != nil:
+      outerStmtList.add quote do:
+        let `outerErrorSym` = @`errorSymbols`
+        `elseBranch`
+    else:
+      outerStmtList.add elseBranch
+
+  else:
+    if errorSymbols.len == 1:
+      # there is only one of branch and no else branch
+      # the error message can be very precise here.
+      let errorSym = errorSymbols[0]
+      outerStmtList.add quote do:
+        failWithMatchingError(`errorSym`)
+    else:
+
+      var patterns: string = ""
+      for i in beginBranches ..< endBranches:
+        let ofBranch = args[i]
+        let pattern = ofBranch[0]
+        patterns.add pattern.repr
+        patterns.add "\n"
+
+      let patternsLit = newLit(patterns)
+      outerStmtList.add quote do:
+        error("Ast pattern mismatch: got " & `astSym`.lispRepr & "\nbut expected one of:\n" & `patternsLit`, `astSym`)
+
+  let lengthLit = newLit(localsArrayLen)
+  result = quote do:
+    block `outerBlockLabel`:
+      let `astSym` = `astExpr`
+      var `localsArraySym`: array[`lengthLit`, NimNode]
+      `outerStmtList`
+
+  debug result.repr
+
+proc recursiveNodeVisiting*(arg: NimNode, callback: proc(arg: NimNode): bool) =
+  ## if `callback` returns true, visitor continues to visit the
+  ## children of `arg` otherwise it stops.
+  if callback(arg):
+    for child in arg:
+      recursiveNodeVisiting(child, callback)
+
+macro matchAstRecursive*(ast: NimNode; args: varargs[untyped]): untyped =
+  # Does not recurse further on matched nodes.
+  if args[^1].kind == nnkElse:
+    error("Recursive matching with an else branch is pointless.", args[^1])
+
+  let visitor = genSym(nskProc, "visitor")
+  let visitorArg = genSym(nskParam, "arg")
+
+  let visitorStmtList = newStmtList()
+
+  let matchingSection = genSym(nskLabel, "matchingSection")
+
+  let localsArraySym = genSym(nskVar, "locals")
+  let branchError = genSym(nskVar, "branchError")
+  var localsArrayLen = 0
+
+  for ofBranch in args:
+    ofBranch.expectKind(nnkOfBranch)
+    ofBranch.expectLen(2)
+    let pattern = ofBranch[0]
+    let code = ofBranch[1]
+    code.expectkind(nnkStmtList)
+
+    let stmtList = newStmtList()
+    let matchingBranch = genSym(nskLabel, "matchingBranch")
+
+    let numLocalsUsed = generateMatchingCode(visitorArg, pattern, 0, matchingBranch, branchError, localsArraySym, stmtList)
+    localsArrayLen = max(localsArrayLen, numLocalsUsed)
+
+    stmtList.add code
+    stmtList.add nnkBreakStmt.newTree(matchingSection)
+
+
+    visitorStmtList.add quote do:
+      `branchError`.kind = NoError
+      block `matchingBranch`:
+        `stmtList`
+
+  let resultIdent = ident"result"
+
+  let visitingProc = bindSym"recursiveNodeVisiting"
+  let lengthLit = newLit(localsArrayLen)
+
+  result = quote do:
+    proc `visitor`(`visitorArg`: NimNode): bool =
+      block `matchingSection`:
+        var `localsArraySym`: array[`lengthLit`, NimNode]
+        var `branchError`: MatchingError
+        `visitorStmtList`
+        `resultIdent` = true
+
+    `visitingProc`(`ast`, `visitor`)
+
+  debug result.repr
+
+################################################################################
+################################# Example Code #################################
+################################################################################
+
+when isMainModule:
+  static:
+    let mykinds = {nnkIdent, nnkCall}
+
+  macro foo(arg: untyped): untyped =
+    matchAst(arg, matchError):
+    of nnkStmtList(nnkIdent, nnkIdent, nnkIdent):
+      echo(88*88+33*33)
+    of nnkStmtList(
+      _(
+        nnkIdentDefs(
+          ident"a",
+          nnkEmpty, nnkIntLit(intVal = 123)
+        )
+      ),
+      _,
+      nnkForStmt(
+        nnkIdent(strVal = "i"),
+        nnkInfix,
+        `mysym` @ nnkStmtList
+      )
+    ):
+      echo "The AST did match!!!"
+      echo "The matched sub tree is the following:"
+      echo mysym.lispRepr
+    #else:
+    #  echo "sadly the AST did not match :("
+    #  echo arg.treeRepr
+    #  failWithMatchingError(matchError[1])
+
+  foo:
+    let a = 123
+    let b = 342
+    for i in a ..< b:
+      echo "Hallo", i
+
+  static:
+
+    var ast = quote do:
+      type
+        A[T: static[int]] = object
+
+    ast = ast[0]
+    ast.matchAst(err):  # this is a sub ast for this a findAst or something like that is useful
+    of nnkTypeDef(_, nnkGenericParams( nnkIdentDefs( nnkIdent(strVal = "T"), `staticTy`, nnkEmpty )), _):
+      echo "`", staticTy.repr, "` used to be of nnkStaticTy, now it is ", staticTy.kind, " with ", staticTy[0].repr
+    ast = quote do:
+      if cond1: expr1 elif cond2: expr2 else: expr3
+
+    ast.matchAst:
+    of {nnkIfExpr, nnkIfStmt}(
+      {nnkElifExpr, nnkElifBranch}(`cond1`, `expr1`),
+      {nnkElifExpr, nnkElifBranch}(`cond2`, `expr2`),
+      {nnkElseExpr, nnkElse}(`expr3`)
+    ):
+      echo "ok"
+
+    let ast2 = nnkStmtList.newTree( newLit(1) )
+
+    ast2.matchAst:
+    of nnkIntLit( 1 ):
+      echo "fail"
+    of nnkStmtList( 1 ):
+      echo "ok"
+
+    ast = bindSym"[]"
+    ast.matchAst(errors):
+    of nnkClosedSymChoice(strVal = "[]"):
+      echo "fail, this is the wrong syntax, a sym choice does not have a `strVal` member."
+    of ident"[]":
+      echo "ok"
+
+    const myConst = 123
+    ast = newLit(123)
+
+    ast.matchAst:
+    of _(intVal = myConst):
+      echo "ok"
+
+    macro testRecCase(ast: untyped): untyped =
+      ast.matchAstRecursive:
+      of nnkIdentDefs(`a`,`b`,`c`):
+        echo "got ident defs a: ", a.repr, " b: ", b.repr, " c: ", c.repr
+      of ident"m":
+        echo "got the ident m"
+
+    testRecCase:
+      type Obj[T] {.inheritable.} = object
+        name: string
+        case isFat: bool
+        of true:
+          m: array[100_000, T]
+        of false:
+          m: array[10, T]
+
+
+    macro testIfCondition(ast: untyped): untyped =
+      let literals = nnkBracket.newTree
+      ast.matchAstRecursive:
+      of `intLit` @ nnkIntLit |= intLit.intVal > 5:
+        literals.add intLit
+
+      let literals2 = quote do:
+        [6,7,8,9]
+
+      doAssert literals2 == literals
+
+    testIfCondition([1,6,2,7,3,8,4,9,5,0,"123"])
diff --git a/tests/astspec/tastspec.nim b/tests/astspec/tastspec.nim
new file mode 100644
index 000000000..c99d8ec79
--- /dev/null
+++ b/tests/astspec/tastspec.nim
@@ -0,0 +1,1119 @@
+discard """
+action: compile
+"""
+
+# this test should ensure that the AST doesn't change slightly without it getting noticed.
+
+import ../ast_pattern_matching
+
+template expectNimNode(arg: untyped): NimNode = arg
+  ## This template here is just to be injected by `myquote`, so that
+  ## a nice error message appears when the captured symbols are not of
+  ## type `NimNode`.
+
+proc substitudeComments(symbols, values, n: NimNode): NimNode =
+  ## substitutes all nodes of kind nnkCommentStmt to parameter
+  ## symbols. Consumes the argument `n`.
+  if n.kind == nnkCommentStmt:
+    values.add newCall(bindSym"newCommentStmtNode", newLit(n.strVal))
+    # Gensym doesn't work for parameters. These identifiers won't
+    # clash unless an argument is constructed to clash here.
+    symbols.add ident("comment" & $values.len & "_XObBdOnh6meCuJK2smZV")
+    return symbols[^1]
+  for i in 0 ..< n.len:
+    n[i] = substitudeComments(symbols, values, n[i])
+  return n
+
+macro myquote*(args: varargs[untyped]): untyped =
+  expectMinLen(args, 1)
+
+  # This is a workaround for #10430 where comments are removed in
+  # template expansions. This workaround lifts all comments
+  # statements to be arguments of the temporary template.
+
+  let extraCommentSymbols = newNimNode(nnkBracket)
+  let extraCommentGenExpr = newNimNode(nnkBracket)
+  let body = substitudeComments(
+    extraCommentSymbols, extraCommentGenExpr, args[^1]
+  )
+
+  let formalParams = nnkFormalParams.newTree(ident"untyped")
+  for i in 0 ..< args.len-1:
+    formalParams.add nnkIdentDefs.newTree(
+      args[i], ident"untyped", newEmptyNode()
+    )
+  for sym in extraCommentSymbols:
+    formalParams.add nnkIdentDefs.newTree(
+      sym, ident"untyped", newEmptyNode()
+    )
+
+  let templateSym = genSym(nskTemplate)
+  let templateDef = nnkTemplateDef.newTree(
+    templateSym,
+    newEmptyNode(),
+    newEmptyNode(),
+    formalParams,
+    nnkPragma.newTree(ident"dirty"),
+    newEmptyNode(),
+    args[^1]
+  )
+
+  let templateCall = newCall(templateSym)
+  for i in 0 ..< args.len-1:
+    let symName = args[i]
+    # identifiers and quoted identifiers are allowed.
+    if symName.kind == nnkAccQuoted:
+      symName.expectLen 1
+      symName[0].expectKind nnkIdent
+    else:
+      symName.expectKind nnkIdent
+    templateCall.add newCall(bindSym"expectNimNode", symName)
+  for expr in extraCommentGenExpr:
+    templateCall.add expr
+  let getAstCall = newCall(bindSym"getAst", templateCall)
+  result = newStmtList(templateDef, getAstCall)
+
+
+macro testAddrAst(arg: typed): bool =
+  arg.expectKind nnkStmtListExpr
+  arg[0].expectKind(nnkVarSection)
+  arg[1].expectKind({nnkAddr, nnkCall})
+  result = newLit(arg[1].kind == nnkCall)
+
+const newAddrAst: bool = testAddrAst((var x: int; addr(x)))
+
+static:
+  echo "new addr ast: ", newAddrAst
+
+# TODO test on matching failures
+
+proc peelOff*(arg: NimNode, kinds: set[NimNodeKind]): NimNode {.compileTime.} =
+  ## Peel off  nodes of a specific kinds.
+  if arg.len == 1 and arg.kind in kinds:
+    arg[0].peelOff(kinds)
+  else:
+    arg
+
+proc peelOff*(arg: NimNode, kind: NimNodeKind): NimNode {.compileTime.} =
+  ## Peel off nodes of a specific kind.
+  if arg.len == 1 and arg.kind == kind:
+    arg[0].peelOff(kind)
+  else:
+    arg
+
+static:
+  template testPattern(pattern, astArg: untyped): untyped =
+    let ast = quote do: `astArg`
+    ast.matchAst:
+    of `pattern`:
+      echo "ok"
+
+  template testPatternFail(pattern, astArg: untyped): untyped =
+    let ast = quote do: `astArg`
+    ast.matchAst:
+    of `pattern`:
+      error("this should not match", ast)
+    else:
+      echo "OK"
+
+
+  testPattern nnkIntLit(intVal = 42), 42
+  testPattern nnkInt8Lit(intVal = 42), 42'i8
+  testPattern nnkInt16Lit(intVal = 42), 42'i16
+  testPattern nnkInt32Lit(intVal = 42), 42'i32
+  testPattern nnkInt64Lit(intVal = 42), 42'i64
+  testPattern nnkUInt8Lit(intVal = 42), 42'u8
+  testPattern nnkUInt16Lit(intVal = 42), 42'u16
+  testPattern nnkUInt32Lit(intVal = 42), 42'u32
+  testPattern nnkUInt64Lit(intVal = 42), 42'u64
+  #testPattern nnkFloat64Lit(floatVal = 42.0), 42.0
+  testPattern nnkFloat32Lit(floatVal = 42.0), 42.0'f32
+  #testPattern nnkFloat64Lit(floatVal = 42.0), 42.0'f64
+  testPattern nnkStrLit(strVal = "abc"), "abc"
+  testPattern nnkRStrLit(strVal = "abc"), r"abc"
+  testPattern nnkTripleStrLit(strVal = "abc"), """abc"""
+  testPattern nnkCharLit(intVal = 32), ' '
+  testPattern nnkNilLit(), nil
+  testPattern nnkIdent(strVal = "myIdentifier"), myIdentifier
+
+  testPatternFail nnkInt8Lit(intVal = 42), 42'i16
+  testPatternFail nnkInt16Lit(intVal = 42), 42'i8
+
+
+
+# this should be just `block` but it doesn't work that way anymore because of VM.
+macro scope(arg: untyped): untyped =
+  let procSym = genSym(nskProc)
+  result = quote do:
+    proc `procSym`() {.compileTime.} =
+      `arg`
+
+    `procSym`()
+
+static:
+  ## Command call
+  scope:
+
+    let ast = myquote:
+      echo "abc", "xyz"
+
+    ast.matchAst:
+    of nnkCommand(ident"echo", "abc", "xyz"):
+      echo "ok"
+
+  ## Call with ``()``
+
+  scope:
+    let ast = myquote:
+      echo("abc", "xyz")
+
+    ast.matchAst:
+    of nnkCall(ident"echo", "abc", "xyz"):
+      echo "ok"
+
+  ## Infix operator call
+
+  macro testInfixOperatorCall(ast: untyped): untyped =
+    ast.matchAst(errorSym):
+    of nnkInfix(
+      ident"&",
+      nnkStrLit(strVal = "abc"),
+      nnkStrLit(strVal = "xyz")
+    ):
+      echo "ok1"
+    of nnkInfix(
+      ident"+",
+      nnkIntLit(intVal = 5),
+      nnkInfix(
+        ident"*",
+        nnkIntLit(intVal = 3),
+        nnkIntLit(intVal = 4)
+      )
+    ):
+      echo "ok2"
+    of nnkCall(
+      nnkAccQuoted(
+        ident"+"
+      ),
+      nnkIntLit(intVal = 3),
+      nnkIntLit(intVal = 4)
+    ):
+      echo "ok3"
+
+  testInfixOperatorCall("abc" & "xyz")
+  testInfixOperatorCall(5 + 3 * 4)
+  testInfixOperatorCall(`+`(3, 4))
+
+
+  ## Prefix operator call
+
+  scope:
+
+    let ast = myquote:
+      ? "xyz"
+
+    ast.matchAst(err):
+    of nnkPrefix(
+      ident"?",
+      nnkStrLit(strVal = "xyz")
+    ):
+      echo "ok"
+
+
+  ## Postfix operator call
+
+  scope:
+
+    let ast = myquote:
+      proc identifier*
+
+    ast[0].matchAst(err):
+    of nnkPostfix(
+      ident"*",
+      ident"identifier"
+    ):
+      echo "ok"
+
+
+  ## Call with named arguments
+
+  macro testCallWithNamedArguments(ast: untyped): untyped =
+    ast.peelOff(nnkStmtList).matchAst:
+    of nnkCall(
+      ident"writeLine",
+      nnkExprEqExpr(
+        ident"file",
+        ident"stdout"
+      ),
+      nnkStrLit(strVal = "hallo")
+    ):
+      echo "ok"
+
+  testCallWithNamedArguments:
+    writeLine(file=stdout, "hallo")
+
+  ## Call with raw string literal
+  scope:
+    let ast = myquote:
+      echo"abc"
+
+
+    ast.matchAst(err):
+    of nnkCallStrLit(
+      ident"echo",
+      nnkRStrLit(strVal = "abc")
+    ):
+      echo "ok"
+
+  ## Dereference operator ``[]``
+
+  scope:
+    # The dereferece operator exists only on a typed ast.
+    macro testDereferenceOperator(ast: typed): untyped =
+      ast.matchAst(err):
+      of nnkDerefExpr(_):
+        echo "ok"
+
+    var x: ptr int
+    testDereferenceOperator(x[])
+
+
+
+  ## Addr operator
+
+  scope:
+    # The addr operator exists only on a typed ast.
+    macro testAddrOperator(ast: untyped): untyped =
+      echo ast.treeRepr
+      ast.matchAst(err):
+      of nnkAddr(ident"x"):
+        echo "old nim"
+      of nnkCall(ident"addr", ident"x"):
+        echo "ok"
+
+    var x: int
+    testAddrOperator(addr(x))
+
+
+  ## Cast operator
+
+  scope:
+
+    let ast = myquote:
+      cast[T](x)
+
+    ast.matchAst:
+    of nnkCast(ident"T", ident"x"):
+      echo "ok"
+
+
+  ## Object access operator ``.``
+
+  scope:
+
+    let ast = myquote:
+      x.y
+
+    ast.matchAst:
+    of nnkDotExpr(ident"x", ident"y"):
+      echo "ok"
+
+  ## Array access operator ``[]``
+
+  macro testArrayAccessOperator(ast: untyped): untyped =
+    ast.matchAst:
+    of nnkBracketExpr(ident"x", ident"y"):
+      echo "ok"
+
+  testArrayAccessOperator(x[y])
+
+  ## Parentheses
+
+  scope:
+    let ast = myquote:
+      (a + b) * c
+
+    ast.matchAst:
+    of nnkInfix(ident"*", nnkPar(nnkInfix(ident"+", ident"a", ident"b")), ident"c"):
+      echo "parentheses ok"
+
+  ## Tuple Constructors
+
+  scope:
+    let ast = myquote:
+      (1, 2, 3)
+      (a: 1, b: 2, c: 3)
+      (1,)
+      (a: 1)
+      ()
+
+    for it in ast:
+      echo it.lispRepr
+      it.matchAst:
+      of nnkTupleConstr(nnkIntLit(intVal = 1), nnkIntLit(intVal = 2), nnkIntLit(intVal = 3)):
+        echo "simple tuple ok"
+      of nnkTupleConstr(
+        nnkExprColonExpr(ident"a", nnkIntLit(intVal = 1)),
+        nnkExprColonExpr(ident"b", nnkIntLit(intVal = 2)),
+        nnkExprColonExpr(ident"c", nnkIntLit(intVal = 3))
+      ):
+        echo "named tuple ok"
+      of nnkTupleConstr(nnkIntLit(intVal = 1)):
+        echo "one tuple ok"
+      of nnkTupleConstr(nnkExprColonExpr(ident"a", nnkIntLit(intVal = 1))):
+        echo "named one tuple ok"
+      of nnkTupleConstr():
+       echo "empty tuple ok"
+
+  ## Curly braces
+
+  scope:
+
+    let ast = myquote:
+      {1, 2, 3}
+
+    ast.matchAst:
+    of nnkCurly(nnkIntLit(intVal = 1), nnkIntLit(intVal = 2), nnkIntLit(intVal = 3)):
+      echo "ok"
+
+  scope:
+
+    let ast = myquote:
+      {a: 3, b: 5}
+
+    ast.matchAst:
+    of nnkTableConstr(
+      nnkExprColonExpr(ident"a", nnkIntLit(intVal = 3)),
+      nnkExprColonExpr(ident"b", nnkIntLit(intVal = 5))
+    ):
+      echo "ok"
+
+
+  ## Brackets
+
+  scope:
+
+    let ast = myquote:
+      [1, 2, 3]
+
+    ast.matchAst:
+    of nnkBracket(nnkIntLit(intVal = 1), nnkIntLit(intVal = 2), nnkIntLit(intVal = 3)):
+      echo "ok"
+
+
+  ## Ranges
+
+  scope:
+
+    let ast = myquote:
+      1..3
+
+    ast.matchAst:
+    of nnkInfix(
+      ident"..",
+      nnkIntLit(intVal = 1),
+      nnkIntLit(intVal = 3)
+    ):
+      echo "ok"
+
+
+  ## If expression
+
+  scope:
+
+    let ast = myquote:
+      if cond1: expr1 elif cond2: expr2 else: expr3
+
+    ast.matchAst:
+    of {nnkIfExpr, nnkIfStmt}(
+      {nnkElifExpr, nnkElifBranch}(`cond1`, `expr1`),
+      {nnkElifExpr, nnkElifBranch}(`cond2`, `expr2`),
+      {nnkElseExpr, nnkElse}(`expr3`)
+    ):
+      echo "ok"
+
+  ## Documentation Comments
+
+  scope:
+
+    let ast = myquote:
+      ## This is a comment
+      ## This is part of the first comment
+      stmt1
+      ## Yet another
+
+    ast.matchAst:
+    of nnkStmtList(
+      nnkCommentStmt(),
+      `stmt1`,
+      nnkCommentStmt()
+    ):
+      echo "ok"
+    else:
+      echo "warning!"
+      echo ast.treeRepr
+      echo "TEST causes no fail, because of a regression in Nim."
+
+  scope:
+    let ast = myquote:
+      {.emit: "#include <stdio.h>".}
+
+    ast.matchAst:
+    of nnkPragma(
+      nnkExprColonExpr(
+        ident"emit",
+        nnkStrLit(strVal = "#include <stdio.h>") # the "argument"
+      )
+    ):
+      echo "ok"
+
+  scope:
+    let ast = myquote:
+      {.pragma: cdeclRename, cdecl.}
+
+    ast.matchAst:
+    of nnkPragma(
+      nnkExprColonExpr(
+        ident"pragma", # this is always first when declaring a new pragma
+        ident"cdeclRename" # the name of the pragma
+      ),
+      ident"cdecl"
+    ):
+      echo "ok"
+
+
+
+  scope:
+    let ast = myquote:
+      if cond1:
+        stmt1
+      elif cond2:
+        stmt2
+      elif cond3:
+        stmt3
+      else:
+        stmt4
+
+    ast.matchAst:
+    of nnkIfStmt(
+      nnkElifBranch(`cond1`, `stmt1`),
+      nnkElifBranch(`cond2`, `stmt2`),
+      nnkElifBranch(`cond3`, `stmt3`),
+      nnkElse(`stmt4`)
+    ):
+      echo "ok"
+
+
+
+  scope:
+    let ast = myquote:
+      x = 42
+
+    ast.matchAst:
+    of nnkAsgn(ident"x", nnkIntLit(intVal = 42)):
+      echo "ok"
+
+
+
+  scope:
+    let ast = myquote:
+      stmt1
+      stmt2
+      stmt3
+
+    ast.matchAst:
+    of nnkStmtList(`stmt1`, `stmt2`, `stmt3`):
+      assert stmt1.strVal == "stmt1"
+      assert stmt2.strVal == "stmt2"
+      assert stmt3.strVal == "stmt3"
+      echo "ok"
+
+  ## Case statement
+
+  scope:
+
+    let ast = myquote:
+      case expr1
+      of expr2, expr3..expr4:
+        stmt1
+      of expr5:
+        stmt2
+      elif cond1:
+        stmt3
+      else:
+        stmt4
+
+    ast.matchAst:
+    of nnkCaseStmt(
+      `expr1`,
+      nnkOfBranch(`expr2`, {nnkRange, nnkInfix}(_, `expr3`, `expr4`), `stmt1`),
+      nnkOfBranch(`expr5`, `stmt2`),
+      nnkElifBranch(`cond1`, `stmt3`),
+      nnkElse(`stmt4`)
+    ):
+      echo "ok"
+
+  ## While statement
+
+  scope:
+
+    let ast = myquote:
+      while expr1:
+        stmt1
+
+    ast.matchAst:
+    of nnkWhileStmt(`expr1`, `stmt1`):
+      echo "ok"
+
+
+  ## For statement
+
+  scope:
+
+    let ast = myquote:
+      for ident1, ident2 in expr1:
+        stmt1
+
+    ast.matchAst:
+    of nnkForStmt(`ident1`, `ident2`, `expr1`, `stmt1`):
+      echo "ok"
+
+
+  ## Try statement
+
+  scope:
+
+    let ast = myquote:
+      try:
+        stmt1
+      except e1, e2:
+        stmt2
+      except e3:
+        stmt3
+      except:
+        stmt4
+      finally:
+        stmt5
+
+    ast.matchAst:
+    of nnkTryStmt(
+      `stmt1`,
+      nnkExceptBranch(`e1`, `e2`, `stmt2`),
+      nnkExceptBranch(`e3`, `stmt3`),
+      nnkExceptBranch(`stmt4`),
+      nnkFinally(`stmt5`)
+    ):
+      echo "ok"
+
+
+  ## Return statement
+
+  scope:
+
+    let ast = myquote:
+      return expr1
+
+    ast.matchAst:
+    of nnkReturnStmt(`expr1`):
+      echo "ok"
+
+
+  ## Continue statement
+
+  scope:
+    let ast = myquote:
+      continue
+
+    ast.matchAst:
+    of nnkContinueStmt:
+      echo "ok"
+
+  ## Break statement
+
+  scope:
+
+    let ast = myquote:
+      break otherLocation
+
+    ast.matchAst:
+    of nnkBreakStmt(ident"otherLocation"):
+      echo "ok"
+
+  ## Block statement
+
+  scope:
+
+    template blockStatement {.dirty.} =
+      block name:
+        discard
+
+    let ast = getAst(blockStatement())
+
+    ast.matchAst:
+    of nnkBlockStmt(ident"name", nnkStmtList):
+      echo "ok"
+
+  ## Asm statement
+
+  scope:
+
+    let ast = myquote:
+      asm """some asm"""
+
+    ast.matchAst:
+    of nnkAsmStmt(
+      nnkEmpty(), # for pragmas
+      nnkTripleStrLit(strVal = "some asm"),
+    ):
+      echo "ok"
+
+  ## Import section
+
+  scope:
+
+    let ast = myquote:
+      import math
+
+    ast.matchAst:
+    of nnkImportStmt(ident"math"):
+      echo "ok"
+
+  scope:
+
+    let ast = myquote:
+      import math except pow
+
+    ast.matchAst:
+    of nnkImportExceptStmt(ident"math",ident"pow"):
+      echo "ok"
+
+  scope:
+
+    let ast = myquote:
+      import strutils as su
+
+    ast.matchAst:
+    of nnkImportStmt(
+      nnkInfix(
+        ident"as",
+        ident"strutils",
+        ident"su"
+      )
+    ):
+      echo "ok"
+
+  ## From statement
+
+  scope:
+
+    let ast = myquote:
+      from math import pow
+
+    ast.matchAst:
+    of nnkFromStmt(ident"math", ident"pow"):
+      echo "ok"
+
+  ## Export statement
+
+  scope:
+
+    let ast = myquote:
+      export unsigned
+
+    ast.matchAst:
+    of nnkExportStmt(ident"unsigned"):
+      echo "ok"
+
+  scope:
+
+    let ast = myquote:
+      export math except pow # we're going to implement our own exponentiation
+
+    ast.matchAst:
+    of nnkExportExceptStmt(ident"math",ident"pow"):
+      echo "ok"
+
+  ## Include statement
+
+  scope:
+
+    let ast = myquote:
+      include blocks
+
+    ast.matchAst:
+    of nnkIncludeStmt(ident"blocks"):
+      echo "ok"
+
+  ## Var section
+
+  scope:
+
+    let ast = myquote:
+      var a = 3
+
+    ast.matchAst:
+    of nnkVarSection(
+      nnkIdentDefs(
+        ident"a",
+        nnkEmpty(), # or nnkIdent(...) if the variable declares the type
+        nnkIntLit(intVal = 3),
+      )
+    ):
+      echo "ok"
+
+  ## Let section
+
+  scope:
+
+    let ast = myquote:
+      let a = 3
+
+    ast.matchAst:
+    of nnkLetSection(
+      nnkIdentDefs(
+        ident"a",
+        nnkEmpty(), # or nnkIdent(...) for the type
+        nnkIntLit(intVal = 3),
+      )
+    ):
+      echo "ok"
+
+  ## Const section
+
+  scope:
+
+    let ast = myquote:
+      const a = 3
+
+    ast.matchAst:
+    of nnkConstSection(
+      nnkConstDef( # not nnkConstDefs!
+        ident"a",
+        nnkEmpty(), # or nnkIdent(...) if the variable declares the type
+        nnkIntLit(intVal = 3), # required in a const declaration!
+      )
+    ):
+      echo "ok"
+
+  ## Type section
+
+  scope:
+
+    let ast = myquote:
+      type A = int
+
+    ast.matchAst:
+    of nnkTypeSection(
+      nnkTypeDef(
+        ident"A",
+        nnkEmpty(),
+        ident"int"
+      )
+    ):
+      echo "ok"
+
+  scope:
+
+    let ast = myquote:
+      type MyInt = distinct int
+
+    ast.peelOff({nnkTypeSection}).matchAst:
+    of# ...
+      nnkTypeDef(
+      ident"MyInt",
+      nnkEmpty(),
+      nnkDistinctTy(
+        ident"int"
+      )
+    ):
+      echo "ok"
+
+  scope:
+
+    let ast = myquote:
+      type A[T] = expr1
+
+    ast.matchAst:
+    of nnkTypeSection(
+      nnkTypeDef(
+        ident"A",
+        nnkGenericParams(
+          nnkIdentDefs(
+            ident"T",
+            nnkEmpty(), # if the type is declared with options, like
+                        # ``[T: SomeInteger]``, they are given here
+            nnkEmpty()
+          )
+        ),
+        `expr1`
+      )
+    ):
+      echo "ok"
+
+  scope:
+
+    let ast = myquote:
+      type IO = object of RootObj
+
+    ast.peelOff(nnkTypeSection).matchAst:
+    of nnkTypeDef(
+      ident"IO",
+      nnkEmpty(),
+      nnkObjectTy(
+        nnkEmpty(), # no pragmas here
+        nnkOfInherit(
+          ident"RootObj" # inherits from RootObj
+        ),
+        nnkEmpty()
+      )
+    ):
+      echo "ok"
+
+  scope:
+    macro testRecCase(ast: untyped): untyped =
+      ast.peelOff({nnkStmtList, nnkTypeSection}).matchAst:
+      of nnkTypeDef(
+        nnkPragmaExpr(
+          ident"Obj",
+          nnkPragma(ident"inheritable")
+        ),
+        nnkGenericParams(
+        nnkIdentDefs(
+          ident"T",
+          nnkEmpty(),
+          nnkEmpty())
+        ),
+        nnkObjectTy(
+        nnkEmpty(),
+        nnkEmpty(),
+        nnkRecList( # list of object parameters
+          nnkIdentDefs(
+            ident"name",
+            ident"string",
+            nnkEmpty()
+          ),
+          nnkRecCase( # case statement within object (not nnkCaseStmt)
+            nnkIdentDefs(
+              ident"isFat",
+              ident"bool",
+              nnkEmpty()
+            ),
+            nnkOfBranch(
+              ident"true",
+              nnkRecList( # again, a list of object parameters
+                nnkIdentDefs(
+                  ident"m",
+                  nnkBracketExpr(
+                    ident"array",
+                    nnkIntLit(intVal = 100000),
+                    ident"T"
+                  ),
+                  nnkEmpty()
+                )
+              )
+            ),
+            nnkOfBranch(
+              ident"false",
+              nnkRecList(
+                nnkIdentDefs(
+                  ident"m",
+                  nnkBracketExpr(
+                    ident"array",
+                    nnkIntLit(intVal = 10),
+                    ident"T"
+                  ),
+                  nnkEmpty()
+                  )
+                )
+              )
+            )
+          )
+        )
+      ):
+        echo "ok"
+
+    testRecCase:
+      type Obj[T] {.inheritable.} = object
+        name: string
+        case isFat: bool
+        of true:
+          m: array[100_000, T]
+        of false:
+          m: array[10, T]
+
+  scope:
+
+    let ast = myquote:
+      type X = enum
+        First
+
+    ast.peelOff({nnkStmtList, nnkTypeSection})[2].matchAst:
+    of nnkEnumTy(
+      nnkEmpty(),
+      ident"First" # you need at least one nnkIdent or the compiler complains
+    ):
+      echo "ok"
+
+  scope:
+
+    let ast = myquote:
+      type Con = concept x,y,z
+        (x & y & z) is string
+
+    ast.peelOff({nnkStmtList, nnkTypeSection}).matchAst:
+    of nnkTypeDef(_, _, nnkTypeClassTy(nnkArgList, _, _, nnkStmtList)):
+      # note this isn't nnkConceptTy!
+      echo "ok"
+
+
+  scope:
+
+    let astX = myquote:
+      type
+        A[T: static[int]] = object
+
+    let ast = astX.peelOff({nnkStmtList, nnkTypeSection})
+
+    ast.matchAst(err):  # this is a sub ast for this a findAst or something like that is useful
+    of nnkTypeDef(_, nnkGenericParams( nnkIdentDefs( ident"T", nnkCall( ident"[]", ident"static", _ ), _ )), _):
+      echo "ok"
+    else:
+      echo "foobar"
+      echo ast.treeRepr
+
+
+  scope:
+    let ast = myquote:
+      type MyProc[T] = proc(x: T)
+
+    ast.peelOff({nnkStmtList, nnkTypeSection}).matchAst(err):
+    of nnkTypeDef(
+      ident"MyProc",
+      nnkGenericParams, # here, not with the proc
+      nnkProcTy( # behaves like a procedure declaration from here on
+        nnkFormalParams, _
+      )
+    ):
+      echo "ok"
+
+  ## Mixin statement
+
+  macro testMixinStatement(ast: untyped): untyped =
+    ast.peelOff(nnkStmtList).matchAst:
+    of nnkMixinStmt(ident"x"):
+      echo "ok"
+
+  testMixinStatement:
+    mixin x
+
+  ## Bind statement
+
+
+  macro testBindStmt(ast: untyped): untyped =
+    ast[0].matchAst:
+    of `node` @ nnkBindStmt(ident"x"):
+      echo "ok"
+
+  testBindStmt:
+    bind x
+
+  ## Procedure declaration
+
+  macro testProcedureDeclaration(ast: untyped): untyped =
+    # NOTE this is wrong in astdef
+
+    ast.peelOff(nnkStmtList).matchAst:
+    of nnkProcDef(
+      nnkPostfix(ident"*", ident"hello"), # the exported proc name
+      nnkEmpty, # patterns for term rewriting in templates and macros (not procs)
+      nnkGenericParams( # generic type parameters, like with type declaration
+        nnkIdentDefs(
+          ident"T",
+          ident"SomeInteger", _
+        )
+      ),
+      nnkFormalParams(
+        ident"int", # the first FormalParam is the return type. nnkEmpty if there is none
+        nnkIdentDefs(
+          ident"x",
+          ident"int", # type type (required for procs, not for templates)
+          nnkIntLit(intVal = 3) # a default value
+        ),
+        nnkIdentDefs(
+          ident"y",
+          ident"float32",
+          nnkEmpty
+        )
+      ),
+      nnkPragma(ident"inline"),
+      nnkEmpty, # reserved slot for future use
+      `meat` @ nnkStmtList # the meat of the proc
+    ):
+      echo "ok got meat: ", meat.lispRepr
+
+  testProcedureDeclaration:
+    proc hello*[T: SomeInteger](x: int = 3, y: float32): int {.inline.} = discard
+
+  scope:
+    var ast = myquote:
+      proc foobar(a, b: int): void
+
+    ast = ast[3]
+
+    ast.matchAst:  # sub expression
+    of nnkFormalParams(
+      _, # return would be here
+      nnkIdentDefs(
+        ident"a", # the first parameter
+        ident"b", # directly to the second parameter
+        ident"int", # their shared type identifier
+        nnkEmpty, # default value would go here
+      )
+    ):
+      echo "ok"
+
+  scope:
+
+    let ast = myquote:
+      proc hello(): var int
+
+    ast[3].matchAst: # subAst
+    of nnkFormalParams(
+      nnkVarTy(
+        ident"int"
+      )
+    ):
+      echo "ok"
+
+  ## Iterator declaration
+
+  scope:
+
+    let ast = myquote:
+      iterator nonsense[T](x: seq[T]): float {.closure.} =
+        discard
+
+    ast.matchAst:
+    of nnkIteratorDef(ident"nonsense", nnkEmpty, _, _, _, _, _):
+      echo "ok"
+
+  ## Converter declaration
+
+  scope:
+
+    let ast = myquote:
+      converter toBool(x: float): bool
+
+    ast.matchAst:
+    of nnkConverterDef(ident"toBool",_,_,_,_,_,_):
+      echo "ok"
+
+  ## Template declaration
+
+  scope:
+    let ast = myquote:
+      template optOpt{expr1}(a: int): int
+
+    ast.matchAst:
+    of nnkTemplateDef(ident"optOpt", nnkStmtList(`expr1`), _, _, _, _, _):
+      echo "ok"
diff --git a/tests/async/nim.cfg b/tests/async/nim.cfg
new file mode 100644
index 000000000..be50f572c
--- /dev/null
+++ b/tests/async/nim.cfg
@@ -0,0 +1 @@
+--experimental:strictEffects
diff --git a/tests/async/t11558.nim b/tests/async/t11558.nim
new file mode 100644
index 000000000..99961e7b6
--- /dev/null
+++ b/tests/async/t11558.nim
@@ -0,0 +1,13 @@
+discard """
+output: "Hello\n9"
+"""
+import std/asyncdispatch
+
+proc foo(): Future[string] {.async.} =
+  "Hello"
+
+proc bar(): Future[int] {.async.} =
+  result = 9
+
+echo waitFor foo()
+echo waitFor bar()
diff --git a/tests/async/t12221.nim b/tests/async/t12221.nim
new file mode 100644
index 000000000..e8bd9c11a
--- /dev/null
+++ b/tests/async/t12221.nim
@@ -0,0 +1,40 @@
+import asyncdispatch, os, times
+
+proc doubleSleep(hardSleep: int) {.async.} =
+  await sleepAsync(50)
+  sleep(hardSleep)
+
+template assertTime(target, timeTook: float): untyped {.dirty.} =
+  doAssert(timeTook*1000 > target - 1000, "Took too short, should've taken " &
+    $target & "ms, but took " & $(timeTook*1000) & "ms")
+  doAssert(timeTook*1000 < target + 1000, "Took too long, should've taken " &
+    $target & "ms, but took " & $(timeTook*1000) & "ms")
+
+var
+  start: float
+  fut: Future[void]
+
+# NOTE: this uses poll(3000) to limit timing error potential.
+start = epochTime()
+fut = sleepAsync(40) and sleepAsync(100) and doubleSleep(20)
+while not fut.finished:
+  poll(1000)
+assertTime(150, epochTime() - start)
+
+start = epochTime()
+fut = sleepAsync(40) and sleepAsync(100) and doubleSleep(50)
+while not fut.finished:
+  poll(1000)
+assertTime(200, epochTime() - start)
+
+start = epochTime()
+fut = sleepAsync(40) and sleepAsync(100) and doubleSleep(20) and sleepAsync(200)
+while not fut.finished:
+  poll(1000)
+assertTime(300, epochTime() - start)
+
+start = epochTime()
+fut = (sleepAsync(40) and sleepAsync(100) and doubleSleep(20)) or sleepAsync(300)
+while not fut.finished:
+  poll(1000)
+assertTime(150, epochTime() - start)
diff --git a/tests/async/t13889.nim b/tests/async/t13889.nim
new file mode 100644
index 000000000..fe75fe38a
--- /dev/null
+++ b/tests/async/t13889.nim
@@ -0,0 +1,27 @@
+discard """
+  output: '''
+believer Foo is saved:true
+believer Bar is saved:true
+believer Baz is saved:true
+'''
+"""
+
+import asyncdispatch
+
+var
+  promise = newFuture[bool]()
+
+proc believers(name: string) {.async.} =
+  let v = await promise
+  echo "believer " & name & " is saved:" & $v
+
+asyncCheck believers("Foo")
+asyncCheck believers("Bar")
+asyncCheck believers("Baz")
+
+proc savior() {.async.} =
+  await sleepAsync(50)
+  complete(promise, true)
+  await sleepAsync(50) # give enough time to see who was saved
+
+waitFor(savior())
diff --git a/tests/async/t14820.nim b/tests/async/t14820.nim
new file mode 100644
index 000000000..359884468
--- /dev/null
+++ b/tests/async/t14820.nim
@@ -0,0 +1,25 @@
+discard """
+output: '''
+iteration: 1
+iteration: 2
+iteration: 3
+iteration: 4
+async done
+iteration: 5
+'''
+"""
+
+import asyncdispatch, times
+
+var done = false
+proc somethingAsync() {.async.} =
+  yield sleepAsync 5000
+  echo "async done"
+  done = true
+  
+asyncCheck somethingAsync()
+var count = 0
+while not done:
+  count += 1
+  drain 1000
+  echo "iteration: ", count 
diff --git a/tests/async/t15148.nim b/tests/async/t15148.nim
new file mode 100644
index 000000000..ba14c1157
--- /dev/null
+++ b/tests/async/t15148.nim
@@ -0,0 +1,12 @@
+import asyncdispatch, asyncfile, os
+
+const Filename = "t15148.txt"
+
+proc saveEmpty() {.async.} =
+  let
+    text = ""
+    file = openAsync(Filename, fmWrite)
+  await file.write(text)
+  file.close()
+
+waitFor saveEmpty()
diff --git a/tests/async/t15804.nim b/tests/async/t15804.nim
new file mode 100644
index 000000000..8de012196
--- /dev/null
+++ b/tests/async/t15804.nim
@@ -0,0 +1,15 @@
+import asyncdispatch
+
+type
+  Foo*[E] = ref object 
+    op: proc(): Future[bool] {.gcsafe.}
+
+proc newFoo*[E](): Foo[E] =
+  result = Foo[E]()
+  result.op = proc(): Future[bool] {.gcsafe,async.} =
+    await sleepAsync(100)
+    result = false
+
+when isMainModule:
+  let f = newFoo[int]()
+  echo waitFor f.op()
diff --git a/tests/async/t17045.nim b/tests/async/t17045.nim
new file mode 100644
index 000000000..2b5acf48a
--- /dev/null
+++ b/tests/async/t17045.nim
@@ -0,0 +1,28 @@
+discard """
+  targets: "c cpp"
+  matrix: "--mm:refc; --mm:arc"
+"""
+
+type Future = ref object
+
+iterator paths: string = 
+  # without "when nimvm" everything works
+  when nimvm:
+    yield "test.md"
+  else:
+    yield "test.md"
+
+template await(f: Future): string =
+  # need this yield, also the template has to return something
+  yield f
+  "hello world"
+
+proc generatePostContextsAsync() =
+  iterator generatePostContextsAsyncIter(): Future {.closure.} =
+    for filePath in paths():
+      var temp = await Future()
+
+  # need this line
+  var nameIterVar = generatePostContextsAsyncIter
+
+generatePostContextsAsync()
\ No newline at end of file
diff --git a/tests/async/t20111.nim b/tests/async/t20111.nim
new file mode 100644
index 000000000..0aaa7d886
--- /dev/null
+++ b/tests/async/t20111.nim
@@ -0,0 +1,19 @@
+discard """
+  action: "run"
+"""
+import asyncdispatch
+type
+    Sync = object
+    Async = object
+    SyncRes = (Sync, string)
+    AsyncRes = (Async, string)
+
+proc foo(val: Sync | Async): Future[(Async, string) | (Sync, string)] {.multisync.} =
+    return (val, "hello")
+
+let
+  myAsync = Async()
+  mySync = Sync()
+
+doAssert typeof(waitFor foo(myAsync)) is AsyncRes
+doAssert typeof(foo(mySync)) is SyncRes
diff --git a/tests/async/t21447.nim b/tests/async/t21447.nim
new file mode 100644
index 000000000..e4f7ae31f
--- /dev/null
+++ b/tests/async/t21447.nim
@@ -0,0 +1,6 @@
+discard """
+  action: "compile"
+  cmd: "nim c -d:release -d:futureLogging $file"
+"""
+
+import std/asyncdispatch
diff --git a/tests/async/t21893.nim b/tests/async/t21893.nim
new file mode 100644
index 000000000..658cb02eb
--- /dev/null
+++ b/tests/async/t21893.nim
@@ -0,0 +1,13 @@
+discard """
+output: "@[97]\ntrue"
+"""
+
+import asyncdispatch
+
+proc test(): Future[bool] {.async.} =
+  const S4 = @[byte('a')]
+  echo S4
+  return true
+
+echo waitFor test()
+
diff --git a/tests/async/t22210.nim b/tests/async/t22210.nim
new file mode 100644
index 000000000..fcf939472
--- /dev/null
+++ b/tests/async/t22210.nim
@@ -0,0 +1,41 @@
+discard """
+output: '''
+stage 1
+stage 2
+stage 3
+(status: 200, data: "SOMEDATA")
+'''
+"""
+
+import std/asyncdispatch
+
+
+# bug #22210
+type
+  ClientResponse = object
+    status*: int
+    data*: string
+
+proc subFoo1(): Future[int] {.async.} =
+  await sleepAsync(100)
+  return 200
+
+proc subFoo2(): Future[string] {.async.} =
+  await sleepAsync(100)
+  return "SOMEDATA"
+
+proc testFoo(): Future[ClientResponse] {.async.} =
+  try:
+    let status = await subFoo1()
+    doAssert(status == 200)
+    let data = await subFoo2()
+    return ClientResponse(status: status, data: data)
+  finally:
+    echo "stage 1"
+    await sleepAsync(100)
+    echo "stage 2"
+    await sleepAsync(200)
+    echo "stage 3"
+
+when isMainModule:
+  echo waitFor testFoo()
\ No newline at end of file
diff --git a/tests/async/t22210_2.nim b/tests/async/t22210_2.nim
new file mode 100644
index 000000000..9db664a32
--- /dev/null
+++ b/tests/async/t22210_2.nim
@@ -0,0 +1,73 @@
+import std/asyncdispatch
+
+
+# bug #22210
+type
+  ClientResponse = object
+    status*: int
+    data*: string
+
+proc subFoo1(): Future[int] {.async.} =
+  await sleepAsync(100)
+  return 200
+
+proc subFoo2(): Future[string] {.async.} =
+  await sleepAsync(100)
+  return "SOMEDATA"
+
+
+proc testFoo2(): Future[ClientResponse] {.async.} =
+  var flag = 0
+  try:
+    let status = await subFoo1()
+    doAssert(status == 200)
+    let data = await subFoo2()
+    result = ClientResponse(status: status, data: data)
+  finally:
+    inc flag
+    await sleepAsync(100)
+    inc flag
+    await sleepAsync(200)
+    inc flag
+  doAssert flag == 3
+
+discard waitFor testFoo2()
+
+proc testFoo3(): Future[ClientResponse] {.async.} =
+  var flag = 0
+  try:
+    let status = await subFoo1()
+    doAssert(status == 200)
+    let data = await subFoo2()
+    if false:
+      return ClientResponse(status: status, data: data)
+  finally:
+    inc flag
+    await sleepAsync(100)
+    inc flag
+    await sleepAsync(200)
+    inc flag
+  doAssert flag == 3
+
+discard waitFor testFoo3()
+
+
+proc testFoo4(): Future[ClientResponse] {.async.} =
+  var flag = 0
+  try:
+    let status = await subFoo1()
+    doAssert(status == 200)
+    let data = await subFoo2()
+    if status == 200:
+      return ClientResponse(status: status, data: data)
+    else:
+      return ClientResponse()
+  finally:
+    inc flag
+    await sleepAsync(100)
+    inc flag
+    await sleepAsync(200)
+    inc flag
+  doAssert flag == 3
+
+discard waitFor testFoo4()
diff --git a/tests/async/t3075.nim b/tests/async/t3075.nim
new file mode 100644
index 000000000..d26aa0a36
--- /dev/null
+++ b/tests/async/t3075.nim
@@ -0,0 +1,29 @@
+import asyncnet, asyncdispatch, strtabs
+
+type
+  WebSocketCallback = proc (client: WebSocket, message: WebSocketMessage) {.closure, gcsafe.}
+  WebSocketRecvClosure = proc (ws: WebSocket): Future[string] {.gcsafe.}
+
+  WebSocketMessage = ref object
+    msg: string
+
+  WebSocket = ref object
+    socket:     AsyncSocket
+    header:     StringTableRef
+    onOpen:     WebSocketCallback
+    onMessage:  WebSocketCallback
+    onClose:    WebSocketCallback
+
+proc recv(ws: WebSocket, p: WebSocketRecvClosure): Future[string] {.async.}=
+  if not ws.socket.isClosed():
+    result = await ws.p()
+    if result == "":
+      ws.socket.close()
+      if ws.onClose != nil:
+        ws.onClose(ws, nil)
+  return result
+
+proc reсvSize(ws: WebSocket, size: int): Future[string] {.async.} =
+  proc recvSizeClosure(ws: WebSocket): Future[string] {.async.} =
+    return await ws.socket.recv(size)
+  return await ws.recv(recvSizeClosure)
diff --git a/tests/async/t6100.nim b/tests/async/t6100.nim
deleted file mode 100644
index b4dc0f146..000000000
--- a/tests/async/t6100.nim
+++ /dev/null
@@ -1,15 +0,0 @@
-discard """
-  file: "t6100.nim"
-  exitcode: 0
-  output: "10000000"
-"""
-import asyncdispatch
-
-let done = newFuture[int]()
-done.complete(1)
-
-proc asyncSum: Future[int] {.async.} =
-  for _ in 1..10_000_000:
-    result += await done
-
-echo waitFor asyncSum()
\ No newline at end of file
diff --git a/tests/async/t6846.nim b/tests/async/t6846.nim
new file mode 100644
index 000000000..7fe38f3b3
--- /dev/null
+++ b/tests/async/t6846.nim
@@ -0,0 +1,15 @@
+discard """
+  exitcode: 0
+  output: "hello world"
+  disabled: windows
+"""
+
+import asyncdispatch
+import asyncfile
+
+var asyncStdout = 1.AsyncFD.newAsyncFile()
+proc doStuff: Future[void] {.async.} =
+  await asyncStdout.write "hello world\n"
+
+let fut = doStuff()
+doAssert fut.finished, "Poll is needed unnecessarily. See #6846."
diff --git a/tests/async/t7192.nim b/tests/async/t7192.nim
new file mode 100644
index 000000000..9ac0e07c0
--- /dev/null
+++ b/tests/async/t7192.nim
@@ -0,0 +1,14 @@
+discard """
+output: '''
+testCallback()
+'''
+"""
+
+import asyncdispatch
+
+proc testCallback() =
+  echo "testCallback()"
+
+when true:
+  callSoon(testCallback)
+  poll()
diff --git a/tests/async/t7758.nim b/tests/async/t7758.nim
new file mode 100644
index 000000000..fe6d32ad3
--- /dev/null
+++ b/tests/async/t7758.nim
@@ -0,0 +1,21 @@
+import asyncdispatch
+import std/unittest
+
+proc task() {.async.} =
+  const tSleep = 40
+  await sleepAsync(tSleep)
+
+proc main() =
+  var counter = 0
+  var f = task()
+  while not f.finished:
+    inc(counter)
+    poll(10)
+
+  const slack = 1
+    # because there is overhead in `async` + `sleepAsync`
+    # as can be seen by increasing `tSleep` from 40 to 49, which increases the number
+    # of failures.
+  check counter <= 4 + slack
+
+for i in 0 .. 10: main()
diff --git a/tests/async/t7985.nim b/tests/async/t7985.nim
deleted file mode 100644
index 0365499d3..000000000
--- a/tests/async/t7985.nim
+++ /dev/null
@@ -1,19 +0,0 @@
-discard """
-  file: "t7985.nim"
-  exitcode: 0
-  output: "(value: 1)"
-"""
-import json, asyncdispatch
-
-proc getData(): Future[JsonNode] {.async.} =
-  result = %*{"value": 1}
-
-type
-  MyData = object
-    value: BiggestInt
-
-proc main() {.async.} =
-  let data = to(await(getData()), MyData)
-  echo data
-
-waitFor(main())
diff --git a/tests/async/t8982.nim b/tests/async/t8982.nim
new file mode 100644
index 000000000..5face7edf
--- /dev/null
+++ b/tests/async/t8982.nim
@@ -0,0 +1,33 @@
+discard """
+output: '''
+timeout
+runForever should throw ValueError, this is expected
+'''
+"""
+
+
+import asyncdispatch
+
+proc failingAwaitable(p: int) {.async.} =
+  await sleepAsync(500)
+  if p > 0:
+    raise newException(Exception, "my exception")
+
+proc main() {.async.} =
+  let fut = failingAwaitable(1)
+  try:
+    await fut or sleepAsync(100)
+    if fut.finished:
+      echo "finished"
+    else:
+      echo "timeout"
+  except:
+    echo "failed"
+
+
+# Previously this would raise "An attempt was made to complete a Future more than once."
+try:
+  asyncCheck main()
+  runForever()
+except ValueError:
+  echo "runForever should throw ValueError, this is expected"
diff --git a/tests/async/t9201.nim b/tests/async/t9201.nim
new file mode 100644
index 000000000..5aaba7063
--- /dev/null
+++ b/tests/async/t9201.nim
@@ -0,0 +1,14 @@
+discard """
+  exitcode: 0
+"""
+
+# Derived from issue #9201
+import asyncdispatch, macros
+
+macro newAsyncProc(name: untyped): untyped =
+  expectKind name, nnkStrLit
+  let pName = genSym(nskProc, name.strVal)
+  result = getAst async quote do:
+    proc `pName`() = discard
+
+newAsyncProc("hello")
diff --git a/tests/async/tacceptcloserace.nim b/tests/async/tacceptcloserace.nim
index cbb5b5098..fee6537d2 100644
--- a/tests/async/tacceptcloserace.nim
+++ b/tests/async/tacceptcloserace.nim
@@ -8,7 +8,7 @@ import asyncdispatch, net, os, nativesockets
 # bug: https://github.com/nim-lang/Nim/issues/5279
 
 proc setupServerSocket(hostname: string, port: Port): AsyncFD =
-  let fd = newNativeSocket()
+  let fd = createNativeSocket()
   if fd == osInvalidSocket:
     raiseOSError(osLastError())
   setSockOptInt(fd, SOL_SOCKET, SO_REUSEADDR, 1)
@@ -30,7 +30,7 @@ for i in 0..100:
     if not fut.failed:
       fut.read().closeSocket()
 
-  var fd = newAsyncNativeSocket()
+  var fd = createAsyncNativeSocket()
   waitFor fd.connect("localhost", port)
   serverFd.closeSocket()
   fd.closeSocket()
diff --git a/tests/async/tasyncRecvLine.nim b/tests/async/tasyncRecvLine.nim
index 679831b27..a13a171c3 100644
--- a/tests/async/tasyncRecvLine.nim
+++ b/tests/async/tasyncRecvLine.nim
@@ -1,6 +1,5 @@
 discard """
-  file: "tasyncRecvLine.nim"
-  output: '''
+output: '''
 Hello World
 Hello World
 '''
diff --git a/tests/async/tasync_gcsafe.nim b/tests/async/tasync_gcsafe.nim
index 89df6456a..bc0eb4271 100644
--- a/tests/async/tasync_gcsafe.nim
+++ b/tests/async/tasync_gcsafe.nim
@@ -7,7 +7,7 @@ discard """
 '''
 """
 
-assert compileOption("threads"), "this test will not do anything useful without --threads:on"
+doAssert compileOption("threads"), "this test will not do anything useful without --threads:on"
 
 import asyncdispatch
 
diff --git a/tests/async/tasync_gcunsafe.nim b/tests/async/tasync_gcunsafe.nim
index f4e2cdcf2..f3e6bc691 100644
--- a/tests/async/tasync_gcunsafe.nim
+++ b/tests/async/tasync_gcunsafe.nim
@@ -1,10 +1,10 @@
 discard """
+  errormsg: "'anotherGCSafeAsyncProc (Async)' is not GC-safe as it calls 'asyncGCUnsafeProc'"
   cmd: "nim c --threads:on $file"
   file: "asyncmacro.nim"
-  errormsg: "'anotherGCSafeAsyncProcIter' is not GC-safe as it calls 'asyncGCUnsafeProc'"
 """
 
-assert compileOption("threads"), "this test will not do anything useful without --threads:on"
+doAssert compileOption("threads"), "this test will not do anything useful without --threads:on"
 
 import asyncdispatch
 
diff --git a/tests/async/tasync_misc.nim b/tests/async/tasync_misc.nim
new file mode 100644
index 000000000..ec1418e8c
--- /dev/null
+++ b/tests/async/tasync_misc.nim
@@ -0,0 +1,83 @@
+import json, asyncdispatch
+block: #6100
+  let done = newFuture[int]()
+  done.complete(1)
+
+  proc asyncSum: Future[int] {.async.} =
+    for _ in 1..1_000_000:
+      result += await done
+
+  let res = waitFor asyncSum()
+  doAssert(res == 1_000_000)
+
+block: #7985
+  proc getData(): Future[JsonNode] {.async.} =
+    result = %*{"value": 1}
+
+  type
+    MyData = object
+      value: BiggestInt
+
+  proc main() {.async.} =
+    let data = to(await(getData()), MyData)
+    doAssert($data == "(value: 1)")
+
+  waitFor(main())
+
+block: #8399
+  proc bar(): Future[string] {.async.} = discard
+
+  proc foo(line: string) {.async.} =
+    var res =
+      case line[0]
+      of '+', '-': @[]
+      of '$': (let x = await bar(); @[""])
+      else: @[]
+
+    doAssert(res == @[""])
+
+  waitFor foo("$asd")
+
+block: # nkCheckedFieldExpr
+  proc bar(): Future[JsonNode] {.async.} =
+    return newJInt(5)
+
+  proc foo() {.async.} =
+    let n = 10 + (await bar()).num
+    doAssert(n == 15)
+
+  waitFor foo()
+
+block: # 12743
+
+  template templ = await sleepAsync 0
+
+  proc prc {.async.} = templ
+
+  waitFor prc()
+
+block: # issue #13899
+  proc someConnect() {.async.} =
+    await sleepAsync(1)
+  proc someClose() {.async.} =
+    await sleepAsync(2)
+  proc testFooFails(): Future[bool] {.async.} =
+    await someConnect()
+    defer:
+      await someClose()
+      result = true
+  proc testFooSucceed(): Future[bool] {.async.} =
+    try:
+      await someConnect()
+    finally:
+      await someClose()
+      result = true
+  doAssert waitFor testFooSucceed()
+  doAssert waitFor testFooFails()
+
+block: # issue #9313
+  doAssert compiles(block:
+    proc a() {.async.} =
+      echo "Hi"
+      quit(0)
+  )
diff --git a/tests/async/tasync_noasync.nim b/tests/async/tasync_noasync.nim
new file mode 100644
index 000000000..0927148bf
--- /dev/null
+++ b/tests/async/tasync_noasync.nim
@@ -0,0 +1,44 @@
+discard """
+  cmd: "nim check --hints:off --warnings:off $file"
+  action: "reject"
+  nimout: '''
+tasync_noasync.nim(21, 10) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead
+tasync_noasync.nim(25, 12) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead
+tasync_noasync.nim(28, 10) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead
+tasync_noasync.nim(31, 10) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead
+tasync_noasync.nim(35, 10) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead
+tasync_noasync.nim(38, 10) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead
+tasync_noasync.nim(40, 8) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead
+'''
+"""
+import async
+
+proc a {.async.} =
+  discard
+
+# Bad await usage
+proc nonAsyncProc =
+  await a()
+
+proc nestedNonAsyncProc {.async.} =
+  proc nested =
+    await a()
+
+iterator customIterator: int =
+  await a()
+
+macro awaitInMacro =
+  await a()
+
+type DummyRef = ref object of RootObj
+method awaitInMethod(_: DummyRef) {.base.} =
+  await a()
+
+proc improperMultisync {.multisync.} =
+  await a()
+
+await a()
+
+# if we overload a fallback handler to get
+# await only available within {.async.}
+# we would need `{.dirty.}` templates for await
diff --git a/tests/async/tasync_nofuture.nim b/tests/async/tasync_nofuture.nim
new file mode 100644
index 000000000..16155601a
--- /dev/null
+++ b/tests/async/tasync_nofuture.nim
@@ -0,0 +1,11 @@
+discard """
+  errormsg: "await expects Future[T], got int"
+  cmd: "nim c $file"
+  file: "asyncmacro.nim"
+"""
+import async
+
+proc a {.async.} =
+  await 0
+
+waitFor a()
diff --git a/tests/async/tasync_traceback.nim b/tests/async/tasync_traceback.nim
index b6c6a916b..98f71b192 100644
--- a/tests/async/tasync_traceback.nim
+++ b/tests/async/tasync_traceback.nim
@@ -1,6 +1,5 @@
 discard """
   exitcode: 0
-  disabled: "windows"
   output: "Matched"
 """
 import asyncdispatch, strutils
@@ -68,75 +67,56 @@ import re
 const expected = """
 b failure
 Async traceback:
-  tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback
-  asyncmacro\.nim\(\d+?\)\s+?a
-  asyncmacro\.nim\(\d+?\)\s+?a_continue
-    ## Resumes an async procedure
-  tasync_traceback\.nim\(\d+?\)\s+?aIter
-  asyncmacro\.nim\(\d+?\)\s+?b
-  asyncmacro\.nim\(\d+?\)\s+?b_continue
-    ## Resumes an async procedure
-  tasync_traceback\.nim\(\d+?\)\s+?bIter
-  #\[
-    tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback
-    asyncmacro\.nim\(\d+?\)\s+?a
-    asyncmacro\.nim\(\d+?\)\s+?a_continue
-      ## Resumes an async procedure
-    tasync_traceback\.nim\(\d+?\)\s+?aIter
-    asyncfutures\.nim\(\d+?\)\s+?read
-  \]#
+  tasync_traceback\.nim\(\d+?\) tasync_traceback
+  tasync_traceback\.nim\(\d+?\) a \(Async\)
+  tasync_traceback\.nim\(\d+?\) b \(Async\)
 Exception message: b failure
-Exception type:
+
 
 bar failure
 Async traceback:
-  tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback
-  asyncdispatch\.nim\(\d+?\)\s+?waitFor
-  asyncdispatch\.nim\(\d+?\)\s+?poll
+  tasync_traceback\.nim\(\d+?\) tasync_traceback
+  asyncdispatch\.nim\(\d+?\) waitFor
+  asyncdispatch\.nim\(\d+?\) poll
     ## Processes asynchronous completion events
-  asyncdispatch\.nim\(\d+?\)\s+?runOnce
-  asyncdispatch\.nim\(\d+?\)\s+?processPendingCallbacks
+  asyncdispatch\.nim\(\d+?\) runOnce
+  asyncdispatch\.nim\(\d+?\) processPendingCallbacks
     ## Executes pending callbacks
-  asyncmacro\.nim\(\d+?\)\s+?bar_continue
-    ## Resumes an async procedure
-  tasync_traceback\.nim\(\d+?\)\s+?barIter
-  #\[
-    tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback
-    asyncdispatch\.nim\(\d+?\)\s+?waitFor
-    asyncdispatch\.nim\(\d+?\)\s+?poll
-      ## Processes asynchronous completion events
-    asyncdispatch\.nim\(\d+?\)\s+?runOnce
-    asyncdispatch\.nim\(\d+?\)\s+?processPendingCallbacks
-      ## Executes pending callbacks
-    asyncmacro\.nim\(\d+?\)\s+?foo_continue
-      ## Resumes an async procedure
-    tasync_traceback\.nim\(\d+?\)\s+?fooIter
-    asyncfutures\.nim\(\d+?\)\s+?read
-  \]#
+  tasync_traceback\.nim\(\d+?\) bar \(Async\)
 Exception message: bar failure
-Exception type:
+
 """
 
+# TODO: is asyncmacro good enough location for fooIter traceback/debugging? just put the callsite info for all?
+
 let resLines = splitLines(result.strip)
 let expLines = splitLines(expected.strip)
 
-if resLines.len != expLines.len:
-  echo("Not matched! Wrong number of lines!")
-  echo()
-  echo(result)
-  quit(QuitFailure)
-
-var ok = true
-for i in 0 ..< resLines.len:
-  if not resLines[i].match(re(expLines[i])):
-    echo "Not matched! Line ", i + 1
-    echo "Expected:"
-    echo expLines[i]
-    echo "Actual:"
-    echo resLines[i]
-    ok = false
-
-if ok:
-  echo("Matched")
+when not defined(cpp): # todo fixme
+  if resLines.len != expLines.len:
+    echo("Not matched! Wrong number of lines!")
+    echo expLines.len
+    echo resLines.len
+    echo("Expected: -----------")
+    echo expected
+    echo("Gotten: -------------")
+    echo result
+    echo("---------------------")
+    quit(QuitFailure)
+
+  var ok = true
+  for i in 0 ..< resLines.len:
+    if not resLines[i].match(re(expLines[i])):
+      echo "Not matched! Line ", i + 1
+      echo "Expected:"
+      echo expLines[i]
+      echo "Actual:"
+      echo resLines[i]
+      ok = false
+
+  if ok:
+    echo("Matched")
+  else:
+    quit(QuitFailure)
 else:
-  quit(QuitFailure)
+  echo("Matched")
diff --git a/tests/async/tasyncall.nim b/tests/async/tasyncall.nim
index 775dd0c6f..3c318dbf7 100644
--- a/tests/async/tasyncall.nim
+++ b/tests/async/tasyncall.nim
@@ -1,8 +1,7 @@
 discard """
-  file: "tasyncall.nim"
   exitcode: 0
 """
-import times, sequtils, unittest
+import times, sequtils
 import asyncdispatch
 
 const
diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim
index 74933f063..e86542b2d 100644
--- a/tests/async/tasyncawait.nim
+++ b/tests/async/tasyncawait.nim
@@ -1,65 +1,56 @@
-discard """
-  file: "tasyncawait.nim"
-  output: "5000"
-"""
-import asyncdispatch, nativesockets, net, strutils, os
-
+import asyncdispatch, asyncnet, nativesockets, net, strutils
+from stdtest/netutils import bindAvailablePort
 var msgCount = 0
 
 const
-  swarmSize = 50
-  messagesToSend = 100
+  swarmSize = 40
+  messagesToSend = 50
 
 var clientCount = 0
 
 proc sendMessages(client: AsyncFD) {.async.} =
-  for i in 0 .. <messagesToSend:
+  for i in 0 ..< messagesToSend:
     await send(client, "Message " & $i & "\c\L")
 
 proc launchSwarm(port: Port) {.async.} =
-  for i in 0 .. <swarmSize:
-    var sock = newAsyncNativeSocket()
+  for i in 0 ..< swarmSize:
+    var sock = createAsyncNativeSocket()
 
     await connect(sock, "localhost", port)
     await sendMessages(sock)
     closeSocket(sock)
 
 proc readMessages(client: AsyncFD) {.async.} =
+  # wrapping the AsyncFd into a AsyncSocket object
+  var sockObj = newAsyncSocket(client)
+  var (ipaddr, port) = sockObj.getPeerAddr()
+  doAssert ipaddr == "127.0.0.1"
+  (ipaddr, port) = sockObj.getLocalAddr()
+  doAssert ipaddr == "127.0.0.1"
   while true:
-    var line = await recvLine(client)
+    var line = await recvLine(sockObj)
     if line == "":
       closeSocket(client)
       clientCount.inc
       break
     else:
-      if line.startswith("Message "):
+      if line.startsWith("Message "):
         msgCount.inc
       else:
         doAssert false
 
-proc createServer(port: Port) {.async.} =
-  var server = newAsyncNativeSocket()
-  block:
-    var name: Sockaddr_in
-    when defined(windows):
-      name.sin_family = toInt(AF_INET).int16
-    else:
-      name.sin_family = toInt(AF_INET)
-    name.sin_port = htons(uint16(port))
-    name.sin_addr.s_addr = htonl(INADDR_ANY)
-    if bindAddr(server.SocketHandle, cast[ptr SockAddr](addr(name)),
-                sizeof(name).Socklen) < 0'i32:
-      raiseOSError(osLastError())
-
+proc createServer(server: AsyncFD) {.async.} =
   discard server.SocketHandle.listen()
   while true:
     asyncCheck readMessages(await accept(server))
 
-asyncCheck createServer(Port(10335))
-asyncCheck launchSwarm(Port(10335))
+let server = createAsyncNativeSocket()
+let port = bindAvailablePort(server.SocketHandle)
+asyncCheck createServer(server)
+asyncCheck launchSwarm(port)
 while true:
   poll()
   if clientCount == swarmSize: break
 
-assert msgCount == swarmSize * messagesToSend
-echo msgCount
+doAssert msgCount == swarmSize * messagesToSend
+doAssert msgCount == 2000
diff --git a/tests/async/tasyncclosestall.nim b/tests/async/tasyncclosestall.nim
new file mode 100644
index 000000000..d1c7a5fba
--- /dev/null
+++ b/tests/async/tasyncclosestall.nim
@@ -0,0 +1,101 @@
+discard """
+  disabled: "windows"
+  outputsub: "send has errored. As expected. All good!"
+  exitcode: 0
+"""
+import asyncdispatch, asyncnet
+
+when defined(windows):
+  from winlean import ERROR_NETNAME_DELETED
+else:
+  from posix import EBADF
+
+# This reproduces a case where a socket remains stuck waiting for writes
+# even when the socket is closed.
+const
+  timeout = 8000
+var port = Port(0)
+
+var sent = 0
+
+proc keepSendingTo(c: AsyncSocket) {.async.} =
+  while true:
+    # This write will eventually get stuck because the client is not reading
+    # its messages.
+    let sendFut = c.send("Foobar" & $sent & "\n", flags = {})
+    if not await withTimeout(sendFut, timeout):
+      # The write is stuck. Let's simulate a scenario where the socket
+      # does not respond to PING messages, and we close it. The above future
+      # should complete after the socket is closed, not continue stalling.
+      echo("Socket has stalled, closing it")
+      c.close()
+
+      let timeoutFut = withTimeout(sendFut, timeout)
+      yield timeoutFut
+      if timeoutFut.failed:
+        let errCode = ((ref OSError)(timeoutFut.error)).errorCode
+        # The behaviour differs across platforms. On Windows ERROR_NETNAME_DELETED
+        # is raised which we classif as a "diconnection error", hence we overwrite
+        # the flags above in the `send` call so that this error is raised.
+        #
+        # On Linux the EBADF error code is raised, this is because the socket
+        # is closed.
+        #
+        # This means that by default the behaviours will differ between Windows
+        # and Linux. I think this is fine though, it makes sense mainly because
+        # Windows doesn't use a IO readiness model. We can fix this later if
+        # necessary to reclassify ERROR_NETNAME_DELETED as not a "disconnection
+        # error" (TODO)
+        when defined(windows):
+          if errCode == ERROR_NETNAME_DELETED:
+            echo("send has errored. As expected. All good!")
+            quit(QuitSuccess)
+          else:
+            raise newException(ValueError, "Test failed. Send failed with code " & $errCode)
+        else:
+          if errCode == EBADF:
+            echo("send has errored. As expected. All good!")
+            quit(QuitSuccess)
+          else:
+            raise newException(ValueError, "Test failed. Send failed with code " & $errCode)
+
+      # The write shouldn't succeed and also shouldn't be stalled.
+      if timeoutFut.read():
+        raise newException(ValueError, "Test failed. Send was expected to fail.")
+      else:
+        raise newException(ValueError, "Test failed. Send future is still stalled.")
+    sent.inc(1)
+
+proc startClient() {.async.} =
+  let client = newAsyncSocket()
+  await client.connect("localhost", port)
+  echo("Connected")
+
+  let firstLine = await client.recvLine()
+  echo("Received first line as a client: ", firstLine)
+  echo("Now not reading anymore")
+  while true: await sleepAsync(1000)
+
+proc debug() {.async.} =
+  while true:
+    echo("Sent ", sent)
+    await sleepAsync(1000)
+
+proc server() {.async.} =
+  var s = newAsyncSocket()
+  s.setSockOpt(OptReuseAddr, true)
+  s.bindAddr(port)
+  s.listen()
+  let (addr2, port2) = s.getLocalAddr
+  port = port2
+
+  # We're now ready to accept connections, so start the client
+  asyncCheck startClient()
+  asyncCheck debug()
+
+  while true:
+    let client = await accept(s)
+    asyncCheck keepSendingTo(client)
+
+when isMainModule:
+  waitFor server()
diff --git a/tests/async/tasyncconnect.nim b/tests/async/tasyncconnect.nim
index 3dac379b2..564f6c67c 100644
--- a/tests/async/tasyncconnect.nim
+++ b/tests/async/tasyncconnect.nim
@@ -1,7 +1,6 @@
 discard """
-  file: "tasyncconnect.nim"
-  exitcode: 1
   outputsub: "Error: unhandled exception: Connection refused"
+  exitcode: 1
 """
 
 import
@@ -19,7 +18,7 @@ when defined(windows) or defined(nimdoc):
     quit("Error: unhandled exception: Connection refused")
 else:
     proc testAsyncConnect() {.async.} =
-        var s = newAsyncNativeSocket()
+        var s = createAsyncNativeSocket()
 
         await s.connect(testHost, testPort)
 
diff --git a/tests/async/tasyncdial.nim b/tests/async/tasyncdial.nim
index fa81235fe..f0377dfd5 100644
--- a/tests/async/tasyncdial.nim
+++ b/tests/async/tasyncdial.nim
@@ -1,20 +1,18 @@
 discard """
-  file: "tasyncdial.nim"
   output: '''
 OK AF_INET
 OK AF_INET6
 '''
-  disabled: "travis"
 """
 
 import
   nativesockets, os, asyncdispatch
 
 proc setupServerSocket(hostname: string, port: Port, domain: Domain): AsyncFD =
-  ## Creates a socket, binds it to the specified address, and starts listening for connecitons.
+  ## Creates a socket, binds it to the specified address, and starts listening for connections.
   ## Registers the descriptor with the dispatcher of the current thread
   ## Raises OSError in case of an error.
-  let fd = newNativeSocket(domain)
+  let fd = createNativeSocket(domain)
   setSockOptInt(fd, SOL_SOCKET, SO_REUSEADDR, 1)
   var aiList = getAddrInfo(hostname, port, domain)
   if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32:
diff --git a/tests/async/tasynceagain.nim b/tests/async/tasynceagain.nim
new file mode 100644
index 000000000..94c3645dc
--- /dev/null
+++ b/tests/async/tasynceagain.nim
@@ -0,0 +1,67 @@
+discard """
+  disabled: "windows"
+  exitcode: 0
+"""
+# AsyncSocketBug.nim
+# Jens Alfke (@snej) -- 16 July 2020
+# Demonstrates data loss by Nim's AsyncSocket.
+# Just run it, and it will raise an assertion failure within a minute.
+
+import asyncdispatch, asyncnet, strformat, strutils, sugar
+
+const FrameSize = 9999   # Exact size not important, but larger sizes fail quicker
+
+proc runServer() {.async.} =
+  # Server side:
+  var server = newAsyncSocket()
+  server.bindAddr(Port(9001))
+  server.listen()
+  let client = await server.accept()
+  echo "Server got client connection"
+  var lastN = 0
+  while true:
+    let frame = await client.recv(FrameSize)
+    doAssert frame.len == FrameSize
+    let n = frame[0..<6].parseInt()
+    echo "RCVD #", n, ":  ", frame[0..80], "..."
+    if n != lastN + 1:
+      echo &"******** ERROR: Server received #{n}, but last was #{lastN}!"
+    doAssert n == lastN + 1
+    lastN = n
+    await sleepAsync 100
+
+
+proc main() {.async.} =
+  asyncCheck runServer()
+
+  # Client side:
+  let socket = newAsyncSocket(buffered = false)
+  await socket.connect("localhost", Port(9001))
+  echo "Client socket connected"
+
+  var sentCount = 0
+  var completedCount = 0
+
+  while sentCount < 2000:
+    sentCount += 1
+    let n = sentCount
+
+    var message = &"{n:06} This is message #{n} of ∞. Please stay tuned for more. "
+    #echo ">>> ", message
+    while message.len < FrameSize:
+      message = message & message
+    let frame = message[0..<FrameSize]
+
+    capture n:
+      socket.send(frame).addCallback proc(f: Future[void]) =
+        # Callback when the send completes:
+        assert not f.failed
+        echo "SENT #", n
+        if n != completedCount + 1:
+          echo &"******** ERROR: Client completed #{n}, but last completed was #{completedCount}!"
+        # If this assert is enabled, it will trigger earlier than the server-side assert above:
+        assert n == completedCount + 1
+        completedCount = n
+    await sleepAsync 1
+
+waitFor main()
\ No newline at end of file
diff --git a/tests/async/tasyncexceptions.nim b/tests/async/tasyncexceptions.nim
index efe31ef27..de61c099d 100644
--- a/tests/async/tasyncexceptions.nim
+++ b/tests/async/tasyncexceptions.nim
@@ -1,7 +1,6 @@
 discard """
-  file: "tasyncexceptions.nim"
-  exitcode: 1
   outputsub: "Error: unhandled exception: foobar"
+  exitcode: 1
 """
 import asyncdispatch
 
@@ -20,7 +19,7 @@ proc processClient(fd: int) {.async.} =
   var line = await recvLine(fd)
   var foo = line[0]
   if foo == 'g':
-    raise newException(EBase, "foobar")
+    raise newException(Exception, "foobar")
 
 proc serve() {.async.} =
 
@@ -28,7 +27,7 @@ proc serve() {.async.} =
     var fut = await accept()
     await processClient(fut)
 
-when isMainModule:
+when true:
   proc main =
     var fut = serve()
     fut.callback =
diff --git a/tests/async/tasyncfile.nim b/tests/async/tasyncfile.nim
index c7b71a2f7..d95850c31 100644
--- a/tests/async/tasyncfile.nim
+++ b/tests/async/tasyncfile.nim
@@ -1,10 +1,9 @@
 discard """
-  output: '''13
+output: '''
+13
 hello humans!
 13
 '''
-  file: "tasyncfile.nim"
-  exitcode: 0
 """
 import asyncfile, asyncdispatch, os
 
@@ -54,8 +53,7 @@ proc main() {.async.} =
 
   # Issue #7347
   block:
-    let appDir = getAppDir()
-    var file = openAsync(appDir & DirSep & "hello.txt")
+    var file = openAsync( parentDir(currentSourcePath) / "hello.txt")
     echo file.getFileSize()
     echo await file.readAll()
     echo file.getFilePos()
diff --git a/tests/async/tasyncfilewrite.nim b/tests/async/tasyncfilewrite.nim
index cda612bae..72a2df0b0 100644
--- a/tests/async/tasyncfilewrite.nim
+++ b/tests/async/tasyncfilewrite.nim
@@ -1,17 +1,20 @@
 discard """
   output: '''string 1
 string 2
-string 3'''
+string 3
+'''
 """
 # bug #5532
 import os, asyncfile, asyncdispatch
 
-removeFile("test.txt")
-let f = openAsync("test.txt", fmWrite)
+const F = "test_async.txt"
+
+removeFile(F)
+let f = openAsync(F, fmWrite)
 var futs = newSeq[Future[void]]()
 for i in 1..3:
   futs.add(f.write("string " & $i & "\n"))
 waitFor(all(futs))
 f.close()
-echo readFile("test.txt")
-
+echo readFile(F)
+removeFile(F)
diff --git a/tests/async/tasyncintemplate.nim b/tests/async/tasyncintemplate.nim
new file mode 100644
index 000000000..4bddb1d18
--- /dev/null
+++ b/tests/async/tasyncintemplate.nim
@@ -0,0 +1,62 @@
+discard """
+  output: '''
+42
+43
+43
+1
+2
+3
+4
+'''
+"""
+
+# xxx move to tests/async/tasyncintemplate.nim
+import asyncdispatch
+
+block: # bug #16159
+  template foo() =
+    proc temp(): Future[int] {.async.} = return 42
+    proc tempVoid(): Future[void] {.async.} = echo await temp()
+  foo()
+  waitFor tempVoid()
+
+block: # aliasing `void`
+  template foo() =
+    type Foo = void
+    proc temp(): Future[int] {.async.} = return 43
+    proc tempVoid(): Future[Foo] {.async.} = echo await temp()
+    proc tempVoid2() {.async.} = echo await temp()
+  foo()
+  waitFor tempVoid()
+  waitFor tempVoid2()
+
+block: # sanity check
+  template foo() =
+    proc bad(): int {.async.} = discard
+  doAssert not compiles(bad())
+
+block: # bug #16786
+  block:
+    proc main(a: int|string)=
+      proc bar(b: int|string) = echo b
+      bar(a)
+    main(1)
+
+  block:
+    proc main(a: int) : Future[void] {.async.} =
+      proc bar(b: int): Future[void] {.async.} = echo b
+      await bar(a)
+    waitFor main(2)
+
+  block:
+    proc main(a: int) : Future[void] {.async.} =
+      proc bar(b: int | string): Future[void] {.async.} = echo b
+      await bar(a)
+    waitFor main(3)
+
+  block:
+    # bug
+    proc main(a: int|string) =
+      proc bar(b: int): Future[void] {.async.} = echo b
+      waitFor bar(a)
+    main(4)
diff --git a/tests/async/tasyncnetudp.nim b/tests/async/tasyncnetudp.nim
new file mode 100644
index 000000000..dade96fb2
--- /dev/null
+++ b/tests/async/tasyncnetudp.nim
@@ -0,0 +1,90 @@
+# It is a reproduction of the 'tnewasyncudp' test code, but using a high level
+# of asynchronous procedures. Output: "5000"
+import asyncdispatch, asyncnet, nativesockets, net, strutils
+
+var msgCount = 0
+var recvCount = 0
+
+const
+  messagesToSend = 100
+  swarmSize = 50
+  serverPort = 10333
+
+var
+  sendports = 0
+  recvports = 0
+
+proc saveSendingPort(port: Port) =
+  sendports = sendports + int(port)
+
+proc saveReceivedPort(port: Port) =
+  recvports = recvports + int(port)
+
+proc launchSwarm(serverIp: string, serverPort: Port) {.async.} =
+  var i = 0
+
+  while i < swarmSize:
+    var sock = newAsyncSocket(nativesockets.AF_INET, nativesockets.SOCK_DGRAM,
+                              Protocol.IPPROTO_UDP, false)
+
+    bindAddr(sock, address = "127.0.0.1")
+
+    let (null, localPort) = getLocalAddr(sock)
+
+    var k = 0
+    
+    while k < messagesToSend:
+      let message = "Message " & $(i * messagesToSend + k)
+
+      await asyncnet.sendTo(sock, serverIp, serverPort, message)
+
+      let (data, fromIp, fromPort) = await recvFrom(sock, 16384)
+
+      if data == message:
+        saveSendingPort(localPort)
+
+        inc(recvCount)
+
+      inc(k)
+    
+    close(sock)
+
+    inc(i)
+
+proc readMessages(server: AsyncSocket) {.async.} =
+  let maxResponses = (swarmSize * messagesToSend)
+
+  var i = 0
+  
+  while i < maxResponses:
+    let (data, fromIp, fromPort) = await recvFrom(server, 16384)
+
+    if data.startsWith("Message ") and fromIp == "127.0.0.1":
+      await sendTo(server, fromIp, fromPort, data)
+
+      inc(msgCount)
+
+      saveReceivedPort(fromPort)
+
+    inc(i)
+
+proc createServer() {.async.} =
+  var server = newAsyncSocket(nativesockets.AF_INET, nativesockets.SOCK_DGRAM, Protocol.IPPROTO_UDP, false)
+  
+  bindAddr(server, Port(serverPort), "127.0.0.1")
+
+  asyncCheck readMessages(server)
+
+asyncCheck createServer()
+asyncCheck launchSwarm("127.0.0.1", Port(serverPort))
+
+while true:
+  poll()
+
+  if recvCount == swarmSize * messagesToSend:
+    break
+
+doAssert msgCount == swarmSize * messagesToSend
+doAssert sendports == recvports
+
+echo msgCount
\ No newline at end of file
diff --git a/tests/async/tasyncrecursion.nim b/tests/async/tasyncrecursion.nim
index 1aeebe9b4..7c12dbb0e 100644
--- a/tests/async/tasyncrecursion.nim
+++ b/tests/async/tasyncrecursion.nim
@@ -1,6 +1,5 @@
 discard """
-  file: "tasyncrecursion.nim"
-  output: "50005000"
+output: "50005000"
 """
 import asyncdispatch
 
@@ -16,7 +15,7 @@ proc asyncRecursionTest*(): Future[int] {.async.} =
     inc(result, await asyncRecursionCycle(i))
     inc(i)
 
-when isMainModule:
+when true:
   setGlobalDispatcher(newDispatcher())
   var i = waitFor asyncRecursionTest()
   echo i
diff --git a/tests/async/tasyncsend4757.nim b/tests/async/tasyncsend4757.nim
index 752bb3e75..29873a905 100644
--- a/tests/async/tasyncsend4757.nim
+++ b/tests/async/tasyncsend4757.nim
@@ -1,25 +1,24 @@
-discard """
-  file: "tasyncsend4754.nim"
-  output: "Finished"
-"""
-
 import asyncdispatch, asyncnet
 
-proc createServer(port: Port) {.async.} =
+var port: Port
+proc createServer() {.async.} =
   var server = newAsyncSocket()
   server.setSockOpt(OptReuseAddr, true)
-  bindAddr(server, port)
+  bindAddr(server)
+  port = getLocalAddr(server)[1]
   server.listen()
   while true:
     let client = await server.accept()
     discard await client.recvLine()
 
-asyncCheck createServer(10335.Port)
+asyncCheck createServer()
 
+var done = false
 proc f(): Future[void] {.async.} =
-  let s = newAsyncNativeSocket()
-  await s.connect("localhost", 10335.Port)
+  let s = createAsyncNativeSocket()
+  await s.connect("localhost", port)
   await s.send("123")
-  echo "Finished"
+  done = true
 
 waitFor f()
+doAssert done
diff --git a/tests/async/tasyncssl.nim b/tests/async/tasyncssl.nim
index 3dc131036..57de3271d 100644
--- a/tests/async/tasyncssl.nim
+++ b/tests/async/tasyncssl.nim
@@ -1,11 +1,13 @@
 discard """
-  file: "tasyncssl.nim"
   cmd: "nim $target --hints:on --define:ssl $options $file"
-  output: "500"
+  disabled: osx
 """
-import asyncdispatch, asyncnet, net, strutils, os
+
+import asyncdispatch, asyncnet, net, strutils
+import stdtest/testutils
 
 when defined(ssl):
+  var port0: Port
   var msgCount = 0
 
   const
@@ -15,11 +17,11 @@ when defined(ssl):
   var clientCount = 0
 
   proc sendMessages(client: AsyncSocket) {.async.} =
-    for i in 0 .. <messagesToSend:
+    for i in 0 ..< messagesToSend:
       await send(client, "Message " & $i & "\c\L")
 
   proc launchSwarm(port: Port) {.async.} =
-    for i in 0 .. <swarmSize:
+    for i in 0 ..< swarmSize:
       var sock = newAsyncSocket()
       var clientContext = newContext(verifyMode = CVerifyNone)
       clientContext.wrapSocket(sock)
@@ -35,32 +37,38 @@ when defined(ssl):
         inc(clientCount)
         break
       else:
-        if line.startswith("Message "):
+        if line.startsWith("Message "):
           inc(msgCount)
         else:
           doAssert false
 
-  proc createServer(port: Port) {.async.} =
+  proc createServer() {.async.} =
     let serverContext = newContext(verifyMode = CVerifyNone,
                                    certFile = "tests/testdata/mycert.pem",
                                    keyFile = "tests/testdata/mycert.pem")
     var server = newAsyncSocket()
     serverContext.wrapSocket(server)
     server.setSockOpt(OptReuseAddr, true)
-    bindAddr(server, port)
+    bindAddr(server)
+    port0 = getLocalAddr(server)[1]
     server.listen()
     while true:
       let client = await accept(server)
       serverContext.wrapConnectedSocket(client, handshakeAsServer)
       asyncCheck readMessages(client)
 
-  asyncCheck createServer(Port(10335))
-  asyncCheck launchSwarm(Port(10335))
+  asyncCheck createServer()
+  asyncCheck launchSwarm(port0)
   while true:
     poll()
     if clientCount == swarmSize: break
 
-  assert msgCount == swarmSize * messagesToSend
-  echo msgCount
-
-
+  template cond(): bool = msgCount == swarmSize * messagesToSend
+  when defined(windows):
+    # currently: msgCount == 0
+    flakyAssert cond()
+  elif defined(linux) and int.sizeof == 8:
+    # currently:  msgCount == 10
+    flakyAssert cond()
+    doAssert msgCount > 0
+  else: doAssert cond(), $msgCount
diff --git a/tests/async/tasynctry.nim b/tests/async/tasynctry.nim
index 6749aabbf..25eab87fb 100644
--- a/tests/async/tasynctry.nim
+++ b/tests/async/tasynctry.nim
@@ -1,13 +1,13 @@
 discard """
-  file: "tasynctry.nim"
-  exitcode: 0
-  output: '''
+output: '''
 Generic except: Test
 Specific except
 Multiple idents in except
 Multiple except branches
 Multiple except branches 2
+success
 '''
+targets: "c"
 """
 import asyncdispatch, strutils
 
@@ -15,7 +15,7 @@ import asyncdispatch, strutils
 
 proc foobar() {.async.} =
   if 5 == 5:
-    raise newException(EInvalidIndex, "Test")
+    raise newException(IndexDefect, "Test")
 
 proc catch() {.async.} =
   # TODO: Create a test for when exceptions are not caught.
@@ -26,26 +26,26 @@ proc catch() {.async.} =
 
   try:
     await foobar()
-  except EInvalidIndex:
+  except IndexDefect:
     echo("Specific except")
 
   try:
     await foobar()
-  except OSError, EInvalidField, EInvalidIndex:
+  except OSError, FieldDefect, IndexDefect:
     echo("Multiple idents in except")
 
   try:
     await foobar()
-  except OSError, EInvalidField:
+  except OSError, FieldDefect:
     assert false
-  except EInvalidIndex:
+  except IndexDefect:
     echo("Multiple except branches")
 
   try:
     await foobar()
-  except EInvalidIndex:
+  except IndexDefect:
     echo("Multiple except branches 2")
-  except OSError, EInvalidField:
+  except OSError, FieldDefect:
     assert false
 
 waitFor catch()
@@ -102,3 +102,17 @@ assert y.waitFor() == 2
 
 y = test4()
 assert y.waitFor() == 2
+
+# bug #14279
+
+proc expandValue: Future[int] {.async.} =
+  return 0
+
+proc a(b: int): Future[void] {.async.} =
+  return
+
+proc b: Future[void] {.async.} =
+  await a(await expandValue())
+  echo "success"
+
+waitFor(b())
diff --git a/tests/async/tawaitsemantics.nim b/tests/async/tawaitsemantics.nim
index 98fb5dfd5..67903cc5e 100644
--- a/tests/async/tawaitsemantics.nim
+++ b/tests/async/tawaitsemantics.nim
@@ -1,7 +1,5 @@
 discard """
-  file: "tawaitsemantics.nim"
-  exitcode: 0
-  output: '''
+output: '''
 Error can be caught using yield
 Infix `or` raises
 Infix `and` raises
diff --git a/tests/async/tbreak_must_exec_finally.nim b/tests/async/tbreak_must_exec_finally.nim
new file mode 100644
index 000000000..8780e6149
--- /dev/null
+++ b/tests/async/tbreak_must_exec_finally.nim
@@ -0,0 +1,25 @@
+discard """
+  output: '''
+finally handler 8
+do not duplicate this one
+'''
+"""
+
+# bug #15243
+
+import asyncdispatch
+
+proc f() {.async.} =
+  try:
+    while true:
+      try:
+        await sleepAsync(400)
+        break
+      finally:
+        var localHere = 8
+        echo "finally handler ", localHere
+  finally:
+    echo "do not duplicate this one"
+
+when isMainModule:
+  waitFor f()
diff --git a/tests/async/tcallbacks.nim b/tests/async/tcallbacks.nim
index 8c08032cd..bd82d5824 100644
--- a/tests/async/tcallbacks.nim
+++ b/tests/async/tcallbacks.nim
@@ -1,8 +1,9 @@
 discard """
   exitcode: 0
-  output: '''3
-2
+  output: '''
 1
+2
+3
 5
 '''
 """
diff --git a/tests/async/tdiscardableproc.nim b/tests/async/tdiscardableproc.nim
new file mode 100644
index 000000000..93cd83be9
--- /dev/null
+++ b/tests/async/tdiscardableproc.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "Cannot make async proc discardable. Futures have to be checked with `asyncCheck` instead of discarded"
+"""
+
+import async
+
+proc foo {.async, discardable.} = discard
+
+foo()
diff --git a/tests/async/testmanyasyncevents.nim b/tests/async/testmanyasyncevents.nim
new file mode 100644
index 000000000..9fdd01b4f
--- /dev/null
+++ b/tests/async/testmanyasyncevents.nim
@@ -0,0 +1,24 @@
+discard """
+output: '''
+hasPendingOperations: false
+triggerCount: 100
+'''
+disabled: "windows"
+"""
+
+import asyncDispatch
+
+var triggerCount = 0
+var evs = newSeq[AsyncEvent]()
+
+for i in 0 ..< 100: # has to be lower than the typical physical fd limit
+  var ev = newAsyncEvent()
+  evs.add(ev)
+  addEvent(ev, proc(fd: AsyncFD): bool {.gcsafe,closure.} = triggerCount += 1; true)
+
+for ev in evs:
+  ev.trigger()
+
+drain()
+echo "hasPendingOperations: ", hasPendingOperations()
+echo "triggerCount: ", triggerCount
diff --git a/tests/async/tfuturestream.nim b/tests/async/tfuturestream.nim
index d76752b7e..a019df400 100644
--- a/tests/async/tfuturestream.nim
+++ b/tests/async/tfuturestream.nim
@@ -1,7 +1,5 @@
 discard """
-  file: "tfuturestream.nim"
-  exitcode: 0
-  output: '''
+output: '''
 0
 1
 2
@@ -19,7 +17,7 @@ var fs = newFutureStream[int]()
 proc alpha() {.async.} =
   for i in 0 .. 5:
     await fs.write(i)
-    await sleepAsync(1000)
+    await sleepAsync(100)
 
   echo("Done")
   fs.complete()
@@ -35,6 +33,26 @@ proc beta() {.async.} =
 asyncCheck alpha()
 waitFor beta()
 
+template ensureCallbacksAreScheduled =
+  # callbacks are called directly if the dispatcher is not running
+  discard getGlobalDispatcher()
+
+proc testCompletion() {.async.} =
+  ensureCallbacksAreScheduled
+
+  var stream = newFutureStream[string]()
+
+  for i in 1..5:
+    await stream.write($i)
+
+  var readFuture = stream.readAll()
+  stream.complete()
+  yield readFuture
+  let data = readFuture.read()
+  doAssert(data.len == 5, "actual data len = " & $data.len)
+
+waitFor testCompletion()
+
 # TODO: Something like this should work eventually.
 # proc delta(): FutureStream[string] {.async.} =
 #   for i in 0 .. 5:
@@ -50,4 +68,4 @@ waitFor beta()
 
 #   echo("Finished")
 
-# waitFor omega()
\ No newline at end of file
+# waitFor omega()
diff --git a/tests/async/tfuturevar.nim b/tests/async/tfuturevar.nim
index 73c0fddf7..b70f1d166 100644
--- a/tests/async/tfuturevar.nim
+++ b/tests/async/tfuturevar.nim
@@ -35,7 +35,7 @@ proc main() {.async.} =
   fut = newFutureVar[string]()
   let retFut = failureTest(fut, true)
   yield retFut
-  doAssert(fut.read().isNil)
+  doAssert(fut.read().len == 0)
   doAssert(fut.finished)
 
   fut = newFutureVar[string]()
@@ -44,4 +44,3 @@ proc main() {.async.} =
 
 
 waitFor main()
-
diff --git a/tests/async/tioselectors.nim b/tests/async/tioselectors.nim
index d2e4cfec1..f53767408 100644
--- a/tests/async/tioselectors.nim
+++ b/tests/async/tioselectors.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tioselectors.nim"
   output: "All tests passed!"
 """
 import selectors
@@ -12,7 +11,7 @@ template processTest(t, x: untyped) =
   if not x: echo(t & " FAILED\r\n")
 
 when not defined(windows):
-  import os, posix, nativesockets, times
+  import os, posix, nativesockets
 
   when ioselSupportedPlatform:
     import osproc
@@ -59,7 +58,11 @@ when not defined(windows):
     registerHandle(selector, client_socket, {Event.Write}, 0)
 
     freeAddrInfo(aiList)
-    discard selector.select(100)
+
+    # make sure both sockets are selected
+    var nevs = 0
+    while nevs < 2:
+      nevs += selector.select(100).len
 
     var sockAddress: SockAddr
     var addrLen = sizeof(sockAddress).Socklen
@@ -127,9 +130,9 @@ when not defined(windows):
     var event = newSelectEvent()
     selector.registerEvent(event, 1)
     var rc0 = selector.select(0)
-    event.setEvent()
+    event.trigger()
     var rc1 = selector.select(0)
-    event.setEvent()
+    event.trigger()
     var rc2 = selector.select(0)
     var rc3 = selector.select(0)
     assert(len(rc0) == 0 and len(rc1) == 1 and len(rc2) == 1 and len(rc3) == 0)
@@ -146,24 +149,25 @@ when not defined(windows):
     proc timer_notification_test(): bool =
       var selector = newSelector[int]()
       var timer = selector.registerTimer(100, false, 0)
-      var rc1 = selector.select(140)
-      var rc2 = selector.select(140)
-      assert(len(rc1) == 1 and len(rc2) == 1)
+      var rc1 = selector.select(10000)
+      var rc2 = selector.select(10000)
+      # if this flakes, see tests/m14634.nim
+      assert len(rc1) == 1 and len(rc2) == 1, $(len(rc1), len(rc2))
       selector.unregister(timer)
       discard selector.select(0)
       selector.registerTimer(100, true, 0)
-      var rc4 = selector.select(120)
-      var rc5 = selector.select(120)
-      assert(len(rc4) == 1 and len(rc5) == 0)
+      var rc4 = selector.select(10000)
+      var rc5 = selector.select(1000) # this will be an actual wait, keep it small
+      assert len(rc4) == 1 and len(rc5) == 0, $(len(rc4), len(rc5))
       assert(selector.isEmpty())
       selector.close()
       result = true
 
     proc process_notification_test(): bool =
       var selector = newSelector[int]()
-      var process2 = startProcess("/bin/sleep", "", ["2"], nil,
+      var process2 = startProcess("sleep", "", ["2"], nil,
                            {poStdErrToStdOut, poUsePath})
-      discard startProcess("/bin/sleep", "", ["1"], nil,
+      discard startProcess("sleep", "", ["1"], nil,
                            {poStdErrToStdOut, poUsePath})
 
       selector.registerProcess(process2.processID, 0)
@@ -234,7 +238,7 @@ when not defined(windows):
       if fd == -1:
         raiseOsError(osLastError())
       let length = len(data).cint
-      if posix.write(fd, cast[pointer](unsafeAddr data[0]),
+      if posix.write(fd, cast[pointer](addr data[0]),
                      len(data).cint) != length:
         raiseOsError(osLastError())
       if posix.close(fd) == -1:
@@ -288,8 +292,8 @@ when not defined(windows):
         events: set[Event]
 
     proc vnode_test(): bool =
-      proc validate(test: openarray[ReadyKey],
-                    check: openarray[valType]): bool =
+      proc validate(test: openArray[ReadyKey],
+                    check: openArray[valType]): bool =
         result = false
         if len(test) == len(check):
           for checkItem in check:
@@ -427,6 +431,20 @@ when not defined(windows):
       doAssert(res[0].fd == dirfd and
                {Event.Vnode, Event.VnodeDelete} <= res[0].events)
 
+  proc pipe_test(): bool =
+    # closing the read end of a pipe will result in it automatically
+    # being removed from the kqueue; make sure no exception is raised
+    var s = newSelector[int]()
+    var fds: array[2, cint]
+    discard pipe(fds)
+    s.registerHandle(fds[1], {Write}, 0)
+    discard close(fds[0])
+    let res = s.select(-1)
+    doAssert(res.len == 1)
+    s.unregister(fds[1])
+    discard close(fds[1])
+    return true
+
   when hasThreadSupport:
 
     var counter = 0
@@ -444,14 +462,14 @@ when not defined(windows):
       var
         thr: array[0..7, Thread[SelectEvent]]
       var selector = newSelector[int]()
-      var sock = newNativeSocket()
+      var sock = createNativeSocket()
       var event = newSelectEvent()
       for i in 0..high(thr):
         createThread(thr[i], event_wait_thread, event)
       selector.registerHandle(sock, {Event.Read}, 1)
       discard selector.select(500)
       selector.unregister(sock)
-      event.setEvent()
+      event.trigger()
       joinThreads(thr)
       assert(counter == 1)
       result = true
@@ -468,13 +486,14 @@ when not defined(windows):
   when defined(macosx) or defined(freebsd) or defined(openbsd) or
        defined(netbsd):
     processTest("File notification test...", vnode_test())
+    processTest("Pipe test...", pipe_test())
   echo("All tests passed!")
 else:
   import nativesockets, winlean, os, osproc
 
   proc socket_notification_test(): bool =
     proc create_test_socket(): SocketHandle =
-      var sock = newNativeSocket()
+      var sock = createNativeSocket()
       setBlocking(sock, false)
       result = sock
 
diff --git a/tests/async/tjsandnativeasync.nim b/tests/async/tjsandnativeasync.nim
index 45839899f..c4db3bcfb 100644
--- a/tests/async/tjsandnativeasync.nim
+++ b/tests/async/tjsandnativeasync.nim
@@ -19,7 +19,7 @@ else:
 proc foo() {.async.} =
     echo "hi"
     var s = epochTime()
-    await sleepAsync(500)
+    await sleepAsync(200)
     var e = epochTime()
     doAssert(e - s > 0.1)
     echo "bye"
diff --git a/tests/async/tnewasyncudp.nim b/tests/async/tnewasyncudp.nim
index e61f630e4..68de796a0 100644
--- a/tests/async/tnewasyncudp.nim
+++ b/tests/async/tnewasyncudp.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tnewasyncudp.nim"
   output: "5000"
 """
 import asyncdispatch, nativesockets, net, strutils, os
@@ -29,10 +28,7 @@ proc saveReceivedPort(port: int) =
 
 proc prepareAddress(intaddr: uint32, intport: uint16): ptr Sockaddr_in =
   result = cast[ptr Sockaddr_in](alloc0(sizeof(Sockaddr_in)))
-  when defined(windows):
-    result.sin_family = toInt(nativesockets.AF_INET).int16
-  else:
-    result.sin_family = toInt(nativesockets.AF_INET)
+  result.sin_family = typeof(result.sin_family)(toInt(nativesockets.AF_INET))
   result.sin_port = nativesockets.htons(intport)
   result.sin_addr.s_addr = nativesockets.htonl(intaddr)
 
@@ -44,9 +40,9 @@ proc launchSwarm(name: ptr SockAddr) {.async.} =
   var saddr = Sockaddr_in()
   while i < swarmSize:
     var peeraddr = prepareAddress(INADDR_LOOPBACK, 0)
-    var sock = newAsyncNativeSocket(nativesockets.AF_INET,
-                                    nativesockets.SOCK_DGRAM,
-                                    Protocol.IPPROTO_UDP)
+    var sock = createAsyncNativeSocket(nativesockets.AF_INET,
+                                       nativesockets.SOCK_DGRAM,
+                                       Protocol.IPPROTO_UDP)
     if bindAddr(sock.SocketHandle, cast[ptr SockAddr](peeraddr),
               sizeof(Sockaddr_in).Socklen) < 0'i32:
       raiseOSError(osLastError())
@@ -62,7 +58,7 @@ proc launchSwarm(name: ptr SockAddr) {.async.} =
                                     16384, cast[ptr SockAddr](addr saddr),
                                     addr slen)
       size = 0
-      var grammString = $cstring(addr buffer)
+      var grammString = $cast[cstring](addr buffer)
       if grammString == message:
         saveSendingPort(sockport)
         inc(recvCount)
@@ -84,8 +80,8 @@ proc readMessages(server: AsyncFD) {.async.} =
                                   16384, cast[ptr SockAddr](addr(saddr)),
                                   addr(slen))
     size = 0
-    var grammString = $cstring(addr buffer)
-    if grammString.startswith("Message ") and
+    var grammString = $cast[cstring](addr buffer)
+    if grammString.startsWith("Message ") and
        saddr.sin_addr.s_addr == nativesockets.ntohl(INADDR_LOOPBACK.uint32):
       await sendTo(server, addr grammString[0], len(grammString),
                    cast[ptr SockAddr](addr saddr), slen)
@@ -95,9 +91,9 @@ proc readMessages(server: AsyncFD) {.async.} =
 
 proc createServer() {.async.} =
   var name = prepareAddress(INADDR_LOOPBACK, serverPort)
-  var server = newAsyncNativeSocket(nativesockets.AF_INET,
-                                    nativesockets.SOCK_DGRAM,
-                                    Protocol.IPPROTO_UDP)
+  var server = createAsyncNativeSocket(nativesockets.AF_INET,
+                                       nativesockets.SOCK_DGRAM,
+                                       Protocol.IPPROTO_UDP)
   if bindAddr(server.SocketHandle, cast[ptr SockAddr](name),
               sizeof(Sockaddr_in).Socklen) < 0'i32:
     raiseOSError(osLastError())
diff --git a/tests/async/tpendingcheck.nim b/tests/async/tpendingcheck.nim
index 825acb613..4eceb0353 100644
--- a/tests/async/tpendingcheck.nim
+++ b/tests/async/tpendingcheck.nim
@@ -1,6 +1,4 @@
 discard """
-  file: "tpendingcheck.nim"
-  exitcode: 0
   output: ""
 """
 
@@ -9,7 +7,7 @@ import asyncdispatch
 doAssert(not hasPendingOperations())
 
 proc test() {.async.} =
-  await sleepAsync(100)
+  await sleepAsync(50)
 
 var f = test()
 while not f.finished:
@@ -18,4 +16,3 @@ while not f.finished:
 f.read
 
 doAssert(not hasPendingOperations())
-
diff --git a/tests/async/ttemplateinasync.nim b/tests/async/ttemplateinasync.nim
new file mode 100644
index 000000000..f4a2da538
--- /dev/null
+++ b/tests/async/ttemplateinasync.nim
@@ -0,0 +1,11 @@
+discard """
+  output: 42
+"""
+
+import asyncdispatch
+
+proc foo(): Future[int] {.async.} =
+  template ret() = return 42
+  ret()
+
+echo (waitFor foo())
diff --git a/tests/async/tupcoming_async.nim b/tests/async/tupcoming_async.nim
index e3170620e..0b6e53454 100644
--- a/tests/async/tupcoming_async.nim
+++ b/tests/async/tupcoming_async.nim
@@ -10,7 +10,7 @@ when defined(upcoming):
 
   proc delayedSet(ev: AsyncEvent, timeout: int): Future[void] {.async.} =
     await sleepAsync(timeout)
-    ev.setEvent()
+    ev.trigger()
 
   proc waitEvent(ev: AsyncEvent, closeEvent = false): Future[void] =
     var retFuture = newFuture[void]("waitEvent")
@@ -39,25 +39,25 @@ when defined(upcoming):
     let e = newAsyncEvent()
     addEvent(e) do (fd: AsyncFD) -> bool:
       assert(not unregistered)
-    e.setEvent()
+    e.trigger()
     e.unregister()
     unregistered = true
     poll()
 
   proc eventTest5298() =
-    # Event must raise `AssertionError` if event was unregistered twice.
+    # Event must raise `AssertionDefect` if event was unregistered twice.
     # Issue #5298.
     let e = newAsyncEvent()
     var eventReceived = false
     addEvent(e) do (fd: AsyncFD) -> bool:
       eventReceived = true
       return true
-    e.setEvent()
+    e.trigger()
     while not eventReceived:
       poll()
     try:
       e.unregister()
-    except AssertionError:
+    except AssertionDefect:
       discard
     e.close()
 
@@ -69,7 +69,7 @@ when defined(upcoming):
     addEvent(e) do (fd: AsyncFD) -> bool:
       e.unregister()
       e.close()
-    e.setEvent()
+    e.trigger()
     poll()
 
   when ioselSupportedPlatform or defined(windows):
@@ -98,9 +98,9 @@ when defined(upcoming):
         var process = startProcess("ping.exe", "",
                                    ["127.0.0.1", "-n", "2", "-w", "100"], nil,
                                    {poStdErrToStdOut, poUsePath, poInteractive,
-                                   poDemon})
+                                   poDaemon})
       else:
-        var process = startProcess("/bin/sleep", "", ["1"], nil,
+        var process = startProcess("sleep", "", ["1"], nil,
                                    {poStdErrToStdOut, poUsePath})
       var fut = waitProcess(process)
       waitFor(fut or waitTimer(2000))
diff --git a/tests/async/twinasyncrw.nim b/tests/async/twinasyncrw.nim
index 17b7d1cf5..f0a8f6a62 100644
--- a/tests/async/twinasyncrw.nim
+++ b/tests/async/twinasyncrw.nim
@@ -1,10 +1,6 @@
-discard """
-  file: "twinasyncrw.nim"
-  output: "5000"
-"""
 when defined(windows):
   import asyncdispatch, nativesockets, net, strutils, os, winlean
-
+  from stdtest/netutils import bindAvailablePort
   var msgCount = 0
 
   const
@@ -14,7 +10,7 @@ when defined(windows):
   var clientCount = 0
 
   proc winConnect*(socket: AsyncFD, address: string, port: Port,
-    domain = Domain.AF_INET): Future[void] =
+      domain = Domain.AF_INET): Future[void] =
     var retFuture = newFuture[void]("winConnect")
     proc cb(fd: AsyncFD): bool =
       var ret = SocketHandle(fd).getSockOptInt(cint(SOL_SOCKET), cint(SO_ERROR))
@@ -23,7 +19,7 @@ when defined(windows):
           retFuture.complete()
           return true
       else:
-          retFuture.fail(newException(OSError, osErrorMsg(OSErrorCode(ret))))
+          retFuture.fail(newOSError(OSErrorCode(ret)))
           return true
 
     var aiList = getAddrInfo(address, port, domain)
@@ -47,9 +43,9 @@ when defined(windows):
           success = false
       it = it.ai_next
 
-    dealloc(aiList)
+    freeAddrInfo(aiList)
     if not success:
-      retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+      retFuture.fail(newOSError(lastError))
     return retFuture
 
   proc winRecv*(socket: AsyncFD, size: int,
@@ -67,7 +63,7 @@ when defined(windows):
         if flags.isDisconnectionError(lastError):
           retFuture.complete("")
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+          retFuture.fail(newOSError(lastError))
       elif res == 0:
         # Disconnected
         retFuture.complete("")
@@ -92,7 +88,7 @@ when defined(windows):
         if flags.isDisconnectionError(lastError):
           retFuture.complete(0)
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+          retFuture.fail(newOSError(lastError))
       else:
         retFuture.complete(res)
     # TODO: The following causes a massive slowdown.
@@ -116,7 +112,7 @@ when defined(windows):
         if flags.isDisconnectionError(lastError):
           retFuture.complete()
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+          retFuture.fail(newOSError(lastError))
       else:
         written.inc(res)
         if res != netSize:
@@ -140,7 +136,7 @@ when defined(windows):
         var client = nativesockets.accept(sock.SocketHandle,
                                           cast[ptr SockAddr](addr(sockAddress)), addr(addrLen))
         if client == osInvalidSocket:
-          retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
+          retFuture.fail(newOSError(osLastError()))
         else:
           retFuture.complete((getAddrString(cast[ptr SockAddr](addr sockAddress)), client.AsyncFD))
 
@@ -183,7 +179,7 @@ when defined(windows):
     ## **Note**: This procedure is mostly used for testing. You likely want to
     ## use ``asyncnet.recvLine`` instead.
 
-    template addNLIfEmpty(): stmt =
+    template addNLIfEmpty() =
       if result.len == 0:
         result.add("\c\L")
 
@@ -204,12 +200,12 @@ when defined(windows):
       add(result, c)
 
   proc sendMessages(client: AsyncFD) {.async.} =
-    for i in 0 .. <messagesToSend:
+    for i in 0 ..< messagesToSend:
       await winSend(client, "Message " & $i & "\c\L")
 
   proc launchSwarm(port: Port) {.async.} =
-    for i in 0 .. <swarmSize:
-      var sock = newNativeSocket()
+    for i in 0 ..< swarmSize:
+      var sock = createNativeSocket()
       setBlocking(sock, false)
 
       await winConnect(AsyncFD(sock), "localhost", port)
@@ -224,34 +220,24 @@ when defined(windows):
         clientCount.inc
         break
       else:
-        if line.startswith("Message "):
+        if line.startsWith("Message "):
           msgCount.inc
         else:
           doAssert false
 
-  proc createServer(port: Port) {.async.} =
-    var server = newNativeSocket()
-    setBlocking(server, false)
-    block:
-      var name = Sockaddr_in()
-      name.sin_family = toInt(Domain.AF_INET).int16
-      name.sin_port = htons(uint16(port))
-      name.sin_addr.s_addr = htonl(INADDR_ANY)
-      if bindAddr(server, cast[ptr SockAddr](addr(name)),
-                  sizeof(name).Socklen) < 0'i32:
-        raiseOSError(osLastError())
-
+  proc createServer(server: SocketHandle) {.async.} =
     discard server.listen()
     while true:
       asyncCheck readMessages(await winAccept(AsyncFD(server)))
 
-  asyncCheck createServer(Port(10335))
-  asyncCheck launchSwarm(Port(10335))
+  var server = createNativeSocket()
+  setBlocking(server, false)
+  let port = bindAvailablePort(server)
+  asyncCheck createServer(server)
+  asyncCheck launchSwarm(port)
   while true:
     poll()
     if clientCount == swarmSize: break
 
   assert msgCount == swarmSize * messagesToSend
-  echo msgCount
-else:
-  echo(5000)
+  doAssert msgCount == 5000
diff --git a/tests/avr/nim.cfg b/tests/avr/nim.cfg
new file mode 100644
index 000000000..d6eba8eda
--- /dev/null
+++ b/tests/avr/nim.cfg
@@ -0,0 +1,12 @@
+avr.standalone.gcc.path = "/usr/bin"
+avr.standalone.gcc.exe = "avr-gcc"
+avr.standalone.gcc.linkerexe = "avr-gcc"
+passC = "-Os"
+passC = "-DF_CPU=16000000UL"
+passC = "-mmcu=atmega328p"
+passL = "-mmcu=atmega328p"
+passC = "-flto"
+passL = "-flto"
+cpu = "avr"
+deadCodeElim = "on"
+gc = "arc"
diff --git a/tests/avr/panicoverride.nim b/tests/avr/panicoverride.nim
new file mode 100644
index 000000000..770933ddd
--- /dev/null
+++ b/tests/avr/panicoverride.nim
@@ -0,0 +1,13 @@
+proc printf(frmt: cstring) {.varargs, importc, header: "<stdio.h>", cdecl.}
+proc exit(code: int) {.importc, header: "<stdlib.h>", cdecl.}
+
+{.push stack_trace: off, profiler:off.}
+
+proc rawoutput(s: string) =
+  printf("%s\n", s)
+
+proc panic(s: string) =
+  rawoutput(s)
+  exit(1)
+
+{.pop.}
diff --git a/tests/avr/thello.nim b/tests/avr/thello.nim
new file mode 100644
index 000000000..7ebaeae5f
--- /dev/null
+++ b/tests/avr/thello.nim
@@ -0,0 +1,6 @@
+discard """
+  cmd: "nim c --compileOnly --os:standalone --exceptions:quirky -d:noSignalHandler -d:danger --threads:off $file"
+  action: "compile"
+"""
+
+echo "hi"
diff --git a/tests/benchmark.nim b/tests/benchmark.nim
deleted file mode 100644
index 69c9a3927..000000000
--- a/tests/benchmark.nim
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-#
-#            Nim Benchmark tool
-#        (c) Copyright 2012 Dominik Picheta
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This program runs benchmarks
-import osproc, os, times, json
-
-type
-  TBenchResult = tuple[file: string, success: bool, time: float]
-
-proc compileBench(file: string) =
-  ## Compiles ``file``.
-  doAssert(execCmdEx("nim c -d:release " & file).exitCode == QuitSuccess)
-
-proc runBench(file: string): TBenchResult =
-  ## Runs ``file`` and returns info on how long it took to run.
-  var start = epochTime()
-  if execCmdEx(file.addFileExt(ExeExt)).exitCode == QuitSuccess:
-    var t = epochTime() - start
-    result = (file, true, t)
-  else: result = (file, false, -1.0)
-
-proc genOutput(benches: seq[TBenchResult]): PJsonNode =
-  result = newJObject()
-  for i in benches:
-    if i.success:
-      result[i.file.extractFilename] = newJFloat(i.time)
-    else:
-      result[i.file.extractFilename] = newJNull()
-
-proc doBench(): seq[TBenchResult] =
-  result = @[]
-  for i in walkFiles("tests/benchmarks/*.nim"):
-    echo(i.extractFilename)
-    compileBench(i)
-    result.add(runBench(i))
-
-when isMainModule:
-  var b = doBench()
-  var output = genOutput(b)
-  writeFile("benchmarkResults.json", pretty(output))
-
diff --git a/tests/benchmarks/fannkuch.nim b/tests/benchmarks/fannkuch.nim
deleted file mode 100644
index 55405b3c9..000000000
--- a/tests/benchmarks/fannkuch.nim
+++ /dev/null
@@ -1,69 +0,0 @@
-import os
-import strutils
-
-proc fannkuch (n: int): int =
-    var
-        count: seq[int]
-        maxFlips = 0
-        m        = n-1
-        r        = n
-        check    = 0
-        perm1: seq[int]
-        perm:  seq[int]
-
-    newSeq (count, n+1)
-    newSeq (perm1, n)
-    newSeq (perm, n)
-    for i in 0 .. n-1:
-        count[i] = i+1
-        perm1[i] = i
-        perm[i]  = i
-    count[n] = n+1
-
-    while True:
-        if check < 30:
-            for i in items (perm1):
-                write (stdout, $(i+1))
-            echo ("")
-            inc (check)
-
-        while r != 1:
-            count[r-1] = r
-            dec (r)
-
-        if perm1[0] != 0 and perm1[m] != m:
-            # perm = perm1
-            # The above line is between 3 and 4 times slower than the loop below!
-            for i in 0 .. n-1:
-                perm[i] = perm1[i]
-            var flipsCount = 0
-            var k = perm[0]
-            while k != 0:
-                for i in 0 .. (k div 2):
-                    swap (perm[i], perm[k-i])
-                inc (flipsCount)
-                k = perm[0]
-
-            if flipsCount > maxFlips:
-                maxFlips = flipsCount
-
-        block makePerm:
-            while r != n:
-                var tmp = perm1[0]
-                # # perm1.delete (0)
-                # # perm1.insert (tmp, r)
-                # # The above is about twice as slow as the following:
-                # moveMem (addr (perm1[0]), addr (perm1[1]), r * sizeof (int))
-                # The call to moveMem is about 50% slower than the loop below!
-                for i in 0 .. r-1:
-                    perm1[i] = perm1[i+1]
-                perm1[r] = tmp
-
-                dec (count[r])
-                if count[r] > 0:
-                    break makePerm
-                inc (r)
-            return maxFlips
-
-var n = 10
-echo ("Pfannkuchen(" & $n & ") = " & $fannkuch (n))
diff --git a/tests/benchmarks/quicksort.nim b/tests/benchmarks/quicksort.nim
deleted file mode 100644
index 6a1222b9a..000000000
--- a/tests/benchmarks/quicksort.nim
+++ /dev/null
@@ -1,54 +0,0 @@
-import os
-import strutils
-
-# Generate some pseudo-random data
-var seed: tuple[s1, s2, s3: int32] = (2'i32, 8'i32, 16'i32)
-
-proc random(): int32 =
-    seed = (((((((seed[0] and 0x0007_FFFF'i32) shl 13'i32) xor seed[0]) shr
-               19'i32) and 0x0000_1FFF'i32) xor
-             ((seed[0] and 0x000F_FFFE'i32) shl 12'i32)),
-
-            ((((((seed[1] and 0x3FFF_FFFF'i32) shl  2'i32) xor seed[1]) shr
-               25'i32) and 0x0000_007F'i32) xor
-             ((seed[1] and 0x0FFF_FFF8'i32) shl  4'i32)),
-
-            ((((((seed[2] and 0x1FFF_FFFF'i32) shl  3'i32) xor seed[2]) shr
-               11'i32) and 0x001F_FFFF'i32) xor
-             ((seed[2] and 0x0000_7FF0'i32) shl 17'i32)))
-    return seed[0] xor seed[1] xor seed[2]
-
-var n = 9999999
-
-var data: seq[int32]
-newSeq (data, n)
-for i in 0 .. data.high():
-    data[i] = random()
-
-
-proc `$` (d: seq[int32]): string =
-    result = "[ "
-    for i in items (d):
-        result.addSep (", ", 2)
-        result.add ($(i and 0xFFFF_FFFF'i64))
-    result.add (" ]")
-
-# Sort the data
-proc sort (start, stop: int) =
-    if stop <= start+1:
-        return
-
-    var j = start
-    for i in start..stop-2:
-        if data[i] <% data[stop-1]:
-            swap (data[i], data[j])
-            inc (j)
-    swap (data[j], data[stop-1])
-
-    sort (start, j)
-    sort (j+1, stop)
-
-sort (0, data.len)
-echo (data[n div 2 - 1] and 0xFFFF_FFFF'i64, ", ",
-      data[n div 2] and 0xFFFF_FFFF'i64, ", ",
-      data[n div 2 + 1] and 0xFFFF_FFFF'i64)
diff --git a/tests/benchmarks/readme.md b/tests/benchmarks/readme.md
new file mode 100644
index 000000000..1e744fc40
--- /dev/null
+++ b/tests/benchmarks/readme.md
@@ -0,0 +1,5 @@
+# Collection of benchmarks
+
+In future work, benchmarks can be added to CI, but for now we provide benchmarks that can be run locally.
+
+See RFC: https://github.com/timotheecour/Nim/issues/425
diff --git a/tests/benchmarks/ttls.nim b/tests/benchmarks/ttls.nim
new file mode 100644
index 000000000..f5314850f
--- /dev/null
+++ b/tests/benchmarks/ttls.nim
@@ -0,0 +1,30 @@
+discard """
+  action: compile
+"""
+
+#[
+## on osx
+nim r -d:danger --threads --tlsEmulation:off tests/benchmarks/ttls.nim
+9.999999999992654e-07
+
+ditto with `--tlsEmulation:on`:
+0.216999
+]#
+
+import times
+
+proc main2(): int =
+  var g0 {.threadvar.}: int
+  g0.inc
+  result = g0
+
+proc main =
+  let n = 100_000_000
+  var c = 0
+  let t = cpuTime()
+  for i in 0..<n:
+    c += main2()
+  let t2 = cpuTime() - t
+  doAssert c != 0
+  echo t2
+main()
diff --git a/tests/bind/tbind1.nim b/tests/bind/tbind1.nim
deleted file mode 100644
index 9b13a7d11..000000000
--- a/tests/bind/tbind1.nim
+++ /dev/null
@@ -1,21 +0,0 @@
-discard """
-  file: "tbind1.nim"
-  output: "3"
-"""
-# Test the new ``bind`` keyword for templates
-
-proc p1(x: int8, y: int): int = return x + y
-
-template tempBind(x, y): untyped =
-  bind p1
-  p1(x, y)
-
-proc p1(x: int, y: int8): int = return x - y
-
-# This is tricky: the call to ``p1(1'i8, 2'i8)`` should not fail in line 6,
-# because it is not ambiguous there. But it is ambiguous after line 8.
-
-echo tempBind(1'i8, 2'i8) #OUT 3
-
-
-
diff --git a/tests/bind/tbind2.nim b/tests/bind/tbind2.nim
deleted file mode 100644
index 799b14381..000000000
--- a/tests/bind/tbind2.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-discard """
-  file: "tbind2.nim"
-  line: 12
-  errormsg: "ambiguous call"
-"""
-# Test the new ``bind`` keyword for templates
-
-proc p1(x: int8, y: int): int = return x + y
-proc p1(x: int, y: int8): int = return x - y
-
-template tempBind(x, y): untyped =
-  (bind p1(x, y))  #ERROR_MSG ambiguous call
-
-echo tempBind(1'i8, 2'i8)
-
-
-
diff --git a/tests/bind/tbind3.nim b/tests/bind/tbind3.nim
deleted file mode 100644
index 551acc10f..000000000
--- a/tests/bind/tbind3.nim
+++ /dev/null
@@ -1,11 +0,0 @@
-discard """
-  file: "tbind3.nim"
-  output: "1"
-"""
-# Module B
-import mbind3
-
-echo genId() #OUT 1
-
-
-
diff --git a/tests/bind/tbindoverload.nim b/tests/bind/tbindoverload.nim
deleted file mode 100644
index 6f5bb339e..000000000
--- a/tests/bind/tbindoverload.nim
+++ /dev/null
@@ -1,12 +0,0 @@
-import strtabs
-
-template t*() =
-  block:
-    bind newStringTable
-    discard {"Content-Type": "text/html"}.newStringTable()
-
-    discard {:}.newStringTable
-
-#discard {"Content-Type": "text/html"}.newStringTable()
-
-t()
diff --git a/tests/bind/tdatabind.nim b/tests/bind/tdatabind.nim
deleted file mode 100644
index 124faee6f..000000000
--- a/tests/bind/tdatabind.nim
+++ /dev/null
@@ -1,95 +0,0 @@
-discard """
-  disabled: true
-"""
-
-import events
-type
-  TProperty*[T] = object of TObject
-    getProc: proc(property: TProperty[T]): T {.nimcall.}
-    setProc: proc(property: var TProperty[T], value: T) {.nimcall.}
-    value: T
-    ValueChanged*: TEventHandler
-    Binders: seq[TProperty[T]]
-    EEmitter: TEventEmitter
-  # Not a descriptive name but it was that or TPropertyValueChangeEventArgs.
-  TValueEventArgs[T] = object of TEventArgs
-    Property*: TProperty[T]
-
-
-proc newProperty*[T](value: T): TProperty[T] =
-  var prop: TProperty[T]
-
-  prop.EEmitter = initEventEmitter()
-  prop.Binders = @[]
-  prop.ValueChanged = initEventHandler("ValueChanged")
-  prop.value = value
-
-  proc getter(property: TProperty[T]): T =
-   return property.value
-
-  prop.getProc = getter
-
-  proc setter(property: var TProperty[T], v: T) =
-    property.value = v
-
-    # fire event here
-    var args: TValueEventArgs[T]
-    args.Property = property
-    property.EEmitter.emit(property.ValueChanged, args)
-
-  prop.setProc = setter
-
-  return prop
-
-proc `prop`[T] (p: TProperty[T]): T =
-  # I'm assuming this is trying to get a value from the property.
-  # i.e. myVar = myProperty
-  return p.getProc(p)
-
-proc `~=`[T] (p: var TProperty[T], v: T) =
-  # Assuming this is setting the value.
-  p.setProc(p, v)
-
-proc `$`[T] (p: TProperty[T]): string =
-  var value = p.getProc(p)
-  return $value
-
-proc propertyBind*[T](p1: var TProperty[T], p2: var TProperty[T]) =
-  p1.Binders.add(p2)
-
-  # make handler -> handler[T] so trigger even more generics bugs ...
-  proc handler(e: TEventArgs) =
-    type TEA = TValueEventArgs[T]
-    var args = TEA(e)
-    var val = args.Property.getProc(p1)
-    for i in countup(0, len(e.Property.ValueChanged.Binders) -1):
-      var binded = e.Property.ValueChanged.Binders[i]
-      binded.setProc(binded, val)
-
-    echo("Property 1 has changed to " & $val)
-
-  if p1.ValueChanged.containsHandler(handler) == false:
-    addHandler(p1.ValueChanged, handler)
-
-proc `->`[T](p1: var TProperty[T], p2: var TProperty[T]) =
-  propertyBind(p2,p1)
-
-when isMainModule:
-  # Initial value testing
-  var myProp = newProperty(5)
-
-  echo(myProp)
-
-  myProp ~= 7 # Temp operator until overloading of '=' is implemented.
-  echo(myProp)
-
-  # Binding testing
-
-  var prop1 = newProperty(9)
-  var prop2: TProperty[int]
-
-  prop2 -> prop1 # Binds prop2 to prop1
-
-  prop1 ~= 7
-  echo(prop2) # Output: 7
-
diff --git a/tests/bind/tinvalidbindtypedesc.nim b/tests/bind/tinvalidbindtypedesc.nim
deleted file mode 100644
index ecdd12603..000000000
--- a/tests/bind/tinvalidbindtypedesc.nim
+++ /dev/null
@@ -1,11 +0,0 @@
-discard """
-  line: 10
-  errormsg: "type mismatch: got <type float, string>"
-"""
-
-proc foo(T: typedesc; some: T) =
-  echo($some)
-
-foo int, 4
-foo float, "bad"
-
diff --git a/tests/bind/tmixin.nim b/tests/bind/tmixin.nim
deleted file mode 100644
index d841326a5..000000000
--- a/tests/bind/tmixin.nim
+++ /dev/null
@@ -1,27 +0,0 @@
-discard """
-  output: "1\n2"
-"""
-
-type
-  TFoo1 = object of TObject
-    v: int
-  TFoo2 = object of TFoo1
-    v2: int
-
-proc test(f: TFoo1) =
-  echo "1"
-
-proc Foo[T](f: T) =
-  mixin test
-  test(f)
-
-var
-  a: TFoo1
-  b: TFoo2
-
-
-proc test(f: TFoo2) =
-  echo "2"
-
-Foo(a)
-Foo(b)
diff --git a/tests/bind/tnicerrorforsymchoice.nim b/tests/bind/tnicerrorforsymchoice.nim
deleted file mode 100644
index 8c3a99c97..000000000
--- a/tests/bind/tnicerrorforsymchoice.nim
+++ /dev/null
@@ -1,18 +0,0 @@
-discard """
-  line: 18
-  errormsg: "type mismatch: got <proc (s: TScgi: ScgiState or AsyncScgiState) | proc (client: AsyncSocket, headers: StringTableRef, input: string){.noSideEffect, gcsafe, locks: 0.}>"
-"""
-
-#bug #442
-import scgi, sockets, asyncio, strtabs
-proc handleSCGIRequest[TScgi: ScgiState | AsyncScgiState](s: TScgi) =
-  discard
-proc handleSCGIRequest(client: AsyncSocket, headers: StringTableRef,
-                       input: string) =
-  discard
-
-proc test(handle: proc (client: AsyncSocket, headers: StringTableRef,
-                        input: string), b: int) =
-  discard
-
-test(handleSCGIRequest)
diff --git a/tests/borrow/tborrow.nim b/tests/borrow/tborrow.nim
deleted file mode 100644
index ee0d0194d..000000000
--- a/tests/borrow/tborrow.nim
+++ /dev/null
@@ -1,21 +0,0 @@
-discard """
-  output: "4887 true"
-"""
-
-# test the new borrow feature that works with generics:
-
-proc `++`*[T: int | float](a, b: T): T =
-  result = a + b
-
-type
-  DI = distinct int
-  DF = distinct float
-  DS = distinct string
-
-proc `++`(x, y: DI): DI {.borrow.}
-proc `++`(x, y: DF): DF {.borrow.}
-
-proc `$`(x: DI): string {.borrow.}
-proc `$`(x: DF): string {.borrow.}
-
-echo  4544.DI ++ 343.DI, " ", (4.5.DF ++ 0.5.DF).float == 5.0
diff --git a/tests/borrow/tinvalidborrow.nim b/tests/borrow/tinvalidborrow.nim
deleted file mode 100644
index 9ab9e8d64..000000000
--- a/tests/borrow/tinvalidborrow.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-discard """
-  line: 11
-  errormsg: "no symbol to borrow from found"
-"""
-
-# bug #516
-
-type
-  TAtom = culong
-
-proc `==`*(a, b: TAtom): bool {.borrow.}
-
-var
-  d, e: TAtom
-
-echo( $(d == e) )
-
diff --git a/tests/c/tcompile.c b/tests/c/tcompile.c
new file mode 100644
index 000000000..fc0482e40
--- /dev/null
+++ b/tests/c/tcompile.c
@@ -0,0 +1,3 @@
+int foo(int a, int b) {
+  return a+b;
+}
diff --git a/tests/c/tcompile.nim b/tests/c/tcompile.nim
new file mode 100644
index 000000000..cf99fd7ed
--- /dev/null
+++ b/tests/c/tcompile.nim
@@ -0,0 +1,10 @@
+discard """
+  output: '''44'''
+  joinable: "false"
+"""
+
+{.compile: "tcompile.c".}
+
+proc foo(a, b: cint): cint {.importc: "foo", cdecl.}
+
+echo foo(40, 4)
diff --git a/tests/misc/temit.nim b/tests/c/temit.nim
index c83235659..1943c94ea 100644
--- a/tests/misc/temit.nim
+++ b/tests/c/temit.nim
@@ -1,10 +1,10 @@
 discard """
-  file: "temit.nim"
   output: "509"
 """
 # Test the new ``emit`` pragma:
 
 {.emit: """
+#include <stdio.h>
 static int cvariable = 420;
 
 """.}
@@ -14,7 +14,3 @@ proc embedsC() =
   {.emit: """printf("%d\n", cvariable + (int)`nimVar`);""".}
 
 embedsC()
-
-
-
-
diff --git a/tests/c/treservedcidentsasfields.nim b/tests/c/treservedcidentsasfields.nim
new file mode 100644
index 000000000..6cdf9e855
--- /dev/null
+++ b/tests/c/treservedcidentsasfields.nim
@@ -0,0 +1,39 @@
+discard """
+  targets: "c cpp"
+"""
+
+import macros
+
+macro make_test_type(idents: varargs[untyped]): untyped =
+  result = nnkStmtList.newTree()
+
+  var ident_defs: seq[NimNode] = @[]
+  for i in idents:
+    ident_defs.add newIdentDefs(i, ident("int"))
+
+  result.add newTree(nnkTypeSection,
+    newTree(nnkTypeDef,
+      ident("TestType"),
+      newEmptyNode(),
+      newTree(nnkObjectTy,
+        newEmptyNode(),
+        newEmptyNode(),
+        newTree(nnkRecList,
+          ident_defs
+        )
+      )
+    )
+  )
+
+make_test_type(
+  auto, bool, catch, char, class, compl, const_cast, default, delete, double,
+  dynamic_cast, explicit, extern, false, float, friend, goto, int, long,
+  mutable, namespace, new, operator, private, protected, public, register,
+  reinterpret_cast, restrict, short, signed, sizeof, static_cast, struct, switch,
+  this, throw, true, typedef, typeid, typeof, typename, union, packed, unsigned,
+  virtual, void, volatile, wchar_t, alignas, alignof, constexpr, decltype, nullptr,
+  noexcept, thread_local, static_assert, char16_t, char32_t
+)
+
+# Make sure the type makes it to codegen.
+var test_instance: TestType
diff --git a/tests/caas/absurd_nesting.nim b/tests/caas/absurd_nesting.nim
deleted file mode 100644
index 136d65cc7..000000000
--- a/tests/caas/absurd_nesting.nim
+++ /dev/null
@@ -1,29 +0,0 @@
-# Tries to test the full ownership path generated by idetools.
-
-proc lev1(t1: string) =
-  var temp = t1
-  for i in 0..len(temp)-1:
-    temp[i] = chr(int(temp[i]) + 1)
-
-  proc lev2(t2: string) =
-    var temp = t2
-    for i in 0..len(temp)-1:
-      temp[i] = chr(int(temp[i]) + 1)
-
-    proc lev3(t3: string) =
-      var temp = t3
-      for i in 0..len(temp)-1:
-        temp[i] = chr(int(temp[i]) + 1)
-
-      proc lev4(t4: string) =
-        var temp = t4
-        for i in 0..len(temp)-1:
-          temp[i] = chr(int(temp[i]) + 1)
-
-        echo temp & "(lev4)"
-      lev4(temp & "(lev3)")
-    lev3(temp & "(lev2)")
-  lev2(temp & "(lev1)")
-
-when isMainModule:
-  lev1("abcd")
diff --git a/tests/caas/absurd_nesting.txt b/tests/caas/absurd_nesting.txt
deleted file mode 100644
index 986e34836..000000000
--- a/tests/caas/absurd_nesting.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-absurd_nesting.nim
-
-> c --verbosity:0 --hints:on
-SuccessX
-
-> idetools --track:$TESTNIM,6,6 --def $SILENT
-skVar\tabsurd_nesting.lev1.temp\tstring
-
-> idetools --track:$TESTNIM,21,13 --def $SILENT
-skVar\tabsurd_nesting.lev1.lev2.lev3.lev4.temp\tstring
-
-> idetools --track:$TESTNIM,6,27 --def $SILENT
-skForVar\tabsurd_nesting.lev1.i\tint
-
-> idetools --track:$TESTNIM,21,33 --def $SILENT
-skForVar\tabsurd_nesting.lev1.lev1.lev3.lev4.i\tint
-
-> idetools --track:$TESTNIM,24,8 --def $SILENT
-skProc\tabsurd_nesting.lev1.lev1.lev3.lev4\tproc \(string\)
-
-> idetools --track:$TESTNIM,4,13 --def $SILENT
-skParam\tabsurd_nesting.lev1.t1\tstring
-
-> idetools --track:$TESTNIM,4,13 --def $SILENT
-skParam\tabsurd_nesting.lev1.t1\tstring
-
-> idetools --track:$TESTNIM,19,19 --def $SILENT
-skParam\tabsurd_nesting.lev1.lev2.lev3.lev4.t4\tstring
-
diff --git a/tests/caas/basic-recompile.txt b/tests/caas/basic-recompile.txt
deleted file mode 100644
index 620e0c059..000000000
--- a/tests/caas/basic-recompile.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-main.nim
-> c --verbosity:0 --hints:on
-SuccessX
-# The "Processing" string will be found always in proc mode since each
-# compilation command will generate it. We need to test it only in Caas mode to
-# verify the server is not recompiling again the file.
-CaasRun > c --verbosity:0 --hints:on
-CaasRun ! Processing
-CaasRun SuccessX
-
diff --git a/tests/caas/compile-suggest.txt b/tests/caas/compile-suggest.txt
deleted file mode 100644
index 378320014..000000000
--- a/tests/caas/compile-suggest.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-main.nim
-> c --verbosity:0 --hints:on
-SuccessX
-> idetools --trackDirty:main_dirty.nim,$TESTNIM,12,7 --suggest $SILENT
-skField\tx
-skField\ty
-
diff --git a/tests/caas/compile-then-def.txt b/tests/caas/compile-then-def.txt
deleted file mode 100644
index 72ba46b04..000000000
--- a/tests/caas/compile-then-def.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-main.nim
-> c --verbosity:0 --hints:on
-SuccessX
-
-> idetools --track:$TESTNIM,5,18 --def --verbosity:0 --hints:on
-strutils.toUpper
-! SuccessX
-
-> idetools --track:$TESTNIM,5,18 --def --verbosity:0 --hints:on
-strutils.toUpper
-! SuccessX
diff --git a/tests/caas/completion_dot_syntax.txt b/tests/caas/completion_dot_syntax.txt
deleted file mode 100644
index 4a975e5df..000000000
--- a/tests/caas/completion_dot_syntax.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-completion_dot_syntax_main.nim
-> idetools --track:$TESTNIM,24,15 --def
-def\tskProc\t$MODULE.echoRemainingDollars
-> idetools --trackDirty:completion_dot_syntax_dirty.nim,$TESTNIM,25,12 --suggest
-sug\tskProc\techoRemainingDollars
-# The suggestion should not mention the other echoRemaining* variants.
-!echoRemainingEuros
-!echoRemainingBugs
-
diff --git a/tests/caas/completion_dot_syntax_dirty.nim b/tests/caas/completion_dot_syntax_dirty.nim
deleted file mode 100644
index 6237c4e79..000000000
--- a/tests/caas/completion_dot_syntax_dirty.nim
+++ /dev/null
@@ -1,25 +0,0 @@
-import strutils
-
-# Verifies if the --suggestion switch differentiates types for dot notation.
-
-type
-  TDollar = distinct int
-  TEuro = distinct int
-
-proc echoRemainingDollars(amount: TDollar) =
-  echo "You have $1 dollars" % [$int(amount)]
-
-proc echoRemainingEuros(amount: TEuro) =
-  echo "You have $1 euros" % [$int(amount)]
-
-proc echoRemainingBugs() =
-  echo "You still have bugs"
-
-proc main =
-  var
-    d: TDollar
-    e: TEuro
-  d = TDollar(23)
-  e = TEuro(32)
-  d.echoRemainingDollars()
-  e.echoRemai
diff --git a/tests/caas/completion_dot_syntax_main.nim b/tests/caas/completion_dot_syntax_main.nim
deleted file mode 100644
index 0be8c7f4f..000000000
--- a/tests/caas/completion_dot_syntax_main.nim
+++ /dev/null
@@ -1,24 +0,0 @@
-import strutils
-
-# Verifies if the --suggestion switch differentiates types for dot notation.
-
-type
-  TDollar = distinct int
-  TEuro = distinct int
-
-proc echoRemainingDollars(amount: TDollar) =
-  echo "You have $1 dollars" % [$int(amount)]
-
-proc echoRemainingEuros(amount: TEuro) =
-  echo "You have $1 euros" % [$int(amount)]
-
-proc echoRemainingBugs() =
-  echo "You still have bugs"
-
-proc main =
-  var
-    d: TDollar
-    e: TEuro
-  d = TDollar(23)
-  e = TEuro(32)
-  d.echoRemainingDollars()
diff --git a/tests/caas/def-def-compile.txt b/tests/caas/def-def-compile.txt
deleted file mode 100644
index 21d5ea962..000000000
--- a/tests/caas/def-def-compile.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-main.nim
-> idetools --track:$TESTNIM,5,18 --def --verbosity:0 --hints:on
-strutils.toUpper
-! SuccessX
-
-> idetools --track:$TESTNIM,5,18 --def --verbosity:0 --hints:on
-strutils.toUpper
-! SuccessX
-
-> c --verbosity:0 --hints:on
-SuccessX
-
diff --git a/tests/caas/def-then-compile.txt b/tests/caas/def-then-compile.txt
deleted file mode 100644
index 2214bf02c..000000000
--- a/tests/caas/def-then-compile.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-main.nim
-> idetools --track:$TESTNIM,5,18 --def --verbosity:0 --hints:on
-strutils.toUpper
-! SuccessX
-
-> c --verbosity:0 --hints:on
-SuccessX
-
diff --git a/tests/caas/forward_declarations.nim b/tests/caas/forward_declarations.nim
deleted file mode 100644
index 177d82f20..000000000
--- a/tests/caas/forward_declarations.nim
+++ /dev/null
@@ -1,15 +0,0 @@
-# This example shows that idetools returns an empty signature for a forward
-# declared proc in proc/symproc runs, but correctly returns the full signature
-# in caas mode.
-
-proc echoHello(text: string)
-
-proc testForward() =
-  echo "T"
-  echoHello("T")
-
-proc echoHello(text: string) =
-  echo "Hello Mr." & text
-
-when isMainModule:
-  testForward()
diff --git a/tests/caas/forward_declarations.txt b/tests/caas/forward_declarations.txt
deleted file mode 100644
index b1695b9c7..000000000
--- a/tests/caas/forward_declarations.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-forward_declarations.nim
-
-> idetools --track:$TESTNIM,9,5 --def $SILENT
-skProc
-proc \(string\)
-
-> idetools --track:$TESTNIM,5,9 --def $SILENT
-skProc
-proc \(string\)
diff --git a/tests/caas/forward_usages.txt b/tests/caas/forward_usages.txt
deleted file mode 100644
index 05ef517dc..000000000
--- a/tests/caas/forward_usages.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-forward_declarations.nim
-
-> c --verbosity:0 --hints:on
-SuccessX
-
-# None of the following return three instances of the echoHello proc, the first
-# being the forward declaration, the second being the usage inside testForward,
-# and the third being the actual implementation.
-
-> idetools --track:$TESTNIM,5,5 --usages $SILENT
-skProc.*\n.*skProc.*\n.*skProc
-
-> idetools --track:$TESTNIM,9,5 --usages $SILENT
-skProc.*\n.*skProc.*\n.*skProc
-
-> idetools --track:$TESTNIM,11,5 --usages $SILENT
-skProc.*\n.*skProc.*\n.*skProc
diff --git a/tests/caas/idetools_api.nim b/tests/caas/idetools_api.nim
deleted file mode 100644
index 281e562d7..000000000
--- a/tests/caas/idetools_api.nim
+++ /dev/null
@@ -1,84 +0,0 @@
-import unicode, sequtils, macros, re
-
-proc test_enums() =
-  var o: Tfile
-  if o.open("files " & "test.txt", fmWrite):
-    o.write("test")
-    o.close()
-
-proc test_iterators(filename = "tests.nim") =
-  let
-    input = readFile(filename)
-    letters = toSeq(runes(string(input)))
-  for letter in letters: echo int(letter)
-
-const SOME_SEQUENCE = @[1, 2]
-type
-  bad_string = distinct string
-  TPerson = object of TObject
-    name*: bad_string
-    age: int
-
-proc adder(a, b: int): int =
-  result = a + b
-
-type
-  PExpr = ref object of TObject ## abstract base class for an expression
-  PLiteral = ref object of PExpr
-    x: int
-  PPlusExpr = ref object of PExpr
-    a, b: PExpr
-
-# watch out: 'eval' relies on dynamic binding
-method eval(e: PExpr): int =
-  # override this base method
-  quit "to override!"
-
-method eval(e: PLiteral): int = e.x
-method eval(e: PPlusExpr): int = eval(e.a) + eval(e.b)
-
-proc newLit(x: int): PLiteral = PLiteral(x: x)
-proc newPlus(a, b: PExpr): PPlusExpr = PPlusExpr(a: a, b: b)
-
-echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
-
-proc findVowelPosition(text: string) =
-  var found = -1
-  block loops:
-    for i, letter in pairs(text):
-      for j in ['a', 'e', 'i', 'o', 'u']:
-        if letter == j:
-          found = i
-          break loops # leave both for-loops
-  echo found
-
-findVowelPosition("Zerg") # should output 1, position of vowel.
-
-macro expect*(exceptions: varargs[expr], body: stmt): stmt {.immediate.} =
-  ## Expect docstrings
-  let exp = callsite()
-  template expectBody(errorTypes, lineInfoLit: expr,
-                      body: stmt): NimNode {.dirty.} =
-    try:
-      body
-      assert false
-    except errorTypes:
-      nil
-
-  var body = exp[exp.len - 1]
-
-  var errorTypes = newNimNode(nnkBracket)
-  for i in countup(1, exp.len - 2):
-    errorTypes.add(exp[i])
-
-  result = getAst(expectBody(errorTypes, exp.lineinfo, body))
-
-proc err =
-  raise newException(EArithmetic, "some exception")
-
-proc testMacro() =
-  expect(EArithmetic):
-    err()
-
-testMacro()
-let notAModule = re"(\w+)=(.*)"
diff --git a/tests/caas/idetools_api.txt b/tests/caas/idetools_api.txt
deleted file mode 100644
index 035590dc3..000000000
--- a/tests/caas/idetools_api.txt
+++ /dev/null
@@ -1,59 +0,0 @@
-idetools_api.nim
-> c --verbosity:0 --hints:on
-SuccessX
-> idetools --track:$TESTNIM,4,11 --def $SILENT
-def\tskType\tsystem.TFile\tTFile
-> idetools --track:$TESTNIM,5,7 --def $SILENT
-def\tskProc\tsystem.Open\tproc \(var TFile, string, TFileMode, int\): bool
-> idetools --track:$TESTNIM,5,21 --def $SILENT
-def\tskProc\tsystem.\&\tproc \(string, string\): string\{.noSideEffect.\}
-> idetools --track:$TESTNIM,5,38 --def $SILENT
-def\tskEnumField\tsystem.TFileMode.fmWrite\tTFileMode
-> idetools --track:$TESTNIM,7,6 --def $SILENT
-def\tskProc\tsystem.Close\tproc \(TFile\)
-> idetools --track:$TESTNIM,12,23 --def $SILENT
-def\tskIterator\tunicode.runes\titerator \(string\): TRune
-> idetools --track:$TESTNIM,12,15 --def $SILENT
-def\tskTemplate\tsequtils.toSeq\tproc \(expr\): expr
-> idetools --track:$TESTNIM,15,7 --def $SILENT
-
-# ProcRun mode will fail the next line, because the type is returned empty.
-def\tskConst\t$MODULE.SOME_SEQUENCE\tseq\[int\]\t
-> idetools --track:$TESTNIM,15,23 --def $SILENT
-def\tskProc\tsystem.@\tproc \(array\[IDX, T\]\): seq\[T\]\{.noSideEffect.\}
-> idetools --track:$TESTNIM,17,3 --def $SILENT
-
-# ProcRun mode will fail the next line, because the type is returned empty.
-def\tskType\t$MODULE.bad_string\tbad_string\t
-> idetools --track:$TESTNIM,11,24 --def $SILENT
-def\tskParam\t$MODULE.test_iterators.filename\tstring
-> idetools --track:$TESTNIM,6,5 --def $SILENT
-def\tskVar\t$MODULE.test_enums.o\tTFile
-> idetools --track:$TESTNIM,12,34 --def $SILENT
-def\tskLet\t$MODULE.test_iterators.input\tTaintedString
-> idetools --track:$TESTNIM,13,35 --def $SILENT
-def\tskForVar\t$MODULE.test_iterators.letter\tTRune
-> idetools --track:$TESTNIM,23,3 --def $SILENT
-def\tskResult\t$MODULE.adder.result\tint
-> idetools --track:$TESTNIM,19,6 --def $SILENT
-
-# ProcRun mode will fail the next line, because the type is returned empty.
-def\tskField\t$MODULE.TPerson.name\tbad_string\t
-
-> idetools --track:$TESTNIM,43,7 --def $SILENT
-def\tskMethod\t$MODULE.eval\tproc \(PPlusExpr\): int\t
-
-> idetools --track:$TESTNIM,47,8 --def $SILENT
-def\tskLabel\t$MODULE.findVowelPosition.loops\t\t
-# For some reason the use of the label with break displaces its position.
-> idetools --track:$TESTNIM,52,16 --def $SILENT
-def\tskLabel\t$MODULE.findVowelPosition.loops\t\t
-
-# Displaced macro usage by one character.
-> idetools --track:$TESTNIM,80,2 --def $SILENT
-def\tskMacro\t$MODULE.expect\tproc \(varargs\[expr\], stmt\): stmt\t
-
-# The syntax for extended raw string literals should not be returned as module
-# but as the proc re() inside the re module.
-> idetools --track:$TESTNIM,84,17 --def $SILENT
-!def\tskModule
diff --git a/tests/caas/imported.nim b/tests/caas/imported.nim
deleted file mode 100644
index a4bc5c0e6..000000000
--- a/tests/caas/imported.nim
+++ /dev/null
@@ -1,3 +0,0 @@
-proc `+++`*(a,b: string): string =
-  return a & "  " & b
-
diff --git a/tests/caas/issue_416_template_shift.nim b/tests/caas/issue_416_template_shift.nim
deleted file mode 100644
index d52f611d6..000000000
--- a/tests/caas/issue_416_template_shift.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-import unicode, sequtils
-
-proc test() =
-  let input = readFile("weird.nim")
-  for letter in runes(string(input)):
-    echo int(letter)
-
-when 1 > 0:
-  proc failtest() =
-    let
-      input = readFile("weird.nim")
-      letters = toSeq(runes(string(input)))
-    for letter in letters:
-      echo int(letter)
-
-when isMainModule:
-  test()
diff --git a/tests/caas/issue_416_template_shift.txt b/tests/caas/issue_416_template_shift.txt
deleted file mode 100644
index e911c1360..000000000
--- a/tests/caas/issue_416_template_shift.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-issue_416_template_shift.nim
-> c --verbosity:0 --hints:on
-SuccessX
-> idetools --track:$TESTNIM,12,28 --def $SILENT
-def\tskType\tsystem.string\tstring
-> idetools --track:$TESTNIM,12,35 --def $SILENT
-def\tskLet\t$MODULE.failtest.input\tTaintedString
-
-# The following fail because they seem shifted one column to the right.
-> idetools --track:$TESTNIM,12,16 --def $SILENT
-def\tskTemplate\tsequtils.toSeq\tproc \(expr\): expr
-> idetools --track:$TESTNIM,12,22 --def $SILENT
-def\tskIterator\tunicode.runes\titerator \(string\): TRune
-
diff --git a/tests/caas/issue_452_export_shift.nim b/tests/caas/issue_452_export_shift.nim
deleted file mode 100644
index 46cff6241..000000000
--- a/tests/caas/issue_452_export_shift.nim
+++ /dev/null
@@ -1,8 +0,0 @@
-const
-  VERSION_STR1* = "0.5.0" ## Idetools shifts this one column.
-  VERSION_STR2 = "0.5.0" ## This one is ok.
-  VERSION_STR3* = "0.5.0" ## Bad.
-  VERSION_STR4 = "0.5.0" ## Ok.
-
-proc forward1*(): string = result = ""
-proc forward2(): string = result = ""
diff --git a/tests/caas/issue_452_export_shift.txt b/tests/caas/issue_452_export_shift.txt
deleted file mode 100644
index 4676ed71e..000000000
--- a/tests/caas/issue_452_export_shift.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-issue_452_export_shift.nim
-> c --verbosity:0 --hints:on
-SuccessX
-> idetools --track:$TESTNIM,2,2 --def $SILENT
-def\tskConst\t$MODULE.VERSION_STR1\tstring
-> idetools --track:$TESTNIM,3,2 --def $SILENT
-def\tskConst\t$MODULE.VERSION_STR2\tstring
-> idetools --track:$TESTNIM,7,5 --def $SILENT
-def\tskProc\t$MODULE.forward1\tproc \(\): string\t
-> idetools --track:$TESTNIM,8,5 --def $SILENT
-def\tskProc\t$MODULE.forward2\tproc \(\): string\t
diff --git a/tests/caas/issue_477_dynamic_dispatch.nim b/tests/caas/issue_477_dynamic_dispatch.nim
deleted file mode 100644
index 6e6b21ef0..000000000
--- a/tests/caas/issue_477_dynamic_dispatch.nim
+++ /dev/null
@@ -1,19 +0,0 @@
-type
-  TThing = object of TObject
-  TUnit = object of TThing
-    x: int
-
-method collide(a, b: TThing) {.inline.} =
-  quit "to override!"
-
-method collide(a: TThing, b: TUnit) {.inline.} =
-  echo "collide1"
-
-method collide(a: TUnit, b: TThing) {.inline.} =
-  echo "collide2"
-
-var
-  a, b: TUnit
-
-when isMainModule:
-  collide(a, b) # output: 2
diff --git a/tests/caas/issue_477_dynamic_dispatch.txt b/tests/caas/issue_477_dynamic_dispatch.txt
deleted file mode 100644
index 12fd750de..000000000
--- a/tests/caas/issue_477_dynamic_dispatch.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-issue_477_dynamic_dispatch.nim
-> c --run
-SuccessX
-> idetools --track:issue_477_dynamic_dispatch.nim,19,5 --def $SILENT
-def\tskMethod\tissue_477_dynamic_dispatch.collide\tproc \(TUnit, TThing\)\{.inline.\}
diff --git a/tests/caas/its_full_of_procs.nim b/tests/caas/its_full_of_procs.nim
deleted file mode 100644
index 8f8b66764..000000000
--- a/tests/caas/its_full_of_procs.nim
+++ /dev/null
@@ -1,29 +0,0 @@
-import unicode, sequtils
-
-# This example shows that idetools returns proc as signature for everything
-# which can be called. While a clever person would use the second column to
-# differentiate between procs, methods and others, why does the output contain
-# incorrect information?
-
-type
-  TThing = object of TObject
-  TUnit = object of TThing
-    x: int
-
-method collide(a, b: TThing) {.inline.} =
-  quit "to override!"
-
-method collide(a: TThing, b: TUnit) {.inline.} =
-  echo "1"
-
-method collide(a: TUnit, b: TThing) {.inline.} =
-  echo "2"
-
-var
-  a, b: TUnit
-
-let
-  input = readFile("its_full_of_procs.nim")
-  letters = toSeq(runes(string(input)))
-
-collide(a, b) # output: 2
diff --git a/tests/caas/its_full_of_procs.txt b/tests/caas/its_full_of_procs.txt
deleted file mode 100644
index 31a2d3baa..000000000
--- a/tests/caas/its_full_of_procs.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-its_full_of_procs.nim
-
-> idetools --track:$TESTNIM,26,15 --def $SILENT
-skProc
-proc \(
-
-> idetools --track:$TESTNIM,27,21 --def $SILENT
-skIterator
-iterator \(
-!proc \(
-
-> idetools --track:$TESTNIM,29,0 --def $SILENT
-skMethod
-method \(
-!proc \(
-
-> idetools --track:$TESTNIM,27,15 --def $SILENT
-skTemplate
-template \(
-!proc \(
diff --git a/tests/caas/main.nim b/tests/caas/main.nim
deleted file mode 100644
index fafeff93b..000000000
--- a/tests/caas/main.nim
+++ /dev/null
@@ -1,7 +0,0 @@
-import imported, strutils
-
-proc main =
-  var t1 = "text"
-  var t2 = t1.toUpper
-  echo(t1 +++ t2)
-
diff --git a/tests/caas/main_dirty.nim b/tests/caas/main_dirty.nim
deleted file mode 100644
index 95fb6c624..000000000
--- a/tests/caas/main_dirty.nim
+++ /dev/null
@@ -1,14 +0,0 @@
-import imported, strutils
-
-type
-  TFoo = object
-    x: int
-    y: string
-
-proc main =
-  var t1 = "text"
-  var t2 = t1.toUpper
-  var foo = TFoo(x: 10, y: "test")
-  foo.
-  echo(t1 +++ t2)
-
diff --git a/tests/caas/suggest-compile.txt b/tests/caas/suggest-compile.txt
deleted file mode 100644
index a322908ac..000000000
--- a/tests/caas/suggest-compile.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-main.nim
-# This example shows how the suggest feature can be used on a partial file
-# using the --trackDirty switch.
-> idetools --trackDirty:main_dirty.nim,$TESTNIM,12,7 --suggest $SILENT
-skField\tx
-skField\ty
-# Repeating the query in caas should work always and retrieve same output.
-CaasRun > idetools --trackDirty:main_dirty.nim,$TESTNIM,12,7 --suggest $SILENT
-CaasRun skField\tx
-CaasRun skField\ty
-> c --verbosity:0 --hints:on
-SuccessX
-
diff --git a/tests/caas/suggest-invalid-source.txt b/tests/caas/suggest-invalid-source.txt
deleted file mode 100644
index 7f8f1213d..000000000
--- a/tests/caas/suggest-invalid-source.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-main_dirty.nim
-# A variant of the suggest-compile.txt, instead of using a "base" correct
-# source, this one uses the "broken" main_dirty.nim which won't compile. The
-# test tries to stress idetools to still provide a valid answer if possible,
-# and at least provide the same output with repeated queries rather than dying
-# after the first compilation error.
-
-# The first query should work and provide valid suggestions.
-> idetools --track:$TESTNIM,12,6 --suggest $SILENT
-skField\tx
-skField\ty
-
-# Repeating the query should work too.
-> idetools --track:$TESTNIM,12,6 --suggest $SILENT
-skField\tx
-skField\ty
-
-# Expect now a compilation failure.
-> c
-!SuccessX
-invalid indentation
-
-# Repeating suggestions *after broken compilation* should work too.
-> idetools --track:$TESTNIM,12,6 --suggest $SILENT
-skField\tx
-skField\ty
diff --git a/tests/casestmt/t18964.nim b/tests/casestmt/t18964.nim
new file mode 100644
index 000000000..1d2de2bbc
--- /dev/null
+++ b/tests/casestmt/t18964.nim
@@ -0,0 +1,12 @@
+discard """
+errormsg: "invalid order of case branches"
+"""
+
+import macros
+
+macro genCase(val: string): untyped =
+  result = nnkCaseStmt.newTree(val,
+    nnkElse.newTree(quote do: echo "else"),
+    nnkOfBranch.newTree(newLit("miauz"), quote do: echo "first branch"))
+
+genCase("miauz")
diff --git a/tests/casestmt/t7699.nim b/tests/casestmt/t7699.nim
new file mode 100644
index 000000000..1354551c1
--- /dev/null
+++ b/tests/casestmt/t7699.nim
@@ -0,0 +1,15 @@
+discard """
+  errormsg: "case statement cannot work on enums with holes for computed goto"
+  line: 13
+"""
+
+type
+  X = enum
+    A = 0, B = 100
+
+var z = A
+while true:
+  {.computedGoto.}
+  case z
+  of A: discard
+  of B: discard
diff --git a/tests/casestmt/tcase_arrayconstr.nim b/tests/casestmt/tcase_arrayconstr.nim
deleted file mode 100644
index cd7156600..000000000
--- a/tests/casestmt/tcase_arrayconstr.nim
+++ /dev/null
@@ -1,19 +0,0 @@
-discard """
-  output: '''Not found!
-Found!'''
-"""
-
-const
-  md_extension = [".md", ".markdown"]
-
-proc test(ext: string) =
-  case ext
-  of ".txt", md_extension:
-    echo "Found!"
-  else:
-    echo "Not found!"
-
-test(".something")
-# ensure it's not evaluated at compile-time:
-var foo = ".markdown"
-test(foo)
diff --git a/tests/casestmt/tcase_emptyset_when.nim b/tests/casestmt/tcase_emptyset_when.nim
deleted file mode 100644
index e9b1ec2df..000000000
--- a/tests/casestmt/tcase_emptyset_when.nim
+++ /dev/null
@@ -1,24 +0,0 @@
-discard """
-  file: "tcaseofwhen.nim"
-  outputsub: "compiles for 1\ni am always two\ndefault for 3\nset is 4 not 5\narray is 6 not 7\ndefault for 8"
-  exitcode: "0"
-"""
-
-proc whenCase(a: int) =
-  case a
-  of (when compiles(whenCase(1)): 1 else: {}): echo "compiles for 1"
-  of {}: echo "me not fail"
-  of 2: echo "i am always two"
-  of []: echo "me neither"
-  of {4,5}: echo "set is 4 not 5"
-  of [6,7]: echo "array is 6 not 7"
-  of (when compiles(neverCompilesIBet()): 3 else: {}): echo "compiles for 3"
-  #of {},[]: echo "me neither"
-  else: echo "default for ", a
-
-whenCase(1)
-whenCase(2)
-whenCase(3)
-whenCase(4)
-whenCase(6)
-whenCase(8)
diff --git a/tests/casestmt/tcase_issues.nim b/tests/casestmt/tcase_issues.nim
new file mode 100644
index 000000000..20a79df2c
--- /dev/null
+++ b/tests/casestmt/tcase_issues.nim
@@ -0,0 +1,7 @@
+discard """
+  targets: "c js"
+"""
+
+block: # bug #24031
+  case 0
+  else: discard
\ No newline at end of file
diff --git a/tests/casestmt/tcase_setconstr.nim b/tests/casestmt/tcase_setconstr.nim
deleted file mode 100644
index 21f657c2b..000000000
--- a/tests/casestmt/tcase_setconstr.nim
+++ /dev/null
@@ -1,15 +0,0 @@
-discard """
-  output: "an identifier"
-"""
-
-const
-  SymChars: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'}
-
-proc classify(s: string) =
-  case s[0]
-  of SymChars, '_': echo "an identifier"
-  of {'0'..'9'}: echo "a number"
-  else: echo "other"
-
-classify("Hurra")
-
diff --git a/tests/casestmt/tcaseexpr1.nim b/tests/casestmt/tcaseexpr1.nim
index 24543f1b8..4f5bbf100 100644
--- a/tests/casestmt/tcaseexpr1.nim
+++ b/tests/casestmt/tcaseexpr1.nim
@@ -1,13 +1,23 @@
 discard """
-  file: "tcaseexpr1.nim"
+  cmd: "nim check $options $file"
+  action: "reject"
+  nimout: '''
+tcaseexpr1.nim(33, 10) Error: not all cases are covered; missing: {C}
+tcaseexpr1.nim(39, 12) Error: type mismatch: got <string> but expected 'int literal(10)'
+'''
+"""
+
+
+
+
 
-  line: 29
-  errormsg: "type mismatch: got <string> but expected 'int'"
 
-  line: 23
-  errormsg: "not all cases are covered"
-"""
 
+
+
+
+
+# line 20
 type
   E = enum A, B, C
 
@@ -27,4 +37,3 @@ var t1 = case x:
 var t2 = case x:
   of A: 10
   of B, C: "23"
-
diff --git a/tests/casestmt/tcaseoverlaprange.nim b/tests/casestmt/tcaseoverlaprange.nim
index 3527c9385..e9651c69f 100644
--- a/tests/casestmt/tcaseoverlaprange.nim
+++ b/tests/casestmt/tcaseoverlaprange.nim
@@ -1,6 +1,6 @@
 discard """
-  line: 13
   errormsg: "duplicate case label"
+  line: 13
 """
 
 type
diff --git a/tests/casestmt/tcaseoverlaprange2.nim b/tests/casestmt/tcaseoverlaprange2.nim
index 4a9479a5f..4a1cb3ea6 100644
--- a/tests/casestmt/tcaseoverlaprange2.nim
+++ b/tests/casestmt/tcaseoverlaprange2.nim
@@ -1,6 +1,6 @@
 discard """
-  line: 13
   errormsg: "duplicate case label"
+  line: 13
 """
 
 
diff --git a/tests/casestmt/tcasestm.nim b/tests/casestmt/tcasestm.nim
deleted file mode 100644
index 4d32d023f..000000000
--- a/tests/casestmt/tcasestm.nim
+++ /dev/null
@@ -1,108 +0,0 @@
-discard """
-  file: "tcasestm.nim"
-  output: "ayyydd"
-"""
-# Test the case statement
-
-type
-  Tenum = enum eA, eB, eC
-
-var
-  x: string = "yyy"
-  y: Tenum = eA
-  i: int
-
-case y
-of eA: write(stdout, "a")
-of eB, eC: write(stdout, "b or c")
-
-case x
-of "Andreas", "Rumpf": write(stdout, "Hallo Meister!")
-of "aa", "bb": write(stdout, "Du bist nicht mein Meister")
-of "cc", "hash", "when": discard
-of "will", "it", "finally", "be", "generated": discard
-
-var z = case i
-  of 1..5, 8, 9: "aa"
-  of 6, 7: "bb"
-  elif x == "Ha":
-    "cc"
-  elif x == "yyy":
-    write(stdout, x)
-    "dd"
-  else:
-    "zz"
-
-echo z
-#OUT ayyy
-
-let str1 = "Y"
-let str2 = "NN"
-let a = case str1:
-  of "Y": true
-  of "N": false
-  else: 
-    echo "no good"
-    quit("quiting")
-
-proc toBool(s: string): bool = 
-  case s:
-    of nil, "": raise newException(ValueError, "Invalid boolean")
-    elif s[0] == 'Y': true
-    elif s[0] == 'N': false
-    else: "error".quit(2)
-
-
-let b = "NN".toBool()
-
-doAssert(a == true)
-doAssert(b == false)
-
-static:
-  #bug #7407
-  let bstatic = "N".toBool()
-  doAssert(bstatic == false)
-
-var bb: bool
-doassert(not compiles(
-  bb = case str2:
-    of nil, "": raise newException(ValueError, "Invalid boolean")
-    elif str.startsWith("Y"): true
-    elif str.startsWith("N"): false
-))
-
-doassert(not compiles(
-  bb = case str2:
-    of "Y": true
-    of "N": false
-))
-
-doassert(not compiles(
-  bb = case str2:
-    of "Y": true
-    of "N": raise newException(ValueError, "N not allowed")
-))
-
-doassert(not compiles(
-  bb = case str2:
-    of "Y": raise newException(ValueError, "Invalid Y")
-    else: raise newException(ValueError, "Invalid N")
-))
-
-
-doassert(not compiles(
-  bb = case str2:
-    of "Y":
-      raise newException(ValueError, "Invalid Y")
-      true    
-    else: raise newException(ValueError, "Invalid")
-))
-
-
-doassert(not compiles(
-  bb = case str2:
-    of "Y":
-      "invalid Y".quit(3)
-      true    
-    else: raise newException(ValueError, "Invalid")
-))
\ No newline at end of file
diff --git a/tests/casestmt/tcasestmt.nim b/tests/casestmt/tcasestmt.nim
new file mode 100644
index 000000000..66de4183d
--- /dev/null
+++ b/tests/casestmt/tcasestmt.nim
@@ -0,0 +1,319 @@
+discard """
+output:
+'''
+Not found!
+Found!
+1
+compiles for 1
+i am always two
+default for 3
+set is 4 not 5
+array is 6 not 7
+default for 8
+an identifier
+OK
+OK
+OK
+ayyydd
+'''
+"""
+
+
+block arrayconstr:
+  const md_extension = [".md", ".markdown"]
+
+  proc test(ext: string) =
+    case ext
+    of ".txt", md_extension:
+      echo "Found!"
+    else:
+      echo "Not found!"
+
+  test(".something")
+  # ensure it's not evaluated at compile-time:
+  var foo = ".markdown"
+  test(foo)
+
+
+converter toInt(x: char): int =
+  x.int
+block t8333:
+  case 0
+  of 'a': echo 0
+  else: echo 1
+block: # issue #11422
+  var c: int = 5
+  case c
+  of 'a' .. 'c': discard
+  else: discard
+
+
+block emptyset_when:
+  proc whenCase(a: int) =
+    case a
+    of (when compiles(whenCase(1)): 1 else: {}): echo "compiles for 1"
+    of {}: echo "me not fail"
+    of 2: echo "i am always two"
+    of []: echo "me neither"
+    of {4,5}: echo "set is 4 not 5"
+    of [6,7]: echo "array is 6 not 7"
+    of (when compiles(neverCompilesIBet()): 3 else: {}): echo "compiles for 3"
+    #of {},[]: echo "me neither"
+    else: echo "default for ", a
+
+  whenCase(1)
+  whenCase(2)
+  whenCase(3)
+  whenCase(4)
+  whenCase(6)
+  whenCase(8)
+
+
+block setconstr:
+  const
+    SymChars: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'}
+
+  proc classify(s: string) =
+    case s[0]
+    of SymChars, '_': echo "an identifier"
+    of {'0'..'9'}: echo "a number"
+    else: echo "other"
+
+  classify("Hurra")
+
+
+
+block tduplicates:
+  type Kind = enum A, B
+  var k = A
+
+  template reject(b) =
+    static: doAssert(not compiles(b))
+
+  reject:
+      var i = 2
+      case i
+      of [1, 1]: discard
+      else: discard
+
+  reject:
+      var i = 2
+      case i
+      of 1, { 1..2 }: discard
+      else: discard
+
+  reject:
+      var i = 2
+      case i
+      of { 1, 1 }: discard
+      of { 1, 1 }: discard
+      else: discard
+
+  reject:
+      case k
+      of [A, A]: discard
+
+  var i = 2
+  case i
+  of { 1, 1 }: discard
+  of { 2, 2 }: echo "OK"
+  else: discard
+
+  case i
+  of { 10..30, 15..25, 5..15, 25..35 }: discard
+  else: echo "OK"
+
+  case k
+  of {A, A..A}: echo "OK"
+  of B: discard
+
+
+block tcasestm:
+  type
+    Tenum = enum eA, eB, eC
+
+  var
+    x: string = "yyy"
+    y: Tenum = eA
+    i: int
+
+  case y
+  of eA: write(stdout, "a")
+  of eB, eC: write(stdout, "b or c")
+
+  case x
+  of "Andreas", "Rumpf": write(stdout, "Hallo Meister!")
+  of "aa", "bb": write(stdout, "Du bist nicht mein Meister")
+  of "cc", "hash", "when": discard
+  of "will", "it", "finally", "be", "generated": discard
+
+  var z = case i
+    of 1..5, 8, 9: "aa"
+    of 6, 7: "bb"
+    elif x == "Ha":
+      "cc"
+    elif x == "yyy":
+      write(stdout, x)
+      "dd"
+    else:
+      "zz"
+
+  echo z
+  #OUT ayyy
+
+  let str1 = "Y"
+  let str2 = "NN"
+  let a = case str1:
+    of "Y": true
+    of "N": false
+    else:
+      echo "no good"
+      quit("quitting")
+
+  proc toBool(s: string): bool =
+    case s:
+    of "": raise newException(ValueError, "Invalid boolean")
+    elif s[0] == 'Y': true
+    elif s[0] == 'N': false
+    else: "error".quit(2)
+
+
+  let b = "NN".toBool()
+
+  doAssert(a == true)
+  doAssert(b == false)
+
+  static:
+    #bug #7407
+    let bstatic = "N".toBool()
+    doAssert(bstatic == false)
+
+  var bb: bool
+  doAssert(not compiles(
+    bb = case str2:
+      of "": raise newException(ValueError, "Invalid boolean")
+      elif str.startsWith("Y"): true
+      elif str.startsWith("N"): false
+  ))
+
+  doAssert(not compiles(
+    bb = case str2:
+      of "Y": true
+      of "N": false
+  ))
+
+  doAssert(not compiles(
+    bb = case str2:
+      of "Y": true
+      of "N": raise newException(ValueError, "N not allowed")
+  ))
+
+  doAssert(not compiles(
+    bb = case str2:
+      of "Y": raise newException(ValueError, "Invalid Y")
+      else: raise newException(ValueError, "Invalid N")
+  ))
+
+
+  doAssert(not compiles(
+    bb = case str2:
+      of "Y":
+        raise newException(ValueError, "Invalid Y")
+        true
+      else: raise newException(ValueError, "Invalid")
+  ))
+
+
+  doAssert(not compiles(
+    bb = case str2:
+      of "Y":
+        "invalid Y".quit(3)
+        true
+      else: raise newException(ValueError, "Invalid")
+  ))
+
+#issue #11552
+
+proc positiveOrNegative(num: int): string =
+  result = case num
+  of (low(int)+2) .. -1:
+    "negative"
+  of 0:
+    "zero"
+  else:
+    "impossible"
+
+#issue #11551
+
+proc negativeOrNot(num: int): string =
+    result = case num
+    of low(int) .. -1:
+      "negative"
+    else:
+      "zero or positive"
+
+doAssert negativeOrNot(-1) == "negative"
+doAssert negativeOrNot(10000000) == "zero or positive"
+doAssert negativeOrNot(0) == "zero or positive"
+
+########################################################
+# issue #13490
+import strutils
+func foo(input: string): int =
+  try:
+    parseInt(input)
+  except:
+    return
+
+func foo2(b, input: string): int =
+  case b:
+    of "Y":
+      for c in input:
+        result =  if c in '0'..'9': parseInt($c)
+                  else: break
+    of "N":
+      for c in input:
+        result =  if c in '0'..'9': parseInt($c)
+                  else: continue
+    else: return
+
+
+static:
+  doAssert(foo("3") == 3)
+  doAssert(foo("a") == 0)
+  doAssert(foo2("Y", "a2") == 0)
+  doAssert(foo2("Y", "2a") == 2)
+  doAssert(foo2("N", "a3") == 3)
+  doAssert(foo2("z", "2") == 0)
+
+doAssert(foo("3") == 3)
+doAssert(foo("a") == 0)
+doAssert(foo2("Y", "a2") == 0)
+doAssert(foo2("Y", "2a") == 2)
+doAssert(foo2("N", "a3") == 3)
+doAssert(foo2("z", "2") == 0)
+
+
+# bug #20031
+proc main(a: uint64) =
+  case a
+  else:
+    discard
+
+static:
+  main(10)
+main(10)
+
+block:
+  # Just needs to compile
+  proc bar(): int {.discardable.} = discard
+
+  proc foo() {.noreturn.} = discard
+
+  case "*"
+  of "*":
+    bar()
+  else:
+    # Make sure this noreturn doesn't
+    # cause the discardable to not discard
+    foo()
diff --git a/tests/casestmt/tcomputedgoto.nim b/tests/casestmt/tcomputedgoto.nim
index 149072964..f7603dac3 100644
--- a/tests/casestmt/tcomputedgoto.nim
+++ b/tests/casestmt/tcomputedgoto.nim
@@ -1,16 +1,24 @@
 discard """
-  output: '''yeah A enumB
+  output: '''
+yeah A enumB
+uneven
 yeah A enumB
 yeah CD enumD
+uneven
 yeah CD enumE
 yeah A enumB
+uneven
 yeah CD enumE
 yeah CD enumD
+uneven
 yeah A enumB
 yeah B enumC
+uneven
+yeah A enumB
 yeah A enumB
+uneven
 yeah A enumB
-yeah A enumB'''
+'''
 """
 
 type
@@ -45,4 +53,7 @@ proc vm() =
     of enumLast: discard
     inc(pc)
 
+    if pc mod 2 == 1:
+      echo "uneven"
+
 vm()
diff --git a/tests/casestmt/tcstring.nim b/tests/casestmt/tcstring.nim
new file mode 100644
index 000000000..288373402
--- /dev/null
+++ b/tests/casestmt/tcstring.nim
@@ -0,0 +1,52 @@
+discard """
+  targets: "c cpp js"
+"""
+
+type Result = enum none, a, b, c, d, e, f
+
+proc foo1(x: cstring): Result =
+  const y = cstring"hash"
+  const arr = [cstring"it", cstring"finally"]
+  result = none
+  case x
+  of "Andreas", "Rumpf": result = a
+  of cstring"aa", "bb": result = b
+  of "cc", y, "when": result = c
+  of "will", arr, "be", "generated": result = d
+  of nil: result = f
+
+var results = [
+  foo1("Rumpf"), foo1("Andreas"),
+  foo1("aa"), foo1(cstring"bb"),
+  foo1("cc"), foo1("hash"),
+  foo1("finally"), foo1("generated"),
+  foo1("no"), foo1("another no"),
+  foo1(nil)]
+doAssert results == [a, a, b, b, c, c, d, d, none, none, f], $results
+
+proc foo2(x: cstring): Result =
+  const y = cstring"hash"
+  const arr = [cstring"it", cstring"finally"]
+  doAssert not (compiles do:
+    result = case x
+    of "Andreas", "Rumpf": a
+    of cstring"aa", "bb": b
+    of "cc", y, "when": c
+    of "will", arr, "be", "generated": d)
+  case x
+  of "Andreas", "Rumpf": a
+  of cstring"aa", "bb": b
+  of "cc", y, "when": c
+  of "will", arr, "be", "generated": d
+  of nil: f
+  else: e
+
+results = [
+  foo2("Rumpf"), foo2("Andreas"),
+  foo2("aa"), foo2(cstring"bb"),
+  foo2("cc"), foo2("hash"),
+  foo2("finally"), foo2("generated"),
+  foo2("no"), foo2("another no"),
+  foo2(nil)]
+
+doAssert results == [a, a, b, b, c, c, d, d, e, e, f], $results
diff --git a/tests/casestmt/tduplicates.nim b/tests/casestmt/tduplicates.nim
deleted file mode 100644
index f9fc1cc26..000000000
--- a/tests/casestmt/tduplicates.nim
+++ /dev/null
@@ -1,50 +0,0 @@
-discard """
-  output: '''
-OK
-OK
-OK
-  '''
-"""
-
-type Kind = enum A, B
-var k = A
-
-template reject(b) =
-  static: doAssert(not compiles(b))
-
-reject:
-    var i = 2
-    case i
-    of [1, 1]: discard
-    else: discard
-
-reject:
-    var i = 2
-    case i
-    of 1, { 1..2 }: discard
-    else: discard
-
-reject:
-    var i = 2
-    case i
-    of { 1, 1 }: discard
-    of { 1, 1 }: discard
-    else: discard
-
-reject:
-    case k
-    of [A, A]: discard
-
-var i = 2
-case i
-of { 1, 1 }: discard
-of { 2, 2 }: echo "OK"
-else: discard
-
-case i
-of { 10..30, 15..25, 5..15, 25..35 }: discard
-else: echo "OK"
-
-case k
-of {A, A..A}: echo "OK"
-of B: discard
\ No newline at end of file
diff --git a/tests/casestmt/tincompletecaseobject.nim b/tests/casestmt/tincompletecaseobject.nim
new file mode 100644
index 000000000..aa5deda7a
--- /dev/null
+++ b/tests/casestmt/tincompletecaseobject.nim
@@ -0,0 +1,115 @@
+discard """
+errormsg: '''
+not all cases are covered; missing: {nnkComesFrom, nnkDotCall, nnkHiddenCallConv, nnkVarTuple, nnkCurlyExpr, nnkRange, nnkCheckedFieldExpr, nnkDerefExpr, nnkElifExpr, nnkElseExpr, nnkLambda, nnkDo, nnkBind, nnkClosedSymChoice, nnkHiddenSubConv, nnkConv, nnkStaticExpr, nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv, nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange, nnkStringToCString, nnkCStringToString, nnkFastAsgn, nnkGenericParams, nnkFormalParams, nnkOfInherit, nnkImportAs, nnkConverterDef, nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch, nnkElifBranch, nnkExceptBranch, nnkElse, nnkAsmStmt, nnkTypeDef, nnkFinally, nnkContinueStmt, nnkImportStmt, nnkImportExceptStmt, nnkExportStmt, nnkExportExceptStmt, nnkFromStmt, nnkIncludeStmt, nnkUsingStmt, nnkBlockExpr, nnkStmtListType, nnkBlockType, nnkWith, nnkWithout, nnkTypeOfExpr, nnkObjectTy, nnkTupleTy, nnkTupleClassTy, nnkTypeClassTy, nnkStaticTy, nnkRecList, nnkRecCase, nnkRecWhen, nnkVarTy, nnkConstTy, nnkMutableTy, nnkDistinctTy, nnkProcTy, nnkIteratorTy, nnkSharedTy, nnkEnumTy, nnkEnumFieldDef, nnkArgList, nnkPattern, nnkReturnToken, nnkClosure, nnkGotoState, nnkState, nnkBreakState, nnkFuncDef, nnkTupleConstr}
+'''
+"""
+
+# this isn't imported from macros.nim to make it robust against possible changes in the ast.
+
+type
+  NimNodeKind* = enum
+    nnkNone, nnkEmpty, nnkIdent, nnkSym,
+    nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit,
+    nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkUIntLit, nnkUInt8Lit,
+    nnkUInt16Lit, nnkUInt32Lit, nnkUInt64Lit, nnkFloatLit,
+    nnkFloat32Lit, nnkFloat64Lit, nnkFloat128Lit, nnkStrLit, nnkRStrLit,
+    nnkTripleStrLit, nnkNilLit, nnkComesFrom, nnkDotCall,
+    nnkCommand, nnkCall, nnkCallStrLit, nnkInfix,
+    nnkPrefix, nnkPostfix, nnkHiddenCallConv,
+    nnkExprEqExpr,
+    nnkExprColonExpr, nnkIdentDefs, nnkVarTuple,
+    nnkPar, nnkObjConstr, nnkCurly, nnkCurlyExpr,
+    nnkBracket, nnkBracketExpr, nnkPragmaExpr, nnkRange,
+    nnkDotExpr, nnkCheckedFieldExpr, nnkDerefExpr, nnkIfExpr,
+    nnkElifExpr, nnkElseExpr, nnkLambda, nnkDo, nnkAccQuoted,
+    nnkTableConstr, nnkBind,
+    nnkClosedSymChoice,
+    nnkOpenSymChoice,
+    nnkHiddenStdConv,
+    nnkHiddenSubConv, nnkConv, nnkCast, nnkStaticExpr,
+    nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv,
+    nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange,
+    nnkStringToCString, nnkCStringToString, nnkAsgn,
+    nnkFastAsgn, nnkGenericParams, nnkFormalParams, nnkOfInherit,
+    nnkImportAs, nnkProcDef, nnkMethodDef, nnkConverterDef,
+    nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch,
+    nnkElifBranch, nnkExceptBranch, nnkElse,
+    nnkAsmStmt, nnkPragma, nnkPragmaBlock, nnkIfStmt, nnkWhenStmt,
+    nnkForStmt, nnkParForStmt, nnkWhileStmt, nnkCaseStmt,
+    nnkTypeSection, nnkVarSection, nnkLetSection, nnkConstSection,
+    nnkConstDef, nnkTypeDef,
+    nnkYieldStmt, nnkDefer, nnkTryStmt, nnkFinally, nnkRaiseStmt,
+    nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt,
+    nnkDiscardStmt, nnkStmtList,
+    nnkImportStmt = 1337, # make a hole just for fun
+    nnkImportExceptStmt,
+    nnkExportStmt,
+    nnkExportExceptStmt,
+    nnkFromStmt,
+    nnkIncludeStmt,
+    nnkBindStmt, nnkMixinStmt, nnkUsingStmt,
+    nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
+    nnkStmtListType, nnkBlockType,
+    nnkWith, nnkWithout,
+    nnkTypeOfExpr, nnkObjectTy,
+    nnkTupleTy, nnkTupleClassTy, nnkTypeClassTy, nnkStaticTy,
+    nnkRecList, nnkRecCase, nnkRecWhen,
+    nnkRefTy, nnkPtrTy, nnkVarTy,
+    nnkConstTy, nnkMutableTy,
+    nnkDistinctTy,
+    nnkProcTy,
+    nnkIteratorTy,         # iterator type
+    nnkSharedTy,           # 'shared T'
+    nnkEnumTy,
+    nnkEnumFieldDef,
+    nnkArgList, nnkPattern
+    nnkReturnToken,
+    nnkClosure,
+    nnkGotoState,
+    nnkState,
+    nnkBreakState,
+    nnkFuncDef,
+    nnkTupleConstr
+
+const
+  nnkLiterals* = {nnkCharLit..nnkNilLit}
+
+  nnkSomething* = {nnkStmtList, nnkStmtListExpr, nnkDiscardStmt, nnkVarSection, nnkLetSection,
+       nnkConstSection, nnkPar, nnkAccQuoted, nnkAsgn, nnkDefer, nnkCurly, nnkBracket,
+       nnkStaticStmt, nnkTableConstr, nnkExprColonExpr, nnkInfix, nnkPrefix,
+       nnkRaiseStmt, nnkYieldStmt, nnkBracketExpr, nnkDotExpr, nnkCast, nnkBlockStmt,
+       nnkExprEqExpr}
+
+type
+  MyFictionalType = object
+    a: int
+    case n: NimNodeKind
+    of nnkLiterals, nnkCommentStmt, nnkNone, nnkEmpty, nnkIdent, nnkSym,
+       nnkType, nnkBindStmt, nnkMixinStmt, nnkTypeSection, nnkPragmaBlock,
+       nnkPragmaExpr, nnkPragma, nnkBreakStmt, nnkCallStrLit, nnkPostfix,
+       nnkOpenSymChoice:
+      b: int
+    of nnkCall, nnkCommand:
+      c: int
+    of nnkReturnStmt:
+      d: int
+    of nnkForStmt, nnkParForStmt, nnkWhileStmt, nnkProcDef, nnkMethodDef:
+      e: int
+    of nnkSomething, nnkRefTy, nnkPtrTy, nnkHiddenStdConv:
+      f: int
+    of nnkObjConstr:
+      g: int
+    of nnkIfStmt, nnkIfExpr, nnkWhenStmt:
+      # if when and case statements are branching statements. So a
+      # single function call is allowed to be in all of the braches and
+      # the entire expression can still be considered as a forwarding
+      # template.
+      h: int
+    of nnkCaseStmt:
+      i: int
+    of nnkTryStmt:
+      j: int
+    of nnkIdentDefs:
+      k: int
+    of nnkConstDef:
+      l: int
diff --git a/tests/casestmt/tincompletecaseobject2.nim b/tests/casestmt/tincompletecaseobject2.nim
new file mode 100644
index 000000000..bbeae1909
--- /dev/null
+++ b/tests/casestmt/tincompletecaseobject2.nim
@@ -0,0 +1,26 @@
+discard """
+cmd: "nim check $file"
+"""
+type
+  ABCD = enum A, B, C, D
+  AliasABCD = ABCD
+  RangeABC = range[A .. C]
+  AliasRangeABC = RangeABC
+  PrintableChars = range[' ' .. '~']
+
+case PrintableChars 'x': #[tt.Error
+^ not all cases are covered; missing: {' ', '!', '\"', '#', '$$', '%', '&', '\'', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~'}]#
+of '0'..'9', 'A'..'Z', 'a'..'z': discard
+of '(', ')': discard
+
+case AliasABCD A: #[tt.Error
+^ not all cases are covered; missing: {B, C, D}]#
+of A: discard
+
+case RangeABC A: #[tt.Error
+^ not all cases are covered; missing: {A, C}]#
+of B: discard
+
+case AliasRangeABC A: #[tt.Error
+^ not all cases are covered; missing: {A, B}]#
+of C: discard
diff --git a/tests/casestmt/tlinearscanend.nim b/tests/casestmt/tlinearscanend.nim
index 9a984e039..96e3727d5 100644
--- a/tests/casestmt/tlinearscanend.nim
+++ b/tests/casestmt/tlinearscanend.nim
@@ -1,3 +1,6 @@
+discard """
+action: compile
+"""
 
 import strutils
 
@@ -21,4 +24,3 @@ of 21: echo "21"
 else:
   {.linearScanEnd.}
   echo "default"
-
diff --git a/tests/casestmt/trangeexhaustiveness.nim b/tests/casestmt/trangeexhaustiveness.nim
new file mode 100644
index 000000000..2b7f3558e
--- /dev/null
+++ b/tests/casestmt/trangeexhaustiveness.nim
@@ -0,0 +1,7 @@
+block: # issue #22661
+  template foo(a: typed) =
+    a
+    
+  foo:
+    case false
+    of false..true: discard
diff --git a/tests/cast/tcast.nim b/tests/cast/tcast.nim
new file mode 100644
index 000000000..205444ea3
--- /dev/null
+++ b/tests/cast/tcast.nim
@@ -0,0 +1,21 @@
+discard """
+  targets: "c cpp js"
+"""
+
+proc main() =
+  block: # bug #16806
+    let
+      a = 42u16
+      b = cast[int16](a)
+    doAssert a.int16 == 42
+    doAssert b in int16.low..int16.high
+
+  block: # bug #16808
+    doAssert cast[int8](cast[uint8](int8(-12))) == int8(-12)
+    doAssert cast[int16](cast[uint16](int16(-12))) == int16(-12)
+    doAssert cast[int32](cast[uint32](int32(-12))) == int32(-12)
+
+  doAssert cast[int8](int16.high) == -1
+
+static: main()
+main()
diff --git a/tests/ccgbugs/m1/defs.nim b/tests/ccgbugs/m1/defs.nim
new file mode 100644
index 000000000..ed78d8b72
--- /dev/null
+++ b/tests/ccgbugs/m1/defs.nim
@@ -0,0 +1,4 @@
+type MyObj* = object
+  field1*: int
+  s*: string
+  ch*: char
diff --git a/tests/ccgbugs/m19445.c b/tests/ccgbugs/m19445.c
new file mode 100644
index 000000000..74c23d4b4
--- /dev/null
+++ b/tests/ccgbugs/m19445.c
@@ -0,0 +1,3 @@
+#include "m19445.h"
+
+const Foo f = {10, 20, 30, 40};
\ No newline at end of file
diff --git a/tests/ccgbugs/m2/defs.nim b/tests/ccgbugs/m2/defs.nim
new file mode 100644
index 000000000..798d1fea8
--- /dev/null
+++ b/tests/ccgbugs/m2/defs.nim
@@ -0,0 +1,4 @@
+type MyObj* = object
+  s*: string
+  field1*: int
+  ch*: char
diff --git a/tests/ccgbugs/mstatic_assert.nim b/tests/ccgbugs/mstatic_assert.nim
new file mode 100644
index 000000000..dbf9c03d1
--- /dev/null
+++ b/tests/ccgbugs/mstatic_assert.nim
@@ -0,0 +1,6 @@
+{.emit:"""
+NIM_STATIC_ASSERT(sizeof(bool) == 1, "");
+#warning "foo2"
+NIM_STATIC_ASSERT(sizeof(bool) == 2, "");
+#warning "foo3"
+""".}
diff --git a/tests/ccgbugs/pkg8616/rtarray.nim b/tests/ccgbugs/pkg8616/rtarray.nim
new file mode 100644
index 000000000..286dbb8cd
--- /dev/null
+++ b/tests/ccgbugs/pkg8616/rtarray.nim
@@ -0,0 +1,2 @@
+proc head*[T](pp: var array[1,T]): var T =
+  result = pp[0]
diff --git a/tests/ccgbugs/pkg8616/scheduler.nim b/tests/ccgbugs/pkg8616/scheduler.nim
new file mode 100644
index 000000000..0730000c4
--- /dev/null
+++ b/tests/ccgbugs/pkg8616/scheduler.nim
@@ -0,0 +1,10 @@
+import rtarray
+
+type
+  T = tuple[x:int]
+
+var
+  arr: array[1,T]
+
+proc init*() =
+  discard head(arr)
diff --git a/tests/ccgbugs/t10128.nim b/tests/ccgbugs/t10128.nim
new file mode 100644
index 000000000..48970916f
--- /dev/null
+++ b/tests/ccgbugs/t10128.nim
@@ -0,0 +1,18 @@
+# bug #10128
+let data = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+var seq2 = newSeq[char](data.len)
+for i in 0..<data.len:
+  seq2[i] = data[i]
+
+let c = '\128'
+
+# case 1
+doAssert data[c.int] == 'y'
+doAssert seq2[c.int] == 'y'
+
+proc play(x: openArray[char]) =
+  doAssert x[c.int] == 'y'
+
+# case2
+play(data)
+play(seq2)
\ No newline at end of file
diff --git a/tests/ccgbugs/t10964.nim b/tests/ccgbugs/t10964.nim
new file mode 100644
index 000000000..c19db6997
--- /dev/null
+++ b/tests/ccgbugs/t10964.nim
@@ -0,0 +1,7 @@
+func test*(input: var openArray[int32], start: int = 0, fin: int = input.len - 1) =

+    discard

+

+var someSeq = @[1'i32]

+

+test(someSeq)

+# bug with gcc 14
\ No newline at end of file
diff --git a/tests/ccgbugs/t13062.nim b/tests/ccgbugs/t13062.nim
new file mode 100644
index 000000000..cfda1da7c
--- /dev/null
+++ b/tests/ccgbugs/t13062.nim
@@ -0,0 +1,33 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c cpp"
+"""
+
+import atomics
+
+type
+  Pledge* {.exportc.} = object
+    p: PledgePtr
+
+  PledgeKind {.exportc.} = enum
+    Single
+    Iteration
+
+  PledgePtr {.exportc.} = ptr object
+    case kind: PledgeKind
+    of Single:
+      impl: PledgeImpl
+    of Iteration:
+      discard
+
+  PledgeImpl {.exportc.} = object
+    fulfilled: Atomic[bool]
+
+var x: Pledge
+when defined(cpp):
+  # TODO: fixme
+  discard "it doesn't work for refc/orc because of contrived `Atomic` in cpp"
+elif defined(gcRefc):
+  doAssert x.repr == "[p = nil]"
+else: # fixme # bug #20081
+  doAssert x.repr == "Pledge(p: nil)"
diff --git a/tests/ccgbugs/t13902.nim b/tests/ccgbugs/t13902.nim
new file mode 100644
index 000000000..fd4f76d15
--- /dev/null
+++ b/tests/ccgbugs/t13902.nim
@@ -0,0 +1,12 @@
+
+#issue #13902
+block:
+  type Slot = distinct uint64
+  var s = Slot(1)
+  proc `$`(x: Slot): string {.borrow.}
+  proc `+=`(x: var Slot, y: uint64) {.borrow.}
+  # test was failing with either 0 or 2 echos but not with 1 echo
+  # echo "s = ", s
+  s += 1
+  # echo "s = ", s
+  doAssert s.uint64 == 2, $s # was failing, showing 18419607611339964418
diff --git a/tests/ccgbugs/t15428.nim b/tests/ccgbugs/t15428.nim
new file mode 100644
index 000000000..d9ae8ff16
--- /dev/null
+++ b/tests/ccgbugs/t15428.nim
@@ -0,0 +1,22 @@
+discard """

+    cmd: "nim $target --mm:refc $file"

+    output: '''5

+5

+[1, 2, 3, 4, 5]

+(data: [1, 2, 3, 4, 5])

+'''

+"""

+

+proc take[T](f: openArray[T]) =

+  echo f.len

+let f = @[0,1,2,3,4]

+take(f.toOpenArray(0,4))

+

+{.experimental: "views".}

+type

+  Foo = object

+    data: openArray[int]

+let f2 = Foo(data: [1,2,3,4,5])

+echo f2.data.len

+echo f2.data

+echo f2
\ No newline at end of file
diff --git a/tests/ccgbugs/t16027.nim b/tests/ccgbugs/t16027.nim
new file mode 100644
index 000000000..58f15eb6e
--- /dev/null
+++ b/tests/ccgbugs/t16027.nim
@@ -0,0 +1,13 @@
+discard """
+  ccodecheck: "__restrict__"
+  action: compile
+  joinable: false
+"""
+
+# see bug #16027
+iterator myitems(s: seq[int]): int =
+  var data {.codegenDecl: "$# __restrict__ $#".} : ptr int = nil
+  yield 1
+
+for i in @[1].myitems:
+  discard
diff --git a/tests/ccgbugs/t16374.nim b/tests/ccgbugs/t16374.nim
new file mode 100644
index 000000000..8ccfa4815
--- /dev/null
+++ b/tests/ccgbugs/t16374.nim
@@ -0,0 +1,38 @@
+discard """
+  matrix: "--gc:refc; --gc:orc"
+"""
+
+block:
+  iterator mvalues(t: var seq[seq[int]]): var seq[int] =
+    yield t[0]
+
+  var t: seq[seq[int]]
+
+  while false:
+    for v in t.mvalues:
+      discard
+
+  proc ok =
+    while false:
+      for v in t.mvalues:
+        discard
+
+  ok()
+
+block:
+  iterator mvalues(t: var seq[seq[int]]): lent seq[int] =
+    yield t[0]
+
+  var t: seq[seq[int]]
+
+  while false:
+    for v in t.mvalues:
+      discard
+
+  proc ok =
+    while false:
+      for v in t.mvalues:
+        discard
+
+  ok()
+
diff --git a/tests/ccgbugs/t19445.nim b/tests/ccgbugs/t19445.nim
new file mode 100644
index 000000000..b6e8d028c
--- /dev/null
+++ b/tests/ccgbugs/t19445.nim
@@ -0,0 +1,13 @@
+discard """
+  matrix: "--nimcache:tests/ccgbugs/nimcache19445 --cincludes:nimcache19445 --header:m19445"
+  targets: "c"
+"""
+
+# bug #19445
+type
+  Foo* {.exportc.} = object
+    a*, b*, c*, d*: int
+
+proc dummy(): Foo {.exportc.} = discard
+
+{.compile:"m19445.c".}
\ No newline at end of file
diff --git a/tests/ccgbugs/t20139.nim b/tests/ccgbugs/t20139.nim
new file mode 100644
index 000000000..4592b994d
--- /dev/null
+++ b/tests/ccgbugs/t20139.nim
@@ -0,0 +1,10 @@
+discard """
+  joinable: false
+"""
+
+# bug #20139
+import m1/defs as md1
+import m2/defs as md2
+
+doAssert $(md1.MyObj(field1: 1)) == """(field1: 1, s: "", ch: '\x00')"""
+doAssert $(md2.MyObj(field1: 1)) == """(s: "", field1: 1, ch: '\x00')"""
diff --git a/tests/ccgbugs/t20141.nim b/tests/ccgbugs/t20141.nim
new file mode 100644
index 000000000..60e130690
--- /dev/null
+++ b/tests/ccgbugs/t20141.nim
@@ -0,0 +1,27 @@
+discard """
+  joinable: false
+"""
+
+# bug #20141
+type
+  A = object
+  B = object
+  U = proc()
+
+proc m(h: var B) = discard
+
+template n[T, U](x: U): T =
+  static: doAssert true
+  cast[ptr T](addr x)[]
+
+proc k() =
+  var res: A
+  m(n[B, A](res))
+
+proc w(mounter: U) = discard
+
+proc mount(proto: U) = discard
+proc v() = mount k
+
+# This is required for failure
+w(v)
diff --git a/tests/ccgbugs/t20787.nim b/tests/ccgbugs/t20787.nim
new file mode 100644
index 000000000..c2d848c2c
--- /dev/null
+++ b/tests/ccgbugs/t20787.nim
@@ -0,0 +1,4 @@
+type
+  Obj = object
+    f: UncheckedArray[byte]
+let o = new Obj
\ No newline at end of file
diff --git a/tests/ccgbugs/t21116.nim b/tests/ccgbugs/t21116.nim
new file mode 100644
index 000000000..cc77de198
--- /dev/null
+++ b/tests/ccgbugs/t21116.nim
@@ -0,0 +1,10 @@
+discard """
+  targets: "c cpp"
+  disabled: windows
+"""
+# bug #21116
+import std/os
+
+proc p(glob: string) =
+  for _ in walkFiles(glob): discard
+p("dir/*")
diff --git a/tests/ccgbugs/t21972.nim b/tests/ccgbugs/t21972.nim
new file mode 100644
index 000000000..58d0cfc62
--- /dev/null
+++ b/tests/ccgbugs/t21972.nim
@@ -0,0 +1,33 @@
+discard """

+    targets: "c cpp"

+    outputsub: "Error: unhandled exception: Err2 [IOError]"

+    exitcode: "1"

+"""

+

+proc bar(x: var int) =

+  inc x

+  if x == 3:

+    raise newException(ValueError, "H0")

+

+  elif x == 5:

+    raise newException(IOError, "H1")

+

+  elif x > 7:

+    raise newException(IOError, "H2")

+

+

+proc foo() =

+  var i = 0

+  while true:

+    try:

+      bar(i)

+      echo i

+

+    except ValueError:

+      debugEcho("ValueError")

+

+    except IOError:

+      raise newException(IOError, "Err2")

+

+when isMainModule:

+  foo()
\ No newline at end of file
diff --git a/tests/ccgbugs/t21995.nim b/tests/ccgbugs/t21995.nim
new file mode 100644
index 000000000..0ec88aa59
--- /dev/null
+++ b/tests/ccgbugs/t21995.nim
@@ -0,0 +1,9 @@
+discard """
+    targets: "c cpp"
+    output: "Hi!"
+"""
+
+try:
+  raise
+except:
+  echo "Hi!"
\ No newline at end of file
diff --git a/tests/ccgbugs/t22462.nim b/tests/ccgbugs/t22462.nim
new file mode 100644
index 000000000..9adfbb19b
--- /dev/null
+++ b/tests/ccgbugs/t22462.nim
@@ -0,0 +1,20 @@
+discard """
+  action: "run"
+  output: '''
+1
+1
+1
+'''
+  matrix: "--mm:refc"
+  targets: "c cpp"
+"""
+
+type Object = object
+  someComplexType: seq[int]
+  index: Natural
+
+func newObject(): Object = result.index.inc
+
+for i in 1..3:
+  let o = newObject()
+  echo o.index
diff --git a/tests/ccgbugs/t23796.nim b/tests/ccgbugs/t23796.nim
new file mode 100644
index 000000000..421ec04d8
--- /dev/null
+++ b/tests/ccgbugs/t23796.nim
@@ -0,0 +1,25 @@
+discard """
+    targets: "c cpp"
+"""
+
+# bug #23796
+
+{.emit: """
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void fooArr(float data[3]) {}
+void fooIntArr(int id, float data[3]) {}
+
+#ifdef __cplusplus
+}
+#endif
+""".}
+
+proc fooArr(data: var array[3, cfloat]) {.importc.}
+proc fooIntArr(id: cint, data: var array[3, cfloat]) {.importc, nodecl.}
+
+var arr = [cfloat 1, 2, 3]
+fooArr(arr)
+fooIntArr(1, arr)
diff --git a/tests/ccgbugs/t2procs.nim b/tests/ccgbugs/t2procs.nim
new file mode 100644
index 000000000..d8b7a2815
--- /dev/null
+++ b/tests/ccgbugs/t2procs.nim
@@ -0,0 +1,18 @@
+discard """
+  output: '''before
+1
+before
+2'''
+"""
+
+proc fn[T1, T2](a: T1, b: T2) =
+  a(1)
+  b(2)
+
+fn( (proc(x: int) =
+      echo "before" # example block, can span multiple lines
+      echo x),
+    (proc (y: int) =
+      echo "before"
+      echo y)
+)
diff --git a/tests/ccgbugs/t5296.nim b/tests/ccgbugs/t5296.nim
index 990b4bee2..8fbed35c4 100644
--- a/tests/ccgbugs/t5296.nim
+++ b/tests/ccgbugs/t5296.nim
@@ -1,6 +1,7 @@
 discard """
 cmd: "nim c -d:release $file"
-output: 1
+output: '''1
+-1'''
 """
 
 proc bug() : void =
@@ -12,3 +13,9 @@ proc bug() : void =
         echo x
 
 bug()
+
+# bug #19051
+type GInt[T] = int
+
+var a = 1
+echo -a
diff --git a/tests/ccgbugs/t5345.nim b/tests/ccgbugs/t5345.nim
new file mode 100644
index 000000000..f9ee833c4
--- /dev/null
+++ b/tests/ccgbugs/t5345.nim
@@ -0,0 +1,10 @@
+discard """
+  output: true
+"""
+
+proc cmpx(d: int): bool {.inline.} = d > 0
+
+proc abc[C](cx: C, d: int) =
+  echo cx(d)
+  
+abc(cmpx, 10)
diff --git a/tests/ccgbugs/t6756.nim b/tests/ccgbugs/t6756.nim
index 0f08557eb..5990eba58 100644
--- a/tests/ccgbugs/t6756.nim
+++ b/tests/ccgbugs/t6756.nim
@@ -1,10 +1,16 @@
+discard """
+output: '''
+(v: 3)
+'''
+"""
+
 import typetraits
 type
   A[T] = ref object
     v: T
 
 template templ(o: A, op: untyped): untyped =
-  type T = type(o.v)
+  type T = typeof(o.v)
 
   var res: A[T]
 
diff --git a/tests/ccgbugs/t8616.nim b/tests/ccgbugs/t8616.nim
new file mode 100644
index 000000000..5fd940d3b
--- /dev/null
+++ b/tests/ccgbugs/t8616.nim
@@ -0,0 +1,4 @@
+import pkg8616 / scheduler
+
+when true:
+  init()
diff --git a/tests/ccgbugs/t8781.nim b/tests/ccgbugs/t8781.nim
new file mode 100644
index 000000000..884c6962a
--- /dev/null
+++ b/tests/ccgbugs/t8781.nim
@@ -0,0 +1,25 @@
+discard """
+output: ""
+"""
+
+type
+  Drawable = object of RootObj
+    discard
+
+  # issue #8781, following type was broken due to 'U' suffix
+  # on `animatedU`. U also added as union identifier for C.
+  # replaced by "_U" prefix, which is not allowed as an
+  # identifier
+  TypeOne = ref object of Drawable
+    animatedU: bool
+    case animated: bool
+    of true:
+        frames: seq[int]
+    of false:
+        region: float
+
+when true:
+  let r = 1.5
+  let a = TypeOne(animatedU: true,
+                  animated: false,
+                  region: r)
diff --git a/tests/ccgbugs/t8967.nim b/tests/ccgbugs/t8967.nim
new file mode 100644
index 000000000..0301a2e4f
--- /dev/null
+++ b/tests/ccgbugs/t8967.nim
@@ -0,0 +1,14 @@
+discard """
+  targets: "c cpp"
+"""
+
+import marshal
+
+template main() =
+  let orig: set[char] = {'A'..'Z'}
+  let m = $$orig
+  let old = to[set[char]](m)
+  doAssert orig - old == {}
+
+static: main()
+main()
diff --git a/tests/ccgbugs/t9098.nim b/tests/ccgbugs/t9098.nim
new file mode 100644
index 000000000..e1dbb6883
--- /dev/null
+++ b/tests/ccgbugs/t9098.nim
@@ -0,0 +1,12 @@
+discard """
+  targets: "c cpp js"
+  output: '''
+{'a', 'b'}
+'''
+"""
+
+var x = new(ref set[char])
+var y = new(ref set[char])
+x[] = {'a'}
+y[] = {'b'}
+echo x[] + y[]
diff --git a/tests/ccgbugs/t9286.nim b/tests/ccgbugs/t9286.nim
new file mode 100644
index 000000000..2fec23307
--- /dev/null
+++ b/tests/ccgbugs/t9286.nim
@@ -0,0 +1,22 @@
+discard """
+  action: run
+"""
+
+import options
+type Foo  = ref object
+  i:      int
+
+proc next(foo: Foo): Option[Foo] =
+  try:    doAssert(foo.i == 0)
+  except: return      # 2º: none
+  return some(foo)    # 1º: some
+
+proc test =
+  let foo = Foo()
+  var opt = next(foo) # 1º Some
+  while isSome(opt) and foo.i < 10:
+    inc(foo.i)
+    opt = next(foo)   # 2º None
+  doAssert foo.i == 1, $foo.i
+
+test()
diff --git a/tests/ccgbugs/t9578.nim b/tests/ccgbugs/t9578.nim
new file mode 100644
index 000000000..25b7b6695
--- /dev/null
+++ b/tests/ccgbugs/t9578.nim
@@ -0,0 +1,76 @@
+discard """
+output: '''
+@[(v: -1), (v: 2), (v: 3)]
+@[(v: -1), (v: 2), (v: 3)]
+[(v: -1), (v: 2), (v: 3)]
+[(v: -1), (v: 2), (v: 3)]
+((v: -1), (v: 2), (v: 3))
+((v: -1), (v: 2), (v: 3))
+@[(v: -1), (v: 2), (v: 3)]
+@[(v: -1), (v: 2), (v: 3)]
+@[(v: -1), (v: 2), (v: 3)]
+'''
+"""
+
+type mytype* = object
+  v:int
+
+proc f*(x:ptr mytype) = x.v = -1
+
+func g(x:int):mytype = mytype(v:x)
+
+
+import xseq9578
+block:
+  var x = @[1.g,2.g,3.g]
+  testSeq(x)
+  echo x
+block:
+  var x = @[1.g,2.g,3.g]
+  var y = addr x
+  testSeq2(y)
+  echo x
+
+
+import xarray9578
+block:
+  var x = [1.g,2.g,3.g]
+  testArray(x)
+  echo x
+block:
+  var x = [1.g,2.g,3.g]
+  var y = addr x
+  testArray2(y)
+  echo x
+
+
+import xtuple9578
+block:
+  var x = (1.g,2.g,3.g)
+  testTuple(x)
+  echo x
+block:
+  var x = (1.g,2.g,3.g)
+  var y = addr x
+  testTuple2(y)
+  echo x
+
+
+import xoa9578
+block:
+  var x = @[1.g,2.g,3.g]
+  testOpenArray(x)
+  echo x
+
+
+import xua9578
+block:
+  var x = @[1.g,2.g,3.g]
+  var y = cast[ptr UncheckedArray[mytype]](addr x[0])
+  testUncheckedArray(y[])
+  echo x
+block:
+  var x = @[1.g,2.g,3.g]
+  var y = cast[ptr UncheckedArray[mytype]](addr x[0])
+  testUncheckedArray2(y)
+  echo x
diff --git a/tests/ccgbugs/t9655.nim b/tests/ccgbugs/t9655.nim
new file mode 100644
index 000000000..29fb903a4
--- /dev/null
+++ b/tests/ccgbugs/t9655.nim
@@ -0,0 +1,30 @@
+discard """
+  action: "compile"
+"""
+
+import std/[asynchttpserver, asyncdispatch]
+import std/[strformat]
+
+proc main() =
+  let local = "123"
+
+  proc serveIndex(req: Request) {.async, gcsafe.} =
+    await req.respond(Http200, &"{local}")
+
+  proc serve404(req: Request) {.async, gcsafe.} =
+    echo req.url.path
+    await req.respond(Http404, "not found")
+
+  proc serve(req: Request) {.async, gcsafe.} =
+    let handler = case req.url.path:
+      of "/":
+        serveIndex
+      else:
+        serve404
+    await handler(req)
+
+  let server = newAsyncHttpServer()
+  waitFor server.serve(Port(8080), serve, address = "127.0.0.1")
+
+when isMainModule:
+  main()
diff --git a/tests/ccgbugs/taddhigh.nim b/tests/ccgbugs/taddhigh.nim
index 549eb8caa..6b0658612 100644
--- a/tests/ccgbugs/taddhigh.nim
+++ b/tests/ccgbugs/taddhigh.nim
@@ -14,6 +14,6 @@ s.add x
 s.add s[s.high]
 
 s.add s[s.len-1]
-s.add s[s.xlen-1]
+s.add s[s.len-1]
 
 echo s # @[5, 5, 0]
diff --git a/tests/ccgbugs/targ_lefttoright.nim b/tests/ccgbugs/targ_lefttoright.nim
new file mode 100644
index 000000000..a0adce157
--- /dev/null
+++ b/tests/ccgbugs/targ_lefttoright.nim
@@ -0,0 +1,71 @@
+discard """
+  nimout: '''1,2
+2,3
+2,2
+1,2
+1,2
+2,2
+1,2
+'''
+  output: '''1,2
+2,3
+1,2
+2,2
+1,2
+1,2
+2,2
+1,2
+'''
+  cmd: "nim c --gc:orc $file"
+"""
+
+template test =
+  proc say(a, b: int) =
+    echo a,",",b
+
+  var a = 1
+  say a, (a += 1; a) #1,2
+
+  var b = 1
+  say (b += 1; b), (b += 1; b) #2,3
+
+  type C {.byRef.} = object
+    i: int
+
+  proc say(a, b: C) =
+    echo a.i,",",b.i
+
+  proc `+=`(x: var C, y: C) = x.i += y.i
+
+  var c = C(i: 1)
+  when nimvm: #XXX: This would output 2,2 in the VM, which is wrong
+    discard
+  else:
+    say c, (c += C(i: 1); c) #1,2
+
+  proc sayVar(a: var int, b: int) =
+    echo a,",",b
+
+  var d = 1
+  sayVar d, (d += 1; d) #2,2
+
+  var e = 1
+  say (addr e)[], (e += 1; e) #1,2
+
+  var f = 1
+  say f, if false: f
+         else: f += 1; f #1,2
+
+  var g = 1
+  say g + 1, if false: g
+             else: g += 1; g #2,2
+
+  proc `+=+`(x: var int, y: int): int = (inc(x, y); x)
+
+  var h = 1
+  say h, h +=+ 1 # 1,2
+
+test
+
+static:
+  test
diff --git a/tests/ccgbugs/tassign_nil_strings.nim b/tests/ccgbugs/tassign_nil_strings.nim
new file mode 100644
index 000000000..e32bfcade
--- /dev/null
+++ b/tests/ccgbugs/tassign_nil_strings.nim
@@ -0,0 +1,13 @@
+discard """
+  matrix: "--mm:refc"
+  output: "Hello"
+  ccodecheck: "\\i@'a = ((NimStringDesc*) NIM_NIL)'"
+"""
+
+proc main() =
+  var a = "Hello"
+  echo a
+  a = ""
+  doAssert a.len == 0
+
+main()
diff --git a/tests/ccgbugs/tborrowmagic.nim b/tests/ccgbugs/tborrowmagic.nim
new file mode 100644
index 000000000..8d42ddcd8
--- /dev/null
+++ b/tests/ccgbugs/tborrowmagic.nim
@@ -0,0 +1,8 @@
+type
+  Bytes = distinct seq[byte]
+
+proc add(x: var Bytes; b: byte) {.borrow.}
+var x = @[].Bytes
+x.add(42)
+let base = cast[seq[byte]](x)
+doAssert base.len == 1 and base[0] == 42
diff --git a/tests/ccgbugs/tbug21505.nim b/tests/ccgbugs/tbug21505.nim
new file mode 100644
index 000000000..0c0811ec5
--- /dev/null
+++ b/tests/ccgbugs/tbug21505.nim
@@ -0,0 +1,39 @@
+discard """
+    action: "compile"
+    targets: "cpp"
+    cmd: "nim cpp $file"
+"""
+
+# see #21505: ensure compilation of imported C++ objects with explicit constructors while retaining default initialization through codegen changes due to #21279
+
+{.emit:"""/*TYPESECTION*/
+
+struct ExplObj
+{
+  explicit ExplObj(int bar = 0) {}  
+};
+
+struct BareObj
+{
+    BareObj() {}
+};
+
+""".}
+
+type
+  ExplObj {.importcpp.} = object
+  BareObj {.importcpp.} = object
+
+type
+  Composer = object
+    explObj: ExplObj
+    bareObj: BareObj
+
+proc foo =
+  var composer1 {.used.}: Composer
+  let composer2 {.used.} = Composer()
+
+var composer1 {.used.}: Composer
+let composer2 {.used.} = Composer()
+
+foo()
\ No newline at end of file
diff --git a/tests/ccgbugs/tccgen1.nim b/tests/ccgbugs/tccgen1.nim
index 4917c9848..be571de08 100644
--- a/tests/ccgbugs/tccgen1.nim
+++ b/tests/ccgbugs/tccgen1.nim
@@ -7,7 +7,7 @@ type
     Features: seq[Feature] # Read-Only
 
   PNode* = ref Node
-  Node = object {.inheritable.}
+  Node {.inheritable.} = object
     attributes*: seq[PAttr]
     childNodes*: seq[PNode]
     FLocalName: string # Read-only
diff --git a/tests/ccgbugs/tccgissues.nim b/tests/ccgbugs/tccgissues.nim
new file mode 100644
index 000000000..8207ccbba
--- /dev/null
+++ b/tests/ccgbugs/tccgissues.nim
@@ -0,0 +1,14 @@
+discard """
+  output: '''
+@[1, 2, 3, 4]
+'''
+"""
+
+# issue #10999
+
+proc varargsToSeq(vals: varargs[int32]): seq[int32] =
+  result = newSeqOfCap[int32](vals.len)
+  for v in vals:
+    result.add v
+
+echo varargsToSeq(1, 2, 3, 4)
diff --git a/tests/ccgbugs/tcgbug.nim b/tests/ccgbugs/tcgbug.nim
index 1c6466c87..2eddc6fdd 100644
--- a/tests/ccgbugs/tcgbug.nim
+++ b/tests/ccgbugs/tcgbug.nim
@@ -1,6 +1,10 @@
 discard """
-  file: "tcgbug.nim"
-  output: "success"
+output: '''
+success
+M1 M2
+ok
+'''
+matrix: "--mm:refc;--mm:orc"
 """
 
 type
@@ -21,6 +25,7 @@ q(a)
 
 # bug #914
 when defined(windows):
+  import std/widestrs
   var x = newWideCString("Hello")
 
 echo "success"
@@ -36,3 +41,123 @@ type
 
 var k = PFuture[void]()
 
+
+##bug #9297 and #13281
+
+import strutils
+
+type
+  MyKind = enum
+    M1, M2, M3
+
+  MyObject {.exportc: "ExtObject"} = object
+    case kind: MyKind
+      of M1: a:int
+      of M2: b:float
+      of M3: c:cstring
+
+  MyObjectRef {.exportc: "ExtObject2"} = ref object
+    case kind: MyKind
+      of M1: a:int
+      of M2: b:float
+      of M3: c:cstring
+
+  Helper* {.exportc: "PublicHelper".} = object
+    case isKind: bool
+      of true:
+        formatted: string
+      of false:
+        parsed1: string
+        parsed2: string
+
+proc newMyObject(kind: MyKind, val: string): MyObject =
+  result = MyObject(kind: kind)
+
+  case kind
+    of M1: result.a = parseInt(val)
+    of M2: result.b = parseFloat(val)
+    of M3: result.c = val
+
+proc newMyObjectRef(kind: MyKind, val: string): MyObjectRef =
+  result = MyObjectRef(kind: kind)
+  case kind
+    of M1: result.a = parseInt(val)
+    of M2: result.b = parseFloat(val)
+    of M3: result.c = val
+
+
+echo newMyObject(M1, "2").kind, " ", newMyObjectRef(M2, "3").kind
+
+
+proc test(c: Helper): string =
+  c.formatted
+
+echo test(Helper(isKind: true, formatted: "ok"))
+
+
+# bug #19613
+
+type
+  Eth2Digest = object
+    data: array[42, byte]
+
+  BlockId* = object
+    root*: Eth2Digest
+
+  BlockSlotId* = object
+    bid*: BlockId
+    slot*: uint64
+
+func init*(T: type BlockSlotId, bid: BlockId, slot: uint64): T =
+  #debugecho "init ", bid, " ", slot
+  BlockSlotId(bid: bid, slot: slot)
+
+proc bug19613 =
+  var x: BlockSlotId
+  x.bid.root.data[0] = 42
+
+  x =
+    if x.slot > 0:
+      BlockSlotId.init(x.bid, x.slot)
+    else:
+      BlockSlotId.init(x.bid, x.slot)
+  doAssert x.bid.root.data[0] == 42
+
+bug19613()
+
+proc foo = # bug #23280
+  let foo = @[1,2,3,4,5,6]
+  doAssert toOpenArray(foo, 0, 5).len == 6
+  doAssert toOpenArray(foo, 0, 5).len mod 6 == 0 # this should output 0
+  doAssert toOpenArray(foo, 0, 5).max mod 6 == 0
+  let L = toOpenArray(foo, 0, 5).len
+  doAssert L mod 6 == 0 
+
+foo()
+
+block: # bug #9940
+  {.emit:"""/*TYPESECTION*/
+typedef struct { int base; } S;
+""".}
+
+  type S {.importc: "S", completeStruct.} = object
+    base: cint
+  proc init(x:ptr S) =
+    x.base = 1
+
+  type
+    Foo = object
+      a: seq[float]
+      b: seq[float]
+      c: seq[float]
+      d: seq[float]
+      s: S
+
+  proc newT(): Foo =
+    var t: Foo
+    t.s.addr.init
+    doAssert t.s.base == 1
+    t
+
+  var t = newT()
+  doAssert t.s.base == 1
diff --git a/tests/ccgbugs/tcodegenbug1.nim b/tests/ccgbugs/tcodegenbug1.nim
index fce74de0c..d2ab97ede 100644
--- a/tests/ccgbugs/tcodegenbug1.nim
+++ b/tests/ccgbugs/tcodegenbug1.nim
@@ -1,13 +1,18 @@
 discard """
+  matrix: "--mm:refc"
   output: '''obj = (inner: (kind: Just, id: 7))
 obj.inner.id = 7
 id = 7
-obj = (inner: (kind: Just, id: 7))'''
+obj = (inner: (kind: Just, id: 7))
+2
+(a: "a", b: "b", c: "")
+caught
+(a: "a", b: "b", c: "")'''
 """
 
 # bug #6960
 
-import future
+import sugar
 type
   Kind = enum None, Just, Huge
   Inner = object
@@ -105,3 +110,76 @@ type
 
 proc bug5137(d: MyIntDistinct) =
   discard d.MyInt
+
+#-------------------------------------
+# bug #8979
+
+type
+  MyKind = enum
+    Fixed, Float
+
+  MyObject = object
+    someInt: int
+    case kind: MyKind
+      of Float: index: string
+      of Fixed: nil
+
+  MyResult = object
+    val: array[0..1, string]
+    vis: set[0..1]
+
+import macros
+
+func myfunc(obj: MyObject): MyResult {.raises: [].} =
+  template index: auto =
+    case obj.kind:
+      of Float: $obj.index
+      of Fixed: "Fixed"
+  macro to_str(a: untyped): string =
+    result = newStrLitNode(a.repr)
+  result.val[0] = index
+  result.val[1] = to_str(obj.kind + Ola)
+
+let x = MyObject(someInt: 10, kind: Fixed)
+echo myfunc(x).val.len
+
+# bug #14126
+
+type X = object
+  a, b, c: string
+
+proc f(): X =
+  result.a = "a"
+  result.b = "b"
+  raise (ref ValueError)()
+
+proc ohmanNoNRVO =
+  var x: X
+  x.a = "1"
+  x.b = "2"
+  x.c = "3"
+
+  try:
+    x = f()
+  except:
+    discard
+
+  echo x
+  # once NVRO is sorted out, x.c == "3"
+  doAssert x.c == "", "shouldn't modify x if f raises"
+
+ohmanNoNRVO()
+
+proc ohmanNoNRVO2(x: var X) =
+  x.a = "1"
+  x.c = "3"
+  x = f()
+
+var xgg: X
+try:
+  ohmanNoNRVO2(xgg)
+except:
+  echo "caught"
+echo xgg
+# once NVRO is sorted out, xgg.c == "3"
+doAssert xgg.c == "", "this assert will fail"
diff --git a/tests/ccgbugs/tcodegenbug_bool.nim b/tests/ccgbugs/tcodegenbug_bool.nim
new file mode 100644
index 000000000..a0dbf4eb2
--- /dev/null
+++ b/tests/ccgbugs/tcodegenbug_bool.nim
@@ -0,0 +1,11 @@
+discard """
+"""
+
+# issue #13798
+{.emit:"""
+#include <stdbool.h>
+void fun(bool a){}
+""".}
+
+proc fun(a: bool) {.importc.}
+fun(true)
diff --git a/tests/ccgbugs/tcodegendecllambda.nim b/tests/ccgbugs/tcodegendecllambda.nim
new file mode 100644
index 000000000..814dcf206
--- /dev/null
+++ b/tests/ccgbugs/tcodegendecllambda.nim
@@ -0,0 +1,14 @@
+discard """
+  targets: "c cpp js"
+  ccodecheck: "'HELLO'"
+  action: compile
+"""
+
+when defined(js):
+  var foo = proc(): void{.codegenDecl: "/*HELLO*/function $2($3)".} =
+    echo "baa"
+else:
+  var foo = proc(): void{.codegenDecl: "/*HELLO*/$1 $2 $3".} =
+    echo "baa"
+
+foo()
diff --git a/tests/ccgbugs/tcompile_time_var_at_runtime.nim b/tests/ccgbugs/tcompile_time_var_at_runtime.nim
new file mode 100644
index 000000000..c0de0390b
--- /dev/null
+++ b/tests/ccgbugs/tcompile_time_var_at_runtime.nim
@@ -0,0 +1,11 @@
+discard """
+  output: "1\n2\n2\n3"
+"""
+var a {.compileTime.} = 1
+
+echo a
+a = 2
+echo a
+echo a
+a = 3
+echo a 
\ No newline at end of file
diff --git a/tests/ccgbugs/tconstobj.nim b/tests/ccgbugs/tconstobj.nim
deleted file mode 100644
index 51cf661ee..000000000
--- a/tests/ccgbugs/tconstobj.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-discard """
-  output: '''(FirstName: "James", LastName: "Franco")'''
-"""
-
-# bug #1547
-import tables
-
-type Person* = object
-    FirstName*: string
-    LastName*: string
-
-let people = {
-    "001": Person(FirstName: "James", LastName: "Franco")
-}.toTable()
-
-echo people["001"]
diff --git a/tests/ccgbugs/tctypes.nim b/tests/ccgbugs/tctypes.nim
new file mode 100644
index 000000000..be6009115
--- /dev/null
+++ b/tests/ccgbugs/tctypes.nim
@@ -0,0 +1,43 @@
+discard """
+  targets: "c cpp"
+  matrix: "--gc:refc; --gc:arc"
+"""
+
+# bug #7308
+proc foo(x: seq[int32]) =
+  var y = newSeq[cint](1)
+
+proc bar =
+  var t = newSeq[int32](1)
+  foo(t)
+
+bar()
+
+
+# bug #16246
+
+proc testWeirdTypeAliases() =
+  var values = newSeq[cuint](8)
+  # var values: seq[cuint] does not produce codegen error
+  var drawCb = proc(): seq[uint32] =
+    result = newSeq[uint32](10)
+
+testWeirdTypeAliases()
+
+block: # bug #11797
+  block:
+    type cdouble2 = cdouble
+    type Foo1 = seq[cdouble]
+    type Foo2 = seq[cdouble2]
+    static: doAssert Foo1 is Foo2
+    var a1: Foo1
+    var a2: Foo2
+    doAssert a1 == @[]
+    doAssert a2 == @[]
+
+  block:
+    proc foo[T: int|cint](fun: proc(): T) = discard
+    proc foo1(): cint = 1
+    proc foo3(): int32 = 2
+    foo(proc(): cint = foo1())
+    foo(proc(): int32 = foo3())
diff --git a/tests/ccgbugs/tcvarargs.nim b/tests/ccgbugs/tcvarargs.nim
index ebaf83a4a..261885f4f 100644
--- a/tests/ccgbugs/tcvarargs.nim
+++ b/tests/ccgbugs/tcvarargs.nim
@@ -12,6 +12,7 @@ discard """
 
 {.emit: """
 #include <stdarg.h>
+#include <stdio.h>
 
 void foo(int n, ...) {
   NI64 k;
diff --git a/tests/ccgbugs/tdeepcopy_addr_rval.nim b/tests/ccgbugs/tdeepcopy_addr_rval.nim
index 07fb8f8ef..4a0b0deaa 100644
--- a/tests/ccgbugs/tdeepcopy_addr_rval.nim
+++ b/tests/ccgbugs/tdeepcopy_addr_rval.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc --deepcopy:on"
   output: "3"
 """
 
diff --git a/tests/ccgbugs/tderefblock.nim b/tests/ccgbugs/tderefblock.nim
new file mode 100644
index 000000000..d3ba07667
--- /dev/null
+++ b/tests/ccgbugs/tderefblock.nim
@@ -0,0 +1,76 @@
+discard """
+  matrix: "--mm:refc -d:release -d:danger;--mm:orc -d:useMalloc -d:release -d:danger"
+  output: "42"
+"""
+
+# bug #20107
+
+type Foo = object
+  a, b, c, d: uint64
+
+proc c(i: uint64): Foo =
+  Foo(a: i, b: i, c: i, d: i)
+
+func x(f: Foo): lent Foo {.inline.} =
+  f
+
+proc m() =
+  let f = block:
+    let i = c(42)
+    x(i)
+
+  echo $f.a
+
+m()
+
+block: # bug #21540
+  type
+    Option = object
+      val: string
+      has: bool
+
+  proc some(val: string): Option =
+    result.has = true
+    result.val = val
+
+  # Remove lent and it works
+  proc get(self: Option): lent string =
+    result = self.val
+
+  type
+    StringStream = ref object
+      data: string
+      pos: int
+
+  proc readAll(s: StringStream): string =
+    result = newString(s.data.len)
+    copyMem(addr(result[0]), addr(s.data[0]), s.data.len)
+
+  proc newStringStream(s: string = ""): StringStream =
+    new(result)
+    result.data = s
+
+  proc parseJson(s: string): string =
+    let stream = newStringStream(s)
+    result = stream.readAll()
+
+  proc main =
+    let initialFEN = block:
+      let initialFEN = some parseJson("startpos")
+      initialFEN.get
+
+    doAssert initialFEN == "startpos"
+
+  main()
+
+import std/[
+    json,
+    options
+]
+
+block: # bug #21540
+  let cheek = block:
+    let initialFEN = some("""{"initialFen": "startpos"}""".parseJson{"initialFen"}.getStr)
+    initialFEN.get
+
+  doAssert cheek == "startpos"
diff --git a/tests/ccgbugs/tforward_decl_only.nim b/tests/ccgbugs/tforward_decl_only.nim
index 2a867bc3b..b115dcbe7 100644
--- a/tests/ccgbugs/tforward_decl_only.nim
+++ b/tests/ccgbugs/tforward_decl_only.nim
@@ -1,5 +1,5 @@
 discard """
-ccodecheck: "\\i !@('struct tyObject_MyRefObject'[0-z]+' {')"
+ccodecheck: "\\i !@('struct tyObject_MyRefObject'[0-z]+' _')"
 output: "hello"
 """
 
diff --git a/tests/ccgbugs/tgeneric_closure.nim b/tests/ccgbugs/tgeneric_closure.nim
index f9d5e7910..9f3c5b446 100644
--- a/tests/ccgbugs/tgeneric_closure.nim
+++ b/tests/ccgbugs/tgeneric_closure.nim
@@ -1,4 +1,10 @@
-
+discard """
+output: '''
+2
+2
+2
+'''
+"""
 
 # bug 2659
 
@@ -9,7 +15,7 @@ type
 proc mult(x:int, y:var int) =
   y = 2 * x
 
-when isMainModule:
+when true:
 
   var input = 1
   var output = 0
diff --git a/tests/ccgbugs/tgeneric_smallobj_asgn_opt.nim b/tests/ccgbugs/tgeneric_smallobj_asgn_opt.nim
index 919dc3fc1..3788b9985 100644
--- a/tests/ccgbugs/tgeneric_smallobj_asgn_opt.nim
+++ b/tests/ccgbugs/tgeneric_smallobj_asgn_opt.nim
@@ -1,5 +1,5 @@
 discard """
-  output: '''false'''
+  output: "done generic smallobj asgn opt"
 """
 
 # bug #5402
@@ -23,4 +23,5 @@ proc newListOfContainers[T](): ListOfContainers[T] =
   result.list = initDoublyLinkedList[Container[T]]()
 
 let q = newListOfContainers[int64]()
-echo q.contains(123)
+if not q.contains(123):
+  echo "done generic smallobj asgn opt"
diff --git a/tests/ccgbugs/thtiobj.nim b/tests/ccgbugs/thtiobj.nim
new file mode 100644
index 000000000..6db24dad0
--- /dev/null
+++ b/tests/ccgbugs/thtiobj.nim
@@ -0,0 +1,9 @@
+discard """
+  matrix: "--mm:refc"
+  targets: "c cpp"
+"""
+
+import typeinfo
+
+var x = ""
+discard (getPointer(toAny(x)))
diff --git a/tests/ccgbugs/tissues.nim b/tests/ccgbugs/tissues.nim
new file mode 100644
index 000000000..a0c402cc0
--- /dev/null
+++ b/tests/ccgbugs/tissues.nim
@@ -0,0 +1,38 @@
+discard """
+action: compile
+"""
+
+# bug #2233
+type MalType = object
+  fun: proc: MalType
+
+proc f(x: proc: MalType) =
+  discard x()
+
+f(nil)
+
+# bug #2823
+
+type A = object #of RootObj <-- Uncomment this to get no errors
+  test: proc(i: A): bool
+var a: proc(i: A): bool # Or comment this line to get no errors
+
+
+# bug #2703
+type
+  fooObj[T] = object of RootObj
+  bazObj[T] = object of fooObj[T]
+    x: T
+
+var troz: fooObj[string]
+echo bazObj[string](troz).x
+
+
+# bug #14880
+type step = object
+  exec: proc ()
+
+const pipeline = @[step()]
+
+let crash = pipeline[0]
+
diff --git a/tests/ccgbugs/tlvalueconv.nim b/tests/ccgbugs/tlvalueconv.nim
new file mode 100644
index 000000000..b2cb11eef
--- /dev/null
+++ b/tests/ccgbugs/tlvalueconv.nim
@@ -0,0 +1,32 @@
+discard """
+  matrix: "--gc:refc; --gc:arc"
+"""
+
+# bug #14160
+
+type
+  TPassContext = object of RootObj
+  PPassContext = ref TPassContext
+
+  PCtx = ref object of TPassContext
+    a: int
+
+  ModuleGraph = object
+    vm: RootRef
+
+proc main() =
+  var g = ModuleGraph(vm: new(Pctx))
+  PCtx(g.vm) = nil #This generates invalid C code
+  doAssert g.vm == nil
+
+main()
+
+# bug #14325
+
+proc main2() =
+  var g = ModuleGraph(vm: new(Pctx))
+  PPassContext(PCtx(g.vm)) = nil #This compiles, but crashes at runtime with gc:arc
+  doAssert g.vm == nil
+
+main2()
+
diff --git a/tests/ccgbugs/tmangle.nim b/tests/ccgbugs/tmangle.nim
new file mode 100644
index 000000000..0050cef92
--- /dev/null
+++ b/tests/ccgbugs/tmangle.nim
@@ -0,0 +1,16 @@
+block:
+  proc hello() =
+    let NAN_INFINITY = 12
+    doAssert NAN_INFINITY == 12
+    let INF = "2.0"
+    doAssert INF == "2.0"
+    let NAN = 2.3
+    doAssert NAN == 2.3
+
+  hello()
+
+block:
+  proc hello(NAN: float) =
+    doAssert NAN == 2.0
+
+  hello(2.0)
diff --git a/tests/ccgbugs/tmangle_field.nim b/tests/ccgbugs/tmangle_field.nim
index 9e4012b8b..da2720aaa 100644
--- a/tests/ccgbugs/tmangle_field.nim
+++ b/tests/ccgbugs/tmangle_field.nim
@@ -3,7 +3,7 @@ discard """
 
 # bug #5404
 
-import parseopt2
+import parseopt
 
 {.emit: """typedef struct {
     int key;
@@ -12,5 +12,5 @@ import parseopt2
 type foo* {.importc: "foo", nodecl.} = object
   key* {.importc: "key".}: cint
 
-for kind, key, value in parseopt2.getopt():
+for kind, key, value in parseopt.getopt():
   discard
diff --git a/tests/ccgbugs/tmarkerproc_regression.nim b/tests/ccgbugs/tmarkerproc_regression.nim
index 261f746fc..3b606b834 100644
--- a/tests/ccgbugs/tmarkerproc_regression.nim
+++ b/tests/ccgbugs/tmarkerproc_regression.nim
@@ -1,5 +1,5 @@
 discard """
-  output: "done"
+  output: "done markerproc regression"
 """
 
 type
@@ -34,14 +34,14 @@ proc foo(x: string): VersionRange =
   result.ver = Version(x)
 
 proc main =
-  var a: array[5000, VersionRange]
-  for i in 0 ..< 5000:
+  var a: array[500, VersionRange]
+  for i in 0 ..< 500:
     a[i] = foo($i & "some longer text here " & $i)
   GC_fullcollect()
-  for i in 0 ..< 5000:
+  for i in 0 ..< 500:
     let expected = $i & "some longer text here " & $i
     if a[i].ver.string != expected:
       quit "bug!"
-  echo "done"
+  echo "done markerproc regression"
 
 main()
diff --git a/tests/ccgbugs/tmissingbracket.nim b/tests/ccgbugs/tmissingbracket.nim
index 886884d0c..2919efe0e 100644
--- a/tests/ccgbugs/tmissingbracket.nim
+++ b/tests/ccgbugs/tmissingbracket.nim
@@ -1,6 +1,8 @@
 discard """
-  output: '''Subobject test called
-5'''
+output: '''
+Subobject test called
+5
+'''
 """
 
 type
@@ -9,7 +11,7 @@ type
     className* : string
   TClassOfTobj = object of TClassOfTCustomObject
     nil
-  TCustomObject = ref object {.inheritable.}
+  TCustomObject {.inheritable.} = ref object
     class* : ptr TClassOfTCustomObject
   TObj = ref object of TCustomObject
     data: int
@@ -38,7 +40,7 @@ type
         t:int
     SubObject* = object of TestObj
 
-method test*(t:var TestObj) =
+method test*(t:var TestObj) {.base.} =
     echo "test called"
 
 method test*(t:var SubObject) =
@@ -49,4 +51,3 @@ var a: SubObject
 
 a.test()
 echo a.t
-
diff --git a/tests/ccgbugs/tmissingderef.nim b/tests/ccgbugs/tmissingderef.nim
index 26418800a..eb7da3023 100644
--- a/tests/ccgbugs/tmissingderef.nim
+++ b/tests/ccgbugs/tmissingderef.nim
@@ -1,6 +1,5 @@
 discard """
   output: '''[10, 0, 0, 0, 0, 0, 0, 0]
-
 255
 1 1
 0.5'''
diff --git a/tests/ccgbugs/tmissingderef2.nim b/tests/ccgbugs/tmissingderef2.nim
index 59cd24dd1..23be61bcb 100644
--- a/tests/ccgbugs/tmissingderef2.nim
+++ b/tests/ccgbugs/tmissingderef2.nim
@@ -4,7 +4,7 @@ discard """
 
 # bug #5079
 
-import tables, strutils
+import tables
 
 type Test = ref object
   s: string
diff --git a/tests/ccgbugs/tmissinginit.nim b/tests/ccgbugs/tmissinginit.nim
index d440608e6..9eb58221c 100644
--- a/tests/ccgbugs/tmissinginit.nim
+++ b/tests/ccgbugs/tmissinginit.nim
@@ -1,10 +1,11 @@
 discard """
+  matrix: "--mm:refc"
   output: '''0
 0
 0
 0
-[[a = nil,
-b = nil]]'''
+[[a = "",
+b = []]]'''
 """
 
 # bug #1475
@@ -27,4 +28,4 @@ echo bug()[0]
 echo bug()[0]
 echo bug()[0]
 
-when isMainModule: test()
+test()
diff --git a/tests/ccgbugs/tmissingvolatile.nim b/tests/ccgbugs/tmissingvolatile.nim
index 4d25e5c22..b877eff71 100644
--- a/tests/ccgbugs/tmissingvolatile.nim
+++ b/tests/ccgbugs/tmissingvolatile.nim
@@ -1,7 +1,8 @@
 discard """
   output: "1"
-  cmd: r"nim c --hints:on $options -d:release $file"
+  cmd: r"nim c --hints:on $options --mm:refc -d:release $file"
   ccodecheck: "'NI volatile state;'"
+  targets: "c"
 """
 
 # bug #1539
diff --git a/tests/ccgbugs/tnil_type.nim b/tests/ccgbugs/tnil_type.nim
new file mode 100644
index 000000000..9921b24a3
--- /dev/null
+++ b/tests/ccgbugs/tnil_type.nim
@@ -0,0 +1,15 @@
+discard """
+  targets: "c cpp"
+"""
+
+proc f1(v: typeof(nil)) = discard
+f1(nil)
+
+proc f2[T]() = discard
+f2[typeof(nil)]()
+
+proc f3(_: typedesc) = discard
+f3(typeof(nil))
+
+proc f4[T](_: T) = discard
+f4(nil)
diff --git a/tests/ccgbugs/tnoalias.nim b/tests/ccgbugs/tnoalias.nim
new file mode 100644
index 000000000..2c3c2f0f4
--- /dev/null
+++ b/tests/ccgbugs/tnoalias.nim
@@ -0,0 +1,13 @@
+discard """
+  ccodecheck: "\\i@'NI* NIM_NOALIAS field;' @'NIM_CHAR* NIM_NOALIAS x_p0,' @'void* NIM_NOALIAS q'"
+"""
+
+type
+  BigNum = object
+    field {.noalias.}: ptr UncheckedArray[int]
+
+proc p(x {.noalias.}: openArray[char]) =
+  var q {.noalias.}: pointer = addr(x[0])
+
+var bn: BigNum
+p "abc"
diff --git a/tests/ccgbugs/tnocodegen_for_compiletime.nim b/tests/ccgbugs/tnocodegen_for_compiletime.nim
index b44e9f8c9..3a952e303 100644
--- a/tests/ccgbugs/tnocodegen_for_compiletime.nim
+++ b/tests/ccgbugs/tnocodegen_for_compiletime.nim
@@ -1,7 +1,7 @@
 # bug #1679
 import macros, tables, hashes
 proc hash(v: NimNode): Hash = 4  # performance is for suckers
-macro test(body: untyped): typed =
+macro test(body: untyped) =
   var a = initCountTable[NimNode]()
   a.inc(body)
 
diff --git a/tests/ccgbugs/tobjconstr_bad_aliasing.nim b/tests/ccgbugs/tobjconstr_bad_aliasing.nim
index ea51ecacb..550f9ab75 100644
--- a/tests/ccgbugs/tobjconstr_bad_aliasing.nim
+++ b/tests/ccgbugs/tobjconstr_bad_aliasing.nim
@@ -1,5 +1,9 @@
 discard """
-  output: '''(10, (20, ))'''
+  output: '''(10, (20, ))
+42
+(x: 900.0, y: 900.0)
+(x: 900.0, y: 900.0)
+(x: 900.0, y: 900.0)'''
 """
 
 import strutils, sequtils
@@ -22,5 +26,51 @@ proc dosomething(): seq[TThing] =
 
   result = @[TThing(data: 10, children: result)]
 
-when isMainModule:
-  echo($dosomething()[0])
+echo($dosomething()[0])
+
+
+# bug #9844
+
+proc f(v: int): int = v
+
+type X = object
+  v: int
+
+var x = X(v: 42)
+
+x = X(v: f(x.v))
+echo x.v
+
+
+# bug #11525
+type
+  Point[T] = object
+    x, y: T
+
+proc adjustPos[T](width, height: int, pos: Point[T]): Point[T] =
+  result = pos
+
+  result = Point[T](
+    x: pos.x - (width / 2),
+    y: pos.y - (height / 2)
+  )
+
+proc adjustPos2[T](width, height: int, pos: Point[T]): Point[T] =
+  result = pos
+
+  result = Point[T](
+    x: result.x - (width / 2),
+    y: result.y - (height / 2)
+  )
+
+proc adjustPos3(width, height: int, pos: Point): Point =
+  result = pos
+
+  result = Point(
+    x: result.x - (width / 2),
+    y: result.y - (height / 2)
+  )
+
+echo adjustPos(200, 200, Point[float](x: 1000, y: 1000))
+echo adjustPos2(200, 200, Point[float](x: 1000, y: 1000))
+echo adjustPos3(200, 200, Point[float](x: 1000, y: 1000))
diff --git a/tests/ccgbugs/topenarraycast.nim b/tests/ccgbugs/topenarraycast.nim
new file mode 100644
index 000000000..7d1bc8d03
--- /dev/null
+++ b/tests/ccgbugs/topenarraycast.nim
@@ -0,0 +1,8 @@
+proc foo[T](s: var openArray[T]): T =
+  for x in s: result += x
+
+proc bar(xyz: var seq[int]) =
+  doAssert 6 == (seq[int](xyz)).foo()
+
+var t = @[1,2,3]
+bar(t)
diff --git a/tests/ccgbugs/tprogmem.nim b/tests/ccgbugs/tprogmem.nim
new file mode 100644
index 000000000..58a20583a
--- /dev/null
+++ b/tests/ccgbugs/tprogmem.nim
@@ -0,0 +1,11 @@
+discard """
+  output: "5"
+  cmd: r"nim c --hints:on $options -d:release $file"
+  ccodecheck: "'/*PROGMEM*/ myLetVariable = {'"
+  targets: "c"
+"""
+
+var myLetVariable {.exportc, codegenDecl: "$# /*PROGMEM*/ $#".} = [1, 2, 3]
+
+myLetVariable[0] = 5
+echo myLetVariable[0]
diff --git a/tests/ccgbugs/trecursive_closure.nim b/tests/ccgbugs/trecursive_closure.nim
deleted file mode 100644
index f64382a8c..000000000
--- a/tests/ccgbugs/trecursive_closure.nim
+++ /dev/null
@@ -1,14 +0,0 @@
-# bug #2233
-type MalType = object
-  fun: proc: MalType
-
-proc f(x: proc: MalType) =
-  discard x()
-
-f(nil)
-
-# bug #2823
-
-type A = object #of RootObj <-- Uncomment this to get no errors
-  test: proc(i: A): bool
-var a: proc(i: A): bool # Or comment this line to get no errors
diff --git a/tests/ccgbugs/tret_arg_init.nim b/tests/ccgbugs/tret_arg_init.nim
index 5cd67de3e..e39e5a0de 100644
--- a/tests/ccgbugs/tret_arg_init.nim
+++ b/tests/ccgbugs/tret_arg_init.nim
@@ -1,6 +1,8 @@
 discard """
   output: '''
 
+
+
 '''
 """
 
diff --git a/tests/ccgbugs/tsamename3.nim b/tests/ccgbugs/tsamename3.nim
new file mode 100644
index 000000000..ded18e9f8
--- /dev/null
+++ b/tests/ccgbugs/tsamename3.nim
@@ -0,0 +1,120 @@
+block: # bug #15526
+  block:
+    type Foo = ref object
+      x1: int
+    let f1 = Foo(x1: 1)
+  block:
+    type Foo = ref object
+      x2: int
+    let f2 = Foo(x2: 2)
+
+block: # ditto
+  template fn() =
+    block:
+      type Foo = ref object
+        x1: int
+      let f1 = Foo(x1: 1)
+      doAssert f1.x1 == 1
+    block:
+      type Foo = ref object
+        x2: int
+      let f2 = Foo(x2: 2)
+      doAssert f2.x2 == 2
+  static: fn()
+  fn()
+
+block: # bug #17162
+  template fn =
+    var ret: string
+    block:
+      type A = enum a0, a1, a2
+      for ai in A:
+        ret.add $ai
+    block:
+      type A = enum b0, b1, b2, b3
+      for ai in A:
+        ret.add $ai
+    doAssert ret == "a0a1a2b0b1b2b3"
+
+  static: fn() # ok
+  fn() # was bug
+
+block: # ditto
+  proc fn =
+    var ret: string
+    block:
+      type A = enum a0, a1, a2
+      for ai in A:
+        ret.add $ai
+    block:
+      type A = enum b0, b1, b2, b3
+      for ai in A:
+        ret.add $ai
+    doAssert ret == "a0a1a2b0b1b2b3"
+
+  static: fn() # ok
+  fn() # was bug
+
+block: # bug #5170
+  block:
+    type Foo = object
+      x1: int
+    let f1 = Foo(x1: 1)
+  block:
+    type Foo = object
+      x2: int
+    let f2 = Foo(x2: 2)
+
+block: # ditto
+  block:
+    type Foo = object
+      bar: bool
+    var f1: Foo
+
+  block:
+    type Foo = object
+      baz: int
+    var f2: Foo
+    doAssert f2.baz == 0
+
+  block:
+    template fn() =
+      block:
+        type Foo = object
+          x1: int
+        let f1 = Foo(x1: 1)
+        doAssert f1.x1 == 1
+      block:
+        type Foo = object
+          x2: int
+        let f2 = Foo(x2: 2)
+        doAssert f2.x2 == 2
+    static: fn()
+    fn()
+
+when true: # ditto, refs https://github.com/nim-lang/Nim/issues/5170#issuecomment-582712132
+  type Foo1 = object # at top level
+    bar: bool
+  var f1: Foo1
+
+  block:
+    type Foo1 = object
+      baz: int
+    var f2: Foo1
+    doAssert f2.baz == 0
+
+block: # make sure `hashType` doesn't recurse infinitely
+  type
+    PFoo = ref object
+      a, b: PFoo
+      c: int
+  var a: PFoo
+
+block: # issue #22571
+  macro foo(x: typed) =
+    result = x
+
+  block: # or `proc main =`
+    foo:
+      type Foo = object
+    doAssert $Foo() == "()"
diff --git a/tests/ccgbugs/tsequence_outoforder.nim b/tests/ccgbugs/tsequence_outoforder.nim
new file mode 100644
index 000000000..93a45900d
--- /dev/null
+++ b/tests/ccgbugs/tsequence_outoforder.nim
@@ -0,0 +1,11 @@
+discard """
+  output: '''@[2]'''
+"""
+
+# bug #9684
+
+var s2 = @[2, 2]
+
+s2 = @[s2.len]
+
+echo s2
diff --git a/tests/ccgbugs/tsighash_typename_regression.nim b/tests/ccgbugs/tsighash_typename_regression.nim
index 7122902d9..b93eebd20 100644
--- a/tests/ccgbugs/tsighash_typename_regression.nim
+++ b/tests/ccgbugs/tsighash_typename_regression.nim
@@ -1,3 +1,10 @@
+discard """
+output: '''
+123
+baz
+'''
+"""
+
 # bug #5147
 
 proc foo[T](t: T) =
@@ -8,3 +15,18 @@ proc foo[T](t: T) =
 
 foo(123)
 foo("baz")
+
+# Empty type in template is correctly disambiguated
+block:
+  template foo() =
+    type M = object
+      discard
+    var y = M()
+
+  foo()
+
+  type M = object
+    x: int
+
+  var x = M(x: 1)
+  doAssert(x.x == 1)
diff --git a/tests/ccgbugs/tunsafeaddr.nim b/tests/ccgbugs/tunsafeaddr.nim
index 8d0a9d8cb..f868739de 100644
--- a/tests/ccgbugs/tunsafeaddr.nim
+++ b/tests/ccgbugs/tunsafeaddr.nim
@@ -1,5 +1,6 @@
 discard """
-  output: '''12'''
+  output: '''12
+4'''
 """
 
 {.emit: """
@@ -26,3 +27,21 @@ p(@[1])
 q(@[1])
 
 main()
+
+# bug #9403
+
+type
+  MyObj = ref object
+    len: int
+    val: UncheckedArray[uint64]
+
+proc spot(x: MyObj): int64 =
+  result = cast[UncheckedArray[int64]](x.val)[0]
+
+proc newMyObj(len: int): MyObj =
+  unsafeNew(result, sizeof(result[]) + len * sizeof(uint64))
+  result.len = len
+  result.val[0] = 4u64
+  result.val[1] = 8u64
+
+echo spot(newMyObj(2))
diff --git a/tests/ccgbugs/tuple_canon.nim b/tests/ccgbugs/tuple_canon.nim
index 7e9e91836..fbb971861 100644
--- a/tests/ccgbugs/tuple_canon.nim
+++ b/tests/ccgbugs/tuple_canon.nim
@@ -1,4 +1,9 @@
-
+discard """
+output: '''
+vidx 18
+0,0
+'''
+"""
 
 # bug #4626
 var foo: (int, array[1, int]) # Tuple must be of length > 1
@@ -7,8 +12,7 @@ foo = bar                     # No error if assigned directly
 
 # bug #2250
 
-import
-    math, strutils
+import math
 
 type
     Meters = float
@@ -64,7 +68,7 @@ template odd*(i: int) : untyped =
 
 proc vidx(hg: HexGrid; col, row: int; i: HexVtxIndex) : Index =
     #NOTE: this variation compiles
-    #var offset : type(evenSharingOffsets[i])
+    #var offset : typeof(evenSharingOffsets[i])
     #
     #if odd(col):
     #    offset = oddSharingOffsets[i]
diff --git a/tests/ccgbugs/twrong_setconstr.nim b/tests/ccgbugs/twrong_setconstr.nim
deleted file mode 100644
index f03ffd928..000000000
--- a/tests/ccgbugs/twrong_setconstr.nim
+++ /dev/null
@@ -1,146 +0,0 @@
-discard """
-  output: ""
-"""
-
-# bug #2880
-
-type
-  TMsgKind* = enum
-    errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated,
-    errXCompilerDoesNotSupportCpp, errStringLiteralExpected,
-    errIntLiteralExpected, errInvalidCharacterConstant,
-    errClosingTripleQuoteExpected, errClosingQuoteExpected,
-    errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong,
-    errInvalidNumber, errInvalidNumberOctalCode, errNumberOutOfRange,
-    errNnotAllowedInCharacter, errClosingBracketExpected, errMissingFinalQuote,
-    errIdentifierExpected, errNewlineExpected, errInvalidModuleName,
-    errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected,
-    errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected,
-    errInvalidPragma, errUnknownPragma, errInvalidDirectiveX,
-    errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation,
-    errExceptionExpected, errExceptionAlreadyHandled,
-    errYieldNotAllowedHere, errYieldNotAllowedInTryStmt,
-    errInvalidNumberOfYieldExpr, errCannotReturnExpr, errAttemptToRedefine,
-    errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel,
-    errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected,
-    errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler,
-    errOnOrOffExpectedButXFound, errOnOffOrListExpectedButXFound,
-    errNoneBoehmRefcExpectedButXFound,
-    errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound,
-    errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound,
-    errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected,
-    errExprExpected, errUndeclaredIdentifier, errUseQualifier, errTypeExpected,
-    errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable,
-    errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue,
-    errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous,
-    errConstantDivisionByZero, errOrdinalTypeExpected,
-    errOrdinalOrFloatTypeExpected, errOverOrUnderflow,
-    errCannotEvalXBecauseIncompletelyDefined, errChrExpectsRange0_255,
-    errDynlibRequiresExportc, errUndeclaredFieldX, errNilAccess,
-    errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType,
-    errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit,
-    errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType,
-    errCastNotInSafeMode, errExprCannotBeCastedToX, errCommaOrParRiExpected,
-    errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected,
-    errMagicOnlyInSystem, errPowerOfTwoExpected,
-    errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv,
-    errSymbolMustBeImported, errExprMustBeBool, errConstExprExpected,
-    errDuplicateCaseLabel, errRangeIsEmpty, errSelectorMustBeOfCertainTypes,
-    errSelectorMustBeOrdinal, errOrdXMustNotBeNegative, errLenXinvalid,
-    errWrongNumberOfVariables, errExprCannotBeRaised, errBreakOnlyInLoop,
-    errTypeXhasUnknownSize, errConstNeedsConstExpr, errConstNeedsValue,
-    errResultCannotBeOpenArray, errSizeTooBig, errSetTooBig,
-    errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects,
-    errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX,
-    errCannotInstantiateX, errExprHasNoAddress, errXStackEscape,
-    errVarForOutParamNeeded,
-    errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX,
-    errAmbiguousCallXYZ, errWrongNumberOfArguments,
-    errXCannotBePassedToProcVar,
-    errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProc, errImplOfXNotAllowed,
-    errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,
-    errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice,
-    errInvalidOrderInArrayConstructor,
-    errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry,
-    errOptionExpected, errXisNoLabel, errNotAllCasesCovered,
-    errUnknownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable,
-    errNoPragmasAllowedForX, errNoGenericParamsAllowedForX,
-    errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent,
-    errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX,
-    errXNotAllowedHere, errInvalidControlFlowX,
-    errXisNoType, errCircumNeedsPointer, errInvalidExpression,
-    errInvalidExpressionX, errEnumHasNoValueX, errNamedExprExpected,
-    errNamedExprNotAllowed, errXExpectsOneTypeParam,
-    errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed,
-    errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType,
-    errNoReturnTypeDeclared,
-    errInvalidCommandX, errXOnlyAtModuleScope,
-    errXNeedsParamObjectType,
-    errTemplateInstantiationTooNested, errInstantiationFrom,
-    errInvalidIndexValueForTuple, errCommandExpectsFilename,
-    errMainModuleMustBeSpecified,
-    errXExpected,
-    errTIsNotAConcreteType,
-    errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
-    errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
-    errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly,
-    errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
-    errMacroBodyDependsOnGenericTypes,
-    errDestructorNotGenericEnough,
-    errInlineIteratorsAsProcParams,
-    errXExpectsTwoArguments,
-    errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations,
-    errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
-    errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument,
-    errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate,
-    errXhasSideEffects, errIteratorExpected, errLetNeedsInit,
-    errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,
-    errXCannotBeClosure, errXMustBeCompileTime,
-    errCannotInferTypeOfTheLiteral,
-    errCannotInferReturnType,
-    errGenericLambdaNotAllowed,
-    errCompilerDoesntSupportTarget,
-    errUser,
-    warnCannotOpenFile,
-    warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
-    warnDeprecated, warnConfigDeprecated,
-    warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
-    warnUnknownSubstitutionX, warnLanguageXNotSupported,
-    warnFieldXNotSupported, warnCommentXIgnored,
-    warnNilStatement, warnTypelessParam,
-    warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode,
-    warnEachIdentIsTuple, warnShadowIdent,
-    warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
-    warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
-    warnUser,
-    hintSuccess, hintSuccessX,
-    hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
-    hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
-    hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
-    hintConditionAlwaysTrue, hintName, hintPattern,
-    hintExecuting, hintLinking, hintDependency,
-    hintSource, hintStackTrace, hintGCStats,
-    hintUser
-
-const
-  warnMin = warnCannotOpenFile
-  hintMax = high(TMsgKind)
-
-type
-  TNoteKind = range[warnMin..hintMax] # "notes" are warnings or hints
-  TNoteKinds = set[TNoteKind]
-
-const
-  NotesVerbosityConst: array[0..0, TNoteKinds] = [
-    {low(TNoteKind)..high(TNoteKind)} - {hintGCStats}]
-  fuckyou = NotesVerbosityConst[0]
-
-var
-  gNotesFromConst: TNoteKinds = NotesVerbosityConst[0]
-  gNotesFromConst2: TNoteKinds = fuckyou
-
-if hintGCStats in gNotesFromConst:
-  echo "hintGCStats in gNotesFromConst A"
-
-if hintGCStats in gNotesFromConst2:
-  echo "hintGCStats in gNotesFromConst B"
diff --git a/tests/ccgbugs/twrong_tupleconv.nim b/tests/ccgbugs/twrong_tupleconv.nim
index 68413e96e..031712dac 100644
--- a/tests/ccgbugs/twrong_tupleconv.nim
+++ b/tests/ccgbugs/twrong_tupleconv.nim
@@ -1,3 +1,8 @@
+discard """
+  targets: "c cpp"
+  matrix: "--gc:refc; --gc:arc"
+"""
+
 # bug #1833
 iterator myitems*[T](a: var seq[T]): var T {.inline.} =
   ## iterates over each item of `a` so that you can modify the yielded value.
@@ -6,7 +11,7 @@ iterator myitems*[T](a: var seq[T]): var T {.inline.} =
   while i < L:
     yield a[i]
     inc(i)
-    assert(len(a) == L, "seq modified while iterating over it")
+    doAssert(len(a) == L, "the length of the seq changed while iterating over it")
 
 # Works fine
 var xs = @[1,2,3]
@@ -18,3 +23,13 @@ var ys = @[(1,"a"),(2,"b"),(3,"c")]
 for y in myitems(ys):
   inc y[0]
 
+# bug #16331
+type T1 = tuple[a, b: int]
+
+proc p(b: bool): string =
+  var x: T1 = (10, 20)
+  x = if b: (x.b, x.a) else: (-x.b, -x.a)
+  $x
+
+assert p(false) == "(a: -20, b: -10)"
+assert p(true) == "(a: 20, b: 10)"
diff --git a/tests/ccgbugs/twrongrefcounting.nim b/tests/ccgbugs/twrongrefcounting.nim
index c0c3e0fc2..8ebaf4058 100644
--- a/tests/ccgbugs/twrongrefcounting.nim
+++ b/tests/ccgbugs/twrongrefcounting.nim
@@ -2,6 +2,17 @@ discard """
   output: '''ok'''
   cmd: "nim c -r --gc:refc -d:useGcAssert -d:useSysAssert -d:fulldebug -d:smokeCycles $file"
 """
+
+# bug #9825
+func empty(T: typedesc): T = discard
+const emptyChunk = @(empty(array[10, byte]))
+
+var lst: seq[seq[byte]]
+lst.add emptyChunk
+
+doAssert($lst == "@[@[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]")
+
+
 # bug #6234
 type
     Foo = ref object
diff --git a/tests/ccgbugs/xarray9578.nim b/tests/ccgbugs/xarray9578.nim
new file mode 100644
index 000000000..849f16821
--- /dev/null
+++ b/tests/ccgbugs/xarray9578.nim
@@ -0,0 +1,7 @@
+import t9578
+
+proc testArray*(x: var array[3,mytype]) =
+  f(x[0].addr)
+
+proc testArray2*(x: var ptr array[3,mytype]) =
+  f(x[0].addr)
diff --git a/tests/ccgbugs/xoa9578.nim b/tests/ccgbugs/xoa9578.nim
new file mode 100644
index 000000000..94ef34519
--- /dev/null
+++ b/tests/ccgbugs/xoa9578.nim
@@ -0,0 +1,4 @@
+import t9578
+
+proc testOpenArray*(x: var openArray[mytype]) =
+  f(x[0].addr)
diff --git a/tests/ccgbugs/xseq9578.nim b/tests/ccgbugs/xseq9578.nim
new file mode 100644
index 000000000..782efe04f
--- /dev/null
+++ b/tests/ccgbugs/xseq9578.nim
@@ -0,0 +1,7 @@
+import t9578
+
+proc testSeq*(x: var seq[mytype]) =
+  f(x[0].addr)
+
+proc testSeq2*(x: var ptr seq[mytype]) =
+  f(x[0].addr)
diff --git a/tests/ccgbugs/xtuple9578.nim b/tests/ccgbugs/xtuple9578.nim
new file mode 100644
index 000000000..b6320fc24
--- /dev/null
+++ b/tests/ccgbugs/xtuple9578.nim
@@ -0,0 +1,7 @@
+import t9578
+
+proc testTuple*(x: var tuple[a:mytype,b:mytype,c:mytype]) =
+  f(x[0].addr)
+
+proc testTuple2*(x: var ptr tuple[a:mytype,b:mytype,c:mytype]) =
+  f(x[0].addr)
diff --git a/tests/ccgbugs/xua9578.nim b/tests/ccgbugs/xua9578.nim
new file mode 100644
index 000000000..9ba6f8fbc
--- /dev/null
+++ b/tests/ccgbugs/xua9578.nim
@@ -0,0 +1,7 @@
+import t9578
+
+proc testUncheckedArray*(x: var UncheckedArray[mytype]) =
+  f(x[0].addr)
+
+proc testUncheckedArray2*(x: var ptr UncheckedArray[mytype]) =
+  f(x[0].addr)
diff --git a/tests/ccgbugs2/tcodegen.nim b/tests/ccgbugs2/tcodegen.nim
new file mode 100644
index 000000000..aac1ecaf3
--- /dev/null
+++ b/tests/ccgbugs2/tcodegen.nim
@@ -0,0 +1,47 @@
+discard """

+  targets: "c cpp"

+"""

+

+# bug #19094

+type

+  X = object

+    filler: array[2048, int]

+    innerAddress: uint

+

+proc initX(): X =

+  result.innerAddress = cast[uint](result.addr)

+

+proc initXInPlace(x: var X) =

+  x.innerAddress = cast[uint](x.addr)

+

+block: # NRVO1

+  var x = initX()

+  let innerAddress = x.innerAddress

+  let outerAddress = cast[uint](x.addr)

+  doAssert(innerAddress == outerAddress) # [OK]

+

+block: # NRVO2

+  var x: X

+  initXInPlace(x)

+  let innerAddress = x.innerAddress

+  let outerAddress = cast[uint](x.addr)

+  doAssert(innerAddress == outerAddress) # [OK]

+

+block: # bug #22354

+  type Object = object

+    foo: int

+

+  proc takeFoo(self: var Object): int =

+    result = self.foo

+    self.foo = 999

+

+  proc doSomething(self: var Object; foo: int = self.takeFoo()) =

+    discard

+

+  proc main() =

+    var obj = Object(foo: 2)

+    obj.doSomething()

+    doAssert obj.foo == 999

+

+

+  main()

diff --git a/tests/ccgbugs/tinefficient_const_table.nim b/tests/ccgbugs2/tinefficient_const_table.nim
index 149b8bcff..8ab895cb0 100644
--- a/tests/ccgbugs/tinefficient_const_table.nim
+++ b/tests/ccgbugs2/tinefficient_const_table.nim
@@ -6,16 +6,16 @@ of
 words'''
   cmd: r"nim c --hints:on $options -d:release $file"
   ccodecheck: "! @'genericSeqAssign'"
+  targets: "c"
 """
 
-
 # bug #4354
 import tables
 import sets
 import strutils
 
 #const FRUITS = ["banana", "apple", "grapes"]
-#let FRUITS = ["banana", "apple", "grapes"].toSet
+#let FRUITS = ["banana", "apple", "grapes"].toHashSet
 const FRUITS = {"banana":0, "apple":0, "grapes":0}.toTable
 
 proc main() =
diff --git a/tests/closure/t11042.nim b/tests/closure/t11042.nim
new file mode 100644
index 000000000..6a3928316
--- /dev/null
+++ b/tests/closure/t11042.nim
@@ -0,0 +1,55 @@
+discard """
+  output:'''
+foo: 1
+foo: 2
+bar: 1
+bar: 2
+foo: 1
+foo: 2
+bar: 1
+bar: 2
+bar: 3
+bar: 4
+bar: 5
+bar: 6
+bar: 7
+bar: 8
+bar: 9
+'''
+"""
+
+# bug #11042
+block:
+  iterator foo: int =
+    for x in 1..2:
+      echo "foo: ", x
+      for y in 1..2:
+        discard
+
+  for x in foo(): discard
+
+  let bar = iterator: int =
+    for x in 1..2:
+      echo "bar: ", x
+      for y in 1..2:
+        discard
+
+  for x in bar(): discard
+
+
+block:
+  iterator foo: int =
+    for x in 1..2:
+      echo "foo: ", x
+      for y in 1..2:
+        discard
+
+  for x in foo(): discard
+
+  let bar = iterator: int =
+    for x in 1..9:
+      echo "bar: ", x
+      for y in 1..2:
+        discard
+
+  for x in bar(): discard
\ No newline at end of file
diff --git a/tests/closure/t15594.nim b/tests/closure/t15594.nim
new file mode 100644
index 000000000..aacd9ed84
--- /dev/null
+++ b/tests/closure/t15594.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "The variable name cannot be `result`!"
+"""
+
+import sugar
+
+proc begin(): int =
+  capture result:
+    echo 1+1
+  result
diff --git a/tests/closure/t19095.nim b/tests/closure/t19095.nim
new file mode 100644
index 000000000..880456e02
--- /dev/null
+++ b/tests/closure/t19095.nim
@@ -0,0 +1,35 @@
+discard """
+  action: compile
+"""
+
+block:
+  func inCheck() =
+    discard
+
+  iterator iter(): int =
+    yield 0
+    yield 0
+
+  func search() =
+    let inCheck = 0
+
+    for i in iter():
+
+      proc hello() =
+        inCheck()
+
+  search()
+block:
+  iterator iter(): int =
+    yield 0
+    yield 0
+
+  func search() =
+    let lmrMoveCounter = 0
+
+    for i in iter():
+
+      proc hello() =
+        discard lmrMoveCounter
+
+  search()
diff --git a/tests/closure/t20152.nim b/tests/closure/t20152.nim
new file mode 100644
index 000000000..484ea0741
--- /dev/null
+++ b/tests/closure/t20152.nim
@@ -0,0 +1,20 @@
+discard """

+  action: compile

+"""

+

+proc foo() =

+  iterator it():int {.closure.} =

+    yield 1

+  proc useIter() {.nimcall.} =

+    var iii = it # <-- illegal capture

+    doAssert iii() == 1

+  useIter()

+foo()

+

+proc foo2() =

+  proc bar() = # Local function, but not a closure, because no captures

+    echo "hi"

+  proc baz() {.nimcall.} = # Calls local function

+    bar()

+  baz()

+foo2()

diff --git a/tests/closure/t8550.nim b/tests/closure/t8550.nim
new file mode 100644
index 000000000..a07f45cdc
--- /dev/null
+++ b/tests/closure/t8550.nim
@@ -0,0 +1,13 @@
+discard """
+  targets: "c js"
+  output: "@[\"42\"]"
+"""
+
+proc chk_fail(): seq[string] =
+  iterator x(): int {.closure.} = yield 42
+  proc f(cl: iterator(): int {.closure.}): seq[string] =
+    result = @[]
+    for i in cl(): result.add($i)
+  result = f(x)
+
+echo(chk_fail())
diff --git a/tests/closure/t9334.nim b/tests/closure/t9334.nim
new file mode 100644
index 000000000..36a9a7d77
--- /dev/null
+++ b/tests/closure/t9334.nim
@@ -0,0 +1,19 @@
+discard """
+  cmd: "nim $target --hints:off $options -r $file"
+  nimout: '''@[1]
+@[1, 1]
+'''
+  nimoutFull: true
+"""
+proc p(s: var seq[int]): auto =
+  let sptr = addr s
+  return proc() = sptr[].add 1
+
+proc f =
+  var data = @[1]
+  p(data)()
+  echo repr data
+
+static:
+  f() # prints [1]
+f() # prints [1, 1]
diff --git a/tests/closure/tcapture.nim b/tests/closure/tcapture.nim
new file mode 100644
index 000000000..dafc44739
--- /dev/null
+++ b/tests/closure/tcapture.nim
@@ -0,0 +1,34 @@
+discard """
+  output: '''
+to be, or not to be
+(v: 1)
+(w: -1)
+(v: 1)
+(w: -1)
+'''
+  joinable: false
+"""
+
+import sequtils, sugar
+
+let m = @[proc (s: string): string = "to " & s, proc (s: string): string = "not to " & s]
+var l = m.mapIt(capture([it], proc (s: string): string = it(s)))
+let r = l.mapIt(it("be"))
+echo r[0] & ", or " & r[1]
+
+type
+  O = object
+    v: int
+  U = object
+    w: int
+var o = O(v: 1)
+var u = U(w: -1)
+var execute: proc()
+capture o, u:
+  execute = proc() =
+    echo o
+    echo u
+execute()
+o.v = -1
+u.w = 1
+execute()
diff --git a/tests/closure/tclosure.nim b/tests/closure/tclosure.nim
index 09d48436e..401a71d40 100644
--- a/tests/closure/tclosure.nim
+++ b/tests/closure/tclosure.nim
@@ -1,47 +1,504 @@
 discard """
-  file: "tclosure.nim"
-  output: "1 3 6 11 20"
+  targets: "c"
+  output: '''
+1 3 6 11 20 foo
+foo88
+23 24foo 88
+18
+18
+99
+99
+99
+99 99
+99 99
+12 99 99
+12 99 99
+success
+@[1, 2, 5]
+click at 10,20
+lost focus 1
+lost focus 2
+registered handler for UserEvent 1
+registered handler for UserEvent 2
+registered handler for UserEvent 3
+registered handler for UserEvent 4
+asdas
+processClient end
+false
+baro0
+foo88
+23 24foo 88
+foo88
+23 24foo 88
+11
+@[1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1]
+'''
+joinable: false
 """
-# Test the closure implementation
 
-proc map(n: var openarray[int], fn: proc (x: int): int {.closure}) =
-  for i in 0..n.len-1: n[i] = fn(n[i])
 
-proc foldr(n: openarray[int], fn: proc (x, y: int): int {.closure}): int =
-  for i in 0..n.len-1:
-    result = fn(result, n[i])
+block tclosure:
+  proc map(n: var openArray[int], fn: proc (x: int): int {.closure}) =
+    for i in 0..n.len-1: n[i] = fn(n[i])
 
-proc each(n: openarray[int], fn: proc(x: int) {.closure.}) =
-  for i in 0..n.len-1:
-    fn(n[i])
+  proc each(n: openArray[int], fn: proc(x: int) {.closure.}) =
+    for i in 0..n.len-1:
+      fn(n[i])
 
-var
-  myData: array[0..4, int] = [0, 1, 2, 3, 4]
+  var myData: array[0..4, int] = [0, 1, 2, 3, 4]
 
-proc testA() =
-  var p = 0
-  map(myData, proc (x: int): int =
-                result = x + 1 shl (proc (y: int): int =
-                  return y + p
-                )(0)
-                inc(p))
+  proc testA() =
+    var p = 0
+    map(myData, proc (x: int): int =
+                  result = x + 1 shl (proc (y: int): int =
+                    return y + p
+                  )(0)
+                  inc(p))
 
-testA()
+  testA()
 
-myData.each do (x: int):
-  write(stdout, x)
-  write(stdout, " ")
+  myData.each do (x: int):
+    write(stdout, x)
+    write(stdout, " ")
 
-#OUT 2 4 6 8 10
+  #OUT 2 4 6 8 10
 
-type
-  ITest = tuple[
-    setter: proc(v: int),
-    getter: proc(): int]
+  # bug #5015
 
-proc getInterf(): ITest =
-  var shared: int
+  type Mutator = proc(matched: string): string {.noSideEffect, gcsafe.}
 
-  return (setter: proc (x: int) = shared = x,
-          getter: proc (): int = return shared)
+  proc putMutated(
+      MutatorCount: static[int],
+      mTable: static[array[MutatorCount, Mutator]], input: string) =
+    for i in 0..<MutatorCount: echo mTable[i](input)
 
+  proc mutator0(matched: string): string =
+      "foo"
+
+  const
+    mTable = [Mutator(mutator0)]
+
+  putMutated(1, mTable, "foo")
+
+
+
+block tclosure0:
+  when true:
+    # test simple closure within dummy 'main':
+    proc dummy =
+      proc main2(param: int) =
+        var fooB = 23
+        proc outer(outerParam: string) =
+          var outerVar = 88
+          echo outerParam, outerVar
+          proc inner() =
+            block Test:
+              echo fooB, " ", param, outerParam, " ", outerVar
+          inner()
+        outer("foo")
+      main2(24)
+
+    dummy()
+
+  when true:
+    proc outer2(x:int) : proc(y:int):int =   # curry-ed application
+        return proc(y:int):int = x*y
+
+    var fn = outer2(6)  # the closure
+    echo fn(3)   # it works
+
+    var rawP = fn.rawProc()
+    var rawE = fn.rawEnv()
+
+    # A type to cast the function pointer into a nimcall
+    type TimesClosure = proc(a: int, x: pointer): int {.nimcall.}
+
+    # Call the function with its closure
+    echo cast[TimesClosure](rawP)(3, rawE)
+
+  when true:
+    proc outer =
+      var x, y: int = 99
+      proc innerA = echo x
+      proc innerB =
+        echo y
+        innerA()
+
+      innerA()
+      innerB()
+
+    outer()
+
+  when true:
+    proc indirectDep =
+      var x, y: int = 99
+      proc innerA = echo x, " ", y
+      proc innerB =
+        innerA()
+
+      innerA()
+      innerB()
+
+    indirectDep()
+
+  when true:
+    proc needlessIndirection =
+      var x, y: int = 99
+      proc indirection =
+        var z = 12
+        proc innerA = echo z, " ", x, " ", y
+        proc innerB =
+          innerA()
+
+        innerA()
+        innerB()
+      indirection()
+
+    needlessIndirection()
+
+
+
+
+
+
+block tclosure3:
+  proc main =
+    const n = 30
+    for iterations in 0..10_000:
+      var s: seq[proc(): string {.closure.}] = @[]
+      for i in 0 .. n-1:
+        (proc () =
+          let ii = i
+          s.add(proc(): string = return $(ii*ii)))()
+      for i in 0 .. n-1:
+        let val = s[i]()
+        if val != $(i*i): echo "bug  ", val
+
+      if getOccupiedMem() > 5000_000: quit("still a leak!")
+    echo "success"
+
+  main()
+
+
+
+import json, tables, sequtils
+block tclosure4:
+  proc run(json_params: OrderedTable) =
+    let json_elems = json_params["files"].elems
+    # These fail compilation.
+    var files = map(json_elems, proc (x: JsonNode): string = x.str)
+
+  let text = """{"files": ["a", "b", "c"]}"""
+  run((text.parseJson).fields)
+
+
+
+import sugar
+block inference3304:
+  type
+    List[T] = ref object
+      val: T
+
+  proc foo[T](l: List[T]): seq[int] =
+    @[1,2,3,5].filter(x => x != l.val)
+
+  echo(foo(List[int](val: 3)))
+
+
+
+block tcodegenerr1923:
+  type
+    Foo[M] = proc() : M
+
+  proc bar[M](f : Foo[M]) =
+    discard f()
+
+  proc baz() : int = 42
+
+  bar(baz)
+
+
+
+block doNotation:
+  type
+    Button = object
+    Event = object
+      x, y: int
+
+  proc onClick(x: Button, handler: proc(x: Event)) =
+    handler(Event(x: 10, y: 20))
+
+  proc onFocusLost(x: Button, handler: proc()) =
+    handler()
+
+  proc onUserEvent(x: Button, eventName: string, handler: proc) =
+    echo "registered handler for ", eventName
+
+  var b = Button()
+
+  b.onClick do (e: Event):
+    echo "click at ", e.x, ",", e.y
+
+  b.onFocusLost do ():
+    echo "lost focus 1"
+
+  b.onFocusLost do ():
+    echo "lost focus 2"
+
+  b.onUserEvent("UserEvent 1") do ():
+    discard
+
+  onUserEvent(b, "UserEvent 2") do ():
+    discard
+
+  b.onUserEvent("UserEvent 3") do ():
+    discard
+
+  b.onUserEvent("UserEvent 4", () => echo "event 4")
+
+
+
+import tables
+block fib50:
+  proc memoize(f: proc (a: int64): int64): proc (a: int64): int64 =
+      var previous = initTable[int64, int64]()
+      return proc(i: int64): int64 =
+          if not previous.hasKey i:
+              previous[i] = f(i)
+          return previous[i]
+
+  var fib: proc(a: int64): int64
+
+  fib = memoize(proc (i: int64): int64 =
+      if i == 0 or i == 1:
+          return 1
+      return fib(i-1) + fib(i-2)
+  )
+
+  doAssert fib(50) == 20365011074
+
+
+
+block tflatmap:
+  # bug #3995
+  type
+    RNG = tuple[]
+    Rand[A] = (RNG) -> (A, RNG)
+
+  proc nextInt(r: RNG): (int, RNG) =
+    (1, ())
+
+  proc flatMap[A,B](f: Rand[A], g: A -> Rand[B]): Rand[B] =
+    (rng: RNG) => (
+      let (a, rng2) = f(rng);
+      let g1 = g(a);
+      g1(rng2)
+    )
+
+  proc map[A,B](s: Rand[A], f: A -> B): Rand[B] =
+    let g: A -> Rand[B] = (a: A) => ((rng: RNG) => (f(a), rng))
+    flatMap(s, g)
+
+  discard nextInt.map(i => i - i mod 2)
+
+
+
+block tforum:
+  type
+    PAsyncHttpServer = ref object
+      value: string
+    PFutureBase = ref object
+      callback: proc () {.closure.}
+      value: string
+      failed: bool
+
+  proc accept(server: PAsyncHttpServer): PFutureBase =
+    new(result)
+    result.callback = proc () =
+      discard
+    server.value = "hahaha"
+
+  proc processClient(): PFutureBase =
+    new(result)
+
+  proc serve(server: PAsyncHttpServer): PFutureBase =
+    iterator serveIter(): PFutureBase {.closure.} =
+      echo server.value
+      while true:
+        var acceptAddrFut = server.accept()
+        yield acceptAddrFut
+        var fut = acceptAddrFut.value
+
+        var f = processClient()
+        f.callback =
+          proc () =
+            echo("processClient end")
+            echo(f.failed)
+        yield f
+    var x = serveIter
+    for i in 0 .. 1:
+      result = x()
+      result.callback()
+
+  discard serve(PAsyncHttpServer(value: "asdas"))
+
+
+
+block futclosure2138:
+  proc any[T](list: varargs[T], pred: (T) -> bool): bool =
+    for item in list:
+        if pred(item):
+            result = true
+            break
+
+  proc contains(s: string, words: varargs[string]): bool =
+    any(words, (word) => s.contains(word))
+
+
+
+block tinterf:
+  type
+    ITest = tuple[
+      setter: proc(v: int) {.closure.},
+      getter1: proc(): int {.closure.},
+      getter2: proc(): int {.closure.}]
+
+  proc getInterf(): ITest =
+    var shared1, shared2: int
+
+    return (setter: proc (x: int) =
+              shared1 = x
+              shared2 = x + 10,
+            getter1: proc (): int = result = shared1,
+            getter2: proc (): int = return shared2)
+
+  var i = getInterf()
+  i.setter(56)
+
+  doAssert i.getter1() == 56
+  doAssert i.getter2() == 66
+
+
+
+block tjester:
+  type
+    Future[T] = ref object
+      data: T
+      callback: proc () {.closure.}
+
+  proc cbOuter(response: string) {.discardable.} =
+    iterator cbIter(): Future[int] {.closure.} =
+      for i in 0..7:
+        proc foo(): int =
+          iterator fooIter(): Future[int] {.closure.} =
+            echo response, i
+            yield Future[int](data: 17)
+          var iterVar = fooIter
+          iterVar().data
+        yield Future[int](data: foo())
+
+    var iterVar2 = cbIter
+    proc cb2() {.closure.} =
+      try:
+        if not finished(iterVar2):
+          let next = iterVar2()
+          if next != nil:
+            next.callback = cb2
+      except:
+        echo "WTF"
+    cb2()
+
+  cbOuter "baro"
+
+
+
+block tnamedparamanonproc:
+  type
+    PButton = ref object
+    TButtonClicked = proc(button: PButton) {.nimcall.}
+
+  proc newButton(onClick: TButtonClicked) =
+    discard
+
+  proc main() =
+    newButton(onClick = proc(b: PButton) =
+      var requestomat = 12
+      )
+
+  main()
+
+
+
+block tnestedclosure:
+  proc main(param: int) =
+    var foo = 23
+    proc outer(outerParam: string) =
+      var outerVar = 88
+      echo outerParam, outerVar
+      proc inner() =
+        block Test:
+          echo foo, " ", param, outerParam, " ", outerVar
+      inner()
+    outer("foo")
+
+  # test simple closure within dummy 'main':
+  proc dummy =
+    proc main2(param: int) =
+      var fooB = 23
+      proc outer(outerParam: string) =
+        var outerVar = 88
+        echo outerParam, outerVar
+        proc inner() =
+          block Test:
+            echo fooB, " ", param, outerParam, " ", outerVar
+        inner()
+      outer("foo")
+    main2(24)
+
+  dummy()
+
+  main(24)
+
+  # Jester + async triggered this bug:
+  proc cbOuter() =
+    var response = "hohoho"
+    block:
+      proc cbIter() =
+        block:
+          proc fooIter() =
+            doAssert response == "hohoho"
+          fooIter()
+      cbIter()
+  cbOuter()
+
+
+
+block tnestedproc:
+  proc p(x, y: int): int =
+    result = x + y
+
+  echo p((proc (): int =
+            var x = 7
+            return x)(),
+         (proc (): int = return 4)())
+
+
+
+block tnoclosure:
+  proc pascal(n: int) =
+    var row = @[1]
+    for r in 1..n:
+      row = zip(row & @[0], @[0] & row).mapIt(it[0] + it[1])
+    echo row
+  pascal(10)
+
+block: # bug #22297
+  iterator f: int {.closure.} =
+    try:
+      yield 12
+    finally:
+      return 14
+
+  let s = f
+  doAssert s() == 12
+  doAssert s() == 14
diff --git a/tests/closure/tclosure0.nim b/tests/closure/tclosure0.nim
deleted file mode 100644
index 9952268d5..000000000
--- a/tests/closure/tclosure0.nim
+++ /dev/null
@@ -1,87 +0,0 @@
-discard """
-  output: '''foo88
-23 24foo 88
-18
-18
-99
-99
-99
-99 99
-99 99
-12 99 99
-12 99 99'''
-"""
-
-when true:
-  # test simple closure within dummy 'main':
-  proc dummy =
-    proc main2(param: int) =
-      var fooB = 23
-      proc outer(outerParam: string) =
-        var outerVar = 88
-        echo outerParam, outerVar
-        proc inner() =
-          block Test:
-            echo fooB, " ", param, outerParam, " ", outerVar
-        inner()
-      outer("foo")
-    main2(24)
-
-  dummy()
-
-when true:
-  proc outer2(x:int) : proc(y:int):int =   # curry-ed application
-      return proc(y:int):int = x*y
-
-  var fn = outer2(6)  # the closure
-  echo fn(3)   # it works
-
-  var rawP = fn.rawProc()
-  var rawE = fn.rawEnv()
-
-  # A type to cast the function pointer into a nimcall
-  type
-    TimesClosure = proc(a: int, x: pointer): int {.nimcall.}
-
-  # Call the function with its closure
-  echo cast[TimesClosure](rawP)(3, rawE)
-
-when true:
-  proc outer =
-    var x, y: int = 99
-    proc innerA = echo x
-    proc innerB =
-      echo y
-      innerA()
-
-    innerA()
-    innerB()
-
-  outer()
-
-when true:
-  proc indirectDep =
-    var x, y: int = 99
-    proc innerA = echo x, " ", y
-    proc innerB =
-      innerA()
-
-    innerA()
-    innerB()
-
-  indirectDep()
-
-when true:
-  proc needlessIndirection =
-    var x, y: int = 99
-    proc indirection =
-      var z = 12
-      proc innerA = echo z, " ", x, " ", y
-      proc innerB =
-        innerA()
-
-      innerA()
-      innerB()
-    indirection()
-
-  needlessIndirection()
diff --git a/tests/closure/tclosure2.nim b/tests/closure/tclosure2.nim
deleted file mode 100644
index 9c5ee1426..000000000
--- a/tests/closure/tclosure2.nim
+++ /dev/null
@@ -1,101 +0,0 @@
-discard """
-  output: '''0
-11
-1
-11
-2
-11
-3
-11
-4
-11
-5
-11
-6
-11
-7
-11
-8
-11
-9
-11
-11
-py
-py
-py
-py
-px
-6'''
-"""
-
-when true:
-  proc ax =
-    for xxxx in 0..9:
-      var i = 0
-      proc bx =
-        if i > 10:
-          echo xxxx
-          return
-        i += 1
-        #for j in 0 .. 0: echo i
-        bx()
-
-      bx()
-      echo i
-
-  ax()
-
-when true:
-  proc accumulator(start: int): (proc(): int {.closure.}) =
-    var x = start-1
-    #let dummy = proc =
-    #  discard start
-
-    result = proc (): int =
-      #var x = 9
-      for i in 0 .. 0: x = x + 1
-
-      return x
-
-  var a = accumulator(3)
-  let b = accumulator(4)
-  echo a() + b() + a()
-
-
-  proc outer =
-
-    proc py() =
-      # no closure here:
-      for i in 0..3: echo "py"
-
-    py()
-
-  outer()
-
-
-when true:
-  proc outer2 =
-    var errorValue = 3
-    proc fac[T](n: T): T =
-      if n < 0: result = errorValue
-      elif n <= 1: result = 1
-      else: result = n * fac(n-1)
-
-    proc px() {.closure.} =
-      echo "px"
-
-    proc py() {.closure.} =
-      echo "py"
-
-    let
-      mapping = {
-        "abc": px,
-        "xyz": py
-      }
-    mapping[0][1]()
-
-    echo fac(3)
-
-
-  outer2()
-
diff --git a/tests/closure/tclosure3.nim b/tests/closure/tclosure3.nim
deleted file mode 100644
index d5ffb5ab2..000000000
--- a/tests/closure/tclosure3.nim
+++ /dev/null
@@ -1,21 +0,0 @@
-discard """
-  file: "tclosure3.nim"
-  output: "success"
-"""
-
-proc main =
-  const n = 30
-  for iterations in 0..50_000:
-    var s: seq[proc(): string {.closure.}] = @[]
-    for i in 0 .. n-1:
-      (proc () =
-        let ii = i
-        s.add(proc(): string = return $(ii*ii)))()
-    for i in 0 .. n-1:
-      let val = s[i]()
-      if val != $(i*i): echo "bug  ", val
-
-    if getOccupiedMem() > 3000_000: quit("still a leak!")
-  echo "success"
-
-main()
diff --git a/tests/closure/tclosure4.nim b/tests/closure/tclosure4.nim
deleted file mode 100644
index bc134ded6..000000000
--- a/tests/closure/tclosure4.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-
-import json, tables, sequtils
-
-proc run(json_params: OrderedTable) =
-  let json_elems = json_params["files"].elems
-  # These fail compilation.
-  var files = map(json_elems, proc (x: JsonNode): string = x.str)
-  #var files = json_elems.map do (x: JsonNode) -> string: x.str
-  echo "Hey!"
-
-when isMainModule:
-  let text = """{"files": ["a", "b", "c"]}"""
-  run((text.parseJson).fields)
diff --git a/tests/closure/tclosure_issues.nim b/tests/closure/tclosure_issues.nim
new file mode 100644
index 000000000..b1a2d7c6b
--- /dev/null
+++ b/tests/closure/tclosure_issues.nim
@@ -0,0 +1,82 @@
+discard """
+  output: '''true
+(999, 0)
+ok 0
+ok 1
+ok 2
+'''
+"""
+
+
+block tissue600:
+  for i in 1..1:
+    var reported = false
+    proc report() =
+      reported = true
+
+
+
+import sequtils
+block tissue1502def:
+  let xs: seq[tuple[key: string, val: seq[string]]] = @[("foo", @["bar"])]
+
+  let maps = xs.map(
+    proc(x: auto): tuple[typ: string, maps: seq[string]] =
+      (x.key, x.val.map(proc(x: string): string = x)))
+
+
+
+block tissue1642:
+  var i = 0
+  proc p() = inc(i)
+
+
+
+block tissue1846:
+  type
+    TBinOp[T] = proc (x,y: T): bool
+    THeap[T] = object
+      cmp: TBinOp[T]
+
+  proc less[T](x,y: T): bool =
+    x < y
+
+  proc initHeap[T](cmp: TBinOp[T]): THeap[T] =
+    result.cmp = cmp
+
+  var h = initHeap[int](less[int])
+  echo h.cmp(2,3)
+
+
+
+block tissue1911:
+  proc foo(x: int) : auto =
+
+    proc helper() : int = x
+    proc bar() : int = helper()
+    proc baz() : int = helper()
+
+    return (bar, baz)
+
+# bug #11523
+proc foo(): proc =
+  let a = 999
+  return proc(): (int, int) =
+    return (a, 0)
+
+echo foo()()
+
+
+block tissue7104:
+  proc sp(cb: proc())=
+      cb()
+
+  sp do ():
+      var i = 0
+      echo "ok ", i
+      sp do ():
+          inc i
+          echo "ok ", i
+          sp do ():
+              inc i
+              echo "ok ", i
diff --git a/tests/closure/tclosurebug2.nim b/tests/closure/tclosurebug2.nim
deleted file mode 100644
index f131406a3..000000000
--- a/tests/closure/tclosurebug2.nim
+++ /dev/null
@@ -1,194 +0,0 @@
-import hashes, math
-
-type
-  TSlotEnum = enum seEmpty, seFilled, seDeleted
-  TKeyValuePair[A, B] = tuple[slot: TSlotEnum, key: A, val: B]
-  TKeyValuePairSeq[A, B] = seq[TKeyValuePair[A, B]]
-
-  TOrderedKeyValuePair[A, B] = tuple[
-    slot: TSlotEnum, next: int, key: A, val: B]
-  TOrderedKeyValuePairSeq[A, B] = seq[TOrderedKeyValuePair[A, B]]
-  TOrderedTable*[A, B] = object ## table that remembers insertion order
-    data: TOrderedKeyValuePairSeq[A, B]
-    counter, first, last: int
-
-const
-  growthFactor = 2
-
-proc mustRehash(length, counter: int): bool {.inline.} =
-  assert(length > counter)
-  result = (length * 2 < counter * 3) or (length - counter < 4)
-
-proc nextTry(h, maxHash: Hash): Hash {.inline.} =
-  result = ((5 * h) + 1) and maxHash
-
-template rawGetImpl() {.dirty.} =
-  var h: Hash = hash(key) and high(t.data) # start with real hash value
-  while t.data[h].slot != seEmpty:
-    if t.data[h].key == key and t.data[h].slot == seFilled:
-      return h
-    h = nextTry(h, high(t.data))
-  result = -1
-
-template rawInsertImpl() {.dirty.} =
-  var h: Hash = hash(key) and high(data)
-  while data[h].slot == seFilled:
-    h = nextTry(h, high(data))
-  data[h].key = key
-  data[h].val = val
-  data[h].slot = seFilled
-
-template addImpl() {.dirty.} =
-  if mustRehash(len(t.data), t.counter): enlarge(t)
-  rawInsert(t, t.data, key, val)
-  inc(t.counter)
-
-template putImpl() {.dirty.} =
-  var index = rawGet(t, key)
-  if index >= 0:
-    t.data[index].val = val
-  else:
-    addImpl()
-
-proc len*[A, B](t: TOrderedTable[A, B]): int {.inline.} =
-  ## returns the number of keys in `t`.
-  result = t.counter
-
-template forAllOrderedPairs(yieldStmt: stmt) {.dirty, immediate.} =
-  var h = t.first
-  while h >= 0:
-    var nxt = t.data[h].next
-    if t.data[h].slot == seFilled: yieldStmt
-    h = nxt
-
-iterator pairs*[A, B](t: TOrderedTable[A, B]): tuple[key: A, val: 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 mpairs*[A, B](t: var TOrderedTable[A, B]): tuple[key: A, val: 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: TOrderedTable[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: TOrderedTable[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 TOrderedTable[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 rawGet[A, B](t: TOrderedTable[A, B], key: A): int =
-  rawGetImpl()
-
-proc `[]`*[A, B](t: TOrderedTable[A, B], key: A): B =
-  ## retrieves the value at ``t[key]``. If `key` is not in `t`,
-  ## default empty value for the type `B` is returned
-  ## and no exception is raised. One can check with ``hasKey`` whether the key
-  ## exists.
-  var index = rawGet(t, key)
-  if index >= 0: result = t.data[index].val
-
-proc mget*[A, B](t: var TOrderedTable[A, B], key: A): var B =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
-  var index = rawGet(t, key)
-  if index >= 0: result = t.data[index].val
-  else: raise newException(KeyError, "key not found: " & $key)
-
-proc hasKey*[A, B](t: TOrderedTable[A, B], key: A): bool =
-  ## returns true iff `key` is in the table `t`.
-  result = rawGet(t, key) >= 0
-
-proc rawInsert[A, B](t: var TOrderedTable[A, B],
-                     data: var TOrderedKeyValuePairSeq[A, B],
-                     key: A, val: B) =
-  rawInsertImpl()
-  data[h].next = -1
-  if t.first < 0: t.first = h
-  if t.last >= 0: data[t.last].next = h
-  t.last = h
-
-proc enlarge[A, B](t: var TOrderedTable[A, B]) =
-  var n: TOrderedKeyValuePairSeq[A, B]
-  newSeq(n, len(t.data) * growthFactor)
-  var h = t.first
-  t.first = -1
-  t.last = -1
-  while h >= 0:
-    var nxt = t.data[h].next
-    if t.data[h].slot == seFilled:
-      rawInsert(t, n, t.data[h].key, t.data[h].val)
-    h = nxt
-  swap(t.data, n)
-
-proc `[]=`*[A, B](t: var TOrderedTable[A, B], key: A, val: B) =
-  ## puts a (key, value)-pair into `t`.
-  putImpl()
-
-proc add*[A, B](t: var TOrderedTable[A, B], key: A, val: B) =
-  ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
-  addImpl()
-
-proc initOrderedTable*[A, B](initialSize=64): TOrderedTable[A, B] =
-  ## creates a new ordered hash table that is empty. `initialSize` needs to be
-  ## a power of two.
-  assert isPowerOfTwo(initialSize)
-  result.counter = 0
-  result.first = -1
-  result.last = -1
-  newSeq(result.data, initialSize)
-
-proc toOrderedTable*[A, B](pairs: openarray[tuple[key: A,
-                           val: B]]): TOrderedTable[A, B] =
-  ## creates a new ordered hash table that contains the given `pairs`.
-  result = initOrderedTable[A, B](nextPowerOfTwo(pairs.len+10))
-  for key, val in items(pairs): result[key] = val
-
-proc sort*[A, B](t: var TOrderedTable[A,B],
-                 cmp: proc (x, y: tuple[key: A, val: B]): int {.closure.}) =
-  ## sorts the ordered table so that the entry with the highest counter comes
-  ## first. This is destructive (with the advantage of being efficient)!
-  ## You must not modify `t` afterwards!
-  ## You can use the iterators `pairs`,  `keys`, and `values` to iterate over
-  ## `t` in the sorted order.
-
-  # we use shellsort here; fast enough and simple
-  var h = 1
-  while true:
-    h = 3 * h + 1
-    if h >= high(t.data): break
-  while true:
-    h = h div 3
-    for i in countup(h, high(t.data)):
-      var j = i
-      #echo(t.data.len, " ", j, " - ", h)
-      #echo(repr(t.data[j-h]))
-      proc rawCmp(x, y: TOrderedKeyValuePair[A, B]): int =
-        if x.slot in {seEmpty, seDeleted} and y.slot in {seEmpty, seDeleted}:
-          return 0
-        elif x.slot in {seEmpty, seDeleted}:
-          return -1
-        elif y.slot in {seEmpty, seDeleted}:
-          return 1
-        else:
-          let item1 = (x.key, x.val)
-          let item2 = (y.key, y.val)
-          return cmp(item1, item2)
-
-      while rawCmp(t.data[j-h], t.data[j]) <= 0:
-        swap(t.data[j], t.data[j-h])
-        j = j-h
-        if j < h: break
-    if h == 1: break
diff --git a/tests/closure/tclosureinference3304.nim b/tests/closure/tclosureinference3304.nim
deleted file mode 100644
index db4aa1d04..000000000
--- a/tests/closure/tclosureinference3304.nim
+++ /dev/null
@@ -1,15 +0,0 @@
-discard """
-  output: '''@[1, 2, 5]'''
-"""
-
-import future, sequtils
-
-type
-  List[T] = ref object
-    val: T
-  
-proc foo[T](l: List[T]): seq[int] =
-  @[1,2,3,5].filter(x => x != l.val)
-
-when isMainModule:
-  echo(foo(List[int](val: 3)))
diff --git a/tests/closure/tcodegenerr1923.nim b/tests/closure/tcodegenerr1923.nim
deleted file mode 100644
index ee131ae15..000000000
--- a/tests/closure/tcodegenerr1923.nim
+++ /dev/null
@@ -1,9 +0,0 @@
-type
-  Foo[M] = proc() : M
-
-proc bar[M](f : Foo[M]) =
-  discard f()
-
-proc baz() : int = 42
-
-bar(baz)
\ No newline at end of file
diff --git a/tests/closure/tdeeplynested.nim b/tests/closure/tdeeplynested.nim
deleted file mode 100644
index ddf4fa6a4..000000000
--- a/tests/closure/tdeeplynested.nim
+++ /dev/null
@@ -1,20 +0,0 @@
-discard """
-  output: '''int: 108'''
-"""
-
-# bug #4070
-
-proc id(f: (proc())): auto =
-  return f
-
-proc foo(myinteger: int): (iterator(): int) =
-  return iterator(): int {.closure.} =
-           proc bar() =
-             proc kk() =
-               echo "int: ", myinteger
-
-             kk()
-
-           id(bar)()
-
-discard foo(108)()
diff --git a/tests/closure/tdonotation.nim b/tests/closure/tdonotation.nim
deleted file mode 100644
index cc4f46bab..000000000
--- a/tests/closure/tdonotation.nim
+++ /dev/null
@@ -1,50 +0,0 @@
-discard """
-output: '''
-click at 10,20
-lost focus 1
-lost focus 2
-registered handler for UserEvent 1
-registered handler for UserEvent 2
-registered handler for UserEvent 3
-registered handler for UserEvent 4
-'''
-"""
-
-import future
-
-type
-  Button = object
-  Event = object
-    x, y: int
-
-proc onClick(x: Button, handler: proc(x: Event)) =
-  handler(Event(x: 10, y: 20))
-
-proc onFocusLost(x: Button, handler: proc()) =
-  handler()
-
-proc onUserEvent(x: Button, eventName: string, handler: proc) =
-  echo "registered handler for ", eventName
-
-var b = Button()
-
-b.onClick do (e: Event):
-  echo "click at ", e.x, ",", e.y
-
-b.onFocusLost:
-  echo "lost focus 1"
-
-b.onFocusLost do:
-  echo "lost focus 2"
-
-b.onUserEvent("UserEvent 1") do:
-  discard
-
-b.onUserEvent "UserEvent 2":
-  discard
-
-b.onUserEvent("UserEvent 3"):
-  discard
-
-b.onUserEvent("UserEvent 4", () => echo "event 4")
-
diff --git a/tests/closure/texplicit_dummy_closure.nim b/tests/closure/texplicit_dummy_closure.nim
index ec608b31a..02b9ac7c7 100644
--- a/tests/closure/texplicit_dummy_closure.nim
+++ b/tests/closure/texplicit_dummy_closure.nim
@@ -1,3 +1,6 @@
+discard """
+  disabled: true
+"""
 
 # This is a regression of the new lambda lifting; detected by Aporia
 import asyncio, sockets
@@ -5,8 +8,8 @@ import os
 
 type
   Window = object
-    oneInstSock*: PAsyncSocket
-    IODispatcher*: PDispatcher
+    oneInstSock*: AsyncSocket
+    IODispatcher*: Dispatcher
 
 var
   win: Window
@@ -14,9 +17,9 @@ var
 proc initSocket() =
   win.oneInstSock = asyncSocket()
   #win.oneInstSock.handleAccept =
-  proc test(s: PAsyncSocket) =
-    var client: PAsyncSocket
-    proc dummy(c: PAsyncSocket) {.closure.} =
+  proc test(s: AsyncSocket) =
+    var client: AsyncSocket
+    proc dummy(c: AsyncSocket) {.closure.} =
       discard
     client.handleRead = dummy
   test(win.oneInstSock)
diff --git a/tests/closure/tfib50.nim b/tests/closure/tfib50.nim
deleted file mode 100644
index 719aa3ad5..000000000
--- a/tests/closure/tfib50.nim
+++ /dev/null
@@ -1,22 +0,0 @@
-discard """
-  output: "20365011074"
-"""
-
-import tables
-
-proc memoize(f: proc (a: int64): int64): proc (a: int64): int64 =
-    var previous = initTable[int64, int64]()
-    return proc(i: int64): int64 =
-        if not previous.hasKey i:
-            previous[i] = f(i)
-        return previous[i]
-
-var fib: proc(a: int64): int64
-
-fib = memoize(proc (i: int64): int64 =
-    if i == 0 or i == 1:
-        return 1
-    return fib(i-1) + fib(i-2)
-)
-
-echo fib(50)
diff --git a/tests/closure/tflatmap.nim b/tests/closure/tflatmap.nim
deleted file mode 100644
index 240756424..000000000
--- a/tests/closure/tflatmap.nim
+++ /dev/null
@@ -1,24 +0,0 @@
-
-# bug #3995
-
-import future
-
-type
-  RNG* = tuple[]
-  Rand*[A] = (RNG) -> (A, RNG)
-
-proc nextInt*(r: RNG): (int, RNG) =
-  (1, ())
-
-proc flatMap[A,B](f: Rand[A], g: A -> Rand[B]): Rand[B] =
-  (rng: RNG) => (
-    let (a, rng2) = f(rng);
-    let g1 = g(a);
-    g1(rng2)
-  )
-
-proc map[A,B](s: Rand[A], f: A -> B): Rand[B] =
-  let g: A -> Rand[B] = (a: A) => ((rng: RNG) => (f(a), rng))
-  flatMap(s, g)
-
-let f = nextInt.map(i => i - i mod 2)
diff --git a/tests/closure/tforum.nim b/tests/closure/tforum.nim
deleted file mode 100644
index 4f6a16ff7..000000000
--- a/tests/closure/tforum.nim
+++ /dev/null
@@ -1,44 +0,0 @@
-discard """
-  output: '''asdas
-processClient end
-false
-'''
-"""
-
-type
-  PAsyncHttpServer = ref object
-    value: string
-  PFutureBase = ref object
-    callback: proc () {.closure.}
-    value: string
-    failed: bool
-
-proc accept(server: PAsyncHttpServer): PFutureBase =
-  new(result)
-  result.callback = proc () =
-    discard
-  server.value = "hahaha"
-
-proc processClient(): PFutureBase =
-  new(result)
-
-proc serve(server: PAsyncHttpServer): PFutureBase =
-  iterator serveIter(): PFutureBase {.closure.} =
-    echo server.value
-    while true:
-      var acceptAddrFut = server.accept()
-      yield acceptAddrFut
-      var fut = acceptAddrFut.value
-
-      var f = processClient()
-      f.callback =
-        proc () =
-          echo("processClient end")
-          echo(f.failed)
-      yield f
-  var x = serveIter
-  for i in 0 .. 1:
-    result = x()
-    result.callback()
-
-discard serve(PAsyncHttpServer(value: "asdas"))
diff --git a/tests/closure/tfutclosure2138.nim b/tests/closure/tfutclosure2138.nim
deleted file mode 100644
index e18834074..000000000
--- a/tests/closure/tfutclosure2138.nim
+++ /dev/null
@@ -1,10 +0,0 @@
-import future, sequtils
-
-proc any[T](list: varargs[T], pred: (T) -> bool): bool =
-    for item in list:
-        if pred(item):
-            result = true
-            break
-
-proc contains(s: string, words: varargs[string]): bool =
-  any(words, (word) => s.contains(word))
\ No newline at end of file
diff --git a/tests/closure/tinfer_closure_for_nestedproc.nim b/tests/closure/tinfer_closure_for_nestedproc.nim
new file mode 100644
index 000000000..6450d1492
--- /dev/null
+++ b/tests/closure/tinfer_closure_for_nestedproc.nim
@@ -0,0 +1,42 @@
+discard """
+  action: compile
+"""
+
+# bug #9441
+import asyncdispatch, asyncfutures, strtabs
+
+type
+  Request = object
+  Context = object
+    position: int
+    accept: bool
+    headers: StringTableRef
+  Handler = proc (r: ref Request, c: Context): Future[Context]
+
+proc respond(req: Request): Future[void] = discard
+
+proc handle*(h: Handler): auto = # (proc (req: Request): Future[void]) =
+  proc server(req: Request): Future[void] {.async.} =
+    let emptyCtx = Context(
+      position: 0,
+      accept: true,
+      headers: newStringTable()
+    )
+    var reqHeap = new(Request)
+    reqHeap[] = req
+    var
+      f: Future[Context]
+      ctx: Context
+    try:
+      f = h(reqHeap, emptyCtx)
+      ctx = await f
+    except:
+      discard
+    if f.failed:
+      await req.respond()
+    else:
+      if not ctx.accept:
+        await req.respond()
+  return server
+
+waitFor handle(nil)(Request())
diff --git a/tests/closure/tinterf.nim b/tests/closure/tinterf.nim
deleted file mode 100644
index 1ac6da945..000000000
--- a/tests/closure/tinterf.nim
+++ /dev/null
@@ -1,24 +0,0 @@
-discard """
-  output: '''56 66'''
-"""
-
-type
-  ITest = tuple[
-    setter: proc(v: int) {.closure.},
-    getter1: proc(): int {.closure.},
-    getter2: proc(): int {.closure.}]
-
-proc getInterf(): ITest =
-  var shared1, shared2: int
-
-  return (setter: proc (x: int) =
-            shared1 = x
-            shared2 = x + 10,
-          getter1: proc (): int = result = shared1,
-          getter2: proc (): int = return shared2)
-
-var i = getInterf()
-i.setter(56)
-
-echo i.getter1(), " ", i.getter2()
-
diff --git a/tests/closure/tinvalidclosure.nim b/tests/closure/tinvalidclosure.nim
index 4e5f61f06..37d0f68a2 100644
--- a/tests/closure/tinvalidclosure.nim
+++ b/tests/closure/tinvalidclosure.nim
@@ -1,6 +1,6 @@
 discard """
+  errormsg: "type mismatch: got <proc (x: int){.nimcall, gcsafe.}>"
   line: 12
-  errormsg: "type mismatch: got <proc (x: int){.gcsafe, locks: 0.}>"
 """
 
 proc ugh[T](x: T) {.nimcall.} =
diff --git a/tests/closure/tinvalidclosure2.nim b/tests/closure/tinvalidclosure2.nim
index 845559309..2d58f0215 100644
--- a/tests/closure/tinvalidclosure2.nim
+++ b/tests/closure/tinvalidclosure2.nim
@@ -1,6 +1,6 @@
 discard """
-  line: 10
   errormsg: "illegal capture 'A'"
+  line: 10
 """
 
 proc outer() =
diff --git a/tests/closure/tinvalidclosure3.nim b/tests/closure/tinvalidclosure3.nim
index 31c4976f8..0cbdaf39e 100644
--- a/tests/closure/tinvalidclosure3.nim
+++ b/tests/closure/tinvalidclosure3.nim
@@ -1,6 +1,6 @@
 discard """
-  line: 9
   errormsg: "illegal capture 'x'"
+  line: 9
 """
 
 proc outer(arg: string) =
@@ -9,4 +9,4 @@ proc outer(arg: string) =
     echo "inner", x
   inner()
 
-outer("abc")
\ No newline at end of file
+outer("abc")
diff --git a/tests/closure/tinvalidclosure4.nim b/tests/closure/tinvalidclosure4.nim
new file mode 100644
index 000000000..7985a2488
--- /dev/null
+++ b/tests/closure/tinvalidclosure4.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "illegal capture 'v'"
+  line: 7
+"""
+
+proc outer(v: int) =
+  proc b {.nimcall.} = echo v
+  b()
+outer(5)
diff --git a/tests/closure/tinvalidclosure5.nim b/tests/closure/tinvalidclosure5.nim
new file mode 100644
index 000000000..3b5f46a40
--- /dev/null
+++ b/tests/closure/tinvalidclosure5.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "type mismatch: got <proc (){.closure, gcsafe.}> but expected 'A = proc (){.nimcall.}'"
+  line: 9
+"""
+
+type A = proc() {.nimcall.}
+proc main =
+  let b = 1
+  let a: A = proc() = echo b
+
diff --git a/tests/closure/tissue1502def.nim b/tests/closure/tissue1502def.nim
deleted file mode 100644
index 0aa6b16e3..000000000
--- a/tests/closure/tissue1502def.nim
+++ /dev/null
@@ -1,6 +0,0 @@
-import sequtils
-let xs: seq[tuple[key: string, val: seq[string]]] = @[("foo", @["bar"])]
-
-let maps = xs.map(
-  proc(x: auto): tuple[typ: string, maps: seq[string]] =
-    (x.key, x.val.map(proc(x: string): string = x)))
\ No newline at end of file
diff --git a/tests/closure/tissue1642.nim b/tests/closure/tissue1642.nim
deleted file mode 100644
index 5b921fc05..000000000
--- a/tests/closure/tissue1642.nim
+++ /dev/null
@@ -1,3 +0,0 @@
-block:
-    var i = 0
-    proc p() = inc(i)
\ No newline at end of file
diff --git a/tests/closure/tissue1846.nim b/tests/closure/tissue1846.nim
deleted file mode 100644
index 3fbef169d..000000000
--- a/tests/closure/tissue1846.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-type
-    TBinOp*[T] = proc (x,y: T): bool
-
-    THeap*[T] = object
-        cmp*:   TBinOp[T]
-
-proc less*[T](x,y: T): bool =
-    x < y
-
-proc initHeap*[T](cmp: TBinOp[T]): THeap[T] =
-    result.cmp = cmp
-
-when isMainModule:
-    var h = initHeap[int](less[int])
-
-    echo h.cmp(2,3)
\ No newline at end of file
diff --git a/tests/closure/tissue1911.nim b/tests/closure/tissue1911.nim
deleted file mode 100644
index 311d99134..000000000
--- a/tests/closure/tissue1911.nim
+++ /dev/null
@@ -1,7 +0,0 @@
-proc foo(x: int) : auto =
-
-  proc helper() : int = x
-  proc bar() : int = helper()
-  proc baz() : int = helper()
-
-  return (bar, baz)
\ No newline at end of file
diff --git a/tests/closure/tissue600.nim b/tests/closure/tissue600.nim
deleted file mode 100644
index eacc7a123..000000000
--- a/tests/closure/tissue600.nim
+++ /dev/null
@@ -1,4 +0,0 @@
-for i in 1..1:
-  var reported = false
-  proc report() =
-    reported = true
\ No newline at end of file
diff --git a/tests/closure/tjester.nim b/tests/closure/tjester.nim
deleted file mode 100644
index 84e0fcb71..000000000
--- a/tests/closure/tjester.nim
+++ /dev/null
@@ -1,32 +0,0 @@
-discard """
-  output: '''baro0'''
-"""
-
-type
-  Future[T] = ref object
-    data: T
-    callback: proc () {.closure.}
-
-proc cbOuter(response: string) {.discardable.} =
-  iterator cbIter(): Future[int] {.closure.} =
-    for i in 0..7:
-      proc foo(): int =
-        iterator fooIter(): Future[int] {.closure.} =
-          echo response, i
-          yield Future[int](data: 17)
-        var iterVar = fooIter
-        iterVar().data
-      yield Future[int](data: foo())
-
-  var iterVar2 = cbIter
-  proc cb2() {.closure.} =
-    try:
-      if not finished(iterVar2):
-        let next = iterVar2()
-        if next != nil:
-          next.callback = cb2
-    except:
-      echo "WTF"
-  cb2()
-
-cbOuter "baro"
diff --git a/tests/closure/tmacrobust1512.nim b/tests/closure/tmacrobust1512.nim
index 5f13e8286..0f44c5e1a 100644
--- a/tests/closure/tmacrobust1512.nim
+++ b/tests/closure/tmacrobust1512.nim
@@ -1,8 +1,12 @@
+discard """
+output: ""
+"""
+
 import macros, strutils
 
 # https://github.com/nim-lang/Nim/issues/1512
 
-proc macrobust0(raw_input: string) =
+proc macrobust0(input: string): string =
   var output = ""
   proc p1(a:string) =
     output.add(a)
@@ -27,13 +31,9 @@ proc macrobust0(raw_input: string) =
   proc p19(a:string) = p18(a)
   proc p20(a:string) = p19(a)
 
-  let input = $raw_input
-
   for a in input.split():
     p20(a)
     p19(a)
-
-
     p18(a)
     p17(a)
     p16(a)
@@ -53,11 +53,9 @@ proc macrobust0(raw_input: string) =
     p2(a)
     p1(a)
 
+  result = output
 
-  echo output
-
-macro macrobust(raw_input: untyped): untyped =
-
+macro macrobust(input: static[string]): untyped =
   var output = ""
   proc p1(a:string) =
     output.add(a)
@@ -82,12 +80,9 @@ macro macrobust(raw_input: untyped): untyped =
   proc p19(a:string) = p18(a)
   proc p20(a:string) = p19(a)
 
-  let input = $raw_input
-
   for a in input.split():
     p20(a)
     p19(a)
-
     p18(a)
     p17(a)
     p16(a)
@@ -105,11 +100,11 @@ macro macrobust(raw_input: untyped): untyped =
     p4(a)
     p3(a)
     p2(a)
+    p1(a)
 
-  echo output
-  discard result
+  result = newLit(output)
 
-macrobust """
+const input = """
   fdsasadfsdfa sadfsdafsdaf
   dsfsdafdsfadsfa fsdaasdfasdf
   fsdafsadfsad asdfasdfasdf
@@ -122,16 +117,7 @@ macrobust """
   sdfasdafsadf sdfasdafsdaf sdfasdafsdaf
 """
 
+let str1 = macrobust(input)
+let str2 = macrobust0(input)
 
-macrobust0 """
-  fdsasadfsdfa sadfsdafsdaf
-  dsfsdafdsfadsfa fsdaasdfasdf
-  fsdafsadfsad asdfasdfasdf
-  fdsasdfasdfa sadfsadfsadf
-  sadfasdfsdaf sadfsdafsdaf dsfasdaf
-  sadfsdafsadf fdsasdafsadf fdsasadfsdaf
-  sdfasadfsdafdfsa sadfsadfsdaf
-  sdafsdaffsda sdfasadfsadf
-  fsdasdafsdfa sdfasdfafsda
-  sdfasdafsadf sdfasdafsdaf sdfasdafsdaf
-"""
+doAssert str1 == str2
diff --git a/tests/closure/tnamedparamanonproc.nim b/tests/closure/tnamedparamanonproc.nim
deleted file mode 100644
index 94e32894f..000000000
--- a/tests/closure/tnamedparamanonproc.nim
+++ /dev/null
@@ -1,14 +0,0 @@
-
-type
-  PButton = ref object
-  TButtonClicked = proc(button: PButton) {.nimcall.}
-
-proc newButton*(onClick: TButtonClicked) =
-  discard
-
-proc main() =
-  newButton(onClick = proc(b: PButton) =
-    var requestomat = 12
-    )
-
-main()
diff --git a/tests/closure/tnested.nim b/tests/closure/tnested.nim
new file mode 100644
index 000000000..ec5af9b13
--- /dev/null
+++ b/tests/closure/tnested.nim
@@ -0,0 +1,215 @@
+discard """
+targets: "c js"
+output: '''
+foo88
+23 24foo 88
+foo88
+23 24foo 88
+11
+int: 108
+0
+11
+1
+11
+2
+11
+3
+11
+4
+11
+5
+11
+6
+11
+7
+11
+8
+11
+9
+11
+11
+py
+py
+py
+py
+px
+6
+proc (){.closure, noSideEffect, gcsafe.}
+'''
+"""
+
+
+block tnestedclosure:
+  proc main(param: int) =
+    var foo = 23
+    proc outer(outerParam: string) =
+      var outerVar = 88
+      echo outerParam, outerVar
+      proc inner() =
+        block Test:
+          echo foo, " ", param, outerParam, " ", outerVar
+      inner()
+    outer("foo")
+
+  # test simple closure within dummy 'main':
+  proc dummy =
+    proc main2(param: int) =
+      var fooB = 23
+      proc outer(outerParam: string) =
+        var outerVar = 88
+        echo outerParam, outerVar
+        proc inner() =
+          block Test:
+            echo fooB, " ", param, outerParam, " ", outerVar
+        inner()
+      outer("foo")
+    main2(24)
+
+  dummy()
+
+  main(24)
+
+  # Jester + async triggered this bug:
+  proc cbOuter() =
+    var response = "hohoho"
+    block:
+      proc cbIter() =
+        block:
+          proc fooIter() =
+            doAssert response == "hohoho"
+          fooIter()
+      cbIter()
+  cbOuter()
+
+
+block tnestedproc:
+  proc p(x, y: int): int =
+    result = x + y
+
+  echo p((proc (): int =
+            var x = 7
+            return x)(),
+         (proc (): int = return 4)())
+
+
+block deeplynested:
+  # bug #4070
+  proc id(f: (proc())): auto =
+    return f
+
+  proc foo(myinteger: int): (iterator(): int) =
+    return iterator(): int {.closure.} =
+            proc bar() =
+              proc kk() =
+                echo "int: ", myinteger
+              kk()
+            id(bar)()
+
+  discard foo(108)()
+
+
+block tclosure2:
+  when true:
+    proc ax =
+      for xxxx in 0..9:
+        var i = 0
+        proc bx =
+          if i > 10:
+            echo xxxx
+            return
+          i += 1
+          #for j in 0 .. 0: echo i
+          bx()
+
+        bx()
+        echo i
+
+    ax()
+
+  when true:
+    proc accumulator(start: int): (proc(): int {.closure.}) =
+      var x = start-1
+      #let dummy = proc =
+      #  discard start
+
+      result = proc (): int =
+        #var x = 9
+        for i in 0 .. 0: x = x + 1
+
+        return x
+
+    var a = accumulator(3)
+    let b = accumulator(4)
+    echo a() + b() + a()
+
+
+    proc outer =
+
+      proc py() =
+        # no closure here:
+        for i in 0..3: echo "py"
+
+      py()
+
+    outer()
+
+
+  when true:
+    proc outer2 =
+      var errorValue = 3
+      proc fac[T](n: T): T =
+        if n < 0: result = errorValue
+        elif n <= 1: result = 1
+        else: result = n * fac(n-1)
+
+      proc px() {.closure.} =
+        echo "px"
+
+      proc py() {.closure.} =
+        echo "py"
+
+      let
+        mapping = {
+          "abc": px,
+          "xyz": py
+        }
+      mapping[0][1]()
+
+      echo fac(3)
+
+
+    outer2()
+
+# bug #5688
+
+import typetraits
+
+block:
+  proc myDiscard[T](a: T) = discard
+
+  proc foo() =
+    let a = 5
+    let f = (proc() =
+              myDiscard (proc() = echo a)
+            )
+    echo name(typeof(f))
+
+  foo()
+
+
+block:
+  iterator foo: int {.closure.} =
+    yield 1
+    yield 2
+    yield 3
+
+  proc pork =
+    let call = foo
+    for i in call():
+      discard i
+
+    let call2 = foo
+    while not finished(call2):
+      discard call2()
+
+  pork()
diff --git a/tests/closure/tnestedclosure.nim b/tests/closure/tnestedclosure.nim
deleted file mode 100644
index 0628a6977..000000000
--- a/tests/closure/tnestedclosure.nim
+++ /dev/null
@@ -1,51 +0,0 @@
-discard """
-  output: '''foo88
-23 24foo 88
-foo88
-23 24foo 88
-hohoho'''
-"""
-
-# test nested closure
-proc main(param: int) =
-  var foo = 23
-  proc outer(outerParam: string) =
-    var outerVar = 88
-    echo outerParam, outerVar
-    proc inner() =
-      block Test:
-        echo foo, " ", param, outerParam, " ", outerVar
-    inner()
-  outer("foo")
-
-# test simple closure within dummy 'main':
-proc dummy =
-  proc main2(param: int) =
-    var fooB = 23
-    proc outer(outerParam: string) =
-      var outerVar = 88
-      echo outerParam, outerVar
-      proc inner() =
-        block Test:
-          echo fooB, " ", param, outerParam, " ", outerVar
-      inner()
-    outer("foo")
-  main2(24)
-
-dummy()
-
-main(24)
-
-# Jester + async triggered this bug:
-proc cbOuter() =
-  var response = "hohoho"
-  block:
-    proc cbIter() =
-      block:
-        proc fooIter() =
-          echo response
-        fooIter()
-
-    cbIter()
-
-cbOuter()
diff --git a/tests/closure/tnestedproc.nim b/tests/closure/tnestedproc.nim
deleted file mode 100644
index 7eeeff198..000000000
--- a/tests/closure/tnestedproc.nim
+++ /dev/null
@@ -1,12 +0,0 @@
-discard """
-  output: "11"
-"""
-
-proc p(x, y: int): int =
-  result = x + y
-
-echo p((proc (): int =
-          var x = 7
-          return x)(),
-       (proc (): int = return 4)())
-
diff --git a/tests/closure/tnoclosure.nim b/tests/closure/tnoclosure.nim
deleted file mode 100644
index 25cce0040..000000000
--- a/tests/closure/tnoclosure.nim
+++ /dev/null
@@ -1,25 +0,0 @@
-discard """
-  output: '''@[1]
-@[1, 1]
-@[1, 2, 1]
-@[1, 3, 3, 1]
-@[1, 4, 6, 4, 1]
-@[1, 5, 10, 10, 5, 1]
-@[1, 6, 15, 20, 15, 6, 1]
-@[1, 7, 21, 35, 35, 21, 7, 1]
-@[1, 8, 28, 56, 70, 56, 28, 8, 1]
-@[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]'''
-"""
-
-import sequtils
-
-proc pascal(n: int) =
-  var row = @[1]
-  for r in 1..n:
-    echo row
-    row = zip(row & @[0], @[0] & row).mapIt(it[0] + it[1])
-
-pascal(10)
-
-# bug #3499 last snippet fixed
-# bug 705  last snippet fixed
diff --git a/tests/closure/tstmtlist.nim b/tests/closure/tstmtlist.nim
new file mode 100644
index 000000000..6a1390617
--- /dev/null
+++ b/tests/closure/tstmtlist.nim
@@ -0,0 +1,9 @@
+discard """
+  action: compile
+"""
+
+proc foo(x: proc()) = x()
+foo: echo "a" #[tt.Warning
+     ^ statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead [StmtListLambda]]#
+foo do: echo "b" #[tt.Warning
+        ^ statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead [StmtListLambda]]#
diff --git a/tests/closure/ttimeinfo.nim b/tests/closure/ttimeinfo.nim
index 3138ae72e..24d535cbf 100644
--- a/tests/closure/ttimeinfo.nim
+++ b/tests/closure/ttimeinfo.nim
@@ -1,15 +1,22 @@
+discard """
+output: '''
+@[2000-01-01T00:00:00Z, 2001-01-01T00:00:00Z, 2002-01-01T00:00:00Z, 2003-01-01T00:00:00Z, 2004-01-01T00:00:00Z, 2005-01-01T00:00:00Z, 2006-01-01T00:00:00Z, 2007-01-01T00:00:00Z, 2008-01-01T00:00:00Z, 2009-01-01T00:00:00Z, 2010-01-01T00:00:00Z, 2011-01-01T00:00:00Z, 2012-01-01T00:00:00Z, 2013-01-01T00:00:00Z, 2014-01-01T00:00:00Z, 2015-01-01T00:00:00Z]
+@[2000-01-01T00:00:00Z, 2001-01-01T00:00:00Z, 2002-01-01T00:00:00Z, 2003-01-01T00:00:00Z, 2004-01-01T00:00:00Z, 2005-01-01T00:00:00Z, 2006-01-01T00:00:00Z, 2007-01-01T00:00:00Z, 2008-01-01T00:00:00Z, 2009-01-01T00:00:00Z, 2010-01-01T00:00:00Z, 2011-01-01T00:00:00Z, 2012-01-01T00:00:00Z, 2013-01-01T00:00:00Z, 2014-01-01T00:00:00Z, 2015-01-01T00:00:00Z]
+'''
+"""
+
 # bug #2073
 
 import sequtils
 import times
 
 # 1
-proc f(n: int): TimeInfo =
-  TimeInfo(year: n, month: mJan, monthday: 1)
+proc f(n: int): DateTime =
+  initDateTime(1, mJan, n, 0, 0, 0, utc())
 
 echo toSeq(2000 || 2015).map(f)
 
 # 2
-echo toSeq(2000 || 2015).map(proc (n: int): TimeInfo =
-  TimeInfo(year: n, month: mJan, monthday: 1)
+echo toSeq(2000 || 2015).map(proc (n: int): DateTime =
+  initDateTime(1, mJan, n, 0, 0, 0, utc())
 )
diff --git a/tests/cnstseq/tcnstseq.nim b/tests/cnstseq/tcnstseq.nim
deleted file mode 100644
index ad0527f10..000000000
--- a/tests/cnstseq/tcnstseq.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-discard """
-  file: "tcnstseq.nim"
-  output: "AngelikaAnneAnnaAnkaAnja"
-"""
-# Test the new implicit conversion from sequences to arrays in a constant
-# context.
-
-import strutils
-
-const
-  myWords = "Angelika Anne Anna Anka Anja".split()
-
-for x in items(myWords):
-  write(stdout, x) #OUT AngelikaAnneAnnaAnkaAnja
-
-
-
diff --git a/tests/cnstseq/tcnstseq2.nim b/tests/cnstseq/tcnstseq2.nim
deleted file mode 100644
index 2f364d278..000000000
--- a/tests/cnstseq/tcnstseq2.nim
+++ /dev/null
@@ -1,12 +0,0 @@
-discard """
-  output: "AngelikaAnneAnnaAnkaAnja"
-"""
-
-const
-  myWords = @["Angelika", "Anne", "Anna", "Anka", "Anja"]
-
-for i in 0 .. high(myWords):
-  write(stdout, myWords[i]) #OUT AngelikaAnneAnnaAnkaAnja
-
-
-
diff --git a/tests/cnstseq/tcnstseq3.nim b/tests/cnstseq/tcnstseq3.nim
deleted file mode 100644
index e59516e85..000000000
--- a/tests/cnstseq/tcnstseq3.nim
+++ /dev/null
@@ -1,7 +0,0 @@
-discard """
-  output: "AngelikaAnneAnnaAnkaAnja"
-"""
-
-for w in items(["Angelika", "Anne", "Anna", "Anka", "Anja"]):
-  write(stdout, w) #OUT AngelikaAnneAnnaAnkaAnja
-
diff --git a/tests/codegen/titaniummangle.nim b/tests/codegen/titaniummangle.nim
new file mode 100644
index 000000000..4b45e59ae
--- /dev/null
+++ b/tests/codegen/titaniummangle.nim
@@ -0,0 +1,193 @@
+discard """
+  targets: "c"
+  matrix: "--debugger:native"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE6stringN14titaniummangle3FooE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3int7varargsI6stringE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle3BooE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE8typeDescIN14titaniummangle17EnumAnotherSampleEE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3ptrI14uncheckedArrayI3intEE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3setIN14titaniummangle10EnumSampleEE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE4procI6string6stringE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3intN10Comparable10ComparableE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3int3int'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle10EnumSampleE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle17EnumAnotherSampleE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3int3int'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle10EnumSampleE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle17EnumAnotherSampleE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE5tupleI3int3intE7cstring'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE5tupleI5float5floatE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3ptrI3intE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3ptrIN14titaniummangle3FooEE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3ptrI3ptrI3intEE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3refIN14titaniummangle3FooEE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3varIN14titaniummangle3FooEE5int325int323refIN14titaniummangle3FooEE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3varI3intE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE9openArrayI6stringE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE5arrayI7range013intE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE9ContainerI3intE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE10Container2I5int325int32E'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE9ContainerI10Container2I5int325int32EE'"
+"""
+
+#When debugging this notice that if one check fails, it can be due to any of the above.
+
+type
+  Comparable = concept x, y
+    (x < y) is bool
+
+  Foo = object
+    a: int32
+    b: int32
+
+  FooTuple = tuple
+    a: int
+    b: int
+
+  Container[T] = object
+    data: T
+      
+  Container2[T, T2] = object
+    data: T
+    data2: T2
+
+  Boo = distinct Foo
+
+  Coo = Foo
+
+  Doo = Boo | Foo 
+
+  TestProc = proc(a:string): string
+
+type EnumSample = enum
+  a, b, c
+
+type EnumAnotherSample = enum
+  a, b, c
+
+proc testFunc(a: set[EnumSample]) = 
+  echo $a
+
+proc testFunc(a: typedesc) = 
+  echo $a
+
+proc testFunc(a: ptr Foo) = 
+  echo repr a
+
+proc testFunc(s: string, a: Coo) = 
+  echo repr a
+
+proc testFunc(s: int, a: Comparable) = 
+  echo repr a
+
+proc testFunc(a: TestProc) = 
+  let b = ""
+  echo repr a("")
+
+proc testFunc(a: ref Foo) = 
+  echo repr a
+
+proc testFunc(b: Boo) = 
+  echo repr b
+
+proc testFunc(a: ptr UncheckedArray[int]) = 
+  echo repr a
+
+proc testFunc(a: ptr int) = 
+  echo repr a
+
+proc testFunc(a: ptr ptr int) = 
+  echo repr a
+
+proc testFunc(e: FooTuple, str: cstring) = 
+  echo e
+
+proc testFunc(e: (float, float)) = 
+  echo e
+
+proc testFunc(e: EnumSample) = 
+  echo e
+
+proc testFunc(e: var int) = 
+  echo e
+
+proc testFunc(e: var Foo, a, b: int32, refFoo: ref Foo) = 
+  echo e
+
+proc testFunc(xs: Container[int]) = 
+  let a = 2
+  echo xs
+
+proc testFunc(xs: Container2[int32, int32]) = 
+  let a = 2
+  echo xs
+
+proc testFunc(xs: Container[Container2[int32, int32]]) = 
+  let a = 2
+  echo xs
+
+proc testFunc(xs: seq[int]) = 
+  let a = 2
+  echo xs
+
+proc testFunc(xs: openArray[string]) = 
+  let a = 2
+  echo xs
+
+proc testFunc(xs: array[2, int]) = 
+  let a = 2
+  echo xs
+
+proc testFunc(e: EnumAnotherSample) = 
+  echo e
+
+proc testFunc(a, b: int) = 
+  echo "hola"
+  discard
+
+proc testFunc(a: int, xs: varargs[string]) = 
+  let a = 10
+  for x in xs:
+    echo x
+
+proc testFunc() = 
+  var a = 2
+  var aPtr = a.addr
+  var foo = Foo()
+  let refFoo : ref Foo = new(Foo)
+  let b = Foo().Boo()
+  let d: Doo = Foo()
+  testFunc("", Coo())
+  testFunc(1, )
+  testFunc(b)
+  testFunc(EnumAnotherSample)
+  var t = [1, 2]
+  let uArr = cast[ptr UncheckedArray[int]](t.addr)
+  testFunc(uArr)
+  testFunc({})
+  testFunc(proc(s:string): string = "test")
+  testFunc(20, a.int32)
+  testFunc(20, 2)
+  testFunc(EnumSample.c)
+  testFunc(EnumAnotherSample.c)
+  testFunc((2, 1), "adios")
+  testFunc((22.1, 1.2))
+  testFunc(a.addr)
+  testFunc(foo.addr)
+  testFunc(aPtr.addr)
+  testFunc(refFoo)
+  testFunc(foo, 2, 1, refFoo)
+  testFunc(a)
+  testFunc(@[2, 1, 2])
+  testFunc(@["hola"])
+  testFunc(2, "hola", "adios")
+  let arr: array[2, int] = [2, 1]
+  testFunc(arr)
+  testFunc(Container[int](data: 10))
+  let c2 = Container2[int32, int32](data: 10, data2: 20)
+  testFunc(c2)
+  testFunc(Container[Container2[int32, int32]](data: c2))
+  
+
+testFunc()
\ No newline at end of file
diff --git a/tests/collections/tableadds.nim b/tests/collections/tableadds.nim
deleted file mode 100644
index 71f1fad7d..000000000
--- a/tests/collections/tableadds.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-discard """
-  output: '''done'''
-"""
-
-import tables
-
-proc main =
-  var tab = newTable[string, string]()
-  for i in 0..1000:
-    tab.add "key", "value " & $i
-
-main()
-echo "done"
diff --git a/tests/collections/tactiontable.nim b/tests/collections/tactiontable.nim
new file mode 100644
index 000000000..3f15a70bd
--- /dev/null
+++ b/tests/collections/tactiontable.nim
@@ -0,0 +1,37 @@
+discard """
+  output: '''
+action 3 arg
+action 3 arg
+'''
+"""
+
+import tables
+
+proc action1(arg: string) =
+  echo "action 1 ", arg
+
+proc action2(arg: string) =
+  echo "action 2 ", arg
+
+proc action3(arg: string) =
+  echo "action 3 ", arg
+
+proc action4(arg: string) =
+  echo "action 4 ", arg
+
+var
+  actionTable1 = {
+    "A": action1,
+    "B": action2,
+    "C": action3,
+    "D": action4}.toTable
+
+const
+  actionTable2 = {
+    "A": action1,
+    "B": action2,
+    "C": action3,
+    "D": action4}.toTable
+
+actionTable1["C"]("arg")
+actionTable2["C"]("arg")
diff --git a/tests/collections/tapply.nim b/tests/collections/tapply.nim
deleted file mode 100644
index 2b7464216..000000000
--- a/tests/collections/tapply.nim
+++ /dev/null
@@ -1,11 +0,0 @@
-discard """
-  output: '''true'''
-"""
-
-import sequtils
-
-var x = @[1, 2, 3]
-x.apply(proc(x: var int) = x = x+10)
-x.apply(proc(x: int): int = x+100)
-x.applyIt(it+5000)
-echo x == @[5111, 5112, 5113]
diff --git a/tests/collections/tcollections.nim b/tests/collections/tcollections.nim
new file mode 100644
index 000000000..7677f7c1a
--- /dev/null
+++ b/tests/collections/tcollections.nim
@@ -0,0 +1,105 @@
+discard """
+  targets: "c js"
+"""
+
+# see also: tdeques, tlists, tcritbits
+
+import sets, tables, sequtils
+
+block tapply:
+  var x = @[1, 2, 3]
+  x.apply(proc(x: var int) = x = x+10)
+  x.apply(proc(x: int): int = x+100)
+  x.applyIt(it+5000)
+  doAssert x == @[5111, 5112, 5113]
+
+block tmapit:
+  var x = @[1, 2, 3]
+  # This mapIt call will run with preallocation because ``len`` is available.
+  var y = x.mapIt($(it+10))
+  doAssert y == @["11", "12", "13"]
+
+  type structureWithoutLen = object
+    a: array[5, int]
+
+  iterator items(s: structureWithoutLen): int {.inline.} =
+    yield s.a[0]
+    yield s.a[1]
+    yield s.a[2]
+    yield s.a[3]
+    yield s.a[4]
+
+  var st: structureWithoutLen
+  st.a[0] = 0
+  st.a[1] = 1
+  st.a[2] = 2
+  st.a[3] = 3
+  st.a[4] = 4
+
+  # this will run without preallocating the result
+  # since ``len`` is not available
+  var r = st.mapIt($(it+10))
+  doAssert r == @["10", "11", "12", "13", "14"]
+
+
+
+# Collections to string:
+
+# Tests for tuples
+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]"
+doAssert $(@["1", "2", "3"]) == """@["1", "2", "3"]"""
+doAssert $(@['1', '2', '3']) == """@['1', '2', '3']"""
+
+# Tests for sets
+doAssert $(toHashSet([1])) == "{1}"
+doAssert $(toHashSet(["1"])) == """{"1"}"""
+doAssert $(toHashSet(['1'])) == """{'1'}"""
+doAssert $(toOrderedSet([1, 2, 3])) == "{1, 2, 3}"
+doAssert $(toOrderedSet(["1", "2", "3"])) == """{"1", "2", "3"}"""
+doAssert $(toOrderedSet(['1', '2', '3'])) == """{'1', '2', '3'}"""
+
+# see also: tcritbitsToString, tlistsToString
+
+# Tests for tables
+when defined(nimIntHash1):
+  doAssert $({1: "1", 2: "2"}.toTable) == """{1: "1", 2: "2"}"""
+else:
+  doAssert $({1: "1", 2: "2"}.toTable) == """{2: "2", 1: "1"}"""
+let tabStr = $({"1": 1, "2": 2}.toTable)
+doAssert (tabStr == """{"2": 2, "1": 1}""" or tabStr == """{"1": 1, "2": 2}""")
+
+# Test escaping behavior
+block:
+  var s = ""
+  s.addQuoted('\0')
+  s.addQuoted('\31')
+  s.addQuoted('\127')
+  doAssert s == "'\\x00''\\x1F''\\x7F'"
+block:
+  var s = ""
+  s.addQuoted('\\')
+  s.addQuoted('\'')
+  s.addQuoted('\"')
+  doAssert s == """'\\''\'''\"'"""
+block:
+  var s = ""
+  s.addQuoted("å")
+  s.addQuoted("ä")
+  s.addQuoted("ö")
+  s.addEscapedChar('\xFF')
+  doAssert s == """"å""ä""ö"\xFF"""
+
+# Test customized element representation
+type CustomString = object
+
+proc addQuoted(s: var string, x: CustomString) =
+  s.add("<CustomString>")
+
+block:
+  let s = @[CustomString()]
+  doAssert $s == "@[<CustomString>]"
diff --git a/tests/collections/tcollections_to_string.nim b/tests/collections/tcollections_to_string.nim
index 0c4f1e91c..62ba87334 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]"
@@ -19,15 +19,18 @@ doAssert $(@["1", "2", "3"]) == """@["1", "2", "3"]"""
 doAssert $(@['1', '2', '3']) == """@['1', '2', '3']"""
 
 # Tests for sets
-doAssert $(toSet([1])) == "{1}"
-doAssert $(toSet(["1"])) == """{"1"}"""
-doAssert $(toSet(['1'])) == """{'1'}"""
+doAssert $(toHashSet([1])) == "{1}"
+doAssert $(toHashSet(["1"])) == """{"1"}"""
+doAssert $(toHashSet(['1'])) == """{'1'}"""
 doAssert $(toOrderedSet([1, 2, 3])) == "{1, 2, 3}"
 doAssert $(toOrderedSet(["1", "2", "3"])) == """{"1", "2", "3"}"""
 doAssert $(toOrderedSet(['1', '2', '3'])) == """{'1', '2', '3'}"""
 
 # Tests for tables
-doAssert $({1: "1", 2: "2"}.toTable) == """{1: "1", 2: "2"}"""
+when defined(nimIntHash1):
+  doAssert $({1: "1", 2: "2"}.toTable) == """{1: "1", 2: "2"}"""
+else:
+  doAssert $({1: "1", 2: "2"}.toTable) == """{2: "2", 1: "1"}"""
 doAssert $({"1": 1, "2": 2}.toTable) == """{"1": 1, "2": 2}"""
 
 # Tests for deques
diff --git a/tests/collections/tcounttable.nim b/tests/collections/tcounttable.nim
deleted file mode 100644
index ebbb1c8e5..000000000
--- a/tests/collections/tcounttable.nim
+++ /dev/null
@@ -1,19 +0,0 @@
-discard """
-  output: "And we get here"
-"""
-
-# bug #2625
-
-const s_len = 32
-
-import tables
-var substr_counts: CountTable[string] = initCountTable[string]()
-var my_string = "Hello, this is sadly broken for strings over 64 characters. Note that it *does* appear to work for short strings."
-for i in 0..(my_string.len - s_len):
-  let s = my_string[i..i+s_len-1]
-  substr_counts[s] = 1
-  # substr_counts[s] = substr_counts[s] + 1  # Also breaks, + 2 as well, etc.
-  # substr_counts.inc(s)  # This works
-  #echo "Iteration ", i
-
-echo "And we get here"
diff --git a/tests/collections/tdeques.nim b/tests/collections/tdeques.nim
deleted file mode 100644
index 664ce4324..000000000
--- a/tests/collections/tdeques.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-discard """
-  output: '''true'''
-"""
-
-import deques
-
-
-proc index(self: Deque[int], idx: Natural): int =
-  self[idx]
-
-proc main =
-  var testDeque = initDeque[int]()
-  testDeque.addFirst(1)
-  assert testDeque.index(0) == 1
-
-main()
-echo "true"
diff --git a/tests/collections/thashes.nim b/tests/collections/thashes.nim
deleted file mode 100644
index 5cc3cc8bb..000000000
--- a/tests/collections/thashes.nim
+++ /dev/null
@@ -1,90 +0,0 @@
-discard """
-  output: '''true'''
-"""
-
-import tables
-from hashes import Hash
-
-# Test with int
-block:
-  var t = initTable[int,int]()
-  t[0] = 42
-  t[1] = t[0] + 1
-  assert(t[0] == 42)
-  assert(t[1] == 43)
-  let t2 = {1: 1, 2: 2}.toTable
-  assert(t2[2] == 2)
-
-# Test with char
-block:
-  var t = initTable[char,int]()
-  t['0'] = 42
-  t['1'] = t['0'] + 1
-  assert(t['0'] == 42)
-  assert(t['1'] == 43)
-  let t2 = {'1': 1, '2': 2}.toTable
-  assert(t2['2'] == 2)
-
-# Test with enum
-block:
-  type
-    E = enum eA, eB, eC
-  var t = initTable[E,int]()
-  t[eA] = 42
-  t[eB] = t[eA] + 1
-  assert(t[eA] == 42)
-  assert(t[eB] == 43)
-  let t2 = {eA: 1, eB: 2}.toTable
-  assert(t2[eB] == 2)
-
-# Test with range
-block:
-  type
-    R = range[1..10]
-  var t = initTable[R,int]() # causes warning, why?
-  t[1] = 42 # causes warning, why?
-  t[2] = t[1] + 1
-  assert(t[1] == 42)
-  assert(t[2] == 43)
-  let t2 = {1.R: 1, 2.R: 2}.toTable
-  assert(t2[2.R] == 2)
-
-# Test which combines the generics for tuples + ordinals
-block:
-  type
-    E = enum eA, eB, eC
-  var t = initTable[(string, E, int, char), int]()
-  t[("a", eA, 0, '0')] = 42
-  t[("b", eB, 1, '1')] = t[("a", eA, 0, '0')] + 1
-  assert(t[("a", eA, 0, '0')] == 42)
-  assert(t[("b", eB, 1, '1')] == 43)
-  let t2 = {("a", eA, 0, '0'): 1, ("b", eB, 1, '1'): 2}.toTable
-  assert(t2[("b", eB, 1, '1')] == 2)
-
-# Test to check if overloading is possible
-# Unfortunately, this does not seem to work for int
-# The same test with a custom hash(s: string) does
-# work though.
-block:
-  proc hash(x: int): Hash {.inline.} =
-    echo "overloaded hash"
-    result = x
-  var t = initTable[int, int]()
-  t[0] = 0
-
-# Check hashability of all integer types (issue #5429)
-block:
-  let intTables = (
-    newTable[int, string](),
-    newTable[int8, string](),
-    newTable[int16, string](),
-    newTable[int32, string](),
-    newTable[int64, string](),
-    newTable[uint, string](),
-    newTable[uint8, string](),
-    newTable[uint16, string](),
-    newTable[uint32, string](),
-    newTable[uint64, string](),
-  )
-
-echo "true"
diff --git a/tests/collections/thashsets.nim b/tests/collections/thashsets.nim
new file mode 100644
index 000000000..359eaa51e
--- /dev/null
+++ b/tests/collections/thashsets.nim
@@ -0,0 +1,382 @@
+import sets, hashes, algorithm
+
+
+block setEquality:
+  var
+    a = initHashSet[int]()
+    b = initHashSet[int]()
+    c = initHashSet[string]()
+
+  for i in 0..5: a.incl(i)
+  for i in 1..6: b.incl(i)
+  for i in 0..5: c.incl($i)
+
+  doAssert map(a, proc(x: int): int = x + 1) == b
+  doAssert map(a, proc(x: int): string = $x) == c
+
+
+block setsContainingTuples:
+  var set = initHashSet[tuple[i: int, i64: int64, f: float]]()
+  set.incl( (i: 123, i64: 123'i64, f: 3.14) )
+  doAssert set.contains( (i: 123, i64: 123'i64, f: 3.14) )
+  doAssert( not set.contains( (i: 456, i64: 789'i64, f: 2.78) ) )
+
+
+block setWithTuplesWithSeqs:
+  var s = initHashSet[tuple[s: seq[int]]]()
+  s.incl( (s: @[1, 2, 3]) )
+  doAssert s.contains( (s: @[1, 2, 3]) )
+  doAssert( not s.contains((s: @[4, 5, 6])) )
+
+
+block setWithSequences:
+  var s = initHashSet[seq[int]]()
+  s.incl( @[1, 2, 3] )
+  doAssert s.contains(@[1, 2, 3])
+  doAssert( not s.contains(@[4, 5, 6]) )
+
+block setClearWorked:
+  var s = initHashSet[char]()
+
+  for c in "this is a test":
+    s.incl(c)
+
+  doAssert len(s) == 7
+  clear(s)
+  doAssert len(s) == 0
+
+  s.incl('z')
+  for c in "this is a test":
+    s.incl(c)
+
+  doAssert len(s) == 8
+
+block orderedSetClearWorked:
+  var s = initOrderedSet[char]()
+
+  for c in "eat at joes":
+    s.incl(c)
+
+  var r = ""
+
+  for c in items(s):
+    add(r, c)
+
+  doAssert r == "eat jos"
+  clear(s)
+
+  s.incl('z')
+  for c in "eat at joes":
+    s.incl(c)
+
+  r = ""
+  for c in items(s):
+    add(r, c)
+
+  doAssert r == "zeat jos"
+
+block hashForHashedSet:
+  let
+    seq1 = "This is the test."
+    seq2 = "the test is This."
+    s1 = seq1.toHashSet()
+    s2 = seq2.toHashSet()
+  doAssert s1 == s2
+  doAssert hash(s1) == hash(s2)
+
+block hashForOrderdSet:
+  let
+    str = "This is the test."
+    rstr = str.reversed
+
+  var
+    s1 = initOrderedSet[char]()
+    s2 = initOrderedSet[char]()
+    r = initOrderedSet[char]()
+    expected: Hash
+    added: seq[char] = @[]
+    reversed: Hash
+    radded: seq[char] = @[]
+
+  expected = 0
+  for c in str:
+    if (not (c in added)):
+      expected = expected !& hash(c)
+      added.add(c)
+    s1.incl(c)
+    s2.incl(c)
+  expected = !$expected
+  doAssert hash(s1) == expected
+  doAssert hash(s1) == hash(s2)
+  doAssert hash(s1) != hash(r)
+
+  reversed = 0
+  for c in rstr:
+    if (not (c in radded)):
+      reversed = reversed !& hash(c)
+      radded.add(c)
+    r.incl(c)
+  reversed = !$reversed
+  doAssert hash(r) == reversed
+  doAssert hash(s1) != reversed
+
+
+proc testModule() =
+  ## Internal micro test to validate docstrings and such.
+  block lenTest:
+    var values: HashSet[int]
+    doAssert values.len == 0
+    doAssert values.card == 0
+
+  block setIterator:
+    type pair = tuple[a, b: int]
+    var a, b = initHashSet[pair]()
+    a.incl((2, 3))
+    a.incl((3, 2))
+    a.incl((2, 3))
+    for x, y in a.items:
+      b.incl((x - 2, y + 1))
+    doAssert a.len == b.card
+    doAssert a.len == 2
+    #echo b
+
+  block setContains:
+    var values = initHashSet[int]()
+    doAssert(not values.contains(2))
+    values.incl(2)
+    doAssert values.contains(2)
+    values.excl(2)
+    doAssert(not values.contains(2))
+
+    values.incl(4)
+    var others = toHashSet([6, 7])
+    values.incl(others)
+    doAssert values.len == 3
+
+    values.init
+    doAssert values.containsOrIncl(2) == false
+    doAssert values.containsOrIncl(2) == true
+    var
+      a = toHashSet([1, 2])
+      b = toHashSet([1])
+    b.incl(2)
+    doAssert a == b
+
+  block exclusions:
+    var s = toHashSet([2, 3, 6, 7])
+    s.excl(2)
+    s.excl(2)
+    doAssert s.len == 3
+
+    var
+      numbers = toHashSet([1, 2, 3, 4, 5])
+      even = toHashSet([2, 4, 6, 8])
+    numbers.excl(even)
+    #echo numbers
+    # --> {1, 3, 5}
+
+  block toSeqAndString:
+    var a = toHashSet([2, 7, 5])
+    var b = initHashSet[int](a.len)
+    for x in [2, 7, 5]: b.incl(x)
+    doAssert($a == $b)
+    #echo a
+    #echo toHashSet(["no", "esc'aping", "is \" provided"])
+
+  #block orderedToSeqAndString:
+  #  echo toOrderedSet([2, 4, 5])
+  #  echo toOrderedSet(["no", "esc'aping", "is \" provided"])
+
+  block setOperations:
+    var
+      a = toHashSet(["a", "b"])
+      b = toHashSet(["b", "c"])
+      c = union(a, b)
+    doAssert c == toHashSet(["a", "b", "c"])
+    var d = intersection(a, b)
+    doAssert d == toHashSet(["b"])
+    var e = difference(a, b)
+    doAssert e == toHashSet(["a"])
+    var f = symmetricDifference(a, b)
+    doAssert f == toHashSet(["a", "c"])
+    doAssert d < a and d < b
+    doAssert((a < a) == false)
+    doAssert d <= a and d <= b
+    doAssert((a <= a))
+    # Alias test.
+    doAssert a + b == toHashSet(["a", "b", "c"])
+    doAssert a * b == toHashSet(["b"])
+    doAssert a - b == toHashSet(["a"])
+    doAssert a -+- b == toHashSet(["a", "c"])
+    doAssert disjoint(a, b) == false
+    doAssert disjoint(a, b - a) == true
+
+  block mapSet:
+    var a = toHashSet([1, 2, 3])
+    var b = a.map(proc (x: int): string = $x)
+    doAssert b == toHashSet(["1", "2", "3"])
+
+  block lenTest:
+    var values: OrderedSet[int]
+    doAssert values.len == 0
+    doAssert values.card == 0
+
+  block setIterator:
+    type pair = tuple[a, b: int]
+    var a, b = initOrderedSet[pair]()
+    a.incl((2, 3))
+    a.incl((3, 2))
+    a.incl((2, 3))
+    for x, y in a.items:
+      b.incl((x - 2, y + 1))
+    doAssert a.len == b.card
+    doAssert a.len == 2
+
+  block setPairsIterator:
+    var s = toOrderedSet([1, 3, 5, 7])
+    var items = newSeq[tuple[a: int, b: int]]()
+    for idx, item in s: items.add((idx, item))
+    doAssert items == @[(0, 1), (1, 3), (2, 5), (3, 7)]
+
+  block exclusions:
+    var s = toOrderedSet([1, 2, 3, 6, 7, 4])
+
+    s.excl(3)
+    s.excl(3)
+    s.excl(1)
+    s.excl(4)
+
+    var items = newSeq[int]()
+    for item in s: items.add item
+    doAssert items == @[2, 6, 7]
+
+  block: #9005
+    var s = initOrderedSet[(int, int)]()
+    for i in 0 .. 30: incl(s, (i, 0))
+    for i in 0 .. 30: excl(s, (i, 0))
+    doAssert s.len == 0
+
+  #block orderedSetIterator:
+  #  var a = initOrderedSet[int]()
+  #  for value in [9, 2, 1, 5, 1, 8, 4, 2]:
+  #    a.incl(value)
+  #  for value in a.items:
+  #    echo "Got ", value
+
+  block setContains:
+    var values = initOrderedSet[int]()
+    doAssert(not values.contains(2))
+    values.incl(2)
+    doAssert values.contains(2)
+
+  block toSeqAndString:
+    var a = toOrderedSet([2, 4, 5])
+    var b = initOrderedSet[int]()
+    for x in [2, 4, 5]: b.incl(x)
+    doAssert($a == $b)
+    doAssert(a == b) # https://github.com/Araq/Nim/issues/1413
+
+  block initBlocks:
+    var a: OrderedSet[int]
+    a.init(4)
+    a.incl(2)
+    a.init
+    doAssert a.len == 0
+    a = initOrderedSet[int](4)
+    a.incl(2)
+    doAssert a.len == 1
+
+    var b: HashSet[int]
+    b.init(4)
+    b.incl(2)
+    b.init
+    doAssert b.len == 0
+    b = initHashSet[int](4)
+    b.incl(2)
+    doAssert b.len == 1
+
+  block missingOrExcl:
+    var s = toOrderedSet([2, 3, 6, 7])
+    doAssert s.missingOrExcl(4) == true
+    doAssert s.missingOrExcl(6) == false
+
+  block orderedSetEquality:
+    type pair = tuple[a, b: int]
+
+    var aa = initOrderedSet[pair]()
+    var bb = initOrderedSet[pair]()
+
+    var x = (a: 1, b: 2)
+    var y = (a: 3, b: 4)
+
+    aa.incl(x)
+    aa.incl(y)
+
+    bb.incl(x)
+    bb.incl(y)
+    doAssert aa == bb
+
+  block setsWithoutInit:
+    var
+      a: HashSet[int]
+      b: HashSet[int]
+      c: HashSet[int]
+      d: HashSet[int]
+      e: HashSet[int]
+
+    doAssert a.containsOrIncl(3) == false
+    doAssert a.contains(3)
+    doAssert a.len == 1
+    doAssert a.containsOrIncl(3)
+    a.incl(3)
+    doAssert a.len == 1
+    a.incl(6)
+    doAssert a.len == 2
+
+    b.incl(5)
+    doAssert b.len == 1
+    b.excl(5)
+    b.excl(c)
+    doAssert b.missingOrExcl(5)
+    doAssert b.disjoint(c)
+
+    d = b + c
+    doAssert d.len == 0
+    d = b * c
+    doAssert d.len == 0
+    d = b - c
+    doAssert d.len == 0
+    d = b -+- c
+    doAssert d.len == 0
+
+    doAssert (d < e) == false
+    doAssert d <= e
+    doAssert d == e
+
+  block setsWithoutInit:
+    var
+      a: OrderedSet[int]
+      b: OrderedSet[int]
+      c: OrderedSet[int]
+      d: HashSet[int]
+
+
+    doAssert a.containsOrIncl(3) == false
+    doAssert a.contains(3)
+    doAssert a.len == 1
+    doAssert a.containsOrIncl(3)
+    a.incl(3)
+    doAssert a.len == 1
+    a.incl(6)
+    doAssert a.len == 2
+
+    b.incl(5)
+    doAssert b.len == 1
+    doAssert b.missingOrExcl(5) == false
+    doAssert b.missingOrExcl(5)
+
+    doAssert c.missingOrExcl(9)
+    d.incl(c)
+    doAssert d.len == 0
+
+testModule()
diff --git a/tests/collections/tindexby.nim b/tests/collections/tindexby.nim
deleted file mode 100644
index 88c0b263e..000000000
--- a/tests/collections/tindexby.nim
+++ /dev/null
@@ -1,22 +0,0 @@
-import tables
-
-doAssert indexBy(newSeq[int](), proc(x: int):int = x) == initTable[int, int](), "empty int table"
-
-var tbl1 = initTable[int, int]()
-tbl1.add(1,1)
-tbl1.add(2,2)
-doAssert indexBy(@[1,2], proc(x: int):int = x) == tbl1, "int table"
-
-type
-  TElem = object
-    foo: int
-    bar: string
-
-let
-  elem1 = TElem(foo: 1, bar: "bar")
-  elem2 = TElem(foo: 2, bar: "baz")
-
-var tbl2 = initTable[string, TElem]()
-tbl2.add("bar", elem1)
-tbl2.add("baz", elem2)
-doAssert indexBy(@[elem1,elem2], proc(x: TElem): string = x.bar) == tbl2, "element table"
diff --git a/tests/collections/tmapit.nim b/tests/collections/tmapit.nim
deleted file mode 100644
index b2afa9429..000000000
--- a/tests/collections/tmapit.nim
+++ /dev/null
@@ -1,33 +0,0 @@
-discard """
-  output: '''true
-true'''
-"""
-
-import sequtils
-
-var x = @[1, 2, 3]
-# This mapIt call will run with preallocation because ``len`` is available.
-var y = x.mapIt($(it+10))
-echo y == @["11", "12", "13"]
-
-type structureWithoutLen = object
-  a: array[5, int]
-
-iterator items(s: structureWithoutLen): int {.inline.} =
-  yield s.a[0]
-  yield s.a[1]
-  yield s.a[2]
-  yield s.a[3]
-  yield s.a[4]
-
-var st: structureWithoutLen
-st.a[0] = 0
-st.a[1] = 1
-st.a[2] = 2
-st.a[3] = 3
-st.a[4] = 4
-
-# this will run without preallocating the result
-# since ``len`` is not available
-var r = st.mapIt($(it+10))
-echo r == @["10", "11", "12", "13", "14"]
diff --git a/tests/collections/tseq.nim b/tests/collections/tseq.nim
new file mode 100644
index 000000000..0f8084c78
--- /dev/null
+++ b/tests/collections/tseq.nim
@@ -0,0 +1,242 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  output: '''
+Hithere, what's your name?Hathere, what's your name?
+fA13msg1falsefB14msg2truefC15msg3false
+Zip: [{"Field0": 1, "Field1": 2}, {"Field0": 3, "Field1": 4}, {"Field0": 5, "Field1": 6}]
+Filter Iterator: 3
+Filter Iterator: 5
+Filter Iterator: 7
+Filter: [3, 5, 7]
+FilterIt: [1, 3, 7]
+Concat: [1, 3, 5, 7, 2, 4, 6]
+Deduplicate: [1, 2, 3, 4, 5, 7]
+@[()]
+Minmax: (1, 7)
+2345623456
+'''
+"""
+
+block tseq2:
+  proc `*`(a, b: seq[int]): seq[int] =
+    # allocate a new sequence:
+    newSeq(result, len(a))
+    # multiply two int sequences:
+    for i in 0..len(a)-1: result[i] = a[i] * b[i]
+
+  doAssert(@[1, 2, 3] * @[1, 2, 3] == @[1, 4, 9])
+
+
+
+block tseqcon:
+  const nestedFixed = true
+
+  type
+    TRec {.final.} = object
+      x, y: int
+      s: string
+      seq: seq[string]
+    TRecSeq = seq[TRec]
+
+  proc test() =
+    var s, b: seq[string]
+    s = @[]
+    add(s, "Hi")
+    add(s, "there, ")
+    add(s, "what's your name?")
+
+    b = s # deep copying here!
+    b[0][1] = 'a'
+
+    for i in 0 .. len(s)-1:
+      write(stdout, s[i])
+    for i in 0 .. len(b)-1:
+      write(stdout, b[i])
+
+  when nestedFixed:
+    proc nested() =
+      var
+        s: seq[seq[string]]
+      for i in 0..10_000: # test if the garbage collector
+        # now works with sequences
+        s = @[
+          @["A", "B", "C", "D"],
+          @["E", "F", "G", "H"],
+          @["I", "J", "K", "L"],
+          @["M", "N", "O", "P"]]
+
+  test()
+  when nestedFixed:
+    nested()
+  echo ""
+
+
+
+import os
+block tseqcon2:
+  proc rec_dir(dir: string): seq[string] =
+    result = @[]
+    for kind, path in walk_dir(dir):
+      if kind == pcDir:
+        add(result, rec_dir(path))
+      else:
+        add(result, path)
+
+
+
+block tseqtuple:
+  type
+    TMsg = tuple[
+      file: string,
+      line: int,
+      msg: string,
+      err: bool]
+
+  var s: seq[TMsg] = @[]
+
+  s.add(("fA", 13, "msg1", false))
+  s.add(("fB", 14, "msg2", true))
+  s.add(("fC", 15, "msg3", false))
+
+  for file, line, msg, err in items(s):
+    stdout.write(file)
+    stdout.write($line)
+    stdout.write(msg)
+    stdout.write($err)
+  echo ""
+
+
+import sequtils, marshal
+block tsequtils:
+  proc testFindWhere(item : int) : bool =
+    if item != 1: return true
+
+  var seq1: seq[int] = @[]
+
+  seq1.add(1)
+  seq1.add(3)
+  seq1.add(5)
+  seq1.add(7)
+
+  var seq2: seq[int] = @[2, 4, 6]
+  var final = zip(seq1, seq2)
+
+  echo "Zip: ", $$(final)
+
+  #Test findWhere as a iterator
+
+  for itms in filter(seq1, testFindWhere):
+    echo "Filter Iterator: ", $$(itms)
+
+
+  #Test findWhere as a proc
+
+  var fullseq: seq[int] = filter(seq1, testFindWhere)
+
+  echo "Filter: ", $$(fullseq)
+
+  #Test findIt as a template
+
+  var finditval: seq[int] = filterIt(seq1, it!=5)
+
+  echo "FilterIt: ", $$(finditval)
+
+  var concatseq = concat(seq1,seq2)
+  echo "Concat: ", $$(concatseq)
+
+  var seq3 = @[1,2,3,4,5,5,5,7]
+  var dedupseq = deduplicate(seq3)
+  echo "Deduplicate: ", $$(dedupseq)
+  # bug #4973
+  type
+    SomeObj = object
+    OtherObj = object
+      field: SomeObj
+
+  let aSeq = @[OtherObj(field: SomeObj())]
+  let someObjSeq = aSeq.mapIt(it.field)
+  echo someObjSeq
+
+  block minmax:
+    doAssert minmax(@[0]) == (0, 0)
+    doAssert minmax(@[0, 1]) == (0, 1)
+    doAssert minmax(@[1, 0]) == (0, 1)
+    doAssert minmax(@[8,2,1,7,3,9,4,0,5]) == (0, 9)
+    echo "Minmax: ", $(minmax(concat(seq1, seq2)))
+
+
+when not defined(nimseqsv2):
+  block tshallowseq:
+    proc xxx() =
+      var x: seq[int] = @[1, 2, 3]
+      var y: seq[int]
+      system.shallowCopy(y, x)
+      y[1] = 42
+      doAssert y == @[1, 42, 3]
+      doAssert x == @[1, 42, 3]
+    xxx()
+
+
+  block tshallowemptyseq:
+    proc test() =
+      var nilSeq: seq[int] = @[]
+      var emptySeq: seq[int] = newSeq[int]()
+      block:
+        var t = @[1,2,3]
+        when defined(gcRefc):
+          shallow(nilSeq)
+        t = nilSeq
+        doAssert t == @[]
+      block:
+        var t = @[1,2,3]
+        when defined(gcRefc):
+          shallow(emptySeq)
+        t = emptySeq
+        doAssert t == @[]
+      block:
+        var t = @[1,2,3]
+        shallowCopy(t, nilSeq)
+        doAssert t == @[]
+      block:
+        var t = @[1,2,3]
+        shallowCopy(t, emptySeq)
+        doAssert t == @[]
+    test()
+
+
+import strutils
+block ttoseq:
+  for x in toSeq(countup(2, 6)):
+    stdout.write(x)
+  for x in items(toSeq(countup(2, 6))):
+    stdout.write(x)
+  var y: typeof("a b c".split)
+  y = "xzy"
+  stdout.write("\n")
+
+block tseqmapitchain:
+  doAssert @[101, 102] == [1, 2].mapIt(func (x: int): int = it + x).mapIt(it(100))
+
+
+for i in 0..100:
+  # fix #14655
+  var test = newSeqOfCap[uint32](1)
+  test.setLen(1)
+  doAssert test[0] == 0, $(test[0], i)
+
+
+# bug #22560
+doAssert len(newSeqOfCap[int](42)) == 0
+
+block: # bug #17197
+  type Matrix = seq[seq[int]]
+
+  proc needlemanWunsch(sequence1: string, sequence2: string, gap_penal: int8, match: int8, indel_penal: int8): bool =
+    let seq2_len = sequence2.len
+
+    var grid: Matrix
+    for i in sequence1:
+      grid.add(newSeqOfCap[seq[int]](seq2_len))
+    result = true
+
+  doAssert needlemanWunsch("ABC", "DEFG", 1, 2, 3)
diff --git a/tests/collections/tsets.nim b/tests/collections/tsets.nim
deleted file mode 100644
index 61e14260a..000000000
--- a/tests/collections/tsets.nim
+++ /dev/null
@@ -1,123 +0,0 @@
-import sets
-import hashes
-import algorithm
-
-block setEquality:
-  var
-    a = initSet[int]()
-    b = initSet[int]()
-    c = initSet[string]()
-
-  for i in 0..5: a.incl(i)
-  for i in 1..6: b.incl(i)
-  for i in 0..5: c.incl($i)
-
-  doAssert map(a, proc(x: int): int = x + 1) == b
-  doAssert map(a, proc(x: int): string = $x) == c
-
-
-block setsContainingTuples:
-  var set = initSet[tuple[i: int, i64: int64, f: float]]()
-  set.incl( (i: 123, i64: 123'i64, f: 3.14) )
-  doAssert set.contains( (i: 123, i64: 123'i64, f: 3.14) )
-  doAssert( not set.contains( (i: 456, i64: 789'i64, f: 2.78) ) )
-
-
-block setWithTuplesWithSeqs:
-  var s = initSet[tuple[s: seq[int]]]()
-  s.incl( (s: @[1, 2, 3]) )
-  doAssert s.contains( (s: @[1, 2, 3]) )
-  doAssert( not s.contains((s: @[4, 5, 6])) )
-
-
-block setWithSequences:
-  var s = initSet[seq[int]]()
-  s.incl( @[1, 2, 3] )
-  doAssert s.contains(@[1, 2, 3])
-  doAssert( not s.contains(@[4, 5, 6]) )
-
-block setClearWorked:
-  var s = initSet[char]()
-
-  for c in "this is a test":
-    s.incl(c)
-
-  doAssert len(s) == 7
-  clear(s)
-  doAssert len(s) == 0
-
-  s.incl('z')
-  for c in "this is a test":
-    s.incl(c)
-
-  doAssert len(s) == 8
-
-block orderedSetClearWorked:
-  var s = initOrderedSet[char]()
-
-  for c in "eat at joes":
-    s.incl(c)
-
-  var r = ""
-
-  for c in items(s):
-    add(r, c)
-
-  doAssert r == "eat jos"
-  clear(s)
-
-  s.incl('z')
-  for c in "eat at joes":
-    s.incl(c)
-
-  r = ""
-  for c in items(s):
-    add(r, c)
-
-  doAssert r == "zeat jos"
-
-block hashForHashedSet:
-  let
-    seq1 = "This is the test."
-    seq2 = "the test is This."
-    s1 = seq1.toSet()
-    s2 = seq2.toSet()
-  var hashSeq: seq[Hash] = @[]
-  doAssert s1 == s2
-  doAssert hash(s1) == hash(s2)
-
-block hashForOrderdSet:
-  let
-    str = "This is the test."
-    rstr = str.reversed
-
-  var
-    s1 = initOrderedSet[char]()
-    s2 = initOrderedSet[char]()
-    r = initOrderedSet[char]()
-    expected: Hash
-    added: seq[char] = @[]
-    reversed: Hash
-    radded: seq[char] = @[]
-
-  expected = 0
-  for c in str:
-    if (not (c in added)):
-      expected = expected !& hash(c)
-      added.add(c)
-    s1.incl(c)
-    s2.incl(c)
-  expected = !$expected
-  doAssert hash(s1) == expected
-  doAssert hash(s1) == hash(s2)
-  doAssert hash(s1) != hash(r)
-
-  reversed = 0
-  for c in rstr:
-    if (not (c in radded)):
-      reversed = reversed !& hash(c)
-      radded.add(c)
-    r.incl(c)
-  reversed = !$reversed
-  doAssert hash(r) == reversed
-  doAssert hash(s1) != reversed
diff --git a/tests/collections/ttableconstr.nim b/tests/collections/ttableconstr.nim
deleted file mode 100644
index 884378a76..000000000
--- a/tests/collections/ttableconstr.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-# Test if the new table constructor syntax works:
-
-template ignoreExpr(e) =
-  discard
-
-# test first class '..' syntactical citizen:
-ignoreExpr x <> 2..4
-# test table constructor:
-ignoreExpr({:})
-ignoreExpr({2: 3, "key": "value"})
-
-# NEW:
-assert 56 in 50..100
-
-assert 56 in ..60
-
diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim
index 7fe4c79b1..95f9418a0 100644
--- a/tests/collections/ttables.nim
+++ b/tests/collections/ttables.nim
@@ -1,275 +1,457 @@
 discard """
-  cmd: "nim c --threads:on $file"
-  output: '''true'''
+output: '''
+done tableadds
+And we get here
+1
+2
+3
+'''
+joinable: false
+targets: "c cpp js"
 """
 
-import hashes, tables, sharedtables
-
-const
-  data = {
-    "34": 123456, "12": 789,
-    "90": 343, "0": 34404,
-    "1": 344004, "2": 344774,
-    "3": 342244, "4": 3412344,
-    "5": 341232144, "6": 34214544,
-    "7": 3434544, "8": 344544,
-    "9": 34435644, "---00": 346677844,
-    "10": 34484, "11": 34474, "19": 34464,
-    "20": 34454, "30": 34141244, "40": 344114,
-    "50": 344490, "60": 344491, "70": 344492,
-    "80": 344497}
-
-  sorteddata = {
-    "---00": 346677844,
-    "0": 34404,
-    "1": 344004,
-    "10": 34484,
-    "11": 34474,
-    "12": 789,
-    "19": 34464,
-    "2": 344774, "20": 34454,
-    "3": 342244, "30": 34141244,
-    "34": 123456,
-    "4": 3412344, "40": 344114,
-    "5": 341232144, "50": 344490,
-    "6": 34214544, "60": 344491,
-    "7": 3434544, "70": 344492,
-    "8": 344544, "80": 344497,
-    "9": 34435644,
-    "90": 343}
-
-block tableTest1:
-  var t = initTable[tuple[x, y: int], string]()
-  t[(0,0)] = "00"
-  t[(1,0)] = "10"
-  t[(0,1)] = "01"
-  t[(1,1)] = "11"
-  for x in 0..1:
-    for y in 0..1:
-      assert t[(x,y)] == $x & $y
-  assert($t ==
-    "{(x: 0, y: 1): \"01\", (x: 0, y: 0): \"00\", (x: 1, y: 0): \"10\", (x: 1, y: 1): \"11\"}")
-
-block tableTest2:
-  var t = initTable[string, float]()
-  t["test"] = 1.2345
-  t["111"] = 1.000043
-  t["123"] = 1.23
-  t.del("111")
-
-  t["012"] = 67.9
-  t["123"] = 1.5 # test overwriting
-
-  assert t["123"] == 1.5
-  try:
-    echo t["111"] # deleted
-  except KeyError:
+# xxx wrap in a template to test in VM, see https://github.com/timotheecour/Nim/issues/534#issuecomment-769565033
+
+import hashes, sequtils, tables, algorithm
+
+proc sortedPairs[T](t: T): auto = toSeq(t.pairs).sorted
+template sortedItems(t: untyped): untyped = sorted(toSeq(t))
+
+block tableDollar:
+  # other tests should use `sortedPairs` to be robust to future table/hash
+  # implementation changes
+  doAssert ${1: 'a', 2: 'b'}.toTable in ["{1: 'a', 2: 'b'}", "{2: 'b', 1: 'a'}"]
+
+# test should not be joined because it takes too long.
+block tableadds:
+  proc main =
+    var tab = newTable[string, string]()
+    for i in 0..1000:
+      tab["key"] = "value " & $i
+
+  main()
+  echo "done tableadds"
+
+
+block tcounttable:
+  # bug #2625
+  const s_len = 32
+  var substr_counts: CountTable[string] = initCountTable[string]()
+  var my_string = "Hello, this is sadly broken for strings over 64 characters. Note that it *does* appear to work for short strings."
+  for i in 0..(my_string.len - s_len):
+    let s = my_string[i..i+s_len-1]
+    substr_counts[s] = 1
+    # substr_counts[s] = substr_counts[s] + 1  # Also breaks, + 2 as well, etc.
+    # substr_counts.inc(s)  # This works
+    #echo "Iteration ", i
+
+  echo "And we get here"
+
+
+block thashes:
+  # Test with int
+  block:
+    var t = initTable[int,int]()
+    t[0] = 42
+    t[1] = t[0] + 1
+    doAssert(t[0] == 42)
+    doAssert(t[1] == 43)
+    let t2 = {1: 1, 2: 2}.toTable
+    doAssert(t2[2] == 2)
+
+  # Test with char
+  block:
+    var t = initTable[char,int]()
+    t['0'] = 42
+    t['1'] = t['0'] + 1
+    doAssert(t['0'] == 42)
+    doAssert(t['1'] == 43)
+    let t2 = {'1': 1, '2': 2}.toTable
+    doAssert(t2['2'] == 2)
+
+  # Test with enum
+  block:
+    type
+      E = enum eA, eB, eC
+    var t = initTable[E,int]()
+    t[eA] = 42
+    t[eB] = t[eA] + 1
+    doAssert(t[eA] == 42)
+    doAssert(t[eB] == 43)
+    let t2 = {eA: 1, eB: 2}.toTable
+    doAssert(t2[eB] == 2)
+
+  # Test with range
+  block:
+    type
+      R = range[0..9]
+    var t = initTable[R,int]() # causes warning, why?
+    t[1] = 42 # causes warning, why?
+    t[2] = t[1] + 1
+    doAssert(t[1] == 42)
+    doAssert(t[2] == 43)
+    let t2 = {1.R: 1, 2.R: 2}.toTable
+    doAssert(t2[2.R] == 2)
+
+  # Test which combines the generics for tuples + ordinals
+  block:
+    type
+      E = enum eA, eB, eC
+    var t = initTable[(string, E, int, char), int]()
+    t[("a", eA, 0, '0')] = 42
+    t[("b", eB, 1, '1')] = t[("a", eA, 0, '0')] + 1
+    doAssert(t[("a", eA, 0, '0')] == 42)
+    doAssert(t[("b", eB, 1, '1')] == 43)
+    let t2 = {("a", eA, 0, '0'): 1, ("b", eB, 1, '1'): 2}.toTable
+    doAssert(t2[("b", eB, 1, '1')] == 2)
+
+  # Test to check if overloading is possible
+  # Unfortunately, this does not seem to work for int
+  # The same test with a custom hash(s: string) does
+  # work though.
+  block:
+    proc hash(x: int): Hash {.inline.} =
+      echo "overloaded hash"
+      result = x
+    var t = initTable[int, int]()
+    t[0] = 0
+
+  # Check hashability of all integer types (issue #5429)
+  block:
+    let intTables = (
+      newTable[int, string](),
+      newTable[int8, string](),
+      newTable[int16, string](),
+      newTable[int32, string](),
+      newTable[int64, string](),
+      newTable[uint, string](),
+      newTable[uint8, string](),
+      newTable[uint16, string](),
+      newTable[uint32, string](),
+      newTable[uint64, string](),
+    )
+  echo "1"
+
+
+block tindexby:
+  doAssert indexBy(newSeq[int](), proc(x: int):int = x) == initTable[int, int](), "empty int table"
+
+  var tbl1 = initTable[int, int]()
+  tbl1[1] = 1
+  tbl1[2] = 2
+  doAssert indexBy(@[1,2], proc(x: int):int = x) == tbl1, "int table"
+
+  type
+    TElem = object
+      foo: int
+      bar: string
+
+  let
+    elem1 = TElem(foo: 1, bar: "bar")
+    elem2 = TElem(foo: 2, bar: "baz")
+
+  var tbl2 = initTable[string, TElem]()
+  tbl2["bar"] = elem1
+  tbl2["baz"] = elem2
+  doAssert indexBy(@[elem1,elem2], proc(x: TElem): string = x.bar) == tbl2, "element table"
+
+
+block tableconstr:
+  # Test if the new table constructor syntax works:
+
+  template ignoreExpr(e) =
     discard
-  assert(not hasKey(t, "111"))
-
-  assert "123" in t
-  assert("111" notin t)
-
-  for key, val in items(data): t[key] = val.toFloat
-  for key, val in items(data): assert t[key] == val.toFloat
-
-  assert(not t.hasKeyOrPut("456", 4.0))     # test absent key
-  assert t.hasKeyOrPut("012", 3.0)          # test present key
-  var x = t.mgetOrPut("111", 1.5)           # test absent key
-  x = x * 2
-  assert x == 3.0
-  x = t.mgetOrPut("test", 1.5)              # test present key
-  x = x * 2
-  assert x == 2 * 1.2345
-
-block orderedTableTest1:
-  var t = initOrderedTable[string, int](2)
-  for key, val in items(data): t[key] = val
-  for key, val in items(data): assert t[key] == val
-  var i = 0
-  # `pairs` needs to yield in insertion order:
-  for key, val in pairs(t):
-    assert key == data[i][0]
-    assert val == data[i][1]
-    inc(i)
-
-  for key, val in mpairs(t): val = 99
-  for val in mvalues(t): assert val == 99
-
-block orderedTableTest2:
-  var
-    s = initOrderedTable[string, int]()
-    t = initOrderedTable[string, int]()
-  assert s == t
-  for key, val in items(data): t[key] = val
-  assert s != t
-  for key, val in items(sorteddata): s[key] = val
-  assert s != t
-  t.clear()
-  assert s != t
-  for key, val in items(sorteddata): t[key] = val
-  assert s == t
-
-block countTableTest1:
-  var s = data.toTable
-  var t = initCountTable[string]()
-
-  for k in s.keys: t.inc(k)
-  for k in t.keys: assert t[k] == 1
-  t.inc("90", 3)
-  t.inc("12", 2)
-  t.inc("34", 1)
-  assert t.largest()[0] == "90"
-
-  t.sort()
-  var i = 0
-  for k, v in t.pairs:
-    case i
-    of 0: assert k == "90" and v == 4
-    of 1: assert k == "12" and v == 3
-    of 2: assert k == "34" and v == 2
-    else: break
-    inc i
-
-block countTableTest2:
-  var
-    s = initCountTable[int]()
-    t = initCountTable[int]()
-  assert s == t
-  s.inc(1)
-  assert s != t
-  t.inc(2)
-  assert s != t
-  t.inc(1)
-  assert s != t
-  s.inc(2)
-  assert s == t
-  s.inc(1)
-  assert s != t
-  t.inc(1)
-  assert s == t
-
-block mpairsTableTest1:
-  var t = initTable[string, int]()
-  t["a"] = 1
-  t["b"] = 2
-  t["c"] = 3
-  t["d"] = 4
-  for k, v in t.mpairs:
-    if k == "a" or k == "c":
-      v = 9
-
-  for k, v in t.pairs:
-    if k == "a" or k == "c":
-      assert v == 9
-    else:
-      assert v != 1 and v != 3
-
-block SyntaxTest:
-  var x = toTable[int, string]({:})
-
-block zeroHashKeysTest:
-  proc doZeroHashValueTest[T, K, V](t: T, nullHashKey: K, value: V) =
-    let initialLen = t.len
-    var testTable = t
-    testTable[nullHashKey] = value
-    assert testTable[nullHashKey] == value
-    assert testTable.len == initialLen + 1
-    testTable.del(nullHashKey)
-    assert testTable.len == initialLen
-
-  # with empty table
-  doZeroHashValueTest(toTable[int,int]({:}), 0, 42)
-  doZeroHashValueTest(toTable[string,int]({:}), "", 23)
-  doZeroHashValueTest(toOrderedTable[int,int]({:}), 0, 42)
-  doZeroHashValueTest(toOrderedTable[string,int]({:}), "", 23)
-
-  # with non-empty table
-  doZeroHashValueTest(toTable[int,int]({1:2}), 0, 42)
-  doZeroHashValueTest(toTable[string,string]({"foo": "bar"}), "", "zero")
-  doZeroHashValueTest(toOrderedTable[int,int]({3:4}), 0, 42)
-  doZeroHashValueTest(toOrderedTable[string,string]({"egg": "sausage"}),
-      "", "spam")
-
-block clearTableTest:
-  var t = data.toTable
-  assert t.len() != 0
-  t.clear()
-  assert t.len() == 0
-
-block clearOrderedTableTest:
-  var t = data.toOrderedTable
-  assert t.len() != 0
-  t.clear()
-  assert t.len() == 0
-
-block clearCountTableTest:
-  var t = initCountTable[string]()
-  t.inc("90", 3)
-  t.inc("12", 2)
-  t.inc("34", 1)
-  assert t.len() != 0
-  t.clear()
-  assert t.len() == 0
-
-block withKeyTest:
-  var t: SharedTable[int, int]
-  t.init()
-  t.withKey(1) do (k: int, v: var int, pairExists: var bool):
-    assert(v == 0)
-    pairExists = true
-    v = 42
-  assert(t.mget(1) == 42)
-  t.withKey(1) do (k: int, v: var int, pairExists: var bool):
-    assert(v == 42)
-    pairExists = false
-  try:
-    discard t.mget(1)
-    assert(false, "KeyError expected")
-  except KeyError:
-    discard
-  t.withKey(2) do (k: int, v: var int, pairExists: var bool):
-    pairExists = false
-  try:
-    discard t.mget(2)
-    assert(false, "KeyError expected")
-  except KeyError:
-    discard
-
-block takeTest:
-  var t = initTable[string, int]()
-  t["key"] = 123
-
-  var val = 0
-  assert(t.take("key", val))
-  assert(val == 123)
-
-  val = -1
-  assert(not t.take("key", val))
-  assert(val == -1)
-
-  assert(not t.take("otherkey", val))
-  assert(val == -1)
-
-proc orderedTableSortTest() =
-  var t = initOrderedTable[string, int](2)
-  for key, val in items(data): t[key] = val
-  for key, val in items(data): assert t[key] == val
-  t.sort(proc (x, y: tuple[key: string, val: int]): int = cmp(x.key, y.key))
-  var i = 0
-  # `pairs` needs to yield in sorted order:
-  for key, val in pairs(t):
-    doAssert key == sorteddata[i][0]
-    doAssert val == sorteddata[i][1]
-    inc(i)
-
-  # check that lookup still works:
-  for key, val in pairs(t):
-    doAssert val == t[key]
-  # check that insert still works:
-  t["newKeyHere"] = 80
-
-
-orderedTableSortTest()
-echo "true"
 
+  # test first class '..' syntactical citizen:
+  ignoreExpr x <> 2..4
+  # test table constructor:
+  ignoreExpr({:})
+  ignoreExpr({2: 3, "key": "value"})
+
+  # NEW:
+  doAssert 56 in 50..100
+
+  doAssert 56 in 0..60
+
+
+block ttables2:
+  proc TestHashIntInt() =
+    var tab = initTable[int,int]()
+    let n = 10
+    for i in 1..n:
+      tab[i] = i
+    for i in 1..n:
+      var x = tab[i]
+      if x != i : echo "not found ", i
+
+  TestHashIntInt()
+
+  # bug #2107
+
+  var delTab = initTable[int,int](4)
+
+  for i in 1..4:
+    delTab[i] = i
+    delTab.del(i)
+  delTab[5] = 5
+
+
+  echo "2"
+
+block tablesref:
+  const
+    data = {
+      "34": 123456, "12": 789,
+      "90": 343, "0": 34404,
+      "1": 344004, "2": 344774,
+      "3": 342244, "4": 3412344,
+      "5": 341232144, "6": 34214544,
+      "7": 3434544, "8": 344544,
+      "9": 34435644, "---00": 346677844,
+      "10": 34484, "11": 34474, "19": 34464,
+      "20": 34454, "30": 34141244, "40": 344114,
+      "50": 344490, "60": 344491, "70": 344492,
+      "80": 344497}
+
+    sorteddata = {
+      "---00": 346677844,
+      "0": 34404,
+      "1": 344004,
+      "10": 34484,
+      "11": 34474,
+      "12": 789,
+      "19": 34464,
+      "2": 344774, "20": 34454,
+      "3": 342244, "30": 34141244,
+      "34": 123456,
+      "4": 3412344, "40": 344114,
+      "5": 341232144, "50": 344490,
+      "6": 34214544, "60": 344491,
+      "7": 3434544, "70": 344492,
+      "8": 344544, "80": 344497,
+      "9": 34435644,
+      "90": 343}
+
+  block tableTest1:
+    var t = newTable[tuple[x, y: int], string]()
+    t[(0,0)] = "00"
+    t[(1,0)] = "10"
+    t[(0,1)] = "01"
+    t[(1,1)] = "11"
+    for x in 0..1:
+      for y in 0..1:
+        doAssert t[(x,y)] == $x & $y
+    doAssert t.sortedPairs ==
+      @[((x: 0, y: 0), "00"), ((x: 0, y: 1), "01"), ((x: 1, y: 0), "10"), ((x: 1, y: 1), "11")]
+
+  block tableTest2:
+    var t = newTable[string, float]()
+    t["test"] = 1.2345
+    t["111"] = 1.000043
+    t["123"] = 1.23
+    t.del("111")
+
+    t["012"] = 67.9
+    t["123"] = 1.5 # test overwriting
+
+    doAssert t["123"] == 1.5
+    try:
+      echo t["111"] # deleted
+    except KeyError:
+      discard
+    doAssert(not hasKey(t, "111"))
+    doAssert "111" notin t
+
+    for key, val in items(data): t[key] = val.toFloat
+    for key, val in items(data): doAssert t[key] == val.toFloat
+
+
+  block orderedTableTest1:
+    var t = newOrderedTable[string, int](2)
+    for key, val in items(data): t[key] = val
+    for key, val in items(data): doAssert t[key] == val
+    var i = 0
+    # `pairs` needs to yield in insertion order:
+    for key, val in pairs(t):
+      doAssert key == data[i][0]
+      doAssert val == data[i][1]
+      inc(i)
+
+    for key, val in mpairs(t): val = 99
+    for val in mvalues(t): doAssert val == 99
+
+  block countTableTest1:
+    var s = data.toTable
+    var t = newCountTable[string]()
+    var r = newCountTable[string]()
+    for x in [t, r]:
+      for k in s.keys:
+        x.inc(k)
+        doAssert x[k] == 1
+      x.inc("90", 3)
+      x.inc("12", 2)
+      x.inc("34", 1)
+    doAssert t.largest()[0] == "90"
+
+    t.sort()
+    r.sort(SortOrder.Ascending)
+    var ps1 = toSeq t.pairs
+    var ps2 = toSeq r.pairs
+    ps2.reverse()
+    for ps in [ps1, ps2]:
+      var i = 0
+      for (k, v) in ps:
+        case i
+        of 0: doAssert k == "90" and v == 4
+        of 1: doAssert k == "12" and v == 3
+        of 2: doAssert k == "34" and v == 2
+        else: break
+        inc i
+
+  block smallestLargestNamedFieldsTest: # bug #14918
+    const a = [7, 8, 8]
+
+    proc testNamedFields(t: CountTable | CountTableRef) =
+      doAssert t.smallest.key == 7
+      doAssert t.smallest.val == 1
+      doAssert t.largest.key == 8
+      doAssert t.largest.val == 2
+
+    let t1 = toCountTable(a)
+    testNamedFields(t1)
+    let t2 = newCountTable(a)
+    testNamedFields(t2)
+
+  block SyntaxTest:
+    var x = newTable[int, string]({:})
+    discard x
+
+  block nilTest:
+    var i, j: TableRef[int, int] = nil
+    doAssert i == j
+    j = newTable[int, int]()
+    doAssert i != j
+    doAssert j != i
+    i = newTable[int, int]()
+    doAssert i == j
+
+  proc orderedTableSortTest() =
+    var t = newOrderedTable[string, int](2)
+    for key, val in items(data): t[key] = val
+    for key, val in items(data): doAssert t[key] == val
+    proc cmper(x, y: tuple[key: string, val: int]): int = cmp(x.key, y.key)
+    t.sort(cmper)
+    var i = 0
+    # `pairs` needs to yield in sorted order:
+    for key, val in pairs(t):
+      doAssert key == sorteddata[i][0]
+      doAssert val == sorteddata[i][1]
+      inc(i)
+    t.sort(cmper, order=SortOrder.Descending)
+    i = 0
+    for key, val in pairs(t):
+      doAssert key == sorteddata[high(data)-i][0]
+      doAssert val == sorteddata[high(data)-i][1]
+      inc(i)
+
+    # check that lookup still works:
+    for key, val in pairs(t):
+      doAssert val == t[key]
+    # check that insert still works:
+    t["newKeyHere"] = 80
+
+  block anonZipTest:
+    let keys = @['a','b','c']
+    let values = @[1, 2, 3]
+    doAssert zip(keys, values).toTable.sortedPairs == @[('a', 1), ('b', 2), ('c', 3)]
+
+  block clearTableTest:
+    var t = newTable[string, float]()
+    t["test"] = 1.2345
+    t["111"] = 1.000043
+    t["123"] = 1.23
+    doAssert t.len() != 0
+    t.clear()
+    doAssert t.len() == 0
+
+  block clearOrderedTableTest:
+    var t = newOrderedTable[string, int](2)
+    for key, val in items(data): t[key] = val
+    doAssert t.len() != 0
+    t.clear()
+    doAssert t.len() == 0
+
+  block clearCountTableTest:
+    var t = newCountTable[string]()
+    t.inc("90", 3)
+    t.inc("12", 2)
+    t.inc("34", 1)
+    doAssert t.len() != 0
+    t.clear()
+    doAssert t.len() == 0
+
+  orderedTableSortTest()
+  echo "3"
+
+
+block: # https://github.com/nim-lang/Nim/issues/13496
+  template testDel(body) =
+    block:
+      body
+      when t is CountTable|CountTableRef:
+        t.inc(15, 1)
+        t.inc(19, 2)
+        t.inc(17, 3)
+        t.inc(150, 4)
+        t.del(150)
+      else:
+        t[15] = 1
+        t[19] = 2
+        t[17] = 3
+        t[150] = 4
+        t.del(150)
+      doAssert t.len == 3
+      doAssert sortedItems(t.values) == @[1, 2, 3]
+      doAssert sortedItems(t.keys) == @[15, 17, 19]
+      doAssert sortedPairs(t) == @[(15, 1), (17, 3), (19, 2)]
+      var s = newSeq[int]()
+      for v in t.values: s.add(v)
+      doAssert s.len == 3
+      doAssert sortedItems(s) == @[1, 2, 3]
+      when t is OrderedTable|OrderedTableRef:
+        doAssert toSeq(t.keys) == @[15, 19, 17]
+        doAssert toSeq(t.values) == @[1,2,3]
+        doAssert toSeq(t.pairs) == @[(15, 1), (19, 2), (17, 3)]
+
+  testDel(): (var t: Table[int, int])
+  testDel(): (let t = newTable[int, int]())
+  testDel(): (var t: OrderedTable[int, int])
+  testDel(): (let t = newOrderedTable[int, int]())
+  testDel(): (var t: CountTable[int])
+  testDel(): (let t = newCountTable[int]())
+
+
+block testNonPowerOf2:
+  var a = initTable[int, int](7)
+  a[1] = 10
+  doAssert a[1] == 10
+
+  var b = initTable[int, int](9)
+  b[1] = 10
+  doAssert b[1] == 10
+
+block emptyOrdered:
+  var t1: OrderedTable[int, string]
+  var t2: OrderedTable[int, string]
+  doAssert t1 == t2
+
+block: # Table[ref, int]
+  type A = ref object
+    x: int
+  var t: OrderedTable[A, int]
+  let a1 = A(x: 3)
+  let a2 = A(x: 3)
+  t[a1] = 10
+  t[a2] = 11
+  doAssert t[a1] == 10
+  doAssert t[a2] == 11
diff --git a/tests/collections/ttables2.nim b/tests/collections/ttables2.nim
deleted file mode 100644
index 6f3fa841a..000000000
--- a/tests/collections/ttables2.nim
+++ /dev/null
@@ -1,30 +0,0 @@
-discard """
-  output: '''true'''
-"""
-
-import tables
-
-proc TestHashIntInt() =
-  var tab = initTable[int,int]()
-  for i in 1..1_000_000:
-    tab[i] = i
-  for i in 1..1_000_000:
-    var x = tab[i]
-    if x != i : echo "not found ", i
-
-proc run1() =         # occupied Memory stays constant, but
-  for i in 1 .. 50:   # aborts at run: 44 on win32 with 3.2GB with out of memory
-    TestHashIntInt()
-
-# bug #2107
-
-var delTab = initTable[int,int](4)
-
-for i in 1..4:
-  delTab[i] = i
-  delTab.del(i)
-delTab[5] = 5
-
-
-run1()
-echo "true"
diff --git a/tests/collections/ttablesref.nim b/tests/collections/ttablesref.nim
deleted file mode 100644
index a4030e0dc..000000000
--- a/tests/collections/ttablesref.nim
+++ /dev/null
@@ -1,171 +0,0 @@
-discard """
-  output: '''true'''
-"""
-
-import hashes, tables, sequtils
-
-const
-  data = {
-    "34": 123456, "12": 789,
-    "90": 343, "0": 34404,
-    "1": 344004, "2": 344774,
-    "3": 342244, "4": 3412344,
-    "5": 341232144, "6": 34214544,
-    "7": 3434544, "8": 344544,
-    "9": 34435644, "---00": 346677844,
-    "10": 34484, "11": 34474, "19": 34464,
-    "20": 34454, "30": 34141244, "40": 344114,
-    "50": 344490, "60": 344491, "70": 344492,
-    "80": 344497}
-
-  sorteddata = {
-    "---00": 346677844,
-    "0": 34404,
-    "1": 344004,
-    "10": 34484,
-    "11": 34474,
-    "12": 789,
-    "19": 34464,
-    "2": 344774, "20": 34454,
-    "3": 342244, "30": 34141244,
-    "34": 123456,
-    "4": 3412344, "40": 344114,
-    "5": 341232144, "50": 344490,
-    "6": 34214544, "60": 344491,
-    "7": 3434544, "70": 344492,
-    "8": 344544, "80": 344497,
-    "9": 34435644,
-    "90": 343}
-
-block tableTest1:
-  var t = newTable[tuple[x, y: int], string]()
-  t[(0,0)] = "00"
-  t[(1,0)] = "10"
-  t[(0,1)] = "01"
-  t[(1,1)] = "11"
-  for x in 0..1:
-    for y in 0..1:
-      assert t[(x,y)] == $x & $y
-  assert($t ==
-    "{(x: 0, y: 1): \"01\", (x: 0, y: 0): \"00\", (x: 1, y: 0): \"10\", (x: 1, y: 1): \"11\"}")
-
-block tableTest2:
-  var t = newTable[string, float]()
-  t["test"] = 1.2345
-  t["111"] = 1.000043
-  t["123"] = 1.23
-  t.del("111")
-
-  t["012"] = 67.9
-  t["123"] = 1.5 # test overwriting
-
-  assert t["123"] == 1.5
-  try:
-    echo t["111"] # deleted
-  except KeyError:
-    discard
-  assert(not hasKey(t, "111"))
-  assert "111" notin t
-
-  for key, val in items(data): t[key] = val.toFloat
-  for key, val in items(data): assert t[key] == val.toFloat
-
-
-block orderedTableTest1:
-  var t = newOrderedTable[string, int](2)
-  for key, val in items(data): t[key] = val
-  for key, val in items(data): assert t[key] == val
-  var i = 0
-  # `pairs` needs to yield in insertion order:
-  for key, val in pairs(t):
-    assert key == data[i][0]
-    assert val == data[i][1]
-    inc(i)
-
-  for key, val in mpairs(t): val = 99
-  for val in mvalues(t): assert val == 99
-
-block countTableTest1:
-  var s = data.toTable
-  var t = newCountTable[string]()
-  for k in s.keys: t.inc(k)
-  for k in t.keys: assert t[k] == 1
-  t.inc("90", 3)
-  t.inc("12", 2)
-  t.inc("34", 1)
-  assert t.largest()[0] == "90"
-
-  t.sort()
-  var i = 0
-  for k, v in t.pairs:
-    case i
-    of 0: assert k == "90" and v == 4
-    of 1: assert k == "12" and v == 3
-    of 2: assert k == "34" and v == 2
-    else: break
-    inc i
-
-block SyntaxTest:
-  var x = newTable[int, string]({:})
-  discard x
-
-block nilTest:
-  var i, j: TableRef[int, int] = nil
-  assert i == j
-  j = newTable[int, int]()
-  assert i != j
-  assert j != i
-  i = newTable[int, int]()
-  assert i == j
-
-proc orderedTableSortTest() =
-  var t = newOrderedTable[string, int](2)
-  for key, val in items(data): t[key] = val
-  for key, val in items(data): assert t[key] == val
-  t.sort(proc (x, y: tuple[key: string, val: int]): int = cmp(x.key, y.key))
-  var i = 0
-  # `pairs` needs to yield in sorted order:
-  for key, val in pairs(t):
-    doAssert key == sorteddata[i][0]
-    doAssert val == sorteddata[i][1]
-    inc(i)
-
-  # check that lookup still works:
-  for key, val in pairs(t):
-    doAssert val == t[key]
-  # check that insert still works:
-  t["newKeyHere"] = 80
-
-block anonZipTest:
-  let keys = @['a','b','c']
-  let values = @[1, 2, 3]
-  doAssert "{'a': 1, 'b': 2, 'c': 3}" == $ toTable zip(keys, values)
-
-block clearTableTest:
-  var t = newTable[string, float]()
-  t["test"] = 1.2345
-  t["111"] = 1.000043
-  t["123"] = 1.23
-  assert t.len() != 0
-  t.clear()
-  assert t.len() == 0
-
-block clearOrderedTableTest:
-  var t = newOrderedTable[string, int](2)
-  for key, val in items(data): t[key] = val
-  assert t.len() != 0
-  t.clear()
-  assert t.len() == 0
-
-block clearCountTableTest:
-  var t = newCountTable[string]()
-  t.inc("90", 3)
-  t.inc("12", 2)
-  t.inc("34", 1)
-  assert t.len() != 0
-  t.clear()
-  assert t.len() == 0
-
-orderedTableSortTest()
-echo "true"
-
diff --git a/tests/collections/ttablesref2.nim b/tests/collections/ttablesref2.nim
deleted file mode 100644
index 939de2b84..000000000
--- a/tests/collections/ttablesref2.nim
+++ /dev/null
@@ -1,20 +0,0 @@
-discard """
-  output: '''true'''
-"""
-
-import tables
-
-proc TestHashIntInt() =
-  var tab = newTable[int,int]()
-  for i in 1..1_000_000:
-    tab[i] = i
-  for i in 1..1_000_000:
-    var x = tab[i]
-    if x != i : echo "not found ", i
-
-proc run1() =         # occupied Memory stays constant, but
-  for i in 1 .. 50:   # aborts at run: 44 on win32 with 3.2GB with out of memory
-    TestHashIntInt()
-
-run1()
-echo "true"
diff --git a/tests/collections/ttablesthreads.nim b/tests/collections/ttablesthreads.nim
new file mode 100644
index 000000000..2a4e1bf42
--- /dev/null
+++ b/tests/collections/ttablesthreads.nim
@@ -0,0 +1,276 @@
+discard """
+  cmd: "nim c --threads:on $file"
+  output: '''true'''
+"""
+
+import hashes, tables, sharedtables, algorithm, sequtils
+
+proc sortedPairs[T](t: T): auto = toSeq(t.pairs).sorted
+
+const
+  data = {
+    "34": 123456, "12": 789,
+    "90": 343, "0": 34404,
+    "1": 344004, "2": 344774,
+    "3": 342244, "4": 3412344,
+    "5": 341232144, "6": 34214544,
+    "7": 3434544, "8": 344544,
+    "9": 34435644, "---00": 346677844,
+    "10": 34484, "11": 34474, "19": 34464,
+    "20": 34454, "30": 34141244, "40": 344114,
+    "50": 344490, "60": 344491, "70": 344492,
+    "80": 344497}
+
+  sorteddata = {
+    "---00": 346677844,
+    "0": 34404,
+    "1": 344004,
+    "10": 34484,
+    "11": 34474,
+    "12": 789,
+    "19": 34464,
+    "2": 344774, "20": 34454,
+    "3": 342244, "30": 34141244,
+    "34": 123456,
+    "4": 3412344, "40": 344114,
+    "5": 341232144, "50": 344490,
+    "6": 34214544, "60": 344491,
+    "7": 3434544, "70": 344492,
+    "8": 344544, "80": 344497,
+    "9": 34435644,
+    "90": 343}
+
+block tableTest1:
+  var t = initTable[tuple[x, y: int], string]()
+  t[(0,0)] = "00"
+  t[(1,0)] = "10"
+  t[(0,1)] = "01"
+  t[(1,1)] = "11"
+  for x in 0..1:
+    for y in 0..1:
+      doAssert t[(x,y)] == $x & $y
+  doAssert t.sortedPairs == @[((x: 0, y: 0), "00"), ((x: 0, y: 1), "01"), ((x: 1, y: 0), "10"), ((x: 1, y: 1), "11")]
+
+block tableTest2:
+  var t = initTable[string, float]()
+  t["test"] = 1.2345
+  t["111"] = 1.000043
+  t["123"] = 1.23
+  t.del("111")
+
+  t["012"] = 67.9
+  t["123"] = 1.5 # test overwriting
+
+  doAssert t["123"] == 1.5
+  try:
+    echo t["111"] # deleted
+  except KeyError:
+    discard
+  doAssert(not hasKey(t, "111"))
+
+  doAssert "123" in t
+  doAssert("111" notin t)
+
+  for key, val in items(data): t[key] = val.toFloat
+  for key, val in items(data): doAssert t[key] == val.toFloat
+
+  doAssert(not t.hasKeyOrPut("456", 4.0))     # test absent key
+  doAssert t.hasKeyOrPut("012", 3.0)          # test present key
+  var x = t.mgetOrPut("111", 1.5)           # test absent key
+  x = x * 2
+  doAssert x == 3.0
+  x = t.mgetOrPut("test", 1.5)              # test present key
+  x = x * 2
+  doAssert x == 2 * 1.2345
+
+block orderedTableTest1:
+  var t = initOrderedTable[string, int](2)
+  for key, val in items(data): t[key] = val
+  for key, val in items(data): doAssert t[key] == val
+  var i = 0
+  # `pairs` needs to yield in insertion order:
+  for key, val in pairs(t):
+    doAssert key == data[i][0]
+    doAssert val == data[i][1]
+    inc(i)
+
+  for key, val in mpairs(t): val = 99
+  for val in mvalues(t): doAssert val == 99
+
+block orderedTableTest2:
+  var
+    s = initOrderedTable[string, int]()
+    t = initOrderedTable[string, int]()
+  doAssert s == t
+  for key, val in items(data): t[key] = val
+  doAssert s != t
+  for key, val in items(sorteddata): s[key] = val
+  doAssert s != t
+  t.clear()
+  doAssert s != t
+  for key, val in items(sorteddata): t[key] = val
+  doAssert s == t
+
+block countTableTest1:
+  var s = data.toTable
+  var t = initCountTable[string]()
+
+  for k in s.keys: t.inc(k)
+  for k in t.keys: doAssert t[k] == 1
+  t.inc("90", 3)
+  t.inc("12", 2)
+  t.inc("34", 1)
+  doAssert t.largest()[0] == "90"
+
+  t.sort()
+  var i = 0
+  for k, v in t.pairs:
+    case i
+    of 0: doAssert k == "90" and v == 4
+    of 1: doAssert k == "12" and v == 3
+    of 2: doAssert k == "34" and v == 2
+    else: break
+    inc i
+
+block countTableTest2:
+  var
+    s = initCountTable[int]()
+    t = initCountTable[int]()
+  doAssert s == t
+  s.inc(1)
+  doAssert s != t
+  t.inc(2)
+  doAssert s != t
+  t.inc(1)
+  doAssert s != t
+  s.inc(2)
+  doAssert s == t
+  s.inc(1)
+  doAssert s != t
+  t.inc(1)
+  doAssert s == t
+
+block mpairsTableTest1:
+  var t = initTable[string, int]()
+  t["a"] = 1
+  t["b"] = 2
+  t["c"] = 3
+  t["d"] = 4
+  for k, v in t.mpairs:
+    if k == "a" or k == "c":
+      v = 9
+
+  for k, v in t.pairs:
+    if k == "a" or k == "c":
+      doAssert v == 9
+    else:
+      doAssert v != 1 and v != 3
+
+block SyntaxTest:
+  var x = toTable[int, string]({:})
+
+block zeroHashKeysTest:
+  proc doZeroHashValueTest[T, K, V](t: T, nullHashKey: K, value: V) =
+    let initialLen = t.len
+    var testTable = t
+    testTable[nullHashKey] = value
+    doAssert testTable[nullHashKey] == value
+    doAssert testTable.len == initialLen + 1
+    testTable.del(nullHashKey)
+    doAssert testTable.len == initialLen
+
+  # with empty table
+  doZeroHashValueTest(toTable[int,int]({:}), 0, 42)
+  doZeroHashValueTest(toTable[string,int]({:}), "", 23)
+  doZeroHashValueTest(toOrderedTable[int,int]({:}), 0, 42)
+  doZeroHashValueTest(toOrderedTable[string,int]({:}), "", 23)
+
+  # with non-empty table
+  doZeroHashValueTest(toTable[int,int]({1:2}), 0, 42)
+  doZeroHashValueTest(toTable[string,string]({"foo": "bar"}), "", "zero")
+  doZeroHashValueTest(toOrderedTable[int,int]({3:4}), 0, 42)
+  doZeroHashValueTest(toOrderedTable[string,string]({"egg": "sausage"}),
+      "", "spam")
+
+block clearTableTest:
+  var t = data.toTable
+  doAssert t.len() != 0
+  t.clear()
+  doAssert t.len() == 0
+
+block clearOrderedTableTest:
+  var t = data.toOrderedTable
+  doAssert t.len() != 0
+  t.clear()
+  doAssert t.len() == 0
+
+block clearCountTableTest:
+  var t = initCountTable[string]()
+  t.inc("90", 3)
+  t.inc("12", 2)
+  t.inc("34", 1)
+  doAssert t.len() != 0
+  t.clear()
+  doAssert t.len() == 0
+
+block withKeyTest:
+  var t: SharedTable[int, int]
+  t.init()
+  t.withKey(1) do (k: int, v: var int, pairExists: var bool):
+    doAssert(v == 0)
+    pairExists = true
+    v = 42
+  doAssert(t.mget(1) == 42)
+  t.withKey(1) do (k: int, v: var int, pairExists: var bool):
+    doAssert(v == 42)
+    pairExists = false
+  try:
+    discard t.mget(1)
+    doAssert(false, "KeyError expected")
+  except KeyError:
+    discard
+  t.withKey(2) do (k: int, v: var int, pairExists: var bool):
+    pairExists = false
+  try:
+    discard t.mget(2)
+    doAssert(false, "KeyError expected")
+  except KeyError:
+    discard
+
+block takeTest:
+  var t = initTable[string, int]()
+  t["key"] = 123
+
+  var val = 0
+  doAssert(t.take("key", val))
+  doAssert(val == 123)
+
+  val = -1
+  doAssert(not t.take("key", val))
+  doAssert(val == -1)
+
+  doAssert(not t.take("otherkey", val))
+  doAssert(val == -1)
+
+proc orderedTableSortTest() =
+  var t = initOrderedTable[string, int](2)
+  for key, val in items(data): t[key] = val
+  for key, val in items(data): doAssert t[key] == val
+  t.sort(proc (x, y: tuple[key: string, val: int]): int = cmp(x.key, y.key))
+  var i = 0
+  # `pairs` needs to yield in sorted order:
+  for key, val in pairs(t):
+    doAssert key == sorteddata[i][0]
+    doAssert val == sorteddata[i][1]
+    inc(i)
+
+  # check that lookup still works:
+  for key, val in pairs(t):
+    doAssert val == t[key]
+  # check that insert still works:
+  t["newKeyHere"] = 80
+
+
+orderedTableSortTest()
+echo "true"
+
diff --git a/tests/compileoption/texperimental.nim b/tests/compileoption/texperimental.nim
new file mode 100644
index 000000000..be637e58a
--- /dev/null
+++ b/tests/compileoption/texperimental.nim
@@ -0,0 +1,20 @@
+static:
+  doAssert compileOption("experimental", "dotOperators")
+  doAssert compileOption("experimental", "callOperator")
+  doAssert compileOption("experimental", "parallel")
+  doAssert compileOption("experimental", "destructor")
+  doAssert compileOption("experimental", "notnil")
+  doAssert compileOption("experimental", "dynamicBindSym")
+  doAssert compileOption("experimental", "codeReordering")
+  doAssert compileOption("experimental", "compiletimeFFI")
+  doAssert compileOption("experimental", "vmopsDanger")
+  doAssert compileOption("experimental", "strictFuncs")
+  doAssert compileOption("experimental", "views")
+  doAssert compileOption("experimental", "strictNotNil")
+  doAssert compileOption("experimental", "strictEffects")
+  doAssert compileOption("experimental", "flexibleOptionalParams")
+  doAssert compileOption("experimental", "strictDefs")
+  doAssert compileOption("experimental", "strictCaseObjects")
+  doAssert compileOption("experimental", "inferGenericTypes")
+  doAssert compileOption("experimental", "openSym")
+  doAssert compileOption("experimental", "vtables")
diff --git a/tests/compileoption/texperimental.nims b/tests/compileoption/texperimental.nims
new file mode 100644
index 000000000..c2c357caa
--- /dev/null
+++ b/tests/compileoption/texperimental.nims
@@ -0,0 +1,19 @@
+switch("experimental", "dotOperators")
+switch("experimental", "callOperator")
+switch("experimental", "parallel")
+switch("experimental", "destructor")
+switch("experimental", "notnil")
+switch("experimental", "dynamicBindSym")
+switch("experimental", "codeReordering")
+switch("experimental", "compiletimeFFI")
+switch("experimental", "vmopsDanger")
+switch("experimental", "strictFuncs")
+switch("experimental", "views")
+switch("experimental", "strictNotNil")
+switch("experimental", "strictEffects")
+switch("experimental", "flexibleOptionalParams")
+switch("experimental", "strictDefs")
+switch("experimental", "strictCaseObjects")
+switch("experimental", "inferGenericTypes")
+switch("experimental", "openSym")
+switch("experimental", "vtables")
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/samplelib.nim b/tests/compiler/samplelib.nim
new file mode 100644
index 000000000..c70971722
--- /dev/null
+++ b/tests/compiler/samplelib.nim
@@ -0,0 +1,2 @@
+# Sample library used by tcmdlineclib.nim
+proc test(): int {.cdecl, exportc, dynlib.} = 123
diff --git a/tests/compiler/t12684.nim b/tests/compiler/t12684.nim
new file mode 100644
index 000000000..a5f33d2c9
--- /dev/null
+++ b/tests/compiler/t12684.nim
@@ -0,0 +1,7 @@
+discard """
+  cmd: "nim check --hints:off --warnings:off $file"
+  errormsg: "undeclared identifier: 'Undeclared'"
+"""
+
+var x: Undeclared
+import compiler/nimeval
diff --git a/tests/compiler/tasciitables.nim b/tests/compiler/tasciitables.nim
new file mode 100644
index 000000000..2f3b7bf2f
--- /dev/null
+++ b/tests/compiler/tasciitables.nim
@@ -0,0 +1,109 @@
+import stdtest/unittest_light
+import std/private/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/tasm.nim b/tests/compiler/tasm.nim
new file mode 100644
index 000000000..9f60231e0
--- /dev/null
+++ b/tests/compiler/tasm.nim
@@ -0,0 +1,15 @@
+proc testAsm() =
+  let src = 41
+  var dst = 0
+
+  asm """
+    mov %1, %0\n\t
+    add $1, %0
+    : "=r" (`dst`)
+    : "r" (`src`)"""
+
+  doAssert dst == 42
+
+when defined(gcc) or defined(clang) and not defined(cpp):
+  {.passc: "-std=c99".}
+  testAsm()
\ No newline at end of file
diff --git a/tests/compiler/tbtrees.nim b/tests/compiler/tbtrees.nim
new file mode 100644
index 000000000..973c26420
--- /dev/null
+++ b/tests/compiler/tbtrees.nim
@@ -0,0 +1,84 @@
+discard """
+  output: '''
+www.amazon.com: 207.171.182.16
+www.apple.com: 17.112.152.32
+www.cnn.com: 64.236.16.20
+www.cs.princeton.edu: abc
+www.dell.com: 143.166.224.230
+www.ebay.com: 66.135.192.87
+www.espn.com: 199.181.135.201
+www.google.com: 216.239.41.99
+www.microsoft.com: 207.126.99.140
+www.nytimes.com: 199.239.136.200
+www.princeton.edu: 128.112.128.15
+www.simpsons.com: 209.052.165.60
+www.slashdot.org: 66.35.250.151
+www.weather.com: 63.111.66.11
+www.yahoo.com: 216.109.118.65
+www.yale.edu: 130.132.143.21
+'''
+"""
+
+import compiler/btrees
+
+import random, tables
+
+proc main =
+  var st = initBTree[string, string]()
+  st.add("www.cs.princeton.edu", "abc")
+  st.add("www.princeton.edu",    "128.112.128.15")
+  st.add("www.yale.edu",         "130.132.143.21")
+  st.add("www.simpsons.com",     "209.052.165.60")
+  st.add("www.apple.com",        "17.112.152.32")
+  st.add("www.amazon.com",       "207.171.182.16")
+  st.add("www.ebay.com",         "66.135.192.87")
+  st.add("www.cnn.com",          "64.236.16.20")
+  st.add("www.google.com",       "216.239.41.99")
+  st.add("www.nytimes.com",      "199.239.136.200")
+  st.add("www.microsoft.com",    "207.126.99.140")
+  st.add("www.dell.com",         "143.166.224.230")
+  st.add("www.slashdot.org",     "66.35.250.151")
+  st.add("www.espn.com",         "199.181.135.201")
+  st.add("www.weather.com",      "63.111.66.11")
+  st.add("www.yahoo.com",        "216.109.118.65")
+
+  doAssert st.getOrDefault("www.cs.princeton.edu") == "abc"
+  doAssert st.getOrDefault("www.harvardsucks.com") == ""
+
+  doAssert st.getOrDefault("www.simpsons.com") == "209.052.165.60"
+  doAssert st.getOrDefault("www.apple.com") == "17.112.152.32"
+  doAssert st.getOrDefault("www.ebay.com") == "66.135.192.87"
+  doAssert st.getOrDefault("www.dell.com") == "143.166.224.230"
+  doAssert(st.len == 16)
+
+  for k, v in st:
+    echo k, ": ", v
+
+  when false:
+    var b2 = initBTree[string, string]()
+    const iters = 10_000
+    for i in 1..iters:
+      b2.add($i, $(iters - i))
+    for i in 1..iters:
+      let x = b2.getOrDefault($i)
+      if x != $(iters - i):
+        echo "got ", x, ", but expected ", iters - i
+    echo b2.len
+
+  when true:
+    var b2 = initBTree[int, string]()
+    var t2 = initTable[int, string]()
+    const iters = 100
+    for i in 1..iters:
+      let x = rand(high(int))
+      if not t2.hasKey(x):
+        doAssert b2.getOrDefault(x).len == 0, " what, tree has this element " & $x
+        t2[x] = $x
+        b2.add(x, $x)
+
+    doAssert(b2.len == t2.len, "unique entries " & $b2.len)
+    for k, v in t2:
+      doAssert $k == v
+      doAssert b2.getOrDefault(k) == $k
+
+main()
diff --git a/tests/compiler/tcmdlineclib.nim b/tests/compiler/tcmdlineclib.nim
new file mode 100644
index 000000000..c06d6f103
--- /dev/null
+++ b/tests/compiler/tcmdlineclib.nim
@@ -0,0 +1,3 @@
+proc test(): int {.importc, cdecl.}
+
+doAssert test() == 123
diff --git a/tests/compiler/tcmdlineclib.nims b/tests/compiler/tcmdlineclib.nims
new file mode 100644
index 000000000..f7899d92e
--- /dev/null
+++ b/tests/compiler/tcmdlineclib.nims
@@ -0,0 +1,10 @@
+import os
+
+selfExec "c --app:lib " & (projectDir() / "samplelib.nim")
+switch("clibdir", projectDir())
+--clib:samplelib
+
+# Make test executable can load sample shared library.
+# `-rpath` option doesn't work and ignored on Windows.
+# But the dll file in same directory as executable file is loaded.
+switch("passL", "-Wl,-rpath," & projectDir())
diff --git a/tests/compiler/tcmdlinecpuamd64.nim b/tests/compiler/tcmdlinecpuamd64.nim
new file mode 100644
index 000000000..0265e3d94
--- /dev/null
+++ b/tests/compiler/tcmdlinecpuamd64.nim
@@ -0,0 +1,10 @@
+discard """
+  cmd: "nim $target $options --cpu:amd64 $file"
+  disabled: "32bit"
+"""
+
+import strutils
+
+static:
+  #cpu is set to "i386" in tcpuamd64.nim.cfg, but --cpu:amd64 in command line should override it.
+  doAssert cmpIgnoreCase(hostCPU, "amd64") == 0
diff --git a/tests/compiler/tcmdlinecpuamd64.nim.cfg b/tests/compiler/tcmdlinecpuamd64.nim.cfg
new file mode 100644
index 000000000..f2e5064ad
--- /dev/null
+++ b/tests/compiler/tcmdlinecpuamd64.nim.cfg
@@ -0,0 +1 @@
+cpu:i386
diff --git a/tests/compiler/tcmdlineoswin.nim b/tests/compiler/tcmdlineoswin.nim
new file mode 100644
index 000000000..602671e98
--- /dev/null
+++ b/tests/compiler/tcmdlineoswin.nim
@@ -0,0 +1,14 @@
+discard """
+  cmd: "nim $target $options --os:windows $file"
+  disabled: "linux"
+  disabled: "bsd"
+  disabled: "osx"
+  disabled: "unix"
+  disabled: "posix"
+"""
+
+import strutils
+
+static:
+  #os is set to "linux" in toswin.nim.cfg, but --os:windows in command line should override it.
+  doAssert cmpIgnoreCase(hostOS, "windows") == 0
diff --git a/tests/compiler/tcmdlineoswin.nim.cfg b/tests/compiler/tcmdlineoswin.nim.cfg
new file mode 100644
index 000000000..037f87651
--- /dev/null
+++ b/tests/compiler/tcmdlineoswin.nim.cfg
@@ -0,0 +1 @@
+os:linux
diff --git a/tests/compiler/tcppCompileToNamespace.nim b/tests/compiler/tcppCompileToNamespace.nim
new file mode 100644
index 000000000..4e895c38b
--- /dev/null
+++ b/tests/compiler/tcppCompileToNamespace.nim
@@ -0,0 +1,11 @@
+discard """
+cmd: "nim cpp --cppCompileToNamespace:foo $options -r $file"
+"""
+
+# Theoretically nim could just ignore the flag cppCompileToNamespace
+# and this test would pass.  Setting ``ccodeCheck`` for a c++ target
+# doesn't work.
+
+import os
+
+echo "a" / "b"
diff --git a/tests/compiler/tdebugutils.nim b/tests/compiler/tdebugutils.nim
new file mode 100644
index 000000000..50b15cb78
--- /dev/null
+++ b/tests/compiler/tdebugutils.nim
@@ -0,0 +1,38 @@
+discard """
+  joinable: false
+  disabled: true
+"""
+
+#[
+This test illustrates some features of `debugutils` to debug the compiler.
+
+## example
+this shows how to enable compiler logging just for a section of user code,
+without generating tons of unrelated log messages for code you're not interested
+in debugging.
+
+```sh
+# enable some debugging code, e.g. the `when false:` block in `semExpr`
+nim c -o:bin/nim_temp --stacktrace -d:debug -d:nimDebugUtils compiler/nim
+bin/nim_temp c tests/compiler/tdebugutils.nim
+```
+
+(use --filenames:abs for abs files)
+
+## result
+("<", "tdebugutils.nim(16, 3)",  {.define(nimCompilerDebug).}, nil)
+(">", "tdebugutils.nim(17, 3)", let a = 2.5 * 3, {}, nkLetSection)
+(">", "tdebugutils.nim(17, 15)", 2.5 * 3, {efAllowDestructor, efWantValue}, nkInfix)
+(">", "tdebugutils.nim(17, 11)", 2.5, {efAllowStmt, efDetermineType, efOperand}, nkFloatLit)
+("<", "tdebugutils.nim(17, 11)", 2.5, float64)
+(">", "tdebugutils.nim(17, 17)", 3, {efAllowStmt, efDetermineType, efOperand}, nkIntLit)
+("<", "tdebugutils.nim(17, 17)", 3, int literal(3))
+("<", "tdebugutils.nim(17, 15)", 2.5 * 3, float)
+("<", "tdebugutils.nim(17, 3)", let a = 2.5 * 3, nil)
+(">", "tdebugutils.nim(18, 3)",  {.undef(nimCompilerDebug).}, {}, nkPragma)
+]#
+
+proc main =
+  {.define(nimCompilerDebug).}
+  let a = 2.5 * 3
+  {.undef(nimCompilerDebug).}
diff --git a/tests/compiler/tgrammar.nim b/tests/compiler/tgrammar.nim
new file mode 100644
index 000000000..772d0f0cc
--- /dev/null
+++ b/tests/compiler/tgrammar.nim
@@ -0,0 +1,7 @@
+discard """
+  matrix: "-d:nimTestGrammar"
+"""
+
+import compiler/parser
+
+checkSameGrammar()
diff --git a/tests/compiler/tint128.nim b/tests/compiler/tint128.nim
new file mode 100644
index 000000000..a30b1b1cc
--- /dev/null
+++ b/tests/compiler/tint128.nim
@@ -0,0 +1,184 @@
+import compiler/int128
+
+let (a,b) = divMod(Ten,Ten)
+
+doAssert $One == "1"
+doAssert $Ten == "10"
+doAssert $Zero == "0"
+let c = parseDecimalInt128("12345678989876543210123456789")
+doAssert $c == "12345678989876543210123456789"
+
+var d : array[39, Int128]
+d[0] =  parseDecimalInt128("1")
+d[1] =  parseDecimalInt128("10")
+d[2] =  parseDecimalInt128("100")
+d[3] =  parseDecimalInt128("1000")
+d[4] =  parseDecimalInt128("10000")
+d[5] =  parseDecimalInt128("100000")
+d[6] =  parseDecimalInt128("1000000")
+d[7] =  parseDecimalInt128("10000000")
+d[8] =  parseDecimalInt128("100000000")
+d[9] =  parseDecimalInt128("1000000000")
+d[10] = parseDecimalInt128("10000000000")
+d[11] = parseDecimalInt128("100000000000")
+d[12] = parseDecimalInt128("1000000000000")
+d[13] = parseDecimalInt128("10000000000000")
+d[14] = parseDecimalInt128("100000000000000")
+d[15] = parseDecimalInt128("1000000000000000")
+d[16] = parseDecimalInt128("10000000000000000")
+d[17] = parseDecimalInt128("100000000000000000")
+d[18] = parseDecimalInt128("1000000000000000000")
+d[19] = parseDecimalInt128("10000000000000000000")
+d[20] = parseDecimalInt128("100000000000000000000")
+d[21] = parseDecimalInt128("1000000000000000000000")
+d[22] = parseDecimalInt128("10000000000000000000000")
+d[23] = parseDecimalInt128("100000000000000000000000")
+d[24] = parseDecimalInt128("1000000000000000000000000")
+d[25] = parseDecimalInt128("10000000000000000000000000")
+d[26] = parseDecimalInt128("100000000000000000000000000")
+d[27] = parseDecimalInt128("1000000000000000000000000000")
+d[28] = parseDecimalInt128("10000000000000000000000000000")
+d[29] = parseDecimalInt128("100000000000000000000000000000")
+d[30] = parseDecimalInt128("1000000000000000000000000000000")
+d[31] = parseDecimalInt128("10000000000000000000000000000000")
+d[32] = parseDecimalInt128("100000000000000000000000000000000")
+d[33] = parseDecimalInt128("1000000000000000000000000000000000")
+d[34] = parseDecimalInt128("10000000000000000000000000000000000")
+d[35] = parseDecimalInt128("100000000000000000000000000000000000")
+d[36] = parseDecimalInt128("1000000000000000000000000000000000000")
+d[37] = parseDecimalInt128("10000000000000000000000000000000000000")
+d[38] = parseDecimalInt128("100000000000000000000000000000000000000")
+
+for i in 0..<d.len:
+  for j in 0..<d.len:
+    doAssert(cmp(d[i], d[j]) == cmp(i,j))
+    if i + j < d.len:
+      doAssert d[i] * d[j] == d[i+j]
+    if i - j >= 0:
+      doAssert d[i] div d[j] == d[i-j]
+
+var sum: Int128
+
+for it in d:
+  sum += it
+
+doAssert $sum == "111111111111111111111111111111111111111"
+
+for it in d.mitems:
+  it = -it
+
+for i in 0..<d.len:
+  for j in 0..<d.len:
+    doAssert(cmp(d[i], d[j]) == -cmp(i,j))
+    if i + j < d.len:
+      doAssert d[i] * d[j] == -d[i+j]
+    if i - j >= 0:
+      doAssert d[i] div d[j] == -d[i-j]
+
+doAssert $high(Int128) == "170141183460469231731687303715884105727"
+doAssert $low(Int128) == "-170141183460469231731687303715884105728"
+
+var ma = 100'i64
+var mb = 13
+
+doAssert toInt128(ma) * toInt128(0) == toInt128(0)
+doAssert toInt128(-ma) * toInt128(0) == toInt128(0)
+
+# sign correctness
+doAssert divMod(toInt128( ma),toInt128( mb)) == (toInt128( ma div  mb), toInt128( ma mod  mb))
+doAssert divMod(toInt128(-ma),toInt128( mb)) == (toInt128(-ma div  mb), toInt128(-ma mod  mb))
+doAssert divMod(toInt128( ma),toInt128(-mb)) == (toInt128( ma div -mb), toInt128( ma mod -mb))
+doAssert divMod(toInt128(-ma),toInt128(-mb)) == (toInt128(-ma div -mb), toInt128(-ma mod -mb))
+
+doAssert divMod(toInt128( mb),toInt128( mb)) == (toInt128( mb div  mb), toInt128( mb mod  mb))
+doAssert divMod(toInt128(-mb),toInt128( mb)) == (toInt128(-mb div  mb), toInt128(-mb mod  mb))
+doAssert divMod(toInt128( mb),toInt128(-mb)) == (toInt128( mb div -mb), toInt128( mb mod -mb))
+doAssert divMod(toInt128(-mb),toInt128(-mb)) == (toInt128(-mb div -mb), toInt128(-mb mod -mb))
+
+doAssert divMod(toInt128( mb),toInt128( ma)) == (toInt128( mb div  ma), toInt128( mb mod  ma))
+doAssert divMod(toInt128(-mb),toInt128( ma)) == (toInt128(-mb div  ma), toInt128(-mb mod  ma))
+doAssert divMod(toInt128( mb),toInt128(-ma)) == (toInt128( mb div -ma), toInt128( mb mod -ma))
+doAssert divMod(toInt128(-mb),toInt128(-ma)) == (toInt128(-mb div -ma), toInt128(-mb mod -ma))
+
+let e = parseDecimalInt128("70997106675279150998592376708984375")
+
+let strArray = [
+  # toHex(e shr 0), toHex(e shr 1), toHex(e shr 2), toHex(e shr 3)
+  "000dac6d782d266a37300c32591eee37", "0006d636bc1693351b9806192c8f771b", "00036b1b5e0b499a8dcc030c9647bb8d", "0001b58daf05a4cd46e601864b23ddc6",
+  "0000dac6d782d266a37300c32591eee3", "00006d636bc1693351b9806192c8f771", "000036b1b5e0b499a8dcc030c9647bb8", "00001b58daf05a4cd46e601864b23ddc",
+  "00000dac6d782d266a37300c32591eee", "000006d636bc1693351b9806192c8f77", "0000036b1b5e0b499a8dcc030c9647bb", "000001b58daf05a4cd46e601864b23dd",
+  "000000dac6d782d266a37300c32591ee", "0000006d636bc1693351b9806192c8f7", "00000036b1b5e0b499a8dcc030c9647b", "0000001b58daf05a4cd46e601864b23d",
+  "0000000dac6d782d266a37300c32591e", "00000006d636bc1693351b9806192c8f", "000000036b1b5e0b499a8dcc030c9647", "00000001b58daf05a4cd46e601864b23",
+  "00000000dac6d782d266a37300c32591", "000000006d636bc1693351b9806192c8", "0000000036b1b5e0b499a8dcc030c964", "000000001b58daf05a4cd46e601864b2",
+  "000000000dac6d782d266a37300c3259", "0000000006d636bc1693351b9806192c", "00000000036b1b5e0b499a8dcc030c96", "0000000001b58daf05a4cd46e601864b",
+  "0000000000dac6d782d266a37300c325", "00000000006d636bc1693351b9806192", "000000000036b1b5e0b499a8dcc030c9", "00000000001b58daf05a4cd46e601864",
+  "00000000000dac6d782d266a37300c32", "000000000006d636bc1693351b980619", "0000000000036b1b5e0b499a8dcc030c", "000000000001b58daf05a4cd46e60186",
+  "000000000000dac6d782d266a37300c3", "0000000000006d636bc1693351b98061", "00000000000036b1b5e0b499a8dcc030", "0000000000001b58daf05a4cd46e6018",
+  "0000000000000dac6d782d266a37300c", "00000000000006d636bc1693351b9806", "000000000000036b1b5e0b499a8dcc03", "00000000000001b58daf05a4cd46e601",
+  "00000000000000dac6d782d266a37300", "000000000000006d636bc1693351b980", "0000000000000036b1b5e0b499a8dcc0", "000000000000001b58daf05a4cd46e60",
+  "000000000000000dac6d782d266a3730", "0000000000000006d636bc1693351b98", "00000000000000036b1b5e0b499a8dcc", "0000000000000001b58daf05a4cd46e6",
+  "0000000000000000dac6d782d266a373", "00000000000000006d636bc1693351b9", "000000000000000036b1b5e0b499a8dc", "00000000000000001b58daf05a4cd46e",
+  "00000000000000000dac6d782d266a37", "000000000000000006d636bc1693351b", "0000000000000000036b1b5e0b499a8d", "000000000000000001b58daf05a4cd46",
+  "000000000000000000dac6d782d266a3", "0000000000000000006d636bc1693351", "00000000000000000036b1b5e0b499a8", "0000000000000000001b58daf05a4cd4",
+  "0000000000000000000dac6d782d266a", "00000000000000000006d636bc169335", "000000000000000000036b1b5e0b499a", "00000000000000000001b58daf05a4cd",
+  "00000000000000000000dac6d782d266", "000000000000000000006d636bc16933", "0000000000000000000036b1b5e0b499", "000000000000000000001b58daf05a4c",
+  "000000000000000000000dac6d782d26", "0000000000000000000006d636bc1693", "00000000000000000000036b1b5e0b49", "0000000000000000000001b58daf05a4",
+  "0000000000000000000000dac6d782d2", "00000000000000000000006d636bc169", "000000000000000000000036b1b5e0b4", "00000000000000000000001b58daf05a",
+  "00000000000000000000000dac6d782d", "000000000000000000000006d636bc16", "0000000000000000000000036b1b5e0b", "000000000000000000000001b58daf05",
+  "000000000000000000000000dac6d782", "0000000000000000000000006d636bc1", "00000000000000000000000036b1b5e0", "0000000000000000000000001b58daf0",
+  "0000000000000000000000000dac6d78", "00000000000000000000000006d636bc", "000000000000000000000000036b1b5e", "00000000000000000000000001b58daf",
+  "00000000000000000000000000dac6d7", "000000000000000000000000006d636b", "0000000000000000000000000036b1b5", "000000000000000000000000001b58da",
+  "000000000000000000000000000dac6d", "0000000000000000000000000006d636", "00000000000000000000000000036b1b", "0000000000000000000000000001b58d",
+  "0000000000000000000000000000dac6", "00000000000000000000000000006d63", "000000000000000000000000000036b1", "00000000000000000000000000001b58",
+  "00000000000000000000000000000dac", "000000000000000000000000000006d6", "0000000000000000000000000000036b", "000000000000000000000000000001b5",
+  "000000000000000000000000000000da", "0000000000000000000000000000006d", "00000000000000000000000000000036", "0000000000000000000000000000001b",
+  "0000000000000000000000000000000d", "00000000000000000000000000000006", "00000000000000000000000000000003", "00000000000000000000000000000001",
+  "00000000000000000000000000000000", "00000000000000000000000000000000", "00000000000000000000000000000000", "00000000000000000000000000000000",
+  "00000000000000000000000000000000", "00000000000000000000000000000000", "00000000000000000000000000000000", "00000000000000000000000000000000",
+  "00000000000000000000000000000000", "00000000000000000000000000000000", "00000000000000000000000000000000", "00000000000000000000000000000000",
+]
+
+for i in 0 ..< 128:
+  let str1 = toHex(e shr i)
+  let str2 = strArray[i]
+  doAssert str1 == str2
+
+let strArray2 = [
+  "000dac6d782d266a37300c32591eee37", "001b58daf05a4cd46e601864b23ddc6e", "0036b1b5e0b499a8dcc030c9647bb8dc", "006d636bc1693351b9806192c8f771b8",
+  "00dac6d782d266a37300c32591eee370", "01b58daf05a4cd46e601864b23ddc6e0", "036b1b5e0b499a8dcc030c9647bb8dc0", "06d636bc1693351b9806192c8f771b80",
+  "0dac6d782d266a37300c32591eee3700", "1b58daf05a4cd46e601864b23ddc6e00", "36b1b5e0b499a8dcc030c9647bb8dc00", "6d636bc1693351b9806192c8f771b800",
+  "dac6d782d266a37300c32591eee37000", "b58daf05a4cd46e601864b23ddc6e000", "6b1b5e0b499a8dcc030c9647bb8dc000", "d636bc1693351b9806192c8f771b8000",
+  "ac6d782d266a37300c32591eee370000", "58daf05a4cd46e601864b23ddc6e0000", "b1b5e0b499a8dcc030c9647bb8dc0000", "636bc1693351b9806192c8f771b80000",
+  "c6d782d266a37300c32591eee3700000", "8daf05a4cd46e601864b23ddc6e00000", "1b5e0b499a8dcc030c9647bb8dc00000", "36bc1693351b9806192c8f771b800000",
+  "6d782d266a37300c32591eee37000000", "daf05a4cd46e601864b23ddc6e000000", "b5e0b499a8dcc030c9647bb8dc000000", "6bc1693351b9806192c8f771b8000000",
+  "d782d266a37300c32591eee370000000", "af05a4cd46e601864b23ddc6e0000000", "5e0b499a8dcc030c9647bb8dc0000000", "bc1693351b9806192c8f771b80000000",
+  "782d266a37300c32591eee3700000000", "f05a4cd46e601864b23ddc6e00000000", "e0b499a8dcc030c9647bb8dc00000000", "c1693351b9806192c8f771b800000000",
+  "82d266a37300c32591eee37000000000", "05a4cd46e601864b23ddc6e000000000", "0b499a8dcc030c9647bb8dc000000000", "1693351b9806192c8f771b8000000000",
+  "2d266a37300c32591eee370000000000", "5a4cd46e601864b23ddc6e0000000000", "b499a8dcc030c9647bb8dc0000000000", "693351b9806192c8f771b80000000000",
+  "d266a37300c32591eee3700000000000", "a4cd46e601864b23ddc6e00000000000", "499a8dcc030c9647bb8dc00000000000", "93351b9806192c8f771b800000000000",
+  "266a37300c32591eee37000000000000", "4cd46e601864b23ddc6e000000000000", "99a8dcc030c9647bb8dc000000000000", "3351b9806192c8f771b8000000000000",
+  "66a37300c32591eee370000000000000", "cd46e601864b23ddc6e0000000000000", "9a8dcc030c9647bb8dc0000000000000", "351b9806192c8f771b80000000000000",
+  "6a37300c32591eee3700000000000000", "d46e601864b23ddc6e00000000000000", "a8dcc030c9647bb8dc00000000000000", "51b9806192c8f771b800000000000000",
+  "a37300c32591eee37000000000000000", "46e601864b23ddc6e000000000000000", "8dcc030c9647bb8dc000000000000000", "1b9806192c8f771b8000000000000000",
+  "37300c32591eee370000000000000000", "6e601864b23ddc6e0000000000000000", "dcc030c9647bb8dc0000000000000000", "b9806192c8f771b80000000000000000",
+  "7300c32591eee3700000000000000000", "e601864b23ddc6e00000000000000000", "cc030c9647bb8dc00000000000000000", "9806192c8f771b800000000000000000",
+  "300c32591eee37000000000000000000", "601864b23ddc6e000000000000000000", "c030c9647bb8dc000000000000000000", "806192c8f771b8000000000000000000",
+  "00c32591eee370000000000000000000", "01864b23ddc6e0000000000000000000", "030c9647bb8dc0000000000000000000", "06192c8f771b80000000000000000000",
+  "0c32591eee3700000000000000000000", "1864b23ddc6e00000000000000000000", "30c9647bb8dc00000000000000000000", "6192c8f771b800000000000000000000",
+  "c32591eee37000000000000000000000", "864b23ddc6e000000000000000000000", "0c9647bb8dc000000000000000000000", "192c8f771b8000000000000000000000",
+  "32591eee370000000000000000000000", "64b23ddc6e0000000000000000000000", "c9647bb8dc0000000000000000000000", "92c8f771b80000000000000000000000",
+  "2591eee3700000000000000000000000", "4b23ddc6e00000000000000000000000", "9647bb8dc00000000000000000000000", "2c8f771b800000000000000000000000",
+  "591eee37000000000000000000000000", "b23ddc6e000000000000000000000000", "647bb8dc000000000000000000000000", "c8f771b8000000000000000000000000",
+  "91eee370000000000000000000000000", "23ddc6e0000000000000000000000000", "47bb8dc0000000000000000000000000", "8f771b80000000000000000000000000",
+  "1eee3700000000000000000000000000", "3ddc6e00000000000000000000000000", "7bb8dc00000000000000000000000000", "f771b800000000000000000000000000",
+  "eee37000000000000000000000000000", "ddc6e000000000000000000000000000", "bb8dc000000000000000000000000000", "771b8000000000000000000000000000",
+  "ee370000000000000000000000000000", "dc6e0000000000000000000000000000", "b8dc0000000000000000000000000000", "71b80000000000000000000000000000",
+  "e3700000000000000000000000000000", "c6e00000000000000000000000000000", "8dc00000000000000000000000000000", "1b800000000000000000000000000000",
+  "37000000000000000000000000000000", "6e000000000000000000000000000000", "dc000000000000000000000000000000", "b8000000000000000000000000000000",
+  "70000000000000000000000000000000", "e0000000000000000000000000000000", "c0000000000000000000000000000000", "80000000000000000000000000000000",
+]
+
+for i in 0 ..< 128:
+  let str1 = toHex(e shl i)
+  let str2 = strArray2[i]
+  doAssert str1 == str2
diff --git a/tests/compiler/tnimblecmd.nim b/tests/compiler/tnimblecmd.nim
new file mode 100644
index 000000000..53bce4625
--- /dev/null
+++ b/tests/compiler/tnimblecmd.nim
@@ -0,0 +1,75 @@
+include compiler/[nimblecmd], sets
+
+proc v(s: string): Version = s.newVersion
+
+proc testVersionsComparison =
+  # #head is special in the sense that it's assumed to always be newest.
+  doAssert v"1.0" < v"#head"
+  doAssert v"1.0" < v"1.1"
+  doAssert v"1.0.1" < v"1.1"
+  doAssert v"1" < v"1.1"
+  doAssert v"#aaaqwe" < v"1.1" # We cannot assume that a branch is newer.
+  doAssert v"#a111" < v"#head"
+
+proc testAddPackageWithoutChecksum =
+  ## For backward compatibility it is not required all packages to have a
+  ## sha1 checksum at the end of the name of the Nimble cache directory.
+  ## This way a new compiler will be able to work with an older Nimble.
+
+  let conf = newConfigRef()
+  var rr: PackageInfo
+
+  addPackage conf, rr, "irc-#a111", unknownLineInfo
+  addPackage conf, rr, "irc-#head", unknownLineInfo
+  addPackage conf, rr, "irc-0.1.0", unknownLineInfo
+
+  addPackage conf, rr, "another-0.1", unknownLineInfo
+
+  addPackage conf, rr, "ab-0.1.3", unknownLineInfo
+  addPackage conf, rr, "ab-0.1", unknownLineInfo
+  addPackage conf, rr, "justone-1.0", unknownLineInfo
+
+  doAssert toSeq(rr.chosen).toHashSet ==
+    ["irc-#head", "another-0.1", "ab-0.1.3", "justone-1.0"].toHashSet
+
+proc testAddPackageWithChecksum =
+  let conf = newConfigRef()
+  var rr: PackageInfo
+
+  # in the case of packages with the same version, but different checksums for
+  # now the first one will be chosen
+
+  addPackage conf, rr, "irc-#a111-DBC1F902CB79946E990E38AF51F0BAD36ACFABD9",
+             unknownLineInfo
+  addPackage conf, rr, "irc-#head-042D4BE2B90ED0672E717D71850ABDB0A2D19CD1",
+             unknownLineInfo
+  addPackage conf, rr, "irc-#head-042D4BE2B90ED0672E717D71850ABDB0A2D19CD2",
+             unknownLineInfo
+  addPackage conf, rr, "irc-0.1.0-6EE6DE936B32E82C7DBE526DA3463574F6568FAF",
+             unknownLineInfo
+
+  addPackage conf, rr, "another-0.1", unknownLineInfo
+  addPackage conf, rr, "another-0.1-F07EE6040579F0590608A8FD34F5F2D91D859340",
+             unknownLineInfo
+
+  addPackage conf, rr, "ab-0.1.3-34BC3B72CE46CF5A496D1121CFEA7369385E9EA2",
+             unknownLineInfo
+  addPackage conf, rr, "ab-0.1.3-24BC3B72CE46CF5A496D1121CFEA7369385E9EA2",
+             unknownLineInfo
+  addPackage conf, rr, "ab-0.1-A3CFFABDC4759F7779D541F5E031AED17169390A",
+             unknownLineInfo
+
+  # lower case hex digits is also a valid sha1 checksum
+  addPackage conf, rr, "justone-1.0-f07ee6040579f0590608a8fd34f5f2d91d859340",
+             unknownLineInfo
+
+  doAssert toSeq(rr.chosen).toHashSet == [
+    "irc-#head-042D4BE2B90ED0672E717D71850ABDB0A2D19CD1",
+    "another-0.1",
+    "ab-0.1.3-34BC3B72CE46CF5A496D1121CFEA7369385E9EA2",
+    "justone-1.0-f07ee6040579f0590608a8fd34f5f2d91d859340"
+    ].toHashSet
+
+testVersionsComparison()
+testAddPackageWithoutChecksum()
+testAddPackageWithChecksum()
diff --git a/tests/compiler/tpathutils.nim b/tests/compiler/tpathutils.nim
new file mode 100644
index 000000000..47ff0f06a
--- /dev/null
+++ b/tests/compiler/tpathutils.nim
@@ -0,0 +1,18 @@
+import compiler/pathutils
+import os, strutils
+
+
+doAssert AbsoluteDir"/Users/me///" / RelativeFile"z.nim" == AbsoluteFile"/Users/me/z.nim"
+doAssert AbsoluteDir"/Users/me" / RelativeFile"../z.nim" == AbsoluteFile"/Users/z.nim"
+doAssert AbsoluteDir"/Users/me/" / RelativeFile"../z.nim" == AbsoluteFile"/Users/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 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")
+  doAssert nasty.replace('/', '\\') == r"C:\Users\rumpf\projects\nim\tests\nimble\nimbleDir\simplePkgs\pkgB-#head\pkgA\module.nim"
diff --git a/tests/compiler/tprefixmatches.nim b/tests/compiler/tprefixmatches.nim
new file mode 100644
index 000000000..6a3186729
--- /dev/null
+++ b/tests/compiler/tprefixmatches.nim
@@ -0,0 +1,32 @@
+import compiler/prefixmatches
+import macros
+
+macro check(val, body: untyped): untyped =
+  result = newStmtList()
+  expectKind body, nnkStmtList
+  for b in body:
+    expectKind b, nnkTupleConstr
+    expectLen b, 2
+    let p = b[0]
+    let s = b[1]
+    result.add quote do:
+      doAssert prefixMatch(`p`, `s`) == `val`
+
+check PrefixMatch.Prefix:
+  ("abc", "abc")
+  ("a", "abc")
+  ("xyz", "X_yzzzZe")
+
+check PrefixMatch.Substr:
+  ("b", "abc")
+  ("abc", "fooabcabc")
+  ("abC", "foo_AB_c")
+
+check PrefixMatch.Abbrev:
+  ("abc", "AxxxBxxxCxxx")
+  ("xyz", "X_yabcZe")
+
+check PrefixMatch.None:
+  ("foobar", "afkslfjd_as")
+  ("xyz", "X_yuuZuuZe")
+  ("ru", "remotes")
diff --git a/tests/compiler/tunittest_light.nim b/tests/compiler/tunittest_light.nim
new file mode 100644
index 000000000..2951cc664
--- /dev/null
+++ b/tests/compiler/tunittest_light.nim
@@ -0,0 +1,55 @@
+import stdtest/unittest_light
+
+proc testAssertEquals() =
+  assertEquals("foo", "foo")
+  doAssertRaises(AssertionDefect):
+    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/compilerapi/exposed.nim b/tests/compilerapi/exposed.nim
index 73becd93d..25a858ec9 100644
--- a/tests/compilerapi/exposed.nim
+++ b/tests/compilerapi/exposed.nim
@@ -1,3 +1,3 @@
 
 proc addFloats*(x, y, z: float): float =
-  discard "implementation overriden by tcompilerapi.nim"
+  discard "implementation overridden by tcompilerapi.nim"
diff --git a/tests/compilerapi/invalid.nim b/tests/compilerapi/invalid.nim
new file mode 100644
index 000000000..3c9364402
--- /dev/null
+++ b/tests/compilerapi/invalid.nim
@@ -0,0 +1 @@
+noSuchProc()
diff --git a/tests/compilerapi/myscript.nim b/tests/compilerapi/myscript.nim
index 539b07de1..c5d9fe22a 100644
--- a/tests/compilerapi/myscript.nim
+++ b/tests/compilerapi/myscript.nim
@@ -1,7 +1,12 @@
 
 import exposed
 
+type NumberHolder = object
+  ival: int
+  fval: float
+
 echo "top level statements are executed!"
+echo NumberHolder(ival: 10, fval: 2.0)
 
 proc hostProgramRunsThis*(a, b: float): float =
   result = addFloats(a, b, 1.0)
diff --git a/tests/compilerapi/tcompilerapi.nim b/tests/compilerapi/tcompilerapi.nim
index 90d343264..2d6cfa0ca 100644
--- a/tests/compilerapi/tcompilerapi.nim
+++ b/tests/compilerapi/tcompilerapi.nim
@@ -1,47 +1,98 @@
 discard """
   output: '''top level statements are executed!
+(ival: 10, fval: 2.0)
 2.0
 my secret
+11
+12
+raising VMQuit
 '''
+  joinable: "false"
 """
 
 ## Example program that demonstrates how to use the
 ## compiler as an API to embed into your own projects.
 
-import "../../compiler" / [ast, vmdef, vm, nimeval]
+import "../../compiler" / [ast, vmdef, vm, nimeval, llstream, lineinfos, options]
 import std / [os]
 
-proc main() =
-  let std = findNimStdLib()
-  if std.len == 0:
-    quit "cannot find Nim's standard library"
+proc initInterpreter(script: string): Interpreter =
+  let std = findNimStdLibCompileTime()
+  result = createInterpreter(script, [std, parentDir(currentSourcePath),
+    std / "pure", std / "core"])
 
-  var intr = createInterpreter("myscript.nim", [std, getAppDir()])
-  intr.implementRoutine("*", "exposed", "addFloats", proc (a: VmArgs) =
+proc main() =
+  let i = initInterpreter("myscript.nim")
+  i.implementRoutine("nim", "exposed", "addFloats", proc (a: VmArgs) =
     setResult(a, getFloat(a, 0) + getFloat(a, 1) + getFloat(a, 2))
   )
-
-  intr.evalScript()
-
-  let foreignProc = selectRoutine(intr, "hostProgramRunsThis")
+  i.evalScript()
+  let foreignProc = i.selectRoutine("hostProgramRunsThis")
   if foreignProc == nil:
     quit "script does not export a proc of the name: 'hostProgramRunsThis'"
-  let res = intr.callRoutine(foreignProc, [newFloatNode(nkFloatLit, 0.9),
-                                           newFloatNode(nkFloatLit, 0.1)])
-  if res.kind == nkFloatLit:
-    echo res.floatVal
-  else:
-    echo "bug!"
-
-  let foreignValue = selectUniqueSymbol(intr, "hostProgramWantsThis")
+  let res = i.callRoutine(foreignProc, [newFloatNode(nkFloatLit, 0.9),
+                                        newFloatNode(nkFloatLit, 0.1)])
+  doAssert res.kind == nkFloatLit
+  echo res.floatVal
+
+  let foreignValue = i.selectUniqueSymbol("hostProgramWantsThis")
   if foreignValue == nil:
     quit "script does not export a global of the name: hostProgramWantsThis"
-  let val = intr.getGlobalValue(foreignValue)
-  if val.kind in {nkStrLit..nkTripleStrLit}:
-    echo val.strVal
-  else:
-    echo "bug!"
+  let val = i.getGlobalValue(foreignValue)
+  doAssert val.kind in {nkStrLit..nkTripleStrLit}
+  echo val.strVal
+  i.destroyInterpreter()
+
+main()
+
+block issue9180:
+  proc evalString(code: string, moduleName = "script.nim") =
+    let stream = llStreamOpen(code)
+    let std = findNimStdLibCompileTime()
+    var intr = createInterpreter(moduleName, [std, std / "pure", std / "core"])
+    intr.evalScript(stream)
+    destroyInterpreter(intr)
+    llStreamClose(stream)
 
-  destroyInterpreter(intr)
+  evalString("echo 10+1")
+  evalString("echo 10+2")
 
-main()
\ No newline at end of file
+block error_hook:
+  type VMQuit = object of CatchableError
+
+  let i = initInterpreter("invalid.nim")
+  i.registerErrorHook proc(config: ConfigRef; info: TLineInfo; msg: string;
+                           severity: Severity) {.gcsafe.} =
+    if severity == Error and config.errorCounter >= config.errorMax:
+      echo "raising VMQuit"
+      raise newException(VMQuit, "Script error")
+
+  doAssertRaises(VMQuit):
+    i.evalScript()
+
+block resetmacrocache:
+  let std = findNimStdLibCompileTime()
+  let intr = createInterpreter("script.nim", [std, std / "pure", std / "core"])
+  proc evalString(intr: Interpreter; code: string) =
+    let stream = llStreamOpen(code)
+    intr.evalScript(stream)
+    llStreamClose(stream)
+  let code = """
+import std/[macrocache, macros]
+static:
+  let counter = CacheCounter"valTest"
+  inc counter
+  assert counter.value == 1
+
+  const mySeq = CacheSeq"addTest"
+  mySeq.add(newLit(5))
+  mySeq.add(newLit("hello ic"))
+  assert mySeq.len == 2
+
+  const mcTable = CacheTable"subTest"
+  mcTable["toAdd"] = newStmtList() #would crash if not empty
+  assert mcTable.len == 1
+"""
+  intr.evalString(code)
+  intr.evalString(code)
+  destroyInterpreter(intr)
\ No newline at end of file
diff --git a/tests/compiles/mstaticlib.nim b/tests/compiles/mstaticlib.nim
new file mode 100644
index 000000000..6ed593691
--- /dev/null
+++ b/tests/compiles/mstaticlib.nim
@@ -0,0 +1 @@
+echo 1234
\ No newline at end of file
diff --git a/tests/compiles/t8630.nim b/tests/compiles/t8630.nim
new file mode 100644
index 000000000..8b447d061
--- /dev/null
+++ b/tests/compiles/t8630.nim
@@ -0,0 +1,13 @@
+discard """
+  output: '''
+foo
+bar
+'''
+"""
+
+proc test(strings: seq[string]) =
+  for s in strings:
+    var p3 = addr(s)
+    echo p3[]
+
+test(@["foo", "bar"])
diff --git a/tests/compiles/trecursive_generic_in_compiles.nim b/tests/compiles/trecursive_generic_in_compiles.nim
index 9c7fd10b3..ab31ad0f5 100644
--- a/tests/compiles/trecursive_generic_in_compiles.nim
+++ b/tests/compiles/trecursive_generic_in_compiles.nim
@@ -1,3 +1,7 @@
+discard """
+action: compile
+"""
+
 # bug #3313
 import unittest, sugar
 {.experimental: "notnil".}
@@ -46,7 +50,7 @@ proc `==`*[T](xs, ys: List[T]): bool =
 
 proc asList*[T](xs: varargs[T]): List[T] =
   ## Creates list from varargs
-  proc initListImpl(i: int, xs: openarray[T]): List[T] =
+  proc initListImpl(i: int, xs: openArray[T]): List[T] =
     if i > high(xs):
       Nil[T]()
     else:
@@ -88,11 +92,3 @@ proc foldLeft*[T,U](xs: List[T], z: U, f: (U, T) -> U): U =
   case xs.isEmpty
   of true: z
   else: foldLeft(xs.tail, f(z, xs.head), f)
-
-suite "unittest compilation error":
-
-  test "issue 3313":
-    let lst = lc[$x | (x <- 'a'..'z'), string].asList
-
-    let lstCopy = lst.dup
-    check: lstCopy == lst
diff --git a/tests/compiles/tstaticlib.nim b/tests/compiles/tstaticlib.nim
new file mode 100644
index 000000000..a18b59204
--- /dev/null
+++ b/tests/compiles/tstaticlib.nim
@@ -0,0 +1,22 @@
+import std/[os, osproc, strformat]
+
+
+const dir = "tests/compiles"
+const fileName = dir / "mstaticlib.nim"
+const nim = getCurrentCompilerExe()
+
+block: # bug #18578
+  const libName = dir / "tstaticlib1.a"
+  let (_, status) = execCmdEx(fmt"{nim} c -o:{libName} --app:staticlib {fileName}")
+  doAssert status == 0
+  doAssert fileExists(libName)
+  removeFile(libName)
+
+block: # bug #16947
+  const libName = dir / "tstaticlib2.a"
+  writeFile(libName, "echo 124")
+  doAssert fileExists(libName)
+  let (_, status) = execCmdEx(fmt"{nim} c -o:{libName} --app:staticlib {fileName}")
+  doAssert status == 0
+  doAssert fileExists(libName)
+  removeFile(libName)
diff --git a/tests/concepts/libs/trie.nim b/tests/concepts/libs/trie.nim
new file mode 100644
index 000000000..bdd318492
--- /dev/null
+++ b/tests/concepts/libs/trie.nim
@@ -0,0 +1,26 @@
+import
+  hashes, tables, trie_database
+
+type
+  MemDBTable = Table[KeccakHash, string]
+
+  MemDB* = object
+    tbl: MemDBTable
+
+proc hash*(key: KeccakHash): int =
+  hashes.hash(key.data)
+
+proc get*(db: MemDB, key: KeccakHash): string =
+  db.tbl[key]
+
+proc del*(db: var MemDB, key: KeccakHash): bool =
+  if db.tbl.hasKey(key):
+    db.tbl.del(key)
+    return true
+  else:
+    return false
+
+proc put*(db: var MemDB, key: KeccakHash, value: string): bool =
+  db.tbl[key] = value
+  return true
+
diff --git a/tests/concepts/libs/trie_database.nim b/tests/concepts/libs/trie_database.nim
new file mode 100644
index 000000000..a45c64842
--- /dev/null
+++ b/tests/concepts/libs/trie_database.nim
@@ -0,0 +1,12 @@
+type
+  KeccakHash* = object
+    data*: string
+
+  BytesRange* = object
+    bytes*: string
+
+  TrieDatabase* = concept db
+    put(var db, KeccakHash, string) is bool
+    del(var db, KeccakHash) is bool
+    get(db, KeccakHash) is string
+
diff --git a/tests/concepts/matrixalgo.nim b/tests/concepts/matrixalgo.nim
index 39cf16685..98e5b8b4f 100644
--- a/tests/concepts/matrixalgo.nim
+++ b/tests/concepts/matrixalgo.nim
@@ -16,8 +16,8 @@ type
   AnyTransform3D* = AnyMatrix[4, 4, float]
 
 proc transposed*(m: AnyMatrix): m.TransposedType =
-  for r in 0 .. <m.R:
-    for c in 0 .. <m.C:
+  for r in 0 ..< m.R:
+    for c in 0 ..< m.C:
       result[r, c] = m[c, r]
 
 proc determinant*(m: AnySquareMatrix): int =
diff --git a/tests/concepts/t1128.nim b/tests/concepts/t1128.nim
deleted file mode 100644
index 69a2fa9b7..000000000
--- a/tests/concepts/t1128.nim
+++ /dev/null
@@ -1,21 +0,0 @@
-discard """
-  output: "true\ntrue"
-"""
-
-type
-  TFooContainer[T] = object
-
-  TContainer[T] = concept var c
-    foo(c, T)
-
-proc foo[T](c: var TFooContainer[T], val: T) =
-  discard
-
-proc bar(c: var TContainer) =
-  discard
-
-var fooContainer: TFooContainer[int]
-echo fooContainer is TFooContainer # true.
-echo fooContainer is TFooContainer[int] # true.
-fooContainer.bar()
-
diff --git a/tests/concepts/t18409.nim b/tests/concepts/t18409.nim
new file mode 100644
index 000000000..0edba2d31
--- /dev/null
+++ b/tests/concepts/t18409.nim
@@ -0,0 +1,37 @@
+discard """
+  action: "compile"
+"""
+
+# A vector space over a field F concept.
+type VectorSpace*[F] = concept x, y, type V
+  vector_add(x, y) is V
+  scalar_mul(x, F) is V
+  dimension(V) is Natural
+
+# Real numbers (here floats) form a vector space.
+func vector_add*(v: float, w: float): float =  v + w
+func scalar_mul*(v: float, s: float): float =   v * s
+func dimension*(x: typedesc[float]): Natural = 1
+
+# 2-tuples of real numbers form a vector space.
+func vector_add*(v, w: (float, float)): (float, float) =
+  (vector_add(v[0], w[0]), vector_add(v[1], w[1]))
+
+func scalar_mul*(v: (float, float), s: float): (float, float) =
+  (scalar_mul(v[0], s), scalar_mul(v[1], s))
+
+func dimension*(x: typedesc[(float, float)]): Natural = 2
+
+# Check concept requirements.
+assert float is VectorSpace
+assert (float, float) is VectorSpace
+
+# Commutivity axiom for vector spaces over the same field.
+func axiom_commutivity*[F](u, v: VectorSpace[F]): bool =
+  vector_add(u, v) == vector_add(v, u)
+
+# This is okay.
+assert axiom_commutivity(2.2, 3.3)
+
+# This is not.
+assert axiom_commutivity((2.2, 3.3), (4.4, 5.5))
diff --git a/tests/concepts/t19730.nim b/tests/concepts/t19730.nim
new file mode 100644
index 000000000..575d45dda
--- /dev/null
+++ b/tests/concepts/t19730.nim
@@ -0,0 +1,20 @@
+discard """
+  output: '''1.01.01.01.0
+1.01.01.01.0
+'''
+"""
+
+type
+  Color = concept c
+    c.r is SomeFloat
+    c.g is SomeFloat
+    c.b is SomeFloat
+    c.a is SomeFloat
+
+proc useColor(color: Color) =
+  echo(color.r, color.g, color.b, color.a)
+
+let color = (r: 1.0, g: 1.0, b: 1.0, a: 1.0)
+useColor(color)
+
+useColor((r: 1.0, g: 1.0, b: 1.0, a: 1.0))
diff --git a/tests/concepts/t20237.nim b/tests/concepts/t20237.nim
new file mode 100644
index 000000000..175c7a9d1
--- /dev/null
+++ b/tests/concepts/t20237.nim
@@ -0,0 +1,3 @@
+type Foo* = concept
+  ## doc comment
+  proc foo(x: Self)
diff --git a/tests/concepts/t3330.nim b/tests/concepts/t3330.nim
index a4fff7fb3..901f8d2f4 100644
--- a/tests/concepts/t3330.nim
+++ b/tests/concepts/t3330.nim
@@ -1,48 +1,55 @@
 discard """
+matrix: "--mm:refc"
 errormsg: "type mismatch: got <Bar[system.int]>"
 nimout: '''
-t3330.nim(63, 4) Error: type mismatch: got <Bar[system.int]>
+t3330.nim(70, 4) Error: type mismatch: got <Bar[system.int]>
 but expected one of:
 proc test(foo: Foo[int])
-t3330.nim(48, 8) Hint: Non-matching candidates for add(k, string, T)
-proc add(x: var string; y: string)
   first type mismatch at position: 1
-  required type: var string
-  but expression 'k' is of type: Alias
+  required type for foo: Foo[int]
+  but expression 'bar' is of type: Bar[system.int]
+t3330.nim(55, 8) Hint: Non-matching candidates for add(k, string, T)
 proc add(x: var string; y: char)
   first type mismatch at position: 1
-  required type: var string
+  required type for x: var string
   but expression 'k' is of type: Alias
-proc add(result: var string; x: int64)
-  first type mismatch at position: 1
-  required type: var string
-  but expression 'k' is of type: Alias
-proc add(result: var string; x: float)
+proc add(x: var string; y: cstring)
   first type mismatch at position: 1
-  required type: var string
+  required type for x: var string
   but expression 'k' is of type: Alias
-proc add(x: var string; y: cstring)
+proc add(x: var string; y: string)
   first type mismatch at position: 1
-  required type: var string
+  required type for x: var string
   but expression 'k' is of type: Alias
 proc add[T](x: var seq[T]; y: openArray[T])
   first type mismatch at position: 1
-  required type: var seq[T]
+  required type for x: var seq[T]
   but expression 'k' is of type: Alias
-proc add[T](x: var seq[T]; y: T)
+proc add[T](x: var seq[T]; y: sink T)
   first type mismatch at position: 1
-  required type: var seq[T]
+  required type for x: var seq[T]
   but expression 'k' is of type: Alias
 
-t3330.nim(48, 8) template/generic instantiation from here
-t3330.nim(55, 6) Foo: 'bar.value' cannot be assigned to
-t3330.nim(48, 8) template/generic instantiation from here
-t3330.nim(56, 6) Foo: 'bar.x' cannot be assigned to
+t3330.nim(55, 8) template/generic instantiation of `add` from here
+t3330.nim(62, 6) Foo: 'bar.value' cannot be assigned to
+t3330.nim(55, 8) template/generic instantiation of `add` from here
+t3330.nim(63, 6) Foo: 'bar.x' cannot be assigned to
 
 expression: test(bar)'''
 """
 
 
+
+
+
+
+
+
+
+
+
+
+## line 60
 type
   Foo[T] = concept k
     add(k, string, T)
@@ -61,4 +68,3 @@ proc test(foo: Foo[int]) =
 
 var bar = Bar[int]()
 bar.test()
-
diff --git a/tests/concepts/t3414.nim b/tests/concepts/t3414.nim
deleted file mode 100644
index d45973034..000000000
--- a/tests/concepts/t3414.nim
+++ /dev/null
@@ -1,22 +0,0 @@
-type
-  View[T] = concept v
-    v.empty is bool
-    v.front is T
-    popFront v
-
-proc find(view: View; target: View.T): View =
-  result = view
-
-  while not result.empty:
-    if view.front == target:
-      return
-
-    mixin popFront
-    popFront result
-
-proc popFront[T](s: var seq[T]) = discard
-proc empty[T](s: seq[T]): bool = false
-
-var s1 = @[1, 2, 3]
-let s2 = s1.find(10)
-
diff --git a/tests/concepts/t5642.nim b/tests/concepts/t5642.nim
deleted file mode 100644
index f08b4629b..000000000
--- a/tests/concepts/t5642.nim
+++ /dev/null
@@ -1,25 +0,0 @@
-discard """
-  output: 9
-"""
-
-type DataTable = concept x
-  x is object
-  for f in fields(x):
-    f is seq
-
-type Students = object
-   id : seq[int]
-   name : seq[string]
-   age: seq[int]
-
-proc nrow*(dt: DataTable) : Natural =
-  var totalLen = 0
-  for f in fields(dt):
-    totalLen += f.len
-  return totalLen
-
-let
-  stud = Students(id : @[1,2,3], name : @["Vas", "Pas", "NafNaf"], age : @[10,16,32])
-
-echo nrow(stud)
-
diff --git a/tests/concepts/t5888.nim b/tests/concepts/t5888.nim
deleted file mode 100644
index dbbab8c4c..000000000
--- a/tests/concepts/t5888.nim
+++ /dev/null
@@ -1,26 +0,0 @@
-discard """
-output: '''
-true
-true
-true
-f
-0
-'''
-"""
-
-import t5888lib/ca, t5888lib/opt
-
-type LocalCA = ca.CA
-
-proc f(c: CA) =
-  echo "f"
-  echo c.x
-
-var o = new(Opt)
-
-echo o is CA
-echo o is LocalCA
-echo o is ca.CA
-
-o.f()
-
diff --git a/tests/concepts/t5968.nim b/tests/concepts/t5968.nim
deleted file mode 100644
index adb374c65..000000000
--- a/tests/concepts/t5968.nim
+++ /dev/null
@@ -1,20 +0,0 @@
-discard """
-  exitcode: 0
-"""
-
-type
-  Enumerable[T] = concept e
-    for it in e:
-      it is T
-
-proc cmap[T, G](e: Enumerable[T], fn: proc(t: T): G): seq[G] =
-  result = @[]
-  for it in e: result.add(fn(it))
-
-import json
-
-var x = %["hello", "world"]
-
-var z = x.cmap(proc(it: JsonNode): string = it.getStr & "!")
-assert z == @["hello!", "world!"]
-
diff --git a/tests/concepts/t5983.nim b/tests/concepts/t5983.nim
deleted file mode 100644
index e69647448..000000000
--- a/tests/concepts/t5983.nim
+++ /dev/null
@@ -1,22 +0,0 @@
-discard """
-  output: "20.0 USD"
-"""
-
-import typetraits
-
-const currencies = ["USD", "EUR"] # in real code 120 currencies
-
-type USD* = distinct float # in real code 120 types generates using macro
-type EUR* = distinct float
-
-type CurrencyAmount = concept c
-  type t = c.type
-  const name = c.type.name
-  name in currencies
-
-proc `$`(x: CurrencyAmount): string =
-  $float(x) & " " & x.name
-
-let amount = 20.USD
-echo amount
-
diff --git a/tests/concepts/t6462.nim b/tests/concepts/t6462.nim
deleted file mode 100644
index 2fa2268f8..000000000
--- a/tests/concepts/t6462.nim
+++ /dev/null
@@ -1,23 +0,0 @@
-discard """
-  output: "true"
-"""
-
-import future
-
-type
-  FilterMixin*[T] = ref object
-    test*:      (T) -> bool
-    trans*:     (T) -> T
-
-  SeqGen*[T] = ref object
-    fil*:     FilterMixin[T]
-  
-  WithFilter[T] = concept a
-    a.fil is FilterMixin[T]
-
-proc test*[T](a: WithFilter[T]): (T) -> bool =
-  a.fil.test
-
-var s = SeqGen[int](fil: FilterMixin[int](test: nil, trans: nil))
-echo s.test() == nil
-
diff --git a/tests/concepts/t6770.nim b/tests/concepts/t6770.nim
deleted file mode 100644
index 1787ee670..000000000
--- a/tests/concepts/t6770.nim
+++ /dev/null
@@ -1,27 +0,0 @@
-discard """
-output: '''
-10
-10
-'''
-"""
-
-type GA = concept c
-  c.a is int
-
-type A = object
-  a: int
-
-type AA = object
-  case exists: bool
-  of true:
-    a: int
-  else:
-    discard
-
-proc print(inp: GA) =
-  echo inp.a
-
-let failing = AA(exists: true, a: 10)
-let working = A(a:10)
-print(working)
-print(failing)
diff --git a/tests/concepts/t8012.nim b/tests/concepts/t8012.nim
new file mode 100644
index 000000000..ec2aa6e5c
--- /dev/null
+++ b/tests/concepts/t8012.nim
@@ -0,0 +1,15 @@
+type
+  MyTypeCon = concept c
+    c.counter is int
+  MyType = object
+    counter: int
+
+proc foo(conc: var MyTypeCon) =
+  conc.counter.inc
+  if conc.counter < 5:
+    foo(conc)
+
+var x: MyType
+
+x.foo
+discard x.repr
diff --git a/tests/concepts/t8558.nim b/tests/concepts/t8558.nim
new file mode 100644
index 000000000..acb2de30e
--- /dev/null
+++ b/tests/concepts/t8558.nim
@@ -0,0 +1,26 @@
+discard """
+  output: '''10
+9
+8
+7
+6
+5
+4
+3
+2
+1
+go!
+'''
+"""
+
+type Integral = concept x
+  x == 0 is bool
+  x - 1 is type(x)
+
+proc countToZero(n: Integral) =
+  if n == 0: echo "go!"
+  else:
+    echo n
+    countToZero(n-1)
+
+countToZero(10)
\ No newline at end of file
diff --git a/tests/concepts/t976.nim b/tests/concepts/t976.nim
index cc0bbdc59..776d53827 100644
--- a/tests/concepts/t976.nim
+++ b/tests/concepts/t976.nim
@@ -1,32 +1,57 @@
-import macros
+discard """
+  output: '''
+Printable
+'''
+joinable: false
+"""
+
+#[
+  The converter is a proper example of a confounding variable
+  Moved to an isolated file
+]#
 
 type
-  int1 = distinct int
-  int2 = distinct int
-
-  int1g = concept x
-    x is int1
-
-  int2g = concept x
-    x is int2
-
-proc take[T: int1g](value: int1) =
-  when T is int2:
-    static: error("killed in take(int1)")
-
-proc take[T: int2g](vale: int2) =
-  when T is int1:
-    static: error("killed in take(int2)")
-
-var i1: int1 = 1.int1
-var i2: int2 = 2.int2
-
-take[int1](i1)
-take[int2](i2)
-
-template reject(e) =
-  static: assert(not compiles(e))
-
-reject take[string](i2)
-reject take[int1](i2)
-
+  Obj1[T] = object
+      v: T
+converter toObj1[T](t: T): Obj1[T] =
+  return Obj1[T](v: t)
+block t976:
+  type
+    int1 = distinct int
+    int2 = distinct int
+    int1g = concept x
+      x is int1
+    int2g = concept x
+      x is int2
+
+  proc take[T: int1g](value: int1) =
+    when T is int2:
+      static: error("killed in take(int1)")
+
+  proc take[T: int2g](vale: int2) =
+    when T is int1:
+      static: error("killed in take(int2)")
+
+  var i1: int1 = 1.int1
+  var i2: int2 = 2.int2
+
+  take[int1](i1)
+  take[int2](i2)
+
+  template reject(e) =
+    static: assert(not compiles(e))
+
+  reject take[string](i2)
+  reject take[int1](i2)
+
+  # bug #6249
+  type
+      Obj2 = ref object
+      PrintAble = concept x
+          $x is string
+
+  proc `$`[T](nt: Obj1[T]): string =
+      when T is PrintAble: result = "Printable"
+      else: result = "Non Printable"
+
+  echo Obj2()
\ No newline at end of file
diff --git a/tests/concepts/tconcepts.nim b/tests/concepts/tconcepts.nim
new file mode 100644
index 000000000..ea3ddc401
--- /dev/null
+++ b/tests/concepts/tconcepts.nim
@@ -0,0 +1,451 @@
+discard """
+output: '''
+10
+20
+int
+20
+3
+x as ParameterizedType[T]
+x as ParameterizedType[T]
+x as ParameterizedType[T]
+x as ParameterizedType
+x as ParameterizedType
+x as CustomTypeClass
+1
+2
+3
+4
+5
+6
+a
+b
+t
+e
+s
+t
+z
+e
+1
+2
+3
+20
+10
+5
+9
+'''
+"""
+
+
+import typetraits, strutils
+
+
+block tcomparable:
+  type
+    Comparable = concept a
+      (a < a) is bool
+
+  proc myMax(a, b: Comparable): Comparable =
+    if a < b:
+      return b
+    else:
+      return a
+
+  doAssert myMax(5, 10) == 10
+  doAssert myMax(31.3, 1.23124) == 31.3
+
+
+
+block tconceptinclosure:
+  type
+    FonConcept = concept x
+      x.x is int
+    GenericConcept[T] = concept x
+      x.x is T
+      const L = T.name.len
+    Implementation = object
+      x: int
+    Closure = object
+      f: proc()
+
+  proc f1(x: FonConcept): Closure =
+    result.f = proc () =
+      echo x.x
+
+  proc f2(x: GenericConcept): Closure =
+    result.f = proc () =
+      echo x.x
+      echo GenericConcept.T.name
+
+  proc f3[T](x: GenericConcept[T]): Closure =
+    result.f = proc () =
+      echo x.x
+      echo x.L
+
+  let x = Implementation(x: 10)
+  let y = Implementation(x: 20)
+
+  let a = x.f1
+  let b = x.f2
+  let c = x.f1
+  let d = y.f2
+  let e = y.f3
+
+  a.f()
+  d.f()
+  e.f()
+
+
+
+block overload_precedence:
+  type ParameterizedType[T] = object
+
+  type CustomTypeClass = concept c
+    true
+
+  # 3 competing procs
+  proc a[T](x: ParameterizedType[T]) =
+    echo "x as ParameterizedType[T]"
+
+  proc a(x: ParameterizedType) =
+    echo "x as ParameterizedType"
+
+  proc a(x: CustomTypeClass) =
+    echo "x as CustomTypeClass"
+
+  # the same procs in different order
+  proc b(x: ParameterizedType) =
+    echo "x as ParameterizedType"
+
+  proc b(x: CustomTypeClass) =
+    echo "x as CustomTypeClass"
+
+  proc b[T](x: ParameterizedType[T]) =
+    echo "x as ParameterizedType[T]"
+
+  # and yet another order
+  proc c(x: CustomTypeClass) =
+    echo "x as CustomTypeClass"
+
+  proc c(x: ParameterizedType) =
+    echo "x as ParameterizedType"
+
+  proc c[T](x: ParameterizedType[T]) =
+    echo "x as ParameterizedType[T]"
+
+  # remove the most specific one
+  proc d(x: ParameterizedType) =
+    echo "x as ParameterizedType"
+
+  proc d(x: CustomTypeClass) =
+    echo "x as CustomTypeClass"
+
+  # then shuffle the order again
+  proc e(x: CustomTypeClass) =
+    echo "x as CustomTypeClass"
+
+  proc e(x: ParameterizedType) =
+    echo "x as ParameterizedType"
+
+  # the least specific one is a match
+  proc f(x: CustomTypeClass) =
+    echo "x as CustomTypeClass"
+
+  a(ParameterizedType[int]())
+  b(ParameterizedType[int]())
+  c(ParameterizedType[int]())
+  d(ParameterizedType[int]())
+  e(ParameterizedType[int]())
+  f(ParameterizedType[int]())
+
+
+
+block templates:
+  template typeLen(x): int = x.type.name.len
+
+  template bunchOfChecks(x) =
+    x.typeLen > 3
+    x != 10 is bool
+
+  template stmtListExprTmpl(x: untyped): untyped =
+    x is int
+    x
+
+  type
+    Obj = object
+      x: int
+
+    Gen[T] = object
+      x: T
+
+    Eq = concept x, y
+      (x == y) is bool
+
+    NotEq = concept x, y
+      (x != y) is bool
+
+    ConceptUsingTemplate1 = concept x
+      echo x
+      sizeof(x) is int
+      bunchOfChecks x
+
+    ConceptUsingTemplate2 = concept x
+      stmtListExprTmpl x
+
+  template ok(x) =
+    static: assert(x)
+
+  template no(x) =
+    static: assert(not(x))
+
+  ok int is Eq
+  ok int is NotEq
+  ok string is Eq
+  ok string is NotEq
+  ok Obj is Eq
+  ok Obj is NotEq
+  ok Gen[string] is Eq
+  ok Gen[int] is NotEq
+
+  no int is ConceptUsingTemplate1
+  ok float is ConceptUsingTemplate1
+  no string is ConceptUsingTemplate1
+
+  ok int is ConceptUsingTemplate2
+  no float is ConceptUsingTemplate2
+  no string is ConceptUsingTemplate2
+
+
+
+block titerable:
+  type
+    Iterable[T] = concept x
+      for value in x:
+        type(value) is T
+
+  proc sum[T](iter: Iterable[T]): T =
+    static: echo T.name
+    for element in iter:
+      static: echo element.type.name
+      result += element
+
+  doAssert sum([1, 2, 3, 4, 5]) == 15
+
+
+
+block tmanual:
+  template accept(e) =
+    static: assert compiles(e)
+
+  template reject(e) =
+    static: assert(not compiles(e))
+
+  type
+    Container[T] = concept c
+      c.len is Ordinal
+      items(c) is T
+      for value in c:
+        type(value) is T
+
+  proc takesIntContainer(c: Container[int]) =
+    for e in c: echo e
+
+  takesIntContainer(@[1, 2, 3])
+  reject takesIntContainer(@["x", "y"])
+
+  proc takesContainer(c: Container) =
+    for e in c: echo e
+
+  takesContainer(@[4, 5, 6])
+  takesContainer(@["a", "b"])
+  takesContainer "test"
+  reject takesContainer(10)
+
+
+
+block modifiers_in_place:
+  type
+    VarContainer[T] = concept c
+      put(var c, T)
+    AltVarContainer[T] = concept var c
+      put(c, T)
+    NonVarContainer[T] = concept c
+      put(c, T)
+    GoodContainer = object
+      x: int
+    BadContainer = object
+      x: int
+
+  proc put(x: BadContainer, y: int) = discard
+  proc put(x: var GoodContainer, y: int) = discard
+
+  template ok(x) = assert(x)
+  template no(x) = assert(not(x))
+
+  static:
+    ok GoodContainer is VarContainer[int]
+    ok GoodContainer is AltVarContainer[int]
+    no BadContainer is VarContainer[int]
+    no BadContainer is AltVarContainer[int]
+    ok GoodContainer is NonVarContainer[int]
+    ok BadContainer is NonVarContainer[int]
+
+
+
+block treversable:
+  type
+    Reversable[T] = concept a
+      a[int] is T
+      a.high is int
+      a.len is int
+      a.low is int
+
+  proc get[T](s: Reversable[T], n: int): T =
+    s[n]
+
+  proc hi[T](s: Reversable[T]): int =
+    s.high
+
+  proc lo[T](s: Reversable[T]): int =
+    s.low
+
+  iterator reverse[T](s: Reversable[T]): T =
+    assert hi(s) - lo(s) == len(s) - 1
+    for z in hi(s).countdown(lo(s)):
+      yield s.get(z)
+
+  for s in @["e", "z"].reverse:
+    echo s
+
+
+
+block tmonoid:
+  type Monoid = concept x, y
+    x + y is type(x)
+    type(z(type(x))) is type(x)
+
+  proc z(x: typedesc[int]): int = 0
+
+  doAssert(int is Monoid)
+
+  # https://github.com/nim-lang/Nim/issues/8126
+  type AdditiveMonoid = concept x, y, type T
+    x + y is T
+
+    # some redundant checks to test an alternative approaches:
+    type TT = type(x)
+    x + y is type(x)
+    x + y is TT
+
+  doAssert(1 is AdditiveMonoid)
+
+
+
+block tesqofconcept:
+  type
+    MyConcept = concept x
+      someProc(x)
+    SomeSeq = seq[MyConcept]
+
+  proc someProc(x:int) = echo x
+
+  proc work (s: SomeSeq) =
+    for item in s:
+      someProc item
+
+  var s = @[1, 2, 3]
+  work s
+
+
+
+block tvectorspace:
+  type VectorSpace[K] = concept x, y
+    x + y is type(x)
+    zero(type(x)) is type(x)
+    -x is type(x)
+    x - y is type(x)
+    var k: K
+    k * x is type(x)
+
+  proc zero(T: typedesc): T = 0
+
+  static:
+    assert float is VectorSpace[float]
+    # assert float is VectorSpace[int]
+    # assert int is VectorSpace
+
+
+
+block tstack:
+  template reject(e) =
+    static: assert(not compiles(e))
+
+  type
+    ArrayStack = object
+      data: seq[int]
+
+  proc push(s: var ArrayStack, item: int) =
+    s.data.add item
+
+  proc pop(s: var ArrayStack): int =
+    return s.data.pop()
+
+  type
+    Stack[T] = concept var s
+      s.push(T)
+      s.pop() is T
+
+      type ValueType = T
+      const ValueTypeName = T.name.toUpperAscii
+
+  proc genericAlgorithm[T](s: var Stack[T], y: T) =
+    static:
+      echo "INFERRED ", T.name
+      echo "VALUE TYPE ", s.ValueType.name
+      echo "VALUE TYPE NAME ", s.ValueTypeName
+
+    s.push(y)
+    echo s.pop
+
+  proc implicitGeneric(s: var Stack): auto =
+    static:
+      echo "IMPLICIT INFERRED ", s.T.name, " ", Stack.T.name
+      echo "IMPLICIT VALUE TYPE ", s.ValueType.name, " ", Stack.ValueType.name
+      echo "IMPLICIT VALUE TYPE NAME ", s.ValueTypeName, " ", Stack.ValueTypeName
+
+    return s.pop()
+
+  var s = ArrayStack(data: @[])
+
+  s.push 10
+  s.genericAlgorithm 20
+  echo s.implicitGeneric
+
+  reject s.genericAlgorithm "x"
+  reject s.genericAlgorithm 1.0
+  reject "str".implicitGeneric
+  reject implicitGeneric(10)
+
+
+
+import libs/[trie_database, trie]
+block ttrie:
+  proc takeDb(d: TrieDatabase) = discard
+  var mdb: MemDB
+  takeDb(mdb)
+
+
+
+import mvarconcept
+block tvar:
+  # bug #2346, bug #2404
+  echo randomInt(5)
+
+block tcomment:
+  type
+    Foo = concept
+      ## Some comment
+      proc bar(x: Self)
+
+  proc bar(x: int) = echo x
+  proc foo(x: Foo) = x.bar
+  foo(9)
diff --git a/tests/concepts/tconcepts_issues.nim b/tests/concepts/tconcepts_issues.nim
new file mode 100644
index 000000000..c6d0267c5
--- /dev/null
+++ b/tests/concepts/tconcepts_issues.nim
@@ -0,0 +1,556 @@
+discard """
+  output: '''
+20.0 USD
+true
+true
+true
+true
+true
+f
+0
+10
+10
+5
+()
+false
+10
+true
+true
+true
+true
+p has been called.
+p has been called.
+implicit generic
+generic
+false
+true
+-1
+Meow
+10 0.0
+1 2.0
+'''
+joinable: false
+"""
+
+import macros, typetraits, os, posix
+
+
+block t5983:
+  const currencies = ["USD", "EUR"] # in real code 120 currencies
+
+  type USD = distinct float # in real code 120 types generates using macro
+  type EUR = distinct float
+
+  type CurrencyAmount = concept c
+    type t = c.type
+    const name = c.type.name
+    name in currencies
+
+  proc `$`(x: CurrencyAmount): string =
+    $float(x) & " " & x.name
+
+  let amount = 20.USD
+  echo amount
+
+
+block t3414:
+  type
+    View[T] = concept v
+      v.empty is bool
+      v.front is T
+      popFront v
+
+  proc find(view: View; target: View.T): View =
+    result = view
+
+    while not result.empty:
+      if view.front == target:
+        return
+
+      mixin popFront
+      popFront result
+
+  proc popFront[T](s: var seq[T]) = discard
+  proc empty[T](s: seq[T]): bool = false
+
+  var s1 = @[1, 2, 3]
+  let s2 = s1.find(10)
+
+
+block t1128:
+  type
+    TFooContainer[T] = object
+
+    TContainer[T] = concept var c
+      foo(c, T)
+
+  proc foo[T](c: var TFooContainer[T], val: T) =
+    discard
+
+  proc bar(c: var TContainer) =
+    discard
+
+  var fooContainer: TFooContainer[int]
+  echo fooContainer is TFooContainer # true.
+  echo fooContainer is TFooContainer[int] # true.
+  fooContainer.bar()
+
+
+
+block t5642:
+  type DataTable = concept x
+    x is object
+    for f in fields(x):
+      f is seq
+
+  type Students = object
+    id : seq[int]
+    name : seq[string]
+    age: seq[int]
+
+  proc nrow(dt: DataTable) : Natural =
+    var totalLen = 0
+    for f in fields(dt):
+      totalLen += f.len
+    return totalLen
+
+  let
+    stud = Students(id: @[1,2,3], name: @["Vas", "Pas", "NafNaf"], age: @[10,16,32])
+
+  doAssert nrow(stud) == 9
+
+
+
+import t5888lib/ca, t5888lib/opt
+block t5888:
+  type LocalCA = ca.CA
+
+  proc f(c: CA) =
+    echo "f"
+    echo c.x
+
+  var o = new(Opt)
+
+  echo o is CA
+  echo o is LocalCA
+  echo o is ca.CA
+
+  o.f()
+
+
+
+import json
+block t5968:
+  type
+    Enumerable[T] = concept e
+      for it in e:
+        it is T
+
+  proc cmap[T, G](e: Enumerable[T], fn: proc(t: T): G): seq[G] =
+    result = @[]
+    for it in e: result.add(fn(it))
+
+  var x = %["hello", "world"]
+
+  var z = x.cmap(proc(it: JsonNode): string = it.getStr & "!")
+  assert z == @["hello!", "world!"]
+
+
+
+import sugar
+block t6462:
+  type
+    FilterMixin[T] = ref object
+      test: (T) -> bool
+      trans: (T) -> T
+
+    SeqGen[T] = ref object
+      fil: FilterMixin[T]
+
+    WithFilter[T] = concept a
+      a.fil is FilterMixin[T]
+
+  proc test[T](a: WithFilter[T]): (T) -> bool =
+    a.fil.test
+
+  var s = SeqGen[int](fil: FilterMixin[int](test: nil, trans: nil))
+  doAssert s.test() == nil
+
+
+
+block t6770:
+  type GA = concept c
+    c.a is int
+
+  type A = object
+    a: int
+
+  type AA = object
+    case exists: bool
+    of true:
+      a: int
+    else:
+      discard
+
+  proc print(inp: GA) =
+    echo inp.a
+
+  let failing = AA(exists: true, a: 10)
+  let working = A(a:10)
+  print(working)
+  print(failing)
+
+
+
+block t7952:
+  type
+    HasLen = concept iter
+      len(iter) is int
+
+  proc echoLen(x: HasLen) =
+    echo len(x)
+
+  echoLen([1, 2, 3, 4, 5])
+
+
+
+block t8280:
+  type
+    Iterable[T] = concept x
+      for elem in x:
+        elem is T
+
+  proc max[A](iter: Iterable[A]): A =
+    discard
+
+  type
+    MyType = object
+
+  echo max(@[MyType()])
+
+
+
+import math
+block t3452:
+  type
+    Node = concept n
+      `==`(n, n) is bool
+    Graph1 = concept g
+      type N = Node
+      distance(g, N, N) is float
+    Graph2 = concept g
+      distance(g, Node, Node) is float
+    Graph3 = concept g
+      var x: Node
+      distance(g, x, x) is float
+    XY = tuple[x, y: int]
+    MyGraph = object
+      points: seq[XY]
+
+  static:
+    assert XY is Node
+
+  proc distance( g: MyGraph, a, b: XY): float =
+    sqrt( pow(float(a.x - b.x), 2) + pow(float(a.y - b.y), 2) )
+
+  static:
+    assert MyGraph is Graph1
+    assert MyGraph is Graph2
+    assert MyGraph is Graph3
+
+
+
+block t6691:
+  type
+    ConceptA = concept c
+    ConceptB = concept c
+        c.myProc(ConceptA)
+    Obj = object
+
+  proc myProc(obj: Obj, x: ConceptA) = discard
+
+  echo Obj is ConceptB
+
+
+
+block t6782:
+  type
+    Reader = concept c
+      c.read(openArray[byte], int, int) is int
+    Rdr = concept c
+      c.rd(openArray[byte], int, int) is int
+
+  type TestFile = object
+
+  proc read(r: TestFile, dest: openArray[byte], offset: int, limit: int): int =
+      result = 0
+  proc rd(r: TestFile, dest: openArray[byte], offset: int, limit: int): int =
+      result = 0
+
+  doAssert TestFile is Reader
+  doAssert TestFile is Rdr
+
+
+
+block t7114:
+  type
+    MyConcept = concept x
+      x.close() # error, doesn't work
+    MyConceptImplementer = object
+
+  proc close(self: MyConceptImplementer) = discard
+  proc takeConcept(window: MyConcept) =
+    discard
+
+  takeConcept(MyConceptImplementer())
+
+
+
+block t7510:
+  type
+    A[T] = concept a
+        a.x is T
+    B[T] = object
+        x: T
+  proc getx(v: A): v.T = v.x
+  var v = B[int32](x: 10)
+  echo v.getx
+
+
+
+block misc_issues:
+  # https://github.com/nim-lang/Nim/issues/1147
+  type TTest = object
+    vals: seq[int]
+
+  proc add(self: var TTest, val: int) =
+    self.vals.add(val)
+
+  type CAddable = concept x
+    x[].add(int)
+
+  echo((ref TTest) is CAddable) # true
+
+  # https://github.com/nim-lang/Nim/issues/1570
+  type ConcretePointOfFloat = object
+    x, y: float
+
+  type ConcretePoint[Value] = object
+    x, y: Value
+
+  type AbstractPointOfFloat = concept p
+    p.x is float and p.y is float
+
+  let p1 = ConcretePointOfFloat(x: 0, y: 0)
+  let p2 = ConcretePoint[float](x: 0, y: 0)
+
+  echo p1 is AbstractPointOfFloat      # true
+  echo p2 is AbstractPointOfFloat      # true
+  echo p2.x is float and p2.y is float # true
+
+  # https://github.com/nim-lang/Nim/issues/2018
+  type ProtocolFollower = concept c
+    true # not a particularly involved protocol
+
+  type ImplementorA = object
+  type ImplementorB = object
+
+  proc p[A: ProtocolFollower, B: ProtocolFollower](a: A, b: B) =
+    echo "p has been called."
+
+  p(ImplementorA(), ImplementorA())
+  p(ImplementorA(), ImplementorB())
+
+  # https://github.com/nim-lang/Nim/issues/2423
+  proc put[T](c: seq[T], x: T) = echo "generic"
+  proc put(c: seq) = echo "implicit generic"
+
+  type
+    Container[T] = concept c
+      put(c)
+      put(c, T)
+
+  proc c1(x: Container) = echo "implicit generic"
+  c1(@[1])
+
+  proc c2[T](x: Container[T]) = echo "generic"
+  c2(@[1])
+
+  # https://github.com/nim-lang/Nim/issues/2882
+  type
+    Paper = object
+      name: string
+
+    Bendable = concept x
+      bend(x is Bendable)
+
+  proc bend(p: Paper): Paper = Paper(name: "bent-" & p.name)
+
+  var paper = Paper(name: "red")
+  echo paper is Bendable
+
+  type
+    A = concept self
+      size(self) is int
+
+    B = object
+
+  proc size(self: B): int =
+    return -1
+
+  proc size(self: A): int =
+    return 0
+
+  let b = B()
+  echo b is A
+  echo b.size()
+
+  # https://github.com/nim-lang/Nim/issues/7125
+  type
+    Thing = concept x
+      x.hello is string
+    Cat = object
+
+  proc hello(d: Cat): string = "Meow"
+
+  proc sayHello(c: Thing) = echo(c.hello)
+
+  # used to be 'var a: Thing = Cat()' but that's not valid Nim code
+  # anyway and will be an error soon.
+  var a: Cat = Cat()
+  a.sayHello()
+
+
+# bug #16897
+
+type
+  Fp[N: static int, T] = object
+    big: array[N, T]
+
+type
+  QuadraticExt* = concept x
+    ## Quadratic Extension concept (like complex)
+    type BaseField = auto
+    x.c0 is BaseField
+    x.c1 is BaseField
+var address = pointer(nil)
+proc prod(r: var QuadraticExt, b: QuadraticExt) =
+  if address == nil:
+    address = addr b
+    prod(r, b)
+  else:
+    assert address == addr b
+
+type
+  Fp2[N: static int, T] {.byref.} = object
+    c0, c1: Fp[N, T]
+
+# This should be passed by reference,
+# but concepts do not respect the 24 bytes rule
+# or `byref` pragma.
+var r, b: Fp2[6, uint64]
+
+prod(r, b)
+
+
+block: # bug #21263
+  type
+    DateDayFraction = concept # no T, an atom
+      proc date(a: Self): int
+      proc fraction(b: Self): float
+    Date = distinct int
+    DateDayFractionImpl = object
+      date : int
+      fraction : float
+
+  proc date(a: Date): int = a.int
+  proc fraction(a:Date): float = 0.0
+
+  proc date(a: DateDayFractionImpl): int = a.date
+  proc fraction(b: DateDayFractionImpl): float = b.fraction
+
+
+  proc print(a: DateDayFraction) =
+    echo a.date, " ", a.fraction
+
+  print(10.Date) # ok
+  print(DateDayFractionImpl(date: 1, fraction: 2))  # error
+
+import sets
+import deques
+
+type AnyTree[V] = concept t, type T
+  for v in t.leaves(V):
+    v is V
+
+type BreadthOrder[V] = ref object
+  frontier: Deque[V]
+  visited: HashSet[V]
+
+proc expand[V, T](order: ref BreadthOrder[T], tree: AnyTree[V], node: V, transform: (V) -> (T)) =
+  for leaf in tree.leaves(node):
+    if not order.visited.containsOrIncl(transform(leaf)):
+      order.frontier.addLast(transform(leaf))
+
+proc hasNext[V](order: ref BreadthOrder[V]): bool =
+  order.frontier.len > 0
+
+proc init[V](_: typedesc[BreadthOrder]): ref BreadthOrder[V] =
+  result.new()
+  result[] = BreadthOrder[V](frontier: initDeque[V](), visited: initHashSet[V]())
+
+proc popNext[V](order: ref BreadthOrder[V]): V =
+  order.frontier.popFirst()
+
+type LevelNode[V] = tuple
+  depth: uint
+  node: V
+
+proc depthOf*[V](orderType: typedesc[BreadthOrder], tree: AnyTree[V], root, goal: V): uint =
+  if root == goal:
+    return 0
+  var order = init[LevelNode[V]](orderType)
+  order.expand(tree, root, (leaf) => (1.uint, leaf))
+  while order.hasNext():
+    let depthNode: LevelNode[V] = order.popNext()
+    if depthNode.node == goal:
+      return depthNode.depth
+    order.expand(tree, depthNode.node, (leaf) => (depthNode.depth + 1, leaf))
+
+type CappedStringTree = ref object
+  symbols: string
+  cap: Natural
+
+iterator leaves*(t: CappedStringTree, s: string): string =
+  if s.len < t.cap:
+    for c in t.symbols:
+      yield s & c
+
+block: # bug #12852
+  var tree = CappedStringTree(symbols: "^v><", cap: 5)
+
+  doAssert BreadthOrder.depthOf(tree, "", ">>>") == 3
+
+block: #bug #22723
+  type
+    Node  = concept n, type T 
+      for i in n.children:
+        i is T
+      n.parent is T
+
+    Nd = ref object
+      parent: Nd
+      children: seq[Nd]
+
+  proc addChild(parent, child: Node) =
+    parent.children.add(child)
+    child.parent = parent
+
+  proc foo =
+    var
+      a = Nd()
+      b = Nd()
+    a.addChild(b)
+    doAssert a.children.len == 1
+
+  foo()
diff --git a/tests/concepts/tconcepts_overload_precedence.nim b/tests/concepts/tconcepts_overload_precedence.nim
index 9eed6256a..c580d2688 100644
--- a/tests/concepts/tconcepts_overload_precedence.nim
+++ b/tests/concepts/tconcepts_overload_precedence.nim
@@ -9,7 +9,7 @@ x as CustomTypeClass'''
 
 type ParameterizedType[T] = object
 
-type CustomTypeClass = concept
+type CustomTypeClass = concept c
   true
 
 # 3 competing procs
diff --git a/tests/concepts/texplain.nim b/tests/concepts/texplain.nim
index 4925a25b3..8cf04ae82 100644
--- a/tests/concepts/texplain.nim
+++ b/tests/concepts/texplain.nim
@@ -1,69 +1,128 @@
 discard """
   cmd: "nim c --verbosity:0 --colors:off $file"
   nimout: '''
-texplain.nim(103, 10) Hint: Non-matching candidates for e(y)
+texplain.nim(162, 10) Hint: Non-matching candidates for e(y)
 proc e(i: int): int
+  first type mismatch at position: 1
+  required type for i: int
+  but expression 'y' is of type: MatchingType
 
-texplain.nim(106, 7) Hint: Non-matching candidates for e(10)
+texplain.nim(165, 7) Hint: Non-matching candidates for e(10)
 proc e(o: ExplainedConcept): int
-texplain.nim(69, 6) ExplainedConcept: undeclared field: 'foo'
-texplain.nim(69, 6) ExplainedConcept: undeclared field: '.'
-texplain.nim(69, 6) ExplainedConcept: expression '.' cannot be called
-texplain.nim(69, 5) ExplainedConcept: concept predicate failed
-texplain.nim(70, 6) ExplainedConcept: undeclared field: 'bar'
-texplain.nim(70, 6) ExplainedConcept: undeclared field: '.'
-texplain.nim(70, 6) ExplainedConcept: expression '.' cannot be called
-texplain.nim(69, 5) ExplainedConcept: concept predicate failed
-
-texplain.nim(109, 10) Hint: Non-matching candidates for e(10)
+  first type mismatch at position: 1
+  required type for o: ExplainedConcept
+  but expression '10' is of type: int literal(10)
+texplain.nim(128, 6) ExplainedConcept: undeclared field: 'foo'
+texplain.nim(128, 5) ExplainedConcept: concept predicate failed
+texplain.nim(129, 6) ExplainedConcept: undeclared field: 'bar'
+texplain.nim(128, 5) ExplainedConcept: concept predicate failed
+
+texplain.nim(168, 10) Hint: Non-matching candidates for e(10)
 proc e(o: ExplainedConcept): int
-texplain.nim(69, 6) ExplainedConcept: undeclared field: 'foo'
-texplain.nim(69, 6) ExplainedConcept: undeclared field: '.'
-texplain.nim(69, 6) ExplainedConcept: expression '.' cannot be called
-texplain.nim(69, 5) ExplainedConcept: concept predicate failed
-texplain.nim(70, 6) ExplainedConcept: undeclared field: 'bar'
-texplain.nim(70, 6) ExplainedConcept: undeclared field: '.'
-texplain.nim(70, 6) ExplainedConcept: expression '.' cannot be called
-texplain.nim(69, 5) ExplainedConcept: concept predicate failed
-
-texplain.nim(113, 20) Error: type mismatch: got <NonMatchingType>
+  first type mismatch at position: 1
+  required type for o: ExplainedConcept
+  but expression '10' is of type: int literal(10)
+texplain.nim(128, 6) ExplainedConcept: undeclared field: 'foo'
+texplain.nim(128, 5) ExplainedConcept: concept predicate failed
+texplain.nim(129, 6) ExplainedConcept: undeclared field: 'bar'
+texplain.nim(128, 5) ExplainedConcept: concept predicate failed
+
+texplain.nim(172, 20) Error: type mismatch: got <NonMatchingType>
 but expected one of:
-proc e(o: ExplainedConcept): int
-texplain.nim(69, 5) ExplainedConcept: concept predicate failed
 proc e(i: int): int
+  first type mismatch at position: 1
+  required type for i: int
+  but expression 'n' is of type: NonMatchingType
+proc e(o: ExplainedConcept): int
+  first type mismatch at position: 1
+  required type for o: ExplainedConcept
+  but expression 'n' is of type: NonMatchingType
+texplain.nim(172, 9) template/generic instantiation of `assert` from here
+texplain.nim(128, 5) ExplainedConcept: concept predicate failed
 
 expression: e(n)
-texplain.nim(114, 20) Error: type mismatch: got <NonMatchingType>
+texplain.nim(173, 20) Error: type mismatch: got <NonMatchingType>
 but expected one of:
+proc r(i: string): int
+  first type mismatch at position: 1
+  required type for i: string
+  but expression 'n' is of type: NonMatchingType
 proc r(o: RegularConcept): int
-texplain.nim(73, 5) RegularConcept: concept predicate failed
+  first type mismatch at position: 1
+  required type for o: RegularConcept
+  but expression 'n' is of type: NonMatchingType
+texplain.nim(173, 9) template/generic instantiation of `assert` from here
+texplain.nim(132, 5) RegularConcept: concept predicate failed
 proc r[T](a: SomeNumber; b: T; c: auto)
-proc r(i: string): int
+  first type mismatch at position: 1
+  required type for a: SomeNumber
+  but expression 'n' is of type: NonMatchingType
 
 expression: r(n)
-texplain.nim(115, 20) Hint: Non-matching candidates for r(y)
-proc r[T](a: SomeNumber; b: T; c: auto)
+texplain.nim(174, 20) Hint: Non-matching candidates for r(y)
 proc r(i: string): int
+  first type mismatch at position: 1
+  required type for i: string
+  but expression 'y' is of type: MatchingType
+proc r[T](a: SomeNumber; b: T; c: auto)
+  first type mismatch at position: 1
+  required type for a: SomeNumber
+  but expression 'y' is of type: MatchingType
 
-texplain.nim(123, 2) Error: type mismatch: got <MatchingType>
+texplain.nim(182, 2) Error: type mismatch: got <MatchingType>
 but expected one of:
 proc f(o: NestedConcept)
-texplain.nim(73, 6) RegularConcept: undeclared field: 'foo'
-texplain.nim(73, 6) RegularConcept: undeclared field: '.'
-texplain.nim(73, 6) RegularConcept: expression '.' cannot be called
-texplain.nim(73, 5) RegularConcept: concept predicate failed
-texplain.nim(74, 6) RegularConcept: undeclared field: 'bar'
-texplain.nim(74, 6) RegularConcept: undeclared field: '.'
-texplain.nim(74, 6) RegularConcept: expression '.' cannot be called
-texplain.nim(73, 5) RegularConcept: concept predicate failed
-texplain.nim(77, 5) NestedConcept: concept predicate failed
-
-expression: f(y)
-'''
-  line: 123
+  first type mismatch at position: 1
+  required type for o: NestedConcept
+  but expression 'y' is of type: MatchingType
+texplain.nim(132, 6) RegularConcept: undeclared field: 'foo'
+texplain.nim(132, 5) RegularConcept: concept predicate failed
+texplain.nim(133, 6) RegularConcept: undeclared field: 'bar'
+texplain.nim(132, 5) RegularConcept: concept predicate failed
+texplain.nim(136, 5) NestedConcept: concept predicate failed
+
+expression: f(y)'''
   errormsg: "type mismatch: got <MatchingType>"
 """
 
+
+
+# proc r[T](a: SomeNumber; b: T; c: auto)
+# proc r(i: string): int
+# proc r(o: RegularConcept): int
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# line 124 HERE
+
 type
   ExplainedConcept {.explain.} = concept o
     o.foo is int
@@ -121,4 +180,3 @@ static:
 # finally, provide multiple nested explanations for failed matching
 # of regular concepts, even when the explain pragma is not used
 f(y)
-
diff --git a/tests/concepts/tgraph.nim b/tests/concepts/tgraph.nim
deleted file mode 100644
index 985f04a61..000000000
--- a/tests/concepts/tgraph.nim
+++ /dev/null
@@ -1,34 +0,0 @@
-# bug #3452
-import math
-
-type
-  Node* = concept n
-    `==`(n, n) is bool
-
-  Graph1* = concept g
-    type N = Node
-    distance(g, N, N) is float
-
-  Graph2 = concept g
-    distance(g, Node, Node) is float
-
-  Graph3 = concept g
-    var x: Node
-    distance(g, x, x) is float
-
-  XY* = tuple[x, y: int]
-
-  MyGraph* = object
-    points: seq[XY]
-
-static:
-  assert XY is Node
-
-proc distance*( g: MyGraph, a, b: XY): float =
-  sqrt( pow(float(a.x - b.x), 2) + pow(float(a.y - b.y), 2) )
-
-static:
-  assert MyGraph is Graph1
-  assert MyGraph is Graph2
-  assert MyGraph is Graph3
-
diff --git a/tests/concepts/tinfrecursion.nim b/tests/concepts/tinfrecursion.nim
deleted file mode 100644
index 60db410de..000000000
--- a/tests/concepts/tinfrecursion.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-
-# bug #6691
-type
-  ConceptA = concept c
-
-  ConceptB = concept c
-      c.myProc(ConceptA)
-
-  Obj = object
-
-proc myProc(obj: Obj, x: ConceptA) = discard
-
-echo Obj is ConceptB
diff --git a/tests/concepts/tmapconcept.nim b/tests/concepts/tmapconcept.nim
index 5082fcb61..cc99737df 100644
--- a/tests/concepts/tmapconcept.nim
+++ b/tests/concepts/tmapconcept.nim
@@ -3,7 +3,7 @@ output: '''10
 10
 
 1'''
-msg: '''
+nimout: '''
 K=string V=int
 K=int64 V=string
 K=int V=int
@@ -78,7 +78,7 @@ proc getFirstValue[K,V](m : Map[K,V]): V =
   for i in m.valuesSeq:
     return i
 
-  raise newException(RangeError, "no values")
+  raise newException(RangeDefect, "no values")
 
 proc useConceptProcInGeneric[K, V](t: Table[K, V]): V =
   return t.getFirstValue
diff --git a/tests/concepts/tmatrixconcept.nim b/tests/concepts/tmatrixconcept.nim
index d2597a212..ca31f5942 100644
--- a/tests/concepts/tmatrixconcept.nim
+++ b/tests/concepts/tmatrixconcept.nim
@@ -1,6 +1,6 @@
 discard """
 output: "0\n0\n0"
-msg: '''
+nimout: '''
 R=3 C=3 TE=9 FF=14 FC=20 T=int
 R=3 C=3 T=int
 '''
@@ -32,7 +32,7 @@ type
     data: array[M*K, T]
 
 # adaptor for the concept's non-matching expectations
-template N(M: type MyMatrix): expr = M.K
+template N(M: type MyMatrix): untyped = M.K
 
 proc `[]`(m: MyMatrix; r, c: int): m.T =
   m.data[r * m.K + c]
@@ -45,7 +45,7 @@ proc foo(x: MyMatrix, arr: array[15, x.T]) = discard
 proc genericMatrixProc[R, C, TE, FF, FC, T](m: Matrix[R, C, TE, FF, FC, T]): T =
   static:
     echo "R=", R, " C=", C, " TE=", TE, " FF=", FF, " FC=", FC, " T=", T.name
-  
+
   m[0, 0]
 
 proc implicitMatrixProc(m: Matrix): m.T =
@@ -57,7 +57,7 @@ proc implicitMatrixProc(m: Matrix): m.T =
         #" FF=", m.FromFoo,
         #" FC=", m.FromConst,
         " T=", m.T.name
-    
+
   m[0, 0]
 
 proc myMatrixProc(x: MyMatrix): MyMatrix.T = genericMatrixProc(x)
diff --git a/tests/concepts/tmisc_issues.nim b/tests/concepts/tmisc_issues.nim
deleted file mode 100644
index 662eba380..000000000
--- a/tests/concepts/tmisc_issues.nim
+++ /dev/null
@@ -1,100 +0,0 @@
-discard """
-output: '''true
-true
-true
-true
-p has been called.
-p has been called.
-implicit generic
-generic
-false
-true
--1'''
-"""
-
-# https://github.com/nim-lang/Nim/issues/1147
-type TTest = object
-  vals: seq[int]
-
-proc add*(self: var TTest, val: int) =
-  self.vals.add(val)
-
-type CAddable = concept x
-  x[].add(int)
-
-echo((ref TTest) is CAddable) # true
-
-# https://github.com/nim-lang/Nim/issues/1570
-type ConcretePointOfFloat = object
-  x, y: float
-
-type ConcretePoint[Value] = object
-  x, y: Value
-
-type AbstractPointOfFloat = concept p
-  p.x is float and p.y is float
-
-let p1 = ConcretePointOfFloat(x: 0, y: 0)
-let p2 = ConcretePoint[float](x: 0, y: 0)
-
-echo p1 is AbstractPointOfFloat      # true
-echo p2 is AbstractPointOfFloat      # true
-echo p2.x is float and p2.y is float # true
-
-# https://github.com/nim-lang/Nim/issues/2018
-type ProtocolFollower = concept
-  true # not a particularly involved protocol
-
-type ImplementorA = object
-type ImplementorB = object
-
-proc p[A: ProtocolFollower, B: ProtocolFollower](a: A, b: B) =
-  echo "p has been called."
-
-p(ImplementorA(), ImplementorA())
-p(ImplementorA(), ImplementorB())
-
-# https://github.com/nim-lang/Nim/issues/2423
-proc put*[T](c: seq[T], x: T) = echo "generic"
-proc put*(c: seq) = echo "implicit generic"
-
-type
-  Container[T] = concept c
-    put(c)
-    put(c, T)
-
-proc c1(x: Container) = echo "implicit generic"
-c1(@[1])
-
-proc c2[T](x: Container[T]) = echo "generic"
-c2(@[1])
-
-# https://github.com/nim-lang/Nim/issues/2882
-type
-  Paper = object
-    name: string
-
-  Bendable = concept x
-    bend(x is Bendable)
-
-proc bend(p: Paper): Paper = Paper(name: "bent-" & p.name)
-
-var paper = Paper(name: "red")
-echo paper is Bendable
-
-type
-  A = concept self
-    size(self) is int
-
-  B = object
-
-proc size(self: B): int =
-  return -1
-
-proc size(self: A): int =
-  return 0
-
-let b = B()
-echo b is A
-echo b.size()
-
diff --git a/tests/concepts/tmonoid.nim b/tests/concepts/tmonoid.nim
index 49b3239bd..e0e19adbc 100644
--- a/tests/concepts/tmonoid.nim
+++ b/tests/concepts/tmonoid.nim
@@ -11,3 +11,15 @@ type Monoid = concept x, y
 proc z(x: typedesc[int]): int = 0
 
 echo(int is Monoid)
+
+# https://github.com/nim-lang/Nim/issues/8126
+type AdditiveMonoid* = concept x, y, type T
+  x + y is T
+
+  # some redundant checks to test an alternative approaches:
+  type TT = type(x)
+  x + y is type(x)
+  x + y is TT
+
+doAssert(1 is AdditiveMonoid)
+
diff --git a/tests/concepts/trandom_vars.nim b/tests/concepts/trandom_vars.nim
deleted file mode 100644
index a236cebad..000000000
--- a/tests/concepts/trandom_vars.nim
+++ /dev/null
@@ -1,42 +0,0 @@
-discard """
-output: "11.0"
-"""
-
-type
-  # A random number generator
-  Random = object
-    random: proc(): float
-  # A generic typeclass for a random var
-  RandomVar[A] = concept x
-    var rng: Random
-    rng.sample(x) is A
-  # A few concrete instances
-  Uniform = object
-    a, b: float
-  ClosureVar[A] = object
-    f: proc(rng: var Random): A
-
-# How to sample from various concrete instances
-proc sample(rng: var Random, u: Uniform): float = u.a + (u.b - u.a) * rng.random()
-
-proc sample[A](rng: var Random, c: ClosureVar[A]): A = c.f(rng)
-
-proc uniform(a, b: float): Uniform = Uniform(a: a, b: b)
-
-# How to lift a function on values to a function on random variables
-proc map[A, B](x: RandomVar[A], f: proc(a: A): B): ClosureVar[B] =
-  proc inner(rng: var Random): B =
-    f(rng.sample(x))
-
-  result.f = inner
-
-import future
-
-proc fakeRandom(): Random =
-  result.random = () => 0.5
-
-let x = uniform(1, 10).map((x: float) => 2 * x)
-
-var rng = fakeRandom()
-
-echo rng.sample(x)
diff --git a/tests/concepts/trandomvars.nim b/tests/concepts/trandomvars.nim
index db41aa901..1f04b9ecf 100644
--- a/tests/concepts/trandomvars.nim
+++ b/tests/concepts/trandomvars.nim
@@ -41,7 +41,8 @@ proc lift1[A, B](f: proc(a: A): B, r: RandomVar[A]): ClosureVar[B] =
 
   return inner
 
-when isMainModule:
+
+proc main() =
   proc sq(x: float): float = x * x
 
   let
@@ -59,3 +60,4 @@ when isMainModule:
   echo rng.sample(u)
   echo rng.sample(t)
 
+main()
diff --git a/tests/concepts/trandomvars2.nim b/tests/concepts/trandomvars2.nim
new file mode 100644
index 000000000..861e876a7
--- /dev/null
+++ b/tests/concepts/trandomvars2.nim
@@ -0,0 +1,42 @@
+discard """
+output: "11.0"
+"""
+
+type
+  # A random number generator
+  Random = object
+    random: proc(): float
+  # A generic typeclass for a random var
+  RandomVar[A] = concept x
+    var rng: Random
+    rng.sample(x) is A
+  # A few concrete instances
+  Uniform = object
+    a, b: float
+  ClosureVar[A] = object
+    f: proc(rng: var Random): A
+
+# How to sample from various concrete instances
+proc sample(rng: var Random, u: Uniform): float = u.a + (u.b - u.a) * rng.random()
+
+proc sample[A](rng: var Random, c: ClosureVar[A]): A = c.f(rng)
+
+proc uniform(a, b: float): Uniform = Uniform(a: a, b: b)
+
+# How to lift a function on values to a function on random variables
+proc map[A, B](x: RandomVar[A], f: proc(a: A): B): ClosureVar[B] =
+  proc inner(rng: var Random): B =
+    f(rng.sample(x))
+
+  result.f = inner
+
+import sugar
+
+proc fakeRandom(): Random =
+  result.random = () => 0.5
+
+let x = uniform(1, 10).map((x: float) => 2 * x)
+
+var rng = fakeRandom()
+
+echo rng.sample(x)
diff --git a/tests/concepts/treversable.nim b/tests/concepts/treversable.nim
new file mode 100644
index 000000000..d30ba0a3c
--- /dev/null
+++ b/tests/concepts/treversable.nim
@@ -0,0 +1,31 @@
+# issue 7705, 7703, 7702
+discard """
+  output: '''
+z
+e
+'''
+"""
+
+type
+  Reversable*[T] = concept a
+    a[int] is T
+    a.high is int
+    a.len is int
+    a.low is int
+
+proc get[T](s: Reversable[T], n: int): T =
+  s[n]
+
+proc hi[T](s: Reversable[T]): int =
+  s.high
+
+proc lo[T](s: Reversable[T]): int =
+  s.low
+
+iterator reverse*[T](s: Reversable[T]): T =
+  assert hi(s) - lo(s) == len(s) - 1
+  for z in hi(s).countdown(lo(s)):
+    yield s.get(z)
+
+for s in @["e", "z"].reverse:
+  echo s
diff --git a/tests/concepts/tspec.nim b/tests/concepts/tspec.nim
new file mode 100644
index 000000000..52f13a40a
--- /dev/null
+++ b/tests/concepts/tspec.nim
@@ -0,0 +1,105 @@
+discard """
+  output: '''4
+0
+4
+4
+1
+2
+3
+yes int
+string int'''
+  joinable: false
+"""
+
+import hashes
+
+type
+  Comparable = concept # no T, an atom
+    proc cmp(a, b: Self): int
+
+  ToStringable = concept
+    proc `$`(a: Self): string
+
+  Hashable = concept   ## the most basic of identity assumptions
+    proc hash(x: Self): int
+    proc `==`(x, y: Self): bool
+
+  Swapable = concept
+    proc swap(x, y: var Self)
+
+
+proc h(x: Hashable) =
+  echo x
+
+h(4)
+
+when true:
+  proc compare(a: Comparable) =
+    echo cmp(a, a)
+
+  compare(4)
+
+proc dollar(x: ToStringable) =
+  echo x
+
+when true:
+  dollar 4
+  dollar "4"
+
+#type D = distinct int
+
+#dollar D(4)
+
+when true:
+  type
+    Iterable[Ix] = concept
+      iterator items(c: Self): Ix
+
+  proc g[Tu](it: Iterable[Tu]) =
+    for x in it:
+      echo x
+
+  g(@[1, 2, 3])
+
+proc hs(x: Swapable) =
+  var y = x
+  swap y, y
+
+hs(4)
+
+type
+  Indexable[T] = concept # has a T, a collection
+    proc `[]`(a: Self; index: int): T # we need to describe how to infer 'T'
+    # and then we can use the 'T' and it must match:
+    proc `[]=`(a: var Self; index: int; value: T)
+    proc len(a: Self): int
+
+proc indexOf[T](a: Indexable[T]; value: T) =
+  echo "yes ", T
+
+block:
+  var x = @[1, 2, 3]
+  indexOf(x, 4)
+
+import tables, typetraits
+
+type
+  Dict[K, V] = concept
+    proc `[]`(s: Self; k: K): V
+    proc `[]=`(s: var Self; k: K; v: V)
+
+proc d[K2, V2](x: Dict[K2, V2]) =
+  echo K2, " ", V2
+
+var x = initTable[string, int]()
+d(x)
+
+
+type Monoid = concept
+  proc `+`(x, y: Self): Self
+  proc z(t: typedesc[Self]): Self
+
+proc z(x: typedesc[int]): int = 0
+
+doAssert int is Monoid
+
diff --git a/tests/concepts/tstackconcept.nim b/tests/concepts/tstackconcept.nim
index cb8db566d..fb6032732 100644
--- a/tests/concepts/tstackconcept.nim
+++ b/tests/concepts/tstackconcept.nim
@@ -1,6 +1,6 @@
 discard """
 output: "20\n10"
-msg: '''
+nimout: '''
 INFERRED int
 VALUE TYPE int
 VALUE TYPE NAME INT
diff --git a/tests/concepts/ttrieconcept.nim b/tests/concepts/ttrieconcept.nim
new file mode 100644
index 000000000..a26e6b146
--- /dev/null
+++ b/tests/concepts/ttrieconcept.nim
@@ -0,0 +1,7 @@
+import libs/[trie_database, trie]
+
+proc takeDb(d: TrieDatabase) = discard
+var mdb: MemDB
+
+takeDb(mdb)
+
diff --git a/tests/concepts/tusertypeclasses.nim b/tests/concepts/tusertypeclasses.nim
index 533bd528d..83e2b176e 100644
--- a/tests/concepts/tusertypeclasses.nim
+++ b/tests/concepts/tusertypeclasses.nim
@@ -1,12 +1,17 @@
 discard """
+  matrix: "--mm:refc"
   output: '''Sortable
 Sortable
 Container
 TObj
 int
+111 111
+(id: @[1, 2, 3], name: @["Vas", "Pas", "NafNaf"], age: @[10, 16, 18])
 '''
 """
 
+# todo wait for https://github.com/nim-lang/Nim/pull/20380
+
 import typetraits
 
 template reject(expr) = assert(not compiles(x))
@@ -25,7 +30,7 @@ type
     C.len is Ordinal
     for v in items(C):
       v.type is tuple|object
-   
+
 proc foo(c: ObjectContainer) =
   echo "Container"
 
@@ -94,6 +99,33 @@ proc to(x: TObj, t: type JSonValue) = discard
 proc testFoo(x: TFoo) =
   echo x.TypeName
   echo x.MappedType.name
-  
+
 testFoo(TObj(x: 10))
 
+# bug #7092
+
+type stringTest = concept x
+  x is string
+
+let usedToFail: stringTest = "111"
+let working: string = "111"
+
+echo usedToFail, " ", working
+
+# bug #5868
+
+type TaggedType[T; Key: static[string]] = T
+
+proc setKey*[DT](dt: DT, key: static[string]): TaggedType[DT, key] =
+  result = cast[type(result)](dt)
+
+type Students = object
+   id : seq[int]
+   name : seq[string]
+   age: seq[int]
+
+let
+  stud = Students(id : @[1,2,3], name : @["Vas", "Pas", "NafNaf"], age : @[10,16,18])
+  stud2 = stud.setkey("id")
+
+echo stud2
diff --git a/tests/concepts/tusertypeclasses2.nim b/tests/concepts/tusertypeclasses2.nim
index ae05540cd..6132bc2d8 100644
--- a/tests/concepts/tusertypeclasses2.nim
+++ b/tests/concepts/tusertypeclasses2.nim
@@ -1,24 +1,63 @@
-type
-  hasFieldX = concept z
-    z.x is int
+discard """
+  targets: "c js"
+"""
 
-  obj_x = object
-    x: int
+block:
+  type
+    hasFieldX = concept z
+      z.x is int
 
-  ref_obj_x = ref object
-    x: int
+    obj_x = object
+      x: int
 
-  ref_to_obj_x = ref obj_x
+    ref_obj_x = ref object
+      x: int
 
-  p_o_x = ptr obj_x
-  v_o_x = var obj_x
+    ref_to_obj_x = ref obj_x
 
-template check(x) =
-  static: assert(x)
+    p_o_x = ptr obj_x
+    v_o_x = var obj_x
 
-check obj_x is hasFieldX
-check ref_obj_x is hasFieldX
-check ref_to_obj_x is hasFieldX
-check p_o_x is hasFieldX
-check v_o_x is hasFieldX
+  template check(x) =
+    static: assert(x)
 
+  check obj_x is hasFieldX
+  check ref_obj_x is hasFieldX
+  check ref_to_obj_x is hasFieldX
+  check p_o_x is hasFieldX
+  check v_o_x is hasFieldX
+
+block:
+  type
+    Foo = concept x
+      x.isFoo
+    Bar = distinct float
+  template isFoo(x: Bar): untyped = true
+  proc foo(x: var Foo) =
+    float(x) = 1.0
+  proc foo2(x: var Bar) =
+    float(x) = 1.0
+  proc foo3(x: var (Bar|SomeNumber)) =
+    float(x) = 1.0
+  proc foo4(x: var any) =
+    float(x) = 1.0
+  var x: Bar
+  foo(x)
+  foo2(x)
+  foo3(x)
+  foo4(x)
+
+block: # bug #9550
+  block:
+    type Foo = concept c
+      for v in c: (v is char)
+
+    func foo(c: Foo) = (for v in c: discard)
+    foo @['a', 'b' ,'c']
+
+  block:
+    type Foo = concept c
+      for v in c: (v is char)
+
+    func foo(c: Foo) = (for v in c: discard)
+    foo ['a', 'b' ,'c']
diff --git a/tests/concepts/tvectorspace.nim b/tests/concepts/tvectorspace.nim
index 74423e0d2..7a18c1762 100644
--- a/tests/concepts/tvectorspace.nim
+++ b/tests/concepts/tvectorspace.nim
@@ -1,3 +1,7 @@
+discard """
+  joinable: false
+"""
+
 type VectorSpace[K] = concept x, y
   x + y is type(x)
   zero(type(x)) is type(x)
diff --git a/tests/concepts/twrapconcept.nim b/tests/concepts/twrapconcept.nim
index 377b63afe..c3dea2ff9 100644
--- a/tests/concepts/twrapconcept.nim
+++ b/tests/concepts/twrapconcept.nim
@@ -1,7 +1,6 @@
 discard """
   errormsg: "type mismatch: got <string>"
-  line: 21
-  nimout: "twrapconcept.nim(11, 5) Foo: concept predicate failed"
+  nimout: "twrapconcept.nim(10, 5) Foo: concept predicate failed"
 """
 
 # https://github.com/nim-lang/Nim/issues/5127
diff --git a/tests/config.nims b/tests/config.nims
new file mode 100644
index 000000000..0b2b66d81
--- /dev/null
+++ b/tests/config.nims
@@ -0,0 +1,49 @@
+switch("path", "$lib/../testament/lib")
+  # so we can `import stdtest/foo` inside tests
+  # Using $lib/../ instead of $nim/ so you can use a different nim to run tests
+  # during local testing, e.g. nim --lib:lib.
+
+## prevent common user config settings to interfere with testament expectations
+## Indifidual tests can override this if needed to test for these options.
+switch("colors", "off")
+
+switch("excessiveStackTrace", "off")
+
+when (NimMajor, NimMinor, NimPatch) >= (1,5,1):
+  # to make it easier to test against older nim versions, (best effort only)
+  switch("filenames", "legacyRelProj")
+  switch("spellSuggest", "0")
+
+# for std/unittest
+switch("define", "nimUnittestOutputLevel:PRINT_FAILURES")
+switch("define", "nimUnittestColor:off")
+
+switch("define", "nimLegacyTypeMismatch")
+
+hint("Processing", off)
+  # dots can cause annoyances; instead, a single test can test `hintProcessing`
+
+# uncomment to enable all flaky tests disabled by this flag
+# (works through process calls, e.g. tests that invoke nim).
+# switch("define", "nimTestsEnableFlaky")
+
+# switch("hint", "ConvFromXtoItselfNotNeeded")
+# switch("warningAsError", "InheritFromException") # would require fixing a few tests
+
+# experimental APIs are enabled in testament, refs https://github.com/timotheecour/Nim/issues/575
+# sync with `kochdocs.docDefines` or refactor.
+switch("define", "nimExperimentalLinenoiseExtra")
+
+# preview APIs are expected to be the new default in upcoming versions
+switch("define", "nimPreviewFloatRoundtrip")
+#switch("define", "nimPreviewDotLikeOps") # deprecated?
+switch("define", "nimPreviewJsonutilsHoleyEnum")
+switch("define", "nimPreviewHashRef")
+switch("define", "nimPreviewRangeDefault")
+switch("define", "nimPreviewNonVarDestructor")
+
+switch("warningAserror", "UnnamedBreak")
+when not defined(testsConciseTypeMismatch):
+  switch("legacy", "verboseTypeMismatch")
+switch("experimental", "vtables")
+switch("experimental", "openSym")
diff --git a/tests/constraints/tconstraints.nim b/tests/constraints/tconstraints.nim
deleted file mode 100644
index 3c9fdc354..000000000
--- a/tests/constraints/tconstraints.nim
+++ /dev/null
@@ -1,18 +0,0 @@
-discard """
-  line: 16
-  errormsg: "type mismatch: got <int literal(232)>"
-"""
-
-proc myGenericProc[T: object|tuple|ptr|ref|distinct](x: T): string =
-  result = $x
-
-type
-  TMyObj = tuple[x, y: int]
-
-var
-  x: TMyObj
-
-assert myGenericProc(x) == "(x: 0, y: 0)"
-assert myGenericProc(232) == "232"
-
-
diff --git a/tests/constructors/a.nim b/tests/constructors/a.nim
new file mode 100644
index 000000000..03788fc57
--- /dev/null
+++ b/tests/constructors/a.nim
@@ -0,0 +1,2 @@
+type A* = object
+  a: uint8
\ No newline at end of file
diff --git a/tests/constructors/b.nim b/tests/constructors/b.nim
new file mode 100644
index 000000000..437dd0550
--- /dev/null
+++ b/tests/constructors/b.nim
@@ -0,0 +1,2 @@
+type B* = object
+proc A*(a, b: float): B = discard
\ No newline at end of file
diff --git a/tests/constructors/t18990.nim b/tests/constructors/t18990.nim
new file mode 100644
index 000000000..2f60f3c2c
--- /dev/null
+++ b/tests/constructors/t18990.nim
@@ -0,0 +1,3 @@
+import a, b
+discard A(1f, 1f) # works
+proc x(b = A(1f, 1f)) = discard # doesn't work
\ No newline at end of file
diff --git a/tests/constructors/t5965_1.nim b/tests/constructors/t5965_1.nim
index 9f947f859..abf07b21c 100644
--- a/tests/constructors/t5965_1.nim
+++ b/tests/constructors/t5965_1.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "incorrect object construction syntax"
   file: "t5965_1.nim"
   line: 10
-  errormsg: "incorrect object construction syntax"
 """
 
 type Foo = object
diff --git a/tests/constructors/t5965_2.nim b/tests/constructors/t5965_2.nim
index a3f7174c9..e04f1b715 100644
--- a/tests/constructors/t5965_2.nim
+++ b/tests/constructors/t5965_2.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "incorrect object construction syntax"
   file: "t5965_2.nim"
   line: 10
-  errormsg: "incorrect object construction syntax"
 """
 
 type Foo = object
diff --git a/tests/constr/tconstr1.nim b/tests/constructors/tconstr1.nim
index b9cf5d00b..a169bf453 100644
--- a/tests/constr/tconstr1.nim
+++ b/tests/constructors/tconstr1.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "type mismatch"
   file: "tconstr1.nim"
   line: 25
-  errormsg: "type mismatch"
 """
 # Test array, record constructors
 
@@ -26,5 +26,3 @@ const
   otherThings = [  # the same
     (s: "hi", x: 69, y: 45, z: 0.0, chars: {'a', 'b', 'c'}),
     (s: "hi", x: 69, y: 45, z: 1.0, chars: {'a'})]
-
-
diff --git a/tests/constr/tconstr2.nim b/tests/constructors/tconstr2.nim
index b16be6c50..2557d7db9 100644
--- a/tests/constr/tconstr2.nim
+++ b/tests/constructors/tconstr2.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tconstr2.nim"
   output: "69"
 """
 # Test array, record constructors
@@ -19,8 +18,5 @@ const
     (s: "hi", x: 69, y: 45, z: 0.0, chars: {'a', 'b', 'c'}),
     (s: "hi", x: 69, y: 45, z: 1.0, chars: {'a'})]
 
-write(stdout, things[0].x)
+writeLine(stdout, things[0].x)
 #OUT 69
-
-
-
diff --git a/tests/constructors/tinvalid_construction.nim b/tests/constructors/tinvalid_construction.nim
index b3e56eec6..4b372d68a 100644
--- a/tests/constructors/tinvalid_construction.nim
+++ b/tests/constructors/tinvalid_construction.nim
@@ -3,11 +3,16 @@ template accept(x) =
 
 template reject(x) =
   static: assert(not compiles(x))
+
 {.experimental: "notnil".}
+
 type
   TRefObj = ref object
     x: int
 
+  IllegalToConstruct = object
+    x: cstring not nil
+
   THasNotNils = object of RootObj
     a: TRefObj not nil
     b: TRefObj not nil
@@ -15,6 +20,8 @@ type
 
   THasNotNilsRef = ref THasNotNils
 
+  TRefObjNotNil = TRefObj not nil
+
   TChoice = enum A, B, C, D, E, F
 
   TBaseHasNotNils = object of THasNotNils
@@ -26,6 +33,22 @@ type
     else:
       discard
 
+  PartialRequiresInit = object
+    a {.requiresInit.}: int
+    b: string
+
+  PartialRequiresInitRef = ref PartialRequiresInit
+
+  FullRequiresInit {.requiresInit.} = object
+    a: int
+    b: int
+
+  FullRequiresInitRef = ref FullRequiresInit
+
+  FullRequiresInitWithParent {.requiresInit.} = object of THasNotNils
+    e: int
+    d: int
+
   TObj = object
     case choice: TChoice
     of A:
@@ -55,11 +78,23 @@ type
 
 var x = D
 var nilRef: TRefObj
-var notNilRef = TRefObj(x: 20)
+let notNilRef = TRefObjNotNil(x: 20)
 
 proc makeHasNotNils: ref THasNotNils =
-  result.a = TRefObj(x: 10)
-  result.b = TRefObj(x: 20)
+  (ref THasNotNils)(a: TRefObj(x: 10),
+                    b: TRefObj(x: 20))
+
+proc userDefinedDefault(T: typedesc): T =
+  # We'll use that to make sure the user cannot cheat
+  # with constructing requiresInit types
+  discard
+
+proc genericDefault(T: typedesc): T =
+  result = default(T)
+
+reject IllegalToConstruct()
+reject:
+  var x: IllegalToConstruct
 
 accept TObj()
 accept TObj(choice: A)
@@ -74,14 +109,22 @@ reject TObj(a: 10, f: "")       # conflicting fields
 accept TObj(choice: E, e1: TRefObj(x: 10), e2: 10)
 
 accept THasNotNils(a: notNilRef, b: notNilRef, c: nilRef)
-# XXX: the "not nil" logic in the compiler is not strong enough to catch this one yet:
-# reject THasNotNils(a: notNilRef, b: nilRef, c: nilRef)
+reject THasNotNils(a: notNilRef, b: nilRef, c: nilRef)      # `b` shouldn't be nil
 reject THasNotNils(b: notNilRef, c: notNilRef)              # there is a missing not nil field
 reject THasNotNils()                                        # again, missing fields
 accept THasNotNils(a: notNilRef, b: notNilRef)              # it's OK to omit a non-mandatory field
+# produces only warning: reject default(THasNotNils)
+# produces only warning: reject userDefinedDefault(THasNotNils)
+
+# produces only warning: reject default(TRefObjNotNil)
+# produces only warning: reject userDefinedDefault(TRefObjNotNil)
+# produces only warning: reject genericDefault(TRefObjNotNil)
 
 # missing not nils in base
 reject TBaseHasNotNils()
+# produces only warning: reject default(TBaseHasNotNils)
+# produces only warning: reject userDefinedDefault(TBaseHasNotNils)
+# produces only warning: reject genericDefault(TBaseHasNotNils)
 
 # once you take care of them, it's ok
 accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: D)
@@ -106,8 +149,118 @@ accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: B, indirectNotNils: m
 reject TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: B, indirectNotNils: THasNotNilsRef())
 accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: B, indirectNotNils: THasNotNilsRef(a: notNilRef, b: notNilRef))
 
+# Accept only instances where the `a` field is present
+accept PartialRequiresInit(a: 10, b: "x")
+accept PartialRequiresInit(a: 20)
+reject PartialRequiresInit(b: "x")
+reject PartialRequiresInit()
+accept PartialRequiresInitRef(a: 10, b: "x")
+accept PartialRequiresInitRef(a: 20)
+reject PartialRequiresInitRef(b: "x")
+reject PartialRequiresInitRef()
+accept((ref PartialRequiresInit)(a: 10, b: "x"))
+accept((ref PartialRequiresInit)(a: 20))
+reject((ref PartialRequiresInit)(b: "x"))
+reject((ref PartialRequiresInit)())
+
+# produces only warning: reject default(PartialRequiresInit)
+# produces only warning: reject userDefinedDefault(PartialRequiresInit)
+reject:
+  var obj: PartialRequiresInit
+
+accept FullRequiresInit(a: 10, b: 20)
+reject FullRequiresInit(a: 10)
+reject FullRequiresInit(b: 20)
+reject FullRequiresInit()
+accept FullRequiresInitRef(a: 10, b: 20)
+reject FullRequiresInitRef(a: 10)
+reject FullRequiresInitRef(b: 20)
+reject FullRequiresInitRef()
+accept((ref FullRequiresInit)(a: 10, b: 20))
+reject((ref FullRequiresInit)(a: 10))
+reject((ref FullRequiresInit)(b: 20))
+reject((ref FullRequiresInit)())
+
+# produces only warning: reject default(FullRequiresInit)
+# produces only warning: reject userDefinedDefault(FullRequiresInit)
+reject:
+  var obj: FullRequiresInit
+
+accept FullRequiresInitWithParent(a: notNilRef, b: notNilRef, c: notNilRef, e: 10, d: 20)
+accept FullRequiresInitWithParent(a: notNilRef, b: notNilRef, c: nil, e: 10, d: 20)
+reject FullRequiresInitWithParent(a: notNilRef, b: nil, c: nil, e: 10, d: 20) # b should not be nil
+reject FullRequiresInitWithParent(a: notNilRef, b: notNilRef, e: 10, d: 20)   # c should not be missing
+reject FullRequiresInitWithParent(a: notNilRef, b: notNilRef, c: nil, e: 10)  # d should not be missing
+reject FullRequiresInitWithParent()
+# produces only warning: reject default(FullRequiresInitWithParent)
+# produces only warning: reject userDefinedDefault(FullRequiresInitWithParent)
+reject:
+  var obj: FullRequiresInitWithParent
+
 # this will be accepted, because the false outer branch will be taken and the inner A branch
 accept TNestedChoices()
+accept default(TNestedChoices)
+accept:
+  var obj: TNestedChoices
+
+#[# produces only warning:
+reject:
+  # This proc is illegal, because it tries to produce
+  # a default object of a type that requires initialization:
+  proc defaultHasNotNils: THasNotNils =
+    discard
+#]#
+
+#[# produces only warning:
+reject:
+  # You cannot cheat by using the result variable to specify
+  # only some of the fields
+  proc invalidPartialTHasNotNils: THasNotNils =
+    result.c = nilRef
+#]#
+
+#[# produces only warning:
+reject:
+  # The same applies for requiresInit types
+  proc invalidPartialRequiersInit: PartialRequiresInit =
+    result.b = "x"
+#]#
+
+#[# produces only warning:
+# All code paths must return a value when the result requires initialization:
+reject:
+  proc ifWithoutAnElse: THasNotNils =
+    if stdin.readLine == "":
+      return THasNotNils(a: notNilRef, b: notNilRef, c: nilRef)
+#]#
+
+accept:
+  # All code paths must return a value when the result requires initialization:
+  proc wellFormedIf: THasNotNils =
+    if stdin.readLine == "":
+      return THasNotNils(a: notNilRef, b: notNilRef, c: nilRef)
+    else:
+      return THasNotNIls(a: notNilRef, b: notNilRef)
+
+#[# produces only warning:
+reject:
+  proc caseWithoutAllCasesCovered: FullRequiresInit =
+    # Please note that these is no else branch here:
+    case stdin.readLine
+    of "x":
+      return FullRequiresInit(a: 10, b: 20)
+    of "y":
+      return FullRequiresInit(a: 30, b: 40)
+#]#
+
+accept:
+  proc wellFormedCase: FullRequiresInit =
+    case stdin.readLine
+    of "x":
+      result = FullRequiresInit(a: 10, b: 20)
+    else:
+      # Mixing result and return is fine:
+      return FullRequiresInit(a: 30, b: 40)
 
 # but if we supply a run-time value for the inner branch, the compiler won't be able to prove
 # that the notnil field was initialized
@@ -120,3 +273,167 @@ reject TNestedChoices(outerChoice: false, innerChoice: C)
 accept TNestedChoices(outerChoice: false, innerChoice: C, notnil: notNilRef)
 reject TNestedChoices(outerChoice: false, innerChoice: C, notnil: nil)
 
+# Tests involving generics and sequences:
+#
+block:
+  # This test aims to show that it's possible to instantiate and
+  # use a sequence with a requiresInit type:
+
+  var legalSeq: seq[IllegalToConstruct]
+  legalSeq.add IllegalToConstruct(x: "one")
+  var two = IllegalToConstruct(x: "two")
+  legalSeq.add two
+  var one = legalSeq[0]
+  var twoAgain = legalSeq.pop
+
+  #[# produces only warning:
+  # It's not possible to tell the sequence to create elements
+  # for us though:
+  reject:
+    var illegalSeq = newSeq[IllegalToConstruct](10)
+  #]#
+
+  #[# produces only warning:
+  reject:
+    var illegalSeq: seq[IllegalToConstruct]
+    newSeq(illegalSeq, 10)
+  #]#
+
+  #[# produces only warning:
+  reject:
+    var illegalSeq: seq[IllegalToConstruct]
+    illegalSeq.setLen 10
+  #]#
+
+  # You can still use newSeqOfCap to write efficient code:
+  var anotherLegalSequence = newSeqOfCap[IllegalToConstruct](10)
+  for i in 0..9:
+    anotherLegalSequence.add IllegalToConstruct(x: "x")
+
+type DefaultConstructible[yesOrNo: static[bool]] = object
+  when yesOrNo:
+    x: string
+  else:
+    x: cstring not nil
+
+block:
+  # Constructability may also depend on the generic parameters of the type:
+  accept:
+    var a: DefaultConstructible[true]
+    var b = DefaultConstructible[true]()
+    var c = DefaultConstructible[true](x: "test")
+    var d = DefaultConstructible[false](x: "test")
+
+  reject:
+    var y: DefaultConstructible[false]
+
+  reject:
+    var y = DefaultConstructible[false]()
+
+block:
+  type
+    Hash = int
+
+    HashTableSlotType = enum
+      Free    = Hash(0)
+      Deleted = Hash(1)
+      HasKey  = Hash(2)
+
+    KeyValuePair[A, B] = object
+      key: A
+      case hash: HashTableSlotType
+      of Free, Deleted:
+        discard
+      else:
+        value: B
+
+  # The above KeyValuePair is an interesting type because it
+  # may become unconstructible depending on the generic parameters:
+  accept KeyValuePair[int, string](hash: Deleted)
+  accept KeyValuePair[int, IllegalToConstruct](hash: Deleted)
+
+  accept KeyValuePair[int, string](hash: HasKey)
+  reject KeyValuePair[int, IllegalToConstruct](hash: HasKey)
+
+  # Since all the above variations don't have a non-constructible
+  # field in the default branch of the case object, we can construct
+  # such values:
+  accept KeyValuePair[int, string]()
+  accept KeyValuePair[int, IllegalToConstruct]()
+  accept KeyValuePair[DefaultConstructible[true], string]()
+  accept KeyValuePair[DefaultConstructible[true], IllegalToConstruct]()
+
+  var a: KeyValuePair[int, string]
+  var b: KeyValuePair[int, IllegalToConstruct]
+  var c: KeyValuePair[DefaultConstructible[true], string]
+  var d: KeyValuePair[DefaultConstructible[true], IllegalToConstruct]
+  var s1 = newSeq[KeyValuePair[int, IllegalToConstruct]](10)
+  var s2 = newSeq[KeyValuePair[DefaultConstructible[true], IllegalToConstruct]](10)
+
+  # But let's put the non-constructible values as keys:
+  reject KeyValuePair[IllegalToConstruct, int](hash: Deleted)
+  reject KeyValuePair[IllegalToConstruct, int]()
+
+  type IllegalPair = KeyValuePair[DefaultConstructible[false], string]
+
+  reject:
+    var x: IllegalPair
+
+  #[# produces only warning:
+  reject:
+    var s = newSeq[IllegalPair](10)
+  #]#
+
+# Specific issues:
+#
+block:
+  # https://github.com/nim-lang/Nim/issues/11428
+  type
+    Enum = enum A, B, C
+    Thing = object
+      case kind: Enum
+      of A: discard
+      of B: s: string
+      of C: r: range[1..1] # DateTime
+
+  # Fine to not initialize 'r' because this is implicitly initialized and known to be branch 'A'.
+  var x = Thing()
+  discard x
+
+block:
+  # https://github.com/nim-lang/Nim/issues/4907
+  type
+    Foo = ref object
+    Bar = object
+
+    Thing[A, B] = ref object
+      a: A not nil
+      b: ref B
+      c: ref B not nil
+
+  proc allocNotNil(T: typedesc): T not nil =
+    new result
+
+  proc mutateThing(t: var Thing[Foo, Bar]) =
+    let fooNotNil = allocNotNil(Foo)
+    var foo: Foo
+
+    let barNotNil = allocNotNil(ref Bar)
+    var bar: ref Bar
+
+    t.a = fooNotNil
+    t.b = bar
+    t.b = barNotNil
+    t.c = barNotNil
+
+    reject:
+      t.a = foo
+
+    reject:
+      t.c = bar
+
+  var thing = Thing[Foo, Bar](a: allocNotNil(Foo),
+                              b: allocNotNil(ref Bar),
+                              c: allocNotNil(ref Bar))
+  mutateThing thing
+
diff --git a/tests/controlflow/tblock1.nim b/tests/controlflow/tblock1.nim
index e3a780dfe..70c844513 100644
--- a/tests/controlflow/tblock1.nim
+++ b/tests/controlflow/tblock1.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "undeclared identifier: \'ha\'"
   file: "tblock1.nim"
   line: 14
-  errormsg: "undeclared identifier: \'ha\'"
 """
 # check for forward label and
 # for failure when label is not declared
@@ -14,5 +14,3 @@ proc main =
   break ha #ERROR
 
 main()
-
-
diff --git a/tests/controlflow/tbreak.nim b/tests/controlflow/tbreak.nim
deleted file mode 100644
index 7deab4caf..000000000
--- a/tests/controlflow/tbreak.nim
+++ /dev/null
@@ -1,44 +0,0 @@
-discard """
-  output: '''10
-true true
-true false
-false true
-false false'''
-"""
-
-var
-  x = false
-  run = true
-
-while run:
-  run = false
-  block myblock:
-    if true:
-      break
-    echo "leaving myblock"
-  x = true
-doAssert(x)
-
-# bug #1418
-iterator foo: int =
-  for x in 0 .. 9:
-    for y in [10,20,30,40,50,60,70,80,90]:
-      yield x + y
-
-for p in foo():
-  echo p
-  break
-
-iterator permutations: int =
-  yield 10
-
-for p in permutations():
-  break
-
-# regression:
-proc main =
-  for x in [true, false]:
-    for y in [true, false]:
-      echo x, " ", y
-
-main()
diff --git a/tests/controlflow/tcontinue.nim b/tests/controlflow/tcontinue.nim
deleted file mode 100644
index ac4fdc2de..000000000
--- a/tests/controlflow/tcontinue.nim
+++ /dev/null
@@ -1,28 +0,0 @@
-discard """
-  output: "came here"
-"""
-
-var i = 0
-while i < 400:
-
-  if i == 10: break
-  elif i == 3:
-    inc i
-    continue
-  inc i
-
-var f = "failure"
-var j = 0
-while j < 300:
-  for x in 0..34:
-    if j < 300: continue
-    if x == 10:
-      echo "failure: should never happen"
-      break
-  f = "came here"
-  break
-
-if i == 10:
-  echo f
-else:
-  echo "failure"
diff --git a/tests/controlflow/tcontrolflow.nim b/tests/controlflow/tcontrolflow.nim
new file mode 100644
index 000000000..dd21a2bb6
--- /dev/null
+++ b/tests/controlflow/tcontrolflow.nim
@@ -0,0 +1,116 @@
+discard """
+  output: '''
+10
+true true
+true false
+false true
+false false
+i == 2
+'''
+"""
+
+
+block tbreak:
+  var
+    x = false
+    run = true
+
+  while run:
+    run = false
+    block myblock:
+      if true:
+        break myblock
+      echo "leaving myblock"
+    x = true
+  doAssert(x)
+
+  # bug #1418
+  iterator foo: int =
+    for x in 0 .. 9:
+      for y in [10,20,30,40,50,60,70,80,90]:
+        yield x + y
+
+  for p in foo():
+    echo p
+    break
+
+  iterator permutations: int =
+    yield 10
+
+  for p in permutations():
+    break
+
+  # regression:
+  proc main =
+    for x in [true, false]:
+      for y in [true, false]:
+        echo x, " ", y
+
+  main()
+
+
+
+block tcontinue:
+  var i = 0
+  while i < 400:
+
+    if i == 10: break
+    elif i == 3:
+      inc i
+      continue
+    inc i
+
+  var f = "failure"
+  var j = 0
+  while j < 300:
+    for x in 0..34:
+      if j < 300: continue
+      if x == 10:
+        echo "failure: should never happen"
+        break
+    f = "came here"
+    break
+
+  if i == 10:
+    doAssert f == "came here"
+  else:
+    echo "failure"
+
+
+
+block tnestif:
+  var
+      x, y: int
+  x = 2
+  if x == 0:
+      write(stdout, "i == 0")
+      if y == 0:
+          writeLine(stdout, x)
+      else:
+          writeLine(stdout, y)
+  elif x == 1:
+      writeLine(stdout, "i == 1")
+  elif x == 2:
+      writeLine(stdout, "i == 2")
+  else:
+      writeLine(stdout, "looks like Python")
+  #OUT i == 2
+
+# bug https://github.com/nim-lang/RFCs/issues/451
+for i in 1..2: # works
+  break
+
+block: # works
+  for i in 1..2:
+    break
+
+block: # works
+  block:
+    discard 12 + 3
+  for i in 1..2:
+    break
+
+block named: # works
+  if true:
+    break named
+  doAssert false, "not reached"
diff --git a/tests/controlflow/tnestif.nim b/tests/controlflow/tnestif.nim
deleted file mode 100644
index 3d8adb337..000000000
--- a/tests/controlflow/tnestif.nim
+++ /dev/null
@@ -1,24 +0,0 @@
-discard """
-  file: "tnestif.nim"
-  output: "i == 2"
-"""
-# test nested ifs
-
-var
-    x, y: int
-x = 2
-if x == 0:
-    write(stdout, "i == 0")
-    if y == 0:
-        write(stdout, x)
-    else:
-        write(stdout, y)
-elif x == 1:
-    write(stdout, "i == 1")
-elif x == 2:
-    write(stdout, "i == 2")
-else:
-    write(stdout, "looks like Python")
-#OUT i == 2
-
-
diff --git a/tests/controlflow/tstatret.nim b/tests/controlflow/tstatret.nim
index 04cac9966..a3558e6f4 100644
--- a/tests/controlflow/tstatret.nim
+++ b/tests/controlflow/tstatret.nim
@@ -1,12 +1,9 @@
 discard """
-  file: "tstatret.nim"
-  line: 9
-  errormsg: "unreachable statement after 'return'"
+  nimout: '''
+tstatret.nim(9, 7) Warning: unreachable code after 'return' statement or '{.noReturn.}' proc [UnreachableCode]
+'''
 """
 # no statement after return
 proc main() =
   return
   echo("huch?") #ERROR_MSG statement not allowed after
-
-
-
diff --git a/tests/controlflow/tunamedbreak.nim b/tests/controlflow/tunamedbreak.nim
new file mode 100644
index 000000000..46113cabc
--- /dev/null
+++ b/tests/controlflow/tunamedbreak.nim
@@ -0,0 +1,15 @@
+
+discard """
+  cmd: "nim check $file"
+  action: "reject"
+  nimout: '''
+tunamedbreak.nim(12, 5) Error: Using an unnamed break in a block is deprecated; Use a named block with a named break instead [UnnamedBreak]
+tunamedbreak.nim(15, 3) Error: Using an unnamed break in a block is deprecated; Use a named block with a named break instead [UnnamedBreak]
+  '''
+"""
+for i in 1..2: # errors
+  block:
+    break
+
+block: # errors
+  break
diff --git a/tests/controlflow/tunreachable.nim b/tests/controlflow/tunreachable.nim
new file mode 100644
index 000000000..06321ce8a
--- /dev/null
+++ b/tests/controlflow/tunreachable.nim
@@ -0,0 +1,79 @@
+discard """
+  cmd: "nim check --warningAsError:UnreachableCode $file"
+  action: "reject"
+  nimout: '''
+tunreachable.nim(26, 3) Error: unreachable code after 'return' statement or '{.noReturn.}' proc [UnreachableCode]
+tunreachable.nim(33, 3) Error: unreachable code after 'return' statement or '{.noReturn.}' proc [UnreachableCode]
+tunreachable.nim(42, 3) Error: unreachable code after 'return' statement or '{.noReturn.}' proc [UnreachableCode]
+tunreachable.nim(65, 5) Error: unreachable code after 'return' statement or '{.noReturn.}' proc [UnreachableCode]
+tunreachable.nim(77, 5) Error: unreachable code after 'return' statement or '{.noReturn.}' proc [UnreachableCode]
+'''
+"""
+  
+# bug #9839
+template myquit1():untyped=
+  ## foo
+  quit(1)
+template myquit2():untyped=
+  echo 123
+  myquit1()
+
+proc main1()=
+
+  # BUG: uncommenting this doesn't give `Error: unreachable statement`
+  myquit2()
+
+  echo "after"
+
+main1()
+
+proc main2() =
+  myquit1()
+
+  echo "after"
+
+main2()
+
+proc main3() =
+  if true:
+    return
+  else:
+    return
+  echo "after"
+
+main3()
+
+
+block:
+  # Cases like strings are not checked for exhaustiveness unless they have an else
+  proc main4(x: string) =
+    case x
+    of "a":
+      return
+    # reachable
+    echo "after"
+
+  main4("a")
+
+  proc main5(x: string) =
+    case x
+    of "a":
+      return
+    else:
+      return
+    # unreachable
+    echo "after"
+
+  main5("a")
+
+block:
+  # In this case no else is needed because it's exhaustive
+  proc exhaustive(x: bool) =
+    case x
+    of true:
+      return
+    of false:
+      return
+    echo "after"
+
+  exhaustive(true)
diff --git a/tests/controlflow/tunreachable2.nim b/tests/controlflow/tunreachable2.nim
new file mode 100644
index 000000000..a658880f0
--- /dev/null
+++ b/tests/controlflow/tunreachable2.nim
@@ -0,0 +1,12 @@
+discard """
+  matrix: "--warningAsError:UnreachableCode"
+"""
+
+proc test(): bool =
+  block okay:
+    if true: break okay
+    return false
+
+  return true # Line 7 is here
+
+doAssert test()
diff --git a/tests/converter/m18986.nim b/tests/converter/m18986.nim
new file mode 100644
index 000000000..0ebf343ae
--- /dev/null
+++ b/tests/converter/m18986.nim
@@ -0,0 +1,3 @@
+import std/macros
+
+converter Lit*(x: uint): NimNode = newLit(x)
diff --git a/tests/converter/mdontleak.nim b/tests/converter/mdontleak.nim
new file mode 100644
index 000000000..e55c3f87c
--- /dev/null
+++ b/tests/converter/mdontleak.nim
@@ -0,0 +1,3 @@
+
+converter toBool(x: uint32): bool = x != 0
+# Note: This convertes is not exported!
diff --git a/tests/converter/t18986.nim b/tests/converter/t18986.nim
new file mode 100644
index 000000000..ef300fa49
--- /dev/null
+++ b/tests/converter/t18986.nim
@@ -0,0 +1,10 @@
+discard """
+  output: "Found a 0"
+"""
+
+import m18986 except Lit
+import std/macros
+
+# bug #18986
+var x = 0.uint
+echo "Found a ", x
diff --git a/tests/converter/t21531.nim b/tests/converter/t21531.nim
new file mode 100644
index 000000000..b0198684d
--- /dev/null
+++ b/tests/converter/t21531.nim
@@ -0,0 +1,10 @@
+import std/typetraits
+
+type Foo* = distinct string
+
+converter toBase*(headers: var Foo): var string =
+  headers.distinctBase
+
+proc bar*(headers: var Foo) =
+  for x in headers: discard
+    
diff --git a/tests/converter/t7097.nim b/tests/converter/t7097.nim
new file mode 100644
index 000000000..d8e953080
--- /dev/null
+++ b/tests/converter/t7097.nim
@@ -0,0 +1,38 @@
+type
+  Byte* = uint8
+  Bytes* = seq[Byte]
+  
+  BytesRange* = object
+    bytes: Bytes
+    ibegin, iend: int
+
+proc initBytesRange*(s: var Bytes, ibegin = 0, iend = -1): BytesRange =
+  let e = if iend < 0: s.len + iend + 1
+          else: iend
+  assert ibegin > 0 and e <= s.len
+  when defined(gcRefc):
+    shallow(s)
+  result.bytes = s
+  result.ibegin = ibegin
+  result.iend = e
+
+template `[]=`*(r: var BytesRange, i: int, v: Byte) =
+  r.bytes[r.ibegin + i] = v
+
+converter fromSeq*(s: Bytes): BytesRange =
+  var seqCopy = s
+  return initBytesRange(seqCopy)
+
+type
+  Reader* = object
+    data: BytesRange
+    position: int
+
+proc readerFromHex*(input: string): Reader =
+  let totalBytes = input.len div 2
+  var backingStore = newSeq[Byte](totalBytes)
+  result.data = initBytesRange(backingStore)
+
+  for i in 0 ..< totalBytes:
+    var nextByte = 0
+    result.data[i] = Byte(nextByte) # <-------- instantiated from here
diff --git a/tests/converter/t7098.nim b/tests/converter/t7098.nim
new file mode 100644
index 000000000..30c9c1e25
--- /dev/null
+++ b/tests/converter/t7098.nim
@@ -0,0 +1,35 @@
+discard """
+action: compile
+"""
+
+type
+  Byte* = uint8
+  Bytes* = seq[Byte]
+
+  BytesRange* = object
+    bytes: Bytes
+    ibegin, iend: int
+
+proc initBytesRange*(s: var Bytes, ibegin = 0, iend = -1): BytesRange =
+  let e = if iend < 0: s.len + iend + 1
+          else: iend
+  assert ibegin >= 0 and e <= s.len
+  when defined(gcRefc):
+    shallow(s)
+  result.bytes = s
+  result.ibegin = ibegin
+  result.iend = e
+
+converter fromSeq*(s: Bytes): BytesRange =
+  var seqCopy = s
+  return initBytesRange(seqCopy)
+
+type
+  Reader* = object
+    data: BytesRange
+    position: int
+
+proc readerFromBytes*(input: BytesRange): Reader =
+  discard
+
+let r = readerFromBytes(@[])
diff --git a/tests/converter/t9165.nim b/tests/converter/t9165.nim
new file mode 100644
index 000000000..d0225ffbc
--- /dev/null
+++ b/tests/converter/t9165.nim
@@ -0,0 +1,11 @@
+type ustring = distinct string
+
+converter toUString(s: string): ustring = ustring(s)
+converter toString(s: ustring): string = string(s)
+
+proc `[]=`*(s: var ustring, slice: Slice[int], replacement: ustring) {.inline.} =
+  s = replacement
+
+var s = ustring("123")
+s[1..2] = "3"
+doAssert s == "3"
\ No newline at end of file
diff --git a/tests/converter/tconvcolors.nim b/tests/converter/tconvcolors.nim
index 07e829550..7b440cabd 100644
--- a/tests/converter/tconvcolors.nim
+++ b/tests/converter/tconvcolors.nim
@@ -1,5 +1,7 @@
+discard """
+output: "16777215A"
+"""
 
 import colors
 
 echo int32(colWhite), 'A'
-
diff --git a/tests/converter/tconvert.nim b/tests/converter/tconvert.nim
index 48367a85b..5eee2a92d 100644
--- a/tests/converter/tconvert.nim
+++ b/tests/converter/tconvert.nim
@@ -18,3 +18,27 @@ converter toPtr*(some: var TFoo): ptr TFoo = (addr some)
 proc zoot(x: ptr TFoo) = discard
 var x: Tfoo
 zoot(x)
+
+# issue #6544
+converter withVar(b: var string): int = ord(b[1])
+
+block:
+  var x = "101"
+  var y: int = x # instantiate withVar
+  doAssert(y == ord('0'))
+
+
+######################
+# bug #3503
+type Foo = object
+  r: float
+
+converter toFoo(r: float): Foo =
+  result.r = r
+
+proc `+=`*(x: var Foo, r: float) =
+  x.r += r
+
+var a: Foo
+a.r += 3.0
+
diff --git a/tests/converter/tconverter.nim b/tests/converter/tconverter.nim
new file mode 100644
index 000000000..0bf067c55
--- /dev/null
+++ b/tests/converter/tconverter.nim
@@ -0,0 +1,11 @@
+discard """
+  output: '''fooo fooo'''
+"""
+
+converter intToString[T](i: T): string = "fooo"
+
+let
+  foo: string = 1
+  bar: string = intToString(2)
+
+echo foo, " ", bar
\ No newline at end of file
diff --git a/tests/converter/tconverter_unique_ptr.nim b/tests/converter/tconverter_unique_ptr.nim
new file mode 100644
index 000000000..6902f9e9e
--- /dev/null
+++ b/tests/converter/tconverter_unique_ptr.nim
@@ -0,0 +1,149 @@
+
+discard """
+  targets: "c cpp"
+  output: ""
+"""
+
+## Bugs 9698 and 9699
+
+type
+  UniquePtr*[T] = object
+    ## non copyable pointer to object T, exclusive ownership of the object is assumed
+    val: ptr T
+
+  MyLen* = distinct int
+
+  MySeq* = object
+    ## Vectorized matrix
+    len: MyLen  # scalar size
+    data: ptr UncheckedArray[float]
+
+proc `$`(x: MyLen): string {.borrow.}
+proc `==`(x1, x2: MyLen): bool {.borrow.}
+
+
+proc `=destroy`*(m: MySeq) {.inline.} =
+  if m.data != nil:
+    deallocShared(m.data)
+
+proc `=copy`*(m: var MySeq, m2: MySeq) =
+  if m.data == m2.data: return
+  if m.data != nil:
+    `=destroy`(m)
+
+  m.len = m2.len
+  let bytes = m.len.int * sizeof(float)
+  if bytes > 0:
+    m.data = cast[ptr UncheckedArray[float]](allocShared(bytes))
+    copyMem(m.data, m2.data, bytes)
+
+proc `=sink`*(m: var MySeq, m2: MySeq) {.inline.} =
+  if m.data != m2.data:
+    if m.data != nil:
+      `=destroy`(m)
+    m.len = m2.len
+    m.data = m2.data
+
+proc len*(m: MySeq): MyLen {.inline.} = m.len
+
+proc lenx*(m: var MySeq): MyLen {.inline.} = m.len
+
+proc `[]`*(m: MySeq; i: MyLen): float {.inline.} =
+  m.data[i.int]
+
+proc `[]`*(m: var MySeq; i: MyLen): var float {.inline.} =
+  m.data[i.int]
+
+proc `[]=`*(m: var MySeq; i: MyLen, val: float) {.inline.} =
+  m.data[i.int] = val
+
+proc setTo(s: var MySeq, val: float) =
+  for i in 0..<s.len.int:
+    s.data[i] = val
+
+proc newMySeq*(size: int, initial_value = 0.0): MySeq =
+  result.len = size.MyLen
+  if size > 0:
+    result.data = cast[ptr UncheckedArray[float]](createShared(float, size))
+
+  result.setTo(initial_value)
+
+converter literalToLen*(x: int{lit}): MyLen =
+  x.MyLen
+
+
+#-------------------------------------------------------------
+# Unique pointer implementation
+#-------------------------------------------------------------
+
+proc `=destroy`*[T](p: UniquePtr[T]) =
+  if p.val != nil:
+    `=destroy`(p.val[])
+    dealloc(p.val)
+
+proc `=copy`*[T](dest: var UniquePtr[T], src: UniquePtr[T]) {.error.}
+
+proc `=sink`*[T](dest: var UniquePtr[T], src: UniquePtr[T]) {.inline.} =
+  if dest.val != nil and dest.val != src.val:
+    `=destroy`(dest)
+  dest.val = src.val
+
+proc newUniquePtr*[T](val: sink T): UniquePtr[T] =
+  result.val = cast[type(result.val)](alloc(sizeof(result.val[])))
+  reset(result.val[])
+  result.val[] = val
+
+converter convertPtrToObj*[T](p: UniquePtr[T]): var T =
+  result = p.val[]
+
+var pu = newUniquePtr(newMySeq(5, 1.0))
+let pu2 = newUniquePtr(newMySeq(5, 1.0))
+doAssert: pu.len == 5
+doAssert: pu2.len == 5
+doAssert: pu.lenx == 5
+doAssert: pu2.lenx == 5
+
+pu[0] = 2.0
+pu2[0] = 2.0
+doAssert pu[0] == 2.0
+doAssert: pu2[0] == 2.0
+
+##-----------------------------------------------------------------------------------------
+## Bugs #9735 and #9736
+type
+  ConstPtr*[T] = object
+    ## This pointer makes it impossible to change underlying value
+    ## as it returns only `lent T`
+    val: ptr T
+
+proc `=destroy`*[T](p: ConstPtr[T]) =
+  if p.val != nil:
+    `=destroy`(p.val[])
+    dealloc(p.val)
+
+proc `=copy`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.error.}
+
+proc `=sink`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.inline.} =
+  if dest.val != nil and dest.val != src.val:
+    `=destroy`(dest)
+  dest.val = src.val
+
+proc newConstPtr*[T](val: sink T): ConstPtr[T] =
+  result.val = cast[type(result.val)](alloc(sizeof(result.val[])))
+  reset(result.val[])
+  result.val[] = val
+
+converter convertConstPtrToObj*[T](p: ConstPtr[T]): lent T =
+  result = p.val[]
+
+var pc = newConstPtr(newMySeq(3, 1.0))
+let pc2 = newConstPtr(newMySeq(3, 1.0))
+doAssert: pc.len == 3
+doAssert: pc.len == 3
+doAssert: compiles(pc.lenx == 2) == false
+doAssert: compiles(pc2.lenx == 2) == false
+doAssert: compiles(pc[0] = 2.0) == false
+doAssert: compiles(pc2[0] = 2.0) == false
+
+doAssert: pc[0] == 1.0
+doAssert: pc2[0] == 1.0
diff --git a/tests/converter/tconverter_with_constraint.nim b/tests/converter/tconverter_with_constraint.nim
new file mode 100644
index 000000000..ce5135586
--- /dev/null
+++ b/tests/converter/tconverter_with_constraint.nim
@@ -0,0 +1,20 @@
+
+discard """
+  errormsg: "type mismatch: got <int>"
+  file: "tconverter_with_constraint.nim"
+  line: 20
+"""
+
+type
+  MyType = distinct int
+
+converter to_mytype(m: int{lit}): MyType =
+  m.MyType
+
+proc myproc(m: MyType) =
+  echo m.int, ".MyType"
+
+myproc(1) # call by literal is ok
+
+var x: int = 12
+myproc(x) # should fail
diff --git a/tests/converter/tconverter_with_varargs.nim b/tests/converter/tconverter_with_varargs.nim
index 6d7e31e85..fae83221b 100644
--- a/tests/converter/tconverter_with_varargs.nim
+++ b/tests/converter/tconverter_with_varargs.nim
@@ -8,7 +8,7 @@ type
 converter to_py*(i: int) : PPyRef = nil
 
 when false:
-  proc to_tuple*(vals: openarray[PPyRef]): PPyRef =
+  proc to_tuple*(vals: openArray[PPyRef]): PPyRef =
     discard
 
 proc abc(args: varargs[PPyRef]) =
diff --git a/tests/converter/tdontleak.nim b/tests/converter/tdontleak.nim
new file mode 100644
index 000000000..4965fa90a
--- /dev/null
+++ b/tests/converter/tdontleak.nim
@@ -0,0 +1,10 @@
+discard """
+  output: '''5'''
+joinable: false
+"""
+
+import mdontleak
+# bug #19213
+
+let a = 5'u32
+echo a
diff --git a/tests/converter/texplicit_conversion.nim b/tests/converter/texplicit_conversion.nim
index 6b2e96faf..e36d78ad5 100644
--- a/tests/converter/texplicit_conversion.nim
+++ b/tests/converter/texplicit_conversion.nim
@@ -11,3 +11,9 @@ converter toInt(s: string): int =
 
 let x = (int)"234"
 echo x
+
+block: # Test for nkconv
+  proc foo(o: var int) =
+    assert o == 0
+  var a = 0
+  foo(int(a))
\ No newline at end of file
diff --git a/tests/converter/tgenericconverter.nim b/tests/converter/tgenericconverter.nim
index e1c9f7c4c..cbbd2e1b0 100644
--- a/tests/converter/tgenericconverter.nim
+++ b/tests/converter/tgenericconverter.nim
@@ -28,3 +28,27 @@ aa.x = 666
 
 p aa
 q aa
+
+
+#-------------------------------------------------------------
+# issue #16651
+type
+  PointTup = tuple
+    x: float32
+    y: float32
+
+converter tupleToPoint[T1, T2: SomeFloat](self: tuple[x: T1, y: T2]): PointTup =
+  result = (self.x.float32, self.y.float32)
+
+proc tupleToPointX(self: tuple[x: SomeFloat, y: SomeFloat]): PointTup =
+  result = (self.x.float32, self.y.float32)
+
+proc tupleToPointX2(self: tuple[x: SomeFloat, y: distinct SomeFloat]): PointTup =
+  result = (self.x.float32, self.y.float32)
+
+var t1: PointTup = tupleToPointX((1.0, 0.0))
+var t2: PointTup = tupleToPointX2((1.0, 0.0))
+var t3: PointTup = tupleToPointX2((1.0'f32, 0.0))
+var t4: PointTup = tupleToPointX2((1.0, 0.0'f32))
+
+var x2: PointTup = (1.0, 0.0)
\ No newline at end of file
diff --git a/tests/converter/tgenericconverter2.nim b/tests/converter/tgenericconverter2.nim
index ae064d852..97b7846c1 100644
--- a/tests/converter/tgenericconverter2.nim
+++ b/tests/converter/tgenericconverter2.nim
@@ -1,6 +1,36 @@
 # bug #3799
 
-import macros
+import strutils
+
+const output = splitLines("""
+00000000000000000000000000000000000000000
+00000000000001111111111111110000000000000
+00000000001111111111111111111110000000000
+00000000111111111111111111111111100000000
+00000011111222222221111111111111111000000
+00000111122222222222221111111111111100000
+00001112222333333459432111111111111110000
+00011122355544344463533221111111111111000
+00111124676667556896443322211111111111100
+00111126545561919686543322221111111111100
+01111123333346967807554322222211111111110
+01111122233334455582015332222221111111110
+01111122222333344567275432222222111111110
+01111112222222334456075443222222211111110
+01111111222222233459965444332222221111110
+01111111122222223457486554433322222111110
+01111111112222222367899655543333322111110
+01111111111122222344573948465444332111110
+00111111111112222334467987727667762111100
+00111111111111122233474655557836432111100
+00011111111111112233 454433334 4321111000
+00001111111111111122354333322222211110000
+00000111111111111111222222222222111100000
+00000001111111111111111122222111110000000
+00000000111111111111111111111111100000000
+00000000000111111111111111111100000000000
+00000000000000111111111111100000000000000
+""")
 
 const nmax = 500
 
@@ -31,19 +61,25 @@ proc julia*[T](z0, c: Complex[T], er2: T, nmax: int): int =
 template dendriteFractal*[T](z0: Complex[T], er2: T, nmax: int): int =
   julia(z0, (T(0.0), T(1.0)), er2, nmax)
 
-iterator stepIt[T](start, step: T, iterations: int): T =
+iterator stepIt[T](start, step: T, iterations: int): (int, T) =
   for i in 0 .. iterations:
-    yield start + T(i) * step
+    yield (i, start + T(i) * step)
 
+var errors = 0
 
 let c = (0.36237, 0.32)
-for y in stepIt(2.0, -0.0375, 107):
+for j, y in stepIt(2.0, -0.0375 * 4, 107 div 4):
   var row = ""
-  for x in stepIt(-2.0, 0.025, 160):
+  for i, x in stepIt(-2.0, 0.025 * 4, 160 div 4):
     #let n = julia((x, y), c, 4.0, nmax)         ### this works
     let n = dendriteFractal((x, y), 4.0, nmax)
-    if n < nmax:
-      row.add($(n mod 10))
-    else:
-      row.add(' ')
-  echo row
+    let c = char(int('0') + n mod 10)
+    let e = output[j][i]  # expected
+    if c != e:
+      errors += 1
+    row.add(c)
+
+  # Printing apparently makes the test fail when joined.
+  # echo row
+
+doAssert errors < 10, "total errors: " & $errors
diff --git a/tests/converter/tor_in_converter.nim b/tests/converter/tor_in_converter.nim
index 5674526a1..df2334647 100644
--- a/tests/converter/tor_in_converter.nim
+++ b/tests/converter/tor_in_converter.nim
@@ -4,7 +4,7 @@ test'''
 """
 # bug #4537
 
-# nim js --d:nodejs
+# nim js -d:nodejs
 
 type
   Str = distinct string
diff --git a/tests/coroutines/texceptions.nim b/tests/coroutines/texceptions.nim
index f3debf0a7..31feffdff 100644
--- a/tests/coroutines/texceptions.nim
+++ b/tests/coroutines/texceptions.nim
@@ -1,3 +1,8 @@
+discard """
+  targets: "c"
+  disabled: true
+"""
+
 import coro
 var
   stackCheckValue = 1100220033
@@ -10,6 +15,7 @@ proc testExceptions(id: int, sleep: float) =
     numbers.add(id)
     raise (ref ValueError)()
   except:
+    suspend(sleep)
     numbers.add(id)
     suspend(sleep)
     numbers.add(id)
@@ -18,6 +24,6 @@ proc testExceptions(id: int, sleep: float) =
 
 start(proc() = testExceptions(1, 0.01))
 start(proc() = testExceptions(2, 0.011))
-run()
+coro.run()
 doAssert(stackCheckValue == 1100220033, "Thread stack got corrupted")
 doAssert(numbers == @[1, 2, 1, 2, 1, 2, 1, 2, 1, 2], "Coroutines executed in incorrect order")
diff --git a/tests/coroutines/tgc.nim b/tests/coroutines/tgc.nim
index 66a12ab9d..770d413f5 100644
--- a/tests/coroutines/tgc.nim
+++ b/tests/coroutines/tgc.nim
@@ -1,15 +1,22 @@
-import coro
+discard """
+  matrix: "--gc:refc; --gc:arc; --gc:orc"
+  targets: "c"
+"""
 
-var maxOccupiedMemory = 0
+when compileOption("gc", "refc") or not defined(openbsd):
+  # xxx openbsd gave: stdlib_coro.nim.c:406:22: error: array type 'jmp_buf' (aka 'long [11]') is not assignable (*dest).execContext = src.execContext;
+  import coro
 
-proc testGC() =
-  var numbers = newSeq[int](100)
-  maxOccupiedMemory = max(maxOccupiedMemory, getOccupiedMem())
-  suspend(0)
+  var maxOccupiedMemory = 0
 
-start(testGC)
-start(testGC)
-run()
+  proc testGC() =
+    var numbers = newSeq[int](100)
+    maxOccupiedMemory = max(maxOccupiedMemory, getOccupiedMem())
+    suspend(0)
 
-GC_fullCollect()
-doAssert(getOccupiedMem() < maxOccupiedMemory, "GC did not free any memory allocated in coroutines")
+  start(testGC)
+  start(testGC)
+  run()
+
+  GC_fullCollect()
+  doAssert(getOccupiedMem() < maxOccupiedMemory, "GC did not free any memory allocated in coroutines")
diff --git a/tests/coroutines/titerators.nim b/tests/coroutines/titerators.nim
index e2623ce2d..1ea134811 100644
--- a/tests/coroutines/titerators.nim
+++ b/tests/coroutines/titerators.nim
@@ -1,3 +1,10 @@
+discard """
+  targets: "c"
+disabled: true
+"""
+
+# Timers are always flakey on the testing servers.
+
 import coro
 include system/timers
 
@@ -18,7 +25,7 @@ var start = getTicks()
 start(proc() = theCoroutine(1, 0.01))
 start(proc() = theCoroutine(2, 0.011))
 run()
+
 var executionTime = getTicks() - start
-doAssert(executionTime >= 55_000_000.Nanos and executionTime < 56_000_000.Nanos, "Coroutines executed too short")
 doAssert(stackCheckValue == 1100220033, "Thread stack got corrupted")
 doAssert(numbers == @[10, 20, 11, 21, 12, 22, 13, 23, 14, 24], "Coroutines executed in incorrect order")
diff --git a/tests/coroutines/twait.nim b/tests/coroutines/twait.nim
index eb2765be4..2edfcf675 100644
--- a/tests/coroutines/twait.nim
+++ b/tests/coroutines/twait.nim
@@ -1,20 +1,28 @@
 discard """
   output: "Exit 1\nExit 2"
-  disabled: "macosx"
+  matrix: "--gc:refc; --gc:arc; --gc:orc"
+  targets: "c"
 """
-import coro
 
-var coro1: CoroutineRef
+when compileOption("gc", "refc") or not defined(openbsd):
+  # xxx openbsd failed, see tgc.nim
+  import coro
 
-proc testCoroutine1() =
-  for i in 0..<10:
-    suspend(0)
-  echo "Exit 1"
+  var coro1: CoroutineRef
 
-proc testCoroutine2() =
-  coro1.wait()
-  echo "Exit 2"
+  proc testCoroutine1() =
+    for i in 0..<10:
+      suspend(0)
+    echo "Exit 1"
+
+  proc testCoroutine2() =
+    coro1.wait()
+    echo "Exit 2"
 
-coro1 = coro.start(testCoroutine1)
-coro.start(testCoroutine2)
-run()
+  coro1 = coro.start(testCoroutine1)
+  coro.start(testCoroutine2)
+  run()
+else:
+  # workaround
+  echo "Exit 1"
+  echo "Exit 2"
diff --git a/tests/cpp/23962.h b/tests/cpp/23962.h
new file mode 100644
index 000000000..2d8147bfb
--- /dev/null
+++ b/tests/cpp/23962.h
@@ -0,0 +1,17 @@
+#include <iostream>
+
+struct Foo {
+
+  Foo(int inX): x(inX) {
+    std::cout << "Ctor Foo(" << x << ")\n";
+  }
+  ~Foo() {
+    std::cout << "Destory Foo(" << x << ")\n";
+  }
+
+  void print() {
+    std::cout << "Foo.x = " << x << '\n';
+  }
+
+  int x;
+};
\ No newline at end of file
diff --git a/tests/cpp/amodule.nim b/tests/cpp/amodule.nim
new file mode 100644
index 000000000..2f3e34051
--- /dev/null
+++ b/tests/cpp/amodule.nim
@@ -0,0 +1,10 @@
+import os
+
+proc findlib: string =
+  let path = getEnv("MYLIB_DOES_NOT_EXIST_PATH")
+  if path.len > 0 and dirExists(path):
+    path / "alib_does_not_matter.dll"
+  else:
+    "alib_does_not_matter.dll"
+
+proc imported_func*(a: cint): cstring {.importc, dynlib: findlib().}
\ No newline at end of file
diff --git a/tests/cpp/enum.hpp b/tests/cpp/enum.hpp
new file mode 100644
index 000000000..268999d68
--- /dev/null
+++ b/tests/cpp/enum.hpp
@@ -0,0 +1,3 @@
+namespace namespaced {
+enum Enum { A, B, C };
+}
diff --git a/tests/cpp/fam.h b/tests/cpp/fam.h
new file mode 100644
index 000000000..ad576425b
--- /dev/null
+++ b/tests/cpp/fam.h
@@ -0,0 +1,4 @@
+struct Test{
+  ~Test() {
+  }
+};
diff --git a/tests/cpp/mexportc.nim b/tests/cpp/mexportc.nim
new file mode 100644
index 000000000..dee51f157
--- /dev/null
+++ b/tests/cpp/mexportc.nim
@@ -0,0 +1,9 @@
+{.used.} # ideally, would not be needed
+
+var fun0 {.exportc.} = 10
+proc fun1() {.exportc.} = discard
+proc fun2() {.exportc: "$1".} = discard
+proc fun3() {.exportc: "fun3Bis".} = discard
+
+when defined cpp:
+  proc funx1() {.exportcpp.} = discard
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/cpp/t12946.nim b/tests/cpp/t12946.nim
new file mode 100644
index 000000000..79cd56251
--- /dev/null
+++ b/tests/cpp/t12946.nim
@@ -0,0 +1,8 @@
+discard """
+  targets: "c cpp"
+"""
+
+import std/atomics
+type Futex = distinct Atomic[int32]
+
+var x: Futex
diff --git a/tests/cpp/t22679.nim b/tests/cpp/t22679.nim
new file mode 100644
index 000000000..81defcb58
--- /dev/null
+++ b/tests/cpp/t22679.nim
@@ -0,0 +1,50 @@
+discard """
+  cmd: "nim cpp $file"
+  output:'''
+cppNZ.x = 123
+cppNZInit.x = 123
+hascpp.cppnz.x = 123
+hasCppInit.cppnz.x = 123
+hasCppCtor.cppnz.x = 123
+'''
+"""
+{.emit:"""/*TYPESECTION*/
+struct CppNonZero {
+  int x = 123;
+};
+""".}
+
+import sugar
+type
+  CppNonZero {.importcpp, inheritable.} = object
+    x: cint
+
+  HasCpp = object
+    cppnz: CppNonZero
+
+proc initCppNonZero: CppNonZero =
+  CppNonZero()
+
+proc initHasCpp: HasCpp =
+  HasCpp()
+
+proc ctorHasCpp: HasCpp {.constructor.} =
+  discard
+
+proc main =
+  var cppNZ: CppNonZero
+  dump cppNZ.x
+
+  var cppNZInit = initCppNonZero()
+  dump cppNZInit.x
+
+  var hascpp: HasCpp
+  dump hascpp.cppnz.x
+
+  var hasCppInit = initHasCpp()
+  dump hasCppInit.cppnz.x
+
+  var hasCppCtor = ctorHasCpp()
+  dump hasCppCtor.cppnz.x
+
+main()
\ No newline at end of file
diff --git a/tests/cpp/t22680.nim b/tests/cpp/t22680.nim
new file mode 100644
index 000000000..80f1a8319
--- /dev/null
+++ b/tests/cpp/t22680.nim
@@ -0,0 +1,50 @@
+discard """
+  cmd: "nim cpp $file"
+  output:'''
+cppNZ.x = 123
+cppNZInit.x = 123
+inheritCpp.x = 123
+inheritCppInit.x = 123
+inheritCppCtor.x = 123
+'''
+"""
+import std/sugar
+
+{.emit:"""/*TYPESECTION*/
+struct CppNonZero {
+  int x = 123;
+};
+""".}
+
+type
+  CppNonZero {.importcpp, inheritable.} = object
+    x: cint
+
+  InheritCpp = object of CppNonZero
+
+proc initCppNonZero: CppNonZero =
+  CppNonZero()
+
+proc initInheritCpp: InheritCpp =
+  InheritCpp()
+
+proc ctorInheritCpp: InheritCpp {.constructor.} =
+  discard
+
+proc main =
+  var cppNZ: CppNonZero
+  dump cppNZ.x
+
+  var cppNZInit = initCppNonZero()
+  dump cppNZInit.x
+
+  var inheritCpp: InheritCpp
+  dump inheritCpp.x
+
+  var inheritCppInit = initInheritCpp()
+  dump inheritCppInit.x
+
+  var inheritCppCtor = ctorInheritCpp()
+  dump inheritCppCtor.x
+
+main()
\ No newline at end of file
diff --git a/tests/cpp/t22712.nim b/tests/cpp/t22712.nim
new file mode 100644
index 000000000..34ef67ac8
--- /dev/null
+++ b/tests/cpp/t22712.nim
@@ -0,0 +1,15 @@
+discard """
+targets: "cpp"
+errormsg: "constructor in an imported type needs importcpp pragma"
+line: 14
+"""
+{.emit: """/*TYPESECTION*/
+struct CppStruct {
+  CppStruct();
+};
+""".}
+
+type CppStruct {.importcpp.} = object
+
+proc makeCppStruct(): CppStruct {.constructor.} = 
+  discard
\ No newline at end of file
diff --git a/tests/cpp/t23306.nim b/tests/cpp/t23306.nim
new file mode 100644
index 000000000..ebb4edb8d
--- /dev/null
+++ b/tests/cpp/t23306.nim
@@ -0,0 +1,12 @@
+discard """
+targets: "cpp"
+"""
+
+type K = object
+  h: iterator(f: K): K
+
+iterator d(g: K): K {.closure.} =
+  defer:
+    discard
+
+discard K(h: d)
\ No newline at end of file
diff --git a/tests/cpp/t23434.nim b/tests/cpp/t23434.nim
new file mode 100644
index 000000000..04a83227e
--- /dev/null
+++ b/tests/cpp/t23434.nim
@@ -0,0 +1,17 @@
+discard """
+cmd:"nim cpp $file"
+errormsg: "type mismatch: got <proc (self: SomeObject){.member, gcsafe.}>"
+line: 17
+"""
+type SomeObject = object
+    value: int
+
+proc printValue(self: SomeObject) {.virtual.} =
+    echo "The value is ", self.value
+
+proc callAProc(p: proc(self: SomeObject){.noconv.}) =
+    let someObj = SomeObject(value: 4)
+    echo "calling param proc"
+    p(someObj)
+
+callAProc(printValue)
\ No newline at end of file
diff --git a/tests/cpp/t23657.nim b/tests/cpp/t23657.nim
new file mode 100644
index 000000000..63deb7fb0
--- /dev/null
+++ b/tests/cpp/t23657.nim
@@ -0,0 +1,54 @@
+discard """
+  targets: "cpp"
+  cmd: "nim cpp -r $file"
+  output: '''
+1.0
+1.0
+'''
+
+"""
+{.emit:"""/*TYPESECTION*/
+struct Point {
+  float x, y, z;
+  Point(float x, float y, float z): x(x), y(y), z(z) {}
+  Point() = default;
+};
+struct Direction {
+  float x, y, z;
+  Direction(float x, float y, float z): x(x), y(y), z(z) {}
+  Direction() = default;
+};
+struct Axis {
+  Point origin;
+  Direction direction;
+  Axis(Point origin, Direction direction): origin(origin), direction(direction) {}
+  Axis() = default;
+};
+
+""".}
+
+type
+  Point {.importcpp.} = object
+    x, y, z: float
+  
+  Direction {.importcpp.} = object
+    x, y, z: float
+
+  Axis {.importcpp.} = object
+    origin: Point
+    direction: Direction
+
+proc makeAxis(origin: Point, direction: Direction): Axis {. constructor, importcpp:"Axis(@)".}
+proc makePoint(x, y, z: float): Point {. constructor, importcpp:"Point(@)".}
+proc makeDirection(x, y, z: float): Direction {. constructor, importcpp:"Direction(@)".}
+
+var axis1 = makeAxis(Point(x: 1.0, y: 2.0, z: 3.0), Direction(x: 4.0, y: 5.0, z: 6.0)) #Triggers the error (T1)
+var axis2Ctor = makeAxis(makePoint(1.0, 2.0, 3.0), makeDirection(4.0, 5.0, 6.0)) #Do not triggers
+
+proc main() = #Do not triggers as Tx are inside the body
+  let test = makeAxis(Point(x: 1.0, y: 2.0, z: 3.0), Direction(x: 4.0, y: 5.0, z: 6.0))
+  echo test.origin.x
+
+main()
+
+echo $axis1.origin.x  #Make sures it's init
\ No newline at end of file
diff --git a/tests/cpp/t23962.nim b/tests/cpp/t23962.nim
new file mode 100644
index 000000000..c79d888df
--- /dev/null
+++ b/tests/cpp/t23962.nim
@@ -0,0 +1,32 @@
+discard """
+  cmd: "nim cpp $file"
+  output: '''
+Ctor Foo(-1)
+Destory Foo(-1)
+Ctor Foo(-1)
+Destory Foo(-1)
+Ctor Foo(-1)
+Destory Foo(-1)
+Foo.x = 1
+Foo.x = 2
+Foo.x = -1
+'''
+"""
+
+type
+  Foo {.importcpp, header: "23962.h".} = object
+    x: cint
+
+proc print(f: Foo) {.importcpp.}
+
+#also tests the right constructor is used
+proc makeFoo(x: int32 = -1): Foo {.importcpp:"Foo(#)", constructor.} 
+
+proc test =
+  var xs = newSeq[Foo](3)
+  xs[0].x = 1
+  xs[1].x = 2
+  for x in xs:
+    x.print
+
+test()
\ No newline at end of file
diff --git a/tests/cpp/t4834.nim b/tests/cpp/t4834.nim
new file mode 100644
index 000000000..0275b1b70
--- /dev/null
+++ b/tests/cpp/t4834.nim
@@ -0,0 +1,17 @@
+discard """
+  targets: "cpp"
+"""
+
+# issue #4834
+block:
+  defer:
+    let x = 0
+
+
+proc main() =
+  block:
+    defer:
+      raise newException(Exception, "foo")
+
+doAssertRaises(Exception):
+  main()
diff --git a/tests/cpp/t6986.nim b/tests/cpp/t6986.nim
new file mode 100644
index 000000000..16e455c3b
--- /dev/null
+++ b/tests/cpp/t6986.nim
@@ -0,0 +1,19 @@
+discard """
+  targets: "cpp"
+  action: "compile"
+"""
+
+import sequtils, strutils
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
+
+
+let rules = toSeq(lines("input"))
+  .mapIt(it.split(" => ").mapIt(it.replace("/", "")))
+  .mapIt((it[0], it[1]))
+
+
+proc pp(s: string): auto =
+  toSeq(lines(s)).mapIt(it.split(" => ").mapIt(it.replace("/", ""))).mapIt((it[0], it[1]))
+echo pp("input")
diff --git a/tests/cpp/t8241.nim b/tests/cpp/t8241.nim
new file mode 100644
index 000000000..9aed13fcb
--- /dev/null
+++ b/tests/cpp/t8241.nim
@@ -0,0 +1,32 @@
+discard """
+  targets: "cpp"
+  action: "compile"
+"""
+
+proc foo(): cstring {.importcpp: "", dynlib: "".}
+echo foo()
+
+
+## bug #9222
+import os
+import amodule
+proc findlib2: string =
+  let path = getEnv("MYLIB2_DOES_NOT_EXIST_PATH")
+  if path.len > 0 and dirExists(path):
+    path / "alib_does_not_matter.dll"
+  else:
+    "alib_does_not_matter.dll"
+
+proc imported_func2*(a: cint): cstring {.importc, dynlib: findlib2().}
+
+echo imported_func(1)
+echo imported_func2(1)
+
+# issue #8946
+
+from json import JsonParsingError
+import marshal
+
+const nothing = ""
+doAssertRaises(JsonParsingError):
+  var bar = marshal.to[int](nothing)
diff --git a/tests/cpp/t9013.nim b/tests/cpp/t9013.nim
new file mode 100644
index 000000000..6103cf2e7
--- /dev/null
+++ b/tests/cpp/t9013.nim
@@ -0,0 +1,9 @@
+discard """
+  targets: "cpp"
+  cmd: "nim $target --debugger:native $options $file"
+"""
+
+# The --debugger switch is needed in order to enable the defined(nimTypeNames)
+# code path in hti.nim
+import typeinfo
+var tt: Any
diff --git a/tests/cpp/tasync_cpp.nim b/tests/cpp/tasync_cpp.nim
index a5e3374b6..3f4ec6208 100644
--- a/tests/cpp/tasync_cpp.nim
+++ b/tests/cpp/tasync_cpp.nim
@@ -1,6 +1,7 @@
 discard """
   targets: "cpp"
   output: "hello"
+  cmd: "nim cpp --clearNimblePath --nimblePath:build/deps/pkgs2 $file"
 """
 
 # bug #3299
diff --git a/tests/cpp/tcasts.nim b/tests/cpp/tcasts.nim
index 24ebb8f62..80527efff 100644
--- a/tests/cpp/tcasts.nim
+++ b/tests/cpp/tcasts.nim
@@ -1,6 +1,5 @@
 discard """
   cmd: "nim cpp $file"
-  output: ""
   targets: "cpp"
 """
 
@@ -9,3 +8,13 @@ block: #5979
   var p: pointer = cast[pointer](a)
   var c = cast[char](p)
   doAssert(c == 'a')
+
+
+#----------------------------------------------------
+# bug #9739
+import tables
+
+var t = initTable[string, string]()
+discard t.hasKeyOrPut("123", "123")
+discard t.mgetOrPut("vas", "kas")
+doAssert t.len == 2
diff --git a/tests/cpp/tcodegendecl.nim b/tests/cpp/tcodegendecl.nim
new file mode 100644
index 000000000..e128c5eb7
--- /dev/null
+++ b/tests/cpp/tcodegendecl.nim
@@ -0,0 +1,17 @@
+discard """
+  targets: "cpp"
+  cmd: "nim cpp $file"
+  output: "3"
+"""
+
+{.emit:"""/*TYPESECTION*/
+  int operate(int x, int y, int (*func)(const int&, const int&)){
+    return func(x, y);
+  };
+""".}
+
+proc operate(x, y: int32, fn: proc(x, y: int32 ): int32 {.cdecl.}): int32 {.importcpp:"$1(@)".}
+
+proc add(a {.codegenDecl:"const $#& $#".}, b {.codegenDecl:"const $# $#", byref.}: int32): int32  {.cdecl.} = a + b
+
+echo operate(1, 2, add)
\ No newline at end of file
diff --git a/tests/cpp/tconstructor.nim b/tests/cpp/tconstructor.nim
new file mode 100644
index 000000000..922ee54fd
--- /dev/null
+++ b/tests/cpp/tconstructor.nim
@@ -0,0 +1,131 @@
+discard """
+  targets: "cpp"
+  cmd: "nim cpp $file"
+  output: '''
+1
+0
+123
+0
+123
+___
+0
+777
+10
+123
+0
+777
+10
+123
+()
+'''
+"""
+
+{.emit:"""/*TYPESECTION*/
+struct CppClass {
+  int x;
+  int y;
+  CppClass(int inX, int inY) {
+    this->x = inX;
+    this->y = inY;
+  }
+  //CppClass() = default;
+};
+""".}
+
+type  CppClass* {.importcpp, inheritable.} = object
+  x: int32
+  y: int32
+
+proc makeCppClass(x, y: int32): CppClass {.importcpp: "CppClass(@)", constructor.}
+#test globals are init with the constructor call
+var shouldCompile {.used.} = makeCppClass(1, 2)
+
+proc newCpp*[T](): ptr T {.importcpp:"new '*0()".}
+
+#creation
+type NimClassNoNarent* = object
+  x: int32
+
+proc makeNimClassNoParent(x:int32): NimClassNoNarent {. constructor.} =
+  result.x = x
+  discard
+
+let nimClassNoParent = makeNimClassNoParent(1)
+echo nimClassNoParent.x #acess to this just fine. Notice the field will appear last because we are dealing with constructor calls here
+
+var nimClassNoParentDef {.used.}: NimClassNoNarent  #test has a default constructor. 
+
+#inheritance 
+type NimClass* = object of CppClass
+
+proc makeNimClass(x:int32): NimClass {. constructor:"NimClass('1 #1) : CppClass(0, #1) ".} =
+  result.x = x
+
+#optinially define the default constructor so we get rid of the cpp warn and we can declare the obj (note: default constructor of 'tyObject_NimClass__apRyyO8cfRsZtsldq1rjKA' is implicitly deleted because base class 'CppClass' has no default constructor)
+proc makeCppClass(): NimClass {. constructor: "NimClass() : CppClass(0, 0) ".} = 
+  result.x = 1
+
+let nimClass = makeNimClass(1)
+var nimClassDef {.used.}: NimClass  #since we explictly defined the default constructor we can declare the obj
+
+#bug: 22662
+type
+  BugClass* = object
+    x: int          # Not initialized
+
+proc makeBugClass(): BugClass {.constructor.} =
+  discard
+
+proc main =
+  for i in 0 .. 1:
+    var n = makeBugClass()
+    echo n.x
+    n.x = 123
+    echo n.x
+
+main()
+#bug:
+echo "___"
+type
+  NimClassWithDefault = object
+    x: int
+    y = 777
+    case kind: bool = true
+    of true:
+      z: int = 10
+    else: discard
+
+proc makeNimClassWithDefault(): NimClassWithDefault {.constructor.} =
+  result = NimClassWithDefault()
+
+proc init =
+  for i in 0 .. 1:
+    var n = makeNimClassWithDefault()
+    echo n.x
+    echo n.y
+    echo n.z
+    n.x = 123
+    echo n.x
+
+init()
+
+#tests that the ctor is not declared with nodecl. 
+#nodelc also prevents the creation of a default one when another is created.
+type Foo {.exportc.} = object
+
+proc makeFoo(): Foo {.used, constructor, nodecl.} = discard
+
+echo $Foo()
+
+type Boo = object
+proc `=copy`(dest: var Boo; src: Boo) = discard
+
+proc makeBoo(): Boo {.constructor.} = Boo()
+proc makeBoo2(): Boo  = Boo()
+
+block:
+  proc main =
+    var b = makeBoo()
+    var b2 = makeBoo2()
+
+  main()
\ No newline at end of file
diff --git a/tests/cpp/tcovariancerules.nim b/tests/cpp/tcovariancerules.nim
index acde1b288..9d49f2cbd 100644
--- a/tests/cpp/tcovariancerules.nim
+++ b/tests/cpp/tcovariancerules.nim
@@ -28,10 +28,10 @@ template reject(x) =
 
 import macros
 
-macro skipElse(n: untyped): typed = n[0]
+macro skipElse(n: untyped): untyped = n[0]
 
-template acceptWithCovariance(x, otherwise): typed =
-  when nimEnableCovariance:
+template acceptWithCovariance(x, otherwise): untyped =
+  when defined nimEnableCovariance:
     x
   else:
     reject(x)
@@ -79,16 +79,16 @@ proc wantsCovariantSeq2(s: seq[AnimalRef]) =
 proc wantsCovariantSeq3(s: seq[RefAlias[Animal]]) =
   for a in s: echo a.x
 
-proc wantsCovariantOperArray(s: openarray[ref Animal]) =
+proc wantsCovariantOpenArray(s: openArray[ref Animal]) =
   for a in s: echo a.x
 
-proc modifiesCovariantOperArray(s: var openarray[ref Animal]) =
+proc modifiesCovariantOpenArray(s: var openArray[ref Animal]) =
   for a in s: echo a.x
 
-proc modifiesDerivedOperArray(s: var openarray[ref Dog]) =
+proc modifiesDerivedOpenArray(s: var openArray[ref Dog]) =
   for a in s: echo a.x
 
-proc wantsNonCovariantOperArray(s: openarray[Animal]) =
+proc wantsNonCovariantOpenArray(s: openArray[Animal]) =
   for a in s: echo a.x
 
 proc wantsCovariantArray(s: array[2, ref Animal]) =
@@ -164,7 +164,7 @@ vcat.x = "cat value"
 reject:
   vcat = vdog
 
-# XXX: The next two cases seem incosistent, perhaps we should change the rules
+# XXX: The next two cases seem inconsistent, perhaps we should change the rules
 accept:
   # truncating copies are allowed
   var vanimal: Animal = vdog
@@ -199,15 +199,15 @@ accept:
   wantsCovariantSeq3(@[AnimalRef(cat), dog])
   wantsCovariantSeq3(@[cat, dog])
 
-  wantsCovariantOperArray([cat, dog])
+  wantsCovariantOpenArray([cat, dog])
 
 acceptWithCovariance:
   wantsCovariantSeq1(@[cat, cat])
   wantsCovariantSeq2(@[dog, makeDerivedRef("dog X")])
   # XXX: wantsCovariantSeq3(@[cat, cat])
 
-  wantsCovariantOperArray(@[cat, cat])
-  wantsCovariantOperArray([dog, dog])
+  wantsCovariantOpenArray(@[cat, cat])
+  wantsCovariantOpenArray([dog, dog])
 else:
   echo "cat"
   echo "cat"
@@ -226,7 +226,7 @@ accept:
   modifiesDerivedArray(dogRefsArray)
   modifiesDerivedSeq(dogRefs)
 
-reject modifiesCovariantSeq(dogRefs)
+reject modifiesCovariantSeqd(ogRefs)
 reject modifiesCovariantSeq(addr(dogRefs))
 reject modifiesCovariantSeq(dogRefs.addr)
 
@@ -245,19 +245,19 @@ when false:
   wantsNonCovariantArray animalValuesArray
 
 reject wantsNonCovariantSeq(dogRefs)
-reject modifiesCovariantOperArray(dogRefs)
+reject modifiesCovariantOpenArray(dogRefs)
 reject wantsNonCovariantArray(dogRefsArray)
 reject wantsNonCovariantSeq(dogValues)
 reject wantsNonCovariantArray(dogValuesArray)
 reject modifiesValueArray()
 
-modifiesDerivedOperArray dogRefs
-reject modifiesDerivedOperArray(dogValues)
-reject modifiesDerivedOperArray(animalRefs)
+modifiesDerivedOpenArray dogRefs
+reject modifiesDerivedOpenArray(dogValues)
+reject modifiesDerivedOpenArray(animalRefs)
 
-reject wantsNonCovariantOperArray(animalRefs)
-reject wantsNonCovariantOperArray(dogRefs)
-reject wantsNonCovariantOperArray(dogValues)
+reject wantsNonCovariantOpenArray(animalRefs)
+reject wantsNonCovariantOpenArray(dogRefs)
+reject wantsNonCovariantOpenArray(dogValues)
 
 var animalRefSeq: seq[ref Animal]
 
@@ -300,12 +300,12 @@ template <class T> struct ARR { typedef T DataType[2]; DataType data; };
 """.}
 
 type
-  MyPtr {.importcpp: "'0 *"} [out T] = object
+  MyPtr[out T] {.importcpp: "'0 *"}  = object
 
-  MySeq {.importcpp: "ARR<'0>", nodecl} [out T] = object
+  MySeq[out T] {.importcpp: "ARR<'0>", nodecl}  = object
     data: array[2, T]
 
-  MyAction {.importcpp: "FN<'0>::type"} [in T] = object
+  MyAction[in T] {.importcpp: "FN<'0>::type"}  = object
 
 var
   cAnimal: MyPtr[Animal]
@@ -415,4 +415,3 @@ reject usesAddressOfAnimalRefSeq(addr cAnimalValues)
 reject usesAddressOfAnimalRefSeq(addr cDogValues)
 accept usesAddressOfAnimalRefSeq(addr cAnimals)
 reject usesAddressOfAnimalRefSeq(addr cDogs)
-
diff --git a/tests/cpp/tcppraise.nim b/tests/cpp/tcppraise.nim
index f359a2e8b..f03956d4c 100644
--- a/tests/cpp/tcppraise.nim
+++ b/tests/cpp/tcppraise.nim
@@ -5,6 +5,9 @@ bar
 Need odd and >= 3 digits##
 baz
 caught
+--------
+Triggered raises2
+Raising ValueError
 '''
 """
 
@@ -35,3 +38,34 @@ try:
 except:
   echo "caught"
 
+
+# issue 5549
+
+var strs: seq[string] = @[]
+
+try:
+  discard
+finally:
+  for foobar in strs:
+    discard
+
+
+# issue #11118
+echo "--------"
+proc raises() =
+  raise newException(ValueError, "Raising ValueError")
+
+proc raises2() =
+  try:
+    raises()
+  except ValueError as e:
+    echo "Triggered raises2"
+    raise e
+
+try:
+  raises2()
+except:
+  echo getCurrentExceptionMsg()
+  discard
+
+doAssert: getCurrentException() == nil
diff --git a/tests/cpp/tdont_init_instantiation.nim b/tests/cpp/tdont_init_instantiation.nim
index fe487fba0..a13a3f6b4 100644
--- a/tests/cpp/tdont_init_instantiation.nim
+++ b/tests/cpp/tdont_init_instantiation.nim
@@ -20,7 +20,7 @@ template <typename X> class C {
 };
 """.}
 
-type C{.importcpp, header: "<stdio.h>", nodecl.} [X] = object
+type C[X] {.importcpp, header: "<stdio.h>", nodecl.} = object
 proc mkC[X]: C[X] {.importcpp: "C<'*0>()", constructor, nodecl.}
 
 proc foo(): C[int] =
diff --git a/tests/cpp/tembarrassing_generic_failure.nim b/tests/cpp/tembarrassing_generic_bug.nim
index 4b5050948..4b5050948 100644
--- a/tests/cpp/tembarrassing_generic_failure.nim
+++ b/tests/cpp/tembarrassing_generic_bug.nim
diff --git a/tests/cpp/temitlist.nim b/tests/cpp/temitlist.nim
index e88bf45bd..9170be079 100644
--- a/tests/cpp/temitlist.nim
+++ b/tests/cpp/temitlist.nim
@@ -1,12 +1,14 @@
 discard """
   targets: "cpp"
-  output: '''6.0
+  output: '''
+6.0
 0'''
+disabled: "windows" # pending bug #18011
 """
 
 # bug #4730
 
-type Vector* {.importcpp: "std::vector", header: "<vector>".}[T] = object
+type Vector*[T] {.importcpp: "std::vector", header: "<vector>".} = object
 
 template `[]=`*[T](v: var Vector[T], key: int, val: T) =
   {.emit: [v, "[", key, "] = ", val, ";"].}
@@ -27,9 +29,9 @@ main()
 #bug #6837
 type StdString {.importCpp: "std::string", header: "<string>", byref.} = object
 proc initString(): StdString {.constructor, importCpp: "std::string(@)", header: "<string>".}
-proc size(this: var StdString): csize {.importCpp: "size", header: "<string>".}
+proc size(this: var StdString): csize_t {.importCpp: "size", header: "<string>".}
 
-proc f(): csize =
+proc f(): csize_t =
   var myString: StdString = initString()
   return myString.size()
 
diff --git a/tests/cpp/tempty_generic_obj.nim b/tests/cpp/tempty_generic_obj.nim
index b61c699f6..6125190b4 100644
--- a/tests/cpp/tempty_generic_obj.nim
+++ b/tests/cpp/tempty_generic_obj.nim
@@ -1,14 +1,16 @@
 discard """
   targets: "cpp"
-  output: '''int
+  output: '''
+int
 float'''
+disabled: "windows" # pending bug #18011
 """
 
 import typetraits
 
 # bug #4625
 type
-  Vector {.importcpp: "std::vector<'0 >", header: "vector".} [T] = object
+  Vector[T] {.importcpp: "std::vector<'0 >", header: "vector".} = object
 
 proc initVector[T](): Vector[T] {.importcpp: "'0(@)", header: "vector", constructor.}
 
@@ -24,7 +26,7 @@ vf.doSomething() # Nim uses doSomething[int] here in C++
 # Alternative definition:
 # https://github.com/nim-lang/Nim/issues/7653
 
-type VectorAlt* {.importcpp: "std::vector", header: "<vector>", nodecl.} [T] = object
+type VectorAlt*[T] {.importcpp: "std::vector", header: "<vector>", nodecl.} = object
 proc mkVector*[T]: VectorAlt[T] {.importcpp: "std::vector<'*0>()", header: "<vector>", constructor, nodecl.}
 
 proc foo(): VectorAlt[cint] =
@@ -36,3 +38,10 @@ proc bar(): VectorAlt[cstring] =
 var x = foo()
 var y = bar()
 
+proc init[T; Self: Vector[T]](_: typedesc[Self], n: csize_t): Vector[T]
+  {.importcpp: "std::vector<'*0>(@)", header: "<vector>", constructor, nodecl.}
+proc size[T](x: Vector[T]): csize_t
+  {.importcpp: "#.size()", header: "<vector>", nodecl.}
+
+var z = Vector[int16].init(32)
+assert z.size == 32
diff --git a/tests/cpp/tenum_set.nim b/tests/cpp/tenum_set.nim
new file mode 100644
index 000000000..6afed722f
--- /dev/null
+++ b/tests/cpp/tenum_set.nim
@@ -0,0 +1,9 @@
+discard """
+targets: "cpp"
+output: "{A, B, C}"
+"""
+
+type Enum {.importcpp: "namespaced::Enum", header: "enum.hpp".} = enum A, B, C
+
+var vals = {low(Enum) .. high(Enum)}
+echo vals
diff --git a/tests/cpp/tevalorder.nim b/tests/cpp/tevalorder.nim
new file mode 100644
index 000000000..4764f3aca
--- /dev/null
+++ b/tests/cpp/tevalorder.nim
@@ -0,0 +1,18 @@
+discard """
+  output: '''0
+1
+2'''
+targets: "cpp"
+"""
+
+# bug #8202
+var current: int = 0
+
+proc gen(): string = current.inc; $(current - 1)
+
+proc allOut(a, b, c: string) =
+    echo a
+    echo b
+    echo c
+
+allOut(gen(), gen(), gen())
diff --git a/tests/cpp/texportc.nim b/tests/cpp/texportc.nim
new file mode 100644
index 000000000..3a2fa8748
--- /dev/null
+++ b/tests/cpp/texportc.nim
@@ -0,0 +1,22 @@
+discard """
+  targets: "c cpp"
+"""
+
+var fun0 {.importc.}: int
+proc fun1() {.importc.}
+proc fun2() {.importc: "$1".}
+proc fun3() {.importc: "fun3Bis".}
+
+when defined cpp:
+  # proc funx1() {.importcpp.} # this does not work yet
+  proc funx1() {.importc: "_Z5funx1v".}
+
+doAssert fun0 == 10
+fun1()
+fun2()
+fun3()
+
+when defined cpp:
+  funx1()
+
+import ./mexportc
diff --git a/tests/cpp/tfam.nim b/tests/cpp/tfam.nim
new file mode 100644
index 000000000..6bd89fe24
--- /dev/null
+++ b/tests/cpp/tfam.nim
@@ -0,0 +1,7 @@
+discard """
+  targets: "cpp"
+"""
+type 
+  Test {.importcpp, header: "fam.h".} = object
+
+let test = newSeq[Test]()
\ No newline at end of file
diff --git a/tests/cpp/tinitializers.nim b/tests/cpp/tinitializers.nim
new file mode 100644
index 000000000..0199fb96b
--- /dev/null
+++ b/tests/cpp/tinitializers.nim
@@ -0,0 +1,60 @@
+discard """
+  cmd: "nim cpp $file"
+"""
+
+{.emit:"""/*TYPESECTION*/
+struct CppStruct {
+  CppStruct(int x, char* y): x(x), y(y){}
+  void doSomething() {}
+  int x;
+  char* y;
+};
+""".}
+type
+  CppStruct {.importcpp, inheritable.} = object
+  ChildStruct = object of CppStruct
+  HasCppStruct = object
+    cppstruct: CppStruct 
+
+proc constructCppStruct(a:cint = 5, b:cstring = "hello"): CppStruct {.importcpp: "CppStruct(@)", constructor.}
+proc doSomething(this: CppStruct) {.importcpp.}
+proc returnCppStruct(): CppStruct = discard
+proc initChildStruct: ChildStruct = ChildStruct() 
+proc makeChildStruct(): ChildStruct {.constructor:"""ChildStruct(): CppStruct(5, "10")""".} = discard
+proc initHasCppStruct(x: cint): HasCppStruct =
+  HasCppStruct(cppstruct: constructCppStruct(x))
+
+proc main =
+  var hasCppStruct = initHasCppStruct(2) #generates cppstruct = { 10 } inside the struct
+  hasCppStruct.cppstruct.doSomething() 
+  discard returnCppStruct() #generates result = { 10 } 
+  discard initChildStruct() #generates ChildStruct temp ({}) bypassed with makeChildStruct
+  (proc (s:CppStruct) = discard)(CppStruct()) #CppStruct temp ({10})
+main()
+
+
+#Should handle ObjectCalls
+{.emit:"""/*TYPESECTION*/
+struct Foo {
+};
+struct Boo {
+  Boo(int x, char* y, Foo f): x(x), y(y), foo(f){}
+  int x;
+  char* y;
+  Foo foo;
+};
+""".}
+type
+  Foo {.importcpp, inheritable, bycopy.} = object
+  Boo {.importcpp, inheritable.} = object
+    x: int32
+    y: cstring
+    foo: Foo
+
+proc makeBoo(a:cint = 10, b:cstring = "hello", foo: Foo = Foo()): Boo {.importcpp, constructor.}
+
+proc main2() = 
+  let cppStruct = makeBoo()
+  (proc (s:Boo) = discard)(Boo()) 
+
+main2()
\ No newline at end of file
diff --git a/tests/cpp/tmember.nim b/tests/cpp/tmember.nim
new file mode 100644
index 000000000..1a5b6fd97
--- /dev/null
+++ b/tests/cpp/tmember.nim
@@ -0,0 +1,75 @@
+discard """
+  targets: "cpp"
+  cmd: "nim cpp $file"
+  output: '''
+2
+false
+hello foo
+hello boo
+hello boo
+FunctorSupport!
+static
+static
+destructing
+destructing
+'''
+"""
+proc print(s: cstring) {.importcpp:"printf(@)", header:"<stdio.h>".}
+
+type
+  Doo  {.exportc.} = object
+    test: int
+
+proc memberProc(f: Doo) {.exportc, member.} = 
+  echo $f.test
+
+proc destructor(f: Doo) {.member: "~'1()", used.} = 
+  print "destructing\n"
+
+proc `==`(self, other: Doo): bool {.member:"operator==('2 const & #2) const -> '0"} = 
+  self.test == other.test
+
+let doo = Doo(test: 2)
+doo.memberProc()
+echo doo == Doo(test: 1)
+
+#virtual
+proc newCpp*[T](): ptr T {.importcpp:"new '*0()".}
+type 
+  Foo {.exportc.} = object of RootObj
+  FooPtr = ptr Foo
+  Boo = object of Foo
+  BooPtr = ptr Boo
+
+proc salute(self: FooPtr) {.member: "virtual $1()".} = 
+  echo "hello foo"
+
+proc salute(self: BooPtr) {.member: "virtual $1()".} =
+  echo "hello boo"
+
+let foo = newCpp[Foo]()
+let boo = newCpp[Boo]()
+let booAsFoo = cast[FooPtr](newCpp[Boo]())  
+
+foo.salute()
+boo.salute()
+booAsFoo.salute()
+
+type
+  NimFunctor = object
+    discard
+proc invoke(f: NimFunctor, n:int) {.member:"operator ()('2 #2)" .} = 
+  echo "FunctorSupport!"
+
+{.experimental: "callOperator".}
+proc `()`(f: NimFunctor, n:int) {.importcpp:"#(@)" .} 
+NimFunctor()(1)
+
+#static
+proc staticProc(self: FooPtr) {.member: "static $1()".} = 
+  echo "static"
+
+proc importedStaticProc() {.importcpp:"Foo::staticProc()".}
+
+foo.staticProc()
+importedStaticProc()
diff --git a/tests/cpp/tmember_forward_declaration.nim b/tests/cpp/tmember_forward_declaration.nim
new file mode 100644
index 000000000..2f4a79daa
--- /dev/null
+++ b/tests/cpp/tmember_forward_declaration.nim
@@ -0,0 +1,27 @@
+discard """
+  targets: "cpp"
+  cmd: "nim cpp $file"
+  output: '''
+abc called
+def called
+abc called
+'''
+"""
+
+type Foo = object
+
+proc abc(this: Foo, x: int): void {.member: "$1('2 #2)".}
+proc def(this: Foo, y: int): void {.virtual: "$1('2 #2)".}
+
+proc abc(this: Foo, x: int): void =
+  echo "abc called"
+  if x > 0:
+    this.def(x - 1)
+
+proc def(this: Foo, y: int): void =
+  echo "def called"
+  this.abc(y)
+
+var x = Foo()
+x.abc(1)
+
diff --git a/tests/cpp/tnativesockets.nim b/tests/cpp/tnativesockets.nim
index c62008050..1284811b5 100644
--- a/tests/cpp/tnativesockets.nim
+++ b/tests/cpp/tnativesockets.nim
@@ -1,5 +1,6 @@
 discard """
   targets: "cpp"
+outputsub: ""
 """
 
 import nativesockets
diff --git a/tests/cpp/tnoinitfield.nim b/tests/cpp/tnoinitfield.nim
new file mode 100644
index 000000000..4deffece8
--- /dev/null
+++ b/tests/cpp/tnoinitfield.nim
@@ -0,0 +1,30 @@
+discard """
+  targets: "cpp"
+  cmd: "nim cpp $file"
+  output: '''
+'''
+"""
+{.emit: """/*TYPESECTION*/
+  struct Foo {
+    Foo(int a){};
+  };
+  struct Boo {
+    Boo(int a){};
+  };
+
+  """.}
+
+type 
+  Foo {.importcpp.} = object
+  Boo {.importcpp, noInit.} = object
+  Test {.exportc.} = object
+    foo {.noInit.}: Foo
+    boo: Boo
+
+proc makeTest(): Test {.constructor: "Test() : foo(10), boo(1)".} = 
+  discard
+
+proc main() = 
+  var t = makeTest()
+
+main()
\ No newline at end of file
diff --git a/tests/cpp/torc.nim b/tests/cpp/torc.nim
new file mode 100644
index 000000000..9f1a41a21
--- /dev/null
+++ b/tests/cpp/torc.nim
@@ -0,0 +1,75 @@
+discard """
+  targets: "cpp"
+  matrix: "--gc:orc"
+"""
+
+import std/options
+
+# bug #18410
+type
+  O = object of RootObj
+   val: pointer
+
+proc p(): Option[O] = none(O)
+
+doAssert $p() == "none(O)"
+
+# bug #17351
+type
+  Foo = object of RootObj
+  Foo2 = object of Foo
+  Bar = object
+    x: Foo2
+
+var b = Bar()
+discard b
+
+# bug #4678
+{.emit: """/*TYPESECTION*/
+enum class SomeEnum {A,B,C};
+""".}
+type
+  EnumVector[T: enum] {.importcpp: "std::vector", header: "vector".} = object
+  SomeEnum {.importcpp, nodecl.} = enum
+    A,B,C
+
+proc asVector*[T](t: T): EnumVector[T] =
+  discard
+# Nim generates this signature here:
+# N_NIMCALL(std::vector<> , asvector_106028_3197418230)(SomeEnum t0)
+
+discard asVector(SomeEnum.A)
+
+
+block: # bug #10219
+  type
+    Vector[T]  {.importcpp: "std::vector", header: "vector".} = object
+
+  proc initVector[T](n: csize_t): Vector[T] 
+      {.importcpp: "std::vector<'*0>(@)", header: "vector".}
+
+  proc unsafeIndex[T](this: var Vector[T], i: csize_t): var T 
+      {.importcpp: "#[#]", header: "vector".}
+
+  proc `[]`[T](this: var Vector[T], i: Natural): var T {.inline, noinit.} =
+    when compileOption("boundChecks"):
+        # this.checkIndex i
+        discard
+    result = this.unsafeIndex(csize_t(i))
+
+  var v1 = initVector[int](10)
+  doAssert v1[0] == 0
+
+block: # bug #12703 bug #19588
+  type
+    cstringConstImpl {.importc:"const char*".} = cstring
+    constChar = distinct cstringConstImpl
+
+  {.emit: """
+  const char* foo() {
+    return "hello";
+  }
+  """.}
+  proc foo(): constChar {.importcpp.} # change to importcpp for C++ backend
+  doAssert $(foo().cstring) == "hello"
+
diff --git a/tests/cpp/tpassbypragmas.nim b/tests/cpp/tpassbypragmas.nim
new file mode 100644
index 000000000..f4301656a
--- /dev/null
+++ b/tests/cpp/tpassbypragmas.nim
@@ -0,0 +1,27 @@
+discard """
+  targets: "cpp"
+  cmd: "nim cpp $file"
+"""
+{.emit:"""/*TYPESECTION*/
+
+  template<typename T>
+  struct Box {
+      T first;
+  };
+  struct Foo {
+  void test(void (*func)(Box<Foo>& another)){
+
+    };
+  };
+""".}
+
+type 
+  Foo {.importcpp.} = object
+  Box[T] {.importcpp:"Box<'0>".} = object
+    first: T
+
+proc test(self: Foo, fn: proc(another {.byref.}: Box[Foo]) {.cdecl.}) {.importcpp.}
+
+proc fn(another {.byref.} : Box[Foo]) {.cdecl.} = discard
+
+Foo().test(fn)
\ No newline at end of file
diff --git a/tests/cpp/tretvar.nim b/tests/cpp/tretvar.nim
new file mode 100644
index 000000000..0c3765346
--- /dev/null
+++ b/tests/cpp/tretvar.nim
@@ -0,0 +1,39 @@
+discard """
+  targets: "cpp"
+  output: '''test1
+xest1
+'''
+"""
+{.passC: "-std=c++14".}
+
+{.experimental: "dotOperators".}
+
+import macros
+
+type
+  stdString {.importcpp: "std::string", header: "<string>".} = object
+  stdUniquePtr[T] {.importcpp: "std::unique_ptr", header: "<memory>".} = object
+
+proc c_str(a: stdString): cstring {.importcpp: "(char *)(#.c_str())", header: "<string>".}
+
+proc len(a: stdString): csize_t {.importcpp: "(#.length())", header: "<string>".}
+
+proc setChar(a: var stdString, i: csize_t, c: char) {.importcpp: "(#[#] = #)", header: "<string>".}
+
+proc `*`*[T](this: stdUniquePtr[T]): var T {.noSideEffect, importcpp: "(* #)", header: "<memory>".}
+
+proc make_unique_str(a: cstring): stdUniquePtr[stdString] {.importcpp: "std::make_unique<std::string>(#)", header: "<string>".}
+
+macro `.()`*[T](this: stdUniquePtr[T], name: untyped, args: varargs[untyped]): untyped =
+  result = nnkCall.newTree(
+    nnkDotExpr.newTree(
+      newNimNode(nnkPar).add(prefix(this, "*")),
+      name
+    )
+  )
+  copyChildrenTo(args, result)
+
+var val = make_unique_str("test1")
+echo val.c_str()
+val.setChar(0, 'x')
+echo val.c_str()
diff --git a/tests/cpp/tsigbreak.nim b/tests/cpp/tsigbreak.nim
index 9a381d84f..14d29adf7 100644
--- a/tests/cpp/tsigbreak.nim
+++ b/tests/cpp/tsigbreak.nim
@@ -1,5 +1,6 @@
 discard """
   targets: "cpp"
+  action: compile
 """
 
 import tables, lists
diff --git a/tests/cpp/ttemplatetype.nim b/tests/cpp/ttemplatetype.nim
index ef24e4cdc..bf243ac43 100644
--- a/tests/cpp/ttemplatetype.nim
+++ b/tests/cpp/ttemplatetype.nim
@@ -3,7 +3,7 @@ discard """
 """
 
 type
-  Map {.importcpp: "std::map", header: "<map>".} [T,U] = object
+  Map[T,U] {.importcpp: "std::map", header: "<map>".} = object
 
 proc cInitMap(T: typedesc, U: typedesc): Map[T,U] {.importcpp: "std::map<'*1,'*2>()", nodecl.}
 
diff --git a/tests/cpp/tterminate_handler.nim b/tests/cpp/tterminate_handler.nim
new file mode 100644
index 000000000..c5ccef53c
--- /dev/null
+++ b/tests/cpp/tterminate_handler.nim
@@ -0,0 +1,10 @@
+discard """
+  targets: "cpp"
+  outputsub: "Error: unhandled unknown cpp exception"
+  exitcode: 1
+  disabled: true
+"""
+type Crap {.importcpp: "int".} = object
+
+var c: Crap
+raise c
diff --git a/tests/cpp/ttypeinfo.nim b/tests/cpp/ttypeinfo1.nim
index 97825f452..97825f452 100644
--- a/tests/cpp/ttypeinfo.nim
+++ b/tests/cpp/ttypeinfo1.nim
diff --git a/tests/cpp/tvector_iterator.nim b/tests/cpp/tvector_iterator.nim
index 4d686955f..c3886c547 100644
--- a/tests/cpp/tvector_iterator.nim
+++ b/tests/cpp/tvector_iterator.nim
@@ -12,8 +12,8 @@ struct Vector {
 """.}
 
 type
-  Vector {.importcpp: "Vector".} [T] = object
-  VectorIterator {.importcpp: "Vector<'0>::Iterator".} [T] = object
+  Vector[T] {.importcpp: "Vector".} = object
+  VectorIterator[T] {.importcpp: "Vector<'0>::Iterator".} = object
 
 var x: VectorIterator[void]
 
diff --git a/tests/cpp/tvirtual.nim b/tests/cpp/tvirtual.nim
new file mode 100644
index 000000000..385d052b8
--- /dev/null
+++ b/tests/cpp/tvirtual.nim
@@ -0,0 +1,126 @@
+discard """
+  targets: "cpp"
+  cmd: "nim cpp $file"
+  output: '''
+hello foo
+hello boo
+hello boo
+Const Message: hello world
+NimPrinter: hello world
+NimPrinterConstRef: hello world
+NimPrinterConstRefByRef: hello world
+'''
+"""
+
+{.emit:"""/*TYPESECTION*/
+#include <iostream>
+  class CppPrinter {
+  public:
+    
+    virtual void printConst(char* message) const {
+        std::cout << "Const Message: " << message << std::endl;
+    }
+    virtual void printConstRef(char* message, const int& flag) const {
+        std::cout << "Const Ref Message: " << message << std::endl;
+    }  
+    virtual void printConstRef2(char* message, const int& flag) const {
+        std::cout << "Const Ref2 Message: " << message << std::endl;
+    }  
+    
+};
+""".}
+
+proc newCpp*[T](): ptr T {.importcpp:"new '*0()".}
+type 
+  Foo = object of RootObj
+  FooPtr = ptr Foo
+  Boo = object of Foo
+  BooPtr = ptr Boo
+  CppPrinter {.importcpp, inheritable.} = object
+  NimPrinter {.exportc.} = object of CppPrinter
+
+proc salute(self: FooPtr) {.virtual.} = 
+  echo "hello foo"
+
+proc salute(self: BooPtr) {.virtual.} =
+  echo "hello boo"
+
+let foo = newCpp[Foo]()
+let boo = newCpp[Boo]()
+let booAsFoo = cast[FooPtr](newCpp[Boo]())
+
+#polymorphism works
+foo.salute()
+boo.salute()
+booAsFoo.salute()
+let message = "hello world".cstring
+
+proc printConst(self: CppPrinter, message: cstring) {.importcpp.}
+CppPrinter().printConst(message)
+
+#notice override is optional. 
+#Will make the cpp compiler to fail if not virtual function with the same signature if found in the base type
+proc printConst(self: NimPrinter, message: cstring) {.virtual:"$1('2 #2) const override".} =
+  echo "NimPrinter: " & $message
+
+proc printConstRef(self: NimPrinter, message: cstring, flag: int32) {.virtual:"$1('2 #2, const '3& #3 ) const override".} =
+  echo "NimPrinterConstRef: " & $message
+
+proc printConstRef2(self: NimPrinter, message: cstring, flag {.byref.}: int32) {.virtual:"$1('2 #2, const '3 #3 ) const override".} =
+  echo "NimPrinterConstRefByRef: " & $message
+
+NimPrinter().printConst(message)
+var val : int32 = 10
+NimPrinter().printConstRef(message, val)
+NimPrinter().printConstRef2(message, val)
+
+#bug 22269
+type Doo = object
+proc naiveMember(x: Doo): int {. virtual .} = 2
+discard naiveMember(Doo())
+
+#asmnostackframe works with virtual
+{.emit:"""/*TYPESECTION*/
+  template<typename T>
+  struct Box {
+      T* first;
+     
+      Box(int x){
+        first = new T(x);
+      };
+  };
+  struct Inner {
+    int val;
+    //Coo() = default;
+    Inner(int x){
+      val = x;
+    };
+  };
+  struct Base {
+    virtual Box<Inner> test() = 0;
+  };
+""".}
+
+type 
+  Inner {.importcpp.} = object
+  Base {.importcpp, inheritable.} = object
+  Child  = object of Base
+  Box[T] {.importcpp, inheritable.} = object
+    first: T
+
+proc makeBox[T](x:int32): Box[T] {.importcpp:"Box<'0>(@)", constructor.}
+
+proc test(self: Child): Box[Inner] {.virtual, asmnostackframe.} = 
+  let res {.exportc.} = makeBox[Inner](100)
+  {.emit:"return res;".}
+  
+
+discard Child().test() 
+
+import virtualptr
+
+#We dont want to pull Loo directly by using it as we are testing that the pointer pulls it. 
+proc makeMoo(): Moo {.importcpp:"{ new Loo() }".}
+
+makeMoo().loo.salute()
+
diff --git a/tests/cpp/virtualptr.nim b/tests/cpp/virtualptr.nim
new file mode 100644
index 000000000..f96264081
--- /dev/null
+++ b/tests/cpp/virtualptr.nim
@@ -0,0 +1,9 @@
+type 
+  Loo* {.exportc.} = object
+  LooPtr* = ptr Loo
+  Moo* {.exportc.} = object 
+    loo*: LooPtr
+
+
+proc salute*(foo: LooPtr) {.virtual.} = 
+  discard 
diff --git a/tests/defaultprocparam/tdefaultprocparam.nim b/tests/defaultprocparam/tdefaultprocparam.nim
deleted file mode 100644
index 23ecf72e9..000000000
--- a/tests/defaultprocparam/tdefaultprocparam.nim
+++ /dev/null
@@ -1,4 +0,0 @@
-
-import mdefaultprocparam
-
-p()
diff --git a/tests/defer/t22309.nim b/tests/defer/t22309.nim
new file mode 100644
index 000000000..34ca4843b
--- /dev/null
+++ b/tests/defer/t22309.nim
@@ -0,0 +1,11 @@
+block:
+  defer:
+    let a = 42
+  doAssert not declared(a)
+
+proc lol() =
+  defer:
+    let a = 42
+  doAssert not declared(a)
+
+lol()
diff --git a/tests/deprecated/tdeprecated.nim b/tests/deprecated/tdeprecated.nim
deleted file mode 100644
index 955a7f6ad..000000000
--- a/tests/deprecated/tdeprecated.nim
+++ /dev/null
@@ -1,9 +0,0 @@
-discard """
-  nimout: "a is deprecated [Deprecated]"
-"""
-
-var
-  a {.deprecated.}: array[0..11, int]
-
-a[8] = 1
-
diff --git a/tests/destructor/const_smart_ptr.nim b/tests/destructor/const_smart_ptr.nim
new file mode 100644
index 000000000..25dd46500
--- /dev/null
+++ b/tests/destructor/const_smart_ptr.nim
@@ -0,0 +1,75 @@
+type
+  ConstPtr*[T] = object
+    val: ptr T
+
+proc `=destroy`*[T](p: ConstPtr[T]) =
+  if p.val != nil:
+    `=destroy`(p.val[])
+    dealloc(p.val)
+
+proc `=copy`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.error.}
+
+proc `=sink`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.inline.} =
+  if dest.val != nil and dest.val != src.val:
+    `=destroy`(dest)
+  dest.val = src.val
+
+proc newConstPtr*[T](val: sink T): ConstPtr[T] {.inline.} =
+  result.val = cast[type(result.val)](alloc(sizeof(result.val[])))
+  reset(result.val[])
+  result.val[] = val
+
+converter convertConstPtrToObj*[T](p: ConstPtr[T]): lent T =
+  result = p.val[]
+
+
+#-------------------------------------------------------------
+
+type
+  MySeqNonCopyable* = object
+    len: int
+    data: ptr UncheckedArray[float]
+
+proc `=destroy`*(m: MySeqNonCopyable) {.inline.} =
+  if m.data != nil:
+    deallocShared(m.data)
+
+proc `=copy`*(m: var MySeqNonCopyable, m2: MySeqNonCopyable) {.error.}
+
+proc `=sink`*(m: var MySeqNonCopyable, m2: MySeqNonCopyable) {.inline.} =
+  if m.data != m2.data:
+    if m.data != nil:
+      `=destroy`(m)
+    m.len = m2.len
+    m.data = m2.data
+
+proc len*(m: MySeqNonCopyable): int {.inline.} = m.len
+
+proc `[]`*(m: MySeqNonCopyable; i: int): float {.inline.} =
+  m.data[i.int]
+
+proc `[]=`*(m: var MySeqNonCopyable; i: int, val: float) {.inline.} =
+  m.data[i.int] = val
+
+proc setTo(s: var MySeqNonCopyable, val: float) =
+  for i in 0..<s.len.int:
+    s.data[i] = val
+
+proc newMySeq*(size: int, initial_value = 0.0): MySeqNonCopyable =
+  result.len = size
+  if size > 0:
+    result.data = cast[ptr UncheckedArray[float]](createShared(float, size))
+  result.setTo(initial_value)
+
+#----------------------------------------------------------------------
+
+
+proc test*(x1: int): ConstPtr[MySeqNonCopyable] {.inline.} = # remove inline here to make it work as expected
+  if x1 == 0:
+    let x = newMySeq(1, 0.0)
+    result = newConstPtr(x)
+  else:
+    let y = newMySeq(x1, 0.0)
+    result = newConstPtr(y)
+
+discard test(10)
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/nim.cfg b/tests/destructor/nim.cfg
new file mode 100644
index 000000000..7c148b797
--- /dev/null
+++ b/tests/destructor/nim.cfg
@@ -0,0 +1 @@
+--sinkInference:on
diff --git a/tests/destructor/objFile.nim b/tests/destructor/objFile.nim
new file mode 100644
index 000000000..436c090d1
--- /dev/null
+++ b/tests/destructor/objFile.nim
@@ -0,0 +1,8 @@
+type Obj* = object
+  v*: int
+
+proc `=destroy`(this: var Obj) =
+  echo "igotdestroyed"
+  this.v = -1
+
+var test* = Obj(v: 42)
diff --git a/tests/destructor/smart_ptr.nim b/tests/destructor/smart_ptr.nim
new file mode 100644
index 000000000..5079dc9db
--- /dev/null
+++ b/tests/destructor/smart_ptr.nim
@@ -0,0 +1,43 @@
+
+type
+  SharedPtr*[T] = object
+    val: ptr tuple[atomicCounter: int, value: T]
+
+proc `=destroy`*[T](p: var SharedPtr[T]) =
+  mixin `=destroy`
+  if p.val != nil:
+    let c = atomicDec(p.val[].atomicCounter)
+    if c == 0:
+      `=destroy`(p.val.value)
+      freeShared(p.val)
+    p.val = nil
+
+proc `=`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) {.inline.} =
+  if dest.val != src.val:
+    if dest.val != nil:
+      `=destroy`(dest)
+    if src.val != nil:
+      discard atomicInc(src.val[].atomicCounter)
+    dest.val = src.val
+
+proc newSharedPtr*[T](val: sink T): SharedPtr[T] =
+  result.val = cast[type(result.val)](allocShared0(sizeof(result.val[])))
+  result.val.atomicCounter = 1
+  result.val.value = val
+
+func get*[T](p: SharedPtr[T]): var T {.inline.} =
+  p.val.value
+
+func isNil*[T](p: SharedPtr[T]): bool {.inline.} =
+  p.val == nil
+
+proc cas*[T](p, old_val: var SharedPtr[T], new_val: SharedPtr[T]): bool {.inline.} =
+  if old_val.val == new_val.val:
+    result = true
+  else:
+    result = cas(p.val.addr, old_val.val, new_val.val)
+    if result:
+      `=destroy`(old_val)
+      if new_val.val != nil:
+        discard atomicInc(new_val.val[].atomicCounter)
+
diff --git a/tests/destructor/t12037.nim b/tests/destructor/t12037.nim
new file mode 100644
index 000000000..30266690f
--- /dev/null
+++ b/tests/destructor/t12037.nim
@@ -0,0 +1,34 @@
+discard """
+  cmd: '''nim c --gc:arc $file'''
+  output: '''
+showing original type, length, and contents seq[int] 1 @[42]
+copy length and contents 1 @[42]
+'''
+"""
+
+proc test() =
+  var sq1 = @[42]
+  echo "showing original type, length, and contents ", sq1.typeof, " ", sq1.len, " ", sq1
+  doAssert cast[int](sq1[0].addr) != 0
+  var sq2 = sq1 # copy of original
+  echo "copy length and contents ", sq2.len, " ", sq2
+  doAssert cast[int](sq2[0].addr) != 0
+  doAssert cast[int](sq1[0].addr) != 0
+
+test()
+
+
+#############################################
+### bug 12820
+import tables
+var t = initTable[string, seq[ptr int]]()
+discard t.hasKeyOrPut("f1", @[])
+
+
+#############################################
+### bug #12989
+proc bug(start: (seq[int], int)) =
+  let (s, i) = start
+
+let input = @[0]
+bug((input, 0))
diff --git a/tests/destructor/t16607.nim b/tests/destructor/t16607.nim
new file mode 100644
index 000000000..f98a6d517
--- /dev/null
+++ b/tests/destructor/t16607.nim
@@ -0,0 +1,23 @@
+discard """
+  matrix: "--gc:refc; --gc:arc"
+"""
+
+# bug #16607
+
+type
+  O {.requiresInit.} = object
+    initialized: bool
+
+proc `=destroy`(o: var O) =
+  doAssert o.initialized, "O was destroyed before initialization!"
+
+proc initO(): O =
+  O(initialized: true)
+
+proc pair(): tuple[a, b: O] =
+  result = (a: initO(), b: initO())
+
+proc main() =
+  discard pair()
+
+main()
diff --git a/tests/destructor/t17198.nim b/tests/destructor/t17198.nim
new file mode 100644
index 000000000..098db8245
--- /dev/null
+++ b/tests/destructor/t17198.nim
@@ -0,0 +1,32 @@
+discard """
+  cmd: '''nim c --gc:arc $file'''
+  output: '''
+other
+'''
+"""
+
+import std/macros
+
+macro bigCaseStmt(arg: untyped): untyped =
+  result = nnkCaseStmt.newTree(arg)
+
+  # try to change 2000 to a bigger value if it doesn't crash
+  for x in 0 ..< 2000:
+    result.add nnkOfBranch.newTree(newStrLitNode($x), newStrLitNode($x))
+
+  result.add nnkElse.newTree(newStrLitNode("other"))
+
+macro bigIfElseExpr(): untyped =
+  result = nnkIfExpr.newTree()
+
+  for x in 0 ..< 1000:
+    result.add nnkElifExpr.newTree(newLit(false), newStrLitNode($x))
+
+  result.add nnkElseExpr.newTree(newStrLitNode("other"))
+
+proc test(arg: string): string =
+  echo bigIfElseExpr()
+
+  result = bigCaseStmt(arg)
+
+discard test("test")
diff --git a/tests/destructor/t23748.nim b/tests/destructor/t23748.nim
new file mode 100644
index 000000000..a3738733e
--- /dev/null
+++ b/tests/destructor/t23748.nim
@@ -0,0 +1,31 @@
+discard """
+  matrix: "--gc:refc; --gc:arc"
+  output: '''
+hello 42
+hello 42
+len = 2
+'''
+"""
+
+# bug #23748
+
+type
+  O = ref object
+    s: string
+    cb: seq[proc()]
+
+proc push1(o: O, i: int) =
+  let o = o
+  echo o.s, " ", i
+  o.cb.add(proc() = echo o.s, " ", i)
+
+proc push2(o: O, i: int) =
+  let o = o
+  echo o.s, " ", i
+  proc p() = echo o.s, " ", i
+  o.cb.add(p)
+
+let o = O(s: "hello", cb: @[])
+o.push1(42)
+o.push2(42)
+echo "len = ", o.cb.len
diff --git a/tests/destructor/t23837.nim b/tests/destructor/t23837.nim
new file mode 100644
index 000000000..e219dd6b5
--- /dev/null
+++ b/tests/destructor/t23837.nim
@@ -0,0 +1,51 @@
+discard """
+  output: '''
+Deallocating OwnedString
+HelloWorld
+'''
+  matrix: "--cursorinference:on; --cursorinference:off"
+  target: "c"
+"""
+
+# bug #23837
+{.
+  emit: [
+    """
+#include <stdlib.h>
+#include <string.h>
+char *allocCString() {
+    char *result = (char *) malloc(10 + 1);
+    strcpy(result, "HelloWorld");
+    return result;
+}
+
+"""
+  ]
+.}
+
+proc rawWrapper(): cstring {.importc: "allocCString", cdecl.}
+proc free(p: pointer) {.importc: "free", cdecl.}
+
+# -------------------------
+
+type OwnedString = distinct cstring
+
+proc `=destroy`(s: OwnedString) =
+  free(cstring s)
+  echo "Deallocating OwnedString"
+
+func `$`(s: OwnedString): string {.borrow.}
+
+proc leakyWrapper(): string =
+  let ostring = rawWrapper().OwnedString
+  $ostring
+
+# -------------------------
+
+proc main() =
+  # destructor not called - definitely lost: 11 bytes in 1 blocks
+  # doesn't leak with --cursorInference:off
+  let s = leakyWrapper()
+  echo s
+
+main()
\ No newline at end of file
diff --git a/tests/destructor/t5342.nim b/tests/destructor/t5342.nim
new file mode 100644
index 000000000..0acd5ef9d
--- /dev/null
+++ b/tests/destructor/t5342.nim
@@ -0,0 +1,24 @@
+discard """
+  matrix: "--mm:refc; --mm:arc"
+  targets: "c js"
+  output: '''
+1
+2
+here
+2
+1
+'''
+"""
+
+
+type
+  A = object
+    id: int
+  B = object
+    a: A
+proc `=destroy`(a: var A) = echo a.id
+var x = A(id: 1)
+var y = B(a: A(id: 2))
+`=destroy`(x)
+`=destroy`(y)
+echo "here"
\ No newline at end of file
diff --git a/tests/destructor/t7346.nim b/tests/destructor/t7346.nim
new file mode 100644
index 000000000..3834d39ff
--- /dev/null
+++ b/tests/destructor/t7346.nim
@@ -0,0 +1,14 @@
+discard """
+joinable: false
+"""
+
+# This bug could only be reproduced with --newruntime
+
+type
+  Obj = object
+    a: int
+
+proc `=`(a: var Obj, b: Obj) = discard
+
+let a: seq[Obj] = @[] # bug #7346
+let b = newSeq[Obj]() # bug #7345
diff --git a/tests/destructor/t9440.nim b/tests/destructor/t9440.nim
new file mode 100644
index 000000000..153bb303d
--- /dev/null
+++ b/tests/destructor/t9440.nim
@@ -0,0 +1,52 @@
+discard """
+  matrix: "--gc:refc; --gc:orc; --gc:arc"
+  output: '''
+()
+Destroyed
+()
+Destroyed
+()
+Destroyed
+end
+-------------------------
+()
+Destroyed
+end
+'''
+
+"""
+
+# bug #9440
+block:
+  type
+    X = object
+
+  proc `=destroy`(x: var X) =
+    echo "Destroyed"
+
+  proc main() =
+    for x in 0 .. 2:
+      var obj = X()
+      echo obj
+    # The destructor call is invoked after "end" is printed
+    echo "end"
+
+  main()
+
+echo "-------------------------"
+
+block:
+  type
+    X = object
+
+  proc `=destroy`(x: var X) =
+    echo "Destroyed"
+
+  proc main() =
+    block:
+      var obj = X()
+      echo obj
+      # The destructor is not called when obj goes out of scope
+    echo "end"
+
+  main()
diff --git a/tests/destructor/tarc.nim b/tests/destructor/tarc.nim
new file mode 100644
index 000000000..54d75a410
--- /dev/null
+++ b/tests/destructor/tarc.nim
@@ -0,0 +1,184 @@
+discard """
+  output: '''
+@[1, 2, 3]
+Success
+@["a", "b", "c"]
+Hello
+1
+2
+0
+List
+@["4", "5", "6", "", "", "a", ""]
+@["", "", "a", ""]
+'''
+  cmd: '''nim c --gc:arc $file'''
+"""
+
+import os
+import math
+import lists
+import strutils
+
+proc mkleak() =
+  # allocate 1 MB via linked lists
+  let numberOfLists = 100
+  for i in countUp(1, numberOfLists):
+    var leakList = initDoublyLinkedList[string]()
+    let numberOfLeaks = 5000
+    for j in countUp(1, numberOfLeaks):
+      leakList.append(newString(200))
+
+proc mkManyLeaks() =
+  for i in 0..0:
+    mkleak()
+  echo "Success"
+
+iterator foobar(c: string): seq[string] {.closure.} =
+  yield @["a", "b", c]
+
+proc tsimpleClosureIterator =
+  var myc = "c"
+  for it in foobar(myc):
+    echo it
+
+type
+  LazyList = ref object
+    c: proc() {.closure.}
+
+proc tlazyList =
+  let dep = @[1, 2, 3]
+  var x = LazyList(c: proc () = echo(dep))
+  x.c()
+
+type
+  Foo = ref object
+
+proc tleakingNewStmt =
+  var x: Foo
+  for i in 0..10:
+    new(x)
+
+iterator infinite(): int {.closure.} =
+  var i = 0
+  while true:
+    yield i
+    inc i
+
+iterator take(it: iterator (): int, numToTake: int): int {.closure.} =
+  var i = 0
+  for x in it():
+    if i >= numToTake:
+      break
+    yield x
+    inc i
+
+proc take3 =
+  for x in infinite.take(3):
+    discard
+
+
+type
+  A = ref object of RootObj
+    x: int
+
+  B = ref object of A
+    more: string
+
+proc inheritanceBug(param: string) =
+  var s: (A, A)
+  s[0] = B(more: "a" & param)
+  s[1] = B(more: "a" & param)
+
+
+type
+  PAsyncHttpServer = ref object
+    value: string
+
+proc serve(server: PAsyncHttpServer) = discard
+
+proc leakObjConstr =
+  serve(PAsyncHttpServer(value: "asdas"))
+
+let startMem = getOccupiedMem()
+take3()
+tlazyList()
+inheritanceBug("whatever")
+mkManyLeaks()
+tsimpleClosureIterator()
+tleakingNewStmt()
+leakObjConstr()
+
+# bug #12964
+
+type
+  Token* = ref object of RootObj
+  Li* = ref object of Token
+
+proc bug12964*() =
+  var token = Li()
+  var tokens = @[Token()]
+  tokens.add token
+
+bug12964()
+
+# bug #13119
+import streams
+
+proc bug13119 =
+  var m = newStringStream("Hello world")
+  let buffer = m.readStr(5)
+  echo buffer
+  m.close
+
+bug13119()
+
+# bug #13105
+
+type
+  Result[T, E] = object
+    a: T
+    b: E
+  D = ref object
+    x: int
+  R = Result[D, int]
+
+proc bug13105 =
+  for n in [R(b: 1), R(b: 2)]:
+    echo n.b
+
+bug13105()
+
+echo getOccupiedMem() - startMem
+
+
+#------------------------------------------------------------------------------
+# issue #14294
+
+import tables
+
+type
+  TagKind = enum
+    List = 0, Compound
+
+  Tag = object
+    case kind: TagKind
+    of List:
+      values: seq[Tag]
+    of Compound:
+      compound: Table[string, Tag]
+
+var a = Tag(kind: List)
+var b = a
+echo a.kind
+var c = a
+
+proc testAdd(i: int; yyy: openArray[string]) =
+  var x: seq[string]
+  x.add [$i, $(i+1), $(i+2)]
+  x.add yyy
+  echo x
+
+var y = newSeq[string](4)
+y[2] = "a"
+testAdd(4, y)
+echo y
diff --git a/tests/destructor/tarc2.nim b/tests/destructor/tarc2.nim
new file mode 100644
index 000000000..a7d7b4945
--- /dev/null
+++ b/tests/destructor/tarc2.nim
@@ -0,0 +1,31 @@
+discard """
+  output: '''leak: false'''
+  cmd: '''nim c --gc:orc $file'''
+"""
+
+type
+  T = ref object
+    s: seq[T]
+    data: string
+
+proc create(): T = T(s: @[], data: "abc")
+
+proc addX(x: T; data: string) =
+  x.data = data
+
+{.push sinkInference: off.}
+
+proc addX(x: T; child: T) =
+  x.s.add child
+
+{.pop.}
+
+proc main(rootName: string) =
+  var root = create()
+  root.data = rootName
+  root.addX root
+
+let mem = getOccupiedMem()
+main("yeah")
+GC_fullCollect()
+echo "leak: ", getOccupiedMem() - mem > 0
diff --git a/tests/destructor/tarc3.nim b/tests/destructor/tarc3.nim
new file mode 100644
index 000000000..55d0ea42d
--- /dev/null
+++ b/tests/destructor/tarc3.nim
@@ -0,0 +1,101 @@
+
+discard """
+  cmd: '''nim c --gc:arc $file'''
+"""
+
+when defined(cpp):
+  {.passC: "-std=gnu++2a".}
+
+type
+  TokenKind* = enum
+    tkColon
+    tkComma
+    tkString
+    tkNumber
+    tkInt64
+    tkIdent
+
+  Token* = object
+    case kind*: TokenKind
+    of tkString: strVal*: string
+    of tkNumber: numVal*: float
+    of tkInt64: int64Val*: int64
+    of tkIdent: ident*: string
+    else: discard
+    pos*: Natural
+
+
+  Token2* = object
+    case kind*: TokenKind
+    of tkString: strVal*: string
+    of tkNumber: numVal*: float
+    of tkInt64, tkColon..tkComma:
+      str1*: array[2, string]
+      float: float
+    else: discard
+    pos*: Natural
+
+  Token3* = object
+    case kind*: TokenKind
+    of tkNumber: numVal*: float
+    of tkInt64, tkComma..tkString: ff: seq[float]
+    else: str1*: string
+  
+  Token4* = object
+    case kind*: TokenKind
+    of tkNumber: numVal*: float
+    of tkInt64, tkComma..tkString: ff: seq[float]
+    else: str1*: string
+    case kind2*: TokenKind
+    of tkNumber: 
+      numVal2*: float
+      intSeqVal3*: seq[int]
+    of tkInt64, tkComma..tkString: 
+      case kind3*: TokenKind
+      of tkNumber: numVal3*: float
+      of tkInt64, tkComma..tkString: 
+        ff3: seq[float]
+        ff5: string
+      else: 
+        str3*: string
+        mysrq: seq[int]
+    else: 
+      case kind4*: TokenKind
+      of tkNumber: numVal4*: float
+      of tkInt64, tkComma..tkString: ff4: seq[float]
+      else: str4*: string
+  
+  BaseLexer* = object of RootObj
+    input*: string
+    pos*: Natural
+
+  Json5Lexer* = object of BaseLexer
+
+  JsonLexer* = object of BaseLexer
+    allowComments*: bool
+    allowSpecialFloats*: bool
+
+  Lexer* = Json5Lexer | JsonLexer
+
+  Parser[T: Lexer] = object
+    l: T
+    tok: Token
+    tok2: Token2
+    tok3: Token3
+    tok4: Token4
+    allowTrailingComma: bool
+    allowIdentifierObjectKey: bool
+
+proc initJson5Lexer*(input: string): Json5Lexer =
+  result.input = input
+
+proc parseJson5*(input: string)  =
+  var p = Parser[Json5Lexer](
+    l: initJson5Lexer(input),
+    allowTrailingComma: true,
+    allowIdentifierObjectKey: true
+  )
+
+
+let x = "string"
+parseJson5(x)
\ No newline at end of file
diff --git a/tests/destructor/tarctypesections.nim b/tests/destructor/tarctypesections.nim
new file mode 100644
index 000000000..da81f1884
--- /dev/null
+++ b/tests/destructor/tarctypesections.nim
@@ -0,0 +1,70 @@
+discard """
+  output: "MEM 0"
+  cmd: "nim c --gc:arc $file"
+"""
+
+type
+  RefNode = ref object
+    le, ri: RefNode
+    name: char
+
+proc edge0(a, b: RefNode) =
+  if a.le == nil: a.le = b
+  else: a.ri = b
+
+proc createNode0(name: char): RefNode =
+  new result
+  result.name = name
+
+proc main0 =
+  let r = createNode0('R')
+  let c = createNode0('C')
+  c.edge0 r
+
+
+type
+  NodeDesc = object
+    le, ri: Node
+    name: char
+  Node = ref NodeDesc
+
+proc edge(a, b: Node) =
+  if a.le == nil: a.le = b
+  else: a.ri = b
+
+proc createNode(name: char): Node =
+  new result
+  result.name = name
+
+proc main =
+  let r = createNode('R')
+  let c = createNode('C')
+  c.edge r
+
+
+type
+  NodeB = ref NodeBo
+  NodeBo = object
+    le, ri: NodeB
+    name: char
+
+proc edge(a, b: NodeB) =
+  if a.le == nil: a.le = b
+  else: a.ri = b
+
+proc createNodeB(name: char): NodeB =
+  new result
+  result.name = name
+
+
+proc mainB =
+  let r = createNodeB('R')
+  let c = createNodeB('C')
+  c.edge r
+
+
+let memB = getOccupiedMem()
+main0()
+main()
+mainB()
+echo "MEM ", getOccupiedMem() - memB
diff --git a/tests/destructor/tarray_indexing.nim b/tests/destructor/tarray_indexing.nim
new file mode 100644
index 000000000..a9dfdf4ed
--- /dev/null
+++ b/tests/destructor/tarray_indexing.nim
@@ -0,0 +1,75 @@
+discard """
+  output: '''allocating 1048576 65536
+filling page from 1048576 len 65536'''
+  cmd: '''nim c --gc:arc $file'''
+"""
+
+# bug #12669
+
+type
+    MemState* = enum
+        memPrivate
+
+    MemPermisison* = enum
+        memperm_Read
+
+    MemInfo* = ref object
+        base*, size*: uint32
+        state*: MemState
+        perm*: set[MemPermisison]
+
+    MemBlock = ref object
+        info: MemInfo
+        data: seq[byte]
+
+    UserProcessMemory* = ref object
+        pageAccess: array[0x40000, ptr UncheckedArray[byte]]
+        pages: array[0x40000, MemInfo]
+        blocks: seq[owned MemBlock]
+
+proc allocMemory*(mem: UserProcessMemory, base, size: uint32) =
+    let
+        roundedBase = base and not(0xFFF'u32)
+        roundedSize = (size + 0xFFF) and not(0xFFF'u32)
+
+    echo "allocating ", base, " ", size
+    for i in (roundedBase shr 12)..<((roundedBase + roundedSize) shr 12):
+        #echo "span ", i
+        doAssert mem.pages[i] == nil
+        # TODO: beserer fehler
+
+    let memBlock = MemBlock(
+        info: MemInfo(
+            base: roundedBase,
+            size: roundedSize,
+            state: memPrivate,
+            perm: {memperm_Read}
+        ),
+        data: newSeq[byte](roundedSize))
+    for i in 0..<(roundedSize shr 12):
+        mem.pages[i + (roundedBase shr 12)] = memBlock.info
+        #echo cast[uint64](addr mem.pageAccess[i + (roundedBase shr 12)])
+        mem.pageAccess[i + (roundedBase shr 12)] = cast[ptr UncheckedArray[byte]](addr memBlock.data[i * 0x1000])
+    mem.blocks.add memBlock
+
+    #for i in (roundedBase shr 12)..<((roundedBase + roundedSize) shr 12):
+    #    assert mem.pageAccess[i] != nil
+
+proc fillPages*(mem: UserProcessMemory, start: uint32, data: seq[byte]) =
+    echo "filling page from ", start, " len ", data.len
+    assert (start and not(0xFFF'u32)) == start
+    assert (uint32(data.len) and not(0xFFF'u32)) == uint32(data.len)
+    for i in (start shr 12)..<((start + uint32(data.len)) shr 12):
+        #echo cast[uint64](addr mem.pageAccess[i])
+        let page = mem.pageAccess[i]
+        assert page != nil
+        #copyMem(page, addr data[i * 0x1000 - start], 0x1000)
+
+const base = 0x00100000
+
+proc a(): owned UserProcessMemory =
+    result = UserProcessMemory()
+    result.allocMemory(base, 0x1000 * 16)
+    result.fillPages(base, newSeq[byte](0x1000 * 16))
+
+discard a()
diff --git a/tests/destructor/tasync_prototype.nim b/tests/destructor/tasync_prototype.nim
new file mode 100644
index 000000000..81fd824e9
--- /dev/null
+++ b/tests/destructor/tasync_prototype.nim
@@ -0,0 +1,59 @@
+discard """
+  output: '''asdas
+processClient end
+false
+MEMORY 0
+'''
+  cmd: '''nim c --gc:arc $file'''
+"""
+
+type
+  PAsyncHttpServer = ref object
+    value: string
+  PFutureBase {.acyclic.} = ref object
+    callback: proc () {.closure.}
+    value: string
+    failed: bool
+
+proc accept(server: PAsyncHttpServer): PFutureBase =
+  new(result)
+  result.callback = proc () =
+    discard
+  server.value = "hahaha"
+
+proc processClient(): PFutureBase =
+  new(result)
+
+proc serve(server: PAsyncHttpServer): PFutureBase =
+  iterator serveIter(): PFutureBase {.closure.} =
+    echo server.value
+    while true:
+      var acceptAddrFut = server.accept()
+      yield acceptAddrFut
+      var fut = acceptAddrFut.value
+
+      # with the new scope based destruction, this cannot
+      # possibly work:
+      var f {.cursor.} = processClient()
+      # It also seems to be the wrong way how to avoid the
+      # cycle. The cycle is caused by capturing the 'env'
+      # part from 'env.f'.
+      when true:
+        f.callback =
+          proc () =
+            echo("processClient end")
+            echo(f.failed)
+      yield f
+  var x = serveIter
+  for i in 0 .. 1:
+    result = x()
+    if result.callback != nil:
+      result.callback()
+
+let mem = getOccupiedMem()
+
+proc main =
+  discard serve(PAsyncHttpServer(value: "asdas"))
+
+main()
+echo "MEMORY ", getOccupiedMem() - mem
diff --git a/tests/destructor/tasync_prototype_cyclic.nim b/tests/destructor/tasync_prototype_cyclic.nim
new file mode 100644
index 000000000..8ba73a8fc
--- /dev/null
+++ b/tests/destructor/tasync_prototype_cyclic.nim
@@ -0,0 +1,55 @@
+discard """
+  output: '''asdas
+processClient end
+false
+MEMORY 0
+'''
+  cmd: '''nim c --gc:orc $file'''
+"""
+
+type
+  PAsyncHttpServer = ref object
+    value: string
+  PFutureBase = ref object
+    callback: proc () {.closure.}
+    value: string
+    failed: bool
+
+proc accept(server: PAsyncHttpServer): PFutureBase =
+  new(result)
+  result.callback = proc () =
+    discard
+  server.value = "hahaha"
+
+proc processClient(): PFutureBase =
+  new(result)
+
+proc serve(server: PAsyncHttpServer): PFutureBase =
+  iterator serveIter(): PFutureBase {.closure.} =
+    echo server.value
+    while true:
+      var acceptAddrFut = server.accept()
+      yield acceptAddrFut
+      var fut = acceptAddrFut.value
+
+      var f = processClient()
+      when true:
+        f.callback =
+          proc () =
+            echo("processClient end")
+            echo(f.failed)
+      yield f
+  var x = serveIter
+  for i in 0 .. 1:
+    result = x()
+    if result.callback != nil:
+      result.callback()
+
+let mem = getOccupiedMem()
+
+proc main =
+  discard serve(PAsyncHttpServer(value: "asdas"))
+
+main()
+GC_fullCollect()
+echo "MEMORY ", getOccupiedMem() - mem
diff --git a/tests/destructor/tatomicptrs.nim b/tests/destructor/tatomicptrs.nim
index d20596415..82870ac82 100644
--- a/tests/destructor/tatomicptrs.nim
+++ b/tests/destructor/tatomicptrs.nim
@@ -8,8 +8,10 @@ allocating
 deallocating
 deallocating
 deallocating
+allocating
+deallocating
 '''
-  cmd: '''nim c --newruntime $file'''
+joinable: false
 """
 
 type
@@ -23,10 +25,9 @@ template incRef(x) =
 
 template decRef(x): untyped = atomicDec(x.refcount)
 
-proc makeShared*[T](x: T): SharedPtr[T] =
-  # XXX could benefit from 'sink' parameter.
+proc makeShared*[T](x: sink T): SharedPtr[T] =
   # XXX could benefit from a macro that generates it.
-  result = cast[SharedPtr[T]](allocShared(sizeof(x)))
+  result = cast[SharedPtr[T]](allocShared0(sizeof(x)))
   result.x[] = x
   echo "allocating"
 
@@ -38,7 +39,7 @@ proc `=destroy`*[T](dest: var SharedPtr[T]) =
     echo "deallocating"
     dest.x = nil
 
-proc `=`*[T](dest: var SharedPtr[T]; src: SharedPtr[T]) =
+proc `=copy`*[T](dest: var SharedPtr[T]; src: SharedPtr[T]) =
   var s = src.x
   if s != nil: incRef(s)
   #atomicSwap(dest, s)
@@ -49,6 +50,9 @@ proc `=`*[T](dest: var SharedPtr[T]; src: SharedPtr[T]) =
     deallocShared(s)
     echo "deallocating"
 
+proc `=dup`*[T](src: SharedPtr[T]): SharedPtr[T] =
+  `=copy`(result, src)
+
 proc `=sink`*[T](dest: var SharedPtr[T]; src: SharedPtr[T]) =
   ## XXX make this an atomic store:
   if dest.x != src.x:
@@ -59,6 +63,9 @@ proc `=sink`*[T](dest: var SharedPtr[T]; src: SharedPtr[T]) =
       echo "deallocating"
     dest.x = src.x
 
+proc get*[T](s: SharedPtr[T]): lent T =
+  s.x[]
+
 template `.`*[T](s: SharedPtr[T]; field: untyped): untyped =
   s.x.field
 
@@ -68,6 +75,7 @@ template `.=`*[T](s: SharedPtr[T]; field, value: untyped) =
 from macros import unpackVarargs
 
 template `.()`*[T](s: SharedPtr[T]; field: untyped, args: varargs[untyped]): untyped =
+  # xxx this isn't used, the test should be improved
   unpackVarargs(s.x.field, args)
 
 
@@ -99,3 +107,70 @@ proc main =
 
 main()
 
+
+
+#-------------------------------------------------------
+#bug #9781
+
+type
+  MySeq* [T] = object
+    refcount: int
+    len: int
+    data: ptr UncheckedArray[T]
+
+proc `=destroy`*[T](m: var MySeq[T]) {.inline.} =
+  if m.data != nil:
+    deallocShared(m.data)
+    m.data = nil
+
+proc `=copy`*[T](m: var MySeq[T], m2: MySeq[T]) =
+  if m.data == m2.data: return
+  if m.data != nil:
+    `=destroy`(m)
+
+  m.len = m2.len
+  let bytes = m.len.int * sizeof(float)
+  if bytes > 0:
+    m.data = cast[ptr UncheckedArray[T]](allocShared(bytes))
+    copyMem(m.data, m2.data, bytes)
+
+proc `=dup`*[T](m: MySeq[T]): MySeq[T] =
+  `=copy`[T](result, m)
+
+proc `=sink`*[T](m: var MySeq[T], m2: MySeq[T]) {.inline.} =
+  if m.data != m2.data:
+    if m.data != nil:
+      `=destroy`(m)
+    m.len = m2.len
+    m.data = m2.data
+    m.refcount = m2.refcount
+
+proc len*[T](m: MySeq[T]): int {.inline.} = m.len
+
+proc newMySeq*[T](size: int, initial_value: T): MySeq[T] =
+  result.len = size
+  result.refcount = 1
+  if size > 0:
+    result.data = cast[ptr UncheckedArray[T]](allocShared(sizeof(T) * size))
+
+
+let x = makeShared(newMySeq(10, 1.0))
+doAssert: x.get().len == 10
+
+
+
+#-------------------------------------------------------
+#bug #12882
+
+type
+  ValueObject = object
+    v: MySeq[int]
+    name: string
+
+  TopObject = object
+    internal: seq[ValueObject]
+
+var zz = new(TopObject)
+
+
+
diff --git a/tests/destructor/tbintree2.nim b/tests/destructor/tbintree2.nim
new file mode 100644
index 000000000..d56c2850b
--- /dev/null
+++ b/tests/destructor/tbintree2.nim
@@ -0,0 +1,101 @@
+discard """
+  cmd: '''nim c -d:nimAllocStats --newruntime $file'''
+  output: '''0
+(allocCount: 5, deallocCount: 5)'''
+"""
+
+import system / ansi_c
+
+import random
+
+type Node = ref object
+  x, y: int32
+  left, right: owned Node
+
+proc newNode(x: int32): owned Node =
+  result = Node(x: x, y: rand(high int32).int32)
+
+proc merge(lower, greater: owned Node): owned Node =
+  if lower.isNil:
+    result = greater
+  elif greater.isNil:
+    result = lower
+  elif lower.y < greater.y:
+    lower.right = merge(move lower.right, greater)
+    result = lower
+  else:
+    greater.left = merge(lower, move greater.left)
+    result = greater
+
+proc splitBinary(orig: owned Node, value: int32): (owned Node, owned Node) =
+  if orig.isNil:
+    result = (nil, nil)
+  elif orig.x < value:
+    let splitPair = splitBinary(move orig.right, value)
+    orig.right = splitPair[0]
+    result = (orig, splitPair[1])
+  else:
+    let splitPair = splitBinary(move orig.left, value)
+    orig.left = splitPair[1]
+    result = (splitPair[0], orig)
+
+proc merge3(lower, equal, greater: owned Node): owned Node =
+  merge(merge(lower, equal), greater)
+
+proc split(orig: owned Node, value: int32): tuple[lower, equal, greater: owned Node] =
+  let
+    (lower, equalGreater) = splitBinary(orig, value)
+    (equal, greater) = splitBinary(equalGreater, value + 1)
+  result = (lower, equal, greater)
+
+type Tree = object
+  root: owned Node
+
+proc `=destroy`(t: var Tree) {.nodestroy.} =
+  var s: seq[owned Node] = @[t.root]
+  while s.len > 0:
+    let x = s.pop
+    if x.left != nil: s.add(x.left)
+    if x.right != nil: s.add(x.right)
+    `=dispose`(x)
+  `=destroy`(s)
+
+proc hasValue(self: var Tree, x: int32): bool =
+  let splited = split(move self.root, x)
+  result = not splited.equal.isNil
+  self.root = merge3(splited.lower, splited.equal, splited.greater)
+
+proc insert(self: var Tree, x: int32) =
+  var splited = split(move self.root, x)
+  if splited.equal.isNil:
+    splited.equal = newNode(x)
+  self.root = merge3(splited.lower, splited.equal, splited.greater)
+
+proc erase(self: var Tree, x: int32) =
+  let splited = split(move self.root, x)
+  self.root = merge(splited.lower, splited.greater)
+
+proc main() =
+  var
+    tree = Tree()
+    cur = 5'i32
+    res = 0
+
+  for i in 1 ..< 10:
+    let a = i mod 3
+    cur = (cur * 57 + 43) mod 10007
+    case a:
+    of 0:
+      tree.insert(cur)
+    of 1:
+      tree.erase(cur)
+    of 2:
+      if tree.hasValue(cur):
+        res += 1
+    else:
+      discard
+  echo res
+
+dumpAllocStats:
+  main()
+
diff --git a/tests/destructor/tcaseobj_transitions.nim b/tests/destructor/tcaseobj_transitions.nim
new file mode 100644
index 000000000..61464101f
--- /dev/null
+++ b/tests/destructor/tcaseobj_transitions.nim
@@ -0,0 +1,49 @@
+discard """
+  cmd: '''nim c --gc:arc $file'''
+  output: '''no crash'''
+"""
+
+# bug #11205
+
+type
+  MyEnum = enum
+    A, B, C
+  MyCaseObject = object
+    case kind: MyEnum
+    of A: iseq: seq[int]
+    of B: fseq: seq[float]
+    of C: str: string
+
+
+  MyCaseObjectB = object # carefully constructed to use the same enum,
+                         # but a different object type!
+    case kind: MyEnum
+    of A, C: x: int
+    of B: fseq: seq[float]
+
+
+var x = MyCaseObject(kind: A)
+x.iseq.add 1
+#x.kind = B
+#x.fseq.add -3.0
+
+var y = MyCaseObjectB(kind: A)
+y.x = 1
+y.kind = C
+echo "no crash"
+
+
+#################
+## bug #12821
+
+type
+  RefBaseObject* = ref object of RootObj
+    case kind: bool
+      of true: a: int
+      of false: b: float
+
+  MyRefObject = ref object of RefBaseObject
+    x: float
+
+let z = new(MyRefObject)
+z.kind = false
diff --git a/tests/destructor/tcast.nim b/tests/destructor/tcast.nim
new file mode 100644
index 000000000..35a7d874b
--- /dev/null
+++ b/tests/destructor/tcast.nim
@@ -0,0 +1,14 @@
+# Make sure we don't walk cast[T] type section while injecting sinks/destructors
+block:
+  type
+    XY[T] = object
+      discard
+
+  proc `=`[T](x: var XY[T]; v: XY[T]) {.error.}
+  proc `=sink`[T](x: var XY[T]; v: XY[T]) {.error.}
+
+  proc main[T]() =
+    var m = cast[ptr XY[T]](alloc0(sizeof(XY[T])))
+    doAssert(m != nil)
+
+  main[int]()
diff --git a/tests/destructor/tcomplexobjconstr.nim b/tests/destructor/tcomplexobjconstr.nim
new file mode 100644
index 000000000..aea0ad1fe
--- /dev/null
+++ b/tests/destructor/tcomplexobjconstr.nim
@@ -0,0 +1,56 @@
+discard """
+  output: '''true
+OK'''
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #12826
+
+type
+  MyObject1* = object of RootObj
+    z*: string
+
+  MyObject2* = object of RootObj
+    x*: float
+    name*: string
+    subobj: MyObject1
+    case flag*: bool
+    of false:
+      more: array[3, MyObject1]
+    of true: y*: float
+
+var x = new(MyObject2)
+doAssert x of MyObject2
+doAssert x.subobj of MyObject1
+doAssert x.more[2] of MyObject1
+doAssert x.more[2] of RootObj
+
+var y: MyObject2
+doAssert y of MyObject2
+doAssert y.subobj of MyObject1
+doAssert y.more[2] of MyObject1
+doAssert y.more[2] of RootObj
+
+echo "true"
+
+# bug #12978
+type
+  Vector2* = object of RootObj
+    x*, y*: float
+
+type
+  Vertex* = ref object
+    point*: Vector2
+
+proc newVertex*(p: Vector2): Vertex =
+  return Vertex(point: p)
+
+proc createVertex*(p: Vector2): Vertex =
+  result = newVertex(p)
+
+proc p =
+  var x = Vector2(x: 1, y: 2)
+  let other = createVertex(x)
+  echo "OK"
+
+p()
diff --git a/tests/destructor/tconst_smart_ptr.nim b/tests/destructor/tconst_smart_ptr.nim
new file mode 100644
index 000000000..39fe12612
--- /dev/null
+++ b/tests/destructor/tconst_smart_ptr.nim
@@ -0,0 +1,7 @@
+discard """
+  action: "compile"
+"""
+
+import const_smart_ptr
+
+discard test(0)
diff --git a/tests/destructor/tconsume_twice.nim b/tests/destructor/tconsume_twice.nim
new file mode 100644
index 000000000..b0a039e9b
--- /dev/null
+++ b/tests/destructor/tconsume_twice.nim
@@ -0,0 +1,15 @@
+discard """
+  cmd: "nim c --newruntime $file"
+  errormsg: "'=copy' is not available for type <owned Foo>; requires a copy because it's not the last read of 'a'; another read is done here: tconsume_twice.nim(13, 10); routine: consumeTwice"
+  line: 11
+"""
+type
+  Foo = ref object
+
+proc use(a: owned Foo): bool = discard
+proc consumeTwice(a: owned Foo): owned Foo =
+  if use(a):
+    return
+  return a
+
+assert consumeTwice(Foo()) != nil
diff --git a/tests/destructor/tcustomseqs.nim b/tests/destructor/tcustomseqs.nim
index 97d7c07b6..17a19f871 100644
--- a/tests/destructor/tcustomseqs.nim
+++ b/tests/destructor/tcustomseqs.nim
@@ -16,7 +16,7 @@ discard """
 1 2 6
 1 3 7
 after 6 6'''
-  cmd: '''nim c --newruntime $file'''
+joinable: false
 """
 
 import typetraits
@@ -43,9 +43,10 @@ proc `=destroy`*[T](x: var myseq[T]) =
 proc `=`*[T](a: var myseq[T]; b: myseq[T]) =
   if a.data == b.data: return
   if a.data != nil:
-    dealloc(a.data)
-    inc deallocCount
-    a.data = nil
+    `=destroy`(a)
+    #dealloc(a.data)
+    #inc deallocCount
+    #a.data = nil
   a.len = b.len
   a.cap = b.cap
   if b.data != nil:
@@ -120,7 +121,7 @@ proc createSeq*[T](elems: varargs[T]): myseq[T] =
   result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
   inc allocCount
   when supportsCopyMem(T):
-    copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T))
+    copyMem(result.data, addr(elems[0]), result.cap * sizeof(T))
   else:
     for i in 0..<result.len:
       result.data[i] = elems[i]
diff --git a/tests/destructor/tcustomstrings.nim b/tests/destructor/tcustomstrings.nim
index 1a78df20b..31891856b 100644
--- a/tests/destructor/tcustomstrings.nim
+++ b/tests/destructor/tcustomstrings.nim
@@ -5,11 +5,9 @@ foo bar to appendmore here
 foo bar to appendmore here
 foo bar to appendmore here
 after 20 20'''
-  cmd: '''nim c --newruntime $file'''
+joinable: false
 """
 
-{.this: self.}
-
 type
   mystring = object
     len, cap: int
@@ -51,7 +49,7 @@ proc resize(self: var mystring) =
   if self.cap == 0: self.cap = 8
   else: self.cap = (self.cap * 3) shr 1
   if self.data == nil: inc allocCount
-  self.data = cast[type(data)](realloc(self.data, self.cap + 1))
+  self.data = cast[type(self.data)](realloc(self.data, self.cap + 1))
 
 proc add*(self: var mystring; c: char) =
   if self.len >= self.cap: resize(self)
@@ -60,22 +58,22 @@ proc add*(self: var mystring; c: char) =
   inc self.len
 
 proc ensure(self: var mystring; newLen: int) =
-  if newLen >= cap:
-    cap = max((cap * 3) shr 1, newLen)
-    if cap > 0:
-      if data == nil: inc allocCount
-      data = cast[type(data)](realloc(data, cap + 1))
+  if newLen >= self.cap:
+    self.cap = max((self.cap * 3) shr 1, newLen)
+    if self.cap > 0:
+      if self.data == nil: inc allocCount
+      self.data = cast[type(self.data)](realloc(self.data, self.cap + 1))
 
 proc add*(self: var mystring; y: mystring) =
-  let newLen = len + y.len
+  let newLen = self.len + y.len
   ensure(self, newLen)
-  copyMem(addr data[len], y.data, y.data.len + 1)
-  len = newLen
+  copyMem(addr self.data[self.len], y.data, y.data.len + 1)
+  self.len = newLen
 
 proc create*(lit: string): mystring =
   let newLen = lit.len
   ensure(result, newLen)
-  copyMem(addr result.data[result.len], unsafeAddr lit[0], newLen + 1)
+  copyMem(addr result.data[result.len], addr lit[0], newLen + 1)
   result.len = newLen
 
 proc `&`*(a, b: mystring): mystring =
diff --git a/tests/destructor/tcycle1.nim b/tests/destructor/tcycle1.nim
new file mode 100644
index 000000000..8dc552294
--- /dev/null
+++ b/tests/destructor/tcycle1.nim
@@ -0,0 +1,54 @@
+discard """
+  output: "MEM 0"
+  cmd: "nim c --gc:orc $file"
+"""
+
+type
+  Node = ref object of RootObj
+    le, ri: Node
+    name: char
+
+proc edge(a, b: Node) =
+  if a.le == nil: a.le = b
+  else: a.ri = b
+
+proc createNode(name: char): Node =
+  new result
+  result.name = name
+
+#[
+
++---------+      +------+
+|         |      |      |
+|  A      +----->+      <------+-------------+
++--+------+      |      |      |             |
+   |             |      |      |     C       |
+   |             |  R   |      |             |
++--v------+      |      |      +-------------+
+|         |      |      |        ^
+|   B     <------+      |        |
+|         |      |      +--------+
++---------+      |      |
+                 +------+
+
+]#
+
+proc main =
+  let a = createNode('A')
+  let b = createNode('B')
+  let r = createNode('R')
+  let c = createNode('C')
+
+  a.edge b
+  a.edge r
+
+  r.edge b
+  r.edge c
+
+  c.edge r
+
+
+let mem = getOccupiedMem()
+main()
+GC_fullCollect()
+echo "MEM ", getOccupiedMem() - mem
diff --git a/tests/destructor/tcycle2.nim b/tests/destructor/tcycle2.nim
new file mode 100644
index 000000000..7b03101fe
--- /dev/null
+++ b/tests/destructor/tcycle2.nim
@@ -0,0 +1,36 @@
+discard """
+  output: "MEM 0"
+  cmd: "nim c --gc:orc $file"
+"""
+
+type
+  Node = ref object
+    kids: seq[Node]
+    data: string
+
+proc main(x: int) =
+  var n = Node(kids: @[], data: "3" & $x)
+  let m = n
+  n.kids.add m
+
+type
+  NodeA = ref object
+    s: char
+    a: array[3, NodeA]
+
+proc m: NodeA =
+  result = NodeA(s: 'a')
+  result.a[0] = result
+  result.a[1] = result
+  result.a[2] = result
+
+proc mainA =
+  for i in 0..10:
+    discard m()
+
+let mem = getOccupiedMem()
+main(90)
+mainA()
+GC_fullCollect()
+
+echo "MEM ", getOccupiedMem() - mem
diff --git a/tests/destructor/tcycle3.nim b/tests/destructor/tcycle3.nim
new file mode 100644
index 000000000..8662136e7
--- /dev/null
+++ b/tests/destructor/tcycle3.nim
@@ -0,0 +1,98 @@
+discard """
+  output: '''BEGIN
+END
+END 2
+cpu.nes false
+cpu step nes is nil? - false
+0'''
+  cmd: '''nim c --gc:orc $file'''
+"""
+
+# extracted from thavlak.nim
+
+type
+  BasicBlock = ref object
+    inEdges: seq[BasicBlock]
+    outEdges: seq[BasicBlock]
+    name: int
+
+proc newBasicBlock(name: int): BasicBlock =
+  result = BasicBlock(
+    inEdges: newSeq[BasicBlock](),
+    outEdges: newSeq[BasicBlock](),
+    name: name
+  )
+
+type
+  Cfg = object
+    basicBlockMap: seq[BasicBlock]
+    startNode: BasicBlock
+
+proc newCfg(): Cfg =
+  result = Cfg(
+    basicBlockMap: newSeq[BasicBlock](),
+    startNode: nil)
+
+proc createNode(cfg: var Cfg, name: int): BasicBlock =
+  if name < cfg.basicBlockMap.len:
+    result = cfg.basicBlockMap[name]
+  else:
+    result = newBasicBlock(name)
+    cfg.basicBlockMap.setLen name+1
+    cfg.basicBlockMap[name] = result
+
+proc newBasicBlockEdge(cfg: var Cfg, fromName, toName: int) =
+  echo "BEGIN"
+  let fr = cfg.createNode(fromName)
+  let to = cfg.createNode(toName)
+
+  fr.outEdges.add(to)
+  to.inEdges.add(fr)
+
+proc run(cfg: var Cfg) =
+  cfg.startNode = cfg.createNode(0) # RC = 2
+  newBasicBlockEdge(cfg, 0, 1) #
+  echo "END"
+
+  discard cfg.createNode(1)
+
+proc main =
+  var c = newCfg()
+  c.run
+  echo "END 2"
+
+# bug #14159
+type
+  NES = ref object
+    cpu: CPU
+    apu: APU
+
+  CPU = ref object
+    nes: NES
+
+  APU = object
+    nes: NES
+    cpu: CPU
+
+proc initAPU(nes: sink NES): APU {.nosinks.} =
+  result.nes = nes
+  result.cpu = nes.cpu
+
+proc step(cpu: CPU): int =
+  echo "cpu.nes ", cpu.isNil
+  echo "cpu step nes is nil? - ", cpu.nes.isNil()
+
+proc newNES(): NES =
+  new result
+  result.cpu = CPU(nes: result)
+  result.apu = initAPU(result)
+
+proc bug14159 =
+  var nesConsole = newNES()
+  discard nesConsole.cpu.step()
+
+let mem = getOccupiedMem()
+main()
+bug14159()
+GC_fullCollect()
+echo getOccupiedMem() - mem
diff --git a/tests/destructor/tdangingref_simple.nim b/tests/destructor/tdangingref_simple.nim
new file mode 100644
index 000000000..279581b0f
--- /dev/null
+++ b/tests/destructor/tdangingref_simple.nim
@@ -0,0 +1,32 @@
+discard """
+  output: '''a
+[FATAL] dangling references exist
+'''
+  exitCode: 1
+  cmd: "nim c --newruntime $file"
+"""
+
+# bug #11350
+
+type
+  Node = ref object
+    data: int
+
+proc use(x: Node) = discard
+
+proc main =
+  var x = Node(data: 3) # inferred to be an ``owned ref``
+  var dangling = unown x
+  assert dangling.data == 3
+  #use x
+  #dangling = nil
+  # reassignment causes the memory of what ``x`` points to to be freed:
+  echo "a"
+  x = Node(data: 4)
+  echo "b"
+  # accessing 'dangling' here is invalid as it is nil.
+  # at scope exit the memory of what ``x`` points to is freed
+  if dangling != nil:
+    echo dangling.data
+
+main()
diff --git a/tests/destructor/tdestructor.nim b/tests/destructor/tdestructor.nim
index c9f1caf2d..e081eb251 100644
--- a/tests/destructor/tdestructor.nim
+++ b/tests/destructor/tdestructor.nim
@@ -1,27 +1,30 @@
 discard """
-  output: '''----
+  output: '''----1
 myobj constructed
 myobj destroyed
-----
+----2
 mygeneric1 constructed
 mygeneric1 destroyed
-----
+----3
 mygeneric2 constructed
 mygeneric2 destroyed
 myobj destroyed
-----
+----4
 mygeneric3 constructed
 mygeneric1 destroyed
-----
+----5
+mydistinctObj constructed
+myobj destroyed
+mygeneric2 destroyed
+------------------8
 mygeneric1 destroyed
-----
+----6
+myobj destroyed
+----7
+---9
 myobj destroyed
-----
-----
 myobj destroyed
 '''
-  cmd: '''nim c --newruntime $file'''
-  disabled: "true"
 """
 
 type
@@ -29,6 +32,13 @@ type
     x, y: int
     p: pointer
 
+proc `=destroy`(o: var TMyObj) =
+  if o.p != nil:
+    dealloc o.p
+    o.p = nil
+    echo "myobj destroyed"
+
+type
   TMyGeneric1[T] = object
     x: T
 
@@ -36,37 +46,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,36 +108,60 @@ proc mygeneric3 =
 
   echo "mygeneric3 constructed"
 
-echo "----"
+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 "----1"
 myobj()
 
-echo "----"
+echo "----2"
 mygeneric1()
 
-echo "----"
+echo "----3"
 mygeneric2[int](10)
 
-echo "----"
+echo "----4"
 mygeneric3()
 
+echo "----5"
+mydistinctObj()
+
 proc caseobj =
   block:
-    echo "----"
     var o1 = TCaseObj(kind: A, x: TMyGeneric1[int](x: 10))
 
   block:
-    echo "----"
+    echo "----6"
     var o2 = TCaseObj(kind: B, y: open())
 
   block:
-    echo "----"
+    echo "----7"
     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 "------------------8"
 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 "---9"
+discard caseobj_test_sink()
+
+# issue #14315
+
+type Vector*[T] = object
+  x1: int
+  # x2: T # uncomment will remove error
+
+# proc `=destroy`*(x: var Vector[int]) = discard # this will remove error
+proc `=destroy`*[T](x: var Vector[T]) = discard
+var a: Vector[int] # Error: unresolved generic parameter
diff --git a/tests/destructor/tdestructor3.nim b/tests/destructor/tdestructor3.nim
index 3e177d3cd..3f5eb2cc1 100644
--- a/tests/destructor/tdestructor3.nim
+++ b/tests/destructor/tdestructor3.nim
@@ -1,12 +1,21 @@
 discard """
-  output: '''assign
+  output: '''
+assign
 destroy
 destroy
 5
 123
+destroy Foo: 123
 destroy Foo: 5
-destroy Foo: 123'''
-  cmd: '''nim c --newruntime $file'''
+(x1: (val: ...))
+destroy
+---------------
+app begin
+(val: ...)
+destroy
+app end
+'''
+joinable: false
 """
 
 # bug #2821
@@ -14,14 +23,18 @@ destroy Foo: 123'''
 type T = object
 
 proc `=`(lhs: var T, rhs: T) =
-    echo "assign"
+  echo "assign"
 
 proc `=destroy`(v: var T) =
-    echo "destroy"
+  echo "destroy"
+
+proc use(x: T) = discard
 
 proc usedToBeBlock =
-    var v1 : T
-    var v2 : T = v1
+  var v1 = T()
+  var v2: T = v1
+  discard addr(v2) # prevent cursorfication
+  use v1
 
 usedToBeBlock()
 
@@ -46,3 +59,127 @@ proc main =
   test(toFooPtr(123))
 
 main()
+
+# bug #11517
+type
+  UniquePtr*[T] = object
+    val: ptr T
+
+proc `=destroy`*[T](p: var UniquePtr[T]) =
+  mixin `=destroy`
+  echo "destroy"
+  if p.val != nil:
+    `=destroy`(p.val[])
+    dealloc(p.val)
+    p.val = nil
+
+proc `=`*[T](dest: var UniquePtr[T], src: UniquePtr[T]) {.error.}
+
+proc `=sink`*[T](dest: var UniquePtr[T], src: UniquePtr[T]) {.inline.} =
+  if dest.val != src.val:
+    if dest.val != nil:
+      `=destroy`(dest)
+    dest.val = src.val
+
+proc newUniquePtr*[T](val: sink T): UniquePtr[T] =
+  result.val = create(T)
+  result.val[] = val
+
+#-------------------------------------------------------------
+
+type
+  MyObject = object of RootObj
+    x1: UniquePtr[int]
+
+  MyObject2 = object of MyObject
+
+proc newObj2(x:int, y: float): MyObject2 =
+  MyObject2(x1: newUniquePtr(x))
+
+proc test =
+  let obj2 = newObj2(1, 1.0)
+  echo obj2
+
+test()
+
+
+#------------------------------------------------------------
+# Issue #12883
+
+type
+  TopObject = object
+    internal: UniquePtr[int]
+
+proc deleteTop(p: ptr TopObject) =
+  if p != nil:
+    `=destroy`(p[]) # !!! this operation used to leak the integer
+    deallocshared(p)
+
+proc createTop(): ptr TopObject =
+  result = cast[ptr TopObject](allocShared0(sizeof(TopObject)))
+  result.internal = newUniquePtr(1)
+
+proc test2() =
+  let x = createTop()
+  echo $x.internal
+  deleteTop(x)
+
+echo "---------------"
+echo "app begin"
+test2()
+echo "app end"
+
+# bug #14601
+
+when true: # D20200607T202043
+  type Foo2 = object
+    x: int
+    x2: array[10, int]
+
+  type Vec = object
+    vals: seq[Foo2]
+
+  proc `=destroy`*(a: var Foo2) {.inline.} =
+    discard
+
+  proc initFoo2(x: int): Foo2 = Foo2(x: x)
+
+  proc add2(v: var Vec, a: Foo2) = # ditto with `a: sink Foo2`
+    v.vals.add a
+
+  proc add3(v: var Vec, a: Foo2) = # ditto with `a: sink Foo2`
+    v.vals = @[a]
+
+  proc add4(v: var Vec, a: sink Foo2) = # ditto with `a: sink Foo2`
+    v.vals.add a
+
+  proc add5(v: var Vec, a: sink Foo2) = # ditto with `a: sink Foo2`
+    v.vals = @[a]
+
+  proc main2()=
+    var a: Vec
+    var b = Foo2(x: 10)
+    a.add2 b # ok
+    a.vals.add Foo2(x: 10) # ok
+    a.add2 initFoo2(x = 10) # ok
+    a.add2 Foo2(x: 10) # bug
+    a.add3 initFoo2(x = 10) # ok
+    a.add3 Foo2(x: 10) # bug
+    a.add4 initFoo2(x = 10) # ok
+    a.add4 Foo2(x: 10) # bug
+    a.add5 initFoo2(x = 10) # ok
+    a.add5 Foo2(x: 10) # bug
+  main2()
+
+
+
+#------------------------------------------------------------
+# Issue #15825
+
+type
+  Union = string | int | char
+
+proc run(a: sink Union) =
+  discard
+
+run("123")
diff --git a/tests/destructor/tdestructor_too_late.nim b/tests/destructor/tdestructor_too_late.nim
new file mode 100644
index 000000000..76d1dde84
--- /dev/null
+++ b/tests/destructor/tdestructor_too_late.nim
@@ -0,0 +1,14 @@
+discard """
+  errormsg: "cannot bind another '=destroy' to: Obj; previous declaration was constructed here implicitly: tdestructor_too_late.nim(7, 16)"
+"""
+type Obj* = object
+  v*: int
+
+proc something(this: sink Obj) = 
+  discard
+
+proc `=destroy`(this: var Obj) =
+  echo "igotdestroyed"
+  this.v = -1
+
+var test* = Obj(v: 42)
\ No newline at end of file
diff --git a/tests/destructor/tdistinctseq.nim b/tests/destructor/tdistinctseq.nim
new file mode 100644
index 000000000..5a2ac5ead
--- /dev/null
+++ b/tests/destructor/tdistinctseq.nim
@@ -0,0 +1,8 @@
+discard """
+  matrix: "-u:nimPreviewNonVarDestructor;"
+"""
+type DistinctSeq* = distinct seq[int]
+
+# `=destroy`(cast[ptr DistinctSeq](0)[])
+var x = @[].DistinctSeq
+`=destroy`(x)
diff --git a/tests/destructor/tdont_return_unowned_from_owned.nim b/tests/destructor/tdont_return_unowned_from_owned.nim
new file mode 100644
index 000000000..ffe87cd76
--- /dev/null
+++ b/tests/destructor/tdont_return_unowned_from_owned.nim
@@ -0,0 +1,56 @@
+discard """
+  cmd: "nim check --newruntime --hints:off $file"
+  nimout: '''
+tdont_return_unowned_from_owned.nim(26, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref
+tdont_return_unowned_from_owned.nim(27, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref
+tdont_return_unowned_from_owned.nim(31, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type
+tdont_return_unowned_from_owned.nim(43, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type
+tdont_return_unowned_from_owned.nim(46, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type
+tdont_return_unowned_from_owned.nim(49, 6) Error: type mismatch: got <Obj>
+but expected one of:
+proc new[T](a: var ref T; finalizer: proc (x: ref T) {.nimcall.})
+  first type mismatch at position: 2
+  missing parameter: finalizer
+2 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them
+
+expression: new(result)
+tdont_return_unowned_from_owned.nim(49, 6) Error: illformed AST: 
+'''
+  errormsg: "illformed AST:"
+"""
+
+
+
+proc testA(result: var (RootRef, RootRef)) =
+  let r: owned RootRef = RootRef()
+  result[0] = r
+  result[1] = RootRef()
+
+proc testB(): RootRef =
+  let r: owned RootRef = RootRef()
+  result = r
+
+
+
+
+
+## line 30
+# bug #11073
+type
+  Obj = ref object
+
+proc newObjA(): Obj =
+  result = new Obj
+
+proc newObjB(): Obj =
+  result = Obj()
+
+proc newObjC(): Obj =
+  new(result) # illFormedAst raises GlobalError,
+              # without pipeline parsing, it needs to placed at the end
+              # in case that it disturbs other errors
+
+let a = newObjA()
+let b = newObjB()
+let c = newObjC()
+
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/destructor/texceptions.nim b/tests/destructor/texceptions.nim
new file mode 100644
index 000000000..335ca23be
--- /dev/null
+++ b/tests/destructor/texceptions.nim
@@ -0,0 +1,29 @@
+discard """
+  cmd: '''nim c --gc:arc $file'''
+  output: '''0'''
+"""
+
+proc other =
+  raise newException(ValueError, "stuff happening")
+
+proc indirectViaProcCall =
+  var correct = 0
+  for i in 1 .. 20:
+    try:
+      other()
+    except:
+      let x = getCurrentException()
+      correct += ord(x of ValueError)
+  doAssert correct == 20
+
+proc direct =
+  for i in 1 .. 20:
+    try:
+      raise newException(ValueError, "stuff happening")
+    except ValueError:
+      discard
+
+let mem = getOccupiedMem()
+indirectViaProcCall()
+direct()
+echo getOccupiedMem() - mem
diff --git a/tests/destructor/texplicit_move.nim b/tests/destructor/texplicit_move.nim
new file mode 100644
index 000000000..93795af42
--- /dev/null
+++ b/tests/destructor/texplicit_move.nim
@@ -0,0 +1,30 @@
+
+discard """
+  output: '''3
+0
+0
+10
+destroyed!
+'''
+joinable: false
+"""
+
+type
+  myseq* = object
+    f: int
+
+proc `=destroy`*(x: var myseq) =
+  echo "destroyed!"
+
+var
+  x: myseq
+x.f = 3
+echo move(x.f)
+echo x.f
+
+# bug #9743
+let a = create int
+a[] = 10
+var b = move a[]
+echo a[]
+echo b
diff --git a/tests/destructor/tfinalizer.nim b/tests/destructor/tfinalizer.nim
new file mode 100644
index 000000000..eb2cd09af
--- /dev/null
+++ b/tests/destructor/tfinalizer.nim
@@ -0,0 +1,31 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+  output: '''Foo(field: "Dick Laurent", k: ka, x: 0.0)
+Nobody is dead
+Dick Laurent is dead'''
+"""
+
+type
+  Kind = enum
+    ka, kb
+  Foo = ref object
+    field: string
+    case k: Kind
+    of ka: x: float
+    of kb: discard
+
+#var x = Foo(field: "lovely")
+proc finalizer(x: Foo) =
+  echo x.field, " is dead"
+
+var x: Foo
+new(x, finalizer)
+x.field = "Dick Laurent"
+# reference to a great movie. If you haven't seen it, highly recommended.
+
+echo repr x
+
+# bug #13112: bind the same finalizer multiple times:
+var xx: Foo
+new(xx, finalizer)
+xx.field = "Nobody"
diff --git a/tests/destructor/tgcdestructors.nim b/tests/destructor/tgcdestructors.nim
new file mode 100644
index 000000000..07a3731a0
--- /dev/null
+++ b/tests/destructor/tgcdestructors.nim
@@ -0,0 +1,203 @@
+discard """
+  cmd: '''nim c -d:nimAllocStats --gc:arc $file'''
+  output: '''hi
+ho
+ha
+@["arg", "asdfklasdfkl", "asdkfj", "dfasj", "klfjl"]
+@[1, 2, 3]
+@["red", "yellow", "orange", "rtrt1", "pink"]
+a: @[4, 2, 3]
+0
+30
+true
+(allocCount: 27, deallocCount: 27)'''
+"""
+
+include system / ansi_c
+
+proc main =
+  var s: seq[string] = @[]
+  for i in 0..<80: s.add "foo"
+
+main()
+
+const
+  test = @["hi", "ho", "ha"]
+
+for t in test:
+  echo t
+
+type
+  InterpolatedKind* = enum
+    ikStr,                   ## ``str`` part of the interpolated string
+    ikDollar,                ## escaped ``$`` part of the interpolated string
+    ikVar,                   ## ``var`` part of the interpolated string
+    ikExpr                   ## ``expr`` part of the interpolated string
+
+iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
+                                                  value: string] =
+  var i = 0
+  var kind: InterpolatedKind
+  while true:
+    var j = i
+    if j < s.len and s[j] == '$':
+      if j+1 < s.len and s[j+1] == '{':
+        inc j, 2
+        var nesting = 0
+        block curlies:
+          while j < s.len:
+            case s[j]
+            of '{': inc nesting
+            of '}':
+              if nesting == 0:
+                inc j
+                break curlies
+              dec nesting
+            else: discard
+            inc j
+          raise newException(ValueError,
+            "Expected closing '}': " & substr(s, i, s.high))
+        inc i, 2 # skip ${
+        kind = ikExpr
+      elif j+1 < s.len and s[j+1] in {'A'..'Z', 'a'..'z', '_'}:
+        inc j, 2
+        while j < s.len and s[j] in {'A'..'Z', 'a'..'z', '0'..'9', '_'}: inc(j)
+        inc i # skip $
+        kind = ikVar
+      elif j+1 < s.len and s[j+1] == '$':
+        inc j, 2
+        inc i # skip $
+        kind = ikDollar
+      else:
+        raise newException(ValueError,
+          "Unable to parse a varible name at " & substr(s, i, s.high))
+    else:
+      while j < s.len and s[j] != '$': inc j
+      kind = ikStr
+    if j > i:
+      # do not copy the trailing } for ikExpr:
+      yield (kind, substr(s, i, j-1-ord(kind == ikExpr)))
+    else:
+      break
+    i = j
+
+proc parseCmdLine(c: string): seq[string] =
+  result = @[]
+  var i = 0
+  var a = ""
+  while true:
+    setLen(a, 0)
+    while i < c.len and c[i] in {' ', '\t', '\l', '\r'}: inc(i)
+    if i >= c.len: break
+    var inQuote = false
+    while i < c.len:
+      case c[i]
+      of '\\':
+        var j = i
+        while j < c.len and c[j] == '\\': inc(j)
+        if j < c.len and c[j] == '"':
+          for k in 1..(j-i) div 2: a.add('\\')
+          if (j-i) mod 2 == 0:
+            i = j
+          else:
+            a.add('"')
+            i = j+1
+        else:
+          a.add(c[i])
+          inc(i)
+      of '"':
+        inc(i)
+        if not inQuote: inQuote = true
+        elif i < c.len and c[i] == '"':
+          a.add(c[i])
+          inc(i)
+        else:
+          inQuote = false
+          break
+      of ' ', '\t':
+        if not inQuote: break
+        a.add(c[i])
+        inc(i)
+      else:
+        a.add(c[i])
+        inc(i)
+    add(result, a)
+
+
+proc other =
+  let input = "$test{}  $this is ${an{  example}}  "
+  let expected = @[(ikVar, "test"), (ikStr, "{}  "), (ikVar, "this"),
+                  (ikStr, " is "), (ikExpr, "an{  example}"), (ikStr, "  ")]
+  var i = 0
+  for s in interpolatedFragments(input):
+    doAssert s == expected[i]
+    inc i
+
+  echo parseCmdLine("arg asdfklasdfkl asdkfj dfasj klfjl")
+
+other()
+
+# bug #11050
+
+type
+  Obj* = object
+    f*: seq[int]
+
+method main(o: Obj) {.base.} =
+  for newb in o.f:
+    discard
+
+# test that o.f was not moved!
+proc testforNoMove =
+  var o = Obj(f: @[1, 2, 3])
+  main(o)
+  echo o.f
+
+testforNoMove()
+
+# bug #11065
+type
+  Warm = seq[string]
+
+proc testWarm =
+  var w: Warm
+  w = @["red", "yellow", "orange"]
+
+  var x = "rt"
+  var y = "rt1"
+  w.add(x & y)
+
+  w.add("pink")
+  echo w
+
+testWarm()
+
+proc mutConstSeq() =
+  # bug #11524
+  var a = @[1,2,3]
+  a[0] = 4
+  echo "a: ", a
+
+mutConstSeq()
+
+proc mainSeqOfCap =
+  # bug #11098
+  var s = newSeqOfCap[int](10)
+  echo s.len
+
+  var s2 = newSeqUninitialized[int](30)
+  echo s2.len
+
+mainSeqOfCap()
+
+# bug #11614
+
+let ga = "foo"
+
+proc takeAinArray =
+  let b = [ga]
+
+takeAinArray()
+echo ga == "foo"
+
+echo getAllocStats()
diff --git a/tests/destructor/tgcleak4.nim b/tests/destructor/tgcleak4.nim
new file mode 100644
index 000000000..4299c8841
--- /dev/null
+++ b/tests/destructor/tgcleak4.nim
@@ -0,0 +1,47 @@
+discard """
+  outputsub: "no leak: "
+  cmd: "nim c --gc:arc $file"
+"""
+# bug #12758
+type
+  TExpr {.inheritable.} = object ## abstract base class for an expression
+  PLiteral = ref TLiteral
+  TLiteral = object of TExpr
+    x: int
+    op1: string
+  TPlusExpr = object of TExpr
+    a, b: ref TExpr
+    op2: string
+
+method eval(e: ref TExpr): int {.base.} =
+  # override this base method
+  quit "to override!"
+
+method eval(e: ref TLiteral): int = return e.x
+
+method eval(e: ref TPlusExpr): int =
+  # watch out: relies on dynamic binding
+  return eval(e.a) + eval(e.b)
+
+proc newLit(x: int): ref TLiteral =
+  new(result)
+  result.x = x
+  result.op1 = $getOccupiedMem()
+
+proc newPlus(a, b: ref TExpr): ref TPlusExpr =
+  new(result)
+  result.a = a
+  result.b = b
+  result.op2 = $getOccupiedMem()
+
+const Limit = when compileOption("gc", "markAndSweep") or compileOption("gc", "boehm"): 5*1024*1024 else: 500_000
+
+for i in 0..100_000:
+  var s: array[0..11, ref TExpr]
+  for j in 0..high(s):
+    s[j] = newPlus(newPlus(newLit(j), newLit(2)), newLit(4))
+    if eval(s[j]) != j+6:
+      quit "error: wrong result"
+  if getOccupiedMem() > Limit: quit("still a leak!")
+
+echo "no leak: ", getOccupiedMem()
diff --git a/tests/destructor/tglobaldestructor.nim b/tests/destructor/tglobaldestructor.nim
new file mode 100644
index 000000000..4d002a092
--- /dev/null
+++ b/tests/destructor/tglobaldestructor.nim
@@ -0,0 +1,9 @@
+discard """
+  cmd: '''nim c --gc:arc $file'''
+  output: '''(v: 42)
+igotdestroyed'''
+"""
+
+import objFile
+
+echo test
diff --git a/tests/destructor/tgotoexc_leak.nim b/tests/destructor/tgotoexc_leak.nim
new file mode 100644
index 000000000..c8a234085
--- /dev/null
+++ b/tests/destructor/tgotoexc_leak.nim
@@ -0,0 +1,19 @@
+discard """
+  output: '''0
+true'''
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #22398
+
+for i in 0 ..< 10_000:
+  try:
+    try:
+      raise newException(ValueError, "")
+    except CatchableError:
+      discard
+      raise newException(ValueError, "") # or raise getCurrentException(), just raise works ok
+  except ValueError:
+    discard
+echo getOccupiedMem()
+echo getCurrentException() == nil
diff --git a/tests/destructor/tgotoexceptions.nim b/tests/destructor/tgotoexceptions.nim
new file mode 100755
index 000000000..f76592270
--- /dev/null
+++ b/tests/destructor/tgotoexceptions.nim
@@ -0,0 +1,117 @@
+discard """
+  output: '''
+msg1
+msg2
+finally2
+finally1
+begin
+one iteration!
+caught!
+except1
+finally1
+caught! 2
+BEFORE
+FINALLY
+BEFORE
+EXCEPT
+FINALLY
+RECOVER
+BEFORE
+EXCEPT: IOError: hi
+FINALLY
+'''
+  cmd: "nim c --gc:arc --exceptions:goto $file"
+"""
+
+#bug 7204
+proc nested_finally =
+  try:
+    raise newException(KeyError, "msg1")
+  except KeyError as ex:
+    echo ex.msg
+    try:
+      raise newException(ValueError, "msg2")
+    except:
+      echo getCurrentExceptionMsg()
+    finally:
+      echo "finally2"
+  finally:
+    echo "finally1"
+
+nested_finally()
+
+proc doraise =
+  raise newException(ValueError, "gah")
+
+proc main =
+  while true:
+    try:
+      echo "begin"
+      doraise()
+    finally:
+      echo "one ", "iteration!"
+
+try:
+  main()
+except:
+  echo "caught!"
+
+when true:
+  proc p =
+    try:
+      raise newException(Exception, "Hello")
+    except:
+      echo "except1"
+      raise
+    finally:
+      echo "finally1"
+
+  try:
+    p()
+  except:
+    echo "caught! 2"
+
+
+proc noException =
+  try:
+    echo "BEFORE"
+
+  except:
+    echo "EXCEPT"
+    raise
+
+  finally:
+    echo "FINALLY"
+
+try: noException()
+except: echo "RECOVER"
+
+proc reraise_in_except =
+  try:
+    echo "BEFORE"
+    raise newException(IOError, "")
+
+  except IOError:
+    echo "EXCEPT"
+    raise
+
+  finally:
+    echo "FINALLY"
+
+try: reraise_in_except()
+except: echo "RECOVER"
+
+proc return_in_except =
+  try:
+    echo "BEFORE"
+    raise newException(IOError, "hi")
+
+  except:
+    echo "EXCEPT: ", getCurrentException().name, ": ", getCurrentExceptionMsg()
+    return
+
+  finally:
+    echo "FINALLY"
+
+try: return_in_except()
+except: echo "RECOVER"
diff --git a/tests/destructor/tgotoexceptions2.nim b/tests/destructor/tgotoexceptions2.nim
new file mode 100644
index 000000000..057caf7b7
--- /dev/null
+++ b/tests/destructor/tgotoexceptions2.nim
@@ -0,0 +1,104 @@
+discard """
+  cmd: "nim c --gc:arc --exceptions:goto $file"
+  output: '''
+B1
+B2
+catch
+A1
+1
+B1
+B2
+catch
+A1
+A2
+0
+B1
+B2
+A1
+1
+B1
+B2
+A1
+A2
+3
+A
+B
+C
+'''
+"""
+
+# More thorough test of return-in-finaly
+
+var raiseEx = true
+var returnA = true
+var returnB = false
+
+proc main: int =
+  try: #A
+    try: #B
+      if raiseEx:
+        raise newException(OSError, "")
+      return 3
+    finally: #B
+      echo "B1"
+      if returnB:
+        return 2
+      echo "B2"
+  except OSError: #A
+    echo "catch"
+  finally: #A
+    echo "A1"
+    if returnA:
+      return 1
+    echo "A2"
+
+for x in [true, false]:
+  for y in [true, false]:
+    # echo "raiseEx: " & $x
+    # echo "returnA: " & $y
+    # echo "returnB: " & $z
+    # in the original test returnB was set to true too and
+    # this leads to swallowing the OSError exception. This is
+    # somewhat compatible with Python but it's non-sense, 'finally'
+    # should not be allowed to swallow exceptions. The goto based
+    # implementation does something sane so we don't "correct" its
+    # behavior just to be compatible with v1.
+    raiseEx = x
+    returnA = y
+    echo main()
+
+# Various tests of return nested in double try/except statements
+
+proc test1() =
+
+  defer: echo "A"
+
+  try:
+    raise newException(OSError, "Problem")
+  except OSError:
+    return
+
+test1()
+
+
+proc test2() =
+
+  defer: echo "B"
+
+  try:
+    return
+  except OSError:
+    discard
+
+test2()
+
+proc test3() =
+  try:
+    try:
+      raise newException(OSError, "Problem")
+    except OSError:
+      return
+  finally:
+    echo "C"
+
+test3()
diff --git a/tests/destructor/tgotoexceptions3.nim b/tests/destructor/tgotoexceptions3.nim
new file mode 100644
index 000000000..308d288b2
--- /dev/null
+++ b/tests/destructor/tgotoexceptions3.nim
@@ -0,0 +1,7 @@
+discard """
+  cmd: "nim c --gc:arc --exceptions:goto $file"
+  outputsub: "Error: unhandled exception: Problem [OSError]"
+  exitcode: "1"
+"""
+
+raise newException(OSError, "Problem")
diff --git a/tests/destructor/tgotoexceptions4.nim b/tests/destructor/tgotoexceptions4.nim
new file mode 100644
index 000000000..b2b481256
--- /dev/null
+++ b/tests/destructor/tgotoexceptions4.nim
@@ -0,0 +1,60 @@
+discard """
+  cmd: "nim c --gc:arc --exceptions:goto $file"
+  output: '''caught in gun
+caught in fun
+caughtsome msgMyExcept
+in finally
+caught1
+123
+123'''
+"""
+
+when true:
+  # bug #13070
+  type MyExcept = object of CatchableError
+  proc gun() =
+    try:
+      raise newException(MyExcept, "some msg")
+    except Exception as eab:
+      echo "caught in gun"
+      raise eab
+
+  proc fun() =
+    try:
+      gun()
+    except Exception as e:
+      echo "caught in fun"
+      echo("caught", e.msg, e.name)
+    finally:
+      echo "in finally"
+  fun()
+
+when true:
+  # bug #13072
+  type MyExceptB = object of CatchableError
+  proc gunB() =
+    raise newException(MyExceptB, "some msg")
+  proc funB() =
+    try:
+      gunB()
+    except CatchableError:
+      echo "caught1"
+  funB()
+
+# bug #13782
+
+import strutils
+var n = 123
+
+try: n = parseInt("xxx")
+except: discard
+
+echo n
+
+proc sameTestButForLocalVar =
+  var n = 123
+  try: n = parseInt("xxx")
+  except: discard
+  echo n
+
+sameTestButForLocalVar()
diff --git a/tests/destructor/tgotoexceptions5.nim b/tests/destructor/tgotoexceptions5.nim
new file mode 100644
index 000000000..695aab0a4
--- /dev/null
+++ b/tests/destructor/tgotoexceptions5.nim
@@ -0,0 +1,45 @@
+discard """
+  output: '''
+before
+swallowed
+before
+swallowed B
+'''
+  cmd: "nim c --gc:arc --exceptions:goto -d:ssl $file"
+"""
+
+# bug #13599
+proc main() =
+  try:
+    echo "before"
+    raise newException(CatchableError, "foo")
+  except AssertionDefect:
+    echo "caught"
+  echo "after"
+
+try:
+  main()
+except:
+  echo "swallowed"
+
+proc mainB() =
+  try:
+    echo "before"
+    raise newException(CatchableError, "foo")
+  # except CatchableError: # would work
+  except AssertionDefect:
+    echo "caught"
+  except:
+    raise
+  echo "after"
+
+try:
+  mainB()
+except:
+  echo "swallowed B"
+
+# bug #14647
+import httpclient
+
+newAsyncHttpClient().close()
+
diff --git a/tests/destructor/tgotoexceptions6.nim b/tests/destructor/tgotoexceptions6.nim
new file mode 100644
index 000000000..7c01f6a52
--- /dev/null
+++ b/tests/destructor/tgotoexceptions6.nim
@@ -0,0 +1,10 @@
+discard """
+  cmd: "nim c --gc:arc --exceptions:goto $file"
+  outputsub: "Error: unhandled exception: virus detected [ValueError]"
+  exitcode: "1"
+"""
+
+# bug #13436
+proc foo =
+  raise newException(ValueError, "virus detected")
+foo()
diff --git a/tests/destructor/tgotoexceptions7.nim b/tests/destructor/tgotoexceptions7.nim
new file mode 100644
index 000000000..c04bd6ba0
--- /dev/null
+++ b/tests/destructor/tgotoexceptions7.nim
@@ -0,0 +1,49 @@
+discard """
+  cmd: "nim c --gc:arc --exceptions:goto --panics:off $file"
+  output: '''prevented!
+caught
+AssertionDefect
+900'''
+"""
+
+type
+  E = enum
+    kindA, kindB
+  Obj = object
+    case kind: E
+    of kindA: s: string
+    of kindB: i: int
+
+  ObjA = ref object of RootObj
+  ObjB = ref object of ObjA
+
+proc takeRange(x: range[0..4]) = discard
+
+proc bplease(x: ObjB) = discard
+
+proc helper = doAssert(false)
+
+proc main(i: int) =
+  var obj = Obj(kind: kindA, s: "abc")
+  {.cast(uncheckedAssign).}:
+    obj.kind = kindB
+  obj.i = 2
+  try:
+    var objA = ObjA()
+    bplease(ObjB(objA))
+  except ObjectConversionDefect:
+    echo "prevented!"
+
+  try:
+    takeRange(i)
+  except RangeDefect:
+    echo "caught"
+
+  try:
+    helper()
+  except AssertionDefect:
+    echo "AssertionDefect"
+
+  echo i * i
+
+main(30)
diff --git a/tests/destructor/tgotoexceptions8.nim b/tests/destructor/tgotoexceptions8.nim
new file mode 100644
index 000000000..8ed2ed0ba
--- /dev/null
+++ b/tests/destructor/tgotoexceptions8.nim
@@ -0,0 +1,76 @@
+discard """
+  output: '''A
+B
+X
+inner finally
+Y
+outer finally
+msg1
+msg2
+finally2
+finally1
+true'''
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #13668
+
+proc main =
+  try:
+    try:
+      raise newException(IOError, "IOError")
+
+    except:
+      echo "A"
+      raise newException(CatchableError, "CatchableError")
+
+  except:
+    echo "B"
+    #discard
+
+proc mainB =
+  try:
+    try:
+      raise newException(IOError, "IOError")
+
+    except:
+      echo "X"
+      raise newException(CatchableError, "CatchableError")
+    finally:
+      echo "inner finally"
+
+  except:
+    echo "Y"
+    #discard
+  finally:
+    echo "outer finally"
+
+main()
+mainB()
+
+when true:
+  #bug 7204
+  proc nested_finally =
+    try:
+      raise newException(KeyError, "msg1")
+    except KeyError as ex:
+      echo ex.msg
+      try:
+        # pop exception
+        raise newException(ValueError, "msg2") # push: exception stack (1 entry)
+      except:
+        echo getCurrentExceptionMsg()
+        # pop exception (except)
+      finally:
+        echo "finally2"
+      # pop exception (except KeyError as ex)
+    finally:
+      echo "finally1"
+
+  nested_finally()
+
+# bug #14925
+proc test(b: bool) =
+  echo b
+
+test(try: true except: false)
diff --git a/tests/destructor/tinvalid_rebind.nim b/tests/destructor/tinvalid_rebind.nim
new file mode 100644
index 000000000..0f15c8f9e
--- /dev/null
+++ b/tests/destructor/tinvalid_rebind.nim
@@ -0,0 +1,15 @@
+discard """
+joinable: false
+cmd: "nim check $file"
+errormsg: "cannot bind another '=destroy' to: Foo; previous declaration was constructed here implicitly: tinvalid_rebind.nim(12, 7)"
+line: 14
+"""
+
+type
+  Foo[T] = object
+
+proc main =
+  var f: Foo[int]
+
+proc `=destroy`[T](f: var Foo[T]) =
+  discard
diff --git a/tests/destructor/tmatrix.nim b/tests/destructor/tmatrix.nim
new file mode 100644
index 000000000..2fd5af789
--- /dev/null
+++ b/tests/destructor/tmatrix.nim
@@ -0,0 +1,135 @@
+discard """
+  output: '''after 2 2
+after 2 2
+after 2 2
+after 2 2'''
+"""
+# bug #9263
+type
+  Matrix* = object
+    # Array for internal storage of elements.
+    data: ptr UncheckedArray[float]
+    # Row and column dimensions.
+    m*, n*: int
+
+var
+  allocCount, deallocCount: int
+
+proc `=destroy`*(m: var Matrix) =
+  if m.data != nil:
+    dealloc(m.data)
+    deallocCount.inc
+    m.data = nil
+    m.m = 0
+    m.n = 0
+
+proc `=sink`*(a: var Matrix; b: Matrix) =
+  if a.data != nil and a.data != b.data:
+    dealloc(a.data)
+    deallocCount.inc
+  a.data = b.data
+  a.m = b.m
+  a.n = b.n
+
+proc `=copy`*(a: var Matrix; b: Matrix) =
+  if a.data != nil and a.data != b.data:
+    dealloc(a.data)
+    deallocCount.inc
+    a.data = nil
+  a.m = b.m
+  a.n = b.n
+  if b.data != nil:
+    a.data = cast[type(a.data)](alloc(a.m * a.n * sizeof(float)))
+    allocCount.inc
+    copyMem(a.data, b.data, b.m * b.n * sizeof(float))
+
+proc `=dup`*(a: Matrix): Matrix =
+  `=copy`(result, a)
+
+proc matrix*(m, n: int, s: float): Matrix =
+  ## Construct an m-by-n constant matrix.
+  result.m = m
+  result.n = n
+  result.data = cast[type(result.data)](alloc(m * n * sizeof(float)))
+  allocCount.inc
+  for i in 0 ..< m * n:
+    result.data[i] = s
+
+proc len(m: Matrix): int = m.n * m.m
+
+proc `[]`*(m: Matrix, i, j: int): float {.inline.} =
+  ## Get a single element.
+  m.data[i * m.n + j]
+
+proc `[]`*(m: var Matrix, i, j: int): var float {.inline.} =
+  ## Get a single element.
+  m.data[i * m.n + j]
+
+proc `[]=`*(m: var Matrix, i, j: int, s: float) =
+  ## Set a single element.
+  m.data[i * m.n + j] = s
+
+proc `-`*(m: sink Matrix): Matrix =
+  ## Unary minus
+  result = m
+  for i in 0 ..< result.m:
+    for j in 0 ..< result.n:
+      result[i, j] = -result[i, j]
+
+proc `+`*(a: sink Matrix; b: Matrix): Matrix =
+  ## ``C = A + B``
+  doAssert(b.m == a.m and b.n == a.n, "Matrix dimensions must agree.")
+  doAssert(a.len == b.len) # non destructive use before sink is ok
+  result = a
+  for i in 0 ..< result.m:
+    for j in 0 ..< result.n:
+      result[i, j] = result[i, j] + b[i, j]
+
+proc `-`*(a: sink Matrix; b: Matrix): Matrix =
+  ## ``C = A - B``
+  assert(b.m == a.m and b.n == a.n, "Matrix dimensions must agree.")
+  doAssert(a.len == b.len) # non destructive use before sink is ok
+  result = a
+  for i in 0 ..< result.m:
+    for j in 0 ..< result.n:
+      result[i, j] = a[i, j] - b[i, j]
+
+proc info =
+  echo "after ", allocCount, " ", deallocCount
+  allocCount = 0
+  deallocCount = 0
+
+proc copy(a: Matrix): Matrix = a
+
+proc test1 =
+  var a = matrix(5, 5, 1.0)
+  var b = copy a
+  var c = a + b
+
+proc test2 =
+  var a = matrix(5, 5, 1.0)
+  var b = copy a
+  var c = -a
+
+proc test3 =
+  var a = matrix(5, 5, 1.0)
+  var b = matrix(5, 5, 2.0)
+  #    a = a - b
+  b = -b + a
+
+proc test4 =
+  # bug #9294
+  var a = matrix(5, 5, 1.0)
+  a = -a + a
+
+test1()
+info()
+
+test2()
+info()
+
+test3()
+info()
+
+test4()
+info()
diff --git a/tests/destructor/tmisc_destructors.nim b/tests/destructor/tmisc_destructors.nim
new file mode 100644
index 000000000..082cb0f78
--- /dev/null
+++ b/tests/destructor/tmisc_destructors.nim
@@ -0,0 +1,43 @@
+discard """
+  output: '''@[0]
+@[1]
+@[2]
+@[3]'''
+  joinable: false
+"""
+
+# bug #6434
+
+type
+  Foo* = object
+    boo: int
+
+var sink_counter = 0
+var assign_counter = 0
+
+proc `=sink`(dest: var Foo, src: Foo) =
+  sink_counter.inc
+
+proc `=`(dest: var Foo, src: Foo) =
+  assign_counter.inc
+
+proc createFoo(): Foo = Foo(boo: 0)
+
+proc test(): auto =
+  var a, b = createFoo()
+  return (a, b, Foo(boo: 5))
+
+var (ag, bg, _) = test()
+
+doAssert assign_counter == 0
+doAssert sink_counter == 0
+
+# bug #11510
+proc main =
+  for i in 0 ..< 4:
+    var buffer: seq[int] # = @[] # uncomment to make it work
+    # var buffer: string # also this is broken
+    buffer.add i
+    echo buffer
+
+main()
diff --git a/tests/destructor/tmove.nim b/tests/destructor/tmove.nim
new file mode 100644
index 000000000..2762aff90
--- /dev/null
+++ b/tests/destructor/tmove.nim
@@ -0,0 +1,18 @@
+discard """
+  targets: "c cpp"
+"""
+
+block:
+  var called = 0
+
+  proc bar(a: var int): var int =
+    inc called
+    result = a
+
+  proc foo =
+    var a = 2
+    var s = move bar(a)
+    doAssert called == 1
+    doAssert s == 2
+
+  foo()
diff --git a/tests/destructor/tmove_objconstr.nim b/tests/destructor/tmove_objconstr.nim
index 8aa12ed05..cdc1eb1c0 100644
--- a/tests/destructor/tmove_objconstr.nim
+++ b/tests/destructor/tmove_objconstr.nim
@@ -7,7 +7,8 @@ test destroyed 0
 3
 4
 Pony is dying!'''
-  cmd: '''nim c --newruntime $file'''
+joinable: false
+targets: "c"
 """
 
 # bug #4214
@@ -36,24 +37,158 @@ proc pointlessWrapper(s: string): Data =
 proc main =
   var x = pointlessWrapper"test"
 
-when isMainModule:
+when true:
   main()
 
 # bug #985
 
 type
-    Pony = object
-        name: string
+  Pony = object
+    name: string
 
 proc `=destroy`(o: var Pony) =
   echo "Pony is dying!"
 
 proc getPony: Pony =
-    result.name = "Sparkles"
+  result = Pony(name: "Sparkles")
 
 iterator items(p: Pony): int =
-    for i in 1..4:
-        yield i
+  for i in 1..4:
+    yield i
 
 for x in getPony():
-    echo x
+  echo x
+
+
+
+
+
+#------------------------------------------------------------
+#-- Move into tuple constructor and move on tuple unpacking
+#------------------------------------------------------------
+
+type
+  MySeqNonCopyable* = object
+    len: int
+    data: ptr UncheckedArray[float]
+
+proc `=destroy`*(m: var MySeqNonCopyable) {.inline.} =
+  if m.data != nil:
+    deallocShared(m.data)
+    m.data = nil
+
+proc `=`*(m: var MySeqNonCopyable, m2: MySeqNonCopyable) {.error.}
+
+proc `=sink`*(m: var MySeqNonCopyable, m2: MySeqNonCopyable) {.inline.} =
+  if m.data != m2.data:
+    if m.data != nil:
+      `=destroy`(m)
+    m.len = m2.len
+    m.data = m2.data
+
+proc len*(m: MySeqNonCopyable): int {.inline.} = m.len
+
+proc `[]`*(m: MySeqNonCopyable; i: int): float {.inline.} =
+  m.data[i.int]
+
+proc `[]=`*(m: var MySeqNonCopyable; i, val: float) {.inline.} =
+  m.data[i.int] = val
+
+proc setTo(s: var MySeqNonCopyable, val: float) =
+  for i in 0..<s.len.int:
+    s.data[i] = val
+
+proc newMySeq*(size: int, initial_value = 0.0): MySeqNonCopyable =#
+  result.len = size
+  if size > 0:
+    result.data = cast[ptr UncheckedArray[float]](createShared(float, size))
+
+  result.setTo(initial_value)
+
+proc myfunc(x, y: int): (MySeqNonCopyable, MySeqNonCopyable) =
+  result = (newMySeq(x, 1.0), newMySeq(y, 5.0))
+
+proc myfunc2(x, y: int): tuple[a: MySeqNonCopyable, b:int, c:MySeqNonCopyable] =
+  var cc = newMySeq(y, 5.0)
+  (a: case x:
+    of 1:
+      let (z1, z2) = myfunc(x, y)
+      z2
+    elif x > 5: raise newException(ValueError, "new error")
+    else: newMySeq(x, 1.0),
+   b: 0,
+   c: block:
+        var tmp = if y > 0: move(cc) else: newMySeq(1, 3.0)
+        tmp[0] = 5
+        tmp
+  )
+
+
+let (seq1, seq2) = myfunc(2, 3)
+doAssert seq1.len == 2
+doAssert seq1[0] == 1.0
+doAssert seq2.len == 3
+doAssert seq2[0] == 5.0
+
+var (seq3, i, _) = myfunc2(2, 3)
+doAssert seq3.len == 2
+doAssert seq3[0] == 1.0
+
+var seq4, seq5: MySeqNonCopyable
+(seq4, i, seq5) = myfunc2(2, 3)
+
+proc foo =
+  seq4 = block:
+    var tmp = newMySeq(4, 1.0)
+    tmp[0] = 3.0
+    tmp
+
+  doAssert seq4[0] == 3.0
+
+
+  seq4 =
+    if i > 0: newMySeq(2, 5.0)
+    elif i < -100: raise newException(ValueError, "Parse Error")
+    else: newMySeq(2, 3.0)
+
+  seq4 =
+    case (char) i:
+      of 'A', {'W'..'Z'}: newMySeq(2, 5.0)
+      of 'B': quit(-1)
+      else:
+        let (x1, x2, x3) = myfunc2(2, 3)
+        x3
+
+foo()
+
+#------------------------------------------------------------
+#-- Move into array constructor
+#------------------------------------------------------------
+
+var ii = 1
+let arr2 = [newMySeq(2, 5.0), if i > 1: newMySeq(3, 1.0) else: newMySeq(0, 0.0)]
+var seqOfSeq2 = @[newMySeq(2, 5.0), newMySeq(3, 1.0)]
+
+
+## issue #10462
+proc myfuncLoop(x: int): MySeqNonCopyable =
+  for i in 0..<x:
+    var cc = newMySeq(i, 5.0)
+    result = cc
+
+discard myfuncLoop(3)
+
+#------------------------------------------------------------
+# Move into table via openArray
+#------------------------------------------------------------
+
+type
+  TableNonCopyable = object
+    x: seq[(string, MySeqNonCopyable)]
+
+proc toTable(pairs: sink openArray[(string, MySeqNonCopyable)]): TableNonCopyable =
+  discard
+
+
+let mytable = {"a": newMySeq(2, 5.0)}.toTable
+
diff --git a/tests/destructor/tnewruntime_misc.nim b/tests/destructor/tnewruntime_misc.nim
new file mode 100644
index 000000000..21c70557d
--- /dev/null
+++ b/tests/destructor/tnewruntime_misc.nim
@@ -0,0 +1,155 @@
+discard """
+  cmd: '''nim cpp -d:nimAllocStats --newruntime --threads:on $file'''
+  output: '''(field: "value")
+Indeed
+axc
+(v: 10)
+...
+destroying GenericObj[T] GenericObj[system.int]
+test
+(allocCount: 12, deallocCount: 10)
+3'''
+"""
+
+import system / ansi_c
+
+import tables
+
+type
+  Node = ref object
+    field: string
+
+# bug #11807
+import os
+putEnv("HEAPTRASHING", "Indeed")
+
+let s1 = getAllocStats()
+
+
+proc newTableOwned[A, B](initialSize = defaultInitialSize): owned(TableRef[A, B]) =
+  new(result)
+  result[] = initTable[A, B](initialSize)
+
+proc main =
+  var w = newTableOwned[string, owned Node]()
+  w["key"] = Node(field: "value")
+  echo w["key"][]
+  echo getEnv("HEAPTRASHING")
+
+  # bug #11891
+  var x = "abc"
+  x[1] = 'x'
+  echo x
+
+main()
+
+# bug #11745
+
+type
+  Foo = object
+    bar: seq[int]
+
+var x = [Foo()]
+
+# bug #11563
+type
+  MyTypeType = enum
+    Zero, One
+  MyType = object
+    case kind: MyTypeType
+    of Zero:
+      s*: seq[MyType]
+    of One:
+      x*: int
+var t: MyType
+
+# bug #11254
+proc test(p: owned proc()) =
+  let x = (proc())p
+
+test(proc() = discard)
+
+# bug #10689
+
+type
+  O = object
+    v: int
+
+proc `=sink`(d: var O, s: O) =
+  d.v = s.v
+
+proc selfAssign =
+  var o = O(v: 10)
+  o = o
+  echo o
+
+selfAssign()
+
+# bug #11833
+type FooAt = object
+
+proc testWrongAt() =
+  var x = @[@[FooAt()]]
+
+testWrongAt()
+
+#-------------------------------------------------
+type
+  Table[A, B] = object
+    x: seq[(A, B)]
+
+
+proc toTable[A,B](p: sink openArray[(A, B)]): Table[A, B] =
+  for zz in mitems(p):
+    result.x.add move(zz)
+
+
+let table = {"a": new(int)}.toTable()
+
+# bug # #12051
+
+type
+  GenericObj[T] = object
+    val: T
+  Generic[T] = owned ref GenericObj[T]
+
+proc `=destroy`[T](x: var GenericObj[T]) =
+  echo "destroying GenericObj[T] ", x.typeof # to know when its being destroyed
+
+proc main12() =
+  let gnrc = Generic[int](val: 42)
+  echo "..."
+
+main12()
+
+#####################################################################
+## bug #12827
+type
+  MyObject = object
+    x: string
+    y: seq[string]
+    needs_ref: ref int
+
+proc xx(xml: string): MyObject =
+  let stream = xml
+  result.x  = xml
+  defer: echo stream
+
+
+discard xx("test")
+
+# Windows has 1 extra allocation in `getEnv` - there it allocates parameter to
+# `_wgetenv` (WideCString). Therefore subtract by 1 to match other OSes'
+# allocation.
+when defined(windows):
+  import std/importutils
+  privateAccess(AllocStats)
+  echo getAllocStats() - s1 - AllocStats(allocCount: 1, deallocCount: 1)
+else:
+  echo getAllocStats() - s1
+
+# bug #13457
+var s = "abcde"
+s.setLen(3)
+
+echo s.cstring.len
diff --git a/tests/destructor/tnewruntime_strutils.nim b/tests/destructor/tnewruntime_strutils.nim
new file mode 100644
index 000000000..9c8d41973
--- /dev/null
+++ b/tests/destructor/tnewruntime_strutils.nim
@@ -0,0 +1,254 @@
+discard """
+  valgrind: true
+  cmd: '''nim c -d:nimAllocStats --gc:arc -d:useMalloc $file'''
+  output: '''
+@[(input: @["KXSC", "BGMC"]), (input: @["PXFX"]), (input: @["WXRQ", "ZSCZD"])]
+14
+First tasks completed.
+Second tasks completed.
+test1'''
+"""
+
+import strutils, os, std / wordwrap
+
+import system / ansi_c
+
+# bug #11004
+proc retTuple(): (seq[int], int) =
+  return (@[1], 1)
+
+# bug #12899
+
+import sequtils, strmisc
+
+const input = ["KXSC, BGMC => 7 PTHL", "PXFX => LBZJ", "WXRQ, ZSCZD => HLQM"]
+
+type
+  Reaction = object
+    input: seq[string]
+
+proc bug12899 =
+  var reactions: seq[Reaction] = @[]
+  for l in input:
+    let x = l.partition(" => ")
+    reactions.add Reaction(input: @(x[0].split(", ")))
+
+  let x = $reactions
+  echo x
+
+bug12899()
+
+
+proc nonStaticTests =
+  doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
+  doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235." # bugs 8242, 12586
+  doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6"
+  doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
+  doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in
+                                                    ["1,0e-11", "1,0e-011"]
+
+  doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
+  doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"
+
+  block: # formatSize tests
+    when not defined(js):
+      doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"   # <=== bug #8231
+    doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
+    doAssert formatSize(4096) == "4KiB"
+    doAssert formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB"
+    doAssert formatSize(4096, includeSpace=true) == "4 KiB"
+    doAssert formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB"
+
+  block: # formatEng tests
+    doAssert formatEng(0, 2, trim=false) == "0.00"
+    doAssert formatEng(0, 2) == "0"
+    doAssert formatEng(53, 2, trim=false) == "53.00"
+    doAssert formatEng(0.053, 2, trim=false) == "53.00e-3"
+    doAssert formatEng(0.053, 4, trim=false) == "53.0000e-3"
+    doAssert formatEng(0.053, 4, trim=true) == "53e-3"
+    doAssert formatEng(0.053, 0) == "53e-3"
+    doAssert formatEng(52731234) == "52.731234e6"
+    doAssert formatEng(-52731234) == "-52.731234e6"
+    doAssert formatEng(52731234, 1) == "52.7e6"
+    doAssert formatEng(-52731234, 1) == "-52.7e6"
+    doAssert formatEng(52731234, 1, decimalSep=',') == "52,7e6"
+    doAssert formatEng(-52731234, 1, decimalSep=',') == "-52,7e6"
+
+    doAssert formatEng(4100, siPrefix=true, unit="V") == "4.1 kV"
+    doAssert formatEng(4.1, siPrefix=true, unit="V", useUnitSpace=true) == "4.1 V"
+    doAssert formatEng(4.1, siPrefix=true) == "4.1" # Note lack of space
+    doAssert formatEng(4100, siPrefix=true) == "4.1 k"
+    doAssert formatEng(4.1, siPrefix=true, unit="", useUnitSpace=true) == "4.1 " # Includes space
+    doAssert formatEng(4100, siPrefix=true, unit="") == "4.1 k"
+    doAssert formatEng(4100) == "4.1e3"
+    doAssert formatEng(4100, unit="V", useUnitSpace=true) == "4.1e3 V"
+    doAssert formatEng(4100, unit="", useUnitSpace=true) == "4.1e3 "
+    # Don't use SI prefix as number is too big
+    doAssert formatEng(3.1e22, siPrefix=true, unit="a", useUnitSpace=true) == "31e21 a"
+    # Don't use SI prefix as number is too small
+    doAssert formatEng(3.1e-25, siPrefix=true, unit="A", useUnitSpace=true) == "310e-27 A"
+
+proc staticTests =
+  doAssert align("abc", 4) == " abc"
+  doAssert align("a", 0) == "a"
+  doAssert align("1232", 6) == "  1232"
+  doAssert align("1232", 6, '#') == "##1232"
+
+  doAssert alignLeft("abc", 4) == "abc "
+  doAssert alignLeft("a", 0) == "a"
+  doAssert alignLeft("1232", 6) == "1232  "
+  doAssert alignLeft("1232", 6, '#') == "1232##"
+
+  let
+    inp = """ this is a long text --  muchlongerthan10chars and here
+                it goes"""
+    outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
+  doAssert wrapWords(inp, 10, false) == outp
+
+  let
+    longInp = """ThisIsOneVeryLongStringWhichWeWillSplitIntoEightSeparatePartsNow"""
+    longOutp = "ThisIsOn\neVeryLon\ngStringW\nhichWeWi\nllSplitI\nntoEight\nSeparate\nPartsNow"
+  doAssert wrapWords(longInp, 8, true) == longOutp
+
+  doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
+            "The cat eats fish."
+
+  doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz "
+  doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz  abc"
+
+  doAssert "-lda-ldz -ld abc".replaceWord("") == "-lda-ldz -ld abc"
+  doAssert "oo".replace("", "abc") == "oo"
+
+  type MyEnum = enum enA, enB, enC, enuD, enE
+  doAssert parseEnum[MyEnum]("enu_D") == enuD
+
+  doAssert parseEnum("invalid enum value", enC) == enC
+
+  doAssert center("foo", 13) == "     foo     "
+  doAssert center("foo", 0) == "foo"
+  doAssert center("foo", 3, fillChar = 'a') == "foo"
+  doAssert center("foo", 10, fillChar = '\t') == "\t\t\tfoo\t\t\t\t"
+
+  doAssert count("foofoofoo", "foofoo") == 1
+  doAssert count("foofoofoo", "foofoo", overlapping = true) == 2
+  doAssert count("foofoofoo", 'f') == 3
+  doAssert count("foofoofoobar", {'f','b'}) == 4
+
+  doAssert strip("  foofoofoo  ") == "foofoofoo"
+  doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo"
+  doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo"
+  doAssert strip("stripme but don't strip this stripme",
+                  chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) ==
+                  " but don't strip this "
+  doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo"
+  doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos"
+
+  doAssert "  foo\n  bar".indent(4, "Q") == "QQQQ  foo\nQQQQ  bar"
+
+  doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab"
+  doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.", "PEOPLE!")) == "HELLO PEOPLE!"
+  doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa"
+
+  doAssert rsplit("foo bar", seps=Whitespace) == @["foo", "bar"]
+  doAssert rsplit(" foo bar", seps=Whitespace, maxsplit=1) == @[" foo", "bar"]
+  doAssert rsplit(" foo bar ", seps=Whitespace, maxsplit=1) == @[" foo bar", ""]
+  doAssert rsplit(":foo:bar", sep=':') == @["", "foo", "bar"]
+  doAssert rsplit(":foo:bar", sep=':', maxsplit=2) == @["", "foo", "bar"]
+  doAssert rsplit(":foo:bar", sep=':', maxsplit=3) == @["", "foo", "bar"]
+  doAssert rsplit("foothebar", sep="the") == @["foo", "bar"]
+
+  doAssert(unescape(r"\x013", "", "") == "\x013")
+
+  doAssert join(["foo", "bar", "baz"]) == "foobarbaz"
+  doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz"
+  doAssert join([1, 2, 3]) == "123"
+  doAssert join(@[1, 2, 3], ", ") == "1, 2, 3"
+
+  doAssert """~~!!foo
+~~!!bar
+~~!!baz""".unindent(2, "~~!!") == "foo\nbar\nbaz"
+
+  doAssert """~~!!foo
+~~!!bar
+~~!!baz""".unindent(2, "~~!!aa") == "~~!!foo\n~~!!bar\n~~!!baz"
+  doAssert """~~foo
+~~  bar
+~~  baz""".unindent(4, "~") == "foo\n  bar\n  baz"
+  doAssert """foo
+  bar
+    baz
+  """.unindent(4) == "foo\nbar\nbaz\n"
+  doAssert """foo
+    bar
+    baz
+  """.unindent(2) == "foo\n  bar\n  baz\n"
+  doAssert """foo
+      bar
+      baz
+    """.unindent(100) == "foo\nbar\nbaz\n"
+
+  doAssert """foo
+      foo
+      bar
+    """.unindent() == "foo\nfoo\nbar\n"
+
+  let s = " this is an example  "
+  let s2 = ":this;is;an:example;;"
+
+  doAssert s.split() == @["", "this", "is", "an", "example", "", ""]
+  doAssert s2.split(seps={':', ';'}) == @["", "this", "is", "an", "example", "", ""]
+  doAssert s.split(maxsplit=4) == @["", "this", "is", "an", "example  "]
+  doAssert s.split(' ', maxsplit=1) == @["", "this is an example  "]
+  doAssert s.split(" ", maxsplit=4) == @["", "this", "is", "an", "example  "]
+
+  doAssert s.splitWhitespace() == @["this", "is", "an", "example"]
+  doAssert s.splitWhitespace(maxsplit=1) == @["this", "is an example  "]
+  doAssert s.splitWhitespace(maxsplit=2) == @["this", "is", "an example  "]
+  doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example  "]
+  doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"]
+
+  discard retTuple()
+
+nonStaticTests()
+staticTests()
+
+# bug #12965
+let xaa = @[""].join()
+let xbb = @["", ""].join()
+
+# bug #16365
+
+# Task 1:
+when true:
+  # Task 1_a:
+  var test_string_a = "name_something"
+  echo test_string_a.len()
+  let new_len_a = test_string_a.len - "_something".len()
+  test_string_a.setLen new_len_a
+
+  echo "First tasks completed."
+
+# Task 2:
+when true:
+  # Task 2_a
+  var test_string: string
+  let some_string = "something"
+  for i in some_string.items:
+    test_string.add $i
+
+  # Task 2_b
+  var test_string_b = "name_something"
+  let new_len_b = test_string_b.len - "_something".len()
+  test_string_b.setLen new_len_b
+
+  echo "Second tasks completed."
+
+# bug #17450
+proc main =
+  var i = 1
+  echo:
+    block:
+      "test" & $i
+
+main()
+
diff --git a/tests/destructor/tnonvardestructor.nim b/tests/destructor/tnonvardestructor.nim
new file mode 100644
index 000000000..1b4413790
--- /dev/null
+++ b/tests/destructor/tnonvardestructor.nim
@@ -0,0 +1,247 @@
+discard """

+  targets: "c cpp"

+  matrix: "--mm:arc; --mm:orc"

+"""

+

+block:

+  type

+    PublicKey = array[32, uint8]

+    PrivateKey = array[64, uint8]

+

+  proc ed25519_create_keypair(publicKey: ptr PublicKey; privateKey: ptr PrivateKey) =

+    publicKey[][0] = uint8(88)

+

+  type

+    KeyPair = object

+      public: PublicKey

+      private: PrivateKey

+

+  proc initKeyPair(): KeyPair =

+    ed25519_create_keypair(result.public.addr, result.private.addr)

+

+  let keys = initKeyPair()

+  doAssert keys.public[0] == 88

+

+

+template minIndexByIt: untyped =

+  var other = 3

+  other

+

+proc bug20303() =

+  var hlibs = @["hello", "world", "how", "are", "you"]

+  let res = hlibs[minIndexByIt()]

+  doAssert res == "are"

+

+bug20303()

+

+proc main() = # todo bug with templates

+  block: # bug #11267

+    var a: seq[char] = block: @[]

+    doAssert a == @[]

+    # 2

+    proc b: seq[string] =

+      discard

+      @[]

+    doAssert b() == @[]

+static: main()

+main()

+

+

+type Obj = tuple

+  value: int

+  arr: seq[int]

+

+proc bug(): seq[Obj] =

+  result.add (value: 0, arr: @[])

+  result[^1].value = 1

+  result[^1].arr.add 1

+

+# bug #19990

+let s = bug()

+doAssert s[0] == (value: 1, arr: @[1])

+

+block: # bug #21974

+  type Test[T] = ref object

+      values : seq[T]

+      counter: int

+

+  proc newTest[T](): Test[T] =

+      result         = new(Test[T])

+      result.values  = newSeq[T](16)

+      result.counter = 0

+

+  proc push[T](self: Test[T], value: T) =

+      self.counter += 1

+      if self.counter >= self.values.len:

+          self.values.setLen(self.values.len * 2)

+      self.values[self.counter - 1] = value

+

+  proc pop[T](self: Test[T]): T =

+      result         = self.values[0]

+      self.values[0] = self.values[self.counter - 1] # <--- This line

+      self.counter  -= 1

+

+

+  type X = tuple

+      priority: int

+      value   : string

+

+  var a = newTest[X]()

+  a.push((1, "One"))

+  doAssert a.pop.value == "One"

+

+# bug #21987

+

+type

+  EmbeddedImage* = distinct Image

+  Image = object

+    len: int

+

+proc imageCopy*(image: Image): Image {.nodestroy.}

+

+proc `=destroy`*(x: Image) =

+  discard

+

+proc `=sink`*(dest: var Image; source: Image) =

+  `=destroy`(dest)

+  wasMoved(dest)

+

+proc `=dup`*(source: Image): Image {.nodestroy.} =

+  result = imageCopy(source)

+

+proc `=copy`*(dest: var Image; source: Image) =

+  dest = imageCopy(source) # calls =sink implicitly

+

+proc `=destroy`*(x: EmbeddedImage) = discard

+

+proc `=dup`*(source: EmbeddedImage): EmbeddedImage {.nodestroy.} = source

+

+proc `=copy`*(dest: var EmbeddedImage; source: EmbeddedImage) {.nodestroy.} =

+  dest = source

+

+proc imageCopy*(image: Image): Image =

+  result = image

+

+proc main2 =

+  block:

+    var a = Image(len: 2).EmbeddedImage

+    var b = Image(len: 1).EmbeddedImage

+    b = a

+    doAssert Image(a).len == 2

+    doAssert Image(b).len == 2

+

+  block:

+    var a = Image(len: 2)

+    var b = Image(len: 1)

+    b = a

+    doAssert a.len == 2

+    doAssert b.len == 0

+

+main2()

+

+type

+  Edge = object

+    neighbor {.cursor.}: Node

+

+  NodeObj = object

+    neighbors: seq[Edge]

+    label: string

+    visited: bool

+  Node = ref NodeObj

+

+  Graph = object

+    nodes: seq[Node]

+

+proc `=destroy`(x: NodeObj) =

+  `=destroy`(x.neighbors)

+  `=destroy`(x.label)

+

+proc addNode(self: var Graph; label: string): Node =

+  self.nodes.add(Node(label: label))

+  result = self.nodes[^1]

+

+proc addEdge(self: Graph; source, neighbor: Node) =

+  source.neighbors.add(Edge(neighbor: neighbor))

+

+block:

+  proc main =

+    var graph: Graph

+    let nodeA = graph.addNode("a")

+    let nodeB = graph.addNode("b")

+    let nodeC = graph.addNode("c")

+

+    graph.addEdge(nodeA, neighbor = nodeB)

+    graph.addEdge(nodeA, neighbor = nodeC)

+

+  main()

+

+block:

+  type RefObj = ref object

+

+  proc `[]`(val: static[int]) = # works with different name/overload or without static arg

+    discard

+

+  template noRef(T: typedesc): typedesc = # works without template indirection

+    typeof(default(T)[])

+

+  proc `=destroy`(x: noRef(RefObj)) =

+    discard

+

+  proc foo =

+    var x = new RefObj

+    doAssert $(x[]) == "()"

+

+  # bug #11705

+  foo()

+

+block: # bug #22197

+  type

+    H5IdObj = object

+    H5Id = ref H5IdObj

+

+    FileID = distinct H5Id

+

+    H5GroupObj = object

+      file_id: FileID

+    H5Group = ref H5GroupObj

+

+  ## This would make it work!

+  #proc `=destroy`*(x: FileID) = `=destroy`(cast[H5Id](x))

+  ## If this does not exist, it also works!

+  proc newFileID(): FileID = FileID(H5Id())

+

+  proc `=destroy`(grp: H5GroupObj) =

+    ## Closes the group and resets all references to nil.

+    if cast[pointer](grp.fileId) != nil:

+      `=destroy`(grp.file_id)

+

+  var grp = H5Group()

+  reset(grp.file_id)

+  reset(grp)

+

+import std/tables

+

+block: # bug #22286

+  type

+    A = object

+    B = object

+      a: A

+    C = object

+      b: B

+

+  proc `=destroy`(self: A) =

+    echo "destroyed"

+

+  proc `=destroy`(self: C) =

+    `=destroy`(self.b)

+

+  var c = C()

+

+block: # https://forum.nim-lang.org/t/10642

+  type AObj = object

+    name: string

+    tableField: Table[string, string]

+

+  proc `=destroy`(x: AObj) =

+    `=destroy`(x.name)

+    `=destroy`(x.tableField)

diff --git a/tests/destructor/tobjfield_analysis.nim b/tests/destructor/tobjfield_analysis.nim
new file mode 100644
index 000000000..83f394c3b
--- /dev/null
+++ b/tests/destructor/tobjfield_analysis.nim
@@ -0,0 +1,51 @@
+discard """
+  output: '''works'''
+"""
+
+#  bug #11095
+
+type
+  MyVal[T] = object
+    f: ptr T
+
+proc `=destroy`[T](x: var MyVal[T]) =
+  if x.f != nil:
+    dealloc(x.f)
+
+proc `=sink`[T](x1: var MyVal[T], x2: MyVal[T]) =
+  if x1.f != x2.f:
+    `=destroy`(x1)
+    x1.f = x2.f
+
+proc `=`[T](x1: var MyVal[T], x2: MyVal[T]) {.error.}
+
+proc newVal[T](x: sink T): MyVal[T] =
+  result.f = create(T)
+  result.f[] = x
+
+proc set[T](x: var MyVal[T], val: T) =
+  x.f[] = val
+
+proc sinkMe[T](x: sink MyVal[T]) =
+  discard
+
+var flag = false
+
+proc main =
+  var y = case flag
+    of true:
+      var x1 = newVal[float](1.0)
+      var x2 = newVal[float](2.0)
+      (newVal(x1), newVal(x2))
+
+    of false:
+      var x1 = newVal[float](1.0)
+      var x2 = newVal[float](2.0)
+      (newVal(x1), newVal(x2))
+
+  sinkMe y[0]
+  sinkMe y[1]
+  echo "works"
+
+main()
+
diff --git a/tests/destructor/topt.nim b/tests/destructor/topt.nim
new file mode 100644
index 000000000..4adda1914
--- /dev/null
+++ b/tests/destructor/topt.nim
@@ -0,0 +1,61 @@
+
+discard """
+  output: '''5
+vseq destroy
+'''
+joinable: false
+"""
+type
+  opt*[T] = object
+    case exists: bool
+      of true: val: T
+      of false: discard
+
+proc some*[T](val: sink T): opt[T] {.inline.} =
+  ## Returns an ``opt`` that has the value.
+  ## nil is considered as none for reference types
+  result = opt[T](exists: true, val: val)
+
+proc none*(T: typedesc): opt[T] {.inline.} =
+  ## Returns an ``opt`` for this type that has no value.
+  # the default is the none type
+  discard
+
+proc none*[T]: opt[T] {.inline.} =
+  ## Alias for ``none(T)``.
+  none(T)
+
+proc unsafeGet*[T](self: opt[T]): lent T {.inline.} =
+  ## Returns the value of a ``some``. Behavior is undefined for ``none``.
+  self.val
+
+type
+  VSeq*[T] = object
+    len: int
+    data: ptr UncheckedArray[T]
+
+proc `=destroy`*[T](m: var VSeq[T]) {.inline.} =
+  if m.data != nil:
+    echo "vseq destroy"
+    dealloc(m.data)
+    m.data = nil
+
+proc `=`*[T](m: var VSeq[T], m2: VSeq[T]) {.error.}
+
+proc `=sink`*[T](m: var VSeq[T], m2: VSeq[T]) {.inline.} =
+  if m.data != m2.data:
+    `=destroy`(m)
+  m.len = m2.len
+  m.data = m2.data
+
+proc newVSeq*[T](len: int): VSeq[T] =
+  ## Only support sequence creation from scalar size because creation from
+  ## vetorized size can't reproduce the original scalar size
+  result.len = len
+  if len > 0:
+    result.data = cast[ptr UncheckedArray[T]](alloc(sizeof(T) * len))
+
+let x = some newVSeq[float](5)
+echo x.unsafeGet.len
+let y = none(VSeq[float])
+
diff --git a/tests/destructor/topttree.nim b/tests/destructor/topttree.nim
index 924644392..8cf757e8b 100644
--- a/tests/destructor/topttree.nim
+++ b/tests/destructor/topttree.nim
@@ -1,4 +1,5 @@
 discard """
+  disabled: i386
   output: '''10.0
 60.0
 90.0
@@ -8,7 +9,7 @@ discard """
 90.0
 120.0
 8 8'''
-  cmd: '''nim c --newruntime $file'''
+joinable: false
 """
 
 import typetraits
@@ -22,6 +23,7 @@ var
 
 proc `=destroy`*[T](x: var opt[T]) =
   if x.data != nil:
+    mixin `=destroy`
     when not supportsCopyMem(T):
       `=destroy`(x.data[])
     dealloc(x.data)
@@ -90,6 +92,8 @@ proc write(t: opt[Tree]) =
     write stdout, it.data, "\n"
     write(it.ri)
 
+proc use(t: opt[Tree]) = discard
+
 proc main =
   var t: opt[Tree]
   insert t, 60.0
@@ -99,6 +103,7 @@ proc main =
   write t
   let copy = t
   write copy
+  use t
 
 main()
 echo allocCount, " ", deallocCount
diff --git a/tests/destructor/towned_binary_tree.nim b/tests/destructor/towned_binary_tree.nim
new file mode 100644
index 000000000..fb635e7c6
--- /dev/null
+++ b/tests/destructor/towned_binary_tree.nim
@@ -0,0 +1,91 @@
+discard """
+  cmd: '''nim c -d:nimAllocStats --gc:arc $file'''
+  output: '''31665
+(allocCount: 33334, deallocCount: 33334)'''
+"""
+
+#  bug #11053
+
+import random
+
+type Node = ref object
+  x, y: int32
+  left, right: owned Node
+
+proc newNode(x: int32): owned Node =
+  result = Node(x: x, y: rand(high int32).int32)
+
+proc merge(lower, greater: owned Node): owned Node =
+  if lower.isNil:
+    result = greater
+  elif greater.isNil:
+    result = lower
+  elif lower.y < greater.y:
+    lower.right = merge(lower.right, greater)
+    result = lower
+  else:
+    greater.left = merge(lower, greater.left)
+    result = greater
+
+proc splitBinary(orig: owned Node, value: int32): (owned Node, owned Node) =
+  if orig.isNil:
+    result = (nil, nil)
+  elif orig.x < value:
+    let splitPair = splitBinary(orig.right, value)
+    orig.right = splitPair[0]
+    result = (orig, splitPair[1])
+  else:
+    let splitPair = splitBinary(orig.left, value)
+    orig.left = splitPair[1]
+    result = (splitPair[0], orig)
+
+proc merge3(lower, equal, greater: owned Node): owned Node =
+  merge(merge(lower, equal), greater)
+
+proc split(orig: owned Node, value: int32): tuple[lower, equal, greater: owned Node] =
+  let
+    (lower, equalGreater) = splitBinary(orig, value)
+    (equal, greater) = splitBinary(equalGreater, value + 1)
+  result = (lower, equal, greater)
+
+type Tree = object
+  root: owned Node
+
+proc hasValue(self: var Tree, x: int32): bool =
+  let splited = split(move self.root, x)
+  result = not splited.equal.isNil
+  self.root = merge3(splited.lower, splited.equal, splited.greater)
+
+proc insert(self: var Tree, x: int32) =
+  var splited = split(move self.root, x)
+  if splited.equal.isNil:
+    splited.equal = newNode(x)
+  self.root = merge3(splited.lower, splited.equal, splited.greater)
+
+proc erase(self: var Tree, x: int32) =
+  let splited = split(move self.root, x)
+  self.root = merge(splited.lower, splited.greater)
+
+proc main() =
+  var
+    tree = Tree()
+    cur = 5'i32
+    res = 0
+
+  for i in 1 ..< 100000:
+    let a = i mod 3
+    cur = (cur * 57 + 43) mod 10007
+    case a:
+    of 0:
+      tree.insert(cur)
+    of 1:
+      tree.erase(cur)
+    of 2:
+      if tree.hasValue(cur):
+        res += 1
+    else:
+      discard
+  echo res
+
+dumpAllocStats:
+  main()
diff --git a/tests/destructor/tprevent_assign.nim b/tests/destructor/tprevent_assign.nim
new file mode 100644
index 000000000..4c484ebc1
--- /dev/null
+++ b/tests/destructor/tprevent_assign.nim
@@ -0,0 +1,33 @@
+discard """
+  errormsg: "'=copy' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'"
+  line: 29
+"""
+
+type
+  Foo = object
+    x: int
+
+proc `=destroy`(f: var Foo) = f.x = 0
+proc `=`(a: var Foo; b: Foo) {.error.} # = a.x = b.x
+proc `=sink`(a: var Foo; b: Foo) = a.x = b.x
+
+proc createTree(x: int): Foo =
+  Foo(x: x)
+
+proc take2(a, b: sink Foo) =
+  echo a.x, " ", b.x
+
+proc allowThis() =
+  # all these temporary lets are harmless:
+  let otherTree = createTree(44)
+  let b = otherTree
+  let c = b
+  take2(createTree(34), c)
+
+proc preventThis() =
+  let otherTree = createTree(44)
+  let b = otherTree
+  take2(createTree(34), otherTree)
+
+allowThis()
+preventThis()
diff --git a/tests/destructor/tprevent_assign2.nim b/tests/destructor/tprevent_assign2.nim
new file mode 100644
index 000000000..eb5588b1a
--- /dev/null
+++ b/tests/destructor/tprevent_assign2.nim
@@ -0,0 +1,56 @@
+discard """
+  errormsg: "'=dup' is not available for type <Foo>, which is inferred from unavailable '=copy'; requires a copy because it's not the last read of 'otherTree'; another read is done here: tprevent_assign2.nim(51, 31); routine: preventThis"
+  file: "tprevent_assign2.nim"
+  line: 49
+"""
+
+type
+  Foo = object
+    x: int
+
+proc `=destroy`(f: var Foo) = f.x = 0
+proc `=copy`(a: var Foo; b: Foo) {.error.} # = a.x = b.x
+
+proc `=sink`(a: var Foo; b: Foo) = a.x = b.x
+
+proc createTree(x: int): Foo =
+  Foo(x: x)
+
+proc take2(a, b: sink Foo) =
+  echo a.x, " ", b.x
+
+when false:
+  var otherTree: Foo
+  try:
+    for i in 0..3:
+      while true:
+        #if i == 0:
+        otherTree = createTree(44)
+        case i
+        of 0:
+          echo otherTree
+          take2(createTree(34), otherTree)
+        of 1:
+          take2(createTree(34), otherTree)
+        else:
+          discard
+  finally:
+    discard
+
+proc preventThis() =
+  var otherTree: Foo
+  for i in 0..3:
+    while true:
+      if i == 0:
+        otherTree = createTree(44)
+      case i
+      of 0:
+        echo otherTree
+        take2(createTree(34), otherTree)
+      of 1:
+        take2(createTree(34), otherTree)
+      else:
+        discard
+
+#allowThis()
+preventThis()
diff --git a/tests/destructor/tprevent_assign3.nim b/tests/destructor/tprevent_assign3.nim
new file mode 100644
index 000000000..aa834a66c
--- /dev/null
+++ b/tests/destructor/tprevent_assign3.nim
@@ -0,0 +1,54 @@
+discard """
+  errormsg: "'=dup' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'"
+  file: "tprevent_assign3.nim"
+  line: 47
+"""
+
+type
+  Foo = object
+    x: int
+
+proc `=destroy`(f: var Foo) = f.x = 0
+proc `=copy`(a: var Foo; b: Foo) {.error.} # = a.x = b.x
+proc `=dup`(a: Foo): Foo {.error.}
+proc `=sink`(a: var Foo; b: Foo) = a.x = b.x
+
+proc createTree(x: int): Foo =
+  Foo(x: x)
+
+proc take2(a, b: sink Foo) =
+  echo a.x, " ", b.x
+
+when false:
+  var otherTree: Foo
+  try:
+    for i in 0..3:
+      while true:
+        #if i == 0:
+        otherTree = createTree(44)
+        case i
+        of 0:
+          echo otherTree
+          take2(createTree(34), otherTree)
+        of 1:
+          take2(createTree(34), otherTree)
+        else:
+          discard
+  finally:
+    discard
+
+proc preventThis2() =
+  var otherTree: Foo
+  try:
+    try:
+      otherTree = createTree(44)
+      echo otherTree
+    finally:
+      take2(createTree(34), otherTree)
+  finally:
+    echo otherTree
+
+#allowThis()
+preventThis2()
+
+
diff --git a/tests/destructor/trecursive.nim b/tests/destructor/trecursive.nim
new file mode 100644
index 000000000..e7afa6ba9
--- /dev/null
+++ b/tests/destructor/trecursive.nim
@@ -0,0 +1,60 @@
+
+discard """
+   output: '''
+test1 OK
+'''
+"""
+
+import smart_ptr
+
+type
+  Node[T] = object
+    value: T
+    next: SharedPtr[Node[T]]
+
+  ForwardList[T] = object
+    first: SharedPtr[Node[T]]
+    len: Natural
+
+proc pushFront*[T] (list: var ForwardList[T], val: sink T) =
+  var newNode = newSharedPtr(Node[T](value: val))
+  var result = false
+  while not result:
+    var head = list.first
+    newNode.get.next = head
+    result = list.first.cas(head, newNode)
+  list.len.atomicInc()
+
+proc test1() =
+  var list: ForwardList[int]
+  list.pushFront(1)
+  doAssert list.len == 1
+  echo "test1 OK"
+
+test1()
+
+#------------------------------------------------------------------------------
+# issue #14217
+
+type
+  MyObject = object
+    p: ptr int
+
+proc `=destroy`(x: var MyObject) =
+  if x.p != nil:
+    deallocShared(x.p)
+
+proc `=`(x: var MyObject, y: MyObject) {.error.}
+
+proc newMyObject(i: int): MyObject = 
+  result.p = createShared(int)
+  result.p[] = i
+
+proc test: seq[MyObject] = 
+  for i in 0..3:
+    let x = newMyObject(i)
+    result.add x
+
+var x = test()
+for i in 0..3:
+  doAssert(x[i].p[] == i)
diff --git a/tests/destructor/tselect.nim b/tests/destructor/tselect.nim
new file mode 100644
index 000000000..c22bf7203
--- /dev/null
+++ b/tests/destructor/tselect.nim
@@ -0,0 +1,50 @@
+discard """
+   output: '''abcsuffix
+xyzsuffix
+destroy foo 2
+destroy foo 1
+'''
+  cmd: '''nim c --gc:arc $file'''
+"""
+
+proc select(cond: bool; a, b: sink string): string =
+  if cond:
+    result = a # moves a into result
+  else:
+    result = b # moves b into result
+
+proc test(param: string; cond: bool) =
+  var x = "abc" & param
+  var y = "xyz" & param
+
+  # possible self-assignment:
+  x = select(cond, x, y)
+
+  echo x
+  # 'select' must communicate what parameter has been
+  # consumed. We cannot simply generate:
+  # (select(...); wasMoved(x); wasMoved(y))
+
+test("suffix", true)
+test("suffix", false)
+
+
+
+#--------------------------------------------------------------------
+# issue #13659
+
+type
+  Foo = ref object
+    data: int
+    parent: Foo
+
+proc `=destroy`(self: var type(Foo()[])) =
+  echo "destroy foo ", self.data
+  for i in self.fields: i.reset
+
+proc getParent(self: Foo): Foo = self.parent
+
+var foo1 = Foo(data: 1)
+var foo2 = Foo(data: 2, parent: foo1)
+
+foo2.getParent.data = 1
\ No newline at end of file
diff --git a/tests/destructor/tsetjmp_raise.nim b/tests/destructor/tsetjmp_raise.nim
new file mode 100644
index 000000000..3a9803f39
--- /dev/null
+++ b/tests/destructor/tsetjmp_raise.nim
@@ -0,0 +1,11 @@
+discard """
+  outputsub: "index 2 not in 0 .. 0 [IndexDefect]"
+  exitcode: 1
+  cmd: "nim c --gc:arc --exceptions:setjmp $file"
+"""
+
+# bug #12961
+# --gc:arc --exceptions:setjmp
+let a = @[1]
+echo a[2]
+
diff --git a/tests/destructor/tsimpleclosure.nim b/tests/destructor/tsimpleclosure.nim
new file mode 100644
index 000000000..9626dd6f8
--- /dev/null
+++ b/tests/destructor/tsimpleclosure.nim
@@ -0,0 +1,62 @@
+discard """
+  cmd: '''nim c -d:nimAllocStats --gc:arc $file'''
+  output: '''a b
+70
+hello
+hello
+hello
+(allocCount: 3, deallocCount: 3)'''
+"""
+
+import system / ansi_c
+
+proc main(): owned(proc()) =
+  var a = "a"
+  var b = "b"
+  result = proc() =
+    echo a, " ", b
+
+
+proc foo(f: (iterator(): int)) =
+  for i in f(): echo i
+
+proc wrap =
+  let p = main()
+  p()
+
+  let fIt = iterator(): int = yield 70
+  foo fIt
+
+wrap()
+
+# bug #11533
+proc say = echo "hello"
+
+# Error: internal error: genAssignment: tyNil
+var err0: proc() = say
+err0()
+
+var ok0: proc()
+ok0 = say
+ok0()
+
+var ok1 = say
+ok1()
+
+when false:
+  # bug #12443
+  func newStringIterator(s: string): owned(iterator(): char) =
+    result = iterator(): char =
+      var pos = 0
+      while pos < s.len:
+        yield s[pos]
+        inc pos
+
+  proc stringIter() =
+    let si = newStringIterator("foo")
+    for i in si():
+      echo i
+
+  stringIter()
+
+echo getAllocStats()
diff --git a/tests/destructor/tsink.nim b/tests/destructor/tsink.nim
new file mode 100644
index 000000000..e8750ad7c
--- /dev/null
+++ b/tests/destructor/tsink.nim
@@ -0,0 +1,70 @@
+discard """
+  matrix: "--mm:arc"
+"""
+
+type AnObject = object of RootObj
+  value*: int
+
+proc mutate(shit: sink AnObject) =
+  shit.value = 1
+
+proc foo = # bug #23359
+  var bar = AnObject(value: 42)
+  mutate(bar)
+  doAssert bar.value == 42
+
+foo()
+
+block: # bug #23902
+  proc foo(a: sink string): auto = (a, a)
+
+  proc bar(a: sink int): auto = return a
+
+  proc foo(a: sink string) =
+    var x = (a, a)
+
+block: # bug #24175
+  block:
+    func mutate(o: sink string): string =
+      o[1] = '1'
+      result = o
+
+    static:
+      let s = "999"
+      let m = mutate(s)
+      doAssert s == "999"
+      doAssert m == "919"
+
+    func foo() =
+      let s = "999"
+      let m = mutate(s)
+      doAssert s == "999"
+      doAssert m == "919"
+
+    static:
+      foo()
+    foo()
+
+  block:
+    type O = object
+      a: int
+
+    func mutate(o: sink O): O =
+      o.a += 1
+      o
+
+    static:
+      let x = O(a: 1)
+      let y = mutate(x)
+      doAssert x.a == 1
+      doAssert y.a == 2
+
+    proc foo() =
+      let x = O(a: 1)
+      let y = mutate(x)
+      doAssert x.a == 1
+      doAssert y.a == 2
+
+    static:
+      foo()
+    foo()
diff --git a/tests/destructor/ttuple.nim b/tests/destructor/ttuple.nim
new file mode 100644
index 000000000..d0ea72c60
--- /dev/null
+++ b/tests/destructor/ttuple.nim
@@ -0,0 +1,130 @@
+
+discard """
+   output: '''5.0 10.0
+=destroy
+=destroy
+'''
+"""
+
+type
+  MyOpt[T] = object
+    case has: bool:
+      of true: val: T
+      of false: nil
+
+  MyVal = object
+    f: ptr float
+
+proc `=destroy`(x: var MyVal) =
+  if x.f != nil:
+    dealloc(x.f)
+
+proc `=sink`(x1: var MyVal, x2: Myval) =
+  if x1.f != x2.f:
+    `=destroy`(x1)
+    x1.f = x2.f
+
+proc `=`(x1: var MyVal, x2: Myval) =
+  if x1.f != x2.f:
+    `=destroy`(x1)
+    x1.f = create(float)
+    x1.f[] = x2.f[]
+
+proc newVal(x: float): MyVal =
+  result.f = create(float)
+  result.f[] = x
+
+template getIt[T, R](self: MyOpt[T], body: untyped, default: R): R =
+  if self.has:
+    template it: untyped {.inject.} = self.val
+    body
+  else:
+    default
+
+proc myproc(h: MyOpt[float]) =
+  let (a, b) = h.getIt((newVal(it), newVal(it * 2)), (newVal(1.0), newVal(1.0)))
+  echo a.f[], " ", b.f[]
+
+let h = MyOpt[float](has: true, val: 5.0)
+myproc(h)
+
+
+#-------------------------------------------------------------
+type
+  MyObject* = object
+    len*: int
+    amount: UncheckedArray[float]
+
+  MyObjPtr* = ptr MyObject
+
+  MyObjContainer* {.byref.} = object
+    size1: int
+    size2: int
+    data: ptr UncheckedArray[MyObjPtr]
+
+ 
+proc size1*(m: MyObjContainer): int {.inline.} = m.size1
+proc size2*(m: MyObjContainer): int {.inline.} = m.size2
+
+proc allocateMyObjPtr(size2: int): MyObjPtr =
+  cast[MyObjPtr](allocShared(sizeof(MyObject) + sizeof(float) * size2.int))
+
+proc `=destroy`*(m: var MyObjContainer) {.inline.} =
+  if m.data != nil:
+    for i in 0..<m.size1:
+      if m.data[i] != nil:
+        deallocShared(m.data[i])
+        m.data[i] = nil
+    deallocShared(m.data)
+    echo "=destroy"
+    m.data = nil
+
+proc `=sink`*(m: var MyObjContainer, m2: MyObjContainer) {.inline.} =
+  if m.data != m2.data:
+    `=destroy`(m)
+  m.size1 = m2.size1
+  m.size2 = m2.size2  
+  m.data = m2.data
+
+
+proc `=`*(m: var MyObjContainer, m2: MyObjContainer) {.error.}
+  ## non copyable
+
+func newMyObjContainer*(size2: Natural): MyObjContainer =
+  result.size2 = size2
+
+proc push(m: var MyObjContainer, cf: MyObjPtr) =
+  ## Add MyObjPtr to MyObjContainer, shallow copy
+  m.size1.inc
+  m.data = cast[ptr UncheckedArray[MyObjPtr]](reallocShared(m.data, m.size1 * sizeof(MyObjPtr)))
+  m.data[m.size1 - 1] = cf
+
+ 
+proc add*(m: var MyObjContainer, amount: float) =
+  assert m.size2 > 0, "MyObjContainer is not initialized, use newMyObjContainer() to initialize object before use"
+  let cf = allocateMyObjPtr(m.size2)
+  for i in 0..<m.size2:
+    cf.amount[i.int] = amount
+
+  m.push(cf)
+
+proc add*(dest: var MyObjContainer, src: sink MyObjContainer) =
+  # merge containers
+
+  for i in 0..<src.size1:
+    dest.push src.data[i]
+    src.data[i] = nil
+
+ 
+proc test = 
+  var cf1 = newMyObjContainer(100)
+  cf1.add(1)
+  cf1.add(2)
+
+  var cf3 = newMyObjContainer(100)
+  cf3.add(2)
+  cf3.add(3)
+
+  cf1.add(cf3)
+
+test()
diff --git a/tests/destructor/turn_destroy_into_finalizer.nim b/tests/destructor/turn_destroy_into_finalizer.nim
new file mode 100644
index 000000000..1409c1c57
--- /dev/null
+++ b/tests/destructor/turn_destroy_into_finalizer.nim
@@ -0,0 +1,26 @@
+discard """
+  output: "turn_destroy_into_finalizer works"
+  joinable: false
+"""
+
+type
+  Foo = object
+    id: int
+
+var destroyed: int
+
+proc `=destroy`(x: var Foo) =
+  #echo "finally ", x.id
+  inc destroyed
+
+proc main =
+  var r: ref Foo
+  for i in 1..50_000:
+    new(r)
+    r.id = i
+  if destroyed > 30_000:
+    echo "turn_destroy_into_finalizer works"
+  else:
+    echo "turn_destroy_into_finalizer failed: ", destroyed
+
+main()
diff --git a/tests/destructor/tuse_ownedref_after_move.nim b/tests/destructor/tuse_ownedref_after_move.nim
new file mode 100644
index 000000000..69348d530
--- /dev/null
+++ b/tests/destructor/tuse_ownedref_after_move.nim
@@ -0,0 +1,57 @@
+discard """
+  cmd: '''nim c --newruntime $file'''
+  errormsg: "'=copy' is not available for type <owned Button>; requires a copy because it's not the last read of ':envAlt.b1'; routine: main"
+  line: 48
+"""
+
+import system / ansi_c
+
+type
+  Widget* = ref object of RootObj
+    drawImpl: owned(proc (self: Widget))
+
+  Button* = ref object of Widget
+    caption: string
+    onclick: owned(proc())
+
+  Window* = ref object of Widget
+    elements: seq[owned Widget]
+
+
+proc newButton(caption: string; onclick: owned(proc())): owned Button =
+  proc draw(self: Widget) =
+    let b = Button(self)
+    echo b.caption
+
+  result = Button(drawImpl: draw, caption: caption, onclick: onclick)
+
+proc newWindow(): owned Window =
+  proc draw(self: Widget) =
+    let w = Window(self)
+    for e in w.elements:
+      if not e.drawImpl.isNil: e.drawImpl(e)
+
+  result = Window(drawImpl: draw, elements: @[])
+
+proc draw(w: Widget) =
+  if not w.drawImpl.isNil: w.drawImpl(w)
+
+proc add*(w: Window; elem: owned Widget) =
+  w.elements.add elem
+
+proc main =
+  var w = newWindow()
+
+  var b = newButton("button", nil)
+  b.onclick = proc () =
+    b.caption = "clicked!"
+  w.add b
+
+  w.draw()
+  # simulate button click:
+  b.onclick()
+
+  w.draw()
+
+main()
+
diff --git a/tests/destructor/tuse_result_prevents_sinks.nim b/tests/destructor/tuse_result_prevents_sinks.nim
new file mode 100644
index 000000000..e74c16da3
--- /dev/null
+++ b/tests/destructor/tuse_result_prevents_sinks.nim
@@ -0,0 +1,37 @@
+discard """
+  output: ""
+  targets: "c"
+"""
+
+# bug #9594
+
+type
+  Foo = object
+    i: int
+
+proc `=`(self: var Foo; other: Foo) =
+  self.i = other.i + 1
+
+proc `=sink`(self: var Foo; other: Foo) =
+  self.i = other.i
+
+proc `=destroy`(self: var Foo) = discard
+
+template preventCursorInference(x) =
+  let p = addr(x)
+
+proc test(): Foo =
+  result = Foo()
+  let temp = result
+  preventCursorInference temp
+  doAssert temp.i > 0
+  return result
+
+proc testB(): Foo =
+  result = Foo()
+  let temp = result
+  preventCursorInference temp
+  doAssert temp.i > 0
+
+discard test()
+discard testB()
diff --git a/tests/destructor/tv2_cast.nim b/tests/destructor/tv2_cast.nim
new file mode 100644
index 000000000..48bdf67dd
--- /dev/null
+++ b/tests/destructor/tv2_cast.nim
@@ -0,0 +1,116 @@
+discard """
+  output: '''@[1]
+@[116, 101, 115, 116]
+@[1953719668, 875770417]
+destroying O1'''
+  cmd: '''nim c --mm:arc --expandArc:main --expandArc:main1 --expandArc:main2 --expandArc:main3 --hints:off --assertions:off $file'''
+  nimout: '''
+--expandArc: main
+
+var
+  data
+  :tmpD
+data = cast[string](encode(cast[seq[byte]](
+  :tmpD = newString(100)
+  :tmpD)))
+`=destroy`(:tmpD)
+`=destroy`(data)
+-- end of expandArc ------------------------
+--expandArc: main1
+
+var
+  s
+  data
+s = newString(100)
+data = cast[string](encode(toOpenArrayByte(s, 0, len(s) - 1)))
+`=destroy`(data)
+`=destroy`(s)
+-- end of expandArc ------------------------
+--expandArc: main2
+
+var
+  s
+  data
+s = newSeq(100)
+data = cast[string](encode(s))
+`=destroy`(data)
+`=destroy_1`(s)
+-- end of expandArc ------------------------
+--expandArc: main3
+
+var
+  data
+  :tmpD
+data = cast[string](encode do:
+  :tmpD = newSeq(100)
+  :tmpD)
+`=destroy`(:tmpD)
+`=destroy_1`(data)
+-- end of expandArc ------------------------
+'''
+"""
+
+func encode*(src: openArray[byte]): seq[byte] =
+  result = newSeq[byte](src.len)
+
+template compress*(src: string): string =
+  cast[string](encode(cast[seq[byte]](src)))
+
+proc main =
+  let data = compress(newString(100))
+main()
+
+proc main1 =
+  var
+    s = newString(100)
+  let data = cast[string](encode(s.toOpenArrayByte(0, s.len-1)))
+main1()
+
+proc main2 =
+  var
+    s = newSeq[byte](100)
+  let data = cast[string](encode(s))
+main2()
+
+proc main3 =
+  let data = cast[string](encode(newSeq[byte](100)))
+main3()
+
+# bug #11018
+discard cast[seq[uint8]](@[1])
+discard cast[seq[uint8]]("test")
+echo cast[seq[uint8]](@[1])
+echo cast[seq[uint8]]("test")
+
+discard cast[string](@[116'u8, 101, 115, 116])
+#echo cast[string](@[116'u8, 101, 115, 116, 0])
+var a = cast[seq[uint32]]("test1234")
+a.setLen(2)
+echo a
+
+
+#issue 11204
+var ac {.compileTime.} = @["a", "b"]
+const bc = ac.len
+
+
+type
+  O = object of RootRef
+    i: int
+
+  O1 = object of O
+  O2 = object of O
+
+proc `=destroy`(o: var O) =
+  echo "destroying O"
+
+proc `=destroy`(o: var O1) =
+  echo "destroying O1"
+
+proc `=destroy`(o: var O2) =
+  echo "destroying O2"
+
+proc test =
+  let o3 = cast[ref O2]((ref O1)())
+
+test()
diff --git a/tests/destructor/tv2_raise.nim b/tests/destructor/tv2_raise.nim
new file mode 100644
index 000000000..66b0aec30
--- /dev/null
+++ b/tests/destructor/tv2_raise.nim
@@ -0,0 +1,53 @@
+discard """
+  valgrind: true
+  cmd: '''nim c -d:nimAllocStats --newruntime $file'''
+  output: '''OK 3
+(allocCount: 7, deallocCount: 4)'''
+"""
+
+import strutils, math
+import system / ansi_c
+
+proc mainA =
+  try:
+    var e: owned(ref ValueError)
+    new(e)
+    e.msg = "message"
+    raise e
+  except Exception as e:
+    raise
+
+
+proc main =
+  raise newException(ValueError, "argh")
+
+var ok = 0
+try:
+  mainA()
+except ValueError:
+  inc ok
+except:
+  discard
+
+try:
+  main()
+except ValueError:
+  inc ok
+except:
+  discard
+
+#  bug #11577
+
+proc newError*: owned(ref Exception) {.noinline.} =
+  new(result)
+
+proc mainC =
+  raise newError()
+
+try:
+  mainC()
+except:
+  inc ok
+
+echo "OK ", ok
+echo getAllocStats()
diff --git a/tests/destructor/twasmoved.nim b/tests/destructor/twasmoved.nim
new file mode 100644
index 000000000..566322702
--- /dev/null
+++ b/tests/destructor/twasmoved.nim
@@ -0,0 +1,14 @@
+type
+  Foo = object
+    id: int
+
+proc `=wasMoved`(x: var Foo) =
+  x.id = -1
+
+proc foo =
+  var s = Foo(id: 999)
+  var m = move s
+  doAssert s.id == -1
+  doAssert m.id == 999
+
+foo()
diff --git a/tests/destructor/twasmoved_error.nim b/tests/destructor/twasmoved_error.nim
new file mode 100644
index 000000000..1cd57e3df
--- /dev/null
+++ b/tests/destructor/twasmoved_error.nim
@@ -0,0 +1,37 @@
+discard """
+  cmd: '''nim c --mm:arc $file'''
+  errormsg: "'=wasMoved' is not available for type <Game>; routine: main"
+"""
+
+# bug #19291
+
+const
+  screenWidth = 800
+  screenHeight = 450
+
+var
+  ready = false
+type
+  Game = object
+
+proc `=destroy`(x: var Game) =
+  assert ready, "Window is already opened"
+  ready = false
+
+proc `=sink`(x: var Game; y: Game) {.error.}
+proc `=copy`(x: var Game; y: Game) {.error.}
+proc `=wasMoved`(x: var Game) {.error.}
+
+proc initGame(width, height: int32, title: string): Game =
+  assert not ready, "Window is already closed"
+  ready = true
+
+proc update(x: Game) = discard
+
+proc main =
+  var g = initGame(screenWidth, screenHeight, "Tetris raylib")
+  g.update()
+  var g2 = g
+  echo "hello"
+
+main()
diff --git a/tests/destructor/twidgets.nim b/tests/destructor/twidgets.nim
new file mode 100644
index 000000000..f13868110
--- /dev/null
+++ b/tests/destructor/twidgets.nim
@@ -0,0 +1,76 @@
+discard """
+  cmd: '''nim c -d:nimAllocStats --newruntime $file'''
+  output: '''button
+clicked!
+(allocCount: 4, deallocCount: 4)'''
+"""
+
+import system / ansi_c
+
+type
+  Widget* = ref object of RootObj
+    drawImpl: owned(proc (self: Widget))
+
+  Button* = ref object of Widget
+    caption: string
+    onclick: owned(proc())
+
+  Window* = ref object of Widget
+    elements: seq[owned Widget]
+
+
+proc newButton(caption: string; onclick: owned(proc())): owned Button =
+  proc draw(self: Widget) =
+    let b = Button(self)
+    echo b.caption
+
+  #result = Button(drawImpl: draw, caption: caption, onclick: onclick)
+  new(result)
+  result.drawImpl = draw
+  result.caption = caption
+  result.onclick = onclick
+
+iterator unitems*[T](a: seq[owned T]): T {.inline.} =
+  ## Iterates over each item of `a`.
+  var i = 0
+  let L = len(a)
+  while i < L:
+    yield a[i]
+    inc(i)
+    assert(len(a) == L, "the length of the seq changed while iterating over it")
+
+proc newWindow(): owned Window =
+  proc windraw(self: Widget) =
+    let w = Window(self)
+    for i in 0..<len(w.elements):
+      let e = Widget(w.elements[i])
+      let d = (proc(self: Widget))e.drawImpl
+      if not d.isNil: d(e)
+
+  result = Window(drawImpl: windraw, elements: @[])
+
+proc draw(w: Widget) =
+  let d = (proc(self: Widget))w.drawImpl
+  if not d.isNil: d(w)
+
+proc add*(w: Window; elem: owned Widget) =
+  w.elements.add elem
+
+proc main =
+  var w = newWindow()
+
+  var b = newButton("button", nil)
+  let u: Button = b
+  b.onclick = proc () =
+    u.caption = "clicked!"
+  w.add b
+
+  w.draw()
+  # simulate button click:
+  u.onclick()
+
+  w.draw()
+
+dumpAllocstats:
+  main()
+
diff --git a/tests/destructor/twidgets_unown.nim b/tests/destructor/twidgets_unown.nim
new file mode 100644
index 000000000..8653d5c28
--- /dev/null
+++ b/tests/destructor/twidgets_unown.nim
@@ -0,0 +1,72 @@
+discard """
+  cmd: '''nim c -d:nimAllocStats --newruntime $file'''
+  output: '''button
+clicked!
+(allocCount: 6, deallocCount: 6)'''
+"""
+
+import system / ansi_c
+
+type
+  Widget* = ref object of RootObj
+    drawImpl: owned(proc (self: Widget))
+
+  Button* = ref object of Widget
+    caption: string
+    onclick: owned(proc())
+
+  Window* = ref object of Widget
+    elements: seq[owned Widget]
+
+
+proc newButton(caption: string; onclick: owned(proc())): owned Button =
+  proc draw(self: Widget) =
+    let b = Button(self)
+    echo b.caption
+
+  #result = Button(drawImpl: draw, caption: caption, onclick: onclick)
+  new(result)
+  result.drawImpl = draw
+  result.caption = caption
+  result.onclick = onclick
+
+proc newWindow(): owned Window =
+  proc windraw(self: Widget) =
+    let w = Window(self)
+    for e in unown(w.elements):
+      let d = unown e.drawImpl
+      if not d.isNil: d(e)
+
+  result = Window(drawImpl: windraw, elements: @[])
+
+proc draw(w: Widget) =
+  let d = unown w.drawImpl
+  if not d.isNil: d(w)
+
+proc add*(w: Window; elem: owned Widget) =
+  w.elements.add elem
+
+proc main =
+  var w = newWindow()
+
+  var b = newButton("button", nil)
+  let u = unown b
+  var clicked = "clicked"
+  b.onclick = proc () =
+    clicked.add "!"
+    u.caption = clicked
+  w.add b
+
+  w.draw()
+  # simulate button click:
+  u.onclick()
+
+  w.draw()
+
+  # bug #11257
+  var a: owned proc()
+  if a != nil:
+    a()
+
+dumpAllocStats:
+  main()
diff --git a/tests/dir with space/more spaces/mspace.nim b/tests/dir with space/more spaces/mspace.nim
new file mode 100644
index 000000000..bc2c90f5e
--- /dev/null
+++ b/tests/dir with space/more spaces/mspace.nim
@@ -0,0 +1 @@
+proc tenTimes*(x: int): int = 10*x
diff --git a/tests/dir with space/tspace.nim b/tests/dir with space/tspace.nim
index 2b74fa629..87a52c271 100644
--- a/tests/dir with space/tspace.nim
+++ b/tests/dir with space/tspace.nim
@@ -1,3 +1,10 @@
-# Test for the compiler to be able to compile a Nim file with spaces in it.
+discard """
+output: "Successful"
+"""
+# Test for the compiler to be able to compile a Nim file with spaces in the directory name.
+# Also test if import of a directory with a space works.
 
+import "more spaces" / mspace
+
+assert tenTimes(5) == 50
 echo("Successful")
diff --git a/tests/discard/t23677.nim b/tests/discard/t23677.nim
new file mode 100644
index 000000000..1ed7386bd
--- /dev/null
+++ b/tests/discard/t23677.nim
@@ -0,0 +1,12 @@
+discard """
+  errormsg: "expression '0' is of type 'int literal(0)' and has to be used (or discarded); start of expression here: t23677.nim(1, 1)"
+  line: 10
+  column: 3
+"""
+
+# issue #23677
+
+if true:
+  0
+else: 
+  raise newException(ValueError, "err") 
diff --git a/tests/discard/tdiscardable.nim b/tests/discard/tdiscardable.nim
index 662d2725a..84b669ed8 100644
--- a/tests/discard/tdiscardable.nim
+++ b/tests/discard/tdiscardable.nim
@@ -1,3 +1,16 @@
+discard """
+output: '''
+tdiscardable
+1
+1
+something defered
+something defered
+hi
+'''
+"""
+
+echo "tdiscardable"
+
 # Test the discardable pragma
 
 proc p(x, y: int): int {.discardable.} =
@@ -13,7 +26,7 @@ q[float](0.8, 0.2)
 
 # bug #942
 
-template maybeMod(x: Tinteger, module:Natural): untyped =
+template maybeMod(x: SomeInteger, module: Natural): untyped =
   if module > 0: x mod module
   else: x
 
@@ -27,3 +40,136 @@ proc bar(b: int):int =
 
 echo foo(0)
 echo bar(0)
+
+# bug #9726
+
+proc foo: (proc: int) =
+  proc bar: int = 1
+  return bar
+
+discard foo()
+
+# bug #10842
+
+proc myDiscardable(): int {.discardable.} =
+  discard
+
+proc main1() =
+  defer:
+    echo "something defered"
+  discard myDiscardable()
+
+proc main2() =
+  defer:
+    echo "something defered"
+  myDiscardable()
+
+main1()
+main2()
+
+block: # bug #13583
+  block:
+    proc hello(): int {.discardable.} = 12
+
+    iterator test(): int {.closure.} =
+      while true:
+        hello()
+
+    let t = test
+
+  block:
+    proc hello(): int {.discardable.} = 12
+
+    iterator test(): int {.closure.} =
+      while true:
+        block:
+          yield 12
+          hello()
+
+    let t = test
+    doAssert t() == 12
+
+  block:
+    proc hello(): int {.discardable.} = 12
+
+    iterator test(): int {.closure.} =
+      while true:
+        yield 12
+        hello()
+
+    let t = test
+    doAssert t() == 12
+
+block:
+  proc bar(): string {.discardable.} =
+    "15"
+
+  proc foo(): int =
+    while true:
+      raise newException(ValueError, "check")
+    12
+
+  doAssertRaises(ValueError):
+    doAssert foo() == 12
+
+block: # issue #10440
+  proc x(): int {.discardable.} = discard
+  try:
+    x()
+  finally:
+    echo "hi"
+
+import macros
+
+block: # issue #14665
+  macro test(): untyped =   
+    let b = @[1, 2, 3, 4]
+
+    result = nnkStmtList.newTree()
+    var i = 0
+    while i < b.len:
+      if false:
+        # this quote do is mandatory, removing it fixes the problem
+        result.add quote do:
+          let testtest = 5
+      else:
+        result.add quote do:
+          let test = 6
+        inc i
+        # removing this continue fixes the problem too
+        continue
+      inc i
+  test()
+
+block: # bug #23775
+  proc retInt(): int {.discardable.} =
+    42
+
+  proc retString(): string {.discardable.} =
+    "text"
+
+  type
+    Enum = enum
+      A, B, C, D
+
+  proc doStuff(msg: Enum) =
+    case msg:
+    of A:
+      retString()
+    of B:
+      retInt()
+    of C:
+      discard retString()
+    else:
+      let _ = retString()
+
+  doStuff(C)
+
+block:
+  proc test(): (int, int) {.discardable.} =
+    discard
+
+  if true:
+    test()
+  else:
+    quit()
diff --git a/tests/discard/tfinallyerrmsg.nim b/tests/discard/tfinallyerrmsg.nim
new file mode 100644
index 000000000..fbc8140aa
--- /dev/null
+++ b/tests/discard/tfinallyerrmsg.nim
@@ -0,0 +1,19 @@
+discard """
+  cmd: "nim check $file"
+"""
+
+block: # issue #19672
+  try:
+    10 #[tt.Error
+    ^ expression '10' is of type 'int literal(10)' and has to be used (or discarded); start of expression here: tfinallyerrmsg.nim(5, 1)]#
+  finally:
+    echo "Finally block"
+
+block: # issue #13871
+  template t(body: int) =
+    try:
+      body
+    finally:
+      echo "expression"
+  t: 2 #[tt.Error
+     ^ expression '2' is of type 'int literal(2)' and has to be used (or discarded)]#
diff --git a/tests/discard/tillegaldiscard.nim b/tests/discard/tillegaldiscard.nim
new file mode 100644
index 000000000..757f4e727
--- /dev/null
+++ b/tests/discard/tillegaldiscard.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "illegal discard"
+  line: 9
+"""
+
+proc pop[T](arg: T): T =
+  echo arg
+
+discard tillegaldiscard.pop
diff --git a/tests/discard/tillegaldiscardtypes.nim b/tests/discard/tillegaldiscardtypes.nim
new file mode 100644
index 000000000..b7877bcd2
--- /dev/null
+++ b/tests/discard/tillegaldiscardtypes.nim
@@ -0,0 +1,14 @@
+discard """
+  cmd: "nim check $file"
+  errormsg: "statement returns no value that can be discarded"
+  nimout: '''
+tillegaldiscardtypes.nim(11, 3) Error: statement returns no value that can be discarded
+tillegaldiscardtypes.nim(12, 3) Error: statement returns no value that can be discarded
+'''
+"""
+
+proc b(v: int) = # bug #21360
+  discard @[]
+  discard []
+
+b(0)
\ No newline at end of file
diff --git a/tests/discard/tneedsdiscard.nim b/tests/discard/tneedsdiscard.nim
index 8d59e7fec..75cd9d2df 100644
--- a/tests/discard/tneedsdiscard.nim
+++ b/tests/discard/tneedsdiscard.nim
@@ -1,6 +1,6 @@
 discard """
+  errormsg: '''expression 'open(f, "arg.txt", fmRead, -1)' is of type 'bool' and has to be used (or discarded); start of expression here: tneedsdiscard.nim(7, 3)'''
   line: 10
-  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)'''
 """
 
 proc p =
diff --git a/tests/discard/tneedsdiscard_in_for.nim b/tests/discard/tneedsdiscard_in_for.nim
new file mode 100644
index 000000000..7685bd577
--- /dev/null
+++ b/tests/discard/tneedsdiscard_in_for.nim
@@ -0,0 +1,22 @@
+discard """
+  errormsg: '''expression 'premultiply(app.gradient[i])' is of type 'Rgba8' and has to be used (or discarded)'''
+  line: 22
+"""
+
+# bug #9076
+type
+  Rgba8 = object
+
+proc premultiply*(c: var Rgba8): var Rgba8 =
+  return c
+
+type
+  App = ref object
+    gradient: seq[Rgba8]
+
+method onDraw(app: App) {.base.} =
+  var
+    width  = 100'f64
+
+  for i in 0..<width.int:
+    app.gradient[i].premultiply()
diff --git a/tests/discard/tvoidcontext.nim b/tests/discard/tvoidcontext.nim
index 25e0d4fdf..9e2b913ad 100644
--- a/tests/discard/tvoidcontext.nim
+++ b/tests/discard/tvoidcontext.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: '''expression '"invalid"' is of type 'string' and has to be discarded'''
+  errormsg: '''expression '"invalid"' is of type 'string' and has to be used (or discarded)'''
   line: 12
 """
 
diff --git a/tests/distinct/t7165.nim b/tests/distinct/t7165.nim
new file mode 100644
index 000000000..b16c29c0e
--- /dev/null
+++ b/tests/distinct/t7165.nim
@@ -0,0 +1,15 @@
+
+type
+  Table[K, V] = object
+    key: K
+    val: V
+
+  MyTable = distinct Table[string, int]
+  MyTableRef = ref MyTable
+
+proc newTable[K, V](): ref Table[K, V] = discard
+
+proc newMyTable: MyTableRef =
+  MyTableRef(newTable[string, int]()) # <--- error here
+
+discard newMyTable()
diff --git a/tests/distinct/tborrow.nim b/tests/distinct/tborrow.nim
new file mode 100644
index 000000000..e34248de5
--- /dev/null
+++ b/tests/distinct/tborrow.nim
@@ -0,0 +1,132 @@
+discard """
+  output: '''4887 true
+0.5'''
+"""
+
+# test the new borrow feature that works with generics:
+
+proc `++`*[T: int | float](a, b: T): T =
+  result = a + b
+
+type
+  DI = distinct int
+  DF = distinct float
+  DS = distinct string
+
+proc `++`(x, y: DI): DI {.borrow.}
+proc `++`(x, y: DF): DF {.borrow.}
+
+proc `$`(x: DI): string {.borrow.}
+proc `$`(x: DF): string {.borrow.}
+
+echo  4544.DI ++ 343.DI, " ", (4.5.DF ++ 0.5.DF).float == 5.0
+
+# issue #14440
+
+type Radians = distinct float64
+
+func `-=`(a: var Radians, b: Radians) {.borrow.}
+
+var a = Radians(1.5)
+let b = Radians(1.0)
+
+a -= b
+
+echo a.float64
+
+block: #14449
+  type 
+    Foo[T] = object
+      foo: T
+
+    Bar[T] {.borrow:`.`.} = distinct Foo[T]
+    SomeThing {.borrow:`.`.} = distinct Foo[float]
+    OtherThing {.borrow:`.`.} = distinct SomeThing
+
+  var
+    a: Bar[int]
+    b: SomeThing
+    c: OtherThing
+  a.foo = 300
+  b.foo = 400
+  c.foo = 42
+  assert a.foo == 300
+  assert b.foo == 400d
+  assert c.foo == 42d
+
+block: # Borrow from muliple aliasses #16666
+  type
+    AImpl = object
+      i: int
+    
+    A = AImpl
+  
+    B {.borrow: `.`.} = distinct A
+    C = B
+    D {.borrow: `.`.} = distinct C
+    E {.borrow: `.`.} = distinct D
+  
+  let
+    b = default(B)
+    d = default(D)
+    e = default(E)
+  
+  assert b.i == 0
+  assert d.i == 0
+  assert e.i == 0
+
+block: # Borrow from generic alias
+  type
+    AImpl[T] = object
+      i: T
+    B[T] = AImpl[T]
+    C {.borrow: `.`.} = distinct B[int]
+    D = B[float]
+    E {.borrow: `.`.} = distinct D
+
+  let
+    c = default(C)
+    e = default(E)
+  assert c.i == int(0)
+  assert e.i == 0d
+
+block: # issue #22069
+  type
+    Vehicle[C: static[int]] = object
+      color: array[C, int]
+    Car[C: static[int]] {.borrow: `.`.} = distinct Vehicle[C]
+    MuscleCar = Car[128]
+  var x: MuscleCar
+  doAssert x.color is array[128, int]
+
+block: # issue #22646
+  type
+    Vec[N : static[int], T: SomeNumber] = object
+      arr: array[N, T]
+    Vec3[T: SomeNumber] = Vec[3, T]
+
+  proc `[]=`[N,T](v: var Vec[N,T]; ix:int; c:T): void {.inline.} = v.arr[ix] = c
+  proc `[]`[N,T](v: Vec[N,T]; ix: int): T {.inline.} = v.arr[ix]
+
+  proc dot[N,T](u,v: Vec[N,T]): T {. inline .} = discard
+  proc length[N,T](v: Vec[N,T]): T = discard
+  proc cross[T](v1,v2:Vec[3,T]): Vec[3,T] = discard
+  proc normalizeWorks[T](v: Vec[3,T]): Vec[3,T] = discard ## <- Explicit size makes it work!
+  proc foo[N,T](u, v: Vec[N,T]): Vec[N,T] = discard ## <- broken
+  proc normalize[N,T](v: Vec[N,T]): Vec[N,T] = discard ## <- broken
+
+  type Color = distinct Vec3[float]
+
+  template borrowOps(typ: typedesc): untyped =
+    proc `[]=`(v: var typ; ix: int; c: float): void {.borrow.}
+    proc `[]`(v: typ; ix: int): float {.borrow.}
+    proc dot(v, u: typ): float {.borrow.}
+    proc cross(v, u: typ): typ {.borrow.}
+    proc length(v: typ): float {.borrow.}
+    proc normalizeWorks(v: typ): typ {.borrow.} ## Up to here everything works
+    proc foo(u, v: typ): typ {.borrow.} ## Broken
+    proc normalize(v: typ): typ {.borrow.} ## Broken
+  borrowOps(Color)
+  var x: Vec[3, float]
+  let y = Color(x)
+  doAssert Vec3[float](y) == x
diff --git a/tests/distinct/tborrowdot.nim b/tests/distinct/tborrowdot.nim
deleted file mode 100644
index 820ee3b71..000000000
--- a/tests/distinct/tborrowdot.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-
-type
-  Foo = object
-    a, b: int
-    s: string
-
-  Bar {.borrow: `.`.} = distinct Foo
-
-var bb: ref Bar
-new bb
-bb.a = 90
-bb.s = "abc"
-
diff --git a/tests/distinct/tcomplexaddressableconv.nim b/tests/distinct/tcomplexaddressableconv.nim
new file mode 100644
index 000000000..00e96bfeb
--- /dev/null
+++ b/tests/distinct/tcomplexaddressableconv.nim
@@ -0,0 +1,21 @@
+# issue #22523
+
+from std/typetraits import distinctBase
+
+type
+  V[p: static int] = distinct int
+  D[p: static int] = distinct int
+  T = V[1]
+
+proc f(y: var T) = discard
+
+var a: D[0]
+
+static:
+  doAssert distinctBase(T) is distinctBase(D[0])
+  doAssert distinctBase(T) is int
+  doAssert distinctBase(D[0]) is int
+  doAssert T(a) is T
+
+f(cast[ptr T](addr a)[])
+f(T(a))
diff --git a/tests/distinct/tcurrncy.nim b/tests/distinct/tcurrncy.nim
deleted file mode 100644
index 2675de739..000000000
--- a/tests/distinct/tcurrncy.nim
+++ /dev/null
@@ -1,38 +0,0 @@
-discard """
-  file: "tcurrncy.nim"
-  output: "25"
-"""
-template Additive(typ: untyped) =
-  proc `+` *(x, y: typ): typ {.borrow.}
-  proc `-` *(x, y: typ): typ {.borrow.}
-
-  # unary operators:
-  proc `+` *(x: typ): typ {.borrow.}
-  proc `-` *(x: typ): typ {.borrow.}
-
-template Multiplicative(typ, base: untyped) =
-  proc `*` *(x: typ, y: base): typ {.borrow.}
-  proc `*` *(x: base, y: typ): typ {.borrow.}
-  proc `div` *(x: typ, y: base): typ {.borrow.}
-  proc `mod` *(x: typ, y: base): typ {.borrow.}
-
-template Comparable(typ: untyped) =
-  proc `<` * (x, y: typ): bool {.borrow.}
-  proc `<=` * (x, y: typ): bool {.borrow.}
-  proc `==` * (x, y: typ): bool {.borrow.}
-
-template DefineCurrency(typ, base: untyped) =
-  type
-    typ* = distinct base
-  Additive(typ)
-  Multiplicative(typ, base)
-  Comparable(typ)
-
-  proc `$` * (t: typ): string {.borrow.}
-
-DefineCurrency(TDollar, int)
-DefineCurrency(TEuro, int)
-echo($( 12.TDollar + 13.TDollar )) #OUT 25
-
-
-
diff --git a/tests/distinct/tdistinct.nim b/tests/distinct/tdistinct.nim
new file mode 100644
index 000000000..b6ba7aa99
--- /dev/null
+++ b/tests/distinct/tdistinct.nim
@@ -0,0 +1,227 @@
+discard """
+  targets: "c js"
+  output: '''
+tdistinct
+25
+false
+false
+false
+false
+Foo
+foo
+'''
+"""
+
+echo "tdistinct"
+
+block tborrowdot:
+  type
+    Foo = object
+      a, b: int
+      s: string
+
+    Bar {.borrow: `.`.} = distinct Foo
+
+  var bb: ref Bar
+  new bb
+  bb.a = 90
+  bb.s = "abc"
+
+block tcurrncy:
+  template Additive(typ: untyped) =
+    proc `+`(x, y: typ): typ {.borrow.}
+    proc `-`(x, y: typ): typ {.borrow.}
+
+    # unary operators:
+    proc `+`(x: typ): typ {.borrow.}
+    proc `-`(x: typ): typ {.borrow.}
+
+  template Multiplicative(typ, base: untyped) =
+    proc `*`(x: typ, y: base): typ {.borrow.}
+    proc `*`(x: base, y: typ): typ {.borrow.}
+    proc `div`(x: typ, y: base): typ {.borrow.}
+    proc `mod`(x: typ, y: base): typ {.borrow.}
+
+  template Comparable(typ: untyped) =
+    proc `<`(x, y: typ): bool {.borrow.}
+    proc `<=`(x, y: typ): bool {.borrow.}
+    proc `==`(x, y: typ): bool {.borrow.}
+
+  template DefineCurrency(typ, base: untyped) =
+    type
+      typ = distinct base
+    Additive(typ)
+    Multiplicative(typ, base)
+    Comparable(typ)
+
+    proc `$`(t: typ): string {.borrow.}
+
+  DefineCurrency(TDollar, int)
+  DefineCurrency(TEuro, int)
+  echo($( 12.TDollar + 13.TDollar )) #OUT 25
+
+block tconsts:
+  # bug #2641
+
+  type MyChar = distinct char
+  const c:MyChar = MyChar('a')
+
+  type MyBool = distinct bool
+  const b:MyBool = MyBool(true)
+
+  type MyBoolSet = distinct set[bool]
+  const bs:MyBoolSet = MyBoolSet({true})
+
+  type MyCharSet= distinct set[char]
+  const cs:MyCharSet = MyCharSet({'a'})
+
+  type MyBoolSeq = distinct seq[bool]
+  const bseq:MyBoolSeq = MyBoolSeq(@[true, false])
+
+  type MyBoolArr = distinct array[3, bool]
+  const barr:MyBoolArr = MyBoolArr([true, false, true])
+
+# bug #2760
+
+type
+  DistTup = distinct tuple
+    foo, bar: string
+
+const d: DistTup = DistTup((
+  foo:"FOO", bar:"BAR"
+))
+
+
+# bug #7167
+
+type Id = distinct range[0..3]
+
+proc `<=`(a, b: Id): bool {.borrow.}
+
+var xs: array[Id, bool]
+
+for x in xs: echo x # type mismatch: got (T) but expected 'bool'
+
+# bug #11715
+
+type FooD = distinct int
+proc `<=`(a, b: FooD): bool {.borrow.}
+
+for f in [FooD(0): "Foo"]: echo f
+
+block tRequiresInit:
+  template accept(x) =
+    static: doAssert compiles(x)
+
+  template reject(x) =
+    static: doAssert not compiles(x)
+
+  type
+    Foo = object
+      x: string
+
+    DistinctFoo {.requiresInit, borrow: `.`.} = distinct Foo
+    DistinctString {.requiresInit.} = distinct string
+
+  reject:
+    var foo: DistinctFoo
+    foo.x = "test"
+    doAssert foo.x == "test"
+
+  accept:
+    let foo = DistinctFoo(Foo(x: "test"))
+    doAssert foo.x == "test"
+
+  reject:
+    var s: DistinctString
+    s = "test"
+    doAssert string(s) == "test"
+
+  accept:
+    let s = DistinctString("test")
+    doAssert string(s) == "test"
+
+block: #17322
+  type
+    A[T] = distinct string
+
+  proc foo(a: var A) =
+    a.string.add "foo"
+
+  type
+    B = distinct A[int]
+
+  var b: B
+  foo(A[int](b))
+  echo A[int](b).string
+  b.string.add "bar"
+  assert b.string == "foobar"
+
+type Foo = distinct string
+
+proc main() = # proc instead of template because of MCS/UFCS.
+  # xxx put everything here to test under RT + VM
+  block: # bug #12282
+    block:
+      proc test() =
+        var s: Foo
+        s.string.add('c')
+        doAssert s.string == "c" # was failing
+      test()
+
+    block:
+      proc add(a: var Foo, b: char) {.borrow.}
+      proc test() =
+        var s: Foo
+        s.add('c')
+        doAssert s.string == "c" # was ok
+      test()
+
+    block:
+      proc add(a: var Foo, b: char) {.borrow.}
+      proc test() =
+        var s: string
+        s.Foo.add('c')
+        doAssert s.string == "c" # was failing
+      test()
+    block: #18061
+      type
+        A = distinct (0..100)
+        B = A(0) .. A(10)
+      proc test(b: B) = discard
+      let
+        a = A(10)
+        b = B(a)
+      test(b)
+
+      proc test(a: A) = discard
+      discard cast[B](A(1))
+      var c: B
+
+
+  block: # bug #9423
+    block:
+      type Foo = seq[int]
+      type Foo2 = distinct Foo
+      template fn() =
+        var a = Foo2(@[1])
+        a.Foo.add 2
+        doAssert a.Foo == @[1, 2]
+      fn()
+
+    block:
+      type Stack[T] = distinct seq[T]
+      proc newStack[T](): Stack[T] =
+        Stack[T](newSeq[T]())
+      proc push[T](stack: var Stack[T], elem: T) =
+        seq[T](stack).add(elem)
+      proc len[T](stack: Stack[T]): int =
+        seq[T](stack).len
+      proc fn() = 
+        var stack = newStack[int]()
+        stack.push(5)
+        doAssert stack.len == 1
+      fn()
+
+static: main()
+main()
diff --git a/tests/distinct/tdistinct_consts.nim b/tests/distinct/tdistinct_consts.nim
deleted file mode 100644
index 4f6ced2d2..000000000
--- a/tests/distinct/tdistinct_consts.nim
+++ /dev/null
@@ -1,20 +0,0 @@
-
-# bug #2641
-
-type MyChar = distinct char
-const c:MyChar = MyChar('a')
-
-type MyBool = distinct bool
-const b:MyBool = MyBool(true)
-
-type MyBoolSet = distinct set[bool]
-const bs:MyBoolSet = MyBoolSet({true})
-
-type MyCharSet= distinct set[char]
-const cs:MyCharSet = MyCharSet({'a'})
-
-type MyBoolSeq = distinct seq[bool]
-const bseq:MyBoolSeq = MyBoolSeq(@[true, false])
-
-type MyBoolArr = distinct array[3, bool]
-const barr:MyBoolArr = MyBoolArr([true, false, true])
diff --git a/tests/distinct/tdistinct_issues.nim b/tests/distinct/tdistinct_issues.nim
new file mode 100644
index 000000000..747cf0b8d
--- /dev/null
+++ b/tests/distinct/tdistinct_issues.nim
@@ -0,0 +1,81 @@
+discard """
+  output: '''
+A
+A
+25.0
+210.0
+apr
+'''
+"""
+
+
+block t4435:
+  type
+    A[T] = distinct T
+    B[T] = distinct T
+
+  proc foo[T](x:A[T]) = echo "A"
+  proc foo[T](x:B[T]) = echo "B"
+  proc bar(x:A) = echo "A"
+  proc bar(x:B) = echo "B"
+
+  var
+    a:A[int]
+
+  foo(a) # fine
+  bar(a) # testdistinct.nim(14, 4) Error: ambiguous call; both testdistinct.bar(x: A) and testdistinct.bar(x: B) match for: (A[system.int])
+
+
+
+block t7010:
+  type MyInt = distinct int
+
+  proc `+`(x: MyInt, y: MyInt): MyInt {.borrow.}
+  proc `+=`(x: var MyInt, y: MyInt) {.borrow.}
+  proc `=`(x: var MyInt, y: MyInt) {.borrow.}
+
+  var next: MyInt
+
+  proc getNext() : MyInt =
+      result = next
+      next += 1.MyInt
+      next = next + 1.MyInt
+
+
+
+block t9079:
+  type
+    Dollars = distinct float
+
+  proc `$`(d: Dollars): string {.borrow.}
+  proc `*`(a, b: Dollars): Dollars {.borrow.}
+  proc `+`(a, b: Dollars): Dollars {.borrow.}
+
+  var a = Dollars(20)
+  a = Dollars(25.0)
+  echo a
+  a = 10.Dollars * (20.Dollars + 1.Dollars)
+  echo a
+
+
+
+block t9322:
+  type Fix = distinct string
+  proc `$`(f: Fix): string {.borrow.}
+  proc mystr(s: string) =
+    echo s
+  mystr($Fix("apr"))
+
+
+block: # bug #13517
+  type MyUint64 = distinct uint64
+
+  proc `==`(a: MyUint64, b: uint64): bool = uint64(a) == b
+
+  block:
+    doAssert MyUint64.high is MyUint64
+    doAssert MyUint64.high == 18446744073709551615'u64
+
+  static:
+    doAssert MyUint64.high is MyUint64
+    doAssert MyUint64.high == 18446744073709551615'u64
diff --git a/tests/distinct/tinvalidborrow.nim b/tests/distinct/tinvalidborrow.nim
new file mode 100644
index 000000000..d4b19fa8d
--- /dev/null
+++ b/tests/distinct/tinvalidborrow.nim
@@ -0,0 +1,42 @@
+discard """
+  cmd: "nim check --hints:off --warnings:off $file"
+  action: "reject"
+  nimout:'''
+tinvalidborrow.nim(25, 3) Error: only a 'distinct' type can borrow `.`
+tinvalidborrow.nim(26, 3) Error: only a 'distinct' type can borrow `.`
+tinvalidborrow.nim(27, 1) Error: borrow proc without distinct type parameter is meaningless
+tinvalidborrow.nim(36, 1) Error: borrow with generic parameter is not supported
+tinvalidborrow.nim(41, 1) Error: borrow from proc return type mismatch: 'T'
+tinvalidborrow.nim(42, 1) Error: borrow from '[]=' is not supported
+'''
+"""
+
+
+
+
+
+# bug #516
+
+type
+  TAtom = culong
+  Test {.borrow:`.`.} = distinct int
+  Foo[T] = object
+    a: int
+  Bar[T] {.borrow:`.`.} = Foo[T]
+  OtherFoo {.borrow:`.`.} = Foo[int]
+proc `==`*(a, b: TAtom): bool {.borrow.}
+
+var
+  d, e: TAtom
+
+discard( $(d == e) )
+
+# issue #4121
+type HeapQueue[T] = distinct seq[T]
+proc len*[T](h: HeapQueue[T]): int {.borrow.}
+
+# issue #3564
+type vec4[T] = distinct array[4, float32]
+
+proc `[]`(v: vec4, i: int): float32 {.borrow.}
+proc `[]=`(v: vec4, i: int, va: float32) {.borrow.}
diff --git a/tests/distinct/tnil.nim b/tests/distinct/tnil.nim
index 759a14657..b54c07432 100644
--- a/tests/distinct/tnil.nim
+++ b/tests/distinct/tnil.nim
@@ -1,44 +1,21 @@
-discard """
-  file: "tnil.nim"
-  output: '''1
-0
-0
-'''
-"""
 {.experimental: "notnil".}
 type
   MyPointer = distinct pointer
   MyString = distinct string
-  MyStringNotNil = distinct (string not nil)
   MyInt = distinct int
 
-proc foo(a: MyPointer) =
+proc foo(a: MyPointer): int =
   # workaround a Windows 'repr' difference:
-  echo cast[int](a)
+  cast[int](a)
 
-foo(cast[MyPointer](1))
-foo(cast[MyPointer](nil))
-foo(nil)
+doAssert foo(cast[MyPointer](1)) == 1
+doAssert foo(cast[MyPointer](nil)) == 0
+doAssert foo(MyPointer(nil)) == 0
 
 var p: MyPointer
 p = cast[MyPointer](1)
 p = cast[MyPointer](nil)
 p = nil.MyPointer
-p = nil
-
-var c: MyString
-c = "Test".MyString
-c = nil.MyString
-c = nil
-
-p = nil
-doAssert(compiles(c = p) == false)
-
-var n: MyStringNotNil = "Test".MyStringNotNil # Cannot prove warning ...
-n = "Test".MyStringNotNil
-doAssert(compiles(n = nil.MyStringNotNil) == false)
-doAssert(compiles(n = nil.MyStringNotNil) == false)
-doAssert(compiles(n = nil) == false)
 
 var i: MyInt
 i = 1.MyInt
diff --git a/tests/distinct/typeclassborrow.nim b/tests/distinct/typeclassborrow.nim
new file mode 100644
index 000000000..5e0c63953
--- /dev/null
+++ b/tests/distinct/typeclassborrow.nim
@@ -0,0 +1,56 @@
+import std/tables
+
+type
+  Foo = distinct seq[int]
+  Bar[N: static[int]] = distinct seq[int]
+  Baz = distinct Bar[10]
+
+proc newSeq(s: var Foo, n: Natural) {.borrow.}
+proc newSeq(s: var Bar, n: Natural) {.borrow.}
+proc newSeq(s: var Baz, n: Natural) {.borrow.}
+
+
+proc `$`(s: Foo): string {.borrow.}
+proc `$`(s: Bar): string {.borrow.}
+proc `$`(s: Baz): string {.borrow.}
+
+proc doThing(b: Bar) = discard
+proc doThing(b: Baz) {.borrow.}
+
+var
+  foo: Foo
+  bar: Bar[10]
+  baz: Baz
+
+newSeq(foo, 100)
+newSeq(bar, bar.N)
+newSeq(baz, 10)
+
+bar.doThing()
+baz.doThing()
+
+assert $seq[int](foo) == $foo
+assert $seq[int](bar) == $bar
+assert $seq[int](baz) == $baz
+
+type
+  Fine* = distinct string
+
+proc `==`*(x, y: Fine): bool {.borrow.} =
+  ## Here is the documentation
+  runnableExamples:
+    var x = Fine("1234")
+    var y = Fine("1234")
+    doAssert x == y
+  doAssert false
+
+
+var x = Fine("1234")
+var y = Fine("1234")
+doAssert x == y
+
+block: # bug #22902
+  type
+    DistinctTable = distinct Table[int, int]
+
+  proc `[]`(t: DistinctTable; key: int): lent int {.borrow.}
diff --git a/tests/dll/client.nim b/tests/dll/client.nim
index 150af3a17..62697569f 100644
--- a/tests/dll/client.nim
+++ b/tests/dll/client.nim
@@ -1,5 +1,4 @@
 discard """
-  output: "Done"
   cmd: "nim $target --debuginfo --hints:on --define:useNimRtl $options $file"
 """
 
@@ -37,5 +36,8 @@ proc eval(n: PNode): int =
 for i in 0..100_000:
   discard eval(buildTree(2))
 
-echo "Done"
-
+# bug https://forum.nim-lang.org/t/8176; Error: ambiguous identifier: 'nimrtl'
+import std/strutils
+doAssert join(@[1, 2]) == "12"
+doAssert join(@[1.5, 2.5]) == "1.52.5"
+doAssert join(@["a", "bc"]) == "abc"
diff --git a/tests/dll/nimhcr_0.nim b/tests/dll/nimhcr_0.nim
new file mode 100644
index 000000000..fe0b29a36
--- /dev/null
+++ b/tests/dll/nimhcr_0.nim
@@ -0,0 +1,4 @@
+
+let g_0 = 1000 # new value! but also a "new" global :)
+
+proc getInt*(): int = return g_0
diff --git a/tests/dll/nimhcr_0_1.nim b/tests/dll/nimhcr_0_1.nim
new file mode 100644
index 000000000..620050be3
--- /dev/null
+++ b/tests/dll/nimhcr_0_1.nim
@@ -0,0 +1,14 @@
+
+import hotcodereloading
+
+let g_0 = 42 # lets start with the ultimate answer
+
+proc getInt*(): int = return g_0
+
+programResult = 0 # should be accessible
+
+beforeCodeReload:
+  echo "   0: before"
+afterCodeReload:
+  echo "   0: after"
+  
\ No newline at end of file
diff --git a/tests/dll/nimhcr_0_2.nim b/tests/dll/nimhcr_0_2.nim
new file mode 100644
index 000000000..9ce228dc1
--- /dev/null
+++ b/tests/dll/nimhcr_0_2.nim
@@ -0,0 +1,18 @@
+
+import hotcodereloading
+
+import nimhcr_1 # new import!
+
+# global scope for this module was executed when loading the program
+# with a previous version which didn't contain this print statement
+echo "   0: I SHOULDN'T BE PRINTED!"
+
+var g_0 = 0 # changed value but won't take effect
+
+proc getInt*(): int = return g_0 + g_1 + f_1()
+
+beforeCodeReload:
+  echo "   0: before - improved!" # changed handlers!
+afterCodeReload:
+  echo "   0: after - improved!"
+  g_0 = 100 # we cannot change it in its initialization but we can in the 'after' handler!
diff --git a/tests/dll/nimhcr_0_3.nim b/tests/dll/nimhcr_0_3.nim
new file mode 100644
index 000000000..183424e11
--- /dev/null
+++ b/tests/dll/nimhcr_0_3.nim
@@ -0,0 +1,19 @@
+
+import hotcodereloading
+
+import nimhcr_1
+import nimhcr_2 # a new and different import!
+
+proc makeCounter*(): auto =
+  return iterator: int {.closure.} =
+    for i in countup(0, 10, 1):
+      yield i
+
+let c = makeCounter()
+
+afterCodeReload:
+  echo "   0: after - closure iterator: ", c()
+  echo "   0: after - closure iterator: ", c()
+  echo "   0: after - c_2 = ", c_2
+
+proc getInt*(): int = return g_1 + g_2.len
diff --git a/tests/dll/nimhcr_0_4.nim b/tests/dll/nimhcr_0_4.nim
new file mode 100644
index 000000000..4471782a7
--- /dev/null
+++ b/tests/dll/nimhcr_0_4.nim
@@ -0,0 +1,19 @@
+
+import hotcodereloading
+
+import nimhcr_1 # only importing 1
+
+let g_0 = 1000 # new value! but also a "new" global :)
+
+proc getInt*(): int = return g_0
+
+proc makeCounter*(): auto =
+  return iterator: int {.closure.} =
+    for i in countup(0, 10, 1):
+      yield i
+
+let c = makeCounter()
+
+afterCodeReload:
+  echo "   0: after - closure iterator! after reload! does it remember? :", c()
+  echo "   0: after - closure iterator! after reload! does it remember? :", c()
diff --git a/tests/dll/nimhcr_0_5.nim b/tests/dll/nimhcr_0_5.nim
new file mode 100644
index 000000000..aff4014ca
--- /dev/null
+++ b/tests/dll/nimhcr_0_5.nim
@@ -0,0 +1,2 @@
+
+proc getInt*(): int = return 42 # back to the answer...
diff --git a/tests/dll/nimhcr_0_6.nim b/tests/dll/nimhcr_0_6.nim
new file mode 100644
index 000000000..fe0b29a36
--- /dev/null
+++ b/tests/dll/nimhcr_0_6.nim
@@ -0,0 +1,4 @@
+
+let g_0 = 1000 # new value! but also a "new" global :)
+
+proc getInt*(): int = return g_0
diff --git a/tinyc/tests/tests2/81_types.expect b/tests/dll/nimhcr_1.nim
index e69de29bb..e69de29bb 100644
--- a/tinyc/tests/tests2/81_types.expect
+++ b/tests/dll/nimhcr_1.nim
diff --git a/tests/dll/nimhcr_1_1.nim b/tests/dll/nimhcr_1_1.nim
new file mode 100644
index 000000000..299c52baa
--- /dev/null
+++ b/tests/dll/nimhcr_1_1.nim
@@ -0,0 +1,51 @@
+
+echo "   1: print me once!"
+
+import hotcodereloading
+
+let g_1* = 8 # devilish!
+
+proc f_1*(): int =
+  var a {.global.} = 1
+  a.inc
+  return a
+
+
+# all these constructs should compile
+let some_glob_1 = 1
+echo "   1: ", some_glob_1
+if true:
+  let some_glob_2 = 2
+  echo "   1: ", some_glob_2
+  if true:
+    let some_glob_3 = 3
+    echo "   1: ", some_glob_3
+block:
+  let some_glob_4 = 4
+  proc inBlock(num: int) =
+    echo "   1: ", num
+  inBlock(some_glob_4)
+var counter = 3
+while counter > 0:
+  let some_glob_5 = 5
+  echo "   1: ", some_glob_5
+  counter.dec
+
+type
+  Type1 = object
+    a: int
+    b: int
+var t = Type1(a: 42, b: 11)
+echo "   1: Type1.a:", t.a
+
+type
+  obj = ref object
+    dat: int
+    str: string
+
+proc foo(): (int, obj) = (1, obj(dat: 3, str: "bar"))
+
+let (aa, bb) = foo()
+afterCodeReload:
+  echo aa
+  echo bb.str
diff --git a/tests/dll/nimhcr_1_2.nim b/tests/dll/nimhcr_1_2.nim
new file mode 100644
index 000000000..caf772450
--- /dev/null
+++ b/tests/dll/nimhcr_1_2.nim
@@ -0,0 +1,4 @@
+
+import nimhcr_2
+
+proc f_1*(): int = return f_2()
diff --git a/tinyc/tests/tests2/82_attribs_position.expect b/tests/dll/nimhcr_1_3.nim
index e69de29bb..e69de29bb 100644
--- a/tinyc/tests/tests2/82_attribs_position.expect
+++ b/tests/dll/nimhcr_1_3.nim
diff --git a/tinyc/tests/tests2/92_enum_bitfield.expect b/tests/dll/nimhcr_2.nim
index e69de29bb..e69de29bb 100644
--- a/tinyc/tests/tests2/92_enum_bitfield.expect
+++ b/tests/dll/nimhcr_2.nim
diff --git a/tests/dll/nimhcr_2_1.nim b/tests/dll/nimhcr_2_1.nim
new file mode 100644
index 000000000..705ed6d5a
--- /dev/null
+++ b/tests/dll/nimhcr_2_1.nim
@@ -0,0 +1,32 @@
+
+import hotcodereloading
+
+type
+  Type2 = ref object of RootObj
+    data*: int
+
+let g_2* = @[Type2(data: 2), Type2(data: 3)][1..^1] # should have a length of 1
+
+const c_2* = [1, 2, 3] # testing that a complext const object is properly exported
+
+var a: tuple[str: string, i: int]
+a.str = "   2: random string"
+echo a.str
+
+beforeCodeReload:
+  echo "   2: before!"
+
+# testing a construct of 2 functions in the same module which reference each other
+# https://github.com/nim-lang/Nim/issues/11608
+proc rec_1(depth: int)
+proc rec_2(depth: int) =
+  rec_1(depth + 1)
+proc rec_1(depth: int) =
+  if depth < 3:
+    rec_2(depth)
+  else:
+    echo("max mutual recursion reached!")
+
+# https://github.com/nim-lang/Nim/issues/11996
+let rec_2_func_ref = rec_2
+rec_2_func_ref(0)
diff --git a/tests/dll/nimhcr_2_2.nim b/tests/dll/nimhcr_2_2.nim
new file mode 100644
index 000000000..04ab2d3bb
--- /dev/null
+++ b/tests/dll/nimhcr_2_2.nim
@@ -0,0 +1,7 @@
+
+import hotcodereloading
+
+proc f_2*(): int = return 1
+
+afterCodeReload:
+  echo "   2: after!"
diff --git a/web/links.rst b/tests/dll/nimhcr_2_3.nim
index e69de29bb..e69de29bb 100644
--- a/web/links.rst
+++ b/tests/dll/nimhcr_2_3.nim
diff --git a/tests/dll/nimhcr_basic.nim b/tests/dll/nimhcr_basic.nim
new file mode 100644
index 000000000..2e1f39ae0
--- /dev/null
+++ b/tests/dll/nimhcr_basic.nim
@@ -0,0 +1,8 @@
+discard """
+  output: '''
+Hello world
+'''
+"""
+# for now orc only tests successful compilation
+
+echo "Hello world"
diff --git a/tests/dll/nimhcr_integration.nim b/tests/dll/nimhcr_integration.nim
new file mode 100644
index 000000000..ac34f1f85
--- /dev/null
+++ b/tests/dll/nimhcr_integration.nim
@@ -0,0 +1,170 @@
+discard """
+  disabled: "true"
+  output: '''
+main: HELLO!
+main: hasAnyModuleChanged? true
+main: before
+   0: after
+main: after
+              The answer is: 1000
+main: hasAnyModuleChanged? false
+              The answer is: 1000
+main: hasAnyModuleChanged? true
+   0: before
+main: before
+   1: print me once!
+   1: 1
+   1: 2
+   1: 3
+   1: 4
+   1: 5
+   1: 5
+   1: 5
+   1: Type1.a:42
+1
+bar
+   0: after - improved!
+main: after
+              The answer is: 110
+main: hasAnyModuleChanged? true
+   0: before - improved!
+main: before
+   2: random string
+max mutual recursion reached!
+1
+bar
+   0: after - closure iterator: 0
+   0: after - closure iterator: 1
+   0: after - c_2 = [1, 2, 3]
+main: after
+              The answer is: 9
+main: hasAnyModuleChanged? true
+   2: before!
+main: before
+   2: after!
+   0: after - closure iterator! after reload! does it remember? :2
+   0: after - closure iterator! after reload! does it remember? :3
+main: after
+              The answer is: 1000
+main: hasAnyModuleChanged? true
+main: before
+main: after
+              The answer is: 42
+done
+'''
+"""
+
+#[
+xxx disabled: "openbsd" because it would otherwise give:
+/home/build/Nim/lib/nimhcr.nim(532) hcrInit
+/home/build/Nim/lib/nimhcr.nim(503) initModules
+/home/build/Nim/lib/nimhcr.nim(463) initPointerData
+/home/build/Nim/lib/nimhcr.nim(346) hcrRegisterProc
+/home/build/Nim/lib/pure/reservedmem.nim(223) setLen
+/home/build/Nim/lib/pure/reservedmem.nim(97) setLen
+/home/build/Nim/lib/pure/includes/oserr.nim(94) raiseOSError
+Error: unhandled exception: Not supported [OSError]
+
+After instrumenting code, the stacktrace actually points to the call to `check mprotect`
+]#
+
+## This is perhaps the most complex test in the nim test suite - calling the
+## compiler on the file itself with the same set or arguments and reloading
+## parts of the program at runtime! In the same folder there are a few modules
+## with names such as `nimhcr_<number>.nim`. Each of them has a few versions which
+## are in the format of `nimhcr_<number>_<version>.nim`. The below code uses the
+## `update` proc to say which of the modules should bump its version (and that
+## is done by copying `nimhcr_<number>_<version>.nim` onto `nimhcr_<number>.nim`).
+## The files should refer to each other (when importing) without the versions.
+## A few files can be updated by calling `update` for each of their indexes
+## and after that with a single call to `compileReloadExecute` the new version
+## of the program will be compiled, reloaded, and the only thing the main module
+## calls from `nimhcr_0.nim` (the procedure `getInt` proc) is called for a result.
+##
+## This test is expected to be executed with arguments - the full nim compiler
+## command used for building it - so it can rebuild iself the same way - example:
+##
+## compiling:
+##                   nim c --hotCodeReloading:on --nimCache:<folder> <this_file>.nim
+## executing:
+##   <this_file>.exe nim c --hotCodeReloading:on --nimCache:<folder> <this_file>.nim
+
+import os, osproc, strutils, hotcodereloading
+
+import nimhcr_0 # getInt() - the only thing we continually call from the main module
+
+proc compileReloadExecute() =
+  # Remove the `--forceBuild` option - is there in the first place because:
+  # - when `koch test` is ran for the first time the nimcache is empty
+  # - when each of the variants are built (debug, release after that, different GCs)
+  #   the main executable that gets built into the appropriate nimcache folder
+  #   gets copied to the originally intended destination and is executed
+  #   (this behaviour is only when the --hotCodeReloading option is used).
+  # - when `koch test` is ran again and the nimcache is full the executable files
+  #   in the nimcache folder aren't relinked and therefore aren't copied to the
+  #   originally intended destination - so when the binary at the intended
+  #   destination is executed - it is actually a remnant from a previous execution.
+  #   That is a problem because it points to shared objects to load from its own
+  #   nimcache folder - the one used for building it - a previous run! And when
+  #   this test changes other modules it references but the main module (this file)
+  #   remains intact - the binary isn't replaced. `--forceBuild` fixes this but has
+  #   to be applied only for the main build - the one done from koch, but when this
+  #   binary triggers rebuilding itself here it shouldn't rebuild the main module -
+  #   that would lead to replacing the main binary executable which is running!
+  let cmd = commandLineParams()[0..^1].join(" ").replace(" --forceBuild")
+  doAssert cmd.len > 0
+  let (stdout, exitcode) = execCmdEx(cmd)
+  if exitcode != 0:
+    echo "COMPILATION ERROR!"
+    echo "COMMAND: ", cmd
+    echo "STDOUT: ", stdout
+    quit 1
+  echo "main: hasAnyModuleChanged? ", hasAnyModuleChanged()
+  performCodeReload()
+  echo "              The answer is: ", getInt()
+
+# there are 3 files and all of them start from their 1st version
+var vers = [1, 1, 1]
+proc update(file: int) =
+  proc getfile(mid: string): string =
+    let (path, _, _) = splitFile(currentSourcePath())
+    return path & "/nimhcr_" & mid & ".nim"
+  copyFile(getfile($file & "_" & $vers[file]), getfile($file))
+  inc vers[file]
+
+beforeCodeReload:
+  echo "main: before"
+
+afterCodeReload:
+  echo "main: after"
+
+echo "main: HELLO!"
+
+update 0
+compileReloadExecute() # versions are: 1 - -
+
+compileReloadExecute() # no change
+
+update 0
+update 1
+compileReloadExecute() # versions are: 2 1 -
+
+update 0
+update 2
+compileReloadExecute() # versions are: 3 1 1
+
+update 0
+update 1
+update 2
+compileReloadExecute() # versions are: 4 2 2
+
+update 0
+compileReloadExecute() # versions are: 5 2 2
+
+# final update so there are no git modifications left after everything
+# (the last versions are like the first files without a version suffix)
+update 0
+update 1
+update 2
+
+echo "done"
\ No newline at end of file
diff --git a/tests/dll/nimhcr_unit.nim b/tests/dll/nimhcr_unit.nim
new file mode 100644
index 000000000..249f3f9f1
--- /dev/null
+++ b/tests/dll/nimhcr_unit.nim
@@ -0,0 +1,149 @@
+discard """
+disabled: "openbsd"
+disabled: "netbsd"
+output: '''
+fastcall_proc implementation #1 10
+11
+fastcall_proc implementation #2 20
+22
+fastcall_proc implementation #2 20
+22
+fastcall_proc implementation #3 30
+33
+fastcall_proc implementation #3 30
+33
+fastcall_proc implementation #3 30
+33
+fastcall_proc implementation #3 40
+43
+cdecl_proc implementation #1 10
+11
+cdecl_proc implementation #2 20
+22
+cdecl_proc implementation #2 20
+22
+cdecl_proc implementation #3 30
+33
+cdecl_proc implementation #3 30
+33
+cdecl_proc implementation #3 30
+33
+cdecl_proc implementation #3 40
+43
+stdcall_proc implementation #1 10
+11
+stdcall_proc implementation #2 20
+22
+stdcall_proc implementation #2 20
+22
+stdcall_proc implementation #3 30
+33
+stdcall_proc implementation #3 30
+33
+stdcall_proc implementation #3 30
+33
+stdcall_proc implementation #3 40
+43
+noconv_proc implementation #1 10
+11
+noconv_proc implementation #2 20
+22
+noconv_proc implementation #2 20
+22
+noconv_proc implementation #3 30
+33
+noconv_proc implementation #3 30
+33
+noconv_proc implementation #3 30
+33
+noconv_proc implementation #3 40
+43
+inline_proc implementation #1 10
+11
+inline_proc implementation #2 20
+22
+inline_proc implementation #2 20
+22
+inline_proc implementation #3 30
+33
+inline_proc implementation #3 30
+33
+inline_proc implementation #3 30
+33
+inline_proc implementation #3 40
+43
+'''
+"""
+
+import macros
+
+macro carryOutTests(callingConv: untyped): untyped =
+  let
+    procName = $callingConv & "_proc"
+    globalName = $callingConv & "_global"
+    callingConv = callingConv
+    p1 = ident(procName & "1")
+    p2 = ident(procName & "2")
+    p3 = ident(procName & "3")
+    g1 = ident(globalName & "1")
+    g2 = ident(globalName & "2")
+
+  result = quote do:
+    var `g1`: pointer = nil
+    if hcrRegisterGlobal("dummy_module", `globalName`, sizeof(int), nil, addr `g1`):
+      cast[ptr int](`g1`)[] = 10
+
+    var `g2`: pointer = nil
+    if hcrRegisterGlobal("dummy_module", `globalName`, sizeof(int), nil, addr `g2`):
+      cast[ptr int](`g2`)[] = 20
+
+    doAssert `g1` == `g2` and cast[ptr int](`g1`)[] == 10
+
+    type
+      F = proc (x: int): int {.placeholder.}
+
+    proc `p1`(x: int): int {.placeholder.}=
+      echo `procName`, " implementation #1 ", x
+      return x + 1
+
+    let fp1 = cast[F](hcrRegisterProc("dummy_module", `procName`, cast[pointer](`p1`)))
+    echo fp1(10)
+
+    proc `p2`(x: int): int {.placeholder.} =
+      echo `procName`, " implementation #2 ", x
+      return x + 2
+
+    let fp2 = cast[F](hcrRegisterProc("dummy_module", `procName`, cast[pointer](`p2`)))
+    echo fp1(20)
+    echo fp2(20)
+
+    proc `p3`(x: int): int {.placeholder.} =
+      echo `procName`, " implementation #3 ", x
+      return x + 3
+
+    let fp3 = cast[F](hcrRegisterProc("dummy_module", `procName`, cast[pointer](`p3`)))
+    echo fp1(30)
+    echo fp2(30)
+    echo fp3(30)
+
+    let fp4 = cast[F](hcrGetProc("dummy_module", `procName`))
+    echo fp4(40)
+
+  proc replacePlaceholderPragmas(n: NimNode) =
+    if n.kind == nnkPragma:
+      n[0] = callingConv
+    else:
+      for i in 0 ..< n.len:
+        replacePlaceholderPragmas n[i]
+
+  replacePlaceholderPragmas result
+  # echo result.treeRepr
+
+hcrAddModule("dummy_module")
+
+carryOutTests fastcall
+carryOutTests cdecl
+carryOutTests stdcall
+carryOutTests noconv
+carryOutTests inline
+
diff --git a/tests/dll/nimhcr_unit.nim.cfg b/tests/dll/nimhcr_unit.nim.cfg
new file mode 100644
index 000000000..b13c310d9
--- /dev/null
+++ b/tests/dll/nimhcr_unit.nim.cfg
@@ -0,0 +1,2 @@
+-d:useNimRtl
+-d:testNimHcr
diff --git a/tests/dll/server.nim b/tests/dll/server.nim
index e6b80df88..dd4606298 100644
--- a/tests/dll/server.nim
+++ b/tests/dll/server.nim
@@ -1,5 +1,7 @@
 discard """
+action: compile
   cmd: "nim $target --debuginfo --hints:on --define:useNimRtl --app:lib $options $file"
+batchable: false
 """
 
 type
@@ -12,23 +14,14 @@ type
   PNode = ref TNode
 
 proc newLit(x: int): PNode {.exportc: "newLit", dynlib.} =
-  new(result)
-  result.x = x
+  result = PNode(k: nkLit, x: x)
 
 proc newOp(k: TNodeKind, a, b: PNode): PNode {.exportc: "newOp", dynlib.} =
   assert a != nil
   assert b != nil
-  new(result)
+  result = PNode(k: nkSub, a: a, b: b)
+  # now overwrite with the real value:
   result.k = k
-  result.a = a
-  result.b = b
 
 proc buildTree(x: int): PNode {.exportc: "buildTree", dynlib.} =
   result = newOp(nkMul, newOp(nkAdd, newLit(x), newLit(x)), newLit(x))
-
-when false:
-  # Test the GC:
-  for i in 0..100_000:
-    discard buildTree(2)
-
-  echo "Done"
diff --git a/tests/dll/test_nimhcr_integration.bat b/tests/dll/test_nimhcr_integration.bat
new file mode 100644
index 000000000..66e6beac4
--- /dev/null
+++ b/tests/dll/test_nimhcr_integration.bat
@@ -0,0 +1,10 @@
+set NIM=nim
+set NIM_FLAGS=-d:debug
+
+%NIM% c --outdir:"." %NIM_FLAGS% ../../lib/nimrtl.nim
+%NIM% c --outdir:"." %NIM_FLAGS% ../../lib/nimhcr.nim
+
+set HCR_FLAGS=--forceBuild --hotCodeReloading:on --nimcache:nimcache %NIM_FLAGS%
+
+%NIM% %HCR_FLAGS% c nimhcr_integration.nim
+nimhcr_integration %NIM% %HCR_FLAGS% c nimhcr_integration.nim
diff --git a/tests/dll/test_nimhcr_integration.sh b/tests/dll/test_nimhcr_integration.sh
new file mode 100755
index 000000000..a2e2d0483
--- /dev/null
+++ b/tests/dll/test_nimhcr_integration.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -e
+
+rm -rf nimcache
+
+NIM_FLAGS=${*:- -d:debug}
+NIM=nim
+
+$NIM c --outdir:"." $NIM_FLAGS ../../lib/nimrtl.nim
+$NIM c --outdir:"." $NIM_FLAGS ../../lib/nimhcr.nim
+
+echo ===== Compiling HCR Integration Test =====
+HCR_FLAGS="--forceBuild --hotCodeReloading:on --nimcache:nimcache $NIM_FLAGS"
+$NIM $HCR_FLAGS c nimhcr_integration.nim
+export LD_LIBRARY_PATH=$(pwd):$LD_LIBRARY_PATH
+./nimhcr_integration $NIM $HCR_FLAGS c nimhcr_integration.nim
diff --git a/tests/dll/visibility.nim b/tests/dll/visibility.nim
new file mode 100644
index 000000000..ec15dad29
--- /dev/null
+++ b/tests/dll/visibility.nim
@@ -0,0 +1,43 @@
+discard """
+  output: ""
+"""
+
+const LibName {.used.} =
+  when defined(windows):
+    "visibility.dll"
+  elif defined(macosx):
+    "libvisibility.dylib"
+  else:
+    "libvisibility.so"
+
+when compileOption("app", "lib"):
+  var
+    bar {.exportc.}: int
+    thr {.exportc, threadvar.}: int
+  proc foo() {.exportc.} = discard
+
+  var
+    exported {.exportc, dynlib.}: int
+    exported_thr {.exportc, threadvar, dynlib.}: int
+  proc exported_func() {.exportc, dynlib.} = discard
+elif isMainModule:
+  import dynlib
+
+  let handle = loadLib(LibName)
+
+  template check(sym: untyped) =
+    const s = astToStr(sym)
+    if handle.symAddr(s) != nil:
+      echo s, " is exported"
+  template checkE(sym: untyped) =
+    const s = astToStr(sym)
+    if handle.symAddr(s) == nil:
+      echo s, " is not exported"
+
+  check foo
+  check bar
+  check thr
+
+  checkE exported
+  checkE exported_thr
+  checkE exported_func
diff --git a/tests/dummy.txt b/tests/dummy.txt
new file mode 100644
index 000000000..64cd361f9
--- /dev/null
+++ b/tests/dummy.txt
@@ -0,0 +1 @@
+Just a simple text for test
\ No newline at end of file
diff --git a/tests/effects/tcast_as_pragma.nim b/tests/effects/tcast_as_pragma.nim
new file mode 100644
index 000000000..1de61333e
--- /dev/null
+++ b/tests/effects/tcast_as_pragma.nim
@@ -0,0 +1,18 @@
+discard """
+  cmd: "nim c $file"
+  action: "compile"
+"""
+
+proc taggy() {.tags: RootEffect.} = discard
+
+proc m {.raises: [], tags: [].} =
+  {.cast(noSideEffect).}:
+    echo "hi"
+
+  {.cast(raises: []).}:
+    raise newException(ValueError, "bah")
+
+  {.cast(tags: []).}:
+    taggy()
+
+m()
diff --git a/tests/effects/tdiagnostic_messages.nim b/tests/effects/tdiagnostic_messages.nim
new file mode 100644
index 000000000..b1acf8c5c
--- /dev/null
+++ b/tests/effects/tdiagnostic_messages.nim
@@ -0,0 +1,37 @@
+discard """
+  nimoutFull: true
+  action: "reject"
+  cmd: "nim r --hint:Conf:off $file"
+  nimout: '''
+tdiagnostic_messages.nim(36, 6) Error: 'a' can have side effects
+> tdiagnostic_messages.nim(37, 30) Hint: 'a' calls `.sideEffect` 'callWithSideEffects'
+>> tdiagnostic_messages.nim(29, 6) Hint: 'callWithSideEffects' called by 'a'
+>>> tdiagnostic_messages.nim(31, 34) Hint: 'callWithSideEffects' calls `.sideEffect` 'indirectCallViaVarParam'
+>>>> tdiagnostic_messages.nim(25, 6) Hint: 'indirectCallViaVarParam' called by 'callWithSideEffects'
+>>>>> tdiagnostic_messages.nim(26, 7) Hint: 'indirectCallViaVarParam' calls routine via hidden pointer indirection
+>>> tdiagnostic_messages.nim(32, 33) Hint: 'callWithSideEffects' calls `.sideEffect` 'indirectCallViaPointer'
+>>>> tdiagnostic_messages.nim(27, 6) Hint: 'indirectCallViaPointer' called by 'callWithSideEffects'
+>>>>> tdiagnostic_messages.nim(28, 32) Hint: 'indirectCallViaPointer' calls routine via pointer indirection
+>>> tdiagnostic_messages.nim(33, 3) Hint: 'callWithSideEffects' calls `.sideEffect` 'myEcho'
+>>>> tdiagnostic_messages.nim(24, 6) Hint: 'myEcho' called by 'callWithSideEffects'
+>>> tdiagnostic_messages.nim(34, 3) Hint: 'callWithSideEffects' accesses global state 'globalVar'
+>>>> tdiagnostic_messages.nim(23, 5) Hint: 'globalVar' accessed by 'callWithSideEffects'
+
+'''
+"""
+
+var globalVar = 0
+proc myEcho(a: string) {.sideEffect.} = discard
+proc indirectCallViaVarParam(call: var proc(): int {.nimcall.}): int =
+  call()
+proc indirectCallViaPointer(call: pointer): int =
+  cast[ptr proc(): int](call)[]()
+proc callWithSideEffects(): int =
+  var p = proc (): int {.nimcall.} = 0
+  discard indirectCallViaVarParam(p)
+  discard indirectCallViaPointer(addr p)
+  myEcho ""
+  globalVar
+
+func a: int =
+  discard callWithSideEffects()
diff --git a/tests/effects/teffects1.nim b/tests/effects/teffects1.nim
index ef76c9130..1d267b5fa 100644
--- a/tests/effects/teffects1.nim
+++ b/tests/effects/teffects1.nim
@@ -1,10 +1,12 @@
 discard """
-  file: "system.nim"
-  errormsg: "can raise an unlisted exception: ref IOError"
+  cmd: "nim check --hint:Conf:off --hint:XDeclaredButNotUsed:off $file"
+  nimout: '''
+teffects1.nim(17, 28) template/generic instantiation from here
+'''
 """
-
+{.push warningAsError[Effect]: on.}
 type
-  TObj = object {.pure, inheritable.}
+  TObj {.pure, inheritable.} = object
   TObjB = object of TObj
     a, b, c: string
 
@@ -12,9 +14,34 @@ type
 
 proc forw: int {. .}
 
-proc lier(): int {.raises: [IO2Error].} =
-  writeLine stdout, "arg"
+proc lier(): int {.raises: [IO2Error].} = #[tt.Hint
+                            ^ 'lier' cannot raise 'IO2Error' [XCannotRaiseY] ]#
+  writeLine stdout, "arg" #[tt.Error
+  ^ writeLine stdout, ["arg"] can raise an unlisted exception: ref IOError ]#
 
 proc forw: int =
   raise newException(IOError, "arg")
 
+block:
+  proc someProc(t: string) {.raises: [Defect].} =
+    discard
+  let vh: proc(topic: string) {.raises: [].} = someProc
+
+{.push raises: [Defect].}
+
+type
+  MyProcType* = proc(x: int): string #{.raises: [ValueError, Defect].}
+
+proc foo(x: int): string {.nimcall, raises: [ValueError].} =
+  if x > 9:
+    raise newException(ValueError, "Use single digit")
+  $x
+
+var p: MyProcType = foo #[tt.Error
+                    ^
+type mismatch: got <proc (x: int): string{.nimcall, raises: [ValueError], noSideEffect, gcsafe.}> but expected 'MyProcType = proc (x: int): string{.closure.}'
+  Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'.
+.raise effects differ
+]#
+{.pop.}
+{.pop.}
diff --git a/tests/effects/teffects10.nim b/tests/effects/teffects10.nim
new file mode 100644
index 000000000..8193b394a
--- /dev/null
+++ b/tests/effects/teffects10.nim
@@ -0,0 +1,12 @@
+discard """
+action: compile
+"""
+
+# https://github.com/nim-lang/Nim/issues/15495
+
+proc f() {.raises: [].} =
+  var a: proc ()
+  var b: proc ()
+  swap(a, b)
+
+f()
diff --git a/tests/effects/teffects11.nim b/tests/effects/teffects11.nim
new file mode 100644
index 000000000..20b7026ab
--- /dev/null
+++ b/tests/effects/teffects11.nim
@@ -0,0 +1,21 @@
+discard """
+action: compile
+errormsg: "type mismatch: got <proc (x: int){.gcsafe.}>"
+line: 21
+"""
+
+type
+  Effect1 = object
+  Effect2 = object
+  Effect3 = object
+
+proc test(fnc: proc(x: int): void {.forbids: [Effect2].}) {.tags: [Effect1, Effect3, RootEffect].} =
+  fnc(1)
+
+proc t1(x: int): void = echo $x
+proc t2(x: int): void {.tags: [Effect2].} = echo $x
+proc t3(x: int): void {.tags: [Effect3].} = echo $x
+
+test(t1)
+test(t3)
+test(t2)
diff --git a/tests/effects/teffects12.nim b/tests/effects/teffects12.nim
new file mode 100644
index 000000000..5f5499c38
--- /dev/null
+++ b/tests/effects/teffects12.nim
@@ -0,0 +1,52 @@
+discard """
+action: compile
+"""
+
+import std/locks
+
+type
+  Test2Effect* = object
+  Test2* = object
+    value2*: int
+  Test1Effect* = object
+  Test1* = object
+    value1*: int
+  Main* = object
+    test1Lock: Lock
+    test1: Test1
+    test2Lock: Lock
+    test2: Test2
+
+proc `=copy`(obj1: var Test2, obj2: Test2) {.error.}
+proc `=copy`(obj1: var Test1, obj2: Test1) {.error.}
+proc `=copy`(obj1: var Main, obj2: Main) {.error.}
+
+proc withTest1(main: var Main,
+               fn: proc(test1: var Test1) {.gcsafe, forbids: [Test1Effect].}) {.gcsafe, tags: [Test1Effect, RootEffect].} =
+  withLock(main.test1Lock):
+    fn(main.test1)
+
+proc withTest2(main: var Main,
+               fn: proc(test1: var Test2) {.gcsafe, forbids: [Test2Effect].}) {.gcsafe, tags: [Test2Effect, RootEffect].} =
+  withLock(main.test2Lock):
+    fn(main.test2)
+
+proc newMain(): Main =
+  var test1lock: Lock
+  initLock(test1Lock)
+  var test2lock: Lock
+  initLock(test2Lock)
+  var main = Main(test1Lock: move(test1Lock), test1: Test1(value1: 1),
+                  test2Lock: move(test2Lock), test2: Test2(value2: 2))
+  main.withTest1(proc(test1: var Test1) = test1.value1 += 1)
+  main.withTest2(proc(test2: var Test2) = test2.value2 += 1)
+  move main
+
+var main = newMain()
+main.withTest1(proc(test1: var Test1) =
+  test1.value1 += 1
+  main.withTest2(proc(test2: var Test2) = test2.value2 += 1)
+)
+
+main.withTest1(proc(test1: var Test1) {.tags: [].} = echo $test1.value1)
+main.withTest2(proc(test2: var Test2) {.tags: [].} = echo $test2.value2)
diff --git a/tests/effects/teffects13.nim b/tests/effects/teffects13.nim
new file mode 100644
index 000000000..73082f997
--- /dev/null
+++ b/tests/effects/teffects13.nim
@@ -0,0 +1,19 @@
+discard """
+action: compile
+errormsg: "writeSomething() has an illegal effect: WriteIO"
+line: 19
+"""
+
+type
+  IO = object of RootEffect ## input/output effect
+  ReadIO = object of IO     ## input effect
+  WriteIO = object of IO    ## output effect
+
+proc readSomething(): string {.tags: [ReadIO].} = ""
+proc writeSomething(): void {.tags: [WriteIO].} = echo "..."
+
+proc noWritesPlease() {.forbids: [WriteIO].} =
+  # this is OK:
+  echo readSomething()
+  # the compiler prevents this:
+  writeSomething()
diff --git a/tests/effects/teffects14.nim b/tests/effects/teffects14.nim
new file mode 100644
index 000000000..6291d9569
--- /dev/null
+++ b/tests/effects/teffects14.nim
@@ -0,0 +1,15 @@
+discard """
+action: compile
+errormsg: "func1() has an illegal effect: IO"
+line: 15
+"""
+
+type IO = object ## input/output effect
+proc func1(): string {.tags: [IO].} = discard
+proc func2(): string = discard
+
+proc no_IO_please() {.forbids: [IO].} =
+  # this is OK because it didn't define any tag:
+  discard func2()
+  # the compiler prevents this:
+  let y = func1()
diff --git a/tests/effects/teffects15.nim b/tests/effects/teffects15.nim
new file mode 100644
index 000000000..c3079cdbc
--- /dev/null
+++ b/tests/effects/teffects15.nim
@@ -0,0 +1,18 @@
+discard """
+action: compile
+errormsg: "method1(c) has an illegal effect: IO"
+line: 18
+"""
+
+type
+  IO = object ## input/output effect
+  CustomObject* = object of RootObj
+    text: string
+
+method method1(obj: var CustomObject): string {.tags: [IO].} = obj.text & "."
+method method2(obj: var CustomObject): string = obj.text & ":"
+
+proc noIO() {.forbids: [IO].} =
+  var c = CustomObject(text: "a")
+  echo c.method2()
+  echo c.method1()
diff --git a/tests/effects/teffects16.nim b/tests/effects/teffects16.nim
new file mode 100644
index 000000000..ee8f782a3
--- /dev/null
+++ b/tests/effects/teffects16.nim
@@ -0,0 +1,20 @@
+discard """
+action: compile
+errormsg: "writeSomething(\"a\") can have an unlisted effect: WriteIO"
+line: 20
+"""
+
+type
+  IO = object of RootEffect ## input/output effect
+  ReadIO = object of IO     ## input effect
+  WriteIO = object of IO    ## output effect
+  LogIO = object of IO      ## another output effect
+
+proc readSomething(): string {.tags: [ReadIO].} = ""
+proc writeSomething(msg: string): void {.tags: [WriteIO].} = echo msg
+proc logSomething(msg: string): void {.tags: [LogIo].} = echo msg
+
+proc noWritesPlease() {.forbids: [WriteIO], tags: [LogIO, ReadIO].} =
+  echo readSomething()
+  logSomething("a")
+  writeSomething("a")
diff --git a/tests/effects/teffects17.nim b/tests/effects/teffects17.nim
new file mode 100644
index 000000000..5e6b83896
--- /dev/null
+++ b/tests/effects/teffects17.nim
@@ -0,0 +1,17 @@
+discard """
+action: compile
+errormsg: "writeSomething(\"a\") has an illegal effect: WriteIO"
+line: 17
+"""
+
+type
+  IO = object of RootEffect ## input/output effect
+  ReadIO = object of IO     ## input effect
+  WriteIO = object of IO    ## output effect
+
+proc readSomething(): string {.tags: [ReadIO].} = ""
+proc writeSomething(msg: string): void {.tags: [WriteIO].} = echo msg
+
+proc illegalEffectNegation() {.forbids: [WriteIO], tags: [ReadIO, WriteIO].} =
+  echo readSomething()
+  writeSomething("a")
diff --git a/tests/effects/teffects18.nim b/tests/effects/teffects18.nim
new file mode 100644
index 000000000..576e76635
--- /dev/null
+++ b/tests/effects/teffects18.nim
@@ -0,0 +1,19 @@
+discard """
+action: compile
+errormsg: "type mismatch: got <ProcType2>"
+line: 19
+"""
+
+type MyEffect = object
+type ProcType1 = proc (i: int): void {.forbids: [MyEffect].}
+type ProcType2 = proc (i: int): void
+
+proc testFunc(p: ProcType1): void = p(1)
+
+proc toBeCalled(i: int): void {.tags: [MyEffect].} = echo $i
+
+let emptyTags = proc(i: int): void {.tags: [].} = echo $i
+let noTags: ProcType2 = proc(i: int): void = toBeCalled(i)
+
+testFunc(emptyTags)
+testFunc(noTags)
diff --git a/tests/effects/teffects19.nim b/tests/effects/teffects19.nim
new file mode 100644
index 000000000..6b4ab0819
--- /dev/null
+++ b/tests/effects/teffects19.nim
@@ -0,0 +1,23 @@
+discard """
+action: compile
+errormsg: "type mismatch: got <proc (i: int){.gcsafe.}>"
+line: 23
+"""
+
+type MyEffect = object
+type ProcType1 = proc (i: int): void {.forbids: [MyEffect].}
+type ProcType2 = proc (i: int): void
+
+proc caller1(p: ProcType1): void = p(1)
+proc caller2(p: ProcType2): void = p(1)
+
+proc effectful(i: int): void {.tags: [MyEffect].} = echo $i
+proc effectless(i: int): void {.forbids: [MyEffect].} = echo $i
+
+proc toBeCalled1(i: int): void = effectful(i)
+proc toBeCalled2(i: int): void = effectless(i)
+
+caller1(toBeCalled2)
+caller2(toBeCalled1)
+caller2(toBeCalled2)
+caller1(toBeCalled1)
diff --git a/tests/effects/teffects2.nim b/tests/effects/teffects2.nim
index 0fa789869..777a4cebc 100644
--- a/tests/effects/teffects2.nim
+++ b/tests/effects/teffects2.nim
@@ -1,10 +1,10 @@
 discard """
-  line: 19
   errormsg: "can raise an unlisted exception: ref IOError"
+  line: 19
 """
-
+{.push warningAsError[Effect]: on.}
 type
-  TObj = object {.pure, inheritable.}
+  TObj {.pure, inheritable.} = object
   TObjB = object of TObj
     a, b, c: string
 
@@ -17,4 +17,4 @@ proc lier(): int {.raises: [IOError].} =
 
 proc forw: int =
   raise newException(IOError, "arg")
-
+{.pop.}
diff --git a/tests/effects/teffects3.nim b/tests/effects/teffects3.nim
index 1b18f7b6d..4c050510a 100644
--- a/tests/effects/teffects3.nim
+++ b/tests/effects/teffects3.nim
@@ -1,19 +1,18 @@
 discard """
-  line: 18
   errormsg: "type mismatch"
+  line: 18
 """
 
 type
-  TObj = object {.pure, inheritable.}
+  TObj {.pure, inheritable.} = object
   TObjB = object of TObj
     a, b, c: string
     fn: proc (): int {.tags: [].}
 
-  EIO2 = ref object of EIO
 
-proc raiser(): int {.tags: [TObj, FWriteIO].} =
+
+proc raiser(): int {.tags: [TObj, WriteIoEffect].} =
   writeLine stdout, "arg"
 
 var o: TObjB
 o.fn = raiser
-
diff --git a/tests/effects/teffects4.nim b/tests/effects/teffects4.nim
index d0960126f..b875754b6 100644
--- a/tests/effects/teffects4.nim
+++ b/tests/effects/teffects4.nim
@@ -1,17 +1,17 @@
 discard """
-  line: 23
   errormsg: "type mismatch"
+  line: 23
 """
 
 type
-  TObj = object {.pure, inheritable.}
+  TObj {.pure, inheritable.} = object
   TObjB = object of TObj
     a, b, c: string
-    fn: proc (): int {.tags: [FReadIO].}
+    fn: proc (): int {.tags: [ReadIOEffect].}
 
-  EIO2 = ref object of EIO
 
-proc q() {.tags: [FIO].} =
+
+proc q() {.tags: [IoEffect].} =
   discard
 
 proc raiser(): int =
@@ -21,4 +21,3 @@ proc raiser(): int =
 
 var o: TObjB
 o.fn = raiser
-
diff --git a/tests/effects/teffects6.nim b/tests/effects/teffects6.nim
index e69fe73b6..d3af22434 100644
--- a/tests/effects/teffects6.nim
+++ b/tests/effects/teffects6.nim
@@ -1,3 +1,8 @@
+discard """
+action: compile
+"""
+
+# XXX: it is not actually tested if the effects are inferred
 
 type
   PMenu = ref object
@@ -11,17 +16,17 @@ createMenuItem(s, "Go to definition...",
       proc (i: PMenuItem, p: pointer) {.cdecl.} =
         try:
           echo(i.repr)
-        except EInvalidValue:
+        except ValueError:
           echo("blah")
 )
 
 
-proc noRaise(x: proc()) {.raises: [].} =
+proc noRaise(x: proc()) {.raises: [], effectsOf: x.} =
   # unknown call that might raise anything, but valid:
   x()
 
-proc doRaise() {.raises: [EIO].} =
-  raise newException(EIO, "IO")
+proc doRaise() {.raises: [IoError].} =
+  raise newException(IoError, "IO")
 
 proc use*() =
   noRaise(doRaise)
@@ -29,3 +34,23 @@ proc use*() =
 
 
 use()
+
+# bug #12642
+import os
+
+proc raises() {.raises: Exception.} = discard
+proc harmless() {.raises: [].} = discard
+
+let x = if paramStr(1) == "true": harmless else: raises
+
+let
+  choice = 0
+
+proc withoutSideEffects(): int = 0
+proc withSideEffects(): int = echo "foo" # the echo causes the side effect
+
+let procPtr = case choice
+              of 0: withoutSideEffects
+              else: withSideEffects
+
+echo procPtr.repr
diff --git a/tests/effects/teffects7.nim b/tests/effects/teffects7.nim
new file mode 100644
index 000000000..9b7fbf5f0
--- /dev/null
+++ b/tests/effects/teffects7.nim
@@ -0,0 +1,16 @@
+discard """
+  errormsg: "can raise an unlisted exception: ref ValueError"
+  line: 10
+"""
+{.push warningAsError[Effect]: on.}
+proc foo() {.raises: [].} =
+  try:
+    discard
+  except KeyError:
+    raise newException(ValueError, "foo")
+  except Exception:
+    discard
+
+foo()
+
+{.pop.}
diff --git a/tests/effects/teffects8.nim b/tests/effects/teffects8.nim
new file mode 100644
index 000000000..359b3a1df
--- /dev/null
+++ b/tests/effects/teffects8.nim
@@ -0,0 +1,13 @@
+discard """
+  errormsg: "can raise an unlisted exception: Exception"
+  line: 10
+"""
+{.push warningAsError[Effect]: on.}
+proc foo() {.raises: [].} =
+  try:
+    discard
+  except ValueError:
+    raise
+
+foo()
+{.pop.}
diff --git a/tests/effects/teffects9.nim b/tests/effects/teffects9.nim
new file mode 100644
index 000000000..d92668fe9
--- /dev/null
+++ b/tests/effects/teffects9.nim
@@ -0,0 +1,22 @@
+discard """
+  errormsg: "can raise an unlisted exception: ref Exception"
+  line: 16
+"""
+
+# bug #8481
+
+type
+  ParentObj = ref object of RootObj
+  DerivedObj = ref object of ParentObj
+
+method doSome(o: ParentObj) {.base, raises: [].} =
+  discard
+
+method doSome(o: DerivedObj) =
+  raise newException(Exception, "oops, this raised")
+
+proc thisRaises() {.raises: [].} =
+  let o = new(DerivedObj)
+  o.doSome()
+
+thisRaises()
diff --git a/tests/effects/teffectsmisc.nim b/tests/effects/teffectsmisc.nim
new file mode 100644
index 000000000..8fb95b275
--- /dev/null
+++ b/tests/effects/teffectsmisc.nim
@@ -0,0 +1,39 @@
+discard """
+  output: '''
+printing from adder
+'''
+"""
+
+import std/sugar
+
+block:
+  proc makeAdder(a: int): (int) -> void =
+    proc discard_adder(x: int) {.closure.} =
+      discard a + x
+
+    proc echo_adder(x: int) {.closure.} =
+      echo("printing from adder")
+
+    if a > 0:
+      discard_adder
+    else:
+      echo_adder
+
+  let newAdder = makeAdder(0)
+  newAdder(5)
+
+block:
+  proc makeAdder(a: int): (int) -> void =
+    proc discard_adder(x: int) {.closure.} =
+      discard a + x
+
+    proc echo_adder(x: int) {.closure.} =
+      echo("printing from adder")
+
+    if a > 0:
+      echo_adder
+    else:
+      discard_adder
+
+  let newAdder = makeAdder(0)
+  newAdder(5)
diff --git a/tests/effects/tfuncs_cannot_mutate.nim b/tests/effects/tfuncs_cannot_mutate.nim
new file mode 100644
index 000000000..9934d27a7
--- /dev/null
+++ b/tests/effects/tfuncs_cannot_mutate.nim
@@ -0,0 +1,35 @@
+discard """
+  errormsg: "cannot mutate location select(x, z).data within a strict func"
+  line: 35
+"""
+
+{.experimental: "strictFuncs".}
+
+type
+  Node = ref object
+    le, ri: Node
+    data: string
+
+func insert(x: var seq[Node]; yyy: Node) =
+  let L = x.len
+  x.setLen L + 1
+  x[L] = yyy
+
+func len(n: Node): int =
+  var it = n
+  while it != nil:
+    inc result
+    it = it.ri
+
+func doNotDistract(n: Node) =
+  var m = Node(data: "abc")
+
+func select(a, b: Node): Node = b
+
+func mutate(n: Node) =
+  var it = n
+  let x = it
+  let y = x
+  let z = y
+
+  select(x, z).data = "tricky"
diff --git a/tests/effects/tfuncs_cannot_mutate2.nim b/tests/effects/tfuncs_cannot_mutate2.nim
new file mode 100644
index 000000000..86f811017
--- /dev/null
+++ b/tests/effects/tfuncs_cannot_mutate2.nim
@@ -0,0 +1,24 @@
+discard """
+  errormsg: "cannot mutate location x[0].a within a strict func"
+  line: 12
+"""
+
+{.experimental: "strictFuncs".}
+
+func copy[T](x: var openArray[T]; y: openArray[T]) =
+  for i in 0..high(x):
+    x[i] = y[i]
+
+  x[0].a = nil
+
+type
+  R = ref object
+    a, b: R
+    data: string
+
+proc main =
+  var a, b: array[3, R]
+  b = [R(data: "a"), R(data: "b"), R(data: "c")]
+  copy a, b
+
+main()
diff --git a/tests/effects/tfuncs_cannot_mutate3.nim b/tests/effects/tfuncs_cannot_mutate3.nim
new file mode 100644
index 000000000..029152029
--- /dev/null
+++ b/tests/effects/tfuncs_cannot_mutate3.nim
@@ -0,0 +1,35 @@
+discard """
+  errormsg: "cannot mutate location kid.parent within a strict func"
+  line: 16
+"""
+
+{.experimental: "strictFuncs".}
+
+type
+  Node = ref object
+    name: string
+    kids: seq[Node]
+    parent: Node
+
+func initParents(tree: Node) =
+  for kid in tree.kids:
+    kid.parent = tree
+    initParents(kid)
+
+proc process(intro: Node): Node =
+  var tree = Node(name: "root", kids: @[
+    intro,
+    Node(name: "one", kids: @[
+      Node(name: "two"),
+      Node(name: "three"),
+    ]),
+    Node(name: "four"),
+  ])
+  initParents(tree)
+
+proc main() =
+  var intro = Node(name: "intro")
+  var tree = process(intro)
+  echo intro.parent.name
+
+main()
diff --git a/tests/effects/tfuncs_cannot_mutate_simple.nim b/tests/effects/tfuncs_cannot_mutate_simple.nim
new file mode 100644
index 000000000..0ae4a0db9
--- /dev/null
+++ b/tests/effects/tfuncs_cannot_mutate_simple.nim
@@ -0,0 +1,19 @@
+discard """
+  errormsg: '''cannot mutate location x.data within a strict func'''
+  line: 15
+"""
+
+{.experimental: "strictFuncs".}
+
+# bug #15508
+
+type
+  MyType = ref object
+    data: string
+
+func edit(x: MyType) =
+  x.data = "hello"
+
+let x = MyType()
+x.edit()
+echo x.data
diff --git a/tests/effects/tgcsafe.nim b/tests/effects/tgcsafe.nim
index d146794b6..cfac3ddd8 100644
--- a/tests/effects/tgcsafe.nim
+++ b/tests/effects/tgcsafe.nim
@@ -1,10 +1,19 @@
 discard """
-  line: 17
-  errormsg: "'mainUnsafe' is not GC-safe"
+  errormsg: "'mainUnsafe' is not GC-safe as it performs an indirect call here"
+  line: 26
   cmd: "nim $target --hints:on --threads:on $options $file"
 """
 
-proc mymap(x: proc ()) =
+# bug #6955
+var global_proc: proc(a: string): int {.nimcall.} = nil
+
+proc myproc(i: int) {.gcsafe.} =
+  if global_proc != nil:
+    echo "a"
+  if isNil(global_proc):
+    return
+
+proc mymap(x: proc ()) {.effectsOf: x.} =
   x()
 
 var
diff --git a/tests/effects/tgcsafe2.nim b/tests/effects/tgcsafe2.nim
index 07da4e3f8..6268592a9 100644
--- a/tests/effects/tgcsafe2.nim
+++ b/tests/effects/tgcsafe2.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: '''type mismatch: got <proc (s: string){.locks: 0.}>'''
+  errormsg: '''type mismatch: got <proc (s: string)>'''
   line: 11
 """
 #5620
diff --git a/tests/effects/tgcsafe3.nim b/tests/effects/tgcsafe3.nim
new file mode 100644
index 000000000..36ea5112c
--- /dev/null
+++ b/tests/effects/tgcsafe3.nim
@@ -0,0 +1,21 @@
+discard """
+  errormsg: "'myproc' is not GC-safe as it calls 'global_proc'"
+  line: 12
+  cmd: "nim $target --hints:on --threads:on $options $file"
+"""
+
+var useGcMem = "string here"
+
+var global_proc: proc(a: string) {.nimcall.} = proc (a: string) =
+  echo useGcMem
+
+proc myproc(i: int) {.gcsafe.} =
+  when false:
+    if global_proc != nil:
+      echo "a"
+    if isNil(global_proc):
+      return
+
+  global_proc("ho")
+
+myproc(0)
diff --git a/tests/effects/thooks.nim b/tests/effects/thooks.nim
new file mode 100644
index 000000000..23cc005cd
--- /dev/null
+++ b/tests/effects/thooks.nim
@@ -0,0 +1,16 @@
+discard """
+  matrix: "--warningAsError:Effect"
+"""
+
+import std/isolation
+
+# bug #23129
+type
+  Thing = object
+    x: string
+
+proc send(x: string) =
+  let wrapper = Thing(x: x)
+  discard isolate(wrapper)
+
+send("la")
\ No newline at end of file
diff --git a/tests/effects/tlaxeffects.nim b/tests/effects/tlaxeffects.nim
new file mode 100644
index 000000000..7eedc372a
--- /dev/null
+++ b/tests/effects/tlaxeffects.nim
@@ -0,0 +1,11 @@
+discard """
+  cmd: "nim $target $options --legacy:laxEffects $file"
+"""
+
+
+type
+  Foo = object
+    bar: seq[Foo]
+
+proc `==`(a, b: Foo): bool =
+  a.bar == b.bar
diff --git a/tests/effects/tnestedprocs.nim b/tests/effects/tnestedprocs.nim
new file mode 100644
index 000000000..125896d44
--- /dev/null
+++ b/tests/effects/tnestedprocs.nim
@@ -0,0 +1,63 @@
+discard """
+  cmd: "nim check --hints:off $file"
+  nimout: '''tnestedprocs.nim(27, 8) Error: 'inner' can have side effects
+> tnestedprocs.nim(29, 13) Hint: 'inner' calls `.sideEffect` 'outer2'
+>> tnestedprocs.nim(26, 6) Hint: 'outer2' called by 'inner'
+
+tnestedprocs.nim(45, 8) Error: 'inner' can have side effects
+> tnestedprocs.nim(47, 13) Hint: 'inner' calls `.sideEffect` 'outer6'
+>> tnestedprocs.nim(44, 6) Hint: 'outer6' called by 'inner'
+
+tnestedprocs.nim(58, 41) Error: type mismatch: got <proc ()> but expected 'proc (){.closure, noSideEffect.}'
+  Pragma mismatch: got '{..}', but expected '{.noSideEffect.}'.
+'''
+  errormsg: "type mismatch: got <proc ()> but expected 'proc (){.closure, noSideEffect.}'"
+"""
+{.experimental: "strictEffects".}
+proc outer {.noSideEffect.} =
+  proc inner(p: int) =
+    if p == 0:
+      outer()
+
+  inner(4)
+
+outer()
+
+proc outer2 =
+  proc inner(p: int) {.noSideEffect.} =
+    if p == 0:
+      outer2()
+
+  inner(4)
+
+outer2()
+
+proc outer3(p: int) {.noSideEffect.} =
+  proc inner(p: int) {.noSideEffect.} =
+    if p == 0:
+      p.outer3()
+
+  inner(4)
+
+outer3(5)
+
+proc outer6 =
+  proc inner(p: int) {.noSideEffect.} =
+    if p == 0:
+      outer6()
+
+  inner(4)
+  echo "bad"
+
+outer6()
+
+
+proc outer4 =
+  proc inner(p: int) {.noSideEffect.} =
+    if p == 0:
+      let x: proc () {.noSideEffect.} = outer4
+      x()
+
+  inner(4)
+
+outer4()
diff --git a/tests/effects/tnosideeffect.nim b/tests/effects/tnosideeffect.nim
new file mode 100644
index 000000000..9fc2f74d4
--- /dev/null
+++ b/tests/effects/tnosideeffect.nim
@@ -0,0 +1,24 @@
+block: # `.noSideEffect`
+  func foo(bar: proc(): int): int {.effectsOf: bar.} = bar()
+  var count = 0
+  proc fn1(): int = 1
+  proc fn2(): int = (count.inc; count)
+
+  template accept(body) =
+    doAssert compiles(block:
+      body)
+
+  template reject(body) =
+    doAssert not compiles(block:
+      body)
+
+  accept:
+    func fun1() = discard foo(fn1)
+  reject:
+    func fun1() = discard foo(fn2)
+
+  var foo2: type(foo) = foo
+  accept:
+    func main() = discard foo(fn1)
+  reject:
+    func main() = discard foo2(fn1)
diff --git a/tests/effects/tsidee1.nim b/tests/effects/tsidee1.nim
index e486d32e7..ca6816561 100644
--- a/tests/effects/tsidee1.nim
+++ b/tests/effects/tsidee1.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "\'SideEffectLyer\' can have side effects"
   file: "tsidee1.nim"
   line: 12
-  errormsg: "\'SideEffectLyer\' can have side effects"
 """
 
 var
@@ -13,6 +13,3 @@ proc SideEffectLyer(x, y: int): int {.noSideEffect.} = #ERROR_MSG 'SideEffectLye
   return x + y + dontcare(x)
 
 echo SideEffectLyer(1, 3)
-
-
-
diff --git a/tests/effects/tsidee2.nim b/tests/effects/tsidee2.nim
index 5ed541300..b2e5f3379 100644
--- a/tests/effects/tsidee2.nim
+++ b/tests/effects/tsidee2.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tsidee2.nim"
   output: "5"
 """
 
@@ -12,6 +11,3 @@ proc SideEffectLyer(x, y: int): int {.noSideEffect.} =
   return x + y + dontcare(x)
 
 echo SideEffectLyer(1, 3) #OUT 5
-
-
-
diff --git a/tests/effects/tsidee3.nim b/tests/effects/tsidee3.nim
index e15fbc3d1..c3801ba28 100644
--- a/tests/effects/tsidee3.nim
+++ b/tests/effects/tsidee3.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tsidee3.nim"
   output: "5"
 """
 
@@ -12,6 +11,3 @@ proc noSideEffect(x, y: int, p: proc (a: int): int {.noSideEffect.}): int {.noSi
   return x + y + dontcare(x)
 
 echo noSideEffect(1, 3, dontcare) #OUT 5
-
-
-
diff --git a/tests/effects/tsidee4.nim b/tests/effects/tsidee4.nim
index ecc79580c..d6c192cbf 100644
--- a/tests/effects/tsidee4.nim
+++ b/tests/effects/tsidee4.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "'noSideEffect' can have side effects"
   file: "tsidee4.nim"
   line: 12
-  errormsg: "'noSideEffect' can have side effects"
 """
 
 var
@@ -13,5 +13,3 @@ proc noSideEffect(x, y: int, p: proc (a: int): int {.noSideEffect.}): int {.noSi
   return x + y + dontcare(x)
 
 echo noSideEffect(1, 3, dontcare) #ERROR_MSG type mismatch
-
-
diff --git a/tests/effects/tstrict_caseobjects.nim b/tests/effects/tstrict_caseobjects.nim
new file mode 100644
index 000000000..20bc810e0
--- /dev/null
+++ b/tests/effects/tstrict_caseobjects.nim
@@ -0,0 +1,47 @@
+discard """
+  errormsg: "field access outside of valid case branch: x.x"
+  line: 45
+"""
+
+{.experimental: "strictCaseObjects".}
+
+type
+  NodeKind = enum
+    nkParent,
+    nkChild
+  
+  Node {.acyclic.} = ref object
+    case kind: NodeKind
+    of nkParent:
+      children: seq[Node]
+    of nkChild:
+      name: string
+
+let list = @[Node(kind: nkParent, children: @[]), Node(kind: nkChild, name: "hello")]
+for node in list:
+  case node.kind
+  of nkChild: 
+    echo $node.name # here this time there is a warning
+  else: discard
+
+
+type
+  Foo = object
+    case b: bool
+    of false:
+      s: string
+    of true:
+      x: int
+
+var x = Foo(b: true, x: 4)
+case x.b
+of true:
+  echo x.x
+of false:
+  echo "no"
+
+case x.b
+of false:
+  echo x.x
+of true:
+  echo "no"
diff --git a/tests/effects/tstrict_effects.nim b/tests/effects/tstrict_effects.nim
new file mode 100644
index 000000000..eee8fb71a
--- /dev/null
+++ b/tests/effects/tstrict_effects.nim
@@ -0,0 +1,27 @@
+discard """
+  errormsg: "s1 can raise an unlisted exception: CatchableError"
+  line: 27
+"""
+
+{.push warningAsError[Effect]: on.}
+{.experimental: "strictEffects".}
+
+# bug #18376
+
+{.push raises: [Defect].}
+type Call = proc (x: int): int {.gcsafe, raises: [Defect, CatchableError].}
+
+type Bar* = object
+  foo*: Call
+
+proc passOn*(x: Call) = discard
+
+proc barCal(b: var Bar, s: string, s1: Call) =
+  #compiler complains that his line can throw CatchableError
+  passOn s1
+
+
+proc passOnB*(x: Call) {.effectsOf: x.} = discard
+
+proc barCal2(b: var Bar, s: string, s1: Call) =
+  passOnB s1
diff --git a/tests/effects/tstrict_effects2.nim b/tests/effects/tstrict_effects2.nim
new file mode 100644
index 000000000..acc0a0540
--- /dev/null
+++ b/tests/effects/tstrict_effects2.nim
@@ -0,0 +1,28 @@
+discard """
+  errormsg: "can raise an unlisted exception: Exception"
+  line: 23
+"""
+
+{.push warningAsError[Effect]: on.}
+{.experimental: "strictEffects".}
+
+# bug #13905
+
+proc atoi(v: cstring): cint {.importc: "atoi", cdecl, raises: [].}
+
+type Conv = proc(v: cstring): cint {.cdecl, raises: [].}
+
+var x: Conv = atoi
+
+# bug #17475
+
+type
+  Callback = proc()
+
+proc f(callback: Callback) {.raises: [].} =
+  callback()
+
+proc main =
+  f(proc () = raise newException(IOError, "IO"))
+
+main()
diff --git a/tests/effects/tstrict_effects3.nim b/tests/effects/tstrict_effects3.nim
new file mode 100644
index 000000000..0d98a0343
--- /dev/null
+++ b/tests/effects/tstrict_effects3.nim
@@ -0,0 +1,57 @@
+discard """
+  action: compile
+"""
+
+{.push warningAsError[Effect]: on.}
+
+{.experimental: "strictEffects".}
+
+proc fn(a: int, p1, p2: proc()) {.effectsOf: p1.} =
+  if a == 7:
+    p1()
+  if a<0:
+    raise newException(ValueError, $a)
+
+proc main() {.raises: [ValueError].} =
+  fn(1, proc()=discard, proc() = raise newException(IOError, "foo"))
+main()
+
+# bug #19159
+
+import macros
+
+func mkEnter() =
+  template helper =
+    discard
+  when defined pass:
+    helper()
+  else:
+    let ast = getAst(helper())
+
+
+# bug #6559
+type
+  SafeFn = proc (): void {. raises: [] }
+
+proc ok() {. raises: [] .} = discard
+proc fail() {. raises: [] .}
+
+let f1 : SafeFn = ok
+let f2 : SafeFn = fail
+
+
+proc fail() = discard
+f1()
+f2()
+
+import std/json
+
+# bug #22254
+proc senri(a, b: seq[JsonNode]) {.raises: [].} = discard a == b
+
+# bug #22253
+proc serika() {.raises: [].} = discard default(JsonNode) == nil
+
+senri(@[newJBool(true)], @[newJBool(false)])
+serika()
+
diff --git a/tests/effects/tstrict_effects_sort.nim b/tests/effects/tstrict_effects_sort.nim
new file mode 100644
index 000000000..8928ed0d3
--- /dev/null
+++ b/tests/effects/tstrict_effects_sort.nim
@@ -0,0 +1,27 @@
+discard """
+  errormsg: "cmpE can raise an unlisted exception: Exception"
+  line: 27
+"""
+
+{.push warningAsError[Effect]: on.}
+
+{.experimental: "strictEffects".}
+
+import algorithm
+
+type
+  MyInt = distinct int
+
+var toSort = @[MyInt 1, MyInt 2, MyInt 3]
+
+proc cmpN(a, b: MyInt): int =
+  cmp(a.int, b.int)
+
+proc harmless {.raises: [].} =
+  toSort.sort cmpN
+
+proc cmpE(a, b: MyInt): int {.raises: [Exception].} =
+  cmp(a.int, b.int)
+
+proc harmfull {.raises: [].} =
+  toSort.sort cmpE
diff --git a/tests/effects/tstrict_funcs.nim b/tests/effects/tstrict_funcs.nim
new file mode 100644
index 000000000..9d20f5d7e
--- /dev/null
+++ b/tests/effects/tstrict_funcs.nim
@@ -0,0 +1,46 @@
+discard """
+  cmd: "nim c --experimental:strictFuncs --experimental:views $file"
+"""
+
+import tables, streams, parsecsv
+
+type
+  Contig2Reads = TableRef[string, seq[string]]
+
+proc get_Contig2Reads(sin: Stream, fn: string, contig2len: TableRef[string, int]): Contig2Reads =
+  result = newTable[string, seq[string]]()
+  var parser: CsvParser
+  open(parser, sin, filename = fn, separator = ' ', skipInitialSpace = true)
+  while readRow(parser, 2):
+    if contig2len.haskey(parser.row[1]):
+      mgetOrPut(result, parser.row[1], @[]).add(parser.row[0])
+
+
+
+block:
+  # issue #15756
+  func `&&&`[T](x: var seq[T], y: sink T): seq[T] =
+    newSeq(result, x.len + 1)
+    for i in 0..x.len-1:
+      result[i] = move(x[i])
+    result[x.len] = move(y)
+
+  var x = @[0, 1]
+  let z = x &&& 2
+
+
+func copy[T](x: var openArray[T]; y: openArray[T]) =
+  for i in 0..high(x):
+    x[i] = y[i]
+
+type
+  R = ref object
+    a, b: R
+    data: string
+
+proc main =
+  var a, b: array[3, R]
+  b = [R(data: "a"), R(data: "b"), R(data: "c")]
+  copy a, b
+
+main()
diff --git a/tests/effects/tstrict_funcs_imports.nim b/tests/effects/tstrict_funcs_imports.nim
new file mode 100644
index 000000000..bf68b61b2
--- /dev/null
+++ b/tests/effects/tstrict_funcs_imports.nim
@@ -0,0 +1,176 @@
+discard """
+  cmd: "nim $target $options --hints:on --experimental:strictFuncs --experimental:views --threads:on -d:ssl -d:nimCoroutines $file"
+  targets: "c"
+"""
+{.warning[UnusedImport]: off.}
+
+when defined(linux):
+  import linenoise
+
+when defined(nimPreviewSlimSystem):
+  import std/[
+    assertions,
+    formatfloat,
+    objectdollar,
+    syncio,
+    widestrs,
+  ]
+
+import
+  algorithm,
+  asyncdispatch,
+  asyncfile,
+  asyncfutures,
+  asynchttpserver,
+  asyncmacro,
+  asyncnet,
+  asyncstreams,
+  atomics,
+  base64,
+  bitops,
+  browsers,
+  cgi,
+  chains,
+  colors,
+  complex,
+  cookies,
+  coro,
+  cpuinfo,
+  cpuload,
+  critbits,
+  cstrutils,
+  deques,
+  distros,
+  dynlib,
+  encodings,
+  endians,
+  epoll,
+  fenv,
+  hashes,
+  heapqueue,
+  hotcodereloading,
+  htmlgen,
+  htmlparser,
+  httpclient,
+  httpcore,
+  inotify,
+  intsets,
+  json,
+  kqueue,
+  lenientops,
+  lexbase,
+  lists,
+  locks,
+  logging,
+  macrocache,
+  macros,
+  marshal,
+  math,
+  memfiles,
+  mersenne,
+  mimetypes,
+  nativesockets,
+  net,
+  nimhcr,
+  # nimprof,
+  nre,
+  oids,
+  options,
+  os,
+  osproc,
+  parsecfg,
+  parsecsv,
+  parsejson,
+  parseopt,
+  parsesql,
+  parseutils,
+  parsexml,
+  pathnorm,
+  pegs,
+  posix_utils,
+  prelude,
+  random,
+  rationals,
+  rdstdin,
+  re,
+  registry,
+  reservedmem,
+  rlocks,
+  ropes,
+  rtarrays,
+  selectors,
+  sequtils,
+  sets,
+  sharedlist,
+  sharedtables,
+  ssl_certs,
+  ssl_config,
+  stats,
+  streams,
+  streamwrapper,
+  strformat,
+  strmisc,
+  strscans,
+  strtabs,
+  strutils,
+  sugar,
+  tables,
+  terminal,
+  threadpool,
+  times,
+  typeinfo,
+  typetraits,
+  unicode,
+  unidecode,
+  unittest,
+  uri,
+  volatile,
+  winlean,
+  xmlparser,
+  xmltree
+
+import experimental/[
+  diff,
+]
+
+import packages/docutils/[
+  highlite,
+  rst,
+  rstast,
+  rstgen,
+]
+
+import std/[
+  compilesettings,
+  decls,
+  editdistance,
+  effecttraits,
+  enumerate,
+  enumutils,
+  exitprocs,
+  isolation,
+  jsonutils,
+  logic,
+  monotimes,
+  packedsets,
+  setutils,
+  socketstreams,
+  stackframes,
+  sums,
+  time_t,
+  varints,
+  with,
+  wordwrap,
+  wrapnils,
+]
+
+import std/private/[
+  asciitables,
+  decode_helpers,
+  gitutils,
+  globs,
+  miscdollars,
+  since,
+  strimpl,
+  underscored_calls,
+]
diff --git a/tests/effects/tstrict_funcs_imports_js.nim b/tests/effects/tstrict_funcs_imports_js.nim
new file mode 100644
index 000000000..667887ff0
--- /dev/null
+++ b/tests/effects/tstrict_funcs_imports_js.nim
@@ -0,0 +1,20 @@
+discard """
+  cmd: "nim $target $options --hints:on --experimental:strictFuncs --experimental:views $file"
+  targets: "js"
+"""
+{.warning[UnusedImport]: off.}
+
+import
+  asyncjs,
+  dom,
+  jsconsole,
+  jsffi,
+  jsre
+
+import std/[
+  jsbigints,
+]
+
+import std/private/[
+  jsutils,
+]
diff --git a/tests/effects/tstrictfuncs_misc.nim b/tests/effects/tstrictfuncs_misc.nim
new file mode 100644
index 000000000..8c573bb3a
--- /dev/null
+++ b/tests/effects/tstrictfuncs_misc.nim
@@ -0,0 +1,65 @@
+discard """
+  action: compile
+"""
+
+{.experimental: "strictFuncs".}
+
+func sortedFake1[T](a: openArray[T]): seq[T] =
+  for i in 0 .. a.high: result.add a[i]
+func sortedFake2[T](a: openArray[T]): seq[T] =
+  result = newSeq[T](a.len)
+  for i in 0 .. a.high: result[i] = a[i]
+type Foo1 = object
+type Foo2 = ref object
+block:
+  let a1 = sortedFake1([Foo1()]) # ok
+  let a2 = sortedFake1([Foo2()]) # ok
+block:
+  let a1 = sortedFake2([Foo1()]) # ok
+  let a2 = sortedFake2([Foo2()]) # error: Error: 'sortedFake2' can have side effects
+
+
+import std/sequtils
+type Foob = ref object
+  x: int
+let a1 = zip(@[1,2], @[1,2]) # ok
+let a2 = zip(@[Foob(x: 1)], @[Foob(x: 2)]) # error in 1.6.0 RC2, but not 1.4.x
+
+
+# bug #20863
+type
+  Fooc = ref object
+
+func twice(foo: Fooc) =
+  var a = newSeq[Fooc](2)
+  a[0] = foo # No error.
+  a[1] = foo # Error: 'twice' can have side effects.
+
+let foo = Fooc()
+twice(foo)
+
+# bug #17387
+import json
+
+func parseColumn(columnNode: JsonNode) =
+  let columnName = columnNode["name"].str
+
+parseColumn(%*{"a": "b"})
+
+type
+  MyTable = object
+    data: seq[int]
+
+  JsonNode3 = ref object
+    fields: MyTable
+
+proc `[]`(t: MyTable, key: string): int =
+  result = t.data[0]
+
+proc `[]`(x: JsonNode3, key: string): int =
+  result = x.fields[key]
+
+func parseColumn(columnNode: JsonNode3) =
+  var columnName = columnNode["test"]
+
+parseColumn(JsonNode3())
diff --git a/tests/enum/m16462_1.nim b/tests/enum/m16462_1.nim
new file mode 100644
index 000000000..631c63256
--- /dev/null
+++ b/tests/enum/m16462_1.nim
@@ -0,0 +1,3 @@
+type
+  Scancode* {.pure.} = enum
+    SCANCODE_LEFT = 80
\ No newline at end of file
diff --git a/tests/enum/m16462_2.nim b/tests/enum/m16462_2.nim
new file mode 100644
index 000000000..631c63256
--- /dev/null
+++ b/tests/enum/m16462_2.nim
@@ -0,0 +1,3 @@
+type
+  Scancode* {.pure.} = enum
+    SCANCODE_LEFT = 80
\ No newline at end of file
diff --git a/tests/enum/mcrossmodule.nim b/tests/enum/mcrossmodule.nim
new file mode 100644
index 000000000..e534a202c
--- /dev/null
+++ b/tests/enum/mcrossmodule.nim
@@ -0,0 +1,6 @@
+
+type
+  OtherEnum* = enum
+    Success, Failed, More
+
+proc some*(x: OtherEnum): bool = x == Success
diff --git a/tests/enum/t16462.nim b/tests/enum/t16462.nim
new file mode 100644
index 000000000..9f38286bb
--- /dev/null
+++ b/tests/enum/t16462.nim
@@ -0,0 +1,5 @@
+import m16462_1 except Scancode
+import m16462_2
+
+# bug #16462
+let a = SCANCODE_LEFT
\ No newline at end of file
diff --git a/tests/enum/t21863.nim b/tests/enum/t21863.nim
new file mode 100644
index 000000000..d0d8b1fcd
--- /dev/null
+++ b/tests/enum/t21863.nim
@@ -0,0 +1,28 @@
+discard """
+cmd: "nim check --hints:off $file"
+action: reject
+nimout: '''
+t21863.nim(28, 16) Error: undeclared field: 'A' 
+  found 'A' [enumField declared in t21863.nim(24, 18)]
+  found 'A' [enumField declared in t21863.nim(25, 18)]
+t21863.nim(28, 16) Error: undeclared field: '.'
+t21863.nim(28, 16) Error: undeclared field: '.'
+t21863.nim(28, 16) Error: expression '' has no type (or is ambiguous)
+'''
+"""
+
+
+
+
+
+
+
+
+
+block:
+  type
+    EnumA = enum A, B
+    EnumB = enum A
+    EnumC = enum C
+
+  discard EnumC.A
diff --git a/tests/enum/tambiguousoverloads.nim b/tests/enum/tambiguousoverloads.nim
new file mode 100644
index 000000000..12c78c848
--- /dev/null
+++ b/tests/enum/tambiguousoverloads.nim
@@ -0,0 +1,26 @@
+discard """
+cmd: "nim check --hints:off $file"
+"""
+
+block: # bug #21887
+  type
+    EnumA = enum A = 300, B
+    EnumB = enum A = 10
+    EnumC = enum C
+
+  doAssert typeof(EnumC(A)) is EnumC #[tt.Error
+                        ^ ambiguous identifier: 'A' -- use one of the following:
+  EnumA.A: EnumA
+  EnumB.A: EnumB]#
+
+block: # issue #22598
+  type
+    A = enum
+      red
+    B = enum
+      red
+
+  let a = red #[tt.Error
+          ^ ambiguous identifier: 'red' -- use one of the following:
+  A.red: A
+  B.red: B]#
diff --git a/tests/enum/tbasicenum.nim b/tests/enum/tbasicenum.nim
deleted file mode 100644
index eb2182f71..000000000
--- a/tests/enum/tbasicenum.nim
+++ /dev/null
@@ -1,11 +0,0 @@
-discard """
-  file: "tbasicenum.nim"
-  output: "ABCDC"
-"""
-
-type
-  MyEnum = enum
-    A,B,C,D
-# trick the optimizer with an seq:
-var x = @[A,B,C,D]
-echo x[0],x[1],x[2],x[3],MyEnum(2)
\ No newline at end of file
diff --git a/tests/enum/tcrossmodule.nim b/tests/enum/tcrossmodule.nim
new file mode 100644
index 000000000..c21072198
--- /dev/null
+++ b/tests/enum/tcrossmodule.nim
@@ -0,0 +1,15 @@
+import mcrossmodule
+
+type
+  MyEnum = enum
+    Success
+
+template t =
+  doAssert some(Success)
+
+t()
+
+block: # legacy support for behavior before overloadableEnums
+  # warning: ambiguous enum field 'Success' assumed to be of type MyEnum
+  let x = {Success}
+  doAssert x is set[MyEnum]
diff --git a/tests/enum/tenum.nim b/tests/enum/tenum.nim
index 6d9bdd539..a03019c5d 100644
--- a/tests/enum/tenum.nim
+++ b/tests/enum/tenum.nim
@@ -1,14 +1,267 @@
-# Test enums
+discard """
+  output: '''
+B
+B
+ABCDC
+foo
+first0second32third64
+my value A1my value Bconc2valueCabc4abc
+my value A0my value Bconc1valueCabc3abc
+'''
+"""
 
-type
-  E = enum a, b, c, x, y, z
 
-var
-  en: E
-en = a
-
-# Bug #4066
 import macros
-macro genEnum(): untyped = newNimNode(nnkEnumTy).add(newEmptyNode(), newIdentNode("geItem1"))
-type GeneratedEnum = genEnum()
-doAssert(type(geItem1) is GeneratedEnum)
+
+block tenum1:
+  type E = enum a, b, c, x, y, z
+  var en: E
+  en = a
+
+  # Bug #4066
+  macro genEnum(): untyped = newNimNode(nnkEnumTy).add(newEmptyNode(), newIdentNode("geItem1"))
+  type GeneratedEnum = genEnum()
+  doAssert(type(geItem1) is GeneratedEnum)
+
+
+
+block tenum2:
+  type
+    TEnumHole = enum
+      eA = 0,
+      eB = 4,
+      eC = 5
+
+  var
+    e: TEnumHole = eB
+
+  case e
+  of eA: echo "A"
+  of eB: echo "B"
+  of eC: echo "C"
+
+
+
+block tenum3:
+  type
+    TEnumHole {.size: sizeof(int).} = enum
+      eA = 0,
+      eB = 4,
+      eC = 5
+
+  var
+    e: TEnumHole = eB
+
+  case e
+  of eA: echo "A"
+  of eB: echo "B"
+  of eC: echo "C"
+
+
+
+block tbasic:
+  type
+    MyEnum = enum
+      A,B,C,D
+  # trick the optimizer with an seq:
+  var x = @[A,B,C,D]
+  echo x[0],x[1],x[2],x[3],MyEnum(2)
+
+
+
+block talias:
+  # bug #5148
+  type
+    A = enum foo, bar
+    B = A
+
+  echo B.foo
+
+
+
+block thole:
+  type Holed = enum
+    hFirst = (0,"first")
+    hSecond = (32,"second")
+    hThird = (64,"third")
+    
+  var x = @[0,32,64] # This is just to avoid the compiler inlining the value of the enum
+
+  echo Holed(x[0]),ord Holed(x[0]),Holed(x[1]),ord Holed(x[1]),Holed(x[2]),ord Holed(x[2])
+
+
+
+block toffset:
+  const
+    strValB = "my value B"
+
+  type
+    TMyEnum = enum
+      valueA = (1, "my value A"),
+      valueB = strValB & "conc",
+      valueC,
+      valueD = (4, "abc")
+
+  proc getValue(i:int): TMyEnum = TMyEnum(i)
+
+  # trick the optimizer with a variable:
+  var x = getValue(4)
+  echo getValue(1), ord(valueA), getValue(2), ord(valueB), getValue(3), getValue(4), ord(valueD), x
+
+
+
+block tnamedfields:
+  const strValB = "my value B"
+
+  type
+    TMyEnum = enum
+      valueA = (0, "my value A"),
+      valueB = strValB & "conc",
+      valueC,
+      valueD = (3, "abc"),
+      valueE = 4
+
+  # trick the optimizer with a variable:
+  var x = valueD
+  echo valueA, ord(valueA), valueB, ord(valueB), valueC, valueD, ord(valueD), x
+  doAssert $x == $valueD, $x
+  doAssert $x == "abc", $x
+
+
+block tfakeOptions:
+  type
+    TFakeOption = enum
+      fakeNone, fakeForceFullMake, fakeBoehmGC, fakeRefcGC, fakeRangeCheck,
+      fakeBoundsCheck, fakeOverflowCheck, fakeNilCheck, fakeAssert, fakeLineDir,
+      fakeWarns, fakeHints, fakeListCmd, fakeCompileOnly,
+      fakeSafeCode,             # only allow safe code
+      fakeStyleCheck, fakeOptimizeSpeed, fakeOptimizeSize, fakeGenDynLib,
+      fakeGenGuiApp, fakeStackTrace
+
+    TFakeOptionset = set[TFakeOption]
+
+  var
+    gFakeOptions: TFakeOptionset = {fakeRefcGC, fakeRangeCheck, fakeBoundsCheck,
+      fakeOverflowCheck, fakeAssert, fakeWarns, fakeHints, fakeLineDir, fakeStackTrace}
+    compilerArgs: int
+    gExitcode: int8
+
+
+
+block nonzero: # bug #6959
+  type SomeEnum = enum
+    A = 10
+    B
+    C
+  let slice = SomeEnum.low..SomeEnum.high
+
+block size_one_byte: # bug #15752
+  type
+    Flag = enum
+      Disabled = 0x00
+      Enabled = 0xFF
+
+  static:
+    assert 1 == sizeof(Flag)
+
+block: # bug #12589
+  when not defined(i386):
+    type
+      OGRwkbGeometryType {.size: sizeof(cuint).} = enum
+        wkbPoint25D = 0x80000001.cuint, wkbLineString25D = 0x80000002,
+        wkbPolygon25D = 0x80000003
+
+    proc typ(): OGRwkbGeometryType =
+      return wkbPoint25D
+
+    when not defined(gcRefc):
+      doAssert $typ() == "wkbPoint25D"
+
+    block: # bug #21280
+      type
+        Test = enum
+          B = 19
+          A = int64.high()
+
+      doAssert ord(A) == int64.high()
+
+import std/enumutils
+from std/sequtils import toSeq
+import std/macros
+
+block: # unordered enum
+  block:
+    type
+      unordered_enum = enum
+        a = 1
+        b = 0
+
+    doAssert (ord(a), ord(b)) == (1, 0)
+    doAssert unordered_enum.toSeq == @[a, b]
+
+  block:
+    type
+      unordered_enum = enum
+        a = 1
+        b = 0
+        c
+
+    doAssert (ord(a), ord(b), ord(c)) == (1, 0, 2)
+
+  block:
+    type
+      unordered_enum = enum
+        a = 100
+        b
+        c = 50
+        d
+
+    doAssert (ord(a), ord(b), ord(c), ord(d)) == (100, 101, 50, 51)
+
+  block:
+    type
+      unordered_enum = enum
+        a = 7
+        b = 6
+        c = 5
+        d
+
+    doAssert (ord(a), ord(b), ord(c), ord(d)) == (7, 6, 5, 8)
+    doAssert unordered_enum.toSeq == @[a, b, c, d]
+
+  block:
+    type
+      unordered_enum = enum
+        a = 100
+        b
+        c = 500
+        d
+        e
+        f = 50
+        g
+        h
+
+    doAssert (ord(a), ord(b), ord(c), ord(d), ord(e), ord(f), ord(g), ord(h)) == 
+             (100, 101, 500, 501, 502, 50, 51, 52)
+
+  block:
+    type
+      unordered_enum = enum
+        A
+        B
+        C = -1
+        D
+        E
+        G = -999
+
+    doAssert (ord(A), ord(B), ord(C), ord(D), ord(E), ord(G)) ==
+             (0, 1, -1, 2, 3, -999)
+
+  block:
+    type
+      SomeEnum = enum
+        seA = 3
+        seB = 2
+        seC = "foo"
+
+    doAssert (ord(seA), ord(seB), ord(seC)) == (3, 2, 4)
diff --git a/tests/enum/tenum2.nim b/tests/enum/tenum2.nim
deleted file mode 100644
index 3e34a21ce..000000000
--- a/tests/enum/tenum2.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-# Test that enum with holes is handled correctly by case statement
-
-type
-  TEnumHole = enum
-    eA = 0,
-    eB = 4,
-    eC = 5
-
-var
-  e: TEnumHole = eB
-
-case e
-of eA: echo "A"
-of eB: echo "B"
-of eC: echo "C"
-
diff --git a/tests/enum/tenum3.nim b/tests/enum/tenum3.nim
deleted file mode 100644
index 49cbf04d5..000000000
--- a/tests/enum/tenum3.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-# Test enum with explicit size
-
-type
-  TEnumHole {.size: sizeof(int).} = enum
-    eA = 0,
-    eB = 4,
-    eC = 5
-
-var
-  e: TEnumHole = eB
-
-case e
-of eA: echo "A"
-of eB: echo "B"
-of eC: echo "C"
-
diff --git a/tests/enum/tenum_duplicate.nim b/tests/enum/tenum_duplicate.nim
new file mode 100644
index 000000000..4bcad7f6f
--- /dev/null
+++ b/tests/enum/tenum_duplicate.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "duplicate value in enum 'd'"
+"""
+
+type
+  unordered_enum = enum
+    a = 1
+    b = 0
+    c
+    d = 2
diff --git a/tests/enum/tenum_invalid.nim b/tests/enum/tenum_invalid.nim
new file mode 100644
index 000000000..8ae0a1057
--- /dev/null
+++ b/tests/enum/tenum_invalid.nim
@@ -0,0 +1,8 @@
+discard """
+cmd: "nim check $file"
+"""
+
+type
+  Test = enum
+    A = 9.0 #[tt.Error
+        ^ ordinal type expected; given: float]#
diff --git a/tests/enum/tenum_no_rtti.nim b/tests/enum/tenum_no_rtti.nim
new file mode 100644
index 000000000..2e5c74b35
--- /dev/null
+++ b/tests/enum/tenum_no_rtti.nim
@@ -0,0 +1,12 @@
+discard """
+  output: '''A
+B'''
+  cmd: '''nim c --gc:arc $file'''
+"""
+type
+  Enum = enum A, B, C
+  EnumRange = range[A .. B]
+proc test_a(x: Enum): string = $x
+proc test_b(x: EnumRange): string = $x
+echo test_a(A)
+echo test_b(B)
diff --git a/tests/enum/tenum_self.nim b/tests/enum/tenum_self.nim
new file mode 100644
index 000000000..27b1c3204
--- /dev/null
+++ b/tests/enum/tenum_self.nim
@@ -0,0 +1,11 @@
+discard """
+  errormsg: "1 can't be converted to ErrorFoo"
+"""
+
+
+type
+  Foo = enum
+    Bar = 0.Foo
+
+  ErrorFoo = enum
+    eBar = 1.ErrorFoo
diff --git a/tests/enum/tenumalias.nim b/tests/enum/tenumalias.nim
deleted file mode 100644
index 2d1f70d0e..000000000
--- a/tests/enum/tenumalias.nim
+++ /dev/null
@@ -1,7 +0,0 @@
-# bug #5148
-
-type
-  A = enum foo, bar
-  B = A
-
-echo B.foo
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/enum/tenumfieldpragmanoannot.nim b/tests/enum/tenumfieldpragmanoannot.nim
new file mode 100644
index 000000000..47f920827
--- /dev/null
+++ b/tests/enum/tenumfieldpragmanoannot.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "annotation to deprecated not supported here"
+  line: 8
+"""
+
+type
+  A = enum
+    a {.deprecated: "njshd".}
+
+var v1 = a
diff --git a/tests/enum/tenumhole.nim b/tests/enum/tenumhole.nim
deleted file mode 100644
index 4928572f9..000000000
--- a/tests/enum/tenumhole.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-discard """
-  file: "tenumhole.nim"
-  output: "first0second32third64"
-"""
-
-type Holed = enum
-  hFirst = (0,"first")
-  hSecond = (32,"second")
-  hThird = (64,"third")
-  
-var x = @[0,32,64] # This is just to avoid the compiler inlining the value of the enum
-
-echo Holed(x[0]),ord Holed(x[0]),Holed(x[1]),ord Holed(x[1]),Holed(x[2]),ord Holed(x[2])
-
-
-
-
diff --git a/tests/enum/tenumitems.nim b/tests/enum/tenumitems.nim
index 38233aad7..76f368f8a 100644
--- a/tests/enum/tenumitems.nim
+++ b/tests/enum/tenumitems.nim
@@ -1,9 +1,7 @@
 discard """
+  errormsg: "attempting to call routine: 'items'"
   line: 7
-  errormsg: "attempting to call undeclared routine: 'items'"
 """
 
 type a = enum b,c,d
 a.items()
-
-
diff --git a/tests/enum/tenummix.nim b/tests/enum/tenummix.nim
index c7db4e056..8c306bae1 100644
--- a/tests/enum/tenummix.nim
+++ b/tests/enum/tenummix.nim
@@ -1,7 +1,7 @@
 discard """
-  tfile: "tenummix.nim"
-  tline: 11
   errormsg: "type mismatch"
+  file: "tenummix.nim"
+  line: 11
 """
 
 type
diff --git a/tests/enum/tenumoffset.nim b/tests/enum/tenumoffset.nim
deleted file mode 100644
index e67164604..000000000
--- a/tests/enum/tenumoffset.nim
+++ /dev/null
@@ -1,20 +0,0 @@
-discard """
-  file: "tenumoffset.nim"
-  output: "my value A1my value Bconc2valueCabc4abc"
-"""
-
-const
-  strValB = "my value B"
-
-type
-  TMyEnum = enum
-    valueA = (1, "my value A"),
-    valueB = strValB & "conc",
-    valueC,
-    valueD = (4, "abc")
-
-proc getValue(i:int): TMyEnum = TMyEnum(i)
-
-# trick the optimizer with a variable:
-var x = getValue(4)
-echo getValue(1), ord(valueA), getValue(2), ord(valueB), getValue(3), getValue(4), ord(valueD), x
diff --git a/tests/enum/tnamedenumfields.nim b/tests/enum/tnamedenumfields.nim
deleted file mode 100644
index e9ac88a42..000000000
--- a/tests/enum/tnamedenumfields.nim
+++ /dev/null
@@ -1,23 +0,0 @@
-discard """
-  file: "tnamedenumfields.nim"
-  output: "my value A0my value Bconc1valueCabc3abc"
-"""
-
-const
-  strValB = "my value B"
-
-type
-  TMyEnum = enum
-    valueA = (0, "my value A"),
-    valueB = strValB & "conc",
-    valueC,
-    valueD = (3, "abc"),
-    valueE = 4
-
-# trick the optimizer with a variable:
-var x = valueD
-echo valueA, ord(valueA), valueB, ord(valueB), valueC, valueD, ord(valueD), x
-
-
-
-
diff --git a/tests/enum/toptions.nim b/tests/enum/toptions.nim
deleted file mode 100644
index da66f0067..000000000
--- a/tests/enum/toptions.nim
+++ /dev/null
@@ -1,18 +0,0 @@
-
-type
-  # please make sure we have under 32 options (improves code efficiency!)
-  TOption = enum
-    optNone, optForceFullMake, optBoehmGC, optRefcGC, optRangeCheck,
-    optBoundsCheck, optOverflowCheck, optNilCheck, optAssert, optLineDir,
-    optWarns, optHints, optListCmd, optCompileOnly,
-    optSafeCode,             # only allow safe code
-    optStyleCheck, optOptimizeSpeed, optOptimizeSize, optGenDynLib,
-    optGenGuiApp, optStackTrace
-
-  TOptionset = set[TOption]
-
-var
-  gOptions: TOptionset = {optRefcGC, optRangeCheck, optBoundsCheck,
-    optOverflowCheck, optAssert, optWarns, optHints, optLineDir, optStackTrace}
-  compilerArgs: int
-  gExitcode: int8
diff --git a/tests/enum/toverloadable_enums.nim b/tests/enum/toverloadable_enums.nim
new file mode 100644
index 000000000..9bb551467
--- /dev/null
+++ b/tests/enum/toverloadable_enums.nim
@@ -0,0 +1,120 @@
+discard """
+  output: '''B
+0
+E2-B'''
+joinable: false
+"""
+
+{.experimental: "overloadableEnums".}
+
+type
+  E1 = enum
+    value1,
+    value2
+  E2 = enum
+    value1,
+    value2 = 4
+
+const
+  Lookuptable = [
+    E1.value1: "1",
+    value2: "2"
+  ]
+
+when false:
+  const
+    Lookuptable: array[E1, string] = [
+      value1: "1",
+      value2: "2"
+    ]
+
+
+proc p(e: E1): int =
+  # test that the 'case' statement is smart enough:
+  case e
+  of value1: echo "A"
+  of value2: echo "B"
+
+
+let v = p value2 # ERROR: ambiguous!
+# (value2|value2)  nkClosedSymChoice -> nkSym
+
+proc x(p: int) = discard
+proc x(p: string) = discard
+
+proc takeCallback(param: proc(p: int)) = discard
+
+takeCallback x
+
+echo ord v
+
+block: # https://github.com/nim-lang/RFCs/issues/8
+  type
+    Enum1 = enum
+      A, B, C
+    Enum2 = enum
+      A, Z
+
+  proc f(e: Enum1): int = ord(e)
+  proc g(e: Enum2): int = ord(e)
+
+  proc h(e: Enum1): int = ord(e)
+  proc h(e: Enum2): int = ord(e)
+
+  let fA = f(A) # Type of A is well defined
+  let gA = g(A) # Same as above
+
+  let hA1 = h(Enum1.A) # A requires disambiguation
+  let hA2 = h(Enum2.A) # Similarly
+  let hA3 = h(B)
+  let hA4 = h(B)
+  let x = ord(Enum1.A) # Also
+  doAssert fA == 0
+  doAssert gA == 0
+  doAssert hA1 == 0
+  doAssert hA2 == 0
+  doAssert x == 0
+  doAssert hA3 == 1
+  doAssert hA4 == 1
+
+# bug #18769
+proc g3[T](x: T, e: E2): int =
+  case e
+  of value1: echo "E2-A"        # Error: type mismatch: got 'E1' for 'value1' but expected 'E2 = enum'
+  of value2: echo "E2-B"
+
+let v5 = g3(99, E2.value2)
+
+block: # only allow enums to overload enums
+  # mirrors behavior without overloadableEnums
+  proc foo() = discard
+  block:
+    type Foo = enum foo
+    doAssert foo is Foo
+    foo()
+
+import macros
+block: # test with macros/templates
+  type
+    Enum1 = enum
+      value01, value02
+    Enum2 = enum
+      value01, value10
+
+  macro isOneM(a: untyped): bool =
+    result = newCall(bindSym"==", a, ident"value01")
+
+  macro isOneMS(a: untyped): bool =
+    result = newCall(bindSym"==", a, bindSym"value01")
+
+  template isOneT(a: untyped): bool =
+    a == value01
+
+  let e1 = Enum1.value01
+  let e2 = Enum2.value01
+  doAssert isOneM(e1)
+  doAssert isOneM(e2)
+  doAssert isOneMS(e1)
+  doAssert isOneMS(e2)
+  doAssert isOneT(e1)
+  doAssert isOneT(e2)
diff --git a/tests/enum/toverloadedname.nim b/tests/enum/toverloadedname.nim
new file mode 100644
index 000000000..d11b0fb83
--- /dev/null
+++ b/tests/enum/toverloadedname.nim
@@ -0,0 +1,7 @@
+block: # issue #23998
+  type
+    Enum {.pure.} = enum
+      a
+    Obj = object
+      a: Enum
+  proc test(a: Enum) = discard Obj(a: a)
diff --git a/tests/enum/tpure_enums_conflict.nim b/tests/enum/tpure_enums_conflict.nim
new file mode 100644
index 000000000..3cf335440
--- /dev/null
+++ b/tests/enum/tpure_enums_conflict.nim
@@ -0,0 +1,27 @@
+discard """
+  matrix: "-d:testsConciseTypeMismatch"
+"""
+
+# bug #8066
+
+when true:
+  type
+    MyEnum {.pure.} = enum
+      valueA, valueB, valueC, valueD, amb
+
+    OtherEnum {.pure.} = enum
+      valueX, valueY, valueZ, amb
+
+
+  echo valueA # MyEnum.valueA
+  echo MyEnum.amb # OK.
+  echo amb #[tt.Error
+  ^ type mismatch
+Expression: echo amb
+  [1] amb: MyEnum | OtherEnum
+
+Expected one of (first mismatch at [position]):
+[1] proc echo(x: varargs[typed, `$$`])
+  ambiguous identifier: 'amb' -- use one of the following:
+    MyEnum.amb: MyEnum
+    OtherEnum.amb: OtherEnum]#
diff --git a/tests/enum/tpure_enums_conflict_legacy.nim b/tests/enum/tpure_enums_conflict_legacy.nim
new file mode 100644
index 000000000..e592925bc
--- /dev/null
+++ b/tests/enum/tpure_enums_conflict_legacy.nim
@@ -0,0 +1,25 @@
+# bug #8066
+
+when true:
+  type
+    MyEnum {.pure.} = enum
+      valueA, valueB, valueC, valueD, amb
+
+    OtherEnum {.pure.} = enum
+      valueX, valueY, valueZ, amb
+
+
+  echo valueA # MyEnum.valueA
+  echo MyEnum.amb # OK.
+  echo amb #[tt.Error
+  ^ type mismatch: got <MyEnum | OtherEnum>
+but expected one of:
+proc echo(x: varargs[typed, `$$`])
+  first type mismatch at position: 1
+  required type for x: varargs[typed]
+  but expression 'amb' is of type: None
+  ambiguous identifier: 'amb' -- use one of the following:
+    MyEnum.amb: MyEnum
+    OtherEnum.amb: OtherEnum
+
+expression: echo amb]#
diff --git a/tests/enum/tredefinition.nim b/tests/enum/tredefinition.nim
new file mode 100644
index 000000000..615ca6b1c
--- /dev/null
+++ b/tests/enum/tredefinition.nim
@@ -0,0 +1,9 @@
+discard """
+  cmd: '''nim check --hints:off $file'''
+  action: reject
+nimout: '''
+tredefinition.nim(9, 25) Error: redefinition of 'Key_a'; previous declaration here: tredefinition.nim(9, 18)
+'''
+"""
+
+type Key* = enum Key_A, Key_a
\ No newline at end of file
diff --git a/tests/enum/ttypenameconflict.nim b/tests/enum/ttypenameconflict.nim
new file mode 100644
index 000000000..b13bf00ce
--- /dev/null
+++ b/tests/enum/ttypenameconflict.nim
@@ -0,0 +1,13 @@
+# issue #23689
+
+type
+  MyEnum {.pure.} = enum
+    A, B, C, D
+
+  B = object
+    field: int
+
+let x: MyEnum = B
+doAssert $x == "B"
+doAssert typeof(x) is MyEnum
+doAssert x in {A, B}
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/mambparam1.nim b/tests/errmsgs/mambparam1.nim
new file mode 100644
index 000000000..1a5133c3c
--- /dev/null
+++ b/tests/errmsgs/mambparam1.nim
@@ -0,0 +1 @@
+const test* = "foo"
diff --git a/tests/errmsgs/mambparam2.nim b/tests/errmsgs/mambparam2.nim
new file mode 100644
index 000000000..073e3f8c8
--- /dev/null
+++ b/tests/errmsgs/mambparam2.nim
@@ -0,0 +1,2 @@
+import mambparam1
+export test
diff --git a/tests/errmsgs/mambparam3.nim b/tests/errmsgs/mambparam3.nim
new file mode 100644
index 000000000..5469244e2
--- /dev/null
+++ b/tests/errmsgs/mambparam3.nim
@@ -0,0 +1 @@
+const test* = "bar"
diff --git a/tests/clearmsg/mb.nim b/tests/errmsgs/mb.nim
index 2d21e2396..2d21e2396 100644
--- a/tests/clearmsg/mb.nim
+++ b/tests/errmsgs/mb.nim
diff --git a/tests/clearmsg/mc.nim b/tests/errmsgs/mc.nim
index 79d7431df..79d7431df 100644
--- a/tests/clearmsg/mc.nim
+++ b/tests/errmsgs/mc.nim
diff --git a/tests/errmsgs/t10251.nim b/tests/errmsgs/t10251.nim
new file mode 100644
index 000000000..19adf02eb
--- /dev/null
+++ b/tests/errmsgs/t10251.nim
@@ -0,0 +1,19 @@
+discard """
+  action:reject
+  cmd: "nim check $options $file"
+  nimout: '''
+t10251.nim(19, 23) Error: redefinition of 'goo1'; previous declaration here: t10251.nim(19, 11)
+'''
+"""
+
+# line 10
+type
+  Enum1 = enum
+    foo, bar, baz
+  Enum2 = enum
+    foo, bar, baz
+
+
+type
+  Enum3 {.pure.} = enum # fixed (by accident?) in https://github.com/nim-lang/Nim/pull/18263
+    goo0, goo1, goo2, goo1
diff --git a/tests/errmsgs/t10376.nim b/tests/errmsgs/t10376.nim
new file mode 100644
index 000000000..814c860dc
--- /dev/null
+++ b/tests/errmsgs/t10376.nim
@@ -0,0 +1,32 @@
+discard """
+  matrix: "--mm:refc"
+  errormsg: "finalizer must be a direct reference to a proc"
+  line: 30
+"""
+
+type
+  A = ref object
+
+proc my_callback(a: A) {. nimcall .} =
+  discard
+
+proc foo(callback: proc(a: A) {. nimcall .}) =
+  var x1: A
+  new(x1, proc (x: A) {.nimcall.} = discard)
+  var x2: A
+  new(x2, func (x: A) {.nimcall.} = discard)
+
+  var x3: A
+  proc foo1(a: A) {.nimcall.} = discard
+  new(x3, foo1)
+  var x4: A
+  func foo2(a: A) {.nimcall.} = discard
+  new(x4, foo2)
+
+  var x5: A
+  new(x5, my_callback)
+
+  var x6: A
+  new(x6, callback)
+
+foo(my_callback)
diff --git a/tests/errmsgs/t10489_a.nim b/tests/errmsgs/t10489_a.nim
new file mode 100644
index 000000000..c762ce876
--- /dev/null
+++ b/tests/errmsgs/t10489_a.nim
@@ -0,0 +1,9 @@
+discard """
+errormsg: "invalid type: 'macro (body: untyped): untyped{.noSideEffect, gcsafe.}' for let. Did you mean to call the macro with '()'?"
+line: 9
+"""
+
+macro m(body: untyped): untyped =
+  body
+
+let x1 = m
diff --git a/tests/errmsgs/t10489_b.nim b/tests/errmsgs/t10489_b.nim
new file mode 100644
index 000000000..df05f0e6e
--- /dev/null
+++ b/tests/errmsgs/t10489_b.nim
@@ -0,0 +1,9 @@
+discard """
+errormsg: "invalid type: 'macro (body: untyped): untyped{.noSideEffect, gcsafe.}' for const. Did you mean to call the macro with '()'?"
+line: 9
+"""
+
+macro m(body: untyped): untyped =
+  body
+
+const x2 = m
diff --git a/tests/errmsgs/t10542.nim b/tests/errmsgs/t10542.nim
new file mode 100644
index 000000000..b57dbf18b
--- /dev/null
+++ b/tests/errmsgs/t10542.nim
@@ -0,0 +1,24 @@
+discard """
+  matrix: "--hintaserror:ConvFromXtoItselfNotNeeded"
+"""
+
+# bug #10542
+
+proc f(args: varargs[string, string], length: int) =
+  doAssert args.len == length
+
+# main use case that requires type conversion (no warning here)
+f("a", "b", 2)
+f("a", 1)
+
+
+proc m(args: varargs[cstring, cstring]) =
+  doAssert args.len == 2
+
+# main use case that requires type conversion (no warning here)
+m("a", "b")
+
+# if an argument already is cstring there's a warning
+let x: cstring = "x"
+m("a", x)
+m(x, "a")
\ No newline at end of file
diff --git a/tests/errmsgs/t10594.nim b/tests/errmsgs/t10594.nim
new file mode 100644
index 000000000..d27dd6433
--- /dev/null
+++ b/tests/errmsgs/t10594.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "expression has no address"
+  line: 7
+"""
+
+template foo(v: varargs[int]) = addr v 
+foo(1, 2)
diff --git a/tests/errmsgs/t10734.nim b/tests/errmsgs/t10734.nim
new file mode 100644
index 000000000..4e73db7cd
--- /dev/null
+++ b/tests/errmsgs/t10734.nim
@@ -0,0 +1,20 @@
+discard """
+  cmd: "nim check $file"
+  errormsg: ""
+  nimout: '''
+t10734.nim(19, 1) Error: invalid indentation
+t10734.nim(19, 6) Error: invalid indentation
+t10734.nim(20, 7) Error: expression expected, but found '[EOF]'
+t10734.nim(18, 5) Error: 'proc' is not a concrete type; for a callback without parameters use 'proc()'
+t10734.nim(19, 6) Error: undeclared identifier: 'p'
+t10734.nim(19, 6) Error: expression 'p' has no type (or is ambiguous)
+t10734.nim(19, 6) Error: 'p' cannot be assigned to
+t10734.nim(17, 3) Hint: 'T' is declared but not used [XDeclaredButNotUsed]
+'''
+"""
+
+type
+  T = object
+    a:
+proc p =
+  case
\ No newline at end of file
diff --git a/tests/errmsgs/t10735.nim b/tests/errmsgs/t10735.nim
new file mode 100644
index 000000000..a39cd196e
--- /dev/null
+++ b/tests/errmsgs/t10735.nim
@@ -0,0 +1,68 @@
+discard """
+  cmd: "nim check $file"
+  errormsg: "illformed AST: case buf[pos]"
+  nimout: '''
+t10735.nim(65, 5) Error: 'let' symbol requires an initialization
+t10735.nim(66, 10) Error: undeclared identifier: 'pos'
+t10735.nim(66, 10) Error: expression 'pos' has no type (or is ambiguous)
+t10735.nim(66, 10) Error: expression 'pos' has no type (or is ambiguous)
+t10735.nim(66, 9) Error: type mismatch: got <cstring, >
+but expected one of:
+proc `[]`(s: string; i: BackwardsIndex): char
+  first type mismatch at position: 1
+  required type for s: string
+  but expression 'buf' is of type: cstring
+proc `[]`(s: var string; i: BackwardsIndex): var char
+  first type mismatch at position: 1
+  required type for s: var string
+  but expression 'buf' is of type: cstring
+proc `[]`[I: Ordinal; T](a: T; i: I): T
+  first type mismatch at position: 0
+proc `[]`[Idx, T; U, V: Ordinal](a: array[Idx, T]; x: HSlice[U, V]): seq[T]
+  first type mismatch at position: 1
+  required type for a: array[Idx, T]
+  but expression 'buf' is of type: cstring
+proc `[]`[Idx, T](a: array[Idx, T]; i: BackwardsIndex): T
+  first type mismatch at position: 1
+  required type for a: array[Idx, T]
+  but expression 'buf' is of type: cstring
+proc `[]`[Idx, T](a: var array[Idx, T]; i: BackwardsIndex): var T
+  first type mismatch at position: 1
+  required type for a: var array[Idx, T]
+  but expression 'buf' is of type: cstring
+proc `[]`[T, U: Ordinal](s: string; x: HSlice[T, U]): string
+  first type mismatch at position: 1
+  required type for s: string
+  but expression 'buf' is of type: cstring
+proc `[]`[T; U, V: Ordinal](s: openArray[T]; x: HSlice[U, V]): seq[T]
+  first type mismatch at position: 1
+  required type for s: openArray[T]
+  but expression 'buf' is of type: cstring
+proc `[]`[T](s: openArray[T]; i: BackwardsIndex): T
+  first type mismatch at position: 1
+  required type for s: openArray[T]
+  but expression 'buf' is of type: cstring
+proc `[]`[T](s: var openArray[T]; i: BackwardsIndex): var T
+  first type mismatch at position: 1
+  required type for s: var openArray[T]
+  but expression 'buf' is of type: cstring
+template `[]`(a: WideCStringObj; idx: int): Utf16Char
+  first type mismatch at position: 1
+  required type for a: WideCStringObj
+  but expression 'buf' is of type: cstring
+template `[]`(s: string; i: int): char
+  first type mismatch at position: 1
+  required type for s: string
+  but expression 'buf' is of type: cstring
+
+expression: buf[pos]
+t10735.nim(66, 9) Error: expression '' has no type (or is ambiguous)
+t10735.nim(68, 3) Error: illformed AST: case buf[pos]
+'''
+  joinable: false
+"""
+
+let buf: cstring
+case buf[pos]
+else:
+  case buf[pos]
diff --git a/tests/errmsgs/t1154.nim b/tests/errmsgs/t1154.nim
index 7fcbf8a27..fee9d0ad6 100644
--- a/tests/errmsgs/t1154.nim
+++ b/tests/errmsgs/t1154.nim
@@ -1,11 +1,11 @@
 discard """
-errormsg: "invalid type: 'expr' in this context: 'proc (a: varargs[expr])' for proc"
+errormsg: "invalid type: 'untyped' in this context: 'proc (a: varargs[untyped])' for proc"
 line: 8
 """
 
 import typetraits
 
-proc foo(a:varargs[expr]) =
+proc foo(a:varargs[untyped]) =
   echo a[0].type.name
 
 foo(1)
diff --git a/tests/errmsgs/t12844.nim b/tests/errmsgs/t12844.nim
new file mode 100644
index 000000000..a274b72b4
--- /dev/null
+++ b/tests/errmsgs/t12844.nim
@@ -0,0 +1,13 @@
+discard """
+cmd: "nim check $file"
+errormsg: "invalid type: 'template (args: varargs[string])' for var. Did you mean to call the template with '()'?"
+nimout: '''
+t12844.nim(11, 7) Error: invalid type: 'template (args: varargs[string])' for const. Did you mean to call the template with '()'?
+t12844.nim(12, 5) Error: invalid type: 'template (args: varargs[string])' for var. Did you mean to call the template with '()'?'''
+"""
+
+template z*(args: varargs[string, `$`]) =
+  discard
+const x = z
+var y = z
+
diff --git a/tests/errmsgs/t14444.nim b/tests/errmsgs/t14444.nim
new file mode 100644
index 000000000..27365236e
--- /dev/null
+++ b/tests/errmsgs/t14444.nim
@@ -0,0 +1,14 @@
+discard """
+  matrix: "--hints:off"
+  exitcode: "1"
+  output: '''
+t14444.nim(13)           t14444
+fatal.nim(53)            sysFatal
+Error: unhandled exception: index out of bounds, the container is empty [IndexDefect]
+'''
+"""
+
+when true: # bug #14444
+  var i: string
+  i[10] = 'j'
+  echo i
\ No newline at end of file
diff --git a/tests/errmsgs/t16654.nim b/tests/errmsgs/t16654.nim
new file mode 100644
index 000000000..b2b57619b
--- /dev/null
+++ b/tests/errmsgs/t16654.nim
@@ -0,0 +1,12 @@
+discard """
+  cmd: "nim check $options $file"
+  errormsg: "type mismatch: got <int literal(1), proc (r: GenericParam): auto>"
+"""
+
+when true: # bug #16654
+  func fn[T](a: T, op: proc(a: T): float) = discard
+  proc main() =
+    let v = 1
+    proc bar(r: auto): auto = v
+    fn(1, bar)
+  main()
diff --git a/tests/errmsgs/t17460.nim b/tests/errmsgs/t17460.nim
new file mode 100644
index 000000000..bb8e21198
--- /dev/null
+++ b/tests/errmsgs/t17460.nim
@@ -0,0 +1,19 @@
+discard """
+  cmd: "nim check $options $file"
+  errormsg: "tuple expected for tuple unpacking, but got 'array[0..2, int]'"
+"""
+
+iterator xclusters*[T](a: openArray[T]; s: static[int]): array[s, T] {.inline.} =
+  var result: array[s, T] # iterators have no default result variable
+  var i = 0
+  while i < len(a):
+    for j, x in mpairs(result):
+      x = a[(i + j) mod len(a)]
+    yield result
+    inc(i)
+
+proc m =
+  for (i, j, k) in xclusters([1, 2, 3, 4, 5], 3):
+    echo i, j, k
+
+m()
diff --git a/tests/errmsgs/t18327.nim b/tests/errmsgs/t18327.nim
new file mode 100644
index 000000000..686a1bd0c
--- /dev/null
+++ b/tests/errmsgs/t18327.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "parameter 'n' requires a type"
+"""
+
+proc fn3(n) = discard
\ No newline at end of file
diff --git a/tests/errmsgs/t18886.nim b/tests/errmsgs/t18886.nim
new file mode 100644
index 000000000..8ed160c64
--- /dev/null
+++ b/tests/errmsgs/t18886.nim
@@ -0,0 +1,18 @@
+discard """
+  cmd: "nim check --hints:off $file"
+  errormsg: ""
+  nimout: '''
+t18886.nim(18, 24) Error: ambiguous identifier: 'bar' -- you need a helper proc to disambiguate the following:
+  t18886.bar: proc (i: ptr int){.noSideEffect, gcsafe.}
+  t18886.bar: proc (i: ptr char){.noSideEffect, gcsafe.}
+'''
+"""
+
+type Foo = (proc(_: pointer): void)
+
+
+proc bar(i: ptr[int]) = discard
+proc bar(i: ptr[char]) = discard
+
+
+let fooBar = cast[Foo](bar)
\ No newline at end of file
diff --git a/tests/errmsgs/t18983.nim b/tests/errmsgs/t18983.nim
new file mode 100644
index 000000000..3451875cb
--- /dev/null
+++ b/tests/errmsgs/t18983.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "illegal recursion in type 'A'"
+"""
+
+type
+  A* = A
+  B = (A,)
diff --git a/tests/errmsgs/t19224.nim b/tests/errmsgs/t19224.nim
new file mode 100644
index 000000000..7a9ecb2e7
--- /dev/null
+++ b/tests/errmsgs/t19224.nim
@@ -0,0 +1,12 @@
+discard """
+cmd: "nim check --hints:off $file"
+errormsg: ""
+nimout: '''
+t19224.nim(10, 10) Error: cannot infer element type of items([])
+t19224.nim(12, 10) Error: cannot infer element type of items(@[])
+'''
+"""
+
+for _ in []: discard
+
+for _ in @[]: discard
diff --git a/tests/errmsgs/t19882.nim b/tests/errmsgs/t19882.nim
new file mode 100644
index 000000000..1f2f95ab7
--- /dev/null
+++ b/tests/errmsgs/t19882.nim
@@ -0,0 +1,10 @@
+
+discard """
+  errormsg: "cannot instantiate 'A[T, P]' inside of type definition: 'init'; Maybe generic arguments are missing?"
+"""
+type A[T,P] = object
+  b:T
+  c:P
+proc init(): ref A =      
+  new(result)
+var a = init()
diff --git a/tests/errmsgs/t19882_2.nim b/tests/errmsgs/t19882_2.nim
new file mode 100644
index 000000000..7f3055a5d
--- /dev/null
+++ b/tests/errmsgs/t19882_2.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "cannot instantiate: 'A[T]'; the object's generic parameters cannot be inferred and must be explicitly given"
+"""
+type A[T] = object
+var a = A()
\ No newline at end of file
diff --git a/tests/errmsgs/t21257.nim b/tests/errmsgs/t21257.nim
new file mode 100644
index 000000000..eecdb1dfe
--- /dev/null
+++ b/tests/errmsgs/t21257.nim
@@ -0,0 +1,20 @@
+discard """
+  action: compile
+  cmd: "nim check $file" 
+"""
+
+type AC_WINCTRL_Fields* = distinct uint8
+
+type AC_STATUSA_WSTATE0* {.pure.} = enum
+  ABOVE = 0x0,
+  INSIDE = 0x1,
+  BELOW = 0x2,
+
+type AC_WINCTRL_WINTSEL0* {.pure.} = enum
+  ABOVE = 0x0,
+  INSIDE = 0x1,
+  BELOW = 0x2,
+  OUTSIDE = 0x3,
+
+proc write*(WINTSEL0: AC_WINCTRL_WINTSEL0 = ABOVE) =
+  discard
diff --git a/tests/errmsgs/t22097.nim b/tests/errmsgs/t22097.nim
new file mode 100644
index 000000000..bb24ee8d3
--- /dev/null
+++ b/tests/errmsgs/t22097.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "type mismatch: got <uint8>"
+"""
+
+proc toUInt16(x: var uint16) =
+  discard
+
+var x = uint8(1)
+toUInt16 x
diff --git a/tests/errmsgs/t22284.nim b/tests/errmsgs/t22284.nim
new file mode 100644
index 000000000..827155e6b
--- /dev/null
+++ b/tests/errmsgs/t22284.nim
@@ -0,0 +1,25 @@
+discard """
+  errormsg: "j(uRef, proc (config: F; sources: auto) {.raises: [].} = discard ) can raise an unlisted exception: Exception"
+"""
+
+import std/macros
+
+macro h(): untyped =
+  result = newTree(nnkStmtList)
+  result.add quote do:
+    new int
+
+type F = object
+
+proc j[SecondarySources](
+    uRef: ref SecondarySources,
+    u: proc (config: F, sources: ref SecondarySources)): F =
+  u(result, uRef)
+
+template programMain(body: untyped) =
+  proc main {.raises: [].} = body  # doesn't SIGSEGV without this {.raises: [].}
+  main()
+
+programMain:
+  var uRef = h()
+  discard j(uRef, u = proc(config: F, sources: auto) {.raises: [].} = discard)
\ No newline at end of file
diff --git a/tests/errmsgs/t22753.nim b/tests/errmsgs/t22753.nim
new file mode 100644
index 000000000..8a504109a
--- /dev/null
+++ b/tests/errmsgs/t22753.nim
@@ -0,0 +1,52 @@
+discard """
+cmd: "nim check --hints:off $file"
+errormsg: "type mismatch"
+nimoutFull: true
+nimout: '''
+t22753.nim(51, 13) Error: array expects two type parameters
+t22753.nim(52, 1) Error: expression 'x' has no type (or is ambiguous)
+t22753.nim(52, 1) Error: expression 'x' has no type (or is ambiguous)
+t22753.nim(52, 2) Error: type mismatch: got <>
+but expected one of:
+proc `[]=`(s: var string; i: BackwardsIndex; x: char)
+  first type mismatch at position: 2
+  required type for i: BackwardsIndex
+  but expression '0' is of type: int literal(0)
+proc `[]=`[I: Ordinal; T, S](a: T; i: I; x: sink S)
+  first type mismatch at position: 0
+proc `[]=`[Idx, T; U, V: Ordinal](a: var array[Idx, T]; x: HSlice[U, V];
+                                  b: openArray[T])
+  first type mismatch at position: 2
+  required type for x: HSlice[[]=.U, []=.V]
+  but expression '0' is of type: int literal(0)
+proc `[]=`[Idx, T](a: var array[Idx, T]; i: BackwardsIndex; x: T)
+  first type mismatch at position: 2
+  required type for i: BackwardsIndex
+  but expression '0' is of type: int literal(0)
+proc `[]=`[T, U: Ordinal](s: var string; x: HSlice[T, U]; b: string)
+  first type mismatch at position: 2
+  required type for x: HSlice[[]=.T, []=.U]
+  but expression '0' is of type: int literal(0)
+proc `[]=`[T; U, V: Ordinal](s: var seq[T]; x: HSlice[U, V]; b: openArray[T])
+  first type mismatch at position: 2
+  required type for x: HSlice[[]=.U, []=.V]
+  but expression '0' is of type: int literal(0)
+proc `[]=`[T](s: var openArray[T]; i: BackwardsIndex; x: T)
+  first type mismatch at position: 2
+  required type for i: BackwardsIndex
+  but expression '0' is of type: int literal(0)
+template `[]=`(a: WideCStringObj; idx: int; val: Utf16Char)
+  first type mismatch at position: 3
+  required type for val: Utf16Char
+  but expression '9' is of type: int literal(9)
+template `[]=`(s: string; i: int; val: char)
+  first type mismatch at position: 3
+  required type for val: char
+  but expression '9' is of type: int literal(9)
+
+expression: x[0] = 9
+'''
+"""
+
+var x: array[3] # bug #22753
+x[0] = 9
diff --git a/tests/errmsgs/t22852.nim b/tests/errmsgs/t22852.nim
new file mode 100644
index 000000000..7c352a49c
--- /dev/null
+++ b/tests/errmsgs/t22852.nim
@@ -0,0 +1,9 @@
+discard """
+  exitcode: 1
+  outputsub: '''
+Error: unhandled exception: value out of range: -2 notin 0 .. 9223372036854775807 [RangeDefect]
+'''
+"""
+
+# bug #22852
+echo [0][2..^2]
diff --git a/tests/errmsgs/t22996.nim b/tests/errmsgs/t22996.nim
new file mode 100644
index 000000000..3a51fd8b0
--- /dev/null
+++ b/tests/errmsgs/t22996.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "invalid type: 'typedesc[string]' for const"
+"""
+
+# bug #22996
+type MyObject = ref object
+  _ = string
diff --git a/tests/errmsgs/t23060.nim b/tests/errmsgs/t23060.nim
new file mode 100644
index 000000000..abb79bcc3
--- /dev/null
+++ b/tests/errmsgs/t23060.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "undeclared identifier: '♔♕♖♗♘♙'"
+"""
+
+♔♕♖♗♘♙ = 1
\ No newline at end of file
diff --git a/tests/errmsgs/t23419.nim b/tests/errmsgs/t23419.nim
new file mode 100644
index 000000000..59a72f081
--- /dev/null
+++ b/tests/errmsgs/t23419.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "invalid type: 'void' in this context: '(array[0..-1, void],)' for var"
+"""
+
+var a: (array[0, void], )
diff --git a/tests/errmsgs/t23435.nim b/tests/errmsgs/t23435.nim
new file mode 100644
index 000000000..5e2e4c82a
--- /dev/null
+++ b/tests/errmsgs/t23435.nim
@@ -0,0 +1,12 @@
+discard """
+  outputsub: "Error: unhandled exception: value out of range: -15 notin 0 .. 9223372036854775807 [RangeDefect]"
+  exitcode: "1"
+"""
+
+# bug #23435
+proc foo() =
+  for _ in @[1, 3, 5]:
+    discard "abcde"[25..<10]
+    break
+
+foo()
diff --git a/tests/errmsgs/t23536.nim b/tests/errmsgs/t23536.nim
new file mode 100644
index 000000000..610a85bab
--- /dev/null
+++ b/tests/errmsgs/t23536.nim
@@ -0,0 +1,26 @@
+discard """
+  matrix: "--stackTrace:on --excessiveStackTrace:off"
+"""
+
+const expected = """
+wrong trace:
+t23536.nim(22)           t23536
+t23536.nim(17)           foo
+assertions.nim(41)       failedAssertImpl
+assertions.nim(36)       raiseAssert
+fatal.nim(53)            sysFatal
+"""
+
+
+try:
+  proc foo = # bug #23536
+    doAssert false
+
+  for i in 0 .. 1:
+
+
+    foo()
+except AssertionDefect:
+  let e = getCurrentException()
+  let trace = e.getStackTrace
+  doAssert "wrong trace:\n" & trace == expected
diff --git a/tests/errmsgs/t2614.nim b/tests/errmsgs/t2614.nim
new file mode 100644
index 000000000..031ecb9d1
--- /dev/null
+++ b/tests/errmsgs/t2614.nim
@@ -0,0 +1,21 @@
+discard """
+  cmd: "nim check $options --hints:off $file"
+  errormsg: ""
+  nimout: '''
+t2614.nim(19, 27) Error: type mismatch: got <array[0..1, proc ()]> but expected 'array[0..1, proc (){.closure.}]'
+  Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'.
+t2614.nim(21, 22) Error: type mismatch: got <seq[proc ()]> but expected 'seq[proc (){.closure.}]'
+  Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'.
+'''
+"""
+
+proc g
+proc f =
+  if false: g()
+proc g =
+  if false: f()
+
+var a = [f, g] # This works
+var b: array[2, proc()] = [f, g] # Error
+
+var c: seq[proc()] = @[f, g] 
\ No newline at end of file
diff --git a/tests/errmsgs/t5167_4.nim b/tests/errmsgs/t5167_4.nim
index 7a263622b..dafd7754d 100644
--- a/tests/errmsgs/t5167_4.nim
+++ b/tests/errmsgs/t5167_4.nim
@@ -1,5 +1,5 @@
 discard """
-errormsg: "type mismatch: got <proc [*missing parameters*](x: int) | proc (x: string){.gcsafe, locks: 0.}>"
+errormsg: "type mismatch: got <proc [*missing parameters*](x: int) | proc (x: string){.gcsafe.}>"
 line: 19
 """
 
diff --git a/tests/errmsgs/t5167_5.nim b/tests/errmsgs/t5167_5.nim
index ab02f29f6..dea7e40b3 100644
--- a/tests/errmsgs/t5167_5.nim
+++ b/tests/errmsgs/t5167_5.nim
@@ -1,25 +1,25 @@
 discard """
-cmd: "nim check $file"
-errormsg: "'m' has unspecified generic parameters"
-nimout: '''
-t5167_5.nim(20, 9) Error: 't' has unspecified generic parameters
-t5167_5.nim(21, 5) Error: 't' has unspecified generic parameters
-t5167_5.nim(23, 9) Error: 'm' has unspecified generic parameters
-t5167_5.nim(24, 5) Error: 'm' has unspecified generic parameters
-'''
+cmd: "nim check --mm:refc $file"
 """
+# issue #11942
+discard newSeq[system]() #[tt.Error
+               ^ expression 'system' has no type (or is ambiguous)]#
 
+# issue #5167
 template t[B]() =
   echo "foo1"
 
-macro m[T]: stmt = nil
+macro m[T]: untyped = nil
 
 proc bar(x: proc (x: int)) =
   echo "bar"
 
-let x = t
-bar t
-
-let y = m
-bar m
+let x = t #[tt.Error
+        ^ 't' has unspecified generic parameters]#
+bar t #[tt.Error
+^ type mismatch: got <template [*missing parameters*]()>]#
 
+let y = m #[tt.Error
+        ^ 'm' has unspecified generic parameters]#
+bar m #[tt.Error
+^ type mismatch: got <macro [*missing parameters*](): untyped{.noSideEffect, gcsafe.}>]#
diff --git a/tests/errmsgs/t5282.nim b/tests/errmsgs/t5282.nim
new file mode 100644
index 000000000..4da49d155
--- /dev/null
+++ b/tests/errmsgs/t5282.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "illegal recursion in type 'x'"
+"""
+
+type x = distinct x
diff --git a/tests/errmsgs/t5870.nim b/tests/errmsgs/t5870.nim
new file mode 100644
index 000000000..96966bcd7
--- /dev/null
+++ b/tests/errmsgs/t5870.nim
@@ -0,0 +1,17 @@
+discard """
+errormsg: "invalid type: 'SomeRefObj' in this context: 'seq[SomeRefObj]' for const"
+line: 14
+"""
+
+# bug #5870
+type SomeRefObj = ref object of RootObj
+    someIntMember: int
+
+proc createSomeRefObj(v: int): SomeRefObj=
+    result.new()
+    result.someIntMember = v
+
+const compileTimeSeqOfRefObjs = @[createSomeRefObj(100500), createSomeRefObj(2)]
+
+for i in 0..1:
+  echo compileTimeSeqOfRefObjs[i].someIntMember
diff --git a/tests/errmsgs/t6281.nim b/tests/errmsgs/t6281.nim
new file mode 100644
index 000000000..59fda5077
--- /dev/null
+++ b/tests/errmsgs/t6281.nim
@@ -0,0 +1,9 @@
+discard """
+errormsg: "invalid type: 'SomeNumber' in this context: 'seq[SomeNumber]' for var"
+line: 6
+"""
+
+var seqwat: seq[SomeNumber] = @[]
+
+proc foo(x: SomeNumber) =
+  seqwat.add(x)
\ No newline at end of file
diff --git a/tests/errmsgs/t6483.nim b/tests/errmsgs/t6483.nim
new file mode 100644
index 000000000..0e977b36d
--- /dev/null
+++ b/tests/errmsgs/t6483.nim
@@ -0,0 +1,31 @@
+discard """
+  errormsg: "request to generate code for .compileTime proc: newSeq"
+  line: 21
+"""
+
+type
+  VarItem = object
+    onode: NimNode
+    nnode: NimNode
+    suffix: string
+
+  VarState = object
+    scopes: seq[VarScope]
+
+  VarScope = object
+    variables: seq[VarItem]
+    children: seq[VarScope]
+
+when true:
+  var scope1 = VarScope(
+    variables: newSeq[VarItem](),
+    children: newSeq[VarScope]()
+  )
+  var scope2 = VarScope(
+    variables: newSeq[VarItem](),
+    children: newSeq[VarScope]()
+  )
+  var state = VarState(scopes: newSeq[VarScope]())
+  state.scopes.add(scope1)
+  state.scopes[0].children.add(scope2)
+  echo($state.scopes)
\ No newline at end of file
diff --git a/tests/errmsgs/t6499.nim b/tests/errmsgs/t6499.nim
new file mode 100644
index 000000000..25e78fdbb
--- /dev/null
+++ b/tests/errmsgs/t6499.nim
@@ -0,0 +1,6 @@
+discard """
+  errormsg: "'chr' is a built-in and cannot be used as a first-class procedure"
+"""
+
+# bug #6499
+let x = (chr, 0)
diff --git a/tests/errmsgs/t6608.nim b/tests/errmsgs/t6608.nim
new file mode 100644
index 000000000..88cbf42fd
--- /dev/null
+++ b/tests/errmsgs/t6608.nim
@@ -0,0 +1,15 @@
+discard """
+  cmd: "nim c --hints:off $file"
+  errormsg: "type mismatch: got <>"
+  nimout: '''t6608.nim(13, 4) Error: type mismatch: got <>
+but expected one of:
+AcceptCB = proc (s: string){.closure.}'''
+"""
+
+type
+  AcceptCB = proc (s: string)
+
+proc x(x: AcceptCB) =
+  x()
+
+x()
diff --git a/tests/errmsgs/t8064.nim b/tests/errmsgs/t8064.nim
new file mode 100644
index 000000000..c35a3abcc
--- /dev/null
+++ b/tests/errmsgs/t8064.nim
@@ -0,0 +1,9 @@
+import tables
+
+values
+
+
+discard """
+  # either this or "expression has no type":
+  errormsg: "ambiguous identifier: 'values' -- use one of the following:"
+"""
diff --git a/tests/errmsgs/t8339.nim b/tests/errmsgs/t8339.nim
new file mode 100644
index 000000000..720e080c0
--- /dev/null
+++ b/tests/errmsgs/t8339.nim
@@ -0,0 +1,8 @@
+discard """
+  errormsg: "type mismatch: got <seq[int]> but expected 'seq[float]'"
+  line: 8
+"""
+
+import sequtils
+
+var x: seq[float] = @[1].mapIt(it)
diff --git a/tests/errmsgs/t8434.nim b/tests/errmsgs/t8434.nim
new file mode 100644
index 000000000..5d962ee5c
--- /dev/null
+++ b/tests/errmsgs/t8434.nim
@@ -0,0 +1,15 @@
+discard """
+  errormsg: "type mismatch: got <byte, int literal(0)>"
+  nimout: '''but expected one of:
+proc fun0[T1: int | float | object | array | seq](a1: T1; a2: int)
+  first type mismatch at position: 1
+  required type for a1: T1: int or float or object or array or seq
+  but expression 'byte(1)' is of type: byte
+
+expression: fun0(byte(1), 0)
+'''
+"""
+
+proc fun0[T1:int|float|object|array|seq](a1:T1, a2:int)=discard
+
+fun0(byte(1), 0)
diff --git a/tests/errmsgs/t8610.nim b/tests/errmsgs/t8610.nim
new file mode 100644
index 000000000..ec99beae5
--- /dev/null
+++ b/tests/errmsgs/t8610.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "invalid type: 'typedesc[int]' for const"
+"""
+## bug #8610
+const Foo = int
diff --git a/tests/errmsgs/t8794.nim b/tests/errmsgs/t8794.nim
new file mode 100644
index 000000000..36f05dbad
--- /dev/null
+++ b/tests/errmsgs/t8794.nim
@@ -0,0 +1,23 @@
+discard """
+  cmd: "nim check $options $file"
+"""
+
+## issue #8794
+
+import m8794
+
+type Foo = object
+  a1: int
+
+discard Foo().a2 #[tt.Error
+             ^ undeclared field: 'a2' for type t8794.Foo [type declared in t8794.nim(9, 6)]]#
+
+type Foo3b = Foo3
+var x2: Foo3b
+
+proc getFun[T](): T =
+  var a: T
+  a
+
+discard getFun[type(x2)]().a3 #[tt.Error
+                          ^ undeclared field: 'a3' for type m8794.Foo3 [type declared in m8794.nim(1, 6)]]#
diff --git a/tests/errmsgs/t9768.nim b/tests/errmsgs/t9768.nim
new file mode 100644
index 000000000..b5ff58367
--- /dev/null
+++ b/tests/errmsgs/t9768.nim
@@ -0,0 +1,31 @@
+discard """
+  errormsg: "unhandled exception: t9768.nim(24, 3) `a < 4`  [AssertionDefect]"
+  file: "std/assertions.nim"
+  matrix: "-d:nimPreviewSlimSystem"
+  nimout: '''
+stack trace: (most recent call last)
+t9768.nim(29, 33)        main
+t9768.nim(24, 11)        foo1
+'''
+"""
+import std/assertions
+
+
+
+
+
+
+
+
+
+## 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/t9908_01.nim b/tests/errmsgs/t9908_01.nim
new file mode 100644
index 000000000..99bc8237d
--- /dev/null
+++ b/tests/errmsgs/t9908_01.nim
@@ -0,0 +1,10 @@
+discard """
+errormsg: "ordinal type expected; given: string"
+line: 10
+"""
+
+# https://github.com/nim-lang/Nim/issues/9908
+
+type
+  X = enum
+    a = ("a", "b")
diff --git a/tests/errmsgs/t9908_02.nim b/tests/errmsgs/t9908_02.nim
new file mode 100644
index 000000000..4fc60b3df
--- /dev/null
+++ b/tests/errmsgs/t9908_02.nim
@@ -0,0 +1,10 @@
+discard """
+errormsg: "ordinal type expected; given: float"
+line: 10
+"""
+
+# https://github.com/nim-lang/Nim/pull/9909#issuecomment-445519287
+
+type
+  E = enum
+    myVal = 80.9
diff --git a/tests/clearmsg/ta.nim b/tests/errmsgs/ta.nim
index 31baae773..31baae773 100644
--- a/tests/clearmsg/ta.nim
+++ b/tests/errmsgs/ta.nim
diff --git a/tests/errmsgs/tambparam.nim b/tests/errmsgs/tambparam.nim
new file mode 100644
index 000000000..5b56a3fce
--- /dev/null
+++ b/tests/errmsgs/tambparam.nim
@@ -0,0 +1,16 @@
+discard """
+  matrix: "-d:testsConciseTypeMismatch"
+"""
+
+import mambparam2, mambparam3
+
+echo test #[tt.Error
+^ type mismatch
+Expression: echo test
+  [1] test: string | string
+
+Expected one of (first mismatch at [position]):
+[1] proc echo(x: varargs[typed, `$$`])
+  ambiguous identifier: 'test' -- use one of the following:
+    mambparam1.test: string
+    mambparam3.test: string]#
diff --git a/tests/errmsgs/tambparam_legacy.nim b/tests/errmsgs/tambparam_legacy.nim
new file mode 100644
index 000000000..bd99a3aac
--- /dev/null
+++ b/tests/errmsgs/tambparam_legacy.nim
@@ -0,0 +1,14 @@
+import mambparam2, mambparam3
+
+echo test #[tt.Error
+^ type mismatch: got <string | string>
+but expected one of:
+proc echo(x: varargs[typed, `$$`])
+  first type mismatch at position: 1
+  required type for x: varargs[typed]
+  but expression 'test' is of type: None
+  ambiguous identifier: 'test' -- use one of the following:
+    mambparam1.test: string
+    mambparam3.test: string
+
+expression: echo test]#
diff --git a/tests/errmsgs/tassignunpack.nim b/tests/errmsgs/tassignunpack.nim
new file mode 100644
index 000000000..09d928a54
--- /dev/null
+++ b/tests/errmsgs/tassignunpack.nim
@@ -0,0 +1,3 @@
+var a, b = 0
+(a, b) = 1 #[tt.Error
+         ^ tuple expected for tuple unpacking, but got 'int literal(1)']#
diff --git a/tests/errmsgs/tcall_with_default_arg.nim b/tests/errmsgs/tcall_with_default_arg.nim
new file mode 100644
index 000000000..44752f1ee
--- /dev/null
+++ b/tests/errmsgs/tcall_with_default_arg.nim
@@ -0,0 +1,18 @@
+discard """
+outputsub: '''tcall_with_default_arg.nim(8) fail'''
+exitcode: 1
+"""
+# issue: #5604
+
+proc fail() =
+  raise newException(ValueError, "dead")
+
+proc getDefault(): int = 123
+
+proc bar*(arg1: int = getDefault()) =
+  fail()
+
+proc anotherFoo(input: string) =
+  bar()
+
+anotherFoo("123")
diff --git a/tests/errmsgs/tcannot_capture_builtin.nim b/tests/errmsgs/tcannot_capture_builtin.nim
index 3b8aae241..65afa627e 100644
--- a/tests/errmsgs/tcannot_capture_builtin.nim
+++ b/tests/errmsgs/tcannot_capture_builtin.nim
@@ -1,5 +1,5 @@
 discard """
-errormsg: "'+' cannot be passed to a procvar"
+errormsg: "'+' is a built-in and cannot be used as a first-class procedure"
 line: 8
 """
 
diff --git a/tests/errmsgs/tcase_stmt.nim b/tests/errmsgs/tcase_stmt.nim
new file mode 100644
index 000000000..cf63b9c17
--- /dev/null
+++ b/tests/errmsgs/tcase_stmt.nim
@@ -0,0 +1,29 @@
+discard """
+  cmd: "nim check --hints:off $file"
+  errormsg: ""
+  nimout: '''
+tcase_stmt.nim(22, 7) Error: selector must be of an ordinal type, float or string
+tcase_stmt.nim(28, 6) Error: selector must be of an ordinal type, float or string
+
+
+
+
+
+
+
+'''
+"""
+
+
+
+# bug #19682
+type A = object
+
+case A()
+else:
+  discard
+
+# bug #20283
+
+case @[]
+else: discard
diff --git a/tests/errmsgs/tconceptconstraint.nim b/tests/errmsgs/tconceptconstraint.nim
index 9ab1708c7..066ec5bdb 100644
--- a/tests/errmsgs/tconceptconstraint.nim
+++ b/tests/errmsgs/tconceptconstraint.nim
@@ -1,8 +1,7 @@
 discard """
   errormsg: "cannot instantiate B"
-  line: 20
   nimout: '''
-got: <type string>
+got: <typedesc[string]>
 but expected: <T: A>
 '''
 """
diff --git a/tests/errmsgs/tconcisetypemismatch.nim b/tests/errmsgs/tconcisetypemismatch.nim
new file mode 100644
index 000000000..3093cc24e
--- /dev/null
+++ b/tests/errmsgs/tconcisetypemismatch.nim
@@ -0,0 +1,23 @@
+discard """
+  cmd: "nim c --hints:off -d:testsConciseTypeMismatch $file"
+  errormsg: "type mismatch"
+  nimout: '''
+tconcisetypemismatch.nim(23, 47) Error: type mismatch
+Expression: int(inNanoseconds(t2 - t1)) / 100.5
+  [1] int(inNanoseconds(t2 - t1)): int
+  [2] 100.5: float64
+
+Expected one of (first mismatch at [position]):
+[1] proc `/`(x, y: float): float
+[1] proc `/`(x, y: float32): float32
+[2] proc `/`(x, y: int): float
+'''
+"""
+
+import std/monotimes
+from times import inNanoseconds
+
+let t1 = getMonotime()
+let result = 1 + 2
+let t2 = getMonotime()
+echo "Elapsed: ", (t2 - t1).inNanoseconds.int / 100.5
diff --git a/tests/errmsgs/tconstinfo.nim b/tests/errmsgs/tconstinfo.nim
new file mode 100644
index 000000000..170972b9f
--- /dev/null
+++ b/tests/errmsgs/tconstinfo.nim
@@ -0,0 +1,7 @@
+# https://forum.nim-lang.org/t/9762
+
+const foo = "abc"
+case 'a'
+of foo: echo "should error" #[tt.Error
+   ^ type mismatch: got <string> but expected 'char']#
+else: discard
diff --git a/tests/clearmsg/tconsttypemismatch.nim b/tests/errmsgs/tconsttypemismatch.nim
index edf480348..727bfbffb 100644
--- a/tests/clearmsg/tconsttypemismatch.nim
+++ b/tests/errmsgs/tconsttypemismatch.nim
@@ -1,8 +1,7 @@
 discard """
+  errormsg: "type mismatch"
   file: "tconsttypemismatch.nim"
   line: 7
-  errormsg: "type mismatch"
 """
 # bug #2252
 const foo: int = 1000 / 30
-
diff --git a/tests/errmsgs/tdeclaredlocs.nim b/tests/errmsgs/tdeclaredlocs.nim
new file mode 100644
index 000000000..926ebf217
--- /dev/null
+++ b/tests/errmsgs/tdeclaredlocs.nim
@@ -0,0 +1,92 @@
+discard """
+  action: reject
+  matrix: "--declaredLocs --hints:off"
+  nimoutFull: true
+  nimout: '''
+tdeclaredlocs.nim(92, 3) Error: type mismatch: got <seq[MyInt2]>
+but expected one of:
+proc fn(a: Bam) [proc declared in tdeclaredlocs.nim(86, 6)]
+  first type mismatch at position: 1
+  required type for a: Bam [object declared in tdeclaredlocs.nim(78, 3)]
+  but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)]
+proc fn(a: Goo[MyInt2]) [proc declared in tdeclaredlocs.nim(89, 6)]
+  first type mismatch at position: 1
+  required type for a: Goo[MyInt2{char}] [object declared in tdeclaredlocs.nim(79, 3)]
+  but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)]
+proc fn(a: Goo[cint]) [proc declared in tdeclaredlocs.nim(88, 6)]
+  first type mismatch at position: 1
+  required type for a: Goo[cint{int32}] [object declared in tdeclaredlocs.nim(79, 3)]
+  but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)]
+proc fn(a: array[3, Bar]) [proc declared in tdeclaredlocs.nim(82, 6)]
+  first type mismatch at position: 1
+  required type for a: array[0..2, Bar] [object declared in tdeclaredlocs.nim(74, 3)]
+  but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)]
+proc fn(a: seq[Bar]) [proc declared in tdeclaredlocs.nim(81, 6)]
+  first type mismatch at position: 1
+  required type for a: seq[Bar] [object declared in tdeclaredlocs.nim(74, 3)]
+  but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)]
+proc fn(a: seq[MyInt1]) [proc declared in tdeclaredlocs.nim(80, 6)]
+  first type mismatch at position: 1
+  required type for a: seq[MyInt1{int}] [int declared in tdeclaredlocs.nim(72, 3)]
+  but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)]
+proc fn(a: set[Baz]) [proc declared in tdeclaredlocs.nim(84, 6)]
+  first type mismatch at position: 1
+  required type for a: set[Baz{enum}] [enum declared in tdeclaredlocs.nim(75, 3)]
+  but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)]
+proc fn(a: set[MyInt2]) [proc declared in tdeclaredlocs.nim(83, 6)]
+  first type mismatch at position: 1
+  required type for a: set[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)]
+  but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)]
+proc fn(a: var SetBaz) [proc declared in tdeclaredlocs.nim(85, 6)]
+  first type mismatch at position: 1
+  required type for a: var SetBaz [enum declared in tdeclaredlocs.nim(75, 3)]
+  but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)]
+proc fn(a: var ref ptr Bam) [proc declared in tdeclaredlocs.nim(87, 6)]
+  first type mismatch at position: 1
+  required type for a: var ref ptr Bam [object declared in tdeclaredlocs.nim(78, 3)]
+  but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)]
+
+expression: fn(a)
+'''
+"""
+
+#[
+see also: tests/errmsgs/tsigmatch.nim
+]#
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# line 70
+type
+  MyInt1 = int
+  MyInt2 = char
+  Bar = object
+  Baz = enum k0, k1
+  Baz2 = Baz
+  SetBaz = set[Baz2]
+  Bam = ref object
+  Goo[T] = object
+proc fn(a: seq[MyInt1]) = discard
+proc fn(a: seq[Bar]) = discard
+proc fn(a: array[3, Bar]) = discard
+proc fn(a: set[MyInt2]) = discard
+proc fn(a: set[Baz]) = discard
+proc fn(a: var SetBaz) = discard
+proc fn(a: Bam) = discard
+proc fn(a: var ref ptr Bam) = discard
+proc fn(a: Goo[cint]) = discard
+proc fn(a: Goo[MyInt2]) = discard
+
+var a: seq[MyInt2]
+fn(a)
diff --git a/tests/errmsgs/tdetailed_position.nim b/tests/errmsgs/tdetailed_position.nim
index ce5b18bbd..ecece7972 100644
--- a/tests/errmsgs/tdetailed_position.nim
+++ b/tests/errmsgs/tdetailed_position.nim
@@ -6,7 +6,7 @@ nimout: '''
 but expected one of:
 proc main(a, b, c: string)
   first type mismatch at position: 1
-  required type: string
+  required type for a: string
   but expression '1' is of type: int literal(1)
 
 expression: main(1, 2, 3)
diff --git a/tests/errmsgs/tdistinct_nil.nim b/tests/errmsgs/tdistinct_nil.nim
new file mode 100644
index 000000000..a2b403293
--- /dev/null
+++ b/tests/errmsgs/tdistinct_nil.nim
@@ -0,0 +1,23 @@
+discard """
+  cmd: "nim check $file"
+  action: reject
+  nimout: '''
+tdistinct_nil.nim(23, 4) Error: type mismatch: got <typeof(nil)>
+but expected one of:
+proc foo(x: DistinctPointer)
+  first type mismatch at position: 1
+  required type for x: DistinctPointer
+  but expression 'nil' is of type: typeof(nil)
+
+expression: foo(nil)
+'''
+"""
+
+type
+  DistinctPointer = distinct pointer
+
+proc foo(x: DistinctPointer) =
+  discard
+
+foo(DistinctPointer(nil))
+foo(nil)
diff --git a/tests/errmsgs/tdont_show_system.nim b/tests/errmsgs/tdont_show_system.nim
index 830113218..164fdd801 100644
--- a/tests/errmsgs/tdont_show_system.nim
+++ b/tests/errmsgs/tdont_show_system.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "expression 'true' is of type 'bool' and has to be discarded"
+  errormsg: "expression 'true' is of type 'bool' and has to be used (or discarded)"
   line: 13
   file: "tdont_show_system.nim"
 """
diff --git a/tests/errmsgs/temptysetparam.nim b/tests/errmsgs/temptysetparam.nim
new file mode 100644
index 000000000..20763feda
--- /dev/null
+++ b/tests/errmsgs/temptysetparam.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "cannot infer the type of parameter 'x'"
+  line: 5
+"""
+proc a(x = {}) = discard
diff --git a/tests/errmsgs/tgcsafety.nim b/tests/errmsgs/tgcsafety.nim
index 4d192db90..66496d364 100644
--- a/tests/errmsgs/tgcsafety.nim
+++ b/tests/errmsgs/tgcsafety.nim
@@ -1,14 +1,16 @@
 discard """
 cmd: "nim check $file"
-errormsg: "type mismatch: got <AsyncHttpServer, Port, proc (req: Request): Future[system.void]{.locks: <unknown>.}>"
+errormsg: "type mismatch: got <AsyncHttpServer, Port, proc (req: Request): Future[system.void]>"
 nimout: '''
-type mismatch: got <AsyncHttpServer, Port, proc (req: Request): Future[system.void]{.locks: <unknown>.}>
+tgcsafety.nim(31, 18) Error: type mismatch: got <AsyncHttpServer, Port, proc (req: Request): Future[system.void]>
 but expected one of:
 proc serve(server: AsyncHttpServer; port: Port;
-          callback: proc (request: Request): Future[void]; address = ""): Future[void]
+           callback: proc (request: Request): Future[void] {.closure, gcsafe.};
+           address = ""; assumedDescriptorsPerRequest = -1; domain = AF_INET): owned(
+    Future[void])
   first type mismatch at position: 3
-  required type: proc (request: Request): Future[system.void]{.closure, gcsafe.}
-  but expression 'cb' is of type: proc (req: Request): Future[system.void]{.locks: <unknown>.}
+  required type for callback: proc (request: Request): Future[system.void]{.closure, gcsafe.}
+  but expression 'cb' is of type: proc (req: Request): Future[system.void]
   This expression is not GC-safe. Annotate the proc with {.gcsafe.} to get extended error information.
 
 expression: serve(server, Port(7898), cb)
diff --git a/tests/errmsgs/tgeneral_excepts.nim b/tests/errmsgs/tgeneral_excepts.nim
new file mode 100644
index 000000000..8f8be797f
--- /dev/null
+++ b/tests/errmsgs/tgeneral_excepts.nim
@@ -0,0 +1,10 @@
+discard """
+errormsg: "Only one general except clause is allowed after more specific exceptions"
+"""
+
+try:
+  discard
+except:
+  discard
+except:
+  discard
diff --git a/tests/errmsgs/tgenericconstraint.nim b/tests/errmsgs/tgenericconstraint.nim
index e3093fead..b51fb3488 100644
--- a/tests/errmsgs/tgenericconstraint.nim
+++ b/tests/errmsgs/tgenericconstraint.nim
@@ -1,8 +1,7 @@
 discard """
   errormsg: "cannot instantiate B"
-  line: 14
   nimout: '''
-got: <type int>
+got: <typedesc[int]>
 but expected: <T: string or float>
 '''
 """
diff --git a/tests/errmsgs/tgenericmismatchsegfault.nim b/tests/errmsgs/tgenericmismatchsegfault.nim
new file mode 100644
index 000000000..dbb783cb3
--- /dev/null
+++ b/tests/errmsgs/tgenericmismatchsegfault.nim
@@ -0,0 +1,13 @@
+discard """
+  matrix: "-d:testsConciseTypeMismatch"
+"""
+
+template v[T](c: SomeOrdinal): T = T(c)
+discard v[int, char]('A') #[tt.Error
+                    ^ type mismatch
+Expression: v[int, char]('A')
+  [1] 'A': char
+
+Expected one of (first mismatch at [position]):
+[2] template v[T](c: SomeOrdinal): T
+  generic parameter mismatch, expected SomeOrdinal but got 'char' of type: char]#
diff --git a/tests/errmsgs/tgenericmismatchsegfault_legacy.nim b/tests/errmsgs/tgenericmismatchsegfault_legacy.nim
new file mode 100644
index 000000000..1532611b9
--- /dev/null
+++ b/tests/errmsgs/tgenericmismatchsegfault_legacy.nim
@@ -0,0 +1,10 @@
+template v[T](c: SomeOrdinal): T = T(c)
+discard v[int, char]('A') #[tt.Error
+                    ^ type mismatch: got <char>
+but expected one of:
+template v[T](c: SomeOrdinal): T
+  first type mismatch at position: 2 in generic parameters
+  required type for SomeOrdinal: SomeOrdinal
+  but expression 'char' is of type: char
+
+expression: v[int, char]('A')]#
diff --git a/tests/errmsgs/tinconsistentgensym.nim b/tests/errmsgs/tinconsistentgensym.nim
new file mode 100644
index 000000000..8e4c85106
--- /dev/null
+++ b/tests/errmsgs/tinconsistentgensym.nim
@@ -0,0 +1,25 @@
+discard """
+  cmd: "nim check --hints:off $file"
+"""
+
+block:
+  template foo =
+    when false:
+      let x = 123
+    else:
+      template x: untyped {.inject.} = 456
+    echo x #[tt.Error
+         ^ undeclared identifier: 'x`gensym0'; if declared in a template, this identifier may be inconsistently marked inject or gensym]#
+  foo()
+
+block:
+  template foo(y: static bool) =
+    block:
+      when y:
+        let x {.gensym.} = 123
+      else:
+        let x {.inject.} = 456
+      echo x #[tt.Error
+           ^ undeclared identifier: 'x']#
+  foo(false)
+  foo(true)
diff --git a/tests/errmsgs/tinteger_literals.nim b/tests/errmsgs/tinteger_literals.nim
new file mode 100644
index 000000000..f90ab14a1
--- /dev/null
+++ b/tests/errmsgs/tinteger_literals.nim
@@ -0,0 +1,14 @@
+discard """
+  cmd: "nim check $file"
+"""
+# high(uint64) + 1
+discard 18446744073709551616'u64 #[tt.Error
+        ^ number out of range: '18446744073709551616'u64' ]#
+# high(int64) + 1
+discard 9223372036854775808'i64 #[tt.Error
+        ^ number out of range: '9223372036854775808'i64' ]#
+# high(int64) + 1
+discard 9223372036854775808 #[tt.Error
+        ^ number out of range: '9223372036854775808' ]#
+discard 300'u8 #[tt.Error
+        ^ number out of range: '300'u8' ]#
diff --git a/tests/errmsgs/tinvalidinout.nim b/tests/errmsgs/tinvalidinout.nim
index 1fa3805ee..268bcf4d5 100644
--- a/tests/errmsgs/tinvalidinout.nim
+++ b/tests/errmsgs/tinvalidinout.nim
@@ -9,7 +9,7 @@ tinvalidinout.nim(18, 9) Error: the 'in' modifier can be used only with imported
 """
 
 type
-  Foo {.header: "foo.h", importcpp.} [in T] = object
+  Foo[in T] {.header: "foo.h", importcpp.} = object
 
   Bar[out X] = object
     x: int
diff --git a/tests/clearmsg/tmacroerrorproc.nim b/tests/errmsgs/tmacroerrorproc.nim
index cd9b15e25..86726af72 100644
--- a/tests/clearmsg/tmacroerrorproc.nim
+++ b/tests/errmsgs/tmacroerrorproc.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "Expected a node of kind nnkCharLit, got nnkCommand"
   file: "tmacroerrorproc.nim"
   line: 13
-  errormsg: "Expected a node of kind nnkCharLit, got nnkCommand"
 """
 # issue #4915
 import macros
diff --git a/tests/errmsgs/tmake_tuple_visible.nim b/tests/errmsgs/tmake_tuple_visible.nim
index e059368ad..90b965c64 100644
--- a/tests/errmsgs/tmake_tuple_visible.nim
+++ b/tests/errmsgs/tmake_tuple_visible.nim
@@ -1,9 +1,6 @@
 discard """
-  errormsg: '''got <tuple of (type NimEdAppWindow, int)>'''
-  line: 22
-  nimout: '''got <tuple of (type NimEdAppWindow, int)>
-but expected one of:
-template xxx(tn: typedesc; i: int)'''
+  errormsg: '''Mixing types and values in tuples is not allowed.'''
+  line: 19
 """
 
 type
diff --git a/tests/errmsgs/tmetaobjectfields.nim b/tests/errmsgs/tmetaobjectfields.nim
new file mode 100644
index 000000000..47d3acf18
--- /dev/null
+++ b/tests/errmsgs/tmetaobjectfields.nim
@@ -0,0 +1,75 @@
+discard """
+  cmd: "nim check --hints:off $file"
+  action: "reject"
+  nimout: '''
+tmetaobjectfields.nim(26, 5) Error: 'array' is not a concrete type
+tmetaobjectfields.nim(30, 5) Error: 'seq' is not a concrete type
+tmetaobjectfields.nim(34, 5) Error: 'set' is not a concrete type
+tmetaobjectfields.nim(37, 3) Error: 'sink' is not a concrete type
+tmetaobjectfields.nim(39, 3) Error: 'lent' is not a concrete type
+tmetaobjectfields.nim(56, 16) Error: 'seq' is not a concrete type
+tmetaobjectfields.nim(60, 5) Error: 'ptr' is not a concrete type
+tmetaobjectfields.nim(61, 5) Error: 'ref' is not a concrete type
+tmetaobjectfields.nim(62, 5) Error: 'auto' is not a concrete type
+tmetaobjectfields.nim(63, 5) Error: 'UncheckedArray' is not a concrete type
+tmetaobjectfields.nim(68, 5) Error: 'object' is not a concrete type
+tmetaobjectfields.nim(72, 5) Error: 'Type3011:ObjectType' is not a concrete type
+'''
+"""
+
+
+# bug #6982
+# bug #19546
+# bug #23531
+type
+  ExampleObj1 = object
+    arr: array
+
+type
+  ExampleObj2 = object
+    arr: seq
+
+type
+  ExampleObj3 = object
+    arr: set
+
+type A = object
+  b: sink
+  # a: openarray
+  c: lent
+
+type PropertyKind = enum
+  tInt,
+  tFloat,
+  tBool,
+  tString,
+  tArray
+
+type
+  Property = ref PropertyObj
+  PropertyObj = object
+    case kind: PropertyKind
+    of tInt: intValue: int
+    of tFloat: floatValue: float
+    of tBool: boolValue: bool
+    of tString: stringValue: string
+    of tArray: arrayValue: seq
+
+type
+  RegressionTest = object
+    a: ptr
+    b: ref
+    c: auto
+    d: UncheckedArray
+
+# bug #3011
+type
+  Type3011 = ref object 
+    context: ref object
+
+type
+  Value3011 = ref object
+    typ: Type3011
+
+proc x3011(): Value3011 =
+  nil
diff --git a/tests/errmsgs/tmultiple_finally.nim b/tests/errmsgs/tmultiple_finally.nim
new file mode 100644
index 000000000..519a80145
--- /dev/null
+++ b/tests/errmsgs/tmultiple_finally.nim
@@ -0,0 +1,12 @@
+discard """
+errormsg: "Only one finally is allowed after all other branches"
+"""
+
+try:
+  discard
+finally:
+  discard
+finally:
+  discard
+
+
diff --git a/tests/errmsgs/tnested_empty_seq.nim b/tests/errmsgs/tnested_empty_seq.nim
index ffe8bc3ee..871b0ee85 100644
--- a/tests/errmsgs/tnested_empty_seq.nim
+++ b/tests/errmsgs/tnested_empty_seq.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "invalid type: 'empty' in this context: 'array[0..0, tuple of (string, seq[empty])]' for var"
+  errormsg: "invalid type: 'empty' in this context: 'array[0..0, (string, seq[empty])]' for var"
   line: 8
 """
 
diff --git a/tests/errmsgs/tnested_generic_instantiation.nim b/tests/errmsgs/tnested_generic_instantiation.nim
new file mode 100644
index 000000000..ab9ab54cd
--- /dev/null
+++ b/tests/errmsgs/tnested_generic_instantiation.nim
@@ -0,0 +1,24 @@
+discard """
+action: compile
+"""
+
+# bug #4766
+
+type
+  Plain = ref object
+    discard
+
+  Wrapped[T] = object
+    value: T
+
+converter toWrapped[T](value: T): Wrapped[T] =
+  Wrapped[T](value: value)
+
+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..c2ec31af8
--- /dev/null
+++ b/tests/errmsgs/tnested_generic_instantiation2.nim
@@ -0,0 +1,27 @@
+discard """
+action: compile
+"""
+
+#[
+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/tnnodeadd.nim b/tests/errmsgs/tnnodeadd.nim
new file mode 100644
index 000000000..61921883e
--- /dev/null
+++ b/tests/errmsgs/tnnodeadd.nim
@@ -0,0 +1,8 @@
+discard """
+  errormsg: "cannot add to node kind: nnkInt8Lit"
+  line: 7
+"""
+import macros
+macro t(x: untyped): untyped =
+  x.add(newEmptyNode())
+t(38'i8)
diff --git a/tests/errmsgs/tnnodeindex.nim b/tests/errmsgs/tnnodeindex.nim
new file mode 100644
index 000000000..5e37e7977
--- /dev/null
+++ b/tests/errmsgs/tnnodeindex.nim
@@ -0,0 +1,8 @@
+discard """
+  errormsg: "index 5 not in 0 .. 2"
+  line: 7
+"""
+import macros
+macro t(x: untyped): untyped =
+  result = x[5]
+t([1, 2, 3])
diff --git a/tests/errmsgs/tnnodeindexkind.nim b/tests/errmsgs/tnnodeindexkind.nim
new file mode 100644
index 000000000..9ea045e66
--- /dev/null
+++ b/tests/errmsgs/tnnodeindexkind.nim
@@ -0,0 +1,8 @@
+discard """
+  errormsg: "cannot set child of node kind: nnkStrLit"
+  line: 7
+"""
+import macros
+macro t(x: untyped): untyped =
+  x[0] = newEmptyNode()
+t("abc")
diff --git a/tests/errmsgs/tnon_concrete_cast.nim b/tests/errmsgs/tnon_concrete_cast.nim
index e4ae890ce..9e05c736c 100644
--- a/tests/errmsgs/tnon_concrete_cast.nim
+++ b/tests/errmsgs/tnon_concrete_cast.nim
@@ -34,7 +34,7 @@ proc write(rw: var MyReadWrite; value: SomeNumber): void =
 proc write[T](rw: var MyReadWrite; value: seq[T]): void =
   rw.write value.len
   let dst  = cast[ptr SomeNumber](cast[uint](rw.memfile.mem) + uint(rw.offset))
-  let src  = cast[pointer](value[0].unsafeAddr)
+  let src  = cast[pointer](value[0].addr)
   let size = sizeof(T) * value.len
   copyMem(dst, src, size)
   rw.offset += size
diff --git a/tests/errmsgs/tnoop.nim b/tests/errmsgs/tnoop.nim
new file mode 100644
index 000000000..f55f2441a
--- /dev/null
+++ b/tests/errmsgs/tnoop.nim
@@ -0,0 +1,12 @@
+discard """
+  nimout: '''
+  found 'a' [var declared in tnoop.nim(10, 3)]
+  '''
+  file: "tnoop.nim"
+  errormsg: "attempting to call routine: 'a'"
+"""
+
+var
+  a: int
+
+a()
diff --git a/tests/errmsgs/tproc_mismatch.nim b/tests/errmsgs/tproc_mismatch.nim
new file mode 100644
index 000000000..16f319f3b
--- /dev/null
+++ b/tests/errmsgs/tproc_mismatch.nim
@@ -0,0 +1,74 @@
+discard """
+  action: reject
+  cmd: '''nim check --hints:off $options $file'''
+  nimoutFull: true
+  nimout: '''
+tproc_mismatch.nim(38, 52) Error: type mismatch: got <proc (a: int, c: float){.cdecl, noSideEffect, gcsafe.}> but expected 'proc (a: int, c: float){.closure, noSideEffect.}'
+  Calling convention mismatch: got '{.cdecl.}', but expected '{.closure.}'.
+tproc_mismatch.nim(42, 6) Error: type mismatch: got <proc (){.inline, noSideEffect, gcsafe.}>
+but expected one of:
+proc bar(a: proc ())
+  first type mismatch at position: 1
+  required type for a: proc (){.closure.}
+  but expression 'fn1' is of type: proc (){.inline, noSideEffect, gcsafe.}
+  Calling convention mismatch: got '{.inline.}', but expected '{.closure.}'.
+
+expression: bar(fn1)
+tproc_mismatch.nim(46, 8) Error: type mismatch: got <proc (){.inline, noSideEffect, gcsafe.}> but expected 'proc (){.closure.}'
+  Calling convention mismatch: got '{.inline.}', but expected '{.closure.}'.
+tproc_mismatch.nim(51, 8) Error: type mismatch: got <proc ()> but expected 'proc (){.closure, noSideEffect.}'
+  Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'.
+  Pragma mismatch: got '{..}', but expected '{.noSideEffect.}'.
+tproc_mismatch.nim(55, 8) Error: type mismatch: got <proc (a: int){.noSideEffect, gcsafe.}> but expected 'proc (a: float){.closure.}'
+  Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'.
+tproc_mismatch.nim(64, 9) Error: type mismatch: got <proc (a: int)> but expected 'proc (a: int){.closure, gcsafe.}'
+  Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'.
+  Pragma mismatch: got '{..}', but expected '{.gcsafe.}'.
+tproc_mismatch.nim(72, 9) Error: type mismatch: got <proc (a: int): int{.nimcall.}> but expected 'proc (a: int): int{.cdecl.}'
+  Calling convention mismatch: got '{.nimcall.}', but expected '{.cdecl.}'.
+tproc_mismatch.nim(73, 9) Error: type mismatch: got <proc (a: int): int{.cdecl.}> but expected 'proc (a: int): int{.nimcall.}'
+  Calling convention mismatch: got '{.cdecl.}', but expected '{.nimcall.}'.
+'''
+"""
+
+
+
+block: # CallConv mismatch
+  func a(a: int, c: float) {.cdecl.} = discard
+  var b: proc(a: int, c: float) {.noSideEffect.} = a
+block: # Parameter CallConv mismatch
+  proc fn1() {.inline.} = discard
+  proc bar(a: proc()) = discard
+  bar(fn1)
+block: # CallConv mismatch
+  proc fn1() {.inline.} = discard
+  var fn: proc()
+  fn = fn1
+block: # Pragma mismatch
+  var a = ""
+  proc fn1() = a.add "b"
+  var fn: proc() {.noSideEffect.}
+  fn = fn1
+block: # Fail match not do to Pragma or CallConv
+  proc fn1(a: int) = discard
+  var fn: proc(a: float)
+  fn = fn1
+block: # Infered noSideEffect assign
+  type Foo = ref object
+    x0: int
+  var g0 = Foo(x0: 1)
+  proc fn1(a: int) = g0.x0 = a
+  var fn2: proc(a: int)
+  var fn3: proc(a: int) {.gcsafe.}
+  fn2 = fn1
+  fn3 = fn1
+block: # Indrection through pragmas
+  {.pragma: inl1, inline.}
+  {.pragma: inl2, inline.}
+  {.pragma: p1, nimcall.}
+  {.pragma: p2, cdecl.}
+  var fn1: proc(a: int): int {.inl1, p1.}
+  var fn2: proc(a: int): int {.inl2, p2.}
+  fn2 = fn1
+  fn1 = fn2
+
diff --git a/tests/errmsgs/tproper_stacktrace.nim b/tests/errmsgs/tproper_stacktrace.nim
index 134946651..b0a008840 100644
--- a/tests/errmsgs/tproper_stacktrace.nim
+++ b/tests/errmsgs/tproper_stacktrace.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--stackTrace:on --hint:all:off --warnings:off"
   output: '''ok'''
 """
 import strscans, strutils
@@ -6,7 +7,7 @@ import strscans, strutils
 proc raiseTestException*() =
   raise newException(Exception, "test")
 
-proc matchStackTrace(actualEntries: openarray[StackTraceEntry], expected: string) =
+proc matchStackTrace(actualEntries: openArray[StackTraceEntry], expected: string) =
   var expectedEntries = newSeq[StackTraceEntry]()
   var i = 0
 
@@ -22,7 +23,7 @@ proc matchStackTrace(actualEntries: openarray[StackTraceEntry], expected: string
     var line: int
     if not scanf(l, "$s$w.nim($i) $w", filename, line, procname):
       doAssert(false, "Wrong expected stack trace")
-    checkEqual($actualEntries[i].filename, filename & ".nim", "file name")
+    checkEqual(actualEntries[i].filename.`$`.split('/')[^1], filename & ".nim", "file name")
     if line != 0:
       checkEqual(actualEntries[i].line, line, "line number")
     checkEqual($actualEntries[i].procname, procname, "proc name")
@@ -66,7 +67,7 @@ template verifyStackTrace*(expectedStackTrace: string, body: untyped) =
 
 
 
-when isMainModule:
+when true:
 # <-- Align with line 70 in the text editor
   block:
     proc bar() =
@@ -76,10 +77,10 @@ when isMainModule:
       bar()
 
     const expectedStackTrace = """
-      tproper_stacktrace.nim(86) tproper_stacktrace
-      tproper_stacktrace.nim(76) foo
-      tproper_stacktrace.nim(73) bar
-      tproper_stacktrace.nim(7) raiseTestException
+      tproper_stacktrace.nim(87) tproper_stacktrace
+      tproper_stacktrace.nim(77) foo
+      tproper_stacktrace.nim(74) bar
+      tproper_stacktrace.nim(8) raiseTestException
     """
 
     verifyStackTrace expectedStackTrace:
@@ -93,9 +94,9 @@ when isMainModule:
       bar(x)
 
     const expectedStackTrace = """
-      tproper_stacktrace.nim(103) tproper_stacktrace
-      tproper_stacktrace.nim(90) bar
-      tproper_stacktrace.nim(7) raiseTestException
+      tproper_stacktrace.nim(104) tproper_stacktrace
+      tproper_stacktrace.nim(91) bar
+      tproper_stacktrace.nim(8) raiseTestException
     """
 
     verifyStackTrace expectedStackTrace:
@@ -110,10 +111,10 @@ when isMainModule:
       bar()
 
     const expectedStackTrace = """
-      tproper_stacktrace.nim(120) tproper_stacktrace
-      tproper_stacktrace.nim(110) foo
-      tproper_stacktrace.nim(107) bar
-      tproper_stacktrace.nim(7) raiseTestException
+      tproper_stacktrace.nim(121) tproper_stacktrace
+      tproper_stacktrace.nim(111) foo
+      tproper_stacktrace.nim(108) bar
+      tproper_stacktrace.nim(8) raiseTestException
     """
 
     verifyStackTrace expectedStackTrace:
@@ -129,10 +130,10 @@ when isMainModule:
       bar()
 
     const expectedStackTrace = """
-      tproper_stacktrace.nim(139) tproper_stacktrace
-      tproper_stacktrace.nim(129) foo
-      tproper_stacktrace.nim(125) baz
-      tproper_stacktrace.nim(7) raiseTestException
+      tproper_stacktrace.nim(140) tproper_stacktrace
+      tproper_stacktrace.nim(130) foo
+      tproper_stacktrace.nim(126) baz
+      tproper_stacktrace.nim(8) raiseTestException
     """
 
     verifyStackTrace expectedStackTrace:
diff --git a/tests/errmsgs/tproper_stacktrace2.nim b/tests/errmsgs/tproper_stacktrace2.nim
index 44b208c87..5a6ca3fcb 100644
--- a/tests/errmsgs/tproper_stacktrace2.nim
+++ b/tests/errmsgs/tproper_stacktrace2.nim
@@ -1,5 +1,6 @@
 discard """
-  outputsub: '''tproper_stacktrace2.nim(20) main'''
+  matrix: "--stackTrace:on"
+  outputsub: '''tproper_stacktrace2.nim(21) main'''
   exitcode: 1
 """
 
diff --git a/tests/errmsgs/tproper_stacktrace3.nim b/tests/errmsgs/tproper_stacktrace3.nim
index c292fa092..97d63e6ec 100644
--- a/tests/errmsgs/tproper_stacktrace3.nim
+++ b/tests/errmsgs/tproper_stacktrace3.nim
@@ -1,5 +1,6 @@
 discard """
-  outputsub: '''tproper_stacktrace3.nim(21) main'''
+  matrix: "--stackTrace:on"
+  outputsub: '''tproper_stacktrace3.nim(22) main'''
   exitcode: 1
 """
 
diff --git a/tests/errmsgs/treportunused.nim b/tests/errmsgs/treportunused.nim
new file mode 100644
index 000000000..46afe163d
--- /dev/null
+++ b/tests/errmsgs/treportunused.nim
@@ -0,0 +1,65 @@
+discard """
+  matrix: "--hint:all:off --hint:XDeclaredButNotUsed"
+  nimoutFull: true
+  nimout: '''
+treportunused.nim(51, 5) Hint: 'A' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(52, 5) Hint: 'B' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(55, 5) Hint: 'D' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(56, 5) Hint: 'E' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(59, 5) Hint: 'G' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(60, 5) Hint: 'H' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(64, 5) Hint: 'K' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(65, 5) Hint: 'L' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(31, 10) Hint: 's1' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(32, 10) Hint: 's2' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(33, 10) Hint: 's3' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(34, 6) Hint: 's4' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(35, 6) Hint: 's5' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(36, 7) Hint: 's6' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(37, 7) Hint: 's7' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(38, 5) Hint: 's8' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(39, 5) Hint: 's9' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(40, 6) Hint: 's10' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(41, 6) Hint: 's11' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(45, 3) Hint: 'v0.99' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(46, 3) Hint: 'v0.99.99' is declared but not used [XDeclaredButNotUsed]
+'''
+action: compile
+"""
+
+# bug #9764
+iterator s1(a:string): int = discard
+iterator s2(): int = discard
+template s3(): untyped = 123
+proc s4(): int = 123
+proc s5[T](a: T): int = 123
+macro s6(a: int): untyped = discard
+const s7 = 0
+let s8 = 0
+var s9: int
+type s10 = object
+type s11 = type(1.2)
+
+# bug #14407 (requires `compiler/nim.cfg` containing define:nimPreviewFloatRoundtrip)
+let
+  `v0.99` = "0.99"
+  `v0.99.99` = "0.99.99"
+
+block: # bug #18201
+  # Test that unused type aliases raise hint XDeclaredButNotUsed.
+  type
+    A = int
+    B = distinct int
+
+    C = object
+    D = C
+    E = distinct C
+
+    F = string
+    G = F
+    H = distinct F
+
+    J = enum
+      Foo
+    K = J
+    L = distinct J
diff --git a/tests/errmsgs/tshow_asgn.nim b/tests/errmsgs/tshow_asgn.nim
index 1627c9b71..28a9acbeb 100644
--- a/tests/errmsgs/tshow_asgn.nim
+++ b/tests/errmsgs/tshow_asgn.nim
@@ -1,7 +1,7 @@
 discard """
   errormsg: "type mismatch: got <int> but expected 'cshort = int16'"
   line: 12
-  column: 10
+  column: 27
   file: "tshow_asgn.nim"
 """
 
diff --git a/tests/errmsgs/tsigmatch.nim b/tests/errmsgs/tsigmatch.nim
new file mode 100644
index 000000000..85ed34169
--- /dev/null
+++ b/tests/errmsgs/tsigmatch.nim
@@ -0,0 +1,172 @@
+discard """
+  cmd: "nim check --mm:refc --showAllMismatches:on --hints:off $file"
+  nimout: '''
+tsigmatch.nim(111, 4) Error: type mismatch: got <A, string>
+but expected one of:
+proc f(a: A)
+  first type mismatch at position: 2
+  extra argument given
+proc f(b: B)
+  first type mismatch at position: 1
+  required type for b: B
+  but expression 'A()' is of type: A
+
+expression: f(A(), "extra")
+tsigmatch.nim(125, 6) Error: type mismatch: got <(string, proc (){.gcsafe.})>
+but expected one of:
+proc foo(x: (string, proc ()))
+  first type mismatch at position: 1
+  required type for x: (string, proc (){.closure.})
+  but expression '("foobar", proc () = echo(["Hello!"]))' is of type: (string, proc (){.gcsafe.})
+
+expression: foo(("foobar", proc () = echo(["Hello!"])))
+tsigmatch.nim(132, 11) Error: type mismatch: got <proc (s: string): string{.noSideEffect, gcsafe.}>
+but expected one of:
+proc foo[T, S](op: proc (x: T): S {.cdecl.}): auto
+  first type mismatch at position: 1
+  required type for op: proc (x: T): S{.cdecl.}
+  but expression 'fun' is of type: proc (s: string): string{.noSideEffect, gcsafe.}
+proc foo[T, S](op: proc (x: T): S {.safecall.}): auto
+  first type mismatch at position: 1
+  required type for op: proc (x: T): S{.safecall.}
+  but expression 'fun' is of type: proc (s: string): string{.noSideEffect, gcsafe.}
+
+expression: foo(fun)
+tsigmatch.nim(143, 13) Error: type mismatch: got <array[0..0, proc (x: int){.gcsafe.}]>
+but expected one of:
+proc takesFuncs(fs: openArray[proc (x: int) {.gcsafe.}])
+  first type mismatch at position: 1
+  required type for fs: openArray[proc (x: int){.closure, gcsafe.}]
+  but expression '[proc (x: int) {.gcsafe.} = echo [x]]' is of type: array[0..0, proc (x: int){.gcsafe.}]
+
+expression: takesFuncs([proc (x: int) {.gcsafe.} = echo [x]])
+tsigmatch.nim(149, 4) Error: type mismatch: got <int literal(10), a0: int literal(5), string>
+but expected one of:
+proc f(a0: uint8; b: string)
+  first type mismatch at position: 2
+  named param already provided: a0
+
+expression: f(10, a0 = 5, "")
+tsigmatch.nim(156, 4) Error: type mismatch: got <string, string, string, string, string, float64, string>
+but expected one of:
+proc f(a1: int)
+  first type mismatch at position: 1
+  required type for a1: int
+  but expression '"asdf"' is of type: string
+proc f(a1: string; a2: varargs[string]; a3: float; a4: var string)
+  first type mismatch at position: 7
+  required type for a4: var string
+  but expression '"bad"' is immutable, not 'var'
+
+expression: f("asdf", "1", "2", "3", "4", 2.3, "bad")
+tsigmatch.nim(164, 4) Error: type mismatch: got <string, a0: int literal(12)>
+but expected one of:
+proc f(x: string; a0: string)
+  first type mismatch at position: 2
+  required type for a0: string
+  but expression 'a0 = 12' is of type: int literal(12)
+proc f(x: string; a0: var int)
+  first type mismatch at position: 2
+  required type for a0: var int
+  but expression 'a0 = 12' is immutable, not 'var'
+
+expression: f(foo, a0 = 12)
+tsigmatch.nim(171, 7) Error: type mismatch: got <Mystring, string>
+but expected one of:
+proc fun1(a1: MyInt; a2: Mystring)
+  first type mismatch at position: 1
+  required type for a1: MyInt
+  but expression 'default(Mystring)' is of type: Mystring
+proc fun1(a1: float; a2: Mystring)
+  first type mismatch at position: 1
+  required type for a1: float
+  but expression 'default(Mystring)' is of type: Mystring
+
+expression: fun1(default(Mystring), "asdf")
+'''
+  errormsg: "type mismatch"
+"""
+
+
+
+#[
+see also: tests/errmsgs/tdeclaredlocs.nim
+]#
+
+
+
+
+
+## line 100
+when true:
+  # bug #11061 Type mismatch error "first type mismatch at" points to wrong argument/position
+  # Note: the error msg now gives correct position for mismatched argument
+  type
+    A = object of RootObj
+    B = object of A
+block:
+  proc f(b: B) = discard
+  proc f(a: A) = discard
+
+  f(A(), "extra")
+#[
+this one is similar but error msg was even more misleading, since the user
+would think float != float64 where in fact the issue is another param:
+first type mismatch at position: 1; required type: float; but expression 'x = 1.2' is of type: float64
+  proc f(x: string, a0 = 0, a1 = 0, a2 = 0) = discard
+  proc f(x: float, a0 = 0, a1 = 0, a2 = 0) = discard
+  f(x = float(1.2), a0 = 0, a0 = 0)
+]#
+
+block:
+  # bug #7808 Passing tuple with proc leads to confusing errors
+  # Note: the error message now shows `closure` which helps debugging the issue
+  proc foo(x: (string, proc ())) = x[1]()
+  foo(("foobar", proc () = echo("Hello!")))
+
+block:
+  # bug #8305 type mismatch error drops crucial pragma info when there's only 1 argument
+  proc fun(s: string): string {.  .} = discard
+  proc foo[T, S](op: proc (x: T): S {. cdecl .}): auto = 1
+  proc foo[T, S](op: proc (x: T): S {. safecall .}): auto = 1
+  echo foo(fun)
+
+block:
+  # bug #10285 Function signature don't match when inside seq/array/openArray
+  # Note: the error message now shows `closure` which helps debugging the issue
+  # out why it doesn't match
+  proc takesFunc(f: proc (x: int) {.gcsafe.}) =
+    echo "takes single Func"
+  proc takesFuncs(fs: openArray[proc (x: int) {.gcsafe.}]) =
+    echo "takes multiple Func"
+  takesFunc(proc (x: int) {.gcsafe.} = echo x)         # works
+  takesFuncs([proc (x: int) {.gcsafe.} = echo x])      # fails
+
+block:
+  # bug https://github.com/nim-lang/Nim/issues/11061#issuecomment-508970465
+  # better fix for removal of `errCannotBindXTwice` due to #3836
+  proc f(a0: uint8, b: string) = discard
+  f(10, a0 = 5, "")
+
+block:
+  # bug: https://github.com/nim-lang/Nim/issues/11061#issuecomment-508969796
+  # sigmatch gets confused with param/arg position after varargs
+  proc f(a1: int) = discard
+  proc f(a1: string, a2: varargs[string], a3: float, a4: var string) = discard
+  f("asdf", "1", "2", "3", "4", 2.3, "bad")
+
+block:
+  # bug: https://github.com/nim-lang/Nim/issues/11061#issuecomment-508970046
+  # err msg incorrectly said something is immutable
+  proc f(x: string, a0: var int) = discard
+  proc f(x: string, a0: string) = discard
+  var foo = ""
+  f(foo, a0 = 12)
+
+when true:
+  type Mystring = string
+  type MyInt = int
+  proc fun1(a1: MyInt, a2: Mystring) = discard
+  proc fun1(a1: float, a2: Mystring) = discard
+  fun1(Mystring.default, "asdf")
+
diff --git a/tests/errmsgs/tsigmatch2.nim b/tests/errmsgs/tsigmatch2.nim
new file mode 100644
index 000000000..31c966337
--- /dev/null
+++ b/tests/errmsgs/tsigmatch2.nim
@@ -0,0 +1,47 @@
+discard """
+  cmd: "nim check --showAllMismatches:on --hints:off $file"
+  nimout: '''
+tsigmatch2.nim(40, 14) Error: type mismatch: got <float64>
+but expected one of:
+proc foo(args: varargs[string, myproc]): string
+  first type mismatch at position: 1
+  required type for args: varargs[string]
+  but expression '1.2' is of type: float64
+proc foo(i: Foo): string
+  first type mismatch at position: 1
+  required type for i: Foo
+  but expression '1.2' is of type: float64
+
+expression: foo(1.2)
+tsigmatch2.nim(40, 14) Error: expression '' has no type (or is ambiguous)
+tsigmatch2.nim(46, 3) Error: type mismatch: got <int literal(1)>
+but expected one of:
+proc foo(args: varargs[string, myproc])
+  first type mismatch at position: 1
+  required type for args: varargs[string]
+  but expression '1' is of type: int literal(1)
+
+expression: foo 1
+'''
+  errormsg: "type mismatch"
+"""
+
+
+# line 30
+type Foo = object
+block: # issue #13182
+  proc myproc(a: int): string = $("myproc", a)
+  proc foo(args: varargs[string, myproc]): string = $args
+
+  proc foo(i: Foo): string = "in foo(i)"
+  static: doAssert foo(Foo()) == "in foo(i)"
+  static: doAssert foo(1) == """["(\"myproc\", 1)"]"""
+  doAssert not compiles(foo(1.2))
+  discard foo(1.2)
+
+block:
+  proc myproc[T](x: T): string =
+    let temp = 12.isNil
+  proc foo(args: varargs[string, myproc]) = discard
+  foo 1
+static: echo "done"
diff --git a/tests/errmsgs/tsimpletypecheck.nim b/tests/errmsgs/tsimpletypecheck.nim
new file mode 100644
index 000000000..422437d3a
--- /dev/null
+++ b/tests/errmsgs/tsimpletypecheck.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "type mismatch: got <bool> but expected \'string\'"
+  file: "tsimpletypecheck.nim"
+  line: 10
+"""
+# Test 2
+# Simple type checking
+
+var a: string
+a = false #ERROR
diff --git a/tests/errmsgs/tstaticexprnotype.nim b/tests/errmsgs/tstaticexprnotype.nim
new file mode 100644
index 000000000..8b6735ff8
--- /dev/null
+++ b/tests/errmsgs/tstaticexprnotype.nim
@@ -0,0 +1,5 @@
+discard """
+  action: reject
+"""
+
+let x = static: discard
diff --git a/tests/errmsgs/tstaticexprscope.nim b/tests/errmsgs/tstaticexprscope.nim
new file mode 100644
index 000000000..6969e389e
--- /dev/null
+++ b/tests/errmsgs/tstaticexprscope.nim
@@ -0,0 +1,11 @@
+discard """
+  errormsg: "undeclared identifier: 'z'"
+  line: 11
+"""
+
+# Open a new scope for static expr blocks
+block:
+  let a = static:
+    var z = 123
+    33
+  echo z
diff --git a/tests/errmsgs/tstaticresult.nim b/tests/errmsgs/tstaticresult.nim
new file mode 100644
index 000000000..bc534c7a8
--- /dev/null
+++ b/tests/errmsgs/tstaticresult.nim
@@ -0,0 +1,10 @@
+discard """
+errormsg: '''
+invalid type: 'static[int]' in this context: 'proc (x: int): static[int]' for proc
+'''
+"""
+
+proc foo(x: int): static int =
+  x + 123
+
+echo foo(123)
diff --git a/tests/errmsgs/tsubscriptmismatch.nim b/tests/errmsgs/tsubscriptmismatch.nim
new file mode 100644
index 000000000..a2b297b68
--- /dev/null
+++ b/tests/errmsgs/tsubscriptmismatch.nim
@@ -0,0 +1,11 @@
+discard """
+  matrix: "-d:testsConciseTypeMismatch"
+  nimout: '''
+[1] proc `[]`[T; U, V: Ordinal](s: openArray[T]; x: HSlice[U, V]): seq[T]
+'''
+"""
+
+type Foo = object
+let x = Foo()
+discard x[1] #[tt.Error
+         ^ type mismatch]#
diff --git a/tests/errmsgs/tsubscriptmismatch_legacy.nim b/tests/errmsgs/tsubscriptmismatch_legacy.nim
new file mode 100644
index 000000000..3e1f1eb71
--- /dev/null
+++ b/tests/errmsgs/tsubscriptmismatch_legacy.nim
@@ -0,0 +1,10 @@
+discard """
+  nimout: '''
+  but expression 'x' is of type: Foo
+'''
+"""
+
+type Foo = object
+let x = Foo()
+discard x[1] #[tt.Error
+         ^ type mismatch: got <Foo, int literal(1)>]#
diff --git a/tests/errmsgs/ttupleindexoutofbounds.nim b/tests/errmsgs/ttupleindexoutofbounds.nim
new file mode 100644
index 000000000..ae634dddb
--- /dev/null
+++ b/tests/errmsgs/ttupleindexoutofbounds.nim
@@ -0,0 +1,2 @@
+let a = (1, 2)[4] #[tt.Error
+              ^ invalid index 4 in subscript for tuple of length 2]#
diff --git a/tests/errmsgs/ttypeAllowed.nim b/tests/errmsgs/ttypeAllowed.nim
new file mode 100644
index 000000000..fdb4c70b8
--- /dev/null
+++ b/tests/errmsgs/ttypeAllowed.nim
@@ -0,0 +1,28 @@
+discard """
+cmd: "nim check $file"
+errormsg: ""
+nimout: '''
+ttypeAllowed.nim(13, 5) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe.}' for let
+ttypeAllowed.nim(17, 7) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe.}' for const
+ttypeAllowed.nim(21, 5) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe.}' for var
+ttypeAllowed.nim(26, 10) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe.}' for result
+'''
+"""
+
+
+let f1 = case true
+  of true:  countup[int]
+  of false: countdown[int]
+
+const f2 = case true
+  of true:  countup[int]
+  of false: countdown[int]
+
+var f3 = case true
+  of true:  countup[int]
+  of false: countdown[int]
+
+proc foobar(): auto =
+  result = case true
+    of true:  countup[int]
+    of false: countdown[int]
diff --git a/tests/errmsgs/tuncheckedarrayvar.nim b/tests/errmsgs/tuncheckedarrayvar.nim
new file mode 100644
index 000000000..9376a0150
--- /dev/null
+++ b/tests/errmsgs/tuncheckedarrayvar.nim
@@ -0,0 +1,7 @@
+discard """
+errormsg: '''
+invalid type: 'UncheckedArray[uint8]' for var
+'''
+"""
+
+var byteUA: UncheckedArray[uint8]
diff --git a/tests/errmsgs/tundeclared_field.nim b/tests/errmsgs/tundeclared_field.nim
new file mode 100644
index 000000000..5668050e0
--- /dev/null
+++ b/tests/errmsgs/tundeclared_field.nim
@@ -0,0 +1,50 @@
+discard """
+cmd: '''nim check --hints:off $file'''
+action: reject
+nimout: '''
+tundeclared_field.nim(25, 12) Error: undeclared field: 'bad1' for type tundeclared_field.A [type declared in tundeclared_field.nim(22, 8)]
+tundeclared_field.nim(30, 17) Error: undeclared field: 'bad2' for type tundeclared_field.A [type declared in tundeclared_field.nim(28, 8)]
+tundeclared_field.nim(36, 4) Error: undeclared field: 'bad3' for type tundeclared_field.A [type declared in tundeclared_field.nim(33, 8)]
+tundeclared_field.nim(42, 12) Error: undeclared field: 'bad4' for type tundeclared_field.B [type declared in tundeclared_field.nim(39, 8)]
+tundeclared_field.nim(43, 4) Error: undeclared field: 'bad5' for type tundeclared_field.B [type declared in tundeclared_field.nim(39, 8)]
+tundeclared_field.nim(44, 23) Error: undeclared field: 'bad6' for type tundeclared_field.B [type declared in tundeclared_field.nim(39, 8)]
+tundeclared_field.nim(46, 19) Error: undeclared field: 'bad7' for type tundeclared_field.B [type declared in tundeclared_field.nim(39, 8)]
+tundeclared_field.nim(50, 13) Error: cannot instantiate Foo [type declared in tundeclared_field.nim(49, 8)]
+'''
+"""
+
+#[
+xxx in future work, generic instantiations (e.g. `B[int]`) should be shown with their instantiation instead of `tundeclared_field.B`,
+maybe using TPreferedDesc.preferResolved or preferMixed
+]#
+# line 20
+block:
+  type A = object
+    a0: int
+  var a: A
+  discard a.bad1
+
+block:
+  type A = object
+    a0: int
+  var a = A(bad2: 0)
+
+block:
+  type A = object
+    a0: int
+  var a: A
+  a.bad3 = 0
+
+block:
+  type B[T] = object
+    b0: int
+  var b: B[int]
+  discard b.bad4
+  b.bad5 = 0
+  var b2 = B[int](bad6: 0)
+  type Bi = B[int]
+  var b3 = Bi(bad7: 0)
+
+block:
+  type Foo[T: SomeInteger] = object
+  var a: Foo[float]
diff --git a/tests/errmsgs/tundeclared_routine.nim b/tests/errmsgs/tundeclared_routine.nim
new file mode 100644
index 000000000..41b1d35f4
--- /dev/null
+++ b/tests/errmsgs/tundeclared_routine.nim
@@ -0,0 +1,44 @@
+discard """
+cmd: '''nim check --hints:off $file'''
+action: reject
+nimout: '''
+tundeclared_routine.nim(24, 17) Error: attempting to call routine: 'myiter'
+  found tundeclared_routine.myiter(a: string) [iterator declared in tundeclared_routine.nim(22, 12)]
+  found tundeclared_routine.myiter() [iterator declared in tundeclared_routine.nim(23, 12)]
+tundeclared_routine.nim(29, 28) Error: invalid pragma: myPragma
+tundeclared_routine.nim(36, 13) Error: undeclared field: 'bar3' for type tundeclared_routine.Foo [type declared in tundeclared_routine.nim(33, 8)]
+  found tundeclared_routine.bar3() [iterator declared in tundeclared_routine.nim(35, 12)]
+tundeclared_routine.nim(41, 13) Error: undeclared field: 'bar4' for type tundeclared_routine.Foo [type declared in tundeclared_routine.nim(39, 8)]
+tundeclared_routine.nim(44, 11) Error: undeclared identifier: 'bad5'
+'''
+"""
+
+
+
+
+
+# line 20
+block:
+  iterator myiter(a:string): int = discard
+  iterator myiter(): int = discard
+  let a = myiter(1)
+
+block:
+  proc myPragma():int=discard
+  iterator myPragma():int=discard
+  proc myfun(a:int): int {.myPragma.} = 1
+  let a = myfun(1)
+
+block:
+  type Foo = object
+  var a = Foo()
+  iterator bar3():int=discard
+  let a2 = a.bar3
+
+block:
+  type Foo = object
+  var a = Foo()
+  let a2 = a.bar4
+
+block:
+  let a = bad5(1)
diff --git a/tests/errmsgs/tundeclared_routine_compiles.nim b/tests/errmsgs/tundeclared_routine_compiles.nim
new file mode 100644
index 000000000..21daf82bf
--- /dev/null
+++ b/tests/errmsgs/tundeclared_routine_compiles.nim
@@ -0,0 +1,11 @@
+# D20180828T234921:here
+template foo*(iter: untyped): untyped =
+  when compiles(iter.unexistingField): 0
+  elif compiles(iter.len): 1
+  else: 2
+
+proc foo[A]()=
+  let a2 = @[10, 11]
+  let a3 = foo(pairs(a2))
+
+foo[int]()
diff --git a/tests/errmsgs/tunknown_named_parameter.nim b/tests/errmsgs/tunknown_named_parameter.nim
new file mode 100644
index 000000000..d3dd6cd2d
--- /dev/null
+++ b/tests/errmsgs/tunknown_named_parameter.nim
@@ -0,0 +1,27 @@
+discard """
+cmd: "nim check $file"
+errormsg: "type mismatch: got <string, set[char], maxsplits: int literal(1)>"
+nimout: '''
+func rsplit(s: string; sep: char; maxsplit: int = -1): seq[string]
+  first type mismatch at position: 2
+  required type for sep: char
+  but expression '{':'}' is of type: set[char]
+func rsplit(s: string; sep: string; maxsplit: int = -1): seq[string]
+  first type mismatch at position: 2
+  required type for sep: string
+  but expression '{':'}' is of type: set[char]
+func rsplit(s: string; seps: set[char] = Whitespace; maxsplit: int = -1): seq[
+    string]
+  first type mismatch at position: 3
+  unknown named parameter: maxsplits
+
+expression: rsplit("abc:def", {':'}, maxsplits = 1)
+'''
+"""
+
+
+# bug #8043
+
+
+import strutils
+"abc:def".rsplit({':'}, maxsplits = 1)
diff --git a/tests/errmsgs/tunresolvedinnerproc.nim b/tests/errmsgs/tunresolvedinnerproc.nim
new file mode 100644
index 000000000..7655a5a41
--- /dev/null
+++ b/tests/errmsgs/tunresolvedinnerproc.nim
@@ -0,0 +1,10 @@
+proc wrap[T]() =
+  proc notConcrete[T](x, y: int): int =
+    var dummy: T
+    result = x - y
+
+  var x: proc (x, y: T): int
+  x = notConcrete #[tt.Error
+      ^ 'notConcrete' doesn't have a concrete type, due to unspecified generic parameters.]#
+
+wrap[int]()
diff --git a/tests/errmsgs/tuntypedoverload.nim b/tests/errmsgs/tuntypedoverload.nim
new file mode 100644
index 000000000..1b1c2809c
--- /dev/null
+++ b/tests/errmsgs/tuntypedoverload.nim
@@ -0,0 +1,37 @@
+discard """
+  cmd: "nim check $file"
+"""
+
+block:
+  template foo(x: var int, y: untyped) = discard
+  var a: float
+  foo(a, undeclared) #[tt.Error
+     ^ type mismatch: got <float, untyped>]# # `untyped` is arbitary
+  # previous error: undeclared identifier: 'undeclared'
+
+block: # issue #8697
+  type
+    Fruit = enum
+      apple
+      banana
+      orange
+  macro hello(x, y: untyped) = discard
+  hello(apple, banana, orange) #[tt.Error
+       ^ type mismatch: got <Fruit, Fruit, Fruit>]#
+
+block: # issue #23265
+  template declareFoo(fooName: untyped, value: uint16) =
+    const `fooName Value` {.inject.} = value
+
+  declareFoo(FOO, 0xFFFF)
+  declareFoo(BAR, 0xFFFFF) #[tt.Error
+            ^ type mismatch: got <untyped, int literal(1048575)>]#
+
+block: # issue #9620
+  template forLoop(index: untyped, length: int{lvalue}, body: untyped) =
+    for `index`{.inject.} in 0 ..< length:
+      body
+  var x = newSeq[int](10)
+  forLoop(i, x.len): #[tt.Error
+         ^ type mismatch: got <untyped, int, void>]#
+    x[i] = i
diff --git a/tests/errmsgs/twrong_at_operator.nim b/tests/errmsgs/twrong_at_operator.nim
index b6b3d101f..438186f01 100644
--- a/tests/errmsgs/twrong_at_operator.nim
+++ b/tests/errmsgs/twrong_at_operator.nim
@@ -1,14 +1,20 @@
 discard """
-errormsg: "type mismatch: got <array[0..0, type int]>"
-line: 15
+errormsg: "type mismatch: got <array[0..0, typedesc[int]]>"
 nimout: '''
-twrong_at_operator.nim(15, 30) Error: type mismatch: got <array[0..0, type int]>
+twrong_at_operator.nim(21, 30) Error: type mismatch: got <array[0..0, typedesc[int]]>
 but expected one of:
+proc `@`[IDX, T](a: sink array[IDX, T]): seq[T]
+  first type mismatch at position: 1
+  required type for a: sink array[IDX, T]
+  but expression '[int]' is of type: array[0..0, typedesc[int]]
 proc `@`[T](a: openArray[T]): seq[T]
-proc `@`[IDX, T](a: array[IDX, T]): seq[T]
+  first type mismatch at position: 1
+  required type for a: openArray[T]
+  but expression '[int]' is of type: array[0..0, typedesc[int]]
 
 expression: @[int]
 '''
+disabled: "32bit"
 """
 
 # bug #7331
diff --git a/tests/errmsgs/twrong_explicit_typeargs.nim b/tests/errmsgs/twrong_explicit_typeargs.nim
new file mode 100644
index 000000000..5236e5f4f
--- /dev/null
+++ b/tests/errmsgs/twrong_explicit_typeargs.nim
@@ -0,0 +1,26 @@
+discard """
+  cmd: "nim c --hints:off -d:testsConciseTypeMismatch $file"
+  action: reject
+  nimout: '''
+twrong_explicit_typeargs.nim(26, 29) Error: type mismatch
+Expression: newImage[string](320, 200)
+  [1] 320: int literal(320)
+  [2] 200: int literal(200)
+
+Expected one of (first mismatch at [position]):
+[1] proc newImage[T: int32 | int64](w, h: int): ref Image[T]
+  generic parameter mismatch, expected int32 or int64 but got 'string' of type: string
+'''
+"""
+
+# bug #4084
+type
+  Image[T] = object
+    data: seq[T]
+
+proc newImage[T: int32|int64](w, h: int): ref Image[T] =
+  new(result)
+  result.data = newSeq[T](w * h)
+
+var correct = newImage[int32](320, 200)
+var wrong = newImage[string](320, 200)
diff --git a/tests/errmsgs/twrong_explicit_typeargs_legacy.nim b/tests/errmsgs/twrong_explicit_typeargs_legacy.nim
new file mode 100644
index 000000000..cfa528c54
--- /dev/null
+++ b/tests/errmsgs/twrong_explicit_typeargs_legacy.nim
@@ -0,0 +1,25 @@
+discard """
+  action: reject
+  nimout: '''
+twrong_explicit_typeargs_legacy.nim(25, 29) Error: type mismatch: got <int literal(320), int literal(200)>
+but expected one of:
+proc newImage[T: int32 | int64](w, h: int): ref Image[T]
+  first type mismatch at position: 1 in generic parameters
+  required type for T: int32 or int64
+  but expression 'string' is of type: string
+
+expression: newImage[string](320, 200)
+'''
+"""
+
+# bug #4084
+type
+  Image[T] = object
+    data: seq[T]
+
+proc newImage[T: int32|int64](w, h: int): ref Image[T] =
+  new(result)
+  result.data = newSeq[T](w * h)
+
+var correct = newImage[int32](320, 200)
+var wrong = newImage[string](320, 200)
diff --git a/tests/errmsgs/twrongcolon.nim b/tests/errmsgs/twrongcolon.nim
index 6f5cc3e5d..06e802eb7 100644
--- a/tests/errmsgs/twrongcolon.nim
+++ b/tests/errmsgs/twrongcolon.nim
@@ -1,11 +1,10 @@
 discard """
-errormsg: "in expression '("
+errormsg: "in expression ' do:"
 nimout: '''
-Error: in expression '(
-  890)': identifier expected, but found ''
+twrongcolon.nim(10, 12) Error: in expression ' do:
+  890': identifier expected, but found ''
 '''
 
-line: 11
 """
 
 var n: int : 890
diff --git a/tests/exception/m21261.nim b/tests/exception/m21261.nim
new file mode 100644
index 000000000..11b12fb5b
--- /dev/null
+++ b/tests/exception/m21261.nim
@@ -0,0 +1 @@
+raise (ref Exception)(msg: "something")
\ No newline at end of file
diff --git a/tests/exception/m22469.nim b/tests/exception/m22469.nim
new file mode 100644
index 000000000..201698701
--- /dev/null
+++ b/tests/exception/m22469.nim
@@ -0,0 +1,4 @@
+# ModuleB
+echo "First top-level statement of ModuleB"
+echo high(int) + 1
+echo "ModuleB last statement"
\ No newline at end of file
diff --git a/tests/exception/t13115.nim b/tests/exception/t13115.nim
new file mode 100644
index 000000000..5db8f9107
--- /dev/null
+++ b/tests/exception/t13115.nim
@@ -0,0 +1,31 @@
+const msg = "This char is `" & '\0' & "` and works fine!"
+
+when defined nim_t13115:
+  # bug #13115
+  template fn =
+    raise newException(Exception, msg)
+  when defined nim_t13115_static:
+    static: fn()
+  fn()
+else:
+  import std/[osproc,strformat,os,strutils]
+  proc main =
+    const nim = getCurrentCompilerExe()
+    const file = currentSourcePath
+    for b in "c js cpp".split:
+      # save CI time by avoiding mostly redundant combinations as far as this bug is concerned
+      var opts = case b
+        of "c": @["", "-d:nim_t13115_static", "-d:danger", "-d:debug"]
+        of "js": @["", "-d:nim_t13115_static"]
+        else: @[""]
+
+      for opt in opts:
+        let cmd = fmt"{nim} r -b:{b} -d:nim_t13115 {opt} --hints:off {file}"
+        let (outp, exitCode) = execCmdEx(cmd)
+        when defined windows:
+          # `\0` not preserved on windows
+          doAssert "` and works fine!" in outp, cmd & "\n" & msg
+        else:
+          doAssert msg in outp, cmd & "\n" & msg
+        doAssert exitCode == 1
+  main()
diff --git a/tests/exception/t18620.nim b/tests/exception/t18620.nim
new file mode 100644
index 000000000..ee23f8bac
--- /dev/null
+++ b/tests/exception/t18620.nim
@@ -0,0 +1,17 @@
+discard """
+  matrix: "--gc:arc; --gc:refc"
+"""
+
+proc hello() =
+  raise newException(ValueError, "You are wrong")
+
+var flag = false
+
+try:
+  hello()
+except ValueError as e:
+  flag = true
+  doAssert len(getStackTraceEntries(e)) > 0
+  doAssert len(getStackTraceEntries(e)) > 0
+
+doAssert flag
diff --git a/tests/exception/t20613.nim b/tests/exception/t20613.nim
new file mode 100644
index 000000000..6edb69415
--- /dev/null
+++ b/tests/exception/t20613.nim
@@ -0,0 +1,8 @@
+discard """
+  matrix: "; --panics:on"
+"""
+
+func test =
+  if 0 > 10:
+    raiseAssert "hey"
+test()
diff --git a/tests/exception/t21261.nim b/tests/exception/t21261.nim
new file mode 100644
index 000000000..84817d854
--- /dev/null
+++ b/tests/exception/t21261.nim
@@ -0,0 +1,9 @@
+discard """
+  exitcode: 1
+  outputsub: '''
+m21261.nim(1)            m21261
+Error: unhandled exception: something [Exception]
+'''
+"""
+
+import m21261
\ No newline at end of file
diff --git a/tests/exception/t22008.nim b/tests/exception/t22008.nim
new file mode 100644
index 000000000..c0758e7b4
--- /dev/null
+++ b/tests/exception/t22008.nim
@@ -0,0 +1,8 @@
+template detect(v: untyped) =
+  doAssert typeof(v) is int
+
+detect:
+  try:
+    raise (ref ValueError)()
+  except ValueError:
+    42
\ No newline at end of file
diff --git a/tests/exception/t22469.nim b/tests/exception/t22469.nim
new file mode 100644
index 000000000..a76c74967
--- /dev/null
+++ b/tests/exception/t22469.nim
@@ -0,0 +1,16 @@
+discard """
+  exitcode: 1
+  output: '''
+First top-level statement of ModuleB
+m22469.nim(3)            m22469
+fatal.nim(53)            sysFatal
+Error: unhandled exception: over- or underflow [OverflowDefect]
+'''
+"""
+
+# bug #22469
+
+# ModuleA
+import m22469
+echo "ModuleA about to have exception"
+echo high(int) + 1
diff --git a/tests/exception/t9657.nim b/tests/exception/t9657.nim
new file mode 100644
index 000000000..6ac525a70
--- /dev/null
+++ b/tests/exception/t9657.nim
@@ -0,0 +1,12 @@
+discard """
+  action: run
+  exitcode: 1
+  targets: "c cpp"
+  disabled: "openbsd"
+  disabled: "netbsd"
+"""
+
+close stdmsg
+const m = "exception!"
+# see #10343 for details on this test
+discard writeBuffer(stdmsg, cstring(m), m.len)
diff --git a/tests/exception/tcontinuexc.nim b/tests/exception/tcontinuexc.nim
index fb9b523d7..b7560a605 100644
--- a/tests/exception/tcontinuexc.nim
+++ b/tests/exception/tcontinuexc.nim
@@ -1,11 +1,10 @@
 discard """
-  file: "tcontinuexc.nim"
   outputsub: "ECcaught"
   exitcode: "1"
 """
 type
-  ESomething = object of E_Base
-  ESomeOtherErr = object of E_Base
+  ESomething = object of Exception
+  ESomeOtherErr = object of Exception
 
 proc genErrors(s: string) =
   if s == "error!":
@@ -25,6 +24,3 @@ finally:
   echo "caught"
 
 #OUT ECcaught
-
-
-
diff --git a/tests/exception/tcpp_imported_exc.nim b/tests/exception/tcpp_imported_exc.nim
index c8349f7d5..55a58440f 100644
--- a/tests/exception/tcpp_imported_exc.nim
+++ b/tests/exception/tcpp_imported_exc.nim
@@ -1,6 +1,8 @@
 discard """
+matrix: "--mm:refc"
 targets: "cpp"
-output: '''caught as std::exception
+output: '''
+caught as std::exception
 expected
 finally1
 finally2
@@ -12,9 +14,10 @@ finally 2
 expected
 cpp exception caught
 '''
+disabled: "windows" # pending bug #18011
 """
 
-type 
+type
   std_exception* {.importcpp: "std::exception", header: "<exception>".} = object
   std_runtime_error* {.importcpp: "std::runtime_error", header: "<stdexcept>".} = object
   std_string* {.importcpp: "std::string", header: "<string>".} = object
@@ -25,7 +28,7 @@ proc constructRuntimeError(s: stdstring): std_runtime_error {.importcpp: "std::r
 
 proc what(ex: std_runtime_error): cstring {.importcpp: "((char *)#.what())".}
 
-proc myexception = 
+proc myexception =
   raise constructRuntimeError(constructStdString("cpp_exception"))
 
 try:
@@ -41,17 +44,17 @@ except std_exception:
 
 doAssert(getCurrentException() == nil)
 
-proc earlyReturn = 
+proc earlyReturn =
   try:
     try:
-        myexception()
+      myexception()
     finally:
       echo "finally1"
   except:
     return
   finally:
     echo "finally2"
-  
+
 earlyReturn()
 doAssert(getCurrentException() == nil)
 
@@ -74,7 +77,7 @@ doAssert(getCurrentException() == nil)
 # raise by pointer and also generic type
 
 type
-  std_vector {.importcpp"std::vector", header"<vector>".} [T] = object
+  std_vector[T] {.importcpp"std::vector", header"<vector>".} = object
 
 proc newVector[T](len: int): ptr std_vector[T] {.importcpp: "new std::vector<'1>(@)".}
 proc deleteVector[T](v: ptr std_vector[T]) {.importcpp: "delete @; @ = NIM_NIL;".}
@@ -118,16 +121,15 @@ try:
     echo "finally 2"
 except:
   echo "expected"
-  
-  
+
 doAssert(getCurrentException() == nil)
 
 try:
-    try:
-      myexception()
-    except std_runtime_error as ex:
-      echo "cpp exception caught"
-      raise newException(ValueError, "rewritten " & $ex.what())
+  try:
+    myexception()
+  except std_runtime_error as ex:
+    echo "cpp exception caught"
+    raise newException(ValueError, "rewritten " & $ex.what())
 except:
   doAssert(getCurrentExceptionMsg() == "rewritten cpp_exception")
 
diff --git a/tests/exception/tcpp_imported_exc2.nim b/tests/exception/tcpp_imported_exc2.nim
new file mode 100644
index 000000000..ff299ea3d
--- /dev/null
+++ b/tests/exception/tcpp_imported_exc2.nim
@@ -0,0 +1,10 @@
+discard """
+targets: "cpp"
+output: ""
+"""
+#issue #14369 case 2
+type RuntimeError {.requiresInit, importcpp: "std::runtime_error", header: "<stdexcept>".} = object
+
+proc initRuntimeError(a: cstring): RuntimeError {.importcpp: "std::runtime_error(@)", constructor.}
+try: raise initRuntimeError("foo2")
+except: discard
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/exception/tdont_overwrite_typename.nim b/tests/exception/tdont_overwrite_typename.nim
index 6e3ff816f..d6dca0990 100644
--- a/tests/exception/tdont_overwrite_typename.nim
+++ b/tests/exception/tdont_overwrite_typename.nim
@@ -7,10 +7,10 @@ Check passed'''
 # bug #5628
 
 proc checkException(ex: ref Exception) =
-  doAssert(ex.name == "ValueError")
+  doAssert(ex.name == cstring"ValueError")
   doAssert(ex.msg == "SecondException")
   doAssert(ex.parent != nil)
-  doAssert(ex.parent.name == "KeyError")
+  doAssert(ex.parent.name == cstring"KeyError")
   doAssert(ex.parent.msg == "FirstException")
   echo "Check passed"
 
diff --git a/tests/exception/testindexerroroutput.nims b/tests/exception/testindexerroroutput.nims
new file mode 100644
index 000000000..e282f14b4
--- /dev/null
+++ b/tests/exception/testindexerroroutput.nims
@@ -0,0 +1,23 @@
+mode = ScriptMode.Verbose
+
+case paramStr(3):
+  of "test1":
+    #543
+    block:
+      let s = "abc"
+      discard s[len(s)]
+  of "test2":
+    #537
+    block:
+      var s = "abc"
+      s[len(s)] = 'd'
+  of "test3":
+    #588
+    block:
+      let arr = ['a', 'b', 'c']
+      discard arr[len(arr)]
+  of "test4":
+    #588
+    block:
+      var arr = ['a', 'b', 'c']
+      arr[len(arr)] = 'd'
diff --git a/tests/exception/texcas.nim b/tests/exception/texcas.nim
index 7108e334c..ad6819f11 100644
--- a/tests/exception/texcas.nim
+++ b/tests/exception/texcas.nim
@@ -1,8 +1,9 @@
 discard """
   targets: "c cpp"
-  output: '''Hello
+  output: '''
 Hello
-  '''
+Hello
+'''
 """
 proc test[T]() =
   try:
diff --git a/tests/exception/texception_inference.nim b/tests/exception/texception_inference.nim
new file mode 100644
index 000000000..7dd01cca1
--- /dev/null
+++ b/tests/exception/texception_inference.nim
@@ -0,0 +1,32 @@
+discard """

+  output: '''good'''

+  cmd: "nim c --gc:orc -d:release $file"

+"""

+

+type

+  Raising[T, E] = object

+

+proc foo[T, Errors](x: proc (x: Raising[T, Errors])) {.raises: Errors.} =

+  discard

+

+proc callback(x: Raising[int, ValueError]) =

+  echo "callback"

+

+proc xy() {.raises: [ValueError].} =

+  foo callback

+

+proc x[E]() {.raises: [E, IOError].} =

+  raise newException(E, "text here")

+

+try:

+  x[ValueError]()

+except ValueError:

+  echo "good"

+

+proc callback2(x: Raising[int, IOError]) =

+  discard

+

+proc foo2[T, OtherErrors](x: proc(x: Raising[T, OtherErrors])) {.raises: [ValueError, OtherErrors].} =

+  discard

+

+foo2 callback2

diff --git a/tests/exception/texceptionbreak.nim b/tests/exception/texceptionbreak.nim
index 00dd8ed9f..b8ce7eead 100644
--- a/tests/exception/texceptionbreak.nim
+++ b/tests/exception/texceptionbreak.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tnestedbreak.nim"
   output: "1\n2\n3\n4"
 """
 
@@ -30,16 +29,16 @@ echo "2"
 try:
   raise newException(OSError, "Problem")
 except OSError:
-  block:
-    break
+  block label:
+    break label
 
 echo "3"
 
 # Fourth Variety
-block:
+block label:
   try:
     raise newException(OSError, "Problem")
   except OSError:
-    break
+    break label
 
 echo "4"
diff --git a/tests/exception/texceptions.nim b/tests/exception/texceptions.nim
index b30b3874b..62d24c934 100644
--- a/tests/exception/texceptions.nim
+++ b/tests/exception/texceptions.nim
@@ -1,5 +1,8 @@
 discard """
+  disabled: "windows" # no sigsetjmp() there
+  matrix: "-d:nimStdSetjmp; -d:nimSigSetjmp; -d:nimRawSetjmp; -d:nimBuiltinSetjmp"
   output: '''
+
 BEFORE
 FINALLY
 
@@ -16,7 +19,7 @@ FINALLY
 
 echo ""
 
-proc no_expcetion =
+proc no_exception =
   try:
     echo "BEFORE"
 
@@ -27,7 +30,7 @@ proc no_expcetion =
   finally:
     echo "FINALLY"
 
-try: no_expcetion()
+try: no_exception()
 except: echo "RECOVER"
 
 echo ""
@@ -64,3 +67,64 @@ proc return_in_except =
 try: return_in_except()
 except: echo "RECOVER"
 
+block: #10417
+  proc moo() {.noreturn.} = discard
+
+  let bar =
+    try:
+      1
+    except:
+      moo()
+
+  doAssert(bar == 1)
+
+# Make sure the VM handles the exceptions correctly
+block:
+  proc fun1(): seq[int] =
+    try:
+      try:
+        raise newException(ValueError, "xx")
+      except:
+        doAssert("xx" == getCurrentExceptionMsg())
+        raise newException(KeyError, "yy")
+    except:
+      doAssert("yy" == getCurrentExceptionMsg())
+      result.add(1212)
+    try:
+      try:
+        raise newException(AssertionDefect, "a")
+      finally:
+        result.add(42)
+    except AssertionDefect:
+      result.add(99)
+    finally:
+      result.add(10)
+    result.add(4)
+    result.add(0)
+    try:
+      result.add(1)
+    except KeyError:
+      result.add(-1)
+    except ValueError:
+      result.add(-1)
+    except IndexDefect:
+      result.add(2)
+    except:
+      result.add(3)
+
+    try:
+      try:
+        result.add(1)
+        return
+      except:
+        result.add(-1)
+      finally:
+        result.add(2)
+    except KeyError:
+      doAssert(false)
+    finally:
+      result.add(3)
+
+  let x1 = fun1()
+  const x2 = fun1()
+  doAssert(x1 == x2)
diff --git a/tests/exception/texceptions2.nim b/tests/exception/texceptions2.nim
new file mode 100644
index 000000000..97fd856a0
--- /dev/null
+++ b/tests/exception/texceptions2.nim
@@ -0,0 +1,130 @@
+discard """
+  disabled: "posix" # already covered by texceptions.nim
+  matrix: "-d:nimStdSetjmp; -d:nimRawSetjmp; -d:nimBuiltinSetjmp"
+  output: '''
+
+BEFORE
+FINALLY
+
+BEFORE
+EXCEPT
+FINALLY
+RECOVER
+
+BEFORE
+EXCEPT: IOError: hi
+FINALLY
+'''
+"""
+
+echo ""
+
+proc no_exception =
+  try:
+    echo "BEFORE"
+
+  except:
+    echo "EXCEPT"
+    raise
+
+  finally:
+    echo "FINALLY"
+
+try: no_exception()
+except: echo "RECOVER"
+
+echo ""
+
+proc reraise_in_except =
+  try:
+    echo "BEFORE"
+    raise newException(IOError, "")
+
+  except IOError:
+    echo "EXCEPT"
+    raise
+
+  finally:
+    echo "FINALLY"
+
+try: reraise_in_except()
+except: echo "RECOVER"
+
+echo ""
+
+proc return_in_except =
+  try:
+    echo "BEFORE"
+    raise newException(IOError, "hi")
+
+  except:
+    echo "EXCEPT: ", getCurrentException().name, ": ", getCurrentExceptionMsg()
+    return
+
+  finally:
+    echo "FINALLY"
+
+try: return_in_except()
+except: echo "RECOVER"
+
+block: #10417
+  proc moo() {.noreturn.} = discard
+
+  let bar =
+    try:
+      1
+    except:
+      moo()
+
+  doAssert(bar == 1)
+
+# Make sure the VM handles the exceptions correctly
+block:
+  proc fun1(): seq[int] =
+    try:
+      try:
+        raise newException(ValueError, "xx")
+      except:
+        doAssert("xx" == getCurrentExceptionMsg())
+        raise newException(KeyError, "yy")
+    except:
+      doAssert("yy" == getCurrentExceptionMsg())
+      result.add(1212)
+    try:
+      try:
+        raise newException(AssertionDefect, "a")
+      finally:
+        result.add(42)
+    except AssertionDefect:
+      result.add(99)
+    finally:
+      result.add(10)
+    result.add(4)
+    result.add(0)
+    try:
+      result.add(1)
+    except KeyError:
+      result.add(-1)
+    except ValueError:
+      result.add(-1)
+    except IndexDefect:
+      result.add(2)
+    except:
+      result.add(3)
+
+    try:
+      try:
+        result.add(1)
+        return
+      except:
+        result.add(-1)
+      finally:
+        result.add(2)
+    except KeyError:
+      doAssert(false)
+    finally:
+      result.add(3)
+
+  let x1 = fun1()
+  const x2 = fun1()
+  doAssert(x1 == x2)
diff --git a/tests/exception/texcpt1.nim b/tests/exception/texcpt1.nim
index 50a95eeec..835f3610a 100644
--- a/tests/exception/texcpt1.nim
+++ b/tests/exception/texcpt1.nim
@@ -4,6 +4,8 @@ discard """
 type
   ESomething = object of Exception
   ESomeOtherErr = object of Exception
+  ESomethingGen[T] = object of Exception
+  ESomethingGenRef[T] = ref object of Exception
 
 proc genErrors(s: string) =
   if s == "error!":
@@ -27,4 +29,17 @@ proc blah(): int =
 
 echo blah()
 
+# Issue #7845, raise generic exception
+var x: ref ESomethingGen[int]
+new(x)
+try:
+  raise x
+except ESomethingGen[int] as e:
+  discard
 
+try:
+  raise new(ESomethingGenRef[int])
+except ESomethingGenRef[int] as e:
+  discard
+except:
+  discard
\ No newline at end of file
diff --git a/tests/exception/texcsub.nim b/tests/exception/texcsub.nim
index 02125d2c0..463e95613 100644
--- a/tests/exception/texcsub.nim
+++ b/tests/exception/texcsub.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "texcsub.nim"
   output: "caught!"
 """
 # Test inheritance for exception matching:
@@ -12,6 +11,3 @@ except:
   echo "wtf!?"
 
 #OUT caught!
-
-
-
diff --git a/tests/exception/tfinally.nim b/tests/exception/tfinally.nim
index 7a218b444..c5b1dd841 100644
--- a/tests/exception/tfinally.nim
+++ b/tests/exception/tfinally.nim
@@ -1,6 +1,6 @@
 discard """
-  file: "tfinally.nim"
-  output: '''came
+  output: '''
+came
 here
 3
 msg1
@@ -59,4 +59,4 @@ try:
 except:
   echo "except2"
 finally:
-  echo "finally2"
\ No newline at end of file
+  echo "finally2"
diff --git a/tests/exception/tfinally2.nim b/tests/exception/tfinally2.nim
index f1acf2774..dae1a468a 100644
--- a/tests/exception/tfinally2.nim
+++ b/tests/exception/tfinally2.nim
@@ -1,9 +1,10 @@
 discard """
-  file: "tfinally2.nim"
-  output: '''A
+output: '''
+A
 B
 C
-D'''
+D
+'''
 """
 # Test break in try statement:
 
@@ -24,7 +25,4 @@ proc main: int =
   finally:
     echo("D")
 
-discard main() #OUT ABCD
-
-
-
+discard main()
diff --git a/tests/exception/tfinally3.nim b/tests/exception/tfinally3.nim
index 6098672a2..9053d397d 100644
--- a/tests/exception/tfinally3.nim
+++ b/tests/exception/tfinally3.nim
@@ -1,7 +1,8 @@
 discard """
-  file: "tfinally3.nim"
-  outputsub: '''false
-Within finally->try'''
+  outputsub: '''
+false
+Within finally->try
+'''
   exitCode: 1
 """
 # Test break in try statement:
diff --git a/tests/exception/tfinally4.nim b/tests/exception/tfinally4.nim
index 3aa707ff6..a7dbbffef 100644
--- a/tests/exception/tfinally4.nim
+++ b/tests/exception/tfinally4.nim
@@ -1,6 +1,39 @@
 discard """
-  file: "tfinally4.nim"
-  output: "B1\nA1\n1\nB1\nB2\ncatch\nA1\n1\nB1\nA1\nA2\n2\nB1\nB2\ncatch\nA1\nA2\n0\nB1\nA1\n1\nB1\nB2\nA1\n1\nB1\nA1\nA2\n2\nB1\nB2\nA1\nA2\n3"
+  output: '''
+B1
+A1
+1
+B1
+B2
+catch
+A1
+1
+B1
+A1
+A2
+2
+B1
+B2
+catch
+A1
+A2
+0
+B1
+A1
+1
+B1
+B2
+A1
+1
+B1
+A1
+A2
+2
+B1
+B2
+A1
+A2
+3'''
 """
 
 # More thorough test of return-in-finaly
diff --git a/tests/exception/tnestedreturn.nim b/tests/exception/tnestedreturn.nim
index bf26f4903..acb83d2c8 100644
--- a/tests/exception/tnestedreturn.nim
+++ b/tests/exception/tnestedreturn.nim
@@ -1,6 +1,5 @@
 discard """
   targets: "c cpp"
-  file: "tnestedreturn.nim"
   output: "A\nB\nC\n"
 """
 
diff --git a/tests/exception/tnestedreturn2.nim b/tests/exception/tnestedreturn2.nim
index 79523a883..167d09b96 100644
--- a/tests/exception/tnestedreturn2.nim
+++ b/tests/exception/tnestedreturn2.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tnestedreturn2.nim"
   outputsub: "Error: unhandled exception: Problem [OSError]"
   exitcode: "1"
 """
diff --git a/tests/exception/tonraise.nim b/tests/exception/tonraise.nim
deleted file mode 100644
index a155f0b8e..000000000
--- a/tests/exception/tonraise.nim
+++ /dev/null
@@ -1,34 +0,0 @@
-discard """
-  output: '''i: 1
-success'''
-"""
-
-type
-  ESomething = object of Exception
-  ESomeOtherErr = object of Exception
-
-proc genErrors(s: string) =
-  if s == "error!":
-    raise newException(ESomething, "Test")
-  else:
-    raise newException(EsomeotherErr, "bla")
-
-proc foo() =
-  var i = 0
-  try:
-    inc i
-    onRaise(proc (e: ref Exception): bool =
-      echo "i: ", i)
-    genErrors("errssor!")
-  except ESomething:
-    echo("ESomething happened")
-  except:
-    echo("Some other error happened")
-
-  # test that raise handler is gone:
-  try:
-    genErrors("error!")
-  except ESomething:
-    echo "success"
-
-foo()
diff --git a/tests/exception/treraise.nim b/tests/exception/treraise.nim
index b2a11d34f..17a38aa53 100644
--- a/tests/exception/treraise.nim
+++ b/tests/exception/treraise.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "treraise.nim"
   outputsub: "Error: unhandled exception: bla [ESomeOtherErr]"
   exitcode: "1"
 """
@@ -19,6 +18,3 @@ except ESomething:
   echo("Error happened")
 except:
   raise
-
-
-
diff --git a/tests/exception/tsetexceptions.nim b/tests/exception/tsetexceptions.nim
new file mode 100644
index 000000000..0e353f43e
--- /dev/null
+++ b/tests/exception/tsetexceptions.nim
@@ -0,0 +1,11 @@
+discard """
+  targets: "c cpp js"
+  joinable: false
+"""
+
+# refs https://github.com/nim-lang/Nim/pull/18247#issuecomment-860877161
+
+let ex = newException(CatchableError, "test")
+setCurrentException(ex)
+doAssert getCurrentException().msg == ex.msg
+doAssert getCurrentExceptionMsg() == ex.msg
diff --git a/tests/exception/tshow_real_exception_name.nim b/tests/exception/tshow_real_exception_name.nim
new file mode 100644
index 000000000..1a708dbd6
--- /dev/null
+++ b/tests/exception/tshow_real_exception_name.nim
@@ -0,0 +1,28 @@
+discard """
+  outputsub: "CustomChildError"
+  exitcode: 1
+"""
+
+type
+  CustomError* = object of Exception
+  CustomChildError* = object of CustomError
+
+  FutureBase* = ref object of RootObj
+    error*: ref Exception
+
+  Future*[T] = ref object of FutureBase
+    v: T
+
+proc fail[T](future: Future[T], error: ref Exception) =
+  future.error = error
+
+proc w1(): Future[int] =
+  result = Future[int]()
+  result.fail(newException(CustomChildError, "abc"))
+
+proc main =
+  var fut = w1()
+  if true:
+    raise fut.error
+
+main()
diff --git a/tests/exception/tunhandledexc.nim b/tests/exception/tunhandledexc.nim
index c318aec81..6ca311d38 100644
--- a/tests/exception/tunhandledexc.nim
+++ b/tests/exception/tunhandledexc.nim
@@ -1,6 +1,7 @@
 discard """
-  file: "tunhandledexc.nim"
-  outputsub: "Error: unhandled exception: bla [ESomeOtherErr]"
+  cmd: "nim $target -d:release $options $file"
+  outputsub: '''tunhandledexc.nim(15)    genErrors
+Error: unhandled exception: bla [ESomeOtherErr]'''
   exitcode: "1"
 """
 type
diff --git a/tests/exception/twrongexc.nim b/tests/exception/twrongexc.nim
index b224d4c83..d229c5749 100644
--- a/tests/exception/twrongexc.nim
+++ b/tests/exception/twrongexc.nim
@@ -1,13 +1,8 @@
 discard """
-  file: "twrongexc.nim"
   outputsub: "Error: unhandled exception:  [ValueError]"
   exitcode: "1"
 """
 try:
   raise newException(ValueError, "")
-except OverflowError:
+except OverflowDefect:
   echo("Error caught")
-
-
-
-
diff --git a/tests/exprs/t22604.nim b/tests/exprs/t22604.nim
new file mode 100644
index 000000000..c41cd3dfa
--- /dev/null
+++ b/tests/exprs/t22604.nim
@@ -0,0 +1,36 @@
+# if
+for i in 0..<1:
+  let x =
+    case false
+    of true:
+      42
+    of false:
+      if true:
+        continue
+      else:
+        raiseAssert "Won't get here"
+
+# nested case
+for i in 0..<1:
+  let x =
+    case false
+    of true:
+      42
+    of false:
+      case true
+      of true:
+        continue
+      of false:
+        raiseAssert "Won't get here"
+
+# try except
+for i in 0..<1:
+  let x =
+    case false
+    of true:
+      42
+    of false:
+      try:
+        continue
+      except:
+        raiseAssert "Won't get here"
\ No newline at end of file
diff --git a/tests/exprs/texprstmt.nim b/tests/exprs/texprstmt.nim
index 6c9759cf5..3c9704650 100644
--- a/tests/exprs/texprstmt.nim
+++ b/tests/exprs/texprstmt.nim
@@ -1,6 +1,6 @@
 discard """
+  errormsg: "expression 'result[1 .. BackwardsIndex(1)]' is of type 'string' and has to be used (or discarded)"
   line: 10
-  errormsg: "expression 'result[1 .. BackwardsIndex(1)]' is of type 'string' and has to be discarded"
 """
 
 # bug #578
diff --git a/tests/exprs/tifexpr_typeinference.nim b/tests/exprs/tifexpr_typeinference.nim
index 3ae95c571..ccaea3e80 100644
--- a/tests/exprs/tifexpr_typeinference.nim
+++ b/tests/exprs/tifexpr_typeinference.nim
@@ -1,11 +1,15 @@
+discard """
+action: compile
+"""
+
 #bug #712
 
 import tables
 
-proc test(): TTable[string, string] =
+proc test(): Table[string, string] =
   discard
 
-proc test2(): TTable[string, string] =
+proc test2(): Table[string, string] =
   discard
 
 var x = 5
diff --git a/tests/exprs/tresultwarning.nim b/tests/exprs/tresultwarning.nim
index 32934408e..28dabfdb1 100644
--- a/tests/exprs/tresultwarning.nim
+++ b/tests/exprs/tresultwarning.nim
@@ -1,5 +1,5 @@
 discard """
-  nimout: "Special variable 'result' is shadowed. [ResultShadowed]"
+  nimout: "tresultwarning.nim(6, 7) Warning: Special variable 'result' is shadowed. [ResultShadowed]"
 """
 
 proc test(): string =
diff --git a/tests/exprs/tstmtexp.nim b/tests/exprs/tstmtexp.nim
index 0fb835bc6..0ae866497 100644
--- a/tests/exprs/tstmtexp.nim
+++ b/tests/exprs/tstmtexp.nim
@@ -1,9 +1,8 @@
 discard """
+  errormsg: "expression '5' is of type 'int literal(5)' and has to be used (or discarded)"
   file: "tstmtexp.nim"
   line: 8
-  errormsg: "expression '5' is of type 'int literal(5)' and has to be discarded"
 """
 # Test 3
 
 1+4
-
diff --git a/tests/exprs/tstmtexprs.nim b/tests/exprs/tstmtexprs.nim
index 577f314ec..615c36024 100644
--- a/tests/exprs/tstmtexprs.nim
+++ b/tests/exprs/tstmtexprs.nim
@@ -33,7 +33,7 @@ when true:
     if (var yy = 0; yy != 0):
       echo yy
     else:
-      echo(try: parseInt("1244") except EINvalidValue: -1)
+      echo(try: parseInt("1244") except ValueError: -1)
     result = case x
              of 23: 3
              of 64:
@@ -87,7 +87,7 @@ proc parseResponse(): JsonNode =
     var excMsg = key & "("
     if (var n=result["key2"]; n != nil):
       excMsg &= n.str
-    raise newException(ESynch, excMsg)
+    raise newException(CatchableError, excMsg)
 
 
 
@@ -99,7 +99,7 @@ let b = (se[1] = 1; 1)
 # bug #1161
 
 type
-  PFooBase = ref object of PObject
+  PFooBase = ref object of RootRef
     field: int
 
   PFoo[T] = ref object of PFooBase
@@ -151,3 +151,13 @@ if true:
   fooBool()
 else:
   raise newException(ValueError, "argh")
+
+# bug #5374
+
+proc test1(): int64 {.discardable.} = discard
+proc test2(): int {.discardable.} = discard
+
+if true:
+  test1()
+else:
+  test2()
diff --git a/tests/fields/tfieldindex.nim b/tests/fields/tfieldindex.nim
deleted file mode 100644
index d11c1a8b2..000000000
--- a/tests/fields/tfieldindex.nim
+++ /dev/null
@@ -1,21 +0,0 @@
-discard """
-  output: "1"
-"""
-
-type
-  TMyTuple = tuple[a, b: int]
-
-proc indexOf*(t: typedesc, name: string): int =
-  ## takes a tuple and looks for the field by name.
-  ## returs index of that field.
-  var
-    d: t
-    i = 0
-  for n, x in fieldPairs(d):
-    if n == name: return i
-    i.inc
-  raise newException(EInvalidValue, "No field " & name & " in type " &
-    astToStr(t))
-
-echo TMyTuple.indexOf("b")
-
diff --git a/tests/fields/tfielditerator.nim b/tests/fields/tfielditerator.nim
deleted file mode 100644
index 6d15ea05d..000000000
--- a/tests/fields/tfielditerator.nim
+++ /dev/null
@@ -1,46 +0,0 @@
-discard """
-  output: '''
-a char: true
-a char: false
-an int: 5
-an int: 6
-a string: abc
-false
-true
-true
-false
-true
-a: a
-b: b
-x: 5
-y: 6
-z: abc
-'''
-"""
-
-type
-  TMyTuple = tuple[a, b: char, x, y: int, z: string]
-
-proc p(x: char) = echo "a char: ", x <= 'a'
-proc p(x: int) = echo "an int: ", x
-proc p(x: string) = echo "a string: ", x
-
-var x: TMyTuple = ('a', 'b', 5, 6, "abc")
-var y: TMyTuple = ('A', 'b', 5, 9, "abc")
-
-for f in fields(x):
-  p f
-
-for a, b in fields(x, y):
-  echo a == b
-
-for key, val in fieldPairs(x):
-  echo key, ": ", val
-
-assert x != y
-assert x == x
-assert(not (x < x))
-assert x <= x
-assert y < x
-assert y <= x
-
diff --git a/tests/fields/tfielditerator2.nim b/tests/fields/tfielditerator2.nim
deleted file mode 100644
index c8e230cf5..000000000
--- a/tests/fields/tfielditerator2.nim
+++ /dev/null
@@ -1,70 +0,0 @@
-discard """
-  output: '''
-a char: true
-a char: false
-an int: 5
-an int: 6
-a string: abc
-a string: I'm root!
-CMP false
-CMP true
-CMP true
-CMP false
-CMP true
-CMP true
-a: a
-b: b
-x: 5
-y: 6
-z: abc
-thaRootMan: I'm root!
-myDisc: enC
-c: Z
-enC
-Z
-'''
-"""
-
-type
-  SomeRootObj = object of RootObj
-    thaRootMan: string
-  TMyObj = object of SomeRootObj
-    a, b: char
-    x, y: int
-    z: string
-
-  TEnum = enum enA, enB, enC
-  TMyCaseObj = object
-    case myDisc: TEnum
-    of enA: a: int
-    of enB: b: string
-    of enC: c: char
-
-proc p(x: char) = echo "a char: ", x <= 'a'
-proc p(x: int) = echo "an int: ", x
-proc p(x: string) = echo "a string: ", x
-
-proc myobj(a, b: char, x, y: int, z: string): TMyObj =
-  result.a = a; result.b = b; result.x = x; result.y = y; result.z = z
-  result.thaRootMan = "I'm root!"
-
-var x = myobj('a', 'b', 5, 6, "abc")
-var y = myobj('A', 'b', 5, 9, "abc")
-
-for f in fields(x):
-  p f
-
-for a, b in fields(x, y):
-  echo "CMP ", a == b
-
-for key, val in fieldPairs(x):
-  echo key, ": ", val
-
-var co: TMyCaseObj
-co.myDisc = enC
-co.c = 'Z'
-for key, val in fieldPairs(co):
-  echo key, ": ", val
-
-for val in fields(co):
-  echo val
diff --git a/tests/fields/tfields_in_template.nim b/tests/fields/tfields_in_template.nim
deleted file mode 100644
index b7d5d2343..000000000
--- a/tests/fields/tfields_in_template.nim
+++ /dev/null
@@ -1,15 +0,0 @@
-discard """
-  output: '''n
-n'''
-"""
-
-# bug #1902
-# This works.
-for name, value in (n: "v").fieldPairs:
-  echo name
-
-# This doesn't compile - "expression 'name' has no type (or is ambiguous)".
-template wrapper: typed =
-  for name, value in (n: "v").fieldPairs:
-    echo name
-wrapper()
diff --git a/tests/fields/tfields_with_break.nim b/tests/fields/tfields_with_break.nim
deleted file mode 100644
index 1f2632692..000000000
--- a/tests/fields/tfields_with_break.nim
+++ /dev/null
@@ -1,33 +0,0 @@
-discard """
-  output: '''(one: 1, two: 2, three: 3)
-1
-2
-3
-(one: 4, two: 5, three: 6)
-4
-(one: 7, two: 8, three: 9)
-7
-8
-9'''
-"""
-
-# bug #2134
-type
-    TestType = object
-        one: int
-        two: int
-        three: int
-
-var
-    ab = TestType(one:1, two:2, three:3)
-    ac = TestType(one:4, two:5, three:6)
-    ad = TestType(one:7, two:8, three:9)
-    tstSeq = [ab, ac, ad]
-
-for tstElement in mitems(tstSeq):
-    echo tstElement
-    for tstField in fields(tstElement):
-        #for tstField in [1,2,4,6]:
-        echo tstField
-        if tstField == 4:
-            break
diff --git a/tests/fields/timplicitfieldswithpartial.nim b/tests/fields/timplicitfieldswithpartial.nim
deleted file mode 100644
index 937833257..000000000
--- a/tests/fields/timplicitfieldswithpartial.nim
+++ /dev/null
@@ -1,35 +0,0 @@
-discard """
-  output: '''(foo: 38, other: "string here")
-43
-100
-90'''
-"""
-
-type
-  Base = ref object of RootObj
-  Foo {.partial.} = ref object of Base
-
-proc my(f: Foo) =
-  #var f.next = f
-  let f.foo = 38
-  let f.other = "string here"
-  echo f[]
-  echo f.foo + 5
-
-var g: Foo
-new(g)
-my(g)
-
-type
-  FooTask {.partial.} = ref object of RootObj
-
-proc foo(t: FooTask) {.liftLocals: t.} =
-  var x = 90
-  if true:
-    var x = 10
-    while x < 100:
-      inc x
-    echo x
-  echo x
-
-foo(FooTask())
diff --git a/tests/flags/tgenscript.nim b/tests/flags/tgenscript.nim
deleted file mode 100644
index 6a037b5d8..000000000
--- a/tests/flags/tgenscript.nim
+++ /dev/null
@@ -1,5 +0,0 @@
-discard """
-  file: "tgenscript.nim"
-"""
-
-echo "--genscript"
diff --git a/tests/float/tfloat1.nim b/tests/float/tfloat1.nim
index ed99260ea..d3497f24c 100644
--- a/tests/float/tfloat1.nim
+++ b/tests/float/tfloat1.nim
@@ -1,6 +1,5 @@
 discard """
-  file: "tfloat1.nim"
-  outputsub: "Error: unhandled exception: FPU operation caused an overflow [FloatOverflowError]"
+  outputsub: "Error: unhandled exception: FPU operation caused an overflow [FloatOverflowDefect]"
   exitcode: "1"
 """
 # Test new floating point exceptions
@@ -11,5 +10,3 @@ var x = 0.8
 var y = 0.0
 
 echo x / y #OUT Error: unhandled exception: FPU operation caused an overflow
-
-
diff --git a/tests/float/tfloat2.nim b/tests/float/tfloat2.nim
index b84120fba..1de63dde3 100644
--- a/tests/float/tfloat2.nim
+++ b/tests/float/tfloat2.nim
@@ -1,6 +1,5 @@
 discard """
-  file: "tfloat2.nim"
-  outputsub: "Error: unhandled exception: FPU operation caused a NaN result [FloatInvalidOpError]"
+  outputsub: "Error: unhandled exception: FPU operation caused a NaN result [FloatInvalidOpDefect]"
   exitcode: "1"
 """
 # Test new floating point exceptions
@@ -11,5 +10,3 @@ var x = 0.0
 var y = 0.0
 
 echo x / y #OUT Error: unhandled exception: FPU operation caused a NaN result
-
-
diff --git a/tests/float/tfloat3.nim b/tests/float/tfloat3.nim
index a14c6c396..215470cfc 100644
--- a/tests/float/tfloat3.nim
+++ b/tests/float/tfloat3.nim
@@ -1,15 +1,15 @@
 discard """
-  file: "tfloat3.nim"
-  output: "Nim    3.4368930843, 0.3299290698 C double: 3.4368930843, 0.3299290698"
+  output: '''
+Nim 3.4368930843, 0.3299290698
+C double: 3.4368930843, 0.3299290698'''
 """
 
 import math, strutils
 
 {.emit: """
 void printFloats(void) {
-    double y = 1.234567890123456789;
-
-    printf("C double: %.10f, %.10f ", exp(y), cos(y));
+  double y = 1.234567890123456789;
+  printf("C double: %.10f, %.10f\n", exp(y), cos(y));
 }
 """.}
 
@@ -17,8 +17,5 @@ proc c_printf(frmt: cstring) {.importc: "printf", header: "<stdio.h>", varargs.}
 proc printFloats {.importc, nodecl.}
 
 var x: float = 1.234567890123456789
-c_printf("Nim    %.10f, %.10f ", exp(x), cos(x))
+c_printf("Nim %.10f, %.10f\n", exp(x), cos(x))
 printFloats()
-
-
-
diff --git a/tests/float/tfloat4.nim b/tests/float/tfloat4.nim
index 68df56be8..2bb61eb58 100644
--- a/tests/float/tfloat4.nim
+++ b/tests/float/tfloat4.nim
@@ -1,15 +1,14 @@
 discard """
-  file: "tfloat4.nim"
   output: "passed all tests."
-  exitcode: 0
 """
-import math, strutils
+
+import strutils
 
 proc c_sprintf(buf, fmt: cstring) {.importc:"sprintf", header: "<stdio.h>", varargs.}
 
 proc floatToStr(f: float64): string =
   var buffer: array[128, char]
-  c_sprintf(addr buffer, "%.16e", f)
+  c_sprintf(cast[cstring](addr buffer), "%.16e", f)
   result = ""
   for ch in buffer:
     if ch == '\0':
@@ -55,4 +54,16 @@ doAssert 9999999999999999.0 == "9999999999999999.0".parseFloat
 doAssert 0.999999999999999 == ".999999999999999".parseFloat
 doAssert 0.9999999999999999 == ".9999999999999999".parseFloat
 
+# bug #18400
+var s = [-13.888888'f32]
+doAssert $s[0] == "-13.888888"
+var x = 1.23456789012345'f32
+doAssert $x == "1.2345679"
+
+# bug #21847
+doAssert parseFloat"0e+42" == 0.0
+doAssert parseFloat"0e+42949672969" == 0.0
+doAssert parseFloat"0e+42949672970" == 0.0
+doAssert parseFloat"0e+42949623223346323563272970" == 0.0
+
 echo("passed all tests.")
diff --git a/tests/float/tfloat5.nim b/tests/float/tfloat5.nim
index aa7dc6c53..0708838fc 100644
--- a/tests/float/tfloat5.nim
+++ b/tests/float/tfloat5.nim
@@ -1,9 +1,10 @@
 discard """
-  file: "tfloat5.nim"
-  output: '''0 : 0.0
+output: '''
 0 : 0.0
 0 : 0.0
-0 : 0.0'''
+0 : 0.0
+0 : 0.0
+'''
 """
 
 import parseutils
diff --git a/tests/float/tfloat6.nim b/tests/float/tfloat6.nim
deleted file mode 100644
index 8e043a658..000000000
--- a/tests/float/tfloat6.nim
+++ /dev/null
@@ -1,22 +0,0 @@
-discard """
-  file: "tfloat6.nim"
-  output: '''1e-06 : 1e-06
-1e-06 : 1e-06
-0.001 : 0.001
-1e-06 : 1e-06
-1e-06 : 1e-06
-10.000001 : 10.000001
-100.000001 : 100.000001'''
-  disabled: "windows"
-"""
-
-import strutils
-
-echo "0.00_0001".parseFloat(), " : ", 1E-6
-echo "0.00__00_01".parseFloat(), " : ", 1E-6
-echo "0.0_01".parseFloat(), " : ", 0.001
-echo "0.00_000_1".parseFloat(), " : ", 1E-6
-echo "0.00000_1".parseFloat(), " : ", 1E-6
-
-echo "1_0.00_0001".parseFloat(), " : ", 10.000001
-echo "1__00.00_0001".parseFloat(), " : ", 1_00.000001
diff --git a/tests/float/tfloat7.nim b/tests/float/tfloat7.nim
index 5fd0d43d9..a6d7af10b 100644
--- a/tests/float/tfloat7.nim
+++ b/tests/float/tfloat7.nim
@@ -1,12 +1,13 @@
 discard """
-  file: "tfloat6.nim"
-  output: '''passed.
+output: '''
 passed.
 passed.
 passed.
 passed.
 passed.
-passed.'''
+passed.
+passed.
+'''
 """
 
 import strutils
diff --git a/tests/float/tfloatmod.nim b/tests/float/tfloatmod.nim
new file mode 100644
index 000000000..37546a64c
--- /dev/null
+++ b/tests/float/tfloatmod.nim
@@ -0,0 +1,130 @@
+discard """
+  targets: "c cpp js"
+  output: "ok"
+  exitcode: "0"
+"""
+
+# Test `mod` on float64 both at compiletime and at runtime
+import math
+
+# Testdata from golang
+const testValues: array[10, tuple[f64, expected: float64]] = [
+  (4.9790119248836735e+00, 4.197615023265299782906368e-02),
+  (7.7388724745781045e+00, 2.261127525421895434476482e+00),
+  (-2.7688005719200159e-01, 3.231794108794261433104108e-02),
+  (-5.0106036182710749e+00, 4.989396381728925078391512e+00),
+  (9.6362937071984173e+00, 3.637062928015826201999516e-01),
+  (2.9263772392439646e+00, 1.220868282268106064236690e+00),
+  (5.2290834314593066e+00, 4.770916568540693347699744e+00),
+  (2.7279399104360102e+00, 1.816180268691969246219742e+00),
+  (1.8253080916808550e+00, 8.734595415957246977711748e-01),
+  (-8.6859247685756013e+00, 1.314075231424398637614104e+00)]
+
+const simpleTestData = [
+  (5.0, 3.0, 2.0),
+  (5.0, -3.0, 2.0),
+  (-5.0, 3.0, -2.0),
+  (-5.0, -3.0, -2.0),
+  (10.0, 1.0, 0.0),
+  (10.0, 0.5, 0.0),
+  (10.0, 1.5, 1.0),
+  (-10.0, 1.0, -0.0),
+  (-10.0, 0.5, -0.0),
+  (-10.0, 1.5, -1.0),
+  (1.5, 1.0, 0.5),
+  (1.25, 1.0, 0.25),
+  (1.125, 1.0, 0.125)
+  ]
+
+const specialCases = [
+  (-Inf, -Inf, Nan),
+  (-Inf, -Pi, Nan),
+  (-Inf, 0.0, Nan),
+  (-Inf, Pi, Nan),
+  (-Inf, Inf, Nan),
+  (-Inf, Nan, Nan),
+  (-PI, -Inf, -PI),
+  (-PI, 0.0, Nan),
+  (-PI, Inf, -PI),
+  (-PI, Nan, Nan),
+  (-0.0, -Inf, -0.0),
+  (-0.0, 0.0, Nan),
+  (-0.0, Inf, -0.0),
+  (-0.0, Nan, Nan),
+  (0.0, -Inf, 0.0),
+  (0.0, 0.0, Nan),
+  (0.0, Inf, 0.0),
+  (0.0, Nan, Nan),
+  (PI, -Inf, PI),
+  (PI, 0.0, Nan),
+  (PI, Inf, PI),
+  (PI, Nan, Nan),
+  (Inf, -Inf, Nan),
+  (Inf, -PI, Nan),
+  (Inf, 0.0, Nan),
+  (Inf, PI, Nan),
+  (Inf, Inf, Nan),
+  (Inf, Nan, Nan),
+  (Nan, -Inf, Nan),
+  (Nan, -PI, Nan),
+  (Nan, 0.0, Nan),
+  (Nan, PI, Nan),
+  (Nan, Inf, Nan),
+  (Nan, Nan, Nan)]
+
+const extremeValues = [
+  (5.9790119248836734e+200, 1.1258465975523544, 0.6447968302508578),
+  (1.0e-100, 1.0e100, 1.0e-100)]
+
+proc errmsg(x, y, r, expected: float64): string =
+  $x & " mod " & $y & " == " & $r & " but expected " & $expected
+
+proc golangtest() =
+  let x = 10.0
+  for tpl in testValues:
+    let (y, expected) = tpl
+    let r = x mod y
+    doAssert(r == expected, errmsg(x, y, r, expected))
+
+proc simpletest() =
+  for tpl in simpleTestData:
+    let(x, y, expected) = tpl
+    let r = x mod y
+    doAssert(r == expected, errmsg(x, y, r, expected))
+
+proc testSpecialCases() =
+  proc isnan(f: float64): bool =
+    case classify(f)
+    of fcNan:
+      result = true
+    else:
+      result = false
+
+  for tpl in specialCases:
+    let(x, y, expected) = tpl
+    let r = x mod y
+    doAssert((r == expected) or (r.isnan and expected.isnan),
+              errmsg(x, y, r, expected))
+
+proc testExtremeValues() =
+  for tpl in extremeValues:
+    let (x, y, expected) = tpl
+    let r = x mod y
+    doAssert(r == expected, errmsg(x, y, r, expected))
+
+static:
+  # compiletime evaluation
+  golangtest()
+  simpletest()
+  testSpecialCases()
+  testExtremeValues()
+
+proc main() =
+  # runtime evaluation
+  golangtest()
+  simpletest()
+  testSpecialCases()
+  testExtremeValues()
+
+main()
+echo "ok"
diff --git a/tests/float/tfloatnan.nim b/tests/float/tfloatnan.nim
index aa288d342..9e3dd94f6 100644
--- a/tests/float/tfloatnan.nim
+++ b/tests/float/tfloatnan.nim
@@ -1,6 +1,6 @@
 discard """
-  file: "tfloatnan.nim"
-  output: '''Nim: nan
+output: '''
+Nim: nan
 Nim: nan (float)
 Nim: nan (double)
 '''
@@ -14,3 +14,44 @@ echo "Nim: ", f32, " (float)"
 
 let f64: float64 = NaN
 echo "Nim: ", f64, " (double)"
+
+block: # bug #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)
+
+template main() =
+  # xxx move all tests under here
+  block: # bug #16469
+    let a1 = 0.0
+    let a2 = -0.0
+    let a3 = 1.0 / a1
+    let a4 = 1.0 / a2
+    doAssert a3 == Inf
+    doAssert a4 == -Inf
+    doAssert $(a1, a2, a3, a4) == "(0.0, -0.0, inf, -inf)"
+
+static: main()
+main()
diff --git a/tests/float/tfloatrange.nim b/tests/float/tfloatrange.nim
new file mode 100644
index 000000000..d345166f4
--- /dev/null
+++ b/tests/float/tfloatrange.nim
@@ -0,0 +1,50 @@
+discard """
+  cmd: "nim c -d:release --rangeChecks:on $file"
+  disabled: "windows"
+  output: '''StrictPositiveRange
+float
+range fail expected
+range fail expected
+'''
+"""
+import math, fenv
+
+type
+  Positive = range[0.0..Inf]
+  StrictPositive = range[minimumPositiveValue(float)..Inf]
+  Negative32 = range[-maximumPositiveValue(float32) .. -1.0'f32]
+
+proc myoverload(x: float) =
+  echo "float"
+
+proc myoverload(x: Positive) =
+  echo "PositiveRange"
+
+proc myoverload(x: StrictPositive) =
+  echo "StrictPositiveRange"
+
+let x = 9.0.StrictPositive
+myoverload(x)
+myoverload(9.0)
+
+doAssert(sqrt(x) == 3.0)
+
+var z = -10.0
+try:
+  myoverload(StrictPositive(z))
+except:
+  echo "range fail expected"
+
+
+proc strictOnlyProc(x: StrictPositive): bool =
+  if x > 1.0: true else: false
+
+let x2 = 5.0.Positive
+doAssert(strictOnlyProc(x2))
+
+try:
+  let x4 = 0.0.Positive
+  discard strictOnlyProc(x4)
+except:
+  echo "range fail expected"
+
diff --git a/tests/float/tfloats.nim b/tests/float/tfloats.nim
new file mode 100644
index 000000000..aaed2d615
--- /dev/null
+++ b/tests/float/tfloats.nim
@@ -0,0 +1,163 @@
+discard """
+  matrix: "-d:nimPreviewFloatRoundtrip; -u:nimPreviewFloatRoundtrip"
+  targets: "c cpp js"
+"""
+
+#[
+xxx merge all or most float tests into this file
+]#
+
+import std/[fenv, math, strutils]
+import stdtest/testutils
+
+proc equalsOrNaNs(a, b: float): bool =
+  if isNaN(a): isNaN(b)
+  elif a == 0:
+    b == 0 and signbit(a) == signbit(b)
+  else:
+    a == b
+
+template reject(a) =
+  doAssertRaises(ValueError): discard parseFloat(a)
+
+template main =
+  block:
+    proc test(a: string, b: float) =
+      let a2 = a.parseFloat
+      doAssert equalsOrNaNs(a2, b), $(a, a2, b)
+    test "0.00_0001", 1E-6
+    test "0.00__00_01", 1E-6
+    test "0.0_01", 0.001
+    test "0.00_000_1", 1E-6
+    test "0.00000_1", 1E-6
+    test "1_0.00_0001", 10.000001
+    test "1__00.00_0001", 1_00.000001
+    test "inf", Inf
+    test "-inf", -Inf
+    test "-Inf", -Inf
+    test "-INF", -Inf
+    test "NaN", NaN
+    test "-nan", NaN
+    test ".1", 0.1
+    test "-.1", -0.1
+    test "-0", -0.0
+    test "-0", -0'f # see #18246, -0 won't work
+    test ".1e-1", 0.1e-1
+    test "0_1_2_3.0_1_2_3E+0_1_2", 123.0123e12
+    test "0_1_2.e-0", 12e0
+    test "0_1_2e-0", 12e0
+    test "-0e0", -0.0
+    test "-0e-0", -0.0
+
+  reject "a"
+  reject ""
+  reject "e1"
+  reject "infa"
+  reject "infe1"
+  reject "_"
+  reject "1e"
+
+  when false: # gray area; these numbers should probably be invalid
+    reject "1_"
+    reject "1_.0"
+    reject "1.0_"
+
+  block: # bugs mentioned in https://github.com/nim-lang/Nim/pull/18504#issuecomment-881635317
+    block: # example 1
+      let a = 0.1+0.2
+      doAssert a != 0.3
+      when defined(nimPreviewFloatRoundtrip):
+        doAssert $a == "0.30000000000000004"
+      else:
+        whenRuntimeJs: discard
+        do: doAssert $a == "0.3"
+    block: # example 2
+      const a = 0.1+0.2
+      when defined(nimPreviewFloatRoundtrip):
+        doAssert $($a, a) == """("0.30000000000000004", 0.30000000000000004)"""
+      else:
+        whenRuntimeJs: discard
+        do: doAssert $($a, a) == """("0.3", 0.3)"""
+    block: # example 3
+      const a1 = 0.1+0.2
+      let a2 = a1
+      doAssert a1 != 0.3
+      when defined(nimPreviewFloatRoundtrip):
+        doAssert $[$a1, $a2] == """["0.30000000000000004", "0.30000000000000004"]"""
+      else:
+        whenRuntimeJs: discard
+        do: doAssert $[$a1, $a2] == """["0.3", "0.3"]"""
+
+  when defined(nimPreviewFloatRoundtrip):
+    block: # bug #18148
+      var a = 1.1'f32
+      doAssert $a == "1.1", $a # was failing
+
+    block: # bug #18400
+      block:
+        let a1 = 0.1'f32
+        let a2 = 0.2'f32
+        let a3 = a1 + a2
+        var s = ""
+        s.addFloat(a3)
+        whenVMorJs: discard # xxx refs #12884
+        do:
+          doAssert a3 == 0.3'f32
+          doAssert $a3 == "0.3"
+
+      block:
+        let a1 = 0.1
+        let a2 = 0.2
+        let a3 = a1 + a2
+        var s = ""
+        s.addFloat(a3)
+        doAssert a3 != 0.3
+        doAssert $a3 == "0.30000000000000004"
+
+      block:
+        var s = [-13.888888'f32]
+        whenRuntimeJs: discard
+        do:
+          doAssert $s == "[-13.888888]"
+          doAssert $s[0] == "-13.888888"
+
+    block: # bug #7717
+      proc test(f: float) =
+        let f2 = $f
+        let f3 = parseFloat(f2)
+        doAssert equalsOrNaNs(f, f3), $(f, f2, f3)
+      test 1.0 + epsilon(float64)
+      test 1000000.0000000123
+      test log2(100000.0)
+      test maximumPositiveValue(float32)
+      test maximumPositiveValue(float64)
+      test minimumPositiveValue(float32)
+      test minimumPositiveValue(float64)
+
+    block: # bug #12884
+      block: # example 1
+        const x0: float32 = 1.32
+        let x1 = 1.32
+        let x2 = 1.32'f32
+        var x3: float32 = 1.32
+        doAssert $(x0, x1, x2, x3) == "(1.32, 1.32, 1.32, 1.32)"
+      block: # example https://github.com/nim-lang/Nim/issues/12884#issuecomment-564967962
+        let x = float(1.32'f32)
+        when nimvm: discard # xxx prints 1.3
+        else:
+          when not defined(js):
+            doAssert $x == "1.3200000524520874"
+        doAssert $1.32 == "1.32"
+        doAssert $1.32'f32 == "1.32"
+        let x2 = 1.32'f32
+        doAssert $x2 == "1.32"
+      block:
+        var x = 1.23456789012345'f32
+        when nimvm:
+          discard # xxx, refs #12884
+        else:
+          doAssert x == 1.2345679'f32
+          doAssert $x == "1.2345679"
+
+static: main()
+main()
diff --git a/tests/float/tissue5821.nim b/tests/float/tissue5821.nim
index e8aa4a1d9..c4f561f09 100644
--- a/tests/float/tissue5821.nim
+++ b/tests/float/tissue5821.nim
@@ -1,7 +1,7 @@
 discard """
-  file: "tissue5821.nim"
-  output: ''''''
+output: "ok"
 """
+
 proc main(): void =
   let a: float32 = 47.11'f32
   doAssert a == 47.11'f32
@@ -10,4 +10,6 @@ proc main(): void =
   doAssert b != 10.123402823e+38'f64
   doAssert b == 10.234402823e+38'f64
 
-main()
\ No newline at end of file
+  echo "ok"
+
+main()
diff --git a/tests/fragmentation/tfragment_alloc.nim b/tests/fragmentation/tfragment_alloc.nim
index 5a44b7434..cd45e2c47 100644
--- a/tests/fragmentation/tfragment_alloc.nim
+++ b/tests/fragmentation/tfragment_alloc.nim
@@ -2,6 +2,7 @@
 discard """
   output: '''occupied ok: true
 total ok: true'''
+  disabled: "true"
 """
 
 import strutils, data
@@ -16,9 +17,12 @@ proc main =
     dealloc p
  #   c_fprintf(stdout, "iteration: %ld size: %ld\n", i, size)
   when defined(cpu64):
-    # bug #7120
-    var x = alloc(((1 shl 29) - 4) * 8)
-    dealloc x
+    # see https://github.com/nim-lang/Nim/issues/8509
+    # this often made appveyor (on windows) fail with out of memory
+    when defined(posix):
+      # bug #7120
+      var x = alloc(((1 shl 29) - 4) * 8)
+      dealloc x
 
 main()
 
diff --git a/tests/fragmentation/tfragment_gc.nim b/tests/fragmentation/tfragment_gc.nim
index d387bbea2..0ae8c3d7f 100644
--- a/tests/fragmentation/tfragment_gc.nim
+++ b/tests/fragmentation/tfragment_gc.nim
@@ -1,7 +1,7 @@
 discard """
   output: '''occupied ok: true
 total ok: true'''
-  disabled: "windows"
+  disabled: "true"
 """
 
 import strutils, data
diff --git a/tests/gc/closureleak.nim b/tests/gc/closureleak.nim
index 18d320bdf..e67beb513 100644
--- a/tests/gc/closureleak.nim
+++ b/tests/gc/closureleak.nim
@@ -1,30 +1,49 @@
 discard """
   outputsub: "true"
+  disabled: "32bit"
 """
 
-from strutils import join
-
 type
-  TFoo * = object
+  TFoo* = object
     id: int
-    fn: proc(){.closure.}
+    fn: proc() {.closure.}
 var foo_counter = 0
 var alive_foos = newseq[int](0)
 
-proc free*(some: ref TFoo) =
-  #echo "Tfoo #", some.id, " freed"
-  alive_foos.del alive_foos.find(some.id)
+when defined(gcDestructors):
+  proc `=destroy`(some: TFoo) =
+    alive_foos.del alive_foos.find(some.id)
+    # TODO: fixme: investigate why `=destroy` requires `some.fn` to be `gcsafe`
+    # the debugging info below came from `symPrototype` in the liftdestructors
+    # proc (){.closure, gcsafe.}, {tfThread, tfHasAsgn, tfCheckedForDestructor, tfExplicitCallConv}
+    # var proc (){.closure, gcsafe.}, {tfHasGCedMem}
+    # it worked by accident with var T destructors because in the sempass2
+    #
+    # let argtype = skipTypes(a.typ, abstractInst) # !!! it does't skip `tyVar`
+    # if argtype.kind == tyProc and notGcSafe(argtype) and not tracked.inEnforcedGcSafe:
+    #   localError(tracked.config, n.info, $n & " is not GC safe")
+    {.cast(gcsafe).}:
+      `=destroy`(some.fn)
+
+else:
+  proc free*(some: ref TFoo) =
+    #echo "Tfoo #", some.id, " freed"
+    alive_foos.del alive_foos.find(some.id)
+
 proc newFoo*(): ref TFoo =
-  new result, free
+  when defined(gcDestructors):
+    new result
+  else:
+    new result, free
 
   result.id = foo_counter
   alive_foos.add result.id
   inc foo_counter
 
-for i in 0 .. <10:
- discard newFoo()
+for i in 0 ..< 10:
+  discard newFoo()
 
-for i in 0 .. <10:
+for i in 0 ..< 10:
   let f = newFoo()
   f.fn = proc =
     echo f.id
diff --git a/tests/gc/cyclecollector.nim b/tests/gc/cyclecollector.nim
index 46fed6c45..2d02a7a3c 100644
--- a/tests/gc/cyclecollector.nim
+++ b/tests/gc/cyclecollector.nim
@@ -9,13 +9,15 @@ type
 proc createCycle(leaf: string): Node =
   new result
   result.a = result
-  shallowCopy result.leaf, leaf
+  when defined(gcArc) or defined(gcOrc):
+    result.leaf = leaf
+  else:
+    shallowCopy result.leaf, leaf
 
 proc main =
   for i in 0 .. 100_000:
     var leaf = "this is the leaf. it allocates"
     let x = createCycle(leaf)
     let y = createCycle(leaf)
-  echo "done ", getOccupiedMem()
 
 main()
diff --git a/tests/gc/cycleleak.nim b/tests/gc/cycleleak.nim
index 9f5c30ebd..e355abc96 100644
--- a/tests/gc/cycleleak.nim
+++ b/tests/gc/cycleleak.nim
@@ -10,7 +10,7 @@ type
   PModule = ref Module
 
   Node = object
-    owner*: PModule
+    owner* {.cursor.}: PModule
     data*: array[0..200, char] # some fat to drain memory faster
     id: int
 
diff --git a/tests/gc/gcbench.nim b/tests/gc/gcbench.nim
index 782daf793..e29ea762d 100644
--- a/tests/gc/gcbench.nim
+++ b/tests/gc/gcbench.nim
@@ -44,7 +44,7 @@ discard """
 # - No way to check on memory use
 # - No cyclic data structures
 # - No attempt to measure variation with object size
-# - Results are sensitive to locking cost, but we dont
+# - Results are sensitive to locking cost, but we don't
 #   check for proper locking
 #
 
@@ -53,11 +53,11 @@ import
 
 type
   PNode = ref TNode
-  TNode {.final.} = object
+  TNode {.final, acyclic.} = object
     left, right: PNode
     i, j: int
 
-proc newNode(L, r: PNode): PNode =
+proc newNode(L, r: sink PNode): PNode =
   new(result)
   result.left = L
   result.right = r
@@ -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() =
-  echo("Total memory available: " & $getTotalMem() & " bytes")
-  echo("Free memory: " & $getFreeMem() & " bytes")
+proc printDiagnostics() =
+  echo("Total memory available: " & formatSize(getTotalMem()) & " bytes")
+  echo("Free memory: " & formatSize(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,42 +130,51 @@ 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()
-  echo("Completed in " & $elapsed & "ms. Success!")
+  printDiagnostics()
+  echo("Completed in " & $elapsed & "s. Success!")
+  when declared(getMaxMem):
+    echo "Max memory ", formatSize getMaxMem()
 
 when defined(GC_setMaxPause):
   GC_setMaxPause 2_000
 
+when defined(gcDestructors):
+  let mem = getOccupiedMem()
 main()
+when defined(gcDestructors):
+  doAssert getOccupiedMem() == mem
diff --git a/tests/gc/gcemscripten.nim b/tests/gc/gcemscripten.nim
index bbef13d98..cc12b230f 100644
--- a/tests/gc/gcemscripten.nim
+++ b/tests/gc/gcemscripten.nim
@@ -15,7 +15,7 @@ when defined(allow_print):
 else:
   const print = false
 
-proc myResult3*(i:int):X {.exportc.} =
+proc myResult3*(i:int): X {.exportc.} =
   if print: echo "3"
   new(result)
   if print: echo "3-2"
diff --git a/tests/gc/gcleak.nim b/tests/gc/gcleak.nim
index 8852a8d91..0bf993968 100644
--- a/tests/gc/gcleak.nim
+++ b/tests/gc/gcleak.nim
@@ -6,16 +6,23 @@ when defined(GC_setMaxPause):
   GC_setMaxPause 2_000
 
 type
-  TTestObj = object of TObject
+  TTestObj = object of RootObj
     x: string
 
-proc MakeObj(): TTestObj =
+proc makeObj(): TTestObj =
   result.x = "Hello"
 
-for i in 1 .. 1_000_000:
+const numIter =
+  # see tests/gc/gcleak2.nim
+  when defined(boehmgc):
+    1_000
+  elif defined(gcMarkAndSweep): 10_000
+  else: 100_000
+
+for i in 1 .. numIter:
   when defined(gcMarkAndSweep) or defined(boehmgc):
     GC_fullcollect()
-  var obj = MakeObj()
+  var obj = makeObj()
   if getOccupiedMem() > 300_000: quit("still a leak!")
 #  echo GC_getstatistics()
 
diff --git a/tests/gc/gcleak2.nim b/tests/gc/gcleak2.nim
index facb8a008..bc943dbe7 100644
--- a/tests/gc/gcleak2.nim
+++ b/tests/gc/gcleak2.nim
@@ -6,20 +6,32 @@ when defined(GC_setMaxPause):
   GC_setMaxPause 2_000
 
 type
-  TTestObj = object of TObject
+  TTestObj = object of RootObj
     x: string
     s: seq[int]
 
-proc MakeObj(): TTestObj =
+proc makeObj(): TTestObj =
   result.x = "Hello"
   result.s = @[1,2,3]
 
+const numIter =
+  when defined(boehmgc):
+    # super slow because GC_fullcollect() at each iteration; especially
+    # on OSX 10.15 where it takes ~170s
+    # `getOccupiedMem` should be constant after each iteration for i >= 3
+    1_000
+  elif defined(gcMarkAndSweep):
+    # likewise, somewhat slow, 1_000_000 would run for 8s
+    # and same remark as above
+    100_000
+  else: 1_000_000
+
 proc inProc() =
-  for i in 1 .. 1_000_000:
+  for i in 1 .. numIter:
     when defined(gcMarkAndSweep) or defined(boehmgc):
       GC_fullcollect()
     var obj: TTestObj
-    obj = MakeObj()
+    obj = makeObj()
     if getOccupiedMem() > 300_000: quit("still a leak!")
 
 inProc()
diff --git a/tests/gc/gcleak3.nim b/tests/gc/gcleak3.nim
index 588e238e9..5e146d69f 100644
--- a/tests/gc/gcleak3.nim
+++ b/tests/gc/gcleak3.nim
@@ -17,14 +17,10 @@ for i in 0..1024:
   s.add(obj)
 
 proc limit*[t](a: var seq[t]) =
-  var loop = s.len() - 512
-  for i in 0..loop:
-    #echo i
-    #GC_fullCollect()
+  while s.len > 0:
     if getOccupiedMem() > 3000_000: quit("still a leak!")
-    s.delete(i)
+    s.delete(0)
 
 s.limit()
-
 echo "no leak: ", getOccupiedMem()
 
diff --git a/tests/gc/gcleak4.nim b/tests/gc/gcleak4.nim
index e9b17e557..a72db67b7 100644
--- a/tests/gc/gcleak4.nim
+++ b/tests/gc/gcleak4.nim
@@ -2,11 +2,8 @@ discard """
   outputsub: "no leak: "
 """
 
-when defined(GC_setMaxPause):
-  GC_setMaxPause 2_000
-
 type
-  TExpr = object {.inheritable.} ## abstract base class for an expression
+  TExpr {.inheritable.} = object ## abstract base class for an expression
   PLiteral = ref TLiteral
   TLiteral = object of TExpr
     x: int
@@ -15,7 +12,7 @@ type
     a, b: ref TExpr
     op2: string
 
-method eval(e: ref TExpr): int =
+method eval(e: ref TExpr): int {.base.} =
   # override this base method
   quit "to override!"
 
@@ -27,20 +24,18 @@ method eval(e: ref TPlusExpr): int =
 
 proc newLit(x: int): ref TLiteral =
   new(result)
-  {.watchpoint: result.}
   result.x = x
   result.op1 = $getOccupiedMem()
 
-proc newPlus(a, b: ref TExpr): ref TPlusExpr =
+proc newPlus(a, b: sink(ref TExpr)): ref TPlusExpr =
   new(result)
-  {.watchpoint: result.}
   result.a = a
   result.b = b
   result.op2 = $getOccupiedMem()
 
 const Limit = when compileOption("gc", "markAndSweep") or compileOption("gc", "boehm"): 5*1024*1024 else: 500_000
 
-for i in 0..100_000:
+for i in 0..50_000:
   var s: array[0..11, ref TExpr]
   for j in 0..high(s):
     s[j] = newPlus(newPlus(newLit(j), newLit(2)), newLit(4))
diff --git a/tests/gc/gcleak5.nim b/tests/gc/gcleak5.nim
index 6ab50e19e..f1913831b 100644
--- a/tests/gc/gcleak5.nim
+++ b/tests/gc/gcleak5.nim
@@ -9,7 +9,7 @@ proc main =
   for ii in 0..50_000:
     #while true:
     var t = getTime()
-    var g = t.getGMTime()
+    var g = t.utc()
     #echo isOnStack(addr g)
 
     if i mod 100 == 0:
diff --git a/tests/gc/gctest.nim b/tests/gc/gctest.nim
index 7f5260200..78b78934c 100644
--- a/tests/gc/gctest.nim
+++ b/tests/gc/gctest.nim
@@ -45,19 +45,14 @@ var
   flip: int
 
 proc newCaseNode(data: string): PCaseNode =
-  new(result)
   if flip == 0:
-    result.kind = nkStr
-    result.data = data
+    result = PCaseNode(kind: nkStr, data: data)
   else:
-    result.kind = nkWhole
-    result.unused = @["", "abc", "abdc"]
+    result = PCaseNode(kind: nkWhole, unused: @["", "abc", "abdc"])
   flip = 1 - flip
 
 proc newCaseNode(a, b: PCaseNode): PCaseNode =
-  new(result)
-  result.kind = nkList
-  result.sons = @[a, b]
+  result = PCaseNode(kind: nkList, sons: @[a, b])
 
 proc caseTree(lvl: int = 0): PCaseNode =
   if lvl == 3: result = newCaseNode("data item")
@@ -66,8 +61,7 @@ proc caseTree(lvl: int = 0): PCaseNode =
 proc finalizeNode(n: PNode) =
   assert(n != nil)
   write(stdout, "finalizing: ")
-  if isNil(n.data): writeLine(stdout, "nil!")
-  else: writeLine(stdout, "not nil")
+  writeLine(stdout, "not nil")
 
 var
   id: int = 1
diff --git a/tests/gc/growobjcrash.nim b/tests/gc/growobjcrash.nim
index a16468c7e..ff1aa7e98 100644
--- a/tests/gc/growobjcrash.nim
+++ b/tests/gc/growobjcrash.nim
@@ -1,8 +1,4 @@
-discard """
-  output: "works"
-"""
-
-import cgi, strtabs
+import std/[cgi, strtabs]
 
 proc handleRequest(query: string): StringTableRef =
   iterator foo(): StringTableRef {.closure.} =
@@ -14,11 +10,11 @@ proc handleRequest(query: string): StringTableRef =
   let x = foo
   result = x()
 
-const Limit = when compileOption("gc", "markAndSweep"): 5*1024*1024 else: 700_000
+const Limit = 5*1024*1024
 
 proc main =
   var counter = 0
-  for i in 0 .. 100_000:
+  for i in 0 .. 10_000:
     for k, v in handleRequest("nick=Elina2&type=activate"):
       inc counter
       if counter mod 100 == 0:
@@ -26,4 +22,3 @@ proc main =
           quit "but now a leak"
 
 main()
-echo "works"
diff --git a/tests/gc/panicoverride.nim b/tests/gc/panicoverride.nim
new file mode 100644
index 000000000..0f28b0b72
--- /dev/null
+++ b/tests/gc/panicoverride.nim
@@ -0,0 +1,14 @@
+
+proc printf(frmt: cstring) {.varargs, importc, header: "<stdio.h>", cdecl.}
+proc exit(code: int) {.importc, header: "<stdlib.h>", cdecl.}
+
+{.push stack_trace: off, profiler:off.}
+
+proc rawoutput(s: string) =
+  printf("%s\n", s)
+
+proc panic(s: string) {.noreturn.} =
+  rawoutput(s)
+  exit(1)
+
+{.pop.}
\ No newline at end of file
diff --git a/tests/gc/stackrefleak.nim b/tests/gc/stackrefleak.nim
index 302ef3599..7f3fbff43 100644
--- a/tests/gc/stackrefleak.nim
+++ b/tests/gc/stackrefleak.nim
@@ -12,7 +12,8 @@ type
 proc makePair: PCyclic =
   new(result)
   new(result.sibling)
-  result.sibling.sibling = result
+  when not defined(gcDestructors):
+    result.sibling.sibling = result
 
 proc loop =
   for i in 0..10000:
diff --git a/tests/gc/tdisable_orc.nim b/tests/gc/tdisable_orc.nim
new file mode 100644
index 000000000..b5f161c79
--- /dev/null
+++ b/tests/gc/tdisable_orc.nim
@@ -0,0 +1,9 @@
+discard """
+  joinable: false
+"""
+
+import std/asyncdispatch
+
+# bug #22256
+GC_disableMarkAndSweep()
+waitFor sleepAsync(1000)
diff --git a/tests/gc/thavlak.nim b/tests/gc/thavlak.nim
index efab49e36..cfd860e25 100644
--- a/tests/gc/thavlak.nim
+++ b/tests/gc/thavlak.nim
@@ -1,51 +1,55 @@
 discard """
   output: '''Welcome to LoopTesterApp, Nim edition
 Constructing Simple CFG...
-15000 dummy loops
+5000 dummy loops
 Constructing CFG...
 Performing Loop Recognition
 1 Iteration
-Another 50 iterations...
-..................................................
-Found 1 loops (including artificial root node) (50)'''
+Another 3 iterations...
+...
+Found 1 loops (including artificial root node) (3)'''
 """
 
 # bug #3184
 
-import tables
-import sequtils
-import sets
+import tables, sets
+
+when not declared(withScratchRegion):
+  template withScratchRegion(body: untyped) = body
 
 type
-  BasicBlock = object
-    inEdges: seq[ref BasicBlock]
-    outEdges: seq[ref BasicBlock]
+  BasicBlock = ref object
+    inEdges: seq[BasicBlock]
+    outEdges: seq[BasicBlock]
     name: int
 
-proc newBasicBlock(name: int): ref BasicBlock =
-  new(result)
-  result.inEdges = newSeq[ref BasicBlock]()
-  result.outEdges = newSeq[ref BasicBlock]()
-  result.name = name
+proc newBasicBlock(name: int): BasicBlock =
+  result = BasicBlock(
+    inEdges: newSeq[BasicBlock](),
+    outEdges: newSeq[BasicBlock](),
+    name: name
+  )
 
-proc hash(x: ref BasicBlock): int {.inline.} =
+proc hash(x: BasicBlock): int {.inline.} =
   result = x.name
 
 type
   BasicBlockEdge = object
-    fr: ref BasicBlock
-    to: ref BasicBlock
+    fr: BasicBlock
+    to: BasicBlock
 
   Cfg = object
-    basicBlockMap: Table[int, ref BasicBlock]
+    basicBlockMap: Table[int, BasicBlock]
     edgeList: seq[BasicBlockEdge]
-    startNode: ref BasicBlock
+    startNode: BasicBlock
 
 proc newCfg(): Cfg =
-  result.basicBlockMap = initTable[int, ref BasicBlock]()
-  result.edgeList = newSeq[BasicBlockEdge]()
+  result = Cfg(
+    basicBlockMap: initTable[int, BasicBlock](),
+    edgeList: newSeq[BasicBlockEdge](),
+    startNode: nil)
 
-proc createNode(self: var Cfg, name: int): ref BasicBlock =
+proc createNode(self: var Cfg, name: int): BasicBlock =
   result = self.basicBlockMap.getOrDefault(name)
   if result == nil:
     result = newBasicBlock(name)
@@ -54,128 +58,94 @@ proc createNode(self: var Cfg, name: int): ref BasicBlock =
   if self.startNode == nil:
     self.startNode = result
 
-proc addEdge(self: var Cfg, edge: BasicBlockEdge) =
-  self.edgeList.add(edge)
-
-proc getNumNodes(self: Cfg): int =
-  self.basicBlockMap.len
-
-proc newBasicBlockEdge(cfg: var Cfg, fromName: int, toName: int): BasicBlockEdge =
-  result.fr = cfg.createNode(fromName)
-  result.to = cfg.createNode(toName)
+proc newBasicBlockEdge(cfg: var Cfg, fromName, toName: int) =
+  var result = BasicBlockEdge(
+    fr: cfg.createNode(fromName),
+    to: cfg.createNode(toName)
+  )
   result.fr.outEdges.add(result.to)
   result.to.inEdges.add(result.fr)
-  cfg.addEdge(result)
+  cfg.edgeList.add(result)
 
 type
-  SimpleLoop = object
-    basicBlocks: seq[ref BasicBlock] # TODO: set here
-    children: seq[ref SimpleLoop] # TODO: set here
-    parent: ref SimpleLoop
-    header: ref BasicBlock
-    isRoot: bool
-    isReducible: bool
-    counter: int
-    nestingLevel: int
-    depthLevel: int
-
-proc newSimpleLoop(): ref SimpleLoop =
-  new(result)
-  result.basicBlocks = newSeq[ref BasicBlock]()
-  result.children = newSeq[ref SimpleLoop]()
-  result.parent = nil
-  result.header = nil
-  result.isRoot = false
-  result.isReducible = true
-  result.counter = 0
-  result.nestingLevel = 0
-  result.depthLevel = 0
-
-proc addNode(self: ref SimpleLoop, bb: ref BasicBlock) =
-  self.basicBlocks.add bb
-
-proc addChildLoop(self: ref SimpleLoop, loop: ref SimpleLoop) =
-  self.children.add loop
-
-proc setParent(self: ref SimpleLoop, parent: ref SimpleLoop) =
+  SimpleLoop = ref object
+    basicBlocks: seq[BasicBlock] # TODO: set here
+    children: seq[SimpleLoop] # TODO: set here
+    parent: SimpleLoop
+    header: BasicBlock
+    isRoot, isReducible: bool
+    counter, nestingLevel, depthLevel: int
+
+proc setParent(self: SimpleLoop, parent: SimpleLoop) =
   self.parent = parent
-  self.parent.addChildLoop(self)
+  self.parent.children.add self
 
-proc setHeader(self: ref SimpleLoop, bb: ref BasicBlock) =
+proc setHeader(self: SimpleLoop, bb: BasicBlock) =
   self.basicBlocks.add(bb)
   self.header = bb
 
-proc setNestingLevel(self: ref SimpleLoop, level: int) =
+proc setNestingLevel(self: SimpleLoop, level: int) =
   self.nestingLevel = level
   if level == 0: self.isRoot = true
 
-var loop_counter: int = 0
+var loopCounter: int = 0
 
 type
   Lsg = object
-    loops: seq[ref SimpleLoop]
-    root: ref SimpleLoop
-
-proc createNewLoop(self: var Lsg): ref SimpleLoop =
-  result = newSimpleLoop()
-  loop_counter += 1
-  result.counter = loop_counter
-
-proc addLoop(self: var Lsg, l: ref SimpleLoop) =
+    loops: seq[SimpleLoop]
+    root: SimpleLoop
+
+proc createNewLoop(self: var Lsg): SimpleLoop =
+  result = SimpleLoop(
+    basicBlocks: newSeq[BasicBlock](),
+    children: newSeq[SimpleLoop](),
+    isReducible: true)
+  loopCounter += 1
+  result.counter = loopCounter
+
+proc addLoop(self: var Lsg, l: SimpleLoop) =
   self.loops.add l
 
 proc newLsg(): Lsg =
-  result.loops = newSeq[ref SimpleLoop]()
-  result.root = result.createNewLoop()
+  result = Lsg(loops: newSeq[SimpleLoop](),
+    root: result.createNewLoop())
   result.root.setNestingLevel(0)
   result.addLoop(result.root)
 
-proc getNumLoops(self: Lsg): int =
-  self.loops.len
-
 type
-  UnionFindNode = object
-    parent: ref UnionFindNode
-    bb: ref BasicBlock
-    l: ref SimpleLoop
+  UnionFindNode = ref object
+    parent {.cursor.}: UnionFindNode
+    bb: BasicBlock
+    l: SimpleLoop
     dfsNumber: int
 
-proc newUnionFindNode(): ref UnionFindNode =
-  new(result)
-  when false:
-    result.parent = nil
-    result.bb = nil
-    result.l = nil
-    result.dfsNumber = 0
-
-proc initNode(self: ref UnionFindNode, bb: ref BasicBlock, dfsNumber: int) =
+proc initNode(self: UnionFindNode, bb: BasicBlock, dfsNumber: int) =
   self.parent = self
   self.bb = bb
   self.dfsNumber = dfsNumber
 
-proc findSet(self: ref UnionFindNode): ref UnionFindNode =
-  var nodeList = newSeq[ref UnionFindNode]()
-  result = self
+proc findSet(self: UnionFindNode): UnionFindNode =
+  var nodeList = newSeq[UnionFindNode]()
+  var it {.cursor.} = self
 
-  while result != result.parent:
-    var parent = result.parent
-    if parent != parent.parent: nodeList.add result
-    result = parent
+  while it != it.parent:
+    var parent {.cursor.} = it.parent
+    if parent != parent.parent: nodeList.add it
+    it = parent
 
-  for iter in nodeList: iter.parent = result.parent
+  for iter in nodeList: iter.parent = it.parent
+  result = it
 
-proc union(self: ref UnionFindNode, unionFindNode: ref UnionFindNode) =
+proc union(self: UnionFindNode, unionFindNode: UnionFindNode) =
   self.parent = unionFindNode
 
 
 const
-  BB_TOP          = 0 # uninitialized
-  BB_NONHEADER    = 1 # a regular BB
-  BB_REDUCIBLE    = 2 # reducible loop
-  BB_SELF         = 3 # single BB loop
-  BB_IRREDUCIBLE  = 4 # irreducible loop
-  BB_DEAD         = 5 # a dead BB
-  BB_LAST         = 6 # Sentinel
+  BB_NONHEADER = 1 # a regular BB
+  BB_REDUCIBLE = 2 # reducible loop
+  BB_SELF = 3 # single BB loop
+  BB_IRREDUCIBLE = 4 # irreducible loop
+  BB_DEAD = 5 # a dead BB
 
   # # Marker for uninitialized nodes.
   UNVISITED = -1
@@ -188,44 +158,44 @@ type
     cfg: Cfg
     lsg: Lsg
 
-proc newHavlakLoopFinder(cfg: Cfg, lsg: Lsg): HavlakLoopFinder =
-  result.cfg = cfg
-  result.lsg = lsg
+proc newHavlakLoopFinder(cfg: Cfg, lsg: sink Lsg): HavlakLoopFinder =
+  result = HavlakLoopFinder(cfg: cfg, lsg: lsg)
 
-proc isAncestor(w: int, v: int, last: seq[int]): bool =
+proc isAncestor(w, v: int, last: seq[int]): bool =
   w <= v and v <= last[w]
 
-proc dfs(currentNode: ref BasicBlock, nodes: var seq[ref UnionFindNode], number: var Table[ref BasicBlock, int], last: var seq[int], current: int): int =
+proc dfs(currentNode: BasicBlock, nodes: var seq[UnionFindNode],
+         number: var Table[BasicBlock, int],
+         last: var seq[int], current: int) =
   var stack = @[(currentNode, current)]
   while stack.len > 0:
     let (currentNode, current) = stack.pop()
     nodes[current].initNode(currentNode, current)
     number[currentNode] = current
 
-    result = current
     for target in currentNode.outEdges:
       if number[target] == UNVISITED:
-        stack.add((target, result+1))
+        stack.add((target, current+1))
         #result = dfs(target, nodes, number, last, result + 1)
-  last[number[currentNode]] = result
+  last[number[currentNode]] = current
 
 proc findLoops(self: var HavlakLoopFinder): int =
   var startNode = self.cfg.startNode
   if startNode == nil: return 0
-  var size = self.cfg.getNumNodes
+  var size = self.cfg.basicBlockMap.len
 
-  var nonBackPreds    = newSeq[HashSet[int]]()
-  var backPreds       = newSeq[seq[int]]()
-  var number          = initTable[ref BasicBlock, int]()
-  var header          = newSeq[int](size)
-  var types           = newSeq[int](size)
-  var last            = newSeq[int](size)
-  var nodes           = newSeq[ref UnionFindNode]()
+  var nonBackPreds = newSeq[HashSet[int]]()
+  var backPreds = newSeq[seq[int]]()
+  var number = initTable[BasicBlock, int]()
+  var header = newSeq[int](size)
+  var types = newSeq[int](size)
+  var last = newSeq[int](size)
+  var nodes = newSeq[UnionFindNode]()
 
   for i in 1..size:
-    nonBackPreds.add initSet[int](1)
+    nonBackPreds.add initHashSet[int](1)
     backPreds.add newSeq[int]()
-    nodes.add newUnionFindNode()
+    nodes.add(UnionFindNode())
 
   # Step a:
   #   - initialize all nodes as unvisited.
@@ -233,7 +203,7 @@ proc findLoops(self: var HavlakLoopFinder): int =
   #   - unreached BB's are marked as dead.
   #
   for v in self.cfg.basicBlockMap.values: number[v] = UNVISITED
-  var res = dfs(startNode, nodes, number, last, 0)
+  dfs(startNode, nodes, number, last, 0)
 
   # Step b:
   #   - iterate over all nodes.
@@ -245,7 +215,7 @@ proc findLoops(self: var HavlakLoopFinder): int =
   #     - the list of backedges (backPreds) or
   #     - the list of non-backedges (nonBackPreds)
   #
-  for w in 0 .. <size:
+  for w in 0 ..< size:
     header[w] = 0
     types[w]  = BB_NONHEADER
 
@@ -278,7 +248,7 @@ proc findLoops(self: var HavlakLoopFinder): int =
 
   for w in countdown(size - 1, 0):
     # this is 'P' in Havlak's paper
-    var nodePool = newSeq[ref UnionFindNode]()
+    var nodePool = newSeq[UnionFindNode]()
 
     var nodeW = nodes[w].bb
     if nodeW != nil: # dead BB
@@ -291,7 +261,7 @@ proc findLoops(self: var HavlakLoopFinder): int =
 
       # Copy nodePool to workList.
       #
-      var workList = newSeq[ref UnionFindNode]()
+      var workList = newSeq[UnionFindNode]()
       for x in nodePool: workList.add x
 
       if nodePool.len != 0: types[w] = BB_REDUCIBLE
@@ -299,7 +269,7 @@ proc findLoops(self: var HavlakLoopFinder): int =
       # work the list...
       #
       while workList.len > 0:
-        var x = workList[0]
+        let x = workList[0]
         workList.del(0)
 
         # Step e:
@@ -332,7 +302,7 @@ proc findLoops(self: var HavlakLoopFinder): int =
       # Collapse/Unionize nodes in a SCC to a single node
       # For every SCC found, create a loop descriptor and link it in.
       #
-      if (nodePool.len > 0) or (types[w] == BB_SELF):
+      if nodePool.len > 0 or types[w] == BB_SELF:
         var l = self.lsg.createNewLoop
 
         l.setHeader(nodeW)
@@ -358,15 +328,15 @@ proc findLoops(self: var HavlakLoopFinder): int =
           node.union(nodes[w])
 
           # Nested loops are not added, but linked together.
-          var node_l = node.l
-          if node_l != nil:
-            node_l.setParent(l)
+          var nodeL = node.l
+          if nodeL != nil:
+            nodeL.setParent(l)
           else:
-            l.addNode(node.bb)
+            l.basicBlocks.add node.bb
 
         self.lsg.addLoop(l)
 
-  result = self.lsg.getNumLoops
+  result = self.lsg.loops.len
 
 
 type
@@ -379,27 +349,26 @@ proc newLoopTesterApp(): LoopTesterApp =
   result.lsg = newLsg()
 
 proc buildDiamond(self: var LoopTesterApp, start: int): int =
-  var bb0 = start
-  var x1 = newBasicBlockEdge(self.cfg, bb0, bb0 + 1)
-  var x2 = newBasicBlockEdge(self.cfg, bb0, bb0 + 2)
-  var x3 = newBasicBlockEdge(self.cfg, bb0 + 1, bb0 + 3)
-  var x4 = newBasicBlockEdge(self.cfg, bb0 + 2, bb0 + 3)
-  result = bb0 + 3
+  newBasicBlockEdge(self.cfg, start, start + 1)
+  newBasicBlockEdge(self.cfg, start, start + 2)
+  newBasicBlockEdge(self.cfg, start + 1, start + 3)
+  newBasicBlockEdge(self.cfg, start + 2, start + 3)
+  result = start + 3
 
-proc buildConnect(self: var LoopTesterApp, start1: int, end1: int) =
-  var x1 = newBasicBlockEdge(self.cfg, start1, end1)
+proc buildConnect(self: var LoopTesterApp, start1, end1: int) =
+  newBasicBlockEdge(self.cfg, start1, end1)
 
-proc buildStraight(self: var LoopTesterApp, start: int, n: int): int =
+proc buildStraight(self: var LoopTesterApp, start, n: int): int =
   for i in 0..n-1:
     self.buildConnect(start + i, start + i + 1)
   result = start + n
 
 proc buildBaseLoop(self: var LoopTesterApp, from1: int): int =
-  var header   = self.buildStraight(from1, 1)
-  var diamond1 = self.buildDiamond(header)
-  var d11      = self.buildStraight(diamond1, 1)
-  var diamond2 = self.buildDiamond(d11)
-  var footer   = self.buildStraight(diamond2, 1)
+  let header = self.buildStraight(from1, 1)
+  let diamond1 = self.buildDiamond(header)
+  let d11 = self.buildStraight(diamond1, 1)
+  let diamond2 = self.buildDiamond(d11)
+  let footer = self.buildStraight(diamond2, 1)
 
   self.buildConnect(diamond2, d11)
   self.buildConnect(diamond1, header)
@@ -410,48 +379,63 @@ proc run(self: var LoopTesterApp) =
   echo "Welcome to LoopTesterApp, Nim edition"
   echo "Constructing Simple CFG..."
 
-  var x1 = self.cfg.createNode(0)
-  var x2 = self.buildBaseLoop(0)
-  var x3 = self.cfg.createNode(1)
+  discard self.cfg.createNode(0)
+  discard self.buildBaseLoop(0)
+  discard self.cfg.createNode(1)
   self.buildConnect(0, 2)
 
-  echo "15000 dummy loops"
+  echo "5000 dummy loops"
 
-  for i in 1..15000:
-    var h = newHavlakLoopFinder(self.cfg, newLsg())
-    var res = h.findLoops
+  for i in 1..5000:
+    withScratchRegion:
+      var h = newHavlakLoopFinder(self.cfg, newLsg())
+      discard h.findLoops
 
   echo "Constructing CFG..."
   var n = 2
 
-  for parlooptrees in 1..10:
-    var x6 = self.cfg.createNode(n + 1)
-    self.buildConnect(2, n + 1)
-    n += 1
-    for i in 1..100:
-      var top = n
-      n = self.buildStraight(n, 1)
-      for j in 1..25: n = self.buildBaseLoop(n)
-      var bottom = self.buildStraight(n, 1)
-      self.buildConnect n, top
-      n = bottom
-    self.buildConnect(n, 1)
+  when true: # not defined(gcOrc):
+    # currently cycle detection is so slow that we disable this part
+    for parlooptrees in 1..10:
+      discard self.cfg.createNode(n + 1)
+      self.buildConnect(2, n + 1)
+      n += 1
+      for i in 1..100:
+        var top = n
+        n = self.buildStraight(n, 1)
+        for j in 1..25: n = self.buildBaseLoop(n)
+        var bottom = self.buildStraight(n, 1)
+        self.buildConnect n, top
+        n = bottom
+      self.buildConnect(n, 1)
 
   echo "Performing Loop Recognition\n1 Iteration"
 
   var h = newHavlakLoopFinder(self.cfg, newLsg())
   var loops = h.findLoops
 
-  echo "Another 50 iterations..."
+  echo "Another 3 iterations..."
 
   var sum = 0
-  for i in 1..50:
-    write stdout, "."
-    flushFile(stdout)
-    var hlf = newHavlakLoopFinder(self.cfg, newLsg())
-    sum += hlf.findLoops
-    #echo getOccupiedMem()
+  for i in 1..3:
+    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, ")"
 
-var l = newLoopTesterApp()
-l.run
+  when false:
+    echo("Total memory available: " & formatSize(getTotalMem()) & " bytes")
+    echo("Free memory: " & formatSize(getFreeMem()) & " bytes")
+
+proc main =
+  var l = newLoopTesterApp()
+  l.run
+
+let mem = getOccupiedMem()
+main()
+when defined(gcOrc):
+  GC_fullCollect()
+  doAssert getOccupiedMem() == mem
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/gc/trace_globals.nim b/tests/gc/trace_globals.nim
new file mode 100644
index 000000000..f62a15692
--- /dev/null
+++ b/tests/gc/trace_globals.nim
@@ -0,0 +1,33 @@
+discard """
+  output: '''
+10000000
+10000000
+10000000'''
+"""
+
+# bug #17085
+
+#[
+refs https://github.com/nim-lang/Nim/issues/17085#issuecomment-786466595
+with --gc:boehm, this warning sometimes gets generated:
+Warning: Repeated allocation of very large block (appr. size 14880768):
+May lead to memory leak and poor performance.
+nim CI now runs this test with `testWithoutBoehm` to avoid running it with --gc:boehm.
+]#
+
+proc init(): string =
+  for a in 0..<10000000:
+    result.add 'c'
+
+proc f() =
+  var a {.global.} = init()
+  var b {.global.} = init()
+  var c {.global.} = init()
+
+  echo a.len
+    # `echo` intentional according to
+    # https://github.com/nim-lang/Nim/pull/17469/files/0c9e94cb6b9ebca9da7cb19a063fba7aa409748e#r600016573
+  echo b.len
+  echo c.len
+
+f()
diff --git a/tests/gc/tregionleak.nim b/tests/gc/tregionleak.nim
new file mode 100644
index 000000000..277cfc987
--- /dev/null
+++ b/tests/gc/tregionleak.nim
@@ -0,0 +1,23 @@
+discard """
+  cmd: '''nim c --gc:regions $file'''
+  output: '''
+finalized
+finalized
+'''
+"""
+
+proc finish(o: RootRef) =
+  echo "finalized"
+
+withScratchRegion:
+  var test: RootRef
+  new(test, finish)
+
+var
+  mr: MemRegion
+  test: RootRef
+
+withRegion(mr):
+  new(test, finish)
+
+deallocAll(mr)
diff --git a/tests/gc/tstandalone.nim b/tests/gc/tstandalone.nim
new file mode 100644
index 000000000..41dad9ba4
--- /dev/null
+++ b/tests/gc/tstandalone.nim
@@ -0,0 +1,14 @@
+discard """
+  matrix: "--os:standalone --gc:none"
+  exitcode: 1
+  output: "value out of range"
+"""
+
+type
+  rangeType = range[0..1]
+
+var
+  r: rangeType = 0
+  i = 2
+
+r = rangeType(i)
diff --git a/tests/gc/weakrefs.nim b/tests/gc/weakrefs.nim
index 0a6d4b873..81c048d74 100644
--- a/tests/gc/weakrefs.nim
+++ b/tests/gc/weakrefs.nim
@@ -19,8 +19,15 @@ var
 proc finalizer(x: StrongObject) =
   valid.excl(x.id)
 
+when defined(gcDestructors):
+  proc `=destroy`(x: var TMyObject) =
+    valid.excl(x.id)
+
 proc create: StrongObject =
-  new(result, finalizer)
+  when defined(gcDestructors):
+    new(result)
+  else:
+    new(result, finalizer)
   result.id = gid
   valid.incl(gid)
   inc gid
diff --git a/tests/generics/m14509.nim b/tests/generics/m14509.nim
new file mode 100644
index 000000000..cabc4f308
--- /dev/null
+++ b/tests/generics/m14509.nim
@@ -0,0 +1,16 @@
+import macros
+
+type float32x4 = array[4, float32]
+type float32x8 = array[8, float32]
+
+{.experimental: "dynamicBindSym".}
+macro dispatch(N: static int, T: type SomeNumber): untyped =
+  let BaseT = getTypeInst(T)[1]
+  result = bindSym($BaseT & "x" & $N)
+
+type
+  VecIntrin*[N: static int, T: SomeNumber] = dispatch(N, T)
+
+func `$`*[N, T](vec: VecIntrin[N, T]): string =
+  ## Display a vector
+  $cast[array[N, T]](vec)
diff --git a/tests/generics/m22373a.nim b/tests/generics/m22373a.nim
new file mode 100644
index 000000000..28e087ca6
--- /dev/null
+++ b/tests/generics/m22373a.nim
@@ -0,0 +1,7 @@
+# module a for t22373
+
+# original:
+type LightClientHeader* = object
+
+# simplified:
+type TypeOrTemplate* = object
diff --git a/tests/generics/m22373b.nim b/tests/generics/m22373b.nim
new file mode 100644
index 000000000..67ee4211b
--- /dev/null
+++ b/tests/generics/m22373b.nim
@@ -0,0 +1,18 @@
+# module b for t22373
+
+import m22373a
+
+# original:
+type
+  LightClientDataFork* {.pure.} = enum
+    None = 0,
+    Altair = 1
+template LightClientHeader*(kind: static LightClientDataFork): auto =
+  when kind == LightClientDataFork.Altair:
+    typedesc[m22373a.LightClientHeader]
+  else:
+    static: raiseAssert "Unreachable"
+
+# simplified:
+template TypeOrTemplate*(num: int): untyped =
+  typedesc[m22373a.TypeOrTemplate]
diff --git a/tests/generics/m3770.nim b/tests/generics/m3770.nim
new file mode 100644
index 000000000..7f5714a2b
--- /dev/null
+++ b/tests/generics/m3770.nim
@@ -0,0 +1,11 @@
+type
+  Noice* = object
+    hidden: int
+
+template jjj*: Noice =
+  Noice(hidden: 15)
+
+type Opt* = object
+  o: int
+
+template none*(O: type Opt): Opt = Opt(o: 0)
diff --git a/tests/generics/mdotlookup.nim b/tests/generics/mdotlookup.nim
index 2984574c2..090b97771 100644
--- a/tests/generics/mdotlookup.nim
+++ b/tests/generics/mdotlookup.nim
@@ -1,16 +1,28 @@
-proc baz(o: any): int = 5 # if bar is exported, it works
+proc baz(o: auto): int = 5 # if bar is exported, it works
 
 type MyObj = object
   x: int
 
-proc foo*(b: any) =
+proc foo*(b: auto) =
   var o: MyObj
   echo b.baz, " ", o.x.baz, " ", b.baz()
 
 import sets
 
-var intset = initSet[int]()
+var intset = initHashSet[int]()
 
 proc fn*[T](a: T) =
   if a in intset: echo("true")
   else: echo("false")
+
+import strutils
+
+proc doStrip*[T](a: T): string =
+  result = ($a).strip()
+
+type Foo = int32
+proc baz2*[T](y: int): auto =
+  result = y.Foo
+
+proc set*(x: var int, a, b: string) =
+  x = a.len + b.len
diff --git a/tests/friends/mfriends.nim b/tests/generics/mfriends.nim
index 19672289e..19672289e 100644
--- a/tests/friends/mfriends.nim
+++ b/tests/generics/mfriends.nim
diff --git a/tests/generics/module_with_generics.nim b/tests/generics/module_with_generics.nim
index e801a4790..960a694d7 100644
--- a/tests/generics/module_with_generics.nim
+++ b/tests/generics/module_with_generics.nim
@@ -1,5 +1,5 @@
 type
-  Base[T] = ref object {.inheritable.}
+  Base[T] {.inheritable.} = ref object
     value*: T
 
   Derived[T] = ref object of Base[T]
diff --git a/tests/generics/mopensymimport1.nim b/tests/generics/mopensymimport1.nim
new file mode 100644
index 000000000..912db1302
--- /dev/null
+++ b/tests/generics/mopensymimport1.nim
@@ -0,0 +1,34 @@
+type
+  Result*[T, E] = object
+    when T is void:
+      when E is void:
+        oResultPrivate*: bool
+      else:
+        case oResultPrivate*: bool
+        of false:
+          eResultPrivate*: E
+        of true:
+          discard
+    else:
+      when E is void:
+        case oResultPrivate*: bool
+        of false:
+          discard
+        of true:
+          vResultPrivate*: T
+      else:
+        case oResultPrivate*: bool
+        of false:
+          eResultPrivate*: E
+        of true:
+          vResultPrivate*: T
+
+template valueOr*[T: not void, E](self: Result[T, E], def: untyped): untyped =
+  let s = (self) # TODO avoid copy
+  case s.oResultPrivate
+  of true:
+    s.vResultPrivate
+  of false:
+    when E isnot void:
+      template error: untyped {.used, inject.} = s.eResultPrivate
+    def
diff --git a/tests/generics/mopensymimport2.nim b/tests/generics/mopensymimport2.nim
new file mode 100644
index 000000000..c17aafd00
--- /dev/null
+++ b/tests/generics/mopensymimport2.nim
@@ -0,0 +1,16 @@
+{.experimental: "openSym".}
+
+import mopensymimport1
+
+type Xxx = enum
+  error
+  value
+
+proc f(): Result[int, cstring] =
+  Result[int, cstring](oResultPrivate: false, eResultPrivate: "f")
+
+proc g*(T: type): string =
+  let x = f().valueOr:
+    return $error
+
+  "ok"
diff --git a/tests/generics/mtypenodes.nim b/tests/generics/mtypenodes.nim
new file mode 100644
index 000000000..e1132241b
--- /dev/null
+++ b/tests/generics/mtypenodes.nim
@@ -0,0 +1,6 @@
+# issue #22699
+
+type Private = distinct int
+
+proc chop*[T](x: int): int =
+  cast[int](cast[tuple[field: Private]](x))
diff --git a/tests/generics/muninstantiatedgenericcalls.nim b/tests/generics/muninstantiatedgenericcalls.nim
new file mode 100644
index 000000000..caed07c98
--- /dev/null
+++ b/tests/generics/muninstantiatedgenericcalls.nim
@@ -0,0 +1,26 @@
+import std/bitops
+
+const
+  lengths = block:
+    var v: array[64, int8]
+    for i in 0..<64:
+      v[i] = int8((i + 7) div 7)
+    v
+
+type
+  Leb128* = object
+
+{.push checks: off.}
+func len(T: type Leb128, x: SomeUnsignedInt): int8 =
+  if x == 0: 1
+  else: lengths[fastLog2(x)]
+{.pop.}
+
+# note private to test scoping issue:
+func maxLen(T: type Leb128, I: type): int8 =
+  Leb128.len(I.high)
+
+type
+  Leb128Buf*[T: SomeUnsignedInt] = object
+    data*: array[maxLen(Leb128, T), byte] 
+    len*: int8
diff --git a/tests/generics/t1050.nim b/tests/generics/t1050.nim
deleted file mode 100644
index 9e83b5ff0..000000000
--- a/tests/generics/t1050.nim
+++ /dev/null
@@ -1,29 +0,0 @@
-discard """
-  msg: "int"
-  output: "4"
-"""
-
-import typetraits
-
-type ArrayType[T] = distinct T
-
-proc arrayItem(a: ArrayType): auto =
-  static: echo(name(type(a).T))
-  result = (type(a).T)(4)
-
-var arr: ArrayType[int]
-echo arrayItem(arr)
-
-# bug #5597
-
-template fail() = "what"
-
-proc g[T](x: var T) =
-  x.fail = 3
-
-type
-  Obj = object
-    fail: int
-
-var y: Obj
-g y
diff --git a/tests/generics/t1056.nim b/tests/generics/t1056.nim
deleted file mode 100644
index de8bde8ef..000000000
--- a/tests/generics/t1056.nim
+++ /dev/null
@@ -1,25 +0,0 @@
-discard """
-  output: '''TMatrix[3, 3, system.int]
-3'''
-"""
-
-import typetraits
-
-type
-  TMatrix*[N,M: static[int], T] = object
-    data*: array[0..N*M-1, T]
-
-  TMat2[T] = TMatrix[2,2,T]
-
-proc echoMatrix(a: TMatrix) =
-  echo a.type.name
-  echo TMatrix.N
-
-proc echoMat2(a: TMat2) =
-  echo TMat2.M
-
-var m = TMatrix[3,3,int](data: [1,2,3,4,5,6,7,8,9])
-
-echoMatrix m
-#echoMat2 m
-
diff --git a/tests/generics/t12938.nim b/tests/generics/t12938.nim
new file mode 100644
index 000000000..e09d65c7a
--- /dev/null
+++ b/tests/generics/t12938.nim
@@ -0,0 +1,9 @@
+type
+  ExampleArray[Size, T] = array[Size, T]
+
+var integerArray: ExampleArray[32, int]  # Compiler crash!
+doAssert integerArray.len == 32
+
+const Size = 2
+var integerArray2: ExampleArray[Size, int]
+doAssert integerArray2.len == 2
diff --git a/tests/generics/t13525.nim b/tests/generics/t13525.nim
new file mode 100644
index 000000000..1fd84852b
--- /dev/null
+++ b/tests/generics/t13525.nim
@@ -0,0 +1,6 @@
+# https://github.com/nim-lang/Nim/issues/13524
+template fun(field): untyped = astToStr(field)
+proc test1(): string = fun(nonexistent1)
+proc test2[T](): string = fun(nonexistent2) # used to cause: Error: undeclared identifier: 'nonexistent2'
+doAssert test1() == "nonexistent1"
+doAssert test2[int]() == "nonexistent2"
diff --git a/tests/generics/t14193.nim b/tests/generics/t14193.nim
new file mode 100644
index 000000000..213b1a8e6
--- /dev/null
+++ b/tests/generics/t14193.nim
@@ -0,0 +1,6 @@
+type
+  Task*[N: int] = object
+    env*: array[N, byte]
+
+var task14193: Task[20]
+doAssert task14193.env.len == 20
diff --git a/tests/generics/t14509.nim b/tests/generics/t14509.nim
new file mode 100644
index 000000000..ef3143ee4
--- /dev/null
+++ b/tests/generics/t14509.nim
@@ -0,0 +1,4 @@
+import m14509
+
+var v: VecIntrin[4, float32]
+doAssert $v == "[0.0, 0.0, 0.0, 0.0]"
diff --git a/tests/generics/t1500.nim b/tests/generics/t1500.nim
new file mode 100644
index 000000000..6dd457d33
--- /dev/null
+++ b/tests/generics/t1500.nim
@@ -0,0 +1,8 @@
+#issue 1500
+
+type
+  TFtpBase*[SockType] = object
+    job: TFTPJob[SockType]
+  PFtpBase*[SockType] = ref TFtpBase[SockType]
+  TFtpClient* = TFtpBase[string]
+  TFTPJob[T] = object
\ No newline at end of file
diff --git a/tests/generics/t17509.nim b/tests/generics/t17509.nim
new file mode 100644
index 000000000..89f507577
--- /dev/null
+++ b/tests/generics/t17509.nim
@@ -0,0 +1,25 @@
+type List[O] = object
+  next: ptr List[O]
+
+proc initList[O](l: ptr List[O]) =
+  l[].next = l
+
+type
+  PolytopeVertex[R] = object
+    list: List[PolytopeVertex[R]]
+
+  PolytopeEdge[R] = object
+    list: List[PolytopeEdge[R]]
+
+  Polytope[R] = object
+    vertices: List[PolytopeVertex[R]]
+    edges: List[PolytopeEdge[R]]
+
+var pt: Polytope[float]
+
+static:
+  doAssert pt.vertices.next is (ptr List[PolytopeVertex[float]])
+  doAssert pt.edges.next is (ptr List[PolytopeEdge[float]])
+
+initList(addr pt.vertices)
+initList(addr pt.edges)
\ No newline at end of file
diff --git a/tests/generics/t1789.nim b/tests/generics/t1789.nim
deleted file mode 100644
index c3fe336af..000000000
--- a/tests/generics/t1789.nim
+++ /dev/null
@@ -1,44 +0,0 @@
-discard """
-  output: "3\n0"
-"""
-
-# https://github.com/Araq/Nim/issues/1789
-
-type
-  Foo[N: static[int]] = object
-
-proc bindStaticN[N](foo: Foo[N]) =
-  var ar0: array[3, int]
-  var ar1: array[N, int]
-  var ar2: array[1..N, int]
-  var ar3: array[0..(N+10), float]
-  echo N
-
-var f: Foo[3]
-f.bindStaticN
-
-# case 2
-
-type
-  ObjectWithStatic[X, Y: static[int], T] = object
-    bar: array[X * Y, T]   # this one works
-
-  AliasWithStatic[X, Y: static[int], T] = array[X * Y, T]
-
-var
-  x: ObjectWithStatic[1, 2, int]
-  y: AliasWithStatic[2, 3, int]
-
-# case 3
-
-type
-  Bar[N: static[int], T] = object
-    bar: array[N, T]
-
-proc `[]`*[N, T](f: Bar[N, T], n: range[0..(N - 1)]): T =
-  assert high(n) == N-1
-  result = f.bar[n]
-
-var b: Bar[3, int]
-echo b[2]
-
diff --git a/tests/generics/t18823.nim b/tests/generics/t18823.nim
new file mode 100644
index 000000000..94c79aebe
--- /dev/null
+++ b/tests/generics/t18823.nim
@@ -0,0 +1,6 @@
+type BitsRange[T] = range[0..sizeof(T)*8-1]
+
+proc bar[T](a: T; b: BitsRange[T]) =
+  discard
+
+bar(1, 2.Natural)
diff --git a/tests/generics/t18859.nim b/tests/generics/t18859.nim
new file mode 100644
index 000000000..ca6c3d10b
--- /dev/null
+++ b/tests/generics/t18859.nim
@@ -0,0 +1,17 @@
+import macros
+
+macro symFromDesc(T: typedesc): untyped =
+  let typ = getType(T)
+  typ[1]
+
+template produceType(T: typedesc): untyped =
+  type
+    XT = object
+      x: symFromDesc(T)
+
+  XT
+
+type
+  X[T] = produceType(T)
+
+var x: X[int]
diff --git a/tests/generics/t19848.nim b/tests/generics/t19848.nim
new file mode 100644
index 000000000..f80f0e298
--- /dev/null
+++ b/tests/generics/t19848.nim
@@ -0,0 +1,16 @@
+discard """
+  output: '''
+todo
+'''
+"""
+
+type
+  Maybe[T] = object
+  List[T] = object
+
+proc dump[M: Maybe](a: List[M]) =
+  echo "todo"
+
+var a: List[Maybe[int]]
+  
+dump(a)
diff --git a/tests/generics/t20996.nim b/tests/generics/t20996.nim
new file mode 100644
index 000000000..8aa83c4e2
--- /dev/null
+++ b/tests/generics/t20996.nim
@@ -0,0 +1,15 @@
+discard """
+  action: compile
+"""
+
+import std/macros
+
+macro matchMe(x: typed): untyped =
+  discard x.getTypeImpl
+
+type
+  ElementRT = object
+  Element[Z] = ElementRT # this version is needed, even though we don't use it
+
+let ar = ElementRT()
+matchMe(ar)
diff --git a/tests/generics/t21742.nim b/tests/generics/t21742.nim
new file mode 100644
index 000000000..c49c8ee97
--- /dev/null
+++ b/tests/generics/t21742.nim
@@ -0,0 +1,10 @@
+type
+  Foo[T] = object
+    x:T
+  Bar[T,R] = Foo[T]
+  Baz = Bar[int,float]
+
+proc qux[T,R](x: Bar[T,R]) = discard
+
+var b:Baz
+b.qux()
\ No newline at end of file
diff --git a/tests/generics/t21760.nim b/tests/generics/t21760.nim
new file mode 100644
index 000000000..5343279bb
--- /dev/null
+++ b/tests/generics/t21760.nim
@@ -0,0 +1,8 @@
+import std/tables
+
+type Url = object
+
+proc myInit(_: type[Url], params = default(Table[string, string])): Url =
+  discard
+
+discard myInit(Url)
\ No newline at end of file
diff --git a/tests/generics/t21958.nim b/tests/generics/t21958.nim
new file mode 100644
index 000000000..f566b57cb
--- /dev/null
+++ b/tests/generics/t21958.nim
@@ -0,0 +1,11 @@
+discard """
+  action: compile
+"""
+
+type
+  Ct*[T: SomeUnsignedInt] = distinct T
+
+template `shr`*[T: Ct](x: T, y: SomeInteger): T = T(T.T(x) shr y)
+
+var x: Ct[uint64]
+let y {.used.} = x shr 2
\ No newline at end of file
diff --git a/tests/generics/t22373.nim b/tests/generics/t22373.nim
new file mode 100644
index 000000000..ecfaf0f1b
--- /dev/null
+++ b/tests/generics/t22373.nim
@@ -0,0 +1,16 @@
+# issue #22373
+
+import m22373a
+import m22373b
+
+# original:
+template lazy_header(name: untyped): untyped {.dirty.} =
+  var `name _ ptr`: ptr[data_fork.LightClientHeader]  # this data_fork.Foo part seems required to reproduce
+proc createLightClientUpdates(data_fork: static LightClientDataFork) =
+  lazy_header(attested_header)
+createLightClientUpdates(LightClientDataFork.Altair)
+
+# simplified:
+proc generic[T](abc: T) =
+  var x: abc.TypeOrTemplate
+generic(123)
diff --git a/tests/generics/t22826.nim b/tests/generics/t22826.nim
new file mode 100644
index 000000000..914d4243a
--- /dev/null
+++ b/tests/generics/t22826.nim
@@ -0,0 +1,8 @@
+import std/tables
+
+var a: Table[string, float]
+
+type Value*[T] = object
+  table: Table[string, Value[T]]
+
+discard toTable({"a": Value[float]()})
\ No newline at end of file
diff --git a/tests/generics/t23186.nim b/tests/generics/t23186.nim
new file mode 100644
index 000000000..76f38da6b
--- /dev/null
+++ b/tests/generics/t23186.nim
@@ -0,0 +1,155 @@
+# issue #23186
+
+block: # simplified
+  template typedTempl(x: int, body): untyped =
+    body
+  proc generic1[T]() =
+    discard
+  proc generic2[T]() =
+    typedTempl(1):
+      let x = generic1[T]
+  generic2[int]()
+
+import std/macros
+
+when not compiles(len((1, 2))):
+  import std/typetraits
+
+  func len(x: tuple): int =
+    arity(type(x))
+
+block: # full issue example
+  type FieldDescription = object
+    name: NimNode
+  func isTuple(t: NimNode): bool =
+    t.kind == nnkBracketExpr and t[0].kind == nnkSym and eqIdent(t[0], "tuple")
+  proc collectFieldsFromRecList(result: var seq[FieldDescription],
+                                n: NimNode,
+                                parentCaseField: NimNode = nil,
+                                parentCaseBranch: NimNode = nil,
+                                isDiscriminator = false) =
+    case n.kind
+    of nnkRecList:
+      for entry in n:
+        collectFieldsFromRecList result, entry,
+                                parentCaseField, parentCaseBranch
+    of nnkIdentDefs:
+      for i in 0 ..< n.len - 2:
+        var field: FieldDescription
+        field.name = n[i]
+        if field.name.kind == nnkPragmaExpr:
+          field.name = field.name[0]
+        if field.name.kind == nnkPostfix:
+          field.name = field.name[1]
+        result.add field
+    of nnkNilLit, nnkDiscardStmt, nnkCommentStmt, nnkEmpty:
+      discard
+    else:
+      doAssert false, "Unexpected nodes in recordFields:\n" & n.treeRepr
+  proc collectFieldsInHierarchy(result: var seq[FieldDescription],
+                                objectType: NimNode) =
+    var objectType = objectType
+    if objectType.kind == nnkRefTy:
+      objectType = objectType[0]
+    let recList = objectType[2]
+    collectFieldsFromRecList result, recList
+  proc recordFields(typeImpl: NimNode): seq[FieldDescription] =
+    let objectType = case typeImpl.kind
+      of nnkObjectTy: typeImpl
+      of nnkTypeDef: typeImpl[2]
+      else:
+        macros.error("object type expected", typeImpl)
+        return
+    collectFieldsInHierarchy(result, objectType)
+  proc skipPragma(n: NimNode): NimNode =
+    if n.kind == nnkPragmaExpr: n[0]
+    else: n
+  func declval(T: type): T =
+    doAssert false,
+      "declval should be used only in `typeof` expressions and concepts"
+    default(ptr T)[]
+  macro enumAllSerializedFieldsImpl(T: type, body: untyped): untyped =
+    var typeAst = getType(T)[1]
+    var typeImpl: NimNode
+    let isSymbol = not typeAst.isTuple
+    if not isSymbol:
+      typeImpl = typeAst
+    else:
+      typeImpl = getImpl(typeAst)
+    result = newStmtList()
+    var i = 0
+    for field in recordFields(typeImpl):
+      let
+        fieldIdent = field.name
+        realFieldName = newLit($fieldIdent.skipPragma)
+        fieldName = realFieldName
+        fieldIndex = newLit(i)
+      let fieldNameDefs =
+        if isSymbol:
+          quote:
+            const fieldName {.inject, used.} = `fieldName`
+            const realFieldName {.inject, used.} = `realFieldName`
+        else:
+          quote:
+            const fieldName {.inject, used.} = $`fieldIndex`
+            const realFieldName {.inject, used.} = $`fieldIndex`
+            # we can't access .Fieldn, so our helper knows
+            # to parseInt this
+      let field =
+        if isSymbol:
+          quote do: declval(`T`).`fieldIdent`
+        else:
+          quote do: declval(`T`)[`fieldIndex`]
+      result.add quote do:
+        block:
+          `fieldNameDefs`
+          type FieldType {.inject, used.} = type(`field`)
+          `body`
+      i += 1
+  template enumAllSerializedFields(T: type, body): untyped =
+    when T is ref|ptr:
+      type TT = type(default(T)[])
+      enumAllSerializedFieldsImpl(TT, body)
+    else:
+      enumAllSerializedFieldsImpl(T, body)
+  type
+    MemRange = object
+      startAddr: ptr byte
+      length: int
+    SszNavigator[T] = object
+      m: MemRange
+  func sszMount(data: openArray[byte], T: type): SszNavigator[T] =
+    let startAddr = unsafeAddr data[0]
+    SszNavigator[T](m: MemRange(startAddr: startAddr, length: data.len))
+  func sszMount(data: openArray[char], T: type): SszNavigator[T] =
+    let startAddr = cast[ptr byte](unsafeAddr data[0])
+    SszNavigator[T](m: MemRange(startAddr: startAddr, length: data.len))
+  template sszMount(data: MemRange, T: type): SszNavigator[T] =
+    SszNavigator[T](m: data)
+  func navigateToField[T](
+      n: SszNavigator[T],
+      FieldType: type): SszNavigator[FieldType] =
+    default(SszNavigator[FieldType])
+  type
+    FieldInfo = ref object
+      navigator: proc (m: MemRange): MemRange {.
+        gcsafe, noSideEffect, raises: [IOError] .}
+  func fieldNavigatorImpl[RecordType; FieldType; fieldName: static string](
+      m: MemRange): MemRange =
+    var typedNavigator = sszMount(m, RecordType)
+    discard navigateToField(typedNavigator, FieldType)
+    default(MemRange)
+  func genTypeInfo(T: type) =
+    when T is object:
+      enumAllSerializedFields(T):
+        discard FieldInfo(navigator: fieldNavigatorImpl[T, FieldType, fieldName])
+  type
+    Foo = object
+      bar: Bar
+    BarList = seq[uint64]
+    Bar = object
+      b: BarList
+      baz: Baz
+    Baz = object
+      i: uint64
+  genTypeInfo(Foo)
diff --git a/tests/generics/t23790.nim b/tests/generics/t23790.nim
new file mode 100644
index 000000000..9ac0df6a1
--- /dev/null
+++ b/tests/generics/t23790.nim
@@ -0,0 +1,14 @@
+# bug #23790
+
+discard compiles($default(seq[seq[ref int]]))
+discard compiles($default(seq[seq[ref uint]]))
+discard compiles($default(seq[seq[ref int8]]))
+discard compiles($default(seq[seq[ref uint8]]))
+discard compiles($default(seq[seq[ref int16]]))
+discard compiles($default(seq[seq[ref uint16]]))
+discard compiles($default(seq[seq[ref int32]]))
+discard compiles($default(seq[seq[ref uint32]]))
+discard compiles($default(seq[seq[ref int64]]))
+discard compiles($default(seq[seq[ref uint64]]))
+proc s(_: int | string) = discard
+s(0)
diff --git a/tests/generics/t23853.nim b/tests/generics/t23853.nim
new file mode 100644
index 000000000..bc9514a53
--- /dev/null
+++ b/tests/generics/t23853.nim
@@ -0,0 +1,91 @@
+# issue #23853
+
+block simplified:
+  type QuadraticExt[F] = object
+    coords: array[2, F]
+  template Name(E: type QuadraticExt): int = 123
+  template getBigInt(Name: static int): untyped = int
+  type Foo[GT] = object
+    a: getBigInt(GT.Name)
+  var x: Foo[QuadraticExt[int]]
+  
+import std/macros
+
+type
+  Algebra* = enum
+    BN254_Snarks
+    BLS12_381
+
+  Fp*[Name: static Algebra] = object
+    limbs*: array[4, uint64]
+
+  QuadraticExt*[F] = object
+    ## Quadratic Extension field
+    coords*: array[2, F]
+
+  CubicExt*[F] = object
+    ## Cubic Extension field
+    coords*: array[3, F]
+
+  ExtensionField*[F] = QuadraticExt[F] or CubicExt[F]
+
+  Fp2*[Name: static Algebra] =
+    QuadraticExt[Fp[Name]]
+
+  Fp4*[Name: static Algebra] =
+    QuadraticExt[Fp2[Name]]
+
+  Fp6*[Name: static Algebra] =
+    CubicExt[Fp2[Name]]
+
+  Fp12*[Name: static Algebra] =
+    CubicExt[Fp4[Name]]
+    # QuadraticExt[Fp6[Name]]
+
+template Name*(E: type ExtensionField): Algebra =
+  E.F.Name
+
+const BLS12_381_Order = [uint64 0x1, 0x2, 0x3, 0x4]
+const BLS12_381_Modulus = [uint64 0x5, 0x6, 0x7, 0x8]
+
+
+{.experimental: "dynamicBindSym".}
+
+macro baseFieldModulus*(Name: static Algebra): untyped =
+  result = bindSym($Name & "_Modulus")
+
+macro scalarFieldModulus*(Name: static Algebra): untyped =
+  result = bindSym($Name & "_Order")
+
+type FieldKind* = enum
+  kBaseField
+  kScalarField
+
+template getBigInt*(Name: static Algebra, kind: static FieldKind): untyped =
+  # Workaround:
+  # in `ptr UncheckedArray[BigInt[EC.getScalarField().bits()]]
+  # EC.getScalarField is not accepted by the compiler
+  #
+  # and `ptr UncheckedArray[BigInt[Fr[EC.F.Name].bits]]` gets undeclared field: 'Name'
+  #
+  # but `ptr UncheckedArray[getBigInt(EC.getName(), kScalarField)]` works fine
+  when kind == kBaseField:
+    Name.baseFieldModulus().typeof()
+  else:
+    Name.scalarFieldModulus().typeof()
+
+# ------------------------------------------------------------------------------
+
+type BenchMultiexpContext*[GT] = object
+  elems: seq[GT]
+  exponents: seq[getBigInt(GT.Name, kScalarField)]
+
+proc createBenchMultiExpContext*(GT: typedesc, inputSizes: openArray[int]): BenchMultiexpContext[GT] =
+  discard
+
+# ------------------------------------------------------------------------------
+
+proc main() =
+  let ctx = createBenchMultiExpContext(Fp12[BLS12_381], [2, 4, 8, 16])
+
+main()
diff --git a/tests/generics/t23854.nim b/tests/generics/t23854.nim
new file mode 100644
index 000000000..f1175c8b2
--- /dev/null
+++ b/tests/generics/t23854.nim
@@ -0,0 +1,71 @@
+# issue #23854, not entirely fixed
+
+import std/bitops
+
+const WordBitWidth = sizeof(pointer) * 8
+
+func wordsRequired*(bits: int): int {.inline.} =
+  const divShiftor = fastLog2(uint32(WordBitWidth))
+  result = (bits + WordBitWidth - 1) shr divShiftor
+
+type
+  Algebra* = enum
+    BLS12_381
+
+  BigInt*[bits: static int] = object
+    limbs*: array[wordsRequired(bits), uint]
+
+  Fr*[Name: static Algebra] = object
+    residue_form*: BigInt[255]
+
+  Fp*[Name: static Algebra] = object
+    residue_form*: BigInt[381]
+
+  FF*[Name: static Algebra] = Fp[Name] or Fr[Name]
+
+template getBigInt*[Name: static Algebra](T: type FF[Name]): untyped =
+  ## Get the underlying BigInt type.
+  typeof(default(T).residue_form)
+
+type
+  EC_ShortW_Aff*[F] = object
+    ## Elliptic curve point for a curve in Short Weierstrass form
+    ##   y² = x³ + a x + b
+    ##
+    ## over a field F
+    x*, y*: F
+
+type FieldKind* = enum
+  kBaseField
+  kScalarField
+
+func bits*[Name: static Algebra](T: type FF[Name]): static int =
+  T.getBigInt().bits
+
+template getScalarField*(EC: type EC_ShortW_Aff): untyped =
+  Fr[EC.F.Name]
+
+# ------------------------------------------------------------------------------
+
+type
+  ECFFT_Descriptor*[EC] = object
+    ## Metadata for FFT on Elliptic Curve
+    order*: int
+    rootsOfUnity1*: ptr UncheckedArray[BigInt[EC.getScalarField().bits()]]  # Error: in expression 'EC.getScalarField()': identifier expected, but found 'EC.getScalarField'
+    rootsOfUnity2*: ptr UncheckedArray[BigInt[getScalarField(EC).bits()]] # Compiler SIGSEGV: Illegal Storage Access
+
+func new*(T: type ECFFT_Descriptor): T =
+  discard
+
+# ------------------------------------------------------------------------------
+
+template getBits[bits: static int](x: ptr UncheckedArray[BigInt[bits]]): int = bits
+
+proc main() =
+  let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new()
+  doAssert getBits(ctx.rootsOfUnity1) == 255
+  doAssert getBits(ctx.rootsOfUnity2) == 255
+  doAssert ctx.rootsOfUnity1[0].limbs.len == wordsRequired(255)
+  doAssert ctx.rootsOfUnity2[0].limbs.len == wordsRequired(255)
+
+main()
diff --git a/tests/generics/t23855.nim b/tests/generics/t23855.nim
new file mode 100644
index 000000000..da8135a98
--- /dev/null
+++ b/tests/generics/t23855.nim
@@ -0,0 +1,61 @@
+# issue #23855, not entirely fixed
+
+import std/bitops
+
+const WordBitWidth = sizeof(pointer) * 8
+
+func wordsRequired*(bits: int): int {.inline.} =
+  const divShiftor = fastLog2(uint32(WordBitWidth))
+  result = (bits + WordBitWidth - 1) shr divShiftor
+
+type
+  Algebra* = enum
+    BLS12_381
+
+  BigInt*[bits: static int] = object
+    limbs*: array[wordsRequired(bits), uint]
+
+  Fr*[Name: static Algebra] = object
+    residue_form*: BigInt[255]
+
+  Fp*[Name: static Algebra] = object
+    residue_form*: BigInt[381]
+
+  FF*[Name: static Algebra] = Fp[Name] or Fr[Name]
+
+template getBigInt*[Name: static Algebra](T: type FF[Name]): untyped =
+  ## Get the underlying BigInt type.
+  typeof(default(T).residue_form)
+
+type
+  EC_ShortW_Aff*[F] = object
+    ## Elliptic curve point for a curve in Short Weierstrass form
+    ##   y² = x³ + a x + b
+    ##
+    ## over a field F
+    x*, y*: F
+
+func bits*[Name: static Algebra](T: type FF[Name]): static int =
+  T.getBigInt().bits
+
+# ------------------------------------------------------------------------------
+
+type
+  ECFFT_Descriptor*[EC] = object
+    ## Metadata for FFT on Elliptic Curve
+    order*: int
+    rootsOfUnity*: ptr UncheckedArray[BigInt[Fr[EC.F.Name].bits()]] # Undeclared identifier `Name`
+
+func new*(T: type ECFFT_Descriptor): T =
+  discard
+
+# ------------------------------------------------------------------------------
+
+template getBits[bits: static int](x: ptr UncheckedArray[BigInt[bits]]): int = bits
+
+proc main() =
+  let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new()
+  doAssert getBits(ctx.rootsOfUnity) == 255
+  doAssert ctx.rootsOfUnity[0].limbs.len == wordsRequired(255)
+
+main()
diff --git a/tests/generics/t2tables.nim b/tests/generics/t2tables.nim
index 3ef5e621e..e4b1fb967 100644
--- a/tests/generics/t2tables.nim
+++ b/tests/generics/t2tables.nim
@@ -1,3 +1,6 @@
+discard """
+action: compile
+"""
 
 # bug #3669
 
@@ -10,4 +13,3 @@ type
 
 var g: G[string]
 echo g.rnodes["foo"]
-
diff --git a/tests/generics/t3770.nim b/tests/generics/t3770.nim
new file mode 100644
index 000000000..ffccbeeb5
--- /dev/null
+++ b/tests/generics/t3770.nim
@@ -0,0 +1,13 @@
+# bug #3770
+import m3770
+
+doAssert $jjj() == "(hidden: 15)"  # works
+
+proc someGeneric(_: type) =
+  doAssert $jjj() == "(hidden: 15)" # fails: "Error: the field 'hidden' is not accessible."
+
+someGeneric(int)
+
+# bug #20900
+proc c(y: int | int, w: Opt = Opt.none) = discard
+c(0)
diff --git a/tests/generics/t3977.nim b/tests/generics/t3977.nim
deleted file mode 100644
index eed1a7d63..000000000
--- a/tests/generics/t3977.nim
+++ /dev/null
@@ -1,14 +0,0 @@
-discard """
-  output: "42\n42"
-"""
-
-type
-  Foo[N: static[int]] = object
-
-proc foo[N](x: Foo[N]) =
-  let n = N
-  echo N
-  echo n
-
-var f1: Foo[42]
-f1.foo
diff --git a/tests/generics/t4668.nim b/tests/generics/t4668.nim
new file mode 100644
index 000000000..bd44cc975
--- /dev/null
+++ b/tests/generics/t4668.nim
@@ -0,0 +1,166 @@
+discard """
+  output: '''
+foo1
+foo2
+'''
+"""
+
+block:
+  type
+    FooObj[T] = object
+      v: T
+    Foo1[T] = FooObj[T]
+    Foo2 = FooObj
+
+  proc foo1(x: Foo1) = echo "foo1"
+  proc foo2(x: Foo2) = echo "foo2"
+
+  var x: FooObj[float]
+  foo1(x)  # works
+  foo2(x)  # works
+
+block:
+  type
+    FooObj[T] = T
+    Foo1[T] = FooObj[T]
+    Foo2 = FooObj
+    Foo3 = Foo1
+    Foo4x = FooObj[SomeInteger]
+    Foo4 = FooObj[SomeFloat]
+    Foo5x = Foo1[SomeInteger]
+    Foo5 = Foo1[SomeFloat]
+
+  proc foo0(x: FooObj): int = 0
+  proc foo1(x: Foo1): int = 1
+  proc foo2(x: Foo2): int = 2
+  proc foo3(x: Foo3): int = 3
+  proc foo4(x: Foo4x): int = 40
+  proc foo4(x: Foo4): int = 4
+  proc foo5(x: Foo5x): int = 50
+  proc foo5(x: Foo5): int = 5
+
+  block:
+    var x: FooObj[float]
+    doAssert(foo0(x) == 0)
+    doAssert(foo1(x) == 1)
+    doAssert(foo2(x) == 2)
+    doAssert(foo3(x) == 3)
+    doAssert(foo4(x) == 4)
+    doAssert(foo5(x) == 5)
+
+  block:
+    var x: Foo1[float]
+    doAssert(foo0(x) == 0)
+    doAssert(foo1(x) == 1)
+    doAssert(foo2(x) == 2)
+    doAssert(foo3(x) == 3)
+    doAssert(foo4(x) == 4)
+    doAssert(foo5(x) == 5)
+
+  block:
+    var x: Foo2[float]
+    doAssert(foo0(x) == 0)
+    doAssert(foo1(x) == 1)
+    doAssert(foo2(x) == 2)
+    doAssert(foo3(x) == 3)
+    doAssert(foo4(x) == 4)
+    doAssert(foo5(x) == 5)
+
+block:
+  type
+    FooObj[T,U] = object
+      x: T
+      y: U
+    Foo1[U,T] = FooObj[T,U]
+    Foo2 = FooObj
+    Foo3 = Foo1
+    Foo4x = FooObj[SomeInteger,SomeInteger]
+    Foo4y = FooObj[SomeInteger,SomeFloat]
+    Foo4z = FooObj[SomeFloat,SomeFloat]
+    Foo4 = FooObj[SomeFloat,SomeInteger]
+    Foo5x = Foo1[SomeInteger,SomeInteger]
+    Foo5y = Foo1[SomeFloat,SomeInteger]
+    Foo5z = Foo1[SomeFloat,SomeFloat]
+    Foo5 = Foo1[SomeInteger,SomeFloat]
+
+  proc foo0(x: FooObj): int = 0
+  proc foo1(x: Foo1): int = 1
+  proc foo2(x: Foo2): int = 2
+  proc foo3(x: Foo3): int = 3
+  proc foo4(x: Foo4x): int = 40
+  proc foo4(x: Foo4y): int = 41
+  proc foo4(x: Foo4z): int = 42
+  proc foo4(x: Foo4): int = 4
+  proc foo5(x: Foo5x): int = 50
+  proc foo5(x: Foo5y): int = 51
+  proc foo5(x: Foo5z): int = 52
+  proc foo5(x: Foo5): int = 5
+
+  block:
+    var x: FooObj[float,int]
+    doAssert(foo0(x) == 0)
+    doAssert(foo1(x) == 1)
+    doAssert(foo2(x) == 2)
+    doAssert(foo3(x) == 3)
+    doAssert(foo4(x) == 4)
+    doAssert(foo5(x) == 5)
+
+  block:
+    var x: Foo1[int,float]
+    doAssert(foo0(x) == 0)
+    doAssert(foo1(x) == 1)
+    doAssert(foo2(x) == 2)
+    doAssert(foo3(x) == 3)
+    doAssert(foo4(x) == 4)
+    doAssert(foo5(x) == 5)
+
+block:
+  type
+    FooObj[T] = object of RootObj
+      v: T
+    FooObj2[T] = object of FooObj[T]
+    Foo1[T] = FooObj[T]
+    Foo2 = FooObj
+    Foo3 = Foo1
+    Foo4x = FooObj[SomeInteger]
+    Foo4 = FooObj[SomeFloat]
+    Foo5x = Foo1[SomeInteger]
+    Foo5 = Foo1[SomeFloat]
+
+  proc foo0(x: FooObj): int = 0
+  proc foo1(x: Foo1): int = 1
+  proc foo2(x: Foo2): int = 2
+  proc foo3(x: Foo3): int = 3
+  proc foo4(x: Foo4x): int = 40
+  proc foo4(x: Foo4): int = 4
+  proc foo5(x: Foo5x): int = 50
+  proc foo5(x: Foo5): int = 5
+
+  block:
+    var x: FooObj[float]
+    doAssert(foo0(x) == 0)
+    doAssert(foo1(x) == 1)
+    doAssert(foo2(x) == 2)
+    doAssert(foo3(x) == 3)
+    doAssert(foo4(x) == 4)
+    doAssert(foo5(x) == 5)
+
+  block:
+    var x: Foo1[float]
+    doAssert(foo0(x) == 0)
+    doAssert(foo1(x) == 1)
+    doAssert(foo2(x) == 2)
+    doAssert(foo3(x) == 3)
+    doAssert(foo4(x) == 4)
+    doAssert(foo5(x) == 5)
+
+  #[ XXX These still fail
+  block:
+    var x: FooObj2[float]
+    doAssert(foo0(x) == 0)
+    doAssert(foo1(x) == 1)
+    doAssert(foo2(x) == 2)
+    doAssert(foo3(x) == 3)
+    doAssert(foo4(x) == 4)
+    doAssert(foo5(x) == 5)
+  ]#
diff --git a/tests/generics/t4884.nim b/tests/generics/t4884.nim
deleted file mode 100644
index 9a560f649..000000000
--- a/tests/generics/t4884.nim
+++ /dev/null
@@ -1,11 +0,0 @@
-type
-  Vec*[N: static[int], T] = object
-    arr*: array[N, T]
-
-  Mat*[N,M: static[int], T] = object
-    arr*: array[N, Vec[M,T]]
-
-var m : Mat[3,3,float]
-var strMat : Mat[m.N, m.M, string]
-var lenMat : Mat[m.N, m.M, int]
-
diff --git a/tests/generics/t500.nim b/tests/generics/t500.nim
new file mode 100644
index 000000000..2486359aa
--- /dev/null
+++ b/tests/generics/t500.nim
@@ -0,0 +1,8 @@
+discard """
+action: compile
+"""
+
+type
+  TTest = tuple[x: range[0..80], y: range[0..25]]
+
+let x: TTest = (2, 23)
diff --git a/tests/generics/t5570.nim b/tests/generics/t5570.nim
deleted file mode 100644
index e3f9ff415..000000000
--- a/tests/generics/t5570.nim
+++ /dev/null
@@ -1,27 +0,0 @@
-discard """
-  nimout: "type uint32\ntype uint32"
-  output: "(weight: 17.0, color: 100)"
-"""
-
-import macros
-
-type
-  BaseFruit[T] = object of RootObj
-    color: T
-
-  Banana[T] = object of BaseFruit[uint32]
-    weight: T
-
-macro printTypeName(typ: typed): untyped =
-  echo "type ", getType(typ).repr
-
-proc setColor[K](self: var BaseFruit[K], c: int) =
-  printTypeName(self.color)
-  self.color = uint32(c)
-
-var x: Banana[float64]
-x.weight = 17
-printTypeName(x.color)
-x.setColor(100)
-echo x
-
diff --git a/tests/generics/t5643.nim b/tests/generics/t5643.nim
deleted file mode 100644
index 962d5cef5..000000000
--- a/tests/generics/t5643.nim
+++ /dev/null
@@ -1,30 +0,0 @@
-type
-  Matrix*[M, N: static[int], T: SomeReal] = object
-    data: ref array[N * M, T]
-
-  Matrix64*[M, N: static[int]] = Matrix[M, N, float64]
-
-proc zeros64(M,N: static[int]): Matrix64[M,N] =
-  new result.data
-  for i in 0 .. < (M * N):
-    result.data[i] = 0'f64
-
-proc bar*[M,N: static[int], T](a: Matrix[M,N,T], b: Matrix[M,N,T]) =
-  discard
-
-let a = zeros64(2,2)
-bar(a,a)
-  # https://github.com/nim-lang/Nim/issues/5643
-  #
-  # The test case was failing here, because the compiler failed to
-  # detect the two matrix instantiations as the same type.
-  #
-  # The root cause was that the `T` type variable is a different
-  # type after the first Matrix type has been matched.
-  #
-  # Sigmatch was failing to match the second version of `T`, but
-  # due to some complex interplay between tyOr, tyTypeDesc and
-  # tyGenericParam this was allowed to went through. The generic
-  # instantiation of the second matrix was incomplete and the
-  # generic cache lookup failed, producing two separate types.
-
diff --git a/tests/generics/t5683.nim b/tests/generics/t5683.nim
deleted file mode 100644
index 38da52ec2..000000000
--- a/tests/generics/t5683.nim
+++ /dev/null
@@ -1,31 +0,0 @@
-discard """
-output: "perm: 22 det: 22"
-"""
-
-type Matrix[M,N: static[int]] = array[M, array[N, float]]
-
-proc det[M,N](a: Matrix[M,N]): int = N*10 + M
-proc perm[M,N](a: Matrix[M,N]): int = M*10 + N
-
-const
-  a = [ [1.0, 2.0]
-      , [3.0, 4.0]
-      ]
-
-echo "perm: ", a.perm, " det: ", a.det
-
-# This tests multiple instantiations of a generic
-# proc involving static params:
-type
-  Vector64*[N: static[int]] = ref array[N, float64]
-  Array64[N: static[int]] = array[N, float64]
-
-proc vector*[N: static[int]](xs: Array64[N]): Vector64[N] =
-  new result
-  for i in 0 .. < N:
-    result[i] = xs[i]
-
-let v1 = vector([1.0, 2.0, 3.0, 4.0, 5.0])
-let v2 = vector([1.0, 2.0, 3.0, 4.0, 5.0])
-let v3 = vector([1.0, 2.0, 3.0, 4.0])
-
diff --git a/tests/generics/t5926.nim b/tests/generics/t5926.nim
new file mode 100644
index 000000000..bb14c3af5
--- /dev/null
+++ b/tests/generics/t5926.nim
@@ -0,0 +1,22 @@
+discard """
+action: compile
+"""
+
+type
+  SomeObj[T] = object
+
+template useSomeObj[T]() =
+  var retObj: SomeObj[T]
+
+useSomeObj[void]()
+useSomeObj[int]()
+
+
+type
+  Data*[T] = object
+    x: T
+
+template test*[T](xxx: T) =
+  let data = Data[T](x: xxx)
+
+test(1)
diff --git a/tests/generics/t6060.nim b/tests/generics/t6060.nim
new file mode 100644
index 000000000..6b1856f1c
--- /dev/null
+++ b/tests/generics/t6060.nim
@@ -0,0 +1,11 @@
+import tables
+
+type MyTab[A,B] = distinct TableRef[A,B]
+
+proc `$`[A,B](t: MyTab[A,B]): string =
+  "My special table " & $TableRef[A,B](t)
+
+proc create[A,B](): MyTab[A,B] = MyTab(newTable[A,B]())
+
+var a = create[int,int]()
+doAssert $a == "My special table {:}"
diff --git a/tests/generics/t6137.nim b/tests/generics/t6137.nim
new file mode 100644
index 000000000..fb7db22f8
--- /dev/null
+++ b/tests/generics/t6137.nim
@@ -0,0 +1,28 @@
+discard """
+  errormsg: "cannot instantiate: 'T'"
+  line: 19
+"""
+
+type
+  # simple vector of declared fixed length
+  vector[N : static[int]] = array[0..N-1, float]
+
+proc `*`[T](x: float, a: vector[T]): vector[T] =
+  # multiplication by scalar
+  for ii in 0..high(a):
+    result[ii] = a[ii]*x
+
+let
+  # define a vector of length 3
+  x: vector[3] = [1.0, 3.0, 5.0]
+
+proc vectFunc[T](x: vector[T]): vector[T] =
+  # Define a vector function
+  result = 2.0*x
+
+proc passVectFunction[T](g: proc(x: vector[T]): vector[T], x: vector[T]): vector[T] =
+  # pass a vector function as input in another procedure
+  result = g(x)
+
+let
+  xNew = passVectFunction(vectFunc,x)
diff --git a/tests/generics/t6637.nim b/tests/generics/t6637.nim
new file mode 100644
index 000000000..dd4f339a2
--- /dev/null
+++ b/tests/generics/t6637.nim
@@ -0,0 +1,9 @@
+
+type
+  Grid2D*[I: SomeInteger, w, h: static[I], T] = object
+    grid: array[w, array[h, T]]
+  Grid2DIns = Grid2D[int, 2, 3, uint8]
+
+let a = Grid2DIns()
+doAssert a.grid.len == 2
+doAssert a.grid[0].len == 3
diff --git a/tests/generics/t7141.nim b/tests/generics/t7141.nim
new file mode 100644
index 000000000..b1e9cbf43
--- /dev/null
+++ b/tests/generics/t7141.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "cannot instantiate: \'T\'"
+  line: 6
+"""
+
+proc foo[T](x: T) =
+  discard
+
+var fun = if true: foo else: foo
diff --git a/tests/generics/t7446.nim b/tests/generics/t7446.nim
new file mode 100644
index 000000000..71aa8f0e8
--- /dev/null
+++ b/tests/generics/t7446.nim
@@ -0,0 +1,10 @@
+proc foo(x: Natural or SomeUnsignedInt):int = 
+  when x is int:
+    result = 1
+  else:
+    result = 2
+let a = 10
+doAssert foo(a) == 1
+
+let b = 10'u8
+doAssert foo(b) == 2
\ No newline at end of file
diff --git a/tests/generics/t7839.nim b/tests/generics/t7839.nim
new file mode 100644
index 000000000..4d17b438b
--- /dev/null
+++ b/tests/generics/t7839.nim
@@ -0,0 +1,9 @@
+type A[I: SomeOrdinal, E] = tuple # same for object
+  length: int
+
+doAssert A.sizeof == sizeof(int) # works without the following proc
+
+proc newA*[I: SomeOrdinal, E](): A[I, E] = # works without `SomeOrdinal`
+  discard
+
+discard newA[uint8, int]()
diff --git a/tests/generics/t8270.nim b/tests/generics/t8270.nim
new file mode 100644
index 000000000..1e731d1d4
--- /dev/null
+++ b/tests/generics/t8270.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "cannot instantiate: \'T\'"
+  line: 6
+"""
+
+proc m[T](x: T): int = discard
+echo [m]
diff --git a/tests/generics/t88.nim b/tests/generics/t88.nim
deleted file mode 100644
index 280a346c5..000000000
--- a/tests/generics/t88.nim
+++ /dev/null
@@ -1,33 +0,0 @@
-# Issue 88
-
-type
-  BaseClass[V] = object of RootObj
-    b: V
-
-proc new[V](t: typedesc[BaseClass], v: V): BaseClass[V] =
-  BaseClass[V](b: v)
-
-proc baseMethod[V](v: BaseClass[V]): V = v.b
-proc overriddenMethod[V](v: BaseClass[V]): V = v.baseMethod
-
-type
-  ChildClass[V] = object of BaseClass[V]
-    c: V
-
-proc new[V](t: typedesc[ChildClass], v1, v2: V): ChildClass[V] =
-  ChildClass[V](b: v1, c: v2)
-
-proc overriddenMethod[V](v: ChildClass[V]): V = v.c
-
-let c = ChildClass[string].new("Base", "Child")
-
-assert c.baseMethod == "Base"
-assert c.overriddenMethod == "Child"
-
-
-# bug #4528
-type GenericBase[T] = ref object of RootObj
-type GenericSubclass[T] = ref object of GenericBase[T]
-proc foo[T](g: GenericBase[T]) = discard
-var bar: GenericSubclass[int]
-foo(bar)
diff --git a/tests/generics/tableref_is_nil.nim b/tests/generics/tableref_is_nil.nim
deleted file mode 100644
index 1ad4b3b0c..000000000
--- a/tests/generics/tableref_is_nil.nim
+++ /dev/null
@@ -1,9 +0,0 @@
-discard """
-  output: "true"
-"""
-
-# bug #2221
-import tables
-
-var tblo: TableRef[string, int]
-echo tblo == nil
diff --git a/tests/generics/taliashijack.nim b/tests/generics/taliashijack.nim
new file mode 100644
index 000000000..fdebadded
--- /dev/null
+++ b/tests/generics/taliashijack.nim
@@ -0,0 +1,8 @@
+# issue #23977
+
+type Foo[T] = int
+
+proc foo(T: typedesc) =
+  var a: T
+
+foo(int)
diff --git a/tests/generics/tarc_misc.nim b/tests/generics/tarc_misc.nim
new file mode 100644
index 000000000..3e7762556
--- /dev/null
+++ b/tests/generics/tarc_misc.nim
@@ -0,0 +1,21 @@
+discard """
+  output: ''''''
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #13519
+
+var unrelated: seq[proc() {.closure, gcsafe.}]
+
+unrelated.add proc () =
+  echo "gcsafe"
+
+import tables, sequtils
+let t = newTable[int, proc()]()
+
+type
+  MyProc = proc() {.closure.}
+
+var result: seq[MyProc] = @[]
+for x in t.values:
+  result.add(x)
diff --git a/tests/generics/tarray_with_somenumber.nim b/tests/generics/tarray_with_somenumber.nim
deleted file mode 100644
index 0bf2537a1..000000000
--- a/tests/generics/tarray_with_somenumber.nim
+++ /dev/null
@@ -1,11 +0,0 @@
-discard """
-  output: '''@[0.9, 0.1]'''
-"""
-
-# bug #2304
-
-type TV2*[T:SomeNumber] = array[0..1, T]
-proc newV2T*[T](x, y: T=0): TV2[T] = [x, y]
-
-let x = newV2T[float](0.9, 0.1)
-echo(@x)
diff --git a/tests/generics/tbadcache.nim b/tests/generics/tbadcache.nim
new file mode 100644
index 000000000..33e65be3a
--- /dev/null
+++ b/tests/generics/tbadcache.nim
@@ -0,0 +1,26 @@
+# issue #16128
+
+import std/[tables, hashes]
+
+type
+  NodeId*[L] = object
+    isSource: bool
+    index: Table[NodeId[L], seq[NodeId[L]]]
+
+func hash*[L](id: NodeId[L]): Hash = discard
+func `==`[L](a, b: NodeId[L]): bool = discard
+
+proc makeIndex*[T, L](tree: T) =
+  var parent = NodeId[L]()
+  var tmp: Table[NodeId[L], seq[NodeId[L]]]
+  tmp[parent] = @[parent]
+
+proc simpleTreeDiff*[T, L](source, target: T) =
+  # Swapping these two lines makes error disappear
+  var m: Table[NodeId[L], NodeId[L]]
+  makeIndex[T, L](target)
+
+var tmp: Table[string, seq[string]] # removing this forward declaration also removes error
+
+proc diff(x1, x2: string): auto =
+  simpleTreeDiff[int, string](12, 12)
diff --git a/tests/generics/tbaddeprecated.nim b/tests/generics/tbaddeprecated.nim
new file mode 100644
index 000000000..335234a25
--- /dev/null
+++ b/tests/generics/tbaddeprecated.nim
@@ -0,0 +1,55 @@
+discard """
+  output: '''
+not deprecated
+not deprecated
+not error
+not error
+'''
+"""
+
+# issue #21724
+
+block: # deprecated
+  {.push warningAsError[Deprecated]: on.}
+  type
+    SomeObj = object
+      hey: bool
+  proc hey() {.deprecated: "Shouldn't use this".} = echo "hey"
+  proc gen(o: auto) =
+    doAssert not compiles(o.hey())
+    if o.hey:
+      echo "not deprecated"
+  gen(SomeObj(hey: true))
+  doAssert not (compiles do:
+    proc hey(o: SomeObj) {.deprecated: "Shouldn't use this".} = echo "hey"
+    proc gen2(o: auto) =
+      if o.hey():
+        echo "not deprecated"
+    gen2(SomeObj(hey: true)))
+  proc hey(o: SomeObj) {.deprecated: "Shouldn't use this".} = echo "hey"
+  proc gen3(o: auto) =
+    if o.hey:
+      echo "not deprecated"
+  gen3(SomeObj(hey: true))
+  {.pop.}
+block: # error
+  type
+    SomeObj = object
+      hey: bool
+  proc hey() {.error: "Shouldn't use this".} = echo "hey"
+  proc gen(o: auto) =
+    doAssert not compiles(o.hey())
+    if o.hey:
+      echo "not error"
+  gen(SomeObj(hey: true))
+  doAssert not (compiles do:
+    proc hey(o: SomeObj) {.error: "Shouldn't use this".} = echo "hey"
+    proc gen2(o: auto) =
+      if o.hey():
+        echo "not error"
+    gen2(SomeObj(hey: true)))
+  proc hey(o: SomeObj) {.error: "Shouldn't use this".} = echo "hey"
+  proc gen3(o: auto) =
+    if o.hey:
+      echo "not error"
+  gen3(SomeObj(hey: true))
diff --git a/tests/generics/tbind_bracket.nim b/tests/generics/tbind_bracket.nim
deleted file mode 100644
index d0c5e2c6b..000000000
--- a/tests/generics/tbind_bracket.nim
+++ /dev/null
@@ -1,20 +0,0 @@
-discard """
-  output: "317"
-"""
-
-# bug #2599
-
-import mbind_bracket
-
-# also test that `[]` can be passed now as a first class construct:
-
-template takeBracket(x, a, i: untyped) =
-  echo x(a, i)
-
-var a: array[10, int]
-a[8] = 317
-
-takeBracket(`[]`, a, 8)
-
-let reg = newRegistry[UUIDObject]()
-reg.register(UUIDObject())
diff --git a/tests/generics/tbintre2.nim b/tests/generics/tbintre2.nim
deleted file mode 100644
index eb46b5157..000000000
--- a/tests/generics/tbintre2.nim
+++ /dev/null
@@ -1,31 +0,0 @@
-discard """
-  file: "tbintre2.nim"
-  output: "helloworld99110223"
-"""
-# Same test, but check module boundaries
-
-import tbintree
-
-var
-  root: PBinaryTree[string]
-  x = newNode("hello")
-add(root, x)
-add(root, "world")
-if find(root, "world"):
-  for str in items(root):
-    stdout.write(str)
-else:
-  stdout.writeLine("BUG")
-
-var
-  r2: PBinaryTree[int]
-add(r2, newNode(110))
-add(r2, 223)
-add(r2, 99)
-for y in items(r2):
-  stdout.write(y)
-
-#OUT helloworld99110223
-
-
-
diff --git a/tests/generics/tbintree.nim b/tests/generics/tbintree.nim
index 13163f764..83f14406b 100644
--- a/tests/generics/tbintree.nim
+++ b/tests/generics/tbintree.nim
@@ -1,6 +1,9 @@
 discard """
-  file: "tbintree.nim"
-  output: "helloworld99110223"
+  output: '''hello
+world
+99
+110
+223'''
 """
 type
   TBinaryTree[T] = object      # TBinaryTree is a generic type with
@@ -52,8 +55,8 @@ proc find*[Ty2](b: PBinaryTree[Ty2], data: Ty2): bool =
 
 iterator preorder*[T](root: PBinaryTree[T]): T =
   # Preorder traversal of a binary tree.
-  # Since recursive iterators are not yet implemented,
-  # this uses an explicit stack:
+  # This uses an explicit stack (which is more efficient than
+  # a recursive iterator factory).
   var stack: seq[PBinaryTree[T]] = @[root]
   while stack.len > 0:
     var n = stack.pop()
@@ -82,7 +85,7 @@ proc debug[T](a: PBinaryTree[T]) =
     echo a.data
     debug(a.ri)
 
-when isMainModule:
+when true:
   var
     root: PBinaryTree[string]
     x = newNode("hello")
@@ -90,9 +93,9 @@ when isMainModule:
   add(root, "world")
   if find(root, "world"):
     for str in items(root):
-      stdout.write(str)
+      echo(str)
   else:
-    stdout.writeLine("BUG")
+    echo("BUG")
 
   var
     r2: PBinaryTree[int]
@@ -100,8 +103,4 @@ when isMainModule:
   add(r2, 223)
   add(r2, 99)
   for y in items(r2):
-    stdout.write(y)
-
-#OUT helloworld99110223
-
-
+    echo(y)
diff --git a/tests/generics/tbracketinstantiation.nim b/tests/generics/tbracketinstantiation.nim
new file mode 100644
index 000000000..22a86af4c
--- /dev/null
+++ b/tests/generics/tbracketinstantiation.nim
@@ -0,0 +1,86 @@
+discard """
+  nimout: '''
+type
+  Bob = object
+type
+  Another = object
+'''
+"""
+
+block: # issue #22645
+  type
+    Opt[T] = object
+    FutureBase = ref object of RootObj
+    Future[T] = ref object of FutureBase ## Typed future.
+      internalValue: T ## Stored value
+  template err[T](E: type Opt[T]): E = E()
+  proc works(): Future[Opt[int]] {.stackTrace: off, gcsafe, raises: [].} =
+    var chronosInternalRetFuture: FutureBase
+    template result(): untyped {.used.} =
+      Future[Opt[int]](chronosInternalRetFuture).internalValue
+    result = err(type(result))
+  proc breaks(): Future[Opt[int]] {.stackTrace: off, gcsafe, raises: [].} =
+    var chronosInternalRetFuture: FutureBase
+    template result(): untyped {.used.} =
+      cast[Future[Opt[int]]](chronosInternalRetFuture).internalValue
+    result = err(type(result))
+
+import macros
+
+block: # issue #16118
+  macro thing(name: static[string]) =
+    result = newStmtList(
+      nnkTypeSection.newTree(
+        nnkTypeDef.newTree(
+          ident(name),
+          newEmptyNode(),
+          nnkObjectTy.newTree(
+            newEmptyNode(),
+            newEmptyNode(),
+            nnkRecList.newTree()))))
+  template foo(name: string): untyped =
+    thing(name)
+  expandMacros:
+    foo("Bob")
+  block:
+    expandMacros:
+      foo("Another")
+
+block: # issue #19670
+  type
+    Past[Z] = object
+    OpenObject = object
+
+  macro rewriter(prc: untyped): untyped =
+    prc.body.add(nnkCall.newTree(
+      prc.params[0]
+    ))
+    prc
+    
+  macro macroAsync(name, restype: untyped): untyped =
+    quote do:
+      proc `name`(): Past[seq[`restype`]] {.rewriter.} = discard
+      
+  macroAsync(testMacro, OpenObject)
+
+import asyncdispatch
+
+block: # issue #11838 long
+  type
+    R[P] = object
+      updates: seq[P]
+    D[T, P] = ref object
+      ps: seq[P]
+      t: T
+  proc newD[T, P](ps: seq[P], t: T): D[T, P] =
+    D[T, P](ps: ps, t: t)
+  proc loop[T, P](d: D[T, P]) =
+    var results = newSeq[Future[R[P]]](10)
+  let d = newD[string, int](@[1], "")
+  d.loop()
+
+block: # issue #11838 minimal
+  type R[T] = object
+  proc loop[T]() =
+    discard newSeq[R[R[T]]]()
+  loop[int]()
diff --git a/tests/generics/tcalltype.nim b/tests/generics/tcalltype.nim
new file mode 100644
index 000000000..cba691f77
--- /dev/null
+++ b/tests/generics/tcalltype.nim
@@ -0,0 +1,26 @@
+discard """
+  joinable: false # breaks everything because of #23977
+"""
+
+# issue #23406
+
+template helper(_: untyped): untyped =
+  int
+
+type # Each of them should always be `int`.
+  GenA[T] = helper int
+  GenB[T] = helper(int)
+  GenC[T] = helper helper(int)
+
+block:
+  template helper(_: untyped): untyped =
+    float
+
+  type
+    A = GenA[int]
+    B = GenB[int]
+    C = GenC[int]
+
+  assert A is int # OK.
+  assert B is int # Fails; it is `float`!
+  assert C is int # OK.
diff --git a/tests/generics/tcan.nim b/tests/generics/tcan.nim
new file mode 100644
index 000000000..bbefa7233
--- /dev/null
+++ b/tests/generics/tcan.nim
@@ -0,0 +1,46 @@
+discard """
+  output: '''
+'''
+"""
+
+# Created by Eric Doughty-Papassideris on 2011-02-16.
+
+block talias_generic:
+  type
+    TGen[T] = object
+    TGen2[T] = TGen[T]
+
+
+block talias_specialised:
+  type
+    TGen[T] = object
+    TSpef = TGen[string]
+  var s: TSpef
+
+
+block tinherit:
+  type
+    TGen[T] = object of RootObj
+      x, y: T
+    TSpef[T] = object of TGen[T]
+
+  var s: TSpef[float]
+  s.x = 0.4
+  s.y = 0.6
+
+
+block tspecialise:
+  type
+    TGen[T] {.inheritable.} = object
+    TSpef = object of TGen[string]
+
+
+block tspecialised_equivalent:
+  type
+    TGen[T] = tuple[a: T]
+    TSpef = tuple[a: string]
+
+  var
+    a: TGen[string]
+    b: TSpef
+  a = b
diff --git a/tests/generics/tcan_alias_generic.nim b/tests/generics/tcan_alias_generic.nim
deleted file mode 100644
index b357b33e9..000000000
--- a/tests/generics/tcan_alias_generic.nim
+++ /dev/null
@@ -1,10 +0,0 @@
-##
-## can_alias_generic Nim Module
-##
-## Created by Eric Doughty-Papassideris on 2011-02-16.
-
-type
-  TGen[T] = object
-  TGen2[T] = TGen[T]
-
-
diff --git a/tests/generics/tcan_alias_specialised_generic.nim b/tests/generics/tcan_alias_specialised_generic.nim
deleted file mode 100644
index c94edd611..000000000
--- a/tests/generics/tcan_alias_specialised_generic.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-discard """
-  disabled: false
-"""
-
-##
-## can_alias_specialised_generic Nim Module
-##
-## Created by Eric Doughty-Papassideris on 2011-02-16.
-
-type
-  TGen[T] = object
-  TSpef = TGen[string]
-
-var
-  s: TSpef
-
diff --git a/tests/generics/tcan_inherit_generic.nim b/tests/generics/tcan_inherit_generic.nim
deleted file mode 100644
index 331fcfd5c..000000000
--- a/tests/generics/tcan_inherit_generic.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-##
-## can_inherit_generic Nim Module
-##
-## Created by Eric Doughty-Papassideris on 2011-02-16.
-
-type
-  TGen[T] = object of TObject
-    x, y: T
-
-  TSpef[T] = object of TGen[T]
-
-
-var s: TSpef[float]
-s.x = 0.4
-s.y = 0.6
-
diff --git a/tests/generics/tcan_specialise_generic.nim b/tests/generics/tcan_specialise_generic.nim
deleted file mode 100644
index 78896db38..000000000
--- a/tests/generics/tcan_specialise_generic.nim
+++ /dev/null
@@ -1,10 +0,0 @@
-##
-## can_specialise_generic Nim Module
-##
-## Created by Eric Doughty-Papassideris on 2011-02-16.
-
-type
-  TGen[T] = object {.inheritable.}
-  TSpef = object of TGen[string]
-
-
diff --git a/tests/generics/tclosed_sym.nim b/tests/generics/tclosed_sym.nim
deleted file mode 100644
index ff620c267..000000000
--- a/tests/generics/tclosed_sym.nim
+++ /dev/null
@@ -1,11 +0,0 @@
-discard """
-  output: "TEST2"
-"""
-
-# bug #2664
-
-import mclosed_sym
-
-proc same(r:R, d:int) = echo "TEST1"
-
-doIt(Data[int](d:123), R())
diff --git a/tests/generics/tconfusing_arrow.nim b/tests/generics/tconfusing_arrow.nim
deleted file mode 100644
index f63a874e0..000000000
--- a/tests/generics/tconfusing_arrow.nim
+++ /dev/null
@@ -1,15 +0,0 @@
-import algorithm, future
-
-type Deck = object
-  value: int
-
-proc sort(h: var seq[Deck]) =
-  # works:
-  h.sort(proc (x, y: Deck): auto =
-    cmp(x.value, y.value))
-  # fails:
-  h.sort((x, y: Deck) => cmp(ord(x.value), ord(y.value)))
-
-var player: seq[Deck] = @[]
-
-player.sort()
diff --git a/tests/generics/tconstraints.nim b/tests/generics/tconstraints.nim
new file mode 100644
index 000000000..3ca01cfd5
--- /dev/null
+++ b/tests/generics/tconstraints.nim
@@ -0,0 +1,16 @@
+discard """
+  errormsg: "type mismatch: got <int literal(232)>"
+  line: 16
+"""
+
+proc myGenericProc[T: object|tuple|ptr|ref|distinct](x: T): string =
+  result = $x
+
+type
+  TMyObj = tuple[x, y: int]
+
+var
+  x: TMyObj
+
+assert myGenericProc(x) == "(x: 0, y: 0)"
+assert myGenericProc(232) == "232"
diff --git a/tests/generics/tdictdestruct.nim b/tests/generics/tdictdestruct.nim
deleted file mode 100644
index 228d93e66..000000000
--- a/tests/generics/tdictdestruct.nim
+++ /dev/null
@@ -1,20 +0,0 @@
-
-type
-  TDict[TK, TV] = object
-    k: TK
-    v: TV
-  PDict[TK, TV] = ref TDict[TK, TV]
-
-proc fakeNew[T](x: var ref T, destroy: proc (a: ref T) {.nimcall.}) =
-  discard
-
-proc destroyDict[TK, TV](a: PDict[TK, TV]) =
-    return
-proc newDict[TK, TV](a: TK, b: TV): PDict[TK, TV] =
-    fakeNew(result, destroyDict[TK, TV])
-
-# Problem: destroyDict is not instantiated when newDict is instantiated!
-
-discard newDict("a", "b")
-
-
diff --git a/tests/generics/tdont_use_inner_scope.nim b/tests/generics/tdont_use_inner_scope.nim
deleted file mode 100644
index 45b11fc22..000000000
--- a/tests/generics/tdont_use_inner_scope.nim
+++ /dev/null
@@ -1,27 +0,0 @@
-
-# bug #2752
-
-import future, sequtils
-
-proc myFilter[T](it: (iterator(): T), f: (proc(anything: T):bool)): (iterator(): T) =
-  iterator aNameWhichWillConflict(): T {.closure.}=
-    for x in it():
-      if f(x):
-        yield x
-  result = aNameWhichWillConflict
-
-
-iterator testIt():int {.closure.}=
-  yield -1
-  yield 2
-
-#let unusedVariable = myFilter(testIt, (x: int) => x > 0)
-
-proc onlyPos(it: (iterator(): int)): (iterator(): int)=
-  iterator aNameWhichWillConflict(): int {.closure.}=
-    var filtered = onlyPos(myFilter(it, (x:int) => x > 0))
-    for x in filtered():
-      yield x
-  result = aNameWhichWillConflict
-
-let x = onlyPos(testIt)
diff --git a/tests/generics/tdotlookup.nim b/tests/generics/tdotlookup.nim
deleted file mode 100644
index 17c60ded2..000000000
--- a/tests/generics/tdotlookup.nim
+++ /dev/null
@@ -1,10 +0,0 @@
-discard """
-  output: '''5 5 5
-false'''
-"""
-
-import mdotlookup
-
-foo(7)
-# bug #1444
-fn(4)
diff --git a/tests/generics/texplicitgeneric1.nim b/tests/generics/texplicitgeneric1.nim
index ac0197c1a..16f4f7330 100644
--- a/tests/generics/texplicitgeneric1.nim
+++ b/tests/generics/texplicitgeneric1.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "texplicitgeneric1.nim"
   output: "Key: 12 value: 12Key: 13 value: 13 Key: A value: 12 Key: B value: 13"
 """
 # test explicit type instantiation
@@ -34,5 +33,4 @@ c.add('B', "13")
 for k, v in items(c):
   stdout.write(" Key: ", $k, " value: ", v)
 
-
-
+stdout.write "\n"
diff --git a/tests/generics/texplicitgeneric2.nim b/tests/generics/texplicitgeneric2.nim
index c4af17b7b..37b133130 100644
--- a/tests/generics/texplicitgeneric2.nim
+++ b/tests/generics/texplicitgeneric2.nim
@@ -1,6 +1,5 @@
 discard """
   output: "Key: 12 value: 12Key: 13 value: 13 Key: A value: 12 Key: B value: 13"
-  disabled: true
 """
 
 # test explicit type instantiation
@@ -14,12 +13,12 @@ proc newDict*[TKey, TValue](): PDict[TKey, TValue] =
   new(result)
   result.data = @[]
 
-proc add*(d: PDict, k: TKey, v: TValue) =
+proc add*(d: PDict, k: d.TKey, v: d.TValue) =
   d.data.add((k, v))
 
 
-#iterator items*(d: PDict): tuple[k: TKey, v: TValue] =
-#  for k, v in items(d.data): yield (k, v)
+iterator items*(d: PDict): tuple[k: d.TKey, v: d.TValue] =
+  for k, v in items(d.data): yield (k, v)
 
 var d = newDict[int, string]()
 d.add(12, "12")
@@ -33,3 +32,4 @@ c.add('B', "13")
 for k, v in items(c):
   stdout.write(" Key: ", $k, " value: ", v)
 
+stdout.write "\n"
diff --git a/tests/generics/tfakedependenttypes.nim b/tests/generics/tfakedependenttypes.nim
deleted file mode 100644
index cd4be806c..000000000
--- a/tests/generics/tfakedependenttypes.nim
+++ /dev/null
@@ -1,61 +0,0 @@
-discard """
-output: '''
-U[3]
-U[(f: 3)]
-U[[3]]
-'''
-"""
-
-# https://github.com/nim-lang/Nim/issues/5106
-
-import typetraits
-
-block:
-  type T = distinct int
-
-  proc `+`(a, b: T): T =
-    T(int(a) + int(b))
-
-  type U[F: static[T]] = distinct int
-
-  proc `+`[P1, P2: static[T]](a: U[P1], b: U[P2]): U[P1 + P2] =
-    U[P1 + P2](int(a) + int(b))
-
-  var a = U[T(1)](1)
-  var b = U[T(2)](2)
-  var c = a + b
-  echo c.type.name
-  
-block:
-  type T = object
-    f: int
-
-  proc `+`(a, b: T): T =
-    T(f: a.f + b.f)
-
-  type U[F: static[T]] = distinct int
-
-  proc `+`[P1, P2: static[T]](a: U[P1], b: U[P2]): U[P1 + P2] =
-    U[P1 + P2](int(a) + int(b))
-
-  var a = U[T(f: 1)](1)
-  var b = U[T(f: 2)](2)
-  var c = a + b
-  echo c.type.name
-
-block:
-  type T = distinct array[0..0, int]
-
-  proc `+`(a, b: T): T =
-    T([array[0..0, int](a)[0] + array[0..0, int](b)[0]])
-
-  type U[F: static[T]] = distinct int
-
-  proc `+`[P1, P2: static[T]](a: U[P1], b: U[P2]): U[P1 + P2] =
-    U[P1 + P2](int(a) + int(b))
-
-  var a = U[T([1])](1)
-  var b = U[T([2])](2)
-  var c = a + b
-  echo c.type.name
-
diff --git a/tests/generics/tforward_generic.nim b/tests/generics/tforward_generic.nim
deleted file mode 100644
index f43e7455f..000000000
--- a/tests/generics/tforward_generic.nim
+++ /dev/null
@@ -1,28 +0,0 @@
-discard """
-  output: '''b()
-720 120.0
-720 120.0'''
-"""
-
-# bug #3055
-proc b(t: int | string)
-proc a(t: int) = b(t)
-proc b(t: int | string) = echo "b()"
-a(1)
-
-# test recursive generics still work:
-proc fac[T](x: T): T =
-  if x == 0: return 1
-  else: return fac(x-1)*x
-
-echo fac(6), " ", fac(5.0)
-
-# test recursive generic with forwarding:
-proc fac2[T](x: T): T
-
-echo fac2(6), " ", fac2(5.0)
-
-proc fac2[T](x: T): T =
-  if x == 0: return 1
-  else: return fac2(x-1)*x
-
diff --git a/tests/generics/tforwardgenericconstrained.nim b/tests/generics/tforwardgenericconstrained.nim
new file mode 100644
index 000000000..6163ea1a8
--- /dev/null
+++ b/tests/generics/tforwardgenericconstrained.nim
@@ -0,0 +1,73 @@
+discard """
+output: '''
+hello some integer
+hello range
+hello tuple
+hello seq
+hello object
+hello distinct
+hello enum
+'''
+"""
+
+
+
+# SomeInteger
+
+proc foo[T : SomeInteger](arg: T)
+proc foo[T : SomeInteger](arg: T) =
+  echo "hello some integer"
+foo(123)
+
+# range
+
+proc foo2[T : range[0..100]](arg: T)
+proc foo2[T : range[0..100]](arg: T) =
+  echo "hello range"
+foo2(7)
+
+# tuple
+
+proc foo3[T : tuple](arg: T)
+proc foo3[T : tuple](arg: T) =
+  echo "hello tuple"
+
+foo3((a:123,b:321))
+
+# seq
+
+proc foo4[T: seq](arg: T)
+proc foo4[T: seq](arg: T) =
+  echo "hello seq"
+
+foo4(@[1,2,3])
+
+# object
+
+proc foo5[T : object](arg: T)
+proc foo5[T : object](arg: T) =
+  echo "hello object"
+
+type MyType = object
+var mt: MyType
+foo5(mt)
+
+# distinct
+
+proc foo6[T : distinct](arg: T)
+proc foo6[T : distinct](arg: T) =
+  echo "hello distinct"
+
+type MyDistinct = distinct string
+var md: MyDistinct
+foo6(md)
+
+# enum
+
+proc foo7[T : enum](arg: T)
+proc foo7[T : enum](arg: T) =
+  echo "hello enum"
+
+type MyEnum = enum
+  ValueA
+foo7(ValueA)
diff --git a/tests/friends/tfriends.nim b/tests/generics/tfriends.nim
index 1e70d50a5..1e70d50a5 100644
--- a/tests/friends/tfriends.nim
+++ b/tests/generics/tfriends.nim
diff --git a/tests/generics/tgeneric0.nim b/tests/generics/tgeneric0.nim
index a045b32f8..16a148f7b 100644
--- a/tests/generics/tgeneric0.nim
+++ b/tests/generics/tgeneric0.nim
@@ -1,27 +1,196 @@
-import tables
+discard """
+  output: '''
+100
+0
+float32
+float32
+(name: "Resource 1", readers: ..., writers: ...)
+'''
+"""
+
+
+import std/tables
+
+
+block tgeneric0:
+  type
+    TX = Table[string, int]
+
+  proc foo(models: seq[Table[string, float]]): seq[float] =
+    result = @[]
+    for model in models.items:
+      result.add model["foobar"]
+
+  # bug #686
+  type TType[T; A] = array[A, T]
+
+  proc foo[T](p: TType[T, range[0..1]]) =
+    echo "foo"
+  proc foo[T](p: TType[T, range[0..2]]) =
+    echo "bar"
+
+  #bug #1366
+
+  proc reversed(x: auto) =
+    for i in countdown(x.low, x.high):
+      echo i
+
+  reversed(@[-19, 7, -4, 6])
+
+
+
+block tgeneric1:
+  type
+    TNode[T] = tuple[priority: int, data: T]
+    TBinHeap[T] = object
+      heap: seq[TNode[T]]
+      last: int
+    PBinHeap[T] = ref TBinHeap[T]
+
+  proc newBinHeap[T](heap: var PBinHeap[T], size: int) =
+    new(heap)
+    heap.last = 0
+    newSeq(heap.heap, size)
+    #newSeq(heap.seq, size)
+
+  proc parent(elem: int): int {.inline.} =
+    return (elem-1) div 2
+
+  proc siftUp[T](heap: PBinHeap[T], elem: int) =
+    var idx = elem
+    while idx != 0:
+      var p = parent(idx)
+      if heap.heap[idx].priority < heap.heap[p].priority:
+        swap(heap.heap[idx], heap.heap[p])
+        idx = p
+      else:
+        break
+
+  proc add[T](heap: PBinHeap[T], priority: int, data: T) =
+    var node: TNode[T]
+    node.priority = priority
+    node.data = data
+    heap.heap[heap.last] = node
+    siftUp(heap, heap.last)
+    inc(heap.last)
+
+  proc print[T](heap: PBinHeap[T]) =
+    for i in countup(0, heap.last):
+      stdout.write($heap.heap[i].data, "\n")
+
+  var heap: PBinHeap[int]
+
+  newBinHeap(heap, 256)
+  add(heap, 1, 100)
+  print(heap)
+
+
+
+block tgeneric2:
+  type
+    TX = Table[string, int]
+
+  proc foo(models: seq[TX]): seq[int] =
+    result = @[]
+    for model in models.items:
+      result.add model["foobar"]
+
+  type
+    Obj = object
+      field: Table[string, string]
+  var t: Obj
+  discard initTable[type(t.field), string]()
+
+
+
+block tgeneric4:
+  type
+    TIDGen[A: Ordinal] = object
+      next: A
+      free: seq[A]
+
+  proc newIDGen[A]: TIDGen[A] =
+      newSeq result.free, 0
+
+  var x = newIDGen[int]()
+
+block tgeneric5:
+  # bug #12528
+  proc foo[T](a: T; b: T) =
+    echo T
+
+  foo(0.0'f32, 0.0)
+
+  proc bar[T](a: T; b: T = 0.0) =
+    echo T
+
+  bar(0.0'f32)
+
+# bug #13378
+
+type
+  Resource = ref object of RootObj
+    name: string
+    readers, writers: seq[RenderTask]
+
+  RenderTask = ref object
+    name: string
+
+var res = Resource(name: "Resource 1")
+
+(proc (r: typeof(res)) =
+   echo r[])(res)
+
+# bug #4061
+
+type List[T] = object
+  e: T
+  n: ptr List[T]
+
+proc zip*[T,U](xs: List[T], ys: List[U]): List[(T,U)] = discard
+
+proc unzip*[T,U](xs: List[tuple[t: T, u: U]]): (List[T], List[U]) = discard
+
+proc unzip2*[T,U](xs: List[(T,U)]): (List[T], List[U]) = discard
 
 type
-  TX = Table[string, int]
+  AtomicType = pointer|ptr|int
+
+  Atomic[T: AtomicType] = distinct T
 
-proc foo(models: seq[Table[string, float]]): seq[float] =
-  result = @[]
-  for model in models.items:
-    result.add model["foobar"]
+  Block[T: AtomicType] = object
 
-# bug #686
-type TType[T; A] = array[A, T]
+  AtomicContainer[T: AtomicType] = object
+    b: Atomic[ptr Block[T]]
 
-proc foo[T](p: TType[T, range[0..1]]) =
-  echo "foo"
-proc foo[T](p: TType[T, range[0..2]]) =
-  echo "bar"
+# bug #8295
+var x = AtomicContainer[int]()
+doAssert (ptr Block[int])(x.b) == nil
+
+
+# bug #23233
+type
+  JsonObjectType*[T: string or uint64] = Table[string, JsonValueRef[T]]
 
-#bug #1366
+  JsonValueRef*[T: string or uint64] = object
+    objVal*: JsonObjectType[T]
 
-proc reversed(x: auto) =
-  for i in countdown(x.low, x.high):
-    echo i
+proc scanValue[K](val: var K) =
+  var map: JsonObjectType[K.T]
+  var newVal: K
+  map["one"] = newVal
 
-reversed(@[-19, 7, -4, 6])
+block:
+  var a: JsonValueRef[uint64]
+  scanValue(a)
 
+  var b: JsonValueRef[string]
+  scanValue(b)
 
+block: # bug #21347
+  type K[T] = object
+  template s[T]() = discard
+  proc b1(n: bool | bool) = s[K[K[int]]]()
+  proc b2(n: bool) =        s[K[K[int]]]()
+  b1(false)   # Error: 's' has unspecified generic parameters
+  b2(false)   # Builds, on its own
diff --git a/tests/generics/tgeneric1.nim b/tests/generics/tgeneric1.nim
deleted file mode 100644
index 5349f8f1d..000000000
--- a/tests/generics/tgeneric1.nim
+++ /dev/null
@@ -1,53 +0,0 @@
-discard """
-  output: "100 0"
-"""
-
-# A min-heap.
-type
-  TNode[T] = tuple[priority: int, data: T]
-
-  TBinHeap[T] = object
-    heap: seq[TNode[T]]
-    last: int
-
-  PBinHeap[T] = ref TBinHeap[T]
-
-proc newBinHeap*[T](heap: var PBinHeap[T], size: int) =
-  new(heap)
-  heap.last = 0
-  newSeq(heap.heap, size)
-  #newSeq(heap.seq, size)
-
-proc parent(elem: int): int {.inline.} =
-  return (elem-1) div 2
-
-proc siftUp[T](heap: PBinHeap[T], elem: int) =
-  var idx = elem
-  while idx != 0:
-    var p = parent(idx)
-    if heap.heap[idx].priority < heap.heap[p].priority:
-      swap(heap.heap[idx], heap.heap[p])
-      idx = p
-    else:
-      break
-
-proc add*[T](heap: PBinHeap[T], priority: int, data: T) =
-  var node: TNode[T]
-  node.priority = priority
-  node.data = data
-  heap.heap[heap.last] = node
-  siftUp(heap, heap.last)
-  inc(heap.last)
-
-proc print*[T](heap: PBinHeap[T]) =
-  for i in countup(0, heap.last):
-    stdout.write($heap.heap[i].data, " ")
-
-var
-  heap: PBinHeap[int]
-
-newBinHeap(heap, 256)
-add(heap, 1, 100)
-print(heap)
-
-
diff --git a/tests/generics/tgeneric2.nim b/tests/generics/tgeneric2.nim
deleted file mode 100644
index 21eb4693e..000000000
--- a/tests/generics/tgeneric2.nim
+++ /dev/null
@@ -1,15 +0,0 @@
-import tables
-
-type
-  TX = Table[string, int]
-
-proc foo(models: seq[TX]): seq[int] =
-  result = @[]
-  for model in models.items:
-    result.add model["foobar"]
-
-type
-  Obj = object
-    field: Table[string, string]
-var t: Obj
-discard initTable[type(t.field), string]()
diff --git a/tests/generics/tgeneric3.nim b/tests/generics/tgeneric3.nim
index d014eb998..29a73afc6 100644
--- a/tests/generics/tgeneric3.nim
+++ b/tests/generics/tgeneric3.nim
@@ -1,8 +1,18 @@
+discard """
+output: '''
+312
+1000
+1000
+500
+0
+'''
+"""
+
 import strutils
 
 type
   PNode[T,D] = ref TNode[T,D]
-  TItem {.acyclic, pure, final, shallow.} [T,D] = object
+  TItem[T,D] {.acyclic, pure, final, shallow.} = object
         key: T
         value: D
         node: PNode[T,D]
@@ -10,7 +20,7 @@ type
           val_set: bool
 
   TItems[T,D] = seq[ref TItem[T,D]]
-  TNode {.acyclic, pure, final, shallow.} [T,D] = object
+  TNode[T,D] {.acyclic, pure, final, shallow.} = object
         slots: TItems[T,D]
         left: PNode[T,D]
         count: int32
@@ -103,7 +113,7 @@ proc DeleteItem[T,D] (n: PNode[T,D], x: int): PNode[T,D] {.inline.} =
 
   else :
     result = n.left
-    n.slots = nil
+    n.slots = @[]
     n.left = nil
 
 proc internalDelete[T,D] (ANode: PNode[T,D], key: T, Avalue: var D): PNode[T,D] =
@@ -200,7 +210,7 @@ proc traceTree[T,D](root: PNode[T,D]) =
       traceln(space)
       write stdout, "left: "
       doTrace(n.left, level+1)
-    for i, el in n.slots :
+    for i, el in n.slots:
       if el != nil and not isClean(el):
         traceln(space)
         traceX(i)
@@ -233,7 +243,7 @@ proc InsertItem[T,D](APath: RPath[T,D], ANode:PNode[T,D], Akey: T, Avalue: D) =
   of cLenCenter: setLen(APath.Nd.slots, cLen4)
   of cLen4: setLen(APath.Nd.slots, cLenMax)
   else: discard
-  for i in countdown(APath.Nd.count.int - 1, x + 1): shallowCopy(APath.Nd.slots[i], APath.Nd.slots[i - 1])
+  for i in countdown(APath.Nd.count.int - 1, x + 1): APath.Nd.slots[i] = move APath.Nd.slots[i - 1]
   APath.Nd.slots[x] = setItem(Akey, Avalue, ANode)
 
 
@@ -245,31 +255,39 @@ proc SplitPage[T,D](n, left: PNode[T,D], xi: int, Akey:var T, Avalue:var D): PNo
   result.slots.newSeq(cLenCenter)
   result.count = cCenter
   if x == cCenter:
-    for i in 0..cCenter-1: shallowCopy(it1[i], left.slots[i])
-    for i in 0..cCenter-1: shallowCopy(result.slots[i], left.slots[cCenter + i])
+    for i in 0..cCenter-1: 
+      it1[i] = move left.slots[i]
+    for i in 0..cCenter-1:
+      result.slots[i] = move left.slots[cCenter + i]
     result.left = n
   else :
     if x < cCenter :
-      for i in 0..x-1: shallowCopy(it1[i], left.slots[i])
+      for i in 0..x-1:
+        it1[i] = move left.slots[i]
       it1[x] = setItem(Akey, Avalue, n)
-      for i in x+1 .. cCenter-1: shallowCopy(it1[i], left.slots[i-1])
+      for i in x+1 .. cCenter-1:
+        it1[i] = move left.slots[i-1]
       var w = left.slots[cCenter-1]
       Akey = w.key
       Avalue = w.value
       result.left = w.node
-      for i in 0..cCenter-1: shallowCopy(result.slots[i], left.slots[cCenter + i])
+      for i in 0..cCenter-1:
+        result.slots[i] = move left.slots[cCenter + i]
     else :
-      for i in 0..cCenter-1: shallowCopy(it1[i], left.slots[i])
+      for i in 0..cCenter-1:
+        it1[i] = move left.slots[i]
       x = x - (cCenter + 1)
-      for i in 0..x-1: shallowCopy(result.slots[i], left.slots[cCenter + i + 1])
+      for i in 0..x-1:
+        result.slots[i] = move left.slots[cCenter + i + 1]
       result.slots[x] = setItem(Akey, Avalue, n)
-      for i in x+1 .. cCenter-1: shallowCopy(result.slots[i], left.slots[cCenter + i])
+      for i in x+1 .. cCenter-1:
+        result.slots[i] = move left.slots[cCenter + i]
       var w = left.slots[cCenter]
       Akey = w.key
       Avalue = w.value
       result.left = w.node
   left.count = cCenter
-  shallowCopy(left.slots, it1)
+  left.slots = move it1
 
 
 proc internalPut[T,D](ANode: ref TNode[T,D], Akey: T, Avalue: D, Oldvalue: var D): ref TNode[T,D] =
@@ -420,55 +438,50 @@ iterator keys* [T,D] (n: PNode[T,D]): T =
         i = Path[level].Xi
         inc(i)
 
-
-when isMainModule:
-
-  proc test() =
-    var oldvalue: int
-    var root = internalPut[int, int](nil, 312, 312, oldvalue)
-    var someOtherRoot = internalPut[string, int](nil, "312", 312, oldvalue)
-    var it1 = internalFind(root, 312)
-    echo it1.value
-
-    for i in 1..1_000_000:
-      root = internalPut(root, i, i, oldvalue)
-
-    var cnt = 0
-    oldvalue = -1
-    when true : # code compiles, when this or the other when is switched to false
-      for k in root.keys :
-        if k <= oldvalue :
-          echo k
-        oldvalue = k
-        inc(cnt)
-      echo cnt
+proc test() =
+  var oldvalue: int
+  var root = internalPut[int, int](nil, 312, 312, oldvalue)
+  var someOtherRoot = internalPut[string, int](nil, "312", 312, oldvalue)
+  var it1 = internalFind(root, 312)
+  echo it1.value
+
+  for i in 1..1_000:
+    root = internalPut(root, i, i, oldvalue)
+
+  var cnt = 0
+  oldvalue = -1
+  when true : # code compiles, when this or the other when is switched to false
+    for k in root.keys :
+      if k <= oldvalue :
+        echo k
+      oldvalue = k
+      inc(cnt)
+    echo cnt
+  when true :
+    cnt = 0
+    VisitAll(root, proc(key, val: int) = inc(cnt))
+    echo cnt
     when true :
-      cnt = 0
-      VisitAll(root, proc(key, val: int) = inc(cnt))
-      echo cnt
-      when true :
-        root = VisitAll(root, proc(key: int, value: var int): bool =
-          return key mod 2 == 0 )
-      cnt = 0
-      oldvalue = -1
-      VisitAll(root, proc(key: int, value: int) {.closure.} =
-        if key <= oldvalue :
-          echo key
-        oldvalue = key
-        inc(cnt) )
-      echo cnt
       root = VisitAll(root, proc(key: int, value: var int): bool =
-        return key mod 2 != 0 )
-      cnt = 0
-      oldvalue = -1
-      VisitAll(root, proc(key: int, value: int) {.closure.} =
-        if key <= oldvalue :
-          echo "error ", key
-        oldvalue = key
-        inc(cnt) )
-      echo cnt
-      #traceTree(root)
-
-
-
-  test()
+        return key mod 2 == 0 )
+    cnt = 0
+    oldvalue = -1
+    VisitAll(root, proc(key: int, value: int) {.closure.} =
+      if key <= oldvalue :
+        echo key
+      oldvalue = key
+      inc(cnt) )
+    echo cnt
+    root = VisitAll(root, proc(key: int, value: var int): bool =
+      return key mod 2 != 0 )
+    cnt = 0
+    oldvalue = -1
+    VisitAll(root, proc(key: int, value: int) {.closure.} =
+      if key <= oldvalue :
+        echo "error ", key
+      oldvalue = key
+      inc(cnt) )
+    echo cnt
+    #traceTree(root)
+
+test()
diff --git a/tests/generics/tgeneric4.nim b/tests/generics/tgeneric4.nim
deleted file mode 100644
index f79096636..000000000
--- a/tests/generics/tgeneric4.nim
+++ /dev/null
@@ -1,10 +0,0 @@
-type
-  TIDGen*[A: Ordinal] = object
-    next: A
-    free: seq[A]
-
-proc newIDGen*[A]: TIDGen[A] =
-    newSeq result.free, 0
-
-var x = newIDGen[int]()
-
diff --git a/tests/generics/tgeneric_closure.nim b/tests/generics/tgeneric_closure.nim
deleted file mode 100644
index 8dcd677fd..000000000
--- a/tests/generics/tgeneric_closure.nim
+++ /dev/null
@@ -1,37 +0,0 @@
-# Test to ensure TEventHandler is '.closure'
-
-# bug #1187
-
-type
-  TEventArgs* = object
-    skip*: bool
-  TEventHandler[T] = proc (e: var TEventArgs, data: T) {.closure.}
-  TEvent*[T] = object
-    #handlers: seq[TEventHandler[T]] # Does not work
-    handlers: seq[proc (e: var TEventArgs, d: T) {.closure.}] # works
-
-  TData = object
-    x: int
-
-  TSomething = object
-    s: TEvent[TData]
-
-proc init*[T](e: var TEvent[T]) =
-  e.handlers.newSeq(0)
-
-#proc add*[T](e: var TEvent[T], h: proc (e: var TEventArgs, data: T) {.closure.}) =
-# this line works
-proc add*[T](e: var TEvent[T], h: TEventHandler[T]) =
-  # this line does not work
-  e.handlers.add(h)
-
-proc main () =
-  var something: TSomething
-  something.s.init()
-  var fromOutside = 4711
-
-  something.s.add() do (e: var TEventArgs, data: TData):
-    var x = data.x
-    x = fromOutside
-
-main()
diff --git a/tests/generics/tgeneric_inheritance.nim b/tests/generics/tgeneric_inheritance.nim
deleted file mode 100644
index 432228797..000000000
--- a/tests/generics/tgeneric_inheritance.nim
+++ /dev/null
@@ -1,19 +0,0 @@
-discard """
-  output: "0.0"
-"""
-
-# bug #1919
-
-type
-  Base[M] = object of RootObj
-    a : M
-
-  Sub1[M] = object of Base[M]
-    b : int
-
-  Sub2[M] = object of Sub1[M]
-    c : int
-
-var x: Sub2[float]
-
-echo x.a
diff --git a/tests/generics/tgeneric_recursionlimit.nim b/tests/generics/tgeneric_recursionlimit.nim
new file mode 100644
index 000000000..5fe9b43c6
--- /dev/null
+++ b/tests/generics/tgeneric_recursionlimit.nim
@@ -0,0 +1,123 @@
+discard """
+  action: "compile"
+"""
+
+# https://github.com/nim-lang/Nim/issues/20348
+
+type
+  Payload[T] = object
+    payload: T
+  Carrier[T] = object
+    val: T
+
+type
+  Payload0*[T] = object
+    payload: Payload[T]
+  Payload1*[T] = object
+    payload: Payload[T]
+  Payload2*[T] = object
+    payload: Payload[T]
+  Payload3*[T] = object
+    payload: Payload[T]
+  Payload4*[T] = object
+    payload: Payload[T]
+  Payload5*[T] = object
+    payload: Payload[T]
+  Payload6*[T] = object
+    payload: Payload[T]
+  Payload7*[T] = object
+    payload: Payload[T]
+  Payload8*[T] = object
+    payload: Payload[T]
+  Payload9*[T] = object
+    payload: Payload[T]
+  Payload10*[T] = object
+    payload: Payload[T]
+  Payload11*[T] = object
+    payload: Payload[T]
+  Payload12*[T] = object
+    payload: Payload[T]
+  Payload13*[T] = object
+    payload: Payload[T]
+  Payload14*[T] = object
+    payload: Payload[T]
+  Payload15*[T] = object
+    payload: Payload[T]
+  Payload16*[T] = object
+    payload: Payload[T]
+  Payload17*[T] = object
+    payload: Payload[T]
+  Payload18*[T] = object
+    payload: Payload[T]
+  Payload19*[T] = object
+    payload: Payload[T]
+  Payload20*[T] = object
+    payload: Payload[T]
+  Payload21*[T] = object
+    payload: Payload[T]
+  Payload22*[T] = object
+    payload: Payload[T]
+  Payload23*[T] = object
+    payload: Payload[T]
+  Payload24*[T] = object
+    payload: Payload[T]
+  Payload25*[T] = object
+    payload: Payload[T]
+  Payload26*[T] = object
+    payload: Payload[T]
+  Payload27*[T] = object
+    payload: Payload[T]
+  Payload28*[T] = object
+    payload: Payload[T]
+  Payload29*[T] = object
+    payload: Payload[T]
+  Payload30*[T] = object
+    payload: Payload[T]
+  Payload31*[T] = object
+    payload: Payload[T]
+  Payload32*[T] = object
+    payload: Payload[T]
+  Payload33*[T] = object
+    payload: Payload[T]
+
+type
+  Carriers*[T] = object
+    c0*: Carrier[Payload0[T]]
+    c1*: Carrier[Payload1[T]]
+    c2*: Carrier[Payload2[T]]
+    c3*: Carrier[Payload3[T]]
+    c4*: Carrier[Payload4[T]]
+    c5*: Carrier[Payload5[T]]
+    c6*: Carrier[Payload6[T]]
+    c7*: Carrier[Payload7[T]]
+    c8*: Carrier[Payload8[T]]
+    c9*: Carrier[Payload9[T]]
+    c10*: Carrier[Payload10[T]]
+    c11*: Carrier[Payload11[T]]
+    c12*: Carrier[Payload12[T]]
+    c13*: Carrier[Payload13[T]]
+    c14*: Carrier[Payload14[T]]
+    c15*: Carrier[Payload15[T]]
+    c16*: Carrier[Payload16[T]]
+    c17*: Carrier[Payload17[T]]
+    c18*: Carrier[Payload18[T]]
+    c19*: Carrier[Payload19[T]]
+    c20*: Carrier[Payload20[T]]
+    c21*: Carrier[Payload21[T]]
+    c22*: Carrier[Payload22[T]]
+    c23*: Carrier[Payload23[T]]
+    c24*: Carrier[Payload24[T]]
+    c25*: Carrier[Payload25[T]]
+    c26*: Carrier[Payload26[T]]
+    c27*: Carrier[Payload27[T]]
+    c28*: Carrier[Payload28[T]]
+    c29*: Carrier[Payload29[T]]
+    c30*: Carrier[Payload30[T]]
+    c31*: Carrier[Payload31[T]]
+    c32*: Carrier[Payload32[T]]
+    c33*: Carrier[Payload33[T]]
+
+var carriers : Carriers[int]
+
+static:
+  assert $(typeof(carriers.c33.val)) == "Payload33[system.int]"
diff --git a/tests/generics/tgenericconst.nim b/tests/generics/tgenericconst.nim
deleted file mode 100644
index 3c86888df..000000000
--- a/tests/generics/tgenericconst.nim
+++ /dev/null
@@ -1,39 +0,0 @@
-discard """
-output: '''
-@[1, 2]
-@[3, 4]
-1
-'''
-"""
-
-# https://github.com/nim-lang/Nim/issues/5756
-
-type
-  Vec*[N : static[int]] = object
-    x: int
-    arr*: array[N, int32]
-
-  Mat*[M,N: static[int]] = object
-    x: int
-    arr*: array[M, Vec[N]]
-
-proc vec2*(x,y:int32) : Vec[2] =
-  result.arr = [x,y]
-  result.x = 10
-
-proc mat2*(a,b: Vec[2]): Mat[2,2] =
-  result.arr = [a,b]
-  result.x = 20
-
-const M = mat2(vec2(1, 2), vec2(3, 4))
-
-let m1 = M
-echo @(m1.arr[0].arr)
-echo @(m1.arr[1].arr)
-
-proc foo =
-  let m2 = M
-  echo m1.arr[0].arr[0]
-
-foo()
-
diff --git a/tests/generics/tgenericdefaults.nim b/tests/generics/tgenericdefaults.nim
deleted file mode 100644
index a4c90a884..000000000
--- a/tests/generics/tgenericdefaults.nim
+++ /dev/null
@@ -1,29 +0,0 @@
-type
-  TFoo[T, U, R = int] = object
-    x: T
-    y: U
-    z: R
-
-  TBar[T] = TFoo[T, array[4, T], T]
-
-var x1: TFoo[int, float]
-
-static:
-  assert type(x1.x) is int
-  assert type(x1.y) is float
-  assert type(x1.z) is int
-
-var x2: TFoo[string, R = float, U = seq[int]]
-
-static:
-  assert type(x2.x) is string
-  assert type(x2.y) is seq[int]
-  assert type(x2.z) is float
-
-var x3: TBar[float]
-
-static:
-  assert type(x3.x) is float
-  assert type(x3.y) is array[4, float]
-  assert type(x3.z) is float
-
diff --git a/tests/generics/tgenericmatcher.nim b/tests/generics/tgenericmatcher.nim
index 2edf46187..ca4dc2a4d 100644
--- a/tests/generics/tgenericmatcher.nim
+++ b/tests/generics/tgenericmatcher.nim
@@ -1,22 +1,40 @@
 discard """
-  disabled: false
+  output: '''
+'''
 """
 
-type
-  TMatcherKind = enum
-    mkTerminal, mkSequence, mkAlternation, mkRepeat
-  TMatcher[T] = object
-    case kind: TMatcherKind
-    of mkTerminal:
-      value: T
-    of mkSequence, mkAlternation:
-      matchers: seq[TMatcher[T]]
-    of mkRepeat:
-      matcher: PMatcher[T]
-      min, max: int
-  PMatcher[T] = ref TMatcher[T]
 
-var
-  m: PMatcher[int]
+block tmatcher1:
+  type
+    TMatcherKind = enum
+      mkTerminal, mkSequence, mkAlternation, mkRepeat
+    TMatcher[T] = object
+      case kind: TMatcherKind
+      of mkTerminal:
+        value: T
+      of mkSequence, mkAlternation:
+        matchers: seq[TMatcher[T]]
+      of mkRepeat:
+        matcher: PMatcher[T]
+        min, max: int
+    PMatcher[T] = ref TMatcher[T]
 
+  var m: PMatcher[int]
+
+
+block tmatcher2:
+  type
+    TMatcherKind = enum
+      mkTerminal, mkSequence, mkAlternation, mkRepeat
+    TMatcher[T] = object
+      case kind: TMatcherKind
+      of mkTerminal:
+        value: T
+      of mkSequence, mkAlternation:
+        matchers: seq[TMatcher[T]]
+      of mkRepeat:
+        matcher: ref TMatcher[T]
+        min, max: int
+  
+  var m: ref TMatcher[int]
 
diff --git a/tests/generics/tgenericmatcher2.nim b/tests/generics/tgenericmatcher2.nim
deleted file mode 100644
index 6832f80b7..000000000
--- a/tests/generics/tgenericmatcher2.nim
+++ /dev/null
@@ -1,18 +0,0 @@
-
-type
-  TMatcherKind = enum
-    mkTerminal, mkSequence, mkAlternation, mkRepeat
-  TMatcher[T] = object
-    case kind: TMatcherKind
-    of mkTerminal:
-      value: T
-    of mkSequence, mkAlternation:
-      matchers: seq[TMatcher[T]]
-    of mkRepeat:
-      matcher: ref TMatcher[T]
-      min, max: int
-
-var
-  m: ref TMatcher[int]
-
-
diff --git a/tests/generics/tgenericprocvar.nim b/tests/generics/tgenericprocvar.nim
index dca9c8538..7935d90c2 100644
--- a/tests/generics/tgenericprocvar.nim
+++ b/tests/generics/tgenericprocvar.nim
@@ -34,3 +34,4 @@ proc test(data: seq[int], value: int): seq[int] =
 for x in items(test(@[1,2,3], 2)):
   stdout.write(x)
 
+stdout.write "\n"
diff --git a/tests/generics/tgenericprop.nim b/tests/generics/tgenericprop.nim
deleted file mode 100644
index 7cddf5617..000000000
--- a/tests/generics/tgenericprop.nim
+++ /dev/null
@@ -1,12 +0,0 @@
-
-type
-  TProperty[T] = object of TObject
-    getProc: proc(property: TProperty[T]): T {.nimcall.}
-    setProc: proc(property: TProperty[T], value: T) {.nimcall.}
-    value: T
-
-proc newProperty[T](value: TObject): TProperty[T] =
-  result.getProc = proc (property: TProperty[T]) =
-    return property.value
-
-
diff --git a/tests/generics/tgenericrefs.nim b/tests/generics/tgenericrefs.nim
deleted file mode 100644
index 245789caf..000000000
--- a/tests/generics/tgenericrefs.nim
+++ /dev/null
@@ -1,38 +0,0 @@
-type
-  PA[T] = ref TA[T]
-  TA[T] = object
-    field: T
-var a: PA[string]
-new(a)
-a.field = "some string"
-
-
-proc someOther[T](len: string): seq[T] = discard
-proc someOther[T](len: int): seq[T] = echo "we"
-
-proc foo[T](x: T) =
-  var s = someOther[T](34)
-  #newSeq[T](34)
-
-foo 23
-
-
-
-when false:
-  # Compiles unless you use var a: PA[string]
-  type
-    PA = ref TA
-    TA[T] = object
-
-
-  # Cannot instantiate:
-  type
-    TA[T] = object
-      a: PA[T]
-    PA[T] = ref TA[T]
-
-  type
-    PA[T] = ref TA[T]
-    TA[T] = object
-
-
diff --git a/tests/generics/tgenerics_issues.nim b/tests/generics/tgenerics_issues.nim
new file mode 100644
index 000000000..3068a22f2
--- /dev/null
+++ b/tests/generics/tgenerics_issues.nim
@@ -0,0 +1,894 @@
+discard """
+  output: '''
+4
+3
+(weight: 17.0, color: 100)
+perm: 22 det: 22
+TMatrix[3, 3, system.int]
+3
+@[0.9, 0.1]
+U[3]
+U[(f: 3)]
+U[[3]]
+b()
+@[1, 2]
+@[3, 4]
+1
+concrete 88
+123
+1
+2
+3
+!!Hi!!
+G:0,1:0.1
+G:0,1:0.1
+H:1:0.1
+0
+(foo: none(seq[Foo]), s: "")
+(foo: some(@[(a: "world", bar: none(Bar))]), s: "hello,")
+@[(a: "hey", bar: none(Bar))]
+'''
+joinable: false
+"""
+
+import macros, sequtils, sets, sugar, tables, typetraits
+
+block t88:
+  type
+    BaseClass[V] = object of RootObj
+      b: V
+
+  proc new[V](t: typedesc[BaseClass], v: V): BaseClass[V] =
+    BaseClass[V](b: v)
+
+  proc baseMethod[V](v: BaseClass[V]): V = v.b
+  proc overriddenMethod[V](v: BaseClass[V]): V = v.baseMethod
+
+  type
+    ChildClass[V] = object of BaseClass[V]
+      c: V
+
+  proc new[V](t: typedesc[ChildClass], v1, v2: V): ChildClass[V] =
+    ChildClass[V](b: v1, c: v2)
+
+  proc overriddenMethod[V](v: ChildClass[V]): V = v.c
+
+  let c = ChildClass[string].new("Base", "Child")
+
+  doAssert c.baseMethod == "Base"
+  doAssert c.overriddenMethod == "Child"
+
+
+
+block t4528:
+  type GenericBase[T] = ref object of RootObj
+  type GenericSubclass[T] = ref object of GenericBase[T]
+  proc foo[T](g: GenericBase[T]) = discard
+  var bar: GenericSubclass[int]
+  foo(bar)
+
+
+
+block t1050_5597:
+  type ArrayType[T] = distinct T
+
+  proc arrayItem(a: ArrayType): auto =
+    static: echo(name(type(a).T))
+    result = (type(a).T)(4)
+
+  var arr: ArrayType[int]
+  echo arrayItem(arr)
+
+  # bug #5597
+
+  template fail() = "what"
+
+  proc g[T](x: var T) =
+    x.fail = 3
+
+  type
+    Obj = object
+      fail: int
+
+  var y: Obj
+  g y
+
+
+
+block t1789:
+  type
+    Foo[N: static[int]] = object
+
+  proc bindStaticN[N](foo: Foo[N]) =
+    var ar0: array[3, int]
+    var ar1: array[N, int]
+    var ar2: array[1..N, int]
+    var ar3: array[0..(N+10), float]
+    echo N
+
+  var f: Foo[3]
+  f.bindStaticN
+
+  # case 2
+
+  type
+    ObjectWithStatic[X, Y: static[int], T] = object
+      bar: array[X * Y, T]   # this one works
+
+    AliasWithStatic[X, Y: static[int], T] = array[X * Y, T]
+
+  var
+    x: ObjectWithStatic[1, 2, int]
+    y: AliasWithStatic[2, 3, int]
+
+  # case 3
+
+  type
+    Bar[N: static[int], T] = object
+      bar: array[N, T]
+
+  proc `[]`[N, T](f: Bar[N, T], n: range[0..(N - 1)]): T =
+    doAssert high(n) == N-1
+    result = f.bar[n]
+
+  var b: Bar[3, int]
+  doAssert b[2] == 0
+
+
+
+block t3977:
+  type Foo[N: static[int]] = object
+
+  proc foo[N](x: Foo[N]) =
+    let n = N
+    doAssert N == n
+
+  var f1: Foo[42]
+  f1.foo
+
+
+
+block t5570:
+  type
+    BaseFruit[T] = object of RootObj
+      color: T
+
+    Banana[T] = object of BaseFruit[uint32]
+      weight: T
+
+  macro printTypeName(typ: typed): untyped =
+    echo "type ", getType(typ).repr
+
+  proc setColor[K](self: var BaseFruit[K], c: int) =
+    printTypeName(self.color)
+    self.color = uint32(c)
+
+  var x: Banana[float64]
+  x.weight = 17
+  printTypeName(x.color)
+  x.setColor(100)
+  echo x
+
+
+
+block t5643:
+  type
+    Matrix[M, N: static[int], T: SomeFloat] = object
+      data: ref array[N * M, T]
+    Matrix64[M, N: static[int]] = Matrix[M, N, float64]
+
+  proc zeros64(M,N: static[int]): Matrix64[M,N] =
+    new result.data
+    for i in 0 ..< (M * N):
+      result.data[i] = 0'f64
+
+  proc bar[M,N: static[int], T](a: Matrix[M,N,T], b: Matrix[M,N,T]) =
+    discard
+
+  let a = zeros64(2,2)
+  bar(a,a)
+    # https://github.com/nim-lang/Nim/issues/5643
+    #
+    # The test case was failing here, because the compiler failed to
+    # detect the two matrix instantiations as the same type.
+    #
+    # The root cause was that the `T` type variable is a different
+    # type after the first Matrix type has been matched.
+    #
+    # Sigmatch was failing to match the second version of `T`, but
+    # due to some complex interplay between tyOr, tyTypeDesc and
+    # tyGenericParam this was allowed to went through. The generic
+    # instantiation of the second matrix was incomplete and the
+    # generic cache lookup failed, producing two separate types.
+
+
+
+block t5683:
+  type Matrix[M,N: static[int]] = array[M, array[N, float]]
+
+  proc det[M,N](a: Matrix[M,N]): int = N*10 + M
+  proc perm[M,N](a: Matrix[M,N]): int = M*10 + N
+
+  const
+    a = [ [1.0, 2.0]
+        , [3.0, 4.0]
+        ]
+
+  echo "perm: ", a.perm, " det: ", a.det
+
+  # This tests multiple instantiations of a generic
+  # proc involving static params:
+  type
+    Vector64[N: static[int]] = ref array[N, float64]
+    Array64[N: static[int]] = array[N, float64]
+
+  proc vector[N: static[int]](xs: Array64[N]): Vector64[N] =
+    new result
+    for i in 0 ..< N:
+      result[i] = xs[i]
+
+  let v1 = vector([1.0, 2.0, 3.0, 4.0, 5.0])
+  let v2 = vector([1.0, 2.0, 3.0, 4.0, 5.0])
+  let v3 = vector([1.0, 2.0, 3.0, 4.0])
+
+
+
+block t7794:
+  type
+    Data[T:SomeNumber, U:SomeFloat] = ref object
+      x: T
+      value*: U
+
+  var d = Data[int, float64](x:10.int, value:2'f64)
+  doAssert d.x == 10
+  doAssert d.value == 2.0
+
+
+
+block t8403:
+  proc sum[T](s: seq[T], R: typedesc): R =
+    var sum: R = 0
+    for x in s:
+      sum += R(x)
+    return sum
+
+  doAssert @[1, 2, 3].sum(float) == 6.0
+
+
+
+block t8439:
+  type
+    Cardinal = enum
+      north, east, south, west
+
+  proc foo[cardinal: static[Cardinal]](): int = 1
+  doAssert foo[north]() == 1
+
+
+
+block t8694:
+  when true:
+    # Error: undeclared identifier: '|'
+    proc bar[T](t:T): bool =
+      runnableExamples:
+        type Foo = int | float
+      true
+    doAssert bar(0)
+
+  when true:
+    # ok
+    proc bar(t:int): bool =
+      runnableExamples:
+        type Foo = int | float
+      true
+    doAssert bar(0)
+
+  when true:
+    # Error: undeclared identifier: '|'
+    proc bar(t:typedesc): bool =
+      runnableExamples:
+        type Foo = int | float
+      true
+    doAssert bar(int)
+
+
+
+block t9130:
+  when true:
+    # stack overflow
+    template baz1(iter: untyped): untyped =
+      runnableExamples:
+        import sugar
+        proc fun(a: proc(x:int): int) = discard
+        baz1(fun(x:int => x))
+      discard
+
+    proc foo1[A](ts: A) =
+      baz1(ts)
+
+  when true:
+    # ok
+    template baz2(iter: untyped): untyped =
+      runnableExamples:
+        import sugar
+        proc fun(a: proc(x:int): int) = discard
+        baz2(fun(x:int => x))
+      discard
+
+    proc foo2(ts: int) =
+      baz2(ts)
+
+  when true:
+    # stack overflow
+    template baz3(iter: untyped): untyped =
+      runnableExamples:
+        baz3(fun(x:int => x))
+      discard
+
+    proc foo3[A](ts: A) =
+      baz3(ts)
+
+
+
+block t1056:
+  type
+    TMatrix[N,M: static[int], T] = object
+      data: array[0..N*M-1, T]
+
+    TMat2[T] = TMatrix[2,2,T]
+
+  proc echoMatrix(a: TMatrix) =
+    echo a.type.name
+    echo TMatrix.N
+
+  proc echoMat2(a: TMat2) =
+    echo TMat2.M
+
+  var m = TMatrix[3,3,int](data: [1,2,3,4,5,6,7,8,9])
+
+  echoMatrix m
+
+
+
+block t4884:
+  type
+    Vec[N: static[int], T] = object
+      arr*: array[N, T]
+
+    Mat[N,M: static[int], T] = object
+      arr: array[N, Vec[M,T]]
+
+  var m : Mat[3,3,float]
+  var strMat : Mat[m.N, m.M, string]
+  var lenMat : Mat[m.N, m.M, int]
+
+
+
+block t2221:
+  var tblo: TableRef[string, int]
+  doAssert tblo == nil
+
+
+
+block t2304:
+  type TV2[T:SomeNumber] = array[0..1, T]
+  proc newV2T[T](x, y: T=0): TV2[T] = [x, y]
+
+  let x = newV2T[float](0.9, 0.1)
+  echo(@x)
+
+
+
+block t2752:
+  proc myFilter[T](it: (iterator(): T), f: (proc(anything: T):bool)): (iterator(): T) =
+    iterator aNameWhichWillConflict(): T {.closure.}=
+      for x in it():
+        if f(x):
+          yield x
+    result = aNameWhichWillConflict
+
+  iterator testIt():int {.closure.}=
+    yield -1
+    yield 2
+
+  #let unusedVariable = myFilter(testIt, (x: int) => x > 0)
+
+  proc onlyPos(it: (iterator(): int)): (iterator(): int)=
+    iterator aNameWhichWillConflict(): int {.closure.}=
+      var filtered = onlyPos(myFilter(it, (x:int) => x > 0))
+      for x in filtered():
+        yield x
+    result = aNameWhichWillConflict
+
+  let x = onlyPos(testIt)
+
+
+
+block t5106:
+  block:
+    type T = distinct int
+
+    proc `+`(a, b: T): T =
+      T(int(a) + int(b))
+
+    type U[F: static[T]] = distinct int
+
+    proc `+`[P1, P2: static[T]](a: U[P1], b: U[P2]): U[P1 + P2] =
+      U[P1 + P2](int(a) + int(b))
+
+    var a = U[T(1)](1)
+    var b = U[T(2)](2)
+    var c = a + b
+    echo c.type.name
+
+  block:
+    type T = object
+      f: int
+
+    proc `+`(a, b: T): T =
+      T(f: a.f + b.f)
+
+    type U[F: static[T]] = distinct int
+
+    proc `+`[P1, P2: static[T]](a: U[P1], b: U[P2]): U[P1 + P2] =
+      U[P1 + P2](int(a) + int(b))
+
+    var a = U[T(f: 1)](1)
+    var b = U[T(f: 2)](2)
+    var c = a + b
+    echo c.type.name
+
+  block:
+    type T = distinct array[0..0, int]
+
+    proc `+`(a, b: T): T =
+      T([array[0..0, int](a)[0] + array[0..0, int](b)[0]])
+
+    type U[F: static[T]] = distinct int
+
+    proc `+`[P1, P2: static[T]](a: U[P1], b: U[P2]): U[P1 + P2] =
+      U[P1 + P2](int(a) + int(b))
+
+    var a = U[T([1])](1)
+    var b = U[T([2])](2)
+    var c = a + b
+    echo c.type.name
+
+
+
+block t3055:
+  proc b(t: int | string)
+  proc a(t: int) = b(t)
+  proc b(t: int | string) = echo "b()"
+  a(1)
+
+  # test recursive generics still work:
+  proc fac[T](x: T): T =
+    if x == 0: return 1
+    else: return fac(x-1)*x
+
+  doAssert fac(6) == 720
+  doAssert fac(5.0) == 120.0
+
+
+  # test recursive generic with forwarding:
+  proc fac2[T](x: T): T
+
+  doAssert fac2(6) == 720
+  doAssert fac2(5.0) == 120.0
+
+  proc fac2[T](x: T): T =
+    if x == 0: return 1
+    else: return fac2(x-1)*x
+
+
+
+block t1187:
+  type
+    TEventArgs = object
+      skip: bool
+    TEventHandler[T] = proc (e: var TEventArgs, data: T) {.closure.}
+    TEvent[T] = object
+      #handlers: seq[TEventHandler[T]] # Does not work
+      handlers: seq[proc (e: var TEventArgs, d: T) {.closure.}] # works
+
+    TData = object
+      x: int
+
+    TSomething = object
+      s: TEvent[TData]
+
+  proc init[T](e: var TEvent[T]) =
+    e.handlers.newSeq(0)
+
+  #proc add*[T](e: var TEvent[T], h: proc (e: var TEventArgs, data: T) {.closure.}) =
+  # this line works
+  proc add[T](e: var TEvent[T], h: TEventHandler[T]) =
+    # this line does not work
+    e.handlers.add(h)
+
+  proc main () =
+    var something: TSomething
+    something.s.init()
+    var fromOutside = 4711
+
+    something.s.add() do (e: var TEventArgs, data: TData):
+      var x = data.x
+      x = fromOutside
+
+  main()
+
+
+
+block t1919:
+  type
+    Base[M] = object of RootObj
+      a : M
+    Sub1[M] = object of Base[M]
+      b : int
+    Sub2[M] = object of Sub1[M]
+      c : int
+
+  var x: Sub2[float]
+  doAssert x.a == 0.0
+
+
+
+block t5756:
+  type
+    Vec[N : static[int]] = object
+      x: int
+      arr: array[N, int32]
+
+    Mat[M,N: static[int]] = object
+      x: int
+      arr: array[M, Vec[N]]
+
+  proc vec2(x,y:int32) : Vec[2] =
+    result.arr = [x,y]
+    result.x = 10
+
+  proc mat2(a,b: Vec[2]): Mat[2,2] =
+    result.arr = [a,b]
+    result.x = 20
+
+  const M = mat2(vec2(1, 2), vec2(3, 4))
+
+  let m1 = M
+  echo @(m1.arr[0].arr)
+  echo @(m1.arr[1].arr)
+
+  proc foo =
+    let m2 = M
+    echo m1.arr[0].arr[0]
+
+  foo()
+
+
+
+block t7854:
+  type
+    Stream = ref StreamObj
+    StreamObj = object of RootObj
+
+    InhStream = ref InhStreamObj
+    InhStreamObj = object of Stream
+      f: string
+
+  proc newInhStream(f: string): InhStream =
+    new(result)
+    result.f = f
+
+  var val: int
+  let str = newInhStream("input_file.json")
+
+  block:
+    # works:
+    proc load[T](data: var T, s: Stream) =
+      discard
+    load(val, str)
+
+  block:
+    # works
+    proc load[T](s: Stream, data: T) =
+      discard
+    load(str, val)
+
+  block:
+    # broken
+    proc load[T](s: Stream, data: var T) =
+      discard
+    load(str, val)
+
+
+
+block t5864:
+  proc defaultStatic(s: openArray, N: static[int] = 1): int = N
+  proc defaultGeneric[T](a: T = 2): int = a
+
+  let a = [1, 2, 3, 4].defaultStatic()
+  let b = defaultGeneric()
+
+  doAssert a == 1
+  doAssert b == 2
+
+
+
+block t3498:
+  template defaultOf[T](t: T): untyped = (var d: T; d)
+
+  doAssert defaultOf(1) == 0
+
+  # assignment using template
+
+  template tassign[T](x: var seq[T]) =
+    x = @[1, 2, 3]
+
+  var y: seq[int]
+  tassign(y) #<- x is expected = @[1, 2, 3]
+  tassign(y)
+
+  doAssert y[0] == 1
+  doAssert y[1] == 2
+  doAssert y[2] == 3
+
+
+
+block t3499:
+  proc foo[T](x: proc(): T) =
+    echo "generic ", x()
+
+  proc foo(x: proc(): int) =
+    echo "concrete ", x()
+
+  # note the following 'proc' is not .closure!
+  foo(proc (): auto {.nimcall.} = 88)
+
+  # bug #3499 last snippet fixed
+  # bug 705  last snippet fixed
+
+
+
+
+block t797:
+  proc foo[T](s:T):string = $s
+
+  type IntStringProc = proc(x: int): string
+
+  var f1 = IntStringProc(foo)
+  var f2: proc(x: int): string = foo
+  var f3: IntStringProc = foo
+
+  echo f1(1), f2(2), f3(3)
+
+  for x in map([1,2,3], foo): echo x
+
+
+
+block t4658:
+  var x = 123
+  proc twice[T](f: T -> T): T -> T = (x: T) => f(f(x))
+  proc quote(s: string): string = "!" & s & "!"
+  echo twice(quote)("Hi")
+
+
+
+block t4589:
+  type SimpleTable[TKey, TVal] = TableRef[TKey, TVal]
+  template newSimpleTable(TKey, TVal: typedesc): SimpleTable[TKey, TVal] = newTable[TKey, TVal]()
+  var fontCache : SimpleTable[string, SimpleTable[int32, int]]
+  fontCache = newSimpleTable(string, SimpleTable[int32, int])
+
+
+
+block t4600:
+  template foo(x: untyped): untyped = echo 1
+  template foo(x,y: untyped): untyped = echo 2
+
+  proc bar1[T](x: T) = foo(x)
+  proc bar2(x: float) = foo(x,x)
+  proc bar3[T](x: T) = foo(x,x)
+
+
+
+block t4672:
+  type
+    EnumContainer[T: enum] = object
+      v: T
+    SomeEnum {.pure.} = enum
+      A,B,C
+
+  proc value[T: enum](this: EnumContainer[T]): T =
+    this.v
+
+  var enumContainer: EnumContainer[SomeEnum]
+  discard enumContainer.value()
+
+
+
+block t4863:
+  type
+    G[i,j: static[int]] = object
+      v:float
+    H[j: static[int]] = G[0,j]
+  proc p[i,j: static[int]](x:G[i,j]) = echo "G:", i, ",", j, ":", x.v
+  proc q[j: static[int]](x:H[j]) = echo "H:", j, ":", x.v
+
+  var
+    g0 = G[0,1](v: 0.1)
+    h0:H[1] = g0
+  p(g0)
+  p(h0)
+  q(h0)
+
+
+
+block t1684:
+  type
+    BaseType {.inheritable pure.} = object
+      idx: int
+
+    DerivedType {.final pure.} = object of BaseType
+
+  proc index[Toohoo: BaseType](h: Toohoo): int {.inline.} = h.idx
+  proc newDerived(idx: int): DerivedType {.inline.} = DerivedType(idx: idx)
+
+  let d = newDerived(2)
+  doAssert(d.index == 2)
+
+
+
+block t5632:
+  type Option[T] = object
+
+  proc point[A](v: A, t: typedesc[Option[A]]): Option[A] =
+    discard
+
+  discard point(1, Option)
+
+
+
+block t7247:
+  type n8 = range[0'i8..127'i8]
+  var tab = initHashSet[n8]()
+  doAssert tab.contains(8) == false
+
+
+
+block t3717:
+  type
+    Foo[T] = object
+      a: T
+    Foo1[T] = Foo[T] | int
+
+  proc foo[T](s: Foo1[Foo[T]]): T =
+    5
+
+  var f: Foo[Foo[int]]
+  discard foo(f)
+
+
+
+block: # issue #9458
+  type
+    Option[T] = object
+      val: T
+      has: bool
+
+    Bar = object
+
+  proc none(T: typedesc): Option[T] =
+    discard
+
+  proc foo[T](self: T; x: Option[Bar] = Bar.none) =
+    discard
+
+  foo(1)
+
+
+# bug #8426
+type
+  MyBool[T: uint] = range[T(0)..T(1)] # Works
+
+var x: MyBool[uint]
+echo x
+
+# x = 2 # correctly prevented
+
+type
+  MyBool2 = range[uint(0)..uint(1)] # Error ordinal or float type expected
+
+
+# bug #10396
+import options, strutils
+
+type
+  Foo {.acyclic.} = object
+    a: string
+    bar: Option[Bar]
+
+  Bar {.acyclic.} = object
+    foo: Option[seq[Foo]]   # if this was just Option[Foo], everything works fine
+    s: string
+
+proc getBar(x: string): Bar
+
+proc intoFoos(ss: seq[string]): seq[Foo] =
+  result = @[]
+  for s in ss:
+    let spl = s.split(',')
+    if spl.len > 1:
+      result.add Foo(a: spl[0],
+                     bar: some(getBar(spl[1])))
+    else:
+      result.add Foo(a: s,
+                     bar: none[Bar]())
+
+proc getBar(x: string): Bar =
+  let spl = x.split(' ')
+  result =
+    if spl.len > 1:
+      Bar(foo: some(spl[1..high(spl)].intoFoos),
+          s: spl[0])
+    else:
+      Bar(foo: none[seq[Foo]](),
+          s: "")
+
+proc fakeReadLine(): string = "hey"
+
+echo getBar(fakeReadLine()) # causes error
+
+echo getBar("hello, world") # causes error
+
+discard $getBar(fakeReadLine()) # causes error
+
+discard $getBar("hello, world") # causes error
+
+discard getBar(fakeReadLine()) # no error
+
+discard getBar("hello, world") # no error
+
+echo intoFoos(fakeReadLine().split(' ')) # no error, works as expected
+
+
+# bug #14990
+type
+  Tile3 = Tile2
+  Tile2 = Tile
+  Tile[n] = object
+    a: n
+
+var a: Tile3[int]
+
+block: # Ensure no segfault from constraint
+  type
+    Regex[A: SomeOrdinal] = ref object
+      val: Regex[A]
+    MyConstraint = (seq or enum or set)
+    MyOtherType[A: MyConstraint] = ref object
+      val: MyOtherType[A]
+
+  var
+    a = Regex[int]()
+    b = Regex[bool]()
+    c = MyOtherType[seq[int]]()
+
+block: # https://github.com/nim-lang/Nim/issues/20416
+  type
+    Item[T] = object
+      link:ptr Item[T]
+      data:T
+
+    KVSeq[A,B] = seq[(A,B)]
+
+    MyTable[A,B] = object
+      data: KVSeq[A,B]
+
+    Container[T] = object
+      a: MyTable[int,ref Item[T]]
+
+  proc p1(sg:Container) = discard # Make sure that a non parameterized 'Container' argument still compiles
+
+  proc p2[T](sg:Container[T]) = discard
+  var v : Container[int]
+  p2(v)
diff --git a/tests/generics/tgenerics_various.nim b/tests/generics/tgenerics_various.nim
new file mode 100644
index 000000000..53661236e
--- /dev/null
+++ b/tests/generics/tgenerics_various.nim
@@ -0,0 +1,254 @@
+discard """
+  output: '''
+we
+direct
+generic
+generic
+'''
+joinable: false
+"""
+
+import algorithm, sugar, sequtils, typetraits, asyncdispatch
+
+block tconfusing_arrow:
+  type Deck = object
+    value: int
+
+  proc sort(h: var seq[Deck]) =
+    # works:
+    h.sort(proc (x, y: Deck): auto =
+      cmp(x.value, y.value))
+    # fails:
+    h.sort((x, y: Deck) => cmp(ord(x.value), ord(y.value)))
+
+  var player: seq[Deck] = @[]
+  player.sort()
+
+
+
+block tdictdestruct:
+  type
+    TDict[TK, TV] = object
+      k: TK
+      v: TV
+    PDict[TK, TV] = ref TDict[TK, TV]
+
+  proc fakeNew[T](x: var ref T, destroy: proc (a: ref T) {.nimcall.}) =
+    discard
+
+  proc destroyDict[TK, TV](a: PDict[TK, TV]) =
+      return
+  proc newDict[TK, TV](a: TK, b: TV): PDict[TK, TV] =
+      fakeNew(result, destroyDict[TK, TV])
+
+  # Problem: destroyDict is not instantiated when newDict is instantiated!
+  discard newDict("a", "b")
+
+
+
+block tgenericdefaults:
+  type
+    TFoo[T, U, R = int] = object
+      x: T
+      y: U
+      z: R
+
+    TBar[T] = TFoo[T, array[4, T], T]
+
+  var x1: TFoo[int, float]
+
+  static:
+    doAssert type(x1.x) is int
+    doAssert type(x1.y) is float
+    doAssert type(x1.z) is int
+
+  var x2: TFoo[string, R = float, U = seq[int]]
+
+  static:
+    doAssert type(x2.x) is string
+    doAssert type(x2.y) is seq[int]
+    doAssert type(x2.z) is float
+
+  var x3: TBar[float]
+
+  static:
+    doAssert type(x3.x) is float
+    doAssert type(x3.y) is array[4, float]
+    doAssert type(x3.z) is float
+
+
+
+block tprop:
+  type
+    TProperty[T] = object of RootObj
+      getProc: proc(property: TProperty[T]): T {.nimcall.}
+      setProc: proc(property: TProperty[T], value: T) {.nimcall.}
+      value: T
+
+  proc newProperty[T](value: RootObj): TProperty[T] =
+    result.getProc = proc (property: TProperty[T]) =
+      return property.value
+
+
+
+block trefs:
+  type
+    PA[T] = ref TA[T]
+    TA[T] = object
+      field: T
+  var a: PA[string]
+  new(a)
+  a.field = "some string"
+
+  proc someOther[T](len: string): seq[T] = discard
+  proc someOther[T](len: int): seq[T] = echo "we"
+
+  proc foo[T](x: T) =
+    var s = someOther[T](34)
+    #newSeq[T](34)
+
+  foo 23
+
+  when false:
+    # Compiles unless you use var a: PA[string]
+    type
+      PA = ref TA
+      TA[T] = object
+
+    # Cannot instantiate:
+    type
+      TA[T] = object
+        a: PA[T]
+      PA[T] = ref TA[T]
+
+    type
+      PA[T] = ref TA[T]
+      TA[T] = object
+
+
+
+block tmap_auto:
+  let x = map(@[1, 2, 3], x => x+10)
+  doAssert x == @[11, 12, 13]
+
+  let y = map(@[(1,"a"), (2,"b"), (3,"c")], x => $x[0] & x[1])
+  doAssert y == @["1a", "2b", "3c"]
+
+  proc eatsTwoArgProc[T,S,U](a: T, b: S, f: proc(t: T, s: S): U): U =
+    f(a,b)
+
+  let z = eatsTwoArgProc(1, "a", (t,s) => $t & s)
+  doAssert z == "1a"
+
+
+
+block tproctypecache_falsepositive:
+  type
+    Callback = proc() {.closure, gcsafe.}
+    GameState = ref object
+      playerChangeHandlers: seq[Callback]
+
+  proc newGameState(): GameState =
+    result = GameState(
+      playerChangeHandlers: newSeq[Callback]() # this fails
+    )
+
+
+
+block tptrinheritance:
+  type NSPasteboardItem = ptr object
+  type NSPasteboard = ptr object
+  type NSArrayAbstract {.inheritable.} = ptr object
+  type NSMutableArrayAbstract = ptr object of NSArrayAbstract
+  type NSArray[T] = ptr object of NSArrayAbstract
+  type NSMutableArray[T] = ptr object of NSArray[T]
+
+  proc newMutableArrayAbstract(): NSMutableArrayAbstract = discard
+
+  template newMutableArray(T: typedesc): NSMutableArray[T] =
+    cast[NSMutableArray[T]](newMutableArrayAbstract())
+
+  proc writeObjects(p: NSPasteboard, o: NSArray[NSPasteboardItem]) = discard
+
+  let a = newMutableArray NSPasteboardItem
+  var x: NSMutableArray[NSPasteboardItem]
+  var y: NSArray[NSPasteboardItem] = x
+
+  writeObjects(nil, a)
+
+
+
+block tsigtypeop:
+  type Vec3[T] = array[3, T]
+
+  proc foo(x: Vec3, y: Vec3.T, z: x.T): x.type.T =
+    return 10
+
+  var y: Vec3[int] = [1, 2, 3]
+  var z: int = foo(y, 3, 4)
+
+
+
+block tvarargs_vs_generics:
+  proc withDirectType(args: string) =
+    echo "direct"
+  proc withDirectType[T](arg: T) =
+    echo "generic"
+  proc withOpenArray(args: openArray[string]) =
+    echo "openArray"
+  proc withOpenArray[T](arg: T) =
+    echo "generic"
+  proc withVarargs(args: varargs[string]) =
+    echo "varargs"
+  proc withVarargs[T](arg: T) =
+    echo "generic"
+
+  withDirectType "string"
+  withOpenArray "string"
+  withVarargs "string"
+
+block:
+  type
+    Que[T] {.gcsafe.} = object
+      x: T
+
+  proc `=`[T](q: var Que[T]; x: Que[T]) =
+    discard
+
+  var x: Que[int]
+  doAssert(x.x == 0)
+
+
+# bug #4466
+proc identity[T](t: T): T = t
+
+proc doSomething[A, B](t: tuple[a: A, b: B]) = discard
+
+discard identity((c: 1, d: 2))
+doSomething(identity((1, 2)))
+
+# bug #6231
+proc myProc[T, U](x: T or U) = discard
+
+myProc[int, string](x = 2)
+
+block: # issue #8390
+  proc x[T:SomeFloat](q: openarray[T], y: T = 1): string =
+    doAssert $q.type == $openarray[y.type]
+    $y.type
+
+  doAssert x(@[1.0]) == $1.0.type
+
+
+block: # issue #9381
+  var evalCount {.compileTime.} = 0
+
+  macro test(t: typed): untyped =
+    inc evalCount
+    t
+
+  type GenericObj[T] = object
+    f: test(T)
+
+  var x: GenericObj[int]
+  static: doAssert evalCount == 1
diff --git a/tests/generics/tgenericsdefaultvalues.nim b/tests/generics/tgenericsdefaultvalues.nim
deleted file mode 100644
index 2604c1031..000000000
--- a/tests/generics/tgenericsdefaultvalues.nim
+++ /dev/null
@@ -1,14 +0,0 @@
-discard """
-output: "12"
-"""
-
-# https://github.com/nim-lang/Nim/issues/5864
-
-proc defaultStatic(s: openarray, N: static[int] = 1): int = N
-proc defaultGeneric[T](a: T = 2): int = a
-
-let a = [1, 2, 3, 4].defaultStatic()
-let b = defaultGeneric()
-
-echo a, b
-
diff --git a/tests/generics/tgenericshardcases.nim b/tests/generics/tgenericshardcases.nim
deleted file mode 100644
index 72a2f4ec2..000000000
--- a/tests/generics/tgenericshardcases.nim
+++ /dev/null
@@ -1,42 +0,0 @@
-discard """
-  file: "tgenericshardcases.nim"
-  output: "2\n5\n126\n3"
-"""
-
-import typetraits
-
-proc typeNameLen(x: typedesc): int {.compileTime.} =
-  result = x.name.len
-
-macro selectType(a, b: typedesc): typedesc =
-  result = a
-
-type
-  Foo[T] = object
-    data1: array[T.high, int]
-    data2: array[typeNameLen(T), float]
-    data3: array[0..T.typeNameLen, selectType(float, int)]
-
-  MyEnum = enum A, B, C, D
-
-var f1: Foo[MyEnum]
-var f2: Foo[int8]
-
-echo high(f1.data1) # (D = 3) - 1 == 2
-echo high(f1.data2) # (MyEnum.len = 6) - 1 == 5
-
-echo high(f2.data1) # 127 - 1 == 126
-echo high(f2.data2) # int8.len - 1 == 3
-
-static:
-  assert high(f1.data1) == ord(C)
-  assert high(f1.data2) == 5 # length of MyEnum minus one, because we used T.high
-
-  assert high(f2.data1) == 126
-  assert high(f2.data2) == 3
-
-  assert high(f1.data3) == 6 # length of MyEnum
-  assert high(f2.data3) == 4 # length of int8
-
-  assert f2.data3[0] is float
-
diff --git a/tests/generics/tgenerictmpl.nim b/tests/generics/tgenerictmpl.nim
deleted file mode 100644
index e18f020c2..000000000
--- a/tests/generics/tgenerictmpl.nim
+++ /dev/null
@@ -1,21 +0,0 @@
-discard """
-  output: '''0
-123'''
-"""
-
-# bug #3498
-
-template defaultOf[T](t: T): untyped = (var d: T; d)
-
-echo defaultOf(1) #<- excpected 0
-
-# assignment using template
-
-template tassign[T](x: var seq[T]) =
-  x = @[1, 2, 3]
-
-var y: seq[int]
-tassign(y) #<- x is expected = @[1, 2, 3]
-tassign(y)
-
-echo y[0], y[1], y[2]
diff --git a/tests/generics/tgenerictmpl2.nim b/tests/generics/tgenerictmpl2.nim
index ac92d3281..2efb000b3 100644
--- a/tests/generics/tgenerictmpl2.nim
+++ b/tests/generics/tgenerictmpl2.nim
@@ -21,7 +21,7 @@ ttmpl(1)
 ttmpl[int](1) #<- crash case #1
 
 tproc[int]()
-discard tproc[int]
+let _ = tproc[int]
 ttmpl[int]()  #<- crash case #2
 ttmpl[int]    #<- crash case #3
 
diff --git a/tests/generics/tgenericvariant.nim b/tests/generics/tgenericvariant.nim
index 348d3da6e..5ba3a2e7c 100644
--- a/tests/generics/tgenericvariant.nim
+++ b/tests/generics/tgenericvariant.nim
@@ -1,3 +1,13 @@
+discard """
+output: '''
+Test
+abcxyz123
+'''
+"""
+
+proc fakeReadLine(): string =
+  "abcxyz123"
+
 type
   TMaybe[T] = object
     case empty: bool
@@ -12,12 +22,15 @@ proc Nothing[T](): TMaybe[T] =
   result.empty = true
 
 proc safeReadLine(): TMaybe[string] =
-  var r = stdin.readLine()
+  var r = fakeReadLine()
   if r == "": return Nothing[string]()
   else: return Just(r)
 
-when isMainModule:
+proc main() =
   var Test = Just("Test")
   echo(Test.value)
   var mSomething = safeReadLine()
   echo(mSomething.value)
+  mSomething = safeReadLine()
+
+main()
diff --git a/tests/generics/tgenericwhen.nim b/tests/generics/tgenericwhen.nim
new file mode 100644
index 000000000..87672a699
--- /dev/null
+++ b/tests/generics/tgenericwhen.nim
@@ -0,0 +1,58 @@
+discard """
+  targets: "c js"
+"""
+
+block: # issue #24041
+  type ArrayBuf[N: static int, T = byte] = object
+    when sizeof(int) > sizeof(uint8):
+      when N <= int(uint8.high):
+        n: uint8
+      else:
+        when sizeof(int) > sizeof(uint16):
+          when N <= int(uint16.high):
+            n: uint16
+          else:
+            when sizeof(int) > sizeof(uint32):
+              when N <= int(uint32.high):
+                n: uint32
+              else:
+                n: int
+            else:
+              n: int
+        else:
+          n: int
+    else:
+      n: int
+
+  var x: ArrayBuf[8]
+  doAssert x.n is uint8
+  when sizeof(int) > sizeof(uint32):
+    var y: ArrayBuf[int(uint32.high) * 8]
+    doAssert y.n is int
+
+block: # constant condition after dynamic one
+  type Foo[T] = object
+    when T is int:
+      a: int
+    elif true:
+      a: string
+    else:
+      a: bool
+  var x: Foo[string]
+  doAssert x.a is string
+  var y: Foo[int]
+  doAssert y.a is int
+  var z: Foo[float]
+  doAssert z.a is string
+
+block: # issue #4774, but not with threads
+  const hasThreadSupport = not defined(js)
+  when hasThreadSupport:
+    type Channel[T] = object
+      value: T
+  type
+    SomeObj[T] = object
+      when hasThreadSupport:
+        channel: ptr Channel[T]
+  var x: SomeObj[int]
+  doAssert compiles(x.channel) == hasThreadSupport
diff --git a/tests/generics/tgensyminst.nim b/tests/generics/tgensyminst.nim
new file mode 100644
index 000000000..3f30188d8
--- /dev/null
+++ b/tests/generics/tgensyminst.nim
@@ -0,0 +1,29 @@
+# issue #24048
+
+import macros
+
+proc map(fn: proc(val: int): void) = fn(1)
+
+# This works fine, and is the exact same function call as what's
+# generated by the macro `aBug`.
+map proc(val: auto): void =
+  let variable = 123
+
+macro aBug() =
+  # 1. let sym = ident("variable")
+  let sym = genSym(nskLet, "variable")
+  let letStmt = newLetStmt(sym, newLit(123))
+
+  let lambda = newProc(
+    params = @[
+      ident("void"),
+      newIdentDefs(ident("val"), ident("auto")),
+      # 2. newIdentDefs(ident("val"), ident("int")),
+    ],
+    body = newStmtList(letStmt),
+    procType = nnkLambda
+  )
+
+  result = newCall(bindSym("map"), lambda)
+
+aBug()
diff --git a/tests/generics/timpl_ast.nim b/tests/generics/timpl_ast.nim
new file mode 100644
index 000000000..97fe128cd
--- /dev/null
+++ b/tests/generics/timpl_ast.nim
@@ -0,0 +1,80 @@
+import std/macros
+import std/assertions
+
+block: # issue #16639
+  type Foo[T] = object
+    when true:
+      x: float
+
+  type Bar = object
+    when true:
+      x: float
+
+  macro test() =
+    let a = getImpl(bindSym"Foo")[^1]
+    let b = getImpl(bindSym"Bar")[^1]
+    doAssert treeRepr(a) == treeRepr(b)
+
+  test()
+
+import strutils
+
+block: # issues #9899, ##14708
+  macro implRepr(a: typed): string =
+    result = newLit(repr(a.getImpl))
+
+  type
+    Option[T] = object
+      when false: discard # issue #14708
+      when false: x: int
+      when T is (ref | ptr):
+        val: T
+      else:
+        val: T
+        has: bool
+
+  static: # check information is retained
+    let r = implRepr(Option)
+    doAssert "when T is" in r
+    doAssert r.count("val: T") == 2
+    doAssert "has: bool" in r
+
+  block: # try to compile the output
+    macro parse(s: static string) =
+      result = parseStmt(s)
+    parse("type " & implRepr(Option))
+
+block: # issue #22639
+  type
+    Spectrum[N: static int] = object
+      data: array[N, float]
+    AngleInterpolator = object
+      data: seq[Spectrum[60]]
+  proc initInterpolator(num: int): AngleInterpolator =
+    result = AngleInterpolator()
+    for i in 0 ..< num:
+      result.data.add Spectrum[60]()
+  macro genCompatibleTuple(t: typed): untyped =
+    let typ = t.getType[1].getTypeImpl[2]
+    result = nnkTupleTy.newTree()
+    for i, ch in typ: # is `nnkObjectTy`
+      result.add nnkIdentDefs.newTree(ident(ch[0].strVal), # ch is `nnkIdentDefs`
+                                      ch[1],
+                                      newEmptyNode())
+  proc fullSize[T: object | tuple](x: T): int =
+    var tmp: genCompatibleTuple(T)
+    result = 0
+    for field, val in fieldPairs(x):
+      result += sizeof(val)
+    doAssert result == sizeof(tmp)
+
+  let reflectivity = initInterpolator(1)
+  for el in reflectivity.data:
+    doAssert fullSize(el) == sizeof(el)
+  doAssert fullSize(reflectivity.data[0]) == sizeof(reflectivity.data[0])
+  doAssert genCompatibleTuple(Spectrum[60]) is tuple[data: array[60, float]]
+  doAssert genCompatibleTuple(Spectrum[120]) is tuple[data: array[120, float]]
+  type Foo[T] = object
+    data: T
+  doAssert genCompatibleTuple(Foo[int]) is tuple[data: int]
+  doAssert genCompatibleTuple(Foo[float]) is tuple[data: float]
diff --git a/tests/generics/timplicit_and_explicit.nim b/tests/generics/timplicit_and_explicit.nim
new file mode 100644
index 000000000..7220b7429
--- /dev/null
+++ b/tests/generics/timplicit_and_explicit.nim
@@ -0,0 +1,65 @@
+
+block: # basic test
+  proc doStuff[T](a: SomeInteger): T = discard
+  proc doStuff[T;Y](a: SomeInteger, b: Y): Y = discard
+  assert typeof(doStuff[int](100)) is int
+  assert typeof(doStuff[int, float](100, 1.0)) is float
+  assert typeof(doStuff[int, string](100, "Hello")) is string
+
+  proc t[T](x: T; z: int | float): seq[T] = result.add(x & $z)
+
+  assert t[string]("Hallo", 2.0) == @["Hallo" & $2.0]
+
+  proc t2[T](z: int | float): seq[T] = result.add($z)
+
+  assert t2[string](2.0) == @[$2.0]
+
+block: # template test
+  template someThing[T;Y](a: SomeFloat, b: SomeOrdinal): (T, Y) = (a, b)
+  assert typeof(someThing[float64, int](1.0, 100)) is (float64, int)
+
+block: # static test
+  proc t[T](s: static bool) = discard
+  proc t2[T](s: static string) = discard
+  t[string](true)
+  t2[int]("hello")
+  t2[string]("world")
+  t2[float]("test222222")
+
+block: #11152
+  proc f[T](X: typedesc) = discard
+  f[int](string)
+
+block: #15622
+  proc test1[T](a: T, b: static[string] = "") = discard
+  test1[int64](123)
+  proc test2[T](a: T, b: static[string] = "") = discard
+  doAssert not (compiles do:
+    test2[int64, static[string]](123))
+
+block: #4688
+  proc convertTo[T](v: int or float): T = (T)(v)
+  discard convertTo[float](1)
+
+block: #4164
+  proc printStr[T](s: static[string]): T = discard
+  discard printStr[int]("hello static")
+
+import macros
+
+block: # issue #9040, statics with template, macro, symchoice explicit generics
+  block: # macro
+    macro fun[N: static int](): untyped =
+      newLit 1
+    const a = fun[2]()
+    doAssert a == 1
+  block: # template
+    template fun[N: static int](): untyped =
+      1
+    const a = fun[2]()
+    doAssert a == 1
+  block: # symchoice
+    proc newSeq[x: static int](): int = 1
+    template foo: int =
+      newSeq[2]()
+    doAssert foo() == 1
diff --git a/tests/generics/timports.nim b/tests/generics/timports.nim
new file mode 100644
index 000000000..e252ad194
--- /dev/null
+++ b/tests/generics/timports.nim
@@ -0,0 +1,63 @@
+discard """
+  output: '''
+317
+TEST2
+5 5 5
+false
+'''
+"""
+
+import mbind_bracket, mclosed_sym, mdotlookup, mmodule_same_as_proc, mtypenodes
+
+
+block tbind_bracket:
+  # bug #2599
+  # also test that `[]` can be passed now as a first class construct:
+
+  template takeBracket(x, a, i: untyped) =
+    echo x(a, i)
+
+  var a: array[10, int]
+  a[8] = 317
+
+  takeBracket(`[]`, a, 8)
+
+  let reg = newRegistry[UUIDObject]()
+  reg.register(UUIDObject())
+
+
+block tclosed_sym:
+  # bug #2664
+  proc same(r:R, d:int) = echo "TEST1"
+  doIt(Data[int](d:123), R())
+
+import strutils, unicode # ambiguous `strip`
+
+block tdotlookup:
+  foo(7)
+  # bug #1444
+  fn(4)
+  doAssert doStrip(123) == "123"
+  # bug #14254
+  doAssert baz2[float](1'i8) == 1
+  # bug #21883
+  proc abc[T: not not int](x: T): T =
+    var x = x
+    x.set("hello", "world")
+    result = x
+  doAssert abc(5) == 10
+  block: # ensure normal call is consistent with dot call 
+    proc T(x: int): float = x.float
+    proc foo[T](x: int) =
+      doAssert typeof(T(x)) is typeof(x.T)
+    foo[uint](123)
+
+block tmodule_same_as_proc:
+  # bug #1965
+  proc test[T](t: T) =
+    mmodule_same_as_proc"a"
+  test(0)
+
+block ttypenodes:
+  # issue #22699
+  doAssert chop[bool](42) == 42
diff --git a/tests/generics/tinferredgenericprocs.nim b/tests/generics/tinferredgenericprocs.nim
deleted file mode 100644
index 359c71ba8..000000000
--- a/tests/generics/tinferredgenericprocs.nim
+++ /dev/null
@@ -1,21 +0,0 @@
-discard """
-  output: '''123
-1
-2
-3'''
-"""
-
-import sequtils
-# https://github.com/Araq/Nim/issues/797
-proc foo[T](s:T):string = $s
-
-type IntStringProc = proc(x: int): string
-
-var f1 = IntStringProc(foo)
-var f2: proc(x: int): string = foo
-var f3: IntStringProc = foo
-
-echo f1(1), f2(2), f3(3)
-
-for x in map([1,2,3], foo): echo x
-
diff --git a/tests/generics/tinheritable_importcpp.nim b/tests/generics/tinheritable_importcpp.nim
new file mode 100644
index 000000000..8ee18b70b
--- /dev/null
+++ b/tests/generics/tinheritable_importcpp.nim
@@ -0,0 +1,10 @@
+discard """
+  targets: "cpp"
+  action: compile
+"""
+
+# #4651
+type
+  Vector[T] {.importcpp: "std::vector<'0 >", header: "vector", inheritable.} = object
+  VectorDerived {.importcpp: "SomeVectorDerived", nodecl.} = object of Vector[int]
+  # Error: inheritance only works with non-final objects
diff --git a/tests/generics/tlamba_in_generic.nim b/tests/generics/tlamba_in_generic.nim
deleted file mode 100644
index 91d417b5e..000000000
--- a/tests/generics/tlamba_in_generic.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-discard """
-  output: '''!!Hi!!'''
-"""
-# bug #4658
-import future
-
-var x = 123
-
-proc twice[T](f: T -> T): T -> T = (x: T) => f(f(x))
-
-proc quote(s: string): string = "!" & s & "!"
-
-echo twice(quote)("Hi")
diff --git a/tests/generics/tlateboundgenericparams.nim b/tests/generics/tlateboundgenericparams.nim
new file mode 100644
index 000000000..9f0580fd2
--- /dev/null
+++ b/tests/generics/tlateboundgenericparams.nim
@@ -0,0 +1,145 @@
+discard """
+  output: "1\n10\n1\n10"
+  nimout: '''
+bar instantiated with 1
+bar instantiated with 10
+'''
+"""
+
+import typetraits
+
+type
+  Foo = object
+
+proc defaultFoo: Foo = discard
+proc defaultInt: int = 1
+proc defaultTInt(T: type): int = 2
+proc defaultTFoo[T](x: typedesc[T]): Foo = discard
+proc defaultTOldSchool[T](x: typedesc[T]): T = discard
+proc defaultTModern(T: type): T = discard
+
+proc specializedDefault(T: type int): int = 10
+proc specializedDefault(T: type string): string = "default"
+
+converter intFromFoo(x: Foo): int = 3
+
+proc consumeInt(x: int) =
+  discard
+
+const activeTests = {1..100}
+
+when true:
+  template test(n, body) =
+    when n in activeTests:
+      block:
+        body
+
+  template reject(x) =
+    static: assert(not compiles(x))
+
+  test 1:
+    proc t[T](val: T = defaultInt()) =
+      consumeInt val
+
+    t[int]()
+    reject t[string]()
+
+  test 2:
+    proc t1[T](val: T = defaultFoo()) =
+      static:
+        assert type(val).name == "int"
+        assert T.name == "int"
+
+      consumeInt val
+
+    # here, the converter should kick in, but notice
+    # how `val` is still typed `int` inside the proc.
+    t1[int]()
+
+    proc t2[T](val: T = defaultFoo()) =
+      discard
+
+    reject t2[string]()
+
+  test 3:
+    proc tInt[T](val = defaultInt()): string =
+      return type(val).name
+
+    doAssert tInt[int]() == "int"
+    doAssert tInt[string]() == "int"
+
+    proc tInt2[T](val = defaultTInt(T)): string =
+      return type(val).name
+
+    doAssert tInt2[int]() == "int"
+    doAssert tInt2[string]() == "int"
+
+    proc tDefTModern[T](val = defaultTModern(T)): string =
+      return type(val).name
+
+    doAssert tDefTModern[int]() == "int"
+    doAssert tDefTModern[string]() == "string"
+    doAssert tDefTModern[Foo]() == "Foo"
+
+    proc tDefTOld[T](val = defaultTOldSchool(T)): string =
+      return type(val).name
+
+    doAssert tDefTOld[int]() == "int"
+    doAssert tDefTOld[string]() == "string"
+    doAssert tDefTOld[Foo]() == "Foo"
+
+  test 4:
+    proc t[T](val: T = defaultTFoo(T)): string =
+      return type(val).name
+
+    doAssert t[int]() == "int"
+    doAssert t[Foo]() == "Foo"
+    reject t[string]()
+
+  test 5:
+    proc t1[T](a: T = specializedDefault(T)): T =
+      return a
+
+    doAssert t1[int]() == 10
+    doAssert t1[string]() == "default"
+
+    proc t2[T](a: T, b = specializedDefault(T)): auto =
+      return $a & $b
+
+    doAssert t2(5) == "510"
+    doAssert t2("string ") == "string default"
+
+    proc t3[T](a: T, b = specializedDefault(type(a))): auto =
+      return $a & $b
+
+    doAssert t3(100) == "10010"
+    doAssert t3("another ") == "another default"
+
+  test 6:
+    # https://github.com/nim-lang/Nim/issues/5595
+    type
+      Point[T] = object
+        x, y: T
+
+    proc getOrigin[T](): Point[T] = Point[T](x: 0, y: 0)
+
+    proc rotate[T](point: Point[T], radians: float,
+                   origin = getOrigin[T]()): Point[T] =
+      discard
+
+    var p = getOrigin[float]()
+    var rotated = p.rotate(2.1)
+
+  test 7:
+    proc bar(x: static[int]) =
+      static: echo "bar instantiated with ", x
+      echo x
+
+    proc foo(x: static[int] = 1) =
+      bar(x)
+
+    foo()
+    foo(10)
+    foo(1)
+    foo(10)
+
diff --git a/tests/generics/tlateboundstatic.nim b/tests/generics/tlateboundstatic.nim
index f68f95f8d..90b44aa8e 100644
--- a/tests/generics/tlateboundstatic.nim
+++ b/tests/generics/tlateboundstatic.nim
@@ -1,5 +1,5 @@
 discard """
-  msg: "array[0..3, int]"
+  nimout: "array[0..3, int]"
 """
 
 type
diff --git a/tests/generics/tmacroinjectedsym.nim b/tests/generics/tmacroinjectedsym.nim
new file mode 100644
index 000000000..985e415f2
--- /dev/null
+++ b/tests/generics/tmacroinjectedsym.nim
@@ -0,0 +1,186 @@
+{.experimental: "openSym".}
+
+block: # issue #22605, normal call syntax
+  const error = "bad"
+
+  template valueOr(self: int, def: untyped): untyped =
+    case false
+    of true: ""
+    of false:
+      template error: untyped {.used, inject.} = "good"
+      def
+
+  proc g(T: type): string =
+    let x = valueOr 123:
+      return $error
+
+    "ok"
+
+  doAssert g(int) == "good"
+
+  proc g2(T: type): string =
+    bind error # use the bad version on purpose
+    let x = valueOr 123:
+      return $error
+
+    "ok"
+
+  doAssert g2(int) == "bad"
+
+block: # issue #22605, method call syntax
+  const error = "bad"
+
+  template valueOr(self: int, def: untyped): untyped =
+    case false
+    of true: ""
+    of false:
+      template error: untyped {.used, inject.} = "good"
+      def
+
+  proc g(T: type): string =
+    let x = 123.valueOr:
+      return $error
+
+    "ok"
+
+  doAssert g(int) == "good"
+
+  proc g2(T: type): string =
+    bind error # use the bad version on purpose
+    let x = 123.valueOr:
+      return $error
+
+    "ok"
+
+  doAssert g2(int) == "bad"
+
+block: # issue #22605, original complex example
+  type Xxx = enum
+    error
+    value
+
+  type
+    Result[T, E] = object
+      when T is void:
+        when E is void:
+          oResultPrivate*: bool
+        else:
+          case oResultPrivate*: bool
+          of false:
+            eResultPrivate*: E
+          of true:
+            discard
+      else:
+        when E is void:
+          case oResultPrivate*: bool
+          of false:
+            discard
+          of true:
+            vResultPrivate*: T
+        else:
+          case oResultPrivate*: bool
+          of false:
+            eResultPrivate*: E
+          of true:
+            vResultPrivate*: T
+
+  template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped =
+    let s = (self) # TODO avoid copy
+    case s.oResultPrivate
+    of true:
+      s.vResultPrivate
+    of false:
+      when E isnot void:
+        template error: untyped {.used, inject.} = s.eResultPrivate
+      def
+
+  proc f(): Result[int, cstring] =
+    Result[int, cstring](oResultPrivate: false, eResultPrivate: "f")
+
+  proc g(T: type): string =
+    let x = f().valueOr:
+      return $error
+
+    "ok"
+
+  doAssert g(int) == "f"
+
+  proc g2(T: type): string =
+    bind error # use the bad version on purpose
+    let x = f().valueOr:
+      return $error
+
+    "ok"
+
+  doAssert g2(int) == "error"
+
+block: # issue #23865
+  type Xxx = enum
+    error
+    value
+
+  type
+    Result[T, E] = object
+      when T is void:
+        when E is void:
+          oResultPrivate: bool
+        else:
+          case oResultPrivate: bool
+          of false:
+            eResultPrivate: E
+          of true:
+            discard
+      else:
+        when E is void:
+          case oResultPrivate: bool
+          of false:
+            discard
+          of true:
+            vResultPrivate: T
+        else:
+          case oResultPrivate: bool
+          of false:
+            eResultPrivate: E
+          of true:
+            vResultPrivate: T
+
+  func error[T, E](self: Result[T, E]): E =
+    ## Fetch error of result if set, or raise Defect
+    case self.oResultPrivate
+    of true:
+      when T isnot void:
+        raiseResultDefect("Trying to access error when value is set", self.vResultPrivate)
+      else:
+        raiseResultDefect("Trying to access error when value is set")
+    of false:
+      when E isnot void:
+        self.eResultPrivate
+
+  template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped =
+    let s = (self) # TODO avoid copy
+    case s.oResultPrivate
+    of true:
+      s.vResultPrivate
+    of false:
+      when E isnot void:
+        template error: untyped {.used, inject.} = s.eResultPrivate
+      def
+  proc f(): Result[int, cstring] =
+    Result[int, cstring](oResultPrivate: false, eResultPrivate: "f")
+  proc g(T: type): string =
+    let x = f().valueOr:
+      return $error
+    "ok"
+  doAssert g(int) == "f"
+
+import sequtils
+
+block: # issue #12283
+  var b = 5
+  type Foo[T] = object
+    h, w: int
+  proc bar[T](foos: seq[Foo[T]]): T =
+    let w = foldl(foos, a + b.w, 0)
+    w
+  let foos = @[Foo[int](h: 3, w: 5), Foo[int](h: 4, w: 6)]
+  doAssert bar(foos) == 11
diff --git a/tests/generics/tmacroinjectedsymwarning.nim b/tests/generics/tmacroinjectedsymwarning.nim
new file mode 100644
index 000000000..77119004b
--- /dev/null
+++ b/tests/generics/tmacroinjectedsymwarning.nim
@@ -0,0 +1,59 @@
+discard """
+  matrix: "--skipParentCfg --filenames:legacyRelProj"
+"""
+
+type Xxx = enum
+  error
+  value
+
+type
+  Result[T, E] = object
+    when T is void:
+      when E is void:
+        oResultPrivate*: bool
+      else:
+        case oResultPrivate*: bool
+        of false:
+          eResultPrivate*: E
+        of true:
+          discard
+    else:
+      when E is void:
+        case oResultPrivate*: bool
+        of false:
+          discard
+        of true:
+          vResultPrivate*: T
+      else:
+        case oResultPrivate*: bool
+        of false:
+          eResultPrivate*: E
+        of true:
+          vResultPrivate*: T
+
+template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped =
+  let s = (self) # TODO avoid copy
+  case s.oResultPrivate
+  of true:
+    s.vResultPrivate
+  of false:
+    when E isnot void:
+      template error: untyped {.used, inject.} = s.eResultPrivate
+    def
+
+proc f(): Result[int, cstring] =
+  Result[int, cstring](oResultPrivate: false, eResultPrivate: "f")
+
+proc g(T: type): string =
+  let x = f().valueOr:
+    {.push warningAsError[IgnoredSymbolInjection]: on.}
+    # test spurious error
+    discard true
+    let _ = f
+    {.pop.}
+    return $error #[tt.Warning
+            ^ a new symbol 'error' has been injected during template or generic instantiation, however 'error' [enumField declared in tmacroinjectedsymwarning.nim(6, 3)] captured at the proc declaration will be used instead; either enable --experimental:openSym to use the injected symbol, or `bind` this captured symbol explicitly [IgnoredSymbolInjection]]#
+
+  "ok"
+
+discard g(int)
diff --git a/tests/generics/tmap_auto.nim b/tests/generics/tmap_auto.nim
deleted file mode 100644
index 572556722..000000000
--- a/tests/generics/tmap_auto.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-import future, sequtils
-
-let x = map(@[1, 2, 3], x => x+10)
-assert x == @[11, 12, 13]
-
-let y = map(@[(1,"a"), (2,"b"), (3,"c")], x => $x[0] & x[1])
-assert y == @["1a", "2b", "3c"]
-
-proc eatsTwoArgProc[T,S,U](a: T, b: S, f: proc(t: T, s: S): U): U =
-  f(a,b)
-
-let z = eatsTwoArgProc(1, "a", (t,s) => $t & s)
-assert z == "1a"
diff --git a/tests/generics/tmetafield.nim b/tests/generics/tmetafield.nim
index 7a2375abe..cf30a936d 100644
--- a/tests/generics/tmetafield.nim
+++ b/tests/generics/tmetafield.nim
@@ -1,10 +1,23 @@
 discard """
   cmd: "nim check $options $file"
-  errormsg: "'proc' is not a concrete type"
-  errormsg: "'Foo' is not a concrete type."
-  errormsg: "invalid type: 'proc' in this context: 'TBaseMed'"
+  action: "reject"
+  nimout: '''
+tmetafield.nim(26, 5) Error: 'proc' is not a concrete type; for a callback without parameters use 'proc()'
+tmetafield.nim(27, 5) Error: 'Foo' is not a concrete type
+tmetafield.nim(29, 5) Error: invalid type: 'proc' in this context: 'TBaseMed' for var
+'''
 """
 
+# bug #188
+
+
+
+
+
+
+
+
+# line 20
 type
   Foo[T] = object
     x: T
@@ -15,4 +28,3 @@ type
 
 var a: TBaseMed
 
-# issue 188
diff --git a/tests/generics/tmodule_same_as_proc.nim b/tests/generics/tmodule_same_as_proc.nim
deleted file mode 100644
index 113ca1bc3..000000000
--- a/tests/generics/tmodule_same_as_proc.nim
+++ /dev/null
@@ -1,9 +0,0 @@
-
-# bug #1965
-
-import mmodule_same_as_proc
-
-proc test[T](t: T) =
-  mmodule_same_as_proc"a"
-
-test(0)
diff --git a/tests/generics/tnestedissues.nim b/tests/generics/tnestedissues.nim
new file mode 100644
index 000000000..e96a1927e
--- /dev/null
+++ b/tests/generics/tnestedissues.nim
@@ -0,0 +1,24 @@
+block: # issue #23568
+  type G[T] = object
+    j: T
+  proc s[T](u: int) = discard
+  proc s[T]() = discard
+  proc c(e: int | int): G[G[G[int]]] = s[G[G[int]]]()
+  discard c(0)
+
+import std/options
+
+block: # issue #23310
+  type
+    BID = string or uint64
+    Future[T] = ref object of RootObj
+      internalValue: T
+    InternalRaisesFuture[T] = ref object of Future[T]
+  proc newInternalRaisesFutureImpl[T](): InternalRaisesFuture[T] =
+    let fut = InternalRaisesFuture[T]()
+  template newFuture[T](): auto =
+    newInternalRaisesFutureImpl[T]()
+  proc problematic(blockId: BID): Future[Option[seq[int]]] =
+    let resultFuture = newFuture[Option[seq[int]]]()
+    return resultFuture
+  let x = problematic("latest")
diff --git a/tests/generics/tnestedtemplate.nim b/tests/generics/tnestedtemplate.nim
new file mode 100644
index 000000000..22d0a2d3c
--- /dev/null
+++ b/tests/generics/tnestedtemplate.nim
@@ -0,0 +1,9 @@
+block: # issue #13979
+  var s: seq[int]
+  proc filterScanline[T](input: openArray[T]) =
+    template currPix: untyped = input[i]
+    for i in 0..<input.len:
+      s.add currPix
+  let pix = [1, 2, 3]
+  filterScanline(pix)
+  doAssert s == @[1, 2, 3]
diff --git a/tests/generics/tnullary_generics.nim b/tests/generics/tnullary_generics.nim
new file mode 100644
index 000000000..c79558ee3
--- /dev/null
+++ b/tests/generics/tnullary_generics.nim
@@ -0,0 +1,26 @@
+discard """
+  nimout: '''
+hah
+hey
+hey
+hah
+'''
+"""
+
+# non-generic
+proc foo(s: string) =
+  static: echo "hah"
+  echo s
+
+static: echo "hey"
+
+foo("hoo")
+
+# nullary generic
+proc bar[](s: string) =
+  static: echo "hah"
+  echo s
+
+static: echo "hey"
+
+bar("hoo")
diff --git a/tests/generics/tobjecttyperel.nim b/tests/generics/tobjecttyperel.nim
index 8c8f90098..6f223c154 100644
--- a/tests/generics/tobjecttyperel.nim
+++ b/tests/generics/tobjecttyperel.nim
@@ -1,26 +1,28 @@
 discard """
+  matrix: "-d:nimInternalNonVtablesTesting"
   output: '''(peel: 0, color: 15)
 (color: 15)
 17
-(width: 0.0, taste: nil, color: 13)
-(width: 0.0, taste: nil, color: 15)
-cool'''
+(width: 0.0, taste: "", color: 13)
+(width: 0.0, taste: "", color: 15)
+cool
+test'''
 """
 
 # bug #5241
 type
   BaseFruit[T] = object of RootObj
     color: T
-    
+
   MidLevel[T] = object of BaseFruit[T]
-  
+
   Mango = object of MidLevel[int]
     peel: int
-    
+
   Peach[X, T, Y] = object of T
     width: X
     taste: Y
-    
+
 proc setColor[T](self: var BaseFruit[T]) =
   self.color = 15
 
@@ -62,4 +64,26 @@ method m[T](o: Foo[T]) = echo "cool"
 
 var v: Bar
 v.new()
-v.m() # Abstract method not called anymore
\ No newline at end of file
+v.m() # Abstract method not called anymore
+
+
+# bug #88
+
+type
+  TGen[T] = object of RootObj
+    field: T
+
+  TDerived[T] = object of TGen[T]
+    nextField: T
+
+proc doSomething[T](x: ref TGen[T]) =
+  type
+    Ty = ref TDerived[T]
+  echo Ty(x).nextField
+
+var
+  x: ref TDerived[string]
+new(x)
+x.nextField = "test"
+
+doSomething(x)
diff --git a/tests/generics/tobjecttyperel3.nim b/tests/generics/tobjecttyperel3.nim
deleted file mode 100644
index 3d8079e28..000000000
--- a/tests/generics/tobjecttyperel3.nim
+++ /dev/null
@@ -1,12 +0,0 @@
-discard """
-  output: '''OK'''
-"""
-#bug #5632
-type
-  Option*[T] = object
-  
-proc point*[A](v: A, t: typedesc[Option[A]]): Option[A] =
-  discard
-  
-discard point(1, Option)
-echo "OK"
\ No newline at end of file
diff --git a/tests/generics/topensymimport.nim b/tests/generics/topensymimport.nim
new file mode 100644
index 000000000..a47496827
--- /dev/null
+++ b/tests/generics/topensymimport.nim
@@ -0,0 +1,5 @@
+# issue #23386
+
+import mopensymimport2
+
+doAssert g(int) == "f"
diff --git a/tests/generics/toverloading_typedesc.nim b/tests/generics/toverloading_typedesc.nim
index 94f4d860d..4d748bfee 100644
--- a/tests/generics/toverloading_typedesc.nim
+++ b/tests/generics/toverloading_typedesc.nim
@@ -1,7 +1,3 @@
-discard """
-  exitcode: 0
-  disabled: '''true'''
-"""
 import moverloading_typedesc
 import tables
 
@@ -9,8 +5,7 @@ type
   LFoo = object
   LBar = object
 
-
-when isMainModule:
+when true:
   doAssert FBar.new() == 3
 
   proc new(_: typedesc[LFoo]): int = 0
diff --git a/tests/generics/tparam_binding.nim b/tests/generics/tparam_binding.nim
index 55acb8f06..fa7558613 100644
--- a/tests/generics/tparam_binding.nim
+++ b/tests/generics/tparam_binding.nim
@@ -1,10 +1,11 @@
 discard """
+  matrix: "--mm:arc; --mm:refc"
   errormsg: "got <ref Matrix[2, 2, system.float], ref Matrix[2, 1, system.float]>"
-  line: 27
+  line: 28
 """
 
 type
-  Matrix[M,N: static[int]; T: SomeReal] = distinct array[0..(M*N - 1), T]
+  Matrix[M,N: static[int]; T: SomeFloat] = distinct array[0..(M*N - 1), T]
 
 let a = new Matrix[2,2,float]
 let b = new Matrix[2,1,float]
diff --git a/tests/generics/tparser_generator.nim b/tests/generics/tparser_generator.nim
new file mode 100644
index 000000000..ac921c0e5
--- /dev/null
+++ b/tests/generics/tparser_generator.nim
@@ -0,0 +1,415 @@
+discard """
+  output: '''Match failed: spam
+Match failed: ham'''
+joinable: false
+"""
+
+# bug #6220
+
+import nre
+import options
+import strutils except isAlpha, isLower, isUpper, isSpace
+from unicode import isAlpha, isLower, isUpper, isTitle, isWhiteSpace
+import os
+
+const debugLex = false
+
+template debug(enable: bool, text: string): typed =
+  when enable:
+    echo(text)
+
+type
+  Parser[N, T] = proc(text: T, start: int, nodes: var seq[Node[N]]): int {.closure.}
+
+  RuleObj[N, T] = object
+    parser: Parser[N, T]
+    kind: N
+
+  Rule[N, T] = ref RuleObj[N, T]
+
+  NodeKind = enum
+    terminal,
+    nonterminal
+
+  Node*[N] = object of RootObj
+    # Uncomment the following lines and the compiler crashes
+    # case nodeKind: NodeKind
+    #   of nonterminal:
+    #     kids: Node[N]
+    #   of terminal:
+    #     discard
+    start*: int
+    length*: int
+    kind*: N
+
+
+  NonTerminal[N] = object of Node
+    children: seq[Node[N]]
+
+proc newRule[N, T](parser: Parser, kind: N): Rule[N, T] =
+  new(result)
+  result.parser = parser
+  result.kind = kind
+
+proc newRule[N, T](kind: N): Rule[N, T] =
+  new(result)
+  result.kind = kind
+
+proc initNode[N](start: int, length: int, kind: N): Node[N] =
+  result.start = start
+  result.length = length
+  result.kind = kind
+
+proc initNode[N](start: int, length: int, children: seq[Node[N]], kind: N): NonTerminal[N] =
+  result.start = start
+  result.length = length
+  result.kind = kind
+  result.children = children
+
+proc substr[T](text: T, first, last: int): T =
+  text[first .. last]
+
+proc continuesWith[N](text: seq[Node[N]], subtext: seq[N], start: Natural): bool =
+  let length = len(text)
+  var pos = 0
+  while pos < len(subtext):
+    let textpos = start + pos
+    if textpos == len(text):
+      return false
+    if text[textpos].kind != subtext[pos].kind:
+      return false
+    pos+=1
+  return true
+
+
+proc render*[N, T](text: T, nodes: seq[Node[N]]): string =
+  ## Uses a sequence of Nodes to render a given text string
+  result = ""
+  for node in nodes:
+    result.add("<" & node.value(text) & ">")
+
+proc render*[N, T](rule: Rule[N, T], text: string): string =
+  ## Uses a rule to render a given text string
+  render(text, rule.parse(text))
+
+proc render*[N, T](text: T, nodes: seq[Node[N]], source: string): string =
+  result = ""
+  for node in nodes:
+    result.add("[" & node.value(text, source) & "]")
+
+proc render*[N, T, X](rule: Rule[N, T], text: seq[Node[X]], source: string): string =
+  ## Uses a rule to render a given series of nodes, providing the source string
+  text.render(rule.parse(text, source = source), source)
+
+proc annotate*[N, T](node: Node[N], text: T): string =
+  result = "<" & node.value(text) & ":" & $node.kind & ">"
+
+proc annotate*[N, T](nodes: seq[Node[N]], text: T): string =
+  result = ""
+  for node in nodes:
+    result.add(node.annotate(text))
+
+proc annotate*[N, T](rule: Rule[N, T], text: T): string =
+  annotate(rule.parse(text), text)
+
+proc value*[N, T](node: Node[N], text: T): string =
+  result = $text.substr(node.start, node.start + node.length - 1)
+
+proc value*[N, X](node: Node[N], text: seq[Node[X]], source: string): string =
+  result = ""
+  for n in node.start ..< node.start + node.length:
+    result &= text[n].annotate(source)
+
+proc parse*[N, T](rule: Rule[N, T], text: T, start = 0, source: string = ""): seq[Node[N]] =
+  result = newSeq[Node[N]]()
+  debug(debugLex, "Parsing: " & $text)
+  let length = rule.parser(text, start, result)
+
+  when T is string:
+    if length == -1:
+      echo("Match failed: " & $text)
+      result = @[]
+    elif length == len(text):
+      debug(debugLex, "Matched: " & $text & " => " & $len(result) & " tokens: " & text.render(result))
+    else:
+      echo("Matched first " & $length & " symbols: " & $text & " => " & $len(result) & " tokens: " & text.render(result))
+  else:
+    if length == -1:
+      echo("Match failed: " & $text)
+      result = @[]
+    elif length == len(text):
+      debug(debugLex, "Matched: " & $text & " => " & $len(result) & " tokens: " & text.render(result, source))
+    else:
+      echo("Matched first " & $length & " symbols: " & $text & " => " & $len(result) & " tokens: " & text.render(result, source))
+
+
+proc literal*[N, T, P](pattern: P, kind: N): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    if start == len(text):
+      return -1
+    doAssert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)])
+    when P is string or P is seq[N]:
+      debug(debugLex, "Literal[" & $kind & "]: testing " & $pattern & " at " & $start & ": " & $text[start..start+len(pattern)-1])
+      if text.continuesWith(pattern, start):
+        let node = initNode(start, len(pattern), kind)
+        nodes.add(node)
+        debug(debugLex, "Literal: matched <" & $text[start ..< start+node.length] & ":" & $node.length & ">" )
+        return node.length
+    elif P is char:
+      debug(debugLex, "Literal[" & $kind & "]: testing " & $pattern & " at " & $start & ": " & $text[start])
+      if text[start] == pattern:
+        let node = initNode(start, 1, kind)
+        nodes.add(node)
+        return 1
+    else:
+      debug(debugLex, "Literal[" & $kind & "]: testing " & $pattern & " at " & $start & ": " & $text[start])
+      if text[start].kind == pattern:
+        let node = initNode(start, 1, kind)
+        nodes.add(node)
+        return 1
+    return -1
+  result = newRule[N, T](parser, kind)
+
+proc token[N, T](pattern: T, kind: N): Rule[N, T] =
+  when T is not string:
+     {.fatal: "Token is only supported for strings".}
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    debug(debugLex, "Token[" & $kind & "]: testing " & pattern & " at " & $start)
+    if start == len(text):
+      return -1
+    doAssert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)])
+    let m = text.match(re(pattern), start)
+    if m.isSome:
+      let node = initNode(start, len(m.get.match), kind)
+      nodes.add(node)
+      result = node.length
+      debug(debugLex, "Token: matched <" & text[start ..< start+node.length] & ":" & $node.length & ">" )
+    else:
+      result = -1
+  result = newRule[N, T](parser, kind)
+
+proc chartest[N, T, S](testfunc: proc(s: S): bool, kind: N): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    if start == len(text):
+      return -1
+    doAssert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)])
+    if testfunc(text[start]):
+      nodes.add(initNode(start, 1, kind))
+      result = 1
+    else:
+      result = -1
+  result = newRule[N, T](parser, kind)
+
+proc any*[N, T, S](symbols: T, kind: N): Rule[N, T] =
+  let test = proc(s: S): bool =
+    when S is string:
+      debug(debugLex, "Any[" & $kind & "]: testing for " & symbols.replace("\n", "\\n").replace("\r", "\\r"))
+    else:
+      debug(debugLex, "Any[" & $kind & "]: testing for " & $symbols)
+    result = s in symbols
+  result = chartest[N, T, S](test, kind)
+
+proc ignore*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    result = rule.parser(text, start, mynodes)
+  result = newRule[N, T](parser, rule.kind)
+
+proc combine*[N, T](rule: Rule[N, T], kind: N): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    result = rule.parser(text, start, mynodes)
+    nodes.add(initNode(start, result, kind))
+  result = newRule[N, T](parser, kind)
+
+proc build*[N, T](rule: Rule[N, T], kind: N): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    result = rule.parser(text, start, mynodes)
+    let nonTerminal = initNode(start, result, mynodes, kind)
+    nodes.add(nonTerminal)
+  result = newRule[N, T](parser, kind)
+
+proc fail*[N, T](message: string, kind: N): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    let lineno = countLines(text[0..start])
+    var startline = start
+    var endline = start
+    while startline>0:
+      if text[startline] in NewLines:
+        break
+      startline-=1
+    while endline < len(text):
+      if text[endline] in NewLines:
+        break
+      endline+=1
+    let charno = start-startline
+    echo text.substr(startline, endline)
+    echo ' '.repeat(max(charno,0)) & '^'
+    raise newException(ValueError, "Position: " & $start & " Line: " & $lineno & ", Symbol: " & $charno & ": " & message)
+  result = newRule[N, T](parser, kind)
+
+proc `+`*[N, T](left: Rule[N, T], right: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    doAssert(not isNil(left.parser), "Left hand side parser is nil")
+    let leftlength = left.parser(text, start, mynodes)
+    if leftlength == -1:
+      return leftlength
+    doAssert(not isNil(right.parser), "Right hand side parser is nil")
+    let rightlength = right.parser(text, start+leftlength, mynodes)
+    if rightlength == -1:
+      return rightlength
+    result = leftlength + rightlength
+    nodes.add(mynodes)
+  result = newRule[N, T](parser, left.kind)
+
+proc `/`*[N, T](left: Rule[N, T], right: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    doAssert(not isNil(left.parser), "Left hand side of / is not fully defined")
+    let leftlength = left.parser(text, start, mynodes)
+    if leftlength != -1:
+      nodes.add(mynodes)
+      return leftlength
+    mynodes = newSeq[Node[N]]()
+    doAssert(not isNil(right.parser), "Right hand side of / is not fully defined")
+    let rightlength = right.parser(text, start, mynodes)
+    if rightlength == -1:
+      return rightlength
+    nodes.add(mynodes)
+    return rightlength
+  result = newRule[N, T](parser, left.kind)
+
+proc `?`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    let success = rule.parser(text, start, nodes)
+    return if success != -1: success else: 0
+  result = newRule[N, T](parser, rule.kind)
+
+proc `+`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var success = rule.parser(text, start, nodes)
+    if success == -1:
+      return success
+    var total = 0
+    while success != -1 and start+total < len(text):
+      total += success
+      success = rule.parser(text, start+total, nodes)
+    return total
+  result = newRule[N, T](parser, rule.kind)
+
+proc `*`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    let success = (+rule).parser(text, start, nodes)
+    return if success != -1: success else: 0
+  result = newRule[N, T](parser, rule.kind)
+
+#Note: this consumes - for zero-width lookahead see !
+proc `^`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    let success = rule.parser(text, start, mynodes)
+    return if success == -1: 1 else: -1
+  result = newRule[N, T](parser, rule.kind)
+
+proc `*`*[N, T](repetitions: int, rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    var total = 0
+    for i in 0..<repetitions:
+      let success = rule.parser(text, start+total, mynodes)
+      if success == -1:
+        return success
+      else:
+        total += success
+    nodes.add(mynodes)
+    return total
+  result = newRule[N, T](parser, rule.kind)
+
+# Positive zero-width lookahead
+proc `&`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    let success = rule.parser(text, start, mynodes)
+    return if success != -1: 0 else: -1
+  result = newRule[N, T](parser, rule.kind)
+
+# Negative zero-width lookahead
+proc `!`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    let failure = rule.parser(text, start, mynodes)
+    return if failure == -1: 0 else: -1
+  result = newRule[N, T](parser, rule.kind)
+
+proc `/`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    var length = 0
+    var success = rule.parser(text, start+length, mynodes)
+    while success == -1 and start+length < len(text):
+      length += 1
+      success = rule.parser(text, start+length, mynodes)
+    if start+length >= len(text):
+      result = -1
+    else:
+      nodes.add(initNode(start, length, rule.kind))
+      nodes.add(mynodes)
+      result = length + success
+  result = newRule[N, T](parser, rule.kind)
+
+proc `->`*(rule: Rule, production: Rule) =
+  doAssert(not isnil(production.parser), "Right hand side of -> is nil - has the rule been defined yet?")
+  rule.parser = production.parser
+
+template grammar*[K](Kind, Text, Symbol: typedesc; default: K, code: untyped): typed {.hint[XDeclaredButNotUsed]: off.} =
+
+    proc newRule(): Rule[Kind, Text] {.inject.} = newRule[Kind, Text](default)
+    proc chartest(testfunc: proc(c: Symbol): bool): Rule[Kind, Text] {.inject.} = chartest[Kind, Text, Symbol](testfunc, default)
+    proc literal[P](pattern: P, kind: K): Rule[Kind, Text] {.inject.} = literal[Kind, Text, P](pattern, kind)
+    proc literal[P](pattern: P): Rule[Kind, Text] {.inject.} = literal[Kind, Text, P](pattern, default)
+
+    when Text is string:
+      proc token(pattern: string): Rule[Kind, Text] {.inject.} = token(pattern, default)
+      proc fail(message: string): Rule[Kind, Text] {.inject.} = fail[Kind, Text](message, default)
+      let alpha {.inject.} = chartest[Kind, Text, Symbol](isAlphaAscii, default)
+      let alphanumeric {.inject.}= chartest[Kind, Text, Symbol](isAlphaNumeric, default)
+      let digit {.inject.} = chartest[Kind, Text, Symbol](isDigit, default)
+      let lower {.inject.} = chartest[Kind, Text, Symbol](isLowerAscii, default)
+      let upper {.inject.} = chartest[Kind, Text, Symbol](isUpperAscii, default)
+      let isspace = proc (x: char): bool = x.isSpaceAscii and not (x in NewLines)
+      let space {.inject.} = chartest[Kind, Text, Symbol](isspace, default)
+      let isnewline = proc (x: char): bool = x in NewLines
+      let newline {.inject.} = chartest[Kind, Text, Symbol](isnewline, default)
+      let alphas {.inject.} = combine(+alpha, default)
+      let alphanumerics {.inject.} = combine(+alphanumeric, default)
+      let digits {.inject.} = combine(+digit, default)
+      let lowers {.inject.} = combine(+lower, default)
+      let uppers {.inject.} = combine(+upper, default)
+      let spaces {.inject.} = combine(+space, default)
+      let newlines {.inject.} = combine(+newline, default)
+
+    proc any(chars: Text): Rule[Kind, Text] {.inject.} = any[Kind, Text, Symbol](chars, default)
+    proc combine(rule: Rule[Kind, Text]): Rule[Kind, Text] {.inject.} = combine[Kind, Text](rule, default)
+
+    code
+
+template grammar*[K](Kind: typedesc; default: K, code: untyped): typed {.hint[XDeclaredButNotUsed]: off.} =
+  grammar(Kind, string, char, default, code)
+
+block:
+  type DummyKind = enum dkDefault
+  grammar(DummyKind, string, char, dkDefault):
+    let rule = token("h[a]+m") + ignore(token(r"\s+")) + (literal("eggs") / literal("beans"))
+    var text = "ham beans"
+    discard rule.parse(text)
+
+    var recursive = newRule()
+    recursive -> (literal("(") + recursive + literal(")")) / token(r"\d+")
+    for test in ["spam", "57", "(25)", "((25))"]:
+      discard recursive.parse(test)
+
+    let repeated = +literal("spam") + ?literal("ham") + *literal("salami")
+    for test in ["ham", "spam", "spamspamspam" , "spamham", "spamsalami", "spamsalamisalami"]:
+      discard  repeated.parse(test)
diff --git a/tests/generics/tpointerprocs.nim b/tests/generics/tpointerprocs.nim
new file mode 100644
index 000000000..2bcaf15b3
--- /dev/null
+++ b/tests/generics/tpointerprocs.nim
@@ -0,0 +1,28 @@
+discard """
+cmd: "nim check $options --hints:off $file"
+action: "reject"
+nimout:'''
+tpointerprocs.nim(15, 11) Error: 'foo' doesn't have a concrete type, due to unspecified generic parameters.
+tpointerprocs.nim(27, 11) Error: cannot instantiate: 'foo[int]'; got 1 typeof(s) but expected 2
+tpointerprocs.nim(27, 14) Error: expression 'foo[int]' has no type (or is ambiguous)
+tpointerprocs.nim(28, 11) Error: expression 'bar' has no type (or is ambiguous)
+'''
+"""
+
+block:
+  proc foo(x: int | float): float = result = 1.0
+  let
+    bar = foo
+    baz = bar
+
+block:
+  proc foo(x: int | float): float = result = 1.0
+  let
+    bar = foo[int]
+    baz = bar
+
+block:
+  proc foo(x: int | float, y: int or string): float = result = 1.0
+  let
+    bar = foo[int]
+    baz = bar
\ No newline at end of file
diff --git a/tests/generics/tprevent_double_bind.nim b/tests/generics/tprevent_double_bind.nim
new file mode 100644
index 000000000..d8fc6e5d3
--- /dev/null
+++ b/tests/generics/tprevent_double_bind.nim
@@ -0,0 +1,21 @@
+discard """
+  errormsg: "type mismatch: got <TT[seq[string]], proc (v: int){.gcsafe.}>"
+  line: 20
+"""
+
+# bug #6732
+import typetraits
+
+type
+  TT[T] = ref object of RootObj
+    val: T
+  CB[T] = proc (v: T)
+
+proc testGeneric[T](val: TT[T], cb: CB[T]) =
+  echo val.type.name
+  echo $val.val
+
+var tt = new(TT[seq[string]])
+echo tt.type.name
+tt.testGeneric( proc (v: int) =
+    echo $v )
diff --git a/tests/generics/tproctypecache_falsepositive.nim b/tests/generics/tproctypecache_falsepositive.nim
deleted file mode 100644
index 4f24a1fc8..000000000
--- a/tests/generics/tproctypecache_falsepositive.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-
-import asyncdispatch
-
-type
-  Callback = proc() {.closure, gcsafe.}
-  GameState = ref object
-    playerChangeHandlers: seq[Callback]
-
-#proc dummy() =
-#  var x = newSeq[proc() {.cdecl, gcsafe.}]()
-
-proc newGameState(): GameState =
-  result = GameState(
-    playerChangeHandlers: newSeq[Callback]() # this fails
-  )
-
-#dummy()
diff --git a/tests/generics/tptrinheritance.nim b/tests/generics/tptrinheritance.nim
deleted file mode 100644
index 221b8777b..000000000
--- a/tests/generics/tptrinheritance.nim
+++ /dev/null
@@ -1,20 +0,0 @@
-type NSPasteboardItem* = ptr object
-type NSPasteboard* = ptr object
-type NSArrayAbstract = ptr object {.inheritable.}
-type NSMutableArrayAbstract = ptr object of NSArrayAbstract
-type NSArray*[T] = ptr object of NSArrayAbstract
-type NSMutableArray*[T] = ptr object of NSArray[T]
-
-proc newMutableArrayAbstract*(): NSMutableArrayAbstract = discard
-
-template newMutableArray*(T: typedesc): NSMutableArray[T] =
-  cast[NSMutableArray[T]](newMutableArrayAbstract())
-
-proc writeObjects*(p: NSPasteboard, o: NSArray[NSPasteboardItem]) = discard
-
-let a = newMutableArray NSPasteboardItem
-var x: NSMutableArray[NSPasteboardItem]
-var y: NSArray[NSPasteboardItem] = x
-
-writeObjects(nil, a)
-
diff --git a/tests/generics/trecursivegenerics.nim b/tests/generics/trecursivegenerics.nim
new file mode 100644
index 000000000..1b152b063
--- /dev/null
+++ b/tests/generics/trecursivegenerics.nim
@@ -0,0 +1,96 @@
+block: # Replicates #18728
+  type
+    FlipFlop[A, B] = ref object
+      val: A
+      next: FlipFlop[B, A]
+  
+    Trinary[A, B, C] = ref object
+      next: Trinary[B, C, A]
+  
+  assert typeof(FlipFlop[int, string]().next) is FlipFlop[string, int]
+  assert typeof(FlipFlop[string, int]().next) is FlipFlop[int, string]
+  assert typeof(Trinary[int, float, string]().next) is Trinary[float, string, int]
+  assert typeof(Trinary[int, float, string]().next.next) is Trinary[string, int, float]
+  var a = FlipFlop[int, string](val: 100, next: FlipFlop[string, int](val: "Hello"))
+  assert a.val == 100
+  assert a.next.val == "Hello"
+
+block: # 18838
+  type
+    DoublyLinkedNodeObj[T] = object
+      value: T
+
+    DoublyLinkedNode[T] = ref DoublyLinkedNodeObj[T]
+
+    Item[T] = ref object
+      link: DoublyLinkedNode[Item[T]]
+
+    Box = object
+
+  proc newDoublyLinkedNode[T](value: T): DoublyLinkedNode[T] =
+    new(result)
+    result.value = value 
+
+  let link = newDoublyLinkedNode(Item[Box]())
+
+import lists
+block:
+  type
+    Box = object
+    Item[T] = ref object
+      link:DoublyLinkedNode[ Item[T] ]
+
+    ItemSimple = ref object
+      link:DoublyLinkedNode[ ItemSimple ]
+
+  let link = newDoublyLinkedNode( Item[Box]() )
+
+block: #18897
+  type
+    SkipListObj[T] = object
+      over: SkipList[T]
+      down: SkipList[T]
+      value: T
+
+    SkipList[T] = ref SkipListObj[T]
+
+    GraphObj[N, E; F: static[int]] = object
+      nodes: SkipList[Node[N, E]]
+
+    Graph[N, E; F: static[int]] = ref GraphObj[N, E, F]
+
+    Node[N, E] = ref NodeObj[N, E]
+
+    NodeObj[N, E] = object
+      value: N
+      incoming: SkipList[Edge[N, E]]
+      outgoing: SkipList[Edge[N, E]]
+
+    Edge[N, E] = ref EdgeObj[N, E]
+
+    EdgeObj[N, E] = object
+      value: E
+      id: int
+      source: Node[N, E]
+      target: Node[N, E]
+
+    EdgeResult[N, E] = tuple
+      source: Node[N, E]
+      edge: Edge[N, E]
+      target: Node[N, E]
+
+  proc newSkipList[T](value: T): SkipList[T] =
+    static: echo T, " ", typeof(result.value)
+    result = SkipList[T](value: value)
+
+  proc toSkipList[T](values: openArray[T] = @[]): SkipList[T] =
+    for item in items(values):
+      if result.isNil:
+        result = newSkipList(item)
+
+  proc newContainer[N, E, F](graph: Graph[N, E, F]; form: typedesc): auto =
+    result = toSkipList[form]([])
+
+  var
+    result = Graph[int, string, 0]()
+  result.nodes = result.newContainer(Node[int, string])
\ No newline at end of file
diff --git a/tests/generics/treentranttypes.nim b/tests/generics/treentranttypes.nim
index 2ef049ce2..801f0e444 100644
--- a/tests/generics/treentranttypes.nim
+++ b/tests/generics/treentranttypes.nim
@@ -1,22 +1,14 @@
 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]]
-
 2x3 Literal [[0.0, 2.0, 3.0], [2.0, 0.0, 5.0]]
-
 2x3 Matrix [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
-
 2x2 ArrayArray[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
-
 2x3 ArrayVector[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
-
 2x3 VectorVector [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
-
 2x3 VectorArray [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
-
 @[1, 2]
 @[1, 2]
 @[1, 2]@[3, 4]
@@ -109,3 +101,14 @@ echo @(b.arr[0].arr), @(b.arr[1].arr)
 let y = b
 echo @(y.arr[0].arr), @(y.arr[1].arr)
 
+import macros
+
+block: # issue #5121
+  type
+    A = object
+    AConst[X] = A
+
+  macro dumpType(t: typedesc): untyped =
+    result = newTree(nnkTupleConstr, newLit $t.getType[1].typeKind, newLit t.getType[1].treeRepr)
+
+  doAssert dumpType(A) == ("ntyObject", "Sym \"A\"")
diff --git a/tests/generics/treturn_inference.nim b/tests/generics/treturn_inference.nim
new file mode 100644
index 000000000..331a9d4db
--- /dev/null
+++ b/tests/generics/treturn_inference.nim
@@ -0,0 +1,184 @@
+
+{.experimental: "inferGenericTypes".}
+
+import std/tables
+
+block:
+  type
+    MyOption[T, Z] = object
+      x: T
+      y: Z
+
+  proc none[T, Z](): MyOption[T, Z] =
+    when T is int:
+      result.x = 22
+    when Z is float:
+      result.y = 12.0
+
+  proc myGenericProc[T, Z](): MyOption[T, Z] =
+    none() # implied by return type
+
+  let a = myGenericProc[int, float]()
+  doAssert a.x == 22
+  doAssert a.y == 12.0
+
+  let b: MyOption[int, float] = none() # implied by type of b
+  doAssert b.x == 22
+  doAssert b.y == 12.0
+
+# Simple template based result with inferred type for errors
+block:
+  type
+    ResultKind {.pure.} = enum
+      Ok
+      Err
+
+    Result[T] = object
+      case kind: ResultKind
+      of Ok:
+        data: T
+      of Err:
+        errmsg: cstring
+
+  template err[T](msg: static cstring): Result[T] =
+    Result[T](kind : ResultKind.Err, errmsg : msg)
+
+  proc testproc(): Result[int] =
+    err("Inferred error!") # implied by proc return
+  let r = testproc()
+  doAssert r.kind == ResultKind.Err
+  doAssert r.errmsg == "Inferred error!"
+
+# Builtin seq
+block:
+  let x: seq[int] = newSeq(1)
+  doAssert x is seq[int]
+  doAssert x.len() == 1
+
+  type
+    MyType[T, Z] = object
+      x: T
+      y: Z
+
+  let y: seq[MyType[int, float]] = newSeq(2)
+  doAssert y is seq[MyType[int, float]]
+  doAssert y.len() == 2
+
+  let z = MyType[seq[float], string](
+    x : newSeq(3),
+    y : "test"
+  )
+  doAssert z.x is seq[float]
+  doAssert z.x.len() == 3
+  doAssert z.y is string
+  doAssert z.y == "test"
+
+# array
+block:
+  proc giveArray[N, T](): array[N, T] =
+    for i in 0 .. N.high:
+      result[i] = i
+  var x: array[2, int] = giveArray()
+  doAssert x == [0, 1]
+
+# tuples
+block:
+  proc giveTuple[T, Z]: (T, Z, T) = discard
+  let x: (int, float, int) = giveTuple()
+  doAssert x is (int, float, int)
+  doAssert x == (0, 0.0, 0)
+
+  proc giveNamedTuple[T, Z]: tuple[a: T, b: Z] = discard
+  let y: tuple[a: int, b: float] = giveNamedTuple()
+  doAssert y is (int, float)
+  doAssert y is tuple[a: int, b: float]
+  doAssert y == (0, 0.0)
+
+  proc giveNestedTuple[T, Z]: ((T, Z), Z) = discard
+  let z: ((int, float), float) = giveNestedTuple()
+  doAssert z is ((int, float), float)
+  doAssert z == ((0, 0.0), 0.0)
+
+  # nesting inside a generic type
+  type MyType[T] = object
+    x: T
+  let a = MyType[(int, MyType[float])](x : giveNamedTuple())
+  doAssert a.x is (int, MyType[float])
+
+
+# basic constructors
+block:
+  type MyType[T] = object
+    x: T
+
+  proc giveValue[T](): T =
+    when T is int:
+      12
+    else:
+      default(T)
+
+  let x = MyType[int](x : giveValue())
+  doAssert x.x is int
+  doAssert x.x == 12
+
+  let y = MyType[MyType[float]](x : MyType[float](x : giveValue()))
+  doAssert y.x is MyType[float]
+  doAssert y.x.x is float
+  doAssert y.x.x == 0.0
+
+  # 'MyType[float]' is bound to 'T' directly
+  #  instead of mapping 'T' to 'float'
+  let z = MyType[MyType[float]](x : giveValue())
+  doAssert z.x is MyType[float]
+  doAssert z.x.x == 0.0
+
+  type Foo = object
+    x: Table[int, float]
+
+  let a = Foo(x: initTable())
+  doAssert a.x is Table[int, float]
+
+# partial binding
+block:
+  type
+    ResultKind = enum
+      Ok, Error
+
+    Result[T, E] = object
+      case kind: ResultKind
+      of Ok:
+        okVal: T
+      of Error:
+        errVal: E
+
+  proc err[T, E](myParam: E): Result[T, E] =
+    Result[T, E](kind : Error, errVal : myParam)
+
+  proc doStuff(): Result[int, string] = 
+    err("Error")
+
+  let res = doStuff()
+  doAssert res.kind == Error
+  doAssert res.errVal == "Error"
+
+# ufcs
+block:
+  proc getValue[T](_: string): T =
+    doAssert T is int
+    44
+  
+  proc `'test`[T](_: string): T =
+    55
+
+  let a: int = getValue("")
+  let b: int = "".getValue()
+  let c: int = "".getValue
+  let d: int = getValue ""
+  let e: int = getValue""
+  let f: int = 12345'test
+  doAssert a == 44
+  doAssert b == 44
+  doAssert c == 44
+  doAssert d == 44
+  doAssert e == 44
+  doAssert f == 55
diff --git a/tests/generics/tsigtypeop.nim b/tests/generics/tsigtypeop.nim
deleted file mode 100644
index 4c863cba1..000000000
--- a/tests/generics/tsigtypeop.nim
+++ /dev/null
@@ -1,9 +0,0 @@
-type
-  Vec3[T] = array[3, T]
-
-proc foo(x: Vec3, y: Vec3.T, z: x.T): x.type.T =
-  return 10
-
-var y: Vec3[int] = [1, 2, 3]
-var z: int = foo(y, 3, 4)
-
diff --git a/tests/generics/tspecial_numeric_inference.nim b/tests/generics/tspecial_numeric_inference.nim
deleted file mode 100644
index 41a84a5e9..000000000
--- a/tests/generics/tspecial_numeric_inference.nim
+++ /dev/null
@@ -1,21 +0,0 @@
-discard """
-  output: '''false'''
-"""
-
-when false:
-  import typetraits
-
-  proc `@`[T: SomeInteger](x, y: T): T = x
-
-  echo(type(5'i64 @ 6'i32))
-
-  echo(type(5'i32 @ 6'i64))
-
-import sets
-# bug #7247
-type
-  n8 = range[0'i8..127'i8]
-
-var tab = initSet[n8]()
-
-echo tab.contains(8)
diff --git a/tests/generics/tspecialised_is_equivalent.nim b/tests/generics/tspecialised_is_equivalent.nim
deleted file mode 100644
index 56fd72630..000000000
--- a/tests/generics/tspecialised_is_equivalent.nim
+++ /dev/null
@@ -1,14 +0,0 @@
-##
-## specialised_is_equivalent Nim Module
-##
-## Created by Eric Doughty-Papassideris on 2011-02-16.
-
-type
-  TGen[T] = tuple[a: T]
-  TSpef = tuple[a: string]
-
-var
-  a: TGen[string]
-  b: TSpef
-a = b
-
diff --git a/tests/generics/tspecialized_procvar.nim b/tests/generics/tspecialized_procvar.nim
deleted file mode 100644
index 4bdc94a66..000000000
--- a/tests/generics/tspecialized_procvar.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-discard """
-  output: '''concrete 88'''
-"""
-
-# Another regression triggered by changed closure computations:
-
-proc foo[T](x: proc(): T) =
-  echo "generic ", x()
-
-proc foo(x: proc(): int) =
-  echo "concrete ", x()
-
-# note the following 'proc' is not .closure!
-foo(proc (): auto {.nimcall.} = 88)
-
-# bug #3499 last snippet fixed
-# bug 705  last snippet fixed
diff --git a/tests/generics/tstatic_constrained.nim b/tests/generics/tstatic_constrained.nim
new file mode 100644
index 000000000..d356b9d1c
--- /dev/null
+++ b/tests/generics/tstatic_constrained.nim
@@ -0,0 +1,79 @@
+discard """
+  cmd: "nim check --hints:off --warnings:off $file"
+  action: "reject"
+  nimout:'''
+tstatic_constrained.nim(44, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)]
+got: <typedesc[int], int literal(10)>
+but expected: <T: float or string, Y>
+tstatic_constrained.nim(44, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)]
+got: <typedesc[int], int literal(10)>
+but expected: <T: float or string, Y>
+tstatic_constrained.nim(44, 31) Error: object constructor needs an object type [error]
+tstatic_constrained.nim(44, 31) Error: expression '' has no type (or is ambiguous)
+tstatic_constrained.nim(45, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)]
+got: <typedesc[byte], uint8>
+but expected: <T: float or string, Y>
+tstatic_constrained.nim(45, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)]
+got: <typedesc[byte], uint8>
+but expected: <T: float or string, Y>
+tstatic_constrained.nim(45, 34) Error: object constructor needs an object type [error]
+tstatic_constrained.nim(45, 34) Error: expression '' has no type (or is ambiguous)
+tstatic_constrained.nim(77, 14) Error: cannot instantiate MyType [type declared in tstatic_constrained.nim(71, 5)]
+got: <typedesc[float], float64>
+but expected: <T: MyConstraint, Y>
+'''
+"""
+block:
+  type 
+    MyType[T; X: static T] = object
+      data: T
+    MyOtherType[T: float or string, Y: static T] = object
+
+  func f[T,X](a: MyType[T,X]): MyType[T,X] =
+    when T is string:
+      MyType[T,X](data: a.data & X)
+    else:
+      MyType[T,X](data: a.data + X)
+
+  discard MyType[int, 2](data: 1)
+  discard MyType[string, "Helelello"](data: "Hmmm")
+  discard MyType[int, 2](data: 1).f()
+  discard MyType[string, "Helelello"](data: "Hmmm").f()
+  discard MyOtherType[float, 1.3]()
+  discard MyOtherType[string, "Hello"]()
+  discard MyOtherType[int, 10]()
+  discard MyOtherType[byte, 10u8]()
+
+block:
+  type
+    Moduloable = concept m, type M
+      m mod m is M
+    Addable = concept a, type A
+      a + a is A
+    Modulo[T: Moduloable; Mod: static T] = distinct T
+    ModuloAdd[T: Moduloable or Addable; Mod: static T] = distinct T
+    ModuAddable = Addable or Moduloable
+    ModdAddClass[T: ModuAddable; Mod: static T] = distinct T
+
+  proc toMod[T](val: T, modVal: static T): Modulo[T, modVal] =
+    mixin `mod`
+    Modulo[T, modVal](val mod modVal)
+  var
+    a = 3231.toMod(10)
+    b = 5483.toMod(10)
+  discard ModuloAdd[int, 3](0)
+  discard ModdAddClass[int, 3](0)
+
+block:
+  type
+    MyConstraint = int or string
+    MyOtherConstraint[T] = object
+    MyType[T: MyConstraint; Y: static T] = object
+    MyOtherType[T: MyOtherConstraint; Y: static T] = object
+
+  var 
+    a: MyType[int, 10]
+    b: MyType[string, "hello"]
+    c: MyType[float, 10d]
+    d: MyOtherType[MyOtherConstraint[float],MyOtherConstraint[float]()]
+    e: MyOtherType[MyOtherConstraint[int], MyOtherConstraint[int]()]
diff --git a/tests/generics/tstatictalias.nim b/tests/generics/tstatictalias.nim
deleted file mode 100644
index 98751b8cb..000000000
--- a/tests/generics/tstatictalias.nim
+++ /dev/null
@@ -1,20 +0,0 @@
-discard """
-  output: '''G:0,1:0.1
-G:0,1:0.1
-H:1:0.1'''
-"""
-
-type
-  G[i,j:static[int]] = object
-    v:float
-  H[j:static[int]] = G[0,j]
-proc p[i,j:static[int]](x:G[i,j]) = echo "G:",i,",",j,":",x.v
-proc q[j:static[int]](x:H[j]) = echo "H:",j,":",x.v
-
-var
-  g0 = G[0,1](v: 0.1)
-  h0:H[1] = g0
-p(g0)
-p(h0)
-q(h0)
-# bug #4863
diff --git a/tests/generics/tsubclassgenericerror.nim b/tests/generics/tsubclassgenericerror.nim
new file mode 100644
index 000000000..87f8a8e64
--- /dev/null
+++ b/tests/generics/tsubclassgenericerror.nim
@@ -0,0 +1,11 @@
+discard """
+  errormsg: "cannot instantiate 'GenericParentType[T]' inside of type definition: 'GenericChildType'; Maybe generic arguments are missing?"
+  line: 8
+"""
+
+type
+  GenericParentType[T] = ref object of RootObj
+  GenericChildType[T] = ref object of GenericParentType # missing the [T]
+    val: T
+
+var instance : GenericChildType[int] = nil
diff --git a/tests/generics/tsubtypeconstraint.nim b/tests/generics/tsubtypeconstraint.nim
deleted file mode 100644
index 2f0954522..000000000
--- a/tests/generics/tsubtypeconstraint.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-
-# bug #1684
-type
-  BaseType {.inheritable pure.} = object
-    idx: int
-
-  DerivedType* {.final pure.} = object of BaseType
-
-proc index*[Toohoo: BaseType](h: Toohoo): int {.inline.} = h.idx
-proc newDerived(idx: int): DerivedType {.inline.} = DerivedType(idx: idx)
-
-let d = newDerived(2)
-assert(d.index == 2)
diff --git a/tests/generics/ttable_alias.nim b/tests/generics/ttable_alias.nim
deleted file mode 100644
index 992ca85e0..000000000
--- a/tests/generics/ttable_alias.nim
+++ /dev/null
@@ -1,7 +0,0 @@
-# bug #4589
-
-import tables
-type SimpleTable*[TKey, TVal] = TableRef[TKey, TVal]
-template newSimpleTable*(TKey, TVal: typedesc): SimpleTable[TKey, TVal] = newTable[TKey, TVal]()
-var fontCache : SimpleTable[string, SimpleTable[int32, int]]
-fontCache = newSimpleTable(string, SimpleTable[int32, int])
diff --git a/tests/generics/ttempl_in_generic.nim b/tests/generics/ttempl_in_generic.nim
deleted file mode 100644
index f04b9d216..000000000
--- a/tests/generics/ttempl_in_generic.nim
+++ /dev/null
@@ -1,8 +0,0 @@
-
-# bug #4600
-template foo(x: untyped): untyped = echo 1
-template foo(x,y: untyped): untyped = echo 2
-
-proc bar1[T](x: T) = foo(x)
-proc bar2(x: float) = foo(x,x)
-proc bar3[T](x: T) = foo(x,x)
diff --git a/tests/generics/tthread_generic.nim b/tests/generics/tthread_generic.nim
index def1acfe1..300da56a6 100644
--- a/tests/generics/tthread_generic.nim
+++ b/tests/generics/tthread_generic.nim
@@ -1,5 +1,6 @@
 discard """
-  cmd: "nim $target --hints:on --threads:on $options $file"
+  matrix: "--mm:refc; --mm:orc"
+  action: compile
 """
 
 type
@@ -25,7 +26,7 @@ proc `@||->`*[T](fn: proc(): T {.thread.},
 proc `||->`*[T](fn: proc(): T{.thread.}, callback: proc(val: T){.thread.}) =
   discard fn @||-> callback
 
-when isMainModule:
+when true:
   import os
   proc testFunc(): int {.thread.} =
     return 1
@@ -36,4 +37,3 @@ when isMainModule:
   echo("test")
   joinThread(thr)
   os.sleep(3000)
-
diff --git a/tests/generics/ttypeclass_to_typeclass.nim b/tests/generics/ttypeclass_to_typeclass.nim
deleted file mode 100644
index a5f010450..000000000
--- a/tests/generics/ttypeclass_to_typeclass.nim
+++ /dev/null
@@ -1,12 +0,0 @@
-# bug #4672
-type
-  EnumContainer[T: enum] = object
-    v: T
-  SomeEnum {.pure.} = enum
-    A,B,C
-
-proc value[T: enum](this: EnumContainer[T]): T =
-  this.v
-
-var enumContainer: EnumContainer[SomeEnum]
-discard enumContainer.value()
diff --git a/tests/generics/tuninstantiated_failure.nim b/tests/generics/tuninstantiated_failure.nim
new file mode 100644
index 000000000..f3d5b34b8
--- /dev/null
+++ b/tests/generics/tuninstantiated_failure.nim
@@ -0,0 +1,16 @@
+discard """
+cmd: "nim check $file"
+"""
+
+type
+  Test[T, K] = object
+    name: string
+  Something = Test[int]
+
+func `[]`[T, K](x: var Test[T, K], idx: int): var Test[T, K] =
+  x
+
+var b: Something
+# Should give an error since Something isn't a valid Test
+b[0].name = "Test" #[tt.Error
+ ^  expression '' has no type (or is ambiguous)]#
diff --git a/tests/generics/tuninstantiatedgenericcalls.nim b/tests/generics/tuninstantiatedgenericcalls.nim
new file mode 100644
index 000000000..f33fc8967
--- /dev/null
+++ b/tests/generics/tuninstantiatedgenericcalls.nim
@@ -0,0 +1,517 @@
+# Cases that used to only work due to weird workarounds in the compiler
+# involving not instantiating calls in generic bodies which are removed
+# due to breaking statics.
+# The issue was that these calls are compiled as regular expressions at
+# the generic declaration with unresolved generic parameter types,
+# which are special cased in some places in the compiler, but sometimes
+# treated like real types.
+
+block:
+  type Base10 = object
+
+  func maxLen(T: typedesc[Base10], I: type): int8 =
+    when I is uint8:
+      3
+    elif I is uint16:
+      5
+    elif I is uint32:
+      10
+    elif I is uint64:
+      20
+    else:
+      when sizeof(uint) == 4:
+        10
+      else:
+        20
+  
+  type
+    Base10Buf[T: SomeUnsignedInt] = object
+      data: array[maxLen(Base10, T), byte]
+      len: int8
+
+  var x: Base10Buf[uint32]
+  doAssert x.data.len == 10
+  var y: Base10Buf[uint16]
+  doAssert y.data.len == 5
+
+import typetraits
+
+block thardcases:
+  proc typeNameLen(x: typedesc): int {.compileTime.} =
+    result = x.name.len
+  macro selectType(a, b: typedesc): typedesc =
+    result = a
+
+  type
+    Foo[T] = object
+      data1: array[T.high, int]
+      data2: array[typeNameLen(T), float]
+      data3: array[0..T.typeNameLen, selectType(float, int)]
+  
+  type MyEnum = enum A, B, C, D
+
+  var f1: Foo[MyEnum]
+  var f2: Foo[int8]
+
+  doAssert high(f1.data1) == 2 # (D = 3) - 1 == 2
+  doAssert high(f1.data2) == 5 # (MyEnum.len = 6) - 1 == 5
+
+  doAssert high(f2.data1) == 126 # 127 - 1 == 126
+  doAssert high(f2.data2) == 3 # int8.len - 1 == 3
+
+  static:
+    doAssert high(f1.data1) == ord(C)
+    doAssert high(f1.data2) == 5 # length of MyEnum minus one, because we used T.high
+
+    doAssert high(f2.data1) == 126
+    doAssert high(f2.data2) == 3
+
+    doAssert high(f1.data3) == 6 # length of MyEnum
+    doAssert high(f2.data3) == 4 # length of int8
+
+    doAssert f2.data3[0] is float
+
+import muninstantiatedgenericcalls
+
+block:
+  var x: Leb128Buf[uint32]
+  doAssert x.data.len == 5
+  var y: Leb128Buf[uint16]
+  doAssert y.data.len == 3
+
+import macros
+
+block: # issue #12415
+  macro isSomePointerImpl(t: typedesc): bool =
+    var impl = t.getTypeInst[1].getTypeImpl
+    if impl.kind == nnkDistinctTy:
+      impl = impl[0].getTypeImpl
+    if impl.kind in {nnkPtrTy,nnkRefTy}:
+      result = newLit(true)
+    elif impl.kind == nnkSym and impl.eqIdent("pointer"):
+      result = newLit(true)
+    else:
+      result = newLit(false)
+
+  proc isSomePointer[T](t: typedesc[T]): bool {.compileTime.} =
+    isSomePointerImpl(t)
+
+  type
+    Option[T] = object
+      ## An optional type that stores its value and state separately in a boolean.
+      when isSomePointer(typedesc(T)):
+        val: T
+      else:
+        val: T
+        has: bool
+  var x: Option[ref int]
+  doAssert not compiles(x.has)
+  var y: Option[int]
+  doAssert compiles(y.has)
+
+block: # issue #2002
+  proc isNillable(T: typedesc): bool =
+    when compiles((let v: T = nil)):
+      return true
+    else:
+      return false
+
+  type
+    Foo[T] = object
+      when isNillable(T):
+        nillable: float
+      else:
+        notnillable: int
+
+  var val1: Foo[ref int]
+  doAssert compiles(val1.nillable)
+  doAssert not compiles(val1.notnillable)
+  var val2: Foo[int]
+  doAssert not compiles(val2.nillable)
+  doAssert compiles(val2.notnillable)
+
+block: # issue #1771
+  type
+    Foo[X, T] = object
+      bar: array[X.low..X.high, T]
+
+  proc test[X, T](f: Foo[X, T]): T =
+    f.bar[X.low]
+
+  var a: Foo[range[0..2], float]
+  doAssert test(a) == 0.0
+
+block: # issue #23730
+  proc test(M: static[int]): array[1 shl M, int] = discard
+  doAssert len(test(3)) == 8
+  doAssert len(test(5)) == 32
+
+block: # issue #19819
+  type
+    Example[N: static int] = distinct int
+    What[E: Example] = Example[E.N + E.N]
+
+block: # issue #23339
+  type
+    A = object
+    B = object
+  template aToB(t: typedesc[A]): typedesc = B
+  type
+    Inner[I] = object
+      innerField: I
+    Outer[O] = object
+      outerField: Inner[O.aToB]
+  var x: Outer[A]
+  doAssert typeof(x.outerField.innerField) is B
+
+block: # deref syntax
+  type
+    Enqueueable = concept x
+      x is ptr
+    Foo[T: Enqueueable] = object
+      x: typeof(default(T)[])
+
+  proc p[T](f: Foo[T]) =
+    var bar: Foo[T]
+    discard
+  var foo: Foo[ptr int]
+  p(foo)
+  doAssert foo.x is int
+  foo.x = 123
+  doAssert foo.x == 123
+  inc foo.x
+  doAssert foo.x == 124
+
+block:
+  type Generic[T] = object
+    field: T
+  macro foo(x: typed): untyped = x
+  macro bar[T](x: typedesc[Generic[T]]): untyped = x
+  type
+    Foo[T] = object
+      field: Generic[int].foo()
+    Foo2[T] = object
+      field: Generic[T].foo()
+    Bar[T] = object
+      field: Generic[int].bar()
+    Bar2[T] = object
+      field: Generic[T].bar()
+  var x: Foo[int]
+  var x2: Foo2[int]
+  var y: Bar[int]
+  var y2: Bar2[int]
+
+block:
+  macro pick(x: static int): untyped =
+    if x < 100:
+      result = bindSym"int"
+    else:
+      result = bindSym"float"
+  
+  type Foo[T: static int] = object
+    fixed1: pick(25)
+    fixed2: pick(125)
+    unknown: pick(T)
+  
+  var a: Foo[123]
+  doAssert a.fixed1 is int
+  doAssert a.fixed2 is float
+  doAssert a.unknown is float
+  var b: Foo[23]
+  doAssert b.fixed1 is int
+  doAssert b.fixed2 is float
+  doAssert b.unknown is int
+
+import std/sequtils
+
+block: # version of #23432 with `typed`, don't delay instantiation
+  type
+    Future[T] = object
+    InternalRaisesFuture[T, E] = object
+  macro Raising[T](F: typedesc[Future[T]], E: varargs[typed]): untyped =
+    let raises = nnkTupleConstr.newTree(E.mapIt(it))
+    nnkBracketExpr.newTree(
+      ident "InternalRaisesFuture",
+      nnkDotExpr.newTree(F, ident"T"),
+      raises
+    )
+  type X[E] = Future[void].Raising(E)
+  proc f(x: X) = discard
+  var v: Future[void].Raising([ValueError])
+  f(v)
+
+block: # issue #22647
+  proc c0(n: static int): int = 8
+  proc c1(n: static int): int = n div 2
+  proc c2(n: static int): int = n * 2
+  proc c3(n: static int, n2: int): int = n * n2
+  proc `**`(n: static int, n2: int): int = n * n2
+  proc c4(n: int, n2: int): int = n * n2
+
+  type
+    a[N: static int] = object
+      f0 : array[N, int]
+
+    b[N: static int] = object
+      f0 : a[c0(N)]  # does not work
+      f1 : a[c1(N)]  # does not work
+      f2 : a[c2(N)]  # does not work
+      f3 : a[N * 2]  # does not work
+      f4 : a[N]      # works
+      f5: a[c3(N, 2)]
+      f6: a[N ** 2]
+      f7: a[2 * N]
+      f8: a[c4(N, 2)]
+
+  proc p[N: static int](x : a[N]) = discard x.f0[0]
+  template check(x, s: untyped) =
+    p(x)
+    doAssert x is a[s]
+    doAssert x.N == s
+    doAssert typeof(x).N == s
+    doAssert x.f0 == default(array[s, int])
+    doAssert x.f0.len == s
+    proc p2[N: static int](y : a[N]) {.gensym.} =
+      doAssert y is a[s]
+      doAssert y.N == s
+      doAssert typeof(y).N == s
+      doAssert y.f0 == default(array[s, int])
+      doAssert y.f0.len == s
+    p2(x)
+    proc p3(z: typeof(x)) {.gensym.} = discard
+    p3(default(a[s]))
+  proc p[N: static int](x : b[N]) =
+    x.f0.check(8)
+    x.f1.check(2)
+    x.f2.check(8)
+    x.f3.check(8)
+    x.f4.check(4)
+    x.f5.check(8)
+    x.f6.check(8)
+    x.f7.check(8)
+    x.f8.check(8)
+
+  var x: b[4]
+  x.p()
+
+block: # issue #1969
+  type ZeroGenerator = object
+  proc next(g: ZeroGenerator): int = 0
+  # This compiles.
+  type TripleOfInts = tuple
+    a, b, c: typeof(new(ZeroGenerator)[].next)
+  # This raises a compiler error before it's even instantiated.
+  # The `new` proc can't be resolved because `Generator` is not defined.
+  type TripleLike[Generator] = tuple
+    a, b, c: typeof(new(Generator)[].next)
+
+import std/atomics
+
+block: # issue #12720
+  const CacheLineSize = 128
+  type
+    Enqueueable = concept x, type T
+      x is ptr
+      x.next is Atomic[pointer]
+    MyChannel[T: Enqueueable] = object
+      pad: array[CacheLineSize - sizeof(default(T)[]), byte]
+      dummy: typeof(default(T)[])
+
+block: # issue #12714
+  type
+    Enqueueable = concept x, type T
+      x is ptr
+      x.next is Atomic[pointer]
+    MyChannel[T: Enqueueable] = object
+      dummy: type(default(T)[])
+
+block: # issue #24044
+  type ArrayBuf[N: static int, T = byte] = object
+    buf: array[N, T]
+  template maxLen(T: type): int =
+    sizeof(T) * 2
+  type MyBuf[I] = ArrayBuf[maxLen(I)]
+  var v: MyBuf[int]
+
+block: # issue #15959
+  proc my[T](a: T): typeof(a[0]) = discard
+  proc my2[T](a: T): array[sizeof(a[0]), T] = discard
+  proc byLent2[T](a: T): lent type(a[0]) = a[0] # Error: type mismatch: got <T, int literal(0)>
+  proc byLent3[T](a: T): lent typeof(a[0]) = a[0] # ditto
+  proc byLent4[T](a: T): lent[type(a[0])] = a[0] # Error: no generic parameters allowed for lent
+  var x = @[1, 2, 3]
+  doAssert my(x) is int
+  doAssert my2(x) is array[sizeof(int), seq[int]]
+  doAssert byLent2(x) == 1
+  doAssert byLent2(x) is lent int
+  doAssert byLent3(x) == 1
+  doAssert byLent3(x) is lent int
+  doAssert byLent4(x) == 1
+  doAssert byLent4(x) is lent int
+  proc fn[U](a: U): auto = a
+  proc my3[T](a: T, b: typeof(fn(a))) = discard
+  my3(x, x)
+  doAssert not compiles(my3(x, x[0]))
+
+block: # issue #22342, type section version of #22607
+  type GenAlias[isInt: static bool] = (
+    when isInt:
+      int
+    else:
+      float
+  )
+  doAssert GenAlias[true] is int
+  doAssert GenAlias[false] is float
+  proc foo(T: static bool): GenAlias[T] = discard
+  doAssert foo(true) is int
+  doAssert foo(false) is float
+  proc foo[T: static bool](v: var GenAlias[T]) =
+    v += 1
+  var x: int
+  foo[true](x)
+  doAssert not compiles(foo[false](x))
+  foo[true](x)
+  doAssert x == 2
+  var y: float
+  foo[false](y)
+  doAssert not compiles(foo[true](y))
+  foo[false](y)
+  doAssert y == 2
+
+block: # `when`, test no constant semchecks
+  type Foo[T] = (
+    when false:
+      {.error: "bad".}
+    elif defined(neverDefined):
+      {.error: "bad 2".}
+    else:
+      T
+  )
+  var x: Foo[int]
+  type Bar[T] = (
+    when true:
+      T
+    elif defined(js):
+      {.error: "bad".}
+    else:
+      {.error: "bad 2".}
+  )
+  var y: Bar[int]
+
+block: # weird regression
+  type
+    Foo[T] = distinct int
+    Bar[T, U] = distinct int
+  proc foo[T, U](x: static Foo[T], y: static Bar[T, U]): Foo[T] =
+    # signature gives:
+    # Error: cannot instantiate Bar
+    # got: <typedesc[T], U>
+    # but expected: <T, U>
+    x
+  doAssert foo(Foo[int](1), Bar[int, int](2)).int == 1
+
+block: # issue #24090
+  type M[V] = object
+  template y[V](N: type M, v: V): M[V] = default(M[V])
+  proc d(x: int | int, f: M[int] = M.y(0)) = discard
+  d(0, M.y(0))
+  type Foo[T] = object
+    x: typeof(M.y(default(T)))
+  var a: Foo[int]
+  doAssert a.x is M[int]
+  var b: Foo[float]
+  doAssert b.x is M[float]
+  doAssert not (compiles do:
+    type Bar[T] = object
+      x: typeof(M()) # actually fails here immediately
+    var bar: Bar[int])
+  doAssert not (compiles do:
+    type Bar[T] = object
+      x: typeof(default(M))
+    var bar: Bar[int]
+    # gives "undeclared identifier x" because of #24091,
+    # normally it should fail in the line above
+    echo bar.x)
+  proc foo[T: M](x: T = default(T)) = discard x
+  foo[M[int]]()
+  doAssert not compiles(foo())
+
+block: # above but encountered by sigmatch using replaceTypeVarsN
+  type Opt[T] = object
+    x: T
+  proc none[T](x: type Opt, y: typedesc[T]): Opt[T] = discard
+  proc foo[T](x: T, a = Opt.none(int)) = discard
+  foo(1, a = Opt.none(int))
+  foo(1)
+
+block: # real version of above
+  type Opt[T] = object
+    x: T
+  template none(x: type Opt, T: type): Opt[T] = Opt[T]()
+  proc foo[T](x: T, a = Opt.none(int)) = discard
+  foo(1, a = Opt.none(int))
+  foo(1)
+
+block: # issue #20880
+  type
+    Child[n: static int] = object
+      data: array[n, int]
+    Parent[n: static int] = object
+      child: Child[3*n]
+  const n = 3
+  doAssert $(typeof Parent[n*3]()) == "Parent[9]"
+  doAssert $(typeof Parent[1]().child) == "Child[3]"
+  doAssert Parent[1]().child.data.len == 3
+
+{.experimental: "dynamicBindSym".}
+block: # issue #16774
+  type SecretWord = distinct uint64
+  const WordBitWidth = 8 * sizeof(uint64)
+  func wordsRequired(bits: int): int {.compileTime.} =
+    ## Compute the number of limbs required
+    # from the **announced** bit length
+    (bits + WordBitWidth - 1) div WordBitWidth
+  type
+    Curve = enum BLS12_381
+    BigInt[bits: static int] = object
+      limbs: array[bits.wordsRequired, SecretWord]
+  const BLS12_381_Modulus = default(BigInt[381])
+  macro Mod(C: static Curve): untyped =
+    ## Get the Modulus associated to a curve
+    result = bindSym($C & "_Modulus")
+  macro getCurveBitwidth(C: static Curve): untyped =
+    result = nnkDotExpr.newTree(
+      getAST(Mod(C)),
+      ident"bits"
+    )
+  type Fp[C: static Curve] = object
+    ## Finite Fields / Modular arithmetic
+    ## modulo the curve modulus
+    mres: BigInt[getCurveBitwidth(C)]
+  var x: Fp[BLS12_381]
+  doAssert x.mres.limbs.len == wordsRequired(getCurveBitWidth(BLS12_381))
+  # minimized, as if we haven't tested it already:
+  macro makeIntLit(c: static int): untyped =
+    result = newLit(c)
+  type Test[T: static int] = object
+    myArray: array[makeIntLit(T), int]
+  var y: Test[2]
+  doAssert y.myArray.len == 2
+  var z: Test[4]
+  doAssert z.myArray.len == 4
+
+block: # issue #16175
+  type
+    Thing[D: static uint] = object
+      when D == 0:
+        kid: char
+      else:
+        kid: Thing[D-1]
+  var t2 = Thing[3]()
+  doAssert t2.kid is Thing[2.uint]
+  doAssert t2.kid.kid is Thing[1.uint]
+  doAssert t2.kid.kid.kid is Thing[0.uint]
+  doAssert t2.kid.kid.kid.kid is char
+  var s = Thing[1]()
+  doAssert s.kid is Thing[0.uint]
+  doAssert s.kid.kid is char
diff --git a/tests/generics/tunique_type.nim b/tests/generics/tunique_type.nim
index ccb367ac8..1150dea49 100644
--- a/tests/generics/tunique_type.nim
+++ b/tests/generics/tunique_type.nim
@@ -11,7 +11,7 @@ discard """
 ## because it's more efficient than using procedure pointers and less
 ## verbose than defining a new callable type for every invocation of `map`.
 
-import future
+import sugar
 import macros
 import strutils
 
diff --git a/tests/generics/tvarargs_vs_generic.nim b/tests/generics/tvarargs_vs_generic.nim
deleted file mode 100644
index 122f3e453..000000000
--- a/tests/generics/tvarargs_vs_generic.nim
+++ /dev/null
@@ -1,26 +0,0 @@
-discard """
-  output: "direct\ngeneric\ngeneric"
-"""
-
-proc withDirectType(args: string) =
-  echo "direct"
-
-proc withDirectType[T](arg: T) =
-  echo "generic"
-
-proc withOpenArray(args: openarray[string]) =
-  echo "openarray"
-
-proc withOpenArray[T](arg: T) =
-  echo "generic"
-
-proc withVarargs(args: varargs[string]) =
-  echo "varargs"
-
-proc withVarargs[T](arg: T) =
-  echo "generic"
-
-withDirectType "string"
-withOpenArray "string"
-withVarargs "string"
-
diff --git a/tests/generics/twrong_explicit_typeargs.nim b/tests/generics/twrong_explicit_typeargs.nim
deleted file mode 100644
index e47b38e99..000000000
--- a/tests/generics/twrong_explicit_typeargs.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-discard """
-  errormsg: "cannot instantiate: 'newImage[string]'"
-  line: 16
-"""
-
-# bug #4084
-type
-  Image[T] = object
-    data: seq[T]
-
-proc newImage[T: int32|int64](w, h: int): ref Image[T] =
-  new(result)
-  result.data = newSeq[T](w * h)
-
-var correct = newImage[int32](320, 200)
-var wrong = newImage[string](320, 200)
diff --git a/tests/generics/twrong_floatlit_type.nim b/tests/generics/twrong_floatlit_type.nim
index c1830cd5a..04bacc0d9 100644
--- a/tests/generics/twrong_floatlit_type.nim
+++ b/tests/generics/twrong_floatlit_type.nim
@@ -108,7 +108,7 @@ proc `/`*[S](a, b: Vector2D[S]): Vector2D[S] =
 proc vec[S](x, y: S): Vector2D[S] =
   Vector2D[S](x: x, y: y)
 
-if isMainModule:
+if true:
   # Comment out this let, and the program will fail to
   # compile with a type mismatch, as expected.
 
diff --git a/tests/generics/twrong_generic_object.nim b/tests/generics/twrong_generic_object.nim
index 00d90c55e..4951f735f 100644
--- a/tests/generics/twrong_generic_object.nim
+++ b/tests/generics/twrong_generic_object.nim
@@ -1,6 +1,6 @@
 discard """
-  errormsg: "cannot instantiate: 'GenericNodeObj'"
-  line: 21
+  errormsg: "'Node' is not a concrete type"
+  line: 11
 """
 # bug #2509
 type
diff --git a/tests/global/a_module.nim b/tests/global/a_module.nim
new file mode 100644
index 000000000..1d3f4848c
--- /dev/null
+++ b/tests/global/a_module.nim
@@ -0,0 +1,6 @@
+# a.nim
+{.push stackTrace: off.}
+proc foo*(): int =
+  var a {.global.} = 0
+  result = a
+{.pop.}
\ No newline at end of file
diff --git a/tests/global/t15005.nim b/tests/global/t15005.nim
new file mode 100644
index 000000000..6395b1dde
--- /dev/null
+++ b/tests/global/t15005.nim
@@ -0,0 +1,18 @@
+type
+  T = ref object
+    data: string
+
+template foo(): T =
+  var a15005 {.global.}: T
+  once:
+    a15005 = T(data: "hi")
+
+  a15005
+
+proc test() =
+  var b15005 = foo()
+
+  doAssert b15005.data == "hi"
+
+test()
+test()
diff --git a/tests/global/t21896.nim b/tests/global/t21896.nim
new file mode 100644
index 000000000..c7765c4dd
--- /dev/null
+++ b/tests/global/t21896.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "cannot assign local to global variable"
+  line: 7
+"""
+
+proc example(a:int) =
+  let b {.global.} = a
+
+example(1)
diff --git a/tests/global/t3505.nim b/tests/global/t3505.nim
new file mode 100644
index 000000000..437a02ae6
--- /dev/null
+++ b/tests/global/t3505.nim
@@ -0,0 +1,41 @@
+discard """
+cmd: "nim check $options --hints:off $file"
+action: "reject"
+nimout: '''
+t3505.nim(22, 22) Error: cannot assign local to global variable
+t3505.nim(31, 28) Error: cannot assign local to global variable
+t3505.nim(39, 29) Error: cannot assign local to global variable
+
+
+
+
+'''
+"""
+
+
+
+
+
+
+proc foo =
+  let a = 0
+  var b {.global.} = a
+foo()
+
+# issue #5132
+proc initX(it: float): int = 8
+proc initX2(it: int): int = it
+
+proc main() =
+  var f: float
+  var x {.global.} = initX2(initX(f))
+  
+main()
+
+# issue #20866
+proc foo2() =
+  iterator bar() {.closure.} =
+    discard
+  var g {.global.} = rawProc(bar)
+
+foo2()
\ No newline at end of file
diff --git a/tests/global/t5958.nim b/tests/global/t5958.nim
new file mode 100644
index 000000000..9b7642363
--- /dev/null
+++ b/tests/global/t5958.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "undeclared identifier: 'a'"
+  line: 9
+"""
+
+static:
+  var a = 1
+
+echo a
diff --git a/tests/global/tglobal2.nim b/tests/global/tglobal2.nim
new file mode 100644
index 000000000..73a575bbd
--- /dev/null
+++ b/tests/global/tglobal2.nim
@@ -0,0 +1,9 @@
+# b.nim
+import a_module
+doAssert foo() == 0
+
+proc hello(x: type) =
+  var s {.global.} = default(x)
+  doAssert s == 0
+
+hello(int)
diff --git a/tests/global/tglobalforvar.nim b/tests/global/tglobalforvar.nim
index af75df5c8..bc18f33f2 100644
--- a/tests/global/tglobalforvar.nim
+++ b/tests/global/tglobalforvar.nim
@@ -1,7 +1,9 @@
+discard """
+output: 100
+"""
 
 var funcs: seq[proc (): int {.nimcall.}] = @[]
 for i in 0..10:
   funcs.add((proc (): int = return i * i))
 
 echo(funcs[3]())
-
diff --git a/tests/ic/config.nims b/tests/ic/config.nims
new file mode 100644
index 000000000..a622ec309
--- /dev/null
+++ b/tests/ic/config.nims
@@ -0,0 +1 @@
+--mm:refc

diff --git a/tests/ic/mbaseobj.nim b/tests/ic/mbaseobj.nim
new file mode 100644
index 000000000..0f4e4a90d
--- /dev/null
+++ b/tests/ic/mbaseobj.nim
@@ -0,0 +1,7 @@
+
+type
+  Base* = ref object of RootObj
+    s*: string
+
+method m*(b: Base) {.base.} =
+  echo "Base ", b.s
diff --git a/tests/ic/mcompiletime_counter.nim b/tests/ic/mcompiletime_counter.nim
new file mode 100644
index 000000000..6fc7b3f2a
--- /dev/null
+++ b/tests/ic/mcompiletime_counter.nim
@@ -0,0 +1,15 @@
+
+import std/macros
+import std/macrocache
+
+const myCounter = CacheCounter"myCounter"
+
+proc getUniqueId*(): int {.compileTime.} =
+  inc myCounter
+  result = myCounter.value
+
+static:
+  myCounter.inc(3)
+  assert myCounter.value == 3
+
+
diff --git a/tests/ic/mdefconverter.nim b/tests/ic/mdefconverter.nim
new file mode 100644
index 000000000..d0a23f801
--- /dev/null
+++ b/tests/ic/mdefconverter.nim
@@ -0,0 +1,2 @@
+
+converter toBool*(x: int): bool = x != 0
diff --git a/tests/ic/mimports.nim b/tests/ic/mimports.nim
new file mode 100644
index 000000000..50773e001
--- /dev/null
+++ b/tests/ic/mimports.nim
@@ -0,0 +1,9 @@
+from mimportsb {.all.} import fnb1, hfnb3
+
+proc fn1*(): int = 1
+proc fn2*(): int = 2
+proc hfn3(): int = 3
+proc hfn4(): int = 4
+proc hfn5(): int = 5
+
+export mimportsb.fnb2, hfnb3
diff --git a/tests/ic/mimportsb.nim b/tests/ic/mimportsb.nim
new file mode 100644
index 000000000..3cae925c5
--- /dev/null
+++ b/tests/ic/mimportsb.nim
@@ -0,0 +1,4 @@
+proc fnb1*(): int = 1
+proc fnb2*(): int = 2
+proc hfnb3(): int = 3
+proc hfnb4(): int = 4
diff --git a/tests/ic/tcompiletime_counter.nim b/tests/ic/tcompiletime_counter.nim
new file mode 100644
index 000000000..695a6ec27
--- /dev/null
+++ b/tests/ic/tcompiletime_counter.nim
@@ -0,0 +1,24 @@
+discard """
+  output: '''id 4'''
+"""
+
+import mcompiletime_counter
+
+const intId = getUniqueId()
+
+echo "id ", intId
+
+#!EDIT!#
+
+discard """
+  output: '''id 4 5'''
+"""
+
+import mcompiletime_counter
+
+const
+  intId = getUniqueId()
+  floatId = getUniqueId()
+
+echo "id ", intId, " ", floatId
+
diff --git a/tests/ic/tconverter.nim b/tests/ic/tconverter.nim
new file mode 100644
index 000000000..aecdf4b48
--- /dev/null
+++ b/tests/ic/tconverter.nim
@@ -0,0 +1,18 @@
+discard """
+  output: "yes"
+"""
+
+import mdefconverter
+
+echo "yes"
+
+#!EDIT!#
+
+discard """
+  output: "converted int to bool"
+"""
+
+import mdefconverter
+
+if 4:
+  echo "converted int to bool"
diff --git a/tests/ic/tgenericinst.nim b/tests/ic/tgenericinst.nim
new file mode 100644
index 000000000..3346764f5
--- /dev/null
+++ b/tests/ic/tgenericinst.nim
@@ -0,0 +1,11 @@
+discard """
+  cmd: "nim cpp --incremental:on $file"
+"""
+
+{.emit:"""/*TYPESECTION*/
+#include <iostream>
+  struct Foo { };
+""".}
+
+type Foo {.importcpp.} = object
+echo $Foo() #Notice the generic is instantiate in the this module if not, it wouldnt find Foo
\ No newline at end of file
diff --git a/tests/ic/tgenerics.nim b/tests/ic/tgenerics.nim
new file mode 100644
index 000000000..138799e85
--- /dev/null
+++ b/tests/ic/tgenerics.nim
@@ -0,0 +1,38 @@
+discard """
+  output: "bar"
+"""
+
+import tables
+
+var tab: Table[string, string]
+
+tab["foo"] = "bar"
+echo tab["foo"]
+
+#!EDIT!#
+
+discard """
+  output: "bar 3"
+"""
+
+import tables
+
+var tab: Table[string, string]
+var tab2: Table[string, int]
+
+tab["foo"] = "bar"
+tab2["meh"] = 3
+echo tab["foo"], " ", tab2["meh"]
+
+#!EDIT!#
+
+discard """
+  output: "3"
+"""
+
+import tables
+
+var tab2: Table[string, int]
+
+tab2["meh"] = 3
+echo tab2["meh"]
diff --git a/tests/ic/thallo.nim b/tests/ic/thallo.nim
new file mode 100644
index 000000000..7ead7c8ba
--- /dev/null
+++ b/tests/ic/thallo.nim
@@ -0,0 +1,28 @@
+discard """
+  output: "Hello World"
+"""
+
+const str = "Hello World"
+echo str
+
+# Splitters are done with this special comment:
+
+#!EDIT!#
+
+discard """
+  output: "Hello World B"
+"""
+
+const str = "Hello World"
+echo str, " B"
+
+#!EDIT!#
+
+discard """
+  output: "Hello World C"
+"""
+
+const str = "Hello World"
+var x = 7
+if 3+4 == x:
+  echo str, " C"
diff --git a/tests/ic/timports.nim b/tests/ic/timports.nim
new file mode 100644
index 000000000..518a689f5
--- /dev/null
+++ b/tests/ic/timports.nim
@@ -0,0 +1,29 @@
+import mimports
+doAssert fn1() == 1
+doAssert not declared(hfn3)
+
+#!EDIT!#
+
+import mimports {.all.}
+doAssert fn1() == 1
+doAssert declared(hfn3)
+doAssert hfn3() == 3
+doAssert mimports.hfn4() == 4
+
+# reexports
+doAssert not declared(fnb1)
+doAssert not declared(hfnb4)
+doAssert fnb2() == 2
+doAssert hfnb3() == 3
+
+#!EDIT!#
+
+from mimports {.all.} import hfn3
+doAssert not declared(fn1)
+from mimports {.all.} as bar import fn1
+doAssert fn1() == 1
+doAssert hfn3() == 3
+doAssert not declared(hfn4)
+doAssert declared(mimports.hfn4)
+doAssert mimports.hfn4() == 4
+doAssert bar.hfn4() == 4
diff --git a/tests/ic/tmethods.nim b/tests/ic/tmethods.nim
new file mode 100644
index 000000000..251f26889
--- /dev/null
+++ b/tests/ic/tmethods.nim
@@ -0,0 +1,28 @@
+discard """
+  output: '''Base abc'''
+"""
+
+import mbaseobj
+
+var c = Base(s: "abc")
+m c
+
+#!EDIT!#
+
+discard """
+  output: '''Base abc
+Inherited abc'''
+"""
+
+import mbaseobj
+
+type
+  Inherited = ref object of Base
+
+method m(i: Inherited) =
+  procCall m(Base i)
+  echo "Inherited ", i.s
+
+var c = Inherited(s: "abc")
+m c
+
diff --git a/tests/ic/tstdlib_import_changed.nim b/tests/ic/tstdlib_import_changed.nim
new file mode 100644
index 000000000..da69a53b5
--- /dev/null
+++ b/tests/ic/tstdlib_import_changed.nim
@@ -0,0 +1,15 @@
+discard """
+  output: '''yes'''
+"""
+
+echo "yes"
+
+#!EDIT!#
+
+discard """
+  output: '''yes2'''
+"""
+
+import std / [monotimes]
+#discard getMonoTime()
+echo "yes2"
diff --git a/tests/implicit/timplicititems.nim b/tests/implicit/timplicititems.nim
deleted file mode 100644
index dbe321cb6..000000000
--- a/tests/implicit/timplicititems.nim
+++ /dev/null
@@ -1,4 +0,0 @@
-
-for x in [1, 2, 3, 4]:
-  echo x
-
diff --git a/tests/implicit/timplictderef.nim b/tests/implicit/timplictderef.nim
deleted file mode 100644
index fcb647217..000000000
--- a/tests/implicit/timplictderef.nim
+++ /dev/null
@@ -1,35 +0,0 @@
-discard """
-  output: '''2
-88'''
-"""
-
-type
-  TValue* {.pure, final.} = object of RootObj
-    a: int
-  PValue = ref TValue
-  PPValue = ptr PValue
-
-
-var x: PValue
-new x
-var sp: PPValue = addr x
-
-sp.a = 2
-if sp.a == 2: echo 2  # with sp[].a the error is gone
-
-# Test the new auto-deref a little
-
-{.experimental.}
-
-proc p(x: var int; y: int) = x += y
-
-block:
-  var x: ref int
-  new(x)
-
-  x.p(44)
-
-  var indirect = p
-  x.indirect(44)
-
-  echo x[]
diff --git a/tests/import/buzz/m21496.nim b/tests/import/buzz/m21496.nim
new file mode 100644
index 000000000..677324000
--- /dev/null
+++ b/tests/import/buzz/m21496.nim
@@ -0,0 +1 @@
+proc fb*: string = "buzz!"
\ No newline at end of file
diff --git a/tests/import/fizz/m21496.nim b/tests/import/fizz/m21496.nim
new file mode 100644
index 000000000..525b653d6
--- /dev/null
+++ b/tests/import/fizz/m21496.nim
@@ -0,0 +1 @@
+proc fb*: string = "fizz!"
\ No newline at end of file
diff --git a/tests/import/t21496.nim b/tests/import/t21496.nim
new file mode 100644
index 000000000..1a4907b12
--- /dev/null
+++ b/tests/import/t21496.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "ambiguous identifier: 'm21496'"
+"""
+
+import fizz/m21496, buzz/m21496
+
+# bug #21496
+
+discard m21496.fb()
diff --git a/tests/import/t22065.nim b/tests/import/t22065.nim
new file mode 100644
index 000000000..dfd87a107
--- /dev/null
+++ b/tests/import/t22065.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "cannot open file: ./sugar"
+"""
+
+import ./sugar
\ No newline at end of file
diff --git a/tests/import/t22208.nim b/tests/import/t22208.nim
new file mode 100644
index 000000000..b935d751c
--- /dev/null
+++ b/tests/import/t22208.nim
@@ -0,0 +1,6 @@
+import fizz/m21496 as alas
+import buzz/m21496
+
+# bug #21496
+
+doAssert m21496.fb() == "buzz!"
diff --git a/tests/import/t23167.nim b/tests/import/t23167.nim
new file mode 100644
index 000000000..0891ee2af
--- /dev/null
+++ b/tests/import/t23167.nim
@@ -0,0 +1,5 @@
+# bug #23167
+template sharedImport() =
+  import std / os
+
+sharedImport()
diff --git a/tests/importalls/m1.nim b/tests/importalls/m1.nim
new file mode 100644
index 000000000..b6ccbf5da
--- /dev/null
+++ b/tests/importalls/m1.nim
@@ -0,0 +1,70 @@
+import ./m2
+import ./m3 {.all.} as m3
+from ./m3 as m3Bis import nil
+
+doAssert m3h2 == 2
+export m3h2
+
+export m3Bis.m3p1
+
+const foo0* = 2
+const foo1 = bar1
+
+const foo1Aux = 2
+export foo1Aux
+
+doAssert not declared(bar2)
+doAssert not compiles(bar2)
+
+var foo2 = 2
+let foo3 = 2
+
+type Foo4 = enum
+  kg1, kg2
+
+type Foo4b {.pure.} = enum
+  foo4b1, foo4b2
+
+type Foo5 = object
+  z1: string
+  z2: Foo4
+  z3: int
+  z4*: int
+
+proc `z3`*(a: Foo5): auto =
+  a.z3 * 10
+
+proc foo6(): auto = 2
+proc foo6b*(): auto = 2
+template foo7: untyped = 2
+macro foo8(): untyped = discard
+template foo9(a: int) = discard
+
+block:
+  template foo10: untyped = 2
+  type Foo11 = enum
+    kg1b, kg2b
+  proc foo12(): auto = 2
+
+proc initFoo5*(z3: int): Foo5 = Foo5(z3: z3)
+
+func foo13(): auto = 2
+iterator foo14a(): int = discard
+iterator foo14b*(): int = discard
+iterator foo14c(): int {.closure.} = discard
+iterator foo14d(): int {.inline.} = discard
+
+# fwd declare
+proc foo15(): int
+proc foo15(): int = 2
+
+proc foo16*(): int
+proc foo16(): int = 2
+
+proc foo17*(): int
+proc foo17*(): int = 2
+
+# other
+type A1 = distinct int
+type A2 = distinct int
+converter foo18(x: A1): A2 = discard
diff --git a/tests/importalls/m2.nim b/tests/importalls/m2.nim
new file mode 100644
index 000000000..186650f68
--- /dev/null
+++ b/tests/importalls/m2.nim
@@ -0,0 +1,3 @@
+const bar1* = 2
+const bar2 = 2
+const bar3 = 3
diff --git a/tests/importalls/m3.nim b/tests/importalls/m3.nim
new file mode 100644
index 000000000..caeae2a01
--- /dev/null
+++ b/tests/importalls/m3.nim
@@ -0,0 +1,5 @@
+# xxx use below naming convention in other test files: p: public, h: hidden
+const m3p1* = 2
+const m3h2 = 2
+const m3h3 = 3
+const m3h4 = 4
diff --git a/tests/importalls/m4.nim b/tests/importalls/m4.nim
new file mode 100644
index 000000000..77ec65c61
--- /dev/null
+++ b/tests/importalls/m4.nim
@@ -0,0 +1,9 @@
+import ./m3 {.all.}
+import ./m3 as m3b
+export m3b
+export m3h3
+export m3.m3h4
+
+import ./m2 {.all.} as m2b
+export m2b except bar3
+
diff --git a/tests/importalls/mt0.nim b/tests/importalls/mt0.nim
new file mode 100644
index 000000000..154d30ef9
--- /dev/null
+++ b/tests/importalls/mt0.nim
@@ -0,0 +1,9 @@
+import ./m1 as m
+doAssert compiles(foo0)
+doAssert not compiles(foo1)
+doAssert foo6b() == 2
+doAssert m3h2 == 2
+
+var f = initFoo5(z3=3)
+doAssert f.z3 == 30
+doAssert z3(f) == 30
diff --git a/tests/importalls/mt1.nim b/tests/importalls/mt1.nim
new file mode 100644
index 000000000..b7983945c
--- /dev/null
+++ b/tests/importalls/mt1.nim
@@ -0,0 +1,23 @@
+import ./m1 {.all.} as m
+doAssert foo1 == 2
+doAssert m.foo1 == 2
+
+doAssert m.m3h2 == 2
+doAssert m3h2 == 2
+doAssert m.foo1Aux == 2
+doAssert m.m3p1 == 2
+
+## field access
+import std/importutils
+privateAccess(Foo5)
+var x = Foo5(z1: "foo", z2: m.kg1)
+doAssert x.z1 == "foo"
+
+var f0: Foo5
+f0.z3 = 3
+doAssert f0.z3 == 3
+var f = initFoo5(z3=3)
+doAssert f.z3 == 3
+doAssert z3(f) == 30
+doAssert m.z3(f) == 30
+doAssert not compiles(mt1.`z3`(f)) # z3 is an imported symbol
diff --git a/tests/importalls/mt2.nim b/tests/importalls/mt2.nim
new file mode 100644
index 000000000..52edbb69e
--- /dev/null
+++ b/tests/importalls/mt2.nim
@@ -0,0 +1,104 @@
+from ./m1 {.all.} as r1 import foo1
+from ./m1 {.all.} as r2 import foo7
+
+block: # different symbol kinds
+  doAssert foo1 == 2
+  doAssert r1.foo1 == 2
+  doAssert r1.foo2 == 2
+  doAssert compiles(foo1)
+  doAssert compiles(r1.foo2)
+  doAssert not compiles(foo2)
+  doAssert not compiles(m3h2)
+  doAssert r1.foo3 == 2
+
+  block: # enum
+    var a: r1.Foo4
+    let a1 = r1.kg1
+    doAssert a1 == r1.Foo4.kg1
+    type A = r1.Foo4
+    doAssert a1 == A.kg1
+    doAssert not compiles(kg1)
+    doAssert compiles(A.kg1)
+    var witness = false
+    for ai in r1.Foo4:
+      doAssert ai == a
+      doAssert ai == a1
+      witness = true
+      break
+    doAssert witness
+
+  block: # {.pure.} enum
+    var a: r1.Foo4b
+    doAssert not compiles(r1.foo4b1) # because pure
+    doAssert not compiles(foo4b1)
+    let a1 = r1.Foo4b.foo4b1
+    doAssert a1 == a
+    type A = r1.Foo4b
+    doAssert a1 == A.foo4b1
+    var witness = false
+    for ai in A:
+      doAssert ai == a
+      doAssert ai == a1
+      witness = true
+      break
+    doAssert witness
+
+  block: # object
+    doAssert compiles(r1.Foo5)
+    var a: r1.Foo5
+    doAssert compiles(a.z4)
+    doAssert not compiles(a.z3)
+
+  block: # remaining symbol kinds
+    doAssert r1.foo6() == 2
+    doAssert r1.foo6b() == 2
+    doAssert foo7() == 2
+    doAssert r2.foo6b() == 2
+
+    r1.foo8()
+    r1.foo9(1)
+    doAssert r1.foo13() == 2
+    for a in r1.foo14a(): discard
+    for a in r1.foo14b(): discard
+    for a in r1.foo14c(): discard
+    for a in r1.foo14d(): discard
+    doAssert r1.foo15() == 2
+    doAssert r1.foo16() == 2
+    doAssert r1.foo17() == 2
+    doAssert compiles(r1.foo18)
+    doAssert declared(r1.foo18)
+
+  block: # declarations at block scope should not be visible
+    doAssert declared(foo7)
+    doAssert declared(r1.foo6)
+    doAssert not declared(foo10)
+    doAssert not declared(foo6)
+    doAssert not declared(r1.Foo11)
+    doAssert not declared(r1.kg1b)
+    doAssert not declared(r1.foo12)
+    doAssert not compiles(r1.foo12())
+
+## field access
+import std/importutils
+privateAccess(r1.Foo5)
+var x = r1.Foo5(z1: "foo", z2: r1.kg1)
+doAssert x.z1 == "foo"
+
+var f0: r1.Foo5
+f0.z3 = 3
+doAssert f0.z3 == 3
+var f = r1.initFoo5(z3=3)
+doAssert f.z3 == 3
+doAssert r1.z3(f) == 30
+
+import ./m1 as r3
+doAssert not declared(foo2)
+doAssert not declared(r3.foo2)
+
+from ./m1 {.all.} as r4 import nil
+doAssert not declared(foo2)
+doAssert declared(r4.foo2)
+
+from ./m1 {.all.} import nil
+doAssert not declared(foo2)
+doAssert declared(m1.foo2)
diff --git a/tests/importalls/mt3.nim b/tests/importalls/mt3.nim
new file mode 100644
index 000000000..2bd7fd79d
--- /dev/null
+++ b/tests/importalls/mt3.nim
@@ -0,0 +1,12 @@
+import ./m1 {.all.}
+  # D20201209T194412:here keep this as is, without `as`, so that mt8.nim test keeps
+  # checking that the original module symbol for `m1` isn't modified and that
+  # only the alias in `createModuleAlias` is affected.
+doAssert declared(m1.foo1)
+doAssert foo1 == 2
+
+
+doAssert m1.foo1 == 2
+
+doAssert not compiles(mt3.foo0) # foo0 is an imported symbol
+doAssert not compiles(mt3.foo1) # ditto
diff --git a/tests/importalls/mt4.nim b/tests/importalls/mt4.nim
new file mode 100644
index 000000000..cd7d32aad
--- /dev/null
+++ b/tests/importalls/mt4.nim
@@ -0,0 +1,4 @@
+import ./m1 {.all.} except foo1
+doAssert foo2 == 2
+doAssert declared(foo2)
+doAssert not compiles(foo1)
diff --git a/tests/importalls/mt4b.nim b/tests/importalls/mt4b.nim
new file mode 100644
index 000000000..4578bc54c
--- /dev/null
+++ b/tests/importalls/mt4b.nim
@@ -0,0 +1,3 @@
+from ./m1 {.all.} as m2 import nil
+doAssert not compiles(foo1)
+doAssert m2.foo1 == 2
diff --git a/tests/importalls/mt5.nim b/tests/importalls/mt5.nim
new file mode 100644
index 000000000..5c5785af6
--- /dev/null
+++ b/tests/importalls/mt5.nim
@@ -0,0 +1,9 @@
+import ./m1 {.all.} as m2 except foo1
+doAssert foo2 == 2
+doAssert not compiles(foo1)
+doAssert m2.foo1 == 2
+doAssert compiles(m2.foo1)
+
+from system {.all.} as s import ThisIsSystem
+doAssert ThisIsSystem
+doAssert s.ThisIsSystem
diff --git a/tests/importalls/mt6.nim b/tests/importalls/mt6.nim
new file mode 100644
index 000000000..ac6f542b6
--- /dev/null
+++ b/tests/importalls/mt6.nim
@@ -0,0 +1,13 @@
+import ./m1 {.all.} as m2
+doAssert compiles(foo1)
+doAssert compiles(m2.foo1)
+doAssert declared(foo1)
+doAssert declared(m2.foo0) # public: works fine
+
+doAssert m2.foo1 == 2
+doAssert declared(m2.foo1)
+doAssert not declared(m2.nonexistent)
+
+# also tests the quoted `""` import
+import "."/"m1" {.all.} as m1b
+doAssert compiles(m1b.foo1)
diff --git a/tests/importalls/mt7.nim b/tests/importalls/mt7.nim
new file mode 100644
index 000000000..45a664610
--- /dev/null
+++ b/tests/importalls/mt7.nim
@@ -0,0 +1,14 @@
+include ./m1
+doAssert compiles(foo1)
+doAssert compiles(mt7.foo1)
+doAssert declared(foo1)
+doAssert declared(mt7.foo1)
+doAssert declared(mt7.foo0)
+
+var f0: Foo5
+f0.z3 = 3
+doAssert f0.z3 == 3
+var f = initFoo5(z3=3)
+doAssert f.z3 == 3
+doAssert mt7.z3(f) == 30
+doAssert z3(f) == 30
diff --git a/tests/importalls/mt8.nim b/tests/importalls/mt8.nim
new file mode 100644
index 000000000..f484e7b01
--- /dev/null
+++ b/tests/importalls/mt8.nim
@@ -0,0 +1,23 @@
+#[
+test multiple imports
+]#
+
+{.warning[UnusedImport]: off.}
+import ./m1, m2 {.all.}, ./m3 {.all.}
+  # make sure this keeps using `import ./m1` without as.
+
+# m1 is regularly imported
+doAssert declared(m1.foo0)
+doAssert declared(foo0)
+
+doAssert not declared(m1.foo1)
+  # if we didn't call `createModuleAlias` even for `import f1 {.all.}`,
+  # this would fail, see D20201209T194412.
+
+# m2
+doAssert declared(m2.bar2)
+doAssert declared(bar2)
+
+# m3
+doAssert declared(m3.m3h2)
+doAssert declared(m3h2)
diff --git a/tests/importalls/mt9.nim b/tests/importalls/mt9.nim
new file mode 100644
index 000000000..42d48ecbd
--- /dev/null
+++ b/tests/importalls/mt9.nim
@@ -0,0 +1,12 @@
+# tests re-export of a module with import {.all.}
+
+import ./m4
+
+doAssert m3p1 == 2
+doAssert not declared(m3h2)
+doAssert m3h3 == 3
+doAssert m3h4 == 4
+
+doAssert bar1 == 2
+doAssert bar2 == 2
+doAssert not declared(bar3)
diff --git a/tests/importalls/tmain2.nim b/tests/importalls/tmain2.nim
new file mode 100644
index 000000000..596f0f135
--- /dev/null
+++ b/tests/importalls/tmain2.nim
@@ -0,0 +1,7 @@
+discard """
+joinable: false # for clarity, but not necessary
+"""
+
+{.warning[UnusedImport]: off.}
+# only import `mt*.nim` here; these depend on `m*.nim`
+import "."/[mt0,mt1,mt2,mt3,mt4,mt4b,mt5,mt6,mt7,mt8,mt9]
diff --git a/tests/init/t8314.nim b/tests/init/t8314.nim
new file mode 100644
index 000000000..47c8480c2
--- /dev/null
+++ b/tests/init/t8314.nim
@@ -0,0 +1,27 @@
+discard """
+  nimout: '''
+t8314.nim(14, 7) Hint: BEGIN [User]
+t8314.nim(25, 7) Hint: END [User]
+  '''
+
+output: '''
+1
+1
+1
+'''
+"""
+
+{.hint: "BEGIN".}
+proc foo(x: range[1..10]) =
+  block:
+    var (y,) = (x,)
+    echo y
+  block:
+    var (_,y) = (1,x)
+    echo y
+  block:
+    var (y,_,) = (x,1,)
+    echo y
+{.hint: "END".}
+
+foo(1)
diff --git a/tests/init/tcompiles.nim b/tests/init/tcompiles.nim
new file mode 100644
index 000000000..67e17b241
--- /dev/null
+++ b/tests/init/tcompiles.nim
@@ -0,0 +1,101 @@
+discard """
+  matrix: "--warningAsError:ProveInit --warningAsError:Uninit"
+"""
+
+{.experimental: "strictdefs".}
+
+type Test = object
+  id: int
+
+proc foo {.noreturn.} = discard
+
+block:
+  proc test(x: bool): Test =
+    if x:
+      foo()
+    else:
+      foo()
+
+block:
+  proc test(x: bool): Test =
+    if x:
+      result = Test()
+    else:
+      foo()
+
+  discard test(true)
+
+block:
+  proc test(x: bool): Test =
+    if x:
+      result = Test()
+    else:
+      return Test()
+
+  discard test(true)
+
+block:
+  proc test(x: bool): Test =
+    if x:
+      return Test()
+    else:
+      return Test()
+
+  discard test(true)
+
+block:
+  proc test(x: bool): Test =
+    if x:
+      result = Test()
+    else:
+      result = Test()
+      return
+
+  discard test(true)
+
+block:
+  proc test(x: bool): Test =
+    if x:
+      result = Test()
+      return
+    else:
+      raise newException(ValueError, "unreachable")
+
+  discard test(true)
+
+# bug #21615
+# bug #16735
+
+block:
+  type Test {.requiresInit.} = object
+    id: int
+
+  proc bar(): int =
+    raise newException(CatchableError, "error")
+
+  proc test(): Test =
+    raise newException(CatchableError, "")
+
+  template catchError(body) =
+    var done = false
+    try:
+      body
+    except CatchableError:
+      done = true
+    doAssert done
+
+  catchError:
+    echo test()
+
+  catchError:
+    echo bar()
+
+block:
+  proc foo(x: ptr int) =
+    discard
+
+  proc main =
+    var s: int
+    foo(addr s)
+
+  main()
diff --git a/tests/init/tinitchecks_v2.nim b/tests/init/tinitchecks_v2.nim
new file mode 100644
index 000000000..f7716bcca
--- /dev/null
+++ b/tests/init/tinitchecks_v2.nim
@@ -0,0 +1,83 @@
+discard """
+cmd: "nim check $file"
+action: "compile"
+"""
+
+{.experimental: "strictDefs".}
+
+proc myopen(f: out File; s: string): bool =
+  f = default(File)
+  result = false
+
+proc main =
+  var f: File
+  if myopen(f, "aarg"):
+    f.close
+
+proc invalid =
+  var s: seq[string]
+  s.add "abc" #[tt.Warning
+  ^ use explicit initialization of 's' for clarity [Uninit] ]#
+
+proc valid =
+  var s: seq[string] = @[]
+  s.add "abc" # valid!
+
+main()
+invalid()
+valid()
+
+proc branchy(cond: bool) =
+  var s: seq[string]
+  if cond:
+    s = @["y"]
+  else:
+    s = @[]
+  s.add "abc" # valid!
+
+branchy true
+
+proc p(x: out int; y: out string; cond: bool) = #[tt.Warning
+                   ^ Cannot prove that 'y' is initialized. This will become a compile time error in the future. [ProveInit] ]#
+  x = 4
+  if cond:
+    y = "abc"
+  # error: not every path initializes 'y'
+
+var gl: int
+var gs: string
+p gl, gs, false
+
+proc canRaise(x: int): int =
+  result = x
+  raise newException(ValueError, "wrong")
+
+proc currentlyValid(x: out int; y: out string; cond: bool) =
+  x = canRaise(45)
+  y = "abc" # <-- error: not every path initializes 'y'
+
+currentlyValid gl, gs, false
+
+block: # previously effects/toutparam
+  proc gah[T](x: out T) =
+    x = 3
+
+  proc arr1 =
+    var a: array[2, int]
+    var x: int
+    gah(x)
+    a[0] = 3
+    a[x] = 3
+    echo x
+
+  arr1()
+
+  proc arr2 =
+    var a: array[2, int]
+    var x: int
+    a[0] = 3
+    a[x] = 3 #[tt.Warning
+      ^ use explicit initialization of 'x' for clarity [Uninit] ]#
+    echo x
+
+  arr2()
diff --git a/tests/init/tlet.nim b/tests/init/tlet.nim
new file mode 100644
index 000000000..e32bedb18
--- /dev/null
+++ b/tests/init/tlet.nim
@@ -0,0 +1,55 @@
+{.experimental: "strictDefs".}
+
+proc bar(x: out string) =
+  x = "abc"
+
+template moe = # bug #21043
+  try:
+    discard
+  except ValueError as e:
+    echo(e.msg)
+
+template moe0 {.dirty.} = # bug #21043
+  try:
+    discard
+  except ValueError as e:
+    echo(e.msg)
+
+proc foo() =
+  block:
+    let x: string
+    if true:
+      x = "abc"
+    else:
+      x = "def"
+    doAssert x == "abc"
+  block:
+    let y: string
+    bar(y)
+    doAssert y == "abc"
+  block:
+    let x: string
+    if true:
+      x = "abc"
+      discard "abc"
+    else:
+      x = "def"
+      discard "def"
+    doAssert x == "abc"
+  block: #
+    let x {.used.} : int
+  block: #
+    let x: float
+    x = 1.234
+    doAssert x == 1.234
+
+  block:
+    try:
+      discard
+    except ValueError as e:
+      echo(e.msg)
+  moe()
+  moe0()
+
+static: foo()
+foo()
diff --git a/tests/init/tlet_uninit2.nim b/tests/init/tlet_uninit2.nim
new file mode 100644
index 000000000..174aa28f7
--- /dev/null
+++ b/tests/init/tlet_uninit2.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "'let' symbol requires an initialization"
+"""
+
+let x: int
\ No newline at end of file
diff --git a/tests/init/tlet_uninit3.nim b/tests/init/tlet_uninit3.nim
new file mode 100644
index 000000000..cb786f8c8
--- /dev/null
+++ b/tests/init/tlet_uninit3.nim
@@ -0,0 +1,24 @@
+discard """
+  cmd: "nim check $file"
+  action: "reject"
+  nimout: '''
+tlet_uninit3.nim(13, 5) Error: 'let' symbol requires an initialization
+tlet_uninit3.nim(19, 5) Error: 'x' cannot be assigned to
+tlet_uninit3.nim(23, 11) Error: 'let' symbol requires an initialization
+'''
+"""
+
+{.experimental: "strictDefs".}
+
+let global {.used.}: int
+
+proc foo() =
+  block:
+    let x: int
+    x = 13
+    x = 14
+
+  block:
+    let x: int
+    doAssert x == 0
+foo()
diff --git a/tests/init/tlet_uninit4.nim b/tests/init/tlet_uninit4.nim
new file mode 100644
index 000000000..c57d15ff8
--- /dev/null
+++ b/tests/init/tlet_uninit4.nim
@@ -0,0 +1,12 @@
+discard """
+  errormsg: "type mismatch: got <string>"
+"""
+
+{.experimental: "strictDefs".}
+
+proc foo(x: var string) =
+  echo x
+
+proc bar() =
+  let x: string
+  foo(x)
diff --git a/tests/init/toutparam_subtype.nim b/tests/init/toutparam_subtype.nim
new file mode 100644
index 000000000..3597f1459
--- /dev/null
+++ b/tests/init/toutparam_subtype.nim
@@ -0,0 +1,24 @@
+discard """
+cmd: "nim check $file"
+action: "compile"
+errormsg: "type mismatch: got <Subclass[system.int]>"
+line: 21
+"""
+
+{.experimental: "strictDefs".}
+
+type
+  Superclass[T] = object of RootObj
+    a: T
+  Subclass[T] = object of Superclass[T]
+    s: string
+
+proc init[T](x: out Superclass[T]) =
+  x = Superclass(a: 8)
+
+proc subtypeCheck =
+  var v: Subclass[int]
+  init(v)
+  echo v.s # the 's' field was never initialized!
+
+subtypeCheck()
diff --git a/tests/init/toutparams.nim b/tests/init/toutparams.nim
new file mode 100644
index 000000000..590768599
--- /dev/null
+++ b/tests/init/toutparams.nim
@@ -0,0 +1,14 @@
+discard """
+  matrix: "--warningAsError:ProveInit"
+"""
+
+{.experimental: "strictdefs".}
+
+proc foo(x: out int) =
+  x = 1
+
+proc bar(x: out int) =
+  foo(x)
+
+var s: int
+bar(s)
diff --git a/tests/init/tproveinit.nim b/tests/init/tproveinit.nim
new file mode 100644
index 000000000..c9f688309
--- /dev/null
+++ b/tests/init/tproveinit.nim
@@ -0,0 +1,18 @@
+discard """
+  joinable: false
+"""
+
+{.warningAsError[ProveInit]:on.}
+template main() =
+  proc fn(): var int =
+    discard
+  discard fn()
+doAssert not compiles(main())
+
+# bug #9901
+import std/[sequtils, times]
+proc parseMyDates(line: string): DateTime =
+  result = parse(line, "yyyy-MM-dd")
+var dateStrings = @["2018-12-01", "2018-12-02", "2018-12-03"]
+var parsed = dateStrings.map(parseMyDates)
+discard parsed
diff --git a/tests/init/treturns.nim b/tests/init/treturns.nim
new file mode 100644
index 000000000..18cebe0b1
--- /dev/null
+++ b/tests/init/treturns.nim
@@ -0,0 +1,106 @@
+{.experimental: "strictdefs".}
+
+type Test = object
+  id: int
+
+proc foo {.noreturn.} = discard
+
+proc test1(): Test =
+  if true: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+    return Test()
+  else:
+    return
+
+proc test0(): Test =
+  if true: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+    return
+  else:
+    foo()
+
+proc test2(): Test =
+  if true: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+    return
+  else:
+    return
+
+proc test3(): Test =
+  if true: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+    return
+  else:
+    return Test()
+
+proc test4(): Test =
+  if true: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+    return
+  else:
+    result = Test()
+    return
+
+proc test5(x: bool): Test =
+  case x: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+  of true:
+    return
+  else:
+    return Test()
+
+proc test6(x: bool): Test =
+  case x: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+  of true:
+    return
+  else:
+    return
+
+proc test7(x: bool): Test =
+  case x: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+  of true:
+    return
+  else:
+    discard
+
+proc test8(x: bool): Test =
+  case x: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+  of true:
+    discard
+  else:
+    raise
+
+proc hasImportStmt(): bool =
+  if false: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+    return true
+  else:
+    discard
+
+discard hasImportStmt()
+
+block:
+  proc hasImportStmt(): bool =
+    if false: #[tt.Warning
+    ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+      return true
+    else:
+      return
+
+  discard hasImportStmt()
+
+block:
+  block:
+    proc foo(x: var int) =
+      discard
+
+    proc main =
+      var s: int
+      foo(s)#[tt.Warning
+          ^ use explicit initialization of 's' for clarity [Uninit]]#
+
+    main()
+
diff --git a/tests/init/tuninit1.nim b/tests/init/tuninit1.nim
index 886a1d766..ac3007e8d 100644
--- a/tests/init/tuninit1.nim
+++ b/tests/init/tuninit1.nim
@@ -1,10 +1,10 @@
 discard """
-  msg: "Warning: 'y' might not have been initialized [Uninit]"
-  line:34
+  nimout: "tuninit1.nim(34, 11) Warning: use explicit initialization of 'y' for clarity [Uninit]"
+  action: compile
 """
 
 import strutils
-
+{.experimental: "strictDefs".}
 {.warning[Uninit]:on.}
 
 proc p =
@@ -22,7 +22,7 @@ proc p =
 
     try:
       z = parseInt("1233")
-    except E_Base:
+    except Exception:
       case x
       of 34: z = 123
       of 13: z = 34
diff --git a/tests/int/t1.nim b/tests/int/t1.nim
new file mode 100644
index 000000000..6e5cdc8d4
--- /dev/null
+++ b/tests/int/t1.nim
@@ -0,0 +1,61 @@
+discard """
+  targets: "c cpp"
+"""
+
+doAssert typeOf(1.int64 + 1.int) is int64
+doAssert typeOf(1.uint64 + 1.uint) is uint64
+doAssert int64 is SomeNumber
+doAssert int64 is (SomeNumber and not(uint32|uint64|uint|int))
+doAssert int64 is (not(uint32|uint64|uint|int))
+doAssert int isnot int64
+doAssert int64 isnot int
+var myInt16 = 5i16
+var myInt: int
+doAssert typeOf(myInt16 + 34) is int16    # of type `int16`
+doAssert typeOf(myInt16 + myInt) is int   # of type `int`
+doAssert typeOf(myInt16 + 2i32) is int32  # of type `int32`
+doAssert int32 isnot int64
+doAssert int32 isnot int
+
+block: # bug #23947
+  template foo =
+    let test_u64 : uint64 =   0xFF07.uint64
+    let test_u8  : uint8  = test_u64.uint8
+        # Error: illegal conversion from '65287' to '[0..255]'
+    doAssert test_u8 == 7
+
+  static: foo()
+  foo()
+
+block:
+  # bug #22085
+  const
+    x = uint32(uint64.high) # vm error
+    u = uint64.high
+    v = uint32(u) # vm error
+
+  let
+    z = uint64.high
+    y = uint32(z)  # runtime ok
+
+  let
+    w = uint32(uint64.high)  # semfold error
+
+  doAssert x == w
+  doAssert v == y
+
+  # bug #14522
+  doAssert 0xFF000000_00000000.uint64 == 18374686479671623680'u64
+
+block: # bug #23954
+  let testRT_u8 : uint8 = 0x107.uint8
+  doAssert testRT_u8 == 7
+  const testCT_u8 : uint8 = 0x107.uint8
+  doAssert testCT_u8 == 7
+
+block: # issue #24104
+  type P = distinct uint  # uint, uint8, uint16, uint32, uint64 
+  let v = 0.P
+  case v
+  of 0.P: discard
+  else:   discard
diff --git a/tests/int/tarithm.nim b/tests/int/tarithm.nim
new file mode 100644
index 000000000..d0943d225
--- /dev/null
+++ b/tests/int/tarithm.nim
@@ -0,0 +1,187 @@
+discard """
+  output: '''
+int32
+int32
+1280
+1280
+3
+1
+2
+2
+3
+4294967295
+2
+0
+tUnsignedOps OK
+'''
+nimout: "tUnsignedOps OK"
+"""
+
+import typetraits
+
+
+block tand:
+  # bug #5216
+  echo(name typeof((0x0A'i8 and 0x7F'i32) shl 7'i32))
+
+  let i8 = 0x0A'i8
+  echo(name typeof((i8 and 0x7F'i32) shl 7'i32))
+
+  echo((0x0A'i8 and 0x7F'i32) shl 7'i32)
+
+  let ii8 = 0x0A'i8
+  echo((ii8 and 0x7F'i32) shl 7'i32)
+
+
+
+block tcast:
+  template crossCheck(ty: untyped, exp: untyped) =
+    let rt = ty(exp)
+    const ct = ty(exp)
+    if $rt != $ct:
+      echo astToStr(exp)
+      echo "Got ", ct
+      echo "Expected ", rt
+
+  template add1(x: uint8): untyped = x + 1
+  template add1(x: uint16): untyped = x + 1
+  template add1(x: uint32): untyped = x + 1
+
+  template sub1(x: uint8): untyped = x - 1
+  template sub1(x: uint16): untyped = x - 1
+  template sub1(x: uint32): untyped = x - 1
+
+  crossCheck(int8, 0'i16 - 5'i16)
+  crossCheck(int16, 0'i32 - 5'i32)
+  crossCheck(int32, 0'i64 - 5'i64)
+
+  crossCheck(uint8, 0'u8 - 5'u8)
+  crossCheck(uint16, 0'u16 - 5'u16)
+  crossCheck(uint32, 0'u32 - 5'u32)
+  crossCheck(uint64, 0'u64 - 5'u64)
+
+  crossCheck(uint8, uint8.high + 5'u8)
+  crossCheck(uint16, uint16.high + 5'u16)
+  crossCheck(uint32, uint32.high + 5'u32)
+  crossCheck(uint64, 0xFFFFFFFFFFFFFFFF'u64 + 5'u64)
+  crossCheck(uint64, uint64.high + 5'u64)
+
+  doAssert $sub1(0'u8) == "255"
+  doAssert $sub1(0'u16) == "65535"
+  doAssert $sub1(0'u32) == "4294967295"
+
+  doAssert $add1(255'u8) == "0"
+  doAssert $add1(65535'u16) == "0"
+  doAssert $add1(4294967295'u32) == "0"
+
+  crossCheck(int32, high(int32))
+  crossCheck(int32, high(int32).int32)
+  crossCheck(int32, low(int32))
+  crossCheck(int32, low(int32).int32)
+  crossCheck(int64, high(int8).int16.int32.int64)
+  crossCheck(int64, low(int8).int16.int32.int64)
+
+  doAssert not compiles(echo int64(0xFFFFFFFFFFFFFFFF'u64))
+  doAssert not compiles(echo int32(0xFFFFFFFFFFFFFFFF'u64))
+  doAssert not compiles(echo int16(0xFFFFFFFFFFFFFFFF'u64))
+  doAssert not compiles(echo  int8(0xFFFFFFFFFFFFFFFF'u64))
+
+block tnot:
+  # Signed types
+  block:
+    const t0: int8  = not 4
+    const t1: int16 = not 4
+    const t2: int32 = not 4
+    const t3: int64 = not 4
+    const t4: int8  = not -5
+    const t5: int16 = not -5
+    const t6: int32 = not -5
+    const t7: int64 = not -5
+    doAssert t0 == -5
+    doAssert t1 == -5
+    doAssert t2 == -5
+    doAssert t3 == -5
+    doAssert t4 == 4
+    doAssert t5 == 4
+    doAssert t6 == 4
+    doAssert t7 == 4
+
+  # Unsigned types
+  block:
+    const t0: uint8  = not 4'u8
+    const t1: uint16 = not 4'u16
+    const t2: uint32 = not 4'u32
+    const t3: uint64 = not 4'u64
+    const t4: uint8  = not 251'u8
+    const t5: uint16 = not 65531'u16
+    const t6: uint32 = not 4294967291'u32
+    const t7: uint64 = not 18446744073709551611'u64
+    doAssert t0 == 251
+    doAssert t1 == 65531
+    doAssert t2 == 4294967291'u32
+    doAssert t3 == 18446744073709551611'u64
+    doAssert t4 == 4
+    doAssert t5 == 4
+    doAssert t6 == 4
+    doAssert t7 == 4
+
+
+block tshr:
+  proc T() =
+    # let VI = -8
+    let VI64 = -8'i64
+    let VI32 = -8'i32
+    let VI16 = -8'i16
+    let VI8 = -8'i8
+    # doAssert( (VI shr 1) == 9_223_372_036_854_775_804, "Actual: " & $(VI shr 1))
+    doAssert( (VI64 shr 1) == -4, "Actual: " & $(VI64 shr 1))
+    doAssert( (VI32 shr 1) == -4, "Actual: " & $(VI32 shr 1))
+    doAssert( (VI16 shr 1) == -4, "Actual: " & $(VI16 shr 1))
+    doAssert( (VI8 shr 1) == -4, "Actual: " & $(VI8 shr 1))
+
+  T()
+  static:
+    T()
+
+
+
+block tsubrange:
+  # bug #5854
+  type
+    n16 = range[0'i16..high(int16)]
+
+  var level: n16 = 1
+  let maxLevel: n16 = 1
+
+  level = min(level + 2, maxLevel).n16
+  doAssert level == 1
+
+block tissue12177:
+  var a: uint16 = 1
+  var b: uint32 = 2
+
+  echo(b + a)
+  echo(b - a)
+  echo(b * a)
+  echo(b div a)
+
+  echo(a + b)
+  echo(a - b)
+  echo(a * b)
+  echo(a div b)
+
+block tUnsignedOps:
+  proc testUnsignedOps() =
+    let a: int8 = -128
+    let b: int8 = 127
+
+    doAssert b +% 1 == -128
+    doAssert b -% -1 == -128
+    doAssert b *% 2 == -2
+    doAssert a /% 4 == 32
+    doAssert a %% 7 == 2
+    echo "tUnsignedOps OK"
+
+  testUnsignedOps()
+  static:
+    testUnsignedOps()
diff --git a/tests/int/tashr.nim b/tests/int/tashr.nim
new file mode 100644
index 000000000..aeb3b6843
--- /dev/null
+++ b/tests/int/tashr.nim
@@ -0,0 +1,46 @@
+discard """
+  output: ''''''
+  targets: '''c js'''
+"""
+
+# issue #6255, feature request
+# arithmetic right shift
+
+var x1 = -123'i8
+var x2 = -123'i16
+var x3 = -123'i32
+var x4 = -123'i64
+var x5 = -123
+
+block codegen_test:
+  doAssert ashr(x1, 1) == -62
+  doAssert ashr(x2, 1) == -62
+  doAssert ashr(x3, 1) == -62
+  doAssert ashr(x4, 1) == -62
+  doAssert ashr(x5, 1) == -62
+
+block semfold_test:
+  doAssert ashr(-123'i8 , 1) == -62
+  doAssert ashr(-123'i16, 1) == -62
+  doAssert ashr(-123'i32, 1) == -62
+  doAssert ashr(-123'i64, 1) == -62
+  doAssert ashr(-123    , 1) == -62
+
+static: # VM test
+  doAssert ashr(-123'i8 , 1) == -62
+  doAssert ashr(-123'i16, 1) == -62
+  doAssert ashr(-123'i32, 1) == -62
+  doAssert ashr(-123'i64, 1) == -62
+  doAssert ashr(-123    , 1) == -62
+
+  var y1 = -123'i8
+  var y2 = -123'i16
+  var y3 = -123'i32
+  var y4 = -123'i64
+  var y5 = -123
+
+  doAssert ashr(y1, 1) == -62
+  doAssert ashr(y2, 1) == -62
+  doAssert ashr(y3, 1) == -62
+  doAssert ashr(y4, 1) == -62
+  doAssert ashr(y5, 1) == -62
diff --git a/tests/int/tdiv.nim b/tests/int/tdiv.nim
new file mode 100644
index 000000000..5d8eed84d
--- /dev/null
+++ b/tests/int/tdiv.nim
@@ -0,0 +1,19 @@
+discard """
+  targets: "c js"
+"""
+
+
+block divUint64:
+  proc divTest() =
+    let x1 = 12'u16
+    let y = x1 div 5'u16
+    let x2 = 1345567'u32
+    let z = x2 div 5'u32
+    let a = 1345567'u64 div uint64(x1)
+    doAssert y == 2
+    doAssert z == 269113
+    doAssert a == 112130
+
+  static: divTest()
+  divTest()
+
diff --git a/tests/int/tints.nim b/tests/int/tints.nim
new file mode 100644
index 000000000..773e8ccad
--- /dev/null
+++ b/tests/int/tints.nim
@@ -0,0 +1,150 @@
+discard """
+  matrix: "; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on"
+  output: '''
+0 0
+0 0
+Success'''
+"""
+# Test the different integer operations
+
+
+import std/private/jsutils
+
+var testNumber = 0
+
+template test(opr, a, b, c: untyped): untyped =
+  # test the expression at compile and runtime
+  block:
+    const constExpr = opr(a, b)
+    when constExpr != c:
+      {.error: "Test failed " & $constExpr & " " & $c.}
+    inc(testNumber)
+    #Echo("Test: " & $testNumber)
+    var aa = a
+    var bb = b
+    var varExpr = opr(aa, bb)
+    assert(varExpr == c)
+
+test(`+`, 12'i8, -13'i16, -1'i16)
+test(`shl`, 0b11, 0b100, 0b110000)
+whenJsNoBigInt64: discard
+do:
+  test(`shl`, 0b11'i64, 0b100'i64, 0b110000'i64)
+when not defined(js):
+  # mixed type shr needlessly complicates codegen with bigint
+  # and thus is not yet supported in JS for 64 bit ints
+  test(`shl`, 0b11'i32, 0b100'i64, 0b110000'i64)
+test(`shl`, 0b11'i32, 0b100'i32, 0b110000'i32)
+
+test(`or`, 0xf0f0'i16, 0x0d0d'i16, 0xfdfd'i16)
+test(`and`, 0xf0f0'i16, 0xfdfd'i16, 0xf0f0'i16)
+
+whenJsNoBigInt64: discard
+do:
+  test(`shr`, 0xffffffffffffffff'i64, 0x4'i64, 0xffffffffffffffff'i64)
+test(`shr`, 0xffff'i16, 0x4'i16, 0xffff'i16)
+test(`shr`, 0xff'i8, 0x4'i8, 0xff'i8)
+
+whenJsNoBigInt64: discard
+do:
+  test(`shr`, 0xffffffff'i64, 0x4'i64, 0x0fffffff'i64)
+test(`shr`, 0xffffffff'i32, 0x4'i32, 0xffffffff'i32)
+
+whenJsNoBigInt64: discard
+do:
+  test(`shl`, 0xffffffffffffffff'i64, 0x4'i64, 0xfffffffffffffff0'i64)
+test(`shl`, 0xffff'i16, 0x4'i16, 0xfff0'i16)
+test(`shl`, 0xff'i8, 0x4'i8, 0xf0'i8)
+
+whenJsNoBigInt64: discard
+do:
+  test(`shl`, 0xffffffff'i64, 0x4'i64, 0xffffffff0'i64)
+test(`shl`, 0xffffffff'i32, 0x4'i32, 0xfffffff0'i32)
+
+# bug #916
+proc unc(a: float): float =
+  return a
+
+echo int(unc(0.5)), " ", int(unc(-0.5))
+echo int(0.5), " ", int(-0.5)
+
+block: # Casts to uint
+  template testCast(fromValue: typed, toType: typed, expectedResult: typed) =
+    let src = fromValue
+    let dst = cast[toType](src)
+    if dst != expectedResult:
+      echo "Casting ", astToStr(fromValue), " to ", astToStr(toType), " = ", dst.int, " instead of ", astToStr(expectedResult)
+    doAssert(dst == expectedResult)
+
+  testCast(-1'i16, uint16, 0xffff'u16)
+  testCast(0xffff'u16, int16, -1'i16)
+
+  testCast(0xff'u16, uint8, 0xff'u8)
+  testCast(0xffff'u16, uint8, 0xff'u8)
+
+  testCast(-1'i16, uint32, 0xffffffff'u32)
+  testCast(0xffffffff'u32, int32, -1)
+
+  testCast(0xfffffffe'u32, int32, -2'i32)
+  testCast(0xffffff'u32, int16, -1'i32)
+
+  testCast(-5'i32, uint8, 251'u8)
+
+# issue #7174
+let c = 1'u
+let val = c > 0
+doAssert val
+
+block: # bug #6752
+  when not defined(js) or (defined(js) and compileOption("jsbigint64")):
+    let x = 711127'i64
+    doAssert x * 86400'i64 == 61441372800'i64
+
+block: # bug #17604
+  let a = 2147483648'u
+  doAssert (a and a) == a
+  doAssert (a or 0) == a
+
+block: # bitwise not
+  let
+    z8 = 0'u8
+    z16 = 0'u16
+    z32 = 0'u32
+    z64 = 0'u64
+  doAssert (not z8) == uint8.high
+  doAssert (not z16) == uint16.high
+  doAssert (not z32) == uint32.high
+  when not defined(js) or (defined(js) and compileOption("jsbigint64")):
+    doAssert (not z64) == uint64.high
+
+block: # shl
+  let i8 = int8.high
+  let i16 = int16.high
+  let i32 = int32.high
+  let i64 = int64.high
+  doAssert i8 shl 1 == -2
+  doAssert i8 shl 2 == -4
+  doAssert i16 shl 1 == -2
+  doAssert i16 shl 2 == -4
+  doAssert i32 shl 1 == -2
+  doAssert i32 shl 2 == -4
+  when not defined(js) or (defined(js) and compileOption("jsbigint64")):
+    doAssert i64 shl 1 == -2
+    doAssert i64 shl 2 == -4
+
+  let u8 = uint8.high
+  let u16 = uint16.high
+  let u32 = uint32.high
+  let u64 = uint64.high
+  doAssert u8 shl 1 == u8 - 1
+  doAssert u16 shl 1 == u16 - 1
+  doAssert u32 shl 1 == u32 - 1
+  when not defined(js) or (defined(js) and compileOption("jsbigint64")):
+    doAssert u64 shl 1 == u64 - 1
+
+block: # bug #23378
+  var neg = -1  # prevent compile-time evaluation
+  let n = abs BiggestInt neg
+  doAssert n == 1
+
+echo("Success") #OUT Success
diff --git a/tests/misc/tunsigned64mod.nim b/tests/int/tunsigned64mod.nim
index 9c9e01c45..ca3286df3 100644
--- a/tests/misc/tunsigned64mod.nim
+++ b/tests/int/tunsigned64mod.nim
@@ -12,13 +12,13 @@ let t4 = (v2 mod 2'u64).uint64 # works
 # bug #2550
 
 var x: uint # doesn't work
-echo x mod 2 == 0
+doAssert x mod 2 == 0
 
 var y: uint64 # doesn't work
-echo y mod 2 == 0
+doAssert y mod 2 == 0
 
 var z: uint32 # works
-echo z mod 2 == 0
+doAssert z mod 2 == 0
 
 var a: int # works
-echo a mod 2 == 0
+doAssert a mod 2 == 0
diff --git a/tests/misc/tunsignedcmp.nim b/tests/int/tunsignedcmp.nim
index 11b67ac5f..11b67ac5f 100644
--- a/tests/misc/tunsignedcmp.nim
+++ b/tests/int/tunsignedcmp.nim
diff --git a/tests/int/tunsignedcomp.nim b/tests/int/tunsignedcomp.nim
new file mode 100644
index 000000000..970c4ae9d
--- /dev/null
+++ b/tests/int/tunsignedcomp.nim
@@ -0,0 +1,136 @@
+discard """
+  output: ''''''
+  disabled: "true"
+"""
+
+# All operations involving uint64 are commented out
+# as they're not yet supported.
+# All other operations are handled by implicit conversions from uints to ints
+# uint64 could be supported but would need special implementation of the operators
+
+# unsigned < signed
+
+doAssert 10'u8 < 20'i8
+doAssert 10'u8 < 20'i16
+doAssert 10'u8 < 20'i32
+doAssert 10'u8 < 20'i64
+
+doAssert 10'u16 < 20'i8
+doAssert 10'u16 < 20'i16
+doAssert 10'u16 < 20'i32
+doAssert 10'u16 < 20'i64
+
+doAssert 10'u32 < 20'i8
+doAssert 10'u32 < 20'i16
+doAssert 10'u32 < 20'i32
+doAssert 10'u32 < 20'i64
+
+# doAssert 10'u64 < 20'i8
+# doAssert 10'u64 < 20'i16
+# doAssert 10'u64 < 20'i32
+# doAssert 10'u64 < 20'i64
+
+# signed < unsigned
+doAssert 10'i8 < 20'u8
+doAssert 10'i8 < 20'u16
+doAssert 10'i8 < 20'u32
+# doAssert 10'i8 < 20'u64
+
+doAssert 10'i16 < 20'u8
+doAssert 10'i16 < 20'u16
+doAssert 10'i16 < 20'u32
+# doAssert 10'i16 < 20'u64
+
+doAssert 10'i32 < 20'u8
+doAssert 10'i32 < 20'u16
+doAssert 10'i32 < 20'u32
+# doAssert 10'i32 < 20'u64
+
+doAssert 10'i64 < 20'u8
+doAssert 10'i64 < 20'u16
+doAssert 10'i64 < 20'u32
+# doAssert 10'i64 < 20'u64
+
+# unsigned <= signed
+doAssert 10'u8 <= 20'i8
+doAssert 10'u8 <= 20'i16
+doAssert 10'u8 <= 20'i32
+doAssert 10'u8 <= 20'i64
+
+doAssert 10'u16 <= 20'i8
+doAssert 10'u16 <= 20'i16
+doAssert 10'u16 <= 20'i32
+doAssert 10'u16 <= 20'i64
+
+doAssert 10'u32 <= 20'i8
+doAssert 10'u32 <= 20'i16
+doAssert 10'u32 <= 20'i32
+doAssert 10'u32 <= 20'i64
+
+# doAssert 10'u64 <= 20'i8
+# doAssert 10'u64 <= 20'i16
+# doAssert 10'u64 <= 20'i32
+# doAssert 10'u64 <= 20'i64
+
+# signed <= unsigned
+doAssert 10'i8 <= 20'u8
+doAssert 10'i8 <= 20'u16
+doAssert 10'i8 <= 20'u32
+# doAssert 10'i8 <= 20'u64
+
+doAssert 10'i16 <= 20'u8
+doAssert 10'i16 <= 20'u16
+doAssert 10'i16 <= 20'u32
+# doAssert 10'i16 <= 20'u64
+
+doAssert 10'i32 <= 20'u8
+doAssert 10'i32 <= 20'u16
+doAssert 10'i32 <= 20'u32
+# doAssert 10'i32 <= 20'u64
+
+doAssert 10'i64 <= 20'u8
+doAssert 10'i64 <= 20'u16
+doAssert 10'i64 <= 20'u32
+# doAssert 10'i64 <= 20'u64
+
+# signed == unsigned
+doAssert 10'i8 == 10'u8
+doAssert 10'i8 == 10'u16
+doAssert 10'i8 == 10'u32
+# doAssert 10'i8 == 10'u64
+
+doAssert 10'i16 == 10'u8
+doAssert 10'i16 == 10'u16
+doAssert 10'i16 == 10'u32
+# doAssert 10'i16 == 10'u64
+
+doAssert 10'i32 == 10'u8
+doAssert 10'i32 == 10'u16
+doAssert 10'i32 == 10'u32
+# doAssert 10'i32 == 10'u64
+
+doAssert 10'i64 == 10'u8
+doAssert 10'i64 == 10'u16
+doAssert 10'i64 == 10'u32
+# doAssert 10'i64 == 10'u64
+
+# unsigned == signed
+doAssert 10'u8 == 10'i8
+doAssert 10'u8 == 10'i16
+doAssert 10'u8 == 10'i32
+# doAssert 10'u8 == 10'i64
+
+doAssert 10'u16 == 10'i8
+doAssert 10'u16 == 10'i16
+doAssert 10'u16 == 10'i32
+# doAssert 10'u16 == 10'i64
+
+doAssert 10'u32 == 10'i8
+doAssert 10'u32 == 10'i16
+doAssert 10'u32 == 10'i32
+# doAssert 10'u32 == 10'i64
+
+# doAssert 10'u64 == 10'i8
+# doAssert 10'u64 == 10'i16
+# doAssert 10'u64 == 10'i32
+# doAssert 10'u64 == 10'i64
diff --git a/tests/int/tunsignedconv.nim b/tests/int/tunsignedconv.nim
new file mode 100644
index 000000000..6c73521d3
--- /dev/null
+++ b/tests/int/tunsignedconv.nim
@@ -0,0 +1,115 @@
+discard """
+  targets: "c cpp js"
+"""
+
+# Tests unsigned literals and implicit conversion between uints and ints
+
+var h8: uint8 = 128
+var h16: uint16 = 32768
+var h32: uint32 = 2147483648'u32
+var h64: uint64 = 9223372036854775808'u64
+var foobar: uint64 = 9223372036854775813'u64 # Issue 728
+
+var v8: uint8 = 10
+var v16: uint16 = 10
+var v32: uint32 = 10
+var v64: uint64 = 10
+
+# u8 + literal produces u8:
+var a8: uint8 = v8 + 10
+var a16: uint16 = v16 + 10
+
+when false:
+  var d8  = v8 + 10'i8
+  var d16 = v8 + 10'i16
+  var d32 = v8 + 10'i32
+
+when false:
+  # these don't work yet because unsigned.nim is stupid. XXX We need to fix this.
+  var f8  = v16 + 10'u8
+  var f16 = v16 + 10'u16
+  var f32 = v16 + 10'u32
+
+  var g8  = v32 + 10'u8
+  var g16 = v32 + 10'u16
+  var g32 = v32 + 10'u32
+
+var ar: array[0..20, int]
+var n8 = ar[v8]
+var n16 = ar[v16]
+var n32 = ar[v32]
+var n64 = ar[v64]
+
+
+block t4176:
+  var yyy: uint8 = 0
+  yyy = yyy - 127
+  doAssert type(yyy) is uint8
+
+# bug #13661
+
+proc fun(): uint = cast[uint](-1)
+const x0 = fun()
+
+doAssert typeof(x0) is uint
+
+discard $x0
+
+# bug #13671
+
+const x1 = cast[uint](-1)
+discard $(x1,)
+
+# bug #13698
+let n2: csize_t = 1
+doAssert $n2.int32 == "1"
+
+# bug #14616
+
+let limit = 1'u64
+
+let rangeVar = 0'u64 ..< limit
+
+when not defined(gcRefc):
+  doAssert repr(rangeVar) == """0 .. 0""", repr(rangeVar)
+
+# bug #15210
+
+let a3 = not 0'u64
+var success = false
+try:
+  discard a3.int64
+except RangeDefect:
+  success = true
+
+doAssert success, "conversion should fail at runtime"
+
+template main() =
+  # xxx move all tests under here so it gets tested in CT and RT
+  block: # bug #17572
+    type T = distinct uint64
+    func f(x: uint64): auto =
+      let a = T(x)
+      (x, a.uint64)
+    const x = 1'u64 shl 63 or 7
+    const b = T(x)
+    doAssert b.uint64 == 9223372036854775815'u64
+    doAssert $b.uint64 == "9223372036854775815"
+    doAssert f(x) == (9223372036854775815'u64, 9223372036854775815'u64)
+
+static: main()
+main()
+
+block:
+  let a = uint64.high
+  let b = uint32.high
+
+  doAssert a.uint64 == a
+  doAssert a.uint32 == uint32.high
+  doAssert a.uint16 == uint16.high
+  doAssert a.uint8 == uint8.high
+
+  doAssert b.uint64 == b
+  doAssert b.uint32 == b
+  doAssert b.uint16 == uint16.high
+  doAssert b.uint8 == uint8.high
diff --git a/tests/int/tunsignedinc.nim b/tests/int/tunsignedinc.nim
new file mode 100644
index 000000000..9392f1b74
--- /dev/null
+++ b/tests/int/tunsignedinc.nim
@@ -0,0 +1,40 @@
+
+block: # bug #2427
+  var x = 0'u8
+  dec x # OverflowDefect
+  x -= 1 # OverflowDefect
+  x = x - 1 # No error
+
+  doAssert(x == 253'u8)
+
+block:
+  var x = 130'u8
+  x += 130'u8
+  doAssert(x == 4'u8)
+
+block:
+  var x = 40000'u16
+  x = x + 40000'u16
+  doAssert(x == 14464'u16)
+
+block:
+  var x = 4000000000'u32
+  x = x + 4000000000'u32
+  doAssert(x == 3705032704'u32)
+
+block:
+  var x = 123'u16
+  x -= 125
+  doAssert(x == 65534'u16)
+
+block t4175:
+  let i = 0u - 1u
+  const j = 0u - 1u
+  doAssert i == j
+  doAssert j + 1u == 0u
+
+block: # https://forum.nim-lang.org/t/12465#76998
+  var a: int = 1
+  var x: uint8 = 1
+  a.inc(x)   # Error: type mismatch
+  doAssert a == 2
diff --git a/tests/misc/tunsignedmisc.nim b/tests/int/tunsignedmisc.nim
index fc02eee19..b2a3849cf 100644
--- a/tests/misc/tunsignedmisc.nim
+++ b/tests/int/tunsignedmisc.nim
@@ -9,7 +9,7 @@ discard """
 # 8 bit
 let ref1 = 128'u8 shr 7
 let hex1 = 0x80'u8 shr 7
-let oct1 = 0c200'u8 shr 7
+let oct1 = 0o200'u8 shr 7
 let dig1 = 0b10000000'u8 shr 7
 
 doAssert(ref1 == 1)
@@ -20,7 +20,7 @@ doAssert(ref1 == dig1)
 # 16 bit
 let ref2 = 32768'u16 shr 15
 let hex2 = 0x8000'u16 shr 15
-let oct2 = 0c100000'u16 shr 15
+let oct2 = 0o100000'u16 shr 15
 let dig2 = 0b1000000000000000'u16 shr 15
 
 doAssert(ref2 == 1)
@@ -31,7 +31,7 @@ doAssert(ref2 == dig2)
 # 32 bit
 let ref3 = 2147483648'u32 shr 31
 let hex3 = 0x80000000'u32 shr 31
-let oct3 = 0c20000000000'u32 shr 31
+let oct3 = 0o20000000000'u32 shr 31
 let dig3 = 0b10000000000000000000000000000000'u32 shr 31
 
 doAssert(ref3 == 1)
diff --git a/tests/int/twrongexplicitvarconv.nim b/tests/int/twrongexplicitvarconv.nim
new file mode 100644
index 000000000..79f770e8e
--- /dev/null
+++ b/tests/int/twrongexplicitvarconv.nim
@@ -0,0 +1,16 @@
+discard """
+  action: reject
+  nimout: '''
+  but expression 'int(a)' is immutable, not 'var'
+'''
+"""
+
+proc `++`(n: var int) =
+  n += 1
+
+var a: int32 = 15
+
+++int(a) #[tt.Error
+^ type mismatch: got <int>]#
+
+echo a
diff --git a/tests/int/twrongvarconv.nim b/tests/int/twrongvarconv.nim
new file mode 100644
index 000000000..db6ac2c53
--- /dev/null
+++ b/tests/int/twrongvarconv.nim
@@ -0,0 +1,9 @@
+proc `++`(n: var int) =
+  n += 1
+
+var a: int32 = 15
+
+++a #[tt.Error
+^ type mismatch: got <int32>]#
+
+echo a
diff --git a/tests/isolate/tisolate.nim b/tests/isolate/tisolate.nim
new file mode 100644
index 000000000..a0e71e321
--- /dev/null
+++ b/tests/isolate/tisolate.nim
@@ -0,0 +1,41 @@
+discard """
+  errormsg: "expression cannot be isolated: select(a, b)"
+  line: 39
+"""
+
+import std / isolation
+
+import json, streams
+
+proc myParseJson(s: Stream; filename: string): JsonNode =
+  {.cast(noSideEffect).}:
+    result = parseJson(s, filename)
+
+
+proc f(): seq[int] =
+  @[1, 2, 3]
+
+type
+  Node = ref object
+    x: string
+
+proc g(): Node = nil
+
+proc select(a, b: Node): Node =
+  a
+
+proc main =
+  discard isolate f()
+
+
+  discard isolate g()
+
+  discard isolate select(Node(x: "a"), nil)
+  discard isolate select(Node(x: "a"), Node(x: "b"))
+
+  discard isolate myParseJson(newFileStream("my.json"), "my.json")
+
+  var a, b: Node
+  discard isolate select(a, b)
+
+main()
diff --git a/tests/isolate/tisolate2.nim b/tests/isolate/tisolate2.nim
new file mode 100644
index 000000000..9bf92d82e
--- /dev/null
+++ b/tests/isolate/tisolate2.nim
@@ -0,0 +1,22 @@
+discard """
+  errormsg: "expression cannot be isolated: a_to_b(a)"
+  line: 22
+"""
+
+# bug #19013
+import std/isolation
+
+type Z = ref object
+  i: int
+
+type A = object
+  z: Z
+
+type B = object
+  z: Z
+
+func a_to_b(a: A): B =
+  result = B(z: a.z)
+
+let a = A(z: Z(i: 3))
+let b = isolate(a_to_b(a))
diff --git a/tests/isolate/tisolated_lock.nim b/tests/isolate/tisolated_lock.nim
new file mode 100644
index 000000000..312abf0f6
--- /dev/null
+++ b/tests/isolate/tisolated_lock.nim
@@ -0,0 +1,67 @@
+discard """
+  cmd: "nim $target --threads:on $options $file"
+  action: "compile"
+"""
+
+import std / [os, locks, atomics, isolation]
+
+type
+  MyList {.acyclic.} = ref object
+    data: string
+    next: Isolated[MyList]
+
+template withMyLock*(a: Lock, body: untyped) =
+  acquire(a)
+  {.gcsafe.}:
+    try:
+      body
+    finally:
+      release(a)
+
+var head: Isolated[MyList]
+var headL: Lock
+
+var shouldStop: Atomic[bool]
+
+initLock headL
+
+proc send(x: sink string) =
+  withMyLock headL:
+    head = isolate MyList(data: x, next: move head)
+
+proc worker() {.thread.} =
+  var workItem = MyList(nil)
+  var echoed = 0
+  while true:
+    withMyLock headL:
+      var h = extract head
+      if h != nil:
+        workItem = h
+        # workitem is now isolated:
+        head = move h.next
+      else:
+        workItem = nil
+    # workItem is isolated, so we can access it outside
+    # the lock:
+    if workItem.isNil:
+      if shouldStop.load:
+        break
+      else:
+        # give producer time to breath:
+        os.sleep 30
+    else:
+      if echoed < 100:
+        echo workItem.data
+      inc echoed
+
+var thr: Thread[void]
+createThread(thr, worker)
+
+send "abc"
+send "def"
+for i in 0 ..< 10_000:
+  send "xzy"
+  send "zzz"
+shouldStop.store true
+
+joinThread(thr)
diff --git a/tests/iter/t1550.nim b/tests/iter/t1550.nim
new file mode 100644
index 000000000..c971943ee
--- /dev/null
+++ b/tests/iter/t1550.nim
@@ -0,0 +1,24 @@
+discard """
+  targets: "c js"
+"""
+
+type
+  A[T] = iterator(x: T): T {.gcsafe, closure.}
+
+iterator aimp[T](x: T): T {.gcsafe, closure.} =
+  var total = 0
+  while (total < 100):
+    yield total
+    total += x
+
+iterator bimp(y: A[int], z:int): int {.gcsafe, closure.} =
+  for i in y(z):
+    yield i
+
+for x in aimp[int](3):
+  discard x
+
+var y = aimp[int]
+var z = bimp
+for x in z(y, 1):
+  discard x
\ No newline at end of file
diff --git a/tests/iter/t16076.nim b/tests/iter/t16076.nim
new file mode 100644
index 000000000..2eb409068
--- /dev/null
+++ b/tests/iter/t16076.nim
@@ -0,0 +1,45 @@
+discard """
+  targets: "c js"
+"""
+
+proc main() =
+  block: # bug #17485
+    type
+      O = ref object
+        i: int
+
+    iterator t(o: O): int =
+      if o != nil:
+        yield o.i
+      yield 0
+
+    proc m =
+      var data = ""
+      for i in t(nil):
+        data.addInt i
+
+      doAssert data == "0"
+
+    m()
+
+
+  block: # bug #16076
+    type
+      R = ref object
+        z: int
+
+    var data = ""
+
+    iterator foo(x: int; y: R = nil): int {.inline.} =
+      if y == nil:
+        yield x
+      else:
+        yield y.z
+
+    for b in foo(10):
+      data.addInt b
+
+    doAssert data == "10"
+
+static: main()
+main()
diff --git a/tests/iter/t20891.nim b/tests/iter/t20891.nim
new file mode 100644
index 000000000..34deec41b
--- /dev/null
+++ b/tests/iter/t20891.nim
@@ -0,0 +1,28 @@
+import macros, tables
+
+var mapping {.compileTime.}: Table[string, NimNode]
+
+macro register(a: static[string], b: typed): untyped =
+  mapping[a] = b
+
+macro getPtr(a: static[string]): untyped =
+  result = mapping[a]
+
+proc foo() =
+  iterator it() {.closure.} =
+    discard
+  proc getIterPtr(): pointer {.nimcall.} =
+    rawProc(it)
+  register("foo", getIterPtr())
+  discard getIterPtr() # Comment either this to make it work
+foo() # or this
+
+proc bar() =
+  iterator it() {.closure.} =
+    discard getPtr("foo") # Or this
+    discard
+  proc getIterPtr(): pointer {.nimcall.} =
+    rawProc(it)
+  register("bar", getIterPtr())
+  discard getIterPtr()
+bar()
diff --git a/tests/iter/t21306.nim b/tests/iter/t21306.nim
new file mode 100644
index 000000000..4d0396294
--- /dev/null
+++ b/tests/iter/t21306.nim
@@ -0,0 +1,118 @@
+discard """
+  targets: "c js"
+"""
+
+# bug #21306
+type
+  FutureState {.pure.} = enum
+    Pending, Finished, Cancelled, Failed
+
+  FutureBase = ref object of RootObj
+    state: FutureState
+    error: ref CatchableError
+    id: uint
+
+  Future[T] = ref object of FutureBase
+    closure: iterator(f: Future[T]): FutureBase {.raises: [Defect, CatchableError, Exception], gcsafe.}
+    value: T
+
+template setupFutureBase() =
+  new(result)
+  result.state = FutureState.Pending
+
+proc newFutureImpl[T](): Future[T] =
+  setupFutureBase()
+
+template newFuture[T](fromProc: static[string] = ""): Future[T] =
+  newFutureImpl[T]()
+
+proc internalRead[T](fut: Future[T]): T =
+  when T isnot void:
+    return fut.value
+
+template await[T](f: Future[T]): untyped =
+  when declared(chronosInternalRetFuture):
+    when not declaredInScope(chronosInternalTmpFuture):
+      var chronosInternalTmpFuture {.inject.}: FutureBase = f
+    else:
+      chronosInternalTmpFuture = f
+
+    yield chronosInternalTmpFuture
+
+    when T isnot void:
+      cast[type(f)](chronosInternalTmpFuture).internalRead()
+
+type
+  VerifierError {.pure.} = enum
+    Invalid
+    MissingParent
+    UnviableFork
+    Duplicate
+  ProcessingCallback = proc() {.gcsafe, raises: [Defect].}
+  BlockVerifier =
+    proc(signedBlock: int):
+      Future[VerifierError] {.gcsafe, raises: [Defect].}
+
+  SyncQueueKind {.pure.} = enum
+    Forward, Backward
+
+  SyncRequest[T] = object
+    kind: SyncQueueKind
+    index: uint64
+    slot: uint64
+    count: uint64
+    item: T
+
+  SyncResult[T] = object
+    request: SyncRequest[T]
+    data: seq[ref int]
+
+  SyncQueue[T] = ref object
+    kind: SyncQueueKind
+    readyQueue: seq[SyncResult[T]]
+    blockVerifier: BlockVerifier
+
+iterator blocks[T](sq: SyncQueue[T],
+                    sr: SyncResult[T]): ref int =
+  case sq.kind
+  of SyncQueueKind.Forward:
+    for i in countup(0, len(sr.data) - 1):
+      yield sr.data[i]
+  of SyncQueueKind.Backward:
+    for i in countdown(len(sr.data) - 1, 0):
+      yield sr.data[i]
+
+proc push[T](sq: SyncQueue[T]; sr: SyncRequest[T]; data: seq[ref int];
+             processingCb: ProcessingCallback = nil): Future[void] {.
+    stackTrace: off, gcsafe.} =
+  iterator push_436208182(chronosInternalRetFuture: Future[void]): FutureBase {.
+      closure, gcsafe, raises: [Defect, CatchableError, Exception].} =
+    block:
+      template result(): auto {.used.} =
+        {.fatal: "You should not reference the `result` variable inside" &
+            " a void async proc".}
+
+      let item = default(SyncResult[T])
+      for blk in sq.blocks(item):
+        let res = await sq.blockVerifier(blk[])
+
+  var resultFuture = newFuture[void]("push")
+  resultFuture.closure = push_436208182
+  return resultFuture
+
+type
+  SomeTPeer = ref object
+    score: int
+
+proc getSlice(): seq[ref int] =
+  discard
+
+template smokeTest(kkind: SyncQueueKind, start, finish: uint64,
+                   chunkSize: uint64) =
+  var queue: SyncQueue[SomeTPeer]
+  var request: SyncRequest[SomeTPeer]
+  discard queue.push(request, getSlice())
+
+for k in {SyncQueueKind.Forward}:
+  for item in [(uint64(1181), uint64(1399), 41'u64)]:
+    smokeTest(k, item[0], item[1], item[2])
\ No newline at end of file
diff --git a/tests/iter/t21737.nim b/tests/iter/t21737.nim
new file mode 100644
index 000000000..da06faea7
--- /dev/null
+++ b/tests/iter/t21737.nim
@@ -0,0 +1,22 @@
+discard """
+  action: compile
+"""
+
+template mytoSeq*(iter: untyped): untyped =
+  var result: seq[typeof(iter)]# = @[]
+  for x in iter:
+    result.add(x)
+  result
+
+iterator test(dir:int): int =
+  yield 1234
+
+iterator walkGlobKinds (): int =
+  let dir2 = 123
+  let it = mytoSeq(test(dir2))  
+
+proc main()=
+    let it = iterator(): int=
+      for path in walkGlobKinds():
+          yield path
+main()
diff --git a/tests/iter/t22148.nim b/tests/iter/t22148.nim
new file mode 100644
index 000000000..9954eed87
--- /dev/null
+++ b/tests/iter/t22148.nim
@@ -0,0 +1,15 @@
+discard """

+  action: compile

+"""

+

+import std/memfiles

+

+# bug #22148

+proc make*(input: string) =

+  var inp = memfiles.open(input)

+  for line in memSlices(inp):

+    let lineF = MemFile(mem: line.data, size: line.size)

+    for word in memSlices(lineF, ','):

+      discard

+

+make("") # Must call to trigger

diff --git a/tests/iter/t22548.nim b/tests/iter/t22548.nim
new file mode 100644
index 000000000..b9abb75d0
--- /dev/null
+++ b/tests/iter/t22548.nim
@@ -0,0 +1,21 @@
+discard """
+  action: compile
+"""
+
+type Xxx[T] = object
+
+iterator x(v: string): char =
+  var v2: Xxx[int]
+
+  var y: v2.T
+
+  echo y
+
+proc bbb(vv: string): proc () =
+  proc xxx() =
+    for c in x(vv):
+      echo c
+
+  return xxx
+
+bbb("test")()
diff --git a/tests/iter/t22619.nim b/tests/iter/t22619.nim
new file mode 100644
index 000000000..6a98391f3
--- /dev/null
+++ b/tests/iter/t22619.nim
@@ -0,0 +1,83 @@
+# bug #22619
+
+when false: # todo fixme
+  block:
+    type
+      Resource = object
+        value: int
+  
+      Object = object
+        r {.cursor.}: Resource
+        s {.cursor.}: seq[Resource]
+  
+    var numDestroy = 0
+  
+    proc `=copy`(x: var Resource, y: Resource) {.error.} # disallow full copies
+    proc `=destroy`(x: Resource) =
+      inc numDestroy
+  
+    proc test() =
+      # perform the test in procedure so that globals aren't used (their different
+      # semantics with regards to destruction would interfere)
+      var
+        r = Resource(value: 1) # initialize a resource
+        s = @[Resource(value: 2)]
+  
+      # make sure no copy is required in the initializer expression:
+      var o = Object(r: r, s: s)
+  
+      # copying the object doesn't perform a full copy of the cursor fields:
+      var o2 = o
+      discard addr(o2) # prevent `o2` from being turned into a cursor
+  
+      # check that the fields were shallow-copied:
+      doAssert o2.r.value == 1
+      doAssert o2.s[0].value == 2
+  
+      # make sure no copy is required with normal field assignments:
+      o.r = r
+      o.s = s
+  
+  
+      # when `o` and `o2` are destroyed, their destructor must not be called on
+      # their fields
+  
+    test()
+  
+    # one call for the `r` local and one for the object in `s`
+    doAssert numDestroy == 2
+
+block:
+  type Value = distinct int
+
+  var numDestroy = 0
+
+  when defined(gcRefc):
+    proc `=destroy`(x: var Value) =
+      inc numDestroy
+  else:
+    proc `=destroy`(x: Value) =
+      inc numDestroy
+
+  iterator iter(s: seq[Value]): int {.closure.} =
+    # because it is used across yields, `s2` is lifted into the iterator's
+    # environment. Since non-ref cursors in object didn't have their hooks
+    # disabled inside the environments lifted hooks, this led to double
+    # frees
+    var s2 {.cursor.} = s
+    var i = 0
+    let L = s2.len
+    while i < L:
+      yield s2[i].int
+      inc i
+
+  proc test() =
+    var s = @[Value(1), Value(2)]
+    let cl = iter
+    # make sure resuming the iterator works:
+    doAssert cl(s) == 1
+    doAssert cl(s) == 2
+    doAssert cl(s) == 0
+
+  test()
+  doAssert numDestroy == 2
diff --git a/tests/iter/t2771.nim b/tests/iter/t2771.nim
new file mode 100644
index 000000000..71a8a9dcd
--- /dev/null
+++ b/tests/iter/t2771.nim
@@ -0,0 +1,25 @@
+discard """
+  targets: "c js"
+"""
+
+template t1(i: int): int=
+  i+1
+template t2(i: int): int=
+  i+1
+
+doAssert t1(10).t2() == 12
+
+
+template it1(i: int): iterator(): int =
+  iterator result(): int {.closure, gensym.} =
+    yield i+1
+  result
+
+template it2(iter: iterator(): int): iterator(): int =
+  iterator result(): int {.closure, gensym.} =
+    yield iter()+1
+  result
+
+let x2 = it1(10).it2()
+
+doAssert x2() == 12
diff --git a/tests/iter/t2closureiters.nim b/tests/iter/t2closureiters.nim
deleted file mode 100644
index ceb24548c..000000000
--- a/tests/iter/t2closureiters.nim
+++ /dev/null
@@ -1,14 +0,0 @@
-discard """
-  output: '''1'''
-"""
-# bug #3837
-
-iterator t1(): int {.closure.} =
-  yield 1
-
-iterator t2(): int {.closure.} =
-  for i in t1():
-    yield i
-
-for i in t2():
-  echo $i
diff --git a/tests/iter/tanoniter1.nim b/tests/iter/tanoniter1.nim
index 9f0d0a74b..fee16497f 100644
--- a/tests/iter/tanoniter1.nim
+++ b/tests/iter/tanoniter1.nim
@@ -1,4 +1,5 @@
 discard """
+  targets: "c js"
   output: '''1
 2
 3
diff --git a/tests/iter/tarrayiter.nim b/tests/iter/tarrayiter.nim
new file mode 100644
index 000000000..eb7ba591a
--- /dev/null
+++ b/tests/iter/tarrayiter.nim
@@ -0,0 +1,14 @@
+block:
+  iterator `[]`(a: int, r: int): int = 
+    for q in 0 .. r:
+      yield a
+    
+  for val in 10[2]: discard
+  
+  type Custom = distinct string
+
+  iterator `[]`(a: Custom, r: int): char = 
+    for q in 0 .. r:
+      yield a.string[q]
+      
+  for val in Custom("test")[2]: discard
\ No newline at end of file
diff --git a/tests/iter/tclosureiters.nim b/tests/iter/tclosureiters.nim
index 37313d4d7..4a2639852 100644
--- a/tests/iter/tclosureiters.nim
+++ b/tests/iter/tclosureiters.nim
@@ -1,4 +1,5 @@
 discard """
+  targets: "c js"
   output: '''0
 1
 2
@@ -19,7 +20,18 @@ discard """
 0
 1
 2
-70'''
+70
+0
+(1, 1)
+(1, 2)
+(1, 3)
+(2, 1)
+(2, 2)
+(2, 3)
+(3, 1)
+(3, 2)
+(3, 3)
+'''
 """
 
 when true:
@@ -79,3 +91,86 @@ proc foo(f: (iterator(): int)) =
 
 let fIt = iterator(): int = yield 70
 foo fIt
+
+# bug #5321
+
+proc lineIter*(filename: string): iterator(): string =
+  result = iterator(): string {.closure.} =
+    for line in lines(filename):
+      yield line
+
+proc unused =
+  var count = 0
+  let iter = lineIter("temp10.nim")
+  for line in iter():
+    count += 1
+
+iterator lineIter2*(filename: string): string {.closure.} =
+  var f = open(filename, bufSize=8000)
+  defer: close(f)   # <-- commenting defer "solves" the problem
+  var res = newStringOfCap(80)
+  while f.readLine(res): yield res
+
+proc unusedB =
+  var count = 0
+  for line in lineIter2("temp10.nim"):
+    count += 1
+
+# bug #5519
+import os, algorithm
+
+iterator filesIt(path: string): auto {.closure.} =
+  var files = newSeq[string]()
+  var dirs = newSeq[string]()
+  for k, p in os.walkDir(path):
+    let (_, n, e) = p.splitFile
+    if e != "":
+      continue
+    case k
+    of pcFile, pcLinkToFile:
+      files.add(n)
+    else:
+      dirs.add(n)
+  files.sort(system.cmp)
+  dirs.sort(system.cmp)
+  for f in files:
+    yield f
+
+  for d in dirs:
+    files = newSeq[string]()
+    for k, p in os.walkDir(path / d):
+      let (_, n, e) = p.splitFile
+      if e != "":
+        continue
+      case k
+      of pcFile, pcLinkToFile:
+        files.add(n)
+      else:
+        discard
+    files.sort(system.cmp)
+    let prefix = path.splitPath[1]
+    for f in files:
+      yield prefix / f
+
+# bug #13815
+when not defined(js):
+  var love = iterator: int {.closure.} =
+    yield cast[type(
+      block:
+        var a = 0
+        yield a
+        a)](0)
+
+  for i in love():
+    echo i
+else:
+  echo 0
+
+# bug #18474
+iterator pairs(): (int, int) {.closure.} =
+  for i in 1..3:
+    for j in 1..3:
+      yield (i, j)
+
+for pair in pairs():
+  echo pair
diff --git a/tests/iter/tcomplex_openarray.nim b/tests/iter/tcomplex_openarray.nim
deleted file mode 100644
index 6fc191e90..000000000
--- a/tests/iter/tcomplex_openarray.nim
+++ /dev/null
@@ -1,33 +0,0 @@
-
-# bug #3221
-
-import algorithm, math, sequtils
-
-
-iterator permutations[T](ys: openarray[T]): seq[T] =
-  var
-    d = 1
-    c = newSeq[int](ys.len)
-    xs = newSeq[T](ys.len)
-  for i, y in ys: xs[i] = y
-  yield xs
-  block outer:
-    while true:
-      while d > 1:
-        dec d
-        c[d] = 0
-      while c[d] >= d:
-        inc d
-        if d >= ys.len: break outer
-      let i = if (d and 1) == 1: c[d] else: 0
-      swap xs[i], xs[d]
-      yield xs
-      inc c[d]
-
-proc dig_vectors(): void =
-  var v_nums: seq[int]
-  v_nums = newSeq[int](1)
-  for perm in permutations(toSeq(0 .. 1)):
-    v_nums[0] = 1
-
-dig_vectors()
diff --git a/tests/iter/tcountup.nim b/tests/iter/tcountup.nim
index 1559041aa..5f75e653c 100644
--- a/tests/iter/tcountup.nim
+++ b/tests/iter/tcountup.nim
@@ -1,14 +1,25 @@
 discard """
-  file: "tcountup.nim"
-  output: "0123456789"
+  output: '''
+0123456789
+0.0
+'''
 """
 
-# Test new countup and unary <
+# Test new countup
 
-for i in 0 .. < 10'i64:
+for i in 0 ..< 10'i64:
   stdout.write(i)
 
-#OUT 0123456789
+echo()
 
+# 11099
 
+var
+  x: uint32
+  y: float
 
+for i in 0 ..< x:
+  if i == 1: echo i
+  y += 1
+
+echo y
diff --git a/tests/iter/tgeniteratorinblock.nim b/tests/iter/tgeniteratorinblock.nim
new file mode 100644
index 000000000..2ab903996
--- /dev/null
+++ b/tests/iter/tgeniteratorinblock.nim
@@ -0,0 +1,54 @@
+discard """
+  output: '''30
+60
+90
+150
+180
+210
+240
+60
+180
+240
+[60, 180, 240]
+[60, 180]'''
+"""
+import std/enumerate
+
+template map[T; Y](i: iterable[T], fn: proc(x: T): Y): untyped =
+  iterator internal(): Y  {.gensym.} =
+    for it in i:
+      yield fn(it)
+  internal()
+
+template filter[T](i: iterable[T], fn: proc(x: T): bool): untyped =
+  iterator internal(): T {.gensym.} =
+    for it in i:
+      if fn(it):
+        yield it
+  internal()
+
+template group[T](i: iterable[T], amount: static int): untyped =
+  iterator internal(): array[amount, T] {.gensym.} =
+    var val: array[amount, T]
+    for ind, it in enumerate i:
+      val[ind mod amount] = it
+      if ind mod amount == amount - 1:
+        yield val
+  internal()
+
+var a = [10, 20, 30, 50, 60, 70, 80]
+
+proc mapFn(x: int): int = x * 3
+proc filterFn(x: int): bool = x mod 20 == 0
+
+for x in a.items.map(mapFn):
+  echo x
+
+for y in a.items.map(mapFn).filter(filterFn):
+  echo y
+
+for y in a.items.map(mapFn).filter(filterFn).group(3):
+  echo y
+
+for y in a.items.map(mapFn).filter(filterFn).group(2):
+  echo y
diff --git a/tests/iter/timplicit_auto.nim b/tests/iter/timplicit_auto.nim
index d5cb95eb8..1b9f06843 100644
--- a/tests/iter/timplicit_auto.nim
+++ b/tests/iter/timplicit_auto.nim
@@ -15,4 +15,4 @@ iterator fields(a = (0,0), b = (h-1,w-1)): auto =
       yield (y,x)
 
 for y,x in fields():
-  stdout.write disp[univ(x, y)]
+  doAssert disp[univ(x, y)] == disp[Tree]
diff --git a/tests/iter/titer.nim b/tests/iter/titer.nim
index c4143ae4f..b03d43f36 100644
--- a/tests/iter/titer.nim
+++ b/tests/iter/titer.nim
@@ -1,3 +1,16 @@
+discard """
+output: '''
+testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest2!test3?hi
+what's
+your
+name
+hi
+what's
+your
+name
+'''
+"""
+
 # Test the new iterators
 
 iterator xrange(fromm, to: int, step = 1): int =
@@ -42,3 +55,93 @@ const
 for i in 0..len(stringArray)-1:
   echo(stringArray[i])
 
+# bug #15360
+
+type Rule[T] = (int, T)
+
+var t: seq[Rule[int]]
+for (c, t) in t:
+  discard
+
+
+
+import std/sugar
+
+# bug #14165
+iterator log_nodups_hamming(): int {.inline.} =
+  let lb3 = 1
+  let lb4 = 123
+  proc mul3(): int = lb3 + lb4
+  yield mul3()
+
+for h in log_nodups_hamming():
+  break
+for h in log_nodups_hamming():
+  break
+for h in log_nodups_hamming():
+  break
+
+# bug #18536
+iterator envPairs*(): int =
+  var foo: seq[int]
+  proc fun() =
+    foo = @[]
+  fun()
+  yield 3
+
+proc main() =
+  for a in envPairs():
+    discard
+  for a in envPairs():
+    discard
+static: main()
+main()
+
+# bug #6269
+iterator makeFn(outer_val: int): proc(a: int): int =
+  for i in 0..1:
+    yield proc(a:int): int =
+      return a + i.int
+
+let v1 = 42
+
+let res = collect:
+  for fn1 in makeFn(v1):
+    let v2 = fn1(v1)
+    for fn2 in makeFn(v2):
+      fn2(v2)
+
+doAssert res == @[42, 43, 43, 44]
+
+block: # bug #21110
+  iterator p(): int =
+    when nimvm:
+      yield 0
+    else:
+      yield 0
+
+  template foo =
+    for k in p():
+      let m = ""
+      proc e() = discard m & ""
+      e()
+  static: foo()
+  foo()
+
+
+# bug #15924
+iterator walk(): (int, int) {.closure.} =
+  yield (10,11)
+
+for (i,j) in walk():
+  doAssert i == 10
+
+proc main123() =
+  let x = false
+  iterator it(): (bool, bool) {.closure.} = # normally {.closure.} here makes #21476 work
+    discard x
+
+  for (_, _) in it():
+    discard
+
+main123()
diff --git a/tests/iter/titer10.nim b/tests/iter/titer10.nim
index 6a6afc780..ae36aa519 100644
--- a/tests/iter/titer10.nim
+++ b/tests/iter/titer10.nim
@@ -23,7 +23,7 @@ when true:
       for val in sortable:
           yield val
 
-  when isMainModule:
+  when true:
     proc main =
       for val in byDistance([2, 3, 5, 1], 3):
           echo val
@@ -40,7 +40,7 @@ when true:
                  @[2, 3]]
 
   iterator threadUniqs(seq1: seq[seq[int]]): seq[seq[int]] =
-    for i in 0 .. <seq1.len:
+    for i in 0 ..< seq1.len:
       block:
         let i = i
         yield seq1.filter do (x: seq[int]) -> bool: x[0] == seq1[i][0]
diff --git a/tests/iter/titer11.nim b/tests/iter/titer11.nim
new file mode 100644
index 000000000..153b3c29a
--- /dev/null
+++ b/tests/iter/titer11.nim
@@ -0,0 +1,40 @@
+discard """
+targets: "c js"
+output: '''
+[
+1
+2
+3
+]
+'''
+"""
+
+proc represent(i: int): iterator(): string =
+  result = iterator(): string =
+    yield $i
+
+proc represent(s: seq[int]): iterator(): string =
+  result = iterator(): string =
+    yield "["
+    for i in s:
+      var events = represent(i)
+      for event in events():
+        yield event
+    yield "]"
+
+let s = @[1, 2, 3]
+var output = represent(s)
+
+for item in output():
+  echo item
+
+
+#------------------------------------------------------------------------------
+# Issue #12747
+
+type
+  ABC = ref object
+      arr: array[0x40000, pointer]
+let a = ABC()
+for a in a.arr:
+    assert a == nil
\ No newline at end of file
diff --git a/tests/iter/titer12.nim b/tests/iter/titer12.nim
new file mode 100644
index 000000000..f264a0e82
--- /dev/null
+++ b/tests/iter/titer12.nim
@@ -0,0 +1,83 @@
+discard """
+targets: "c js"
+output: '''
+Selecting 2
+1.0
+Selecting 4
+2.0
+'''
+"""
+
+
+# bug #5522
+import macros, sugar, sequtils
+
+proc tryS(f: () -> void): void =
+  (try: f() except: discard)
+
+template trySTImpl(body: untyped): untyped =
+  tryS do() -> auto:
+    `body`
+
+macro tryST*(body: untyped): untyped =
+  var b = if body.kind == nnkDo: body[^1] else: body
+  result = quote do:
+    trySTImpl((block:
+      `b`
+    ))
+
+iterator testIt(): int {.closure.} =
+  for x in 0..10:
+    yield x
+
+var xs = newSeq[int]()
+proc test = tryST do:
+  for x in testIt():
+    xs.add(x)
+
+test()
+
+doAssert xs == toSeq(0..10)
+
+
+
+# bug #5690
+proc filter[T](it: (iterator : T), f: proc(x: T): bool): (iterator : T) =
+  return iterator (): T {.closure.} =
+    for x in it():
+      if f(x): 
+        yield x
+
+proc len[T](it : iterator : T) : Natural =
+  for i in it():
+    result += 1
+
+proc simpleSeqIterator(s :seq[int]) : iterator : int =
+  iterator it: int {.closure.} =
+    for x in s:
+      yield x
+  result = it
+
+let a = newSeq[int](99)
+
+doAssert len(simpleSeqIterator(a).filter(proc(x : int) : bool = true)) == 99
+
+
+
+# bug #5340
+proc where[A](input: seq[A], filter: (A) -> bool): iterator (): A =
+  result = iterator (): A {.closure.} = 
+    for item in input:
+      if filter(item):
+        yield item
+
+proc select[A,B](input: iterator(): A {.closure.}, selector: (A) -> B): iterator (): B {.closure.} = 
+  result = iterator (): B =
+    for item in input():
+      echo "Selecting " & $item
+      yield selector(item)
+
+let query = @[1,2,3,4].where(x=>x mod 2==0).select((x)=>x/2)
+
+for i in query():
+  echo $i
diff --git a/tests/iter/titer13.nim b/tests/iter/titer13.nim
new file mode 100644
index 000000000..086c40ca4
--- /dev/null
+++ b/tests/iter/titer13.nim
@@ -0,0 +1,83 @@
+discard """
+  output: '''b yields
+c yields
+a returns
+b yields
+b returns
+c yields
+
+
+1
+2
+3
+4
+'''
+"""
+
+block:
+  template tloop(iter: untyped) =
+    for i in iter():
+      echo i
+
+  template twhile(iter: untyped) =
+    let it = iter
+    while not finished(it):
+      echo it()
+
+  iterator a(): auto {.closure.} =
+    if true: return "a returns"
+    yield "a yields"
+
+  iterator b(): auto {.closure.} =
+    yield "b yields"
+    if true: return "b returns"
+
+  iterator c(): auto {.closure.} =
+    yield "c yields"
+    if true: return
+
+  iterator d(): auto {.closure.} =
+    if true: return
+    yield "d yields"
+
+  tloop(a)
+  tloop(b)
+  tloop(c)
+  tloop(d)
+  twhile(a)
+  twhile(b)
+  twhile(c)
+  twhile(d)
+
+block:
+  iterator a: auto =
+    yield 1
+  for x in a():
+    echo x
+
+  let b = iterator: int =
+    yield 2
+  for x in b():
+    echo x
+
+  let c = iterator: auto =
+    yield 3
+  for x in c():
+    echo x
+
+block:
+  iterator myIter2(): auto {.closure.} =
+    yield 4
+  for a in myIter2():
+    echo a
+
+block t5859:
+  proc flatIterator[T](s: openArray[T]): auto {.noSideEffect.}=
+    result = iterator(): auto =
+      when (T is not seq|array):
+        for item in s:
+          yield item
+      else:
+        yield 123456
+  # issue #5859
+  let it = flatIterator(@[@[1,2], @[3,4]])
diff --git a/tests/iter/titer14.nim b/tests/iter/titer14.nim
new file mode 100644
index 000000000..7e483bbae
--- /dev/null
+++ b/tests/iter/titer14.nim
@@ -0,0 +1,7 @@
+proc f() =
+  var s: seq[int]
+  iterator a(): int =
+    for x in s: yield x
+
+  iterator b(): int =
+    for x in a(): yield x
diff --git a/tests/iter/titer2.nim b/tests/iter/titer2.nim
index f60aed73a..975cc786c 100644
--- a/tests/iter/titer2.nim
+++ b/tests/iter/titer2.nim
@@ -17,7 +17,7 @@ type
   TSlotEnum = enum seEmpty, seFilled, seDeleted
   TKeyValuePair[A, B] = tuple[slot: TSlotEnum, key: A, val: B]
   TKeyValuePairSeq[A, B] = seq[TKeyValuePair[A, B]]
-  TTable* {.final.}[A, B] = object
+  TTable*[A, B] {.final.} = object
     data: TKeyValuePairSeq[A, B]
     counter: int
 
diff --git a/tests/iter/titer3.nim b/tests/iter/titer3.nim
index ab95dd7bd..defd56c98 100644
--- a/tests/iter/titer3.nim
+++ b/tests/iter/titer3.nim
@@ -1,6 +1,13 @@
 discard """
-  file: "titer3.nim"
-  output: "1231"
+  output: '''1231
+4
+6
+8
+--------
+4
+6
+8
+'''
 """
 
 iterator count1_3: int =
@@ -17,6 +24,31 @@ iterator iter1(a: openArray[int]): int =
 
 var x = [[1, 2, 3], [4, 5, 6]]
 for y in iter1(x[0]): write(stdout, $y)
+writeLine(stdout, "")
 
-#OUT 1231
+# ensure closure and inline iterators have the same behaviour regarding
+# parameter passing
 
+iterator clo(a: int): int {.closure.} =
+  yield 0+a
+  yield 1+a
+  yield 2+a
+
+iterator inl(a: int): int {.inline.} =
+  yield 0+a
+  yield 1+a
+  yield 2+a
+
+proc main =
+  var y = 4
+  for i in clo(y):
+    echo i
+    inc y
+
+  echo "--------"
+  y = 4
+  for i in inl(y):
+    echo i
+    inc y
+
+main()
diff --git a/tests/iter/titer4.nim b/tests/iter/titer4.nim
index 9b52f8055..912883a63 100644
--- a/tests/iter/titer4.nim
+++ b/tests/iter/titer4.nim
@@ -1,10 +1,8 @@
 discard """
+  errormsg: "iterator within for loop context expected"
   file: "titer4.nim"
   line: 7
-  errormsg: "iterator within for loop context expected"
 """
 # implicit items/pairs, but not if we have 3 for loop vars:
 for x, y, z in {'a'..'z'}: #ERROR_MSG iterator within for loop context expected
   nil
-
-
diff --git a/tests/iter/titer5.nim b/tests/iter/titer5.nim
index bbd50fcb1..cb691ffdb 100644
--- a/tests/iter/titer5.nim
+++ b/tests/iter/titer5.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "titer5.nim"
   output: "abcxyz"
 """
 # Test method call syntax for iterators:
@@ -11,6 +10,4 @@ for x in lines.split():
   stdout.write(x)
 
 #OUT abcxyz
-
-
-
+stdout.write "\n"
diff --git a/tests/iter/titer6.nim b/tests/iter/titer6.nim
index b7c8fee80..69a10d868 100644
--- a/tests/iter/titer6.nim
+++ b/tests/iter/titer6.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "titer6.nim"
   output: "000"
 """
 # Test iterator with more than 1 yield statement
@@ -33,5 +32,4 @@ proc wordWrap2(s: string, maxLineWidth = 80,
   for word, isSep in tokenize2(s, seps):
     var w = 0
 
-
-
+stdout.write "\n"
diff --git a/tests/iter/titer7.nim b/tests/iter/titer7.nim
index c2bd9b9cb..9cba3038d 100644
--- a/tests/iter/titer7.nim
+++ b/tests/iter/titer7.nim
@@ -45,9 +45,9 @@ proc square(x:int): int =
 let list = @[1,2,3,4,5,6,7,8,9]
 
 echo ("--- evens")
-for item in list / isEven : echo(item)
+for item in list / isEven: echo(item)
 echo ("--- squares")
-for item in list >> square : echo(item)
+for item in list >> square: echo(item)
 #echo ("--- squares of evens, only")
 # next line doesn't compile. Generic types are not inferred
 #for item in list />> (isEven, square) : echo(item)
diff --git a/tests/iter/titer_issues.nim b/tests/iter/titer_issues.nim
new file mode 100644
index 000000000..c82b3902d
--- /dev/null
+++ b/tests/iter/titer_issues.nim
@@ -0,0 +1,411 @@
+discard """
+  target: "c js"
+  output: '''
+0
+1
+2
+3
+4
+1
+start
+false
+0
+1
+2
+end
+@[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 18, 20, 21, 24, 27, 30, 36, 40, 42]
+1002
+0
+1
+2
+7
+9002
+9004
+9006
+9008
+9010
+9012
+9014
+9016
+9018
+@[1, 2]
+@[1, 2, 3]
+1
+nested finally
+outer finally
+nested finally
+outer finally
+nested finally
+outer finally
+nested finally
+outer finally
+In defer
+trying
+exception caught
+finally block
+'''
+"""
+
+
+import sequtils, strutils
+
+
+block t338:
+  proc moo(): iterator (): int =
+    iterator fooGen: int {.closure.} =
+      while true:
+        yield result
+        result.inc
+    return fooGen
+
+  var foo = moo()
+
+  for i in 0 .. 4:
+    echo foo()
+
+
+
+block t8041:
+  iterator xy[T](a: T, b: set[T]): T =
+    if a in b:
+      yield a
+
+  for a in xy(1'i8, {}):
+    for b in xy(a, {}):
+      echo a
+
+
+
+block t3837_chained:
+  iterator t1(): int {.closure.} =
+    yield 1
+
+  iterator t2(): int {.closure.} =
+    for i in t1():
+      yield i
+
+  for i in t2():
+    echo $i
+
+
+  proc iter1(): (iterator: int) =
+    let coll = [0,1,2]
+    result = iterator: int {.closure.} =
+      for i in coll:
+        yield i
+
+  proc iter2(it: (iterator: int)): (iterator: int)  =
+    result = iterator: int {.closure.} =
+      echo finished(it)
+      for i in it():
+        yield i
+
+  echo "start"
+  let myiter1 = iter1()
+  let myiter2 = iter2(myiter1)
+  for i in myiter2():
+    echo i
+  echo "end"
+
+
+  type Iterable[T] = (iterator: T) | Slice[T]
+    ## Everything that can be iterated over, iterators and slices so far.
+
+  proc toIter[T](s: Slice[T]): iterator: T =
+    ## Iterate over a slice.
+    iterator it: T {.closure.} =
+      for x in s.a..s.b:
+        yield x
+    return it
+
+  proc toIter[T](i: iterator: T): iterator: T =
+    ## Nop
+    i
+
+  iterator map[T,S](i: Iterable[T], f: proc(x: T): S): S =
+    let i = toIter(i)
+    for x in i():
+      yield f(x)
+
+  proc filter[T](i: Iterable[T], f: proc(x: T): bool): iterator: T =
+    let i = toIter(i)
+    iterator it: T {.closure.} =
+      for x in i():
+        if f(x):
+          yield x
+    result = it
+
+  iterator filter[T](i: Iterable[T], f: proc(x: T): bool): T =
+    let i = toIter(i)
+    for x in i():
+      if f(x):
+        yield x
+
+  var it = toSeq(filter(2..10, proc(x: int): bool = x mod 2 == 0))
+  doAssert it == @[2, 4, 6, 8, 10]
+  it = toSeq(map(filter(2..10, proc(x: int): bool = x mod 2 == 0), proc(x: int): int = x * 2))
+  doAssert it == @[4, 8, 12, 16, 20]
+
+
+
+block t3221_complex:
+  iterator permutations[T](ys: openArray[T]): seq[T] =
+    var
+      d = 1
+      c = newSeq[int](ys.len)
+      xs = newSeq[T](ys.len)
+    for i, y in ys: xs[i] = y
+    yield xs
+    block outer:
+      while true:
+        while d > 1:
+          dec d
+          c[d] = 0
+        while c[d] >= d:
+          inc d
+          if d >= ys.len: break outer
+        let i = if (d and 1) == 1: c[d] else: 0
+        swap xs[i], xs[d]
+        yield xs
+        inc c[d]
+
+  proc dig_vectors(): void =
+    var v_nums: seq[int]
+    v_nums = newSeq[int](1)
+    for perm in permutations(toSeq(0 .. 1)):
+      v_nums[0] = 1
+
+  dig_vectors()
+
+
+
+block t3499_keepstate:
+  proc slice[T](iter: iterator(): T {.closure.}, sl: auto): seq[T] =
+    var res: seq[int64] = @[]
+    var i = 0
+    for n in iter():
+      if i > sl.b:
+        break
+      if i >= sl.a:
+        res.add(n)
+      inc i
+    res
+
+  iterator harshad(): int64 {.closure.} =
+    for n in 1 ..< int64.high:
+      var sum = 0
+      for ch in string($n):
+        sum += parseInt("" & ch)
+      if n mod sum == 0:
+        yield n
+
+  echo harshad.slice 0 ..< 20
+
+  for n in harshad():
+    if n > 1000:
+      echo n
+      break
+
+  # bug #3499 last snippet fixed
+  # bug #705  last snippet fixed
+
+
+
+block t1725_nested:
+  iterator factory(): int {.closure.} =
+    iterator bar(): int {.closure.} =
+      yield 0
+      yield 1
+      yield 2
+
+    for x in bar(): yield x
+
+  for x in factory():
+    echo x
+
+
+
+block t2023_objiter:
+  type
+    Obj = object
+      iter: iterator (): int8 {.closure.}
+
+  iterator test(): int8 {.closure.} =
+    yield 7
+
+  proc init():Obj=
+    result.iter = test
+
+  var o = init()
+  echo(o.iter())
+
+
+block:
+  # bug #13739
+  iterator myIter(arg: openArray[int]): int =
+    var tmp = 0
+    let len = arg.len
+    while tmp < len:
+      yield arg[tmp] * 2
+      inc tmp
+
+  proc someProc() =
+    var data = [4501,4502,4503,4504,4505,4506,4507,4508,4509]
+    # StmtListExpr should not get special treatment.
+    for x in myIter((discard;data)):
+      echo x
+
+  someProc()
+
+block:
+  # bug #12576
+  iterator ff(sq: varargs[seq[int]]): int =
+    for x in sq:
+      echo x
+
+  for x in ff(@[1, 2], @[1, 2, 3]):
+    echo x
+
+
+# bug #19575
+
+iterator bb() {.closure.} =
+    while true:
+        try: discard
+        except: break
+        finally: break
+
+var a = bb
+
+iterator cc() {.closure.} =
+    while true:
+        try: discard
+        except:
+          if true:
+            break
+        finally:
+          if true:
+            break
+
+var a2 = cc
+
+# bug #16876
+block:
+  iterator a(num: int): int {.closure.} =
+      if num == 1:
+          yield num
+      else:
+          for i in a(num - 1):
+              yield i
+
+  for i in a(5):
+    echo i
+
+block:
+  # bug #19911 (return in nested try)
+
+  # try yield -> try
+  iterator p1: int {.closure.} =
+    try:
+      yield 0
+      try:
+        return
+      finally:
+        echo "nested finally"
+      echo "shouldn't run"
+    finally:
+      echo "outer finally"
+    echo "shouldn't run"
+
+  for _ in p1():
+    discard
+
+  # try -> try yield
+  iterator p2: int {.closure.} =
+    try:
+      try:
+        yield 0
+        return
+      finally:
+        echo "nested finally"
+      echo "shouldn't run"
+    finally:
+      echo "outer finally"
+    echo "shouldn't run"
+
+  for _ in p2():
+    discard
+
+  # try yield -> try yield
+  iterator p3: int {.closure.} =
+    try:
+      yield 0
+      try:
+        yield 0
+        return
+      finally:
+        echo "nested finally"
+      echo "shouldn't run"
+    finally:
+      echo "outer finally"
+    echo "shouldn't run"
+
+  for _ in p3():
+    discard
+
+  # try -> try
+  iterator p4: int {.closure.} =
+    try:
+      try:
+        return
+      finally:
+        echo "nested finally"
+      echo "shouldn't run"
+    finally:
+      echo "outer finally"
+    echo "shouldn't run"
+
+  for _ in p4():
+    discard
+
+# bug #18824
+iterator poc_iterator: int {.closure.}  =
+  block bug18824:
+    try:
+      break bug18824
+    finally:
+      echo "In defer"
+
+for _ in poc_iterator():
+  discard
+
+# bug #20624
+iterator tryFinally() {.closure.} =
+  block route:
+    try:
+      echo "trying"
+      raise
+    except:
+      echo "exception caught"
+      break route
+    finally:
+      echo "finally block"
+
+var x = tryFinally
+x()
+
+block: # bug #24033
+  type Query = ref object
+
+  iterator pairs(query: Query): (int, (string, float32)) =
+    var output: (int, (string, float32)) = (0, ("foo", 3.14))
+    for id in @[0, 1, 2]:
+      output[0] = id
+      yield output
+
+  var collections: seq[(int, string, string)]
+
+  for id, (str, num) in Query():
+    collections.add (id, str, $num)
+
+  doAssert collections[1] == (1, "foo", "3.14")
diff --git a/tests/iter/titer_no_tuple_unpack.nim b/tests/iter/titer_no_tuple_unpack.nim
index 13ec11bd6..d8df10189 100644
--- a/tests/iter/titer_no_tuple_unpack.nim
+++ b/tests/iter/titer_no_tuple_unpack.nim
@@ -1,3 +1,18 @@
+discard """
+output: '''
+3 4
+4 5
+5 6
+6 7
+7 8
+(x: 3, y: 4)
+(x: 4, y: 5)
+(x: 5, y: 6)
+(x: 6, y: 7)
+(x: 7, y: 8)
+'''
+"""
+
 
 iterator xrange(fromm, to: int, step = 1): tuple[x, y: int] =
   var a = fromm
@@ -10,4 +25,3 @@ for a, b in xrange(3, 7):
 
 for tup in xrange(3, 7):
   echo tup
-
diff --git a/tests/iter/titerable.nim b/tests/iter/titerable.nim
index fc1a8f934..f503836d3 100644
--- a/tests/iter/titerable.nim
+++ b/tests/iter/titerable.nim
@@ -14,7 +14,7 @@ discard """
 iterator map[T, U](s: iterator:T{.inline.}, f: proc(x: T): U): U =
   for e in s: yield f(e)
 
-template toSeq(s: expr): expr =
+template toSeq(s: untyped): untyped =
   var res = newSeq[type(s)](0)
   for e in s: res.add(e)
   res
diff --git a/tests/iter/titerautoerr1.nim b/tests/iter/titerautoerr1.nim
new file mode 100644
index 000000000..c7e5642c8
--- /dev/null
+++ b/tests/iter/titerautoerr1.nim
@@ -0,0 +1,8 @@
+discard """
+  errormsg: "type mismatch: got <int literal(1)> but expected 'string'"
+  line: 8
+"""
+
+iterator a(): auto {.closure.} =
+  if true: return "str"
+  yield 1
diff --git a/tests/iter/titerautoerr2.nim b/tests/iter/titerautoerr2.nim
new file mode 100644
index 000000000..eb48a36f9
--- /dev/null
+++ b/tests/iter/titerautoerr2.nim
@@ -0,0 +1,8 @@
+discard """
+  errormsg: "type mismatch: got <string> but expected 'int literal(1)'"
+  line: 8
+"""
+
+iterator b(): auto {.closure.} =
+  yield 1
+  if true: return "str"
diff --git a/tests/iter/titerautoerr3.nim b/tests/iter/titerautoerr3.nim
new file mode 100644
index 000000000..e47b65540
--- /dev/null
+++ b/tests/iter/titerautoerr3.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "cannot infer the return type of 'bar'"
+  line: 6
+"""
+
+iterator bar(): auto =
+  discard
+for t in bar():
+  discard
diff --git a/tests/iter/tconcat.nim b/tests/iter/titerconcat.nim
index 477ac5e26..477ac5e26 100644
--- a/tests/iter/tconcat.nim
+++ b/tests/iter/titerconcat.nim
diff --git a/tests/iter/titertypedesc.nim b/tests/iter/titertypedesc.nim
new file mode 100644
index 000000000..a69f703c6
--- /dev/null
+++ b/tests/iter/titertypedesc.nim
@@ -0,0 +1,17 @@
+discard """
+  output: '''0
+(id: 0)
+@[]
+[0, 0, 0]'''
+"""
+
+iterator foo*(T: typedesc): T =
+  var x: T
+  yield x
+
+for a in foo(int): echo a
+for b in foo(tuple[id: int]): echo b
+for c in foo(seq[int]): echo c
+
+type Generic[T] = T
+for d in foo(Generic[array[0..2, int]]): echo d
diff --git a/tests/iter/titervaropenarray.nim b/tests/iter/titervaropenarray.nim
index 1e70ce247..b2fe71ceb 100644
--- a/tests/iter/titervaropenarray.nim
+++ b/tests/iter/titervaropenarray.nim
@@ -1,6 +1,6 @@
 discard """
-  file: "titer2.nim"
   output: "123"
+  targets: "c cpp"
 """
 # Try to break the transformation pass:
 iterator iterAndZero(a: var openArray[int]): int =
@@ -12,5 +12,4 @@ var x = [[1, 2, 3], [4, 5, 6]]
 for y in iterAndZero(x[0]): write(stdout, $y)
 #OUT 123
 
-
-
+write stdout, "\n"
diff --git a/tests/iter/tkeep_state_between_yield.nim b/tests/iter/tkeep_state_between_yield.nim
deleted file mode 100644
index f4f0ee363..000000000
--- a/tests/iter/tkeep_state_between_yield.nim
+++ /dev/null
@@ -1,36 +0,0 @@
-discard """
-  output: '''@[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 18, 20, 21, 24, 27, 30, 36, 40, 42]
-1002'''
-"""
-
-import strutils
-
-proc slice[T](iter: iterator(): T {.closure.}, sl: auto): seq[T] =
-  var res: seq[int64] = @[]
-  var i = 0
-  for n in iter():
-    if i > sl.b:
-      break
-    if i >= sl.a:
-      res.add(n)
-    inc i
-  res
-
-iterator harshad(): int64 {.closure.} =
-  for n in 1 .. < int64.high:
-    var sum = 0
-    for ch in string($n):
-      sum += parseInt("" & ch)
-    if n mod sum == 0:
-      yield n
-
-echo harshad.slice 0 .. <20
-
-for n in harshad():
-  if n > 1000:
-    echo n
-    break
-
-
-# bug #3499 last snippet fixed
-# bug 705  last snippet fixed
diff --git a/tests/iter/tmoditer.nim b/tests/iter/tmoditer.nim
index 1e6be37e4..99e5b642d 100644
--- a/tests/iter/tmoditer.nim
+++ b/tests/iter/tmoditer.nim
@@ -4,7 +4,7 @@ discard """
 
 iterator modPairs(a: var array[0..4,string]): tuple[key: int, val: var string] =
   for i in 0..a.high:
-    yield (i, a[i])
+    yield (key: i, val: a[i])
 
 iterator modItems*[T](a: var array[0..4,T]): var T =
   for i in 0..a.high:
@@ -27,3 +27,62 @@ for a in items(arr):
 
 echo ""
 
+#--------------------------------------------------------------------
+# Lent iterators
+#--------------------------------------------------------------------
+type
+  NonCopyable = object
+    x: int
+
+
+proc `=destroy`(o: var NonCopyable) =
+  discard
+
+proc `=copy`(dst: var NonCopyable, src: NonCopyable) {.error.}
+
+proc `=sink`(dst: var NonCopyable, src: NonCopyable) =
+  dst.x = src.x
+
+iterator lentItems[T](a: openArray[T]): lent T =
+  for i in 0..a.high:
+    yield a[i]
+
+iterator lentPairs[T](a: array[0..1, T]): tuple[key: int, val: lent T] =
+  for i in 0..a.high:
+    yield (key: i, val: a[i])
+
+
+let arr1 = [1, 2, 3]
+let arr2 = @["a", "b", "c"]
+let arr3 = [NonCopyable(x: 1), NonCopyable(x: 2)]
+let arr4 = @[(1, "a"), (2, "b"), (3, "c")]
+
+var accum: string
+for x in lentItems(arr1):
+  accum &= $x
+doAssert(accum == "123")
+
+accum = ""
+for x in lentItems(arr2):
+  accum &= $x
+doAssert(accum == "abc")
+
+accum = ""
+for val in lentItems(arr3):
+  accum &= $val.x
+doAssert(accum == "12")
+
+accum = ""
+for i, val in lentPairs(arr3):
+  accum &= $i & "-" & $val.x & " "
+doAssert(accum == "0-1 1-2 ")
+
+accum = ""
+for i, val in lentItems(arr4):
+  accum &= $i & "-" & $val & " "
+doAssert(accum == "1-a 2-b 3-c ")
+
+accum = ""
+for (i, val) in lentItems(arr4):
+  accum &= $i & "-" & $val & " "
+doAssert(accum == "1-a 2-b 3-c ")
diff --git a/tests/iter/tnested_closure_iter.nim b/tests/iter/tnested_closure_iter.nim
deleted file mode 100644
index ec2253cf1..000000000
--- a/tests/iter/tnested_closure_iter.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-discard """
-  output: '''0
-1
-2'''
-"""
-# bug #1725
-iterator factory(): int {.closure.} =
-  iterator bar(): int {.closure.} =
-    yield 0
-    yield 1
-    yield 2
-
-  for x in bar(): yield x
-
-for x in factory():
-  echo x
diff --git a/tests/iter/tobj_iter.nim b/tests/iter/tobj_iter.nim
deleted file mode 100644
index a894755d7..000000000
--- a/tests/iter/tobj_iter.nim
+++ /dev/null
@@ -1,18 +0,0 @@
-discard """
-  output: "7"
-"""
-
-# bug #2023
-
-type
-    Obj = object
-        iter: iterator (): int8 {.closure.}
-
-iterator test(): int8 {.closure.} =
-    yield 7
-
-proc init():Obj=
-    result.iter = test
-
-var o = init()
-echo(o.iter())
diff --git a/tests/iter/tpermutations.nim b/tests/iter/tpermutations.nim
index a3b383323..30a66460f 100644
--- a/tests/iter/tpermutations.nim
+++ b/tests/iter/tpermutations.nim
@@ -1,7 +1,18 @@
+discard """
+output: '''
+@[@[1.0, 2.0], @[3.0, 4.0]]
+perm: 10.0 det: -2.0
+@[@[1.0, 2.0, 3.0, 4.0], @[4.0, 5.0, 6.0, 7.0], @[7.0, 8.0, 9.0, 10.0], @[10.0, 11.0, 12.0, 13.0]]
+perm: 29556.0 det: 0.0
+@[@[0.0, 1.0, 2.0, 3.0, 4.0], @[5.0, 6.0, 7.0, 8.0, 9.0], @[10.0, 11.0, 12.0, 13.0, 14.0], @[15.0, 16.0, 17.0, 18.0, 19.0], @[20.0, 21.0, 22.0, 23.0, 24.0]]
+perm: 6778800.0 det: 0.0
+'''
+"""
 
-import sequtils, future
 
-iterator permutations*[T](ys: openarray[T]): tuple[perm: seq[T], sign: int] =
+import sequtils, sugar
+
+iterator permutations*[T](ys: openArray[T]): tuple[perm: seq[T], sign: int] =
   var
     d = 1
     c = newSeq[int](ys.len)
diff --git a/tests/iter/treciter.nim b/tests/iter/treciter.nim
index dacdbdfd7..11fb58224 100644
--- a/tests/iter/treciter.nim
+++ b/tests/iter/treciter.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "recursion is not supported in iterators: 'myrec'"
   file: "treciter.nim"
   line: 9
-  errormsg: "recursive dependency: \'myrec\'"
 """
 # Test that an error message occurs for a recursive iterator
 
@@ -10,5 +10,3 @@ iterator myrec(n: int): int =
     yield x
 
 for x in myrec(10): echo x
-
-
diff --git a/tests/iter/tshallowcopy_closures.nim b/tests/iter/tshallowcopy_closures.nim
index 2f024ee7e..06b04a788 100644
--- a/tests/iter/tshallowcopy_closures.nim
+++ b/tests/iter/tshallowcopy_closures.nim
@@ -1,5 +1,10 @@
 discard """
+  matrix: "--mm:refc"
   ccodecheck: "!@('{' \\s* 'NI HEX3Astate;' \\s* '}')"
+  output: '''
+a1 10
+a1 9
+'''
 """
 
 # bug #1803
@@ -26,6 +31,6 @@ var
   z: TaskFn
 
 discard x()
-z = x #shallowCopy(z, x)
-z = y #shallowCopy(z, y)
+shallowCopy(z, x)
+shallowCopy(z, y)
 discard x()
diff --git a/tests/iter/twrap_walkdir.nim b/tests/iter/twrap_walkdir.nim
index 4ac487d8e..1d52e9791 100644
--- a/tests/iter/twrap_walkdir.nim
+++ b/tests/iter/twrap_walkdir.nim
@@ -1,5 +1,6 @@
-
-
+discard """
+action: compile
+"""
 
 import os
 
diff --git a/tests/iter/twrongiter.nim b/tests/iter/twrongiter.nim
index 33394219b..577b8c4f1 100644
--- a/tests/iter/twrongiter.nim
+++ b/tests/iter/twrongiter.nim
@@ -1,6 +1,6 @@
 discard """
-line: 12
 errormsg: "type mismatch"
+line: 12
 """
 
 proc first(it: iterator(): int): seq[int] =
diff --git a/tests/iter/tyieldintry.nim b/tests/iter/tyieldintry.nim
index 6f0acb169..e51ab7f0d 100644
--- a/tests/iter/tyieldintry.nim
+++ b/tests/iter/tyieldintry.nim
@@ -1,15 +1,16 @@
 discard """
-targets: "c cpp"
-output: "ok"
+  matrix: "; --experimental:strictdefs; -d:nimOptIters"
+  targets: "c cpp"
 """
+
 var closureIterResult = newSeq[int]()
 
 proc checkpoint(arg: int) =
   closureIterResult.add(arg)
 
 type
-  TestException = object of Exception
-  AnotherException = object of Exception
+  TestError = object of CatchableError
+  AnotherError = object of CatchableError
 
 proc testClosureIterAux(it: iterator(): int, exceptionExpected: bool, expectedResults: varargs[int]) =
   closureIterResult.setLen(0)
@@ -19,7 +20,7 @@ proc testClosureIterAux(it: iterator(): int, exceptionExpected: bool, expectedRe
   try:
     for i in it():
       closureIterResult.add(i)
-  except TestException:
+  except TestError:
     exceptionCaught = true
 
   if closureIterResult != @expectedResults or exceptionCaught != exceptionExpected:
@@ -37,8 +38,8 @@ proc test(it: iterator(): int, expectedResults: varargs[int]) =
 proc testExc(it: iterator(): int, expectedResults: varargs[int]) =
   testClosureIterAux(it, true, expectedResults)
 
-proc raiseException() =
-  raise newException(TestException, "Test exception!")
+proc raiseTestError() =
+  raise newException(TestError, "Test exception!")
 
 block:
   iterator it(): int {.closure.} =
@@ -56,8 +57,8 @@ block:
     yield 0
     try:
       checkpoint(1)
-      raiseException()
-    except TestException:
+      raiseTestError()
+    except TestError:
       checkpoint(2)
       yield 3
       checkpoint(4)
@@ -87,7 +88,7 @@ block:
     yield 0
     try:
       yield 1
-      raiseException()
+      raiseTestError()
       yield 2
     finally:
       checkpoint(3)
@@ -101,8 +102,8 @@ block:
   iterator it(): int {.closure.} =
     try:
       try:
-        raiseException()
-      except AnotherException:
+        raiseTestError()
+      except AnotherError:
         yield 123
       finally:
         checkpoint(3)
@@ -115,8 +116,8 @@ block:
   iterator it(): int {.closure.} =
     try:
       yield 1
-      raiseException()
-    except AnotherException:
+      raiseTestError()
+    except AnotherError:
       checkpoint(123)
     finally:
       checkpoint(2)
@@ -132,12 +133,12 @@ block:
         yield 1
         try:
           yield 2
-          raiseException()
-        except AnotherException:
+          raiseTestError()
+        except AnotherError:
           yield 123
         finally:
           yield 3
-      except AnotherException:
+      except AnotherError:
         yield 124
       finally:
         yield 4
@@ -168,10 +169,10 @@ block:
     try:
       try:
         yield 0
-        raiseException()
+        raiseTestError()
       finally:
         checkpoint(1)
-    except TestException:
+    except TestError:
       yield 2
       return
     finally:
@@ -186,10 +187,10 @@ block:
     try:
       try:
         yield 0
-        raiseException()
+        raiseTestError()
       finally:
         return # Return in finally should stop exception propagation
-    except AnotherException:
+    except AnotherError:
       yield 2
       return
     finally:
@@ -226,9 +227,9 @@ block:
     var foo = 123
     let i = try:
         yield 0
-        raiseException()
+        raiseTestError()
         1
-      except TestException as e:
+      except TestError as e:
         assert(e.msg == "Test exception!")
         case foo
         of 1:
@@ -260,9 +261,9 @@ block:
     template tryexcept: int =
       try:
         yield 1
-        raiseException()
+        raiseTestError()
         123
-      except TestException:
+      except TestError:
         yield 2
         checkpoint(3)
         4
@@ -321,11 +322,11 @@ block:
     for i in 0 .. 1:
       try:
         yield 1
-        raiseException()
-      except TestException as e:
+        raiseTestError()
+      except TestError as e:
         doAssert(e.msg == "Test exception!")
         yield 2
-      except AnotherException:
+      except AnotherError:
         yield 123
       except:
         yield 1234
@@ -403,5 +404,126 @@ block: # yield in blockexpr
 
   test(it, 1, 2, 3)
 
+block: #8851
+  type
+    Foo = ref object of RootObj
+  template someFoo(): Foo =
+    var f: Foo
+    yield 1
+    f
+  iterator it(): int {.closure.} =
+    var o: RootRef
+    o = someFoo()
+
+  test(it, 1)
+
+block: # 8243
+  iterator it(): int {.closure.} =
+    template yieldAndSeq: seq[int] =
+      yield 1
+      @[123, 5, 123]
+
+    checkpoint(yieldAndSeq[1])
+
+  test(it, 1, 5)
+
+block:
+  iterator it(): int {.closure.} =
+    template yieldAndSeq: seq[int] =
+      yield 1
+      @[123, 5, 123]
+
+    template yieldAndNum: int =
+      yield 2
+      1
+
+    checkpoint(yieldAndSeq[yieldAndNum])
+
+  test(it, 1, 2, 5)
+
+block: #9694 - yield in ObjConstr
+  type Foo = object
+    a, b: int
+
+  template yieldAndNum: int =
+    yield 1
+    2
+
+  iterator it(): int {.closure.} =
+    let a = Foo(a: 5, b: yieldAndNum())
+    checkpoint(a.b)
+
+  test(it, 1, 2)
+
+block: #9716
+  iterator it(): int {.closure.} =
+    var a = 0
+    for i in 1 .. 3:
+      var a: int # Make sure the "local" var is reset
+      var b: string # ditto
+      yield 1
+      a += 5
+      b &= "hello"
+      doAssert(a == 5)
+      doAssert(b == "hello")
+  test(it, 1, 1, 1)
+
+block: # nnkChckRange
+  type Foo = distinct uint64
+  template yieldDistinct: Foo =
+    yield 2
+    Foo(0)
+
+  iterator it(): int {.closure.} =
+    yield 1
+    var a: int
+    a = int(yieldDistinct())
+    yield 3
+
+  test(it, 1, 2, 3)
+
+block: #17849 - yield in case subject
+  template yieldInCase: int =
+    yield 2
+    3
+
+  iterator it(): int {.closure.} =
+    yield 1
+    case yieldInCase()
+    of 1: checkpoint(11)
+    of 3: checkpoint(13)
+    else: checkpoint(14)
+    yield 5
 
-echo "ok"
+  test(it, 1, 2, 13, 5)
+
+block: # void iterator
+  iterator it() {.closure.} =
+    try:
+      yield
+    except:
+      discard
+  var a = it
+
+if defined(nimOptIters): # Locals present in only 1 state should be on the stack
+  proc checkOnStack(a: pointer, shouldBeOnStack: bool) =
+    # Quick and dirty way to check if a points to stack
+    var dummy = 0
+    let dummyAddr = addr dummy
+    let distance = abs(cast[int](dummyAddr) - cast[int](a))
+    const requiredDistance = 300
+    if shouldBeOnStack:
+      doAssert(distance <= requiredDistance, "a is not on stack, but should")
+    else:
+      doAssert(distance > requiredDistance, "a is on stack, but should not")
+
+  iterator it(): int {.closure.} =
+    var a = 1
+    var b = 2
+    var c {.liftLocals.} = 3
+    checkOnStack(addr a, true)
+    checkOnStack(addr b, false)
+    checkOnStack(addr c, false)
+    yield a
+    yield b
+  test(it, 1, 2)
diff --git a/tests/js/readme.md b/tests/js/readme.md
new file mode 100644
index 000000000..7fcc722fa
--- /dev/null
+++ b/tests/js/readme.md
@@ -0,0 +1,5 @@
+## notes
+Prefer moving tests to a non-js directory so that they get tested across all backends automatically.
+Ideally, tests/js should be reserved to code that only makes sense in js.
+
+Note also that tests for a js specific module (e.g.: `std/jsbigints`) belong to `tests/stdlib`, (e.g.: `tests/stdlib/tjsbigints.nim`)
diff --git a/tests/js/t11166.nim b/tests/js/t11166.nim
new file mode 100644
index 000000000..e98ccda10
--- /dev/null
+++ b/tests/js/t11166.nim
@@ -0,0 +1,20 @@
+discard """
+  output: '''
+test1
+test2
+'''
+"""
+
+import jsffi
+
+type
+  C = object
+    props: int
+
+var c: C
+
+when compiles(c.props):
+  echo "test1"
+
+when not compiles(d.props):
+  echo "test2"
diff --git a/tests/js/t11353.nim b/tests/js/t11353.nim
new file mode 100644
index 000000000..c1bc0ad4b
--- /dev/null
+++ b/tests/js/t11353.nim
@@ -0,0 +1,14 @@
+discard """
+  output: '''
+{}
+{}
+'''
+"""
+
+proc foo() =
+  var bar: set[int16] = {}
+  echo bar
+  bar.incl(1)
+
+foo()
+foo()
diff --git a/tests/js/t11354.nim b/tests/js/t11354.nim
new file mode 100644
index 000000000..8dee90de0
--- /dev/null
+++ b/tests/js/t11354.nim
@@ -0,0 +1,20 @@
+discard """
+  output: '''
+0
+@[@[0, 1]]
+'''
+"""
+
+type
+  TrackySeq[T] = object
+    s: seq[T]
+    pos: int
+
+proc foobar(ls: var TrackySeq[seq[int]], i: int): var seq[int] =
+  echo ls.pos  # removing this, or making the return explicit works
+  ls.s[i]
+
+var foo: TrackySeq[seq[int]]
+foo.s.add(@[0])
+foo.foobar(0).add(1)
+echo foo.s
\ No newline at end of file
diff --git a/tests/js/t11697.nim b/tests/js/t11697.nim
new file mode 100644
index 000000000..967752586
--- /dev/null
+++ b/tests/js/t11697.nim
@@ -0,0 +1,5 @@
+import tables
+
+var xs: Table[int, Table[int, int]]
+
+doAssertRaises(KeyError): reset xs[0]
diff --git a/tests/js/t12223.nim b/tests/js/t12223.nim
new file mode 100644
index 000000000..c0e75fb44
--- /dev/null
+++ b/tests/js/t12223.nim
@@ -0,0 +1,20 @@
+discard """
+  action: "run"
+  output: '''
+caught
+index out of bounds, the container is empty
+'''
+"""
+
+proc fun() =
+  var z: seq[string]
+  discard z[4]
+
+proc main()=
+  try:
+    fun()
+  except Exception as e:
+    echo "caught"
+    echo getCurrentExceptionMsg()
+
+main()
\ No newline at end of file
diff --git a/tests/js/t12303.nim b/tests/js/t12303.nim
new file mode 100644
index 000000000..270d82ced
--- /dev/null
+++ b/tests/js/t12303.nim
@@ -0,0 +1,16 @@
+discard """
+  output: "{ b: 2 }"
+"""
+
+import jsconsole, jsffi
+
+type
+  A = ref object
+   b: B
+
+  B = object
+    b: int
+
+var a = cast[A](js{})
+a.b = B(b: 2)
+console.log a.b
diff --git a/tests/js/t12672.nim b/tests/js/t12672.nim
new file mode 100644
index 000000000..a658fbcbe
--- /dev/null
+++ b/tests/js/t12672.nim
@@ -0,0 +1,12 @@
+discard """
+  output: ""
+"""
+
+proc foo =
+  var x: seq[seq[int]]
+  for row in x.mitems:
+    let i = 1
+    echo row
+    inc row[i-1]
+
+foo()
diff --git a/tests/js/t14153.nim b/tests/js/t14153.nim
new file mode 100644
index 000000000..350bbd83b
--- /dev/null
+++ b/tests/js/t14153.nim
@@ -0,0 +1,22 @@
+discard """
+  output: '''
+index 5 not in 0 .. 2
+index 5 not in 0 .. 2
+'''
+"""
+
+var x = @[1, 2, 3]
+
+try:
+  echo x[5]
+except IndexError:
+  echo getCurrentExceptionMsg()
+except:
+  doAssert false
+
+try:
+  x[5] = 8
+except IndexError:
+  echo getCurrentExceptionMsg()
+except:
+  doAssert false
diff --git a/tests/js/t14570.nim b/tests/js/t14570.nim
new file mode 100644
index 000000000..100c2651d
--- /dev/null
+++ b/tests/js/t14570.nim
@@ -0,0 +1,11 @@
+discard """
+  output: '''
+18
+'''
+"""
+
+type A = range[15 .. 30]
+
+let a: A = 18
+
+echo ord(a)
diff --git a/tests/js/t17177.nim b/tests/js/t17177.nim
new file mode 100644
index 000000000..fc362cec1
--- /dev/null
+++ b/tests/js/t17177.nim
@@ -0,0 +1,10 @@
+import std/asyncjs
+
+proc fn1(n: int): Future[int] {.async.} = return n
+proc main2() =
+  proc fn2(n: int): Future[int] {.async.} = return n
+proc main3(a: auto) =
+  proc fn3(n: int): Future[int] {.async.} = return n
+proc main4() {.async.} =
+  proc fn4(n: int): Future[int] {.async.} = return n
+  discard
diff --git a/tests/js/t20233.nim b/tests/js/t20233.nim
new file mode 100644
index 000000000..401d14122
--- /dev/null
+++ b/tests/js/t20233.nim
@@ -0,0 +1,7 @@
+discard """
+  output: "yes"
+"""
+case 1.0
+of 1.0..2.0, 4.0: echo "yes"
+of 3.0: discard
+else: echo "no"
\ No newline at end of file
diff --git a/tests/js/t20235.nim b/tests/js/t20235.nim
new file mode 100644
index 000000000..3a69c2bd6
--- /dev/null
+++ b/tests/js/t20235.nim
@@ -0,0 +1,11 @@
+discard """
+  action: "run"
+  output: "0 4"
+"""
+
+proc main =
+  var s = ""
+  s.setLen(4)
+  echo s[0].ord, " ", s.len
+
+main()
diff --git a/tests/js/t21209.nim b/tests/js/t21209.nim
new file mode 100644
index 000000000..4de34f035
--- /dev/null
+++ b/tests/js/t21209.nim
@@ -0,0 +1,6 @@
+discard """
+  action: "compile"
+  cmd: "nim check --warning[UnusedImport]:off $file"
+"""
+
+import std/times
diff --git a/tests/js/t21209.nims b/tests/js/t21209.nims
new file mode 100644
index 000000000..318e28f97
--- /dev/null
+++ b/tests/js/t21209.nims
@@ -0,0 +1 @@
+--b:js
\ No newline at end of file
diff --git a/tests/js/t21247.nim b/tests/js/t21247.nim
new file mode 100644
index 000000000..5b38787b3
--- /dev/null
+++ b/tests/js/t21247.nim
@@ -0,0 +1,15 @@
+import std/typetraits
+
+type
+  QueryParams* = distinct seq[(string, string)]
+
+converter toBase*(params: var QueryParams): var seq[(string, string)] =
+  params.distinctBase
+
+proc foo(): QueryParams =
+  # Issue was that the implicit converter call didn't say that it took the
+  # address of the parameter it was converting. This led to the parameter not being
+  # passed as a fat pointer which toBase expected
+  result.add(("hello", "world"))
+
+assert foo().distinctBase() == @[("hello", "world")]
diff --git a/tests/js/t21439.nim b/tests/js/t21439.nim
new file mode 100644
index 000000000..3caeb090a
--- /dev/null
+++ b/tests/js/t21439.nim
@@ -0,0 +1,11 @@
+proc test(a: openArray[string]): proc =
+  let a = @a
+  result = proc =
+    for i in a:
+      discard i
+
+
+const a = ["t1", "t2"]
+
+discard test(a)
+
diff --git a/tests/js/t6612.nim b/tests/js/t6612.nim
new file mode 100644
index 000000000..232711c16
--- /dev/null
+++ b/tests/js/t6612.nim
@@ -0,0 +1,24 @@
+discard """
+  action: "run"
+"""
+
+proc fillWith(sq: var seq[int], n: int, unused: string) =
+  sq = @[n]
+
+type
+  Object = object of RootObj
+    case hasNums: bool
+    of true:
+      numbers: seq[int]
+    of false:
+      discard
+    always: seq[int]
+
+var obj = Object(hasNums: true)
+
+obj.always.fillWith(5, "unused")
+doAssert obj.always == @[5]
+
+obj.numbers.fillWith(3, "unused")
+doAssert obj.numbers == @[3]
+doAssert obj.always == @[5]
diff --git a/tests/js/t7109.nim b/tests/js/t7109.nim
new file mode 100644
index 000000000..a1a3b718e
--- /dev/null
+++ b/tests/js/t7109.nim
@@ -0,0 +1,8 @@
+iterator iter*(): int {.closure.} =
+  yield 3
+
+var x = iter
+doAssert x() == 3
+
+let fIt = iterator(): int = yield 70
+doAssert fIt() == 70
diff --git a/tests/js/t7127.nim b/tests/js/t7127.nim
new file mode 100644
index 000000000..364aedd4a
--- /dev/null
+++ b/tests/js/t7127.nim
@@ -0,0 +1,2 @@
+doAssertRaises(DivByZeroDefect): discard 1 mod 0
+doAssertRaises(DivByZeroDefect): discard 1 div 0
\ No newline at end of file
diff --git a/tests/js/t7224.nim b/tests/js/t7224.nim
new file mode 100644
index 000000000..77fef10a7
--- /dev/null
+++ b/tests/js/t7224.nim
@@ -0,0 +1,28 @@
+discard """
+  cmd: "nim $target $options --stackTrace:on --lineTrace:on $file"
+  outputsub: '''
+t7224.nim(25) at module t7224
+t7224.nim(22) at t7224.aaa
+t7224.nim(19) at t7224.bbb
+t7224.nim(16) at t7224.ccc
+t7224.nim(13) at t7224.ddd
+'''
+"""
+
+proc ddd() =
+  raise newException(IOError, "didn't do stuff")
+
+proc ccc() =
+  ddd()
+
+proc bbb() =
+  ccc()
+
+proc aaa() =
+  bbb()
+
+try:
+  aaa()
+
+except IOError as e:
+  echo getStackTrace(e)
diff --git a/tests/js/t7249.nim b/tests/js/t7249.nim
new file mode 100644
index 000000000..52eee2f7c
--- /dev/null
+++ b/tests/js/t7249.nim
@@ -0,0 +1,21 @@
+discard """
+  output: '''
+a -> 2
+a <- 2
+'''
+"""
+
+import jsffi
+
+var a = JsAssoc[cstring, int]{a: 2}
+
+for z, b in a:
+  echo z, " -> ", b
+
+proc f =
+  var a = JsAssoc[cstring, int]{a: 2}
+
+  for z, b in a:
+    echo z, " <- ", b
+
+f()
diff --git a/tests/js/t7534.nim b/tests/js/t7534.nim
new file mode 100644
index 000000000..64aadb8d6
--- /dev/null
+++ b/tests/js/t7534.nim
@@ -0,0 +1,7 @@
+proc f(x: int): int =
+  result = case x
+    of 1: 2
+    elif x == 2: 3
+    else: 1
+
+doAssert 2 == f(f(f(f(1))))
diff --git a/tests/js/t8231.nim b/tests/js/t8231.nim
new file mode 100644
index 000000000..b0625a621
--- /dev/null
+++ b/tests/js/t8231.nim
@@ -0,0 +1,3 @@
+import strutils
+
+doAssert formatSize(2462056448, '.', bpIEC, false) == "2.293GiB"
\ No newline at end of file
diff --git a/tests/js/t8821.nim b/tests/js/t8821.nim
new file mode 100644
index 000000000..38c88efa8
--- /dev/null
+++ b/tests/js/t8821.nim
@@ -0,0 +1,9 @@
+
+proc isInt32(i: int): bool =
+  case i 
+  of 1 .. 70000:
+    return true
+  else:
+    return false
+
+doAssert isInt32(1) == true
\ No newline at end of file
diff --git a/tests/js/t8914.nim b/tests/js/t8914.nim
new file mode 100644
index 000000000..ff716b42a
--- /dev/null
+++ b/tests/js/t8914.nim
@@ -0,0 +1,12 @@
+discard """
+  output: '''
+@[42]
+@[24, 42]
+'''
+"""
+
+var x = @[42,4242]
+x.delete(1)
+echo x
+x.insert(24)
+echo x
diff --git a/tests/js/t9410.nim b/tests/js/t9410.nim
new file mode 100644
index 000000000..042520dc5
--- /dev/null
+++ b/tests/js/t9410.nim
@@ -0,0 +1,471 @@
+template tests =
+  block:
+    var i = 0
+    i = 2
+
+    var y: ptr int
+    doAssert y == nil
+    doAssert isNil(y)
+    y = i.addr
+    y[] = 3
+    doAssert i == 3
+    doAssert i == y[]
+
+    let z = i.addr
+    z[] = 4
+    doAssert i == 4
+    doAssert i == y[] and y[] == z[]
+
+    var hmm = (a: (b: z))
+    var hmmptr = hmm.a.b.addr
+    hmmptr[][] = 5
+
+    doAssert i == 5
+    doAssert y == z
+    doAssert z == hmmptr[]
+    doAssert 5 == y[] and 5 == z[] and 5 == hmmptr[][]
+
+  block:
+    var someint = 500
+
+    let p: ptr int = someint.addr
+    let tup = (f: p)
+    let tcopy = tup
+    var vtcopy = tcopy
+    p[] = 654
+    doAssert p[] == 654
+    doAssert tup.f[] == 654
+    doAssert tcopy.f[] == 654
+    doAssert vtcopy.f[] == 654
+
+  block:
+    var someint = 500
+
+    var p: ptr int = someint.addr
+    let arr = [p]
+    let arrc = arr
+    p[] = 256
+    doAssert someint == 256
+    doAssert p[] == 256
+    doAssert arr[0][] == 256
+    doAssert arrc[0][] == 256
+
+  block:
+    var someref: ref int
+    new(someref)
+    var someref2 = someref
+
+    var tup1 = (f: someref)
+    tup1.f = someref
+    let tup2 = tup1
+
+    someref[] = 543
+
+    proc passref(r: var ref int): var ref int = r
+    new(passref(someref))
+
+    doAssert someref[] == 0
+    doAssert tup1.f[] == 543
+    doAssert tup2.f[] == 543
+    doAssert someref2[] == 543
+
+  block:
+    type Whatever = object
+      i: ref int
+
+    var someref: ref int
+    new(someref)
+    someref[] = 10
+
+    let w = Whatever(i: someref)
+    var wcopy = w
+
+    someref[] = 20
+
+    doAssert w.i[] == 20
+    doAssert someref[] == 20
+    doAssert wcopy.i[] == 20
+    doAssert w.i == wcopy.i
+    #echo w.i[], " ", someref[], " ", wcopy.i[]
+
+  block:
+    var oneseq: ref seq[ref int]
+    new(oneseq)
+    var aref: ref int
+    new(aref)
+    aref[] = 123
+    let arefs = [aref]
+    oneseq[] &= arefs[0]
+    oneseq[] &= aref
+    aref[] = 222
+    new(aref)
+    doAssert oneseq[0] == oneseq[1]
+    doAssert oneseq[0][] == 222
+    doAssert oneseq[1][] == 222
+    doAssert aref[] == 0
+
+  block:
+    var seqs: ref seq[ref seq[ref int]]
+    new(seqs)
+    seqs[] = newSeq[ref seq[ref int]](1)
+    new(seqs[0])
+    seqs[0][] = newSeq[ref int](0)
+
+    var aref: ref int
+    new aref
+    aref[] = 654
+
+    let arefs = [aref]
+    doAssert arefs[0] == aref
+    seqs[0][] &= arefs[0]
+    seqs[0][] &= aref
+    seqs[0][1][] = 456
+    let seqs2 = seqs
+    let same = seqs2[0][0] == seqs2[0][1]
+    doAssert arefs[0] == aref
+    doAssert aref[] == 456
+    doAssert seqs[].len == 1
+    doAssert seqs[0][].len == 2
+    doAssert seqs[0][0][] == 456
+    doAssert seqs[0][1][] == 456
+    doAssert same
+
+  block:
+    type Obj = object
+      x, y: int
+
+    var objrefs: seq[ref Obj] = @[(ref Obj)(nil), nil, nil]
+    objrefs[2].new
+    objrefs[2][] = Obj(x: 123, y: 321)
+    objrefs[1] = objrefs[2]
+    doAssert objrefs[0] == nil
+    doAssert objrefs[1].y == 321
+    doAssert objrefs[2].y == 321
+    doAssert objrefs[1] == objrefs[2]
+
+  block:
+    var refs: seq[ref string] = @[(ref string)(nil), nil, nil]
+    refs[1].new
+    refs[1][] = "it's a ref!"
+    refs[0] = refs[1]
+    refs[2] = refs[1]
+    new(refs[0])
+    doAssert refs[0][] == ""
+    doAssert refs[1][] == "it's a ref!"
+    doAssert refs[2][] == "it's a ref!"
+    doAssert refs[1] == refs[2]
+
+  block:
+    var retaddr_calls = 0
+    proc retaddr(p: var int): var int =
+      retaddr_calls += 1
+      p
+
+    var tfoo_calls = 0
+    proc tfoo(x: var int) =
+      tfoo_calls += 1
+      x += 10
+      var y = x.addr
+      y[] += 20
+      retaddr(x) += 30
+      let z = retaddr(x).addr
+      z[] += 40
+
+    var ints = @[1, 2, 3]
+    tfoo(ints[1])
+    doAssert retaddr_calls == 2
+    doAssert tfoo_calls == 1
+    doAssert ints[1] == 102
+
+    var tbar_calls = 0
+    proc tbar(x: var int): var int =
+      tbar_calls += 1
+      x
+
+    tbar(ints[2]) += 10
+    tbar(ints[2]) *= 2
+    doAssert tbar_calls == 2
+
+    var tqux_calls = 0
+    proc tqux(x: var int): ptr int =
+      tqux_calls += 1
+      x.addr
+
+    discard tqux(ints[2]) == tqux(ints[2])
+    doAssert tqux_calls == 2
+    doAssert isNil(tqux(ints[2])) == false
+    doAssert tqux_calls == 3
+
+    var tseq_calls = 0
+    proc tseq(x: var seq[int]): var seq[int] =
+      tseq_calls += 1
+      x
+
+    tseq(ints) &= 999
+    doAssert tseq_calls == 1
+    doAssert ints == @[1, 102, 26, 999]
+
+    var rawints = @[555]
+    rawints &= 666
+    doAssert rawints == @[555, 666]
+
+    var resetints_calls = 0
+    proc resetInts(): int =
+      resetints_calls += 1
+      ints = @[0, 0, 0]
+      1
+
+    proc incr(x: var int; b: int): var int =
+      x = x + b
+      x
+
+    var q = 0
+    var qp = q.addr
+    qp[] += 123
+    doAssert q == 123
+    # check order of evaluation
+    doAssert (resetInts() + incr(q, tqux(ints[2])[])) == 124
+
+  block: # reset
+    var calls = 0
+    proc passsomething(x: var int): var int =
+      calls += 1
+      x
+
+    var
+      a = 123
+      b = 500
+      c = a.addr
+    reset(passsomething(a))
+    doAssert calls == 1
+    reset(b)
+    doAssert a == b
+    reset(c)
+    doAssert c == nil
+
+  block: # strings
+    var calls = 0
+    proc stringtest(s: var string): var string =
+      calls += 1
+      s
+
+    var somestr: string
+
+    stringtest(somestr) &= 'a'
+    stringtest(somestr) &= 'b'
+    doAssert calls == 2
+    doAssert somestr == "ab"
+    stringtest(somestr) &= "woot!"
+    doAssert somestr == "abwoot!"
+    doAssert calls == 3
+
+    doAssert stringtest(somestr).len == 7
+    doAssert calls == 4
+    doAssert high(stringtest(somestr)) == 6
+    doAssert calls == 5
+
+    var somestr2: string
+    stringtest(somestr2).setLen(stringtest(somestr).len)
+    doAssert calls == 7
+    doAssert somestr2.len == somestr.len
+
+    var somestr3: string
+    doAssert (somestr3 & "foo") == "foo"
+
+    block:
+      var a, b, c, d: string
+      d = a & b & c
+      doAssert d == ""
+      d = stringtest(a) & stringtest(b) & stringtest(c)
+      doAssert calls == 10
+      doAssert d == ""
+
+  block: # seqs
+    var calls = 0
+    proc seqtest(s: var seq[int]): var seq[int] =
+      calls += 1
+      s
+
+    var someseq: seq[int]
+
+    seqtest(someseq) &= 1
+    seqtest(someseq) &= 2
+    doAssert calls == 2
+    doAssert someseq == @[1, 2]
+    seqtest(someseq) &= @[3, 4, 5]
+    doAssert someseq == @[1, 2, 3, 4, 5]
+    doAssert calls == 3
+
+    doAssert seqtest(someseq).len == 5
+    doAssert calls == 4
+    doAssert high(seqtest(someseq)) == 4
+    doAssert calls == 5
+
+    # genArrayAddr
+    doAssert seqtest(someseq)[2] == 3
+    doAssert calls == 6
+
+    seqtest(someseq).setLen(seqtest(someseq).len)
+    doAssert calls == 8
+
+    var somenilseq: seq[int]
+    seqtest(somenilseq).setLen(3)
+    doAssert calls == 9
+    doAssert somenilseq[1] == 0
+
+    someseq = @[1, 2, 3]
+    doAssert (seqtest(someseq) & seqtest(someseq)) == @[1, 2, 3, 1, 2, 3]
+
+
+  block: # mInc, mDec
+    var calls = 0
+    proc someint(x: var int): var int =
+      calls += 1
+      x
+
+    var x = 10
+
+    inc(someint(x))
+    doAssert x == 11
+    doAssert calls == 1
+
+    dec(someint(x))
+    doAssert x == 10
+    doAssert calls == 2
+
+  block: # uints
+    var calls = 0
+    proc passuint(x: var uint32): var uint32 =
+      calls += 1
+      x
+
+    var u: uint32 = 5
+    passuint(u) += 1
+    doAssert u == 6
+    doAssert calls == 1
+
+    passuint(u) -= 1
+    doAssert u == 5
+    doAssert calls == 2
+
+    passuint(u) *= 2
+    doAssert u == 10
+    doAssert calls == 3
+
+  block: # objs
+    type Thing = ref object
+      x, y: int
+
+    var a, b: Thing
+    a = Thing()
+    b = a
+
+    doAssert a == b
+
+    var calls = 0
+    proc passobj(o: var Thing): var Thing =
+      calls += 1
+      o
+
+    passobj(b) = Thing(x: 123)
+    doAssert calls == 1
+    doAssert a != b
+    doAssert b.x == 123
+
+    var passobjptr_calls = 0
+    proc passobjptr(o: var Thing): ptr Thing =
+      passobjptr_calls += 1
+      o.addr
+
+    passobjptr(b)[] = Thing(x: 234)
+    doAssert passobjptr_calls == 1
+    doAssert a != b
+    doAssert b.x == 234
+    passobjptr(b)[].x = 500
+    doAssert b.x == 500
+
+    var pptr = passobjptr(b)
+    pptr.x += 100
+    doAssert b.x == 600
+
+    proc getuninitptr(): ptr int =
+      return
+
+    doAssert getuninitptr() == nil
+
+  block: # pointer casting
+    var obj = (x: 321, y: 543)
+    var x = 500
+
+    var objptr = obj.addr
+    var xptr = x.addr
+
+    var p1, p2: pointer
+    p1 = cast[pointer](objptr)
+    p2 = cast[pointer](xptr)
+    doAssert p1 != p2
+
+    p1 = cast[pointer](objptr)
+    p2 = cast[pointer](objptr)
+    doAssert p1 == p2
+
+    let objptr2 = cast[type(objptr)](p2)
+    doAssert objptr == objptr2
+
+    p1 = cast[pointer](xptr)
+    p2 = cast[pointer](xptr)
+    doAssert p1 == p2
+
+    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
+          # Error: internal error: genAddr: nkStmtListExpr
+      var calls = 0
+      proc getvarint(x: var openArray[int]): var int =
+        calls += 1
+        if true:
+          x[1]
+        else:
+          x[0]
+
+      var arr = [1, 2, 3]
+      getvarint(arr) += 5
+      doAssert calls == 1
+      doAssert arr[1] == 7
+
+proc tests_in_proc =
+  tests
+
+# since pointers are handled differently in global/local contexts
+# let's just run all of them twice
+tests_in_proc()
+tests
diff --git a/tests/js/taddr.nim b/tests/js/taddr.nim
deleted file mode 100644
index d8ff4c11b..000000000
--- a/tests/js/taddr.nim
+++ /dev/null
@@ -1,79 +0,0 @@
-discard """
-  action: run
-"""
-
-type T = object
-  x: int
-  s: string
-
-var obj: T
-var fieldAddr = addr(obj.x)
-var objAddr = addr(obj)
-
-# Integer tests
-var field = fieldAddr[]
-doAssert field == 0
-
-var objDeref = objAddr[]
-doAssert objDeref.x == 0
-
-# Change value
-obj.x = 42
-
-doAssert field == 0
-doAssert objDeref.x == 0
-
-field = fieldAddr[]
-objDeref = objAddr[]
-
-doAssert field == 42
-doAssert objDeref.x == 42
-
-# String tests
-obj.s = "lorem ipsum dolor sit amet"
-var indexAddr = addr(obj.s[2])
-
-doAssert indexAddr[] == 'r'
-
-indexAddr[] = 'd'
-
-doAssert indexAddr[] == 'd'
-
-doAssert obj.s == "lodem ipsum dolor sit amet"
-
-# Bug #2148
-var x: array[2, int]
-var y = addr x[1]
-
-y[] = 12
-doAssert(x[1] == 12)
-
-type
-  Foo = object
-    bar: int
-
-var foo: array[2, Foo]
-var z = addr foo[1]
-
-z[].bar = 12345
-doAssert(foo[1].bar == 12345)
-
-var t : tuple[a, b: int]
-var pt = addr t[1]
-pt[] = 123
-doAssert(t.b == 123)
-
-#block: # Test "untyped" pointer.
-proc testPtr(p: pointer, a: int) =
-  doAssert(a == 5)
-  (cast[ptr int](p))[] = 124
-var i = 123
-testPtr(addr i, 5)
-doAssert(i == 124)
-
-var someGlobal = 5
-proc getSomeGlobalPtr(): ptr int = addr someGlobal
-let someGlobalPtr = getSomeGlobalPtr()
-doAssert(someGlobalPtr[] == 5)
-someGlobalPtr[] = 10
-doAssert(someGlobal == 10)
diff --git a/tests/js/tarrayboundscheck.nim b/tests/js/tarrayboundscheck.nim
index f0eaeb89d..d8bf8de97 100644
--- a/tests/js/tarrayboundscheck.nim
+++ b/tests/js/tarrayboundscheck.nim
@@ -39,6 +39,12 @@ proc test_arrayboundscheck() =
         echo "month out of bounds: ", idx
     except:
       echo "idx out of bounds: ", i
+  
+  # #13966
+  var negativeIndexed: array[-2..2, int] = [0, 1, 2, 3, 4]
+  negativeIndexed[-1] = 2
+  negativeIndexed[1] = 2
+  doAssert negativeIndexed == [0, 2, 2, 2, 4]
 
 test_arrayboundscheck()
 {.pop.}
\ No newline at end of file
diff --git a/tests/js/tasync.nim b/tests/js/tasync.nim
deleted file mode 100644
index 318237651..000000000
--- a/tests/js/tasync.nim
+++ /dev/null
@@ -1,33 +0,0 @@
-discard """
-  output: '''
-x
-e
-'''
-"""
-
-import asyncjs
-
-# demonstrate forward definition
-# for js
-proc y(e: int): Future[string] {.async.}
-
-proc e: int {.discardable.} =
-  echo "e"
-  return 2
-
-proc x(e: int): Future[void] {.async.} =
-  var s = await y(e)
-  if e > 2:
-    return
-  echo s
-  e()
-
-proc y(e: int): Future[string] {.async.} =
-  if e > 0:
-    return await y(0)
-  else:
-    return "x"
-
-
-discard x(2)
-
diff --git a/tests/js/tasync_pragma.nim b/tests/js/tasync_pragma.nim
deleted file mode 100644
index 916769fad..000000000
--- a/tests/js/tasync_pragma.nim
+++ /dev/null
@@ -1,27 +0,0 @@
-discard """
-  output: '''
-0
-t
-'''
-"""
-
-import asyncjs, macros
-
-macro f*(a: untyped): untyped =
-  assert a.kind == nnkProcDef
-  result = nnkProcDef.newTree(a.name, a[1], a[2], a.params, a.pragma, a[5], nnkStmtList.newTree())
-  let call = quote:
-    echo 0
-  result.body.add(call)
-  for child in a.body:
-    result.body.add(child)
-  #echo result.body.repr
-
-proc t* {.async, f.} =
-  echo "t"
-
-proc t0* {.async.} =
-  await t()
-
-discard t0()
-
diff --git a/tests/js/tasyncjs.nim b/tests/js/tasyncjs.nim
new file mode 100644
index 000000000..f3b273c44
--- /dev/null
+++ b/tests/js/tasyncjs.nim
@@ -0,0 +1,107 @@
+discard """
+  output: '''
+x
+e
+done
+'''
+"""
+
+#[
+xxx move this to tests/stdlib/tasyncjs.nim
+]#
+
+import std/asyncjs
+
+block:
+  # demonstrate forward definition for js
+  proc y(e: int): Future[string] {.async.}
+
+  proc e: int {.discardable.} =
+    echo "e"
+    return 2
+
+  proc x(e: int): Future[void] {.async.} =
+    var s = await y(e)
+    if e > 2:
+      return
+    echo s
+    e()
+
+  proc y(e: int): Future[string] {.async.} =
+    if e > 0:
+      return await y(0)
+    else:
+      return "x"
+
+  discard x(2)
+
+import std/sugar
+from std/strutils import contains
+
+var witness: seq[string]
+
+proc fn(n: int): Future[int] {.async.} =
+  if n >= 7:
+    raise newException(ValueError, "foobar: " & $n)
+  if n > 0:
+    var ret = 1 + await fn(n-1)
+    witness.add $(n, ret)
+    return ret
+  else:
+    return 10
+
+proc asyncFact(n: int): Future[int] {.async.} =
+  if n > 0: result = n * await asyncFact(n-1)
+  else: result = 1
+
+proc asyncIdentity(n: int): Future[int] {.async.} =
+  if n > 0: result = 1 + await asyncIdentity(n-1)
+  else: result = 0
+
+proc main() {.async.} =
+  block: # then
+    let x = await fn(4)
+      .then((a: int) => a.float)
+      .then((a: float) => $a)
+    doAssert x == "14.0"
+    doAssert witness == @["(1, 11)", "(2, 12)", "(3, 13)", "(4, 14)"]
+
+    doAssert (await fn(2)) == 12
+
+    let x2 = await fn(4).then((a: int) => (discard)).then(() => 13)
+    doAssert x2 == 13
+
+    let x4 = await asyncFact(3).then(asyncIdentity).then(asyncIdentity).then((a:int) => a * 7).then(asyncIdentity)
+    doAssert x4 == 3 * 2 * 7
+
+    block: # bug #17177
+      proc asyncIdentityNested(n: int): Future[int] {.async.} = return n
+      let x5 = await asyncFact(3).then(asyncIdentityNested)
+      doAssert x5 == 3 * 2
+
+    when false: # xxx pending bug #17254
+      let x6 = await asyncFact(3).then((a:int) {.async.} => a * 11)
+      doAssert x6 == 3 * 2 * 11
+
+  block: # catch
+    var reason: Error
+    await fn(6).then((a: int) => (witness.add $a)).catch((r: Error) => (reason = r))
+    doAssert reason == nil
+
+    await fn(7).then((a: int) => (discard)).catch((r: Error) => (reason = r))
+    doAssert reason != nil
+    doAssert reason.name == "Error"
+    doAssert "foobar: 7" in $reason.message
+  echo "done" # justified here to make sure we're running this, since it's inside `async`
+
+block asyncPragmaInType:
+  type Handler = proc () {.async.}
+  proc foo() {.async.} = discard
+  var x: Handler = foo
+
+block: # 13341
+  proc f {.async.} =
+    proc g: int =
+      result = 123
+
+discard main()
diff --git a/tests/js/tasyncjs_bad.nim b/tests/js/tasyncjs_bad.nim
new file mode 100644
index 000000000..b1e5a7bc3
--- /dev/null
+++ b/tests/js/tasyncjs_bad.nim
@@ -0,0 +1,22 @@
+discard """
+  exitCode: 1
+ outputsub: "Error: unhandled exception: foobar: 13"
+"""
+
+# note: this needs `--unhandled-rejections=strict`, see D20210217T215950
+
+import std/asyncjs
+from std/sugar import `=>`
+
+proc fn(n: int): Future[int] {.async.} =
+  if n >= 7: raise newException(ValueError, "foobar: " & $n)
+  else: result = n
+
+proc main() {.async.} =
+  let x1 = await fn(6)
+  doAssert x1 == 6
+  await fn(7).catch((a: Error) => (discard))
+  let x3 = await fn(13)
+  doAssert false # shouldn't go here, should fail before
+
+discard main()
diff --git a/tests/js/tasyncjs_pragma.nim b/tests/js/tasyncjs_pragma.nim
new file mode 100644
index 000000000..2b6f32e92
--- /dev/null
+++ b/tests/js/tasyncjs_pragma.nim
@@ -0,0 +1,29 @@
+discard """
+  output: '''
+0
+t
+'''
+"""
+
+# xxx merge into tasyncjs.nim
+
+import asyncjs, macros
+
+macro f*(a: untyped): untyped =
+  assert a.kind == nnkProcDef
+  result = nnkProcDef.newTree(a.name, a[1], a[2], a.params, a.pragma, a[5], nnkStmtList.newTree())
+  let call = quote:
+    echo 0
+  result.body.add(call)
+  for child in a.body:
+    result.body.add(child)
+  #echo result.body.repr
+
+proc t* {.async, f.} =
+  echo "t"
+
+proc t0* {.async.} =
+  await t()
+
+discard t0()
+
diff --git a/tests/js/tbasicenum.nim b/tests/js/tbasicenum.nim
deleted file mode 100644
index a9e9ce2da..000000000
--- a/tests/js/tbasicenum.nim
+++ /dev/null
@@ -1,10 +0,0 @@
-discard """
-  output: "ABCDC"
-"""
-
-type
-  MyEnum = enum
-    A,B,C,D
-# trick the optimizer with an seq:
-var x = @[A,B,C,D]
-echo x[0],x[1],x[2],x[3],MyEnum(2)
\ No newline at end of file
diff --git a/tests/js/tbasics.nim b/tests/js/tbasics.nim
new file mode 100644
index 000000000..ef84f8bb5
--- /dev/null
+++ b/tests/js/tbasics.nim
@@ -0,0 +1,62 @@
+discard """
+  output: '''ABCDC
+1
+14
+ok
+1'''
+"""
+
+type
+  MyEnum = enum
+    A,B,C,D
+# trick the optimizer with an seq:
+var x = @[A,B,C,D]
+echo x[0],x[1],x[2],x[3],MyEnum(2)
+
+# bug #10651
+
+var xa: seq[int]
+var ya = @[1,2]
+xa &= ya
+echo xa[0]
+
+proc test =
+  var yup: seq[int]
+  try:
+    yup.add 14
+    echo yup.pop
+  finally:
+    discard
+
+test()
+
+when true:
+  var a: seq[int]
+
+  a.setLen(0)
+
+  echo "ok"
+
+# bug #10697
+proc test2 =
+  var val = uint16(0)
+  var i = 0
+  if i < 2:
+    val += uint16(1)
+  echo int(val)
+
+test2()
+
+
+var someGlobal = default(array[5, int])
+for x in someGlobal: doAssert(x == 0)
+
+proc tdefault =
+  var x = default(int)
+  doAssert(x == 0)
+  proc inner(v: openArray[string]) =
+    doAssert(v.len == 0)
+
+  inner(default(seq[string]))
+
+tdefault()
diff --git a/tests/js/tbigint_backend.nim b/tests/js/tbigint_backend.nim
new file mode 100644
index 000000000..8f2f6c4f4
--- /dev/null
+++ b/tests/js/tbigint_backend.nim
@@ -0,0 +1,57 @@
+import std/private/jsutils
+
+
+type JsBigIntImpl {.importc: "bigint".} = int
+type JsBigInt = distinct JsBigIntImpl
+
+doAssert JsBigInt isnot int
+func big*(integer: SomeInteger): JsBigInt {.importjs: "BigInt(#)".}
+func big*(integer: cstring): JsBigInt {.importjs: "BigInt(#)".}
+func `<=`*(x, y: JsBigInt): bool {.importjs: "(# $1 #)".}
+func `==`*(x, y: JsBigInt): bool {.importjs: "(# === #)".}
+func inc*(x: var JsBigInt) {.importjs: "[#][0][0]++".}
+func inc2*(x: var JsBigInt) {.importjs: "#++".}
+func toCstring*(this: JsBigInt): cstring {.importjs: "#.toString()".}
+func `$`*(this: JsBigInt): string =
+  $toCstring(this)
+
+block:
+  doAssert defined(nimHasJsBigIntBackend)
+  let z1 = big"10"
+  let z2 = big"15"
+  doAssert z1 == big"10"
+  doAssert z1 == z1
+  doAssert z1 != z2
+  var s: seq[cstring]
+  for i in z1 .. z2:
+    s.add $i
+  doAssert s == @["10".cstring, "11", "12", "13", "14", "15"]
+  block:
+    var a=big"3"
+    a.inc
+    doAssert a == big"4"
+  block:
+    var z: JsBigInt
+    doAssert $z == "0"
+    doAssert z.jsTypeOf == "bigint" # would fail without codegen change
+    doAssert z != big(1)
+    doAssert z == big"0" # ditto
+
+  # ditto below
+  block:
+    let z: JsBigInt = big"1"
+    doAssert $z == "1"
+    doAssert z.jsTypeOf == "bigint"
+    doAssert z == big"1"
+
+  block:
+    let z = JsBigInt.default
+    doAssert $z == "0"
+    doAssert z.jsTypeOf == "bigint"
+    doAssert z == big"0"
+
+  block:
+    var a: seq[JsBigInt]
+    a.setLen 3
+    doAssert a[^1].jsTypeOf == "bigint"
+    doAssert a[^1] == big"0"
diff --git a/tests/js/tbyvar.nim b/tests/js/tbyvar.nim
index 705d62574..93724a2f1 100644
--- a/tests/js/tbyvar.nim
+++ b/tests/js/tbyvar.nim
@@ -1,15 +1,16 @@
 discard """
-  output: '''foo 12
+  output: '''
+foo 12
 bar 12
 2
 foo 12
 bar 12
 2
 12.5
-(nums: @[5, 5, 10, 5, 5, 5, 5, 5, 5, 5])
-(nums: @[5, 5, 50, 5, 5, 5, 5, 5, 5, 5])
-(nums: @[5, 5, 45, 5, 5, 5, 5, 5, 5, 5])
-(nums: @[5, 5, 9, 5, 5, 5, 5, 5, 5, 5])
+(nums: @[5.0, 5.0, 10.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0])
+(nums: @[5.0, 5.0, 50.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0])
+(nums: @[5.0, 5.0, 45.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0])
+(nums: @[5.0, 5.0, 9.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0])
 asd
 '''
 """
@@ -38,9 +39,9 @@ proc main =
 
 main()
 
-# Test: pass var seq to var openarray
+# Test: pass var seq to var openArray
 var s = @[2, 1]
-proc foo(a: var openarray[int]) = a[0] = 123
+proc foo(a: var openArray[int]) = a[0] = 123
 
 proc bar(s: var seq[int], a: int) =
   doAssert(a == 5)
diff --git a/tests/js/tclosures.nim b/tests/js/tclosures.nim
index 67243c937..4f1c28de3 100644
--- a/tests/js/tclosures.nim
+++ b/tests/js/tclosures.nim
@@ -2,7 +2,7 @@ discard """
   action: run
 """
 
-import math, random, strutils
+import random, strutils
 const consolePrefix = "jsCallbacks"
 
 asm """
@@ -22,7 +22,7 @@ asm """
     function print (text) { console.log (text); }
 """
 
-proc consoleprint (str:cstring): void {.importc: "print", noDecl.}
+proc consoleprint (str:cstring): void {.importc: "print", nodecl.}
 proc print* (a: varargs[string, `$`]) = consoleprint "$1: $2" % [consolePrefix, join(a, " ")]
 
 type CallbackProc {.importc.} = proc () : cstring
@@ -33,8 +33,8 @@ proc runCallbacks ():cstring {.importc.}
 proc `*` (s:string, n:Natural) : string = s.repeat(n)
 
 proc outer (i:Natural) : (string, int) =
-    let c = $char(random(93) + 33)
-    let n = random(40)
+    let c = $char(rand(93) + 33)
+    let n = rand(40)
     let s = c * n
     proc inner(): cstring = ("[$1]" % $n) & s & " <--"
     regCallback(inner)
@@ -48,4 +48,48 @@ for i in 1 .. 10:
 
 let results = runCallbacks()
 
-doAssert(expected == results)
+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
diff --git a/tests/js/tcodegendeclproc.nim b/tests/js/tcodegendeclproc.nim
index 3acf0bc13..33064bdf1 100644
--- a/tests/js/tcodegendeclproc.nim
+++ b/tests/js/tcodegendeclproc.nim
@@ -3,7 +3,7 @@ discard """
 -1
 8
 '''
-  ccodecheck: "'console.log(-1); function fac_' \\d+ '(n_' \\d+ ')'"
+  ccodecheck: "'console.log(-1); function fac__tcodegendeclproc_u1(n_p0)'"
 """
 proc fac(n: int): int {.codegenDecl: "console.log(-1); function $2($3)".} =
   return n
diff --git a/tests/js/tconsole.nim b/tests/js/tconsole.nim
index f6da71c20..88c71ea18 100644
--- a/tests/js/tconsole.nim
+++ b/tests/js/tconsole.nim
@@ -1,7 +1,8 @@
 discard """
-  output: '''Hello, console
+  output: '''
+Hello, console
 1 2 3
-1 'hi' 1.1'''
+'''
 """
 
 # This file tests the JavaScript console
@@ -10,4 +11,3 @@ import jsconsole
 
 console.log("Hello, console")
 console.log(1, 2, 3)
-console.log(1, "hi", 1.1)
\ No newline at end of file
diff --git a/tests/js/tcopying.nim b/tests/js/tcopying.nim
index 387df9cd3..306d25090 100644
--- a/tests/js/tcopying.nim
+++ b/tests/js/tcopying.nim
@@ -2,6 +2,11 @@ discard """
   output: '''123
 2 9
 2 9
+1 124
+true false
+100 300 100
+1
+1
 '''
 """
 
@@ -10,7 +15,7 @@ type MyArray = array[1, int]
 proc changeArray(a: var MyArray) =
     a = [123]
 
-var a : MyArray
+var a: MyArray
 changeArray(a)
 echo a[0]
 
@@ -30,8 +35,46 @@ block:
         ary2: array[3, int]
 
     let ary1 = [1, 2, 3]
-    var obj = TestObj(ary2:ary1)
+    var obj = TestObj(ary2: ary1)
 
     obj.ary2[1] = 9
 
     echo ary1[1], " ", obj.ary2[1]
+
+block:
+    type TestObj = object
+        x, y: int
+
+    let obj = TestObj(x: 1, y: 2)
+    var s = @[obj]
+    s[0].x += 123
+    echo obj.x, " ", s[0].x
+
+block:
+    var nums = {1, 2, 3, 4}
+    let obj = (n: nums)
+    nums.incl 5
+    echo (5 in nums), " ", (5 in obj.n)
+
+block:
+    let tup1 = (a: 100)
+    var tup2 = (t: (t2: tup1))
+    var tup3 = tup1
+    tup2.t.t2.a = 300
+    echo tup1.a, " ", tup2.t.t2.a, " ", tup3.a
+
+block:
+    proc foo(arr: array[2, int]) =
+        var s = @arr
+        s[0] = 500
+
+    var nums = [1, 2]
+    foo(nums)
+    echo nums[0]
+
+proc bug9674 =
+  var b = @[1,2,3]
+  var a = move(b)
+  echo a[0]
+
+bug9674()
diff --git a/tests/js/tcsymbol.nim b/tests/js/tcsymbol.nim
new file mode 100644
index 000000000..07e52b9b6
--- /dev/null
+++ b/tests/js/tcsymbol.nim
@@ -0,0 +1,6 @@
+discard """
+  matrix: "--cc:gcc; --cc:tcc"
+"""
+
+doAssert not defined(gcc)
+doAssert not defined(tcc)
\ No newline at end of file
diff --git a/tests/js/tdanger.nim b/tests/js/tdanger.nim
new file mode 100644
index 000000000..9088859a8
--- /dev/null
+++ b/tests/js/tdanger.nim
@@ -0,0 +1,17 @@
+discard """
+  matrix: ";--d:danger"
+"""
+
+block:
+  proc foo() =
+    var name = int64(12)
+    var x = uint32(name)
+    var m = x + 12
+
+    var y = int32(name)
+    var n = y + 1
+
+    doAssert m == uint32(n + 11)
+
+
+  foo()
diff --git a/tests/js/tdiscard.nim b/tests/js/tdiscard.nim
new file mode 100644
index 000000000..9aa6ea1b1
--- /dev/null
+++ b/tests/js/tdiscard.nim
@@ -0,0 +1,3 @@
+import dom
+
+discard Node()
\ No newline at end of file
diff --git a/tests/js/tdollar_float.nim b/tests/js/tdollar_float.nim
new file mode 100644
index 000000000..4fd8e3cba
--- /dev/null
+++ b/tests/js/tdollar_float.nim
@@ -0,0 +1,62 @@
+#[
+merge into tests/system/tdollars.nim once https://github.com/nim-lang/Nim/pull/14122
+is merged
+]#
+
+import unittest
+
+block: # https://github.com/timotheecour/Nim/issues/133
+  # simple test
+  var a: float = 2
+  check $a == "2.0"
+
+  # systematic tests
+  template fun(a2: static float) =
+    const a: float = a2 # needed pending https://github.com/timotheecour/Nim/issues/132
+    var b = a
+    check $b == $a
+
+  fun 2
+  fun 2.0
+  fun 2.1
+  fun 1_000
+  fun 1_000.1
+  fun 1_000_000_000.1
+  fun 1_000_000_000_000.1
+
+  # negatives
+  fun -2.0
+  fun -2.1
+
+  # 0
+  fun 0
+  fun -0
+  fun 0.0
+
+  block:
+    var a = -0.0
+    check $a in ["-0.0", "0.0"]
+
+  # exponents
+  block:
+    var a = 5e20
+    check $a in ["5e20", "500000000000000000000.0"]
+
+  fun 3.4e1'f32
+  fun 3.4e-1'f32
+  fun -3.4e-1'f32
+  fun 3.4e-1'f32
+  fun 3e-1'f32
+
+  block:
+    var a = 3.4e38'f32
+    check $a in ["3.4e+38", "3.4e+038"]
+      # on windows, printf (used in VM) prints as 3.4e+038
+      # but js prints as 3.4e+38
+      # on osx, both print as 3.4e+38
+      # see https://github.com/timotheecour/Nim/issues/138
+
+  when false: # edge cases
+    fun -0.0 # see https://github.com/timotheecour/Nim/issues/136
+    fun 5e20
+    fun 3.4e38'f32
diff --git a/tests/js/temptyseq.nim b/tests/js/temptyseq.nim
new file mode 100644
index 000000000..6489cf817
--- /dev/null
+++ b/tests/js/temptyseq.nim
@@ -0,0 +1,8 @@
+# #12671
+
+proc foo =
+  var x: seq[int]
+  doAssertRaises(IndexDefect):
+    inc x[0]
+
+foo()
diff --git a/tests/js/test1.nim b/tests/js/test1.nim
index 7f1d346f0..7ad3f85f6 100644
--- a/tests/js/test1.nim
+++ b/tests/js/test1.nim
@@ -4,8 +4,7 @@ discard """
 
 # This file tests the JavaScript generator
 
-import
-  dom, strutils
+import strutils
 
 var
   inputElement = "1123"
@@ -20,3 +19,34 @@ 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..fa857ccc5 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"
@@ -18,7 +21,7 @@ proc foo() =
 foo()
 
 # #376
-when not defined(JS):
+when not defined(js):
   proc foo(val: float): string = "no js " & $val
 else:
   proc foo(val: float): string = "js " & $val
diff --git a/tests/js/testobjs.nim b/tests/js/testobjs.nim
index dd66825ec..b61d06471 100644
--- a/tests/js/testobjs.nim
+++ b/tests/js/testobjs.nim
@@ -34,7 +34,7 @@ var
   recurse1 = Recurse[int](data: 1, next: recurse2)
 
 
-doAssert test.name == "Jorden"
+doAssert test.name == cstring"Jorden"
 doAssert knight.age == 19
 doAssert knight.item.price == 50
 doAssert recurse1.next.next.data == 3
@@ -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/tfieldchecks.nim b/tests/js/tfieldchecks.nim
new file mode 100644
index 000000000..a0679a349
--- /dev/null
+++ b/tests/js/tfieldchecks.nim
@@ -0,0 +1,48 @@
+discard """
+  output: '''
+foo
+C
+3.14
+foo
+3.14
+3.14
+'''
+"""
+
+type
+  V = enum
+    A, B, C
+  X = object
+    f0: string
+    case f1: V
+    of A: f2: string
+    of B: discard
+    of C: f3: float
+
+var obj = X(f0: "foo", f1: C, f3: 3.14)
+
+block:
+  echo obj.f0
+  echo obj.f1
+  doAssertRaises(FieldDefect): echo obj.f2
+  echo obj.f3
+
+block:
+  let a0 = addr(obj.f0)
+  echo a0[]
+  # let a1 = addr(obj.f1)
+  # echo a1[]
+  doAssertRaises(FieldDefect):
+    let a2 = addr(obj.f2)
+    echo a2[]
+  let a3 = addr(obj.f3)
+  echo a3[]
+
+# Prevent double evaluation of LHS
+block:
+  var flag = false
+  proc wrap(x: X): X =
+    doAssert flag == false
+    flag = true
+    result = x
+  echo wrap(obj).f3
diff --git a/tests/js/tglobal.nim b/tests/js/tglobal.nim
new file mode 100644
index 000000000..38f5eec34
--- /dev/null
+++ b/tests/js/tglobal.nim
@@ -0,0 +1,30 @@
+block global:
+  proc getState(): int =
+    var state0 {.global.}: int
+    inc state0
+    result = state0
+
+  for i in 0 ..< 3:
+    doAssert getState() == i + 1
+
+  for i in 0 ..< 3:
+    once:
+      doAssert i == 0
+
+
+block threadvar:
+  proc getThreadState0(): int =
+    var state0 {.threadvar.}: int
+    inc state0
+    result = state0
+
+  for i in 0 ..< 3:
+    doAssert getThreadState0() == i + 1
+
+  proc getThreadState1(): int =
+    var state1 {.threadvar.}: int
+    inc state1
+    result = state1
+
+  for i in 0 ..< 3:
+    doAssert getThreadState1() == i + 1
diff --git a/tests/js/tindexdefect.nim b/tests/js/tindexdefect.nim
new file mode 100644
index 000000000..37994ec2e
--- /dev/null
+++ b/tests/js/tindexdefect.nim
@@ -0,0 +1,9 @@
+discard """
+  outputsub: "unhandled exception: index 10000 not in 0 .. 0 [IndexDefect]"
+  exitcode: 1
+  joinable: false
+"""
+
+var s = ['a']
+let z = s[10000] == 'a'
+echo z
\ No newline at end of file
diff --git a/tests/js/tjsffi.nim b/tests/js/tjsffi.nim
index 325ab6366..f27ea5546 100644
--- a/tests/js/tjsffi.nim
+++ b/tests/js/tjsffi.nim
@@ -1,21 +1,6 @@
 discard """
+matrix: "--legacy:jsnolambdalifting;"
 output: '''
-true
-true
-true
-true
-true
-true
-true
-true
-true
-true
-true
-true
-true
-true
-true
-true
 3
 2
 12
@@ -25,260 +10,188 @@ Event { name: 'updates: test' }
 '''
 """
 
-import macros, jsffi, jsconsole
+import jsffi, jsconsole
 
 # Tests for JsObject
-# Test JsObject []= and []
-block:
-  proc test(): bool =
-    let obj = newJsObject()
-    var working = true
-    obj["a"] = 11
-    obj["b"] = "test"
-    obj["c"] = "test".cstring
-    working = working and obj["a"].to(int) == 11
-    working = working and obj["c"].to(cstring) == "test".cstring
-    working
-  echo test()
+block: # Test JsObject []= and []
+  let obj = newJsObject()
+  obj["a"] = 11
+  obj["b"] = "test"
+  obj["c"] = "test".cstring
+  doAssert obj["a"].to(int) == 11
+  doAssert obj["c"].to(cstring) == "test".cstring
 
-# Test JsObject .= and .
-block:
-  proc test(): bool =
-    let obj = newJsObject()
-    var working = true
-    obj.a = 11
-    obj.b = "test"
-    obj.c = "test".cstring
-    obj.`$!&` = 42
-    obj.`while` = 99
-    working = working and obj.a.to(int) == 11
-    working = working and obj.b.to(string) == "test"
-    working = working and obj.c.to(cstring) == "test".cstring
-    working = working and obj.`$!&`.to(int) == 42
-    working = working and obj.`while`.to(int) == 99
-    working
-  echo test()
+block: # Test JsObject .= and .
+  let obj = newJsObject()
+  obj.a = 11
+  obj.b = "test"
+  obj.c = "test".cstring
+  obj.`$!&` = 42
+  obj.`while` = 99
+  doAssert obj.a.to(int) == 11
+  doAssert obj.b.to(string) == "test"
+  doAssert obj.c.to(cstring) == "test".cstring
+  doAssert obj.`$!&`.to(int) == 42
+  doAssert obj.`while`.to(int) == 99
 
-# Test JsObject .()
-block:
-  proc test(): bool =
-    let obj = newJsObject()
-    obj.`?!$` = proc(x, y, z: int, t: cstring): cstring = t & $(x + y + z)
-    obj.`?!$`(1, 2, 3, "Result is: ").to(cstring) == "Result is: 6"
-  echo test()
+block: # Test JsObject .()
+  let obj = newJsObject()
+  obj.`?!$` = proc(x, y, z: int, t: cstring): cstring = t & $(x + y + z)
+  doAssert obj.`?!$`(1, 2, 3, "Result is: ").to(cstring) == cstring"Result is: 6"
 
-# Test JsObject []()
-block:
-  proc test(): bool =
-    let obj = newJsObject()
-    obj.a = proc(x, y, z: int, t: string): string = t & $(x + y + z)
-    let call = obj["a"].to(proc(x, y, z: int, t: string): string)
-    call(1, 2, 3, "Result is: ") == "Result is: 6"
-  echo test()
+block: # Test JsObject []()
+  let obj = newJsObject()
+  obj.a = proc(x, y, z: int, t: string): string = t & $(x + y + z)
+  let call = obj["a"].to(proc(x, y, z: int, t: string): string)
+  doAssert call(1, 2, 3, "Result is: ") == "Result is: 6"
 
 # Test JsObject Iterators
-block:
-  proc testPairs(): bool =
-    let obj = newJsObject()
-    var working = true
-    obj.a = 10
-    obj.b = 20
-    obj.c = 30
-    for k, v in obj.pairs:
-      case $k
-      of "a":
-        working = working and v.to(int) == 10
-      of "b":
-        working = working and v.to(int) == 20
-      of "c":
-        working = working and v.to(int) == 30
-      else:
-        return false
-    working
-  proc testItems(): bool =
-    let obj = newJsObject()
-    var working = true
-    obj.a = 10
-    obj.b = 20
-    obj.c = 30
-    for v in obj.items:
-      working = working and v.to(int) in [10, 20, 30]
-    working
-  proc testKeys(): bool =
-    let obj = newJsObject()
-    var working = true
-    obj.a = 10
-    obj.b = 20
-    obj.c = 30
-    for v in obj.keys:
-      working = working and $v in ["a", "b", "c"]
-    working
-  proc test(): bool = testPairs() and testItems() and testKeys()
-  echo test()
+block: # testPairs
+  let obj = newJsObject()
+  obj.a = 10
+  obj.b = 20
+  obj.c = 30
+  for k, v in obj.pairs:
+    case $k
+    of "a":
+      doAssert v.to(int) == 10
+    of "b":
+      doAssert v.to(int) == 20
+    of "c":
+      doAssert v.to(int) == 30
+    else:
+      doAssert false
+block: # testItems
+  let obj = newJsObject()
+  obj.a = 10
+  obj.b = 20
+  obj.c = 30
+  for v in obj.items:
+    doAssert v.to(int) in [10, 20, 30]
+block: # testKeys
+  let obj = newJsObject()
+  obj.a = 10
+  obj.b = 20
+  obj.c = 30
+  for v in obj.keys:
+    doAssert $v in ["a", "b", "c"]
 
-# Test JsObject equality
-block:
-  proc test(): bool =
-    {. emit: "var comparison = {a: 22, b: 'test'};" .}
-    var comparison {. importc, nodecl .}: JsObject
-    let obj = newJsObject()
-    obj.a = 22
-    obj.b = "test".cstring
-    obj.a == comparison.a and obj.b == comparison.b
-  echo test()
+block: # Test JsObject equality
+  {. emit: "var comparison = {a: 22, b: 'test'};" .}
+  var comparison {. importjs, nodecl .}: JsObject
+  let obj = newJsObject()
+  obj.a = 22
+  obj.b = "test".cstring
+  doAssert obj.a == comparison.a and obj.b == comparison.b
 
-# Test JsObject literal
-block:
-  proc test(): bool =
-    {. emit: "var comparison = {a: 22, b: 'test'};" .}
-    var comparison {. importc, nodecl .}: JsObject
-    let obj = JsObject{ a: 22, b: "test".cstring }
-    obj.a == comparison.a and obj.b == comparison.b
-  echo test()
+block: # Test JsObject literal
+  {. emit: "var comparison = {a: 22, b: 'test'};" .}
+  var comparison {. importjs, nodecl .}: JsObject
+  let obj = JsObject{ a: 22, b: "test".cstring }
+  doAssert obj.a == comparison.a and obj.b == comparison.b
 
 # Tests for JsAssoc
-# Test JsAssoc []= and []
-block:
-  proc test(): bool =
-    let obj = newJsAssoc[int, int]()
-    var working = true
-    obj[1] = 11
-    working = working and not compiles(obj["a"] = 11)
-    working = working and not compiles(obj["a"])
-    working = working and not compiles(obj[2] = "test")
-    working = working and not compiles(obj[3] = "test".cstring)
-    working = working and obj[1] == 11
-    working
-  echo test()
+block: # Test JsAssoc []= and []
+  let obj = newJsAssoc[int, int]()
+  obj[1] = 11
+  doAssert not compiles(obj["a"] = 11)
+  doAssert not compiles(obj["a"])
+  doAssert not compiles(obj[2] = "test")
+  doAssert not compiles(obj[3] = "test".cstring)
+  doAssert obj[1] == 11
 
-# Test JsAssoc .= and .
-block:
-  proc test(): bool =
-    let obj = newJsAssoc[string, int]()
-    var working = true
-    obj.a = 11
-    obj.`$!&` = 42
-    working = working and not compiles(obj.b = "test")
-    working = working and not compiles(obj.c = "test".cstring)
-    working = working and obj.a == 11
-    working = working and obj.`$!&` == 42
-    working
-  echo test()
+block: # Test JsAssoc .= and .
+  let obj = newJsAssoc[cstring, int]()
+  var working = true
+  obj.a = 11
+  obj.`$!&` = 42
+  doAssert not compiles(obj.b = "test")
+  doAssert not compiles(obj.c = "test".cstring)
+  doAssert obj.a == 11
+  doAssert obj.`$!&` == 42
 
-# Test JsAssoc .()
-block:
-  proc test(): bool =
-    let obj = newJsAssoc[string, proc(e: int): int]()
-    obj.a = proc(e: int): int = e * e
-    obj.a(10) == 100
-  echo test()
+block: # Test JsAssoc .()
+  let obj = newJsAssoc[cstring, proc(e: int): int]()
+  obj.a = proc(e: int): int = e * e
+  doAssert obj.a(10) == 100
 
-# Test JsAssoc []()
-block:
-  proc test(): bool =
-    let obj = newJsAssoc[string, proc(e: int): int]()
-    obj.a = proc(e: int): int = e * e
-    let call = obj["a"]
-    call(10) == 100
-  echo test()
+block: # Test JsAssoc []()
+  let obj = newJsAssoc[cstring, proc(e: int): int]()
+  obj.a = proc(e: int): int = e * e
+  let call = obj["a"]
+  doAssert call(10) == 100
 
 # Test JsAssoc Iterators
-block:
-  proc testPairs(): bool =
-    let obj = newJsAssoc[string, int]()
-    var working = true
-    obj.a = 10
-    obj.b = 20
-    obj.c = 30
-    for k, v in obj.pairs:
-      case $k
-      of "a":
-        working = working and v == 10
-      of "b":
-        working = working and v == 20
-      of "c":
-        working = working and v == 30
-      else:
-        return false
-    working
-  proc testItems(): bool =
-    let obj = newJsAssoc[string, int]()
-    var working = true
-    obj.a = 10
-    obj.b = 20
-    obj.c = 30
-    for v in obj.items:
-      working = working and v in [10, 20, 30]
-    working
-  proc testKeys(): bool =
-    let obj = newJsAssoc[string, int]()
-    var working = true
-    obj.a = 10
-    obj.b = 20
-    obj.c = 30
-    for v in obj.keys:
-      working = working and v in ["a", "b", "c"]
-    working
-  proc test(): bool = testPairs() and testItems() and testKeys()
-  echo test()
+block: # testPairs
+  let obj = newJsAssoc[cstring, int]()
+  obj.a = 10
+  obj.b = 20
+  obj.c = 30
+  for k, v in obj.pairs:
+    case $k
+    of "a":
+      doAssert v == 10
+    of "b":
+      doAssert v == 20
+    of "c":
+      doAssert v == 30
+    else:
+      doAssert false
+block: # testItems
+  let obj = newJsAssoc[cstring, int]()
+  obj.a = 10
+  obj.b = 20
+  obj.c = 30
+  for v in obj.items:
+    doAssert v in [10, 20, 30]
+block: # testKeys
+  let obj = newJsAssoc[cstring, int]()
+  obj.a = 10
+  obj.b = 20
+  obj.c = 30
+  for v in obj.keys:
+    doAssert v in [cstring"a", cstring"b", cstring"c"]
 
-# Test JsAssoc equality
-block:
-  proc test(): bool =
-    {. emit: "var comparison = {a: 22, b: 55};" .}
-    var comparison {. importcpp, nodecl .}: JsAssoc[string, int]
-    let obj = newJsAssoc[string, int]()
-    obj.a = 22
-    obj.b = 55
-    obj.a == comparison.a and obj.b == comparison.b
-  echo test()
+block: # Test JsAssoc equality
+  {. emit: "var comparison = {a: 22, b: 55};" .}
+  var comparison {. importjs, nodecl .}: JsAssoc[cstring, int]
+  let obj = newJsAssoc[cstring, int]()
+  obj.a = 22
+  obj.b = 55
+  doAssert obj.a == comparison.a and obj.b == comparison.b
 
-# Test JsAssoc literal
-block:
-  proc test(): bool =
-    {. emit: "var comparison = {a: 22, b: 55};" .}
-    var comparison {. importcpp, nodecl .}: JsAssoc[string, int]
-    let obj = JsAssoc[string, int]{ a: 22, b: 55 }
-    var working = true
-    working = working and
-      compiles(JsAssoc[int, int]{ 1: 22, 2: 55 })
-    working = working and
-      comparison.a == obj.a and comparison.b == obj.b
-    working = working and
-      not compiles(JsAssoc[string, int]{ a: "test" })
-    working
-  echo test()
+block: # Test JsAssoc literal
+  {. emit: "var comparison = {a: 22, b: 55};" .}
+  var comparison {. importjs, nodecl .}: JsAssoc[cstring, int]
+  let obj = JsAssoc[cstring, int]{ a: 22, b: 55 }
+  doAssert compiles(JsAssoc[int, int]{ 1: 22, 2: 55 })
+  doAssert comparison.a == obj.a and comparison.b == obj.b
+  doAssert not compiles(JsAssoc[cstring, int]{ a: "test" })
 
 # Tests for macros on non-JsRoot objects
-# Test lit
-block:
+block: # Test lit
   type TestObject = object
     a: int
     b: cstring
-  proc test(): bool =
-    {. emit: "var comparison = {a: 1};" .}
-    var comparison {. importc, nodecl .}: TestObject
-    let obj = TestObject{ a: 1 }
-    obj == comparison
-  echo test()
+  {. emit: "var comparison = {a: 1};" .}
+  var comparison {. importjs, nodecl .}: TestObject
+  let obj = TestObject{ a: 1 }
+  doAssert obj == comparison
 
-# Test bindMethod
-block:
+block: # Test bindMethod
   type TestObject = object
     a: int
-    onWhatever: proc(e: int): int
-  proc handleWhatever(that: TestObject, e: int): int =
-    e + that.a
-  proc test(): bool =
+    onWhatever: proc(e: int): int {.nimcall.}
+  proc handleWhatever(this: TestObject, e: int): int =
+    e + this.a
+  block:
     let obj = TestObject(a: 9, onWhatever: bindMethod(handleWhatever))
-    obj.onWhatever(1) == 10
-  echo test()
+    doAssert obj.onWhatever(1) == 10
 
 block:
   {.emit: "function jsProc(n) { return n; }" .}
-  proc jsProc(x: int32): JsObject {.importc: "jsProc".}
-
-  proc test() =
+  proc jsProc(x: int32): JsObject {.importjs: "jsProc(#)".}
+  block:
     var x = jsProc(1)
     var y = jsProc(2)
     console.log x + y
@@ -287,10 +200,6 @@ block:
     x += jsProc(10)
     console.log x
 
-  test()
-
-import macros
-
 block:
   {.emit:
   """
@@ -303,13 +212,13 @@ block:
   type Event = object
     name: cstring
 
-  proc on(event: cstring, handler: proc) {.importc: "on".}
-  var jslib {.importc: "jslib", nodecl.}: JsObject
+  proc on(event: cstring, handler: proc) {.importjs: "on(#,#)".}
+  var jslib {.importjs: "jslib", nodecl.}: JsObject
 
   on("click") do (e: Event):
     console.log e
 
-  jslib.on("reloaded") do:
+  jslib.on("reloaded") do ():
     console.log jsarguments[0]
 
   # this test case is different from the above, because
@@ -317,3 +226,49 @@ block:
   jslib.subscribe("updates"):
     console.log jsarguments[0]
 
+block:
+  doAssert jsUndefined == jsNull
+  doAssert jsUndefined == nil
+  doAssert jsNull == nil
+  doAssert jsUndefined.isNil
+  doAssert jsNull.isNil
+  doAssert jsNull.isNull
+  doAssert jsUndefined.isUndefined
+
+block: # test **
+  var a = toJs(0)
+  var b = toJs(0)
+  doAssert to(a ** b, int) == 1
+  a = toJs(1)
+  b = toJs(1)
+  doAssert to(a ** b, int) == 1
+  a = toJs(-1)
+  b = toJs(-1)
+  doAssert to(a ** b, int) == -1
+  a = toJs(6)
+  b = toJs(6)
+  doAssert to(a ** b, int) == 46656
+  a = toJs(5.5)
+  b = toJs(3)
+  doAssert to(a ** b, float) == 166.375
+  a = toJs(5)
+  b = toJs(3.0)
+  doAssert to(a ** b, float) == 125.0
+  a = toJs(7.0)
+  b = toJS(6.0)
+  doAssert to(a ** b, float) == 117649.0
+  a = toJs(8)
+  b = toJS(-2)
+  doAssert to(a ** b, float) == 0.015625
+
+  a = toJs(1)
+  b = toJs(1)
+  doAssert to(`**`(a + a, b), int) == 2
+
+  doAssert to(`**`(toJs(1) + toJs(1), toJs(2)), int) == 4
+
+block: # issue #21208
+  type MyEnum = enum baz
+  var obj: JsObject
+  {.emit: "`obj` = {bar: {baz: 123}};".}
+  discard obj.bar.baz
diff --git a/tests/js/tjsffi_old.nim b/tests/js/tjsffi_old.nim
new file mode 100644
index 000000000..378003f4e
--- /dev/null
+++ b/tests/js/tjsffi_old.nim
@@ -0,0 +1,340 @@
+discard """
+output: '''
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+3
+2
+12
+Event { name: 'click: test' }
+Event { name: 'reloaded: test' }
+Event { name: 'updates: test' }
+true
+true
+true
+true
+true
+true
+true
+'''
+"""
+
+## same as tjsffi, but this test uses the old names: importc and
+## importcpp. This test is for backwards compatibility.
+
+# xxx instead of maintaining this near-duplicate test file, just have tests
+# that check that importc, importcpp, importjs work and remove this file.
+
+import jsffi, jsconsole
+
+# Tests for JsObject
+# Test JsObject []= and []
+block:
+  proc test(): bool =
+    let obj = newJsObject()
+    var working = true
+    obj["a"] = 11
+    obj["b"] = "test"
+    obj["c"] = "test".cstring
+    working = working and obj["a"].to(int) == 11
+    working = working and obj["c"].to(cstring) == "test".cstring
+    working
+  echo test()
+
+# Test JsObject .= and .
+block:
+  proc test(): bool =
+    let obj = newJsObject()
+    var working = true
+    obj.a = 11
+    obj.b = "test"
+    obj.c = "test".cstring
+    obj.`$!&` = 42
+    obj.`while` = 99
+    working = working and obj.a.to(int) == 11
+    working = working and obj.b.to(string) == "test"
+    working = working and obj.c.to(cstring) == "test".cstring
+    working = working and obj.`$!&`.to(int) == 42
+    working = working and obj.`while`.to(int) == 99
+    working
+  echo test()
+
+# Test JsObject .()
+block:
+  proc test(): bool =
+    let obj = newJsObject()
+    obj.`?!$` = proc(x, y, z: int, t: cstring): cstring = t & $(x + y + z)
+    obj.`?!$`(1, 2, 3, "Result is: ").to(cstring) == cstring"Result is: 6"
+  echo test()
+
+# Test JsObject []()
+block:
+  proc test(): bool =
+    let obj = newJsObject()
+    obj.a = proc(x, y, z: int, t: string): string = t & $(x + y + z)
+    let call = obj["a"].to(proc(x, y, z: int, t: string): string)
+    call(1, 2, 3, "Result is: ") == "Result is: 6"
+  echo test()
+
+# Test JsObject Iterators
+block:
+  proc testPairs(): bool =
+    let obj = newJsObject()
+    var working = true
+    obj.a = 10
+    obj.b = 20
+    obj.c = 30
+    for k, v in obj.pairs:
+      case $k
+      of "a":
+        working = working and v.to(int) == 10
+      of "b":
+        working = working and v.to(int) == 20
+      of "c":
+        working = working and v.to(int) == 30
+      else:
+        return false
+    working
+  proc testItems(): bool =
+    let obj = newJsObject()
+    var working = true
+    obj.a = 10
+    obj.b = 20
+    obj.c = 30
+    for v in obj.items:
+      working = working and v.to(int) in [10, 20, 30]
+    working
+  proc testKeys(): bool =
+    let obj = newJsObject()
+    var working = true
+    obj.a = 10
+    obj.b = 20
+    obj.c = 30
+    for v in obj.keys:
+      working = working and $v in ["a", "b", "c"]
+    working
+  proc test(): bool = testPairs() and testItems() and testKeys()
+  echo test()
+
+# Test JsObject equality
+block:
+  proc test(): bool =
+    {. emit: "var comparison = {a: 22, b: 'test'};" .}
+    var comparison {. importc, nodecl .}: JsObject
+    let obj = newJsObject()
+    obj.a = 22
+    obj.b = "test".cstring
+    obj.a == comparison.a and obj.b == comparison.b
+  echo test()
+
+# Test JsObject literal
+block:
+  proc test(): bool =
+    {. emit: "var comparison = {a: 22, b: 'test'};" .}
+    var comparison {. importc, nodecl .}: JsObject
+    let obj = JsObject{ a: 22, b: "test".cstring }
+    obj.a == comparison.a and obj.b == comparison.b
+  echo test()
+
+# Tests for JsAssoc
+# Test JsAssoc []= and []
+block:
+  proc test(): bool =
+    let obj = newJsAssoc[int, int]()
+    var working = true
+    obj[1] = 11
+    working = working and not compiles(obj["a"] = 11)
+    working = working and not compiles(obj["a"])
+    working = working and not compiles(obj[2] = "test")
+    working = working and not compiles(obj[3] = "test".cstring)
+    working = working and obj[1] == 11
+    working
+  echo test()
+
+# Test JsAssoc .= and .
+block:
+  proc test(): bool =
+    let obj = newJsAssoc[cstring, int]()
+    var working = true
+    obj.a = 11
+    obj.`$!&` = 42
+    working = working and not compiles(obj.b = "test")
+    working = working and not compiles(obj.c = "test".cstring)
+    working = working and obj.a == 11
+    working = working and obj.`$!&` == 42
+    working
+  echo test()
+
+# Test JsAssoc .()
+block:
+  proc test(): bool =
+    let obj = newJsAssoc[cstring, proc(e: int): int]()
+    obj.a = proc(e: int): int = e * e
+    obj.a(10) == 100
+  echo test()
+
+# Test JsAssoc []()
+block:
+  proc test(): bool =
+    let obj = newJsAssoc[cstring, proc(e: int): int]()
+    obj.a = proc(e: int): int = e * e
+    let call = obj["a"]
+    call(10) == 100
+  echo test()
+
+# Test JsAssoc Iterators
+block:
+  proc testPairs(): bool =
+    let obj = newJsAssoc[cstring, int]()
+    var working = true
+    obj.a = 10
+    obj.b = 20
+    obj.c = 30
+    for k, v in obj.pairs:
+      case $k
+      of "a":
+        working = working and v == 10
+      of "b":
+        working = working and v == 20
+      of "c":
+        working = working and v == 30
+      else:
+        return false
+    working
+  proc testItems(): bool =
+    let obj = newJsAssoc[cstring, int]()
+    var working = true
+    obj.a = 10
+    obj.b = 20
+    obj.c = 30
+    for v in obj.items:
+      working = working and v in [10, 20, 30]
+    working
+  proc testKeys(): bool =
+    let obj = newJsAssoc[cstring, int]()
+    var working = true
+    obj.a = 10
+    obj.b = 20
+    obj.c = 30
+    for v in obj.keys:
+      working = working and v in [cstring"a", cstring"b", cstring"c"]
+    working
+  proc test(): bool = testPairs() and testItems() and testKeys()
+  echo test()
+
+# Test JsAssoc equality
+block:
+  proc test(): bool =
+    {. emit: "var comparison = {a: 22, b: 55};" .}
+    var comparison {. importcpp, nodecl .}: JsAssoc[cstring, int]
+    let obj = newJsAssoc[cstring, int]()
+    obj.a = 22
+    obj.b = 55
+    obj.a == comparison.a and obj.b == comparison.b
+  echo test()
+
+# Test JsAssoc literal
+block:
+  proc test(): bool =
+    {. emit: "var comparison = {a: 22, b: 55};" .}
+    var comparison {. importcpp, nodecl .}: JsAssoc[cstring, int]
+    let obj = JsAssoc[cstring, int]{ a: 22, b: 55 }
+    var working = true
+    working = working and
+      compiles(JsAssoc[int, int]{ 1: 22, 2: 55 })
+    working = working and
+      comparison.a == obj.a and comparison.b == obj.b
+    working = working and
+      not compiles(JsAssoc[cstring, int]{ a: "test" })
+    working
+  echo test()
+
+# Tests for macros on non-JsRoot objects
+# Test lit
+block:
+  type TestObject = object
+    a: int
+    b: cstring
+  proc test(): bool =
+    {. emit: "var comparison = {a: 1};" .}
+    var comparison {. importc, nodecl .}: TestObject
+    let obj = TestObject{ a: 1 }
+    obj == comparison
+  echo test()
+
+# Test bindMethod
+block:
+  type TestObject = object
+    a: int
+    onWhatever: proc(e: int): int {.nimcall.}
+  proc handleWhatever(this: TestObject, e: int): int {.nimcall.} =
+    e + this.a
+  proc test(): bool =
+    let obj = TestObject(a: 9, onWhatever: bindMethod(handleWhatever))
+    obj.onWhatever(1) == 10
+  echo test()
+
+block:
+  {.emit: "function jsProc(n) { return n; }" .}
+  proc jsProc(x: int32): JsObject {.importc: "jsProc".}
+
+  proc test() =
+    var x = jsProc(1)
+    var y = jsProc(2)
+    console.log x + y
+    console.log ++x
+
+    x += jsProc(10)
+    console.log x
+
+  test()
+
+
+block:
+  {.emit:
+  """
+  function Event(name) { this.name = name; }
+  function on(eventName, eventHandler) { eventHandler(new Event(eventName + ": test")); }
+  var jslib = { "on": on, "subscribe": on };
+  """
+  .}
+
+  type Event = object
+    name: cstring
+
+  proc on(event: cstring, handler: proc) {.importc: "on".}
+  var jslib {.importc: "jslib", nodecl.}: JsObject
+
+  on("click") do (e: Event):
+    console.log e
+
+  jslib.on("reloaded") do ():
+    console.log jsarguments[0]
+
+  # this test case is different from the above, because
+  # `subscribe` is not overloaded in the current scope
+  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/js/tjshello.nim b/tests/js/tjshello.nim
index 19e0b90ae..8e090b3d2 100644
--- a/tests/js/tjshello.nim
+++ b/tests/js/tjshello.nim
@@ -1,4 +1,5 @@
 discard """
+  cmd: "nim $target $options --stackTrace:off --lineTrace:off $file"
   output: "Hello World"
   maxcodesize: 1000
   ccodecheck: "!@'function'"
@@ -7,4 +8,3 @@ discard """
 import jsconsole
 
 console.log "Hello World"
-
diff --git a/tests/js/tjshello_stacktrace.nim b/tests/js/tjshello_stacktrace.nim
new file mode 100644
index 000000000..d5e1c36eb
--- /dev/null
+++ b/tests/js/tjshello_stacktrace.nim
@@ -0,0 +1,9 @@
+discard """
+  output: "Hello World"
+  maxcodesize: 4500
+  ccodecheck: "!@'function'"
+"""
+
+import jsconsole
+
+console.log "Hello World"
diff --git a/tests/js/tjsnimscombined.nim b/tests/js/tjsnimscombined.nim
new file mode 100644
index 000000000..4d3e6c453
--- /dev/null
+++ b/tests/js/tjsnimscombined.nim
@@ -0,0 +1 @@
+import std/jsffi
diff --git a/tests/js/tjsnimscombined.nims b/tests/js/tjsnimscombined.nims
new file mode 100644
index 000000000..01b93d3fa
--- /dev/null
+++ b/tests/js/tjsnimscombined.nims
@@ -0,0 +1 @@
+# test the condition where both `js` and `nimscript` are defined (nimscript receives priority)
diff --git a/tests/js/tlent.nim b/tests/js/tlent.nim
new file mode 100644
index 000000000..2546e5b1d
--- /dev/null
+++ b/tests/js/tlent.nim
@@ -0,0 +1,33 @@
+discard """
+  output: '''
+hmm
+100
+hmm
+100
+'''
+"""
+
+# #16800
+
+type A = object
+  b: int
+var t = A(b: 100)
+block:
+  proc getValues: lent int =
+    echo "hmm"
+    result = t.b
+  echo getValues()
+block:
+  proc getValues: lent int =
+    echo "hmm"
+    t.b
+  echo getValues()
+
+when false: # still an issue, #16908
+  template main =
+    iterator fn[T](a:T): lent T = yield a
+    let a = @[10]
+    for b in fn(a): echo b
+
+  static: main()
+  main()
diff --git a/tests/js/tmangle.nim b/tests/js/tmangle.nim
index c4167ba39..caaa15fa1 100644
--- a/tests/js/tmangle.nim
+++ b/tests/js/tmangle.nim
@@ -27,10 +27,10 @@ block:
   var global = T(a: 11, b: "foo")
   proc test(): bool =
     var obj = T(a: 11, b: "foo")
-    {. emit: [result, " = (", obj.addr[], "[0].a == 11);"] .}
-    {. emit: [result, " = ", result, " && (", obj.addr[], "[0].b == \"foo\");"] .}
-    {. emit: [result, " = ", result, " && (", global, "[0].a == 11);"] .}
-    {. emit: [result, " = ", result, " && (", global, "[0].b == \"foo\");"] .}
+    {. emit: [result, " = (", obj.addr[], ".a == 11);"] .}
+    {. emit: [result, " = ", result, " && (", obj.addr[], ".b == \"foo\");"] .}
+    {. emit: [result, " = ", result, " && (", global, ".a == 11);"] .}
+    {. emit: [result, " = ", result, " && (", global, ".b == \"foo\");"] .}
   echo test()
 
 # Test addr of field:
@@ -62,8 +62,8 @@ block:
     result = result and obj1.`&&`.addr[] == "bar".cstring
     result = result and obj2.`if` == 0
     result = result and obj2.`for` == 0
-    result = result and obj2.`==`.isNil()
-    result = result and obj2.`&&`.isNil()
+    result = result and obj2.`==`.isNil
+    result = result and obj2.`&&`.isNil
   echo test()
 
 # Test codegen for fields with uppercase letters:
diff --git a/tests/js/tmodify_cstring.nim b/tests/js/tmodify_cstring.nim
new file mode 100644
index 000000000..82f8ccb23
--- /dev/null
+++ b/tests/js/tmodify_cstring.nim
@@ -0,0 +1,6 @@
+discard """
+  errormsg: "cstring doesn't support `[]=` operator"
+"""
+
+var x = cstring"abcd"
+x[0] = 'x'
diff --git a/tests/js/tnativeexc.nim b/tests/js/tnativeexc.nim
new file mode 100644
index 000000000..8b2b43e8f
--- /dev/null
+++ b/tests/js/tnativeexc.nim
@@ -0,0 +1,31 @@
+discard """
+  action: "run"
+"""
+
+import jsffi
+
+# Can catch JS exceptions
+try:
+  asm """throw new Error('a new error');"""
+except JsError as e:
+  doAssert e.message == "a new error"
+except:
+  doAssert false
+
+# Can distinguish different exceptions
+try:
+  asm """JSON.parse(';;');"""
+except JsEvalError:
+  doAssert false
+except JsSyntaxError as se:
+  doAssert se.message == "Unexpected token ';', \";;\" is not valid JSON"
+except JsError as e:
+  doAssert false
+
+# Can catch parent exception
+try:
+  asm """throw new SyntaxError();"""
+except JsError as e:
+  discard
+except:
+  doAssert false
diff --git a/tests/js/tneginthash.nim b/tests/js/tneginthash.nim
new file mode 100644
index 000000000..c082405c9
--- /dev/null
+++ b/tests/js/tneginthash.nim
@@ -0,0 +1,21 @@
+# issue #19929
+
+import std/[tables, hashes]
+
+type Foo = object
+  a: int
+
+proc hash(f: Foo): Hash =
+  var h: Hash = 0
+  h = h !& hash(f.a)
+  result = !$h
+
+proc transpose[T, S](data: array[T, S]): Table[S, T] =
+  for i, x in data:
+    result[x] = i
+
+const xs = [Foo(a: 5), Foo(a: -5)]
+const x = transpose(xs)
+
+doAssert x[Foo(a: -5)] == 1
+doAssert x[Foo(a: 5)] == 0
diff --git a/tests/js/tnilstrs.nim b/tests/js/tnilstrs.nim
new file mode 100644
index 000000000..6c1e4e401
--- /dev/null
+++ b/tests/js/tnilstrs.nim
@@ -0,0 +1,25 @@
+block:
+  var x: string
+  var y = "foo"
+
+  echo x
+  doAssert x == ""
+  doAssert "" == x
+
+  add(x, y)
+  y[0] = 'm'
+  doAssert y == "moo" and x == "foo"
+
+block:
+  var x = "foo".cstring
+  var y: string
+  add(y, x)
+  doAssert y == "foo"
+
+block:
+  type Foo = object
+    a: string
+  var foo = Foo(a: "foo")
+  var y = move foo.a
+  doAssert foo.a.len == 0
+  doAssert y == "foo"
diff --git a/tests/js/tos.nim b/tests/js/tos.nim
new file mode 100644
index 000000000..40fb52bcf
--- /dev/null
+++ b/tests/js/tos.nim
@@ -0,0 +1,21 @@
+# xxx consider merging this in tests/stdlib/tos.nim for increased coverage (with selecting disabling)
+
+static: doAssert defined(nodejs)
+
+import os
+
+block:
+  doAssert "./foo//./bar/".normalizedPath == "foo/bar"
+  doAssert relativePath(".//foo/bar", "foo") == "bar"
+  doAssert "/".isAbsolute
+  doAssert not "".isAbsolute
+  doAssert not ".".isAbsolute
+  doAssert not "foo".isAbsolute
+  doAssert relativePath("", "bar") == ""
+  doAssert normalizedPath(".///foo//./") == "foo"
+
+  when nimvm: discard
+  else:
+    let cwd = getCurrentDir()
+    doAssert cwd.isAbsolute
+    doAssert relativePath(getCurrentDir() / "foo", "bar") == ".." / "foo"
diff --git a/tests/js/trepr.nim b/tests/js/trepr.nim
index 366d247c5..a562ad63b 100644
--- a/tests/js/trepr.nim
+++ b/tests/js/trepr.nim
@@ -1,6 +1,4 @@
-discard """
-  action: run
-"""
+# xxx consider merging with `tests/stdlib/trepr.nim` to increase overall test coverage
 
 block ints:
   let
@@ -137,15 +135,13 @@ block tuples:
   when defined js:
     doAssert(repr(ot) == """
 [Field0 = true,
-Field1 = 120]
-""")
+Field1 = 120]""")
     doAssert(repr(t) == """
 [Field0 = 42,
 Field1 = 12.34,
 Field2 = "tuple",
 Field3 = [Field0 = true,
-Field1 = 120]]
-""")
+Field1 = 120]]""")
 
 block objects:
   type
@@ -162,14 +158,12 @@ block objects:
 
   doAssert(repr(oo) == """
 [a = true,
-b = 120]
-""")
+b = 120]""")
   doAssert(repr(o) == """
 [a = 42,
 b = 12.34,
 c = [a = true,
-b = 120]]
-""")
+b = 120]]""")
 
 block arrays:
   type
@@ -183,15 +177,14 @@ block arrays:
     c = [o, o, o]
     d = ["hi", "array", "!"]
 
-  doAssert(repr(a) == "[0.0, 1.0, 2.0]\n")
-  doAssert(repr(b) == "[[0.0, 1.0, 2.0], [0.0, 1.0, 2.0], [0.0, 1.0, 2.0]]\n")
+  doAssert(repr(a) == "[0.0, 1.0, 2.0]")
+  doAssert(repr(b) == "[[0.0, 1.0, 2.0], [0.0, 1.0, 2.0], [0.0, 1.0, 2.0]]")
   doAssert(repr(c) == """
 [[x = 42,
 y = [0.0, 1.0, 2.0]], [x = 42,
 y = [0.0, 1.0, 2.0]], [x = 42,
-y = [0.0, 1.0, 2.0]]]
-""")
-  doAssert(repr(d) == "[\"hi\", \"array\", \"!\"]\n")
+y = [0.0, 1.0, 2.0]]]""")
+  doAssert(repr(d) == "[\"hi\", \"array\", \"!\"]")
 
 block seqs:
   type
@@ -205,15 +198,14 @@ block seqs:
     c = @[o, o, o]
     d = @["hi", "array", "!"]
 
-  doAssert(repr(a) == "@[0.0, 1.0, 2.0]\n")
-  doAssert(repr(b) == "@[@[0.0, 1.0, 2.0], @[0.0, 1.0, 2.0], @[0.0, 1.0, 2.0]]\n")
+  doAssert(repr(a) == "@[0.0, 1.0, 2.0]")
+  doAssert(repr(b) == "@[@[0.0, 1.0, 2.0], @[0.0, 1.0, 2.0], @[0.0, 1.0, 2.0]]")
   doAssert(repr(c) == """
 @[[x = 42,
 y = @[0.0, 1.0, 2.0]], [x = 42,
 y = @[0.0, 1.0, 2.0]], [x = 42,
-y = @[0.0, 1.0, 2.0]]]
-""")
-  doAssert(repr(d) == "@[\"hi\", \"array\", \"!\"]\n")
+y = @[0.0, 1.0, 2.0]]]""")
+  doAssert(repr(d) == "@[\"hi\", \"array\", \"!\"]")
 
 block ptrs:
   type
@@ -226,13 +218,12 @@ block ptrs:
     c = addr a[2]
     d = AObj()
 
-  doAssert(repr(a) == "[12.0, 13.0, 14.0]\n")
-  doAssert(repr(b) == "ref 0 --> 12.0\n")
-  doAssert(repr(c) == "ref 2 --> 14.0\n")
+  doAssert(repr(a) == "[12.0, 13.0, 14.0]")
+  doAssert(repr(b) == "ref 0 --> 12.0")
+  doAssert(repr(c) == "ref 2 --> 14.0")
   doAssert(repr(d) == """
 [x = nil,
-y = 0]
-""")
+y = 0]""")
 
 block ptrs:
   type
@@ -248,8 +239,7 @@ block ptrs:
 [x = ref 0 --> [[x = nil,
 y = 0], [x = nil,
 y = 0]],
-y = 0]
-""")
+y = 0]""")
 
 block procs:
   proc test(): int =
@@ -258,9 +248,9 @@ block procs:
     ptest = test
     nilproc: proc(): int
 
-  doAssert(repr(test) == "0\n")
-  doAssert(repr(ptest) == "0\n")
-  doAssert(repr(nilproc) == "nil\n")
+  doAssert(repr(test) == "0")
+  doAssert(repr(ptest) == "0")
+  doAssert(repr(nilproc) == "nil")
 
 block bunch:
   type
@@ -293,7 +283,7 @@ block bunch:
     result[] = b
 
   var
-    aa: A
+    aa = default(A)
     bb: B = B(a: "inner", b: @['o', 'b', 'j'])
     cc: A = A(a: 12, b: 1, c: 1.2, d: '\0', e: eC,
                 f: "hello", g: {'A'}, h: {2'i16},
@@ -308,22 +298,21 @@ b = 0,
 c = 0.0,
 d = '\0',
 e = eA,
-f = nil,
+f = "",
 g = {},
 h = {},
-i = [nil, nil, nil],
-j = nil,
-k = 0,
-l = [a = nil,
-b = nil],
+i = ["", "", ""],
+j = @[],
+k = -12,
+l = [a = "",
+b = @[]],
 m = nil,
 n = nil,
-o = [Field0 = [a = nil,
-b = nil],
-Field1 = nil],
+o = [Field0 = [a = "",
+b = @[]],
+Field1 = ""],
 p = nil,
-q = nil]
-""")
+q = nil]""")
   doAssert(repr(cc) == """
 [a = 12,
 b = 1,
@@ -346,8 +335,7 @@ o = [Field0 = [a = "inner",
 b = @['o', 'b', 'j']],
 Field1 = "tuple!"],
 p = 0,
-q = "cstringtest"]
-""")
+q = "cstringtest"]""")
 
 block another:
   type
@@ -358,9 +346,9 @@ block another:
     Size3 = enum
       s3e=0, s3f=2000000000
 
-  doAssert(repr([s1a, s1b]) == "[s1a, s1b]\n")
-  doAssert(repr([s2c, s2d]) == "[s2c, s2d]\n")
-  doAssert(repr([s3e, s3f]) == "[s3e, s3f]\n")
+  doAssert(repr([s1a, s1b]) == "[s1a, s1b]")
+  doAssert(repr([s2c, s2d]) == "[s2c, s2d]")
+  doAssert(repr([s3e, s3f]) == "[s3e, s3f]")
 
 block another2:
 
@@ -395,15 +383,13 @@ block another2:
 y = 13,
 z = 45,
 s = ["abc", "xyz"],
-e = en6]
-""")
+e = en6]""")
   doAssert(repr(q) == """
 ref 0 --> [x = 0,
 y = 13,
 z = 45,
 s = ["abc", "xyz"],
-e = en6]
-""")
+e = en6]""")
   doAssert(repr(s) == """
 @[ref 0 --> [x = 0,
 y = 13,
@@ -421,8 +407,7 @@ e = en6], ref 3 --> [x = 0,
 y = 13,
 z = 45,
 s = ["abc", "xyz"],
-e = en6]]
-""")
+e = en6]]""")
   doAssert(repr(en4) == "en4")
 
   doAssert(repr({'a'..'p'}) == "{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'}")
diff --git a/tests/js/treprinifexpr.nim b/tests/js/treprinifexpr.nim
new file mode 100644
index 000000000..09ded18b9
--- /dev/null
+++ b/tests/js/treprinifexpr.nim
@@ -0,0 +1,18 @@
+type
+  Enum = enum A
+
+let
+  enumVal = A
+  tmp = if true: $enumVal else: $enumVal
+
+let
+  intVal = 12
+  tmp2 = if true: repr(intVal) else: $enumVal
+
+let
+  strVal = "123"
+  tmp3 = if true: repr(strVal) else: $strVal
+
+let
+  floatVal = 12.4
+  tmp4 = if true: repr(floatVal) else: $floatVal
\ No newline at end of file
diff --git a/tests/js/tseqops.nim b/tests/js/tseqops.nim
index d10e1ca6a..8cfb50886 100644
--- a/tests/js/tseqops.nim
+++ b/tests/js/tseqops.nim
@@ -1,11 +1,3 @@
-discard """
-  output: '''(x: 0, y: 0)
-(x: 5, y: 0)
-@[(x: 2, y: 4), (x: 4, y: 5), (x: 4, y: 5)]
-@[(a: 3, b: 3), (a: 1, b: 1), (a: 2, b: 2)]
-'''
-"""
-
 # bug #4139
 
 type
@@ -17,8 +9,8 @@ proc onLoad() =
   var foo = TestO(x: 0, y: 0)
   test.add(foo)
   foo.x = 5
-  echo(test[0])
-  echo foo
+  doAssert $test[0] == "(x: 0, y: 0)"
+  doAssert $foo == "(x: 5, y: 0)"
 
 onLoad()
 
@@ -34,7 +26,7 @@ proc foo(x: var seq[MyObj]) =
 
 var s = @[MyObj(x: "2", y: 4), MyObj(x: "4", y: 5)]
 foo(s)
-echo s
+doAssert $s == """@[(x: "2", y: 4), (x: "4", y: 5), (x: "4", y: 5)]"""
 
 # bug  #5933
 import sequtils
@@ -48,4 +40,9 @@ var test = @[Test(a: "1", b: 1), Test(a: "2", b: 2)]
 
 test.insert(@[Test(a: "3", b: 3)], 0)
 
-echo test
+doAssert $test == """@[(a: "3", b: 3), (a: "1", b: 1), (a: "2", b: 2)]"""
+
+proc hello(): array[5, int] = discard
+var x = @(hello())
+x.add(2)
+doAssert x == @[0, 0, 0, 0, 0, 2]
diff --git a/tests/js/tsourcemap.nim b/tests/js/tsourcemap.nim
new file mode 100644
index 000000000..d358e4a57
--- /dev/null
+++ b/tests/js/tsourcemap.nim
@@ -0,0 +1,96 @@
+discard """
+  action: "run"
+  targets: "js"
+  cmd: "nim js -r -d:nodejs $options --sourceMap:on $file"
+"""
+import std/[os, json, strutils, sequtils, algorithm, assertions, paths, compilesettings]
+
+# Implements a very basic sourcemap parser and then runs it on itself.
+# Allows to check for basic problems such as bad counts and lines missing (e.g. issue #21052)
+
+type
+  SourceMap = object
+    version:   int
+    sources:   seq[string]
+    names:     seq[string]
+    mappings:  string
+    file:      string
+
+  Line = object
+    line, column: int
+    file: string
+
+const
+  flag = 1 shl 5
+  signBit = 0b1
+  fourBits = 0b1111
+  fiveBits = 0b11111
+  mask = (1 shl 5) - 1
+  alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+
+var b64Table: seq[int] = 0.repeat(max(alphabet.mapIt(it.ord)) + 1)
+for i, b in alphabet.pairs:
+  b64Table[b.ord] = i
+
+# From https://github.com/juancarlospaco/nodejs/blob/main/src/nodejs/jsfs.nim
+proc importFs*() {.importjs: "var fs = require(\"fs\");".}
+proc readFileSync*(path: cstring): cstring {.importjs: "(fs.$1(#).toString())".}
+importFS()
+# Read in needed files
+let
+  jsFileName = string(querySetting(outDir).Path / "tsourcemap.js".Path)
+  mapFileName = jsFileName & ".map"
+
+  data = parseJson($mapFileName.cstring.readFileSync()).to(SourceMap)
+  jsFile = $readFileSync(jsFileName.cstring)
+
+proc decodeVLQ(inp: string): seq[int] =
+  var
+    shift, value: int
+  for v in inp.mapIt(b64Table[it.ord]):
+    value += (v and mask) shl shift
+    if (v and flag) > 0:
+      shift += 5
+      continue
+    result &= (value shr 1) * (if (value and 1) > 0: -1 else: 1)
+    shift = 0
+    value = 0
+
+
+# Keep track of state
+var
+  line = 0
+  source = 0
+  name = 0
+  column = 0
+  jsLine = 1
+  lines: seq[Line]
+
+for gline in data.mappings.split(';'):
+  jsLine += 1
+  var jsColumn = 0
+  for item in gline.strip().split(','):
+    let value = item.decodeVLQ()
+    doAssert value.len in [0, 1, 4, 5]
+    if value.len == 0:
+      continue
+    jsColumn += value[0]
+    if value.len >= 4:
+      source += value[1]
+      line += value[2]
+      column += value[3]
+      lines &= Line(line: line, column: column, file: data.sources[source])
+
+let jsLines = jsFile.splitLines().len
+# There needs to be a mapping for every line in the JS
+# If there isn't then the JS lines wont match up with Nim lines.
+# Except we don't care about the final line since that doesn't need to line up
+doAssert data.mappings.count(';') == jsLines - 1
+
+# Check we can find this file somewhere in the source map
+var foundSelf = false
+for line in lines:
+  if "tsourcemap.nim" in line.file:
+    foundSelf = true
+    doAssert line.line in 0..<jsLines, "Lines is out of bounds for file"
+doAssert foundSelf, "Couldn't find tsourcemap.nim in source map"
diff --git a/tests/js/tstdlib_imports.nim b/tests/js/tstdlib_imports.nim
new file mode 100644
index 000000000..db851ba28
--- /dev/null
+++ b/tests/js/tstdlib_imports.nim
@@ -0,0 +1,80 @@
+discard """
+  action: compile
+"""
+
+{.warning[UnusedImport]: off.}
+
+when defined(nimPreviewSlimSystem):
+  import std/[
+    syncio, assertions, formatfloat, objectdollar, widestrs
+  ]
+
+import std/[
+  # Core:
+  bitops, typetraits, lenientops, macros, volatile, typeinfo,
+  # fails due to FFI: rlocks
+  # fails due to cstring cast/copyMem: endians
+  # works but uses FFI: cpuinfo, locks
+
+  # Algorithms:
+  algorithm, enumutils, sequtils, setutils,
+  
+  # Collections:
+  critbits, deques, heapqueue, intsets, lists, options, sets,
+  tables, packedsets,
+
+  # Strings:
+  cstrutils, editdistance, wordwrap, parseutils, ropes,
+  pegs, strformat, strmisc, strscans, strtabs,
+  strutils, unicode, unidecode,
+  # fails due to FFI: encodings
+
+  # Time handling:
+  monotimes, times,
+
+  # Generic operator system services:
+  os, streams,
+  # fails intentionally: dynlib, marshal, memfiles
+  # fails due to FFI: osproc, terminal
+  # fails due to osproc import: distros
+
+  # Math libraries:
+  complex, math, random, rationals, stats, sums, sysrand,
+  # works but uses FFI: fenv
+
+  # Internet protocols:
+  cookies, httpcore, mimetypes, uri,
+  # fails due to FFI: asyncdispatch, asyncfile, asyncftpclient, asynchttpserver,
+  # asyncnet, cgi, httpclient, nativesockets, net, selectors
+  # works but no need to test: asyncstreams, asyncfutures
+  
+  # Threading:
+  # fails due to FFI: threadpool
+
+  # Parsers:
+  htmlparser, json, lexbase, parsecfg, parsecsv, parsesql, parsexml,
+  parseopt, jsonutils,
+
+  # XML processing:
+  xmltree, xmlparser,
+
+  # Generators:
+  htmlgen,
+
+  # Hashing:
+  base64, hashes,
+  # fails due to cstring cast/endians import: oids
+  # fails due to copyMem/endians import: sha1
+
+  # Miscellaneous:
+  colors, logging, sugar, unittest, varints, enumerate, with,
+  # fails due to FFI: browsers, coro
+  # works but uses FFI: segfaults
+
+  # Modules for JS backend:
+  asyncjs, dom, jsconsole, jscore, jsffi, jsbigints,
+
+  # Unlisted in lib.html:
+  decls, compilesettings, wrapnils, exitprocs, effecttraits,
+  genasts, importutils, isolation, jsfetch, jsformdata, jsheaders
+]
diff --git a/tests/js/tstdlib_various.nim b/tests/js/tstdlib_various.nim
new file mode 100644
index 000000000..1e584f735
--- /dev/null
+++ b/tests/js/tstdlib_various.nim
@@ -0,0 +1,174 @@
+discard """
+output: '''
+abc
+def
+definition
+prefix
+xyz
+def
+definition
+Hi Andreas! How do you feel, Rumpf?
+
+@[0, 2, 1]
+@[1, 0, 2]
+@[1, 2, 0]
+@[2, 0, 1]
+@[2, 1, 0]
+@[2, 0, 1]
+@[1, 2, 0]
+@[1, 0, 2]
+@[0, 2, 1]
+@[0, 1, 2]
+[5]
+[4, 5]
+[3, 4, 5]
+[2, 3, 4, 5]
+[2, 3, 4, 5, 6]
+[1, 2, 3, 4, 5, 6]
+'''
+"""
+
+import
+  critbits, sets, strutils, tables, random, algorithm, ropes,
+  lists, htmlgen, xmltree, strtabs
+
+
+block tcritbits:
+  var r: CritBitTree[void]
+  r.incl "abc"
+  r.incl "xyz"
+  r.incl "def"
+  r.incl "definition"
+  r.incl "prefix"
+  doAssert r.contains"def"
+  #r.del "def"
+
+  for w in r.items:
+    echo w
+  for w in r.itemsWithPrefix("de"):
+    echo w
+
+
+
+block testequivalence:
+  doAssert(toHashSet(@[1,2,3]) <= toHashSet(@[1,2,3,4]), "equivalent or subset")
+  doAssert(toHashSet(@[1,2,3]) <= toHashSet(@[1,2,3]), "equivalent or subset")
+  doAssert((not(toHashSet(@[1,2,3]) <= toHashSet(@[1,2]))), "equivalent or subset")
+  doAssert(toHashSet(@[1,2,3]) <= toHashSet(@[1,2,3,4]), "strict subset")
+  doAssert((not(toHashSet(@[1,2,3]) < toHashSet(@[1,2,3]))), "strict subset")
+  doAssert((not(toHashSet(@[1,2,3]) < toHashSet(@[1,2]))), "strict subset")
+  doAssert((not(toHashSet(@[1,2,3]) == toHashSet(@[1,2,3,4]))), "==")
+  doAssert(toHashSet(@[1,2,3]) == toHashSet(@[1,2,3]), "==")
+  doAssert((not(toHashSet(@[1,2,3]) == toHashSet(@[1,2]))), "==")
+
+
+
+block tformat:
+  echo("Hi $1! How do you feel, $2?\n" % ["Andreas", "Rumpf"])
+
+
+
+block tnilecho:
+  var x = @["1", "", "3"]
+  doAssert $x == """@["1", "", "3"]"""
+
+
+
+block torderedtable:
+  var t = initOrderedTable[int,string]()
+
+  # this tests issue #5917
+  var data = newSeq[int]()
+  for i in 0..<1000:
+    var x = rand(1000)
+    if x notin t: data.add(x)
+    t[x] = "meh"
+
+  # this checks that keys are re-inserted
+  # in order when table is enlarged.
+  var i = 0
+  for k, v in t:
+    doAssert(k == data[i])
+    doAssert(v == "meh")
+    inc(i)
+
+
+
+block tpermutations:
+  var v = @[0, 1, 2]
+  while v.nextPermutation():
+    echo v
+  while v.prevPermutation():
+    echo v
+
+
+block tropes:
+  var
+    r1 = rope("")
+    r2 = rope("123")
+  doAssert r1.len == 0
+  doAssert r2.len == 3
+  doAssert $r1 == ""
+  doAssert $r2 == "123"
+
+  r1.add("123")
+  r2.add("456")
+  doAssert r1.len == 3
+  doAssert r2.len == 6
+  doAssert $r1 == "123"
+  doAssert $r2 == "123456"
+  doAssert $r1[1] == "2"
+  doAssert $r2[2] == "3"
+
+
+block tsinglylinkedring:
+  var r = initSinglyLinkedRing[int]()
+  r.prepend(5)
+  echo r
+  r.prepend(4)
+  echo r
+  r.prepend(3)
+  echo r
+  r.prepend(2)
+  echo r
+  r.append(6)
+  echo r
+  r.prepend(1)
+  echo r
+
+block tsplit:
+  var s = ""
+  for w in split("|abc|xy|z", {'|'}):
+    s.add("#")
+    s.add(w)
+
+  doAssert s == "##abc#xy#z"
+
+block tsplit2:
+  var s = ""
+  for w in split("|abc|xy|z", {'|'}):
+    s.add("#")
+    s.add(w)
+
+  doAssert "true".split("") == @["true"]
+
+block txmlgen:
+  var nim = "Nim"
+  doAssert h1(a(href="http://force7.de/nim", nim)) ==
+    "<h1><a href=\"http://force7.de/nim\">Nim</a></h1>"
+
+block txmltree:
+  var x = <>a(href="nim.de", newText("www.nim-test.de"))
+
+  doAssert($x == "<a href=\"nim.de\">www.nim-test.de</a>")
+  doAssert(newText("foo").innerText == "foo")
+  doAssert(newEntity("bar").innerText == "bar")
+  doAssert(newComment("baz").innerText == "")
+
+  let y = newXmlTree("x", [
+    newText("foo"),
+    newXmlTree("y", [
+      newText("bar")
+    ])
+  ])
+  doAssert(y.innerText == "foobar")
diff --git a/tests/js/tstreams.nim b/tests/js/tstreams.nim
new file mode 100644
index 000000000..43c26e01a
--- /dev/null
+++ b/tests/js/tstreams.nim
@@ -0,0 +1,22 @@
+discard """
+  output: '''
+I
+AM
+GROOT
+'''
+"""
+
+import streams
+
+var s = newStringStream("I\nAM\nGROOT")
+doAssert s.peekStr(1) == "I"
+doAssert s.peekChar() == 'I'
+for line in s.lines:
+  echo line
+s.close
+
+var s2 = newStringStream("abc")
+doAssert s2.readAll == "abc"
+s2.write("def")
+doAssert s2.data == "abcdef"
+s2.close
diff --git a/tests/js/tstringitems.nim b/tests/js/tstringitems.nim
index ff016642e..16df04149 100644
--- a/tests/js/tstringitems.nim
+++ b/tests/js/tstringitems.nim
@@ -1,6 +1,9 @@
 discard """
   output: '''Hello
-Hello'''
+Hello
+c
+d
+e'''
 """
 
 block: # bug #2581
@@ -45,7 +48,7 @@ block: # Test compile-time binary data generation, invalid unicode
 block: # Test unicode strings
   const constStr = "Привет!"
   var jsStr : cstring
-  {.emit: """`jsStr`[0] = "Привет!";""".}
+  {.emit: """`jsStr` = "Привет!";""".}
 
   doAssert($jsStr == constStr)
   var runtimeStr = "При"
@@ -85,3 +88,8 @@ block: # String cmp
   doAssert(cmp("foo", "foobar") == -3)
   doAssert(cmp("fooz", "foog") == 19)
   doAssert(cmp("foog", "fooz") == -19)
+
+proc main(x: openArray[char]) =
+  for c in x: echo c
+
+main(toOpenArray(['a', 'b', 'c', 'd', 'e'], 2, 4))
diff --git a/tests/js/ttempgen.nim b/tests/js/ttempgen.nim
new file mode 100644
index 000000000..badc66c1b
--- /dev/null
+++ b/tests/js/ttempgen.nim
@@ -0,0 +1,79 @@
+discard """
+  output: '''
+foo
+'''
+"""
+
+block: # #12672
+  var a = @[1]
+  let i = 1
+  inc a[i-1]
+
+  var b: seq[int]
+  doAssertRaises(IndexDefect): inc b[0]
+  doAssertRaises(IndexDefect): inc b[i-1]
+
+  var x: seq[seq[int]]
+  doAssertRaises(IndexDefect): # not TypeError
+    inc x[0][i-1]
+
+block: # #14087
+  type Obj = object
+    str: string
+
+  var s = @[Obj(str: "abc"), Obj(str: "def")]
+  s[1].str.add("ghi")
+  s[s.len - 1].str.add("jkl")
+  s[^1].str.add("mno")
+  s[s.high].str.add("pqr")
+
+  let slen = s.len
+  s[slen - 1].str.add("stu")
+
+  let shigh = s.high
+  s[shigh].str.add("vwx")
+
+  proc foo(): int =
+    echo "foo"
+    shigh
+  s[foo()].str.add("yz")
+  doAssert s[1].str == "defghijklmnopqrstuvwxyz"
+
+block: # #14117
+  type
+    A = object
+      case kind: bool
+      of true:
+        sons: seq[int]
+      else: discard
+
+  var a = A(kind: true)
+  doAssert a.sons.len == 0
+  a.sons.add(1)
+  doAssert a.sons.len == 1
+
+import tables
+
+block: # #13966
+  var t: Table[int8, array[int8, seq[tuple[]]]]
+
+  t[0] = default(array[int8, seq[tuple[]]])
+  t[0][0].add ()
+
+block: # #11783
+  proc fun(): string =
+    discard
+
+  var ret: string
+  ret.add fun()
+  doAssert ret == ""
+
+block: # #12256
+  var x: bool
+
+  doAssert x == false
+
+  reset x
+
+  doAssert x == false
+  doAssert x != true
diff --git a/tests/js/tthismangle.nim b/tests/js/tthismangle.nim
new file mode 100644
index 000000000..880abcc83
--- /dev/null
+++ b/tests/js/tthismangle.nim
@@ -0,0 +1,23 @@
+proc moo1(this: int) =
+  doAssert this == 42
+
+proc moo2(x: int) =
+  var this = x
+  doAssert this == 42
+
+proc moo3() =
+  for this in [1,1,1]:
+    doAssert this == 1
+
+proc moo4() =
+  type
+    X = object
+      this: int
+
+  var q = X(this: 42)
+  doAssert q.this == 42
+
+moo1(42)
+moo2(42)
+moo3()
+moo4()
diff --git a/tests/js/ttimes.nim b/tests/js/ttimes.nim
deleted file mode 100644
index 63972dd76..000000000
--- a/tests/js/ttimes.nim
+++ /dev/null
@@ -1,43 +0,0 @@
-discard """
-  action: run
-"""
-
-import times
-
-# $ date --date='@2147483647'
-# Tue 19 Jan 03:14:07 GMT 2038
-
-block yeardayTest:
-  doAssert fromUnix(2147483647).utc.yearday == 18
-
-block localTime:
-  var local = now()
-  let utc = local.utc
-  doAssert local.toTime == utc.toTime
-
-let a = fromUnix(1_000_000_000)
-let b = fromUnix(1_500_000_000)
-doAssert b - a == initDuration(seconds = 500_000_000)
-
-# Because we can't change the timezone JS uses, we define a simple static timezone for testing.
-
-proc staticZoneInfoFromUtc(time: Time): ZonedTime =
-  result.utcOffset = -7200
-  result.isDst = false
-  result.adjTime = time + 7200.seconds
-
-proc staticZoneInfoFromTz(adjTime: Time): ZonedTIme =
-  result.utcOffset = -7200
-  result.isDst = false
-  result.adjTime = adjTime
-
-let utcPlus2 = Timezone(zoneInfoFromUtc: staticZoneInfoFromUtc, zoneInfoFromTz: staticZoneInfoFromTz, name: "")
-
-block timezoneTests:
-  let dt = initDateTime(01, mJan, 2017, 12, 00, 00, utcPlus2)
-  doAssert $dt == "2017-01-01T12:00:00+02:00"
-  doAssert $dt.utc == "2017-01-01T10:00:00+00:00"
-  doAssert $dt.utc.inZone(utcPlus2) == $dt
-
-doAssert $initDateTime(01, mJan, 1911, 12, 00, 00, utc()) == "1911-01-01T12:00:00+00:00"
-doAssert $initDateTime(01, mJan, 0023, 12, 00, 00, utc()) == "0023-01-01T12:00:00+00:00"
\ No newline at end of file
diff --git a/tests/js/ttypedarray.nim b/tests/js/ttypedarray.nim
new file mode 100644
index 000000000..4807cb103
--- /dev/null
+++ b/tests/js/ttypedarray.nim
@@ -0,0 +1,26 @@
+discard """
+  matrix: "--jsbigint64:off -d:nimStringHash2; --jsbigint64:on"
+"""
+
+import std/private/jsutils
+
+proc main()=
+  template fn(a): untyped = jsConstructorName(a)
+  doAssert fn(array[2, int8].default) == "Int8Array"
+  doAssert fn(array[2, uint8].default) == "Uint8Array"
+  doAssert fn(array[2, byte].default) == "Uint8Array"
+  doAssert fn(array[2, char].default) == "Uint8Array"
+  whenJsNoBigInt64: discard
+  do:
+    doAssert fn(array[2, uint64].default) == "BigUint64Array"
+  doAssert fn([1'u8]) == "Uint8Array"
+  doAssert fn([1'u16]) == "Uint16Array"
+  doAssert fn([byte(1)]) == "Uint8Array"
+  doAssert fn([1.0'f32]) == "Float32Array"
+  doAssert fn(array[2, float32].default) == "Float32Array"
+  doAssert fn(array[2, float].default) == "Float64Array"
+  doAssert fn(array[2, float64].default) == "Float64Array"
+  doAssert fn([1.0]) == "Float64Array"
+  doAssert fn([1.0'f64]) == "Float64Array"
+
+main()
diff --git a/tests/js/tunion.nim b/tests/js/tunion.nim
new file mode 100644
index 000000000..e185495ad
--- /dev/null
+++ b/tests/js/tunion.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "`{.union.}` is not implemented for js backend."
+"""
+
+type Foo {.union.} = object
+  as_bytes: array[8, int8]
+  data: int64
diff --git a/tests/js/tunittest_error.nim b/tests/js/tunittest_error.nim
new file mode 100644
index 000000000..781e34338
--- /dev/null
+++ b/tests/js/tunittest_error.nim
@@ -0,0 +1,24 @@
+discard """
+  exitcode: 1
+  outputsub: "[FAILED] with exception"
+"""
+
+# see also: `tests/stdlib/tunittest_error.nim`
+
+import unittest
+
+proc ddd() =
+  raise newException(IOError, "didn't do stuff")
+
+proc ccc() =
+  ddd()
+
+proc bbb() =
+  ccc()
+
+proc aaa() =
+  bbb()
+
+test "with exception":
+  check 3 == 3
+  aaa()
diff --git a/tests/js/tunittest_error2.nim b/tests/js/tunittest_error2.nim
new file mode 100644
index 000000000..9c5af7529
--- /dev/null
+++ b/tests/js/tunittest_error2.nim
@@ -0,0 +1,16 @@
+discard """
+  exitcode: 1
+  outputsub: '''
+[<foreign exception>]
+[FAILED] Bad test
+  '''
+  matrix: "-d:nodejs"
+  targets: "js"
+  joinable: false
+"""
+
+# bug #16978
+import unittest
+test "Bad test":
+  var x: cstring = nil
+  let y = x[0]
diff --git a/tests/js/tunittests.nim b/tests/js/tunittests.nim
deleted file mode 100644
index 7c2e70563..000000000
--- a/tests/js/tunittests.nim
+++ /dev/null
@@ -1,11 +0,0 @@
-discard """
-  output: '''
-[Suite] Bacon
-  [OK] >:)'''
-"""
-
-import unittest
-
-suite "Bacon":
-  test ">:)":
-    check(true == true)
diff --git a/tests/js/tvarargs.nim b/tests/js/tvarargs.nim
index 2ddb421f8..8d57af58d 100644
--- a/tests/js/tvarargs.nim
+++ b/tests/js/tvarargs.nim
@@ -1,3 +1,6 @@
+discard """
+  output: "Hello, world"
+"""
 
 # bug #3584
 
@@ -8,5 +11,5 @@ type
 
 var console* {.importc.}: Console
 
-when isMainModule:
+when true:
   console.log "Hello, world"
diff --git a/tests/js/twritestacktrace.nim b/tests/js/twritestacktrace.nim
new file mode 100644
index 000000000..2fe2b1987
--- /dev/null
+++ b/tests/js/twritestacktrace.nim
@@ -0,0 +1,12 @@
+discard """
+  cmd: "nim js --panics:on $file"
+  output: '''Traceback (most recent call last)
+twritestacktrace.nim(12) at module twritestacktrace
+twritestacktrace.nim(10) at twritestacktrace.hello
+'''
+"""
+
+proc hello() =
+  writeStackTrace()
+
+hello()
diff --git a/tests/lent/t16898.nim b/tests/lent/t16898.nim
new file mode 100644
index 000000000..a69c6d244
--- /dev/null
+++ b/tests/lent/t16898.nim
@@ -0,0 +1,31 @@
+discard """
+  errormsg: "invalid type: 'lent QuadraticExt' in this context: 'proc (r: var QuadraticExt, a: lent QuadraticExt, b: lent QuadraticExt){.noSideEffect, gcsafe.}' for proc"
+"""
+
+# bug #16898
+type
+  Fp[N: static int, T] = object
+    big: array[N, T]
+
+type
+  QuadraticExt* = concept x
+    ## Quadratic Extension concept (like complex)
+    type BaseField = auto
+    x.c0 is BaseField
+    x.c1 is BaseField
+
+{.experimental:"views".}
+
+func prod(r: var QuadraticExt, a, b: lent QuadraticExt) =
+  discard
+
+type
+  Fp2[N: static int, T] = object
+    c0, c1: Fp[N, T]
+
+# This should be passed by reference,
+# but concepts do not respect the 24 bytes rule
+# or `byref` pragma.
+var r, a, b: Fp2[6, uint64]
+
+prod(r, a, b)
diff --git a/tests/lent/t17621.nim b/tests/lent/t17621.nim
new file mode 100644
index 000000000..e324a963e
--- /dev/null
+++ b/tests/lent/t17621.nim
@@ -0,0 +1,15 @@
+discard """
+  errormsg: "invalid type: 'lent Test' in this context: 'proc (self: lent Test)' for proc"
+"""
+
+# bug #17621
+{.experimental: "views".}
+
+type Test = ref object
+  foo: int
+
+proc modify(self: lent Test) =
+  self.foo += 1
+
+let test = Test(foo: 12)
+modify(test)
diff --git a/tests/lent/tbasic_lent_check.nim b/tests/lent/tbasic_lent_check.nim
index 1d954725b..ce9b89adf 100644
--- a/tests/lent/tbasic_lent_check.nim
+++ b/tests/lent/tbasic_lent_check.nim
@@ -1,5 +1,5 @@
 discard """
-  cmd: "nim c --newRuntime $file"
+  targets: "c cpp js"
   output: "1"
 """
 
@@ -15,3 +15,48 @@ proc main =
   doAssert(not compiles(passToVar(viewInto(x))))
 
 main()
+
+template main2 = # bug #15958
+  when defined(js):
+    proc sameAddress[T](a, b: T): bool {.importjs: "(# === #)".}
+  else:
+    template sameAddress(a, b): bool = a.unsafeAddr == b.unsafeAddr
+  proc byLent[T](a: T): lent T = a
+  let a = [11,12]
+  let b = @[21,23]
+  let ss = {1, 2, 3, 5}
+  doAssert byLent(a) == [11,12]
+  doAssert sameAddress(byLent(a), a)
+  doAssert byLent(b) == @[21,23]
+  # bug #16073
+  doAssert sameAddress(byLent(b), b)
+  doAssert byLent(ss) == {1, 2, 3, 5}
+  doAssert sameAddress(byLent(ss), ss)
+
+  let r = new(float)
+  r[] = 10.0
+  # bug #16073
+  doAssert byLent(r)[] == 10.0
+
+  when not defined(js): # pending bug https://github.com/timotheecour/Nim/issues/372
+    let p = create(float)
+    p[] = 20.0
+    doAssert byLent(p)[] == 20.0
+
+  proc byLent2[T](a: openArray[T]): lent T = a[0]
+  doAssert byLent2(a) == 11
+  doAssert sameAddress(byLent2(a), a[0])
+  doAssert byLent2(b) == 21
+  doAssert sameAddress(byLent2(b), b[0])
+
+  proc byLent3[T](a: varargs[T]): lent T = a[1]
+  let 
+    x = 10
+    y = 20
+    z = 30
+  doAssert byLent3(x, y, z) == 20
+
+main2()
+when false:
+  # bug: Error: unhandled exception: 'node' is not accessible using discriminant 'kind' of type 'TFullReg' [FieldDefect]
+  static: main2()
diff --git a/tests/lent/tlent_from_var.nim b/tests/lent/tlent_from_var.nim
new file mode 100644
index 000000000..1fb3d0c17
--- /dev/null
+++ b/tests/lent/tlent_from_var.nim
@@ -0,0 +1,107 @@
+discard """
+  output: '''x
+[10, 11, 12, 13]'''
+"""
+
+# bug #14805
+
+type Foo = object
+  a: string
+
+proc bar(f: var Foo): lent string =
+  result = f.a
+
+var foo = Foo(a: "x")
+echo bar(foo)
+
+
+# bug #14878
+
+# proc byLentImpl[T](a: T): lent T = a
+proc byLentVar[T](a: var T): lent T =
+  result = a
+  # result = a.byLentImpl # this doesn't help
+
+var x = 3 # error: cannot take the address of an rvalue of type 'NI *'
+
+var xs = [10,11,12,13] # SIGSEGV
+
+let x2 = x.byLentVar
+
+let xs2 = xs.byLentVar
+echo xs2
+
+# bug #22138
+
+type Xxx = object
+
+type
+  Opt[T] = object
+    case oResultPrivate*: bool
+    of false:
+      discard
+    of true:
+      vResultPrivate*: T
+
+func value*[T: not void](self: Opt[T]): lent T {.inline.} =
+  self.vResultPrivate
+template get*[T: not void](self: Opt[T]): T = self.value()
+
+method connect*(
+  self: Opt[(int, int)]) =
+  discard self.get()[0]
+
+block: # bug #23454
+  type
+    Letter = enum
+      A
+
+    LetterPairs = object
+      values: seq[(Letter, string)]
+
+  iterator items(list: var LetterPairs): lent (Letter, string) =
+    for item in list.values:
+      yield item
+
+  var instance = LetterPairs(values: @[(A, "foo")])
+
+  for (a, _) in instance:
+    case a
+    of A: discard
+
+block: # bug #23454
+  type
+    Letter = enum
+      A
+
+    LetterPairs = object
+      values: seq[(Letter, string)]
+
+  iterator items(list: var LetterPairs): var (Letter, string) =
+    for item in list.values.mItems:
+      yield item
+
+  var instance = LetterPairs(values: @[(A, "foo")])
+
+  for (a, _) in instance:
+    case a
+    of A: discard
+
+block: # bug #24034
+  type T = object
+    v: array[100, byte]
+
+
+  iterator pairs(t: T): (int, lent array[100, byte]) =
+    yield (0, t.v)
+
+
+  block:
+    for a, b in default(T):
+      doAssert a == 0
+      doAssert b.len == 100
+
+  block:
+    for (a, b) in pairs(default(T)):
+      doAssert a == 0
+      doAssert b.len == 100
diff --git a/tests/lent/tnot_allowed_lent.nim b/tests/lent/tnot_allowed_lent.nim
new file mode 100644
index 000000000..a1db6d184
--- /dev/null
+++ b/tests/lent/tnot_allowed_lent.nim
@@ -0,0 +1,24 @@
+discard """
+  errormsg: "expression has no address"
+"""
+type
+  MyObject = object
+    x: seq[string]
+
+proc mytest1(s: MyObject, i: int): lent string =
+  ## works fine
+  if i < s.x.len - 1 and s.x[i] != "":
+    result = s.x[i]
+  else: raise newException(KeyError, "err1")
+
+proc mytest2(s: MyObject, i: int): lent string =
+  ## reject due to if expr
+  if i < s.x.len - 1 and s.x[i] != "": s.x[i]
+  else: raise newException(KeyError, "err1")
+
+for i in 1..5:
+  var x = MyObject(x: @["1", "2", "3"])
+  echo mytest1(x, 1)
+  echo mytest2(x, 1)
+
+
diff --git a/tests/lent/tnot_allowed_lent2.nim b/tests/lent/tnot_allowed_lent2.nim
new file mode 100644
index 000000000..d0c958df3
--- /dev/null
+++ b/tests/lent/tnot_allowed_lent2.nim
@@ -0,0 +1,14 @@
+discard """
+  errormsg: "'x' cannot be assigned to"
+  line: 10
+"""
+
+proc bug14498 =
+  var a = @['a', 'b', 'c', 'd', 'e', 'f']
+
+  for x in a:
+    x = 'c'
+
+  echo a
+
+bug14498()
diff --git a/tests/lent/tvm.nim b/tests/lent/tvm.nim
new file mode 100644
index 000000000..5df1d1270
--- /dev/null
+++ b/tests/lent/tvm.nim
@@ -0,0 +1,21 @@
+block: # issue #17527
+  iterator items2[IX, T](a: array[IX, T]): lent T {.inline.} =
+    var i = low(IX)
+    if i <= high(IX):
+      while true:
+        yield a[i]
+        if i >= high(IX): break
+        inc(i)
+
+  proc main() =
+    var s: seq[string] = @[]
+    for i in 0..<3:
+      for (key, val) in items2([("any", "bar")]):
+        s.add $(i, key, val)
+    doAssert s == @[
+      "(0, \"any\", \"bar\")",
+      "(1, \"any\", \"bar\")",
+      "(2, \"any\", \"bar\")"
+    ]
+
+  static: main()
diff --git a/tests/let/t7936.nim b/tests/let/t7936.nim
new file mode 100644
index 000000000..3819dfc02
--- /dev/null
+++ b/tests/let/t7936.nim
@@ -0,0 +1,27 @@
+discard """
+  action: "run"
+"""
+
+import
+  tables, deques, sequtils
+
+const
+  lookupTable = {'(': ')', '{': '}', '[': ']'}.toTable
+
+proc isPaired*(value: string): bool =
+  var stack = initDeque[char]() 
+
+  for item in value:
+    # echo "Looking at " & item
+    if item in lookupTable:
+      stack.addLast(item)
+    if item in toSeq(lookupTable.values):
+      if stack.len == 0:
+        return false
+      if lookupTable[stack.popLast()] != item:
+        return false
+
+  return stack.len == 0
+
+doAssert isPaired("{[()]}") == true
+doAssert isPaired("a)b(c") == false
diff --git a/tests/let/timportc.nim b/tests/let/timportc.nim
new file mode 100644
index 000000000..85244da9f
--- /dev/null
+++ b/tests/let/timportc.nim
@@ -0,0 +1,24 @@
+discard """
+targets: "c cpp js"
+"""
+
+when defined(c) or defined(cpp):
+  {.emit:"""
+  const int TEST1 = 123;
+  #define TEST2 321
+  """.}
+
+when defined(js):
+  {.emit:"""
+  const TEST1 = 123;
+  const TEST2 = 321; // JS doesn't have macros, so we just duplicate
+  """.}
+
+let
+  TEST0 = 1
+  TEST1 {.importc, nodecl.}: cint
+  TEST2 {.importc, nodecl.}: cint
+
+doAssert TEST0 == 1
+doAssert TEST1 == 123
+doAssert TEST2 == 321
diff --git a/tests/let/timportc2.nim b/tests/let/timportc2.nim
new file mode 100644
index 000000000..964305923
--- /dev/null
+++ b/tests/let/timportc2.nim
@@ -0,0 +1,8 @@
+discard """
+  errormsg: "'let' symbol requires an initialization"
+  line: "7"
+"""
+
+# Test that this still works when not annotated with importc
+let test: cint
+echo test
diff --git a/tests/let/tlet.nim b/tests/let/tlet.nim
index 6703ba254..25d7b9bf7 100644
--- a/tests/let/tlet.nim
+++ b/tests/let/tlet.nim
@@ -1,6 +1,6 @@
 discard """
-  line: "10"
   errormsg: "'name' cannot be assigned to"
+  line: "10"
 """
 
 echo("What's your name? ")
@@ -8,4 +8,3 @@ let name = readLine(stdin)
 while name == "":
   echo("Please tell me your name: ")
   name = readLine(stdin)
-
diff --git a/tests/let/tlet2.nim b/tests/let/tlet2.nim
index 2df15fa5c..63e7d6128 100644
--- a/tests/let/tlet2.nim
+++ b/tests/let/tlet2.nim
@@ -1,6 +1,6 @@
 discard """
-  line: "13"
   errormsg: "type mismatch: got <int literal(8), int literal(5), int, int>"
+  line: "13"
 """
 
 proc divmod(a, b: int, res, remainder: var int) =
@@ -13,4 +13,3 @@ let
 divmod(8, 5, x, y) # modifies x and y
 echo(x)
 echo(y)
-
diff --git a/tests/lexer/mlexerutils.nim b/tests/lexer/mlexerutils.nim
new file mode 100644
index 000000000..eae7a0006
--- /dev/null
+++ b/tests/lexer/mlexerutils.nim
@@ -0,0 +1,9 @@
+import macros
+
+macro lispReprStr*(a: untyped): untyped = newLit(a.lispRepr)
+
+macro assertAST*(expected: string, struct: untyped): untyped =
+  var ast = newLit(struct.treeRepr)
+  result = quote do:
+    if `ast` != `expected`:
+      doAssert false, "\nGot:\n" & `ast`.indent(2) & "\nExpected:\n" & `expected`.indent(2)
\ No newline at end of file
diff --git a/tests/lexer/tcustom_numeric_literals.nim b/tests/lexer/tcustom_numeric_literals.nim
new file mode 100644
index 000000000..35b4803d3
--- /dev/null
+++ b/tests/lexer/tcustom_numeric_literals.nim
@@ -0,0 +1,177 @@
+discard """
+  targets: "c cpp js"
+"""
+
+# Test tkStrNumLit
+
+import std/[macros, strutils]
+import mlexerutils
+
+# AST checks
+
+assertAST dedent """
+  StmtList
+    ProcDef
+      AccQuoted
+        Ident "\'"
+        Ident "wrap"
+      Empty
+      Empty
+      FormalParams
+        Ident "string"
+        IdentDefs
+          Ident "number"
+          Ident "string"
+          Empty
+      Empty
+      Empty
+      StmtList
+        Asgn
+          Ident "result"
+          Infix
+            Ident "&"
+            Infix
+              Ident "&"
+              StrLit "[["
+              Ident "number"
+            StrLit "]]"""":
+  proc `'wrap`(number: string): string =
+    result = "[[" & number & "]]"
+
+assertAST dedent """
+  StmtList
+    DotExpr
+      RStrLit "-38383839292839283928392839283928392839283.928493849385935898243e-50000"
+      Ident "\'wrap"""":
+  -38383839292839283928392839283928392839283.928493849385935898243e-50000'wrap
+
+proc `'wrap`(number: string): string = "[[" & number & "]]"
+proc wrap2(number: string): string = "[[" & number & "]]"
+doAssert lispReprStr(-1'wrap) == """(DotExpr (RStrLit "-1") (Ident "\'wrap"))"""
+
+template main =
+  block: # basic suffix usage
+    template `'twrap`(number: string): untyped =
+      number.`'wrap`
+    proc extraContext(): string =
+      22.40'wrap
+    proc `*`(left, right: string): string =
+      result = left & "times" & right
+    proc `+`(left, right: string): string =
+      result = left & "plus" & right
+
+    doAssert 1'wrap == "[[1]]"
+    doAssert -1'wrap == "[[-1]]":
+      "unable to resolve a negative integer-suffix pattern"
+    doAssert 12345.67890'wrap == "[[12345.67890]]"
+    doAssert 1'wrap*1'wrap == "[[1]]times[[1]]":
+      "unable to resolve an operator between two suffixed numeric literals"
+    doAssert 1'wrap+ -1'wrap == "[[1]]plus[[-1]]":  # will generate a compiler warning about inconsistent spacing
+      "unable to resolve a negative suffixed numeric literal following an operator"
+    doAssert 1'wrap + -1'wrap == "[[1]]plus[[-1]]"
+    doAssert 1'twrap == "[[1]]"
+    doAssert extraContext() == "[[22.40]]":
+      "unable to return a suffixed numeric literal by an implicit return"
+    doAssert 0x5a3a'wrap == "[[0x5a3a]]"
+    doAssert 0o5732'wrap == "[[0o5732]]"
+    doAssert 0b0101111010101'wrap == "[[0b0101111010101]]"
+    doAssert -38383839292839283928392839283928392839283.928493849385935898243e-50000'wrap == "[[-38383839292839283928392839283928392839283.928493849385935898243e-50000]]"
+    doAssert 1234.56'wrap == "[[1234.56]]":
+      "unable to properly account for context with suffixed numeric literals"
+
+  block: # verify that the i64, f32, etc builtin suffixes still parse correctly
+    const expectedF32: float32 = 123.125
+    proc `'f9`(number: string): string =   # proc starts with 'f' just like 'f32'
+      "[[" & number & "]]"
+    proc `'f32a`(number: string): string =   # looks even more like 'f32'
+      "[[" & number & "]]"
+    proc `'d9`(number: string): string =   # proc starts with 'd' just like the d suffix
+      "[[" & number & "]]"
+    proc `'i9`(number: string): string =   # proc starts with 'i' just like 'i64'
+      "[[" & number & "]]"
+    proc `'u9`(number: string): string =   # proc starts with 'u' just like 'u8'
+      "[[" & number & "]]"
+
+    doAssert 123.125f32 == expectedF32:
+      "failing to support non-quoted legacy f32 floating point suffix"
+    doAssert 123.125'f32 == expectedF32
+    doAssert 123.125e0'f32 == expectedF32
+    doAssert 1234.56'wrap == 1234.56'f9
+    doAssert 1234.56'wrap == 1234.56'f32a
+    doAssert 1234.56'wrap == 1234.56'd9
+    doAssert 1234.56'wrap == 1234.56'i9
+    doAssert 1234.56'wrap == 1234.56'u9
+    doAssert lispReprStr(1234.56'u9) == """(DotExpr (RStrLit "1234.56") (Ident "\'u9"))""":
+      "failed to properly build AST for suffix that starts with u"
+    doAssert -128'i8 == (-128).int8
+
+  block: # case checks
+    doAssert 1E2 == 100:
+      "lexer not handling upper-case exponent"
+    doAssert 1.0E2 == 100.0
+    doAssert 1e2 == 100
+    doAssert 0xdeadBEEF'wrap == "[[0xdeadBEEF]]":
+      "lexer not maintaining original case"
+    doAssert 0.1E12'wrap == "[[0.1E12]]"
+    doAssert 0.0e12'wrap == "[[0.0e12]]"
+    doAssert 0.0e+12'wrap == "[[0.0e+12]]"
+    doAssert 0.0e-12'wrap == "[[0.0e-12]]"
+    doAssert 0e-12'wrap == "[[0e-12]]"
+
+  block: # macro and template usage
+    template `'foo`(a: string): untyped = (a, 2)
+    doAssert -12'foo == ("-12", 2)
+    template `'fooplus`(a: string, b: int): untyped = (a, b)
+    doAssert -12'fooplus(2) == ("-12", 2)
+    template `'fooplusopt`(a: string, b: int = 99): untyped = (a, b)
+    doAssert -12'fooplusopt(2) == ("-12", 2)
+    doAssert -12'fooplusopt() == ("-12", 99)
+    doAssert -12'fooplusopt == ("-12", 99)
+    macro `'bar`(a: static string): untyped = newLit(a.repr)
+    doAssert -12'bar == "\"-12\""
+    macro deb(a): untyped = newLit(a.repr)
+    doAssert deb(-12'bar) == "-12'bar"
+
+  block: # bug 1 from https://github.com/nim-lang/Nim/pull/17020#issuecomment-803193947
+    macro deb1(a): untyped = newLit a.repr
+    macro deb2(a): untyped =
+      a[1] = ident($a[1])
+      newLit a.lispRepr
+    doAssert deb1(-12'wrap) == "-12'wrap"
+    doAssert deb1(-12'nonexistent) == "-12'nonexistent"
+    doAssert deb2(-12'nonexistent) == """(DotExpr (RStrLit "-12") (Ident "\'nonexistent"))"""
+    doAssert deb2(-12.wrap2) == """(DotExpr (IntLit -12) (Ident "wrap2"))"""
+    doAssert deb2(-12'wrap) == """(DotExpr (RStrLit "-12") (Ident "\'wrap"))"""
+
+  block: # bug 2 from https://github.com/nim-lang/Nim/pull/17020#issuecomment-803193947
+    template toSuf(`'suf`): untyped =
+      let x = -12'suf
+      x
+    doAssert toSuf(`'wrap`) == "[[-12]]"
+
+  block: # bug 10 from https://github.com/nim-lang/Nim/pull/17020#issuecomment-803193947
+    proc `myecho`(a: auto): auto = a
+    template fn1(): untyped =
+      let a = "abc"
+      -12'wrap
+    template fn2(): untyped =
+      `myecho` -12'wrap
+    template fn3(): untyped =
+      -12'wrap
+    doAssert fn1() == "[[-12]]"
+    doAssert fn2() == "[[-12]]"
+    doAssert fn3() == "[[-12]]"
+
+    block: # bug 9 from https://github.com/nim-lang/Nim/pull/17020#issuecomment-803193947
+      macro metawrap(): untyped =
+        func wrap1(a: string): string = "{" & a & "}"
+        func `'wrap3`(a: string): string = "{" & a & "}"
+        result = quote do:
+          let a1 {.inject.} = wrap1"-128"
+          let a2 {.inject.} = -128'wrap3
+      metawrap()
+      doAssert a1 == "{-128}"
+      doAssert a2 == "{-128}"
+
+static: main()
+main()
diff --git a/tests/lexer/tident.nim b/tests/lexer/tident.nim
index 68cc01e70..e5177436d 100644
--- a/tests/lexer/tident.nim
+++ b/tests/lexer/tident.nim
@@ -1,6 +1,19 @@
+discard """
+output: '''
+Length correct
+Correct
+Correct
+Correct
+Correct
+Correct
+Correct
+Correct
+Correct
+'''
+"""
 
 type
-  TIdObj* = object of TObject
+  TIdObj* = object of RootObj
     id*: int                  # unique id; use this for comparisons and not the pointers
 
   PIdObj* = ref TIdObj
@@ -19,4 +32,3 @@ proc myNewString(L: int): string {.inline.} =
       echo("Wrong")
 
 var s = myNewString(8)
-
diff --git a/tests/lexer/tind1.nim b/tests/lexer/tind1.nim
index ffbde48fd..2185c3074 100644
--- a/tests/lexer/tind1.nim
+++ b/tests/lexer/tind1.nim
@@ -1,6 +1,6 @@
 discard """
-  line: 24
   errormsg: "invalid indentation"
+  line: 24
 """
 
 import macros
diff --git a/tests/lexer/tintegerliterals.nim b/tests/lexer/tintegerliterals.nim
new file mode 100644
index 000000000..fd401b71b
--- /dev/null
+++ b/tests/lexer/tintegerliterals.nim
@@ -0,0 +1,9 @@
+# test the valid literals
+doAssert 0b10 == 2
+doAssert 0B10 == 2
+doAssert 0x10 == 16
+doAssert 0X10 == 16
+doAssert 0o10 == 8
+# the following is deprecated:
+doAssert 0c10 == 8
+doAssert 0C10 == 8
diff --git a/tests/lexer/tinvalidintegerliteral1.nim b/tests/lexer/tinvalidintegerliteral1.nim
new file mode 100644
index 000000000..6bf7624f3
--- /dev/null
+++ b/tests/lexer/tinvalidintegerliteral1.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "invalid number"
+  file: "tinvalidintegerliteral1.nim"
+  line: 7
+"""
+
+echo 0b
diff --git a/tests/lexer/tinvalidintegerliteral2.nim b/tests/lexer/tinvalidintegerliteral2.nim
new file mode 100644
index 000000000..eb6efc131
--- /dev/null
+++ b/tests/lexer/tinvalidintegerliteral2.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "invalid number"
+  file: "tinvalidintegerliteral2.nim"
+  line: 7
+"""
+
+echo 0x
diff --git a/tests/lexer/tinvalidintegerliteral3.nim b/tests/lexer/tinvalidintegerliteral3.nim
new file mode 100644
index 000000000..e09cda54a
--- /dev/null
+++ b/tests/lexer/tinvalidintegerliteral3.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "0O5 is an invalid int literal; For octal literals use the '0o' prefix."
+  file: "tinvalidintegerliteral3.nim"
+  line: 7
+"""
+
+echo 0O5
diff --git a/tests/lexer/tlexerspaces.nim b/tests/lexer/tlexerspaces.nim
new file mode 100644
index 000000000..14b16111d
--- /dev/null
+++ b/tests/lexer/tlexerspaces.nim
@@ -0,0 +1,2 @@
+discard 12 +                                                                                                                                                                                                                                                                           5
+discard 12 + 5                                                                                                                        
diff --git a/tests/lexer/tmissingnl.nim b/tests/lexer/tmissingnl.nim
index 33b7debf1..dc939bcd2 100644
--- a/tests/lexer/tmissingnl.nim
+++ b/tests/lexer/tmissingnl.nim
@@ -1,10 +1,9 @@
 discard """
+  errormsg: "invalid indentation"
   file: "tmissingnl.nim"
   line: 7
-  errormsg: "invalid indentation"
 """
 
-import strutils var s: seq[int] = @[0, 1, 2, 3, 4, 5, 6]
+import strutils let s: seq[int] = @[0, 1, 2, 3, 4, 5, 6]
 
 #s[1..3] = @[]
-
diff --git a/tests/misc/trawstr.nim b/tests/lexer/trawstr.nim
index 55e508acc..aa41071d5 100644
--- a/tests/misc/trawstr.nim
+++ b/tests/lexer/trawstr.nim
@@ -1,12 +1,10 @@
 discard """
+  errormsg: "closing \" expected"
   file: "trawstr.nim"
   line: 10
-  errormsg: "closing \" expected"
 """
 # Test the new raw strings:
 
 const
   xxx = r"This is a raw string!"
   yyy = "This not\" #ERROR
-
-
diff --git a/tests/lexer/tstrlits.nim b/tests/lexer/tstrlits.nim
index cc8872f60..8e8250a5b 100644
--- a/tests/lexer/tstrlits.nim
+++ b/tests/lexer/tstrlits.nim
@@ -1,6 +1,5 @@
 discard """
-  file: "tstrlits.nim"
-  output: "a\"\"long string\"\"\"\"\"abc\"def_'2'●"
+  output: "a\"\"long string\"\"\"\"\"abc\"def_'2'●𝌆𝌆A"
 """
 # Test the new different string literals
 
@@ -11,14 +10,10 @@ const
 
   raw = r"abc""def"
 
-  escaped = "\x5f'\50'\u25cf"
+  escaped = "\x5f'\50'\u25cf\u{1D306}\u{1d306}\u{41}"
 
 
 stdout.write(rawQuote)
 stdout.write(tripleEmpty)
 stdout.write(raw)
-stdout.write(escaped)
-#OUT a""long string"""""abc"def
-
-
-
+stdout.writeLine(escaped)
diff --git a/tests/lexer/tunary_minus.nim b/tests/lexer/tunary_minus.nim
new file mode 100644
index 000000000..5ec2b5c70
--- /dev/null
+++ b/tests/lexer/tunary_minus.nim
@@ -0,0 +1,83 @@
+discard """
+  targets: "c cpp js"
+"""
+
+# Test numeric literals and handling of minus symbol
+
+import std/[macros, strutils]
+import std/private/jsutils
+
+import mlexerutils
+
+const one = 1
+const minusOne = `-`(one)
+
+# border cases that *should* generate compiler errors:
+assertAST dedent """
+  StmtList
+    Asgn
+      Ident "x"
+      Command
+        IntLit 4
+        IntLit -1""":
+  x = 4 -1
+assertAST dedent """
+  StmtList
+    VarSection
+      IdentDefs
+        Ident "x"
+        Ident "uint"
+        IntLit -1""":
+  var x: uint = -1
+template bad() =
+  x = 4 -1
+doAssert not compiles(bad())
+
+template main =
+  block: # check when a minus (-) is a negative sign for a literal
+    doAssert -1 == minusOne:
+      "unable to parse a spaced-prefixed negative int"
+    doAssert lispReprStr(-1) == """(IntLit -1)"""
+    doAssert -1.0'f64 == minusOne.float64
+    doAssert lispReprStr(-1.000'f64) == """(Float64Lit -1.0)"""
+    doAssert lispReprStr( -1.000'f64) == """(Float64Lit -1.0)"""
+    doAssert [-1].contains(minusOne):
+      "unable to handle negatives after square bracket"
+    doAssert lispReprStr([-1]) == """(Bracket (IntLit -1))"""
+    doAssert (-1, 2)[0] == minusOne:
+      "unable to handle negatives after parenthesis"
+    doAssert lispReprStr((-1, 2)) == """(TupleConstr (IntLit -1) (IntLit 2))"""
+    proc x(): int =
+      var a = 1;-1  # the -1 should act as the return value
+    doAssert x() == minusOne:
+      "unable to handle negatives after semi-colon"
+
+  block:
+    doAssert -0b111 == -7
+    doAssert -0xff == -255
+    doAssert -128'i8 == (-128).int8
+    doAssert $(-128'i8) == "-128"
+    doAssert -32768'i16 == int16.low
+    doAssert -2147483648'i32 == int32.low
+    when int.sizeof > 4:
+      doAssert -9223372036854775808 == int.low
+    whenJsNoBigInt64: discard
+    do:
+      doAssert -9223372036854775808 == int64.low
+
+  block: # check when a minus (-) is an unary op
+    doAssert -one == minusOne:
+      "unable to a negative prior to identifier"
+
+  block: # check when a minus (-) is a a subtraction op
+    doAssert 4-1 == 3:
+      "unable to handle subtraction sans surrounding spaces with a numeric literal"
+    doAssert 4-one == 3:
+      "unable to handle subtraction sans surrounding spaces with an identifier"
+    doAssert 4 - 1 == 3:
+      "unable to handle subtraction with surrounding spaces with a numeric literal"
+    doAssert 4 - one == 3:
+      "unable to handle subtraction with surrounding spaces with an identifier"
+
+static: main()
+main()
diff --git a/tests/lexer/tunderscores.nim b/tests/lexer/tunderscores.nim
index f64f36977..1896a2898 100644
--- a/tests/lexer/tunderscores.nim
+++ b/tests/lexer/tunderscores.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "invalid token: trailing underscore"
   file: "tunderscores.nim"
   line: 8
-  errormsg: "invalid token: trailing underscore"
 """
 # Bug #502670
 
@@ -9,6 +9,3 @@ var ef_ = 3  #ERROR_MSG invalid token: _
 var a__b = 1
 var c___d = 2
 echo(ab, cd, ef_)
-
-
-
diff --git a/tests/lexer/tunicode_operators.nim b/tests/lexer/tunicode_operators.nim
new file mode 100644
index 000000000..6ad40beab
--- /dev/null
+++ b/tests/lexer/tunicode_operators.nim
@@ -0,0 +1,16 @@
+#{.experimental: "unicodeOperators".}
+
+proc `⊙`(x, y: int): int = x * y
+proc `⊙=`(x: var int, y: int) = x *= y
+
+proc `⊞++`(x, y: int): int = x + y
+
+const a = 9
+
+var x = 45
+x ⊙= a⊞++4⊙3
+
+var y = 45
+y *= 9 + 4 * 3
+
+assert x == y
diff --git a/tests/lookups/issue_23032/deep_scope.nim b/tests/lookups/issue_23032/deep_scope.nim
new file mode 100644
index 000000000..3e25809a7
--- /dev/null
+++ b/tests/lookups/issue_23032/deep_scope.nim
@@ -0,0 +1,2 @@
+type A*[T] = object
+proc foo*(a: A[int]): bool = false
diff --git a/tests/lookups/issue_23172/m23172.nim b/tests/lookups/issue_23172/m23172.nim
new file mode 100644
index 000000000..36af48761
--- /dev/null
+++ b/tests/lookups/issue_23172/m23172.nim
@@ -0,0 +1,6 @@
+type
+  Foo* = object
+  Bar* = object
+
+func `$`*(x: Foo | Bar): string =
+  "X"
diff --git a/tests/ambsym/mambsym1.nim b/tests/lookups/mambsym1.nim
index c4902b1b4..c4902b1b4 100644
--- a/tests/ambsym/mambsym1.nim
+++ b/tests/lookups/mambsym1.nim
diff --git a/tests/ambsym/mambsym2.nim b/tests/lookups/mambsym2.nim
index 21d980073..21d980073 100644
--- a/tests/ambsym/mambsym2.nim
+++ b/tests/lookups/mambsym2.nim
diff --git a/tests/lookups/mambsym3.nim b/tests/lookups/mambsym3.nim
new file mode 100644
index 000000000..946a5ff29
--- /dev/null
+++ b/tests/lookups/mambsym3.nim
@@ -0,0 +1,4 @@
+# Module A
+var x*: string
+proc foo*(a: string) =
+  echo "A: ", a
diff --git a/tests/lookups/mambsym4.nim b/tests/lookups/mambsym4.nim
new file mode 100644
index 000000000..ed66cc16d
--- /dev/null
+++ b/tests/lookups/mambsym4.nim
@@ -0,0 +1,4 @@
+# Module B
+var x*: int
+proc foo*(b: int) =
+  echo "B: ", b
diff --git a/tests/ambsym/mambsys1.nim b/tests/lookups/mambsys1.nim
index 22e54cb94..22e54cb94 100644
--- a/tests/ambsym/mambsys1.nim
+++ b/tests/lookups/mambsys1.nim
diff --git a/tests/ambsym/mambsys2.nim b/tests/lookups/mambsys2.nim
index ef63e4f7e..ef63e4f7e 100644
--- a/tests/ambsym/mambsys2.nim
+++ b/tests/lookups/mambsys2.nim
diff --git a/tests/lookups/mambtype1.nim b/tests/lookups/mambtype1.nim
new file mode 100644
index 000000000..47046142e
--- /dev/null
+++ b/tests/lookups/mambtype1.nim
@@ -0,0 +1 @@
+type K* = object
diff --git a/tests/lookups/mambtype2.nim b/tests/lookups/mambtype2.nim
new file mode 100644
index 000000000..cf622466b
--- /dev/null
+++ b/tests/lookups/mambtype2.nim
@@ -0,0 +1,4 @@
+import ./mambtype1
+export mambtype1
+template K*(kind: static int): auto = typedesc[mambtype1.K]
+template B*(kind: static int): auto = typedesc[mambtype1.K]
diff --git a/tests/bind/mbind3.nim b/tests/lookups/mbind3.nim
index 1a7d3b63b..1a7d3b63b 100644
--- a/tests/bind/mbind3.nim
+++ b/tests/lookups/mbind3.nim
diff --git a/tests/lookups/mdisambsym1.nim b/tests/lookups/mdisambsym1.nim
new file mode 100644
index 000000000..b8beca035
--- /dev/null
+++ b/tests/lookups/mdisambsym1.nim
@@ -0,0 +1,2 @@
+proc count*(s: string): int = 
+  s.len
diff --git a/tests/lookups/mdisambsym2.nim b/tests/lookups/mdisambsym2.nim
new file mode 100644
index 000000000..1e056311d
--- /dev/null
+++ b/tests/lookups/mdisambsym2.nim
@@ -0,0 +1 @@
+var count*: int = 10
diff --git a/tests/lookups/mdisambsym3.nim b/tests/lookups/mdisambsym3.nim
new file mode 100644
index 000000000..95bd19702
--- /dev/null
+++ b/tests/lookups/mdisambsym3.nim
@@ -0,0 +1 @@
+const count* = 3.142
diff --git a/tests/lookups/mmacroamb.nim b/tests/lookups/mmacroamb.nim
new file mode 100644
index 000000000..107e51055
--- /dev/null
+++ b/tests/lookups/mmacroamb.nim
@@ -0,0 +1,10 @@
+# issue #12732
+
+import std/macros
+const getPrivate3_tmp* = 0
+const foobar1* = 0 # comment this or make private and it'll compile fine
+macro foobar4*(): untyped =
+  newLit "abc"
+template currentPkgDir2*: string = foobar4()
+macro currentPkgDir2*(dir: string): untyped =
+  newLit "abc2"
diff --git a/tests/lookups/t23032.nim b/tests/lookups/t23032.nim
new file mode 100644
index 000000000..144abcb05
--- /dev/null
+++ b/tests/lookups/t23032.nim
@@ -0,0 +1,13 @@
+discard """
+action: "run"
+outputsub: "proc (a: A[system.float]): bool{.noSideEffect, gcsafe.}"
+"""
+
+import issue_23032/deep_scope
+
+proc foo(a: A[float]):bool = true
+
+let p: proc = foo
+echo p.typeof
+doAssert p(A[float]()) == true
+doAssert compiles(doAssert p(A[int]()) == true) == false
diff --git a/tests/lookups/t23172.nim b/tests/lookups/t23172.nim
new file mode 100644
index 000000000..9edf9905a
--- /dev/null
+++ b/tests/lookups/t23172.nim
@@ -0,0 +1,9 @@
+import issue_23172/m23172
+
+type FooX = distinct Foo
+
+func `$`*(x: FooX): string =
+  $m23172.Foo(x)
+
+var a: FooX
+doAssert $a == "X"
diff --git a/tests/lookups/t23749.nim b/tests/lookups/t23749.nim
new file mode 100644
index 000000000..650f04ea4
--- /dev/null
+++ b/tests/lookups/t23749.nim
@@ -0,0 +1,37 @@
+discard """
+  action: compile
+"""
+
+{.pragma: callback, gcsafe, raises: [].}
+
+type
+  DataProc* = proc(val: openArray[byte]) {.callback.}
+  GetProc = proc (db: RootRef, key: openArray[byte], onData: DataProc): bool {.nimcall, callback.}
+  KvStoreRef* = ref object
+    obj: RootRef
+    getProc: GetProc
+
+template get(dbParam: KvStoreRef, key: openArray[byte], onData: untyped): bool =
+  let db = dbParam
+  db.getProc(db.obj, key, onData)
+
+func decode(input: openArray[byte], maxSize = 128): seq[byte] =
+  @[]
+
+proc getSnappySSZ(db: KvStoreRef, key: openArray[byte]): string =
+  var status = "not found"
+  proc decode(data: openArray[byte]) =
+    status =
+      if true: "found"
+      else: "corrupted"
+  discard db.get(key, decode)
+  status
+
+
+var ksr: KvStoreRef
+var k = [byte(1), 2, 3, 4, 5]
+
+proc foo(): string =
+  getSnappySSZ(ksr, toOpenArray(k, 1, 3))
+
+echo foo()
diff --git a/tests/lookups/tambiguousemit.nim b/tests/lookups/tambiguousemit.nim
new file mode 100644
index 000000000..4f4bacce4
--- /dev/null
+++ b/tests/lookups/tambiguousemit.nim
@@ -0,0 +1,12 @@
+discard """
+  cmd: "nim check $options $file" # use check to assure error is pre-codegen
+  matrix: "; --backend:js" # backend shouldn't matter but at least check js
+"""
+
+proc foo(x: int) = discard
+proc foo(x: float) = discard
+
+{.emit: ["// ", foo].} #[tt.Error
+                ^ ambiguous identifier: 'foo' -- use one of the following:
+  tambiguousemit.foo: proc (x: int){.noSideEffect, gcsafe.}
+  tambiguousemit.foo: proc (x: float){.noSideEffect, gcsafe.}]#
diff --git a/tests/lookups/tambprocvar.nim b/tests/lookups/tambprocvar.nim
new file mode 100644
index 000000000..f5c3fde4f
--- /dev/null
+++ b/tests/lookups/tambprocvar.nim
@@ -0,0 +1,19 @@
+discard """
+  action: reject
+  cmd: "nim check $file"
+  nimout: '''
+tambprocvar.nim(15, 11) Error: ambiguous identifier: 'foo' -- use one of the following:
+  tambprocvar.foo: proc (x: int){.noSideEffect, gcsafe.}
+  tambprocvar.foo: proc (x: float){.noSideEffect, gcsafe.}
+'''
+"""
+
+block:
+  proc foo(x: int) = discard
+  proc foo(x: float) = discard
+
+  let x = foo
+
+block:
+  let x = `+` #[tt.Error
+          ^ ambiguous identifier: '+' -- use one of the following:]#
diff --git a/tests/ambsym/tambsym.nim b/tests/lookups/tambsym.nim
index d9115e16d..bd0f41717 100644
--- a/tests/ambsym/tambsym.nim
+++ b/tests/lookups/tambsym.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "ambiguous identifier"
   file: "tambsym.nim"
   line: 11
-  errormsg: "ambiguous identifier"
 """
 # Test ambiguous symbols
 
@@ -11,5 +11,3 @@ var
   v: TExport #ERROR_MSG ambiguous identifier
 
 v = y
-
-
diff --git a/tests/ambsym/tambsym2.nim b/tests/lookups/tambsym2.nim
index 8e288e73a..747f1a086 100644
--- a/tests/ambsym/tambsym2.nim
+++ b/tests/lookups/tambsym2.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tambsym2.nim"
   output: "7"
 """
 # Test overloading of procs with locals
@@ -20,5 +19,3 @@ m.len = 7
 m.data = "1234"
 
 x(m, 5) #OUT 7
-
-
diff --git a/tests/lookups/tambsym3.nim b/tests/lookups/tambsym3.nim
new file mode 100644
index 000000000..6bbebca10
--- /dev/null
+++ b/tests/lookups/tambsym3.nim
@@ -0,0 +1,13 @@
+discard """
+  errormsg: "ambiguous identifier: 'mDec' -- use one of the following:"
+  file: "tambsym3.nim"
+  line: 11
+"""
+# Test ambiguous symbols
+
+import mambsym1, times
+
+var
+  v = mDec #ERROR_MSG ambiguous identifier
+
+writeLine(stdout, ord(v))
diff --git a/tests/lookups/tambsymmanual.nim b/tests/lookups/tambsymmanual.nim
new file mode 100644
index 000000000..3ad91b8cc
--- /dev/null
+++ b/tests/lookups/tambsymmanual.nim
@@ -0,0 +1,25 @@
+discard """
+  output: '''
+A: abc
+B: 123
+A: def
+4
+'''
+"""
+# Module C
+import mambsym3, mambsym4
+
+foo("abc") # A: abc
+foo(123) # B: 123
+let inferred: proc (x: string) = foo
+foo("def") # A: def
+
+doAssert not compiles(write(stdout, x)) # error: x is ambiguous
+write(stdout, mambsym3.x) # no error: qualifier used
+
+proc bar(a: int): int = a + 1
+doAssert bar(x) == x + 1 # no error: only A.x of type int matches
+
+var x = 4
+write(stdout, x) # not ambiguous: uses the module C's x
+echo() # for test output
diff --git a/tests/ambsym/tambsys.nim b/tests/lookups/tambsys.nim
index 67522d7c9..aa740c38f 100644
--- a/tests/ambsym/tambsys.nim
+++ b/tests/lookups/tambsys.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tambsys.nim"
   output: ""
 """
 # Test ambiguous symbols
@@ -9,5 +8,3 @@ import mambsys1, mambsys2
 var
   v: mambsys1.TExport
 mambsys2.foo(3) #OUT
-
-
diff --git a/tests/lookups/tambtype.nim b/tests/lookups/tambtype.nim
new file mode 100644
index 000000000..a292db83a
--- /dev/null
+++ b/tests/lookups/tambtype.nim
@@ -0,0 +1,20 @@
+import ./mambtype2
+
+block: # issue #23893
+  discard default(K(0))       # works
+  discard default(mambtype2.B(0))     # works
+  discard default(mambtype2.K(0))     # doesn't work
+
+block: # issue #23898, in template
+  template r() =
+    discard default(B(0))     # compiles
+    discard default(mambtype2.B(0))   # compiles
+    discard default(K(0))     # does not compile
+  r()
+
+block: # in generics
+  proc foo[T]() =
+    discard default(B(0))     # compiles
+    discard default(mambtype2.B(0))   # compiles
+    discard default(K(0))     # does not compile
+  foo[int]()
diff --git a/tests/lookups/tbind.nim b/tests/lookups/tbind.nim
new file mode 100644
index 000000000..f3fb952e3
--- /dev/null
+++ b/tests/lookups/tbind.nim
@@ -0,0 +1,78 @@
+discard """
+output: '''
+3
+1
+1
+1
+5
+'''
+"""
+
+
+block tbind:
+# Test the new ``bind`` keyword for templates
+
+  proc p1(x: int8, y: int): int = return x + y
+
+  template tempBind(x, y): untyped =
+    bind p1
+    p1(x, y)
+
+  proc p1(x: int, y: int8): int = return x - y
+
+  # This is tricky: the call to ``p1(1'i8, 2'i8)`` should not fail in line 6,
+  # because it is not ambiguous there. But it is ambiguous after line 8.
+
+  echo tempBind(1'i8, 2'i8) #OUT 3
+
+
+import mbind3
+echo genId() #OUT 1
+
+
+import strtabs
+block tbinoverload:
+  template t() =
+    block:
+      bind newStringTable
+      discard {"Content-Type": "text/html"}.newStringTable()
+
+      discard {:}.newStringTable
+  #discard {"Content-Type": "text/html"}.newStringTable()
+  t()
+
+
+block tmixin:
+  type
+    TFoo1 = object of RootObj
+      v: int
+    TFoo2 = object of TFoo1
+      v2: int
+
+  proc test(f: TFoo1) =
+    echo "1"
+
+  proc Foo[T](f: T) =
+    mixin test
+    test(f)
+
+  var
+    a: TFoo1
+    b: TFoo2
+
+
+  proc test(f: TFoo2) =
+    echo "2"
+
+  Foo(a)
+  Foo(b)
+
+# issue #11811
+proc p(a : int) =
+  echo a
+
+proc printVar*[T:int|float|string](a : T) =
+  bind p
+  p(a)
+
+printVar(5)
diff --git a/tests/lookups/tbind_for_generics.nim b/tests/lookups/tbind_for_generics.nim
new file mode 100644
index 000000000..db5fbebbc
--- /dev/null
+++ b/tests/lookups/tbind_for_generics.nim
@@ -0,0 +1,17 @@
+discard """
+  errormsg: "type mismatch: got <Foo, Foo>"
+  line: 8
+"""
+proc g[T](x: T) =
+  bind `+`
+  # because we bind `+` here, we must not find the `+` for 'Foo' below:
+  echo x + x
+
+type
+  Foo = object
+    a: int
+
+proc `+`(a, b: Foo): Foo = Foo(a: a.a+b.a)
+
+g(3)
+g(Foo(a: 8))
diff --git a/tests/lookups/tdisambsym.nim b/tests/lookups/tdisambsym.nim
new file mode 100644
index 000000000..678528ad6
--- /dev/null
+++ b/tests/lookups/tdisambsym.nim
@@ -0,0 +1,8 @@
+# issue #15247
+
+import mdisambsym1, mdisambsym2, mdisambsym3
+
+proc twice(n: int): int =
+  n*2
+
+doAssert twice(count) == 20
diff --git a/tests/lookups/tenumlocalsym.nim b/tests/lookups/tenumlocalsym.nim
new file mode 100644
index 000000000..575227c07
--- /dev/null
+++ b/tests/lookups/tenumlocalsym.nim
@@ -0,0 +1,22 @@
+block:
+  type Enum = enum a, b
+
+  block:
+    let a = b
+    let x: Enum = a
+    doAssert x == b
+  
+block:
+  type
+    Enum = enum
+      a = 2
+      b = 10
+
+  iterator items2(): Enum =
+    for a in [a, b]:
+      yield a
+
+  var s = newSeq[Enum]()
+  for i in items2():
+    s.add i
+  doAssert s == @[a, b]
diff --git a/tests/lookups/test.nim b/tests/lookups/test.nim
index a17d235a4..dfacaf5b5 100644
--- a/tests/lookups/test.nim
+++ b/tests/lookups/test.nim
@@ -1,3 +1,10 @@
+discard """
+output: '''
+
+[Suite] memoization
+'''
+"""
+
 # This file needs to be called 'test' nim to provoke a clash
 # with the unittest.test name. Issue #
 
@@ -5,7 +12,7 @@ import unittest, macros
 
 # bug #4555
 
-macro memo(n: untyped): typed =
+macro memo(n: untyped) =
   result = n
 
 proc fastFib(n: int): int {.memo.} = 40
@@ -14,4 +21,3 @@ proc fib(n: int): int = 40
 suite "memoization":
   test "recursive function memoization":
     check fastFib(40) == fib(40)
-
diff --git a/tests/gensym/tgensym.nim b/tests/lookups/tgensym.nim
index e33a2783f..e33a2783f 100644
--- a/tests/gensym/tgensym.nim
+++ b/tests/lookups/tgensym.nim
diff --git a/tests/gensym/tgensymgeneric.nim b/tests/lookups/tgensymgeneric.nim
index 9963ba808..6e1df4842 100644
--- a/tests/gensym/tgensymgeneric.nim
+++ b/tests/lookups/tgensymgeneric.nim
@@ -21,7 +21,7 @@ template genImpl() =
     gensymed.x = "abc"
   else:
     gensymed.x = 123
-  shallowCopy(result, gensymed)
+  result = move gensymed
 
 proc gen[T](x: T): T =
   genImpl()
@@ -40,15 +40,15 @@ doAssert y.x == "abc"
 import macros
 
 static:
-  let sym1   = genSym()
-  let sym2   = genSym()
-  let sym3   = sym1
+  let sym1 = genSym()
+  let sym2 = genSym()
+  let sym3 = sym1
   let nimsym = sym1.symbol
-  doAssert sym1        == sym1
-  doAssert sym2        != sym3
+  doAssert sym1 == sym1
+  doAssert sym2 != sym3
   doAssert sym2.symbol != sym3.symbol
-  doAssert sym3        == sym1
+  doAssert sym3 == sym1
   doAssert sym1.symbol == sym1.symbol
-  doAssert nimsym      == nimsym
+  doAssert nimsym == nimsym
 
 echo "true"
diff --git a/tests/lookups/tinvalidbindtypedesc.nim b/tests/lookups/tinvalidbindtypedesc.nim
new file mode 100644
index 000000000..1c71c8daf
--- /dev/null
+++ b/tests/lookups/tinvalidbindtypedesc.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "type mismatch: got <typedesc[float], string>"
+  line: 10
+"""
+
+proc foo(T: typedesc; some: T) =
+  echo($some)
+
+foo int, 4
+foo float, "bad"
diff --git a/tests/lookups/tmacroamb.nim b/tests/lookups/tmacroamb.nim
new file mode 100644
index 000000000..854017e72
--- /dev/null
+++ b/tests/lookups/tmacroamb.nim
@@ -0,0 +1,5 @@
+# issue #12732
+
+import mmacroamb
+const s0 = currentPkgDir2 #[tt.Error
+           ^ ambiguous identifier: 'currentPkgDir2' -- use one of the following:]#
diff --git a/tests/lookups/tmoduleclash1.nim b/tests/lookups/tmoduleclash1.nim
new file mode 100644
index 000000000..7058f691e
--- /dev/null
+++ b/tests/lookups/tmoduleclash1.nim
@@ -0,0 +1,13 @@
+# issue #23596
+
+import std/heapqueue
+type Algo = enum heapqueue, quick
+when false:
+  let x = heapqueue
+let y: Algo = heapqueue
+proc bar*(algo=quick) =
+  var x: HeapQueue[int]
+  case algo
+  of heapqueue: echo 1 # `Algo.heapqueue` works on devel
+  of quick: echo 2
+  echo x.len
diff --git a/tests/lookups/tmoduleclash2.nim b/tests/lookups/tmoduleclash2.nim
new file mode 100644
index 000000000..958da2299
--- /dev/null
+++ b/tests/lookups/tmoduleclash2.nim
@@ -0,0 +1,6 @@
+import std/heapqueue
+proc heapqueue(x: int) = discard
+let x: proc (x: int) = heapqueue
+let y: proc = heapqueue
+when false:
+  let z = heapqueue
diff --git a/tests/lookups/tnicerrorforsymchoice.nim b/tests/lookups/tnicerrorforsymchoice.nim
new file mode 100644
index 000000000..6001b6b09
--- /dev/null
+++ b/tests/lookups/tnicerrorforsymchoice.nim
@@ -0,0 +1,23 @@
+discard """
+  errormsg: "type mismatch: got <proc (s: TScgi: ScgiState or AsyncScgiState) | proc (client: AsyncSocket, headers: StringTableRef, input: string){.noSideEffect, gcsafe.}>"
+  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 asyncnet, strtabs
+proc handleSCGIRequest[TScgi: ScgiState | AsyncScgiState](s: TScgi) =
+  discard
+proc handleSCGIRequest(client: AsyncSocket, headers: StringTableRef,
+                       input: string) =
+  discard
+
+proc test(handle: proc (client: AsyncSocket, headers: StringTableRef,
+                        input: string), b: int) =
+  discard
+
+test(handleSCGIRequest)
diff --git a/tests/lookups/told_bind_expr.nim b/tests/lookups/told_bind_expr.nim
new file mode 100644
index 000000000..56389eaf6
--- /dev/null
+++ b/tests/lookups/told_bind_expr.nim
@@ -0,0 +1,15 @@
+discard """
+  errormsg: "ambiguous call"
+  file: "told_bind_expr.nim"
+  line: 13
+"""
+
+# Pre-0.9 deprecated bind expression syntax
+
+proc p1(x: int8, y: int): int = return x + y
+proc p1(x: int, y: int8): int = return x - y
+
+template tempBind(x, y): untyped =
+  (bind p1(x, y))  #ERROR_MSG ambiguous call
+
+echo tempBind(1'i8, 2'i8)
diff --git a/tests/lookups/tprefer_proc.nim b/tests/lookups/tprefer_proc.nim
deleted file mode 100644
index 57ee8e539..000000000
--- a/tests/lookups/tprefer_proc.nim
+++ /dev/null
@@ -1,4 +0,0 @@
-
-# bug #4353
-import random
-echo random[int](low(int) .. high(int))
diff --git a/tests/lookups/tredef.nim b/tests/lookups/tredef.nim
index 8f1b38bd1..7c1df238e 100644
--- a/tests/lookups/tredef.nim
+++ b/tests/lookups/tredef.nim
@@ -4,7 +4,7 @@ foo(1, "test")
 proc bar(a: int, b: string) = discard
 bar(1, "test")
 
-template foo(a: int, b: string) = bar(a, b)
+template foo(a: int, b: string) {.redefine.} = bar(a, b)
 foo(1, "test")
 
 block:
diff --git a/tests/lookups/tundeclaredmixin.nim b/tests/lookups/tundeclaredmixin.nim
new file mode 100644
index 000000000..74d16cdde
--- /dev/null
+++ b/tests/lookups/tundeclaredmixin.nim
@@ -0,0 +1,17 @@
+discard """
+  nimout: '''
+  mixin nothing, add
+'''
+"""
+
+# issue #22012
+
+import macros
+
+expandMacros:
+  proc foo[T](): int =
+    # `nothing` is undeclared, `add` is declared
+    mixin nothing, add
+    123
+
+  doAssert foo[int]() == 123
diff --git a/tests/m14634.nim b/tests/m14634.nim
new file mode 100644
index 000000000..f19f02f0c
--- /dev/null
+++ b/tests/m14634.nim
@@ -0,0 +1,48 @@
+#[
+Tool to investigate underlying reasons for https://github.com/nim-lang/Nim/pull/14634
+nim r --threads:on -d:threadsafe tests/m14634.nim
+]#
+
+when not defined(windows):
+  import std/selectors
+
+  type TestData = object
+    s1, s2, s3: int
+
+  proc timerNotificationTestImpl(data: var TestData) =
+    var selector = newSelector[int]()
+    let t0 = 5
+    var timer = selector.registerTimer(t0, false, 0)
+    let t = 2000
+      # values too close to `t0` cause the test to be flaky in CI on OSX+freebsd
+      # When running locally, t0=100, t=98 will succeed some of the time which indicates
+      # there is some lag involved. Note that the higher `t-t0` is, the less times
+      # the test fails.
+    var rc1 = selector.select(t)
+    var rc2 = selector.select(t)
+    doAssert len(rc1) <= 1 and len(rc2) <= 1
+    data.s1 += ord(len(rc1) == 1)
+    data.s2 += ord(len(rc2) == 1)
+    selector.unregister(timer)
+    discard selector.select(0)
+    selector.registerTimer(t0, true, 0)
+      # same comment as above
+    var rc4 = selector.select(t)
+    let t2 = 100
+      # this can't be too large as it'll actually wait that long:
+      # timer_notification_test.n * t2
+    var rc5 = selector.select(t2)
+    doAssert len(rc4) + len(rc5) <= 1
+    data.s3 += ord(len(rc4) + len(rc5) == 1)
+    doAssert(selector.isEmpty())
+    selector.close()
+
+  proc timerNotificationTest() =
+    var data: TestData
+    let n = 10
+    for i in 0..<n:
+      timerNotificationTestImpl(data)
+    doAssert data.s1 == n and data.s2 == n and data.s3 == n, $data
+
+  when isMainModule:
+    timerNotificationTest()
diff --git a/tests/macros/m18235.nim b/tests/macros/m18235.nim
new file mode 100644
index 000000000..6bb4547e0
--- /dev/null
+++ b/tests/macros/m18235.nim
@@ -0,0 +1,42 @@
+import macros
+
+# Necessary code to update the AST on a symbol across module boundaries when
+# processed by a type macro. Used by a test of a corresponding name of this
+# file.
+
+macro eexport(n: typed): untyped =
+  result = copyNimTree(n)
+  # turn exported nnkSym -> nnkPostfix(*, nnkIdent), forcing re-sem
+  result[0] = nnkPostfix.newTree(ident"*").add:
+    n.name.strVal.ident
+
+macro unexport(n: typed): untyped =
+  result = copyNimTree(n)
+  # turn nnkSym -> nnkIdent, forcing re-sem and dropping any exported-ness
+  # that might be present
+  result[0] = n.name.strVal.ident
+
+proc foo*() {.unexport.} = discard
+proc bar() {.eexport.} = discard
+
+proc foooof*() {.unexport, eexport, unexport.} = discard
+proc barrab() {.eexport, unexport, eexport.} = discard
+
+macro eexportMulti(n: typed): untyped =
+  # use the call version of `eexport` macro for one or more decls
+  result = copyNimTree(n)
+  for i in 0..<result.len:
+    result[i] = newCall(ident"eexport", result[i])
+
+macro unexportMulti(n: typed): untyped =
+  # use the call version of `unexport` macro for one or more decls
+  result = copyNimTree(n)
+  for i in 0..<result.len:
+    result[i] = newCall(ident"unexport", result[i])
+
+unexportMulti:
+  proc oof*() = discard
+
+eexportMulti:
+  proc rab() = discard
+  proc baz*() = discard
\ No newline at end of file
diff --git a/tests/macros/macro_bug.nim b/tests/macros/macro_bug.nim
index 0d0fa76ac..c723a4ab6 100644
--- a/tests/macros/macro_bug.nim
+++ b/tests/macros/macro_bug.nim
@@ -1,6 +1,7 @@
 import macros
 
-macro macro_bug*(s: stmt): stmt {.immediate.} =
+macro macro_bug*(s: untyped) =
+  echo s.treeRepr
   s.expectKind({nnkProcDef, nnkMethodDef})
 
   var params = s.params
@@ -9,9 +10,9 @@ macro macro_bug*(s: stmt): stmt {.immediate.} =
   result = newNimNode(nnkProcDef).add(
     s.name, s[1], genericParams, params, pragma(s), newEmptyNode())
 
+  # don't really do anything
   var body = body(s)
-
-  # Fails here.
-  var call = newCall("macro_bug", s.params[1][0])
-  body.insert(0, call)
   result.add(body)
+
+  echo "result:"
+  echo result.repr
diff --git a/tests/macros/mparsefile.nim b/tests/macros/mparsefile.nim
new file mode 100644
index 000000000..8ac99d568
--- /dev/null
+++ b/tests/macros/mparsefile.nim
@@ -0,0 +1,4 @@
+let a = 1
+let b = 2
+let c =
+let d = 4
diff --git a/tests/macros/t14227.nim b/tests/macros/t14227.nim
new file mode 100644
index 000000000..4206e2c06
--- /dev/null
+++ b/tests/macros/t14227.nim
@@ -0,0 +1,23 @@
+discard """
+  action: "compile"
+"""
+import sugar
+
+
+block:
+  let y = @[@[1, 2], @[2, 4, 6]]
+  let x = collect(newSeq):
+    for i in y:
+      if i.len > 2:
+        for j in i:
+          j
+  echo(x)
+
+block:
+  let y = @[@[1, 2], @[2, 4, 6]]
+  let x = collect(newSeq):
+    for i in y:
+      for j in i:
+        if i.len > 2:
+          j
+  echo(x)
diff --git a/tests/macros/t14329.nim b/tests/macros/t14329.nim
new file mode 100644
index 000000000..b5606424a
--- /dev/null
+++ b/tests/macros/t14329.nim
@@ -0,0 +1,4 @@
+import macros
+
+macro myMacro(n) =
+  let x = if true: newLit"test" else: error "error", n
diff --git a/tests/macros/t14511.nim b/tests/macros/t14511.nim
new file mode 100644
index 000000000..f3b1e2894
--- /dev/null
+++ b/tests/macros/t14511.nim
@@ -0,0 +1,54 @@
+discard """
+  output: "true\n(y: XInt, a: 5)\n(y: XString, b: \"abc\")"
+"""
+
+import macros
+
+block TEST_1:
+  # https://github.com/nim-lang/Nim/issues/14511
+
+  template myPragma() {.pragma.}
+
+  type
+    XType = enum
+      XInt,
+      XString,
+      XUnused
+    X = object
+      case y {.myPragma.}: XType
+        of XInt, XUnused:
+          a: int
+        else: # <-- Else case caused the "Error: index 1 not in 0 .. 0" error
+          b: string
+
+  var x: X = X(y: XInt, a: 5)
+  echo x.y.hasCustomPragma(myPragma)
+  echo x
+  echo X(y: XString, b: "abc")
+
+
+block TEST_2:
+  template myDevice(val: string) {.pragma.}
+  template myKey(val: string) {.pragma.}
+  template myMouse(val: string) {.pragma.}
+
+  type
+    Device {.pure.} = enum Keyboard, Mouse
+    Key = enum Key1, Key2
+    Mouse = enum Mouse1, Mouse2
+
+  type
+    Obj = object of RootObj
+      case device {.myDevice: "MyDevicePragmaStr".}: Device
+      of Device.Keyboard:
+        key {.myKey: "MyKeyPragmaStr".}: Key
+      else: # <-- Else case caused the "Error: index 1 not in 0 .. 0" error
+        mouse {.myMouse: "MyMousePragmaStr".}: Mouse
+
+  var obj: Obj
+  assert obj.device.hasCustomPragma(myDevice) == true
+  assert obj.key.hasCustomPragma(myKey) == true
+  assert obj.mouse.hasCustomPragma(myMouse) == true
+  assert obj.device.getCustomPragmaVal(myDevice) == "MyDevicePragmaStr"
+  assert obj.key.getCustomPragmaVal(myKey) == "MyKeyPragmaStr"
+  assert obj.mouse.getCustomPragmaVal(myMouse) == "MyMousePragmaStr"
\ No newline at end of file
diff --git a/tests/macros/t14847.nim b/tests/macros/t14847.nim
new file mode 100644
index 000000000..0e6d0dd2d
--- /dev/null
+++ b/tests/macros/t14847.nim
@@ -0,0 +1,20 @@
+discard """
+  output: "98"
+"""
+import macros
+
+#bug #14847
+proc hello*(b: string) =
+  echo b
+
+macro dispatch(pro: typed, params: untyped): untyped =
+  var impl = pro.getImpl
+  let id = ident(pro.strVal & "_coverage")
+  impl[0] = id
+  let call = newCall(id, params)
+
+  result = newStmtList()
+  result.add(impl)
+  result.add(call)
+
+dispatch(hello, "98")
diff --git a/tests/macros/t15691.nim b/tests/macros/t15691.nim
new file mode 100644
index 000000000..c1e8a8648
--- /dev/null
+++ b/tests/macros/t15691.nim
@@ -0,0 +1,22 @@
+discard """
+  action: compile
+"""
+
+import std/macros
+
+macro simplifiedExpandMacros(body: typed): untyped =
+  result = body
+
+simplifiedExpandMacros:
+  proc testProc() = discard
+
+simplifiedExpandMacros:
+  template testTemplate(): untyped = discard
+
+# Error: illformed AST: macro testMacro(): untyped =
+simplifiedExpandMacros:
+  macro testMacro(): untyped = discard
+
+# Error: illformed AST: converter testConverter(x: int): float =
+simplifiedExpandMacros:
+  converter testConverter(x: int): float = discard
diff --git a/tests/macros/t15751.nim b/tests/macros/t15751.nim
new file mode 100644
index 000000000..fcabb2f9e
--- /dev/null
+++ b/tests/macros/t15751.nim
@@ -0,0 +1,11 @@
+discard """
+  cmd: "nim c --hints:off $file"
+  nimout: "out"
+"""
+
+# bug #15751
+macro print(n: untyped): untyped =
+  echo n.repr
+
+print:
+  out
diff --git a/tests/macros/t16758.nim b/tests/macros/t16758.nim
new file mode 100644
index 000000000..66b6d42c5
--- /dev/null
+++ b/tests/macros/t16758.nim
@@ -0,0 +1,38 @@
+discard """

+errormsg: "'blk.p(a)' has nil child at index 1"

+action: reject

+"""

+import macros

+

+type BlockLiteral[T] = object

+  p: T

+

+proc p[T](a:int) = echo 1

+proc p[T](a:string) = echo "a"

+

+iterator arguments(formalParams: NimNode): NimNode =

+  var iParam = 0

+  for i in 1 ..< formalParams.len:

+    let pp = formalParams[i]

+    for j in 0 .. pp.len - 3:

+      yield pp[j]

+      inc iParam

+

+macro implementInvoke(T: typedesc): untyped =

+  let t = getTypeImpl(T)[1]

+

+  let call = newCall(newDotExpr(ident"blk", ident"p"))

+  let params = copyNimTree(t[0])

+  result = newProc(ident"invoke", body = call)

+  # result[2] = newTree(nnkGenericParams,T)

+  for n in arguments(params):

+    call.add(n)

+  

+  params.insert(1, newIdentDefs(ident"blk", newTree(nnkBracketExpr, bindSym"BlockLiteral", T)))

+  result.params = params

+

+proc getInvoke(T: typedesc) =

+  implementInvoke(T)

+

+

+getInvoke(proc(a: int))

diff --git a/tests/macros/t17836.nim b/tests/macros/t17836.nim
new file mode 100644
index 000000000..2453637f5
--- /dev/null
+++ b/tests/macros/t17836.nim
@@ -0,0 +1,15 @@
+import macros
+
+# Ensure that `isNil` works in the typed macro context when pass procs.
+
+type
+  O = object
+    fn: proc(i: int): int
+
+var o: O
+
+macro typedBug(expr: typed) =
+  doAssert expr[1] != nil
+  doAssert not expr[1].isNil
+
+typedBug(o.fn)
\ No newline at end of file
diff --git a/tests/macros/t18203.nim b/tests/macros/t18203.nim
new file mode 100644
index 000000000..aae0a2690
--- /dev/null
+++ b/tests/macros/t18203.nim
@@ -0,0 +1,15 @@
+discard """
+  matrix: "--hint:SuccessX:off --hint:Link:off --hint:Conf:off --hint:CC:off --hint:XDeclaredButNotUsed:on"
+  nimout: '''
+'''
+nimoutFull: true
+action: compile
+"""
+
+# bug #18203
+import std/macros
+
+macro foo(x: typed) = newProc ident"bar"
+proc bar() {.foo.} = raise
+bar()
+
diff --git a/tests/macros/t18235.nim b/tests/macros/t18235.nim
new file mode 100644
index 000000000..ba5c48a24
--- /dev/null
+++ b/tests/macros/t18235.nim
@@ -0,0 +1,18 @@
+import m18235
+
+# this must error out because it was never actually exported
+doAssert(not declared(foo))
+doAssert not compiles(foo())
+
+doAssert(not declared(foooof))
+doAssert not compiles(foooof())
+
+doAssert(not declared(oof))
+doAssert not compiles(oof())
+
+# this should have been exported just fine
+
+bar()
+barrab()
+rab()
+baz()
\ No newline at end of file
diff --git a/tests/macros/t19766_20114.nim b/tests/macros/t19766_20114.nim
new file mode 100644
index 000000000..ac336f150
--- /dev/null
+++ b/tests/macros/t19766_20114.nim
@@ -0,0 +1,16 @@
+discard """
+  action: compile
+  nimout: '''
+const
+  foo {.strdefine.} = "abc"
+let hey {.tddd.} = 5
+'''
+"""
+
+import macros
+
+template tddd {.pragma.}
+
+expandMacros:
+  const foo {.strdefine.} = "abc"
+  let hey {.tddd.} = 5
diff --git a/tests/macros/t20067.nim b/tests/macros/t20067.nim
new file mode 100644
index 000000000..0ee3a4712
--- /dev/null
+++ b/tests/macros/t20067.nim
@@ -0,0 +1,28 @@
+discard """
+  output: '''
+b.defaultVal = foo
+$c.defaultVal = bar
+'''
+"""
+
+import macros
+
+# #18976
+
+macro getString(identifier): string =
+  result = newLit($identifier)
+doAssert getString(abc) == "abc"
+doAssert getString(`a b c`) == "abc"
+
+# #20067
+
+template defaultVal*(value : typed) {.pragma.}
+
+type A = ref object
+  b {.defaultVal: "foo".}: string
+  `$c` {.defaultVal: "bar".}: string
+
+let a = A(b: "a", `$c`: "b")
+
+echo "b.defaultVal = " & a.b.getCustomPragmaVal(defaultVal)
+echo "$c.defaultVal = " & a.`$c`.getCustomPragmaVal(defaultVal)
diff --git a/tests/macros/t20435.nim b/tests/macros/t20435.nim
new file mode 100644
index 000000000..824282198
--- /dev/null
+++ b/tests/macros/t20435.nim
@@ -0,0 +1,30 @@
+
+#[
+  A better test requires matching, so the use of @ working can be showcased
+  For example:
+
+  proc regularCase[T]() = 
+    case [(1, 3), (3, 4)]:
+    of [(1, @a), (_, @b)]:
+      echo a, b
+    else: discard
+]#
+
+{.experimental: "caseStmtMacros".}
+
+import macros
+
+type Foo = object
+
+macro `case`(obj: Foo) = quote do: discard
+
+proc notGeneric() =
+  case Foo()
+  of a b c d: discard
+
+proc generic[T]() =
+  case Foo()
+  of a b c d: discard
+
+notGeneric()
+generic[int]()
diff --git a/tests/macros/t21593.nim b/tests/macros/t21593.nim
new file mode 100644
index 000000000..b0b7ebe75
--- /dev/null
+++ b/tests/macros/t21593.nim
@@ -0,0 +1,13 @@
+discard """
+nimout: '''
+StmtList
+  UIntLit 18446744073709551615
+  IntLit -1'''
+"""
+
+import macros
+
+dumpTree:
+  0xFFFFFFFF_FFFFFFFF'u
+  0xFFFFFFFF_FFFFFFFF
+
diff --git a/tests/macros/t23032_1.nim b/tests/macros/t23032_1.nim
new file mode 100644
index 000000000..4e1707414
--- /dev/null
+++ b/tests/macros/t23032_1.nim
@@ -0,0 +1,19 @@
+import std/macros
+
+type A[T, H] = object
+
+proc `%*`(a: A): bool = true
+proc `%*`[T](a: A[int, T]): bool = false
+
+macro collapse(s: untyped) =
+  result = newStmtList()
+  result.add quote do:
+    doAssert(`s`(A[float, int]()) == true)
+
+macro startHere(n: untyped): untyped =
+  result = newStmtList()
+  let s = n[0]
+  result.add quote do:
+    `s`.collapse()
+
+startHere(`a` %* `b`)
diff --git a/tests/macros/t23032_2.nim b/tests/macros/t23032_2.nim
new file mode 100644
index 000000000..8dde29e10
--- /dev/null
+++ b/tests/macros/t23032_2.nim
@@ -0,0 +1,20 @@
+discard """
+  action: "reject"
+  errormsg: "ambiguous identifier: '%*'"
+"""
+import std/macros
+
+type A[T, H] = object
+
+proc `%*`[T](a: A) = discard
+proc `%*`[T](a: A[int, T]) = discard
+
+macro collapse(s: typed) = discard
+
+macro startHere(n: untyped): untyped =
+  result = newStmtList()
+  let s = n[0]
+  result.add quote do:
+    collapse(`s`.typeof())
+
+startHere(`a` %* `b`)
diff --git a/tests/macros/t23547.nim b/tests/macros/t23547.nim
new file mode 100644
index 000000000..9a2bff9ff
--- /dev/null
+++ b/tests/macros/t23547.nim
@@ -0,0 +1,23 @@
+# https://github.com/nim-lang/Nim/issues/23547
+
+type
+  A[T] = object
+    x: T
+
+proc mulCheckSparse[F](dummy: var A[F], xmulchecksparse: static A[F]) =
+  static:
+    echo "mulCheckSparse: ", typeof(dummy), ", ", typeof(xmulchecksparse) # when generic params not specified: A[system.int], A
+
+template sumImpl(xsumimpl: typed) =
+  static:
+    echo "sumImpl: ", typeof(xsumimpl) # A
+  var a = A[int](x: 55)
+  mulCheckSparse(a, xsumimpl) # fails here
+
+proc sum[T](xsum: static T) =
+  static:
+    echo "sum: ", typeof(xsum) # A[system.int]
+  sumImpl(xsum)
+
+const constA = A[int](x : 100)
+sum[A[int]](constA)
diff --git a/tests/macros/t23784.nim b/tests/macros/t23784.nim
new file mode 100644
index 000000000..31b4544c6
--- /dev/null
+++ b/tests/macros/t23784.nim
@@ -0,0 +1,157 @@
+discard """
+  joinable: false
+"""
+
+
+# debug ICE: genCheckedRecordField
+# apparently after https://github.com/nim-lang/Nim/pull/23477
+
+# bug #23784
+
+import std/bitops, std/macros
+
+# --------------------------------------------------------------
+
+type Algebra = enum
+  BN254_Snarks
+
+type SecretWord* = distinct uint64
+const WordBitWidth* = sizeof(SecretWord) * 8
+
+func wordsRequired*(bits: int): int {.inline.} =
+  const divShiftor = fastLog2(WordBitWidth)
+  result = (bits + WordBitWidth - 1) shr divShiftor
+
+type
+  BigInt*[bits: static int] = object
+    limbs*: array[bits.wordsRequired, SecretWord]  # <--- crash points to here
+
+# --------------------------------------------------------------
+
+const CurveBitWidth = [
+  BN254_Snarks: 254
+]
+
+const BN254_Snarks_Modulus = BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x2, SecretWord 0x3, SecretWord 0x4])
+const BN254_Snarks_Order = BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x1, SecretWord 0x2, SecretWord 0x2])
+
+func montyOne*(M: BigInt[254]): BigInt[254] =
+  ## Returns "1 (mod M)" in the Montgomery domain.
+  ## This is equivalent to R (mod M) in the natural domain
+  BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x1, SecretWord 0x1, SecretWord 0x1])
+
+
+{.experimental: "dynamicBindSym".}
+
+type
+  DerivedConstantMode* = enum
+    kModulus
+    kOrder
+
+macro genDerivedConstants*(mode: static DerivedConstantMode): untyped =
+  ## Generate constants derived from the main constants
+  ##
+  ## For example
+  ## - the Montgomery magic constant "R^2 mod N" in ROM
+  ##   For each curve under the private symbol "MyCurve_R2modP"
+  ## - the Montgomery magic constant -1/P mod 2^Wordbitwidth
+  ##   For each curve under the private symbol "MyCurve_NegInvModWord
+  ## - ...
+
+  # Now typedesc are NimNode and there is no way to translate
+  # NimNode -> typedesc easily so we can't
+  # "for curve in low(Curve) .. high(Curve):"
+  # As an ugly workaround, we count
+  # The item at position 0 is a pragma
+  result = newStmtList()
+
+  template used(name: string): NimNode =
+    nnkPragmaExpr.newTree(
+      ident(name),
+      nnkPragma.newTree(ident"used")
+    )
+
+  let ff = if mode == kModulus: "_Fp" else: "_Fr"
+
+  for curveSym in low(Algebra) .. high(Algebra):
+    let curve = $curveSym
+    let M = if mode == kModulus: bindSym(curve & "_Modulus")
+            else: bindSym(curve & "_Order")
+
+    # const MyCurve_montyOne = montyOne(MyCurve_Modulus)
+    result.add newConstStmt(
+      used(curve & ff & "_MontyOne"), newCall(
+        bindSym"montyOne",
+        M
+      )
+    )
+
+# --------------------------------------------------------------
+
+{.experimental: "dynamicBindSym".}
+
+genDerivedConstants(kModulus)
+genDerivedConstants(kOrder)
+
+proc bindConstant(ff: NimNode, property: string): NimNode =
+  # Need to workaround https://github.com/nim-lang/Nim/issues/14021
+  # which prevents checking if a type FF[Name] = Fp[Name] or Fr[Name]
+  # was instantiated with Fp or Fr.
+  # getTypeInst only returns FF and sameType doesn't work.
+  # so quote do + when checks.
+  let T = getTypeInst(ff)
+  T.expectKind(nnkBracketExpr)
+  doAssert T[0].eqIdent("typedesc")
+
+  let curve =
+    if T[1].kind == nnkBracketExpr: # typedesc[Fp[BLS12_381]] as used internally
+      # doAssert T[1][0].eqIdent"Fp" or T[1][0].eqIdent"Fr", "Found ident: '" & $T[1][0] & "' instead of 'Fp' or 'Fr'"
+      T[1][1].expectKind(nnkIntLit) # static enum are ints in the VM
+      $Algebra(T[1][1].intVal)
+    else: # typedesc[bls12381_fp] alias as used for C exports
+      let T1 = getTypeInst(T[1].getImpl()[2])
+      if T1.kind != nnkBracketExpr or
+         T1[1].kind != nnkIntLit:
+        echo T.repr()
+        echo T1.repr()
+        echo getTypeInst(T1).treerepr()
+        error "getTypeInst didn't return the full instantiation." &
+          " Dealing with types in macros is hard, complain at https://github.com/nim-lang/RFCs/issues/44"
+      $Algebra(T1[1].intVal)
+
+  let curve_fp = bindSym(curve & "_Fp_" & property)
+  let curve_fr = bindSym(curve & "_Fr_" & property)
+  result = quote do:
+    when `ff` is Fp:
+      `curve_fp`
+    elif `ff` is Fr:
+      `curve_fr`
+    else:
+      {.error: "Unreachable, received type: " & $`ff`.}
+
+# --------------------------------------------------------------
+
+template matchingBigInt*(Name: static Algebra): untyped =
+  ## BigInt type necessary to store the prime field Fp
+  # Workaround: https://github.com/nim-lang/Nim/issues/16774
+  # as we cannot do array accesses in type section.
+  # Due to generic sandwiches, it must be exported.
+  BigInt[CurveBitWidth[Name]]
+
+type
+  Fp*[Name: static Algebra] = object
+    mres*: matchingBigInt(Name)
+
+macro getMontyOne*(ff: type Fp): untyped =
+  ## Get one in Montgomery representation (i.e. R mod P)
+  result = bindConstant(ff, "MontyOne")
+
+func getOne*(T: type Fp): T {.noInit, inline.} =
+  result = cast[ptr T](unsafeAddr getMontyOne(T))[]
+
+# --------------------------------------------------------------
+proc foo(T: Fp) =
+  discard T
+
+let a = Fp[BN254_Snarks].getOne()
+foo(a) # oops this was a leftover that broke the bisect.
diff --git a/tests/macros/t7454.nim b/tests/macros/t7454.nim
new file mode 100644
index 000000000..e527de0c3
--- /dev/null
+++ b/tests/macros/t7454.nim
@@ -0,0 +1,8 @@
+discard """
+errormsg: "expression has no type:"
+line: 8
+"""
+
+macro p(t: typedesc): typedesc =
+  discard
+var a: p(int)
diff --git a/tests/macros/t7875.nim b/tests/macros/t7875.nim
new file mode 100644
index 000000000..7b6e47b86
--- /dev/null
+++ b/tests/macros/t7875.nim
@@ -0,0 +1,22 @@
+discard """
+  nimout: "var mysym`gensym0: MyType[float32]"
+  joinable: false
+"""
+
+import macros
+
+type
+  MyType[T] = object
+
+# this is totally fine
+var mysym: MyType[float32]
+
+macro foobar(): untyped =
+  let floatSym = bindSym"float32"
+
+  result = quote do:
+    var mysym: MyType[`floatSym`]
+
+  echo result.repr
+
+foobar()
diff --git a/tests/macros/t8997.nim b/tests/macros/t8997.nim
new file mode 100644
index 000000000..b06223717
--- /dev/null
+++ b/tests/macros/t8997.nim
@@ -0,0 +1,26 @@
+discard """
+  errormsg: "illformed AST: "
+  line: 24
+"""
+
+import macros
+
+type
+  Node* = ref object
+    children: seq[Node]
+
+proc newNode*(): Node =
+  Node(children: newSeq[Node]())
+
+macro build*(body: untyped): untyped =
+
+  template appendElement(tmp, childrenBlock) {.dirty.} =
+    bind newNode
+    let tmp = newNode()
+    tmp.children = childrenBlock  # this line seems to be the problem
+
+  let tmp = genSym(nskLet, "tmp")
+  let childrenBlock = newEmptyNode()
+  result = getAst(appendElement(tmp, childrenBlock))
+
+build(body)
diff --git a/tests/macros/tastrepr.nim b/tests/macros/tastrepr.nim
new file mode 100644
index 000000000..96a37c7a2
--- /dev/null
+++ b/tests/macros/tastrepr.nim
@@ -0,0 +1,58 @@
+discard """
+output: '''
+
+var data = @[(1, "one"), (2, "two")]
+for (i, d) in pairs(data):
+  discard
+for i, d in pairs(data):
+  discard
+for i, (x, y) in pairs(data):
+  discard
+var
+  a = 1
+  b = 2
+type
+  A* = object
+
+var data = @[(1, "one"), (2, "two")]
+for (i, d) in pairs(data):
+  discard
+for i, d in pairs(data):
+  discard
+for i, (x, y) in pairs(data):
+  discard
+var (a, b) = (1, 2)
+type
+  A* = object
+
+var t04 = 1.0'f128
+t04 = 2.0'f128
+'''
+"""
+
+import macros
+
+macro echoTypedRepr(arg: typed) =
+  result = newCall(ident"echo", newLit(arg.repr))
+
+macro echoUntypedRepr(arg: untyped) =
+  result = newCall(ident"echo", newLit(arg.repr))
+
+template echoTypedAndUntypedRepr(arg: untyped) =
+  echoTypedRepr(arg)
+  echoUntypedRepr(arg)
+
+echoTypedAndUntypedRepr:
+  var data = @[(1,"one"), (2,"two")]
+  for (i, d) in pairs(data):
+    discard
+  for i, d in pairs(data):
+    discard
+  for i, (x,y) in pairs(data):
+    discard
+  var (a,b) = (1,2)
+  type A* = object # issue #22933
+
+echoUntypedRepr:
+  var t04 = 1'f128
+  t04 = 2'f128
diff --git a/tests/macros/tbindsym.nim b/tests/macros/tbindsym.nim
index 6289d3eb2..a493d6a88 100644
--- a/tests/macros/tbindsym.nim
+++ b/tests/macros/tbindsym.nim
@@ -1,4 +1,9 @@
 discard """
+  nimout: '''initApple
+deinitApple
+Coral
+enum
+  redCoral, blackCoral'''
   output: '''TFoo
 TBar'''
 """
@@ -23,3 +28,40 @@ macro test: untyped =
         bindSym("TBar"))
 
 test()
+
+# issue 7827, bindSym power up
+{.experimental: "dynamicBindSym".}
+type
+  Apple = ref object
+    name: string
+    color: int
+    weight: int
+
+proc initApple(name: string): Apple =
+  discard
+
+proc deinitApple(x: Apple) =
+  discard
+
+macro wrapObject(obj: typed, n: varargs[untyped]): untyped =
+  let m = n[0]
+  for x in m:
+    var z = bindSym x
+    echo z.repr
+
+wrapObject(Apple):
+  initApple
+  deinitApple
+
+type
+  Coral = enum
+    redCoral
+    blackCoral
+
+macro mixer(): untyped =
+  let m = "Co" & "ral"
+  let x = bindSym(m)
+  echo x.repr
+  echo getType(x).repr
+
+mixer()
diff --git a/tests/macros/tbugs.nim b/tests/macros/tbugs.nim
deleted file mode 100644
index 990edf1e2..000000000
--- a/tests/macros/tbugs.nim
+++ /dev/null
@@ -1,106 +0,0 @@
-discard """
-msg: '''a
-s
-d
-f
-TTaa
-TTaa
-TTaa
-TTaa
-true
-true
-nil
-42
-false
-true'''
-
-output: '''test
-2'''
-"""
-
-type
-  Foo = object
-    s: char
-
-iterator test2(f: string): Foo =
-  for i in f:
-    yield Foo(s: i)
-
-macro test(): untyped =
-  for i in test2("asdf"):
-    echo i.s
-
-test()
-
-
-# bug 1297
-
-import macros
-
-type TType = tuple[s: string]
-
-macro echotest(): untyped =
-  var t: TType
-  t.s = ""
-  t.s.add("test")
-  result = newCall(newIdentNode("echo"), newStrLitNode(t.s))
-
-echotest()
-
-# bug #1103
-
-type
-    Td = tuple
-        a:string
-        b:int
-
-proc get_data(d: Td) : string {.compileTime.} =
-    result = d.a # Works if a literal string is used here.
-    # Bugs if line A or B is active. Works with C
-    result &= "aa"          # A
-    #result.add("aa")       # B
-    #result = result & "aa" # C
-
-macro m(s:static[Td]) : untyped =
-    echo get_data(s)
-    echo get_data(s)
-    result = newEmptyNode()
-
-const s=("TT", 3)
-m(s)
-m(s)
-
-# bug #933
-
-proc nilcheck(): NimNode {.compileTime.} =
-  echo(result == nil) # true
-  echo(result.isNil) # true
-  echo(repr(result)) # nil
-
-macro testnilcheck(): untyped =
-  result = newNimNode(nnkStmtList)
-  discard nilcheck()
-
-testnilcheck()
-
-# bug #1323
-
-proc calc(): array[1, int] =
-  result[0].inc()
-  result[0].inc()
-
-const c = calc()
-echo c[0]
-
-
-# bug #3046
-
-macro sampleMacroInt(i: int): untyped =
-  echo i.intVal
-
-macro sampleMacroBool(b: bool): untyped =
-  echo b.boolVal
-
-sampleMacroInt(42)
-sampleMacroBool(false)
-sampleMacroBool(system.true)
diff --git a/tests/macros/tcasestmtmacro.nim b/tests/macros/tcasestmtmacro.nim
new file mode 100644
index 000000000..32019a92a
--- /dev/null
+++ b/tests/macros/tcasestmtmacro.nim
@@ -0,0 +1,33 @@
+discard """
+  output: '''
+yes
+'''
+"""
+
+import macros
+
+macro `case`(n: tuple): untyped =
+  result = newTree(nnkIfStmt)
+  let selector = n[0]
+  for i in 1 ..< n.len:
+    let it = n[i]
+    case it.kind
+    of nnkElse, nnkElifBranch, nnkElifExpr, nnkElseExpr:
+      result.add it
+    of nnkOfBranch:
+      for j in 0..it.len-2:
+        let cond = newCall("==", selector, it[j])
+        result.add newTree(nnkElifBranch, cond, it[^1])
+    else:
+      error "custom 'case' for tuple cannot handle this node", it
+
+var correct = false
+
+case ("foo", 78)
+of ("foo", 78):
+  correct = true
+  echo "yes"
+of ("bar", 88): echo "no"
+else: discard
+
+doAssert correct
diff --git a/tests/macros/tclosuremacro.nim b/tests/macros/tclosuremacro.nim
index 9f2137dec..44c2411a5 100644
--- a/tests/macros/tclosuremacro.nim
+++ b/tests/macros/tclosuremacro.nim
@@ -1,11 +1,6 @@
 discard """
-  output: '''10
-10
-10
-3
-3
+  output: '''
 noReturn
-6
 calling mystuff
 yes
 calling mystuff
@@ -13,7 +8,7 @@ yes
 '''
 """
 
-import future, macros
+import sugar, macros
 
 proc twoParams(x: (int, int) -> int): int =
   result = x(5, 5)
@@ -30,23 +25,23 @@ proc noReturn(x: () -> void) =
 proc doWithOneAndTwo(f: (int, int) -> int): int =
   f(1,2)
 
-echo twoParams(proc (a, b: auto): auto = a + b)
-echo twoParams((x, y) => x + y)
-
-echo oneParam(x => x+5)
-
-echo noParams(() => 3)
-
-echo doWithOneAndTwo((x, y) => x + y)
+doAssert twoParams(proc (a, b: auto): auto = a + b) == 10
+doAssert twoParams((x, y) => x + y) == 10
+doAssert oneParam(x => x+5) == 10
+doAssert noParams(() => 3) == 3
+doAssert doWithOneAndTwo((x, y) => x + y) == 3
 
 noReturn((() -> void) => echo("noReturn"))
 
 proc pass2(f: (int, int) -> int): (int) -> int =
   ((x: int) -> int) => f(2, x)
 
-echo pass2((x, y) => x + y)(4)
+doAssert pass2((x, y) => x + y)(4) == 6
 
+const fun = (x, y: int) {.noSideEffect.} => x + y
 
+doAssert typeof(fun) is (proc (x, y: int): int {.nimcall.})
+doAssert fun(3, 4) == 7
 
 proc register(name: string; x: proc()) =
   echo "calling ", name
@@ -72,3 +67,14 @@ macro m(x: untyped): untyped =
 m:
   proc mystuff() =
     echo "yes"
+
+const typedParamAndPragma = (x, y: int) -> int => x + y
+doAssert typedParamAndPragma(1, 2) == 3
+
+type
+  Bot = object
+    call: proc (): string {.noSideEffect.}
+
+var myBot = Bot()
+myBot.call = () {.noSideEffect.} => "I'm a bot."
+doAssert myBot.call() == "I'm a bot."
diff --git a/tests/macros/tcollect.nim b/tests/macros/tcollect.nim
new file mode 100644
index 000000000..ae28ab61b
--- /dev/null
+++ b/tests/macros/tcollect.nim
@@ -0,0 +1,63 @@
+discard """
+  output: '''@[2, 3, 4, 2, 3, 4, 2, 3, 4, 2, 3, 4, 2, 3, 4]
+@[0, 1, 2, 3]'''
+"""
+
+const data = [1,2,3,4,5,6]
+
+import macros
+
+macro collect(body): untyped =
+  # analyse the body, find the deepest expression 'it' and replace it via
+  # 'result.add it'
+  let res = genSym(nskVar, "collectResult")
+
+  when false:
+    proc detectForLoopVar(n: NimNode): NimNode =
+      if n.kind == nnkForStmt:
+        result = n[0]
+      else:
+        for x in n:
+          result = detectForLoopVar(x)
+          if result != nil: return result
+        return nil
+
+  proc t(n, res: NimNode): NimNode =
+    case n.kind
+    of nnkStmtList, nnkStmtListExpr, nnkBlockStmt, nnkBlockExpr,
+       nnkWhileStmt,
+       nnkForStmt, nnkIfExpr, nnkIfStmt, nnkTryStmt, nnkCaseStmt,
+       nnkElifBranch, nnkElse, nnkElifExpr:
+      result = copyNimTree(n)
+      if n.len >= 1:
+        result[^1] = t(n[^1], res)
+    else:
+      if true: #n == it:
+        template adder(res, it) =
+          res.add it
+        result = getAst adder(res, n)
+      else:
+        result = n
+
+  when false:
+    let it = detectForLoopVar(body)
+    if it == nil: error("no for loop in body", body)
+
+  let v = newTree(nnkVarSection,
+     newTree(nnkIdentDefs, res, newTree(nnkBracketExpr, bindSym"seq",
+     newCall(bindSym"type", body)), newEmptyNode()))
+
+  result = newTree(nnkStmtListExpr, v, t(body, res), res)
+  #echo repr result
+
+let stuff = collect:
+  var i = -1
+  while i < 4:
+    inc i
+    for it in data:
+      if it < 5 and it > 1:
+        it
+
+echo stuff
+
+echo collect(for i in 0..3: i)
diff --git a/tests/macros/tcomplexecho.nim b/tests/macros/tcomplexecho.nim
index 0b70a3ef7..d58fa561c 100644
--- a/tests/macros/tcomplexecho.nim
+++ b/tests/macros/tcomplexecho.nim
@@ -32,7 +32,7 @@ proc foo(): seq[NimNode] {.compiletime.} =
   result.add test()
   result.add parseExpr("echo(5+56)")
 
-macro bar(): typed =
+macro bar() =
   result = newNimNode(nnkStmtList)
   let x = foo()
   for xx in x:
diff --git a/tests/macros/tcprag.nim b/tests/macros/tcprag.nim
new file mode 100644
index 000000000..71618883f
--- /dev/null
+++ b/tests/macros/tcprag.nim
@@ -0,0 +1,32 @@
+discard """
+  output: '''true
+true
+true
+'''
+"""
+
+# issue #7615
+import macros
+
+template table(name: string) {.pragma.}
+
+type
+   User {.table("tuser").} = object
+      id: int
+      name: string
+      age: int
+
+echo User.hasCustomPragma(table)
+
+
+## crash: Error: internal error: (filename: "sempass2.nim", line: 560, column: 19)
+macro m1(T: typedesc): untyped =
+  getAST hasCustomPragma(T, table)
+echo m1(User) # Oops crash
+
+
+## This works
+macro m2(T: typedesc): untyped =
+  result = quote do:
+    `T`.hasCustomPragma(table)
+echo m2(User)
diff --git a/tests/macros/tdebugstmt.nim b/tests/macros/tdebugstmt.nim
deleted file mode 100644
index 69e610075..000000000
--- a/tests/macros/tdebugstmt.nim
+++ /dev/null
@@ -1,29 +0,0 @@
-discard """
-  output: '''a[0]: 42
-a[1]: 45
-x: some string'''
-"""
-
-import macros
-
-macro debug(n: varargs[untyped]): untyped =
-  # `n` is a Nim AST that contains the whole macro invocation
-  # this macro returns a list of statements:
-  result = newNimNode(nnkStmtList, n)
-  # iterate over any argument that is passed to this macro:
-  for i in 0..n.len-1:
-    # add a call to the statement list that writes the expression;
-    # `toStrLit` converts an AST to its string representation:
-    add(result, newCall("write", newIdentNode("stdout"), toStrLit(n[i])))
-    # add a call to the statement list that writes ": "
-    add(result, newCall("write", newIdentNode("stdout"), newStrLitNode(": ")))
-    # add a call to the statement list that writes the expressions value:
-    add(result, newCall("writeLine", newIdentNode("stdout"), n[i]))
-
-var
-  a: array[0..10, int]
-  x = "some string"
-a[0] = 42
-a[1] = 45
-
-debug(a[0], a[1], x)
diff --git a/tests/macros/tdump.nim b/tests/macros/tdump.nim
deleted file mode 100644
index e4c14dc6b..000000000
--- a/tests/macros/tdump.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-discard """
-  output: '''x = 10
-x + y = 30
-'''
-"""
-
-import future
-
-let
-  x = 10
-  y = 20
-dump x
-dump(x + y)
\ No newline at end of file
diff --git a/tests/macros/tdumpast.nim b/tests/macros/tdumpast.nim
index 3a26ffd2f..484b3c2f3 100644
--- a/tests/macros/tdumpast.nim
+++ b/tests/macros/tdumpast.nim
@@ -2,32 +2,160 @@
 
 import macros
 
-template plus(a, b: untyped): untyped {.dirty} =
-  a + b
+block:
+  template plus(a, b: untyped): untyped {.dirty} =
+    a + b
 
-macro call(e: untyped): untyped =
-  result = newCall("foo", newStrLitNode("bar"))
+  macro call(e: untyped): untyped =
+    result = newCall("foo", newStrLitNode("bar"))
 
-macro dumpAST(n: untyped): untyped =
-  # dump AST as a side-effect and return the inner node
-  let n = callsite()
-  echo n.lispRepr
-  echo n.treeRepr
+  macro dumpAST(n: untyped): string =
+    var msg = ""
+    msg.add "lispRepr:\n" & n.lispRepr & "\n"
+    msg.add "treeRepr:\n" & n.treeRepr & "\n"
 
-  var plusAst = getAst(plus(1, 2))
-  echo plusAst.lispRepr
+    var plusAst = getAst(plus(1, 2))
+    msg.add "lispRepr:\n" & n.lispRepr & "\n"
 
-  var callAst = getAst(call(4))
-  echo callAst.lispRepr
+    var callAst = getAst(call(4))
+    msg.add "callAst.lispRepr:\n" & callAst.lispRepr & "\n"
 
-  var e = parseExpr("foo(bar + baz)")
-  echo e.lispRepr
+    var e = parseExpr("foo(bar + baz)")
+    msg.add "e.lispRepr:\n" & e.lispRepr & "\n"
+    result = msg.newLit
 
-  result = n[1]
+  let a = dumpAST:
+    proc add(x, y: int): int =
+      return x + y
+    const foo = 3
 
-dumpAST:
-  proc add(x, y: int): int =
-    return x + y
+  doAssert a == """
+lispRepr:
+(StmtList (ProcDef (Ident "add") (Empty) (Empty) (FormalParams (Ident "int") (IdentDefs (Ident "x") (Ident "y") (Ident "int") (Empty))) (Empty) (Empty) (StmtList (ReturnStmt (Infix (Ident "+") (Ident "x") (Ident "y"))))) (ConstSection (ConstDef (Ident "foo") (Empty) (IntLit 3))))
+treeRepr:
+StmtList
+  ProcDef
+    Ident "add"
+    Empty
+    Empty
+    FormalParams
+      Ident "int"
+      IdentDefs
+        Ident "x"
+        Ident "y"
+        Ident "int"
+        Empty
+    Empty
+    Empty
+    StmtList
+      ReturnStmt
+        Infix
+          Ident "+"
+          Ident "x"
+          Ident "y"
+  ConstSection
+    ConstDef
+      Ident "foo"
+      Empty
+      IntLit 3
+lispRepr:
+(StmtList (ProcDef (Ident "add") (Empty) (Empty) (FormalParams (Ident "int") (IdentDefs (Ident "x") (Ident "y") (Ident "int") (Empty))) (Empty) (Empty) (StmtList (ReturnStmt (Infix (Ident "+") (Ident "x") (Ident "y"))))) (ConstSection (ConstDef (Ident "foo") (Empty) (IntLit 3))))
+callAst.lispRepr:
+(Call (Ident "foo") (StrLit "bar"))
+e.lispRepr:
+(Call (Ident "foo") (Infix (Ident "+") (Ident "bar") (Ident "baz")))
+"""
 
-  proc sub(x, y: int): int = return x - y
+macro fun() =
+  let n = quote do:
+    1+1 == 2
+  doAssert n.repr == "1 + 1 == 2", n.repr
+fun()
 
+macro fun2(): untyped =
+  let n = quote do:
+    1 + 2 * 3 == 1 + 6
+  doAssert n.repr == "1 + 2 * 3 == 1 + 6", n.repr
+fun2()
+
+macro fun3(): untyped =
+  let n = quote do:
+    int | float | array | seq | object | ptr | pointer | float32
+  doAssert n.repr == "int | float | array | seq | object | ptr | pointer | float32", n.repr
+fun3()
+
+macro fun4() =
+  let n = quote do:
+    (a: 1)
+  doAssert n.repr == "(a: 1)", n.repr
+fun4()
+
+# nkTupleConstr vs nkPar tests:
+block: # lispRepr
+  macro lispRepr2(a: untyped): string = newLit a.lispRepr
+
+  doAssert lispRepr2(()) == """(TupleConstr)"""
+  doAssert lispRepr2((a: 1)) == """(TupleConstr (ExprColonExpr (Ident "a") (IntLit 1)))"""
+  doAssert lispRepr2((a: 1, b: 2)) == """(TupleConstr (ExprColonExpr (Ident "a") (IntLit 1)) (ExprColonExpr (Ident "b") (IntLit 2)))"""
+  doAssert lispRepr2((1,)) == """(TupleConstr (IntLit 1))"""
+  doAssert lispRepr2((1, 2)) == """(TupleConstr (IntLit 1) (IntLit 2))"""
+  doAssert lispRepr2((1, 2, 3.0)) == """(TupleConstr (IntLit 1) (IntLit 2) (FloatLit 3.0))"""
+  doAssert lispRepr2((1)) == """(Par (IntLit 1))"""
+  doAssert lispRepr2((1+2)) == """(Par (Infix (Ident "+") (IntLit 1) (IntLit 2)))"""
+
+block: # repr
+  macro repr2(a: untyped): string = newLit a.repr
+
+  doAssert repr2(()) == "()"
+  doAssert repr2((a: 1)) == "(a: 1)"
+  doAssert repr2((a: 1, b: 2)) == "(a: 1, b: 2)"
+  doAssert repr2((1,)) == "(1,)"
+  doAssert repr2((1, 2)) == "(1, 2)"
+  doAssert repr2((1, 2, 3.0)) == "(1, 2, 3.0)"
+  doAssert repr2((1)) == "(1)"
+  doAssert repr2((1+2)) == "(1 + 2)"
+
+block: # treeRepr
+  macro treeRepr2(a: untyped): string = newLit a.treeRepr
+  macro treeRepr3(a: typed): string = newLit a.treeRepr
+
+  doAssert treeRepr2(1+1 == 2) == """
+Infix
+  Ident "=="
+  Infix
+    Ident "+"
+    IntLit 1
+    IntLit 1
+  IntLit 2"""
+
+  proc baz() = discard
+  proc baz(a: int) = discard
+  proc baz(a: float) = discard
+
+  doAssert treeRepr3(baz()) == """
+Call
+  Sym "baz""""
+
+  let a = treeRepr3(block:
+    proc bar(a: auto) = baz())
+  doAssert a == """
+BlockStmt
+  Empty
+  ProcDef
+    Sym "bar"
+    Empty
+    GenericParams
+      Sym "a:type"
+    FormalParams
+      Empty
+      IdentDefs
+        Sym "a"
+        Sym "auto"
+        Empty
+    Empty
+    Bracket
+      Empty
+      Empty
+    StmtList
+      Call
+        OpenSymChoice 3 "baz""""
diff --git a/tests/macros/tdumpastgen.nim b/tests/macros/tdumpastgen.nim
index 0a1836886..0e0581f6a 100644
--- a/tests/macros/tdumpastgen.nim
+++ b/tests/macros/tdumpastgen.nim
@@ -1,5 +1,5 @@
 discard """
-msg: '''nnkStmtList.newTree(
+nimout: '''nnkStmtList.newTree(
   nnkVarSection.newTree(
     nnkIdentDefs.newTree(
       newIdentNode("x"),
diff --git a/tests/macros/tdumptree.nim b/tests/macros/tdumptree.nim
index 58b011b45..f540306c4 100644
--- a/tests/macros/tdumptree.nim
+++ b/tests/macros/tdumptree.nim
@@ -1,13 +1,14 @@
 discard """
-msg: '''StmtList
+nimout: '''
+StmtList
   VarSection
     IdentDefs
-      Ident !"x"
+      Ident "x"
       Empty
       Call
         DotExpr
-          Ident !"foo"
-          Ident !"create"
+          Ident "foo"
+          Ident "create"
         IntLit 56'''
 """
 
diff --git a/tests/macros/tescape_var_into_quotedo_as_const.nim b/tests/macros/tescape_var_into_quotedo_as_const.nim
new file mode 100644
index 000000000..1ed93f012
--- /dev/null
+++ b/tests/macros/tescape_var_into_quotedo_as_const.nim
@@ -0,0 +1,36 @@
+discard """
+  output: '''ok'''
+"""
+# bug #9864
+import macros, tables
+
+proc bar(shOpt: Table[string, int]) = discard
+
+macro dispatchGen(): untyped =
+  var shOpt = initTable[string, int]()
+  shOpt["foo"] = 10
+  result = quote do:
+     bar(`shOpt`)
+
+dispatchGen()
+
+type
+  Foo = object
+    data: seq[int]
+
+proc barB(a: Foo) = discard
+
+proc shOptB(): auto =
+  var shOpt: Foo
+  shOpt.data.setLen 1 # fails
+  shOpt
+
+macro dispatchGenB(): untyped =
+  var shOpt = shOptB() # fails
+
+  result = quote do:
+     barB(`shOpt`)
+
+dispatchGenB()
+
+echo "ok"
diff --git a/tests/macros/texpectIdent1.nim b/tests/macros/texpectIdent1.nim
new file mode 100644
index 000000000..26e52afb5
--- /dev/null
+++ b/tests/macros/texpectIdent1.nim
@@ -0,0 +1,18 @@
+discard """
+errormsg: "Expected identifier to be `foo` here"
+line: 18
+"""
+
+import macros
+
+macro testUntyped(arg: untyped): void =
+  arg.expectKind nnkStmtList
+  arg.expectLen 2
+  arg[0].expectKind nnkCall
+  arg[0][0].expectIdent "foo"  # must pass
+  arg[1].expectKind nnkCall
+  arg[1][0].expectIdent "foo"  # must fail
+
+testUntyped:
+  foo(123)
+  bar(321)
diff --git a/tests/macros/texpectIdent2.nim b/tests/macros/texpectIdent2.nim
new file mode 100644
index 000000000..887a6ddc3
--- /dev/null
+++ b/tests/macros/texpectIdent2.nim
@@ -0,0 +1,24 @@
+discard """
+errormsg: "Expected identifier to be `foo` here"
+line: 24
+"""
+
+import macros
+
+macro testTyped(arg: typed): void =
+  arg.expectKind nnkStmtList
+  arg.expectLen 2
+  arg[0].expectKind nnkCall
+  arg[0][0].expectIdent "foo"  # must pass
+  arg[1].expectKind nnkCall
+  arg[1][0].expectIdent "foo"  # must fail
+
+proc foo(arg: int) =
+  discard
+
+proc bar(arg: int) =
+  discard
+
+testTyped:
+  foo(123)
+  bar(321)
diff --git a/tests/macros/texprcolonexpr.nim b/tests/macros/texprcolonexpr.nim
deleted file mode 100644
index 59c799771..000000000
--- a/tests/macros/texprcolonexpr.nim
+++ /dev/null
@@ -1,19 +0,0 @@
-discard """
-  msg: '''
-Infix
-  Ident !"=>"
-  Call
-    Ident !"name"
-    Ident !"a"
-    ExprColonExpr
-      Ident !"b"
-      Ident !"cint"
-  NilLit nil
-'''
-"""
-import macros
-
-macro def(x): untyped =
-  echo treeRepr(x)
-
-def name(a, b:cint) => nil
diff --git a/tests/macros/tfail_parse.nim b/tests/macros/tfail_parse.nim
new file mode 100644
index 000000000..1925f2b69
--- /dev/null
+++ b/tests/macros/tfail_parse.nim
@@ -0,0 +1,15 @@
+discard """
+action: "reject"
+cmd: "nim check $file"
+errormsg: "expected expression, but got multiple statements [ValueError]"
+file: "macros.nim"
+"""
+
+import macros
+static:
+  discard parseStmt("'")
+  discard parseExpr("'")
+  discard parseExpr("""
+proc foo()
+proc foo() = discard
+""")
diff --git a/tests/macros/tgenericparams.nim b/tests/macros/tgenericparams.nim
deleted file mode 100644
index d656f045a..000000000
--- a/tests/macros/tgenericparams.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-discard """
-output: '''proc foo[T, N: static[int]]()
-proc foo[T; N: static[int]]()'''
-"""
-import macros
-
-macro test():string =
-    let expr0 = "proc foo[T, N: static[int]]()"
-    let expr1 = "proc foo[T; N: static[int]]()"
-
-    $toStrLit(parseExpr(expr0)) & "\n" & $toStrLit(parseExpr(expr1))
-    
-echo test()
diff --git a/tests/macros/tgensym.nim b/tests/macros/tgensym.nim
deleted file mode 100644
index 1237b8bf7..000000000
--- a/tests/macros/tgensym.nim
+++ /dev/null
@@ -1,65 +0,0 @@
-import nativesockets, asyncdispatch, macros
-var p = newDispatcher()
-var sock = newAsyncNativeSocket()
-
-proc convertReturns(node, retFutureSym: NimNode): NimNode {.compileTime.} =
-  case node.kind
-  of nnkReturnStmt:
-    result = newCall(newIdentNode("complete"), retFutureSym, node[0])
-  else:
-    result = node
-    for i in 0 .. <node.len:
-      result[i] = convertReturns(node[i], retFutureSym)
-
-macro async2(prc: untyped): untyped =
-  expectKind(prc, nnkProcDef)
-
-  var outerProcBody = newNimNode(nnkStmtList)
-
-  # -> var retFuture = newFuture[T]()
-  var retFutureSym = newIdentNode("retFuture") #genSym(nskVar, "retFuture")
-  outerProcBody.add(
-    newVarStmt(retFutureSym,
-      newCall(
-        newNimNode(nnkBracketExpr).add(
-          newIdentNode("newFuture"),
-          prc[3][0][1])))) # Get type from return type of this proc.
-
-  # -> iterator nameIter(): FutureBase {.closure.} = <proc_body>
-  # Changing this line to: newIdentNode($prc[0].ident & "Iter") # will make it work.
-  var iteratorNameSym = genSym(nskIterator, $prc[0].ident & "Iter")
-  assert iteratorNameSym.symKind == nskIterator
-  #var iteratorNameSym = newIdentNode($prc[0].ident & "Iter")
-  var procBody = prc[6].convertReturns(retFutureSym)
-
-  var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")],
-                                procBody, nnkIteratorDef)
-  closureIterator[4] = newNimNode(nnkPragma).add(newIdentNode("closure"))
-  outerProcBody.add(closureIterator)
-
-  # -> var nameIterVar = nameIter
-  # -> var first = nameIterVar()
-  var varNameIterSym = newIdentNode($prc[0].ident & "IterVar") #genSym(nskVar, $prc[0].ident & "IterVar")
-  var varNameIter = newVarStmt(varNameIterSym, iteratorNameSym)
-  outerProcBody.add varNameIter
-  var varFirstSym = genSym(nskVar, "first")
-  assert varFirstSym.symKind ==  nskVar
-  var varFirst = newVarStmt(varFirstSym, newCall(varNameIterSym))
-  outerProcBody.add varFirst
-
-
-  result = prc
-
-  # Remove the 'closure' pragma.
-  for i in 0 .. <result[4].len:
-    if result[4][i].ident == !"async":
-      result[4].del(i)
-
-  result[6] = outerProcBody
-
-proc readStuff(): Future[string] {.async2.} =
-  var fut = connect(sock, "irc.freenode.org", Port(6667))
-  yield fut
-  var fut2 = recv(sock, 50)
-  yield fut2
-  return fut2.read
diff --git a/tests/macros/tgentemplates.nim b/tests/macros/tgentemplates.nim
deleted file mode 100644
index 301d58c6a..000000000
--- a/tests/macros/tgentemplates.nim
+++ /dev/null
@@ -1,35 +0,0 @@
-# bug #1140
-
-import parseutils, macros
-
-proc parse_until_symbol(node: NimNode, value: string, index: var int): bool {.compiletime.} =
-    var splitValue: string
-    var read = value.parseUntil(splitValue, '$', index)
-
-    # when false:
-    if false:
-        var identifier: string
-        read = value.parseWhile(identifier, {}, index)
-        node.add newCall("add", ident("result"), newCall("$", ident(identifier)))
-
-    if splitValue.len > 0:
-        node.insert node.len, newCall("add", ident("result"), newStrLitNode(splitValue))
-
-proc parse_template(node: NimNode, value: string) {.compiletime.} =
-    var index = 0
-    while index < value.len and
-        parse_until_symbol(node, value, index): discard
-
-macro tmpli*(body: untyped): typed =
-    result = newStmtList()
-    result.add parseExpr("result = \"\"")
-    result.parse_template body[1].strVal
-
-
-proc actual: string = tmpli html"""
-    <p>Test!</p>
-    """
-
-proc another: string = tmpli html"""
-    <p>what</p>
-    """
diff --git a/tests/macros/tgetimpl.nim b/tests/macros/tgetimpl.nim
index d38492934..ab33131b0 100644
--- a/tests/macros/tgetimpl.nim
+++ b/tests/macros/tgetimpl.nim
@@ -1,6 +1,7 @@
 discard """
-  msg: '''"muhaha"
+  nimout: '''foo = "muhaha"
 proc poo(x, y: int) =
+  let y = x
   echo ["poo"]'''
 """
 
@@ -10,11 +11,93 @@ const
   foo = "muhaha"
 
 proc poo(x, y: int) =
+  let y = x
   echo "poo"
 
 macro m(x: typed): untyped =
-  echo repr x.symbol.getImpl
-  result = x
+  echo repr x.getImpl
 
-discard m foo
-discard m poo
+m(foo)
+m(poo)
+
+#------------
+
+macro checkOwner(x: typed, check_id: static[int]): untyped =
+  let sym = case check_id:
+    of 0: x
+    of 1: x.getImpl.body[0][0][0]
+    of 2: x.getImpl.body[0][0][^1]
+    of 3: x.getImpl.body[1][0]
+    else: x
+  result = newStrLitNode($sym.owner.symKind)
+
+macro isSameOwner(x, y: typed): untyped =
+  result =
+    if x.owner == y.owner: bindSym"true"
+    else: bindSym"false"
+
+
+static:
+  doAssert checkOwner(foo, 0) == "nskModule"
+  doAssert checkOwner(poo, 0) == "nskModule"
+  doAssert checkOwner(poo, 1) == "nskProc"
+  doAssert checkOwner(poo, 2) == "nskProc"
+  doAssert checkOwner(poo, 3) == "nskModule"
+  doAssert isSameOwner(foo, poo)
+  proc wrappedScope() =
+    proc dummyproc() = discard
+    doAssert isSameOwner(foo, dummyproc) == false
+    doAssert isSameOwner(poo, dummyproc) == false
+  wrappedScope()
+
+macro check_gen_proc(ex: typed): (bool, bool) =
+  let lenChoice = bindsym"len"
+  var is_equal = false 
+  var is_instance_of = false 
+  for child in lenChoice:
+    if not is_equal:
+      is_equal = ex[0] == child
+    if not is_instance_of:
+      is_instance_of = isInstantiationOf(ex[0], child)
+         
+  result = nnkTupleConstr.newTree(newLit(is_equal), newLit(is_instance_of))
+
+# check that len(seq[int]) is not equal to bindSym"len", but is instance of it
+let a = @[1,2,3]
+assert: check_gen_proc(len(a)) == (false, true)
+
+
+#---------------------------------------------------------------
+# issue #16110
+
+macro check(x: type): untyped =
+  let z = getType(x)
+  let y = getImpl(z[1])  
+  var sym = y[0]
+  if sym.kind == nnkPragmaExpr: sym = sym[0]
+  if sym.kind == nnkPostfix: sym = sym[1]
+  expectKind(z[1], nnkSym)
+  expectKind(sym, nnkSym)
+  expectKind(y[2], nnkObjectTy)
+  doAssert(sym == z[1])
+
+type
+  TirePtr = ptr object
+    code: int
+
+  TireRef* = ref object
+    code: int
+
+  TireRef2* {.inheritable.} = ref object
+    code: int
+
+  TireRef3* {.inheritable.} = object
+    code: int
+
+var z1: TirePtr
+check(typeof(z1[]))
+var z2: TireRef
+check(typeof(z2[]))
+var z3: TireRef2
+check(typeof(z3[]))
+check(TireRef3)
diff --git a/tests/macros/tgetraiseslist.nim b/tests/macros/tgetraiseslist.nim
new file mode 100644
index 000000000..79694a66f
--- /dev/null
+++ b/tests/macros/tgetraiseslist.nim
@@ -0,0 +1,29 @@
+discard """
+  nimout: '''##[ValueError, Gen[string]]##
+%%[RootEffect]%%
+true true'''
+"""
+
+import macros
+import std / effecttraits
+
+type
+  Gen[T] = object of CatchableError
+    x: T
+
+macro m(call: typed): untyped =
+  echo "##", repr getRaisesList(call[0]), "##"
+  echo "%%", repr getTagsList(call[0]), "%%"
+  echo isGcSafe(call[0]), " ", hasNoSideEffects(call[0])
+  result = call
+
+proc gutenTag() {.tags: RootEffect.} = discard
+
+proc r(inp: int) =
+  if inp == 0:
+    raise newException(ValueError, "bah")
+  elif inp == 1:
+    raise newException(Gen[string], "bahB")
+  gutenTag()
+
+m r(2)
diff --git a/tests/macros/tgettype.nim b/tests/macros/tgettype.nim
index fa02bce57..d91efe1fe 100644
--- a/tests/macros/tgettype.nim
+++ b/tests/macros/tgettype.nim
@@ -1,27 +1,85 @@
-discard """
-msg: '''ObjectTy(Sym(Model), RecList(Sym(name), Sym(password)))
-BracketExpr(Sym(typeDesc), Sym(User))'''
-"""
-import strutils, macros
+import std/macros
+import stdtest/testutils
 
-type
-  Model = object of RootObj
-  User = object of Model
-    name : string
-    password : string
+# getType
 
-macro testUser: string =
-  result = newLit(User.getType.lispRepr)
+block:
+  type
+    Model = object of RootObj
+    User = object of Model
+      name : string
+      password : string
 
-macro testGeneric(T: typedesc[Model]): string=
-  result = newLit(T.getType.lispRepr)
+  macro testUser: string =
+    result = newLit(User.getType.lispRepr)
 
-echo testUser
-echo User.testGeneric
+  macro testGeneric(T: typedesc[Model]): string=
+    result = newLit(T.getType.lispRepr)
 
-macro assertVoid(e: typed): untyped =
-  assert(getTypeInst(e).typeKind == ntyVoid)
+  doAssert testUser == """(ObjectTy (Empty) (Sym "Model") (RecList (Sym "name") (Sym "password")))"""
+  doAssert User.testGeneric == """(BracketExpr (Sym "typeDesc") (Sym "User"))"""
 
-proc voidProc() = discard
+  macro assertVoid(e: typed): untyped =
+    assert(getTypeInst(e).typeKind == ntyVoid)
 
-assertVoid voidProc()
+  proc voidProc() = discard
+
+  assertVoid voidProc()
+
+block:
+  # refs #18220; not an actual solution (yet) but at least shows what's currently
+  # possible
+
+  type Callable1[R, T, U] = concept fn
+    fn(default(T)) is R
+    fn is U
+
+  # note that typetraits.arity doesn't work
+  macro arity(a: typed): int =
+    # number of params
+    # this is not production code!
+    let a2 = a.getType[1] # this used to crash nim, with: `vmdeps.nim(292, 25) `false``
+    newLit a2.len - 1
+
+  type Callable2[R, T, U] = concept fn
+    fn(default(T)) is R
+    fn is U
+    arity(U) == 2
+
+  proc map1[T, R, U](a: T, fn: Callable1[R, T, U]): R =
+    let fn = U(fn)
+      # `cast[U](fn)` would also work;
+      # this is currently needed otherwise, sigmatch errors with:
+      # Error: attempting to call routine: 'fn'
+      #  found 'fn' [param declared in tgettype.nim(53, 28)]
+      # this can be fixed in future work
+    fn(a)
+
+  proc map2[T, R, U](a: T, fn: Callable2[R, T, U]): R =
+    let fn = U(fn)
+    fn(a)
+
+  proc fn1(a: int, a2 = 'x'): string = $(a, a2, "fn1")
+  proc fn2(a: int, a2 = "zoo"): string = $(a, a2, "fn2")
+  proc fn3(a: int, a2 = "zoo2"): string = $(a, a2, "fn3")
+  proc fn4(a: int): string {.inline.} = $(a, "fn4")
+  proc fn5(a: int): string = $(a, "fn5")
+
+  assertAll:
+    # Callable1
+    1.map1(fn1) == """(1, 'x', "fn1")"""
+    1.map1(fn2) == """(1, "zoo", "fn2")"""
+    1.map1(fn3) == """(1, "zoo", "fn3")"""
+      # fn3's optional param is not honored, because fn3 and fn2 yield same
+      # generic instantiation; this is a caveat with this approach
+      # There are several possible ways to improve things in future work.
+    1.map1(fn4) == """(1, "fn4")"""
+    1.map1(fn5) == """(1, "fn5")"""
+
+    # Callable2; prevents passing procs with optional params to avoid above
+    # mentioned caveat, but more restrictive
+    not compiles(1.map2(fn1))
+    not compiles(1.map2(fn2))
+    not compiles(1.map2(fn3))
+    1.map2(fn4) == """(1, "fn4")"""
+    1.map2(fn5) == """(1, "fn5")"""
diff --git a/tests/macros/tgettype2.nim b/tests/macros/tgettype2.nim
index f129e6e1b..c579cf6ff 100644
--- a/tests/macros/tgettype2.nim
+++ b/tests/macros/tgettype2.nim
@@ -1,3 +1,35 @@
+discard """
+output: '''
+############
+#### gt ####
+############
+gt(Foo):	typeDesc[Foo]
+gt(Bar):	typeDesc[Bar]
+gt(Baz):	typeDesc[int]
+gt(foo):	distinct[int]
+gt(bar):	distinct[int]
+gt(baz):	int, int
+gt(v):	seq[int]
+gt(vv):	seq[float]
+gt(t):	distinct[tuple[int, int]]
+gt(tt):	distinct[tuple[float, float]]
+gt(s):	distinct[tuple[int, int]]
+#############
+#### gt2 ####
+#############
+gt2(Foo): 	Foo
+gt2(Bar): 	Bar
+gt2(Baz): 	Baz
+gt2(foo): 	Foo
+gt2(bar): 	Bar
+gt2(baz): 	Baz
+gt2(v): 	seq[int]
+gt2(vv): 	seq[float]
+gt2(t): 	MyType[system.int]
+gt2(tt): 	MyType[system.float]
+gt2(s): 	MySimpleType
+'''
+"""
 
 import macros, typetraits
 
diff --git a/tests/macros/tgettypeinst.nim b/tests/macros/tgettypeinst.nim
index 2f1abe193..e722f14aa 100644
--- a/tests/macros/tgettypeinst.nim
+++ b/tests/macros/tgettypeinst.nim
@@ -1,7 +1,7 @@
 discard """
 """
 
-import macros, strUtils
+import macros
 
 proc symToIdent(x: NimNode): NimNode =
   case x.kind:
@@ -22,7 +22,7 @@ proc symToIdent(x: NimNode): NimNode =
         result.add symToIdent(c)
 
 # check getTypeInst and getTypeImpl for given symbol x
-macro testX(x,inst0: typed; recurse: static[bool]; implX: typed): typed =
+macro testX(x,inst0: typed; recurse: static[bool]; implX: typed) =
   # check that getTypeInst(x) equals inst0
   let inst = x.getTypeInst
   let instr = inst.symToIdent.treeRepr
@@ -183,3 +183,32 @@ test(Vec4[float32]):
 # bug #4862
 static:
   discard typedesc[(int, int)].getTypeImpl
+
+# custom pragmas
+template myAttr() {.pragma.}
+template myAttr2() {.pragma.}
+template myAttr3() {.pragma.}
+template serializationKey(key: string) {.pragma.}
+
+type
+  MyObj {.packed,myAttr,serializationKey: "one".} = object
+    myField {.myAttr2,serializationKey: "two".}: int
+    myField2 {.myAttr3,serializationKey: "three".}: float
+
+# field pragmas not currently supported
+test(MyObj):
+  type
+    _ {.packed,myAttr,serializationKey: "one".} = object
+      myField: int
+      myField2: float
+
+block t9600:
+  type
+    Apple = ref object of RootObj
+
+  macro mixer(x: typed): untyped =
+    let w = getType(x)
+    let v = getTypeImpl(w[1])
+
+  var z: Apple
+  mixer(z)
diff --git a/tests/macros/tgettypeinst7737.nim b/tests/macros/tgettypeinst7737.nim
new file mode 100644
index 000000000..e49f82562
--- /dev/null
+++ b/tests/macros/tgettypeinst7737.nim
@@ -0,0 +1,61 @@
+discard """
+  nimout: '''
+seq[int]
+CustomSeq[int]
+'''
+"""
+
+import macros, typetraits, sequtils
+
+block: # issue #7737 original
+  type
+    CustomSeq[T] = object
+      data: seq[T]
+
+  proc getSubType(T: NimNode): NimNode =
+    echo getTypeInst(T).repr
+    result = getTypeInst(T)[1]
+
+  macro typed_helper(x: varargs[typed]): untyped =
+    let foo = getSubType(x[0])
+    result = quote do: discard
+
+  macro untyped_heavylifting(x: varargs[untyped]): untyped =
+    var containers = nnkArgList.newTree()
+    for arg in x:
+      case arg.kind:
+      of nnkInfix:
+        if eqIdent(arg[0], "in"):
+          containers.add arg[2]
+      else:
+        discard
+    result = quote do:
+      typed_helper(`containers`)
+  var a, b, c: seq[int]
+  untyped_heavylifting z in c, x in a, y in b:
+    discard
+  ## The following gives me CustomSeq instead
+  ## of CustomSeq[int] in getTypeInst
+  var u, v, w: CustomSeq[int]
+  untyped_heavylifting z in u, x in v, y in w:
+    discard
+
+block: # issue #7737 comment
+  type
+    CustomSeq[T] = object
+      data: seq[T]
+  # when using just one argument, `foo` and `bar` should be exactly
+  # identical.
+  macro foo(arg: typed): string =
+    result = newLit(arg.getTypeInst.repr)
+  macro bar(args: varargs[typed]): untyped =
+    result = newTree(nnkBracket)
+    for arg in args:
+      result.add newLit(arg.getTypeInst.repr)
+  var
+    a: seq[int]
+    b: CustomSeq[int]
+  doAssert foo(a) == "seq[int]"
+  doAssert bar(a) == ["seq[int]"]
+  doAssert foo(b) == "CustomSeq[int]"
+  doAssert bar(b) == ["CustomSeq[int]"]
diff --git a/tests/macros/tidgen.nim b/tests/macros/tidgen.nim
deleted file mode 100644
index 88322b227..000000000
--- a/tests/macros/tidgen.nim
+++ /dev/null
@@ -1,19 +0,0 @@
-discard """
-  output: "3 4"
-"""
-
-import macros
-
-# Test compile-time state in same module
-
-var gid {.compileTime.} = 3
-
-macro genId(): int =
-  result = newIntLitNode(gid)
-  inc gid
-
-proc Id1(): int {.compileTime.} = return genId()
-proc Id2(): int {.compileTime.} = return genId()
-
-echo Id1(), " ", Id2()
-
diff --git a/tests/macros/tincremental.nim b/tests/macros/tincremental.nim
new file mode 100644
index 000000000..401d6f3f8
--- /dev/null
+++ b/tests/macros/tincremental.nim
@@ -0,0 +1,150 @@
+discard """
+  output: '''heavy_calc_impl is called
+sub_calc1_impl is called
+sub_calc2_impl is called
+** no changes recompute effectively
+** change one input and recompute effectively
+heavy_calc_impl is called
+sub_calc2_impl is called'''
+"""
+
+# sample incremental
+
+import tables
+import macros
+
+var inputs = initTable[string, float]() 
+var cache = initTable[string, float]()
+var dep_tree {.compileTime.} = initTable[string, string]()
+
+macro symHash(s: typed{nkSym}): string = 
+  result = newStrLitNode(symBodyHash(s))
+
+#######################################################################################
+
+template graph_node(key: string) {.pragma.}
+
+proc tag(n: NimNode): NimNode = 
+  ## returns graph node unique name of a function or nil if it is not a graph node
+  expectKind(n, {nnkProcDef, nnkFuncDef})
+  for p in n.pragma:
+    if p.len > 0 and p[0] == bindSym"graph_node":
+      return p[1]
+  return nil 
+
+macro graph_node_key(n: typed{nkSym}): untyped =
+  result = newStrLitNode(n.symBodyHash)
+
+macro graph_discovery(n: typed{nkSym}): untyped =
+  # discovers graph dependency tree and updated dep_tree global var
+  let mytag = newStrLitNode(n.symBodyHash)
+  var visited: seq[NimNode]
+  proc discover(n: NimNode) = 
+    case n.kind:
+      of nnkNone..pred(nnkSym), succ(nnkSym)..nnkNilLit: discard
+      of nnkSym:
+        if n.symKind in {nskFunc, nskProc}:
+          if n notin visited:
+            visited.add n
+            let tag = n.getImpl.tag
+            if tag != nil:
+              dep_tree[tag.strVal] =  mytag.strVal
+            else:
+              discover(n.getImpl.body)
+      else:
+        for child in n:
+          discover(child)
+  discover(n.getImpl.body)
+  result = newEmptyNode()
+
+#######################################################################################
+
+macro incremental_input(key: static[string], n: untyped{nkFuncDef}): untyped =
+  # mark leaf nodes of the graph
+  template getInput(key) {.dirty.} =
+    {.noSideEffect.}:
+      inputs[key]
+  result = n
+  result.pragma = nnkPragma.newTree(nnkCall.newTree(bindSym"graph_node", newStrLitNode(key)))
+  result.body = getAst(getInput(key))
+
+macro incremental(n: untyped{nkFuncDef}): untyped =
+  ## incrementalize side effect free computation
+  ## wraps function into caching layer, mark caching function as a graph_node
+  ## injects dependency discovery between graph nodes
+  template cache_func_body(func_name, func_name_str, func_call) {.dirty.} =
+    {.noSideEffect.}: 
+      graph_discovery(func_name)
+      let key = graph_node_key(func_name)
+      if key in cache:
+        result = cache[key]
+      else:
+        echo func_name_str & " is called"
+        result = func_call
+        cache[key] = result
+
+  let func_name = n.name.strVal & "_impl"
+  let func_call = nnkCall.newTree(ident func_name)
+  for i in 1..<n.params.len:
+    func_call.add n.params[i][0]
+  let cache_func = n.copyNimTree
+  cache_func.body = getAst(cache_func_body(ident func_name, func_name, func_call))
+  cache_func.pragma = nnkPragma.newTree(newCall(bindSym"graph_node", 
+    newCall(bindSym"symHash", ident func_name)))
+  
+  n.name = ident(func_name)
+  result = nnkStmtList.newTree(n, cache_func)
+
+###########################################################################
+### Example
+###########################################################################
+
+func input1(): float {.incremental_input("a1").}
+
+func input2(): float {.incremental_input("a2").}
+
+func sub_calc1(a: float): float  {.incremental.} = 
+  a + input1()
+
+func sub_calc2(b: float): float  {.incremental.} = 
+  b + input2()
+
+func heavy_calc(a: float, b: float): float {.incremental.} = 
+  sub_calc1(a) + sub_calc2(b)
+
+###########################################################################
+## graph finalize and inputs
+###########################################################################
+
+macro finalize_dep_tree(): untyped = 
+  result = nnkTableConstr.newNimNode
+  for key, val in dep_tree:
+    result.add nnkExprColonExpr.newTree(newStrLitNode key, newStrLitNode val)
+  result = nnkCall.newTree(bindSym"toTable", result)
+
+const dep_tree_final = finalize_dep_tree()
+
+proc set_input(key: string, val: float) = 
+  ## set input value
+  ## all affected nodes of graph are invalidated
+  inputs[key] = val
+  var k = key
+  while k != "":
+    k = dep_tree_final.getOrDefault(k , "")
+    cache.del(k)
+
+###########################################################################
+## demo
+###########################################################################
+
+set_input("a1", 5)
+set_input("a2", 2)
+discard heavy_calc(5.0, 10.0)
+
+echo "** no changes recompute effectively"
+discard heavy_calc(5.0, 10.0)
+
+echo "** change one input and recompute effectively"
+
+set_input("a2", 10)
+discard heavy_calc(5.0, 10.0)
diff --git a/tests/macros/tinvalidtypesym.nim b/tests/macros/tinvalidtypesym.nim
new file mode 100644
index 000000000..af6f31d10
--- /dev/null
+++ b/tests/macros/tinvalidtypesym.nim
@@ -0,0 +1,14 @@
+discard """
+errormsg: "type expected, but symbol 'MyType' has no type."
+"""
+
+import macros
+
+macro foobar(name) =
+  let sym = genSym(nskType, "MyType")
+
+  result = quote do:
+    type
+      `name` = `sym`
+
+foobar(MyAlias)
diff --git a/tests/macros/tisexported.nim b/tests/macros/tisexported.nim
new file mode 100644
index 000000000..53766edf5
--- /dev/null
+++ b/tests/macros/tisexported.nim
@@ -0,0 +1,10 @@
+import macros
+
+proc t1* = discard
+proc t2 = discard
+
+macro check(p1: typed, p2: typed) =
+  doAssert isExported(p1) == true
+  doAssert isExported(p2) == false
+
+check t1, t2
diff --git a/tests/macros/tlexerex.nim b/tests/macros/tlexerex.nim
deleted file mode 100644
index db2c38ef6..000000000
--- a/tests/macros/tlexerex.nim
+++ /dev/null
@@ -1,15 +0,0 @@
-import macros
-
-macro match*(s: cstring|string; pos: int; sections: varargs[untyped]): untyped =
-  for sec in sections:
-    expectKind sec, nnkOfBranch
-    expectLen sec, 2
-  result = newStmtList()
-
-when isMainModule:
-  var input = "the input"
-  var pos = 0
-  match input, pos:
-  of r"[a-zA-Z_]\w+": echo "an identifier"
-  of r"\d+": echo "an integer"
-  of r".": echo "something else"
diff --git a/tests/macros/tlocktypednode1.nim b/tests/macros/tlocktypednode1.nim
new file mode 100644
index 000000000..f760c7cf1
--- /dev/null
+++ b/tests/macros/tlocktypednode1.nim
@@ -0,0 +1,12 @@
+discard """
+errormsg: "typechecked nodes may not be modified"
+"""
+
+import macros
+
+macro doSomething(arg: typed): untyped =
+  echo arg.treeREpr
+  result = arg
+  result.add newCall(bindSym"echo", newLit(1))
+
+doSomething((echo(1); echo(2)))
diff --git a/tests/macros/tlocktypednode2.nim b/tests/macros/tlocktypednode2.nim
new file mode 100644
index 000000000..e921772b0
--- /dev/null
+++ b/tests/macros/tlocktypednode2.nim
@@ -0,0 +1,12 @@
+discard """
+errormsg: "typechecked nodes may not be modified"
+"""
+
+import macros
+
+macro doSomething(arg: typed): untyped =
+  echo arg.treeREpr
+  result = arg
+  result[0] = newCall(bindSym"echo", newLit(1))
+
+doSomething((echo(1); echo(2)))
diff --git a/tests/macros/tlocktypednode3.nim b/tests/macros/tlocktypednode3.nim
new file mode 100644
index 000000000..0eb9d4467
--- /dev/null
+++ b/tests/macros/tlocktypednode3.nim
@@ -0,0 +1,15 @@
+discard """
+errormsg: "typechecked nodes may not be modified"
+"""
+
+import macros
+
+macro doSomething(arg: typed): untyped =
+  echo arg.treeREpr
+  result = arg
+  result.add(
+    newCall(bindSym"echo", newLit(3)),
+    newCall(bindSym"echo", newLit(1))
+  )
+
+doSomething((echo(1); echo(2)))
diff --git a/tests/macros/tmacro1.nim b/tests/macros/tmacro1.nim
index ac2bf9094..18bbeb53d 100644
--- a/tests/macros/tmacro1.nim
+++ b/tests/macros/tmacro1.nim
@@ -1,7 +1,5 @@
 import  macros
 
-from uri import `/`
-
 macro test*(a: untyped): untyped =
   var nodes: tuple[a, b: int]
   nodes.a = 4
@@ -22,7 +20,6 @@ macro test*(a: untyped): untyped =
 test:
   "hi"
 
-import strutils
 
 template assertNot(arg: untyped): untyped =
   assert(not(arg))
@@ -79,3 +76,44 @@ static:
   assert fooSym.kind in {nnkOpenSymChoice, nnkClosedSymChoice}
   assert    fooSym.eqIdent("fOO")
   assertNot fooSym.eqIdent("bar")
+
+  # eqIdent on exported and backtick quoted identifiers
+  let procName = ident("proc")
+  let quoted = nnkAccQuoted.newTree(procName)
+  let exported = nnkPostfix.newTree(ident"*", procName)
+  let exportedQuoted = nnkPostfix.newTree(ident"*", quoted)
+
+  let nodes = @[procName, quoted, exported, exportedQuoted]
+
+  for i in 0 ..< nodes.len:
+    for j in 0 ..< nodes.len:
+      doAssert eqIdent(nodes[i], nodes[j])
+
+  for node in nodes:
+    doAssert eqIdent(node, "proc")
+
+
+  var empty: NimNode
+  var myLit = newLit("str")
+
+  assert( (empty or myLit) == myLit )
+
+  empty = newEmptyNode()
+
+  assert( (empty or myLit) == myLit )
+
+  proc bottom(): NimNode =
+    quit("may not be evaluated")
+
+  assert( (myLit or bottom()) == myLit )
+
+type
+  Fruit = enum
+    apple
+    banana
+    orange
+
+macro foo(x: typed) =
+  doAssert Fruit(x.intVal) == banana
+
+foo(banana)
diff --git a/tests/macros/tmacro2.nim b/tests/macros/tmacro2.nim
index 72972c0c1..1ad63ec8c 100644
--- a/tests/macros/tmacro2.nim
+++ b/tests/macros/tmacro2.nim
@@ -26,3 +26,11 @@ const t = mac("HEllo World")
 echo s, " ", t
 
 
+#-----------------------------------------------------------------------------
+# issue #15326
+macro m(n:typed):auto =
+  result = n
+
+proc f[T](x:T): T {.m.} = x
+
+discard f(3)
\ No newline at end of file
diff --git a/tests/macros/tmacro3.nim b/tests/macros/tmacro3.nim
index a1dc4f371..38e8349e7 100644
--- a/tests/macros/tmacro3.nim
+++ b/tests/macros/tmacro3.nim
@@ -18,7 +18,7 @@ test:
 
 macro test2*(a: untyped): untyped =
   proc testproc(recurse: int) =
-    echo "Thats weird"
+    echo "That's weird"
     var o : NimNode = nil
     echo "  no its not!"
     o = newNimNode(nnkNone)
diff --git a/tests/macros/tmacro4.nim b/tests/macros/tmacro4.nim
index 164afaeb7..cb0399894 100644
--- a/tests/macros/tmacro4.nim
+++ b/tests/macros/tmacro4.nim
@@ -2,8 +2,7 @@ discard """
   output: "after"
 """
 
-import
-  macros, strutils
+import macros
 
 macro test_macro*(s: string, n: untyped): untyped =
   result = newNimNode(nnkStmtList)
@@ -11,7 +10,7 @@ macro test_macro*(s: string, n: untyped): untyped =
   add(ass, newIdentNode("str"))
   add(ass, newStrLitNode("after"))
   add(result, ass)
-when isMainModule:
+when true:
   var str: string = "before"
   test_macro(str):
     var i : integer = 123
diff --git a/tests/macros/tmacro5.nim b/tests/macros/tmacro5.nim
index 1c60e1ffd..802fb28d5 100644
--- a/tests/macros/tmacro5.nim
+++ b/tests/macros/tmacro5.nim
@@ -38,7 +38,7 @@ macro importImpl_forward(name, returns: untyped): untyped =
   decls.add res
   echo(repr(res))
 
-macro importImpl(name, returns, body: untyped): typed =
+macro importImpl(name, returns, body: untyped) =
   #var res = getAST(importImpl_forward(name, returns))
   discard getAST(importImpl_forward(name, returns))
   var res = copyNimTree(decls[decls.high])
@@ -46,7 +46,7 @@ macro importImpl(name, returns, body: untyped): typed =
   echo repr(res)
   impls.add res
 
-macro okayy: typed =
+macro okayy() =
   result = newNimNode(nnkStmtList)
   for node in decls: result.add node
   for node in impls: result.add node
diff --git a/tests/macros/tmacro6.nim b/tests/macros/tmacro6.nim
new file mode 100644
index 000000000..c65d34b6d
--- /dev/null
+++ b/tests/macros/tmacro6.nim
@@ -0,0 +1,75 @@
+discard """
+errormsg: "expression '123' is of type 'int literal(123)' and has to be used (or discarded)"
+line: 71
+"""
+
+import macros
+
+proc foo(a, b, c: int): int =
+  result += a
+  result += b
+  result += c
+
+macro bar(a, b, c: int): int =
+  result = newCall(ident"echo")
+  result.add a
+  result.add b
+  result.add c
+
+macro baz(a, b, c: int): int =
+  let stmt = nnkStmtListExpr.newTree()
+  stmt.add newCall(ident"echo", a)
+  stmt.add newCall(ident"echo", b)
+  stmt.add newCall(ident"echo", c)
+  stmt.add newLit(123)
+  return c
+
+# test no result type with explicit return
+
+macro baz2(a, b, c: int) =
+  let stmt = nnkStmtListExpr.newTree()
+  stmt.add newCall(ident"echo", a)
+  stmt.add newCall(ident"echo", b)
+  stmt.add newCall(ident"echo", c)
+  return stmt
+
+# test explicit void type with explicit return
+
+macro baz3(a, b, c: int): void =
+  let stmt = nnkStmtListExpr.newTree()
+  stmt.add newCall(ident"echo", a)
+  stmt.add newCall(ident"echo", b)
+  stmt.add newCall(ident"echo", c)
+  return stmt
+
+# test no result type with result variable
+
+macro baz4(a, b, c: int) =
+  result = nnkStmtListExpr.newTree()
+  result.add newCall(ident"echo", a)
+  result.add newCall(ident"echo", b)
+  result.add newCall(ident"echo", c)
+
+# test explicit void type with result variable
+
+macro baz5(a, b, c: int): void =
+  let result = nnkStmtListExpr.newTree()
+  result.add newCall(ident"echo", a)
+  result.add newCall(ident"echo", b)
+  result.add newCall(ident"echo", c)
+
+macro foobar1(): int =
+  result = quote do:
+    echo "Hello World"
+    1337
+
+echo foobar1()
+
+# this should create an error message, because 123 has to be discarded.
+
+macro foobar2() =
+  result = quote do:
+    echo "Hello World"
+    123
+
+echo foobar2()
diff --git a/tests/macros/tmacro7.nim b/tests/macros/tmacro7.nim
new file mode 100644
index 000000000..602191506
--- /dev/null
+++ b/tests/macros/tmacro7.nim
@@ -0,0 +1,36 @@
+discard """
+output: '''
+calling!stuff
+calling!stuff
+'''
+disabled: true
+"""
+
+# this test modifies an already semchecked ast (bad things happen)
+# this test relies on the bug #4547
+# issue #7792
+
+import macros
+
+proc callProc(str: string) =
+  echo "calling!" & str
+
+macro testMacro(code: typed): untyped =
+  let stmtList = newNimNode(nnkStmtList)
+
+  let stmts = code[6]
+
+  for n in stmts.children:
+    # the error happens here
+    stmtList.add(newCall(bindSym("callProc"), newLit("stuff")))
+
+  code[6] = stmtList
+
+  result = newEmptyNode()
+
+proc main() {.testMacro.} =
+  echo "test"
+  echo "test2"
+
+when isMainModule:
+  main()
diff --git a/tests/macros/tmacro8.nim b/tests/macros/tmacro8.nim
new file mode 100644
index 000000000..fdcec4dd4
--- /dev/null
+++ b/tests/macros/tmacro8.nim
@@ -0,0 +1,35 @@
+# issue #8573
+
+import
+  macros,
+  strutils,
+  terminal
+
+type LogSeverity* = enum
+  sevError = "Error"
+  sevWarn  = "Warn"
+  sevInfo  = "Info"
+  sevDebug = "Debug"
+
+macro log*(severity: static[LogSeverity], group: static[string], m: varargs[typed]): untyped =
+  let sevStr   = align("[" & toUpperAscii($severity) & "] ", 8)
+  let sevColor = case severity
+    of sevError: fgRed
+    of sevWarn:  fgYellow
+    of sevInfo:  fgWhite
+    of sevDebug: fgBlack
+
+  let groupStr = "[" & $group & "] "
+
+  result = quote do:
+    setStyle({ styleBright })
+    setForegroundColor(sevColor) # <==
+    write(stdout, sevStr)
+
+    setStyle({ styleDim })
+    setForegroundColor(fgWhite)
+    write(stdout, groupStr)
+
+  let wl = newCall(bindSym"styledWriteLine", bindSym"stdout")
+  for arg in m: wl.add(arg)
+  result.add(wl)
diff --git a/tests/macros/tmacro_in_template.nim b/tests/macros/tmacro_in_template.nim
deleted file mode 100644
index 9668495df..000000000
--- a/tests/macros/tmacro_in_template.nim
+++ /dev/null
@@ -1,10 +0,0 @@
-
-# bug #1944
-import macros
-
-template t(e: untyped): untyped =
-  macro m(eNode: untyped): untyped =
-    echo eNode.treeRepr
-  m e
-
-t 5
diff --git a/tests/macros/tmacrogenerics.nim b/tests/macros/tmacrogenerics.nim
index 919a15b46..c31b5564c 100644
--- a/tests/macros/tmacrogenerics.nim
+++ b/tests/macros/tmacrogenerics.nim
@@ -1,8 +1,9 @@
 discard """
-  file: "tmacrogenerics.nim"
-  msg: '''
-instantiation 1 with typedesc and typedesc
-counter: 1
+  nimout: '''
+instantiation 1 with typeDesc[int] and typeDesc[float]
+instantiation 2 with typeDesc[float] and typeDesc[string]
+instantiation 3 with typeDesc[string] and typeDesc[string]
+counter: 3
 '''
   output: "int\nfloat\nint\nstring"
 """
@@ -13,7 +14,7 @@ var counter {.compileTime.} = 0
 
 macro makeBar(A, B: typedesc): typedesc =
   inc counter
-  echo "instantiation ", counter, " with ", A.name, " and ", B.name
+  echo "instantiation ", counter, " with ", A.getTypeInst.repr, " and ", B.getTypeInst.repr
   result = A
 
 type
diff --git a/tests/macros/tmacrogensym.nim b/tests/macros/tmacrogensym.nim
new file mode 100644
index 000000000..7c0d75f82
--- /dev/null
+++ b/tests/macros/tmacrogensym.nim
@@ -0,0 +1,65 @@
+import nativesockets, asyncdispatch, macros
+var p = newDispatcher()
+var sock = createAsyncNativeSocket()
+
+proc convertReturns(node, retFutureSym: NimNode): NimNode {.compileTime.} =
+  case node.kind
+  of nnkReturnStmt:
+    result = newCall(newIdentNode("complete"), retFutureSym, node[0])
+  else:
+    result = node
+    for i in 0 ..< node.len:
+      result[i] = convertReturns(node[i], retFutureSym)
+
+macro async2(prc: untyped): untyped =
+  expectKind(prc, nnkProcDef)
+
+  var outerProcBody = newNimNode(nnkStmtList)
+
+  # -> var retFuture = newFuture[T]()
+  var retFutureSym = newIdentNode("retFuture") #genSym(nskVar, "retFuture")
+  outerProcBody.add(
+    newVarStmt(retFutureSym,
+      newCall(
+        newNimNode(nnkBracketExpr).add(
+          newIdentNode("newFuture"),
+          prc[3][0][1])))) # Get type from return type of this proc.
+
+  # -> iterator nameIter(): FutureBase {.closure.} = <proc_body>
+  # Changing this line to: newIdentNode($prc[0].ident & "Iter") # will make it work.
+  var iteratorNameSym = genSym(nskIterator, $prc[0] & "Iter")
+  assert iteratorNameSym.symKind == nskIterator
+  #var iteratorNameSym = newIdentNode($prc[0].ident & "Iter")
+  var procBody = prc[6].convertReturns(retFutureSym)
+
+  var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")],
+                                procBody, nnkIteratorDef)
+  closureIterator[4] = newNimNode(nnkPragma).add(newIdentNode("closure"))
+  outerProcBody.add(closureIterator)
+
+  # -> var nameIterVar = nameIter
+  # -> var first = nameIterVar()
+  var varNameIterSym = newIdentNode($prc[0] & "IterVar") #genSym(nskVar, $prc[0].ident & "IterVar")
+  var varNameIter = newVarStmt(varNameIterSym, iteratorNameSym)
+  outerProcBody.add varNameIter
+  var varFirstSym = genSym(nskVar, "first")
+  assert varFirstSym.symKind ==  nskVar
+  var varFirst = newVarStmt(varFirstSym, newCall(varNameIterSym))
+  outerProcBody.add varFirst
+
+
+  result = prc
+
+  # Remove the 'closure' pragma.
+  for i in 0 ..< result[4].len:
+    if result[4][i] == newIdentNode("async"):
+      result[4].del(i)
+
+  result[6] = outerProcBody
+
+proc readStuff(): Future[string] {.async2.} =
+  var fut = connect(sock, "irc.freenode.org", Port(6667))
+  yield fut
+  var fut2 = recv(sock, 50)
+  yield fut2
+  return fut2.read
diff --git a/tests/macros/tmacrogetimpl.nim b/tests/macros/tmacrogetimpl.nim
new file mode 100644
index 000000000..1d996ff29
--- /dev/null
+++ b/tests/macros/tmacrogetimpl.nim
@@ -0,0 +1,31 @@
+import macros
+
+# bug #5034
+
+macro copyImpl(srsProc: typed, toSym: untyped) =
+  result = copyNimTree(getImplTransformed(srsProc))
+  result[0] = ident $toSym.toStrLit()
+
+proc foo1(x: float, one: bool = true): float =
+  if one:
+    return 1'f
+  result = x
+
+proc bar1(what: string): string =
+  ## be a little more adversarial with `skResult`
+  proc buzz: string =
+    result = "lightyear"
+  if what == "buzz":
+    result = "buzz " & buzz()
+  else:
+    result = what
+  return result
+
+copyImpl(foo1, foo2)
+doAssert foo1(1'f) == 1.0
+doAssert foo2(10.0, false) == 10.0
+doAssert foo2(10.0) == 1.0
+
+copyImpl(bar1, bar2)
+doAssert bar1("buzz") == "buzz lightyear"
+doAssert bar1("macros") == "macros"
diff --git a/tests/macros/tmacros1.nim b/tests/macros/tmacros1.nim
index 80afb6641..c588ff7e6 100644
--- a/tests/macros/tmacros1.nim
+++ b/tests/macros/tmacros1.nim
@@ -1,9 +1,11 @@
 discard """
-  output: "Got: 'nnkCall' hi"
+  output: '''Got: 'nnkCall' hi
+{a}
+{b}
+{a, b}'''
 """
 
-import
-  macros, strutils
+import macros
 
 macro outterMacro*(n, blck: untyped): untyped =
   let n = callsite()
@@ -27,3 +29,53 @@ var str: string
 outterMacro(str):
   "hellow"
 echo str
+
+type E = enum a b
+macro enumerators1(): set[E] = newLit({a})
+
+macro enumerators2(): set[E] =
+  return newLit({b})
+
+macro enumerators3(): set[E] =
+  result = newLit({E.low .. E.high})
+
+var myEnums: set[E]
+
+
+myEnums = enumerators1()
+echo myEnums
+myEnums = enumerators2()
+echo myEnums
+myEnums = enumerators3()
+echo myEnums
+
+#10751
+
+type Tuple = tuple
+  a: string
+  b: int
+
+macro foo(t: static Tuple): untyped =
+  doAssert t.a == "foo"
+  doAssert t.b == 12345
+
+foo((a: "foo", b: 12345))
+
+
+# bug #16307
+
+macro bug(x: untyped): string =
+  newLit repr(x)
+
+let res = bug:
+  block:
+    ## one
+    ## two
+    ## three
+
+doAssert res == """
+
+block:
+  ## one
+  ## two
+  ## three"""
diff --git a/tests/macros/tmacros_issues.nim b/tests/macros/tmacros_issues.nim
new file mode 100644
index 000000000..a81c51658
--- /dev/null
+++ b/tests/macros/tmacros_issues.nim
@@ -0,0 +1,521 @@
+discard """
+  nimout: '''
+IntLit 5
+proc (x: int): string => typeDesc[proc[string, int]]
+proc (x: int): void => typeDesc[proc[void, int]]
+proc (x: int) => typeDesc[proc[void, int]]
+x => seq[int]
+a
+s
+d
+f
+TTaa
+TTaa
+TTaa
+TTaa
+true
+true
+nil
+42
+false
+true
+@[i0, i1, i2, i3, i4]
+@[tmp, tmp, tmp, tmp, tmp]
+'''
+
+  output: '''
+range[0 .. 100]
+array[0 .. 100, int]
+10
+test
+0o377'i8
+0o000000000755'i32
+1
+2
+3
+foo1
+foo2
+foo3
+true
+false
+true
+false
+1.0
+'''
+"""
+
+
+import macros, parseutils
+
+
+block t7723:
+  macro foo1(): untyped =
+    result = newStmtList()
+    result.add quote do:
+      proc init(foo: int, bar: typedesc[int]): int =
+        foo
+
+  #expandMacros:
+  foo1()
+
+  doAssert init(1, int) == 1
+
+
+
+block t8706:
+  macro varargsLen(args:varargs[untyped]): untyped =
+    doAssert args.kind == nnkArgList
+    doAssert args.len == 0
+    result = newLit(args.len)
+
+  template bar(a0:varargs[untyped]): untyped =
+    varargsLen(a0)
+
+  template foo(x: int, a0:varargs[untyped]): untyped =
+    bar(a0)
+
+  doAssert foo(42) == 0
+  doAssert bar() == 0
+
+
+
+block t9194:
+  type
+    Foo1 = range[0 .. 100]
+    Foo2 = array[0 .. 100, int]
+
+  macro get(T: typedesc): untyped =
+    # Get the X out of typedesc[X]
+    let tmp = getTypeImpl(T)
+    result = newStrLitNode(getTypeImpl(tmp[1]).repr)
+
+  echo Foo1.get
+  echo Foo2.get
+
+
+
+block t1944:
+  template t(e: untyped): untyped =
+    macro m(eNode: untyped): untyped =
+      echo eNode.treeRepr
+    m e
+
+  t 5
+
+
+block t926:
+  proc test(f: var NimNode) {.compileTime.} =
+    f = newNimNode(nnkStmtList)
+    f.add newCall(newIdentNode("echo"), newLit(10))
+
+  macro blah(prc: untyped): untyped =
+    result = prc
+    test(result)
+
+  proc test() {.blah.} =
+    echo 5
+
+
+
+block t2211:
+  macro showType(t:typed): untyped =
+    let ty = t.getType
+    echo t.repr, " => ", ty.repr
+
+  showType(proc(x:int): string)
+  showType(proc(x:int): void)
+  showType(proc(x:int))
+
+  var x: seq[int]
+  showType(x)
+
+
+
+block t1140:
+  proc parse_until_symbol(node: NimNode, value: string, index: var int): bool {.compiletime.} =
+    var splitValue: string
+    var read = value.parseUntil(splitValue, '$', index)
+
+    # when false:
+    if false:
+        var identifier: string
+        read = value.parseWhile(identifier, {}, index)
+        node.add newCall("add", ident("result"), newCall("$", ident(identifier)))
+
+    if splitValue.len > 0:
+        node.insert node.len, newCall("add", ident("result"), newStrLitNode(splitValue))
+
+  proc parse_template(node: NimNode, value: string) {.compiletime.} =
+      var index = 0
+      while index < value.len and
+          parse_until_symbol(node, value, index): discard
+
+  macro tmpli(body: untyped): typed =
+      result = newStmtList()
+      result.add parseExpr("result = \"\"")
+      result.parse_template body[1].strVal
+
+
+  proc actual: string {.used.} = tmpli html"""
+      <p>Test!</p>
+      """
+
+  proc another: string {.used.} = tmpli html"""
+      <p>what</p>
+      """
+
+
+
+block tbugs:
+  type
+    Foo = object
+      s: char
+
+  iterator test2(f: string): Foo =
+    for i in f:
+      yield Foo(s: i)
+
+  macro test(): untyped =
+    for i in test2("asdf"):
+      echo i.s
+
+  test()
+
+
+  # bug 1297
+
+  type TType = tuple[s: string]
+
+  macro echotest(): untyped =
+    var t: TType
+    t.s = ""
+    t.s.add("test")
+    result = newCall(newIdentNode("echo"), newStrLitNode(t.s))
+
+  echotest()
+
+  # bug #1103
+
+  type
+      Td = tuple
+          a:string
+          b:int
+
+  proc get_data(d: Td) : string {.compileTime.} =
+      result = d.a # Works if a literal string is used here.
+      # Bugs if line A or B is active. Works with C
+      result &= "aa"          # A
+      #result.add("aa")       # B
+      #result = result & "aa" # C
+
+  macro m(s:static[Td]) : untyped =
+      echo get_data(s)
+      echo get_data(s)
+      result = newEmptyNode()
+
+  const s = ("TT", 3)
+  m(s)
+  m(s)
+
+  # bug #933
+
+  proc nilcheck(): NimNode {.compileTime.} =
+    echo(result == nil) # true
+    echo(result.isNil) # true
+    echo(repr(result)) # nil
+
+  macro testnilcheck(): untyped =
+    result = newNimNode(nnkStmtList)
+    discard nilcheck()
+
+  testnilcheck()
+
+  # bug #1323
+
+  proc calc(): array[1, int] =
+    result[0].inc()
+    result[0].inc()
+
+  const c = calc()
+  doAssert c[0] == 2
+
+
+  # bug #3046
+
+  macro sampleMacroInt(i: int): untyped =
+    echo i.intVal
+
+  macro sampleMacroBool(b: bool): untyped =
+    echo b.boolVal
+
+  sampleMacroInt(42)
+  sampleMacroBool(false)
+  sampleMacroBool(system.true)
+
+
+# bug #11131
+macro toRendererBug(n): untyped =
+  result = newLit repr(n)
+
+echo toRendererBug(0o377'i8)
+echo toRendererBug(0o755'i32)
+
+# bug #12129
+macro foobar() =
+  var loopVars = newSeq[NimNode](5)
+  for i, sym in loopVars.mpairs():
+    sym = ident("i" & $i)
+  echo loopVars
+  for sym in loopVars.mitems():
+    sym = ident("tmp")
+  echo loopVars
+
+foobar()
+
+
+# bug #13253
+import macros
+
+type
+  FooBar = object
+    a: seq[int]
+
+macro genFoobar(a: static FooBar): untyped =
+  result = newStmtList()
+  for b in a.a:
+    result.add(newCall(bindSym"echo", newLit b))
+
+proc foobar(a: static FooBar) =
+  genFoobar(a)  # removing this make it work
+  for b in a.a:
+    echo "foo" & $b
+
+proc main() =
+  const a: seq[int] = @[1, 2,3]
+  # Error: type mismatch: got <array[0..2, int]> but expected 'seq[int]'
+  const fb = Foobar(a: a)
+  foobar(fb)
+main()
+
+# bug #13484
+
+proc defForward(id, nid: NimNode): NimNode =
+  result = newProc(id, @[newIdentNode("bool"), newIdentDefs(nid, newIdentNode("int"))], body=newEmptyNode())
+
+proc defEven(evenid, oddid, nid: NimNode): NimNode =
+  result = quote do:
+    proc `evenid`(`nid`: int): bool =
+      if `nid` == 0:
+        return true
+      else:
+        return `oddid`(`nid` - 1)
+
+proc defOdd(evenid, oddid, nid: NimNode): NimNode =
+  result = quote do:
+    proc `oddid`(`nid`: int): bool =
+      if `nid` == 0:
+        return false
+      else:
+        return `evenid`(`nid` - 1)
+
+proc callNode(funid, param: NimNode): NimNode =
+  result = quote do:
+    `funid`(`param`)
+
+macro testEvenOdd3(): untyped =
+  let
+    evenid = newIdentNode("even3")
+    oddid = newIdentNode("odd3")
+    nid = newIdentNode("n")
+    oddForward = defForward(oddid, nid)
+    even = defEven(evenid, oddid, nid)
+    odd = defOdd(evenid, oddid, nid)
+    callEven = callNode(evenid, newLit(42))
+    callOdd = callNode(oddid, newLit(42))
+  result = quote do:
+    `oddForward`
+    `even`
+    `odd`
+    echo `callEven`
+    echo `callOdd`
+
+macro testEvenOdd4(): untyped =
+  let
+    evenid = newIdentNode("even4")
+    oddid = newIdentNode("odd4")
+    nid = newIdentNode("n")
+    oddForward = defForward(oddid, nid)
+    even = defEven(evenid, oddid, nid)
+    odd = defOdd(evenid, oddid, nid)
+    callEven = callNode(evenid, newLit(42))
+    callOdd = callNode(oddid, newLit(42))
+  # rewrite the body of proc node.
+  oddForward[6] = newStmtList()
+  result = quote do:
+    `oddForward`
+    `even`
+    `odd`
+    echo `callEven`
+    echo `callOdd`
+
+macro testEvenOdd5(): untyped =
+  let
+    evenid = genSym(nskProc, "even5")
+    oddid = genSym(nskProc, "odd5")
+    nid = newIdentNode("n")
+    oddForward = defForward(oddid, nid)
+    even = defEven(evenid, oddid, nid)
+    odd = defOdd(evenid, oddid, nid)
+    callEven = callNode(evenid, newLit(42))
+    callOdd = callNode(oddid, newLit(42))
+  result = quote do:
+    `oddForward`
+    `even`
+    `odd`
+    echo `callEven`
+    echo `callOdd`
+
+macro testEvenOdd6(): untyped =
+  let
+    evenid = genSym(nskProc, "even6")
+    oddid = genSym(nskProc, "odd6")
+    nid = newIdentNode("n")
+    oddForward = defForward(oddid, nid)
+    even = defEven(evenid, oddid, nid)
+    odd = defOdd(evenid, oddid, nid)
+    callEven = callNode(evenid, newLit(42))
+    callOdd = callNode(oddid, newLit(42))
+  # rewrite the body of proc node.
+  oddForward[6] = newStmtList()
+  result = quote do:
+    `oddForward`
+    `even`
+    `odd`
+    echo `callEven`
+    echo `callOdd`
+
+# it works
+testEvenOdd3()
+
+# it causes an error (redefinition of odd4), which is correct
+assert not compiles testEvenOdd4()
+
+# it caused an error (still forwarded: odd5)
+testEvenOdd5()
+
+# it works, because the forward decl and definition share the symbol and the compiler is forgiving here
+#testEvenOdd6() #Don't test it though, the compiler may become more strict in the future
+
+# bug #15385
+var captured_funcs {.compileTime.}: seq[NimNode] = @[]
+
+macro aad*(fns: varargs[typed]): typed =
+  result = newStmtList()
+  for fn in fns:
+    captured_funcs.add fn[0]
+    result.add fn
+
+func exp*(x: float): float ## get different error if you remove forward declaration
+
+func exp*(x: float): float {.aad.} =
+  var x1 = min(max(x, -708.4), 709.8)
+  var result: float   ## looks weird because it is taken from template expansion
+  result = x1 + 1.0
+  result
+
+template check_accuracy(f: untyped, rng: Slice[float], n: int, verbose = false): auto =
+
+  proc check_accuracy: tuple[avg_ulp: float, max_ulp: int] {.gensym.} =
+    let k = (rng.b - rng.a) / (float) n
+    var
+      res, x: float
+      i, max_ulp = 0
+      avg_ulp = 0.0
+
+    x = rng.a
+    while (i < n):
+      res = f(x)
+      i.inc
+      x = x + 0.001
+    (avg_ulp, max_ulp)
+  check_accuracy()
+
+discard check_accuracy(exp, -730.0..709.4, 4)
+
+# And without forward decl
+macro aad2*(fns: varargs[typed]): typed =
+  result = newStmtList()
+  for fn in fns:
+    captured_funcs.add fn[0]
+    result.add fn
+
+func exp2*(x: float): float {.aad2.} =
+  var x1 = min(max(x, -708.4), 709.8)
+  var result: float   ## looks weird because it is taken from template expansion
+  result = x1 + 1.0
+  result
+
+template check_accuracy2(f: untyped, rng: Slice[float], n: int, verbose = false): auto =
+
+  proc check_accuracy2: tuple[avg_ulp: float, max_ulp: int] {.gensym.} =
+    let k = (rng.b - rng.a) / (float) n
+    var
+      res, x: float
+      i, max_ulp = 0
+      avg_ulp = 0.0
+
+    x = rng.a
+    while (i < n):
+      res = f(x)
+      i.inc
+      x = x + 0.001
+    (avg_ulp, max_ulp)
+  check_accuracy2()
+
+discard check_accuracy2(exp2, -730.0..709.4, 4)
+
+# And minimized:
+macro aadMin(fn: typed): typed = fn
+
+func expMin: float
+
+func expMin: float {.aadMin.} = 1
+
+echo expMin()
+
+
+# doubly-typed forward decls
+macro noop(x: typed) = x
+noop:
+  proc cally() = discard
+
+cally()
+
+noop:
+  proc barry()
+
+proc barry() = discard
+
+# some more:
+proc barry2() {.noop.}
+proc barry2() = discard
+
+proc barry3() {.noop.}
+proc barry3() {.noop.} = discard
+
+
+# issue #15389
+block double_sem_for_procs:
+
+  macro aad(fns: varargs[typed]): typed =
+    result = newStmtList()
+    for fn in fns:
+      result.add fn
+
+  func exp(x: float): float {.aad.} =
+    var x1 = min(max(x, -708.4), 709.8)
+    if x1 > 0.0:
+      return x1 + 1.0
+    result = 10.0
+
+  discard exp(5.0)
diff --git a/tests/macros/tmacros_various.nim b/tests/macros/tmacros_various.nim
new file mode 100644
index 000000000..e351b4527
--- /dev/null
+++ b/tests/macros/tmacros_various.nim
@@ -0,0 +1,391 @@
+discard """
+  nimout: '''
+Infix
+  Ident "=>"
+  Call
+    Ident "name"
+    Ident "a"
+    ExprColonExpr
+      Ident "b"
+      Ident "cint"
+  NilLit
+macrocache ok
+'''
+
+  output: '''
+x = 10
+x + y = 30
+proc foo[T, N: static[int]]()
+proc foo[T; N: static[int]]()
+a[0]: 42
+a[1]: 45
+x: some string
+([("key", "val"), ("keyB", "2")], [("val", "key"), ("2", "keyB")])
+([("key", "val"), ("keyB", "2")], [("val", "key"), ("2", "keyB")])
+0
+0
+0
+'''
+"""
+
+
+import macros, sugar, macrocache
+
+
+block tdump:
+  let
+    x = 10
+    y = 20
+  dump x
+  dump(x + y)
+
+
+block texprcolonexpr:
+  macro def(x): untyped =
+    echo treeRepr(x)
+
+  def name(a, b:cint) => nil
+
+
+
+block tgenericparams:
+  macro test():string =
+    let expr0 = "proc foo[T, N: static[int]]()"
+    let expr1 = "proc foo[T; N: static[int]]()"
+
+    newLit($toStrLit(parseExpr(expr0)) & "\n" & $toStrLit(parseExpr(expr1)))
+
+  echo test()
+
+
+
+block tidgen:
+  # Test compile-time state in same module
+  var gid {.compileTime.} = 3
+
+  macro genId(): int =
+    result = newIntLitNode(gid)
+    inc gid
+
+  proc Id1(): int {.compileTime.} = return genId()
+  proc Id2(): int {.compileTime.} = return genId()
+
+  doAssert Id1() == 3
+  doAssert Id2() == 4
+
+
+
+block tlexerex:
+  macro match(s: cstring|string; pos: int; sections: varargs[untyped]): untyped =
+    for sec in sections:
+      expectKind sec, nnkOfBranch
+      expectLen sec, 2
+    result = newStmtList()
+
+  var input = "the input"
+  var pos = 0
+  match input, pos:
+  of r"[a-zA-Z_]\w+": echo "an identifier"
+  of r"\d+": echo "an integer"
+  of r".": echo "something else"
+
+
+
+block tcopylineinfo:
+  # issue #5617, feature request
+  type Test = object
+
+  macro mixer(n: typed): untyped =
+    let x = newIdentNode("echo")
+    x.copyLineInfo(n)
+    result = newLit(x.lineInfo == n.lineInfo)
+
+  var z = mixer(Test)
+  doAssert z
+
+block tsetgetlineinfo:
+  # issue #21098, feature request
+  type Test = object
+
+  macro mixer1(n: typed): untyped =
+    let x = newIdentNode("echo")
+    var lineInfo = n.lineInfoObj
+    x.setLineInfo lineInfo
+    result = newLit(x.lineInfo == n.lineInfo)
+
+  macro mixer2(n: typed): untyped =
+    let x = newIdentNode("echo")
+    var lineInfo = n.lineInfoObj
+    lineInfo.line += 1
+    x.setLineInfo lineInfo
+    result = newLit(x.lineInfo != n.lineInfo)
+
+  doAssert mixer1(Test)
+
+  doAssert mixer2(Test)
+
+
+
+block tdebugstmt:
+  macro debug(n: varargs[untyped]): untyped =
+    result = newNimNode(nnkStmtList, n)
+    for i in 0..n.len-1:
+      add(result, newCall("write", newIdentNode("stdout"), toStrLit(n[i])))
+      add(result, newCall("write", newIdentNode("stdout"), newStrLitNode(": ")))
+      add(result, newCall("writeLine", newIdentNode("stdout"), n[i]))
+
+  var
+    a: array[0..10, int]
+    x = "some string"
+  a[0] = 42
+  a[1] = 45
+
+  debug(a[0], a[1], x)
+
+const
+  pairs = {"key": "val", "keyB": "2"}
+
+macro bilookups(arg: static[openArray[(string, string)]]): untyped =
+  var a = newTree(nnkBracket)
+  var b = newTree(nnkBracket)
+  for (k, v) in items(arg):
+    a.add(newTree(nnkTupleConstr, newLit k, newLit v))
+    b.add(newTree(nnkTupleConstr, newLit v, newLit k))
+  result = newTree(nnkTupleConstr, a, b)
+
+macro bilookups2(arg: untyped): untyped =
+  var a = newTree(nnkBracket)
+  var b = newTree(nnkBracket)
+  arg.expectKind(nnkTableConstr)
+  for x in items(arg):
+    x.expectKind(nnkExprColonExpr)
+    a.add(newTree(nnkTupleConstr, x[0], x[1]))
+    b.add(newTree(nnkTupleConstr, x[1], x[0]))
+  result = newTree(nnkTupleConstr, a, b)
+
+const cnst1 = bilookups(pairs)
+echo cnst1
+const cnst2 = bilookups2({"key": "val", "keyB": "2"})
+echo cnst2
+
+
+
+# macrocache #11404
+const
+  mcTable = CacheTable"nimTest"
+  mcSeq = CacheSeq"nimTest"
+  mcCounter = CacheCounter"nimTest"
+
+static:
+  doAssert(mcCounter.value == 0) # CacheCounter.value
+  mcCounter.inc                  # CacheCounter.inc
+  doAssert(mcCounter.value == 1) # CacheCounter.value
+
+  let a = newLit(1)
+  let b = newLit(2)
+  let c = newLit(3)
+  let d = newLit(4)
+
+  mcSeq.add a # CacheSeq.add
+  mcSeq.add b # CacheSeq.add
+  mcSeq.add c # CacheSeq.add
+
+  doAssert(mcSeq.len == 3)  # CacheSeq.len
+  #doAssert(c in mcSeq)      # CacheSeq.contains
+  #doAssert(d notin mcSeq)   # CacheSeq.contains
+
+  mcSeq.incl d              # CacheSeq.incl
+  doAssert(mcSeq.len == 4)  # CacheSeq.len
+
+  mcSeq.incl c              # CacheSeq.incl
+  doAssert(mcSeq.len == 4)  # CacheSeq.len
+
+  doAssert(mcSeq[3] == d)   # CacheSeq.[]
+
+  #doAssert(mcSeq.pop() == d)# CacheSeq.pop
+  #doAssert(mcSeq.len == 3)  # CacheSeq.len
+
+  doAssert(mcTable.len == 0)  # CacheTable.len
+  mcTable["a"] = a            # CacheTable.[]=
+  doAssert(mcTable.len == 1)  # CacheTable.len
+
+  doAssert(mcTable["a"] == a) # CacheTable.[]
+  #doAssert("a" in mcTable)    # CacheTable.contains
+  #doAssert(mcTable.hasKey("a"))# CacheTable.hasKey
+
+  for k, v in mcTable:  # CacheTable.items
+    doAssert(k == "a")
+    doAssert(v == a)
+
+  echo "macrocache ok"
+
+block tupleNewLitTests:
+  macro t(): untyped =
+    result = newLit (1, "foo", (), (1,), (a1: 'x', a2: @["ba"]))
+  doAssert $t() == """(1, "foo", (), (1,), (a1: 'x', a2: @["ba"]))"""
+    # this `$` test is needed because tuple equality doesn't distinguish
+    # between named vs unnamed tuples
+  doAssert t() == (1, "foo", (), (1, ), (a1: 'x', a2: @["ba"]))
+
+from strutils import contains
+block getImplTransformed:
+  macro bar(a: typed): string =
+    # newLit a.getImpl.repr # this would be before code transformation
+    let b = a.getImplTransformed
+    newLit b.repr
+  template toExpand() =
+    for ai in 0..2: echo ai
+  proc baz(a=1): int =
+    defer: discard
+    toExpand()
+    12
+  const code = bar(baz)
+  # sanity check:
+  doAssert "finally" in code # `defer` is lowered to try/finally
+  doAssert "while" in code # `for` is lowered to `while`
+  doAssert "toExpand" notin code
+    # template is expanded (but that would already be the case with
+    # `a.getImpl.repr`, unlike the other transformations mentioned above
+
+
+# test macro resemming
+macro makeVar(): untyped =
+  quote:
+    var tensorY {.inject.}: int
+
+macro noop(a: typed): untyped =
+  a
+
+noop:
+  makeVar
+echo tensorY
+
+macro xbenchmark(body: typed): untyped =
+  result = body
+
+xbenchmark:
+  proc fastSHA(inputtest: string) =
+    discard inputtest
+  fastSHA("hey")
+
+block: # issue #4547
+  macro lazy(stmtList : typed) : untyped =
+    let decl = stmtList[0]
+    decl.expectKind nnkLetSection
+    let name = decl[0][0].strVal
+    let call = decl[0][2].copy
+    call.expectKind nnkCall
+    let ident = newIdentNode("get" & name)
+    result = quote do:
+      var value : type(`call`)
+      proc `ident`() : type(`call`) =
+        if value.isNil:
+          value = `call`
+        value
+  type MyObject = object
+    a,b: int    
+  # this part, the macro call and it's result (written in the comment below) is important
+  lazy:
+    let y = new(MyObject)
+  #[
+    var value: type(new(MyObject))
+    proc gety(): type(new(MyObject)) =
+      if value.isNil:
+        value = new(MyObject)
+      value    
+  ]#
+  doAssert gety().a == 0  # works and should work
+  doAssert gety().b == 0  # works and should work
+  doAssert not declared(y)
+  doAssert not compiles(y.a)       # identifier y should not exist anymore
+  doAssert not compiles(y.b)       # identifier y should not exist anymore
+
+block: # bug #13511
+  type
+    Builder = ref object
+      components: seq[Component]
+    Component = object
+
+  proc add(builder: var Builder, component: Component) {.compileTime.} =
+    builder.components.add(component)
+
+  macro debugAst(arg: typed): untyped =
+    ## just for debugging purpose.
+    discard arg.treeRepr
+    return arg
+
+  static:
+    var component = Component()
+    var builder = Builder()
+
+    template foo(): untyped =
+      ## WAS: this doc comment causes compilation failure.
+      builder
+
+    debugAst:
+      add(foo(), component)
+
+block: # bug #15118
+  macro flop(name: static string) =
+    let id = genSym(nskType, "env")
+    let r =
+      nnkStmtList.newTree(
+        nnkTypeSection.newTree(
+          nnkTypeDef.newTree(
+            id,
+            newEmptyNode(),
+            nnkRefTy.newTree(
+              nnkObjectTy.newTree(
+                newEmptyNode(),
+                newEmptyNode(),
+                nnkRecList.newTree(
+                  nnkIdentDefs.newTree(
+                    newIdentNode(name),
+                    newIdentNode("int"),
+                    newEmptyNode()
+                  )
+                )
+              ) 
+            )
+          )
+        ),
+
+        # var f: T
+  
+        nnkVarSection.newTree(
+          nnkIdentDefs.newTree(
+            newIdentNode("f"),
+            id,
+            newEmptyNode()
+          )
+        ),
+
+        # echo f.a
+        nnkCommand.newTree(
+          newIdentNode("new"),
+          newIdentNode("f")
+        ),
+
+        nnkCommand.newTree(
+          newIdentNode("echo"),
+          nnkDotExpr.newTree(
+            newIdentNode("f"),
+            newIdentNode(name)
+          )
+        )
+      )
+    r
+
+
+  block:
+    flop("a")
+
+  block:
+    flop("b")
+
+static:
+  block:
+    const containsTable = CacheTable"containsTable"
+    doAssert "foo" notin containsTable
+    containsTable["foo"] = newLit 42
+    doAssert "foo" in containsTable
diff --git a/tests/macros/tmacrostmt.nim b/tests/macros/tmacrostmt.nim
index 849a32ea3..817bc8352 100644
--- a/tests/macros/tmacrostmt.nim
+++ b/tests/macros/tmacrostmt.nim
@@ -18,9 +18,138 @@ case_token: inc i
 
 #bug #488
 
-macro foo: typed =
+macro foo() =
   var exp = newCall("whatwhat", newIntLitNode(1))
-  if compiles(getAst(exp)): return exp
+  if compiles(getAst(exp)):
+    return exp
   else: echo "Does not compute! (test OK)"
 
 foo()
+
+#------------------------------------
+# bug #8287
+type MyString = distinct string
+
+proc `$` (c: MyString): string {.borrow.}
+
+proc `!!` (c: cstring): int =
+  c.len
+
+proc f(name: MyString): int =
+  !! $ name
+
+macro repr_and_parse(fn: typed) =
+  let fn_impl = fn.getImpl
+  fn_impl.name = genSym(nskProc, $fn_impl.name)
+  echo fn_impl.repr
+  result = parseStmt(fn_impl.repr)
+
+macro repr_to_string(fn: typed): string =
+  let fn_impl = fn.getImpl
+  result = newStrLitNode(fn_impl.repr)
+
+repr_and_parse(f)
+
+
+#------------------------------------
+# bugs #8343 and #8344
+proc one_if_proc(x, y : int): int =
+  if x < y: result = x
+  else: result = y
+
+proc test_block(x, y : int): int =
+  block label:
+    result = x
+    result = y
+
+#------------------------------------
+# bugs #8348
+
+template `>`(x, y: untyped): untyped =
+  ## "is greater" operator. This is the same as ``y < x``.
+  y < x
+
+proc test_cond_stmtlist(x, y: int): int =
+  result = x
+  if x > y:
+    result = x
+
+
+#------------------------------------
+# bug #8762
+proc t2(a, b: int): int =
+  `+`(a, b)
+
+
+#------------------------------------
+# bug #8761
+
+proc fn1(x, y: int):int =
+  2 * (x + y)
+
+proc fn2(x, y: float): float =
+  (y + 2 * x) / (x - y)
+
+proc fn3(x, y: int): bool =
+  (((x and 3) div 4) or (x mod (y xor -1))) == 0 or y notin [1,2]
+
+proc fn4(x: int): int =
+  if x mod 2 == 0: return x + 2
+  else: return 0
+
+proc fn5(a, b: float): float =
+  result = - a * a / (b * b)
+
+proc `{}`(x: seq[float], i: int, j: int): float = 
+  x[i + 0 * j]
+
+proc `{}=`(x: var seq[float], i: int, val: float) = 
+  x[i] = val
+
+proc fn6() =
+  var a = @[1.0, 2.0]
+  let z = a{0, 1} 
+  a{2} = 5.0
+
+
+#------------------------------------
+# bug #10807
+proc fn_unsafeaddr(x: int): int =
+  cast[int](unsafeAddr(x))
+
+static:
+  let fn1s = "proc fn1(x, y: int): int =\n  result = 2 * (x + y)\n"
+  let fn2s = "proc fn2(x, y: float): float =\n  result = (y + 2 * x) / (x - y)\n"
+  let fn3s = "proc fn3(x, y: int): bool =\n  result = ((x and 3) div 4 or x mod (y xor -1)) == 0 or not contains([1, 2], y)\n"
+  let fn4s = "proc fn4(x: int): int =\n  if x mod 2 == 0:\n    return x + 2\n  else:\n    return 0\n"
+  let fn5s = "proc fn5(a, b: float): float =\n  result = -a * a / (b * b)\n"
+  let fn6s = "proc fn6() =\n  var a = @[1.0, 2.0]\n  let z = a{0, 1}\n  a{2} = 5.0\n"
+  let fnAddr = "proc fn_unsafeaddr(x: int): int =\n  result = cast[int](addr(x))\n"
+
+  doAssert fn1.repr_to_string == fn1s
+  doAssert fn2.repr_to_string == fn2s
+  doAssert fn3.repr_to_string == fn3s
+  doAssert fn4.repr_to_string == fn4s
+  doAssert fn5.repr_to_string == fn5s
+  doAssert fn6.repr_to_string == fn6s
+  doAssert fn_unsafeaddr.repr_to_string == fnAddr
+
+#------------------------------------
+# bug #8763
+
+type
+  A {.pure.} = enum
+    X, Y
+  B {.pure.} = enum
+    X, Y
+
+proc test_pure_enums(a: B) =
+  case a
+    of B.X: echo B.X
+    of B.Y: echo B.Y
+
+repr_and_parse(one_if_proc)
+repr_and_parse(test_block)
+repr_and_parse(test_cond_stmtlist)
+repr_and_parse(t2)
+repr_and_parse(test_pure_enums)
diff --git a/tests/macros/tmacrotypes.nim b/tests/macros/tmacrotypes.nim
index e8a68c34d..13b421303 100644
--- a/tests/macros/tmacrotypes.nim
+++ b/tests/macros/tmacrotypes.nim
@@ -1,16 +1,157 @@
 discard """
-  nimout: '''void
-int'''
+  nimout: '''intProc; ntyProc; proc[int, int, float]; proc (a: int; b: float): int {.nimcall.}
+void; ntyVoid; void; void
+int; ntyInt; int; int
+proc () {.nimcall.}; ntyProc; proc[void]; proc () {.nimcall.}
+voidProc; ntyProc; proc[void]; proc () {.nimcall.}
+listing fields for ObjType
+a: string
+b: int
+listing fields for ObjRef
+skipping ref type
+a: string
+b: int
+listing fields for RefType
+skipping ref type
+a: int
+b: float
+listing fields for typeof(a)
+skipping ref type
+a: string
+b: int
+listing fields for typeof(b)
+skipping ref type
+a: string
+b: int
+listing fields for typeof(c)
+skipping ref type
+a: int
+b: float
+listing fields for typeof(x)
+a: string
+b: int
+listing fields for typeof(x)
+a: int
+b: float
+typeDesc[range[1 .. 5]]; ntyTypeDesc; typeDesc[range[1, 5]]; typeDesc[range[1 .. 5]]
+typeDesc[range]; ntyTypeDesc; typeDesc[range[T]]; typeDesc[range]'''
 """
 
-import macros
+import macros, typetraits
 
-macro checkType(ex: typed; expected: string): untyped =
-  var t = ex.getType()
-  echo t
+macro checkType(ex: typed): untyped =
+  echo ex.getTypeInst.repr, "; ", ex.typeKind, "; ", ex.getType.repr, "; ", ex.getTypeImpl.repr
+
+macro checkProcType(fn: typed): untyped =
+  if fn.kind == nnkProcDef:
+    result = fn
+  let fn_sym = if fn.kind == nnkProcDef: fn[0] else: fn
+  echo fn_sym, "; ", fn_sym.typeKind, "; ", fn_sym.getType.repr, "; ", fn_sym.getTypeImpl.repr
 
 proc voidProc = echo "hello"
-proc intProc(a: int, b: float): int = 10
+proc intProc(a: int, b: float): int {.checkProcType.} = 10
+
+checkType(voidProc())
+checkType(intProc(10, 20.0))
+checkType(voidProc)
+checkProcType(voidProc)
+
+macro listFields(T: typed) =
+  echo "listing fields for ", repr(T)
+  let inputExprType = getType(T)
+
+  var objType = inputExprType[1]
+  if objType.kind == nnkBracketExpr and objType.len > 1:
+    if ((objType[0].kind == nnkRefTy) or
+        (objType[0].kind == nnkSym and eqIdent(objType[0], "ref"))):
+      echo "skipping ref type"
+      objType = objType[1]
+
+  let typeAst = objType.getImpl
+
+  var objectDef = typeAst[2]
+  if objectDef.kind == nnkRefTy:
+    objectDef = objectDef[0]
+
+  let recList = objectDef[2]
+  for rec in recList:
+    echo $rec[0], ": ", $rec[1]
+
+type
+  ObjType* = object of RootObj
+    a: string
+    b: int
+
+  ObjRef = ref ObjType
+
+  RefType* = ref object of RootObj
+    a: int
+    b: float
+
+listFields ObjType
+listFields ObjRef
+listFields RefType
+
+let
+  a = new ObjType
+  b = new ObjRef
+  c = new RefType
+
+listFields typeOf(a)
+listFields typeOf(b)
+listFields typeOf(c)
+
+proc genericProc(x: object) =
+  listFields typeOf(x)
+
+genericProc a[]
+genericProc b[]
+genericProc c[]
+
+# bug #10548
+block:
+  var c {.compileTime.} = 0
+
+  macro meshImpl(arg: typed): untyped =
+    inc c
+    result = arg
+
+  type
+    Blub = int32
+    Mesh = meshImpl(Club)
+    Club = Blub
+
+  static: doAssert(c == 1)
+
+# bug #10702
+type
+  VectorElementType = SomeNumber | bool
+  Vec*[N : static[int], T: VectorElementType] = object
+    arr*: array[N, T]
+
+type
+  Vec4*[T: VectorElementType] = Vec[4,T]
+  Vec3*[T: VectorElementType] = Vec[3,T]
+  Vec2*[T: VectorElementType] = Vec[2,T]
+
+template vecGen(U:untyped,V:typed):typed=
+  ## ``U`` suffix
+  ## ``V`` valType
+  ##
+  type
+    `Vec2 U`* {.inject.} = Vec2[V]
+    `Vec3 U`* {.inject.} = Vec3[V]
+    `Vec4 U`* {.inject.} = Vec4[V]
+
+vecGen(f, float32)
+
+macro foobar(arg: typed): untyped =
+  let typ = arg.getTypeInst
+  doAssert typ.getImpl[^1].kind == nnkCall
+
+var x: Vec2f
+
+foobar(x)
 
-checkType(voidProc(), "void")
-checkType(intProc(10, 20.0), "int")
+checkType(range[1..5])
+checkType(range)
diff --git a/tests/macros/tmemit.nim b/tests/macros/tmemit.nim
index 3a3b8734b..6c9f9f935 100644
--- a/tests/macros/tmemit.nim
+++ b/tests/macros/tmemit.nim
@@ -1,13 +1,12 @@
 discard """
-  output: '''HELLO WORLD
+  output: '''
 c_func
-12'''
+12
+'''
 """
 
 import macros, strutils
 
-emit("echo " & '"' & "hello world".toUpperAscii & '"')
-
 # bug #1025
 
 macro foo(icname): untyped =
diff --git a/tests/macros/tmsginfo.nim b/tests/macros/tmsginfo.nim
new file mode 100644
index 000000000..f08a231fb
--- /dev/null
+++ b/tests/macros/tmsginfo.nim
@@ -0,0 +1,24 @@
+discard """
+  nimout: '''tmsginfo.nim(21, 1) Warning: foo1 [User]
+tmsginfo.nim(22, 13) template/generic instantiation of `foo2` from here
+tmsginfo.nim(15, 10) Warning: foo2 [User]
+tmsginfo.nim(23, 1) Hint: foo3 [User]
+tmsginfo.nim(19, 7) Hint: foo4 [User]
+'''
+"""
+
+import macros
+
+macro foo1(y: untyped): untyped =
+  warning("foo1", y)
+macro foo2(y: untyped): untyped =
+  warning("foo2")
+macro foo3(y: untyped): untyped =
+  hint("foo3", y)
+macro foo4(y: untyped): untyped =
+  hint("foo4")
+
+proc x1() {.foo1.} = discard
+proc x2() {.foo2.} = discard
+proc x3() {.foo3.} = discard
+proc x4() {.foo4.} = discard
diff --git a/tests/macros/tnewlit.nim b/tests/macros/tnewlit.nim
index 3ba1e09e1..70683f880 100644
--- a/tests/macros/tnewlit.nim
+++ b/tests/macros/tnewlit.nim
@@ -5,6 +5,14 @@ type
     a : int
     b : string
 
+  RefObject = ref object
+    x: int
+
+  RegularObject = object
+    x: int
+
+  ObjectRefAlias = ref RegularObject
+
 macro test_newLit_MyType: untyped =
   let mt = MyType(a: 123, b:"foobar")
   result = newLit(mt)
@@ -147,3 +155,40 @@ block:
   # x needs to be of type seq[string]
   var x = test_newLit_empty_seq_string
   x.add("xyz")
+
+type
+  MyEnum = enum
+    meA
+    meB
+
+macro test_newLit_Enum: untyped =
+  result = newLit(meA)
+
+block:
+  let tmp: MyEnum = meA
+  doAssert tmp == test_newLit_Enum
+
+macro test_newLit_set: untyped =
+  let myset = {MyEnum.low .. MyEnum.high}
+  result = newLit(myset)
+
+block:
+  let tmp: set[MyEnum] = {MyEnum.low .. MyEnum.high}
+  doAssert tmp == test_newLit_set
+
+macro test_newLit_ref_object: untyped =
+  var x = RefObject(x: 10)
+  return newLit(x)
+
+block:
+  let x = test_newLit_ref_object()
+  doAssert $(x[]) == "(x: 10)"
+
+macro test_newLit_object_ref_alias: untyped =
+  var x = ObjectRefAlias(x: 10)
+  return newLit(x)
+
+block:
+  let x = test_newLit_object_ref_alias()
+  doAssert $(x[]) == "(x: 10)"
+
diff --git a/tests/macros/tnewproc.nim b/tests/macros/tnewproc.nim
new file mode 100644
index 000000000..a5bfd6dca
--- /dev/null
+++ b/tests/macros/tnewproc.nim
@@ -0,0 +1,51 @@
+import macros
+
+macro test(a: untyped): untyped =
+  # proc hello*(x: int = 3, y: float32): int {.inline.} = discard
+  let
+    nameNode = nnkPostfix.newTree(
+      newIdentNode("*"),
+      newIdentNode("hello")
+    )
+    params = @[
+      newIdentNode("int"),
+      nnkIdentDefs.newTree(
+        newIdentNode("x"),
+        newIdentNode("int"),
+        newLit(3)
+      ),
+      nnkIdentDefs.newTree(
+        newIdentNode("y"),
+        newIdentNode("float32"),
+        newEmptyNode()
+      )
+    ]
+    paramsNode = nnkFormalParams.newTree(params)
+    pragmasNode = nnkPragma.newTree(
+      newIdentNode("inline")
+    )
+    bodyNode = nnkStmtList.newTree(
+      nnkDiscardStmt.newTree(
+        newEmptyNode()
+      )
+    )
+
+  var
+    expected = nnkProcDef.newTree(
+      nameNode,
+      newEmptyNode(),
+      newEmptyNode(),
+      paramsNode,
+      pragmasNode,
+      newEmptyNode(),
+      bodyNode
+    )
+
+  doAssert expected == newProc(name=nameNode, params=params,
+                                    body = bodyNode, pragmas=pragmasNode)
+  expected.pragma = newEmptyNode()
+  doAssert expected == newProc(name=nameNode, params=params,
+                                    body = bodyNode)
+
+test:
+  42
diff --git a/tests/macros/tparsefile.nim b/tests/macros/tparsefile.nim
new file mode 100644
index 000000000..a41223f80
--- /dev/null
+++ b/tests/macros/tparsefile.nim
@@ -0,0 +1,11 @@
+import macros
+
+static:
+  let fn = "mparsefile.nim"
+  var raised = false
+  try:
+    discard parseStmt(staticRead(fn), filename = fn)
+  except ValueError as e:
+    raised = true
+    doAssert e.msg == "mparsefile.nim(4, 1) Error: invalid indentation"
+  doAssert raised
diff --git a/tests/macros/tprintf.nim b/tests/macros/tprintf.nim
deleted file mode 100644
index 94cbfbc2b..000000000
--- a/tests/macros/tprintf.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-discard """
-  file: "tprintf.nim"
-  output: "Andreas Rumpf"
-"""
-# Test a printf proc
-
-proc printf(file: TFile, args: openarray[string]) =
-  var i = 0
-  while i < args.len:
-    write(file, args[i])
-    inc(i)
-
-printf(stdout, ["Andreas ", "Rumpf\n"])
-#OUT Andreas Rumpf
-
-
diff --git a/tests/macros/tprocgettype.nim b/tests/macros/tprocgettype.nim
new file mode 100644
index 000000000..0c1cc4270
--- /dev/null
+++ b/tests/macros/tprocgettype.nim
@@ -0,0 +1,28 @@
+discard """
+  nimout: '''
+var x: proc () {.cdecl.} = foo
+var x: iterator (): int {.closure.} = bar
+'''
+"""
+
+# issue #19010
+
+import macros
+
+macro createVar(x: typed): untyped =
+  result = nnkVarSection.newTree:
+    newIdentDefs(ident"x", getTypeInst(x), copy(x))
+  
+  echo repr result
+
+block:
+  proc foo() {.cdecl.} = discard
+
+  createVar(foo)
+  x()
+
+block:
+  iterator bar(): int {.closure.} = discard
+
+  createVar(bar)
+  for a in x(): discard
diff --git a/tests/macros/tprochelpers.nim b/tests/macros/tprochelpers.nim
new file mode 100644
index 000000000..d95a2ced8
--- /dev/null
+++ b/tests/macros/tprochelpers.nim
@@ -0,0 +1,22 @@
+import std/macros
+import stdtest/testutils
+
+macro test1(prc: untyped): untyped =
+  assertAll:
+    prc.params.len == 2
+    prc.params[1].len == 4
+    prc.pragma.len == 2
+
+  prc.params = nnkFormalParams.newTree(
+    ident("int")
+  )
+  prc.pragma = newEmptyNode()
+
+  assertAll:
+    prc.params.len == 1
+    prc.pragma.len == 0
+  prc
+
+proc test(a, b: int): int {.gcsafe, raises: [], test1.} = 5
+
+type hello = proc(a, b: int): int {.gcsafe, raises: [], test1.}
diff --git a/tests/macros/tquotedo.nim b/tests/macros/tquotedo.nim
new file mode 100644
index 000000000..6acb8ef4e
--- /dev/null
+++ b/tests/macros/tquotedo.nim
@@ -0,0 +1,51 @@
+discard """
+output: '''
+123
+Hallo Welt
+Hallo Welt
+1
+()
+'''
+"""
+
+import macros
+
+macro mac(): untyped =
+  quote do:
+    proc test(): int =
+      (proc(): int = result = 123)()
+
+mac()
+echo test()
+
+macro foobar(arg: untyped): untyped =
+  result = arg
+  result.add quote do:
+    `result`
+
+foobar:
+  echo "Hallo Welt"
+
+# bug #3744
+import macros
+macro t(): untyped =
+  return quote do:
+    proc tp(): int =
+      result = 1
+t()
+
+echo tp()
+
+
+# https://github.com/nim-lang/Nim/issues/9866
+type
+  # Foo = int # works
+  Foo = object # fails
+
+macro dispatchGen(): untyped =
+  var shOpt: Foo
+  result = quote do:
+    let baz = `shOpt`
+    echo `shOpt`
+
+dispatchGen()
diff --git a/tests/macros/tquotewords.nim b/tests/macros/tquotewords.nim
index fa00ba9de..e718d25f9 100644
--- a/tests/macros/tquotewords.nim
+++ b/tests/macros/tquotewords.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tquotewords.nim"
   output: "thisanexample"
 """
 # Test an idea I recently had:
diff --git a/tests/macros/trecmacro.nim b/tests/macros/trecmacro.nim
index 69ebe3da3..d804178bc 100644
--- a/tests/macros/trecmacro.nim
+++ b/tests/macros/trecmacro.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "recursive dependency: 'dump'"
   file: "trecmacro.nim"
   line: 8
-  errormsg: "recursive dependency: 'dump'"
 """
 
 macro dump(n: untyped): untyped =
diff --git a/tests/macros/tsame_name_497.nim b/tests/macros/tsame_name_497.nim
index ed5d5c6d8..9f26a4024 100644
--- a/tests/macros/tsame_name_497.nim
+++ b/tests/macros/tsame_name_497.nim
@@ -1,6 +1,3 @@
-discard """
-  disabled: true
-"""
 
 import macro_bug
 
diff --git a/tests/macros/tsametype.nim b/tests/macros/tsametype.nim
index 865fb4cb8..5219a3767 100644
--- a/tests/macros/tsametype.nim
+++ b/tests/macros/tsametype.nim
@@ -9,6 +9,7 @@ true
 false
 true
 false'''
+joinable: false
 """
 
 import macros
diff --git a/tests/macros/tslice.nim b/tests/macros/tslice.nim
new file mode 100644
index 000000000..c64289ec6
--- /dev/null
+++ b/tests/macros/tslice.nim
@@ -0,0 +1,38 @@
+import macros
+
+macro test(): untyped =
+  result = nnkStmtList.newTree()
+  let n = nnkStmtList.newTree(
+    newIdentNode("one"),
+    newIdentNode("two"),
+    newIdentNode("three"),
+    newIdentNode("four"),
+    newIdentNode("five"),
+    newIdentNode("six")
+  )
+
+  var i = 1
+  for x in n[1 .. ^2]:
+    assert x == n[i]
+    i.inc
+  assert i == 5
+
+  i = 3
+  for x in n[3..^1]:
+    assert x == n[i]
+    i.inc
+  assert i == 6
+
+  i = 0
+  for x in n[0..3]:
+    assert x == n[i]
+    i.inc
+  assert i == 4
+
+  i = 0
+  for x in n[0..5]:
+    assert x == n[i]
+    i.inc
+  assert i == 6
+
+test()
diff --git a/tests/macros/tstaticparamsmacro.nim b/tests/macros/tstaticparamsmacro.nim
index ea59936e0..2632ca730 100644
--- a/tests/macros/tstaticparamsmacro.nim
+++ b/tests/macros/tstaticparamsmacro.nim
@@ -1,14 +1,15 @@
 discard """
-  msg: '''letters
+  nimout: '''
+letters
 aa
 bb
 numbers
 11
 22
 AST a
-[(11, 22), (33, 44)]
+@[(c: 11, d: 22), (c: 33, d: 44)]
 AST b
-(e: [55, 66], f: [77, 88])
+(e: @[55, 66], f: @[77, 88])
 55
 10
 20Test
@@ -44,10 +45,10 @@ const
   b : Tb = (@[55,66], @[77, 88])
 
 macro mA(data: static[Ta]): untyped =
-  echo "AST a \n", repr(data)
+  echo "AST a\n", repr(data)
 
 macro mB(data: static[Tb]): untyped =
-  echo "AST b \n", repr(data)
+  echo "AST b\n", repr(data)
   echo data.e[0]
 
 mA(a)
diff --git a/tests/macros/tstringinterp.nim b/tests/macros/tstringinterp.nim
index 305f40ac5..74c73599b 100644
--- a/tests/macros/tstringinterp.nim
+++ b/tests/macros/tstringinterp.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tstringinterp.nim"
   output: "Hello Alice, 64 | Hello Bob, 10$"
 """
 
@@ -71,3 +70,4 @@ var
   s2 = formatStyleInterpolation"Hello ${bob}, ${sum(alice.len, bob.len, 2)}$$"
 
 write(stdout, s1 & " | " & s2)
+write(stdout, "\n")
diff --git a/tests/macros/tstructuredlogging.nim b/tests/macros/tstructuredlogging.nim
index 05bb52a40..649c6c0bd 100644
--- a/tests/macros/tstructuredlogging.nim
+++ b/tests/macros/tstructuredlogging.nim
@@ -1,7 +1,7 @@
 discard """
 output: '''
 main started: a=10, b=inner-b, c=10, d=some-d, x=16, z=20
-exiting: a=12, b=overriden-b, c=100, msg=bye bye, x=16
+exiting: a=12, b=overridden-b, c=100, msg=bye bye, x=16
 '''
 """
 
@@ -66,7 +66,7 @@ macro mergeScopes(scopeHolders: typed, newBindings: untyped): untyped =
     newScopeDefinition.add newAssignment(newIdentNode(k), v)
 
   result = quote:
-    template scopeHolder = `newScopeDefinition`
+    template scopeHolder {.redefine.} = `newScopeDefinition`
 
 template scope(newBindings: untyped) {.dirty.} =
   mergeScopes(bindSym"scopeHolder", newBindings)
@@ -134,7 +134,7 @@ scope:
 
 scope:
   x = 16
-  b = "overriden-b"
+  b = "overridden-b"
 
 scope:
   c = 100
diff --git a/tests/macros/ttemplatesymbols.nim b/tests/macros/ttemplatesymbols.nim
index 8d9c9ec02..3182de79d 100644
--- a/tests/macros/ttemplatesymbols.nim
+++ b/tests/macros/ttemplatesymbols.nim
@@ -122,7 +122,7 @@ template overloadedTemplate(x: string) =
 """
 
 inspectUntyped bindSym("overloadedTemplate"), """bindSym("overloadedTemplate")"""
-  # binSym is active only in the presense of `typed` params.
+  # binSym is active only in the presence of `typed` params.
   # `untyped` params still get the raw AST
 
 inspectSymbol normalProc, """
@@ -148,8 +148,6 @@ proc overloadedProc[T](x: T) =
   echo x
 
 """
-  # XXX: There seems to be a repr rendering problem above.
-  # Notice that `echo [x]`
 
 inspectSymbol overloadedProc[float], """
 proc overloadedProc(x: T) =
diff --git a/tests/macros/ttryparseexpr.nim b/tests/macros/ttryparseexpr.nim
index 54442b662..e6e9e9880 100644
--- a/tests/macros/ttryparseexpr.nim
+++ b/tests/macros/ttryparseexpr.nim
@@ -1,5 +1,5 @@
 discard """
-  outputsub: '''Error: invalid indentation 45'''
+  outputsub: '''Error: expression expected, but found '[EOF]' 45'''
 """
 
 # feature request #1473
@@ -18,3 +18,7 @@ const
   c = test("\"") # bug #2504
 
 echo a, " ", b
+
+static:
+  # Issue #9918
+  discard parseStmt("echo(1+1);")
diff --git a/tests/macros/ttypenodes.nim b/tests/macros/ttypenodes.nim
new file mode 100644
index 000000000..233ea9780
--- /dev/null
+++ b/tests/macros/ttypenodes.nim
@@ -0,0 +1,16 @@
+import macros
+
+macro makeEnum(): untyped =
+  newTree(nnkEnumTy, newEmptyNode(), ident"a", ident"b", ident"c")
+
+macro makeObject(): untyped =
+  newTree(nnkObjectTy, newEmptyNode(), newEmptyNode(), newTree(nnkRecList,
+    newTree(nnkIdentDefs, ident"x", ident"y", ident"int", newEmptyNode())))
+
+type
+  Foo = makeEnum()
+  Bar = makeObject()
+
+doAssert {a, b, c} is set[Foo]
+let bar = Bar(x: 3, y: 4)
+doAssert (bar.x, bar.y) == (3, 4)
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/macros/tvarnimnode.nim b/tests/macros/tvarnimnode.nim
deleted file mode 100644
index 26c9b1f1a..000000000
--- a/tests/macros/tvarnimnode.nim
+++ /dev/null
@@ -1,19 +0,0 @@
-discard """
-  output: 10
-"""
-
-#bug #926
-
-import macros
-
-proc test(f: var NimNode) {.compileTime.} =
-  f = newNimNode(nnkStmtList)
-  f.add newCall(newIdentNode("echo"), newLit(10))
-
-macro blah(prc: untyped): untyped =
-  result = prc
-
-  test(result)
-
-proc test() {.blah.} =
-  echo 5
diff --git a/tests/macros/tvtable.nim b/tests/macros/tvtable.nim
index dc07a726b..0f322b7d5 100644
--- a/tests/macros/tvtable.nim
+++ b/tests/macros/tvtable.nim
@@ -18,7 +18,7 @@ type
   # it's also possible to use a strongly typed tuple here
   VTable = array[0..1, pointer]
 
-  TBase = object {.inheritable.}
+  TBase {.inheritable.} = object
     vtbl: ptr VTable
 
   TUserObject1 = object of TBase
diff --git a/tests/macros/typesafeprintf.nim b/tests/macros/typesafeprintf.nim
index 2f4622f3e..daf213bd3 100644
--- a/tests/macros/typesafeprintf.nim
+++ b/tests/macros/typesafeprintf.nim
@@ -9,7 +9,7 @@ proc printfImpl(formatstr: cstring) {.importc: "printf", varargs.}
 
 iterator tokenize(format: string): char =
   var i = 0
-  while true:
+  while i < format.len:
     case format[i]
     of '%':
       case format[i+1]
@@ -42,7 +42,8 @@ macro printf(formatString: string{lit}, args: varargs[typed]): untyped =
             $expectedType & ", actual type: " & $actualType
 
   # keep the original callsite, but use cprintf instead
-  result = callsite()
-  result[0] = bindSym"printfImpl"
+  result = newCall(bindSym"printfImpl")
+  result.add formatString
+  for a in args: result.add a
 
 printf("test %d\n", 10)
diff --git a/tests/macros/typesapi.nim b/tests/macros/typesapi.nim
deleted file mode 100644
index cdbfc0ad2..000000000
--- a/tests/macros/typesapi.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-discard """
-  nimout: '''proc (x: int): string => typeDesc[proc[string, int]]
-proc (x: int): void => typeDesc[proc[void, int]]
-proc (x: int) => typeDesc[proc[void, int]]'''
-"""
-
-#2211
-
-import macros
-
-macro showType(t:typed): untyped =
-  let ty = t.getType
-  echo t.repr, " => ", ty.repr
-
-showType(proc(x:int): string)
-showType(proc(x:int): void)
-showType(proc(x:int))
diff --git a/tests/magics/tlowhigh.nim b/tests/magics/tlowhigh.nim
deleted file mode 100644
index 60ed13c99..000000000
--- a/tests/magics/tlowhigh.nim
+++ /dev/null
@@ -1,24 +0,0 @@
-discard """
-  file: "tlowhigh.nim"
-  output: "10"
-"""
-# Test the magic low() and high() procs
-
-type
-  myEnum = enum e1, e2, e3, e4, e5
-
-var
-  a: array[myEnum, int]
-
-for i in low(a) .. high(a):
-  a[i] = 0
-
-proc sum(a: openarray[int]): int =
-  result = 0
-  for i in low(a)..high(a):
-    inc(result, a[i])
-
-write(stdout, sum([1, 2, 3, 4]))
-#OUT 10
-
-
diff --git a/tests/manyloc/argument_parser/argument_parser.nim b/tests/manyloc/argument_parser/argument_parser.nim
index 1095a893e..0ad57167b 100644
--- a/tests/manyloc/argument_parser/argument_parser.nim
+++ b/tests/manyloc/argument_parser/argument_parser.nim
@@ -74,14 +74,14 @@ type
     ## nothing prevents you from accessing directly the type of field you want
     ## if you expect only one kind.
     case kind*: Tparam_kind
-    of PK_EMPTY: nil
+    of PK_EMPTY: discard
     of PK_INT: int_val*: int
     of PK_BIGGEST_INT: big_int_val*: BiggestInt
     of PK_FLOAT: float_val*: float
     of PK_BIGGEST_FLOAT: big_float_val*: BiggestFloat
     of PK_STRING: str_val*: string
     of PK_BOOL: bool_val*: bool
-    of PK_HELP: nil
+    of PK_HELP: discard
 
   Tcommandline_results* = object of RootObj ## \
     ## Contains the results of the parsing.
@@ -102,7 +102,7 @@ type
 
 # - Tparam_kind procs
 
-proc `$`*(value: Tparam_kind): string {.procvar.} =
+proc `$`*(value: Tparam_kind): string =
   ## Stringifies the type, used to generate help texts.
   case value:
   of PK_EMPTY: result = ""
@@ -137,7 +137,7 @@ proc new_parameter_specification*(consumes = PK_EMPTY,
 
 # - Tparsed_parameter procs
 
-proc `$`*(data: Tparsed_parameter): string {.procvar.} =
+proc `$`*(data: Tparsed_parameter): string =
   ## Stringifies the value, mostly for debug purposes.
   ##
   ## The proc will display the value followed by non string type in brackets.
@@ -168,14 +168,14 @@ template new_parsed_parameter*(tkind: Tparam_kind, expr): Tparsed_parameter =
   ## initialised with. The template figures out at compile time what field to
   ## assign the variable to, and thus you reduce code clutter and may use this
   ## to initialise single assignments variables in `let` blocks. Example:
-  ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   let
   ##     parsed_param1 = new_parsed_parameter(PK_FLOAT, 3.41)
   ##     parsed_param2 = new_parsed_parameter(PK_BIGGEST_INT, 2358123 * 23123)
   ##     # The following line doesn't compile due to
   ##     # type mismatch: got <string> but expected 'int'
   ##     #parsed_param3 = new_parsed_parameter(PK_INT, "231")
+  ##   ```
   var result {.gensym.}: Tparsed_parameter
   result.kind = tkind
   when tkind == PK_EMPTY: discard
@@ -225,14 +225,14 @@ template raise_or_quit(exception, message: untyped) =
 
 template run_custom_proc(parsed_parameter: Tparsed_parameter,
     custom_validator: Tparameter_callback,
-    parameter: TaintedString) =
+    parameter: string) =
   ## Runs the custom validator if it is not nil.
   ##
   ## Pass in the string of the parameter triggering the call. If the
   if not custom_validator.isNil:
     try:
       let message = custom_validator(parameter, parsed_parameter)
-      if not message.isNil and message.len > 0:
+      if message.len > 0:
         raise_or_quit(ValueError, ("Failed to validate value for " &
           "parameter $1:\n$2" % [escape(parameter), message]))
     except:
@@ -251,8 +251,8 @@ proc parse_parameter(quit_on_failure: bool, param, value: string,
   case param_kind:
   of PK_INT:
     try: result.int_val = value.parseInt
-    except OverflowError:
-      raise_or_quit(OverflowError, ("parameter $1 requires an " &
+    except OverflowDefect:
+      raise_or_quit(OverflowDefect, ("parameter $1 requires an " &
         "integer, but $2 is too large to fit into one") % [param,
         escape(value)])
     except ValueError:
@@ -301,8 +301,7 @@ template build_specification_lookup():
     OrderedTable[string, ptr Tparameter_specification] =
   ## Returns the table used to keep pointers to all of the specifications.
   var result {.gensym.}: OrderedTable[string, ptr Tparameter_specification]
-  result = initOrderedTable[string, ptr Tparameter_specification](
-    tables.rightSize(expected.len))
+  result = initOrderedTable[string, ptr Tparameter_specification](expected.len)
   for i in 0..expected.len-1:
     for param_to_detect in expected[i].names:
       if result.hasKey(param_to_detect):
@@ -319,7 +318,7 @@ proc echo_help*(expected: seq[Tparameter_specification] = @[],
 
 
 proc parse*(expected: seq[Tparameter_specification] = @[],
-    type_of_positional_parameters = PK_STRING, args: seq[TaintedString] = nil,
+    type_of_positional_parameters = PK_STRING, args: seq[string] = @[],
     bad_prefixes = @["-", "--"], end_of_options = "--",
     quit_on_failure = true): Tcommandline_results =
   ## Parses parameters and returns results.
@@ -339,7 +338,7 @@ proc parse*(expected: seq[Tparameter_specification] = @[],
   ##
   ## The args sequence should be the list of parameters passed to your program
   ## without the program binary (usually OSes provide the path to the binary as
-  ## the zeroth parameter). If args is nil, the list will be retrieved from the
+  ## the zeroth parameter). If args is empty, the list will be retrieved from the
   ## OS.
   ##
   ## If there is any kind of error and quit_on_failure is true, the quit proc
@@ -358,7 +357,7 @@ proc parse*(expected: seq[Tparameter_specification] = @[],
 
   # Prepare the input parameter list, maybe get it from the OS if not available.
   var args = args
-  if args == nil:
+  if args.len == 0:
     let total_params = paramCount()
     #echo "Got no explicit args, retrieving from OS. Count: ", total_params
     newSeq(args, total_params)
@@ -447,7 +446,7 @@ proc build_help*(expected: seq[Tparameter_specification] = @[],
 
   # First generate the joined version of input parameters in a list.
   var
-    seen = initSet[string]()
+    seen = initHashSet[string]()
     prefixes: seq[string] = @[]
     helps: seq[string] = @[]
   for key in keys:
@@ -471,7 +470,7 @@ proc build_help*(expected: seq[Tparameter_specification] = @[],
   let width = prefixes.map(proc (x: string): int = 3 + len(x)).max
 
   for line in zip(prefixes, helps):
-    result.add(line.a & spaces(width - line.a.len) & line.b)
+    result.add(line[0] & spaces(width - line[0].len) & line[1])
 
 
 proc echo_help*(expected: seq[Tparameter_specification] = @[],
@@ -486,7 +485,7 @@ proc echo_help*(expected: seq[Tparameter_specification] = @[],
     echo line
 
 
-when isMainModule:
+when true:
   # Simply tests code embedded in docs.
   let
     parsed_param1 = new_parsed_parameter(PK_FLOAT, 3.41)
diff --git a/tests/manyloc/argument_parser/ex_wget.nim b/tests/manyloc/argument_parser/ex_wget.nim
index 625a6f595..ebbf1933f 100644
--- a/tests/manyloc/argument_parser/ex_wget.nim
+++ b/tests/manyloc/argument_parser/ex_wget.nim
@@ -81,7 +81,7 @@ proc process_commandline(): Tcommandline_results =
     echo "Will use progress type $1" % [result.options[PARAM_PROGRESS[0]].str_val]
 
 
-when isMainModule:
+when true:
   let args = process_commandline()
   for param in args.positional_parameters:
     echo "Downloading $1" % param.str_val
diff --git a/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim
index ac425c7a0..6eb1b3844 100644
--- a/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim
+++ b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim
@@ -106,7 +106,7 @@ type
 
   #/ Collision begin event function callback type.
   #/ Returning false from a begin callback causes the collision to be ignored until
-  #/ the the separate callback is called when the objects stop colliding.
+  #/ the separate callback is called when the objects stop colliding.
   TCollisionBeginFunc* = proc (arb: PArbiter; space: PSpace; data: pointer): bool{.
       cdecl.}
   #/ Collision pre-solve event function callback type.
@@ -455,7 +455,7 @@ proc removeCollisionHandler*(space: PSpace; a: TCollisionType;
 #/ If the shape is attached to a static body, it will be added as a static shape.
 proc addShape*(space: PSpace; shape: PShape): PShape{.
   cdecl, importc: "cpSpaceAddShape", dynlib: Lib.}
-#/ Explicity add a shape as a static shape to the simulation.
+#/ Explicitly add a shape as a static shape to the simulation.
 proc addStaticShape*(space: PSpace; shape: PShape): PShape{.
   cdecl, importc: "cpSpaceAddStaticShape", dynlib: Lib.}
 #/ Add a rigid body to the simulation.
@@ -1028,12 +1028,12 @@ proc getCircleRadius*(shape: PShape): CpFloat {.
 proc allocPolyShape*(): PPolyShape {.
   cdecl, importc: "cpPolyShapeAlloc", dynlib: Lib.}
 #/ Initialize a polygon shape.
-#/ A convex hull will be created from the vertexes.
+#/ A convex hull will be created from the vertices.
 proc init*(poly: PPolyShape; body: PBody, numVerts: cint;
             verts: ptr TVector; offset: TVector): PPolyShape {.
   cdecl, importc: "cpPolyShapeInit", dynlib: Lib.}
 #/ Allocate and initialize a polygon shape.
-#/ A convex hull will be created from the vertexes.
+#/ A convex hull will be created from the vertices.
 proc newPolyShape*(body: PBody; numVerts: cint; verts: ptr TVector;
                     offset: TVector): PShape {.
   cdecl, importc: "cpPolyShapeNew", dynlib: Lib.}
@@ -1050,7 +1050,7 @@ proc newBoxShape*(body: PBody; width, height: CpFloat): PShape {.
 proc newBoxShape*(body: PBody; box: TBB): PShape {.
   cdecl, importc: "cpBoxShapeNew2", dynlib: Lib.}
 
-#/ Check that a set of vertexes is convex and has a clockwise winding.
+#/ Check that a set of vertices is convex and has a clockwise winding.
 #/ NOTE: Due to floating point precision issues, hulls created with cpQuickHull() are not guaranteed to validate!
 proc validatePoly*(verts: ptr TVector; numVerts: cint): bool {.
   cdecl, importc: "cpPolyValidate", dynlib: Lib.}
diff --git a/tests/manyloc/keineschweine/dependencies/enet/enet.nim b/tests/manyloc/keineschweine/dependencies/enet/enet.nim
index 07079f2ea..5dee6ae9c 100644
--- a/tests/manyloc/keineschweine/dependencies/enet/enet.nim
+++ b/tests/manyloc/keineschweine/dependencies/enet/enet.nim
@@ -79,10 +79,10 @@ type
 
   PPacket* = ptr TPacket
   TPacket*{.pure, final.} = object
-    referenceCount: csize
+    referenceCount: csize_t
     flags*: cint
     data*: cstring#ptr cuchar
-    dataLength*: csize
+    dataLength*: csize_t
     freeCallback*: TPacketFreeCallback
 
   PAcknowledgement* = ptr TAcknowledgement
@@ -265,7 +265,7 @@ const
   ENET_PEER_RELIABLE_WINDOW_SIZE         = 0x1000
   ENET_PEER_FREE_RELIABLE_WINDOWS        = 8
 
-when defined(Linux) or true:
+when defined(linux) or true:
   import posix
   const
     ENET_SOCKET_NULL*: cint = -1
@@ -274,7 +274,7 @@ when defined(Linux) or true:
     PEnetBuffer* = ptr object
     TENetBuffer*{.pure, final.} = object
       data*: pointer
-      dataLength*: csize
+      dataLength*: csize_t
     TENetSocketSet* = Tfd_set
   ## see if these are different on win32, if not then get rid of these
   template ENET_HOST_TO_NET_16*(value: untyped): untyped =
@@ -295,7 +295,7 @@ when defined(Linux) or true:
   template ENET_SOCKETSET_CHECK*(sockset, socket: untyped): untyped =
     FD_ISSET(socket, addr((sockset)))
 
-when defined(Windows):
+when defined(windows):
   ## put the content of win32.h in here
 
 
@@ -324,7 +324,7 @@ type
     data*: pointer
     state*: TPeerState
     channels*: PChannel
-    channelCount*: csize
+    channelCount*: csize_t
     incomingBandwidth*: cuint
     outgoingBandwidth*: cuint
     incomingBandwidthThrottleEpoch*: cuint
@@ -374,13 +374,13 @@ type
   TCompressor*{.pure, final.} = object
     context*: pointer
     compress*: proc (context: pointer; inBuffers: ptr TEnetBuffer;
-                     inBufferCount: csize; inLimit: csize;
-                     outData: ptr cuchar; outLimit: csize): csize{.cdecl.}
-    decompress*: proc (context: pointer; inData: ptr cuchar; inLimit: csize;
-                       outData: ptr cuchar; outLimit: csize): csize{.cdecl.}
+                     inBufferCount: csize_t; inLimit: csize_t;
+                     outData: ptr cuchar; outLimit: csize_t): csize_t{.cdecl.}
+    decompress*: proc (context: pointer; inData: ptr cuchar; inLimit: csize_t;
+                       outData: ptr cuchar; outLimit: csize_t): csize_t{.cdecl.}
     destroy*: proc (context: pointer){.cdecl.}
 
-  TChecksumCallback* = proc (buffers: ptr TEnetBuffer; bufferCount: csize): cuint{.
+  TChecksumCallback* = proc (buffers: ptr TEnetBuffer; bufferCount: csize_t): cuint{.
       cdecl.}
 
   PHost* = ptr THost
@@ -394,25 +394,25 @@ type
     randomSeed*: cuint
     recalculateBandwidthLimits*: cint
     peers*: ptr TPeer
-    peerCount*: csize
-    channelLimit*: csize
+    peerCount*: csize_t
+    channelLimit*: csize_t
     serviceTime*: cuint
     dispatchQueue*: TEnetList
     continueSending*: cint
-    packetSize*: csize
+    packetSize*: csize_t
     headerFlags*: cushort
     commands*: array[0..ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS - 1,
                      TEnetProtocol]
-    commandCount*: csize
+    commandCount*: csize_t
     buffers*: array[0..ENET_BUFFER_MAXIMUM - 1, TEnetBuffer]
-    bufferCount*: csize
+    bufferCount*: csize_t
     checksum*: TChecksumCallback
     compressor*: TCompressor
     packetData*: array[0..ENET_PROTOCOL_MAXIMUM_MTU - 1,
                        array[0..2 - 1, cuchar]]
     receivedAddress*: TAddress
     receivedData*: ptr cuchar
-    receivedDataLength*: csize
+    receivedDataLength*: csize_t
     totalSentData*: cuint
     totalSentPackets*: cuint
     totalReceivedData*: cuint
@@ -430,12 +430,12 @@ type
     packet*: ptr TPacket
 
   TENetCallbacks*{.pure, final.} = object
-    malloc*: proc (size: csize): pointer{.cdecl.}
+    malloc*: proc (size: csize_t): pointer{.cdecl.}
     free*: proc (memory: pointer){.cdecl.}
     no_memory*: proc (){.cdecl.}
 
 {.push callConv:cdecl.}
-proc enet_malloc*(a2: csize): pointer{.
+proc enet_malloc*(a2: csize_t): pointer{.
   importc: "enet_malloc", dynlib: Lib.}
 proc enet_free*(a2: pointer){.
   importc: "enet_free", dynlib: Lib.}
@@ -468,15 +468,15 @@ proc connect*(socket: TEnetSocket; address: var TAddress): cint{.
   importc: "enet_socket_connect", dynlib: Lib.}
 proc connect*(socket: TEnetSocket; address: ptr TAddress): cint{.
   importc: "enet_socket_connect", dynlib: Lib.}
-proc send*(socket: TEnetSocket; address: var TAddress; buffer: ptr TEnetBuffer; size: csize): cint{.
+proc send*(socket: TEnetSocket; address: var TAddress; buffer: ptr TEnetBuffer; size: csize_t): cint{.
   importc: "enet_socket_send", dynlib: Lib.}
-proc send*(socket: TEnetSocket; address: ptr TAddress; buffer: ptr TEnetBuffer; size: csize): cint{.
+proc send*(socket: TEnetSocket; address: ptr TAddress; buffer: ptr TEnetBuffer; size: csize_t): cint{.
   importc: "enet_socket_send", dynlib: Lib.}
 proc receive*(socket: TEnetSocket; address: var TAddress;
-               buffer: ptr TEnetBuffer; size: csize): cint{.
+               buffer: ptr TEnetBuffer; size: csize_t): cint{.
   importc: "enet_socket_receive", dynlib: Lib.}
 proc receive*(socket: TEnetSocket; address: ptr TAddress;
-               buffer: ptr TEnetBuffer; size: csize): cint{.
+               buffer: ptr TEnetBuffer; size: csize_t): cint{.
   importc: "enet_socket_receive", dynlib: Lib.}
 proc wait*(socket: TEnetSocket; a3: ptr cuint; a4: cuint): cint{.
   importc: "enet_socket_wait", dynlib: Lib.}
@@ -492,42 +492,42 @@ proc setHost*(address: PAddress; hostName: cstring): cint{.
   importc: "enet_address_set_host", dynlib: Lib.}
 proc setHost*(address: var TAddress; hostName: cstring): cint{.
   importc: "enet_address_set_host", dynlib: Lib.}
-proc getHostIP*(address: var TAddress; hostName: cstring; nameLength: csize): cint{.
+proc getHostIP*(address: var TAddress; hostName: cstring; nameLength: csize_t): cint{.
   importc: "enet_address_get_host_ip", dynlib: Lib.}
-proc getHost*(address: var TAddress; hostName: cstring; nameLength: csize): cint{.
+proc getHost*(address: var TAddress; hostName: cstring; nameLength: csize_t): cint{.
   importc: "enet_address_get_host", dynlib: Lib.}
 
 ## Call the above two funcs but trim the result string
-proc getHostIP*(address: var TAddress; hostName: var string; nameLength: csize): cint{.inline.} =
+proc getHostIP*(address: var TAddress; hostName: var string; nameLength: csize_t): cint{.inline.} =
   hostName.setLen nameLength
   result = getHostIP(address, cstring(hostName), nameLength)
   if result == 0:
     hostName.setLen(len(cstring(hostName)))
-proc getHost*(address: var TAddress; hostName: var string; nameLength: csize): cint{.inline.} =
+proc getHost*(address: var TAddress; hostName: var string; nameLength: csize_t): cint{.inline.} =
   hostName.setLen nameLength
   result = getHost(address, cstring(hostName), nameLength)
   if result == 0:
     hostName.setLen(len(cstring(hostName)))
 
-proc createPacket*(data: pointer; len: csize; flag: TPacketFlag): PPacket{.
+proc createPacket*(data: pointer; len: csize_t; flag: TPacketFlag): PPacket{.
   importc: "enet_packet_create", dynlib: Lib.}
 proc destroy*(packet: PPacket){.
   importc: "enet_packet_destroy", dynlib: Lib.}
-proc resize*(packet: PPacket; dataLength: csize): cint{.
+proc resize*(packet: PPacket; dataLength: csize_t): cint{.
   importc: "enet_packet_resize", dynlib: Lib.}
 
-proc crc32*(buffers: ptr TEnetBuffer; bufferCount: csize): cuint{.
+proc crc32*(buffers: ptr TEnetBuffer; bufferCount: csize_t): cuint{.
   importc: "enet_crc32", dynlib: Lib.}
 
-proc createHost*(address: ptr TAddress; maxConnections, maxChannels: csize; downSpeed, upSpeed: cuint): PHost{.
+proc createHost*(address: ptr TAddress; maxConnections, maxChannels: csize_t; downSpeed, upSpeed: cuint): PHost{.
   importc: "enet_host_create", dynlib: Lib.}
-proc createHost*(address: var TAddress; maxConnections, maxChannels: csize; downSpeed, upSpeed: cuint): PHost{.
+proc createHost*(address: var TAddress; maxConnections, maxChannels: csize_t; downSpeed, upSpeed: cuint): PHost{.
   importc: "enet_host_create", dynlib: Lib.}
 proc destroy*(host: PHost){.
   importc: "enet_host_destroy", dynlib: Lib.}
-proc connect*(host: PHost; address: ptr TAddress; channelCount: csize; data: cuint): PPeer{.
+proc connect*(host: PHost; address: ptr TAddress; channelCount: csize_t; data: cuint): PPeer{.
   importc: "enet_host_connect", dynlib: Lib.}
-proc connect*(host: PHost; address: var TAddress; channelCount: csize; data: cuint): PPeer{.
+proc connect*(host: PHost; address: var TAddress; channelCount: csize_t; data: cuint): PPeer{.
   importc: "enet_host_connect", dynlib: Lib.}
 
 proc checkEvents*(host: PHost; event: var TEvent): cint{.
@@ -546,7 +546,7 @@ proc compress*(host: PHost; compressor: PCompressor){.
   importc: "enet_host_compress", dynlib: Lib.}
 proc compressWithRangeCoder*(host: PHost): cint{.
   importc: "enet_host_compress_with_range_coder", dynlib: Lib.}
-proc channelLimit*(host: PHost; channelLimit: csize){.
+proc channelLimit*(host: PHost; channelLimit: csize_t){.
   importc: "enet_host_channel_limit", dynlib: Lib.}
 proc bandwidthLimit*(host: PHost; incoming, outgoing: cuint){.
   importc: "enet_host_bandwidth_limit", dynlib: Lib.}
@@ -596,12 +596,12 @@ proc createRangeCoder*(): pointer{.
 proc rangeCoderDestroy*(context: pointer){.
   importc: "enet_range_coder_destroy", dynlib: Lib.}
 proc rangeCoderCompress*(context: pointer; inBuffers: PEnetBuffer; inLimit,
-               bufferCount: csize; outData: cstring; outLimit: csize): csize{.
+               bufferCount: csize_t; outData: cstring; outLimit: csize_t): csize_t{.
   importc: "enet_range_coder_compress", dynlib: Lib.}
-proc rangeCoderDecompress*(context: pointer; inData: cstring; inLimit: csize;
-                            outData: cstring; outLimit: csize): csize{.
+proc rangeCoderDecompress*(context: pointer; inData: cstring; inLimit: csize_t;
+                            outData: cstring; outLimit: csize_t): csize_t{.
   importc: "enet_range_coder_decompress", dynlib: Lib.}
-proc protocolCommandSize*(commandNumber: cuchar): csize{.
+proc protocolCommandSize*(commandNumber: cuchar): csize_t{.
   importc: "enet_protocol_command_size", dynlib: Lib.}
 
 {.pop.}
@@ -609,4 +609,3 @@ proc protocolCommandSize*(commandNumber: cuchar): csize{.
 from hashes import `!$`, `!&`, Hash, hash
 proc hash*(x: TAddress): Hash {.nimcall, noSideEffect.} =
   result = !$(hash(x.host.int32) !& hash(x.port.int16))
-
diff --git a/tests/manyloc/keineschweine/dependencies/genpacket/genpacket.nim b/tests/manyloc/keineschweine/dependencies/genpacket/genpacket.nim
deleted file mode 100644
index 3026cc4b9..000000000
--- a/tests/manyloc/keineschweine/dependencies/genpacket/genpacket.nim
+++ /dev/null
@@ -1,295 +0,0 @@
-import macros, macro_dsl, streams, streams_enh
-from strutils import format
-
-template newLenName(): stmt {.immediate.} =
-  let lenName {.inject.} = ^("len"& $lenNames)
-  inc(lenNames)
-
-template defPacketImports*(): stmt {.immediate, dirty.} =
-  import macros, macro_dsl, streams, streams_enh
-  from strutils import format
-
-proc `$`*[T](x: seq[T]): string =
-  result = "[seq len="
-  result.add($x.len)
-  result.add ':'
-  for i in 0.. <len(x):
-    result.add "   "
-    result.add($x[i])
-  result.add ']'
-
-macro defPacket*(typeNameN: expr, typeFields: expr): stmt {.immediate.} =
-  result = newNimNode(nnkStmtList)
-  let
-    typeName = quoted2ident(typeNameN)
-    packetID = ^"p"
-    streamID = ^"s"
-  var
-    constructorParams = newNimNode(nnkFormalParams).und(typeName)
-    constructor = newNimNode(nnkProcDef).und(
-      postfix(^("new"& $typeName.ident), "*"),
-      emptyNode(),
-      emptyNode(),
-      constructorParams,
-      emptyNode(),
-      emptyNode())
-    pack = newNimNode(nnkProcDef).und(
-      postfix(^"pack", "*"),
-      emptyNode(),
-      emptyNode(),
-      newNimNode(nnkFormalParams).und(
-        emptyNode(),   # : void
-        newNimNode(nnkIdentDefs).und(
-          packetID,    # p: var typeName
-          newNimNode(nnkVarTy).und(typeName),
-          emptyNode()),
-        newNimNode(nnkIdentDefs).und(
-          streamID,    # s: PStream
-          ^"PStream",
-          newNimNode(nnkNilLit))),
-      emptyNode(),
-      emptyNode())
-    read = newNimNode(nnkProcDef).und(
-      newIdentNode("read"& $typeName.ident).postfix("*"),
-      emptyNode(),
-      emptyNode(),
-      newNimNode(nnkFormalParams).und(
-        typeName,   #result type
-        newNimNode(nnkIdentDefs).und(
-          streamID, # s: PStream = nil
-          ^"PStream",
-          newNimNode(nnkNilLit))),
-      emptyNode(),
-      emptyNode())
-    constructorBody = newNimNode(nnkStmtList)
-    packBody = newNimNode(nnkStmtList)
-    readBody = newNimNode(nnkStmtList)
-    lenNames = 0
-  for i in 0.. typeFields.len - 1:
-    let
-      name = typeFields[i][0]
-      dotName = packetID.dot(name)
-      resName = newIdentNode(!"result").dot(name)
-    case typeFields[i][1].kind
-    of nnkBracketExpr: #ex: paddedstring[32, '\0'], array[range, type]
-      case $typeFields[i][1][0].ident
-      of "paddedstring":
-        let length = typeFields[i][1][1]
-        let padChar = typeFields[i][1][2]
-        packBody.add(newCall(
-          "writePaddedStr", streamID, dotName, length, padChar))
-        ## result.name = readPaddedStr(s, length, char)
-        readBody.add(resName := newCall(
-          "readPaddedStr", streamID, length, padChar))
-        ## make the type a string
-        typeFields[i] = newNimNode(nnkIdentDefs).und(
-          name,
-          ^"string",
-          newNimNode(nnkEmpty))
-      of "array":
-        readBody.add(
-          newNimNode(nnkDiscardStmt).und(
-            newCall("readData", streamID, newNimNode(nnkAddr).und(resName), newCall("sizeof", resName))))
-        packBody.add(
-          newCall("writeData", streamID, newNimNode(nnkAddr).und(dotName), newCall("sizeof", dotName)))
-      of "seq":
-        ## let lenX = readInt16(s)
-        newLenName()
-        let
-          item = ^"item"  ## item name in our iterators
-          seqType = typeFields[i][1][1] ## type of seq
-          readName = newIdentNode("read"& $seqType.ident)
-        readBody.add(newNimNode(nnkLetSection).und(
-          newNimNode(nnkIdentDefs).und(
-            lenName,
-            newNimNode(nnkEmpty),
-            newCall("readInt16", streamID))))
-        readBody.add(      ## result.name = @[]
-          resName := ("@".prefix(newNimNode(nnkBracket))),
-          newNimNode(nnkForStmt).und(  ## for item in 1..len:
-            item,
-            infix(1.lit, "..", lenName),
-            newNimNode(nnkStmtList).und(
-              newCall(  ## add(result.name, unpack[seqType](stream))
-                "add", resName, newNimNode(nnkCall).und(readName, streamID)
-        ) ) ) )
-        packbody.add(
-          newNimNode(nnkVarSection).und(newNimNode(nnkIdentDefs).und(
-            lenName,  ## var lenName = int16(len(p.name))
-            newIdentNode("int16"),
-            newCall("int16", newCall("len", dotName)))),
-          newCall("writeData", streamID, newNimNode(nnkAddr).und(lenName), 2.lit),
-          newNimNode(nnkForStmt).und(  ## for item in 0..length - 1: pack(p.name[item], stream)
-            item,
-            infix(0.lit, "..", infix(lenName, "-", 1.lit)),
-            newNimNode(nnkStmtList).und(
-              newCall("echo", item, ": ".lit),
-              newCall("pack", dotName[item], streamID))))
-        #set the default value to @[] (new sequence)
-        typeFields[i][2] = "@".prefix(newNimNode(nnkBracket))
-      else:
-        error("Unknown type: "& treeRepr(typeFields[i]))
-    of nnkIdent: ##normal type
-      case $typeFields[i][1].ident
-      of "string": # length encoded string
-        packBody.add(newCall("writeLEStr", streamID, dotName))
-        readBody.add(resName := newCall("readLEStr", streamID))
-      of "int8", "int16", "int32", "float32", "float64", "char", "bool":
-        packBody.add(newCall(
-          "writeData", streamID, newNimNode(nnkAddr).und(dotName), newCall("sizeof", dotName)))
-        readBody.add(resName := newCall("read"& $typeFields[i][1].ident, streamID))
-      else:  ## hopefully the type you specified was another defpacket() type
-        packBody.add(newCall("pack", dotName, streamID))
-        readBody.add(resName := newCall("read"& $typeFields[i][1].ident, streamID))
-    else:
-      error("I dont know what to do with: "& treerepr(typeFields[i]))
-
-  var
-    toStringFunc = newNimNode(nnkProcDef).und(
-      newNimNode(nnkPostfix).und(
-        ^"*",
-        newNimNode(nnkAccQuoted).und(^"$")),
-      emptyNode(),
-      emptyNode(),
-      newNimNode(nnkFormalParams).und(
-        ^"string",
-        newNimNode(nnkIdentDefs).und(
-          packetID, # p: typeName
-          typeName,
-          emptyNode())),
-      emptyNode(),
-      emptyNode(),
-      newNimNode(nnkStmtList).und(#[6]
-        newNimNode(nnkAsgn).und(
-          ^"result",                  ## result =
-          newNimNode(nnkCall).und(#[6][0][1]
-            ^"format",  ## format
-            emptyNode()))))  ## "[TypeName   $1   $2]"
-    formatStr = "["& $typeName.ident
-
-  const emptyFields = {nnkEmpty, nnkNilLit}
-  var objFields = newNimNode(nnkRecList)
-  for i in 0.. < len(typeFields):
-    let fname = typeFields[i][0]
-    constructorParams.add(newNimNode(nnkIdentDefs).und(
-      fname,
-      typeFields[i][1],
-      typeFields[i][2]))
-    constructorBody.add((^"result").dot(fname) := fname)
-    #export the name
-    typeFields[i][0] = fname.postfix("*")
-    if not(typeFields[i][2].kind in emptyFields):
-      ## empty the type default for the type def
-      typeFields[i][2] = newNimNode(nnkEmpty)
-    objFields.add(typeFields[i])
-    toStringFunc[6][0][1].add(
-      prefix("$", packetID.dot(fname)))
-    formatStr.add "   $"
-    formatStr.add($(i + 1))
-
-  formatStr.add ']'
-  toStringFunc[6][0][1][1] = formatStr.lit()
-
-  result.add(
-    newNimNode(nnkTypeSection).und(
-      newNimNode(nnkTypeDef).und(
-        typeName.postfix("*"),
-        newNimNode(nnkEmpty),
-        newNimNode(nnkObjectTy).und(
-          newNimNode(nnkEmpty), #not sure what this is
-          newNimNode(nnkEmpty), #parent: OfInherit(Ident(!"SomeObj"))
-          objFields))))
-  result.add(constructor.und(constructorBody))
-  result.add(pack.und(packBody))
-  result.add(read.und(readBody))
-  result.add(toStringFunc)
-  when defined(GenPacketShowOutput):
-    echo(repr(result))
-
-proc `->`(a: string, b: string): NimNode {.compileTime.} =
-  result = newNimNode(nnkIdentDefs).und(^a, ^b, newNimNode(nnkEmpty))
-proc `->`(a: string, b: NimNode): NimNode {.compileTime.} =
-  result = newNimNode(nnkIdentDefs).und(^a, b, newNimNode(nnkEmpty))
-proc `->`(a, b: NimNode): NimNode {.compileTime.} =
-  a[2] = b
-  result = a
-
-proc newProc*(name: string, params: varargs[NimNode], resultType: NimNode): NimNode {.compileTime.} =
-  result = newNimNode(nnkProcDef).und(
-    ^name,
-    emptyNode(),
-    emptyNode(),
-    newNimNode(nnkFormalParams).und(resultType),
-    emptyNode(),
-    emptyNode(),
-    newNimNode(nnkStmtList))
-  result[3].add(params)
-macro forwardPacket*(typeName: expr, underlyingType: typedesc): stmt {.immediate.} =
-  result = newNimNode(nnkStmtList).und(
-    newProc(
-      "read"& $typeName.ident,
-      ["s" -> "PStream" -> newNimNode(nnkNilLit)],
-      typeName),
-    newProc(
-      "pack",
-      [ "p" -> newNimNode(nnkVarTy).und(typeName),
-        "s" -> "PStream" -> newNimNode(nnkNilLit)],
-      emptyNode()))
-  result[0][6].add(newNimNode(nnkDiscardStmt).und(
-    newCall(
-      "readData", ^"s", newNimNode(nnkAddr).und(^"result"), newCall("sizeof", ^"result")
-    )))
-  result[1][6].add(
-    newCall(
-      "writeData", ^"s", newNimNode(nnkAddr).und(^"p"), newCall(
-        "sizeof", ^"p")))
-  when defined(GenPacketShowOutput):
-    echo(repr(result))
-
-template forwardPacketT*(typeName: expr): stmt {.dirty, immediate.} =
-  proc `read typeName`*(s: PStream): typeName =
-    discard readData(s, addr result, sizeof(result))
-  proc `pack typeName`*(p: var typeName; s: PStream) =
-    writeData(s, addr p, sizeof(p))
-
-when isMainModule:
-  type
-    SomeEnum = enum
-      A = 0'i8,
-      B, C
-  forwardPacket(SomeEnum, int8)
-
-
-  defPacket(Foo, tuple[x: array[0..4, int8]])
-  var f = newFoo([4'i8, 3'i8, 2'i8, 1'i8, 0'i8])
-  var s2 = newStringStream("")
-  f.pack(s2)
-  assert s2.data == "\4\3\2\1\0"
-
-  var s = newStringStream()
-  s.flushImpl = proc(s: PStream) =
-    var z = PStringStream(s)
-    z.setPosition(0)
-    z.data.setLen(0)
-
-
-  s.setPosition(0)
-  s.data.setLen(0)
-  var o = B
-  o.pack(s)
-  o = A
-  o.pack(s)
-  o = C
-  o.pack(s)
-  assert s.data == "\1\0\2"
-  s.flush
-
-  defPacket(Y, tuple[z: int8])
-  proc `$`(z: Y): string = result = "Y("& $z.z &")"
-  defPacket(TestPkt, tuple[x: seq[Y]])
-  var test = newTestPkt()
-  test.x.add([newY(5), newY(4), newY(3), newY(2), newY(1)])
-  for itm in test.x:
-    echo(itm)
-  test.pack(s)
-  echo(repr(s.data))
diff --git a/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim b/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim
index 3fb4dc7d9..d91f1cb35 100644
--- a/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim
+++ b/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim
@@ -2,7 +2,7 @@ import macros, macro_dsl, estreams
 from strutils import format
 
 template newLenName() =
-  let lenName {.inject.} = ^("len"& $lenNames)
+  let lenName {.inject.} = ^("len" & $lenNames)
   inc(lenNames)
 
 template defPacketImports*() {.dirty.} =
@@ -18,7 +18,7 @@ macro defPacket*(typeNameN: untyped, typeFields: untyped): untyped =
   var
     constructorParams = newNimNode(nnkFormalParams).und(typeName)
     constructor = newNimNode(nnkProcDef).und(
-      postfix(^("new"& $typeName.ident), "*"),
+      postfix(^("new" & $typeName.ident), "*"),
       emptyNode(),
       emptyNode(),
       constructorParams,
@@ -41,7 +41,7 @@ macro defPacket*(typeNameN: untyped, typeFields: untyped): untyped =
       emptyNode(),
       emptyNode())
     read = newNimNode(nnkProcDef).und(
-      newIdentNode("read"& $typeName.ident).postfix("*"),
+      newIdentNode("read" & $typeName.ident).postfix("*"),
       emptyNode(),
       emptyNode(),
       newNimNode(nnkFormalParams).und(
@@ -70,7 +70,7 @@ macro defPacket*(typeNameN: untyped, typeFields: untyped): untyped =
         let
           item = ^"item"  ## item name in our iterators
           seqType = typeFields[i][1][1] ## type of seq
-          readName = newIdentNode("read"& $seqType.ident)
+          readName = newIdentNode("read" & $seqType.ident)
         readBody.add(newNimNode(nnkLetSection).und(
           newNimNode(nnkIdentDefs).und(
             lenName,
@@ -100,7 +100,7 @@ macro defPacket*(typeNameN: untyped, typeFields: untyped): untyped =
         #set the default value to @[] (new sequence)
         typeFields[i][2] = "@".prefix(newNimNode(nnkBracket))
       else:
-        error("Unknown type: "& treeRepr(typeFields[i]))
+        error("Unknown type: " & treeRepr(typeFields[i]))
     of nnkIdent: ##normal type
       case $typeFields[i][1].ident
       of "string": # length encoded string
@@ -109,12 +109,12 @@ macro defPacket*(typeNameN: untyped, typeFields: untyped): untyped =
       of "int8", "int16", "int32", "float32", "float64", "char", "bool":
         packBody.add(newCall(
           "writeBE", streamID, dotName))
-        readBody.add(resName := newCall("read"& $typeFields[i][1].ident, streamID))
+        readBody.add(resName := newCall("read" & $typeFields[i][1].ident, streamID))
       else:  ## hopefully the type you specified was another defpacket() type
         packBody.add(newCall("pack", streamID, dotName))
-        readBody.add(resName := newCall("read"& $typeFields[i][1].ident, streamID))
+        readBody.add(resName := newCall("read" & $typeFields[i][1].ident, streamID))
     else:
-      error("I dont know what to do with: "& treerepr(typeFields[i]))
+      error("I don't know what to do with: " & treerepr(typeFields[i]))
 
   var
     toStringFunc = newNimNode(nnkProcDef).und(
@@ -137,7 +137,7 @@ macro defPacket*(typeNameN: untyped, typeFields: untyped): untyped =
           newNimNode(nnkCall).und(# [6][0][1]
             ^"format",  ## format
             emptyNode()))))  ## "[TypeName   $1   $2]"
-    formatStr = "["& $typeName.ident
+    formatStr = "[" & $typeName.ident
 
   const emptyFields = {nnkEmpty, nnkNilLit}
   var objFields = newNimNode(nnkRecList)
@@ -206,7 +206,7 @@ macro forwardPacket*(typeName: untyped, underlyingType: untyped): untyped =
     streamID = ^"s"
   result = newNimNode(nnkStmtList).und(
     newProc(
-      (^("read"& $typeName.ident)).postfix("*"),
+      (^("read" & $typeName.ident)).postfix("*"),
       [ iddefs("s", "PBuffer", newNimNode(nnkNilLit)) ],
       typeName),
     newProc(
@@ -244,7 +244,7 @@ template forwardPacketT*(typeName: untyped; underlyingType: untyped) {.dirty.} =
     #writeData(s, addr p, sizeof(p))
     buffer.write(underlyingType(ord))
 
-when isMainModule:
+when false:
   type
     SomeEnum = enum
       A = 0'i8,
diff --git a/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim b/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim
index 33d2a7177..3341f42c2 100644
--- a/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim
+++ b/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim
@@ -50,7 +50,7 @@ macro `?`(a: untyped): untyped =
   result = ($a[1].ident)[0].lit
 ## echo(?F,?a,?t,?t,?y)
 
-when isMainModule:
+when false:
   macro foo(x: untyped) =
     result = newNimNode(nnkStmtList)
     result.add(newNimNode(nnkCall).und(!!"echo", "Hello thar".lit))
diff --git a/tests/manyloc/keineschweine/dependencies/genpacket/streams_enh.nim b/tests/manyloc/keineschweine/dependencies/genpacket/streams_enh.nim
index 5a1dffc93..a0c8a7a3c 100644
--- a/tests/manyloc/keineschweine/dependencies/genpacket/streams_enh.nim
+++ b/tests/manyloc/keineschweine/dependencies/genpacket/streams_enh.nim
@@ -1,7 +1,7 @@
 import streams
 from strutils import repeat
 
-proc readPaddedStr*(s: PStream, length: int, padChar = '\0'): TaintedString =
+proc readPaddedStr*(s: PStream, length: int, padChar = '\0'): string =
   var lastChr = length
   result = s.readStr(length)
   while lastChr >= 0 and result[lastChr - 1] == padChar: dec(lastChr)
@@ -16,7 +16,7 @@ proc writePaddedStr*(s: PStream, str: string, length: int, padChar = '\0') =
   else:
     s.write(str)
 
-proc readLEStr*(s: PStream): TaintedString =
+proc readLEStr*(s: PStream): string =
   var len = s.readInt16()
   result = s.readStr(len)
 
@@ -24,7 +24,7 @@ proc writeLEStr*(s: PStream, str: string) =
   s.write(str.len.int16)
   s.write(str)
 
-when isMainModule:
+when true:
   var testStream = newStringStream()
 
   testStream.writeLEStr("Hello")
diff --git a/tests/manyloc/keineschweine/dependencies/nake/nake.nim b/tests/manyloc/keineschweine/dependencies/nake/nake.nim
index 5341c1079..36538097e 100644
--- a/tests/manyloc/keineschweine/dependencies/nake/nake.nim
+++ b/tests/manyloc/keineschweine/dependencies/nake/nake.nim
@@ -7,6 +7,10 @@ contents thereof.
 
 As said in the Olde Country, `Keepe it Gangster'."""
 
+#[
+xxx remove this? seems mostly duplicate of: tests/manyloc/nake/nake.nim
+]#
+
 import strutils, parseopt, tables, os
 
 type
@@ -50,8 +54,8 @@ template withDir*(dir: string; body: stmt): stmt =
   body
   cd(curDir)
 
-when isMainModule:
-  if not existsFile("nakefile.nim"):
+when true:
+  if not fileExists("nakefile.nim"):
     echo "No nakefile.nim found. Current working dir is ", getCurrentDir()
     quit 1
   var args = ""
@@ -60,11 +64,12 @@ when isMainModule:
     args.add " "
   quit(shell("nim", "c", "-r", "nakefile.nim", args))
 else:
-  addQuitProc(proc() {.noconv.} =
+  import std/exitprocs
+  addExitProc(proc() {.noconv.} =
     var
       task: string
       printTaskList: bool
-    for kind, key, val in getOpt():
+    for kind, key, val in getopt():
       case kind
       of cmdLongOption, cmdShortOption:
         case key.tolower
diff --git a/tests/manyloc/keineschweine/enet_server/enet_client.nim b/tests/manyloc/keineschweine/enet_server/enet_client.nim
index ac600c0af..7aa7b9c2f 100644
--- a/tests/manyloc/keineschweine/enet_server/enet_client.nim
+++ b/tests/manyloc/keineschweine/enet_server/enet_client.nim
@@ -197,7 +197,7 @@ proc lobbyInit*() =
     messageArea.scrollBack -= 1
     update(messageArea))
   gui.newButton(text = "Flood msg area", position = vec2f(185, 30), onClick = proc(b: PButton) =
-    for i in 0.. <30:
+    for i in 0..< 30:
       dispMessage($i))"""
   dirServer = newServer()
   dirServer.addHandler HChat, handleChat
@@ -217,7 +217,7 @@ proc lobbyUpdate*(dt: float) =
   gui.update(dt)
   i = (i + 1) mod 60
   if i == 0:
-    fpsText.setString("FPS: "& ff(1.0/dt))
+    fpsText.setString("FPS: " & ff(1.0/dt))
 
 proc lobbyDraw*(window: PRenderWindow) =
   window.clear(Black)
diff --git a/tests/manyloc/keineschweine/enet_server/enet_server.nim b/tests/manyloc/keineschweine/enet_server/enet_server.nim
index eae7c034e..86d0ab360 100644
--- a/tests/manyloc/keineschweine/enet_server/enet_server.nim
+++ b/tests/manyloc/keineschweine/enet_server/enet_server.nim
@@ -102,8 +102,8 @@ handlers[HZoneJoinReq] = proc(client: PClient; buffer: PBuffer) =
 
 
 
-when isMainModule:
-  import parseopt, matchers, os, json
+when true:
+  import parseopt, os, json
 
 
   if enetInit() != 0:
@@ -113,12 +113,12 @@ when isMainModule:
 
   block:
     var zoneCfgFile = "./server_settings.json"
-    for kind, key, val in getOpt():
+    for kind, key, val in getopt():
       case kind
       of cmdShortOption, cmdLongOption:
         case key
         of "f", "file":
-          if existsFile(val):
+          if fileExists(val):
             zoneCfgFile = val
           else:
             echo("File does not exist: ", val)
@@ -136,7 +136,7 @@ when isMainModule:
     address.port = port
 
     var path = getAppDir()/../"data"/zoneFile
-    if not existsFile(path):
+    if not fileExists(path):
       echo("Zone settings file does not exist: ../data/", zoneFile)
       echo(path)
       quit(1)
diff --git a/tests/manyloc/keineschweine/enet_server/server_utils.nim b/tests/manyloc/keineschweine/enet_server/server_utils.nim
index 1fb8326ed..3940dcf01 100644
--- a/tests/manyloc/keineschweine/enet_server/server_utils.nim
+++ b/tests/manyloc/keineschweine/enet_server/server_utils.nim
@@ -1,4 +1,6 @@
-import enet, sg_packets, estreams, md5, zlib_helpers, client_helpers, strutils,
+import ../../../../dist/checksums/src/checksums/md5
+
+import enet, sg_packets, estreams, zlib_helpers, client_helpers, strutils,
   idgen, sg_assets, tables, os
 type
   PClient* = ref object
diff --git a/tests/manyloc/keineschweine/keineschweine.nim b/tests/manyloc/keineschweine/keineschweine.nim
index 59347ee46..123a4aebb 100644
--- a/tests/manyloc/keineschweine/keineschweine.nim
+++ b/tests/manyloc/keineschweine/keineschweine.nim
@@ -230,7 +230,7 @@ proc explode*(b: PLiveBullet) =
 proc bulletUpdate(body: PBody, gravity: TVector, damping, dt: CpFloat){.cdecl.} =
   body.UpdateVelocity(gravity, damping, dt)
 
-template getPhysical() {.immediate.} =
+template getPhysical() {.dirty.} =
   result.body = space.addBody(newBody(
     record.physics.mass,
     record.physics.moment))
@@ -281,7 +281,7 @@ proc draw*(window: PRenderWindow; b: PLiveBullet) {.inline.} =
 
 
 proc free*(veh: PVehicle) =
-  ("Destroying vehicle "& veh.record.name).echo
+  ("Destroying vehicle " & veh.record.name).echo
   destroy(veh.sprite)
   if veh.shape.isNil: "Free'd vehicle's shape was NIL!".echo
   else: space.removeShape(veh.shape)
@@ -290,12 +290,12 @@ proc free*(veh: PVehicle) =
   veh.body.free()
   veh.shape.free()
   veh.sprite = nil
-  veh.body   = nil
+  veh.body = nil
   veh.shape  = nil
 
 
 proc newVehicle*(record: PVehicleRecord): PVehicle =
-  echo("Creating "& record.name)
+  echo("Creating " & record.name)
   new(result, free)
   result.record = record
   result.sprite = result.record.anim.spriteSheet.sprite.copy()
@@ -446,7 +446,7 @@ ingameClient.registerHandler(KeyF12, down, proc() = toggleSpec())
 ingameClient.registerHandler(KeyF11, down, toggleShipSelect)
 ingameClient.registerHandler(MouseLeft, down, handleLClick)
 when defined(recordMode):
-  if not existsDir("data/snapshots"):
+  if not dirExists("data/snapshots"):
     createDir("data/snapshots")
   ingameClient.registerHandler(keynum9, down, proc() =
     if not isRecording: startRecording()
@@ -486,7 +486,7 @@ when defined(DebugKeys):
       activeVehicle.body.setPos mouseToSpace())
   ingameClient.registerHandler(KeyY, down, proc() =
     const looloo = ["Asteroid1", "Asteroid2"]
-    addObject(looloo[random(looloo.len)]))
+    addObject(looloo[rand(looloo.len)]))
   ingameClient.registerHandler(KeyO, down, proc() =
     if objects.len == 0:
       echo "Objects is empty"
@@ -663,7 +663,7 @@ proc mainRender() =
 proc readyMainState() =
   specInputClient.setActive()
 
-when isMainModule:
+when true:
   import parseopt
 
   localPlayer = newPlayer()
@@ -691,7 +691,7 @@ when isMainModule:
 
   block:
     var bPlayOffline = false
-    for kind, key, val in getOpt():
+    for kind, key, val in getopt():
       case kind
       of cmdArgument:
         if key == "offline": bPlayOffline = true
diff --git a/tests/manyloc/keineschweine/keineschweine.nim.cfg b/tests/manyloc/keineschweine/keineschweine.nim.cfg
index ca6c75f6e..a670e2b77 100644
--- a/tests/manyloc/keineschweine/keineschweine.nim.cfg
+++ b/tests/manyloc/keineschweine/keineschweine.nim.cfg
@@ -7,3 +7,4 @@ path = "dependencies/genpacket"
 path = "enet_server"
 debugger = off
 warning[SmallLshouldNotBeUsed] = off
+mm = refc
diff --git a/tests/manyloc/keineschweine/lib/client_helpers.nim b/tests/manyloc/keineschweine/lib/client_helpers.nim
index 5f819a7d1..b21e67cf7 100644
--- a/tests/manyloc/keineschweine/lib/client_helpers.nim
+++ b/tests/manyloc/keineschweine/lib/client_helpers.nim
@@ -1,6 +1,8 @@
+import ../../../../dist/checksums/src/checksums/md5
+
 import
   tables, sg_packets, enet, estreams, sg_gui, sfml,
-  zlib_helpers, md5, sg_assets, os
+  zlib_helpers, sg_assets, os
 type
   PServer* = ptr TServer
   TServer* = object
@@ -8,7 +10,7 @@ type
     addy: enet.TAddress
     host*: PHost
     peer*: PPeer
-    handlers*: TTable[char, TScPktHandler]
+    handlers*: Table[char, TScPktHandler]
   TScPktHandler* = proc(serv: PServer; buffer: PBuffer)
   TFileTransfer = object
     fileName: string
@@ -69,7 +71,7 @@ proc updateFileProgress*() =
   downloadProgress.setString($currentFileTransfer.pos & '/' & $currentFileTransfer.fullLen)
 
 ## HFileTransfer
-proc handleFilePartRecv*(serv: PServer; buffer: PBuffer) {.procvar.} =
+proc handleFilePartRecv*(serv: PServer; buffer: PBuffer) =
   var
     f = readScFileTransfer(buffer)
   updateFileProgress()
@@ -103,14 +105,14 @@ proc saveCurrentFile() =
   let
     path = expandPath(currentFileTransfer.assetType, currentFileTransfer.fileName)
     parent = parentDir(path)
-  if not existsDir(parent):
+  if not dirExists(parent):
     createDir(parent)
     echo("Created dir")
   writeFile path, currentFIleTransfer.data
   echo "Write file"
 
 ## HChallengeResult
-proc handleFileChallengeResult*(serv: PServer; buffer: PBuffer) {.procvar.} =
+proc handleFileChallengeResult*(serv: PServer; buffer: PBuffer) =
   var res = readScChallengeResult(buffer).status
   echo "got challnege result: ", res
   if res and currentFileTransfer.readyToSave:
@@ -122,12 +124,12 @@ proc handleFileChallengeResult*(serv: PServer; buffer: PBuffer) {.procvar.} =
     echo "REsetting current file"
 
 ## HFileCHallenge
-proc handleFileChallenge*(serv: PServer; buffer: PBuffer) {.procvar.} =
+proc handleFileChallenge*(serv: PServer; buffer: PBuffer) =
   var
     challenge = readScFileChallenge(buffer)
     path = expandPath(challenge)
     resp: CsFileChallenge
-  if not existsFile(path):
+  if not fileExists(path):
     resp.needFile = true
     echo "Got file challenge, need file."
   else:
diff --git a/tests/manyloc/keineschweine/lib/estreams.nim b/tests/manyloc/keineschweine/lib/estreams.nim
index 00a55c626..c5e45e0e7 100644
--- a/tests/manyloc/keineschweine/lib/estreams.nim
+++ b/tests/manyloc/keineschweine/lib/estreams.nim
@@ -26,7 +26,7 @@ proc newBuffer*(pkt: PPacket): PBuffer =
   copyMem(addr result.data[0], pkt.data, pkt.dataLength)
 proc toPacket*(buffer: PBuffer; flags: TPacketFlag): PPacket =
   buffer.data.setLen buffer.pos
-  result = createPacket(cstring(buffer.data), buffer.pos, flags)
+  result = createPacket(cstring(buffer.data), cast[csize_t](buffer.pos), flags)
 
 proc isDirty*(buffer: PBuffer): bool {.inline.} =
   result = (buffer.pos != 0)
@@ -78,9 +78,9 @@ proc write*(buffer: PBuffer; val: var string) =
   setLen buffer.data, buffer.pos + length.int
   copyMem(addr buffer.data[buffer.pos], addr val[0], length.int)
   inc buffer.pos, length.int
-proc write*[T: SomeNumber|bool|char|byte](buffer: PBuffer; val: T) =
+proc write*[T: SomeNumber|bool|char|byte](buffer: PBuffer; val: sink T) =
   var v: T
-  shallowCopy v, val
+  v = val
   writeBE buffer, v
 
 proc readInt8*(buffer: PBuffer): int8 =
@@ -106,7 +106,7 @@ proc readChar*(buffer: PBuffer): char {.inline.} = return readInt8(buffer).char
 proc readBool*(buffer: PBuffer): bool {.inline.} = return readInt8(buffer).bool
 
 
-when isMainModule:
+when false:
   var b = newBuffer(100)
   var str = "hello there"
   b.write str
@@ -118,5 +118,3 @@ when isMainModule:
   echo "flushed"
   b.writeC([1,2,3])
   echo(repr(b))
-
-
diff --git a/tests/manyloc/keineschweine/lib/glut.nim b/tests/manyloc/keineschweine/lib/glut.nim
index 44a290728..de9a97456 100644
--- a/tests/manyloc/keineschweine/lib/glut.nim
+++ b/tests/manyloc/keineschweine/lib/glut.nim
@@ -93,7 +93,7 @@ const
   GLUT_NORMAL* = 0
   GLUT_OVERLAY* = 1
 
-when defined(Windows):
+when defined(windows):
   const                       # Stroke font constants (use these in GLUT program).
     GLUT_STROKE_ROMAN* = cast[Pointer](0)
     GLUT_STROKE_MONO_ROMAN* = cast[Pointer](1) # Bitmap font constants (use these in GLUT program).
diff --git a/tests/manyloc/keineschweine/lib/map_filter.nim b/tests/manyloc/keineschweine/lib/map_filter.nim
index 42ef74ceb..acb58c60e 100644
--- a/tests/manyloc/keineschweine/lib/map_filter.nim
+++ b/tests/manyloc/keineschweine/lib/map_filter.nim
@@ -1,4 +1,4 @@
-template filterIt2*(seq, pred: expr, body: stmt): stmt {.immediate, dirty.} =
+template filterIt2*(seq, pred: untyped, body: untyped) =
   ## sequtils defines a filterIt() that returns a new seq, but this one is called
   ## with a statement body to iterate directly over it
   for it in items(seq):
@@ -13,11 +13,11 @@ proc mapInPlace*[A](x: var seq[A], fun: proc(y: A): A {.closure.}) =
   for i in 0..x.len-1:
     x[i] = fun(x[i])
 
-template unless*(condition: expr; body: stmt): stmt {.dirty.} =
-  if not(condition):
+template unless*(condition: untyped; body: untyped) {.dirty.} =
+  if not condition:
     body
 
-when isMainModule:
+when false:
   proc dumpSeq[T](x: seq[T]) =
     for index, item in x.pairs:
       echo index, " ", item
diff --git a/tests/manyloc/keineschweine/lib/sg_assets.nim b/tests/manyloc/keineschweine/lib/sg_assets.nim
index 1e8a99c83..96c962dc3 100644
--- a/tests/manyloc/keineschweine/lib/sg_assets.nim
+++ b/tests/manyloc/keineschweine/lib/sg_assets.nim
@@ -1,6 +1,8 @@
+import ../../../../dist/checksums/src/checksums/md5
+
 import
   re, json, strutils, tables, math, os, math_helpers,
-  sg_packets, md5, zlib_helpers
+  sg_packets, zlib_helpers
 
 when defined(NoSFML):
   import server_utils
@@ -145,7 +147,7 @@ proc importHandling(data: JsonNode): THandlingRecord
 proc importBullet(data: JsonNode; errors: var seq[string]): PBulletRecord
 proc importSoul(data: JsonNode): TSoulRecord
 proc importExplosion(data: JsonNode; errors: var seq[string]): TExplosionRecord
-proc importSound*(data: JsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord
+proc importSound*(data: JsonNode; errors: var seq[string]; fieldName: string = ""): PSoundRecord
 
 ## this is the only pipe between lobby and main.nim
 proc getActiveState*(): TGameState =
@@ -221,8 +223,8 @@ template cacheImpl(procName, cacheName, resultType, body: untyped) {.dirty.} =
     cacheName[filename] = result
 
 template checkFile(path: untyped) {.dirty.} =
-  if not existsFile(path):
-    errors.add("File missing: "& path)
+  if not fileExists(path):
+    errors.add("File missing: " & path)
 
 cacheImpl newSprite, SpriteSheets, PSpriteSheet:
   result.file = filename
@@ -231,7 +233,7 @@ cacheImpl newSprite, SpriteSheets, PSpriteSheet:
     result.frameh = strutils.parseInt(matches[1])
     checkFile("data/gfx"/result.file)
   else:
-    errors.add "Bad file: "&filename&" must be in format name_WxH.png"
+    errors.add "Bad file: " & filename & " must be in format name_WxH.png"
     return
 
 cacheImpl newSound, SoundCache, PSoundRecord:
@@ -314,15 +316,15 @@ proc validateSettings*(settings: JsonNode, errors: var seq[string]): bool =
     else:
       var id = 0
       for i in items.items:
-        if i.kind != JArray: errors.add("Item #$1 is not an array"% $id)
+        if i.kind != JArray: errors.add("Item #$1 is not an array" % $id)
         elif i.len != 3: errors.add("($1) Item record should have 3 fields"%($id))
         elif i[0].kind != JString or i[1].kind != JString or i[2].kind != JObject:
-          errors.add("($1) Item should be in form [name, type, {item: data}]"% $id)
+          errors.add("($1) Item should be in form [name, type, {item: data}]" % $id)
           result = false
         inc id
 
 proc loadSettingsFromFile*(filename: string, errors: var seq[string]): bool =
-  if not existsFile(filename):
+  if not fileExists(filename):
     errors.add("File does not exist: "&filename)
   else:
     result = loadSettings(readFile(filename), errors)
@@ -332,10 +334,10 @@ proc loadSettings*(rawJson: string, errors: var seq[string]): bool =
   try:
     settings = parseJson(rawJson)
   except JsonParsingError:
-    errors.add("JSON parsing error: "& getCurrentExceptionMsg())
+    errors.add("JSON parsing error: " & getCurrentExceptionMsg())
     return
   except:
-    errors.add("Unknown exception: "& getCurrentExceptionMsg())
+    errors.add("Unknown exception: " & getCurrentExceptionMsg())
     return
   if not validateSettings(settings, errors):
     return
@@ -378,7 +380,7 @@ proc loadSettings*(rawJson: string, errors: var seq[string]): bool =
     inc vID
     if itm.kind == Projectile:
       if itm.bullet.isNil:
-        errors.add("Projectile #$1 has no bullet!"% $vID)
+        errors.add("Projectile #$1 has no bullet!" % $vID)
       elif itm.bullet.id == -1:
         ## this item has an anonymous bullet, fix the ID and name
         itm.bullet.id = bID
@@ -514,7 +516,7 @@ proc importExplosion(data: JsonNode; errors: var seq[string]): TExplosionRecord
   let expl = data["explode"]
   result.anim = importAnim(expl, errors)
   result.sound = importSound(expl, errors, "sound")
-proc importSound*(data: JsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord =
+proc importSound*(data: JsonNode; errors: var seq[string]; fieldName: string = ""): PSoundRecord =
   if data.kind == JObject:
     checkKey(data, fieldName)
     result = newSound(data[fieldName].str, errors)
diff --git a/tests/manyloc/keineschweine/lib/sg_gui.nim b/tests/manyloc/keineschweine/lib/sg_gui.nim
index b7448d9df..bcf415556 100644
--- a/tests/manyloc/keineschweine/lib/sg_gui.nim
+++ b/tests/manyloc/keineschweine/lib/sg_gui.nim
@@ -5,13 +5,13 @@ from strutils import countlines
 
 type
   PGuiContainer* = ref TGuiContainer
-  TGuiContainer* = object of TObject
+  TGuiContainer* = object of RootObj
     position: TVector2f
     activeEntry: PTextEntry
     widgets: seq[PGuiObject]
     buttons: seq[PButton]
   PGuiObject* = ref TGuiObject
-  TGuiObject* = object of TObject
+  TGuiObject* = object of RootObj
   PButton* = ref TButton
   TButton* = object of TGuiObject
     enabled: bool
@@ -92,8 +92,8 @@ proc newGuiContainer*(pos: TVector2f): PGuiContainer =
   result = newGuiContainer()
   result.setPosition pos
 proc free*(container: PGuiContainer) =
-  container.widgets = nil
-  container.buttons = nil
+  container.widgets = @[]
+  container.buttons = @[]
 proc add*(container: PGuiContainer; widget: PGuiObject) =
   container.widgets.add(widget)
 proc add*(container: PGuiContainer; button: PButton) =
@@ -193,28 +193,28 @@ proc setActive*(t: PTextEntry) =
   if not t.isNil and not t.inputClient.isNil:
     input_helpers.setActive(t.inputClient)
 
+when false:
+  proc newMessageArea*(container: PGuiContainer; position: TVector2f): PMessageArea =
+    new(result)
+    result.messages = @[]
+    result.pos = position
+    container.add(result)
+  proc add*(m: PMessageArea, text: string): PText =
+    result = messageProto.copy()
+    result.setString(text)
+    m.messages.add(result)
+    let nmsgs = len(m.messages)
+    var pos   = vec2f(m.pos.x, m.pos.y)
+    for i in countdown(nmsgs - 1, max(nmsgs - 30, 0)):
+      setPosition(m.messages[i], pos)
+      pos.y -= 16.0
 
-discard """proc newMessageArea*(container: PGuiContainer; position: TVector2f): PMessageArea =
-  new(result)
-  result.messages = @[]
-  result.pos = position
-  container.add(result)
-proc add*(m: PMessageArea, text: string): PText =
-  result = messageProto.copy()
-  result.setString(text)
-  m.messages.add(result)
-  let nmsgs = len(m.messages)
-  var pos   = vec2f(m.pos.x, m.pos.y)
-  for i in countdown(nmsgs - 1, max(nmsgs - 30, 0)):
-    setPosition(m.messages[i], pos)
-    pos.y -= 16.0
+  proc draw*(window: PRenderWindow; m: PMessageArea) =
+    let nmsgs = len(m.messages)
+    if nmsgs == 0: return
+    for i in countdown(nmsgs - 1, max(nmsgs - 30, 0)):
+      window.draw(m.messages[i])
 
-proc draw*(window: PRenderWindow; m: PMessageArea) =
-  let nmsgs = len(m.messages)
-  if nmsgs == 0: return
-  for i in countdown(nmsgs - 1, max(nmsgs - 30, 0)):
-    window.draw(m.messages[i])
-"""
 proc newMessageArea*(container: PGuiContainer; position: TVector2f): PMessageArea =
   new(result)
   result.messages = @[]
@@ -260,7 +260,7 @@ proc update*(m: PMessageArea) =
       m.texts.add messageProto.copy()
   elif m.texts.len > m.sizeVisible:
     echo "cutting ", m.texts.len - m.sizeVisible, " fields"
-    for i in m.sizeVisible.. < m.texts.len:
+    for i in m.sizeVisible ..< m.texts.len:
       m.texts.pop().destroy()
   let nmsgs = m.messages.len()
   if m.sizeVisible == 0 or nmsgs == 0:
diff --git a/tests/manyloc/keineschweine/lib/sg_packets.nim b/tests/manyloc/keineschweine/lib/sg_packets.nim
index f3a0e8925..797a60706 100644
--- a/tests/manyloc/keineschweine/lib/sg_packets.nim
+++ b/tests/manyloc/keineschweine/lib/sg_packets.nim
@@ -1,4 +1,6 @@
-import genpacket_enet, sockets, md5, enet
+import ../../../../dist/checksums/src/checksums/md5
+
+import genpacket_enet, nativesockets, net, enet
 defPacketImports()
 
 type
@@ -92,7 +94,7 @@ defPacket(DsMsg, tuple[msg: string])
 let HVerifyClient* = 'v'
 defPacket(SdVerifyClient, tuple[session: ScLogin])
 
-when isMainModule:
+when true:
 
   var buf = newBuffer(100)
   var m = toMd5("hello there")
diff --git a/tests/manyloc/keineschweine/lib/vehicles.nim b/tests/manyloc/keineschweine/lib/vehicles.nim
index e245c9e8c..2c6e54d2b 100644
--- a/tests/manyloc/keineschweine/lib/vehicles.nim
+++ b/tests/manyloc/keineschweine/lib/vehicles.nim
@@ -23,7 +23,7 @@ proc strafe_left*(obj: PVehicle, dt: float) =
     VectorZero)
 proc strafe_right*(obj: PVehicle, dt: float) =
   obj.body.applyImpulse(
-    vectorForAngle(obj.body.getAngle()).rperp()* obj.record.handling.strafe * dt,
+    vectorForAngle(obj.body.getAngle()).rperp() * obj.record.handling.strafe * dt,
     VectorZero)
 proc turn_right*(obj: PVehicle, dt: float) =
   #obj.angle = (obj.angle + (obj.record.handling.rotation.float / 10.0 * dt)) mod TAU
diff --git a/tests/manyloc/keineschweine/lib/zlib_helpers.nim b/tests/manyloc/keineschweine/lib/zlib_helpers.nim
index 076475964..e51c000c8 100644
--- a/tests/manyloc/keineschweine/lib/zlib_helpers.nim
+++ b/tests/manyloc/keineschweine/lib/zlib_helpers.nim
@@ -1,26 +1,31 @@
+# xxx this test is bad (echo instead of error, etc)
+
 import zip/zlib
 
 proc compress*(source: string): string =
   var
     sourcelen = source.len
-    destlen = sourcelen + (sourcelen.float * 0.1).int + 16
+    destLen = sourcelen + (sourcelen.float * 0.1).int + 16
   result = ""
   result.setLen destLen
-  var res = zlib.compress(cstring(result), addr destLen, cstring(source), sourceLen)
+  # see http://www.zlib.net/zlib-1.2.11.tar.gz for correct definitions
+  var destLen2 = destLen.Ulongf
+  var res = zlib.compress(cstring(result), addr destLen2, cstring(source), sourceLen.Ulong)
   if res != Z_OK:
     echo "Error occurred: ", res
-  elif destLen < result.len:
-    result.setLen(destLen)
+  elif destLen2.int < result.len:
+    result.setLen(destLen2.int)
 
 proc uncompress*(source: string, destLen: var int): string =
   result = ""
   result.setLen destLen
-  var res = zlib.uncompress(cstring(result), addr destLen, cstring(source), source.len)
+  var destLen2 = destLen.Ulongf
+  var res = zlib.uncompress(cstring(result), addr destLen2, cstring(source), source.len.Ulong)
   if res != Z_OK:
     echo "Error occurred: ", res
 
 
-when isMainModule:
+when true:
   import strutils
   var r = compress("Hello")
   echo repr(r)
diff --git a/tests/manyloc/keineschweine/server/old_dirserver.nim b/tests/manyloc/keineschweine/server/old_dirserver.nim
index 202dc6fe7..a63829691 100644
--- a/tests/manyloc/keineschweine/server/old_dirserver.nim
+++ b/tests/manyloc/keineschweine/server/old_dirserver.nim
@@ -1,9 +1,9 @@
 ## directory server
 ## handles client authorization and assets
-
+import ../../../dist/checksums/src/checksums/md5
 import
   sockets, times, streams, streams_enh, tables, json, os,
-  sg_packets, sg_assets, md5, server_utils, map_filter
+  sg_packets, sg_assets, server_utils, map_filter
 type
   THandler = proc(client: PCLient; stream: PStream)
 var
@@ -156,15 +156,15 @@ proc poll*(timeout: int = 250) =
       echo("Write ", c, " result: ", res, " data: ", repr(c.outputBuf.data))
       c.outputBuf.flush()
 
-when isMainModule:
-  import parseopt, matchers, strutils
+when true:
+  import parseopt, strutils
   var cfgFile = "dirserver_settings.json"
-  for kind, key, val in getOpt():
+  for kind, key, val in getopt():
     case kind
     of cmdShortOption, cmdLongOption:
       case key
       of "f", "file":
-        if existsFile(val):
+        if fileExists(val):
           cfgFile = val
         else:
           echo("File does not exist: ", val)
diff --git a/tests/manyloc/keineschweine/server/old_server_utils.nim b/tests/manyloc/keineschweine/server/old_server_utils.nim
index 3da6e078c..f389c0836 100644
--- a/tests/manyloc/keineschweine/server/old_server_utils.nim
+++ b/tests/manyloc/keineschweine/server/old_server_utils.nim
@@ -1,5 +1,7 @@
+import ../../../dist/checksums/src/checksums/md5
+
 import
-  streams, md5, sockets,
+  streams, sockets,
   sg_packets, zlib_helpers, idgen
 type
   TClientType* = enum
diff --git a/tests/manyloc/keineschweine/server/old_sg_server.nim b/tests/manyloc/keineschweine/server/old_sg_server.nim
index bddc74c6d..d6fbbe99e 100644
--- a/tests/manyloc/keineschweine/server/old_sg_server.nim
+++ b/tests/manyloc/keineschweine/server/old_sg_server.nim
@@ -141,15 +141,15 @@ proc poll*(timeout: int = 250) =
       echo("Write ", c, " result: ", res, " data: ", c.outputBuf.data)
       c.outputBuf.flush()
 
-when isMainModule:
-  import parseopt, matchers, strutils
+when true:
+  import parseopt, strutils
   var zoneCfgFile = "./server_settings.json"
-  for kind, key, val in getOpt():
+  for kind, key, val in getopt():
     case kind
     of cmdShortOption, cmdLongOption:
       case key
       of "f", "file":
-        if existsFile(val):
+        if fileExists(val):
           zoneCfgFile = val
         else:
           echo("File does not exist: ", val)
@@ -165,7 +165,7 @@ when isMainModule:
     dirServerInfo = jsonSettings["dirserver"]
 
   var path = getAppDir()/../"data"/zoneFile
-  if not existsFile(path):
+  if not fileExists(path):
     echo("Zone settings file does not exist: ../data/", zoneFile)
     echo(path)
     quit(1)
diff --git a/tests/manyloc/keineschweine/server/sg_lobby.nim b/tests/manyloc/keineschweine/server/sg_lobby.nim
index f130e1b54..04ce10f08 100644
--- a/tests/manyloc/keineschweine/server/sg_lobby.nim
+++ b/tests/manyloc/keineschweine/server/sg_lobby.nim
@@ -1,6 +1,7 @@
+import ../../../dist/checksums/src/checksums/md5
 
 import
-  sockets, streams, tables, times, math, strutils, json, os, md5,
+  sockets, streams, tables, times, math, strutils, json, os,
   sfml, sfml_vector, sfml_colors,
   streams_enh, input_helpers, zlib_helpers, client_helpers, sg_packets, sg_assets, sg_gui
 type
@@ -51,7 +52,7 @@ proc setConnected(state: bool) =
     for b in connectionButtons: disable(b)
 
 proc setActiveZone(ind: int; zone: ScZoneRecord) =
-  #hilight it or something
+  #highlight it or something
   dispmessage("Selected " & zone.name)
   connectZone(zone.ip, zone.port)
   playBtn.enable()
@@ -243,7 +244,7 @@ proc lobbyInit*() =
     messageArea.scrollBack -= 1
     update(messageArea))
   gui.newButton(text = "Flood msg area", position = vec2f(185, 30), onClick = proc(b: PButton) =
-    for i in 0.. <30:
+    for i in 0..< 30:
       dispMessage($i))
 
 var i = 0
diff --git a/tests/manyloc/nake/nake.nim b/tests/manyloc/nake/nake.nim
index 728619e47..5d3173a20 100644
--- a/tests/manyloc/nake/nake.nim
+++ b/tests/manyloc/nake/nake.nim
@@ -22,10 +22,10 @@ proc runTask*(name: string) {.inline.}
 proc shell*(cmd: varargs[string, `$`]): int {.discardable.}
 proc cd*(dir: string) {.inline.}
 
-template nakeImports*(): stmt {.immediate.} =
+template nakeImports*() =
   import tables, parseopt, strutils, os
 
-template task*(name: string; description: string; body: stmt): stmt {.dirty, immediate.} =
+template task*(name: string; description: string; body: untyped) {.dirty.} =
   block:
     var t = newTask(description, proc() {.closure.} =
       body)
@@ -40,7 +40,7 @@ proc runTask*(name: string) = tasks[name].action()
 proc shell*(cmd: varargs[string, `$`]): int =
   result = execShellCmd(cmd.join(" "))
 proc cd*(dir: string) = setCurrentDir(dir)
-template withDir*(dir: string; body: stmt): stmt =
+template withDir*(dir: string; body: untyped) =
   ## temporary cd
   ## withDir "foo":
   ##   # inside foo
@@ -50,8 +50,8 @@ template withDir*(dir: string; body: stmt): stmt =
   body
   cd(curDir)
 
-when isMainModule:
-  if not existsFile("nakefile.nim"):
+when true:
+  if not fileExists("nakefile.nim"):
     echo "No nakefile.nim found. Current working dir is ", getCurrentDir()
     quit 1
   var args = ""
@@ -60,11 +60,12 @@ when isMainModule:
     args.add " "
   quit(shell("nim", "c", "-r", "nakefile.nim", args))
 else:
-  addQuitProc(proc() {.noconv.} =
+  import std/exitprocs
+  addExitProc(proc() {.noconv.} =
     var
       task: string
       printTaskList: bool
-    for kind, key, val in getOpt():
+    for kind, key, val in getopt():
       case kind
       of cmdLongOption, cmdShortOption:
         case key.tolowerAscii
@@ -75,7 +76,7 @@ else:
       of cmdArgument:
         task = key
       else: discard
-    if printTaskList or task.isNil or not(tasks.hasKey(task)):
+    if printTaskList or task.len == 0 or not(tasks.hasKey(task)):
       echo "Available tasks:"
       for name, task in pairs(tasks):
         echo name, " - ", task.desc
diff --git a/tests/manyloc/nake/nakefile.nim b/tests/manyloc/nake/nakefile.nim
index 3e8609169..fc479a5c2 100644
--- a/tests/manyloc/nake/nakefile.nim
+++ b/tests/manyloc/nake/nakefile.nim
@@ -29,11 +29,12 @@ task "test2", "Build release test build test release build":
   if shell("nim", ReleaseDefines, ReleaseTestDefines, "compile", ExeName) == 0:
     shell "."/ExeName
 
-discard """task "dirserver", "build the directory server":
-  withDir "server":
-    if shell("nim", ServerDefines, "compile", "dirserver") != 0:
-      echo "Failed to build the dirserver"
-      quit 1"""
+when false:
+  task "dirserver", "build the directory server":
+    withDir "server":
+      if shell("nim", ServerDefines, "compile", "dirserver") != 0:
+        echo "Failed to build the dirserver"
+        quit 1
 
 task "zoneserver", "build the zone server":
   withDir "enet_server":
@@ -63,7 +64,7 @@ task "release", "release build":
     ## zip up all the files and such or something useful here
 
 task "testskel", "create skeleton test dir for testing":
-  let dirname = "test-"& $random(5000)
+  let dirname = "test-" & $rand(5000)
   removeDir dirName
   createDir dirName/"data/fnt"
   copyFile "data/fnt/LiberationMono-Regular", dirName/"data/fnt/LiberationMono-Regular.ttf"
@@ -76,16 +77,17 @@ task "testskel", "create skeleton test dir for testing":
 
 task "clean", "cleanup generated files":
   var dirs = @["nimcache", "server"/"nimcache"]
-  dirs.map(proc(x: var string) =
-    if existsDir(x): removeDir(x))
+  dirs.apply(proc(x: var string) =
+    if dirExists(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):
+  if fileExists(path):
     echo "The file already exists\n",
       "[R]emove  [M]ove  [Q]uit  [S]kip    Source: ", GameAssets
     case stdin.readLine.toLowerAscii
@@ -101,7 +103,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,13 +128,13 @@ 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
     var
       z: TZipArchive
-      destDir = getCurrentDir()/("unzip"& $random(5000))
+      destDir = getCurrentDir()/("unzip" & $rand(5000))
     if not z.open(path, fmRead):
       echo "Could not open zip, bad download?"
       return
diff --git a/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim b/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim
index 7d787c07b..29e23f9d0 100644
--- a/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim
+++ b/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim
@@ -53,7 +53,7 @@ else:
   const
     glRealType* = cGLfloat
 
-proc setUniformV4*[T](loc: GLint, vecs: var openarray[TV4[T]]) =
+proc setUniformV4*[T](loc: GLint, vecs: var openArray[TV4[T]]) =
   glUniform4fv(loc, vecs.len.GLsizei, cast[ptr GLfloat](vecs[0].addr))
 
 proc setUniformV4*[T](loc: GLint, vec: TV4[T]) =
diff --git a/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/primitive.nim b/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/primitive.nim
index 8c26c04eb..accc2d96b 100644
--- a/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/primitive.nim
+++ b/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/primitive.nim
@@ -47,7 +47,7 @@ proc newVert*(rect: rect.TRect): seq[TVert] =
 proc newVertAttrib(i: GLuint, size: GLint, stride: GLsizei, offset: GLvoid): TVertAttrib =
   TVertAttrib(i: i, size: size, stride: stride, offset: offset)
 
-proc genBuf*[T](vboTarget, objUsage: GLenum, data: var openarray[T]): GLuint =
+proc genBuf*[T](vboTarget, objUsage: GLenum, data: var openArray[T]): GLuint =
   result = 0.GLuint
   ?glGenBuffers(1, result.addr)
   ?glBindBuffer(vboTarget, result)
@@ -61,7 +61,7 @@ proc newPrimitive*(verts: var seq[TVert],
                    color=white(),
                    z: TZ_range=0): PPrimitive =
   var indices = newSeq[GLushort](verts.len)
-  for i in 0 .. <verts.len:
+  for i in 0 ..< verts.len:
     indices[i] = i.GLushort
 
   new(result)
@@ -108,7 +108,7 @@ proc updVerts*(o: PPrimitive, start, `end`: int, f: proc(i: int, vert: var TVert
                    cast[GLvoid](cast[int](o.verts[0].addr) + byteOffset))
 
 proc updAllVerts(o: PPrimitive, f: proc(i: int, vert: var TVert)) =
-  for i in 0 .. <o.verts.len:
+  for i in 0 ..< o.verts.len:
     f(i, o.verts[i])
 
   ?glBindBuffer(GLarrayBuffer, o.arrBufId)
@@ -132,7 +132,7 @@ proc newVertCircle*(circle: TCircle, nSegs: Natural=0): seq[TVert] =
 
   result = newSeq[TVert](nSegs)
   #result[0] = newVert(circle.p, newV2xy(0.5))
-  for i in 1 .. <nSegs:
+  for i in 1 ..< nSegs:
     let pos = newV2(x + circle.p.x, y + circle.p.y)
     let texCoord = pos * newV2xy(1.0 / circle.r)
 
diff --git a/tests/manyloc/named_argument_bug/tri_engine/math/vec.nim b/tests/manyloc/named_argument_bug/tri_engine/math/vec.nim
index 926958fe4..18ede6100 100644
--- a/tests/manyloc/named_argument_bug/tri_engine/math/vec.nim
+++ b/tests/manyloc/named_argument_bug/tri_engine/math/vec.nim
@@ -14,7 +14,7 @@ type
 # TODO: Change to TVT when compiler issue is resolved.
 proc `$`*[T](o: TV2[T]): string =
   result = "("
-  for i in 0 .. <o.len:
+  for i in 0 ..< o.len:
     result &= $o[0]
     if i != o.len - 1:
       result &= ", "
@@ -47,7 +47,7 @@ proc `+`*(lhs: TV2[TR], rhs: TV2[TR]): TV2[TR] =
 #    result += a[i] * b[i]
 
 proc dot[T](a, b: TV2[T]): T =
-  for i in 0 .. <a.len:
+  for i in 0 ..< a.len:
     result += a[i] * b[i]
 
 assert dot(newV2(), newV2()) == 0.0
diff --git a/tests/manyloc/standalone/barebone.nim b/tests/manyloc/standalone/barebone.nim
index 9d75f8f2e..487f6da65 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
 
@@ -7,3 +11,6 @@ proc printf(frmt: cstring) {.varargs, header: "<stdio.h>", cdecl.}
 var x = 0
 inc x
 printf("hi %ld\n", x+4777)
+
+proc substr(a: string): string = a[0 .. 3] # This should compile. See #9762
+const a = substr("foobar")
diff --git a/tests/manyloc/standalone/panicoverride.nim b/tests/manyloc/standalone/panicoverride.nim
index d9b3f4388..c0b8bb030 100644
--- a/tests/manyloc/standalone/panicoverride.nim
+++ b/tests/manyloc/standalone/panicoverride.nim
@@ -11,9 +11,4 @@ proc panic(s: string) {.noreturn.} =
   rawoutput(s)
   exit(1)
 
-# Alternatively we also could implement these 2 here:
-#
-# proc sysFatal(exceptn: typeDesc, message: string) {.noReturn.}
-# proc sysFatal(exceptn: typeDesc, message, arg: string) {.noReturn.}
-
 {.pop.}
diff --git a/tests/manyloc/standalone2/panicoverride.nim b/tests/manyloc/standalone2/panicoverride.nim
new file mode 100644
index 000000000..c0b8bb030
--- /dev/null
+++ b/tests/manyloc/standalone2/panicoverride.nim
@@ -0,0 +1,14 @@
+
+proc printf(frmt: cstring) {.varargs, importc, header: "<stdio.h>", cdecl.}
+proc exit(code: int) {.importc, header: "<stdlib.h>", cdecl.}
+
+{.push stack_trace: off, profiler:off.}
+
+proc rawoutput(s: string) =
+  printf("%s\n", s)
+
+proc panic(s: string) {.noreturn.} =
+  rawoutput(s)
+  exit(1)
+
+{.pop.}
diff --git a/tests/manyloc/standalone2/tavr.nim b/tests/manyloc/standalone2/tavr.nim
new file mode 100644
index 000000000..6cbc5c699
--- /dev/null
+++ b/tests/manyloc/standalone2/tavr.nim
@@ -0,0 +1,7 @@
+# bug #16404
+
+proc printf(frmt: cstring) {.varargs, header: "<stdio.h>", cdecl.}
+
+var x = 0
+inc x
+printf("hi %ld\n", x+4777)
diff --git a/tests/manyloc/standalone2/tavr.nim.cfg b/tests/manyloc/standalone2/tavr.nim.cfg
new file mode 100644
index 000000000..2a31618d0
--- /dev/null
+++ b/tests/manyloc/standalone2/tavr.nim.cfg
@@ -0,0 +1,5 @@
+--gc:arc
+--cpu:avr
+--os:standalone
+--compileOnly
+--threads:off
diff --git a/tests/metatype/tautoproc.nim b/tests/metatype/tautoproc.nim
deleted file mode 100644
index 2c8f6a3f7..000000000
--- a/tests/metatype/tautoproc.nim
+++ /dev/null
@@ -1,22 +0,0 @@
-discard """
-  output: "void"
-"""
-
-# bug #898
-
-import typetraits
-
-proc measureTime(e: auto) =
-  echo e.type.name
-
-proc generate(a: int): void =
-  discard
-
-proc runExample =
-  var builder: int = 0
-
-  measureTime:
-    builder.generate()
-
-measureTime:
-  discard
diff --git a/tests/metatype/tautotypetrait.nim b/tests/metatype/tautotypetrait.nim
deleted file mode 100644
index 84cd7b9f3..000000000
--- a/tests/metatype/tautotypetrait.nim
+++ /dev/null
@@ -1,14 +0,0 @@
-discard """
-  output: '''(Field0: "string", Field1: "string")'''
-"""
-
-# 7528
-import macros
-import typetraits
-
-macro bar*(n: untyped): typed =
-  result = newNimNode(nnkStmtList, n)
-  result.add(newCall("write", newIdentNode("stdout"), n))
-
-proc foo0[T](): auto = return (T.name, T.name)
-bar foo0[string]()
diff --git a/tests/metatype/tbindtypedesc.nim b/tests/metatype/tbindtypedesc.nim
index 039acfbe9..d9f034432 100644
--- a/tests/metatype/tbindtypedesc.nim
+++ b/tests/metatype/tbindtypedesc.nim
@@ -1,10 +1,5 @@
 discard """
-  msg: '''int int
-float float
-int int
-TFoo TFoo
-int float
-TFoo TFoo'''
+  output: '''ok'''
 """
 
 import typetraits
@@ -86,3 +81,15 @@ reject bindArg(int, int, 10, 20, 30, "test")
 reject bindArg(int, string, 10.0, 20, "test", "nest")
 reject bindArg(int, string, "test", "nest", 10, 20)
 
+echo "ok"
+
+#11058:
+template test(S: type, U: type) =
+  discard
+
+test(int, float)
+
+proc test2(S: type, U: type) =
+  discard
+
+test2(float, int)
diff --git a/tests/metatype/tcompilesregression.nim b/tests/metatype/tcompilesregression.nim
deleted file mode 100644
index 489cd06d6..000000000
--- a/tests/metatype/tcompilesregression.nim
+++ /dev/null
@@ -1,18 +0,0 @@
-discard """
-  output: '''ok'''
-"""
-
-# bug #5638
-
-type X = object
-  a_impl: int
-
-proc a(x: X): int =
-  x.a_impl
-
-var x: X
-assert(not compiles((block:
-  x.a = 1
-)))
-
-echo "ok"
diff --git a/tests/metatype/tcompositetypeclasses.nim b/tests/metatype/tcompositetypeclasses.nim
index 1cb86e4d7..43b6a57e4 100644
--- a/tests/metatype/tcompositetypeclasses.nim
+++ b/tests/metatype/tcompositetypeclasses.nim
@@ -19,7 +19,7 @@ var
   vfoo: TFoo[int, string]
   vbar: TFoo[string, string]
   vbaz: TFoo[int, int]
-  vnotbaz: TFoo[TObject, TObject]
+  vnotbaz: TFoo[RootObj, RootObj]
 
 proc foo(x: TFoo) = echo "foo"
 proc bar(x: TBar) = echo "bar"
@@ -30,7 +30,7 @@ accept bar(vbar)
 accept baz(vbar)
 accept baz(vbaz)
 
-#reject baz(vnotbaz) # XXX this really shouldn't compile
+reject baz(vnotbaz)
 reject bar(vfoo)
 
 # https://github.com/Araq/Nim/issues/517
diff --git a/tests/metatype/tconstraints.nim b/tests/metatype/tconstraints.nim
deleted file mode 100644
index 76e738a85..000000000
--- a/tests/metatype/tconstraints.nim
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-proc myGenericProc[T: object|tuple|int|ptr|ref|distinct](x: T): string =
-  result = $x
-
-type
-  TMyObj = tuple[x, y: int]
-
-var
-  x: TMyObj
-
-assert myGenericProc(232) == "232"
-assert myGenericProc(x) == "(x: 0, y: 0)"
-
-
diff --git a/tests/metatype/tcps.nim b/tests/metatype/tcps.nim
new file mode 100644
index 000000000..042e32bd6
--- /dev/null
+++ b/tests/metatype/tcps.nim
@@ -0,0 +1,35 @@
+discard """
+  output: '''10
+string'''
+"""
+
+# bug #18059
+type
+  C = ref object of RootObj
+    fn: ContProc
+    ex: ref Exception
+
+  ContProc = proc (c: C): C {.nimcall.}
+
+proc noop(c: C): C = c
+
+type
+  Env[T] = ref object of C
+    x: T
+
+proc foo_cont[U](c: C): C =
+  proc afterCall[V](c: C): C =
+    echo Env[V](c).x
+
+  c.fn = afterCall[U]
+  return noop(c)
+
+proc foo[T](x: T): C =
+  return Env[T](fn: foo_cont[T], x: x)
+
+proc tramp(c: sink C) =
+  while c != nil and c.fn != nil:
+    c = c.fn(c)
+
+tramp foo(10)
+tramp foo("string")
diff --git a/tests/metatype/tfieldaccessor.nim b/tests/metatype/tfieldaccessor.nim
deleted file mode 100644
index 7054dd22b..000000000
--- a/tests/metatype/tfieldaccessor.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-type
-  Test = object
-    x: int
-    case p: bool
-    of true:
-      a: int
-    else:
-      case q: bool
-      of true:
-        b: int
-      else:
-        discard
-
-proc f[T](t: typedesc[T]): int =
-  1
-
-assert Test.f == 1
diff --git a/tests/metatype/tmatrix.nim b/tests/metatype/tmatrix.nim
deleted file mode 100644
index 4fd32a932..000000000
--- a/tests/metatype/tmatrix.nim
+++ /dev/null
@@ -1,48 +0,0 @@
-discard """
-  file: "tmatrix.nim"
-  output: "111"
-"""
-# Test overloading of [] with multiple indices
-
-type
-  TMatrix* = object
-    data: seq[float]
-    fWidth, fHeight: int
-
-template `|`(x, y: int): untyped = y * m.fWidth + x
-
-proc createMatrix*(width, height: int): TMatrix =
-  result.fWidth = width
-  result.fHeight = height
-  newSeq(result.data, width*height)
-
-proc width*(m: TMatrix): int {.inline.} = return m.fWidth
-proc height*(m: TMatrix): int {.inline.} = return m.fHeight
-
-proc `[]`*(m: TMatrix, x, y: int): float {.inline.} =
-  result = m.data[x|y]
-
-proc `[]=`*(m: var TMatrix, x, y: int, val: float) {.inline.} =
-  m.data[x|y] = val
-
-proc `-|`*(m: TMatrix): TMatrix =
-  ## transposes a matrix
-  result = createMatrix(m.height, m.width)
-  for x in 0..m.width-1:
-    for y in 0..m.height-1: result[y,x] = m[x,y]
-
-#m.row(0, 2) # select row
-#m.col(0, 89) # select column
-
-const
-  w = 3
-  h = 20
-
-var m = createMatrix(w, h)
-for i in 0..w-1:
-  m[i, i] = 1.0
-
-for i in 0..w-1:
-  stdout.write(m[i,i]) #OUT 111
-
-
diff --git a/tests/metatype/tmatrix1.nim b/tests/metatype/tmatrix1.nim
index 0f325a17b..15913499f 100644
--- a/tests/metatype/tmatrix1.nim
+++ b/tests/metatype/tmatrix1.nim
@@ -8,10 +8,10 @@ type
   TMatrix2x2*[T] = TMatrixNM[range[0..1], range[0..1], T]
   TMatrix3x3*[T] = TMatrixNM[range[0..2], range[0..2], T]
 
-proc test*[T] (matrix: TMatrix2x2[T]) =
+proc test*[T](matrix: TMatrix2x2[T]) =
   echo "wrong proc called"
 
-proc test*[T] (matrix: TMatrix3x3[T]) =
+proc test*[T](matrix: TMatrix3x3[T]) =
   echo "right proc called"
 
 var matrix: TMatrix3x3[float]
diff --git a/tests/metatype/tmatrix3.nim b/tests/metatype/tmatrix3.nim
index a143e2bc9..28e85fcee 100644
--- a/tests/metatype/tmatrix3.nim
+++ b/tests/metatype/tmatrix3.nim
@@ -1,5 +1,5 @@
 discard """
-  output: ""
+  output: "\n"
 """
 
 type Matrix[M,N: static[int]] = array[M, array[N, float]]
diff --git a/tests/metatype/ttypeclasses.nim b/tests/metatype/tmeta_typeclasses.nim
index 720527088..720527088 100644
--- a/tests/metatype/ttypeclasses.nim
+++ b/tests/metatype/tmeta_typeclasses.nim
diff --git a/tests/metatype/tmetatype_issues.nim b/tests/metatype/tmetatype_issues.nim
new file mode 100644
index 000000000..d33d8dd31
--- /dev/null
+++ b/tests/metatype/tmetatype_issues.nim
@@ -0,0 +1,168 @@
+discard """
+output:'''
+void
+("string", "string")
+1 mod 7
+@[2, 2, 2, 2, 2]
+impl 2 called
+asd
+Foo
+Bar
+'''
+joinable: false
+"""
+
+import typetraits, macros
+
+
+block t898:
+  proc measureTime(e: auto) =
+    echo e.type.name
+
+  proc generate(a: int): void =
+    discard
+
+  proc runExample =
+    var builder: int = 0
+
+    measureTime:
+      builder.generate()
+
+  measureTime:
+    discard
+
+
+
+block t7528:
+  macro bar(n: untyped) =
+    result = newNimNode(nnkStmtList, n)
+    result.add(newCall("write", newIdentNode("stdout"), n))
+
+  proc foo0[T](): auto = return (T.name, T.name)
+  bar foo0[string]()
+  echo ""
+
+
+
+block t5638:
+  type X = object
+    a_impl: int
+
+  proc a(x: X): int =
+    x.a_impl
+
+  var x: X
+  assert(not compiles((block:
+    x.a = 1
+  )))
+
+
+
+block t3706:
+  type Modulo[M: static[int]] = distinct int
+  proc modulo(a: int, M: static[int]): Modulo[M] = Modulo[M](a %% M)
+  proc `+`[M: static[int]](a, b: Modulo[M]): Modulo[M] = (a.int + b.int).modulo(M)
+  proc `$`[M: static[int]](a: Modulo[M]): string = $(a.int) & " mod " & $(M)
+
+  let
+    a = 3.modulo(7)
+    b = 5.modulo(7)
+  echo a + b
+
+
+
+block t3144:
+  type IntArray[N: static[int]] = array[N, int]
+
+  proc `$`(a: IntArray): string = $(@(a))
+
+  proc `+=`[N: static[int]](a: var IntArray[N], b: IntArray[N]) =
+    for i in 0 ..< N:
+      a[i] += b[i]
+
+  proc zeros(N: static[int]): IntArray[N] =
+    for i in 0 ..< N:
+      result[i] = 0
+
+  proc ones(N: static[int]): IntArray[N] =
+    for i in 0 ..< N:
+      result[i] = 1
+
+  proc sum[N: static[int]](vs: seq[IntArray[N]]): IntArray[N] =
+    result = zeros(N)
+    for v in vs:
+      result += v
+
+  echo sum(@[ones(5), ones(5)])
+
+
+
+block t6533:
+  type Value[T: static[int]] = typedesc
+  proc foo(order: Value[1]): auto = 0
+  doAssert foo(Value[1]) == 0
+
+
+
+block t2266:
+  proc impl(op: static[int]) = echo "impl 1 called"
+  proc impl(op: static[int], init: int) = echo "impl 2 called"
+
+  macro wrapper2: untyped = newCall(bindSym"impl", newLit(0), newLit(0))
+
+  wrapper2() # Code generation for this fails.
+
+
+
+block t602:
+  type
+    TTest = object
+    TTest2 = object
+    TFoo = TTest | TTest2
+
+  proc f(src: ptr TFoo, dst: ptr TFoo) =
+    echo("asd")
+
+  var x: TTest
+  f(addr x, addr x)
+
+
+
+block t3338:
+  type
+    Base[T] = Foo[T] | Bar[T]
+
+    Foo[T] = ref object
+      x: T
+
+    Bar[T] = ref object
+      x: T
+
+  proc test[T](ks: Foo[T], x, y: T): T =
+    echo("Foo")
+    return x + y + ks.x
+
+  proc test[T](ks: Bar[T], x, y: T): T =
+    echo("Bar")
+    return x
+
+  proc add[T](ksa: Base[T]) =
+    var test = ksa.test(5, 10)
+    ksa.x = test
+
+  var t1 = Foo[int32]()
+  t1.add()
+  doAssert t1.x == 15
+
+  var t2 = Bar[int32]()
+  t2.add()
+  doAssert t2.x == 5
+
+block: # issue #24203
+  proc b(G: typedesc) =
+    type U = G
+  template s(h: untyped) = h
+  s(b(typeof (0, 0)))
+  b(seq[int])
+  b((int, int))
+  b(typeof (0, 0))
diff --git a/tests/metatype/tmetatype_various.nim b/tests/metatype/tmetatype_various.nim
new file mode 100644
index 000000000..45c74432d
--- /dev/null
+++ b/tests/metatype/tmetatype_various.nim
@@ -0,0 +1,73 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  output: '''[1, 0, 0, 0, 0, 0, 0, 0] CTBool[Ct[system.uint32]]'''
+"""
+
+block tconstraints:
+  proc myGenericProc[T: object|tuple|int|ptr|ref|distinct](x: T): string =
+    result = $x
+
+  type TMyObj = tuple[x, y: int]
+
+  var x: TMyObj
+
+  assert myGenericProc(232) == "232"
+  assert myGenericProc(x) == "(x: 0, y: 0)"
+
+
+
+block tfieldaccessor:
+  type
+    Test = object
+      x: int
+      case p: bool
+      of true:
+        a: int
+      else:
+        case q: bool
+        of true:
+          b: int
+        else:
+          discard
+
+  proc f[T](t: typedesc[T]): int =
+    1
+
+  assert Test.f == 1
+
+
+
+block tprocbothmeta:
+  proc myFun[A](x: A): auto =
+    result = float(x+10)
+
+  proc myMap[T,S](sIn: seq[T], f: proc (q: T): S): seq[S] =
+    result = newSeq[S](sIn.len)
+    for i in 0..<sIn.len:
+      result[i] = f(sIn[i])
+
+  assert myMap(@[1,2,3], myFun) == @[11.0, 12.0, 13.0]
+
+
+# https://github.com/nim-lang/Nim/issues/13646
+
+type
+  BaseUint* = SomeUnsignedInt or byte
+  Ct*[T] = distinct T
+    ## Constant-Time wrapper
+    ## Only constant-time operations in particular the ternary operator equivalent
+    ##   condition: if true: a else: b
+    ## are allowed
+
+  CTBool*[T] = distinct range[T(0)..T(1)]
+    ## Constant-Time boolean wrapper
+
+var x: array[8, CTBool[Ct[uint32]]]
+x[0] = (CTBool[Ct[uint32]])(1)
+echo x.repr, " ", typeof(x[0])
+
+block: # bug #23139
+  type Foo = enum a, b
+
+  var x: range[a..b]
+  doAssert (repr x) == "a"
diff --git a/tests/metatype/tmetatypematrix.nim b/tests/metatype/tmetatypematrix.nim
new file mode 100644
index 000000000..3ddbc239f
--- /dev/null
+++ b/tests/metatype/tmetatypematrix.nim
@@ -0,0 +1,46 @@
+discard """
+  output: "1.01.01.0"
+"""
+# Test overloading of [] with multiple indices
+
+type
+  TMatrix* = object
+    data: seq[float]
+    fWidth, fHeight: int
+
+template `|`(x, y: int): untyped = y * m.fWidth + x
+
+proc createMatrix*(width, height: int): TMatrix =
+  result.fWidth = width
+  result.fHeight = height
+  newSeq(result.data, width*height)
+
+proc width*(m: TMatrix): int {.inline.} = return m.fWidth
+proc height*(m: TMatrix): int {.inline.} = return m.fHeight
+
+proc `[]`*(m: TMatrix, x, y: int): float {.inline.} =
+  result = m.data[x|y]
+
+proc `[]=`*(m: var TMatrix, x, y: int, val: float) {.inline.} =
+  m.data[x|y] = val
+
+proc `-|`*(m: TMatrix): TMatrix =
+  ## transposes a matrix
+  result = createMatrix(m.height, m.width)
+  for x in 0..m.width-1:
+    for y in 0..m.height-1: result[y,x] = m[x,y]
+
+#m.row(0, 2) # select row
+#m.col(0, 89) # select column
+
+const
+  w = 3
+  h = 20
+
+var m = createMatrix(w, h)
+for i in 0..w-1:
+  m[i, i] = 1.0
+
+for i in 0..w-1:
+  stdout.write(m[i,i]) #OUT 111
+stdout.write "\n"
diff --git a/tests/metatype/tmodulo.nim b/tests/metatype/tmodulo.nim
deleted file mode 100644
index 08bcc7935..000000000
--- a/tests/metatype/tmodulo.nim
+++ /dev/null
@@ -1,20 +0,0 @@
-discard """
-  output: '''1 mod 7'''
-"""
-
-# bug #3706
-
-type Modulo[M: static[int]] = distinct int
-
-proc modulo(a: int, M: static[int]): Modulo[M] = Modulo[M](a %% M)
-
-proc `+`[M: static[int]](a, b: Modulo[M]): Modulo[M] = (a.int + b.int).modulo(M)
-
-proc `$`*[M: static[int]](a: Modulo[M]): string = $(a.int) & " mod " & $(M)
-
-when isMainModule:
-  let
-    a = 3.modulo(7)
-    b = 5.modulo(7)
-  echo a + b
-
diff --git a/tests/metatype/tprocbothmeta.nim b/tests/metatype/tprocbothmeta.nim
deleted file mode 100644
index ba061dda2..000000000
--- a/tests/metatype/tprocbothmeta.nim
+++ /dev/null
@@ -1,10 +0,0 @@
-
-proc myFun[A](x: A): auto =
-  result = float(x+10)
-
-proc myMap[T,S](sIn: seq[T], f: proc (q: T): S): seq[S] =
-  result = newSeq[S](sIn.len)
-  for i in 0..<sIn.len:
-    result[i] = f(sIn[i])
-
-assert myMap(@[1,2,3], myFun) == @[11.0, 12.0, 13.0]
diff --git a/tests/metatype/tsemistatic.nim b/tests/metatype/tsemistatic.nim
index 3f36abde9..56b9a6218 100644
--- a/tests/metatype/tsemistatic.nim
+++ b/tests/metatype/tsemistatic.nim
@@ -1,5 +1,5 @@
 discard """
-  msg: "static 10\ndynamic\nstatic 20\n"
+  nimout: "static 10\ndynamic\nstatic 20\n"
   output: "s\nd\nd\ns"
 """
 
diff --git a/tests/metatype/tshacontext.nim b/tests/metatype/tshacontext.nim
new file mode 100644
index 000000000..7f649d202
--- /dev/null
+++ b/tests/metatype/tshacontext.nim
@@ -0,0 +1,44 @@
+
+# bug #14136
+
+type
+  MDigest*[bits: static[int]] = object
+    ## Message digest type
+    data*: array[bits div 8, byte]
+
+  Sha2Context*[bits: static[int],
+               bsize: static[int],
+               T: uint32|uint64] = object
+    count: array[2, T]
+    state: array[8, T]
+    buffer: array[bsize, byte]
+
+  sha256* = Sha2Context[256, 64, uint32]
+
+template hmacSizeBlock*(h: typedesc): int =
+  when (h is Sha2Context):
+    int(h.bsize)
+  else:
+    {.fatal: "Choosen hash primitive is not yet supported!".}
+
+type
+  HMAC*[HashType] = object
+    ## HMAC context object.
+    mdctx: HashType
+    opadctx: HashType
+    ipad: array[HashType.hmacSizeBlock, byte]
+    opad: array[HashType.hmacSizeBlock, byte]
+
+func hkdfExtract*[T;S,I: char|byte](ctx: var HMAC[T],
+                     prk: var MDigest[T.bits], # <------- error here "Error: type expected"
+                     salt: openArray[S],
+                     ikm: openArray[I]
+                    ) =
+  discard
+
+var ctx: HMAC[sha256]
+var prk: MDigest[sha256.bits]
+let salt = [byte 0x00, 0x01, 0x02]
+let ikm = "CompletelyRandomInput"
+
+ctx.hkdfExtract(prk, salt, ikm)
diff --git a/tests/metatype/tstatic_generic_typeclass.nim b/tests/metatype/tstatic_generic_typeclass.nim
new file mode 100644
index 000000000..0e9256a62
--- /dev/null
+++ b/tests/metatype/tstatic_generic_typeclass.nim
@@ -0,0 +1,30 @@
+type MyThing[T: static int] = object
+  when T == 300:
+    a: int
+
+var a = MyThing[300]()
+proc doThing(myThing: MyThing): string = $myThing
+proc doOtherThing[T](myThing: MyThing[T]): string = $myThing
+assert doThing(a) == $a
+assert doThing(MyThing[0]()) == $MyThing[0]()
+assert doOtherThing(a) == "(a: 0)"
+
+type
+  Backend* = enum
+    Cpu,
+    Cuda
+
+  Tensor*[B: static[Backend]; T] = object
+    shape: seq[int]
+    strides: seq[int]
+    offset: int
+    when B == Backend.Cpu:
+      data: seq[T]
+    else:
+      data_ptr: ptr T
+
+template shape*(t: Tensor): seq[int] =
+  t.shape
+
+
+assert Tensor[Cpu, int]().shape == @[]
diff --git a/tests/metatype/tstatic_ones.nim b/tests/metatype/tstatic_ones.nim
deleted file mode 100644
index 73a88447d..000000000
--- a/tests/metatype/tstatic_ones.nim
+++ /dev/null
@@ -1,28 +0,0 @@
-discard """
-  output: "@[2, 2, 2, 2, 2]"
-"""
-
-# bug #3144
-
-type IntArray[N: static[int]] = array[N, int]
-
-proc `$`(a: IntArray): string = $(@(a))
-
-proc `+=`[N: static[int]](a: var IntArray[N], b: IntArray[N]) =
-  for i in 0 .. < N:
-    a[i] += b[i]
-
-proc zeros(N: static[int]): IntArray[N] =
-  for i in 0 .. < N:
-    result[i] = 0
-
-proc ones(N: static[int]): IntArray[N] =
-  for i in 0 .. < N:
-    result[i] = 1
-
-proc sum[N: static[int]](vs: seq[IntArray[N]]): IntArray[N] =
-  result = zeros(N)
-  for v in vs:
-    result += v
-
-echo sum(@[ones(5), ones(5)])
diff --git a/tests/metatype/tstatic_overloading.nim b/tests/metatype/tstatic_overloading.nim
deleted file mode 100644
index 9c065e48d..000000000
--- a/tests/metatype/tstatic_overloading.nim
+++ /dev/null
@@ -1,10 +0,0 @@
-# bug #2266
-
-import macros
-
-proc impl(op: static[int]) = echo "impl 1 called"
-proc impl(op: static[int], init: int) = echo "impl 2 called"
-
-macro wrapper2: untyped = newCall(bindSym"impl", newLit(0), newLit(0))
-
-wrapper2() # Code generation for this fails.
diff --git a/tests/metatype/tstaticparammacro.nim b/tests/metatype/tstaticparammacro.nim
index 02021185f..bcf28d331 100644
--- a/tests/metatype/tstaticparammacro.nim
+++ b/tests/metatype/tstaticparammacro.nim
@@ -1,14 +1,14 @@
 discard """
-  msg: '''letters
+  nimout: '''letters
 aa
 bb
 numbers
 11
 22
 AST a
-[(11, 22), (33, 44)]
+@[(c: 11, d: 22), (c: 33, d: 44)]
 AST b
-(e: [55, 66], f: [77, 88])
+(e: @[55, 66], f: @[77, 88])
 55
 10
 20Test
@@ -44,10 +44,10 @@ const
   b : Tb = (@[55,66], @[77, 88])
 
 macro mA(data: static[Ta]): untyped =
-  echo "AST a \n", repr(data)
+  echo "AST a\n", repr(data)
 
 macro mB(data: static[Tb]): untyped =
-  echo "AST b \n", repr(data)
+  echo "AST b\n", repr(data)
   echo data.e[0]
 
 mA(a)
diff --git a/tests/metatype/tstaticparams.nim b/tests/metatype/tstaticparams.nim
index ad6aa6589..de80d46ae 100644
--- a/tests/metatype/tstaticparams.nim
+++ b/tests/metatype/tstaticparams.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tstaticparams.nim"
   output: "abracadabra\ntest\n3\n15\n4\n2\nfloat\n3\nfloat\nyin\nyang\n2\n4\n4\n2\n3"
 """
 
@@ -140,7 +139,7 @@ dontBind1 bb_2
 dontBind2 bb_1
 dontBind2 bb_2
 
-# https://github.com/nim-lang/Nim/issues/4524 
+# https://github.com/nim-lang/Nim/issues/4524
 const
   size* = 2
 
@@ -173,3 +172,26 @@ echo inSize([
   [4, 5, 6]
 ])
 
+block: # #12864
+  template fun() =
+    type Object = object
+    proc fun(f: Object): int = 1
+    proc fun(f: static[int]): int = 2
+    doAssert fun(Object()) == 1
+
+    var a: Object
+    doAssert fun(a) == 1
+
+    proc fun2(f: Object): int = 1
+    proc fun2(f: static[Object]): int = 2
+    doAssert fun2(Object()) == 2
+    doAssert fun2(a) == 1
+    const a2 = Object()
+    doAssert fun2(a2) == 2
+
+  fun()
+  static: fun()
+
+when true: #12864 original snippet
+  import times
+  discard times.format(initDateTime(30, mMar, 2017, 0, 0, 0, 0, utc()), TimeFormat())
diff --git a/tests/metatype/tstaticvector.nim b/tests/metatype/tstaticvector.nim
index 1a7bdeafe..85a66974b 100644
--- a/tests/metatype/tstaticvector.nim
+++ b/tests/metatype/tstaticvector.nim
@@ -1,9 +1,10 @@
 discard """
+  matrix: "--mm:orc"
   output: '''0
 0
 2
 100
-30.0 [data = [2.0]]
+30.0 TVec[1, system.float32](data: [2.0])
 '''
 """
 
@@ -61,7 +62,7 @@ proc row*(a: TMatrix; i: int): auto =
 
 proc col*(a: TMatrix; j: int): auto =
   result = TVec[TMatrix.N, TMatrix.T]()
-  for idx in 0 .. <TMatrix.N:
+  for idx in 0 ..< TMatrix.N:
     result.data[idx] = a.data[(TMatrix.N * (idx)) + (j-1)]
 
 proc mul*(a: TMat4f; b: TMat4f): TMat4f =
diff --git a/tests/metatype/ttensor.nim b/tests/metatype/ttensor.nim
index f2f24ba8c..89a0bf007 100644
--- a/tests/metatype/ttensor.nim
+++ b/tests/metatype/ttensor.nim
@@ -1,13 +1,11 @@
 discard """
-  output: '''before tensor2:
+  output: '''
+before tensor2:
 [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0]
-
 before tensor3:
 [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0]
-
 after tensor3:
 [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0]
-
 a3:
 [1000.0, 1001.0, 1002.0, 1003.0, 1004.0, 1005.0, 1006.0, 1007.0, 1008.0, 1009.0, 1010.0, 1011.0, 1012.0, 1013.0, 1014.0, 1015.0, 1016.0, 1017.0, 1018.0, 1019.0, 1020.0, 1021.0, 1022.0, 1023.0, 1024.0, 1025.0, 1026.0]'''
 """
diff --git a/tests/metatype/ttypebar.nim b/tests/metatype/ttypebar.nim
deleted file mode 100644
index 304dfffcb..000000000
--- a/tests/metatype/ttypebar.nim
+++ /dev/null
@@ -1,14 +0,0 @@
-
-# bug #602
-
-type
-  TTest = object
-  TTest2* = object
-  TFoo = TTest | TTest2
-
-proc f(src: ptr TFoo, dst: ptr TFoo) =
-  echo("asd")
-
-var x: TTest
-f(addr x, addr x)
-
diff --git a/tests/metatype/ttypedesc1.nim b/tests/metatype/ttypedesc1.nim
index e9eee581f..549dbc62a 100644
--- a/tests/metatype/ttypedesc1.nim
+++ b/tests/metatype/ttypedesc1.nim
@@ -5,15 +5,16 @@ type
     x: T
     y: U
 
-proc getTypeName(t: typedesc): string = t.name
+proc getTypeName1(t: typedesc): string = t.name
+proc getTypeName2(t: type): string = t.name
 
-proc foo(T: typedesc[float], a: auto): string =
+proc foo(T: type float, a: auto): string =
   result = "float " & $(a.len > 5)
 
 proc foo(T: typedesc[TFoo], a: int): string =
   result = "TFoo "  & $(a)
 
-proc foo(T: typedesc[int or bool]): string =
+proc foo(T: type[int or bool]): string =
   var a: T
   a = 10
   result = "int or bool " & ($a)
@@ -23,8 +24,8 @@ template foo(T: typedesc[seq]): string = "seq"
 test "types can be used as proc params":
   # XXX: `check` needs to know that TFoo[int, float] is a type and
   # cannot be assigned for a local variable for later inspection
-  check ((string.getTypeName == "string"))
-  check ((getTypeName(int) == "int"))
+  check ((string.getTypeName1 == "string"))
+  check ((getTypeName2(int) == "int"))
 
   check ((foo(TFoo[int, float], 1000) == "TFoo 1000"))
 
@@ -37,6 +38,31 @@ test "types can be used as proc params":
   check ((foo(seq[int]) == "seq"))
   check ((foo(seq[TFoo[bool, string]]) == "seq"))
 
-when false:
-  proc foo(T: typedesc[seq], s: T) = nil
+template accept(x) =
+  static: assert(compiles(x))
 
+template reject(x) =
+  static: assert(not compiles(x))
+
+var
+  si: seq[int]
+  ss: seq[string]
+
+proc foo(T: typedesc[seq], s: T) =
+  discard
+
+accept:
+  foo seq[int], si
+
+reject:
+  foo seq[string], si
+
+reject:
+  foo seq[int], ss
+
+# issue #12398
+reject:
+  let xs = [int, float, string]
+
+reject:
+  let data = @[int, typedesc]
diff --git a/tests/metatype/ttypedesc2.nim b/tests/metatype/ttypedesc2.nim
index 4b6cfe6bc..96dab9052 100644
--- a/tests/metatype/ttypedesc2.nim
+++ b/tests/metatype/ttypedesc2.nim
@@ -34,3 +34,60 @@ when true:
 type Point[T] = tuple[x, y: T]
 proc origin(T: typedesc): Point[T] = discard
 discard origin(int)
+
+block: # issue #12704
+  const a = $("a", "b")
+  proc fun() =
+    const str = $int
+    let b = $(str, "asdf")
+  fun()
+
+# https://github.com/nim-lang/Nim/issues/7516
+import typetraits
+
+block: #issue #12704
+  const a = $("a", "b")
+  proc fun() =
+    const str = name(int)
+    let b = $(str, "asdf")
+  fun()
+
+proc hasDefault1(T: type = int): auto = return T.name
+doAssert hasDefault1(int) == "int"
+doAssert hasDefault1(string) == "string"
+doAssert hasDefault1() == "int"
+
+proc hasDefault2(T = string): auto = return T.name
+doAssert hasDefault2(int) == "int"
+doAssert hasDefault2(string) == "string"
+doAssert hasDefault2() == "string"
+
+
+# bug #9195
+type
+  Error = enum
+    erA, erB, erC
+  Result[T, U] = object
+    x: T
+    u: U
+  PB = object
+
+proc decodeUVarint*(itzzz: typedesc[SomeUnsignedInt],
+                    data: openArray[char]): Result[itzzz, Error] =
+  result = Result[itzzz, Error](x: 0, u: erC)
+
+discard decodeUVarint(uint32, "abc")
+
+type
+  X = object
+  Y[T] = object
+
+proc testObj(typ: typedesc[object]): Y[typ] =
+  discard
+
+discard testObj(X)
+
+
+#bug 12804
+import typetraits
+discard int.name[0]
\ No newline at end of file
diff --git a/tests/metatype/ttypedesc3.nim b/tests/metatype/ttypedesc3.nim
index 9f19bd6e3..d3a58853d 100644
--- a/tests/metatype/ttypedesc3.nim
+++ b/tests/metatype/ttypedesc3.nim
@@ -1,19 +1,53 @@
+discard """
+  matrix: "--mm:arc; --mm:refc"
+  output: '''
+proc Base
+proc Child
+method Base
+method Child
+yield Base
+yield Child
+12
+1
+'''
+"""
+
 import typetraits
 
 type
   Base = object of RootObj
   Child = object of Base
 
-proc pr(T: typedesc[Base]) = echo "proc " & T.name
-method me(T: typedesc[Base]) = echo "method " & T.name
-iterator it(T: typedesc[Base]): auto = yield "yield " & T.name
+proc pr(T: type[Base]) = echo "proc " & T.name
+method me(T: type[Base]) = echo "method " & T.name
+iterator it(T: type[Base]): auto = yield "yield " & T.name
 
 Base.pr
 Child.pr
 
 Base.me
-when false:
-  Child.me #<- bug #2710
+Child.me #<- bug #2710
 
 for s in Base.it: echo s
 for s in Child.it: echo s #<- bug #2662
+
+
+# bug #11747
+
+type
+  MyType = object
+    a: int32
+    b: int32
+    c: int32
+
+  MyRefType = ref MyType
+
+echo sizeof(default(MyRefType)[])
+
+type
+  Foo[T] = object
+    val: T
+
+var x: Foo[int].val
+inc(x)
+echo x
diff --git a/tests/metatype/ttypedescnotnimnode.nim b/tests/metatype/ttypedescnotnimnode.nim
new file mode 100644
index 000000000..52a04815b
--- /dev/null
+++ b/tests/metatype/ttypedescnotnimnode.nim
@@ -0,0 +1,21 @@
+discard """
+  errormsg: "type mismatch: got <NimNode> but expected 'typedesc'"
+  line: 14
+"""
+
+import macros
+
+# This is the same example as ttypeselectors but using a proc instead of a macro
+# Instead of type mismatch for macro, proc just failed with internal error: getTypeDescAux(tyNone)
+# https://github.com/nim-lang/Nim/issues/7231
+
+proc getBase2*(bits: static[int]): typedesc =
+  if bits == 128:
+    result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint64"))
+  else:
+    result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint32"))
+
+type
+  MpUint2*[bits: static[int]] = getbase2(bits)
+# technically shouldn't error until instantiation, so instantiate it
+var x: MpUint2[123]
diff --git a/tests/metatype/ttypeor.nim b/tests/metatype/ttypeor.nim
deleted file mode 100644
index f5acce772..000000000
--- a/tests/metatype/ttypeor.nim
+++ /dev/null
@@ -1,35 +0,0 @@
-discard """
-  output: '''Foo
-Bar'''
-"""
-
-# bug #3338
-
-type
-  Base[T] = Foo[T] | Bar[T]
-
-  Foo[T] = ref object
-    x: T
-
-  Bar[T] = ref object
-    x: T
-
-proc test[T](ks: Foo[T], x, y: T): T =
-  echo("Foo")
-  return x + y + ks.x
-
-proc test[T](ks: Bar[T], x, y: T): T =
-  echo("Bar")
-  return x
-
-proc add[T](ksa: Base[T]) =
-  var test = ksa.test(5, 10)
-  ksa.x = test
-
-var t1 = Foo[int32]()
-t1.add()
-doAssert t1.x == 15
-
-var t2 = Bar[int32]()
-t2.add()
-doAssert t2.x == 5
diff --git a/tests/metatype/ttypeselectors.nim b/tests/metatype/ttypeselectors.nim
index 1209fe78f..d0843511d 100644
--- a/tests/metatype/ttypeselectors.nim
+++ b/tests/metatype/ttypeselectors.nim
@@ -5,16 +5,16 @@ output: "8\n8\n4"
 import
   macros, typetraits
 
-template selectType(x: int): typeDesc =
+template selectType(x: int): type =
   when x < 10:
     int
   else:
     string
 
-template simpleTypeTempl: typeDesc =
+template simpleTypeTempl: type =
   string
 
-macro typeFromMacro: typedesc = string
+macro typeFromMacro: type = bindSym"string"
 
 # The tests below check that the result variable of the
 # selected type matches the literal types in the code:
@@ -45,7 +45,7 @@ proc t6*(x: type(t3(0))): type(t1(0)) =
 proc t7*(x: int): type($x) =
   result = "test"
 
-# This is a more compicated example involving a type
+# This is a more complicated example involving a type
 # selection through a macro:
 # https://github.com/nim-lang/Nim/issues/7230
 
@@ -98,4 +98,3 @@ var c: Bar[32]
 echo sizeof(a)
 echo sizeof(b)
 echo sizeof(c)
-
diff --git a/tests/metatype/ttypetraits.nim b/tests/metatype/ttypetraits.nim
index 106257828..0523390a7 100644
--- a/tests/metatype/ttypetraits.nim
+++ b/tests/metatype/ttypetraits.nim
@@ -1,60 +1,404 @@
-discard """
-  msg:    "int\nstring\nTBar[int]"
-  output: "int\nstring\nTBar[int]\nint\nrange 0..2(int)\nstring"
-  disabled: true
-"""
-
 import typetraits
+import macros
+
+block: # toUnsigned, toSigned
+  var a1: toSigned(int16)
+  doAssert a1 is int16
+  var a2: toSigned(uint16)
+  doAssert $a2.typeof == "int16"
+  doAssert toSigned(uint32) is int32
+  doAssert uint64.toSigned is int64
+  doAssert int64.toSigned is int64
+  doAssert int64.toUnsigned is uint64
+  doAssert int.toUnsigned is uint
+  doAssert $uint.toUnsigned == "uint"
+  # disallowed for now
+  doAssert not compiles(toUnsigned(range[0..7]))
+  doAssert not compiles(toSigned(range[0..7]))
+
+block: # isNamedTuple
+  type Foo1 = (a:1,).type
+  type Foo2 = (Field0:1,).type
+  type Foo3 = ().type
+  type Foo4 = object
+  type Foo5[T] = tuple[x:int, y: T]
+  type Foo6[T] = (T,)
 
-# simple case of type trait usage inside/outside of static blocks
-proc foo(x) =
+  doAssert (a:1,).type.isNamedTuple
+  doAssert Foo1.isNamedTuple
+  doAssert Foo2.isNamedTuple
+  doAssert isNamedTuple(tuple[key: int])
+  doAssert not Foo3.isNamedTuple
+  doAssert not Foo4.isNamedTuple
+  doAssert not (1,).type.isNamedTuple
+  doAssert isNamedTuple(Foo5[int8])
+  doAssert not isNamedTuple(Foo5)
+  doAssert not isNamedTuple(Foo6[int8])
+
+proc typeToString*(t: typedesc, prefer = "preferTypeName"): string {.magic: "TypeTrait".}
+  ## Returns the name of the given type, with more flexibility than `name`,
+  ## and avoiding the potential clash with a variable named `name`.
+  ## prefer = "preferResolved" will resolve type aliases recursively.
+  # Move to typetraits.nim once api stabilized.
+
+block: # name, `$`
   static:
-    var t = type(x)
-    echo t.name
+    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)
+
+  doAssert $(int,) == "(int,)"
+  doAssert $(1,) == "(1,)" # just for comparison to make sure it has same structure
+  doAssert $tuple[] == "tuple[]"
+  doAssert $(int,) == "(int,)"
+  doAssert $(int, float) == "(int, float)"
+  doAssert $((int), tuple[], tuple[a: uint], tuple[a: uint, b: float], (int,), (int, float)) ==
+    "(int, tuple[], tuple[a: uint], tuple[a: uint, b: float], (int,), (int, float))"
+
+block: # typeToString
+  type MyInt = int
+  type
+    C[T0, T1] = object
+  type C2=C # alias => will resolve as C
+  type C2b=C # alias => will resolve as C (recursively)
+  type C3[U,V] = C[V,U]
+  type C4[X] = C[X,X]
+  template name2(T): string = typeToString(T, "preferResolved")
+  doAssert MyInt.name2 == "int"
+  doAssert C3[MyInt, C2b].name2 == "C3[int, C]"
+    # C3 doesn't get resolved to C, not an alias (nor does C4)
+  doAssert C2b[MyInt, C4[cstring]].name2 == "C[int, C4[cstring]]"
+  doAssert C4[MyInt].name2 == "C4[int]"
+  when BiggestFloat is float and cint is int:
+    doAssert C2b[cint, BiggestFloat].name2 == "C3[int, C3[float, int32]]"
+
+  template name3(T): string = typeToString(T, "preferMixed")
+  doAssert MyInt.name3 == "MyInt{int}"
+  doAssert (tuple[a: MyInt, b: float]).name3 == "tuple[a: MyInt{int}, b: float]"
+  doAssert (tuple[a: C2b[MyInt, C4[cstring]], b: cint, c: float]).name3 ==
+    "tuple[a: C[MyInt{int}, C4[cstring]], b: cint{int32}, c: float]"
+
+  macro fn(): string =
+    # not 100% sure whether this should even compile; if some PR breaks this test,
+    # this could be revisited, maybe.
+    newLit $($getType(untyped), $getType(typed))
+  doAssert fn() == """("untyped", "typed")"""
+
+block distinctBase:
+  block:
+    type
+      Foo[T] = distinct seq[T]
+    var a: Foo[int]
+    doAssert a.type.distinctBase is seq[int]
+    doAssert seq[int].distinctBase is seq[int]
+    doAssert "abc".distinctBase == "abc"
+
+  block:
+    # simplified from https://github.com/nim-lang/Nim/pull/8531#issuecomment-410436458
+    macro uintImpl(bits: static[int]): untyped =
+      if bits >= 128:
+        let inner = getAST(uintImpl(bits div 2))
+        result = newTree(nnkBracketExpr, ident("UintImpl"), inner)
+      else:
+        result = ident("uint64")
+
+    type
+      BaseUint = UintImpl or SomeUnsignedInt
+      UintImpl[Baseuint] = object
+      Uint[bits: static[int]] = distinct uintImpl(bits)
+
+    doAssert Uint[128].distinctBase is UintImpl[uint64]
+
+    block:
+      type
+        AA = distinct seq[int]
+        BB = distinct string
+        CC = distinct int
+        AAA = AA
+
+      static:
+        var a2: AAA
+        var b2: BB
+        var c2: CC
+
+        doAssert(a2 is distinct)
+        doAssert(b2 is distinct)
+        doAssert(c2 is distinct)
+
+        doAssert($distinctBase(typeof(a2)) == "seq[int]")
+        doAssert($distinctBase(typeof(b2)) == "string")
+        doAssert($distinctBase(typeof(c2)) == "int")
+
+block: # rangeBase
+  {.push warningAsError[EnumConv]: on.}
+  proc foo[T: not range](x: T): string =
+    $T & "(" & $x & ")"
+  proc foo[T: range](x: T): string =
+    "ranged(" & $low(T) & ".." & $high(T) & " of " & $rangeBase(T) & ") " & foo(rangeBase(x))
+  doAssert foo(123) == "int(123)"
+  type IntRange = range[0..3]
+  let x: IntRange = 2
+  doAssert foo(x) == "ranged(0..3 of int) int(2)"
+  type E = enum a, b, c, d, e, f
+  type EnumRange = range[c..e]
+  let y: EnumRange = d
+  doAssert foo(y) == "ranged(c..e of E) E(d)"
+  let z: range['a'..'z'] = 'g'
+  doAssert foo(z) == "ranged(a..z of char) char(g)"
+  {.pop.}
+
+  # works only with #24037:
+  var toChange: range[0..3] = 1
+  proc bar[T: int and not range](y: var T) =
+    inc y
+  doAssert not compiles(bar(toChange))
+  bar(rangeBase(toChange))
+  doAssert toChange == 2
 
-  echo x.type.name
+block: # tupleLen
+  doAssert not compiles(tupleLen(int))
+
+  type
+    MyTupleType = (int,float,string)
+
+  static: doAssert MyTupleType.tupleLen == 3
+
+  type
+    MyGenericTuple[T] = (T,int,float)
+    MyGenericAlias = MyGenericTuple[string]
+  static: doAssert MyGenericAlias.tupleLen == 3
+
+  type
+    MyGenericTuple2[T,U] = (T,U,string)
+    MyGenericTuple2Alias[T] =  MyGenericTuple2[T,int]
+
+    MyGenericTuple2Alias2 =   MyGenericTuple2Alias[float]
+  static: doAssert MyGenericTuple2Alias2.tupleLen == 3
+
+  static: doAssert (int, float).tupleLen == 2
+  static: doAssert (1, ).tupleLen == 1
+  static: doAssert ().tupleLen == 0
+
+  let x = (1,2,)
+  doAssert x.tupleLen == 2
+  doAssert ().tupleLen == 0
+  doAssert (1,).tupleLen == 1
+  doAssert (int,).tupleLen == 1
+  doAssert type(x).tupleLen == 2
+  doAssert type(x).default.tupleLen == 2
+  type T1 = (int,float)
+  type T2 = T1
+  doAssert T2.tupleLen == 2
+
+block genericParams:
+  type Foo[T1, T2]=object
+  doAssert genericParams(Foo[float, string]) is (float, string)
+  type Foo1 = Foo[float, int]
+  doAssert genericParams(Foo1) is (float, int)
+  type Foo2 = Foo[float, Foo1]
+  doAssert genericParams(Foo2) is (float, Foo[float, int])
+  doAssert genericParams(Foo2) is (float, Foo1)
+  doAssert genericParams(Foo2).get(1) is Foo1
+  doAssert (int,).get(0) is int
+  doAssert (int, float).get(1) is float
+
+  type Bar[N: static int, T] = object
+  type Bar3 = Bar[3, float]
+  doAssert genericParams(Bar3) is (StaticParam[3], float)
+  doAssert genericParams(Bar3).get(0) is StaticParam
+  doAssert genericParams(Bar3).get(0).value == 3
+  doAssert genericParams(Bar[3, float]).get(0).value == 3
+  static: doAssert genericParams(Bar[3, float]).get(0).value == 3
+
+  type
+    VectorElementType = SomeNumber | bool
+    Vec[N: static[int], T: VectorElementType] = object
+      arr: array[N, T]
+    Vec4[T: VectorElementType] = Vec[4,T]
+    Vec4f = Vec4[float32]
+
+    MyTupleType = (int,float,string)
+    MyGenericTuple[T] = (T,int,float)
+    MyGenericAlias = MyGenericTuple[string]
+    MyGenericTuple2[T,U] = (T,U,string)
+    MyGenericTuple2Alias[T] =  MyGenericTuple2[T,int]
+    MyGenericTuple2Alias2 =   MyGenericTuple2Alias[float]
+
+  doAssert genericParams(MyGenericAlias) is (string,)
+  doAssert genericHead(MyGenericAlias) is MyGenericTuple
+  doAssert genericParams(MyGenericTuple2Alias2) is (float,)
+  doAssert genericParams(MyGenericTuple2[float, int]) is (float, int)
+  doAssert genericParams(MyGenericAlias) is (string,)
+  doAssert genericParams(Vec4f) is (float32,)
+  doAssert genericParams(Vec[4, bool]) is (StaticParam[4], bool)
+
+  block:
+    type Foo[T1, T2]=object
+    doAssert genericParams(Foo[float, string]) is (float, string)
+    type Bar[N: static float, T] = object
+    doAssert genericParams(Bar[1.0, string]) is (StaticParam[1.0], string)
+    type Bar2 = Bar[2.0, string]
+    doAssert genericParams(Bar2) is (StaticParam[2.0], string)
+    type Bar3 = Bar[1.0 + 2.0, string]
+    doAssert genericParams(Bar3) is (StaticParam[3.0], string)
+
+    const F = 5.0
+    type Bar4 = Bar[F, string]
+    doAssert genericParams(Bar4) is (StaticParam[5.0], string)
+    doAssert genericParams(Bar[F, string]) is (StaticParam[5.0], string)
+
+  block typeof:
+    var
+      a: seq[int]
+      b: array[42, float]
+      c: array[char, int]
+      d: array[1..2, char]
+
+    doAssert genericParams(typeof(a)).get(0) is int
+    doAssert genericParams(typeof(b)) is (range[0..41], float)
+    doAssert genericParams(typeof(c)) is (char, int)
+    doAssert genericParams(typeof(d)) is (range[1..2], char)
+
+  block nestedContainers:
+    doAssert genericParams(seq[Foo[string, float]]).get(0) is Foo[string, float]
+    doAssert genericParams(array[10, Foo[Bar[1, int], Bar[2, float]]]) is (StaticParam[10], Foo[Bar[1, int], Bar[2, float]])
+    doAssert genericParams(array[1..9, int]) is (range[1..9], int)
+
+##############################################
+# bug 13095
 
 type
-  TBar[U] = object
-    x: U
+  CpuStorage[T] {.shallow.} = ref object
+    when supportsCopyMem(T):
+      raw_buffer*: ptr UncheckedArray[T] # 8 bytes
+      memalloc*: pointer                 # 8 bytes
+      isMemOwner*: bool                  # 1 byte
+    else: # Tensors of strings, other ref types or non-trivial destructors
+      raw_buffer*: seq[T]                # 8 bytes (16 for seq v2 backed by destructors?)
+
+var x = CpuStorage[string]()
 
-var bar: TBar[int]
+static:
+  doAssert(not string.supportsCopyMem)
+  doAssert x.T is string          # true
+  doAssert x.raw_buffer is seq
 
-foo 10
-foo "test"
-foo bar
+block genericHead:
+  type Foo[T1,T2] = object
+    x1: T1
+    x2: T2
+  type FooInst = Foo[int, float]
+  type Foo2 = genericHead(FooInst)
+  doAssert Foo2 is Foo # issue #13066
 
-# generic params on user types work too
-proc foo2[T](x: TBar[T]) =
-  echo T.name
+  block:
+    type Goo[T] = object
+    type Moo[U] = object
+    type Hoo = Goo[Moo[float]]
+    type Koo = genericHead(Hoo)
+    doAssert Koo is Goo
+    doAssert genericParams(Hoo) is (Moo[float],)
+    doAssert genericParams(Hoo).get(0) is Moo[float]
+    doAssert genericHead(genericParams(Hoo).get(0)) is Moo
 
-foo2 bar
+  type Foo2Inst = Foo2[int, float]
+  doAssert FooInst.default == Foo2Inst.default
+  doAssert FooInst.default.x2 == 0.0
+  doAssert Foo2Inst is FooInst
+  doAssert FooInst is Foo2Inst
+  doAssert compiles(genericHead(FooInst))
+  doAssert not compiles(genericHead(Foo))
+  type Bar = object
+  doAssert not compiles(genericHead(Bar))
 
-# less usual generic params on built-in types
-var arr: array[0..2, int] = [1, 2, 3]
+  when false: # xxx not supported yet
+    doAssert seq[int].genericHead is seq
+  when false: # xxx not supported yet, gives: Error: identifier expected
+    type Hoo[T] = object
+    doAssert genericHead(Hoo[int])[float] is Hoo[float]
 
-proc foo3[R, T](x: array[R, T]) =
-  echo name(R)
+block: # elementType
+  iterator myiter(n: int): auto =
+    for i in 0..<n: yield i
+  iterator myiter3(): int = yield 10
+  iterator myiter2(n: int): auto {.closure.} =
+    for i in 0..<n: yield i
+  doAssert elementType(@[1,2]) is int
+  doAssert elementType("asdf") is char
+  doAssert elementType(myiter(3)) is int
+  doAssert elementType(myiter2(3)) is int
+  doAssert elementType([1.1]) is float
+  doAssert compiles elementType([1])
+  doAssert not compiles elementType(1)
+  doAssert compiles elementType(myiter3())
+  doAssert not compiles elementType(myiter3)
+  # check that it also works for 0-sized seq:
+  var a: seq[int]
+  doAssert elementType(a) is int
+  doAssert elementType(seq[char].default) is char
 
-foo3 arr
+block: # enum.len
+  type
+    Direction = enum
+      north, east, south, west
+    
+    Direction2 = Direction
+    Direction3 = Direction2
 
-const TypeList = [int, string, seq[int]]
+    TokenType = enum
+      a = 2, b = 4, c = 89
+    
+    MyEnum = enum
+      ##[This is test of enum with a doc comment.
 
-macro selectType(inType: typedesc): typedesc =
-  var typeSeq = @[float, TBar[int]]
+         Which is also a multi line one.]##
+      valueA = (0, "my value A"),
+        ## The items are also commented. This has both integer and string
+        ## values.
+      valueB = "value B",
+        ## This item has only a string value,
+      valueC = 2,
+        ## and this one only an integer.
+      valueD = (3, "abc")
+        ## Both, integer and string values again.
 
-  for t in TypeList:
-    typeSeq.add(t)
+    OtherEnum {.pure.} = enum
+      valueX, valueY, valueZ
 
-  typeSeq.add(inType)
-  typeSeq.add(type(10))
+    MyFlag {.size: sizeof(cint).} = enum
+      A, B, C, D
+
+  static:
+    doAssert Direction.enumLen == 4
+    doAssert Direction2.enumLen == 4
+    doAssert Direction3.enumLen == 4
+    doAssert TokenType.enumLen == 3
+    doAssert MyEnum.enumLen == 4
+    doAssert OtherEnum.enumLen == 3
+    doAssert MyFlag.enumLen == 4
 
-  var typeSeq2: seq[typedesc] = @[]
-  typeSeq2 = typeSeq
+when true: # Odd bug where alias can seep inside of `distinctBase`
+  import std/unittest
 
-  result = typeSeq2[5]
+  type
+    AdtChild* = concept t
+      distinctBase(t)
 
-var xvar: selectType(string)
-xvar = "proba"
-echo xvar.type.name
+  proc `$`*[T: AdtChild](adtChild: T): string = ""
 
+  check 10 is int
diff --git a/tests/metatype/tunresolved_return_type.nim b/tests/metatype/tunresolved_return_type.nim
index f67e065ea..637a9b733 100644
--- a/tests/metatype/tunresolved_return_type.nim
+++ b/tests/metatype/tunresolved_return_type.nim
@@ -11,7 +11,7 @@ type
 
 proc toNumber[T: int|uint|int64|uint64](v: ResultValue): T =
   if v < low(T) or v > high(T):
-    raise newException(RangeError, "protocol error")
+    raise newException(RangeDefect, "protocol error")
   return T(v)
 
 #proc toNumber[T](v: int32): T =
diff --git a/tests/metatype/twildtypedesc.nim b/tests/metatype/twildtypedesc.nim
index 268bff0d8..d1c5ffba5 100644
--- a/tests/metatype/twildtypedesc.nim
+++ b/tests/metatype/twildtypedesc.nim
@@ -17,8 +17,8 @@ proc unpack[T](v: string): T =
 
 var s = "123"
 
-assert(unpack[string](s) is string)
-assert(unpack[int](s) is int)
+doAssert(unpack[string](s) is string)
+doAssert(unpack[int](s) is int)
 
 echo unpack[int](s)
 echo unpack[string](s)
@@ -37,7 +37,7 @@ proc unit(t: typedesc[int]): t = 0
 proc unit(t: typedesc[string]): t = ""
 proc unit(t: typedesc[float]): t = 0.0
 
-assert unit(int) == 0
-assert unit(string) == ""
-assert unit(float) == 0.0
+doAssert unit(int) == 0
+doAssert unit(string) == ""
+doAssert unit(float) == 0.0
 
diff --git a/tests/metatype/twrong_same_type.nim b/tests/metatype/twrong_same_type.nim
new file mode 100644
index 000000000..ea903b7a3
--- /dev/null
+++ b/tests/metatype/twrong_same_type.nim
@@ -0,0 +1,28 @@
+# bug #23418
+
+template mapIt*(x: untyped): untyped =
+  type OutType {.gensym.} = typeof(x) #typeof(x, typeOfProc)
+  newSeq[OutType](5)
+
+type F[E] = object
+
+proc start(v: int): F[(ValueError,)] = discard
+proc stop(v: int): F[tuple[]] = discard
+
+assert $typeof(mapIt(start(9))) == "seq[F[(ValueError,)]]"
+assert $typeof(mapIt(stop(9))) == "seq[F[tuple[]]]"
+
+# bug #23445
+
+type F2[T; I: static int] = distinct int
+
+proc start2(v: int): F2[void, 22] = discard
+proc stop2(v: int): F2[void, 33] = discard
+
+var a = mapIt(start2(5))
+
+assert $type(a) == "seq[F2[system.void, 22]]", $type(a)
+
+var b = mapIt(stop2(5))
+
+assert $type(b) == "seq[F2[system.void, 33]]", $type(b)
diff --git a/tests/metatype/tymatrix.nim b/tests/metatype/tymatrix.nim
index 14c4d8c88..cf36bfa63 100644
--- a/tests/metatype/tymatrix.nim
+++ b/tests/metatype/tymatrix.nim
@@ -1,5 +1,3 @@
-import typetraits
-
 template reject(e) =
   static: assert(not compiles(e))
 
diff --git a/tests/metatype/typeclassinference.nim b/tests/metatype/typeclassinference.nim
index c845e04f7..b3f197718 100644
--- a/tests/metatype/typeclassinference.nim
+++ b/tests/metatype/typeclassinference.nim
@@ -19,3 +19,25 @@ var ptr1: ptr = addr(str1)
 var str2: string = "hello, world!"
 var ptr2: ptr = str2
 
+block: # built in typeclass inference
+  proc tupleA(): tuple = return (1, 2)
+  proc tupleB(): tuple = (1f, 2f)
+  assert typeof(tupleA()) is (int, int)
+  assert typeof(tupleB()) is (float32, float32)
+
+  proc a(val: int or float): tuple = 
+    when typeof(val) is int:
+      (10, 10)
+    else:
+      (30f, 30f)
+
+  assert typeof(a(10)) is (int, int)
+  assert typeof(a(10.0)) is (float32, float32)
+
+  proc b(val: int or float): set = 
+    when typeof(val) is int:
+      {10u8, 3}
+    else:
+      {'a', 'b'}
+  assert typeof(b(10)) is set[uint8]
+  assert typeof(b(10.0)) is set[char]
\ No newline at end of file
diff --git a/tests/metatype/typedesc_as_value.nim b/tests/metatype/typedesc_as_value.nim
index f6e526987..463d23724 100644
--- a/tests/metatype/typedesc_as_value.nim
+++ b/tests/metatype/typedesc_as_value.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "'typedesc' metatype is not valid here; typed '=' instead of ':'?"
+  errormsg: "invalid type: 'typedesc[int]' for var"
 """
 
 
diff --git a/tests/metatype/utypeclasses.nim b/tests/metatype/utypeclasses.nim
index 06bab375e..f94b39742 100644
--- a/tests/metatype/utypeclasses.nim
+++ b/tests/metatype/utypeclasses.nim
@@ -3,11 +3,11 @@ import unittest
 proc concat(a, b): string =
   result = $a & $b
 
-test "if proc param types are not supplied, the params are assumed to be generic":
+block: # if proc param types are not supplied, the params are assumed to be generic
   check concat(1, "test") == "1test"
   check concat(1, 20) == "120"
   check concat("foo", "bar") == "foobar"
 
-test "explicit param types can still be specified":
+block: # explicit param types can still be specified
   check concat[cstring, cstring]("x", "y") == "xy"
 
diff --git a/tests/method/mmultim3.nim b/tests/method/mmultim3.nim
index b391731be..a97248203 100644
--- a/tests/method/mmultim3.nim
+++ b/tests/method/mmultim3.nim
@@ -1,5 +1,5 @@
 type
-    TObj* = object {.inheritable.}
+    TObj* {.inheritable.} = object
 
 var myObj* : ref TObj
 
diff --git a/tests/method/t20515.nim b/tests/method/t20515.nim
new file mode 100644
index 000000000..1921f2e46
--- /dev/null
+++ b/tests/method/t20515.nim
@@ -0,0 +1,20 @@
+discard """
+  errormsg: "Base method 'zzz' requires explicit '{.gcsafe.}' to be GC-safe"
+  line: 10
+"""
+
+type
+  A = ref object of RootObj
+  B = ref object of A
+
+method zzz(a: A) {.base.} =
+  discard
+
+var s: seq[int]
+method zzz(a: B) =
+  echo s
+
+proc xxx(someObj: A) {.gcsafe.} =
+  someObj.zzz()
+
+xxx(B())
diff --git a/tests/method/t22673.nim b/tests/method/t22673.nim
new file mode 100644
index 000000000..1689e9d42
--- /dev/null
+++ b/tests/method/t22673.nim
@@ -0,0 +1,21 @@
+discard """
+  matrix: "--warningAsError:UseBase"
+"""
+
+# bug #22673
+type RefEntry = ref object of RootObj
+
+type RefFile = ref object of RefEntry
+    filename*: string
+    data*: string
+
+type RefDir = ref object of RefEntry
+    dirname*: string
+    files*: seq[RefFile]
+
+method name*(e: RefEntry): lent string {.base.} =
+  raiseAssert "Don't call the base method"
+
+method name*(e: RefFile): lent string = e.filename
+
+method name*(e: RefDir): lent string = e.dirname
\ No newline at end of file
diff --git a/tests/method/tautonotgeneric.nim b/tests/method/tautonotgeneric.nim
deleted file mode 100644
index f0d6932f9..000000000
--- a/tests/method/tautonotgeneric.nim
+++ /dev/null
@@ -1,24 +0,0 @@
-discard """
-  output: '''wof!
-wof!'''
-"""
-
-# bug #1659
-type Animal = ref object {.inheritable.}
-type Dog = ref object of Animal
-
-method say(a: Animal): auto {.base.} = "wat!"
-method say(a: Dog): auto = "wof!"
-
-proc saySomething(a: Animal): auto = a.say()
-
-
-method ec(a: Animal): auto {.base.} = echo "wat!"
-method ec(a: Dog): auto = echo "wof!"
-
-proc ech(a: Animal): auto = a.ec()
-
-
-var a = Dog()
-echo saySomething(a)
-ech a
diff --git a/tests/method/tcompilegenerics.nim b/tests/method/tcompilegenerics.nim
new file mode 100644
index 000000000..d0732895b
--- /dev/null
+++ b/tests/method/tcompilegenerics.nim
@@ -0,0 +1,24 @@
+discard """
+  matrix: "--mm:arc; --mm:refc"
+  output: '''
+newDNode base
+'''
+"""
+
+type
+  SNodeAny = ref object of RootObj
+  SNode[T] = ref object of SNodeAny
+    m: T
+  DNode[T] = ref object
+
+method getStr(s: SNode[float]): string {.base.} = "blahblah"
+
+method newDNode(s: SNodeAny) {.base.} =
+  echo "newDNode base"
+
+method newDNode[T](s: SNode[T]) =
+  echo "newDNode generic"
+
+let m = SNode[float]()
+let s = SNodeAny(m)
+newDnode(s)
\ No newline at end of file
diff --git a/tests/method/temptybody.nim b/tests/method/temptybody.nim
deleted file mode 100644
index aad945f81..000000000
--- a/tests/method/temptybody.nim
+++ /dev/null
@@ -1,11 +0,0 @@
-# bug #2401
-
-type MyClass = ref object of RootObj
-
-method HelloWorld*(obj: MyClass) {.base.} =
-  when defined(myPragma):
-    echo("Hello World")
-  # discard # with this line enabled it works
-
-var obj = MyClass()
-obj.HelloWorld()
diff --git a/tests/method/tgeneric_methods.nim b/tests/method/tgeneric_methods.nim
index 76a68fbb0..f8d068cc5 100644
--- a/tests/method/tgeneric_methods.nim
+++ b/tests/method/tgeneric_methods.nim
@@ -1,24 +1,42 @@
-discard """
-  output: "wow2"
-"""
-type
-  First[T] = ref object of RootObj
-    value: T
-
-  Second[T] = ref object of First[T]
-    value2: T
-
-method wow[T](y: int; x: First[T]) {.base.} =
-  echo "wow1"
-
-method wow[T](y: int; x: Second[T]) =
-  echo "wow2"
-
-var
-  x: Second[int]
-new(x)
-
-proc takeFirst(x: First[int]) =
-  wow(2, x)
-
-takeFirst(x)
+discard """

+  matrix: "--mm:arc --multimethods:on -d:nimInternalNonVtablesTesting; --mm:refc --multimethods:on -d:nimInternalNonVtablesTesting"

+  output: '''wow2

+X 1

+X 3'''

+"""

+type

+  First[T] = ref object of RootObj

+    value: T

+

+  Second[T] = ref object of First[T]

+    value2: T

+

+method wow[T](y: int; x: First[T]) {.base.} =

+  echo "wow1"

+

+method wow[T](y: int; x: Second[T]) =

+  echo "wow2"

+

+var

+  x: Second[int]

+new(x)

+

+proc takeFirst(x: First[int]) =

+  wow(2, x)

+

+takeFirst(x)

+

+

+# bug #5479

+type

+  Base[T: static[int]] = ref object of RootObj

+

+method test[T](t: Base[T]) {.base.} =

+  echo "X ", t.T

+

+let ab = Base[1]()

+

+ab.test()

+

+let ac = Base[3]()

+ac.test()

diff --git a/tests/method/tgeneric_methods2.nim b/tests/method/tgeneric_methods2.nim
deleted file mode 100644
index 6e761dc48..000000000
--- a/tests/method/tgeneric_methods2.nim
+++ /dev/null
@@ -1,15 +0,0 @@
-#5432
-type
-  Iterator[T] = ref object of RootObj
-
-# base methods with `T` in the return type are okay
-method methodThatWorks*[T](i: Iterator[T]): T {.base.} =
-  discard
-
-# base methods without `T` (void or basic types) fail
-method methodThatFails*[T](i: Iterator[T]) {.base.} =
-  discard
-
-type
-  SpecificIterator1 = ref object of Iterator[string]
-  SpecificIterator2 = ref object of Iterator[int]
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/method/tmethod.nim b/tests/method/tmethod.nim
index 0cfe24c70..005294d64 100644
--- a/tests/method/tmethod.nim
+++ b/tests/method/tmethod.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "\'method\' needs a parameter that has an object type"
   file: "tmethod.nim"
   line: 7
-  errormsg: "\'method\' needs a parameter that has an object type"
 """
 
 method m(i: int): int =
diff --git a/tests/method/tmethod_issues.nim b/tests/method/tmethod_issues.nim
new file mode 100644
index 000000000..daaa46522
--- /dev/null
+++ b/tests/method/tmethod_issues.nim
@@ -0,0 +1,170 @@
+discard """
+  matrix: "--mm:arc; --mm:refc"
+  output: '''
+wof!
+wof!
+type A
+type B
+'''
+"""
+
+
+# bug #1659
+type Animal {.inheritable.} = ref object
+type Dog = ref object of Animal
+
+method say(a: Animal): auto {.base.} = "wat!"
+method say(a: Dog): auto = "wof!"
+
+proc saySomething(a: Animal): auto = a.say()
+
+method ec(a: Animal): auto {.base.} = echo "wat!"
+method ec(a: Dog): auto = echo "wof!"
+
+proc ech(a: Animal): auto = a.ec()
+
+var a = Dog()
+echo saySomething(a)
+ech a
+
+
+
+# bug #2401
+type MyClass = ref object of RootObj
+
+method HelloWorld*(obj: MyClass) {.base.} =
+  when defined(myPragma):
+    echo("Hello World")
+  # discard # with this line enabled it works
+
+var obj = MyClass()
+obj.HelloWorld()
+
+
+
+
+# bug #5432
+type
+  Iterator[T] = ref object of RootObj
+
+# base methods with `T` in the return type are okay
+method methodThatWorks*[T](i: Iterator[T]): T {.base.} =
+  discard
+
+# base methods without `T` (void or basic types) fail
+method methodThatFails*[T](i: Iterator[T]) {.base.} =
+  discard
+
+type
+  SpecificIterator1 = ref object of Iterator[string]
+  SpecificIterator2 = ref object of Iterator[int]
+
+
+
+
+# bug #3431
+type
+  Lexer = object
+    buf*: string
+    pos*: int
+    lastchar*: char
+
+  ASTNode = object
+
+method init*(self: var Lexer; buf: string) {.base.} =
+  self.buf = buf
+  self.pos = 0
+  self.lastchar = self.buf[0]
+
+method init*(self: var ASTNode; val: string) =
+  discard
+
+
+
+# bug #3370
+type
+  RefTestA*[T] = ref object of RootObj
+    data*: T
+
+method tester*[S](self: S): bool =
+  true
+
+type
+  RefTestB* = RefTestA[(string, int)]
+
+method tester*(self: RefTestB): bool =
+  true
+
+type
+  RefTestC = RefTestA[string]
+
+method tester*(self: RefTestC): bool =
+  false
+
+
+
+# bug #3468
+type X = ref object of RootObj
+type Y = ref object of RootObj
+
+method draw*(x: X) {.base.} = discard
+method draw*(y: Y) {.base.} = discard
+
+
+
+# bug #3550
+type 
+  BaseClass = ref object of RootObj
+  Class1 = ref object of BaseClass
+  Class2 = ref object of BaseClass
+  
+method test(obj: Class1, obj2: BaseClass) =
+  discard
+
+method test(obj: Class2, obj2: BaseClass) =
+  discard
+  
+var obj1 = Class1()
+var obj2 = Class2()
+
+obj1.test(obj2) 
+obj2.test(obj1)
+
+
+# -------------------------------------------------------
+# issue #16516
+
+type
+  A = ref object of RootObj
+    x: int
+
+  B = ref object of A
+
+method foo(v: sink A, lst: var seq[A]) {.base,locks:0.} =
+  echo "type A"
+  lst.add v
+
+method foo(v: sink B, lst: var seq[A]) =
+  echo "type B"
+  lst.add v
+
+proc main() =
+  let
+    a = A(x: 5)
+    b: A = B(x: 5)
+
+  var lst: seq[A]
+
+  foo(a, lst)
+  foo(b, lst)
+
+main()
+
+block: # bug #20391
+  type Container[T] = ref object of RootRef
+    item: T
+
+  let a = Container[int]()
+
+  doAssert a of Container[int]
+  doAssert not (a of Container[string])
diff --git a/tests/method/tmethod_various.nim b/tests/method/tmethod_various.nim
new file mode 100644
index 000000000..3b64aea8d
--- /dev/null
+++ b/tests/method/tmethod_various.nim
@@ -0,0 +1,79 @@
+discard """
+  matrix: "--mm:arc; --mm:refc"
+  output: '''
+HELLO WORLD!
+'''
+"""
+
+
+
+
+type
+  TNode* {.inheritable.} = object
+  PNode* = ref TNode
+
+  PNodeFoo* = ref object of TNode
+
+  TSomethingElse = object
+  PSomethingElse = ref TSomethingElse
+
+method foo(a: PNode, b: PSomethingElse) {.base.} = discard
+method foo(a: PNodeFoo, b: PSomethingElse) = discard
+
+
+
+
+# tmproto
+type
+  Obj1 {.inheritable.} = ref object
+  Obj2 = ref object of Obj1
+
+method beta(x: Obj1): int {.base.}
+
+proc delta(x: Obj2): int =
+  beta(x)
+
+method beta(x: Obj2): int
+
+proc alpha(x: Obj1): int =
+  beta(x)
+
+method beta(x: Obj1): int = 1
+method beta(x: Obj2): int = 2
+
+proc gamma(x: Obj1): int =
+  beta(x)
+
+doAssert alpha(Obj1()) == 1
+doAssert gamma(Obj1()) == 1
+doAssert alpha(Obj2()) == 2
+doAssert gamma(Obj2()) == 2
+doAssert delta(Obj2()) == 2
+
+
+
+# tsimmeth
+import strutils
+var x = "hello world!".toLowerAscii.toUpperAscii
+x.echo()
+
+
+
+# trecmeth
+# Note: We only compile this to verify that code generation
+# for recursive methods works, no code is being executed
+type Obj = ref object of RootObj
+
+# Mutual recursion
+method alpha(x: Obj) {.base.}
+method beta(x: Obj) {.base.}
+
+method alpha(x: Obj) =
+  beta(x)
+
+method beta(x: Obj) =
+  alpha(x)
+
+# Simple recursion
+method gamma(x: Obj) {.base.} =
+  gamma(x)
diff --git a/tests/method/tmethods1.nim b/tests/method/tmethods1.nim
deleted file mode 100644
index cb4da5ef2..000000000
--- a/tests/method/tmethods1.nim
+++ /dev/null
@@ -1,22 +0,0 @@
-discard """
-  output: "do nothing"
-"""
-
-method somethin(obj: RootObj) {.base.} =
-  echo "do nothing"
-
-type
-  TNode* = object {.inheritable.}
-  PNode* = ref TNode
-
-  PNodeFoo* = ref object of TNode
-
-  TSomethingElse = object
-  PSomethingElse = ref TSomethingElse
-
-method foo(a: PNode, b: PSomethingElse) {.base.} = discard
-method foo(a: PNodeFoo, b: PSomethingElse) = discard
-
-var o: RootObj
-o.somethin()
-
diff --git a/tests/method/tmethods_old.nim b/tests/method/tmethods_old.nim
new file mode 100644
index 000000000..d24eb0cc7
--- /dev/null
+++ b/tests/method/tmethods_old.nim
@@ -0,0 +1,12 @@
+discard """
+  matrix: "--mm:arc -d:nimInternalNonVtablesTesting"
+  output: '''
+do nothing
+'''
+"""
+
+# tmethods1
+method somethin(obj: RootObj) {.base.} =
+  echo "do nothing"
+var o: RootObj
+o.somethin()
diff --git a/tests/method/tmproto.nim b/tests/method/tmproto.nim
deleted file mode 100644
index 087666ea0..000000000
--- a/tests/method/tmproto.nim
+++ /dev/null
@@ -1,25 +0,0 @@
-type
-  Obj1 = ref object {.inheritable.}
-  Obj2 = ref object of Obj1
-
-method beta(x: Obj1): int {.base.}
-
-proc delta(x: Obj2): int =
-  beta(x)
-
-method beta(x: Obj2): int
-
-proc alpha(x: Obj1): int =
-  beta(x)
-
-method beta(x: Obj1): int = 1
-method beta(x: Obj2): int = 2
-
-proc gamma(x: Obj1): int =
-  beta(x)
-
-doAssert alpha(Obj1()) == 1
-doAssert gamma(Obj1()) == 1
-doAssert alpha(Obj2()) == 2
-doAssert gamma(Obj2()) == 2
-doAssert delta(Obj2()) == 2
diff --git a/tests/method/tmultim.nim b/tests/method/tmultim.nim
new file mode 100644
index 000000000..bba4d8c5c
--- /dev/null
+++ b/tests/method/tmultim.nim
@@ -0,0 +1,97 @@
+discard """

+  matrix: "--multimethods:on"

+  output: '''

+collide: unit, thing

+collide: unit, thing

+collide: thing, unit

+collide: thing, thing

+collide: unit, thing |

+collide: unit, thing |

+collide: thing, unit |

+do nothing

+'''

+  joinable: false

+  disabled: true

+"""

+

+

+# tmultim2

+type

+  TThing {.inheritable.} = object

+  TUnit = object of TThing

+    x: int

+  TParticle = object of TThing

+    a, b: int

+

+method collide(a, b: TThing) {.base, inline.} =

+  echo "collide: thing, thing"

+

+method collide(a: TThing, b: TUnit) {.inline.} =

+  echo "collide: thing, unit"

+

+method collide(a: TUnit, b: TThing) {.inline.} =

+  echo "collide: unit, thing"

+

+proc test(a, b: TThing) {.inline.} =

+  collide(a, b)

+

+proc staticCollide(a, b: TThing) {.inline.} =

+  procCall collide(a, b)

+

+var

+  a: TThing

+  b, c: TUnit

+collide(b, c) # ambiguous (unit, thing) or (thing, unit)? -> prefer unit, thing!

+test(b, c)

+collide(a, b)

+staticCollide(a, b)

+

+

+

+# tmultim6

+type

+  Thing {.inheritable.} = object

+  Unit[T] = object of Thing

+    x: T

+  Particle = object of Thing

+    a, b: int

+

+method collide(a, b: Thing) {.base, inline.} =

+  quit "to override!"

+

+method collide[T](a: Thing, b: Unit[T]) {.inline.} =

+  echo "collide: thing, unit |"

+

+method collide[T](a: Unit[T], b: Thing) {.inline.} =

+  echo "collide: unit, thing |"

+

+proc test(a, b: Thing) {.inline.} =

+  collide(a, b)

+

+var

+  aaa: Thing

+  bbb, ccc: Unit[string]

+collide(bbb, Thing(ccc))

+test(bbb, ccc)

+collide(aaa, bbb)

+

+

+

+# tmethods1

+method somethin(obj: RootObj) {.base.} =

+  echo "do nothing"

+

+type

+  TNode* {.inheritable.} = object

+  PNode* = ref TNode

+

+  PNodeFoo* = ref object of TNode

+

+  TSomethingElse = object

+  PSomethingElse = ref TSomethingElse

+

+method foo(a: PNode, b: PSomethingElse) {.base.} = discard

+method foo(a: PNodeFoo, b: PSomethingElse) = discard

+

+var o: RootObj

+o.somethin()

diff --git a/tests/method/tmultim1.nim b/tests/method/tmultim1.nim
deleted file mode 100644
index 010468a5b..000000000
--- a/tests/method/tmultim1.nim
+++ /dev/null
@@ -1,29 +0,0 @@
-discard """
-  file: "tmultim1.nim"
-  output: "7"
-"""
-# Test multi methods
-
-type
-  Expression = ref object {.inheritable.}
-  Literal = ref object of Expression
-    x: int
-  PlusExpr = ref object of Expression
-    a, b: Expression
-
-method eval(e: Expression): int {.base.} = quit "to override!"
-method eval(e: Literal): int = return e.x
-method eval(e: PlusExpr): int = return eval(e.a) + eval(e.b)
-
-proc newLit(x: int): Literal =
-  new(result)
-  result.x = x
-
-proc newPlus(a, b: Expression): PlusExpr =
-  new(result)
-  result.a = a
-  result.b = b
-
-echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4))) #OUT 7
-
-
diff --git a/tests/method/tmultim2.nim b/tests/method/tmultim2.nim
deleted file mode 100644
index 98a08b1cb..000000000
--- a/tests/method/tmultim2.nim
+++ /dev/null
@@ -1,39 +0,0 @@
-discard """
-  file: "tmultim2.nim"
-  output: '''collide: unit, thing
-collide: unit, thing
-collide: thing, unit
-collide: thing, thing'''
-"""
-# Test multi methods
-
-type
-  TThing = object {.inheritable.}
-  TUnit = object of TThing
-    x: int
-  TParticle = object of TThing
-    a, b: int
-
-method collide(a, b: TThing) {.base, inline.} =
-  echo "collide: thing, thing"
-
-method collide(a: TThing, b: TUnit) {.inline.} =
-  echo "collide: thing, unit"
-
-method collide(a: TUnit, b: TThing) {.inline.} =
-  echo "collide: unit, thing"
-
-proc test(a, b: TThing) {.inline.} =
-  collide(a, b)
-
-proc staticCollide(a, b: TThing) {.inline.} =
-  procCall collide(a, b)
-
-
-var
-  a: TThing
-  b, c: TUnit
-collide(b, c) # ambiguous (unit, thing) or (thing, unit)? -> prefer unit, thing!
-test(b, c)
-collide(a, b)
-staticCollide(a, b)
diff --git a/tests/method/tmultim3.nim b/tests/method/tmultim3.nim
deleted file mode 100644
index 373c84c0e..000000000
--- a/tests/method/tmultim3.nim
+++ /dev/null
@@ -1,20 +0,0 @@
-discard """
-  file: "tmultim3.nim"
-  output: "Hi derived!"
-"""
-import mmultim3
-
-type
-    TBObj* = object of TObj
-
-
-method test123(a : ref TBObj) =
-    echo("Hi derived!")
-
-var a : ref TBObj
-new(a)
-myObj = a
-testMyObj()
-
-
-
diff --git a/tests/method/tmultim4.nim b/tests/method/tmultim4.nim
deleted file mode 100644
index eabf8d126..000000000
--- a/tests/method/tmultim4.nim
+++ /dev/null
@@ -1,47 +0,0 @@
-discard """
-  file: "tmultim4.nim"
-  output: "hello"
-"""
-type
-  Test = object of TObject
-
-method doMethod(a: ref TObject) {.base, raises: [EIO].} =
-  quit "override"
-
-method doMethod(a: ref Test) =
-  echo "hello"
-  if a == nil:
-    raise newException(EIO, "arg")
-
-proc doProc(a: ref Test) =
-  echo "hello"
-
-proc newTest(): ref Test =
-  new(result)
-
-var s:ref Test = newTest()
-
-
-#doesn't work
-for z in 1..4:
-  s.doMethod()
-  break
-
-#works
-#for z in 1..4:
-#  s.doProc()
-#  break
-
-#works
-#while true:
-#  s.doMethod()
-#  break
-
-#works
-#while true:
-#  s.doProc()
-#  break
-
-
-
-
diff --git a/tests/method/tmultim6.nim b/tests/method/tmultim6.nim
deleted file mode 100644
index 2c622b832..000000000
--- a/tests/method/tmultim6.nim
+++ /dev/null
@@ -1,30 +0,0 @@
-discard """
-  output: "collide: unit, thing | collide: unit, thing | collide: thing, unit |"
-"""
-# Test multi methods
-
-type
-  Thing = object {.inheritable.}
-  Unit[T] = object of Thing
-    x: T
-  Particle = object of Thing
-    a, b: int
-
-method collide(a, b: Thing) {.base, inline.} =
-  quit "to override!"
-
-method collide[T](a: Thing, b: Unit[T]) {.inline.} =
-  write stdout, "collide: thing, unit | "
-
-method collide[T](a: Unit[T], b: Thing) {.inline.} =
-  write stdout, "collide: unit, thing | "
-
-proc test(a, b: Thing) {.inline.} =
-  collide(a, b)
-
-var
-  a: Thing
-  b, c: Unit[string]
-collide(b, Thing(c))
-test(b, c)
-collide(a, b)
diff --git a/tests/method/tmultim7.nim b/tests/method/tmultim7.nim
deleted file mode 100644
index 7a8859679..000000000
--- a/tests/method/tmultim7.nim
+++ /dev/null
@@ -1,48 +0,0 @@
-
-# bug #3431
-
-type
-  Lexer = object
-    buf*: string
-    pos*: int
-    lastchar*: char
-
-  ASTNode = object
-
-method init*(self: var Lexer; buf: string) {.base.} =
-  self.buf = buf
-  self.pos = 0
-  self.lastchar = self.buf[0]
-
-method init*(self: var ASTNode; val: string) =
-  discard
-
-
-# bug #3370
-type
-  RefTestA*[T] = ref object of RootObj
-    data*: T
-
-method tester*[S](self: S): bool =
-  true
-
-type
-  RefTestB* = RefTestA[(string, int)]
-
-method tester*(self: RefTestB): bool =
-  true
-
-type
-  RefTestC = RefTestA[string]
-
-method tester*(self: RefTestC): bool =
-  false
-
-
-# bug #3468
-
-type X = ref object of RootObj
-type Y = ref object of RootObj
-
-method draw*(x: X) {.base.} = discard
-method draw*(y: Y) {.base.} = discard
diff --git a/tests/method/tmultim8.nim b/tests/method/tmultim8.nim
deleted file mode 100644
index 0d067b668..000000000
--- a/tests/method/tmultim8.nim
+++ /dev/null
@@ -1,19 +0,0 @@
-
-# bug #3550
-
-type 
-  BaseClass = ref object of RootObj
-  Class1 = ref object of BaseClass
-  Class2 = ref object of BaseClass
-  
-method test(obj: Class1, obj2: BaseClass) =
-  discard
-
-method test(obj: Class2, obj2: BaseClass) =
-  discard
-  
-var obj1 = Class1()
-var obj2 = Class2()
-
-obj1.test(obj2) 
-obj2.test(obj1)
diff --git a/tests/method/tmultimjs.nim b/tests/method/tmultimjs.nim
new file mode 100644
index 000000000..36960f2e1
--- /dev/null
+++ b/tests/method/tmultimjs.nim
@@ -0,0 +1,73 @@
+discard """
+  output: '''
+7
+Hi derived!
+hello
+'''
+  disabled: true
+"""
+
+
+# tmultim1
+type
+  Expression {.inheritable.} = ref object
+  Literal = ref object of Expression
+    x: int
+  PlusExpr = ref object of Expression
+    a, b: Expression
+
+method eval(e: Expression): int {.base.} = quit "to override!"
+method eval(e: Literal): int = return e.x
+method eval(e: PlusExpr): int = return eval(e.a) + eval(e.b)
+
+proc newLit(x: int): Literal =
+  new(result)
+  result.x = x
+
+proc newPlus(a, b: Expression): PlusExpr =
+  new(result)
+  result.a = a
+  result.b = b
+
+echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4))) #OUT 7
+
+
+
+# tmultim3
+import mmultim3
+
+type TBObj* = object of TObj
+
+method test123(a : ref TBObj) =
+    echo("Hi derived!")
+
+var aa: ref TBObj
+new(aa)
+myObj = aa
+testMyObj()
+
+
+
+# tmultim4
+type Test = object of RootObj
+
+method doMethod(a: ref RootObj) {.base, raises: [IoError].} =
+  quit "override"
+
+method doMethod(a: ref Test) =
+  echo "hello"
+  if a == nil:
+    raise newException(IoError, "arg")
+
+proc doProc(a: ref Test) =
+  echo "hello"
+
+proc newTest(): ref Test =
+  new(result)
+
+var s:ref Test = newTest()
+
+#doesn't work
+for z in 1..4:
+  s.doMethod()
+  break
diff --git a/tests/method/tnildispatcher.nim b/tests/method/tnildispatcher.nim
index 017e8155f..219d2b29f 100644
--- a/tests/method/tnildispatcher.nim
+++ b/tests/method/tnildispatcher.nim
@@ -1,5 +1,5 @@
 discard """
-  outputsub: '''Error: unhandled exception: cannot dispatch; dispatcher is nil [NilAccessError]'''
+  outputsub: '''Error: unhandled exception: cannot dispatch; dispatcher is nil [NilAccessDefect]'''
   exitcode: 1
 """
 # bug #5599
diff --git a/tests/method/trecmeth.nim b/tests/method/trecmeth.nim
deleted file mode 100644
index ac0a1e977..000000000
--- a/tests/method/trecmeth.nim
+++ /dev/null
@@ -1,22 +0,0 @@
-# Note: We only compile this to verify that code generation
-# for recursive methods works, no code is being executed
-
-type
-  Obj = ref object of RootObj
-
-# Mutual recursion
-
-method alpha(x: Obj) {.base.}
-method beta(x: Obj) {.base.}
-
-method alpha(x: Obj) =
-  beta(x)
-
-method beta(x: Obj) =
-  alpha(x)
-
-# Simple recursion
-
-method gamma(x: Obj) {.base.} =
-  gamma(x)
-
diff --git a/tests/method/treturn_var_t.nim b/tests/method/treturn_var_t.nim
new file mode 100644
index 000000000..91d982902
--- /dev/null
+++ b/tests/method/treturn_var_t.nim
@@ -0,0 +1,25 @@
+discard """
+  output: '''Inh
+45'''
+"""
+
+type
+  Base = ref object of RootObj
+    field: int
+
+  Inh = ref object of Base
+
+# bug #6777
+method foo(b: Base): var int {.base.} =
+  echo "Base"
+  result = b.field
+
+method foo(b: Inh): var int =
+  echo "Inh"
+  result = b.field
+
+var x: Base
+var y = Inh(field: 45)
+x = y
+echo foo(x)
+
diff --git a/tests/method/tsimmeth.nim b/tests/method/tsimmeth.nim
deleted file mode 100644
index a057c35b7..000000000
--- a/tests/method/tsimmeth.nim
+++ /dev/null
@@ -1,14 +0,0 @@
-discard """
-  file: "tsimmeth.nim"
-  output: "HELLO WORLD!"
-"""
-# Test method simulation
-
-import strutils
-
-var x = "hello world!".toLowerAscii.toUpperAscii
-x.echo()
-#OUT HELLO WORLD!
-
-
-
diff --git a/tests/method/tsingle_methods.nim b/tests/method/tsingle_methods.nim
new file mode 100644
index 000000000..b564e7d87
--- /dev/null
+++ b/tests/method/tsingle_methods.nim
@@ -0,0 +1,48 @@
+discard """
+  matrix: "--mm:arc --multimethods:off; --mm:refc --multimethods:off"
+  output: '''base
+base
+base
+base
+base
+base
+'''
+"""
+
+# bug #10912
+
+type
+  X = ref object of RootObj
+
+type
+  A* = ref object of RootObj
+  B* = ref object of A
+  C* = ref object of A
+  D* = ref object of A
+  E* = ref object of A
+  F* = ref object of A
+
+method resolve(self: var X, stmt: A) {.base.} = echo "base"
+
+proc resolveSeq*(self: var X, statements: seq[A]) =
+  for statement in statements:
+    resolve(self, statement)
+
+method resolve(self: var X, stmt: B) =
+  echo "B"
+
+method resolve(self: var X, stmt: D) =
+  echo "D"
+
+method resolve(self: var X, stmt: E) =
+  echo "E"
+
+method resolve(self: var X, stmt: C) =
+  echo "C"
+
+method resolve(self: var X, stmt: F) =
+  echo "F"
+
+var x = X()
+var a = @[A(), B(), C(), D(), E(), F()]
+resolveSeq(x, a)
diff --git a/tests/method/tvtable.nim b/tests/method/tvtable.nim
new file mode 100644
index 000000000..a1b33d6b7
--- /dev/null
+++ b/tests/method/tvtable.nim
@@ -0,0 +1,24 @@
+discard """

+  targets: "c cpp"

+"""

+

+type FooBase = ref object of RootObj

+  dummy: int

+type Foo = ref object of FooBase

+  value : float32

+type Foo2 = ref object of Foo

+  change : float32

+method bar(x: FooBase, a: float32) {.base.} =

+  discard

+method bar(x: Foo, a: float32)  =

+  x.value += a

+method bar(x: Foo2, a: float32)  =

+  x.value += a

+

+

+proc test() =

+  var x = new Foo2

+  x.bar(2.3)

+  doAssert x.value <= 2.3

+

+test()
\ No newline at end of file
diff --git a/tests/misc/m15316.nim b/tests/misc/m15316.nim
new file mode 100644
index 000000000..188d06946
--- /dev/null
+++ b/tests/misc/m15316.nim
@@ -0,0 +1 @@
+proc foo = (if)
diff --git a/tests/misc/m15955.nim b/tests/misc/m15955.nim
new file mode 100644
index 000000000..22da345db
--- /dev/null
+++ b/tests/misc/m15955.nim
@@ -0,0 +1,4 @@
+proc add*(a, b: int): int {.cdecl, exportc.} =
+    a + b
+proc sub*(a, b: int): int {.cdecl, exportc.} =
+    a - b
\ No newline at end of file
diff --git a/tests/misc/m15955_main.nim b/tests/misc/m15955_main.nim
new file mode 100644
index 000000000..a71af8121
--- /dev/null
+++ b/tests/misc/m15955_main.nim
@@ -0,0 +1,11 @@
+import stdtest/specialpaths
+import std/os
+
+const buildLib = buildDir / "libD20220923T19380"
+
+{.passL: buildLib.}
+proc add*(a, b: int):int {.cdecl, importc.}
+proc sub*(a, b: int):int {.cdecl, importc.}
+
+echo add(10, 5)
+echo sub(10, 5)
diff --git a/tests/misc/m20149.nim b/tests/misc/m20149.nim
new file mode 100644
index 000000000..942262a6e
--- /dev/null
+++ b/tests/misc/m20149.nim
@@ -0,0 +1,2 @@
+let x = 12
+echo x
diff --git a/tests/misc/m20456.nims b/tests/misc/m20456.nims
new file mode 100644
index 000000000..8a9313129
--- /dev/null
+++ b/tests/misc/m20456.nims
@@ -0,0 +1 @@
+echo 123
\ No newline at end of file
diff --git a/tests/misc/mbackend.nim b/tests/misc/mbackend.nim
new file mode 100644
index 000000000..b6578a212
--- /dev/null
+++ b/tests/misc/mbackend.nim
@@ -0,0 +1,30 @@
+#[
+We can't merge this test inside a `when defined(cpp)` because some bug that was
+fixed would not trigger in that case.
+]#
+
+import std/compilesettings
+
+static:
+  ## bugfix 1: this used to CT error with: Error: unhandled exception: mimportcpp.nim(6, 18) `defined(cpp)`
+  doAssert defined(cpp)
+  doAssert querySetting(backend) == "cpp"
+
+  ## checks that `--backend:c` has no side effect (ie, can be overridden by subsequent commands)
+  doAssert not defined(c)
+  doAssert not defined(js)
+  doAssert not defined(js)
+
+type
+  std_exception {.importcpp: "std::exception", header: "<exception>".} = object
+proc what(s: std_exception): cstring {.importcpp: "((char *)#.what())".}
+
+var isThrown = false
+try:
+  ## bugfix 2: this used to CT error with: Error: only a 'ref object' can be raised
+  raise std_exception()
+except std_exception as ex:
+  doAssert ex.what().len > 0
+  isThrown = true
+
+doAssert isThrown
diff --git a/tests/misc/mbetterrun.nim b/tests/misc/mbetterrun.nim
new file mode 100644
index 000000000..d4f427af0
--- /dev/null
+++ b/tests/misc/mbetterrun.nim
@@ -0,0 +1,3 @@
+const mbetterrunVal {.strdefine.} = ""
+static: echo "compiling: " & mbetterrunVal
+echo "running: " & mbetterrunVal
diff --git a/tests/misc/mfield_defect.nim b/tests/misc/mfield_defect.nim
new file mode 100644
index 000000000..53bfba40e
--- /dev/null
+++ b/tests/misc/mfield_defect.nim
@@ -0,0 +1,30 @@
+#[
+ran from trunner
+]#
+
+
+
+
+
+
+# line 10
+type Kind = enum k0, k1, k2, k3, k4
+
+type Foo = object
+  case kind: Kind
+  of k0: f0: int
+  of k1: f1: int
+  of k2: f2: int
+  of k3: f3: int
+  of k4: f4: int
+
+proc main()=
+  var foo = Foo(kind: k3, f3: 3)
+  let s1 = foo.f3
+  doAssert s1 == 3
+  let s2 = foo.f2
+
+when defined case1:
+  static: main()
+when defined case2:
+  main()
diff --git a/tests/misc/mimportc.nim b/tests/misc/mimportc.nim
new file mode 100644
index 000000000..602c6372d
--- /dev/null
+++ b/tests/misc/mimportc.nim
@@ -0,0 +1,26 @@
+#[
+this test will grow with more importc+importcpp tests; see driver in trunner.nim
+]#
+
+{.emit:"""
+struct A {
+  static int fun0(int a){
+    return a;
+  }
+  static int& fun1(int& a){
+    return a;
+  }
+};
+""".}
+
+proc fun0*(a: cint): int {.importcpp:"A::$1(@)".}
+proc fun1*(a: var cint): var int {.importcpp:"A::$1(@)".} =
+  ## some comment; this test is for #14314
+  runnableExamples: discard
+
+proc main()=
+  var a = 10.cint
+  doAssert fun0(a) == a
+  doAssert fun1(a).addr == a.addr
+  echo "witness"
+main()
diff --git a/tests/misc/mjsondoc.nim b/tests/misc/mjsondoc.nim
new file mode 100644
index 000000000..016c8522d
--- /dev/null
+++ b/tests/misc/mjsondoc.nim
@@ -0,0 +1,14 @@
+proc doSomething*(x, y: int): int =
+  ## do something
+  x + y
+
+const
+  a* = 1 ## echo 1234
+  b* = "test"
+
+type
+  MyEnum* = enum
+    foo, bar
+
+proc foo2*[T: int, M: string, U](x: T, y: U, z: M) =
+  echo 1
diff --git a/tests/misc/modulea.nim b/tests/misc/modulea.nim
new file mode 100644
index 000000000..a9e6b364e
--- /dev/null
+++ b/tests/misc/modulea.nim
@@ -0,0 +1,2 @@
+type modulea* = object
+  a: int
diff --git a/tests/misc/msizeof5.nim b/tests/misc/msizeof5.nim
new file mode 100644
index 000000000..63573a705
--- /dev/null
+++ b/tests/misc/msizeof5.nim
@@ -0,0 +1,131 @@
+## tests for -d:checkAbi used by addAbiCheck via NIM_STATIC_ASSERT
+
+{.emit:"""/*TYPESECTION*/
+struct Foo1{
+  int a;
+};
+struct Foo2{
+  int a;
+};
+enum Foo3{k1, k2};
+typedef enum Foo3 Foo3b;
+typedef enum Foo4{k3, k4} Foo4;
+
+typedef int Foo5[3];
+
+typedef struct Foo6{
+  int a1;
+  bool a2;
+  double a3;
+  struct Foo6* a4;
+} Foo6;
+""".}
+
+template ensureCgen(T: typedesc) =
+  ## ensures cgen
+  var a {.volatile.}: T
+
+block:
+  type Foo1Alias{.importc: "struct Foo1", size: sizeof(cint).} = object
+    a: cint
+  ensureCgen Foo1Alias
+
+block:
+  type Foo3Alias{.importc: "enum Foo3", size: sizeof(cint).} = enum
+    k1, k2
+  ensureCgen Foo3Alias
+
+block:
+  type Foo3bAlias{.importc: "Foo3b", size: sizeof(cint).} = enum
+    k1, k2
+  ensureCgen Foo3bAlias
+
+block:
+  type Foo3b{.importc, size: sizeof(cint).} = enum
+    k1, k2
+  ensureCgen Foo3b
+  static:
+    doAssert Foo3b.sizeof == cint.sizeof
+
+block:
+  type Foo4{.importc, size: sizeof(cint).} = enum
+    k3, k4
+  # adding entries should not yield duplicate ABI checks, as enforced by
+  # `typeABICache`.
+  # Currently the test doesn't check for this but you can inspect the cgen'd file
+  ensureCgen Foo4
+  ensureCgen Foo4
+  ensureCgen Foo4
+
+block:
+  type Foo5{.importc.} = array[3, cint]
+  ensureCgen Foo5
+
+block:
+  type Foo5{.importc.} = array[3, cint]
+  ensureCgen Foo5
+
+block: # CT sizeof
+  type Foo6GT = object # grountruth
+    a1: cint
+    a2: bool
+    a3: cfloat
+    a4: ptr Foo6GT
+
+  type Foo6{.importc, completeStruct.} = object
+    a1: cint
+    a2: bool
+    a3: cfloat
+    a4: ptr Foo6
+
+  static: doAssert compiles(static(Foo6.sizeof))
+  static: doAssert Foo6.sizeof == Foo6GT.sizeof
+  static: doAssert (Foo6, int, array[2, Foo6]).sizeof ==
+    (Foo6GT, int, array[2, Foo6GT]).sizeof
+
+block:
+  type GoodImportcType {.importc: "signed char", nodecl.} = char
+    # "good" in sense the sizeof will match
+  ensureCgen GoodImportcType
+
+block:
+  type Foo6{.importc.} = object
+    a1: cint
+  doAssert compiles(Foo6.sizeof)
+  static: doAssert not compiles(static(Foo6.sizeof))
+
+when defined caseBad:
+  # Each case below should give a static cgen assert fail message
+
+  block:
+    type BadImportcType {.importc: "unsigned char", nodecl.} = uint64
+      # "sizeof" check will fail
+    ensureCgen BadImportcType
+
+  block:
+    type Foo2AliasBad{.importc: "struct Foo2", size: 1.} = object
+      a: cint
+    ensureCgen Foo2AliasBad
+
+  block:
+    type Foo5{.importc.} = array[4, cint]
+    ensureCgen Foo5
+
+  block:
+    type Foo5{.importc.} = array[3, bool]
+    ensureCgen Foo5
+
+  block:
+    type Foo6{.importc:"struct Foo6", completeStruct.} = object
+      a1: cint
+      # a2: bool # missing this should trigger assert fail
+      a3: cfloat
+      a4: ptr Foo6
+    ensureCgen Foo6
+
+  when false:
+    block:
+      # pre-existing BUG: this should give a CT error in semcheck because `size`
+      # disagrees with `array[3, cint]`
+      type Foo5{.importc, size: 1.} = array[3, cint]
+      ensureCgen Foo5
diff --git a/tests/misc/msizeof5.nim.cfg b/tests/misc/msizeof5.nim.cfg
new file mode 100644
index 000000000..dc0712a8c
--- /dev/null
+++ b/tests/misc/msizeof5.nim.cfg
@@ -0,0 +1,5 @@
+# Do not limit number of error messages from backend compiler.
+gcc.options.always %= "${gcc.options.always} -fmax-errors=100"
+clang.options.always %= "${clang.options.always} -ferror-limit=100"
+gcc.cpp.options.always %= "${gcc.cpp.options.always} -fmax-errors=100"
+clang.cpp.options.always %= "${clang.cpp.options.always} -ferror-limit=100"
diff --git a/tests/misc/mtlsemulation.h b/tests/misc/mtlsemulation.h
new file mode 100644
index 000000000..992977acd
--- /dev/null
+++ b/tests/misc/mtlsemulation.h
@@ -0,0 +1,37 @@
+#include <stdio.h>
+
+struct Foo1 {
+  /*
+  uncommenting would give:
+  error: initializer for thread-local variable must be a constant expression
+  N_LIB_PRIVATE NIM_THREADVAR Foo1 g1__9brEZhPEldbVrNpdRGmWESA;
+  */
+  // Foo1() noexcept { }
+
+  /*
+  uncommenting would give:
+  error: type of thread-local variable has non-trivial destruction
+  */
+  // ~Foo1() { }
+  int x;
+};
+
+struct Foo2 {
+  Foo2() noexcept { }
+  ~Foo2() { }
+  int x;
+};
+
+static int ctorCalls = 0;
+static int dtorCalls = 0;
+
+struct Foo3 {
+  Foo3() noexcept {
+    ctorCalls = ctorCalls + 1;
+    x = 10;
+  }
+  ~Foo3() {
+    dtorCalls = dtorCalls + 1;
+  }
+  int x;
+};
diff --git a/tests/misc/parsecomb.nim b/tests/misc/parsecomb.nim
index 4ff2f65d2..4f149cbf6 100644
--- a/tests/misc/parsecomb.nim
+++ b/tests/misc/parsecomb.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:arc; --mm:refc"
+"""
+
 type Input[T] = object
   toks: seq[T]
   index: int
diff --git a/tests/misc/t11634.nim b/tests/misc/t11634.nim
new file mode 100644
index 000000000..390af40f4
--- /dev/null
+++ b/tests/misc/t11634.nim
@@ -0,0 +1,17 @@
+discard """
+  action: reject
+"""
+
+type Foo = ref object
+  val: int
+
+proc divmod(a, b: Foo): (Foo, Foo) =
+  (
+    Foo(val: a.val div b.val),
+    Foo(val: a.val mod b.val)
+  )
+
+block:
+  let a {.compileTime.} = Foo(val: 2)
+  let b {.compileTime.} = Foo(val: 3)
+  let (c11634 {.compileTime.}, d11634 {.compileTime.}) = divmod(a, b)
diff --git a/tests/misc/t12480.nim b/tests/misc/t12480.nim
new file mode 100644
index 000000000..992533ef6
--- /dev/null
+++ b/tests/misc/t12480.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "'return' not allowed here"
+"""
+
+return
\ No newline at end of file
diff --git a/tests/misc/t12869.nim b/tests/misc/t12869.nim
new file mode 100644
index 000000000..054e28a03
--- /dev/null
+++ b/tests/misc/t12869.nim
@@ -0,0 +1,14 @@
+discard """
+  errormsg: "type mismatch: got <openArray[int], proc (x: GenericParam, y: GenericParam): auto, SortOrder>"
+  line: 12
+"""
+
+import sugar
+from algorithm import sorted, SortOrder
+
+let a = 5
+
+proc sorted*[T](a: openArray[T], key: proc(v: T): int, order = SortOrder.Ascending): seq[T] =
+  sorted(a, (x, y) => key(x) < key(y), order)
+
+echo sorted(@[9, 1, 8, 2, 6, 4, 5, 0], (x) => (a - x).abs)
diff --git a/tests/misc/t14667.nim b/tests/misc/t14667.nim
new file mode 100644
index 000000000..3034e2841
--- /dev/null
+++ b/tests/misc/t14667.nim
@@ -0,0 +1,12 @@
+discard """
+  matrix: "--cc:vcc"
+  disabled: "linux"
+  disabled: "bsd"
+  disabled: "osx"
+  disabled: "unix"
+  disabled: "posix"
+"""
+
+type A = tuple
+discard ()
+discard default(A)
diff --git a/tests/misc/t15351.nim b/tests/misc/t15351.nim
new file mode 100644
index 000000000..c31e604a2
--- /dev/null
+++ b/tests/misc/t15351.nim
@@ -0,0 +1,5 @@
+discard """
+  action: "compile"
+"""
+var
+  ## TODO: broken
diff --git a/tests/misc/t15955.nim b/tests/misc/t15955.nim
new file mode 100644
index 000000000..7441e5398
--- /dev/null
+++ b/tests/misc/t15955.nim
@@ -0,0 +1,22 @@
+discard """
+joinable: false
+"""
+
+import stdtest/specialpaths
+import std/[osproc, strformat, os]
+
+const
+  nim = getCurrentCompilerExe()
+  buildLib = buildDir / "libD20220923T19380"
+  currentDir = splitFile(currentSourcePath).dir
+  file = currentDir / "m15955.nim"
+  main = currentDir / "m15955_main.nim"
+
+
+proc runCmd(cmd: string) =
+  let (msg, code) = execCmdEx(cmd)
+  doAssert code == 0, msg
+
+
+runCmd fmt"{nim} c -o:{buildLib} --nomain --nimMainPrefix:libA -f --app:staticlib {file}"
+runCmd fmt"{nim} c -r {main}"
diff --git a/tests/misc/t16244.nim b/tests/misc/t16244.nim
new file mode 100644
index 000000000..5e8128736
--- /dev/null
+++ b/tests/misc/t16244.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "type mismatch: got <int, float64>"
+  line: 9
+"""
+
+proc g(): auto = 1
+proc h(): auto = 1.0
+
+var a = g() + h()
diff --git a/tests/misc/t16264.nim b/tests/misc/t16264.nim
new file mode 100644
index 000000000..afe319e6c
--- /dev/null
+++ b/tests/misc/t16264.nim
@@ -0,0 +1,2 @@
+import times
+doAssert low(Time) == fromUnix(0)
\ No newline at end of file
diff --git a/tests/misc/t16541.nim b/tests/misc/t16541.nim
new file mode 100644
index 000000000..452327e8f
--- /dev/null
+++ b/tests/misc/t16541.nim
@@ -0,0 +1,12 @@
+discard """
+  action: "reject"
+
+"""
+
+import strutils, sugar, nre
+
+proc my_replace*(s: string, r: Regex, by: string | (proc (match: string): string)): string =
+  nre.replace(s, r, by)
+
+discard my_replace("abcde", re"[bcd]", match => match.to_upper) == "aBCDe"
+discard my_replace("abcde", re"[bcd]", (match: string) => match.to_upper) == "aBCDe"
diff --git a/tests/misc/t17286.nim b/tests/misc/t17286.nim
new file mode 100644
index 000000000..3a54e624e
--- /dev/null
+++ b/tests/misc/t17286.nim
@@ -0,0 +1,16 @@
+discard """
+  cmd: "nim check -b:js $file"
+  action: "compile"
+"""
+
+# bug #17286
+
+import std/compilesettings
+
+static:
+  doAssert querySetting(backend) == "js"
+  doAssert defined(js)
+  doAssert not defined(c)
+
+import random
+randomize()
\ No newline at end of file
diff --git a/tests/misc/t18077.nim b/tests/misc/t18077.nim
new file mode 100644
index 000000000..6cd05d575
--- /dev/null
+++ b/tests/misc/t18077.nim
@@ -0,0 +1,21 @@
+discard """
+  cmd: '''nim doc -d:nimTestsT18077b:4 --doccmd:"-d:nimTestsT18077 -d:nimTestsT18077b:3 --hints:off" $file'''
+  action: compile
+"""
+
+# bug #18077
+
+const nimTestsT18077b {.intdefine.} = 1
+
+static:
+  when defined(nimdoc):
+    doAssert nimTestsT18077b == 4
+    doAssert not defined(nimTestsT18077)
+  else:
+    doAssert defined(nimTestsT18077)
+    doAssert nimTestsT18077b == 3
+
+runnableExamples:
+  const nimTestsT18077b {.intdefine.} = 2
+  doAssert nimTestsT18077b == 3
+  doAssert defined(nimTestsT18077)
diff --git a/tests/misc/t18079.nim b/tests/misc/t18079.nim
new file mode 100644
index 000000000..ae64bbff9
--- /dev/null
+++ b/tests/misc/t18079.nim
@@ -0,0 +1,15 @@
+discard """
+  matrix: "--mm:orc"
+"""
+
+type
+  Foo = object
+    y: int
+
+  Bar = object
+    x: Foo
+
+proc baz(state: var Bar):int = 
+  state.x.y = 2
+  state.x.y
+doAssert baz((ref Bar)(x: (new Foo)[])[]) == 2
diff --git a/tests/misc/t19046.nim b/tests/misc/t19046.nim
new file mode 100644
index 000000000..b3bfec7ae
--- /dev/null
+++ b/tests/misc/t19046.nim
@@ -0,0 +1,19 @@
+discard """
+  targets: "c cpp"
+  matrix: "--threads:on"
+  disabled: "win"
+  disabled: "osx"
+  action: compile
+"""
+
+# bug #19046
+
+import std/os
+
+var t: Thread[void]
+
+proc test = discard
+proc main = 
+  createThread(t, test)
+  pinToCpu(t, 1)
+main()
\ No newline at end of file
diff --git a/tests/misc/t20253.nim b/tests/misc/t20253.nim
new file mode 100644
index 000000000..d47c36c55
--- /dev/null
+++ b/tests/misc/t20253.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "'result' requires explicit initialization"
+  line: 10
+"""
+
+type Meow {.requiresInit.} = object 
+  init: bool
+
+proc initMeow(): Meow =
+  discard
diff --git a/tests/misc/t20289.nim b/tests/misc/t20289.nim
new file mode 100644
index 000000000..5a0a269f0
--- /dev/null
+++ b/tests/misc/t20289.nim
@@ -0,0 +1,15 @@
+discard """
+  action: reject
+"""
+
+type E[T] = object
+  v: T
+
+template j[T](R: type E[T], x: untyped): R = R(v: x)
+template d[T](O: type E, v: T): E[T] = E[T].j(v)
+
+proc w[T](): E[T] =
+  template r(k: int): auto = default(T)
+  E.d r
+
+discard w[int]()
diff --git a/tests/misc/t20456_2.nim b/tests/misc/t20456_2.nim
new file mode 100644
index 000000000..37e52c452
--- /dev/null
+++ b/tests/misc/t20456_2.nim
@@ -0,0 +1,14 @@
+discard """
+  joinable: false
+"""
+
+import std/[osproc, os, strformat]
+from stdtest/specialpaths import testsDir
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+const
+  nim = getCurrentCompilerExe()
+  file = testsDir / "misc" / "m20456.nims"
+doAssert execCmd(fmt"{nim} check {file}") == 0
diff --git a/tests/misc/t20883.nim b/tests/misc/t20883.nim
new file mode 100644
index 000000000..92e7929f4
--- /dev/null
+++ b/tests/misc/t20883.nim
@@ -0,0 +1,13 @@
+discard """
+  action: reject
+nimout: '''
+t20883.nim(13, 4) template/generic instantiation of `foo` from here
+t20883.nim(9, 11) Error: cannot instantiate: 'U'
+'''
+"""
+
+proc foo*[U](x: U = U(1e-6)) =
+  echo x
+
+foo[float]()
+foo()
diff --git a/tests/misc/t21109.nim b/tests/misc/t21109.nim
new file mode 100644
index 000000000..0f7980896
--- /dev/null
+++ b/tests/misc/t21109.nim
@@ -0,0 +1,13 @@
+discard """
+  action: reject
+  errormsg: "type expected"
+  file: "iterators.nim"
+"""
+
+
+template b(j: untyped) = j
+template m() = discard
+
+b:
+  for t, f in @[]:
+    m()
diff --git a/tests/misc/t21443.nim b/tests/misc/t21443.nim
new file mode 100644
index 000000000..70413c5b3
--- /dev/null
+++ b/tests/misc/t21443.nim
@@ -0,0 +1,6 @@
+import std/envvars
+
+# bug #19292
+putEnv("NimPutEnvTest", "test")
+# bug #21122
+doAssert getEnv("NimPutEnvTest") == "test"
diff --git a/tests/misc/t23240.nim b/tests/misc/t23240.nim
new file mode 100644
index 000000000..d5edcefe8
--- /dev/null
+++ b/tests/misc/t23240.nim
@@ -0,0 +1,6 @@
+discard """
+  cmd: "nim c foo/bar.nim"
+  action: "reject"
+  errormsg: "cannot open 'foo/'"
+  file: ""
+"""
diff --git a/tests/misc/t3482.nim b/tests/misc/t3482.nim
new file mode 100644
index 000000000..33b3b8f40
--- /dev/null
+++ b/tests/misc/t3482.nim
@@ -0,0 +1,15 @@
+discard """
+  action: reject
+  nimout: "t3482.nim(13, 8) Error: undeclared identifier: 'output'"
+"""
+# bug #3482 (correct behavior since 1.4.0, cgen error in 1.2.0)
+template foo*(body: typed) =
+  if true:
+    body
+
+proc test =
+  foo:
+    var output = ""
+  echo output.len
+
+test()
diff --git a/tests/misc/t3907.nim b/tests/misc/t3907.nim
new file mode 100644
index 000000000..45fc75e81
--- /dev/null
+++ b/tests/misc/t3907.nim
@@ -0,0 +1,10 @@
+import std/assertions
+
+let a = 0
+let b = if false: -1 else: a
+doAssert b == 0
+
+let c: range[0..high(int)] = 0
+let d = if false: -1 else: c
+
+doAssert d == 0
diff --git a/tests/misc/t5540.nim b/tests/misc/t5540.nim
new file mode 100644
index 000000000..6a19e70e1
--- /dev/null
+++ b/tests/misc/t5540.nim
@@ -0,0 +1,45 @@
+# bug #5540; works in 1.2.0
+# fails in 1.0 (Error: cannot generate VM code for)
+# fails in 0.18.0 (Error: type mismatch: got <type T>)
+
+block:
+  type
+    Fruit = object
+    Yellow = object
+      a: int
+  template getColor(x: typedesc[Fruit]): typedesc = Yellow
+  type
+    Banana[T] = object
+      b: T
+      a: getColor(Fruit)
+    Apple[T] = object
+      a: T
+      b: getColor(T)
+  block:
+    var x: Banana[int]
+    doAssert x.b == 0
+    doAssert x.a is Yellow
+  block:
+    var x: Apple[Fruit]
+    doAssert x.b is Yellow
+
+block:
+  type
+    Fruit = object
+    Yellow = object
+      a: int
+    
+  template getColor(x: typedesc[Fruit]): typedesc = Yellow
+
+  type
+    Banana[T] = object
+      b: T
+      a: getColor(Fruit)
+
+    Apple[T] = object
+      a: T
+      b: getColor(T)
+      
+  var x: Banana[int]
+  x.b = 13
+  x.a.a = 17
diff --git a/tests/misc/t6549.nim b/tests/misc/t6549.nim
new file mode 100644
index 000000000..1d7393318
--- /dev/null
+++ b/tests/misc/t6549.nim
@@ -0,0 +1,4 @@
+
+const l = $(range[low(uint64) .. high(uint64)])
+const r = "range 0..18446744073709551615(uint64)"
+doAssert l == r
diff --git a/tests/misc/t8404.nim b/tests/misc/t8404.nim
new file mode 100644
index 000000000..87991071c
--- /dev/null
+++ b/tests/misc/t8404.nim
@@ -0,0 +1,33 @@
+discard """
+  targets: "c cpp js"
+"""
+template main() =
+  block: # bug #8404
+    # can conv
+    template float2int(T) =
+      var a = -1.0
+      let b = T(a)
+      doAssert b < 0
+      let c = b + 1
+      doAssert c is T
+      doAssert c == 0
+
+    float2int(int8)
+    float2int(int16)
+    float2int(int32)
+    float2int(int64)
+
+  block:
+    # can handle middle conv
+    # `/` can trigger int to float
+    template float2int(T) =
+      let n = T(1 / 256)
+      doAssert n == 0
+
+    float2int(int8)
+    float2int(int16)
+    float2int(int32)
+    # float2int(int64)
+main()
+static:
+  main()
diff --git a/tests/misc/t8545.nim b/tests/misc/t8545.nim
new file mode 100644
index 000000000..48b886cb8
--- /dev/null
+++ b/tests/misc/t8545.nim
@@ -0,0 +1,24 @@
+discard """
+  # just tests that this doesn't crash the compiler
+  errormsg: "cannot instantiate: 'a:type'"
+"""
+
+# bug #8545
+
+template bar(a: static[bool]): untyped = int
+
+proc main() =
+  proc foo1(a: static[bool]): auto = 1
+  doAssert foo1(true) == 1
+
+  proc foo2(a: static[bool]): bar(a) = 1
+  doAssert foo2(true) == 1
+
+  proc foo3(a: static[bool]): bar(cast[static[bool]](a)) = 1
+  doAssert foo3(true) == 1
+
+  proc foo4(a: static[bool]): bar(static(a)) = 1
+  doAssert foo4(true) == 1
+
+static: main()
+main()
diff --git a/tests/misc/t9039.nim b/tests/misc/t9039.nim
new file mode 100644
index 000000000..3271cd34e
--- /dev/null
+++ b/tests/misc/t9039.nim
@@ -0,0 +1,24 @@
+discard """
+  action: reject
+  nimout: '''
+t9039.nim(22, 22) Error: type mismatch: got <array[0..2, int], int, array[0..1, int]>
+but expression 'nesting + 1' is of type: int
+'''
+"""
+
+# bug #9039; this used to hang in 0.19.0
+
+
+
+
+
+# line 15
+func default(T: typedesc[array]): T = discard
+doAssert default(array[3, int]) == [0, 0, 0]
+func shapeBad*[T: not char](s: openArray[T], rank: static[int], nesting = 0, parent_shape = default(array[rank, int])): array[rank, int] =
+  result = parent_shape
+  result[nesting] = s.len
+  when (T is seq|array):
+    result = shapeBad(s[0], nesting + 1, result)
+let a1 = [1, 2, 3].shapeBad(rank = 1)
+let a2 = [[1, 2, 3], [4, 5, 6]].shapeBad(rank = 2)
diff --git a/tests/misc/t9091.nim b/tests/misc/t9091.nim
new file mode 100644
index 000000000..6e7a98ca5
--- /dev/null
+++ b/tests/misc/t9091.nim
@@ -0,0 +1,33 @@
+# bug #9091
+
+import streams
+
+block:
+  type Mine = ref object
+    a: int
+
+  proc write(io: Stream, t: Mine) =
+    io.write("sure")
+
+  let str = newStringStream()
+  let mi = new Mine
+
+  str.write(mi)
+  str.setPosition 0
+  doAssert str.readAll == "sure"
+
+block:
+  type
+    AObj = object
+      x: int
+
+  proc foo(a: int): string = ""
+
+  proc test(args: varargs[string, foo]) =
+    doAssert false
+
+  proc test(a: AObj) =
+    discard
+
+  let x = AObj()
+  test(x)
diff --git a/tests/misc/t9710.nim b/tests/misc/t9710.nim
new file mode 100644
index 000000000..c65cb7bf4
--- /dev/null
+++ b/tests/misc/t9710.nim
@@ -0,0 +1,6 @@
+discard """
+  matrix: "--debugger:native"
+"""
+# bug #9710
+for i in 1 || 200:
+  discard i
diff --git a/tests/misc/t99bott.nim b/tests/misc/t99bott.nim
index 62ccfbe16..f60023818 100644
--- a/tests/misc/t99bott.nim
+++ b/tests/misc/t99bott.nim
@@ -1,9 +1,9 @@
 discard """
+  errormsg: "cannot evaluate at compile time: bn"
   file: "t99bott.nim"
   line: 26
-  errormsg: "cannot evaluate at compile time: bn"
-  disabled: false
 """
+
 ## 99 Bottles of Beer
 ## http://www.99-bottles-of-beer.net/
 ## Nim version
@@ -30,7 +30,3 @@ for bn in countdown(99, 1):
 
 echo "No more bottles of beer on the wall, no more bottles of beer."
 echo "Go to the store and buy some more, 99 bottles of beer on the wall."
-
-
-
-
diff --git a/tests/misc/tack.nim b/tests/misc/tack.nim
index a0afab9e8..458395ef6 100644
--- a/tests/misc/tack.nim
+++ b/tests/misc/tack.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tack.nim"
   output: "125"
 """
 # the Ackermann function
@@ -17,5 +16,4 @@ proc ack(x, y: int): int =
 
 # echo(ack(0, 0))
 write(stdout, ack(3, 4)) #OUT 125
-
-
+write stdout, "\n"
diff --git a/tests/misc/taddr.nim b/tests/misc/taddr.nim
new file mode 100644
index 000000000..64f95c7e3
--- /dev/null
+++ b/tests/misc/taddr.nim
@@ -0,0 +1,295 @@
+discard """
+  targets: "c cpp js"
+  matrix: "; -d:release"
+"""
+
+type T = object
+  x: int
+  s: string
+
+var obj: T
+var fieldAddr = addr(obj.x)
+var objAddr = addr(obj)
+
+# Integer tests
+var field = fieldAddr[]
+doAssert field == 0
+
+var objDeref = objAddr[]
+doAssert objDeref.x == 0
+
+# Change value
+obj.x = 42
+
+doAssert field == 0
+doAssert objDeref.x == 0
+
+field = fieldAddr[]
+objDeref = objAddr[]
+
+doAssert field == 42
+doAssert objDeref.x == 42
+
+# String tests
+obj.s = "lorem ipsum dolor sit amet"
+when defined(gcArc) or defined(gcOrc):
+  prepareMutation(obj.s)
+
+
+var indexAddr = addr(obj.s[2])
+
+doAssert indexAddr[] == 'r'
+
+indexAddr[] = 'd'
+
+doAssert indexAddr[] == 'd'
+
+doAssert obj.s == "lodem ipsum dolor sit amet"
+
+# Bug #2148
+var x: array[2, int]
+var y = addr x[1]
+
+y[] = 12
+doAssert(x[1] == 12)
+
+type
+  Foo = object
+    bar: int
+
+var foo: array[2, Foo]
+var z = addr foo[1]
+
+z[].bar = 12345
+doAssert(foo[1].bar == 12345)
+
+var t : tuple[a, b: int]
+var pt = addr t[1]
+pt[] = 123
+doAssert(t.b == 123)
+
+#block: # Test "untyped" pointer.
+proc testPtr(p: pointer, a: int) =
+  doAssert(a == 5)
+  (cast[ptr int](p))[] = 124
+var i = 123
+testPtr(addr i, 5)
+doAssert(i == 124)
+
+var someGlobal = 5
+proc getSomeGlobalPtr(): ptr int = addr someGlobal
+let someGlobalPtr = getSomeGlobalPtr()
+doAssert(someGlobalPtr[] == 5)
+someGlobalPtr[] = 10
+doAssert(someGlobal == 10)
+
+block:
+  # bug #14576
+  # lots of these used to give: Error: internal error: genAddr: 2
+  proc byLent[T](a: T): lent T = a
+  proc byPtr[T](a: T): ptr T = a.addr
+
+  block:
+    let a = (10,11)
+    let (x,y) = byLent(a)
+    doAssert (x,y) == a
+
+  block: # (with -d:release) bug #14578
+    let a = 10
+    doAssert byLent(a) == 10
+    let a2 = byLent(a)
+    doAssert a2 == 10
+
+  block:
+    let a = [11,12]
+    doAssert byLent(a) == [11,12] # bug #15958
+    let a2 = (11,)
+    doAssert byLent(a2) == (11,)
+
+  block:
+    proc byLent2[T](a: seq[T]): lent T = a[1]
+    var a = @[20,21,22]
+    doAssert byLent2(a) == 21
+
+  block: # sanity checks
+    proc bar[T](a: var T): var T = a
+    var a = (10, 11)
+    let (k,v) = bar(a)
+    doAssert (k, v) == a
+    doAssert k == 10
+    bar(a)[0]+=100
+    doAssert a == (110, 11)
+    var a2 = 12
+    doAssert bar(a2) == a2
+    bar(a2).inc
+    doAssert a2 == 13
+
+  block: # pending bug #15959
+    when false:
+      proc byLent2[T](a: T): lent type(a[0]) = a[0]
+
+proc test14420() = # bug #14420
+  # s/proc/template/ would hit bug #16005
+  block:
+    type Foo = object
+      x: float
+
+    proc fn(a: var Foo): var float =
+      ## WAS: discard <- turn this into a comment (or a `discard`) and error disappears
+      # result = a.x # this works
+      a.x #  WAS: Error: limited VM support for 'addr'
+
+    proc fn2(a: var Foo): var float =
+      result = a.x # this works
+      a.x #  WAS: Error: limited VM support for 'addr'
+
+    var a = Foo()
+    discard fn(a)
+    discard fn2(a)
+
+  block:
+    proc byLent2[T](a: T): lent T =
+      runnableExamples: discard
+      a
+    proc byLent3[T](a: T): lent T =
+      runnableExamples: discard
+      result = a
+    var a = 10
+    let x3 = byLent3(a) # works
+    let x2 = byLent2(a) # WAS: Error: internal error: genAddr: nkStmtListExpr
+
+  block:
+    type MyOption[T] = object
+      case has: bool
+      of true:
+        value: T
+      of false:
+        discard
+    func some[T](val: T): MyOption[T] =
+      result = MyOption[T](has: true, value: val)
+    func get[T](opt: MyOption[T]): lent T =
+      doAssert opt.has
+      # result = opt.value # this was ok
+      opt.value # this had the bug
+    let x = some(10)
+    doAssert x.get() == 10
+
+template test14339() = # bug #14339
+  block:
+    type
+      Node = ref object
+        val: int
+    proc bar(c: Node): var int =
+      var n = c # was: Error: limited VM support for 'addr'
+      c.val
+    var a = Node()
+    discard a.bar()
+  block:
+    type
+      Node = ref object
+        val: int
+    proc bar(c: Node): var int =
+      var n = c
+      doAssert n.val == n[].val
+      n.val
+    var a = Node(val: 3)
+    a.bar() = 5
+    when nimvm:
+      doAssert a.val == 5
+    else:
+      when not defined(js): # pending bug #16003
+        doAssert a.val == 5
+
+template testStatic15464() = # bug #15464
+  proc access(s: var seq[char], i: int): var char = s[i]
+  proc access(s: var string, i: int): var char = s[i]
+  static:
+    var s = @['a', 'b', 'c']
+    access(s, 2) = 'C'
+    doAssert access(s, 2) == 'C'
+  static:
+    var s = "abc"
+    access(s, 2) = 'C'
+    doAssert access(s, 2) == 'C'
+
+proc test15464() = # bug #15464 (v2)
+  proc access(s: var seq[char], i: int): var char = s[i]
+  proc access(s: var string, i: int): var char = s[i]
+  block:
+    var s = @['a', 'b', 'c']
+    access(s, 2) = 'C'
+    doAssert access(s, 2) == 'C'
+  block:
+    var s = "abc"
+    access(s, 2) = 'C'
+    doAssert access(s, 2) == 'C'
+
+block: # bug #15939
+  block:
+    const foo = "foo"
+    proc proc1(s: var string) =
+      if s[^1] notin {'a'..'z'}:
+        s = ""
+    proc proc2(f: string): string =
+      result = f
+      proc1(result)
+    const bar = proc2(foo)
+    doAssert bar == "foo"
+
+template prepareMutationForOrc(x: string) =
+  when defined(gcArc) or defined(gcOrc):
+    when nimvm:
+      discard
+    else:
+      prepareMutation(x)
+
+proc test15939() = # bug #15939 (v2)
+  template fn(a) =
+    when typeof(a) is string:
+      prepareMutationForOrc(a)
+    let pa = a[0].addr
+    doAssert pa != nil
+    doAssert pa[] == 'a'
+    pa[] = 'x'
+    doAssert pa[] == 'x'
+    doAssert a == "xbc"
+    when not defined js: # otherwise overflows
+      let pa2 = cast[ptr char](cast[int](pa) + 1)
+      doAssert pa2[] == 'b'
+      pa2[] = 'B'
+      doAssert a == "xBc"
+
+  # mystring[ind].addr
+  var a = "abc"
+  fn(a)
+
+  # mycstring[ind].addr
+  template cstringTest =
+    var a2 = "abc"
+    prepareMutationForOrc(a2)
+    var b2 = a2.cstring
+    fn(b2)
+  when nimvm: cstringTest()
+  else: # can't take address of cstring element in js
+    when not defined(js): cstringTest()
+
+block: # bug #23499
+  template volatileStore[T](dest: ptr T, val: T) =
+    dest[] = val
+
+  proc foo =
+    var ctr = 0
+    volatileStore(addr ctr, 0)
+
+  foo()
+
+template main =
+  # xxx wrap all other tests here like that so they're also tested in VM
+  test14420()
+  test14339()
+  test15464()
+  test15939()
+
+testStatic15464()
+static: main()
+main()
diff --git a/tests/misc/tapp_lib_staticlib.nim b/tests/misc/tapp_lib_staticlib.nim
new file mode 100644
index 000000000..92c9acbc3
--- /dev/null
+++ b/tests/misc/tapp_lib_staticlib.nim
@@ -0,0 +1,27 @@
+discard """
+joinable: false
+"""
+
+# bug #16949
+
+when defined case1:
+  proc foo(): int {.exportc.} = 10
+elif defined case2:
+  proc foo(): int {.exportc, dynlib.} = 10
+elif defined caseMain:
+  proc foo(): int {.importc.}
+  doAssert foo() == 10
+else:
+  import stdtest/specialpaths
+  import std/[os, strformat, strutils, compilesettings]
+  proc runCmd(cmd: string) =
+    doAssert execShellCmd(cmd) == 0, $cmd
+  const
+    file = currentSourcePath
+    nim = getCurrentCompilerExe()
+    mode = querySetting(backend)
+  proc test(lib, options: string) =
+    runCmd fmt"{nim} {mode} -o:{lib} --nomain {options} -f {file}"
+    # runCmd fmt"{nim} r -b:{mode} --passl:{lib} -d:caseMain -f {file}" # pending https://github.com/nim-lang/Nim/pull/16945
+  test(buildDir / "libD20210205T172314.a", "--app:staticlib -d:nimLinkerWeakSymbols -d:case1")
+  test(buildDir / DynlibFormat % "D20210205T172720", "--app:lib -d:case2")
diff --git a/tests/misc/tbug511622.nim b/tests/misc/tbug511622.nim
index a5360423d..1af6380ed 100644
--- a/tests/misc/tbug511622.nim
+++ b/tests/misc/tbug511622.nim
@@ -1,8 +1,7 @@
 discard """
-  file: "tbug511622.nim"
   output: "3"
 """
-import StrUtils, Math
+import math
 
 proc FibonacciA(n: int): int64 =
   var fn = float64(n)
@@ -11,6 +10,3 @@ proc FibonacciA(n: int): int64 =
   return int64((pow(p, fn) + pow(q, fn)) / sqrt(5.0))
 
 echo FibonacciA(4) #OUT 3
-
-
-
diff --git a/tests/misc/tcast.nim b/tests/misc/tcast.nim
index 4e27040fb..73196e76c 100644
--- a/tests/misc/tcast.nim
+++ b/tests/misc/tcast.nim
@@ -1,13 +1,17 @@
 discard """
   output: '''
 Hello World
+Hello World
 Hello World'''
+  joinable: false
 """
 type MyProc = proc() {.cdecl.}
 type MyProc2 = proc() {.nimcall.}
 type MyProc3 = proc() #{.closure.} is implicit
 
-proc testProc()  = echo "Hello World"
+proc testProc() {.exportc:"foo".} = echo "Hello World"
+
+template reject(x) = doAssert(not compiles(x))
 
 proc callPointer(p: pointer) =
   # can cast to proc(){.cdecl.}
@@ -15,9 +19,90 @@ proc callPointer(p: pointer) =
   # can cast to proc(){.nimcall.}
   let ffunc1 = cast[MyProc2](p)
   # cannot cast to proc(){.closure.}
-  doAssert(not compiles(cast[MyProc3](p)))
+  reject: cast[MyProc3](p)
 
   ffunc0()
   ffunc1()
 
+    # bug #5901
+  proc foo() {.importc.}
+  (cast[proc(a: int) {.cdecl.}](foo))(5)
+
 callPointer(cast[pointer](testProc))
+
+reject: discard cast[enum](0)
+proc a = echo "hi"
+
+reject: discard cast[ptr](a)
+
+# bug #15623
+block:
+  if false:
+    let x = cast[ptr int](nil)
+    echo x[]
+
+block:
+  if false:
+    var x: ref int = nil
+    echo cast[ptr int](x)[]
+
+block:
+  doAssert cast[int](cast[ptr int](nil)) == 0
+
+block:
+  var x: ref int = nil
+  doAssert cast[int](cast[ptr int](x)) == 0
+
+block: # cast of nil
+  block:
+    static:
+      let a = cast[pointer](nil)
+      doAssert a.repr == "nil"
+
+  block:
+    static:
+      doAssert cast[ptr int](nil).repr == "nil"
+
+  block:
+    const str = cast[ptr int](nil)
+    static:
+      doAssert str.repr == "nil"
+
+  block:
+    static:
+      doAssert cast[ptr int](nil).repr == "nil"
+
+  block:
+    static:
+      doAssert cast[RootRef](nil).repr == "nil"
+
+  when false: # xxx bug #15730, not fixed yet
+    block:
+      static:
+        doAssert cast[cstring](nil).repr == "nil"
+
+template main() =
+  # xxx move all under here to get tested in VM
+  block: # cast of enum
+    type Koo = enum k1, k2
+    type Goo = enum g1, g2
+    type Boo = enum b1 = -1, b2, b3, b4
+    type Coo = enum c1 = -1i8, c2, c3, c4
+    when nimvm:
+      # xxx: Error: VM does not support 'cast' from tyEnum to tyEnum
+      discard
+    else:
+      doAssert cast[Koo](k2) == k2
+      doAssert cast[Goo](k2) == g2
+      doAssert cast[Goo](k2.ord) == g2
+
+      doAssert b3.ord == 1
+      doAssert cast[Koo](b3) == k2
+      doAssert cast[Boo](k2) == b3
+
+      doAssert c3.ord == 1
+      doAssert cast[Koo](c3) == k2
+      doAssert cast[Coo](k2) == c3
+
+static: main()
+main()
diff --git a/tests/misc/tcmdline.nim b/tests/misc/tcmdline.nim
index cb8cb402c..71e1301ca 100644
--- a/tests/misc/tcmdline.nim
+++ b/tests/misc/tcmdline.nim
@@ -1,3 +1,7 @@
+discard """
+outputsub: "Number of parameters: 0"
+joinable: false
+"""
 # Test the command line
 
 import
diff --git a/tests/misc/tconv.nim b/tests/misc/tconv.nim
new file mode 100644
index 000000000..90fae868b
--- /dev/null
+++ b/tests/misc/tconv.nim
@@ -0,0 +1,143 @@
+discard """
+  matrix: "--warningAsError:EnumConv --warningAsError:CStringConv"
+"""
+
+from std/enumutils import items  # missing from the example code
+from std/sequtils import toSeq
+
+template reject(x) =
+  static: doAssert(not compiles(x))
+template accept(x) =
+  static: doAssert(compiles(x))
+
+reject:
+    const x = int8(300)
+
+reject:
+    const x = int64(NaN)
+
+type
+    R = range[0..10]
+
+reject:
+    const x = R(11)
+
+reject:
+    const x = R(11.0)
+
+reject:
+    const x = R(NaN)
+
+reject:
+    const x = R(Inf)
+
+type
+    FloatRange = range[0'f..10'f]
+
+reject:
+    const x = FloatRange(-1'f)
+
+reject:
+    const x = FloatRange(-1)
+
+reject:
+    const x = FloatRange(NaN)
+
+block:
+    const x = float32(NaN)
+
+type E = enum a, b, c
+
+reject:
+    const e = E(4)
+
+block: # issue 3766
+
+  type R = range[0..2]
+
+  reject:
+    type
+      T[n: static[R]] = object
+      V = T[3.R]
+
+  reject:
+    proc r(x: static[R]) =
+      echo x
+    r 3.R
+
+
+block: # https://github.com/nim-lang/RFCs/issues/294
+  type Koo = enum k1, k2
+  type Goo = enum g1, g2
+
+  accept: Koo(k2)
+  accept: k2.Koo
+  accept: k2.int.Goo
+
+  reject: Goo(k2)
+  reject: k2.Goo
+  reject: k2.string
+
+  {.push warningAsError[EnumConv]:off.}
+  discard Goo(k2)
+  accept: Goo(k2)
+  accept: k2.Goo
+  reject: k2.string
+  {.pop.}
+
+  reject: Goo(k2)
+  reject: k2.Goo
+
+  type KooRange = range[k2..k2]
+  accept: KooRange(k2)
+  accept: k2.KooRange
+  let k2ranged: KooRange = k2
+  accept: Koo(k2ranged)
+  accept: k2ranged.Koo
+
+reject:
+  # bug #18550
+  proc f(c: char): cstring =
+    var x = newString(109*1024*1024)
+    x[0] = c
+    x
+
+{.push warning[AnyEnumConv]:on, warningAsError[AnyEnumConv]:on.}
+
+reject:
+  type
+    Foo = enum
+      one
+      three
+
+  var va = 2
+  var vb = va.Foo
+
+{.pop.}
+
+{.push warningAsError[HoleEnumConv]:on.}
+
+reject:
+  # bug #12815
+  type
+    Hole = enum
+      one = 1
+      three = 3
+
+  var va = 2
+  var vb = va.Hole
+
+block: # bug #22844
+  type
+    A = enum
+      a0 = 2
+      a1 = 4
+      a2
+    B[T] = enum
+      b0 = 2
+      b1 = 4
+
+  doAssert A.toSeq == [a0, a1, a2]
+  doAssert B[float].toSeq == [B[float].b0, B[float].b1]
+
+{.pop.}
diff --git a/tests/misc/tcsharpusingstatement.nim b/tests/misc/tcsharpusingstatement.nim
new file mode 100644
index 000000000..1ce553895
--- /dev/null
+++ b/tests/misc/tcsharpusingstatement.nim
@@ -0,0 +1,76 @@
+discard """
+  output: "Using test.Closing test."
+"""
+
+import
+  macros
+
+# This macro mimics the using statement from C#
+#
+# It's kept only as a test for the macro system
+# Nim's destructors offer a mechanism for automatic
+# disposal of resources.
+#
+macro autoClose(args: varargs[untyped]): untyped =
+  let e = callsite()
+  if e.len != 3:
+    error "Using statement: unexpected number of arguments. Got " &
+      $e.len & ", expected: 1 or more variable assignments and a block"
+
+  var args = e
+  var body = e[2]
+
+  var
+    variables : seq[NimNode]
+    closingCalls : seq[NimNode]
+
+  newSeq(variables, 0)
+  newSeq(closingCalls, 0)
+
+  for i in countup(1, args.len-2):
+    if args[i].kind == nnkExprEqExpr:
+      var varName = args[i][0]
+      var varValue = args[i][1]
+
+      var varAssignment = newNimNode(nnkIdentDefs)
+      varAssignment.add(varName)
+      varAssignment.add(newNimNode(nnkEmpty)) # empty means no type
+      varAssignment.add(varValue)
+      variables.add(varAssignment)
+
+      closingCalls.add(newCall(newIdentNode("close"), varName))
+    else:
+      error "Using statement: Unexpected expression. Got " &
+        $args[i].kind & " instead of assignment."
+
+  var varSection = newNimNode(nnkVarSection)
+  varSection.add(variables)
+
+  var finallyBlock = newNimNode(nnkStmtList)
+  finallyBlock.add(closingCalls)
+
+  result = quote do:
+    block:
+      `varSection`
+      try:
+        `body`
+      finally:
+        `finallyBlock`
+
+type
+  TResource* = object
+    field*: string
+
+proc openResource(param: string): TResource =
+  result.field = param
+
+proc close(r: var TResource) =
+  write(stdout, "Closing " & r.field & ".")
+
+proc use(r: var TResource) =
+  write(stdout, "Using " & r.field & ".")
+
+autoClose(r = openResource("test")):
+  use r
+
+write stdout, "\n"
diff --git a/tests/misc/tdangerisrelease.nim b/tests/misc/tdangerisrelease.nim
new file mode 100644
index 000000000..e1854dca5
--- /dev/null
+++ b/tests/misc/tdangerisrelease.nim
@@ -0,0 +1,14 @@
+discard """
+  cmd: "nim c $options -r $file"
+  matrix: "-d:danger; -d:release"
+  output: '''
+a
+b
+c
+'''
+"""
+
+echo "a"
+when defined(release):
+  echo "b"
+echo "c"
diff --git a/tests/misc/tdefine.nim b/tests/misc/tdefine.nim
new file mode 100644
index 000000000..f3fa4711f
--- /dev/null
+++ b/tests/misc/tdefine.nim
@@ -0,0 +1,77 @@
+discard """
+joinable: false
+cmd: "nim c $options -d:booldef -d:booldef2=false -d:intdef=2 -d:strdef=foobar -d:namespaced.define=false -d:double.namespaced.define -r $file"
+matrix: "; -d:useGenericDefine"
+"""
+
+when defined(useGenericDefine):
+  {.pragma: booldefine2, define.}
+  {.pragma: intdefine2, define.}
+  {.pragma: strdefine2, define.}
+else:
+  
+  {.pragma: booldefine2, booldefine.}
+  {.pragma: intdefine2, intdefine.}
+  {.pragma: strdefine2, strdefine.}
+
+const booldef {.booldefine2.} = false
+const booldef2 {.booldefine2.} = true
+const intdef {.intdefine2.} = 0
+const strdef {.strdefine2.} = ""
+
+doAssert defined(booldef)
+doAssert defined(booldef2)
+doAssert defined(intdef)
+doAssert defined(strdef)
+doAssert booldef
+doAssert not booldef2
+doAssert intdef == 2
+doAssert strdef == "foobar"
+
+when defined(useGenericDefine):
+  block:
+    const uintdef {.define: "intdef".}: uint = 17
+    doAssert intdef == int(uintdef)
+    const cstrdef {.define: "strdef".}: cstring = "not strdef"
+    doAssert $cstrdef == strdef
+    type FooBar = enum foo, bar, foobar
+    const enumdef {.define: "strdef".} = foo
+    doAssert $enumdef == strdef
+    doAssert enumdef == foobar
+
+# Intentionally not defined from command line
+const booldef3 {.booldefine2.} = true
+const intdef2 {.intdefine2.} = 1
+const strdef2 {.strdefine2.} = "abc"
+type T = object
+  when booldef3:
+    field1: int
+  when intdef2 == 1:
+    field2: int
+  when strdef2 == "abc":
+    field3: int
+
+doAssert not defined(booldef3)
+doAssert not defined(intdef2)
+doAssert not defined(strdef2)
+discard T(field1: 1, field2: 2, field3: 3)
+
+doAssert defined(namespaced.define)
+const `namespaced.define` {.booldefine2.} = true
+doAssert not `namespaced.define`
+when defined(useGenericDefine):
+  const aliasToNamespacedDefine {.define: "namespaced.define".} = not `namespaced.define`
+else:
+  const aliasToNamespacedDefine {.booldefine: "namespaced.define".} = not `namespaced.define`
+doAssert aliasToNamespacedDefine == `namespaced.define`
+
+doAssert defined(double.namespaced.define)
+const `double.namespaced.define` {.booldefine2.} = false
+doAssert `double.namespaced.define`
+when defined(useGenericDefine):
+  const aliasToDoubleNamespacedDefine {.define: "double.namespaced.define".} = not `double.namespaced.define`
+else:
+  const aliasToDoubleNamespacedDefine {.booldefine: "double.namespaced.define".} = not `double.namespaced.define`
+doAssert aliasToDoubleNamespacedDefine == `double.namespaced.define`
+
+doAssert not defined(namespaced.butnotdefined)
diff --git a/tests/misc/tdllvar.nim b/tests/misc/tdllvar.nim
index 1c1238e8d..68029ddf4 100644
--- a/tests/misc/tdllvar.nim
+++ b/tests/misc/tdllvar.nim
@@ -1,3 +1,7 @@
+discard """
+disabled: true
+"""
+
 import os
 
 proc getDllName: string =
@@ -12,5 +16,3 @@ proc myImport2(s: int) {.cdecl, importc, dynlib: getDllName().}
 
 myImport("test2")
 myImport2(12)
-
-
diff --git a/tests/misc/temptyecho.nim b/tests/misc/temptyecho.nim
deleted file mode 100644
index 5f1aa6515..000000000
--- a/tests/misc/temptyecho.nim
+++ /dev/null
@@ -1,2 +0,0 @@
-echo()
-
diff --git a/tests/misc/tendian.nim b/tests/misc/tendian.nim
deleted file mode 100644
index 91044f4d5..000000000
--- a/tests/misc/tendian.nim
+++ /dev/null
@@ -1,3 +0,0 @@
-# test the new endian magic
-
-writeLine(stdout, repr(system.cpuEndian))
diff --git a/tests/misc/tevents.nim b/tests/misc/tevents.nim
deleted file mode 100644
index caf674084..000000000
--- a/tests/misc/tevents.nim
+++ /dev/null
@@ -1,48 +0,0 @@
-discard """
-file: "tevents.nim"
-output: '''HandlePrintEvent: Output -> Handled print event
-HandlePrintEvent2: Output -> printing for ME
-HandlePrintEvent2: Output -> printing for ME'''
-"""
-
-import events
-
-type
-  PrintEventArgs = object of EventArgs
-    user*: string
-
-proc handleprintevent*(e: EventArgs) =
-    write(stdout, "HandlePrintEvent: Output -> Handled print event\n")
-
-proc handleprintevent2*(e: EventArgs) =
-    var args: PrintEventArgs = PrintEventArgs(e)
-    write(stdout, "HandlePrintEvent2: Output -> printing for " & args.user)
-
-var ee = initEventEmitter()
-
-var eventargs: PrintEventArgs
-eventargs.user = "ME\n"
-
-##method one test
-
-ee.on("print", handleprintevent)
-ee.on("print", handleprintevent2)
-
-ee.emit("print", eventargs)
-
-##method two test
-
-type
-  SomeObject = object of RootObj
-    printEvent: EventHandler
-
-var obj: SomeObject
-obj.printEvent = initEventHandler("print")
-obj.printEvent.addHandler(handleprintevent2)
-
-ee.emit(obj.printEvent, eventargs)
-
-obj.printEvent.removeHandler(handleprintevent2)
-
-ee.emit(obj.printEvent, eventargs)
-
diff --git a/tests/misc/tgenconstraints.nim b/tests/misc/tgenconstraints.nim
deleted file mode 100644
index 6e8fdc738..000000000
--- a/tests/misc/tgenconstraints.nim
+++ /dev/null
@@ -1,32 +0,0 @@
-discard """
-  file: "tgenconstraints.nim"
-  line: 25
-  disabled: true
-  errormsg: "cannot instantiate T2"
-"""
-
-type
-  T1[T: int|string] = object
-    x: T
-
-  T2[T: Ordinal] = object
-    x: T
-
-var x1: T1[int]
-var x2: T1[string]
-var x3: T2[int]
-
-proc foo[T](x: T): T2[T] {.discardable.} =
-  var o: T1[T]
-
-foo(10)
-
-# XXX: allow type intersections in situation like this
-proc bar(x: int|TNumber): T1[type(x)] {.discardable.} =
-  when type(x) is TNumber:
-    var o: T2[type(x)]
-
-bar "test"
-bar 100
-bar 1.1
-
diff --git a/tests/misc/tgetstartmilsecs.nim b/tests/misc/tgetstartmilsecs.nim
deleted file mode 100644
index bf508dd54..000000000
--- a/tests/misc/tgetstartmilsecs.nim
+++ /dev/null
@@ -1,7 +0,0 @@
-#
-import times, os
-
-var start = epochTime()
-os.sleep(1000)
-
-echo epochTime() - start #OUT 1000
diff --git a/tests/misc/thallo.nim b/tests/misc/thallo.nim
index 17e6089ed..8dac56023 100644
--- a/tests/misc/thallo.nim
+++ b/tests/misc/thallo.nim
@@ -1,4 +1,8 @@
-# Hallo
+discard """
+action: compile
+"""
+
+# noted this seems to be an old test file designed for manual testing.
 
 import
   os, strutils, macros
@@ -7,7 +11,7 @@ type
   TMyEnum = enum
     meA, meB, meC, meD
 
-when isMainModule:
+when true:
   {.hint: "this is the main file".}
 
 proc fac[T](x: T): T =
@@ -24,7 +28,7 @@ macro macrotest(n: varargs[untyped]): untyped =
     result.add(newCall("write", n[1], n[i]))
   result.add(newCall("writeLine", n[1], newStrLitNode("")))
 
-macro debug(n: untyped): untyped {.immediate.} =
+macro debug(n: untyped): untyped =
   let n = callsite()
   result = newNimNode(nnkStmtList, n)
   for i in 1..n.len-1:
@@ -44,7 +48,6 @@ echo(["a", "b", "c", "d"].len)
 for x in items(["What's", "your", "name", "?", ]):
   echo(x)
 var `name` = readLine(stdin)
-{.breakpoint.}
 echo("Hi " & thallo.name & "!\n")
 debug(name)
 
@@ -80,5 +83,5 @@ for i in 2..6:
   for j in countdown(i+4, 2):
     echo(fac(i * j))
 
-when isMainModule:
+when true:
   {.hint: "this is the main file".}
diff --git a/tests/misc/theaproots.nim b/tests/misc/theaproots.nim
index 77d0207b0..2dd345254 100644
--- a/tests/misc/theaproots.nim
+++ b/tests/misc/theaproots.nim
@@ -1,3 +1,7 @@
+discard """
+action: compile
+"""
+
 type
   Bar = object
     x: int
@@ -24,7 +28,7 @@ proc acc(x: var Foo): var ref Bar =
 
 proc test(maybeFoo: var Foo,
           maybeSeq: var seq[ref Bar],
-          bars: var openarray[ref Bar],
+          bars: var openArray[ref Bar],
           maybeTup: var Tup) =
   var bb: ref Bar
   maybeFoo.rmaybe = bb
diff --git a/tests/misc/thintoff.nim b/tests/misc/thintoff.nim
deleted file mode 100644
index 95318ce9b..000000000
--- a/tests/misc/thintoff.nim
+++ /dev/null
@@ -1,12 +0,0 @@
-discard """
-  file: "thintoff.nim"
-  output: "0"
-"""
-
-{.hint[XDeclaredButNotUsed]: off.}
-var
-  x: int
-
-echo x #OUT 0
-
-
diff --git a/tests/misc/tidentconcatenations.nim b/tests/misc/tidentconcatenations.nim
deleted file mode 100644
index 302c51d87..000000000
--- a/tests/misc/tidentconcatenations.nim
+++ /dev/null
@@ -1,32 +0,0 @@
-type
-  Hash*[bits: static[int]] = object
-    data*: array[bits div 8, uint8]
-
-{.emit: """
-
-void sha_256(void* input, int input_len, void* output, int output_len) {}
-void sha_512(void* input, int input_len, void* output, int output_len) {}
-
-void keccak_256(void* input, int input_len, void* output, int output_len) {}
-void keccak_512(void* input, int input_len, void* output, int output_len) {}
-
-""".}
-
-template defineKeccak(bits: untyped) =
-  proc `extKeccak bits`(output: pointer, outSize: csize, input: pointer, inputSize: csize) {.nodecl, importc: "keccak_" & astToStr(bits).}
-
-template defineSha(bits: static[int]) =
-  proc `extSha bits`(output: pointer, outSize: csize, input: pointer, inputSize: csize) {.nodecl, importc: "sha_" & astToStr(bits).}
-
-template defineHashProcs(bits) =
-  defineSha(bits)
-  defineKeccak(bits)
-
-defineHashProcs(256)
-defineHashProcs(512)
-
-extSha256(nil, 0, nil, 0)
-extSha512(nil, 0, nil, 0)
-extKeccak256(nil, 0, nil, 0)
-extKeccak512(nil, 0, nil, 0)
-
diff --git a/tests/misc/tinc.nim b/tests/misc/tinc.nim
deleted file mode 100644
index 7819775e3..000000000
--- a/tests/misc/tinc.nim
+++ /dev/null
@@ -1,12 +0,0 @@
-discard """
-  file: "tinc.nim"
-  line: 8
-  errormsg: "type mismatch: got <int>"
-"""
-var x = 0
-
-inc(x+1)
-
-
-
-
diff --git a/tests/misc/tinit.nim b/tests/misc/tinit.nim
index 02607909b..207cb17e8 100644
--- a/tests/misc/tinit.nim
+++ b/tests/misc/tinit.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tinit.nim"
   output: "Hello from module! Hello from main module!"
 """
 # Test the new init section in modules
@@ -8,5 +7,3 @@ import minit
 
 write(stdout, "Hello from main module!\n")
 #OUT Hello from module! Hello from main module!
-
-
diff --git a/tests/misc/tinout.nim b/tests/misc/tinout.nim
index 46af2f5de..bae0fb185 100644
--- a/tests/misc/tinout.nim
+++ b/tests/misc/tinout.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "type mismatch: got <int literal(3)>"
   file: "tinout.nim"
   line: 12
-  errormsg: "type mismatch: got <int literal(3)>"
 """
 # Test in out checking for parameters
 
@@ -12,5 +12,3 @@ proc b() =
     abc(3) #ERROR
 
 b()
-
-
diff --git a/tests/misc/tints.nim b/tests/misc/tints.nim
deleted file mode 100644
index d5374a543..000000000
--- a/tests/misc/tints.nim
+++ /dev/null
@@ -1,81 +0,0 @@
-discard """
-  output: '''
-0 0
-0 0
-Success'''
-"""
-# Test the different integer operations
-
-var testNumber = 0
-
-template test(opr, a, b, c: untyped): untyped =
-  # test the expression at compile and runtime
-  block:
-    const constExpr = opr(a, b)
-    when constExpr != c:
-      {.error: "Test failed " & $constExpr & " " & $c.}
-    inc(testNumber)
-    #Echo("Test: " & $testNumber)
-    var aa = a
-    var bb = b
-    var varExpr = opr(aa, bb)
-    assert(varExpr == c)
-
-test(`+`, 12'i8, -13'i16, -1'i16)
-test(`shl`, 0b11, 0b100, 0b110000)
-when not defined(js):
-  test(`shl`, 0b11'i32, 0b100'i64, 0b110000'i64)
-test(`shl`, 0b11'i32, 0b100'i32, 0b110000'i32)
-
-test(`or`, 0xf0f0'i16, 0x0d0d'i16, 0xfdfd'i16)
-test(`and`, 0xf0f0'i16, 0xfdfd'i16, 0xf0f0'i16)
-
-when not defined(js):
-  test(`shr`, 0xffffffffffffffff'i64, 0x4'i64, 0x0fffffffffffffff'i64)
-test(`shr`, 0xffff'i16, 0x4'i16, 0x0fff'i16)
-test(`shr`, 0xff'i8, 0x4'i8, 0x0f'i8)
-
-when not defined(js):
-  test(`shr`, 0xffffffff'i64, 0x4'i64, 0x0fffffff'i64)
-test(`shr`, 0xffffffff'i32, 0x4'i32, 0x0fffffff'i32)
-
-when not defined(js):
-  test(`shl`, 0xffffffffffffffff'i64, 0x4'i64, 0xfffffffffffffff0'i64)
-test(`shl`, 0xffff'i16, 0x4'i16, 0xfff0'i16)
-test(`shl`, 0xff'i8, 0x4'i8, 0xf0'i8)
-
-when not defined(js):
-  test(`shl`, 0xffffffff'i64, 0x4'i64, 0xffffffff0'i64)
-test(`shl`, 0xffffffff'i32, 0x4'i32, 0xfffffff0'i32)
-
-# bug #916
-proc unc(a: float): float =
-  return a
-
-echo int(unc(0.5)), " ", int(unc(-0.5))
-echo int(0.5), " ", int(-0.5)
-
-block: # Casts to uint
-  template testCast(fromValue: typed, toType: typed, expectedResult: typed) =
-    let src = fromValue
-    let dst = cast[toType](src)
-    if dst != expectedResult:
-      echo "Casting ", astToStr(fromValue), " to ", astToStr(toType), " = ", dst.int, " instead of ", astToStr(expectedResult)
-    doAssert(dst == expectedResult)
-
-  testCast(-1'i16, uint16, 0xffff'u16)
-  testCast(0xffff'u16, int16, -1'i16)
-
-  testCast(0xff'u16, uint8, 0xff'u8)
-  testCast(0xffff'u16, uint8, 0xff'u8)
-
-  testCast(-1'i16, uint32, 0xffffffff'u32)
-  testCast(0xffffffff'u32, int32, -1)
-
-  testCast(0xfffffffe'u32, int32, -2'i32)
-  testCast(0xffffff'u32, int16, -1'i32)
-
-  testCast(-5'i32, uint8, 251'u8)
-
-
-echo("Success") #OUT Success
diff --git a/tests/misc/tinvalidarrayaccess.nim b/tests/misc/tinvalidarrayaccess.nim
deleted file mode 100644
index 03105b41b..000000000
--- a/tests/misc/tinvalidarrayaccess.nim
+++ /dev/null
@@ -1,14 +0,0 @@
-discard """
-  errormsg: "index out of bounds"
-  line: 11
-"""
-
-
-type TTestArr = array[0..1, int16]
-var f: TTestArr
-f[0] = 30
-f[1] = 40
-f[2] = 50
-f[3] = 60
-
-echo(repr(f))
diff --git a/tests/misc/tinvalidnewseq.nim b/tests/misc/tinvalidnewseq.nim
index 66e9388ef..7a95db020 100644
--- a/tests/misc/tinvalidnewseq.nim
+++ b/tests/misc/tinvalidnewseq.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "type mismatch: got <array[0..6, string], int literal(7)>"
   file: "tinvalidnewseq.nim"
   line: 15
-  errormsg: "type mismatch: got <array[0..6, string], int literal(7)>"
 """
 import re, strutils
 
@@ -13,7 +13,7 @@ proc parseURL(url: string): TURL =
   var pattern: string = r"([a-zA-Z]+://)?(\w+?\.)?(\w+)(\.\w+)(:[0-9]+)?(/.+)?"
   var m: array[0..6, string] #Array with the matches
   newSeq(m, 7) #ERROR
-  discard regexprs.match(url, re(pattern), m)
+  discard re.match(url, re(pattern), m)
 
   result = (protocol: m[1], subdomain: m[2], domain: m[3] & m[4],
             port: m[5], path: m[6].split('/'))
@@ -22,6 +22,3 @@ var r: TUrl
 
 r = parseUrl(r"http://google.com/search?var=bleahdhsad")
 echo(r.domain)
-
-
-
diff --git a/tests/misc/tissue710.nim b/tests/misc/tissue710.nim
index 3b6d3e5f3..ec125b840 100644
--- a/tests/misc/tissue710.nim
+++ b/tests/misc/tissue710.nim
@@ -1,7 +1,7 @@
 discard """
+  errorMsg: "attempting to call routine: '||'"
   file: "tissue710.nim"
   line: 8
-  errorMsg: "attempting to call undeclared routine: '||'"
 """
 var sum = 0
 for x in 3..1000:
diff --git a/tests/misc/tjoinable.nim b/tests/misc/tjoinable.nim
new file mode 100644
index 000000000..f23fca0d4
--- /dev/null
+++ b/tests/misc/tjoinable.nim
@@ -0,0 +1,3 @@
+# checks that megatest allows duplicate names, see also `tests/testament/tjoinable.nim`
+doAssert defined(testing)
+doAssert defined(nimMegatest)
diff --git a/tests/misc/tlastmod.nim b/tests/misc/tlastmod.nim
index 538b5e656..1cc1d4bd9 100644
--- a/tests/misc/tlastmod.nim
+++ b/tests/misc/tlastmod.nim
@@ -1,18 +1,25 @@
+discard """
+outputsub: "is newer than"
+"""
 # test the new LastModificationTime() proc
 
+let
+  file1 = "tests/testdata/data.csv"
+  file2 = "tests/testdata/doc1.xml"
+
 import
   os, times, strutils
 
 proc main() =
   var
-    a, b: TTime
-  a = getLastModificationTime(paramStr(1))
-  b = getLastModificationTime(paramStr(2))
+    a, b: Time
+  a = getLastModificationTime(file1)
+  b = getLastModificationTime(file2)
   writeLine(stdout, $a)
   writeLine(stdout, $b)
   if a < b:
-    write(stdout, "$2 is newer than $1\n" % [paramStr(1), paramStr(2)])
+    write(stdout, "$2 is newer than $1\n" % [file1, file2])
   else:
-    write(stdout, "$1 is newer than $2\n" % [paramStr(1), paramStr(2)])
+    write(stdout, "$1 is newer than $2\n" % [file1, file2])
 
 main()
diff --git a/tests/misc/tlocals.nim b/tests/misc/tlocals.nim
deleted file mode 100644
index 09b7432f5..000000000
--- a/tests/misc/tlocals.nim
+++ /dev/null
@@ -1,11 +0,0 @@
-discard """
-  output: '''(x: "string here", a: 1)'''
-"""
-
-proc simple[T](a: T) =
-  var
-    x = "string here"
-  echo locals()
-
-simple(1)
-
diff --git a/tests/misc/tloops.nim b/tests/misc/tloops.nim
index b160500af..61e0baf10 100644
--- a/tests/misc/tloops.nim
+++ b/tests/misc/tloops.nim
@@ -1,3 +1,10 @@
+discard """
+output: '''
+Hello!(x: 1, y: 2, z: 3)
+(x: 1.0, y: 2.0)
+'''
+"""
+
 # Test nested loops and some other things
 
 proc andTest() =
@@ -84,4 +91,3 @@ proc main[T]() =
   echo myType2
 
 main[int]()
-
diff --git a/tests/misc/tmandelbrot.nim b/tests/misc/tmandelbrot.nim
deleted file mode 100644
index 504628313..000000000
--- a/tests/misc/tmandelbrot.nim
+++ /dev/null
@@ -1,57 +0,0 @@
-discard """
-  cmd: "nim $target --hints:on -d:release $options $file"
-"""
-
-# -*- nim -*-
-
-import math
-import os
-import strutils
-
-type TComplex = tuple[re, im: float]
-
-proc `+` (a, b: TComplex): TComplex =
-    return (a.re + b.re, a.im + b.im)
-
-proc `*` (a, b: TComplex): TComplex =
-    result.re = a.re * b.re - a.im * b.im
-    result.im = a.re * b.im + a.im * b.re
-
-proc abs2 (a: TComplex): float =
-    return a.re * a.re + a.im * a.im
-
-var size    = parseInt(paramStr(1))
-var bit     = 128
-var byteAcc = 0
-
-stdout.writeLine("P4")
-stdout.write($size)
-stdout.write(" ")
-stdout.writeLine($size)
-
-var fsize = float(size)
-for y in 0 .. size-1:
-    var fy = 2.0 * float(y) / fsize - 1.0
-    for x in 0 .. size-1:
-        var z = (0.0, 0.0)
-        var c = (float(2*x) / fsize - 1.5, fy)
-
-        block iter:
-            for i in 0 .. 49:
-                z = z*z + c
-                if abs2(z) >= 4.0:
-                    break iter
-            byteAcc = byteAcc + bit
-
-        if bit > 1:
-            bit = bit div 2
-        else:
-            stdout.write(chr(byteAcc))
-            bit     = 128
-            byteAcc = 0
-
-    if bit != 128:
-        stdout.write(chr(byteAcc))
-        bit     = 128
-        byteAcc = 0
-
diff --git a/tests/misc/tmemoization.nim b/tests/misc/tmemoization.nim
index 840eb3b0d..c65692608 100644
--- a/tests/misc/tmemoization.nim
+++ b/tests/misc/tmemoization.nim
@@ -1,5 +1,5 @@
 discard """
-  msg:    "test 1\ntest 2\ntest 3"
+  nimout:    "test 1\ntest 2\ntest 3"
   output: "TEST 1\nTEST 2\nTEST 3"
 """
 
diff --git a/tests/misc/tmodulea.nim b/tests/misc/tmodulea.nim
new file mode 100644
index 000000000..5aeb1824b
--- /dev/null
+++ b/tests/misc/tmodulea.nim
@@ -0,0 +1,3 @@
+from modulea import modulea
+
+#bug #6731
diff --git a/tests/misc/tnewuns.nim b/tests/misc/tnewuns.nim
deleted file mode 100644
index d6bae4fb1..000000000
--- a/tests/misc/tnewuns.nim
+++ /dev/null
@@ -1,12 +0,0 @@
-# test the new unsigned operations:
-
-import
-  strutils
-
-var
-  x, y: int
-
-x = 1
-y = high(int)
-
-writeLine(stdout, $ ( x +% y ) )
diff --git a/tests/misc/tnoforward.nim b/tests/misc/tnoforward.nim
index 342e757b8..b6a71897a 100644
--- a/tests/misc/tnoforward.nim
+++ b/tests/misc/tnoforward.nim
@@ -1,8 +1,9 @@
 discard """
-  disabled: true
+  output: "10"
 """
 
-{. noforward: on .}
+# {. noforward: on .}
+{.experimental: "codeReordering".}
 
 proc foo(x: int) =
   bar x
diff --git a/tests/misc/tnoinst.nim b/tests/misc/tnoinst.nim
deleted file mode 100644
index 25ebe8dfc..000000000
--- a/tests/misc/tnoinst.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-discard """
-  line: 12
-  errormsg: "instantiate 'notConcrete' explicitly"
-  disabled: "true"
-"""
-
-proc wrap[T]() =
-  proc notConcrete[T](x, y: int): int =
-    var dummy: T
-    result = x - y
-
-  var x: proc (x, y: T): int
-  x = notConcrete
-
-
-wrap[int]()
-
diff --git a/tests/misc/tnolen.nim b/tests/misc/tnolen.nim
index 2831e5048..e0e8025d4 100644
--- a/tests/misc/tnolen.nim
+++ b/tests/misc/tnolen.nim
@@ -1,9 +1,8 @@
 discard """
-  line: 8
   errormsg: "type mismatch: got <int literal(3)>"
+  line: 8
 """
 
 # please finally disallow Len(3)
 
 echo len(3)
-
diff --git a/tests/misc/tnoop.nim b/tests/misc/tnoop.nim
deleted file mode 100644
index 1e3fbe6cf..000000000
--- a/tests/misc/tnoop.nim
+++ /dev/null
@@ -1,11 +0,0 @@
-discard """
-  file: "tnoop.nim"
-  line: 11
-  errormsg: "attempting to call undeclared routine: 'a'"
-"""
-
-
-var
-  a: int
-
-a()
diff --git a/tests/misc/tnot.nim b/tests/misc/tnot.nim
deleted file mode 100644
index 5c268981e..000000000
--- a/tests/misc/tnot.nim
+++ /dev/null
@@ -1,19 +0,0 @@
-discard """
-  file: "tnot.nim"
-  line: 14
-  errormsg: "type mismatch"
-"""
-# BUG: following compiles, but should not:
-
-proc nodeOfDegree(x: int): bool =
-  result = false
-
-proc main =
-  for j in 0..2:
-    for i in 0..10:
-      if not nodeOfDegree(1) >= 0: #ERROR_MSG type mismatch
-        echo "Yes"
-      else:
-        echo "No"
-
-main()
diff --git a/tests/misc/tparseopt.nim b/tests/misc/tparseopt.nim
index badbc59ad..47be05bac 100644
--- a/tests/misc/tparseopt.nim
+++ b/tests/misc/tparseopt.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tparseopt.nim"
   output: '''
 parseopt
 first round
@@ -9,6 +8,8 @@ kind: cmdLongOption	key:val  --  left:
 kind: cmdLongOption	key:val  --  debug:3
 kind: cmdShortOption	key:val  --  l:4
 kind: cmdShortOption	key:val  --  r:2
+cmdLongOption foo
+cmdLongOption path
 parseoptNoVal
 kind: cmdLongOption	key:val  --  left:
 kind: cmdLongOption	key:val  --  debug:3
@@ -20,20 +21,32 @@ kind: cmdShortOption	key:val  --  r:1
 kind: cmdShortOption	key:val  --  r:0
 kind: cmdShortOption	key:val  --  l:
 kind: cmdShortOption	key:val  --  r:4
-parseopt2
-first round
-kind: cmdLongOption	key:val  --  left:
-second round
-kind: cmdLongOption	key:val  --  left:
-kind: cmdLongOption	key:val  --  debug:3
-kind: cmdShortOption	key:val  --  l:4
-kind: cmdShortOption	key:val  --  r:2'''
+kind: cmdLongOption	key:val  --  debug:
+cmdShortOption key: v value: ''
+cmdArgument key: ABC value: ''
+cmdShortOption key: v value: 'ABC'
+cmdShortOption key: v value: ''
+cmdArgument key: ABC value: ''
+cmdShortOption key: v value: ''
+cmdArgument key: ABC value: ''
+cmdShortOption key: j value: '4'
+cmdArgument key: ok value: ''
+'''
+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
 
-block:
+  block:
     echo "parseopt"
     for kind, key, val in parseopt.getopt():
       echo "kind: ", kind, "\tkey:val  --  ", key, ":", val
@@ -50,28 +63,94 @@ block:
     for kind, key, val in parseopt.getopt(p):
       echo "kind: ", kind, "\tkey:val  --  ", key, ":", val
 
-block:
+    # 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 argv = "--left --debug:3 -l -r:2 --debug 2 --debug=1 -r1 -r=0 -lr4 --debug:"
     var p = parseopt.initOptParser(argv,
-                                   shortNoVal = {'l'}, longNoVal = @["left"])
+                                    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
+  import osproc, os, strutils
+  from stdtest/specialpaths import buildDir
+  import stdtest/unittest_light
 
-    # 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
+  block: # fix #9951
+    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)
+
+  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}"""
+
+
+
+  block:
+    let args = @["-v", "ABC"]
+    var p = parseopt.initOptParser(args, shortnoVal = {'n'}, longnoVal = @["novalue"])
+    for kind, key, val in parseopt.getopt(p):
+      echo kind," key: ", key, " value: '", val, "'"
+
+    var r = parseopt.initOptParser(@["-v ABC"], shortnoVal = {'n'}, longnoVal = @["novalue"])
+    for kind, key, val in parseopt.getopt(r):
+      echo kind," key: ", key, " value: '", val, "'"
+
+    var s = parseopt.initOptParser("-v ABC", shortnoVal = {'v'}, longnoVal = @["novalue"])
+    for kind, key, val in parseopt.getopt(s):
+      echo kind," key: ", key, " value: '", val, "'"
+
+    var m = parseopt.initOptParser("-v ABC", shortnoVal = {'n'}, longnoVal = @["novalue"])
+    for kind, key, val in parseopt.getopt(m):
+      echo kind," key: ", key, " value: '", val, "'"
+
+    var n = parseopt.initOptParser("-j4 ok", shortnoVal = {'n'}, longnoVal = @["novalue"])
+    for kind, key, val in parseopt.getopt(n):
+      echo kind," key: ", key, " value: '", val, "'"
diff --git a/tests/misc/tpos.nim b/tests/misc/tpos.nim
index bedb62e62..f7607d643 100644
--- a/tests/misc/tpos.nim
+++ b/tests/misc/tpos.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tpos.nim"
   output: "6"
 """
 # test this particular function
@@ -30,6 +29,5 @@ proc mypos(sub, s: string, start: int = 0): int =
 var sub = "hello"
 var s = "world hello"
 write(stdout, mypos(sub, s))
+write stdout, "\n"
 #OUT 6
-
-
diff --git a/tests/misc/tprep.nim b/tests/misc/tprep.nim
index 8f40300d6..45f25b790 100644
--- a/tests/misc/tprep.nim
+++ b/tests/misc/tprep.nim
@@ -1,3 +1,11 @@
+discard """
+nimout: '''
+tprep.nim(25, 9) Hint: Case 2 [User]
+tprep.nim(27, 11) Hint: Case 2.3 [User]
+'''
+outputsub: ""
+"""
+
 # Test the features that used to belong to the preprocessor
 
 import
diff --git a/tests/misc/tquicksort.nim b/tests/misc/tquicksort.nim
index 0867a3769..017c73fbc 100644
--- a/tests/misc/tquicksort.nim
+++ b/tests/misc/tquicksort.nim
@@ -17,10 +17,7 @@ proc echoSeq(a: seq[int]) =
     for i in low(a)..high(a):
         echo(a[i])
 
-var
-    list: seq[int]
-
-list = QuickSort(@[89,23,15,23,56,123,356,12,7,1,6,2,9,4,3])
-echoSeq(list)
-
+let list = QuickSort(@[89,23,15,23,56,123,356,12,7,1,6,2,9,4,3])
+let expected = @[1, 2, 3, 4, 6, 7, 9, 12, 15, 23, 56, 89, 123, 356]
 
+doAssert list == expected
diff --git a/tests/misc/tradix.nim b/tests/misc/tradix.nim
index 07674af18..f4fb56849 100644
--- a/tests/misc/tradix.nim
+++ b/tests/misc/tradix.nim
@@ -1,9 +1,37 @@
+discard """
+output: '''
+start tradix.nim
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+128
+1
+2
+3
+4
+255
+17
+45
+19000
+4294967288
+'''
+"""
+
 # implements and tests an efficient radix tree
 
 ## another method to store an efficient array of pointers:
 ## We use a radix tree with node compression.
 ## There are two node kinds:
 
+echo "start tradix.nim"
+
 const BitsPerUnit = 8*sizeof(int)
 
 type
@@ -12,8 +40,8 @@ type
   TRadixNode {.pure, inheritable.} = object
     kind: TRadixNodeKind
   TRadixNodeLinear = object of TRadixNode
-    len: int8
-    keys: array[0..31, int8]
+    len: uint8
+    keys: array[0..31, uint8]
     vals: array[0..31, PRadixNode]
 
   TRadixNodeFull = object of TRadixNode
@@ -21,8 +49,8 @@ type
   TRadixNodeLeafBits = object of TRadixNode
     b: array[0..7, int]
   TRadixNodeLeafLinear = object of TRadixNode
-    len: int8
-    keys: array[0..31, int8]
+    len: uint8
+    keys: array[0..31, uint8]
 
 var
   root: PRadixNode
@@ -31,8 +59,8 @@ proc searchInner(r: PRadixNode, a: int): PRadixNode =
   case r.kind
   of rnLinear:
     var x = cast[ptr TRadixNodeLinear](r)
-    for i in 0..ze(x.len)-1:
-      if ze(x.keys[i]) == a: return x.vals[i]
+    for i in 0..int(x.len)-1:
+      if int(x.keys[i]) == a: return x.vals[i]
   of rnFull:
     var x = cast[ptr TRadixNodeFull](r)
     return x.b[a]
@@ -59,8 +87,8 @@ proc searchLeaf(r: PRadixNode, a: int): bool =
     return testBit(x.b[a /% BitsPerUnit], a)
   of rnLeafLinear:
     var x = cast[ptr TRadixNodeLeafLinear](r)
-    for i in 0..ze(x.len)-1:
-      if ze(x.keys[i]) == a: return true
+    for i in 0..int(x.len)-1:
+      if int(x.keys[i]) == a: return true
   else: assert(false)
 
 proc exclLeaf(r: PRadixNode, a: int) =
@@ -70,9 +98,9 @@ proc exclLeaf(r: PRadixNode, a: int) =
     resetBit(x.b[a /% BitsPerUnit], a)
   of rnLeafLinear:
     var x = cast[ptr TRadixNodeLeafLinear](r)
-    var L = ze(x.len)
+    var L = int(x.len)
     for i in 0..L-1:
-      if ze(x.keys[i]) == a:
+      if int(x.keys[i]) == a:
         x.keys[i] = x.keys[L-1]
         dec(x.len)
         return
@@ -101,10 +129,10 @@ proc excl*(r: PRadixNode, a: ByteAddress): bool =
 proc addLeaf(r: var PRadixNode, a: int): bool =
   if r == nil:
     # a linear node:
-    var x = cast[ptr TRadixNodeLinear](alloc(sizeof(TRadixNodeLinear)))
+    var x = cast[ptr TRadixNodeLinear](alloc0(sizeof(TRadixNodeLinear)))
     x.kind = rnLeafLinear
-    x.len = 1'i8
-    x.keys[0] = toU8(a)
+    x.len = 1'u8
+    x.keys[0] = uint8(a)
     r = x
     return false # not already in set
   case r.kind
@@ -113,18 +141,18 @@ proc addLeaf(r: var PRadixNode, a: int): bool =
     return testOrSetBit(x.b[a /% BitsPerUnit], a)
   of rnLeafLinear:
     var x = cast[ptr TRadixNodeLeafLinear](r)
-    var L = ze(x.len)
+    var L = int(x.len)
     for i in 0..L-1:
-      if ze(x.keys[i]) == a: return true
+      if int(x.keys[i]) == a: return true
     if L <= high(x.keys):
-      x.keys[L] = toU8(a)
+      x.keys[L] = uint8(a)
       inc(x.len)
     else:
       # transform into a full node:
       var y = cast[ptr TRadixNodeLeafBits](alloc0(sizeof(TRadixNodeLeafBits)))
       y.kind = rnLeafBits
-      for i in 0..ze(x.len)-1:
-        var u = ze(x.keys[i])
+      for i in 0..int(x.len)-1:
+        var u = int(x.keys[i])
         setBit(y.b[u /% BitsPerUnit], u)
       setBit(y.b[a /% BitsPerUnit], a)
       dealloc(r)
@@ -137,28 +165,28 @@ proc addInner(r: var PRadixNode, a: int, d: int): bool =
   var k = a shr d and 0xff
   if r == nil:
     # a linear node:
-    var x = cast[ptr TRadixNodeLinear](alloc(sizeof(TRadixNodeLinear)))
+    var x = cast[ptr TRadixNodeLinear](alloc0(sizeof(TRadixNodeLinear)))
     x.kind = rnLinear
-    x.len = 1'i8
-    x.keys[0] = toU8(k)
+    x.len = 1'u8
+    x.keys[0] = uint8(k)
     r = x
     return addInner(x.vals[0], a, d-8)
   case r.kind
   of rnLinear:
     var x = cast[ptr TRadixNodeLinear](r)
-    var L = ze(x.len)
+    var L = int(x.len)
     for i in 0..L-1:
-      if ze(x.keys[i]) == k: # already exists
+      if int(x.keys[i]) == k: # already exists
         return addInner(x.vals[i], a, d-8)
     if L <= high(x.keys):
-      x.keys[L] = toU8(k)
+      x.keys[L] = uint8(k)
       inc(x.len)
       return addInner(x.vals[L], a, d-8)
     else:
       # transform into a full node:
       var y = cast[ptr TRadixNodeFull](alloc0(sizeof(TRadixNodeFull)))
       y.kind = rnFull
-      for i in 0..L-1: y.b[ze(x.keys[i])] = x.vals[i]
+      for i in 0..L-1: y.b[int(x.keys[i])] = x.vals[i]
       dealloc(r)
       r = y
       return addInner(y.b[k], a, d-8)
@@ -183,8 +211,8 @@ iterator innerElements(r: PRadixNode): tuple[prefix: int, n: PRadixNode] =
           yield (i, r.b[i])
     of rnLinear:
       var r = cast[ptr TRadixNodeLinear](r)
-      for i in 0..ze(r.len)-1:
-        yield (ze(r.keys[i]), r.vals[i])
+      for i in 0..int(r.len)-1:
+        yield (int(r.keys[i]), r.vals[i])
     else: assert(false)
 
 iterator leafElements(r: PRadixNode): int =
@@ -200,8 +228,8 @@ iterator leafElements(r: PRadixNode): int =
               yield i*BitsPerUnit+j
     of rnLeafLinear:
       var r = cast[ptr TRadixNodeLeafLinear](r)
-      for i in 0..ze(r.len)-1:
-        yield ze(r.keys[i])
+      for i in 0..int(r.len)-1:
+        yield int(r.keys[i])
     else: assert(false)
 
 iterator elements*(r: PRadixNode): ByteAddress {.inline.} =
@@ -218,102 +246,9 @@ proc main() =
     r: PRadixNode = nil
   for x in items(numbers):
     echo testOrIncl(r, x)
-  for x in elements(r): echo(x)
+  for x in elements(r):
+    # ByteAddress being defined as a signed integer cases trouble
+    # exactly here
+    echo(cast[uint](x))
 
 main()
-
-
-when false:
-  proc traverse(r: PRadixNode, prefix: int, d: int) =
-    if r == nil: return
-    case r.kind
-    of rnLeafBits:
-      assert(d == 0)
-      var x = cast[ptr TRadixNodeLeafBits](r)
-      # iterate over any bit:
-      for i in 0..high(x.b):
-        if x.b[i] != 0: # test all bits for zero
-          for j in 0..BitsPerUnit-1:
-            if testBit(x.b[i], j):
-              visit(prefix or i*BitsPerUnit+j)
-    of rnLeafLinear:
-      assert(d == 0)
-      var x = cast[ptr TRadixNodeLeafLinear](r)
-      for i in 0..ze(x.len)-1:
-        visit(prefix or ze(x.keys[i]))
-    of rnFull:
-      var x = cast[ptr TRadixNodeFull](r)
-      for i in 0..high(r.b):
-        if r.b[i] != nil:
-          traverse(r.b[i], prefix or (i shl d), d-8)
-    of rnLinear:
-      var x = cast[ptr TRadixNodeLinear](r)
-      for i in 0..ze(x.len)-1:
-        traverse(x.vals[i], prefix or (ze(x.keys[i]) shl d), d-8)
-
-  type
-    TRadixIter {.final.} = object
-      r: PRadixNode
-      p: int
-      x: int
-
-  proc init(i: var TRadixIter, r: PRadixNode) =
-    i.r = r
-    i.x = 0
-    i.p = 0
-
-  proc nextr(i: var TRadixIter): PRadixNode =
-    if i.r == nil: return nil
-    case i.r.kind
-    of rnFull:
-      var r = cast[ptr TRadixNodeFull](i.r)
-      while i.x <= high(r.b):
-        if r.b[i.x] != nil:
-          i.p = i.x
-          return r.b[i.x]
-        inc(i.x)
-    of rnLinear:
-      var r = cast[ptr TRadixNodeLinear](i.r)
-      if i.x < ze(r.len):
-        i.p = ze(r.keys[i.x])
-        result = r.vals[i.x]
-        inc(i.x)
-    else: assert(false)
-
-  proc nexti(i: var TRadixIter): int =
-    result = -1
-    case i.r.kind
-    of rnLeafBits:
-      var r = cast[ptr TRadixNodeLeafBits](i.r)
-      # iterate over any bit:
-      for i in 0..high(r.b):
-        if x.b[i] != 0: # test all bits for zero
-          for j in 0..BitsPerUnit-1:
-            if testBit(x.b[i], j):
-              visit(prefix or i*BitsPerUnit+j)
-    of rnLeafLinear:
-      var r = cast[ptr TRadixNodeLeafLinear](i.r)
-      if i.x < ze(r.len):
-        result = ze(r.keys[i.x])
-        inc(i.x)
-
-  iterator elements(r: PRadixNode): ByteAddress {.inline.} =
-    var
-      a, b, c, d: TRadixIter
-    init(a, r)
-    while true:
-      var x = nextr(a)
-      if x != nil:
-        init(b, x)
-        while true:
-          var y = nextr(b)
-          if y != nil:
-            init(c, y)
-            while true:
-              var z = nextr(c)
-              if z != nil:
-                init(d, z)
-                while true:
-                  var q = nexti(d)
-                  if q != -1:
-                    yield a.p shl 24 or b.p shl 16 or c.p shl 8 or q
diff --git a/tests/misc/trangechecks.nim b/tests/misc/trangechecks.nim
deleted file mode 100644
index 2c6f0f66d..000000000
--- a/tests/misc/trangechecks.nim
+++ /dev/null
@@ -1,43 +0,0 @@
-discard """
-  output: '''10
-10
-1
-1
-true'''
-"""
-
-# bug #1344
-
-var expected: int
-var x: range[1..10] = 10
-
-try:
-  x += 1
-  echo x
-except OverflowError, RangeError:
-  expected += 1
-  echo x
-
-try:
-  inc x
-  echo x
-except OverflowError, RangeError:
-  expected += 1
-  echo x
-
-x = 1
-try:
-  x -= 1
-  echo x
-except OverflowError, RangeError:
-  expected += 1
-  echo x
-
-try:
-  dec x
-  echo x
-except OverflowError, RangeError:
-  expected += 1
-  echo x
-
-echo expected == 4
diff --git a/tests/misc/treadln.nim b/tests/misc/treadln.nim
deleted file mode 100644
index 1edbea992..000000000
--- a/tests/misc/treadln.nim
+++ /dev/null
@@ -1,12 +0,0 @@
-# test the improved readline handling that does not care whether its
-# Macintosh, Unix or Windows text format.
-
-var
-  inp: File
-  line: string
-
-if open(inp, "readme.txt"):
-  while not endOfFile(inp):
-    line = readLine(inp)
-    echo("#" & line & "#")
-  close(inp)
diff --git a/tests/misc/treadx.nim b/tests/misc/treadx.nim
deleted file mode 100644
index e68b8933d..000000000
--- a/tests/misc/treadx.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-
-when not defined(windows):
-  import posix
-
-  var inp = ""
-  var buf: array[0..10, char]
-  while true:
-    var r = read(0, addr(buf), sizeof(buf)-1)
-    add inp, $cstring(addr buf)
-    if r != sizeof(buf)-1: break
-
-  echo inp
-  #dafkladskölklödsaf ölksdakölfölksfklwe4iojr389wr 89uweokf sdlkf jweklr jweflksdj fioewjfsdlfsd
diff --git a/tests/misc/trfc405.nim b/tests/misc/trfc405.nim
new file mode 100644
index 000000000..0828879ee
--- /dev/null
+++ b/tests/misc/trfc405.nim
@@ -0,0 +1,112 @@
+
+{.experimental: "flexibleOptionalParams".}
+
+# https://github.com/nim-lang/RFCs/issues/405
+
+template main =
+  template fn1(a = 1, b = 2, body): auto = (a, b, astToStr(body))
+  let a1 = fn1(10, 20):
+    foo
+  doAssert a1 == (10, 20, "\nfoo")
+
+  template fn2(a = 1, b = 2, body): auto = (a, b, astToStr(body))
+  let a2 = fn2(a = 10): foo
+  doAssert a2 == (10, 2, "\nfoo")
+  let a2b = fn2(b = 20): foo
+  doAssert a2b == (1, 20, "\nfoo")
+
+  template fn3(x: int, a = 1, b = 2, body): auto = (a, b, astToStr(body))
+  let a3 = fn3(3, 10, 20): foo
+  doAssert a3 == (10, 20, "\nfoo")
+  let a3b = fn3(3, a = 10): foo
+  doAssert a3b == (10, 2, "\nfoo")
+
+  template fn4(x: int, y: int, body): auto = (x, y, astToStr(body))
+  let a4 = fn4(1, 2): foo
+  doAssert a4 == (1, 2, "\nfoo")
+
+  template fn5(x = 1, y = 2, body: untyped = 3): auto = (x, y, astToStr(body))
+  doAssert compiles(fn5(1, 2, foo))
+  doAssert not compiles(fn5(1, foo))
+
+  block:
+    # with an overload
+    var witness = 0
+    template fn6() = discard
+    template fn6(procname: string, body: untyped): untyped = witness.inc
+    fn6("abc"): discard
+    assert witness == 1
+
+  block:
+    # with overloads
+    var witness = 0
+    template fn6() = discard
+    template fn6(a: int) = discard
+    template fn6(procname: string, body: untyped): untyped = witness.inc
+    fn6("abc"): discard
+    assert witness == 1
+
+    template fn6(b = 1.5, body: untyped): untyped = witness.inc
+    fn6(1.3): discard
+    assert witness == 2
+
+  block:
+    var witness = 0
+    template fn6(a: int) = discard
+    template fn6(a: string) = discard
+    template fn6(ignore: string, b = 1.5, body: untyped): untyped = witness.inc
+    fn6(""):
+      foobar1
+      foobar2
+    doAssert witness == 1
+    fn6(""): discard
+    doAssert witness == 2
+
+  block: # multi block args
+    template fn8(a = 1, b = 2, body1: untyped, body2: untyped): auto = (a, b, astToStr(body1), astToStr(body2))
+    let a1 = fn8():
+      foobar1
+      foobar2
+    do:
+      foobar3
+      foobar4
+    doAssert a1 == (1, 2, "\nfoobar1\nfoobar2", "\nfoobar3\nfoobar4")
+
+    let a2 = fn8(b = 20):
+      foobar1
+      foobar2
+    do:
+      foobar3
+      foobar4
+    doAssert a2 == (1, 20, "\nfoobar1\nfoobar2", "\nfoobar3\nfoobar4")
+  
+  block: # issue #19015
+    template hi(a: untyped, b: varargs[untyped]): untyped =
+      a
+
+    var worked = false
+    hi:
+      worked = true
+    doAssert worked
+    worked = false
+    hi(doAssert(not worked)):
+      doesntCompile
+    hi(doAssert(not worked), doesntCompile, againDoesntCompile):
+      definitelyDoesntCompile
+
+    template hi2(a: bool, b: untyped, c: varargs[untyped]): untyped =
+      b
+      doAssert a
+
+    hi2 worked:
+      worked = true
+    doAssert worked
+    hi2 worked, doAssert(worked):
+      doesntCompile
+    hi2 worked, doAssert(worked), doesntCompile, againDoesntCompile:
+      definitelyDoesntCompile
+    hi2 worked, doAssert(worked), againDoesntCompile:
+      definitelyDoesntCompile
+
+static: main()
+main()
diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim
new file mode 100644
index 000000000..6e5487d1b
--- /dev/null
+++ b/tests/misc/trunner.nim
@@ -0,0 +1,444 @@
+discard """
+  targets: "c cpp"
+  joinable: false
+"""
+
+## tests that don't quite fit the mold and are easier to handle via `execCmdEx`
+## A few others could be added to here to simplify code.
+## Note: this test is a bit slow but tests a lot of things; please don't disable.
+## Note: if needed, we could use `matrix: "-d:case1; -d:case2"` to split this
+## into several independent tests while retaining the common test helpers.
+
+import std/[strformat,os,osproc,unittest,compilesettings]
+from std/sequtils import toSeq,mapIt
+from std/algorithm import sorted
+import stdtest/[specialpaths, unittest_light]
+from std/private/globs import nativeToUnixPath
+from strutils import startsWith, strip, removePrefix
+from std/sugar import dup
+import "$lib/../compiler/nimpaths"
+
+proc isDots(a: string): bool =
+  ## test for `hintProcessing` dots
+  a.startsWith(".") and a.strip(chars = {'.'}) == ""
+
+const
+  nim = getCurrentCompilerExe()
+  mode = querySetting(backend)
+  nimcache = buildDir / "nimcacheTrunner"
+    # instead of `querySetting(nimcacheDir)`, avoids stomping on other parallel tests
+
+proc runNimCmd(file, options = "", rtarg = ""): auto =
+  let fileabs = testsDir / file.unixToNativePath
+  # doAssert fileabs.fileExists, fileabs # disabled because this allows passing `nim r --eval:code fakefile`
+  let cmd = fmt"{nim} {mode} --hint:all:off {options} {fileabs} {rtarg}"
+  result = execCmdEx(cmd)
+  when false: # for debugging
+    echo cmd
+    echo result[0] & "\n" & $result[1]
+
+proc runNimCmdChk(file, options = "", rtarg = "", status = 0): string =
+  let (ret, status2) = runNimCmd(file, options, rtarg = rtarg)
+  doAssert status2 == status, $(file, options, status, status2) & "\n" & ret
+  ret
+
+proc genShellCmd(filename: string): string =
+  let filename = filename.quoteShell
+  when defined(windows): "cmd /c " & filename # or "cmd /c " ?
+  else: "sh " & filename
+
+when defined(nimTrunnerFfi):
+  block: # mevalffi
+    when defined(openbsd):
+      #[
+      openbsd defines `#define stderr (&__sF[2])` which makes it cumbersome
+      for dlopen'ing inside `importcSymbol`. Instead of adding special rules
+      inside `importcSymbol` to handle this, we disable just the part that's
+      not working and will provide a more general, clean fix in future PR.
+      ]#
+      var opt = "-d:nimEvalffiStderrWorkaround"
+      let prefix = ""
+    else:
+      var opt = ""
+      let prefix = """
+hello world stderr
+hi stderr
+"""
+    let output = runNimCmdChk("vm/mevalffi.nim", fmt"{opt} --warnings:off --experimental:compiletimeFFI")
+    doAssert output == fmt"""
+{prefix}foo
+foo:100
+foo:101
+foo:102:103
+foo:102:103:104
+foo:0.03:asdf:103:105
+ret=[s1:foobar s2:foobar age:25 pi:3.14]
+""", output
+
+elif not defined(nimTestsTrunnerDebugging):
+  # don't run twice the same test with `nimTrunnerFfi`
+  # use `-d:nimTestsTrunnerDebugging` for debugging convenience when you want to just run 1 test
+  import std/strutils
+  import std/json
+  template check2(msg) = doAssert msg in output, output
+
+  block: # tests with various options `nim doc --project --index --docroot`
+    # regression tests for issues and PRS: #14376 #13223 #6583 ##13647
+    let file = testsDir / "nimdoc/sub/mmain.nim"
+    let mainFname = "mmain.html"
+    let htmldocsDirCustom = nimcache / "htmldocsCustom"
+    let docroot = testsDir / "nimdoc"
+    let options = [
+      0: "--project",
+      1: "--project --docroot",
+      2: "",
+      3: fmt"--outDir:{htmldocsDirCustom}",
+      4: fmt"--docroot:{docroot}",
+      5: "--project --useNimcache",
+      6: "--index:off",
+    ]
+
+    for i in 0..<options.len:
+      let htmldocsDir = case i
+      of 3: htmldocsDirCustom
+      of 5: nimcache / htmldocsDirname
+      else: file.parentDir / htmldocsDirname
+
+      var cmd = fmt"{nim} doc --index:on --filenames:abs --hint:successX:on --nimcache:{nimcache} {options[i]} {file}"
+      removeDir(htmldocsDir)
+      let (outp, exitCode) = execCmdEx(cmd)
+      check exitCode == 0
+      let ret = toSeq(walkDirRec(htmldocsDir, relative=true)).mapIt(it.nativeToUnixPath).sorted.join("\n")
+      let context = $(i, ret, cmd)
+      case i
+      of 0,5:
+        let htmlFile = htmldocsDir/mainFname
+        check htmlFile in outp # sanity check for `hintSuccessX`
+        assertEquals ret, fmt"""
+{dotdotMangle}/imp.html
+{dotdotMangle}/imp.idx
+{docHackJsFname}
+imp.html
+imp.idx
+imp2.html
+imp2.idx
+{mainFname}
+mmain.idx
+{nimdocOutCss}
+{theindexFname}""", context
+      of 1: assertEquals ret, fmt"""
+{docHackJsFname}
+{nimdocOutCss}
+tests/nimdoc/imp.html
+tests/nimdoc/imp.idx
+tests/nimdoc/sub/imp.html
+tests/nimdoc/sub/imp.idx
+tests/nimdoc/sub/imp2.html
+tests/nimdoc/sub/imp2.idx
+tests/nimdoc/sub/{mainFname}
+tests/nimdoc/sub/mmain.idx
+{theindexFname}"""
+      of 2, 3: assertEquals ret, fmt"""
+{docHackJsFname}
+{mainFname}
+mmain.idx
+{nimdocOutCss}""", context
+      of 4: assertEquals ret, fmt"""
+{docHackJsFname}
+{nimdocOutCss}
+sub/{mainFname}
+sub/mmain.idx""", context
+      of 6: assertEquals ret, fmt"""
+{mainFname}
+{nimdocOutCss}""", context
+      else: doAssert false
+
+  block: # mstatic_assert
+    let (output, exitCode) = runNimCmd("ccgbugs/mstatic_assert.nim", "-d:caseBad")
+    check2 "sizeof(bool) == 2"
+    check exitCode != 0
+
+  block: # ABI checks
+    let file = "misc/msizeof5.nim"
+    block:
+      discard runNimCmdChk(file, "-d:checkAbi")
+    block:
+      let (output, exitCode) = runNimCmd(file, "-d:checkAbi -d:caseBad")
+      # on platforms that support _StaticAssert natively, errors will show full context, e.g.:
+      # error: static_assert failed due to requirement 'sizeof(unsigned char) == 8'
+      # "backend & Nim disagree on size for: BadImportcType{int64} [declared in mabi_check.nim(1, 6)]"
+      check2 "sizeof(unsigned char) == 8"
+      check2 "sizeof(struct Foo2) == 1"
+      check2 "sizeof(Foo5) == 16"
+      check2 "sizeof(Foo5) == 3"
+      check2 "sizeof(struct Foo6) == "
+      check exitCode != 0
+
+  import streams
+  block: # stdin input
+    let nimcmd = fmt"""{nim} r --hints:off - -firstparam "-second param" """
+    let expected = """@["-firstparam", "-second param"]"""
+    block:
+      let p = startProcess(nimcmd, options = {poEvalCommand})
+      p.inputStream.write("import os; echo commandLineParams()")
+      p.inputStream.close
+      var output = p.outputStream.readAll
+      let error = p.errorStream.readAll
+      doAssert p.waitForExit == 0
+      doAssert error.len == 0, $error
+      output.stripLineEnd
+      check output == expected
+      p.errorStream.close
+      p.outputStream.close
+
+    block:
+      when defined posix:
+        # xxx on windows, `poEvalCommand` should imply `/cmd`, (which should
+        # make this work), but currently doesn't
+        let cmd = fmt"""echo "import os; echo commandLineParams()" | {nimcmd}"""
+        var (output, exitCode) = execCmdEx(cmd)
+        output.stripLineEnd
+        check output == expected
+        doAssert exitCode == 0
+
+  block: # nim doc --backend:$backend --doccmd:$cmd
+    # test for https://github.com/nim-lang/Nim/issues/13129
+    # test for https://github.com/nim-lang/Nim/issues/13891
+    let file = testsDir / "nimdoc/m13129.nim"
+    for backend in fmt"{mode} js".split:
+      # pending #14343 this fails on windows: --doccmd:"-d:m13129Foo2 --hints:off"
+      let cmd = fmt"""{nim} doc -b:{backend} --nimcache:{nimcache} -d:m13129Foo1 "--doccmd:-d:m13129Foo2 --hints:off" --usenimcache --hints:off {file}"""
+      check execCmdEx(cmd) == (&"ok1:{backend}\nok2: backend: {backend}\n", 0)
+    # checks that --usenimcache works with `nim doc`
+    check fileExists(nimcache / "htmldocs/m13129.html")
+
+    block: # mak sure --backend works with `nim r`
+      let cmd = fmt"{nim} r --backend:{mode} --hints:off --nimcache:{nimcache} {file}"
+      check execCmdEx(cmd) == ("ok3\n", 0)
+
+  block: # nim jsondoc # bug #20132
+    let file = testsDir / "misc/mjsondoc.nim"
+    let output = "nimcache_tjsondoc.json"
+    defer: removeFile(output)
+    let (msg, exitCode) = execCmdEx(fmt"{nim} jsondoc -o:{output} {file}")
+    doAssert exitCode == 0, msg
+
+    let data = parseJson(readFile(output))["entries"]
+    doAssert data.len == 5
+    let doSomething = data[0]
+    doAssert doSomething["name"].getStr == "doSomething"
+    doAssert doSomething["type"].getStr == "skProc"
+    doAssert doSomething["line"].getInt == 1
+    doAssert doSomething["col"].getInt == 0
+    doAssert doSomething["code"].getStr == "proc doSomething(x, y: int): int {.raises: [], tags: [], forbids: [].}"
+    let foo2 = data[4]
+    doAssert $foo2["signature"] == """{"arguments":[{"name":"x","type":"T"},{"name":"y","type":"U"},{"name":"z","type":"M"}],"genericParams":[{"name":"T","types":"int"},{"name":"M","types":"string"},{"name":"U"}]}"""
+
+  block: # nim jsondoc # bug #11953
+    let file = testsDir / "misc/mjsondoc.nim"
+    let destDir = testsDir / "misc/htmldocs"
+    removeDir(destDir)
+    defer: removeDir(destDir)
+    let (msg, exitCode) = execCmdEx(fmt"{nim} jsondoc {file}")
+    doAssert exitCode == 0, msg
+
+    let data = parseJson(readFile(destDir / "mjsondoc.json"))["entries"]
+    doAssert data.len == 5
+    let doSomething = data[0]
+    doAssert doSomething["name"].getStr == "doSomething"
+    doAssert doSomething["type"].getStr == "skProc"
+    doAssert doSomething["line"].getInt == 1
+    doAssert doSomething["col"].getInt == 0
+    doAssert doSomething["code"].getStr == "proc doSomething(x, y: int): int {.raises: [], tags: [], forbids: [].}"
+
+  block: # further issues with `--backend`
+    let file = testsDir / "misc/mbackend.nim"
+    var cmd = fmt"{nim} doc -b:cpp --hints:off --nimcache:{nimcache} {file}"
+    check execCmdEx(cmd) == ("", 0)
+    cmd = fmt"{nim} check -b:c -b:cpp --hints:off --nimcache:{nimcache} {file}"
+    check execCmdEx(cmd) == ("", 0)
+    # issue https://github.com/timotheecour/Nim/issues/175
+    cmd = fmt"{nim} c -b:js -b:cpp --hints:off --nimcache:{nimcache} {file}"
+    check execCmdEx(cmd) == ("", 0)
+
+  block: # some importc tests
+    # issue #14314
+    let file = testsDir / "misc/mimportc.nim"
+    let cmd = fmt"{nim} r -b:cpp --hints:off --nimcache:{nimcache} --warningAsError:ProveInit {file}"
+    check execCmdEx(cmd) == ("witness\n", 0)
+
+  block: # bug #20149
+    let file = testsDir / "misc/m20149.nim"
+    let cmd = fmt"{nim} r --hints:off --nimcache:{nimcache} --hintAsError:XDeclaredButNotUsed {file}"
+    check execCmdEx(cmd) == ("12\n", 0)
+
+  block: # bug #15316
+    when not defined(windows):
+      # This never worked reliably on Windows. Needs further investigation but it is hard to reproduce.
+      # Looks like a mild stack corruption when bailing out of nested exception handling.
+      let file = testsDir / "misc/m15316.nim"
+      let cmd = fmt"{nim} check --hints:off --nimcache:{nimcache} {file}"
+      check execCmdEx(cmd) == ("m15316.nim(1, 15) Error: expression expected, but found \')\'\nm15316.nim(2, 1) Error: expected: \':\', but got: \'[EOF]\'\nm15316.nim(2, 1) Error: expression expected, but found \'[EOF]\'\nm15316.nim(2, 1) " &
+            "Error: expected: \')\', but got: \'[EOF]\'\nError: illformed AST: \n", 1)
+
+
+  block: # config.nims, nim.cfg, hintConf, bug #16557
+    let cmd = fmt"{nim} r --hint:all:off --hint:conf tests/newconfig/bar/mfoo.nim"
+    let (outp, exitCode) = execCmdEx(cmd, options = {poStdErrToStdOut})
+    doAssert exitCode == 0
+    let dir = getCurrentDir()
+    let files = """
+tests/config.nims
+tests/newconfig/bar/nim.cfg
+tests/newconfig/bar/config.nims
+tests/newconfig/bar/mfoo.nim.cfg
+tests/newconfig/bar/mfoo.nims""".splitLines
+    var expected = ""
+    for a in files:
+      let b = dir / a
+      expected.add &"Hint: used config file '{b}' [Conf]\n"
+    doAssert outp.endsWith expected, outp & "\n" & expected
+
+  block: # bug #8219
+    let file = "tests/newconfig/mconfigcheck.nims"
+    let cmd = fmt"{nim} check --hints:off {file}"
+    check execCmdEx(cmd) == ("", 0)
+
+  block: # mfoo2.customext
+    let filename = testsDir / "newconfig/foo2/mfoo2.customext"
+    let cmd = fmt"{nim} e --hint:conf {filename}"
+    let (outp, exitCode) = execCmdEx(cmd, options = {poStdErrToStdOut})
+    doAssert exitCode == 0
+    var expected = &"Hint: used config file '{filename}' [Conf]\n"
+    doAssert outp.endsWith "123" & "\n" & expected
+
+
+  block: # nim --eval
+    let opt = "--hints:off"
+    check fmt"""{nim} {opt} --eval:"echo defined(nimscript)"""".execCmdEx == ("true\n", 0)
+    check fmt"""{nim} r {opt} --eval:"echo defined(c)"""".execCmdEx == ("true\n", 0)
+    check fmt"""{nim} r -b:js {opt} --eval:"echo defined(js)"""".execCmdEx == ("true\n", 0)
+
+  block: # `hintProcessing` dots should not interfere with `static: echo` + friends
+    let cmd = fmt"""{nim} r --hint:all:off --hint:processing -f --eval:"static: echo 1+1""""
+    let (outp, exitCode) = execCmdEx(cmd, options = {poStdErrToStdOut})
+    template check3(cond) = doAssert cond, $(outp,)
+    doAssert exitCode == 0
+    let lines = outp.splitLines
+    check3 lines.len == 3
+    when not defined(windows): # xxx: on windows, dots not properly handled, gives: `....2\n\n`
+      check3 lines[0].isDots
+      check3 lines[1] == "2"
+      check3 lines[2] == ""
+    else:
+      check3 "2" in outp
+
+  block: # nim secret
+    let opt = "--hint:all:off --hint:processing"
+    template check3(cond) = doAssert cond, $(outp,)
+    for extra in ["", "--stdout"]:
+      let cmd = fmt"""{nim} secret {opt} {extra}"""
+      # xxx minor bug: `nim --hint:QuitCalled:off secret` ignores the hint cmdline flag
+      template run(input2): untyped =
+        execCmdEx(cmd, options = {poStdErrToStdOut}, input = input2)
+      block:
+        let (outp, exitCode) = run """echo 1+2; import strutils; echo strip(" ab "); quit()"""
+        let lines = outp.splitLines
+        when not defined(windows):
+          check3 lines.len == 5
+          check3 lines[0].isDots
+          # check3 lines[1].isDots # todo nim secret might use parsing pipeline
+          check3 lines[2].dup(removePrefix(">>> ")) == "3" # prompt depends on `nimUseLinenoise`
+          check3 lines[3] == "ab"
+          check3 lines[4] == ""
+        else:
+          check3 "3" in outp
+          check3 "ab" in outp
+        doAssert exitCode == 0
+      block:
+        let (outp, exitCode) = run "echo 1+2; quit(2)"
+        check3 "3" in outp
+        doAssert exitCode == 2
+
+  block: # nimBetterRun
+    let file = "misc/mbetterrun.nim"
+    const nimcache2 = buildDir / "D20210423T185116"
+    removeDir nimcache2
+    # related to `-d:nimBetterRun`
+    let opt = fmt"-r --usenimcache --nimcache:{nimcache2}"
+    var ret = ""
+    for a in @["v1", "v2", "v1", "v3"]:
+      ret.add runNimCmdChk(file, fmt"{opt} -d:mbetterrunVal:{a}")
+    ret.add runNimCmdChk(file, fmt"{opt} -d:mbetterrunVal:v2", rtarg = "arg1 arg2")
+      # rt arguments should not cause a recompilation
+    doAssert ret == """
+compiling: v1
+running: v1
+compiling: v2
+running: v2
+running: v1
+compiling: v3
+running: v3
+running: v2
+""", ret
+
+  block: # nim dump
+    let cmd = fmt"{nim} dump --dump.format:json -d:D20210428T161003 --hints:off ."
+    let (ret, status) = execCmdEx(cmd)
+    doAssert status == 0
+    let j = ret.parseJson
+    # sanity checks
+    doAssert "D20210428T161003" in j["defined_symbols"].to(seq[string])
+    doAssert j["version"].to(string) == NimVersion
+    doAssert j["nimExe"].to(string) == getCurrentCompilerExe()
+
+  block: # genscript
+    const nimcache2 = buildDir / "D20210524T212851"
+    removeDir(nimcache2)
+    let input = "tgenscript_fakefile" # no need for a real file, --eval is good enough
+    let output = runNimCmdChk(input, fmt"""--genscript --nimcache:{nimcache2.quoteShell} --eval:"echo(12345)" """)
+    doAssert output.len == 0, output
+    let ext = when defined(windows): ".bat" else: ".sh"
+    let filename = fmt"compile_{input}{ext}" # synchronize with `generateScript`
+    doAssert fileExists(nimcache2/filename), nimcache2/filename
+    let (outp, status) = execCmdEx(genShellCmd(filename), options = {poStdErrToStdOut}, workingDir = nimcache2)
+    doAssert status == 0, outp
+    let (outp2, status2) = execCmdEx(nimcache2 / input, options = {poStdErrToStdOut})
+    doAssert outp2 == "12345\n", outp2
+    doAssert status2 == 0
+
+  block: # UnusedImport
+    proc fn(opt: string, expected: string) =
+      let output = runNimCmdChk("msgs/mused3.nim", fmt"--warning:all:off --warning:UnusedImport --hint:DuplicateModuleImport {opt}")
+      doAssert output == expected, opt & "\noutput:\n" & output & "expected:\n" & expected
+    fn("-d:case1"): """
+mused3.nim(13, 8) Warning: imported and not used: 'mused3b' [UnusedImport]
+"""
+    fn("-d:case2"): ""
+    fn("-d:case3"): ""
+    fn("-d:case4"): ""
+    fn("-d:case5"): ""
+    fn("-d:case6"): ""
+    fn("-d:case7"): ""
+    fn("-d:case8"): ""
+    fn("-d:case9"): ""
+    fn("-d:case10"): ""
+    when false:
+      fn("-d:case11"): """
+  Warning: imported and not used: 'm2' [UnusedImport]
+  """
+    fn("-d:case12"): """
+mused3.nim(75, 10) Hint: duplicate import of 'mused3a'; previous import here: mused3.nim(74, 10) [DuplicateModuleImport]
+"""
+
+  block: # FieldDefect
+    proc fn(opt: string, expected: string) =
+      let output = runNimCmdChk("misc/mfield_defect.nim", fmt"-r --warning:all:off --declaredlocs {opt}", status = 1)
+      doAssert expected in output, opt & "\noutput:\n" & output & "expected:\n" & expected
+    fn("-d:case1"): """mfield_defect.nim(25, 15) Error: field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'"""
+    fn("-d:case2 --gc:refc"): """mfield_defect.nim(25, 15) field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'"""
+    fn("-d:case1 -b:js"): """mfield_defect.nim(25, 15) Error: field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'"""
+    fn("-d:case2 -b:js"): """field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'"""
+    fn("-d:case2 --gc:arc"): """mfield_defect.nim(25, 15) field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'"""
+else:
+  discard # only during debugging, tests added here will run with `-d:nimTestsTrunnerDebugging` enabled
diff --git a/tests/misc/trunner_special.nim b/tests/misc/trunner_special.nim
new file mode 100644
index 000000000..e08b419b0
--- /dev/null
+++ b/tests/misc/trunner_special.nim
@@ -0,0 +1,37 @@
+discard """
+  targets: "c cpp"
+  joinable: false
+  disabled: osx
+"""
+
+#[
+Runs tests that require special treatment, e.g. because they rely on 3rd party code
+or require external networking.
+
+xxx test all tests/untestable/* here, possibly with adjustments to make running times reasonable
+]#
+
+import std/[strformat,os,unittest,compilesettings]
+import stdtest/specialpaths
+
+
+from stdtest/testutils import disableSSLTesting
+
+
+const
+  nim = getCurrentCompilerExe()
+  mode = querySetting(backend)
+
+proc runCmd(cmd: string) =
+  let ret = execShellCmd(cmd)
+  check ret == 0 # allows more than 1 failure
+
+proc main =
+  let options = fmt"-b:{mode} --hints:off"
+  block: # SSL nimDisableCertificateValidation integration tests
+    runCmd fmt"{nim} r {options} -d:nimDisableCertificateValidation -d:ssl {testsDir}/untestable/thttpclient_ssl_disabled.nim"
+  block: # SSL certificate check integration tests
+    runCmd fmt"{nim} r {options} -d:ssl --threads:on --mm:refc {testsDir}/untestable/thttpclient_ssl_remotenetwork.nim"
+
+when not disableSSLTesting():
+  main()
diff --git a/tests/misc/tsamename.nim b/tests/misc/tsamename.nim
new file mode 100644
index 000000000..b2d17a506
--- /dev/null
+++ b/tests/misc/tsamename.nim
@@ -0,0 +1,15 @@
+# bug #9891
+
+import "."/tsamename2
+
+# this works
+callFun(fooBar2)
+
+when true:
+  # Error: attempting to call routine: 'processPattern'
+  callFun(fooBar)
+
+when true:
+  # BUG: Error: internal error: expr(skModule); unknown symbol
+  proc processPattern() = discard
+  callFun(fooBar)
diff --git a/tests/misc/tsamename2.nim b/tests/misc/tsamename2.nim
new file mode 100644
index 000000000..d2272f557
--- /dev/null
+++ b/tests/misc/tsamename2.nim
@@ -0,0 +1,4 @@
+proc fooBar*()=discard
+proc fooBar2*()=discard
+proc callFun*[Fun](processPattern: Fun) =
+  processPattern()
diff --git a/tests/misc/tsemfold.nim b/tests/misc/tsemfold.nim
index 18c282d9e..2ce8d9560 100644
--- a/tests/misc/tsemfold.nim
+++ b/tests/misc/tsemfold.nim
@@ -2,22 +2,26 @@ discard """
   action: run
 """
 
-doAssertRaises(OverflowError): discard low(int8) - 1'i8
-doAssertRaises(OverflowError): discard high(int8) + 1'i8
-doAssertRaises(OverflowError): discard abs(low(int8))
-doAssertRaises(DivByZeroError): discard 1 mod 0
-doAssertRaises(DivByZeroError): discard 1 div 0
-doAssertRaises(OverflowError): discard low(int8) div -1'i8
+doAssertRaises(OverflowDefect): discard low(int8) - 1'i8
+doAssertRaises(OverflowDefect): discard high(int8) + 1'i8
+doAssertRaises(OverflowDefect): discard abs(low(int8))
+doAssertRaises(DivByZeroDefect): discard 1 mod 0
+doAssertRaises(DivByZeroDefect): discard 1 div 0
+doAssertRaises(OverflowDefect): discard low(int8) div -1'i8
 
-doAssertRaises(OverflowError): discard low(int64) - 1'i64
-doAssertRaises(OverflowError): discard high(int64) + 1'i64
+doAssertRaises(OverflowDefect): discard -low(int64)
+doAssertRaises(OverflowDefect): discard low(int64) - 1'i64
+doAssertRaises(OverflowDefect): discard high(int64) + 1'i64
 
 type E = enum eA, eB
-doAssertRaises(OverflowError): discard eA.pred
-doAssertRaises(OverflowError): discard eB.succ
+doAssertRaises(OverflowDefect): discard eA.pred
+doAssertRaises(OverflowDefect): discard eB.succ
 
-doAssertRaises(OverflowError): discard low(int8) * -1
-doAssertRaises(OverflowError): discard low(int64) * -1
-doAssertRaises(OverflowError): discard high(int8) * 2
-doAssertRaises(OverflowError): discard high(int64) * 2
+doAssertRaises(OverflowDefect): discard low(int8) * -1
+doAssertRaises(OverflowDefect): discard low(int64) * -1
+doAssertRaises(OverflowDefect): discard high(int8) * 2
+doAssertRaises(OverflowDefect): discard high(int64) * 2
 
+doAssert abs(-1) == 1
+doAssert 2 div 2 == 1
+doAssert 2 * 3 == 6
diff --git a/tests/misc/tshadow_magic_type.nim b/tests/misc/tshadow_magic_type.nim
index 03c83079e..8b2d26133 100644
--- a/tests/misc/tshadow_magic_type.nim
+++ b/tests/misc/tshadow_magic_type.nim
@@ -1,3 +1,10 @@
+discard """
+output: '''
+mylist
+'''
+"""
+
+
 type
   TListItemType* = enum
     RedisNil, RedisString
@@ -14,11 +21,9 @@ proc seq*() =
   discard
 
 proc lrange*(key: string): TRedisList =
-  var foo: TListItem
-  foo.kind = RedisNil
+  var foo = TListItem(kind: RedisString, str: key)
   result = @[foo]
 
-when isMainModule:
-  var p = lrange("mylist")
-  for i in items(p):
-    echo(i.str)
+var p = lrange("mylist")
+for i in items(p):
+  echo(i.str)
diff --git a/tests/misc/tsimplesort.nim b/tests/misc/tsimplesort.nim
index e4a8e0b37..395db55e6 100644
--- a/tests/misc/tsimplesort.nim
+++ b/tests/misc/tsimplesort.nim
@@ -10,7 +10,7 @@ type
   TSlotEnum = enum seEmpty, seFilled, seDeleted
   TKeyValuePair[A, B] = tuple[slot: TSlotEnum, key: A, val: B]
   TKeyValuePairSeq[A, B] = seq[TKeyValuePair[A, B]]
-  TTable* {.final, myShallow.}[A, B] = object
+  TTable*[A, B] {.final, myShallow.} = object
     data: TKeyValuePairSeq[A, B]
     counter: int
 
@@ -112,7 +112,7 @@ proc initTable*[A, B](initialSize=64): TTable[A, B] =
   result.counter = 0
   newSeq(result.data, initialSize)
 
-proc toTable*[A, B](pairs: openarray[tuple[key: A,
+proc toTable*[A, B](pairs: openArray[tuple[key: A,
                     val: B]]): TTable[A, B] =
   ## creates a new hash table that contains the given `pairs`.
   result = initTable[A, B](nextPowerOfTwo(pairs.len+10))
@@ -137,8 +137,7 @@ proc `$`*[A, B](t: TTable[A, B]): string =
 # ------------------------------ count tables -------------------------------
 
 type
-  TCountTable* {.final, myShallow.}[
-      A] = object ## table that counts the number of each key
+  TCountTable*[A] {.final, myShallow.} = object ## table that counts the number of each key
     data: seq[tuple[key: A, val: int]]
     counter: int
 
diff --git a/tests/misc/tsimtych.nim b/tests/misc/tsimtych.nim
deleted file mode 100644
index 037172bd5..000000000
--- a/tests/misc/tsimtych.nim
+++ /dev/null
@@ -1,12 +0,0 @@
-discard """
-  file: "tsimtych.nim"
-  line: 10
-  errormsg: "type mismatch: got <bool> but expected \'string\'"
-"""
-# Test 2
-# Simple type checking
-
-var a: string
-a = false #ERROR
-
-
diff --git a/tests/misc/tsizeof.nim b/tests/misc/tsizeof.nim
index 4afd48472..ce5334664 100644
--- a/tests/misc/tsizeof.nim
+++ b/tests/misc/tsizeof.nim
@@ -1,10 +1,742 @@
-# Test the sizeof proc
+discard """
+  targets: "c cpp"
+  output: '''
+body executed
+body executed
+OK
+macros api OK
+'''
+"""
 
+# This is for Azure. The keyword ``alignof`` only exists in ``c++11``
+# and newer. On Azure gcc does not default to c++11 yet.
+when defined(cpp) and not defined(windows):
+  {.passC: "-std=c++11".}
+
+# Object offsets are different for inheritance objects when compiling
+# to c++.
+
+type
+  TMyEnum = enum
+    tmOne, tmTwo, tmThree, tmFour
+
+  TMyArray1 = array[3, uint8]
+  TMyArray2 = array[1..3, int32]
+  TMyArray3 = array[TMyEnum, float64]
+
+var failed = false
+
+const
+  mysize1 = sizeof(TMyArray1)
+  mysize2 = sizeof(TMyArray2)
+  mysize3 = sizeof(TMyArray3)
+
+doAssert mysize1 == 3
+doAssert mysize2 == 12
+doAssert mysize3 == 32
+
+import macros, typetraits
+
+macro testSizeAlignOf(args: varargs[untyped]): untyped =
+  result = newStmtList()
+  for arg in args:
+    result.add quote do:
+      let
+        c_size = c_sizeof(`arg`)
+        nim_size = sizeof(`arg`)
+        c_align = c_alignof(type(`arg`))
+        nim_align = alignof(`arg`)
+
+      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
+        if nim_align != c_align:
+          msg.add  " align(get, expected): " & $nim_align & " != " & $c_align
+        echo msg
+        failed = true
+
+
+macro testOffsetOf(a, b: untyped): untyped =
+  let typeName = newLit(a.repr)
+  let member   = newLit(b.repr)
+  result = quote do:
+    let
+      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
+
+proc strAlign(arg: string): string =
+  const minLen = 22
+  result = arg
+  for i in 0 ..< minLen - arg.len:
+    result &= ' '
+
+macro c_offsetof(fieldAccess: typed): int32 =
+  ## Bullet proof implementation that works on actual offsetof operator
+  ## in the c backend. Assuming of course this implementation is
+  ## correct.
+  let s = if fieldAccess.kind == nnkCheckedFieldExpr: fieldAccess[0]
+          else: fieldAccess
+  let a = s[0].getTypeInst
+  let b = s[1]
+  result = quote do:
+    var res: int32
+    {.emit: [res, " = offsetof(", `a`, ", ", `b`, ");"] .}
+    res
+
+template c_offsetof(t: typedesc, a: untyped): int32 =
+  var x: ptr t
+  c_offsetof(x[].a)
+
+macro c_sizeof(a: typed): int32 =
+  ## Bullet proof implementation that works using the sizeof operator
+  ## in the c backend. Assuming of course this implementation is
+  ## correct.
+  result = quote do:
+    var res: int32
+    {.emit: [res, " = sizeof(", `a`, ");"] .}
+    res
+
+macro c_alignof(arg: untyped): untyped =
+  ## Bullet proof implementation that works on actual alignment
+  ## behavior measured at runtime.
+  let typeSym = genSym(nskType, "AlignTestType"&arg.repr)
+  result = quote do:
+    type
+      `typeSym` = object
+        causeAlign: byte
+        member: `arg`
+    c_offsetof(`typeSym`, member)
+
+macro testAlign(arg:untyped):untyped =
+  let prefix = newLit(arg.lineinfo & "  alignof " & arg.repr & " ")
+  result = quote do:
+    let cAlign = c_alignof(`arg`)
+    let nimAlign = alignof(`arg`)
+    if cAlign != nimAlign:
+      echo `prefix`, cAlign, " != ", nimAlign
+      failed = true
+
+macro testSize(arg:untyped):untyped =
+  let prefix = newLit(arg.lineinfo & "  sizeof " & arg.repr & " ")
+  result = quote do:
+    let cSize = c_sizeof(`arg`)
+    let nimSize = sizeof(`arg`)
+    if cSize != nimSize:
+      echo `prefix`, cSize, " != ", nimSize
+      failed = true
+
+type
+  MyEnum {.pure.} = enum
+    ValueA
+    ValueB
+    ValueC
+
+  OtherEnum {.pure, size: 8.} = enum
+    ValueA
+    ValueB
+
+  Enum1 {.pure, size: 1.} = enum
+    ValueA
+    ValueB
+
+  Enum2 {.pure, size: 2.} = enum
+    ValueA
+    ValueB
+
+  Enum4 {.pure, size: 4.} = enum
+    ValueA
+    ValueB
+
+  Enum8 {.pure, size: 8.} = enum
+    ValueA
+    ValueB
+
+  # Must have more than 32 elements so that set[MyEnum33] will become compile to an int64.
+  MyEnum33 {.pure.} = enum
+    Value1, Value2, Value3, Value4, Value5, Value6,
+    Value7, Value8, Value9, Value10, Value11, Value12,
+    Value13, Value14, Value15, Value16, Value17, Value18,
+    Value19, Value20, Value21, Value22, Value23, Value24,
+    Value25, Value26, Value27, Value28, Value29, Value30,
+    Value31, Value32, Value33
+
+proc transformObjectconfigPacked(arg: NimNode): NimNode =
+  let debug = arg.kind == nnkPragmaExpr
+
+  if arg.eqIdent("objectconfig"):
+    result = ident"packed"
+  else:
+    result = copyNimNode(arg)
+    for child in arg:
+      result.add transformObjectconfigPacked(child)
+
+proc removeObjectconfig(arg: NimNode): NimNode =
+  if arg.kind == nnkPragmaExpr and arg[1][0].eqIdent "objectconfig":
+    result = arg[0]
+  else:
+    result = copyNimNode(arg)
+    for child in arg:
+      result.add removeObjectconfig(child)
+
+macro testinstance(body: untyped): untyped =
+  let bodyPure = removeObjectconfig(body)
+  let bodyPacked = transformObjectconfigPacked(body)
+
+  result = quote do:
+    proc pureblock(): void =
+      const usePacked {.inject.} = false
+      `bodyPure`
+
+    pureblock()
+
+    proc packedblock(): void =
+      const usePacked {.inject.} = true
+      `bodyPacked`
+
+    packedblock()
+
+proc testPrimitiveTypes(): void =
+  testAlign(pointer)
+  testAlign(int)
+  testAlign(uint)
+  testAlign(int8)
+  testAlign(int16)
+  testAlign(int32)
+  testAlign(int64)
+  testAlign(uint8)
+  testAlign(uint16)
+  testAlign(uint32)
+  testAlign(uint64)
+  testAlign(float)
+  testAlign(float32)
+  testAlign(float64)
+
+  testAlign(MyEnum)
+  testAlign(OtherEnum)
+  testAlign(Enum1)
+  testAlign(Enum2)
+  testAlign(Enum4)
+  testAlign(Enum8)
+
+testPrimitiveTypes()
+
+testinstance:
+  type
+
+    EnumObjectA  {.objectconfig.} = object
+      a : Enum1
+      b : Enum2
+      c : Enum4
+      d : Enum8
+
+    EnumObjectB  {.objectconfig.} = object
+      a : Enum8
+      b : Enum4
+      c : Enum2
+      d : Enum1
+
+    TrivialType  {.objectconfig.} = object
+      x,y,z: int8
+
+    SimpleAlignment {.objectconfig.} = object
+      # behaves differently on 32bit Windows and 32bit Linux
+      a,b: int8
+      c: int64
+
+    AlignAtEnd {.objectconfig.} = object
+      a: int64
+      b,c: int8
+
+    SimpleBranch {.objectconfig.} = object
+      case kind: MyEnum
+      of MyEnum.ValueA:
+        a: int16
+      of MyEnum.ValueB:
+        b: int32
+      of MyEnum.ValueC:
+        c: int64
+
+    PaddingBeforeBranchA {.objectconfig.} = object
+      cause: int8
+      case kind: MyEnum
+      of MyEnum.ValueA:
+        a: int16
+      of MyEnum.ValueB:
+        b: int32
+      of MyEnum.ValueC:
+        c: int64
+
+    PaddingBeforeBranchB {.objectconfig.} = object
+      cause: int8
+      case kind: MyEnum
+      of MyEnum.ValueA:
+        a: int8
+      of MyEnum.ValueB:
+        b: int16
+      of MyEnum.ValueC:
+        c: int32
+
+    PaddingAfterBranch {.objectconfig.} = object
+      case kind: MyEnum
+      of MyEnum.ValueA:
+        a: int8
+      of MyEnum.ValueB:
+        b: int16
+      of MyEnum.ValueC:
+        c: int32
+      cause: int64
+
+    RecursiveStuff {.objectconfig.} = object
+      case kind: MyEnum    # packedOffset:    0
+      of MyEnum.ValueA:    # packedOffset:
+        a: int16           # packedOffset:    1
+      of MyEnum.ValueB:    # packedOffset:
+        b: int32           # packedOffset:    1
+      of MyEnum.ValueC:    # packedOffset:
+        case kind2: MyEnum # packedOffset:    1
+        of MyEnum.ValueA:  # packedOffset:
+          ca1: int8
+          ca2: int32
+        of MyEnum.ValueB:  # packedOffset:
+          cb: int32        # packedOffset:    2
+        of MyEnum.ValueC:  # packedOffset:
+          cc: int64        # packedOffset:    2
+        d1: int8
+        d2: int64
+
+    Foobar {.objectconfig.} = object
+      case kind: OtherEnum
+      of OtherEnum.ValueA:
+        a: uint8
+      of OtherEnum.ValueB:
+        b: int8
+      c: int8
+
+    PaddingOfSetEnum33 {.objectconfig.} = object
+      cause: int8
+      theSet: set[MyEnum33]
+
+    Bazing {.objectconfig.} = object of RootObj
+      a: int64
+      # TODO test on 32 bit system
+      # only there the object header is smaller than the first member
+
+    InheritanceA {.objectconfig.} = object of RootObj
+      a: char
+
+    InheritanceB {.objectconfig.} = object of InheritanceA
+      b: char
+
+    InheritanceC {.objectconfig.} = object of InheritanceB
+      c: char
+
+    # from issue 4763
+    GenericObject[T] {.objectconfig.} = object
+      a: int32
+      b: T
+
+    # this type mixes `packed` with `align`.
+    MyCustomAlignPackedObject {.objectconfig.} = object
+      a: char
+      b {.align: 32.}: int32 # align overrides `packed` for this field.
+      c: char
+      d: int32  # unaligned
+
+    Kind = enum
+      K1, K2
+  
+    AnotherEnum = enum
+      X1, X2, X3
+
+    MyObject = object
+      s: string
+      case k: Kind
+      of K1: nil
+      of K2:
+          x: float
+          y: int32
+      z: AnotherEnum
+
+    Stack[N: static int, T: object] = object
+      pad: array[128 - sizeof(array[N, ptr T]) - sizeof(int) - sizeof(pointer), byte]
+      stack: array[N, ptr T]
+      len*: int
+      rawMem: ptr array[N, T]
+
+    Stack2[T: object] = object
+      pad: array[128 - sizeof(array[sizeof(T), ptr T]), byte]
+    
+
+  const trivialSize = sizeof(TrivialType) # needs to be able to evaluate at compile time
+
+  proc main(): void =
+    var t : TrivialType
+    var a : SimpleAlignment
+    var b : AlignAtEnd
+    var c : SimpleBranch
+    var d : PaddingBeforeBranchA
+    var e : PaddingBeforeBranchB
+    var f : PaddingAfterBranch
+    var g : RecursiveStuff
+    var ro : RootObj
+    var go : GenericObject[int64]
+    var po : PaddingOfSetEnum33
+    var capo: MyCustomAlignPackedObject
+    var issue15516: MyObject
+    var issue12636_1: Stack[5, MyObject]
+    var issue12636_2: Stack2[MyObject]
+
+    var
+      e1: Enum1
+      e2: Enum2
+      e4: Enum4
+      e8: Enum8
+    var
+      eoa: EnumObjectA
+      eob: EnumObjectB
+
+    testAlign(SimpleAlignment)
+
+    # sanity check to ensure both branches are actually executed
+    when usePacked:
+      doAssert sizeof(SimpleAlignment) == 10
+    else:
+      doAssert sizeof(SimpleAlignment) > 10
+
+    testSizeAlignOf(t,a,b,c,d,e,f,g,ro,go,po, e1, e2, e4, e8, eoa, eob, capo, issue15516, issue12636_1, issue12636_2)
+
+    type
+      WithBitsize {.objectconfig.} = object
+        bitfieldA {.bitsize: 16.}: uint32
+        bitfieldB {.bitsize: 16.}: uint32
+
+    var wbs: WithBitsize
+    testSize(wbs)
+
+    testOffsetOf(TrivialType, x)
+    testOffsetOf(TrivialType, y)
+    testOffsetOf(TrivialType, z)
+
+    testOffsetOf(SimpleAlignment, a)
+    testOffsetOf(SimpleAlignment, b)
+    testOffsetOf(SimpleAlignment, c)
+
+    testOffsetOf(AlignAtEnd, a)
+    testOffsetOf(AlignAtEnd, b)
+    testOffsetOf(AlignAtEnd, c)
+
+    testOffsetOf(SimpleBranch, a)
+    testOffsetOf(SimpleBranch, b)
+    testOffsetOf(SimpleBranch, c)
+
+    testOffsetOf(PaddingBeforeBranchA, cause)
+    testOffsetOf(PaddingBeforeBranchA, a)
+    testOffsetOf(PaddingBeforeBranchB, cause)
+    testOffsetOf(PaddingBeforeBranchB, a)
+
+    testOffsetOf(PaddingAfterBranch, a)
+    testOffsetOf(PaddingAfterBranch, cause)
+
+    testOffsetOf(Foobar, c)
+
+    testOffsetOf(PaddingOfSetEnum33, cause)
+    testOffsetOf(PaddingOfSetEnum33, theSet)
+
+    testOffsetOf(Bazing, a)
+    testOffsetOf(InheritanceA, a)
+    testOffsetOf(InheritanceB, b)
+    testOffsetOf(InheritanceC, c)
+
+    testOffsetOf(EnumObjectA, a)
+    testOffsetOf(EnumObjectA, b)
+    testOffsetOf(EnumObjectA, c)
+    testOffsetOf(EnumObjectA, d)
+    testOffsetOf(EnumObjectB, a)
+    testOffsetOf(EnumObjectB, b)
+    testOffsetOf(EnumObjectB, c)
+    testOffsetOf(EnumObjectB, d)
+
+    testOffsetOf(RecursiveStuff, kind)
+    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)
+
+    testOffsetOf(MyCustomAlignPackedObject, a)
+    testOffsetOf(MyCustomAlignPackedObject, b)
+    testOffsetOf(MyCustomAlignPackedObject, c)
+    testOffsetOf(MyCustomAlignPackedObject, d)
+
+    echo "body executed" # sanity check to ensure this logic isn't skipped entirely
+
+
+  main()
+
+{.emit: """/*TYPESECTION*/
+typedef struct{
+  float a; float b;
+} Foo;
+""".}
+
+type
+  Foo {.importc.} = object
+
+  Bar = object
+    b: byte
+    foo: Foo
+
+assert sizeof(Bar) == 12
+
+# bug #10082
+type
+  A = int8        # change to int16 and get sizeof(C)==6
+  B = int16
+  C {.packed.} = object
+    d {.bitsize:  1.}: A
+    e {.bitsize:  7.}: A
+    f {.bitsize: 16.}: B
+
+assert sizeof(C) == 3
+
+
+type
+  MixedBitsize {.packed.} = object
+    a: uint32
+    b {.bitsize:  8.}: uint8
+    c {.bitsize:  1.}: uint8
+    d {.bitsize:  7.}: uint8
+    e {.bitsize: 16.}: uint16
+    f: uint32
+
+doAssert sizeof(MixedBitsize) == 12
+
+
+type
+  MyUnionType {.union.} = object
+    a: int32
+    b: float32
+
+  MyCustomAlignUnion {.union.} = object
+    c: char
+    a {.align: 32.}: int
+
+  MyCustomAlignObject = object
+    c: char
+    a {.align: 32.}: int
+
+doAssert sizeof(MyUnionType) == 4
+doAssert sizeof(MyCustomAlignUnion) == 32
+doAssert alignof(MyCustomAlignUnion) == 32
+doAssert sizeof(MyCustomAlignObject) == 64
+doAssert alignof(MyCustomAlignObject) == 32
+
+
+
+
+
+
+##########################################
+# bug #9794
+##########################################
+
+type
+  imported_double {.importc: "double".} = object
+
+  Pod = object
+    v* : imported_double
+    seed*: int32
+
+  Pod2 = tuple[v: imported_double, seed: int32]
+
+proc foobar() =
+  testAlign(Pod)
+  testSize(Pod)
+  testAlign(Pod2)
+  testSize(Pod2)
+  doAssert sizeof(Pod) == sizeof(Pod2)
+  doAssert alignof(Pod) == alignof(Pod2)
+foobar()
+
+if failed:
+  quit("FAIL")
+else:
+  echo "OK"
+
+##########################################
+# sizeof macros API
+##########################################
+
+import macros
+
+type
+  Vec2f = object
+    x,y: float32
+
+  Vec4f = object
+    x,y,z,w: float32
+
+  # this type is constructed to have no platform depended alignment.
+  ParticleDataA = object
+    pos, vel: Vec2f
+    birthday: float32
+    padding: float32
+    moreStuff: Vec4f
+
+const expected = [
+  # name size align offset
+  ("pos", 8, 4, 0),
+  ("vel", 8, 4, 8),
+  ("birthday", 4, 4, 16),
+  ("padding", 4, 4, 20),
+  ("moreStuff", 16, 4, 24)
+]
+
+macro typeProcessing(arg: typed): untyped =
+  let recList = arg.getTypeImpl[2]
+  recList.expectKind nnkRecList
+  for i, identDefs in recList:
+    identDefs.expectKind nnkIdentDefs
+    identDefs.expectLen 3
+    let sym = identDefs[0]
+    sym.expectKind nnkSym
+    doAssert expected[i][0] == sym.strVal
+    doAssert expected[i][1] == getSize(sym)
+    doAssert expected[i][2] == getAlign(sym)
+    doAssert expected[i][3] == getOffset(sym)
+
+  result = newCall(bindSym"echo", newLit("macros api OK"))
+
+proc main() =
+  var mylocal: ParticleDataA
+  typeProcessing(mylocal)
+
+main()
+
+# issue #11320 use UncheckedArray
+
+type
+  Payload = object
+    something: int8
+    vals: UncheckedArray[int32]
+
+proc payloadCheck() =
+  doAssert offsetOf(Payload, vals) == 4
+  doAssert sizeof(Payload) == 4
+
+payloadCheck()
+
+# offsetof tuple types
+
+type
+  MyTupleType = tuple
+    a: float64
+    b: float64
+    c: float64
+
+  MyOtherTupleType = tuple
+    a: float64
+    b: imported_double
+    c: float64
+
+  MyCaseObject = object
+    val1: imported_double
+    case kind: bool
+    of true:
+      val2,val3: float32
+    else:
+      val4,val5: int32
+
+doAssert offsetof(MyTupleType, a) == 0
+doAssert offsetof(MyTupleType, b) == 8
+doAssert offsetof(MyTupleType, c) == 16
+
+doAssert offsetof(MyOtherTupleType, a) == 0
+doAssert offsetof(MyOtherTupleType, b) == 8
+
+# The following expression can only work if the offsetof expression is
+# properly forwarded for the C code generator.
+doAssert offsetof(MyOtherTupleType, c) == 16
+doAssert offsetof(Bar, foo) == 4
+doAssert offsetof(MyCaseObject, val1) == 0
+doAssert offsetof(MyCaseObject, kind) == 8
+doAssert offsetof(MyCaseObject, val2) == 12
+doAssert offsetof(MyCaseObject, val3) == 16
+doAssert offsetof(MyCaseObject, val4) == 12
+doAssert offsetof(MyCaseObject, val5) == 16
+
+template reject(e) =
+  static: assert(not compiles(e))
+
+reject:
+  const off1 = offsetof(MyOtherTupleType, c)
+
+reject:
+  const off2 = offsetof(MyOtherTupleType, b)
+
+reject:
+  const off3 = offsetof(MyCaseObject, kind)
+
+
+type
+  MyPackedCaseObject {.packed.} = object
+    val1: imported_double
+    case kind: bool
+    of true:
+      val2,val3: float32
+    else:
+      val4,val5: int32
+
+# packed case object
+
+doAssert offsetof(MyPackedCaseObject, val1) == 0
+doAssert offsetof(MyPackedCaseObject, val2) == 9
+doAssert offsetof(MyPackedCaseObject, val3) == 13
+doAssert offsetof(MyPackedCaseObject, val4) == 9
+doAssert offsetof(MyPackedCaseObject, val5) == 13
+
+reject:
+  const off5 = offsetof(MyPackedCaseObject, val2)
+
+reject:
+  const off6 = offsetof(MyPackedCaseObject, val3)
+
+reject:
+  const off7 = offsetof(MyPackedCaseObject, val4)
+
+reject:
+  const off8 = offsetof(MyPackedCaseObject, val5)
+
+
+type
+  O0 = object
+  T0 = tuple[]
+
+doAssert sizeof(O0) == 1
+doAssert sizeof(T0) == 1
+
+
+type
+  # this thing may not have padding bytes at the end
+  PackedUnion* {.union, packed.} = object
+    a*: array[11, byte]
+    b*: int64
+
+doAssert sizeof(PackedUnion) == 11
+doAssert alignof(PackedUnion) == 1
+
+# bug #22553
 type
-  TMyRecord {.final.} = object
-    x, y: int
-    b: bool
-    r: float
-    s: string
+  ChunkObj = object
+    data: UncheckedArray[byte]
 
-write(stdout, sizeof(TMyRecord))
+doAssert sizeof(ChunkObj) == 1
+doAssert offsetOf(ChunkObj, data) == 1
diff --git a/tests/misc/tsizeof2.nim b/tests/misc/tsizeof2.nim
new file mode 100644
index 000000000..da28de508
--- /dev/null
+++ b/tests/misc/tsizeof2.nim
@@ -0,0 +1,17 @@
+discard """
+errormsg: "'sizeof' requires '.importc' types to be '.completeStruct'"
+line: 9
+"""
+
+type
+  MyStruct {.importc: "MyStruct".} = object
+
+const i = sizeof(MyStruct)
+
+echo i
+
+# bug #9868
+proc foo(a: SomeInteger): array[sizeof(a), byte] =
+  discard
+
+discard foo(1)
diff --git a/tests/misc/tsizeof3.nim b/tests/misc/tsizeof3.nim
new file mode 100644
index 000000000..f0ba8c4d0
--- /dev/null
+++ b/tests/misc/tsizeof3.nim
@@ -0,0 +1,58 @@
+discard """
+output: '''
+@[48, 57]
+'''
+"""
+# bug #7238
+
+type ByteArrayBE*[N: static[int]] = array[N, byte]
+  ## A byte array that stores bytes in big-endian order
+
+proc toByteArrayBE*[T: SomeInteger](num: T): ByteArrayBE[sizeof(T)]=
+  ## Convert an integer (in native host endianness) to a big-endian byte array
+  ## Notice the result type
+  const N = T.sizeof
+  for i in 0 ..< N:
+    result[i] = byte((num shr ((N-1-i) * 8)) and high(int8))
+
+let a = 12345.toByteArrayBE
+echo a[^2 .. ^1] # to make it work on both 32-bit and 64-bit
+
+#---------------------------------------------------------------------
+
+type
+  Payload = object
+    something: int
+    vals: UncheckedArray[int]
+
+static:
+  doAssert(compiles(offsetOf(Payload, vals)))
+
+
+type
+  GoodboySave* {.bycopy.} = object
+    saveCount: uint8
+    savePoint: uint16
+    shards: uint32
+    friendCount: uint8
+    friendCards: set[0..255]
+    locationsKnown: set[0..127]
+    locationsUnlocked: set[0..127]
+    pickupsObtained: set[0..127]
+    pickupsUsed: set[0..127]
+    pickupCount: uint8
+
+block: # bug #20914
+  block:
+    proc csizeof[T](a: T): int {.importc:"sizeof", nodecl.}
+
+    var s: GoodboySave
+    doAssert sizeof(s) == 108
+    doAssert csizeof(s) == static(sizeof(s))
+
+  block:
+    proc calignof[T](a: T): int {.importc:"alignof", header: "<stdalign.h>".}
+
+    var s: set[0..256]
+    doAssert alignof(s) == 1
+    doAssert calignof(s) == static(alignof(s))
diff --git a/tests/misc/tsizeof4.nim b/tests/misc/tsizeof4.nim
new file mode 100644
index 000000000..94c08ba39
--- /dev/null
+++ b/tests/misc/tsizeof4.nim
@@ -0,0 +1,20 @@
+discard """
+disabled: "arm64"
+"""
+
+# bug #11792
+type
+  m256d {.importc: "__m256d", header: "immintrin.h".} = object
+
+  MyKind = enum
+    k1, k2, k3
+
+  MyTypeObj = object
+    kind: MyKind
+    x: int
+    amount: UncheckedArray[m256d]
+
+
+# The sizeof(MyTypeObj) is not equal to (sizeof(int) + sizeof(MyKind)) due to
+# alignment requirement of m256d, make sure Nim understands that
+doAssert(sizeof(MyTypeObj) > sizeof(int) + sizeof(MyKind))
diff --git a/tests/misc/tslices.nim b/tests/misc/tslices.nim
deleted file mode 100644
index 388a46509..000000000
--- a/tests/misc/tslices.nim
+++ /dev/null
@@ -1,59 +0,0 @@
-discard """
-  file: "tslices.nim"
-  output: '''456456
-456456
-456456
-Zugr5nd
-egerichtetd
-verichtetd
-'''
-"""
-
-# Test the new slices.
-
-import strutils
-
-var mystr = "Abgrund"
-mystr[..1] = "Zu"
-
-mystr[4..4] = "5"
-
-type
-  TEnum = enum e1, e2, e3, e4, e5, e6
-
-var myarr: array[TEnum, int] = [1, 2, 3, 4, 5, 6]
-myarr[e1..e3] = myarr[e4..e6]
-myarr[..e3] = myarr[e4..e6]
-
-for x in items(myarr): stdout.write(x)
-echo()
-
-var myarr2: array[0..5, int] = [1, 2, 3, 4, 5, 6]
-myarr2[0..2] = myarr2[3..5]
-
-for x in items(myarr2): stdout.write(x)
-echo()
-
-
-var myseq = @[1, 2, 3, 4, 5, 6]
-myseq[0..2] = myseq[^3 .. ^1]
-
-for x in items(myseq): stdout.write(x)
-echo()
-
-echo mystr
-
-mystr[4..4] = "u"
-
-# test full replacement
-mystr[.. ^2] = "egerichtet"
-
-echo mystr
-
-mystr[0..2] = "ve"
-echo mystr
-
-var s = "abcdef"
-s[1 .. ^2] = "xyz"
-assert s == "axyzf"
-
diff --git a/tests/misc/tsortdev.nim b/tests/misc/tsortdev.nim
index f360d9646..6a290577b 100644
--- a/tests/misc/tsortdev.nim
+++ b/tests/misc/tsortdev.nim
@@ -1,5 +1,5 @@
 discard """
-  output: "done"
+  output: "done tsortdev"
 """
 
 import algorithm, strutils
@@ -39,7 +39,7 @@ proc cmpPlatforms(a, b: string): int =
 
 proc sorted[T](a: openArray[T]): bool =
   result = true
-  for i in 0 .. < a.high:
+  for i in 0 ..< a.high:
     if cmpPlatforms(a[i], a[i+1]) > 0:
       echo "Out of order: ", a[i], " ", a[i+1]
       result = false
@@ -55,5 +55,4 @@ proc main() =
 for i in 0..1_000:
   main()
 
-echo "done"
-
+echo "done tsortdev"
diff --git a/tests/misc/tstrace.nim b/tests/misc/tstrace.nim
index 23590d958..00af0af69 100644
--- a/tests/misc/tstrace.nim
+++ b/tests/misc/tstrace.nim
@@ -1,3 +1,23 @@
+discard """
+exitcode: 1
+output: '''
+Traceback (most recent call last)
+tstrace.nim(36)          tstrace
+tstrace.nim(28)          recTest
+tstrace.nim(28)          recTest
+tstrace.nim(28)          recTest
+tstrace.nim(28)          recTest
+tstrace.nim(28)          recTest
+tstrace.nim(28)          recTest
+tstrace.nim(28)          recTest
+tstrace.nim(28)          recTest
+tstrace.nim(28)          recTest
+tstrace.nim(28)          recTest
+tstrace.nim(31)          recTest
+SIGSEGV: Illegal storage access. (Attempt to read from nil?)
+'''
+"""
+
 # Test the new stacktraces (great for debugging!)
 
 {.push stack_trace: on.}
diff --git a/tests/misc/tstrange.nim b/tests/misc/tstrange.nim
index fee0f44e4..f8c063240 100644
--- a/tests/misc/tstrange.nim
+++ b/tests/misc/tstrange.nim
@@ -1,8 +1,9 @@
 discard """
-  file: "tstrange.nim"
-  output: '''hallo40
+output: '''
+hallo40
 1
-2'''
+2
+'''
 """
 # test for extremely strange bug
 
@@ -23,6 +24,5 @@ write(stdout, ack(5, 4))
 
 # bug #1442
 let h=3
-for x in 0.. <h.int:
+for x in 0 ..< h.int:
   echo x
-
diff --git a/tests/misc/tstrdist.nim b/tests/misc/tstrdist.nim
index 3e1939e73..53ace2fae 100644
--- a/tests/misc/tstrdist.nim
+++ b/tests/misc/tstrdist.nim
@@ -23,4 +23,4 @@ proc editDistance(a, b: string): int =
       c[(i-1)*n + (j-1)] = min(x,min(y,z))
   return c[n*m]
 
-write(stdout, editDistance("abc", "abd"))
+doAssert editDistance("abc", "abd") == 3
diff --git a/tests/misc/ttlsemulation.nim b/tests/misc/ttlsemulation.nim
new file mode 100644
index 000000000..767a9bd4e
--- /dev/null
+++ b/tests/misc/ttlsemulation.nim
@@ -0,0 +1,76 @@
+discard """
+  disabled: i386
+  matrix: "-d:nimTtlsemulationCase1 --threads --tlsEmulation:on; -d:nimTtlsemulationCase2 --threads --tlsEmulation:off; -d:nimTtlsemulationCase3 --threads"
+  targets: "c cpp"
+"""
+
+#[
+tests for: `.cppNonPod`, `--tlsEmulation`
+]#
+
+import std/sugar
+
+block:
+  # makes sure the logic in config/nim.cfg or testament doesn't interfere with `--tlsEmulation` so we test the right thing.
+  when defined(nimTtlsemulationCase1):
+    doAssert compileOption("tlsEmulation")
+  elif defined(nimTtlsemulationCase2):
+    doAssert not compileOption("tlsEmulation")
+  elif defined(nimTtlsemulationCase3):
+    when defined(osx):
+      doAssert not compileOption("tlsEmulation")
+  else:
+    doAssert false
+
+block:
+  proc main1(): int =
+    var g0 {.threadvar.}: int
+    g0.inc
+    g0
+  let s = collect:
+    for i in 0..<3: main1()
+  doAssert s == @[1,2,3]
+
+when defined(cpp): # bug #16752
+  when defined(windows) and defined(nimTtlsemulationCase2):
+    discard # xxx this failed with exitCode 1
+  else:
+    type Foo1 {.importcpp: "Foo1", header: "mtlsemulation.h".} = object
+      x: cint
+    type Foo2 {.cppNonPod, importcpp: "Foo2", header: "mtlsemulation.h".} = object
+      x: cint
+
+    var ctorCalls {.importcpp.}: cint
+    var dtorCalls {.importcpp.}: cint
+    type Foo3 {.cppNonPod, importcpp: "Foo3", header: "mtlsemulation.h".} = object
+      x: cint
+
+    proc sub(i: int) =
+      var g1 {.threadvar.}: Foo1
+      var g2 {.threadvar.}: Foo2
+      var g3 {.threadvar.}: Foo3
+      discard g1
+      discard g2
+
+      # echo (g3.x, ctorCalls, dtorCalls)
+      when compileOption("tlsEmulation"):
+        # xxx bug
+        discard
+      else:
+        doAssert g3.x.int == 10 + i
+        doAssert ctorCalls == 2
+      doAssert dtorCalls == 1
+      g3.x.inc
+
+    proc main() =
+      doAssert ctorCalls == 0
+      doAssert dtorCalls == 0
+      block:
+        var f3: Foo3
+        doAssert f3.x == 10
+      doAssert ctorCalls == 1
+      doAssert dtorCalls == 1
+
+      for i in 0..<3:
+        sub(i)
+    main()
diff --git a/tests/misc/tunsignedcomp.nim b/tests/misc/tunsignedcomp.nim
deleted file mode 100644
index 19c8876b1..000000000
--- a/tests/misc/tunsignedcomp.nim
+++ /dev/null
@@ -1,136 +0,0 @@
-discard """
-  output: ''''''
-  disabled: "true"
-"""
-
-# All operations involving uint64 are commented out
-# as they're not yet supported.
-# All other operations are handled by implicit conversions from uints to ints
-# uint64 could be supported but would need special implementation of the operators
-
-# unsigned < signed
-
-assert 10'u8 < 20'i8
-assert 10'u8 < 20'i16
-assert 10'u8 < 20'i32
-assert 10'u8 < 20'i64
-
-assert 10'u16 < 20'i8
-assert 10'u16 < 20'i16
-assert 10'u16 < 20'i32
-assert 10'u16 < 20'i64
-
-assert 10'u32 < 20'i8
-assert 10'u32 < 20'i16
-assert 10'u32 < 20'i32
-assert 10'u32 < 20'i64
-
-# assert 10'u64 < 20'i8
-# assert 10'u64 < 20'i16
-# assert 10'u64 < 20'i32
-# assert 10'u64 < 20'i64
-
-# signed < unsigned
-assert 10'i8 < 20'u8
-assert 10'i8 < 20'u16
-assert 10'i8 < 20'u32
-# assert 10'i8 < 20'u64
-
-assert 10'i16 < 20'u8
-assert 10'i16 < 20'u16
-assert 10'i16 < 20'u32
-# assert 10'i16 < 20'u64
-
-assert 10'i32 < 20'u8
-assert 10'i32 < 20'u16
-assert 10'i32 < 20'u32
-# assert 10'i32 < 20'u64
-
-assert 10'i64 < 20'u8
-assert 10'i64 < 20'u16
-assert 10'i64 < 20'u32
-# assert 10'i64 < 20'u64
-
-# unsigned <= signed
-assert 10'u8 <= 20'i8
-assert 10'u8 <= 20'i16
-assert 10'u8 <= 20'i32
-assert 10'u8 <= 20'i64
-
-assert 10'u16 <= 20'i8
-assert 10'u16 <= 20'i16
-assert 10'u16 <= 20'i32
-assert 10'u16 <= 20'i64
-
-assert 10'u32 <= 20'i8
-assert 10'u32 <= 20'i16
-assert 10'u32 <= 20'i32
-assert 10'u32 <= 20'i64
-
-# assert 10'u64 <= 20'i8
-# assert 10'u64 <= 20'i16
-# assert 10'u64 <= 20'i32
-# assert 10'u64 <= 20'i64
-
-# signed <= unsigned
-assert 10'i8 <= 20'u8
-assert 10'i8 <= 20'u16
-assert 10'i8 <= 20'u32
-# assert 10'i8 <= 20'u64
-
-assert 10'i16 <= 20'u8
-assert 10'i16 <= 20'u16
-assert 10'i16 <= 20'u32
-# assert 10'i16 <= 20'u64
-
-assert 10'i32 <= 20'u8
-assert 10'i32 <= 20'u16
-assert 10'i32 <= 20'u32
-# assert 10'i32 <= 20'u64
-
-assert 10'i64 <= 20'u8
-assert 10'i64 <= 20'u16
-assert 10'i64 <= 20'u32
-# assert 10'i64 <= 20'u64
-
-# signed == unsigned
-assert 10'i8 == 10'u8
-assert 10'i8 == 10'u16
-assert 10'i8 == 10'u32
-# assert 10'i8 == 10'u64
-
-assert 10'i16 == 10'u8
-assert 10'i16 == 10'u16
-assert 10'i16 == 10'u32
-# assert 10'i16 == 10'u64
-
-assert 10'i32 == 10'u8
-assert 10'i32 == 10'u16
-assert 10'i32 == 10'u32
-# assert 10'i32 == 10'u64
-
-assert 10'i64 == 10'u8
-assert 10'i64 == 10'u16
-assert 10'i64 == 10'u32
-# assert 10'i64 == 10'u64
-
-# unsigned == signed
-assert 10'u8 == 10'i8
-assert 10'u8 == 10'i16
-assert 10'u8 == 10'i32
-# assert 10'u8 == 10'i64
-
-assert 10'u16 == 10'i8
-assert 10'u16 == 10'i16
-assert 10'u16 == 10'i32
-# assert 10'u16 == 10'i64
-
-assert 10'u32 == 10'i8
-assert 10'u32 == 10'i16
-assert 10'u32 == 10'i32
-# assert 10'u32 == 10'i64
-
-# assert 10'u64 == 10'i8
-# assert 10'u64 == 10'i16
-# assert 10'u64 == 10'i32
-# assert 10'u64 == 10'i64
diff --git a/tests/misc/tunsignedconv.nim b/tests/misc/tunsignedconv.nim
deleted file mode 100644
index 956e014da..000000000
--- a/tests/misc/tunsignedconv.nim
+++ /dev/null
@@ -1,41 +0,0 @@
-
-# Tests unsigned literals and implicit conversion between uints and ints
-# Passes if it compiles
-
-var h8:uint8 = 128
-var h16:uint16 = 32768
-var h32:uint32 = 2147483648'u32
-var h64:uint64 = 9223372036854775808'u64
-var foobar:uint64 = 9223372036854775813'u64 # Issue 728
-
-var v8:uint8 = 10
-var v16:uint16 = 10
-var v32:uint32 = 10
-var v64:uint64 = 10
-
-# u8 + literal produces u8:
-var a8: uint8 = v8 + 10
-var a16: uint16 = v16 + 10
-
-when false:
-  var d8  = v8 + 10'i8
-  var d16 = v8 + 10'i16
-  var d32 = v8 + 10'i32
-
-when false:
-  # these dont work yet because unsigned.nim is stupid. XXX We need to fix this.
-  var f8  = v16 + 10'u8
-  var f16 = v16 + 10'u16
-  var f32 = v16 + 10'u32
-
-  var g8  = v32 + 10'u8
-  var g16 = v32 + 10'u16
-  var g32 = v32 + 10'u32
-
-var ar: array[0..20, int]
-var n8 = ar[v8]
-var n16 = ar[v16]
-var n32 = ar[v32]
-var n64 = ar[v64]
-
-
diff --git a/tests/misc/tunsignedinc.nim b/tests/misc/tunsignedinc.nim
deleted file mode 100644
index 60c0559b0..000000000
--- a/tests/misc/tunsignedinc.nim
+++ /dev/null
@@ -1,28 +0,0 @@
-
-block: # bug #2427
-  var x = 0'u8
-  dec x # OverflowError
-  x -= 1 # OverflowError
-  x = x - 1 # No error
-
-  doAssert(x == 253'u8)
-
-block:
-  var x = 130'u8
-  x += 130'u8
-  doAssert(x == 4'u8)
-
-block:
-  var x = 40000'u16
-  x = x + 40000'u16
-  doAssert(x == 14464'u16)
-
-block:
-  var x = 4000000000'u32
-  x = x + 4000000000'u32
-  doAssert(x == 3705032704'u32)
-
-block:
-  var x = 123'u16
-  x -= 125
-  doAssert(x == 65534'u16)
diff --git a/tests/misc/tupcomingfeatures.nim b/tests/misc/tupcomingfeatures.nim
deleted file mode 100644
index d37ce85cf..000000000
--- a/tests/misc/tupcomingfeatures.nim
+++ /dev/null
@@ -1,35 +0,0 @@
-discard """
-  output: '''0 -2 0
-0 -2'''
-"""
-
-{.this: self.}
-
-type
-  Foo = object
-    a, b, x: int
-
-proc yay(self: Foo) =
-  echo a, " ", b, " ", x
-
-proc footest[T](self: var Foo, a: T) =
-  b = 1+a
-  yay()
-
-proc nongeneric(self: Foo) =
-  echo a, " ", b
-
-var ff: Foo
-footest(ff, -3)
-ff.nongeneric
-
-{.experimental.}
-using
-  c: Foo
-  x, y: int
-
-proc usesSig(c) =
-  echo "yummy"
-
-proc foobar(c, y) =
-  echo "yay"
diff --git a/tests/misc/tvarious.nim b/tests/misc/tvarious.nim
index 8124b3fc7..191107a87 100644
--- a/tests/misc/tvarious.nim
+++ b/tests/misc/tvarious.nim
@@ -1,3 +1,7 @@
+discard """
+action: compile
+"""
+
 # Test various aspects
 
 # bug #572
diff --git a/tests/misc/tvarious1.nim b/tests/misc/tvarious1.nim
index 595c77919..9c912ee8e 100644
--- a/tests/misc/tvarious1.nim
+++ b/tests/misc/tvarious1.nim
@@ -1,6 +1,6 @@
 discard """
-  file: "tlenopenarray.nim"
-  output: '''1
+output: '''
+1
 0
 Whopie
 12
@@ -46,3 +46,13 @@ echo value
 var ys = @[4.1, 5.6, 7.2, 1.7, 9.3, 4.4, 3.2]
 #var x = int(ys.high / 2) #echo ys[x] # Works
 echo ys[int(ys.high / 2)] # Doesn't work
+
+
+# bug #19680
+var here = ""
+when stderr is static:
+  doAssert false
+else:
+  here = "works"
+
+doAssert here == "works"
diff --git a/tests/misc/tvarnums.nim b/tests/misc/tvarnums.nim
index 5daa2c4b8..498099c49 100644
--- a/tests/misc/tvarnums.nim
+++ b/tests/misc/tvarnums.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tvarnums.nim"
   output: "Success!"
 """
 # Test variable length binary integers
@@ -8,7 +7,7 @@ import
   strutils
 
 type
-  TBuffer = array[0..10, int8]
+  TBuffer = array[0..10, uint8]
 
 proc toVarNum(x: int32, b: var TBuffer) =
   # encoding: first bit indicates end of number (0 if at end)
@@ -22,11 +21,11 @@ proc toVarNum(x: int32, b: var TBuffer) =
     # anyway
     a = abs(x)
   # first 6 bits:
-  b[0] = toU8(ord(a >% 63'i32) shl 7 or (ord(x < 0'i32) shl 6) or (int(a) and 63))
-  a = a shr 6'i32 # skip first 6 bits
+  b[0] = uint8(ord(a >% 63'i32) shl 7 or (ord(x < 0'i32) shl 6) or (int(a) and 63))
+  a = (a shr 6'i32) and 0x03ffffff # skip first 6 bits
   var i = 1
   while a != 0'i32:
-    b[i] = toU8(ord(a >% 127'i32) shl 7 or (int(a) and 127))
+    b[i] = uint8(ord(a >% 127'i32) shl 7 or (int(a) and 127))
     inc(i)
     a = a shr 7'i32
 
@@ -42,40 +41,40 @@ proc toVarNum64(x: int64, b: var TBuffer) =
     # anyway
     a = abs(x)
   # first 6 bits:
-  b[0] = toU8(ord(a >% 63'i64) shl 7 or (ord(x < 0'i64) shl 6) or int(a and 63))
-  a = a shr 6 # skip first 6 bits
+  b[0] = uint8(ord(a >% 63'i64) shl 7 or (ord(x < 0'i64) shl 6) or int(a and 63))
+  a = (a shr 6) and 0x03ffffffffffffff # skip first 6 bits
   var i = 1
   while a != 0'i64:
-    b[i] = toU8(ord(a >% 127'i64) shl 7 or int(a and 127))
+    b[i] = uint8(ord(a >% 127'i64) shl 7 or int(a and 127))
     inc(i)
     a = a shr 7
 
 proc toNum64(b: TBuffer): int64 =
   # treat first byte different:
-  result = ze64(b[0]) and 63
+  result = int64(b[0]) and 63
   var
     i = 0
     Shift = 6'i64
-  while (ze(b[i]) and 128) != 0:
+  while (int(b[i]) and 128) != 0:
     inc(i)
-    result = result or ((ze64(b[i]) and 127) shl Shift)
+    result = result or ((int64(b[i]) and 127) shl Shift)
     inc(Shift, 7)
-  if (ze(b[0]) and 64) != 0: # sign bit set?
+  if (int(b[0]) and 64) != 0: # sign bit set?
     result = not result +% 1
     # this is the same as ``- result``
     # but gives no overflow error for low(int)
 
 proc toNum(b: TBuffer): int32 =
   # treat first byte different:
-  result = int32 ze(b[0]) and 63
+  result = int32(b[0]) and 63
   var
     i = 0
     Shift = 6'i32
-  while (ze(b[i]) and 128) != 0:
+  while (int(b[i]) and 128) != 0:
     inc(i)
-    result = result or ((int32(ze(b[i])) and 127'i32) shl Shift)
+    result = result or ((int32(b[i]) and 127'i32) shl Shift)
     Shift = Shift + 7'i32
-  if (ze(b[0]) and (1 shl 6)) != 0: # sign bit set?
+  if (int(b[0]) and (1 shl 6)) != 0: # sign bit set?
     result = (not result) +% 1'i32
     # this is the same as ``- result``
     # but gives no overflow error for low(int)
@@ -138,5 +137,3 @@ tm(low(int32))
 tm(high(int32))
 
 writeLine(stdout, "Success!") #OUT Success!
-
-
diff --git a/tests/misc/tvcc.nim b/tests/misc/tvcc.nim
new file mode 100644
index 000000000..10533729c
--- /dev/null
+++ b/tests/misc/tvcc.nim
@@ -0,0 +1,9 @@
+discard """
+  matrix: "--cc:vcc"
+  disabled: "linux"
+  disabled: "bsd"
+  disabled: "osx"
+  disabled: "unix"
+  disabled: "posix"
+"""
+doAssert true
diff --git a/tests/misc/åäö.nim b/tests/misc/åäö.nim
index 69bb3e22c..b3caa9861 100644
--- a/tests/misc/åäö.nim
+++ b/tests/misc/åäö.nim
@@ -5,4 +5,7 @@ discard """
 # Tests that module names can contain multi byte characters
 
 let a = 1
-doAssert åäö.a == 1
\ No newline at end of file
+doAssert åäö.a == 1
+
+proc inlined() {.inline.} = discard
+inlined()
\ No newline at end of file
diff --git a/tests/mmaptest.nim b/tests/mmaptest.nim
index 84036cbf0..33010606f 100644
--- a/tests/mmaptest.nim
+++ b/tests/mmaptest.nim
@@ -1,25 +1,7 @@
 # Small test program to test for mmap() weirdnesses
 
-include "lib/system/ansi_c"
-
-const
-  PageSize = 4096
-  PROT_READ  = 1             # page can be read
-  PROT_WRITE = 2             # page can be written
-  MAP_PRIVATE = 2            # Changes are private
-
-when defined(macosx) or defined(bsd):
-  const MAP_ANONYMOUS = 0x1000
-elif defined(solaris):
-  const MAP_ANONYMOUS = 0x100
-else:
-  var
-    MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
-
-proc mmap(adr: pointer, len: int, prot, flags, fildes: cint,
-          off: int): pointer {.header: "<sys/mman.h>".}
-
-proc munmap(adr: pointer, len: int) {.header: "<sys/mman.h>".}
+import system/ansi_c
+import posix
 
 proc osAllocPages(size: int): pointer {.inline.} =
   result = mmap(nil, size, PROT_READ or PROT_WRITE,
@@ -31,18 +13,24 @@ proc osAllocPages(size: int): pointer {.inline.} =
 
 proc osDeallocPages(p: pointer, size: int) {.inline} =
   cfprintf(c_stdout, "freed pages %p..%p\n", p, cast[int](p) + size)
-  munmap(p, size-1)
+  discard munmap(p, size-1)
 
 proc `+!!`(p: pointer, size: int): pointer {.inline.} =
   result = cast[pointer](cast[int](p) + size)
 
+const
+  PageShift = when defined(nimPage256) or defined(cpu16): 8
+              elif defined(nimPage512): 9
+              elif defined(nimPage1k): 10
+              else: 12 # \ # my tests showed no improvements for using larger page sizes.
+
+  PageSize = 1 shl PageShift
+
 var p = osAllocPages(3 * PageSize)
 
 osDeallocPages(p, PageSize)
 # If this fails the OS has freed the whole block starting at 'p':
-echo(cast[ptr int](p +!! (pageSize*2))[])
+echo(cast[ptr int](p +!! (PageSize*2))[])
 
 osDeallocPages(p +!! PageSize*2, PageSize)
 osDeallocPages(p +!! PageSize, PageSize)
-
-
diff --git a/tests/modules/a/module_name_clashes.nim b/tests/modules/a/module_name_clashes.nim
new file mode 100644
index 000000000..209526e22
--- /dev/null
+++ b/tests/modules/a/module_name_clashes.nim
@@ -0,0 +1,8 @@
+# See `tmodule_name_clashes`
+
+import ../b/module_name_clashes
+type A* = object
+  b*: B
+
+proc print*(a: A) =
+  echo repr a
diff --git a/tests/modules/a/utils.nim b/tests/modules/a/utils.nim
new file mode 100644
index 000000000..f37abfb93
--- /dev/null
+++ b/tests/modules/a/utils.nim
@@ -0,0 +1,2 @@
+proc burnMem*(a: int) =
+  discard
diff --git a/tests/modules/b/module_name_clashes.nim b/tests/modules/b/module_name_clashes.nim
new file mode 100644
index 000000000..6a10cac33
--- /dev/null
+++ b/tests/modules/b/module_name_clashes.nim
@@ -0,0 +1,3 @@
+# See `tmodule_name_clashes`
+
+type B* = object
diff --git a/tests/modules/b/utils.nim b/tests/modules/b/utils.nim
new file mode 100644
index 000000000..e343385f5
--- /dev/null
+++ b/tests/modules/b/utils.nim
@@ -0,0 +1,2 @@
+# module b/utils.nim
+let x* = 10
diff --git a/tests/modules/m9627/a.nim b/tests/modules/m9627/a.nim
new file mode 100644
index 000000000..0dd32430e
--- /dev/null
+++ b/tests/modules/m9627/a.nim
@@ -0,0 +1 @@
+var a = 10
diff --git a/tests/modules/m9627/b.nim b/tests/modules/m9627/b.nim
new file mode 100644
index 000000000..2806a78ed
--- /dev/null
+++ b/tests/modules/m9627/b.nim
@@ -0,0 +1 @@
+var b = 9
diff --git a/tests/modules/mforwarded_pure_enum.nim b/tests/modules/mforwarded_pure_enum.nim
new file mode 100644
index 000000000..3f03390a5
--- /dev/null
+++ b/tests/modules/mforwarded_pure_enum.nim
@@ -0,0 +1,3 @@
+
+import mforwarded_pure_enum2
+export mforwarded_pure_enum2.PureEnum
diff --git a/tests/modules/mforwarded_pure_enum2.nim b/tests/modules/mforwarded_pure_enum2.nim
new file mode 100644
index 000000000..e5d5d2a71
--- /dev/null
+++ b/tests/modules/mforwarded_pure_enum2.nim
@@ -0,0 +1,4 @@
+
+type
+  PureEnum* {.pure.} = enum
+    x, y, z
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/mincludeprefix.nim b/tests/modules/mincludeprefix.nim
new file mode 100644
index 000000000..6d557a430
--- /dev/null
+++ b/tests/modules/mincludeprefix.nim
@@ -0,0 +1 @@
+const bar = 456
diff --git a/tests/modules/mincludetemplate.nim b/tests/modules/mincludetemplate.nim
new file mode 100644
index 000000000..febe9bfcf
--- /dev/null
+++ b/tests/modules/mincludetemplate.nim
@@ -0,0 +1 @@
+const foo = 123
diff --git a/tests/modules/mmodule_same_proc.nim b/tests/modules/mmodule_same_proc.nim
new file mode 100644
index 000000000..5ce56ec11
--- /dev/null
+++ b/tests/modules/mmodule_same_proc.nim
@@ -0,0 +1,6 @@
+
+# the module being the same name as the proc
+# is a requirement to trigger the error
+import mmodule_same_proc_client
+
+proc bar*[T](foo: T): bool = foo.mmodule_same_proc_client()
diff --git a/tests/modules/mmodule_same_proc_client.nim b/tests/modules/mmodule_same_proc_client.nim
new file mode 100644
index 000000000..e36ec42cf
--- /dev/null
+++ b/tests/modules/mmodule_same_proc_client.nim
@@ -0,0 +1,2 @@
+
+proc mmodule_same_proc_client*(x: string): bool = x.len > 0
diff --git a/tests/modules/mnotuniquename.nim b/tests/modules/mnotuniquename.nim
index e69de29bb..54d5883cd 100644
--- a/tests/modules/mnotuniquename.nim
+++ b/tests/modules/mnotuniquename.nim
@@ -0,0 +1 @@
+proc flat*() = echo "flat"
diff --git a/tests/modules/morder_depa.nim b/tests/modules/morder_depa.nim
new file mode 100644
index 000000000..846fb1441
--- /dev/null
+++ b/tests/modules/morder_depa.nim
@@ -0,0 +1,5 @@
+
+import morder_depb
+
+proc Foo*(x: int): Foo = discard
+
diff --git a/tests/modules/morder_depb.nim b/tests/modules/morder_depb.nim
new file mode 100644
index 000000000..b77bc5acb
--- /dev/null
+++ b/tests/modules/morder_depb.nim
@@ -0,0 +1 @@
+type Foo* = array[2, byte]
diff --git a/tests/modules/seq.nim b/tests/modules/seq.nim
new file mode 100644
index 000000000..176e44025
--- /dev/null
+++ b/tests/modules/seq.nim
@@ -0,0 +1,5 @@
+var seq: seq[int]
+var b: seq[float]
+
+echo seq
+echo b
diff --git a/tests/modules/t8665.nim b/tests/modules/t8665.nim
new file mode 100644
index 000000000..74d31452f
--- /dev/null
+++ b/tests/modules/t8665.nim
@@ -0,0 +1,5 @@
+discard """
+  action: compile
+"""
+
+import treorder
diff --git a/tests/modules/t9627.nim b/tests/modules/t9627.nim
new file mode 100644
index 000000000..daba46374
--- /dev/null
+++ b/tests/modules/t9627.nim
@@ -0,0 +1,7 @@
+discard """
+  output: "109"
+"""
+
+include m9627 / [a, b]
+
+echo a, b
diff --git a/tests/modules/tambig_range.nim b/tests/modules/tambig_range.nim
index 010350521..e1ecc0013 100644
--- a/tests/modules/tambig_range.nim
+++ b/tests/modules/tambig_range.nim
@@ -1,6 +1,6 @@
 discard """
-  errormsg: "ambiguous identifier: 'range' --use system.range or mrange.range"
-  line: 13
+  errormsg: "ambiguous identifier: 'range' -- use one of the following:"
+  line: "13"
 """
 
 import mrange
diff --git a/tests/modules/texplicit_system_import.nim b/tests/modules/texplicit_system_import.nim
index bc4d018bf..0a4cedc71 100644
--- a/tests/modules/texplicit_system_import.nim
+++ b/tests/modules/texplicit_system_import.nim
@@ -1,9 +1,9 @@
-##.
 import system except `+`
+
 discard """
   errormsg: "undeclared identifier: '+'"
   line: 9
 """
-# Testament requires that the initial """ occurs before the 40th byte
-# in the file. No kidding...
+
+
 echo 4+5
diff --git a/tests/modules/texport2.nim b/tests/modules/texport2.nim
index 6e55873c5..e90c58673 100644
--- a/tests/modules/texport2.nim
+++ b/tests/modules/texport2.nim
@@ -1,9 +1,16 @@
+discard """
+output: '''
+abc
+xyz
+B.foo
+'''
+"""
+
 # bug #1595, #1612
 
 import mexport2a
 
 proc main() =
-  echo "Import Test, two lines should follow. One with abc and one with xyz."
   printAbc()
   printXyz()
 
diff --git a/tests/modules/tfowarded_pure_enum.nim b/tests/modules/tfowarded_pure_enum.nim
new file mode 100644
index 000000000..1d2c4f342
--- /dev/null
+++ b/tests/modules/tfowarded_pure_enum.nim
@@ -0,0 +1,7 @@
+discard """
+  output: '''z'''
+"""
+
+import mforwarded_pure_enum as t2
+
+echo z
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/timportas.nim b/tests/modules/timportas.nim
new file mode 100644
index 000000000..179613c6b
--- /dev/null
+++ b/tests/modules/timportas.nim
@@ -0,0 +1,21 @@
+discard """
+    action: run
+"""
+
+import .. / modules / [mexporta as a1, definitions as foo1]
+import .. / modules / definitions as foo2
+import ./[mexporta as a2, definitions as foo3]
+import std / times as bar
+from times as bar2 import nil
+import times as bar3 except convert
+import definitions as baz
+
+discard foo1.v
+discard foo2.v
+discard foo3.v
+discard bar.now()
+discard bar2.now()
+discard bar3.now()
+discard baz.v
+discard a1.xyz
+discard a2.xyz
diff --git a/tests/modules/timportexcept.nim b/tests/modules/timportexcept.nim
index 93a7fd642..40b748088 100644
--- a/tests/modules/timportexcept.nim
+++ b/tests/modules/timportexcept.nim
@@ -1,10 +1,9 @@
 discard """
-  line: 9
   errormsg: "undeclared identifier: '%'"
+  line: 9
 """
 
 import strutils except `%`
 
 # doesn't work
 echo "$1" % "abc"
-
diff --git a/tests/modules/tincludeas.nim b/tests/modules/tincludeas.nim
new file mode 100644
index 000000000..b82e38b14
--- /dev/null
+++ b/tests/modules/tincludeas.nim
@@ -0,0 +1,6 @@
+discard """
+  errormsg: "Cannot use 'as' in 'include'."
+  line: 6
+"""
+
+include foobar as foo
diff --git a/tests/modules/tincludeprefix.nim b/tests/modules/tincludeprefix.nim
new file mode 100644
index 000000000..d45a6eff3
--- /dev/null
+++ b/tests/modules/tincludeprefix.nim
@@ -0,0 +1,3 @@
+include ./[mincludeprefix, mincludetemplate]
+doAssert foo == 123
+doAssert bar == 456
diff --git a/tests/modules/tincludetemplate.nim b/tests/modules/tincludetemplate.nim
new file mode 100644
index 000000000..77e409ee5
--- /dev/null
+++ b/tests/modules/tincludetemplate.nim
@@ -0,0 +1,5 @@
+# issue #12539
+
+template includePath(n: untyped) = include ../modules/n # But `include n` works
+includePath(mincludetemplate)
+doAssert foo == 123
diff --git a/tests/modules/tmismatchedvisibility.nim b/tests/modules/tmismatchedvisibility.nim
index 2e8636d1e..b649a5a3e 100644
--- a/tests/modules/tmismatchedvisibility.nim
+++ b/tests/modules/tmismatchedvisibility.nim
@@ -1,6 +1,6 @@
 discard """
+  errormsg: "public implementation 'tmismatchedvisibility.foo(a: int)' has non-public forward declaration at "
   line: 8
-  errormsg: "public implementation 'tmismatchedvisibility.foo(a: int)[declared in tmismatchedvisibility.nim(6, 5)]' has non-public forward declaration in "
 """
 
 proc foo(a: int): int
diff --git a/tests/modules/tmodule_name_clashes.nim b/tests/modules/tmodule_name_clashes.nim
new file mode 100644
index 000000000..814d5d152
--- /dev/null
+++ b/tests/modules/tmodule_name_clashes.nim
@@ -0,0 +1,17 @@
+discard """
+matrix: "--mm:refc"
+targets: "c"
+ccodecheck: "\\i @('atmaatsmodule_name_clashesdotnim_DatInit000')"
+ccodecheck: "\\i @('atmbatsmodule_name_clashesdotnim_DatInit000')"
+joinable: false
+"""
+
+# Test module name clashes within same package.
+# This was created to test that module symbol mangling functioned correctly
+# for the C backend when there are one or more modules with the same name in
+# a package, and more than one of them require module initialization procs.
+# I'm not sure of the simplest method to cause the init procs to be generated.
+
+import a/module_name_clashes
+
+print A()
diff --git a/tests/modules/tmodule_same_proc.nim b/tests/modules/tmodule_same_proc.nim
new file mode 100644
index 000000000..dc4dfd3d6
--- /dev/null
+++ b/tests/modules/tmodule_same_proc.nim
@@ -0,0 +1,9 @@
+
+import mmodule_same_proc
+
+# importing baz causes the error not to trigger
+#import baz
+
+# bug #11188
+
+discard "foo".bar()
diff --git a/tests/modules/tmodulesymtype.nim b/tests/modules/tmodulesymtype.nim
new file mode 100644
index 000000000..d17c4cca4
--- /dev/null
+++ b/tests/modules/tmodulesymtype.nim
@@ -0,0 +1,22 @@
+discard """
+cmd: "nim check $file"
+"""
+
+# bug #19225
+import std/sequtils
+sequtils #[tt.Error
+^ expression has no type: sequtils]#
+proc foo() =
+  block: #[tt.Error
+  ^ expression has no type: block:
+  sequtils]#
+    sequtils
+
+foo()
+
+# issue #23399
+when isMainModule:
+  sequtils #[tt.Error
+  ^ expression has no type: sequtils]#
+
+discard
diff --git a/tests/modules/tnamspc.nim b/tests/modules/tnamspc.nim
index 2f488644c..93ce71568 100644
--- a/tests/modules/tnamspc.nim
+++ b/tests/modules/tnamspc.nim
@@ -1,12 +1,10 @@
 discard """
+  errormsg: "undeclared identifier: \'global\'"
   file: "tnamspc.nim"
   line: 10
-  errormsg: "undeclared identifier: \'global\'"
 """
 # Test17 - test correct handling of namespaces
 
 import mnamspc1
 
 global = 9 #ERROR
-
-
diff --git a/tests/modules/tnotuniquename.nim b/tests/modules/tnotuniquename.nim
index 2d8ce4869..bc401e662 100644
--- a/tests/modules/tnotuniquename.nim
+++ b/tests/modules/tnotuniquename.nim
@@ -1,7 +1,10 @@
 discard """
-  file: "tnotuniquename/mnotuniquename.nim"
-  errormsg: "module names need to be unique per Nimble package"
+  output: '''nested
+flat'''
 """
 
 import mnotuniquename
-import tnotuniquename/mnotuniquename as nun
+import tnotuniquename_dir/mnotuniquename as nun
+
+nested()
+flat()
diff --git a/tests/modules/tnotuniquename2.nim b/tests/modules/tnotuniquename2.nim
index 8e486d19f..e4501bc24 100644
--- a/tests/modules/tnotuniquename2.nim
+++ b/tests/modules/tnotuniquename2.nim
@@ -1,6 +1,7 @@
 discard """
-  file: "tnotuniquename/mnotuniquename.nim"
   errormsg: "module names need to be unique per Nimble package"
+  file: "tnotuniquename/mnotuniquename.nim"
+  disabled: "true"
 """
 
 import mnotuniquename
diff --git a/tests/modules/tnotuniquename_dir/mnotuniquename.nim b/tests/modules/tnotuniquename_dir/mnotuniquename.nim
new file mode 100644
index 000000000..11e52d9d0
--- /dev/null
+++ b/tests/modules/tnotuniquename_dir/mnotuniquename.nim
@@ -0,0 +1,2 @@
+
+proc nested*() = echo "nested"
diff --git a/tests/modules/topaque.nim b/tests/modules/topaque.nim
index 84e2388bc..94ff8ff25 100644
--- a/tests/modules/topaque.nim
+++ b/tests/modules/topaque.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "undeclared field: \'buffer\'"
   file: "topaque.nim"
   line: 16
-  errormsg: "undeclared field: \'buffer\'"
 """
 # Test the new opaque types
 
@@ -14,5 +14,3 @@ var
 L.filename = "ha"
 L.line = 34
 L.buffer[0] = '\0' #ERROR_MSG undeclared field: 'buffer'
-
-
diff --git a/tests/modules/torder_dep.nim b/tests/modules/torder_dep.nim
new file mode 100644
index 000000000..85211228a
--- /dev/null
+++ b/tests/modules/torder_dep.nim
@@ -0,0 +1,10 @@
+discard """
+  output: '''[0, 0]'''
+"""
+
+import morder_depb
+import morder_depa
+
+# bug #11187
+
+echo Foo(3)
diff --git a/tests/modules/trecinca.nim b/tests/modules/trecinca.nim
index 7a74d7a46..56798dedd 100644
--- a/tests/modules/trecinca.nim
+++ b/tests/modules/trecinca.nim
@@ -1,12 +1,10 @@
 discard """
+  errormsg: "recursive dependency: 'trecincb.nim'"
   file: "trecincb.nim"
   line: 9
-  errormsg: "recursive dependency: 'trecincb.nim'"
 """
 # Test recursive includes
 
 include trecincb
 
 echo "trecina"
-
-
diff --git a/tests/modules/trecincb.nim b/tests/modules/trecincb.nim
index 1d3eb5503..30a5d7800 100644
--- a/tests/modules/trecincb.nim
+++ b/tests/modules/trecincb.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "recursive dependency: 'trecincb.nim'"
   file: "trecincb.nim"
   line: 9
-  errormsg: "recursive dependency: 'trecincb.nim'"
 """
 # Test recursive includes
 
@@ -9,5 +9,3 @@ discard """
 include trecincb
 
 echo "trecinb"
-
-
diff --git a/tests/modules/trecmod.nim b/tests/modules/trecmod.nim
index 5f053bcae..43e510e87 100644
--- a/tests/modules/trecmod.nim
+++ b/tests/modules/trecmod.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "recursive module dependency detected"
   file: "mrecmod.nim"
   line: 1
-  errormsg: "recursive module dependency detected"
   disabled: true
 """
 # recursive module
diff --git a/tests/modules/treorder.nim b/tests/modules/treorder.nim
index 8715e4548..ff0b2e071 100644
--- a/tests/modules/treorder.nim
+++ b/tests/modules/treorder.nim
@@ -1,13 +1,14 @@
 discard """
-  cmd: "nim -d:testdef $target $file"
+  matrix: "-d:testdef"
   output: '''works 34
 34
 defined
 3'''
 """
 
-{.reorder: on.}
-{.experimental.}
+{.experimental: "codeReordering".}
+
+{.push callconv: stdcall.}
 
 proc bar(x: T)
 
@@ -42,3 +43,5 @@ using
   my, omy: int
 
 goo(3, 4)
+
+{.pop.}
diff --git a/tests/modules/tselfimport.nim b/tests/modules/tselfimport.nim
index ddb3a5b09..ba5d9b4cf 100644
--- a/tests/modules/tselfimport.nim
+++ b/tests/modules/tselfimport.nim
@@ -1,9 +1,8 @@
 discard """
+  errormsg: "module 'tselfimport' cannot import itself"
   file: "tselfimport.nim"
   line: 7
-  errormsg: "A module cannot import itself"
 """
 import strutils as su # guard against regression
 import tselfimport #ERROR
 echo("Hello World")
-
diff --git a/tests/modules/tseq.nim b/tests/modules/tseq.nim
new file mode 100644
index 000000000..22ee48f42
--- /dev/null
+++ b/tests/modules/tseq.nim
@@ -0,0 +1,8 @@
+discard """
+  joinable: false
+  output: '''@[]
+@[]
+'''
+"""
+
+import seq
diff --git a/tests/modules/tstrutils_insert_sep.nim b/tests/modules/tstrutils_insert_sep.nim
new file mode 100644
index 000000000..775fe7da1
--- /dev/null
+++ b/tests/modules/tstrutils_insert_sep.nim
@@ -0,0 +1,13 @@
+discard """
+  output: '''
+-100
+-100,000
+100,000
+'''
+"""
+# test https://github.com/nim-lang/Nim/issues/11352
+
+import strutils
+echo insertSep($(-100), ',')
+echo insertSep($(-100_000), ',')
+echo insertSep($(100_000), ',')
\ No newline at end of file
diff --git a/tests/modules/tutils_ab.nim b/tests/modules/tutils_ab.nim
new file mode 100644
index 000000000..25bd08f3c
--- /dev/null
+++ b/tests/modules/tutils_ab.nim
@@ -0,0 +1,5 @@
+import a/utils as autils, b/utils
+
+# bug #12420
+
+burnMem(x)
diff --git a/tests/msgs/mdeprecated3.nim b/tests/msgs/mdeprecated3.nim
new file mode 100644
index 000000000..0a9c6e37d
--- /dev/null
+++ b/tests/msgs/mdeprecated3.nim
@@ -0,0 +1,10 @@
+type
+  Ty* {.deprecated.} = uint32
+  Ty1* {.deprecated: "hello".} = uint32
+
+var aVar* {.deprecated.}: char
+
+proc aProc*() {.deprecated.} = discard
+proc aProc1*() {.deprecated: "hello".} = discard
+
+{.deprecated: "goodbye".}
diff --git a/tests/msgs/mspellsuggest.nim b/tests/msgs/mspellsuggest.nim
new file mode 100644
index 000000000..ad449554f
--- /dev/null
+++ b/tests/msgs/mspellsuggest.nim
@@ -0,0 +1,7 @@
+proc fooBar4*(a: int) = discard
+var fooBar9* = 0
+
+var fooCar* = 0
+type FooBar* = int
+type FooCar* = int
+type GooBa* = int
diff --git a/tests/msgs/mused2a.nim b/tests/msgs/mused2a.nim
new file mode 100644
index 000000000..d9b2bb9bf
--- /dev/null
+++ b/tests/msgs/mused2a.nim
@@ -0,0 +1,23 @@
+import std/strutils
+
+from std/os import fileExists
+
+import std/typetraits as typetraits2
+from std/setutils import complement
+
+
+
+
+
+proc fn1() = discard
+proc fn2*() = discard
+
+
+let fn4 = 0
+let fn5* = 0
+
+
+const fn7 = 0
+const fn8* = 0
+
+type T1 = object
diff --git a/tests/msgs/mused2b.nim b/tests/msgs/mused2b.nim
new file mode 100644
index 000000000..39c92b964
--- /dev/null
+++ b/tests/msgs/mused2b.nim
@@ -0,0 +1,3 @@
+import mused2c
+export mused2c
+
diff --git a/tests/msgs/mused2c.nim b/tests/msgs/mused2c.nim
new file mode 100644
index 000000000..a374e634e
--- /dev/null
+++ b/tests/msgs/mused2c.nim
@@ -0,0 +1 @@
+proc baz*() = discard
\ No newline at end of file
diff --git a/tests/msgs/mused3.nim b/tests/msgs/mused3.nim
new file mode 100644
index 000000000..0beec1d44
--- /dev/null
+++ b/tests/msgs/mused3.nim
@@ -0,0 +1,76 @@
+#[
+ran from trunner
+]#
+
+
+
+
+
+
+# line 10
+when defined case1:
+  from mused3a import nil
+  from mused3b import nil
+  mused3a.fn1()
+
+when defined case2:
+  from mused3a as m1 import nil
+  m1.fn1()
+
+when defined case3:
+  from mused3a import fn1
+  fn1()
+
+when defined case4:
+  from mused3a as m1 import fn1
+  m1.fn1()
+
+when defined case5:
+  import mused3a as m1
+  fn1()
+
+when defined case6:
+  import mused3a except nonexistent
+  fn1()
+
+when defined case7:
+  import mused3a
+  mused3a.fn1()
+
+when defined case8:
+  # re-export test
+  import mused3a except nonexistent
+  gn1()
+
+when defined case9:
+  # re-export test
+  import mused3a
+  gn1()
+
+when defined case10:
+  #[
+  edge case which happens a lot in compiler code:
+  don't report UnusedImport for mused3b here even though it works without `import mused3b`,
+  because `a.b0.f0` is accessible from both mused3a and mused3b (fields are given implicit access)
+  ]#
+  import mused3a
+  import mused3b
+  var a: Bar
+  discard a.b0.f0
+
+when false:
+  when defined case11:
+    #[
+    xxx minor bug: this should give:
+    Warning: imported and not used: 'm2' [UnusedImport]
+    but doesn't, because currently implementation in `markOwnerModuleAsUsed`
+    only looks at `fn1`, not fully qualified call `m1.fn1()
+    ]#
+    from mused3a as m1 import nil
+    from mused3a as m2 import nil
+    m1.fn1()
+
+when defined case12:
+  import mused3a
+  import mused3a
+  fn1()
diff --git a/tests/msgs/mused3a.nim b/tests/msgs/mused3a.nim
new file mode 100644
index 000000000..c33d1ecb3
--- /dev/null
+++ b/tests/msgs/mused3a.nim
@@ -0,0 +1,41 @@
+when defined case1:
+  proc fn1*() = discard
+
+when defined case2:
+  proc fn1*() = discard
+
+when defined case3:
+  proc fn1*() = discard
+  proc fn2*() = discard
+
+when defined case4:
+  proc fn1*() = discard
+  proc fn2*() = discard
+
+when defined case5:
+  proc fn1*() = discard
+
+when defined case6:
+  proc fn1*() = discard
+
+when defined case7:
+  proc fn1*() = discard
+
+when defined case8:
+  import mused3b
+  export mused3b
+
+when defined case9:
+  import mused3b
+  export mused3b
+
+when defined case10:
+  import mused3b
+  type Bar* = object
+    b0*: Foo
+
+when defined case11:
+  proc fn1*() = discard
+
+when defined case12:
+  proc fn1*() = discard
diff --git a/tests/msgs/mused3b.nim b/tests/msgs/mused3b.nim
new file mode 100644
index 000000000..de288bb08
--- /dev/null
+++ b/tests/msgs/mused3b.nim
@@ -0,0 +1,12 @@
+when defined case1:
+  proc gn1*()=discard
+
+when defined case8:
+  proc gn1*()=discard
+
+when defined case9:
+  proc gn1*()=discard
+
+when defined case10:
+  type Foo* = object
+    f0*: int
diff --git a/tests/msgs/tdeprecated1.nim b/tests/msgs/tdeprecated1.nim
new file mode 100644
index 000000000..f4e85da0b
--- /dev/null
+++ b/tests/msgs/tdeprecated1.nim
@@ -0,0 +1,15 @@
+let foo* {.deprecated: "abcd".} = 42
+var foo1* {.deprecated: "efgh".} = 42
+foo1 = foo #[tt.Warning
+^ efgh; foo1 is deprecated [Deprecated]; tt.Warning
+       ^ abcd; foo is deprecated [Deprecated]]#
+
+proc hello[T](a: T) {.deprecated: "Deprecated since v1.2.0, use 'HelloZ'".} =
+  discard
+
+hello[int](12) #[tt.Warning
+^ Deprecated since v1.2.0, use 'HelloZ'; hello is deprecated [Deprecated]]#
+
+const foo2* {.deprecated: "abcd".} = 42
+discard foo2 #[tt.Warning
+        ^ abcd; foo2 is deprecated [Deprecated]]#
diff --git a/tests/msgs/tdeprecated2.nim b/tests/msgs/tdeprecated2.nim
new file mode 100644
index 000000000..71d20081e
--- /dev/null
+++ b/tests/msgs/tdeprecated2.nim
@@ -0,0 +1,42 @@
+discard """
+  nimout: '''
+tdeprecated2.nim(23, 3) Warning: a is deprecated [Deprecated]
+tdeprecated2.nim(30, 11) Warning: asdf; enum 'Foo' which contains field 'a' is deprecated [Deprecated]
+tdeprecated2.nim(40, 16) Warning: use fooX instead; fooA is deprecated [Deprecated]
+end
+'''
+"""
+
+
+
+
+
+
+## line 15
+
+
+
+block:
+  var
+    a {.deprecated.}: array[0..11, int]
+
+  a[8] = 1
+
+block t10111:
+  type
+    Foo {.deprecated: "asdf" .} = enum
+      a 
+  
+  var _ = a
+  
+
+block: # issue #8063
+  type
+    Foo = enum
+      fooX
+
+  const fooA {.deprecated: "use fooX instead".} = fooX
+  let
+    foo: Foo = fooA
+  echo foo
+  static: echo "end"
diff --git a/tests/msgs/tdeprecated3.nim b/tests/msgs/tdeprecated3.nim
new file mode 100644
index 000000000..0c1b7deff
--- /dev/null
+++ b/tests/msgs/tdeprecated3.nim
@@ -0,0 +1,33 @@
+discard """
+  matrix: "--hint:all:off"
+  nimoutFull: true
+  nimout: '''
+tdeprecated3.nim(21, 8) Warning: goodbye; mdeprecated3 is deprecated [Deprecated]
+tdeprecated3.nim(24, 10) Warning: Ty is deprecated [Deprecated]
+tdeprecated3.nim(27, 10) Warning: hello; Ty1 is deprecated [Deprecated]
+tdeprecated3.nim(30, 8) Warning: aVar is deprecated [Deprecated]
+tdeprecated3.nim(32, 3) Warning: aProc is deprecated [Deprecated]
+tdeprecated3.nim(33, 3) Warning: hello; aProc1 is deprecated [Deprecated]
+'''
+"""
+
+
+
+
+
+
+
+# line 20
+import mdeprecated3
+
+block:
+  var z: Ty
+  z = 0
+block:
+  var z: Ty1
+  z = 0
+block:
+  echo aVar
+block:
+  aProc()
+  aProc1()
diff --git a/tests/msgs/tdeprecatedequalhook.nim b/tests/msgs/tdeprecatedequalhook.nim
new file mode 100644
index 000000000..79ee835f8
--- /dev/null
+++ b/tests/msgs/tdeprecatedequalhook.nim
@@ -0,0 +1,11 @@
+discard """
+  errormsg: "Overriding `=` hook is deprecated; Override `=copy` hook instead"
+  matrix: "--warningAsError[Deprecated]:on"
+"""
+
+type
+  SharedString = object
+    data: string
+
+proc `=`(x: var SharedString, y: SharedString) =
+  discard
\ No newline at end of file
diff --git a/tests/msgs/texpandmacro.nim b/tests/msgs/texpandmacro.nim
new file mode 100644
index 000000000..fea8b571f
--- /dev/null
+++ b/tests/msgs/texpandmacro.nim
@@ -0,0 +1,18 @@
+discard """
+  cmd: "nim c --expandMacro:foo $file"
+  nimout: '''texpandmacro.nim(17, 1) Hint: expanded macro:
+echo ["injected echo"]
+var x = 4 [ExpandMacro]
+'''
+  output: '''injected echo'''
+"""
+
+import macros
+
+macro foo(x: untyped): untyped =
+  result = quote do:
+    echo "injected echo"
+    `x`
+
+foo:
+  var x = 4
diff --git a/tests/msgs/thints_off.nim b/tests/msgs/thints_off.nim
new file mode 100644
index 000000000..5a4cadad6
--- /dev/null
+++ b/tests/msgs/thints_off.nim
@@ -0,0 +1,4 @@
+discard """
+  matrix: "--hints:off"
+"""
+doAssert true
diff --git a/tests/msgs/tspellsuggest.nim b/tests/msgs/tspellsuggest.nim
new file mode 100644
index 000000000..ea0a98cd3
--- /dev/null
+++ b/tests/msgs/tspellsuggest.nim
@@ -0,0 +1,45 @@
+discard """
+  matrix: "--spellsuggest:15 --hints:off"
+  action: "reject"
+  nimout: '''
+tspellsuggest.nim(45, 13) Error: undeclared identifier: 'fooBar'
+candidates (edit distance, scope distance); see '--spellSuggest':
+ (1, 0): 'fooBar8'
+ (1, 1): 'fooBar7'
+ (1, 3): 'fooBar1'
+ (1, 3): 'fooBar2'
+ (1, 3): 'fooBar3'
+ (1, 3): 'fooBar4'
+ (1, 3): 'fooBar5'
+ (1, 3): 'fooBar6'
+ (1, 5): 'FooBar'
+ (1, 5): 'fooBar4'
+ (1, 5): 'fooBar9'
+ (1, 5): 'fooCar'
+ (2, 5): 'FooCar'
+ (2, 5): 'GooBa'
+ (3, 0): 'fooBarBaz'
+'''
+"""
+
+# tests `--spellsuggest:num`
+
+
+
+
+# line 30
+import ./mspellsuggest
+
+var fooBar1 = 0
+let fooBar2 = 0
+const fooBar3 = 0
+proc fooBar4() = discard
+template fooBar5() = discard
+macro fooBar6() = discard
+
+proc main =
+  var fooBar7 = 0
+  block:
+    var fooBar8 = 0
+    const fooBarBaz = 0
+    let x = fooBar
diff --git a/tests/msgs/tspellsuggest2.nim b/tests/msgs/tspellsuggest2.nim
new file mode 100644
index 000000000..4bf05799e
--- /dev/null
+++ b/tests/msgs/tspellsuggest2.nim
@@ -0,0 +1,45 @@
+discard """
+  matrix: "--spellsuggest:12 --hints:off"
+  action: "reject"
+  nimout: '''
+tspellsuggest2.nim(45, 13) Error: undeclared identifier: 'fooBar'
+candidates (edit distance, scope distance); see '--spellSuggest':
+ (1, 0): 'fooBar8'
+ (1, 1): 'fooBar7'
+ (1, 3): 'fooBar1'
+ (1, 3): 'fooBar2'
+ (1, 3): 'fooBar3'
+ (1, 3): 'fooBar4'
+ (1, 3): 'fooBar5'
+ (1, 3): 'fooBar6'
+ (1, 5): 'FooBar'
+ (1, 5): 'fooBar4'
+ (1, 5): 'fooBar9'
+ (1, 5): 'fooCar'
+'''
+"""
+
+# tests `--spellsuggest`
+
+
+
+
+
+
+
+# line 30
+import ./mspellsuggest
+
+var fooBar1 = 0
+let fooBar2 = 0
+const fooBar3 = 0
+proc fooBar4() = discard
+template fooBar5() = discard
+macro fooBar6() = discard
+
+proc main =
+  var fooBar7 = 0
+  block:
+    var fooBar8 = 0
+    const fooBarBaz = 0
+    let x = fooBar
diff --git a/tests/msgs/tspellsuggest3.nim b/tests/msgs/tspellsuggest3.nim
new file mode 100644
index 000000000..bd4d5256f
--- /dev/null
+++ b/tests/msgs/tspellsuggest3.nim
@@ -0,0 +1,21 @@
+discard """
+  matrix: "--spellsuggest:4 --hints:off"
+  action: "reject"
+  nimout: '''
+tspellsuggest3.nim(21, 1) Error: undeclared identifier: 'fooBar'
+candidates (edit distance, scope distance); see '--spellSuggest':
+ (1, 2): 'FooBar'
+ (1, 2): 'fooBar4'
+ (1, 2): 'fooBar9'
+ (1, 2): 'fooCar'
+'''
+"""
+
+
+import ./mspellsuggest
+import ./mspellsuggest
+import ./mspellsuggest
+import ./mspellsuggest
+
+
+fooBar
diff --git a/tests/msgs/tused2.nim b/tests/msgs/tused2.nim
new file mode 100644
index 000000000..5ccda7737
--- /dev/null
+++ b/tests/msgs/tused2.nim
@@ -0,0 +1,46 @@
+discard """
+  matrix: "--hint:all:off --hint:XDeclaredButNotUsed --path:."
+  joinable: false
+  nimoutFull: true
+  nimout: '''
+mused2a.nim(12, 6) Hint: 'fn1' is declared but not used [XDeclaredButNotUsed]
+mused2a.nim(16, 5) Hint: 'fn4' is declared but not used [XDeclaredButNotUsed]
+mused2a.nim(20, 7) Hint: 'fn7' is declared but not used [XDeclaredButNotUsed]
+mused2a.nim(23, 6) Hint: 'T1' is declared but not used [XDeclaredButNotUsed]
+mused2a.nim(1, 12) Warning: imported and not used: 'strutils' [UnusedImport]
+mused2a.nim(3, 10) Warning: imported and not used: 'os' [UnusedImport]
+mused2a.nim(5, 23) Warning: imported and not used: 'typetraits2' [UnusedImport]
+mused2a.nim(6, 10) Warning: imported and not used: 'setutils' [UnusedImport]
+tused2.nim(42, 8) Warning: imported and not used: 'mused2a' [UnusedImport]
+tused2.nim(45, 12) Warning: imported and not used: 'strutils' [UnusedImport]
+'''
+"""
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# line 40
+
+import mused2a
+import mused2b
+
+import std/strutils
+baz()
diff --git a/tests/msgs/twarningaserror.nim b/tests/msgs/twarningaserror.nim
new file mode 100644
index 000000000..6f7b76095
--- /dev/null
+++ b/tests/msgs/twarningaserror.nim
@@ -0,0 +1,35 @@
+discard """
+  joinable: false
+"""
+
+#[
+tests: hintAsError, warningAsError
+]#
+
+template fn1 =
+  {.hintAsError[ConvFromXtoItselfNotNeeded]:on.}
+  proc fn(a: string) = discard a.string
+  {.hintAsError[ConvFromXtoItselfNotNeeded]:off.}
+
+template fn2 =
+  {.hintAsError[ConvFromXtoItselfNotNeeded]:on.}
+  proc fn(a: string) = discard a
+  {.hintAsError[ConvFromXtoItselfNotNeeded]:off.}
+
+template gn1 =
+  {.warningAsError[ProveInit]:on.}
+  proc fn(): var int = discard
+  discard fn()
+  {.warningAsError[ProveInit]:off.}
+
+template gn2 =
+  {.warningAsError[ProveInit]:on.}
+  proc fn(): int = discard
+  discard fn()
+  {.warningAsError[ProveInit]:off.}
+
+doAssert not compiles(fn1())
+doAssert compiles(fn2())
+
+doAssert not compiles(gn1())
+doAssert compiles(gn2())
diff --git a/tests/namedparams/tnamedparams2.nim b/tests/namedparams/tnamedparams2.nim
deleted file mode 100644
index fcbbd32da..000000000
--- a/tests/namedparams/tnamedparams2.nim
+++ /dev/null
@@ -1,8 +0,0 @@
-import pegs
-
-discard parsePeg(
-      pattern = "input",
-      filename = "filename",
-      line = 1,
-      col = 23)
-
diff --git a/tests/navigator/minclude.nim b/tests/navigator/minclude.nim
new file mode 100644
index 000000000..f65ebfab9
--- /dev/null
+++ b/tests/navigator/minclude.nim
@@ -0,0 +1,2 @@
+# An include file.
+foo(3)
diff --git a/tests/navigator/tincludefile.nim b/tests/navigator/tincludefile.nim
new file mode 100644
index 000000000..a913d0736
--- /dev/null
+++ b/tests/navigator/tincludefile.nim
@@ -0,0 +1,29 @@
+discard """
+  disabled: true
+  cmd: "nim check $options --defusages:$file,12,7 $file"
+  nimout: '''def tincludefile_temp.nim(11, 10)
+usage tincludefile_temp.nim(12, 8)
+  '''
+"""
+
+
+
+proc foo(x: int) =
+  echo x
+
+foo(3)
+echo "yes", 1 != 3
+
+#!EDIT!#
+discard """
+  cmd: "nim check $options --defusages:$file/../minclude.nim,2,2 $file"
+  nimout: '''def tincludefile_temp.nim(10, 6)
+usage minclude.nim(2, 1)
+  '''
+"""
+
+
+proc foo(x: int) =
+  echo x
+
+include minclude
diff --git a/tests/navigator/tnav1.nim b/tests/navigator/tnav1.nim
new file mode 100644
index 000000000..e76c921f3
--- /dev/null
+++ b/tests/navigator/tnav1.nim
@@ -0,0 +1,33 @@
+discard """
+  disabled: true
+  cmd: "nim check $options --defusages:$file,12,7 $file"
+  nimout: '''def tnav1_temp.nim(11, 10)
+usage tnav1_temp.nim(12, 8)
+  '''
+"""
+
+import std / [times]
+
+proc foo(x: int) =
+  echo x
+
+foo(3)
+echo "yes", 1 != 3
+
+#!EDIT!#
+discard """
+  cmd: "nim check $options --defusages:$file,15,2 $file"
+  nimout: '''def tnav1_temp.nim(12, 6)
+usage tnav1_temp.nim(15, 1)
+  '''
+"""
+
+
+import std / [times]
+
+proc foo(x: int) =
+  echo x
+
+foo(3)
+echo "yes", 1 != 3
+
diff --git a/tests/newconfig/bar/config.nims b/tests/newconfig/bar/config.nims
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/newconfig/bar/config.nims
diff --git a/tests/newconfig/bar/mfoo.nim b/tests/newconfig/bar/mfoo.nim
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/newconfig/bar/mfoo.nim
diff --git a/tests/newconfig/bar/mfoo.nim.cfg b/tests/newconfig/bar/mfoo.nim.cfg
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/newconfig/bar/mfoo.nim.cfg
diff --git a/tests/newconfig/bar/mfoo.nims b/tests/newconfig/bar/mfoo.nims
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/newconfig/bar/mfoo.nims
diff --git a/tests/newconfig/bar/nim.cfg b/tests/newconfig/bar/nim.cfg
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/newconfig/bar/nim.cfg
diff --git a/tests/newconfig/foo2/mfoo2.customext b/tests/newconfig/foo2/mfoo2.customext
new file mode 100644
index 000000000..66c8b1d15
--- /dev/null
+++ b/tests/newconfig/foo2/mfoo2.customext
@@ -0,0 +1,2 @@
+doAssert defined(nimscript)
+echo "123"
diff --git a/tests/newconfig/mconfigcheck.nims b/tests/newconfig/mconfigcheck.nims
new file mode 100644
index 000000000..8df6715f6
--- /dev/null
+++ b/tests/newconfig/mconfigcheck.nims
@@ -0,0 +1,9 @@
+mode = ScriptMode.Verbose
+proc build() =
+  echo "building nim... "
+  exec "sleep 10"
+  exec "nonexistant command"
+  echo getCurrentDir()
+
+echo "hello"
+build()
diff --git a/tests/newconfig/tfoo.nim b/tests/newconfig/tfoo.nim
index 52ea841ee..0c6ded470 100644
--- a/tests/newconfig/tfoo.nim
+++ b/tests/newconfig/tfoo.nim
@@ -1,7 +1,7 @@
 discard """
-  cmd: "nim default $file"
-  output: '''hello world! 0.5'''
-  msg: '''[NimScript] exec: gcc -v'''
+  cmd: "nim default --hint:cc:off --hint:cc $file"
+  output: '''hello world! 0.5 true'''
+  nimout: '''[NimScript] exec: gcc -v'''
 """
 
 when not defined(definedefine):
@@ -10,4 +10,4 @@ when not defined(definedefine):
 import math, mfriends
 
 discard gen[int]()
-echo "hello world! ", ln 2.0
+echo "hello world! ", ln 2.0, " ", compileOption("opt", "speed")
diff --git a/tests/newconfig/tfoo.nims b/tests/newconfig/tfoo.nims
index 3be42c38a..f22caaacd 100644
--- a/tests/newconfig/tfoo.nims
+++ b/tests/newconfig/tfoo.nims
@@ -3,14 +3,32 @@ mode = ScriptMode.Whatif
 
 exec "gcc -v"
 
-# test that ospaths actually compiles:
-import ospaths
+--define:release
 
 --forceBuild
---path: "../friends"
+--path: "../generics"
 
 warning("uninit", off)
-hint("processing", off)
+
+block: # supported syntaxes for hint,warning,switch
+  --hint:processing
+  hint("processing", on)
+  hint("processing", off)
+  switch("hint", "processing")
+  switch("hint", "processing:on")
+  switch("hint", "processing:off")
+  switch("hint", "[processing]")
+  switch("hint", "[processing]:on")
+  switch("hint", "[processing]:off") # leave it off
+
+  --warning:UnusedImport
+  switch("warning", "UnusedImport:off")
+  switch("warning", "UnusedImport:on")
+  switch("warning", "[UnusedImport]:off")
+  switch("warning", "[UnusedImport]:on")
+  switch("warning", "[UnusedImport]")
+  switch("warning", "UnusedImport") # leave it on
+
 #--verbosity:2
 patchFile("stdlib", "math", "mymath")
 
@@ -27,54 +45,64 @@ doAssert(existsEnv("dummy") == false)
 
 # issue #7283
 putEnv("dummy", "myval")
-doAssert(existsEnv("dummy") == true)
+doAssert(existsEnv("dummy"))
 doAssert(getEnv("dummy") == "myval")
+delEnv("dummy")
+doAssert(existsEnv("dummy") == false)
 
 # issue #7393
 let wd = getCurrentDir()
 cd("..")
-assert wd != getCurrentDir()
+doAssert wd != getCurrentDir()
 cd(wd)
-assert wd == getCurrentDir()
+doAssert wd == getCurrentDir()
 
-assert findExe("nim") != ""
+when false:
+  # this doesn't work in a 'koch testintall' environment
+  doAssert findExe("nim") != ""
 
 # general tests
 mode = ScriptMode.Verbose
 
-assert getCommand() == "c"
+doAssert getCommand() == "c"
 setCommand("cpp")
-assert getCommand() == "cpp"
+doAssert getCommand() == "cpp"
 setCommand("c")
 
-assert cmpic("HeLLO", "hello") == 0
+doAssert cmpic("HeLLO", "hello") == 0
 
-assert fileExists("tests/newconfig/tfoo.nims") == true
-assert dirExists("tests") == true
+doAssert fileExists("tests/newconfig/tfoo.nims") == true
+doAssert dirExists("tests") == true
 
-assert existsFile("tests/newconfig/tfoo.nims") == true
-assert existsDir("tests") == true
+doAssert fileExists("tests/newconfig/tfoo.nims") == true
+doAssert dirExists("tests") == true
 
 discard selfExe()
 
 when defined(windows):
-  assert toExe("nim") == "nim.exe"
-  assert toDll("nim") == "nim.dll"
+  doAssert toExe("nim") == "nim.exe"
+  doAssert toDll("nim") == "nim.dll"
 else:
-  assert toExe("nim") == "nim"
-  assert toDll("nim") == "libnim.so"
+  doAssert toExe("nim") == "nim"
+  doAssert toDll("nim") == "libnim.so"
 
 rmDir("tempXYZ")
-assert dirExists("tempXYZ") == false
+doAssertRaises(OSError):
+  rmDir("tempXYZ", checkDir = true)
+doAssert dirExists("tempXYZ") == false
 mkDir("tempXYZ")
-assert dirExists("tempXYZ") == true
-assert fileExists("tempXYZ/koch.nim") == false
-cpFile("koch.nim", "tempXYZ/koch.nim")
-assert fileExists("tempXYZ/koch.nim") == true
-cpDir("nimsuggest", "tempXYZ/.")
-assert dirExists("tempXYZ/tests") == true
-assert fileExists("tempXYZ/nimsuggest.nim") == true
-rmFile("tempXYZ/koch.nim")
-assert fileExists("tempXYZ/koch.nim") == false
+doAssert dirExists("tempXYZ") == true
+doAssert fileExists("tempXYZ/koch.nim") == false
+
+when false:
+  # this doesn't work in a 'koch testintall' environment
+  cpFile("koch.nim", "tempXYZ/koch.nim")
+  doAssert fileExists("tempXYZ/koch.nim") == true
+  cpDir("nimsuggest", "tempXYZ/.")
+  doAssert dirExists("tempXYZ/tests") == true
+  doAssert fileExists("tempXYZ/nimsuggest.nim") == true
+  rmFile("tempXYZ/koch.nim")
+  doAssert fileExists("tempXYZ/koch.nim") == false
+
 rmDir("tempXYZ")
-assert dirExists("tempXYZ") == false
+doAssert dirExists("tempXYZ") == false
diff --git a/tests/nimble/tnimblepathdollar.nim b/tests/nimble/tnimblepathdollar.nim
new file mode 100644
index 000000000..994d975bb
--- /dev/null
+++ b/tests/nimble/tnimblepathdollar.nim
@@ -0,0 +1,7 @@
+import pkgA/module as A
+import pkgB/module as B
+import pkgC/module as C
+
+doAssert pkgATest() == 1, "Simple pkgA-0.1.0 wasn't added to path correctly."
+doAssert pkgBTest() == 0xDEADBEEF, "pkgB-#head wasn't picked over pkgB-0.1.0"
+doAssert pkgCTest() == 0xDEADBEEF, "pkgC-#head wasn't picked over pkgC-#aa11"
diff --git a/tests/nimble/tnimblepathdollar.nims b/tests/nimble/tnimblepathdollar.nims
new file mode 100644
index 000000000..9621fb29f
--- /dev/null
+++ b/tests/nimble/tnimblepathdollar.nims
@@ -0,0 +1,5 @@
+switch("clearNimblePath")
+switch("nimblePath", "$projectdir/nimbleDir/simplePkgs")
+switch("path", "$nimblepath/pkgA-0.1.0")
+switch("path", "$nimblepath/pkgB-#head")
+switch("path", "$nimblepath/pkgC-#head")
diff --git a/tests/nimble/tnimblepathdollar_fault.nim b/tests/nimble/tnimblepathdollar_fault.nim
new file mode 100644
index 000000000..3f0270123
--- /dev/null
+++ b/tests/nimble/tnimblepathdollar_fault.nim
@@ -0,0 +1,13 @@
+discard """
+  errormsg: "cannot open file: pkgA/module"
+"""
+
+# see nims file; comment out `switch("noNimblePath")` there and there would be no error
+
+import pkgA/module as A
+import pkgB/module as B
+import pkgC/module as C
+
+doAssert pkgATest() == 1, "Simple pkgA-0.1.0 wasn't added to path correctly."
+doAssert pkgBTest() == 0xDEADBEEF, "pkgB-#head wasn't picked over pkgB-0.1.0"
+doAssert pkgCTest() == 0xDEADBEEF, "pkgC-#head wasn't picked over pkgC-#aa11"
diff --git a/tests/nimble/tnimblepathdollar_fault.nims b/tests/nimble/tnimblepathdollar_fault.nims
new file mode 100644
index 000000000..7d47da744
--- /dev/null
+++ b/tests/nimble/tnimblepathdollar_fault.nims
@@ -0,0 +1,5 @@
+switch("noNimblePath")
+switch("nimblePath", "$projectdir/nimbleDir/simplePkgs")
+switch("path", "$nimblepath/pkgA-0.1.0")
+switch("path", "$nimblepath/pkgB-#head")
+switch("path", "$nimblepath/pkgC-#head")
diff --git a/tests/nimdoc/imp.nim b/tests/nimdoc/imp.nim
new file mode 100644
index 000000000..ca08dcb35
--- /dev/null
+++ b/tests/nimdoc/imp.nim
@@ -0,0 +1 @@
+proc fn5*() = discard
diff --git a/tests/nimdoc/m13129.nim b/tests/nimdoc/m13129.nim
new file mode 100644
index 000000000..34e118381
--- /dev/null
+++ b/tests/nimdoc/m13129.nim
@@ -0,0 +1,37 @@
+# issue #13129
+
+when defined(cpp):
+  {.push header: "<vector>".}
+  type
+    Vector[T] {.importcpp: "std::vector".} = object
+  {.pop.}
+elif defined(js):
+  proc endsWith*(s, suffix: cstring): bool {.noSideEffect,importjs: "#.endsWith(#)".}
+elif defined(c):
+  proc c_printf*(frmt: cstring): cint {.
+    importc: "printf", header: "<stdio.h>", varargs, discardable.}
+
+proc main*() =
+  runnableExamples:
+    import std/compilesettings
+    doAssert not defined(m13129Foo1)
+    doAssert defined(m13129Foo2)
+    doAssert not defined(nimdoc)
+    echo "ok2: backend: " & querySetting(backend)
+
+import std/compilesettings
+when defined nimdoc:
+  static:
+    doAssert defined(m13129Foo1)
+    doAssert not defined(m13129Foo2)
+    echo "ok1:" & querySetting(backend)
+
+when isMainModule:
+  when not defined(js):
+    import std/os
+    let cache = querySetting(nimcacheDir)
+    doAssert cache.len > 0
+    let app = getAppFilename()
+    doAssert app.isRelativeTo(cache), $(app, cache)
+    doAssert querySetting(projectFull) == currentSourcePath
+    echo "ok3"
diff --git a/tests/nimdoc/readme.md b/tests/nimdoc/readme.md
new file mode 100644
index 000000000..40b5841eb
--- /dev/null
+++ b/tests/nimdoc/readme.md
@@ -0,0 +1,3 @@
+## links
+* $nim/nimdoc/tester.nim: tests html validation
+* $nim/tests/nimdoc/: tests `runnableExamples` + `nim doc` logic
diff --git a/tests/nimdoc/sub/imp.nim b/tests/nimdoc/sub/imp.nim
new file mode 100644
index 000000000..d66542e45
--- /dev/null
+++ b/tests/nimdoc/sub/imp.nim
@@ -0,0 +1 @@
+proc fn4*() = discard
diff --git a/tests/nimdoc/sub/imp2.nim b/tests/nimdoc/sub/imp2.nim
new file mode 100644
index 000000000..60fa1e72d
--- /dev/null
+++ b/tests/nimdoc/sub/imp2.nim
@@ -0,0 +1 @@
+proc fn3*() = discard
diff --git a/tests/nimdoc/sub/mmain.nim b/tests/nimdoc/sub/mmain.nim
new file mode 100644
index 000000000..42547b0b8
--- /dev/null
+++ b/tests/nimdoc/sub/mmain.nim
@@ -0,0 +1,8 @@
+{.warning[UnusedImport]: off.}
+
+import ../imp as impa
+import imp as impb
+import imp2
+
+proc fn1*() = discard
+proc fn2*() = discard
diff --git a/tests/nimdoc/t15916.nim b/tests/nimdoc/t15916.nim
new file mode 100644
index 000000000..c6c09d94b
--- /dev/null
+++ b/tests/nimdoc/t15916.nim
@@ -0,0 +1,16 @@
+discard """
+cmd: "nim doc --hints:off $file"
+action: "compile"
+joinable: false
+"""
+
+type
+  Test* = object
+    id: int
+
+proc initTest*(id: int): Test =
+  result.id = id
+
+proc hello*() =
+  runnableExamples:
+    discard
diff --git a/tests/nimdoc/t17615.nim b/tests/nimdoc/t17615.nim
new file mode 100644
index 000000000..77ae35a15
--- /dev/null
+++ b/tests/nimdoc/t17615.nim
@@ -0,0 +1,11 @@
+discard """
+  cmd: "nim doc -r $file"
+  errormsg: "runnableExamples must appear before the first non-comment statement"
+  line: 10
+"""
+
+func fn*() =
+  ## foo
+  discard
+  runnableExamples:
+    assert true
diff --git a/tests/nimdoc/trunnableexamples.nim b/tests/nimdoc/trunnableexamples.nim
new file mode 100644
index 000000000..57e725b2e
--- /dev/null
+++ b/tests/nimdoc/trunnableexamples.nim
@@ -0,0 +1,213 @@
+discard """
+cmd: '''nim doc --doccmd:"-d:testFooExternal --hints:off" --hints:off $file'''
+action: "compile"
+nimoutFull: true
+nimout: '''
+foo1
+foo2
+foo3
+foo5
+foo7
+in examplesInTemplate1
+doc in outer
+doc in inner1
+doc in inner2
+foo8
+foo9
+foo6
+'''
+joinable: false
+"""
+
+
+proc fun*() =
+  runnableExamples:
+    block: # `defer` only allowed inside a block
+      defer: echo "foo1"
+
+  runnableExamples:
+    # `fun*` only allowed at top level
+    proc fun*()=echo "foo2"
+    fun()
+    block:
+      defer: echo "foo3"
+
+  runnableExamples:
+    # ditto
+    proc fun*()=echo "foo5"
+    fun()
+
+  runnableExamples("--experimental:codeReordering --warnings:off"):
+    # `codeReordering` only allowed at top level
+    {.experimental: "codeReordering".}
+    proc fun1() = fun2()
+    proc fun2() = echo "foo6"
+    fun1()
+
+  runnableExamples:
+    # only works at top level
+    import std/macros
+    macro myImport(a: static string): untyped =
+      newTree(nnkImportStmt, [newLit a])
+    myImport "str" & "utils"
+    doAssert declared(isAlphaAscii)
+    echo "foo7"
+
+when true: # issue #12746
+  # this proc on its own works fine with `nim doc`
+  proc goodProc*() =
+    runnableExamples:
+      try:
+        discard
+      except CatchableError:
+        # just the general except will work
+        discard
+
+  # FIXED: this proc fails with `nim doc`
+  proc badProc*() =
+    runnableExamples:
+      try:
+        discard
+      except IOError:
+        # specifying Error is culprit
+        discard
+
+when true: # runnableExamples with rdoccmd
+  runnableExamples "-d:testFoo -d:testBar":
+    doAssert defined(testFoo) and defined(testBar)
+    doAssert defined(testFooExternal)
+  runnableExamples "-d:testFoo2":
+    doAssert defined(testFoo2)
+    doAssert not defined(testFoo) # doesn't get confused by other examples
+
+  ## all these syntaxes work too
+  runnableExamples("-d:testFoo2"): discard
+  runnableExamples(): discard
+  runnableExamples: discard
+  runnableExamples "-r:off": # issue #10731
+    doAssert false ## we compile only (-r:off), so this won't be run
+  runnableExamples "-b:js":
+    import std/compilesettings
+    proc startsWith*(s, prefix: cstring): bool {.noSideEffect, importjs: "#.startsWith(#)".}
+    doAssert querySetting(backend) == "js"
+  runnableExamples "-b:cpp":
+    static: doAssert defined(cpp)
+    type std_exception {.importcpp: "std::exception", header: "<exception>".} = object
+
+  proc fun2*() =
+    runnableExamples "-d:foo": discard # checks that it also works inside procs
+
+  template fun3Impl(): untyped =
+    runnableExamples(rdoccmd="-d:foo"):
+      nonexistent
+        # bugfix: this shouldn't be semchecked when `runnableExamples`
+        # has more than 1 argument
+    discard
+
+  proc fun3*[T]() =
+    fun3Impl()
+
+  when false: # future work
+    # passing non-string-litterals (for reuse)
+    const a = "-b:cpp"
+    runnableExamples(a): discard
+
+    # passing seq (to run with multiple compilation options)
+    runnableExamples(@["-b:cpp", "-b:js"]): discard
+
+when true: # bug #16993
+  template examplesInTemplate1*(cond: untyped) =
+    ## in examplesInTemplate1
+    runnableExamples:
+      echo "in examplesInTemplate1"
+    discard
+  examplesInTemplate1 true
+  examplesInTemplate1 true
+  examplesInTemplate1 true
+
+when true: # bug #18054
+  template outer*(body: untyped) =
+    ## outer template doc string.
+    runnableExamples:
+      echo "doc in outer"
+    ##
+    template inner1*() =
+      ## inner1 template doc string.
+      runnableExamples:
+        echo "doc in inner1"
+      ##
+
+    template inner2*() =
+      ## inner2 template doc string.
+      runnableExamples:
+        echo "doc in inner2"
+    body
+  outer:
+    inner1()
+    inner2()
+
+when true: # bug #17835
+  template anyItFake*(s, pred: untyped): bool =
+    ## Foo
+    runnableExamples: discard
+    true
+
+  proc anyItFakeMain*(n: seq[int]): bool =
+    result = anyItFake(n, it == 0)
+      # this was giving: Error: runnableExamples must appear before the first non-comment statement
+
+runnableExamples:
+  block: # bug #17279
+    when int.sizeof == 8:
+      let x = 0xffffffffffffffff
+      doAssert x == -1
+
+  # bug #13491
+  block:
+    proc fun(): int = doAssert false
+    doAssertRaises(AssertionDefect, (discard fun()))
+
+  block:
+    template foo(body) = discard
+    foo (discard)
+
+  block:
+    template fn(body: untyped): untyped = true
+    doAssert(fn do: nonexistent)
+  import std/macros
+  macro foo*(x, y) =
+    result = newLetStmt(x[0][0], x[0][1])
+  foo:
+    a = 1
+  do: discard
+
+# also check for runnableExamples at module scope
+runnableExamples:
+  block:
+    defer: echo "foo8"
+
+runnableExamples:
+  proc fun*()=echo "foo9"
+  fun()
+
+# import std/assertions by default
+runnableExamples("-d:nimPreviewSlimSystem"):
+  doAssert true
+
+# note: there are yet other examples where putting runnableExamples at module
+# scope is needed, for example when using an `include` before an `import`, etc.
+
+##[
+snippet:
+
+.. code-block:: Nim
+    :test:
+
+  doAssert defined(testFooExternal)
+
+]##
+
+when true: # runnableExamples with rdoccmd
+  runnableExamples "-d:testFoo -d:testBar":
+    doAssert defined(testFoo) and defined(testBar)
+    doAssert defined(testFooExternal)
diff --git a/tests/niminaction/Chapter1/various1.nim b/tests/niminaction/Chapter1/various1.nim
index 688180fd2..21553dc40 100644
--- a/tests/niminaction/Chapter1/various1.nim
+++ b/tests/niminaction/Chapter1/various1.nim
@@ -19,7 +19,7 @@ proc showNumber(num: int | float) =
 showNumber(3.14)
 showNumber(42)
 
-for i in 0 .. <10:
+for i in 0 ..< 10:
   echo(i)
 
 block: # Block added due to clash.
@@ -32,7 +32,7 @@ block: # Block added due to clash.
   let dog = Dog()
   dog.bark() #<2>
 
-import sequtils, future, strutils
+import sequtils, sugar, strutils
 let list = @["Dominik Picheta", "Andreas Rumpf", "Desmond Hume"]
 list.map(
   (x: string) -> (string, string) => (x.split[0], x.split[1])
diff --git a/tests/niminaction/Chapter2/explicit_discard.nim b/tests/niminaction/Chapter2/explicit_discard.nim
index 3e94c335b..7f3b3395e 100644
--- a/tests/niminaction/Chapter2/explicit_discard.nim
+++ b/tests/niminaction/Chapter2/explicit_discard.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "has to be used (or discarded)"
   line: 7
-  errormsg: "has to be discarded"
 """
 
 proc myProc(name: string): string = "Hello " & name
-myProc("Dominik")
\ No newline at end of file
+myProc("Dominik")
diff --git a/tests/niminaction/Chapter2/no_def_eq.nim b/tests/niminaction/Chapter2/no_def_eq.nim
index 77f0a7dd8..b9d62e036 100644
--- a/tests/niminaction/Chapter2/no_def_eq.nim
+++ b/tests/niminaction/Chapter2/no_def_eq.nim
@@ -1,6 +1,6 @@
 discard """
-  line: 16
   errormsg: "type mismatch"
+  line: 16
 """
 
 type
@@ -13,4 +13,4 @@ type
 let dog: Dog = Dog(name: "Fluffy")
 let cat: Cat = Cat(name: "Fluffy")
 
-echo(dog == cat)
\ No newline at end of file
+echo(dog == cat)
diff --git a/tests/niminaction/Chapter2/no_iterator.nim b/tests/niminaction/Chapter2/no_iterator.nim
index 331d69480..555fac21a 100644
--- a/tests/niminaction/Chapter2/no_iterator.nim
+++ b/tests/niminaction/Chapter2/no_iterator.nim
@@ -1,7 +1,7 @@
 discard """
-  line: 6
   errormsg: "type mismatch"
+  line: 6
 """
 
 for i in 5:
-  echo i
\ No newline at end of file
+  echo i
diff --git a/tests/niminaction/Chapter2/no_seq_type.nim b/tests/niminaction/Chapter2/no_seq_type.nim
index 493be270a..f1494124b 100644
--- a/tests/niminaction/Chapter2/no_seq_type.nim
+++ b/tests/niminaction/Chapter2/no_seq_type.nim
@@ -1,6 +1,6 @@
 discard """
-  line: 6
   errormsg: "cannot infer the type of the sequence"
+  line: 6
 """
 
-var list = @[]
\ No newline at end of file
+var list = @[]
diff --git a/tests/niminaction/Chapter2/resultaccept.nim b/tests/niminaction/Chapter2/resultaccept.nim
index 7dd976b40..390f7b329 100644
--- a/tests/niminaction/Chapter2/resultaccept.nim
+++ b/tests/niminaction/Chapter2/resultaccept.nim
@@ -22,7 +22,7 @@ proc resultVar2: string =
   result.add("returned")
 
 doAssert implicit() == "I will be returned"
-doAssert discarded() == nil
+doAssert discarded().len == 0
 doAssert explicit() == "I will be returned"
 doAssert resultVar() == "I will be returned"
 doAssert resultVar2() == "I will be returned"
\ No newline at end of file
diff --git a/tests/niminaction/Chapter2/resultreject.nim b/tests/niminaction/Chapter2/resultreject.nim
index de59af7d9..145345072 100644
--- a/tests/niminaction/Chapter2/resultreject.nim
+++ b/tests/niminaction/Chapter2/resultreject.nim
@@ -1,13 +1,13 @@
 discard """
+  errormsg: "has to be used (or discarded)"
   line: 27
-  errormsg: "has to be discarded"
 """
 
 # Page 35.
 
 proc implicit: string =
   "I will be returned"
-  
+
 proc discarded: string =
   discard "I will not be returned"
 
@@ -16,7 +16,7 @@ proc explicit: string =
 
 proc resultVar: string =
   result = "I will be returned"
-  
+
 proc resultVar2: string =
   result = ""
   result.add("I will be ")
@@ -30,4 +30,4 @@ doAssert implicit() == "I will be returned"
 doAssert discarded() == nil
 doAssert explicit() == "I will be returned"
 doAssert resultVar() == "I will be returned"
-doAssert resultVar2() == "I will be returned"
\ No newline at end of file
+doAssert resultVar2() == "I will be returned"
diff --git a/tests/niminaction/Chapter2/various2.nim b/tests/niminaction/Chapter2/various2.nim
index 3f6a3f453..921f38c7d 100644
--- a/tests/niminaction/Chapter2/various2.nim
+++ b/tests/niminaction/Chapter2/various2.nim
@@ -140,7 +140,7 @@ let numbers = @[1, 2, 3, 4, 5, 6]
 let odd = filter(numbers, proc (x: int): bool = x mod 2 != 0)
 doAssert odd == @[1, 3, 5]
 
-import sequtils, future
+import sequtils, sugar
 let numbers1 = @[1, 2, 3, 4, 5, 6]
 let odd1 = filter(numbers1, (x: int) -> bool => x mod 2 != 0)
 assert odd1 == @[1, 3, 5]
@@ -149,7 +149,7 @@ proc isValid(x: int, validator: proc (x: int): bool) =
   if validator(x): echo(x, " is valid")
   else: echo(x, " is NOT valid")
 
-import future
+import sugar
 proc isValid2(x: int, validator: (x: int) -> bool) =
   if validator(x): echo(x, " is valid")
   else: echo(x, " is NOT valid")
@@ -179,13 +179,13 @@ for i in list4.low .. list4.high:
   echo(list4[i])
 
 var list5: seq[int] = @[]
-doAssertRaises(IndexError):
+doAssertRaises(IndexDefect):
   list5[0] = 1
 
 list5.add(1)
 
 assert list5[0] == 1
-doAssertRaises(IndexError):
+doAssertRaises(IndexDefect):
   echo list5[42]
 
 # -- Errata: var list: seq[int]; echo(list[0]). This now creates an exception,
@@ -193,7 +193,7 @@ doAssertRaises(IndexError):
 
 block:
   var list = newSeq[string](3)
-  assert list[0] == nil
+  assert list[0].len == 0
   list[0] = "Foo"
   list[1] = "Bar"
   list[2] = "Baz"
@@ -202,7 +202,7 @@ block:
 
 block:
   let list = @[4, 8, 15, 16, 23, 42]
-  for i in 0 .. <list.len:
+  for i in 0 ..< list.len:
     stdout.write($list[i] & " ")
 
 var collection: set[int16]
@@ -366,4 +366,4 @@ block:
     Color {.pure.} = enum
       red, green, blue
 
-  let color = Color.red
\ No newline at end of file
+  let color = Color.red
diff --git a/tests/niminaction/Chapter3/ChatApp/src/client.nim b/tests/niminaction/Chapter3/ChatApp/src/client.nim
index 4d139655c..d479ebf43 100644
--- a/tests/niminaction/Chapter3/ChatApp/src/client.nim
+++ b/tests/niminaction/Chapter3/ChatApp/src/client.nim
@@ -1,3 +1,7 @@
+discard """
+action: compile
+"""
+
 import os, threadpool, asyncdispatch, asyncnet
 import protocol
 
diff --git a/tests/niminaction/Chapter3/ChatApp/src/protocol.nim b/tests/niminaction/Chapter3/ChatApp/src/protocol.nim
index af515861c..4c122d4cc 100644
--- a/tests/niminaction/Chapter3/ChatApp/src/protocol.nim
+++ b/tests/niminaction/Chapter3/ChatApp/src/protocol.nim
@@ -37,7 +37,7 @@ proc createMessage*(username, message: string): string =
     "message": %message
   }) & "\c\l"
 
-when isMainModule:
+when true:
   block:
     let data = """{"username": "dom", "message": "hello"}"""
     let parsed = parseMessage(data)
diff --git a/tests/niminaction/Chapter3/ChatApp/src/server.nim b/tests/niminaction/Chapter3/ChatApp/src/server.nim
index 8c572aeb0..fbf0e5110 100644
--- a/tests/niminaction/Chapter3/ChatApp/src/server.nim
+++ b/tests/niminaction/Chapter3/ChatApp/src/server.nim
@@ -1,3 +1,7 @@
+discard """
+action: compile
+"""
+
 import asyncdispatch, asyncnet
 
 type
@@ -75,10 +79,10 @@ proc loop(server: Server, port = 7687) {.async.} =
 
 # Check whether this module has been imported as a dependency to another
 # module, or whether this module is the main module.
-when isMainModule:
+when true:
   # Initialise a new server.
   var server = newServer()
   echo("Server initialised!")
   # Execute the ``loop`` procedure. The ``waitFor`` procedure will run the
   # asyncdispatch event loop until the ``loop`` procedure finishes executing.
-  waitFor loop(server)
\ No newline at end of file
+  waitFor loop(server)
diff --git a/tests/niminaction/Chapter3/various3.nim b/tests/niminaction/Chapter3/various3.nim
index 478229b00..c7cdf7db4 100644
--- a/tests/niminaction/Chapter3/various3.nim
+++ b/tests/niminaction/Chapter3/various3.nim
@@ -1,7 +1,14 @@
+discard """
+matrix: "--mm:refc"
+output: '''
+Future is no longer empty, 42
+'''
+"""
+
 import threadpool
 proc foo: string = "Dog"
 var x: FlowVar[string] = spawn foo()
-assert(^x == "Dog")
+doAssert(^x == "Dog")
 
 block:
   type
@@ -13,36 +20,36 @@ block:
         discard
 
   var obj = Box(empty: false, contents: "Hello")
-  assert obj.contents == "Hello"
+  doAssert obj.contents == "Hello"
 
   var obj2 = Box(empty: true)
-  doAssertRaises(FieldError):
+  doAssertRaises(FieldDefect):
     echo(obj2.contents)
 
 import json
-assert parseJson("null").kind == JNull
-assert parseJson("true").kind == JBool
-assert parseJson("42").kind == JInt
-assert parseJson("3.14").kind == JFloat
-assert parseJson("\"Hi\"").kind == JString
-assert parseJson("""{ "key": "value" }""").kind == JObject
-assert parseJson("[1, 2, 3, 4]").kind == JArray
+doAssert parseJson("null").kind == JNull
+doAssert parseJson("true").kind == JBool
+doAssert parseJson("42").kind == JInt
+doAssert parseJson("3.14").kind == JFloat
+doAssert parseJson("\"Hi\"").kind == JString
+doAssert parseJson("""{ "key": "value" }""").kind == JObject
+doAssert parseJson("[1, 2, 3, 4]").kind == JArray
 
 import json
 let data = """
   {"username": "Dominik"}
 """
 
-let obj = parseJson(data) 
-assert obj.kind == JObject 
-assert obj["username"].kind == JString 
-assert obj["username"].str == "Dominik"
+let obj = parseJson(data)
+doAssert obj.kind == JObject
+doAssert obj["username"].kind == JString
+doAssert obj["username"].str == "Dominik"
 
 block:
   proc count10(): int =
-    for i in 0 .. <10:
+    for i in 0 ..< 10:
       result.inc
-  assert count10() == 10
+  doAssert count10() == 10
 
 type
   Point = tuple[x, y: int]
@@ -60,12 +67,12 @@ var amy = Human(name: "Amy", age: 20)
 
 import asyncdispatch
 
-var future = newFuture[int]() 
-doAssert(not future.finished) 
+var future = newFuture[int]()
+doAssert(not future.finished)
 
-future.callback = 
-  proc (future: Future[int]) = 
-    echo("Future is no longer empty, ", future.read) 
+future.callback =
+  proc (future: Future[int]) =
+    echo("Future is no longer empty, ", future.read)
 
 future.complete(42)
 
@@ -85,9 +92,8 @@ import asyncdispatch, asyncfile, os
 proc readFiles() {.async.} =
   # --- Changed to getTempDir here.
   var file = openAsync(getTempDir() / "test.txt", fmReadWrite)
-  let data = await file.readAll() 
-  echo(data) 
-  await file.write("Hello!\n") 
+  let data = await file.readAll()
+  echo(data)
+  await file.write("Hello!\n")
 
 waitFor readFiles()
-
diff --git a/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim b/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim
index 478f533d9..913cd77db 100644
--- a/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim
+++ b/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim
@@ -1,3 +1,7 @@
+discard """
+action: compile
+"""
+
 # See this page for info about the format https://wikitech.wikimedia.org/wiki/Analytics/Data/Pagecounts-all-sites
 import tables, parseutils, strutils, threadpool
 
@@ -63,9 +67,9 @@ proc readChunks(filename: string, chunksize = 1000000): Stats =
       # Find where the last line ends
       chunkLen.dec
 
-    responses.add(spawn parse(buffer[0 .. <chunkLen]))
+    responses.add(spawn parse(buffer[0 ..< chunkLen]))
     oldBufferLen = readSize - chunkLen
-    buffer[0 .. <oldBufferLen] = buffer[readSize - oldBufferLen .. ^1]
+    buffer[0 ..< oldBufferLen] = buffer[readSize - oldBufferLen .. ^1]
 
   for resp in responses:
     let statistic = ^resp
@@ -75,5 +79,5 @@ proc readChunks(filename: string, chunksize = 1000000): Stats =
   file.close()
 
 
-when isMainModule:
+when true:
   echo readChunks(filename)
diff --git a/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim b/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim
index 8df3b6aeb..102313de9 100644
--- a/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim
+++ b/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim
@@ -1,3 +1,7 @@
+discard """
+action: compile
+"""
+
 # See this page for info about the format https://wikitech.wikimedia.org/wiki/Analytics/Data/Pagecounts-all-sites
 import tables, parseutils, strutils, threadpool, re
 
@@ -47,9 +51,9 @@ proc readChunks(filename: string, chunksize = 1000000): Stats =
       # Find where the last line ends
       chunkLen.dec
 
-    responses.add(spawn parse(buffer[0 .. <chunkLen]))
+    responses.add(spawn parse(buffer[0 ..< chunkLen]))
     oldBufferLen = readSize - chunkLen
-    buffer[0 .. <oldBufferLen] = buffer[readSize - oldBufferLen .. ^1]
+    buffer[0 ..< oldBufferLen] = buffer[readSize - oldBufferLen .. ^1]
 
   echo("Spawns: ", responses.len)
   for resp in responses:
@@ -60,5 +64,5 @@ proc readChunks(filename: string, chunksize = 1000000): Stats =
   file.close()
 
 
-when isMainModule:
+when true:
   echo readChunks(filename)
diff --git a/tests/niminaction/Chapter6/WikipediaStats/naive.nim b/tests/niminaction/Chapter6/WikipediaStats/naive.nim
index ed4fba8e2..687177f74 100644
--- a/tests/niminaction/Chapter6/WikipediaStats/naive.nim
+++ b/tests/niminaction/Chapter6/WikipediaStats/naive.nim
@@ -1,3 +1,7 @@
+discard """
+action: compile
+"""
+
 # See this page for info about the format https://wikitech.wikimedia.org/wiki/Analytics/Data/Pagecounts-all-sites
 import tables, parseutils, strutils
 
@@ -25,5 +29,5 @@ proc parse(filename: string): tuple[projectName, pageTitle: string,
 
   file.close()
 
-when isMainModule:
+when true:
   echo parse(filename)
diff --git a/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim b/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim
index 7181145e9..379ec7364 100644
--- a/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim
+++ b/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim
@@ -1,3 +1,7 @@
+discard """
+action: compile
+"""
+
 import os, parseutils, threadpool, strutils
 
 type
@@ -54,9 +58,9 @@ proc readPageCounts(filename: string, chunkSize = 1_000_000) =
     while chunkLen >= 0 and buffer[chunkLen - 1] notin NewLines:
       chunkLen.dec
 
-    responses.add(spawn parseChunk(buffer[0 .. <chunkLen]))
+    responses.add(spawn parseChunk(buffer[0 ..< chunkLen]))
     oldBufferLen = readSize - chunkLen
-    buffer[0 .. <oldBufferLen] = buffer[readSize - oldBufferLen .. ^1]
+    buffer[0 ..< oldBufferLen] = buffer[readSize - oldBufferLen .. ^1]
 
   var mostPopular = newStats()
   for resp in responses:
@@ -66,7 +70,7 @@ proc readPageCounts(filename: string, chunkSize = 1_000_000) =
 
   echo("Most popular is: ", mostPopular)
 
-when isMainModule:
+when true:
   const file = "pagecounts-20160101-050000"
   let filename = getCurrentDir() / file
-  readPageCounts(filename)
\ No newline at end of file
+  readPageCounts(filename)
diff --git a/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim b/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim
index c62b2f93e..f4b072204 100644
--- a/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim
+++ b/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim
@@ -1,13 +1,17 @@
+discard """
+action: compile
+"""
+
 import threadpool
 
 var counter = 0
 
 proc increment(x: int) =
-  for i in 0 .. <x:
+  for i in 0 ..< x:
     let value = counter + 1
     counter = value
 
 spawn increment(10_000)
 spawn increment(10_000)
 sync()
-echo(counter)
\ No newline at end of file
+echo(counter)
diff --git a/tests/niminaction/Chapter6/WikipediaStats/sequential_counts.nim b/tests/niminaction/Chapter6/WikipediaStats/sequential_counts.nim
index 25ad7d5f4..f4bae3df5 100644
--- a/tests/niminaction/Chapter6/WikipediaStats/sequential_counts.nim
+++ b/tests/niminaction/Chapter6/WikipediaStats/sequential_counts.nim
@@ -1,3 +1,7 @@
+discard """
+action: compile
+"""
+
 import os, parseutils
 
 proc parse(line: string, domainCode, pageTitle: var string,
@@ -28,7 +32,7 @@ proc readPageCounts(filename: string) =
 
   echo("Most popular is: ", mostPopular)
 
-when isMainModule:
+when true:
   const file = "pagecounts-20160101-050000"
   let filename = getCurrentDir() / file
-  readPageCounts(filename)
\ No newline at end of file
+  readPageCounts(filename)
diff --git a/tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim b/tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim
index 502ea61a9..7bdde8397 100644
--- a/tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim
+++ b/tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim
@@ -10,7 +10,7 @@ initLock(counterLock)
 var counter {.guard: counterLock.} = 0
 
 proc increment(x: int) =
-  for i in 0 .. <x:
+  for i in 0 ..< x:
     let value = counter + 1
     counter = value
 
diff --git a/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim b/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim
index c7aee1b44..67d9323f2 100644
--- a/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim
+++ b/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim
@@ -1,6 +1,11 @@
+discard """
+disabled: true
+output: "Database created successfully!"
+"""
+
 import database
 
 var db = newDatabase()
 db.setup()
 echo("Database created successfully!")
-db.close()
\ No newline at end of file
+db.close()
diff --git a/tests/niminaction/Chapter7/Tweeter/src/database.nim b/tests/niminaction/Chapter7/Tweeter/src/database.nim
index 4faba3f6a..bd6667f70 100644
--- a/tests/niminaction/Chapter7/Tweeter/src/database.nim
+++ b/tests/niminaction/Chapter7/Tweeter/src/database.nim
@@ -50,7 +50,7 @@ proc post*(database: Database, message: Message) =
     raise newException(ValueError, "Message has to be less than 140 characters.")
 
   database.db.exec(sql"INSERT INTO Message VALUES (?, ?, ?);", #<2>
-    message.username, $message.time.toSeconds().int, message.msg) #<3>
+    message.username, $message.time.toUnix().int, message.msg) #<3>
 
 proc follow*(database: Database, follower: User, user: User) =
   database.db.exec(sql"INSERT INTO Following VALUES (?, ?);",#<2>
@@ -79,9 +79,9 @@ proc findMessages*(database: Database, usernames: seq[string],
   result = @[]
   if usernames.len == 0: return
   var whereClause = " WHERE "
-  for i in 0 .. <usernames.len:
+  for i in 0 ..< usernames.len:
     whereClause.add("username = ? ")
-    if i != <usernames.len:
+    if i != usernames.high:
       whereClause.add("or ")
 
   let messages = database.db.getAllRows(
@@ -90,4 +90,4 @@ proc findMessages*(database: Database, usernames: seq[string],
           "ORDER BY time DESC LIMIT " & $limit),
       usernames)
   for row in messages:
-    result.add(Message(username: row[0], time: fromSeconds(row[1].parseInt), msg: row[2]))
+    result.add(Message(username: row[0], time: fromUnix(row[1].parseInt), msg: row[2]))
diff --git a/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim b/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim
index b8a36306e..1b521521c 100644
--- a/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim
+++ b/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim
@@ -1,3 +1,9 @@
+discard """
+disabled: true
+action: compile
+matrix: "--threads:off"
+"""
+
 import asyncdispatch, times
 
 import jester
@@ -47,7 +53,7 @@ routes:
     redirect(uri("/" & @"target"))
 
   post "/login":
-    setCookie("username", @"username", getTime().getGMTime() + 2.hours)
+    setCookie("username", @"username", getTime().utc() + 2.hours)
     redirect("/")
 
   post "/createMessage":
diff --git a/tests/niminaction/Chapter7/Tweeter/src/views/user.nim b/tests/niminaction/Chapter7/Tweeter/src/views/user.nim
index f3791b493..4abcf440d 100644
--- a/tests/niminaction/Chapter7/Tweeter/src/views/user.nim
+++ b/tests/niminaction/Chapter7/Tweeter/src/views/user.nim
@@ -33,14 +33,14 @@
   #for message in messages:
     <div>
       <a href="/${message.username}">${message.username}</a>
-      <span>${message.time.getGMTime().format("HH:mm MMMM d',' yyyy")}</span>
+      <span>${message.time.utc().format("HH:mm MMMM d',' yyyy")}</span>
       <h3>${message.msg}</h3>
     </div>
   #end for
 </div>
 #end proc
 #
-#when isMainModule:
+#when true:
 #  echo renderUser(User(username: "d0m96<>", following: @[]))
 #  echo renderMessages(@[
 #    Message(username: "d0m96", time: getTime(), msg: "Hello World!"),
diff --git a/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim b/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim
index 926ca452c..c8beb4a30 100644
--- a/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim
+++ b/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim
@@ -1,6 +1,11 @@
+discard """
+disabled: true
+outputsub: "All tests finished successfully!"
+"""
+
 import database, os, times
 
-when isMainModule:
+when true:
   removeFile("tweeter_test.db")
   var db = newDatabase("tweeter_test.db")
   db.setup()
diff --git a/tests/niminaction/Chapter8/canvas/canvas.nim b/tests/niminaction/Chapter8/canvas/canvas.nim
index 713d1e9e2..ae2765630 100644
--- a/tests/niminaction/Chapter8/canvas/canvas.nim
+++ b/tests/niminaction/Chapter8/canvas/canvas.nim
@@ -1,3 +1,7 @@
+discard """
+action: compile
+"""
+
 import dom
 
 type
diff --git a/tests/niminaction/Chapter8/sdl/sdl.nim b/tests/niminaction/Chapter8/sdl/sdl.nim
index a1b30281b..212f7b022 100644
--- a/tests/niminaction/Chapter8/sdl/sdl.nim
+++ b/tests/niminaction/Chapter8/sdl/sdl.nim
@@ -1,9 +1,13 @@
-when defined(Windows):
+when defined(windows):
   const libName* = "SDL2.dll"
-elif defined(Linux):
+elif defined(linux) or defined(freebsd) or defined(netbsd):
   const libName* = "libSDL2.so"
-elif defined(MacOsX):
+elif defined(macosx):
   const libName* = "libSDL2.dylib"
+elif defined(openbsd):
+  const libName* = "libSDL2.so.0.6"
+else:
+  {.error: "SDL library name not set for this platform".}
 
 type
   SdlWindow = object
diff --git a/tests/niminaction/Chapter8/sdl/sdl_test.nim b/tests/niminaction/Chapter8/sdl/sdl_test.nim
index a572d5231..db1700e0d 100644
--- a/tests/niminaction/Chapter8/sdl/sdl_test.nim
+++ b/tests/niminaction/Chapter8/sdl/sdl_test.nim
@@ -1,3 +1,7 @@
+discard """
+action: compile
+"""
+
 import os
 import sdl
 
@@ -13,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 false: # no long work with gcc 14!
+  # 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/niminaction/Chapter8/sfml/sfml_test.nim b/tests/niminaction/Chapter8/sfml/sfml_test.nim
index 7d56d0903..e71060cb4 100644
--- a/tests/niminaction/Chapter8/sfml/sfml_test.nim
+++ b/tests/niminaction/Chapter8/sfml/sfml_test.nim
@@ -1,5 +1,6 @@
 discard """
-  disabled: "windows"
+action: compile
+disabled: "windows"
 """
 
 import sfml, os
diff --git a/tests/notnil/tmust_compile.nim b/tests/notnil/tmust_compile.nim
index a32c6c7ec..3a013e9ed 100644
--- a/tests/notnil/tmust_compile.nim
+++ b/tests/notnil/tmust_compile.nim
@@ -44,16 +44,16 @@ import json
 type
 
   foo = object
-    thing: string not nil
+    thing: ptr int not nil
 
   CTS = ref object
     subs_by_sid: Table[int, foo]
 
 
 proc parse(cts: CTS, jn: JsonNode) =
-
+  var y = jn.getInt(4523)
   let ces = foo(
-    thing: jn.getStr("thing")
+    thing: addr y
   )
 
   cts.subs_by_sid[0] = ces
@@ -64,16 +64,12 @@ proc parse(cts: CTS, jn: JsonNode) =
 proc p(x: proc(){.closure.} not nil) = discard
 p(proc(){.closure.} = discard)
 
-# bug #3993
-
-type
-  List[T] = seq[T] not nil
-
-proc `^^`[T](v: T, lst: List[T]): List[T] =
-  result = @[v]
-  result.add(lst)
+# bug #6490
 
-proc Nil[T](): List[T] = @[]
+proc p2(a: proc()) =
+    if a.isNil:
+        raise newException(ValueError, "a is nil")
+    else:
+        let b: proc() not nil = a
 
-when isMainModule:
-  let lst = 1 ^^ 2 ^^ Nil[int]()
+p2(writeStackTrace)
diff --git a/tests/notnil/tnotnil.nim b/tests/notnil/tnotnil.nim
index e392b155c..c33b6fcac 100644
--- a/tests/notnil/tnotnil.nim
+++ b/tests/notnil/tnotnil.nim
@@ -1,6 +1,6 @@
 discard """
-  line: 22
   errormsg: "type mismatch"
+  line: 13
 """
 {.experimental: "notnil".}
 type
@@ -8,16 +8,6 @@ type
   TObj = object
     x: int
 
-  MyString = string not nil
-
-#var x: PObj = nil
-
-proc p(x: string not nil): int =
-  result = 45
-
-proc q(x: MyString) = discard
 proc q2(x: string) = discard
 
 q2(nil)
-q(nil)
-
diff --git a/tests/notnil/tnotnil1.nim b/tests/notnil/tnotnil1.nim
index 7f9d02295..60666d64d 100644
--- a/tests/notnil/tnotnil1.nim
+++ b/tests/notnil/tnotnil1.nim
@@ -1,6 +1,6 @@
 discard """
   errormsg: "'y' is provably nil"
-  line:38
+  line:25
 """
 
 import strutils
@@ -10,19 +10,6 @@ type
   TObj = object
     x, y: int
 
-type
-  superstring = string not nil
-
-
-proc q(s: superstring) =
-  echo s
-
-proc p2() =
-  var a: string = "I am not nil"
-  q(a) # but this should and does not
-
-p2()
-
 proc q(x: pointer not nil) =
   discard
 
diff --git a/tests/notnil/tnotnil4.nim b/tests/notnil/tnotnil4.nim
index 4fd169827..c5178f71b 100644
--- a/tests/notnil/tnotnil4.nim
+++ b/tests/notnil/tnotnil4.nim
@@ -10,8 +10,9 @@ proc check(a: TObj not nil) =
 proc doit() =
    var x : array[0..1, TObj]
 
-   if x[0] != nil:
-      check(x[0])
+   let y = x[0]
+   if y != nil:
+      check(y)
 
 doit()
 
diff --git a/tests/notnil/tnotnil5.nim b/tests/notnil/tnotnil5.nim
new file mode 100644
index 000000000..2dcb7f7c3
--- /dev/null
+++ b/tests/notnil/tnotnil5.nim
@@ -0,0 +1,28 @@
+discard """
+  matrix: "--threads:on"
+"""
+
+{.experimental: "parallel".}
+{.experimental: "notnil".}
+import threadpool
+
+type
+  AO = object
+    x: int
+
+  A = ref AO not nil
+
+proc process(a: A): A =
+  return A(x: a.x+1)
+
+proc processMany(ayys: openArray[A]): seq[A] =
+  var newAs: seq[FlowVar[A]]
+
+  parallel:
+    for a in ayys:
+      newAs.add(spawn process(a))
+  for newAflow in newAs:
+    let newA = ^newAflow
+    if isNil(newA):
+      return @[]
+    result.add(newA)
diff --git a/tests/notnil/tnotnil_in_objconstr.nim b/tests/notnil/tnotnil_in_objconstr.nim
index d33709906..471150f44 100644
--- a/tests/notnil/tnotnil_in_objconstr.nim
+++ b/tests/notnil/tnotnil_in_objconstr.nim
@@ -1,14 +1,18 @@
 discard """
-  errormsg: "fields not initialized: bar"
-  line: "13"
+  errormsg: "The Foo type requires the following fields to be initialized: bar, baz"
+  line: "17"
 """
 {.experimental: "notnil".}
 # bug #2355
 type
-  Foo = object
-    foo: string not nil
-    bar: string not nil
+  Base = object of RootObj
+    baz: ref int not nil
 
-# Create instance without initializaing the `bar` field
-var f = Foo(foo: "foo")
+  Foo = object of Base
+    foo: ref int
+    bar: ref int not nil
+
+var x: ref int = new(int)
+# Create instance without initializing the `bar` field
+var f = Foo(foo: x)
 echo f.bar.isNil # true
diff --git a/tests/notnil/tparse.nim b/tests/notnil/tparse.nim
new file mode 100644
index 000000000..5c938ff04
--- /dev/null
+++ b/tests/notnil/tparse.nim
@@ -0,0 +1,18 @@
+# issue #16324
+
+{.push experimental: "notnil".}
+
+block:
+  type Foo = ref object
+    value: int
+    
+  proc newFoo1(): Foo not nil =               # This compiles
+    return Foo(value: 1)
+    
+  proc newFoo2(): Foo not nil {.inline.} =    # This does not
+    return Foo(value: 1)
+
+  doAssert newFoo1().value == 1
+  doAssert newFoo2().value == 1
+
+{.pop.}
diff --git a/tests/objects/m19342.c b/tests/objects/m19342.c
new file mode 100644
index 000000000..113f65309
--- /dev/null
+++ b/tests/objects/m19342.c
@@ -0,0 +1,12 @@
+struct Node
+{
+  int data[25];
+};
+
+
+struct Node hello(int name) {
+  struct Node x = {999, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+            0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,
+            1, 2, 3, 4, 5};
+  return x;
+}
\ No newline at end of file
diff --git a/tests/objects/mobject_default_value.nim b/tests/objects/mobject_default_value.nim
new file mode 100644
index 000000000..224549501
--- /dev/null
+++ b/tests/objects/mobject_default_value.nim
@@ -0,0 +1,15 @@
+type
+  Clean = object
+    mem: int
+  Default* = object
+    poi: int = 12
+    clc: Clean
+    se*: range[0'i32 .. high(int32)]
+
+  NonDefault* = object
+    poi: int
+
+  PrellDeque*[T] = object
+    pendingTasks*: range[0'i32 .. high(int32)]
+    head: T
+    tail: T
diff --git a/tests/objects/t12753.nim b/tests/objects/t12753.nim
new file mode 100644
index 000000000..1009433be
--- /dev/null
+++ b/tests/objects/t12753.nim
@@ -0,0 +1,22 @@
+discard """
+  output: '''
+(v: [(v: [0.0, 1.1]), (v: [2.2, 3.3])])
+(v: [(v: [0.0, 1.1]), (v: [2.2, 3.3])])
+'''
+"""
+
+type
+  V = object
+    v:array[2,float]
+  M = object
+    v:array[2,V]
+
+var
+  a = M(v:[ V(v:[0.0,1.0]), V(v:[2.0,3.0]) ])
+  b = M(v:[ V(v:[0.0,0.1]), V(v:[0.2,0.3]) ])
+
+echo M(v: [V(v: [b.v[0].v[0] + a.v[0].v[0], b.v[0].v[1] + a.v[0].v[1]]),
+       V(v: [b.v[1].v[0] + a.v[1].v[0], b.v[1].v[1] + a.v[1].v[1]])])
+b = M(v: [V(v: [b.v[0].v[0] + a.v[0].v[0], b.v[0].v[1] + a.v[0].v[1]]),
+      V(v: [b.v[1].v[0] + a.v[1].v[0], b.v[1].v[1] + a.v[1].v[1]])])
+echo b
diff --git a/tests/objects/t17437.nim b/tests/objects/t17437.nim
new file mode 100644
index 000000000..b5c0e0525
--- /dev/null
+++ b/tests/objects/t17437.nim
@@ -0,0 +1,22 @@
+discard """
+  cmd: "nim check $file"
+  errormsg: ""
+  nimout: '''
+t17437.nim(20, 16) Error: undeclared identifier: 'x'
+t17437.nim(20, 16) Error: expression 'x' has no type (or is ambiguous)
+t17437.nim(20, 19) Error: incorrect object construction syntax
+t17437.nim(20, 19) Error: incorrect object construction syntax
+t17437.nim(20, 12) Error: expression '' has no type (or is ambiguous)
+'''
+"""
+
+# bug #17437 invalid object construction should result in error
+
+type
+  V = ref object
+    x, y: int
+
+proc m =
+  var v = V(x: x, y)
+
+m()
diff --git a/tests/objects/t19342.nim b/tests/objects/t19342.nim
new file mode 100644
index 000000000..d40d15a4b
--- /dev/null
+++ b/tests/objects/t19342.nim
@@ -0,0 +1,18 @@
+discard """
+  targets: "c cpp"
+"""
+
+{.compile: "m19342.c".}
+
+# bug #19342
+type
+  Node* {.bycopy.} = object
+    data: array[25, cint]
+
+proc myproc(name: cint): Node {.importc: "hello", cdecl.}
+
+proc parse =
+  let node = myproc(10)
+  doAssert node.data[0] == 999
+
+parse()
diff --git a/tests/objects/t19342_2.nim b/tests/objects/t19342_2.nim
new file mode 100644
index 000000000..6f6d0f2b3
--- /dev/null
+++ b/tests/objects/t19342_2.nim
@@ -0,0 +1,18 @@
+discard """
+  targets: "c cpp"
+"""
+
+{.compile: "m19342.c".}
+
+# bug #19342
+type
+  Node* {.byRef.} = object
+    data: array[25, cint]
+
+proc myproc(name: cint): Node {.importc: "hello", cdecl.}
+
+proc parse =
+  let node = myproc(10)
+  doAssert node.data[0] == 999
+
+parse()
\ No newline at end of file
diff --git a/tests/objects/t20972.nim b/tests/objects/t20972.nim
new file mode 100644
index 000000000..6383dc9b1
--- /dev/null
+++ b/tests/objects/t20972.nim
@@ -0,0 +1,15 @@
+discard """
+  matrix: "--mm:refc -d:release; --mm:orc -d:release"
+"""
+
+{.passC: "-fsanitize=undefined -fsanitize-undefined-trap-on-error -Wall -Wextra -pedantic -flto".}
+{.passL: "-fsanitize=undefined -fsanitize-undefined-trap-on-error -flto".}
+
+# bug #20972
+type ForkedEpochInfo = object
+  case kind: bool
+  of true, false: discard
+var info = ForkedEpochInfo(kind: true)
+doAssert info.kind
+info.kind = false
+doAssert not info.kind
diff --git a/tests/objects/t22301.nim b/tests/objects/t22301.nim
new file mode 100644
index 000000000..8746bf584
--- /dev/null
+++ b/tests/objects/t22301.nim
@@ -0,0 +1,17 @@
+discard """
+  errormsg: "branch initialization with a runtime discriminator is not supported for a branch whose fields have default values."
+"""
+
+# bug #22301
+type
+  Enum = enum A, B
+  Object = object
+    case a: Enum
+    of A:
+      integer: int = 200
+    of B:
+      time: string
+
+let x = A
+let s = Object(a: x)
+echo s
\ No newline at end of file
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/t4318.nim b/tests/objects/t4318.nim
new file mode 100644
index 000000000..beadd6909
--- /dev/null
+++ b/tests/objects/t4318.nim
@@ -0,0 +1,17 @@
+discard """
+  matrix: "--mm:refc"
+"""
+
+
+type
+  A = object of RootObj
+  B = object of A
+
+method identify(a:A) {.base.} = echo "A"
+method identify(b:B) = echo "B"
+
+var b: B
+
+doAssertRaises(ObjectAssignmentDefect):
+  var a: A = b
+  discard a
diff --git a/tests/objects/tdefaultfieldscheck.nim b/tests/objects/tdefaultfieldscheck.nim
new file mode 100644
index 000000000..8a05439d9
--- /dev/null
+++ b/tests/objects/tdefaultfieldscheck.nim
@@ -0,0 +1,16 @@
+discard """
+  cmd: "nim check --hints:off $file"
+  errormsg: ""
+  nimout:
+'''
+tdefaultfieldscheck.nim(14, 17) Error: type mismatch: got <string> but expected 'int'
+'''
+"""
+
+
+type
+  Date* = object
+    goal: float = 7
+    name: int = "string"
+
+echo default(Date)
diff --git a/tests/objects/tdefaultrangetypescheck.nim b/tests/objects/tdefaultrangetypescheck.nim
new file mode 100644
index 000000000..71e7ac59b
--- /dev/null
+++ b/tests/objects/tdefaultrangetypescheck.nim
@@ -0,0 +1,11 @@
+discard """
+  errormsg: "cannot convert 0 to range 1..5(int)"
+  line: 9
+"""
+
+type
+  Point = object
+    y: int
+    x: range[1..5] = 0
+
+echo default(Point)
diff --git a/tests/objects/tillegal_recursion.nim b/tests/objects/tillegal_recursion.nim
index 222139101..428c2e445 100644
--- a/tests/objects/tillegal_recursion.nim
+++ b/tests/objects/tillegal_recursion.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "inheritance only works with non-final objects"
+  errormsg: "Cannot inherit from: 'Foo:ObjectType'"
   line: 7
 """
 # bug #1691
diff --git a/tests/objects/tillegal_recursion2.nim b/tests/objects/tillegal_recursion2.nim
new file mode 100644
index 000000000..ce2279f04
--- /dev/null
+++ b/tests/objects/tillegal_recursion2.nim
@@ -0,0 +1,6 @@
+discard """
+  errormsg: "Cannot inherit from: 'Foo'"
+  line: 6
+"""
+type
+  Foo = object of Foo
diff --git a/tests/objects/tillegal_recursion3.nim b/tests/objects/tillegal_recursion3.nim
new file mode 100644
index 000000000..c80f29e28
--- /dev/null
+++ b/tests/objects/tillegal_recursion3.nim
@@ -0,0 +1,6 @@
+discard """
+  errormsg: "Cannot inherit from: 'Foo'"
+  line: 6
+"""
+type
+  Foo = object of ref Foo
diff --git a/tests/objects/tinherentedvalues.nim b/tests/objects/tinherentedvalues.nim
deleted file mode 100644
index ad7b5f326..000000000
--- a/tests/objects/tinherentedvalues.nim
+++ /dev/null
@@ -1,53 +0,0 @@
-discard """
-  output: '''tbObj of TC true
-true
-5'''
-"""
-
-# bug #1053
-type
-  TA = object of TObject
-    a: int
-
-  TB = object of TA
-    b: int
-
-  TC = object of TB
-    c: int
-
-proc test(p: TA) =
-  #echo "p of TB ", p of TB
-  if p of TB:
-    #var tbObj = TB(p)
-
-    # tbObj is actually no longer compatible with TC:
-    echo "tbObj of TC ", p of TC
-
-var v = TC()
-v.a = 1
-v.b = 2
-v.c = 3
-test(v)
-
-
-# bug #924
-type
-  MyObject = object of TObject
-    x: int
-
-var
-  asd: MyObject
-
-proc isMyObject(obj: TObject) =
-    echo obj of MyObject
-    if obj of MyObject:
-        let a = MyObject(obj)
-        echo a.x
-
-asd.x = 5
-
-#var asdCopy = TObject(asd)
-#echo asdCopy of MyObject
-
-isMyObject(asd)
-#isMyObject(asdCopy)
diff --git a/tests/objects/tinherit_from_generic.nim b/tests/objects/tinherit_from_generic.nim
deleted file mode 100644
index 6e0e929ce..000000000
--- a/tests/objects/tinherit_from_generic.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-discard """
-  output: '''true'''
-"""
-
-# bug #4673
-type
-  BaseObj[T] = ref object of RootObj
-  SomeObj = ref object of BaseObj[int]
-
-proc doSomething[T](o: BaseObj[T]) =
-  echo "true"
-var o = new(SomeObj)
-o.doSomething() # Error: cannot instantiate: 'T'
diff --git a/tests/objects/tobj_asgn_dont_slice.nim b/tests/objects/tobj_asgn_dont_slice.nim
index 866b5aecc..ce67c4490 100644
--- a/tests/objects/tobj_asgn_dont_slice.nim
+++ b/tests/objects/tobj_asgn_dont_slice.nim
@@ -1,5 +1,6 @@
 discard """
-  outputsub: '''ObjectAssignmentError'''
+  matrix: "--mm:refc"
+  outputsub: '''ObjectAssignmentDefect'''
   exitcode: "1"
 """
 
diff --git a/tests/objects/tobjconstr.nim b/tests/objects/tobjconstr.nim
index 7238d10c7..ee5a5b221 100644
--- a/tests/objects/tobjconstr.nim
+++ b/tests/objects/tobjconstr.nim
@@ -1,14 +1,15 @@
 discard """
-  output: '''(k: kindA, a: (x: "abc", z: [1, 1, 3]), method: ())
-(k: kindA, a: (x: "abc", z: [1, 2, 3]), method: ())
-(k: kindA, a: (x: "abc", z: [1, 3, 3]), method: ())
-(k: kindA, a: (x: "abc", z: [1, 4, 3]), method: ())
-(k: kindA, a: (x: "abc", z: [1, 5, 3]), method: ())
-(k: kindA, a: (x: "abc", z: [1, 6, 3]), method: ())
-(k: kindA, a: (x: "abc", z: [1, 7, 3]), method: ())
-(k: kindA, a: (x: "abc", z: [1, 8, 3]), method: ())
-(k: kindA, a: (x: "abc", z: [1, 9, 3]), method: ())
-(k: kindA, a: (x: "abc", z: [1, 10, 3]), method: ())
+  output: '''
+(k: kindA, a: (x: "abc", z: @[1, 1, 3]), method: ())
+(k: kindA, a: (x: "abc", z: @[1, 2, 3]), method: ())
+(k: kindA, a: (x: "abc", z: @[1, 3, 3]), method: ())
+(k: kindA, a: (x: "abc", z: @[1, 4, 3]), method: ())
+(k: kindA, a: (x: "abc", z: @[1, 5, 3]), method: ())
+(k: kindA, a: (x: "abc", z: @[1, 6, 3]), method: ())
+(k: kindA, a: (x: "abc", z: @[1, 7, 3]), method: ())
+(k: kindA, a: (x: "abc", z: @[1, 8, 3]), method: ())
+(k: kindA, a: (x: "abc", z: @[1, 9, 3]), method: ())
+(k: kindA, a: (x: "abc", z: @[1, 10, 3]), method: ())
 (y: 0, x: 123)
 (y: 678, x: 123)
 (z: 89, y: 0, x: 128)
@@ -16,7 +17,8 @@ discard """
 (y: 678, x: 123)
 (y: 0, x: 123)
 (y: 678, x: 123)
-(y: 123, x: 678)'''
+(y: 123, x: 678)
+'''
 """
 
 type
@@ -32,13 +34,6 @@ type
       a: TArg
       `method`: TEmpty # bug #1791
 
-proc `$`[T](s: seq[T]): string =
-  result = "["
-  for i, x in s:
-    if i > 0: result.add(", ")
-    result.add($x)
-  result.add("]")
-
 proc main() =
   for i in 1..10:
     let d = TDummy(k: kindA, a: TArg(x: "abc", z: @[1,i,3]), `method`: TEmpty())
@@ -55,27 +50,30 @@ type
   BS = object of B
   C = object of BS
     z*: int
-# inherited fields are ignored, so no fields are set
-when true:
-  var
-    o: B
-  o = B(x: 123)
-  echo o
-  o = B(y: 678, x: 123)
-  echo o
 
-# inherited fields are ignored
-echo C(x: 128, z: 89)          # (y: 0, x: 0)
-echo B(y: 678, x: 123)  # (y: 678, x: 0)
-echo B(x: 123, y: 678)  # (y: 678, x: 0)
+proc main2 =
+  # inherited fields are ignored, so no fields are set
+  when true:
+    var
+      o: B
+    o = B(x: 123)
+    echo o
+    o = B(y: 678, x: 123)
+    echo o
+
+  # inherited fields are ignored
+  echo C(x: 128, z: 89)          # (y: 0, x: 0)
+  echo B(y: 678, x: 123)  # (y: 678, x: 0)
+  echo B(x: 123, y: 678)  # (y: 678, x: 0)
 
-when true:
-  # correct, both with `var` and `let`;
-  var b=B(x: 123)
-  echo b                  # (y: 0, x: 123)
-  b=B(y: 678, x: 123)
-  echo b                  # (y: 678, x: 123)
-  b=B(y: b.x, x: b.y)
-  echo b                  # (y: 123, x: 678)
+  when true:
+    # correct, both with `var` and `let`;
+    var b=B(x: 123)
+    echo b                  # (y: 0, x: 123)
+    b=B(y: 678, x: 123)
+    echo b                  # (y: 678, x: 123)
+    b=B(y: b.x, x: b.y)
+    echo b                  # (y: 123, x: 678)
 
+main2()
 GC_fullCollect()
diff --git a/tests/objects/tobjconstr2.nim b/tests/objects/tobjconstr2.nim
index 6253edab0..cf4a694b4 100644
--- a/tests/objects/tobjconstr2.nim
+++ b/tests/objects/tobjconstr2.nim
@@ -15,8 +15,8 @@ echo s[0].x
 
 # bug #563
 type
-  Foo =
-    object {.inheritable.}
+  Foo {.inheritable.} =
+    object
       x: int
 
   Bar =
diff --git a/tests/objects/tobjcov.nim b/tests/objects/tobjcov.nim
index cf2e5becf..a12f74702 100644
--- a/tests/objects/tobjcov.nim
+++ b/tests/objects/tobjcov.nim
@@ -1,7 +1,15 @@
+discard """
+action: compile
+targets: "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 TObject
+  TA = object of RootObj
     a: int
   TB = object of TA
     b: array[0..5000_000, int]
@@ -14,4 +22,3 @@ proc bp(x: var TB) = x.b[high(x.b)] = -1
 var f = cast[proc (x: var TA) {.nimcall.}](bp)
 var a: TA
 f(a) # bp expects a TB, but gets a TA
-
diff --git a/tests/objects/tobject.nim b/tests/objects/tobject.nim
index cdb8f80db..a185bebcb 100644
--- a/tests/objects/tobject.nim
+++ b/tests/objects/tobject.nim
@@ -1,4 +1,3 @@
-import unittest
 
 type Obj = object
   foo: int
@@ -6,10 +5,70 @@ type Obj = object
 proc makeObj(x: int): Obj =
   result.foo = x
 
-suite "object basic methods":
-  test "it should convert an object to a string":
+block: # object basic methods
+  block: # it should convert an object to a string
     var obj = makeObj(1)
     # Should be "obj: (foo: 1)" or similar.
-    check($obj == "(foo: 1)")
-  test "it should test equality based on fields":
-    check(makeObj(1) == makeObj(1))
+    doAssert($obj == "(foo: 1)")
+  block: # it should test equality based on fields
+    doAssert(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
+
+# bug #10195
+type
+  InheritableFoo {.inheritable.} = ref object
+  InheritableBar = ref object of InheritableFoo # ERROR.
+
+block: # bug #14698
+  const N = 3
+  type Foo[T] = ref object
+    x1: int
+    when N == 2:
+      x2: float
+    when N == 3:
+      x3: seq[int]
+    else:
+      x4: char
+      x4b: array[9, char]
+
+  let t = Foo[float](x1: 1)
+  doAssert $(t[]) == "(x1: 1, x3: @[])"
+  doAssert t.sizeof == int.sizeof
+  type Foo1 = object
+    x1: int
+    x3: seq[int]
+  doAssert t[].sizeof == Foo1.sizeof
+
+# bug #147
+type
+  TValue* {.pure, final.} = object of RootObj
+    a: int
+  PValue = ref TValue
+  PPValue = ptr PValue
+
+
+var x: PValue
+new x
+var sp: PPValue = addr x
+
+sp.a = 2
+doAssert sp.a == 2
diff --git a/tests/objects/tobject2.nim b/tests/objects/tobject2.nim
deleted file mode 100644
index a49296843..000000000
--- a/tests/objects/tobject2.nim
+++ /dev/null
@@ -1,21 +0,0 @@
-# Tests the object implementation
-
-type
-  TPoint2d {.inheritable.} = object
-    x, y: int
-
-  TPoint3d = object of TPoint2d
-    z: int # added a field
-
-proc getPoint( p: var TPoint2d) =
-  {.breakpoint.}
-  writeLine(stdout, p.x)
-
-var
-  p: TPoint3d
-
-TPoint2d(p).x = 34
-p.y = 98
-p.z = 343
-
-getPoint(p)
diff --git a/tests/objects/tobject3.nim b/tests/objects/tobject3.nim
index a24a48c8b..9ff1743ce 100644
--- a/tests/objects/tobject3.nim
+++ b/tests/objects/tobject3.nim
@@ -1,13 +1,10 @@
 discard """
   output: '''TBar2
 TFoo
-16
-12
-16
-12'''
+'''
 """
 
-## XXX this output needs to be adapated for VCC which produces different results.
+## XXX this output needs to be adapted for VCC which produces different results.
 
 # It turned out that it's hard to generate correct for these two test cases at
 # the same time.
@@ -67,29 +64,6 @@ var aa = makeWindow()
 
 thisCausesError(dd, aa)
 
-# bug #4763
-type
-  testObject_1 = object
-    size: int32
-    value: int64
-
-  testObject_2 {.packed.} = object
-    size: int32
-    value: int64
-
-  testObject_3[T] = object
-    size: int32
-    value: T
-
-  testObject_4 {.packed.} [T] = object
-    size: int32
-    value: T
-
-echo sizeof(testObject_1)
-echo sizeof(testObject_2)
-echo sizeof(testObject_3[int64])
-echo sizeof(testObject_4[int64])
-
 # bug  #5892
 type
     Foo6 = distinct array[4, float32]
@@ -108,3 +82,14 @@ proc newArrayAnimationSampler*[T](): ArrayAnimationSampler[T] =
 
 discard newArrayAnimationSampler[Foo6]()
 discard newArrayAnimationSampler[AnotherFoo]()
+
+type
+  DefaultIsNone* = pointer | ptr | ref | proc {.nimcall.} | cstring | cstringArray
+  OptionKind* {.pure.} = enum None, Some
+  OptionA* [T] = object of RootObj
+    when T is DefaultIsNone:
+      value: T
+    else:
+      value: T
+      kind: OptionKind
+  SomeA* [T] = object of OptionA[T]
diff --git a/tests/objects/tobject_default_value.nim b/tests/objects/tobject_default_value.nim
new file mode 100644
index 000000000..0cd05e4f3
--- /dev/null
+++ b/tests/objects/tobject_default_value.nim
@@ -0,0 +1,780 @@
+discard """
+  matrix: "-d:nimPreviewRangeDefault --mm:refc; -d:nimPreviewRangeDefault --warningAsError:ProveInit --mm:orc"
+  targets: "c cpp js"
+"""
+
+import std/[times, macros, tables]
+
+type
+  Guess = object
+    poi: DateTime
+
+  GuessDistinct = distinct Guess
+
+block:
+  var x: Guess
+  discard Guess()
+
+  var y: GuessDistinct
+
+  discard y
+
+  discard GuessDistinct(x)
+
+
+import mobject_default_value
+
+block:
+  let x = Default()
+  doAssert x.se == 0'i32
+
+block:
+  let x = default(Default)
+  doAssert x.se == 0'i32
+# echo Default(poi: 12)
+# echo Default(poi: 17)
+
+# echo NonDefault(poi: 77)
+
+block:
+  var x: Default
+  doAssert x.se == 0'i32
+
+type
+  ObjectBase = object of RootObj
+    value = 12
+
+  ObjectBaseDistinct = distinct ObjectBase
+
+  DinstinctInObject = object
+    data: ObjectBaseDistinct
+
+  Object = object of ObjectBase
+    time: float = 1.2
+    date: int
+    scale: range[1..10]
+
+  Object2 = object
+    name: Object
+
+  Object3 = object
+    obj: Object2
+
+  ObjectTuple = tuple
+    base: ObjectBase
+    typ: int
+    obj: Object
+
+  TupleInObject = object
+    size = 777
+    data: ObjectTuple
+
+  Ref = ref object of ObjectBase
+
+  RefInt = ref object of Ref
+    data = 73
+
+  Ref2 = ref object of ObjectBase
+
+  RefInt2 = ref object of Ref
+    data = 73
+
+var t {.threadvar.}: Default
+# var m1, m2 {.threadvar.}: Default
+
+block:
+  doAssert t.se == 0'i32
+
+block: # ARC/ORC cannot bind destructors twice, so it cannot
+      # be moved into main
+  block:
+    var x: Ref
+    new(x)
+    doAssert x.value == 12, "Ref.value = " & $x.value
+
+    var y: RefInt
+    new(y)
+    doAssert y.value == 12
+    doAssert y.data == 73
+
+  block:
+    var x: Ref2
+    new(x, proc (x: Ref2) {.nimcall.} = discard "call Ref")
+    doAssert x.value == 12, "Ref.value = " & $x.value
+
+    proc call(x: RefInt2) =
+      discard "call RefInt"
+
+    var y: RefInt2
+    new(y, call)
+    doAssert y.value == 12
+    doAssert y.data == 73
+
+template main {.dirty.} =
+  block: # bug #16744
+    type
+      R = range[1..10]
+      Obj = object
+        r: R
+
+    var
+      rVal: R = default(R) # Works fine
+      objVal = default(Obj)
+
+    doAssert rVal == 1
+    doAssert objVal.r == 1
+
+  block: # bug #16744
+    type
+      R = range[1..10]
+      Obj = object
+        r: R
+
+    var
+      rVal: R = default(R) # Works fine
+      objVal = Obj()
+
+    doAssert rVal == 1 # it should be 1
+    doAssert objVal.r == 1
+
+  block: # bug #3608
+    type
+      abc = ref object
+        w: range[2..100]
+
+    proc createABC(): abc =
+      new(result)
+      result.w = 20
+
+    doAssert createABC().w == 20
+
+  block:
+    var x = new ObjectBase
+    doAssert x.value == 12
+
+    proc hello(): ref ObjectBase =
+      new result
+
+    let z = hello()
+    doAssert z.value == 12
+
+  block:
+    var base = ObjectBase()
+    var x: ObjectBaseDistinct = ObjectBaseDistinct(base)
+    doAssert ObjectBase(x).value == 12
+    let y = ObjectBaseDistinct(default(ObjectBase))
+    doAssert ObjectBase(y).value == 12
+
+    let m = ObjectBaseDistinct(ObjectBase())
+    doAssert ObjectBase(m).value == 12
+
+    proc hello(): ObjectBaseDistinct =
+      result = ObjectBaseDistinct(default(ObjectBase))
+
+    let z = hello()
+    doAssert ObjectBase(z).value == 12
+
+  block:
+    var x: DinstinctInObject
+    x.data = ObjectBaseDistinct(default(ObjectBase))
+
+    doAssert ObjectBase(x.data).value == 12
+
+  block:
+    var x = Object()
+    doAssert x.value == 12
+    doAssert x.time == 1.2
+    doAssert x.scale == 1
+
+    let y = default(Object)
+    doAssert y.value == 12
+    doAssert y.time == 1.2
+    doAssert y.scale == 1
+
+    var x1, x2, x3 = default(Object)
+    doAssert x1.value == 12
+    doAssert x1.time == 1.2
+    doAssert x1.scale == 1
+    doAssert x2.value == 12
+    doAssert x2.time == 1.2
+    doAssert x2.scale == 1
+    doAssert x3.value == 12
+    doAssert x3.time == 1.2
+    doAssert x3.scale == 1
+
+  block:
+    var x = new Object
+    doAssert x[] == default(Object)
+
+  block:
+    var x = default(Object2)
+    doAssert x.name.value == 12
+    doAssert x.name.time == 1.2
+    doAssert x.name.scale == 1
+
+  block:
+    let x = Object2()
+    doAssert x.name.value == 12
+    doAssert x.name.time == 1.2
+    doAssert x.name.scale == 1
+
+  block:
+    var x: ref Object2
+    new x
+    doAssert x[] == default(Object2)
+
+  block:
+    var x = default(Object3)
+    doAssert x.obj.name.value == 12
+    doAssert x.obj.name.time == 1.2
+    doAssert x.obj.name.scale == 1
+
+  block:
+    var x = Object3()
+    doAssert x.obj.name.value == 12
+    doAssert x.obj.name.time == 1.2
+    doAssert x.obj.name.scale == 1
+
+  when nimvm:
+    # todo
+    discard "fixme"
+  else:
+    when defined(gcArc) or defined(gcOrc):
+      block: #seq
+        var x = newSeq[Object](10)
+        let y = x[0]
+        doAssert y.value == 12
+        doAssert y.time == 1.2
+        doAssert y.scale == 1
+
+      block:
+        var x: seq[Object]
+        setLen(x, 5)
+        let y = x[^1]
+        doAssert y.value == 12
+        doAssert y.time == 1.2
+        doAssert y.scale == 1
+
+      block:
+        var my = @[1, 2, 3, 4, 5]
+        my.setLen(0)
+        my.setLen(5)
+        doAssert my == @[0, 0, 0, 0, 0]
+
+      block:
+        var my = "hello"
+        my.setLen(0)
+        my.setLen(5)
+        doAssert $(@my) == """@['\x00', '\x00', '\x00', '\x00', '\x00']"""
+
+  block: # array
+    var x: array[10, Object] = default(array[10, Object])
+    let y = x[0]
+    doAssert y.value == 12
+    doAssert y.time == 1.2
+    doAssert y.scale == 1
+
+  block: # array
+    var x {.noinit.}: array[10, Object]
+    discard x
+
+  block: # tuple
+    var x = default(ObjectTuple)
+    doAssert x.base.value == 12
+    doAssert x.typ == 0
+    doAssert x.obj.time == 1.2
+    doAssert x.obj.date == 0
+    doAssert x.obj.scale == 1
+    doAssert x.obj.value == 12
+
+  block: # tuple in object
+    var x = default(TupleInObject)
+    doAssert x.data.base.value == 12
+    doAssert x.data.typ == 0
+    doAssert x.data.obj.time == 1.2
+    doAssert x.data.obj.date == 0
+    doAssert x.data.obj.scale == 1
+    doAssert x.data.obj.value == 12
+    doAssert x.size == 777
+
+  type
+    ObjectArray = object
+      data: array[10, Object]
+
+  block:
+    var x = default(ObjectArray)
+    let y = x.data[0]
+    doAssert y.value == 12
+    doAssert y.time == 1.2
+    doAssert y.scale == 1
+
+  block:
+    var x: PrellDeque[int]
+    doAssert x.pendingTasks == 0
+
+  type
+    Color = enum
+      Red, Blue, Yellow
+
+    ObjectVarint = object
+      case kind: Color
+      of Red:
+        data: int = 10
+      of Blue:
+        fill = "123"
+      of Yellow:
+        time = 1.8'f32
+
+    ObjectVarint1 = object
+      case kind: Color = Blue
+      of Red:
+        data1: int = 10
+      of Blue:
+        fill2 = "123"
+        cry: float
+      of Yellow:
+        time3 = 1.8'f32
+        him: int
+
+  block:
+    var x = ObjectVarint(kind: Red)
+    doAssert x.kind == Red
+    doAssert x.data == 10
+
+  block:
+    var x = ObjectVarint(kind: Blue)
+    doAssert x.kind == Blue
+    doAssert x.fill == "123"
+
+  block:
+    var x = ObjectVarint(kind: Yellow)
+    doAssert x.kind == Yellow
+    doAssert typeof(x.time) is float32
+
+  block:
+    var x = default(ObjectVarint1)
+    doAssert x.kind == Blue
+    doAssert x.fill2 == "123"
+    x.cry = 326
+
+  type
+    ObjectVarint2 = object
+      case kind: Color
+      of Red:
+        data: int = 10
+      of Blue:
+        fill = "123"
+      of Yellow:
+        time = 1.8'f32
+
+  block:
+    var x = ObjectVarint2(kind: Blue)
+    doAssert x.fill == "123"
+
+  block:
+    type
+      Color = enum
+        Red, Blue, Yellow
+  
+    type
+      ObjectVarint3 = object
+        case kind: Color = Blue
+        of Red:
+          data1: int = 10
+        of Blue:
+          case name: Color = Blue
+          of Blue:
+            go = 12
+          else:
+            temp = 66
+          fill2 = "123"
+          cry: float
+        of Yellow:
+          time3 = 1.8'f32
+          him: int
+
+    block:
+      var x = default(ObjectVarint3)
+      doAssert x.kind == Blue
+      doAssert x.name == Blue
+      doAssert x.go == 12
+
+    block:
+      var x = ObjectVarint3(kind: Blue, name: Red, temp: 99)
+      doAssert x.kind == Blue
+      doAssert x.name == Red
+      doAssert x.temp == 99
+
+  block:
+    type
+      Default = tuple
+        id: int = 1
+        obj: ObjectBase
+        name: string
+
+      Class = object
+        def: Default
+
+      Member = object
+        def: Default = (id: 777, obj: ObjectBase(), name: "fine")
+
+    block:
+      var x = default(Default)
+      doAssert x.id == 1
+      doAssert x.obj == default(ObjectBase)
+      doAssert x.name == ""
+
+    block:
+      var x = default(Class)
+      doAssert x.def == default(Default)
+      doAssert x.def.id == 1
+      doAssert x.def.obj == default(ObjectBase)
+      doAssert x.def.name == ""
+
+    block:
+      var x = default(Member)
+      doAssert x.def.id == 777
+      doAssert x.def.obj == default(ObjectBase)
+      doAssert x.def.name == "fine"
+
+  block:
+    var x {.noinit.} = 12
+    doAssert x == 12
+
+    type
+      Pure = object
+        id: int = 12
+
+    var y {.noinit.}: Pure
+    doAssert y.id == 0
+
+    var z {.noinit.}: Pure = Pure(id: 77)
+    doAssert z.id == 77
+
+  block: # bug #20681
+    type A = object
+      d: DateTime = DateTime()
+
+    let x = default(A)
+    doAssert $x == "(d: Uninitialized DateTime)"
+
+  block: # bug #20715
+    block:
+      type
+        Foo = enum
+          A
+          B
+
+        Bar = object
+          case foo: Foo
+          of A:
+            t: range[-1..2]
+          else: discard
+
+      var d = default(Bar)
+      doAssert d.t == -1
+
+    block:
+      type
+        Foo = enum
+          A
+          B
+
+        Bar = object
+          case foo: Foo
+          of A:
+            t: range[0..2]
+          else: discard
+
+      var d = default(Bar)
+      doAssert d.t == 0
+
+    block: # bug #20740
+      block:
+        proc foo(x: static DateTime = Datetime()) =
+          discard
+
+        foo()
+
+      block:
+        macro foo(x: static DateTime) =
+          discard x
+
+        macro foo2: untyped =
+          var x = DateTime()
+
+          result = quote do:
+            foo(`x`)
+
+        foo2()
+
+
+  block: # issue #20699
+    type
+      Either[A,B] = object
+        case kind:bool
+        of false:
+          b: B
+        of true:
+            a: A
+      O = object of RootRef
+
+    proc oToEither(o:O):Either[O,void] =
+      Either[O,void](kind:true,a: o)
+
+    discard oToEither(O())
+
+  block: # bug #20695
+    type
+      Default = object
+        tabs: Table[string, int] = initTable[string, int]()
+
+    let d = default(Default)
+    doAssert d.tabs.len == 0
+
+  block:
+    type
+      Default = object
+        tabs: Table[string, int] = Table[string, int]()
+
+    let d = default(Default)
+    doAssert d.tabs.len == 0
+
+
+  block:
+    type DjangoDateTime = distinct DateTime
+
+    type Default = object
+      data: DjangoDateTime = DjangoDateTime(DateTime())
+
+    let x = default(Default)
+    doAssert x.data is DjangoDateTime
+
+  block:
+    type DjangoDateTime = distinct DateTime
+
+    type Default = object
+      data = DjangoDateTime(DateTime())
+
+    let x = default(Default)
+    doAssert x.data is DjangoDateTime
+
+  block:
+    type
+      Result2 = object
+        case o: bool
+        of false:
+          e: float
+        of true:
+          v {.requiresInit.} : int = 1
+
+    proc startSessionSync(): Result2 =
+      return Result2(o: true)
+
+    proc mainSync =
+      let ff = startSessionSync()
+      doAssert ff.v == 1
+
+    mainSync()
+
+  block:
+    type
+      Result2 = object
+        v {.requiresInit.} : int = 1
+
+    proc startSessionSync(): Result2 =
+      return Result2()
+
+    proc mainSync =
+      let ff = startSessionSync()
+      doAssert ff.v == 1
+
+    mainSync()
+
+  block: # bug #21801
+    func evaluate(i: int): float =
+      0.0
+
+    func evaluate(): float =
+      0.0
+
+    type SearchOptions = object
+        evaluation: proc(): float = evaluate
+
+  block:
+    func evaluate(): float =
+      0.0
+
+    type SearchOptions = object
+        evaluation: proc(): float = evaluate
+
+  block:
+    func evaluate(i: int): float =
+      0.0
+
+    type SearchOptions = object
+        evaluation = evaluate
+  block:
+    type
+      Result[T, E] = object
+        when T is void:
+          when E is void:
+            oResultPrivate: bool
+          else:
+            case oResultPrivate: bool
+            of false:
+              eResultPrivate: E
+            of true:
+              discard
+        else:
+          when E is void:
+            case oResultPrivate: bool
+            of false:
+              discard
+            of true:
+              vResultPrivate: T
+          else:
+            case oResultPrivate: bool
+            of false:
+              eResultPrivate: E
+            of true:
+              vResultPrivate: T
+
+
+    template `?`[T, E](self: Result[T, E]): auto =
+      let v = (self)
+      if not v.oResultPrivate:
+        when compiles(`assignResult?`(default(typeof(result)))):
+          when typeof(result) is typeof(v):
+            `assignResult?`(v)
+          elif E is void:
+            `assignResult?`(err(typeof(result)))
+          else:
+            `assignResult?`(err(typeof(result), v.eResultPrivate))
+          return
+        else:
+          return
+            when typeof(result) is typeof(v):
+              v
+            elif E is void:
+              err(typeof(result))
+            else:
+              err(typeof(result), v.eResultPrivate)
+
+      when not(T is void):
+        v.vResultPrivate
+        
+    type R = Result[int, string]
+
+    proc testAssignResult() =
+      var assigned: bool
+      template `assignResult?`(v: Result) =
+        assigned = true
+        result = v
+
+      proc failed(): Result[int, string] =
+        discard
+
+      proc calling(): Result[int, string] =
+        let _ = ? failed()
+        doAssert false
+
+      let r = calling()
+      doAssert assigned
+
+    when nimvm:
+      when not defined(js):
+        testAssignResult()
+    else:
+      testAssignResult()
+
+  block: # bug #22123
+    type Thing = object
+      x: float32 = 1
+
+    type ThingWithArray = object
+        arr: array[256, float32]
+        n: float32 = 1
+
+    type Container = ref object
+        thing: array[5, Thing]
+        thing_with_array: array[5, ThingWithArray]
+
+    var foo = new Container
+    doAssert int(foo.thing[0].x) == 1
+
+  block: # bug #22613
+    type
+      K = enum
+        A = "a"
+        B = "b"
+      T = object
+        case kind: K = B
+        of A:
+          a: int
+        of B:
+          b: float
+
+    doAssert T().kind == B
+
+  block: # bug #22926
+    type
+      Direction = enum
+        North
+        South
+        East
+        West
+
+      ArrayObj1 = object
+        list: array[Direction, int]
+
+      ArrayObj2 = object
+        list: array[Direction, int] = [1, 2, 3, 4]
+
+    block:
+      var a: ArrayObj1
+      doAssert a.list[West] == 0
+      var b = default ArrayObj1
+      doAssert b.list[North] == 0
+
+
+    block:
+      var a: ArrayObj2
+      doAssert a.list[West] == 0
+      var b = default ArrayObj2
+      doAssert b.list[North] == 1
+
+  block:
+    type limited_float = range[1.2..20.0]
+    doAssert default(limited_float) == 1.2
+
+
+  block:
+    type
+      range1 = range[1..10]
+      range2 = range[-1..10]
+
+    proc foo =
+      doAssert default(range1) == 1
+      doAssert default(range2) == -1
+
+      let s = default(array[5, range1])
+      doAssert s == [range1 1, 1, 1, 1, 1]
+
+    foo()
+
+  block:
+    type
+      Object = object
+        id: range[1.2..29.3]
+
+    var s = default(Object)
+    doAssert s.id == 1.2
+
+  block: # bug #23943
+    type limited_int = range[1..20]
+    var d: limited_int;
+    doAssert d == 1
+
+static: main()
+main()
diff --git a/tests/objects/tobjects.nim b/tests/objects/tobjects.nim
deleted file mode 100644
index 2f46b46b5..000000000
--- a/tests/objects/tobjects.nim
+++ /dev/null
@@ -1,52 +0,0 @@
-type
-  TBase = object of TObject
-    x, y: int
-
-  TSubclassKind = enum ka, kb, kc, kd, ke, kf
-  TSubclass = object of TBase
-    case c: TSubclassKind
-    of ka, kb, kc, kd:
-      a, b: int
-    of ke:
-      d, e, f: char
-    else: nil
-    n: bool
-
-type
-  TMyObject = object of TObject
-    case disp: range[0..4]
-      of 0: arg: char
-      of 1: s: string
-      else: wtf: bool
-
-var
-  x: TMyObject
-
-var
-  global: int
-
-var
-  s: string
-  r: float = 0.0
-  i: int = 500 + 400
-
-case i
-of 500..999: write(stdout, "ha!\n")
-of 1000..3000, 12: write(stdout, "ganz schön groß\n")
-of 1, 2, 3: write(stdout, "1 2 oder 3\n")
-else: write(stdout, "sollte nicht passieren\n")
-
-case readLine(stdin)
-of "Rumpf": write(stdout, "Hallo Meister!\n")
-of "Andreas": write(stdout, "Hallo Meister!\n")
-else: write(stdout, "Nicht mein Meister!\n")
-
-global = global + 1
-write(stdout, "Hallo wie heißt du? \n")
-s = readLine(stdin)
-i = 0
-while i < len(s):
-  if s[i] == 'c': write(stdout, "'c' in deinem Namen gefunden\n")
-  i = i + 1
-
-write(stdout, "Du heißt " & s)
diff --git a/tests/objects/tobjects_issues.nim b/tests/objects/tobjects_issues.nim
new file mode 100644
index 000000000..f1a416d04
--- /dev/null
+++ b/tests/objects/tobjects_issues.nim
@@ -0,0 +1,117 @@
+discard """
+  output: '''
+tbObj of TC true
+true
+5
+true
+is Nil false
+'''
+"""
+
+
+block t1053:
+  type
+    TA = object of RootObj
+      a: int
+    TB = object of TA
+      b: int
+    TC = object of TB
+      c: int
+
+  proc test(p: TA) =
+    if p of TB:
+      echo "tbObj of TC ", p of TC
+
+  var v = TC()
+  v.a = 1
+  v.b = 2
+  v.c = 3
+  test(v)
+
+
+
+block t924:
+  type
+    MyObject = object of RootObj
+      x: int
+  var asd: MyObject
+
+  proc isMyObject(obj: RootObj) =
+      echo obj of MyObject
+      if obj of MyObject:
+          let a = MyObject(obj)
+          echo a.x
+
+  asd.x = 5
+  isMyObject(asd)
+
+
+
+block t4673:
+  type
+    BaseObj[T] = ref object of RootObj
+    SomeObj = ref object of BaseObj[int]
+
+  proc doSomething[T](o: BaseObj[T]) =
+    echo "true"
+  var o = new(SomeObj)
+  o.doSomething() # Error: cannot instantiate: 'T'
+
+
+
+block t1658:
+  type
+    Loop = ref object
+      onBeforeSelect: proc (L: Loop)
+
+  var L: Loop
+  new L
+  L.onBeforeSelect = proc (bar: Loop) =
+    echo "is Nil ", bar.isNil
+
+  L.onBeforeSelect(L)
+
+
+
+block t2508:
+  type
+    GenericNodeObj[T] = ref object
+      obj: T
+    Node = ref object
+      children: seq[Node]
+      parent: Node
+      nodeObj: GenericNodeObj[int]
+
+  proc newNode(nodeObj: GenericNodeObj): Node =
+    result = Node(nodeObj: nodeObj)
+    newSeq(result.children, 10)
+
+  var genericObj = GenericNodeObj[int]()
+  var myNode = newNode(genericObj)
+
+
+
+block t2540:
+  type
+    BaseSceneNode[T] = ref object of RootObj
+      children: seq[BaseSceneNode[T]]
+      parent: BaseSceneNode[T]
+    SceneNode[T] = ref object of BaseSceneNode[T]
+    SomeObj = ref object
+
+  proc newSceneNode[T](): SceneNode[T] =
+    new result
+    result.children = @[]
+
+  var aNode = newSceneNode[SomeObj]()
+
+
+block t3038:
+  type
+    Data[T] = ref object of RootObj
+      data: T
+    Type = ref object of RootObj
+    SubType[T] = ref object of Type
+      data: Data[T]
+    SubSubType = ref object of SubType[int]
+    SubSubSubType = ref object of SubSubType
diff --git a/tests/objects/tobjects_various.nim b/tests/objects/tobjects_various.nim
new file mode 100644
index 000000000..55db9312e
--- /dev/null
+++ b/tests/objects/tobjects_various.nim
@@ -0,0 +1,120 @@
+discard """
+  output: '''
+34
+b
+wohoo
+baz
+'''
+"""
+
+
+block tobject2:
+  # Tests the object implementation
+  type
+    TPoint2d {.inheritable.} = object
+      x, y: int
+    TPoint3d = object of TPoint2d
+      z: int # added a field
+
+  proc getPoint( p: var TPoint2d) =
+    writeLine(stdout, p.x)
+
+  var p: TPoint3d
+
+  TPoint2d(p).x = 34
+  p.y = 98
+  p.z = 343
+
+  getPoint(p)
+
+
+
+block tofopr:
+  type
+    TMyType {.inheritable.} = object
+      len: int
+      data: string
+
+    TOtherType = object of TMyType
+
+  proc p(x: TMyType): bool =
+    return x of TOtherType
+
+  var
+    m: TMyType
+    n: TOtherType
+
+  doAssert p(m) == false
+  doAssert p(n)
+
+
+
+block toop:
+  type
+    TA = object of RootObj
+      x, y: int
+    TB = object of TA
+      z: int
+    TC = object of TB
+      whatever: string
+
+  proc p(a: var TA) = echo "a"
+  proc p(b: var TB) = echo "b"
+
+  var c: TC
+  p(c)
+
+
+
+block tfefobjsyntax:
+  type
+    Foo = object
+      a, b: int
+      s: string
+    FooBar = object of RootObj
+      n, m: string
+    Baz = object of FooBar
+
+  proc invoke(a: ref Baz) =
+    echo "baz"
+
+  # check object construction:
+  let x = (ref Foo)(a: 0, b: 45, s: "wohoo")
+  echo x.s
+
+  var y: ref FooBar = (ref Baz)(n: "n", m: "m")
+  invoke((ref Baz)(y))
+
+
+
+block t3012:
+  type
+    A {.inheritable.} = object
+    C {.inheritable.} = ref object
+
+  type
+    AA = ref object of A
+    CC = ref object of C
+
+
+
+block t7244:
+  type
+    Foo = ref object of RootRef
+    Bar = ref object of Foo
+
+  proc test(foo: var Foo) = discard
+  proc test(bar: var Bar) = test(Foo(bar))
+
+
+import std/macros
+
+#bug #20856
+macro ensureImplWorksOnConstr(t: typed): untyped =
+  expectKind(t, nnkObjConstr)
+  doAssert t[0].getTypeInst.getImpl.repr == "A = object"
+  doAssert t[0].getImpl.repr == "A = object"
+
+type A = object
+
+ensureImplWorksOnConstr(A())
diff --git a/tests/objects/tobjloop.nim b/tests/objects/tobjloop.nim
deleted file mode 100644
index 9fea1e2fb..000000000
--- a/tests/objects/tobjloop.nim
+++ /dev/null
@@ -1,15 +0,0 @@
-discard """
-  output: "is Nil false"
-"""
-# bug #1658
-
-type
-  Loop* = ref object
-    onBeforeSelect*: proc (L: Loop)
-
-var L: Loop
-new L
-L.onBeforeSelect = proc (bar: Loop) =
-  echo "is Nil ", bar.isNil
-
-L.onBeforeSelect(L)
diff --git a/tests/objects/tobjpragma.nim b/tests/objects/tobjpragma.nim
index 0a6cc893b..789b3ec4e 100644
--- a/tests/objects/tobjpragma.nim
+++ b/tests/objects/tobjpragma.nim
@@ -1,13 +1,14 @@
 discard """
-  file: "tobjpragma.nim"
-  output: '''2
+  output: '''
+2
 3
 9
 257
 1
 2
-3'''
-  disabled: "true"
+3
+'''
+disabled: "true"
 """
 
 # Disabled since some versions of GCC ignore the 'packed' attribute
diff --git a/tests/objects/tofopr.nim b/tests/objects/tofopr.nim
deleted file mode 100644
index ab2854571..000000000
--- a/tests/objects/tofopr.nim
+++ /dev/null
@@ -1,26 +0,0 @@
-discard """
-  file: "tofopr.nim"
-  output: "falsetrue"
-"""
-# Test is operator
-
-type
-  TMyType = object {.inheritable.}
-    len: int
-    data: string
-
-  TOtherType = object of TMyType
-
-proc p(x: TMyType): bool =
-  return x of TOtherType
-
-var
-  m: TMyType
-  n: TOtherType
-
-write(stdout, p(m))
-write(stdout, p(n))
-
-#OUT falsetrue
-
-
diff --git a/tests/objects/toop.nim b/tests/objects/toop.nim
deleted file mode 100644
index ebc59f637..000000000
--- a/tests/objects/toop.nim
+++ /dev/null
@@ -1,21 +0,0 @@
-discard """
-  output: "b"
-"""
-
-type
-  TA = object of TObject
-    x, y: int
-
-  TB = object of TA
-    z: int
-
-  TC = object of TB
-    whatever: string
-
-proc p(a: var TA) = echo "a"
-proc p(b: var TB) = echo "b"
-
-var c: TC
-
-p(c)
-
diff --git a/tests/objects/toop1.nim b/tests/objects/toop1.nim
index f4880e3c6..3e9b24990 100644
--- a/tests/objects/toop1.nim
+++ b/tests/objects/toop1.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "toop1.nim"
   output: "34[]o 5"
 """
 # Test the stuff in the tutorial
@@ -35,7 +34,7 @@ proc init(my: var TRectangle) =
   my.height = 10
   my.draw = cast[proc (my: var TFigure) {.nimcall.}](drawRectangle)
 
-macro `!` (n: untyped): typed {.immediate.}=
+macro `!` (n: varargs[untyped]): typed =
   let n = callsite()
   result = newNimNode(nnkCall, n)
   var dot = newNimNode(nnkDotExpr, n)
diff --git a/tests/objects/trefobjsyntax.nim b/tests/objects/trefobjsyntax.nim
deleted file mode 100644
index 9b48de718..000000000
--- a/tests/objects/trefobjsyntax.nim
+++ /dev/null
@@ -1,27 +0,0 @@
-discard """
-  output: '''wohoo
-baz'''
-"""
-
-# Test to ensure the popular 'ref T' syntax works everywhere
-
-type
-  Foo = object
-    a, b: int
-    s: string
-
-  FooBar = object of RootObj
-    n, m: string
-  Baz = object of FooBar
-
-proc invoke(a: ref Baz) =
-  echo "baz"
-
-# check object construction:
-let x = (ref Foo)(a: 0, b: 45, s: "wohoo")
-echo x.s
-
-var y: ref FooBar = (ref Baz)(n: "n", m: "m")
-
-invoke((ref Baz)(y))
-
diff --git a/tests/objects/trefobjsyntax2.nim b/tests/objects/trefobjsyntax2.nim
deleted file mode 100644
index 8ee209cc7..000000000
--- a/tests/objects/trefobjsyntax2.nim
+++ /dev/null
@@ -1,19 +0,0 @@
-# bug #2508
-
-type
-  GenericNodeObj[T] = ref object
-    obj: T
-
-  Node* = ref object
-    children*: seq[Node]
-    parent*: Node
-
-    nodeObj*: GenericNodeObj[int]
-
-proc newNode*(nodeObj: GenericNodeObj): Node =
-    result = Node(nodeObj: nodeObj)
-    newSeq(result.children, 10)
-
-var genericObj = GenericNodeObj[int]()
-
-var myNode = newNode(genericObj)
diff --git a/tests/objects/trefobjsyntax3.nim b/tests/objects/trefobjsyntax3.nim
deleted file mode 100644
index 2d466eeda..000000000
--- a/tests/objects/trefobjsyntax3.nim
+++ /dev/null
@@ -1,28 +0,0 @@
-# bug #2540
-
-type
-  BaseSceneNode[T] = ref object of RootObj
-    children*: seq[BaseSceneNode[T]]
-    parent*: BaseSceneNode[T]
-
-  SceneNode[T] = ref object of BaseSceneNode[T]
-
-  SomeObj = ref object
-
-proc newSceneNode[T](): SceneNode[T] =
-  new result
-  result.children = @[]
-
-var aNode = newSceneNode[SomeObj]()
-
-
-# bug #3038
-
-type
-  Data[T] = ref object of RootObj
-    data: T
-  Type = ref object of RootObj
-  SubType[T] = ref object of Type
-    data: Data[T]
-  SubSubType = ref object of SubType
-  SubSubSubType = ref object of SubSubType
diff --git a/tests/objects/trequireinit.nim b/tests/objects/trequireinit.nim
new file mode 100644
index 000000000..202667b02
--- /dev/null
+++ b/tests/objects/trequireinit.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "The MPlayerObj type doesn't have a default value. The following fields must be initialized: foo."
+"""
+
+type
+  MPlayerObj* {.requiresInit.} = object
+    foo: range[5..10] = 5
+
+var a: MPlayerObj
+echo a.foo
\ No newline at end of file
diff --git a/tests/objects/tunsafenew.nim b/tests/objects/tunsafenew.nim
new file mode 100644
index 000000000..6c1b33cd9
--- /dev/null
+++ b/tests/objects/tunsafenew.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "conversion from int literal(-1) to Natural is invalid"
+"""
+
+type
+  Obj = object
+    case b: bool
+    else: discard
+var o: ref Obj
+unsafeNew(o, -1)
\ No newline at end of file
diff --git a/tests/objects/tunsafenew2.nim b/tests/objects/tunsafenew2.nim
new file mode 100644
index 000000000..83112bcfc
--- /dev/null
+++ b/tests/objects/tunsafenew2.nim
@@ -0,0 +1,15 @@
+discard """
+valgrind: "leaks"
+matrix: "-d:useMalloc"
+targets: "c cpp"
+"""
+
+type
+  Obj = object
+    case b: bool
+    else: discard
+    a: UncheckedArray[byte]
+
+var o: ref Obj
+unsafeNew(o, sizeof(Obj) + 512)
+zeroMem(addr o.a, 512)
diff --git a/tests/objects/twhen1.nim b/tests/objects/twhen1.nim
new file mode 100644
index 000000000..fe072a46b
--- /dev/null
+++ b/tests/objects/twhen1.nim
@@ -0,0 +1,89 @@
+const Z = 0
+
+type
+  Foo[T] = object
+   when true:
+     u: int
+   else:
+     v: int
+  Foo1[T] = object
+   when T is int:
+     x: T
+   elif true:
+     z: char
+  Foo2[x:static[int]] = object
+    when (x and 1) == 1:
+      x: array[x+1,int]
+    else:
+      x: array[x,int]
+
+  Foo3 = Foo2[128]
+
+  # #8417
+  Foo4[A: static[int]] = object
+    when Z == 0:
+      discard
+    else:
+      discard
+
+block:
+  var x: Foo[int] = Foo[int](u: 42)
+  doAssert x.u == 42
+
+# Don't evaluate `when` branches before the type is instantiated
+block:
+  var x: Foo1[bool] = Foo1[bool](z: 'o')
+  doAssert x.z == 'o'
+
+block:
+  var x: Foo2[3]
+  doAssert x.x.len == 4
+
+block:
+  var x: Foo2[4]
+  doAssert x.x.len == 4
+
+block:
+  var x: Foo3
+  doAssert x.x.len == 128
+
+block:
+  var x: Foo4[0]
+
+type
+  MyObject = object
+    x: int
+    when (NimMajor, NimMinor) >= (1, 1):
+      y: int
+discard MyObject(x: 100, y: 200)
+
+block: # Ensure when evaluates properly in objects
+  type X[bits: static int] = object #22474
+    when bits >= 256:
+     data32: byte
+    else:
+     data16: byte
+
+  static:
+    discard X[255]().data16
+    discard X[256]().data32
+
+
+  type ComplexExprObject[S: static string, I: static int, Y: static auto] = object
+    when 'h' in S and I < 10 and Y isnot float:
+      a: int
+    elif I > 30:
+      b: int
+    elif typeof(Y) is float:
+      c: int
+    else:
+      d: int
+
+  static:
+    discard ComplexExprObject["hello", 9, 300i32]().a
+    discard ComplexExprObject["", 40, 30f]().b
+    discard ComplexExprObject["", 20, float 30]().c
+    discard ComplexExprObject["", 20, ""]().d
+
+
+
diff --git a/tests/objvariant/t14581.nim b/tests/objvariant/t14581.nim
new file mode 100644
index 000000000..72ba32f18
--- /dev/null
+++ b/tests/objvariant/t14581.nim
@@ -0,0 +1,25 @@
+discard """
+  matrix: "--gc:refc; --gc:arc"
+  output: "abc: @[(kind: A, x: 0)]"
+"""
+
+import std/tables
+
+type E = enum
+  A, B
+
+type O = object
+  case kind: E
+  of A:
+    x: int
+  of B:
+    y: int 
+
+proc someTable(): Table[string, seq[O]] =
+  result = initTable[string, seq[O]]()
+  result["abc"] = @[O(kind: A)]
+
+const t = someTable()
+
+for k, v in t:
+  echo k, ": ", v
diff --git a/tests/objvariant/tadrdisc.nim b/tests/objvariant/tadrdisc.nim
index 258fb42f3..5e4e39a44 100644
--- a/tests/objvariant/tadrdisc.nim
+++ b/tests/objvariant/tadrdisc.nim
@@ -1,9 +1,9 @@
 discard """
+  errormsg: "type mismatch: got <TKind>"
   file: "tadrdisc.nim"
   line: 20
-  errormsg: "type mismatch: got <TKind>"
 """
-# Test that the address of a dicriminants cannot be taken
+# Test that the address of a discriminants cannot be taken
 
 type
   TKind = enum ka, kb, kc
diff --git a/tests/objvariant/tcheckedfield1.nim b/tests/objvariant/tcheckedfield1.nim
index a7f232c5b..4a6c49f66 100644
--- a/tests/objvariant/tcheckedfield1.nim
+++ b/tests/objvariant/tcheckedfield1.nim
@@ -1,6 +1,7 @@
 discard """
-  msg: "Warning: cannot prove that field 'x.s' is accessible [ProveField]"
-  line:51
+  nimout: "tcheckedfield1.nim(39, 6) Warning: cannot prove that field 'x.s' is accessible [ProveField]"
+  action: run
+  output: "abc abc"
 """
 
 import strutils
diff --git a/tests/objvariant/tconstobjvariant.nim b/tests/objvariant/tconstobjvariant.nim
new file mode 100644
index 000000000..45a647707
--- /dev/null
+++ b/tests/objvariant/tconstobjvariant.nim
@@ -0,0 +1,18 @@
+# This is a sample code, the first echo statement prints out the error
+type
+  A = object
+    case w: uint8
+    of 1:
+      n: int
+    else:
+      other: string
+
+const
+  a = A(w: 1, n: 5)
+
+proc foo =
+
+  let c = [a]
+  doAssert c[0].n == 5
+
+foo()
\ No newline at end of file
diff --git a/tests/objvariant/tconstructionorder.nim b/tests/objvariant/tconstructionorder.nim
index 19ddea7a1..5ca484884 100644
--- a/tests/objvariant/tconstructionorder.nim
+++ b/tests/objvariant/tconstructionorder.nim
@@ -23,22 +23,22 @@ type
 # This will test that all the values are what we expect.
 proc assertTree(root: Node) =
   # check root of tree
-  assert root.kind == Operator
-  assert root.operator == '*'
+  doAssert root.kind == Operator
+  doAssert root.operator == '*'
 
   # check left subtree
-  assert root.left.value == 5
-  assert root.left.kind == Literal
+  doAssert root.left.value == 5
+  doAssert root.left.kind == Literal
 
   # check right subtree
-  assert root.right.kind == Operator
-  assert root.right.operator == '+'
+  doAssert root.right.kind == Operator
+  doAssert root.right.operator == '+'
 
-  assert root.right.left.value == 5
-  assert root.right.left.kind == Literal
+  doAssert root.right.left.value == 5
+  doAssert root.right.left.kind == Literal
 
-  assert root.right.right.value == 10
-  assert root.right.right.kind == Literal
+  doAssert root.right.right.value == 10
+  doAssert root.right.right.kind == Literal
 
 proc newLiteralNode(value: int): Node =
   result = Node(
diff --git a/tests/objvariant/temptycaseobj.nim b/tests/objvariant/temptycaseobj.nim
index 53171e054..2b2c40514 100644
--- a/tests/objvariant/temptycaseobj.nim
+++ b/tests/objvariant/temptycaseobj.nim
@@ -1,6 +1,6 @@
 discard """
-  line: 11
   errormsg: "identifier expected, but got 'keyword of'"
+  line: 11
 """
 
 type
@@ -10,5 +10,3 @@ type
     of enA:
     of enU: x, y: int
     of enO: a, b: string
-
-
diff --git a/tests/objvariant/tfloatrangeobj.nim b/tests/objvariant/tfloatrangeobj.nim
new file mode 100644
index 000000000..67e886302
--- /dev/null
+++ b/tests/objvariant/tfloatrangeobj.nim
@@ -0,0 +1,14 @@
+discard """
+  output: '''(kind: 2.0, twoStr: "TWO STR")
+(kind: 1.0)
+'''
+disabled: "true"
+"""
+type
+  FloatRange = range[1.0..3.0]
+  VariantObj = object
+    case kind: FloatRange
+    of 2.0: twoStr: string
+
+echo VariantObj(kind: 2.0, twoStr: "TWO STR")
+echo VariantObj(kind: 1.0)
diff --git a/tests/objvariant/tnon_zero_discrim_err.nim b/tests/objvariant/tnon_zero_discrim_err.nim
new file mode 100644
index 000000000..c595bba1b
--- /dev/null
+++ b/tests/objvariant/tnon_zero_discrim_err.nim
@@ -0,0 +1,14 @@
+discard """
+  errormsg: "low(kind) must be 0 for discriminant"
+  line: 7
+"""
+type
+  HoledObj = object
+    case kind: int
+    of 0: a: int
+    else: discard
+
+let someInt = low(int)
+case someInt
+of 938: echo HoledObj(kind: someInt, a: 1)
+else: discard
diff --git a/tests/objvariant/treassign.nim b/tests/objvariant/treassign.nim
index 2938b30a3..527204616 100644
--- a/tests/objvariant/treassign.nim
+++ b/tests/objvariant/treassign.nim
@@ -25,3 +25,34 @@ t.curr = TokenObject(kind: Token.bar, bar: BasicNumber(value: 12.34))
 t.curr = TokenObject(kind: Token.foo, foo: "foo")
 
 echo "SUCCESS"
+
+proc passToVar(x: var Token) = discard
+
+{.cast(uncheckedAssign).}:
+  passToVar(t.curr.kind)
+
+  t.curr = TokenObject(kind: t.curr.kind, foo: "abc")
+
+  t.curr.kind = Token.foo
+
+
+block:
+  type
+    TokenKind = enum
+      strLit, intLit
+    Token = object
+      case kind*: TokenKind
+      of strLit:
+        s*: string
+      of intLit:
+        i*: int64
+
+  var t = Token(kind: strLit, s: "abc")
+
+  {.cast(uncheckedAssign).}:
+
+    # inside the 'cast' section it is allowed to assign to the 't.kind' field directly:
+    t.kind = intLit
+
+  {.cast(uncheckedAssign).}:
+    t.kind = strLit
\ No newline at end of file
diff --git a/tests/objvariant/trt_discrim.nim b/tests/objvariant/trt_discrim.nim
new file mode 100644
index 000000000..95b2a9f76
--- /dev/null
+++ b/tests/objvariant/trt_discrim.nim
@@ -0,0 +1,198 @@
+template accept(x) =
+  static: assert(compiles(x))
+
+template reject(x) =
+  static: assert(not compiles(x))
+
+type
+  Kind = enum k1 = 0, k2 = 33, k3 = 84, k4 = 278, k5 = 1000 # Holed enum work! #No they don't..
+  KindObj = object
+    case kind: Kind
+    of k1, k2..k3: i32: int32
+    of k4: f32: float32
+    else: str: string
+
+  IntObj = object
+    case kind: uint8
+    of low(uint8) .. 127: bad: string
+    of 128'u8: neutral: string
+    of 129 .. high(uint8): good: string
+
+  OtherKind = enum ok1, ok2, ok3, ok4, ok5
+  NestedKindObj = object
+    case kind: Kind
+    of k3, k5: discard
+    of k2: str: string
+    of k1, k4:
+      case otherKind: OtherKind
+      of ok1, ok2..ok3: i32: int32
+      of ok4: f32: float32
+      else: nestedStr: string
+
+let kind = k4 # actual value should have no impact on the analysis.
+
+accept: # Mimics the structure of the type. The optimial case.
+  case kind
+  of k1, k2, k3: discard KindObj(kind: kind, i32: 1)
+  of k4: discard KindObj(kind: kind, f32: 2.0)
+  else: discard KindObj(kind: kind, str: "3")
+
+accept: # Specifying the else explicitly is fine too.
+  case kind
+  of k1, k2, k3: discard KindObj(kind: kind, i32: 1)
+  of k4: discard KindObj(kind: kind, f32: 2.0)
+  of k5: discard KindObj(kind: kind, str: "3")
+
+accept:
+  case kind
+  of k1..k3, k5: discard
+  else: discard KindObj(kind: kind, f32: 2.0)
+
+accept:
+  case kind
+  of k4, k5: discard
+  else: discard KindObj(kind: kind, i32: 1)
+
+accept: # elif branches are ignored
+  case kind
+  of k1, k2, k3: discard KindObj(kind: kind, i32: 1)
+  of k4: discard KindObj(kind: kind, f32: 2.0)
+  elif kind in {k1..k5}: discard
+  else: discard KindObj(kind: kind, str: "3")
+
+reject: # k4 conflicts with i32
+  case kind
+  of k1, k2, k3, k4: discard KindObj(kind: kind, i32: 1)
+  else: discard KindObj(kind: kind, str: "3")
+
+reject: # k4 is not caught, conflicts with str in the else branch
+  case kind
+  of k1, k2, k3: discard KindObj(kind: kind, i32: 1)
+  else: discard KindObj(kind: kind, str: "3")
+
+reject: # elif branches are ignored
+  case kind
+  of k1, k2, k3: discard KindObj(kind: kind, i32: 1)
+  elif kind == k4: discard
+  else: discard KindObj(kind: kind, str: "3")
+
+let intKind = 29'u8
+
+accept:
+  case intKind
+  of low(uint8) .. 127: discard IntObj(kind: intKind, bad: "bad")
+  of 128'u8: discard IntObj(kind: intKind, neutral: "neutral")
+  of 129 .. high(uint8): discard IntObj(kind: intKind, good: "good")
+
+reject: # 0 leaks to else
+  case intKind
+  of low(uint8) .. 127: discard IntObj(kind: intKind, bad: "bad")
+  of 129 .. high(uint8): discard IntObj(kind: intKind, good: "good")
+
+accept:
+  case intKind
+  of low(uint8) .. 127: discard IntObj(kind: intKind, bad: "bad")
+  of 128'u8: discard IntObj(kind: intKind, neutral: "neutral")
+  of 139'u8, 140 .. high(uint8), 129'u8 .. 138'u8: discard IntObj(kind: intKind, good: "good")
+
+
+accept:
+  case kind
+  of {k1, k2}, [k3]: discard KindObj(kind: kind, i32: 1)
+  of k4: discard KindObj(kind: kind, f32: 2.0)
+  else: discard KindObj(kind: kind, str: "3")
+
+reject:
+  case kind
+  of {k1, k2, k3}, [k4]: discard KindObj(kind: kind, i32: 1)
+  else: discard KindObj(kind: kind, str: "3")
+
+accept:
+  case kind
+  of k3, k5: discard NestedKindObj(kind: kind)
+  of k2: discard NestedKindObj(kind: kind, str: "not nested")
+  of k1, k4:
+    let otherKind = ok5
+    case otherKind
+    of ok1..ok3: discard NestedKindObj(kind: kind, otherKind: otherKind, i32: 3)
+    of ok4: discard NestedKindObj(kind: kind, otherKind: otherKind, f32: 5.0)
+    else: discard NestedKindObj(kind: kind, otherKind: otherKind,
+                                nestedStr: "nested")
+
+reject:
+  case kind
+  of k3, k5: discard NestedKindObj(kind: kind)
+  of k2: discard NestedKindObj(kind: kind, str: "not nested")
+  of k1, k4:
+    let otherKind = ok5
+    case otherKind
+    of ok1..ok3: discard NestedKindObj(kind: kind, otherKind: otherKind, i32: 3)
+    else: discard NestedKindObj(kind: kind, otherKind: otherKind,
+                                nestedStr: "nested")
+
+var varkind = k4
+
+reject: # not immutable.
+  case varkind
+  of k1, k2, k3: discard KindObj(varkind: kind, i32: 1)
+  of k4: discard KindObj(varkind: kind, f32: 2.0)
+  else: discard KindObj(varkind: kind, str: "3")
+
+accept:
+  proc kindProc(kind: Kind): KindObj =
+    case kind:
+    of k1: result = KindObj(kind: kind, i32: 1)
+    else: discard
+
+reject:
+  proc varKindProc(kind: var Kind): KindObj =
+    case kind:
+    of k1: result = KindObj(kind: kind, i32: 1)
+    else: discard
+
+type
+  Kind3 = enum
+    A, B, C, E
+
+  OkRange = range[B..C]
+  NotOkRange = range[B..E]
+
+  CaseObject = object
+    case kind: Kind3
+    of B, C:
+      field: int
+    else: discard
+
+accept:
+  let rtDiscriminator: OkRange = B
+  discard CaseObject(kind: rtDiscriminator, field: 1)
+
+accept:
+  let rtDiscriminator = B
+  discard CaseObject(kind: OkRange(rtDiscriminator), field: 1)
+
+accept:
+  const rtDiscriminator: NotOkRange = B
+  discard CaseObject(kind: rtDiscriminator, field: 1)
+
+accept:
+  discard CaseObject(kind: NotOkRange(B), field: 1)
+
+reject:
+  let rtDiscriminator: NotOkRange = B
+  discard CaseObject(kind: rtDiscriminator, field: 1)
+
+reject:
+  let rtDiscriminator = B
+  discard CaseObject(kind: NotOkRange(rtDiscriminator), field: 1)
+
+reject:
+  type Obj = object
+    case x: int
+    of 0 .. 1000:
+      field: int
+    else:
+      discard
+
+  let x: range[0..15] = 1
+  let o = Obj(x: x, field: 1)
diff --git a/tests/objvariant/trt_discrim_err0.nim b/tests/objvariant/trt_discrim_err0.nim
new file mode 100644
index 000000000..02b551cbc
--- /dev/null
+++ b/tests/objvariant/trt_discrim_err0.nim
@@ -0,0 +1,17 @@
+discard """
+  errormsg: "possible values {k1, k3, k4} are in conflict with discriminator values for selected object branch 3"
+  line: 17
+"""
+
+type
+  Kind = enum k1, k2, k3, k4, k5
+  KindObj = object
+    case kind: Kind
+    of k1, k2..k3: i32: int32
+    of k4: f32: float32
+    else: str: string
+
+let kind = k3
+case kind
+of k2: discard KindObj(kind: kind, i32: 1)
+else: discard KindObj(kind: kind, str: "3")
diff --git a/tests/objvariant/trt_discrim_err1.nim b/tests/objvariant/trt_discrim_err1.nim
new file mode 100644
index 000000000..de29420a2
--- /dev/null
+++ b/tests/objvariant/trt_discrim_err1.nim
@@ -0,0 +1,17 @@
+discard """
+  errormsg: "branch initialization with a runtime discriminator is not supported inside of an `elif` branch."
+  line: 16
+"""
+type
+  Color = enum Red, Green, Blue
+  ColorObj = object
+    case colorKind: Color
+    of Red: red: string
+    of Green: green: string
+    of Blue: blue: string
+
+let colorKind = Blue
+case colorKind
+of Red: echo ColorObj(colorKind: colorKind, red: "red")
+elif colorKind == Green: echo ColorObj(colorKind: colorKind, green: "green")
+else: echo ColorObj(colorKind: colorKind, blue: "blue")
diff --git a/tests/objvariant/trt_discrim_err2.nim b/tests/objvariant/trt_discrim_err2.nim
new file mode 100644
index 000000000..4f2790bc6
--- /dev/null
+++ b/tests/objvariant/trt_discrim_err2.nim
@@ -0,0 +1,14 @@
+discard """
+  errormsg: " branch initialization with a runtime discriminator only supports ordinal types with 2^16 elements or less."
+  line: 13
+"""
+type
+  HoledObj = object
+    case kind: range[0 .. 20000]
+    of 0: a: int
+    else: discard
+
+let someInt = low(int)
+case someInt
+of 938: echo HoledObj(kind: someInt, a: 1)
+else: discard
diff --git a/tests/objvariant/trt_discrim_err3.nim b/tests/objvariant/trt_discrim_err3.nim
new file mode 100644
index 000000000..e739c3d50
--- /dev/null
+++ b/tests/objvariant/trt_discrim_err3.nim
@@ -0,0 +1,17 @@
+discard """
+  errormsg: "runtime discriminator must be immutable if branch fields are initialized, a 'let' binding is required."
+  line: 16
+"""
+
+type
+  Kind = enum k1, k2, k3, k4, k5
+  KindObj = object
+    case kind: Kind
+    of k1, k2..k3: i32: int32
+    of k4: f32: float32
+    else: str: string
+
+var kind = k3
+case kind
+of k2: discard KindObj(kind: kind, i32: 1)
+else: discard KindObj(kind: kind, str: "3")
diff --git a/tests/objvariant/tvariantstack.nim b/tests/objvariant/tvariantstack.nim
index 0cdde5a20..7fd41b476 100644
--- a/tests/objvariant/tvariantstack.nim
+++ b/tests/objvariant/tvariantstack.nim
@@ -1,6 +1,5 @@
 discard """
-  file: "tvariantstack.nim"
-  output: "came here"
+output: "came here"
 """
 #BUG
 type
@@ -33,15 +32,11 @@ proc pop(Stack: var TStack): TAny =
 
 var stack = newStack()
 
-var s: TAny
-s.kind = nkString
-s.strVal = "test"
+var s = TAny(kind: nkString, strVal: "test")
 
 stack.push(s)
 
-var nr: TAny
-nr.kind = nkint
-nr.intVal = 78
+var nr = TAny(kind: nkInt, intVal: 78)
 
 stack.push(nr)
 
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/openarray/t6163.nim b/tests/openarray/t6163.nim
new file mode 100644
index 000000000..0e9d4e0e4
--- /dev/null
+++ b/tests/openarray/t6163.nim
@@ -0,0 +1,17 @@
+discard """
+  exitcode: 0
+  targets: "c cpp js"
+  output: '''19316
+'''
+"""
+
+from sugar import `->`, `=>`
+from math import `^`, sum
+from sequtils import filter, map, toSeq
+
+proc f: int =
+  toSeq(10..<10_000).filter(a => a == ($a).map(d => (d.ord-'0'.ord).int^4).sum).sum
+
+var a = f()
+
+echo a
diff --git a/tests/openarray/t8259.nim b/tests/openarray/t8259.nim
new file mode 100644
index 000000000..283c6cd02
--- /dev/null
+++ b/tests/openarray/t8259.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "invalid type: 'openArray[int]' for result"
+  line: 6
+"""
+
+proc foo(a: openArray[int]):auto = a
+echo foo(toOpenArray([1, 2], 0, 2))
diff --git a/tests/openarray/topena1.nim b/tests/openarray/topena1.nim
index 0dbc5506a..380c57f2a 100644
--- a/tests/openarray/topena1.nim
+++ b/tests/openarray/topena1.nim
@@ -1,12 +1,9 @@
 discard """
+  errormsg: "invalid type"
   file: "topena1.nim"
   line: 9
-  errormsg: "invalid type"
 """
 # Tests a special bug
 
 var
-  x: ref openarray[string] #ERROR_MSG invalid type
-
-
-
+  x: ref openArray[string] #ERROR_MSG invalid type
diff --git a/tests/openarray/topenarray.nim b/tests/openarray/topenarray.nim
new file mode 100644
index 000000000..25b983651
--- /dev/null
+++ b/tests/openarray/topenarray.nim
@@ -0,0 +1,88 @@
+discard """
+  targets: "c cpp js"
+"""
+
+proc fn1[T](a: openArray[T]): seq[T] =
+  for ai in a: result.add ai
+
+proc fn2[T](a: var openArray[T]): seq[T] =
+  for ai in a: result.add ai
+
+proc fn3[T](a: var openArray[T]) =
+  for i, ai in mpairs(a): ai = i * 10
+
+proc main =
+  var a = [1,2,3,4,5]
+
+  doAssert fn1(a.toOpenArray(1,3)) == @[2,3,4]
+
+  doAssert fn2(toOpenArray(a, 1, 3)) == @[2,3,4]
+  doAssert fn2(a.toOpenArray(1,3)) == @[2,3,4]
+
+  fn3(a.toOpenArray(1,3))
+  when defined(js): discard # xxx bug #15952: `a` left unchanged
+  else: doAssert a == [1, 0, 10, 20, 5]
+
+  block: # bug #12521
+    block:
+      type slice[T] = openArray[T]
+
+      # Proc using that alias
+      proc testing(sl: slice[int]): seq[int] =
+        for item in sl:
+          result.add item
+
+      let mySeq = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
+      doAssert testing(mySeq) == mySeq
+      doAssert testing(mySeq[2..^2]) == mySeq[2..^2]
+
+    block:
+      type slice = openArray[int]
+
+      # Proc using that alias
+      proc testing(sl: slice): seq[int] =
+        for item in sl:
+          result.add item
+
+      let mySeq = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
+      doAssert testing(mySeq) == mySeq
+      doAssert testing(mySeq[2..^2]) == mySeq[2..^2]
+
+  block: # bug #23321
+    block:
+      proc foo(x: openArray[int]) =
+        doAssert x[0] == 0
+
+      var d = new array[1, int]
+      foo d[].toOpenArray(0, 0)
+
+    block:
+      proc foo(x: openArray[int]) =
+        doAssert x[0] == 0
+
+      proc task(x: var array[1, int]): var array[1, int] =
+        result = x
+      var d: array[1, int]
+      foo task(d).toOpenArray(0, 0)
+
+    block:
+      proc foo(x: openArray[int]) =
+        doAssert x[0] == 0
+
+      proc task(x: var array[1, int]): lent array[1, int] =
+        result = x
+      var d: array[1, int]
+      foo task(d).toOpenArray(0, 0)
+
+    block:
+      proc foo(x: openArray[int]) =
+        doAssert x[0] == 0
+
+      proc task(x: var array[1, int]): ptr array[1, int] =
+        result = addr x
+      var d: array[1, int]
+      foo task(d)[].toOpenArray(0, 0)
+
+
+main()
+static: main()
diff --git a/tests/openarray/topenarrayrepr.nim b/tests/openarray/topenarrayrepr.nim
index d276756bc..fc40d88c3 100644
--- a/tests/openarray/topenarrayrepr.nim
+++ b/tests/openarray/topenarrayrepr.nim
@@ -1,17 +1,13 @@
 discard """
-  file: "topenarrayrepr.nim"
   output: "5 - [1]"
 """
 type
-  TProc = proc (n: int, m: openarray[int64]) {.nimcall.}
+  TProc = proc (n: int, m: openArray[int64]) {.nimcall.}
 
 proc Foo(x: int, P: TProc) =
   P(x, [ 1'i64 ])
 
-proc Bar(n: int, m: openarray[int64]) =
+proc Bar(n: int, m: openArray[int64]) =
   echo($n & " - " & repr(m))
 
 Foo(5, Bar) #OUT 5 - [1]
-
-
-
diff --git a/tests/openarray/topenlen.nim b/tests/openarray/topenlen.nim
index fec8e87b7..164241b85 100644
--- a/tests/openarray/topenlen.nim
+++ b/tests/openarray/topenlen.nim
@@ -1,12 +1,11 @@
 discard """
-  file: "topenlen.nim"
   output: "7"
 """
 # Tests a special bug
 
 proc choose(b: openArray[string]): string = return b[0]
 
-proc p(a, b: openarray[string]): int =
+proc p(a, b: openArray[string]): int =
   result = a.len + b.len - 1
   for j in 0 .. a.len: inc(result)
   discard choose(a)
@@ -14,5 +13,3 @@ proc p(a, b: openarray[string]): int =
 
 discard choose(["sh", "-c", $p([""], ["a"])])
 echo($p(["", "ha", "abc"], ["xyz"])) #OUT 7
-
-
diff --git a/tests/openarray/tptrarrayderef.nim b/tests/openarray/tptrarrayderef.nim
index 1e73be108..1b7ef0df0 100644
--- a/tests/openarray/tptrarrayderef.nim
+++ b/tests/openarray/tptrarrayderef.nim
@@ -1,6 +1,9 @@
 discard """
-  file: "tptrarrayderef.nim"
-  output: "OK"
+  output: '''[1, 2, 3, 4]
+3
+['1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C']
+OK
+'''
 """
 
 var
@@ -12,14 +15,14 @@ var
   raa = [11,12,13]
 
 #bug #3586
-proc mutate[T](arr:openarray[T], brr: openArray[T]) =
+proc mutate[T](arr:openArray[T], brr: openArray[T]) =
   for i in 0..arr.len-1:
     doAssert(arr[i] == brr[i])
-    
+
 mutate(arr, arr)
 
 #bug #2240
-proc f(a: openarray[int], b: openArray[int]) =
+proc f(a: openArray[int], b: openArray[int]) =
   for i in 0..a.len-1:
    doAssert(a[i] == b[i])
 
@@ -34,7 +37,7 @@ ra[2] = 13
 f(ra[], raa)
 
 #bug #2240b
-proc fillBuffer(buf: var openarray[char]) =
+proc fillBuffer(buf: var openArray[char]) =
   for i in 0..buf.len-1:
     buf[i] = chr(i)
 
@@ -46,9 +49,36 @@ proc getFilledBuffer(sz: int): ref seq[char] =
   s[] = newSeq[char](sz)
   fillBuffer(s[])
   return s
-  
+
 let aa = getFilledBuffer(3)
 for i in 0..aa[].len-1:
   doAssert(aa[i] == chr(i))
-  
-echo "OK"
\ No newline at end of file
+
+var
+  x = [1, 2, 3, 4]
+  y1 = block: (
+    a: (block:
+      echo x
+      cast[ptr array[2, int]](addr(x[0]))[]),
+    b: 3)
+  y2 = block:
+    echo y1.a[0] + y1.a[1]
+    cast[ptr array[4, int]](addr(x))[]
+doAssert y1 == ([1, 2], 3)
+doAssert y2 == [1, 2, 3, 4]
+
+template newOpenArray(x: var string, size: int): openArray[char] =
+  var z = 1
+  toOpenArray(x, z, size)
+
+template doSomethingAndCreate(x: var string): openArray[char] =
+  let size = 12
+  newOpenArray(x, size)
+
+proc sinkk(x: openArray[char]) =
+  echo x
+
+var xArrayDeref = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+sinkk doSomethingAndCreate(xArrayDeref)
+
+echo "OK"
diff --git a/tests/openarray/tuncheckedarray.nim b/tests/openarray/tuncheckedarray.nim
new file mode 100644
index 000000000..c8ca9d2d4
--- /dev/null
+++ b/tests/openarray/tuncheckedarray.nim
@@ -0,0 +1,19 @@
+discard """
+  exitcode: 0
+  targets: "c cpp"
+"""
+
+proc main =
+  block: # issue 19171
+    var a = ['A']
+    proc mutB(x: var openArray[char]) =
+      x[0] = 'B'
+    mutB(toOpenArray(cast[ptr UncheckedArray[char]](addr a), 0, 0))
+    doAssert a[0] == 'B'
+    proc mutC(x: var openArray[char]; c: char) =
+      x[0] = c
+    let p = cast[ptr UncheckedArray[char]](addr a)
+    mutC(toOpenArray(p, 0, 0), 'C')
+    doAssert p[0] == 'C'
+
+main()
diff --git a/tests/options/tnimbasepattern.nim b/tests/options/tnimbasepattern.nim
new file mode 100644
index 000000000..1237af5c5
--- /dev/null
+++ b/tests/options/tnimbasepattern.nim
@@ -0,0 +1,26 @@
+discard """
+  cmd: "nim cpp --nimbasepattern:test.h --cincludes:./tests/options $file "
+  output:'''
+(a: 1)
+'''
+"""
+const header = """
+#pragma once
+#include "nimbase.h"
+struct Foo {
+  int a;
+};
+"""
+
+import os
+static:
+  const dir = "./tests/options/"
+  createDir(dir)
+  writeFile(dir / "test.h", header)
+
+type 
+  Foo {.importc.} = object
+    a: int32 = 1
+  
+
+echo $Foo()
\ No newline at end of file
diff --git a/tests/osproc/passenv.nim b/tests/osproc/passenv.nim
index 815f7536f..40b1c3f7c 100644
--- a/tests/osproc/passenv.nim
+++ b/tests/osproc/passenv.nim
@@ -1,7 +1,7 @@
 discard """
   file: "passenv.nim"
   output: "123"
-  targets: "c c++ objc"
+  targets: "c cpp objc"
 """
 
 import osproc, os, strtabs
diff --git a/tests/osproc/ta_in.nim b/tests/osproc/ta_in.nim
deleted file mode 100644
index b46890f6e..000000000
--- a/tests/osproc/ta_in.nim
+++ /dev/null
@@ -1,5 +0,0 @@
-# This file is prefixed with an "a", because other tests
-# depend on it and it must be compiled first.
-import strutils
-let x = stdin.readLine()
-echo x.parseInt + 5
diff --git a/tests/osproc/ta_out.nim b/tests/osproc/ta_out.nim
deleted file mode 100644
index f7091a7f6..000000000
--- a/tests/osproc/ta_out.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-# This file is prefixed with an "a", because other tests
-# depend on it and it must be compiled first.
-stdout.writeLine("to stdout")
-stdout.flushFile()
-stdout.writeLine("to stdout")
-stdout.flushFile()
-
-stderr.writeLine("to stderr")
-stderr.flushFile()
-stderr.writeLine("to stderr")
-stderr.flushFile()
-
-stdout.writeLine("to stdout")
-stdout.flushFile()
-stdout.writeLine("to stdout")
-stdout.flushFile()
diff --git a/tests/osproc/tafalse.nim b/tests/osproc/tafalse.nim
deleted file mode 100644
index 24fd4fb2e..000000000
--- a/tests/osproc/tafalse.nim
+++ /dev/null
@@ -1,3 +0,0 @@
-# 'tafalse.nim' to ensure it is compiled before texitcode.nim
-import system
-quit(QuitFailure)
diff --git a/tests/osproc/tclose.nim b/tests/osproc/tclose.nim
index d466b466a..1c99237c7 100644
--- a/tests/osproc/tclose.nim
+++ b/tests/osproc/tclose.nim
@@ -13,12 +13,12 @@ when defined(linux):
   let initCount = countFds()
 
   let p = osproc.startProcess("echo", options={poUsePath})
-  assert countFds() == initCount + 3
+  doAssert countFds() == initCount + 3
   p.close
-  assert countFds() == initCount
+  doAssert countFds() == initCount
 
   let p1 = osproc.startProcess("echo", options={poUsePath})
   discard p1.inputStream
-  assert countFds() == initCount + 3
+  doAssert countFds() == initCount + 3
   p.close
-  assert countFds() == initCount
+  doAssert countFds() == initCount
diff --git a/tests/osproc/texecps.nim b/tests/osproc/texecps.nim
index 887d79bfb..b818fe8eb 100644
--- a/tests/osproc/texecps.nim
+++ b/tests/osproc/texecps.nim
@@ -1,6 +1,5 @@
 discard """
-  file: "texecps.nim"
-  output: ""
+joinable: false
 """
 
 import osproc, streams, strutils, os
@@ -14,8 +13,7 @@ proc execCb(idx: int, p: Process) =
   if exitCode < len(gResults):
     gResults[exitCode] = p.outputStream.readAll.strip
 
-when isMainModule:
-
+when true:
   if paramCount() == 0:
     gResults = newSeq[string](NumberOfProcesses)
     var checks = newSeq[string](NumberOfProcesses)
diff --git a/tests/osproc/texitcode.nim b/tests/osproc/texitcode.nim
deleted file mode 100644
index 4eaab6da2..000000000
--- a/tests/osproc/texitcode.nim
+++ /dev/null
@@ -1,23 +0,0 @@
-discard """
-  file: "texitcode.nim"
-  output: ""
-"""
-import osproc, os
-
-const filename = when defined(Windows): "tafalse.exe" else: "tafalse"
-let dir = getCurrentDir() / "tests" / "osproc"
-doAssert fileExists(dir / filename)
-
-var p = startProcess(filename, dir)
-doAssert(waitForExit(p) == QuitFailure)
-
-p = startProcess(filename, dir)
-var running = true
-while running:
-  running = running(p)
-doAssert(waitForExit(p) == QuitFailure)
-
-# make sure that first call to running() after process exit returns false
-p = startProcess(filename, dir)
-os.sleep(500)
-doAssert(not running(p))
diff --git a/tests/osproc/texitsignal.nim b/tests/osproc/texitsignal.nim
index c0bed70ee..fbf5068ea 100644
--- a/tests/osproc/texitsignal.nim
+++ b/tests/osproc/texitsignal.nim
@@ -28,9 +28,12 @@ if paramCount() == 0:
     # windows kill happens using TerminateProcess(h, 0), so we should get a
     # 0 here
     echo p.waitForExit() == 0
+  elif defined(haiku):
+    # on Haiku, the program main thread receive SIGKILLTHR
+    echo p.waitForExit() == 128 + SIGKILLTHR
   else:
     # on posix (non-windows), kill sends SIGKILL
     echo p.waitForExit() == 128 + SIGKILL
 
 else:
-  sleep(5000)  # should get killed before this
\ No newline at end of file
+  sleep(5000)  # should get killed before this
diff --git a/tests/osproc/tnoexe.nim b/tests/osproc/tnoexe.nim
new file mode 100644
index 000000000..19a3cca67
--- /dev/null
+++ b/tests/osproc/tnoexe.nim
@@ -0,0 +1,27 @@
+discard """
+  output: '''true
+true'''
+"""
+
+import std/osproc
+
+const command = "lsaaa -lah"
+
+try:
+  let process = startProcess(command, options = {poUsePath})
+  discard process.waitForExit()
+except OSError as e:
+  echo e.errorCode != 0
+
+# `poEvalCommand`, invokes the system shell to run the specified command
+try:
+  let process = startProcess(command, options = {poUsePath, poEvalCommand})
+  # linux
+  let exitCode = process.waitForExit()
+  echo exitCode != 0
+except OSError as e:
+  # Because the implementation of `poEvalCommand` on different platforms is inconsistent, 
+  # Linux will not throw an exception, but Windows will throw an exception
+
+  # windows
+  echo e.errorCode != 0
diff --git a/tests/osproc/treadlines.nim b/tests/osproc/treadlines.nim
new file mode 100644
index 000000000..bb6a7f129
--- /dev/null
+++ b/tests/osproc/treadlines.nim
@@ -0,0 +1,23 @@
+discard """
+  output: '''
+Error: cannot open 'a.nim'
+Error: cannot open 'b.nim'
+'''
+  targets: "c"
+"""
+
+import osproc
+from std/os import getCurrentCompilerExe
+
+var ps: seq[Process] # compile & run 2 progs in parallel
+const nim = getCurrentCompilerExe()
+for prog in ["a", "b"]:
+  ps.add startProcess(nim, "",
+                      ["r", "--hint:Conf:off", "--hint:Processing:off", prog],
+                      options = {poUsePath, poDaemon, poStdErrToStdOut})
+
+for p in ps:
+  let (lines, exCode) = p.readLines
+  if exCode != 0:
+    for line in lines: echo line
+  p.close
diff --git a/tests/osproc/tstdin.nim b/tests/osproc/tstdin.nim
deleted file mode 100644
index 9b49ed786..000000000
--- a/tests/osproc/tstdin.nim
+++ /dev/null
@@ -1,18 +0,0 @@
-discard """
-  file: "tstdin.nim"
-  output: "10"
-"""
-import osproc, os, streams
-
-const filename = when defined(Windows): "ta_in.exe" else: "ta_in"
-
-doAssert fileExists(getCurrentDir() / "tests" / "osproc" / filename)
-
-var p = startProcess(filename, getCurrentDir() / "tests" / "osproc")
-p.inputStream.write("5\n")
-p.inputStream.flush()
-
-var line = ""
-
-while p.outputStream.readLine(line.TaintedString):
-  echo line
diff --git a/tests/osproc/tstdout.nim b/tests/osproc/tstdout.nim
deleted file mode 100644
index 0cb5bd9c0..000000000
--- a/tests/osproc/tstdout.nim
+++ /dev/null
@@ -1,29 +0,0 @@
-discard """
-  output: '''--------------------------------------
-to stdout
-to stdout
-to stderr
-to stderr
-to stdout
-to stdout
---------------------------------------
-'''
-"""
-import osproc, os, streams
-
-const filename = when defined(Windows): "ta_out.exe" else: "ta_out"
-
-doAssert fileExists(getCurrentDir() / "tests" / "osproc" / filename)
-
-var p = startProcess(filename, getCurrentDir() / "tests" / "osproc",
-                     options={poStdErrToStdOut})
-
-let outputStream = p.outputStream
-var x = newStringOfCap(120)
-var output = ""
-while outputStream.readLine(x.TaintedString):
-  output.add(x & "\n")
-
-echo "--------------------------------------"
-stdout.write output
-echo "--------------------------------------"
diff --git a/tests/osproc/twaitforexit.nim b/tests/osproc/twaitforexit.nim
new file mode 100644
index 000000000..535faca63
--- /dev/null
+++ b/tests/osproc/twaitforexit.nim
@@ -0,0 +1,38 @@
+import std/[osproc, os, times]
+
+block: # bug #5091
+    when defined(linux):
+        const filename = "false"
+        var p = startProcess(filename, options = {poStdErrToStdOut, poUsePath})
+        os.sleep(1000) # make sure process has exited already
+
+        let atStart = getTime()
+        const msWait = 2000
+
+        try:
+            discard waitForExit(p, msWait)
+        except OSError:
+            discard
+
+        # check that we don't have to wait msWait milliseconds
+        doAssert(getTime() <  atStart + milliseconds(msWait))
+
+block: # bug #23825
+
+    # the sleep command might not be available in all Windows installations
+
+    when defined(linux):
+
+        var thr: array[0..99, Thread[int]]
+
+        proc threadFunc(i: int) {.thread.} =
+            let sleepTime = float(i) / float(thr.len + 1)
+            doAssert sleepTime < 1.0
+            let p = startProcess("sleep", workingDir = "", args = @[$sleepTime], options = {poUsePath, poParentStreams})
+            # timeout = 1_000_000 seconds ~= 278 hours ~= 11.5 days
+            doAssert p.waitForExit(timeout=1_000_000_000) == 0
+
+        for i in low(thr)..high(thr):
+            createThread(thr[i], threadFunc, i)
+
+        joinThreads(thr) 
diff --git a/tests/osproc/tworkingdir.nim b/tests/osproc/tworkingdir.nim
index eeed9240d..3a3c1b6ab 100644
--- a/tests/osproc/tworkingdir.nim
+++ b/tests/osproc/tworkingdir.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tworkingdir.nim"
   output: ""
 """
 
@@ -12,6 +11,8 @@ else:
   var process: Process
   when defined(android):
     process = startProcess("/system/bin/env", "/system/bin", ["true"])
+  elif defined(haiku):
+    process = startProcess("/bin/env", "/bin", ["true"])
   else:
     process = startProcess("/usr/bin/env", "/usr/bin", ["true"])
   let dir2 = getCurrentDir()
diff --git a/tests/overflow/tdistinct_range.nim b/tests/overflow/tdistinct_range.nim
new file mode 100644
index 000000000..f53515d45
--- /dev/null
+++ b/tests/overflow/tdistinct_range.nim
@@ -0,0 +1,6 @@
+discard """
+  outputsub: "Error: unhandled exception: over- or underflow [OverflowDefect]"
+  exitcode: "1"
+"""
+var x: distinct range[0..5]
+dec(x)
\ No newline at end of file
diff --git a/tests/overflow/toverflow.nim b/tests/overflow/toverflow.nim
new file mode 100644
index 000000000..972f929c6
--- /dev/null
+++ b/tests/overflow/toverflow.nim
@@ -0,0 +1,82 @@
+discard """
+  output: "ok"
+  matrix: "--overflowChecks:off; --overflowChecks:off --b:js"
+"""
+# Tests nim's ability to detect overflows
+
+{.push overflowChecks: on.}
+
+var
+  a = high(int)
+  b = -2
+  overflowDetected = false
+
+try:
+  echo(b - a)
+except OverflowDefect:
+  overflowDetected = true
+
+{.pop.} # overflow check
+
+doAssert(overflowDetected)
+
+block: # Overflow checks in a proc
+  var
+    a = high(int)
+    b = -2
+    overflowDetected = false
+
+  {.push overflowChecks: on.}
+  proc foo() =
+    let c = b - a
+  {.pop.}
+
+  try:
+    foo()
+  except OverflowDefect:
+    overflowDetected = true
+
+  doAssert(overflowDetected)
+
+block: # Overflow checks in a forward declared proc
+  var
+    a = high(int)
+    b = -2
+    overflowDetected = false
+
+  proc foo()
+
+  {.push overflowChecks: on.}
+  proc foo() =
+    let c = b - a
+  {.pop.}
+
+  try:
+    foo()
+  except OverflowDefect:
+    overflowDetected = true
+
+  doAssert(overflowDetected)
+
+block: # Overflow checks doesn't affect fwd declaration
+  var
+    a = high(int)
+    b = -2
+    overflowDetected = false
+
+  {.push overflowChecks: on.}
+  proc foo()
+  {.pop.}
+
+  proc foo() =
+    let c = b - a
+
+  try:
+    foo()
+  except OverflowDefect:
+    overflowDetected = true
+
+  doAssert(not overflowDetected)
+
+
+echo "ok"
diff --git a/tests/overflow/toverflow2.nim b/tests/overflow/toverflow2.nim
new file mode 100644
index 000000000..c06b35c6d
--- /dev/null
+++ b/tests/overflow/toverflow2.nim
@@ -0,0 +1,7 @@
+discard """
+  outputsub: "Error: unhandled exception: over- or underflow [OverflowDefect]"
+  exitcode: "1"
+"""
+var a : int32 = 2147483647
+var b : int32 = 2147483647
+var c = a + b
diff --git a/tests/overflow/toverflow_reorder.nim b/tests/overflow/toverflow_reorder.nim
new file mode 100644
index 000000000..fcf7b0c82
--- /dev/null
+++ b/tests/overflow/toverflow_reorder.nim
@@ -0,0 +1,84 @@
+{.experimental: "codeReordering".}
+
+discard """
+  output: "ok"
+  cmd: "nim $target --overflowChecks:off $options $file"
+"""
+# Tests nim's ability to detect overflows
+
+{.push overflowChecks: on.}
+
+var
+  a = high(int)
+  b = -2
+  overflowDetected = false
+
+try:
+  echo b - a
+except OverflowDefect:
+  overflowDetected = true
+
+{.pop.} # overflow check
+
+doAssert(overflowDetected)
+
+block: # Overflow checks in a proc
+  var
+    a = high(int)
+    b = -2
+    overflowDetected = false
+
+  {.push overflowChecks: on.}
+  proc foo() =
+    let c = b - a
+  {.pop.}
+
+  try:
+    foo()
+  except OverflowDefect:
+    overflowDetected = true
+
+  doAssert(overflowDetected)
+
+block: # Overflow checks in a forward declared proc
+  var
+    a = high(int)
+    b = -2
+    overflowDetected = false
+
+  proc foo()
+
+  {.push overflowChecks: on.}
+  proc foo() =
+    let c = b - a
+  {.pop.}
+
+  try:
+    foo()
+  except OverflowDefect:
+    overflowDetected = true
+
+  doAssert(overflowDetected)
+
+block: # Overflow checks doesn't affect fwd declaration
+  var
+    a = high(int)
+    b = -2
+    overflowDetected = false
+
+  {.push overflowChecks: on.}
+  proc foo()
+  {.pop.}
+
+  proc foo() =
+    let c = b - a
+
+  try:
+    foo()
+  except OverflowDefect:
+    overflowDetected = true
+
+  doAssert(not overflowDetected)
+
+
+echo "ok"
diff --git a/tests/overflw/tovfint.nim b/tests/overflow/tovfint.nim
index f775d2e1c..5c440a540 100644
--- a/tests/overflw/tovfint.nim
+++ b/tests/overflow/tovfint.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tovfint.nim"
   output: "works!"
 """
 # this tests the new overflow literals
@@ -19,5 +18,3 @@ else:
     write(stdout, "broken!\n")
 
 #OUT works!
-
-
diff --git a/tests/overflow/trangechecks.nim b/tests/overflow/trangechecks.nim
new file mode 100644
index 000000000..e48b1272b
--- /dev/null
+++ b/tests/overflow/trangechecks.nim
@@ -0,0 +1,48 @@
+discard """
+  output: '''10
+10
+1
+1
+true'''
+"""
+
+# bug #1344
+
+var expected: int
+var x: range[1..10] = 10
+
+try:
+  x += 1
+  echo x
+except OverflowDefect, RangeDefect:
+  expected += 1
+  echo x
+
+try:
+  inc x
+  echo x
+except OverflowDefect, RangeDefect:
+  expected += 1
+  echo x
+
+x = 1
+try:
+  x -= 1
+  echo x
+except OverflowDefect, RangeDefect:
+  expected += 1
+  echo x
+
+try:
+  dec x
+  echo x
+except OverflowDefect, RangeDefect:
+  expected += 1
+  echo x
+
+echo expected == 4
+
+# bug #13698
+var
+  x45 = "hello".cstring
+  p = x45.len.int32
diff --git a/tests/overflow/twronginference.nim b/tests/overflow/twronginference.nim
new file mode 100644
index 000000000..34a982976
--- /dev/null
+++ b/tests/overflow/twronginference.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "cannot convert 256 to int8"
+  line: 9
+"""
+
+# issue #23177
+
+var x: int8
+x = 256
+echo x # 0
diff --git a/tests/overflw/toverflw.nim b/tests/overflw/toverflw.nim
deleted file mode 100644
index 20bc56a53..000000000
--- a/tests/overflw/toverflw.nim
+++ /dev/null
@@ -1,84 +0,0 @@
-discard """
-  file: "toverflw.nim"
-  output: "ok"
-  cmd: "nim $target -d:release $options $file"
-
-"""
-# Tests nim's ability to detect overflows
-
-{.push overflowChecks: on.}
-
-var
-  a = high(int)
-  b = -2
-  overflowDetected = false
-
-try:
-  writeLine(stdout, b - a)
-except OverflowError:
-  overflowDetected = true
-
-{.pop.} # overflow check
-
-doAssert(overflowDetected)
-
-block: # Overflow checks in a proc
-  var
-    a = high(int)
-    b = -2
-    overflowDetected = false
-
-  {.push overflowChecks: on.}
-  proc foo() =
-    let c = b - a
-  {.pop.}
-
-  try:
-    foo()
-  except OverflowError:
-    overflowDetected = true
-
-  doAssert(overflowDetected)
-
-block: # Overflow checks in a forward declared proc
-  var
-    a = high(int)
-    b = -2
-    overflowDetected = false
-
-  proc foo()
-
-  {.push overflowChecks: on.}
-  proc foo() =
-    let c = b - a
-  {.pop.}
-
-  try:
-    foo()
-  except OverflowError:
-    overflowDetected = true
-
-  doAssert(overflowDetected)
-
-block: # Overflow checks doesn't affect fwd declaration
-  var
-    a = high(int)
-    b = -2
-    overflowDetected = false
-
-  {.push overflowChecks: on.}
-  proc foo()
-  {.pop.}
-
-  proc foo() =
-    let c = b - a
-
-  try:
-    foo()
-  except OverflowError:
-    overflowDetected = true
-
-  doAssert(not overflowDetected)
-
-
-echo "ok"
diff --git a/tests/overflw/toverflw2.nim b/tests/overflw/toverflw2.nim
deleted file mode 100644
index 75bd4cdf5..000000000
--- a/tests/overflw/toverflw2.nim
+++ /dev/null
@@ -1,10 +0,0 @@
-discard """
-  file: "toverflw2.nim"
-  outputsub: "Error: unhandled exception: over- or underflow [OverflowError]"
-  exitcode: "1"
-"""
-var a : int32 = 2147483647
-var b : int32 = 2147483647
-var c = a + b
-
-
diff --git a/tests/overload/issue22142/tfail_implicit_ambiguous.nim b/tests/overload/issue22142/tfail_implicit_ambiguous.nim
new file mode 100644
index 000000000..2586e0877
--- /dev/null
+++ b/tests/overload/issue22142/tfail_implicit_ambiguous.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "ambiguous call"
+"""
+type
+  A[T] = object
+  C = object
+
+proc test[T: A](param: T): bool = false
+proc test(param: A): bool = true
+doAssert test(A[C]()) == true  # previously would pass
diff --git a/tests/overload/issue22142/tfail_nested_pointers.nim b/tests/overload/issue22142/tfail_nested_pointers.nim
new file mode 100644
index 000000000..1603d98cb
--- /dev/null
+++ b/tests/overload/issue22142/tfail_nested_pointers.nim
@@ -0,0 +1,12 @@
+discard """
+  errormsg: "ambiguous call"
+"""
+
+type
+  A[T] = object
+  C = object
+    x:int
+proc p[T: A[ptr]](x:ptr[T]):bool = false
+proc p(x: ptr[A[ptr]]):bool = true
+var a: A[ptr[C]]
+doAssert p(a.addr) == true
diff --git a/tests/overload/issue22142/tfail_object_is_generic.nim b/tests/overload/issue22142/tfail_object_is_generic.nim
new file mode 100644
index 000000000..b46795bd5
--- /dev/null
+++ b/tests/overload/issue22142/tfail_object_is_generic.nim
@@ -0,0 +1,16 @@
+discard """
+  errormsg: "ambiguous call"
+"""
+
+#[
+As of the time of writing `object` needs some special
+treament in order to be considered "generic" in the right
+context when used implicitly
+]#
+
+type
+  C = object
+
+proc test[T: object](param: T): bool = false
+proc test(param: object): bool = true  
+doAssert test(C()) == true  # previously would pass
diff --git a/tests/overload/issue22142/tfail_typeclass_var_invar.nim b/tests/overload/issue22142/tfail_typeclass_var_invar.nim
new file mode 100644
index 000000000..07db65fef
--- /dev/null
+++ b/tests/overload/issue22142/tfail_typeclass_var_invar.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "ambiguous call"
+"""
+
+type C = object
+proc test[T: ptr](param: var T): bool = false
+proc test(param: var ptr): bool = true
+var d: ptr[C]
+doAssert test(d) == true  # previously would pass
diff --git a/tests/overload/issue22142/tissue22142_shouldpass.nim b/tests/overload/issue22142/tissue22142_shouldpass.nim
new file mode 100644
index 000000000..90d4efe51
--- /dev/null
+++ b/tests/overload/issue22142/tissue22142_shouldpass.nim
@@ -0,0 +1,68 @@
+type
+  A[T] = object of RootObj
+  B[T] = object
+  C = object
+    x:int
+
+# change (previously true)
+block:
+  proc test[J;H: A[J];T: B[H]](param: T): bool = false
+  proc test[T](param: B[T]): bool = true
+  doAssert test(B[A[int]]()) == false
+block:  # object is more specific then `T`
+  proc p[H:object;T:ptr[H]](param:T):bool = false
+  proc p[T](param:ptr[T]):bool= true
+  var l: ptr[C]
+  doAssert p(l) == false
+block:
+  proc p[T:A[object]](param:T):bool = false
+  proc p[T](param: A[T]):bool= true
+  doAssert p(A[C]()) == false
+block:
+  proc test[H;T: A[H]](param: T): bool = false
+  proc test(param: A): bool = true
+  doAssert test(A[C]()) == false
+
+# change (previously ambiguous)
+block:
+  proc p[T](a: A[T]): bool = false
+  proc p[T: object](a: T): bool = true
+  doAssert p(A[int]()) == false
+block:  # A is more specific than `object`
+  proc test[T: A](param: T): bool = false
+  proc test[T: object](param: T): bool = true
+  doAssert test(A[int]()) == false
+block:
+  proc test[T: A](param: T): bool = false
+  proc test(param: object): bool = true
+  doAssert test(A[int]()) == false
+block:
+  proc test[H;T: A[H]](param: T): bool = false
+  proc test(param: object): bool = true
+  doAssert test(A[C]()) == false
+block:
+  proc test[H;T: A[B[H]]](param: T): bool = false
+  proc test[T: object](param: T): bool = true
+  doAssert test(A[B[int]]()) == false
+block:
+  #[
+  This was referenced in the nim compiler source (`sumGeneric`) as a case
+  that was supposed to not be ambiguous, yet it was
+  ]#
+  proc test[J;H:A[J]; T: A[H]](param: T): bool = false
+  proc test[H;T: A[H]](param: T): bool = true
+  doAssert test(A[A[C]]()) == false
+block:
+  proc test[J;T:A[J]](param: A[T]): bool = false
+  proc test[T](param: A[T]): bool = true
+  doAssert test(A[A[C]]()) == false
+block:
+  proc test[T](param: A[T]): bool = false
+  proc test[T: object](param: A[T]): bool = true
+  doAssert test(A[C]()) == true
+
+
+block: #anti-regression (object is more specific then `T`)
+  proc test[J;T:A[J]](param: A[T]): bool = false
+  proc test(param: A[A[object]]): bool = true
+  doAssert test(A[A[C]]()) == true
\ No newline at end of file
diff --git a/tests/overload/m19737.nim b/tests/overload/m19737.nim
new file mode 100644
index 000000000..7f7ac98e2
--- /dev/null
+++ b/tests/overload/m19737.nim
@@ -0,0 +1,10 @@
+type
+  UInt128* = object
+    lo, hi: uint64
+
+func `<`*(x, y: UInt128): bool =
+  (x.hi < y.hi) or ((x.hi == y.hi) and (x.lo < y.lo))
+
+when not defined(works):
+  func `>`*(x, y: UInt128): bool =
+    (x.hi > y.hi) or ((x.hi == y.hi) and (x.lo > y.lo))
diff --git a/tests/overload/mvaruintconv.nim b/tests/overload/mvaruintconv.nim
new file mode 100644
index 000000000..b889c90cf
--- /dev/null
+++ b/tests/overload/mvaruintconv.nim
@@ -0,0 +1,145 @@
+import
+  std/[macros, tables, hashes]
+
+export
+  macros
+
+type
+  FieldDescription* = object
+    name*: NimNode
+    isPublic*: bool
+    isDiscriminator*: bool
+    typ*: NimNode
+    pragmas*: NimNode
+    caseField*: NimNode
+    caseBranch*: NimNode
+
+{.push raises: [].}
+
+func isTuple*(t: NimNode): bool =
+  t.kind == nnkBracketExpr and t[0].kind == nnkSym and eqIdent(t[0], "tuple")
+
+macro isTuple*(T: type): untyped =
+  newLit(isTuple(getType(T)[1]))
+
+proc collectFieldsFromRecList(result: var seq[FieldDescription],
+                              n: NimNode,
+                              parentCaseField: NimNode = nil,
+                              parentCaseBranch: NimNode = nil,
+                              isDiscriminator = false) =
+  case n.kind
+  of nnkRecList:
+    for entry in n:
+      collectFieldsFromRecList result, entry,
+                               parentCaseField, parentCaseBranch
+  of nnkRecWhen:
+    for branch in n:
+      case branch.kind:
+      of nnkElifBranch:
+        collectFieldsFromRecList result, branch[1],
+                                 parentCaseField, parentCaseBranch
+      of nnkElse:
+        collectFieldsFromRecList result, branch[0],
+                                 parentCaseField, parentCaseBranch
+      else:
+        doAssert false
+
+  of nnkRecCase:
+    collectFieldsFromRecList result, n[0],
+                             parentCaseField,
+                             parentCaseBranch,
+                             isDiscriminator = true
+
+    for i in 1 ..< n.len:
+      let branch = n[i]
+      case branch.kind
+      of nnkOfBranch:
+        collectFieldsFromRecList result, branch[^1], n[0], branch
+      of nnkElse:
+        collectFieldsFromRecList result, branch[0], n[0], branch
+      else:
+        doAssert false
+
+  of nnkIdentDefs:
+    let fieldType = n[^2]
+    for i in 0 ..< n.len - 2:
+      var field: FieldDescription
+      field.name = n[i]
+      field.typ = fieldType
+      field.caseField = parentCaseField
+      field.caseBranch = parentCaseBranch
+      field.isDiscriminator = isDiscriminator
+
+      if field.name.kind == nnkPragmaExpr:
+        field.pragmas = field.name[1]
+        field.name = field.name[0]
+
+      if field.name.kind == nnkPostfix:
+        field.isPublic = true
+        field.name = field.name[1]
+
+      result.add field
+
+  of nnkSym:
+    result.add FieldDescription(
+      name: n,
+      typ: getType(n),
+      caseField: parentCaseField,
+      caseBranch: parentCaseBranch,
+      isDiscriminator: isDiscriminator)
+
+  of nnkNilLit, nnkDiscardStmt, nnkCommentStmt, nnkEmpty:
+    discard
+
+  else:
+    doAssert false, "Unexpected nodes in recordFields:\n" & n.treeRepr
+
+proc collectFieldsInHierarchy(result: var seq[FieldDescription],
+                              objectType: NimNode) =
+  var objectType = objectType
+
+  objectType.expectKind {nnkObjectTy, nnkRefTy}
+
+  if objectType.kind == nnkRefTy:
+    objectType = objectType[0]
+
+  objectType.expectKind nnkObjectTy
+
+  var baseType = objectType[1]
+  if baseType.kind != nnkEmpty:
+    baseType.expectKind nnkOfInherit
+    baseType = baseType[0]
+    baseType.expectKind nnkSym
+    baseType = getImpl(baseType)
+    baseType.expectKind nnkTypeDef
+    baseType = baseType[2]
+    baseType.expectKind {nnkObjectTy, nnkRefTy}
+    collectFieldsInHierarchy result, baseType
+
+  let recList = objectType[2]
+  collectFieldsFromRecList result, recList
+
+proc recordFields*(typeImpl: NimNode): seq[FieldDescription] =
+  if typeImpl.isTuple:
+    for i in 1 ..< typeImpl.len:
+      result.add FieldDescription(typ: typeImpl[i], name: ident("Field" & $(i - 1)))
+    return
+
+  let objectType = case typeImpl.kind
+    of nnkObjectTy: typeImpl
+    of nnkTypeDef: typeImpl[2]
+    else:
+      macros.error("object type expected", typeImpl)
+      return
+
+  collectFieldsInHierarchy(result, objectType)
+
+macro field*(obj: typed, fieldName: static string): untyped =
+  newDotExpr(obj, ident fieldName)
+
+proc skipPragma*(n: NimNode): NimNode =
+  if n.kind == nnkPragmaExpr: n[0]
+  else: n
+
+
+{.pop.}
diff --git a/tests/overload/t19737.nim b/tests/overload/t19737.nim
new file mode 100644
index 000000000..b33ba9d8b
--- /dev/null
+++ b/tests/overload/t19737.nim
@@ -0,0 +1,15 @@
+# issue #19737
+
+import ./m19737
+
+var m: seq[uint64]
+
+proc foo(x: bool) = discard
+
+proc test[T: uint64|uint32](s: var seq[T]) =
+  var tmp = newSeq[T](1)
+  s = newSeq[T](1)
+
+  foo s[0] > tmp[0]
+
+test(m)
diff --git a/tests/overload/t23249.nim b/tests/overload/t23249.nim
new file mode 100644
index 000000000..f4657833b
--- /dev/null
+++ b/tests/overload/t23249.nim
@@ -0,0 +1,17 @@
+# issue #23249
+
+type Control* = object
+proc onAction*(c: Control, handler: proc(e: int) {.gcsafe.}) = discard
+proc onAction*(c: Control, handler: proc() {.gcsafe.}) = discard
+
+template setControlHandlerBlock(c: Control, p: untyped, a: untyped) =
+    when compiles(c.p(nil)):
+        c.p() do() {.gcsafe.}: a
+    else:
+        c.p = proc() {.gcsafe.} =
+            a
+
+proc mkLayout() =
+  var b: Control
+  setControlHandlerBlock(b, onAction):
+    echo "hi"
diff --git a/tests/overload/t23755.nim b/tests/overload/t23755.nim
new file mode 100644
index 000000000..de338a2ce
--- /dev/null
+++ b/tests/overload/t23755.nim
@@ -0,0 +1,62 @@
+type
+  BigInt[bits: static int] = object
+    limbs: array[8, uint64]
+
+block:
+  proc view[N](a: array[N, uint64]) =
+    discard
+
+  proc view[N](a: var array[N, uint64]) =
+    discard
+
+  var r: BigInt[64]
+  r.limbs.view()
+
+
+type Limbs[N: static int] = array[N, uint64]
+
+block:
+  proc view(a: Limbs) =
+    discard
+
+  proc view(a: var Limbs) =
+    discard
+
+  var r: BigInt[64]
+  r.limbs.view()
+
+
+block:
+  type IntArray[N: static[int]] = array[N, int]
+
+  proc p[T](a: IntArray[T]): bool= true
+  proc p(a: IntArray[5]): bool= false
+
+  var s: IntArray[5]
+  doAssert s.p == false
+
+block:
+  type IntArray[N: static[int]] = array[N, int]
+
+  proc `$`(a: IntArray): string =
+    return "test"
+
+  var s: IntArray[5] = [1,1,1,1,1]
+  doAssert `$`(s) == "test"
+
+block: 
+  proc p[n:static[int]](a: array[n, char]):bool=true
+  proc p[T, IDX](a: array[IDX, T]):bool=false
+
+  var g: array[32, char]
+  doAssert p(g)
+
+block:  # issue #23823
+  func p[N,T](a, b: array[N,T]) =
+    discard
+
+  func p[N: static int; T](x, y: array[N, T]) =
+    discard
+
+  var a: array[5, int]
+  p(a,a)
diff --git a/tests/overload/t7416.nim b/tests/overload/t7416.nim
new file mode 100644
index 000000000..4a9b2e7cb
--- /dev/null
+++ b/tests/overload/t7416.nim
@@ -0,0 +1,9 @@
+type
+  Foo[T] = object
+  IntFoo = Foo[int]
+
+proc bar(b: object|tuple) = discard
+proc bar(b: IntFoo) = discard
+
+var f: IntFoo
+bar(f)
\ No newline at end of file
diff --git a/tests/overload/t8829.nim b/tests/overload/t8829.nim
new file mode 100644
index 000000000..85d87f136
--- /dev/null
+++ b/tests/overload/t8829.nim
@@ -0,0 +1,18 @@
+block:
+  let txt = "Hello World"
+
+  template `[]`[T](p: ptr T, span: Slice[int]): untyped =
+    toOpenArray(cast[ptr UncheckedArray[T]](p), span.a, span.b)
+
+  doAssert $cast[ptr uint8](txt[0].addr)[0 ..< txt.len] == 
+                "[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]"
+
+
+block:
+  let txt = "Hello World"
+
+  template `[]`[T](p: ptr T, span: Slice[int]): untyped =
+    toOpenArray(cast[ptr UncheckedArray[T]](p), span.a, span.b)
+
+  doAssert $cast[ptr uint8](txt[0].addr)[0 ..< txt.len] == 
+                "[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]"
diff --git a/tests/overload/tconverter_to_string.nim b/tests/overload/tconverter_to_string.nim
new file mode 100644
index 000000000..1960372d8
--- /dev/null
+++ b/tests/overload/tconverter_to_string.nim
@@ -0,0 +1,22 @@
+discard """
+  output: '''123
+c is not nil'''
+"""
+
+# bug #9149
+
+type
+  Container = ref object
+    data: int
+
+converter containerToString*(x: Container): string = $x.data
+
+var c = Container(data: 123)
+var str = string c
+echo str
+
+if c == nil: # this line can compile on v0.18, but not on 0.19
+  echo "c is nil"
+
+if not c.isNil:
+  echo "c is not nil"
diff --git a/tests/overload/tgenericalias.nim b/tests/overload/tgenericalias.nim
new file mode 100644
index 000000000..50a44bd32
--- /dev/null
+++ b/tests/overload/tgenericalias.nim
@@ -0,0 +1,13 @@
+block: # issue #13799
+  type
+    X[A, B] = object
+      a: A
+      b: B
+
+    Y[A] = X[A, int]
+  template s(T: type X): X = T()
+  template t[A, B](T: type X[A, B]): X[A, B] = T()
+  proc works1(): Y[int] = s(X[int, int])
+  proc works2(): Y[int] = t(X[int, int])
+  proc works3(): Y[int] = t(Y[int])
+  proc broken(): Y[int] = s(Y[int])
diff --git a/tests/overload/timport.nim b/tests/overload/timport.nim
deleted file mode 100644
index 8ea65e54d..000000000
--- a/tests/overload/timport.nim
+++ /dev/null
@@ -1,7 +0,0 @@
-# issue 4675
-import importA  # comment this out to make it work
-import importB
-
-var x: Foo[float]
-var y: Foo[float]
-let r = t1(x) + t2(y)
diff --git a/tests/overload/tissue4475.nim b/tests/overload/tissue4475.nim
deleted file mode 100644
index 34618cac5..000000000
--- a/tests/overload/tissue4475.nim
+++ /dev/null
@@ -1,6 +0,0 @@
-# Bug: https://github.com/nim-lang/Nim/issues/4475
-# Fix: https://github.com/nim-lang/Nim/pull/4477
-
-proc test(x: varargs[string], y: int) = discard
-
-test(y = 1)
diff --git a/tests/overload/tnamedparamoverloading.nim b/tests/overload/tnamedparamoverloading.nim
new file mode 100644
index 000000000..a7c37ba52
--- /dev/null
+++ b/tests/overload/tnamedparamoverloading.nim
@@ -0,0 +1,14 @@
+discard """
+  output: '''
+Using x: 2
+Using y: 2
+'''
+"""
+
+proc foo(x: int) =
+  echo "Using x: ", x
+proc foo(y: int) =
+  echo "Using y: ", y
+
+foo(x = 2)
+foo(y = 2)
\ No newline at end of file
diff --git a/tests/overload/tor_isnt_better.nim b/tests/overload/tor_isnt_better.nim
new file mode 100644
index 000000000..bee125386
--- /dev/null
+++ b/tests/overload/tor_isnt_better.nim
@@ -0,0 +1,41 @@
+type
+  D[T] = object
+  E[T] = object
+
+block: # PR #22261
+  proc d(x: D):bool= false
+  proc d(x: int | D[SomeInteger]):bool= true
+  doAssert d(D[5]()) == false
+
+block: # bug #8568
+#[
+  Since PR #22261 and amendment has been made. Since D is a subset of D | E but
+  not the other way around `checkGeneric` should favor proc g(a: D) instead
+  of asserting ambiguity
+]#
+  proc g(a: D|E): string = "foo D|E"
+  proc g(a: D): string = "foo D"
+  doAssert g(D[int]()) == "foo D"
+
+type Obj1[T] = object
+  v: T
+converter toObj1[T](t: T): Obj1[T] = return Obj1[T](v: t)
+block: # issue #10019
+  proc fun1[T](elements: seq[T]): string = "fun1 seq"
+  proc fun1(o: object|tuple): string = "fun1 object|tuple"
+  proc fun2[T](elements: openArray[T]): string = "fun2 openarray"
+  proc fun2(o: object): string = "fun2 object"
+  proc fun_bug[T](elements: openArray[T]): string = "fun_bug openarray"
+  proc fun_bug(o: object|tuple):string = "fun_bug object|tuple"
+  proc main() =
+    var x = @["hello", "world"]
+    block:
+      # no ambiguity error shown here even though this would compile if we remove either 1st or 2nd overload of fun1
+      doAssert fun1(x) == "fun1 seq"
+    block:
+      # ditto
+      doAssert fun2(x) == "fun2 openarray"
+    block:
+      # Error: ambiguous call; both t0065.fun_bug(elements: openarray[T])[declared in t0065.nim(17, 5)] and t0065.fun_bug(o: object or tuple)[declared in t0065.nim(20, 5)] match for: (array[0..1, string])
+      doAssert fun_bug(x) == "fun_bug openarray"
+  main()
diff --git a/tests/overload/toverl.nim b/tests/overload/toverl.nim
index 807b643a4..64257be77 100644
--- a/tests/overload/toverl.nim
+++ b/tests/overload/toverl.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "redefinition of \'TNone\'"
   file: "toverl.nim"
   line: 11
-  errormsg: "redefinition of \'TNone\'"
 """
 # Test for overloading
 
@@ -9,5 +9,3 @@ type
   TNone {.exportc: "_NONE", final.} = object
 
 proc TNone(a, b: int) = nil #ERROR_MSG attempt to redefine 'TNone'
-
-
diff --git a/tests/overload/toverl2.nim b/tests/overload/toverl2.nim
deleted file mode 100644
index 54714ac1b..000000000
--- a/tests/overload/toverl2.nim
+++ /dev/null
@@ -1,33 +0,0 @@
-discard """
-  file: "toverl2.nim"
-  output: "true012innertrue"
-"""
-# Test new overloading resolution rules
-
-import strutils
-
-proc toverl2(x: int): string = return $x
-proc toverl2(x: bool): string = return $x
-
-iterator toverl2(x: int): int =
-  var res = 0
-  while res < x:
-    yield res
-    inc(res)
-
-var
-  pp: proc (x: bool): string {.nimcall.} = toverl2
-
-stdout.write(pp(true))
-
-for x in toverl2(3):
-  stdout.write(toverl2(x))
-
-block:
-  proc toverl2(x: int): string = return "inner"
-  stdout.write(toverl2(5))
-  stdout.write(true)
-
-stdout.write("\n")
-#OUT true012innertrue
-
diff --git a/tests/overload/toverl3.nim b/tests/overload/toverl3.nim
deleted file mode 100644
index 92cfc150d..000000000
--- a/tests/overload/toverl3.nim
+++ /dev/null
@@ -1,20 +0,0 @@
-discard """
-  file: "toverl3.nim"
-  output: '''m1
-tup1'''
-"""
-
-# Tests more specific generic match:
-
-proc m[T](x: T) = echo "m2"
-proc m[T](x: var ref T) = echo "m1"
-
-proc tup[S, T](x: tuple[a: S, b: ref T]) = echo "tup1"
-proc tup[S, T](x: tuple[a: S, b: T]) = echo "tup2"
-
-var
-  obj: ref int
-  tu: tuple[a: int, b: ref bool]
-
-m(obj)
-tup(tu)
diff --git a/tests/overload/toverl4.nim b/tests/overload/toverl4.nim
index b63265bdf..21cedaa96 100644
--- a/tests/overload/toverl4.nim
+++ b/tests/overload/toverl4.nim
@@ -1,5 +1,6 @@
 discard """
-  output: '''true'''
+  output: '''true
+5.0'''
 """
 
 #bug #592
@@ -28,9 +29,9 @@ proc newElement[TKey, TData](key: TKey, left: PElement[TKey, TData] = nil, right
 proc newElement[Tkey, TData](key: Tkey, data: TData) : PElement[Tkey, TData] =
   PElement[TKey, TData](kind: ElementKind.leaf, data: data)
 
-proc find*[TKey, TData](root: PElement[TKey, TData], key: TKey): TData {.raises: [EInvalidKey].} =
+proc find*[TKey, TData](root: PElement[TKey, TData], key: TKey): TData {.raises: [KeyError].} =
   if root.left == nil:
-    raise newException(EInvalidKey, "key does not exist: " & key)
+    raise newException(KeyError, "key does not exist: " & key)
 
   var tmp_element = addr(root)
 
@@ -43,7 +44,7 @@ proc find*[TKey, TData](root: PElement[TKey, TData], key: TKey): TData {.raises:
   if tmp_element.key == key:
     return tmp_element.left.data
   else:
-    raise newException(EInvalidKey, "key does not exist: " & key)
+    raise newException(KeyError, "key does not exist: " & key)
 
 proc add*[TKey, TData](root: var PElement[TKey, TData], key: TKey, data: TData) : bool =
   if root.left == nil:
@@ -75,3 +76,26 @@ proc add*[TKey, TData](root: var PElement[TKey, TData], key: TKey, data: TData)
 var tree = PElement[int, int](kind: ElementKind.inner, key: 0, left: nil, right: nil)
 let result = add(tree, 1, 1)
 echo(result)
+
+# bug #3748
+type
+  Foo = object
+    bar: int
+
+proc bar(cur: Foo, val: int, s:seq[string]) =
+  discard cur.bar
+
+proc does_fail(): Foo =
+  let a = @["a"]
+  result.bar(5, a)
+
+doAssert does_fail().bar == 0
+
+# bug #20645
+
+type Zzz[Gen] = object
+
+proc testZ(z: Zzz) =
+  echo z.Gen(5)
+
+testZ(Zzz[float]())
diff --git a/tests/overload/toverload_issues.nim b/tests/overload/toverload_issues.nim
new file mode 100644
index 000000000..26bf89091
--- /dev/null
+++ b/tests/overload/toverload_issues.nim
@@ -0,0 +1,200 @@
+discard """
+  output: '''
+Version 2 was called.
+This has the highest precedence.
+This has the second-highest precedence.
+This has the lowest precedence.
+baseobj ==
+true
+even better! ==
+true
+done extraI=0
+test 0 complete, loops=0
+done extraI=1
+test 1.0 complete, loops=1
+done extraI=0
+done extraI passed 0
+test no extra complete, loops=2
+1
+'''
+"""
+
+
+# issue 4675
+import importA  # comment this out to make it work
+import importB
+
+var x: Foo[float]
+var y: Foo[float]
+let r = t1(x) + t2(y)
+
+
+
+# Bug: https://github.com/nim-lang/Nim/issues/4475
+# Fix: https://github.com/nim-lang/Nim/pull/4477
+proc test(x: varargs[string], y: int) = discard
+test(y = 1)
+
+
+
+# bug #2220
+when true:
+  type A[T] = object
+  type B = A[int]
+
+  proc q[X](x: X) =
+    echo "Version 1 was called."
+
+  proc q(x: B) =
+    echo "Version 2 was called."
+
+  q(B()) # This call reported as ambiguous.
+
+
+
+# bug #2219
+template testPred(a: untyped) =
+  block:
+    type A = object of RootObj
+    type B = object of A
+    type SomeA = A|A # A hack to make "A" a typeclass.
+
+    when a >= 3:
+      proc p[X: A](x: X) =
+        echo "This has the highest precedence."
+    when a == 2:
+      proc p[X: SomeA](x: X) =
+        echo "This has the second-highest precedence."
+    when a >= 1:
+      proc p[X](x: X) =
+        echo "This has the lowest precedence."
+
+    p(B())
+
+testPred(3)
+testPred(2)
+testPred(1)
+
+
+
+block: # bug #6526
+  type
+    BaseObj = ref object of RootObj
+    DerivedObj = ref object of BaseObj
+    OtherDerivate = ref object of BaseObj
+
+  proc p[T](a: T, b: T): bool =
+    assert false
+
+  proc p[T1, T2: BaseObj](a: T1, b: T2): bool =
+    echo "baseobj =="
+    return true
+
+  let a = DerivedObj()
+  let b = DerivedObj()
+  echo p(a,b)
+
+  proc p[T1, T2: OtherDerivate](a: T1, b: T2): bool =
+    echo "even better! =="
+    return true
+
+  let a2 = OtherDerivate()
+  let b2 = OtherDerivate()
+  echo p(a2, b2)
+
+
+
+# bug #2481
+import math
+
+template test(loopCount: int, extraI: int, testBody: untyped): typed =
+  block:
+    for i in 0..loopCount-1:
+      testBody
+    echo "done extraI=", extraI
+
+template test(loopCount: int, extraF: float, testBody: untyped): typed =
+  block:
+    test(loopCount, round(extraF).int, testBody)
+
+template test(loopCount: int, testBody: untyped): typed =
+  block:
+    test(loopCount, 0, testBody)
+    echo "done extraI passed 0"
+
+var
+  loops = 0
+
+test 0, 0:
+  loops += 1
+echo "test 0 complete, loops=", loops
+
+test 1, 1.0:
+  loops += 1
+echo "test 1.0 complete, loops=", loops
+
+when true:
+  # when true we get the following compile time error:
+  #   b.nim(35, 6) Error: expression 'loops += 1' has no type (or is ambiguous)
+  loops = 0
+  test 2:
+    loops += 1
+  echo "test no extra complete, loops=", loops
+
+# bug #2229
+type
+  Type1 = object
+    id: int
+  Type2 = object
+    id: int
+
+proc init(self: var Type1, a: int, b: ref Type2) =
+  echo "1"
+
+proc init(self: var Type2, a: int) =
+  echo """
+    Works when this proc commented out
+    Otherwise error:
+    test.nim(14, 4) Error: ambiguous call; both test.init(self: var Type1, a: int, b: ref Type2) and test.init(self: var Type1, a: int, b: ref Type2) match for: (Type1, int literal(1), ref Type2)
+  """
+
+var aa: Type1
+init(aa, 1, (
+    var bb = new(Type2);
+    bb
+))
+
+
+
+# bug #4545
+type
+  SomeObject = object
+    a: int
+  AbstractObject = object
+    objet: ptr SomeObject
+
+proc convert(this: var SomeObject): AbstractObject =
+  AbstractObject(objet: this.addr)
+
+proc varargProc(args: varargs[AbstractObject, convert]): int =
+  for arg in args:
+    result += arg.objet.a
+
+var obj = SomeObject(a: 17)
+discard varargProc(obj)
+
+
+
+# bug #11239
+
+type MySeq*[T] = object
+
+proc foo(a: seq[int]): string = "foo: seq[int]"
+proc foo[T](a: seq[T]): string = "foo: seq[T]"
+proc foo(a: MySeq[int]): string = "foo: MySeq[int]"
+proc foo[T](a: MySeq[T]): string = "foo: MySeq[T]"
+
+doAssert foo(@[1,2,3]) == "foo: seq[int]"
+doAssert foo(@["WER"]) == "foo: seq[T]"
+doAssert foo(MySeq[int]()) == "foo: MySeq[int]"
+doAssert foo(MySeq[string]()) == "foo: MySeq[T]"
diff --git a/tests/overload/toverload_various.nim b/tests/overload/toverload_various.nim
new file mode 100644
index 000000000..d195a069d
--- /dev/null
+++ b/tests/overload/toverload_various.nim
@@ -0,0 +1,568 @@
+discard """
+  output: '''
+true012innertrue
+m1
+tup1
+another number: 123
+yay
+helloa 1 b 2 x @[3, 4, 5] y 6 z 7
+yay
+12
+ref ref T ptr S
+dynamic: let
+dynamic: var
+static: const
+static: literal
+static: constant folding
+static: static string
+foo1
+1
+'''
+"""
+
+
+import strutils, sequtils
+
+
+block overl2:
+  # Test new overloading resolution rules
+  proc toverl2(x: int): string = return $x
+  proc toverl2(x: bool): string = return $x
+
+  iterator toverl2(x: int): int =
+    var res = 0
+    while res < x:
+      yield res
+      inc(res)
+
+  var
+    pp: proc (x: bool): string {.nimcall.} = toverl2
+
+  stdout.write(pp(true))
+
+  for x in toverl2(3):
+    stdout.write(toverl2(x))
+
+  block:
+    proc toverl2(x: int): string = return "inner"
+    stdout.write(toverl2(5))
+    stdout.write(true)
+
+  stdout.write("\n")
+  #OUT true012innertrue
+
+
+
+block overl3:
+  # Tests more specific generic match:
+  proc m[T](x: T) = echo "m2"
+  proc m[T](x: var ref T) = echo "m1"
+  proc tup[S, T](x: tuple[a: S, b: ref T]) = echo "tup1"
+  proc tup[S, T](x: tuple[a: S, b: T]) = echo "tup2"
+
+  var
+    obj: ref int
+    tu: tuple[a: int, b: ref bool]
+
+  m(obj)
+  tup(tu)
+
+
+
+block toverprc:
+  # Test overloading of procs when used as function pointers
+  proc parseInt(x: float): int {.noSideEffect.} = discard
+  proc parseInt(x: bool): int {.noSideEffect.} = discard
+  proc parseInt(x: float32): int {.noSideEffect.} = discard
+  proc parseInt(x: int8): int {.noSideEffect.} = discard
+  proc parseInt(x: File): int {.noSideEffect.} = discard
+  proc parseInt(x: char): int {.noSideEffect.} = discard
+  proc parseInt(x: int16): int {.noSideEffect.} = discard
+
+  proc parseInt[T](x: T): int = echo x; 34
+
+  type
+    TParseInt = proc (x: string): int {.noSideEffect.}
+
+  var
+    q = TParseInt(parseInt)
+    p: TParseInt = parseInt
+
+  proc takeParseInt(x: proc (y: string): int {.noSideEffect.}): int =
+    result = x("123")
+
+  if false:
+    echo "Give a list of numbers (separated by spaces): "
+    var x = stdin.readline.split.map(parseInt).max
+    echo x, " is the maximum!"
+  echo "another number: ", takeParseInt(parseInt)
+
+
+  type
+    TFoo[a,b] = object
+      lorem: a
+      ipsum: b
+
+  proc bar[a,b](f: TFoo[a,b], x: a) = echo(x, " ", f.lorem, f.ipsum)
+  proc bar[a,b](f: TFoo[a,b], x: b) = echo(x, " ", f.lorem, f.ipsum)
+
+  discard parseInt[string]("yay")
+
+
+
+block toverwr:
+  # Test the overloading resolution in connection with a qualifier
+  proc write(t: File, s: string) =
+    discard # a nop
+  system.write(stdout, "hello")
+  #OUT hello
+
+
+
+block tparams_after_varargs:
+  proc test(a, b: int, x: varargs[int]; y, z: int) =
+    echo "a ", a, " b ", b, " x ", @x, " y ", y, " z ", z
+
+  test 1, 2, 3, 4, 5, 6, 7
+
+  # XXX maybe this should also work with ``varargs[untyped]``
+  template takesBlockA(a, b: untyped; x: varargs[typed]; blck: untyped): untyped =
+    blck
+    echo a, b
+
+  takesBlockA 1, 2, "some", 0.90, "random stuff":
+    echo "yay"
+
+
+
+block tprefer_specialized_generic:
+  proc foo[T](x: T) =
+    echo "only T"
+
+  proc foo[T](x: ref T) =
+    echo "ref T"
+
+  proc foo[T, S](x: ref ref T; y: ptr S) =
+    echo "ref ref T ptr S"
+
+  proc foo[T, S](x: ref T; y: ptr S) =
+    echo "ref T ptr S"
+
+  proc foo[T](x: ref T; default = 0) =
+    echo "ref T; default"
+
+  var x: ref ref int
+  var y: ptr ptr int
+  foo(x, y)
+
+
+
+block tstaticoverload:
+  proc foo(s: string) =
+    echo "dynamic: ", s
+
+  proc foo(s: static[string]) =
+    echo "static: ", s
+
+  let l = "let"
+  var v = "var"
+  const c = "const"
+
+  type staticString = static[string]
+
+  foo(l)
+  foo(v)
+  foo(c)
+  foo("literal")
+  foo("constant" & " " & "folding")
+  foo(staticString("static string"))
+
+# bug #8568 (2)
+
+proc goo(a: int): string = "int"
+proc goo(a: static[int]): string = "static int"
+proc goo(a: var int): string = "var int"
+proc goo[T: int](a: T): string = "T: int"
+#proc goo[T](a: T): string = "nur T"
+
+const tmp1 = 1
+let tmp2 = 1
+var tmp3 = 1
+
+doAssert goo(1) == "static int"
+doAssert goo(tmp1) == "static int"
+doAssert goo(tmp2) == "int"
+doAssert goo(tmp3) == "var int"
+
+doAssert goo[int](1) == "T: int"
+
+doAssert goo[int](tmp1) == "T: int"
+doAssert goo[int](tmp2) == "T: int"
+doAssert goo[int](tmp3) == "T: int"
+
+# bug #6076
+
+type A[T] = object
+
+proc regr(a: A[void]) = echo "foo1"
+proc regr[T](a: A[T]) = doAssert(false)
+
+regr(A[void]())
+
+
+type Foo[T] = object
+
+proc regr[T](p: Foo[T]): seq[T] =
+  discard
+
+proc regr(p: Foo[void]): seq[int] =
+  discard
+
+
+discard regr(Foo[int]())
+discard regr(Foo[void]())
+
+
+type
+  Sha2Context*[bits: static[int],
+               bsize: static[int],
+               T: uint32|uint64] = object
+    count: array[2, T]
+    state: array[8, T]
+    buffer: array[bsize, byte]
+
+  sha224* = Sha2Context[224, 64, uint32]
+  sha256* = Sha2Context[256, 64, uint32]
+  sha384* = Sha2Context[384, 128, uint64]
+  sha512* = Sha2Context[512, 128, uint64]
+  sha512_224* = Sha2Context[224, 128, uint64]
+  sha512_256* = Sha2Context[256, 128, uint64]
+
+type
+  RipemdContext*[bits: static[int]] = object
+    count: array[2, uint32]
+    state: array[bits div 32, uint32]
+    buffer: array[64, byte]
+
+  ripemd128* = RipemdContext[128]
+  ripemd160* = RipemdContext[160]
+  ripemd256* = RipemdContext[256]
+  ripemd320* = RipemdContext[320]
+
+const
+  MaxHmacBlockSize = 256
+
+type
+  HMAC*[HashType] = object
+    mdctx: HashType
+    opadctx: HashType
+
+template sizeBlock*(h: HMAC[Sha2Context]): uint = 1u
+template sizeBlock*(h: HMAC[RipemdContext]): uint = 0u
+
+proc init*[T](hmctx: HMAC[T], key: ptr byte, ulen: uint) =
+  const sizeBlock = hmctx.sizeBlock
+  echo sizeBlock
+
+proc hmac*[A, B](HashType: typedesc, key: openArray[A],
+                 data: openArray[B]) =
+  var ctx: HMAC[HashType]
+  ctx.init(nil, 0)
+
+sha256.hmac("", "")
+
+
+
+# nested generic types
+block:
+  type
+    Foo[T] = object
+      f: T
+    Bar[T] = object
+      b: T
+    Baz[T] = object
+      z: T
+    FooBar[T] = Foo[Bar[T]]
+    FooBarBaz[T] = FooBar[Baz[T]]
+    #Int = int
+    Int = SomeInteger
+    FooBarBazInt = FooBarBaz[Int]
+    FooBarBazX = FooBarBaz[int]
+
+  proc p00(x: Foo): auto = x.f
+  proc p01[T](x: Foo[T]): auto = x.f
+  proc p02[T:Foo](x: T): auto = x.f
+
+  proc p10(x: FooBar): auto = x.f
+  proc p11[T](x: FooBar[T]): auto = x.f
+  proc p12[T:FooBar](x: T): auto = x.f
+  proc p13(x: Foo[Bar]): auto = x.f
+  proc p14[T](x: Foo[Bar[T]]): auto = x.f
+  proc p15[T:Bar](x: Foo[T]): auto = x.f
+  proc p16[T:Foo[Bar]](x: T): auto = x.f
+
+  proc p20(x: FooBarBaz): auto = x.f
+  proc p21[T](x: FooBarBaz[T]): auto = x.f
+  proc p22[T:FooBarBaz](x: T): auto = x.f
+  proc p23(x: FooBar[Baz]): auto = x.f
+  proc p24[T](x: FooBar[Baz[T]]): auto = x.f
+  proc p25[T:Baz](x: FooBar[T]): auto = x.f
+  proc p26[T:FooBar[Baz]](x: T): auto = x.f
+  proc p27(x: Foo[Bar[Baz]]): auto = x.f
+  proc p28[T](x: Foo[Bar[Baz[T]]]): auto = x.f
+  proc p29[T:Baz](x: Foo[Bar[T]]): auto = x.f
+  proc p2A[T:Bar[Baz]](x: Foo[T]): auto = x.f
+  proc p2B[T:Foo[Bar[Baz]]](x: T): auto = x.f
+
+  proc p30(x: FooBarBazInt): auto = x.f
+  proc p31[T:FooBarBazInt](x: T): auto = x.f
+  proc p32(x: FooBarBaz[Int]): auto = x.f
+  proc p33[T:Int](x: FooBarBaz[T]): auto = x.f
+  proc p34[T:FooBarBaz[Int]](x: T): auto = x.f
+  proc p35(x: FooBar[Baz[Int]]): auto = x.f
+  proc p36[T:Int](x: FooBar[Baz[T]]): auto = x.f
+  proc p37[T:Baz[Int]](x: FooBar[T]): auto = x.f
+  proc p38[T:FooBar[Baz[Int]]](x: T): auto = x.f
+  proc p39(x: Foo[Bar[Baz[Int]]]): auto = x.f
+  proc p3A[T:Int](x: Foo[Bar[Baz[T]]]): auto = x.f
+  proc p3B[T:Baz[Int]](x: Foo[Bar[T]]): auto = x.f
+  proc p3C[T:Bar[Baz[Int]]](x: Foo[T]): auto = x.f
+  proc p3D[T:Foo[Bar[Baz[Int]]]](x: T): auto = x.f
+
+  template test(x: typed) =
+    let t00 = p00(x)
+    let t01 = p01(x)
+    let t02 = p02(x)
+    let t10 = p10(x)
+    let t11 = p11(x)
+    let t12 = p12(x)
+    #let t13 = p13(x)
+    let t14 = p14(x)
+    #let t15 = p15(x)
+    #let t16 = p16(x)
+    let t20 = p20(x)
+    let t21 = p21(x)
+    let t22 = p22(x)
+    #let t23 = p23(x)
+    let t24 = p24(x)
+    #let t25 = p25(x)
+    #let t26 = p26(x)
+    #let t27 = p27(x)
+    let t28 = p28(x)
+    #let t29 = p29(x)
+    #let t2A = p2A(x)
+    #let t2B = p2B(x)
+    let t30 = p30(x)
+    let t31 = p31(x)
+    let t32 = p32(x)
+    let t33 = p33(x)
+    let t34 = p34(x)
+    let t35 = p35(x)
+    let t36 = p36(x)
+    let t37 = p37(x)
+    let t38 = p38(x)
+    let t39 = p39(x)
+    let t3A = p3A(x)
+    let t3B = p3B(x)
+    let t3C = p3C(x)
+    let t3D = p3D(x)
+
+  var a: Foo[Bar[Baz[int]]]
+  test(a)
+  var b: FooBar[Baz[int]]
+  test(b)
+  var c: FooBarBaz[int]
+  test(c)
+  var d: FooBarBazX
+  test(d)
+
+
+# overloading on tuples with generic alias
+block:
+  type
+    Foo[F,T] = object
+      exArgs: T
+    FooUn[F,T] = Foo[F,tuple[a:T]]
+    FooBi[F,T1,T2] = Foo[F,tuple[a:T1,b:T2]]
+
+  proc foo1[F,T](x: Foo[F,tuple[a:T]]): int = 1
+  proc foo1[F,T1,T2](x: Foo[F,tuple[a:T1,b:T2]]): int = 2
+  proc foo2[F,T](x: FooUn[F,T]): int = 1
+  proc foo2[F,T1,T2](x: FooBi[F,T1,T2]):int = 2
+
+  template bar1[F,T](x: Foo[F,tuple[a:T]]): int = 1
+  template bar1[F,T1,T2](x: Foo[F,tuple[a:T1,b:T2]]): int = 2
+  template bar2[F,T](x: FooUn[F,T]): int = 1
+  template bar2[F,T1,T2](x: FooBi[F,T1,T2]): int = 2
+
+  proc test(x: auto, n: int) =
+    doAssert(foo1(x) == n)
+    doAssert(foo2(x) == n)
+    doAssert(bar1(x) == n)
+    doAssert(bar2(x) == n)
+
+  var a: Foo[int, tuple[a:int]]
+  test(a, 1)
+  var b: FooUn[int, int]
+  test(b, 1)
+  var c: Foo[int, tuple[a:int,b:int]]
+  test(c, 2)
+  var d: FooBi[int, int, int]
+  test(d, 2)
+
+
+# inheritance and generics
+block:
+  type
+    Foo[T] = object of RootObj
+      x: T
+    Bar[T] = object of Foo[T]
+      y: T
+    Baz[T] = object of Bar[T]
+      z: T
+
+  template t0(x: Foo[int]): int = 0
+  template t0(x: Bar[int]): int = 1
+  template t0(x: Foo[bool or int]): int = 10
+  template t0(x: Bar[bool or int]): int = 11
+  #template t0[T:bool or int](x: Bar[T]): int = 11
+  template t0[T](x: Foo[T]): int = 20
+  template t0[T](x: Bar[T]): int = 21
+  proc p0(x: Foo[int]): int = 0
+  proc p0(x: Bar[int]): int = 1
+  #proc p0(x: Foo[bool or int]): int = 10
+  #proc p0(x: Bar[bool or int]): int = 11
+  proc p0[T](x: Foo[T]): int = 20
+  proc p0[T](x: Bar[T]): int = 21
+
+  var a: Foo[int]
+  var b: Bar[int]
+  var c: Baz[int]
+  var d: Foo[bool]
+  var e: Bar[bool]
+  var f: Baz[bool]
+  var g: Foo[float]
+  var h: Bar[float]
+  var i: Baz[float]
+  doAssert(t0(a) == 0)
+  doAssert(t0(b) == 1)
+  doAssert(t0(c) == 1)
+  doAssert(t0(d) == 10)
+  doAssert(t0(e) == 11)
+  doAssert(t0(f) == 11)
+  doAssert(t0(g) == 20)
+  doAssert(t0(h) == 21)
+  #doAssert(t0(i) == 21)
+  doAssert(p0(a) == 0)
+  doAssert(p0(b) == 1)
+  doAssert(p0(c) == 1)
+  #doAssert(p0(d) == 10)
+  #doAssert(p0(e) == 11)
+  #doAssert(p0(f) == 11)
+  doAssert(p0(g) == 20)
+  doAssert(p0(h) == 21)
+  doAssert(p0(i) == 21)
+
+  #type
+  #  f0 = proc(x:Foo)
+
+
+block:
+  type
+    TilesetCT[n: static[int]] = distinct int
+    TilesetRT = int
+    Tileset = TilesetCT | TilesetRT
+
+  func prepareTileset(tileset: var Tileset) = discard
+
+  func prepareTileset(tileset: Tileset): Tileset =
+    result = tileset
+    result.prepareTileset
+
+  var parsedTileset: TilesetRT
+  prepareTileset(parsedTileset)
+
+
+block:
+  proc p1[T,U: SomeInteger|SomeFloat](x: T, y: U): int|float =
+    when T is SomeInteger and U is SomeInteger:
+      result = int(x) + int(y)
+    else:
+      result = float(x) + float(y)
+  doAssert(p1(1,2) == 3)
+  doAssert(p1(1.0,2) == 3.0)
+  doAssert(p1(1,2.0) == 3.0)
+  doAssert(p1(1.0,2.0) == 3.0)
+
+  type Foo[T,U] = U
+  template F[T,U](t: typedesc[T], x: U): untyped = Foo[T,U](x)
+  proc p2[T; U,V:Foo[T,SomeNumber]](x: U, y: V): T =
+    T(x) + T(y)
+  #proc p2[T; U:Foo[T,SomeNumber], V:Foo[not T,SomeNumber]](x: U, y: V): T =
+  #  T(x) + T(y)
+  doAssert(p2(F(int,1),F(int,2)) == 3)
+  doAssert(p2(F(float,1),F(float,2)) == 3.0)
+  doAssert(p2(F(float,1),F(float,2.0)) == 3.0)
+  doAssert(p2(F(float,1.0),F(float,2)) == 3.0)
+  doAssert(p2(F(float,1.0),F(float,2.0)) == 3.0)
+  #doAssert(p2(F(float,1),F(int,2.0)) == 3.0)
+
+block: # PR #23870
+  type
+    A {.inheritable.} = object
+    B = object of A
+    C = object of B
+
+  proc p[T: A](x: T): int = 0
+  proc p[T: B](x: T): int = 1
+
+  proc d(x: A): int = 0
+  proc d(x: B): int = 1
+  
+  proc g[T:A](x: typedesc[T]): int = 0
+  proc g[T: B](x: typedesc[T]): int = 1
+  
+  proc f[T](x: typedesc[T]): int = 0
+  proc f[T:B](x: typedesc[T]): int = 1
+
+  assert p(C()) == 1
+  assert d(C()) == 1
+  assert g(C) == 1
+  assert f(C) == 1
+
+block: # PR #23870
+  type
+    A = object of RootObj
+    PT = proc(ev: A) {.closure.}
+    sdt = seq[(PT, PT)]
+
+  proc encap() =
+    proc p(a: A) {.closure.} =
+      discard
+
+    var s: sdt
+    s.add (p, nil)
+
+  encap()
+
+block: # PR #23870
+  type
+    A = object of RootObj
+    B = object of A
+    C = object of B
+
+  proc p(a: B | RootObj): int =
+    0
+
+  proc p(a: A | A): int =
+    1
+
+  assert p(C()) == 0
+
+  proc d(a: RootObj | B): int =
+    0
+
+  proc d(a: A | A): int =
+    1
+
+  assert d(C()) == 0
diff --git a/tests/overload/toverprc.nim b/tests/overload/toverprc.nim
deleted file mode 100644
index 9be2203f6..000000000
--- a/tests/overload/toverprc.nim
+++ /dev/null
@@ -1,45 +0,0 @@
-discard """
-  output: '''another number: 123
-yay'''
-"""
-
-# Test overloading of procs when used as function pointers
-
-import strutils, sequtils
-
-proc parseInt(x: float): int {.noSideEffect.} = discard
-proc parseInt(x: bool): int {.noSideEffect.} = discard
-proc parseInt(x: float32): int {.noSideEffect.} = discard
-proc parseInt(x: int8): int {.noSideEffect.} = discard
-proc parseInt(x: File): int {.noSideEffect.} = discard
-proc parseInt(x: char): int {.noSideEffect.} = discard
-proc parseInt(x: int16): int {.noSideEffect.} = discard
-
-proc parseInt[T](x: T): int = echo x; 34
-
-type
-  TParseInt = proc (x: string): int {.noSideEffect.}
-
-var
-  q = TParseInt(parseInt)
-  p: TParseInt = parseInt
-
-proc takeParseInt(x: proc (y: string): int {.noSideEffect.}): int =
-  result = x("123")
-
-if false:
-  echo "Give a list of numbers (separated by spaces): "
-  var x = stdin.readline.split.map(parseInt).max
-  echo x, " is the maximum!"
-echo "another number: ", takeParseInt(parseInt)
-
-
-type
-  TFoo[a,b] = object
-    lorem: a
-    ipsum: b
-
-proc bar[a,b](f: TFoo[a,b], x: a) = echo(x, " ", f.lorem, f.ipsum)
-proc bar[a,b](f: TFoo[a,b], x: b) = echo(x, " ", f.lorem, f.ipsum)
-
-discard parseInt[string]("yay")
diff --git a/tests/overload/toverwr.nim b/tests/overload/toverwr.nim
deleted file mode 100644
index 5945a6149..000000000
--- a/tests/overload/toverwr.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-discard """
-  file: "toverwr.nim"
-  output: "hello"
-"""
-# Test the overloading resolution in connection with a qualifier
-
-proc write(t: TFile, s: string) =
-  discard # a nop
-
-system.write(stdout, "hello")
-#OUT hello
-
-
diff --git a/tests/overload/tparams_after_varargs.nim b/tests/overload/tparams_after_varargs.nim
deleted file mode 100644
index ad8f19ad3..000000000
--- a/tests/overload/tparams_after_varargs.nim
+++ /dev/null
@@ -1,19 +0,0 @@
-discard """
-  output: '''a 1 b 2 x @[3, 4, 5] y 6 z 7
-yay
-12
-'''
-"""
-
-proc test(a, b: int, x: varargs[int]; y, z: int) =
-  echo "a ", a, " b ", b, " x ", @x, " y ", y, " z ", z
-
-test 1, 2, 3, 4, 5, 6, 7
-
-# XXX maybe this should also work with ``varargs[untyped]``
-template takesBlockA(a, b: untyped; x: varargs[typed]; blck: untyped): untyped =
-  blck
-  echo a, b
-
-takesBlockA 1, 2, "some", 0.90, "random stuff":
-  echo "yay"
diff --git a/tests/overload/tprefer_specialized_generic.nim b/tests/overload/tprefer_specialized_generic.nim
deleted file mode 100644
index 2b41502d1..000000000
--- a/tests/overload/tprefer_specialized_generic.nim
+++ /dev/null
@@ -1,22 +0,0 @@
-discard """
-  output: '''ref ref T ptr S'''
-"""
-
-proc foo[T](x: T) =
-  echo "only T"
-
-proc foo[T](x: ref T) =
-  echo "ref T"
-
-proc foo[T, S](x: ref ref T; y: ptr S) =
-  echo "ref ref T ptr S"
-
-proc foo[T, S](x: ref T; y: ptr S) =
-  echo "ref T ptr S"
-
-proc foo[T](x: ref T; default = 0) =
-  echo "ref T; default"
-
-var x: ref ref int
-var y: ptr ptr int
-foo(x, y)
diff --git a/tests/overload/tprefer_tygenericinst.nim b/tests/overload/tprefer_tygenericinst.nim
deleted file mode 100644
index ab461a0f4..000000000
--- a/tests/overload/tprefer_tygenericinst.nim
+++ /dev/null
@@ -1,68 +0,0 @@
-discard """
-  output: '''Version 2 was called.
-This has the highest precedence.
-This has the second-highest precedence.
-This has the lowest precedence.
-baseobj ==
-true
-even better! ==
-true'''
-"""
-
-# bug #2220
-when true:
-  type A[T] = object
-  type B = A[int]
-
-  proc q[X](x: X) =
-    echo "Version 1 was called."
-
-  proc q(x: B) =
-    echo "Version 2 was called."
-
-  q(B()) # This call reported as ambiguous.
-
-# bug #2219
-template testPred(a: untyped) =
-  block:
-    type A = object of RootObj
-    type B = object of A
-    type SomeA = A|A # A hack to make "A" a typeclass.
-
-    when a >= 3:
-      proc p[X: A](x: X) =
-        echo "This has the highest precedence."
-    when a == 2:
-      proc p[X: SomeA](x: X) =
-        echo "This has the second-highest precedence."
-    when a >= 1:
-      proc p[X](x: X) =
-        echo "This has the lowest precedence."
-
-    p(B())
-
-testPred(3)
-testPred(2)
-testPred(1)
-
-# bug #6526
-type
-  BaseObj = ref object of RootObj
-  DerivedObj = ref object of BaseObj
-  OtherDerivate = ref object of BaseObj
-
-proc `==`*[T1, T2: BaseObj](a: T1, b: T2): bool =
-  echo "baseobj =="
-  return true
-
-let a = DerivedObj()
-let b = DerivedObj()
-echo a == b
-
-proc `==`*[T1, T2: OtherDerivate](a: T1, b: T2): bool =
-  echo "even better! =="
-  return true
-
-let a2 = OtherDerivate()
-let b2 = OtherDerivate()
-echo a2 == b2
diff --git a/tests/overload/tproc_types_dont_like_subtypes.nim b/tests/overload/tproc_types_dont_like_subtypes.nim
new file mode 100644
index 000000000..6774be156
--- /dev/null
+++ b/tests/overload/tproc_types_dont_like_subtypes.nim
@@ -0,0 +1,20 @@
+discard """
+  errormsg: "got <B, proc (b: B){.closure, gcsafe.}>"
+  line: 20
+"""
+
+type
+  A = ref object of RootObj
+  B = ref object of A
+
+  P = proc (a: A)
+
+# bug #16325
+
+proc doThings(a: A, p: P) =
+  p(a)
+
+var x = proc (b: B) {.closure.} =
+  echo "B"
+
+doThings(B(), x)
diff --git a/tests/overload/tselfderef.nim b/tests/overload/tselfderef.nim
deleted file mode 100644
index 708e4043b..000000000
--- a/tests/overload/tselfderef.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-# bug #4671
-{.experimental.}
-{.this: self.}
-
-type
-  SomeObj = object
-    f: int
-
-proc f(num: int) =
-  discard
-
-var intptr: ptr int
-intptr.f() # compiles fine
-
-proc doSomething(self: var SomeObj) =
-  var pint: ptr int
-  pint.f() # Error: expression '.(pint, "f")' cannot be called
diff --git a/tests/overload/tstatic_with_converter.nim b/tests/overload/tstatic_with_converter.nim
index 2871744eb..2bc1dfaab 100644
--- a/tests/overload/tstatic_with_converter.nim
+++ b/tests/overload/tstatic_with_converter.nim
@@ -1,6 +1,7 @@
 discard """
 output: '''
-9.0'''
+9.0
+'''
 """
 
 ### bug #6773
@@ -37,11 +38,11 @@ proc `^`(x: vfloat, exp: static[float]): vfloat =
   when exp == 0.5:
     sqrt(x)
   else:
-   pow(x, exp)
+    pow(x, exp)
  
 proc `$`(x: vfloat): string =
-  let y = cast[ptr float](unsafeAddr x)
-  echo y[]
+  let y = cast[ptr float](addr x)
+  result = $y[]
  
 let x = set1(9.0)
 echo x^0.5
diff --git a/tests/overload/tstaticoverload.nim b/tests/overload/tstaticoverload.nim
deleted file mode 100644
index 33ca49e56..000000000
--- a/tests/overload/tstaticoverload.nim
+++ /dev/null
@@ -1,30 +0,0 @@
-discard """
-output: '''
-dynamic: let
-dynamic: var
-static: const
-static: literal
-static: constant folding
-static: static string
-'''
-"""
-
-proc foo(s: string) =
-  echo "dynamic: ", s
-
-proc foo(s: static[string]) =
-  echo "static: ", s
-
-let l = "let"
-var v = "var"
-const c = "const"
-
-type staticString = static[string]
-
-foo(l)
-foo(v)
-foo(c)
-foo("literal")
-foo("constant" & " " & "folding")
-foo(staticString("static string"))
-
diff --git a/tests/overload/tstmtoverload.nim b/tests/overload/tstmtoverload.nim
deleted file mode 100644
index 7c0194e60..000000000
--- a/tests/overload/tstmtoverload.nim
+++ /dev/null
@@ -1,38 +0,0 @@
-
-# bug #2481
-import math
-
-template test(loopCount: int, extraI: int, testBody: untyped): typed =
-  block:
-    for i in 0..loopCount-1:
-      testBody
-    echo "done extraI=", extraI
-
-template test(loopCount: int, extraF: float, testBody: untyped): typed =
-  block:
-    test(loopCount, round(extraF).int, testBody)
-
-template test(loopCount: int, testBody: untyped): typed =
-  block:
-    test(loopCount, 0, testBody)
-    echo "done extraI passed 0"
-
-when isMainModule:
-  var
-    loops = 0
-
-  test 0, 0:
-    loops += 1
-  echo "test 0 complete, loops=", loops
-
-  test 1, 1.0:
-    loops += 1
-  echo "test 1.0 complete, loops=", loops
-
-  when true:
-    # when true we get the following compile time error:
-    #   b.nim(35, 6) Error: expression 'loops += 1' has no type (or is ambiguous)
-    loops = 0
-    test 2:
-      loops += 1
-    echo "test no extra complete, loops=", loops
diff --git a/tests/overload/tsymtabchange_during_or.nim b/tests/overload/tsymtabchange_during_or.nim
deleted file mode 100644
index b5551bcc7..000000000
--- a/tests/overload/tsymtabchange_during_or.nim
+++ /dev/null
@@ -1,24 +0,0 @@
-
-# bug #2229
-
-type Type1 = object
-        id: int
-
-type Type2 = object
-    id: int
-
-proc init(self: var Type1, a: int, b: ref Type2) =
-    echo "1"
-
-proc init(self: var Type2, a: int) =
-    echo """
-        Works when this proc commented out
-        Otherwise error:
-        test.nim(14, 4) Error: ambiguous call; both test.init(self: var Type1, a: int, b: ref Type2) and test.init(self: var Type1, a: int, b: ref Type2) match for: (Type1, int literal(1), ref Type2)
-    """
-
-var a: Type1
-init(a, 1, (
-    var b = new(Type2);
-    b
-))
diff --git a/tests/overload/tuntypedarg.nim b/tests/overload/tuntypedarg.nim
new file mode 100644
index 000000000..9aa4fad3b
--- /dev/null
+++ b/tests/overload/tuntypedarg.nim
@@ -0,0 +1,19 @@
+import macros
+
+block: # issue #7385
+  type CustomSeq[T] = object
+    data: seq[T]
+  macro `[]`[T](s: CustomSeq[T], args: varargs[untyped]): untyped =
+    ## The end goal is to replace the joker "_" by something else
+    result = newIntLitNode(10)
+  proc foo1(): CustomSeq[int] =
+    result.data.newSeq(10)
+    # works since no overload matches first argument with type `CustomSeq`
+    # except magic `[]`, which always matches without checking arguments
+    doAssert result[_] == 10
+  doAssert foo1() == CustomSeq[int](data: newSeq[int](10))
+  proc foo2[T](): CustomSeq[T] =
+    result.data.newSeq(10)
+    # works fine with generic return type
+    doAssert result[_] == 10
+  doAssert foo2[int]() == CustomSeq[int](data: newSeq[int](10))
diff --git a/tests/overload/tvart_varargs.nim b/tests/overload/tvart_varargs.nim
deleted file mode 100644
index c0c460c76..000000000
--- a/tests/overload/tvart_varargs.nim
+++ /dev/null
@@ -1,18 +0,0 @@
-
-# bug #4545
-type SomeObject = object
-    a : int
-
-type AbstractObject = object
-  objet: ptr SomeObject
-
-proc convert(this: var SomeObject): AbstractObject =
-  AbstractObject(objet: this.addr)
-
-proc varargProc(args: varargs[AbstractObject, convert]): int =
-  for arg in args:
-    result += arg.objet.a
-
-var obj = SomeObject(a: 17)
-
-discard varargProc(obj)
diff --git a/tests/overload/tvartypeclass.nim b/tests/overload/tvartypeclass.nim
new file mode 100644
index 000000000..04f3f5a91
--- /dev/null
+++ b/tests/overload/tvartypeclass.nim
@@ -0,0 +1,11 @@
+# issue #13302
+
+proc foo(x: object): int = x.i*2
+proc foo(x: var object) = x.i*=2
+type Foo = object
+  i: int
+let x = Foo(i: 3)
+var y = Foo(i: 4)
+doAssert foo(x) == 6
+foo(y)
+doAssert y.i == 8
diff --git a/tests/overload/tvaruintconv.nim b/tests/overload/tvaruintconv.nim
new file mode 100644
index 000000000..87ebd285d
--- /dev/null
+++ b/tests/overload/tvaruintconv.nim
@@ -0,0 +1,207 @@
+discard """
+  action: compile
+"""
+
+# https://github.com/status-im/nimbus-eth2/pull/6554#issuecomment-2354977102
+# failed with "for a 'var' type a variable needs to be passed; but 'uint64(result)' is immutable"
+
+import
+  std/[typetraits, macros]
+
+type
+  DefaultFlavor = object
+
+template serializationFormatImpl(Name: untyped) {.dirty.} =
+  type Name = object
+
+template serializationFormat(Name: untyped) =
+  serializationFormatImpl(Name)
+
+template setReader(Format, FormatReader: distinct type) =
+  when arity(FormatReader) > 1:
+    template Reader(T: type Format, F: distinct type = DefaultFlavor): type = FormatReader[F]
+  else:
+    template ReaderType(T: type Format): type = FormatReader
+    template Reader(T: type Format): type = FormatReader
+
+template useDefaultReaderIn(T: untyped, Flavor: type) =
+  mixin Reader
+
+  template readValue(r: var Reader(Flavor), value: var T) =
+    mixin readRecordValue
+    readRecordValue(r, value)
+
+import mvaruintconv
+
+type
+  FieldTag[RecordType: object; fieldName: static string] = distinct void
+
+func declval*(T: type): T {.compileTime.} =
+  default(ptr T)[]
+
+macro enumAllSerializedFieldsImpl(T: type, body: untyped): untyped =
+  var typeAst = getType(T)[1]
+  var typeImpl: NimNode
+  let isSymbol = not typeAst.isTuple
+
+  if not isSymbol:
+    typeImpl = typeAst
+  else:
+    typeImpl = getImpl(typeAst)
+  result = newStmtList()
+
+  var i = 0
+  for field in recordFields(typeImpl):
+    let
+      fieldIdent = field.name
+      realFieldName = newLit($fieldIdent.skipPragma)
+      fieldName = realFieldName
+      fieldIndex = newLit(i)
+
+    let fieldNameDefs =
+      if isSymbol:
+        quote:
+          const fieldName {.inject, used.} = `fieldName`
+          const realFieldName {.inject, used.} = `realFieldName`
+      else:
+        quote:
+          const fieldName {.inject, used.} = $`fieldIndex`
+          const realFieldName {.inject, used.} = $`fieldIndex`
+
+    let field =
+      if isSymbol:
+        quote do: declval(`T`).`fieldIdent`
+      else:
+        quote do: declval(`T`)[`fieldIndex`]
+
+    result.add quote do:
+      block:
+        `fieldNameDefs`
+
+        template FieldType: untyped {.inject, used.} = typeof(`field`)
+
+        `body`
+
+  # echo repr(result)
+
+template enumAllSerializedFields(T: type, body): untyped =
+  enumAllSerializedFieldsImpl(T, body)
+
+type
+  FieldReader[RecordType, Reader] = tuple[
+    fieldName: string,
+    reader: proc (rec: var RecordType, reader: var Reader)
+                 {.gcsafe, nimcall.}
+  ]
+
+proc totalSerializedFieldsImpl(T: type): int =
+  mixin enumAllSerializedFields
+  enumAllSerializedFields(T): inc result
+
+template totalSerializedFields(T: type): int =
+  (static(totalSerializedFieldsImpl(T)))
+
+template GetFieldType(FT: type FieldTag): type =
+  typeof field(declval(FT.RecordType), FT.fieldName)
+
+proc makeFieldReadersTable(RecordType, ReaderType: distinct type,
+                           numFields: static[int]):
+                           array[numFields, FieldReader[RecordType, ReaderType]] =
+  mixin enumAllSerializedFields, handleReadException
+  var idx = 0
+
+  enumAllSerializedFields(RecordType):
+    proc readField(obj: var RecordType, reader: var ReaderType)
+                  {.gcsafe, nimcall.} =
+
+      mixin readValue
+
+      type F = FieldTag[RecordType, realFieldName]
+      field(obj, realFieldName) = reader.readValue(GetFieldType(F))
+
+    result[idx] = (fieldName, readField)
+    inc idx
+
+proc fieldReadersTable(RecordType, ReaderType: distinct type): auto =
+  mixin readValue
+  type T = RecordType
+  const numFields = totalSerializedFields(T)
+  var tbl {.threadvar.}: ref array[numFields, FieldReader[RecordType, ReaderType]]
+  if tbl == nil:
+    tbl = new typeof(tbl)
+    tbl[] = makeFieldReadersTable(RecordType, ReaderType, numFields)
+  return addr(tbl[])
+
+proc readValue(reader: var auto, T: type): T =
+  mixin readValue
+  reader.readValue(result)
+
+template decode(Format: distinct type,
+                 input: string,
+                 RecordType: distinct type): auto =
+  mixin Reader
+  block:  # https://github.com/nim-lang/Nim/issues/22874
+    var reader: Reader(Format)
+    reader.readValue(RecordType)
+
+template readValue(Format: type,
+                    ValueType: type): untyped =
+  mixin Reader, init, readValue
+  var reader: Reader(Format)
+  readValue reader, ValueType
+
+template parseArrayImpl(numElem: untyped,
+                        actionValue: untyped) =
+  actionValue
+
+serializationFormat Json
+template createJsonFlavor(FlavorName: untyped,
+                           skipNullFields = false) {.dirty.} =
+  type FlavorName = object
+
+  template Reader(T: type FlavorName): type = Reader(Json, FlavorName)
+type
+  JsonReader[Flavor = DefaultFlavor] = object
+
+Json.setReader JsonReader
+
+template parseArray(r: var JsonReader; body: untyped) =
+  parseArrayImpl(idx): body
+
+template parseArray(r: var JsonReader; idx: untyped; body: untyped) =
+  parseArrayImpl(idx): body
+
+proc readRecordValue[T](r: var JsonReader, value: var T) =
+  type
+    ReaderType {.used.} = type r
+    T = type value
+
+  discard T.fieldReadersTable(ReaderType)
+
+proc readValue[T](r: var JsonReader, value: var T) =
+  mixin readValue
+
+  when value is seq:
+    r.parseArray:
+      readValue(r, value[0])
+
+  elif value is object:
+    readRecordValue(r, value)
+
+type
+  RemoteSignerInfo = object
+    id: uint32
+  RemoteKeystore = object
+
+proc readValue(reader: var JsonReader, value: var RemoteKeystore) =
+  discard reader.readValue(seq[RemoteSignerInfo])
+
+createJsonFlavor RestJson
+useDefaultReaderIn(RemoteSignerInfo, RestJson)
+proc readValue(reader: var JsonReader[RestJson], value: var uint64) =
+  discard reader.readValue(string)
+
+discard Json.decode("", RemoteKeystore)
+block:  # https://github.com/nim-lang/Nim/issues/22874
+  var reader: Reader(RestJson)
+  discard reader.readValue(RemoteSignerInfo)
diff --git a/tests/package/stdlib/stdlib.nimble b/tests/package/stdlib/stdlib.nimble
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/package/stdlib/stdlib.nimble
diff --git a/tests/package/stdlib/system.nim b/tests/package/stdlib/system.nim
new file mode 100644
index 000000000..475f8ec5b
--- /dev/null
+++ b/tests/package/stdlib/system.nim
@@ -0,0 +1,2 @@
+# this module is part of tstdlib_name_not_special
+doAssert true
\ No newline at end of file
diff --git a/tests/package/tstdlib_name_not_special.nim b/tests/package/tstdlib_name_not_special.nim
new file mode 100644
index 000000000..e8226a82d
--- /dev/null
+++ b/tests/package/tstdlib_name_not_special.nim
@@ -0,0 +1,3 @@
+# Test whether a another package named 'stdlib' can be imported and used.
+# This caused a crash in the past.
+import stdlib/system
\ No newline at end of file
diff --git a/tests/parallel/t10913.nim b/tests/parallel/t10913.nim
new file mode 100644
index 000000000..191939100
--- /dev/null
+++ b/tests/parallel/t10913.nim
@@ -0,0 +1,20 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  errormsg: "'spawn'ed function cannot have a 'typed' or 'untyped' parameter"
+"""
+
+# bug #10913
+
+import threadpool
+
+proc useParallel*[T](unused: T) =
+  # use a generic T here to show the problem.
+  {.push experimental: "parallel".}
+  parallel:
+    for i in 0..4:
+      spawn echo "echo in parallel"
+  sync()
+  
+  {.pop.}
+
+useParallel(1)
diff --git a/tests/parallel/t7535.nim b/tests/parallel/t7535.nim
new file mode 100644
index 000000000..7817a1c9e
--- /dev/null
+++ b/tests/parallel/t7535.nim
@@ -0,0 +1,11 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  errormsg: "'spawn' takes a call expression; got: proc (x: uint32) = echo [x]"
+"""
+
+import threadpool
+
+# bug #7535
+proc print_parallel_nok(r: uint32) =
+  for x in 0..r:
+    spawn (proc (x: uint32) = echo x)
diff --git a/tests/parallel/t9691.nim b/tests/parallel/t9691.nim
new file mode 100644
index 000000000..254f03416
--- /dev/null
+++ b/tests/parallel/t9691.nim
@@ -0,0 +1,9 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  errormsg: "'spawn'ed function cannot have a 'typed' or 'untyped' parameter"
+"""
+
+# bug #9691
+
+import threadpool
+spawn echo()
diff --git a/tests/parallel/tarray_of_channels.nim b/tests/parallel/tarray_of_channels.nim
index 90ae8369c..9479227aa 100644
--- a/tests/parallel/tarray_of_channels.nim
+++ b/tests/parallel/tarray_of_channels.nim
@@ -1,3 +1,16 @@
+discard """
+sortoutput: true
+output: '''
+(x: 0.0)
+(x: 0.0)
+(x: 0.0)
+test
+test
+test
+'''
+disabled: "openbsd"
+"""
+
 # bug #2257
 import threadpool
 
@@ -22,5 +35,5 @@ proc main =
   sync()
   for ix in 1..3: channels[ix].close()
 
-when isMainModule:
+when true:
   main()
diff --git a/tests/parallel/tblocking_channel.nim b/tests/parallel/tblocking_channel.nim
index 8b8b49454..f3ccd166a 100644
--- a/tests/parallel/tblocking_channel.nim
+++ b/tests/parallel/tblocking_channel.nim
@@ -1,6 +1,8 @@
 discard """
 output: ""
+disabled: "freebsd" # see #15725
 """
+
 import threadpool, os
 
 var chan: Channel[int]
@@ -25,11 +27,11 @@ proc emitter() =
 
 spawn emitter()
 # At this point emitter should be stuck in `send`
-sleep(100) # Sleep a bit to ensure that it is still stuck
+sleep(50) # Sleep a bit to ensure that it is still stuck
 doAssert(not msgSent)
 
 spawn receiver()
-sleep(100) # Sleep a bit to let receicer consume the messages
+sleep(50) # Sleep a bit to let receicer consume the messages
 doAssert(msgSent) # Sender should be unblocked
 
 doAssert(chan.trySend(4))
diff --git a/tests/parallel/tconvexhull.nim b/tests/parallel/tconvexhull.nim
index c4990bf2d..a89aa910b 100644
--- a/tests/parallel/tconvexhull.nim
+++ b/tests/parallel/tconvexhull.nim
@@ -1,12 +1,7 @@
 discard """
-  output: '''true
-true
-true
-true
-true
-true'''
-
-ccodeCheck: "\\i ! @'deepCopy(' .*"
+  matrix: "--mm:refc"
+  output: '''
+'''
 """
 
 # parallel convex hull for Nim bigbreak
@@ -55,10 +50,11 @@ proc convex_hull[T](points: var seq[T], cmp: proc(x, y: T): int {.closure.}) : s
       ul[k] = spawn half[T](points, k == 0)
   result = concat(^ul[0], ^ul[1])
 
-var s = map(toSeq(0..999999), proc(x: int): Point = (float(x div 1000), float(x mod 1000)))
+var s = map(toSeq(0..9999), proc(x: int): Point = (float(x div 100), float(x mod 100)))
+# On some runs, this pool size reduction will set the "shutdown" attribute on the
+# worker thread that executes our spawned task, before we can read the flowvars.
 setMaxPoolSize 2
 
-#echo convex_hull[Point](s, cmpPoint)
-for i in 0..5:
-  echo convex_hull[Point](s, cmpPoint) ==
-      @[(0.0, 0.0), (999.0, 0.0), (999.0, 999.0), (0.0, 999.0)]
+for i in 0..2:
+  doAssert convex_hull[Point](s, cmpPoint) ==
+      @[(0.0, 0.0), (99.0, 0.0), (99.0, 99.0), (0.0, 99.0)]
diff --git a/tests/parallel/tdeepcopy.nim b/tests/parallel/tdeepcopy.nim
index 84e2edf3f..96ca15ca3 100644
--- a/tests/parallel/tdeepcopy.nim
+++ b/tests/parallel/tdeepcopy.nim
@@ -1,18 +1,55 @@
 discard """
-  output: '''13 abc'''
+  matrix: "--mm:refc"
+  output: '''
+13 abc
+called deepCopy for int
+called deepCopy for int
+done999 999
+'''
 """
 
-type
-  PBinaryTree = ref object
-    le, ri: PBinaryTree
-    value: int
+import threadpool
 
+block one:
+  type
+    PBinaryTree = ref object
+      le, ri: PBinaryTree
+      value: int
 
-proc main =
-  var x: PBinaryTree
-  deepCopy(x, PBinaryTree(ri: PBinaryTree(le: PBinaryTree(value: 13))))
-  var y: string
-  deepCopy y, "abc"
-  echo x.ri.le.value, " ", y
+  proc main =
+    var x: PBinaryTree
+    deepCopy(x, PBinaryTree(ri: PBinaryTree(le: PBinaryTree(value: 13))))
+    var y: string
+    deepCopy y, "abc"
+    echo x.ri.le.value, " ", y
 
-main()
+  main()
+
+
+block two:
+  type
+    Bar[T] = object
+      x: T
+
+  proc `=deepCopy`[T](b: ref Bar[T]): ref Bar[T] =
+    result.new
+    result.x = b.x
+    when T is int:
+      echo "called deepCopy for int"
+    else:
+      echo "called deepCopy for something else"
+
+  proc foo(b: ref Bar[int]): int = 999
+
+# test that the disjoint checker deals with 'a = spawn f(); g = spawn f()':
+
+  proc main =
+    var dummy: ref Bar[int]
+    new(dummy)
+    dummy.x = 44
+    #parallel:
+    let f = spawn foo(dummy)
+    let b = spawn foo(dummy)
+    echo "done", ^f, " ", ^b
+
+  main()
diff --git a/tests/parallel/tdeepcopy2.nim b/tests/parallel/tdeepcopy2.nim
index 8ffdcc5f2..e8305173d 100644
--- a/tests/parallel/tdeepcopy2.nim
+++ b/tests/parallel/tdeepcopy2.nim
@@ -1,7 +1,10 @@
 discard """
-  output: '''called deepCopy for int
+  matrix: "--mm:refc"
+  output: '''
 called deepCopy for int
-done999 999'''
+called deepCopy for int
+done999 999
+'''
 """
 
 import threadpool
@@ -11,7 +14,7 @@ type
   Bar[T] = object
     x: T
 
-proc deepCopy[T](b: ref Bar[T]): ref Bar[T] {.override.} =
+proc `=deepCopy`[T](b: ref Bar[T]): ref Bar[T] =
   result.new
   result.x = b.x
   when T is int:
diff --git a/tests/parallel/tdisjoint_slice1.nim b/tests/parallel/tdisjoint_slice1.nim
index c1d0e52f8..6892e7383 100644
--- a/tests/parallel/tdisjoint_slice1.nim
+++ b/tests/parallel/tdisjoint_slice1.nim
@@ -1,21 +1,51 @@
 discard """
+  matrix: "--mm:refc"
   outputsub: "EVEN 28"
 """
 
-import threadpool
+import threadpool, locks
 
-proc odd(a: int) =  echo "ODD  ", a
-proc even(a: int) = echo "EVEN ", a
+block one:
+  proc odd(a: int) =  echo "ODD  ", a
+  proc even(a: int) = echo "EVEN ", a
 
-proc main() =
-  var a: array[0..30, int]
-  for i in low(a)..high(a): a[i] = i
-  parallel:
-    var i = 0
-    while i <= 29:
-      spawn even(a[i])
-      spawn odd(a[i+1])
-      inc i, 2
-      # is correct here
+  proc main() =
+    var a: array[0..30, int]
+    for i in low(a)..high(a): a[i] = i
+    parallel:
+      var i = 0
+      while i <= 29:
+        spawn even(a[i])
+        spawn odd(a[i+1])
+        inc i, 2
+        # is correct here
 
-main()
+  main()
+
+
+block two:
+  var echoLock: Lock
+  initLock echoLock
+
+  proc f(a: openArray[int]) =
+    for x in a:
+      withLock echoLock:
+        echo x
+
+  proc f(a: int) =
+    withLock echoLock:
+      echo a
+
+  proc main() =
+    var a: array[0..9, int] = [0,1,2,3,4,5,6,7,8,9]
+    parallel:
+      spawn f(a[0..2])
+      #spawn f(a[16..30])
+      var i = 3
+      while i <= 8:
+        spawn f(a[i])
+        spawn f(a[i+1])
+        inc i, 2
+        # is correct here
+
+  main()
diff --git a/tests/parallel/tdisjoint_slice2.nim b/tests/parallel/tdisjoint_slice2.nim
deleted file mode 100644
index 25cb2362f..000000000
--- a/tests/parallel/tdisjoint_slice2.nim
+++ /dev/null
@@ -1,40 +0,0 @@
-discard """
-  output: '''0
-1
-2
-3
-4
-5
-6
-7
-8'''
-  sortoutput: true
-"""
-
-import threadpool, locks
-
-var echoLock: Lock
-initLock echoLock
-
-proc f(a: openArray[int]) =
-  for x in a:
-    withLock echoLock:
-      echo x
-
-proc f(a: int) =
-  withLock echoLock:
-    echo a
-
-proc main() =
-  var a: array[0..9, int] = [0,1,2,3,4,5,6,7,8,9]
-  parallel:
-    spawn f(a[0..2])
-    #spawn f(a[16..30])
-    var i = 3
-    while i <= 8:
-      spawn f(a[i])
-      spawn f(a[i+1])
-      inc i, 2
-      # is correct here
-
-main()
diff --git a/tests/parallel/tdont_be_stupid.nim b/tests/parallel/tdont_be_stupid.nim
deleted file mode 100644
index a7e82466a..000000000
--- a/tests/parallel/tdont_be_stupid.nim
+++ /dev/null
@@ -1,15 +0,0 @@
-
-import threadpool, os
-
-proc single(time: int) =
-  sleep time
-  echo time
-
-proc sleepsort(nums: openArray[int]) =
-  parallel:
-    var i = 0
-    while i <= len(nums) + -1:
-      spawn single(nums[i])
-      i += 1
-
-sleepsort([50,3,40,25])
diff --git a/tests/parallel/tflowvar.nim b/tests/parallel/tflowvar.nim
index fd3aa326e..e44b29a87 100644
--- a/tests/parallel/tflowvar.nim
+++ b/tests/parallel/tflowvar.nim
@@ -1,9 +1,11 @@
 discard """
+  matrix: "--mm:refc"
   output: '''foobarfoobar
 bazbearbazbear
 
 1'''
   cmd: "nim $target --threads:on $options $file"
+  disabled: "openbsd"
 """
 
 import threadpool
diff --git a/tests/parallel/tgc_unsafe.nim b/tests/parallel/tgc_unsafe.nim
index a4d96cd73..baf0dc24a 100644
--- a/tests/parallel/tgc_unsafe.nim
+++ b/tests/parallel/tgc_unsafe.nim
@@ -28,5 +28,5 @@ proc main =
   sync()
   for ix in 1..3: channels[ix].close()
 
-when isMainModule:
+when true:
   main()
diff --git a/tests/parallel/tgc_unsafe2.nim b/tests/parallel/tgc_unsafe2.nim
index 40bfbdadb..7d98dafcb 100644
--- a/tests/parallel/tgc_unsafe2.nim
+++ b/tests/parallel/tgc_unsafe2.nim
@@ -1,10 +1,9 @@
 discard """
-  line: 28
-  nimout: '''tgc_unsafe2.nim(22, 6) Warning: 'trick' is not GC-safe as it accesses 'global' which is a global using GC'ed memory [GcUnsafe2]
-tgc_unsafe2.nim(26, 6) Warning: 'track' is not GC-safe as it calls 'trick' [GcUnsafe2]
-tgc_unsafe2.nim(28, 6) Error: 'consumer' is not GC-safe as it calls 'track'
-'''
   errormsg: "'consumer' is not GC-safe as it calls 'track'"
+  nimout: '''tgc_unsafe2.nim(21, 6) Warning: 'trick' is not GC-safe as it accesses 'global' which is a global using GC'ed memory [GcUnsafe2]
+tgc_unsafe2.nim(25, 6) Warning: 'track' is not GC-safe as it calls 'trick' [GcUnsafe2]
+tgc_unsafe2.nim(27, 6) Error: 'consumer' is not GC-safe as it calls 'track'
+'''
 """
 
 import threadpool
@@ -35,5 +34,5 @@ proc main =
   sync()
   for ix in 1..3: channels[ix].close()
 
-when isMainModule:
+when true:
   main()
diff --git a/tests/parallel/tguard1.nim b/tests/parallel/tguard1.nim
index c7972d225..f4c92319b 100644
--- a/tests/parallel/tguard1.nim
+++ b/tests/parallel/tguard1.nim
@@ -1,7 +1,11 @@
+discard """
+output: "90"
+"""
+
 
 when false:
   template lock(a, b: ptr Lock; body: stmt) =
-    if cast[ByteAddress](a) < cast[ByteAddress](b):
+    if cast[int](a) < cast[int](b):
       pthread_mutex_lock(a)
       pthread_mutex_lock(b)
     else:
diff --git a/tests/parallel/tinvalid_array_bounds.nim b/tests/parallel/tinvalid_array_bounds.nim
index 4c6065fd6..8dc93c33f 100644
--- a/tests/parallel/tinvalid_array_bounds.nim
+++ b/tests/parallel/tinvalid_array_bounds.nim
@@ -1,5 +1,6 @@
 discard """
-  errormsg: "can prove: i + 1 > 30"
+  matrix: "--mm:refc"
+  errormsg: "cannot prove (i)..(i) disjoint from (i + 1)..(i + 1)"
   line: 21
 """
 
diff --git a/tests/parallel/tlet_spawn.nim b/tests/parallel/tlet_spawn.nim
index 463ee1a47..853ffc443 100644
--- a/tests/parallel/tlet_spawn.nim
+++ b/tests/parallel/tlet_spawn.nim
@@ -1,5 +1,10 @@
+discard """
+output: '''
+done999 999
+'''
+"""
 
-import threadpool
+import std/[threadpool, os]
 
 proc foo(): int = 999
 
@@ -12,3 +17,12 @@ proc main =
   echo "done", f, " ", b
 
 main()
+
+# bug #13781
+proc thread(): string =
+  os.sleep(1000)
+  return "ok"
+
+var fv = spawn thread()
+sync()
+doAssert ^fv == "ok"
diff --git a/tests/parallel/tmissing_deepcopy.nim b/tests/parallel/tmissing_deepcopy.nim
index 53481e4df..ea77936ad 100644
--- a/tests/parallel/tmissing_deepcopy.nim
+++ b/tests/parallel/tmissing_deepcopy.nim
@@ -1,5 +1,7 @@
 discard """
-  ccodeCheck: "\\i @'deepCopy(' .*"
+  matrix: "--mm:refc"
+  ccodeCheck: "@'genericDeepCopy(' .*"
+  action: compile
 """
 
 # bug #2286
@@ -25,16 +27,16 @@ proc greet(p:Person) =
     " friend:", p.friend.name, "(", cast[int](addr p.friend.name),") }"
 
 proc setup =
-  for i in 0 .. <20:
+  for i in 0 ..< 10:
     people.add newPerson("Person" & $(i + 1))
-  for i in 0 .. <20:
-    people[i].friend = people[19-i]
+  for i in 0 ..< 10:
+    people[i].friend = people[9-i]
 
 proc update =
   parallel:
     for i in 0 .. people.high:
       spawn people[i].greet()
 
-when isMainModule:
+when true:
   setup()
   update()
diff --git a/tests/parallel/tnon_disjoint_slice1.nim b/tests/parallel/tnon_disjoint_slice1.nim
index 72d008bbd..51762187d 100644
--- a/tests/parallel/tnon_disjoint_slice1.nim
+++ b/tests/parallel/tnon_disjoint_slice1.nim
@@ -1,6 +1,7 @@
 discard """
+  matrix: "--mm:refc"
   errormsg: "cannot prove (i)..(i) disjoint from (i + 1)..(i + 1)"
-  line: 20
+  line: 21
 """
 
 import threadpool
diff --git a/tests/parallel/tparfind.nim b/tests/parallel/tparfind.nim
index 4b3610c67..cf1bc9336 100644
--- a/tests/parallel/tparfind.nim
+++ b/tests/parallel/tparfind.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc"
   output: "500"
 """
 
diff --git a/tests/parallel/tpi.nim b/tests/parallel/tpi.nim
index dcb9b8fc5..cd965d585 100644
--- a/tests/parallel/tpi.nim
+++ b/tests/parallel/tpi.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc"
   output: '''3.141792613595791
 3.141792613595791'''
 """
@@ -9,9 +10,9 @@ proc term(k: float): float = 4 * math.pow(-1, k) / (2*k + 1)
 
 proc piU(n: int): float =
   var ch = newSeq[FlowVar[float]](n+1)
-  for k in 0..n:
+  for k in 0..ch.high:
     ch[k] = spawn term(float(k))
-  for k in 0..n:
+  for k in 0..ch.high:
     result += ^ch[k]
 
 proc piS(n: int): float =
diff --git a/tests/parallel/tptr_to_ref.nim b/tests/parallel/tptr_to_ref.nim
index fee210dcd..ae02c16e5 100644
--- a/tests/parallel/tptr_to_ref.nim
+++ b/tests/parallel/tptr_to_ref.nim
@@ -11,7 +11,7 @@ type
   Killer* = object
     lock:                      Lock
     bailed    {.guard: lock.}: bool
-    processes {.guard: lock.}: array[0..MAX_WORKERS-1, foreign ptr Process]
+    processes {.guard: lock.}: array[0..MAX_WORKERS-1, ptr Process]
 
 # Hold a lock for a statement.
 template hold(lock: Lock, body: untyped) =
@@ -32,7 +32,7 @@ proc initKiller*(): Killer =
 var killer = initKiller()
 
 # remember that a process has been launched, killing it if we have bailed.
-proc launched*(process: foreign ptr Process): int {.gcsafe.} =
+proc launched*(process: ptr Process): int {.gcsafe.} =
   result = killer.processes.high + 1
   killer.lock.hold:
     if killer.bailed:
diff --git a/tests/parallel/tsendtwice.nim b/tests/parallel/tsendtwice.nim
index 0700fc4da..9f4a2e06e 100644
--- a/tests/parallel/tsendtwice.nim
+++ b/tests/parallel/tsendtwice.nim
@@ -1,24 +1,17 @@
 discard """
-  output: '''obj2 nil
-obj nil
-obj3 nil
-3
-obj2 nil
-obj nil
-obj3 nil'''
-  cmd: "nim c -r --threads:on $file"
+  matrix: "--mm:refc"
 """
 
 # bug #4776
 
-import tables
+import tables, algorithm
 
 type
   Base* = ref object of RootObj
     someSeq: seq[int]
-    baseData: array[400000, byte]
+    baseData: array[40000, byte]
   Derived* = ref object of Base
-    data: array[400000, byte]
+    data: array[40000, byte]
 
 type
   ThreadPool = ref object
@@ -30,24 +23,25 @@ type
 var globalTable {.threadvar.}: TableRef[string, Base]
 globalTable = newTable[string, Base]()
 let d = new(Derived)
-globalTable.add("obj", d)
-globalTable.add("obj2", d)
-globalTable.add("obj3", d)
+globalTable.add("ob", d)
+globalTable.add("ob2", d)
+globalTable.add("ob3", d)
+
+proc `<`(x, y: seq[int]): bool = x.len < y.len
+proc kvs(t: TableRef[string, Base]): seq[(string, seq[int])] =
+  for k, v in t.pairs: result.add (k, v.someSeq)
+  result.sort
 
 proc testThread(channel: ptr TableChannel) {.thread.} =
   globalTable = channel[].recv()
-  for k, v in pairs globaltable:
-    echo k, " ", v.someSeq
   var myObj: Base
-  deepCopy(myObj, globalTable["obj"])
+  deepCopy(myObj, globalTable["ob"])
   myObj.someSeq = newSeq[int](100)
   let table = channel[].recv() # same table
-  echo table.len
-  for k, v in mpairs table:
-    echo k, " ", v.someSeq
-  assert(table.contains("obj")) # fails!
-  assert(table.contains("obj2")) # fails!
-  assert(table.contains("obj3")) # fails!
+  assert(table.contains("ob")) # fails!
+  assert(table.contains("ob2")) # fails!
+  assert(table.contains("ob3")) # fails!
+  assert table.kvs == globalTable.kvs # Last to see above spot checks first
 
 var channel: TableChannel
 
diff --git a/tests/parallel/tsimple_array_checks.nim b/tests/parallel/tsimple_array_checks.nim
index 9874d3299..ab292f935 100644
--- a/tests/parallel/tsimple_array_checks.nim
+++ b/tests/parallel/tsimple_array_checks.nim
@@ -1,3 +1,25 @@
+discard """
+sortoutput: true
+output: '''
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+Hello 1
+Hello 2
+Hello 3
+Hello 4
+Hello 5
+Hello 6
+'''
+"""
+
 # bug #2287
 
 import threadPool
@@ -13,9 +35,9 @@ proc main =
   parallel:
     for n in nums: # Error: cannot prove: i <= len(nums) + -1
       spawn log(n)
-    #for i in 0 .. <nums.len: # Error: cannot prove: i <= len(nums) + -1
+    #for i in 0 ..< nums.len: # Error: cannot prove: i <= len(nums) + -1
     #for i in 0 .. nums.len-1: # WORKS!
-    #for i in 0 .. <nums.len: # WORKS!
+    #for i in 0 ..< nums.len: # WORKS!
     #  spawn log(nums[i])
 
 # Array needs explicit size to work, probably related to issue #2287
@@ -37,5 +59,17 @@ proc maino =
 
 maino() # Doesn't work outside a proc
 
-when isMainModule:
+when true:
   main()
+
+block two:
+  proc f(a: openArray[int]) =
+    discard
+
+  proc main() =
+    var a: array[0..9, int] = [0,1,2,3,4,5,6,7,8,9]
+    parallel:
+      spawn f(a[0..2])
+
+
+  main()
\ No newline at end of file
diff --git a/tests/parallel/tsysspawn.nim b/tests/parallel/tsysspawn.nim
index 7244a5ee6..b7ecd1264 100644
--- a/tests/parallel/tsysspawn.nim
+++ b/tests/parallel/tsysspawn.nim
@@ -1,7 +1,11 @@
 discard """
   output: '''4
-8'''
-  cmd: "nim $target --threads:on $options $file"
+8
+(a: 1)
+2
+2
+'''
+  matrix: "--mm:refc"
 """
 
 import threadpool
@@ -29,3 +33,32 @@ sync()
 
 echo x
 echo y
+
+
+#--------------------------------------------------------
+# issue #14014
+
+import threadpool
+
+type A = object
+    a: int
+
+proc f(t: typedesc): t =
+  t(a:1)
+
+let r = spawn f(A)
+echo ^r
+
+proc f2(x: static[int]): int =
+  x
+
+let r2 = spawn f2(2)
+echo ^r2
+
+type statint = static[int]
+
+proc f3(x: statint): int =
+  x
+
+let r3 = spawn f3(2)
+echo ^r3
diff --git a/tests/parallel/tsysspawnbadarg.nim b/tests/parallel/tsysspawnbadarg.nim
index 2d3ffd241..a8f1ed401 100644
--- a/tests/parallel/tsysspawnbadarg.nim
+++ b/tests/parallel/tsysspawnbadarg.nim
@@ -1,6 +1,6 @@
 discard """
-  line: 9
   errormsg: "'spawn' takes a call expression"
+  line: 9
   cmd: "nim $target --threads:on $options $file"
 """
 
diff --git a/tests/parallel/tuseafterdef.nim b/tests/parallel/tuseafterdef.nim
index 833c72a0a..64f835a1b 100644
--- a/tests/parallel/tuseafterdef.nim
+++ b/tests/parallel/tuseafterdef.nim
@@ -1,6 +1,9 @@
 discard """
+  matrix: "--mm:refc"
+  disabled: true
   errormsg: "(k)..(k) not disjoint from (k)..(k)"
-  line: 23
+  line: 24
+  action: compile
 """
 
 # bug #1597
diff --git a/tests/parallel/twaitany.nim b/tests/parallel/twaitany.nim
index 69136a3b6..d57c5f40f 100644
--- a/tests/parallel/twaitany.nim
+++ b/tests/parallel/twaitany.nim
@@ -1,9 +1,10 @@
 discard """
+  matrix: "--mm:refc"
   output: '''true'''
 """
 
 # bug #7638
-import threadpool, os, strformat
+import threadpool, os
 
 proc timer(d: int): int =
   #echo fmt"sleeping {d}"
@@ -11,25 +12,23 @@ proc timer(d: int): int =
   #echo fmt"done {d}"
   return d
 
-var durations = [1000, 2000, 3000, 4000, 5000]
+var durations = [1000, 1500, 2000]
 var tasks: seq[FlowVarBase] = @[]
 var results: seq[int] = @[]
 
 for i in 0 .. durations.high:
   tasks.add spawn timer(durations[i])
 
-var index = awaitAny(tasks)
+var index = blockUntilAny(tasks)
 while index != -1:
   results.add ^cast[FlowVar[int]](tasks[index])
   tasks.del(index)
   #echo repr results
-  index = awaitAny(tasks)
+  index = blockUntilAny(tasks)
 
-doAssert results.len == 5
+doAssert results.len == 3
 doAssert 1000 in results
+doAssert 1500 in results
 doAssert 2000 in results
-doAssert 3000 in results
-doAssert 4000 in results
-doAssert 5000 in results
 sync()
 echo "true"
diff --git a/tests/parallel/twrong_refcounts.nim b/tests/parallel/twrong_refcounts.nim
deleted file mode 100644
index 57e0588a0..000000000
--- a/tests/parallel/twrong_refcounts.nim
+++ /dev/null
@@ -1,53 +0,0 @@
-discard """
-  output: "Success"
-"""
-
-import math, random, threadPool
-
-# ---
-
-type
-  Person = object
-    age: int
-    friend: ref Person
-
-var
-  people: seq[ref Person] = @[]
-
-proc newPerson(age:int): ref Person =
-  result.new()
-  result.age = age
-
-proc greet(p:Person) =
-  #echo p.age, ", ", p.friend.age
-  p.friend.age += 1
-
-# ---
-
-proc setup =
-  for i in 0 .. <20:
-    people.add newPerson(i + 1)
-  for i in 0 .. <20:
-    people[i].friend = people[random(20)]
-
-proc update =
-  var countA: array[20, int]
-  var countB: array[20, int]
-
-  for i, p in people:
-    countA[i] = getRefCount(p)
-  parallel:
-    for i in 0 .. people.high:
-      spawn greet(people[i][])
-  for i, p in people:
-    countB[i] = getRefCount(p)
-
-  for i in 0 .. <20:
-    doAssert countA[i] == countB[i]
-  echo "Success"
-
-# ---
-
-when isMainModule:
-  setup()
-  update()
diff --git a/tests/parser/t12274.nim b/tests/parser/t12274.nim
new file mode 100644
index 000000000..6b7c9f55a
--- /dev/null
+++ b/tests/parser/t12274.nim
@@ -0,0 +1,13 @@
+discard """
+  joinable: false
+"""
+
+var s: seq[int]
+s.add block:
+  let i = 1
+  i
+s.add try:
+  2
+except:
+  3
+doAssert s == @[1, 2]
diff --git a/tests/parser/t15667.nim b/tests/parser/t15667.nim
new file mode 100644
index 000000000..fcb862825
--- /dev/null
+++ b/tests/parser/t15667.nim
@@ -0,0 +1,61 @@
+discard """
+  cmd: "nim check $options $file"
+  action: "reject"
+  nimout: '''
+t15667.nim(23, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.nim(22, 13) ?
+t15667.nim(28, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.nim(26, 13) ?
+t15667.nim(33, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.nim(31, 25) ?
+t15667.nim(42, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.nim(38, 12) ?
+t15667.nim(56, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.nim(55, 13) ?
+t15667.nim(61, 48) Error: expression expected, but found ','
+'''
+"""
+
+
+
+
+
+
+
+# line 20
+block:
+  proc fn1()
+    discard
+
+block:
+  proc fn2()
+    #
+    discard
+
+block:
+  proc fn3() {.exportc.}
+    #
+    discard
+
+block: # complex example
+  proc asdfasdfsd() {. exportc, 
+      inline
+         .}     # foo
+    #[
+    bar
+    ]#
+    discard
+
+block: # xxx this doesn't work yet (only a bare `invalid indentation` error)
+  proc fn5()
+    ##
+    discard
+
+block: # ditto
+  proc fn6*()
+    ## foo bar
+    runnableExamples: discard
+
+block:
+  proc fn8()
+    runnableExamples:
+      discard
+    discard
+
+# semiStmtList loop issue
+proc bar(k:static bool):SomeNumber = (when k: 3, else: 3.0)
diff --git a/tests/parser/t19430.nim b/tests/parser/t19430.nim
new file mode 100644
index 000000000..c1aa6a92d
--- /dev/null
+++ b/tests/parser/t19430.nim
@@ -0,0 +1,3 @@
+let x = proc() = ## abc
+let y = 3 #[tt.Error
+^ invalid indentation]#
diff --git a/tests/parser/t19662.nim b/tests/parser/t19662.nim
new file mode 100644
index 000000000..7a1864ac6
--- /dev/null
+++ b/tests/parser/t19662.nim
@@ -0,0 +1,6 @@
+                var i: int # bug #19662
+echo i
+
+discard """
+  errormsg: "invalid indentation"
+"""
\ No newline at end of file
diff --git a/tests/parser/t20922.nim b/tests/parser/t20922.nim
new file mode 100644
index 000000000..110610fb1
--- /dev/null
+++ b/tests/parser/t20922.nim
@@ -0,0 +1,35 @@
+discard """
+  cmd: "nim check $options --verbosity:0 --hints:off $file"
+  action: "reject"
+  nimout: '''
+t20922.nim(26, 5) Error: expression expected, but found ':'
+t20922.nim(34, 7) Error: expression expected, but found ':'
+t20922.nim(35, 5) Error: ':' or '=' expected, but got 'keyword of'
+Error: in expression ' '+'': identifier expected, but found ''
+t20922.nim(26, 7) Error: attempting to call undeclared routine: '<Error>'
+Error: in expression ' '+'': identifier expected, but found ''
+t20922.nim(26, 7) Error: attempting to call undeclared routine: '<Error>'
+t20922.nim(26, 7) Error: expression '' cannot be called
+t20922.nim(26, 7) Error: expression '' has no type (or is ambiguous)
+t20922.nim(26, 7) Error: VM problem: dest register is not set
+'''
+"""
+# original test case issue #20922
+type Token = enum
+  incDataPtr,
+  incDataPtrByte
+
+proc mapInstrToToken(instr: char): Token =
+  case instr:
+  of '>':
+    incDataPtr
+  of: '+':
+    incDataPtrByte
+
+# same issue with `of` in object branches (different parser procs calling `exprList`)
+type
+  Bar = enum A, B
+  Foo = object
+    case kind: Bar
+    of: x: int
+    of B: y: float
diff --git a/tests/parser/tbinarynotindented.nim b/tests/parser/tbinarynotindented.nim
new file mode 100644
index 000000000..8d124aade
--- /dev/null
+++ b/tests/parser/tbinarynotindented.nim
@@ -0,0 +1,3 @@
+type Foo = ref int
+  not nil #[tt.Error
+  ^ invalid indentation]#
diff --git a/tests/parser/tbinarynotsameline.nim b/tests/parser/tbinarynotsameline.nim
new file mode 100644
index 000000000..ca417e023
--- /dev/null
+++ b/tests/parser/tbinarynotsameline.nim
@@ -0,0 +1,10 @@
+# issue #23565
+
+func foo: bool =
+  true
+
+const bar = block:
+  type T = int
+  not foo()
+
+doAssert not bar
diff --git a/tests/parser/tcommand_as_expr.nim b/tests/parser/tcommand_as_expr.nim
index b25ec4bd8..f37c34f63 100644
--- a/tests/parser/tcommand_as_expr.nim
+++ b/tests/parser/tcommand_as_expr.nim
@@ -36,3 +36,12 @@ echo f -4
 
 echo int -1 # doesn't compile
 echo int `-` 1 # compiles
+
+var num = 1
+num += int 2
+doAssert num == 3
+
+import options
+var opt = some some none int
+opt = some some none int
+opt = some none Option[int]
diff --git a/tests/parser/tcommandequals.nim b/tests/parser/tcommandequals.nim
new file mode 100644
index 000000000..f41b318ac
--- /dev/null
+++ b/tests/parser/tcommandequals.nim
@@ -0,0 +1,17 @@
+discard """
+  output: '''
+5
+'''
+"""
+
+proc foo(a, b: int) =
+  echo a + b
+
+foo a = 2, b = 3
+
+import macros
+
+macro bar(args: varargs[untyped]): untyped =
+  doAssert args[0].kind == nnkExprEqExpr
+
+bar "a" = 1
diff --git a/tests/parser/tcommandindent.nim b/tests/parser/tcommandindent.nim
new file mode 100644
index 000000000..449c218db
--- /dev/null
+++ b/tests/parser/tcommandindent.nim
@@ -0,0 +1,16 @@
+when false: # parse the following
+  let foo = Obj(
+    field1: proc (src: pointer, srcLen: Natural)
+                    {.nimcall, gcsafe, raises: [IOError, Defect].} =
+      var file = FileOutputStream(s).file
+
+      implementWrites s.buffers, src, srcLen, "FILE",
+                      writeStartAddr, writeLen,
+        file.writeBuffer(writeStartAddr, writeLen)
+    ,
+    field2: proc {.nimcall, gcsafe, raises: [IOError, Defect].} =
+      flushFile FileOutputStream(s).file
+    ,
+    field3: proc () {.nimcall, gcsafe, raises: [IOError, Defect].} =
+      close FileOutputStream(s).file
+  )
diff --git a/tests/parser/tdo.nim b/tests/parser/tdo.nim
index 7bd1f7411..382d03398 100644
--- a/tests/parser/tdo.nim
+++ b/tests/parser/tdo.nim
@@ -1,8 +1,12 @@
 discard """
-  output: '''true
+  output: '''
 true
 true
-true inner B'''
+true
+true inner B
+running with pragma
+ran with pragma
+'''
 """
 
 template withValue(a, b, c, d, e: untyped) =
@@ -77,3 +81,14 @@ proc main2 =
       echo "true inner B"
 
 main2()
+
+proc withPragma(foo: int, bar: proc() {.raises: [].}) =
+  echo "running with pragma"
+  bar()
+
+withPragma(3) do {.raises: [].}:
+  echo "ran with pragma"
+
+doAssert not (compiles do:
+  withPragma(3) do {.raises: [].}:
+    raise newException(Exception))
diff --git a/tests/parser/tdoc_comments.nim b/tests/parser/tdoc_comments.nim
index fa1374b45..3c86e69ea 100644
--- a/tests/parser/tdoc_comments.nim
+++ b/tests/parser/tdoc_comments.nim
@@ -69,3 +69,7 @@ type
 
   MyEnum3* = enum
     value5  ## only document the enum value
+
+# bug #18847
+proc close*() =   ## asdfasdfsdfa
+  discard         ## adsfasdfads
diff --git a/tests/parser/tdomulttest.nim b/tests/parser/tdomulttest.nim
index 418192ac8..9e1afd034 100644
--- a/tests/parser/tdomulttest.nim
+++ b/tests/parser/tdomulttest.nim
@@ -1,14 +1,14 @@
 discard """
-  file: "tdomulttest.nim"
   output: "555\ntest\nmulti lines\n99999999\nend"
-  disabled: true
 """
+
 proc foo(bar, baz: proc (x: int): int) =
   echo bar(555)
   echo baz(99999999)
 
 foo do (x: int) -> int:
   return x
+
 do (x: int) -> int:
   echo("test")
   echo("multi lines")
diff --git a/tests/parser/tdotlikeoperators.nim b/tests/parser/tdotlikeoperators.nim
new file mode 100644
index 000000000..c2d23bd15
--- /dev/null
+++ b/tests/parser/tdotlikeoperators.nim
@@ -0,0 +1,30 @@
+discard """
+  matrix: "-d:nimPreviewDotLikeOps"
+"""
+# test for https://github.com/nim-lang/RFCs/issues/341
+import std/json
+import std/jsonutils
+import std/macros
+
+macro fn1(a: untyped): string = newLit a.lispRepr
+
+doAssert fn1(a.?b.c) == """(DotExpr (Infix (Ident ".?") (Ident "a") (Ident "b")) (Ident "c"))"""
+
+template `.?`(a: JsonNode, b: untyped{ident}): JsonNode =
+  a[astToStr(b)]
+
+proc identity[T](a: T): T = a
+proc timesTwo[T](a: T): T = a * 2
+
+template main =
+  let a = (a1: 1, a2: "abc", a3: (a4: 2.5))
+  let j = a.toJson
+  doAssert j.?a1.getInt == 1
+  doAssert j.?a3.?a4.getFloat == 2.5
+  doAssert j.?a3.?a4.getFloat.timesTwo == 5.0
+  doAssert j.?a3.identity.?a4.getFloat.timesTwo == 5.0
+  doAssert j.identity.?a3.identity.?a4.identity.getFloat.timesTwo == 5.0
+  doAssert j.identity.?a3.?a4.identity.getFloat.timesTwo == 5.0
+
+static: main()
+main()
diff --git a/tests/parser/tdoublenotnil.nim b/tests/parser/tdoublenotnil.nim
new file mode 100644
index 000000000..c61008c54
--- /dev/null
+++ b/tests/parser/tdoublenotnil.nim
@@ -0,0 +1,3 @@
+when false:
+  type Foo = Bar not nil not nil #[tt.Error
+                         ^ invalid indentation]#
diff --git a/tests/parser/tifexprs.nim b/tests/parser/tifexprs.nim
new file mode 100644
index 000000000..3a7e2bddc
--- /dev/null
+++ b/tests/parser/tifexprs.nim
@@ -0,0 +1,12 @@
+discard """
+  output: '''
+1
+'''
+"""
+
+var a, b: int
+let x = if a > b:
+    0
+    else: 1
+
+echo x
diff --git a/tests/parser/tifextracolon.nim b/tests/parser/tifextracolon.nim
new file mode 100644
index 000000000..171569111
--- /dev/null
+++ b/tests/parser/tifextracolon.nim
@@ -0,0 +1,8 @@
+# issue #21982
+
+if true:
+  if true:
+    discard default int:
+else: #[tt.Error
+^ invalid indentation]#
+  discard
diff --git a/tests/parser/tinvcolonlocation1.nim b/tests/parser/tinvcolonlocation1.nim
index 2fddab2f8..7fca5deb7 100644
--- a/tests/parser/tinvcolonlocation1.nim
+++ b/tests/parser/tinvcolonlocation1.nim
@@ -1,8 +1,8 @@
 discard """
+  errormsg: "expected: ':', but got: 'echo'"
   file: "tinvcolonlocation1.nim"
   line: 8
   column: 7
-  errormsg: "expected: ':', but got: 'echo'"
 """
 try #<- missing ':'
   echo "try"
diff --git a/tests/parser/tinvcolonlocation2.nim b/tests/parser/tinvcolonlocation2.nim
index 4251598b9..e3de393b8 100644
--- a/tests/parser/tinvcolonlocation2.nim
+++ b/tests/parser/tinvcolonlocation2.nim
@@ -1,8 +1,8 @@
 discard """
+  errormsg: "expected: ':', but got: 'keyword finally'"
   file: "tinvcolonlocation2.nim"
   line: 11
   column: 8
-  errormsg: "expected: ':', but got: 'keyword finally'"
 """
 try:
   echo "try"
diff --git a/tests/parser/tinvcolonlocation3.nim b/tests/parser/tinvcolonlocation3.nim
index a8db658eb..46252f24e 100644
--- a/tests/parser/tinvcolonlocation3.nim
+++ b/tests/parser/tinvcolonlocation3.nim
@@ -1,8 +1,8 @@
 discard """
+  errormsg: "expected: ':', but got: 'echo'"
   file: "tinvcolonlocation3.nim"
   line: 12
   column: 7
-  errormsg: "expected: ':', but got: 'echo'"
 """
 try:
   echo "try"
diff --git a/tests/parser/tinvifstmt.nim b/tests/parser/tinvifstmt.nim
new file mode 100644
index 000000000..0455bce60
--- /dev/null
+++ b/tests/parser/tinvifstmt.nim
@@ -0,0 +1,12 @@
+discard """
+  errormsg: "invalid indentation"
+  line: 9
+  column: 3
+"""
+
+if true:
+    echo "a"
+  elif:
+    echo "b"
+ else:
+   echo "c"
diff --git a/tests/parser/tinvwhen.nim b/tests/parser/tinvwhen.nim
index 99701bdf5..7a47f69a4 100644
--- a/tests/parser/tinvwhen.nim
+++ b/tests/parser/tinvwhen.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "invalid indentation"
   file: "tinvwhen.nim"
   line: 11
-  errormsg: "invalid indentation"
 """
 # This was parsed even though it should not!
 
@@ -11,5 +11,3 @@ proc getcwd(buf: cstring, buflen: cint): cstring
     when defined(unix): {.importc: "getcwd", header: "<unistd.h>".} #ERROR_MSG invalid indentation
     elif defined(windows): {.importc: "getcwd", header: "<direct.h>"}
     else: {.error: "os library not ported to your OS. Please help!".}
-
-
diff --git a/tests/parser/tletcolon.nim b/tests/parser/tletcolon.nim
index 7eaa5e3e5..a2dde148a 100644
--- a/tests/parser/tletcolon.nim
+++ b/tests/parser/tletcolon.nim
@@ -58,4 +58,18 @@ block:
     var y = 2
     echo "block expression works"
     y*y
-  doAssert x == 4
\ No newline at end of file
+  doAssert x == 4
+
+
+# bug 10861
+macro foo(a: untyped): untyped = 
+  a             
+
+let c1 = foo:
+  1 + 1
+
+const c2 = foo:
+  1 + 1
+
+const c3 = 
+  foo: 1 + 1
diff --git a/tests/parser/toprprec.nim b/tests/parser/toprprec.nim
index 1acd381e7..e6a43d42e 100644
--- a/tests/parser/toprprec.nim
+++ b/tests/parser/toprprec.nim
@@ -1,10 +1,9 @@
 discard """
-  file: "toprprec.nim"
   output: "done"
 """
 # Test operator precedence:
 
-template `@` (x: untyped): untyped {.immediate.} =
+template `@@` (x: untyped): untyped =
   `self`.x
 
 template `@!` (x: untyped): untyped = x
@@ -16,11 +15,11 @@ type
   TA = tuple[a, b: int, obj: TO]
 
 proc init(self: var TA): string =
-  @a = 3
-  === @b = 4
-  @obj.x = 4
+  @@a = 3
+  === @@b = 4
+  @@obj.x = 4
   @! === result = "abc"
-  result = @b.`$`
+  result = @@b.`$`
 
 assert 3+5*5-2 == 28- -26-28
 
diff --git a/tests/parser/tpostexprblocks.nim b/tests/parser/tpostexprblocks.nim
index c27bbf321..6cd4a8350 100644
--- a/tests/parser/tpostexprblocks.nim
+++ b/tests/parser/tpostexprblocks.nim
@@ -406,6 +406,105 @@ StmtList
           DiscardStmt
             Empty
       IntLit 0
+  Command
+    Ident "foo390"
+    Call
+      Ident "x"
+      Do
+        Empty
+        Empty
+        Empty
+        FormalParams
+          Empty
+          IdentDefs
+            Ident "y"
+            Empty
+            Empty
+        Empty
+        Empty
+        StmtList
+          DiscardStmt
+            Empty
+      Do
+        Empty
+        Empty
+        Empty
+        FormalParams
+          Ident "int"
+          IdentDefs
+            Ident "z"
+            Empty
+            Empty
+        Empty
+        Empty
+        StmtList
+          DiscardStmt
+            Empty
+      Do
+        Empty
+        Empty
+        Empty
+        FormalParams
+          Ident "int"
+          IdentDefs
+            Ident "w"
+            Ident "int"
+            Empty
+        Empty
+        Empty
+        StmtList
+          DiscardStmt
+            Empty
+      StmtList
+        DiscardStmt
+          Empty
+      OfBranch
+        Ident "a"
+        StmtList
+          DiscardStmt
+            Empty
+      OfBranch
+        TupleConstr
+          Ident "a"
+          Ident "b"
+        StmtList
+          DiscardStmt
+            Empty
+      ElifBranch
+        Ident "a"
+        StmtList
+          DiscardStmt
+            Empty
+      ElifBranch
+        TupleConstr
+          Ident "a"
+          Ident "b"
+        StmtList
+          DiscardStmt
+            Empty
+      ExceptBranch
+        Ident "a"
+        StmtList
+          DiscardStmt
+            Empty
+      ExceptBranch
+        TupleConstr
+          Ident "a"
+          Ident "b"
+        StmtList
+          DiscardStmt
+            Empty
+      Finally
+        StmtList
+          DiscardStmt
+            Empty
+
+  Call
+    Ident "foo"
+    Finally
+      StmtList
+        DiscardStmt
+          Empty
 '''
 """
 
@@ -540,3 +639,30 @@ dumpTree:
   foo380.add((quote do:
     discard
   )[0])
+
+  foo390 x do (y):
+    discard
+  do (z) -> int:
+    discard
+  do (w: int) -> int:
+    discard
+  do:
+    discard
+  of a:
+    discard
+  of (a, b):
+    discard
+  elif a:
+    discard
+  elif (a, b):
+    discard
+  except a:
+    discard
+  except (a, b):
+    discard
+  finally:
+    discard
+
+  foo:
+  finally:
+    discard
diff --git a/tests/parser/tprecedence.nim b/tests/parser/tprecedence.nim
index d586f14a3..9be79543b 100644
--- a/tests/parser/tprecedence.nim
+++ b/tests/parser/tprecedence.nim
@@ -1,7 +1,8 @@
 discard """
   output: '''holla
 true
-defabc 4'''
+defabc 4
+0'''
 """
 
 # Test top level semicolon works properly:
@@ -19,3 +20,44 @@ proc foo[S, T](x: S, y: T): T = x & y
 proc bar[T](x: T): T = x
 
 echo "def".foo[:string, string]("abc"), " ", 4.bar[:int]
+
+# bug #9574
+proc isFalse(a: int): bool = false
+
+assert not isFalse(3)
+
+# bug #9633
+
+type
+  MyField = object
+    b: seq[string]
+
+  MyObject = object
+    f: MyField
+
+proc getX(x: MyObject): lent MyField {.inline.} =
+  x.f
+
+let a = MyObject()
+echo a.getX.b.len
+
+
+# bug  #10458
+template t(x: untyped): untyped = "x"
+
+let
+  aaa = t 2 + 4
+  ccc = t (1, 1) + 6
+  ddd = t [0, 1, 2] + 5
+
+# bug #10896
+const
+  test =
+    proc(): int = 1
+
+# bug #8759
+block:
+  template `=>`(a, b): untyped = (a, b)
+  template `+=`(a, b): untyped = a * b
+
+  doAssert ("abc" => 3 += 5) == ("abc", 15)
diff --git a/tests/parser/tprocexprasstmt.nim b/tests/parser/tprocexprasstmt.nim
new file mode 100644
index 000000000..22fb4a7c8
--- /dev/null
+++ b/tests/parser/tprocexprasstmt.nim
@@ -0,0 +1,14 @@
+func r(): auto =
+  func(): int = 2
+doAssert r()() == 2
+
+block: # issue #11726
+  let foo = block:
+    var x: int
+    proc = inc x # "identifier expected, but got '='"
+
+  template paint(): untyped =
+    proc (s: string): string = s
+
+  let s = paint()
+  doAssert s("abc") == "abc"
diff --git a/tests/parser/tstatementoperators.nim b/tests/parser/tstatementoperators.nim
new file mode 100644
index 000000000..d2b85bb4b
--- /dev/null
+++ b/tests/parser/tstatementoperators.nim
@@ -0,0 +1,12 @@
+discard """
+  nimout: '''
+Infix
+  Ident "from"
+  Ident "a"
+  Ident "b"
+'''
+"""
+
+from macros import dumpTree
+
+dumpTree(a from b)
\ No newline at end of file
diff --git a/tests/parser/tstmtlists.nim b/tests/parser/tstmtlists.nim
new file mode 100644
index 000000000..158c93340
--- /dev/null
+++ b/tests/parser/tstmtlists.nim
@@ -0,0 +1,180 @@
+discard """
+  output: '''
+2
+2
+2
+2
+2
+2
+2
+2
+2
+2
+hello
+1
+hello
+2
+hello
+3
+hello
+4
+hello
+5
+hello
+6
+hello
+7
+hello
+8
+hello
+9
+hello
+10
+hello
+1
+hello
+2
+hello
+3
+hello
+4
+hello
+5
+hello
+6
+hello
+7
+hello
+8
+hello
+9
+hello
+10
+lucky
+lucky
+'''
+"""
+
+block: (
+  discard;
+  echo 1 + 1;
+  )
+
+block: (
+  discard; #Haha
+    #haha
+  echo 1 + 1;
+)
+
+block: (
+  discard;
+  #Hmm
+  echo 1 +
+    1;
+)
+
+block: (
+  discard
+  echo "2"
+)
+
+block: (
+  discard;
+  echo 1 +
+    1
+)
+
+block: (
+  discard
+  echo 1 +
+    1
+)
+
+block: (
+  discard;
+  discard
+)
+
+block: (
+  discard
+  echo 1 + 1;
+  )
+
+block: (
+  discard
+  echo 1 + 1;
+)
+
+block: (
+  discard
+  echo 1 +
+    1;
+)
+
+block: (
+  discard;
+    )
+
+block: ( discard; echo 1 + #heh
+                         1;
+)
+
+for i in 1..10:
+    echo "hello"
+    echo i
+
+for i in 1..10: (
+    echo "hello";
+    echo i;
+)
+
+proc square(inSeq: seq[float]): seq[float] = (
+  result = newSeq[float](len(inSeq));
+  for i, v in inSeq: (
+    result[i] = v * v;
+  )
+)
+
+proc square2(inSeq: seq[float]): seq[float] =
+  result = newSeq[float](len(inSeq));
+  for i, v in inSeq: (
+    result[i] = v * v;
+  )
+
+proc cstringCheck(tracked: int; n: int) =
+  if true == false and (let a = high(int); let b = high(int);
+      a.int8 == 8 and a.int8 notin {3..9}):
+    echo(tracked, n)
+
+template dim: int =
+  (if int.high == 0:
+    int.high
+  else:
+    int.high)
+
+template dim2: int =
+  (if int.high == 0:
+    int.high
+   else:
+    int.high)
+
+template dim3: int =
+  (
+   if int.high == 0:
+     int.high
+   else:
+     int.high)
+
+# lenient indentation:
+
+echo (if 0 == 1:
+  "0 == 1"
+else:
+  "lucky")
+
+# bug #16426
+echo (when 0 == 1:
+  "0 == 1"
+else:
+  "lucky")
+
diff --git a/tests/parser/tstrongspaces.nim b/tests/parser/tstrongspaces.nim
deleted file mode 100644
index adab7f709..000000000
--- a/tests/parser/tstrongspaces.nim
+++ /dev/null
@@ -1,83 +0,0 @@
-#? strongSpaces
-
-discard """
-  output: '''35
-true
-true
-4
-true
-1
-false
-77
-(Field0: 1, Field1: 2, Field2: 2)
-ha
-true
-tester args
-all
-all args
-19
--3
-false
--2
-'''
-"""
-
-echo 2+5 * 5
-
-# Keyword operators
-echo 1 + 16 shl 1 == 1 + (16 shl 1)
-echo 2 and 1  in  {0, 30}
-echo 2+2 * 2 shr 1
-echo false  or  2 and 1  in  {0, 30}
-
-proc `^`(a, b: int): int = a + b div 2
-echo 19 mod 16 ^ 4  +  2 and 1
-echo 18 mod 16 ^ 4 > 0
-
-# echo $foo gotcha
-let foo = 77
-echo $foo
-
-echo (1, 2, 2)
-
-template `&`(a, b: int): int = a and b
-template `|`(a, b: int): int = a - b
-template `++`(a, b: int): bool = a + b == 8009
-
-when true:
-  let b = 66
-  let c = 90
-  let bar = 8000
-  if foo+4 * 4 == 8  and  b&c | 9  ++
-      bar:
-    echo "ho"
-  else:
-    echo "ha"
-
-  let booA = foo+4 * 4  -  b&c | 9  +
-      bar
-  # is parsed as
-  let booB = ((foo+4)*4) - ((b&c) | 9) + bar
-
-  echo booA == booB
-
-
-template `|`(a, b): untyped = (if a.len > 0: a else: b)
-
-const
-  tester = "tester"
-  args = "args"
-
-echo tester & " " & args|"all"
-echo "all"  |  tester & " " & args
-echo "all"|tester & " " & args
-
-# Test arrow like operators. See also tests/macros/tclosuremacro.nim
-proc `+->`(a, b: int): int = a + b*4
-template `===>`(a, b: int): int = a - b shr 1
-
-echo 3 +-> 2 + 2 and 4
-var arrowed = 3+->2 + 2 and 4  # arrowed = 4
-echo arrowed ===> 15
-echo (2 * 3+->2) == (2*3 +-> 2)
-echo arrowed ===> 2 + 3+->2
diff --git a/tests/parser/ttry.nim b/tests/parser/ttry.nim
new file mode 100644
index 000000000..190b0b8dc
--- /dev/null
+++ b/tests/parser/ttry.nim
@@ -0,0 +1,27 @@
+# bug #21144
+block:
+  try:
+    let c = try:
+        10
+      except ValueError as exc:
+        10
+  except ValueError as exc:
+    discard
+
+if true:
+  block:
+    let c = try:
+          10
+        except ValueError as exc:
+          10
+    except OSError:
+      99
+
+
+try:
+  let c = try:
+    10
+  except ValueError as exc:
+    10
+except ValueError as exc:
+  discard
\ No newline at end of file
diff --git a/tests/parser/ttupleunpack.nim b/tests/parser/ttupleunpack.nim
index aaa06f9f4..993501fbb 100644
--- a/tests/parser/ttupleunpack.nim
+++ b/tests/parser/ttupleunpack.nim
@@ -1,9 +1,3 @@
-discard """
-  file: "ttupleunpack.nim"
-  output: ""
-  exitcode: 0
-"""
-
 proc returnsTuple(): (int, int, int) = (4, 2, 3)
 
 proc main2 =
@@ -33,3 +27,68 @@ proc main() =
 
 main()
 main2()
+
+block: # nested unpacking
+  block: # simple let
+    let (a, (b, c), d) = (1, (2, 3), 4)
+    doAssert (a, b, c, d) == (1, 2, 3, 4)
+    let foo = (a, (b, c), d)
+    let (a2, (b2, c2), d2) = foo
+    doAssert (a, b, c, d) == (a2, b2, c2, d2)
+
+  block: # var and assignment
+    var (x, (y, z), t) = ('a', (true, @[123]), "abc")
+    doAssert (x, y, z, t) == ('a', true, @[123], "abc")
+    (x, (y, z), t) = ('b', (false, @[456]), "def")
+    doAssert (x, y, z, t) == ('b', false, @[456], "def")
+
+  block: # very nested
+    let (_, (_, (_, (_, (_, a))))) = (1, (2, (3, (4, (5, 6)))))
+    doAssert a == 6
+
+  block: # const
+    const (a, (b, c), d) = (1, (2, 3), 4)
+    doAssert (a, b, c, d) == (1, 2, 3, 4)
+    const foo = (a, (b, c), d)
+    const (a2, (b2, c2), d2) = foo
+    doAssert (a, b, c, d) == (a2, b2, c2, d2)
+  
+  block: # evaluation semantics preserved between literal and not literal
+    var s: seq[string]
+    block: # literal
+      let (a, (b, c), d) = ((s.add("a"); 1), ((s.add("b"); 2), (s.add("c"); 3)), (s.add("d"); 4))
+      doAssert (a, b, c, d) == (1, 2, 3, 4)
+      doAssert s == @["a", "b", "c", "d"]
+    block: # underscore
+      s = @[]
+      let (a, (_, c), _) = ((s.add("a"); 1), ((s.add("b"); 2), (s.add("c"); 3)), (s.add("d"); 4))
+      doAssert (a, c) == (1, 3)
+      doAssert s == @["a", "b", "c", "d"]
+    block: # temp
+      s = @[]
+      let foo = ((s.add("a"); 1), ((s.add("b"); 2), (s.add("c"); 3)), (s.add("d"); 4))
+      let (a, (b, c), d) = foo
+      doAssert (a, b, c, d) == (1, 2, 3, 4)
+      doAssert s == @["a", "b", "c", "d"]
+
+block: # unary assignment unpacking
+  var a: int
+  (a,) = (1,)
+  doAssert a == 1
+
+block: # type annotations
+  block: # basic
+    let (a, b): (int, int) = (1, 2)
+    doAssert (a, b) == (1, 2)
+  block: # type inference
+    let (a, b): (byte, float) = (1, 2)
+    doAssert (a, b) == (1.byte, 2.0)
+  block: # type mismatch
+    doAssert not (compiles do:
+      let (a, b): (int, string) = (1, 2))
+  block: # nested
+    let (a, (b, c)): (int, (int, int)) = (1, (2, 3))
+    doAssert (a, b, c) == (1, 2, 3)
+  block: # nested type inference
+    let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc"))
+    doAssert (a, b, c) == (1.byte, 2.0, cstring"abc")
diff --git a/tests/parser/ttypeclasses.nim b/tests/parser/ttypeclasses.nim
index 9f487c7a8..e6e7a48b8 100644
--- a/tests/parser/ttypeclasses.nim
+++ b/tests/parser/ttypeclasses.nim
@@ -1,17 +1,46 @@
-discard """
-    action: run
-"""
-
 type
     R = ref
     V = var
     D = distinct
     P = ptr
+    T = type
+    S = static
+    OBJ = object
+    TPL = tuple
+    SEQ = seq
 
+var i: int
 var x: ref int
 var y: distinct int
 var z: ptr int
+const C = @[1, 2, 3]
+
+static:
+  doAssert x is ref
+  doAssert y is distinct
+  doAssert z is ptr
+  doAssert C is static
+  doAssert C[1] is static[int]
+  doAssert C[0] is static[SomeInteger]
+  doAssert C isnot static[string]
+  doAssert C is SEQ|OBJ
+  doAssert C isnot OBJ|TPL
+  doAssert int is int
+  doAssert int is T
+  doAssert int is SomeInteger
+  doAssert seq[int] is type
+  doAssert seq[int] is type[seq]
+  doAssert seq[int] isnot type[seq[float]]
+  doAssert i isnot type[int]
+  doAssert type(i) is type[int]
+  doAssert x isnot T
+  doAssert y isnot S
+  doAssert z isnot enum
+  doAssert x isnot object
+  doAssert y isnot tuple
+  doAssert z isnot seq
+
+  # XXX: These cases don't work properly at the moment:
+  # doAssert type[int] isnot int
+  # doAssert type(int) isnot int
 
-doAssert x is ref
-doAssert y is distinct
-doAssert z is ptr
\ No newline at end of file
diff --git a/tests/parser/ttypecommandcomma.nim b/tests/parser/ttypecommandcomma.nim
new file mode 100644
index 000000000..7ca59a799
--- /dev/null
+++ b/tests/parser/ttypecommandcomma.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "invalid indentation"
+  line: 8
+  column: 19
+"""
+
+type
+  Foo = call(1, 2), 3:
+    4
\ No newline at end of file
diff --git a/tests/parser/ttypecommandindent1.nim b/tests/parser/ttypecommandindent1.nim
new file mode 100644
index 000000000..9aa274e2a
--- /dev/null
+++ b/tests/parser/ttypecommandindent1.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "invalid indentation"
+  line: 9
+  column: 3
+"""
+
+type
+  Foo = call x, y, z:
+  abc
diff --git a/tests/parser/ttypecommandindent2.nim b/tests/parser/ttypecommandindent2.nim
new file mode 100644
index 000000000..0883df90f
--- /dev/null
+++ b/tests/parser/ttypecommandindent2.nim
@@ -0,0 +1,11 @@
+discard """
+  errormsg: "invalid indentation"
+  line: 10
+  column: 5
+"""
+
+type
+  Foo = call x, y, z:
+      abc
+    do:
+        def
diff --git a/tests/parser/ttypecommandindent3.nim b/tests/parser/ttypecommandindent3.nim
new file mode 100644
index 000000000..f9fdcc1e4
--- /dev/null
+++ b/tests/parser/ttypecommandindent3.nim
@@ -0,0 +1,11 @@
+discard """
+  errormsg: "expression expected, but found 'keyword do'"
+  line: 10
+  column: 1
+"""
+
+type
+  Foo = call x, y, z:
+    abc
+do:
+    def
diff --git a/tests/parser/ttypeexprobject.nim b/tests/parser/ttypeexprobject.nim
new file mode 100644
index 000000000..6895f1731
--- /dev/null
+++ b/tests/parser/ttypeexprobject.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "invalid indentation"
+  line: 10
+  column: 14
+"""
+
+type
+  A = (object | tuple | int)
+  B = int | object | tuple
+  C = object | tuple | int # issue #8846
diff --git a/tests/parser/ttypeexprs.nim b/tests/parser/ttypeexprs.nim
new file mode 100644
index 000000000..e40efc7d9
--- /dev/null
+++ b/tests/parser/ttypeexprs.nim
@@ -0,0 +1,25 @@
+proc foo[T: ptr int | ptr string](x: T) = discard
+var x = "abc"
+foo(addr x)
+
+let n = 3'u32
+type Double = (
+  when n.sizeof == 4: uint64
+  elif n.sizeof == 2: uint32
+  else: uint16
+)
+
+type
+  A = (ref | ptr | pointer)
+  B = pointer | ptr | ref
+  C = ref | ptr | pointer
+
+template `+`(a, b): untyped = (b, a)
+template `*`(a, b): untyped = (a, b)
+
+doAssert (ref int + ref float * ref string + ref bool) is
+  (ref bool, ((ref float, ref string), ref int))
+type X = ref int + ref float * ref string + ref bool
+doAssert X is (ref bool, ((ref float, ref string), ref int))
+
+type SomePointer = proc | ref | ptr | pointer
diff --git a/tests/parser/ttypemodifiers.nim b/tests/parser/ttypemodifiers.nim
new file mode 100644
index 000000000..9a1ccb1a5
--- /dev/null
+++ b/tests/parser/ttypemodifiers.nim
@@ -0,0 +1,526 @@
+discard """
+nimout: '''
+StmtList
+  TypeSection
+    TypeDef
+      Ident "BarePtr"
+      Empty
+      PtrTy
+    TypeDef
+      Ident "GenericPtr"
+      Empty
+      PtrTy
+        Bracket
+          Ident "int"
+    TypeDef
+      Ident "PrefixPtr"
+      Empty
+      PtrTy
+        Ident "int"
+    TypeDef
+      Ident "PtrTuple"
+      Empty
+      PtrTy
+        TupleConstr
+          Ident "int"
+          Ident "string"
+    TypeDef
+      Ident "BareRef"
+      Empty
+      RefTy
+    TypeDef
+      Ident "GenericRef"
+      Empty
+      RefTy
+        Bracket
+          Ident "int"
+    TypeDef
+      Ident "RefTupleCl"
+      Empty
+      RefTy
+        TupleTy
+    TypeDef
+      Ident "RefTupleType"
+      Empty
+      RefTy
+        TupleConstr
+          Ident "int"
+          Ident "string"
+    TypeDef
+      Ident "RefTupleVars"
+      Empty
+      RefTy
+        TupleConstr
+          Ident "a"
+          Ident "b"
+    TypeDef
+      Ident "BareStatic"
+      Empty
+      Ident "static"
+    TypeDef
+      Ident "GenericStatic"
+      Empty
+      BracketExpr
+        Ident "static"
+        Ident "int"
+    TypeDef
+      Ident "PrefixStatic"
+      Empty
+      Command
+        Ident "static"
+        Ident "int"
+    TypeDef
+      Ident "StaticTupleCl"
+      Empty
+      Command
+        Ident "static"
+        TupleClassTy
+    TypeDef
+      Ident "StaticTuple"
+      Empty
+      Command
+        Ident "static"
+        TupleConstr
+          Ident "int"
+          Ident "string"
+    TypeDef
+      Ident "BareType"
+      Empty
+      Ident "type"
+    TypeDef
+      Ident "GenericType"
+      Empty
+      BracketExpr
+        Ident "type"
+        Ident "float"
+    TypeDef
+      Ident "TypeTupleGen"
+      Empty
+      BracketExpr
+        Ident "type"
+        TupleClassTy
+    TypeDef
+      Ident "TypeTupleCl"
+      Empty
+      Command
+        Ident "type"
+        TupleClassTy
+    TypeDef
+      Ident "TypeInstance"
+      Empty
+      Command
+        Ident "type"
+        BracketExpr
+          Ident "Foo"
+          RefTy
+    TypeDef
+      Ident "bareTypeDesc"
+      Empty
+      Ident "typedesc"
+    TypeDef
+      Ident "TypeOfVar"
+      Empty
+      Call
+        Ident "type"
+        Ident "a"
+    TypeDef
+      Ident "TypeOfVarAlt"
+      Empty
+      Command
+        Ident "type"
+        Par
+          Ident "a"
+    TypeDef
+      Ident "TypeOfTuple1"
+      Empty
+      Call
+        Ident "type"
+        Ident "a"
+    TypeDef
+      Ident "TypeOfTuple2"
+      Empty
+      Call
+        Ident "type"
+        Ident "a"
+        Ident "b"
+    TypeDef
+      Ident "TypeOfTuple1A"
+      Empty
+      Command
+        Ident "type"
+        TupleConstr
+          Ident "a"
+    TypeDef
+      Ident "TypeOfTuple2A"
+      Empty
+      Command
+        Ident "type"
+        TupleConstr
+          Ident "a"
+          Ident "b"
+    TypeDef
+      Ident "TypeTuple"
+      Empty
+      Command
+        Ident "type"
+        TupleConstr
+          Ident "int"
+          Ident "string"
+    TypeDef
+      Ident "GenericTypedesc"
+      Empty
+      BracketExpr
+        Ident "typedesc"
+        Ident "int"
+    TypeDef
+      Ident "T"
+      Empty
+      Ident "type"
+  ProcDef
+    Ident "foo"
+    Empty
+    Empty
+    FormalParams
+      Ident "type"
+      IdentDefs
+        Ident "bareType"
+        Ident "type"
+        Empty
+      IdentDefs
+        Ident "genType"
+        BracketExpr
+          Ident "type"
+          Ident "int"
+        Empty
+      IdentDefs
+        Ident "typeInt"
+        Command
+          Ident "type"
+          Ident "int"
+        Empty
+      IdentDefs
+        Ident "typeIntAlt"
+        Call
+          Ident "type"
+          Ident "int"
+        Empty
+      IdentDefs
+        Ident "typeOfVar"
+        Call
+          Ident "type"
+          Ident "a"
+        Empty
+      IdentDefs
+        Ident "typeDotType"
+        DotExpr
+          Ident "foo"
+          Ident "type"
+        Empty
+      IdentDefs
+        Ident "typeTupleCl"
+        Command
+          Ident "type"
+          TupleClassTy
+        Empty
+      IdentDefs
+        Ident "bareStatic"
+        Ident "static"
+        Empty
+      IdentDefs
+        Ident "genStatic"
+        BracketExpr
+          Ident "static"
+          Ident "int"
+        Empty
+      IdentDefs
+        Ident "staticInt"
+        Command
+          Ident "static"
+          Ident "int"
+        Empty
+      IdentDefs
+        Ident "staticVal1"
+        Command
+          Ident "static"
+          IntLit 10
+        Empty
+      IdentDefs
+        Ident "staticVal2"
+        Call
+          Ident "static"
+          StrLit "str"
+        Empty
+      IdentDefs
+        Ident "staticVal3"
+        Command
+          Ident "static"
+          StrLit "str"
+        Empty
+      IdentDefs
+        Ident "staticVal4"
+        CallStrLit
+          Ident "static"
+          RStrLit "str"
+        Empty
+      IdentDefs
+        Ident "staticDotVal"
+        DotExpr
+          IntLit 10
+          Ident "static"
+        Empty
+      IdentDefs
+        Ident "bareRef"
+        RefTy
+        Empty
+      IdentDefs
+        Ident "refTuple1"
+        RefTy
+          Par
+            Ident "int"
+        Empty
+      IdentDefs
+        Ident "refTuple1A"
+        RefTy
+          TupleConstr
+            Ident "int"
+        Empty
+      IdentDefs
+        Ident "refTuple2"
+        RefTy
+          TupleConstr
+            Ident "int"
+            Ident "string"
+        Empty
+      IdentDefs
+        Ident "genRef"
+        RefTy
+          Bracket
+            Ident "int"
+        Empty
+      IdentDefs
+        Ident "refInt"
+        RefTy
+          Ident "int"
+        Empty
+      IdentDefs
+        Ident "refCall"
+        RefTy
+          Par
+            Ident "a"
+        Empty
+      IdentDefs
+        Ident "macroCall1"
+        Command
+          Ident "foo"
+          Ident "bar"
+        Empty
+      IdentDefs
+        Ident "macroCall2"
+        Call
+          Ident "foo"
+          Ident "bar"
+        Empty
+      IdentDefs
+        Ident "macroCall3"
+        Call
+          DotExpr
+            Ident "foo"
+            Ident "bar"
+          Ident "baz"
+        Empty
+      IdentDefs
+        Ident "macroCall4"
+        Call
+          BracketExpr
+            Ident "foo"
+            Ident "bar"
+          Ident "baz"
+        Empty
+      IdentDefs
+        Ident "macroCall5"
+        Command
+          Ident "foo"
+          Command
+            Ident "bar"
+            Ident "baz"
+        IntLit 10
+    Empty
+    Empty
+    StmtList
+      Asgn
+        Ident "staticTen"
+        Command
+          Ident "static"
+          IntLit 10
+      Asgn
+        Ident "staticA"
+        Call
+          Ident "static"
+          Ident "a"
+      Asgn
+        Ident "staticCall"
+        Command
+          Ident "static"
+          Call
+            Ident "foo"
+            IntLit 1
+      Asgn
+        Ident "staticStrCall"
+        Command
+          Ident "static"
+          CallStrLit
+            Ident "foo"
+            RStrLit "x"
+      Asgn
+        Ident "staticChainCall"
+        Command
+          Ident "static"
+          Command
+            Ident "foo"
+            Ident "bar"
+      Asgn
+        Ident "typeTen"
+        Command
+          Ident "type"
+          IntLit 10
+      Asgn
+        Ident "typeA"
+        Call
+          Ident "type"
+          Ident "a"
+      Asgn
+        Ident "typeCall"
+        Command
+          Ident "type"
+          Call
+            Ident "foo"
+            IntLit 1
+      Asgn
+        Ident "typeStrCall"
+        Command
+          Ident "type"
+          CallStrLit
+            Ident "foo"
+            RStrLit "x"
+      Asgn
+        Ident "typeChainCall"
+        Command
+          Ident "type"
+          Command
+            Ident "foo"
+            Ident "bar"
+      Asgn
+        Ident "normalChainCall"
+        Command
+          Ident "foo"
+          Command
+            Ident "bar"
+            Ident "baz"
+      Asgn
+        Ident "normalTupleCall2"
+        Call
+          Ident "foo"
+          Ident "a"
+          Ident "b"
+      StaticStmt
+        StmtList
+          Ident "singleStaticStmt"
+      StaticStmt
+        StmtList
+          Ident "staticStmtList1"
+          Ident "staticStmtList2"
+'''
+"""
+
+import macros
+
+dumpTree:
+  type
+    BarePtr       = ptr
+    GenericPtr    = ptr[int]
+    PrefixPtr     = ptr int
+    PtrTuple      = ptr (int, string)
+    BareRef       = ref
+    GenericRef    = ref[int]
+    RefTupleCl    = ref tuple
+    RefTupleType  = ref (int, string)
+    RefTupleVars  = ref (a, b)
+    BareStatic    = static                # Used to be Error: invalid indentation
+    GenericStatic = static[int]
+    PrefixStatic  = static int
+    StaticTupleCl = static tuple
+    StaticTuple   = static (int, string)
+    BareType      = type
+    GenericType   = type[float]
+    TypeTupleGen  = type[tuple]
+    TypeTupleCl   = type tuple            # Used to be Error: invalid indentation
+    TypeInstance  = type Foo[ref]
+    bareTypeDesc  = typedesc
+    TypeOfVar     = type(a)
+    TypeOfVarAlt  = type (a)              # Used to be Error: invalid indentation
+    TypeOfTuple1  = type(a,)
+    TypeOfTuple2  = type(a,b)
+    TypeOfTuple1A = type (a,)             # Used to be Error: invalid indentation
+    TypeOfTuple2A = type (a,b)            # Used to be Error: invalid indentation
+    TypeTuple     = type (int, string)    # Used to be Error: invalid indentation
+    GenericTypedesc = typedesc[int]
+    T = type
+
+  proc foo(
+    bareType        : type,
+    genType         : type[int],
+    typeInt         : type int,
+    typeIntAlt      : type(int),
+    typeOfVar       : type(a),
+    typeDotType     : foo.type,
+    typeTupleCl     : type tuple,         # Used to be Error: ')' expected
+    bareStatic      : static,             # Used to be Error: expression expected, but found ','
+    genStatic       : static[int],
+    staticInt       : static int,
+    staticVal1      : static 10,
+    staticVal2      : static("str"),
+    staticVal3      : static "str",
+    staticVal4      : static"str",        # Used to be Error: expression expected, but found 'str'
+    staticDotVal    : 10.static,
+    bareRef         : ref,
+    refTuple1       : ref (int),
+    refTuple1A      : ref (int,),
+    refTuple2       : ref (int,string),
+    genRef          : ref[int],
+    refInt          : ref int,
+    refCall         : ref(a),
+    macroCall1      : foo bar,
+    macroCall2      : foo(bar),
+    macroCall3      : foo.bar(baz),
+    macroCall4      : foo[bar](baz),
+    macroCall5      : foo bar baz = 10
+  ): type =
+    staticTen       = static 10
+    staticA         = static(a)
+    # staticAspace    = static (a)          # With newTypedesc: Error: invalid indentation
+    # staticAtuple    = static (a,)         # With newTypedesc: Error: invalid indentation
+    # staticTuple     = static (a,b)        # With newTypedesc: Error: invalid indentation
+    # staticTypeTuple = static (int,string) # With newTypedesc: Error: invalid indentation
+    staticCall      = static foo(1)
+    staticStrCall   = static foo"x"
+    staticChainCall = static foo bar
+
+    typeTen         = type 10
+    typeA           = type(a)
+    # typeAspace    = type (a)            # Error: invalid indentation
+    # typeAtuple    = type (a,)           # Error: invalid indentation
+    # typeTuple     = type (a,b)          # Error: invalid indentation
+    # typeTypeTuple = type (int,string)   # Error: invalid indentation
+    typeCall        = type foo(1)
+    typeStrCall     = type foo"x"
+    typeChainCall   = type foo bar
+
+    normalChainCall = foo bar baz
+    # normalTupleCall1 = foo(a,)          # Error: invalid indentation
+    normalTupleCall2 = foo(a,b)
+    # normalTupleCall3 = foo (a,b)        # Error: invalid indentation
+
+    static: singleStaticStmt
+    static:
+      staticStmtList1
+      staticStmtList2
diff --git a/tests/parser/ttypesectioncalls.nim b/tests/parser/ttypesectioncalls.nim
new file mode 100644
index 000000000..003444fc5
--- /dev/null
+++ b/tests/parser/ttypesectioncalls.nim
@@ -0,0 +1,328 @@
+discard """
+nimout: '''
+StmtList
+  TypeSection
+    TypeDef
+      Ident "A"
+      Empty
+      Call
+        Ident "call"
+        IntLit 1
+  TypeSection
+    TypeDef
+      Ident "B"
+      Empty
+      Command
+        Ident "call"
+        IntLit 2
+    TypeDef
+      Ident "C"
+      Empty
+      Call
+        Ident "call"
+        StmtList
+          IntLit 3
+    TypeDef
+      Ident "D"
+      Empty
+      Call
+        Ident "call"
+        StmtList
+          IntLit 4
+  TypeSection
+    TypeDef
+      Ident "E"
+      Empty
+      Call
+        Ident "call"
+        IntLit 5
+        IntLit 6
+    TypeDef
+      Ident "F"
+      Empty
+      Command
+        Ident "call"
+        IntLit 7
+        IntLit 8
+    TypeDef
+      Ident "G"
+      Empty
+      Call
+        Ident "call"
+        IntLit 9
+        StmtList
+          IntLit 10
+    TypeDef
+      Ident "H"
+      Empty
+      Call
+        Ident "call"
+        IntLit 11
+        StmtList
+          IntLit 12
+    TypeDef
+      Ident "I"
+      Empty
+      Command
+        Ident "call"
+        IntLit 13
+        StmtList
+          IntLit 14
+    TypeDef
+      Ident "J"
+      Empty
+      Command
+        Ident "call"
+        IntLit 15
+        StmtList
+          IntLit 16
+  TypeSection
+    TypeDef
+      Ident "K"
+      Empty
+      Call
+        Ident "call"
+        IntLit 17
+        IntLit 18
+        IntLit 19
+    TypeDef
+      Ident "L"
+      Empty
+      Command
+        Ident "call"
+        IntLit 20
+        IntLit 21
+        IntLit 22
+    TypeDef
+      Ident "M"
+      Empty
+      Call
+        Ident "call"
+        IntLit 23
+        IntLit 24
+        StmtList
+          IntLit 25
+    TypeDef
+      Ident "N"
+      Empty
+      Command
+        Ident "call"
+        IntLit 26
+        IntLit 27
+        StmtList
+          IntLit 28
+    TypeDef
+      Ident "O"
+      Empty
+      Command
+        Ident "call"
+        IntLit 29
+        IntLit 30
+        StmtList
+          IntLit 31
+  TypeSection
+    TypeDef
+      Ident "P"
+      Empty
+      Command
+        Ident "call"
+        TupleConstr
+          IntLit 32
+          IntLit 33
+        Infix
+          Ident "+"
+          Infix
+            Ident "*"
+            IntLit 34
+            IntLit 35
+          IntLit 36
+        StmtList
+          IntLit 37
+    TypeDef
+      Ident "R"
+      Empty
+      Command
+        Ident "call"
+        Infix
+          Ident "@"
+          TupleConstr
+            IntLit 38
+            IntLit 39
+          Infix
+            Ident "shl"
+            IntLit 40
+            IntLit 41
+        Infix
+          Ident "-"
+          Infix
+            Ident "*"
+            IntLit 42
+            IntLit 43
+          IntLit 44
+        StmtList
+          IntLit 45
+    TypeDef
+      Ident "S"
+      Empty
+      Command
+        Ident "call"
+        IntLit 46
+        StmtList
+          IntLit 47
+        StmtList
+          IntLit 48
+    TypeDef
+      Ident "T"
+      Empty
+      Call
+        Ident "call"
+        StmtList
+          IntLit 49
+        StmtList
+          IntLit 50
+        StmtList
+          IntLit 51
+a: IntLit 1
+a: IntLit 2
+a: StmtList
+  IntLit 3
+a: StmtList
+  IntLit 4
+a: IntLit 5
+b: IntLit 6
+a: IntLit 7
+b: IntLit 8
+a: IntLit 9
+b: StmtList
+  IntLit 10
+a: IntLit 11
+b: StmtList
+  IntLit 12
+a: IntLit 13
+b: StmtList
+  IntLit 14
+a: IntLit 15
+b: StmtList
+  IntLit 16
+a: IntLit 17
+b: IntLit 18
+c: IntLit 19
+a: IntLit 20
+b: IntLit 21
+c: IntLit 22
+a: IntLit 23
+b: IntLit 24
+c: StmtList
+  IntLit 25
+a: IntLit 26
+b: IntLit 27
+c: StmtList
+  IntLit 28
+a: IntLit 29
+b: IntLit 30
+c: StmtList
+  IntLit 31
+a: TupleConstr
+  IntLit 32
+  IntLit 33
+b: Infix
+  Ident "+"
+  Infix
+    Ident "*"
+    IntLit 34
+    IntLit 35
+  IntLit 36
+c: StmtList
+  IntLit 37
+a: Infix
+  Ident "@"
+  TupleConstr
+    IntLit 38
+    IntLit 39
+  Infix
+    Ident "shl"
+    IntLit 40
+    IntLit 41
+b: Infix
+  Ident "-"
+  Infix
+    Ident "*"
+    IntLit 42
+    IntLit 43
+  IntLit 44
+c: StmtList
+  IntLit 45
+a: IntLit 46
+b: StmtList
+  IntLit 47
+c: StmtList
+  IntLit 48
+a: StmtList
+  IntLit 49
+b: StmtList
+  IntLit 50
+c: StmtList
+  IntLit 51
+'''
+"""
+import macros
+
+macro call(a): untyped =
+  echo "a: ", a.treeRepr
+  result = ident"int"
+macro call(a, b): untyped =
+  echo "a: ", a.treeRepr
+  echo "b: ", b.treeRepr
+  result = ident"int"
+macro call(a, b, c): untyped =
+  echo "a: ", a.treeRepr
+  echo "b: ", b.treeRepr
+  echo "c: ", c.treeRepr
+  result = ident"int"
+
+macro sections(x): untyped =
+  echo x.treeRepr
+  result = newStmtList(x)
+  for ts in x:
+    for td in ts:
+      let t = td[0]
+      result.add quote do:
+        doAssert `t` is int
+
+sections:
+  type A = call(1)
+  type
+    B = call 2
+    C = call: 3
+    D = call(): 4
+  type
+    E = call(5, 6)
+    F = call 7, 8
+    G = call(9): 10
+    H = call(11):
+      12
+    I = call 13: 14
+    J = call 15:
+      16
+  type
+    K = call(17, 18, 19)
+    L = call 20, 21, 22
+    M = call(23, 24): 25
+    N = call 26, 27: 28
+    O = call 29, 30:
+      31
+  type
+    P = call (32, 33), 34 * 35 + 36:
+      37
+    R = call (38, 39) @ 40 shl 41, 42 * 43 - 44:
+      45
+    S = call 46:
+      47
+    do:
+      48
+    T = call:
+      49
+    do:
+      50
+    do:
+      51
diff --git a/tests/pragmas/cfunction.c b/tests/pragmas/cfunction.c
new file mode 100644
index 000000000..bf49d6a29
--- /dev/null
+++ b/tests/pragmas/cfunction.c
@@ -0,0 +1,4 @@
+
+int cfunction(void) {
+  return NUMBER_HERE;
+}
diff --git a/tests/pragmas/custom_pragma.nim b/tests/pragmas/custom_pragma.nim
index 9e8f51deb..d2fc969d0 100644
--- a/tests/pragmas/custom_pragma.nim
+++ b/tests/pragmas/custom_pragma.nim
@@ -2,4 +2,4 @@
 
 template serializationKey*(s: string) {.pragma.}
 template defaultValue*(V: typed) {.pragma.}
-template alternativeKey*(s: string = nil, V: typed) {.pragma.}
\ No newline at end of file
+template alternativeKey*(s: string = "", V: typed) {.pragma.}
diff --git a/tests/pragmas/foobar.nim b/tests/pragmas/foobar.nim
new file mode 100644
index 000000000..46032e187
--- /dev/null
+++ b/tests/pragmas/foobar.nim
@@ -0,0 +1,3 @@
+import macros
+macro async*(body: untyped): untyped =
+  return newStmtList()
diff --git a/tests/pragmas/monoff1.nim b/tests/pragmas/monoff1.nim
new file mode 100644
index 000000000..85d6c57b3
--- /dev/null
+++ b/tests/pragmas/monoff1.nim
@@ -0,0 +1 @@
+proc on*() = discard
diff --git a/tests/pragmas/mpushexperimental.nim b/tests/pragmas/mpushexperimental.nim
new file mode 100644
index 000000000..569861c1d
--- /dev/null
+++ b/tests/pragmas/mpushexperimental.nim
@@ -0,0 +1,30 @@
+
+import macros
+
+macro enumerate(x: ForLoopStmt): untyped =
+  expectKind x, nnkForStmt
+  # we strip off the first for loop variable and use
+  # it as an integer counter:
+  result = newStmtList()
+  result.add newVarStmt(x[0], newLit(0))
+  var body = x[^1]
+  if body.kind != nnkStmtList:
+    body = newTree(nnkStmtList, body)
+  body.add newCall(bindSym"inc", x[0])
+  var newFor = newTree(nnkForStmt)
+  for i in 1..x.len-3:
+    newFor.add x[i]
+  # transform enumerate(X) to 'X'
+  newFor.add x[^2][1]
+  newFor.add body
+  result.add newFor
+
+proc main*[T](x: T) =
+  {.push experimental: "forLoopMacros".}
+
+  for a, b in enumerate(items([1, 2, 3])):
+    echo a, " ", b
+
+  for a2, b2 in enumerate([1, 2, 3, 5]):
+    echo a2, " ", b2
+  {.pop.}
diff --git a/tests/pragmas/mqualifiedmacro.nim b/tests/pragmas/mqualifiedmacro.nim
new file mode 100644
index 000000000..908973206
--- /dev/null
+++ b/tests/pragmas/mqualifiedmacro.nim
@@ -0,0 +1,10 @@
+template t*(x:untyped): untyped = 
+  echo "template t"
+
+import macros
+macro m*(name: static string, x: untyped): untyped =
+  let newName = ident(name)
+  result = quote do:
+    type `newName` = object
+  if result.kind == nnkStmtList:
+    result = result[^1]
diff --git a/tests/pragmas/t12558.nim b/tests/pragmas/t12558.nim
new file mode 100644
index 000000000..14fc74cee
--- /dev/null
+++ b/tests/pragmas/t12558.nim
@@ -0,0 +1,15 @@
+discard """
+  nimout: '''@["1", "2", "3"]'''
+"""
+
+import sequtils
+
+{.push compile_time.}
+
+proc foo =
+  echo map_it([1, 2, 3], $it)
+
+{.pop.}
+
+static:
+  foo()
diff --git a/tests/pragmas/t12640.nim b/tests/pragmas/t12640.nim
new file mode 100644
index 000000000..c85185699
--- /dev/null
+++ b/tests/pragmas/t12640.nim
@@ -0,0 +1,26 @@
+discard """
+  matrix: "--mm:refc"
+  nimout: '''1
+2
+3
+[1, 2, 3]'''
+
+  output: '''1
+2
+3
+[1, 2, 3]'''
+"""
+
+# todo fixme it doesn't work with ORC
+proc doIt(a: openArray[int]) =
+  echo a
+
+proc foo() = 
+  var bug {.global, compiletime.}: seq[int]
+  bug = @[1, 2 ,3]
+  for i in 0 .. high(bug): echo bug[i]
+  doIt(bug)
+
+static:
+  foo()
+foo()
diff --git a/tests/pragmas/t13306.nim b/tests/pragmas/t13306.nim
new file mode 100644
index 000000000..36713dd04
--- /dev/null
+++ b/tests/pragmas/t13306.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "'testEpo' can have side effects"
+  line: 8
+"""
+
+import times
+
+func testEpo(x: float): float = epochTime() + x
+
+echo testEpo(1.0)
diff --git a/tests/pragmas/t22713.nim b/tests/pragmas/t22713.nim
new file mode 100644
index 000000000..3d3384632
--- /dev/null
+++ b/tests/pragmas/t22713.nim
@@ -0,0 +1,12 @@
+import std/macros
+
+
+template myPragma(x: int) {.pragma.}
+
+type
+  A = object
+    x: int64
+
+  B {.myPragma(sizeof(A)).} = object
+
+doAssert B.getCustomPragmaVal(myPragma) == 8
\ No newline at end of file
diff --git a/tests/pragmas/t4384.nim b/tests/pragmas/t4384.nim
new file mode 100644
index 000000000..e6b193f79
--- /dev/null
+++ b/tests/pragmas/t4384.nim
@@ -0,0 +1,3 @@
+macro testMacro(body: untyped): untyped = discard
+macro testMacro(s: string, body: untyped): untyped = discard
+proc foo() {.testMacro: "foo".} = discard
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/t6448.nim b/tests/pragmas/t6448.nim
new file mode 100644
index 000000000..6efb56e08
--- /dev/null
+++ b/tests/pragmas/t6448.nim
@@ -0,0 +1,17 @@
+discard """
+  errormsg: '''ambiguous call'''
+  line: 10
+  disabled: "32bit"
+"""
+
+import foobar
+import asyncdispatch, macros
+
+proc bar() {.async.} =
+  echo 42
+
+proc foo() {.async.} =
+  await bar()
+
+asyncCheck foo()
+runForever()
diff --git a/tests/pragmas/t8741.nim b/tests/pragmas/t8741.nim
new file mode 100644
index 000000000..bf97b0e29
--- /dev/null
+++ b/tests/pragmas/t8741.nim
@@ -0,0 +1,29 @@
+discard """
+  cmd: "nim check --hint:processing:off $file"
+  errormsg: "3 is not two"
+  nimout: '''t8741.nim(13, 9) Error: invalid pragma: foobar
+t8741.nim(29, 15) template/generic instantiation of `onlyTwo` from here
+t8741.nim(25, 12) Error: 3 is not two
+'''
+"""
+
+for a {.gensym, inject.} in @[1,2,3]:
+  discard
+
+for a {.foobar.} in @[1,2,3]:
+  discard
+
+type Foo[N: static[int]] = distinct int
+
+proc isTwo(n: int): bool =
+  n == 2
+
+proc onlyTwo[N: static[int]](a: Foo[N]): int =
+  when isTwo(N):
+    int(a)
+  else:
+    {.error: $(N) & " is not two".}
+
+when isMainModule:
+  let foo: Foo[3] = Foo[3](5)
+  echo onlyTwo(foo)
diff --git a/tests/pragmas/tbitsize.nim b/tests/pragmas/tbitsize.nim
index 7a44944d2..39aee445f 100644
--- a/tests/pragmas/tbitsize.nim
+++ b/tests/pragmas/tbitsize.nim
@@ -10,13 +10,13 @@ type
 var
   b: bits
 
-assert b.flag == 0
+doAssert b.flag == 0
 b.flag = 1
-assert b.flag == 1
+doAssert b.flag == 1
 b.flag = 2
-assert b.flag == 0
+doAssert b.flag == 0
 
 b.opts = 7
-assert b.opts == 7
+doAssert b.opts == 7
 b.opts = 9
-assert b.opts == -7
+doAssert b.opts == -7
diff --git a/tests/pragmas/tcompile_missing_file.nim b/tests/pragmas/tcompile_missing_file.nim
new file mode 100644
index 000000000..fd90bd57d
--- /dev/null
+++ b/tests/pragmas/tcompile_missing_file.nim
@@ -0,0 +1,5 @@
+discard """
+  joinable: false
+  errormsg: "cannot find: noexist.c"
+"""
+{.compile: "noexist.c".}
diff --git a/tests/pragmas/tcompile_pragma.nim b/tests/pragmas/tcompile_pragma.nim
new file mode 100644
index 000000000..5b99352dd
--- /dev/null
+++ b/tests/pragmas/tcompile_pragma.nim
@@ -0,0 +1,10 @@
+discard """
+  output: '''34'''
+  joinable: false
+"""
+
+{.compile("cfunction.c", "-DNUMBER_HERE=34").}
+
+proc cfunction(): cint {.importc.}
+
+echo cfunction()
diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim
index 33a4a7e65..11a6df813 100644
--- a/tests/pragmas/tcustom_pragma.nim
+++ b/tests/pragmas/tcustom_pragma.nim
@@ -1,4 +1,6 @@
-import macros
+{.experimental: "notnil".}
+
+import macros, asyncmacro, asyncfutures
 
 block:
   template myAttr() {.pragma.}
@@ -6,23 +8,41 @@ block:
   proc myProc():int {.myAttr.} = 2
   const hasMyAttr = myProc.hasCustomPragma(myAttr)
   static:
-    assert(hasMyAttr)
+    doAssert(hasMyAttr)
 
 block:
   template myAttr(a: string) {.pragma.}
 
-  type MyObj = object
-    myField1, myField2 {.myAttr: "hi".}: int
+  type
+    MyObj = object
+      myField1, myField2 {.myAttr: "hi".}: int
+
+    MyGenericObj[T] = object
+      myField1, myField2 {.myAttr: "hi".}: int
+
+    MyOtherObj = MyObj
+
+
   var o: MyObj
   static:
-    assert o.myField2.hasCustomPragma(myAttr)
-    assert(not o.myField1.hasCustomPragma(myAttr))
+    doAssert o.myField2.hasCustomPragma(myAttr)
+    doAssert(not o.myField1.hasCustomPragma(myAttr))
+    doAssert(not o.myField1.hasCustomPragma(MyObj))
+    doAssert(not o.myField1.hasCustomPragma(MyOtherObj))
+
+  var ogen: MyGenericObj[int]
+  static:
+    doAssert ogen.myField2.hasCustomPragma(myAttr)
+    doAssert(not ogen.myField1.hasCustomPragma(myAttr))
+    doAssert(not ogen.myField1.hasCustomPragma(MyGenericObj))
+    doAssert(not ogen.myField1.hasCustomPragma(MyGenericObj))
+
 
 import custom_pragma
 block: # A bit more advanced case
   type
     Subfield {.defaultValue: "catman".} = object
-      c {.serializationKey: "cc".}: float
+      `c`* {.serializationKey: "cc".}: float
 
     MySerializable = object
       a {.serializationKey"asdf", defaultValue: 5.} : int
@@ -38,28 +58,33 @@ block: # A bit more advanced case
   var s: MySerializable
 
   const aDefVal = s.a.getCustomPragmaVal(defaultValue)
-  static: assert(aDefVal == 5)
+  static: doAssert(aDefVal == 5)
 
   const aSerKey = s.a.getCustomPragmaVal(serializationKey)
-  static: assert(aSerKey == "asdf")
+  static: doAssert(aSerKey == "asdf")
 
   const cSerKey = getCustomPragmaVal(s.field.c, serializationKey)
-  static: assert(cSerKey == "cc")
+  static: doAssert(cSerKey == "cc")
 
   const procSerKey = getCustomPragmaVal(myproc, serializationKey)
-  static: assert(procSerKey == "myprocSS")
+  static: doAssert(procSerKey == "myprocSS")
 
-  static: assert(hasCustomPragma(myproc, alternativeKey))
+  static: doAssert(hasCustomPragma(myproc, alternativeKey))
 
   const hasFieldCustomPragma = s.field.hasCustomPragma(defaultValue)
-  static: assert(hasFieldCustomPragma == false)
+  static: doAssert(hasFieldCustomPragma == false)
 
   # pragma on an object
   static:
-    assert Subfield.hasCustomPragma(defaultValue)
-    assert(Subfield.getCustomPragmaVal(defaultValue) == "catman")
+    doAssert Subfield.hasCustomPragma(defaultValue)
+    doAssert(Subfield.getCustomPragmaVal(defaultValue) == "catman")
+
+    doAssert hasCustomPragma(type(s.field), defaultValue)
 
-    assert hasCustomPragma(type(s.field), defaultValue)
+  proc foo(s: var MySerializable) =
+    static: doAssert(s.a.getCustomPragmaVal(defaultValue) == 5)
+
+  foo(s)
 
 block: # ref types
   type
@@ -82,8 +107,8 @@ block: # ref types
     leftSerKey = getCustomPragmaVal(s.left, serializationKey)
     rightSerKey = getCustomPragmaVal(s.right, serializationKey)
   static:
-    assert leftSerKey == "l"
-    assert rightSerKey == "r"
+    doAssert leftSerKey == "l"
+    doAssert rightSerKey == "r"
 
   var specS = SpecialNodeRef()
 
@@ -91,25 +116,25 @@ block: # ref types
     dataDefVal = hasCustomPragma(specS.data, defaultValue)
     specLeftSerKey = hasCustomPragma(specS.left, serializationKey)
   static:
-    assert dataDefVal == true
-    assert specLeftSerKey == true
+    doAssert dataDefVal == true
+    doAssert specLeftSerKey == true
 
   var ptrS = NodePtr(nil)
   const
     ptrRightSerKey = getCustomPragmaVal(ptrS.right, serializationKey)
   static:
-    assert ptrRightSerKey == "r"
+    doAssert ptrRightSerKey == "r"
 
   var f = MyFile()
   const
     fileDefVal = f.getCustomPragmaVal(defaultValue)
     filePathDefVal = f.path.getCustomPragmaVal(defaultValue)
   static:
-    assert fileDefVal == "closed"
-    assert filePathDefVal == "invalid"
+    doAssert fileDefVal == "closed"
+    doAssert filePathDefVal == "invalid"
 
   static:
-    assert TypeWithoutPragma.hasCustomPragma(defaultValue) == false
+    doAssert TypeWithoutPragma.hasCustomPragma(defaultValue) == false
 
 block:
   type
@@ -135,6 +160,381 @@ block:
     nestedItemDefVal = vari.nestedItem.getCustomPragmaVal(defaultValue)
 
   static:
-    assert hasIntSerKey
-    assert strSerKey == "string"
-    assert nestedItemDefVal == "Nimmers of the world, unite!"
\ No newline at end of file
+    doAssert hasIntSerKey
+    doAssert strSerKey == "string"
+    doAssert nestedItemDefVal == "Nimmers of the world, unite!"
+
+block:
+  template simpleAttr {.pragma.}
+
+  type Annotated {.simpleAttr.} = object
+
+  proc generic_proc[T]() =
+    doAssert Annotated.hasCustomPragma(simpleAttr)
+
+#--------------------------------------------------------------------------
+# Pragma on proc type
+
+type
+  MyAnnotatedProcType {.defaultValue(4).} = proc(x: int)
+
+let a {.defaultValue(4).}: proc(x: int)  = nil
+var b: MyAnnotatedProcType = nil
+var c: proc(x: int): void {.defaultValue(5).}  = nil
+var d {.defaultValue(44).}: MyAnnotatedProcType = nil
+static:
+  doAssert hasCustomPragma(a, defaultValue)
+  doAssert hasCustomPragma(MyAnnotatedProcType, defaultValue)
+  doAssert hasCustomPragma(b, defaultValue)
+  doAssert hasCustomPragma(typeof(c), defaultValue)
+  doAssert getCustomPragmaVal(d, defaultValue) == 44
+  doAssert getCustomPragmaVal(typeof(d), defaultValue) == 4
+
+# bug #8371
+template thingy {.pragma.}
+
+type
+  Cardinal = enum
+    north, east, south, west
+  Something = object
+    a: float32
+    case cardinal: Cardinal
+    of north:
+      b {.thingy.}: int
+    of east:
+      c: int
+    of south: discard
+    else: discard
+
+var foo: Something
+foo.cardinal = north
+doAssert foo.b.hasCustomPragma(thingy) == true
+
+proc myproc(s: string): int =
+  {.thingy.}:
+    s.len
+
+doAssert myproc("123") == 3
+
+let xx = compiles:
+  proc myproc_bad(s: string): int =
+    {.not_exist.}:
+      s.len
+doAssert: xx == false
+
+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)
+
+# var and let pragmas
+block:
+  template myAttr() {.pragma.}
+  template myAttr2(x: int) {.pragma.}
+  template myAttr3(x: string) {.pragma.}
+
+  type
+    MyObj2 = ref object
+    MyObjNotNil = MyObj2 not nil
+
+  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
+  var z2 {.myAttr.}: MyObjNotNil
+
+  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
+
+# pragma with implicit&explicit generic types
+block:
+  template fooBar[T](x: T; c: static[int] = 42; m: char) {.pragma.}
+  var e {.fooBar("foo", 123, 'u').}: int
+  doAssert(hasCustomPragma(e, fooBar))
+  doAssert(getCustomPragmaVal(e, fooBar).c == 123)
+
+block:
+  macro expectedAst(expectedRepr: static[string], input: untyped): untyped =
+    doAssert input.treeRepr & "\n" == expectedRepr
+    return input
+
+  macro expectedAstRepr(expectedRepr: static[string], input: untyped): untyped =
+    doAssert input.repr == expectedRepr
+    return input
+
+  const procTypeAst = """
+ProcTy
+  FormalParams
+    Empty
+    IdentDefs
+      Ident "x"
+      Ident "int"
+      Empty
+  Pragma
+    Ident "async"
+"""
+
+  type
+    Foo = proc (x: int) {.expectedAst(procTypeAst), async.}
+
+  static: doAssert Foo is proc(x: int): Future[void]
+
+  const asyncProcTypeAst = """
+proc (s: string): Future[void] {..}"""
+  # using expectedAst would show `OpenSymChoice` for Future[void], which is fragile.
+  type
+    Bar = proc (s: string) {.async, expectedAstRepr(asyncProcTypeAst).}
+
+  static: doAssert Bar is proc(x: string): Future[void]
+
+  const typeAst = """
+TypeDef
+  PragmaExpr
+    Ident "Baz"
+    Pragma
+  Empty
+  ObjectTy
+    Empty
+    Empty
+    RecList
+      IdentDefs
+        Ident "x"
+        Ident "string"
+        Empty
+"""
+
+  type
+    Baz {.expectedAst(typeAst).} = object
+      x: string
+
+  static: doAssert Baz.x is string
+
+  const procAst = """
+ProcDef
+  Ident "bar"
+  Empty
+  Empty
+  FormalParams
+    Ident "string"
+    IdentDefs
+      Ident "s"
+      Ident "string"
+      Empty
+  Empty
+  Empty
+  StmtList
+    ReturnStmt
+      Ident "s"
+"""
+
+  proc bar(s: string): string {.expectedAst(procAst).} =
+    return s
+
+  static: doAssert bar("x") == "x"
+
+#------------------------------------------------------
+# bug #13909
+
+template dependency*(id: string, weight = 0.0) {.pragma.}
+
+type
+  MyObject* = object
+    provider*: proc(obj: string): pointer {.dependency("Data/" & obj, 16.1), noSideEffect.}
+
+proc myproc(obj: string): string {.dependency("Data/" & obj, 16.1).} =
+  result = obj
+
+# bug 12523
+template myCustomPragma {.pragma.}
+
+type
+  RefType = ref object
+    field {.myCustomPragma.}: int
+
+  ObjType = object
+    field {.myCustomPragma.}: int
+  RefType2 = ref ObjType
+
+block:
+  let x = RefType()
+  for fieldName, fieldSym in fieldPairs(x[]):
+    doAssert hasCustomPragma(fieldSym, myCustomPragma)
+
+block:
+  let x = RefType2()
+  for fieldName, fieldSym in fieldPairs(x[]):
+    doAssert hasCustomPragma(fieldSym, myCustomPragma)
+
+# bug 8457
+block:
+  template world {.pragma.}
+
+  type
+    Hello = ref object
+      a: float32
+      b {.world.}: int
+
+  discard Hello(a: 1.0, b: 12)
+
+# test routines
+block:
+  template prag {.pragma.}
+  proc hello {.prag.} = discard
+  iterator hello2: int {.prag.} = discard
+  template hello3(x: int): int {.prag.} = x
+  macro hello4(x: int): int {.prag.} = x
+  func hello5(x: int): int {.prag.} = x
+  doAssert hello.hasCustomPragma(prag)
+  doAssert hello2.hasCustomPragma(prag)
+  doAssert hello3.hasCustomPragma(prag)
+  doAssert hello4.hasCustomPragma(prag)
+  doAssert hello5.hasCustomPragma(prag)
+
+# test push doesn't break
+block:
+  template prag {.pragma.}
+  {.push prag.}
+  proc hello = discard
+  iterator hello2: int = discard
+  template hello3(x: int): int = x
+  macro hello4(x: int): int = x
+  func hello5(x: int): int = x
+  type
+    Foo = enum a
+    Bar[T] = ref object of RootObj
+      x: T
+      case y: bool
+      of false: discard
+      else:
+        when true: discard
+  for a in [1]: discard a
+  {.pop.}
+
+# issue #11511
+when false:
+  template myAttr {.pragma.}
+
+  type TObj = object
+      a {.myAttr.}: int
+
+  macro hasMyAttr(t: typedesc): untyped =
+    let objTy = t.getType[1].getType
+    let recList = objTy[2]
+    let sym = recList[0]
+    assert sym.kind == nnkSym and sym.eqIdent("a")
+    let hasAttr = sym.hasCustomPragma(myAttr)
+    newLit(hasAttr)
+
+  doAssert hasMyAttr(TObj)
+
+
+# bug #11415
+template noserialize() {.pragma.}
+
+type
+  Point[T] = object
+    x, y: T
+
+  ReplayEventKind = enum
+    FoodAppeared, FoodEaten, DirectionChanged
+
+  ReplayEvent = object
+    case kind: ReplayEventKind
+    of FoodEaten, FoodAppeared: # foodPos is in multiple branches
+      foodPos {.noserialize.}: Point[float]
+    of DirectionChanged:
+      playerPos: float
+let ev = ReplayEvent(
+    kind: FoodEaten,
+    foodPos: Point[float](x: 5.0, y: 1.0)
+  )
+
+doAssert ev.foodPos.hasCustomPragma(noserialize)
+
+
+when false:
+  # misc
+  {.pragma: haha.}
+  {.pragma: hoho.}
+  template hehe(key, val: string, haha) {.pragma.}
+
+  type A {.haha, hoho, haha, hehe("hi", "hu", "he").} = int
+
+  assert A.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he")
+
+  template hehe(key, val: int) {.pragma.}
+
+  var bb {.haha, hoho, hehe(1, 2), haha, hehe("hi", "hu", "he").} = 3
+
+  # left-to-right priority/override order for getCustomPragmaVal
+  assert bb.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he")
+
+{.experimental: "dynamicBindSym".}
+
+# const
+block:
+  template myAttr() {.pragma.}
+  template myAttr2(x: int) {.pragma.}
+  template myAttr3(x: string) {.pragma.}
+
+  type
+    MyObj2 = ref object
+
+  const a {.myAttr,myAttr2(2),myAttr3:"test".}: int = 0
+  const b {.myAttr,myAttr2(2),myAttr3:"test".} = 0
+
+  macro forceHasCustomPragma(x: untyped, y: typed): untyped =
+    var x = bindSym(x.repr)
+    for c in x:
+      if c.symKind == nskConst:
+        x = c
+        break
+    result = getAst(hasCustomPragma(x, y))
+
+  macro forceGetCustomPragmaVal(x: untyped, y: typed): untyped =
+    var x = bindSym(x.repr)
+    for c in x:
+      if c.symKind == nskConst:
+        x = c
+        break
+    result = getAst(getCustomPragmaVal(x, y))
+
+  template check(s: untyped) =
+    doAssert forceHasCustomPragma(s, myAttr)
+    doAssert forceHasCustomPragma(s, myAttr2)
+    doAssert forceGetCustomPragmaVal(s, myAttr2) == 2
+    doAssert forceHasCustomPragma(s, myAttr3)
+    doAssert forceGetCustomPragmaVal(s, myAttr3) == "test"
+
+  check(a)
+  check(b)
+
+block: # https://forum.nim-lang.org/t/12522, backticks
+  template `mypragma`() {.pragma.}
+  # Error: invalid pragma: `mypragma`
+  type Test = object
+    field {.`mypragma`.}: int
+  doAssert Test().field.hasCustomPragma(mypragma)
diff --git a/tests/pragmas/tdeprecated.nim b/tests/pragmas/tdeprecated.nim
new file mode 100644
index 000000000..a5d07f727
--- /dev/null
+++ b/tests/pragmas/tdeprecated.nim
@@ -0,0 +1,10 @@
+# bug #6436
+proc foo(size: int, T: typedesc): seq[T]  {.deprecated.}=
+  result = newSeq[T](size)
+
+proc foo[T](size: int): seq[T]=
+  result = newSeq[T](size)
+
+let bar = foo[int](3) # Warning foo is deprecated
+
+doAssert bar == @[0, 0, 0]
diff --git a/tests/pragmas/thintprocessing.nim b/tests/pragmas/thintprocessing.nim
new file mode 100644
index 000000000..c608bc6e4
--- /dev/null
+++ b/tests/pragmas/thintprocessing.nim
@@ -0,0 +1,18 @@
+discard """
+  disabled: windows
+  matrix: "--hint:processing"
+  nimout: '''
+compile start
+..
+warn_module.nim(6, 6) Hint: 'test' is declared but not used [XDeclaredButNotUsed]
+compile end
+'''
+"""
+
+static:
+  echo "compile start"
+
+import warn_module
+
+static:
+  echo "compile end"
diff --git a/tests/pragmas/tinvalid_user_pragma.nim b/tests/pragmas/tinvalid_user_pragma.nim
new file mode 100644
index 000000000..3081db842
--- /dev/null
+++ b/tests/pragmas/tinvalid_user_pragma.nim
@@ -0,0 +1,9 @@
+discard """
+cmd: "nim check $file"
+"""
+
+{.pragma test: foo.} #[tt.Error
+^ invalid pragma:  {.pragma, test: foo.} ]#
+
+{.pragma: 1.} #[tt.Error
+^ invalid pragma:  {.pragma: 1.} ]#
diff --git a/tests/pragmas/tinvalidcustompragma.nim b/tests/pragmas/tinvalidcustompragma.nim
new file mode 100644
index 000000000..a31695809
--- /dev/null
+++ b/tests/pragmas/tinvalidcustompragma.nim
@@ -0,0 +1,23 @@
+discard """
+  cmd: "nim check $file"
+"""
+
+# issue #21652
+type Foo = object
+template foo() {.tags:[Foo].} = #[tt.Error
+                     ^ invalid pragma: tags: [Foo]]#
+  discard
+
+{.foobar.} #[tt.Error
+  ^ invalid pragma: foobar]#
+type A = enum a {.foobar.} #[tt.Error
+                  ^ invalid pragma: foobar]#
+for b {.foobar.} in [1]: discard #[tt.Error
+        ^ invalid pragma: foobar]#
+template foobar {.pragma.}
+{.foobar.} #[tt.Error
+  ^ cannot attach a custom pragma to 'tinvalidcustompragma'; custom pragmas are not supported for modules]#
+type A = enum a {.foobar.} #[tt.Error
+                  ^ cannot attach a custom pragma to 'a'; custom pragmas are not supported for enum fields]#
+for b {.foobar.} in [1]: discard #[tt.Error
+        ^ cannot attach a custom pragma to 'b'; custom pragmas are not supported for `for` loop variables]#
diff --git a/tests/pragmas/tlocks.nim b/tests/pragmas/tlocks.nim
index ba66a2dca..5d6fcdd9c 100644
--- a/tests/pragmas/tlocks.nim
+++ b/tests/pragmas/tlocks.nim
@@ -1,4 +1,3 @@
-
 type SomeBase* = ref object of RootObj
 type SomeDerived* = ref object of SomeBase
   memberProc*: proc ()
diff --git a/tests/pragmas/tnoreturn.nim b/tests/pragmas/tnoreturn.nim
index bb59b1c71..6a58055fe 100644
--- a/tests/pragmas/tnoreturn.nim
+++ b/tests/pragmas/tnoreturn.nim
@@ -1,5 +1,7 @@
 discard """
+matrix: "--mm:refc"
 ccodeCheck: "\\i @'__attribute__((noreturn))' .*"
+action: compile
 """
 
 proc noret1*(i: int) {.noreturn.} =
@@ -9,8 +11,8 @@ proc noret1*(i: int) {.noreturn.} =
 proc noret2*(i: int): void {.noreturn.} =
   echo i
 
-noret1(1)
-noret2(2)
+if true: noret1(1)
+if true: noret2(2)
 
 var p {.used.}: proc(i: int): int
 doAssert(not compiles(
diff --git a/tests/pragmas/tonoff1.nim b/tests/pragmas/tonoff1.nim
new file mode 100644
index 000000000..20ba7def2
--- /dev/null
+++ b/tests/pragmas/tonoff1.nim
@@ -0,0 +1,8 @@
+# issue #23002
+
+import monoff1
+
+proc test() =
+  {.warning[ProveInit]: on.}
+
+test()
diff --git a/tests/pragmas/tonoff2.nim b/tests/pragmas/tonoff2.nim
new file mode 100644
index 000000000..9dff5ef11
--- /dev/null
+++ b/tests/pragmas/tonoff2.nim
@@ -0,0 +1,14 @@
+discard """
+    action: compile
+"""
+
+# issue #22841
+
+import unittest
+
+proc on() =
+    discard
+
+suite "some suite":
+    test "some test":
+        discard
diff --git a/tests/pragmas/tpragmas_misc.nim b/tests/pragmas/tpragmas_misc.nim
new file mode 100644
index 000000000..adb7e73c3
--- /dev/null
+++ b/tests/pragmas/tpragmas_misc.nim
@@ -0,0 +1,75 @@
+##[
+tests for misc pragmas that don't need a separate file
+]##
+
+block:
+  static: doAssert not defined(tpragmas_misc_def)
+  {.undef(tpragmas_misc_def).} # works even if not set
+  static: doAssert not defined(tpragmas_misc_def)
+  {.define(tpragmas_misc_def).}
+  static: doAssert defined(tpragmas_misc_def)
+  {.undef(tpragmas_misc_def).}
+  static: doAssert not defined(tpragmas_misc_def)
+
+block: # (partial fix) bug #15920
+  block: # var template pragmas don't work in templates
+    template foo(expr) =
+      expr
+    proc fun1()=
+      let a {.foo.} = 1
+    template fun2()=
+      let a {.foo.} = 1
+    fun1() # ok
+    fun2() # WAS bug
+
+  template foo2() = discard # distractor (template or other symbol kind)
+  block:
+    template foo2(expr) =
+      expr
+    proc fun1()=
+      let a {.foo2.} = 1
+    template fun2()=
+      let a {.foo2.} = 1
+    fun1() # ok
+    fun2() # bug: Error: invalid pragma: foo2
+
+  block: # template pragmas don't work for templates, #18212
+    # adapted from $nim/lib/std/private/since.nim
+    # case without overload
+    template since3(version: (int, int), body: untyped) {.dirty.} =
+      when (NimMajor, NimMinor) >= version:
+        body
+    when true: # bug
+      template fun3(): int {.since3: (1, 3).} = 12
+
+  block: # ditto, w
+    # case with overload
+    template since2(version: (int, int), body: untyped) {.dirty.} =
+      when (NimMajor, NimMinor) >= version:
+        body
+    template since2(version: (int, int, int), body: untyped) {.dirty.} =
+      when (NimMajor, NimMinor, NimPatch) >= version:
+        body
+    when true: # bug
+      template fun3(): int {.since2: (1, 3).} = 12
+
+when true: # D20210801T100514:here
+  from macros import genSym
+  block:
+    template fn() =
+      var ret {.gensym.}: int # must special case template pragmas so it doesn't get confused
+      discard ret
+    fn()
+    static: discard genSym()
+
+block: # issue #10994
+  macro foo(x): untyped = x
+  template bar {.pragma.}
+
+  proc a {.bar.} = discard # works
+  proc b {.bar, foo.} = discard # doesn't
+
+block: # issue #22525
+  macro catch(x: typed) = x
+  proc thing {.catch.} = discard
+  thing()
diff --git a/tests/pragmas/tpragmas_reorder.nim b/tests/pragmas/tpragmas_reorder.nim
new file mode 100644
index 000000000..c4b1a6b0a
--- /dev/null
+++ b/tests/pragmas/tpragmas_reorder.nim
@@ -0,0 +1,19 @@
+discard """
+  matrix: "--experimental:codeReordering"
+"""
+
+runnableExamples:
+  import strtabs
+  var t = newStringTable()
+  t["name"] = "John"
+  t["city"] = "Monaco"
+  doAssert t.len == 2
+  doAssert t.hasKey "name"
+  doAssert "name" in t
+
+include "system/inclrtl"
+
+{.pragma: rtlFunc, rtl.}
+
+proc hasKey*(): bool {.rtlFunc.} =
+  discard
\ No newline at end of file
diff --git a/tests/pragmas/tpush.nim b/tests/pragmas/tpush.nim
index 5ecfe9704..9c6b85c4e 100644
--- a/tests/pragmas/tpush.nim
+++ b/tests/pragmas/tpush.nim
@@ -1,3 +1,7 @@
+discard """
+  targets: "c js"
+"""
+
 # test the new pragmas
 
 {.push warnings: off, hints: off.}
@@ -13,3 +17,128 @@ proc WarnMe() =
     x: int
   echo(x)
 
+# bug #11852
+proc foo(x: string, y: int, res: int) =
+  {.push checks: off}
+  var a: ptr char = unsafeAddr(x[y])
+  {.pop.}
+  if x.len > y:
+    doAssert ord(a[]) == 51
+  else:
+    doAssert x.len + 48 == res
+
+foo("", 0, 48)
+foo("abc", 40, 51)
+
+# bug #22362
+{.push staticBoundChecks: on.}
+proc main(): void =
+  {.pop.}
+  discard
+  {.push staticBoundChecks: on.}
+
+main()
+
+
+proc timnFoo[T](obj: T) {.noSideEffect.} = discard # BUG
+
+{.push exportc.}
+proc foo1() =
+  var s1 = "bar"
+  timnFoo(s1)
+  var s2 = @[1]
+  timnFoo(s2)
+{.pop.}
+
+
+block: # bug #22913
+  block:
+    type r = object
+
+    template std[T](x: T) =
+      let ttt {.used.} = x
+      result = $ttt
+
+    proc bar[T](x: T): string =
+      std(x)
+
+    {.push exportc: "$1".}
+    proc foo(): r =
+      let s = bar(123)
+    {.pop.}
+
+    discard foo()
+
+  block:
+    type r = object
+    {.push exportc: "$1".}
+    proc foo2(): r =
+      let s = $result
+    {.pop.}
+
+    discard foo2()
+
+block: # bug #23019
+  proc f(x: bool)
+
+  proc a(x: int) =
+    if false: f(true)
+
+  proc f(x: bool) =
+    if false: a(0)
+
+  proc k(r: int|int) {.inline.} =  # seems to require being generic and inline
+    if false: a(0)
+
+
+  # {.push tags: [].}
+  {.push raises: [].}
+
+  {.push warning[ObservableStores]:off.}  # can be any warning, off or on
+  let w = 0
+  k(w)
+  {.pop.}
+  {.pop.}
+
+{.push exportC.}
+
+block:
+  proc foo11() =
+    const factor = [1, 2, 3, 4]
+    doAssert factor[0] == 1
+  proc foo21() =
+    const factor = [1, 2, 3, 4]
+    doAssert factor[0] == 1
+
+  foo11()
+  foo21()
+
+template foo31() =
+  let factor = [1, 2, 3, 4]
+  doAssert factor[0] == 1
+template foo41() =
+  let factor = [1, 2, 3, 4]
+  doAssert factor[0] == 1
+
+foo31()
+foo41()
+
+{.pop.}
+
+import macros
+
+block:
+  {.push deprecated.}
+  template test() = discard
+  test()
+  {.pop.}
+  macro foo(): bool =
+    let ast = getImpl(bindSym"test")
+    var found = false
+    if ast[4].kind == nnkPragma:
+      for x in ast[4]:
+        if x.eqIdent"deprecated":
+          found = true
+          break
+    result = newLit(found)
+  doAssert foo()
diff --git a/tests/pragmas/tpushexperimental.nim b/tests/pragmas/tpushexperimental.nim
new file mode 100644
index 000000000..301419f60
--- /dev/null
+++ b/tests/pragmas/tpushexperimental.nim
@@ -0,0 +1,13 @@
+discard """
+  output: '''0 1
+1 2
+2 3
+0 1
+1 2
+2 3
+3 5'''
+"""
+
+import mpushexperimental
+
+main(12)
diff --git a/tests/pragmas/tpushnotes.nim b/tests/pragmas/tpushnotes.nim
new file mode 100644
index 000000000..27ba0bec4
--- /dev/null
+++ b/tests/pragmas/tpushnotes.nim
@@ -0,0 +1,13 @@
+discard """
+  matrix: "--warningAsError:HoleEnumConv"
+"""
+
+type
+  e = enum
+    a = 0
+    b = 2
+
+var i: int
+{.push warning[HoleEnumConv]:off.}
+discard i.e
+{.pop.}
diff --git a/tests/pragmas/tqualifiedmacro.nim b/tests/pragmas/tqualifiedmacro.nim
new file mode 100644
index 000000000..bc95ec1ea
--- /dev/null
+++ b/tests/pragmas/tqualifiedmacro.nim
@@ -0,0 +1,14 @@
+discard """
+  output: '''
+template t
+'''
+"""
+
+# issue #12696
+
+import mqualifiedmacro
+proc p() {. mqualifiedmacro.t .} = # errors with identifier expected but a.t found
+  echo "proc p"
+
+type Foo {. mqualifiedmacro.m("Bar") .} = object
+doAssert Bar is object
diff --git a/tests/pragmas/treorder.nim b/tests/pragmas/treorder.nim
index 6a6bbff4d..09a98ef6a 100644
--- a/tests/pragmas/treorder.nim
+++ b/tests/pragmas/treorder.nim
@@ -6,17 +6,18 @@ output:'''0
 """
 
 import macros
-{.reorder: on .}
+# {.reorder: on .}
+{.experimental: "codeReordering".}
 
 echo foo(-1)
 echo callWithFoo(0)
 echo(CA+CD)
 echo useTypes(TA(x:TB(x:1)), 2)
 second(0)
-  
+
 template callWithFoo(arg: untyped): untyped =
   foo(arg)
-  
+
 proc first(i: int): void
 
 proc second(i: int): void =
@@ -34,7 +35,7 @@ type
 type
   TCyclicA = ref object
     x: TDoubleCyclic
-  
+
 type
   TCyclicB = ref object
     x: TDoubleCyclic
@@ -71,5 +72,4 @@ macro make(arg: untyped): untyped =
 proc first(i: int): void =
   make(second)
 
-static:
-  var ss: string = ""
\ No newline at end of file
+var ss {.compileTime.}: string = ""
diff --git a/tests/pragmas/ttypedef_macro.nim b/tests/pragmas/ttypedef_macro.nim
new file mode 100644
index 000000000..dd4c87757
--- /dev/null
+++ b/tests/pragmas/ttypedef_macro.nim
@@ -0,0 +1,66 @@
+import macros
+
+macro makeref(s): untyped =
+  expectKind s, nnkTypeDef
+  result = newTree(nnkTypeDef, s[0], s[1], newTree(nnkRefTy, s[2]))
+
+type
+  Obj {.makeref.} = object
+    a: int
+
+doAssert Obj is ref
+doAssert Obj(a: 3)[].a == 3
+
+macro multiply(amount: static int, s): untyped =
+  let name = $s[0].basename
+  result = newNimNode(nnkTypeSection)
+  for i in 1 .. amount:
+    result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2]))
+
+type
+  Foo = object
+  Bar {.multiply: 2.} = object
+    x, y, z: int
+  Baz = object
+
+let bar1 = Bar1(x: 1, y: 2, z: 3)
+let bar2 = Bar2(x: bar1.x, y: bar1.y, z: bar1.z)
+doAssert Bar1 isnot Bar2
+doAssert not declared(Bar)
+doAssert not declared(Bar3)
+
+# https://github.com/nim-lang/RFCs/issues/219
+
+macro inferKind(td): untyped =
+  let name = $td[0].basename
+  var rhs = td[2]
+  while rhs.kind in {nnkPtrTy, nnkRefTy}: rhs = rhs[0]
+  if rhs.kind != nnkObjectTy:
+    result = td
+  else:
+    for n in rhs[^1]:
+      if n.kind == nnkRecCase and n[0][^2].eqIdent"_":
+        let kindTypeName = ident(name & "Kind")
+        let en = newTree(nnkEnumTy, newEmptyNode())
+        for i in 1 ..< n.len:
+          let branch = n[i]
+          if branch.kind == nnkOfBranch:
+            for j in 0 ..< branch.len - 1:
+              en.add(branch[j])
+        n[0][^2] = kindTypeName
+        return newTree(nnkTypeSection,
+          newTree(nnkTypeDef, kindTypeName, newEmptyNode(), en),
+          td)
+
+type Node {.inferKind.} = ref object
+  case kind: _
+  of opValue: value: int
+  of opAdd, opSub, opMul, opCall: kids: seq[Node]
+
+doAssert opValue is NodeKind
+let node = Node(kind: opMul, kids: @[
+  Node(kind: opValue, value: 3),
+  Node(kind: opValue, value: 5)
+])
+doAssert node.kind == opMul
+doAssert node.kids[0].value * node.kids[1].value == 15
diff --git a/tests/pragmas/tused.nim b/tests/pragmas/tused.nim
index 83c62b7bb..d0c533f9a 100644
--- a/tests/pragmas/tused.nim
+++ b/tests/pragmas/tused.nim
@@ -1,11 +1,13 @@
 discard """
   nimout: '''
 compile start
-tused.nim(15, 8) Hint: 'tused.echoSub(a: int, b: int)[declared in tused.nim(15, 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
 """
 
+# not joinable because paths in nimout differ when imported
 static:
   echo "compile start"
 
@@ -31,5 +33,11 @@ block:
   implementArithOpsNew(int)
   echoAdd 3, 5
 
+# issue #9896
+type
+  MyEnum {.used.} = enum
+    Val1, Val2, Val3
+
+
 static:
   echo "compile end"
diff --git a/tests/pragmas/tuserpragma2.nim b/tests/pragmas/tuserpragma2.nim
index bf8844e66..c3f31cd5e 100644
--- a/tests/pragmas/tuserpragma2.nim
+++ b/tests/pragmas/tuserpragma2.nim
@@ -1,11 +1,12 @@
 discard """
+  errormsg: "can raise an unlisted exception: ref Exception"
   file: "tuserpragma2.nim"
   line: 11
-  errormsg: "can raise an unlisted exception: ref Exception"
 """
-
+{.push warningAsError[Effect]: on.}
 # bug #7216
 {.pragma: my_pragma, raises: [].}
 
 proc test1 {.my_pragma.} =
   raise newException(Exception, "msg")
+{.pop.}
diff --git a/tests/pragmas/tuserpragmaargs.nim b/tests/pragmas/tuserpragmaargs.nim
new file mode 100644
index 000000000..791d703ac
--- /dev/null
+++ b/tests/pragmas/tuserpragmaargs.nim
@@ -0,0 +1,5 @@
+var foo {.exportc: "abc".} = 123
+{.pragma: importc2, importc.}
+var bar {.importc2: "abc".}: int #[tt.Error
+                  ^ user pragma cannot have arguments]#
+echo bar
diff --git a/tests/pragmas/tvar_macro.nim b/tests/pragmas/tvar_macro.nim
new file mode 100644
index 000000000..3fb6e3e53
--- /dev/null
+++ b/tests/pragmas/tvar_macro.nim
@@ -0,0 +1,128 @@
+import macros
+
+block: # test usage
+  macro modify(sec) =
+    result = copy sec
+    result[0][0] = ident(repr(result[0][0]) & "Modified")
+
+  block:
+    let foo {.modify.} = 3
+    doAssert fooModified == 3
+
+  block: # in section 
+    let
+      a = 1
+      b {.modify.} = 2
+      c = 3
+    doAssert (a, bModified, c) == (1, 2, 3)
+
+block: # with single argument
+  macro appendToName(name: static string, sec) =
+    result = sec
+    result[0][0] = ident(repr(result[0][0]) & name)
+
+  block:
+    let foo {.appendToName: "Bar".} = 3
+    doAssert fooBar == 3
+
+  block:
+    let
+      a = 1
+      b {.appendToName("").} = 2
+      c = 3
+    doAssert (a, b, c) == (1, 2, 3)
+
+macro appendToNameAndAdd(name: static string, incr: static int, sec) =
+  result = sec
+  result[0][0] = ident(repr(result[0][0]) & name)
+  result[0][2] = infix(result[0][2], "+", newLit(incr))
+
+block: # with multiple arguments
+  block:
+    let foo {.appendToNameAndAdd("Bar", 5).} = 3
+    doAssert fooBar == 8
+
+  block:
+    let
+      a = 1
+      b {.appendToNameAndAdd("", 15).} = 2
+      c = 3
+    doAssert (a, b, c) == (1, 17, 3)
+
+block: # in other kinds of sections
+  block:
+    const
+      a = 1
+      b {.appendToNameAndAdd("", 15).} = 2
+      c = 3
+    doAssert (a, b, c) == (1, 17, 3)
+    doAssert static(b) == b
+
+  block:
+    var
+      a = 1
+      b {.appendToNameAndAdd("", 15).} = 2
+      c = 3
+    doAssert (a, b, c) == (1, 17, 3)
+    b += a
+    c += b
+    doAssert (a, b, c) == (1, 18, 21)
+
+block: # with other pragmas
+  macro appendToNameAndAdd(name: static string, incr, sec) =
+    result = sec
+    result[0][0][0] = ident(repr(result[0][0][0]) & name)
+    result[0][0][1].add(ident"deprecated")
+    result[0][2] = infix(result[0][2], "+", incr)
+
+  var
+    a = 1
+    foo {.exportc: "exportedFooBar", appendToNameAndAdd("Bar", {'0'..'9'}), used.} = {'a'..'z', 'A'..'Z'}
+    b = 2
+  
+  doAssert (a, b) == (1, 2)
+
+  let importedFooBar {.importc: "exportedFooBar", nodecl.}: set[char]
+
+  doAssert importedFooBar == fooBar #[tt.Warning
+                             ^ fooBar is deprecated
+  ]#
+  
+
+block: # with stropping
+  macro `cast`(def) =
+    let def = def[0]
+    let
+      lhs = def[0]
+      typ = def[1]
+      ex = def[2]
+      addrTyp = if typ.kind == nnkEmpty: typ else: newTree(nnkPtrTy, typ)
+    result = quote do:
+      let tmp: `addrTyp` = unsafeAddr(`ex`)
+      template `lhs`: untyped = tmp[]
+  
+  macro assign(def) =
+    result = getAst(`cast`(def))
+
+  block:
+    let s = @["foo", "bar"]
+    let a {.`assign`.} = s[0]
+    doAssert a == "foo"
+    doAssert a[0].addr == s[0][0].addr
+
+  block:
+    let
+      s = @["foo", "bar"]
+      a {.`cast`.} = s[0]
+    doAssert a == "foo"
+    doAssert a[0].addr == s[0][0].addr
+
+block: # bug #15920
+  macro foo(def) =
+    result = def
+  proc fun1()=
+    let a {.foo.} = 1
+  template fun2()=
+    let a {.foo.} = 1
+  fun1() # ok
+  fun2() # BUG
diff --git a/tests/pragmas/twarning_off.nim b/tests/pragmas/twarning_off.nim
new file mode 100644
index 000000000..ccf07b9c4
--- /dev/null
+++ b/tests/pragmas/twarning_off.nim
@@ -0,0 +1,15 @@
+discard """
+  nimout: '''
+compile start
+warn_module.nim(6, 6) Hint: 'test' is declared but not used [XDeclaredButNotUsed]
+compile end
+'''
+"""
+
+static:
+  echo "compile start"
+
+import warn_module
+
+static:
+  echo "compile end"
diff --git a/tests/pragmas/warn_module.nim b/tests/pragmas/warn_module.nim
new file mode 100644
index 000000000..0d1e5f419
--- /dev/null
+++ b/tests/pragmas/warn_module.nim
@@ -0,0 +1,7 @@
+
+{.warning[UnusedImport]: off.}
+
+import hashes
+
+proc test(a: float): float =
+  a
diff --git a/tests/defaultprocparam/mdefaultprocparam.nim b/tests/proc/mdefaultprocparam.nim
index 4a17277c0..4a17277c0 100644
--- a/tests/defaultprocparam/mdefaultprocparam.nim
+++ b/tests/proc/mdefaultprocparam.nim
diff --git a/tests/proc/t15949.nim b/tests/proc/t15949.nim
new file mode 100644
index 000000000..6467ed5d3
--- /dev/null
+++ b/tests/proc/t15949.nim
@@ -0,0 +1,16 @@
+# bug #15949 and RFC #480
+
+proc procWarn(a, b = 1): (int, int) = (a, b) #[tt.Warning
+              ^ a, b all have default value '1', this may be unintentional, either use ';' (semicolon) or explicitly write each default value [ImplicitDefaultValue]]#
+
+proc procGood(a = 1, b = 1): (int, int) = (a, b)
+
+doAssert procGood() == (1, 1)
+doAssert procGood(b = 3) == (1, 3)
+doAssert procGood(a = 2) == (2, 1)
+doAssert procGood(a = 5, b = 6) == (5, 6)
+
+# The type (and default value propagation breaks in the below example
+# as semicolon is used instead of comma.
+proc procBad(a; b = 1): (int, int) = (a, b) #[tt.Error
+             ^ parameter 'a' requires a type]#
diff --git a/tests/proc/t17157.nim b/tests/proc/t17157.nim
new file mode 100644
index 000000000..020e93fce
--- /dev/null
+++ b/tests/proc/t17157.nim
@@ -0,0 +1,6 @@
+discard """
+  errormsg: "'untyped' is only allowed in templates and macros or magic procs"
+"""
+
+template something(op: proc (v: untyped): void): void =
+  discard
diff --git a/tests/proc/t19795.nim b/tests/proc/t19795.nim
new file mode 100644
index 000000000..677ec0a63
--- /dev/null
+++ b/tests/proc/t19795.nim
@@ -0,0 +1,19 @@
+discard """
+  matrix: "--mm:arc"
+"""
+
+# bug #19795
+# bug #21085
+
+type Vector = seq[int]
+
+var vect: Vector = newSeq[int](5)
+doAssert vect == @[0, 0, 0, 0, 0]
+
+# Needed to get the problem. Could also use "var".
+let vectCopy = vect
+
+# Then some procedure definition is needed to get the problem.
+proc p(): int = 3
+
+doAssert vect == @[0, 0, 0, 0, 0]
\ No newline at end of file
diff --git a/tests/proc/t23874.nim b/tests/proc/t23874.nim
new file mode 100644
index 000000000..940bc4ac8
--- /dev/null
+++ b/tests/proc/t23874.nim
@@ -0,0 +1,26 @@
+block:
+  type Head[T] = object
+    wasc: bool
+
+  proc `=destroy`[T](x: var Head[T]) =
+    discard
+
+  proc `=copy`[T](x: var Head[T], y: Head[T]) =
+    x.wasc = true
+
+  proc `=dup`[T](x: Head[T]): Head[T] =
+    result.wasc = true
+
+  proc update(h: var Head) =
+    discard
+
+  proc digest(h: sink Head) =
+    assert h.wasc
+
+  var h = Head[int](wasc: false)
+  h.digest() # sink h
+  h.update() # use after sink
+
+block:
+  proc two(a: sink auto) =discard
+  assert typeof(two[int]) is proc(a: sink int) {.nimcall.}
diff --git a/tests/misc/tcolonisproc.nim b/tests/proc/tcolonisproc.nim
index 665e9e604..c10dabcf1 100644
--- a/tests/misc/tcolonisproc.nim
+++ b/tests/proc/tcolonisproc.nim
@@ -1,3 +1,9 @@
+discard """
+output: '''
+1
+2
+'''
+"""
 
 proc p(a, b: int, c: proc ()) =
   c()
diff --git a/tests/proc/tdefaultprocparam.nim b/tests/proc/tdefaultprocparam.nim
new file mode 100644
index 000000000..90edfa8c9
--- /dev/null
+++ b/tests/proc/tdefaultprocparam.nim
@@ -0,0 +1,90 @@
+discard """
+output: '''
+hi
+hi
+topLevel|topLevel|
+topLevel2|topLevel2|
+inProc|inProc|
+inProc2|inProc2|
+topLevel|9
+topLevel2|10
+inProc|7
+inProc2|8
+must have been the wind..
+I'm there
+must have been the wind..
+I'm there
+symbol'a'symbol'a'
+symbol'b'symbol'b'
+symbol'a'symbol'b'
+symbol'a'9
+symbol'b'9
+symbol'a'0
+'''
+"""
+import mdefaultprocparam
+
+p()
+
+proc testP =
+  p()
+
+testP()
+
+proc p2(s: string, count = s): string = s & count
+
+proc testP2 =
+  echo p2 """inProc|"""
+  echo p2 """inProc2|"""
+
+echo p2 """topLevel|"""
+echo p2 """topLevel2|"""
+
+testP2()
+
+import macros
+macro dTT(a: typed) = echo a.treeRepr
+
+proc p3(s: string, count = len(s)): string = s & $count
+
+proc testP3 =
+  echo p3 """inProc|"""
+  echo p3 """inProc2|"""
+
+echo p3 """topLevel|"""
+echo p3 """topLevel2|"""
+
+testP3()
+
+proc cut(s: string, c = len(s)): string =
+  s[0..<s.len-c]
+
+echo "must have been the wind.." & cut "I'm gone"
+echo cut("I'm gone", 4) & "there"
+
+proc testCut =
+  echo "must have been the wind.." & cut "I'm gone"
+  echo cut("I'm gone", 4) & "there"
+
+testCut()
+
+var a = "symbol'a'"
+var b = "symbol'b'"
+
+block:
+  echo p2(a)
+block:
+  echo p2(b)
+block:
+  echo p2(a, b)
+block:
+  echo p3(a)
+  echo p3(b)
+  echo p3(a, 0)
+
+# bug #12252
+proc foo(a = 0, b = a.high, c = high(typeof(a))) = 
+  discard
+
+foo()
+
diff --git a/tests/proc/texplicitgenericcount.nim b/tests/proc/texplicitgenericcount.nim
new file mode 100644
index 000000000..8654a1d13
--- /dev/null
+++ b/tests/proc/texplicitgenericcount.nim
@@ -0,0 +1,24 @@
+discard """
+  cmd: "nim check -d:testsConciseTypeMismatch $file"
+"""
+
+proc foo[T, U](x: T, y: U): (T, U) = (x, y)
+
+let x = foo[int](1, 2) #[tt.Error
+                ^ type mismatch
+Expression: foo[int](1, 2)
+  [1] 1: int literal(1)
+  [2] 2: int literal(2)
+
+Expected one of (first mismatch at [position]):
+[2] proc foo[T, U](x: T; y: U): (T, U)
+  missing generic parameter: U]#
+let y = foo[int, float, string](1, 2) #[tt.Error
+                               ^ type mismatch
+Expression: foo[int, float, string](1, 2)
+  [1] 1: int literal(1)
+  [2] 2: int literal(2)
+
+Expected one of (first mismatch at [position]):
+[3] proc foo[T, U](x: T; y: U): (T, U)
+  extra generic param given]#
diff --git a/tests/proc/texplicitgenericcountverbose.nim b/tests/proc/texplicitgenericcountverbose.nim
new file mode 100644
index 000000000..76228eeaf
--- /dev/null
+++ b/tests/proc/texplicitgenericcountverbose.nim
@@ -0,0 +1,22 @@
+discard """
+  cmd: "nim check $file"
+"""
+
+proc foo[T, U](x: T, y: U): (T, U) = (x, y)
+
+let x = foo[int](1, 2) #[tt.Error
+                ^ type mismatch: got <int literal(1), int literal(2)>
+but expected one of:
+proc foo[T, U](x: T; y: U): (T, U)
+  first type mismatch at position: 2 in generic parameters
+  missing generic parameter: U
+
+expression: foo[int](1, 2)]#
+let y = foo[int, float, string](1, 2) #[tt.Error
+                               ^ type mismatch: got <int literal(1), int literal(2)>
+but expected one of:
+proc foo[T, U](x: T; y: U): (T, U)
+  first type mismatch at position: 3 in generic parameters
+  extra generic param given
+
+expression: foo[int, float, string](1, 2)]#
diff --git a/tests/proc/texplicitgenerics.nim b/tests/proc/texplicitgenerics.nim
new file mode 100644
index 000000000..833d77b3b
--- /dev/null
+++ b/tests/proc/texplicitgenerics.nim
@@ -0,0 +1,55 @@
+block: # issue #16376
+  type
+    Matrix[T] = object
+      data: T
+  proc randMatrix[T](m, n: int, max: T): Matrix[T] = discard
+  proc randMatrix[T](m, n: int, x: Slice[T]): Matrix[T] = discard
+  template randMatrix[T](m, n: int): Matrix[T] = randMatrix[T](m, n, T(1.0))
+  let B = randMatrix[float32](20, 10)
+
+block: # different generic param counts 
+  type
+    Matrix[T] = object
+      data: T
+  proc randMatrix[T](m: T, n: T): Matrix[T] = Matrix[T](data: T(1.0))
+  proc randMatrix[T; U: not T](m: T, n: U): (Matrix[T], U) = (Matrix[T](data: T(1.0)), default(U))
+  let b = randMatrix[float32](20, 10)
+  doAssert b == Matrix[float32](data: 1.0)
+
+block: # above for templates 
+  type
+    Matrix[T] = object
+      data: T
+  template randMatrix[T](m: T, n: T): Matrix[T] = Matrix[T](data: T(1.0))
+  template randMatrix[T; U: not T](m: T, n: U): (Matrix[T], U) = (Matrix[T](data: T(1.0)), default(U))
+  let b = randMatrix[float32](20, 10)
+  doAssert b == Matrix[float32](data: 1.0)
+
+block: # sigmatch can't handle this without pre-instantiating the type:
+  # minimized from numericalnim
+  type Foo[T] = proc (x: T)
+  proc foo[T](x: T) = discard
+  proc bar[T](f: Foo[T]) = discard
+  bar[int](foo)
+
+block: # ditto but may be wrong minimization
+  # minimized from measuremancer
+  type Foo[T] = object
+  proc foo[T](): Foo[T] = Foo[T]()
+  # this is the actual issue but there are other instantiation problems
+  proc bar[T](x = foo[T]()) = discard
+  bar[int](Foo[int]())
+  bar[int]()
+  # alternative version, also causes instantiation issue
+  proc baz[T](x: typeof(foo[T]())) = discard
+  baz[int](Foo[int]())
+
+block: # issue #21346
+  type K[T] = object
+  template s[T](x: int) = doAssert T is K[K[int]]
+  proc b1(n: bool | bool) = s[K[K[int]]](3)
+  proc b2(n: bool)        = s[K[K[int]]](3)
+  template b3(n: bool)    = s[K[K[int]]](3)
+  b1(false)     # Error: cannot instantiate K; got: <T> but expected: <T>
+  b2(false)     # Builds, on its own
+  b3(false)
diff --git a/tests/proc/tfunc_type.nim b/tests/proc/tfunc_type.nim
new file mode 100644
index 000000000..93697acb1
--- /dev/null
+++ b/tests/proc/tfunc_type.nim
@@ -0,0 +1,14 @@
+
+discard """
+  errormsg: "func keyword is not allowed in type descriptions, use proc with {.noSideEffect.} pragma instead"
+"""
+
+type
+  MyObject = object
+    fn: func(a: int): int
+
+proc myproc(a: int): int =
+  echo "bla"
+  result = a
+
+var x = MyObject(fn: myproc)
\ No newline at end of file
diff --git a/tests/proc/tgenericdefaultparam.nim b/tests/proc/tgenericdefaultparam.nim
new file mode 100644
index 000000000..7bce591ce
--- /dev/null
+++ b/tests/proc/tgenericdefaultparam.nim
@@ -0,0 +1,98 @@
+block: # issue #16700
+  type MyObject[T] = object
+    x: T
+  proc initMyObject[T](value = T.default): MyObject[T] =
+    MyObject[T](x: value)
+  var obj = initMyObject[int]()
+
+block: # issue #20916
+  type
+    SomeX = object
+      v: int
+  var val = 0
+  proc f(_: type int, x: SomeX, v = x.v) =
+    doAssert v == 42
+    val = v
+  proc a(): proc() =
+    let v = SomeX(v: 42)
+    var tmp = proc() =
+      int.f(v)
+    tmp
+  a()()
+  doAssert val == 42
+
+import std/typetraits
+
+block: # issue #24099, original example
+  type
+    ColorRGBU = distinct array[3, uint8] ## RGB range 0..255
+    ColorRGBAU = distinct array[4, uint8] ## RGB range 0..255
+    ColorRGBUAny = ColorRGBU | ColorRGBAU
+  template componentType(t: typedesc[ColorRGBUAny]): typedesc =
+    ## Returns component type of a given color type.
+    arrayType distinctBase t
+  func `~=`[T: ColorRGBUAny](a, b: T, e = componentType(T)(1.0e-11)): bool =
+    ## Compares colors with given accuracy.
+    abs(a[0] - b[0]) < e and abs(a[1] - b[1]) < e and abs(a[2] - b[2]) < e
+
+block: # issue #24099, modified to actually work
+  type
+    ColorRGBU = distinct array[3, uint8] ## RGB range 0..255
+    ColorRGBAU = distinct array[4, uint8] ## RGB range 0..255
+    ColorRGBUAny = ColorRGBU | ColorRGBAU
+  template arrayType[I, T](t: typedesc[array[I, T]]): typedesc =
+    T
+  template `[]`(a: ColorRGBUAny, i: untyped): untyped = distinctBase(a)[i]
+  proc abs(a: uint8): uint8 = a
+  template componentType(t: typedesc[ColorRGBUAny]): typedesc =
+    ## Returns component type of a given color type.
+    arrayType distinctBase t
+  func `~=`[T: ColorRGBUAny](a, b: T, e = componentType(T)(1.0e-11)): bool =
+    ## Compares colors with given accuracy.
+    abs(a[0] - b[0]) <= e and abs(a[1] - b[1]) <= e and abs(a[2] - b[2]) <= e
+  doAssert ColorRGBU([1.uint8, 1, 1]) ~= ColorRGBU([1.uint8, 1, 1])
+
+block: # issue #24099, modified to work but using float32
+  type
+    ColorRGBU = distinct array[3, float32] ## RGB range 0..255
+    ColorRGBAU = distinct array[4, float32] ## RGB range 0..255
+    ColorRGBUAny = ColorRGBU | ColorRGBAU
+  template arrayType[I, T](t: typedesc[array[I, T]]): typedesc =
+    T
+  template `[]`(a: ColorRGBUAny, i: untyped): untyped = distinctBase(a)[i]
+  template componentType(t: typedesc[ColorRGBUAny]): typedesc =
+    ## Returns component type of a given color type.
+    arrayType distinctBase t
+  func `~=`[T: ColorRGBUAny](a, b: T, e = componentType(T)(1.0e-11)): bool =
+    ## Compares colors with given accuracy.
+    abs(a[0] - b[0]) < e and abs(a[1] - b[1]) < e and abs(a[2] - b[2]) < e
+  doAssert ColorRGBU([1.float32, 1, 1]) ~= ColorRGBU([1.float32, 1, 1])
+
+block: # issue #13270
+  type
+    A = object
+    B = object
+  proc f(a: A) = discard
+  proc g[T](value: T, cb: (proc(a: T)) = f) =
+    cb value
+  g A()
+  # This should fail because there is no f(a: B) overload available
+  doAssert not compiles(g B())
+
+block: # issue #24121
+  type
+    Foo = distinct int
+    Bar = distinct int
+    FooBar = Foo | Bar
+
+  proc foo[T: distinct](x: T): string = "a"
+  proc foo(x: Foo): string = "b"
+  proc foo(x: Bar): string = "c"
+
+  proc bar(x: FooBar, y = foo(x)): string = y
+  doAssert bar(Foo(123)) == "b"
+  doAssert bar(Bar(123)) == "c"
+
+  proc baz[T: FooBar](x: T, y = foo(x)): string = y
+  doAssert baz(Foo(123)) == "b"
+  doAssert baz(Bar(123)) == "c"
diff --git a/tests/proc/tillegalreturntype.nim b/tests/proc/tillegalreturntype.nim
new file mode 100644
index 000000000..1076f7f75
--- /dev/null
+++ b/tests/proc/tillegalreturntype.nim
@@ -0,0 +1,21 @@
+discard """
+  cmd: "nim check --hints:off $file"
+  errormsg: ""
+  nimout: '''
+tillegalreturntype.nim(11, 11) Error: return type 'typed' is only valid for macros and templates
+tillegalreturntype.nim(14, 11) Error: return type 'untyped' is only valid for macros and templates
+tillegalreturntype.nim(17, 41) Error: return type 'auto' cannot be used in forward declarations
+'''
+"""
+
+proc x(): typed =
+  discard
+
+proc y(): untyped =
+  discard
+
+proc test_proc[T, U](arg1: T, arg2: U): auto
+
+proc test_proc[T, U](arg1: T, arg2: U): auto =
+    echo "Proc has been called"
+    return arg1 / arg2
diff --git a/tests/proc/tinferlambdareturn.nim b/tests/proc/tinferlambdareturn.nim
new file mode 100644
index 000000000..e9e592871
--- /dev/null
+++ b/tests/proc/tinferlambdareturn.nim
@@ -0,0 +1,36 @@
+import std/[sugar, sequtils]
+
+block: # issue #23200
+  proc dosomething(iter: int -> (iterator: int)) =
+    discard
+  proc dosomething(iter: int -> seq[int]) =
+    discard
+  proc makeSeq(x: int): seq[int] =
+    @[x]
+  # Works fine with 1.6.12 and 1.6.14
+  dosomething(makeSeq)
+  # Works with 1.6.12, fails with 1.6.14
+  dosomething((y) => makeSeq(y))
+  dosomething(proc (y: auto): auto = makeSeq(y))
+  proc foo(y: auto): auto = makeSeq(y)
+  dosomething(foo)
+
+block: # issue #18866
+  proc somefn[T](list: openarray[T], op: proc (v: T): float) =
+    discard op(list[0])
+
+  type TimeD = object
+    year:  Natural
+    month: 1..12
+    day:   1..31
+
+  doAssert not compiles(@[TimeD()].somefn(proc (v: auto): auto =
+    v
+  ))
+  @[TimeD()].somefn(proc (v: auto): auto =
+    v.year.float
+  )
+  proc foo(v: auto): auto = v
+  doAssert not compiles(@[TimeD()].somefn(foo))
+  proc bar(v: auto): auto = v.year.float
+  @[TimeD()].somefn(bar)
diff --git a/tests/proc/tlambdadonotation.nim b/tests/proc/tlambdadonotation.nim
new file mode 100644
index 000000000..3160c0972
--- /dev/null
+++ b/tests/proc/tlambdadonotation.nim
@@ -0,0 +1,78 @@
+discard """
+output: '''
+issue #11812
+issue #10899
+123
+issue #11367
+event consumed!
+'''
+"""
+
+echo "issue #11812"
+
+proc run(a: proc()) = a()
+
+proc main() =
+  var test: int
+  run(proc() = test = 0)
+  run do:
+    test = 0
+
+main()
+
+
+echo "issue #10899"
+
+proc foo(x: proc {.closure.}) =
+  x()
+
+proc bar =
+  var x = 123
+  # foo proc = echo x     #[ ok ]#
+  foo: echo x             #[ SIGSEGV: Illegal storage access. (Attempt to read from nil?) ]#
+
+bar()
+
+echo "issue #11367"
+
+type
+
+  EventCB = proc()
+
+  Emitter = object
+    cb: EventCB
+
+  Subscriber = object
+    discard
+
+proc newEmitter(): Emitter =
+  result
+
+proc on_event(self: var Emitter, cb: EventCB) =
+  self.cb = cb
+
+proc emit(self: Emitter) =
+  self.cb()
+
+proc newSubscriber(): Subscriber =
+  result
+
+proc consume(self: Subscriber) =
+  echo "event consumed!"
+
+proc main2() =
+  var emitter = newEmitter()
+  var subscriber = newSubscriber()
+
+  proc foo() =
+    subscriber.consume()
+
+  emitter.on_event() do ():
+    subscriber.consume()
+
+  # this works
+  # emitter.on_event(foo)
+
+  emitter.emit()
+
+main2()
diff --git a/tests/proc/tlambdapragma.nim b/tests/proc/tlambdapragma.nim
new file mode 100644
index 000000000..daa952b1e
--- /dev/null
+++ b/tests/proc/tlambdapragma.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "invalid pragma: exportc"
+"""
+
+let _ = proc () {.exportc.} =
+  # this would previously cause a codegen error
+  discard
diff --git a/tests/namedparams/tnamedparams.nim b/tests/proc/tnamedparams.nim
index 8799e8a15..d0774f0d8 100644
--- a/tests/namedparams/tnamedparams.nim
+++ b/tests/proc/tnamedparams.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "type mismatch: got <input: string, filename: string, line: int literal(1), col: int literal(23)>"
   file: "tnamedparams.nim"
   line: 8
-  errormsg: "type mismatch: got <input: string, filename: string, line: int literal(1), col: int literal(23)>"
 """
 import pegs
 
@@ -10,6 +10,3 @@ discard parsePeg(
       filename = "filename",
       line = 1,
       col = 23)
-
-
-
diff --git a/tests/proc/tnamedparams2.nim b/tests/proc/tnamedparams2.nim
new file mode 100644
index 000000000..9acdeed87
--- /dev/null
+++ b/tests/proc/tnamedparams2.nim
@@ -0,0 +1,15 @@
+import pegs
+
+discard parsePeg(
+      pattern = "input",
+      filename = "filename",
+      line = 1,
+      col = 23)
+
+# bug #12196
+type
+  Renderer = object
+
+var xs0, x0, xs1, x1: int
+proc init(xs=xs0; x=x0; renderer: Renderer; r: byte) = discard
+init(xs=xs1, x=x1, r=3, renderer=Renderer())
diff --git a/tests/namedparams/tnamedparams3.nim b/tests/proc/tnamedparams3.nim
index e736c338c..e736c338c 100644
--- a/tests/namedparams/tnamedparams3.nim
+++ b/tests/proc/tnamedparams3.nim
diff --git a/tests/proc/tnestprc.nim b/tests/proc/tnestprc.nim
deleted file mode 100644
index 9b3c33d5e..000000000
--- a/tests/proc/tnestprc.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-discard """
-  file: "tnestprc.nim"
-  output: "10"
-"""
-# Test nested procs without closures
-
-proc Add3(x: int): int =
-  proc add(x, y: int): int {.noconv.} =
-    result = x + y
-
-  result = add(x, 3)
-
-echo Add3(7) #OUT 10
-
-
-
diff --git a/tests/proc/tparamsindefault.nim b/tests/proc/tparamsindefault.nim
new file mode 100644
index 000000000..3fe917f2b
--- /dev/null
+++ b/tests/proc/tparamsindefault.nim
@@ -0,0 +1,120 @@
+discard """
+output: '''
+@[1, 2, 3]@[1, 2, 3]
+a
+a
+1
+3 is an int
+2 is an int
+miau is a string
+f1 1 1 1
+f1 2 3 3
+f1 10 20 30
+f2 100 100 100
+f2 200 300 300
+f2 300 400 400
+f3 10 10 20
+f3 10 15 25
+true true
+false true
+world
+typedescDefault
+'''
+"""
+
+template reject(x) =
+  assert(not compiles(x))
+
+block:
+  # https://github.com/nim-lang/Nim/issues/7756
+  proc foo[T](x: seq[T], y: seq[T] = x) =
+    echo x, y
+
+  let a = @[1, 2, 3]
+  foo(a)
+
+block:
+  # https://github.com/nim-lang/Nim/issues/1201
+  proc issue1201(x: char|int = 'a') = echo x
+
+  issue1201()
+  issue1201('a')
+  issue1201(1)
+
+  # https://github.com/nim-lang/Nim/issues/7000
+  proc test(a: int|string = 2) =
+    when a is int:
+        echo a, " is an int"
+    elif a is string:
+        echo a, " is a string"
+
+  test(3) # works
+  test() # works
+  test("miau")
+
+block:
+  # https://github.com/nim-lang/Nim/issues/3002 and similar
+  proc f1(a: int, b = a, c = b) =
+    echo "f1 ", a, " ", b, " ", c
+
+  proc f2(a: int, b = a, c: int = b) =
+    echo "f2 ", a, " ", b, " ", c
+
+  proc f3(a: int, b = a, c = a + b) =
+    echo "f3 ", a, " ", b, " ", c
+
+  f1 1
+  f1(2, 3)
+  f1 10, 20, 30
+  100.f2
+  200.f2 300
+  300.f2(400)
+
+  10.f3()
+  10.f3(15)
+
+  reject:
+    # This is a type mismatch error:
+    proc f4(a: int, b = a, c: float = b) = discard
+
+  reject:
+    # undeclared identifier
+    proc f5(a: int, b = c, c = 10) = discard
+
+  reject:
+    # undeclared identifier
+    proc f6(a: int, b = b) = discard
+
+  reject:
+    # undeclared identifier
+    proc f7(a = a) = discard
+
+block:
+  proc f(a: var int, b: ptr int, c = addr(a)) =
+    echo addr(a) == b, " ",  b == c
+
+  var x = 10
+  f(x, addr(x))
+  f(x, nil, nil)
+
+block:
+  # https://github.com/nim-lang/Nim/issues/1046
+  proc pySubstr(s: string, start: int, endd = s.len()): string =
+    var
+      revStart = start
+      revEnd = endd
+
+    if start < 0:
+      revStart = s.len() + start
+    if endd < 0:
+      revEnd = s.len() + endd
+
+    return s[revStart ..  revEnd-1]
+
+  echo pySubstr("Hello world", -5)
+
+
+# bug #11660
+
+func typedescDefault(T: typedesc; arg: T = 0) = debugEcho "typedescDefault"
+typedescDefault(int)
diff --git a/tests/proc/tproc.nim b/tests/proc/tproc.nim
new file mode 100644
index 000000000..d7f861991
--- /dev/null
+++ b/tests/proc/tproc.nim
@@ -0,0 +1,31 @@
+discard """
+  output: '''
+Hello
+1
+'''
+"""
+
+
+block t8357:
+  type T = ref int
+
+  let r = new(string)
+  r[] = "Hello"
+  echo r[]
+
+
+block t8683:
+  proc foo[T](bar: proc (x, y: T): int = system.cmp, baz: int) =
+    echo "1"
+  proc foo[T](bar: proc (x, y: T): int = system.cmp) =
+    echo "2"
+
+  foo[int](baz = 5)
+
+
+block tnestprc:
+  proc Add3(x: int): int =
+    proc add(x, y: int): int {.noconv.} =
+      result = x + y
+    result = add(x, 3)
+  doAssert Add3(7) == 10
diff --git a/tests/proc/tprocredef.nim b/tests/proc/tprocredef.nim
index 4ec771510..0cd6ec770 100644
--- a/tests/proc/tprocredef.nim
+++ b/tests/proc/tprocredef.nim
@@ -1,9 +1,8 @@
 discard """
+  errormsg: "redefinition of \'foo\'"
   file: "tprocredef.nim"
   line: 8
-  errormsg: "redefinition of \'foo\'"
 """
 
 proc foo(a: int, b: string) = discard
 proc foo(a: int, b: string) = discard
-
diff --git a/tests/proc/tprocvar.nim b/tests/proc/tprocvar.nim
new file mode 100644
index 000000000..14f24efdc
--- /dev/null
+++ b/tests/proc/tprocvar.nim
@@ -0,0 +1,39 @@
+discard """
+  output: '''
+papbpcpdpe7
+'''
+"""
+
+block genericprocvar:
+  proc foo[T](thing: T) =
+    discard thing
+  var a: proc (thing: int) {.nimcall.} = foo[int]
+
+
+block tprocvar2:
+  proc pa() {.cdecl.} = write(stdout, "pa")
+  proc pb() {.cdecl.} = write(stdout, "pb")
+  proc pc() {.cdecl.} = write(stdout, "pc")
+  proc pd() {.cdecl.} = write(stdout, "pd")
+  proc pe() {.cdecl.} = write(stdout, "pe")
+
+  const algos = [pa, pb, pc, pd, pe]
+  var x: proc (a, b: int): int {.cdecl.}
+
+  proc ha(c, d: int): int {.cdecl.} =
+    echo(c + d)
+    result = c + d
+
+  for a in items(algos):
+    a()
+
+  x = ha
+  discard x(3, 4)
+
+
+block tprocvars:
+  proc doSomething(v: int, x: proc(v:int):int): int = return x(v)
+  proc doSomething(v: int, x: proc(v:int)) = x(v)
+
+  doAssert doSomething(10, proc(v: int): int = return v div 2) == 5
+
diff --git a/tests/proc/tprocvarmismatch.nim b/tests/proc/tprocvarmismatch.nim
new file mode 100644
index 000000000..4d6be9be6
--- /dev/null
+++ b/tests/proc/tprocvarmismatch.nim
@@ -0,0 +1,18 @@
+discard """
+  errormsg: "type mismatch"
+  line: 17
+  file: "tprocvarmismatch.nim"
+"""
+
+type
+  TCallback = proc (a, b: int)
+
+proc huh(x, y: var int) =
+  x = 0
+  y = x+1
+
+proc so(c: TCallback) =
+  c(2, 4)
+
+so(huh)
+
diff --git a/tests/proc/tstaticsignature.nim b/tests/proc/tstaticsignature.nim
new file mode 100644
index 000000000..25aa09c5d
--- /dev/null
+++ b/tests/proc/tstaticsignature.nim
@@ -0,0 +1,268 @@
+block: # issue #4228
+  template seqType(t: typedesc): typedesc =
+    when t is int:
+      seq[int]
+    else:
+      seq[string]
+
+  proc mkSeq[T: int|string](v: T): seqType(T) =
+    result = newSeq[T](1)
+    result[0] = v
+
+  doAssert mkSeq("a") == @["a"]
+  doAssert mkSeq(1) == @[1]
+
+block: # expanded version of t8545
+  template bar(a: static[bool]): untyped =
+    when a:
+      int
+    else:
+      float
+
+  proc main() =
+    proc foo1(a: static[bool]): auto = 1
+    doAssert foo1(true) == 1
+
+    proc foo2(a: static[bool]): bar(a) = 1
+    doAssert foo2(true) == 1
+    doAssert foo2(true) is int
+    doAssert foo2(false) == 1.0
+    doAssert foo2(false) is float
+
+    proc foo3(a: static[bool]): bar(cast[bool](a)) = 1
+    doAssert foo3(true) == 1
+    doAssert foo3(true) is int
+    doAssert foo3(false) == 1.0
+    doAssert foo3(false) is float
+
+    proc foo4(a: static[bool]): bar(static(a)) = 1
+    doAssert foo4(true) == 1
+    doAssert foo4(true) is int
+    doAssert foo4(false) == 1.0
+    doAssert foo4(false) is float
+
+  static: main()
+  main()
+
+block: # issue #8406
+  macro f(x: static[int]): untyped = discard
+  proc g[X: static[int]](v: f(X)) = discard
+
+import macros
+
+block: # issue #8551
+  macro distinctBase2(T: typedesc): untyped =
+    let typeNode = getTypeImpl(T)
+    expectKind(typeNode, nnkBracketExpr)
+    if typeNode[0].typeKind != ntyTypeDesc:
+      error "expected typeDesc, got " & $typeNode[0]
+    var typeSym = typeNode[1]
+
+    typeSym = getTypeImpl(typeSym)
+
+    if typeSym.typeKind != ntyDistinct:
+      error "type is not distinct: " & $typeSym.typeKind
+
+    typeSym = typeSym[0]
+    typeSym
+
+  func distinctBase[T](a: T): distinctBase2(T) = distinctBase2(T)(a)
+
+  type T = distinct int
+  doAssert distinctBase(T(0)) is int
+
+block:
+  type Foo[T] = object
+    x: T
+
+  proc foo(x: Foo): Foo[x.T] =
+    doAssert typeof(result) is typeof(x)
+
+  var a: Foo[int]
+  let b: Foo[int] = foo(a)
+  doAssert b.x is int
+
+block:
+  type Foo[T: static int] = object
+    x: array[T, int]
+  
+  proc double(x: int): int = x * 2
+
+  proc foo[T: static int](x: Foo[T]): Foo[T.double] =
+    doAssert typeof(result).T == double(typeof(x).T)
+
+  var a: Foo[3]
+  let b: Foo[6] = foo(a)
+  doAssert $typeof(foo(a)) == "Foo[6]"
+
+block:
+  type Foo[T: static int] = object
+    x: array[T, int]
+
+  proc foo(x: Foo): Foo[x.T] =
+    doAssert typeof(result).T == typeof(x).T
+    doAssert typeof(result) is typeof(x)
+
+  var a: Foo[3]
+  let b: Foo[3] = foo(a)
+  doAssert $typeof(foo(a)) == "Foo[3]"
+
+block: # issue #7006
+  type
+    Node[T] = object
+      val: T
+      next: ref Node[T]
+    HHSet[T, Key] = object
+      data: seq[Node[T]]
+  proc rawGet(hhs:HHSet; key: hhs.Key): ptr Node[hhs.T] =
+    return nil # body doesn't matter
+  var hhs: HHSet[string, cstring]
+  discard hhs.rawGet("hello".cstring)
+
+block: # issue #7008
+  type Node[T] = object
+    val: T
+  # Compiles fine
+  proc concreteProc(s: Node[cstring]; key: s.T) = discard
+  # Also fine
+  proc implicitGenericProc1(s: Node; key: s.T) = discard
+  # still fine
+  proc explicitGenericProc1[T](s: Node[T]; key: T) = discard
+  # Internal Compiler Error!
+  proc explicitGenericProc2[T](s: Node[T]; key: s.T) = discard
+  let n = Node[int](val: 5)
+  implicitGenericProc1(n, 5) # works
+  explicitGenericProc1(n, 5) # works
+  explicitGenericProc2(n, 5) # doesn't
+
+block: # issue #20027
+  block:
+    type Test[T] = object
+    proc run(self: Test): self.T = discard
+    discard run(Test[int]())
+  block:
+    type Test[T] = object
+    proc run[T](self: Test[T]): self.T = discard
+    discard run(Test[int]())
+  block:
+    type Test[T] = object
+    proc run(self: Test[auto]): self.T = discard
+    discard run(Test[int]())
+
+block: # issue #11112
+  proc foo[A, B]: type(A.default + B.default) =
+    discard
+  doAssert foo[int, int]() is int
+
+block: # tyStatic and tyFromExpr instantiation mid-match
+  proc bar(x: int): int = x * 3
+  proc bar2(x: static int): int = x * 4
+  type Foo[T: static int] = distinct array[T, int]
+  proc foo[T: static int](x: Foo[T], y: Foo[bar(T)]) = discard
+  proc foo2[T: static int](x: Foo[T], y: Foo[bar2(T)]) = discard
+  foo(Foo[1]([1]), Foo[3]([1, 2, 3]))
+  foo2(Foo[1]([1]), Foo[4]([1, 2, 3, 4]))
+
+block: # issue #4990
+  type Foo[I: static[int], A: static[array[I, int]]] = object
+    curIndex: int
+
+  proc next[I: static[int], A: static[array[I, int]]](f: Foo[I, A]): string =
+    discard
+  const arr = [1, 2, 3]
+  var f: Foo[arr.len, arr]
+  discard next(f)
+
+block: # issue #4990 comment
+  type
+    Foo[A: static[int], B: static[int], TokenType: enum, EofToken: static[TokenType]] = object
+      curIndex: int
+    MyEnum = enum
+      meA, meB
+    Bar = Foo[2, 3, MyEnum, meA]
+  proc next[A: static[int], B: static[int], TokenType: enum,
+            EofToken: static[TokenType]](f: Foo[A, B, TokenType, EofToken],
+      a: static[(array[A, int], array[B, int])]): TokenType =
+    TokenType(a[0][f.curIndex])
+  const
+    a = [1, 2]
+    b = [3, 4, 5]
+  template next(bar: Bar): MyEnum =
+    next(Foo[2, 3, MyEnum, meA](bar), (a, b))
+  let bar = Bar(curIndex: 0)
+  doAssert bar.next() == meB
+
+block: # issue #14053
+  template returnType(value: static[int]): typedesc =
+    when value == 1:
+      int
+    else:
+      float
+  proc fun(value: static[int]): returnType(value) = discard
+  doAssert fun(1) is int
+  template returnType2(value: static[int]): typedesc =
+    int
+  proc fun2(value: static[int]): returnType2(value) = discard
+  doAssert fun2(1) is int
+
+block: # issue #7547
+  macro foo(N: static[int]): untyped =
+    result = getType(int)
+  type
+    Foo[N: static[int]] = foo(N)
+    ContainsFoo[N: static[int]] = object
+      Ffoo: Foo[N]
+  proc initFoo(N: static[int]): Foo[N] = discard
+  proc initContainsFoo(size: static[int]): ContainsFoo[size] = discard
+  var a: Foo[10] # Works
+  doAssert a is int
+  let b = initFoo(10) # Works
+  doAssert b is int
+  let c = ContainsFoo[5]() # Works
+  doAssert c.Ffoo is int
+  let z = initContainsFoo(5) # Error: undeclared identifier: 'N'
+  doAssert z.Ffoo is int
+
+block: # issue #22607, needs nkWhenStmt to be handled like nkRecWhen
+  proc test[x: static bool](
+    t: (
+      when x:
+        int
+      else:
+        float
+      )
+  ) = discard
+  test[true](1.int)
+  test[false](1.0)
+  doAssert not compiles(test[])
+
+block: # `when` in static signature
+  template ctAnd(a, b): bool =
+    when a:
+      when b: true
+      else: false
+    else: false
+  template test(): untyped =
+    when ctAnd(declared(SharedTable), typeof(result) is SharedTable):
+      result = SharedTable()
+    else:
+      result = 123
+  proc foo[T](): T = test()
+  proc bar[T](x = foo[T]()): T = x
+  doAssert bar[int]() == 123
+
+block: # issue #22276
+  type Foo = enum A, B
+  macro test(y: static[Foo]): untyped =
+    if y == A:
+      result = parseExpr("proc (x: int)")
+    else:
+      result = parseExpr("proc (x: float)")
+  proc foo(y: static[Foo], x: test(y)) = # We want to make the type of `x` depend on what `y` is
+    x(9)
+  foo(A, proc (x: int) = doAssert x == 9)
+  var a: int
+  foo(A, proc (x: int) =
+    a = x * 2)
+  doAssert a == 18
+  foo(B, proc (x: float) = doAssert x == 9)
diff --git a/tests/proc/tunderscoreparam.nim b/tests/proc/tunderscoreparam.nim
new file mode 100644
index 000000000..8d60603f1
--- /dev/null
+++ b/tests/proc/tunderscoreparam.nim
@@ -0,0 +1,122 @@
+discard """
+  targets: "c cpp js"
+"""
+
+import std/[assertions, sequtils]
+
+proc test() =
+  block:
+    proc ok(_, _, a: int): int =
+      doAssert not compiles(_)
+      a
+    doassert ok(4, 2, 5) == 5
+
+  block:
+    proc ok(_: int, _: int, a: int): int = a
+    doAssert ok(4, 2, 5) == 5
+
+  block:
+    proc ok(_: int, _: float, a: int): int = a
+    doAssert ok(1, 2.0, 5) == 5
+
+  block:
+    proc ok(_: int, _: float, _: string, a: int): int = a
+    doAssert ok(1, 2.6, "5", 5) == 5
+    
+  block:
+    proc ok[T](_, _, a: T): T =
+      doAssert not compiles(_)
+      a
+    doAssert ok(4, 2, 5) == 5
+    doAssert ok("a", "b", "c") == "c"
+    doAssert not compiles(ok(1, 2, "a"))
+  
+  block:
+    let ok = proc (_, _, a: int): int =
+      doAssert not compiles(_)
+      a
+    doAssert ok(4, 2, 5) == 5
+  
+  block:
+    proc foo(lam: proc (_, _, a: int): int): int =
+      lam(4, 2, 5)
+    doAssert foo(proc (_, _, a: auto): auto =
+      doAssert not compiles(_)
+      a) == 5
+    
+  block:
+    iterator fn(_, _: int, c: int): int = yield c
+    doAssert toSeq(fn(1,2,3)) == @[3]
+
+  block:
+    template ok(_, _, a: int): int = a
+    doAssert ok(4, 2, 5) == 5
+
+  block:
+    doAssert not (compiles do:
+      template bad(_: int): int = _
+      discard bad(3))
+
+  block:
+    template ok(_: int, _: int, a: int): int = a
+    doAssert ok(4, 2, 5) == 5
+
+  block:
+    template ok(_: int, _: float, a: int): int = a
+    doAssert ok(1, 2.0, 5) == 5
+
+  block:
+    template ok(_: int, _: float, _: string, a: int): int = a
+    doAssert ok(1, 2.6, "5", 5) == 5
+  
+  block:
+    template main2() =
+      iterator fn(_, _: int, c: int): int = yield c
+    main2()
+
+  block:
+    template main =
+      proc foo(_: int) =
+        let a = _
+    doAssert not compiles(main())
+  
+  block: # generic params
+    doAssert not (compiles do:
+      proc foo[_](t: typedesc[_]): seq[_] = @[default(_)]
+      doAssert foo[int]() == 0)
+  
+  block:
+    proc foo[_, _](): int = 123
+    doAssert foo[int, bool]() == 123
+  
+  block:
+    proc foo[T; U](_: typedesc[T]; _: typedesc[U]): (T, U) = (default(T), default(U))
+    doAssert foo(int, bool) == (0, false)
+
+proc closureTest() =
+  var x = 0
+
+  block:
+    proc foo(_, _: int) = x += 5
+
+    foo(1, 2)
+    doAssert x == 5
+
+  block:
+    proc foo(_: int, _: float) = x += 5
+
+    foo(1, 2)
+    doAssert x == 10
+
+  block:
+    proc foo(_: int, _: float, _: string) = x += 5
+
+    foo(1, 2, "5")
+    doAssert x == 15
+
+static: test()
+test()
+
+when not defined(js):
+  static: closureTest()
+closureTest()
diff --git a/tests/proc/twrongdefaultvalue.nim b/tests/proc/twrongdefaultvalue.nim
new file mode 100644
index 000000000..2c36c2247
--- /dev/null
+++ b/tests/proc/twrongdefaultvalue.nim
@@ -0,0 +1,25 @@
+discard """
+  cmd: "nim check $file"
+  action: reject
+  nimout: '''
+twrongdefaultvalue.nim(20, 12) template/generic instantiation of `doit` from here
+twrongdefaultvalue.nim(17, 37) Error: type mismatch: got <proc (p: int): Item[initItem.T]> but expected 'Item[system.string]'
+twrongdefaultvalue.nim(25, 3) template/generic instantiation of `foo` from here
+twrongdefaultvalue.nim(23, 33) Error: type mismatch: got <string> but expected 'int'
+'''
+"""
+
+block: # issue #21258
+  type Item[T] = object
+    pos: int
+  proc initItem[T](p:int=10000) : Item[T] = 
+    result = Item[T](p)
+  proc doit[T](x:Item[T], s:Item[T]=initItem) : string = 
+    return $x.pos
+  let x = Item[string](pos:100)
+  echo doit(x)
+
+block: # issue #21258, reduced case
+  proc foo[T](x: seq[T], y: T = "foo") =
+    discard
+  foo @[1, 2, 3]
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..4a87f2b07
--- /dev/null
+++ b/tests/proc/untyped.nim
@@ -0,0 +1,15 @@
+discard """
+  errormsg: "'untyped' is only allowed in templates and macros or magic procs"
+  line: 14
+"""
+
+# magic procs are allowed with `untyped`
+proc declaredInScope2*(x: untyped): bool {.magic: "DefinedInScope", noSideEffect, compileTime.}
+proc bar(): bool =
+  var x = 1
+  declaredInScope2(x)
+static: doAssert bar()
+
+# but not non-magic procs
+proc fun(x:untyped)=discard
+fun(10)
diff --git a/tests/procvar/tgenericprocvar.nim b/tests/procvar/tgenericprocvar.nim
deleted file mode 100644
index e642e3577..000000000
--- a/tests/procvar/tgenericprocvar.nim
+++ /dev/null
@@ -1,5 +0,0 @@
-proc foo[T](thing: T) =
-    discard thing
-
-var a: proc (thing: int) {.nimcall.} = foo[int]
-
diff --git a/tests/procvar/tprocvar.nim b/tests/procvar/tprocvar.nim
deleted file mode 100644
index f523aa391..000000000
--- a/tests/procvar/tprocvar.nim
+++ /dev/null
@@ -1,18 +0,0 @@
-discard """
-  errormsg: "type mismatch"
-  line: 17
-  file: "tprocvar.nim"
-"""
-
-type
-  TCallback = proc (a, b: int)
-
-proc huh(x, y: var int) =
-  x = 0
-  y = x+1
-
-proc so(c: TCallback) =
-  c(2, 4)
-
-so(huh)
-
diff --git a/tests/procvar/tprocvar2.nim b/tests/procvar/tprocvar2.nim
deleted file mode 100644
index a590bc4bd..000000000
--- a/tests/procvar/tprocvar2.nim
+++ /dev/null
@@ -1,32 +0,0 @@
-discard """
-  file: "tprocvar.nim"
-  output: "papbpcpdpe7"
-"""
-# test variables of type proc
-
-proc pa() {.cdecl.} = write(stdout, "pa")
-proc pb() {.cdecl.} = write(stdout, "pb")
-proc pc() {.cdecl.} = write(stdout, "pc")
-proc pd() {.cdecl.} = write(stdout, "pd")
-proc pe() {.cdecl.} = write(stdout, "pe")
-
-const
-  algos = [pa, pb, pc, pd, pe]
-
-var
-  x: proc (a, b: int): int {.cdecl.}
-
-proc ha(c, d: int): int {.cdecl.} =
-  echo(c + d)
-  result = c + d
-
-for a in items(algos):
-  a()
-
-x = ha
-discard x(3, 4)
-
-#OUT papbpcpdpe7
-
-
-
diff --git a/tests/procvar/tprocvars.nim b/tests/procvar/tprocvars.nim
deleted file mode 100644
index 50d5d29f2..000000000
--- a/tests/procvar/tprocvars.nim
+++ /dev/null
@@ -1,6 +0,0 @@
-proc doSomething(v: int, x: proc(v:int):int): int = return x(v)
-proc doSomething(v: int, x: proc(v:int)) = x(v)
-
-
-echo doSomething(10, proc(v: int): int = return v div 2)
-
diff --git a/tests/range/t19678.nim b/tests/range/t19678.nim
new file mode 100644
index 000000000..88f7eff89
--- /dev/null
+++ b/tests/range/t19678.nim
@@ -0,0 +1,17 @@
+discard """
+  cmd: "nim check --hints:off $file"
+  errormsg: ""
+  nimout: '''
+t19678.nim(13, 13) Error: range of string is invalid
+
+
+
+'''
+"""
+
+case "5":
+  of "0" .. "9":
+    discard
+  else:
+    discard
+
diff --git a/tests/range/tbug499771.nim b/tests/range/tbug499771.nim
deleted file mode 100644
index 1504a2ad7..000000000
--- a/tests/range/tbug499771.nim
+++ /dev/null
@@ -1,14 +0,0 @@
-discard """
-  file: "tbug499771.nim"
-  output: '''TSubRange: 5 from 1 to 10
-true true true'''
-"""
-type
-  TSubRange = range[1 .. 10]
-  TEnum = enum A, B, C
-var sr: TSubRange = 5
-echo("TSubRange: " & $sr & " from " & $low(TSubRange) & " to " &
-     $high(TSubRange))
-
-const cset = {A} + {B}
-echo A in cset, " ", B in cset, " ", C notin cset
diff --git a/tests/range/tcolors.nim b/tests/range/tcolors.nim
deleted file mode 100644
index ab5b3e199..000000000
--- a/tests/range/tcolors.nim
+++ /dev/null
@@ -1,39 +0,0 @@
-import strutils
-
-type
-  TColor = distinct int32
-
-proc rgb(r, g, b: range[0..255]): TColor =
-  result = TColor(r or g shl 8 or b shl 16)
-
-proc `$`(c: TColor): string =
-  result = "#" & toHex(int32(c), 6)
-
-echo rgb(34, 55, 255)
-
-when false:
-  type
-    TColor = distinct int32
-    TColorComponent = distinct int8
-
-  proc red(a: TColor): TColorComponent =
-    result = TColorComponent(int32(a) and 0xff'i32)
-
-  proc green(a: TColor): TColorComponent =
-    result = TColorComponent(int32(a) shr 8'i32 and 0xff'i32)
-
-  proc blue(a: TColor): TColorComponent =
-    result = TColorComponent(int32(a) shr 16'i32 and 0xff'i32)
-
-  proc rgb(r, g, b: range[0..255]): TColor =
-    result = TColor(r or g shl 8 or b shl 8)
-
-  proc `+!` (a, b: TColorComponent): TColorComponent =
-    ## saturated arithmetic:
-    result = TColorComponent(min(ze(int8(a)) + ze(int8(b)), 255))
-
-  proc `+` (a, b: TColor): TColor =
-    ## saturated arithmetic for colors makes sense, I think:
-    return rgb(red(a) +! red(b), green(a) +! green(b), blue(a) +! blue(b))
-
-  rgb(34, 55, 255)
diff --git a/tests/range/tcompiletime_range_checks.nim b/tests/range/tcompiletime_range_checks.nim
new file mode 100644
index 000000000..2d3f292ec
--- /dev/null
+++ b/tests/range/tcompiletime_range_checks.nim
@@ -0,0 +1,52 @@
+discard """
+  cmd: "nim check --hint:Processing:off --hint:Conf:off $file"
+  errormsg: "18446744073709551615 can't be converted to int8"
+  nimout: '''
+tcompiletime_range_checks.nim(36, 21) Error: 2147483648 can't be converted to int32
+tcompiletime_range_checks.nim(38, 34) Error: 255 can't be converted to FullNegativeRange
+tcompiletime_range_checks.nim(39, 34) Error: 18446744073709551615 can't be converted to HalfNegativeRange
+tcompiletime_range_checks.nim(40, 34) Error: 300 can't be converted to FullPositiveRange
+tcompiletime_range_checks.nim(41, 30) Error: 101 can't be converted to UnsignedRange
+tcompiletime_range_checks.nim(42, 32) Error: -9223372036854775808 can't be converted to SemiOutOfBounds
+tcompiletime_range_checks.nim(44, 22) Error: nan can't be converted to int32
+tcompiletime_range_checks.nim(46, 23) Error: 1e+100 can't be converted to uint64
+tcompiletime_range_checks.nim(49, 22) Error: 18446744073709551615 can't be converted to int64
+tcompiletime_range_checks.nim(50, 22) Error: 18446744073709551615 can't be converted to int32
+tcompiletime_range_checks.nim(51, 22) Error: 18446744073709551615 can't be converted to int16
+tcompiletime_range_checks.nim(52, 21) Error: 18446744073709551615 can't be converted to int8
+  '''
+"""
+
+type
+  UnsignedRange* = range[0'u64 .. 100'u64]
+  SemiOutOfBounds* = range[0x7ffffffffffffe00'u64 .. 0x8000000000000100'u64]
+  FullOutOfBounds* = range[0x8000000000000000'u64 .. 0x8000000000000200'u64]
+
+  FullNegativeRange* = range[-200 .. -100]
+  HalfNegativeRange* = range[-50 .. 50]
+  FullPositiveRange* = range[100 .. 200]
+
+let acceptA* = int32(0x7fffffff'i64)
+let acceptB* = (uint64(0'i64))
+let acceptD* = (HalfNegativeRange(25'u64))
+let acceptE* = (UnsignedRange(50'u64))
+let acceptF* = (SemiOutOfBounds(0x7ffffffffffffe00'i64))
+let acceptH* = (SemiOutOfBounds(0x8000000000000000'u64))
+
+let rejectA* = int32(0x80000000'i64)
+let rejectB* = (uint64(-1'i64))
+let rejectC* = (FullNegativeRange(0xff'u32))
+let rejectD* = (HalfNegativeRange(0xffffffffffffffff'u64)) # internal `intVal` is `-1` which would be in range.
+let rejectE* = (FullPositiveRange(300'u64))
+let rejectF* = (UnsignedRange(101'u64))
+let rejectG* = (SemiOutOfBounds(0x8000000000000000'i64))  #
+
+let rejectH* = (int32(NaN))
+let rejectI* = (int64(1e100))
+let rejectJ* = (uint64(1e100))
+
+# removed cross checks from tarithm.nim
+let rejectK* = (int64(0xFFFFFFFFFFFFFFFF'u64))
+let rejectL* = (int32(0xFFFFFFFFFFFFFFFF'u64))
+let rejectM* = (int16(0xFFFFFFFFFFFFFFFF'u64))
+let rejectN* = (int8(0xFFFFFFFFFFFFFFFF'u64))
diff --git a/tests/range/tenums.nim b/tests/range/tenums.nim
new file mode 100644
index 000000000..3cdf06fe2
--- /dev/null
+++ b/tests/range/tenums.nim
@@ -0,0 +1,33 @@
+discard """
+  cmd: "nim check --hints:off $file"
+  errormsg: "type mismatch: got <BC>"
+  nimout: '''
+tenums.nim(32, 20) Error: type mismatch: got <Letters>
+but expected one of:
+proc takesChristmasColor(color: ChristmasColors)
+  first type mismatch at position: 1
+  required type for color: ChristmasColors
+  but expression 'A' is of type: Letters
+
+expression: takesChristmasColor(A)
+tenums.nim(33, 20) Error: type mismatch: got <BC>
+but expected one of:
+proc takesChristmasColor(color: ChristmasColors)
+  first type mismatch at position: 1
+  required type for color: ChristmasColors
+  but expression 'BC(C)' is of type: BC
+
+expression: takesChristmasColor(BC(C))
+'''
+"""
+
+type
+  Colors = enum Red, Green, Blue
+  ChristmasColors = range[Red .. Green]
+  Letters = enum A, B, C
+  BC = range[B .. C]
+
+proc takesChristmasColor(color: ChristmasColors) = discard
+takesChristmasColor(Green)
+takesChristmasColor(A)
+takesChristmasColor(BC(C))
diff --git a/tests/range/texplicitvarconv.nim b/tests/range/texplicitvarconv.nim
new file mode 100644
index 000000000..8da8a8878
--- /dev/null
+++ b/tests/range/texplicitvarconv.nim
@@ -0,0 +1,13 @@
+# related to issue #24032
+
+proc `++`(n: var int) =
+    n += 1
+
+type
+    r = range[ 0..15 ]
+
+var a: r = 14
+
+++int(a) # this should be mutable
+
+doAssert a == 15
diff --git a/tests/range/tmatrix3.nim b/tests/range/tmatrix3.nim
deleted file mode 100644
index fe666b0d8..000000000
--- a/tests/range/tmatrix3.nim
+++ /dev/null
@@ -1,41 +0,0 @@
-discard """
-  output: '''0.0
-0.0
-0
-0
-0
-'''
-"""
-
-include compilehelpers
-
-type
-  Matrix*[M, N, T] = object
-    aij*: array[M, array[N, T]]
-
-  Matrix2*[T] = Matrix[range[0..1], range[0..1], T]
-
-  Matrix3*[T] = Matrix[range[0..2], range[0..2], T]
-
-proc mn(x: Matrix): Matrix.T = x.aij[0][0]
-
-proc m2(x: Matrix2): Matrix2.T = x.aij[0][0]
-
-proc m3(x: Matrix3): auto = x.aij[0][0]
-
-var
-  matn: Matrix[range[0..3], range[0..2], int]
-  mat2: Matrix2[int]
-  mat3: Matrix3[float]
-
-echo m3(mat3)
-echo mn(mat3)
-echo m2(mat2)
-echo mn(mat2)
-echo mn(matn)
-
-reject m3(mat2)
-reject m3(matn)
-reject m2(mat3)
-reject m2(matn)
-
diff --git a/tests/range/tn8vsint16.nim b/tests/range/tn8vsint16.nim
deleted file mode 100644
index bf4f78e3b..000000000
--- a/tests/range/tn8vsint16.nim
+++ /dev/null
@@ -1,18 +0,0 @@
-discard """
-  output: '''-9'''
-"""
-
-type
-  n32 = range[0..high(int)]
-  n8* = range[0'i8..high(int8)]
-
-proc `+`*(a: n32, b: n32{nkIntLit}): n32 = discard
-
-proc `-`*(a: n8, b: n8): n8 = n8(system.`-`(a, b))
-
-var x, y: n8
-var z: int16
-
-# ensure this doesn't call our '-' but system.`-` for int16:
-echo z - n8(9)
-
diff --git a/tests/range/toutofrangevarconv.nim b/tests/range/toutofrangevarconv.nim
new file mode 100644
index 000000000..1ee4d340e
--- /dev/null
+++ b/tests/range/toutofrangevarconv.nim
@@ -0,0 +1,14 @@
+discard """
+  outputsub: "value out of range: 5 notin 0 .. 3 [RangeDefect]"
+  exitcode: "1"
+"""
+
+# make sure out of bounds range conversion is detected for `var` conversions
+
+type R = range[0..3]
+
+proc foo(x: var R) =
+  doAssert x in 0..3
+
+var x = 5
+foo(R(x))
diff --git a/tests/range/trange.nim b/tests/range/trange.nim
new file mode 100644
index 000000000..abfa7d474
--- /dev/null
+++ b/tests/range/trange.nim
@@ -0,0 +1,156 @@
+discard """
+  output: '''
+TSubRange: 5 from 1 to 10
+#FF3722
+'''
+"""
+
+
+block tbug499771:
+  type
+    TSubRange = range[1 .. 10]
+    TEnum = enum A, B, C
+  var sr: TSubRange = 5
+  echo("TSubRange: " & $sr & " from " & $low(TSubRange) & " to " &
+       $high(TSubRange))
+
+  const cset = {A} + {B}
+  doAssert A in cset
+  doAssert B in cset
+  doAssert C notin cset
+
+
+
+include compilehelpers
+block tmatrix3:
+  type
+    Matrix[M, N, T] = object
+      aij: array[M, array[N, T]]
+
+    Matrix2[T] = Matrix[range[0..1], range[0..1], T]
+
+    Matrix3[T] = Matrix[range[0..2], range[0..2], T]
+
+  proc mn(x: Matrix): Matrix.T = x.aij[0][0]
+
+  proc m2(x: Matrix2): Matrix2.T = x.aij[0][0]
+
+  proc m3(x: Matrix3): auto = x.aij[0][0]
+
+  var
+    matn: Matrix[range[0..3], range[0..2], int]
+    mat2: Matrix2[int]
+    mat3: Matrix3[float]
+
+  doAssert m3(mat3) == 0.0
+  doAssert mn(mat3) == 0.0
+  doAssert m2(mat2) == 0
+  doAssert mn(mat2) == 0
+  doAssert mn(matn) == 0
+
+  reject m3(mat2)
+  reject m3(matn)
+  reject m2(mat3)
+  reject m2(matn)
+
+
+
+block tn8vsint16:
+  type
+    n32 = range[0..high(int)]
+    n8 = range[0'i8..high(int8)]
+
+  proc `+`(a: n32, b: n32{nkIntLit}): n32 = discard
+
+  proc `-`(a: n8, b: n8): n8 = n8(system.`-`(a, b))
+
+  var x, y: n8
+  var z: int16
+
+  # ensure this doesn't call our '-' but system.`-` for int16:
+  doAssert z - n8(9) == -9
+
+
+
+import strutils
+block tcolors:
+  type TColor = distinct uint32
+
+  proc rgb(r, g, b: range[0..255]): TColor =
+    result = TColor(r or g shl 8 or b shl 16)
+  proc `$`(c: TColor): string =
+    result = "#" & toHex(uint32(c), 6)
+  echo rgb(34, 55, 255)
+
+  block:
+    type
+      TColor = distinct uint32
+      TColorComponent = distinct uint8
+
+    proc red(a: TColor): TColorComponent =
+      result = TColorComponent(uint32(a) and 0xff'u32)
+    proc green(a: TColor): TColorComponent =
+      result = TColorComponent(uint32(a) shr 8'u32 and 0xff'u32)
+    proc blue(a: TColor): TColorComponent =
+      result = TColorComponent(uint32(a) shr 16'u32 and 0xff'u32)
+    proc rgb(r, g, b: range[0..255]): TColor =
+      result = TColor(r or g shl 8 or b shl 8)
+
+    proc `+!` (a, b: TColorComponent): TColorComponent =
+      ## saturated arithmetic:
+      result = TColorComponent(min(int(uint8(a)) + int(uint8(b)), 255))
+
+    proc `+` (a, b: TColor): TColor =
+      ## saturated arithmetic for colors makes sense, I think:
+      return rgb(int(red(a) +! red(b)), int(green(a) +! green(b)), int(blue(a) +! blue(b)))
+
+    discard rgb(34, 55, 255)
+
+block:
+  type
+    R8  = range[0'u8 .. 10'u8]
+    R16 = range[0'u16 .. 10'u16]
+    R32 = range[0'u32 .. 10'u32]
+
+  var
+    x1 = R8(4)
+    x2 = R16(4)
+    x3 = R32(4)
+
+  doAssert $x1 & $x2 & $x3 == "444"
+
+block:
+  var x1: range[0'f..1'f] = 1
+  const x2: range[0'f..1'f] = 1
+  var x3: range[0'u8..1'u8] = 1
+  const x4: range[0'u8..1'u8] = 1
+
+  var x5: range[0'f32..1'f32] = 1'f64
+  const x6: range[0'f32..1'f32] = 1'f64
+
+  reject:
+    var x09: range[0'i8..1'i8] = 1.int
+  reject:
+    var x10: range[0'i64..1'i64] = 1'u64
+
+    const x11: range[0'f..1'f] = 2'f
+  reject:
+    const x12: range[0'f..1'f] = 2
+
+# ensure unsigned array indexing is remains lenient:
+var a: array[4'u, string]
+
+for i in 0..<a.len:
+  a[i] = "foo"
+
+# Check range to ordinal conversions
+block:
+  var
+    a: int16
+    b: range[0'i32..45'i32] = 3
+    c: uint16
+    d: range[0'u32..46'u32] = 3
+  a = b
+  c = d
+  doAssert a == b
+  doAssert c == d
diff --git a/tests/range/tsubrange.nim b/tests/range/tsubrange.nim
index 914e7c6e7..f778c55eb 100644
--- a/tests/range/tsubrange.nim
+++ b/tests/range/tsubrange.nim
@@ -1,6 +1,6 @@
 discard """
-  line: 20
   errormsg: "cannot convert 60 to TRange"
+  line: 20
 """
 
 type
@@ -18,4 +18,3 @@ p y
 
 const
   myConst: TRange = 60
-
diff --git a/tests/range/tsubrange2.nim b/tests/range/tsubrange2.nim
index 7097faed2..fbd1ca760 100644
--- a/tests/range/tsubrange2.nim
+++ b/tests/range/tsubrange2.nim
@@ -1,6 +1,5 @@
 discard """
-  file: "tsubrange2.nim"
-  outputsub: "value out of range: 50 [RangeError]"
+  outputsub: "value out of range: 50 notin 0 .. 40 [RangeDefect]"
   exitcode: "1"
 """
 
@@ -14,4 +13,3 @@ var
   r: TRange
   y = 50
 p y
-
diff --git a/tests/range/tsubrange3.nim b/tests/range/tsubrange3.nim
index f5bb2f161..55eb4618b 100644
--- a/tests/range/tsubrange3.nim
+++ b/tests/range/tsubrange3.nim
@@ -1,6 +1,5 @@
 discard """
-  file: "tsubrange.nim"
-  outputsub: "value out of range: 50 [RangeError]"
+  outputsub: "value out of range: 50 notin 0 .. 40 [RangeDefect]"
   exitcode: "1"
 """
 
diff --git a/tests/readme.md b/tests/readme.md
index 34a2c4bfb..f638ddc10 100644
--- a/tests/readme.md
+++ b/tests/readme.md
@@ -2,21 +2,20 @@ This directory contains the test cases.
 
 Each test must have a filename of the form: ``t*.nim``
 
-**Note:** Tests are only compiled by default. In order to get the tester to
-execute the compiled binary, you need to specify a spec with an ``action`` key
-(see below for details).
+**Note:** [Testament](https://nim-lang.github.io/Nim/testament.html) is only aware of tests under a directory (eg `tests/foo/`) and will ignore
+top-level tests like `tests/tbar.nim`.
 
 # Specs
 
 Each test can contain a spec in a ``discard """ ... """`` block.
 
-**Check out the [``parseSpec`` procedure](https://github.com/nim-lang/Nim/blob/devel/tests/testament/specs.nim#L124) in the ``specs`` module for a full and reliable reference**
+**Check out the [``parseSpec`` procedure](https://github.com/nim-lang/Nim/blob/devel/testament/specs.nim#L315) in the ``specs`` module for a full and reliable reference**
 
 ## action
 
 Specifies what action this test should take.
 
-**Default: compile**
+**Default: run**
 
 Options:
 
@@ -28,35 +27,13 @@ Options:
 There are certain spec keys that imply ``run``, including ``output`` and
 ``outputsub``.
 
-## cmd
-
-Specifies the Nim command to use for compiling the test.
-
-There are a number of variables that are replaced in this spec option:
-
-* ``$target`` - the compilation target, e.g. ``c``.
-* ``$options`` - the options for the compiler.
-* ``$file`` - the filename of the test.
-* ``$filedir`` - the directory of the test file.
-
-Example:
-
-```nim
-discard """
-  cmd: "nim $target --nimblePath:./nimbleDir/simplePkgs $options $file"
-"""
-```
-
 # Categories
 
 Each folder under this directory represents a test category, which can be
-tested by running `koch tests cat <category>`.
-
-The folder ``rodfiles`` contains special tests that test incremental
-compilation via symbol files.
+tested by running `koch tests pcat <category>` (or `cat` to avoid parallel
+testing, which is slower).
 
 The folder ``dll`` contains simple DLL tests.
 
 The folder ``realtimeGC`` contains a test for validating that the realtime GC
-can run properly without linking against the nimrtl.dll/so. It includes a C
-client and platform specific build files for manual compilation.
+can run properly without linking against the nimrtl.dll/so.
diff --git a/tests/realtimeGC/cmain.c b/tests/realtimeGC/cmain.c
deleted file mode 100644
index 0d4bb096a..000000000
--- a/tests/realtimeGC/cmain.c
+++ /dev/null
@@ -1,67 +0,0 @@
-#ifdef WIN
-#include <windows.h>
-#else
-#include <dlfcn.h>
-#include <unistd.h> /* for sleep(3) */
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <time.h>
-
-#define RUNTIME (15*60)
-
-
-typedef void (*pFunc)(void);
-
-int main(int argc, char* argv[])
-{
-    int i;
-    void* hndl;
-    pFunc status;
-    pFunc count;
-    pFunc checkOccupiedMem;
-
-#ifdef WIN
-    hndl = (void*) LoadLibrary((char const*)"./tests/realtimeGC/shared.dll");
-    status = (pFunc)GetProcAddress((HMODULE) hndl, (char const*)"status");
-    count = (pFunc)GetProcAddress((HMODULE) hndl, (char const*)"count");
-    checkOccupiedMem = (pFunc)GetProcAddress((HMODULE) hndl, (char const*)"checkOccupiedMem");
-#else /* OSX || NIX */
-    hndl = (void*) dlopen((char const*)"./tests/realtimeGC/libshared.so", RTLD_LAZY);
-    status = (pFunc) dlsym(hndl, (char const*)"status");
-    count = (pFunc) dlsym(hndl, (char const*)"count");
-    checkOccupiedMem = (pFunc) dlsym(hndl, (char const*)"checkOccupiedMem");
-#endif
-
-    assert(hndl);
-    assert(status);
-    assert(count);
-    assert(checkOccupiedMem);
-
-    time_t startTime = time((time_t*)0);
-    time_t runTime = (time_t)(RUNTIME);
-    time_t accumTime = 0;
-    while (accumTime < runTime) {
-        for (i = 0; i < 10; i++)
-            count();
-        /* printf("1. sleeping...\n"); */
-        sleep(1);
-        for (i = 0; i < 10; i++)
-            status();
-        /* printf("2. sleeping...\n"); */
-        sleep(1);
-        checkOccupiedMem();
-        accumTime = time((time_t*)0) - startTime;
-        /* printf("--- Minutes left to run: %d\n", (int)(runTime-accumTime)/60); */
-    }
-    printf("Cleaning up the shared object pointer...\n");
-#ifdef WIN
-    FreeLibrary((HMODULE)hndl);
-#else /* OSX || NIX */
-    dlclose(hndl);
-#endif
-    printf("Done\n");
-    return 0;
-}
diff --git a/tests/realtimeGC/main.nim.cfg b/tests/realtimeGC/main.nim.cfg
deleted file mode 100644
index fed4fa471..000000000
--- a/tests/realtimeGC/main.nim.cfg
+++ /dev/null
@@ -1,6 +0,0 @@
-
---app:console
---threads:on
-
--d:release
--d:useRealtimeGC
diff --git a/tests/realtimeGC/nmain.nim b/tests/realtimeGC/nmain.nim
deleted file mode 100644
index c9f558dbc..000000000
--- a/tests/realtimeGC/nmain.nim
+++ /dev/null
@@ -1,46 +0,0 @@
-discard """
-  cmd: "nim $target --debuginfo $options $file"
-  output: "Done"
-"""
-
-import times, os, threadpool
-
-const RUNTIME = 15 * 60 # 15 minutes
-
-when defined(windows):
-  const dllname = "./tests/realtimeGC/shared.dll"
-elif defined(macosx):
-  const dllname = "./tests/realtimeGC/libshared.dylib"
-else:
-  const dllname = "./tests/realtimeGC/libshared.so"
-
-proc status() {.importc: "status", dynlib: dllname.}
-proc count() {.importc: "count", dynlib: dllname.}
-proc checkOccupiedMem() {.importc: "checkOccupiedMem", dynlib: dllname.}
-
-proc process() =
-  let startTime = getTime()
-  let runTime = cast[Time](RUNTIME) #
-  var accumTime: Time
-  while accumTime < runTime:
-    for i in 0..10:
-      count()
-    # echo("1. sleeping... ")
-    sleep(500)
-    for i in 0..10:
-      status()
-    # echo("2. sleeping... ")
-    sleep(500)
-    checkOccupiedMem()
-    accumTime = cast[Time]((getTime() - startTime))
-    # echo("--- Minutes left to run: ", int(int(runTime-accumTime)/60))
-
-proc main() =
-  process()
-  # parallel:
-  #   for i in 0..0:
-  #     spawn process()
-  # sync()
-  echo("Done")
-
-main()
diff --git a/tests/realtimeGC/readme.txt b/tests/realtimeGC/readme.txt
deleted file mode 100644
index b2e37a1f0..000000000
--- a/tests/realtimeGC/readme.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-Test the realtime GC without linking nimrtl.dll/so.
-
-Note, this is a long running test, default is 35 minutes. To change the
-the run time see RUNTIME in nmain.nim and cmain.c.
-
-You can build shared.nim, nmain.nim, and cmain.c by running make (nix systems)
-or make.bat (Windows systems). They both assume GCC and that it's in your
-path. Output: shared.(dll/so), cmain(.exe), nmain(.exe).
-
-To run the test: execute either nmain or cmain in a shell window.
-
-To build by hand:
-
-  - build the shared object (shared.nim):
-
-    $ nim c tests/realtimeGC/shared.nim
-
-  - build the client executables:
-
-    $ nim c --threads:on tests/realtimeGC/nmain.nim
-    $ gcc -o tests/realtimeGC/cmain tests/realtimeGC/cmain.c -ldl
diff --git a/tests/realtimeGC/shared.nim b/tests/realtimeGC/shared.nim
index 2d1dd6c3c..8b5c05e47 100644
--- a/tests/realtimeGC/shared.nim
+++ b/tests/realtimeGC/shared.nim
@@ -1,7 +1,3 @@
-discard """
-  cmd: "nim $target --debuginfo --hints:on --app:lib $options $file"
-"""
-
 import strutils
 
 # Global state, accessing with threads, no locks. Don't do this at
diff --git a/tests/realtimeGC/shared.nim.cfg b/tests/realtimeGC/shared.nim.cfg
deleted file mode 100644
index e153b26fa..000000000
--- a/tests/realtimeGC/shared.nim.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
---app:lib
---threads:on
-
--d:release
--d:useRealtimeGC
diff --git a/tests/realtimeGC/tmain.nim b/tests/realtimeGC/tmain.nim
new file mode 100644
index 000000000..ca0177e3d
--- /dev/null
+++ b/tests/realtimeGC/tmain.nim
@@ -0,0 +1,62 @@
+discard """
+  cmd: "nim $target --threads:on -d:release -d:useRealtimeGC $options $file"
+  joinable:false
+"""
+
+#[
+was: cmd: "nim $target --debuginfo $options $file"
+these dont' seem needed --debuginfo
+nor these from the previous main.nim.cfg: --app:console
+]#
+
+#[
+Test the realtime GC without linking nimrtl.dll/so.
+
+To build by hand and run the test for 35 minutes:
+`nim r --threads:on -d:runtimeSecs:2100 tests/realtimeGC/tmain.nim`
+]#
+
+import times, os, strformat, strutils
+from stdtest/specialpaths import buildDir
+# import threadpool
+
+const runtimeSecs {.intdefine.} = 5
+
+const file = "shared.nim"
+const dllname = buildDir / (DynlibFormat % "shared_D20210524T180506")
+
+static:
+  # D20210524T180826:here we compile the dependency on the fly
+  let nim = getCurrentCompilerExe()
+  let (output, exitCode) = gorgeEx(fmt"{nim} c -o:{dllname} --debuginfo --app:lib --threads:on -d:release -d:useRealtimeGC {file}")
+  doAssert exitCode == 0, output
+
+proc status() {.importc: "status", dynlib: dllname.}
+proc count() {.importc: "count", dynlib: dllname.}
+proc checkOccupiedMem() {.importc: "checkOccupiedMem", dynlib: dllname.}
+
+proc process() =
+  let startTime = getTime()
+  let runTime = cast[Time](runtimeSecs)
+  var accumTime: Time
+  while accumTime < runTime:
+    for i in 0..10:
+      count()
+    # echo("1. sleeping... ")
+    sleep(500)
+    for i in 0..10:
+      status()
+    # echo("2. sleeping... ")
+    sleep(500)
+    checkOccupiedMem()
+    accumTime = cast[Time]((getTime() - startTime))
+    # echo("--- Minutes left to run: ", int(int(runTime-accumTime)/60))
+
+proc main() =
+  process()
+  # parallel:
+  #   for i in 0..0:
+  #     spawn process()
+  # sync()
+
+main()
diff --git a/tests/refc/tsinkbug.nim b/tests/refc/tsinkbug.nim
new file mode 100644
index 000000000..de2ec98a5
--- /dev/null
+++ b/tests/refc/tsinkbug.nim
@@ -0,0 +1,26 @@
+discard """
+  matrix: "--gc:refc; --gc:arc"
+  output: '''
+Value is: 42
+Value is: 42'''
+"""
+
+type AnObject* = object of RootObj
+  value*: int
+
+proc mutate(a: sink AnObject) =
+  a.value = 1
+
+var obj = AnObject(value: 42)
+echo "Value is: ", obj.value
+mutate(obj)
+echo "Value is: ", obj.value
+
+proc p(x: sink string) = 
+  var y = move(x)
+  doAssert x.len == 0
+  doAssert y.len == 4
+
+p("1234")
+var s = "oooo"
+p(s)
diff --git a/tests/rodfiles/aconv.nim b/tests/rodfiles/aconv.nim
deleted file mode 100644
index ffd8f3648..000000000
--- a/tests/rodfiles/aconv.nim
+++ /dev/null
@@ -1,9 +0,0 @@
-discard """
-  output: "ugly conversion successful"
-"""
-
-import int2bool
-
-if 4:
-  echo "ugly conversion successful"
-
diff --git a/tests/rodfiles/amethods.nim b/tests/rodfiles/amethods.nim
deleted file mode 100644
index 29cf757f7..000000000
--- a/tests/rodfiles/amethods.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-
-type
-  TBaseClass* = object of RootObj
-
-proc newBaseClass*: ref TBaseClass =
-  new result
-
-method echoType*(x: ref TBaseClass) {.base.} =
-  echo "base class"
-
-proc echoAlias*(x: ref TBaseClass) =
-  echoType x
-
diff --git a/tests/rodfiles/bconv.nim b/tests/rodfiles/bconv.nim
deleted file mode 100644
index 2289a7f97..000000000
--- a/tests/rodfiles/bconv.nim
+++ /dev/null
@@ -1,9 +0,0 @@
-discard """
-  output: "ugly conversion successful 2"
-"""
-
-import int2bool
-
-if 4:
-  echo "ugly conversion successful 2"
-
diff --git a/tests/rodfiles/bmethods.nim b/tests/rodfiles/bmethods.nim
deleted file mode 100644
index c77941e4a..000000000
--- a/tests/rodfiles/bmethods.nim
+++ /dev/null
@@ -1,29 +0,0 @@
-discard """
-  output: '''derived class
-base class
-'''
-"""
-
-import amethods
-
-
-type
-  TDerivedClass* = object of TBaseClass
-
-proc newDerivedClass: ref TDerivedClass =
-  new result
-
-method echoType*(x: ref TDerivedClass) =
-  echo "derived class"
-
-var b, d: ref TBaseClass
-
-b = newBaseClass()
-d = newDerivedClass()
-
-#b.echoType()
-#d.echoType()
-
-echoAlias d
-echoAlias b
-
diff --git a/tests/rodfiles/bmethods2.nim b/tests/rodfiles/bmethods2.nim
deleted file mode 100644
index c9d25eee4..000000000
--- a/tests/rodfiles/bmethods2.nim
+++ /dev/null
@@ -1,29 +0,0 @@
-discard """
-  output: '''derived class 2
-base class
-'''
-"""
-
-import amethods
-
-
-type
-  TDerivedClass* = object of TBaseClass
-
-proc newDerivedClass: ref TDerivedClass =
-  new result
-
-method echoType*(x: ref TDerivedClass) =
-  echo "derived class 2"
-
-var b, d: ref TBaseClass
-
-b = newBaseClass()
-d = newDerivedClass()
-
-#b.echoType()
-#d.echoType()
-
-echoAlias d
-echoAlias b
-
diff --git a/tests/rodfiles/deada.nim b/tests/rodfiles/deada.nim
deleted file mode 100644
index 3fa4192f8..000000000
--- a/tests/rodfiles/deada.nim
+++ /dev/null
@@ -1,8 +0,0 @@
-discard """
-  output: '''246
-'''
-"""
-
-import deadg, deadb
-
-
diff --git a/tests/rodfiles/deada2.nim b/tests/rodfiles/deada2.nim
deleted file mode 100644
index 2925b4d43..000000000
--- a/tests/rodfiles/deada2.nim
+++ /dev/null
@@ -1,12 +0,0 @@
-discard """
-  output: '''246
-xyzabc
-'''
-"""
-
-import deadg, deadb
-
-# now add call to previously unused proc p2:
-echo p2("xyz", "abc")
-
-
diff --git a/tests/rodfiles/deadb.nim b/tests/rodfiles/deadb.nim
deleted file mode 100644
index 776a07ac7..000000000
--- a/tests/rodfiles/deadb.nim
+++ /dev/null
@@ -1,7 +0,0 @@
-
-import deadg
-
-
-echo p1(123, 123)
-
-
diff --git a/tests/rodfiles/deadg.nim b/tests/rodfiles/deadg.nim
deleted file mode 100644
index 0aee59bb8..000000000
--- a/tests/rodfiles/deadg.nim
+++ /dev/null
@@ -1,7 +0,0 @@
-proc p1*(x, y: int): int =
-  result = x + y
-
-proc p2*(x, y: string): string =
-  result = x & y
-
-
diff --git a/tests/rodfiles/gtkex1.nim b/tests/rodfiles/gtkex1.nim
deleted file mode 100644
index 50779cb9e..000000000
--- a/tests/rodfiles/gtkex1.nim
+++ /dev/null
@@ -1,14 +0,0 @@
-import
-  cairo, glib2, gtk2
-
-proc destroy(widget: PWidget, data: Pgpointer) {.cdecl.} =
-  main_quit()
-
-var
-  window: PWidget
-nim_init()
-window = window_new(WINDOW_TOPLEVEL)
-discard signal_connect(window, "destroy",
-                       SIGNAL_FUNC(gtkex1.destroy), nil)
-show(window)
-main()
diff --git a/tests/rodfiles/gtkex2.nim b/tests/rodfiles/gtkex2.nim
deleted file mode 100644
index 0949e4872..000000000
--- a/tests/rodfiles/gtkex2.nim
+++ /dev/null
@@ -1,22 +0,0 @@
-
-import
-  glib2, gtk2
-
-proc destroy(widget: PWidget, data: Pgpointer){.cdecl.} =
-  main_quit()
-
-var
-  window: PWidget
-  button: PWidget
-
-nim_init()
-window = window_new(WINDOW_TOPLEVEL)
-button = button_new("Click me")
-set_border_width(PContainer(window), 5)
-add(PContainer(window), button)
-discard signal_connect(window, "destroy",
-                           SIGNAL_FUNC(gtkex2.destroy), nil)
-show(button)
-show(window)
-main()
-
diff --git a/tests/rodfiles/hallo.nim b/tests/rodfiles/hallo.nim
deleted file mode 100644
index ac45be9fa..000000000
--- a/tests/rodfiles/hallo.nim
+++ /dev/null
@@ -1,6 +0,0 @@
-discard """
-  output: "Hello World"
-"""
-
-echo "Hello World"
-
diff --git a/tests/rodfiles/hallo2.nim b/tests/rodfiles/hallo2.nim
deleted file mode 100644
index 40fe64cfd..000000000
--- a/tests/rodfiles/hallo2.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-discard """
-  output: "Hello World"
-"""
-
-# Test incremental type information
-
-type
-  TNode = object {.pure.}
-    le, ri: ref TNode
-    data: string
-
-proc newNode(data: string): ref TNode =
-  new(result)
-  result.data = data
-
-echo newNode("Hello World").data
-
diff --git a/tests/rodfiles/int2bool.nim b/tests/rodfiles/int2bool.nim
deleted file mode 100644
index bb0682844..000000000
--- a/tests/rodfiles/int2bool.nim
+++ /dev/null
@@ -1,7 +0,0 @@
-
-{.overflowchecks: on.}
-
-converter uglyToBool*(x: int): bool =
-  result = x != 0
-
-
diff --git a/tests/rodfiles/nim.cfg b/tests/rodfiles/nim.cfg
deleted file mode 100644
index 78fc8db64..000000000
--- a/tests/rodfiles/nim.cfg
+++ /dev/null
@@ -1,2 +0,0 @@
---nimcache:"$projectPath/nimcache"
---symbolFiles:on
diff --git a/tests/rodfiles/tgeneric1.nim b/tests/rodfiles/tgeneric1.nim
deleted file mode 100644
index a3f7b870b..000000000
--- a/tests/rodfiles/tgeneric1.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-discard """
-  output: "abcd"
-"""
-
-import tables
-
-var x = initTable[int, string]()
-
-x[2] = "ab"
-x[5] = "cd"
-
-echo x[2], x[5]
-
diff --git a/tests/rodfiles/tgeneric2.nim b/tests/rodfiles/tgeneric2.nim
deleted file mode 100644
index 552d60267..000000000
--- a/tests/rodfiles/tgeneric2.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-discard """
-  output: "abef"
-"""
-
-import tables
-
-var x = initTable[int, string]()
-
-x[2] = "ab"
-x[5] = "ef"
-
-echo x[2], x[5]
-
diff --git a/tests/sandwich/generic_library.nim b/tests/sandwich/generic_library.nim
new file mode 100644
index 000000000..43e7bd65f
--- /dev/null
+++ b/tests/sandwich/generic_library.nim
@@ -0,0 +1,6 @@
+
+proc libraryFunc*[T](x: T) =
+  mixin mixedIn, indirectlyMixedIn
+  echo mixedIn()
+  echo indirectlyMixedIn()
+
diff --git a/tests/sandwich/helper_module.nim b/tests/sandwich/helper_module.nim
new file mode 100644
index 000000000..d003bf044
--- /dev/null
+++ b/tests/sandwich/helper_module.nim
@@ -0,0 +1,3 @@
+
+proc indirectlyMixedIn*: int =
+  200
diff --git a/tests/sandwich/module_using_generic_library.nim b/tests/sandwich/module_using_generic_library.nim
new file mode 100644
index 000000000..bbb0d92a4
--- /dev/null
+++ b/tests/sandwich/module_using_generic_library.nim
@@ -0,0 +1,12 @@
+
+import
+  generic_library, helper_module
+
+proc mixedIn: int = 100
+
+proc makeUseOfLibrary*[T](x: T) =
+  bind mixedIn, indirectlyMixedIn
+  libraryFunc(x)
+
+when isMainModule:
+  makeUseOfLibrary "test"
diff --git a/tests/sandwich/tmain.nim b/tests/sandwich/tmain.nim
new file mode 100644
index 000000000..aa50bfb04
--- /dev/null
+++ b/tests/sandwich/tmain.nim
@@ -0,0 +1,9 @@
+discard """
+  output: '''100
+200'''
+"""
+
+import
+  module_using_generic_library
+
+makeUseOfLibrary "test"
diff --git a/tests/seq/tseq2.nim b/tests/seq/tseq2.nim
deleted file mode 100644
index 5de9402ec..000000000
--- a/tests/seq/tseq2.nim
+++ /dev/null
@@ -1,11 +0,0 @@
-proc `*` *(a, b: seq[int]): seq[int] =
-  # allocate a new sequence:
-  newSeq(result, len(a))
-  # multiply two int sequences:
-  for i in 0..len(a)-1: result[i] = a[i] * b[i]
-
-when isMainModule:
-  # test the new ``*`` operator for sequences:
-  assert(@[1, 2, 3] * @[1, 2, 3] == @[1, 4, 9])
-
-
diff --git a/tests/seq/tseqcon.nim b/tests/seq/tseqcon.nim
deleted file mode 100644
index 902ac3485..000000000
--- a/tests/seq/tseqcon.nim
+++ /dev/null
@@ -1,51 +0,0 @@
-discard """
-  file: "tseqcon.nim"
-  output: "Hithere, what\'s your name?Hathere, what\'s your name?"
-"""
-# Test the add proc for sequences and strings
-
-const
-  nestedFixed = true
-
-type
-  TRec {.final.} = object
-    x, y: int
-    s: string
-    seq: seq[string]
-  TRecSeq = seq[TRec]
-
-proc test() =
-  var s, b: seq[string]
-  s = @[]
-  add(s, "Hi")
-  add(s, "there, ")
-  add(s, "what's your name?")
-
-  b = s # deep copying here!
-  b[0][1] = 'a'
-
-  for i in 0 .. len(s)-1:
-    write(stdout, s[i])
-  for i in 0 .. len(b)-1:
-    write(stdout, b[i])
-
-
-when nestedFixed:
-  proc nested() =
-    var
-      s: seq[seq[string]]
-    for i in 0..10_000: # test if the garbage collector
-      # now works with sequences
-      s = @[
-        @["A", "B", "C", "D"],
-        @["E", "F", "G", "H"],
-        @["I", "J", "K", "L"],
-        @["M", "N", "O", "P"]]
-
-test()
-when nestedFixed:
-  nested()
-
-#OUT Hithere, what's your name?Hathere, what's your name?
-
-
diff --git a/tests/seq/tseqcon2.nim b/tests/seq/tseqcon2.nim
deleted file mode 100644
index 4f2763ffe..000000000
--- a/tests/seq/tseqcon2.nim
+++ /dev/null
@@ -1,9 +0,0 @@
-import os
-
-proc rec_dir(dir: string): seq[string] =
-  result = @[]
-  for kind, path in walk_dir(dir):
-    if kind == pcDir:
-      add(result, rec_dir(path))
-    else:
-      add(result, path)
diff --git a/tests/seq/tseqtuple.nim b/tests/seq/tseqtuple.nim
deleted file mode 100644
index a0102c9ef..000000000
--- a/tests/seq/tseqtuple.nim
+++ /dev/null
@@ -1,28 +0,0 @@
-discard """
-  file: "tseqtuple.nim"
-  output: "fA13msg1falsefB14msg2truefC15msg3false"
-"""
-
-type
-  TMsg = tuple[
-    file: string,
-    line: int,
-    msg: string,
-    err: bool]
-
-var s: seq[TMsg] = @[]
-
-s.add(("fA", 13, "msg1", false))
-s.add(("fB", 14, "msg2", true))
-s.add(("fC", 15, "msg3", false))
-
-for file, line, msg, err in items(s):
-  stdout.write(file)
-  stdout.write($line)
-  stdout.write(msg)
-  stdout.write($err)
-
-#OUT fA13msg1falsefB14msg2truefC15msg3false
-
-
-
diff --git a/tests/seq/tsequtils.nim b/tests/seq/tsequtils.nim
deleted file mode 100644
index 06a981e92..000000000
--- a/tests/seq/tsequtils.nim
+++ /dev/null
@@ -1,64 +0,0 @@
-discard """
-file: "tsequtils.nim"
-output: '''Zip: [{"Field0": 1, "Field1": 2}, {"Field0": 3, "Field1": 4}, {"Field0": 5, "Field1": 6}]
-Filter Iterator: 3
-Filter Iterator: 5
-Filter Iterator: 7
-Filter: [3, 5, 7]
-FilterIt: [1, 3, 7]
-Concat: [1, 3, 5, 7, 2, 4, 6]
-Deduplicate: [1, 2, 3, 4, 5, 7]
-@[()]'''
-
-"""
-
-import sequtils, marshal
-
-proc testFindWhere(item : int) : bool =
-  if item != 1: return true
-
-var seq1: seq[int] = @[]
-
-seq1.add(1)
-seq1.add(3)
-seq1.add(5)
-seq1.add(7)
-
-var seq2: seq[int] = @[2, 4, 6]
-var final = zip(seq1, seq2)
-
-echo "Zip: ", $$(final)
-
-#Test findWhere as a iterator
-
-for itms in filter(seq1, testFindWhere):
-  echo "Filter Iterator: ", $$(itms)
-
-
-#Test findWhere as a proc
-
-var fullseq: seq[int] = filter(seq1, testFindWhere)
-
-echo "Filter: ", $$(fullseq)
-
-#Test findIt as a template
-
-var finditval: seq[int] = filterIt(seq1, it!=5)
-
-echo "FilterIt: ", $$(finditval)
-
-var concatseq = concat(seq1,seq2)
-echo "Concat: ", $$(concatseq)
-
-var seq3 = @[1,2,3,4,5,5,5,7]
-var dedupseq = deduplicate(seq3)
-echo "Deduplicate: ", $$(dedupseq)
-# bug #4973
-type
-  SomeObj = object
-  OtherObj = object
-    field: SomeObj
-
-let aSeq = @[OtherObj(field: SomeObj())]
-let someObjSeq = aSeq.mapIt(it.field)
-echo someObjSeq
diff --git a/tests/seq/tshallowseq.nim b/tests/seq/tshallowseq.nim
deleted file mode 100644
index 9a8bdb954..000000000
--- a/tests/seq/tshallowseq.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-discard """
-  output: '''@[1, 42, 3]
-@[1, 42, 3]
-'''
-"""
-proc xxx() =
-  var x: seq[int] = @[1, 2, 3]
-  var y: seq[int]
-
-  system.shallowCopy(y, x)
-
-  y[1] = 42
-
-  echo y
-  echo x
-
-xxx()
diff --git a/tests/seq/ttoseq.nim b/tests/seq/ttoseq.nim
deleted file mode 100644
index 33de59538..000000000
--- a/tests/seq/ttoseq.nim
+++ /dev/null
@@ -1,18 +0,0 @@
-discard """
-  output: "2345623456"
-"""
-
-import sequtils
-
-for x in toSeq(countup(2, 6)):
-  stdout.write(x)
-for x in items(toSeq(countup(2, 6))):
-  stdout.write(x)
-
-import strutils
-
-var y: type("a b c".split)
-y = "xzy"
-
-
-
diff --git a/tests/sets/m17385.nim b/tests/sets/m17385.nim
new file mode 100644
index 000000000..3919e067a
--- /dev/null
+++ b/tests/sets/m17385.nim
@@ -0,0 +1,11 @@
+import std/sets
+
+type
+  Diff*[T] = object
+    data: T
+
+proc test*[T](diff: Diff[T]) =
+  var bPopular = initHashSet[T]()
+  for element in bPopular.items():
+    echo element
+
diff --git a/tests/sets/t13764.nim b/tests/sets/t13764.nim
new file mode 100644
index 000000000..1634f113d
--- /dev/null
+++ b/tests/sets/t13764.nim
@@ -0,0 +1,6 @@
+discard """
+errormsg: "conversion from int literal(1000000) to range 0..255(int) is invalid"
+line: 6
+"""
+
+let a = {1_000_000} # Compiles
diff --git a/tests/sets/t15435.nim b/tests/sets/t15435.nim
new file mode 100644
index 000000000..5ead7e641
--- /dev/null
+++ b/tests/sets/t15435.nim
@@ -0,0 +1,29 @@
+# bug #15435
+discard """
+errormsg: "type mismatch: got <set[uint8], set[range 1..5(uint8)]>"
+nimout: '''t15435.nim(28, 13) Error: type mismatch: got <set[uint8], set[range 1..5(uint8)]>
+but expected one of:
+proc `<`[T](x, y: set[T]): bool
+  first type mismatch at position: 2
+  required type for y: set[T]
+  but expression 'x' is of type: set[range 1..5(uint8)]
+20 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them
+
+expression: {1'u8, 5} < x'''
+"""
+
+
+
+
+
+
+## line 20
+var
+  x: set[range[1u8..5u8]]
+
+x.incl(1)
+x.incl(3)
+x.incl(5)
+
+if {1u8, 5} < x:
+  echo "successful"
diff --git a/tests/sets/t17385.nim b/tests/sets/t17385.nim
new file mode 100644
index 000000000..cc08b4882
--- /dev/null
+++ b/tests/sets/t17385.nim
@@ -0,0 +1,4 @@
+import m17385
+
+let a = Diff[int]()
+a.test()
diff --git a/tests/sets/t20997.nim b/tests/sets/t20997.nim
new file mode 100644
index 000000000..b320eee1a
--- /dev/null
+++ b/tests/sets/t20997.nim
@@ -0,0 +1,18 @@
+discard """
+  joinable: false
+"""
+
+{.passC: "-flto".}
+{.passL: "-flto".}
+
+template f(n: int) = discard card(default(set[range[0 .. (1 shl n) - 1]]))
+f( 7)
+f( 8)
+f( 9)
+f(10)
+f(11)
+f(12)
+f(13)
+f(14)
+f(15)
+f(16)
diff --git a/tests/sets/t2669.nim b/tests/sets/t2669.nim
new file mode 100644
index 000000000..0a92818fa
--- /dev/null
+++ b/tests/sets/t2669.nim
@@ -0,0 +1,6 @@
+discard """
+errormsg: "cannot convert 6 to range 1..5(int8)"
+line: 6
+"""
+
+var c: set[range[1i8..5i8]] = {1i8, 2i8, 6i8}
diff --git a/tests/sets/t5792.nim b/tests/sets/t5792.nim
new file mode 100644
index 000000000..297a1fc15
--- /dev/null
+++ b/tests/sets/t5792.nim
@@ -0,0 +1,17 @@
+discard """
+  matrix: "--gc:refc; --gc:arc"
+"""
+
+type
+  T = enum
+    a
+    b
+    c
+  U = object
+    case k: T
+    of a:
+      x: int
+    of {b, c} - {a}:
+      y: int
+
+doAssert U(k: b, y: 1).y == 1
diff --git a/tests/sets/thugeset.nim b/tests/sets/thugeset.nim
new file mode 100644
index 000000000..1d82ebede
--- /dev/null
+++ b/tests/sets/thugeset.nim
@@ -0,0 +1,10 @@
+let x = 20_000
+let s = {x, 123} #[tt.Warning
+        ^ type 'int' is too big to be a `set` element, assuming a range of 0..65535, explicitly write this range to get rid of warning [AboveMaxSizeSet]]#
+doAssert x in s
+doAssert 20_000 in s
+{.push warningAsError[AboveMaxSizeSet]: on.}
+let s2 = {range[0..65535](x), 123}
+doAssert x in s
+doAssert 20_000 in s
+{.pop.}
diff --git a/tests/misc/tnewsets.nim b/tests/sets/tnewsets.nim
index f239d4aa2..f239d4aa2 100644
--- a/tests/misc/tnewsets.nim
+++ b/tests/sets/tnewsets.nim
diff --git a/tests/sets/trangeincompatible.nim b/tests/sets/trangeincompatible.nim
new file mode 100644
index 000000000..554a50235
--- /dev/null
+++ b/tests/sets/trangeincompatible.nim
@@ -0,0 +1,32 @@
+block: # issue #20142
+  let
+    s1: set['a' .. 'g'] = {'a', 'e'}
+    s2: set['a' .. 'g'] = {'b', 'c', 'd', 'f'} # this works fine
+    s3 = {'b', 'c', 'd', 'f'}
+
+  doAssert s1 != s2
+  doAssert s1 == {range['a'..'g'] 'a', 'e'}
+  doAssert s2 == {range['a'..'g'] 'b', 'c', 'd', 'f'}
+  # literal conversion:
+  doAssert s1 == {'a', 'e'}
+  doAssert s2 == {'b', 'c', 'd', 'f'}
+  doAssert s3 == {'b', 'c', 'd', 'f'}
+  doAssert not compiles(s1 == s3)
+  doAssert not compiles(s2 == s3)
+  # can't convert literal 'z', overload match fails
+  doAssert not compiles(s1 == {'a', 'z'})
+
+block: # issue #18396
+  var s1: set[char] = {'a', 'b'}
+  var s2: set['a'..'z'] = {'a', 'b'}
+  doAssert s1 == {'a', 'b'}
+  doAssert s2 == {range['a'..'z'] 'a', 'b'}
+  doAssert s2 == {'a', 'b'}
+  doAssert not compiles(s1 == s2)
+
+block: # issue #16270
+  var s1: set[char] = {'a', 'b'}
+  var s2: set['a'..'z'] = {'a', 'c'}
+  doAssert not (compiles do: s2 = s2 + s1)
+  s2 = s2 + {'a', 'b'}
+  doAssert s2 == {'a', 'b', 'c'}
diff --git a/tests/sets/tsets.nim b/tests/sets/tsets.nim
index 96d5debc7..6125a3715 100644
--- a/tests/sets/tsets.nim
+++ b/tests/sets/tsets.nim
@@ -1,208 +1,133 @@
 discard """
-  file: "tsets.nim"
-  output: '''Ha ein F ist in s!
-false'''
+  targets: "c cpp"
 """
-# Test the handling of sets
 
-import
-  strutils
+# Test builtin sets
+
+# xxx these tests are not very good, this should be revisited.
+
+when defined nimTestsTsetsGenerate:
+  # to generate enums for this test
+  var ret: string
+  for i in 0..<276:
+    ret.add "k" & $i & ", "
+  echo ret
 
 proc testSets(s: var set[char]) =
   s = {'A', 'B', 'C', 'E'..'G'} + {'Z'} + s
 
 # test sets if the first element is different from 0:
+block:
+  type
+    TAZ = range['a'..'z']
+    TAZset = set[TAZ]
+    FakeTokType = enum
+      k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k30, k31, k32, k33, k34, k35, k36, k37, k38, k39, k40, k41, k42, k43, k44, k45, k46, k47, k48, k49, k50, k51, k52, k53, k54, k55, k56, k57, k58, k59, k60, k61, k62, k63, k64, k65, k66, k67, k68, k69, k70, k71, k72, k73, k74, k75, k76, k77, k78, k79, k80, k81, k82, k83, k84, k85, k86, k87, k88, k89, k90, k91, k92, k93, k94, k95, k96, k97, k98, k99, k100, k101, k102, k103, k104, k105, k106, k107, k108, k109, k110, k111, k112, k113, k114, k115, k116, k117, k118, k119, k120, k121, k122, k123, k124, k125, k126, k127, k128, k129, k130, k131, k132, k133, k134, k135, k136, k137, k138, k139, k140, k141, k142, k143, k144, k145, k146, k147, k148, k149, k150, k151, k152, k153, k154, k155, k156, k157, k158, k159, k160, k161, k162, k163, k164, k165, k166, k167, k168, k169, k170, k171, k172, k173, k174, k175, k176, k177, k178, k179, k180, k181, k182, k183, k184, k185, k186, k187, k188, k189, k190, k191, k192, k193, k194, k195, k196, k197, k198, k199, k200, k201, k202, k203, k204, k205, k206, k207, k208, k209, k210, k211, k212, k213, k214, k215, k216, k217, k218, k219, k220, k221, k222, k223, k224, k225, k226, k227, k228, k229, k230, k231, k232, k233, k234, k235, k236, k237, k238, k239, k240, k241, k242, k243, k244, k245, k246, k247, k248, k249
+    FakeTokTypeRange = range[k2..k101]
+    FakeTokTypes = set[FakeTokTypeRange]
+    Foo = object
+      field: set[Bar] #Bug: 6259
+    Bar = enum
+      bar1, bar2, bar3
+
+  const toktypes: FakeTokTypes = {FakeTokTypeRange(k2)..pred(k64), k72..k74}
+
+  var
+    s: set[char]
+    a: TAZset
+  s = {'0'..'9'}
+  testSets(s)
+  doAssert 'F' in s
+  a = {} #{'a'..'z'}
+  for x in low(TAZ) .. high(TAZ):
+    incl(a, x)
+    doAssert x in a
+
+  for x in low(FakeTokTypeRange) .. high(FakeTokTypeRange):
+    if x in tokTypes:
+      discard
+
 type
-  TAZ = range['a'..'z']
-  TAZset = set[TAZ]
-
-  TTokType* = enum
-    tkInvalid, tkEof,
-    tkSymbol,
-    tkAddr, tkAnd, tkAs, tkAsm, tkBlock, tkBreak, tkCase, tkCast, tkConst,
-    tkContinue, tkConverter, tkDiscard, tkDiv, tkElif, tkElse, tkEnd, tkEnum,
-    tkExcept, tkException, tkFinally, tkFor, tkFrom, tkGeneric, tkIf, tkImplies,
-    tkImport, tkIn, tkInclude, tkIs, tkIsnot, tkIterator, tkLambda, tkMacro,
-    tkMethod, tkMod, tkNil, tkNot, tkNotin, tkObject, tkOf, tkOr, tkOut, tkProc,
-    tkPtr, tkRaise, tkRecord, tkRef, tkReturn, tkShl, tkShr, tkTemplate, tkTry,
-    tkType, tkVar, tkWhen, tkWhere, tkWhile, tkWith, tkWithout, tkXor, tkYield,
-    tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit, tkFloatLit,
-    tkFloat32Lit, tkFloat64Lit, tkStrLit, tkRStrLit, tkTripleStrLit, tkCharLit,
-    tkRCharLit, tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe,
-    tkCurlyRi, tkBracketDotLe, tkBracketDotRi,
-    tkCurlyDotLe, tkCurlyDotRi,
-    tkParDotLe, tkParDotRi,
-    tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkHat, tkOpr,
-    tkComment, tkAccent, tkInd, tkSad, tkDed,
-    tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr
-  TTokTypeRange = range[tkSymbol..tkDed]
-  TTokTypes* = set[TTokTypeRange]
-
-const
-  toktypes: TTokTypes = {TTokTypeRange(tkSymbol)..pred(tkIntLit),
-                         tkStrLit..tkTripleStrLit}
-
-var
-  s: set[char]
-  a: TAZset
-s = {'0'..'9'}
-testSets(s)
-if 'F' in s: write(stdout, "Ha ein F ist in s!\n")
-else: write(stdout, "BUG: F ist nicht in s!\n")
-a = {} #{'a'..'z'}
-for x in low(TAZ) .. high(TAZ):
-  incl(a, x)
-  if x in a: discard
-  else: write(stdout, "BUG: something not in a!\n")
-
-for x in low(TTokTypeRange) .. high(TTokTypeRange):
-  if x in tokTypes:
-    discard
-    #writeLine(stdout, "the token '$1' is in the set" % repr(x))
-
-#OUT Ha ein F ist in s!
+  FakeMsgKind* = enum
+    k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k30, k31, k32, k33, k34, k35, k36, k37, k38, k39, k40, k41, k42, k43, k44, k45, k46, k47, k48, k49, k50, k51, k52, k53, k54, k55, k56, k57, k58, k59, k60, k61, k62, k63, k64, k65, k66, k67, k68, k69, k70, k71, k72, k73, k74, k75, k76, k77, k78, k79, k80, k81, k82, k83, k84, k85, k86, k87, k88, k89, k90, k91, k92, k93, k94, k95, k96, k97, k98, k99, k100, k101, k102, k103, k104, k105, k106, k107, k108, k109, k110, k111, k112, k113, k114, k115, k116, k117, k118, k119, k120, k121, k122, k123, k124, k125, k126, k127, k128, k129, k130, k131, k132, k133, k134, k135, k136, k137, k138, k139, k140, k141, k142, k143, k144, k145, k146, k147, k148, k149, k150, k151, k152, k153, k154, k155, k156, k157, k158, k159, k160, k161, k162, k163, k164, k165, k166, k167, k168, k169, k170, k171, k172, k173, k174, k175, k176, k177, k178, k179, k180, k181, k182, k183, k184, k185, k186, k187, k188, k189, k190, k191, k192, k193, k194, k195, k196, k197, k198, k199, k200, k201, k202, k203, k204, k205, k206, k207, k208, k209, k210, k211, k212, k213, k214, k215, k216, k217, k218, k219, k220, k221, k222, k223, k224, k225, k226, k227, k228, k229, k230, k231, k232, k233, k234, k235, k236, k237, k238, k239, k240, k241, k242, k243, k244, k245, k246, k247, k248, k249, k250, k251, k252, k253, k254, k255, k256, k257, k258, k259, k260, k261, k262, k263, k264, k265, k266, k267, k268, k269, k270, k271, k272, k273, k274, k275,
 
+doAssert pred(k260) == k259
 
 type
-  TMsgKind* = enum
-    errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated,
-    errXCompilerDoesNotSupportCpp, errStringLiteralExpected,
-    errIntLiteralExpected, errInvalidCharacterConstant,
-    errClosingTripleQuoteExpected, errClosingQuoteExpected,
-    errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong,
-    errInvalidNumber, errNumberOutOfRange, errNnotAllowedInCharacter,
-    errClosingBracketExpected, errMissingFinalQuote, errIdentifierExpected,
-    errNewlineExpected,
-    errInvalidModuleName,
-    errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected,
-    errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected,
-    errInvalidPragma, errUnknownPragma, errInvalidDirectiveX,
-    errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation,
-    errExceptionExpected, errExceptionAlreadyHandled,
-    errYieldNotAllowedHere, errYieldNotAllowedInTryStmt,
-    errInvalidNumberOfYieldExpr, errCannotReturnExpr, errAttemptToRedefine,
-    errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel,
-    errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected,
-    errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler,
-    errOnOrOffExpectedButXFound, errNoneBoehmRefcExpectedButXFound,
-    errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound,
-    errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound,
-    errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected,
-    errExprExpected, errUndeclaredIdentifier, errUseQualifier, errTypeExpected,
-    errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable,
-    errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue,
-    errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous,
-    errConstantDivisionByZero, errOrdinalTypeExpected,
-    errOrdinalOrFloatTypeExpected, errOverOrUnderflow,
-    errCannotEvalXBecauseIncompletelyDefined, errChrExpectsRange0_255,
-    errDynlibRequiresExportc, errUndeclaredFieldX, errNilAccess,
-    errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType,
-    errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit,
-    errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType,
-    errCastNotInSafeMode, errExprCannotBeCastedToX, errCommaOrParRiExpected,
-    errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected,
-    errMagicOnlyInSystem, errPowerOfTwoExpected,
-    errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv,
-    errSymbolMustBeImported, errExprMustBeBool, errConstExprExpected,
-    errDuplicateCaseLabel, errRangeIsEmpty, errSelectorMustBeOfCertainTypes,
-    errSelectorMustBeOrdinal, errOrdXMustNotBeNegative, errLenXinvalid,
-    errWrongNumberOfVariables, errExprCannotBeRaised, errBreakOnlyInLoop,
-    errTypeXhasUnknownSize, errConstNeedsConstExpr, errConstNeedsValue,
-    errResultCannotBeOpenArray, errSizeTooBig, errSetTooBig,
-    errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects,
-    errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX,
-    errCannotInstantiateX, errExprHasNoAddress, errXStackEscape,
-    errVarForOutParamNeeded,
-    errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX,
-    errAmbiguousCallXYZ, errWrongNumberOfArguments,
-    errXCannotBePassedToProcVar,
-    errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProc, errImplOfXNotAllowed,
-    errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,
-    errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice,
-    errInvalidOrderInArrayConstructor,
-    errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry,
-    errOptionExpected, errXisNoLabel, errNotAllCasesCovered,
-    errUnknownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable,
-    errNoPragmasAllowedForX, errNoGenericParamsAllowedForX,
-    errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent,
-    errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX,
-    errXNotAllowedHere, errInvalidControlFlowX,
-    errXisNoType, errCircumNeedsPointer, errInvalidExpression,
-    errInvalidExpressionX, errEnumHasNoValueX, errNamedExprExpected,
-    errNamedExprNotAllowed, errXExpectsOneTypeParam,
-    errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed,
-    errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType,
-    errNoReturnTypeDeclared,
-    errInvalidCommandX, errXOnlyAtModuleScope,
-    errXNeedsParamObjectType,
-    errTemplateInstantiationTooNested, errInstantiationFrom,
-    errInvalidIndexValueForTuple, errCommandExpectsFilename,
-    errMainModuleMustBeSpecified,
-    errXExpected,
-    errTIsNotAConcreteType,
-    errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
-    errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
-    errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly,
-    errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
-    errMacroBodyDependsOnGenericTypes,
-    errDestructorNotGenericEnough,
-    errInlineIteratorsAsProcParams,
-    errXExpectsTwoArguments,
-    errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations,
-    errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
-    errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument,
-    errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate,
-    errXhasSideEffects, errIteratorExpected, errLetNeedsInit,
-    errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,
-    errXCannotBeClosure, errXMustBeCompileTime,
-    errCannotInferTypeOfTheLiteral,
-    errCannotInferReturnType,
-    errGenericLambdaNotAllowed,
-    errCompilerDoesntSupportTarget,
-    errUser,
-    warnCannotOpenFile,
-    warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
-    warnDeprecated, warnConfigDeprecated,
-    warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
-    warnUnknownSubstitutionX, warnLanguageXNotSupported,
-    warnFieldXNotSupported, warnCommentXIgnored,
-    warnNilStatement, warnTypelessParam,
-    warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode,
-    warnEachIdentIsTuple, warnShadowIdent,
-    warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
-    warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
-    warnUser,
-    hintSuccess, hintSuccessX,
-    hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
-    hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
-    hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
-    hintConditionAlwaysTrue, hintName, hintPattern,
-    hintUser
-
-const
-  fatalMin* = errUnknown
-  fatalMax* = errInternal
-  errMin* = errUnknown
-  errMax* = errUser
-  warnMin* = warnCannotOpenFile
-  warnMax* = pred(hintSuccess)
-  hintMin* = hintSuccess
-  hintMax* = high(TMsgKind)
+  FakeMsgKind2 = range[k230..high(FakeMsgKind)]
+  FakeMsgKind3 = set[FakeMsgKind2]
 
-type
-  TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
-  TNoteKinds* = set[TNoteKind]
+var gNotes: FakeMsgKind3 = {low(FakeMsgKind2)..high(FakeMsgKind2)} - {k233, k235}
 
-var
-  gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} -
-                        {warnShadowIdent, warnUninit,
-                         warnProveField, warnProveIndex, warnGcUnsafe}
+doAssert k233 notin gNotes
 
+# 7555
+doAssert {-1.int8, -2, -2}.card == 2
+doAssert {1, 2, 2, 3..5, 4..6}.card == 6
 
-#import compiler.msgs
+# merely test the alias
+doAssert {-1.int8, -2, -2}.len == 2
+doAssert {1, 2, 2, 3..5, 4..6}.len == 6
 
-echo warnUninit in gNotes
+type Foo = enum
+  Foo1 = 0
+  Foo2 = 1
+  Foo3 = 3
 
-# 7555
-doAssert {-1.int8, -2, -2}.card == 2
-doAssert {1, 2, 2, 3..5, 4..6}.card == 6
\ No newline at end of file
+let x = { Foo1, Foo2 }
+# bug #8425
+
+block:
+  # bug #2880
+  type
+    FakeMsgKind = enum
+      k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k30, k31, k32, k33, k34, k35, k36, k37, k38, k39, k40, k41, k42, k43, k44, k45, k46, k47, k48, k49, k50, k51, k52, k53, k54, k55, k56, k57, k58, k59, k60, k61, k62, k63, k64, k65, k66, k67, k68, k69, k70, k71, k72, k73, k74, k75, k76, k77, k78, k79, k80, k81, k82, k83, k84, k85, k86, k87, k88, k89, k90, k91, k92, k93, k94, k95, k96, k97, k98, k99, k100
+
+  type
+    FakeMsgKind2 = range[FakeMsgKind.k50..high(FakeMsgKind)]
+    FakeMsgKind2s = set[FakeMsgKind2]
+
+  const
+    a1: array[0..0, FakeMsgKind2s] = [{low(FakeMsgKind2)..high(FakeMsgKind2)} - {FakeMsgKind.k99}]
+    a2 = a1[0]
+
+  var
+    s1: FakeMsgKind2s = a1[0]
+    s2: FakeMsgKind2s = a2
+
+  doAssert k99 notin s1
+  doAssert k99 notin s2
+
+block: # bug #23422
+  block:
+    var a: set[uint8] = {1'u8}
+
+    proc printLen(x: set[uint8]): int =
+      doAssert x.len == card(x)
+      result = card(x)
+
+    proc printLenVar(x: var set[uint8]): int =
+      doAssert x.len == card(x)
+      result = card(x)
+
+    doAssert a.len == 1
+    doAssert printLen(a) == 1
+    doAssert printLenVar(a) == card(a)
+
+  block:
+    type Fruit = enum
+      Apple, Banana, Melon
+
+    var a: set[Fruit] = {Apple}
+
+    proc printLen(x: set[Fruit]): int =
+      doAssert x.len == card(x)
+      result = card(x)
+
+    proc printLenVar(x: var set[Fruit]): int =
+      doAssert x.len == card(x)
+      result = card(x)
+
+    doAssert a.len == 1
+    doAssert printLen(a) == 1
+    doAssert printLenVar(a) == card(a)
diff --git a/tests/sets/tsets2.nim b/tests/sets/tsets2.nim
deleted file mode 100644
index f28822840..000000000
--- a/tests/sets/tsets2.nim
+++ /dev/null
@@ -1,71 +0,0 @@
-discard """
-  output: '''true'''
-"""
-
-import hashes, sets
-
-const
-  data = [
-    "34", "12",
-    "90", "0",
-    "1", "2",
-    "3", "4",
-    "5", "6",
-    "7", "8",
-    "9", "---00",
-    "10", "11", "19",
-    "20", "30", "40",
-    "50", "60", "70",
-    "80"]
-
-block tableTest1:
-  var t = initSet[tuple[x, y: int]]()
-  t.incl((0,0))
-  t.incl((1,0))
-  assert(not t.containsOrIncl((0,1)))
-  t.incl((1,1))
-
-  for x in 0..1:
-    for y in 0..1:
-      assert((x,y) in t)
-  #assert($t ==
-  #  "{(x: 0, y: 0), (x: 0, y: 1), (x: 1, y: 0), (x: 1, y: 1)}")
-
-block setTest2:
-  var t = initSet[string]()
-  t.incl("test")
-  t.incl("111")
-  t.incl("123")
-  t.excl("111")
-  t.incl("012")
-  t.incl("123") # test duplicates
-
-  assert "123" in t
-  assert "111" notin t # deleted
-
-  assert t.missingOrExcl("000") == true
-  assert "000" notin t
-  assert t.missingOrExcl("012") == false
-  assert "012" notin t
-
-  assert t.containsOrIncl("012") == false 
-  assert t.containsOrIncl("012") == true
-  assert "012" in t # added back 
-
-  for key in items(data): t.incl(key)
-  for key in items(data): assert key in t
-
-  for key in items(data): t.excl(key)
-  for key in items(data): assert key notin t
-
-block orderedSetTest1:
-  var t = data.toOrderedSet
-  for key in items(data): assert key in t
-  var i = 0
-  # `items` needs to yield in insertion order:
-  for key in items(t):
-    assert key == data[i]
-    inc(i)
-
-echo "true"
-
diff --git a/tests/sets/tsets3.nim b/tests/sets/tsets3.nim
deleted file mode 100644
index f599f8e7d..000000000
--- a/tests/sets/tsets3.nim
+++ /dev/null
@@ -1,100 +0,0 @@
-include sets
-
-let
-  s1: TSet[int] = toSet([1, 2, 4, 8, 16])
-  s2: TSet[int] = toSet([1, 2, 3, 5, 8])
-  s3: TSet[int] = toSet([3, 5, 7])
-
-block union:
-  let
-    s1_s2 = union(s1, s2)
-    s1_s3 = s1 + s3
-    s2_s3 = s2 + s3
-
-  assert s1_s2.len == 7
-  assert s1_s3.len == 8
-  assert s2_s3.len == 6
-
-  for i in s1:
-    assert i in s1_s2
-    assert i in s1_s3
-  for i in s2:
-    assert i in s1_s2
-    assert i in s2_s3
-  for i in s3:
-    assert i in s1_s3
-    assert i in s2_s3
-
-  assert((s1 + s1) == s1)
-  assert((s2 + s1) == s1_s2)
-
-block intersection:
-  let
-    s1_s2 = intersection(s1, s2)
-    s1_s3 = intersection(s1, s3)
-    s2_s3 = s2 * s3
-
-  assert s1_s2.len == 3
-  assert s1_s3.len == 0
-  assert s2_s3.len == 2
-
-  for i in s1_s2:
-    assert i in s1
-    assert i in s2
-  for i in s1_s3:
-    assert i in s1
-    assert i in s3
-  for i in s2_s3:
-    assert i in s2
-    assert i in s3
-
-  assert((s2 * s2) == s2)
-  assert((s3 * s2) == s2_s3)
-
-block symmetricDifference:
-  let
-    s1_s2 = symmetricDifference(s1, s2)
-    s1_s3 = s1 -+- s3
-    s2_s3 = s2 -+- s3
-
-  assert s1_s2.len == 4
-  assert s1_s3.len == 8
-  assert s2_s3.len == 4
-
-  for i in s1:
-    assert i in s1_s2 xor i in s2
-    assert i in s1_s3 xor i in s3
-  for i in s2:
-    assert i in s1_s2 xor i in s1
-    assert i in s2_s3 xor i in s3
-  for i in s3:
-    assert i in s1_s3 xor i in s1
-    assert i in s2_s3 xor i in s2
-
-  assert((s3 -+- s3) == initSet[int]())
-  assert((s3 -+- s1) == s1_s3)
-
-block difference:
-  let
-    s1_s2 = difference(s1, s2)
-    s1_s3 = difference(s1, s3)
-    s2_s3 = s2 - s3
-
-  assert s1_s2.len == 2
-  assert s1_s3.len == 5
-  assert s2_s3.len == 3
-
-  for i in s1:
-    assert i in s1_s2 xor i in s2
-    assert i in s1_s3 xor i in s3
-  for i in s2:
-    assert i in s2_s3 xor i in s3
-
-  assert((s2 - s2) == initSet[int]())
-  assert((s1 - s3 - s1) == s1 -+- s3)
-
-block disjoint:
-  assert(not disjoint(s1, s2))
-  assert disjoint(s1, s3)
-  assert(not disjoint(s2, s3))
-  assert(not disjoint(s2, s2))
diff --git a/tests/sets/tsets_lt.nim b/tests/sets/tsets_lt.nim
deleted file mode 100644
index 6d0b3a60c..000000000
--- a/tests/sets/tsets_lt.nim
+++ /dev/null
@@ -1,12 +0,0 @@
-discard """
-  output: '''true
-true
-true'''
-"""
-
-var s, s1: set[char]
-s = {'a'..'d'}
-s1 = {'a'..'c'}
-echo s1 < s
-echo s1 * s == {'a'..'c'}
-echo s1 <= s
diff --git a/tests/sets/tsets_various.nim b/tests/sets/tsets_various.nim
new file mode 100644
index 000000000..419bcfdcc
--- /dev/null
+++ b/tests/sets/tsets_various.nim
@@ -0,0 +1,286 @@
+discard """
+  targets: "c cpp js"
+"""
+
+import std/[sets, hashes]
+
+from std/sequtils import toSeq
+from std/algorithm import sorted
+from stdtest/testutils import whenVMorJs
+
+proc sortedPairs[T](t: T): auto = toSeq(t.pairs).sorted
+template sortedItems(t: untyped): untyped = sorted(toSeq(t))
+
+block tsetpop:
+  var a = initHashSet[int]()
+  for i in 1..1000:
+    a.incl(i)
+  doAssert len(a) == 1000
+  for i in 1..1000:
+    discard a.pop()
+  doAssert len(a) == 0
+
+  var msg = ""
+  try:
+    echo a.pop()
+  except KeyError as e:
+    msg = e.msg
+  doAssert msg == "set is empty"
+
+
+
+block tsets_lt:
+  var s, s1: set[char]
+  s = {'a'..'d'}
+  s1 = {'a'..'c'}
+  doAssert s1 < s
+  doAssert s1 * s == {'a'..'c'}
+  doAssert s1 <= s
+
+
+
+block tsets2:
+  const
+    data = [
+      "34", "12",
+      "90", "0",
+      "1", "2",
+      "3", "4",
+      "5", "6",
+      "7", "8",
+      "9", "---00",
+      "10", "11", "19",
+      "20", "30", "40",
+      "50", "60", "70",
+      "80"]
+
+  block tableTest1:
+    var t = initHashSet[tuple[x, y: int]]()
+    t.incl((0,0))
+    t.incl((1,0))
+    doAssert(not t.containsOrIncl((0,1)))
+    t.incl((1,1))
+
+    for x in 0..1:
+      for y in 0..1:
+        doAssert((x,y) in t)
+    #doAssert($t ==
+    #  "{(x: 0, y: 0), (x: 0, y: 1), (x: 1, y: 0), (x: 1, y: 1)}")
+
+  block setTest2:
+    var t = initHashSet[string]()
+    t.incl("test")
+    t.incl("111")
+    t.incl("123")
+    t.excl("111")
+    t.incl("012")
+    t.incl("123") # test duplicates
+
+    doAssert "123" in t
+    doAssert "111" notin t # deleted
+
+    doAssert t.missingOrExcl("000")
+    doAssert "000" notin t
+    doAssert t.missingOrExcl("012") == false
+    doAssert "012" notin t
+
+    doAssert t.containsOrIncl("012") == false
+    doAssert t.containsOrIncl("012")
+    doAssert "012" in t # added back
+
+    for key in items(data): t.incl(key)
+    for key in items(data): doAssert key in t
+
+    for key in items(data): t.excl(key)
+    for key in items(data): doAssert key notin t
+
+  block orderedSetTest1:
+    var t = data.toOrderedSet
+    for key in items(data): doAssert key in t
+    var i = 0
+    # `items` needs to yield in insertion order:
+    for key in items(t):
+      doAssert key == data[i]
+      inc(i)
+
+
+
+block tsets3:
+  let
+    s1: HashSet[int] = toHashSet([1, 2, 4, 8, 16])
+    s2: HashSet[int] = toHashSet([1, 2, 3, 5, 8])
+    s3: HashSet[int] = toHashSet([3, 5, 7])
+
+  block union:
+    let
+      s1_s2 = union(s1, s2)
+      s1_s3 = s1 + s3
+      s2_s3 = s2 + s3
+
+    doAssert s1_s2.len == 7
+    doAssert s1_s3.len == 8
+    doAssert s2_s3.len == 6
+
+    for i in s1:
+      doAssert i in s1_s2
+      doAssert i in s1_s3
+    for i in s2:
+      doAssert i in s1_s2
+      doAssert i in s2_s3
+    for i in s3:
+      doAssert i in s1_s3
+      doAssert i in s2_s3
+
+    doAssert((s1 + s1) == s1)
+    doAssert((s2 + s1) == s1_s2)
+
+  block intersection:
+    let
+      s1_s2 = intersection(s1, s2)
+      s1_s3 = intersection(s1, s3)
+      s2_s3 = s2 * s3
+
+    doAssert s1_s2.len == 3
+    doAssert s1_s3.len == 0
+    doAssert s2_s3.len == 2
+
+    for i in s1_s2:
+      doAssert i in s1
+      doAssert i in s2
+    for i in s1_s3:
+      doAssert i in s1
+      doAssert i in s3
+    for i in s2_s3:
+      doAssert i in s2
+      doAssert i in s3
+
+    doAssert((s2 * s2) == s2)
+    doAssert((s3 * s2) == s2_s3)
+
+  block symmetricDifference:
+    let
+      s1_s2 = symmetricDifference(s1, s2)
+      s1_s3 = s1 -+- s3
+      s2_s3 = s2 -+- s3
+
+    doAssert s1_s2.len == 4
+    doAssert s1_s3.len == 8
+    doAssert s2_s3.len == 4
+
+    for i in s1:
+      doAssert i in s1_s2 xor i in s2
+      doAssert i in s1_s3 xor i in s3
+    for i in s2:
+      doAssert i in s1_s2 xor i in s1
+      doAssert i in s2_s3 xor i in s3
+    for i in s3:
+      doAssert i in s1_s3 xor i in s1
+      doAssert i in s2_s3 xor i in s2
+
+    doAssert((s3 -+- s3) == initHashSet[int]())
+    doAssert((s3 -+- s1) == s1_s3)
+
+  block difference:
+    let
+      s1_s2 = difference(s1, s2)
+      s1_s3 = difference(s1, s3)
+      s2_s3 = s2 - s3
+
+    doAssert s1_s2.len == 2
+    doAssert s1_s3.len == 5
+    doAssert s2_s3.len == 3
+
+    for i in s1:
+      doAssert i in s1_s2 xor i in s2
+      doAssert i in s1_s3 xor i in s3
+    for i in s2:
+      doAssert i in s2_s3 xor i in s3
+
+    doAssert((s2 - s2) == initHashSet[int]())
+
+  block disjoint:
+    doAssert(not disjoint(s1, s2))
+    doAssert disjoint(s1, s3)
+    doAssert(not disjoint(s2, s3))
+    doAssert(not disjoint(s2, s2))
+
+block: # https://github.com/nim-lang/Nim/issues/13496
+  template testDel(body) =
+    block:
+      body
+      t.incl(15)
+      t.incl(19)
+      t.incl(17)
+      t.incl(150)
+      t.excl(150)
+      doAssert t.len == 3
+      doAssert sortedItems(t) == @[15, 17, 19]
+      var s = newSeq[int]()
+      for v in t: s.add(v)
+      doAssert s.len == 3
+      doAssert sortedItems(s) == @[15, 17, 19]
+      when t is OrderedSet:
+        doAssert sortedPairs(t) == @[(a: 0, b: 15), (a: 1, b: 19), (a: 2, b: 17)]
+        doAssert toSeq(t) == @[15, 19, 17]
+
+  testDel(): (var t: HashSet[int])
+  testDel(): (var t: OrderedSet[int])
+
+block: # test correctness after a number of inserts/deletes
+  template testDel(body) =
+    block:
+      body
+      var expected: seq[int]
+      let n = 100
+      let n2 = n*2
+      for i in 0..<n:
+        t.incl(i)
+      for i in 0..<n:
+        if i mod 3 == 0:
+          t.excl(i)
+      for i in n..<n2:
+        t.incl(i)
+      for i in 0..<n2:
+        if i mod 7 == 0:
+          t.excl(i)
+
+      for i in 0..<n2:
+        if (i>=n or i mod 3 != 0) and i mod 7 != 0:
+          expected.add i
+
+      for i in expected: doAssert i in t
+      doAssert t.len == expected.len
+      doAssert sortedItems(t) == expected
+
+  testDel(): (var t: HashSet[int])
+  testDel(): (var t: OrderedSet[int])
+
+
+template main() =
+  # xxx move all tests inside this
+  block:
+    let a = {true, false}
+    doAssert $a == "{false, true}"
+    doAssert a.len == 2
+
+  block:
+    let a = {false .. true}
+    doAssert $a == "{false, true}"
+    doAssert a.len == 2
+
+  block:
+    let a = {false .. false}
+    doAssert $a == "{false}"
+    doAssert a.len == 1
+
+  block: # bug #16123
+    whenVMorJs: discard
+    do:
+      type CallType = proc() {.closure.}
+      var setA = initHashSet[CallType]()
+      let foo = proc() = discard
+      setA.incl(foo)
+      doAssert setA.contains(foo)
+
+static: main()
+main()
diff --git a/tests/sets/twrongenumrange.nim b/tests/sets/twrongenumrange.nim
new file mode 100644
index 000000000..a8d64ac44
--- /dev/null
+++ b/tests/sets/twrongenumrange.nim
@@ -0,0 +1,50 @@
+discard """
+  cmd: "nim check --hints:off $file"
+"""
+
+# issue #17848
+
+block:
+  # generate with:
+  # var a = ""
+  # for i in 0..<80: a.add "k" & $i & ", "
+  # echo a
+  type
+    TMsgKind = enum
+      k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k30, k31, k32, k33, k34, k35, k36, k37, k38, k39, k40, k41, k42, k43, k44, k45, k46, k47, k48, k49, k50, k51, k52, k53, k54, k55, k56, k57, k58, k59, k60, k61, k62, k63, k64, k65, k66, k67, k68, k69, k70, k71, k72, k73, k74, k75, k76, k77, k78, k79
+  type
+    TNoteKind = range[k10..k79]
+    Conf = ref object
+      notes: set[TNoteKind]
+  proc bad(conf: Conf, noteSet: set[TMsgKind]) =
+    conf.notes = noteSet #[tt.Error
+                 ^ type mismatch: got <set[TMsgKind]> but expected 'set[TNoteKind]']#
+  var conf = Conf()
+  bad(conf, {k10..k60})
+
+block:
+  type
+    TMsgKind = enum k0, k1, k2, k3
+    TNoteKind = range[k1..k2]
+    TNoteKinds = set[TNoteKind]
+  type Conf = ref object
+    notes: TNoteKinds
+  proc fn(conf: Conf, b: set[TMsgKind]) =
+    conf.notes = b #[tt.Error
+                 ^ type mismatch: got <set[TMsgKind]> but expected 'TNoteKinds = set[TNoteKind]']#
+  var conf = Conf()
+  conf.fn({k0..k3}) # BUG: this should give error
+  echo conf.notes # {k1, k2}
+
+block:
+  #[
+  compiler/bitsets.nim(43, 9) `elem >= 0`  [AssertionDefect]
+  ]#
+  type
+    TMsgKind = enum k0, k1, k2, k3
+    TNoteKind = range[k1..k2]
+  var notes: set[TNoteKind]
+  notes = {k0} #[tt.Error
+          ^ type mismatch: got <set[TMsgKind]> but expected 'set[TNoteKind]']#
+  notes = {k0..k3} #[tt.Error
+          ^ type mismatch: got <set[TMsgKind]> but expected 'set[TNoteKind]']#
diff --git a/tests/showoff/tdrdobbs_examples.nim b/tests/showoff/tdrdobbs_examples.nim
index 8687ee529..c61e177dc 100644
--- a/tests/showoff/tdrdobbs_examples.nim
+++ b/tests/showoff/tdrdobbs_examples.nim
@@ -40,14 +40,14 @@ const msb3999 = mostSignificantBit(3999)
 
 echo msb3999, " ", mostSignificantBit(0), " ", square(44)
 
-proc filter[T](a: openarray[T], predicate: proc (x: T): bool): seq[T] =
+proc filter[T](a: openArray[T], predicate: proc (x: T): bool): seq[T] =
   result = @[] # @[] constructs the empty seq
   for x in a:
     if predicate(x): result.add(x)
 
-proc map[T, S](a: openarray[T], fn: proc (x: T): S): seq[S] =
+proc map[T, S](a: openArray[T], fn: proc (x: T): S): seq[S] =
   newSeq(result, a.len)
-  for i in 0 .. <a.len: result[i] = fn(a[i])
+  for i in 0 ..< a.len: result[i] = fn(a[i])
 
 
 type
diff --git a/tests/showoff/tformatopt.nim b/tests/showoff/tformatopt.nim
index 6e790c38e..420dd026b 100644
--- a/tests/showoff/tformatopt.nim
+++ b/tests/showoff/tformatopt.nim
@@ -37,7 +37,7 @@ macro optFormat{`%`(f, a)}(f: string{lit}, a: openArray[string]): untyped =
   result = newNimNode(nnkBracket)
   let f = f.strVal
   formatImpl(newLit)
-  result = nestList(!"&", result)
+  result = nestList(newIdentNode("&"), result)
 
 template optAdd1{x = y; add(x, z)}(x, y, z: string) =
   x = y & z
diff --git a/tests/showoff/tgenericmacrotypes.nim b/tests/showoff/tgenericmacrotypes.nim
new file mode 100644
index 000000000..cc07f4355
--- /dev/null
+++ b/tests/showoff/tgenericmacrotypes.nim
@@ -0,0 +1,55 @@
+# issue #7974
+
+import macros
+
+macro genTypeA(arg: typed): untyped =
+  if arg.typeKind != ntyTypeDesc:
+    error("expected typedesc", arg)
+
+  result = arg.getTypeInst[1]
+
+macro genTypeB(arg: typed): untyped =
+  if arg.typeKind != ntyTypeDesc:
+    error("expected typedesc", arg)
+
+
+  let typeSym = arg.getTypeInst[1]
+  result =
+    nnkTupleTy.newTree(
+      nnkIdentDefs.newTree(
+        ident"a", typeSym, newEmptyNode()
+      )
+    )
+
+type
+  # this is the trivial case, MyTypeA[T] is basically just T, nothing else. But it works.
+  MyTypeA[T] = genTypeA(T)
+  # in this case I generate `tuple[a: T]`. This this is something the compiler does not want
+  MyTypeB[T] = genTypeB(T)
+
+# these are just alias types for int32 and float32, nothing really happens, but it works
+var a1: MyTypeA[int32]
+doAssert a1 is MyTypeA[int32]
+doAssert a1 is int32
+a1 = 0'i32
+var a2: MyTypeA[float32]
+doAssert a2 is MyTypeA[float32]
+doAssert a2 is float32
+a2 = 0'f32
+var a3: MyTypeA[float32]
+doAssert a3 is MyTypeA[float32]
+doAssert a3 is float32
+a3 = 0'f32
+
+var b1: MyTypeB[int32]   # cannot generate VM code fur tuple[a: int32]
+doAssert b1 is MyTypeB[int32]
+doAssert b1 is tuple[a: int32]
+b1 = (a: 0'i32)
+var b2: MyTypeB[float32]
+doAssert b2 is MyTypeB[float32]
+doAssert b2 is tuple[a: float32]
+b2 = (a: 0'f32)
+var b3: MyTypeB[float32]
+doAssert b3 is MyTypeB[float32]
+doAssert b3 is tuple[a: float32]
+b3 = (a: 0'f32)
diff --git a/tests/specialops/tcallops.nim b/tests/specialops/tcallops.nim
new file mode 100644
index 000000000..c541a0c1d
--- /dev/null
+++ b/tests/specialops/tcallops.nim
@@ -0,0 +1,49 @@
+import macros
+
+{.experimental: "callOperator".}
+
+type Foo[T: proc] = object
+  callback: T
+
+macro `()`(foo: Foo, args: varargs[untyped]): untyped =
+  result = newCall(newDotExpr(foo, ident"callback"))
+  for a in args:
+    result.add(a)
+
+var f1Calls = 0
+var f = Foo[proc()](callback: proc() = inc f1Calls)
+doAssert f1Calls == 0
+f()
+doAssert f1Calls == 1
+var f2Calls = 0
+f.callback = proc() = inc f2Calls
+doAssert f2Calls == 0
+f()
+doAssert f2Calls == 1
+
+let g = Foo[proc (x: int): int](callback: proc (x: int): int = x * 2 + 1)
+doAssert g(15) == 31
+
+proc `()`(args: varargs[string]): string =
+  result = "("
+  for a in args: result.add(a)
+  result.add(')')
+
+let a = "1"
+let b = "2"
+let c = "3"
+
+doAssert a(b) == "(12)"
+doAssert a.b(c) == `()`(b, a, c)
+doAssert (a.b)(c) == `()`(a.b, c)
+doAssert `()`(a.b, c) == `()`(`()`(b, a), c)
+
+block: # bug #1072
+  var x: int
+
+  proc foo(some:int):int = some
+  proc `()`(l,r:string): string = discard
+
+  block:
+    var foo = 42
+    doAssert x.foo == 0
diff --git a/tests/specialops/tcallprecedence.nim b/tests/specialops/tcallprecedence.nim
new file mode 100644
index 000000000..6116f83d5
--- /dev/null
+++ b/tests/specialops/tcallprecedence.nim
@@ -0,0 +1,44 @@
+import macros
+
+{.experimental: "dotOperators".}
+{.experimental: "callOperator".}
+
+block:
+  template `.()`(foo: int, args: varargs[untyped]): untyped {.used.} =
+    ".()"
+
+  template `()`(foo: int, args: varargs[untyped]): untyped =
+    "()"
+
+  let a = (b: 1)
+  let c = 3
+
+  doAssert a.b(c) == "()"
+  doAssert not compiles(a(c))
+  doAssert (a.b)(c) == "()"
+
+macro `()`(args: varargs[typed]): untyped =
+  result = newLit("() " & args.treeRepr)
+
+macro `.`(args: varargs[typed]): untyped =
+  result = newLit(". " & args.treeRepr)
+
+block:
+  let a = 1
+  let b = 2
+  doAssert a.b == `()`(b, a)
+
+block:
+  let a = 1
+  proc b(): int {.used.} = 2
+  doAssert a.b == `.`(a, b)
+
+block:
+  let a = 1
+  proc b(x: int): int = x + 1
+  let c = 3
+
+  doAssert a.b(c) == `.`(a, b, c)
+  doAssert a(b) == `()`(a, b)
+  doAssert (a.b)(c) == `()`(a.b, c)
+  doAssert a.b == b(a)
diff --git a/tests/specialops/tdotops.nim b/tests/specialops/tdotops.nim
index 227204f51..ca5eee665 100644
--- a/tests/specialops/tdotops.nim
+++ b/tests/specialops/tdotops.nim
@@ -1,5 +1,6 @@
 discard """
   output: '''
+
 10
 assigning z = 20
 reading field y
@@ -12,9 +13,18 @@ no params call to b
 100
 one param call to c with 10
 100
-0 4'''
+0 4
+'''
 """
 
+block:
+  type Foo = object
+  var a: Foo
+  template `.`(a: Foo, b: untyped): untyped = astToStr(b)
+  template callme(a, f): untyped = a.f
+  doAssert callme(a, f2) == "f2" # not `f`
+  doAssert a.callme(f3) == "f3"
+
 type
   T1 = object
     x*: int
diff --git a/tests/specialops/terrmsgs.nim b/tests/specialops/terrmsgs.nim
new file mode 100644
index 000000000..081bca451
--- /dev/null
+++ b/tests/specialops/terrmsgs.nim
@@ -0,0 +1,76 @@
+discard """
+action: reject
+cmd: '''nim check $options $file'''
+matrix: "; -d:testWithout; --mm:refc"
+"""
+
+when not defined(testWithout): # test for same errors before and after
+  {.experimental: "dotOperators".}
+  {.experimental: "callOperator".}
+
+# issue #13063
+
+block:
+  type Foo = object
+  type Bar = object
+    x1: int
+  var b: Bar
+  block:
+    template `.`(a: Foo, b: untyped): untyped = 123
+    echo b.x #[tt.Error
+          ^ undeclared field: 'x' for type terrmsgs.Bar [type declared in terrmsgs.nim(15, 8)]]#
+  block:
+    template `.()`(a: Foo, b: untyped): untyped = 123
+    echo b.x() #[tt.Error
+          ^ attempting to call undeclared routine: 'x']#
+  block:
+    template `.=`(a: Foo, b: untyped, c: untyped) = b = c
+    b.x = 123 #[tt.Error
+        ^ undeclared field: 'x=' for type terrmsgs.Bar [type declared in terrmsgs.nim(15, 8)]]#
+    # yeah it says x= but does it matter in practice
+  block:
+    template `()`(a: Foo, b: untyped, c: untyped) = echo "something"
+
+    # completely undeclared::
+    xyz(123) #[tt.Error
+    ^ undeclared identifier: 'xyz']#
+
+    # already declared routine:
+    min(123) #[tt.Error
+       ^ type mismatch: got <int literal(123)>]#
+
+    # non-routine type shows `()` overloads:
+    b(123) #[tt.Error
+     ^ attempting to call routine: 'b']#
+
+    echo b.x #[tt.Error
+          ^ undeclared field: 'x' for type terrmsgs.Bar [type declared in terrmsgs.nim(15, 8)]]#
+    echo b.x() #[tt.Error
+          ^ attempting to call undeclared routine: 'x']#
+
+# issue #7777
+
+import macros
+
+block:
+  type TestType = object
+    private_field: string
+
+  when false:
+    template getField(obj, field: untyped): untyped = obj.field
+
+  macro `.`(obj: TestType, field: untyped): untyped =
+    let private = newIdentNode("private_" & $field)
+    result = quote do:
+      `obj`.getField(`private`) #[tt.Error
+           ^ attempting to call undeclared routine: 'getField']#
+
+  var tt: TestType
+  discard tt.field
+
+block: # related to issue #6981
+  proc `()`(a:string, b:string):string = a & b
+  proc mewSeq[T](a,b:int)=discard
+  proc mewSeq[T](c:int)= discard
+  mewSeq[int]() #[tt.Error
+             ^ type mismatch: got <>]#
diff --git a/tests/specialops/tnewseq.nim b/tests/specialops/tnewseq.nim
new file mode 100644
index 000000000..970a5a8c2
--- /dev/null
+++ b/tests/specialops/tnewseq.nim
@@ -0,0 +1,22 @@
+# issue #6981
+
+import std/assertions
+
+{.experimental: "callOperator".}
+
+block: # issue #6981
+  proc `()`(a:string, b:string):string = a & b
+
+  var s = newSeq[int](3)
+
+  doAssert s == @[0, 0, 0]
+
+block: # generalized example from #6981
+  proc mewSeq[T](a: int)=discard
+  proc mewSeq[T]()= discard
+  mewSeq[int]()
+
+block: # issue #9831
+  type Foo = object
+  proc `()`(foo: Foo) = discard
+  let x = newSeq[int]()
diff --git a/tests/specialops/tsetter.nim b/tests/specialops/tsetter.nim
new file mode 100644
index 000000000..6175cbec4
--- /dev/null
+++ b/tests/specialops/tsetter.nim
@@ -0,0 +1,10 @@
+block: # ensure RHS of setter statement is treated as call operand
+  proc `b=`(a: var int, c: proc (x: int): int) =
+    a = c(a)
+
+  proc foo(x: int): int = x + 1
+  proc foo(x: float): float = x - 1
+
+  var a = 123
+  a.b = foo
+  doAssert a == 124
diff --git a/tests/statictypes/t20148.nim b/tests/statictypes/t20148.nim
new file mode 100644
index 000000000..d74a7755e
--- /dev/null
+++ b/tests/statictypes/t20148.nim
@@ -0,0 +1,8 @@
+type Percent = range[0.0 .. 1.0]
+# type Percent = float # using unlimited `float` works fine
+
+proc initColor*(alpha: Percent): bool =
+  echo alpha
+
+const moduleInstanceStyle = initColor(1)
+# let moduleInstanceStyle = initColor(1) # using runtime conversion works fine
diff --git a/tests/statictypes/t5780.nim b/tests/statictypes/t5780.nim
new file mode 100644
index 000000000..85548aaad
--- /dev/null
+++ b/tests/statictypes/t5780.nim
@@ -0,0 +1,3 @@
+type StringArray[N:int] = array[N, string]
+let a = ["one", "two"]
+doAssert a is StringArray
diff --git a/tests/statictypes/t9255.nim b/tests/statictypes/t9255.nim
new file mode 100644
index 000000000..aec2fbaf5
--- /dev/null
+++ b/tests/statictypes/t9255.nim
@@ -0,0 +1,13 @@
+discard """
+  errormsg: '''
+type mismatch: got <proc (a0: int): string{.noSideEffect, gcsafe.}>
+'''
+  line: 13
+"""
+
+macro fun(a: static float): untyped =
+  discard
+
+when true:
+  proc bar(a0: int): string = discard
+  fun(bar)
diff --git a/tests/statictypes/tgenericcomputedrange.nim b/tests/statictypes/tgenericcomputedrange.nim
new file mode 100644
index 000000000..9e3a49ae0
--- /dev/null
+++ b/tests/statictypes/tgenericcomputedrange.nim
@@ -0,0 +1,125 @@
+import math
+
+block: # issue #19916
+  type
+    Test[S: static[Natural]] = object
+      a: array[ceilDiv(S, 8), uint8]
+
+  let a = Test[32]()
+  doAssert a.a.len == 4
+
+block: # issue #20514
+  type Foo[S:static[array[2, int]]] = object
+    values: array[prod(S), float]
+
+  doAssert Foo[[4,5]]().values.len == 20
+
+block: # issue #20937
+  type
+    Vec3[T: SomeNumber] {.bycopy.} = tuple[x, y, z: T]
+
+  func volume[T](v: Vec3[T]): T =
+    when T is SomeUnsignedInt:
+      v.x * v.y * v.z
+    else:
+      abs (v.x * v.y * v.z)
+
+  type
+    Matrix3[C: static Vec3[uint], T] = object
+      cells: array[C.volume, T]
+
+  let m = Matrix3[(1.uint, 1.uint, 1.uint), uint](cells: [0.uint])
+  doAssert m.cells.len == 1
+  let m2 = Matrix3[(4.uint, 3.uint, 5.uint), uint]()
+  doAssert m2.cells.len == 60
+
+block:  # issue #19284
+  type Board[N, M: static Slice[int]] = array[len(N)*len(M), int8]
+
+  var t: Board[0..4, 0..4]
+  doAssert t.len == 25
+
+block: # minimal issue #19284
+  proc foo[T](x: T): int =
+    result = 0
+  type Foo[N: static int] = array[0..foo(N), int]
+
+  var t: Foo[5]
+  doAssert t.len == 1
+
+block:
+  type Foo[T; U: static T] = range[T(0) .. U]
+
+  block:
+    var x: array[Foo[int, 1], int]
+    x[0] = 1
+    x[1] = 2
+    doAssert x == [0: 1, 1: 2]
+    doAssert x is array[0 .. 1, int]
+
+  block:
+    type Bar = enum a, b, c
+    var x: array[Foo[Bar, c], int]
+    x[a] = 1
+    x[b] = 2
+    x[c] = 3
+    doAssert x == [a: 1, b: 2, c: 3]
+    doAssert x is array[a .. c, int]
+
+block:
+  type Foo[T; U: static T] = array[T(0) .. U, int]
+
+  block:
+    var x: Foo[int, 1]
+    x[0] = 1
+    x[1] = 2
+    doAssert x == [0: 1, 1: 2]
+    doAssert x is array[0 .. 1, int]
+
+  block:
+    type Bar = enum a, b, c
+    var x: Foo[Bar, c]
+    x[a] = 1
+    x[b] = 2
+    x[c] = 3
+    doAssert x == [a: 1, b: 2, c: 3]
+    doAssert x is array[a .. c, int]
+
+block:
+  type Foo[T; U: static T] = array[T(0) .. U + 1, int]
+
+  block:
+    var x: Foo[int, 1]
+    x[0] = 1
+    x[1] = 2
+    x[2] = 3
+    doAssert x == [0: 1, 1: 2, 2: 3]
+    doAssert x is array[0 .. 2, int]
+
+block:
+  type Foo[T; U: static T] = array[T(0) .. (U * 2) + 1, int]
+
+  block:
+    var x: Foo[int, 1]
+    x[0] = 1
+    x[1] = 2
+    x[2] = 3
+    x[3] = 4
+    doAssert x == [0: 1, 1: 2, 2: 3, 3: 4]
+    doAssert x is array[0 .. 3, int]
+
+block: # issue #22187
+  template m(T: type, s: int64): int64 = s
+  func p(n: int64): int = int(n)
+  type F[T; s: static int64] = object
+    k: array[p(m(T, s)), int64]
+  var x: F[int, 3]
+  doAssert x.k is array[3, int64]
+
+block: # issue #22490
+  proc log2trunc(x: uint64): int =
+    if x == 0: int(0) else: int(0)
+  template maxChunkIdx(T: typedesc): int64 = 0'i64
+  template layer(vIdx: int64): int = log2trunc(0'u64)
+  type HashList[T] = object
+    indices: array[int(layer(maxChunkIdx(T))), int]
diff --git a/tests/statictypes/tpassthruarith.nim b/tests/statictypes/tpassthruarith.nim
index 90fc7824c..33e4e0303 100644
--- a/tests/statictypes/tpassthruarith.nim
+++ b/tests/statictypes/tpassthruarith.nim
@@ -1,13 +1,9 @@
 discard """
 output: '''
 [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
-
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
-
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
-
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
-
 [1, 2, 3, 4]
 '''
 """
@@ -46,7 +42,7 @@ type
   Vect[N: static[int], A] = array[N, A]
 
 proc push[N: static[int], A](a: Vect[N, A], x: A): Vect[N + 1, A] =
-  for n in 0 .. < N:
+  for n in 0 ..<  N:
     result[n] = a[n]
   result[N] = x
 
diff --git a/tests/statictypes/tstatic.nim b/tests/statictypes/tstatic.nim
new file mode 100644
index 000000000..aaa63534b
--- /dev/null
+++ b/tests/statictypes/tstatic.nim
@@ -0,0 +1,56 @@
+discard """
+  targets: "c cpp js"
+"""
+
+template main() =
+  block: # bug #17589
+    #[
+    # all those gave some variation of the same bug:
+    'intVal' is not accessible using discriminant 'kind' of type 'TFullReg'
+    'floatVal' is not accessible using discriminant 'kind' of type 'TFullReg'
+    ]#
+    block:
+      proc f(a: static uint64): uint64 =
+        a
+      const x = 3'u64
+      static: doAssert f(x) == 3'u64
+      doAssert f(x) == 3'u64
+
+    block:
+      proc f(a: static uint64): uint64 =
+        a
+      const x = 3'u64
+      static: doAssert f(x) == 3'u64
+      doAssert f(x) == 3'u64
+
+    block:
+      proc foo(x: uint8): uint8 = x
+      proc f(a: static uint8): auto = foo(a)
+      const x = 3'u8
+      static: doAssert f(x) == 3'u8
+      doAssert f(x) == 3'u8
+
+    block:
+      template foo2(x: float) =
+        let b = x == 0
+      proc foo(x: float) = foo2(x)
+      proc f(a: static float) = foo(a)
+      const x = 1.0
+      static: f(x)
+
+    block:
+      proc foo(x: int32) =
+        let b = x == 0
+      proc f(a: static int32) = foo(a)
+      static: f(32767) # was working
+      static: f(32768) # was failing because >= int16.high (see isInt16Lit)
+
+  block: # bug #14585
+    const foo_m0ninv = 0x1234'u64
+    proc foo(m0ninv: static uint64) =
+      let b = $m0ninv
+    static:
+      foo(foo_m0ninv)
+
+static: main()
+main()
diff --git a/tests/statictypes/tstaticgenericparam.nim b/tests/statictypes/tstaticgenericparam.nim
new file mode 100644
index 000000000..6acd05b70
--- /dev/null
+++ b/tests/statictypes/tstaticgenericparam.nim
@@ -0,0 +1,24 @@
+# static types that depend on a generic parameter
+
+block: # issue #19365
+  var ss: seq[string]
+  proc f[T](x: static T) =
+    ss.add($x & ": " & $T)
+
+  f(123)
+  doAssert ss == @["123: int"]
+  f("abc")
+  doAssert ss == @["123: int", "abc: string"]
+
+block: # issue #7209
+  type Modulo[A; M: static[A]] = distinct A
+
+  proc `$`[A; M: static[A]](x: Modulo[A, M]): string =
+    $(A(x)) & " mod " & $(M)
+
+  proc modulo[A](a: A, M: static[A]): Modulo[A, M] = Modulo[A, M](a %% M)
+
+  proc `+`[A; M: static[A]](x, y: Modulo[A, M]): Modulo[A, M] =
+    (A(x) + A(y)).modulo(M)
+
+  doAssert $(3.modulo(7) + 5.modulo(7)) == "1 mod 7"
diff --git a/tests/statictypes/tstaticimportcpp.nim b/tests/statictypes/tstaticimportcpp.nim
index 0cbbc1df6..ec4696379 100644
--- a/tests/statictypes/tstaticimportcpp.nim
+++ b/tests/statictypes/tstaticimportcpp.nim
@@ -1,6 +1,6 @@
 discard """
 targets: "cpp"
-output: "[0, 0, 10, 0]\n5\n1.2\n15\ntest\n[0, 0, 20, 0]"
+output: "[0, 0, 10, 0]\n5\n1.2\n15\ntest\n[0, 0, 20, 0]\n4"
 """
 
 {.emit: """/*TYPESECTION*/
@@ -23,10 +23,12 @@ struct SimpleStruct {
 """ .}
 
 type
-  GenericIntType {.importcpp: "GenericIntType<'0, '1>".} [N: static[int]; T] = object
+  GenericIntType[N: static[int]; T] {.importcpp: "GenericIntType<'0, '1>".} = object
     data: array[N, T]
 
-  GenericTType {.importcpp: "GenericTType<'0>".} [T] = object
+  GenericIntTypeAlt[N: static[int]; T] {.importcpp: "GenericIntType".} = object
+
+  GenericTType[T] {.importcpp: "GenericTType<'0>".} = object
     field: T
 
   GenInt4 = GenericIntType[4, int]
@@ -40,6 +42,7 @@ var
   c = GenericTType[float]()
   d = SimpleStruct(field: 15)
   e = GenericTType[string](field: "test")
+  f = GenericIntTypeAlt[4, int8]()
 
 a.data[2] = 10
 b.field = 5
@@ -57,3 +60,4 @@ proc plus(a, b: GenInt4): GenInt4 =
 
 echo plus(a, a).data
 
+echo sizeof(f)
diff --git a/tests/statictypes/tstaticprocparams.nim b/tests/statictypes/tstaticprocparams.nim
new file mode 100644
index 000000000..75d8288ac
--- /dev/null
+++ b/tests/statictypes/tstaticprocparams.nim
@@ -0,0 +1,16 @@
+proc consumer[T: static proc(i: int): int{.nimcall.}](i: int): int = T(i)
+proc consumer(T: static proc(i: int): int{.nimcall.}, i: int): int = T(i)
+
+proc addIt(i: int): int = i + i
+proc add(i: int): int = i + i # Checks if we can use overloads
+proc squareIt(i: int): int = i * i
+
+assert consumer[addIt](10) == 20
+assert consumer[add](10) == 20
+assert consumer[squareIt](30) == 900
+assert consumer[proc(i: int): int{.nimcall.} = i * i + i](10) == 110
+
+assert consumer(addIt, 10) == 20
+assert consumer(add, 10) == 20
+assert consumer(squareIt, 30) == 900
+assert consumer(proc(i: int): int{.nimcall.} = i * i + i, 10) == 110
diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim
new file mode 100644
index 000000000..572b8c4a5
--- /dev/null
+++ b/tests/statictypes/tstatictypes.nim
@@ -0,0 +1,459 @@
+discard """
+nimoutFull: true
+nimout: '''
+staticAlialProc instantiated with 358
+staticAlialProc instantiated with 368
+0: Foo
+1: Bar
+0: Foo
+1: Bar
+0: Foo
+1: Bar
+0: Foo
+1: Bar
+'''
+output: '''
+16
+16
+b is 2 times a
+17
+['\x00', '\x00', '\x00', '\x00']
+heyho
+Val1
+Val1
+'''
+matrix: "--hints:off --mm:orc; --hints:off --mm:refc"
+"""
+
+import macros
+
+template ok(x) = doAssert(x)
+template no(x) = doAssert(not x)
+
+template accept(x) =
+  static: doAssert(compiles(x))
+
+template reject(x) =
+  static: doAssert(not compiles(x))
+
+proc plus(a, b: int): int = a + b
+
+template isStatic(x: static): bool = true
+template isStatic(x: auto): bool = false
+
+var v = 1
+
+when true:
+  # test that `isStatic` works as expected
+  const C = 2
+
+  static:
+    ok C.isStatic
+    ok isStatic(plus(1, 2))
+    ok plus(C, 2).isStatic
+
+    no isStatic(v)
+    no plus(1, v).isStatic
+
+when true:
+  # test that proc instantiation works as expected
+  type
+    StaticTypeAlias = static[int]
+
+  proc staticAliasProc(a: StaticTypeAlias,
+                       b: static[int],
+                       c: static int) =
+    static:
+      doAssert a.isStatic and b.isStatic and c.isStatic
+      doAssert isStatic(a + plus(b, c))
+      echo "staticAlialProc instantiated with ", a, b, c
+
+    when b mod a == 0:
+      echo "b is ", b div a, " times a"
+
+    echo a + b + c
+
+  staticAliasProc 1+2, 5, 8
+  staticAliasProc 3, 2+3, 9-1
+  staticAliasProc 3, 3+3, 4+4
+
+when true:
+  # test static coercions. normal cases that should work:
+  accept:
+    var s1 = static[int] plus(1, 2)
+    var s2 = static(plus(1,2))
+    var s3 = static plus(1,2)
+    var s4 = static[SomeInteger](1 + 2)
+
+  # the sub-script operator can be used only with types:
+  reject:
+    var just_static3 = static[plus(1,2)]
+
+  # static coercion takes into account the type:
+  reject:
+    var x = static[string](plus(1, 2))
+  reject:
+    var x = static[string] plus(1, 2)
+  reject:
+    var x = static[SomeFloat] plus(3, 4)
+
+  # you cannot coerce a run-time variable
+  reject:
+    var x = static(v)
+
+block: # issue #13730
+  type Foo[T: static[float]] = object
+  doAssert Foo[0.0] is Foo[-0.0]
+
+when true:
+  type
+    ArrayWrapper1[S: static int] = object
+      data: array[S + 1, int]
+
+    ArrayWrapper2[S: static[int]] = object
+      data: array[S.plus(2), int]
+
+    ArrayWrapper3[S: static[(int, string)]] = object
+      data: array[S[0], int]
+
+  var aw1: ArrayWrapper1[5]
+  var aw2: ArrayWrapper2[5]
+  var aw3: ArrayWrapper3[(10, "str")]
+
+  static:
+    doAssert aw1.data.high == 5
+    doAssert aw2.data.high == 6
+    doAssert aw3.data.high == 9
+
+# #6077
+block:
+  type
+    Backend = enum
+      Cpu
+
+    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]
+
+# https://github.com/nim-lang/Nim/issues/10339
+block:
+  type
+    MicroKernel = object
+      a: float
+      b: int
+
+  macro extractA(ukernel: static MicroKernel): untyped =
+    result = newLit ukernel.a
+
+  proc tFunc[ukernel: static MicroKernel]() =
+    const x = ukernel.extractA
+    doAssert x == 5.5
+
+  const uk = MicroKernel(a: 5.5, b: 1)
+  tFunc[uk]()
+
+
+# bug #7258
+type
+  StringValue*[LEN: static[Natural]] = array[LEN+Natural(2),char]
+  StringValue16* = StringValue[2]
+
+var
+  s: StringValue16
+
+echo s
+
+block: #13529
+  block:
+    type Foo[T: static type] = object
+    var foo: Foo["test"]
+    doAssert $foo == "()"
+    doAssert foo.T is string
+    static: doAssert foo.T == "test"
+    doAssert not compiles(
+      block:
+        type Foo2[T: static type] = object
+          x: T)
+
+  block:
+    type Foo[T: static[float]] = object
+    var foo: Foo[1.2]
+    doAssert $foo == "()"
+    doAssert foo.T == 1.2
+
+  block: # routines also work
+    proc fun(a: static) = (const a2 = a)
+    fun(1)
+    fun(1.2)
+  block: # routines also work
+    proc fun(a: static type) = (const a2 = a)
+    fun(1)
+    fun(1.2)
+
+  block: # this also works
+    proc fun[T](a: static[T]) = (const a2 = a)
+    fun(1)
+    fun(1.2)
+
+block: # #12713
+  block:
+    type Cell = object
+      c: int
+    proc test(c: static string) = discard #Remove this and it compiles
+    proc test(c: Cell) = discard
+    test Cell(c: 0)
+  block:
+    type Cell = object
+      c: int
+    proc test(c: static string) = discard #Remove this and it compiles
+    proc test(c: Cell) = discard
+    test Cell()
+
+block: # issue #14802
+  template fn(s: typed): untyped =
+    proc bar() = discard
+    12
+  const myConst = static(fn(1))
+  doAssert myConst == 12
+
+
+# bug #12571
+type
+  T[K: static bool] = object of RootObj
+    when K == true:
+      foo: string
+    else:
+      bar: string
+  U[K: static bool] = object of T[K]
+
+let t = T[true](foo: "hey")
+let u = U[false](bar: "ho")
+echo t.foo, u.bar
+
+
+#------------------------------------------------------------------------------
+# issue #9679
+
+type
+  Foo*[T] = object
+    bar*: int
+    dummy: T
+
+proc initFoo(T: type, bar: int): Foo[T] =
+  result.bar = 1
+
+proc fails[T](x: static Foo[T]) = # Change to non-static and it compiles
+  doAssert($x == "(bar: 1, dummy: 0)")
+
+block:
+  const foo = initFoo(int, 2)
+  fails(foo)
+
+
+import tables
+
+var foo{.compileTime.} = [
+  "Foo",
+  "Bar"
+]
+
+var bar{.compileTime.} = {
+  0: "Foo",
+  1: "Bar"
+}.toTable()
+
+macro fooM(): untyped =
+  for i, val in foo:
+    echo i, ": ", val
+
+macro barM(): untyped =
+  for i, val in bar:
+    echo i, ": ", val
+
+macro fooParam(x: static array[2, string]): untyped =
+  for i, val in x:
+    echo i, ": ", val
+
+macro barParam(x: static Table[int, string]): untyped =
+  let barParamInsides = proc(i: int, val: string): NimNode =
+    echo i, ": ", val
+  for i, val in x:
+    discard barParamInsides(i, val)
+
+fooM()
+barM()
+fooParam(foo)
+barParam(bar)
+
+
+#-----------------------------------------------------------------------------------------
+# issue #7546
+type
+  rangeB[N: static[int16]] = range[0'i16 .. N]
+  setB[N: static[int16]] = set[rangeB[N]]
+
+block:
+  var s : setB[14'i16]
+
+
+#-----------------------------------------------------------------------------------------
+# issue #9520
+
+type
+  MyEnum = enum
+    Val1, Val2
+
+proc myproc(a: static[MyEnum], b: int) =
+  if b < 0:
+    myproc(a, -b)
+
+  echo $a
+
+myproc(Val1, -10)
+
+
+#------------------------------------------------------------------------------------------
+# issue #6177
+
+type                                                                                                 
+  G[N,M:static[int], T] = object                                                                      
+    o: T                                                                                             
+                                                                                                     
+proc newG[N,M:static[int],T](x:var G[N,M,T], y:T) =                                                  
+  x.o = y+10*N+100*M                                                                                 
+                                                                                                     
+proc newG[N,M:static[int],T](x:T):G[N,M,T] = result.newG(x)                                          
+                                                                                                     
+var x:G[2,3,int]                                                                                     
+x.newG(4)                                                                                            
+var y = newG[2,3,int](4)
+
+
+#------------------------------------------------------------------------------------------
+# issue #12897
+
+type
+  TileCT[n: static int] = object
+    a: array[n, int]
+  Tile = TileCT #Commenting this out to make it work
+
+
+#------------------------------------------------------------------------------------------
+# issue #15858
+
+proc fn(N1: static int, N2: static int, T: typedesc): array[N1 * N2, T] = 
+  doAssert(len(result) == N1 * N2)
+
+let yy = fn(5, 10, float)
+
+
+block:
+  block:
+    type Foo[N: static int] = array[cint(0) .. cint(N), float]
+    type T = Foo[3]
+  block:
+    type Foo[N: static int] = array[int32(0) .. int32(N), float]
+    type T = Foo[3]
+
+
+#------------------------------------------------------------------------------------------
+# static proc/lambda param
+func isSorted2[T](a: openArray[T], cmp: static proc(x, y: T): bool {.inline.}): bool =
+  result = true
+  for i in 0..<len(a)-1:
+    if not cmp(a[i], a[i+1]):
+      return false
+
+proc compare(a, b: int): bool {.inline.} = a < b
+
+var sorted = newSeq[int](1000)
+for i in 0..<sorted.len: sorted[i] = i*2
+doAssert isSorted2(sorted, compare)
+doAssert isSorted2(sorted, proc (a, b: int): bool {.inline.} = a < b)
+
+
+block: # Ensure static descriminated objects compile
+  type
+    ObjKind = enum
+      KindA, KindB, KindC
+
+    MyObject[kind: static[ObjKind]] = object of RootObj
+      myNumber: int
+      when kind != KindA:
+        driverType: int
+        otherField: int
+      elif kind == KindC:
+        driverType: uint
+        otherField: int
+
+  var instance: MyObject[KindA]
+  discard instance
+  discard MyObject[KindC]()
+
+block: # more cases of above, issue #8446
+  type
+    Color = enum
+      red, green, blue
+    Blah[color: static[Color]] = object
+      when color == red:
+        a: string
+      else:
+        b: int
+
+  proc foo(blah: Blah) = discard
+  foo(Blah[red](a: "abc"))
+
+  type
+    Mytype[K: static[int]] = object
+      when K < 16:
+        data: uint8
+      else:
+        data: uint64
+  proc usingMyt(k: Mytype) = discard  # triggers Error: cannot generate code for: K
+
+block: # bug #22600
+  proc f(n: static int): int = n * 2 # same for template
+
+  type
+    a[N: static int] = object
+      field : array[N, uint8]
+
+    b[N: static int] = object
+      field : a[N]
+
+    c[N: static int] = object
+      f0    : a[N     ]         # works
+      f1    : a[N + 1 ]         # asserts
+      f2    : a[f(N)  ]         # asserts
+
+      f3    : b[N     ]         # works
+      f4    : b[N + 1 ]         # asserts
+      f5    : b[f(N)  ]         # asserts
+
+  proc init[N: static int](x : var a[N]) = discard
+  proc init[N: static int](x : var b[N]) = discard
+  proc init[N: static int](x : var c[N]) = x.f1.init() # this is needed
+
+  var x: c[2]
+  x.init()
diff --git a/tests/stdlib/concurrency/atomicSample.nim b/tests/stdlib/concurrency/atomicSample.nim
new file mode 100644
index 000000000..d56d867df
--- /dev/null
+++ b/tests/stdlib/concurrency/atomicSample.nim
@@ -0,0 +1,9 @@
+import atomics
+
+type
+  AtomicWithGeneric*[T] = object
+    value: Atomic[T]
+
+proc initAtomicWithGeneric*[T](value: T): AtomicWithGeneric[T] =
+  result.value.store(value)
+
diff --git a/tests/stdlib/concurrency/tatomic_import.nim b/tests/stdlib/concurrency/tatomic_import.nim
new file mode 100644
index 000000000..e8faaae20
--- /dev/null
+++ b/tests/stdlib/concurrency/tatomic_import.nim
@@ -0,0 +1,11 @@
+import atomicSample
+
+block crossFileObjectContainingAGenericWithAComplexObject:
+  discard initAtomicWithGeneric[string]("foo")
+
+block crossFileObjectContainingAGenericWithAnInteger:
+  discard initAtomicWithGeneric[int](1)
+  discard initAtomicWithGeneric[int8](1)
+  discard initAtomicWithGeneric[int16](1)
+  discard initAtomicWithGeneric[int32](1)
+  discard initAtomicWithGeneric[int64](1)
diff --git a/tests/stdlib/concurrency/tatomics.nim b/tests/stdlib/concurrency/tatomics.nim
new file mode 100644
index 000000000..08f2e7d3e
--- /dev/null
+++ b/tests/stdlib/concurrency/tatomics.nim
@@ -0,0 +1,637 @@
+discard """
+  # test C with -d:nimUseCppAtomics as well to check nothing breaks
+  matrix: "--mm:refc; --mm:orc; --mm:refc -d:nimUseCppAtomics; --mm:orc -d:nimUseCppAtomics"
+  targets: "c cpp"
+"""
+
+# test atomic operations
+
+import std/[atomics, bitops]
+import std/assertions
+
+
+type
+  Object = object
+    val: int
+
+
+# Atomic operations for trivial objects
+
+
+block trivialLoad:
+  var location: Atomic[int]
+  location.store(1)
+  doAssert location.load == 1
+  location.store(2)
+  doAssert location.load(moRelaxed) == 2
+  location.store(3)
+  doAssert location.load(moAcquire) == 3
+
+
+block trivialStore:
+  var location: Atomic[int]
+  location.store(1)
+  doAssert location.load == 1
+  location.store(2, moRelaxed)
+  doAssert location.load == 2
+  location.store(3, moRelease)
+  doAssert location.load == 3
+
+
+block trivialExchange:
+  var location: Atomic[int]
+  location.store(1)
+  doAssert location.exchange(2) == 1
+  doAssert location.exchange(3, moRelaxed) == 2
+  doAssert location.exchange(4, moAcquire) == 3
+  doAssert location.exchange(5, moRelease) == 4
+  doAssert location.exchange(6, moAcquireRelease) == 5
+  doAssert location.load == 6
+
+
+block trivialCompareExchangeDoesExchange:
+  var location: Atomic[int]
+  var expected = 1
+  location.store(1)
+  doAssert location.compareExchange(expected, 2)
+  doAssert expected == 1
+  doAssert location.load == 2
+  expected = 2
+  doAssert location.compareExchange(expected, 3, moRelaxed)
+  doAssert expected == 2
+  doAssert location.load == 3
+  expected = 3
+  doAssert location.compareExchange(expected, 4, moAcquire)
+  doAssert expected == 3
+  doAssert location.load == 4
+  expected = 4
+  doAssert location.compareExchange(expected, 5, moRelease)
+  doAssert expected == 4
+  doAssert location.load == 5
+  expected = 5
+  doAssert location.compareExchange(expected, 6, moAcquireRelease)
+  doAssert expected == 5
+  doAssert location.load == 6
+
+
+block trivialCompareExchangeDoesNotExchange:
+  var location: Atomic[int]
+  var expected = 10
+  location.store(1)
+  doAssert not location.compareExchange(expected, 2)
+  doAssert expected == 1
+  doAssert location.load == 1
+  expected = 10
+  doAssert not location.compareExchange(expected, 3, moRelaxed)
+  doAssert expected == 1
+  doAssert location.load == 1
+  expected = 10
+  doAssert not location.compareExchange(expected, 4, moAcquire)
+  doAssert expected == 1
+  doAssert location.load == 1
+  expected = 10
+  doAssert not location.compareExchange(expected, 5, moRelease)
+  doAssert expected == 1
+  doAssert location.load == 1
+  expected = 10
+  doAssert not location.compareExchange(expected, 6, moAcquireRelease)
+  doAssert expected == 1
+  doAssert location.load == 1
+
+
+block trivialCompareExchangeSuccessFailureDoesExchange:
+  var location: Atomic[int]
+  var expected = 1
+  location.store(1)
+  doAssert location.compareExchange(expected, 2, moSequentiallyConsistent, moSequentiallyConsistent)
+  doAssert expected == 1
+  doAssert location.load == 2
+  expected = 2
+  doAssert location.compareExchange(expected, 3, moRelaxed, moRelaxed)
+  doAssert expected == 2
+  doAssert location.load == 3
+  expected = 3
+  doAssert location.compareExchange(expected, 4, moAcquire, moAcquire)
+  doAssert expected == 3
+  doAssert location.load == 4
+  expected = 4
+  doAssert location.compareExchange(expected, 5, moRelease, moRelease)
+  doAssert expected == 4
+  doAssert location.load == 5
+  expected = 5
+  doAssert location.compareExchange(expected, 6, moAcquireRelease, moAcquireRelease)
+  doAssert expected == 5
+  doAssert location.load == 6
+
+
+block trivialCompareExchangeSuccessFailureDoesNotExchange:
+  var location: Atomic[int]
+  var expected = 10
+  location.store(1)
+  doAssert not location.compareExchange(expected, 2, moSequentiallyConsistent, moSequentiallyConsistent)
+  doAssert expected == 1
+  doAssert location.load == 1
+  expected = 10
+  doAssert not location.compareExchange(expected, 3, moRelaxed, moRelaxed)
+  doAssert expected == 1
+  doAssert location.load == 1
+  expected = 10
+  doAssert not location.compareExchange(expected, 4, moAcquire, moAcquire)
+  doAssert expected == 1
+  doAssert location.load == 1
+  expected = 10
+  doAssert not location.compareExchange(expected, 5, moRelease, moRelease)
+  doAssert expected == 1
+  doAssert location.load == 1
+  expected = 10
+  doAssert not location.compareExchange(expected, 6, moAcquireRelease, moAcquireRelease)
+  doAssert expected == 1
+  doAssert location.load == 1
+
+
+block trivialCompareExchangeWeakDoesExchange:
+  var location: Atomic[int]
+  var expected = 1
+  location.store(1)
+  doAssert location.compareExchangeWeak(expected, 2)
+  doAssert expected == 1
+  doAssert location.load == 2
+  expected = 2
+  doAssert location.compareExchangeWeak(expected, 3, moRelaxed)
+  doAssert expected == 2
+  doAssert location.load == 3
+  expected = 3
+  doAssert location.compareExchangeWeak(expected, 4, moAcquire)
+  doAssert expected == 3
+  doAssert location.load == 4
+  expected = 4
+  doAssert location.compareExchangeWeak(expected, 5, moRelease)
+  doAssert expected == 4
+  doAssert location.load == 5
+  expected = 5
+  doAssert location.compareExchangeWeak(expected, 6, moAcquireRelease)
+  doAssert expected == 5
+  doAssert location.load == 6
+
+
+block trivialCompareExchangeWeakDoesNotExchange:
+  var location: Atomic[int]
+  var expected = 10
+  location.store(1)
+  doAssert not location.compareExchangeWeak(expected, 2)
+  doAssert expected == 1
+  doAssert location.load == 1
+  expected = 10
+  doAssert not location.compareExchangeWeak(expected, 3, moRelaxed)
+  doAssert expected == 1
+  doAssert location.load == 1
+  expected = 10
+  doAssert not location.compareExchangeWeak(expected, 4, moAcquire)
+  doAssert expected == 1
+  doAssert location.load == 1
+  expected = 10
+  doAssert not location.compareExchangeWeak(expected, 5, moRelease)
+  doAssert expected == 1
+  doAssert location.load == 1
+  expected = 10
+  doAssert not location.compareExchangeWeak(expected, 6, moAcquireRelease)
+  doAssert expected == 1
+  doAssert location.load == 1
+
+
+block trivialCompareExchangeWeakSuccessFailureDoesExchange:
+  var location: Atomic[int]
+  var expected = 1
+  location.store(1)
+  doAssert location.compareExchangeWeak(expected, 2, moSequentiallyConsistent, moSequentiallyConsistent)
+  doAssert expected == 1
+  doAssert location.load == 2
+  expected = 2
+  doAssert location.compareExchangeWeak(expected, 3, moRelaxed, moRelaxed)
+  doAssert expected == 2
+  doAssert location.load == 3
+  expected = 3
+  doAssert location.compareExchangeWeak(expected, 4, moAcquire, moAcquire)
+  doAssert expected == 3
+  doAssert location.load == 4
+  expected = 4
+  doAssert location.compareExchangeWeak(expected, 5, moRelease, moRelease)
+  doAssert expected == 4
+  doAssert location.load == 5
+  expected = 5
+  doAssert location.compareExchangeWeak(expected, 6, moAcquireRelease, moAcquireRelease)
+  doAssert expected == 5
+  doAssert location.load == 6
+
+
+block trivialCompareExchangeWeakSuccessFailureDoesNotExchange:
+  var location: Atomic[int]
+  var expected = 10
+  location.store(1)
+  doAssert not location.compareExchangeWeak(expected, 2, moSequentiallyConsistent, moSequentiallyConsistent)
+  doAssert expected == 1
+  doAssert location.load == 1
+  expected = 10
+  doAssert not location.compareExchangeWeak(expected, 3, moRelaxed, moRelaxed)
+  doAssert expected == 1
+  doAssert location.load == 1
+  expected = 10
+  doAssert not location.compareExchangeWeak(expected, 4, moAcquire, moAcquire)
+  doAssert expected == 1
+  doAssert location.load == 1
+  expected = 10
+  doAssert not location.compareExchangeWeak(expected, 5, moRelease, moRelease)
+  doAssert expected == 1
+  doAssert location.load == 1
+  expected = 10
+  doAssert not location.compareExchangeWeak(expected, 6, moAcquireRelease, moAcquireRelease)
+  doAssert expected == 1
+  doAssert location.load == 1
+
+
+# Atomic operations for non-trivial objects
+
+
+block objectLoad:
+  var location: Atomic[Object]
+  location.store(Object(val: 1))
+  doAssert location.load == Object(val: 1)
+  location.store(Object(val: 2))
+  doAssert location.load(moRelaxed) == Object(val: 2)
+  location.store(Object(val: 3))
+  doAssert location.load(moAcquire) == Object(val: 3)
+
+
+block objectStore:
+  var location: Atomic[Object]
+  location.store(Object(val: 1))
+  doAssert location.load == Object(val: 1)
+  location.store(Object(val: 2), moRelaxed)
+  doAssert location.load == Object(val: 2)
+  location.store(Object(val: 3), moRelease)
+  doAssert location.load == Object(val: 3)
+
+
+block objectExchange:
+  var location: Atomic[Object]
+  location.store(Object(val: 1))
+  doAssert location.exchange(Object(val: 2)) == Object(val: 1)
+  doAssert location.exchange(Object(val: 3), moRelaxed) == Object(val: 2)
+  doAssert location.exchange(Object(val: 4), moAcquire) == Object(val: 3)
+  doAssert location.exchange(Object(val: 5), moRelease) == Object(val: 4)
+  doAssert location.exchange(Object(val: 6), moAcquireRelease) == Object(val: 5)
+  doAssert location.load == Object(val: 6)
+
+
+block objectCompareExchangeDoesExchange:
+  var location: Atomic[Object]
+  var expected = Object(val: 1)
+  location.store(Object(val: 1))
+  doAssert location.compareExchange(expected, Object(val: 2))
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 2)
+  expected = Object(val: 2)
+  doAssert location.compareExchange(expected, Object(val: 3), moRelaxed)
+  doAssert expected == Object(val: 2)
+  doAssert location.load == Object(val: 3)
+  expected = Object(val: 3)
+  doAssert location.compareExchange(expected, Object(val: 4), moAcquire)
+  doAssert expected == Object(val: 3)
+  doAssert location.load == Object(val: 4)
+  expected = Object(val: 4)
+  doAssert location.compareExchange(expected, Object(val: 5), moRelease)
+  doAssert expected == Object(val: 4)
+  doAssert location.load == Object(val: 5)
+  expected = Object(val: 5)
+  doAssert location.compareExchange(expected, Object(val: 6), moAcquireRelease)
+  doAssert expected == Object(val: 5)
+  doAssert location.load == Object(val: 6)
+
+
+block objectCompareExchangeDoesNotExchange:
+  var location: Atomic[Object]
+  var expected = Object(val: 10)
+  location.store(Object(val: 1))
+  doAssert not location.compareExchange(expected, Object(val: 2))
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+  expected = Object(val: 10)
+  doAssert not location.compareExchange(expected, Object(val: 3), moRelaxed)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+  expected = Object(val: 10)
+  doAssert not location.compareExchange(expected, Object(val: 4), moAcquire)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+  expected = Object(val: 10)
+  doAssert not location.compareExchange(expected, Object(val: 5), moRelease)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+  expected = Object(val: 10)
+  doAssert not location.compareExchange(expected, Object(val: 6), moAcquireRelease)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+
+
+block objectCompareExchangeSuccessFailureDoesExchange:
+  var location: Atomic[Object]
+  var expected = Object(val: 1)
+  location.store(Object(val: 1))
+  doAssert location.compareExchange(expected, Object(val: 2), moSequentiallyConsistent, moSequentiallyConsistent)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 2)
+  expected = Object(val: 2)
+  doAssert location.compareExchange(expected, Object(val: 3), moRelaxed, moRelaxed)
+  doAssert expected == Object(val: 2)
+  doAssert location.load == Object(val: 3)
+  expected = Object(val: 3)
+  doAssert location.compareExchange(expected, Object(val: 4), moAcquire, moAcquire)
+  doAssert expected == Object(val: 3)
+  doAssert location.load == Object(val: 4)
+  expected = Object(val: 4)
+  doAssert location.compareExchange(expected, Object(val: 5), moRelease, moRelease)
+  doAssert expected == Object(val: 4)
+  doAssert location.load == Object(val: 5)
+  expected = Object(val: 5)
+  doAssert location.compareExchange(expected, Object(val: 6), moAcquireRelease, moAcquireRelease)
+  doAssert expected == Object(val: 5)
+  doAssert location.load == Object(val: 6)
+
+
+block objectCompareExchangeSuccessFailureDoesNotExchange:
+  var location: Atomic[Object]
+  var expected = Object(val: 10)
+  location.store(Object(val: 1))
+  doAssert not location.compareExchange(expected, Object(val: 2), moSequentiallyConsistent, moSequentiallyConsistent)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+  expected = Object(val: 10)
+  doAssert not location.compareExchange(expected, Object(val: 3), moRelaxed, moRelaxed)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+  expected = Object(val: 10)
+  doAssert not location.compareExchange(expected, Object(val: 4), moAcquire, moAcquire)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+  expected = Object(val: 10)
+  doAssert not location.compareExchange(expected, Object(val: 5), moRelease, moRelease)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+  expected = Object(val: 10)
+  doAssert not location.compareExchange(expected, Object(val: 6), moAcquireRelease, moAcquireRelease)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+
+
+block objectCompareExchangeWeakDoesExchange:
+  var location: Atomic[Object]
+  var expected = Object(val: 1)
+  location.store(Object(val: 1))
+  doAssert location.compareExchangeWeak(expected, Object(val: 2))
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 2)
+  expected = Object(val: 2)
+  doAssert location.compareExchangeWeak(expected, Object(val: 3), moRelaxed)
+  doAssert expected == Object(val: 2)
+  doAssert location.load == Object(val: 3)
+  expected = Object(val: 3)
+  doAssert location.compareExchangeWeak(expected, Object(val: 4), moAcquire)
+  doAssert expected == Object(val: 3)
+  doAssert location.load == Object(val: 4)
+  expected = Object(val: 4)
+  doAssert location.compareExchangeWeak(expected, Object(val: 5), moRelease)
+  doAssert expected == Object(val: 4)
+  doAssert location.load == Object(val: 5)
+  expected = Object(val: 5)
+  doAssert location.compareExchangeWeak(expected, Object(val: 6), moAcquireRelease)
+  doAssert expected == Object(val: 5)
+  doAssert location.load == Object(val: 6)
+
+
+block objectCompareExchangeWeakDoesNotExchange:
+  var location: Atomic[Object]
+  var expected = Object(val: 10)
+  location.store(Object(val: 1))
+  doAssert not location.compareExchangeWeak(expected, Object(val: 2))
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+  expected = Object(val: 10)
+  doAssert not location.compareExchangeWeak(expected, Object(val: 3), moRelaxed)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+  expected = Object(val: 10)
+  doAssert not location.compareExchangeWeak(expected, Object(val: 4), moAcquire)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+  expected = Object(val: 10)
+  doAssert not location.compareExchangeWeak(expected, Object(val: 5), moRelease)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+  expected = Object(val: 10)
+  doAssert not location.compareExchangeWeak(expected, Object(val: 6), moAcquireRelease)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+
+
+block objectCompareExchangeWeakSuccessFailureDoesExchange:
+  var location: Atomic[Object]
+  var expected = Object(val: 1)
+  location.store(Object(val: 1))
+  doAssert location.compareExchangeWeak(expected, Object(val: 2), moSequentiallyConsistent, moSequentiallyConsistent)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 2)
+  expected = Object(val: 2)
+  doAssert location.compareExchangeWeak(expected, Object(val: 3), moRelaxed, moRelaxed)
+  doAssert expected == Object(val: 2)
+  doAssert location.load == Object(val: 3)
+  expected = Object(val: 3)
+  doAssert location.compareExchangeWeak(expected, Object(val: 4), moAcquire, moAcquire)
+  doAssert expected == Object(val: 3)
+  doAssert location.load == Object(val: 4)
+  expected = Object(val: 4)
+  doAssert location.compareExchangeWeak(expected, Object(val: 5), moRelease, moRelease)
+  doAssert expected == Object(val: 4)
+  doAssert location.load == Object(val: 5)
+  expected = Object(val: 5)
+  doAssert location.compareExchangeWeak(expected, Object(val: 6), moAcquireRelease, moAcquireRelease)
+  doAssert expected == Object(val: 5)
+  doAssert location.load == Object(val: 6)
+
+
+block objectCompareExchangeWeakSuccessFailureDoesNotExchange:
+  var location: Atomic[Object]
+  var expected = Object(val: 10)
+  location.store(Object(val: 1))
+  doAssert not location.compareExchangeWeak(expected, Object(val: 2), moSequentiallyConsistent, moSequentiallyConsistent)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+  expected = Object(val: 10)
+  doAssert not location.compareExchangeWeak(expected, Object(val: 3), moRelaxed, moRelaxed)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+  expected = Object(val: 10)
+  doAssert not location.compareExchangeWeak(expected, Object(val: 4), moAcquire, moAcquire)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+  expected = Object(val: 10)
+  doAssert not location.compareExchangeWeak(expected, Object(val: 5), moRelease, moRelease)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+  expected = Object(val: 10)
+  doAssert not location.compareExchangeWeak(expected, Object(val: 6), moAcquireRelease, moAcquireRelease)
+  doAssert expected == Object(val: 1)
+  doAssert location.load == Object(val: 1)
+
+
+# Numerical operations
+
+
+block fetchAdd:
+  var location: Atomic[int]
+  doAssert location.fetchAdd(1) == 0
+  doAssert location.fetchAdd(1, moRelaxed) == 1
+  doAssert location.fetchAdd(1, moRelease) == 2
+  doAssert location.load == 3
+
+
+block fetchSub:
+  var location: Atomic[int]
+  doAssert location.fetchSub(1) == 0
+  doAssert location.fetchSub(1, moRelaxed) == -1
+  doAssert location.fetchSub(1, moRelease) == -2
+  doAssert location.load == -3
+
+
+block fetchAnd:
+  var location: Atomic[int]
+
+  for i in 0..16:
+    for j in 0..16:
+      location.store(i)
+      doAssert(location.fetchAnd(j) == i)
+      doAssert(location.load == i.bitand(j))
+
+  for i in 0..16:
+    for j in 0..16:
+      location.store(i)
+      doAssert(location.fetchAnd(j, moRelaxed) == i)
+      doAssert(location.load == i.bitand(j))
+
+  for i in 0..16:
+    for j in 0..16:
+      location.store(i)
+      doAssert(location.fetchAnd(j, moRelease) == i)
+      doAssert(location.load == i.bitand(j))
+
+
+block fetchOr:
+  var location: Atomic[int]
+
+  for i in 0..16:
+    for j in 0..16:
+      location.store(i)
+      doAssert(location.fetchOr(j) == i)
+      doAssert(location.load == i.bitor(j))
+
+  for i in 0..16:
+    for j in 0..16:
+      location.store(i)
+      doAssert(location.fetchOr(j, moRelaxed) == i)
+      doAssert(location.load == i.bitor(j))
+
+  for i in 0..16:
+    for j in 0..16:
+      location.store(i)
+      doAssert(location.fetchOr(j, moRelease) == i)
+      doAssert(location.load == i.bitor(j))
+
+
+block fetchXor:
+  var location: Atomic[int]
+
+  for i in 0..16:
+    for j in 0..16:
+      location.store(i)
+      doAssert(location.fetchXor(j) == i)
+      doAssert(location.load == i.bitxor(j))
+
+  for i in 0..16:
+    for j in 0..16:
+      location.store(i)
+      doAssert(location.fetchXor(j, moRelaxed) == i)
+      doAssert(location.load == i.bitxor(j))
+
+  for i in 0..16:
+    for j in 0..16:
+      location.store(i)
+      doAssert(location.fetchXor(j, moRelease) == i)
+      doAssert(location.load == i.bitxor(j))
+
+
+block atomicInc:
+  var location: Atomic[int]
+  location.atomicInc
+  doAssert location.load == 1
+  location.atomicInc(1)
+  doAssert location.load == 2
+  location += 1
+  doAssert location.load == 3
+
+
+block atomicDec:
+  var location: Atomic[int]
+  location.atomicDec
+  doAssert location.load == -1
+  location.atomicDec(1)
+  doAssert location.load == -2
+  location -= 1
+  doAssert location.load == -3
+
+
+# Flag operations
+
+
+block testAndSet:
+  var location: AtomicFlag
+  doAssert not location.testAndSet
+  doAssert location.testAndSet
+  doAssert location.testAndSet
+  location.clear()
+  doAssert not location.testAndSet(moRelaxed)
+  doAssert location.testAndSet(moRelaxed)
+  doAssert location.testAndSet(moRelaxed)
+  location.clear()
+  doAssert not location.testAndSet(moRelease)
+  doAssert location.testAndSet(moRelease)
+  doAssert location.testAndSet(moRelease)
+
+
+block clear:
+  var location: AtomicFlag
+  discard location.testAndSet
+  location.clear
+  doAssert not location.testAndSet
+  location.clear(moRelaxed)
+  doAssert not location.testAndSet
+  location.clear(moRelease)
+  doAssert not location.testAndSet
+
+block: # bug #18844
+  when not defined(cpp): # cpp pending pr #18836
+    type
+      Deprivation = object of RootObj
+        memes: Atomic[int]
+      Zoomer = object
+        dopamine: Deprivation
+
+    block:
+      var x = Deprivation()
+      var y = Zoomer()
+      doAssert x.memes.load == 0
+      doAssert y.dopamine.memes.load == 0
+
+    block:
+      var x: Deprivation
+      var y: Zoomer
+      doAssert x.memes.load == 0
+      doAssert y.dopamine.memes.load == 0
diff --git a/tests/stdlib/concurrency/tatomics_size.nim b/tests/stdlib/concurrency/tatomics_size.nim
new file mode 100644
index 000000000..f64adb308
--- /dev/null
+++ b/tests/stdlib/concurrency/tatomics_size.nim
@@ -0,0 +1,21 @@
+discard """
+  # test C with -d:nimUseCppAtomics as well to check nothing breaks
+  matrix: "--mm:refc; --mm:orc; --mm:refc -d:nimUseCppAtomics; --mm:orc -d:nimUseCppAtomics"
+  targets: "c cpp"
+"""
+import std/atomics
+import std/assertions
+
+block testSize: # issue 12726
+  type
+    Node = ptr object
+      # works
+      next: Atomic[pointer]
+      f:AtomicFlag
+    MyChannel = object
+      # type not defined completely
+      back: Atomic[ptr int]
+      f: AtomicFlag
+  static:
+    doAssert sizeof(Node) == sizeof(pointer)
+    doAssert sizeof(MyChannel) == sizeof(pointer) * 2
diff --git a/tests/stdlib/config.nims b/tests/stdlib/config.nims
new file mode 100644
index 000000000..dffae2812
--- /dev/null
+++ b/tests/stdlib/config.nims
@@ -0,0 +1,5 @@
+switch("styleCheck", "usages")
+switch("styleCheck", "error")
+switch("define", "nimPreviewSlimSystem")
+switch("define", "nimPreviewCstringConversion")
+switch("define", "nimPreviewProcConversion")
diff --git a/tests/stdlib/genericstrformat.nim b/tests/stdlib/genericstrformat.nim
new file mode 100644
index 000000000..0446f3269
--- /dev/null
+++ b/tests/stdlib/genericstrformat.nim
@@ -0,0 +1,16 @@
+# from issue #7632
+# imported and used in tstrformat
+
+import strformat
+
+proc fails*(a: static[int]): string =
+  &"formatted {a:2}"
+
+proc fails2*[N: static[int]](a: int): string =
+  &"formatted {a:2}"
+
+proc works*(a: int): string =
+  &"formatted {a:2}"
+
+proc fails0*(a: int or uint): string =
+  &"formatted {a:2}"
diff --git a/tests/stdlib/mgenast.nim b/tests/stdlib/mgenast.nim
new file mode 100644
index 000000000..b0904847e
--- /dev/null
+++ b/tests/stdlib/mgenast.nim
@@ -0,0 +1,57 @@
+import std/genasts
+import std/macros
+
+# Using a enum instead of, say, int, to make apparent potential bugs related to
+# forgetting converting to NimNode via newLit, see bug #9607
+
+type Foo* = enum kfoo0, kfoo1, kfoo2, kfoo3, kfoo4
+
+proc myLocalPriv(): auto = kfoo1
+proc myLocalPriv2(): auto = kfoo1
+macro bindme2*(): untyped =
+  genAst: myLocalPriv()
+macro bindme3*(): untyped =
+  ## myLocalPriv must be captured explicitly
+  genAstOpt({kDirtyTemplate}, myLocalPriv): myLocalPriv()
+
+macro bindme4*(): untyped =
+  ## calling this won't compile because `myLocalPriv` isn't captured
+  genAstOpt({kDirtyTemplate}): myLocalPriv()
+
+macro bindme5UseExpose*(): untyped =
+  genAst: myLocalPriv2()
+
+macro bindme5UseExposeFalse*(): untyped =
+  genAstOpt({kDirtyTemplate}): myLocalPriv2()
+
+# example from bug #7889
+from std/streams import newStringStream, readData, writeData
+
+macro bindme6UseExpose*(): untyped =
+  genAst:
+    var tst = "sometext"
+    var ss = newStringStream("anothertext")
+    when defined(gcArc) or defined(gcOrc):
+      prepareMutation(tst)
+    writeData(ss, tst[0].addr, 2)
+    discard readData(ss, tst[0].addr, 2)
+
+macro bindme6UseExposeFalse*(): untyped =
+  ## with `kDirtyTemplate`, requires passing all referenced symbols
+  ## which can be tedious
+  genAstOpt({kDirtyTemplate}, newStringStream, writeData, readData):
+    var tst = "sometext"
+    var ss = newStringStream("anothertext")
+    when defined(gcArc) or defined(gcOrc):
+      prepareMutation(tst)
+    writeData(ss, tst[0].addr, 2)
+    discard readData(ss, tst[0].addr, 2)
+
+
+proc locafun1(): auto = "in locafun1"
+proc locafun2(): auto = "in locafun2"
+# locafun3 in caller scope only
+macro mixinExample*(): untyped =
+  genAst:
+    mixin locafun1
+    (locafun1(), locafun2(), locafun3())
diff --git a/tests/stdlib/mimportutils.nim b/tests/stdlib/mimportutils.nim
new file mode 100644
index 000000000..678d9ec02
--- /dev/null
+++ b/tests/stdlib/mimportutils.nim
@@ -0,0 +1,38 @@
+type
+  A* = object
+    a0*: int
+    ha1: float
+  B = object
+    b0*: int
+    hb1: float
+  C* = ref object
+    c0: int
+    hc1: float
+  D* = ptr object
+    d0: int
+    hd1: float
+  PA* = ref A
+  PtA* = ptr A
+  E*[T] = object
+    he1: int
+  FSub[T1, T2] = object
+    h3: T1
+    h4: T2
+  F*[T1, T2] = ref FSub[T1, T2]
+  G*[T] = ref E[T]
+  H3*[T] = object
+    h5: T
+  H2*[T] = H3[T]
+  H1*[T] = ref H2[T]
+  H*[T] = H1[T]
+
+  Pity[T] = object
+    a: T
+  PityRef*[T] = ref Pity[T]
+  Hope*[T] = ref object
+    a: T
+
+type BAalias* = typeof(B.default)
+  # typeof is not a transparent abstraction, creates a `tyAlias`
+
+proc initB*(): B = B()
diff --git a/tests/stdlib/mintsets.nim b/tests/stdlib/mintsets.nim
new file mode 100644
index 000000000..98786e9ba
--- /dev/null
+++ b/tests/stdlib/mintsets.nim
@@ -0,0 +1,11 @@
+import std/intsets
+import std/assertions
+
+proc test1*[]() =
+  let a = initIntSet()
+  doAssert len(a) == 0
+
+proc test2*[]() =
+  var a = initIntSet()
+  var b = initIntSet()
+  a.incl b
diff --git a/tests/stdlib/mstackframes.nim b/tests/stdlib/mstackframes.nim
new file mode 100644
index 000000000..f3daa1437
--- /dev/null
+++ b/tests/stdlib/mstackframes.nim
@@ -0,0 +1,38 @@
+import std/stackframes
+
+
+
+# line 5
+var count = 0
+
+proc main1(n: int) =
+  setFrameMsg $("main1", n)
+  if n > 0:
+    main1(n-1)
+
+proc main2(n: int) =
+  count.inc
+  setFrameMsg $("main2", n, count)
+  proc bar() =
+    setFrameMsg $("bar ",)
+    if n < 3: raise newException(CatchableError, "on purpose")
+  bar()
+  main2(n-1)
+
+proc main() =
+  var z = 0
+  setFrameMsg "\n  z: " & $z, prefix = ""
+  # multiple calls inside a frame are possible
+  z.inc
+  setFrameMsg "\n  z: " & $z, prefix = ""
+  try:
+    main2(5)
+  except CatchableError:
+    main1(10) # goes deep and then unwinds; sanity check to ensure `setFrameMsg` from inside
+              # `main1` won't invalidate the stacktrace; if StackTraceEntry.frameMsg
+              # were a reference instead of a copy, this would fail.
+    let e = getCurrentException()
+    let trace = e.getStackTrace
+    echo trace
+
+main()
diff --git a/tests/stdlib/nre/captures.nim b/tests/stdlib/nre/captures.nim
index 19c344a8d..acc141baf 100644
--- a/tests/stdlib/nre/captures.nim
+++ b/tests/stdlib/nre/captures.nim
@@ -1,59 +1,64 @@
 import unittest, optional_nonstrict
 include nre
 
-suite "captures":
-  test "map capture names to numbers":
+block: # captures
+  block: # map capture names to numbers
     check(getNameToNumberTable(re("(?<v1>1(?<v2>2(?<v3>3))(?'v4'4))()")) ==
       { "v1" : 0, "v2" : 1, "v3" : 2, "v4" : 3 }.toTable())
 
-  test "capture bounds are correct":
+  block: # capture bounds are correct
     let ex1 = re("([0-9])")
     check("1 23".find(ex1).matchBounds == 0 .. 0)
-    check("1 23".find(ex1).captureBounds[0].get == 0 .. 0)
+    check("1 23".find(ex1).captureBounds[0] == 0 .. 0)
     check("1 23".find(ex1, 1).matchBounds == 2 .. 2)
     check("1 23".find(ex1, 3).matchBounds == 3 .. 3)
 
     let ex2 = re("()()()()()()()()()()([0-9])")
-    check("824".find(ex2).captureBounds[0].get == 0 .. -1)
-    check("824".find(ex2).captureBounds[10].get == 0 .. 0)
+    check("824".find(ex2).captureBounds[0] == 0 .. -1)
+    check("824".find(ex2).captureBounds[10] == 0 .. 0)
 
     let ex3 = re("([0-9]+)")
-    check("824".find(ex3).captureBounds[0].get == 0 .. 2)
+    check("824".find(ex3).captureBounds[0] == 0 .. 2)
 
-  test "named captures":
+  block: # named captures
     let ex1 = "foobar".find(re("(?<foo>foo)(?<bar>bar)"))
     check(ex1.captures["foo"] == "foo")
     check(ex1.captures["bar"] == "bar")
 
     let ex2 = "foo".find(re("(?<foo>foo)(?<bar>bar)?"))
+    check("foo" in ex2.captureBounds)
     check(ex2.captures["foo"] == "foo")
-    check(ex2.captures["bar"] == nil)
+    check(not ("bar" in ex2.captures))
+    expect KeyError:
+        discard ex2.captures["bar"]
 
-  test "named capture bounds":
+  block: # named capture bounds
     let ex1 = "foo".find(re("(?<foo>foo)(?<bar>bar)?"))
-    check(ex1.captureBounds["foo"] == some(0..2))
-    check(ex1.captureBounds["bar"] == none(Slice[int]))
+    check("foo" in ex1.captureBounds)
+    check(ex1.captureBounds["foo"] == 0..2)
+    check(not ("bar" in ex1.captures))
+    expect KeyError:
+        discard ex1.captures["bar"]
 
-  test "capture count":
+  block: # capture count
     let ex1 = re("(?<foo>foo)(?<bar>bar)?")
     check(ex1.captureCount == 2)
     check(ex1.captureNameId == {"foo" : 0, "bar" : 1}.toTable())
 
-  test "named capture table":
+  block: # named capture table
     let ex1 = "foo".find(re("(?<foo>foo)(?<bar>bar)?"))
-    check(ex1.captures.toTable == {"foo" : "foo", "bar" : nil}.toTable())
-    check(ex1.captureBounds.toTable == {"foo" : some(0..2), "bar" : none(Slice[int])}.toTable())
-    check(ex1.captures.toTable("") == {"foo" : "foo", "bar" : ""}.toTable())
+    check(ex1.captures.toTable == {"foo" : "foo"}.toTable())
+    check(ex1.captureBounds.toTable == {"foo" : 0..2}.toTable())
 
     let ex2 = "foobar".find(re("(?<foo>foo)(?<bar>bar)?"))
     check(ex2.captures.toTable == {"foo" : "foo", "bar" : "bar"}.toTable())
 
-  test "capture sequence":
+  block: # capture sequence
     let ex1 = "foo".find(re("(?<foo>foo)(?<bar>bar)?"))
-    check(ex1.captures.toSeq == @["foo", nil])
+    check(ex1.captures.toSeq == @[some("foo"), none(string)])
     check(ex1.captureBounds.toSeq == @[some(0..2), none(Slice[int])])
-    check(ex1.captures.toSeq("") == @["foo", ""])
+    check(ex1.captures.toSeq(some("")) == @[some("foo"), some("")])
 
     let ex2 = "foobar".find(re("(?<foo>foo)(?<bar>bar)?"))
-    check(ex2.captures.toSeq == @["foo", "bar"])
+    check(ex2.captures.toSeq == @[some("foo"), some("bar")])
 
diff --git a/tests/stdlib/nre/escape.nim b/tests/stdlib/nre/escape.nim
index db5e8a001..5e7dc0c0e 100644
--- a/tests/stdlib/nre/escape.nim
+++ b/tests/stdlib/nre/escape.nim
@@ -1,7 +1,7 @@
 import nre, unittest
 
-suite "escape strings":
-  test "escape strings":
+block: # escape strings
+  block: # escape strings
     check("123".escapeRe() == "123")
     check("[]".escapeRe() == r"\[\]")
     check("()".escapeRe() == r"\(\)")
diff --git a/tests/stdlib/nre/find.nim b/tests/stdlib/nre/find.nim
index caa953ff4..7e7555d73 100644
--- a/tests/stdlib/nre/find.nim
+++ b/tests/stdlib/nre/find.nim
@@ -3,23 +3,23 @@ import nre except toSeq
 import optional_nonstrict
 import times, strutils
 
-suite "find":
-  test "find text":
+block: # find
+  block: # find text
     check("3213a".find(re"[a-z]").match == "a")
     check(toSeq(findIter("1 2 3 4 5 6 7 8 ", re" ")).map(
       proc (a: RegexMatch): string = a.match
     ) == @[" ", " ", " ", " ", " ", " ", " ", " "])
 
-  test "find bounds":
+  block: # find bounds
     check(toSeq(findIter("1 2 3 4 5 ", re" ")).map(
       proc (a: RegexMatch): Slice[int] = a.matchBounds
     ) == @[1..1, 3..3, 5..5, 7..7, 9..9])
 
-  test "overlapping find":
+  block: # overlapping find
     check("222".findAll(re"22") == @["22"])
     check("2222".findAll(re"22") == @["22", "22"])
 
-  test "len 0 find":
+  block: # len 0 find
     check("".findAll(re"\ ") == newSeq[string]())
     check("".findAll(re"") == @[""])
     check("abc".findAll(re"") == @["", "", "", ""])
@@ -27,7 +27,7 @@ suite "find":
     check("word\r\lword".findAll(re"(*ANYCRLF)(?m)$") == @["", ""])
     check("слово слово".findAll(re"(*U)\b") == @["", "", "", ""])
 
-  test "bail early":
+  block: # bail early
     ## we expect nothing to be found and we should be bailing out early which means that
     ## the timing difference between searching in small and large data should be well
     ## within a tolerance margin
diff --git a/tests/stdlib/nre/init.nim b/tests/stdlib/nre/init.nim
index 26e668104..f0c8e0a00 100644
--- a/tests/stdlib/nre/init.nim
+++ b/tests/stdlib/nre/init.nim
@@ -1,12 +1,12 @@
 import unittest
 include nre
 
-suite "Test NRE initialization":
-  test "correct initialization":
+block: # Test NRE initialization
+  block: # correct initialization
     check(re("[0-9]+") != nil)
     check(re("(?i)[0-9]+") != nil)
 
-  test "options":
+  block: # options
     check(extractOptions("(*NEVER_UTF)") ==
           ("", pcre.NEVER_UTF, true))
     check(extractOptions("(*UTF8)(*ANCHORED)(*UCP)z") ==
@@ -19,14 +19,14 @@ suite "Test NRE initialization":
     check(extractOptions("(*LIMIT_MATCH=6)(*ANCHORED)z") ==
           ("(*LIMIT_MATCH=6)z", pcre.ANCHORED, true))
 
-  test "incorrect options":
+  block: # incorrect options
     for s in ["CR", "(CR", "(*CR", "(*abc)", "(*abc)CR",
               "(?i)",
               "(*LIMIT_MATCH=5", "(*NO_AUTO_POSSESS=5)"]:
       let ss = s & "(*NEVER_UTF)"
       check(extractOptions(ss) == (ss, 0, true))
 
-  test "invalid regex":
+  block: # invalid regex
     expect(SyntaxError): discard re("[0-9")
     try:
       discard re("[0-9")
diff --git a/tests/stdlib/nre/match.nim b/tests/stdlib/nre/match.nim
index 38ee5214b..7e09a4b2f 100644
--- a/tests/stdlib/nre/match.nim
+++ b/tests/stdlib/nre/match.nim
@@ -1,18 +1,18 @@
 include nre, unittest, optional_nonstrict
 
-suite "match":
-  test "upper bound must be inclusive":
+block: # match
+  block: # upper bound must be inclusive
     check("abc".match(re"abc", endpos = -1) == none(RegexMatch))
     check("abc".match(re"abc", endpos = 1) == none(RegexMatch))
     check("abc".match(re"abc", endpos = 2) != none(RegexMatch))
 
-  test "match examples":
+  block: # match examples
     check("abc".match(re"(\w)").captures[0] == "a")
     check("abc".match(re"(?<letter>\w)").captures["letter"] == "a")
     check("abc".match(re"(\w)\w").captures[-1] == "ab")
-    check("abc".match(re"(\w)").captureBounds[0].get == 0 .. 0)
-    check("abc".match(re"").captureBounds[-1].get == 0 .. -1)
-    check("abc".match(re"abc").captureBounds[-1].get == 0 .. 2)
+    check("abc".match(re"(\w)").captureBounds[0] == 0 .. 0)
+    check("abc".match(re"").captureBounds[-1] == 0 .. -1)
+    check("abc".match(re"abc").captureBounds[-1] == 0 .. 2)
 
-  test "match test cases":
+  block: # match test cases
     check("123".match(re"").matchBounds == 0 .. -1)
diff --git a/tests/stdlib/nre/misc.nim b/tests/stdlib/nre/misc.nim
index f4a88b639..b7df08ee9 100644
--- a/tests/stdlib/nre/misc.nim
+++ b/tests/stdlib/nre/misc.nim
@@ -1,13 +1,13 @@
 import unittest, nre, strutils, optional_nonstrict
 
-suite "Misc tests":
-  test "unicode":
+block: # Misc tests
+  block: # unicode
     check("".find(re"(*UTF8)").match == "")
     check("перевірка".replace(re"(*U)\w", "") == "")
 
-  test "empty or non-empty match":
-    check("abc".findall(re"|.").join(":") == ":a::b::c:")
-    check("abc".findall(re".|").join(":") == "a:b:c:")
+  block: # empty or non-empty match
+    check("abc".findAll(re"|.").join(":") == ":a::b::c:")
+    check("abc".findAll(re".|").join(":") == "a:b:c:")
 
     check("abc".replace(re"|.", "x") == "xxxxxxx")
     check("abc".replace(re".|", "x") == "xxxx")
diff --git a/tests/stdlib/nre/replace.nim b/tests/stdlib/nre/replace.nim
index 516fd4328..5cf659f21 100644
--- a/tests/stdlib/nre/replace.nim
+++ b/tests/stdlib/nre/replace.nim
@@ -1,13 +1,13 @@
 include nre
 import unittest
 
-suite "replace":
-  test "replace with 0-length strings":
+block: # replace
+  block: # replace with 0-length strings
     check("".replace(re"1", proc (v: RegexMatch): string = "1") == "")
     check(" ".replace(re"", proc (v: RegexMatch): string = "1") == "1 1")
     check("".replace(re"", proc (v: RegexMatch): string = "1") == "1")
 
-  test "regular replace":
+  block: # regular replace
     check("123".replace(re"\d", "foo") == "foofoofoo")
     check("123".replace(re"(\d)", "$1$1") == "112233")
     check("123".replace(re"(\d)(\d)", "$1$2") == "123")
@@ -15,6 +15,8 @@ suite "replace":
     check("123".replace(re"(?<foo>\d)(\d)", "$foo$#$#") == "1123")
     check("123".replace(re"(?<foo>\d)(\d)", "${foo}$#$#") == "1123")
 
-  test "replacing missing captures should throw instead of segfaulting":
-    expect ValueError: discard "ab".replace(re"(a)|(b)", "$1$2")
-    expect ValueError: discard "b".replace(re"(a)?(b)", "$1$2")
+  block: # replacing missing captures should throw instead of segfaulting
+    expect IndexDefect: discard "ab".replace(re"(a)|(b)", "$1$2")
+    expect IndexDefect: discard "b".replace(re"(a)?(b)", "$1$2")
+    expect KeyError: discard "b".replace(re"(a)?", "${foo}")
+    expect KeyError: discard "b".replace(re"(?<foo>a)?", "${foo}")
diff --git a/tests/stdlib/nre/split.nim b/tests/stdlib/nre/split.nim
index 9d57ea7d8..3cd57bb82 100644
--- a/tests/stdlib/nre/split.nim
+++ b/tests/stdlib/nre/split.nim
@@ -1,8 +1,8 @@
 import unittest, strutils
 include nre
 
-suite "string splitting":
-  test "splitting strings":
+block: # string splitting
+  block: # splitting strings
     check("1 2 3 4 5 6 ".split(re" ") == @["1", "2", "3", "4", "5", "6", ""])
     check("1  2  ".split(re(" ")) == @["1", "", "2", "", ""])
     check("1 2".split(re(" ")) == @["1", "2"])
@@ -10,22 +10,22 @@ suite "string splitting":
     check("".split(re"foo") == @[""])
     check("9".split(re"\son\s") == @["9"])
 
-  test "captured patterns":
+  block: # captured patterns
     check("12".split(re"(\d)") == @["", "1", "", "2", ""])
 
-  test "maxsplit":
+  block: # maxsplit
     check("123".split(re"", maxsplit = 2) == @["1", "23"])
     check("123".split(re"", maxsplit = 1) == @["123"])
     check("123".split(re"", maxsplit = -1) == @["1", "2", "3"])
 
-  test "split with 0-length match":
+  block: # split with 0-length match
     check("12345".split(re("")) == @["1", "2", "3", "4", "5"])
     check("".split(re"") == newSeq[string]())
     check("word word".split(re"\b") == @["word", " ", "word"])
     check("word\r\lword".split(re"(*ANYCRLF)(?m)$") == @["word", "\r\lword"])
     check("слово слово".split(re"(*U)(\b)") == @["", "слово", "", " ", "", "слово", ""])
 
-  test "perl split tests":
+  block: # perl split tests
     check("forty-two"                    .split(re"")      .join(",") == "f,o,r,t,y,-,t,w,o")
     check("forty-two"                    .split(re"", 3)   .join(",") == "f,o,rty-two")
     check("split this string"            .split(re" ")     .join(",") == "split,this,string")
@@ -47,7 +47,7 @@ suite "string splitting":
     check(""                             .split(re"")      .len       == 0)
     check(":"                            .split(re"")      .len       == 1)
 
-  test "start position":
+  block: # start position
     check("abc".split(re"", start = 1) == @["b", "c"])
     check("abc".split(re"", start = 2) == @["c"])
     check("abc".split(re"", start = 3) == newSeq[string]())
diff --git a/tests/stdlib/osproctest.nim b/tests/stdlib/osproctest.nim
new file mode 100644
index 000000000..8c4fba9ba
--- /dev/null
+++ b/tests/stdlib/osproctest.nim
@@ -0,0 +1,8 @@
+# This is test program for the osproc module.
+
+import os
+
+echo getCurrentDir()
+
+for i in 1..paramCount():
+  echo paramStr(i)
diff --git a/tests/stdlib/somesql.sql b/tests/stdlib/somesql.sql
index 285f93cec..74afcbab0 100644
--- a/tests/stdlib/somesql.sql
+++ b/tests/stdlib/somesql.sql
@@ -295,4 +295,4 @@ create table anon207(
   anon209 varchar(30) not null,
   anon204 varchar(30) default null,
   anon70 int not null  references anon40(anon41));
-
+select * from anon207 where anon41 in (1, 2, 3);
diff --git a/tests/stdlib/t10231.nim b/tests/stdlib/t10231.nim
new file mode 100644
index 000000000..3d09721aa
--- /dev/null
+++ b/tests/stdlib/t10231.nim
@@ -0,0 +1,16 @@
+discard """
+  targets: "cpp"
+  action: run
+  exitcode: 0
+"""
+
+import os
+import std/assertions
+
+# 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/t14139.nim b/tests/stdlib/t14139.nim
new file mode 100644
index 000000000..866bdb45f
--- /dev/null
+++ b/tests/stdlib/t14139.nim
@@ -0,0 +1,10 @@
+import std/heapqueue
+import std/assertions
+
+var test_queue : HeapQueue[int]
+
+test_queue.push(7)
+test_queue.push(3)
+test_queue.push(9)
+let i = test_queue.pushpop(10)
+doAssert i == 3
diff --git a/tests/stdlib/t15663.nim b/tests/stdlib/t15663.nim
new file mode 100644
index 000000000..8e8bfd9a8
--- /dev/null
+++ b/tests/stdlib/t15663.nim
@@ -0,0 +1,9 @@
+discard """
+  cmd: "nim c --gc:arc $file"
+  output: "Test"
+"""
+
+import std/widestrs
+
+let ws = newWideCString("Test")
+echo ws
diff --git a/tests/stdlib/t19304.nim b/tests/stdlib/t19304.nim
new file mode 100644
index 000000000..5e8795ac5
--- /dev/null
+++ b/tests/stdlib/t19304.nim
@@ -0,0 +1,7 @@
+import times
+
+type DjangoDateTime* = distinct DateTime
+
+# proc toTime*(x: DjangoDateTime): Time  {.borrow.} # <-- works
+proc format*(x: DjangoDateTime, f: TimeFormat,
+    loc: DateTimeLocale = DefaultLocale): string {.borrow.}
diff --git a/tests/stdlib/t20023.nim b/tests/stdlib/t20023.nim
new file mode 100644
index 000000000..8f12f8993
--- /dev/null
+++ b/tests/stdlib/t20023.nim
@@ -0,0 +1,10 @@
+import std/[tables, hashes, assertions]
+
+
+let t = ()
+var a = toTable({t:t})
+del(a,t)
+let b = default(typeof(a))
+
+doAssert a==b , "tables are not equal"
+doAssert hash(a) == hash(b), "table hashes are not equal"
diff --git a/tests/stdlib/t21251.nim b/tests/stdlib/t21251.nim
new file mode 100644
index 000000000..4402e9b7e
--- /dev/null
+++ b/tests/stdlib/t21251.nim
@@ -0,0 +1,6 @@
+import std / [tables, sets, sharedtables]
+
+var shared: SharedTable[int, int]
+shared.init
+
+shared[1] = 1
diff --git a/tests/stdlib/t21406.nim b/tests/stdlib/t21406.nim
new file mode 100644
index 000000000..86bf7b0c7
--- /dev/null
+++ b/tests/stdlib/t21406.nim
@@ -0,0 +1,7 @@
+import std/[times, strformat]
+import std/assertions
+
+let aTime = getTime()
+doAssert fmt"{aTime}" == $aTime
+let aNow = now()
+doAssert fmt"{aNow}" == $aNow
diff --git a/tests/stdlib/t21564.nim b/tests/stdlib/t21564.nim
new file mode 100644
index 000000000..0a5777d12
--- /dev/null
+++ b/tests/stdlib/t21564.nim
@@ -0,0 +1,32 @@
+discard """
+targets: "c js"
+"""
+
+import bitops
+import std/assertions
+
+proc main() =
+  block: # bug #21564
+    # tesk `bitops.bitsliced` patch
+    doAssert(0x17.bitsliced(4..7) == 0x01)
+    doAssert(0x17.bitsliced(0..3) == 0x07)
+
+  block:
+    # test in-place `bitops.bitslice`
+    var t = 0x12F4
+    t.bitslice(4..7)
+
+    doAssert(t == 0xF)
+
+  block:
+    # test `bitops.toMask` patch via bitops.masked
+    doAssert(0x12FFFF34.masked(8..23) == 0x00FFFF00)
+
+  block: # bug #22687
+    var a: uint8 = 0b1111_1111
+    doAssert a.bitsliced(4..7).int == 15
+
+main()
+
+static:
+  main()
diff --git a/tests/stdlib/t7686.nim b/tests/stdlib/t7686.nim
new file mode 100644
index 000000000..9902cfcb5
--- /dev/null
+++ b/tests/stdlib/t7686.nim
@@ -0,0 +1,10 @@
+import std/strutils
+import std/assertions
+
+type
+  MyEnum = enum
+    A,
+    a
+
+doAssert parseEnum[MyEnum]("A") == A
+doAssert parseEnum[MyEnum]("a") == a
diff --git a/tests/stdlib/t8925.nim b/tests/stdlib/t8925.nim
new file mode 100644
index 000000000..c55e93e73
--- /dev/null
+++ b/tests/stdlib/t8925.nim
@@ -0,0 +1,16 @@
+discard """
+  errormsg: "type mismatch between pattern '$$i' (position: 1) and HourRange var 'hour'"
+  file: "strscans.nim"
+"""
+
+import strscans
+
+type
+  HourRange = range[0..23]
+
+var
+  hour: HourRange
+  timeStr: string
+
+if scanf(timeStr, "$i", hour):
+  discard
diff --git a/tests/stdlib/t9754.nim b/tests/stdlib/t9754.nim
new file mode 100644
index 000000000..971b5a8fb
--- /dev/null
+++ b/tests/stdlib/t9754.nim
@@ -0,0 +1,6 @@
+discard """
+  joinable: false
+"""
+
+import tmarshal
+import tparsesql
\ No newline at end of file
diff --git a/tests/stdlib/talgorithm.nim b/tests/stdlib/talgorithm.nim
new file mode 100644
index 000000000..e2024df0c
--- /dev/null
+++ b/tests/stdlib/talgorithm.nim
@@ -0,0 +1,275 @@
+discard """
+  targets: "c js"
+  matrix: "--mm:refc; --mm:orc"
+  output:'''@["3", "2", "1"]
+'''
+"""
+#12928,10456
+
+import std/[sequtils, algorithm, json, sugar]
+import std/assertions
+
+proc test() = 
+  try: 
+    let info = parseJson("""
+    {"a": ["1", "2", "3"]}
+    """)
+    let prefixes = info["a"].getElems().mapIt(it.getStr()).sortedByIt(it).reversed()
+    echo prefixes
+  except:
+    discard
+
+test()
+
+block:
+  # Tests for lowerBound
+  var arr = @[1, 2, 3, 5, 6, 7, 8, 9]
+  doAssert arr.lowerBound(0) == 0
+  doAssert arr.lowerBound(4) == 3
+  doAssert arr.lowerBound(5) == 3
+  doAssert arr.lowerBound(10) == 8
+  arr = @[1, 5, 10]
+  doAssert arr.lowerBound(4) == 1
+  doAssert arr.lowerBound(5) == 1
+  doAssert arr.lowerBound(6) == 2
+  # Tests for isSorted
+  var srt1 = [1, 2, 3, 4, 4, 4, 4, 5]
+  var srt2 = ["iello", "hello"]
+  var srt3 = [1.0, 1.0, 1.0]
+  var srt4: seq[int]
+  doAssert srt1.isSorted(cmp) == true
+  doAssert srt2.isSorted(cmp) == false
+  doAssert srt3.isSorted(cmp) == true
+  doAssert srt4.isSorted(cmp) == true
+  var srtseq = newSeq[int]()
+  doAssert srtseq.isSorted(cmp) == true
+  # Tests for reversed
+  var arr1 = @[0, 1, 2, 3, 4]
+  doAssert arr1.reversed() == @[4, 3, 2, 1, 0]
+  for i in 0 .. high(arr1):
+    doAssert arr1.reversed(0, i) == arr1.reversed()[high(arr1) - i .. high(arr1)]
+    doAssert arr1.reversed(i, high(arr1)) == arr1.reversed()[0 .. high(arr1) - i]
+
+block:
+  var list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+  let list2 = list.rotatedLeft(1 ..< 9, 3)
+  let expected = [0, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10]
+
+  doAssert list.rotateLeft(1 ..< 9, 3) == 6
+  doAssert list == expected
+  doAssert list2 == @expected
+
+  var s0, s1, s2, s3, s4, s5 = "xxxabcdefgxxx"
+
+  doAssert s0.rotateLeft(3 ..< 10, 3) == 7
+  doAssert s0 == "xxxdefgabcxxx"
+  doAssert s1.rotateLeft(3 ..< 10, 2) == 8
+  doAssert s1 == "xxxcdefgabxxx"
+  doAssert s2.rotateLeft(3 ..< 10, 4) == 6
+  doAssert s2 == "xxxefgabcdxxx"
+  doAssert s3.rotateLeft(3 ..< 10, -3) == 6
+  doAssert s3 == "xxxefgabcdxxx"
+  doAssert s4.rotateLeft(3 ..< 10, -10) == 6
+  doAssert s4 == "xxxefgabcdxxx"
+  doAssert s5.rotateLeft(3 ..< 10, 11) == 6
+  doAssert s5 == "xxxefgabcdxxx"
+
+  block product:
+    doAssert product(newSeq[seq[int]]()) == newSeq[seq[int]](), "empty input"
+    doAssert product(@[newSeq[int](), @[], @[]]) == newSeq[seq[int]](), "bit more empty input"
+    doAssert product(@[@[1, 2]]) == @[@[1, 2]], "a simple case of one element"
+    doAssert product(@[@[1, 2], @[3, 4]]) == @[@[2, 4], @[1, 4], @[2, 3], @[1,
+        3]], "two elements"
+    doAssert product(@[@[1, 2], @[3, 4], @[5, 6]]) == @[@[2, 4, 6], @[1, 4, 6],
+        @[2, 3, 6], @[1, 3, 6], @[2, 4, 5], @[1, 4, 5], @[2, 3, 5], @[1, 3, 5]], "three elements"
+    doAssert product(@[@[1, 2], @[]]) == newSeq[seq[int]](), "two elements, but one empty"
+
+  block lowerBound:
+    doAssert lowerBound([1, 2, 4], 3, system.cmp[int]) == 2
+    doAssert lowerBound([1, 2, 2, 3], 4, system.cmp[int]) == 4
+    doAssert lowerBound([1, 2, 3, 10], 11) == 4
+
+  block upperBound:
+    doAssert upperBound([1, 2, 4], 3, system.cmp[int]) == 2
+    doAssert upperBound([1, 2, 2, 3], 3, system.cmp[int]) == 4
+    doAssert upperBound([1, 2, 3, 5], 3) == 3
+
+  block fillEmptySeq:
+    var s = newSeq[int]()
+    s.fill(0)
+
+  block testBinarySearch:
+    var noData: seq[int]
+    doAssert binarySearch(noData, 7) == -1
+    let oneData = @[1]
+    doAssert binarySearch(oneData, 1) == 0
+    doAssert binarySearch(oneData, 7) == -1
+    let someData = @[1, 3, 4, 7]
+    doAssert binarySearch(someData, 1) == 0
+    doAssert binarySearch(someData, 7) == 3
+    doAssert binarySearch(someData, -1) == -1
+    doAssert binarySearch(someData, 5) == -1
+    doAssert binarySearch(someData, 13) == -1
+    let moreData = @[1, 3, 5, 7, 4711]
+    doAssert binarySearch(moreData, -1) == -1
+    doAssert binarySearch(moreData, 1) == 0
+    doAssert binarySearch(moreData, 5) == 2
+    doAssert binarySearch(moreData, 6) == -1
+    doAssert binarySearch(moreData, 4711) == 4
+    doAssert binarySearch(moreData, 4712) == -1
+
+# merge
+proc main() =
+  block:
+    var x = @[1, 7, 8, 11, 21, 33, 45, 99]
+    var y = @[6, 7, 9, 12, 57, 66]
+
+    var merged: seq[int]
+    merged.merge(x, y)
+    doAssert merged.isSorted
+    doAssert merged == sorted(x & y)
+
+  block:
+    var x = @[111, 88, 76, 56, 45, 31, 22, 19, 11, 3]
+    var y = @[99, 85, 83, 82, 69, 64, 48, 42, 33, 31, 26, 13]
+
+    var merged: seq[int]
+    merged.merge(x, y, (x, y) => -system.cmp(x, y))
+    doAssert merged.isSorted((x, y) => -system.cmp(x, y))
+    doAssert merged == sorted(x & y, SortOrder.Descending)
+
+  block:
+    var x: seq[int] = @[]
+    var y = @[1]
+
+    var merged: seq[int]
+    merged.merge(x, y)
+    doAssert merged.isSorted
+    doAssert merged.isSorted(SortOrder.Descending)
+    doAssert merged == @[1]
+
+  block:
+    var x = [1, 3, 5, 5, 7]
+    var y: seq[int] = @[]
+
+    var merged: seq[int]
+    merged.merge(x, y)
+    doAssert merged.isSorted
+    doAssert merged == @x
+
+  block:
+    var x = [1, 3, 5, 5, 7]
+    var y: seq[int] = @[]
+
+    var merged: seq[int] = @[1, 2, 3, 5, 6, 56, 99, 2, 34]
+    merged.merge(x, y)
+    doAssert merged == @[1, 2, 3, 5, 6, 56, 99, 2, 34, 1, 3, 5, 5, 7]
+
+
+  block:
+    var x: array[0, int]
+    var y = [1, 4, 6, 7, 9]
+
+    var merged: seq[int]
+    merged.merge(x, y)
+    doAssert merged.isSorted
+    doAssert merged == @y
+
+  block:
+    var x: array[0, int]
+    var y: array[0, int]
+
+    var merged: seq[int]
+    merged.merge(x, y)
+    doAssert merged.isSorted
+    doAssert merged.len == 0
+
+  block:
+    var x: array[0, int]
+    var y: array[0, int]
+
+    var merged: seq[int] = @[99, 99, 99]
+    merged.setLen(0)
+    merged.merge(x, y)
+    doAssert merged.isSorted
+    doAssert merged.len == 0
+
+  block:
+    var x: seq[int]
+    var y: seq[int]
+
+    var merged: seq[int]
+    merged.merge(x, y)
+    doAssert merged.isSorted
+    doAssert merged.len == 0
+
+  block:
+    type
+      Record = object
+        id: int
+    
+    proc r(id: int): Record =
+      Record(id: id)
+
+    proc cmp(x, y: Record): int =
+      if x.id == y.id: return 0
+      if x.id < y.id: return -1
+      result = 1
+
+    var x = @[r(-12), r(1), r(3), r(8), r(13), r(88)]
+    var y = @[r(4), r(7), r(12), r(13), r(77), r(99)]
+
+    var merged: seq[Record] = @[]
+    merged.merge(x, y, cmp)
+    doAssert merged.isSorted(cmp)
+    doAssert merged.len == 12
+
+  block:
+    type
+      Record = object
+        id: int
+    
+    proc r(id: int): Record =
+      Record(id: id)
+
+    proc ascendingCmp(x, y: Record): int =
+      if x.id == y.id: return 0
+      if x.id < y.id: return -1
+      result = 1
+
+    proc descendingCmp(x, y: Record): int =
+      if x.id == y.id: return 0
+      if x.id < y.id: return 1
+      result = -1
+
+    var x = @[r(-12), r(1), r(3), r(8), r(13), r(88)]
+    var y = @[r(4), r(7), r(12), r(13), r(77), r(99)]
+
+    var merged: seq[Record]
+    merged.setLen(0)
+    merged.merge(x, y, ascendingCmp)
+    doAssert merged.isSorted(ascendingCmp)
+    doAssert merged == sorted(x & y, ascendingCmp)
+
+    reverse(x)
+    reverse(y)
+
+    merged.setLen(0)
+    merged.merge(x, y, descendingCmp)
+    doAssert merged.isSorted(descendingCmp)
+    doAssert merged == sorted(x & y, ascendingCmp, SortOrder.Descending)
+
+    reverse(x)
+    reverse(y)
+    merged.setLen(0)
+    merged.merge(x, y, proc (x, y: Record): int = -descendingCmp(x, y))
+    doAssert merged.isSorted(proc (x, y: Record): int = -descendingCmp(x, y))
+    doAssert merged == sorted(x & y, ascendingCmp)
+
+
+  var x: seq[(int, int)]
+  x.merge([(1,1)], [(1,2)], (a,b) => a[0] - b[0])
+  doAssert x == @[(1, 1), (1, 2)]
+
+static: main()
+main()
diff --git a/tests/stdlib/tarithmetics.nim b/tests/stdlib/tarithmetics.nim
new file mode 100644
index 000000000..0a6dd1fcf
--- /dev/null
+++ b/tests/stdlib/tarithmetics.nim
@@ -0,0 +1,50 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c cpp js"
+"""
+import std/assertions
+# TODO: in future work move existing arithmetic tests (tests/arithm/*) into this file
+# FYI https://github.com/nim-lang/Nim/pull/17767
+
+template main =
+  # put all arithmetic tests
+
+  block tshr:
+    block: # Signed types
+      let
+        a1 = -3
+        a2 = -2
+        b1 = -4'i8
+        b2 = 1'i8
+        c1 = -5'i16
+        c2 = 1'i16
+        d1 = -7i32
+        d2 = 1'i32
+        e1 = -9'i64
+        e2 = 1'i64
+      doAssert a1 shr a2 == -1
+      doAssert b1 shr b2 == -2
+      doAssert c1 shr c2 == -3
+      doAssert d1 shr d2 == -4
+      doAssert e1 shr e2 == -5
+
+    block: # Unsigned types
+      let
+        a1 = 3'u
+        a2 = 2'u
+        b1 = 2'u8
+        b2 = 1'u8
+        c1 = 5'u16
+        c2 = 1'u16
+        d1 = 6'u32
+        d2 = 1'u32
+        e1 = 8'u64
+        e2 = 1'u64
+      doAssert a1 shr a2 == 0
+      doAssert b1 shr b2 == 1
+      doAssert c1 shr c2 == 2
+      doAssert d1 shr d2 == 3
+      doAssert e1 shr e2 == 4
+
+static: main()
+main()
diff --git a/tests/stdlib/tasynchttpserver.nim b/tests/stdlib/tasynchttpserver.nim
new file mode 100644
index 000000000..5a7e2da40
--- /dev/null
+++ b/tests/stdlib/tasynchttpserver.nim
@@ -0,0 +1,121 @@
+discard """
+  cmd: "nim c --threads:on $file"
+  exitcode: 0
+  output: "OK"
+  disabled: false
+"""
+
+import strutils
+from net import TimeoutError
+import std/assertions
+
+import httpclient, asynchttpserver, asyncdispatch, asyncfutures
+
+template runTest(
+    handler: proc (request: Request): Future[void] {.gcsafe.},
+    request: proc (server: AsyncHttpServer): Future[AsyncResponse],
+    test: proc (response: AsyncResponse, body: string): Future[void]) =
+
+  let server = newAsyncHttpServer()
+
+  discard server.serve(Port(64123), handler)
+
+  let
+    response = waitFor(request(server))
+    body = waitFor(response.body)
+
+  discard test(response, body)
+
+proc test200() {.async.} =
+  proc handler(request: Request) {.async.} =
+    await request.respond(Http200, "Hello World, 200")
+
+  proc request(server: AsyncHttpServer): Future[AsyncResponse] {.async.} =
+    let
+      client = newAsyncHttpClient()
+      clientResponse = await client.request("http://localhost:64123/")
+
+    server.close()
+
+    return clientResponse
+
+  proc test(response: AsyncResponse, body: string) {.async.} =
+    doAssert(response.status == $Http200)
+    doAssert(body == "Hello World, 200")
+    doAssert(response.headers.hasKey("Content-Length"))
+    doAssert(response.headers["Content-Length"] == "16")
+
+  runTest(handler, request, test)
+
+proc test404() {.async.} =
+  proc handler(request: Request) {.async.} =
+    await request.respond(Http404, "Hello World, 404")
+
+  proc request(server: AsyncHttpServer): Future[AsyncResponse] {.async.} =
+    let
+      client = newAsyncHttpClient()
+      clientResponse = await client.request("http://localhost:64123/")
+
+    server.close()
+
+    return clientResponse
+
+  proc test(response: AsyncResponse, body: string) {.async.} =
+    doAssert(response.status == $Http404)
+    doAssert(body == "Hello World, 404")
+    doAssert(response.headers.hasKey("Content-Length"))
+    doAssert(response.headers["Content-Length"] == "16")
+
+  runTest(handler, request, test)
+
+proc testCustomEmptyHeaders() {.async.} =
+  proc handler(request: Request) {.async.} =
+    await request.respond(Http200, "Hello World, 200", newHttpHeaders())
+
+  proc request(server: AsyncHttpServer): Future[AsyncResponse] {.async.} =
+    let
+      client = newAsyncHttpClient()
+      clientResponse = await client.request("http://localhost:64123/")
+
+    server.close()
+
+    return clientResponse
+
+  proc test(response: AsyncResponse, body: string) {.async.} =
+    doAssert(response.status == $Http200)
+    doAssert(body == "Hello World, 200")
+    doAssert(response.headers.hasKey("Content-Length"))
+    doAssert(response.headers["Content-Length"] == "16")
+
+  runTest(handler, request, test)
+
+proc testCustomContentLength() {.async.} =
+  proc handler(request: Request) {.async.} =
+    let headers = newHttpHeaders()
+    headers["Content-Length"] = "0"
+    await request.respond(Http200, "Hello World, 200", headers)
+
+  proc request(server: AsyncHttpServer): Future[AsyncResponse] {.async.} =
+    let
+      client = newAsyncHttpClient()
+      clientResponse = await client.request("http://localhost:64123/")
+
+    server.close()
+
+    return clientResponse
+
+  proc test(response: AsyncResponse, body: string) {.async.} =
+    doAssert(response.status == $Http200)
+    doAssert(body == "")
+    doAssert(response.headers.hasKey("Content-Length"))
+    doAssert(response.headers["Content-Length"] == "0")
+    doAssert contentLength(response) == 0 # bug #22778
+
+  runTest(handler, request, test)
+
+waitFor(test200())
+waitFor(test404())
+waitFor(testCustomEmptyHeaders())
+waitFor(testCustomContentLength())
+
+echo "OK"
diff --git a/tests/stdlib/tasynchttpserver_transferencoding.nim b/tests/stdlib/tasynchttpserver_transferencoding.nim
new file mode 100644
index 000000000..886ba0f33
--- /dev/null
+++ b/tests/stdlib/tasynchttpserver_transferencoding.nim
@@ -0,0 +1,91 @@
+discard """
+  matrix: "--mm:arc; --mm:arc -d:danger; --mm:refc"
+  disabled: "freebsd"
+"""
+
+import httpclient, asynchttpserver, asyncdispatch, asyncfutures
+import net
+
+import std/asyncnet
+import std/nativesockets
+import std/assertions
+
+const postBegin = """
+POST / HTTP/1.1
+Transfer-Encoding:chunked
+
+"""
+
+template genTest(input, expected: string) =
+  proc handler(request: Request, future: Future[bool]) {.async, gcsafe.} =
+    doAssert(request.body == expected)
+    doAssert(request.headers.hasKey("Transfer-Encoding"))
+    doAssert(not request.headers.hasKey("Content-Length"))
+    future.complete(true)
+    await request.respond(Http200, "Good")
+
+  proc sendData(data: string, port: Port) {.async.} =
+    var socket = newSocket()
+    defer: socket.close()
+
+    socket.connect("127.0.0.1", port)
+    socket.send(data)
+
+  proc runTest(): Future[bool] {.async.} =
+    var handlerFuture = newFuture[bool]("runTest")
+    let data = postBegin & input
+    let server = newAsyncHttpServer()
+    server.listen(Port(0))
+
+    proc wrapper(request: Request): Future[void] {.gcsafe, closure.} =
+      handler(request, handlerFuture)
+    
+    asyncCheck sendData(data, server.getPort)
+    asyncCheck server.acceptRequest(wrapper)
+    doAssert await handlerFuture
+    
+    server.close()
+    return true
+
+  doAssert waitFor runTest()
+
+block:
+  const expected = "hello=world"
+  const input = ("b\r\n" &
+                 "hello=world\r\n" &
+                 "0\r\n" &
+                 "\r\n")
+  genTest(input, expected)
+block:
+  const expected = "hello encoding"
+  const input = ("e\r\n" &
+                 "hello encoding\r\n" &
+                 "0\r\n" &
+                 "\r\n")
+  genTest(input, expected)
+block:
+  # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding
+  const expected = "MozillaDeveloperNetwork"
+  const input = ("7\r\n" &
+                "Mozilla\r\n" &
+                "9\r\n" &
+                "Developer\r\n" &
+                "7\r\n" &
+                "Network\r\n" &
+                "0\r\n" &
+                "\r\n")
+  genTest(input, expected)
+block:
+  # https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example
+  const expected = "Wikipedia in \r\n\r\nchunks."
+  const input = ("4\r\n" &
+                "Wiki\r\n" &
+                "6\r\n" &
+                "pedia \r\n" &
+                "E\r\n" &
+                "in \r\n" &
+                "\r\n" &
+                "chunks.\r\n" &
+                "0\r\n" &
+                "\r\n")
+  genTest(input, expected)
diff --git a/tests/stdlib/tbase64.nim b/tests/stdlib/tbase64.nim
new file mode 100644
index 000000000..c3bfb818e
--- /dev/null
+++ b/tests/stdlib/tbase64.nim
@@ -0,0 +1,63 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c js"
+"""
+import std/assertions
+import std/base64
+
+template main() =
+  doAssert encode("a") == "YQ=="
+  doAssert encode("Hello World") == "SGVsbG8gV29ybGQ="
+  doAssert encode("leasure.") == "bGVhc3VyZS4="
+  doAssert encode("easure.") == "ZWFzdXJlLg=="
+  doAssert encode("asure.") == "YXN1cmUu"
+  doAssert encode("sure.") == "c3VyZS4="
+  doAssert encode([1,2,3]) == "AQID"
+  doAssert encode(['h','e','y']) == "aGV5"
+
+  doAssert encode("") == ""
+  doAssert decode("") == ""
+
+  doAssert decode(" ") == ""
+
+  const testInputExpandsTo76 = "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
+  const testInputExpands = "++++++++++++++++++++++++++++++"
+  const longText = """Man is distinguished, not only by his reason, but by this
+    singular passion from other animals, which is a lust of the mind,
+    that by a perseverance of delight in the continued and indefatigable
+    generation of knowledge, exceeds the short vehemence of any carnal
+    pleasure."""
+  const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.",
+                 "asure.", longText, testInputExpandsTo76, testInputExpands]
+
+  doAssert encodeMime("foobarbaz", lineLen=4) == "Zm9v\r\nYmFy\r\nYmF6"
+  doAssert decode("Zm9v\r\nYmFy\r\nYmF6") == "foobarbaz"
+
+  for t in items(tests):
+    doAssert decode(encode(t)) == t
+    doAssert decode(encodeMime(t, lineLen=40)) == t
+    doAssert decode(encodeMime(t, lineLen=76)) == t
+
+  doAssertRaises(ValueError): discard decode("SGVsbG\x008gV29ybGQ=")
+
+  block base64urlSafe:
+    doAssert encode("c\xf7>", safe = true) == "Y_c-"
+    doAssert encode("c\xf7>", safe = false) == "Y/c+" # Not a nice URL :(
+    doAssert decode("Y/c+") == decode("Y_c-")
+    # Output must not change with safe=true
+    doAssert encode("Hello World", safe = true) == "SGVsbG8gV29ybGQ="
+    doAssert encode("leasure.", safe = true)  == "bGVhc3VyZS4="
+    doAssert encode("easure.", safe = true) == "ZWFzdXJlLg=="
+    doAssert encode("asure.", safe = true) == "YXN1cmUu"
+    doAssert encode("sure.", safe = true) == "c3VyZS4="
+    doAssert encode([1,2,3], safe = true) == "AQID"
+    doAssert encode(['h','e','y'], safe = true) == "aGV5"
+    doAssert encode("", safe = true) == ""
+    doAssert encode("the quick brown dog jumps over the lazy fox", safe = true) == "dGhlIHF1aWNrIGJyb3duIGRvZyBqdW1wcyBvdmVyIHRoZSBsYXp5IGZveA=="
+
+func mainNoSideEffects() = main()
+
+static: main()
+main()
+static: mainNoSideEffects()
+mainNoSideEffects()
diff --git a/tests/stdlib/tbitops.nim b/tests/stdlib/tbitops.nim
index 8301256c4..3ecab2c64 100644
--- a/tests/stdlib/tbitops.nim
+++ b/tests/stdlib/tbitops.nim
@@ -1,9 +1,12 @@
 discard """
-  file: "tbitops.nim"
-  output: "OK"
+  nimout: "OK"
+  matrix: "--mm:refc; --mm:orc"
+  output: '''
+OK
+'''
 """
 import bitops
-
+import std/assertions
 
 proc main() =
   const U8 = 0b0011_0010'u8
@@ -16,98 +19,123 @@ proc main() =
   const I64A = 0b01000100_00111111_01111100_10001010_10011001_01001000_01111010_00010001'i64
   const U64B = 0b00110010_11011101_10001111_00101000_00000000_00000000_00000000_00000000'u64
   const I64B = 0b00110010_11011101_10001111_00101000_00000000_00000000_00000000_00000000'i64
+  const U64C = 0b00101010_11110101_10001111_00101000_00000100_00000000_00000100_00000000'u64
+  const I64C = 0b00101010_11110101_10001111_00101000_00000100_00000000_00000100_00000000'i64
+
+  doAssert (U8 and U8) == bitand(U8,U8)
+  doAssert (I8 and I8) == bitand(I8,I8)
+  doAssert (U16 and U16) == bitand(U16,U16)
+  doAssert (I16 and I16) == bitand(I16,I16)
+  doAssert (U32 and U32) == bitand(U32,U32)
+  doAssert (I32 and I32) == bitand(I32,I32)
+  doAssert (U64A and U64B) == bitand(U64A,U64B)
+  doAssert (I64A and I64B) == bitand(I64A,I64B)
+  doAssert (U64A and U64B and U64C) == bitand(U64A,U64B,U64C)
+  doAssert (I64A and I64B and I64C) == bitand(I64A,I64B,I64C)
+
+  doAssert (U8 or U8) == bitor(U8,U8)
+  doAssert (I8 or I8) == bitor(I8,I8)
+  doAssert (U16 or U16) == bitor(U16,U16)
+  doAssert (I16 or I16) == bitor(I16,I16)
+  doAssert (U32 or U32) == bitor(U32,U32)
+  doAssert (I32 or I32) == bitor(I32,I32)
+  doAssert (U64A or U64B) == bitor(U64A,U64B)
+  doAssert (I64A or I64B) == bitor(I64A,I64B)
+  doAssert (U64A or U64B or U64C) == bitor(U64A,U64B,U64C)
+  doAssert (I64A or I64B or I64C) == bitor(I64A,I64B,I64C)
+
+  doAssert (U8 xor U8) == bitxor(U8,U8)
+  doAssert (I8 xor I8) == bitxor(I8,I8)
+  doAssert (U16 xor U16) == bitxor(U16,U16)
+  doAssert (I16 xor I16) == bitxor(I16,I16)
+  doAssert (U32 xor U32) == bitxor(U32,U32)
+  doAssert (I32 xor I32) == bitxor(I32,I32)
+  doAssert (U64A xor U64B) == bitxor(U64A,U64B)
+  doAssert (I64A xor I64B) == bitxor(I64A,I64B)
+  doAssert (U64A xor U64B xor U64C) == bitxor(U64A,U64B,U64C)
+  doAssert (I64A xor I64B xor I64C) == bitxor(I64A,I64B,I64C)
+
+  doAssert not(U8) == bitnot(U8)
+  doAssert not(I8) == bitnot(I8)
+  doAssert not(U16) == bitnot(U16)
+  doAssert not(I16) == bitnot(I16)
+  doAssert not(U32) == bitnot(U32)
+  doAssert not(I32) == bitnot(I32)
+  doAssert not(U64A) == bitnot(U64A)
+  doAssert not(I64A) == bitnot(I64A)
+
+  doAssert U64A.fastLog2 == 62
+  doAssert I64A.fastLog2 == 62
+  doAssert U64A.countLeadingZeroBits == 1
+  doAssert I64A.countLeadingZeroBits == 1
+  doAssert U64A.countTrailingZeroBits == 0
+  doAssert I64A.countTrailingZeroBits == 0
+  doAssert U64A.firstSetBit == 1
+  doAssert I64A.firstSetBit == 1
+  doAssert U64A.parityBits == 1
+  doAssert I64A.parityBits == 1
+  doAssert U64A.countSetBits == 29
+  doAssert I64A.countSetBits == 29
+  doAssert U64A.rotateLeftBits(37) == 0b00101001_00001111_01000010_00101000_10000111_11101111_10010001_01010011'u64
+  doAssert U64A.rotateRightBits(37) == 0b01010100_11001010_01000011_11010000_10001010_00100001_11111011_11100100'u64
 
-  doAssert( U64A.fastLog2 == 62)
-  doAssert( I64A.fastLog2 == 62)
-  doAssert( U64A.countLeadingZeroBits == 1)
-  doAssert( I64A.countLeadingZeroBits == 1)
-  doAssert( U64A.countTrailingZeroBits == 0)
-  doAssert( I64A.countTrailingZeroBits == 0)
-  doAssert( U64A.firstSetBit == 1)
-  doAssert( I64A.firstSetBit == 1)
-  doAssert( U64A.parityBits == 1)
-  doAssert( I64A.parityBits == 1)
-  doAssert( U64A.countSetBits == 29)
-  doAssert( I64A.countSetBits == 29)
-  doAssert( U64A.rotateLeftBits(37) == 0b00101001_00001111_01000010_00101000_10000111_11101111_10010001_01010011'u64)
-  doAssert( U64A.rotateRightBits(37) == 0b01010100_11001010_01000011_11010000_10001010_00100001_11111011_11100100'u64)
-
-  doAssert( U64B.firstSetBit == 36)
-  doAssert( I64B.firstSetBit == 36)
-
-  doAssert( U32.fastLog2 == 31)
-  doAssert( I32.fastLog2 == 31)
-  doAssert( U32.countLeadingZeroBits == 0)
-  doAssert( I32.countLeadingZeroBits == 0)
-  doAssert( U32.countTrailingZeroBits == 4)
-  doAssert( I32.countTrailingZeroBits == 4)
-  doAssert( U32.firstSetBit == 5)
-  doAssert( I32.firstSetBit == 5)
-  doAssert( U32.parityBits == 0)
-  doAssert( I32.parityBits == 0)
-  doAssert( U32.countSetBits == 16)
-  doAssert( I32.countSetBits == 16)
-  doAssert( U32.rotateLeftBits(21) == 0b01001010_00011010_10110011_10011011'u32)
-  doAssert( U32.rotateRightBits(21) == 0b11100110_11010010_10000110_10101100'u32)
-
-  doAssert( U16.fastLog2 == 13)
-  doAssert( I16.fastLog2 == 13)
-  doAssert( U16.countLeadingZeroBits == 2)
-  doAssert( I16.countLeadingZeroBits == 2)
-  doAssert( U16.countTrailingZeroBits == 3)
-  doAssert( I16.countTrailingZeroBits == 3)
-  doAssert( U16.firstSetBit == 4)
-  doAssert( I16.firstSetBit == 4)
-  doAssert( U16.parityBits == 0)
-  doAssert( I16.parityBits == 0)
-  doAssert( U16.countSetBits == 6)
-  doAssert( I16.countSetBits == 6)
-  doAssert( U16.rotateLeftBits(12) == 0b10000010_01110010'u16)
-  doAssert( U16.rotateRightBits(12) == 0b01110010_10000010'u16)
-
-  doAssert( U8.fastLog2 == 5)
-  doAssert( I8.fastLog2 == 5)
-  doAssert( U8.countLeadingZeroBits == 2)
-  doAssert( I8.countLeadingZeroBits == 2)
-  doAssert( U8.countTrailingZeroBits == 1)
-  doAssert( I8.countTrailingZeroBits == 1)
-  doAssert( U8.firstSetBit == 2)
-  doAssert( I8.firstSetBit == 2)
-  doAssert( U8.parityBits == 1)
-  doAssert( I8.parityBits == 1)
-  doAssert( U8.countSetBits == 3)
-  doAssert( I8.countSetBits == 3)
-  doAssert( U8.rotateLeftBits(3) == 0b10010001'u8)
-  doAssert( U8.rotateRightBits(3) == 0b0100_0110'u8)
-
-  static :
-    # test bitopts at compile time with vm
-    doAssert( U8.fastLog2 == 5)
-    doAssert( I8.fastLog2 == 5)
-    doAssert( U8.countLeadingZeroBits == 2)
-    doAssert( I8.countLeadingZeroBits == 2)
-    doAssert( U8.countTrailingZeroBits == 1)
-    doAssert( I8.countTrailingZeroBits == 1)
-    doAssert( U8.firstSetBit == 2)
-    doAssert( I8.firstSetBit == 2)
-    doAssert( U8.parityBits == 1)
-    doAssert( I8.parityBits == 1)
-    doAssert( U8.countSetBits == 3)
-    doAssert( I8.countSetBits == 3)
-    doAssert( U8.rotateLeftBits(3) == 0b10010001'u8)
-    doAssert( U8.rotateRightBits(3) == 0b0100_0110'u8)
+  doAssert U64B.firstSetBit == 36
+  doAssert I64B.firstSetBit == 36
 
+  doAssert U32.fastLog2 == 31
+  doAssert I32.fastLog2 == 31
+  doAssert U32.countLeadingZeroBits == 0
+  doAssert I32.countLeadingZeroBits == 0
+  doAssert U32.countTrailingZeroBits == 4
+  doAssert I32.countTrailingZeroBits == 4
+  doAssert U32.firstSetBit == 5
+  doAssert I32.firstSetBit == 5
+  doAssert U32.parityBits == 0
+  doAssert I32.parityBits == 0
+  doAssert U32.countSetBits == 16
+  doAssert I32.countSetBits == 16
+  doAssert U32.rotateLeftBits(21) == 0b01001010_00011010_10110011_10011011'u32
+  doAssert U32.rotateRightBits(21) == 0b11100110_11010010_10000110_10101100'u32
 
+  doAssert U16.fastLog2 == 13
+  doAssert I16.fastLog2 == 13
+  doAssert U16.countLeadingZeroBits == 2
+  doAssert I16.countLeadingZeroBits == 2
+  doAssert U16.countTrailingZeroBits == 3
+  doAssert I16.countTrailingZeroBits == 3
+  doAssert U16.firstSetBit == 4
+  doAssert I16.firstSetBit == 4
+  doAssert U16.parityBits == 0
+  doAssert I16.parityBits == 0
+  doAssert U16.countSetBits == 6
+  doAssert I16.countSetBits == 6
+  doAssert U16.rotateLeftBits(12) == 0b10000010_01110010'u16
+  doAssert U16.rotateRightBits(12) == 0b01110010_10000010'u16
+
+  doAssert U8.fastLog2 == 5
+  doAssert I8.fastLog2 == 5
+  doAssert U8.countLeadingZeroBits == 2
+  doAssert I8.countLeadingZeroBits == 2
+  doAssert U8.countTrailingZeroBits == 1
+  doAssert I8.countTrailingZeroBits == 1
+  doAssert U8.firstSetBit == 2
+  doAssert I8.firstSetBit == 2
+  doAssert U8.parityBits == 1
+  doAssert I8.parityBits == 1
+  doAssert U8.countSetBits == 3
+  doAssert I8.countSetBits == 3
+  doAssert U8.rotateLeftBits(3) == 0b10010001'u8
+  doAssert U8.rotateRightBits(3) == 0b0100_0110'u8
 
   template test_undefined_impl(ffunc: untyped; expected: int; is_static: bool) =
-    doAssert( ffunc(0'u8) == expected)
-    doAssert( ffunc(0'i8) == expected)
-    doAssert( ffunc(0'u16) == expected)
-    doAssert( ffunc(0'i16) == expected)
-    doAssert( ffunc(0'u32) == expected)
-    doAssert( ffunc(0'i32) == expected)
-    doAssert( ffunc(0'u64) == expected)
-    doAssert( ffunc(0'i64) == expected)
+    doAssert ffunc(0'u8) == expected
+    doAssert ffunc(0'i8) == expected
+    doAssert ffunc(0'u16) == expected
+    doAssert ffunc(0'i16) == expected
+    doAssert ffunc(0'u32) == expected
+    doAssert ffunc(0'i32) == expected
+    doAssert ffunc(0'u64) == expected
+    doAssert ffunc(0'i64) == expected
 
   template test_undefined(ffunc: untyped; expected: int) =
     test_undefined_impl(ffunc, expected, false)
@@ -124,45 +152,200 @@ proc main() =
     test_undefined(fastLog2, -1)
 
     # check for undefined behavior with rotate by zero.
-    doAssert( U8.rotateLeftBits(0) == U8)
-    doAssert( U8.rotateRightBits(0) == U8)
-    doAssert( U16.rotateLeftBits(0) == U16)
-    doAssert( U16.rotateRightBits(0) == U16)
-    doAssert( U32.rotateLeftBits(0) == U32)
-    doAssert( U32.rotateRightBits(0) == U32)
-    doAssert( U64A.rotateLeftBits(0) == U64A)
-    doAssert( U64A.rotateRightBits(0) == U64A)
+    doAssert U8.rotateLeftBits(0) == U8
+    doAssert U8.rotateRightBits(0) == U8
+    doAssert U16.rotateLeftBits(0) == U16
+    doAssert U16.rotateRightBits(0) == U16
+    doAssert U32.rotateLeftBits(0) == U32
+    doAssert U32.rotateRightBits(0) == U32
+    doAssert U64A.rotateLeftBits(0) == U64A
+    doAssert U64A.rotateRightBits(0) == U64A
 
     # check for undefined behavior with rotate by integer width.
-    doAssert( U8.rotateLeftBits(8) == U8)
-    doAssert( U8.rotateRightBits(8) == U8)
-    doAssert( U16.rotateLeftBits(16) == U16)
-    doAssert( U16.rotateRightBits(16) == U16)
-    doAssert( U32.rotateLeftBits(32) == U32)
-    doAssert( U32.rotateRightBits(32) == U32)
-    doAssert( U64A.rotateLeftBits(64) == U64A)
-    doAssert( U64A.rotateRightBits(64) == U64A)
-
-    static:    # check for undefined behavior with rotate by zero.
-      doAssert( U8.rotateLeftBits(0) == U8)
-      doAssert( U8.rotateRightBits(0) == U8)
-      doAssert( U16.rotateLeftBits(0) == U16)
-      doAssert( U16.rotateRightBits(0) == U16)
-      doAssert( U32.rotateLeftBits(0) == U32)
-      doAssert( U32.rotateRightBits(0) == U32)
-      doAssert( U64A.rotateLeftBits(0) == U64A)
-      doAssert( U64A.rotateRightBits(0) == U64A)
-
-      # check for undefined behavior with rotate by integer width.
-      doAssert( U8.rotateLeftBits(8) == U8)
-      doAssert( U8.rotateRightBits(8) == U8)
-      doAssert( U16.rotateLeftBits(16) == U16)
-      doAssert( U16.rotateRightBits(16) == U16)
-      doAssert( U32.rotateLeftBits(32) == U32)
-      doAssert( U32.rotateRightBits(32) == U32)
-      doAssert( U64A.rotateLeftBits(64) == U64A)
-      doAssert( U64A.rotateRightBits(64) == U64A)
+    doAssert U8.rotateLeftBits(8) == U8
+    doAssert U8.rotateRightBits(8) == U8
+    doAssert U16.rotateLeftBits(16) == U16
+    doAssert U16.rotateRightBits(16) == U16
+    doAssert U32.rotateLeftBits(32) == U32
+    doAssert U32.rotateRightBits(32) == U32
+    doAssert U64A.rotateLeftBits(64) == U64A
+    doAssert U64A.rotateRightBits(64) == U64A
+
+  block:
+    # basic mask operations (mutating)
+    var v: uint8
+    v.setMask(0b1100_0000)
+    v.setMask(0b0000_1100)
+    doAssert v == 0b1100_1100
+    v.flipMask(0b0101_0101)
+    doAssert v == 0b1001_1001
+    v.clearMask(0b1000_1000)
+    doAssert v == 0b0001_0001
+    v.clearMask(0b0001_0001)
+    doAssert v == 0b0000_0000
+    v.setMask(0b0001_1110)
+    doAssert v == 0b0001_1110
+    v.mask(0b0101_0100)
+    doAssert v == 0b0001_0100
+  block:
+    # basic mask operations (non-mutating)
+    let v = 0b1100_0000'u8
+    doAssert v.masked(0b0000_1100) == 0b0000_0000
+    doAssert v.masked(0b1000_1100) == 0b1000_0000
+    doAssert v.setMasked(0b0000_1100) == 0b1100_1100
+    doAssert v.setMasked(0b1000_1110) == 0b1100_1110
+    doAssert v.flipMasked(0b1100_1000) == 0b0000_1000
+    doAssert v.flipMasked(0b0000_1100) == 0b1100_1100
+    let t = 0b1100_0110'u8
+    doAssert t.clearMasked(0b0100_1100) == 0b1000_0010
+    doAssert t.clearMasked(0b1100_0000) == 0b0000_0110
+  block:
+    # basic bitslice opeartions
+    let a = 0b1111_1011'u8
+    doAssert a.bitsliced(0 .. 3) == 0b1011
+    doAssert a.bitsliced(2 .. 3) == 0b10
+    doAssert a.bitsliced(4 .. 7) == 0b1111
+
+    # same thing, but with exclusive ranges.
+    doAssert a.bitsliced(0 ..< 4) == 0b1011
+    doAssert a.bitsliced(2 ..< 4) == 0b10
+    doAssert a.bitsliced(4 ..< 8) == 0b1111
+
+    # mutating
+    var b = 0b1111_1011'u8
+    b.bitslice(1 .. 3)
+    doAssert b == 0b101
+
+    # loop test:
+    let c = 0b1111_1111'u8
+    for i in 0 .. 7:
+      doAssert c.bitsliced(i .. 7) == c shr i
+  block:
+    # bitslice versions of mask operations (mutating)
+    var a = 0b1100_1100'u8
+    let b = toMask[uint8](2 .. 3)
+    a.mask(b)
+    doAssert a == 0b0000_1100
+    a.setMask(4 .. 7)
+    doAssert a == 0b1111_1100
+    a.flipMask(1 .. 3)
+    doAssert a == 0b1111_0010
+    a.flipMask(2 .. 4)
+    doAssert a == 0b1110_1110
+    a.clearMask(2 .. 4)
+    doAssert a == 0b1110_0010
+    a.mask(0 .. 3)
+    doAssert a == 0b0000_0010
+
+    # composition of mask from slices:
+    let c = bitor(toMask[uint8](2 .. 3), toMask[uint8](5 .. 7))
+    doAssert c == 0b1110_1100'u8
+  block:
+    # bitslice versions of mask operations (non-mutating)
+    let a = 0b1100_1100'u8
+    doAssert a.masked(toMask[uint8](2 .. 3)) == 0b0000_1100
+    doAssert a.masked(2 .. 3) == 0b0000_1100
+    doAssert a.setMasked(0 .. 3) == 0b1100_1111
+    doAssert a.setMasked(3 .. 4) == 0b1101_1100
+    doAssert a.flipMasked(0 .. 3) == 0b1100_0011
+    doAssert a.flipMasked(0 .. 7) == 0b0011_0011
+    doAssert a.flipMasked(2 .. 3) == 0b1100_0000
+    doAssert a.clearMasked(2 .. 3) == 0b1100_0000
+    doAssert a.clearMasked(3 .. 6) == 0b1000_0100
+  block:
+    # single bit operations
+    var v: uint8
+    v.setBit(0)
+    doAssert v == 0x0000_0001
+    v.setBit(1)
+    doAssert v == 0b0000_0011
+    v.flipBit(7)
+    doAssert v == 0b1000_0011
+    v.clearBit(0)
+    doAssert v == 0b1000_0010
+    v.flipBit(1)
+    doAssert v == 0b1000_0000
+    doAssert v.testBit(7)
+    doAssert not v.testBit(6)
+  block:
+    # multi bit operations
+    var v: uint8
+    v.setBits(0, 1, 7)
+    doAssert v == 0b1000_0011
+    v.flipBits(2, 3)
+    doAssert v == 0b1000_1111
+    v.clearBits(7, 0, 1)
+    doAssert v == 0b0000_1100
+  block:
+    # signed
+    var v: int8
+    v.setBit(7)
+    doAssert v == -128
+  block:
+    var v: uint64
+    v.setBit(63)
+    doAssert v == 0b1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000'u64
+
+  block:
+    proc testReverseBitsInvo(x: SomeUnsignedInt) =
+      doAssert reverseBits(reverseBits(x)) == x
+
+    proc testReverseBitsPerType(x, reversed: uint64) =
+      doAssert reverseBits(x) == reversed
+      doAssert reverseBits(cast[uint32](x)) == cast[uint32](reversed shr 32)
+      doAssert reverseBits(cast[uint32](x shr 16)) == cast[uint32](reversed shr 16)
+      doAssert reverseBits(cast[uint16](x)) == cast[uint16](reversed shr 48)
+      doAssert reverseBits(cast[uint8](x)) == cast[uint8](reversed shr 56)
+
+      testReverseBitsInvo(x)
+      testReverseBitsInvo(cast[uint32](x))
+      testReverseBitsInvo(cast[uint16](x))
+      testReverseBitsInvo(cast[uint8](x))
+
+    proc testReverseBitsRefl(x, reversed: uint64) =
+      testReverseBitsPerType(x, reversed)
+      testReverseBitsPerType(reversed, x)
+
+    proc testReverseBitsShift(d, b: uint64) =
+      var
+        x = d
+        y = b
+
+      for i in 1..64:
+        testReverseBitsRefl(x, y)
+        x = x shl 1
+        y = y shr 1
+
+    proc testReverseBits(d, b: uint64) =
+      testReverseBitsShift(d, b)
+
+    testReverseBits(0x0u64, 0x0u64)
+    testReverseBits(0xffffffffffffffffu64, 0xffffffffffffffffu64)
+    testReverseBits(0x0123456789abcdefu64, 0xf7b3d591e6a2c480u64)
+    testReverseBits(0x5555555555555555u64, 0xaaaaaaaaaaaaaaaau64)
+    testReverseBits(0x5555555500000001u64, 0x80000000aaaaaaaau64)
+    testReverseBits(0x55555555aaaaaaaau64, 0x55555555aaaaaaaau64)
+    testReverseBits(0xf0f0f0f00f0f0f0fu64, 0xf0f0f0f00f0f0f0fu64)
+    testReverseBits(0x181881810ff00916u64, 0x68900ff081811818u64)
 
   echo "OK"
 
+  # bug #7587
+  doAssert popcount(0b11111111'i8) == 8
+
+block: # not ready for vm because exception is compile error
+  try:
+    var v: uint32
+    var i = 32
+    v.setBit(i)
+    doAssert false
+  except RangeDefect:
+    discard
+  except:
+    doAssert false
+
+
 main()
+static:
+  # test everything on vm as well
+  main()
diff --git a/tests/stdlib/tbitops.nim.cfg b/tests/stdlib/tbitops.nim.cfg
index f0d7668a7..013e8d38e 100644
--- a/tests/stdlib/tbitops.nim.cfg
+++ b/tests/stdlib/tbitops.nim.cfg
@@ -1 +1 @@
--d:noUndefinedBitOps
+-d:noUndefinedBitOpts
diff --git a/tests/stdlib/tbitops2.nim b/tests/stdlib/tbitops2.nim
deleted file mode 100644
index 31952316c..000000000
--- a/tests/stdlib/tbitops2.nim
+++ /dev/null
@@ -1,168 +0,0 @@
-discard """
-  file: "tbitops.nim"
-  output: "OK"
-"""
-import bitops
-
-
-proc main() =
-  const U8 = 0b0011_0010'u8
-  const I8 = 0b0011_0010'i8
-  const U16 = 0b00100111_00101000'u16
-  const I16 = 0b00100111_00101000'i16
-  const U32 = 0b11010101_10011100_11011010_01010000'u32
-  const I32 = 0b11010101_10011100_11011010_01010000'i32
-  const U64A = 0b01000100_00111111_01111100_10001010_10011001_01001000_01111010_00010001'u64
-  const I64A = 0b01000100_00111111_01111100_10001010_10011001_01001000_01111010_00010001'i64
-  const U64B = 0b00110010_11011101_10001111_00101000_00000000_00000000_00000000_00000000'u64
-  const I64B = 0b00110010_11011101_10001111_00101000_00000000_00000000_00000000_00000000'i64
-
-  doAssert( U64A.fastLog2 == 62)
-  doAssert( I64A.fastLog2 == 62)
-  doAssert( U64A.countLeadingZeroBits == 1)
-  doAssert( I64A.countLeadingZeroBits == 1)
-  doAssert( U64A.countTrailingZeroBits == 0)
-  doAssert( I64A.countTrailingZeroBits == 0)
-  doAssert( U64A.firstSetBit == 1)
-  doAssert( I64A.firstSetBit == 1)
-  doAssert( U64A.parityBits == 1)
-  doAssert( I64A.parityBits == 1)
-  doAssert( U64A.countSetBits == 29)
-  doAssert( I64A.countSetBits == 29)
-  doAssert( U64A.rotateLeftBits(37) == 0b00101001_00001111_01000010_00101000_10000111_11101111_10010001_01010011'u64)
-  doAssert( U64A.rotateRightBits(37) == 0b01010100_11001010_01000011_11010000_10001010_00100001_11111011_11100100'u64)
-
-  doAssert( U64B.firstSetBit == 36)
-  doAssert( I64B.firstSetBit == 36)
-
-  doAssert( U32.fastLog2 == 31)
-  doAssert( I32.fastLog2 == 31)
-  doAssert( U32.countLeadingZeroBits == 0)
-  doAssert( I32.countLeadingZeroBits == 0)
-  doAssert( U32.countTrailingZeroBits == 4)
-  doAssert( I32.countTrailingZeroBits == 4)
-  doAssert( U32.firstSetBit == 5)
-  doAssert( I32.firstSetBit == 5)
-  doAssert( U32.parityBits == 0)
-  doAssert( I32.parityBits == 0)
-  doAssert( U32.countSetBits == 16)
-  doAssert( I32.countSetBits == 16)
-  doAssert( U32.rotateLeftBits(21) == 0b01001010_00011010_10110011_10011011'u32)
-  doAssert( U32.rotateRightBits(21) == 0b11100110_11010010_10000110_10101100'u32)
-
-  doAssert( U16.fastLog2 == 13)
-  doAssert( I16.fastLog2 == 13)
-  doAssert( U16.countLeadingZeroBits == 2)
-  doAssert( I16.countLeadingZeroBits == 2)
-  doAssert( U16.countTrailingZeroBits == 3)
-  doAssert( I16.countTrailingZeroBits == 3)
-  doAssert( U16.firstSetBit == 4)
-  doAssert( I16.firstSetBit == 4)
-  doAssert( U16.parityBits == 0)
-  doAssert( I16.parityBits == 0)
-  doAssert( U16.countSetBits == 6)
-  doAssert( I16.countSetBits == 6)
-  doAssert( U16.rotateLeftBits(12) == 0b10000010_01110010'u16)
-  doAssert( U16.rotateRightBits(12) == 0b01110010_10000010'u16)
-
-  doAssert( U8.fastLog2 == 5)
-  doAssert( I8.fastLog2 == 5)
-  doAssert( U8.countLeadingZeroBits == 2)
-  doAssert( I8.countLeadingZeroBits == 2)
-  doAssert( U8.countTrailingZeroBits == 1)
-  doAssert( I8.countTrailingZeroBits == 1)
-  doAssert( U8.firstSetBit == 2)
-  doAssert( I8.firstSetBit == 2)
-  doAssert( U8.parityBits == 1)
-  doAssert( I8.parityBits == 1)
-  doAssert( U8.countSetBits == 3)
-  doAssert( I8.countSetBits == 3)
-  doAssert( U8.rotateLeftBits(3) == 0b10010001'u8)
-  doAssert( U8.rotateRightBits(3) == 0b0100_0110'u8)
-
-  static :
-    # test bitopts at compile time with vm
-    doAssert( U8.fastLog2 == 5)
-    doAssert( I8.fastLog2 == 5)
-    doAssert( U8.countLeadingZeroBits == 2)
-    doAssert( I8.countLeadingZeroBits == 2)
-    doAssert( U8.countTrailingZeroBits == 1)
-    doAssert( I8.countTrailingZeroBits == 1)
-    doAssert( U8.firstSetBit == 2)
-    doAssert( I8.firstSetBit == 2)
-    doAssert( U8.parityBits == 1)
-    doAssert( I8.parityBits == 1)
-    doAssert( U8.countSetBits == 3)
-    doAssert( I8.countSetBits == 3)
-    doAssert( U8.rotateLeftBits(3) == 0b10010001'u8)
-    doAssert( U8.rotateRightBits(3) == 0b0100_0110'u8)
-
-
-
-  template test_undefined_impl(ffunc: untyped; expected: int; is_static: bool) =
-    doAssert( ffunc(0'u8) == expected)
-    doAssert( ffunc(0'i8) == expected)
-    doAssert( ffunc(0'u16) == expected)
-    doAssert( ffunc(0'i16) == expected)
-    doAssert( ffunc(0'u32) == expected)
-    doAssert( ffunc(0'i32) == expected)
-    doAssert( ffunc(0'u64) == expected)
-    doAssert( ffunc(0'i64) == expected)
-
-  template test_undefined(ffunc: untyped; expected: int) =
-    test_undefined_impl(ffunc, expected, false)
-    static:
-      test_undefined_impl(ffunc, expected, true)
-
-  when defined(noUndefinedBitOpts):
-    # check for undefined behavior with zero.
-    test_undefined(countSetBits, 0)
-    test_undefined(parityBits, 0)
-    test_undefined(firstSetBit, 0)
-    test_undefined(countLeadingZeroBits, 0)
-    test_undefined(countTrailingZeroBits, 0)
-    test_undefined(fastLog2, -1)
-
-    # check for undefined behavior with rotate by zero.
-    doAssert( U8.rotateLeftBits(0) == U8)
-    doAssert( U8.rotateRightBits(0) == U8)
-    doAssert( U16.rotateLeftBits(0) == U16)
-    doAssert( U16.rotateRightBits(0) == U16)
-    doAssert( U32.rotateLeftBits(0) == U32)
-    doAssert( U32.rotateRightBits(0) == U32)
-    doAssert( U64A.rotateLeftBits(0) == U64A)
-    doAssert( U64A.rotateRightBits(0) == U64A)
-
-    # check for undefined behavior with rotate by integer width.
-    doAssert( U8.rotateLeftBits(8) == U8)
-    doAssert( U8.rotateRightBits(8) == U8)
-    doAssert( U16.rotateLeftBits(16) == U16)
-    doAssert( U16.rotateRightBits(16) == U16)
-    doAssert( U32.rotateLeftBits(32) == U32)
-    doAssert( U32.rotateRightBits(32) == U32)
-    doAssert( U64A.rotateLeftBits(64) == U64A)
-    doAssert( U64A.rotateRightBits(64) == U64A)
-
-    static:    # check for undefined behavior with rotate by zero.
-      doAssert( U8.rotateLeftBits(0) == U8)
-      doAssert( U8.rotateRightBits(0) == U8)
-      doAssert( U16.rotateLeftBits(0) == U16)
-      doAssert( U16.rotateRightBits(0) == U16)
-      doAssert( U32.rotateLeftBits(0) == U32)
-      doAssert( U32.rotateRightBits(0) == U32)
-      doAssert( U64A.rotateLeftBits(0) == U64A)
-      doAssert( U64A.rotateRightBits(0) == U64A)
-
-      # check for undefined behavior with rotate by integer width.
-      doAssert( U8.rotateLeftBits(8) == U8)
-      doAssert( U8.rotateRightBits(8) == U8)
-      doAssert( U16.rotateLeftBits(16) == U16)
-      doAssert( U16.rotateRightBits(16) == U16)
-      doAssert( U32.rotateLeftBits(32) == U32)
-      doAssert( U32.rotateRightBits(32) == U32)
-      doAssert( U64A.rotateLeftBits(64) == U64A)
-      doAssert( U64A.rotateRightBits(64) == U64A)
-
-  echo "OK"
-
-main()
diff --git a/tests/stdlib/tbitops2.nim.cfg b/tests/stdlib/tbitops2.nim.cfg
deleted file mode 100644
index e1cb77e82..000000000
--- a/tests/stdlib/tbitops2.nim.cfg
+++ /dev/null
@@ -1,2 +0,0 @@
--d:noIntrinsicsBitOpts
--d:noUndefinedBitOps
diff --git a/tests/stdlib/tbitops_utils.nim b/tests/stdlib/tbitops_utils.nim
new file mode 100644
index 000000000..e3f96fecc
--- /dev/null
+++ b/tests/stdlib/tbitops_utils.nim
@@ -0,0 +1,19 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/private/bitops_utils
+import std/assertions
+
+template chk(a, b) =
+  let a2 = castToUnsigned(a)
+  doAssert a2 == b
+  doAssert type(a2) is type(b)
+  doAssert type(b) is type(a2)
+
+chk 1'i8, 1'u8
+chk -1'i8, 255'u8
+chk 1'u8, 1'u8
+chk 1'u, 1'u
+chk -1, cast[uint](-1)
+chk -1'i64, cast[uint64](-1)
diff --git a/tests/stdlib/tbug5382.nim b/tests/stdlib/tbug5382.nim
deleted file mode 100644
index c86656d32..000000000
--- a/tests/stdlib/tbug5382.nim
+++ /dev/null
@@ -1,11 +0,0 @@
-discard """
-  output: '''
-02
-'''
-"""
-import re
-
-let regexp = re"^\/([0-9]{2})\.html$"
-var matches: array[1, string]
-discard "/02.html".find(regexp, matches)
-echo matches[0]
diff --git a/tests/stdlib/tcasts.nim b/tests/stdlib/tcasts.nim
new file mode 100644
index 000000000..e01c7e940
--- /dev/null
+++ b/tests/stdlib/tcasts.nim
@@ -0,0 +1,26 @@
+import std/[strutils]
+import std/[assertions, objectdollar]
+
+# bug #19101
+type
+  Small = object
+    a: int
+
+  Big = object
+    a, b, c, d: int
+
+proc main =
+  var
+    n = 1'i8
+    f = 2.0
+    s = Small(a: 1)
+    b = Big(a: 12345, b: 23456, c: 34567, d: 45678)
+
+  doAssert $cast[int](f).toBin(64) == "0100000000000000000000000000000000000000000000000000000000000000"
+  f = cast[float](n)
+  doAssert $cast[int](f).toBin(64) == "0000000000000000000000000000000000000000000000000000000000000001"
+
+  doAssert $b == "(a: 12345, b: 23456, c: 34567, d: 45678)"
+  b = cast[Big](s)
+  doAssert $b == "(a: 1, b: 0, c: 0, d: 0)"
+main()
diff --git a/tests/stdlib/tcgi.nim b/tests/stdlib/tcgi.nim
new file mode 100644
index 000000000..ef39450da
--- /dev/null
+++ b/tests/stdlib/tcgi.nim
@@ -0,0 +1,31 @@
+discard """

+  matrix: "--mm:refc; --mm:orc"

+"""

+

+import std/unittest

+import std/[cgi, strtabs, sugar]

+import std/assertions

+

+block: # Test cgi module

+  const queryString = "foo=bar&фу=бар&checked=✓&list=1,2,3&with_space=text%20with%20space"

+

+  block: # test query parsing with readData

+    let parsedQuery = readData(queryString)

+

+    check parsedQuery["foo"] == "bar"

+    check parsedQuery["фу"] == "бар"

+    check parsedQuery["checked"] == "✓"

+    check parsedQuery["list"] == "1,2,3"

+    check parsedQuery["with_space"] == "text with space"

+

+    expect KeyError:

+      discard parsedQuery["not_existing_key"]

+

+# bug #15369

+let queryString = "a=1&b=0&c=3&d&e&a=5&a=t%20e%20x%20t&e=http%3A%2F%2Fw3schools.com%2Fmy%20test.asp%3Fname%3Dst%C3%A5le%26car%3Dsaab"

+

+doAssert collect(for pair in decodeData(queryString): pair) ==

+  @[("a", "1"), ("b", "0"), ("c", "3"),

+    ("d", ""),("e", ""), ("a", "5"), ("a", "t e x t"),

+  ("e", "http://w3schools.com/my test.asp?name=ståle&car=saab")

+]

diff --git a/tests/stdlib/tclosures.nim b/tests/stdlib/tclosures.nim
new file mode 100644
index 000000000..84b033fa8
--- /dev/null
+++ b/tests/stdlib/tclosures.nim
@@ -0,0 +1,47 @@
+discard """
+  targets: "c js"
+"""
+
+import std/assertions
+
+block: # bug #4299
+  proc scopeProc() =
+    proc normalProc() =
+      discard
+
+    proc genericProc[T]() =
+      normalProc()
+
+    genericProc[string]()
+
+  scopeProc()
+
+block: # bug #12492
+  proc foo() =
+    var i = 0
+    proc bar() =
+      inc i
+
+    bar()
+    doAssert i == 1
+
+  foo()
+  static:
+    foo()
+
+block: # bug #10849
+  type
+    Generic[T] = ref object
+      getState: proc(): T
+
+  proc newGeneric[T](): Generic[T] =
+    var state: T
+
+    proc getState[T](): T =
+      state
+
+    Generic[T](getState: getState)
+
+  let g = newGeneric[int]()
+  let state = g.getState()
+  doAssert state == 0
diff --git a/tests/stdlib/tcmdline.nim b/tests/stdlib/tcmdline.nim
new file mode 100644
index 000000000..8b428900b
--- /dev/null
+++ b/tests/stdlib/tcmdline.nim
@@ -0,0 +1,13 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c js"
+  joinable: false
+"""
+
+import std/os
+import std/assertions
+
+var params = paramCount()
+doAssert params == 0
+doAssert paramStr(0).len > 0
+doAssert commandLineParams().len == 0
diff --git a/tests/stdlib/tcomplex.nim b/tests/stdlib/tcomplex.nim
new file mode 100644
index 000000000..ca83314b9
--- /dev/null
+++ b/tests/stdlib/tcomplex.nim
@@ -0,0 +1,115 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/[complex, math]
+import std/assertions
+
+proc `=~`[T](x, y: Complex[T]): bool =
+  result = abs(x.re-y.re) < 1e-6 and abs(x.im-y.im) < 1e-6
+
+proc `=~`[T](x: Complex[T]; y: T): bool =
+  result = abs(x.re-y) < 1e-6 and abs(x.im) < 1e-6
+
+let
+  z: Complex64 = complex(0.0, 0.0)
+  oo: Complex64 = complex(1.0, 1.0)
+  a: Complex64 = complex(1.0, 2.0)
+  b: Complex64 = complex(-1.0, -2.0)
+  m1: Complex64 = complex(-1.0, 0.0)
+  i: Complex64 = complex(0.0, 1.0)
+  one: Complex64 = complex(1.0, 0.0)
+  tt: Complex64 = complex(10.0, 20.0)
+  ipi: Complex64 = complex(0.0, -PI)
+
+doAssert(a/2.0 =~ complex(0.5, 1.0))
+doAssert(a == a)
+doAssert((a-a) == z)
+doAssert((a+b) == z)
+doAssert((a+b) =~ 0.0)
+doAssert((a/b) == m1)
+doAssert((1.0/a) =~ complex(0.2, -0.4))
+doAssert((a*b) == complex(3.0, -4.0))
+doAssert(10.0*a == tt)
+doAssert(a*10.0 == tt)
+doAssert(tt/10.0 == a)
+doAssert(oo+(-1.0) == i)
+doAssert( (-1.0)+oo == i)
+doAssert(abs(oo) == sqrt(2.0))
+doAssert(conjugate(a) == complex(1.0, -2.0))
+doAssert(sqrt(m1) == i)
+doAssert(exp(ipi) =~ m1)
+
+doAssert(pow(a, b) =~ complex(-3.72999124927876, -1.68815826725068))
+doAssert(pow(z, a) =~ complex(0.0, 0.0))
+doAssert(pow(z, z) =~ complex(1.0, 0.0))
+doAssert(pow(a, one) =~ a)
+doAssert(pow(a, m1) =~ complex(0.2, -0.4))
+doAssert(pow(a, 2.0) =~ complex(-3.0, 4.0))
+doAssert(pow(a, 2) =~ complex(-3.0, 4.0))
+doAssert(not(pow(a, 2.0) =~ a))
+
+doAssert(ln(a) =~ complex(0.804718956217050, 1.107148717794090))
+doAssert(log10(a) =~ complex(0.349485002168009, 0.480828578784234))
+doAssert(log2(a) =~ complex(1.16096404744368, 1.59727796468811))
+
+doAssert(sin(a) =~ complex(3.16577851321617, 1.95960104142161))
+doAssert(cos(a) =~ complex(2.03272300701967, -3.05189779915180))
+doAssert(tan(a) =~ complex(0.0338128260798967, 1.0147936161466335))
+doAssert(cot(a) =~ 1.0 / tan(a))
+doAssert(sec(a) =~ 1.0 / cos(a))
+doAssert(csc(a) =~ 1.0 / sin(a))
+doAssert(arcsin(a) =~ complex(0.427078586392476, 1.528570919480998))
+doAssert(arccos(a) =~ complex(1.14371774040242, -1.52857091948100))
+doAssert(arctan(a) =~ complex(1.338972522294494, 0.402359478108525))
+doAssert(arccot(a) =~ complex(0.2318238045004031, -0.402359478108525))
+doAssert(arcsec(a) =~ complex(1.384478272687081, 0.3965682301123288))
+doAssert(arccsc(a) =~ complex(0.1863180541078155, -0.3965682301123291))
+
+doAssert(cosh(a) =~ complex(-0.642148124715520, 1.068607421382778))
+doAssert(sinh(a) =~ complex(-0.489056259041294, 1.403119250622040))
+doAssert(tanh(a) =~ complex(1.1667362572409199, -0.243458201185725))
+doAssert(sech(a) =~ 1.0 / cosh(a))
+doAssert(csch(a) =~ 1.0 / sinh(a))
+doAssert(coth(a) =~ 1.0 / tanh(a))
+doAssert(arccosh(a) =~ complex(1.528570919480998, 1.14371774040242))
+doAssert(arcsinh(a) =~ complex(1.469351744368185, 1.06344002357775))
+doAssert(arctanh(a) =~ complex(0.173286795139986, 1.17809724509617))
+doAssert(arcsech(a) =~ arccosh(1.0/a))
+doAssert(arccsch(a) =~ arcsinh(1.0/a))
+doAssert(arccoth(a) =~ arctanh(1.0/a))
+
+doAssert(phase(a) == 1.1071487177940904)
+let t = polar(a)
+doAssert(rect(t.r, t.phi) =~ a)
+doAssert(rect(1.0, 2.0) =~ complex(-0.4161468365471424, 0.9092974268256817))
+
+doAssert(almostEqual(a, a + complex(1e-16, 1e-16)))
+doAssert(almostEqual(a, a + complex(2e-15, 2e-15), unitsInLastPlace = 5))
+
+
+let
+  i64: Complex32 = complex(0.0f, 1.0f)
+  a64: Complex32 = 2.0f*i64 + 1.0.float32
+  b64: Complex32 = complex(-1.0'f32, -2.0'f32)
+
+doAssert(a64 == a64)
+doAssert(a64 == -b64)
+doAssert(a64 + b64 =~ 0.0'f32)
+doAssert(not(pow(a64, b64) =~ a64))
+doAssert(pow(a64, 0.5f) =~ sqrt(a64))
+doAssert(pow(a64, 2) =~ complex(-3.0'f32, 4.0'f32))
+doAssert(sin(arcsin(b64)) =~ b64)
+doAssert(cosh(arccosh(a64)) =~ a64)
+
+doAssert(phase(a64) - 1.107149f < 1e-6)
+let t64 = polar(a64)
+doAssert(rect(t64.r, t64.phi) =~ a64)
+doAssert(rect(1.0f, 2.0f) =~ complex(-0.4161468f, 0.90929742f))
+doAssert(sizeof(a64) == 8)
+doAssert(sizeof(a) == 16)
+
+doAssert 123.0.im + 456.0 == complex64(456, 123)
+
+let localA = complex(0.1'f32)
+doAssert localA.im is float32
diff --git a/tests/stdlib/tcookies.nim b/tests/stdlib/tcookies.nim
new file mode 100644
index 000000000..3ff0f3bae
--- /dev/null
+++ b/tests/stdlib/tcookies.nim
@@ -0,0 +1,22 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c js"
+"""
+
+
+import std/[cookies, times, strtabs]
+import std/assertions
+
+let expire = fromUnix(0) + 1.seconds
+
+let theCookies = [
+  setCookie("test", "value", expire),
+  setCookie("test", "value", expire.local),
+  setCookie("test", "value", expire.utc)
+]
+let expected = "Set-Cookie: test=value; Expires=Thu, 01 Jan 1970 00:00:01 GMT"
+doAssert theCookies == [expected, expected, expected]
+
+let table = parseCookies("uid=1; kp=2")
+doAssert table["uid"] == "1"
+doAssert table["kp"] == "2"
diff --git a/tests/stdlib/tcount.nim b/tests/stdlib/tcount.nim
deleted file mode 100644
index ce1d14b6c..000000000
--- a/tests/stdlib/tcount.nim
+++ /dev/null
@@ -1,29 +0,0 @@
-discard """
-  output: '''1
-2
-3
-4
-5
-done'''
-"""
-
-# bug #1845, #2224
-
-var arr = [3,2,1,5,4]
-
-# bubble sort
-for i in low(arr)..high(arr):
-  for j in i+1..high(arr): # Error: unhandled exception: value out of range: 5 [RangeError]
-    if arr[i] > arr[j]:
-      let tmp = arr[i]
-      arr[i] = arr[j]
-      arr[j] = tmp
-
-for i in low(arr)..high(arr):
-  echo arr[i]
-
-# check this terminates:
-for x in countdown('\255', '\0'):
-  discard
-
-echo "done"
diff --git a/tests/stdlib/tcputime.nim b/tests/stdlib/tcputime.nim
deleted file mode 100644
index 2fc46ee64..000000000
--- a/tests/stdlib/tcputime.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-
-import times, os
-
-var e = epochTime()
-var c = cpuTime()
-
-os.sleep(1500)
-
-e = epochTime() - e
-c = cpuTime() - c
-
-echo "epochTime: ", e, " cpuTime: ", c
-
diff --git a/tests/stdlib/tcritbits.nim b/tests/stdlib/tcritbits.nim
index 8280ec881..e6282f045 100644
--- a/tests/stdlib/tcritbits.nim
+++ b/tests/stdlib/tcritbits.nim
@@ -1,28 +1,89 @@
 discard """
-  output: '''abc
-def
-definition
-prefix
-xyz
-def
-definition'''
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c js"
 """
 
-import critbits
+import std/[sequtils,critbits]
+import std/assertions
 
-when isMainModule:
-  var r: TCritBitTree[void]
+template main =
+  var r: CritBitTree[void]
   r.incl "abc"
   r.incl "xyz"
   r.incl "def"
   r.incl "definition"
   r.incl "prefix"
+  r.incl "foo"
+
   doAssert r.contains"def"
-  #r.del "def"
 
-  for w in r.items:
-    echo w
+  r.excl "def"
+  doAssert r.missingOrExcl("foo") == false
+  doAssert "foo" notin toSeq(r.items)
+
+  doAssert r.missingOrExcl("foo") == true
+
+  doAssert toSeq(r.items) == @["abc", "definition", "prefix", "xyz"]
+
+  doAssert toSeq(r.itemsWithPrefix("de")) == @["definition"]
+  var c = CritBitTree[int]()
+
+  c.inc("a")
+  doAssert c["a"] == 1
+
+  c.inc("a", 4)
+  doAssert c["a"] == 5
+
+  c.inc("a", -5)
+  doAssert c["a"] == 0
+
+  c.inc("b", 2)
+  doAssert c["b"] == 2
+
+  c.inc("c", 3)
+  doAssert c["c"] == 3
+
+  c.inc("a", 1)
+  doAssert c["a"] == 1
+
+  var cf = CritBitTree[float]()
+
+  cf.incl("a", 1.0)
+  doAssert cf["a"] == 1.0
+
+  cf.incl("b", 2.0)
+  doAssert cf["b"] == 2.0
+
+  cf.incl("c", 3.0)
+  doAssert cf["c"] == 3.0
+
+  doAssert cf.len == 3
+  cf.excl("c")
+  doAssert cf.len == 2
+
+  var cb: CritBitTree[string]
+  cb.incl("help", "help")
+  for k in cb.keysWithPrefix("helpp"):
+    doAssert false, "there is no prefix helpp"
+
+  block: # bug #14339
+    var strings: CritBitTree[int]
+    discard strings.containsOrIncl("foo", 3)
+    doAssert strings["foo"] == 3
 
-  for w in r.itemsWithPrefix("de"):
-    echo w
+  block tcritbitsToString:
+    block:
+      var t: CritBitTree[int]
+      t["a"] = 1
+      doAssert $t == """{"a": 1}"""
+    block:
+      var t: CritBitTree[string]
+      t["a"] = "1"
+      doAssert $t == """{"a": "1"}"""
+    block:
+      var t: CritBitTree[char]
+      t["a"] = '1'
+      doAssert $t == """{"a": '1'}"""
 
+main()
+static: main()
diff --git a/tests/stdlib/tcstring.nim b/tests/stdlib/tcstring.nim
new file mode 100644
index 000000000..d7fdd7738
--- /dev/null
+++ b/tests/stdlib/tcstring.nim
@@ -0,0 +1,93 @@
+discard """
+  targets: "c cpp js"
+  matrix: "--gc:refc; --gc:arc"
+"""
+
+from std/sugar import collect
+from stdtest/testutils import whenRuntimeJs, whenVMorJs
+import std/assertions
+
+template testMitems() =
+  block:
+    var a = "abc"
+    var b = a.cstring
+    let s = collect:
+      for bi in mitems(b):
+        if bi == 'b': bi = 'B'
+        bi
+    whenRuntimeJs:
+      discard # xxx mitems should give CT error instead of @['\x00', '\x00', '\x00']
+    do:
+      doAssert s == @['a', 'B', 'c']
+
+  block:
+    var a = "abc\0def"
+    var b = a.cstring
+    let s = collect:
+      for bi in mitems(b):
+        if bi == 'b': bi = 'B'
+        bi
+    whenRuntimeJs:
+      discard # ditto
+    do:
+      doAssert s == @['a', 'B', 'c']
+
+proc mainProc() =
+  testMitems()
+
+template main() =
+  block: # bug #13859
+    let str = "abc".cstring
+    doAssert len(str).int8 == 3
+    doAssert len(str).int16 == 3
+    doAssert len(str).int32 == 3
+    var str2 = "cde".cstring
+    doAssert len(str2).int8 == 3
+    doAssert len(str2).int16 == 3
+    doAssert len(str2).int32 == 3
+
+    const str3 = "abc".cstring
+    doAssert len(str3).int32 == 3
+    doAssert len("abc".cstring).int16 == 3
+    doAssert len("abc".cstring).float32 == 3.0
+
+  block: # bug #17159
+    block:
+      var a = "abc"
+      var b = a.cstring
+      doAssert $(b, ) == """("abc",)"""
+      let s = collect:
+        for bi in b: bi
+      doAssert s == @['a', 'b', 'c']
+
+    block:
+      var a = "abc\0def"
+      var b = a.cstring
+      let s = collect:
+        for bi in b: bi
+      whenRuntimeJs:
+        doAssert $(b, ) == """("abc\x00def",)"""
+        doAssert s == @['a', 'b', 'c', '\x00', 'd', 'e', 'f']
+      do:
+        doAssert $(b, ) == """("abc",)"""
+        doAssert s == @['a', 'b', 'c']
+
+  block:
+    when defined(gcArc): # xxx SIGBUS
+      discard
+    else:
+      mainProc()
+    when false: # xxx bug vm: Error: unhandled exception: 'node' is not accessible using discriminant 'kind' of type 'TFullReg' [FieldDefect]
+      testMitems()
+
+  block: # bug #13321: [codegen] --gc:arc does not properly emit cstring, results in SIGSEGV
+    let a = "hello".cstring
+    doAssert $a == "hello"
+    doAssert $a[0] == "h"
+    doAssert $a[4] == "o"
+    whenVMorJs: discard # xxx this should work in vm, refs https://github.com/timotheecour/Nim/issues/619
+    do:
+      doAssert a[a.len] == '\0'
+
+static: main()
+main()
diff --git a/tests/stdlib/tcstrutils.nim b/tests/stdlib/tcstrutils.nim
new file mode 100644
index 000000000..e73b2b681
--- /dev/null
+++ b/tests/stdlib/tcstrutils.nim
@@ -0,0 +1,39 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c cpp js"
+"""
+
+import std/cstrutils
+import std/assertions
+
+proc main() =
+  let s = cstring "abcdef"
+  doAssert s.startsWith("a")
+  doAssert not s.startsWith("b")
+  doAssert s.endsWith("f")
+  doAssert not s.endsWith("a")
+  doAssert s.startsWith("")
+  doAssert s.endsWith("")
+
+  let a = cstring "abracadabra"
+  doAssert a.startsWith("abra")
+  doAssert not a.startsWith("bra")
+  doAssert a.endsWith("abra")
+  doAssert not a.endsWith("dab")
+  doAssert a.startsWith("")
+  doAssert a.endsWith("")
+
+  doAssert cmpIgnoreCase(cstring "FooBar", "foobar") == 0
+  doAssert cmpIgnoreCase(cstring "bar", "Foo") < 0
+  doAssert cmpIgnoreCase(cstring "Foo5", "foo4") > 0
+
+  doAssert cmpIgnoreStyle(cstring "foo_bar", "FooBar") == 0
+  doAssert cmpIgnoreStyle(cstring "foo_bar_5", "FooBar4") > 0
+
+  doAssert cmpIgnoreCase(cstring "", cstring "") == 0
+  doAssert cmpIgnoreCase(cstring "", cstring "Hello") < 0
+  doAssert cmpIgnoreCase(cstring "wind", cstring "") > 0
+
+
+static: main()
+main()
diff --git a/tests/stdlib/tdb.nim b/tests/stdlib/tdb.nim
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/stdlib/tdb.nim
diff --git a/tests/stdlib/tdb.nims b/tests/stdlib/tdb.nims
new file mode 100644
index 000000000..d31d0b26f
--- /dev/null
+++ b/tests/stdlib/tdb.nims
@@ -0,0 +1 @@
+--styleCheck:off
\ No newline at end of file
diff --git a/tests/stdlib/tdb_mysql.nim b/tests/stdlib/tdb_mysql.nim
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/stdlib/tdb_mysql.nim
diff --git a/tests/stdlib/tdecls.nim b/tests/stdlib/tdecls.nim
new file mode 100644
index 000000000..42dc646f2
--- /dev/null
+++ b/tests/stdlib/tdecls.nim
@@ -0,0 +1,50 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c cpp js"
+"""
+import std/assertions
+import std/decls
+
+template fun() =
+  var s = @[10,11,12]
+  var a {.byaddr.} = s[0]
+  a+=100
+  doAssert s == @[110,11,12]
+  doAssert a is int
+  var b {.byaddr.}: int = s[0]
+  doAssert a.addr == b.addr
+
+  {.push warningAsError[ImplicitTemplateRedefinition]: on.}
+  # in the future ImplicitTemplateRedefinition will be an error anyway
+  doAssert not compiles(block:
+    # redeclaration not allowed
+    var foo = 0
+    var foo {.byaddr.} = s[0])
+
+  doAssert not compiles(block:
+    # ditto
+    var foo {.byaddr.} = s[0]
+    var foo {.byaddr.} = s[0])
+  {.pop.}
+
+  block:
+    var b {.byaddr.} = s[1] # redeclaration ok in sub scope
+    b = 123
+
+  doAssert s == @[110,123,12]
+
+  b = b * 10
+  doAssert s == @[1100,123,12]
+
+  doAssert not compiles(block:
+    var b2 {.byaddr.}: float = s[2])
+
+  doAssert compiles(block:
+    var b2 {.byaddr.}: int = s[2])
+
+proc fun2() = fun()
+fun()
+fun2()
+static: fun2()
+when false: # pending bug #13887
+  static: fun()
diff --git a/tests/stdlib/tdecode_helpers.nim b/tests/stdlib/tdecode_helpers.nim
new file mode 100644
index 000000000..1c0735e05
--- /dev/null
+++ b/tests/stdlib/tdecode_helpers.nim
@@ -0,0 +1,27 @@
+import std/private/decode_helpers
+import std/assertions
+
+block:
+  var i = 0
+  let c = decodePercent("%t9", i)
+  doAssert (i, c) == (0, '%')
+
+block:
+  var i = 0
+  let c = decodePercent("19", i)
+  doAssert (i, c) == (0, '%')
+
+block:
+  var i = 0
+  let c = decodePercent("%19", i)
+  doAssert (i, c) == (2, '\x19')
+
+block:
+  var i = 0
+  let c = decodePercent("%A9", i)
+  doAssert (i, c) == (2, '\xA9')
+
+block:
+  var i = 0
+  let c = decodePercent("%Aa", i)
+  doAssert (i, c) == (2, '\xAA')
diff --git a/tests/stdlib/tdeques.nim b/tests/stdlib/tdeques.nim
new file mode 100644
index 000000000..39ff996d1
--- /dev/null
+++ b/tests/stdlib/tdeques.nim
@@ -0,0 +1,243 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c cpp js"
+"""
+
+import std/deques
+from std/sequtils import toSeq
+import std/assertions
+
+block:
+  proc index(self: Deque[int], idx: Natural): int =
+    self[idx]
+
+  proc main =
+    var testDeque = initDeque[int]()
+    testDeque.addFirst(1)
+    doAssert testDeque.index(0) == 1
+
+  main()
+
+block:
+  var d = initDeque[int]()
+  d.addLast(1)
+  doAssert $d == "[1]"
+block:
+  var d = initDeque[string]()
+  d.addLast("1")
+  doAssert $d == """["1"]"""
+block:
+  var d = initDeque[char]()
+  d.addLast('1')
+  doAssert $d == "['1']"
+
+block:
+  var deq = initDeque[int](1)
+  deq.addLast(4)
+  deq.addFirst(9)
+  deq.addFirst(123)
+  var first = deq.popFirst()
+  deq.addLast(56)
+  doAssert(deq.peekLast() == 56)
+  deq.addLast(6)
+  doAssert(deq.peekLast() == 6)
+  var second = deq.popFirst()
+  deq.addLast(789)
+  doAssert(deq.peekLast() == 789)
+
+  doAssert first == 123
+  doAssert second == 9
+  doAssert($deq == "[4, 56, 6, 789]")
+  doAssert deq == [4, 56, 6, 789].toDeque
+
+  doAssert deq[0] == deq.peekFirst and deq.peekFirst == 4
+  #doAssert deq[^1] == deq.peekLast and deq.peekLast == 789
+  deq[0] = 42
+  deq[deq.len - 1] = 7
+
+  doAssert 6 in deq and 789 notin deq
+  doAssert deq.find(6) >= 0
+  doAssert deq.find(789) < 0
+
+  block:
+    var d = initDeque[int](1)
+    d.addLast 7
+    d.addLast 8
+    d.addLast 10
+    d.addFirst 5
+    d.addFirst 2
+    d.addFirst 1
+    d.addLast 20
+    d.shrink(fromLast = 2)
+    doAssert($d == "[1, 2, 5, 7, 8]")
+    d.shrink(2, 1)
+    doAssert($d == "[5, 7]")
+    d.shrink(2, 2)
+    doAssert d.len == 0
+
+  for i in -2 .. 10:
+    if i in deq:
+      doAssert deq.contains(i) and deq.find(i) >= 0
+    else:
+      doAssert(not deq.contains(i) and deq.find(i) < 0)
+
+  when compileOption("boundChecks"):
+    try:
+      echo deq[99]
+      doAssert false
+    except IndexDefect:
+      discard
+
+    try:
+      doAssert deq.len == 4
+      for i in 0 ..< 5: deq.popFirst()
+      doAssert false
+    except IndexDefect:
+      discard
+
+  # grabs some types of resize error.
+  deq = initDeque[int]()
+  for i in 1 .. 4: deq.addLast i
+  deq.popFirst()
+  deq.popLast()
+  for i in 5 .. 8: deq.addFirst i
+  doAssert $deq == "[8, 7, 6, 5, 2, 3]"
+
+  # Similar to proc from the documentation example
+  proc foo(a, b: Positive) = # assume random positive values for `a` and `b`.
+    var deq = initDeque[int]()
+    doAssert deq.len == 0
+    for i in 1 .. a: deq.addLast i
+
+    if b < deq.len: # checking before indexed access.
+      doAssert deq[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.
+    doAssert deq.peekFirst == 1
+    doAssert deq.peekLast == a
+
+    while deq.len > 0: # checking if the deque is empty
+      doAssert deq.popFirst() > 0
+
+  #foo(0,0)
+  foo(8, 5)
+  foo(10, 9)
+  foo(1, 1)
+  foo(2, 1)
+  foo(1, 5)
+  foo(3, 2)
+
+import std/sets
+
+block t13310:
+  proc main() =
+    var q = initDeque[HashSet[int16]](2)
+    q.addFirst([1'i16].toHashSet)
+    q.addFirst([2'i16].toHashSet)
+    q.addFirst([3'i16].toHashSet)
+    doAssert $q == "[{3}, {2}, {1}]"
+
+  static:
+    main()
+
+
+proc main() =
+  block:
+    let a = [10, 20, 30].toDeque
+    doAssert toSeq(a.pairs) == @[(0, 10), (1, 20), (2, 30)]
+
+  block:
+    let q = [7, 9].toDeque
+    doAssert 7 in q
+    doAssert q.contains(7)
+    doAssert 8 notin q
+
+  block:
+    let a = [10, 20, 30, 40, 50].toDeque
+    doAssert $a == "[10, 20, 30, 40, 50]"
+    doAssert a.peekFirst == 10
+    doAssert len(a) == 5
+
+  block:
+    let a = [10, 20, 30, 40, 50].toDeque
+    doAssert $a == "[10, 20, 30, 40, 50]"
+    doAssert a.peekLast == 50
+    doAssert len(a) == 5
+
+  block:
+    var a = [10, 20, 30, 40, 50].toDeque
+    doAssert $a == "[10, 20, 30, 40, 50]"
+    doAssert a.popFirst == 10
+    doAssert $a == "[20, 30, 40, 50]"
+
+  block:
+    var a = [10, 20, 30, 40, 50].toDeque
+    doAssert $a == "[10, 20, 30, 40, 50]"
+    doAssert a.popLast == 50
+    doAssert $a == "[10, 20, 30, 40]"
+
+  block:
+    var a = [10, 20, 30, 40, 50].toDeque
+    doAssert $a == "[10, 20, 30, 40, 50]"
+    clear(a)
+    doAssert len(a) == 0
+
+  block: # bug #21278
+    var a = [10, 20, 30, 40].toDeque
+
+    a.shrink(fromFirst = 0, fromLast = 1)
+    doAssert $a == "[10, 20, 30]"
+
+  block:
+    var a, b: Deque[int]
+    for i in 1 .. 256:
+      a.addLast(i)
+    for i in 1 .. 255:
+      a.popLast
+    b.addLast(1)
+    doAssert a == b
+
+  block:
+    # Issue 23275
+    # Test `==`.
+    block:
+      var a, b = initDeque[int]()
+      doAssert a == b
+      doAssert a.hash == b.hash
+      a.addFirst(1)
+      doAssert a != b
+      doAssert a.hash != b.hash
+      b.addLast(1)
+      doAssert a == b
+      doAssert a.hash == b.hash
+      a.popFirst
+      b.popLast
+      doAssert a == b
+      doAssert a.hash == b.hash
+      a.addLast 2
+      doAssert a != b
+      doAssert a.hash != b.hash
+      b.addFirst 2
+      doAssert a == b
+      doAssert a.hash == b.hash
+
+    block:
+      var a, b = initDeque[int]()
+      for i in countDown(100, 1):
+        a.addFirst(i)
+      for i in 1..100:
+        b.addLast(i)
+      doAssert a == b
+      for i in 1..99:
+        a.popLast
+      let a1 = [1].toDeque
+      doAssert a == a1
+      doAssert a.hash == a1.hash
+      var c = initDeque[int]()
+      c.addLast(1)
+      doAssert a == c
+      doAssert a.hash == c.hash
+
+static: main()
+main()
diff --git a/tests/stdlib/tdiff.nim b/tests/stdlib/tdiff.nim
new file mode 100644
index 000000000..132f7120b
--- /dev/null
+++ b/tests/stdlib/tdiff.nim
@@ -0,0 +1,75 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c js"
+"""
+
+import experimental/diff
+import std/strutils
+import std/assertions
+
+proc testHelper(f: seq[Item]): string =
+  for it in f:
+    result.add(
+      $it.deletedA & "." & $it.insertedB & "." & $it.startA & "." & $it.startB & "*"
+    )
+
+proc main() =
+  var a, b: string
+
+  # Diff Self Test
+  # test all changes
+  a = "a,b,c,d,e,f,g,h,i,j,k,l".replace(',', '\n')
+  b = "0,1,2,3,4,5,6,7,8,9".replace(',', '\n')
+  doAssert(testHelper(diffText(a, b)) ==
+    "12.10.0.0*",
+    "all-changes test failed.")
+  # test all same
+  a = "a,b,c,d,e,f,g,h,i,j,k,l".replace(',', '\n')
+  b = a
+  doAssert(testHelper(diffText(a, b)) ==
+    "",
+    "all-same test failed.")
+
+  # test snake
+  a = "a,b,c,d,e,f".replace(',', '\n')
+  b = "b,c,d,e,f,x".replace(',', '\n')
+  doAssert(testHelper(diffText(a, b)) ==
+    "1.0.0.0*0.1.6.5*",
+    "snake test failed.")
+
+  # 2002.09.20 - repro
+  a = "c1,a,c2,b,c,d,e,g,h,i,j,c3,k,l".replace(',', '\n')
+  b = "C1,a,C2,b,c,d,e,I1,e,g,h,i,j,C3,k,I2,l".replace(',', '\n')
+  doAssert(testHelper(diffText(a, b)) ==
+    "1.1.0.0*1.1.2.2*0.2.7.7*1.1.11.13*0.1.13.15*",
+    "repro20020920 test failed.")
+
+  # 2003.02.07 - repro
+  a = "F".replace(',', '\n')
+  b = "0,F,1,2,3,4,5,6,7".replace(',', '\n')
+  doAssert(testHelper(diffText(a, b)) ==
+    "0.1.0.0*0.7.1.2*",
+    "repro20030207 test failed.")
+
+  # Muegel - repro
+  a = "HELLO\nWORLD"
+  b = "\n\nhello\n\n\n\nworld\n"
+  doAssert(testHelper(diffText(a, b)) ==
+    "2.8.0.0*",
+    "repro20030409 test failed.")
+
+  # test some differences
+  a = "a,b,-,c,d,e,f,f".replace(',', '\n')
+  b = "a,b,x,c,e,f".replace(',', '\n')
+  doAssert(testHelper(diffText(a, b)) ==
+    "1.1.2.2*1.0.4.4*1.0.7.6*",
+    "some-changes test failed.")
+
+  # test one change within long chain of repeats
+  a = "a,a,a,a,a,a,a,a,a,a".replace(',', '\n')
+  b = "a,a,a,a,-,a,a,a,a,a".replace(',', '\n')
+  doAssert(testHelper(diffText(a, b)) ==
+    "0.1.4.4*1.0.9.10*",
+    "long chain of repeats test failed.")
+main()
+static: main()
diff --git a/tests/stdlib/tdistros_detect.nim b/tests/stdlib/tdistros_detect.nim
new file mode 100644
index 000000000..1176c8993
--- /dev/null
+++ b/tests/stdlib/tdistros_detect.nim
@@ -0,0 +1,16 @@
+import std/[assertions, distros]
+
+when defined(windows):
+    doAssert detectOs(Windows) == true
+    doAssert detectOs(Linux) == false
+    doAssert detectOs(MacOSX) == false
+
+when defined(linux):
+    doAssert detectOs(Linux) == true
+    doAssert detectOs(Windows) == false
+    doAssert detectOs(MacOSX) == false
+
+when defined(macosx):
+    doAssert detectOs(MacOSX) == true
+    doAssert detectOs(Windows) == false
+    doAssert detectOs(Linux) == false
diff --git a/tests/stdlib/tdochelpers.nim b/tests/stdlib/tdochelpers.nim
new file mode 100644
index 000000000..4d532b5d0
--- /dev/null
+++ b/tests/stdlib/tdochelpers.nim
@@ -0,0 +1,221 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  output: '''
+
+[Suite] Integration with Nim
+'''
+"""
+
+# tests for dochelpers.nim module
+
+import ../../lib/packages/docutils/[rstast, rst, dochelpers]
+import unittest
+import std/assertions
+
+proc testMsgHandler(filename: string, line, col: int, msgkind: MsgKind,
+                    arg: string) =
+  doAssert msgkind == mwBrokenLink
+
+proc fromRst(text: string): LangSymbol =
+  let r = rstParse(text, "-input-", LineRstInit, ColRstInit,
+                   {roNimFile},
+                   msgHandler=testMsgHandler)
+  assert r.node.kind == rnRstRef
+  result = toLangSymbol(r.node)
+
+proc fromMd(text: string): LangSymbol =
+  let r = rstParse(text, "-input-", LineRstInit, ColRstInit,
+                   {roPreferMarkdown, roSupportMarkdown, roNimFile},
+                   msgHandler=testMsgHandler)
+  assert r.node.kind == rnPandocRef
+  assert r.node.len == 2
+  # this son is the target:
+  assert r.node.sons[1].kind == rnInner
+  result = toLangSymbol(r.node.sons[1])
+
+suite "Integration with Nim":
+  test "simple symbol parsing (shortest form)":
+    let expected = LangSymbol(symKind: "", name: "g")
+    check "g_".fromRst == expected
+    check "[g]".fromMd == expected
+    # test also alternative syntax variants of Pandoc Markdown:
+    check "[g][]".fromMd == expected
+    check "[this symbol][g]".fromMd == expected
+
+  test "simple symbol parsing (group of words)":
+    #let input1 = "`Y`_".rstParseTest
+    let expected1 = LangSymbol(symKind: "", name: "Y")
+    check "`Y`_".fromRst == expected1
+    check "[Y]".fromMd == expected1
+
+    # this means not a statement 'type', it's a backticked identifier `type`:
+    let expected2 = LangSymbol(symKind: "", name: "type")
+    check "`type`_".fromRst == expected2
+    check "[type]".fromMd == expected2
+
+    let expected3 = LangSymbol(symKind: "", name: "[]")
+    check "`[]`_".fromRst == expected3
+    # Markdown syntax for this case is NOT [[]]
+    check "[`[]`]".fromMd == expected3
+
+    let expected4 = LangSymbol(symKind: "", name: "Xyz")
+    check "`X Y Z`_".fromRst == expected4
+    check "[X Y Z]".fromMd == expected4
+
+  test "simple proc parsing":
+    let expected = LangSymbol(symKind: "proc", name: "f")
+    check "`proc f`_".fromRst == expected
+    check "[proc f]".fromMd == expected
+
+  test "another backticked name":
+    let expected = LangSymbol(symKind: "template", name: "type")
+    check """`template \`type\``_""".fromRst == expected
+    # no backslash in Markdown:
+    check """[template `type`]""".fromMd == expected
+
+  test "simple proc parsing with parameters":
+    let expected = LangSymbol(symKind: "proc", name: "f",
+                              parametersProvided: true)
+    check "`proc f*()`_".fromRst == expected
+    check "`proc f()`_".fromRst == expected
+    check "[proc f*()]".fromMd == expected
+    check "[proc f()]".fromMd == expected
+
+  test "symbol parsing with 1 parameter":
+    let expected = LangSymbol(symKind: "", name: "f",
+                              parameters: @[("G[int]", "")],
+                              parametersProvided: true)
+    check "`f(G[int])`_".fromRst == expected
+    check "[f(G[int])]".fromMd == expected
+
+  test "more proc parsing":
+    let input1 = "`proc f[T](x:G[T]):M[T]`_".fromRst
+    let input2 = "`proc f[ T ] ( x: G [T] ): M[T]`_".fromRst
+    let input3 = "`proc f*[T](x: G[T]): M[T]`_".fromRst
+    let expected = LangSymbol(symKind: "proc",
+                              name: "f",
+                              generics: "[T]",
+                              parameters: @[("x", "G[T]")],
+                              parametersProvided: true,
+                              outType: "M[T]")
+    check(input1 == expected)
+    check(input2 == expected)
+    check(input3 == expected)
+
+  test "advanced proc parsing with Nim identifier normalization":
+    let inputRst = """`proc binarySearch*[T, K](a: openarray[T]; key: K;
+                       cmp: proc (x: T; y: K): int)`_"""
+    let inputMd = """[proc binarySearch*[T, K](a: openarray[T]; key: K;
+                       cmp: proc (x: T; y: K): int)]"""
+    let expected = LangSymbol(symKind: "proc",
+                              name: "binarysearch",
+                              generics: "[T,K]",
+                              parameters: @[
+                                ("a", "openarray[T]"),
+                                ("key", "K"),
+                                ("cmp", "proc(x:T;y:K):int")],
+                              parametersProvided: true,
+                              outType: "")
+    check(inputRst.fromRst == expected)
+    check(inputMd.fromMd == expected)
+
+  test "the same without proc":
+    let input = """`binarySearch*[T, K](a: openarray[T]; key: K;
+                    cmp: proc (x: T; y: K): int {.closure.})`_"""
+    let expected = LangSymbol(symKind: "",
+                              name: "binarysearch",
+                              generics: "[T,K]",
+                              parameters: @[
+                                ("a", "openarray[T]"),
+                                ("key", "K"),
+                                ("cmp", "proc(x:T;y:K):int")],
+                              parametersProvided: true,
+                              outType: "")
+    check(input.fromRst == expected)
+    let inputMd = """[binarySearch*[T, K](a: openarray[T]; key: K;
+                      cmp: proc (x: T; y: K): int {.closure.})]"""
+    check(inputMd.fromMd == expected)
+
+  test "operator $ with and without backticks":
+    let input1 = """`func \`$\`*[T](a: \`open Array\`[T]): string`_"""
+    let input1md = "[func `$`*[T](a: `open Array`[T]): string]"
+    let input2 = """`func $*[T](a: \`open Array\`[T]): string`_"""
+    let input2md = "[func $*[T](a: `open Array`[T]): string]"
+    let expected = LangSymbol(symKind: "func",
+                              name: "$",
+                              generics: "[T]",
+                              parameters: @[("a", "openarray[T]")],
+                              parametersProvided: true,
+                              outType: "string")
+    check input1.fromRst == expected
+    check input2.fromRst == expected
+    check input1md.fromMd == expected
+    check input2md.fromMd == expected
+
+  test "operator [] with and without backticks":
+    let input1 = """`func \`[]\`[T](a: \`open Array\`[T], idx: int): T`_"""
+    let input1md = "[func `[]`[T](a: `open Array`[T], idx: int): T]"
+    let input2 = """`func [][T](a: \`open Array\`[T], idx: int): T`_"""
+    let input2md = "[func [][T](a: `open Array`[T], idx: int): T]"
+    let expected = LangSymbol(symKind: "func",
+                              name: "[]",
+                              generics: "[T]",
+                              parameters: @[("a", "openarray[T]"),
+                                            ("idx", "int")],
+                              parametersProvided: true,
+                              outType: "T")
+    check input1.fromRst == expected
+    check input2.fromRst == expected
+    check input1md.fromMd == expected
+    check input2md.fromMd == expected
+
+  test "postfix symbol specifier #1":
+    let input = "`walkDir iterator`_"
+    let inputMd = "[walkDir iterator]"
+    let expected = LangSymbol(symKind: "iterator",
+                              name: "walkdir")
+    check input.fromRst == expected
+    check inputMd.fromMd == expected
+
+  test "postfix symbol specifier #2":
+    let input1 = """`\`[]\`[T](a: \`open Array\`[T], idx: int): T func`_"""
+    let input1md = "[`[]`[T](a: `open Array`[T], idx: int): T func]"
+    let input2 = """`[][T](a: \`open Array\`[T], idx: int): T func`_"""
+    # note again that ` is needed between 1st and second [
+    let input2md = "[`[]`[T](a: `open Array`[T], idx: int): T func]"
+    let expected = LangSymbol(symKind: "func",
+                              name: "[]",
+                              generics: "[T]",
+                              parameters: @[("a", "openarray[T]"),
+                                            ("idx", "int")],
+                              parametersProvided: true,
+                              outType: "T")
+    check input1.fromRst == expected
+    check input2.fromRst == expected
+    check input1md.fromMd == expected
+    check input2md.fromMd == expected
+
+  test "type of type":
+    let inputRst = "`CopyFlag enum`_"
+    let inputMd = "[CopyFlag enum]"
+    let expected = LangSymbol(symKind: "type",
+                              symTypeKind: "enum",
+                              name: "Copyflag")
+    check inputRst.fromRst == expected
+    check inputMd.fromMd == expected
+
+  test "prefixed module":
+    let inputRst = "`module std / paths`_"
+    let inputMd = "[module std / paths]"
+    let expected = LangSymbol(symKind: "module",
+                              name: "std/paths")
+    check inputRst.fromRst == expected
+    check inputMd.fromMd == expected
+
+  test "postfixed module":
+    let inputRst = "`std / paths module`_"
+    let inputMd = "[std / paths module]"
+    let expected = LangSymbol(symKind: "module",
+                              name: "std/paths")
+    check inputRst.fromRst == expected
+    check inputMd.fromMd == expected
diff --git a/tests/stdlib/techo.nim b/tests/stdlib/techo.nim
deleted file mode 100644
index 9cef9205f..000000000
--- a/tests/stdlib/techo.nim
+++ /dev/null
@@ -1,3 +0,0 @@
-# Simplest Nim program
-
-echo "Hello, World!"
diff --git a/tests/stdlib/teditdistance.nim b/tests/stdlib/teditdistance.nim
new file mode 100644
index 000000000..14ba6df97
--- /dev/null
+++ b/tests/stdlib/teditdistance.nim
@@ -0,0 +1,45 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/editdistance
+import std/assertions
+
+doAssert editDistance("", "") == 0
+doAssert editDistance("kitten", "sitting") == 3 # from Wikipedia
+doAssert editDistance("flaw", "lawn") == 2 # from Wikipedia
+
+doAssert editDistance("привет", "превет") == 1
+doAssert editDistance("Åge", "Age") == 1
+# editDistance, one string is longer in bytes, but shorter in rune length
+# first string: 4 bytes, second: 6 bytes, but only 3 runes
+doAssert editDistance("aaaa", "×××") == 4
+
+block veryLongStringEditDistanceTest:
+  const cap = 256
+  var
+    s1 = newStringOfCap(cap)
+    s2 = newStringOfCap(cap)
+  while len(s1) < cap:
+    s1.add 'a'
+  while len(s2) < cap:
+    s2.add 'b'
+  doAssert editDistance(s1, s2) == cap
+
+block combiningCodePointsEditDistanceTest:
+  const s = "A\xCC\x8Age"
+  doAssert editDistance(s, "Age") == 1
+
+doAssert editDistanceAscii("", "") == 0
+doAssert editDistanceAscii("kitten", "sitting") == 3 # from Wikipedia
+doAssert editDistanceAscii("flaw", "lawn") == 2 # from Wikipedia
+
+
+doAssert(editDistance("prefix__hallo_suffix", "prefix__hallo_suffix") == 0)
+doAssert(editDistance("prefix__hallo_suffix", "prefix__hallo_suffi1") == 1)
+doAssert(editDistance("prefix__hallo_suffix", "prefix__HALLO_suffix") == 5)
+doAssert(editDistance("prefix__hallo_suffix", "prefix__ha_suffix") == 3)
+doAssert(editDistance("prefix__hallo_suffix", "prefix") == 14)
+doAssert(editDistance("prefix__hallo_suffix", "suffix") == 14)
+doAssert(editDistance("prefix__hallo_suffix", "prefix__hao_suffix") == 2)
+doAssert(editDistance("main", "malign") == 2)
\ No newline at end of file
diff --git a/tests/stdlib/tencodings.nim b/tests/stdlib/tencodings.nim
new file mode 100644
index 000000000..2f4daaba3
--- /dev/null
+++ b/tests/stdlib/tencodings.nim
@@ -0,0 +1,107 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/encodings
+import std/assertions
+
+var fromGBK = open("utf-8", "gbk")
+var toGBK = open("gbk", "utf-8")
+
+var fromGB2312 = open("utf-8", "gb2312")
+var toGB2312 = open("gb2312", "utf-8")
+
+
+block:
+  let data = "\215\237\186\243\178\187\214\170\204\236\212\218\203\174\163\172\194\250\180\178\208\199\195\206\209\185\208\199\186\211"
+  doAssert fromGBK.convert(data) == "醉后不知天在水,满床星梦压星河"
+
+block:
+  let data = "万两黄金容易得,知心一个也难求"
+  doAssert toGBK.convert(data) == "\205\242\193\189\187\198\189\240\200\221\210\215\181\195\163\172\214\170\208\196\210\187\184\246\210\178\196\209\199\243"
+
+
+block:
+  let data = "\215\212\208\197\200\203\201\250\182\254\176\217\196\234\163\172\187\225\181\177\203\174\187\247\200\253\199\167\192\239"
+  doAssert fromGB2312.convert(data) == "自信人生二百年,会当水击三千里"
+
+block:
+  let data = "谁怕?一蓑烟雨任平生"
+  doAssert toGB2312.convert(data) == "\203\173\197\194\163\191\210\187\203\242\209\204\211\234\200\206\198\189\201\250"
+
+
+when defined(windows):
+  block should_throw_on_unsupported_conversions:
+    let original = "some string"
+
+    doAssertRaises(EncodingError):
+      discard convert(original, "utf-8", "utf-32")
+
+    doAssertRaises(EncodingError):
+      discard convert(original, "utf-8", "unicodeFFFE")
+
+    doAssertRaises(EncodingError):
+      discard convert(original, "utf-8", "utf-32BE")
+
+    doAssertRaises(EncodingError):
+      discard convert(original, "unicodeFFFE", "utf-8")
+
+    doAssertRaises(EncodingError):
+      discard convert(original, "utf-32", "utf-8")
+
+    doAssertRaises(EncodingError):
+      discard convert(original, "utf-32BE", "utf-8")
+
+  block should_convert_from_utf16_to_utf8:
+    let original = "\x42\x04\x35\x04\x41\x04\x42\x04" # utf-16 little endian test string "тест"
+    let result = convert(original, "utf-8", "utf-16")
+    doAssert(result == "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82")
+
+  block should_convert_from_utf16_to_win1251:
+    let original = "\x42\x04\x35\x04\x41\x04\x42\x04" # utf-16 little endian test string "тест"
+    let result = convert(original, "windows-1251", "utf-16")
+    doAssert(result == "\xf2\xe5\xf1\xf2")
+
+  block should_convert_from_win1251_to_koi8r:
+    let original = "\xf2\xe5\xf1\xf2" # win1251 test string "тест"
+    let result = convert(original, "koi8-r", "windows-1251")
+    doAssert(result == "\xd4\xc5\xd3\xd4")
+
+  block should_convert_from_koi8r_to_win1251:
+    let original = "\xd4\xc5\xd3\xd4" # koi8r test string "тест"
+    let result = convert(original, "windows-1251", "koi8-r")
+    doAssert(result == "\xf2\xe5\xf1\xf2")
+
+  block should_convert_from_utf8_to_win1251:
+    let original = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82" # utf-8 test string "тест"
+    let result = convert(original, "windows-1251", "utf-8")
+    doAssert(result == "\xf2\xe5\xf1\xf2")
+
+  block should_convert_from_utf8_to_utf16:
+    let original = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82" # utf-8 test string "тест"
+    let result = convert(original, "utf-16", "utf-8")
+    doAssert(result == "\x42\x04\x35\x04\x41\x04\x42\x04")
+
+  block should_handle_empty_string_for_any_conversion:
+    let original = ""
+    var result = convert(original, "utf-16", "utf-8")
+    doAssert(result == "")
+    result = convert(original, "utf-8", "utf-16")
+    doAssert(result == "")
+    result = convert(original, "windows-1251", "koi8-r")
+    doAssert(result == "")
+
+
+block:
+  let
+    orig = "öäüß"
+    cp1252 = convert(orig, "CP1252", "UTF-8")
+    ibm850 = convert(cp1252, "ibm850", "CP1252")
+    current = getCurrentEncoding()
+  doAssert orig == "\195\182\195\164\195\188\195\159"
+  doAssert ibm850 == "\148\132\129\225"
+  doAssert convert(ibm850, current, "ibm850") == orig
+
+block: # fixes about #23481
+  doAssertRaises EncodingError:
+    discard open(destEncoding="this is a invalid enc")
diff --git a/tests/stdlib/tenumerate.nim b/tests/stdlib/tenumerate.nim
new file mode 100644
index 000000000..2789ebe3a
--- /dev/null
+++ b/tests/stdlib/tenumerate.nim
@@ -0,0 +1,24 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/enumerate
+import std/assertions
+
+let a = @[1, 3, 5, 7]
+
+block:
+  var res: seq[(int, int)]
+  for i, x in enumerate(a):
+    res.add (i, x)
+  doAssert res == @[(0, 1), (1, 3), (2, 5), (3, 7)]
+block:
+  var res: seq[(int, int)]
+  for (i, x) in enumerate(a.items):
+    res.add (i, x)
+  doAssert res == @[(0, 1), (1, 3), (2, 5), (3, 7)]
+block:
+  var res: seq[(int, int)]
+  for i, x in enumerate(3, a):
+    res.add (i, x)
+  doAssert res == @[(3, 1), (4, 3), (5, 5), (6, 7)]
diff --git a/tests/stdlib/tenumutils.nim b/tests/stdlib/tenumutils.nim
new file mode 100644
index 000000000..2662a660d
--- /dev/null
+++ b/tests/stdlib/tenumutils.nim
@@ -0,0 +1,49 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c js"
+"""
+
+import std/enumutils
+from std/sequtils import toSeq
+import std/assertions
+
+template main =
+  block: # items
+    type A = enum a0 = 2, a1 = 4, a2
+    type B[T] = enum b0 = 2, b1 = 4
+    doAssert A.toSeq == [a0, a1, a2]
+    doAssert B[float].toSeq == [B[float].b0, B[float].b1]
+
+  block: # symbolName
+    block:
+      type A2 = enum a20, a21, a22
+      doAssert $a21 == "a21"
+      doAssert a21.symbolName == "a21"
+      proc `$`(a: A2): string = "foo"
+      doAssert $a21 == "foo"
+      doAssert a21.symbolName == "a21"
+      var a = a22
+      doAssert $a == "foo"
+      doAssert a.symbolName == "a22"
+
+    type B = enum
+      b0 = (10, "kb0")
+      b1 = "kb1"
+      b2
+    let b = B.low
+    doAssert b.symbolName == "b0"
+    doAssert $b == "kb0"
+    static: doAssert B.high.symbolName == "b2"
+
+  block:
+    type
+      Color = enum
+        Red = "red", Yellow = "yellow", Blue = "blue"
+
+    var s = Red
+    doAssert symbolName(s) == "Red"
+    var x: range[Red..Blue] = Yellow
+    doAssert symbolName(x) == "Yellow"
+
+static: main()
+main()
diff --git a/tests/stdlib/tenvvars.nim b/tests/stdlib/tenvvars.nim
new file mode 100644
index 000000000..1a07f02b8
--- /dev/null
+++ b/tests/stdlib/tenvvars.nim
@@ -0,0 +1,162 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  joinable: false
+  targets: "c js cpp"
+"""
+
+import std/envvars
+from std/sequtils import toSeq
+import stdtest/testutils
+import std/[assertions]
+
+when not defined(js):
+  import std/typedthreads
+
+# "LATIN CAPITAL LETTER AE" in UTF-8 (0xc386)
+const unicodeUtf8 = "\xc3\x86"
+
+template main =
+  block: # delEnv, existsEnv, getEnv, envPairs
+    for val in ["val", "", unicodeUtf8]: # ensures empty val works too
+      const key = "NIM_TESTS_TOSENV_KEY"
+      doAssert not existsEnv(key)
+
+      putEnv(key, "tempval")
+      doAssert existsEnv(key)
+      doAssert getEnv(key) == "tempval"
+
+      putEnv(key, val) # change a key that already exists
+      doAssert existsEnv(key)
+      doAssert getEnv(key) == val
+
+      doAssert (key, val) in toSeq(envPairs())
+      delEnv(key)
+      doAssert (key, val) notin toSeq(envPairs())
+      doAssert not existsEnv(key)
+      delEnv(key) # deleting an already deleted env var
+      doAssert not existsEnv(key)
+
+    block:
+      doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", "") == ""
+      doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", " ") == " "
+      doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", "defval") == "defval"
+
+    whenVMorJs: discard # xxx improve
+    do:
+      doAssertRaises(OSError, putEnv("NIM_TESTS_TOSENV_PUT=DUMMY_VALUE", "NEW_DUMMY_VALUE"))
+      doAssertRaises(OSError, putEnv("", "NEW_DUMMY_VALUE"))
+      doAssert not existsEnv("")
+      doAssert not existsEnv("NIM_TESTS_TOSENV_PUT=DUMMY_VALUE")
+      doAssert not existsEnv("NIM_TESTS_TOSENV_PUT")
+
+static: main()
+main()
+
+when defined(windows):
+  import std/widestrs
+  proc c_wgetenv(env: WideCString): WideCString {.importc: "_wgetenv", header: "<stdlib.h>".}
+proc c_getenv(env: cstring): cstring {.importc: "getenv", header: "<stdlib.h>".}
+
+when not defined(js) and not defined(nimscript):
+  block: # bug #18533
+    var thr: Thread[void]
+    proc threadFunc {.thread.} = putEnv("foo", "fooVal2")
+
+    putEnv("foo", "fooVal1")
+    doAssert getEnv("foo") == "fooVal1"
+    createThread(thr, threadFunc)
+    joinThreads(thr)
+    when defined(windows):
+      doAssert getEnv("foo") == $c_wgetenv("foo".newWideCString)
+    else:
+      doAssert getEnv("foo") == $c_getenv("foo".cstring)
+
+    doAssertRaises(OSError): delEnv("foo=bar")
+
+when defined(windows) and not defined(nimscript):
+  import std/encodings
+
+  proc c_putenv(env: cstring): int32 {.importc: "putenv", header: "<stdlib.h>".}
+  proc c_wputenv(env: WideCString): int32 {.importc: "_wputenv", header: "<stdlib.h>".}
+
+  block: # Bug #20083
+    # These test that `getEnv`, `putEnv` and `existsEnv` handle Unicode
+    # characters correctly. This means that module X in the process calling the
+    # CRT environment variable API will get the correct string. Raw CRT API
+    # calls below represent module X.
+
+    # Getting an env. var. with unicode characters returns the correct UTF-8
+    # encoded string.
+    block:
+      const envName = "twin_envvars1"
+      doAssert c_wputenv(newWideCString(envName & "=" & unicodeUtf8)) == 0
+      doAssert existsEnv(envName)
+      doAssert getEnv(envName) == unicodeUtf8
+
+    # Putting an env. var. with unicode characters gives the correct UTF-16
+    # encoded string from low-level routine.
+    block:
+      const envName = "twin_envvars2"
+      putEnv(envName, unicodeUtf8)
+      doAssert $c_wgetenv(envName.newWideCString) == unicodeUtf8
+
+    # Env. name containing Unicode characters is retrieved correctly
+    block:
+      const envName = unicodeUtf8 & "1"
+      doAssert c_wputenv(newWideCString(envName & "=" & unicodeUtf8)) == 0
+      doAssert existsEnv(envName)
+      doAssert getEnv(envName) == unicodeUtf8
+
+    # Env. name containing Unicode characters is set correctly
+    block:
+      const envName = unicodeUtf8 & "2"
+      putEnv(envName, unicodeUtf8)
+      doAssert existsEnv(envName)
+      doAssert $c_wgetenv(envName.newWideCString) == unicodeUtf8
+
+    # Env. name containing Unicode characters and empty value is set correctly
+    block:
+      const envName = unicodeUtf8 & "3"
+      putEnv(envName, "")
+      doAssert existsEnv(envName)
+      doAssert $c_wgetenv(envName.newWideCString) == ""
+
+    # It's hard to test on Windows code pages, because there is no "change
+    # a process' locale" API.
+    if getCurrentEncoding(true) == "windows-1252":
+      const
+        unicodeAnsi = "\xc6" # `unicodeUtf8` in `windows-1252` encoding
+
+      # Test that env. var. ANSI API has correct encoding
+      block:
+        const
+          envName = unicodeUtf8 & "4"
+          envNameAnsi = unicodeAnsi & "4"
+        putEnv(envName, unicodeUtf8)
+        doAssert $c_getenv(envNameAnsi.cstring) == unicodeAnsi
+
+      block:
+        const
+          envName = unicodeUtf8 & "5"
+          envNameAnsi = unicodeAnsi & "5"
+        doAssert c_putenv((envNameAnsi & "=" & unicodeAnsi).cstring) == 0
+        doAssert getEnv(envName) == unicodeUtf8
+
+      # Env. name containing Unicode characters and empty value is set correctly;
+      # and, if env. name. characters cannot be represented in codepage, don't
+      # raise an error.
+      #
+      # `win_setenv.nim` converts UTF-16 to ANSI when setting empty env. var. The
+      # windows-1250 locale has no representation of `abreveUtf8` below, so the
+      # conversion will fail, but this must not be fatal. It is expected that the
+      # routine ignores updating MBCS environment (`environ` global) and carries
+      # on.
+      block:
+        const
+          # "LATIN SMALL LETTER A WITH BREVE" in UTF-8
+          abreveUtf8 = "\xc4\x83"
+          envName = abreveUtf8 & "6"
+        putEnv(envName, "")
+        doAssert existsEnv(envName)
+        doAssert $c_wgetenv(envName.newWideCString) == ""
+        doAssert getEnv(envName) == ""
diff --git a/tests/stdlib/testequivalence.nim b/tests/stdlib/testequivalence.nim
deleted file mode 100644
index 7acaad340..000000000
--- a/tests/stdlib/testequivalence.nim
+++ /dev/null
@@ -1,14 +0,0 @@
-discard """
-  output: ''''''
-"""
-import sets
-
-doAssert(toSet(@[1,2,3]) <= toSet(@[1,2,3,4]), "equivalent or subset")
-doAssert(toSet(@[1,2,3]) <= toSet(@[1,2,3]), "equivalent or subset")
-doAssert((not(toSet(@[1,2,3]) <= toSet(@[1,2]))), "equivalent or subset")
-doAssert(toSet(@[1,2,3]) <= toSet(@[1,2,3,4]), "strict subset")
-doAssert((not(toSet(@[1,2,3]) < toSet(@[1,2,3]))), "strict subset")
-doAssert((not(toSet(@[1,2,3]) < toSet(@[1,2]))), "strict subset")
-doAssert((not(toSet(@[1,2,3]) == toSet(@[1,2,3,4]))), "==")
-doAssert(toSet(@[1,2,3]) == toSet(@[1,2,3]), "==")
-doAssert((not(toSet(@[1,2,3]) == toSet(@[1,2]))), "==")
diff --git a/tests/stdlib/texitprocs.nim b/tests/stdlib/texitprocs.nim
new file mode 100644
index 000000000..ea29d8f58
--- /dev/null
+++ b/tests/stdlib/texitprocs.nim
@@ -0,0 +1,22 @@
+discard """
+matrix: "--mm:refc; --mm:orc"
+targets: "c cpp js"
+output: '''
+ok4
+ok3
+ok2
+ok1
+'''
+"""
+
+import std/exitprocs
+
+proc fun1() {.noconv.} = echo "ok1"
+proc fun2() = echo "ok2"
+proc fun3() {.noconv.} = echo "ok3"
+proc fun4() = echo "ok4"
+
+addExitProc(fun1)
+addExitProc(fun2)
+addExitProc(fun3)
+addExitProc(fun4)
diff --git a/tests/stdlib/tfdleak.nim b/tests/stdlib/tfdleak.nim
new file mode 100644
index 000000000..272a7507c
--- /dev/null
+++ b/tests/stdlib/tfdleak.nim
@@ -0,0 +1,152 @@
+discard """
+  exitcode: 0
+  output: ""
+  matrix: "; -d:nimInheritHandles; --mm:refc"
+  joinable: false
+"""
+
+import os, osproc, strutils, nativesockets, net, selectors, memfiles,
+       asyncdispatch, asyncnet
+
+import std/[assertions, syncio]
+
+when defined(windows):
+  import winlean
+
+  # Note: Windows 10-only API
+  proc compareObjectHandles(first, second: Handle): WINBOOL
+                           {.stdcall, dynlib: "kernelbase",
+                             importc: "CompareObjectHandles".}
+else:
+  import posix
+
+proc leakCheck(f: AsyncFD | int | FileHandle | SocketHandle, msg: string,
+               expectLeak = defined(nimInheritHandles)) =
+  var args = @[$f.int, msg, $expectLeak]
+
+  when defined(windows):
+    var refFd: Handle
+    # NOTE: This function shouldn't be used to duplicate sockets,
+    #       as this function may mess with the socket internal refcounting.
+    #       but due to the lack of type segmentation in the stdlib for
+    #       Windows (AsyncFD can be a file or a socket), we will have to
+    #       settle with this.
+    #
+    #       Now, as a poor solution for the refcounting problem, we just
+    #       simply let the duplicated handle leak. This should not interfere
+    #       with the test since new handles can't occupy the slot held by
+    #       the leaked ones.
+    if duplicateHandle(getCurrentProcess(), f.Handle,
+                       getCurrentProcess(), addr refFd,
+                       0, 1, DUPLICATE_SAME_ACCESS) == 0:
+      raiseOSError osLastError(), "Couldn't create the reference handle"
+    args.add $refFd
+
+  discard startProcess(
+    getAppFilename(),
+    args = args,
+    options = {poParentStreams}
+  ).waitForExit
+
+proc isValidHandle(f: int): bool =
+  ## Check if a handle is valid. Requires OS-native handles.
+  when defined(windows):
+    var flags: DWORD
+    result = getHandleInformation(f.Handle, addr flags) != 0
+  else:
+    result = fcntl(f.cint, F_GETFD) != -1
+
+proc main() =
+  if paramCount() == 0:
+    # Parent process
+    let f = syncio.open("__test_fdleak", fmReadWrite)
+    defer: close f
+
+    leakCheck(f.getOsFileHandle, "system.open()")
+
+    doAssert f.reopen("__test_fdleak2", fmReadWrite), "reopen failed"
+
+    leakCheck(f.getOsFileHandle, "reopen")
+
+    let sock = createNativeSocket()
+    defer: close sock
+    leakCheck(sock, "createNativeSocket()")
+    if sock.setInheritable(not defined(nimInheritHandles)):
+      leakCheck(sock, "createNativeSocket()", not defined(nimInheritHandles))
+    else:
+      raiseOSError osLastError()
+
+    let server = newSocket()
+    defer: close server
+    server.bindAddr(address = "127.0.0.1")
+    server.listen()
+    let (_, port) = server.getLocalAddr
+
+    leakCheck(server.getFd, "newSocket()")
+
+    let client = newSocket()
+    defer: close client
+    client.connect("127.0.0.1", port)
+
+    var input: Socket
+    server.accept(input)
+
+    leakCheck(input.getFd, "accept()")
+
+    # ioselectors_select doesn't support returning a handle.
+    when not defined(windows):
+      let selector = newSelector[int]()
+      leakCheck(selector.getFd, "selector()", false)
+
+    var mf = memfiles.open("__test_fdleak3", fmReadWrite, newFileSize = 1)
+    defer: close mf
+    when defined(windows):
+      leakCheck(mf.mapHandle, "memfiles.open().mapHandle", false)
+    else:
+      leakCheck(mf.handle, "memfiles.open().handle", false)
+
+    let sockAsync = createAsyncNativeSocket()
+    defer: closeSocket sockAsync
+    leakCheck(sockAsync, "createAsyncNativeSocket()")
+    if sockAsync.setInheritable(not defined(nimInheritHandles)):
+      leakCheck(sockAsync, "createAsyncNativeSocket()", not defined(nimInheritHandles))
+    else:
+      raiseOSError osLastError()
+
+    let serverAsync = newAsyncSocket()
+    defer: close serverAsync
+    serverAsync.bindAddr(address = "127.0.0.1")
+    serverAsync.listen()
+    let (_, portAsync) = serverAsync.getLocalAddr
+
+    leakCheck(serverAsync.getFd, "newAsyncSocket()")
+
+    let clientAsync = newAsyncSocket()
+    defer: close clientAsync
+    waitFor clientAsync.connect("127.0.0.1", portAsync)
+
+    let inputAsync = waitFor serverAsync.accept()
+
+    leakCheck(inputAsync.getFd, "accept() async")
+  else:
+    let
+      fd = parseInt(paramStr 1)
+      expectLeak = parseBool(paramStr 3)
+      msg = (if expectLeak: "not " else: "") & "leaked " & paramStr 2
+    let validHandle =
+      when defined(windows):
+        # On Windows, due to the use of winlean, causes the program to open
+        # a handle to the various dlls that's loaded. This handle might
+        # collide with the handle sent for testing.
+        #
+        # As a walkaround, we pass an another handle that's purposefully leaked
+        # as a reference so that we can verify whether the "leaked" handle
+        # is the right one.
+        let refFd = parseInt(paramStr 4)
+        fd.isValidHandle and compareObjectHandles(fd, refFd) != 0
+      else:
+        fd.isValidHandle
+    if expectLeak xor validHandle:
+      echo msg
+
+when isMainModule: main()
diff --git a/tests/stdlib/tfdleak_multiple.nim b/tests/stdlib/tfdleak_multiple.nim
new file mode 100644
index 000000000..c26681217
--- /dev/null
+++ b/tests/stdlib/tfdleak_multiple.nim
@@ -0,0 +1,31 @@
+discard """
+joinable: false
+"""
+
+import os, osproc, strutils
+import std/assertions
+
+const Iterations = 200
+
+proc testFdLeak() =
+  var count = 0
+  let
+    test = getAppDir() / "tfdleak"
+    exe = test.addFileExt(ExeExt).quoteShell
+    options = ["", "-d:nimInheritHandles"]
+  for opt in options:
+    let
+      run = "nim c $1 $2" % [opt, quoteShell test]
+      (output, status) = execCmdEx run
+    doAssert status == 0, "Test complination failed:\n$1\n$2" % [run, output]
+    for i in 1..Iterations:
+      let (output, status) = execCmdEx exe
+      doAssert status == 0, "Execution of " & exe & " failed"
+      if "leaked" in output:
+        count.inc
+    doAssert count == 0, "Leaked " & $count & " times"
+
+when defined(windows):
+  # tfdleak was only flaky for windows (and for netbsd, there is still a bug)
+  # note that this test is quite slow, 87 sec on windows.
+  testFdLeak()
diff --git a/tests/stdlib/tfenv.nim b/tests/stdlib/tfenv.nim
new file mode 100644
index 000000000..a486b8a9d
--- /dev/null
+++ b/tests/stdlib/tfenv.nim
@@ -0,0 +1,8 @@
+import std/fenv
+import std/assertions
+
+
+func is_significant(x: float): bool =
+  x > minimumPositiveValue(float) and x < maximumPositiveValue(float)
+
+doAssert is_significant(10.0)
diff --git a/tests/stdlib/tfilesanddirs.nim b/tests/stdlib/tfilesanddirs.nim
new file mode 100644
index 000000000..a1920d4f2
--- /dev/null
+++ b/tests/stdlib/tfilesanddirs.nim
@@ -0,0 +1,36 @@
+import std/[paths, files, dirs, appdirs]
+
+from stdtest/specialpaths import buildDir
+import std/[syncio, assertions]
+
+block fileOperations:
+  let files = @[Path"these.txt", Path"are.x", Path"testing.r", Path"files.q"]
+  let dirs = @[Path"some", Path"created", Path"test", Path"dirs"]
+
+  let dname = Path"__really_obscure_dir_name"
+
+  createDir(dname.Path)
+  doAssert dirExists(Path(dname))
+ 
+  # Test creating files and dirs
+  for dir in dirs:
+    createDir(Path(dname/dir))
+    doAssert dirExists(Path(dname/dir))
+
+  for file in files:
+    let fh = open(string(dname/file), fmReadWrite) # createFile
+    fh.close()
+    doAssert fileExists(Path(dname/file))
+
+block: # getCacheDir
+  doAssert getCacheDir().dirExists
+
+block: # moveFile
+  let tempDir = getTempDir() / Path("D20221022T151608")
+  createDir(tempDir)
+  defer: removeDir(tempDir)
+
+block: # moveDir
+  let tempDir = getTempDir() / Path("D20220609T161443")
+  createDir(tempDir)
+  defer: removeDir(tempDir)
diff --git a/tests/stdlib/tformat.nim b/tests/stdlib/tformat.nim
deleted file mode 100644
index 160ab0fd5..000000000
--- a/tests/stdlib/tformat.nim
+++ /dev/null
@@ -1,12 +0,0 @@
-discard """
-  file: "tformat.nim"
-  output: "Hi Andreas! How do you feel, Rumpf?"
-"""
-# Tests the new format proc (including the & and &= operators)
-
-import strutils
-
-echo("Hi $1! How do you feel, $2?\n" % ["Andreas", "Rumpf"])
-#OUT Hi Andreas! How do you feel, Rumpf?
-
-
diff --git a/tests/stdlib/tfrexp1.nim b/tests/stdlib/tfrexp1.nim
index c6bb2b38c..aa734ddac 100644
--- a/tests/stdlib/tfrexp1.nim
+++ b/tests/stdlib/tfrexp1.nim
@@ -1,13 +1,16 @@
 discard """
-  targets: "js c c++"
-  output: '''ok'''
+  matrix: "--mm:refc; --mm:orc"
+  targets: "js c cpp"
 """
 
-import math
-import strformat
+import std/math
+import std/assertions
 
 const manualTest = false
 
+when manualTest:
+  import strformat
+
 proc frexp_test(lo, hi, step: float64) =
   var exp: int
   var frac: float64
@@ -39,6 +42,14 @@ when manualTest:
 
   frexp_test(-1000.0, 1000.0, 0.0125)
 else:
-  frexp_test(-1000000.0, 1000000.0, 0.125)
+  frexp_test(-200000.0, 200000.0, 0.125)
+
+
+doAssert frexp(8.0) == (0.5, 4)
+doAssert frexp(-8.0) == (-0.5, 4)
+doAssert frexp(0.0) == (0.0, 0)
 
-echo "ok"
+block:
+  var x: int
+  doAssert frexp(5.0, x) == 0.625
+  doAssert x == 3
diff --git a/tests/stdlib/tgenast.nim b/tests/stdlib/tgenast.nim
new file mode 100644
index 000000000..d99c9312e
--- /dev/null
+++ b/tests/stdlib/tgenast.nim
@@ -0,0 +1,274 @@
+discard """
+  matrix: "--mm:orc; --mm:refc"
+"""
+
+# xxx also test on js
+
+import std/genasts
+import std/macros
+from std/strformat import `&`
+import std/assertions
+import ./mgenast
+
+proc main =
+  block:
+    macro bar(x0: static Foo, x1: Foo, x2: Foo, xignored: Foo): untyped =
+      let s0 = "not captured!"
+      let s1 = "not captured!"
+      let xignoredLocal = kfoo4
+
+      # newLit optional:
+      let x3 = newLit kfoo4
+      let x3b = kfoo4
+
+      result = genAstOpt({kDirtyTemplate}, s1=true, s2="asdf", x0, x1=x1, x2, x3, x3b):
+        doAssert not declared(xignored)
+        doAssert not declared(xignoredLocal)
+        (s1, s2, s0, x0, x1, x2, x3, x3b)
+
+    let s0 = "caller scope!"
+
+    doAssert bar(kfoo1, kfoo2, kfoo3, kfoo4) ==
+      (true, "asdf", "caller scope!", kfoo1, kfoo2, kfoo3, kfoo4, kfoo4)
+
+  block:
+    # doesn't have limitation mentioned in https://github.com/nim-lang/RFCs/issues/122#issue-401636535
+    macro abc(name: untyped): untyped =
+      result = genAst(name):
+        type name = object
+
+    abc(Bar)
+    doAssert Bar.default == Bar()
+
+  block:
+    # backticks parser limitations / ambiguities not are an issue with `genAst`:
+    # (#10326 #9745 are fixed but `quote do` still has underlying ambiguity issue
+    # with backticks)
+    type Foo = object
+      a: int
+
+    macro m1(): untyped =
+      # result = quote do: # Error: undeclared identifier: 'a1'
+      result = genAst:
+        template `a1=`(x: var Foo, val: int) =
+          x.a = val
+
+    m1()
+    var x0: Foo
+    x0.a1 = 10
+    doAssert x0 == Foo(a: 10)
+
+  block:
+    # avoids bug #7375
+    macro fun(b: static[bool], b2: bool): untyped =
+      result = newStmtList()
+    macro foo(c: bool): untyped =
+      var b = false
+      result = genAst(b, c):
+        fun(b, c)
+
+    foo(true)
+
+  block:
+    # avoids bug #7589
+    # since `==` works with genAst, the problem goes away
+    macro foo2(): untyped =
+      # result = quote do: # Error: '==' cannot be passed to a procvar
+      result = genAst:
+        `==`(3,4)
+    doAssert not foo2()
+
+  block:
+    # avoids bug #7726
+    # expressions such as `a.len` are just passed as arguments to `genAst`, and
+    # caller scope is not polluted with definitions such as `let b = newLit a.len`
+    macro foo(): untyped =
+      let a = @[1, 2, 3, 4, 5]
+      result = genAst(a, b = a.len): # shows 2 ways to get a.len
+        (a.len, b)
+    doAssert foo() == (5, 5)
+
+  block:
+    # avoids bug #9607
+    proc fun1(info:LineInfo): string = "bar1"
+    proc fun2(info:int): string = "bar2"
+
+    macro bar2(args: varargs[untyped]): untyped =
+      let info = args.lineInfoObj
+      let fun1 = bindSym"fun1" # optional; we can remove this and also the
+      # capture of fun1, as show in next example
+      result = genAst(info, fun1):
+        (fun1(info), fun2(info.line))
+    doAssert bar2() == ("bar1", "bar2")
+
+    macro bar3(args: varargs[untyped]): untyped =
+      let info = args.lineInfoObj
+      result = genAst(info):
+        (fun1(info), fun2(info.line))
+    doAssert bar3() == ("bar1", "bar2")
+
+    macro bar(args: varargs[untyped]): untyped =
+      let info = args.lineInfoObj
+      let fun1 = bindSym"fun1"
+      let fun2 = bindSym"fun2"
+      result = genAstOpt({kDirtyTemplate}, info):
+        (fun1(info), fun2(info.line))
+    doAssert bar() == ("bar1", "bar2")
+
+  block:
+    # example from bug #7889 works
+    # after changing method call syntax to regular call syntax; this is a
+    # limitation described in bug #7085
+    # note that `quote do` would also work after that change in this example.
+    doAssert bindme2() == kfoo1
+    doAssert bindme3() == kfoo1
+    doAssert not compiles(bindme4()) # correctly gives Error: undeclared identifier: 'myLocalPriv'
+    proc myLocalPriv2(): auto = kfoo2
+    doAssert bindme5UseExpose() == kfoo1
+
+    # example showing hijacking behavior when using `kDirtyTemplate`
+    doAssert bindme5UseExposeFalse() == kfoo2
+      # local `myLocalPriv2` hijacks symbol `mgenast.myLocalPriv2`. In most
+      # use cases this is probably not what macro writer intends as it's
+      # surprising; hence `kDirtyTemplate` is not the default.
+
+    when nimvm: # disabled because `newStringStream` is used
+      discard
+    else:
+      bindme6UseExpose()
+      bindme6UseExposeFalse()
+
+  block:
+    macro mbar(x3: Foo, x3b: static Foo): untyped =
+      var x1=kfoo3
+      var x2=newLit kfoo3
+      var x4=kfoo3
+      var xLocal=kfoo3
+
+      proc funLocal(): auto = kfoo4
+
+      result = genAst(x1, x2, x3, x4):
+        # local x1 overrides remote x1
+        when false:
+          # one advantage of using `kDirtyTemplate` is that these would hold:
+          doAssert not declared xLocal
+          doAssert not compiles(echo xLocal)
+          # however, even without it, we at least correctly generate CT error
+          # if trying to use un-captured symbol; this correctly gives:
+          # Error: internal error: environment misses: xLocal
+          echo xLocal
+
+        proc foo1(): auto =
+          # note that `funLocal` is captured implicitly, according to hygienic
+          # template rules; with `kDirtyTemplate` it would not unless
+          # captured in `genAst` capture list explicitly
+          (a0: xRemote, a1: x1, a2: x2, a3: x3, a4: x4, a5: funLocal())
+
+      return result
+
+    proc main()=
+      var xRemote=kfoo1
+      var x1=kfoo2
+      mbar(kfoo4, kfoo4)
+      doAssert foo1() == (a0: kfoo1, a1: kfoo3, a2: kfoo3, a3: kfoo4, a4: kfoo3, a5: kfoo4)
+
+    main()
+
+  block:
+    # With `kDirtyTemplate`, the example from #8220 works.
+    # See https://nim-lang.github.io/Nim/strformat.html#limitations for
+    # an explanation of why {.dirty.} is needed.
+    macro foo(): untyped =
+      result = genAstOpt({kDirtyTemplate}):
+        let bar = "Hello, World"
+        &"Let's interpolate {bar} in the string"
+    doAssert foo() == "Let's interpolate Hello, World in the string"
+
+
+  block: # nested application of genAst
+    macro createMacro(name, obj, field: untyped): untyped =
+      result = genAst(obj = newDotExpr(obj, field), lit = 10, name, field):
+        # can't reuse `result` here, would clash
+        macro name(arg: untyped): untyped =
+          genAst(arg2=arg): # somehow `arg2` rename is needed
+            (obj, astToStr(field), lit, arg2)
+
+    var x = @[1, 2, 3]
+    createMacro foo, x, len
+    doAssert (foo 20) == (3, "len", 10, 20)
+
+  block: # test with kNoNewLit
+    macro bar(): untyped =
+      let s1 = true
+      template boo(x): untyped =
+        fun(x)
+      result = genAstOpt({kNoNewLit}, s1=newLit(s1), s1b=s1): (s1, s1b)
+    doAssert bar() == (true, 1)
+
+  block: # sanity check: check passing `{}` also works
+    macro bar(): untyped =
+      result = genAstOpt({}, s1=true): s1
+    doAssert bar() == true
+
+  block: # test passing function and type symbols
+    proc z1(): auto = 41
+    type Z4 = type(1'i8)
+    macro bar(Z1: typedesc): untyped =
+      proc z2(): auto = 42
+      proc z3[T](a: T): auto = 43
+      let Z2 = genAst():
+        type(true)
+      let z4 = genAst():
+        proc myfun(): auto = 44
+        myfun
+      type Z3 = type(1'u8)
+      result = genAst(z4, Z1, Z2):
+        # z1, z2, z3, Z3, Z4 are captured automatically
+        # z1, z2, z3 can optionally be specified in capture list
+        (z1(), z2(), z3('a'), z4(), $Z1, $Z2, $Z3, $Z4)
+    type Z1 = type('c')
+    doAssert bar(Z1) == (41, 42, 43, 44, "char", "bool", "uint8", "int8")
+
+  block: # fix bug #11986
+    proc foo(): auto =
+      var s = { 'a', 'b' }
+      # var n = quote do: `s` # would print {97, 98}
+      var n = genAst(s): s
+      n.repr
+    static: doAssert foo() == "{'a', 'b'}"
+
+  block: # also from #11986
+    macro foo(): untyped =
+      var s = { 'a', 'b' }
+      # quote do:
+      #   let t = `s`
+      #   $typeof(t) # set[range 0..65535(int)]
+      genAst(s):
+        let t = s
+        $typeof(t)
+    doAssert foo() == "set[char]"
+
+  block:
+    macro foo(): untyped =
+      type Foo = object
+      template baz2(a: int): untyped = a*10
+      macro baz3(a: int): untyped = newLit 13
+      result = newStmtList()
+
+      result.add genAst(Foo, baz2, baz3) do: # shows you can pass types, templates etc
+        var x: Foo
+        $($typeof(x), baz2(3), baz3(4))
+
+      let ret = genAst() do: # shows you don't have to, since they're inject'd
+        var x: Foo
+        $($typeof(x), baz2(3), baz3(4))
+    doAssert foo() == """("Foo", 30, 13)"""
+
+  block: # illustrates how symbol visiblity can be controlled precisely using `mixin`
+    proc locafun1(): auto = "in locafun1 (caller scope)" # this will be used because of `mixin locafun1` => explicit hijacking is ok
+    proc locafun2(): auto = "in locafun2 (caller scope)" # this won't be used => no hijacking
+    proc locafun3(): auto = "in locafun3 (caller scope)"
+    doAssert mixinExample() == ("in locafun1 (caller scope)", "in locafun2", "in locafun3 (caller scope)")
+
+static: main()
+main()
diff --git a/tests/stdlib/tgetaddrinfo.nim b/tests/stdlib/tgetaddrinfo.nim
new file mode 100644
index 000000000..3a90034c8
--- /dev/null
+++ b/tests/stdlib/tgetaddrinfo.nim
@@ -0,0 +1,38 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  exitcode: 0
+  output: ""
+"""
+
+# bug: https://github.com/nim-lang/Nim/issues/10198
+
+import nativesockets
+import std/assertions
+
+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.SockLen == sizeof(Sockaddr_in).SockLen
+  doAssert aiList.ai_next == nil
+  freeAddrInfo aiList
+
+when defined(posix) and not defined(haiku) and not defined(freebsd) and not defined(openbsd) and not defined(netbsd):
+
+  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.SockLen == sizeof(Sockaddr_in).SockLen
+    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.SockLen == sizeof(Sockaddr_in6).SockLen
+    doAssert aiList.ai_next == nil
+    freeAddrInfo aiList
diff --git a/tests/stdlib/tgetfileinfo.nim b/tests/stdlib/tgetfileinfo.nim
index 019c2eb7f..ae1480a4c 100644
--- a/tests/stdlib/tgetfileinfo.nim
+++ b/tests/stdlib/tgetfileinfo.nim
@@ -1,8 +1,11 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   output: "pcDir\npcFile\npcLinkToDir\npcLinkToFile\n"
+  joinable: false
 """
 
 import os, strutils
+import std/[syncio, assertions]
 # Cases
 #  1 - String : Existing File : Symlink true
 #  2 - String : Existing File : Symlink false
@@ -14,13 +17,13 @@ import os, strutils
 #  8 - Handle : Invalid Handle
 
 proc genBadFileName(limit = 100): string =
-    ## Generates a filename of a nonexistant file.
+    ## Generates a filename of a nonexistent file.
     ## Returns "" if generation fails.
     result = "a"
     var hitLimit = true
 
     for i in 0..100:
-      if existsFile(result):
+      if fileExists(result):
         result.add("a")
       else:
         hitLimit = false
@@ -77,8 +80,8 @@ proc testGetFileInfo =
   # Case 6 and 8
   block:
     let
-      testFile: TFile = nil
-      testHandle = TFileHandle(-1)
+      testFile: File = nil
+      testHandle = FileHandle(-1)
     try:
       discard getFileInfo(testFile)
       echo("Handle : Invalid File : Failure")
@@ -125,10 +128,37 @@ proc testGetFileInfo =
       echo pcLinkToDir
       echo pcLinkToFile
 
+    doAssert dirInfo.isSpecial == false
+    doAssert fileInfo.isSpecial == false
+    when defined(posix):
+      doAssert linkDirInfo.isSpecial == false
+      doAssert linkFileInfo.isSpecial == false
+
     removeDir(dirPath)
     removeFile(filePath)
     when defined(posix):
       removeFile(linkDirPath)
       removeFile(linkFilePath)
 
+  # Test that `isSpecial` is set correctly
+  block:
+    when defined(posix):
+      let
+        tmp = getTempDir()
+        fifoPath     = tmp / "test-fifo"
+        linkFifoPath = tmp / "test-link-fifo"
+
+      doAssert execShellCmd("mkfifo " & fifoPath) == 0
+      createSymlink(fifoPath, linkFifoPath)
+
+      let
+        fifoInfo = getFileInfo(fifoPath)
+        linkFifoInfo = getFileInfo(linkFifoPath)
+
+      doAssert fifoInfo.isSpecial == true
+      doAssert linkFifoInfo.isSpecial == true
+
+      removeFile(fifoPath)
+      removeFile(linkFifoPath)
+
 testGetFileInfo()
diff --git a/tests/stdlib/tgetprotobyname.nim b/tests/stdlib/tgetprotobyname.nim
new file mode 100644
index 000000000..1fc060ffe
--- /dev/null
+++ b/tests/stdlib/tgetprotobyname.nim
@@ -0,0 +1,19 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import nativesockets
+import std/assertions
+
+doAssert getProtoByName("ipv6") == 41
+doAssert getProtoByName("tcp") == 6
+doAssert getProtoByName("udp") == 17
+doAssert getProtoByName("icmp") == 1
+doAssert getProtoByName("ipv6-icmp") == 58
+
+when defined(windows):
+  doAssertRaises(OSError):
+    discard getProtoByName("raw")
+
+doAssertRaises(OSError):
+  discard getProtoByName("Error")
diff --git a/tests/stdlib/tglobs.nim b/tests/stdlib/tglobs.nim
new file mode 100644
index 000000000..4aa21992c
--- /dev/null
+++ b/tests/stdlib/tglobs.nim
@@ -0,0 +1,25 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/private/globs
+import std/assertions
+
+template main =
+  when defined(windows):
+    doAssert nativeToUnixPath("C:") == "/C"
+    doAssert nativeToUnixPath(r"D:\") == "/D/"
+    doAssert nativeToUnixPath(r"E:\a") == "/E/a"
+    doAssert nativeToUnixPath(r"E:\a1\") == "/E/a1/"
+    doAssert nativeToUnixPath(r"E:\a1\bc") == "/E/a1/bc"
+    doAssert nativeToUnixPath(r"\a1\bc") == "/a1/bc"
+    doAssert nativeToUnixPath(r"a1\bc") == "a1/bc"
+    doAssert nativeToUnixPath("a1") == "a1"
+    doAssert nativeToUnixPath("") == ""
+    doAssert nativeToUnixPath(".") == "."
+    doAssert nativeToUnixPath("..") == ".."
+    doAssert nativeToUnixPath(r"..\") == "../"
+    doAssert nativeToUnixPath(r"..\..\.\") == "../.././"
+
+static: main()
+main()
diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim
index c442b43fb..4555fbcb3 100644
--- a/tests/stdlib/thashes.nim
+++ b/tests/stdlib/thashes.nim
@@ -1,8 +1,242 @@
-import unittest
-import hashes
-
-suite "hashes":
-  suite "hashing":
-    test "0.0 and -0.0 should have the same hash value":
-      var dummy = 0.0
-      check hash(dummy) == hash(-dummy)
+discard """
+  matrix: "--mm:refc; --mm:orc; --backend:cpp; --backend:js --jsbigint64:on; --backend:c -d:nimStringHash2; --backend:cpp -d:nimStringHash2; --backend:js -d:nimStringHash2"
+"""
+
+import std/hashes
+from stdtest/testutils import disableVm, whenVMorJs
+import std/assertions
+
+when not defined(js) and not defined(cpp):
+  block:
+    var x = 12
+    iterator hello(): int {.closure.} =
+      yield x
+
+    discard hash(hello)
+
+block hashes:
+  block hashing:
+    var dummy = 0.0
+    doAssert hash(dummy) == hash(-dummy)
+
+  # "VM and runtime should make the same hash value (hashIdentity)"
+  block:
+    const hi123 = hashIdentity(123)
+    doAssert hashIdentity(123) == hi123
+
+  # "VM and runtime should make the same hash value (hashWangYi1)"
+  block:
+    const wy123 = hashWangYi1(123)
+    doAssert wy123 != 0
+    doAssert hashWangYi1(123) == wy123
+    const wyNeg123 = hashWangYi1(-123)
+    doAssert wyNeg123 != 0
+    when not defined(js): # TODO: fixme it doesn't work for JS
+      doAssert hashWangYi1(-123) == wyNeg123
+
+
+  # "hashIdentity value incorrect at 456"
+  block:
+    doAssert hashIdentity(456) == 456
+
+  # "hashWangYi1 value incorrect at 456"
+  block:
+    when Hash.sizeof < 8:
+      doAssert hashWangYi1(456) == 1293320666
+    else:
+      doAssert hashWangYi1(456) == -6421749900419628582
+
+template jsNoInt64: untyped =
+  when defined js:
+    when compiles(compileOption("jsbigint64")):
+      when not compileOption("jsbigint64"): true
+      else: false
+    else: false
+  else: false
+const sHash2 = (when defined(nimStringHash2) or jsNoInt64(): true else: false)
+
+block empty:
+  const emptyStrHash = # Hash=int=4B on js even w/--jsbigint64:on => cast[Hash]
+    when sHash2: 0 else: cast[Hash](-7286425919675154353i64)
+  var
+    a = ""
+    b = newSeq[char]()
+    c = newSeq[int]()
+    d = cstring""
+    e = "abcd"
+  doAssert hash(a) == emptyStrHash
+  doAssert hash(b) == emptyStrHash
+  doAssert hash(c) == 0
+  doAssert hash(d) == emptyStrHash
+  doAssert hashIgnoreCase(a) == 0
+  doAssert hashIgnoreStyle(a) == 0
+  doAssert hash(e, 3, 2) == emptyStrHash
+
+block sameButDifferent:
+  doAssert hash("aa bb aaaa1234") == hash("aa bb aaaa1234", 0, 13)
+  doAssert hash("aa bb aaaa1234") == hash(cstring"aa bb aaaa1234")
+  doAssert hashIgnoreCase("aA bb aAAa1234") == hashIgnoreCase("aa bb aaaa1234")
+  doAssert hashIgnoreStyle("aa_bb_AAaa1234") == hashIgnoreCase("aaBBAAAa1234")
+
+block smallSize: # no multibyte hashing
+  let
+    xx = @['H', 'i']
+    ii = @[72'u8, 105]
+    ss = "Hi"
+  doAssert hash(xx) == hash(ii)
+  doAssert hash(xx) == hash(ss)
+  doAssert hash(xx) == hash(xx, 0, xx.high)
+  doAssert hash(ss) == hash(ss, 0, ss.high)
+
+block largeSize: # longer than 4 characters
+  let
+    xx = @['H', 'e', 'l', 'l', 'o']
+    xxl = @['H', 'e', 'l', 'l', 'o', 'w', 'e', 'e', 'n', 's']
+    ssl = "Helloweens"
+  doAssert hash(xxl) == hash(ssl)
+  doAssert hash(xxl) == hash(xxl, 0, xxl.high)
+  doAssert hash(ssl) == hash(ssl, 0, ssl.high)
+  doAssert hash(xx) == hash(xxl, 0, 4)
+  doAssert hash(xx) == hash(ssl, 0, 4)
+  doAssert hash(xx, 0, 3) == hash(xxl, 0, 3)
+  doAssert hash(xx, 0, 3) == hash(ssl, 0, 3)
+
+proc main() =
+  doAssert hash(0.0) == hash(0)
+  # bug #16061
+  when not sHash2: # Hash=int=4B on js even w/--jsbigint64:on => cast[Hash]
+    doAssert hash(cstring"abracadabra") == cast[Hash](-1119910118870047694i64)
+  else:
+    doAssert hash(cstring"abracadabra") == 97309975
+  doAssert hash(cstring"abracadabra") == hash("abracadabra")
+
+  when sizeof(int) == 8 or defined(js):
+    block:
+      var s: seq[Hash]
+      for a in [0.0, 1.0, -1.0, 1000.0, -1000.0]:
+        let b = hash(a)
+        doAssert b notin s
+        s.add b
+    when defined(js):
+      doAssert hash(0.345602) == 2035867618
+      doAssert hash(234567.45) == -20468103
+      doAssert hash(-9999.283456) == -43247422
+      doAssert hash(84375674.0) == 707542256
+    else:
+      doAssert hash(0.345602) == 387936373221941218
+      doAssert hash(234567.45) == -8179139172229468551
+      doAssert hash(-9999.283456) == 5876943921626224834
+      doAssert hash(84375674.0) == 1964453089107524848
+  else:
+    doAssert hash(0.345602) != 0
+    doAssert hash(234567.45) != 0
+    doAssert hash(-9999.283456) != 0
+    doAssert hash(84375674.0) != 0
+
+  block: # bug #16555
+    proc fn(): auto =
+      # avoids hardcoding values
+      var a = "abc\0def"
+      var b = a.cstring
+      result = (hash(a), hash(b))
+      doAssert result[0] != result[1]
+    when not defined(js):
+      doAssert fn() == static(fn())
+    else:
+      # xxx this is a tricky case; consistency of hashes for cstring's containing
+      # '\0\' matters for c backend but less for js backend since such strings
+      # are much less common in js backend; we make vm for js backend consistent
+      # with c backend instead of js backend because FFI code (or other) could
+      # run at CT, expecting c semantics.
+      discard
+
+  block: # hash(object)
+    type
+      Obj = object
+        x: int
+        y: string
+      Obj2[T] = object
+        x: int
+        y: string
+      Obj3 = object
+        x: int
+        y: string
+      Obj4 = object
+        case t: bool
+        of false:
+          x: int
+        of true:
+          y: int
+        z: int
+      Obj5 = object
+        case t: bool
+        of false:
+          x: int
+        of true:
+          y: int
+        z: int
+
+    proc hash(a: Obj2): Hash = hash(a.x)
+    proc hash(a: Obj3): Hash = hash((a.x,))
+    proc hash(a: Obj5): Hash =
+      case a.t
+      of false: hash(a.x)
+      of true: hash(a.y)
+
+    doAssert hash(Obj(x: 520, y: "Nim")) != hash(Obj(x: 520, y: "Nim2"))
+    doAssert hash(Obj2[float](x: 520, y: "Nim")) == hash(Obj2[float](x: 520, y: "Nim2"))
+    doAssert hash(Obj2[float](x: 520, y: "Nim")) != hash(Obj2[float](x: 521, y: "Nim2"))
+    doAssert hash(Obj3(x: 520, y: "Nim")) == hash(Obj3(x: 520, y: "Nim2"))
+
+    doAssert hash(Obj4(t: false, x: 1)) == hash(Obj4(t: false, x: 1))
+    doAssert hash(Obj4(t: false, x: 1)) != hash(Obj4(t: false, x: 2))
+    doAssert hash(Obj4(t: false, x: 1)) != hash(Obj4(t: true, y: 1))
+
+    doAssert hash(Obj5(t: false, x: 1)) != hash(Obj5(t: false, x: 2))
+    doAssert hash(Obj5(t: false, x: 1)) == hash(Obj5(t: true, y: 1))
+    doAssert hash(Obj5(t: false, x: 1)) != hash(Obj5(t: true, y: 2))
+
+  block: # hash(ref|ptr|pointer)
+    var a: array[10, uint8]
+    # disableVm:
+    whenVMorJs:
+      # pending fix proposed in https://github.com/nim-lang/Nim/issues/15952#issuecomment-786312417
+      discard
+    do:
+      doAssert a[0].addr.hash != a[1].addr.hash
+      doAssert cast[pointer](a[0].addr).hash == a[0].addr.hash
+
+  block: # hash(ref)
+    type A = ref object
+      x: int
+    let a = A(x: 3)
+    disableVm: # xxx Error: VM does not support 'cast' from tyRef to tyPointer
+      let ha = a.hash
+      doAssert ha != A(x: 3).hash # A(x: 3) is a different ref object from `a`.
+      a.x = 4
+      doAssert ha == a.hash # the hash only depends on the address
+
+  block: # hash(proc)
+    proc fn(a: int): auto = a*2
+    doAssert fn isnot "closure"
+    doAssert fn is (proc)
+    const fn2 = fn
+    let fn3 = fn
+    whenVMorJs: discard
+    do:
+      doAssert hash(fn2) == hash(fn)
+      doAssert hash(fn3) == hash(fn)
+
+  block: # hash(closure)
+    proc outer() =
+      var a = 0
+      proc inner() = a.inc
+      doAssert inner is "closure"
+      let inner2 = inner
+      whenVMorJs: discard
+      do:
+        doAssert hash(inner2) == hash(inner)
+    outer()
+
+static: main()
+main()
diff --git a/tests/stdlib/theapqueue.nim b/tests/stdlib/theapqueue.nim
new file mode 100644
index 000000000..afb09c7e3
--- /dev/null
+++ b/tests/stdlib/theapqueue.nim
@@ -0,0 +1,106 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/heapqueue
+import std/assertions
+
+proc toSortedSeq[T](h: HeapQueue[T]): seq[T] =
+  var tmp = h
+  result = @[]
+  while tmp.len > 0:
+    result.add(pop(tmp))
+
+proc heapProperty[T](h: HeapQueue[T]): bool =
+  for k in 0 .. h.len - 2: # the last element is always a leaf
+    let left = 2 * k + 1
+    if left < h.len and h[left] < h[k]:
+      return false
+    let right = left + 1
+    if right < h.len and h[right] < h[k]:
+      return false
+  true
+
+template main() =
+  block: # simple sanity test
+    var heap = initHeapQueue[int]()
+    let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
+    for item in data:
+      push(heap, item)
+    doAssert(heap == data.toHeapQueue)
+    doAssert(heap[0] == 0)
+    doAssert(heap.toSortedSeq == @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
+
+  block: # test del
+    var heap = initHeapQueue[int]()
+    let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
+    for item in data: push(heap, item)
+
+    heap.del(0)
+    doAssert(heap[0] == 1)
+
+    heap.del(heap.find(7))
+    doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 5, 6, 8, 9])
+
+    heap.del(heap.find(5))
+    doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 6, 8, 9])
+
+    heap.del(heap.find(6))
+    doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 8, 9])
+
+    heap.del(heap.find(2))
+    doAssert(heap.toSortedSeq == @[1, 3, 4, 8, 9])
+
+    doAssert(heap.find(2) == -1)
+
+  block: # test del last
+    var heap = initHeapQueue[int]()
+    let data = [1, 2, 3]
+    for item in data: push(heap, item)
+
+    heap.del(2)
+    doAssert(heap.toSortedSeq == @[1, 2])
+
+    heap.del(1)
+    doAssert(heap.toSortedSeq == @[1])
+
+    heap.del(0)
+    doAssert(heap.toSortedSeq == @[])
+
+  block: # testing the heap proeprty
+    var heap = [1, 4, 2, 5].toHeapQueue
+    doAssert heapProperty(heap)
+
+    heap.push(42)
+    doAssert heapProperty(heap)
+    heap.push(0)
+    doAssert heapProperty(heap)
+    heap.push(3)
+    doAssert heapProperty(heap)
+    heap.push(3)
+    doAssert heapProperty(heap)
+
+    # [0, 3, 1, 4, 42, 2, 3, 5]
+
+    discard heap.pop()
+    doAssert heapProperty(heap)
+    discard heap.pop()
+    doAssert heapProperty(heap)
+
+    heap.del(2)
+    doAssert heapProperty(heap)
+
+    # [2, 3, 5, 4, 42]
+
+    discard heap.replace(12)
+    doAssert heapProperty(heap)
+    discard heap.replace(1)
+    doAssert heapProperty(heap)
+
+    discard heap.pushpop(2)
+    doAssert heapProperty(heap)
+    discard heap.pushpop(0)
+    doAssert heapProperty(heap)
+
+static: main()
+main()
diff --git a/tests/stdlib/thighlite.nim b/tests/stdlib/thighlite.nim
new file mode 100644
index 000000000..0cd334254
--- /dev/null
+++ b/tests/stdlib/thighlite.nim
@@ -0,0 +1,45 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import unittest, strutils
+import ../../lib/packages/docutils/highlite
+import std/objectdollar
+
+block: # Nim tokenizing
+  test "string literals and escape seq":
+    check("\"ok1\\nok2\\nok3\"".tokenize(langNim) ==
+       @[("\"ok1", gtStringLit), ("\\n", gtEscapeSequence), ("ok2", gtStringLit),
+         ("\\n", gtEscapeSequence), ("ok3\"", gtStringLit)
+      ])
+    check("\"\"\"ok1\\nok2\\nok3\"\"\"".tokenize(langNim) ==
+       @[("\"\"\"ok1\\nok2\\nok3\"\"\"", gtLongStringLit)
+      ])
+
+  test "whitespace at beginning of line is preserved":
+    check("  discard 1".tokenize(langNim) ==
+       @[("  ", gtWhitespace), ("discard", gtKeyword), (" ", gtWhitespace),
+         ("1", gtDecNumber)
+       ])
+
+block: # Cmd (shell) tokenizing
+  test "cmd with dollar and output":
+    check(
+      dedent"""
+        $ nim c file.nim
+        out: file [SuccessX]"""
+        .tokenize(langConsole) ==
+      @[("$ ", gtPrompt), ("nim", gtProgram),
+        (" ", gtWhitespace), ("c", gtOption), (" ", gtWhitespace),
+        ("file.nim", gtIdentifier), ("\n", gtWhitespace),
+        ("out: file [SuccessX]", gtProgramOutput)
+      ])
+
+block: # bug #21232
+  let code = "/"
+  var toknizr: GeneralTokenizer
+
+  initGeneralTokenizer(toknizr, code)
+
+  getNextToken(toknizr, langC)
+  check $toknizr == """(kind: gtOperator, start: 0, length: 1, buf: "/", pos: 1, state: gtEof, lang: langC)"""
diff --git a/tests/stdlib/thtmlparser.nim b/tests/stdlib/thtmlparser.nim
new file mode 100644
index 000000000..853a1c0cc
--- /dev/null
+++ b/tests/stdlib/thtmlparser.nim
@@ -0,0 +1,159 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c js"
+  output: '''
+true
+https://example.com/test?format=jpg&name=orig##
+https://example.com/test?format=jpg&name=orig##text
+https://example.com/test?format=jpg##text
+'''
+"""
+import htmlparser
+import xmltree
+import strutils
+from streams import newStringStream
+import std/assertions
+
+block t2813:
+  const
+    html = """
+    <html>
+      <head>
+        <title>Test</title>
+      </head>
+      <body>
+        <table>
+          <thead>
+            <tr><td>A</td></tr>
+            <tr><td>B</td></tr>
+          </thead>
+          <tbody>
+            <tr><td></td>A<td></td></tr>
+            <tr><td></td>B<td></td></tr>
+            <tr><td></td>C<td></td></tr>
+          </tbody>
+          <tfoot>
+            <tr><td>A</td></tr>
+          </tfoot>
+        </table>
+      </body>
+    </html>
+    """
+  var errors: seq[string] = @[]
+  let tree = parseHtml(newStringStream(html), "test.html", errors)
+  doAssert errors.len == 0 # Errors: </thead> expected,...
+
+  var len = tree.findAll("tr").len # len = 6
+  var rows: seq[XmlNode] = @[]
+  for n in tree.findAll("table"):
+    n.findAll("tr", rows)  # len = 2
+    break
+  doAssert tree.findAll("tr").len == rows.len
+
+
+block t2814:
+  ## builds the two cases below and test that
+  ## ``//[dd,li]`` has "<p>that</p>" as children
+  ##
+  ##  <dl>
+  ##    <dt>this</dt>
+  ##    <dd>
+  ##      <p>that</p>
+  ##    </dd>
+  ##  </dl>
+
+  ##
+  ## <ul>
+  ##   <li>
+  ##     <p>that</p>
+  ##   </li>
+  ## </ul>
+  for ltype in [["dl","dd"], ["ul","li"]]:
+    let desc_item = if ltype[0]=="dl": "<dt>this</dt>" else: ""
+    let item = "$1<$2><p>that</p></$2>" % [desc_item, ltype[1]]
+    let list = """ <$1>
+     $2
+  </$1> """ % [ltype[0], item]
+
+    var errors : seq[string] = @[]
+    let parseH = parseHtml(newStringStream(list),"statichtml", errors =errors)
+
+    if $parseH.findAll(ltype[1])[0].child("p") != "<p>that</p>":
+      echo "case " & ltype[0] & " failed !"
+      quit(2)
+  echo "true"
+
+block t6154:
+  let foo = """
+  <!DOCTYPE html>
+  <html>
+      <head>
+        <title> foobar </title>
+      </head>
+      <body>
+        <p class=foo id=bar></p>
+        <p something=&#9;foo&#9;bar&#178;></p>
+        <p something=  &#9;foo&#9;bar&#178; foo  =bloo></p>
+        <p class="foo2" id="bar2"></p>
+        <p wrong= ></p>
+        <p data-foo data-bar="correct!" enabled  ></p>
+        <p quux whatever></p>
+      </body>
+  </html>
+  """
+
+  var errors: seq[string] = @[]
+  let html = parseHtml(newStringStream(foo), "statichtml", errors=errors)
+  doAssert "statichtml(11, 18) Error: attribute value expected" in errors
+  let ps = html.findAll("p")
+  doAssert ps.len == 7
+
+  doAssert ps[0].attrsLen == 2
+  doAssert ps[0].attr("class") == "foo"
+  doAssert ps[0].attr("id") == "bar"
+  doAssert ps[0].len == 0
+
+  doAssert ps[1].attrsLen == 1
+  doAssert ps[1].attr("something") == "\tfoo\tbar²"
+  doAssert ps[1].len == 0
+
+  doAssert ps[2].attrsLen == 2
+  doAssert ps[2].attr("something") == "\tfoo\tbar²"
+  doAssert ps[2].attr("foo") == "bloo"
+  doAssert ps[2].len == 0
+
+  doAssert ps[3].attrsLen == 2
+  doAssert ps[3].attr("class") == "foo2"
+  doAssert ps[3].attr("id") == "bar2"
+  doAssert ps[3].len == 0
+
+  doAssert ps[4].attrsLen == 1
+  doAssert ps[4].attr("wrong") == ""
+
+  doAssert ps[5].attrsLen == 3
+  doAssert ps[5].attr("data-foo") == ""
+  doAssert ps[5].attr("data-bar") == "correct!"
+  doAssert ps[5].attr("enabled") == ""
+  doAssert ps[5].len == 0
+
+  doAssert ps[6].attrsLen == 2
+  doAssert ps[6].attr("quux") == ""
+  doAssert ps[6].attr("whatever") == ""
+  doAssert ps[6].len == 0
+
+# bug #11713, #1034
+var content = """
+# with &
+<img src="https://example.com/test?format=jpg&name=orig" alt="">
+<img src="https://example.com/test?format=jpg&name=orig" alt="text">
+
+# without &
+<img src="https://example.com/test?format=jpg" alt="text">
+"""
+
+var
+  stream = newStringStream(content)
+  body = parseHtml(stream)
+
+for y in body.findAll("img"):
+  echo y.attr("src"), "##", y.attr("alt")
diff --git a/tests/stdlib/thtmlparser2813.nim b/tests/stdlib/thtmlparser2813.nim
deleted file mode 100644
index 4b04bc3f0..000000000
--- a/tests/stdlib/thtmlparser2813.nim
+++ /dev/null
@@ -1,45 +0,0 @@
-discard """
-  output: "@[]"
-"""
-import htmlparser
-import xmltree
-from streams import newStringStream
-
-const
-  html = """
-  <html>
-    <head>
-      <title>Test</title>
-    </head>
-    <body>
-      <table>
-        <thead>
-          <tr><td>A</td></tr>
-          <tr><td>B</td></tr>
-        </thead>
-        <tbody>
-          <tr><td></td>A<td></td></tr>
-          <tr><td></td>B<td></td></tr>
-          <tr><td></td>C<td></td></tr>
-        </tbody>
-        <tfoot>
-          <tr><td>A</td></tr>
-        </tfoot>
-      </table>
-    </body>
-  </html>
-  """
-var errors: seq[string] = @[]
-
-let tree = parseHtml(newStringStream(html), "test.html", errors)
-
-echo errors # Errors: </thead> expected,...
-
-var len = tree.findAll("tr").len # len = 6
-
-var rows: seq[XmlNode] = @[]
-for n in tree.findAll("table"):
-  n.findAll("tr", rows)  # len = 2
-  break
-
-assert tree.findAll("tr").len == rows.len
diff --git a/tests/stdlib/thtmlparser2814.nim b/tests/stdlib/thtmlparser2814.nim
deleted file mode 100644
index 968d390f1..000000000
--- a/tests/stdlib/thtmlparser2814.nim
+++ /dev/null
@@ -1,44 +0,0 @@
-discard """
-  output: true
-"""
-import htmlparser
-import xmltree
-import strutils
-from streams import newStringStream
-
-
-## builds the two cases below and test that
-## ``//[dd,li]`` has "<p>that</p>" as children
-##
-##  <dl>
-##    <dt>this</dt>
-##    <dd>
-##      <p>that</p>
-##    </dd>
-##  </dl>
-
-##
-## <ul>
-##   <li>
-##     <p>that</p>
-##   </li>
-## </ul>
-
-
-for ltype in [["dl","dd"], ["ul","li"]]:
-  let desc_item = if ltype[0]=="dl": "<dt>this</dt>" else: ""
-  let item = "$1<$2><p>that</p></$2>" % [desc_item, ltype[1]]
-  let list = """ <$1>
-   $2
-</$1> """ % [ltype[0], item]
-
-  var errors : seq[string] = @[]
-
-  let parseH = parseHtml(newStringStream(list),"statichtml", errors =errors)
-
-  if $parseH.findAll(ltype[1])[0].child("p") != "<p>that</p>":
-    echo "case " & ltype[0] & " failed !"
-    quit(2)
-
-
-echo "true"
diff --git a/tests/stdlib/thttpclient.nim b/tests/stdlib/thttpclient.nim
index fff02722a..0bd479670 100644
--- a/tests/stdlib/thttpclient.nim
+++ b/tests/stdlib/thttpclient.nim
@@ -1,33 +1,65 @@
 discard """
   cmd: "nim c --threads:on -d:ssl $file"
-  exitcode: 0
-  output: "OK"
-  disabled: "travis"
-  disabled: "appveyor"
+  disabled: "openbsd"
+  disabled: "freebsd"
+  disabled: "windows"
 """
 
+#[
+disabled: see https://github.com/timotheecour/Nim/issues/528
+]#
+
 import strutils
 from net import TimeoutError
 
 import nativesockets, os, httpclient, asyncdispatch
 
+import std/[assertions, syncio]
+from stdtest/testutils import enableRemoteNetworking
+
 const manualTests = false
 
+proc makeIPv6HttpServer(hostname: string, port: Port,
+    message: string): AsyncFD =
+  let fd = createNativeSocket(AF_INET6)
+  setSockOptInt(fd, SOL_SOCKET, SO_REUSEADDR, 1)
+  var aiList = getAddrInfo(hostname, port, AF_INET6)
+  if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32:
+    freeAddrInfo(aiList)
+    raiseOSError(osLastError())
+  freeAddrInfo(aiList)
+  if listen(fd) != 0:
+    raiseOSError(osLastError())
+  setBlocking(fd, false)
+
+  var serverFd = fd.AsyncFD
+  register(serverFd)
+  result = serverFd
+
+  proc onAccept(fut: Future[AsyncFD]) {.gcsafe.} =
+    if not fut.failed:
+      let clientFd = fut.read()
+      clientFd.send(message).callback = proc() =
+        clientFd.closeSocket()
+      serverFd.accept().callback = onAccept
+  serverFd.accept().callback = onAccept
+
 proc asyncTest() {.async.} =
   var client = newAsyncHttpClient()
-  var resp = await client.request("http://example.com/")
+  var resp = await client.request("http://example.com/", HttpGet)
   doAssert(resp.code.is2xx)
   var body = await resp.body
   body = await resp.body # Test caching
   doAssert("<title>Example Domain</title>" in body)
 
   resp = await client.request("http://example.com/404")
-  doAssert(resp.code.is4xx)
-  doAssert(resp.code == Http404)
-  doAssert(resp.status == Http404)
+  doAssert(resp.code.is4xx or resp.code.is5xx)
+  doAssert(resp.code == Http404 or resp.code == Http500)
+  doAssert(resp.status == $Http404 or resp.status == $Http500)
 
-  resp = await client.request("https://google.com/")
-  doAssert(resp.code.is2xx or resp.code.is3xx)
+  when false: # occasionally does not give success code 
+    resp = await client.request("https://google.com/")
+    doAssert(resp.code.is2xx or resp.code.is3xx)
 
   # getContent
   try:
@@ -46,7 +78,7 @@ proc asyncTest() {.async.} =
     data["output"] = "soap12"
     data["uploaded_file"] = ("test.html", "text/html",
       "<html><head></head><body><p>test</p></body></html>")
-    resp = await client.post("http://validator.w3.org/check", multipart=data)
+    resp = await client.post("http://validator.w3.org/check", multipart = data)
     doAssert(resp.code.is2xx)
 
   # onProgressChanged
@@ -58,6 +90,16 @@ proc asyncTest() {.async.} =
     await client.downloadFile("http://speedtest-ams2.digitalocean.com/100mb.test",
                               "100mb.test")
 
+  # HTTP/1.1 without Content-Length - issue #10726
+  var serverFd = makeIPv6HttpServer("::1", Port(18473),
+     "HTTP/1.1 200 \c\L" &
+     "\c\L" &
+     "Here comes reply")
+  resp = await client.request("http://[::1]:18473/")
+  body = await resp.body
+  doAssert(body == "Here comes reply")
+  serverFd.closeSocket()
+
   client.close()
 
   # Proxy test
@@ -68,17 +110,18 @@ proc asyncTest() {.async.} =
 
 proc syncTest() =
   var client = newHttpClient()
-  var resp = client.request("http://example.com/")
+  var resp = client.request("http://example.com/", HttpGet)
   doAssert(resp.code.is2xx)
   doAssert("<title>Example Domain</title>" in resp.body)
 
   resp = client.request("http://example.com/404")
-  doAssert(resp.code.is4xx)
-  doAssert(resp.code == Http404)
-  doAssert(resp.status == Http404)
+  doAssert(resp.code.is4xx or resp.code.is5xx)
+  doAssert(resp.code == Http404 or resp.code == Http500)
+  doAssert(resp.status == $Http404 or resp.status == $Http500)
 
-  resp = client.request("https://google.com/")
-  doAssert(resp.code.is2xx or resp.code.is3xx)
+  when false: # occasionally does not give success code
+    resp = client.request("https://google.com/")
+    doAssert(resp.code.is2xx or resp.code.is3xx)
 
   # getContent
   try:
@@ -96,7 +139,7 @@ proc syncTest() =
     data["output"] = "soap12"
     data["uploaded_file"] = ("test.html", "text/html",
       "<html><head></head><body><p>test</p></body></html>")
-    resp = client.post("http://validator.w3.org/check", multipart=data)
+    resp = client.post("http://validator.w3.org/check", multipart = data)
     doAssert(resp.code.is2xx)
 
   # onProgressChanged
@@ -110,6 +153,12 @@ proc syncTest() =
 
   client.close()
 
+  # SIGSEGV on HEAD body read: issue #16743
+  block:
+    let client = newHttpClient()
+    let resp = client.head("http://httpbin.org/head")
+    doAssert(resp.body == "")
+
   when false:
     # Disabled for now because it causes troubles with AppVeyor
     # Timeout test.
@@ -122,40 +171,17 @@ proc syncTest() =
     except:
       doAssert false, "TimeoutError should have been raised."
 
-proc makeIPv6HttpServer(hostname: string, port: Port): AsyncFD =
-  let fd = newNativeSocket(AF_INET6)
-  setSockOptInt(fd, SOL_SOCKET, SO_REUSEADDR, 1)
-  var aiList = getAddrInfo(hostname, port, AF_INET6)
-  if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32:
-    freeAddrInfo(aiList)
-    raiseOSError(osLastError())
-  freeAddrInfo(aiList)
-  if listen(fd) != 0:
-    raiseOSError(osLastError())
-  setBlocking(fd, false)
-
-  var serverFd = fd.AsyncFD
-  register(serverFd)
-  result = serverFd
-
-  proc onAccept(fut: Future[AsyncFD]) {.gcsafe.} =
-    if not fut.failed:
-      let clientFd = fut.read()
-      clientFd.send("HTTP/1.1 200 OK\r\LContent-Length: 0\r\LConnection: Closed\r\L\r\L").callback = proc() =
-        clientFd.closeSocket()
-      serverFd.accept().callback = onAccept
-  serverFd.accept().callback = onAccept
-
 proc ipv6Test() =
   var client = newAsyncHttpClient()
-  let serverFd = makeIPv6HttpServer("::1", Port(18473))
+  let serverFd = makeIPv6HttpServer("::1", Port(18473),
+    "HTTP/1.1 200 OK\r\LContent-Length: 0\r\LConnection: Closed\r\L\r\L")
   var resp = waitFor client.request("http://[::1]:18473/")
   doAssert(resp.status == "200 OK")
   serverFd.closeSocket()
   client.close()
 
-syncTest()
-waitFor(asyncTest())
 ipv6Test()
 
-echo "OK"
+when enableRemoteNetworking:
+  syncTest()
+  waitFor(asyncTest())
diff --git a/tests/stdlib/thttpclient_ssl.nim b/tests/stdlib/thttpclient_ssl.nim
new file mode 100644
index 000000000..6b963f029
--- /dev/null
+++ b/tests/stdlib/thttpclient_ssl.nim
@@ -0,0 +1,150 @@
+discard """
+  cmd: "nim $target --mm:refc -d:ssl $options $file"
+  disabled: "openbsd"
+"""
+
+#            Nim - Basic SSL integration tests
+#        (c) Copyright 2018 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+## Warning: this test performs local networking.
+## Test with:
+## ./bin/nim c -d:ssl -p:. --threads:on -r tests/stdlib/thttpclient_ssl.nim
+
+
+from stdtest/testutils import disableSSLTesting
+
+when not defined(windows) and not disableSSLTesting():
+  # Disabled on Windows due to old OpenSSL version
+  import std/[formatfloat, syncio]
+  import
+    httpclient,
+    net,
+    openssl,
+    os,
+    strutils,
+    threadpool,
+    times,
+    unittest
+
+  # bogus self-signed certificate
+  const
+    certFile = "tests/stdlib/thttpclient_ssl_cert.pem"
+    keyFile = "tests/stdlib/thttpclient_ssl_key.pem"
+
+  proc log(msg: string) =
+    when defined(ssldebug):
+      echo "    [" & $epochTime() & "] " & msg
+    # FIXME
+    echo "    [" & $epochTime() & "] " & msg
+    discard
+
+  proc runServer(port: Port): bool {.thread.} =
+    ## Run a trivial HTTPS server in a {.thread.}
+    ## Exit after serving one request
+
+    var socket = newSocket()
+    socket.setSockOpt(OptReusePort, true)
+    socket.bindAddr(port)
+
+    var ctx = newContext(certFile=certFile, keyFile=keyFile)
+
+    ##  Handle one connection
+    socket.listen()
+
+    var client: Socket
+    var address = ""
+
+    log "server: ready"
+    socket.acceptAddr(client, address)
+    log "server: incoming connection"
+
+    var ssl: SslPtr = SSL_new(ctx.context)
+    discard SSL_set_fd(ssl, client.getFd())
+    log "server: accepting connection"
+    ErrClearError()
+    if SSL_accept(ssl) <= 0:
+      ERR_print_errors_fp(stderr)
+    else:
+      const reply = "HTTP/1.0 200 OK\r\nServer: test\r\nContent-type: text/html\r\nContent-Length: 0\r\n\r\n"
+      log "server: sending reply"
+      discard SSL_write(ssl, reply.cstring, reply.len)
+
+    log "server: receiving a line"
+    let line = client.recvLine()
+    log "server: received $# bytes" % $line.len
+    log "closing"
+    SSL_free(ssl)
+    close(client)
+    close(socket)
+    log "server: exited"
+
+
+  suite "SSL self signed certificate check":
+
+    test "TCP socket":
+      const port = 12347.Port
+      let t = spawn runServer(port)
+      sleep(100)
+      var sock = newSocket()
+      var ctx = newContext()
+      ctx.wrapSocket(sock)
+      try:
+        log "client: connect"
+        sock.connect("127.0.0.1", port)
+        fail()
+      except:
+        let msg = getCurrentExceptionMsg()
+        check(msg.contains("certificate verify failed"))
+
+    test "HttpClient default: no check":
+      const port = 12345.Port
+      let t = spawn runServer(port)
+      sleep(100)
+
+      var client = newHttpClient(sslContext=newContext(verifyMode=CVerifyNone))
+      try:
+        log "client: connect"
+        discard client.getContent("https://127.0.0.1:12345")
+      except:
+        let msg = getCurrentExceptionMsg()
+        log "client: unexpected exception: " & msg
+        fail()
+
+    test "HttpClient with CVerifyPeer":
+      const port = 12346.Port
+      let t = spawn runServer(port)
+      sleep(100)
+
+      var client = newHttpClient(sslContext=newContext(verifyMode=CVerifyPeer))
+      try:
+        log "client: connect"
+        discard client.getContent("https://127.0.0.1:12346")
+        log "getContent should have raised an exception"
+        fail()
+      except:
+        let msg = getCurrentExceptionMsg()
+        log "client: exception: " & msg
+        # SSL_shutdown:shutdown while in init
+        if not (msg.contains("alert number 48") or
+          msg.contains("certificate verify failed")):
+          echo "CVerifyPeer exception: " & msg
+          check(false)
+
+    test "HttpClient with CVerifyPeerUseEnvVars":
+      const port = 12346.Port
+      let t = spawn runServer(port)
+      sleep(100)
+
+      putEnv("SSL_CERT_FILE", getCurrentDir() / certFile)
+      var client = newHttpClient(sslContext=newContext(verifyMode=CVerifyPeerUseEnvVars))
+      try:
+        log "client: connect"
+        discard client.getContent("https://127.0.0.1:12346")
+      except:
+        let msg = getCurrentExceptionMsg()
+        log "client: exception: " & msg
+        log "getContent should not have raised an exception"
+        fail()
diff --git a/tests/stdlib/thttpclient_ssl_cert.pem b/tests/stdlib/thttpclient_ssl_cert.pem
new file mode 100644
index 000000000..f15c15c52
--- /dev/null
+++ b/tests/stdlib/thttpclient_ssl_cert.pem
@@ -0,0 +1,29 @@
+-----BEGIN CERTIFICATE-----
+MIIFCTCCAvGgAwIBAgIURYQOmGzeh3Vy7Gk6Go4uAPwcNwAwDQYJKoZIhvcNAQEL
+BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTE5MDEyMzAwMTgzNFoXDTQ2MDYw
+OTAwMTgzNFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF
+AAOCAg8AMIICCgKCAgEAzoEVEl7yqY+RqIagXDD4JB7LyONDvh8aJvBMnJVBgjaL
+JdkfQjvPGUzUkEbU5nc6u7lqFxzEv7hXrssQCB7TwJwfS2PT1Rj14IFlYPyw4DEe
+P1RVS/awurtv3jwumarVl7LR+IQfo59kJ/P8jZt8H3HscDbyhXcHeOWI6q+XlfdV
+mTUJVvABdUuOiIFjgfFVTpo+CKxy7c5caRDK7g1s9xB1/M9PUfJvHY1WrBWFOZf0
+Bl8iwn+ahuxfIVqsFL9leqLykgi1f4L20p7RaAK95TXCo3CszZm4Fsw9zhzkjoU7
+2h0nuYl197LZvRs3u/JJjzZERmsfVPIs5BtO8/MN1MvRn6hIGU5Q3kOVWqWxSkSl
+njrf+uwUdn/24uSCnygNeDuJzwW/2q4N9YI3oovqNIGpkT3FbAm7UKwI4lwhwmqw
+7WH+92ELj0BinmsMMRPD2OqvK+vzLVqwUIQkYug+Hjys6QGXMlrL0krrj7XOKSc3
+SvZa4j0S/Y5CKkw5xuZXxITsdaV6hGi3d/kuT+1ttOSfIIXJXDEiu4pYRfziKU1a
+8EhHMEajEi6ueLw7QmEPVx398erRwiUuP2y43yZ4mwVwvN3i5jlVztl4XsglDmQ+
+hahstVdMMA34K2rK0U8q8YjdYm+z99NmGEPYrS6Qnpr1xrICN83FOWFI0k7ttyMC
+AwEAAaNTMFEwHQYDVR0OBBYEFLqMY6eP3h3gu+ANs77xDBRnElxyMB8GA1UdIwQY
+MBaAFLqMY6eP3h3gu+ANs77xDBRnElxyMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
+hvcNAQELBQADggIBAJS+wyy0r+tVAlCa6V/xxlCDtW9n6L2nsqJXEjME0VvwGs3m
+ima68LyTQJqCSjjxSotaNOYKzUu4vRA3JssV+fUDR+NpmhpRuM74XmO05HUQkp9U
+dBEHyXp2aRQ9LSdvHo5D+RW+J4sHFb3PbU8NPx/t5Dg7il92S2QJQz1jNl+Nezc6
+2O8Vt1YbvWXfqM47URTpnQbWoo38pI44AgAuW3QagucKWsyounmhx65XcdtLn99g
+oZt496pU+hBpYu/IpXuBKNC4FvOrXTWAPkAbbYP39UFyiKwIyTosK+qdbhBlt1xi
+bBPn6N1W9L2BvUwM8fEB/qBuR9UfcMsIYJsWbbXMfyeF6lbaP7xD01rm+yU5PMMI
+Co40abixMntz4J3T2ixdCptf0He1U/UegOHwG1ZGgQzvOG6qI/xkNktDaSA75KR7
+BvPV1CmZC4ovVo1L4STrwnoRz5J49PNOHi9Okj9zJ99H7nsmsK16oxpIYkYHJWn+
+45jpG8SlDp7oev1OGGk/z+ZOTz+LcNxyvsRQVN8w5zNmjCSWiGqz+UUgppCZg8qd
+ECWokNQ5Lr20t1whynrX5bH0l887WPCQmm5VduRoyKFGhCRBSzcCtowSpiwZglUk
+CV0jgFKoteItdzZgsND5I1GaNOxZlnK3wN4H0pgZv7HlW6SP1OYd2Y67waJ7
+-----END CERTIFICATE-----
diff --git a/tests/stdlib/thttpclient_ssl_key.pem b/tests/stdlib/thttpclient_ssl_key.pem
new file mode 100644
index 000000000..6ab04122c
--- /dev/null
+++ b/tests/stdlib/thttpclient_ssl_key.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDOgRUSXvKpj5Go
+hqBcMPgkHsvI40O+Hxom8EyclUGCNosl2R9CO88ZTNSQRtTmdzq7uWoXHMS/uFeu
+yxAIHtPAnB9LY9PVGPXggWVg/LDgMR4/VFVL9rC6u2/ePC6ZqtWXstH4hB+jn2Qn
+8/yNm3wfcexwNvKFdwd45Yjqr5eV91WZNQlW8AF1S46IgWOB8VVOmj4IrHLtzlxp
+EMruDWz3EHX8z09R8m8djVasFYU5l/QGXyLCf5qG7F8hWqwUv2V6ovKSCLV/gvbS
+ntFoAr3lNcKjcKzNmbgWzD3OHOSOhTvaHSe5iXX3stm9Gze78kmPNkRGax9U8izk
+G07z8w3Uy9GfqEgZTlDeQ5VapbFKRKWeOt/67BR2f/bi5IKfKA14O4nPBb/arg31
+gjeii+o0gamRPcVsCbtQrAjiXCHCarDtYf73YQuPQGKeawwxE8PY6q8r6/MtWrBQ
+hCRi6D4ePKzpAZcyWsvSSuuPtc4pJzdK9lriPRL9jkIqTDnG5lfEhOx1pXqEaLd3
++S5P7W205J8ghclcMSK7ilhF/OIpTVrwSEcwRqMSLq54vDtCYQ9XHf3x6tHCJS4/
+bLjfJnibBXC83eLmOVXO2XheyCUOZD6FqGy1V0wwDfgrasrRTyrxiN1ib7P302YY
+Q9itLpCemvXGsgI3zcU5YUjSTu23IwIDAQABAoICAQCdR60/57cUs/dxjs/2R4nH
+IPl/ELEYzeGCRMVlATz6qwZCFmN7c8ghceX32SrwOWEvd2G5Jr0ndIS76YdVV/1Z
+ls8zAV5m0HL8wjDvtKYWqvJps5afm80w+++RKO8pNPcnahgIGsFqQszqrSbux7y6
+ym8VbJQ8WNMFHnWwoXpnyxCT9tQdNgE2UAzIJRwf7SpXCp0yx/1k6CZ0E0ksFGeo
+qQ3kNhUoyegdbvfTazSkD/rZG36C+uM73i36Xm/wAXKN/CuaVC3AZ4QMGNBPUr9F
+IzQSfY/vrCOMoZR1NoZRkmJqlogaBPsnZD34jRFfAYNLIz7PD2m2rhjIx4/Tt4wQ
+5mUwga9ud0ly5wSzswudw07mTYtsLbWrUn6QdFxSwbQ0tXh9PJrqCSJDmYIptuu/
+6zjg8hQLg7y37xMDMCdKtviHx+ndVpW3StTwB/z7lDA6yuYY6nYN0dJTJS3qQheo
+maPG4Xf4FBcD4Is73BjBCf3QR6WIv0ZOG3/GZ1OqLRrPg1u/3UJkpa4LE/6qNUxf
+zdBZSPyQZExBvOqdklEI+1OcqofmWq2n7Amct45buDbFryehEhfJ1HHtkXkTEsut
+azfQeaGem/jKxcTD+1bWs/Q5Nn+QFfKr0NFjXSLoITWQkgQD1qISw3DC72jYXlsm
+S4CmCDW1dHZlmWZq+Mh34QKCAQEA+2JFRa1yYZ0tPt88sOjJYyw9yUxB9Nv9cKrs
+kdkhKHKevF+0BUbRLfp9bod+Wlv66pgQi6ZGKkGD7lCam/3FIBlmmiG8AOoXdoGy
+t17XCzlYy348mnHra2X+JBAN51ivPemdlGZShLbNMkGdL1khtjHL9vSr1KgFn3F/
+8nstVQ9nzHTCK0HWpBGn/EK3dd8lcYZDd7Fcgjz7E3xQDz/XZt0HMwwGaLnQ1L7T
+glIyeNdqLBp4v0NT6L1AAk5rQJONo57AepblwacYhoW9mR5K0bm/BMo5+xwMtYz9
+69ZuMNW8qdaWrzeEsxM1PDbcOoVqChF10w0Ih/MkhKGpN/GxUQKCAQEA0kvWUkEK
+1BBhwGyuKrMnUC3jnQ36KpsjlryMUArdjS2gVBztGW2p2CUWasEgZdxpwQmnqKyz
+4hcZaU/JUleutTI5raxzju4Ve87c+koOiamhw/zaiLCpLn2j0Rh2qxp7QvPPRO0V
+1MN347wjCTx/5/j8WffgWqWfqdrd8JheKal/OHlTZA3DG77FIVnUyov8Np+lTd1x
+NpWr/AOreZlMBq/X/kmWCe+fP901fGdi3cdsKcJcdLPv9KFciSjBlAlaLMnBgLWo
+RrIuNxdH3dRX4rzBSpdNq72n8NaH+A10eoXrlC4eWLo7vRSTe4WRgUAIbhVifnJk
+z4B5FqC/aVgkMwKCAQEAtq983h0lcbDy76z2Ay65I/xDzqU/jX3OGfHtSDS+NxHN
+L+JxBiCn5b0TKJ8JAQu1NoVaCNLGTPEdurQTF+f9OM2c1chMQ3HbqUCqKz6eEscT
+M5dC3Y6KYptVbMnKAOVfPSQoY29U6qOaTbqHS6B/slNQAeFfeoS8yVmHfSVtFVLD
+wT7c2OjY3pUCOn4Vq3CGWpETOMnJC9DbOhbua5aeqF9aWwuTIMpg7CrdtOidS1pp
+CzIVrBF2yj22ZbatlNlmZpD5Gl3NDMWtOh25Yqwz/WP6YLXCGy4QQmP7KEfF/nFl
+0RtkmGNFaYo89sx7kX/hRv3XXZAsMfhOAqElQ8W+cQKCAQAdL/lnIS/njv6CPpNN
+yd/C+RuGSNJX54BhA3pWAawOVC7Ufc9KoDXakgsydeuRN65V5IkomA+/aYVVYIWI
+sDLHY1kuCalgRRsmO+fftTefU7PoB8gtAJf6o+WAt+yAgwRonn4+Csnk5dxV917F
+gWgfQieENSsmaaZnZME5C2zGS4gkxnIUiPRzfV7O6jDmi9dNnYrL69gyw0NDjx7V
+mbk7lFxeJsh0SJXJv2IVCiRms68HfLpoWDENuvek8cssSMADR11cB9p7NW/Epa6L
+01T/W0NYnvdgxsnwW1Yzz2pDNyMjReNgXTi9XYW6tyci0UhaPw2Ujzv+sM4dneHz
+NRCRAoIBAHqXaeC1uTGSzfLvRz81ifgDRP8H9L1HLt7ZWL6XMp1ph+P6yYFXM4JK
+WeP3cdKO/kQOD/fLuhYT92T2hHEadT8CQqpsBMQt29Zlm4oYWHB7ERiZqaGX3/T0
+U1TlL0WxthoHPY2HwA6pmDTmUzDk3tFlgk+XOmLsDacBdC6EsFwA+tyEPVxmkb0J
+H+j7D4NxwysAyWCB9fWU1FV+JJJel+nz88i7Gb8uJ+kSktnFxjv/G9p+OkDYlaUt
+j8lc6LOuNOA9M7XT1BIKpZytnSVtwZWkMmu23OLMM/d07tPJYtHIa92On7XKBPc2
+6THbQsJpR5AalTVvXs3X1RnCLnHiNYg=
+-----END PRIVATE KEY-----
diff --git a/tests/stdlib/thttpclient_standalone.nim b/tests/stdlib/thttpclient_standalone.nim
new file mode 100644
index 000000000..2f432eede
--- /dev/null
+++ b/tests/stdlib/thttpclient_standalone.nim
@@ -0,0 +1,59 @@
+discard """
+  cmd: "nim c --threads:on $file"
+"""
+
+import asynchttpserver, httpclient, asyncdispatch, strutils, net
+
+import std/assertions
+
+block: # bug #16436
+  proc startServer(): AsyncHttpServer =
+    result = newAsyncHttpServer()
+    result.listen(Port(0))
+
+  proc processRequest(server: AsyncHttpServer) {.async.} =
+    proc cb(req: Request) {.async.} =
+      let headers = { "Content-length": "15"} # Provide invalid content-length
+      await req.respond(Http200, "Hello World", headers.newHttpHeaders())
+
+    await server.acceptRequest(cb)
+
+  proc runClient(port: Port) {.async.} =
+    let c = newAsyncHttpClient(headers = {"Connection": "close"}.newHttpHeaders)
+    discard await c.getContent("http://127.0.0.1:" & $port)
+    doAssert false, "should fail earlier"
+
+  let server = startServer()
+  asyncCheck processRequest(server)
+  let port = server.getPort()
+  doAssertRaises(ProtocolError):
+    waitFor runClient(port)
+
+block: # bug #14794 (And test for presence of content-length header when using postContent)
+  proc startServer(): AsyncHttpServer =
+    result = newAsyncHttpServer()
+    result.listen(Port(0))
+
+  proc runServer(server: AsyncHttpServer) {.async.} =
+    proc cb(req: Request) {.async.} =
+      doAssert(req.body.endsWith(httpNewLine), "Multipart body does not end with a newline.")
+      # this next line is probably not required because asynchttpserver does not call
+      # the callback when there is no content-length header.  It instead errors with 
+      # Error: unhandled exception: 411 Length Required
+      # Added for good measure in case the server becomes more permissive.
+      doAssert(req.headers.hasKey("content-length"), "Content-Length header is not present.")
+      asyncCheck req.respond(Http200, "OK")
+
+    await server.acceptRequest(cb)
+
+  proc runClient(port: Port) {.async.} =
+    let c = newAsyncHttpClient()
+    let data = newMultipartData()
+    data.add("file.txt", "This is intended to be an example text file.\r\nThis would be the second line.\r\n")
+    discard await c.postContent("http://127.0.0.1:" & $port, multipart = data)
+    c.close()
+
+  let server = startServer()
+  let port = server.getPort()
+  asyncCheck runServer(server)
+  waitFor runClient(port)
diff --git a/tests/stdlib/thttpcore.nim b/tests/stdlib/thttpcore.nim
index 9f99df93a..93e7d85c6 100644
--- a/tests/stdlib/thttpcore.nim
+++ b/tests/stdlib/thttpcore.nim
@@ -1,28 +1,103 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
 
-import unittest
+import httpcore, strutils
+import std/assertions
 
-import httpcore
+block:
+  block HttpCode:
+    doAssert $Http418 == "418 I'm a teapot"
+    doAssert Http418.is4xx() == true
+    doAssert Http418.is2xx() == false
 
-suite "httpcore":
-
-  test "HttpCode":
-    assert $Http418 == "418 I'm a teapot"
-    assert Http418.is4xx() == true
-    assert Http418.is2xx() == false
-
-  test "headers":
+  block headers:
     var h = newHttpHeaders()
-    assert h.len == 0
+    doAssert h.len == 0
     h.add("Cookie", "foo")
-    assert h.len == 1
-    assert h.hasKey("cooKIE")
-    assert h["Cookie"] == "foo"
-    assert h["cookie"] == "foo"
+    doAssert h.len == 1
+    doAssert h.hasKey("cooKIE")
+    doAssert h["Cookie"] == "foo"
+    doAssert h["cookie"] == "foo"
     h["cookie"] = @["bar", "x"]
-    assert h["Cookie"] == "bar"
-    assert h["Cookie", 1] == "x"
-    assert h["Cookie"].contains("BaR") == true
-    assert h["Cookie"].contains("X") == true
-    assert "baR" in h["cookiE"]
+    doAssert h["Cookie"] == "bar"
+    doAssert h["Cookie", 1] == "x"
+    doAssert h["Cookie"].contains("BaR") == true
+    doAssert h["Cookie"].contains("X") == true
+    doAssert "baR" in h["cookiE"]
     h.del("coOKie")
-    assert h.len == 0
+    doAssert h.len == 0
+
+    # Test that header constructor works with repeated values
+    let h1 = newHttpHeaders({"a": "1", "a": "2", "A": "3"})
+
+    doAssert seq[string](h1["a"]).join(",") == "1,2,3"
+
+  block test_cookies_with_comma:
+    doAssert parseHeader("cookie: foo, bar") ==  ("cookie", @["foo, bar"])
+    doAssert parseHeader("cookie: foo, bar, prologue") == ("cookie", @["foo, bar, prologue"])
+    doAssert parseHeader("cookie: foo, bar, prologue, starlight") == ("cookie", @["foo, bar, prologue, starlight"])
+
+    doAssert parseHeader("cookie:   foo, bar") ==  ("cookie", @["foo, bar"])
+    doAssert parseHeader("cookie:  foo, bar, prologue") == ("cookie", @["foo, bar, prologue"])
+    doAssert parseHeader("cookie:   foo, bar, prologue, starlight") == ("cookie", @["foo, bar, prologue, starlight"])
+
+    doAssert parseHeader("Cookie: foo, bar") == (key: "Cookie", value: @["foo, bar"])
+    doAssert parseHeader("Cookie: foo, bar, prologue") == (key: "Cookie", value: @["foo, bar, prologue"])
+    doAssert parseHeader("Cookie: foo, bar, prologue, starlight") == (key: "Cookie", value: @["foo, bar, prologue, starlight"])
+
+    doAssert parseHeader("Accept: foo, bar") == (key: "Accept", value: @["foo", "bar"])
+    doAssert parseHeader("Accept: foo, bar, prologue") == (key: "Accept", value: @["foo", "bar", "prologue"])
+    doAssert parseHeader("Accept: foo, bar, prologue, starlight") == (key: "Accept", value: @["foo", "bar", "prologue", "starlight"])
+
+  block add_empty_sequence_to_HTTP_headers:
+    block:
+      var headers = newHttpHeaders()
+      headers["empty"] = @[]
+
+      doAssert not headers.hasKey("empty")
+
+    block:
+      var headers = newHttpHeaders()
+      headers["existing"] = "true"
+      headers["existing"] = @[]
+
+      doAssert not headers.hasKey("existing")
+
+    block:
+      var headers = newHttpHeaders()
+      headers["existing"] = @["true"]
+      headers["existing"] = @[]
+
+      doAssert not headers.hasKey("existing")
+
+    block:
+      var headers = newHttpHeaders()
+      headers["existing"] = @[]
+      headers["existing"] = @["true"]
+      doAssert headers.hasKey("existing")
+
+block:
+  var test = newHttpHeaders()
+  test["Connection"] = @["Upgrade", "Close"]
+  doAssert test["Connection", 0] == "Upgrade"
+  doAssert test["Connection", 1] == "Close"
+  test.add("Connection", "Test")
+  doAssert test["Connection", 2] == "Test"
+  doAssert "upgrade" in test["Connection"]
+
+  # Bug #5344.
+  doAssert parseHeader("foobar: ") == ("foobar", @[""])
+  let (key, value) = parseHeader("foobar: ")
+  test = newHttpHeaders()
+  test[key] = value
+  doAssert test["foobar"] == ""
+
+  doAssert parseHeader("foobar:") == ("foobar", @[""])
+
+  block: # test title case
+    var testTitleCase = newHttpHeaders(titleCase=true)
+    testTitleCase.add("content-length", "1")
+    doAssert testTitleCase.hasKey("Content-Length")
+    for key, val in testTitleCase:
+        doAssert key == "Content-Length"
diff --git a/tests/stdlib/timportutils.nim b/tests/stdlib/timportutils.nim
new file mode 100644
index 000000000..672092282
--- /dev/null
+++ b/tests/stdlib/timportutils.nim
@@ -0,0 +1,151 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/[importutils, assertions]
+import stdtest/testutils
+import mimportutils
+
+template main =
+  block: # privateAccess
+    assertAll:
+      var a: A
+      var b = initB() # B is private
+      compiles(a.a0)
+      compiles(b.b0)
+      not compiles(a.ha1)
+      not compiles(b.hb1)
+
+    block:
+      assertAll:
+        privateAccess A
+        compiles(a.ha1)
+        a.ha1 == 0.0
+        not compiles(a.hb1)
+        privateAccess b.typeof
+        b.hb1 = 3
+        type B2 = b.typeof
+        let b2 = B2(b0: 4, hb1: 5)
+        b.hb1 == 3
+        b2 == B2(b0: 4, hb1: 5)
+
+    assertAll:
+      not compiles(a.ha1)
+      not compiles(b.hb1)
+
+    block:
+      assertAll:
+        not compiles(C(c0: 1, hc1: 2))
+        privateAccess C
+        let c = C(c0: 1, hc1: 2)
+        c.hc1 == 2
+
+    block:
+      assertAll:
+        not compiles(E[int](he1: 1))
+        privateAccess E[int]
+        var e = E[int](he1: 1)
+        e.he1 == 1
+        e.he1 = 2
+        e.he1 == 2
+        e.he1 += 3
+        e.he1 == 5
+        # xxx caveat: this currently compiles but in future, we may want
+        # to make `privateAccess E[int]` only affect a specific instantiation;
+        # note that `privateAccess E` does work to cover all instantiations.
+        var e2 = E[float](he1: 1)
+
+    block:
+      assertAll:
+        not compiles(E[int](he1: 1))
+        privateAccess E
+        var e = E[int](he1: 1)
+        e.he1 == 1
+
+    block:
+      assertAll:
+        not compiles(F[int, int](h3: 1))
+        privateAccess F[int, int]
+        var e = F[int, int](h3: 1)
+        e.h3 == 1
+
+    block:
+      assertAll:
+        not compiles(F[int, int](h3: 1))
+        privateAccess F[int, int].default[].typeof
+        var e = F[int, int](h3: 1)
+        e.h3 == 1
+
+    block:
+      assertAll:
+        var a = G[int]()
+        var b = a.addr
+        privateAccess b.type
+        discard b.he1
+        discard b[][].he1
+
+    block:
+      assertAll:
+        privateAccess H[int]
+        var a = H[int](h5: 2)
+
+    block:
+      assertAll:
+        privateAccess PA
+        var pa = PA(a0: 1, ha1: 2)
+        pa.ha1 == 2
+        pa.ha1 = 3
+        pa.ha1 == 3
+
+    block:
+      assertAll:
+        var b = BAalias()
+        not compiles(b.hb1)
+        privateAccess BAalias
+        discard b.hb1
+
+    block:
+      assertAll:
+        var a = A(a0: 1)
+        var a2 = a.addr
+        not compiles(a2.ha1)
+        privateAccess PtA
+        a2.type is PtA
+        a2.ha1 = 2
+        a2.ha1 == 2
+        a.ha1 = 3
+        a2.ha1 == 3
+
+    block:
+      disableVm:
+        assertAll:
+          var a = A.create()
+          defer: dealloc(a)
+          a is PtA
+          a.typeof is PtA
+          not compiles(a.ha1)
+          privateAccess a.typeof
+          a.ha1 = 2
+          a.ha1 == 2
+          a[].ha1 = 3
+          a.ha1 == 3
+
+    block:
+      disableVm:
+        assertAll:
+          var a = A.create()
+          defer: dealloc(a)
+          privateAccess PtA
+          a.ha1 == 0
+
+    block:
+      privateAccess PityRef
+      let x = PityRef[int](a: 1)  # works
+      doAssert x.a == 1
+
+      privateAccess Hope
+      let y = Hope[int](a: 1)
+      doAssert y.a == 1
+
+static: main()
+main()
diff --git a/tests/stdlib/tintsets.nim b/tests/stdlib/tintsets.nim
new file mode 100644
index 000000000..191ef117e
--- /dev/null
+++ b/tests/stdlib/tintsets.nim
@@ -0,0 +1,6 @@
+import ./mintsets
+
+block: # bug https://github.com/nim-lang/Nim/pull/15564#issuecomment-729878104
+  # related to bug #11167
+  test1()
+  test2()
diff --git a/tests/stdlib/tio.nim b/tests/stdlib/tio.nim
index 28e1881e8..80a119763 100644
--- a/tests/stdlib/tio.nim
+++ b/tests/stdlib/tio.nim
@@ -1,40 +1,60 @@
 discard """
-  output: '''9
-b = false
-123456789
-Second readLine raised an exception
-123456789
-'''
+  matrix: "--mm:refc; --mm:orc"
 """
-# bug #5349
-import os
 
-# test the file-IO
-
-const fn = "file9char.txt"
-
-writeFile(fn, "123456789")
-
-var f = open(fn)
-echo getFileSize(f)
-
-var line = newString(10)
-try:
-  let b = readLine(f, line)
-  echo "b = ", b
-except:
-  echo "First readLine raised an exception"
-
-echo line
-
-try:
-  line = readLine(f)
-  let b = readLine(f, line)
-  echo "b = ", b
-except:
-  echo "Second readLine raised an exception"
-
-echo line
-f.close()
-
-removeFile(fn)
+# xxx move to here other tests that belong here; io is a proper module
+
+import std/os
+from stdtest/specialpaths import buildDir
+import std/[assertions, syncio]
+
+block: # readChars
+  let file = buildDir / "D20201118T205105.txt"
+  let s = "he\0l\0lo"
+  writeFile(file, s)
+  defer: removeFile(file)
+  let f = open(file)
+  defer: close(f)
+  let n = f.getFileInfo.blockSize
+  var buf = newString(n)
+  template fn =
+    let n2 = f.readChars(buf)
+    doAssert n2 == s.len
+    doAssert buf[0..<n2] == s
+  fn()
+  setFilePos(f, 0)
+  fn()
+
+  block:
+    setFilePos(f, 0)
+    var s2: string
+    let nSmall = 2
+    for ai in buf.mitems: ai = '\0'
+    var n2s: seq[int]
+    while true:
+      let n2 = f.readChars(toOpenArray(buf, 0, nSmall-1))
+      # xxx: maybe we could support: toOpenArray(buf, 0..nSmall)
+      n2s.add n2
+      s2.add buf[0..<n2]
+      if n2 == 0:
+        break
+    doAssert n2s == @[2,2,2,1,0]
+    doAssert s2 == s
+
+
+import std/strutils
+
+block: # bug #21273
+  let FILE = buildDir / "D20220119T134305.txt"
+
+  let hex = "313632313920313632343720313632353920313632363020313632393020323035363520323037323120323131353020323239393820323331303520323332313020323332343820323332363820"
+
+
+  writeFile FILE, parseHexStr(hex)
+
+  doAssert readFile(FILE).toHex == hex
+
+  let f = open(FILE)
+  var s = newString(80)
+  while f.readLine(s):
+    doAssert s.toHex == hex
diff --git a/tests/stdlib/tisolation.nim b/tests/stdlib/tisolation.nim
new file mode 100644
index 000000000..18b83ea2e
--- /dev/null
+++ b/tests/stdlib/tisolation.nim
@@ -0,0 +1,135 @@
+discard """
+  targets: "c cpp"
+  matrix: "--gc:refc; --gc:orc"
+"""
+
+import std/[isolation, json]
+import std/[assertions, objectdollar]
+
+
+proc main(moveZeroesOut: static bool) =
+  block:
+    type
+      Empty = ref object
+
+
+    var x = isolate(Empty())
+    discard extract(x)
+
+  block: # string literals
+    var data = isolate("string")
+    doAssert data.extract == "string"
+    if moveZeroesOut:
+      doAssert data.extract == ""
+
+  block: # string literals
+    var data = isolate("")
+    doAssert data.extract == ""
+    if moveZeroesOut:
+      doAssert data.extract == ""
+
+  block:
+    var src = "string"
+    var data = isolate(move src)
+    doAssert data.extract == "string"
+    if moveZeroesOut:
+      doAssert src.len == 0
+
+  block: # int literals
+    var data = isolate(1)
+    doAssert data.extract == 1
+    if moveZeroesOut:
+      doAssert data.extract == 0
+
+  block: # float literals
+    var data = isolate(1.6)
+    doAssert data.extract == 1.6
+    if moveZeroesOut:
+      doAssert data.extract == 0.0
+
+  block:
+    var data = isolate(@["1", "2"])
+    doAssert data.extract == @["1", "2"]
+    if moveZeroesOut:
+      doAssert data.extract == @[]
+
+  block:
+    var data = isolate(@["1", "2", "3", "4", "5"])
+    doAssert data.extract == @["1", "2", "3", "4", "5"]
+    if moveZeroesOut:
+      doAssert data.extract == @[]
+
+  block:
+    var data = isolate(@["", ""])
+    doAssert data.extract == @["", ""]
+    if moveZeroesOut:
+      doAssert data.extract == @[]
+
+  block:
+    var src = @["1", "2"]
+    var data = isolate(move src)
+    doAssert data.extract == @["1", "2"]
+    if moveZeroesOut:
+      doAssert src.len == 0
+
+  block:
+    var data = isolate(@[1, 2])
+    doAssert data.extract == @[1, 2]
+    if moveZeroesOut:
+      doAssert data.extract == @[]
+
+  block:
+    var data = isolate(["1", "2"])
+    doAssert data.extract == ["1", "2"]
+    if moveZeroesOut:
+      doAssert data.extract == ["", ""]
+
+  block:
+    var data = isolate([1, 2])
+    doAssert data.extract == [1, 2]
+    if moveZeroesOut:
+      doAssert data.extract == [0, 0]
+
+  block:
+    type
+      Test = object
+        id: int
+
+    var data = isolate(Test(id: 12))
+    doAssert data.extract.id == 12
+
+  block:
+    type
+      Test = object
+        id: int
+
+    var src = Test(id: 12)
+    var data = isolate(src)
+    doAssert data.extract.id == 12
+
+  block:
+    type
+      Test = object
+        id: int
+
+    var src = Test(id: 12)
+    var data = isolate(move src)
+    doAssert data.extract.id == 12
+
+  block:
+    type
+      Test = ref object
+        id: int
+
+    var data = isolate(Test(id: 12))
+    doAssert data.extract.id == 12
+
+  block:
+    var x: seq[Isolated[JsonNode]]
+    x.add isolate(newJString("1234"))
+
+    doAssert $x == """@[(value: "1234")]"""
+
+
+static: main(moveZeroesOut = false)
+main(moveZeroesOut = true)
diff --git a/tests/stdlib/tjsbigints.nim b/tests/stdlib/tjsbigints.nim
new file mode 100644
index 000000000..29b0ac3e7
--- /dev/null
+++ b/tests/stdlib/tjsbigints.nim
@@ -0,0 +1,46 @@
+discard """
+  targets: "js"
+"""
+
+import std/[jsbigints, assertions]
+
+
+let big1: JsBigInt = big"2147483647"
+let big2: JsBigInt = big"666"
+var big3: JsBigInt = big"2"
+
+doAssert big3 == big"2"
+doAssert (big3 xor big2) == big"664"
+doAssert (big"555" and big"2") == big"2"
+doAssert (big"555" or big"2") == big"555"
+doAssert (big1 mod big2) == big"613"
+doAssert -big1 == big"-2147483647"
+doAssert big1 div big2 == big"3224449"
+doAssert big1 + big2 == big"2147484313"
+doAssert big1 - big2 == big"2147482981"
+doAssert big1 shl big3 == big"8589934588"
+doAssert big1 shr big3 == big"536870911"
+doAssert big1 * big2 == big"1430224108902"
+doAssert $big1 == "2147483647n"
+doAssert big1.toCstring(10) == "2147483647".cstring
+doAssert big2 ** big3 == big(443556)
+var huge = big"999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
+huge.inc
+huge = huge + -999999999999999999999999999999999999999999999999999999999999999999999999999999999999999'big
+doAssert huge == big"1"
+var list: seq[JsBigInt]
+for i in big"0" .. big"5":
+  doAssert i is JsBigInt
+  list.add i
+doAssert list == @[big"0", big"1", big"2", big"3", big"4", big"5"]
+list = @[]
+for i in big"0" ..< big"5":
+  doAssert i is JsBigInt
+  list.add i
+doAssert list == @[big"0", big"1", big"2", big"3", big"4"]
+
+block:
+  let b = 2'big
+  doAssert -b ** 3'big == -8'big
+  doAssert -b ** big"2" == big"4" # not -4 because of precedence
+  doAssert -big"3" == big"-3"
diff --git a/tests/stdlib/tjson.nim b/tests/stdlib/tjson.nim
new file mode 100644
index 000000000..e425501f6
--- /dev/null
+++ b/tests/stdlib/tjson.nim
@@ -0,0 +1,382 @@
+discard """
+  matrix: "; --backend:cpp; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on"
+"""
+
+
+#[
+Note: Macro tests are in tests/stdlib/tjsonmacro.nim
+]#
+
+import std/[json,parsejson,strutils]
+import std/private/jsutils
+from std/math import isNaN
+when not defined(js):
+  import std/streams
+import stdtest/testutils
+from std/fenv import epsilon
+import std/[assertions, objectdollar]
+
+proc testRoundtrip[T](t: T, expected: string) =
+  # checks that `T => json => T2 => json2` is such that json2 = json
+  let j = %t
+  doAssert $j == expected, $j
+  doAssert %(j.to(T)) == j
+
+proc testRoundtripVal[T](t: T, expected: string) =
+  # similar to testRoundtrip, but also checks that the `T => json => T2` is such that `T2 == T`
+  # note that this isn't always possible, e.g. for pointer-like types or nans
+  let j = %t
+  doAssert $j == expected, $j
+  let j2 = ($j).parseJson
+  doAssert $j2 == expected, $(j2, t)
+  let t2 = j2.to(T)
+  doAssert t2 == t
+  doAssert $(%* t2) == expected # sanity check, because -0.0 = 0.0 but their json representation differs
+
+let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd", "c": "\ud83c\udf83", "d": "\u00E6"}"""
+# nil passthrough
+doAssert(testJson{"doesnt_exist"}{"anything"}.isNil)
+testJson{["e", "f"]} = %true
+doAssert(testJson["e"]["f"].bval)
+
+# make sure UTF-16 decoding works.
+doAssert(testJson["c"].str == "🎃")
+doAssert(testJson["d"].str == "æ")
+
+# make sure no memory leek when parsing invalid string
+let startMemory = getOccupiedMem()
+for i in 0 .. 10000:
+  try:
+    discard parseJson"""{ invalid"""
+  except:
+    discard
+# memory diff should less than 4M
+doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024)
+
+
+# test `$`
+let stringified = $testJson
+let parsedAgain = parseJson(stringified)
+doAssert(parsedAgain["b"].str == "asd")
+
+parsedAgain["abc"] = %5
+doAssert parsedAgain["abc"].num == 5
+
+# Bounds checking
+when compileOption("boundChecks"):
+  try:
+    let a = testJson["a"][9]
+    doAssert(false, "IndexDefect not thrown")
+  except IndexDefect:
+    discard
+  try:
+    let a = testJson["a"][-1]
+    doAssert(false, "IndexDefect not thrown")
+  except IndexDefect:
+    discard
+  try:
+    doAssert(testJson["a"][0].num == 1, "Index doesn't correspond to its value")
+  except:
+    doAssert(false, "IndexDefect thrown for valid index")
+
+doAssert(testJson{"b"}.getStr() == "asd", "Couldn't fetch a singly nested key with {}")
+doAssert(isNil(testJson{"nonexistent"}), "Non-existent keys should return nil")
+doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil")
+doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil")
+doAssert(testJson{"a"} == parseJson"[1, 2, 3, 4]", "Didn't return a non-JObject when there was one to be found")
+doAssert(isNil(parseJson("[1, 2, 3]"){"foo"}), "Indexing directly into a list should return nil")
+
+# Generator:
+var j = %* [{"name": "John", "age": 30}, {"name": "Susan", "age": 31}]
+doAssert j == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
+
+var j2 = %*
+  [
+    {
+      "name": "John",
+      "age": 30
+    },
+    {
+      "name": "Susan",
+      "age": 31
+    }
+  ]
+doAssert j2 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
+
+var name = "John"
+let herAge = 30
+const hisAge = 31
+
+var j3 = %*
+  [ {"name": "John"
+    , "age": herAge
+    }
+  , {"name": "Susan"
+    , "age": hisAge
+    }
+  ]
+doAssert j3 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
+
+var j4 = %*{"test": nil}
+doAssert j4 == %{"test": newJNull()}
+
+let seqOfNodes = @[%1, %2]
+let jSeqOfNodes = %seqOfNodes
+doAssert(jSeqOfNodes[1].num == 2)
+
+type MyObj = object
+  a, b: int
+  s: string
+  f32: float32
+  f64: float64
+  next: ref MyObj
+var m: MyObj
+m.s = "hi"
+m.a = 5
+let jMyObj = %m
+doAssert(jMyObj["a"].num == 5)
+doAssert(jMyObj["s"].str == "hi")
+
+# Test loading of file.
+when not defined(js):
+  var parsed = parseFile("tests/testdata/jsontest.json")
+
+  try:
+    discard parsed["key2"][12123]
+    doAssert(false)
+  except IndexDefect: doAssert(true)
+
+  var parsed2 = parseFile("tests/testdata/jsontest2.json")
+  doAssert(parsed2{"repository", "description"}.str ==
+      "IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}")
+
+doAssert escapeJsonUnquoted("\10Foo🎃barÄ") == "\\nFoo🎃barÄ"
+doAssert escapeJsonUnquoted("\0\7\20") == "\\u0000\\u0007\\u0014" # for #7887
+doAssert escapeJson("\10Foo🎃barÄ") == "\"\\nFoo🎃barÄ\""
+doAssert escapeJson("\0\7\20") == "\"\\u0000\\u0007\\u0014\"" # for #7887
+
+# Test with extra data
+when not defined(js):
+  try:
+    discard parseJson("123 456")
+    doAssert(false)
+  except JsonParsingError:
+    doAssert getCurrentExceptionMsg().contains(errorMessages[errEofExpected])
+
+  try:
+    discard parseFile("tests/testdata/jsonwithextradata.json")
+    doAssert(false)
+  except JsonParsingError:
+    doAssert getCurrentExceptionMsg().contains(errorMessages[errEofExpected])
+
+# bug #6438
+doAssert($ %*[] == "[]")
+doAssert($ %*{} == "{}")
+
+doAssert(not compiles(%{"error": "No messages"}))
+
+# bug #9111
+block:
+  type
+    Bar = string
+    Foo = object
+      a: int
+      b: Bar
+
+  let
+    js = """{"a": 123, "b": "abc"}""".parseJson
+    foo = js.to Foo
+
+  doAssert(foo.b == "abc")
+
+# Generate constructors for range[T] types
+block:
+  type
+    Q1 = range[0'u8 .. 50'u8]
+    Q2 = range[0'u16 .. 50'u16]
+    Q3 = range[0'u32 .. 50'u32]
+    Q4 = range[0'i8 .. 50'i8]
+    Q5 = range[0'i16 .. 50'i16]
+    Q6 = range[0'i32 .. 50'i32]
+    Q7 = range[0'f32 .. 50'f32]
+    Q8 = range[0'f64 .. 50'f64]
+    Q9 = range[0 .. 50]
+
+    X = object
+      m1: Q1
+      m2: Q2
+      m3: Q3
+      m4: Q4
+      m5: Q5
+      m6: Q6
+      m7: Q7
+      m8: Q8
+      m9: Q9
+
+  let obj = X(
+    m1: Q1(42),
+    m2: Q2(42),
+    m3: Q3(42),
+    m4: Q4(42),
+    m5: Q5(42),
+    m6: Q6(42),
+    m7: Q7(42),
+    m8: Q8(42),
+    m9: Q9(42)
+  )
+
+  doAssert(obj == to(%obj, type(obj)))
+
+  when not defined(js):
+    const fragments = """[1,2,3] {"hi":3} 12 [] """
+    var res = ""
+    for x in parseJsonFragments(newStringStream(fragments)):
+      res.add($x)
+      res.add " "
+    doAssert res == fragments
+
+
+# test isRefSkipDistinct
+type
+  MyRef = ref object
+  MyObject = object
+  MyDistinct = distinct MyRef
+  MyOtherDistinct = distinct MyRef
+
+var x0: ref int
+var x1: MyRef
+var x2: MyObject
+var x3: MyDistinct
+var x4: MyOtherDistinct
+
+doAssert isRefSkipDistinct(x0)
+doAssert isRefSkipDistinct(x1)
+doAssert not isRefSkipDistinct(x2)
+doAssert isRefSkipDistinct(x3)
+doAssert isRefSkipDistinct(x4)
+
+
+doAssert isRefSkipDistinct(ref int)
+doAssert isRefSkipDistinct(MyRef)
+doAssert not isRefSkipDistinct(MyObject)
+doAssert isRefSkipDistinct(MyDistinct)
+doAssert isRefSkipDistinct(MyOtherDistinct)
+
+let x = parseJson("9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999")
+
+doAssert x.kind == JString
+
+block: # bug #15835
+  type
+    Foo = object
+      ii*: int
+      data*: JsonNode
+
+  block:
+    const jt = """{"ii": 123, "data": ["some", "data"]}"""
+    let js = parseJson(jt)
+    discard js.to(Foo)
+
+  block:
+    const jt = """{"ii": 123}"""
+    let js = parseJson(jt)
+    doAssertRaises(KeyError):
+      echo js.to(Foo)
+
+type
+  ContentNodeKind* = enum
+    P,
+    Br,
+    Text,
+  ContentNode* = object
+    case kind*: ContentNodeKind
+    of P: pChildren*: seq[ContentNode]
+    of Br: nil
+    of Text: textStr*: string
+
+let mynode = ContentNode(kind: P, pChildren: @[
+  ContentNode(kind: Text, textStr: "mychild"),
+  ContentNode(kind: Br)
+])
+
+doAssert $mynode == """(kind: P, pChildren: @[(kind: Text, textStr: "mychild"), (kind: Br)])"""
+
+let jsonNode = %*mynode
+doAssert $jsonNode == """{"kind":"P","pChildren":[{"kind":"Text","textStr":"mychild"},{"kind":"Br"}]}"""
+doAssert $jsonNode.to(ContentNode) == """(kind: P, pChildren: @[(kind: Text, textStr: "mychild"), (kind: Br)])"""
+
+block: # bug #17383
+  testRoundtrip(int32.high): "2147483647"
+  testRoundtrip(uint32.high): "4294967295"
+  when int.sizeof == 4:
+    testRoundtrip(int.high): "2147483647"
+    testRoundtrip(uint.high): "4294967295"
+  else:
+    testRoundtrip(int.high): "9223372036854775807"
+    testRoundtrip(uint.high): "18446744073709551615"
+  whenJsNoBigInt64: discard
+  do:
+    testRoundtrip(int64.high): "9223372036854775807"
+    testRoundtrip(uint64.high): "18446744073709551615"
+
+block: # bug #18007
+  testRoundtrip([NaN, Inf, -Inf, 0.0, -0.0, 1.0]): """["nan","inf","-inf",0.0,-0.0,1.0]"""
+  # pending https://github.com/nim-lang/Nim/issues/18025 use:
+  # testRoundtrip([float32(NaN), Inf, -Inf, 0.0, -0.0, 1.0])
+  let inf = float32(Inf)
+  testRoundtrip([float32(NaN), inf, -inf, 0.0, -0.0, 1.0]): """["nan","inf","-inf",0.0,-0.0,1.0]"""
+  when not defined(js): # because of Infinity vs inf
+    testRoundtripVal([inf, -inf, 0.0, -0.0, 1.0]): """["inf","-inf",0.0,-0.0,1.0]"""
+  let a = parseJson($(%NaN)).to(float)
+  doAssert a.isNaN
+
+  whenRuntimeJs: discard # refs bug #18009
+  do:
+    testRoundtripVal(0.0): "0.0"
+    testRoundtripVal(-0.0): "-0.0"
+
+block: # bug #15397, bug #13196
+  testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002"
+  testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568"
+
+block:
+  let a = "18446744073709551615"
+  let b = a.parseJson
+  doAssert b.kind == JString
+  let c = $b
+  when defined(js):
+    doAssert c == "18446744073709552000"
+  else:
+    doAssert c == "18446744073709551615"
+
+block:
+  let a = """
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+    [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+"""
+
+  when not defined(js):
+    try:
+      discard parseJson(a)
+    except JsonParsingError:
+      doAssert getCurrentExceptionMsg().contains("] expected")
diff --git a/tests/stdlib/tjsonexternproc.nim b/tests/stdlib/tjsonexternproc.nim
index ec90e580d..1091d72cd 100644
--- a/tests/stdlib/tjsonexternproc.nim
+++ b/tests/stdlib/tjsonexternproc.nim
@@ -1,5 +1,11 @@
+discard """
+output: '''
+{"data":[1]}
+'''
+"""
+
 # Test case for https://github.com/nim-lang/Nim/issues/6385
 
 import mjsonexternproc
 # import json
-foo(1)
\ No newline at end of file
+foo(1)
diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim
index f13d2e5cb..5a1b4b294 100644
--- a/tests/stdlib/tjsonmacro.nim
+++ b/tests/stdlib/tjsonmacro.nim
@@ -1,28 +1,45 @@
 discard """
-  file: "tjsonmacro.nim"
   output: ""
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c js"
 """
-import json, strutils, options, tables
 
-when isMainModule:
+import json, strutils, options, tables
+import std/assertions
+
+# The definition of the `%` proc needs to be here, since the `% c` calls below
+# can only find our custom `%` proc for `Pix` if defined in global scope.
+type
+  Pix = tuple[x, y: uint8, ch: uint16]
+proc `%`(p: Pix): JsonNode =
+  result = %* { "x" : % p.x,
+                "y" : % p.y,
+                "ch" : % p.ch }
+
+proc testJson() =
   # Tests inspired by own use case (with some additional tests).
   # This should succeed.
   type
     Point[T] = object
       x, y: T
 
-    ReplayEventKind* = enum
+    ReplayEventKind = enum
       FoodAppeared, FoodEaten, DirectionChanged
 
-    ReplayEvent* = object
+    ReplayEvent = object
       time*: float
       case kind*: ReplayEventKind
       of FoodAppeared, FoodEaten:
         foodPos*: Point[float]
+        case subKind*: bool
+        of true:
+          it: int
+        of false:
+          ot: float
       of DirectionChanged:
         playerPos*: float
 
-    Replay* = ref object
+    Replay = ref object
       events*: seq[ReplayEvent]
       test: int
       test2: string
@@ -34,13 +51,15 @@ when isMainModule:
       ReplayEvent(
         time: 1.2345,
         kind: FoodEaten,
-        foodPos: Point[float](x: 5.0, y: 1.0)
+        foodPos: Point[float](x: 5.0, y: 1.0),
+        subKind: true,
+        it: 7
       )
     ],
     test: 18827361,
     test2: "hello world",
     test3: true,
-    testNil: nil
+    testNil: "nil"
   )
 
   let node = %x
@@ -53,7 +72,7 @@ when isMainModule:
   doAssert y.test == 18827361
   doAssert y.test2 == "hello world"
   doAssert y.test3
-  doAssert y.testNil.isNil
+  doAssert y.testNil == "nil"
 
   # Test for custom object variants (without an enum) and with an else branch.
   block:
@@ -91,7 +110,7 @@ when isMainModule:
     doAssert result.other == node["other"].getBiggestInt()
 
   # TODO: Test object variant with set in of branch.
-  # TODO: Should we support heterogenous arrays?
+  # TODO: Should we support heterogeneous arrays?
 
   # Tests that verify the error messages for invalid data.
   block:
@@ -242,7 +261,7 @@ when isMainModule:
         colors: array[2, BirdColor]
 
     var red = BirdColor(name: "red", rgb: [1.0, 0.0, 0.0])
-    var blue = Birdcolor(name: "blue", rgb: [0.0, 0.0, 1.0])
+    var blue = BirdColor(name: "blue", rgb: [0.0, 0.0, 1.0])
     var b = Bird(age: 3, height: 1.734, name: "bardo", colors: [red, blue])
     let jnode = %b
     let data = jnode.to(Bird)
@@ -278,24 +297,26 @@ when isMainModule:
     doAssert parsed.color == Red
 
   block:
-    type
-      Car = object
-        engine: tuple[name: string, capacity: float]
-        model: string
+    when not defined(js):
+      # disable on js because of #12492
+      type
+        Car = object
+          engine: tuple[name: string, capacity: float]
+          model: string
 
-    let j = """
-      {"engine": {"name": "V8", "capacity": 5.5}, "model": "Skyline"}
-    """
+      let j = """
+        {"engine": {"name": "V8", "capacity": 5.5}, "model": "Skyline"}
+      """
 
-    var i = 0
-    proc mulTest: JsonNode =
-      i.inc()
-      return parseJson(j)
+      var i = 0
+      proc mulTest(): JsonNode =
+        inc i
+        return parseJson(j)
 
-    let parsed = mulTest().to(Car)
-    doAssert parsed.engine.name == "V8"
+      let parsed = mulTest().to(Car)
+      doAssert parsed.engine.name == "V8"
 
-    doAssert i == 1
+      doAssert i == 1
 
   block:
     # Option[T] support!
@@ -337,7 +358,7 @@ when isMainModule:
         n2: Option[int]
         n3: Option[string]
         n4: Option[bool]
-        
+
     var j0 = parseJson("""{"n1": 1, "n2": null, "n3": null, "n4": null}""")
     let j0Deser = j0.to(Obj)
     doAssert j0Deser.n1 == 1
@@ -411,10 +432,219 @@ when isMainModule:
     doAssert dataDeser.a == 1
     doAssert dataDeser.f == 6
     doAssert dataDeser.i == 9.9'f32
-  
+
   # deserialize directly into a table
   block:
     let s = """{"a": 1, "b": 2}"""
     let t = parseJson(s).to(Table[string, int])
     doAssert t["a"] == 1
-    doAssert t["b"] == 2
\ No newline at end of file
+    doAssert t["b"] == 2
+
+  block:
+    # bug #8037
+    type
+      Apple = distinct string
+      String = distinct Apple
+      Email = distinct string
+      MyList = distinct seq[int]
+      MyYear = distinct Option[int]
+      MyTable = distinct Table[string, int]
+      MyArr = distinct array[3, float]
+      MyRef = ref object
+        name: string
+      MyObj = object
+        color: int
+      MyDistRef = distinct MyRef
+      MyDistObj = distinct MyObj
+      Toot = object
+        name*: String
+        email*: Email
+        list: MyList
+        year: MyYear
+        dict: MyTable
+        arr: MyArr
+        person: MyDistRef
+        distfruit: MyDistObj
+        dog: MyRef
+        fruit: MyObj
+        emails: seq[String]
+
+    var tJson = parseJson("""
+      {
+        "name":"Bongo",
+        "email":"bongo@bingo.com",
+        "list": [11,7,15],
+        "year": 1975,
+        "dict": {"a": 1, "b": 2},
+        "arr": [1.0, 2.0, 7.0],
+        "person": {"name": "boney"},
+        "dog": {"name": "honey"},
+        "fruit": {"color": 10},
+        "distfruit": {"color": 11},
+        "emails": ["abc", "123"]
+      }
+    """)
+
+    var t = to(tJson, Toot)
+    doAssert string(t.name) == "Bongo"
+    doAssert string(t.email) == "bongo@bingo.com"
+    doAssert seq[int](t.list) == @[11,7,15]
+    doAssert Option[int](t.year).get() == 1975
+    doAssert Table[string,int](t.dict)["a"] == 1
+    doAssert Table[string,int](t.dict)["b"] == 2
+    doAssert array[3, float](t.arr) == [1.0,2.0,7.0]
+
+    doAssert MyRef(t.person).name == "boney"
+    doAssert MyObj(t.distfruit).color == 11
+    doAssert t.dog.name == "honey"
+    doAssert t.fruit.color == 10
+    doAssert seq[string](t.emails) == @["abc", "123"]
+
+    block test_table:
+      var y = parseJson("""{"a": 1, "b": 2, "c": 3}""")
+      var u = y.to(MyTable)
+      var v = y.to(Table[string, int])
+      doAssert Table[string, int](u)["a"] == 1
+      doAssert Table[string, int](u)["b"] == 2
+      doAssert Table[string, int](u)["c"] == 3
+      doAssert v["a"] == 1
+
+    block primitive_string:
+      const kApple = "apple"
+      var u = newJString(kApple)
+      var v = u.to(Email)
+      var w = u.to(Apple)
+      var x = u.to(String)
+      doAssert string(v) == kApple
+      doAssert string(w) == kApple
+      doAssert string(x) == kApple
+
+    block test_option:
+      var u = newJInt(1137)
+      var v = u.to(MyYear)
+      var w = u.to(Option[int])
+      doAssert Option[int](v).get() == 1137
+      doAssert w.get() == 1137
+
+    block test_object:
+      var u = parseJson("""{"color": 987}""")
+      var v = u.to(MyObj)
+      var w = u.to(MyDistObj)
+      doAssert v.color == 987
+      doAssert MyObj(w).color == 987
+
+    block test_ref_object:
+      var u = parseJson("""{"name": "smith"}""")
+      var v = u.to(MyRef)
+      var w = u.to(MyDistRef)
+      doAssert v.name == "smith"
+      doAssert MyRef(w).name == "smith"
+
+  block:
+    # bug #12015
+    type
+      Cluster = object
+        works: tuple[x, y: uint8, ch: uint16] # working
+        fails: Pix # previously broken
+
+    let data = (x: 123'u8, y: 53'u8, ch: 1231'u16)
+    let c = Cluster(works: data, fails: data)
+    let cFromJson = (% c).to(Cluster)
+    doAssert c == cFromJson
+
+  block:
+    # bug related to #12015
+    type
+      PixInt = tuple[x, y, ch: int]
+      SomePix = Pix | PixInt
+      Cluster[T: SomePix] = seq[T]
+      ClusterObject[T: SomePix] = object
+        data: Cluster[T]
+      RecoEvent[T: SomePix] = object
+        cluster: seq[ClusterObject[T]]
+
+    let data = @[(x: 123'u8, y: 53'u8, ch: 1231'u16)]
+    var c = RecoEvent[Pix](cluster: @[ClusterObject[Pix](data: data)])
+    let cFromJson = (% c).to(RecoEvent[Pix])
+    doAssert c == cFromJson
+
+
+  block:
+    # ref objects with cycles.
+    type
+      Misdirection = object
+        cycle: Cycle
+
+      Cycle = ref object
+        foo: string
+        cycle: Misdirection
+
+    let data = """
+      {"cycle": null}
+    """
+
+    let dataParsed = parseJson(data)
+    let dataDeser = to(dataParsed, Misdirection)
+
+  block:
+    # ref object from #12316
+    type
+      Foo = ref Bar
+      Bar = object
+
+    discard "null".parseJson.to Foo
+
+  block:
+    # named array #12289
+    type Vec = array[2, int]
+    let arr = "[1,2]".parseJson.to Vec
+    doAssert arr == [1,2]
+
+  block:
+    # test error message in exception
+
+    type
+      MyType = object
+        otherMember: string
+        member: MySubType
+
+      MySubType = object
+        somethingElse: string
+        list: seq[MyData]
+
+      MyData = object
+        value: int
+
+    let jsonNode = parseJson("""
+      {
+        "otherMember": "otherValue",
+        "member": {
+          "somethingElse": "something",
+          "list": [{"value": 1}, {"value": 2}, {}]
+        }
+      }
+    """)
+
+    try:
+      let tmp = jsonNode.to(MyType)
+      doAssert false, "this should be unreachable"
+    except KeyError:
+      doAssert getCurrentExceptionMsg().contains ".member.list[2].value"
+
+  block:
+    # Enum indexed array test
+    type Test = enum
+      one, two, three, four, five
+    let a = [
+      one: 300,
+      two: 20,
+      three: 10,
+      four: 0,
+      five: -10
+    ]
+    doAssert (%* a).to(a.typeof) == a
+
+
+testJson()
+static:
+  testJson()
diff --git a/tests/stdlib/tjsonmacro_reject.nim b/tests/stdlib/tjsonmacro_reject.nim
index 00506449f..ada365d7d 100644
--- a/tests/stdlib/tjsonmacro_reject.nim
+++ b/tests/stdlib/tjsonmacro_reject.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "Use a named tuple instead of: (string, float)"
   file: "tjsonmacro_reject.nim"
   line: 11
-  errormsg: "Use a named tuple instead of: (string, float)"
 """
 
 import json
@@ -15,4 +15,4 @@ let j = """
   {"engine": {"name": "V8", "capacity": 5.5}, model: "Skyline"}
 """
 let parsed = parseJson(j)
-echo(to(parsed, Car))
\ No newline at end of file
+echo(to(parsed, Car))
diff --git a/tests/stdlib/tjsonmacro_reject2.nim b/tests/stdlib/tjsonmacro_reject2.nim
deleted file mode 100644
index b01153553..000000000
--- a/tests/stdlib/tjsonmacro_reject2.nim
+++ /dev/null
@@ -1,21 +0,0 @@
-discard """
-  file: "tjsonmacro_reject2.nim"
-  line: 10
-  errormsg: "The `to` macro does not support ref objects with cycles."
-"""
-import json
-
-type
-  Misdirection = object
-    cycle: Cycle
-
-  Cycle = ref object
-    foo: string
-    cycle: Misdirection
-
-let data = """
-  {"cycle": null}
-"""
-
-let dataParsed = parseJson(data)
-let dataDeser = to(dataParsed, Cycle)
\ No newline at end of file
diff --git a/tests/stdlib/tjsontestsuite.nim b/tests/stdlib/tjsontestsuite.nim
index 06f783a73..db31963fd 100644
--- a/tests/stdlib/tjsontestsuite.nim
+++ b/tests/stdlib/tjsontestsuite.nim
@@ -1,3 +1,7 @@
+discard """
+disabled: true
+"""
+
 ## JSON tests based on https://github.com/nst/JSONTestSuite
 
 import unittest,
diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim
new file mode 100644
index 000000000..9acf4c9e5
--- /dev/null
+++ b/tests/stdlib/tjsonutils.nim
@@ -0,0 +1,476 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c cpp js"
+"""
+
+import std/jsonutils
+import std/json
+from std/math import isNaN, signbit
+from std/fenv import epsilon
+from stdtest/testutils import whenRuntimeJs
+import std/[assertions, objectdollar]
+
+proc testRoundtrip[T](t: T, expected: string) =
+  # checks that `T => json => T2 => json2` is such that json2 = json
+  let j = t.toJson
+  doAssert $j == expected, "\n" & $j & "\n" & expected
+  doAssert j.jsonTo(T).toJson == j
+  var t2: T
+  t2.fromJson(j)
+  doAssert t2.toJson == j
+
+proc testRoundtripVal[T](t: T, expected: string) =
+  # similar to testRoundtrip, but also checks that the `T => json => T2` is such that `T2 == T`
+  # note that this isn't always possible, e.g. for pointer-like types.
+  let j = t.toJson
+  let j2 = $j
+  doAssert j2 == expected, j2
+  let j3 = j2.parseJson
+  let t2 = j3.jsonTo(T)
+  doAssert t2 == t
+  doAssert $t2.toJson == j2 # still needed, because -0.0 = 0.0 but their json representation differs
+
+import tables, sets, algorithm, sequtils, options, strtabs
+from strutils import contains
+
+type Foo = ref object
+  id: int
+
+proc `==`(a, b: Foo): bool =
+  a.id == b.id
+
+type MyEnum = enum me0, me1 = "me1Alt", me2, me3, me4
+
+proc `$`(a: MyEnum): string =
+  # putting this here pending https://github.com/nim-lang/Nim/issues/13747
+  if a == me2: "me2Modif"
+  else: system.`$`(a)
+
+template fn() =
+  block: # toJson, jsonTo
+    type Foo = distinct float
+    testRoundtrip('x', """120""")
+    when not defined(js):
+      testRoundtrip(cast[pointer](12345)): """12345"""
+      when nimvm:
+        discard
+        # bugs:
+        # Error: unhandled exception: 'intVal' is not accessible using discriminant 'kind' of type 'TNode' [
+        # Error: VM does not support 'cast' from tyNil to tyPointer
+      else:
+        testRoundtrip(pointer(nil)): """0"""
+        testRoundtrip(cast[pointer](nil)): """0"""
+
+    # refs bug #9423
+    testRoundtrip(Foo(1.5)): """1.5"""
+
+  block: # OrderedTable
+    testRoundtrip({"z": "Z", "y": "Y"}.toOrderedTable): """{"z":"Z","y":"Y"}"""
+    doAssert toJson({"z": 10, "": 11}.newTable).`$`.contains """"":11""" # allows hash to change
+    testRoundtrip({"z".cstring: 1, "".cstring: 2}.toOrderedTable): """{"z":1,"":2}"""
+    testRoundtrip({"z": (f1: 'f'), }.toTable): """{"z":{"f1":102}}"""
+
+  block: # StringTable
+    testRoundtrip({"name": "John", "city": "Monaco"}.newStringTable): """{"mode":"modeCaseSensitive","table":{"city":"Monaco","name":"John"}}"""
+
+  block: # complex example
+    let t = {"z": "Z", "y": "Y"}.newStringTable
+    type A = ref object
+      a1: string
+    let a = (1.1, "fo", 'x', @[10,11], [true, false], [t,newStringTable()], [0'i8,3'i8], -4'i16, (foo: 0.5'f32, bar: A(a1: "abc"), bar2: A.default, cstring1: "foo", cstring2: "", cstring3: cstring(nil)))
+    testRoundtrip(a):
+      """[1.1,"fo",120,[10,11],[true,false],[{"mode":"modeCaseSensitive","table":{"y":"Y","z":"Z"}},{"mode":"modeCaseSensitive","table":{}}],[0,3],-4,{"foo":0.5,"bar":{"a1":"abc"},"bar2":null,"cstring1":"foo","cstring2":"","cstring3":null}]"""
+
+  block:
+    # edge case when user defined `==` doesn't handle `nil` well, e.g.:
+    # https://github.com/nim-lang/nimble/blob/63695f490728e3935692c29f3d71944d83bb1e83/src/nimblepkg/version.nim#L105
+    testRoundtrip(@[Foo(id: 10), nil]): """[{"id":10},null]"""
+
+  block: # enum
+    type Foo = enum f1, f2, f3, f4, f5
+    type Bar = enum b1, b2, b3, b4
+    let a = [f2: b2, f3: b3, f4: b4]
+    doAssert b2.ord == 1 # explains the `1`
+    testRoundtrip(a): """[1,2,3]"""
+
+  block: # JsonNode
+    let a = ((1, 2.5, "abc").toJson, (3, 4.5, "foo"))
+    testRoundtripVal(a): """[[1,2.5,"abc"],[3,4.5,"foo"]]"""
+
+    block:
+      template toInt(a): untyped = cast[int](a)
+
+      let a = 3.toJson
+      let b = (a, a)
+
+      let c1 = b.toJson
+      doAssert c1[0].toInt == a.toInt
+      doAssert c1[1].toInt == a.toInt
+
+      let c2 = b.toJson(ToJsonOptions(jsonNodeMode: joptJsonNodeAsCopy))
+      doAssert c2[0].toInt != a.toInt
+      doAssert c2[1].toInt != c2[0].toInt
+      doAssert c2[1] == c2[0]
+
+      let c3 = b.toJson(ToJsonOptions(jsonNodeMode: joptJsonNodeAsObject))
+      doAssert $c3 == """[{"isUnquoted":false,"kind":2,"num":3},{"isUnquoted":false,"kind":2,"num":3}]"""
+
+  block: # ToJsonOptions
+    let a = (me1, me2)
+    doAssert $a.toJson() == "[1,2]"
+    doAssert $a.toJson(ToJsonOptions(enumMode: joptEnumSymbol)) == """["me1","me2"]"""
+    doAssert $a.toJson(ToJsonOptions(enumMode: joptEnumString)) == """["me1Alt","me2Modif"]"""
+
+  block: # set
+    type Foo = enum f1, f2, f3, f4, f5
+    type Goo = enum g1 = 10, g2 = 15, g3 = 17, g4
+    let a = ({f1, f3}, {1'u8, 7'u8}, {'0'..'9'}, {123'u16, 456, 789, 1121, 1122, 1542}, {g2, g3})
+    testRoundtrip(a): """[[0,2],[1,7],[48,49,50,51,52,53,54,55,56,57],[123,456,789,1121,1122,1542],[15,17]]"""
+
+  block: # bug #17383
+    block:
+      let a = (int32.high, uint32.high)
+      testRoundtrip(a): "[2147483647,4294967295]"
+    when int.sizeof > 4:
+      block:
+        let a = (int64.high, uint64.high)
+        testRoundtrip(a): "[9223372036854775807,18446744073709551615]"
+    block:
+      let a = (int.high, uint.high)
+      when int.sizeof == 4:
+        testRoundtrip(a): "[2147483647,4294967295]"
+      else:
+        testRoundtrip(a): "[9223372036854775807,18446744073709551615]"
+
+  block: # bug #18007
+    testRoundtrip((NaN, Inf, -Inf, 0.0, -0.0, 1.0)): """["nan","inf","-inf",0.0,-0.0,1.0]"""
+    testRoundtrip((float32(NaN), Inf, -Inf, 0.0, -0.0, 1.0)): """["nan","inf","-inf",0.0,-0.0,1.0]"""
+    testRoundtripVal((Inf, -Inf, 0.0, -0.0, 1.0)): """["inf","-inf",0.0,-0.0,1.0]"""
+    doAssert ($NaN.toJson).parseJson.jsonTo(float).isNaN
+
+  block: # bug #18009; unfixable unless we change parseJson (which would have overhead),
+         # but at least we can guarantee that the distinction between 0.0 and -0.0 is preserved.
+    let a = (0, 0.0, -0.0, 0.5, 1, 1.0)
+    testRoundtripVal(a): "[0,0.0,-0.0,0.5,1,1.0]"
+    let a2 = $($a.toJson).parseJson
+    whenRuntimeJs:
+      doAssert a2 == "[0,0,-0.0,0.5,1,1]"
+    do:
+      doAssert a2 == "[0,0.0,-0.0,0.5,1,1.0]"
+    let b = a2.parseJson.jsonTo(type(a))
+    doAssert not b[1].signbit
+    doAssert b[2].signbit
+    doAssert not b[3].signbit
+
+  block: # bug #15397, bug #13196
+    let a = 0.1
+    let x = 0.12345678901234567890123456789
+    let b = (a + 0.2, 0.3, x)
+    testRoundtripVal(b): "[0.30000000000000004,0.3,0.12345678901234568]"
+
+    testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568"
+    testRoundtripVal(epsilon(float64)): "2.220446049250313e-16"
+    testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002"
+
+  block: # case object
+    type Foo = object
+      x0: float
+      case t1: bool
+      of true: z1: int8
+      of false: z2: uint16
+      x1: string
+    testRoundtrip(Foo(t1: true, z1: 5, x1: "bar")): """{"x0":0.0,"t1":true,"z1":5,"x1":"bar"}"""
+    testRoundtrip(Foo(x0: 1.5, t1: false, z2: 6)): """{"x0":1.5,"t1":false,"z2":6,"x1":""}"""
+    type PFoo = ref Foo
+    testRoundtrip(PFoo(x0: 1.5, t1: false, z2: 6)): """{"x0":1.5,"t1":false,"z2":6,"x1":""}"""
+
+  block: # ref case object
+    type Foo = ref object
+      x0: float
+      case t1: bool
+      of true: z1: int8
+      of false: z2: uint16
+      x1: string
+    testRoundtrip(Foo(t1: true, z1: 5, x1: "bar")): """{"x0":0.0,"t1":true,"z1":5,"x1":"bar"}"""
+    testRoundtrip(Foo(x0: 1.5, t1: false, z2: 6)): """{"x0":1.5,"t1":false,"z2":6,"x1":""}"""
+
+  block: # generic case object
+    type Foo[T] = ref object
+      x0: float
+      case t1: bool
+      of true: z1: int8
+      of false: z2: uint16
+      x1: string
+    testRoundtrip(Foo[float](t1: true, z1: 5, x1: "bar")): """{"x0":0.0,"t1":true,"z1":5,"x1":"bar"}"""
+    testRoundtrip(Foo[int](x0: 1.5, t1: false, z2: 6)): """{"x0":1.5,"t1":false,"z2":6,"x1":""}"""
+    # sanity check: nesting inside a tuple
+    testRoundtrip((Foo[int](x0: 1.5, t1: false, z2: 6), "foo")): """[{"x0":1.5,"t1":false,"z2":6,"x1":""},"foo"]"""
+
+  block: # case object: 2 discriminants, `when` branch, range discriminant
+    type Foo[T] = object
+      case t1: bool
+      of true:
+        z1: int8
+      of false:
+        z2: uint16
+      when T is float:
+        case t2: range[0..3]
+        of 0: z3: int8
+        of 2,3: z4: uint16
+        else: discard
+    testRoundtrip(Foo[float](t1: true, z1: 5, t2: 3, z4: 12)): """{"t1":true,"z1":5,"t2":3,"z4":12}"""
+    testRoundtrip(Foo[int](t1: false, z2: 7)): """{"t1":false,"z2":7}"""
+    # pending https://github.com/nim-lang/Nim/issues/14698, test with `type Foo[T] = ref object`
+
+  block: # bug: pass opt params in fromJson
+    type Foo = object
+      a: int
+      b: string
+      c: float
+    type Bar = object
+      foo: Foo
+      boo: string
+    var f: seq[Foo]
+    try:
+      fromJson(f, parseJson """[{"b": "bbb"}]""")
+      doAssert false
+    except ValueError:
+      doAssert true
+    fromJson(f, parseJson """[{"b": "bbb"}]""", Joptions(allowExtraKeys: true, allowMissingKeys: true))
+    doAssert f == @[Foo(a: 0, b: "bbb", c: 0.0)]
+    var b: Bar
+    fromJson(b, parseJson """{"foo": {"b": "bbb"}}""", Joptions(allowExtraKeys: true, allowMissingKeys: true))
+    doAssert b == Bar(foo: Foo(a: 0, b: "bbb", c: 0.0))
+    block: # jsonTo with `opt`
+      let b2 = """{"foo": {"b": "bbb"}}""".parseJson.jsonTo(Bar,  Joptions(allowExtraKeys: true, allowMissingKeys: true))
+      doAssert b2 == Bar(foo: Foo(a: 0, b: "bbb", c: 0.0))
+
+  block testHashSet:
+    testRoundtrip(HashSet[string]()): "[]"
+    testRoundtrip([""].toHashSet): """[""]"""
+    testRoundtrip(["one"].toHashSet): """["one"]"""
+
+    var s: HashSet[string]
+    fromJson(s, parseJson("""["one","two"]"""))
+    doAssert s == ["one", "two"].toHashSet
+
+    let jsonNode = toJson(s)
+    doAssert jsonNode.elems.mapIt(it.str).sorted == @["one", "two"]
+
+  block testOrderedSet:
+    testRoundtrip(["one", "two", "three"].toOrderedSet):
+      """["one","two","three"]"""
+
+  block testOption:
+    testRoundtrip(some("test")): "\"test\""
+    testRoundtrip(none[string]()): "null"
+    testRoundtrip(some(42)): "42"
+    testRoundtrip(none[int]()): "null"
+
+  block testStrtabs:
+    testRoundtrip(newStringTable(modeStyleInsensitive)):
+      """{"mode":"modeStyleInsensitive","table":{}}"""
+
+    testRoundtrip(
+      newStringTable("name", "John", "surname", "Doe", modeCaseSensitive)):
+        """{"mode":"modeCaseSensitive","table":{"name":"John","surname":"Doe"}}"""
+
+  block testJoptions:
+    type
+      AboutLifeUniverseAndEverythingElse = object
+        question: string
+        answer: int
+
+    block testExceptionOnExtraKeys:
+      var guide: AboutLifeUniverseAndEverythingElse
+      let json = parseJson(
+        """{"question":"6*9=?","answer":42,"author":"Douglas Adams"}""")
+      doAssertRaises ValueError, fromJson(guide, json)
+      doAssertRaises ValueError,
+                     fromJson(guide, json, Joptions(allowMissingKeys: true))
+
+      type
+        A = object
+          a1,a2,a3: int
+      var a: A
+      let j = parseJson("""{"a3": 1, "a4": 2}""")
+      doAssertRaises ValueError,
+                     fromJson(a, j, Joptions(allowMissingKeys: true))
+
+    block testExceptionOnMissingKeys:
+      var guide: AboutLifeUniverseAndEverythingElse
+      let json = parseJson("""{"answer":42}""")
+      doAssertRaises ValueError, fromJson(guide, json)
+      doAssertRaises ValueError,
+                     fromJson(guide, json, Joptions(allowExtraKeys: true))
+
+    block testAllowExtraKeys:
+      var guide: AboutLifeUniverseAndEverythingElse
+      let json = parseJson(
+        """{"question":"6*9=?","answer":42,"author":"Douglas Adams"}""")
+      fromJson(guide, json, Joptions(allowExtraKeys: true))
+      doAssert guide == AboutLifeUniverseAndEverythingElse(
+        question: "6*9=?", answer: 42)
+
+      block refObject: #bug 17986
+        type A = ref object
+          case is_a: bool
+          of true:
+            a: int
+          else:
+            b: int
+
+        var a = A()
+        fromJson(a, """{"is_a": true, "a":1, "extra_key": 1}""".parseJson, Joptions(allowExtraKeys: true))
+        doAssert $a[] == "(is_a: true, a: 1)"
+
+    block testAllowMissingKeys:
+      var guide = AboutLifeUniverseAndEverythingElse(
+        question: "6*9=?", answer: 54)
+      let json = parseJson("""{"answer":42}""")
+      fromJson(guide, json, Joptions(allowMissingKeys: true))
+      doAssert guide == AboutLifeUniverseAndEverythingElse(
+        question: "6*9=?", answer: 42)
+
+    block testAllowExtraAndMissingKeys:
+      var guide = AboutLifeUniverseAndEverythingElse(
+        question: "6*9=?", answer: 54)
+      let json = parseJson(
+        """{"answer":42,"author":"Douglas Adams"}""")
+      fromJson(guide, json, Joptions(
+        allowExtraKeys: true, allowMissingKeys: true))
+      doAssert guide == AboutLifeUniverseAndEverythingElse(
+        question: "6*9=?", answer: 42)
+
+    type
+      Foo = object
+        a: array[2, string]
+        case b: bool
+        of false: f: float
+        of true: t: tuple[i: int, s: string]
+        case c: range[0 .. 2]
+        of 0: c0: int
+        of 1: c1: float
+        of 2: c2: string
+
+    block testExceptionOnMissingDiscriminantKey:
+      var foo: Foo
+      let json = parseJson("""{"a":["one","two"]}""")
+      doAssertRaises ValueError, fromJson(foo, json)
+
+    block testDoNotResetMissingFieldsWhenHaveDiscriminantKey:
+      var foo = Foo(a: ["one", "two"], b: true, t: (i: 42, s: "s"),
+                    c: 0, c0: 1)
+      let json = parseJson("""{"b":true,"c":2}""")
+      fromJson(foo, json, Joptions(allowMissingKeys: true))
+      doAssert foo.a == ["one", "two"]
+      doAssert foo.b
+      doAssert foo.t == (i: 42, s: "s")
+      doAssert foo.c == 2
+      doAssert foo.c2 == ""
+
+    block testAllowMissingDiscriminantKeys:
+      var foo: Foo
+      let json = parseJson("""{"a":["one","two"],"c":1,"c1":3.14159}""")
+      fromJson(foo, json, Joptions(allowMissingKeys: true))
+      doAssert foo.a == ["one", "two"]
+      doAssert not foo.b
+      doAssert foo.f == 0.0
+      doAssert foo.c == 1
+      doAssert foo.c1 == 3.14159
+
+    block testExceptionOnWrongDiscirminatBranchInJson:
+      var foo = Foo(b: false, f: 3.14159, c: 0, c0: 42)
+      let json = parseJson("""{"c2": "hello"}""")
+      doAssertRaises ValueError,
+                     fromJson(foo, json, Joptions(allowMissingKeys: true))
+      # Test that the original fields are not reset.
+      doAssert not foo.b
+      doAssert foo.f == 3.14159
+      doAssert foo.c == 0
+      doAssert foo.c0 == 42
+
+    block testNoExceptionOnRightDiscriminantBranchInJson:
+      var foo = Foo(b: false, f: 0, c:1, c1: 0)
+      let json = parseJson("""{"f":2.71828,"c1": 3.14159}""")
+      fromJson(foo, json, Joptions(allowMissingKeys: true))
+      doAssert not foo.b
+      doAssert foo.f == 2.71828
+      doAssert foo.c == 1
+      doAssert foo.c1 == 3.14159
+
+    block testAllowExtraKeysInJsonOnWrongDisciriminatBranch:
+      var foo = Foo(b: false, f: 3.14159, c: 0, c0: 42)
+      let json = parseJson("""{"c2": "hello"}""")
+      fromJson(foo, json, Joptions(allowMissingKeys: true,
+                                   allowExtraKeys: true))
+      # Test that the original fields are not reset.
+      doAssert not foo.b
+      doAssert foo.f == 3.14159
+      doAssert foo.c == 0
+      doAssert foo.c0 == 42
+
+
+    block testInvalidTupleLength:
+      let json = parseJson("[0]")
+      # Should raise ValueError instead of index error
+      doAssertRaises(ValueError):
+        discard json.jsonTo((int, int))
+
+    type
+      InnerEnum = enum
+        A
+        B
+        C
+      InnerObject = object
+        x: string
+        y: InnerEnum
+
+    block testOptionsArePassedWhenDeserialising:
+      let json = parseJson("""{"x": "hello"}""")
+      let inner = json.jsonTo(Option[InnerObject], Joptions(allowMissingKeys: true))
+      doAssert inner.isSome()
+      doAssert inner.get().x == "hello"
+      doAssert inner.get().y == A
+
+    block testOptionsArePassedWhenSerialising:
+      let inner = some InnerObject(x: "hello", y: A)
+      let json = inner.toJson(ToJsonOptions(enumMode: joptEnumSymbol))
+      doAssert $json == """{"x":"hello","y":"A"}"""
+
+    block: # bug #21638
+      type Something = object
+
+      doAssert "{}".parseJson.jsonTo(Something) == Something()
+
+    when false:
+      ## TODO: Implement support for nested variant objects allowing the tests
+      ## bellow to pass.
+      block testNestedVariantObjects:
+        type
+          Variant = object
+            case b: bool
+            of false:
+              case bf: bool
+              of false: bff: int
+              of true: bft: float
+            of true:
+              case bt: bool
+              of false: btf: string
+              of true: btt: char
+
+        testRoundtrip(Variant(b: false, bf: false, bff: 42)):
+          """{"b": false, "bf": false, "bff": 42}"""
+        testRoundtrip(Variant(b: false, bf: true, bft: 3.14159)):
+          """{"b": false, "bf": true, "bft": 3.14159}"""
+        testRoundtrip(Variant(b: true, bt: false, btf: "test")):
+          """{"b": true, "bt": false, "btf": "test"}"""
+        testRoundtrip(Variant(b: true, bt: true, btt: 'c')):
+          """{"b": true, "bt": true, "btt": "c"}"""
+
+        # TODO: Add additional tests with missing and extra JSON keys, both when
+        # allowed and forbidden analogous to the tests for the not nested
+        # variant objects.
+
+static: fn()
+fn()
diff --git a/tests/stdlib/tlists.nim b/tests/stdlib/tlists.nim
index 37e73c53f..5993278c7 100644
--- a/tests/stdlib/tlists.nim
+++ b/tests/stdlib/tlists.nim
@@ -1,66 +1,277 @@
 discard """
-  output: '''true'''
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c js"
 """
 
-import lists
+import std/[lists, sequtils]
+import std/assertions
 
 const
   data = [1, 2, 3, 4, 5, 6]
 
-block SinglyLinkedListTest1:
-  var L: TSinglyLinkedList[int]
-  for d in items(data): L.prepend(d)
-  assert($L == "[6, 5, 4, 3, 2, 1]")
 
-  assert(4 in L)
+template main =
+  block SinglyLinkedListTest1:
+    var L: SinglyLinkedList[int]
+    for d in items(data): L.prepend(d)
+    for d in items(data): L.add(d)
+    doAssert($L == "[6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6]")
 
-block SinglyLinkedListTest2:
-  var L: TSinglyLinkedList[string]
-  for d in items(data): L.prepend($d)
-  assert($L == """["6", "5", "4", "3", "2", "1"]""")
+    doAssert(4 in L)
 
-  assert("4" in L)
+  block SinglyLinkedListTest2:
+    var L: SinglyLinkedList[string]
+    for d in items(data): L.prepend($d)
+    doAssert($L == """["6", "5", "4", "3", "2", "1"]""")
 
+    doAssert("4" in L)
 
-block DoublyLinkedListTest1:
-  var L: TDoublyLinkedList[int]
-  for d in items(data): L.prepend(d)
-  for d in items(data): L.append(d)
-  L.remove(L.find(1))
-  assert($L == "[6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6]")
 
-  assert(4 in L)
+  block DoublyLinkedListTest1:
+    var L: DoublyLinkedList[int]
+    for d in items(data): L.prepend(d)
+    for d in items(data): L.add(d)
+    L.remove(L.find(1))
+    doAssert($L == "[6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6]")
 
-block SinglyLinkedRingTest1:
-  var L: TSinglyLinkedRing[int]
-  L.prepend(4)
-  assert($L == "[4]")
-  L.prepend(4)
+    doAssert(4 in L)
 
-  assert($L == "[4, 4]")
-  assert(4 in L)
+  block SinglyLinkedRingTest1:
+    var L: SinglyLinkedRing[int]
+    L.prepend(4)
+    doAssert($L == "[4]")
+    L.prepend(4)
 
+    doAssert($L == "[4, 4]")
+    doAssert(4 in L)
 
-block DoublyLinkedRingTest1:
-  var L: TDoublyLinkedRing[int]
-  L.prepend(4)
-  assert($L == "[4]")
-  L.prepend(4)
 
-  assert($L == "[4, 4]")
-  assert(4 in L)
+  block DoublyLinkedRingTest1:
+    var L: DoublyLinkedRing[int]
+    L.prepend(4)
+    doAssert($L == "[4]")
+    L.prepend(4)
 
-  L.append(3)
-  L.append(5)
-  assert($L == "[4, 4, 3, 5]")
+    doAssert($L == "[4, 4]")
+    doAssert(4 in L)
 
-  L.remove(L.find(3))
-  L.remove(L.find(5))
-  L.remove(L.find(4))
-  L.remove(L.find(4))
-  assert($L == "[]")
-  assert(4 notin L)
+    L.add(3)
+    L.add(5)
+    doAssert($L == "[4, 4, 3, 5]")
 
+    L.remove(L.find(3))
+    L.remove(L.find(5))
+    L.remove(L.find(4))
+    L.remove(L.find(4))
+    doAssert($L == "[]")
+    doAssert(4 notin L)
 
-echo "true"
+  block tlistsToString:
+    block:
+      var l = initDoublyLinkedList[int]()
+      l.add(1)
+      l.add(2)
+      l.add(3)
+      doAssert $l == "[1, 2, 3]"
+    block:
+      var l = initDoublyLinkedList[string]()
+      l.add("1")
+      l.add("2")
+      l.add("3")
+      doAssert $l == """["1", "2", "3"]"""
+    block:
+      var l = initDoublyLinkedList[char]()
+      l.add('1')
+      l.add('2')
+      l.add('3')
+      doAssert $l == """['1', '2', '3']"""
 
+  # Copied here until it is merged into sequtils
+  template take(a: untyped, max: int): untyped =
+    type T = typeof(block: (for ai in a: ai))
+    var ret: seq[T]
+    var i = 0
+    if max > 0:
+      for ai in a:
+        ret.add ai
+        i.inc
+        if i >= max: break
+    ret
+
+  template testCommon(initList, toList) =
+
+    block: # toSinglyLinkedList, toDoublyLinkedList
+      let l = seq[int].default
+      doAssert l.toList.toSeq == []
+      doAssert [1].toList.toSeq == [1]
+      doAssert [1, 2, 3].toList.toSeq == [1, 2, 3]
+
+    block copy:
+      doAssert array[0, int].default.toList.copy.toSeq == []
+      doAssert [1].toList.copy.toSeq == [1]
+      doAssert [1, 2].toList.copy.toSeq == [1, 2]
+      doAssert [1, 2, 3].toList.copy.toSeq == [1, 2, 3]
+      type Foo = ref object
+        x: int
+      var f0 = Foo(x: 0)
+      let f1 = Foo(x: 1)
+      var a = [f0].toList
+      var b = a.copy
+      b.add f1
+      doAssert a.toSeq == [f0]
+      doAssert b.toSeq == [f0, f1]
+      f0.x = 42
+      doAssert a.head.value.x == 42
+      doAssert b.head.value.x == 42
+
+    block: # add, addMoved
+      block:
+        var
+          l0 = initList[int]()
+          l1 = [1].toList
+          l2 = [2, 3].toList
+          l3 = [4, 5, 6].toList
+        l0.add l3
+        l1.add l3
+        l2.addMoved l3
+        doAssert l0.toSeq == [4, 5, 6]
+        doAssert l1.toSeq == [1, 4, 5, 6]
+        doAssert l2.toSeq == [2, 3, 4, 5, 6]
+        doAssert l3.toSeq == []
+        l2.add l3 # re-adding l3 that was destroyed is now a no-op
+        doAssert l2.toSeq == [2, 3, 4, 5, 6]
+        doAssert l3.toSeq == []
+      block:
+        var
+          l0 = initList[int]()
+          l1 = [1].toList
+          l2 = [2, 3].toList
+          l3 = [4, 5, 6].toList
+        l3.addMoved l0
+        l2.addMoved l1
+        doAssert l3.toSeq == [4, 5, 6]
+        doAssert l2.toSeq == [2, 3, 1]
+        l3.add l0
+        doAssert l3.toSeq == [4, 5, 6]
+      block:
+        var c = [0, 1].toList
+        c.addMoved c
+        doAssert c.take(6) == [0, 1, 0, 1, 0, 1]
+
+    block: # prepend, prependMoved
+      block:
+        var
+          l0 = initList[int]()
+          l1 = [1].toList
+          l2 = [2, 3].toList
+          l3 = [4, 5, 6].toList
+        l0.prepend l3
+        l1.prepend l3
+        doAssert l3.toSeq == [4, 5, 6]
+        l2.prependMoved l3
+        doAssert l0.toSeq == [4, 5, 6]
+        doAssert l1.toSeq == [4, 5, 6, 1]
+        doAssert l2.toSeq == [4, 5, 6, 2, 3]
+        doAssert l3.toSeq == []
+        l2.prepend l3 # re-prepending l3 that was destroyed is now a no-op
+        doAssert l2.toSeq == [4, 5, 6, 2, 3]
+        doAssert l3.toSeq == []
+      block:
+        var
+          l0 = initList[int]()
+          l1 = [1].toList
+          l2 = [2, 3].toList
+          l3 = [4, 5, 6].toList
+        l3.prependMoved l0
+        l2.prependMoved l1
+        doAssert l3.toSeq == [4, 5, 6]
+        doAssert l2.toSeq == [1, 2, 3]
+        l3.prepend l0
+        doAssert l3.toSeq == [4, 5, 6]
+      block:
+        var c = [0, 1].toList
+        c.prependMoved c
+        doAssert c.take(6) == [0, 1, 0, 1, 0, 1]
+
+    block remove:
+      var l = [0, 1, 2, 3].toList
+      let
+        l0 = l.head
+        l1 = l0.next
+        l2 = l1.next
+        l3 = l2.next
+      l.remove l0
+      doAssert l.toSeq == [1, 2, 3]
+      l.remove l2
+      doAssert l.toSeq == [1, 3]
+      l.remove l2
+      doAssert l.toSeq == [1, 3]
+      l.remove l3
+      doAssert l.toSeq == [1]
+      l.remove l1
+      doAssert l.toSeq == []
+      # Cycle preservation
+      var a = [10, 11, 12].toList
+      a.addMoved a
+      doAssert a.take(6) == @[10, 11, 12, 10, 11, 12]
+      a.remove a.head.next
+      doAssert a.take(6) == @[10, 12, 10, 12, 10, 12]
+      a.remove a.head
+      doAssert a.take(6) == @[12, 12, 12, 12, 12, 12]
+
+  testCommon initSinglyLinkedList, toSinglyLinkedList
+  testCommon initDoublyLinkedList, toDoublyLinkedList
+
+  block remove: # return value check
+    var l = [0, 1, 2, 3].toSinglyLinkedList
+    let n = l.head.next.next
+    doAssert l.remove(n) == true
+    doAssert l.toSeq == [0, 1, 3]
+    doAssert l.remove(n) == false
+    doAssert l.toSeq == [0, 1, 3]
+    doAssert l.remove(l.head) == true
+    doAssert l.toSeq == [1, 3]
+    doAssert l.remove(l.head.next) == true
+    doAssert l.toSeq == [1]
+    doAssert l.remove(l.head) == true
+    doAssert l.toSeq == []
+  
+  block issue19297: # add (appends a shallow copy)
+    var a: SinglyLinkedList[int]
+    var b: SinglyLinkedList[int]
+
+    doAssert a.toSeq == @[]
+    a.add(1)
+    doAssert a.toSeq == @[1]
+    a.add(b)
+    doAssert a.toSeq == @[1]
+    a.add(2)
+    doAssert a.toSeq == @[1, 2]
+  
+  block issue19314: # add (appends a shallow copy)
+    var a: DoublyLinkedList[int]
+    var b: DoublyLinkedList[int]
+
+    doAssert a.toSeq == @[]
+    a.add(1)
+    doAssert a.toSeq == @[1]
+    a.add(b)
+    doAssert a.toSeq == @[1]
+    a.add(2)
+    doAssert a.toSeq == @[1, 2]
+
+  block RemoveLastNodeFromSinglyLinkedList:
+    var list = initSinglyLinkedList[string]()
+    let n1 = newSinglyLinkedNode("sonic")
+    let n2 = newSinglyLinkedNode("the")
+    let n3 = newSinglyLinkedNode("tiger")
+    let n4 = newSinglyLinkedNode("hedgehog")
+    list.add(n1)
+    list.add(n2)
+    list.add(n3)
+    list.remove(n3)
+    list.add(n4)
+    doAssert list.toSeq == @["sonic", "the", "hedgehog"]
+
+static: main()
+main()
diff --git a/tests/stdlib/tlocks.nim b/tests/stdlib/tlocks.nim
index e567cfde8..1c5f67119 100644
--- a/tests/stdlib/tlocks.nim
+++ b/tests/stdlib/tlocks.nim
@@ -1,10 +1,11 @@
 discard """
-  output: '''3'''
-  cmd: "nim $target --threads:on $options $file"
+  targets: "c cpp js"
+  matrix: "--mm:refc; --mm:orc"
 """
 
 #bug #6049
 import uselocks
+import std/assertions
 
 var m = createMyType[int]()
-echo $m.use()
+doAssert m.use() == 3
diff --git a/tests/stdlib/tlwip.nim b/tests/stdlib/tlwip.nim
new file mode 100644
index 000000000..fc53be592
--- /dev/null
+++ b/tests/stdlib/tlwip.nim
@@ -0,0 +1,30 @@
+discard """
+  targets: "c"
+  cmd: "nim $target --compileOnly --os:freertos --gc:arc $options $file"
+  disabled: "bsd"
+  disabled: "windows"
+  action: compile
+"""
+
+# Note:
+#   This file tests FreeRTOS/LwIP cross-compilation on UNIX platforms
+#   Windows should run when compiled with esp-idf, however I'm not
+#   sure how to test for only compilation on Windows without running 
+#   a test exe
+# 
+# Note:
+#   disabling *BSDs since they're not playing well with `gcc`
+
+import net
+import asynchttpserver, asyncdispatch
+
+proc cb*(req: Request) {.async.} =
+  await req.respond(Http200, "Hello World")
+
+proc run_http_server*() {.exportc.} =
+  echo "starting http server"
+  var server = newAsyncHttpServer()
+
+  waitFor server.serve(Port(8181), cb)
+
+echo("ok")
diff --git a/tests/stdlib/tmacros.nim b/tests/stdlib/tmacros.nim
new file mode 100644
index 000000000..06a9a9c27
--- /dev/null
+++ b/tests/stdlib/tmacros.nim
@@ -0,0 +1,349 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+#[
+xxx macros tests need to be reorganized to makes sure each API is tested once
+See also:
+  tests/macros/tdumpast.nim for treeRepr + friends
+]#
+
+import std/macros
+import std/assertions
+
+block: # hasArgOfName
+  macro m(u: untyped): untyped =
+    for name in ["s","i","j","k","b","xs","ys"]:
+      doAssert hasArgOfName(params u,name)
+    doAssert not hasArgOfName(params u,"nonexistent")
+
+  proc p(s: string; i,j,k: int; b: bool; xs,ys: seq[int] = @[]) {.m.} = discard
+
+block: # bug #17454
+  proc f(v: NimNode): string {.raises: [].} = $v
+
+block: # unpackVarargs
+  block:
+    proc bar1(a: varargs[int]): string =
+      for ai in a: result.add " " & $ai
+    proc bar2(a: varargs[int]) =
+      let s1 = bar1(a)
+      let s2 = unpackVarargs(bar1, a) # `unpackVarargs` makes no difference here
+      doAssert s1 == s2
+    bar2(1, 2, 3)
+    bar2(1)
+    bar2()
+
+  block:
+    template call1(fun: typed; args: varargs[untyped]): untyped =
+      unpackVarargs(fun, args)
+    template call2(fun: typed; args: varargs[untyped]): untyped =
+      # fun(args) # works except for last case with empty `args`, pending bug #9996
+      when varargsLen(args) > 0: fun(args)
+      else: fun()
+
+    proc fn1(a = 0, b = 1) = discard (a, b)
+
+    call1(fn1)
+    call1(fn1, 10)
+    call1(fn1, 10, 11)
+
+    call2(fn1)
+    call2(fn1, 10)
+    call2(fn1, 10, 11)
+
+  block:
+    template call1(fun: typed; args: varargs[typed]): untyped =
+      unpackVarargs(fun, args)
+    template call2(fun: typed; args: varargs[typed]): untyped =
+      # xxx this would give a confusing error message:
+      # required type for a: varargs[typed] [varargs] but expression '[10]' is of type: varargs[typed] [varargs]
+      when varargsLen(args) > 0: fun(args)
+      else: fun()
+    macro toString(a: varargs[typed, `$`]): string =
+      var msg = genSym(nskVar, "msg")
+      result = newStmtList()
+      result.add quote do:
+        var `msg` = ""
+      for ai in a:
+        result.add quote do: `msg`.add $`ai`
+      result.add quote do: `msg`
+    doAssert call1(toString) == ""
+    doAssert call1(toString, 10) == "10"
+    doAssert call1(toString, 10, 11) == "1011"
+
+block: # SameType
+  type
+    A = int
+    B = distinct int
+    C = object
+    Generic[T, Y] = object
+  macro isSameType(a, b: typed): untyped =
+    newLit(sameType(a, b))
+
+  static:
+    assert Generic[int, int].isSameType(Generic[int, int])
+    assert Generic[A, string].isSameType(Generic[int, string])
+    assert not Generic[A, string].isSameType(Generic[B, string])
+    assert not Generic[int, string].isSameType(Generic[int, int])
+    assert isSameType(int, A)
+    assert isSameType(10, 20)
+    assert isSameType("Hello", "world")
+    assert not isSameType("Hello", cstring"world")
+    assert not isSameType(int, B)
+    assert not isSameType(int, Generic[int, int])
+    assert not isSameType(C, string)
+    assert not isSameType(C, int)
+
+
+  #[
+    # compiler sameType fails for the following, read more in `types.nim`'s `sameTypeAux`.
+    type
+      D[T] = C
+      G[T] = T
+    static:
+      assert isSameType(D[int], C)
+      assert isSameType(D[int], D[float])
+      assert isSameType(G[float](1.0), float(1.0))
+      assert isSameType(float(1.0), G[float](1.0))
+  ]#
+
+  type Tensor[T] = object
+    data: T
+
+  macro testTensorInt(x: typed): untyped =
+    let
+      tensorIntType = getTypeInst(Tensor[int])[1]
+      xTyp = x.getTypeInst
+    
+    newLit(xTyp.sameType(tensorIntType))
+
+  var
+    x: Tensor[int]
+    x1 = Tensor[float]()
+    x2 = Tensor[A]()
+    x3 = Tensor[B]()
+
+  static: 
+    assert testTensorInt(x)
+    assert not testTensorInt(x1)
+    assert testTensorInt(x2)
+    assert not testTensorInt(x3)
+
+block: # extractDocCommentsAndRunnables
+  macro checkRunnables(prc: untyped) =
+    let runnables = prc.body.extractDocCommentsAndRunnables()
+    doAssert runnables[0][0].eqIdent("runnableExamples")
+
+  macro checkComments(comment: static[string], prc: untyped) =
+    let comments = prc.body.extractDocCommentsAndRunnables()
+    doAssert comments[0].strVal == comment
+    
+  proc a() {.checkRunnables.} =
+    runnableExamples: discard
+    discard
+
+  proc b() {.checkRunnables.} =
+    runnableExamples "-d:ssl": discard
+    discard
+    
+  proc c() {.checkComments("Hello world").} =
+    ## Hello world
+
+block: # bug #19020
+  type
+    foo = object
+
+  template typ(T:typedesc) {.pragma.}
+
+  proc bar() {.typ: foo.} = discard
+
+  static:
+    doAssert $bar.getCustomPragmaVal(typ) == "foo"
+  doAssert $bar.getCustomPragmaVal(typ) == "foo"
+
+block hasCustomPragmaGeneric:
+  template examplePragma() {.pragma.}
+  type
+    Foo[T] {.examplePragma.} = object
+      x {.examplePragma.}: T
+  var f: Foo[string]
+  doAssert f.hasCustomPragma(examplePragma)
+  doAssert f.x.hasCustomPragma(examplePragma)
+
+block getCustomPragmaValGeneric:
+  template examplePragma(x: int) {.pragma.}
+  type
+    Foo[T] {.examplePragma(42).} = object
+      x {.examplePragma(25).}: T
+  var f: Foo[string]
+  doAssert f.getCustomPragmaVal(examplePragma) == 42
+  doAssert f.x.getCustomPragmaVal(examplePragma) == 25
+
+block: # bug #21326
+  macro foo(body: untyped): untyped =
+    let a = body.lineInfoObj()
+    let aLit = a.newLit
+    result = quote do:
+      doAssert $`a` == $`aLit`
+
+  foo:
+    let c = 1
+
+  template name(a: LineInfo): untyped =
+    discard a # `aLit` works though
+
+  macro foo3(body: untyped): untyped =
+    let a = body.lineInfoObj()
+    # let ax = newLit(a)
+    result = getAst(name(a))
+
+  foo3:
+    let c = 1
+
+block: # bug #7375
+  macro fails(b: static[bool]): untyped =
+    doAssert b == false
+    result = newStmtList()
+
+  macro foo(): untyped =
+
+    var b = false
+
+    ## Fails
+    result = quote do:
+      fails(`b`)
+
+  foo()
+
+  macro someMacro(): untyped =
+    template tmpl(boolean: bool) =
+      when boolean:
+        discard "it's true!"
+      else:
+        doAssert false
+    result = getAst(tmpl(true))
+
+  someMacro()
+
+block:
+  macro foo(): untyped =
+    result = quote do: `littleEndian`
+
+  doAssert littleEndian == foo()
+
+block:
+  macro eqSym(x, y: untyped): untyped =
+    let eq = $x == $y # Unfortunately eqIdent compares to string.
+    result = quote do: `eq`
+
+  var r, a, b: int
+
+  template fma(result: var int, a, b: int, op: untyped) =
+    # fused multiple-add
+    when eqSym(op, `+=`):
+      discard "+="
+    else:
+      discard "+"
+
+  fma(r, a, b, `+=`)
+
+block:
+  template test(boolArg: bool) =
+    static:
+      doAssert typeof(boolArg) is bool
+    let x: bool = boolArg # compile error here, because boolArg became an int
+
+  macro testWrapped1(boolArg: bool): untyped =
+    # forwarding boolArg directly works
+    result = getAst(test(boolArg))
+
+  macro testWrapped2(boolArg: bool): untyped =
+    # forwarding boolArg via a local variable also works
+    let b = boolArg
+    result = getAst(test(b))
+
+  macro testWrapped3(boolArg: bool): untyped =
+    # but using a literal `true` as a local variable will be converted to int
+    let b = true
+    result = getAst(test(b))
+
+  test(true) # ok
+  testWrapped1(true) # ok
+  testWrapped2(true) # ok
+  testWrapped3(true) 
+
+block:
+  macro foo(): untyped =
+    var s = { 'a', 'b' }
+    quote do:              
+      let t = `s`         
+      doAssert $typeof(t) == "set[char]"
+
+  foo()
+
+block: # bug #9607
+  proc fun1(info:LineInfo): string = "bar"
+  proc fun2(info:int): string = "bar"
+
+  macro echoL(args: varargs[untyped]): untyped =
+    let info = args.lineInfoObj
+    let fun1 = bindSym"fun1"
+    let fun2 = bindSym"fun2"
+
+    # this would work instead
+    # result = newCall(bindSym"fun2", info.line.newLit)
+
+    result = quote do:
+
+      # BUG1: ???(0, 0) Error: internal error: genLiteral: ty is nil
+      `fun1`(`info`)
+
+  macro echoM(args: varargs[untyped]): untyped =
+    let info = args.lineInfoObj
+    let fun1 = bindSym"fun1"
+    let fun2 = bindSym"fun2"
+
+    # this would work instead
+    # result = newCall(bindSym"fun2", info.line.newLit)
+
+    result = quote do:
+
+      # BUG1: ???(0, 0) Error: internal error: genLiteral: ty is nil
+      `fun2`(`info`.line)
+
+
+  doAssert echoL() == "bar"
+  doAssert echoM() == "bar"
+
+block:
+  macro hello[T](x: T): untyped =
+    result = quote do:
+      let m: `T` = `x`
+      discard m
+
+  hello(12)
+
+block:
+  proc hello(x: int, y: typedesc) =
+    discard
+
+  macro main =
+    let x = 12
+    result = quote do:
+      `hello`(12, type(x))
+
+  main()
+
+block: # bug #22947
+  macro bar[N: static int](a: var array[N, int]) =
+    result = quote do:
+      for i in 0 ..< `N`:
+        `a`[i] = i
+
+  func foo[N: static int](a: var array[N, int]) =
+    bar(a)
+
+
+  var a: array[4, int]
+  foo(a)
diff --git a/tests/stdlib/tmarshal.nim b/tests/stdlib/tmarshal.nim
index 38937590f..32991ccc9 100644
--- a/tests/stdlib/tmarshal.nim
+++ b/tests/stdlib/tmarshal.nim
@@ -1,23 +1,24 @@
 discard """
-  output: '''{"age": 12, "bio": "Я Cletus", "blob": [65, 66, 67, 128], "name": "Cletus"}
-true
-true
-alpha 100
-omega 200
-'''
+  matrix: "--mm:orc; --mm:refc"
 """
 
-import marshal
+import std/marshal
+import std/[assertions, objectdollar, streams]
 
-template testit(x) = discard $$to[type(x)]($$x)
+# TODO: add static tests
 
-var x: array[0..4, array[0..4, string]] = [
-  ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
-  ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
-  ["test", "1", "2", "3", "4"]]
-testit(x)
-var test2: tuple[name: string, s: int] = ("tuple test", 56)
-testit(test2)
+proc testit[T](x: T): string = $$to[T]($$x)
+
+template check1 =
+  let test1: array[0..1, array[0..4, string]] = [
+    ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"]]
+  doAssert testit(test1) ==
+    """[["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"]]"""
+  let test2: tuple[name: string, s: int] = ("tuple test", 56)
+  doAssert testit(test2) == """{"Field0": "tuple test", "Field1": 56}"""
+
+static: check1()
+check1()
 
 type
   TE = enum
@@ -48,60 +49,185 @@ proc buildList(): PNode =
   result.prev.next = result
   result.prev.prev = result.next
 
-var test3: TestObj
-test3.test = 42
-test3.test2 = blah
-testit(test3)
+let test3 = TestObj(test: 42, test2: blah)
+doAssert testit(test3) ==
+  """{"test": 42, "asd": 0, "test2": "blah", "help": ""}"""
 
 var test4: ref tuple[a, b: string]
 new(test4)
 test4.a = "ref string test: A"
 test4.b = "ref string test: B"
-testit(test4)
+discard testit(test4) # serialization uses the pointer address, which is not consistent
 
-var test5 = @[(0,1),(2,3),(4,5)]
-testit(test5)
+let test5 = @[(0,1),(2,3),(4,5)]
+doAssert testit(test5) ==
+  """[{"Field0": 0, "Field1": 1}, {"Field0": 2, "Field1": 3}, {"Field0": 4, "Field1": 5}]"""
 
-var test7 = buildList()
-testit(test7)
+let test6: set[char] = {'A'..'Z', '_'}
+doAssert testit(test6) ==
+  """[65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 95]"""
 
-var test6: set[char] = {'A'..'Z', '_'}
-testit(test6)
+let test7 = buildList()
+discard testit(test7) # serialization uses the pointer address, which is not consistent
 
 
 # bug #1352
+block:
+  type
+    Entity = object of RootObj
+      name: string
+
+    Person = object of Entity
+      age: int
+      bio: string
+      blob: string
+
+  let instance1 = Person(name: "Cletus", age: 12,
+                         bio: "Я Cletus",
+                         blob: "ABC\x80")
+  doAssert $$instance1 == """{"age": 12, "bio": "Я Cletus", "blob": [65, 66, 67, 128], "name": "Cletus"}"""
+  doAssert to[Person]($$instance1).bio == instance1.bio
+  doAssert to[Person]($$instance1).blob == instance1.blob
+
+# bug #5757
+block:
+  type
+    Something = object
+      x: string
+      y: int
+
+  let data1 = """{"x": "alpha", "y": 100}"""
+  let data2 = """{"x": "omega", "y": 200}"""
+
+  var r = to[Something](data1)
+  doAssert $r.x & " " & $r.y == "alpha 100"
+  r = to[Something](data2)
+  doAssert $r.x & " " & $r.y == "omega 200"
+
+block:
+  type
+    Foo = object
+      a1: string
+      a2: string
+      a3: seq[string]
+      a4: seq[int]
+      a5: seq[int]
+      a6: seq[int]
+  var foo = Foo(a2: "", a4: @[], a6: @[1])
+  foo.a6.setLen 0
+  doAssert $$foo == """{"a1": "", "a2": "", "a3": [], "a4": [], "a5": [], "a6": []}"""
+  doAssert testit(foo) == """{"a1": "", "a2": "", "a3": [], "a4": [], "a5": [], "a6": []}"""
+
+import std/[options, json]
+
+# bug #15934
+block:
+  let
+    a1 = some(newJNull())
+    a2 = none(JsonNode)
+  doAssert $($$a1).to[:Option[JsonNode]] == "some(null)"
+  doAssert $($$a2).to[:Option[JsonNode]] == "none(JsonNode)"
+  doAssert ($$a1).to[:Option[JsonNode]] == some(newJNull())
+  doAssert ($$a2).to[:Option[JsonNode]] == none(JsonNode)
+
+# bug #15620
+block:
+  let str = """{"numeric": null}"""
+
+  type
+    LegacyEntry = object
+      numeric: string
+
+  let test = to[LegacyEntry](str)
+  doAssert $test == """(numeric: "")"""
+
+block:
+  let str = """{"numeric": null}"""
+
+  type
+    LegacyEntry = object
+      numeric: seq[int]
+
+  var test = to[LegacyEntry](str)
+  doAssert $test == """(numeric: @[])"""
+
+# bug #16022
+block:
+  let p: proc (): string = proc (): string = "hello world"
+  let poc = to[typeof(p)]($$p)
+  doAssert poc() == "hello world"
+
+block:
+  type
+    A {.inheritable.} = object
+    B = object of A
+      f: int
+
+  let a: ref A = new(B)
+  doAssert $$a[] == "{}" # not "{f: 0}"
+
+# bug #16496
+block:
+  type
+    A = ref object
+      data: seq[int]
+
+    B = ref object
+      x: A
+  let o = A(data: @[1, 2, 3, 4])
+  let s1 = @[B(x: o), B(x: o)]
+  let m  = $$ s1
+  let s2 = to[seq[B]](m)
+  doAssert s2[0].x.data == s2[1].x.data
+  doAssert s1[0].x.data == s2[1].x.data
+
+
+block:
+  type
+    Obj = ref object
+      i: int
+      b: bool
+
+  let
+    strm = newStringStream()
+
+  var
+    o = Obj(i: 1, b: false)
+    t1 = @[o, o]
+    t2: seq[Obj]
+
+  doAssert t1[0] == t1[1]
+
+  strm.store(t1)
+  strm.setPosition(0)
+  strm.load(t2)
+  strm.close()
+
+  doAssert t2[0] == t2[1]
+
+
+template checkMarshal(data: typed) =
+  let orig = data
+  let m = $$orig
+
+  let old = to[typeof(orig)](m)
+  doAssert data == old
+
+template main() =
+  type
+    Book = object
+      page: int
+      name: string
+
+  let book = Book(page: 12, name: "persona")
+
+  checkMarshal(486)
+  checkMarshal(3.14)
+  checkMarshal("azure sky")
+  checkMarshal(book)
+  checkMarshal([1, 2, 3])
+  checkMarshal(@[1.5, 2.7, 3.9, 4.2])
+  checkMarshal(@["dream", "is", "possible"])
 
-type
-  Entity = object of RootObj
-    name: string
-
-  Person = object of Entity
-    age: int
-    bio: string
-    blob: string
-
-var instance1 = Person(name: "Cletus", age: 12,
-                       bio: "Я Cletus",
-                       blob: "ABC\x80")
-echo($$instance1)
-echo(to[Person]($$instance1).bio == instance1.bio)
-echo(to[Person]($$instance1).blob == instance1.blob)
-
-# bug 5757
-
-type
-  Something = object
-    x: string
-    y: int
-
-var data1 = """{"x": "alpha", "y": 100}"""
-var data2 = """{"x": "omega", "y": 200}"""
-
-var r = to[Something](data1)
-
-echo r.x, " ", r.y
-
-r = to[Something](data2)
-
-echo r.x, " ", r.y
-
+static: main()
+main()
diff --git a/tests/stdlib/tmarshalsegfault.nim b/tests/stdlib/tmarshalsegfault.nim
new file mode 100644
index 000000000..71f2766c8
--- /dev/null
+++ b/tests/stdlib/tmarshalsegfault.nim
@@ -0,0 +1,54 @@
+# issue #12405
+
+import std/[marshal, streams, times, tables, os, assertions]
+
+type AiredEpisodeState * = ref object
+    airedAt * : DateTime
+    tvShowId * : string
+    seasonNumber * : int
+    number * : int
+    title * : string
+
+type ShowsWatchlistState * = ref object
+    aired * : seq[AiredEpisodeState]
+
+type UiState * = ref object
+    shows: ShowsWatchlistState
+
+# Helpers to marshal and unmarshal
+proc load * ( state : var UiState, file : string ) =
+    var strm = newFileStream( file, fmRead )
+
+    strm.load( state )
+
+    strm.close()
+
+proc store * ( state : UiState, file : string ) =
+    var strm = newFileStream( file, fmWrite )
+
+    strm.store( state )
+
+    strm.close()
+
+# 1. We fill the state initially
+var state : UiState = UiState( shows: ShowsWatchlistState( aired: @[] ) )
+
+# VERY IMPORTANT: For some reason, small numbers (like 2 or 3) don't trigger the bug. Anything above 7 or 8 on my machine triggers though
+for i in 0..30:
+    var episode = AiredEpisodeState( airedAt: now(), tvShowId: "1", seasonNumber: 1, number: 1, title: "string" )
+
+    state.shows.aired.add( episode )
+
+# 2. Store it in a file with the marshal module, and then load it back up
+store( state, "tmarshalsegfault_data" )
+load( state, "tmarshalsegfault_data" )
+removeFile("tmarshalsegfault_data")
+
+# 3. VERY IMPORTANT: Without this line, for some reason, everything works fine
+state.shows.aired[ 0 ] = AiredEpisodeState( airedAt: now(), tvShowId: "1", seasonNumber: 1, number: 1, title: "string" )
+
+# 4. And formatting the airedAt date will now trigger the exception
+for ep in state.shows.aired:
+    let x = $ep.seasonNumber & "x" & $ep.number & " (" & $ep.airedAt & ")"
+    let y = $ep.seasonNumber & "x" & $ep.number & " (" & $ep.airedAt & ")"
+    doAssert x == y
diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim
index 581308a7e..22e5f7d88 100644
--- a/tests/stdlib/tmath.nim
+++ b/tests/stdlib/tmath.nim
@@ -1,82 +1,473 @@
 discard """
-  action: run
-  output: '''[Suite] random int
+  targets: "c cpp js"
+  matrix:"; -d:danger; --mm:refc"
+"""
 
-[Suite] random float
+# xxx: there should be a test with `-d:nimTmathCase2 -d:danger --passc:-ffast-math`,
+# but it requires disabling certain lines with `when not defined(nimTmathCase2)`
 
-[Suite] ^
+import std/math
+import std/assertions
 
-'''
-"""
 
-import math, random, os
-import unittest
-import sets
-
-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()
-    var rand1 = random(1000000)
-    os.sleep(200)
-    randomize()
-    var rand2 = random(1000000)
-    check rand1 != rand2
-
-
-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()
-    var rand1:float = random(1000000.0)
-    os.sleep(200)
-    randomize()
-    var rand2:float = random(1000000.0)
-    check rand1 != rand2
-
-suite "^":
-  test "compiles for valid types":
-    check: compiles(5 ^ 2)
-    check: compiles(5.5 ^ 2)
-    check: compiles(5.5 ^ 2.int8)
-    check: compiles(5.5 ^ 2.uint)
-    check: compiles(5.5 ^ 2.uint8)
-    check: not compiles(5.5 ^ 2.2)
\ No newline at end of file
+# Function for approximate comparison of floats
+proc `==~`(x, y: float): bool = abs(x - y) < 1e-9
+
+
+template main() =
+  block:
+    when not defined(js):
+      # check for no side effect annotation
+      proc mySqrt(num: float): float {.noSideEffect.} =
+        # xxx unused
+        sqrt(num)
+
+      # check gamma function
+      doAssert gamma(5.0) == 24.0 # 4!
+      doAssert almostEqual(gamma(0.5), sqrt(PI))
+      doAssert almostEqual(gamma(-0.5), -2 * sqrt(PI))
+      doAssert lgamma(1.0) == 0.0 # ln(1.0) == 0.0
+      doAssert almostEqual(lgamma(0.5), 0.5 * ln(PI))
+      doAssert erf(6.0) > erf(5.0)
+      doAssert erfc(6.0) < erfc(5.0)
+
+  block: # sgn() tests
+    doAssert sgn(1'i8) == 1
+    doAssert sgn(1'i16) == 1
+    doAssert sgn(1'i32) == 1
+    doAssert sgn(1'i64) == 1
+    doAssert sgn(1'u8) == 1
+    doAssert sgn(1'u16) == 1
+    doAssert sgn(1'u32) == 1
+    doAssert sgn(1'u64) == 1
+    doAssert sgn(-12342.8844'f32) == -1
+    doAssert sgn(123.9834'f64) == 1
+    doAssert sgn(0'i32) == 0
+    doAssert sgn(0'f32) == 0
+    doAssert sgn(-0.0'f64) == 0
+    doAssert sgn(NegInf) == -1
+    doAssert sgn(Inf) == 1
+    doAssert sgn(NaN) == 0
+
+  block: # fac() tests
+    when nimvm: discard
+    else:
+      try:
+        discard fac(-1)
+      except AssertionDefect:
+        discard
+
+    doAssert fac(0) == 1
+    doAssert fac(1) == 1
+    doAssert fac(2) == 2
+    doAssert fac(3) == 6
+    doAssert fac(4) == 24
+    doAssert fac(5) == 120
+
+  block: # floorMod/floorDiv
+    doAssert floorDiv(8, 3) == 2
+    doAssert floorMod(8, 3) == 2
+
+    doAssert floorDiv(8, -3) == -3
+    doAssert floorMod(8, -3) == -1
+
+    doAssert floorDiv(-8, 3) == -3
+    doAssert floorMod(-8, 3) == 1
+
+    doAssert floorDiv(-8, -3) == 2
+    doAssert floorMod(-8, -3) == -2
+
+    doAssert floorMod(8.0, -3.0) == -1.0
+    doAssert floorMod(-8.5, 3.0) == 0.5
+
+  block: # euclDiv/euclMod
+    doAssert euclDiv(8, 3) == 2
+    doAssert euclMod(8, 3) == 2
+
+    doAssert euclDiv(8, -3) == -2
+    doAssert euclMod(8, -3) == 2
+
+    doAssert euclDiv(-8, 3) == -3
+    doAssert euclMod(-8, 3) == 1
+
+    doAssert euclDiv(-8, -3) == 3
+    doAssert euclMod(-8, -3) == 1
+
+    doAssert euclMod(8.0, -3.0) == 2.0
+    doAssert euclMod(-8.5, 3.0) == 0.5
+
+    doAssert euclDiv(9, 3) == 3
+    doAssert euclMod(9, 3) == 0
+
+    doAssert euclDiv(9, -3) == -3
+    doAssert euclMod(9, -3) == 0
+
+    doAssert euclDiv(-9, 3) == -3
+    doAssert euclMod(-9, 3) == 0
+
+    doAssert euclDiv(-9, -3) == 3
+    doAssert euclMod(-9, -3) == 0
+
+  block: # ceilDiv
+    doAssert ceilDiv(8,  3) ==  3
+    doAssert ceilDiv(8,  4) ==  2
+    doAssert ceilDiv(8,  5) ==  2
+    doAssert ceilDiv(11, 3) ==  4
+    doAssert ceilDiv(12, 3) ==  4
+    doAssert ceilDiv(13, 3) ==  5
+    doAssert ceilDiv(41, 7) ==  6
+    doAssert ceilDiv(0,  1) ==  0
+    doAssert ceilDiv(1,  1) ==  1
+    doAssert ceilDiv(1,  2) ==  1
+    doAssert ceilDiv(2,  1) ==  2
+    doAssert ceilDiv(2,  2) ==  1
+    doAssert ceilDiv(0, high(int)) == 0
+    doAssert ceilDiv(1, high(int)) == 1
+    doAssert ceilDiv(0, high(int) - 1) == 0
+    doAssert ceilDiv(1, high(int) - 1) == 1
+    doAssert ceilDiv(high(int) div 2, high(int) div 2 + 1) == 1
+    doAssert ceilDiv(high(int) div 2, high(int) div 2 + 2) == 1
+    doAssert ceilDiv(high(int) div 2 + 1, high(int) div 2) == 2
+    doAssert ceilDiv(high(int) div 2 + 2, high(int) div 2) == 2
+    doAssert ceilDiv(high(int) div 2 + 1, high(int) div 2 + 1) == 1
+    doAssert ceilDiv(high(int), 1) == high(int)
+    doAssert ceilDiv(high(int) - 1, 1) == high(int) - 1
+    doAssert ceilDiv(high(int) - 1, 2) == high(int) div 2
+    doAssert ceilDiv(high(int) - 1, high(int)) == 1
+    doAssert ceilDiv(high(int) - 1, high(int) - 1) == 1
+    doAssert ceilDiv(high(int) - 1, high(int) - 2) == 2
+    doAssert ceilDiv(high(int), high(int)) == 1
+    doAssert ceilDiv(high(int), high(int) - 1) == 2
+    doAssert ceilDiv(255'u8,  1'u8) == 255'u8
+    doAssert ceilDiv(254'u8,  2'u8) == 127'u8
+    when not defined(danger):
+      doAssertRaises(AssertionDefect): discard ceilDiv(41,  0)
+      doAssertRaises(AssertionDefect): discard ceilDiv(41, -1)
+      doAssertRaises(AssertionDefect): discard ceilDiv(-1,  1)
+      doAssertRaises(AssertionDefect): discard ceilDiv(-1, -1)
+      doAssertRaises(AssertionDefect): discard ceilDiv(254'u8, 3'u8)
+      doAssertRaises(AssertionDefect): discard ceilDiv(255'u8, 2'u8)
+
+  block: # splitDecimal() tests
+    doAssert splitDecimal(54.674).intpart == 54.0
+    doAssert splitDecimal(54.674).floatpart ==~ 0.674
+    doAssert splitDecimal(-693.4356).intpart == -693.0
+    doAssert splitDecimal(-693.4356).floatpart ==~ -0.4356
+    doAssert splitDecimal(0.0).intpart == 0.0
+    doAssert splitDecimal(0.0).floatpart == 0.0
+
+  block: # trunc tests for vcc
+    doAssert trunc(-1.1) == -1
+    doAssert trunc(1.1) == 1
+    doAssert trunc(-0.1) == -0
+    doAssert trunc(0.1) == 0
+
+    # special case
+    doAssert classify(trunc(1e1000000)) == fcInf
+    doAssert classify(trunc(-1e1000000)) == fcNegInf
+    when not defined(nimTmathCase2):
+      doAssert classify(trunc(0.0/0.0)) == fcNan
+    doAssert classify(trunc(0.0)) == fcZero
+
+    # trick the compiler to produce signed zero
+    let
+      f_neg_one = -1.0
+      f_zero = 0.0
+      f_nan = f_zero / f_zero
+
+    doAssert classify(trunc(f_neg_one*f_zero)) == fcNegZero
+
+    doAssert trunc(-1.1'f32) == -1
+    doAssert trunc(1.1'f32) == 1
+    doAssert trunc(-0.1'f32) == -0
+    doAssert trunc(0.1'f32) == 0
+    doAssert classify(trunc(1e1000000'f32)) == fcInf
+    doAssert classify(trunc(-1e1000000'f32)) == fcNegInf
+    when not defined(nimTmathCase2):
+      doAssert classify(trunc(f_nan.float32)) == fcNan
+    doAssert classify(trunc(0.0'f32)) == fcZero
+  
+  block: # divmod
+    doAssert divmod(int.high, 1) == (int.high, 0)
+    doAssert divmod(-1073741823, 17) == (-63161283, -12)
+    doAssert divmod(int32.high, 1.int32) == (int32.high, 0.int32)
+    doAssert divmod(1073741823.int32, 5.int32) == (214748364.int32, 3.int32)
+    doAssert divmod(4611686018427387903.int64, 5.int64) == (922337203685477580.int64, 3.int64)
+    when not defined(js) and (not compileOption("panics")) and compileOption("overflowChecks"):
+      when nimvm:
+        discard # cannot catch OverflowDefect here
+      else:
+        doAssertRaises(OverflowDefect, (discard divmod(cint.low, -1.cint)))
+        doAssertRaises(OverflowDefect, (discard divmod(clong.low, -1.clong)))
+        doAssertRaises(OverflowDefect, (discard divmod(clonglong.low, -1.clonglong)))
+        doAssertRaises(DivByZeroDefect, (discard divmod(1, 0)))
+  
+  block: # log
+    doAssert log(4.0, 3.0) ==~ ln(4.0) / ln(3.0)
+    doAssert log2(8.0'f64) == 3.0'f64
+    doAssert log2(4.0'f64) == 2.0'f64
+    doAssert log2(2.0'f64) == 1.0'f64
+    doAssert log2(1.0'f64) == 0.0'f64
+    doAssert classify(log2(0.0'f64)) == fcNegInf
+
+    doAssert log2(8.0'f32) == 3.0'f32
+    doAssert log2(4.0'f32) == 2.0'f32
+    doAssert log2(2.0'f32) == 1.0'f32
+    doAssert log2(1.0'f32) == 0.0'f32
+    doAssert classify(log2(0.0'f32)) == fcNegInf
+
+  block: # cumsum
+    block: # cumsum int seq return
+      let counts = [1, 2, 3, 4]
+      doAssert counts.cumsummed == @[1, 3, 6, 10]
+      let empty: seq[int] = @[]
+      doAssert empty.cumsummed == @[]
+
+    block: # cumsum float seq return
+      let counts = [1.0, 2.0, 3.0, 4.0]
+      doAssert counts.cumsummed == @[1.0, 3.0, 6.0, 10.0]
+      let empty: seq[float] = @[]
+      doAssert empty.cumsummed == @[]
+
+    block: # cumsum int in-place
+      var counts = [1, 2, 3, 4]
+      counts.cumsum
+      doAssert counts == [1, 3, 6, 10]
+      var empty: seq[int] = @[]
+      empty.cumsum
+      doAssert empty == @[]
+
+    block: # cumsum float in-place
+      var counts = [1.0, 2.0, 3.0, 4.0]
+      counts.cumsum
+      doAssert counts == [1.0, 3.0, 6.0, 10.0]
+      var empty: seq[float] = @[]
+      empty.cumsum
+      doAssert empty == @[]
+
+  block: # ^ compiles for valid types
+    doAssert: compiles(5 ^ 2)
+    doAssert: compiles(5.5 ^ 2)
+    doAssert: compiles(5.5 ^ 2.int8)
+    doAssert: compiles(5.5 ^ 2.uint)
+    doAssert: compiles(5.5 ^ 2.uint8)
+    doAssert: not compiles(5.5 ^ 2.2)
+
+  block: # isNaN
+    doAssert NaN.isNaN
+    doAssert not Inf.isNaN
+    doAssert isNaN(Inf - Inf)
+    doAssert not isNaN(0.0)
+    doAssert not isNaN(3.1415926)
+    doAssert not isNaN(0'f32)
+
+  block: # signbit
+    doAssert not signbit(0.0)
+    doAssert signbit(-0.0)
+    doAssert signbit(-0.1)
+    doAssert not signbit(0.1)
+
+    doAssert not signbit(Inf)
+    doAssert signbit(-Inf)
+    doAssert not signbit(NaN)
+
+    let x1 = NaN
+    let x2 = -NaN
+    let x3 = -x1
+
+    doAssert isNaN(x1)
+    doAssert isNaN(x2)
+    doAssert isNaN(x3)
+    doAssert not signbit(x1)
+    doAssert signbit(x2)
+    doAssert signbit(x3)
+
+  block: # copySign
+    doAssert copySign(10.0, 1.0) == 10.0
+    doAssert copySign(10.0, -1.0) == -10.0
+    doAssert copySign(-10.0, -1.0) == -10.0
+    doAssert copySign(-10.0, 1.0) == 10.0
+    doAssert copySign(float(10), -1.0) == -10.0
+
+    doAssert copySign(10.0'f64, 1.0) == 10.0
+    doAssert copySign(10.0'f64, -1.0) == -10.0
+    doAssert copySign(-10.0'f64, -1.0) == -10.0
+    doAssert copySign(-10.0'f64, 1.0) == 10.0
+    doAssert copySign(10'f64, -1.0) == -10.0
+
+    doAssert copySign(10.0'f32, 1.0) == 10.0
+    doAssert copySign(10.0'f32, -1.0) == -10.0
+    doAssert copySign(-10.0'f32, -1.0) == -10.0
+    doAssert copySign(-10.0'f32, 1.0) == 10.0
+    doAssert copySign(10'f32, -1.0) == -10.0
+
+    doAssert copySign(Inf, -1.0) == -Inf
+    doAssert copySign(-Inf, 1.0) == Inf
+    doAssert copySign(Inf, 1.0) == Inf
+    doAssert copySign(-Inf, -1.0) == -Inf
+    doAssert copySign(Inf, 0.0) == Inf
+    doAssert copySign(Inf, -0.0) == -Inf
+    doAssert copySign(-Inf, 0.0) == Inf
+    doAssert copySign(-Inf, -0.0) == -Inf
+    doAssert copySign(1.0, -0.0) == -1.0
+    doAssert copySign(0.0, -0.0) == -0.0
+    doAssert copySign(-1.0, 0.0) == 1.0
+    doAssert copySign(10.0, 0.0) == 10.0
+    doAssert copySign(-1.0, NaN) == 1.0
+    doAssert copySign(10.0, NaN) == 10.0
+
+    doAssert copySign(NaN, NaN).isNaN
+    doAssert copySign(-NaN, NaN).isNaN
+    doAssert copySign(NaN, -NaN).isNaN
+    doAssert copySign(-NaN, -NaN).isNaN
+    doAssert copySign(NaN, 0.0).isNaN
+    doAssert copySign(NaN, -0.0).isNaN
+    doAssert copySign(-NaN, 0.0).isNaN
+    doAssert copySign(-NaN, -0.0).isNaN
+
+    doAssert copySign(-1.0, NaN) == 1.0
+    doAssert copySign(-1.0, -NaN) == -1.0
+    doAssert copySign(1.0, copySign(NaN, -1.0)) == -1.0
+
+  block: # almostEqual
+    doAssert almostEqual(3.141592653589793, 3.1415926535897936)
+    doAssert almostEqual(1.6777215e7'f32, 1.6777216e7'f32)
+    doAssert almostEqual(Inf, Inf)
+    doAssert almostEqual(-Inf, -Inf)
+    doAssert not almostEqual(Inf, -Inf)
+    doAssert not almostEqual(-Inf, Inf)
+    doAssert not almostEqual(Inf, NaN)
+    doAssert not almostEqual(NaN, NaN)
+
+  block: # round
+    block: # Round to 0 decimal places
+      doAssert round(54.652) == 55.0
+      doAssert round(54.352) == 54.0
+      doAssert round(-54.652) == -55.0
+      doAssert round(-54.352) == -54.0
+      doAssert round(0.0) == 0.0
+      doAssert 1 / round(0.0) == Inf
+      doAssert 1 / round(-0.0) == -Inf
+      doAssert round(Inf) == Inf
+      doAssert round(-Inf) == -Inf
+      doAssert round(NaN).isNaN
+      doAssert round(-NaN).isNaN
+      doAssert round(-0.5) == -1.0
+      doAssert round(0.5) == 1.0
+      doAssert round(-1.5) == -2.0
+      doAssert round(1.5) == 2.0
+      doAssert round(-2.5) == -3.0
+      doAssert round(2.5) == 3.0
+      doAssert round(2.5'f32) == 3.0'f32
+      doAssert round(2.5'f64) == 3.0'f64
+
+    block: # func round*[T: float32|float64](x: T, places: int): T
+      doAssert round(54.345, 0) == 54.0
+      template fn(x) =
+        doAssert round(x, 2).almostEqual 54.35
+        doAssert round(x, 2).almostEqual 54.35
+        doAssert round(x, -1).almostEqual 50.0
+        doAssert round(x, -2).almostEqual 100.0
+        doAssert round(x, -3).almostEqual 0.0
+      fn(54.346)
+      fn(54.346'f32)
+
+  block: # abs
+    doAssert 1.0 / abs(-0.0) == Inf
+    doAssert 1.0 / abs(0.0) == Inf
+    doAssert -1.0 / abs(-0.0) == -Inf
+    doAssert -1.0 / abs(0.0) == -Inf
+    doAssert abs(0.0) == 0.0
+    doAssert abs(0.0'f32) == 0.0'f32
+
+    doAssert abs(Inf) == Inf
+    doAssert abs(-Inf) == Inf
+    doAssert abs(NaN).isNaN
+    doAssert abs(-NaN).isNaN
+
+  block: # classify
+    doAssert classify(0.3) == fcNormal
+    doAssert classify(-0.3) == fcNormal
+    doAssert classify(5.0e-324) == fcSubnormal
+    doAssert classify(-5.0e-324) == fcSubnormal
+    doAssert classify(0.0) == fcZero
+    doAssert classify(-0.0) == fcNegZero
+    doAssert classify(NaN) == fcNan
+    doAssert classify(0.3 / 0.0) == fcInf
+    doAssert classify(Inf) == fcInf
+    doAssert classify(-0.3 / 0.0) == fcNegInf
+    doAssert classify(-Inf) == fcNegInf
+
+  block: # sum
+    let empty: seq[int] = @[]
+    doAssert sum(empty) == 0
+    doAssert sum([1, 2, 3, 4]) == 10
+    doAssert sum([-4, 3, 5]) == 4
+
+  block: # prod
+    let empty: seq[int] = @[]
+    doAssert prod(empty) == 1
+    doAssert prod([1, 2, 3, 4]) == 24
+    doAssert prod([-4, 3, 5]) == -60
+    doAssert almostEqual(prod([1.5, 3.4]), 5.1)
+    let x: seq[float] = @[]
+    doAssert prod(x) == 1.0
+
+  block: # clamp range
+    doAssert clamp(10, 1..5) == 5
+    doAssert clamp(3, 1..5) == 3
+    doAssert clamp(5, 1..5) == 5
+    doAssert clamp(42.0, 1.0 .. 3.1415926535) == 3.1415926535
+    doAssert clamp(NaN, 1.0 .. 2.0).isNaN
+    doAssert clamp(-Inf, -Inf .. -1.0) == -Inf
+    type A = enum a0, a1, a2, a3, a4, a5
+    doAssert a1.clamp(a2..a4) == a2
+    doAssert clamp((3, 0), (1, 0) .. (2, 9)) == (2, 9)
+
+  block: # edge cases
+    doAssert sqrt(-4.0).isNaN
+
+    doAssert ln(0.0) == -Inf
+    doAssert ln(-0.0) == -Inf
+    doAssert ln(-12.0).isNaN
+
+    doAssert log10(0.0) == -Inf
+    doAssert log10(-0.0) == -Inf
+    doAssert log10(-12.0).isNaN
+
+    doAssert log2(0.0) == -Inf
+    doAssert log2(-0.0) == -Inf
+    doAssert log2(-12.0).isNaN
+
+    when nimvm: discard
+    else:
+      doAssert frexp(0.0) == (0.0, 0)
+      doAssert frexp(-0.0) == (-0.0, 0)
+      doAssert classify(frexp(-0.0)[0]) == fcNegZero
+
+    when not defined(js):
+      doAssert gamma(0.0) == Inf
+      doAssert gamma(-0.0) == -Inf
+      doAssert gamma(-1.0).isNaN
+
+      doAssert lgamma(0.0) == Inf
+      doAssert lgamma(-0.0) == Inf
+      doAssert lgamma(-1.0) == Inf
+
+static: main()
+main()
+
+when not defined(js) and not defined(danger):
+  block: # bug #21792
+    block:
+      type Digit = 0..9
+      var x = [Digit 4, 7]
+
+      doAssertRaises(RangeDefect):
+        discard sum(x)
+
+    block:
+      var x = [int8 124, 127]
+
+      doAssertRaises(OverflowDefect):
+        discard sum(x)
diff --git a/tests/stdlib/tmath2.nim b/tests/stdlib/tmath2.nim
deleted file mode 100644
index eb0506f5f..000000000
--- a/tests/stdlib/tmath2.nim
+++ /dev/null
@@ -1,85 +0,0 @@
-# tests for the interpreter
-
-proc loops(a: var int) =
-  discard
-  #var
-  #  b: int
-  #b = glob
-  #while b != 0:
-  #  b = b + 1
-  #a = b
-
-proc mymax(a, b: int): int =
-  #loops(result)
-  result = a
-  if b > a: result = b
-
-proc test(a, b: int) =
-  var
-    x, y: int
-  x = 0
-  y = 7
-  if x == a + b * 3 - 7 or
-      x == 8 or
-      x == y and y > -56 and y < 699:
-    y = 0
-  elif y == 78 and x == 0:
-    y = 1
-  elif y == 0 and x == 0:
-    y = 2
-  else:
-    y = 3
-
-type
-  TTokType = enum
-    tkNil, tkType, tkConst, tkVar, tkSymbol, tkIf,
-    tkWhile, tkFor, tkLoop, tkCase, tkLabel, tkGoto
-
-proc testCase(t: TTokType): int =
-  case t
-  of tkNil, tkType, tkConst: result = 0
-  of tkVar: result = 1
-  of tkSymbol: result = 2
-  of tkIf..tkFor: result = 3
-  of tkLoop: result = 56
-  else: result = -1
-  test(0, 9) # test the call
-
-proc TestLoops() =
-  var
-    i, j: int
-
-  while i >= 0:
-    if i mod 3 == 0:
-      break
-    i = i + 1
-    while j == 13:
-      j = 13
-      break
-    break
-
-  while true:
-    break
-
-
-var
-  glob: int
-  a: array[0..5, int]
-
-proc main() =
-  #glob = 0
-  #loops( glob )
-  var
-    res: int
-    s: string
-  #write(stdout, mymax(23, 45))
-  write(stdout, "Hallo! Wie heisst du? ")
-  s = readLine(stdin)
-  # test the case statement
-  case s
-  of "Andreas": write(stdout, "Du bist mein Meister!\n")
-  of "Rumpf": write(stdout, "Du bist in der Familie meines Meisters!\n")
-  else: write(stdout, "ich kenne dich nicht!\n")
-  write(stdout, "Du heisst " & s & "\n")
-
-main()
diff --git a/tests/stdlib/tmemfiles1.nim b/tests/stdlib/tmemfiles1.nim
index 8b66dfcc1..33657256c 100644
--- a/tests/stdlib/tmemfiles1.nim
+++ b/tests/stdlib/tmemfiles1.nim
@@ -1,12 +1,11 @@
-discard """
-  file: "tmemfiles1.nim"
-"""
 import memfiles, os
+import std/syncio
+
 var
   mm: MemFile
   fn = "test.mmap"
 # Create a new file
 mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 20)
 mm.close()
-mm.close()
+# mm.close()
 if fileExists(fn): removeFile(fn)
diff --git a/tests/stdlib/tmemfiles2.nim b/tests/stdlib/tmemfiles2.nim
index d6cfa533a..c79f85ebf 100644
--- a/tests/stdlib/tmemfiles2.nim
+++ b/tests/stdlib/tmemfiles2.nim
@@ -1,9 +1,12 @@
 discard """
-  file: "tmemfiles2.nim"
+  disabled: "Windows"
   output: '''Full read size: 20
 Half read size: 10 Data: Hello'''
 """
 import memfiles, os
+import std/syncio
+
+
 const
   fn = "test.mmap"
 var
@@ -12,14 +15,16 @@ var
 
 if fileExists(fn): removeFile(fn)
 
-# Create a new file, data all zeros
-mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 20)
+# Create a new file, data all zeros, starting at size 10
+mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 10, allowRemap=true)
+mm.resize 20  # resize up to 20
 mm.close()
 
 # read, change
 mm_full = memfiles.open(fn, mode = fmWrite, mappedSize = -1, allowRemap = true)
-echo "Full read size: ",mm_full.size
+let size = mm_full.size
 p = mm_full.mapMem(fmReadWrite, 20, 0)
+echo "Full read size: ", size
 var p2 = cast[cstring](p)
 p2[0] = 'H'
 p2[1] = 'e'
@@ -32,7 +37,7 @@ mm_full.close()
 
 # read half, and verify data change
 mm_half = memfiles.open(fn, mode = fmRead, mappedSize = 10)
-echo "Half read size: ",mm_half.size, " Data: ", cast[cstring](mm_half.mem)
+echo "Half read size: ", mm_half.size, " Data: ", cast[cstring](mm_half.mem)
 mm_half.close()
 
 if fileExists(fn): removeFile(fn)
diff --git a/tests/stdlib/tmemlines.nim b/tests/stdlib/tmemlines.nim
index 19821ea26..98e03b5bb 100644
--- a/tests/stdlib/tmemlines.nim
+++ b/tests/stdlib/tmemlines.nim
@@ -1,5 +1,9 @@
+discard """
+outputsub: ""
+"""
+
 import memfiles
-var inp = memfiles.open("readme.txt")
+var inp = memfiles.open("tests/stdlib/tmemlines.nim")
 for line in lines(inp):
   echo("#" & line & "#")
 close(inp)
diff --git a/tests/stdlib/tmemlinesBuf.nim b/tests/stdlib/tmemlinesBuf.nim
index 21edc2322..7bd89d4f2 100644
--- a/tests/stdlib/tmemlinesBuf.nim
+++ b/tests/stdlib/tmemlinesBuf.nim
@@ -1,6 +1,9 @@
-import memfiles
-var inp = memfiles.open("readme.txt")
-var buffer: TaintedString = ""
+import std/[memfiles, assertions]
+var inp = memfiles.open("tests/stdlib/tmemlinesBuf.nim")
+var buffer: string = ""
+var lineCount = 0
 for line in lines(inp, buffer):
-  echo("#" & line & "#")
+  lineCount += 1
+
 close(inp)
+doAssert lineCount == 9, $lineCount # this file's number of lines
diff --git a/tests/stdlib/tmemmapstreams.nim b/tests/stdlib/tmemmapstreams.nim
index 243574f1a..9cfae62c7 100644
--- a/tests/stdlib/tmemmapstreams.nim
+++ b/tests/stdlib/tmemmapstreams.nim
@@ -1,6 +1,6 @@
 discard """
-  file: "tmemmapstreams.nim"
-  output: '''Created size: 10
+output: '''
+Created size: 10
 Position after writing: 5
 Position after writing one char: 6
 Peeked data: Hello
@@ -12,6 +12,8 @@ Readed line: Hello!
 Position after reading line: 7'''
 """
 import os, streams, memfiles
+import std/syncio
+
 const
   fn = "test.mmapstream"
 var
diff --git a/tests/stdlib/tmemslices.nim b/tests/stdlib/tmemslices.nim
index 951807cc4..c0d6d3960 100644
--- a/tests/stdlib/tmemslices.nim
+++ b/tests/stdlib/tmemslices.nim
@@ -1,5 +1,11 @@
+discard """
+outputsub: "rlwuiadtrnzb"
+"""
+
+# chatever the sub pattern it will find itself
+
 import memfiles
-var inp = memfiles.open("readme.txt")
+var inp = memfiles.open("tests/stdlib/tmemslices.nim")
 for mem in memSlices(inp):
   if mem.size > 3:
     echo("#" & $mem & "#")
diff --git a/tests/stdlib/tmersenne.nim b/tests/stdlib/tmersenne.nim
new file mode 100644
index 000000000..64450a045
--- /dev/null
+++ b/tests/stdlib/tmersenne.nim
@@ -0,0 +1,13 @@
+import std/mersenne
+import std/assertions
+
+template main() =
+  var mt = newMersenneTwister(2525)
+
+  doAssert mt.getNum == 407788156'u32
+  doAssert mt.getNum == 1071751096'u32
+  doAssert mt.getNum == 3805347140'u32
+
+
+static: main()
+main()
diff --git a/tests/stdlib/tmget.nim b/tests/stdlib/tmget.nim
index 5792b6282..f41963f02 100644
--- a/tests/stdlib/tmget.nim
+++ b/tests/stdlib/tmget.nim
@@ -1,20 +1,25 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   output: '''Can't access 6
 10
 11
+2
 Can't access 6
 10
 11
+2
 Can't access 6
 10
 11
+2
 Can't access 6
 10
 11
-Can't access 6
+2
+0
 10
 11
-Can't access 6
+0
 10
 11
 Can't access 6
@@ -40,6 +45,9 @@ block:
   x[5] += 1
   var c = x[5]
   echo c
+  x.mgetOrPut(7).inc
+  x.mgetOrPut(7).inc
+  echo x[7]
 
 block:
   var x = newTable[int, int]()
@@ -52,6 +60,9 @@ block:
   x[5] += 1
   var c = x[5]
   echo c
+  x.mgetOrPut(7).inc
+  x.mgetOrPut(7).inc
+  echo x[7]
 
 block:
   var x = initOrderedTable[int, int]()
@@ -64,6 +75,9 @@ block:
   x[5] += 1
   var c = x[5]
   echo c
+  x.mgetOrPut(7).inc
+  x.mgetOrPut(7).inc
+  echo x[7]
 
 block:
   var x = newOrderedTable[int, int]()
@@ -76,6 +90,9 @@ block:
   x[5] += 1
   var c = x[5]
   echo c
+  x.mgetOrPut(7).inc
+  x.mgetOrPut(7).inc
+  echo x[7]
 
 block:
   var x = initCountTable[int]()
@@ -85,7 +102,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,14 +114,14 @@ block:
   except KeyError:
     echo "Can't access 6"
   echo x[5]
-  x[5] += 1
+  x.inc 5, 1
   var c = x[5]
   echo c
 
 import sets
 
 block:
-  var x = initSet[int]()
+  var x = initHashSet[int]()
   x.incl 5
   try:
     echo x[6]
diff --git a/tests/stdlib/tmimetypes.nim b/tests/stdlib/tmimetypes.nim
new file mode 100644
index 000000000..fd66ebd97
--- /dev/null
+++ b/tests/stdlib/tmimetypes.nim
@@ -0,0 +1,28 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c js"
+"""
+
+import std/mimetypes
+import std/assertions
+
+
+template main() =
+  var m = newMimetypes()
+  doAssert m.getMimetype("mp4") == "video/mp4"
+  doAssert m.getExt("application/json") == "json"
+  doAssert m.getMimetype("json") == "application/json"
+  m.register("foo", "baa")
+  doAssert m.getMimetype("foo") == "baa"
+  doAssert m.getMimetype("txt") == "text/plain"
+  doAssert m.getExt("text/plain") == "txt"
+  # see also `runnableExamples`.
+  # xxx we should have a way to avoid duplicating code between runnableExamples and tests
+
+  doAssert m.getMimetype("nim") == "text/nim"
+  doAssert m.getMimetype("nimble") == "text/nimble"
+  doAssert m.getMimetype("nimf") == "text/nim"
+  doAssert m.getMimetype("nims") == "text/nim"
+
+static: main()
+main()
diff --git a/tests/stdlib/tmisc_issues.nim b/tests/stdlib/tmisc_issues.nim
new file mode 100644
index 000000000..86dcf4162
--- /dev/null
+++ b/tests/stdlib/tmisc_issues.nim
@@ -0,0 +1,39 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c cpp js"
+"""
+
+import std/assertions
+
+# bug #20227
+type
+  Data = object
+    id: int
+
+  Test = distinct Data
+
+  Object = object
+    data: Test
+
+
+var x: Object = Object(data: Test(Data(id: 12)))
+doAssert Data(x.data).id == 12
+
+block: # bug #16771
+  type A = object
+    n: int
+
+  proc foo(a, b: var A) =
+    swap a, b
+
+  var a, b: A
+  a.n = 42
+  b.n = 1
+  doAssert a.n == 42
+  doAssert b.n == 1
+  a.swap b
+  doAssert a.n == 1
+  doAssert b.n == 42
+  a.foo b
+  doAssert a.n == 42
+  doAssert b.n == 1
diff --git a/tests/stdlib/tmitems.nim b/tests/stdlib/tmitems.nim
index 17265e1f7..cc515a175 100644
--- a/tests/stdlib/tmitems.nim
+++ b/tests/stdlib/tmitems.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   output: '''@[11, 12, 13]
 @[11, 12, 13]
 @[1, 3, 5]
@@ -16,7 +17,18 @@ fpqeew
 <Students>
   <Student Name="Aprilfoo" />
   <Student Name="bar" />
-</Students>'''
+</Students>
+<chapter>
+    <title>This is a Docbook title</title>
+    <para>
+        This is a Docbook paragraph containing <emphasis>emphasized</emphasis>,
+        <literal>literal</literal> and <replaceable>replaceable</replaceable>
+        text. Sometimes scrunched together like this:
+        <literal>literal</literal><replaceable>replaceable</replaceable>
+        and sometimes not:
+        <literal>literal</literal> <replaceable>replaceable</replaceable>
+    </para>
+</chapter>'''
 """
 
 block:
@@ -51,6 +63,7 @@ block:
 
 block:
   var x = "foobar"
+  prepareMutation(x)
   var y = cast[cstring](addr x[0])
   for c in y.mitems:
     inc c
@@ -64,6 +77,7 @@ block:
 
 block:
   var x = "foobar"
+  prepareMutation(x)
   var y = cast[cstring](addr x[0])
   for i, c in y.mpairs:
     inc c, i
@@ -123,7 +137,7 @@ block:
     x.num += 10
   echo j
 
-import xmltree, xmlparser, streams, strtabs
+import xmltree, xmlparser, parsexml, streams, strtabs
 
 block:
   var d = parseXml(newStringStream """<Students>
@@ -134,3 +148,17 @@ block:
     x = <>Student(Name=x.attrs["Name"] & "foo")
   d[1].attrs["Name"] = "bar"
   echo d
+
+block:
+  var d = parseXml(newStringStream """<chapter>
+    <title>This is a Docbook title</title>
+    <para>
+        This is a Docbook paragraph containing <emphasis>emphasized</emphasis>,
+        <literal>literal</literal> and <replaceable>replaceable</replaceable>
+        text. Sometimes scrunched together like this:
+        <literal>literal</literal><replaceable>replaceable</replaceable>
+        and sometimes not:
+        <literal>literal</literal> <replaceable>replaceable</replaceable>
+    </para>
+</chapter>""",{reportComments, reportWhitespace})
+  echo d
diff --git a/tests/stdlib/tmonotimes.nim b/tests/stdlib/tmonotimes.nim
new file mode 100644
index 000000000..1366dbfe9
--- /dev/null
+++ b/tests/stdlib/tmonotimes.nim
@@ -0,0 +1,22 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c js"
+"""
+
+import std/[monotimes, times]
+import std/assertions
+
+let d = initDuration(nanoseconds = 10)
+let t1 = getMonoTime()
+let t2 = t1 + d
+
+doAssert t2 - t1 == d
+doAssert t1 == t1
+doAssert t1 != t2
+doAssert t2 - d == t1
+doAssert t1 < t2
+doAssert t1 <= t2
+doAssert t1 <= t1
+doAssert not(t2 < t1)
+doAssert t1 < high(MonoTime)
+doAssert low(MonoTime) < t1
diff --git a/tests/stdlib/tnativesockets.nim b/tests/stdlib/tnativesockets.nim
index c683647bc..8242beb83 100644
--- a/tests/stdlib/tnativesockets.nim
+++ b/tests/stdlib/tnativesockets.nim
@@ -1,8 +1,30 @@
-import nativesockets, unittest
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
 
-suite "nativesockets":
-  test "getHostname":
-    let hostname = getHostname()
-    check hostname.len > 0
-    check hostname.len < 64
+import std/nativesockets
+import stdtest/testutils
+import std/assertions
 
+block:
+  let hostname = getHostname()
+  doAssert hostname.len > 0
+
+when defined(windows):
+  assertAll:
+    toInt(IPPROTO_IP) == 0
+    toInt(IPPROTO_ICMP) == 1
+    toInt(IPPROTO_TCP) == 6
+    toInt(IPPROTO_UDP) == 17
+    toInt(IPPROTO_IPV6) == 41
+    toInt(IPPROTO_ICMPV6) == 58
+    toInt(IPPROTO_RAW) == 20
+
+    # no changes to enum value
+    ord(IPPROTO_TCP) == 6
+    ord(IPPROTO_UDP) == 17
+    ord(IPPROTO_IP) == 18
+    ord(IPPROTO_IPV6) == 19
+    ord(IPPROTO_RAW) == 20
+    ord(IPPROTO_ICMP) == 21
+    ord(IPPROTO_ICMPV6) == 22
diff --git a/tests/stdlib/tnet.nim b/tests/stdlib/tnet.nim
index d364447da..27a6ac49c 100644
--- a/tests/stdlib/tnet.nim
+++ b/tests/stdlib/tnet.nim
@@ -1,51 +1,92 @@
+discard """
+matrix: "--mm:refc; --mm:orc"
+outputsub: ""
+"""
+
 import net, nativesockets
 import unittest
+import std/assertions
 
-suite "isIpAddress tests":
-  test "127.0.0.1 is valid":
+block: # isIpAddress tests
+  block: # 127.0.0.1 is valid
     check isIpAddress("127.0.0.1") == true
 
-  test "ipv6 localhost is valid":
+  block: # ipv6 localhost is valid
     check isIpAddress("::1") == true
 
-  test "fqdn is not an ip address":
+  block: # fqdn is not an ip address
     check isIpAddress("example.com") == false
 
-  test "random string is not an ipaddress":
+  block: # random string is not an ipaddress
     check isIpAddress("foo bar") == false
 
-  test "5127.0.0.1 is invalid":
+  block: # 5127.0.0.1 is invalid
     check isIpAddress("5127.0.0.1") == false
 
-  test "ipv6 is valid":
+  block: # ipv6 is valid
     check isIpAddress("2001:cdba:0000:0000:0000:0000:3257:9652") == true
 
-  test "invalid ipv6":
+  block: # invalid ipv6
     check isIpAddress("gggg:cdba:0000:0000:0000:0000:3257:9652") == false
 
 
-suite "parseIpAddress tests":
-  test "127.0.0.1 is valid":
+block: # parseIpAddress tests
+  block: # 127.0.0.1 is valid
     discard parseIpAddress("127.0.0.1")
 
-  test "ipv6 localhost is valid":
+  block: # ipv6 localhost is valid
     discard parseIpAddress("::1")
 
-  test "fqdn is not an ip address":
+  block: # fqdn is not an ip address
     expect(ValueError):
       discard parseIpAddress("example.com")
 
-  test "random string is not an ipaddress":
+  block: # random string is not an ipaddress
     expect(ValueError):
       discard parseIpAddress("foo bar")
 
-  test "ipv6 is valid":
+  block: # ipv6 is valid
     discard parseIpAddress("2001:cdba:0000:0000:0000:0000:3257:9652")
 
-  test "invalid ipv6":
+  block: # invalid ipv6
     expect(ValueError):
       discard parseIpAddress("gggg:cdba:0000:0000:0000:0000:3257:9652")
 
+  block: # ipv4-compatible ipv6 address (embedded ipv4 address)
+    check parseIpAddress("::ffff:10.0.0.23") == parseIpAddress("::ffff:0a00:0017")
+
+  block: # octal number in ipv4 address
+    expect(ValueError):
+      discard parseIpAddress("010.8.8.8")
+    expect(ValueError):
+      discard parseIpAddress("8.010.8.8")
+
+  block: # hexadecimal number in ipv4 address
+    expect(ValueError):
+      discard parseIpAddress("0xc0.168.0.1")
+    expect(ValueError):
+      discard parseIpAddress("192.0xa8.0.1")
+
+  block: # less than 4 numbers in ipv4 address
+    expect(ValueError):
+      discard parseIpAddress("127.0.1")
+
+  block: # octal number in embedded ipv4 address
+    expect(ValueError):
+      discard parseIpAddress("::ffff:010.8.8.8")
+    expect(ValueError):
+      discard parseIpAddress("::ffff:8.010.8.8")
+
+  block: # hexadecimal number in embedded ipv4 address
+    expect(ValueError):
+      discard parseIpAddress("::ffff:0xc0.168.0.1")
+    expect(ValueError):
+      discard parseIpAddress("::ffff:192.0xa8.0.1")
+
+  block: # less than 4 numbers in embedded ipv4 address
+    expect(ValueError):
+      discard parseIpAddress("::ffff:127.0.1")
+
 block: # "IpAddress/Sockaddr conversion"
   proc test(ipaddrstr: string) =
     var ipaddr_1 = parseIpAddress(ipaddrstr)
@@ -54,7 +95,7 @@ block: # "IpAddress/Sockaddr conversion"
     doAssert($ipaddrstr == $ipaddr_1)
 
     var sockaddr: Sockaddr_storage
-    var socklen: Socklen
+    var socklen: SockLen
     var ipaddr_2: IpAddress
     var port_2: Port
 
@@ -66,11 +107,11 @@ block: # "IpAddress/Sockaddr conversion"
     doAssert(ipaddr_1 == ipaddr_2)
     doAssert($ipaddr_1 == $ipaddr_2)
 
-    if sockaddr.ss_family == AF_INET.toInt:
+    if sockaddr.ss_family.cint == AF_INET.toInt:
       var sockaddr4: Sockaddr_in
       copyMem(addr sockaddr4, addr sockaddr, sizeof(sockaddr4))
       fromSockAddr(sockaddr4, socklen, ipaddr_2, port_2)
-    elif sockaddr.ss_family == AF_INET6.toInt:
+    elif sockaddr.ss_family.cint == AF_INET6.toInt:
       var sockaddr6: Sockaddr_in6
       copyMem(addr sockaddr6, addr sockaddr, sizeof(sockaddr6))
       fromSockAddr(sockaddr6, socklen, ipaddr_2, port_2)
diff --git a/tests/stdlib/tnet_ll.nim b/tests/stdlib/tnet_ll.nim
index 2ac272fd1..199946482 100644
--- a/tests/stdlib/tnet_ll.nim
+++ b/tests/stdlib/tnet_ll.nim
@@ -1,42 +1,52 @@
-discard """

-  action: run

-  output: '''

-[Suite] inet_ntop tests

-'''

-"""

-

-when defined(windows):

-  import winlean

-elif defined(posix):

-  import posix

-else:

-  {.error: "Unsupported OS".}

-

-import unittest, strutils

-

-suite "inet_ntop tests":

-

-  setup:

-    when defined(windows):

-      var wsa: WSAData

-      discard wsaStartup(0x101'i16, wsa.addr)

-  

-  test "IP V4":

-    var ip4 = 0x10111213

-    var buff: array[0..255, char]

-    let r = inet_ntop(AF_INET, ip4.addr, buff[0].addr, buff.sizeof.int32)

-    let res = if r == nil: "" else: $r

-    check: res == "19.18.17.16"

-      

-

-  test "IP V6":

-    when defined(windows):

-      let ipv6Support = (getVersion() and 0xff) > 0x5

-    else:

-      let ipv6Support = true

-          

-    var ip6 = [0x1000'u16, 0x1001, 0x2000, 0x2001, 0x3000, 0x3001, 0x4000, 0x4001]

-    var buff: array[0..255, char]

-    let r = inet_ntop(AF_INET6, ip6[0].addr, buff[0].addr, buff.sizeof.int32)

-    let res = if r == nil: "" else: $r

-    check: not ipv6Support or res == "10:110:20:120:30:130:40:140"

+discard """
+  action: run
+  matrix: "--mm:refc; --mm:orc"
+  output: '''
+
+[Suite] inet_ntop tests
+'''
+"""
+
+when defined(windows):
+  import winlean
+elif defined(posix):
+  import posix
+else:
+  {.error: "Unsupported OS".}
+
+import unittest, strutils
+
+suite "inet_ntop tests":
+
+  setup:
+    when defined(windows):
+      var wsa: WSAData
+      discard wsaStartup(0x101'i16, wsa.addr)
+  
+  test "IP V4":
+    # regular
+    var ip4 = InAddr()
+    ip4.s_addr = 0x10111213'u32
+    check: ip4.s_addr == 0x10111213'u32
+
+    var buff: array[0..255, char]
+    let r = inet_ntop(AF_INET, cast[pointer](ip4.s_addr.addr), cast[cstring](buff[0].addr), buff.len.int32)
+    let res = if r == nil: "" else: $r
+    check: res == "19.18.17.16"
+      
+  test "IP V6":
+    when defined(windows):
+      let ipv6Support = (getVersion() and 0xff) > 0x5
+    else:
+      let ipv6Support = true
+          
+    var ip6 = [0x1000'u16, 0x1001, 0x2000, 0x2001, 0x3000, 0x3001, 0x4000, 0x4001]
+    var buff: array[0..255, char]
+    let r = inet_ntop(AF_INET6, cast[pointer](ip6[0].addr), cast[cstring](buff[0].addr), buff.len.int32)
+    let res = if r == nil: "" else: $r
+    check: not ipv6Support or res == "10:110:20:120:30:130:40:140"
+
+  test "InAddr":
+    # issue 19244
+    var ip4 = InAddr(s_addr: 0x10111213'u32)
+    check: ip4.s_addr == 0x10111213'u32
diff --git a/tests/stdlib/tnetbind.nim b/tests/stdlib/tnetbind.nim
new file mode 100644
index 000000000..84f9ac464
--- /dev/null
+++ b/tests/stdlib/tnetbind.nim
@@ -0,0 +1,25 @@
+discard """
+matrix: "--mm:refc; --mm:orc"
+joinable: false
+"""
+
+#[
+joinable: false
+otherwise:
+Error: unhandled exception: Address already in use [OSError]
+]#
+
+import net
+
+## Test for net.bindAddr
+
+proc test() =
+  # IPv4 TCP
+  newSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP).bindAddr(Port(1900), "0.0.0.0")
+  newSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP).bindAddr(Port(1901))
+
+  # IPv6 TCP
+  newSocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP).bindAddr(Port(1902), "::")
+  newSocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP).bindAddr(Port(1903))
+
+test()
diff --git a/tests/stdlib/tnetconnect.nim b/tests/stdlib/tnetconnect.nim
new file mode 100644
index 000000000..ae654aed9
--- /dev/null
+++ b/tests/stdlib/tnetconnect.nim
@@ -0,0 +1,30 @@
+discard """
+  disabled: "i386"
+  matrix: "-d:ssl"
+"""
+
+import std/net
+from std/strutils import `%`
+from stdtest/testutils import enableRemoteNetworking
+
+# bug #15215
+proc test() =
+  let ctx = newContext()
+
+  proc fn(url: string) =
+    let socket = newSocket()
+    defer: close(socket)
+    connect(socket, url, Port(443), 5000) # typically 20 could be enough
+    send(socket, "GET / HTTP/1.0\nHost: $#\nConnection: close\n\n" % [url])
+    wrapSocket(ctx, socket)
+
+  # trying 2 sites makes it more resilent: refs #17458 this could give:
+  # * Call to 'connect' timed out. [TimeoutError]
+  # * No route to host [OSError]
+  try:
+    fn("www.nim-lang.org")
+  except TimeoutError, OSError:
+    fn("www.google.com")
+
+when enableRemoteNetworking:
+  test()
diff --git a/tests/stdlib/tnetdial.nim b/tests/stdlib/tnetdial.nim
index 695150179..a1e147ad5 100644
--- a/tests/stdlib/tnetdial.nim
+++ b/tests/stdlib/tnetdial.nim
@@ -2,20 +2,20 @@ discard """
   cmd: "nim c --threads:on $file"
   exitcode: 0
   output: "OK"
-  disabled: "travis"
 """
 
 import os, net, nativesockets, asyncdispatch
+import std/[assertions, typedthreads]
 
 ## Test for net.dial
 
 const port = Port(28431)
 
 proc initIPv6Server(hostname: string, port: Port): AsyncFD =
-  let fd = newNativeSocket(AF_INET6)
+  let fd = createNativeSocket(AF_INET6)
   setSockOptInt(fd, SOL_SOCKET, SO_REUSEADDR, 1)
   var aiList = getAddrInfo(hostname, port, AF_INET6)
-  if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32:
+  if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32:
     freeAddrInfo(aiList)
     raiseOSError(osLastError())
   freeAddrInfo(aiList)
@@ -39,10 +39,10 @@ proc testThread() {.thread.} =
   fd.close()
 
 proc test() =
+  let serverFd = initIPv6Server("::1", port)
   var t: Thread[void]
   createThread(t, testThread)
 
-  let serverFd = initIPv6Server("::1", port)
   var done = false
 
   serverFd.accept().callback = proc(fut: Future[AsyncFD]) =
@@ -58,4 +58,5 @@ proc test() =
 
   joinThread(t)
 
+# this would cause #13132 `for i in 0..<10000: test()`
 test()
diff --git a/tests/stdlib/tnilecho.nim b/tests/stdlib/tnilecho.nim
deleted file mode 100644
index 147b5e492..000000000
--- a/tests/stdlib/tnilecho.nim
+++ /dev/null
@@ -1,2 +0,0 @@
-var x = @["1", nil, "3"]
-doAssert $x == "@[1, nil, 3]"
diff --git a/tests/stdlib/tnre.nim b/tests/stdlib/tnre.nim
index fabbb69a8..3b40e9e83 100644
--- a/tests/stdlib/tnre.nim
+++ b/tests/stdlib/tnre.nim
@@ -1,24 +1,8 @@
 discard """
+matrix: "--mm:refc; --mm:orc"
 # Since the tests for nre are all bundled together we treat failure in one test as an nre failure
-# When running 'tests/testament/tester' a failed check() in the test suite will cause the exit
+# When running 'testament/tester' a failed check() in the test suite will cause the exit
 # codes to differ and be reported as a failure
-
-  output:
-    '''[Suite] Test NRE initialization
-
-[Suite] captures
-
-[Suite] find
-
-[Suite] string splitting
-
-[Suite] match
-
-[Suite] replace
-
-[Suite] escape strings
-
-[Suite] Misc tests'''
 """
 
 import nre
diff --git a/tests/stdlib/tntpath.nim b/tests/stdlib/tntpath.nim
new file mode 100644
index 000000000..8efdd6bd0
--- /dev/null
+++ b/tests/stdlib/tntpath.nim
@@ -0,0 +1,50 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/private/ntpath
+import std/assertions
+
+block: # From Python's `Lib/test/test_ntpath.py`
+  doAssert splitDrive(r"c:\foo\bar") == (r"c:", r"\foo\bar")
+  doAssert splitDrive(r"c:/foo/bar") == (r"c:", r"/foo/bar")
+  doAssert splitDrive(r"\\conky\mountpoint\foo\bar") == (r"\\conky\mountpoint", r"\foo\bar")
+  doAssert splitDrive(r"//conky/mountpoint/foo/bar") == (r"//conky/mountpoint", r"/foo/bar")
+  doAssert splitDrive(r"\\\conky\mountpoint\foo\bar") == (r"", r"\\\conky\mountpoint\foo\bar")
+  doAssert splitDrive(r"///conky/mountpoint/foo/bar") == (r"", r"///conky/mountpoint/foo/bar")
+  doAssert splitDrive(r"\\conky\\mountpoint\foo\bar") == (r"", r"\\conky\\mountpoint\foo\bar")
+  doAssert splitDrive(r"//conky//mountpoint/foo/bar") == (r"", r"//conky//mountpoint/foo/bar")
+  # Issue #19911: UNC part containing U+0130
+  doAssert splitDrive(r"//conky/MOUNTPOİNT/foo/bar") == (r"//conky/MOUNTPOİNT", r"/foo/bar")
+  # gh-81790: support device namespace, including UNC drives.
+  doAssert splitDrive(r"//?/c:") == (r"//?/c:", r"")
+  doAssert splitDrive(r"//?/c:/") == (r"//?/c:", r"/")
+  doAssert splitDrive(r"//?/c:/dir") == (r"//?/c:", r"/dir")
+  doAssert splitDrive(r"//?/UNC") == (r"", r"//?/UNC")
+  doAssert splitDrive(r"//?/UNC/") == (r"", r"//?/UNC/")
+  doAssert splitDrive(r"//?/UNC/server/") == (r"//?/UNC/server/", r"")
+  doAssert splitDrive(r"//?/UNC/server/share") == (r"//?/UNC/server/share", r"")
+  doAssert splitDrive(r"//?/UNC/server/share/dir") == (r"//?/UNC/server/share", r"/dir")
+  doAssert splitDrive(r"//?/VOLUME{00000000-0000-0000-0000-000000000000}/spam") == (r"//?/VOLUME{00000000-0000-0000-0000-000000000000}", r"/spam")
+  doAssert splitDrive(r"//?/BootPartition/") == (r"//?/BootPartition", r"/")
+
+  doAssert splitDrive(r"\\?\c:") == (r"\\?\c:", r"")
+  doAssert splitDrive(r"\\?\c:\") == (r"\\?\c:", r"\")
+  doAssert splitDrive(r"\\?\c:\dir") == (r"\\?\c:", r"\dir")
+  doAssert splitDrive(r"\\?\UNC") == (r"", r"\\?\UNC")
+  doAssert splitDrive(r"\\?\UNC\") == (r"", r"\\?\UNC\")
+  doAssert splitDrive(r"\\?\UNC\server\") == (r"\\?\UNC\server\", r"")
+  doAssert splitDrive(r"\\?\UNC\server\share") == (r"\\?\UNC\server\share", r"")
+  doAssert splitDrive(r"\\?\UNC\server\share\dir") == (r"\\?\UNC\server\share", r"\dir")
+  doAssert splitDrive(r"\\?\VOLUME{00000000-0000-0000-0000-000000000000}\spam") == (r"\\?\VOLUME{00000000-0000-0000-0000-000000000000}", r"\spam")
+  doAssert splitDrive(r"\\?\BootPartition\") == (r"\\?\BootPartition", r"\")
+
+block:
+  doAssert splitDrive(r"C:") == (r"C:", r"")
+  doAssert splitDrive(r"C:\") == (r"C:", r"\")
+  doAssert splitDrive(r"non/absolute/path") == (r"", r"non/absolute/path")
+
+  # Special for `\`-rooted paths on Windows. I don't know if this is correct,
+  # rbut `\` is not recognized as a drive, in contrast to `C:` or `\?\c:`.
+  # This behavior is the same for Python's `splitdrive` function.
+  doAssert splitDrive(r"\\") == (r"", r"\\")
diff --git a/tests/stdlib/tobjectdollar.nim b/tests/stdlib/tobjectdollar.nim
new file mode 100644
index 000000000..cf78fa255
--- /dev/null
+++ b/tests/stdlib/tobjectdollar.nim
@@ -0,0 +1,14 @@
+discard """
+  matrix: "-d:nimPreviewSlimSystem"
+"""
+
+import std/assertions
+
+type Foo = object
+  a, b: int
+
+let x = Foo(a: 23, b: 45)
+doAssert not compiles($x)
+import std/objectdollar
+doAssert compiles($x)
+doAssert $x == "(a: 23, b: 45)"
diff --git a/tests/stdlib/toids.nim b/tests/stdlib/toids.nim
new file mode 100644
index 000000000..dd5b84c51
--- /dev/null
+++ b/tests/stdlib/toids.nim
@@ -0,0 +1,15 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/oids
+import std/assertions
+
+block: # genOid
+  let x = genOid()
+  doAssert ($x).len == 24
+
+block:
+  let x = genOid()
+  let y = parseOid(cstring($x))
+  doAssert x == y
diff --git a/tests/stdlib/topenssl.nim b/tests/stdlib/topenssl.nim
new file mode 100644
index 000000000..af259627f
--- /dev/null
+++ b/tests/stdlib/topenssl.nim
@@ -0,0 +1,46 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/wordwrap
+import openssl
+import std/assertions
+
+const PubKey = r"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAknKWvrdnncCIzBnIGrZ5qtZrPH+Yo3t7ag9WZIu6Gmc/JgIDDaZhJeyGW0YSnifeAEhooWvM4jDWhTEARzktalSHqYtmwI/1Oxwp6NTYH8akMe2LCpZ5pX9FVA6m9o2tkbdXatbDKRqeD4UA8Ow7Iyrdo6eb1SU8vk+26i+uXHTtsb25p8uf2ppOJrJCy+1vr8Gsnuwny1UdoYZTxMsxRFPf+UX/LrSXMHVq/oPVa3SJ4VHMpYrG/httAugVP6K58xiZ93jst63/dd0JL85mWJu1uS3uz92aL5O97xzth3wR4BbdmDUlN4LuTIwi6DtEcC7gUOTnOzH4zgp2b5RyHwIDAQAB"
+const PrivateKey = r"MIIEpAIBAAKCAQEAknKWvrdnncCIzBnIGrZ5qtZrPH+Yo3t7ag9WZIu6Gmc/JgIDDaZhJeyGW0YSnifeAEhooWvM4jDWhTEARzktalSHqYtmwI/1Oxwp6NTYH8akMe2LCpZ5pX9FVA6m9o2tkbdXatbDKRqeD4UA8Ow7Iyrdo6eb1SU8vk+26i+uXHTtsb25p8uf2ppOJrJCy+1vr8Gsnuwny1UdoYZTxMsxRFPf+UX/LrSXMHVq/oPVa3SJ4VHMpYrG/httAugVP6K58xiZ93jst63/dd0JL85mWJu1uS3uz92aL5O97xzth3wR4BbdmDUlN4LuTIwi6DtEcC7gUOTnOzH4zgp2b5RyHwIDAQABAoIBACSOxmLFlfAjaALLTNCeTLEA5bQshgYJhT1sprxixQpiS7lJN0npBsdYzBFs5KjmetzHNpdVOcgdOO/204L0Gwo4H8WLLxNS3HztAulEeM813zc3fUYfWi6eHshk//j8VR/TDNd21TElm99z7FA4KGsXAE0iQhxrN0aqz5aWYIhjprtHA5KxXIiESnTkof5Cud8oXEnPiwPGNhq93QeQzh7xQIKSaDKBcdAa6edTFhzc4RLUQRfrik/GqJzouEDQ9v6H/uiOLTB3FxxwErQIf6dvSVhD9gs1nSLQfyj3S2Hxe9S2zglTl07EsawTQUxtVQkdZUOok67c7CPBxecZ2wECgYEA2c31gr/UJwczT+P/AE52GkHHETXMxqE3Hnh9n4CitfAFSD5X0VwZvGjZIlln2WjisTd92Ymf65eDylX2kCm93nzZ2GfXgS4zl4oY1N87+VeNQlx9f2+6GU7Hs0HFdfu8bGd+0sOuWA1PFqQCobxCACMPTkuzsG9M7knUTN59HS8CgYEArCEoP4ReYoOFveXUE0AteTPb4hryvR9VDEolP+LMoiPe8AzBMeB5fP493TPdjtnWmrPCXNLc7UAFSj2CZsRhau4PuiqnNrsb5iz/7iXVl3E8wZvS4w7WYpO4m33L0cijA6MdcdqilQu4Z5tw4nG45lAW9UYyOc9D4hJTzgtGHhECgYA6QyDoj931brSoK0ocT+DB11Sj4utbOuberMaV8zgTSRhwodSl+WgdAUMMMDRacPcrBrgQiAMSZ15msqYZHEFhEa7Id8arFKvSXquTzf9iDKyJ0unzO/ThLjS3W+GxVNyrdufzA0tQ3IaKfOcDUrOpC7fdbtyrVqqSl4dF5MI9GwKBgQCl3OF6qyOEDDZgsUk1L59h7k3QR6VmBf4e9IeGUxZamvQlHjU/yY1nm1mjgGnbUB/SPKtqZKoMV6eBTVoNiuhQcItpGda9D3mnx+7p3T0/TBd+fJeuwcplfPDjrEktogcq5w/leQc3Ve7gr1EMcwb3r28f8/9L42QHQR/OKODs8QKBgQCFAvxDRPyYg7V/AgD9rt1KzXi4+b3Pls5NXZa2g/w+hmdhHUNxV5IGmHlqFnptGyshgYgQGxMMkW0iJ1j8nLamFnkbFQOp5/UKbdPLRKiB86oPpxsqYtPXucDUqEfcMsp57mD1CpGVODbspogFpSUvQpMECkhvI0XLMbolMdo53g=="
+
+proc rsaPublicEncrypt(fr: string): string =
+  let mKey = "-----BEGIN PUBLIC KEY-----\n" & PubKey.wrapWords(64) & "\n-----END PUBLIC KEY-----"
+  let bio = bioNew(bioSMem())
+  doAssert BIO_write(bio, mKey.cstring, mKey.len.cint) >= 0
+  let rsa = PEM_read_bio_RSA_PUBKEY(bio, nil, nil, nil)
+  doAssert rsa != nil
+  doAssert BIO_free(bio) >= 0
+  result = newString(RSA_size(rsa))
+  let frdata = cast[ptr uint8](fr.cstring)
+  var todata = cast[ptr uint8](result.cstring)
+  doAssert RSA_public_encrypt(fr.len.cint, frdata, todata, rsa, RSA_PKCS1_PADDING) != -1
+  RSA_free(rsa)
+
+proc rasPrivateDecrypt(fr: string): string =
+  let mKey = "-----BEGIN RSA PRIVATE KEY-----\n" & PrivateKey.wrapWords(64) & "\n-----END RSA PRIVATE KEY-----"
+  let bio = bioNew(bioSMem())
+  doAssert BIO_write(bio, mKey.cstring, mKey.len.cint) >= 0
+  let rsa = PEM_read_bio_RSAPrivateKey(bio, nil, nil, nil)
+  doAssert rsa != nil
+  doAssert BIO_free(bio) >= 0
+  let rsaLen = RSA_size(rsa)
+  result = newString(rsaLen)
+  let frdata = cast[ptr uint8](fr.cstring)
+  var todata = cast[ptr uint8](result.cstring)
+  let lenOrig = RSA_private_decrypt(rsaLen, frdata, todata, rsa, RSA_PKCS1_PADDING)
+  doAssert lenOrig >= 0 and lenOrig < result.len
+  doAssert result[lenOrig] == '\0'
+  result.setLen lenOrig
+  RSA_free(rsa)
+
+let res = "TEST"
+let miwen = rsaPublicEncrypt(res)
+let mingwen = rasPrivateDecrypt(miwen)
+doAssert mingwen == res
+
diff --git a/tests/stdlib/toptions.nim b/tests/stdlib/toptions.nim
new file mode 100644
index 000000000..63a10e746
--- /dev/null
+++ b/tests/stdlib/toptions.nim
@@ -0,0 +1,207 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c js"
+"""
+
+import std/[json, options]
+
+import std/assertions
+import std/objectdollar
+
+
+# RefPerson is used to test that overloaded `==` operator is not called by
+# options. It is defined here in the global scope, because otherwise the test
+# will not even consider the `==` operator. Different bug?
+type RefPerson = ref object
+  name: string
+
+proc `==`(a, b: RefPerson): bool =
+  assert(not a.isNil and not b.isNil)
+  a.name == b.name
+
+
+template disableJsVm(body) =
+  # something doesn't work in JS VM
+  when defined(js):
+    when nimvm: discard
+    else: body
+  else:
+    body
+
+proc main() =
+  type
+    Foo = ref object
+      test: string
+    Test = object
+      foo: Option[Foo]
+
+  let js = """{"foo": {"test": "123"}}"""
+  let parsed = parseJson(js)
+  let a = parsed.to(Test)
+  doAssert $(%*a) == """{"foo":{"test":"123"}}"""
+
+  block options:
+    # work around a bug in unittest
+    let intNone = none(int)
+    let stringNone = none(string)
+
+    block example:
+      proc find(haystack: string, needle: char): Option[int] =
+        for i, c in haystack:
+          if c == needle:
+            return some i
+
+      doAssert("abc".find('c').get() == 2)
+
+      let result = "team".find('i')
+
+      doAssert result == intNone
+      doAssert result.isNone
+
+    block some:
+      doAssert some(6).get() == 6
+      doAssert some("a").unsafeGet() == "a"
+      doAssert some(6).isSome
+      doAssert some("a").isSome
+
+    block none:
+      doAssertRaises UnpackDefect:
+        discard none(int).get()
+      doAssert(none(int).isNone)
+      doAssert(not none(string).isSome)
+
+    block equality:
+      doAssert some("a") == some("a")
+      doAssert some(7) != some(6)
+      doAssert some("a") != stringNone
+      doAssert intNone == intNone
+
+      when compiles(some("a") == some(5)):
+        doAssert false
+      when compiles(none(string) == none(int)):
+        doAssert false
+
+    block get_with_a_default_value:
+      doAssert(some("Correct").get("Wrong") == "Correct")
+      doAssert(stringNone.get("Correct") == "Correct")
+
+    block stringify:
+      doAssert($(some("Correct")) == "some(\"Correct\")")
+      doAssert($(stringNone) == "none(string)")
+
+    disableJsVm:
+      block map_with_a_void_result:
+        var procRan = 0
+        # TODO closure anonymous functions doesn't work in VM with JS
+        # Error: cannot evaluate at compile time: procRan
+        some(123).map(proc (v: int) = procRan = v)
+        doAssert procRan == 123
+        intNone.map(proc (v: int) = doAssert false)
+
+    block map:
+      doAssert(some(123).map(proc (v: int): int = v * 2) == some(246))
+      doAssert(intNone.map(proc (v: int): int = v * 2).isNone)
+
+    block filter:
+      doAssert(some(123).filter(proc (v: int): bool = v == 123) == some(123))
+      doAssert(some(456).filter(proc (v: int): bool = v == 123).isNone)
+      doAssert(intNone.filter(proc (v: int): bool = doAssert false).isNone)
+
+    block flatMap:
+      proc addOneIfNotZero(v: int): Option[int] =
+        if v != 0:
+          result = some(v + 1)
+        else:
+          result = none(int)
+
+      doAssert(some(1).flatMap(addOneIfNotZero) == some(2))
+      doAssert(some(0).flatMap(addOneIfNotZero) == none(int))
+      doAssert(some(1).flatMap(addOneIfNotZero).flatMap(addOneIfNotZero) == some(3))
+
+      proc maybeToString(v: int): Option[string] =
+        if v != 0:
+          result = some($v)
+        else:
+          result = none(string)
+
+      doAssert(some(1).flatMap(maybeToString) == some("1"))
+
+      proc maybeExclaim(v: string): Option[string] =
+        if v != "":
+          result = some v & "!"
+        else:
+          result = none(string)
+
+      doAssert(some(1).flatMap(maybeToString).flatMap(maybeExclaim) == some("1!"))
+      doAssert(some(0).flatMap(maybeToString).flatMap(maybeExclaim) == none(string))
+
+    block SomePointer:
+      var intref: ref int
+      doAssert(option(intref).isNone)
+      intref.new
+      doAssert(option(intref).isSome)
+
+      let tmp = option(intref)
+      doAssert(sizeof(tmp) == sizeof(ptr int))
+
+      var prc = proc (x: int): int = x + 1
+      doAssert(option(prc).isSome)
+      prc = nil
+      doAssert(option(prc).isNone)
+
+    block:
+      doAssert(none[int]().isNone)
+      doAssert(none(int) == none[int]())
+
+    # "$ on typed with .name"
+    block:
+      type Named = object
+        name: string
+
+      let nobody = none(Named)
+      doAssert($nobody == "none(Named)")
+
+    # "$ on type with name()"
+    block:
+      type Person = object
+        myname: string
+
+      let noperson = none(Person)
+      doAssert($noperson == "none(Person)")
+
+    # "Ref type with overloaded `==`"
+    block:
+      let p = some(RefPerson.new())
+      doAssert p.isSome
+
+    block: # test cstring
+      block:
+        let x = some("".cstring)
+        doAssert x.isSome
+        doAssert x.get == ""
+
+      block:
+        let x = some("12345".cstring)
+        doAssert x.isSome
+        doAssert x.get == "12345"
+
+      block:
+        let x = "12345".cstring
+        let y = some(x)
+        doAssert y.isSome
+        doAssert y.get == "12345"
+
+      block:
+        let x = none(cstring)
+        doAssert x.isNone
+        doAssert $x == "none(cstring)"
+
+static: main()
+main()
+
+when not defined(js):
+  block: # bug #22932
+    var it = iterator: int {.closure.} = discard
+    doAssert it.option.isSome # Passes.
+    it = nil
+    doAssert it.option.isNone # Passes.
diff --git a/tests/stdlib/torderedtable.nim b/tests/stdlib/torderedtable.nim
deleted file mode 100644
index 91a916930..000000000
--- a/tests/stdlib/torderedtable.nim
+++ /dev/null
@@ -1,18 +0,0 @@
-import tables, random
-var t = initOrderedTable[int,string]()
-
-# this tests issue #5917
-var data = newSeq[int]()
-for i in 0..<1000:
-  var x = random(1000)
-  if x notin t: data.add(x)
-  t[x] = "meh"
-
-# this checks that keys are re-inserted
-# in order when table is enlarged.
-var i = 0
-for k, v in t:
-  doAssert(k == data[i])
-  doAssert(v == "meh")
-  inc(i)
-
diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim
index e6fbb0e51..611659fdb 100644
--- a/tests/stdlib/tos.nim
+++ b/tests/stdlib/tos.nim
@@ -1,13 +1,5 @@
 discard """
-  output: '''true
-true
-true
-true
-true
-true
-true
-true
-true
+  output: '''
 All:
 __really_obscure_dir_name/are.x
 __really_obscure_dir_name/created
@@ -27,115 +19,857 @@ __really_obscure_dir_name/created
 __really_obscure_dir_name/dirs
 __really_obscure_dir_name/some
 __really_obscure_dir_name/test
-false
-false
-false
-false
-false
-false
-false
-false
-false
-true
-true
 Raises
-true
-true
-true
-true
+Raises
 '''
+  matrix: "--mm:refc; --mm:orc"
+  joinable: false
 """
 # test os path creation, iteration, and deletion
 
-import os, strutils
+from stdtest/specialpaths import buildDir
+import std/[syncio, assertions, osproc, os, strutils, pathnorm]
+
+block fileOperations:
+  let files = @["these.txt", "are.x", "testing.r", "files.q"]
+  let dirs = @["some", "created", "test", "dirs"]
 
-let files = @["these.txt", "are.x", "testing.r", "files.q"]
-let dirs = @["some", "created", "test", "dirs"]
+  let dname = "__really_obscure_dir_name"
+
+  createDir(dname)
+  doAssert dirExists(dname)
 
-let dname = "__really_obscure_dir_name"
+  block: # copyFile, copyFileToDir
+    doAssertRaises(OSError): copyFile(dname/"nonexistent.txt", dname/"nonexistent.txt")
+    let fname = "D20201009T112235"
+    let fname2 = "D20201009T112235.2"
+    let str = "foo1\0foo2\nfoo3\0"
+    let file = dname/fname
+    let file2 = dname/fname2
+    writeFile(file, str)
+    doAssert readFile(file) == str
+    let sub = "sub"
+    doAssertRaises(OSError): copyFile(file, dname/sub/fname2)
+    doAssertRaises(OSError): copyFileToDir(file, dname/sub)
+    doAssertRaises(ValueError): copyFileToDir(file, "")
+    copyFile(file, file2)
+    doAssert fileExists(file2)
+    doAssert readFile(file2) == str
+    createDir(dname/sub)
+    copyFileToDir(file, dname/sub)
+    doAssert fileExists(dname/sub/fname)
+    removeDir(dname/sub)
+    doAssert not dirExists(dname/sub)
+    removeFile(file)
+    removeFile(file2)
 
-createDir(dname)
-echo dirExists(dname)
+  # Test creating files and dirs
+  for dir in dirs:
+    createDir(dname/dir)
+    doAssert dirExists(dname/dir)
 
-# Test creating files and dirs
-for dir in dirs:
-  createDir(dname/dir)
-  echo dirExists(dname/dir)
+  for file in files:
+    let fh = open(dname/file, fmReadWrite)
+    fh.close()
+    doAssert fileExists(dname/file)
 
-for file in files:
-  let fh = open(dname/file, fmReadWrite)
-  fh.close()
-  echo fileExists(dname/file)
+  echo "All:"
 
-echo "All:"
+  template norm(x): untyped =
+    (when defined(windows): x.replace('\\', '/') else: x)
 
-template norm(x): untyped =
-  (when defined(windows): x.replace('\\', '/') else: x)
+  for path in walkPattern(dname/"*"):
+    echo path.norm
 
-for path in walkPattern(dname/"*"):
-  echo path.norm
+  echo "Files:"
 
-echo "Files:"
+  for path in walkFiles(dname/"*"):
+    echo path.norm
 
-for path in walkFiles(dname/"*"):
-  echo path.norm
+  echo "Dirs:"
 
-echo "Dirs:"
+  for path in walkDirs(dname/"*"):
+    echo path.norm
 
-for path in walkDirs(dname/"*"):
-  echo path.norm
+  # Test removal of files dirs
+  for dir in dirs:
+    removeDir(dname/dir)
+    doAssert: not dirExists(dname/dir)
 
-# Test removal of files dirs
-for dir in dirs:
-  removeDir(dname/dir)
-  echo dirExists(dname/dir)
+  for file in files:
+    removeFile(dname/file)
+    doAssert: not fileExists(dname/file)
 
-for file in files:
-  removeFile(dname/file)
-  echo fileExists(dname/file)
+  removeDir(dname)
+  doAssert: not dirExists(dname)
 
-removeDir(dname)
-echo dirExists(dname)
+  # createDir should create recursive directories
+  createDir(dirs[0] / dirs[1])
+  doAssert dirExists(dirs[0] / dirs[1]) # true
+  removeDir(dirs[0])
 
-# createDir should create recursive directories
-createDir(dirs[0] / dirs[1])
-echo dirExists(dirs[0] / dirs[1]) # true
-removeDir(dirs[0])
+  # createDir should properly handle trailing separator
+  createDir(dname / "")
+  doAssert dirExists(dname) # true
+  removeDir(dname)
 
-# createDir should properly handle trailing separator
-createDir(dname / "")
-echo dirExists(dname) # true
-removeDir(dname)
+  # createDir should raise IOError if the path exists
+  # and is not a directory
+  open(dname, fmWrite).close
+  try:
+    createDir(dname)
+  except IOError:
+    echo "Raises"
+  removeFile(dname)
 
-# createDir should raise IOError if the path exists
-# and is not a directory
-open(dname, fmWrite).close
-try:
+  # removeFile should not remove directory
   createDir(dname)
-except IOError:
-  echo "Raises"
-removeFile(dname)
+  try:
+    removeFile(dname)
+  except OSError:
+    echo "Raises"
+  removeDir(dname)
+
+  # test copyDir:
+  createDir("a/b")
+  open("a/b/file.txt", fmWrite).close
+  createDir("a/b/c")
+  open("a/b/c/fileC.txt", fmWrite).close
+
+  copyDir("a", "../dest/a")
+  removeDir("a")
+
+  doAssert dirExists("../dest/a/b")
+  doAssert fileExists("../dest/a/b/file.txt")
+
+  doAssert fileExists("../dest/a/b/c/fileC.txt")
+  removeDir("../dest")
+
+  # test copyDir:
+  # if separator at the end of a path
+  createDir("a/b")
+  open("a/file.txt", fmWrite).close
+
+  copyDir("a/", "../dest/a/")
+  removeDir("a")
+
+  doAssert dirExists("../dest/a/b")
+  doAssert fileExists("../dest/a/file.txt")
+  removeDir("../dest")
+
+  # createDir should not fail if `dir` is empty
+  createDir("")
+
+
+  when defined(linux): # bug #24174
+    createDir("a/b")
+    open("a/file.txt", fmWrite).close
+
+    if not fileExists("a/fifoFile"):
+      doAssert execCmd("mkfifo -m 600 a/fifoFile") == 0
+
+    copyDir("a/", "../dest/a/", skipSpecial = true)
+    copyDirWithPermissions("a/", "../dest2/a/", skipSpecial = true)
+    removeDir("a")
+
+  # Symlink handling in `copyFile`, `copyFileWithPermissions`, `copyFileToDir`,
+  # `copyDir`, `copyDirWithPermissions`, `moveFile`, and `moveDir`.
+  block:
+    const symlinksAreHandled = not defined(windows)
+    const dname = buildDir/"D20210116T140629"
+    const subDir = dname/"sub"
+    const subDir2 = dname/"sub2"
+    const brokenSymlinkName = "D20210101T191320_BROKEN_SYMLINK"
+    const brokenSymlink = dname/brokenSymlinkName
+    const brokenSymlinkSrc = "D20210101T191320_nonexistent"
+    const brokenSymlinkCopy = brokenSymlink & "_COPY"
+    const brokenSymlinkInSubDir = subDir/brokenSymlinkName
+    const brokenSymlinkInSubDir2 = subDir2/brokenSymlinkName
+
+    createDir(subDir)
+    createSymlink(brokenSymlinkSrc, brokenSymlink)
+
+    # Test copyFile
+    when symlinksAreHandled:
+      doAssertRaises(OSError):
+        copyFile(brokenSymlink, brokenSymlinkCopy)
+      doAssertRaises(OSError):
+        copyFile(brokenSymlink, brokenSymlinkCopy, {cfSymlinkFollow})
+    copyFile(brokenSymlink, brokenSymlinkCopy, {cfSymlinkIgnore})
+    doAssert not fileExists(brokenSymlinkCopy)
+    copyFile(brokenSymlink, brokenSymlinkCopy, {cfSymlinkAsIs})
+    when symlinksAreHandled:
+      doAssert expandSymlink(brokenSymlinkCopy) == brokenSymlinkSrc
+      removeFile(brokenSymlinkCopy)
+    else:
+      doAssert not fileExists(brokenSymlinkCopy)
+    doAssertRaises(AssertionDefect):
+      copyFile(brokenSymlink, brokenSymlinkCopy,
+               {cfSymlinkAsIs, cfSymlinkFollow})
+
+    # Test copyFileWithPermissions
+    when symlinksAreHandled:
+      doAssertRaises(OSError):
+        copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy)
+      doAssertRaises(OSError):
+        copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy,
+                                options = {cfSymlinkFollow})
+    copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy,
+                            options = {cfSymlinkIgnore})
+    doAssert not fileExists(brokenSymlinkCopy)
+    copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy,
+                            options = {cfSymlinkAsIs})
+    when symlinksAreHandled:
+      doAssert expandSymlink(brokenSymlinkCopy) == brokenSymlinkSrc
+      removeFile(brokenSymlinkCopy)
+    else:
+      doAssert not fileExists(brokenSymlinkCopy)
+    doAssertRaises(AssertionDefect):
+      copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy,
+                              options = {cfSymlinkAsIs, cfSymlinkFollow})
+
+    # Test copyFileToDir
+    when symlinksAreHandled:
+      doAssertRaises(OSError):
+        copyFileToDir(brokenSymlink, subDir)
+      doAssertRaises(OSError):
+        copyFileToDir(brokenSymlink, subDir, {cfSymlinkFollow})
+    copyFileToDir(brokenSymlink, subDir, {cfSymlinkIgnore})
+    doAssert not fileExists(brokenSymlinkInSubDir)
+    copyFileToDir(brokenSymlink, subDir, {cfSymlinkAsIs})
+    when symlinksAreHandled:
+      doAssert expandSymlink(brokenSymlinkInSubDir) == brokenSymlinkSrc
+      removeFile(brokenSymlinkInSubDir)
+    else:
+      doAssert not fileExists(brokenSymlinkInSubDir)
+
+    createSymlink(brokenSymlinkSrc, brokenSymlinkInSubDir)
+
+    # Test copyDir
+    copyDir(subDir, subDir2)
+    when symlinksAreHandled:
+      doAssert expandSymlink(brokenSymlinkInSubDir2) == brokenSymlinkSrc
+    else:
+      doAssert not fileExists(brokenSymlinkInSubDir2)
+    removeDir(subDir2)
+
+    # Test copyDirWithPermissions
+    copyDirWithPermissions(subDir, subDir2)
+    when symlinksAreHandled:
+      doAssert expandSymlink(brokenSymlinkInSubDir2) == brokenSymlinkSrc
+    else:
+      doAssert not fileExists(brokenSymlinkInSubDir2)
+    removeDir(subDir2)
+
+    # Test moveFile
+    moveFile(brokenSymlink, brokenSymlinkCopy)
+    when not defined(windows):
+      doAssert expandSymlink(brokenSymlinkCopy) == brokenSymlinkSrc
+    else:
+      doAssert symlinkExists(brokenSymlinkCopy)
+    removeFile(brokenSymlinkCopy)
+
+    # Test moveDir
+    moveDir(subDir, subDir2)
+    when not defined(windows):
+      doAssert expandSymlink(brokenSymlinkInSubDir2) == brokenSymlinkSrc
+    else:
+      doAssert symlinkExists(brokenSymlinkInSubDir2)
+
+    removeDir(dname)
 
-# test copyDir:
-createDir("a/b")
-open("a/b/file.txt", fmWrite).close
-createDir("a/b/c")
-open("a/b/c/fileC.txt", fmWrite).close
+block: # moveFile
+  let tempDir = getTempDir() / "D20210609T151608"
+  createDir(tempDir)
+  defer: removeDir(tempDir)
 
-copyDir("a", "../dest/a")
-removeDir("a")
+  writeFile(tempDir / "a.txt", "")
+  moveFile(tempDir / "a.txt", tempDir / "b.txt")
+  doAssert not fileExists(tempDir / "a.txt")
+  doAssert fileExists(tempDir / "b.txt")
+  removeFile(tempDir / "b.txt")
 
-echo dirExists("../dest/a/b")
-echo fileExists("../dest/a/b/file.txt")
+  createDir(tempDir / "moveFile_test")
+  writeFile(tempDir / "moveFile_test/a.txt", "")
+  moveFile(tempDir / "moveFile_test/a.txt", tempDir / "moveFile_test/b.txt")
+  doAssert not fileExists(tempDir / "moveFile_test/a.txt")
+  doAssert fileExists(tempDir / "moveFile_test/b.txt")
+  removeDir(tempDir / "moveFile_test")
 
-echo fileExists("../dest/a/b/c/fileC.txt")
-removeDir("../dest")
+  createDir(tempDir / "moveFile_test")
+  writeFile(tempDir / "a.txt", "")
+  moveFile(tempDir / "a.txt", tempDir / "moveFile_test/b.txt")
+  doAssert not fileExists(tempDir / "a.txt")
+  doAssert fileExists(tempDir / "moveFile_test/b.txt")
+  removeDir(tempDir / "moveFile_test")
+
+block: # moveDir
+  let tempDir = getTempDir() / "D20210609T161443"
+  createDir(tempDir)
+  defer: removeDir(tempDir)
+
+  createDir(tempDir / "moveDir_test")
+  moveDir(tempDir / "moveDir_test/", tempDir / "moveDir_test_dest")
+  doAssert not dirExists(tempDir / "moveDir_test")
+  doAssert dirExists(tempDir / "moveDir_test_dest")
+  removeDir(tempDir / "moveDir_test_dest")
+
+  createDir(tempDir / "moveDir_test")
+  writeFile(tempDir / "moveDir_test/a.txt", "")
+  moveDir(tempDir / "moveDir_test", tempDir / "moveDir_test_dest")
+  doAssert not dirExists(tempDir / "moveDir_test")
+  doAssert not fileExists(tempDir / "moveDir_test/a.txt")
+  doAssert dirExists(tempDir / "moveDir_test_dest")
+  doAssert fileExists(tempDir / "moveDir_test_dest/a.txt")
+  removeDir(tempDir / "moveDir_test_dest")
 
-# Test get/set modification times
-# Should support at least microsecond resolution
 import times
-let tm = fromUnix(0) + 100.microseconds
-writeFile("a", "")
-setLastModificationTime("a", tm)
-echo getLastModificationTime("a") == tm
-removeFile("a")
\ No newline at end of file
+block modificationTime:
+  # Test get/set modification times
+  # Should support at least microsecond resolution
+  let tm = fromUnix(0) + 100.microseconds
+  writeFile("a", "")
+  setLastModificationTime("a", tm)
+
+  when defined(macosx):
+    doAssert true
+  else:
+    doAssert getLastModificationTime("a") == tm
+  removeFile("a")
+
+block walkDirRec:
+  createDir("walkdir_test/a/b")
+  open("walkdir_test/a/b/file_1", fmWrite).close()
+  open("walkdir_test/a/file_2", fmWrite).close()
+
+  for p in walkDirRec("walkdir_test"):
+    doAssert p.fileExists
+    doAssert p.startsWith("walkdir_test")
+
+  var s: seq[string]
+  for p in walkDirRec("walkdir_test", {pcFile}, {pcDir}, relative = true):
+    s.add(p)
+
+  doAssert s.len == 2
+  doAssert "a" / "b" / "file_1" in s
+  doAssert "a" / "file_2" in s
+
+  removeDir("walkdir_test")
+
+import std/sequtils
+
+block: # walkDir
+  doAssertRaises(OSError):
+    for a in walkDir("nonexistent", checkDir = true): discard
+  doAssertRaises(OSError):
+    for p in walkDirRec("nonexistent", checkDir = true): discard
+
+  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")
+
+  when defined(posix):
+    block walkDirSpecial:
+      createDir("walkdir_test")
+      doAssert execShellCmd("mkfifo walkdir_test/fifo") == 0
+      createSymlink("fifo", "walkdir_test/fifo_link")
+      let withSpecialFiles = toSeq(walkDir("walkdir_test", relative = true))
+      doAssert (withSpecialFiles.len == 2 and
+                (pcFile, "fifo") in withSpecialFiles and
+                (pcLinkToFile, "fifo_link") in withSpecialFiles)
+      # now Unix special files are excluded from walkdir output:
+      let skipSpecialFiles = toSeq(walkDir("walkdir_test", relative = true,
+                                           skipSpecial = true))
+      doAssert skipSpecialFiles.len == 0
+      removeDir("walkdir_test")
+
+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):
+    doAssert ".foo.txt".isHidden
+    doAssert "bar/.foo.ext".isHidden
+    doAssert not "bar".isHidden
+    doAssert not "foo/".isHidden
+    doAssert ".foo/.".isHidden
+    # Corner cases: `isHidden` is not yet `..` aware
+    doAssert not ".foo/..".isHidden
+
+block absolutePath:
+  doAssertRaises(ValueError): discard absolutePath("a", "b")
+  doAssert absolutePath("a") == getCurrentDir() / "a"
+  doAssert absolutePath("a", "/b") == "/b" / "a"
+  when defined(posix):
+    doAssert absolutePath("a", "/b/") == "/b" / "a"
+    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", "..", "")
+  doAssert splitFile("/foo/abc....txt") == ("/foo", "abc...", ".txt")
+
+# execShellCmd is tested in tosproc
+
+block ospaths:
+  doAssert unixToNativePath("") == ""
+  doAssert unixToNativePath(".") == $CurDir
+  doAssert unixToNativePath("..") == $ParDir
+  doAssert isAbsolute(unixToNativePath("/"))
+  doAssert isAbsolute(unixToNativePath("/", "a"))
+  doAssert isAbsolute(unixToNativePath("/a"))
+  doAssert isAbsolute(unixToNativePath("/a", "a"))
+  doAssert isAbsolute(unixToNativePath("/a/b"))
+  doAssert isAbsolute(unixToNativePath("/a/b", "a"))
+  doAssert unixToNativePath("a/b") == joinPath("a", "b")
+
+  when defined(macos):
+    doAssert unixToNativePath("./") == ":"
+    doAssert unixToNativePath("./abc") == ":abc"
+    doAssert unixToNativePath("../abc") == "::abc"
+    doAssert unixToNativePath("../../abc") == ":::abc"
+    doAssert unixToNativePath("/abc", "a") == "abc"
+    doAssert unixToNativePath("/abc/def", "a") == "abc:def"
+  elif doslikeFileSystem:
+    doAssert unixToNativePath("./") == ".\\"
+    doAssert unixToNativePath("./abc") == ".\\abc"
+    doAssert unixToNativePath("../abc") == "..\\abc"
+    doAssert unixToNativePath("../../abc") == "..\\..\\abc"
+    doAssert unixToNativePath("/abc", "a") == "a:\\abc"
+    doAssert unixToNativePath("/abc/def", "a") == "a:\\abc\\def"
+  else:
+    #Tests for unix
+    doAssert unixToNativePath("./") == "./"
+    doAssert unixToNativePath("./abc") == "./abc"
+    doAssert unixToNativePath("../abc") == "../abc"
+    doAssert unixToNativePath("../../abc") == "../../abc"
+    doAssert unixToNativePath("/abc", "a") == "/abc"
+    doAssert unixToNativePath("/abc/def", "a") == "/abc/def"
+
+  block extractFilenameTest:
+    doAssert extractFilename("") == ""
+    when defined(posix):
+      doAssert extractFilename("foo/bar") == "bar"
+      doAssert extractFilename("foo/bar.txt") == "bar.txt"
+      doAssert extractFilename("foo/") == ""
+      doAssert extractFilename("/") == ""
+    when doslikeFileSystem:
+      doAssert extractFilename(r"foo\bar") == "bar"
+      doAssert extractFilename(r"foo\bar.txt") == "bar.txt"
+      doAssert extractFilename(r"foo\") == ""
+      doAssert extractFilename(r"C:\") == ""
+
+  block lastPathPartTest:
+    doAssert lastPathPart("") == ""
+    when defined(posix):
+      doAssert lastPathPart("foo/bar.txt") == "bar.txt"
+      doAssert lastPathPart("foo/") == "foo"
+      doAssert lastPathPart("/") == ""
+    when doslikeFileSystem:
+      doAssert lastPathPart(r"foo\bar.txt") == "bar.txt"
+      doAssert lastPathPart(r"foo\") == "foo"
+
+  template canon(x): untyped = normalizePath(x, '/')
+  doAssert canon"/foo/../bar" == "/bar"
+  doAssert canon"foo/../bar" == "bar"
+
+  doAssert canon"/f/../bar///" == "/bar"
+  doAssert canon"f/..////bar" == "bar"
+
+  doAssert canon"../bar" == "../bar"
+  doAssert canon"/../bar" == "/../bar"
+
+  doAssert canon("foo/../../bar/") == "../bar"
+  doAssert canon("./bla/blob/") == "bla/blob"
+  doAssert canon(".hiddenFile") == ".hiddenFile"
+  doAssert canon("./bla/../../blob/./zoo.nim") == "../blob/zoo.nim"
+
+  doAssert canon("C:/file/to/this/long") == "C:/file/to/this/long"
+  doAssert canon("") == ""
+  doAssert canon("foobar") == "foobar"
+  doAssert canon("f/////////") == "f"
+
+  doAssert relativePath("/foo/bar//baz.nim", "/foo", '/') == "bar/baz.nim"
+  doAssert normalizePath("./foo//bar/../baz", '/') == "foo/baz"
+
+  doAssert relativePath("/Users/me/bar/z.nim", "/Users/other/bad", '/') == "../../me/bar/z.nim"
+
+  doAssert relativePath("/Users/me/bar/z.nim", "/Users/other", '/') == "../me/bar/z.nim"
+
+  # `//` is a UNC path, `/` is the current working directory's drive, so can't
+  # run this test on Windows.
+  when not doslikeFileSystem:
+    doAssert relativePath("/Users///me/bar//z.nim", "//Users/", '/') == "me/bar/z.nim"
+  doAssert relativePath("/Users/me/bar/z.nim", "/Users/me", '/') == "bar/z.nim"
+  doAssert relativePath("", "/users/moo", '/') == ""
+  doAssert relativePath("foo", "", '/') == "foo"
+  doAssert relativePath("/foo", "/Foo", '/') == (when FileSystemCaseSensitive: "../foo" else: ".")
+  doAssert relativePath("/Foo", "/foo", '/') == (when FileSystemCaseSensitive: "../Foo" else: ".")
+  doAssert relativePath("/foo", "/fOO", '/') == (when FileSystemCaseSensitive: "../foo" else: ".")
+  doAssert relativePath("/foO", "/foo", '/') == (when FileSystemCaseSensitive: "../foO" else: ".")
+
+  doAssert relativePath("foo", ".", '/') == "foo"
+  doAssert relativePath(".", ".", '/') == "."
+  doAssert relativePath("..", ".", '/') == ".."
+
+  doAssert relativePath("foo", "foo") == "."
+  doAssert relativePath("", "foo") == ""
+  doAssert relativePath("././/foo", "foo//./") == "."
+
+  doAssert relativePath(getCurrentDir() / "bar", "foo") == "../bar".unixToNativePath
+  doAssert relativePath("bar", getCurrentDir() / "foo") == "../bar".unixToNativePath
+
+  when doslikeFileSystem:
+    doAssert relativePath(r"c:\foo.nim", r"C:\") == r"foo.nim"
+    doAssert relativePath(r"c:\foo\bar\baz.nim", r"c:\foo") == r"bar\baz.nim"
+    doAssert relativePath(r"c:\foo\bar\baz.nim", r"d:\foo") == r"c:\foo\bar\baz.nim"
+    doAssert relativePath(r"\foo\baz.nim", r"\foo") == r"baz.nim"
+    doAssert relativePath(r"\foo\bar\baz.nim", r"\bar") == r"..\foo\bar\baz.nim"
+    doAssert relativePath(r"\\foo\bar\baz.nim", r"\\foo\bar") == r"baz.nim"
+    doAssert relativePath(r"\\foo\bar\baz.nim", r"\\foO\bar") == r"baz.nim"
+    doAssert relativePath(r"\\foo\bar\baz.nim", r"\\bar\bar") == r"\\foo\bar\baz.nim"
+    doAssert relativePath(r"\\foo\bar\baz.nim", r"\\foo\car") == r"\\foo\bar\baz.nim"
+    doAssert relativePath(r"\\foo\bar\baz.nim", r"\\goo\bar") == r"\\foo\bar\baz.nim"
+    doAssert relativePath(r"\\foo\bar\baz.nim", r"c:\") == r"\\foo\bar\baz.nim"
+    doAssert relativePath(r"\\foo\bar\baz.nim", r"\foo") == r"\\foo\bar\baz.nim"
+    doAssert relativePath(r"c:\foo.nim", r"\foo") == r"c:\foo.nim"
+
+  doAssert joinPath("usr", "") == unixToNativePath"usr"
+  doAssert joinPath("", "lib") == "lib"
+  doAssert joinPath("", "/lib") == unixToNativePath"/lib"
+  doAssert joinPath("usr/", "/lib") == unixToNativePath"usr/lib"
+  doAssert joinPath("", "") == unixToNativePath"" # issue #13455
+  doAssert joinPath("", "/") == unixToNativePath"/"
+  doAssert joinPath("/", "/") == unixToNativePath"/"
+  doAssert joinPath("/", "") == unixToNativePath"/"
+  doAssert joinPath("/" / "") == unixToNativePath"/" # weird test case...
+  doAssert joinPath("/", "/a/b/c") == unixToNativePath"/a/b/c"
+  doAssert joinPath("foo/", "") == unixToNativePath"foo/"
+  doAssert joinPath("foo/", "abc") == unixToNativePath"foo/abc"
+  doAssert joinPath("foo//./", "abc/.//") == unixToNativePath"foo/abc/"
+  doAssert joinPath("foo", "abc") == unixToNativePath"foo/abc"
+  doAssert joinPath("", "abc") == unixToNativePath"abc"
+
+  doAssert joinPath("zook/.", "abc") == unixToNativePath"zook/abc"
+
+  # controversial: inconsistent with `joinPath("zook/.","abc")`
+  # on linux, `./foo` and `foo` are treated a bit differently for executables
+  # but not `./foo/bar` and `foo/bar`
+  doAssert joinPath(".", "/lib") == unixToNativePath"./lib"
+  doAssert joinPath(".", "abc") == unixToNativePath"./abc"
+
+  # cases related to issue #13455
+  doAssert joinPath("foo", "", "") == "foo"
+  doAssert joinPath("foo", "") == "foo"
+  doAssert joinPath("foo/", "") == unixToNativePath"foo/"
+  doAssert joinPath("foo/", ".") == "foo"
+  doAssert joinPath("foo", "./") == unixToNativePath"foo/"
+  doAssert joinPath("foo", "", "bar/") == unixToNativePath"foo/bar/"
+
+  # issue #13579
+  doAssert joinPath("/foo", "../a") == unixToNativePath"/a"
+  doAssert joinPath("/foo/", "../a") == unixToNativePath"/a"
+  doAssert joinPath("/foo/.", "../a") == unixToNativePath"/a"
+  doAssert joinPath("/foo/.b", "../a") == unixToNativePath"/foo/a"
+  doAssert joinPath("/foo///", "..//a/") == unixToNativePath"/a/"
+  doAssert joinPath("foo/", "../a") == unixToNativePath"a"
+
+  when doslikeFileSystem:
+    doAssert joinPath("C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Common7\\Tools\\", "..\\..\\VC\\vcvarsall.bat") == r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
+    doAssert joinPath("C:\\foo", "..\\a") == r"C:\a"
+    doAssert joinPath("C:\\foo\\", "..\\a") == r"C:\a"
+
+block getTempDir:
+  block TMPDIR:
+    # TMPDIR env var is not used if either of these are defined.
+    when not (defined(tempDir) or defined(windows) or defined(android)):
+      if existsEnv("TMPDIR"):
+        let origTmpDir = getEnv("TMPDIR")
+        putEnv("TMPDIR", "/mytmp")
+        doAssert getTempDir() == "/mytmp/"
+        delEnv("TMPDIR")
+        doAssert getTempDir() == "/tmp/"
+        putEnv("TMPDIR", origTmpDir)
+      else:
+        doAssert getTempDir() == "/tmp/"
+
+block: # getCacheDir
+  doAssert getCacheDir().dirExists
+
+block isRelativeTo:
+  doAssert isRelativeTo("/foo", "/")
+  doAssert isRelativeTo("/foo/bar", "/foo")
+  doAssert isRelativeTo("foo/bar", "foo")
+  doAssert isRelativeTo("/foo/bar.nim", "/foo/bar.nim")
+  doAssert isRelativeTo("./foo/", "foo")
+  doAssert isRelativeTo("foo", "./foo/")
+  doAssert isRelativeTo(".", ".")
+  doAssert isRelativeTo("foo/bar", ".")
+  doAssert not isRelativeTo("foo/bar.nims", "foo/bar.nim")
+  doAssert not isRelativeTo("/foo2", "/foo")
+
+block: # quoteShellWindows
+  doAssert quoteShellWindows("aaa") == "aaa"
+  doAssert quoteShellWindows("aaa\"") == "aaa\\\""
+  doAssert quoteShellWindows("") == "\"\""
+
+block: # quoteShellCommand
+  when defined(windows):
+    doAssert quoteShellCommand(["a b c", "d", "e"]) == """"a b c" d e"""
+    doAssert quoteShellCommand(["""ab"c""", r"\", "d"]) == """ab\"c \ d"""
+    doAssert quoteShellCommand(["""ab"c""", """ \""", "d"]) == """ab\"c " \\" d"""
+    doAssert quoteShellCommand(["""a\\\b""", """de fg""", "h"]) == """a\\\b "de fg" h"""
+    doAssert quoteShellCommand(["""a\"b""", "c", "d"]) == """a\\\"b c d"""
+    doAssert quoteShellCommand(["""a\\b c""", "d", "e"]) == """"a\\b c" d e"""
+    doAssert quoteShellCommand(["""a\\b\ c""", "d", "e"]) == """"a\\b\ c" d e"""
+    doAssert quoteShellCommand(["ab", ""]) == """ab """""
+
+block: # quoteShellPosix
+  doAssert quoteShellPosix("aaa") == "aaa"
+  doAssert quoteShellPosix("aaa a") == "'aaa a'"
+  doAssert quoteShellPosix("") == "''"
+  doAssert quoteShellPosix("a'a") == "'a'\"'\"'a'"
+
+block: # quoteShell
+  when defined(posix):
+    doAssert quoteShell("") == "''"
+
+block: # normalizePathEnd
+  # handle edge cases correctly: shouldn't affect whether path is
+  # absolute/relative
+  doAssert "".normalizePathEnd(true) == ""
+  doAssert "".normalizePathEnd(false) == ""
+  doAssert "/".normalizePathEnd(true) == $DirSep
+  doAssert "/".normalizePathEnd(false) == $DirSep
+
+  when defined(posix):
+    doAssert "//".normalizePathEnd(false) == "/"
+    doAssert "foo.bar//".normalizePathEnd == "foo.bar"
+    doAssert "bar//".normalizePathEnd(trailingSep = true) == "bar/"
+  when defined(windows):
+    doAssert r"C:\foo\\".normalizePathEnd == r"C:\foo"
+    doAssert r"C:\foo".normalizePathEnd(trailingSep = true) == r"C:\foo\"
+    # this one is controversial: we could argue for returning `D:\` instead,
+    # but this is simplest.
+    doAssert r"D:\".normalizePathEnd == r"D:"
+    doAssert r"E:/".normalizePathEnd(trailingSep = true) == r"E:\"
+    doAssert "/".normalizePathEnd == r"\"
+
+
+import sugar
+
+block: # normalizeExe
+  doAssert "".dup(normalizeExe) == ""
+  when defined(posix):
+    doAssert "foo".dup(normalizeExe) == "./foo"
+    doAssert "foo/../bar".dup(normalizeExe) == "foo/../bar"
+  when defined(windows):
+    doAssert "foo".dup(normalizeExe) == "foo"
+
+block: # isAdmin
+  let isAzure = existsEnv("TF_BUILD") # xxx factor with testament.specs.isAzure
+  # In Azure on Windows tests run as an admin user
+  if isAzure and defined(windows): doAssert isAdmin()
+  # In Azure on POSIX tests run as a normal user
+  if isAzure and defined(posix): doAssert not isAdmin()
+
+
+import sugar
+
+block: # normalizeExe
+  doAssert "".dup(normalizeExe) == ""
+  when defined(posix):
+    doAssert "foo".dup(normalizeExe) == "./foo"
+    doAssert "foo/../bar".dup(normalizeExe) == "foo/../bar"
+  when defined(windows):
+    doAssert "foo".dup(normalizeExe) == "foo"
+
+block: # isAdmin
+  let isAzure = existsEnv("TF_BUILD") # xxx factor with testament.specs.isAzure
+  # In Azure on Windows tests run as an admin user
+  if isAzure and defined(windows): doAssert isAdmin()
+  # In Azure on POSIX tests run as a normal user
+  if isAzure and defined(posix): doAssert not isAdmin()
+
+when doslikeFileSystem:
+  import std/private/ntpath
+
+  block: # Bug #19103 UNC paths
+
+    # Easiest way of generating a valid, readable and writable UNC path
+    let tempDir = r"\\?\" & getTempDir()
+    doAssert dirExists tempDir
+    createDir tempDir / "test"
+    removeDir tempDir / "test"
+    createDir tempDir / "recursive" / "test"
+    removeDir tempDir / "recursive" / "test"
+
+    let tempDir2 = getTempDir()
+    let (drive, pathNoDrive) = splitDrive(tempDir2)
+    setCurrentDir drive
+    doAssert cmpIgnoreCase(getCurrentDir().splitDrive.drive, drive) == 0
+
+    # Test `\Users` path syntax on Windows by stripping away drive. `\`
+    # resolves to the drive in current working directory. This drive will be
+    # the same as `tempDir2` because of the `setCurrentDir` above.
+    doAssert pathNoDrive[0] == '\\'
+    createDir pathNoDrive / "test"
+    doAssert dirExists pathNoDrive / "test"
+    removeDir pathNoDrive / "test"
+
+    doAssert splitPath("//?/c:") == ("//?/c:", "")
+
+    doAssert relativePath("//?/c:///Users//me", "//?/c:", '/') == "Users/me"
+
+    doAssert parentDir(r"\\?\c:") == r""
+    doAssert parentDir(r"//?/c:/Users") == r"\\?\c:"
+    doAssert parentDir(r"\\localhost\c$") == r""
+    doAssert parentDir(r"\Users") == r"\"
+
+    doAssert tailDir("//?/c:") == ""
+    doAssert tailDir("//?/c:/Users") == "Users"
+    doAssert tailDir(r"\\localhost\c$\Windows\System32") == r"Windows\System32"
+
+    doAssert isRootDir("//?/c:")
+    doAssert isRootDir("//?/UNC/localhost/c$")
+    doAssert not isRootDir(r"\\?\c:\Users")
+
+    doAssert parentDirs(r"C:\Users", fromRoot = true).toSeq == @[r"C:\", r"C:\Users"]
+    doAssert parentDirs(r"C:\Users", fromRoot = false).toSeq == @[r"C:\Users", r"C:"]
+    doAssert parentDirs(r"\\?\c:\Users", fromRoot = true).toSeq ==
+      @[r"\\?\c:\", r"\\?\c:\Users"]
+    doAssert parentDirs(r"\\?\c:\Users", fromRoot = false).toSeq ==
+      @[r"\\?\c:\Users", r"\\?\c:"]
+    doAssert parentDirs(r"//localhost/c$/Users", fromRoot = true).toSeq ==
+      @[r"//localhost/c$/", r"//localhost/c$/Users"]
+    doAssert parentDirs(r"//?/UNC/localhost/c$/Users", fromRoot = false).toSeq ==
+      @[r"//?/UNC/localhost/c$/Users", r"\\?\UNC\localhost\c$"]
+    doAssert parentDirs(r"\Users", fromRoot = true).toSeq == @[r"\", r"\Users"]
+    doAssert parentDirs(r"\Users", fromRoot = false).toSeq == @[r"\Users", r"\"]
+
+    doAssert r"//?/c:" /../ "d/e" == r"\\?\c:\d\e"
+    doAssert r"//?/c:/Users" /../ "d/e" == r"\\?\c:\d\e"
+    doAssert r"\\localhost\c$" /../ "d/e" == r"\\localhost\c$\d\e"
+
+    doAssert splitFile("//?/c:") == ("//?/c:", "", "")
+    doAssert splitFile("//?/c:/Users") == ("//?/c:", "Users", "")
+    doAssert splitFile(r"\\localhost\c$\test.txt") == (r"\\localhost\c$", "test", ".txt")
+
+else:
+  block: # parentDirs
+    doAssert parentDirs("/home", fromRoot=true).toSeq == @["/", "/home"]
+    doAssert parentDirs("/home", fromRoot=false).toSeq == @["/home", "/"]
+    doAssert parentDirs("home", fromRoot=true).toSeq == @["home"]
+    doAssert parentDirs("home", fromRoot=false).toSeq == @["home"]
+
+    doAssert parentDirs("/home/user", fromRoot=true).toSeq == @["/", "/home/", "/home/user"]
+    doAssert parentDirs("/home/user", fromRoot=false).toSeq == @["/home/user", "/home", "/"]
+    doAssert parentDirs("home/user", fromRoot=true).toSeq == @["home/", "home/user"]
+    doAssert parentDirs("home/user", fromRoot=false).toSeq == @["home/user", "home"]
+
+
+# https://github.com/nim-lang/Nim/pull/19643#issuecomment-1235102314
+block:  # isValidFilename
+  # Negative Tests.
+  doAssert not isValidFilename("abcd", maxLen = 2)
+  doAssert not isValidFilename("0123456789", maxLen = 8)
+  doAssert not isValidFilename("con")
+  doAssert not isValidFilename("aux")
+  doAssert not isValidFilename("prn")
+  doAssert not isValidFilename("OwO|UwU")
+  doAssert not isValidFilename(" foo")
+  doAssert not isValidFilename("foo ")
+  doAssert not isValidFilename("foo.")
+  doAssert not isValidFilename("con.txt")
+  doAssert not isValidFilename("aux.bat")
+  doAssert not isValidFilename("prn.exe")
+  doAssert not isValidFilename("nim>.nim")
+  doAssert not isValidFilename(" foo.log")
+  # Positive Tests.
+  doAssert isValidFilename("abcd", maxLen = 42.Positive)
+  doAssert isValidFilename("c0n")
+  doAssert isValidFilename("foo.aux")
+  doAssert isValidFilename("bar.prn")
+  doAssert isValidFilename("OwO_UwU")
+  doAssert isValidFilename("cron")
+  doAssert isValidFilename("ux.bat")
+  doAssert isValidFilename("nim.nim")
+  doAssert isValidFilename("foo.log")
+
+block: # searchExtPos
+  doAssert "foo.nim".searchExtPos == 3
+  doAssert "/foo.nim".searchExtPos == 4
+  doAssert "".searchExtPos == -1
+  doAssert "/".searchExtPos == -1
+  doAssert "a.b/foo".searchExtPos == -1
+  doAssert ".".searchExtPos == -1
+  doAssert "foo.".searchExtPos == 3
+  doAssert "foo..".searchExtPos == 4
+  doAssert "..".searchExtPos == -1
+  doAssert "...".searchExtPos == -1
+  doAssert "./".searchExtPos == -1
+  doAssert "../".searchExtPos == -1
+  doAssert "/.".searchExtPos == -1
+  doAssert "/..".searchExtPos == -1
+  doAssert ".b".searchExtPos == -1
+  doAssert "..b".searchExtPos == -1
+  doAssert "/.b".searchExtPos == -1
+  doAssert "a/.b".searchExtPos == -1
+  doAssert ".a.b".searchExtPos == 2
+  doAssert "a/.b.c".searchExtPos == 4
+  doAssert "a/..b".searchExtPos == -1
+  doAssert "a/b..c".searchExtPos == 4
+
+  when doslikeFileSystem:
+    doAssert "c:a.b".searchExtPos == 3
+    doAssert "c:.a".searchExtPos == -1
+    doAssert r"c:\.a".searchExtPos == -1
+    doAssert "c:..a".searchExtPos == -1
+    doAssert r"c:\..a".searchExtPos == -1
+    doAssert "c:.a.b".searchExtPos == 4
diff --git a/tests/stdlib/tos_unc.nim b/tests/stdlib/tos_unc.nim
new file mode 100644
index 000000000..194deeb42
--- /dev/null
+++ b/tests/stdlib/tos_unc.nim
@@ -0,0 +1,11 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  disabled: "posix"
+"""
+
+# bug 10952, UNC paths
+import os
+import std/assertions
+
+doAssert r"\\hostname\foo\bar" / "baz" == r"\\hostname\foo\bar\baz"
+doAssert r"\\?\C:\foo" / "bar" == r"\\?\C:\foo\bar"
diff --git a/tests/stdlib/tosenv.nim b/tests/stdlib/tosenv.nim
new file mode 100644
index 000000000..17e397987
--- /dev/null
+++ b/tests/stdlib/tosenv.nim
@@ -0,0 +1,163 @@
+discard """
+  matrix: "--mm:refc; --mm:arc"
+  joinable: false
+  targets: "c js cpp"
+"""
+
+import std/os
+from std/sequtils import toSeq
+import stdtest/testutils
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions]
+
+# "LATIN CAPITAL LETTER AE" in UTF-8 (0xc386)
+const unicodeUtf8 = "\xc3\x86"
+
+template main =
+  block: # delEnv, existsEnv, getEnv, envPairs
+    for val in ["val", "", unicodeUtf8]: # ensures empty val works too
+      const key = "NIM_TESTS_TOSENV_KEY"
+      doAssert not existsEnv(key)
+
+      putEnv(key, "tempval")
+      doAssert existsEnv(key)
+      doAssert getEnv(key) == "tempval"
+
+      putEnv(key, val) # change a key that already exists
+      doAssert existsEnv(key)
+      doAssert getEnv(key) == val
+
+      doAssert (key, val) in toSeq(envPairs())
+      delEnv(key)
+      doAssert (key, val) notin toSeq(envPairs())
+      doAssert not existsEnv(key)
+      delEnv(key) # deleting an already deleted env var
+      doAssert not existsEnv(key)
+
+    block:
+      doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", "") == ""
+      doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", " ") == " "
+      doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", "defval") == "defval"
+
+    whenVMorJs: discard # xxx improve
+    do:
+      doAssertRaises(OSError, putEnv("NIM_TESTS_TOSENV_PUT=DUMMY_VALUE", "NEW_DUMMY_VALUE"))
+      doAssertRaises(OSError, putEnv("", "NEW_DUMMY_VALUE"))
+      doAssert not existsEnv("")
+      doAssert not existsEnv("NIM_TESTS_TOSENV_PUT=DUMMY_VALUE")
+      doAssert not existsEnv("NIM_TESTS_TOSENV_PUT")
+
+static: main()
+main()
+
+when defined(windows):
+  import std/widestrs
+  proc c_wgetenv(env: WideCString): WideCString {.importc: "_wgetenv", header: "<stdlib.h>".}
+proc c_getenv(env: cstring): cstring {.importc: "getenv", header: "<stdlib.h>".}
+
+when not defined(js) and not defined(nimscript):
+  when defined(nimPreviewSlimSystem):
+    import std/typedthreads
+  block: # bug #18533
+    var thr: Thread[void]
+    proc threadFunc {.thread.} = putEnv("foo", "fooVal2")
+
+    putEnv("foo", "fooVal1")
+    doAssert getEnv("foo") == "fooVal1"
+    createThread(thr, threadFunc)
+    joinThreads(thr)
+    when defined(windows):
+      doAssert getEnv("foo") == $c_wgetenv("foo".newWideCString)
+    else:
+      doAssert getEnv("foo") == $c_getenv("foo".cstring)
+
+    doAssertRaises(OSError): delEnv("foo=bar")
+
+when defined(windows) and not defined(nimscript):
+  import std/encodings
+
+  proc c_putenv(env: cstring): int32 {.importc: "putenv", header: "<stdlib.h>".}
+  proc c_wputenv(env: WideCString): int32 {.importc: "_wputenv", header: "<stdlib.h>".}
+
+  block: # Bug #20083
+    # These test that `getEnv`, `putEnv` and `existsEnv` handle Unicode
+    # characters correctly. This means that module X in the process calling the
+    # CRT environment variable API will get the correct string. Raw CRT API
+    # calls below represent module X.
+
+    # Getting an env. var. with unicode characters returns the correct UTF-8
+    # encoded string.
+    block:
+      const envName = "twin_envvars1"
+      doAssert c_wputenv(newWideCString(envName & "=" & unicodeUtf8)) == 0
+      doAssert existsEnv(envName)
+      doAssert getEnv(envName) == unicodeUtf8
+
+    # Putting an env. var. with unicode characters gives the correct UTF-16
+    # encoded string from low-level routine.
+    block:
+      const envName = "twin_envvars2"
+      putEnv(envName, unicodeUtf8)
+      doAssert $c_wgetenv(envName.newWideCString) == unicodeUtf8
+
+    # Env. name containing Unicode characters is retrieved correctly
+    block:
+      const envName = unicodeUtf8 & "1"
+      doAssert c_wputenv(newWideCString(envName & "=" & unicodeUtf8)) == 0
+      doAssert existsEnv(envName)
+      doAssert getEnv(envName) == unicodeUtf8
+
+    # Env. name containing Unicode characters is set correctly
+    block:
+      const envName = unicodeUtf8 & "2"
+      putEnv(envName, unicodeUtf8)
+      doAssert existsEnv(envName)
+      doAssert $c_wgetenv(envName.newWideCString) == unicodeUtf8
+
+    # Env. name containing Unicode characters and empty value is set correctly
+    block:
+      const envName = unicodeUtf8 & "3"
+      putEnv(envName, "")
+      doAssert existsEnv(envName)
+      doAssert $c_wgetenv(envName.newWideCString) == ""
+
+    # It's hard to test on Windows code pages, because there is no "change
+    # a process' locale" API.
+    if getCurrentEncoding(true) == "windows-1252":
+      const
+        unicodeAnsi = "\xc6" # `unicodeUtf8` in `windows-1252` encoding
+
+      # Test that env. var. ANSI API has correct encoding
+      block:
+        const
+          envName = unicodeUtf8 & "4"
+          envNameAnsi = unicodeAnsi & "4"
+        putEnv(envName, unicodeUtf8)
+        doAssert $c_getenv(envNameAnsi.cstring) == unicodeAnsi
+
+      block:
+        const
+          envName = unicodeUtf8 & "5"
+          envNameAnsi = unicodeAnsi & "5"
+        doAssert c_putenv((envNameAnsi & "=" & unicodeAnsi).cstring) == 0
+        doAssert getEnv(envName) == unicodeUtf8
+
+      # Env. name containing Unicode characters and empty value is set correctly;
+      # and, if env. name. characters cannot be represented in codepage, don't
+      # raise an error.
+      #
+      # `win_setenv.nim` converts UTF-16 to ANSI when setting empty env. var. The
+      # windows-1250 locale has no representation of `abreveUtf8` below, so the
+      # conversion will fail, but this must not be fatal. It is expected that the
+      # routine ignores updating MBCS environment (`environ` global) and carries
+      # on.
+      block:
+        const
+          # "LATIN SMALL LETTER A WITH BREVE" in UTF-8
+          abreveUtf8 = "\xc4\x83"
+          envName = abreveUtf8 & "6"
+        putEnv(envName, "")
+        doAssert existsEnv(envName)
+        doAssert $c_wgetenv(envName.newWideCString) == ""
+        doAssert getEnv(envName) == ""
diff --git a/tests/stdlib/toserrors.nim b/tests/stdlib/toserrors.nim
new file mode 100644
index 000000000..e907dfe63
--- /dev/null
+++ b/tests/stdlib/toserrors.nim
@@ -0,0 +1,9 @@
+discard """
+  action: compile
+"""
+
+import std/oserrors
+
+let x1 = osLastError()
+raiseOSError(x1)
+echo osErrorMsg(x1)
diff --git a/tests/stdlib/tosproc.nim b/tests/stdlib/tosproc.nim
new file mode 100644
index 000000000..da4f6252d
--- /dev/null
+++ b/tests/stdlib/tosproc.nim
@@ -0,0 +1,316 @@
+discard """
+matrix: "--mm:refc; --mm:orc"
+joinable: false
+"""
+
+#[
+joinable: false
+because it'd need cleanup up stdout
+
+see also: tests/osproc/*.nim; consider merging those into a single test here
+(easier to factor and test more things as a single self contained test)
+]#
+import std/[assertions, syncio]
+
+when defined(case_testfile): # compiled test file for child process
+  from posix import exitnow
+  proc c_exit2(code: cint): 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)
+
+  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 "exit_1":
+      if true: quit(1)
+    of "exit_2":
+      if true: quit(2)
+    of "exit_42":
+      if true: quit(42)
+    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, returning
+      # (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()
+
+elif defined(case_testfile2):
+  import strutils
+  let x = stdin.readLine()
+  echo x.parseInt + 5
+
+elif defined(case_testfile3):
+  echo "start ta_out"
+  stdout.writeLine("to stdout")
+  stdout.flushFile()
+  stdout.writeLine("to stdout")
+  stdout.flushFile()
+
+  stderr.writeLine("to stderr")
+  stderr.flushFile()
+  stderr.writeLine("to stderr")
+  stderr.flushFile()
+
+  stdout.writeLine("to stdout")
+  stdout.flushFile()
+  stdout.writeLine("to stdout")
+  stdout.flushFile()
+  echo "end ta_out"
+
+elif defined(case_testfile4):
+  import system # we could remove that
+  quit(QuitFailure)
+
+else: # main driver
+  import stdtest/[specialpaths, unittest_light]
+  import os, osproc, strutils
+  const nim = getCurrentCompilerExe()
+  const sourcePath = currentSourcePath()
+  let dir = getCurrentDir() / "tests" / "osproc"
+
+  template deferring(cleanup, body) =
+    try: body
+    finally: cleanup
+
+  # 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>".}
+
+  proc compileNimProg(opt: string, name: string): string =
+    result = buildDir / name.addFileExt(ExeExt)
+    let cmd = "$# c -o:$# --hints:off $# $#" % [nim.quoteShell, result.quoteShell, opt, sourcePath.quoteShell]
+    doAssert c_system(cmd) == 0, $cmd
+    doAssert result.fileExists
+
+  block execShellCmdTest:
+    let output = compileNimProg("-d:release -d:case_testfile", "D20190111T024543")
+
+    ## 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)
+    when defined(posix):
+      runTest("quit_139", 127) # The quit value gets saturated to 127
+    else:
+      runTest("quit_139", 139)
+
+  block execCmdTest:
+    let output = compileNimProg("-d:release -d:case_testfile", "D20220705T221100")
+    doAssert execCmd(output & " exit_0") == 0
+    doAssert execCmd(output & " exit_1") == 1
+    doAssert execCmd(output & " exit_2") == 2
+    doAssert execCmd(output & " exit_42") == 42
+
+  import std/streams
+
+  block execProcessTest:
+    let dir = sourcePath.parentDir
+    let (_, 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)
+
+    # test for PipeOutStream
+    var
+      p = startProcess(exePath, args = ["abcdefghi", "foo", "bar", "0123456"])
+      outStrm = p.peekableOutputStream
+
+    var tmp: string
+    doAssert outStrm.readLine(tmp)
+    doAssert outStrm.readChar == 'a'
+    doAssert outStrm.peekChar == 'b'
+    doAssert outStrm.readChar == 'b'
+    doAssert outStrm.readChar == 'c'
+    doAssert outStrm.peekChar == 'd'
+    doAssert outStrm.peekChar == 'd'
+    doAssert outStrm.readChar == 'd'
+    doAssert outStrm.readStr(2) == "ef"
+    doAssert outStrm.peekStr(2) == "gh"
+    doAssert outStrm.peekStr(2) == "gh"
+    doAssert outStrm.readStr(1) == "g"
+    doAssert outStrm.readStr(3) == "hi\n"
+
+    doAssert outStrm.readLine == "foo"
+    doAssert outStrm.readChar == 'b'
+    doAssert outStrm.peekChar == 'a'
+    doAssert outStrm.readLine == "ar"
+
+    tmp.setLen(4)
+    tmp[0] = 'n'
+    doAssert outStrm.readDataStr(tmp, 1..3) == 3
+    doAssert tmp == "n012"
+    doAssert outStrm.peekStr(3) == "345"
+    doAssert outStrm.readDataStr(tmp, 1..2) == 2
+    doAssert tmp == "n342"
+    doAssert outStrm.peekStr(2) == "56"
+    doAssert outStrm.readDataStr(tmp, 0..3) == 3
+    doAssert tmp == "56\n2"
+    p.close
+
+    p = startProcess(exePath, args = ["123"])
+    outStrm = p.peekableOutputStream
+    let c = outStrm.peekChar
+    doAssert outStrm.readLine(tmp)
+    doAssert tmp[0] == c
+    tmp.setLen(7)
+    doAssert outStrm.peekData(addr tmp[0], 7) == 4
+    doAssert tmp[0..3] == "123\n"
+    doAssert outStrm.peekData(addr tmp[0], 7) == 4
+    doAssert tmp[0..3] == "123\n"
+    doAssert outStrm.readData(addr tmp[0], 7) == 4
+    doAssert tmp[0..3] == "123\n"
+    p.close
+
+    try:
+      removeFile(exePath)
+    except OSError:
+      discard
+
+  block: # test for startProcess (more tests needed)
+    # bugfix: windows stdin.close was a noop and led to blocking reads
+    proc startProcessTest(command: string, options: set[ProcessOption] = {
+                    poStdErrToStdOut, poUsePath}, input = ""): tuple[
+                    output: string,
+                    exitCode: int] {.tags:
+                    [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} =
+      var p = startProcess(command, options = options + {poEvalCommand})
+      var outp = outputStream(p)
+      if input.len > 0: inputStream(p).write(input)
+      close inputStream(p)
+      result = ("", -1)
+      var line = newStringOfCap(120)
+      while true:
+        if outp.readLine(line):
+          result[0].add(line)
+          result[0].add("\n")
+        else:
+          result[1] = peekExitCode(p)
+          if result[1] != -1: break
+      close(p)
+
+    var result = startProcessTest("nim r --hints:off -", options = {}, input = "echo 3*4")
+    doAssert result == ("12\n", 0)
+
+  block: # startProcess stdin (replaces old test `tstdin` + `ta_in`)
+    let output = compileNimProg("-d:case_testfile2", "D20200626T215919")
+    var p = startProcess(output, dir) # dir not needed though
+    p.inputStream.write("5\n")
+    p.inputStream.flush()
+    var line = ""
+    var s: seq[string]
+    while p.outputStream.readLine(line):
+      s.add line
+    doAssert s == @["10"]
+
+  block:
+    let output = compileNimProg("-d:case_testfile3", "D20200626T221233")
+    var x = newStringOfCap(120)
+    block: # startProcess stdout poStdErrToStdOut (replaces old test `tstdout` + `ta_out`)
+      var p = startProcess(output, dir, options={poStdErrToStdOut})
+      deferring: p.close()
+      do:
+        var sout: seq[string]
+        while p.outputStream.readLine(x): sout.add x
+        doAssert sout == @["start ta_out", "to stdout", "to stdout", "to stderr", "to stderr", "to stdout", "to stdout", "end ta_out"]
+    block: # startProcess stderr (replaces old test `tstderr` + `ta_out`)
+      var p = startProcess(output, dir, options={})
+      deferring: p.close()
+      do:
+        var serr, sout: seq[string]
+        while p.errorStream.readLine(x): serr.add x
+        while p.outputStream.readLine(x): sout.add x
+        doAssert serr == @["to stderr", "to stderr"]
+        doAssert sout == @["start ta_out", "to stdout", "to stdout", "to stdout", "to stdout", "end ta_out"]
+
+  block: # startProcess exit code (replaces old test `texitcode` + `tafalse`)
+    let output = compileNimProg("-d:case_testfile4", "D20200626T224758")
+    var p = startProcess(output, dir)
+    doAssert waitForExit(p) == QuitFailure
+    p = startProcess(output, dir)
+    var running = true
+    while running:
+      # xxx: avoid busyloop?
+      running = running(p)
+    doAssert waitForExit(p) == QuitFailure
+
+    # make sure that first call to running() after process exit returns false
+    p = startProcess(output, dir)
+    for j in 0..<30: # refs #13449
+      os.sleep(50)
+      if not running(p): break
+    doAssert not running(p)
+    doAssert waitForExit(p) == QuitFailure # avoid zombies
+
+  import std/strtabs
+  block execProcessTest:
+    var result = execCmdEx("nim r --hints:off -", options = {}, input = "echo 3*4")
+    stripLineEnd(result[0])
+    doAssert result == ("12", 0)
+    when not defined(windows):
+      doAssert execCmdEx("ls --nonexistent").exitCode != 0
+    when false:
+      # bug: on windows, this raises; on posix, passes
+      doAssert execCmdEx("nonexistent").exitCode != 0
+    when defined(posix):
+      doAssert execCmdEx("echo $FO", env = newStringTable({"FO": "B"})) == ("B\n", 0)
+      doAssert execCmdEx("echo $PWD", workingDir = "/") == ("/\n", 0)
+
+  block: # bug #17749
+    let output = compileNimProg("-d:case_testfile4", "D20210417T011153")
+    var p = startProcess(output, dir)
+    let inp = p.inputStream
+    var count = 0
+    when defined(windows):
+      # xxx we should make osproc.hsWriteData raise IOError on windows, consistent
+      # with posix; we could also (in addition) make IOError a subclass of OSError.
+      type SIGPIPEError = OSError
+    else:
+      type SIGPIPEError = IOError
+    doAssertRaises(SIGPIPEError):
+      for i in 0..<100000:
+        count.inc
+        inp.writeLine "ok" # was giving SIGPIPE and crashing
+    doAssert count >= 100
+    doAssert waitForExit(p) == QuitFailure
+    close(p) # xxx isn't that missing in other places?
diff --git a/tests/stdlib/tosprocterminate.nim b/tests/stdlib/tosprocterminate.nim
index 7fc6c5d85..93b0317f7 100644
--- a/tests/stdlib/tosprocterminate.nim
+++ b/tests/stdlib/tosprocterminate.nim
@@ -1,7 +1,16 @@
-import os, osproc
+discard """
+  cmd: "nim $target $options -r $file"
+  targets: "c cpp"
+  matrix: "--mm:refc; --mm:orc"
+"""
 
-when defined(Windows):
+import os, osproc, times, std / monotimes
+import std/assertions
+
+when defined(windows):
   const ProgramWhichDoesNotEnd = "notepad"
+elif defined(openbsd):
+  const ProgramWhichDoesNotEnd = "/bin/cat"
 else:
   const ProgramWhichDoesNotEnd = "/bin/sh"
 
@@ -15,7 +24,21 @@ while process.running() and TimeToWait > 0:
   sleep(100)
   TimeToWait = TimeToWait - 100
 
-if process.running():
-  echo("FAILED")
-else:
-  echo("SUCCESS")
+doAssert not process.running()
+echo("stopped process")
+
+process.close()
+
+echo("starting " & ProgramWhichDoesNotEnd)
+process = startProcess(ProgramWhichDoesNotEnd)
+echo("process should be stopped after 2s")
+
+let start = getMonoTime()
+discard process.waitForExit(2000)
+let took = getMonoTime() - start
+
+doAssert not process.running()
+# some additional time to account for overhead
+doAssert took < initDuration(seconds = 3)
+
+echo("stopped process after ", took)
diff --git a/tests/stdlib/tpackedsets.nim b/tests/stdlib/tpackedsets.nim
new file mode 100644
index 000000000..f519c08a7
--- /dev/null
+++ b/tests/stdlib/tpackedsets.nim
@@ -0,0 +1,265 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/packedsets
+import std/sets
+
+import sequtils
+import algorithm
+
+import std/assertions
+
+block basicIntSetTests:
+  var y = initPackedSet[int]()
+  y.incl(1)
+  y.incl(2)
+  y.incl(7)
+  y.incl(1056)
+
+  y.incl(1044)
+  y.excl(1044)
+
+  doAssert y == [1, 2, 7, 1056].toPackedSet
+  doAssert toSeq(y.items) == [1, 2, 7, 1056]
+
+  doAssert y.containsOrIncl(888) == false
+  doAssert 888 in y
+  doAssert y.containsOrIncl(888) == true
+
+  doAssert y.missingOrExcl(888) == false
+  doAssert 888 notin y
+  doAssert y.missingOrExcl(888) == true
+
+proc sortedPairs[T](t: T): auto = toSeq(t.pairs).sorted
+template sortedItems(t: untyped): untyped = sorted(toSeq(t))
+
+type Id = distinct int
+proc `$`(x: Id): string {.borrow.}
+proc cmp(a: Id, b: Id): int {.borrow.}
+proc `==`(a: Id, b: Id): bool {.borrow.}
+proc `<`(a: Id, b: Id): bool {.borrow.}
+
+block genericTests: 
+  # we use HashSet as groundtruth, it's well tested elsewhere
+  template testDel(A: typedesc, t: typed, t0: typed) =
+
+    block:
+      template checkEquals() =
+        doAssert t.len == t0.len
+        for k in t0:
+          doAssert k in t
+        for k in t:
+          doAssert k in t0
+
+        doAssert sortedItems(t) == sortedItems(t0)
+
+      template incl2(i) =
+        t.incl i
+        t0.incl i
+
+      template excl2(i) =
+        t.excl i
+        t0.excl i
+
+      var expected: seq[A]
+      let n = 100
+      let n2 = n*2
+      for i in 0..<n:
+        incl2(A(i))
+      checkEquals()
+      for i in 0..<n:
+        if i mod 3 == 0:
+          if i < n div 2:
+            excl2(A(i))
+          else:
+            t0.excl A(i)
+            doAssert A(i) in t
+            doAssert not t.missingOrExcl A(i)
+
+      checkEquals()
+      for i in n..<n2:
+        incl2(A(i))
+      checkEquals()
+      for i in 0..<n2:
+        if i mod 7 == 0:
+          excl2(A(i))
+      checkEquals()
+
+      # notin check
+      for i in 0..<t.len:
+        if i mod 7 == 0:
+          doAssert A(i) notin t0
+          doAssert A(i) notin t
+          # issue #13505
+          doAssert t.missingOrExcl(A(i))
+
+  var t: PackedSet[int]
+  var t0: HashSet[int]
+  testDel(int, t, t0)
+
+  var distT: PackedSet[Id]
+  var distT0: HashSet[Id]
+  testDel(Id, distT, distT0)
+
+  doAssert union(distT, initPackedSet[Id]()) == distT
+
+  var charT: PackedSet[char]
+  var charT0: HashSet[char]
+  testDel(char, charT, charT0)
+
+
+block typeSafetyTest:
+  # mixing sets of different types shouldn't compile
+  doAssert not compiles( union(initPackedSet[Id](), initPackedSet[int]()) )
+  doAssert     compiles( union(initPackedSet[Id](), initPackedSet[Id]()))
+
+  var ids: PackedSet[Id]
+  doAssert not compiles( ids.incl(3) )
+  doAssert     compiles( ids.incl(Id(3)) )
+
+  type NonOrdinal = string
+  doAssert not compiles( initPackedSet[NonOrdinal]() )
+
+type EnumABCD = enum A, B, C, D
+
+block enumTest:
+  var letterSet = initPackedSet[EnumABCD]()
+
+  for x in [A, C]:
+    letterSet.incl(x)
+
+  doAssert A in letterSet
+  doAssert B notin letterSet
+  doAssert C in letterSet
+  doAssert D notin letterSet
+
+type Foo = distinct int16
+proc `$`(a: Foo): string {.borrow.} # `echo a` below won't work without `$` defined, as expected
+
+block printTest:
+  var a = initPackedSet[EnumABCD]()
+  a.incl A
+  a.incl C 
+  doAssert $a == "{A, C}"
+
+import intsets
+
+block legacyMainModuleTests:
+  template genericTests(A: typedesc[Ordinal], x: typed) =
+    block:
+      proc typSeq(s: seq[int]): seq[A] = s.map(proc (i: int): A = A(i))
+      x.incl(A(1))
+      x.incl(A(2))
+      x.incl(A(7))
+      x.incl(A(1056))
+
+      x.incl(A(1044))
+      x.excl(A(1044))
+
+      doAssert x == typSeq(@[1, 2, 7, 1056]).toPackedSet
+
+      doAssert x.containsOrIncl(A(888)) == false
+      doAssert A(888) in x
+      doAssert x.containsOrIncl(A(888)) == true
+
+      doAssert x.missingOrExcl(A(888)) == false
+      doAssert A(888) notin x
+      doAssert x.missingOrExcl(A(888)) == true
+
+      var xs = toSeq(items(x))
+      xs.sort(cmp[A])
+      doAssert xs == typSeq(@[1, 2, 7, 1056])
+
+      var y: PackedSet[A]
+      assign(y, x)
+      var ys = toSeq(items(y))
+      ys.sort(cmp[A])
+      doAssert ys == typSeq(@[1, 2, 7, 1056])
+
+      doAssert x == y
+
+      var z: PackedSet[A]
+      for i in 0..1000:
+        incl z, A(i)
+        doAssert z.len() == i+1
+      for i in 0..1000:
+        doAssert z.contains(A(i))
+
+      var w = initPackedSet[A]()
+      w.incl(A(1))
+      w.incl(A(4))
+      w.incl(A(50))
+      w.incl(A(1001))
+      w.incl(A(1056))
+
+      var xuw = x.union(w)
+      var xuws = toSeq(items(xuw))
+      xuws.sort(cmp)
+      doAssert xuws == typSeq(@[1, 2, 4, 7, 50, 1001, 1056])
+
+      var xiw = x.intersection(w)
+      var xiws = toSeq(items(xiw))
+      xiws.sort(cmp)
+      doAssert xiws == @[A(1), A(1056)]
+
+      var xdw = x.difference(w)
+      var xdws = toSeq(items(xdw))
+      xdws.sort(cmp[A])
+      doAssert xdws == @[A(2), A(7)]
+
+      var xsw = x.symmetricDifference(w)
+      var xsws = toSeq(items(xsw))
+      xsws.sort(cmp[A])
+      doAssert xsws == typSeq(@[2, 4, 7, 50, 1001])
+
+      x.incl(w)
+      xs = toSeq(items(x))
+      xs.sort(cmp[A])
+      doAssert xs == typSeq(@[1, 2, 4, 7, 50, 1001, 1056])
+
+      doAssert w <= x
+
+      doAssert w < x
+
+      doAssert(not disjoint(w, x))
+
+      var u = initPackedSet[A]()
+      u.incl(A(3))
+      u.incl(A(5))
+      u.incl(A(500))
+      doAssert disjoint(u, x)
+
+      var v = initPackedSet[A]()
+      v.incl(A(2))
+      v.incl(A(50))
+
+      x.excl(v)
+      xs = toSeq(items(x))
+      xs.sort(cmp[A])
+      doAssert xs == typSeq(@[1, 4, 7, 1001, 1056])
+
+      proc bug12366 =
+        var
+          x = initPackedSet[A]()
+          y = initPackedSet[A]()
+          n = 3584
+
+        for i in 0..n:
+          x.incl(A(i))
+          y.incl(A(i))
+
+        let z = symmetricDifference(x, y)
+        doAssert z.len == 0
+        doAssert $z == "{}"
+
+      bug12366()
+
+  var legacyInit = initIntSet()
+  genericTests(int, legacyInit)
+
+  var intGenericInit = initPackedSet[int]()
+  genericTests(int, intGenericInit)
+
+  var intDistinct = initPackedSet[Id]()
+  genericTests(Id, intDistinct)
diff --git a/tests/stdlib/tparscfg.nim b/tests/stdlib/tparscfg.nim
deleted file mode 100644
index fc735f3eb..000000000
--- a/tests/stdlib/tparscfg.nim
+++ /dev/null
@@ -1,69 +0,0 @@
-discard """
-output: '''
-utf-8
-on
-hello
-lihf8515
-10214028
-lihaifeng@wxm.com
-===
-charset=utf-8
-[Package]
-name=hello
---threads:on
-[Author]
-name=lhf
-qq=10214028
-email="lihaifeng@wxm.com"
-===
-charset=utf-8
-[Package]
-name=hello
---threads:on
-[Author]
-name=lihf8515
-qq=10214028
-'''
-"""
-import parsecfg, streams
-
-## Creating a configuration file.
-var dict1=newConfig()
-dict1.setSectionKey("","charset","utf-8")
-dict1.setSectionKey("Package","name","hello")
-dict1.setSectionKey("Package","--threads","on")
-dict1.setSectionKey("Author","name","lihf8515")
-dict1.setSectionKey("Author","qq","10214028")
-dict1.setSectionKey("Author","email","lihaifeng@wxm.com")
-var ss = newStringStream()
-dict1.writeConfig(ss)
-
-## Reading a configuration file.
-var dict2 = loadConfig(newStringStream(ss.data))
-var charset = dict2.getSectionValue("","charset")
-var threads = dict2.getSectionValue("Package","--threads")
-var pname = dict2.getSectionValue("Package","name")
-var name = dict2.getSectionValue("Author","name")
-var qq = dict2.getSectionValue("Author","qq")
-var email = dict2.getSectionValue("Author","email")
-echo charset
-echo threads
-echo pname
-echo name
-echo qq
-echo email
-
-echo "==="
-
-## Modifying a configuration file.
-var dict3 = loadConfig(newStringStream(ss.data))
-dict3.setSectionKey("Author","name","lhf")
-write(stdout, $dict3)
-
-echo "==="
-
-## Deleting a section key in a configuration file.
-var dict4 = loadConfig(newStringStream(ss.data))
-dict4.delSectionKey("Author","email")
-write(stdout, $dict4)
-
diff --git a/tests/stdlib/tparsecfg.nim b/tests/stdlib/tparsecfg.nim
index 1c214e5d2..2600d6f66 100644
--- a/tests/stdlib/tparsecfg.nim
+++ b/tests/stdlib/tparsecfg.nim
@@ -1,23 +1,132 @@
 discard """
-  output: '''OK'''
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c js"
 """
 
-#bug #6046
-import parsecfg
-
-var config = newConfig()
-config.setSectionKey("foo","bar","-1")
-config.setSectionKey("foo","foo","abc")
-config.writeConfig("test.ini")
-
-# test.ini now contains
-# [foo]
-# bar=-1
-# foo=abc
-
-var config2 = loadConfig("test.ini")
-let bar = config2.getSectionValue("foo","bar")
-let foo = config2.getSectionValue("foo","foo")
-assert(bar == "-1")
-assert(foo == "abc")
-echo "OK"
+import parsecfg, streams, sequtils
+import std/assertions
+
+when not defined(js):
+  from stdtest/specialpaths import buildDir
+  import os
+  # bug #6046
+  block:
+    var config = newConfig()
+    config.setSectionKey("foo", "bar", "-1")
+    config.setSectionKey("foo", "foo", "abc")
+
+    const file = buildDir / "tparsecfg.ini"
+    config.writeConfig(file)
+
+    # file now contains
+    # [foo]
+    # bar=-1
+    # foo=abc
+
+    var config2 = loadConfig(file)
+    let bar = config2.getSectionValue("foo", "bar")
+    let foo = config2.getSectionValue("foo", "foo")
+    doAssert(bar == "-1")
+    doAssert(foo == "abc")
+
+## Creating a configuration file.
+var dict1 = newConfig()
+dict1.setSectionKey("", "charset", "utf-8")
+dict1.setSectionKey("Package", "name", "hello")
+dict1.setSectionKey("Package", "--threads", "on")
+dict1.setSectionKey("Author", "name", "lihf8515")
+dict1.setSectionKey("Author", "qq", "10214028")
+dict1.setSectionKey("Author", "email", "lihaifeng@wxm.com")
+var ss = newStringStream()
+dict1.writeConfig(ss)
+
+## Reading a configuration file.
+let dict2 = loadConfig(newStringStream(ss.data))
+doAssert dict2.getSectionValue("", "charset") == "utf-8"
+doAssert dict2.getSectionValue("Package", "--threads") == "on"
+doAssert dict2.getSectionValue("Package", "name") == "hello"
+doAssert dict2.getSectionValue("Author", "name") == "lihf8515"
+doAssert dict2.getSectionValue("Author", "qq") == "10214028"
+doAssert dict2.getSectionValue("Author", "email") == "lihaifeng@wxm.com"
+doAssert toSeq(dict2.sections) == @["", "Package", "Author"]
+
+## Modifying a configuration file.
+var dict3 = loadConfig(newStringStream(ss.data))
+dict3.setSectionKey("Author", "name", "lhf")
+doAssert $dict3 == """charset=utf-8
+[Package]
+name=hello
+--threads:on
+[Author]
+name=lhf
+qq=10214028
+email="lihaifeng@wxm.com"
+"""
+
+## Deleting a section key in a configuration file.
+var dict4 = loadConfig(newStringStream(ss.data))
+dict4.delSectionKey("Author", "email")
+doAssert $dict4 == """charset=utf-8
+[Package]
+name=hello
+--threads:on
+[Author]
+name=lihf8515
+qq=10214028
+"""
+
+block:
+  var dict = loadConfig(newStringStream("""[Simple Values]
+  key=value
+  spaces in keys=allowed
+  spaces in values=allowed as well
+  spaces around the delimiter = obviously
+  you can also use : to delimit keys from values
+  [All Values Are Strings]
+  values like this: 19990429
+  or this: 3.14159265359
+  are they treated as numbers : no
+  integers floats and booleans are held as: strings
+  can use the API to get converted values directly: true
+  [No Values]
+  key_without_value
+  # empty string value is not allowed =
+  [ Seletion A   ]
+  space around section name will be ignored
+  [You can use comments]
+  # like this
+  ; or this
+  # By default only in an empty line.
+  # Inline comments can be harmful because they prevent users
+  # from using the delimiting characters as parts of values.
+  # That being said, this can be customized.
+      [Sections Can Be Indented]
+          can_values_be_as_well = True
+          does_that_mean_anything_special = False
+          purpose = formatting for readability
+          # Did I mention we can indent comments, too?
+  """)
+  )
+
+  let section1 = "Simple Values"
+  doAssert dict.getSectionValue(section1, "key") == "value"
+  doAssert dict.getSectionValue(section1, "spaces in keys") == "allowed"
+  doAssert dict.getSectionValue(section1, "spaces in values") == "allowed as well"
+  doAssert dict.getSectionValue(section1, "spaces around the delimiter") == "obviously"
+  doAssert dict.getSectionValue(section1, "you can also use") == "to delimit keys from values"
+
+  let section2 = "All Values Are Strings"
+  doAssert dict.getSectionValue(section2, "values like this") == "19990429"
+  doAssert dict.getSectionValue(section2, "or this") == "3.14159265359"
+  doAssert dict.getSectionValue(section2, "are they treated as numbers") == "no"
+  doAssert dict.getSectionValue(section2, "integers floats and booleans are held as") == "strings"
+  doAssert dict.getSectionValue(section2, "can use the API to get converted values directly") == "true"
+
+  let section3 = "Seletion A"
+  doAssert dict.getSectionValue(section3, 
+    "space around section name will be ignored", "not an empty value") == ""
+
+  let section4 = "Sections Can Be Indented"
+  doAssert dict.getSectionValue(section4, "can_values_be_as_well") == "True"
+  doAssert dict.getSectionValue(section4, "does_that_mean_anything_special") == "False"
+  doAssert dict.getSectionValue(section4, "purpose") == "formatting for readability"
diff --git a/tests/stdlib/tparsecsv.nim b/tests/stdlib/tparsecsv.nim
new file mode 100644
index 000000000..5a1e41bce
--- /dev/null
+++ b/tests/stdlib/tparsecsv.nim
@@ -0,0 +1,36 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+include parsecsv
+import strutils, os
+import std/assertions
+
+block: # Tests for reading the header row
+  let content = "\nOne,Two,Three,Four\n1,2,3,4\n10,20,30,40,\n100,200,300,400\n"
+  writeFile("temp.csv", content)
+
+  var p: CsvParser
+  p.open("temp.csv")
+  p.readHeaderRow()
+  while p.readRow():
+    let zeros = repeat('0', p.currRow-2)
+    doAssert p.rowEntry("One") == "1" & zeros
+    doAssert p.rowEntry("Two") == "2" & zeros
+    doAssert p.rowEntry("Three") == "3" & zeros
+    doAssert p.rowEntry("Four") == "4" & zeros
+  p.close()
+
+  when not defined(testing):
+    var parser: CsvParser
+    parser.open("temp.csv")
+    parser.readHeaderRow()
+    while parser.readRow():
+      echo "new row: "
+      for col in items(parser.headers):
+        echo "##", col, ":", parser.rowEntry(col), "##"
+    parser.close()
+    removeFile("temp.csv")
+
+  # Tidy up
+  removeFile("temp.csv")
diff --git a/tests/stdlib/tparseipv6.nim b/tests/stdlib/tparseipv6.nim
index 3e1c23e58..31ec4ecfb 100644
--- a/tests/stdlib/tparseipv6.nim
+++ b/tests/stdlib/tparseipv6.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   output: "all ok"
 """
 
@@ -8,23 +9,23 @@ const
   positives = [
     "::f:8:a8f:218.17.235.229",
     "::b:228.19.241.2",
-    "::8:c:a:f:8.35.8.096",
-    "::3:e:a:bc:04.19.2.9",
+    "::8:c:a:f:8.35.8.96",
+    "::3:e:a:bc:4.19.2.9",
     "::2:212.242.248.19",
     "::df:a5f:3.250.208.9",
     "::8:c:5:e63:250.208.249.0",
     "::b:f:181.12.9.98",
-    "::a:f8:77.08.243.232",
-    "::a:b:85:e4d9:252.9.229.056",
+    "::a:f8:77.8.243.232",
+    "::a:b:85:e4d9:252.9.229.56",
     "941:c:8a:c:e::917",
     "e8:7a:e:ad:88a:8:203.235.225.46",
-    "139c:9e::f8:254.08.21.249",
+    "139c:9e::f8:254.8.21.249",
     "b38:f0:e::f9:89.6.12.18",
     "ef::8",
     "5::ab",
     "a::8:255.247.96.253",
     "b:c0::c:254.248.95.254",
-    "::8c:2:99.251.024.3",
+    "::8c:2:99.251.24.3",
     "98::c:247.240.249.57",
     "9::9",
     "628::f1ed:f",
@@ -106,68 +107,68 @@ const
     "::315",
     "::a:a",
     "::aed3:a",
-    "f0eb:0:e8:b:c:a:254.098.233.17",
+    "f0eb:0:e8:b:c:a:254.98.233.17",
     "bfa:7fc:c66d:15:e9a:ded:254.119.9.9",
-    "d:ffa8:9:a:879:3:202.39.08.245",
+    "d:ffa8:9:a:879:3:202.39.8.245",
     "8e:2:8:fa8a:f1d1:1aa8:252.254.245.81",
-    "5:d4:a:e9:8:8:06.38.98.253",
-    "9c5:4:a5c:f:a6:8c9d:05.250.8.2",
+    "5:d4:a:e9:8:8:6.38.98.253",
+    "9c5:4:a5c:f:a6:8c9d:5.250.8.2",
     "d19a:2:f808:be:f:c:98.86.197.249",
-    "8:26ac:8:8:cb:f:242.00.254.85",
+    "8:26ac:8:8:cb:f:242.0.254.85",
     "38:e:1:0b88:f:0:8.89.248.92",
-    "e7:ff96:a:f:f:b:253.91.052.195",
+    "e7:ff96:a:f:f:b:253.91.52.195",
     "d:8:2:5:894:5:254.0.240.199",
     "2:98:9:8aa:9c8f:fa:252.98.248.17",
-    "e9:d4f:890:ccbe:5:8:088.200.228.216",
+    "e9:d4f:890:ccbe:5:8:88.200.228.216",
     "3:3:9:5:6a:df5:255.251.8.12",
-    "0280:3:8:8:4:9:255.000.251.249",
+    "0280:3:8:8:4:9:255.0.251.249",
     "8:af7:db:aa:0:9:238.248.250.255",
-    "ff:ee:9a:9252:a:289:059.083.18.255",
+    "ff:ee:9a:9252:a:289:59.83.18.255",
     "9f6:5:fc9:b:a89:a:142.1.250.254",
     "e:981a:da:bf94:9:f8:254.242.18.95",
     "3c:1:4:f2:89:f:8.91.255.14",
-    "e::9a2:c:9.050.80.8",
+    "e::9a2:c:9.50.80.8",
     "9::4a:07:fb:211.241.254.228",
     "9be::2:e:215.189.48.188",
-    "f::f:d:069.148.99.168",
+    "f::f:d:69.148.99.168",
     "f::a:97.18.240.47",
     "c::a98e:1:251.253.252.254",
     "668::82:214.87.208.9",
     "9c0::cf0:ecb:253.208.238.255",
-    "a::0:f1:210.240.238.049",
-    "8::a:1:251.238.34.09",
+    "a::0:f1:210.240.238.49",
+    "8::a:1:251.238.34.9",
     "81:dfe::b8:8.255.249.248",
-    "d3::7:b:9:83.189.08.244",
-    "8::9:8:8:00.7.11.252",
+    "d3::7:b:9:83.189.8.244",
+    "8::9:8:8:0.7.11.252",
     "2:8::c:a8:250.221.9.249",
     "2::f:99.8.249.247",
     "c:22f5::5:2c:243.15.79.89",
     "e:8e::da:251.243.255.2",
     "f15f:9::a:255.70.247.218",
-    "f:b::9f38:31.220.94.022",
-    "9::9a48:03.98.249.119",
+    "f:b::9f38:31.220.94.22",
+    "9::9a48:3.98.249.119",
     "d:d:9b87::2d:a:249.253.38.8",
     "d86d:99b::a9b:5:242.236.8.244",
     "eb:3::f:9cf:1.253.1.228",
     "b::ba2:255.247.114.64",
-    "2f:ec:bcb::9:219.254.250.094",
+    "2f:ec:bcb::9:219.254.250.94",
     "da8a:f6::a:e0:19.251.241.251",
-    "5e:c1::a:021.250.8.254",
+    "5e:c1::a:21.250.8.254",
     "c:9::8c9b:248.219.212.252",
     "2:a::8d4a:216.255.198.223",
-    "1f::66:255.30.08.150",
+    "1f::66:255.30.8.150",
     "bc2b:8f::2ff9:6.245.99.230",
     "a:8::a8:9.251.246.255",
-    "f:7:7::98:06.14.1.208",
+    "f:7:7::98:6.14.1.208",
     "e:2::9:218.249.255.254",
     "79:f::6:250.255.98.246",
-    "47:9:fb9f::9:038.136.17.251",
+    "47:9:fb9f::9:38.136.17.251",
     "ed::a:247.9.23.239",
     "6f::f1:88.254.119.9",
     "a::d:218.199.236.0",
-    "fc88::9:203.196.04.95",
-    "::8.048.255.85",
-    "::253.07.255.36",
+    "fc88::9:203.196.4.95",
+    "::8.48.255.85",
+    "::253.7.255.36",
     "9:d::253.7.178.229",
     "::250.84.158.253",
     "::8.55.204.248",
@@ -175,39 +176,47 @@ const
     "df9:88ca::248.255.108.17",
     "8e9b::250.206.0.82",
     "::209.8.254.209",
-    "::247.088.8.8",
+    "::247.88.8.8",
     "::cb:f:ba41:250.208.19.249",
     "::fe:0e8:243.240.229.5",
     "::c:223.251.5.226",
-    "::8:08.03.8.250",
+    "::8:8.3.8.250",
     "::f:8.88.11.255",
-    "::fda:48:aa:05.189.07.2",
-    "::8:c3f:f:240.06.212.255",
+    "::fda:48:aa:5.189.7.2",
+    "::8:c3f:f:240.6.212.255",
     "::f:0aa:244.123.99.16",
-    "::c9b5:c:034.8.090.196",
+    "::c9b5:c:34.8.90.196",
     "::98:c9:254.14.241.81"
   ]
   negatives = ["foo.bar",
     "::::::::::::",
     "yet another failure",
-    "de:6:c:ab5:6a::9:252.06.06.249",
-    "f9:5f7:fa38:9:b::b6:09.255.248.252",
+    "de:6:c:ab5:6a::9:252.6.6.249",
+    "f9:5f7:fa38:9:b::b6:9.255.248.252",
     "97:c:5b:81:8a::f5dd:144.252.250.9",
-    "9:8:cd:8:a9::f:247.255.09.255",
+    "9:8:cd:8:a9::f:247.255.9.255",
     "18:1:8c:2:3::9:8.254.252.139",
-    "e:c298:3:e:a::bb12:254.246.05.250",
+    "e:c298:3:e:a::bb12:254.246.5.250",
     "e:e:c:8e:fd::8:253.8.49.231",
     "9:97f:f:e929:8a::c9:0.8.252.10",
-    "0df:b24:7:89:c::2b:16.249.240.092",
+    "0df:b24:7:89:c::2b:16.249.240.92",
     "b:8f5f:485:c:9a::84c:178.7.249.34",
+    "::3:e:a:bc:091.19.2.9",
+    "::a:f8:77.08.243.232",
+    "::8c:2:99.251.029.3",
+    "::8:c:a:f:8.35.8.096",
+    "d:ffa8:9:a:879:3:0202.39.8.245",
+    "139c:9e::f8:254.07.21.249",
+    "f0eb:0:e8:b:c:a:254.233.043.17",
+    "::a:b:85:e4d9:252.9.229.056",
   ]
 
-proc ok(pos: openarray[string]) =
+proc ok(pos: openArray[string]) =
   for p in pos:
     if not isIpAddress(p):
       echo "failure ", p
 
-proc notok(neg: openarray[string]) =
+proc notok(neg: openArray[string]) =
   for n in neg:
     if isIpAddress(n):
       echo "failure ", n
diff --git a/tests/stdlib/tparsesql.nim b/tests/stdlib/tparsesql.nim
index 126020ed6..cd582551d 100644
--- a/tests/stdlib/tparsesql.nim
+++ b/tests/stdlib/tparsesql.nim
@@ -1,11 +1,23 @@
 discard """
-  file: "tparsesql.nim"
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c js"
 """
-
 import parsesql
+import std/assertions
+
+doAssert treeRepr(parseSql("INSERT INTO STATS VALUES (10, 5.5); ")
+) == """
 
-doAssert $parseSQL("SELECT foo FROM table;") == "select foo from table;"
-doAssert $parseSQL("""
+nkStmtList
+  nkInsert
+    nkIdent STATS
+    nkNone
+    nkValueList
+      nkIntegerLit 10
+      nkNumericLit 5.5"""
+
+doAssert $parseSql("SELECT foo FROM table;") == "select foo from table;"
+doAssert $parseSql("""
 SELECT
   CustomerName,
   ContactName,
@@ -21,26 +33,26 @@ SELECT
   Country
 FROM table;""") == "select CustomerName, ContactName, Address, City, PostalCode, Country, CustomerName, ContactName, Address, City, PostalCode, Country from table;"
 
-doAssert $parseSQL("SELECT foo FROM table limit 10") == "select foo from table limit 10;"
-doAssert $parseSQL("SELECT foo, bar, baz FROM table limit 10") == "select foo, bar, baz from table limit 10;"
-doAssert $parseSQL("SELECT foo AS bar FROM table") == "select foo as bar from table;"
-doAssert $parseSQL("SELECT foo AS foo_prime, bar AS bar_prime, baz AS baz_prime FROM table") == "select foo as foo_prime, bar as bar_prime, baz as baz_prime from table;"
-doAssert $parseSQL("SELECT * FROM table") == "select * from table;"
-doAssert $parseSQL("SELECT count(*) FROM table") == "select count(*) from table;"
-doAssert $parseSQL("SELECT count(*) as 'Total' FROM table") == "select count(*) as 'Total' from table;"
-doAssert $parseSQL("SELECT count(*) as 'Total', sum(a) as 'Aggr' FROM table") == "select count(*) as 'Total', sum(a) as 'Aggr' from table;"
+doAssert $parseSql("SELECT foo FROM table limit 10") == "select foo from table limit 10;"
+doAssert $parseSql("SELECT foo, bar, baz FROM table limit 10") == "select foo, bar, baz from table limit 10;"
+doAssert $parseSql("SELECT foo AS bar FROM table") == "select foo as bar from table;"
+doAssert $parseSql("SELECT foo AS foo_prime, bar AS bar_prime, baz AS baz_prime FROM table") == "select foo as foo_prime, bar as bar_prime, baz as baz_prime from table;"
+doAssert $parseSql("SELECT * FROM table") == "select * from table;"
+doAssert $parseSql("SELECT count(*) FROM table") == "select count(*) from table;"
+doAssert $parseSql("SELECT count(*) as 'Total' FROM table") == "select count(*) as 'Total' from table;"
+doAssert $parseSql("SELECT count(*) as 'Total', sum(a) as 'Aggr' FROM table") == "select count(*) as 'Total', sum(a) as 'Aggr' from table;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT * FROM table
 WHERE a = b and c = d
 """) == "select * from table where a = b and c = d;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT * FROM table
 WHERE not b
 """) == "select * from table where not b;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT
   *
 FROM
@@ -49,63 +61,63 @@ WHERE
   a and not b
 """) == "select * from table where a and not b;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT * FROM table
 ORDER BY 1
 """) == "select * from table order by 1;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT * FROM table
 GROUP BY 1
 ORDER BY 1
 """) == "select * from table group by 1 order by 1;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT * FROM table
 ORDER BY 1
 LIMIT 100
 """) == "select * from table order by 1 limit 100;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT * FROM table
 WHERE a = b and c = d or n is null and not b + 1 = 3
 """) == "select * from table where a = b and c = d or n is null and not b + 1 = 3;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT * FROM table
 WHERE (a = b and c = d) or (n is null and not b + 1 = 3)
 """) == "select * from table where(a = b and c = d) or (n is null and not b + 1 = 3);"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT * FROM table
 HAVING a = b and c = d
 """) == "select * from table having a = b and c = d;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT a, b FROM table
 GROUP BY a
 """) == "select a, b from table group by a;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT a, b FROM table
 GROUP BY 1, 2
 """) == "select a, b from table group by 1, 2;"
 
-doAssert $parseSQL("SELECT t.a FROM t as t") == "select t.a from t as t;"
+doAssert $parseSql("SELECT t.a FROM t as t") == "select t.a from t as t;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT a, b FROM (
   SELECT * FROM t
 )
 """) == "select a, b from(select * from t);"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT a, b FROM (
   SELECT * FROM t
 ) as foo
 """) == "select a, b from(select * from t) as foo;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT a, b FROM (
   SELECT * FROM (
     SELECT * FROM (
@@ -117,49 +129,49 @@ SELECT a, b FROM (
 ) as inner5
 """) == "select a, b from(select * from(select * from(select * from(select * from innerTable as inner1) as inner2) as inner3) as inner4) as inner5;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT a, b FROM
   (SELECT * FROM a),
   (SELECT * FROM b),
   (SELECT * FROM c)
 """) == "select a, b from(select * from a),(select * from b),(select * from c);"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT * FROM Products
 WHERE Price BETWEEN 10 AND 20;
 """) == "select * from Products where Price between 10 and 20;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT id FROM a
 JOIN b
 ON a.id == b.id
 """) == "select id from a join b on a.id == b.id;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT id FROM a
 JOIN (SELECT id from c) as b
 ON a.id == b.id
 """) == "select id from a join(select id from c) as b on a.id == b.id;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT id FROM a
 INNER JOIN b
 ON a.id == b.id
 """) == "select id from a inner join b on a.id == b.id;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT id FROM a
 OUTER JOIN b
 ON a.id == b.id
 """) == "select id from a outer join b on a.id == b.id;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT id FROM a
 CROSS JOIN b
 ON a.id == b.id
 """) == "select id from a cross join b on a.id == b.id;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic');
 CREATE TABLE holidays (
   num_weeks int,
@@ -169,31 +181,54 @@ CREATE INDEX table1_attr1 ON table1(attr1);
 SELECT * FROM myTab WHERE col1 = 'happy';
 """) == "create type happiness as enum ('happy' , 'very happy' , 'ecstatic' ); create table holidays(num_weeks  int , happiness  happiness );; create index table1_attr1 on table1(attr1 );; select * from myTab where col1 = 'happy';"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 INSERT INTO Customers (CustomerName, ContactName, Address, City, PostalCode, Country)
 VALUES ('Cardinal', 'Tom B. Erichsen', 'Skagen 21', 'Stavanger', '4006', 'Norway');
 """) == "insert into Customers (CustomerName , ContactName , Address , City , PostalCode , Country ) values ('Cardinal' , 'Tom B. Erichsen' , 'Skagen 21' , 'Stavanger' , '4006' , 'Norway' );"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 INSERT INTO TableName DEFAULT VALUES
 """) == "insert into TableName default values;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 UPDATE Customers
 SET ContactName = 'Alfred Schmidt', City= 'Frankfurt'
 WHERE CustomerID = 1;
 """) == "update Customers set ContactName  = 'Alfred Schmidt' , City  = 'Frankfurt' where CustomerID = 1;"
 
-doAssert $parseSQL("DELETE FROM table_name;") == "delete from table_name;"
+doAssert treeRepr(parseSql("""UPDATE Customers
+                              SET ContactName = 'Alice', City= 'Frankfurt';""")
+) == """
+
+nkStmtList
+  nkUpdate
+    nkIdent Customers
+    nkAsgn
+      nkIdent ContactName
+      nkStringLit Alice
+    nkAsgn
+      nkIdent City
+      nkStringLit Frankfurt
+    nkNone"""
+
+doAssert $parseSql("DELETE FROM table_name;") == "delete from table_name;"
+
+doAssert treeRepr(parseSql("DELETE FROM table_name;")
+) == """
+
+nkStmtList
+  nkDelete
+    nkIdent table_name
+    nkNone"""
 
-doAssert $parseSQL("DELETE * FROM table_name;") == "delete from table_name;"
+doAssert $parseSql("DELETE * FROM table_name;") == "delete from table_name;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 --Select all:
 SELECT * FROM Customers;
 """) == "select * from Customers;"
 
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT * FROM Customers WHERE (CustomerName LIKE 'L%'
 OR CustomerName LIKE 'R%' /*OR CustomerName LIKE 'S%'
 OR CustomerName LIKE 'T%'*/ OR CustomerName LIKE 'W%')
@@ -202,9 +237,9 @@ ORDER BY CustomerName;
 """) == "select * from Customers where(CustomerName like 'L%' or CustomerName like 'R%' or CustomerName like 'W%') and Country = 'USA' order by CustomerName;"
 
 # parse quoted keywords as identifires
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT `SELECT`, `FROM` as `GROUP` FROM `WHERE`;
 """) == """select "SELECT", "FROM" as "GROUP" from "WHERE";"""
-doAssert $parseSQL("""
+doAssert $parseSql("""
 SELECT "SELECT", "FROM" as "GROUP" FROM "WHERE";
 """) == """select "SELECT", "FROM" as "GROUP" from "WHERE";"""
diff --git a/tests/stdlib/tparseuints.nim b/tests/stdlib/tparseuints.nim
index 6b228d933..9c71a27d6 100644
--- a/tests/stdlib/tparseuints.nim
+++ b/tests/stdlib/tparseuints.nim
@@ -1,13 +1,11 @@
 discard """
-  action: run
-  output: '''
-[Suite] parseutils'''
+  matrix: "--mm:refc; --mm:orc"
 """
+
 import unittest, strutils
 
-suite "parseutils":
-  test "uint":
-    check: parseBiggestUInt("0") == 0'u64
-    check: parseBiggestUInt("18446744073709551615") == 0xFFFF_FFFF_FFFF_FFFF'u64
-    expect(ValueError):
-      discard parseBiggestUInt("18446744073709551616")
+block: # parseutils
+  check: parseBiggestUInt("0") == 0'u64
+  check: parseBiggestUInt("18446744073709551615") == 0xFFFF_FFFF_FFFF_FFFF'u64
+  expect(ValueError):
+    discard parseBiggestUInt("18446744073709551616")
diff --git a/tests/stdlib/tparseutils.nim b/tests/stdlib/tparseutils.nim
new file mode 100644
index 000000000..b69900864
--- /dev/null
+++ b/tests/stdlib/tparseutils.nim
@@ -0,0 +1,112 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c cpp"
+"""
+
+import std/[parseutils, sequtils, sugar, formatfloat]
+import std/assertions
+
+proc test() =
+  let input = "$test{}  $this is ${an{  example}}  "
+  let expected = @[(ikVar, "test"), (ikStr, "{}  "), (ikVar, "this"),
+                    (ikStr, " is "), (ikExpr, "an{  example}"), (ikStr, "  ")]
+  doAssert toSeq(interpolatedFragments(input)) == expected
+
+  var value = 0
+  discard parseHex("0x38", value)
+  doAssert value == 56
+
+  value = -1
+  doAssert(parseSaturatedNatural("848", value) == 3)
+  doAssert value == 848
+
+  value = -1
+  discard parseSaturatedNatural("84899999999999999999324234243143142342135435342532453", value)
+  doAssert value == high(int)
+
+  value = -1
+  discard parseSaturatedNatural("9223372036854775808", value)
+  doAssert value == high(int)
+
+  value = -1
+  discard parseSaturatedNatural("9223372036854775807", value)
+  doAssert value == high(int)
+
+  value = -1
+  discard parseSaturatedNatural("18446744073709551616", value)
+  doAssert value == high(int)
+
+  value = -1
+  discard parseSaturatedNatural("18446744073709551615", value)
+  doAssert value == high(int)
+
+  value = -1
+  doAssert(parseSaturatedNatural("1_000_000", value) == 9)
+  doAssert value == 1_000_000
+
+  var i64Value: int64
+  discard parseBiggestInt("9223372036854775807", i64Value)
+  doAssert i64Value == 9223372036854775807
+
+  block:
+    var f: float
+    let res = collect:
+      for x in ["9.123456789012345+","11.123456789012345+","9.123456789012345-","8.123456789012345+","9.12345678901234-","9.123456789012345"]:
+        (parseFloat(x, f, 0), $f)
+    doAssert res == @[(17, "9.123456789012344"), (18, "11.123456789012344"),
+                      (17, "9.123456789012344"), (17, "8.123456789012344"),
+                      (16, "9.12345678901234"), (17, "9.123456789012344")]
+
+test()
+static: test()
+
+block:  # With this included, static: test() crashes the compiler (from a
+        # VM problem with parseSize calling parseFloat).
+  var sz: int64
+  template checkParseSize(s, expectLen, expectVal) =
+    if (let got = parseSize(s, sz); got != expectLen):
+      raise newException(IOError, "got len " & $got & " != " & $expectLen)
+    if sz != expectVal:
+      raise newException(IOError, "got sz " & $sz & " != " & $expectVal)
+  #              STRING    LEN SZ
+  # Good, complete parses
+  checkParseSize "1  b"   , 4, 1
+  checkParseSize "1  B"   , 4, 1
+  checkParseSize "1k"     , 2, 1000
+  checkParseSize "1 kib"  , 5, 1024
+  checkParseSize "1 ki"   , 4, 1024
+  checkParseSize "1mi"    , 3, 1048576
+  checkParseSize "1 mi"   , 4, 1048576
+  checkParseSize "1 mib"  , 5, 1048576
+  checkParseSize "1 Mib"  , 5, 1048576
+  checkParseSize "1 MiB"  , 5, 1048576
+  checkParseSize "1.23GiB", 7, 1320702444 # 1320702443.52 rounded
+  checkParseSize "0.001k" , 6, 1
+  checkParseSize "0.0004k", 7, 0
+  checkParseSize "0.0006k", 7, 1
+  # Incomplete parses
+  checkParseSize "1  "    , 1, 1          # Trailing white IGNORED
+  checkParseSize "1  B "  , 4, 1          # Trailing white IGNORED
+  checkParseSize "1  B/s" , 4, 1          # Trailing junk IGNORED
+  checkParseSize "1 kX"   , 3, 1000
+  checkParseSize "1 kiX"  , 4, 1024
+  checkParseSize "1j"     , 1, 1          # Unknown prefix IGNORED
+  checkParseSize "1 jib"  , 2, 1          # Unknown prefix post space
+  checkParseSize "1  ji"  , 3, 1
+  # Bad parses; `sz` should stay last good|incomplete value
+  checkParseSize "-1b"    , 0, 1          # Negative numbers
+  checkParseSize "abc"    , 0, 1          # Non-numeric
+  checkParseSize " 12"    , 0, 1          # Leading white
+  # Value Edge cases
+  checkParseSize "9223372036854775807", 19, int64.high
+
+block: # bug #23936
+  func parsePyFloat(
+      a: openArray[char],  # here must be openArray instead of string to reproduce this bug
+      res: var BiggestFloat): int =
+    result = parseFloat(a, res)
+
+  static:
+    var f = 0.0
+    doAssert "1.0".parsePyFloat(f) == 3
+    doAssert f == 1.0
diff --git a/tests/stdlib/tparsopt.nim b/tests/stdlib/tparsopt.nim
index 848fba2da..f3a9a9798 100644
--- a/tests/stdlib/tparsopt.nim
+++ b/tests/stdlib/tparsopt.nim
@@ -1,8 +1,16 @@
-# Test the new parseopt module
+discard """
+disabled: true
+"""
+
+# this file has a type in the name, and it does not really test
+# parseopt module, because tester has no support to set arguments. Test the
+# new parseopt module. Therefore it is disabled.
 
 import
   parseopt
 
+import std/[assertions, syncio]
+
 proc writeHelp() =
   writeLine(stdout, "Usage: tparsopt [options] filename [options]")
 
@@ -21,7 +29,7 @@ for kind, key, val in getopt():
     of "version", "v": writeVersion()
     else:
       writeLine(stdout, "Unknown command line option: ", key, ": ", val)
-  of cmdEnd: assert(false) # cannot happen
+  of cmdEnd: doAssert(false) # cannot happen
 if filename == "":
   # no filename has been given, so we show the help:
   writeHelp()
diff --git a/tests/stdlib/tpathnorm.nim b/tests/stdlib/tpathnorm.nim
new file mode 100644
index 000000000..3dd287a77
--- /dev/null
+++ b/tests/stdlib/tpathnorm.nim
@@ -0,0 +1,36 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/os
+import std/assertions
+
+when doslikeFileSystem:
+  import std/pathnorm
+
+  template initVars =
+    var state {.inject.} = 0
+    var result {.inject.}: string
+
+  block: # / -> /
+    initVars
+    addNormalizePath("//?/c:/./foo//bar/../baz", result, state, '/')
+    doAssert result == "//?/c:/foo/baz"
+    addNormalizePath("me", result, state, '/')
+    doAssert result == "//?/c:/foo/baz/me"
+
+  block: # / -> \
+    initVars
+    addNormalizePath(r"//?/c:/./foo//bar/../baz", result, state, '\\')
+    doAssert result == r"\\?\c:\foo\baz"
+    addNormalizePath("me", result, state, '\\')
+    doAssert result == r"\\?\c:\foo\baz\me"
+
+  block: # Append path component to UNC drive
+    initVars
+    addNormalizePath(r"//?/c:", result, state, '\\')
+    doAssert result == r"\\?\c:"
+    addNormalizePath("Users", result, state, '\\')
+    doAssert result == r"\\?\c:\Users"
+    addNormalizePath("me", result, state, '\\')
+    doAssert result == r"\\?\c:\Users\me"
diff --git a/tests/stdlib/tpaths.nim b/tests/stdlib/tpaths.nim
new file mode 100644
index 000000000..edb56209a
--- /dev/null
+++ b/tests/stdlib/tpaths.nim
@@ -0,0 +1,238 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/paths
+import std/assertions
+import pathnorm
+from std/private/ospaths2 {.all.} import joinPathImpl
+import std/[sugar, sets]
+
+
+proc normalizePath*(path: Path; dirSep = DirSep): Path =
+  result = Path(pathnorm.normalizePath(path.string, dirSep))
+
+func joinPath*(parts: varargs[Path]): Path =
+  var estimatedLen = 0
+  var state = 0
+  for p in parts: estimatedLen += p.string.len
+  var res = newStringOfCap(estimatedLen)
+  for i in 0..high(parts):
+    joinPathImpl(res, state, parts[i].string)
+  result = Path(res)
+
+
+func joinPath(head, tail: Path): Path {.inline.} =
+  head / tail
+
+block absolutePath:
+  doAssertRaises(ValueError): discard absolutePath(Path"a", Path"b")
+  doAssert absolutePath(Path"a") == getCurrentDir() / Path"a"
+  doAssert absolutePath(Path"a", Path"/b") == Path"/b" / Path"a"
+  when defined(posix):
+    doAssert absolutePath(Path"a", Path"/b/") == Path"/b" / Path"a"
+    doAssert absolutePath(Path"a", Path"/b/c") == Path"/b/c" / Path"a"
+    doAssert absolutePath(Path"/a", Path"b/") == Path"/a"
+
+block splitFile:
+  doAssert splitFile(Path"") == (Path"", Path"", "")
+  doAssert splitFile(Path"abc/") == (Path"abc", Path"", "")
+  doAssert splitFile(Path"/") == (Path"/", Path"", "")
+  doAssert splitFile(Path"./abc") == (Path".", Path"abc", "")
+  doAssert splitFile(Path".txt") == (Path"", Path".txt", "")
+  doAssert splitFile(Path"abc/.txt") == (Path"abc", Path".txt", "")
+  doAssert splitFile(Path"abc") == (Path"", Path"abc", "")
+  doAssert splitFile(Path"abc.txt") == (Path"", Path"abc", ".txt")
+  doAssert splitFile(Path"/abc.txt") == (Path"/", Path"abc", ".txt")
+  doAssert splitFile(Path"/foo/abc.txt") == (Path"/foo", Path"abc", ".txt")
+  doAssert splitFile(Path"/foo/abc.txt.gz") == (Path"/foo", Path"abc.txt", ".gz")
+  doAssert splitFile(Path".") == (Path"", Path".", "")
+  doAssert splitFile(Path"abc/.") == (Path"abc", Path".", "")
+  doAssert splitFile(Path"..") == (Path"", Path"..", "")
+  doAssert splitFile(Path"a/..") == (Path"a", Path"..", "")
+  doAssert splitFile(Path"/foo/abc....txt") == (Path"/foo", Path"abc...", ".txt")
+
+# execShellCmd is tested in tosproc
+
+block ospaths:
+  doAssert unixToNativePath(Path"") == Path""
+  doAssert unixToNativePath(Path".") == Path($CurDir)
+  doAssert unixToNativePath(Path"..") == Path($ParDir)
+  doAssert isAbsolute(unixToNativePath(Path"/"))
+  doAssert isAbsolute(unixToNativePath(Path"/", Path"a"))
+  doAssert isAbsolute(unixToNativePath(Path"/a"))
+  doAssert isAbsolute(unixToNativePath(Path"/a", Path"a"))
+  doAssert isAbsolute(unixToNativePath(Path"/a/b"))
+  doAssert isAbsolute(unixToNativePath(Path"/a/b", Path"a"))
+  doAssert unixToNativePath(Path"a/b") == joinPath(Path"a", Path"b")
+
+  when defined(macos):
+    doAssert unixToNativePath(Path"./") == Path":"
+    doAssert unixToNativePath(Path"./abc") == Path":abc"
+    doAssert unixToNativePath(Path"../abc") == Path"::abc"
+    doAssert unixToNativePath(Path"../../abc") == Path":::abc"
+    doAssert unixToNativePath(Path"/abc", Path"a") == Path"abc"
+    doAssert unixToNativePath(Path"/abc/def", Path"a") == Path"abc:def"
+  elif doslikeFileSystem:
+    doAssert unixToNativePath(Path"./") == Path(".\\")
+    doAssert unixToNativePath(Path"./abc") == Path(".\\abc")
+    doAssert unixToNativePath(Path"../abc") == Path("..\\abc")
+    doAssert unixToNativePath(Path"../../abc") == Path("..\\..\\abc")
+    doAssert unixToNativePath(Path"/abc", Path"a") == Path("a:\\abc")
+    doAssert unixToNativePath(Path"/abc/def", Path"a") == Path("a:\\abc\\def")
+  else:
+    #Tests for unix
+    doAssert unixToNativePath(Path"./") == Path"./"
+    doAssert unixToNativePath(Path"./abc") == Path"./abc"
+    doAssert unixToNativePath(Path"../abc") == Path"../abc"
+    doAssert unixToNativePath(Path"../../abc") == Path"../../abc"
+    doAssert unixToNativePath(Path"/abc", Path"a") == Path"/abc"
+    doAssert unixToNativePath(Path"/abc/def", Path"a") == Path"/abc/def"
+
+  block extractFilenameTest:
+    doAssert extractFilename(Path"") == Path""
+    when defined(posix):
+      doAssert extractFilename(Path"foo/bar") == Path"bar"
+      doAssert extractFilename(Path"foo/bar.txt") == Path"bar.txt"
+      doAssert extractFilename(Path"foo/") == Path""
+      doAssert extractFilename(Path"/") == Path""
+    when doslikeFileSystem:
+      doAssert extractFilename(Path(r"foo\bar")) == Path"bar"
+      doAssert extractFilename(Path(r"foo\bar.txt")) == Path"bar.txt"
+      doAssert extractFilename(Path(r"foo\")) == Path""
+      doAssert extractFilename(Path(r"C:\")) == Path""
+
+  block lastPathPartTest:
+    doAssert lastPathPart(Path"") == Path""
+    when defined(posix):
+      doAssert lastPathPart(Path"foo/bar.txt") == Path"bar.txt"
+      doAssert lastPathPart(Path"foo/") == Path"foo"
+      doAssert lastPathPart(Path"/") == Path""
+    when doslikeFileSystem:
+      doAssert lastPathPart(Path(r"foo\bar.txt")) == Path"bar.txt"
+      doAssert lastPathPart(Path(r"foo\")) == Path"foo"
+
+  template canon(x): Path = normalizePath(Path(x), '/')
+  doAssert canon"/foo/../bar" == Path"/bar"
+  doAssert canon"foo/../bar" == Path"bar"
+
+  doAssert canon"/f/../bar///" == Path"/bar"
+  doAssert canon"f/..////bar" == Path"bar"
+
+  doAssert canon"../bar" == Path"../bar"
+  doAssert canon"/../bar" == Path"/../bar"
+
+  doAssert canon("foo/../../bar/") == Path"../bar"
+  doAssert canon("./bla/blob/") == Path"bla/blob"
+  doAssert canon(".hiddenFile") == Path".hiddenFile"
+  doAssert canon("./bla/../../blob/./zoo.nim") == Path"../blob/zoo.nim"
+
+  doAssert canon("C:/file/to/this/long") == Path"C:/file/to/this/long"
+  doAssert canon("") == Path""
+  doAssert canon("foobar") == Path"foobar"
+  doAssert canon("f/////////") == Path"f"
+
+  doAssert relativePath(Path"/foo/bar//baz.nim", Path"/foo", '/') == Path"bar/baz.nim"
+  doAssert normalizePath(Path"./foo//bar/../baz", '/') == Path"foo/baz"
+
+  doAssert relativePath(Path"/Users/me/bar/z.nim", Path"/Users/other/bad", '/') == Path"../../me/bar/z.nim"
+
+  doAssert relativePath(Path"/Users/me/bar/z.nim", Path"/Users/other", '/') == Path"../me/bar/z.nim"
+
+  # `//` is a UNC path, `/` is the current working directory's drive, so can't
+  # run this test on Windows.
+  when not doslikeFileSystem:
+    doAssert relativePath(Path"/Users///me/bar//z.nim", Path"//Users/", '/') == Path"me/bar/z.nim"
+  doAssert relativePath(Path"/Users/me/bar/z.nim", Path"/Users/me", '/') == Path"bar/z.nim"
+  doAssert relativePath(Path"", Path"/users/moo", '/') == Path""
+  doAssert relativePath(Path"foo", Path"", '/') == Path"foo"
+  doAssert relativePath(Path"/foo", Path"/Foo", '/') == (when FileSystemCaseSensitive: Path"../foo" else: Path".")
+  doAssert relativePath(Path"/Foo", Path"/foo", '/') == (when FileSystemCaseSensitive: Path"../Foo" else: Path".")
+  doAssert relativePath(Path"/foo", Path"/fOO", '/') == (when FileSystemCaseSensitive: Path"../foo" else: Path".")
+  doAssert relativePath(Path"/foO", Path"/foo", '/') == (when FileSystemCaseSensitive: Path"../foO" else: Path".")
+
+  doAssert relativePath(Path"foo", Path".", '/') == Path"foo"
+  doAssert relativePath(Path".", Path".", '/') == Path"."
+  doAssert relativePath(Path"..", Path".", '/') == Path".."
+
+  doAssert relativePath(Path"foo", Path"foo") == Path"."
+  doAssert relativePath(Path"", Path"foo") == Path""
+  doAssert relativePath(Path"././/foo", Path"foo//./") == Path"."
+
+  doAssert relativePath(getCurrentDir() / Path"bar", Path"foo") == Path"../bar".unixToNativePath
+  doAssert relativePath(Path"bar", getCurrentDir() / Path"foo") == Path"../bar".unixToNativePath
+
+  when doslikeFileSystem:
+    doAssert relativePath(r"c:\foo.nim".Path, r"C:\".Path) == r"foo.nim".Path
+    doAssert relativePath(r"c:\foo\bar\baz.nim".Path, r"c:\foo".Path) == r"bar\baz.nim".Path
+    doAssert relativePath(r"c:\foo\bar\baz.nim".Path, r"d:\foo".Path) == r"c:\foo\bar\baz.nim".Path
+    doAssert relativePath(r"\foo\baz.nim".Path, r"\foo".Path) == r"baz.nim".Path
+    doAssert relativePath(r"\foo\bar\baz.nim".Path, r"\bar".Path) == r"..\foo\bar\baz.nim".Path
+    doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\foo\bar".Path) == r"baz.nim".Path
+    doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\foO\bar".Path) == r"baz.nim".Path
+    doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\bar\bar".Path) == r"\\foo\bar\baz.nim".Path
+    doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\foo\car".Path) == r"\\foo\bar\baz.nim".Path
+    doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\goo\bar".Path) == r"\\foo\bar\baz.nim".Path
+    doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"c:\".Path) == r"\\foo\bar\baz.nim".Path
+    doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\foo".Path) == r"\\foo\bar\baz.nim".Path
+    doAssert relativePath(r"c:\foo.nim".Path, r"\foo".Path) == r"c:\foo.nim".Path
+
+  doAssert joinPath(Path"usr", Path"") == unixToNativePath(Path"usr")
+  doAssert joinPath(Path"usr", Path"") == (Path"usr").dup(add Path"")
+  doAssert joinPath(Path"", Path"lib") == Path"lib"
+  doAssert joinPath(Path"", Path"lib") == Path"".dup(add Path"lib")
+  doAssert joinPath(Path"", Path"/lib") == unixToNativePath(Path"/lib")
+  doAssert joinPath(Path"", Path"/lib") == unixToNativePath(Path"/lib")
+  doAssert joinPath(Path"usr/", Path"/lib") == Path"usr/".dup(add Path"/lib")
+  doAssert joinPath(Path"", Path"") == unixToNativePath(Path"") # issue #13455
+  doAssert joinPath(Path"", Path"") == Path"".dup(add Path"")
+  doAssert joinPath(Path"", Path"/") == unixToNativePath(Path"/")
+  doAssert joinPath(Path"", Path"/") == Path"".dup(add Path"/")
+  doAssert joinPath(Path"/", Path"/") == unixToNativePath(Path"/")
+  doAssert joinPath(Path"/", Path"/") == Path"/".dup(add Path"/")
+  doAssert joinPath(Path"/", Path"") == unixToNativePath(Path"/")
+  doAssert joinPath(Path"/" / Path"") == unixToNativePath(Path"/") # weird test case...
+  doAssert joinPath(Path"/", Path"/a/b/c") == unixToNativePath(Path"/a/b/c")
+  doAssert joinPath(Path"foo/", Path"") == unixToNativePath(Path"foo/")
+  doAssert joinPath(Path"foo/", Path"abc") == unixToNativePath(Path"foo/abc")
+  doAssert joinPath(Path"foo//./", Path"abc/.//") == unixToNativePath(Path"foo/abc/")
+  doAssert Path"foo//./".dup(add Path"abc/.//") == unixToNativePath(Path"foo/abc/")
+  doAssert joinPath(Path"foo", Path"abc") == unixToNativePath(Path"foo/abc")
+  doAssert Path"foo".dup(add Path"abc") == unixToNativePath(Path"foo/abc")
+  doAssert joinPath(Path"", Path"abc") == unixToNativePath(Path"abc")
+
+  doAssert joinPath(Path"zook/.", Path"abc") == unixToNativePath(Path"zook/abc")
+
+  # controversial: inconsistent with `joinPath("zook/.","abc")`
+  # on linux, `./foo` and `foo` are treated a bit differently for executables
+  # but not `./foo/bar` and `foo/bar`
+  doAssert joinPath(Path".", Path"/lib") == unixToNativePath(Path"./lib")
+  doAssert joinPath(Path".", Path"abc") == unixToNativePath(Path"./abc")
+
+  # cases related to issue #13455
+  doAssert joinPath(Path"foo", Path"", Path"") == Path"foo"
+  doAssert joinPath(Path"foo", Path"") == Path"foo"
+  doAssert joinPath(Path"foo/", Path"") == unixToNativePath(Path"foo/")
+  doAssert joinPath(Path"foo/", Path".") == Path"foo"
+  doAssert joinPath(Path"foo", Path"./") == unixToNativePath(Path"foo/")
+  doAssert joinPath(Path"foo", Path"", Path"bar/") == unixToNativePath(Path"foo/bar/")
+
+  # issue #13579
+  doAssert joinPath(Path"/foo", Path"../a") == unixToNativePath(Path"/a")
+  doAssert joinPath(Path"/foo/", Path"../a") == unixToNativePath(Path"/a")
+  doAssert joinPath(Path"/foo/.", Path"../a") == unixToNativePath(Path"/a")
+  doAssert joinPath(Path"/foo/.b", Path"../a") == unixToNativePath(Path"/foo/a")
+  doAssert joinPath(Path"/foo///", Path"..//a/") == unixToNativePath(Path"/a/")
+  doAssert joinPath(Path"foo/", Path"../a") == unixToNativePath(Path"a")
+
+  when doslikeFileSystem:
+    doAssert joinPath(Path"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Common7\\Tools\\", Path"..\\..\\VC\\vcvarsall.bat") == r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat".Path
+    doAssert joinPath(Path"C:\\foo", Path"..\\a") == r"C:\a".Path
+    doAssert joinPath(Path"C:\\foo\\", Path"..\\a") == r"C:\a".Path
+
+
+block: # bug #23663
+  var s: HashSet[Path]
+  s.incl("/a/b/c/..".Path)
+  doAssert "/a/b/".Path in s
+  doAssert "/a/b/c".Path notin s
diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim
index e5b709a66..da3fc14b7 100644
--- a/tests/stdlib/tpegs.nim
+++ b/tests/stdlib/tpegs.nim
@@ -1,5 +1,9 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c cpp js"
   output: '''
+PEG AST traversal output
+------------------------
 pkNonTerminal: Sum @(2, 3)
   pkSequence: (Product (('+' / '-') Product)*)
     pkNonTerminal: Product @(3, 7)
@@ -26,53 +30,315 @@ pkNonTerminal: Sum @(2, 3)
           pkChar: '+'
           pkChar: '-'
         pkNonTerminal: Product @(3, 7)
+
+Event parser output
+-------------------
+@[5.0]
++
+@[5.0, 3.0]
+@[8.0]
+
+/
+@[8.0, 2.0]
+@[4.0]
+
+-
+@[4.0, 7.0]
+-*
+@[4.0, 7.0, 22.0]
+@[4.0, 154.0]
+-
+@[-150.0]
 '''
 """
 
-import strutils, streams
-import pegs
+when defined(nimHasEffectsOf):
+  {.experimental: "strictEffects".}
+
+import std/[strutils, streams, pegs, assertions]
 
 const
   indent = "  "
 
 let
-  pegSrc = """
-Expr <- Sum
-Sum <- Product (('+' / '-') Product)*
-Product <- Value (('*' / '/') Value)*
-Value <- [0-9]+ / '(' Expr ')'
-  """
-  pegAst: Peg = pegSrc.peg
-
-var
-  outp = newStringStream()
-  processed: seq[string] = @[]
-
-proc prt(outp: Stream, kind: PegKind, s: string; level: int = 0) =
-  outp.writeLine indent.repeat(level) & "$1: $2" % [$kind, s]
-
-proc recLoop(p: Peg, level: int = 0) =
-  case p.kind
-  of pkEmpty..pkWhitespace:
-    discard
-  of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle:
-    outp.prt(p.kind, $p, level)
-  of pkChar, pkGreedyRepChar:
-    outp.prt(p.kind, $p, level)
-  of pkCharChoice, pkGreedyRepSet:
-    outp.prt(p.kind, $p, level)
-  of pkNonTerminal:
-    outp.prt(p.kind,
-      "$1 @($3, $4)" % [p.nt.name, $p.nt.rule.kind, $p.nt.line, $p.nt.col], level)
-    if not(p.nt.name in processed):
-      processed.add p.nt.name
-      p.nt.rule.recLoop level+1
-  of pkBackRef..pkBackRefIgnoreStyle:
-    outp.prt(p.kind, $p, level)
-  else:
-    outp.prt(p.kind, $p, level)
-    for s in items(p):
-      s.recLoop level+1
-
-pegAst.recLoop
-echo outp.data
\ No newline at end of file
+  pegAst = """
+Expr    <- Sum
+Sum     <- Product (('+' / '-')Product)*
+Product <- Value (('*' / '/')Value)*
+Value   <- [0-9]+ / '(' Expr ')'
+  """.peg
+  txt = "(5+3)/2-7*22"
+
+block:
+  var
+    outp = newStringStream()
+    processed: seq[string] = @[]
+
+  proc prt(outp: Stream, kind: PegKind, s: string; level: int = 0) =
+    outp.writeLine indent.repeat(level) & "$1: $2" % [$kind, s]
+
+  proc recLoop(p: Peg, level: int = 0) =
+    case p.kind
+    of pkEmpty..pkWhitespace:
+      discard
+    of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle:
+      outp.prt(p.kind, $p, level)
+    of pkChar, pkGreedyRepChar:
+      outp.prt(p.kind, $p, level)
+    of pkCharChoice, pkGreedyRepSet:
+      outp.prt(p.kind, $p, level)
+    of pkNonTerminal:
+      outp.prt(p.kind,
+        "$1 @($3, $4)" % [p.nt.name, $p.nt.rule.kind, $p.nt.line, $p.nt.col], level)
+      if not(p.nt.name in processed):
+        processed.add p.nt.name
+        p.nt.rule.recLoop level+1
+    of pkBackRef..pkBackRefIgnoreStyle:
+      outp.prt(p.kind, $p, level)
+    else:
+      outp.prt(p.kind, $p, level)
+      for s in items(p):
+        s.recLoop level+1
+
+  pegAst.recLoop
+  echo "PEG AST traversal output"
+  echo "------------------------"
+  echo outp.data
+
+block:
+  var
+    pStack {.threadvar.}: seq[string]
+    valStack {.threadvar.}: seq[float]
+    opStack {.threadvar.}: string
+  let
+    parseArithExpr = pegAst.eventParser:
+      pkNonTerminal:
+        enter:
+          pStack.add p.nt.name
+        leave:
+          pStack.setLen pStack.high
+          if length > 0:
+            let matchStr = s.substr(start, start+length-1)
+            case p.nt.name
+            of "Value":
+              try:
+                valStack.add matchStr.parseFloat
+                echo valStack
+              except ValueError:
+                discard
+            of "Sum", "Product":
+              try:
+                let val {.used.} = matchStr.parseFloat
+              except ValueError:
+                if valStack.len > 1 and opStack.len > 0:
+                  valStack[^2] = case opStack[^1]
+                  of '+': valStack[^2] + valStack[^1]
+                  of '-': valStack[^2] - valStack[^1]
+                  of '*': valStack[^2] * valStack[^1]
+                  else: valStack[^2] / valStack[^1]
+                  valStack.setLen valStack.high
+                  echo valStack
+                  opStack.setLen opStack.high
+                  echo opStack
+      pkChar:
+        leave:
+          if length == 1 and "Value" != pStack[^1]:
+            let matchChar = s[start]
+            opStack.add matchChar
+            echo opStack
+  echo "Event parser output"
+  echo "-------------------"
+  let pLen = parseArithExpr(txt)
+  doAssert txt.len == pLen
+
+
+import std/importutils
+
+block:
+  proc pegsTest() =
+    privateAccess(NonTerminal)
+    privateAccess(Captures)
+
+    if "test" =~ peg"s <- {{\ident}}": # bug #19104
+      doAssert matches[0] == "test"
+      doAssert matches[1] == "test", $matches[1]
+
+    doAssert escapePeg("abc''def'") == r"'abc'\x27\x27'def'\x27"
+    doAssert match("(a b c)", peg"'(' @ ')'")
+    doAssert match("W_HI_Le", peg"\y 'while'")
+    doAssert(not match("W_HI_L", peg"\y 'while'"))
+    doAssert(not match("W_HI_Le", peg"\y v'while'"))
+    doAssert match("W_HI_Le", peg"y'while'")
+
+    doAssert($ +digits == $peg"\d+")
+    doAssert "0158787".match(peg"\d+")
+    doAssert "ABC 0232".match(peg"\w+\s+\d+")
+    doAssert "ABC".match(peg"\d+ / \w+")
+
+    var accum: seq[string] = @[]
+    for word in split("00232this02939is39an22example111", peg"\d+"):
+      accum.add(word)
+    doAssert(accum == @["this", "is", "an", "example"])
+
+    doAssert matchLen("key", ident) == 3
+
+    var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident)
+    doAssert matchLen("key1=  cal9", pattern) == 11
+
+    var ws = newNonTerminal("ws", 1, 1)
+    ws.rule = *whitespace
+
+    var expr = newNonTerminal("expr", 1, 1)
+    expr.rule = sequence(capture(ident), *sequence(
+                  nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr)))
+
+    var c: Captures
+    var s = "a+b +  c +d+e+f"
+    doAssert rawMatch(s, expr.rule, 0, c) == len(s)
+    var a = ""
+    for i in 0..c.ml-1:
+      a.add(substr(s, c.matches[i][0], c.matches[i][1]))
+    doAssert a == "abcdef"
+    #echo expr.rule
+
+    #const filename = "lib/devel/peg/grammar.txt"
+    #var grammar = parsePeg(newFileStream(filename, fmRead), filename)
+    #echo "a <- [abc]*?".match(grammar)
+    doAssert find("_____abc_______", term("abc"), 2) == 5
+    doAssert match("_______ana", peg"A <- 'ana' / . A")
+    doAssert match("abcs%%%", peg"A <- ..A / .A / '%'")
+
+    var matches: array[0..MaxSubpatterns-1, string]
+    if "abc" =~ peg"{'a'}'bc' 'xyz' / {\ident}":
+      doAssert matches[0] == "abc"
+    else:
+      doAssert false
+
+    var g2 = peg"""S <- A B / C D
+                   A <- 'a'+
+                   B <- 'b'+
+                   C <- 'c'+
+                   D <- 'd'+
+                """
+    doAssert($g2 == "((A B) / (C D))")
+    doAssert match("cccccdddddd", g2)
+    doAssert("var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2") ==
+           "var1<-keykey; var2<-key2key2")
+    doAssert("var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2") ==
+           "$1<-$2$2; $1<-$2$2")
+    doAssert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}")
+
+    if "aaaaaa" =~ peg"'aa' !. / ({'a'})+":
+      doAssert matches[0] == "a"
+    else:
+      doAssert false
+
+    if match("abcdefg", peg"c {d} ef {g}", matches, 2):
+      doAssert matches[0] == "d"
+      doAssert matches[1] == "g"
+    else:
+      doAssert false
+
+    accum = @[]
+    for x in findAll("abcdef", peg".", 3):
+      accum.add(x)
+    doAssert(accum == @["d", "e", "f"])
+
+    for x in findAll("abcdef", peg"^{.}", 3):
+      doAssert x == "d"
+
+    if "f(a, b)" =~ peg"{[0-9]+} / ({\ident} '(' {@} ')')":
+      doAssert matches[0] == "f"
+      doAssert matches[1] == "a, b"
+    else:
+      doAssert false
+
+    doAssert match("eine übersicht und außerdem", peg"(\letter \white*)+")
+    # ß is not a lower cased letter?!
+    doAssert match("eine übersicht und auerdem", peg"(\lower \white*)+")
+    doAssert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+")
+    doAssert(not match("456678", peg"(\letter)+"))
+
+    doAssert("var1 = key; var2 = key2".replacef(
+      peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") ==
+           "var1<-keykey;var2<-key2key2")
+
+    doAssert match("prefix/start", peg"^start$", 7)
+
+    if "foo" =~ peg"{'a'}?.*":
+      doAssert matches[0].len == 0
+    else: doAssert false
+
+    if "foo" =~ peg"{''}.*":
+      doAssert matches[0] == ""
+    else: doAssert false
+
+    if "foo" =~ peg"{'foo'}":
+      doAssert matches[0] == "foo"
+    else: doAssert false
+
+    let empty_test = peg"^\d*"
+    let str = "XYZ"
+
+    doAssert(str.find(empty_test) == 0)
+    doAssert(str.match(empty_test))
+
+    proc handleMatches(m: int, n: int, c: openArray[string]): string =
+      result = ""
+
+      if m > 0:
+        result.add ", "
+
+      result.add case n:
+        of 2: toLowerAscii(c[0]) & ": '" & c[1] & "'"
+        of 1: toLowerAscii(c[0]) & ": ''"
+        else: ""
+
+    doAssert("Var1=key1;var2=Key2;   VAR3".
+      replace(peg"{\ident}('='{\ident})* ';'* \s*",
+      handleMatches) == "var1: 'key1', var2: 'Key2', var3: ''")
+
+
+    doAssert "test1".match(peg"""{@}$""")
+    doAssert "test2".match(peg"""{(!$ .)*} $""")
+
+    doAssert "abbb".match(peg"{a} {b} $2 $^1")
+    doAssert "abBA".match(peg"{a} {b} i$2 i$^2")
+
+    doAssert "abba".match(peg"{a} {b} $^1 {} $^1")
+
+    block:
+      let grammar = peg"""
+program <- {''} stmt* $
+stmt <- call / block
+call <- 'call()' EOL
+EOL <- \n / $
+block <- 'block:' \n indBody
+indBody <- {$^1 ' '+} stmt ($^1 stmt)* {}
+"""
+      let program = """
+call()
+block:
+  block:
+    call()
+    call()
+  call()
+call()
+"""
+      var c: Captures
+      doAssert program.len == program.rawMatch(grammar, 0, c)
+      doAssert c.ml == 1
+
+    block:
+      # bug #21632
+
+      let p = peg"""
+        atext <- \w / \d
+      """
+
+      doAssert "a".match(p)
+      doAssert "1".match(p)
+
+  pegsTest()
+  static:
+    pegsTest()
diff --git a/tests/stdlib/tpermutations.nim b/tests/stdlib/tpermutations.nim
deleted file mode 100644
index a6e07ded6..000000000
--- a/tests/stdlib/tpermutations.nim
+++ /dev/null
@@ -1,19 +0,0 @@
-discard """
-  output: '''@[0, 2, 1]
-@[1, 0, 2]
-@[1, 2, 0]
-@[2, 0, 1]
-@[2, 1, 0]
-@[2, 0, 1]
-@[1, 2, 0]
-@[1, 0, 2]
-@[0, 2, 1]
-@[0, 1, 2]'''
-"""
-import algorithm
-
-var v = @[0, 1, 2]
-while v.nextPermutation():
-  echo v
-while v.prevPermutation():
-  echo v
diff --git a/tests/stdlib/tposix.nim b/tests/stdlib/tposix.nim
index 229035d22..060482229 100644
--- a/tests/stdlib/tposix.nim
+++ b/tests/stdlib/tposix.nim
@@ -1,11 +1,17 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  disabled: windows
+"""
+
 # Test Posix interface
 
 when not defined(windows):
 
   import posix
+  import std/[assertions, syncio]
 
   var
-    u: Tutsname
+    u: Utsname
 
   discard uname(u)
 
@@ -14,3 +20,69 @@ when not defined(windows):
   writeLine(stdout, u.release)
   writeLine(stdout, u.machine)
 
+  when not (defined(nintendoswitch) or defined(macos) or defined(macosx)):
+    block:
+      type Message = object
+        value: int
+
+      const MQ_PATH: cstring = "/top_level_file"
+      const MQ_PRIORITY: cuint = 170
+      const MQ_MESSAGE_SIZE: csize_t = csize_t(sizeof(Message))
+
+      let mqd_a: posix.MqAttr = MqAttr(mq_maxmsg: 10, mq_msgsize: clong(MQ_MESSAGE_SIZE))
+      let writable: posix.Mqd = posix.mq_open(
+        MQ_PATH,
+        posix.O_CREAT or posix.O_WRONLY or posix.O_NONBLOCK,
+        posix.S_IRWXU,
+        addr(mqd_a)
+      )
+      let readable: posix.Mqd = posix.mq_open(
+        MQ_PATH,
+        posix.O_RDONLY or posix.O_NONBLOCK,
+        posix.S_IRWXU,
+        addr(mqd_a)
+      )
+
+      let sent: Message = Message(value: 88)
+      block:
+        let success: int = writable.mq_send(
+          cast[cstring](sent.addr),
+          MQ_MESSAGE_SIZE,
+          MQ_PRIORITY
+        )
+        doAssert success == 0, $success
+
+      block:
+        var buffer: Message
+        var priority: cuint
+        let bytesRead: int = readable.mq_receive(
+          cast[cstring](buffer.addr),
+          MQ_MESSAGE_SIZE,
+          priority
+        )
+        doAssert buffer == sent
+        doAssert bytesRead == int(MQ_MESSAGE_SIZE)
+
+  block:
+    var rl: RLimit
+    var res = getrlimit(RLIMIT_STACK, rl)
+    doAssert res == 0
+
+    # save old value
+    let oldrlim = rl.rlim_cur
+
+    # set new value
+    rl.rlim_cur = rl.rlim_max - 1
+    res = setrlimit(RLIMIT_STACK, rl)
+    doAssert res == 0
+
+    # get new value
+    var rl1: RLimit
+    res = getrlimit(RLIMIT_STACK, rl1)
+    doAssert res == 0
+    doAssert rl1.rlim_cur == rl.rlim_max - 1
+
+    # restore old value
+    rl.rlim_cur = oldrlim
+    res = setrlimit(RLIMIT_STACK, rl)
+    doAssert res == 0
diff --git a/tests/stdlib/tprelude.nim b/tests/stdlib/tprelude.nim
new file mode 100644
index 000000000..47f46b511
--- /dev/null
+++ b/tests/stdlib/tprelude.nim
@@ -0,0 +1,16 @@
+discard """
+  targets: "c js"
+  matrix: "; -d:nimTestTpreludeCase1"
+"""
+
+when defined nimTestTpreludeCase1:
+  include std/prelude
+else:
+  include prelude
+
+import std/assertions
+
+template main() =
+  doAssert toSeq(1..3) == @[1,2,3]
+static: main()
+main()
diff --git a/tests/stdlib/tquit.nim b/tests/stdlib/tquit.nim
deleted file mode 100644
index d18b468c8..000000000
--- a/tests/stdlib/tquit.nim
+++ /dev/null
@@ -1,6 +0,0 @@
-# Test the new beforeQuit variable:
-
-proc myExit() {.noconv.} =
-  write(stdout, "just exiting...\n")
-
-addQuitProc(myExit)
diff --git a/tests/stdlib/trandom.nim b/tests/stdlib/trandom.nim
new file mode 100644
index 000000000..eb32f7757
--- /dev/null
+++ b/tests/stdlib/trandom.nim
@@ -0,0 +1,310 @@
+discard """
+  joinable: false # to avoid messing with global rand state
+  matrix: "--mm:refc; --mm:orc; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on"
+"""
+import std/[assertions, formatfloat]
+import std/[random, math, stats, sets, tables]
+import std/private/jsutils
+when not defined(js):
+  import std/os
+
+randomize(233)
+
+proc main() =
+  var occur: array[1000, int]
+
+  for i in 0..100_000:
+    let x = rand(high(occur))
+    inc occur[x]
+
+  doAssert max(occur) <= 140 and min(occur) >= 60 # gives some slack
+
+  var a = [0, 1]
+  shuffle(a)
+  doAssert a in [[0,1], [1,0]]
+
+  doAssert rand(0) == 0
+  when not defined(nimscript):
+    doAssert sample("a") == 'a'
+
+  when compileOption("rangeChecks") and not defined(nimscript):
+    doAssertRaises(RangeDefect):
+      discard rand(-1)
+
+    doAssertRaises(RangeDefect):
+      discard rand(-1.0)
+
+  # don't use causes integer overflow
+  doAssert compiles(rand[int](low(int) .. high(int)))
+
+main()
+
+block:
+  when not defined(js):
+    doAssert almostEqual(rand(12.5), 7.355175342026979)
+    doAssert almostEqual(rand(2233.3322), 499.342386778917)
+
+  type DiceRoll = range[0..6]
+  when not defined(js):
+    doAssert rand(DiceRoll).int == 3
+  elif compileOption("jsbigint64"):
+    doAssert rand(DiceRoll).int == 1
+  else:
+    doAssert rand(DiceRoll).int == 6
+
+var rs: RunningStat
+for j in 1..5:
+  for i in 1 .. 100_000:
+    rs.push(gauss())
+  doAssert abs(rs.mean-0) < 0.08, $rs.mean
+  doAssert abs(rs.standardDeviation()-1.0) < 0.1
+  let bounds = [3.5, 5.0]
+  for a in [rs.max, -rs.min]:
+    doAssert a >= bounds[0] and a <= bounds[1]
+  rs.clear()
+
+block:
+  type DiceRoll = range[3..6]
+  var flag = false
+  for i in 0..<100:
+    if rand(5.DiceRoll) < 3:
+      flag = true
+  doAssert flag # because of: rand(max: int): int
+
+
+block: # random int
+  block: # there might be some randomness
+    var set = initHashSet[int](128)
+
+    for i in 1..1000:
+      incl(set, rand(high(int)))
+    doAssert len(set) == 1000
+
+  block: # single number bounds work
+    var rand: int
+    for i in 1..1000:
+      rand = rand(1000)
+      doAssert rand <= 1000
+      doAssert rand >= 0
+
+  block: # slice bounds work
+    var rand: int
+    for i in 1..1000:
+      rand = rand(100..1000)
+      doAssert rand <= 1000
+      doAssert rand >= 100
+
+  block: # again gives new numbers
+    var rand1 = rand(1000000)
+    when not (defined(js) or defined(nimscript)):
+      os.sleep(200)
+
+    var rand2 = rand(1000000)
+    doAssert rand1 != rand2
+
+block: # random float
+  block: # there might be some randomness
+    var set = initHashSet[float](128)
+
+    for i in 1..100:
+      incl(set, rand(1.0))
+    doAssert len(set) == 100
+
+  block: # single number bounds work
+    var rand: float
+    for i in 1..1000:
+      rand = rand(1000.0)
+      doAssert rand <= 1000.0
+      doAssert rand >= 0.0
+
+  block: # slice bounds work
+    var rand: float
+    for i in 1..1000:
+      rand = rand(100.0..1000.0)
+      doAssert rand <= 1000.0
+      doAssert rand >= 100.0
+
+  block: # again gives new numbers
+    var rand1: float = rand(1000000.0)
+    when not (defined(js) or defined(nimscript)):
+      os.sleep(200)
+
+    var rand2: float = rand(1000000.0)
+    doAssert rand1 != rand2
+
+block: # random sample
+  block: # "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))
+    doAssert 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:
+        doAssert 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))
+      doAssert abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev
+
+  block: # 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))
+    doAssert histo.len == 4                  # number of non-zero in ``counts``
+    for i, c in counts:
+      if c == 0:
+        doAssert 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.
+      doAssert abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev
+
+block:
+  # 0 is a valid seed
+  var r = initRand(0)
+  doAssert r.rand(1.0) != r.rand(1.0)
+  r = initRand(10)
+  doAssert r.rand(1.0) != r.rand(1.0)
+  # changing the seed changes the sequence
+  var r1 = initRand(123)
+  var r2 = initRand(124)
+  doAssert r1.rand(1.0) != r2.rand(1.0)
+
+block: # bug #17467
+  let n = 1000
+  for i in -n .. n:
+    var r = initRand(i)
+    let x = r.rand(1.0)
+    doAssert x > 1e-4, $(x, i)
+      # This used to fail for each i in 0..<26844, i.e. the 1st produced value
+      # was predictable and < 1e-4, skewing distributions.
+
+block: # bug #16360, Natural overload
+  var r = initRand()
+  template test(a) =
+    let a2 = a
+    block:
+      let a3 = r.rand(a2)
+      doAssert a3 <= a2
+      doAssert a3.type is a2.type
+    block:
+      let a3 = rand(a2)
+      doAssert a3 <= a2
+      doAssert a3.type is a2.type
+  test int.high
+  test int.high - 1
+  test int.high - 2
+  test 0
+
+block: # same as above but use slice overload
+  var r = initRand()
+  template test[T](a: T) =
+    let a2: T = a
+    block:
+      let a3 = r.rand(T(0) .. a2)
+      doAssert a3 <= a2
+      doAssert a3.type is a2.type
+    block:
+      let a3 = rand(T(0) .. a2)
+      doAssert a3 <= a2
+      doAssert a3.type is a2.type
+  test cast[uint](int.high)
+  test cast[uint](int.high) + 1
+  whenJsNoBigInt64: discard
+  do:
+    test uint64.high
+    test uint64.high - 1
+  test uint.high - 2
+  test uint.high - 1
+  test uint.high
+  test int.high
+  test int.high - 1
+  test int.high - 2
+  test 0
+  test 0'u
+  test 0'u64
+
+block: # bug #16296
+  var r = initRand()
+  template test(x) =
+    let a2 = x
+    let a3 = r.rand(a2)
+    doAssert a3 <= a2.b
+    doAssert a3 >= a2.a
+    doAssert a3.type is a2.a.type
+  test(-2 .. int.high-1)
+  test(int.low .. int.high)
+  test(int.low+1 .. int.high)
+  test(int.low .. int.high-1)
+  test(int.low .. 0)
+  test(int.low .. -1)
+  test(int.low .. 1)
+  test(int64.low .. 1'i64)
+  test(10'u64 .. uint64.high)
+
+block: # bug #17670
+  type UInt48 = range[0'u64..2'u64^48-1]
+  let x = rand(UInt48)
+  doAssert x is UInt48
+
+block: # bug #17898
+  # Checks whether `initRand()` generates unique states.
+  # size should be 2^64, but we don't have time and space.
+
+  # Disable this test for js until js gets proper skipRandomNumbers.
+  when not defined(js):
+    const size = 1000
+    var
+      rands: array[size, Rand]
+      randSet: HashSet[Rand]
+    for i in 0..<size:
+      rands[i] = initRand()
+      randSet.incl rands[i]
+
+    doAssert randSet.len == size
+
+    # Checks random number sequences overlapping.
+    const numRepeat = 100
+    for i in 0..<size:
+      for j in 0..<numRepeat:
+        discard rands[i].next
+        doAssert rands[i] notin randSet
+
+block: # bug #22360
+  const size = 1000
+  var fc = 0
+  var tc = 0
+
+  for _ in 1..size:
+    let s = rand(bool)
+
+    if s:
+      inc tc
+    else:
+      inc fc
+
+  when defined(js) and not compileOption("jsbigint64"):
+    doAssert (tc, fc) == (515, 485), $(tc, fc)
+  else:
+    doAssert (tc, fc) == (510, 490), $(tc, fc)
+
+block:
+  when defined(js) and not compileOption("jsbigint64"):
+    doAssert rand(int32.high) == 335507522
+  else:
+    doAssert rand(int32.high) == 607539621
diff --git a/tests/rational/trat_float.nim b/tests/stdlib/trat_float.nim
index 24797c4a0..663973bf9 100644
--- a/tests/rational/trat_float.nim
+++ b/tests/stdlib/trat_float.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: '''type mismatch: got'''
   file: "trat_float.nim"
   line: "9,19"
-  errormsg: '''type mismatch: got'''
 """
 import rationals
 var
diff --git a/tests/rational/trat_init.nim b/tests/stdlib/trat_init.nim
index 360a48537..2be0c0099 100644
--- a/tests/rational/trat_init.nim
+++ b/tests/stdlib/trat_init.nim
@@ -10,5 +10,5 @@ var
 try:
   var
     r = initRational(1, 0)  # this fails - no zero denominator
-except AssertionError:
+except AssertionDefect:
   echo "true"
diff --git a/tests/stdlib/trationals.nim b/tests/stdlib/trationals.nim
new file mode 100644
index 000000000..22d7f5c2d
--- /dev/null
+++ b/tests/stdlib/trationals.nim
@@ -0,0 +1,117 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/[rationals, math]
+import std/assertions
+
+template main() =
+  var
+    z = Rational[int](num: 0, den: 1)
+    o = initRational(num = 1, den = 1)
+    a = initRational(1, 2)
+    u = 3u // 2
+    b = -1 // -2
+    m1 = -1 // 1
+    tt = 10 // 2
+
+  doAssert a == a
+  doAssert a - a == z
+  doAssert a + b == o
+  doAssert a / b == o
+  doAssert a * b == 1 // 4
+  doAssert 3 / a == 6 // 1
+  doAssert a / 3 == 1 // 6
+  doAssert tt * z == z
+  doAssert 10 * a == tt
+  doAssert a * 10 == tt
+  doAssert tt / 10 == a
+  doAssert a - m1 == 3 // 2
+  doAssert a + m1 == -1 // 2
+  doAssert m1 + tt == 16 // 4
+  doAssert m1 - tt == 6 // -1
+
+  doAssert z < o
+  doAssert z <= o
+  doAssert z == z
+  doAssert cmp(z, o) < 0
+  doAssert cmp(o, z) > 0
+
+  doAssert o == o
+  doAssert o >= o
+  doAssert not(o > o)
+  doAssert cmp(o, o) == 0
+  doAssert cmp(z, z) == 0
+  doAssert hash(o) == hash(o)
+
+  doAssert a == b
+  doAssert a >= b
+  doAssert not(b > a)
+  doAssert cmp(a, b) == 0
+  doAssert hash(a) == hash(b)
+
+  var x = 1 // 3
+
+  x *= 5 // 1
+  doAssert x == 5 // 3
+  x += 2 // 9
+  doAssert x == 17 // 9
+  x -= 9 // 18
+  doAssert x == 25 // 18
+  x /= 1 // 2
+  doAssert x == 50 // 18
+
+  var y = 1 // 3
+
+  y *= 4
+  doAssert y == 4 // 3
+  y += 5
+  doAssert y == 19 // 3
+  y -= 2
+  doAssert y == 13 // 3
+  y /= 9
+  doAssert y == 13 // 27
+
+  doAssert toRational(5) == 5 // 1
+  doAssert abs(toFloat(y) - 0.4814814814814815) < 1.0e-7
+  doAssert toInt(z) == 0
+
+  when sizeof(int) == 8:
+    doAssert toRational(0.98765432) == 2111111029 // 2137499919
+    doAssert toRational(PI) == 817696623 // 260280919
+  when sizeof(int) == 4:
+    doAssert toRational(0.98765432) == 80 // 81
+    doAssert toRational(PI) == 355 // 113
+
+  doAssert toRational(0.1) == 1 // 10
+  doAssert toRational(0.9) == 9 // 10
+
+  doAssert toRational(0.0) == 0 // 1
+  doAssert toRational(-0.25) == 1 // -4
+  doAssert toRational(3.2) == 16 // 5
+  doAssert toRational(0.33) == 33 // 100
+  doAssert toRational(0.22) == 11 // 50
+  doAssert toRational(10.0) == 10 // 1
+
+  doAssert (1 // 1) div (3 // 10) == 3
+  doAssert (-1 // 1) div (3 // 10) == -3
+  doAssert (3 // 10) mod (1 // 1) == 3 // 10
+  doAssert (-3 // 10) mod (1 // 1) == -3 // 10
+  doAssert floorDiv(1 // 1, 3 // 10) == 3
+  doAssert floorDiv(-1 // 1, 3 // 10) == -4
+  doAssert floorMod(3 // 10, 1 // 1) == 3 // 10
+  doAssert floorMod(-3 // 10, 1 // 1) == 7 // 10
+
+  when sizeof(int) == 8:
+    doAssert almostEqual(PI.toRational.toFloat, PI)
+
+  # unsigned
+  doAssert u == u
+  doAssert u + u == 3u // 1
+  doAssert 3u.toRational - u == u
+  doAssert u * 2 == 3u // 1
+
+
+
+static: main()
+main()
diff --git a/tests/stdlib/tre.nim b/tests/stdlib/tre.nim
new file mode 100644
index 000000000..39637434d
--- /dev/null
+++ b/tests/stdlib/tre.nim
@@ -0,0 +1,122 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/re
+import std/assertions
+
+proc testAll() =
+  doAssert match("(a b c)", rex"\( .* \)")
+  doAssert match("WHiLe", re("while", {reIgnoreCase}))
+
+  doAssert "0158787".match(re"\d+")
+  doAssert "ABC 0232".match(re"\w+\s+\d+")
+  doAssert "ABC".match(rex"\d+ | \w+")
+
+  {.push warnings:off.}
+  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]+"
+  doAssert matchLen("key1=  cal9", pattern) == 11
+
+  doAssert find("_____abc_______", re"abc") == 5
+  doAssert findBounds("_____abc_______", re"abc") == (5,7)
+
+  var matches: array[6, string]
+  if match("abcdefg", re"c(d)ef(g)", matches, 2):
+    doAssert matches[0] == "d"
+    doAssert matches[1] == "g"
+  else:
+    doAssert false
+
+  if "abc" =~ re"(a)bcxyz|(\w+)":
+    doAssert matches[1] == "abc"
+  else:
+    doAssert false
+
+  if "abc" =~ re"(cba)?.*":
+    doAssert matches[0] == ""
+  else: doAssert false
+
+  if "abc" =~ re"().*":
+    doAssert matches[0] == ""
+  else: doAssert false
+
+  doAssert "var1=key; var2=key2".endsWith(re"\w+=\w+")
+  doAssert("var1=key; var2=key2".replacef(re"(\w+)=(\w+)", "$1<-$2$2") ==
+         "var1<-keykey; var2<-key2key2")
+  doAssert("var1=key; var2=key2".replace(re"(\w+)=(\w+)", "$1<-$2$2") ==
+         "$1<-$2$2; $1<-$2$2")
+
+  var accum: seq[string] = @[]
+  for word in split("00232this02939is39an22example111", re"\d+"):
+    accum.add(word)
+  doAssert(accum == @["", "this", "is", "an", "example", ""])
+
+  accum = @[]
+  for word in split("00232this02939is39an22example111", re"\d+", maxsplit=2):
+    accum.add(word)
+  doAssert(accum == @["", "this", "is39an22example111"])
+
+  accum = @[]
+  for word in split("AAA :   : BBB", re"\s*:\s*"):
+    accum.add(word)
+  doAssert(accum == @["AAA", "", "BBB"])
+
+  doAssert(split("abc", re"") == @["a", "b", "c"])
+  doAssert(split("", re"") == @[])
+
+  doAssert(split("a;b;c", re";") == @["a", "b", "c"])
+  doAssert(split(";a;b;c", re";") == @["", "a", "b", "c"])
+  doAssert(split(";a;b;c;", re";") == @["", "a", "b", "c", ""])
+  doAssert(split("a;b;c;", re";") == @["a", "b", "c", ""])
+  doAssert(split("00232this02939is39an22example111", re"\d+", maxsplit=2) == @["", "this", "is39an22example111"])
+
+
+  for x in findAll("abcdef", re"^{.}", 3):
+    doAssert x == "d"
+  accum = @[]
+  for x in findAll("abcdef", re".", 3):
+    accum.add(x)
+  doAssert(accum == @["d", "e", "f"])
+
+  doAssert("XYZ".find(re"^\d*") == 0)
+  doAssert("XYZ".match(re"^\d*") == true)
+
+  block:
+    var matches: array[16, string]
+    if match("abcdefghijklmnop", re"(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)(m)(n)(o)(p)", matches):
+      for i in 0..matches.high:
+        doAssert matches[i] == $chr(i + 'a'.ord)
+    else:
+      doAssert false
+
+  block:   # Buffer based RE
+    var cs: cstring = "_____abc_______"
+    doAssert(cs.find(re"abc", bufSize=15) == 5)
+    doAssert(cs.matchLen(re"_*abc", bufSize=15) == 8)
+    doAssert(cs.matchLen(re"abc", start=5, bufSize=15) == 3)
+    doAssert(cs.matchLen(re"abc", start=5, bufSize=7) == -1)
+    doAssert(cs.matchLen(re"abc_*", start=5, bufSize=10) == 5)
+    var accum: seq[string] = @[]
+    for x in cs.findAll(re"[a-z]", start=3, bufSize=15):
+      accum.add($x)
+    doAssert(accum == @["a","b","c"])
+
+  block: # bug #9306
+    doAssert replace("bar", re"^", "foo") == "foobar"
+    doAssert replace("foo", re"$", "bar") == "foobar"
+
+
+  block: # bug #9437
+    doAssert replace("foo", re"", "-") == "-f-o-o-"
+    doAssert replace("ooo", re"o", "-") == "---"
+
+  block: # bug #14468
+    accum = @[]
+    for word in split("this is an example", re"\b"):
+      accum.add(word)
+    doAssert(accum == @["this", " ", "is", " ", "an", " ", "example"])
+
+testAll()
diff --git a/tests/stdlib/treadln.nim b/tests/stdlib/treadln.nim
new file mode 100644
index 000000000..4a070e848
--- /dev/null
+++ b/tests/stdlib/treadln.nim
@@ -0,0 +1,23 @@
+
+discard """
+output: '''
+test the improved readline handling that does not care whether its
+Macintosh, Unix or Windows text format.
+'''
+"""
+
+import std/syncio
+
+# test the improved readline handling that does not care whether its
+# Macintosh, Unix or Windows text format.
+
+var
+  inp: File
+  line: string
+
+if open(inp, "tests/stdlib/treadln.nim"):
+  while not endOfFile(inp):
+    line = readLine(inp)
+    if line.len >= 2 and line[0] == '#' and line[1] == ' ':
+      echo line[2..^1]
+  close(inp)
diff --git a/tests/stdlib/tregex.nim b/tests/stdlib/tregex.nim
index ae6714de1..9dd66cd60 100644
--- a/tests/stdlib/tregex.nim
+++ b/tests/stdlib/tregex.nim
@@ -1,6 +1,6 @@
 discard """
-  file: "tregex.nim"
   output: "key: keyAYes!"
+  matrix: "--mm:refc; --mm:orc"
 """
 # Test the new regular expression module
 # which is based on the PCRE library
@@ -12,7 +12,7 @@ when defined(powerpc64):
 else:
   import
     re
-
+  import std/syncio
   if "keyA = valueA" =~ re"\s*(\w+)\s*\=\s*(\w+)":
     write(stdout, "key: ", matches[0])
   elif "# comment!" =~ re.re"\s*(\#.*)":
@@ -27,5 +27,3 @@ else:
     echo("Bug!")
 
   #OUT key: keyAYes!
-
-
diff --git a/tests/stdlib/tregistry.nim b/tests/stdlib/tregistry.nim
new file mode 100644
index 000000000..25aed8df8
--- /dev/null
+++ b/tests/stdlib/tregistry.nim
@@ -0,0 +1,16 @@
+discard """
+  disabled: "unix"
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+when defined(windows):
+  import std/registry
+  import std/assertions
+
+  block: # bug #14010
+    let path = "Environment"
+    let key = "D20210328T202842_key"
+    let val = "D20210328T202842_val"
+    let handle = HKEY_CURRENT_USER
+    setUnicodeValue("Environment", key, val, handle)
+    doAssert getUnicodeValue(path, key, handle) == val
diff --git a/tests/stdlib/treguse.nim b/tests/stdlib/treguse.nim
deleted file mode 100644
index 3d09eb731..000000000
--- a/tests/stdlib/treguse.nim
+++ /dev/null
@@ -1,27 +0,0 @@
-discard """
-  file: "treguse.nim"
-  output: "055this should be the casehugh"
-"""
-# Test the register usage of the virtual machine and
-# the blocks in var statements
-
-proc main(a, b: int) =
-  var x = 0
-  write(stdout, x)
-  if x == 0:
-    var y = 55
-    write(stdout, y)
-    write(stdout, "this should be the case")
-    var input = "<no input>"
-    if input == "Andreas":
-      write(stdout, "wow")
-    else:
-      write(stdout, "hugh")
-  else:
-    var z = 66
-    write(stdout, z) # "bug!")
-
-main(45, 1000)
-#OUT 055this should be the casehugh
-
-
diff --git a/tests/stdlib/treloop.nim b/tests/stdlib/treloop.nim
deleted file mode 100644
index b4221525d..000000000
--- a/tests/stdlib/treloop.nim
+++ /dev/null
@@ -1,9 +0,0 @@
-discard """
-  output: '''@["(", "+", " 1", " 2", ")"]'''
-"""
-
-import re
-
-let str = "(+ 1 2)"
-var tokenRE = re"""[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)"""
-echo str.findAll(tokenRE)
diff --git a/tests/stdlib/trepr.nim b/tests/stdlib/trepr.nim
index 18fe7e054..3956b98f9 100644
--- a/tests/stdlib/trepr.nim
+++ b/tests/stdlib/trepr.nim
@@ -1,29 +1,328 @@
 discard """
-  file: "trepr.nim"
-  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'}"
+  targets: "c cpp js"
+  matrix: "--mm:refc;--mm:arc"
 """
 
-type
-  TEnum = enum
-    a, b
-
-var val = {a, b}
-stdout.write(repr(val))
-stdout.writeLine(repr({'a'..'z', 'A'..'Z'}))
-
-type
-  TObj {.pure, inheritable.} = object
-    data: int
-  TFoo = ref object of TObj
-    d2: float
-var foo: TFoo
-new(foo)
-
-when false:
-  # cannot capture this output as it contains a memory address :-/
-  echo foo.repr
-#var testseq: seq[string] = @[
-#  "a", "b", "c", "d", "e"
-#]
-#echo(repr(testseq))
+# if excessive, could remove 'cpp' from targets
 
+from strutils import endsWith, contains, strip
+from std/macros import newLit
+import std/assertions
+
+macro deb(a): string = newLit a.repr.strip
+macro debTyped(a: typed): string = newLit a.repr.strip
+
+template main() =
+  doAssert repr({3,5}) == "{3, 5}"
+
+  block:
+    type TEnum = enum a, b
+    var val = {a, b}
+    when nimvm:
+      discard
+      #[
+      # BUG:
+      {0, 1}
+      {97..99, 65..67}
+      ]#
+    else:
+      doAssert repr(val) == "{a, b}"
+      doAssert repr({'a'..'c', 'A'..'C'}) == "{'A', 'B', 'C', 'a', 'b', 'c'}"
+
+    type
+      TObj {.pure, inheritable.} = object
+        data: int
+      TFoo = ref object of TObj
+        d2: float
+    var foo: TFoo
+    new(foo)
+
+  #[
+  BUG:
+  --gc:arc returns `"abc"`
+  regular gc returns with address, e.g. 0x1068aae60"abc", but only
+  for c,cpp backends (not js, vm)
+  ]#
+  block:
+    doAssert repr("abc").endsWith "\"abc\""
+    var b: cstring = "def"
+    doAssert repr(b).endsWith "\"def\""
+
+  block:
+    var c = @[1,2]
+    when nimvm:
+      discard # BUG: this shows [1, 2] instead of @[1, 2]
+    else:
+      # BUG (already mentioned above): some backends / gc show address, others don't
+      doAssert repr(c).endsWith "@[1, 2]"
+
+    let d = @["foo", "bar"]
+    let s = repr(d)
+    # depending on backend/gc, we get 0x106a1c350@[0x106a1c390"foo", 0x106a1c3c0"bar"]
+    doAssert "\"foo\"," in s
+
+  var arr = [1, 2, 3]
+  doAssert repr(arr) == "[1, 2, 3]"
+
+  block: # bug #7878
+    proc reprOpenarray(variable: var openArray[int]): string = repr(variable)
+    when defined(js): discard # BUG: doesn't work
+    else:
+      doAssert reprOpenarray(arr) == "[1, 2, 3]"
+
+  block: # bug #17292 repr with `do`
+    template foo(a, b, c, d) = discard
+    block:
+      let a = deb:
+        foo(1, 2, 3, 4)
+      doAssert a == "foo(1, 2, 3, 4)"
+    block:
+      let a = deb:
+        foo(1, 2, 3): 4
+      doAssert a == """
+foo(1, 2, 3):
+  4"""
+
+    block:
+      let a = deb:
+        foo(1, 2): 3
+        do: 4
+      doAssert a == """
+foo(1, 2):
+  3
+do:
+  4"""
+
+    block:
+      let a = deb:
+        foo(1): 3
+        do: 3
+        do: 4
+      doAssert a == """
+foo(1):
+  3
+do:
+  3
+do:
+  4"""
+
+    block:
+      let a = deb:
+        foo(1):
+          3
+        do:
+          discard
+          3
+        do:
+          discard
+          4
+
+      doAssert a == """
+foo(1):
+  3
+do:
+  discard
+  3
+do:
+  discard
+  4"""
+
+    block:
+      let a = deb:
+        foo: 1
+        do: 2
+        do: 3
+        do: 4
+      doAssert a == """
+foo:
+  1
+do:
+  2
+do:
+  3
+do:
+  4"""
+
+  block: # bug #17292 repr with `(discard)` (`discard` would result in illegal code)
+    let a = deb:
+      let f {.inject.} = () => (discard)
+    doAssert a == """
+let f {.inject.} = () =>
+    (discard )"""
+
+    let a2 = deb:
+      block:
+        discard
+      discard
+
+      block:
+        when true: discard
+
+      # let a = b => discard # illegal
+      discard b => (discard) # legal
+
+      block:
+        return
+    doAssert a2 == """
+block:
+  discard
+discard
+block:
+  when true:
+    discard
+discard b =>
+    (discard )
+block:
+  return"""
+
+  block: # bug #17292 (bug 4)
+    let a = deb:
+      proc `=destroy`() = discard
+      proc `'foo`(): int = discard
+      proc `foo bar baz`(): int = discard
+    let a2 = """
+proc `=destroy`() =
+  discard
+
+proc `'foo`(): int =
+  discard
+
+proc `foo bar baz`(): int =
+  discard"""
+    doAssert a2 == a
+
+  block: # setters: `foo=`
+    let a = deb:
+      proc `foo=`() = discard
+    doAssert a == """
+proc `foo=`() =
+  discard"""
+
+  block: # bug #14850
+    block:
+      let a = deb:
+        template bar(): untyped =
+          foo1:
+            discard
+            4
+          foo2(1):
+            discard
+            4
+          foo3(1):
+            discard
+            4
+          do: 1
+          do: 2
+          x.add foo4
+          x.add: foo5: 3
+          x.add foo6 do: 4
+          a.add(foo7 do:
+            echo "baz"
+            4)
+
+      doAssert a == """
+template bar(): untyped =
+  foo1:
+    discard
+    4
+  foo2(1):
+    discard
+    4
+  foo3(1):
+    discard
+    4
+  do:
+    1
+  do:
+    2
+  x.add foo4
+  x.add:
+    foo5:
+      3
+  x.add foo6 do:
+    4
+  a.add(foo7 do:
+    echo "baz"
+    4)"""
+
+  block: # one liner doc comments
+    let a1 = deb:
+      func fn1(): int = 1  ## comment
+      func fn2(): int = 1
+        ## comment
+    let a2 = debTyped:
+      func fn1(): int = 1  ## comment
+      func fn2(): int = 1
+        ## comment
+    doAssert a1 == """
+func fn1(): int =
+  ## comment
+  1
+
+func fn2(): int =
+  ## comment
+  1"""
+    doAssert a2 == """
+func fn1(): int =
+  ## comment
+  result = 1
+
+func fn2(): int =
+  ## comment
+  result = 1"""
+
+  block: # block calls
+    let a = deb:
+      foo(a, b, (c, d)):
+        e
+        f
+      do: g
+      of h: i
+      elif j: k
+      except m: n
+      do () -> u: v
+      finally: o
+
+      a + b:
+        c
+        d
+      do:
+        e
+        f
+      else: g
+
+      *a: b
+      do: c
+
+    doAssert a == """foo(a, b, (c, d)):
+  e
+  f
+do:
+  g
+of h:
+  i
+elif j:
+  k
+except m:
+  n
+do -> u:
+  v
+finally:
+  o
+a + b:
+  c
+  d
+do:
+  e
+  f
+else:
+  g
+*a:
+  b
+do:
+  c"""
+
+  doAssert repr(1..2) == "1 .. 2"
+
+static: main()
+main()
diff --git a/tests/stdlib/trepr2.nim b/tests/stdlib/trepr2.nim
deleted file mode 100644
index 300df565d..000000000
--- a/tests/stdlib/trepr2.nim
+++ /dev/null
@@ -1,32 +0,0 @@
-# test the new "repr" built-in proc
-
-type
-  TEnum = enum
-    en1, en2, en3, en4, en5, en6
-
-  TPoint {.final.} = object
-    x, y, z: int
-    s: array[0..1, string]
-    e: TEnum
-
-var
-  p: TPoint
-  q: ref TPoint
-  s: seq[ref TPoint]
-
-p.x = 0
-p.y = 13
-p.z = 45
-p.s[0] = "abc"
-p.s[1] = "xyz"
-p.e = en6
-
-new(q)
-q[] = p
-
-s = @[q, q, q, q]
-
-writeLine(stdout, repr(p))
-writeLine(stdout, repr(q))
-writeLine(stdout, repr(s))
-writeLine(stdout, repr(en4))
diff --git a/tests/stdlib/trlocks.nim b/tests/stdlib/trlocks.nim
new file mode 100644
index 000000000..135d9b028
--- /dev/null
+++ b/tests/stdlib/trlocks.nim
@@ -0,0 +1,14 @@
+discard """
+  action: "compile"
+  # Disallow joining to ensure it can compile in isolation.
+  # See #15584
+  joinable: false
+  cmd: "nim $target --threads:on $options $file"
+"""
+
+# bugfix #15584
+
+import rlocks
+
+var r: RLock
+r.initRLock()
diff --git a/tests/stdlib/tropes.nim b/tests/stdlib/tropes.nim
new file mode 100644
index 000000000..eb0edc364
--- /dev/null
+++ b/tests/stdlib/tropes.nim
@@ -0,0 +1,104 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c js"
+"""
+
+import std/ropes
+import std/assertions
+
+template main() =
+  block:
+    let r: Rope = nil
+    doAssert r[0] == '\0'
+    doAssert $r == ""
+
+  block:
+    var
+      r1 = rope("Hello, ")
+      r2 = rope("Nim-Lang")
+
+    let r = r1 & r2
+    let s = $r
+    doAssert s == "Hello, Nim-Lang"
+    for i in 0 ..< r.len:
+      doAssert r[i] == s[i]
+
+    doAssert r[66] == '\0'
+
+  block:
+    let r = rope("Hello, Nim-Lang")
+
+    let s = $r
+    doAssert s == "Hello, Nim-Lang"
+    for i in 0 ..< r.len:
+      doAssert r[i] == s[i]
+
+    doAssert r[66] == '\0'
+
+  block:
+    var r: Rope
+    r.add rope("Nim ")
+    r.add rope("is ")
+    r.add rope("a ")
+    r.add rope("great ")
+    r.add rope("language")
+
+    let s = $r
+    doAssert s == "Nim is a great language"
+    for i in 0 ..< r.len:
+      doAssert r[i] == s[i]
+
+    doAssert r[66] == '\0'
+
+  block:
+    var r: Rope
+    r.add rope("My Conquest")
+    r.add rope(" is ")
+    r.add rope("the Sea of Stars")
+
+    let s = $r
+    doAssert s == "My Conquest is the Sea of Stars"
+    for i in 0 ..< r.len:
+      doAssert r[i] == s[i]
+
+    doAssert r[66] == '\0'
+
+  block:
+    var r: Rope
+    r.add rope("My Conquest")
+    r.add rope(" is ")
+    r.add rope("the Sea of Stars")
+
+    doAssert $r == "My Conquest is the Sea of Stars"
+
+    var i: int
+    for item in r:
+      doAssert r[i] == item
+      inc i
+
+    doAssert r[66] == '\0'
+
+  block:
+    let r1 = "$1 $2 $3" % [rope("Nim"), rope("is"), rope("a great language")]
+    doAssert $r1 == "Nim is a great language"
+
+    let r2 = "$# $# $#" % [rope("Nim"), rope("is"), rope("a great language")]
+    doAssert $r2 == "Nim is a great language"
+
+  block: # `[]`
+    let r1 = rope("Hello, Nim!")
+
+    doAssert r1[-2] == '\0'
+    doAssert r1[0] == 'H'
+    doAssert r1[7] == 'N'
+    doAssert r1[22] == '\0'
+
+    let r2 = rope("Hello") & rope(", Nim!")
+
+    doAssert r2[-2] == '\0'
+    doAssert r2[0] == 'H'
+    doAssert r2[7] == 'N'
+    doAssert r2[22] == '\0'
+
+static: main()
+main()
diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim
new file mode 100644
index 000000000..ceab34bc9
--- /dev/null
+++ b/tests/stdlib/trst.nim
@@ -0,0 +1,1994 @@
+discard """
+  output: '''
+
+[Suite] RST parsing
+
+[Suite] RST tables
+
+[Suite] RST indentation
+
+[Suite] Markdown indentation
+
+[Suite] Warnings
+
+[Suite] RST include directive
+
+[Suite] RST escaping
+
+[Suite] RST inline markup
+
+[Suite] Misc isssues
+'''
+matrix: "--mm:refc; --mm:orc"
+"""
+
+# tests for rst module
+
+import ../../lib/packages/docutils/[rstgen, rst, rstast]
+import unittest, strutils
+import std/private/miscdollars
+import os
+import std/[assertions, syncio]
+
+const preferMarkdown = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled}
+# legacy nimforum / old default mode:
+const preferRst = {roSupportMarkdown, roNimFile, roSandboxDisabled}
+const pureRst = {roNimFile, roSandboxDisabled}
+
+proc toAst(input: string,
+            rstOptions: RstParseOptions = preferMarkdown,
+            error: ref string = nil,
+            warnings: ref seq[string] = nil): string =
+  ## If `error` is nil then no errors should be generated.
+  ## The same goes for `warnings`.
+  proc testMsgHandler(filename: string, line, col: int, msgkind: MsgKind,
+                      arg: string) =
+    let mc = msgkind.whichMsgClass
+    let a = $msgkind % arg
+    var message: string
+    toLocation(message, filename, line, col + ColRstOffset)
+    message.add " $1: $2" % [$mc, a]
+    if mc == mcError:
+      if error == nil:
+        raise newException(EParseError, "[unexpected error] " & message)
+      error[] = message
+      # we check only first error because subsequent ones may be meaningless
+      raise newException(EParseError, "")
+    else:
+      doAssert warnings != nil, "unexpected RST warning '" & message & "'"
+      warnings[].add message
+  try:
+    const filen = "input"
+
+    proc myFindFile(filename: string): string =
+      # we don't find any files in online mode:
+      result = ""
+
+    var (rst, _, _) = rstParse(input, filen, line=LineRstInit, column=ColRstInit,
+                               rstOptions, myFindFile, nil, testMsgHandler)
+    result = treeRepr(rst)
+  except EParseError as e:
+    if e.msg != "":
+      result = e.msg
+
+suite "RST parsing":
+  test "Standalone punctuation is not parsed as heading overlines":
+    check(dedent"""
+        Paragraph
+
+        !""".toAst ==
+      dedent"""
+        rnInner
+          rnParagraph
+            rnLeaf  'Paragraph'
+          rnParagraph
+            rnLeaf  '!'
+      """)
+
+    check(dedent"""
+        Paragraph1
+
+        ...
+
+        Paragraph2""".toAst ==
+      dedent"""
+        rnInner
+          rnParagraph
+            rnLeaf  'Paragraph1'
+          rnParagraph
+            rnLeaf  '...'
+          rnParagraph
+            rnLeaf  'Paragraph2'
+      """)
+
+    check(dedent"""
+        ---
+        Paragraph""".toAst ==
+      dedent"""
+        rnInner
+          rnLeaf  '---'
+          rnLeaf  ' '
+          rnLeaf  'Paragraph'
+      """)
+
+  test "References are whitespace-neutral and case-insensitive":
+    # refname is 'lexical-analysis', the same for all the 3 variants:
+    check(dedent"""
+        Lexical Analysis
+        ================
+
+        Ref. `Lexical Analysis`_ or `Lexical analysis`_ or `lexical analysis`_.
+        """.toAst ==
+      dedent"""
+        rnInner
+          rnHeadline  level=1  anchor='lexical-analysis'
+            rnLeaf  'Lexical'
+            rnLeaf  ' '
+            rnLeaf  'Analysis'
+          rnParagraph
+            rnLeaf  'Ref'
+            rnLeaf  '.'
+            rnLeaf  ' '
+            rnInternalRef
+              rnInner
+                rnLeaf  'Lexical'
+                rnLeaf  ' '
+                rnLeaf  'Analysis'
+              rnLeaf  'lexical-analysis'
+            rnLeaf  ' '
+            rnLeaf  'or'
+            rnLeaf  ' '
+            rnInternalRef
+              rnInner
+                rnLeaf  'Lexical'
+                rnLeaf  ' '
+                rnLeaf  'analysis'
+              rnLeaf  'lexical-analysis'
+            rnLeaf  ' '
+            rnLeaf  'or'
+            rnLeaf  ' '
+            rnInternalRef
+              rnInner
+                rnLeaf  'lexical'
+                rnLeaf  ' '
+                rnLeaf  'analysis'
+              rnLeaf  'lexical-analysis'
+            rnLeaf  '.'
+            rnLeaf  ' '
+      """)
+
+  test "RST quoted literal blocks":
+    let expected =
+      dedent"""
+        rnInner
+          rnLeaf  'Paragraph'
+          rnLeaf  ':'
+          rnLiteralBlock
+            rnLeaf  '>x'
+        """
+
+    check(dedent"""
+        Paragraph::
+
+        >x""".toAst(rstOptions = preferRst) == expected)
+
+    check(dedent"""
+        Paragraph::
+
+            >x""".toAst(rstOptions = preferRst) == expected)
+
+  test "RST quoted literal blocks, :: at a separate line":
+    let expected =
+      dedent"""
+        rnInner
+          rnInner
+            rnLeaf  'Paragraph'
+          rnLiteralBlock
+            rnLeaf  '>x
+        >>y'
+      """
+
+    check(dedent"""
+        Paragraph
+
+        ::
+
+        >x
+        >>y""".toAst(rstOptions = preferRst) == expected)
+
+    check(dedent"""
+        Paragraph
+
+        ::
+
+          >x
+          >>y""".toAst(rstOptions = preferRst) == expected)
+
+  test "Markdown quoted blocks":
+    check(dedent"""
+        Paragraph.
+        >x""".toAst ==
+      dedent"""
+        rnInner
+          rnLeaf  'Paragraph'
+          rnLeaf  '.'
+          rnMarkdownBlockQuote
+            rnMarkdownBlockQuoteItem  quotationDepth=1
+              rnLeaf  'x'
+      """)
+
+    # bug #17987
+    check(dedent"""
+        foo https://github.com/nim-lang/Nim/issues/8258
+
+        > bar""".toAst ==
+      dedent"""
+        rnInner
+          rnInner
+            rnLeaf  'foo'
+            rnLeaf  ' '
+            rnStandaloneHyperlink
+              rnLeaf  'https://github.com/nim-lang/Nim/issues/8258'
+          rnMarkdownBlockQuote
+            rnMarkdownBlockQuoteItem  quotationDepth=1
+              rnLeaf  'bar'
+      """)
+
+    let expected = dedent"""
+        rnInner
+          rnLeaf  'Paragraph'
+          rnLeaf  '.'
+          rnMarkdownBlockQuote
+            rnMarkdownBlockQuoteItem  quotationDepth=1
+              rnInner
+                rnLeaf  'x1'
+                rnLeaf  ' '
+                rnLeaf  'x2'
+            rnMarkdownBlockQuoteItem  quotationDepth=2
+              rnInner
+                rnLeaf  'y1'
+                rnLeaf  ' '
+                rnLeaf  'y2'
+            rnMarkdownBlockQuoteItem  quotationDepth=1
+              rnLeaf  'z'
+        """
+
+    check(dedent"""
+        Paragraph.
+        >x1 x2
+        >>y1 y2
+        >z""".toAst == expected)
+
+    check(dedent"""
+        Paragraph.
+        > x1 x2
+        >> y1 y2
+        > z""".toAst == expected)
+
+    check(dedent"""
+        >x
+        >y
+        >z""".toAst ==
+      dedent"""
+        rnMarkdownBlockQuote
+          rnMarkdownBlockQuoteItem  quotationDepth=1
+            rnInner
+              rnLeaf  'x'
+              rnLeaf  ' '
+              rnLeaf  'y'
+              rnLeaf  ' '
+              rnLeaf  'z'
+      """)
+
+    check(dedent"""
+        > z
+        > > >y
+        """.toAst ==
+      dedent"""
+        rnMarkdownBlockQuote
+          rnMarkdownBlockQuoteItem  quotationDepth=1
+            rnLeaf  'z'
+          rnMarkdownBlockQuoteItem  quotationDepth=3
+            rnLeaf  'y'
+        """)
+
+  test "Markdown quoted blocks: lazy":
+    let expected = dedent"""
+        rnInner
+          rnMarkdownBlockQuote
+            rnMarkdownBlockQuoteItem  quotationDepth=2
+              rnInner
+                rnLeaf  'x'
+                rnLeaf  ' '
+                rnLeaf  'continuation1'
+                rnLeaf  ' '
+                rnLeaf  'continuation2'
+          rnParagraph
+            rnLeaf  'newParagraph'
+      """
+    check(dedent"""
+        >>x
+        continuation1
+        continuation2
+
+        newParagraph""".toAst == expected)
+
+    check(dedent"""
+        >> x
+        continuation1
+        continuation2
+
+        newParagraph""".toAst == expected)
+
+    # however mixing more than 1 non-lazy line and lazy one(s) splits quote
+    # in our parser, which appeared the easiest way to handle such cases:
+    var warnings = new seq[string]
+    check(dedent"""
+        >> x
+        >> continuation1
+        continuation2
+
+        newParagraph""".toAst(warnings=warnings) ==
+      dedent"""
+        rnInner
+          rnMarkdownBlockQuote
+            rnMarkdownBlockQuoteItem  quotationDepth=2
+              rnLeaf  'x'
+            rnMarkdownBlockQuoteItem  quotationDepth=2
+              rnInner
+                rnLeaf  'continuation1'
+                rnLeaf  ' '
+                rnLeaf  'continuation2'
+          rnParagraph
+            rnLeaf  'newParagraph'
+        """)
+    check(warnings[] == @[
+        "input(2, 1) Warning: RST style: two or more quoted lines " &
+        "are followed by unquoted line 3"])
+
+  test "Markdown quoted blocks: not lazy":
+    # here is where we deviate from CommonMark specification: 'bar' below is
+    # not considered as continuation of 2-level '>> foo' quote.
+    check(dedent"""
+        >>> foo
+        > bar
+        >> baz
+        """.toAst() ==
+      dedent"""
+        rnMarkdownBlockQuote
+          rnMarkdownBlockQuoteItem  quotationDepth=3
+            rnLeaf  'foo'
+          rnMarkdownBlockQuoteItem  quotationDepth=1
+            rnLeaf  'bar'
+          rnMarkdownBlockQuoteItem  quotationDepth=2
+            rnLeaf  'baz'
+        """)
+
+
+  test "Markdown quoted blocks: inline markup works":
+    check(dedent"""
+        > hi **bold** text
+        """.toAst == dedent"""
+          rnMarkdownBlockQuote
+            rnMarkdownBlockQuoteItem  quotationDepth=1
+              rnInner
+                rnLeaf  'hi'
+                rnLeaf  ' '
+                rnStrongEmphasis
+                  rnLeaf  'bold'
+                rnLeaf  ' '
+                rnLeaf  'text'
+        """)
+
+  test "Markdown quoted blocks: blank line separator":
+    let expected = dedent"""
+      rnInner
+        rnMarkdownBlockQuote
+          rnMarkdownBlockQuoteItem  quotationDepth=1
+            rnInner
+              rnLeaf  'x'
+              rnLeaf  ' '
+              rnLeaf  'y'
+        rnMarkdownBlockQuote
+          rnMarkdownBlockQuoteItem  quotationDepth=1
+            rnInner
+              rnLeaf  'z'
+              rnLeaf  ' '
+              rnLeaf  't'
+      """
+    check(dedent"""
+        >x
+        >y
+
+        > z
+        > t""".toAst == expected)
+
+    check(dedent"""
+        >x
+        y
+
+        > z
+         t""".toAst == expected)
+
+  test "Markdown quoted blocks: nested body blocks/elements work #1":
+    let expected = dedent"""
+      rnMarkdownBlockQuote
+        rnMarkdownBlockQuoteItem  quotationDepth=1
+          rnBulletList
+            rnBulletItem
+              rnInner
+                rnLeaf  'x'
+            rnBulletItem
+              rnInner
+                rnLeaf  'y'
+      """
+
+    check(dedent"""
+        > - x
+          - y
+        """.toAst == expected)
+
+    # TODO: if bug #17340 point 28 is resolved then this may work:
+    # check(dedent"""
+    #     > - x
+    #     - y
+    #     """.toAst == expected)
+
+    check(dedent"""
+        > - x
+        > - y
+        """.toAst == expected)
+
+    check(dedent"""
+        >
+        > - x
+        >
+        > - y
+        >
+        """.toAst == expected)
+
+  test "Markdown quoted blocks: nested body blocks/elements work #2":
+    let expected = dedent"""
+      rnAdmonition  adType=note
+        [nil]
+        [nil]
+        rnDefList
+          rnDefItem
+            rnDefName
+              rnLeaf  'deflist'
+              rnLeaf  ':'
+            rnDefBody
+              rnMarkdownBlockQuote
+                rnMarkdownBlockQuoteItem  quotationDepth=2
+                  rnInner
+                    rnLeaf  'quote'
+                    rnLeaf  ' '
+                    rnLeaf  'continuation'
+      """
+
+    check(dedent"""
+        .. Note:: deflist:
+                    >> quote
+                    continuation
+        """.toAst(rstOptions = preferRst) == expected)
+
+    check(dedent"""
+        .. Note::
+           deflist:
+             >> quote
+             continuation
+        """.toAst(rstOptions = preferRst) == expected)
+
+    check(dedent"""
+        .. Note::
+           deflist:
+             >> quote
+             >> continuation
+        """.toAst(rstOptions = preferRst) == expected)
+
+    # spaces are not significant between `>`:
+    check(dedent"""
+        .. Note::
+           deflist:
+             > > quote
+             > > continuation
+        """.toAst(rstOptions = preferRst) == expected)
+
+  test "Markdown quoted blocks: de-indent handled well":
+    check(dedent"""
+        >
+        >   - x
+        >   - y
+        >
+        > Paragraph.
+        """.toAst(rstOptions = preferRst) == dedent"""
+          rnMarkdownBlockQuote
+            rnMarkdownBlockQuoteItem  quotationDepth=1
+              rnInner
+                rnBlockQuote
+                  rnBulletList
+                    rnBulletItem
+                      rnInner
+                        rnLeaf  'x'
+                    rnBulletItem
+                      rnInner
+                        rnLeaf  'y'
+                rnParagraph
+                  rnLeaf  'Paragraph'
+                  rnLeaf  '.'
+          """)
+
+  let expectCodeBlock = dedent"""
+      rnCodeBlock
+        [nil]
+        rnFieldList
+          rnField
+            rnFieldName
+              rnLeaf  'default-language'
+            rnFieldBody
+              rnLeaf  'Nim'
+        rnLiteralBlock
+          rnLeaf  'let a = 1
+      ```'
+      """
+
+  test "Markdown footnotes":
+    # Testing also 1) correct order of manually-numbered and automatically-
+    # numbered footnotes; 2) no spaces between references (html & 3 below):
+
+    check(dedent"""
+        Paragraph [^1] [^html-hyphen][^3] and [^latex]
+
+        [^1]: footnote1
+
+        [^html-hyphen]: footnote2
+           continuation2
+
+        [^latex]: footnote4
+
+        [^3]: footnote3
+            continuation3
+        """.toAst ==
+      dedent"""
+        rnInner
+          rnInner
+            rnLeaf  'Paragraph'
+            rnLeaf  ' '
+            rnFootnoteRef
+              rnInner
+                rnLeaf  '1'
+              rnLeaf  'footnote-1'
+            rnLeaf  ' '
+            rnFootnoteRef
+              rnInner
+                rnLeaf  '2'
+              rnLeaf  'footnote-htmlminushyphen'
+            rnFootnoteRef
+              rnInner
+                rnLeaf  '3'
+              rnLeaf  'footnote-3'
+            rnLeaf  ' '
+            rnLeaf  'and'
+            rnLeaf  ' '
+            rnFootnoteRef
+              rnInner
+                rnLeaf  '4'
+              rnLeaf  'footnote-latex'
+          rnFootnoteGroup
+            rnFootnote  anchor='footnote-1'
+              rnInner
+                rnLeaf  '1'
+              rnLeaf  'footnote1'
+            rnFootnote  anchor='footnote-htmlminushyphen'
+              rnInner
+                rnLeaf  '2'
+              rnInner
+                rnLeaf  'footnote2'
+                rnLeaf  ' '
+                rnLeaf  'continuation2'
+            rnFootnote  anchor='footnote-latex'
+              rnInner
+                rnLeaf  '4'
+              rnLeaf  'footnote4'
+            rnFootnote  anchor='footnote-3'
+              rnInner
+                rnLeaf  '3'
+              rnInner
+                rnLeaf  'footnote3'
+                rnLeaf  ' '
+                rnLeaf  'continuation3'
+      """)
+
+  test "Markdown code blocks with more > 3 backticks":
+    check(dedent"""
+        ````
+        let a = 1
+        ```
+        ````""".toAst == expectCodeBlock)
+
+  test "Markdown code blocks with ~~~":
+    check(dedent"""
+        ~~~
+        let a = 1
+        ```
+        ~~~""".toAst == expectCodeBlock)
+    check(dedent"""
+        ~~~~~
+        let a = 1
+        ```
+        ~~~~~""".toAst == expectCodeBlock)
+
+  test "Markdown code blocks with Nim-specific arguments":
+    check(dedent"""
+        ```nim number-lines=1 test
+        let a = 1
+        ```""".toAst ==
+      dedent"""
+        rnCodeBlock
+          rnDirArg
+            rnLeaf  'nim'
+          rnFieldList
+            rnField
+              rnFieldName
+                rnLeaf  'number-lines'
+              rnFieldBody
+                rnLeaf  '1'
+            rnField
+              rnFieldName
+                rnLeaf  'test'
+              rnFieldBody
+          rnLiteralBlock
+            rnLeaf  'let a = 1'
+        """)
+
+    check(dedent"""
+        ```nim test = "nim c $1"  number-lines = 1
+        let a = 1
+        ```""".toAst ==
+      dedent"""
+        rnCodeBlock
+          rnDirArg
+            rnLeaf  'nim'
+          rnFieldList
+            rnField
+              rnFieldName
+                rnLeaf  'test'
+              rnFieldBody
+                rnLeaf  '"nim c $1"'
+            rnField
+              rnFieldName
+                rnLeaf  'number-lines'
+              rnFieldBody
+                rnLeaf  '1'
+          rnLiteralBlock
+            rnLeaf  'let a = 1'
+        """)
+
+  test "additional indentation < 4 spaces is handled fine":
+    check(dedent"""
+        Indentation
+
+          ```nim
+            let a = 1
+          ```""".toAst ==
+      dedent"""
+        rnInner
+          rnParagraph
+            rnLeaf  'Indentation'
+          rnParagraph
+            rnCodeBlock
+              rnDirArg
+                rnLeaf  'nim'
+              [nil]
+              rnLiteralBlock
+                rnLeaf  '  let a = 1'
+      """)
+      # | |
+      # |  \ indentation of exactly two spaces before 'let a = 1'
+
+  test "no blank line is required before or after Markdown code block":
+    let inputBacktick = dedent"""
+        Some text
+        ```
+        CodeBlock()
+        ```
+        Other text"""
+    let inputTilde = dedent"""
+        Some text
+        ~~~~~~~~~
+        CodeBlock()
+        ~~~~~~~~~
+        Other text"""
+    let expected = dedent"""
+        rnInner
+          rnParagraph
+            rnLeaf  'Some'
+            rnLeaf  ' '
+            rnLeaf  'text'
+          rnParagraph
+            rnCodeBlock
+              [nil]
+              rnFieldList
+                rnField
+                  rnFieldName
+                    rnLeaf  'default-language'
+                  rnFieldBody
+                    rnLeaf  'Nim'
+              rnLiteralBlock
+                rnLeaf  'CodeBlock()'
+            rnLeaf  ' '
+            rnLeaf  'Other'
+            rnLeaf  ' '
+            rnLeaf  'text'
+      """
+    check inputBacktick.toAst == expected
+    check inputTilde.toAst == expected
+
+  test "option list has priority over definition list":
+    for opt in [preferMarkdown, preferRst]:
+      check(dedent"""
+          --defusages
+                        file
+          -o            set
+          """.toAst(rstOptions = opt) ==
+        dedent"""
+          rnOptionList
+            rnOptionListItem  order=1
+              rnOptionGroup
+                rnLeaf  '--'
+                rnLeaf  'defusages'
+              rnDescription
+                rnInner
+                  rnLeaf  'file'
+            rnOptionListItem  order=2
+              rnOptionGroup
+                rnLeaf  '-'
+                rnLeaf  'o'
+              rnDescription
+                rnLeaf  'set'
+          """)
+
+  test "items of 1 option list can be separated by blank lines":
+    check(dedent"""
+        -a  desc1
+
+        -b  desc2
+        """.toAst ==
+      dedent"""
+        rnOptionList
+          rnOptionListItem  order=1
+            rnOptionGroup
+              rnLeaf  '-'
+              rnLeaf  'a'
+            rnDescription
+              rnLeaf  'desc1'
+          rnOptionListItem  order=2
+            rnOptionGroup
+              rnLeaf  '-'
+              rnLeaf  'b'
+            rnDescription
+              rnLeaf  'desc2'
+      """)
+
+  test "definition list does not gobble up the following blocks":
+    check(dedent"""
+        defName
+            defBody
+
+        -b  desc2
+        """.toAst(rstOptions = preferRst) ==
+      dedent"""
+        rnInner
+          rnDefList
+            rnDefItem
+              rnDefName
+                rnLeaf  'defName'
+              rnDefBody
+                rnInner
+                  rnLeaf  'defBody'
+          rnOptionList
+            rnOptionListItem  order=1
+              rnOptionGroup
+                rnLeaf  '-'
+                rnLeaf  'b'
+              rnDescription
+                rnLeaf  'desc2'
+      """)
+
+  test "definition lists work correctly with additional indentation in Markdown":
+    check(dedent"""
+        Paragraph:
+          -c  desc1
+          -b  desc2
+        """.toAst() ==
+      dedent"""
+        rnInner
+          rnInner
+            rnLeaf  'Paragraph'
+            rnLeaf  ':'
+          rnOptionList
+            rnOptionListItem  order=1
+              rnOptionGroup
+                rnLeaf  '-'
+                rnLeaf  'c'
+              rnDescription
+                rnLeaf  'desc1'
+            rnOptionListItem  order=2
+              rnOptionGroup
+                rnLeaf  '-'
+                rnLeaf  'b'
+              rnDescription
+                rnLeaf  'desc2'
+      """)
+
+  test "RST comment":
+    check(dedent"""
+        .. comment1
+         comment2
+        someParagraph""".toAst ==
+      dedent"""
+        rnLeaf  'someParagraph'
+        """)
+
+    check(dedent"""
+        ..
+         comment1
+         comment2
+        someParagraph""".toAst ==
+      dedent"""
+        rnLeaf  'someParagraph'
+        """)
+
+  test "check that additional line right after .. ends comment":
+    check(dedent"""
+        ..
+
+         notAcomment1
+         notAcomment2
+        someParagraph""".toAst(rstOptions = preferRst) ==
+      dedent"""
+        rnInner
+          rnBlockQuote
+            rnInner
+              rnLeaf  'notAcomment1'
+              rnLeaf  ' '
+              rnLeaf  'notAcomment2'
+          rnParagraph
+            rnLeaf  'someParagraph'
+        """)
+
+  test "check that additional line right after .. ends comment (Markdown mode)":
+    # in Markdown small indentation does not matter so this should
+    # just be split to 2 paragraphs.
+    check(dedent"""
+        ..
+
+         notAcomment1
+         notAcomment2
+        someParagraph""".toAst ==
+      dedent"""
+        rnInner
+          rnInner
+            rnLeaf  'notAcomment1'
+            rnLeaf  ' '
+            rnLeaf  'notAcomment2'
+          rnParagraph
+            rnLeaf  'someParagraph'
+        """)
+
+  test "but blank lines after 2nd non-empty line don't end the comment":
+    check(dedent"""
+        ..
+           comment1
+
+
+         comment2
+        someParagraph""".toAst ==
+      dedent"""
+        rnLeaf  'someParagraph'
+        """)
+
+  test "using .. as separator b/w directives and block quotes":
+    check(dedent"""
+        .. note:: someNote
+
+        ..
+
+          someBlockQuote""".toAst(rstOptions = preferRst) ==
+      dedent"""
+        rnInner
+          rnAdmonition  adType=note
+            [nil]
+            [nil]
+            rnLeaf  'someNote'
+          rnBlockQuote
+            rnInner
+              rnLeaf  'someBlockQuote'
+        """)
+
+  test "no redundant blank lines in literal blocks":
+    check(dedent"""
+      Check::
+
+
+        code
+
+      """.toAst(rstOptions = preferRst) ==
+      dedent"""
+        rnInner
+          rnLeaf  'Check'
+          rnLeaf  ':'
+          rnLiteralBlock
+            rnLeaf  'code'
+      """)
+
+  test "Markdown indented code blocks":
+    check(dedent"""
+      See
+
+          some code""".toAst ==
+      dedent"""
+        rnInner
+          rnInner
+            rnLeaf  'See'
+          rnLiteralBlock
+            rnLeaf  'some code'
+      """)
+
+    # not a code block -- no blank line before:
+    check(dedent"""
+      See
+          some code""".toAst ==
+      dedent"""
+        rnInner
+          rnLeaf  'See'
+          rnLeaf  ' '
+          rnLeaf  'some'
+          rnLeaf  ' '
+          rnLeaf  'code'
+      """)
+
+suite "RST tables":
+
+  test "formatting in tables works":
+    check(
+      dedent"""
+        =========  ===
+        `build`    `a`
+        =========  ===
+        """.toAst ==
+      dedent"""
+        rnTable  colCount=2
+          rnTableRow
+            rnTableDataCell
+              rnInlineCode
+                rnDirArg
+                  rnLeaf  'nim'
+                [nil]
+                rnLiteralBlock
+                  rnLeaf  'build'
+            rnTableDataCell
+              rnInlineCode
+                rnDirArg
+                  rnLeaf  'nim'
+                [nil]
+                rnLiteralBlock
+                  rnLeaf  'a'
+      """)
+
+  test "tables with slightly overflowed cells cause an error (1)":
+    var error = new string
+    check(
+      dedent"""
+        ======   ======
+         Inputs  Output
+        ======   ======
+        """.toAst(rstOptions = pureRst, error = error) == "")
+    check(error[] == "input(2, 2) Error: Illformed table: " &
+                     "this word crosses table column from the right")
+
+    # In nimforum compatibility mode & Markdown we raise a warning instead:
+    let expected = dedent"""
+      rnTable  colCount=2
+        rnTableRow
+          rnTableDataCell
+            rnLeaf  'Inputs'
+          rnTableDataCell
+            rnLeaf  'Output'
+      """
+    for opt in [preferRst, preferMarkdown]:
+      var warnings = new seq[string]
+
+      check(
+        dedent"""
+          ======   ======
+           Inputs  Output
+          ======   ======
+          """.toAst(rstOptions = opt, warnings = warnings) == expected)
+      check(warnings[] == @[
+        "input(2, 2) Warning: RST style: this word crosses table column from the right"])
+
+  test "tables with slightly overflowed cells cause an error (2)":
+    var error = new string
+    check("" == dedent"""
+      =====  =====  ======
+      Input  Output
+      =====  =====  ======
+      False  False  False
+      =====  =====  ======
+      """.toAst(rstOptions = pureRst, error = error))
+    check(error[] == "input(2, 8) Error: Illformed table: " &
+                     "this word crosses table column from the right")
+
+  test "tables with slightly underflowed cells cause an error":
+    var error = new string
+    check("" == dedent"""
+      =====  =====  ======
+      Input Output
+      =====  =====  ======
+      False  False  False
+      =====  =====  ======
+      """.toAst(rstOptions = pureRst, error = error))
+    check(error[] == "input(2, 7) Error: Illformed table: " &
+                     "this word crosses table column from the left")
+
+  test "tables with unequal underlines should be reported (1)":
+    var error = new string
+    error[] = "none"
+    check("" == dedent"""
+      =====  ======
+      Input  Output
+      =====  ======
+      False  False
+      =====  =======
+      """.toAst(rstOptions = pureRst, error = error))
+    check(error[] == "input(5, 14) Error: Illformed table: " &
+                     "end of table column #2 should end at position 13")
+
+  test "tables with unequal underlines should be reported (2)":
+    var error = new string
+    check("" == dedent"""
+      =====  ======
+      Input  Output
+      =====  =======
+      False  False
+      =====  ======
+      """.toAst(rstOptions = pureRst, error = error))
+    check(error[] == "input(3, 14) Error: Illformed table: " &
+                     "end of table column #2 should end at position 13")
+
+  test "tables with empty first cells":
+    check(
+      dedent"""
+          = = =
+          x y z
+              t
+          = = =
+          """.toAst ==
+      dedent"""
+        rnTable  colCount=3
+          rnTableRow
+            rnTableDataCell
+              rnLeaf  'x'
+            rnTableDataCell
+              rnInner
+                rnLeaf  'y'
+                rnLeaf  ' '
+            rnTableDataCell
+              rnInner
+                rnLeaf  'z'
+                rnLeaf  ' '
+                rnLeaf  't'
+        """)
+
+  test "tables with spanning cells & separators":
+    check(
+      dedent"""
+        =====  =====  ======
+           Inputs     Output
+        ------------  ------
+          A      B    A or B
+        =====  =====  ======
+        False  False  False
+        True   False  True
+        -----  -----  ------
+        False  True   True
+        True   True   True
+        =====  =====  ======
+        """.toAst ==
+      dedent"""
+        rnTable  colCount=3
+          rnTableRow
+            rnTableHeaderCell  span=2
+              rnLeaf  'Inputs'
+            rnTableHeaderCell  span=1
+              rnLeaf  'Output'
+          rnTableRow  endsHeader
+            rnTableHeaderCell
+              rnLeaf  'A'
+            rnTableHeaderCell
+              rnLeaf  'B'
+            rnTableHeaderCell
+              rnInner
+                rnLeaf  'A'
+                rnLeaf  ' '
+                rnLeaf  'or'
+                rnLeaf  ' '
+                rnLeaf  'B'
+          rnTableRow
+            rnTableDataCell
+              rnLeaf  'False'
+            rnTableDataCell
+              rnLeaf  'False'
+            rnTableDataCell
+              rnLeaf  'False'
+          rnTableRow
+            rnTableDataCell  span=1
+              rnLeaf  'True'
+            rnTableDataCell  span=1
+              rnLeaf  'False'
+            rnTableDataCell  span=1
+              rnLeaf  'True'
+          rnTableRow
+            rnTableDataCell
+              rnLeaf  'False'
+            rnTableDataCell
+              rnLeaf  'True'
+            rnTableDataCell
+              rnLeaf  'True'
+          rnTableRow
+            rnTableDataCell
+              rnLeaf  'True'
+            rnTableDataCell
+              rnLeaf  'True'
+            rnTableDataCell
+              rnLeaf  'True'
+      """)
+
+  test "tables with spanning cells with uneqal underlines cause an error":
+    var error = new string
+    check(
+      dedent"""
+        =====  =====  ======
+           Inputs     Output
+        ------------- ------
+          A      B    A or B
+        =====  =====  ======
+        """.toAst(error=error) == "")
+    check(error[] == "input(3, 1) Error: Illformed table: " &
+                     "spanning underline does not match main table columns")
+
+  let expTable = dedent"""
+      rnTable  colCount=2
+        rnTableRow
+          rnTableDataCell
+            rnLeaf  'Inputs'
+          rnTableDataCell
+            rnLeaf  'Output'
+      """
+
+  test "only tables with `=` columns specs are allowed (1)":
+    var warnings = new seq[string]
+    check(
+      dedent"""
+        ------  ------
+        Inputs  Output
+        ------  ------
+        """.toAst(warnings=warnings) ==
+      expTable)
+    check(warnings[] ==
+          @["input(1, 1) Warning: RST style: " &
+              "only tables with `=` columns specification are allowed",
+            "input(3, 1) Warning: RST style: " &
+              "only tables with `=` columns specification are allowed"])
+
+  test "only tables with `=` columns specs are allowed (2)":
+    var warnings = new seq[string]
+    check(
+      dedent"""
+        ======  ======
+        Inputs  Output
+        ~~~~~~  ~~~~~~
+        """.toAst(warnings=warnings) ==
+      expTable)
+    check(warnings[] ==
+          @["input(3, 1) Warning: RST style: "&
+              "only tables with `=` columns specification are allowed"])
+
+
+suite "RST indentation":
+  test "nested bullet lists":
+    let input = dedent """
+      * - bullet1
+        - bullet2
+      * - bullet3
+        - bullet4
+      """
+    let output = input.toAst
+    check(output == dedent"""
+      rnBulletList
+        rnBulletItem
+          rnBulletList
+            rnBulletItem
+              rnInner
+                rnLeaf  'bullet1'
+            rnBulletItem
+              rnInner
+                rnLeaf  'bullet2'
+        rnBulletItem
+          rnBulletList
+            rnBulletItem
+              rnInner
+                rnLeaf  'bullet3'
+            rnBulletItem
+              rnInner
+                rnLeaf  'bullet4'
+      """)
+
+  test "nested markup blocks":
+    let input = dedent"""
+      #) .. Hint:: .. Error:: none
+      #) .. Warning:: term0
+                        Definition0
+      #) some
+         paragraph1
+      #) term1
+           Definition1
+         term2
+           Definition2
+    """
+    check(input.toAst(rstOptions = preferRst) == dedent"""
+      rnEnumList  labelFmt=1)
+        rnEnumItem
+          rnAdmonition  adType=hint
+            [nil]
+            [nil]
+            rnAdmonition  adType=error
+              [nil]
+              [nil]
+              rnLeaf  'none'
+        rnEnumItem
+          rnAdmonition  adType=warning
+            [nil]
+            [nil]
+            rnDefList
+              rnDefItem
+                rnDefName
+                  rnLeaf  'term0'
+                rnDefBody
+                  rnInner
+                    rnLeaf  'Definition0'
+        rnEnumItem
+          rnInner
+            rnLeaf  'some'
+            rnLeaf  ' '
+            rnLeaf  'paragraph1'
+        rnEnumItem
+          rnDefList
+            rnDefItem
+              rnDefName
+                rnLeaf  'term1'
+              rnDefBody
+                rnInner
+                  rnLeaf  'Definition1'
+            rnDefItem
+              rnDefName
+                rnLeaf  'term2'
+              rnDefBody
+                rnInner
+                  rnLeaf  'Definition2'
+      """)
+
+  test "code-block parsing":
+    let input1 = dedent"""
+      .. code-block:: nim
+          :test: "nim c $1"
+
+        template additive(typ: typedesc) =
+          discard
+      """
+    let input2 = dedent"""
+      .. code-block:: nim
+        :test: "nim c $1"
+
+        template additive(typ: typedesc) =
+          discard
+      """
+    let input3 = dedent"""
+      .. code-block:: nim
+         :test: "nim c $1"
+         template additive(typ: typedesc) =
+           discard
+      """
+    let inputWrong = dedent"""
+      .. code-block:: nim
+       :test: "nim c $1"
+
+         template additive(typ: typedesc) =
+           discard
+      """
+    let ast = dedent"""
+      rnCodeBlock
+        rnDirArg
+          rnLeaf  'nim'
+        rnFieldList
+          rnField
+            rnFieldName
+              rnLeaf  'test'
+            rnFieldBody
+              rnInner
+                rnLeaf  '"'
+                rnLeaf  'nim'
+                rnLeaf  ' '
+                rnLeaf  'c'
+                rnLeaf  ' '
+                rnLeaf  '$'
+                rnLeaf  '1'
+                rnLeaf  '"'
+          rnField
+            rnFieldName
+              rnLeaf  'default-language'
+            rnFieldBody
+              rnLeaf  'Nim'
+        rnLiteralBlock
+          rnLeaf  'template additive(typ: typedesc) =
+        discard'
+      """
+    check input1.toAst == ast
+    check input2.toAst == ast
+    check input3.toAst == ast
+    # "template..." should be parsed as a definition list attached to ":test:":
+    check inputWrong.toAst != ast
+
+  test "Markdown definition lists work in conjunction with bullet lists":
+    check(dedent"""
+        * some term
+          : the definition
+
+        Paragraph.""".toAst ==
+      dedent"""
+        rnInner
+          rnBulletList
+            rnBulletItem
+              rnMdDefList
+                rnDefItem
+                  rnDefName
+                    rnLeaf  'some'
+                    rnLeaf  ' '
+                    rnLeaf  'term'
+                  rnDefBody
+                    rnInner
+                      rnLeaf  'the'
+                      rnLeaf  ' '
+                      rnLeaf  'definition'
+          rnParagraph
+            rnLeaf  'Paragraph'
+            rnLeaf  '.'
+      """)
+
+  test "Markdown definition lists work with blank lines and extra paragraphs":
+    check(dedent"""
+        Term1
+
+        :   Definition1
+
+        Term2 *inline markup*
+
+        :   Definition2
+
+            Paragraph2
+
+        Term3
+        : * point1
+          * point2
+        : term3definition2
+      """.toAst == dedent"""
+        rnMdDefList
+          rnDefItem
+            rnDefName
+              rnLeaf  'Term1'
+            rnDefBody
+              rnInner
+                rnLeaf  'Definition1'
+          rnDefItem
+            rnDefName
+              rnLeaf  'Term2'
+              rnLeaf  ' '
+              rnEmphasis
+                rnLeaf  'inline'
+                rnLeaf  ' '
+                rnLeaf  'markup'
+            rnDefBody
+              rnParagraph
+                rnLeaf  'Definition2'
+              rnParagraph
+                rnLeaf  'Paragraph2'
+          rnDefItem
+            rnDefName
+              rnLeaf  'Term3'
+            rnDefBody
+              rnBulletList
+                rnBulletItem
+                  rnInner
+                    rnLeaf  'point1'
+                rnBulletItem
+                  rnInner
+                    rnLeaf  'point2'
+            rnDefBody
+              rnInner
+                rnLeaf  'term3definition2'
+      """)
+
+suite "Markdown indentation":
+  test "Markdown paragraph indentation":
+    # Additional spaces (<=3) of indentation does not break the paragraph.
+    # TODO: in 2nd case de-indentation causes paragraph to break, this is
+    # reasonable but does not seem to conform the Markdown spec.
+    check(dedent"""
+      Start1
+        stop1
+
+        Start2
+      stop2
+      """.toAst ==
+      dedent"""
+        rnInner
+          rnParagraph
+            rnLeaf  'Start1'
+            rnLeaf  ' '
+            rnLeaf  'stop1'
+          rnParagraph
+            rnLeaf  'Start2'
+          rnParagraph
+            rnLeaf  'stop2'
+            rnLeaf  ' '
+      """)
+
+suite "Warnings":
+  test "warnings for broken footnotes/links/substitutions":
+    let input = dedent"""
+      firstParagraph
+
+      footnoteRef [som]_
+
+      link `a broken Link`_
+
+      substitution |undefined subst|
+
+      link short.link_
+
+      lastParagraph
+      """
+    var warnings = new seq[string]
+    let output = input.toAst(rstOptions=preferRst, warnings=warnings)
+    check(warnings[] == @[
+        "input(3, 14) Warning: broken link 'citation-som'",
+        "input(5, 7) Warning: broken link 'a broken Link'",
+        "input(7, 15) Warning: unknown substitution 'undefined subst'",
+        "input(9, 6) Warning: broken link 'short.link'"
+        ])
+
+  test "Pandoc Markdown concise link warning points to target":
+    var warnings = new seq[string]
+    check(
+      "ref [here][target]".toAst(warnings=warnings) ==
+      dedent"""
+        rnInner
+          rnLeaf  'ref'
+          rnLeaf  ' '
+          rnPandocRef
+            rnInner
+              rnLeaf  'here'
+            rnInner
+              rnLeaf  'target'
+      """)
+    check warnings[] == @["input(1, 12) Warning: broken link 'target'"]
+
+  test "With include directive and blank lines at the beginning":
+    "other.rst".writeFile(dedent"""
+
+
+        firstParagraph
+
+        here brokenLink_""")
+    let input = ".. include:: other.rst"
+    var warnings = new seq[string]
+    let output = input.toAst(warnings=warnings)
+    check warnings[] == @["other.rst(5, 6) Warning: broken link 'brokenLink'"]
+    check(output == dedent"""
+      rnInner
+        rnParagraph
+          rnLeaf  'firstParagraph'
+        rnParagraph
+          rnLeaf  'here'
+          rnLeaf  ' '
+          rnRstRef
+            rnLeaf  'brokenLink'
+      """)
+    removeFile("other.rst")
+
+  test "warnings for ambiguous links (references + anchors)":
+    # Reference like `x`_ generates a link alias x that may clash with others
+    let input = dedent"""
+      Manual reference: `foo <#foo,string,string>`_
+
+      .. _foo:
+
+      Paragraph.
+
+      Ref foo_
+      """
+    var warnings = new seq[string]
+    let output = input.toAst(warnings=warnings)
+    check(warnings[] == @[
+      dedent """
+      input(7, 5) Warning: ambiguous doc link `foo`
+        clash:
+          (3, 8): (manual directive anchor)
+          (1, 45): (implicitly-generated hyperlink alias)"""
+    ])
+    # reference should be resolved to the manually set anchor:
+    check(output ==
+      dedent"""
+        rnInner
+          rnParagraph
+            rnLeaf  'Manual'
+            rnLeaf  ' '
+            rnLeaf  'reference'
+            rnLeaf  ':'
+            rnLeaf  ' '
+            rnHyperlink
+              rnInner
+                rnLeaf  'foo'
+              rnInner
+                rnLeaf  '#foo,string,string'
+          rnParagraph  anchor='foo'
+            rnLeaf  'Paragraph'
+            rnLeaf  '.'
+          rnParagraph
+            rnLeaf  'Ref'
+            rnLeaf  ' '
+            rnInternalRef
+              rnInner
+                rnLeaf  'foo'
+              rnLeaf  'foo'
+            rnLeaf  ' '
+      """)
+
+suite "RST include directive":
+  test "Include whole":
+    "other.rst".writeFile("**test1**")
+    let input = ".. include:: other.rst"
+    doAssert "<strong>test1</strong>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig())
+    removeFile("other.rst")
+
+  test "Include starting from":
+    "other.rst".writeFile("""
+And this should **NOT** be visible in `docs.html`
+OtherStart
+*Visible*
+""")
+
+    let input = """
+.. include:: other.rst
+             :start-after: OtherStart
+"""
+    check "<em>Visible</em>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig())
+    removeFile("other.rst")
+
+  test "Include everything before":
+    "other.rst".writeFile("""
+*Visible*
+OtherEnd
+And this should **NOT** be visible in `docs.html`
+""")
+
+    let input = """
+.. include:: other.rst
+             :end-before: OtherEnd
+"""
+    doAssert "<em>Visible</em>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig())
+    removeFile("other.rst")
+
+
+  test "Include everything between":
+    "other.rst".writeFile("""
+And this should **NOT** be visible in `docs.html`
+OtherStart
+*Visible*
+OtherEnd
+And this should **NOT** be visible in `docs.html`
+""")
+
+    let input = """
+.. include:: other.rst
+             :start-after: OtherStart
+             :end-before: OtherEnd
+"""
+    check "<em>Visible</em>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig())
+    removeFile("other.rst")
+
+
+  test "Ignore premature ending string":
+    "other.rst".writeFile("""
+
+OtherEnd
+And this should **NOT** be visible in `docs.html`
+OtherStart
+*Visible*
+OtherEnd
+And this should **NOT** be visible in `docs.html`
+""")
+
+    let input = """
+.. include:: other.rst
+             :start-after: OtherStart
+             :end-before: OtherEnd
+"""
+    doAssert "<em>Visible</em>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig())
+    removeFile("other.rst")
+
+suite "RST escaping":
+  test "backspaces":
+    check("""\ this""".toAst == dedent"""
+      rnLeaf  'this'
+      """)
+
+    check("""\\ this""".toAst == dedent"""
+      rnInner
+        rnLeaf  '\'
+        rnLeaf  ' '
+        rnLeaf  'this'
+      """)
+
+    check("""\\\ this""".toAst == dedent"""
+      rnInner
+        rnLeaf  '\'
+        rnLeaf  'this'
+      """)
+
+    check("""\\\\ this""".toAst == dedent"""
+      rnInner
+        rnLeaf  '\'
+        rnLeaf  '\'
+        rnLeaf  ' '
+        rnLeaf  'this'
+      """)
+
+suite "RST inline markup":
+  test "* and ** surrounded by spaces are not inline markup":
+    check("a * b * c ** d ** e".toAst == dedent"""
+      rnInner
+        rnLeaf  'a'
+        rnLeaf  ' '
+        rnLeaf  '*'
+        rnLeaf  ' '
+        rnLeaf  'b'
+        rnLeaf  ' '
+        rnLeaf  '*'
+        rnLeaf  ' '
+        rnLeaf  'c'
+        rnLeaf  ' '
+        rnLeaf  '**'
+        rnLeaf  ' '
+        rnLeaf  'd'
+        rnLeaf  ' '
+        rnLeaf  '**'
+        rnLeaf  ' '
+        rnLeaf  'e'
+      """)
+
+  test "end-string has repeating symbols":
+    check("*emphasis content****".toAst == dedent"""
+      rnEmphasis
+        rnLeaf  'emphasis'
+        rnLeaf  ' '
+        rnLeaf  'content'
+        rnLeaf  '***'
+      """)
+
+    check("""*emphasis content\****""".toAst == dedent"""
+      rnEmphasis
+        rnLeaf  'emphasis'
+        rnLeaf  ' '
+        rnLeaf  'content'
+        rnLeaf  '*'
+        rnLeaf  '**'
+      """)  # exact configuration of leafs with * is not really essential,
+            # only total number of * is essential
+
+    check("**strong content****".toAst == dedent"""
+      rnStrongEmphasis
+        rnLeaf  'strong'
+        rnLeaf  ' '
+        rnLeaf  'content'
+        rnLeaf  '**'
+      """)
+
+    check("""**strong content*\****""".toAst == dedent"""
+      rnStrongEmphasis
+        rnLeaf  'strong'
+        rnLeaf  ' '
+        rnLeaf  'content'
+        rnLeaf  '*'
+        rnLeaf  '*'
+        rnLeaf  '*'
+      """)
+
+    check("``lit content`````".toAst == dedent"""
+      rnInlineLiteral
+        rnLeaf  'lit'
+        rnLeaf  ' '
+        rnLeaf  'content'
+        rnLeaf  '```'
+      """)
+
+  test "interpreted text parsing: code fragments":
+    check(dedent"""
+        .. default-role:: option
+
+        `--gc:refc`""".toAst ==
+      dedent"""
+        rnInner
+          rnDefaultRole
+            rnDirArg
+              rnLeaf  'option'
+            [nil]
+            [nil]
+          rnParagraph
+            rnCodeFragment
+              rnInner
+                rnLeaf  '--'
+                rnLeaf  'gc'
+                rnLeaf  ':'
+                rnLeaf  'refc'
+              rnLeaf  'option'
+        """)
+
+  test """interpreted text can be ended with \` """:
+    let output = (".. default-role:: literal\n" & """`\``""").toAst
+    check(output.endsWith """
+  rnParagraph
+    rnInlineLiteral
+      rnLeaf  '`'""" & "\n")
+
+    let output2 = """`\``""".toAst
+    check(output2 == dedent"""
+      rnInlineCode
+        rnDirArg
+          rnLeaf  'nim'
+        [nil]
+        rnLiteralBlock
+          rnLeaf  '`'
+      """)
+
+    let output3 = """`proc \`+\``""".toAst
+    check(output3 == dedent"""
+      rnInlineCode
+        rnDirArg
+          rnLeaf  'nim'
+        [nil]
+        rnLiteralBlock
+          rnLeaf  'proc `+`'
+      """)
+
+    check("""`\\`""".toAst ==
+      dedent"""
+        rnInlineCode
+          rnDirArg
+            rnLeaf  'nim'
+          [nil]
+          rnLiteralBlock
+            rnLeaf  '\\'
+        """)
+
+  test "Markdown-style code/backtick":
+    # no whitespace is required before `
+    check("`try`...`except`".toAst ==
+      dedent"""
+        rnInner
+          rnInlineCode
+            rnDirArg
+              rnLeaf  'nim'
+            [nil]
+            rnLiteralBlock
+              rnLeaf  'try'
+          rnLeaf  '...'
+          rnInlineCode
+            rnDirArg
+              rnLeaf  'nim'
+            [nil]
+            rnLiteralBlock
+              rnLeaf  'except'
+        """)
+
+
+  test """inline literals can contain \ anywhere""":
+    check("""``\``""".toAst == dedent"""
+      rnInlineLiteral
+        rnLeaf  '\'
+      """)
+
+    check("""``\\``""".toAst == dedent"""
+      rnInlineLiteral
+        rnLeaf  '\'
+        rnLeaf  '\'
+      """)
+
+    check("""``\```""".toAst == dedent"""
+      rnInlineLiteral
+        rnLeaf  '\'
+        rnLeaf  '`'
+      """)
+
+    check("""``\\```""".toAst == dedent"""
+      rnInlineLiteral
+        rnLeaf  '\'
+        rnLeaf  '\'
+        rnLeaf  '`'
+      """)
+
+    check("""``\````""".toAst == dedent"""
+      rnInlineLiteral
+        rnLeaf  '\'
+        rnLeaf  '`'
+        rnLeaf  '`'
+      """)
+
+  test "references with _ at the end":
+    check(dedent"""
+      .. _lnk: https
+
+      lnk_""".toAst ==
+      dedent"""
+        rnHyperlink
+          rnInner
+            rnLeaf  'lnk'
+          rnInner
+            rnLeaf  'https'
+      """)
+
+  test "not a hyper link":
+    check(dedent"""
+      .. _lnk: https
+
+      lnk___""".toAst ==
+      dedent"""
+        rnInner
+          rnLeaf  'lnk'
+          rnLeaf  '___'
+      """)
+
+  test "no punctuation in the end of a standalone URI is allowed":
+    check(dedent"""
+        [see (http://no.org)], end""".toAst(rstOptions = preferRst) ==
+      dedent"""
+        rnInner
+          rnLeaf  '['
+          rnLeaf  'see'
+          rnLeaf  ' '
+          rnLeaf  '('
+          rnStandaloneHyperlink
+            rnLeaf  'http://no.org'
+          rnLeaf  ')'
+          rnLeaf  ']'
+          rnLeaf  ','
+          rnLeaf  ' '
+          rnLeaf  'end'
+        """)
+
+    # but `/` at the end is OK
+    check(
+      dedent"""
+        See http://no.org/ end""".toAst ==
+      dedent"""
+        rnInner
+          rnLeaf  'See'
+          rnLeaf  ' '
+          rnStandaloneHyperlink
+            rnLeaf  'http://no.org/'
+          rnLeaf  ' '
+          rnLeaf  'end'
+        """)
+
+    # a more complex URL with some made-up ending '&='.
+    # Github Markdown would include final &= and
+    # so would rst2html.py in contradiction with RST spec.
+    check(
+      dedent"""
+        See https://www.google.com/url?sa=t&source=web&cd=&cad=rja&url=https%3A%2F%2Fnim-lang.github.io%2FNim%2Frst.html%23features&usg=AO&= end""".toAst ==
+      dedent"""
+        rnInner
+          rnLeaf  'See'
+          rnLeaf  ' '
+          rnStandaloneHyperlink
+            rnLeaf  'https://www.google.com/url?sa=t&source=web&cd=&cad=rja&url=https%3A%2F%2Fnim-lang.github.io%2FNim%2Frst.html%23features&usg=AO'
+          rnLeaf  '&'
+          rnLeaf  '='
+          rnLeaf  ' '
+          rnLeaf  'end'
+        """)
+
+  test "Markdown-style link can be split to a few lines":
+    check(dedent"""
+        is [term-rewriting
+        macros](manual.html#term-rewriting-macros)""".toAst ==
+      dedent"""
+        rnInner
+          rnLeaf  'is'
+          rnLeaf  ' '
+          rnHyperlink
+            rnLeaf  'term-rewriting macros'
+            rnLeaf  'manual.html#term-rewriting-macros'
+      """)
+
+  test "URL with balanced parentheses (Markdown rule)":
+    # 2 balanced parens, 1 unbalanced:
+    check(dedent"""
+        https://en.wikipedia.org/wiki/APL_((programming_language)))""".toAst ==
+      dedent"""
+        rnInner
+          rnStandaloneHyperlink
+            rnLeaf  'https://en.wikipedia.org/wiki/APL_((programming_language))'
+          rnLeaf  ')'
+      """)
+
+    # the same for Markdown-style link:
+    check(dedent"""
+        [foo [bar]](https://en.wikipedia.org/wiki/APL_((programming_language))))""".toAst ==
+      dedent"""
+        rnInner
+          rnHyperlink
+            rnLeaf  'foo [bar]'
+            rnLeaf  'https://en.wikipedia.org/wiki/APL_((programming_language))'
+          rnLeaf  ')'
+      """)
+
+    # unbalanced (here behavior is more RST-like actually):
+    check(dedent"""
+        https://en.wikipedia.org/wiki/APL_(programming_language(""".toAst ==
+      dedent"""
+        rnInner
+          rnStandaloneHyperlink
+            rnLeaf  'https://en.wikipedia.org/wiki/APL_(programming_language'
+          rnLeaf  '('
+      """)
+
+    # unbalanced [, but still acceptable:
+    check(dedent"""
+        [my {link example](http://example.com/bracket_(symbol_[))""".toAst ==
+      dedent"""
+        rnHyperlink
+          rnLeaf  'my {link example'
+          rnLeaf  'http://example.com/bracket_(symbol_[)'
+      """)
+
+  test "not a Markdown link":
+    # bug #17340 (27) `f` will be considered as a protocol and blocked as unsafe
+    var warnings = new seq[string]
+    check("[T](f: var Foo)".toAst(warnings = warnings) ==
+      dedent"""
+        rnInner
+          rnLeaf  '['
+          rnLeaf  'T'
+          rnLeaf  ']'
+          rnLeaf  '('
+          rnLeaf  'f'
+          rnLeaf  ':'
+          rnLeaf  ' '
+          rnLeaf  'var'
+          rnLeaf  ' '
+          rnLeaf  'Foo'
+          rnLeaf  ')'
+      """)
+    check(warnings[] == @["input(1, 5) Warning: broken link 'f'"])
+
+suite "Misc isssues":
+  test "Markdown CodeblockFields in one line (lacking enclosing ```)":
+    let message = """
+    ```llvm-profdata merge first.profraw second.profraw third.profraw <more stuff maybe> -output data.profdata```"""
+
+    try:
+      echo rstgen.rstToHtml(message, {roSupportMarkdown}, nil)
+    except EParseError:
+      discard
diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim
index c702ccc2a..6253e7146 100644
--- a/tests/stdlib/trstgen.nim
+++ b/tests/stdlib/trstgen.nim
@@ -1,7 +1,59 @@
+discard """
+matrix: "--mm:refc; --mm:orc"
+outputsub: ""
+"""
+
 # tests for rstgen module.
 
 import ../../lib/packages/docutils/rstgen
-import unittest
+import ../../lib/packages/docutils/rst
+import unittest, strutils, strtabs
+import std/private/miscdollars
+import std/assertions
+
+const
+  NoSandboxOpts = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled}
+  preferMarkdown = {roPreferMarkdown, roSupportMarkdown, roNimFile}
+  preferRst = {roSupportMarkdown, roNimFile}
+
+proc toHtml(input: string,
+            rstOptions: RstParseOptions = preferMarkdown,
+            error: ref string = nil,
+            warnings: ref seq[string] = nil): string =
+  ## If `error` is nil then no errors should be generated.
+  ## The same goes for `warnings`.
+  proc testMsgHandler(filename: string, line, col: int, msgkind: MsgKind,
+                      arg: string) =
+    let mc = msgkind.whichMsgClass
+    let a = $msgkind % arg
+    var message: string
+    toLocation(message, filename, line, col + ColRstOffset)
+    message.add " $1: $2" % [$mc, a]
+    if mc == mcError:
+      if error == nil:
+        raise newException(EParseError, "[unexpected error] " & message)
+      error[] = message
+      # we check only first error because subsequent ones may be meaningless
+      raise newException(EParseError, "")
+    else:
+      doAssert warnings != nil, "unexpected RST warning '" & message & "'"
+      warnings[].add message
+  try:
+    result = rstToHtml(input, rstOptions, defaultConfig(),
+                       msgHandler=testMsgHandler)
+  except EParseError as e:
+    if e.msg != "":
+      result = e.msg
+
+# inline code tags (for parsing originated from highlite.nim)
+proc id(str: string): string = """<span class="Identifier">"""  & str & "</span>"
+proc op(str: string): string = """<span class="Operator">"""    & str & "</span>"
+proc pu(str: string): string = """<span class="Punctuation">""" & str & "</span>"
+proc optionListLabel(opt: string): string =
+  """<div class="option-list-label"><tt><span class="option">""" &
+  opt &
+  "</span></tt></div>"
+
 
 suite "YAML syntax highlighting":
   test "Basics":
@@ -16,8 +68,8 @@ suite "YAML syntax highlighting":
     ? key
     : value
     ..."""
-    let output = rstTohtml(input, {}, defaultConfig())
-    assert output == """<pre class = "listing"><span class="Directive">%YAML 1.2</span>
+    let output = input.toHtml({})
+    doAssert output == """<pre class = "listing"><span class="Directive">%YAML 1.2</span>
 <span class="Keyword">---</span>
 <span class="StringLit">a string</span><span class="Punctuation">:</span> <span class="StringLit">string</span>
 <span class="StringLit">a list</span><span class="Punctuation">:</span>
@@ -27,7 +79,7 @@ suite "YAML syntax highlighting":
 <span class="Punctuation">?</span> <span class="StringLit">key</span>
 <span class="Punctuation">:</span> <span class="StringLit">value</span>
 <span class="Keyword">...</span></pre>"""
-  
+
   test "Block scalars":
     let input = """.. code-block:: yaml
     a literal block scalar: |
@@ -42,8 +94,8 @@ suite "YAML syntax highlighting":
     another literal block scalar:
       |+ # comment after header
      allowed, since more indented than parent"""
-    let output = rstToHtml(input, {}, defaultConfig())
-    assert output == """<pre class = "listing"><span class="StringLit">a literal block scalar</span><span class="Punctuation">:</span> <span class="Command">|</span><span class="Command"></span><span class="LongStringLit">
+    let output = input.toHtml({})
+    doAssert output == """<pre class = "listing"><span class="StringLit">a literal block scalar</span><span class="Punctuation">:</span> <span class="Command">|</span><span class="Command"></span><span class="LongStringLit">
   some text
   # not a comment
  </span><span class="Comment"># a comment, since less indented</span>
@@ -55,7 +107,7 @@ suite "YAML syntax highlighting":
 <span class="StringLit">another literal block scalar</span><span class="Punctuation">:</span>
   <span class="Command">|+</span> <span class="Comment"># comment after header</span><span class="LongStringLit">
  allowed, since more indented than parent</span></pre>"""
- 
+
   test "Directives":
     let input = """.. code-block:: yaml
     %YAML 1.2
@@ -68,8 +120,8 @@ suite "YAML syntax highlighting":
     % not a directive
     ...
     %TAG ! !foo:"""
-    let output = rstToHtml(input, {}, defaultConfig())
-    assert output == """<pre class = "listing"><span class="Directive">%YAML 1.2</span>
+    let output = input.toHtml({})
+    doAssert output == """<pre class = "listing"><span class="Directive">%YAML 1.2</span>
 <span class="Keyword">---</span>
 <span class="StringLit">%not a directive</span>
 <span class="Keyword">...</span>
@@ -89,15 +141,34 @@ suite "YAML syntax highlighting":
       more numbers: [-783, 11e78],
       not numbers: [ 42e, 0023, +32.37, 8 ball]
     }"""
-    let output = rstToHtml(input, {}, defaultConfig())
-    assert output == """<pre class = "listing"><span class="Punctuation">{</span>
+    let output = input.toHtml({})
+    doAssert output == """<pre class = "listing"><span class="Punctuation">{</span>
   <span class="StringLit">&quot;</span><span class="StringLit">quoted string&quot;</span><span class="Punctuation">:</span> <span class="DecNumber">42</span><span class="Punctuation">,</span>
   <span class="StringLit">'single quoted string'</span><span class="Punctuation">:</span> <span class="StringLit">false</span><span class="Punctuation">,</span>
   <span class="Punctuation">[</span> <span class="StringLit">list</span><span class="Punctuation">,</span> <span class="StringLit">&quot;</span><span class="StringLit">with&quot;</span><span class="Punctuation">,</span> <span class="StringLit">'entries'</span> <span class="Punctuation">]</span><span class="Punctuation">:</span> <span class="FloatNumber">73.32e-73</span><span class="Punctuation">,</span>
   <span class="StringLit">more numbers</span><span class="Punctuation">:</span> <span class="Punctuation">[</span><span class="DecNumber">-783</span><span class="Punctuation">,</span> <span class="FloatNumber">11e78</span><span class="Punctuation">]</span><span class="Punctuation">,</span>
   <span class="StringLit">not numbers</span><span class="Punctuation">:</span> <span class="Punctuation">[</span> <span class="StringLit">42e</span><span class="Punctuation">,</span> <span class="StringLit">0023</span><span class="Punctuation">,</span> <span class="StringLit">+32.37</span><span class="Punctuation">,</span> <span class="StringLit">8 ball</span><span class="Punctuation">]</span>
 <span class="Punctuation">}</span></pre>"""
-  
+
+  test "Directives: warnings":
+    let input = dedent"""
+      .. non-existent-warning: Paragraph.
+
+      .. another.wrong:warning::: Paragraph.
+      """
+    var warnings = new seq[string]
+    let output = input.toHtml(warnings=warnings)
+    check output == ""
+    doAssert warnings[].len == 2
+    check "(1, 24) Warning: RST style:" in warnings[0]
+    check "double colon :: may be missing at end of 'non-existent-warning'" in warnings[0]
+    check "(3, 25) Warning: RST style:" in warnings[1]
+    check "RST style: too many colons for a directive (should be ::)" in warnings[1]
+
+  test "not a directive":
+    let input = "..warning:: I am not a warning."
+    check input.toHtml == input
+
   test "Anchors, Aliases, Tags":
     let input = """.. code-block:: yaml
     --- !!map
@@ -106,8 +177,8 @@ suite "YAML syntax highlighting":
     : !localtag foo
     alias: *anchor
     """
-    let output = rstToHtml(input, {}, defaultConfig())
-    assert output == """<pre class = "listing"><span class="Keyword">---</span> <span class="TagStart">!!map</span>
+    let output = input.toHtml({})
+    doAssert output == """<pre class = "listing"><span class="Keyword">---</span> <span class="TagStart">!!map</span>
 <span class="TagStart">!!str</span> <span class="StringLit">string</span><span class="Punctuation">:</span> <span class="TagStart">!&lt;tag:yaml.org,2002:int&gt;</span> <span class="DecNumber">42</span>
 <span class="Punctuation">?</span> <span class="Label">&amp;anchor</span> <span class="TagStart">!!seq</span> <span class="Punctuation">[</span><span class="Punctuation">]</span><span class="Punctuation">:</span>
 <span class="Punctuation">:</span> <span class="TagStart">!localtag</span> <span class="StringLit">foo</span>
@@ -126,8 +197,8 @@ suite "YAML syntax highlighting":
     example.com/not/a#comment:
       ?not a map key
     """
-    let output = rstToHtml(input, {}, defaultConfig())
-    assert output == """<pre class = "listing"><span class="Keyword">...</span>
+    let output = input.toHtml({})
+    doAssert output == """<pre class = "listing"><span class="Keyword">...</span>
  <span class="StringLit">%a string</span><span class="Punctuation">:</span>
   <span class="StringLit">a:string:not:a:map</span>
 <span class="Keyword">...</span>
@@ -136,4 +207,1486 @@ suite "YAML syntax highlighting":
   <span class="DecNumber">-3</span>
   <span class="DecNumber">-4</span>
 <span class="StringLit">example.com/not/a#comment</span><span class="Punctuation">:</span>
-  <span class="StringLit">?not a map key</span></pre>"""
\ No newline at end of file
+  <span class="StringLit">?not a map key</span></pre>"""
+
+
+suite "RST/Markdown general":
+  test "RST emphasis":
+    doAssert rstToHtml("*Hello* **world**!", {},
+      newStringTable(modeStyleInsensitive)) ==
+      "<em>Hello</em> <strong>world</strong>!"
+
+  test "Markdown links":
+    check("(( [Nim](https://nim-lang.org/) ))".toHtml ==
+        """(( <a class="reference external" href="https://nim-lang.org/">Nim</a> ))""")
+    check("(([Nim](https://nim-lang.org/)))".toHtml ==
+        """((<a class="reference external" href="https://nim-lang.org/">Nim</a>))""")
+    check("[[Nim](https://nim-lang.org/)]".toHtml ==
+        """[<a class="reference external" href="https://nim-lang.org/">Nim</a>]""")
+
+  test "Markdown tables":
+    let input1 = """
+| A1 header    | A2 \| not fooled
+| :---         | ----:       |
+| C1           | C2 **bold** | ignored |
+| D1 `code \|` | D2          | also ignored
+| E1 \| text   |
+|              | F2 without pipe
+not in table"""
+    let output1 = input1.toHtml
+    #[
+    TODO: `\|` inside a table cell should render as `|`
+        `|` outside a table cell should render as `\|`
+    consistently with markdown, see https://stackoverflow.com/a/66557930/1426932
+    ]#
+    check(output1 == """
+<table border="1" class="docutils"><tr><th>A1 header</th><th>A2 | not fooled</th></tr>
+<tr><td>C1</td><td>C2 <strong>bold</strong></td></tr>
+<tr><td>D1 <tt class="docutils literal"><span class="pre">""" & id"code" & " " & op"\|" & """</span></tt></td><td>D2</td></tr>
+<tr><td>E1 | text</td><td></td></tr>
+<tr><td></td><td>F2 without pipe</td></tr>
+</table><p>not in table</p>""")
+    let input2 = """
+| A1 header | A2 |
+| --- | --- |"""
+    let output2 = input2.toHtml
+    doAssert output2 == """<table border="1" class="docutils"><tr><th>A1 header</th><th>A2</th></tr>
+</table>"""
+
+  test "RST tables":
+    let input1 = """
+Test 2 column/4 rows table:
+====   ===
+H0     H1
+====   ===
+A0     A1
+====   ===
+A2     A3
+====   ===
+A4     A5
+====   === """
+    let output1 = rstToLatex(input1, {})
+    doAssert "{LL}" in output1  # 2 columns
+    doAssert count(output1, "\\\\") == 4  # 4 rows
+    for cell in ["H0", "H1", "A0", "A1", "A2", "A3", "A4", "A5"]:
+      doAssert cell in output1
+
+    let input2 = """
+Now test 3 columns / 2 rows, and also borders containing 4 =, 3 =, 1 = signs:
+
+====   ===  =
+H0     H1   H
+====   ===  =
+A0     A1   X
+       Ax   Y
+====   ===  = """
+    let output2 = rstToLatex(input2, {})
+    doAssert "{LLL}" in output2  # 3 columns
+    doAssert count(output2, "\\\\") == 2  # 2 rows
+    for cell in ["H0", "H1", "H", "A0", "A1", "X", "Ax", "Y"]:
+      doAssert cell in output2
+
+
+  test "RST adornments":
+    let input1 = """
+Check that a few punctuation symbols are not parsed as adornments:
+:word1: word2 .... word3 """
+    let output1 = input1.toHtml
+    discard output1
+
+  test "RST sections":
+    let input1 = """
+Long chapter name
+'''''''''''''''''''
+"""
+    let output1 = input1.toHtml
+    doAssert "Long chapter name" in output1 and "<h1" in output1
+
+    let input2 = """
+Short chapter name:
+
+ChA
+===
+"""
+    let output2 = input2.toHtml
+    doAssert "ChA" in output2 and "<h1" in output2
+
+    let input3 = """
+Very short chapter name:
+
+X
+~
+"""
+    let output3 = input3.toHtml
+    doAssert "X" in output3 and "<h1" in output3
+
+    let input4 = """
+Check that short underline is not enough to make section:
+
+Wrong chapter
+------------
+
+"""
+    var error4 = new string
+    let output4 = input4.toHtml(error = error4)
+    check(error4[] == "input(3, 1) Error: new section expected (underline " &
+            "\'------------\' is too short)")
+
+    let input5 = """
+Check that punctuation after adornment and indent are not detected as adornment.
+
+Some chapter
+--------------
+
+  "punctuation symbols" """
+    let output5 = input5.toHtml
+    doAssert "&quot;punctuation symbols&quot;" in output5 and "<h1" in output5
+
+    # check that EOF after adornment does not prevent it parsing as heading
+    let input6 = dedent """
+      Some chapter
+      ------------"""
+    let output6 = input6.toHtml
+    doAssert "<h1 id=\"some-chapter\">Some chapter</h1>" in output6
+
+    # check that overline and underline match
+    let input7 = dedent """
+      ------------
+      Some chapter
+      -----------
+      """
+    var error7 = new string
+    let output7 = input7.toHtml(error=error7)
+    check(error7[] == "input(1, 1) Error: new section expected (underline " &
+            "\'-----------\' does not match overline \'------------\')")
+
+    let input8 = dedent """
+      -----------
+          Overflow
+      -----------
+      """
+    var error8 = new string
+    let output8 = input8.toHtml(error=error8)
+    check(error8[] == "input(1, 1) Error: new section expected (overline " &
+            "\'-----------\' is too short)")
+
+    # check that hierarchy of title styles works
+    let input9good = dedent """
+      Level1
+      ======
+
+      Level2
+      ------
+
+      Level3
+      ~~~~~~
+
+      L1
+      ==
+
+      Another2
+      --------
+
+      More3
+      ~~~~~
+
+      """
+    let output9good = input9good.toHtml(preferRst)
+    doAssert "<h1 id=\"level1\">Level1</h1>" in output9good
+    doAssert "<h2 id=\"level2\">Level2</h2>" in output9good
+    doAssert "<h3 id=\"level3\">Level3</h3>" in output9good
+    doAssert "<h1 id=\"l1\">L1</h1>" in output9good
+    doAssert "<h2 id=\"another2\">Another2</h2>" in output9good
+    doAssert "<h3 id=\"more3\">More3</h3>" in output9good
+
+    # check that swap causes an exception
+    let input9Bad = dedent """
+      Level1
+      ======
+
+      Level2
+      ------
+
+      Level3
+      ~~~~~~
+
+      L1
+      ==
+
+      More
+      ~~~~
+
+      Another
+      -------
+
+      """
+    var error9Bad = new string
+    let output9Bad = input9Bad.toHtml(preferRst, error=error9Bad)
+    check(error9Bad[] == "input(15, 1) Error: new section expected (section " &
+            "level inconsistent: underline ~~~~~ unexpectedly found, while " &
+            "the following intermediate section level(s) are missing on " &
+            "lines 12..15: underline -----)")
+
+  test "RST sections overline":
+    # the same as input9good but with overline headings
+    # first overline heading has a special meaning: document title
+    let input = dedent """
+      ======
+      Title0
+      ======
+
+      +++++++++
+      SubTitle0
+      +++++++++
+
+      ------
+      Level1
+      ------
+
+      Level2
+      ------
+
+      ~~~~~~
+      Level3
+      ~~~~~~
+
+      --
+      L1
+      --
+
+      Another2
+      --------
+
+      ~~~~~
+      More3
+      ~~~~~
+
+      """
+    var rstGenera: RstGenerator
+    var output: string
+    let (rst, files, _) = rstParse(input, "", 1, 1, {})
+    rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", filenames = files)
+    rstGenera.renderRstToOut(rst, output)
+    doAssert rstGenera.meta[metaTitle] == "Title0"
+    doAssert rstGenera.meta[metaSubtitle] == "SubTitle0"
+    doAssert "<h1 id=\"level1\"><center>Level1</center></h1>" in output
+    doAssert "<h2 id=\"level2\">Level2</h2>" in output
+    doAssert "<h3 id=\"level3\"><center>Level3</center></h3>" in output
+    doAssert "<h1 id=\"l1\"><center>L1</center></h1>" in output
+    doAssert "<h2 id=\"another2\">Another2</h2>" in output
+    doAssert "<h3 id=\"more3\"><center>More3</center></h3>" in output
+
+  test "RST sections overline 2":
+    # check that a paragraph prevents interpreting overlines as document titles
+    let input = dedent """
+      Paragraph
+
+      ======
+      Title0
+      ======
+
+      +++++++++
+      SubTitle0
+      +++++++++
+      """
+    var rstGenera: RstGenerator
+    var output: string
+    let (rst, files, _) = rstParse(input, "", 1, 1, {})
+    rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", filenames=files)
+    rstGenera.renderRstToOut(rst, output)
+    doAssert rstGenera.meta[metaTitle] == ""
+    doAssert rstGenera.meta[metaSubtitle] == ""
+    doAssert "<h1 id=\"title0\"><center>Title0</center></h1>" in output
+    doAssert "<h2 id=\"subtitle0\"><center>SubTitle0</center></h2>" in output
+
+  test "RST+Markdown sections":
+    # check that RST and Markdown headings don't interfere
+    let input = dedent """
+      ======
+      Title0
+      ======
+
+      MySection1a
+      +++++++++++
+
+      # MySection1b
+
+      MySection1c
+      +++++++++++
+
+      ##### MySection5a
+
+      MySection2a
+      -----------
+      """
+    var rstGenera: RstGenerator
+    var output: string
+    let (rst, files, _) = rstParse(input, "", 1, 1, {roSupportMarkdown})
+    rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", filenames=files)
+    rstGenera.renderRstToOut(rst, output)
+    doAssert rstGenera.meta[metaTitle] == "Title0"
+    doAssert rstGenera.meta[metaSubtitle] == ""
+    doAssert output ==
+             "\n<h1 id=\"mysection1a\">MySection1a</h1>" & # RST
+             "\n<h1 id=\"mysection1b\">MySection1b</h1>" & # Markdown
+             "\n<h1 id=\"mysection1c\">MySection1c</h1>" & # RST
+             "\n<h5 id=\"mysection5a\">MySection5a</h5>" & # Markdown
+             "\n<h2 id=\"mysection2a\">MySection2a</h2>"   # RST
+
+  test "RST inline text":
+    let input1 = "GC_step"
+    let output1 = input1.toHtml
+    doAssert output1 == "GC_step"
+
+  test "RST anchors/links to headings":
+    # Currently in TOC mode anchors are modified (for making links from
+    # the TOC unique)
+    let inputNoToc = dedent"""
+        Type relations
+        ==============
+
+        Convertible relation
+        --------------------
+
+        Ref. `Convertible relation`_
+        """
+    let outputNoToc = inputNoToc.toHtml
+    check outputNoToc.count("id=\"type-relations\"") == 1
+    check outputNoToc.count("id=\"convertible-relation\"") == 1
+    check outputNoToc.count("href=\"#convertible-relation\"") == 1
+
+    let inputTocCases = @[
+      dedent"""
+        .. contents::
+
+        Type relations
+        ==============
+
+        Convertible relation
+        --------------------
+
+        Ref. `Convertible relation`_
+
+        Guards and locks
+        ================
+        """,
+      dedent"""
+        Ref. `Convertible relation`_
+
+        .. contents::
+
+        Type relations
+        ==============
+
+        Convertible relation
+        --------------------
+
+        Guards and locks
+        ================
+        """
+    ]
+    for inputToc in inputTocCases:
+      let outputToc = inputToc.toHtml
+      check outputToc.count("id=\"type-relations\"") == 1
+      check outputToc.count("id=\"type-relations-convertible-relation\"") == 1
+      check outputToc.count("id=\"convertible-relation\">") == 0
+      # Besides "Ref.", heading also contains link to itself:
+      check outputToc.count(
+          "href=\"#type-relations-convertible-relation\">") == 2
+      check outputToc.count("href=\"#convertible-relation\"") == 0
+
+  test "RST links":
+    let input1 = """
+Want to learn about `my favorite programming language`_?
+
+.. _my favorite programming language: https://nim-lang.org"""
+    let output1 = input1.toHtml
+    doAssert "<a" in output1 and "href=\"https://nim-lang.org\"" in output1
+
+  test "RST transitions":
+    let input1 = """
+context1
+
+~~~~
+
+context2
+"""
+    let output1 = input1.toHtml(preferRst)
+    doAssert "<hr" in output1
+
+    let input2 = """
+This is too short to be a transition:
+
+---
+context2
+---
+"""
+    var error2 = new string
+    let output2 = input2.toHtml(error=error2)
+    check(error2[] == "input(3, 1) Error: new section expected (overline " &
+            "\'---\' is too short)")
+
+  test "RST literal block":
+    let input1 = """
+Test literal block
+
+::
+
+  check """
+    let output1 = input1.toHtml(preferRst)
+    doAssert "<pre>" in output1
+
+  test "Markdown code block":
+    let input1 = """
+```
+let x = 1
+``` """
+    let output1 = input1.toHtml({roSupportMarkdown, roPreferMarkdown})
+    doAssert "<pre" in output1 and "class=\"Keyword\"" notin output1
+
+    # Check Nim highlighting by default in .nim files:
+    let output1nim = input1.toHtml({roSupportMarkdown, roPreferMarkdown,
+                                    roNimFile})
+    doAssert "<pre" in output1nim and "class=\"Keyword\"" in output1nim
+
+    let input2 = """
+Parse the block with language specifier:
+```Nim
+let x = 1
+``` """
+    let output2 = input2.toHtml
+    doAssert "<pre" in output2 and "class=\"Keyword\"" in output2
+
+  test "interpreted text":
+    check("""`foo.bar`""".toHtml ==
+      """<tt class="docutils literal"><span class="pre">""" &
+      id"foo" & op"." & id"bar" & "</span></tt>")
+    check("""`foo\`\`bar`""".toHtml ==
+      """<tt class="docutils literal"><span class="pre">""" &
+      id"foo" & pu"`" & pu"`" & id"bar" & "</span></tt>")
+    check("""`foo\`bar`""".toHtml ==
+      """<tt class="docutils literal"><span class="pre">""" &
+      id"foo" & pu"`" & id"bar" & "</span></tt>")
+    check("""`\`bar`""".toHtml ==
+      """<tt class="docutils literal"><span class="pre">""" &
+      pu"`" & id"bar" & "</span></tt>")
+    check("""`a\b\x\\ar`""".toHtml ==
+      """<tt class="docutils literal"><span class="pre">""" &
+      id"a" & op"""\""" & id"b" & op"""\""" & id"x" & op"""\\""" & id"ar" &
+      "</span></tt>")
+
+  test "inline literal":
+    check """``foo.bar``""".toHtml == """<tt class="docutils literal"><span class="pre">foo.bar</span></tt>"""
+    check """``foo\bar``""".toHtml == """<tt class="docutils literal"><span class="pre">foo\bar</span></tt>"""
+    check """``f\`o\\o\b`ar``""".toHtml == """<tt class="docutils literal"><span class="pre">f\`o\\o\b`ar</span></tt>"""
+
+  test "default-role":
+    # nim(default) -> literal -> nim -> code(=literal)
+    let input = dedent"""
+      Par1 `value1`.
+
+      .. default-role:: literal
+
+      Par2 `value2`.
+
+      .. default-role:: nim
+
+      Par3 `value3`.
+
+      .. default-role:: code
+
+      Par4 `value4`."""
+    let p1 = """Par1 <tt class="docutils literal"><span class="pre">""" & id"value1" & "</span></tt>."
+    let p2 = """<p>Par2 <tt class="docutils literal"><span class="pre">value2</span></tt>.</p>"""
+    let p3 = """<p>Par3 <tt class="docutils literal"><span class="pre">""" & id"value3" & "</span></tt>.</p>"
+    let p4 = """<p>Par4 <tt class="docutils literal"><span class="pre">value4</span></tt>.</p>"""
+    let expected = p1 & p2 & "\n" & p3 & "\n" & p4
+    check(
+      input.toHtml(NoSandboxOpts) == expected
+    )
+
+  test "role directive":
+    let input = dedent"""
+      .. role:: y(code)
+         :language: yaml
+
+      .. role:: brainhelp(code)
+         :language: brainhelp
+    """
+    var warnings = new seq[string]
+    let output = input.toHtml(
+      NoSandboxOpts,
+      warnings=warnings
+    )
+    check(warnings[].len == 1 and "language 'brainhelp' not supported" in warnings[0])
+
+  test "RST comments":
+    let input1 = """
+
+Check that comment disappears:
+
+..
+  some comment """
+    let output1 = input1.toHtml
+    doAssert output1 == "Check that comment disappears:"
+
+  test "RST line blocks + headings":
+    let input = """
+=====
+Test1
+=====
+
+|
+|
+| line block
+| other line
+
+"""
+    var rstGenera: RstGenerator
+    var output: string
+    let (rst, files, _) = rstParse(input, "", 1, 1, {})
+    rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", filenames=files)
+    rstGenera.renderRstToOut(rst, output)
+    doAssert rstGenera.meta[metaTitle] == "Test1"
+      # check that title was not overwritten to '|'
+    doAssert output == "<p><br/><br/>line block<br/>other line<br/></p>"
+    let output1l = rstToLatex(input, {})
+    doAssert "line block\n\n" in output1l
+    doAssert "other line\n\n" in output1l
+    doAssert output1l.count("\\vspace") == 2 + 2  # +2 surrounding paddings
+
+  test "RST line blocks":
+    let input2 = dedent"""
+      Paragraph1
+
+      |
+
+      Paragraph2"""
+
+    let output2 = input2.toHtml
+    doAssert "Paragraph1<p><br/></p> <p>Paragraph2</p>" == output2
+
+    let input3 = dedent"""
+      | xxx
+      |   yyy
+      |     zzz"""
+
+    let output3 = input3.toHtml
+    doAssert "xxx<br/>" in output3
+    doAssert "<span style=\"margin-left: 1.0em\">yyy</span><br/>" in output3
+    doAssert "<span style=\"margin-left: 2.0em\">zzz</span><br/>" in output3
+
+    # check that '|   ' with a few spaces is still parsed as new line
+    let input4 = dedent"""
+      | xxx
+      |
+      |     zzz"""
+
+    let output4 = input4.toHtml
+    doAssert "xxx<br/><br/>" in output4
+    doAssert "<span style=\"margin-left: 2.0em\">zzz</span><br/>" in output4
+
+  test "RST enumerated lists":
+    let input1 = dedent """
+      1. line1
+         1
+      2. line2
+         2
+
+      3. line3
+         3
+
+
+      4. line4
+         4
+
+
+
+      5. line5
+         5
+      """
+    let output1 = input1.toHtml
+    for i in 1..5:
+      doAssert ($i & ". line" & $i) notin output1
+      doAssert ("<li>line" & $i & " " & $i & "</li>") in output1
+
+    let input2 = dedent """
+      3. line3
+
+      4. line4
+
+
+      5. line5
+
+
+
+      7. line7
+
+
+
+
+      8. line8
+      """
+    let output2 = input2.toHtml
+    for i in [3, 4, 5, 7, 8]:
+      doAssert ($i & ". line" & $i) notin output2
+      doAssert ("<li>line" & $i & "</li>") in output2
+
+    # check that nested enumerated lists work
+    let input3 = dedent """
+      1.  a) string1
+      2. string2
+      """
+    let output3 = input3.toHtml
+    doAssert count(output3, "<ol ") == 2
+    doAssert count(output3, "</ol>") == 2
+    doAssert "<li>string1</li>" in output3 and "<li>string2</li>" in output3
+
+    let input4 = dedent """
+      Check that enumeration specifiers are respected
+
+      9. string1
+      10. string2
+      12. string3
+
+      b) string4
+      c) string5
+      e) string6
+      """
+    let output4 = input4.toHtml
+    doAssert count(output4, "<ol ") == 4
+    doAssert count(output4, "</ol>") == 4
+    for enumerator in [9, 12]:
+      doAssert "start=\"$1\"" % [$enumerator] in output4
+    for enumerator in [2, 5]:  # 2=b, 5=e
+      doAssert "start=\"$1\"" % [$enumerator] in output4
+
+    let input5 = dedent """
+      Check that auto-numbered enumeration lists work.
+
+      #. string1
+
+      #. string2
+
+      #. string3
+
+      #) string5
+      #) string6
+      """
+    let output5 = input5.toHtml
+    doAssert count(output5, "<ol ") == 2
+    doAssert count(output5, "</ol>") == 2
+    doAssert count(output5, "<li>") == 5
+
+    let input5a = dedent """
+      Auto-numbered RST list can start with 1 even when Markdown support is on.
+
+      1. string1
+      #. string2
+      #. string3
+      """
+    let output5a = input5a.toHtml
+    doAssert count(output5a, "<ol ") == 1
+    doAssert count(output5a, "</ol>") == 1
+    doAssert count(output5a, "<li>") == 3
+
+    let input6 = dedent """
+      ... And for alphabetic enumerators too!
+
+      b. string1
+      #. string2
+      #. string3
+      """
+    let output6 = input6.toHtml
+    doAssert count(output6, "<ol ") == 1
+    doAssert count(output6, "</ol>") == 1
+    doAssert count(output6, "<li>") == 3
+    doAssert "start=\"2\"" in output6 and "class=\"loweralpha simple\"" in output6
+
+    let input7 = dedent """
+      ... And for uppercase alphabetic enumerators.
+
+      C. string1
+      #. string2
+      #. string3
+      """
+    let output7 = input7.toHtml
+    doAssert count(output7, "<ol ") == 1
+    doAssert count(output7, "</ol>") == 1
+    doAssert count(output7, "<li>") == 3
+    doAssert "start=\"3\"" in output7 and "class=\"upperalpha simple\"" in output7
+
+    # check that it's not recognized as enum.list without indentation on 2nd line
+    let input8 = dedent """
+      Paragraph.
+
+      A. stringA
+      B. stringB
+      C. string1
+      string2
+      """
+    var warnings8 = new seq[string]
+    let output8 = input8.toHtml(warnings = warnings8)
+    check(warnings8[].len == 1)
+    check("input(6, 1) Warning: RST style: \n" &
+          "not enough indentation on line 6" in warnings8[0])
+    doAssert output8 == "Paragraph.<ol class=\"upperalpha simple\">" &
+        "<li>stringA</li>\n<li>stringB</li>\n</ol>\n<p>C. string1 string2 </p>"
+
+  test "Markdown enumerated lists":
+    let input1 = dedent """
+      Below are 2 enumerated lists: Markdown-style (5 items) and RST (1 item)
+      1. line1
+      1. line2
+      1. line3
+      1. line4
+
+      1. line5
+
+      #. lineA
+      """
+    let output1 = input1.toHtml
+    for i in 1..5:
+      doAssert ($i & ". line" & $i) notin output1
+      doAssert ("<li>line" & $i & "</li>") in output1
+    doAssert count(output1, "<ol ") == 2
+    doAssert count(output1, "</ol>") == 2
+
+  test "RST bullet lists":
+    let input1 = dedent """
+      * line1
+        1
+      * line2
+        2
+
+      * line3
+        3
+
+
+      * line4
+        4
+
+
+
+      * line5
+        5
+      """
+    let output1 = input1.toHtml
+    for i in 1..5:
+      doAssert ("<li>line" & $i & " " & $i & "</li>") in output1
+    doAssert count(output1, "<ul ") == 1
+    doAssert count(output1, "</ul>") == 1
+
+  test "Nim RST footnotes and citations":
+    # check that auto-label footnote enumerated properly after a manual one
+    let input1 = dedent """
+      .. [1] Body1.
+      .. [#note] Body2
+
+      Ref. [#note]_
+      """
+    let output1 = input1.toHtml(preferRst)
+    doAssert output1.count(">[1]</a>") == 1
+    doAssert output1.count(">[2]</a>") == 2
+    doAssert "href=\"#footnote-note\"" in output1
+    doAssert ">[-1]" notin output1
+    doAssert "Body1." in output1
+    doAssert "Body2" in output1
+
+    # check that there are NO footnotes/citations, only comments:
+    let input2 = dedent """
+      .. [1 #] Body1.
+      .. [# note] Body2.
+      .. [wrong citation] That gives you a comment.
+
+      .. [not&allowed] That gives you a comment.
+
+      Not references[#note]_[1 #]_ [wrong citation]_ and [not&allowed]_.
+      """
+    let output2 = input2.toHtml(preferRst)
+    doAssert output2 == "Not references[#note]_[1 #]_ [wrong citation]_ and [not&amp;allowed]_."
+
+    # check that auto-symbol footnotes work:
+    let input3 = dedent """
+      Ref. [*]_ and [*]_ and [*]_.
+
+      .. [*] Body1
+      .. [*] Body2.
+
+
+      .. [*] Body3.
+      .. [*] Body4
+
+      And [*]_.
+      """
+    let output3 = input3.toHtml(preferRst)
+    # both references and footnotes. Footnotes have link to themselves.
+    doAssert output3.count("href=\"#footnotesym-1\">[*]</a>") == 2
+    doAssert output3.count("href=\"#footnotesym-2\">[**]</a>") == 2
+    doAssert output3.count("href=\"#footnotesym-3\">[***]</a>") == 2
+    doAssert output3.count("href=\"#footnotesym-4\">[^]</a>") == 2
+    # footnote group
+    doAssert output3.count("<hr class=\"footnote\">" &
+                           "<div class=\"footnote-group\">") == 1
+    # footnotes
+    doAssert output3.count("<div class=\"footnote-label\"><sup><strong>" &
+               "<a href=\"#footnotesym-1\">[*]</a></strong></sup></div>") == 1
+    doAssert output3.count("<div class=\"footnote-label\"><sup><strong>" &
+               "<a href=\"#footnotesym-2\">[**]</a></strong></sup></div>") == 1
+    doAssert output3.count("<div class=\"footnote-label\"><sup><strong>" &
+               "<a href=\"#footnotesym-3\">[***]</a></strong></sup></div>") == 1
+    doAssert output3.count("<div class=\"footnote-label\"><sup><strong>" &
+               "<a href=\"#footnotesym-4\">[^]</a></strong></sup></div>") == 1
+    for i in 1 .. 4: doAssert ("Body" & $i) in output3
+
+    # check manual, auto-number and auto-label footnote enumeration
+    let input4 = dedent """
+      .. [3] Manual1.
+      .. [#] Auto-number1.
+      .. [#mylabel] Auto-label1.
+      .. [#note] Auto-label2.
+      .. [#] Auto-number2.
+
+      Ref. [#note]_ and [#]_ and [#]_.
+      """
+    let output4 = input4.toHtml(preferRst)
+    doAssert ">[-1]" notin output1
+    let order = @[
+        "footnote-3", "[3]", "Manual1.",
+        "footnoteauto-1", "[1]", "Auto-number1",
+        "footnote-mylabel", "[2]", "Auto-label1",
+        "footnote-note", "[4]", "Auto-label2",
+        "footnoteauto-2", "[5]", "Auto-number2",
+        ]
+    for i in 0 .. order.len-2:
+      let pos1 = output4.find(order[i])
+      let pos2 = output4.find(order[i+1])
+      doAssert pos1 >= 0
+      doAssert pos2 >= 0
+      doAssert pos1 < pos2
+
+    # forgot [#]_
+    let input5 = dedent """
+      .. [3] Manual1.
+      .. [#] Auto-number1.
+      .. [#note] Auto-label2.
+
+      Ref. [#note]_
+      """
+    var error5 = new string
+    let output5 = input5.toHtml(preferRst, error=error5)
+    check(error5[] == "input(1, 1) Error: mismatch in number of footnotes " &
+            "and their refs: 1 (lines 2) != 0 (lines ) for auto-numbered " &
+            "footnotes")
+
+    # extra [*]_
+    let input6 = dedent """
+      Ref. [*]_
+
+      .. [*] Auto-Symbol.
+
+      Ref. [*]_
+      """
+    var error6 = new string
+    let output6 = input6.toHtml(preferRst, error=error6)
+    check(error6[] == "input(1, 1) Error: mismatch in number of footnotes " &
+            "and their refs: 1 (lines 3) != 2 (lines 2, 6) for auto-symbol " &
+            "footnotes")
+
+    let input7 = dedent """
+      .. [Some:CITATION-2020] Citation.
+
+      Ref. [some:citation-2020]_.
+      """
+    let output7 = input7.toHtml(preferRst)
+    doAssert output7.count("href=\"#citation-somecoloncitationminus2020\"") == 2
+    doAssert output7.count("[Some:CITATION-2020]") == 1
+    doAssert output7.count("[some:citation-2020]") == 1
+    doAssert output3.count("<hr class=\"footnote\">" &
+                           "<div class=\"footnote-group\">") == 1
+
+    let input8 = dedent """
+      .. [Some] Citation.
+
+      Ref. [som]_.
+      """
+    var warnings8 = new seq[string]
+    let output8 = input8.toHtml(preferRst, warnings=warnings8)
+    check(warnings8[] == @["input(3, 7) Warning: broken link 'citation-som'"])
+
+    # check that footnote group does not break parsing of other directives:
+    let input9 = dedent """
+      .. [Some] Citation.
+
+      .. _`internal anchor`:
+
+      .. [Another] Citation.
+      .. just comment.
+      .. [Third] Citation.
+
+      Paragraph1.
+
+      Paragraph2 ref `internal anchor`_.
+      """
+    let output9 = input9.toHtml(preferRst)
+    # _`internal anchor` got erased:
+    check "href=\"#internal-anchor\"" notin output9
+    check "href=\"#citation-another\"" in output9
+    doAssert output9.count("<hr class=\"footnote\">" &
+                           "<div class=\"footnote-group\">") == 1
+    doAssert output9.count("<div class=\"footnote-label\">") == 3
+    doAssert "just comment" notin output9
+
+    # check that nested citations/footnotes work
+    let input10 = dedent """
+      Paragraph1 [#]_.
+
+      .. [First] Citation.
+
+         .. [#] Footnote.
+
+            .. [Third] Citation.
+      """
+    let output10 = input10.toHtml(preferRst)
+    doAssert output10.count("<hr class=\"footnote\">" &
+                            "<div class=\"footnote-group\">") == 3
+    doAssert output10.count("<div class=\"footnote-label\">") == 3
+    doAssert "<a href=\"#citation-first\">[First]</a>" in output10
+    doAssert "<a href=\"#footnoteauto-1\">[1]</a>" in output10
+    doAssert "<a href=\"#citation-third\">[Third]</a>" in output10
+
+    let input11 = ".. [note]\n"  # should not crash
+    let output11 = input11.toHtml(preferRst)
+    doAssert "<a href=\"#citation-note\">[note]</a>" in output11
+
+    # check that references to auto-numbered footnotes work
+    let input12 = dedent """
+      Ref. [#]_ and [#]_ STOP.
+
+      .. [#] Body1.
+      .. [#] Body3
+      .. [2] Body2.
+      """
+    let output12 = input12.toHtml(preferRst)
+    let orderAuto = @[
+        "#footnoteauto-1", "[1]",
+        "#footnoteauto-2", "[3]",
+        "STOP.",
+        "Body1.", "Body3", "Body2."
+        ]
+    for i in 0 .. orderAuto.len-2:
+      let pos1 = output12.find(orderAuto[i])
+      let pos2 = output12.find(orderAuto[i+1])
+      doAssert pos1 >= 0
+      doAssert pos2 >= 0
+      doAssert pos1 < pos2
+
+  test "Nim (RST extension) code-block":
+    # check that presence of fields doesn't consume the following text as
+    # its code (which is a literal block)
+    let input0 = dedent """
+      .. code-block:: nim
+         :number-lines: 0
+
+      Paragraph1"""
+    let output0 = input0.toHtml
+    doAssert "<p>Paragraph1</p>" in output0
+
+  test "Nim code-block :number-lines:":
+    let input = dedent """
+      .. code-block:: nim
+         :number-lines: 55
+
+         x
+         y
+      """
+    check "<pre class=\"line-nums\">55\n56\n</pre>" in input.toHtml
+
+  test "Nim code-block indentation":
+    let input = dedent """
+      .. code-block:: nim
+        :number-lines: 55
+
+       x
+      """
+    let output = input.toHtml
+    check "<pre class=\"line-nums\">55\n</pre>" in output
+    check "<span class=\"Identifier\">x</span>" in output
+
+  test "Nim code-block indentation":
+    let input = dedent """
+      .. code-block:: nim
+        :number-lines: 55
+         let a = 1
+      """
+    var error = new string
+    let output = input.toHtml(error=error)
+    check(error[] == "input(2, 3) Error: invalid field: " &
+                     "extra arguments were given to number-lines: ' let a = 1'")
+    check "" == output
+
+  test "code-block warning":
+    let input = dedent """
+      .. code:: Nim
+         :unsupportedField: anything
+
+      .. code:: unsupportedLang
+
+         anything
+
+      ```anotherLang
+      someCode
+      ```
+      """
+    let warnings = new seq[string]
+    let output = input.toHtml(warnings=warnings)
+    check(warnings[] == @[
+        "input(2, 4) Warning: field 'unsupportedField' not supported",
+        "input(4, 11) Warning: language 'unsupportedLang' not supported",
+        "input(8, 4) Warning: language 'anotherLang' not supported"
+        ])
+    check(output == "<pre class = \"listing\">anything</pre>" &
+                    "<p><pre class = \"listing\">someCode</pre> </p>")
+
+  test "RST admonitions":
+    # check that all admonitions are implemented
+    let input0 = dedent """
+      .. admonition:: endOf admonition
+      .. attention:: endOf attention
+      .. caution:: endOf caution
+      .. danger:: endOf danger
+      .. error:: endOf error
+      .. hint:: endOf hint
+      .. important:: endOf important
+      .. note:: endOf note
+      .. tip:: endOf tip
+      .. warning:: endOf warning
+    """
+    let output0 = input0.toHtml(
+      NoSandboxOpts
+    )
+    for a in ["admonition", "attention", "caution", "danger", "error", "hint",
+        "important", "note", "tip", "warning" ]:
+      doAssert "endOf " & a & "</div>" in output0
+
+    # Test that admonition does not swallow up the next paragraph.
+    let input1 = dedent """
+      .. error:: endOfError
+
+      Test paragraph.
+    """
+    let output1 = input1.toHtml(
+      NoSandboxOpts
+    )
+    doAssert "endOfError</div>" in output1
+    doAssert "<p>Test paragraph. </p>" in output1
+    doAssert "class=\"admonition admonition-error\"" in output1
+
+    # Test that second line is parsed as continuation of the first line.
+    let input2 = dedent """
+      .. error:: endOfError
+        Test2p.
+
+      Test paragraph.
+    """
+    let output2 = input2.toHtml(
+      NoSandboxOpts
+    )
+    doAssert "endOfError Test2p.</div>" in output2
+    doAssert "<p>Test paragraph. </p>" in output2
+    doAssert "class=\"admonition admonition-error\"" in output2
+
+    let input3 = dedent """
+      .. note:: endOfNote
+    """
+    let output3 = input3.toHtml(
+      NoSandboxOpts
+    )
+    doAssert "endOfNote</div>" in output3
+    doAssert "class=\"admonition admonition-info\"" in output3
+
+  test "RST internal links":
+    let input1 = dedent """
+      Start.
+
+      .. _target000:
+
+      Paragraph.
+
+      .. _target001:
+
+      * bullet list
+      * Y
+
+      .. _target002:
+
+      1. enumeration list
+      2. Y
+
+      .. _target003:
+
+      term 1
+        Definition list 1.
+
+      .. _target004:
+
+      | line block
+
+      .. _target005:
+
+      :a: field list value
+
+      .. _target006:
+
+      -a  option description
+
+      .. _target007:
+
+      ::
+
+        Literal block
+
+      .. _target008:
+
+      Doctest blocks are not implemented.
+
+      .. _target009:
+
+          block quote
+
+      .. _target010:
+
+      =====  =====  =======
+        A      B    A and B
+      =====  =====  =======
+      False  False  False
+      =====  =====  =======
+
+      .. _target100:
+
+      .. CAUTION:: admonition
+
+      .. _target101:
+
+      .. code:: nim
+
+         const pi = 3.14
+
+      .. _target102:
+
+      .. code-block::
+
+         const pi = 3.14
+
+      Paragraph2.
+
+      .. _target202:
+
+      ----
+
+      That was a transition.
+    """
+    let output1 = input1.toHtml(
+      preferRst
+    )
+    doAssert "<p id=\"target000\""     in output1
+    doAssert "<ul id=\"target001\""    in output1
+    doAssert "<ol id=\"target002\""    in output1
+    doAssert "<dl id=\"target003\""    in output1
+    doAssert "<p id=\"target004\""     in output1
+    doAssert "<table id=\"target005\"" in output1  # field list
+    doAssert "<div id=\"target006\""   in output1  # option list
+    doAssert "<pre id=\"target007\""   in output1
+    doAssert "<blockquote id=\"target009\"" in output1
+    doAssert "<table id=\"target010\"" in output1  # just table
+    doAssert "<span id=\"target100\""  in output1
+    doAssert "<pre id=\"target101\""   in output1  # code
+    doAssert "<pre id=\"target102\""   in output1  # code-block
+    doAssert "<hr id=\"target202\""    in output1
+
+  test "RST internal links for sections":
+    let input1 = dedent """
+      .. _target101:
+      .. _target102:
+
+      Section xyz
+      -----------
+
+      Ref. target101_
+    """
+    let output1 = input1.toHtml
+    # "target101" should be erased and changed to "section-xyz":
+    check "href=\"#target101\"" notin output1
+    check "id=\"target101\""    notin output1
+    check "href=\"#target102\"" notin output1
+    check "id=\"target102\""    notin output1
+    check "id=\"section-xyz\""     in output1
+    check "href=\"#section-xyz\""  in output1
+
+    let input2 = dedent """
+      .. _target300:
+
+      Section xyz
+      ===========
+
+      .. _target301:
+
+      SubsectionA
+      -----------
+
+      Ref. target300_ and target301_.
+
+      .. _target103:
+
+      .. [cit2020] note.
+
+      Ref. target103_.
+
+    """
+    let output2 = input2.toHtml(preferRst)
+    # "target101" should be erased and changed to "section-xyz":
+    doAssert "href=\"#target300\"" notin output2
+    doAssert "id=\"target300\""    notin output2
+    doAssert "href=\"#target301\"" notin output2
+    doAssert "id=\"target301\""    notin output2
+    doAssert "<h1 id=\"section-xyz\"" in output2
+    doAssert "<h2 id=\"subsectiona\"" in output2
+    # links should preserve their original names but point to section labels:
+    doAssert "href=\"#section-xyz\">target300" in output2
+    doAssert "href=\"#subsectiona\">target301" in output2
+    doAssert "href=\"#citation-cit2020\">target103" in output2
+
+    let output2l = rstToLatex(input2, {})
+    doAssert "\\label{section-xyz}\\hypertarget{section-xyz}{}" in output2l
+    doAssert "\\hyperlink{section-xyz}{target300}"  in output2l
+    doAssert "\\hyperlink{subsectiona}{target301}"  in output2l
+
+  test "RST internal links (inline)":
+    let input1 = dedent """
+      Paragraph with _`some definition`.
+
+      Ref. `some definition`_.
+    """
+    let output1 = input1.toHtml
+    doAssert "<span class=\"target\" " &
+        "id=\"some-definition\">some definition</span>" in output1
+    doAssert "Ref. <a class=\"reference internal\" " &
+        "href=\"#some-definition\">some definition</a>" in output1
+
+  test "RST references (additional symbols)":
+    # check that ., _, -, +, : are allowed symbols in references without ` `
+    let input1 = dedent """
+      sec.1
+      -----
+
+      2-other:sec+c_2
+      ^^^^^^^^^^^^^^^
+
+      .. _link.1_2021:
+
+      Paragraph
+
+      Ref. sec.1_! and 2-other:sec+c_2_;and link.1_2021_.
+    """
+    let output1 = input1.toHtml
+    doAssert "id=\"secdot1\"" in output1
+    doAssert "id=\"Z2minusothercolonsecplusc-2\"" in output1
+    check "id=\"linkdot1-2021\"" in output1
+    let ref1 = "<a class=\"reference internal\" href=\"#secdot1\">sec.1</a>"
+    let ref2 = "<a class=\"reference internal\" href=\"#Z2minusothercolonsecplusc-2\">2-other:sec+c_2</a>"
+    let ref3 = "<a class=\"reference internal\" href=\"#linkdot1-2021\">link.1_2021</a>"
+    let refline = "Ref. " & ref1 & "! and " & ref2 & ";and " & ref3 & "."
+    doAssert refline in output1
+
+  test "Option lists 1":
+    # check that "* b" is not consumed by previous bullet item because of
+    # incorrect indentation handling in option lists
+    let input = dedent """
+      * a
+        -m   desc
+        -n   very long
+             desc
+      * b"""
+    let output = input.toHtml
+    check(output.count("<ul") == 1)
+    check(output.count("<li>") == 2)
+    check(output.count("<div class=\"option-list\"") == 1)
+    check(optionListLabel("-m") &
+          """<div class="option-list-description">desc</div></div>""" in
+          output)
+    check(optionListLabel("-n") &
+          """<div class="option-list-description">very long desc</div></div>""" in
+          output)
+
+  test "Option lists 2":
+    # check that 2nd option list is not united with the 1st
+    let input = dedent """
+      * a
+        -m   desc
+        -n   very long
+             desc
+      -d  option"""
+    let output = input.toHtml
+    check(output.count("<ul") == 1)
+    check output.count("<div class=\"option-list\"") == 2
+    check(optionListLabel("-m") &
+          """<div class="option-list-description">desc</div></div>""" in
+          output)
+    check(optionListLabel("-n") &
+          """<div class="option-list-description">very long desc</div></div>""" in
+          output)
+    check(optionListLabel("-d") &
+          """<div class="option-list-description">option</div></div>""" in
+          output)
+    check "<p>option</p>" notin output
+
+  test "Option list 3 (double /)":
+    let input = dedent """
+      * a
+        //compile  compile1
+        //doc      doc1
+                   cont
+      -d  option"""
+    let output = input.toHtml
+    check(output.count("<ul") == 1)
+    check output.count("<div class=\"option-list\"") == 2
+    check(optionListLabel("compile") &
+          """<div class="option-list-description">compile1</div></div>""" in
+          output)
+    check(optionListLabel("doc") &
+          """<div class="option-list-description">doc1 cont</div></div>""" in
+          output)
+    check(optionListLabel("-d") &
+          """<div class="option-list-description">option</div></div>""" in
+          output)
+    check "<p>option</p>" notin output
+
+  test "Roles: subscript prefix/postfix":
+    let expected = "See <sub>some text</sub>."
+    check "See :subscript:`some text`.".toHtml == expected
+    check "See `some text`:subscript:.".toHtml == expected
+
+  test "Roles: correct parsing from beginning of line":
+    let expected = "<sup>3</sup>He is an isotope of helium."
+    check """:superscript:`3`\ He is an isotope of helium.""".toHtml == expected
+    check """:sup:`3`\ He is an isotope of helium.""".toHtml == expected
+    check """`3`:sup:\ He is an isotope of helium.""".toHtml == expected
+    check """`3`:superscript:\ He is an isotope of helium.""".toHtml == expected
+
+  test "Roles: warnings":
+    let input = dedent"""
+      See function :py:func:`spam`.
+
+      See also `egg`:py:class:.
+      """
+    var warnings = new seq[string]
+    let output = input.toHtml(warnings=warnings)
+    doAssert warnings[].len == 2
+    check "(1, 14) Warning: " in warnings[0]
+    check "language 'py:func' not supported" in warnings[0]
+    check "(3, 15) Warning: " in warnings[1]
+    check "language 'py:class' not supported" in warnings[1]
+    check("""<p>See function <span class="py:func">spam</span>.</p>""" & "\n" &
+          """<p>See also <span class="py:class">egg</span>. </p>""" ==
+          output)
+
+  test "(not) Roles: check escaping 1":
+    let expected = """See :subscript:<tt class="docutils literal">""" &
+                   """<span class="pre">""" & id"some" & " " & id"text" &
+                   "</span></tt>."
+    check """See \:subscript:`some text`.""".toHtml == expected
+    check """See :subscript\:`some text`.""".toHtml == expected
+
+  test "(not) Roles: check escaping 2":
+    check("""See :subscript:\`some text\`.""".toHtml ==
+          "See :subscript:`some text`.")
+
+  test "Field list":
+    check(":field: text".toHtml ==
+            """<table class="docinfo" frame="void" rules="none">""" &
+            """<col class="docinfo-name" /><col class="docinfo-content" />""" &
+            """<tbody valign="top"><tr><th class="docinfo-name">field:</th>""" &
+            """<td>text</td></tr>""" & "\n</tbody></table>")
+
+  test "Field list: body after newline":
+    let output = dedent"""
+      :field:
+        text1""".toHtml
+    check "<table class=\"docinfo\"" in output
+    check ">field:</th>" in output
+    check "<td>text1</td>" in output
+
+  test "Field list (incorrect)":
+    check ":field:text".toHtml == ":field:text"
+
+suite "RST/Code highlight":
+  test "Basic Python code highlight":
+    let pythonCode = """
+    .. code-block:: python
+
+      def f_name(arg=42):
+          print(f"{arg}")
+
+    """
+
+    let expected = """<blockquote><p><span class="Keyword">def</span> f_name<span class="Punctuation">(</span><span class="Punctuation">arg</span><span class="Operator">=</span><span class="DecNumber">42</span><span class="Punctuation">)</span><span class="Punctuation">:</span>
+    print<span class="Punctuation">(</span><span class="RawData">f&quot;{arg}&quot;</span><span class="Punctuation">)</span></p></blockquote>"""
+
+    check strip(rstToHtml(pythonCode, {}, newStringTable(modeCaseSensitive))) ==
+      strip(expected)
+
+
+suite "invalid targets":
+  test "invalid image target":
+    let input1 = dedent """.. image:: /images/myimage.jpg
+      :target: https://bar.com
+      :alt: Alt text for the image"""
+    let output1 = input1.toHtml
+    check output1 == """<a class="reference external" href="https://bar.com"><img src="/images/myimage.jpg" alt="Alt text for the image"/></a>"""
+
+    let input2 = dedent """.. image:: /images/myimage.jpg
+      :target: javascript://bar.com
+      :alt: Alt text for the image"""
+    let output2 = input2.toHtml
+    check output2 == """<img src="/images/myimage.jpg" alt="Alt text for the image"/>"""
+
+    let input3 = dedent """.. image:: /images/myimage.jpg
+      :target: bar.com
+      :alt: Alt text for the image"""
+    let output3 = input3.toHtml
+    check output3 == """<a class="reference external" href="bar.com"><img src="/images/myimage.jpg" alt="Alt text for the image"/></a>"""
+
+  test "invalid links":
+    check("(([Nim](https://nim-lang.org/)))".toHtml ==
+        """((<a class="reference external" href="https://nim-lang.org/">Nim</a>))""")
+    # unknown protocol is treated just like plain text, not a link
+    var warnings = new seq[string]
+    check("(([Nim](javascript://nim-lang.org/)))".toHtml(warnings=warnings) ==
+        """(([Nim](javascript://nim-lang.org/)))""")
+    check(warnings[] == @["input(1, 9) Warning: broken link 'javascript'"])
+    warnings[].setLen 0
+    check("`Nim <javascript://nim-lang.org/>`_".toHtml(warnings=warnings) ==
+      """Nim &lt;javascript://nim-lang.org/&gt;""")
+    check(warnings[] == @["input(1, 33) Warning: broken link 'javascript'"])
+
+suite "local file inclusion":
+  test "cannot include files in sandboxed mode":
+    var error = new string
+    discard ".. include:: ./readme.md".toHtml(error=error)
+    check(error[] == "input(1, 11) Error: disabled directive: 'include'")
+
+  test "code-block file directive is disabled":
+    var error = new string
+    discard ".. code-block:: nim\n    :file: ./readme.md".toHtml(error=error)
+    check(error[] == "input(2, 20) Error: disabled directive: 'file'")
+
+  test "code-block file directive is disabled - Markdown":
+    var error = new string
+    discard "```nim file = ./readme.md\n```".toHtml(error=error)
+    check(error[] == "input(1, 23) Error: disabled directive: 'file'")
+
+proc documentToHtml*(doc: string, isMarkdown: bool = false): string {.gcsafe.} =
+  var options = {roSupportMarkdown}
+  if isMarkdown:
+    options.incl roPreferMarkdown
+  result = rstToHtml(doc, options, defaultConfig())
diff --git a/tests/stdlib/tsegfaults.nim b/tests/stdlib/tsegfaults.nim
deleted file mode 100644
index 1d8508c52..000000000
--- a/tests/stdlib/tsegfaults.nim
+++ /dev/null
@@ -1,29 +0,0 @@
-discard """
-  output: '''caught a crash!
-caught a crash!
-caught a crash!
-caught a crash!
-caught a crash!
-caught a crash!
-caught a crash!
-caught a crash!
-caught a crash!
-caught a crash!
-caught a crash!'''
-"""
-
-import segfaults
-
-proc main =
-  try:
-    var x: ptr int
-    echo x[]
-    try:
-      raise newException(ValueError, "not a crash")
-    except ValueError:
-      discard
-  except NilAccessError:
-    echo "caught a crash!"
-
-for i in 0..10:
-  main()
diff --git a/tests/stdlib/tsequtils.nim b/tests/stdlib/tsequtils.nim
new file mode 100644
index 000000000..1094ae233
--- /dev/null
+++ b/tests/stdlib/tsequtils.nim
@@ -0,0 +1,547 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c js"
+"""
+
+# xxx move all tests under `main`
+
+import std/sequtils
+import strutils
+from algorithm import sorted
+import std/assertions
+
+{.experimental: "strictEffects".}
+{.push warningAsError[Effect]: on.}
+{.experimental: "strictFuncs".}
+
+# helper for testing double substitution side effects which are handled
+# by `evalOnceAs`
+var counter = 0
+proc identity[T](a: T): auto =
+  counter.inc
+  a
+
+block: # concat test
+  let
+    s1 = @[1, 2, 3]
+    s2 = @[4, 5]
+    s3 = @[6, 7]
+    total = concat(s1, s2, s3)
+  doAssert total == @[1, 2, 3, 4, 5, 6, 7]
+
+block: # count test
+  let
+    s1 = @[1, 2, 3, 2]
+    s2 = @['a', 'b', 'x', 'a']
+    a1 = [1, 2, 3, 2]
+    a2 = ['a', 'b', 'x', 'a']
+    r0 = count(s1, 0)
+    r1 = count(s1, 1)
+    r2 = count(s1, 2)
+    r3 = count(s2, 'y')
+    r4 = count(s2, 'x')
+    r5 = count(s2, 'a')
+    ar0 = count(a1, 0)
+    ar1 = count(a1, 1)
+    ar2 = count(a1, 2)
+    ar3 = count(a2, 'y')
+    ar4 = count(a2, 'x')
+    ar5 = count(a2, 'a')
+  doAssert r0 == 0
+  doAssert r1 == 1
+  doAssert r2 == 2
+  doAssert r3 == 0
+  doAssert r4 == 1
+  doAssert r5 == 2
+  doAssert ar0 == 0
+  doAssert ar1 == 1
+  doAssert ar2 == 2
+  doAssert ar3 == 0
+  doAssert ar4 == 1
+  doAssert ar5 == 2
+
+block: # cycle tests
+  let
+    a = @[1, 2, 3]
+    b: seq[int] = @[]
+    c = [1, 2, 3]
+
+  doAssert a.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
+  doAssert a.cycle(0) == @[]
+  #doAssert a.cycle(-1) == @[] # will not compile!
+  doAssert b.cycle(3) == @[]
+  doAssert c.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
+  doAssert c.cycle(0) == @[]
+
+block: # repeat tests
+  doAssert repeat(10, 5) == @[10, 10, 10, 10, 10]
+  doAssert repeat(@[1, 2, 3], 2) == @[@[1, 2, 3], @[1, 2, 3]]
+  doAssert repeat([1, 2, 3], 2) == @[[1, 2, 3], [1, 2, 3]]
+
+block: # deduplicates test
+  let
+    dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
+    dup2 = @["a", "a", "c", "d", "d"]
+    dup3 = [1, 1, 3, 4, 2, 2, 8, 1, 4]
+    dup4 = ["a", "a", "c", "d", "d"]
+    unique1 = deduplicate(dup1)
+    unique2 = deduplicate(dup2)
+    unique3 = deduplicate(dup3)
+    unique4 = deduplicate(dup4)
+    unique5 = deduplicate(dup1.sorted, true)
+    unique6 = deduplicate(dup2, true)
+    unique7 = deduplicate(dup3.sorted, true)
+    unique8 = deduplicate(dup4, true)
+  doAssert unique1 == @[1, 3, 4, 2, 8]
+  doAssert unique2 == @["a", "c", "d"]
+  doAssert unique3 == @[1, 3, 4, 2, 8]
+  doAssert unique4 == @["a", "c", "d"]
+  doAssert unique5 == @[1, 2, 3, 4, 8]
+  doAssert unique6 == @["a", "c", "d"]
+  doAssert unique7 == @[1, 2, 3, 4, 8]
+  doAssert unique8 == @["a", "c", "d"]
+
+block: # zip test
+  let
+    short = @[1, 2, 3]
+    long = @[6, 5, 4, 3, 2, 1]
+    words = @["one", "two", "three"]
+    ashort = [1, 2, 3]
+    along = [6, 5, 4, 3, 2, 1]
+    awords = ["one", "two", "three"]
+    zip1 = zip(short, long)
+    zip2 = zip(short, words)
+    zip3 = zip(ashort, along)
+  doAssert zip1 == @[(1, 6), (2, 5), (3, 4)]
+  doAssert zip2 == @[(1, "one"), (2, "two"), (3, "three")]
+  doAssert zip3 == @[(1, 6), (2, 5), (3, 4)]
+  doAssert zip1[2][1] == 4
+  doAssert zip2[2][1] == "three"
+  doAssert zip3[2][1] == 4
+  when (NimMajor, NimMinor) <= (1, 0):
+    let
+      # In Nim 1.0.x and older, zip returned a seq of tuple strictly
+      # with fields named "a" and "b".
+      zipAb = zip(ashort, awords)
+    doAssert zipAb == @[(a: 1, b: "one"), (2, "two"), (3, "three")]
+    doAssert zipAb[2].b == "three"
+  else:
+    let
+      # As zip returns seq of anonymous tuples, they can be assigned
+      # to any variable that's a sequence of named tuples too.
+      zipXy: seq[tuple[x: int, y: string]] = zip(ashort, awords)
+      zipMn: seq[tuple[m: int, n: string]] = zip(ashort, words)
+    doAssert zipXy == @[(x: 1, y: "one"), (2, "two"), (3, "three")]
+    doAssert zipMn == @[(m: 1, n: "one"), (2, "two"), (3, "three")]
+    doAssert zipXy[2].y == "three"
+    doAssert zipMn[2].n == "three"
+
+block: # distribute tests
+  let numbers = @[1, 2, 3, 4, 5, 6, 7]
+  doAssert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
+  doAssert numbers.distribute(6)[0] == @[1, 2]
+  doAssert numbers.distribute(6)[5] == @[7]
+  let a = @[1, 2, 3, 4, 5, 6, 7]
+  doAssert a.distribute(1, true) == @[@[1, 2, 3, 4, 5, 6, 7]]
+  doAssert a.distribute(1, false) == @[@[1, 2, 3, 4, 5, 6, 7]]
+  doAssert a.distribute(2, true) == @[@[1, 2, 3, 4], @[5, 6, 7]]
+  doAssert a.distribute(2, false) == @[@[1, 2, 3, 4], @[5, 6, 7]]
+  doAssert a.distribute(3, true) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
+  doAssert a.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]]
+  doAssert a.distribute(4, true) == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
+  doAssert a.distribute(4, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
+  doAssert a.distribute(5, true) == @[@[1, 2], @[3, 4], @[5], @[6], @[7]]
+  doAssert a.distribute(5, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]]
+  doAssert a.distribute(6, true) == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]]
+  doAssert a.distribute(6, false) == @[
+    @[1, 2], @[3, 4], @[5, 6], @[7], @[], @[]]
+  doAssert a.distribute(8, false) == a.distribute(8, true)
+  doAssert a.distribute(90, false) == a.distribute(90, true)
+  var b = @[0]
+  for f in 1 .. 25: b.add(f)
+  doAssert b.distribute(5, true)[4].len == 5
+  doAssert b.distribute(5, false)[4].len == 2
+
+block: # map test
+  let
+    numbers = @[1, 4, 5, 8, 9, 7, 4]
+    anumbers = [1, 4, 5, 8, 9, 7, 4]
+    m1 = map(numbers, proc(x: int): int = 2*x)
+    m2 = map(anumbers, proc(x: int): int = 2*x)
+  doAssert m1 == @[2, 8, 10, 16, 18, 14, 8]
+  doAssert m2 == @[2, 8, 10, 16, 18, 14, 8]
+
+block: # apply test
+  var a = @["1", "2", "3", "4"]
+  apply(a, proc(x: var string) = x &= "42")
+  doAssert a == @["142", "242", "342", "442"]
+
+block: # filter proc test
+  let
+    colors = @["red", "yellow", "black"]
+    acolors = ["red", "yellow", "black"]
+    f1 = filter(colors, proc(x: string): bool = x.len < 6)
+    f2 = filter(colors) do (x: string) -> bool: x.len > 5
+    f3 = filter(acolors, proc(x: string): bool = x.len < 6)
+    f4 = filter(acolors) do (x: string) -> bool: x.len > 5
+  doAssert f1 == @["red", "black"]
+  doAssert f2 == @["yellow"]
+  doAssert f3 == @["red", "black"]
+  doAssert f4 == @["yellow"]
+
+block: # filter iterator test
+  let numbers = @[1, 4, 5, 8, 9, 7, 4]
+  let anumbers = [1, 4, 5, 8, 9, 7, 4]
+  doAssert toSeq(filter(numbers, proc (x: int): bool = x mod 2 == 0)) ==
+    @[4, 8, 4]
+  doAssert toSeq(filter(anumbers, proc (x: int): bool = x mod 2 == 0)) ==
+    @[4, 8, 4]
+
+block: # keepIf test
+  var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1]
+  keepIf(floats, proc(x: float): bool = x > 10)
+  doAssert floats == @[13.0, 12.5, 10.1]
+
+block: # insert tests
+  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)
+  doAssert dest == outcome, """\
+  Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1]
+  at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]"""
+
+block: # filterIt test
+  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)
+  doAssert acceptable == @[-2.0, 24.5, 44.31]
+  doAssert notAcceptable == @[-272.15, 99.9, -113.44]
+
+block: # keepItIf test
+  var candidates = @["foo", "bar", "baz", "foobar"]
+  keepItIf(candidates, it.len == 3 and it[0] == 'b')
+  doAssert candidates == @["bar", "baz"]
+
+block: # all
+  let
+    numbers = @[1, 4, 5, 8, 9, 7, 4]
+    anumbers = [1, 4, 5, 8, 9, 7, 4]
+    len0seq: seq[int] = @[]
+  doAssert all(numbers, proc (x: int): bool = return x < 10) == true
+  doAssert all(numbers, proc (x: int): bool = return x < 9) == false
+  doAssert all(len0seq, proc (x: int): bool = return false) == true
+  doAssert all(anumbers, proc (x: int): bool = return x < 10) == true
+  doAssert all(anumbers, proc (x: int): bool = return x < 9) == false
+
+block: # allIt
+  let
+    numbers = @[1, 4, 5, 8, 9, 7, 4]
+    anumbers = [1, 4, 5, 8, 9, 7, 4]
+    len0seq: seq[int] = @[]
+  doAssert allIt(numbers, it < 10) == true
+  doAssert allIt(numbers, it < 9) == false
+  doAssert allIt(len0seq, false) == true
+  doAssert allIt(anumbers, it < 10) == true
+  doAssert allIt(anumbers, it < 9) == false
+
+block: # any
+  let
+    numbers = @[1, 4, 5, 8, 9, 7, 4]
+    anumbers = [1, 4, 5, 8, 9, 7, 4]
+    len0seq: seq[int] = @[]
+  doAssert any(numbers, proc (x: int): bool = return x > 8) == true
+  doAssert any(numbers, proc (x: int): bool = return x > 9) == false
+  doAssert any(len0seq, proc (x: int): bool = return true) == false
+  doAssert any(anumbers, proc (x: int): bool = return x > 8) == true
+  doAssert any(anumbers, proc (x: int): bool = return x > 9) == false
+
+block: # anyIt
+  let
+    numbers = @[1, 4, 5, 8, 9, 7, 4]
+    anumbers = [1, 4, 5, 8, 9, 7, 4]
+    len0seq: seq[int] = @[]
+  doAssert anyIt(numbers, it > 8) == true
+  doAssert anyIt(numbers, it > 9) == false
+  doAssert anyIt(len0seq, true) == false
+  doAssert anyIt(anumbers, it > 8) == true
+  doAssert anyIt(anumbers, it > 9) == false
+
+block: # toSeq test
+  block:
+    let
+      numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
+      oddNumbers = toSeq(filter(numeric) do (x: int) -> bool:
+        if x mod 2 == 1:
+          result = true)
+    doAssert oddNumbers == @[1, 3, 5, 7, 9]
+
+  block:
+    doAssert [1, 2].toSeq == @[1, 2]
+    doAssert @[1, 2].toSeq == @[1, 2]
+
+    doAssert @[1, 2].toSeq == @[1, 2]
+    doAssert toSeq(@[1, 2]) == @[1, 2]
+
+  block:
+    iterator myIter(seed: int): auto =
+      for i in 0..<seed:
+        yield i
+    doAssert toSeq(myIter(2)) == @[0, 1]
+
+  block:
+    iterator myIter(): auto {.inline.} =
+      yield 1
+      yield 2
+
+    doAssert myIter.toSeq == @[1, 2]
+    doAssert toSeq(myIter) == @[1, 2]
+
+  when not defined(js):
+    # pending #4695
+    block:
+        iterator myIter(): int {.closure.} =
+          yield 1
+          yield 2
+
+        doAssert myIter.toSeq == @[1, 2]
+        doAssert toSeq(myIter) == @[1, 2]
+
+    block:
+      proc myIter(): auto =
+        iterator ret(): int {.closure.} =
+          yield 1
+          yield 2
+        result = ret
+
+      doAssert myIter().toSeq == @[1, 2]
+      doAssert toSeq(myIter()) == @[1, 2]
+
+    block:
+      proc myIter(n: int): auto =
+        var counter = 0
+        iterator ret(): int {.closure.} =
+          while counter < n:
+            yield counter
+            counter.inc
+        result = ret
+
+      block:
+        let myIter3 = myIter(3)
+        doAssert myIter3.toSeq == @[0, 1, 2]
+      block:
+        let myIter3 = myIter(3)
+        doAssert toSeq(myIter3) == @[0, 1, 2]
+      block:
+        # makes sure this does not hang forever
+        doAssert myIter(3).toSeq == @[0, 1, 2]
+        doAssert toSeq(myIter(3)) == @[0, 1, 2]
+
+block:
+  # tests https://github.com/nim-lang/Nim/issues/7187
+  counter = 0
+  let ret = toSeq(@[1, 2, 3].identity().filter(proc (x: int): bool = x < 3))
+  doAssert ret == @[1, 2]
+  doAssert counter == 1
+block: # foldl tests
+  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)
+  doAssert addition == 25, "Addition is (((5)+9)+11)"
+  doAssert subtraction == -15, "Subtraction is (((5)-9)-11)"
+  doAssert multiplication == 495, "Multiplication is (((5)*9)*11)"
+  doAssert concatenation == "nimiscool"
+
+block: # foldr tests
+  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)
+  doAssert addition == 25, "Addition is (5+(9+(11)))"
+  doAssert subtraction == 7, "Subtraction is (5-(9-(11)))"
+  doAssert multiplication == 495, "Multiplication is (5*(9*(11)))"
+  doAssert concatenation == "nimiscool"
+  doAssert toSeq(1..3).foldr(a + b) == 6 # issue #14404
+
+block: # mapIt + applyIt test
+  counter = 0
+  var
+    nums = @[1, 2, 3, 4]
+    strings = nums.identity.mapIt($(4 * it))
+  doAssert counter == 1
+  nums.applyIt(it * 3)
+  doAssert nums[0] + nums[3] == 15
+  doAssert strings[2] == "12"
+
+block: # newSeqWith tests
+  var seq2D = newSeqWith(4, newSeq[bool](2))
+  seq2D[0][0] = true
+  seq2D[1][0] = true
+  seq2D[0][1] = true
+  doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]]
+
+block: # bug #21538
+  var x: seq[int] = @[2, 4]
+  var y = newSeqWith(x.pop(), true)
+  doAssert y == @[true, true, true, true]
+
+block: # mapLiterals tests
+  let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int)
+  doAssert x is array[4, int]
+  doAssert mapLiterals((1, ("abc"), 2), float, nested = false) ==
+    (float(1), "abc", float(2))
+  doAssert mapLiterals(([1], ("abc"), 2), `$`, nested = true) ==
+    (["1"], "abc", "2")
+
+block: # mapIt with openArray
+  counter = 0
+  proc foo(x: openArray[int]): seq[int] = x.mapIt(it * 10)
+  doAssert foo([identity(1), identity(2)]) == @[10, 20]
+  doAssert counter == 2
+
+block: # mapIt with direct openArray
+  proc foo1(x: openArray[int]): seq[int] = x.mapIt(it * 10)
+  counter = 0
+  doAssert foo1(openArray[int]([identity(1), identity(2)])) == @[10, 20]
+  doAssert counter == 2
+
+  # Corner cases (openArray literals should not be common)
+  template foo2(x: openArray[int]): seq[int] = x.mapIt(it * 10)
+  counter = 0
+  doAssert foo2(openArray[int]([identity(1), identity(2)])) == @[10, 20]
+  doAssert counter == 2
+
+  counter = 0
+  doAssert openArray[int]([identity(1), identity(2)]).mapIt(it) == @[1, 2]
+  doAssert counter == 2
+
+block: # mapIt empty test, see https://github.com/nim-lang/Nim/pull/8584#pullrequestreview-144723468
+  # NOTE: `[].mapIt(it)` is illegal, just as `let a = @[]` is (lacks type
+  # of elements)
+  doAssert: not compiles(mapIt(@[], it))
+  doAssert: not compiles(mapIt([], it))
+  doAssert newSeq[int](0).mapIt(it) == @[]
+
+block: # mapIt redifinition check, see https://github.com/nim-lang/Nim/issues/8580
+  let s2 = [1, 2].mapIt(it)
+  doAssert s2 == @[1, 2]
+
+block:
+  counter = 0
+  doAssert [1, 2].identity().mapIt(it*2).mapIt(it*10) == @[20, 40]
+  # https://github.com/nim-lang/Nim/issues/7187 test case
+  doAssert counter == 1
+
+block: # mapIt with invalid RHS for `let` (#8566)
+  type X = enum
+    A, B
+  doAssert mapIt(X, $it) == @["A", "B"]
+
+block:
+  # bug #9093
+  let inp = "a:b,c:d"
+
+  let outp = inp.split(",").mapIt(it.split(":"))
+  doAssert outp == @[@["a", "b"], @["c", "d"]]
+
+
+block:
+  proc iter(len: int): auto =
+    result = iterator(): int =
+      for i in 0..<len:
+        yield i
+
+  # xxx: obscure CT error: basic_types.nim(16, 16) Error: internal error: symbol has no generated name: true
+  when not defined(js):
+    doAssert: iter(3).mapIt(2*it).foldl(a + b) == 6
+
+block: # strictFuncs tests with ref object
+  type Foo = ref object
+
+  let foo1 = Foo()
+  let foo2 = Foo()
+  let foos = @[foo1, foo2]
+
+  # Procedures that are `func`
+  discard concat(foos, foos)
+  discard count(foos, foo1)
+  discard cycle(foos, 3)
+  discard deduplicate(foos)
+  discard minIndex(foos)
+  discard maxIndex(foos)
+  discard distribute(foos, 2)
+  var mutableFoos = foos
+  mutableFoos.delete(0..1)
+  mutableFoos.insert(foos)
+
+  # Some procedures that are `proc`, but were reverted from `func`
+  discard repeat(foo1, 3)
+  discard zip(foos, foos)
+  let fooTuples = @[(foo1, 1), (foo2, 2)]
+  discard unzip(fooTuples)
+
+template main =
+  # xxx move all tests here
+  block: # delete tests
+    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)
+    doAssert outcome == dest, """\
+    Deleting range 3-9 from [1,1,1,2,2,2,2,2,2,1,1,1,1,1]
+    is [1,1,1,1,1,1,1,1]"""
+    var x = @[1, 2, 3]
+    x.delete(100, 100)
+    doAssert x == @[1, 2, 3]
+
+  block: # delete tests
+    var a = @[10, 11, 12, 13, 14]
+    doAssertRaises(IndexDefect): a.delete(4..5)
+    doAssertRaises(IndexDefect): a.delete(4..<6)
+    doAssertRaises(IndexDefect): a.delete(-1..1)
+    doAssertRaises(IndexDefect): a.delete(-1 .. -1)
+    doAssertRaises(IndexDefect): a.delete(5..5)
+    doAssertRaises(IndexDefect): a.delete(5..3)
+    doAssertRaises(IndexDefect): a.delete(5..<5) # edge case
+    doAssert a == @[10, 11, 12, 13, 14]
+    a.delete(4..4)
+    doAssert a == @[10, 11, 12, 13]
+    a.delete(1..2)
+    doAssert a == @[10, 13]
+    a.delete(1..<1) # empty slice
+    doAssert a == @[10, 13]
+    a.delete(0..<0)
+    doAssert a == @[10, 13]
+    a.delete(0..0)
+    doAssert a == @[13]
+    a.delete(0..0)
+    doAssert a == @[]
+    doAssertRaises(IndexDefect): a.delete(0..0)
+    doAssertRaises(IndexDefect): a.delete(0..<0) # edge case
+    block:
+      type A = object
+        a0: int
+      var a = @[A(a0: 10), A(a0: 11), A(a0: 12)]
+      a.delete(0..1)
+      doAssert a == @[A(a0: 12)]
+    block:
+      type A = ref object
+      let a0 = A()
+      let a1 = A()
+      let a2 = A()
+      var a = @[a0, a1, a2]
+      a.delete(0..1)
+      doAssert a == @[a2]
+
+static: main()
+main()
+
+{.pop.}
diff --git a/tests/stdlib/tsetutils.nim b/tests/stdlib/tsetutils.nim
new file mode 100644
index 000000000..c8498f23e
--- /dev/null
+++ b/tests/stdlib/tsetutils.nim
@@ -0,0 +1,49 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c js"
+"""
+
+import std/setutils
+import std/assertions
+
+type 
+  Colors = enum
+    red, green = 5, blue = 10
+  Bar = enum
+    bar0 = -1, bar1, bar2
+
+template main =
+  block: # toSet
+    doAssert "abcbb".toSet == {'a', 'b', 'c'}
+    doAssert toSet([10u8, 12, 13]) == {10u8, 12, 13}
+    doAssert toSet(0u16..30) == {0u16..30}
+    type A = distinct char
+    doAssert [A('x')].toSet == {A('x')}
+
+  block: # fullSet
+    doAssert fullSet(Colors) == {red, green, blue}
+    doAssert fullSet(char) == {0.chr..255.chr}
+    doAssert fullSet(Bar) == {bar0, bar1, bar2}
+    doAssert fullSet(bool) == {true, false}
+
+  block: # complement
+    doAssert {red, blue}.complement == {green}
+    doAssert (complement {red, green, blue}).card == 0
+    doAssert (complement {false}) == {true}
+    doAssert {bar0}.complement == {bar1, bar2}
+    doAssert {range[0..10](0), 1, 2, 3}.complement == {range[0..10](4), 5, 6, 7, 8, 9, 10}
+    doAssert {'0'..'9'}.complement == {0.char..255.char} - {'0'..'9'}
+
+  block: # `[]=`
+    type A = enum
+      a0, a1, a2, a3
+    var s = {a0, a3}
+    s[a0] = false
+    s[a1] = false
+    doAssert s == {a3}
+    s[a2] = true
+    s[a3] = true
+    doAssert s == {a2, a3}
+
+main()
+static: main()
diff --git a/tests/stdlib/tsharedlist.nim b/tests/stdlib/tsharedlist.nim
new file mode 100644
index 000000000..b91302d19
--- /dev/null
+++ b/tests/stdlib/tsharedlist.nim
@@ -0,0 +1,49 @@
+discard """
+  matrix: "--mm:orc; --mm:refc"
+"""
+
+import std/sharedlist
+import std/assertions
+
+block:
+  var
+    list: SharedList[int]
+    count: int
+
+  init(list)
+
+  for i in 1 .. 250:
+    list.add i
+
+  for i in list:
+    inc count
+
+  doAssert count == 250
+
+  deinitSharedList(list)
+
+
+block: # bug #17696
+  var keysList = SharedList[string]()
+  init(keysList)
+
+  keysList.add("a")
+  keysList.add("b")
+  keysList.add("c")
+  keysList.add("d")
+  keysList.add("e")
+  keysList.add("f")
+
+
+  # Remove element "b" and "d" from the list. 
+  keysList.iterAndMutate(proc (key: string): bool =
+    if key == "b" or key == "d": # remove only "b" and "d"
+      return true
+    return false
+  )
+
+  var results: seq[string]
+  for key in keysList.items:
+    results.add key
+
+  doAssert results == @["a", "f", "c", "e"]
diff --git a/tests/stdlib/tsharedtable.nim b/tests/stdlib/tsharedtable.nim
new file mode 100644
index 000000000..10ad5f658
--- /dev/null
+++ b/tests/stdlib/tsharedtable.nim
@@ -0,0 +1,90 @@
+discard """
+matrix: "--mm:refc; --mm:orc"
+output: '''
+'''
+"""
+
+import sharedtables
+import std/assertions
+
+block:
+  var table: SharedTable[int, int]
+
+  init(table)
+  table[1] = 10
+  doAssert table.mget(1) == 10
+  doAssert table.mgetOrPut(3, 7) == 7
+  doAssert table.mgetOrPut(3, 99) == 7
+  deinitSharedTable(table)
+
+import sequtils, algorithm
+proc sortedPairs[T](t: T): auto = toSeq(t.pairs).sorted
+template sortedItems(t: untyped): untyped = sorted(toSeq(t))
+
+import tables # refs issue #13504
+
+block: # we use Table as groundtruth, it's well tested elsewhere
+  template testDel(t, t0) =
+    template put2(i) =
+      t[i] = i
+      t0[i] = i
+
+    template add2(i, val) =
+      t.add(i, val)
+      t0.add(i, val)
+
+    template del2(i) =
+      t.del(i)
+      t0.del(i)
+
+    template checkEquals() =
+      doAssert t.len == t0.len
+      for k,v in t0:
+        doAssert t.mgetOrPut(k, -1) == v # sanity check
+        doAssert t.mget(k) == v
+
+    let n = 100
+    let n2 = n*2
+    let n3 = n*3
+    let n4 = n*4
+    let n5 = n*5
+
+    for i in 0..<n:
+      put2(i)
+    for i in 0..<n:
+      if i mod 3 == 0:
+        del2(i)
+    for i in n..<n2:
+      put2(i)
+    for i in 0..<n2:
+      if i mod 7 == 0:
+        del2(i)
+
+    checkEquals()
+
+    for i in n2..<n3:
+      t0[i] = -2
+      doAssert t.mgetOrPut(i, -2) == -2
+      doAssert t.mget(i) == -2
+
+    for i in 0..<n4:
+      let ok = i in t0
+      if not ok: t0[i] = -i
+      doAssert t.hasKeyOrPut(i, -i) == ok
+
+    checkEquals()
+
+    for i in n4..<n5:
+      add2(i, i*10)
+      add2(i, i*11)
+      add2(i, i*12)
+      del2(i)
+      del2(i)
+
+    checkEquals()
+
+  var t: SharedTable[int, int]
+  init(t) # ideally should be auto-init
+  var t0: Table[int, int]
+  testDel(t, t0)
+  deinitSharedTable(t)
diff --git a/tests/stdlib/tsince.nim b/tests/stdlib/tsince.nim
new file mode 100644
index 000000000..a0a4229cb
--- /dev/null
+++ b/tests/stdlib/tsince.nim
@@ -0,0 +1,32 @@
+import std/private/since
+import std/assertions
+
+proc fun1(): int {.since: (1, 3).} = 12
+proc fun1Bad(): int {.since: (99, 3).} = 12
+proc fun2(): int {.since: (1, 3, 1).} = 12
+proc fun2Bad(): int {.since: (99, 3, 1).} = 12
+
+doAssert fun1() == 12
+doAssert declared(fun1)
+doAssert not declared(fun1Bad)
+
+doAssert fun2() == 12
+doAssert declared(fun2)
+doAssert not declared(fun2Bad)
+
+var ok = false
+since (1, 3):
+  ok = true
+doAssert ok
+
+ok = false
+since (1, 3, 1):
+  ok = true
+doAssert ok
+
+since (99, 3):
+  doAssert false
+
+template fun3(): int {.since: (1, 3).} = 12
+
+doAssert declared(fun3)
diff --git a/tests/stdlib/tsinglylinkedring.nim b/tests/stdlib/tsinglylinkedring.nim
deleted file mode 100644
index 93f0c69cd..000000000
--- a/tests/stdlib/tsinglylinkedring.nim
+++ /dev/null
@@ -1,29 +0,0 @@
-discard """
-  output: '''[5]
-[4, 5]
-[3, 4, 5]
-[2, 3, 4, 5]
-[2, 3, 4, 5, 6]
-[2, 3, 4, 5, 6, 7]
-[2, 3, 4, 5, 6, 7, 8]
-[1, 2, 3, 4, 5, 6, 7, 8]'''
-"""
-import lists
-
-var r = initSinglyLinkedRing[int]()
-r.prepend(5)
-echo r
-r.prepend(4)
-echo r
-r.prepend(3)
-echo r
-r.prepend(2)
-echo r
-r.append(6)
-echo r
-r.append(7)
-echo r
-r.append(8)
-echo r
-r.prepend(1)
-echo r
diff --git a/tests/stdlib/tsocketstreams.nim b/tests/stdlib/tsocketstreams.nim
new file mode 100644
index 000000000..a37e7c34c
--- /dev/null
+++ b/tests/stdlib/tsocketstreams.nim
@@ -0,0 +1,65 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  output: '''
+OM
+NIM
+3
+NIM
+NIM
+Hello server!
+Hi there client!
+'''"""
+import std/socketstreams, net, streams
+
+block UDP:
+  var recvSocket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
+  var recvStream = newReadSocketStream(recvSocket)
+  recvSocket.bindAddr(Port(12345), "127.0.0.1")
+
+  var sendSocket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
+  sendSocket.connect("127.0.0.1", Port(12345))
+  var sendStream = newWriteSocketStream(sendSocket)
+  sendStream.write "NOM\n"
+  sendStream.setPosition(1)
+  echo sendStream.peekStr(2)
+  sendStream.write "I"
+  sendStream.setPosition(0)
+  echo sendStream.readStr(3)
+  echo sendStream.getPosition()
+  sendStream.flush()
+
+  echo recvStream.readLine()
+  recvStream.setPosition(0)
+  echo recvStream.readLine()
+  recvStream.close()
+
+block TCP:
+  var server = newSocket()
+  server.setSockOpt(OptReusePort, true)
+  server.bindAddr(Port(12345))
+  server.listen()
+
+  var
+    client = newSocket()
+    clientRequestStream = newWriteSocketStream(client)
+    clientResponseStream = newReadSocketStream(client)
+  client.connect("127.0.0.1", Port(12345))
+  clientRequestStream.writeLine("Hello server!")
+  clientRequestStream.flush()
+
+  var
+    incoming: Socket
+    address: string
+  server.acceptAddr(incoming, address)
+  var
+    serverRequestStream = newReadSocketStream(incoming)
+    serverResponseStream = newWriteSocketStream(incoming)
+  echo serverRequestStream.readLine()
+  serverResponseStream.writeLine("Hi there client!")
+  serverResponseStream.flush()
+  serverResponseStream.close()
+  serverRequestStream.close()
+
+  echo clientResponseStream.readLine()
+  clientResponseStream.close()
+  clientRequestStream.close()
diff --git a/tests/stdlib/tsortcall.nim b/tests/stdlib/tsortcall.nim
index efe1d0b8b..32e004921 100644
--- a/tests/stdlib/tsortcall.nim
+++ b/tests/stdlib/tsortcall.nim
@@ -1,5 +1,69 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import algorithm
+import unittest
+
+
+suite "test sort, sorted, and isSorted procs":
+  proc foosort(ships: var seq[int]) = sort(ships, system.cmp[int])
+
+  type
+    User = object
+      name: string
+      age: int
+
+  func newUser(name: string, age: int): User =
+    result.name = name
+    result.age = age
+
+  proc compareUsers(x, y: User): int =
+    if x.age == y.age: return 0
+    if x.age < y.age: return -1
+    return 1
+
+  setup:
+    var
+      unSortedIntSeq = @[1, 4, 3, 5, -1]
+      unSortedUserSeq = @[newUser("Andreas", 34), newUser("Alice", 12), newUser("Bob", 23)]
+
+    let
+      sortedIntSeq = @[-1, 1, 3, 4, 5]
+      sortedUserSeq = @[newUser("Alice", 12), newUser("Bob", 23), newUser("Andreas", 34)]
+
+  test "test the shortcut versions of sort, sorted, and isSorted":
+    check(not unSortedIntSeq.isSorted)
+    check sorted(unSortedIntSeq) == sortedIntSeq
+    check sorted(unSortedIntSeq).isSorted
+
+    unSortedIntSeq.sort()
+    check unSortedIntSeq == sortedIntSeq
+    check unSortedIntSeq.isSorted
+
+  test "test the shortcut versions with descending sort order":
+    check(not unSortedIntSeq.isSorted(SortOrder.Descending))
+    check sorted(unSortedIntSeq, SortOrder.Descending) == reversed sortedIntSeq
+    check sorted(unSortedIntSeq).isSorted(SortOrder.Ascending)
+
+    unSortedIntSeq.sort(SortOrder.Descending)
+    check unSortedIntSeq == reversed sortedIntSeq
+    check unSortedIntSeq.isSorted(SortOrder.Descending)
 
-proc foosort(ships: var seq[int]) = sort(ships, system.cmp[int])
+  test "test the versions that accept a custom compareUsers function":
+    check(not unSortedUserSeq.isSorted(compareUsers))
+    check sorted(unSortedUserSeq, compareUsers) == sortedUserSeq
+    check sorted(unSortedUserSeq, compareUsers).isSorted(compareUsers)
 
+    unSortedUserSeq.sort(compareUsers)
+    check unSortedUserSeq == sortedUserSeq
+    check unSortedUserSeq.isSorted(compareUsers)
 
+  test "test the long versions with descending sort order":
+    check(not unSortedUserSeq.isSorted(compareUsers, SortOrder.Descending))
+    check sorted(unSortedUserSeq, compareUsers, SortOrder.Descending) == reversed sortedUserSeq
+    check sorted(unSortedUserSeq, compareUsers,
+                  SortOrder.Descending).isSorted(compareUsers, SortOrder.Descending)
+    unSortedUserSeq.sort(compareUsers, SortOrder.Descending)
+    check unSortedUserSeq == reversed sortedUserSeq
+    check unSortedUserSeq.isSorted(compareUsers, SortOrder.Descending)
diff --git a/tests/stdlib/tsplit.nim b/tests/stdlib/tsplit.nim
deleted file mode 100644
index 44da58aca..000000000
--- a/tests/stdlib/tsplit.nim
+++ /dev/null
@@ -1,20 +0,0 @@
-discard """
-  file: "tsplit.nim"
-  output: "true"
-"""
-import strutils
-
-var s = ""
-for w in split("|abc|xy|z", {'|'}):
-  s.add("#")
-  s.add(w)
-
-if s == "##abc#xy#z":
-  echo "true"
-else:
-  echo "false"
-
-#OUT true
-
-
-
diff --git a/tests/stdlib/tsplit2.nim b/tests/stdlib/tsplit2.nim
deleted file mode 100644
index 7fd9dda74..000000000
--- a/tests/stdlib/tsplit2.nim
+++ /dev/null
@@ -1,19 +0,0 @@
-discard """
-  file: "tsplit2.nim"
-  output: "true"
-"""
-import strutils
-
-var s = ""
-for w in split("|abc|xy|z", {'|'}):
-  s.add("#")
-  s.add(w)
-
-try:
-  discard "hello".split("")
-  echo "false"
-except AssertionError:
-  echo "true"
-
-#OUT true
-
diff --git a/tests/stdlib/tsqlitebindatas.nim b/tests/stdlib/tsqlitebindatas.nim
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/stdlib/tsqlitebindatas.nim
diff --git a/tests/stdlib/tsqlparser.nim b/tests/stdlib/tsqlparser.nim
index 4a7b2f7d7..6f123f21d 100644
--- a/tests/stdlib/tsqlparser.nim
+++ b/tests/stdlib/tsqlparser.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   output: '''true'''
 """
 
@@ -6,7 +7,7 @@ discard """
 
 import parsesql, streams, os
 
-var tree = parseSql(newFileStream(getAppDir() / "somesql.sql"), "somesql")
+var tree = parseSql(newFileStream(parentDir(currentSourcePath) / "somesql.sql"), "somesql")
 discard renderSql(tree)
 
 echo "true"
diff --git a/tests/stdlib/tssl.nim b/tests/stdlib/tssl.nim
new file mode 100644
index 000000000..1628b9326
--- /dev/null
+++ b/tests/stdlib/tssl.nim
@@ -0,0 +1,138 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  joinable: false
+  disabled: "freebsd" # see #15713
+  disabled: "openbsd" # see #15713
+  disabled: "netbsd" # see #15713
+"""
+
+import std/[net, nativesockets, assertions, typedthreads]
+
+when defined(posix): import os, posix
+else:
+  import winlean
+  const SD_SEND = 1
+
+when not defined(ssl):
+  {.error: "This test must be compiled with -d:ssl".}
+
+const DummyData = "dummy data\n"
+
+proc abruptShutdown(port: Port) {.thread.} =
+  let clientContext = newContext(verifyMode = CVerifyNone)
+  var client = newSocket(buffered = false)
+  clientContext.wrapSocket(client)
+  client.connect("localhost", port)
+
+  discard client.recvLine()
+  client.getFd.close()
+
+proc notifiedShutdown(port: Port) {.thread.} =
+  let clientContext = newContext(verifyMode = CVerifyNone)
+  var client = newSocket(buffered = false)
+  clientContext.wrapSocket(client)
+  client.connect("localhost", port)
+
+  discard client.recvLine()
+  client.close()
+
+proc main() =
+  when defined(posix):
+    var
+      ignoreAction = Sigaction(sa_handler: SIG_IGN)
+      oldSigPipeHandler: Sigaction
+    if sigemptyset(ignoreAction.sa_mask) == -1:
+      raiseOSError(osLastError(), "Couldn't create an empty signal set")
+    if sigaction(SIGPIPE, ignoreAction, oldSigPipeHandler) == -1:
+      raiseOSError(osLastError(), "Couldn't ignore SIGPIPE")
+
+  let serverContext = newContext(verifyMode = CVerifyNone,
+                                 certFile = "tests/testdata/mycert.pem",
+                                 keyFile = "tests/testdata/mycert.pem")
+
+  block peer_close_during_write_without_shutdown:
+    var server = newSocket(buffered = false)
+    defer: server.close()
+    serverContext.wrapSocket(server)
+    server.bindAddr(address = "localhost")
+    let (_, port) = server.getLocalAddr()
+    server.listen()
+
+    var clientThread: Thread[Port]
+    createThread(clientThread, abruptShutdown, port)
+
+    var peer: Socket
+    try:
+      server.accept(peer)
+      peer.send(DummyData)
+
+      joinThread clientThread
+
+      while true:
+        # Send data until we get EPIPE.
+        peer.send(DummyData, {})
+    except OSError:
+      discard
+    finally:
+      peer.close()
+
+  when defined(posix):
+    if sigaction(SIGPIPE, oldSigPipeHandler, nil) == -1:
+      raiseOSError(osLastError(), "Couldn't restore SIGPIPE handler")
+
+  block peer_close_before_received_shutdown:
+    var server = newSocket(buffered = false)
+    defer: server.close()
+    serverContext.wrapSocket(server)
+    server.bindAddr(address = "localhost")
+    let (_, port) = server.getLocalAddr()
+    server.listen()
+
+    var clientThread: Thread[Port]
+    createThread(clientThread, abruptShutdown, port)
+
+    var peer: Socket
+    try:
+      server.accept(peer)
+      peer.send(DummyData)
+
+      joinThread clientThread
+
+      # Tell the OS to close off the write side so shutdown attempts will
+      # be met with SIGPIPE.
+      when defined(posix):
+        discard peer.getFd.shutdown(SHUT_WR)
+      else:
+        discard peer.getFd.shutdown(SD_SEND)
+    finally:
+      peer.close()
+
+  block peer_close_after_received_shutdown:
+    var server = newSocket(buffered = false)
+    defer: server.close()
+    serverContext.wrapSocket(server)
+    server.bindAddr(address = "localhost")
+    let (_, port) = server.getLocalAddr()
+    server.listen()
+
+    var clientThread: Thread[Port]
+    createThread(clientThread, notifiedShutdown, port)
+
+    var peer: Socket
+    try:
+      server.accept(peer)
+      peer.send(DummyData)
+
+      doAssert peer.recv(1024) == "" # Get the shutdown notification
+      joinThread clientThread
+
+      # Tell the OS to close off the write side so shutdown attempts will
+      # be met with SIGPIPE.
+      when defined(posix):
+        discard peer.getFd.shutdown(SHUT_WR)
+      else:
+        discard peer.getFd.shutdown(SD_SEND)
+    finally:
+      peer.close()
+
+when isMainModule: main()
diff --git a/tests/stdlib/tssl.nims b/tests/stdlib/tssl.nims
new file mode 100644
index 000000000..4739e7f07
--- /dev/null
+++ b/tests/stdlib/tssl.nims
@@ -0,0 +1,5 @@
+--threads:on
+--d:ssl
+when defined(freebsd) or defined(netbsd):
+  # See https://github.com/nim-lang/Nim/pull/15066#issuecomment-665541265 and https://github.com/nim-lang/Nim/issues/15493
+  --tlsEmulation:off
diff --git a/tests/stdlib/tstackframes.nim b/tests/stdlib/tstackframes.nim
new file mode 100644
index 000000000..b0f05d51d
--- /dev/null
+++ b/tests/stdlib/tstackframes.nim
@@ -0,0 +1,34 @@
+import std/[strformat,os,osproc,assertions]
+import stdtest/unittest_light
+
+proc main(opt: string, expected: string) =
+  const nim = getCurrentCompilerExe()
+  const file = currentSourcePath().parentDir / "mstackframes.nim"
+  let cmd = fmt"{nim} c -r --excessiveStackTrace:off --stacktraceMsgs:{opt} --hints:off {file}"
+  let (output, exitCode) = execCmdEx(cmd)
+  assertEquals output, expected
+  doAssert exitCode == 0
+
+main("on"): """
+mstackframes.nim(38)     mstackframes
+mstackframes.nim(29)     main
+  z: 0
+  z: 1
+mstackframes.nim(20)     main2 ("main2", 5, 1)
+mstackframes.nim(20)     main2 ("main2", 4, 2)
+mstackframes.nim(20)     main2 ("main2", 3, 3)
+mstackframes.nim(19)     main2 ("main2", 2, 4)
+mstackframes.nim(18)     bar ("bar ",)
+
+"""
+
+main("off"): """
+mstackframes.nim(38)     mstackframes
+mstackframes.nim(29)     main
+mstackframes.nim(20)     main2
+mstackframes.nim(20)     main2
+mstackframes.nim(20)     main2
+mstackframes.nim(19)     main2
+mstackframes.nim(18)     bar
+
+"""
diff --git a/tests/stdlib/tstaticos.nim b/tests/stdlib/tstaticos.nim
new file mode 100644
index 000000000..41ab995dd
--- /dev/null
+++ b/tests/stdlib/tstaticos.nim
@@ -0,0 +1,8 @@
+import std/[assertions, staticos, os]
+
+block:
+  static:
+    doAssert staticDirExists("MISSINGFILE") == false
+    doAssert staticFileExists("MISSINGDIR") == false
+    doAssert staticDirExists(currentSourcePath().parentDir)
+    doAssert staticFileExists(currentSourcePath())
diff --git a/tests/stdlib/tstats.nim b/tests/stdlib/tstats.nim
new file mode 100644
index 000000000..728d93d09
--- /dev/null
+++ b/tests/stdlib/tstats.nim
@@ -0,0 +1,61 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/[stats, assertions]
+import std/math
+
+
+func `~=`(x, y: float32): bool =
+  math.almostEqual(x, y)
+
+template main() =
+  var rs: RunningStat
+  rs.push(@[1.0, 2.0, 1.0, 4.0, 1.0, 4.0, 1.0, 2.0])
+  doAssert(rs.n == 8)
+  doAssert rs.mean ~= 2.0
+  doAssert rs.variance() ~= 1.5
+  doAssert rs.varianceS() ~= 1.71428571
+  doAssert rs.skewness() ~= 0.81649658
+  doAssert rs.skewnessS() ~= 1.01835015
+  doAssert rs.kurtosis() ~= -1.0
+  doAssert rs.kurtosisS() ~= -0.7000000000000001
+
+  var rs1, rs2: RunningStat
+  rs1.push(@[1.0, 2.0, 1.0, 4.0])
+  rs2.push(@[1.0, 4.0, 1.0, 2.0])
+  let rs3 = rs1 + rs2
+  doAssert rs3.variance ~= rs.variance
+  doAssert rs3.skewness ~= rs.skewness
+  doAssert rs3.kurtosis ~= rs.kurtosis
+  rs1 += rs2
+  doAssert rs1.variance ~= rs.variance
+  doAssert rs1.skewness ~= rs.skewness
+  doAssert rs1.kurtosis ~= rs.kurtosis
+  rs1.clear()
+  rs1.push(@[1.0, 2.2, 1.4, 4.9])
+  doAssert rs1.sum ~= 9.5
+  doAssert rs1.mean() ~= 2.375
+
+  when not defined(cpu32):
+    # XXX For some reason on 32bit CPUs these results differ
+    var rr: RunningRegress
+    rr.push(@[0.0, 1.0, 2.8, 3.0, 4.0], @[0.0, 1.0, 2.3, 3.0, 4.0])
+    doAssert rr.slope() ~= 0.9695585996955861
+    doAssert rr.intercept() ~= -0.03424657534246611
+    doAssert rr.correlation() ~= 0.9905100362239381
+    var rr1, rr2: RunningRegress
+    rr1.push(@[0.0, 1.0], @[0.0, 1.0])
+    rr2.push(@[2.8, 3.0, 4.0], @[2.3, 3.0, 4.0])
+    let rr3 = rr1 + rr2
+    doAssert rr3.correlation() ~= rr.correlation()
+    doAssert rr3.slope() ~= rr.slope()
+    doAssert rr3.intercept() ~= rr.intercept()
+
+  block: # bug #18718
+    var rs: RunningStat
+    rs.push(-1.0)
+    doAssert rs.max == -1.0
+
+static: main()
+main()
diff --git a/tests/stdlib/tstdlib_issues.nim b/tests/stdlib/tstdlib_issues.nim
new file mode 100644
index 000000000..b7b806db8
--- /dev/null
+++ b/tests/stdlib/tstdlib_issues.nim
@@ -0,0 +1,110 @@
+discard """
+matrix: "--mm:refc; --mm:orc"
+output: '''
+02
+1
+2
+3
+4
+5
+9
+b = true
+123456789
+Second readLine raised an exception
+123456789
+1
+2aaaaaaaa
+3bbbbbbb
+'''
+"""
+
+import std/[terminal, colors, re, encodings, strutils, os, assertions, syncio]
+
+
+block t9394:
+  let codeFg = ansiForegroundColorCode(colAliceBlue)
+  let codeBg = ansiBackgroundColorCode(colAliceBlue)
+
+  doAssert codeFg == "\27[38;2;240;248;255m"
+  doAssert codeBg == "\27[48;2;240;248;255m"
+
+
+
+block t5382:
+  let regexp = re"^\/([0-9]{2})\.html$"
+  var matches: array[1, string]
+  discard "/02.html".find(regexp, matches)
+  echo matches[0]
+
+
+
+block tcount:
+  # bug #1845, #2224
+  var arr = [3,2,1,5,4]
+
+  # bubble sort
+  for i in low(arr)..high(arr):
+    for j in i+1..high(arr): # Error: unhandled exception: value out of range: 5 [RangeDefect]
+      if arr[i] > arr[j]:
+        let tmp = arr[i]
+        arr[i] = arr[j]
+        arr[j] = tmp
+
+  for i in low(arr)..high(arr):
+    echo arr[i]
+
+  # check this terminates:
+  for x in countdown('\255', '\0'):
+    discard
+
+
+
+block t8468:
+  when defined(windows):
+    var utf16to8 = open(destEncoding = "utf-16", srcEncoding = "utf-8")
+    var s = "some string"
+    var c = utf16to8.convert(s)
+
+    var z = newStringOfCap(s.len * 2)
+    for x in s:
+      z.add x
+      z.add chr(0)
+
+    doAssert z == c
+
+
+
+block t5349:
+  const fn = "file9char.txt"
+  writeFile(fn, "123456789")
+
+  var f = syncio.open(fn)
+  echo getFileSize(f)
+
+  var line = newString(10)
+  try:
+    let b = readLine(f, line)
+    echo "b = ", b
+  except:
+    echo "First readLine raised an exception"
+  echo line
+
+  try:
+    line = readLine(f)
+    let b = readLine(f, line)
+    echo "b = ", b
+  except:
+    echo "Second readLine raised an exception"
+  echo line
+  f.close()
+
+  removeFile(fn)
+  # bug #8961
+  writeFile("test.txt", "1\C\L2aaaaaaaa\C\L3bbbbbbb")
+
+  for line in lines("test.txt"):
+    echo line
+
+block t9456:
+  var f: File
+  f.close()
diff --git a/tests/stdlib/tstdlib_various.nim b/tests/stdlib/tstdlib_various.nim
new file mode 100644
index 000000000..bac5018fa
--- /dev/null
+++ b/tests/stdlib/tstdlib_various.nim
@@ -0,0 +1,240 @@
+discard """
+matrix: "--mm:refc"
+output: '''
+abc
+def
+definition
+prefix
+xyz
+def
+definition
+Hi Andreas! How do you feel, Rumpf?
+
+@[0, 2, 1]
+@[1, 0, 2]
+@[1, 2, 0]
+@[2, 0, 1]
+@[2, 1, 0]
+@[2, 0, 1]
+@[1, 2, 0]
+@[1, 0, 2]
+@[0, 2, 1]
+@[0, 1, 2]
+055this should be the casehugh@["(", "+", " 1", " 2", ")"]
+[5]
+[4, 5]
+[3, 4, 5]
+[2, 3, 4, 5]
+[2, 3, 4, 5, 6]
+[1, 2, 3, 4, 5, 6]
+<h1><a href="http://force7.de/nim">Nim</a></h1>
+'''
+"""
+
+import
+  std/[critbits, sets, strutils, tables, random, algorithm, re, ropes,
+  segfaults, lists, parsesql, streams, os, htmlgen, xmltree, strtabs]
+import std/[syncio, assertions]
+
+block tcritbits:
+  var r: CritBitTree[void]
+  r.incl "abc"
+  r.incl "xyz"
+  r.incl "def"
+  r.incl "definition"
+  r.incl "prefix"
+  doAssert r.contains"def"
+  #r.del "def"
+
+  for w in r.items:
+    echo w
+  for w in r.itemsWithPrefix("de"):
+    echo w
+
+
+
+block testequivalence:
+  doAssert(toHashSet(@[1,2,3]) <= toHashSet(@[1,2,3,4]), "equivalent or subset")
+  doAssert(toHashSet(@[1,2,3]) <= toHashSet(@[1,2,3]), "equivalent or subset")
+  doAssert((not(toHashSet(@[1,2,3]) <= toHashSet(@[1,2]))), "equivalent or subset")
+  doAssert(toHashSet(@[1,2,3]) <= toHashSet(@[1,2,3,4]), "strict subset")
+  doAssert((not(toHashSet(@[1,2,3]) < toHashSet(@[1,2,3]))), "strict subset")
+  doAssert((not(toHashSet(@[1,2,3]) < toHashSet(@[1,2]))), "strict subset")
+  doAssert((not(toHashSet(@[1,2,3]) == toHashSet(@[1,2,3,4]))), "==")
+  doAssert(toHashSet(@[1,2,3]) == toHashSet(@[1,2,3]), "==")
+  doAssert((not(toHashSet(@[1,2,3]) == toHashSet(@[1,2]))), "==")
+
+
+
+block tformat:
+  echo("Hi $1! How do you feel, $2?\n" % ["Andreas", "Rumpf"])
+
+
+
+block tnilecho:
+  var x = @["1", "", "3"]
+  doAssert $x == """@["1", "", "3"]"""
+
+
+
+block torderedtable:
+  var t = initOrderedTable[int,string]()
+
+  # this tests issue #5917
+  var data = newSeq[int]()
+  for i in 0..<1000:
+    var x = rand(1000)
+    if x notin t: data.add(x)
+    t[x] = "meh"
+
+  # this checks that keys are re-inserted
+  # in order when table is enlarged.
+  var i = 0
+  for k, v in t:
+    doAssert(k == data[i])
+    doAssert(v == "meh")
+    inc(i)
+
+
+
+block tpermutations:
+  var v = @[0, 1, 2]
+  while v.nextPermutation():
+    echo v
+  while v.prevPermutation():
+    echo v
+
+
+
+block treguse:
+  proc main(a, b: int) =
+    var x = 0
+    write(stdout, x)
+    if x == 0:
+      var y = 55
+      write(stdout, y)
+      write(stdout, "this should be the case")
+      var input = "<no input>"
+      if input == "Andreas":
+        write(stdout, "wow")
+      else:
+        write(stdout, "hugh")
+    else:
+      var z = 66
+      write(stdout, z) # "bug!")
+
+  main(45, 1000)
+
+
+
+block treloop:
+  let str = "(+ 1 2)"
+  var tokenRE = re"""[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)"""
+  echo str.findAll(tokenRE)
+
+
+
+block tropes:
+  var
+    r1 = rope("")
+    r2 = rope("123")
+  doAssert r1.len == 0
+  doAssert r2.len == 3
+  doAssert $r1 == ""
+  doAssert $r2 == "123"
+
+  r1.add("123")
+  r2.add("456")
+  doAssert r1.len == 3
+  doAssert r2.len == 6
+  doAssert $r1 == "123"
+  doAssert $r2 == "123456"
+  doAssert $r1[1] == "2"
+  doAssert $r2[2] == "3"
+
+
+
+block tsegfaults:
+  when not defined(arm64):
+    var crashes = 0
+    proc main =
+      try:
+        var x: ptr int
+        echo x[]
+        try:
+          raise newException(ValueError, "not a crash")
+        except ValueError:
+          discard
+      except NilAccessDefect:
+        inc crashes
+    for i in 0..5:
+      main()
+    assert crashes == 6
+
+
+
+block tsinglylinkedring:
+  var r = initSinglyLinkedRing[int]()
+  r.prepend(5)
+  echo r
+  r.prepend(4)
+  echo r
+  r.prepend(3)
+  echo r
+  r.prepend(2)
+  echo r
+  r.append(6)
+  echo r
+  r.prepend(1)
+  echo r
+
+
+
+block tsplit:
+  var s = ""
+  for w in split("|abc|xy|z", {'|'}):
+    s.add("#")
+    s.add(w)
+
+  doAssert s == "##abc#xy#z"
+
+
+
+block tsplit2:
+  var s = ""
+  for w in split("|abc|xy|z", {'|'}):
+    s.add("#")
+    s.add(w)
+
+  doAssert "true".split("") == @["true"]
+
+
+
+block tsqlparser:
+  # Just check that we can parse 'somesql' and render it without crashes.
+  var tree = parseSql(newFileStream( parentDir(currentSourcePath) / "somesql.sql"), "somesql")
+  discard renderSql(tree)
+
+
+
+block txmlgen:
+  var nim = "Nim"
+  echo h1(a(href="http://force7.de/nim", nim))
+
+
+
+block txmltree:
+  var x = <>a(href="nim.de", newText("www.nim-test.de"))
+
+  doAssert($x == "<a href=\"nim.de\">www.nim-test.de</a>")
+  doAssert(newText("foo").innerText == "foo")
+  doAssert(newEntity("bar").innerText == "bar")
+  doAssert(newComment("baz").innerText == "")
+
+  let y = newXmlTree("x", [
+    newText("foo"),
+    newXmlTree("y", [
+      newText("bar")
+    ])
+  ])
+  doAssert(y.innerText == "foobar")
diff --git a/tests/stdlib/tstrbasics.nim b/tests/stdlib/tstrbasics.nim
new file mode 100644
index 000000000..a965ff15f
--- /dev/null
+++ b/tests/stdlib/tstrbasics.nim
@@ -0,0 +1,103 @@
+discard """
+  targets: "c cpp js"
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/[strbasics, sugar, assertions]
+
+template strip2(input: string, args: varargs[untyped]): untyped =
+  var a = input
+  when varargsLen(args) > 0:
+    strip(a, args)
+  else:
+    strip(a)
+  a
+
+proc main() =
+  block: # strip
+    block: # bug #17173
+      var a = "  vhellov   "
+      strip(a)
+      doAssert a == "vhellov"
+
+    doAssert strip2("  vhellov   ") == "vhellov"
+    doAssert strip2("  vhellov   ", leading = false) == "  vhellov"
+    doAssert strip2("  vhellov   ", trailing = false) == "vhellov   "
+    doAssert strip2("vhellov", chars = {'v'}) == "hello"
+    doAssert strip2("vhellov", leading = false, chars = {'v'}) == "vhello"
+    doAssert strip2("blaXbla", chars = {'b', 'a'}) == "laXbl"
+    doAssert strip2("blaXbla", chars = {'b', 'a', 'l'}) == "X"
+    doAssert strip2("xxxxxx", chars={'x'}) == ""
+    doAssert strip2("x", chars={'x'}) == ""
+    doAssert strip2("x", chars={'1'}) == "x"
+    doAssert strip2("", chars={'x'}) == ""
+    doAssert strip2("xxx xxx", chars={'x'}) == " "
+    doAssert strip2("xxx  wind", chars={'x'}) == "  wind"
+    doAssert strip2("xxx  iii", chars={'i'}) == "xxx  "
+
+    block:
+      var a = "xxx  iii"
+      doAssert a.dup(strip(chars = {'i'})) == "xxx  "
+      doAssert a.dup(strip(chars = {' '})) == "xxx  iii"
+      doAssert a.dup(strip(chars = {'x'})) == "  iii"
+      doAssert a.dup(strip(chars = {'x', ' '})) == "iii"
+      doAssert a.dup(strip(chars = {'x', 'i'})) == "  "
+      doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0
+
+    block:
+      var a = "x  i"
+      doAssert a.dup(strip(chars = {'i'})) == "x  "
+      doAssert a.dup(strip(chars = {' '})) == "x  i"
+      doAssert a.dup(strip(chars = {'x'})) == "  i"
+      doAssert a.dup(strip(chars = {'x', ' '})) == "i"
+      doAssert a.dup(strip(chars = {'x', 'i'})) == "  "
+      doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0
+
+    block:
+      var a = ""
+      doAssert a.dup(strip(chars = {'i'})).len == 0
+      doAssert a.dup(strip(chars = {' '})).len == 0
+      doAssert a.dup(strip(chars = {'x'})).len == 0
+      doAssert a.dup(strip(chars = {'x', ' '})).len == 0
+      doAssert a.dup(strip(chars = {'x', 'i'})).len == 0
+      doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0
+
+    block:
+      var a = " "
+      doAssert a.dup(strip(chars = {'i'})) == " "
+      doAssert a.dup(strip(chars = {' '})).len == 0
+      doAssert a.dup(strip(chars = {'x'})) == " "
+      doAssert a.dup(strip(chars = {'x', ' '})).len == 0
+      doAssert a.dup(strip(chars = {'x', 'i'})) == " "
+      doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0
+
+  block: # setSlice
+    var a = "Hello, Nim!"
+    doAssert a.dup(setSlice(7 .. 9)) == "Nim"
+    doAssert a.dup(setSlice(0 .. 0)) == "H"
+    doAssert a.dup(setSlice(0 .. 1)) == "He"
+    doAssert a.dup(setSlice(0 .. 10)) == a
+    doAssert a.dup(setSlice(1 .. 0)).len == 0
+    doAssert a.dup(setSlice(20 .. -1)).len == 0
+
+    doAssertRaises(AssertionDefect):
+      discard a.dup(setSlice(-1 .. 1))
+
+    doAssertRaises(AssertionDefect):
+      discard a.dup(setSlice(1 .. 11))
+
+  block: # add
+    var a0 = "hi"
+    var b0 = "foobar"
+    when nimvm:
+      discard # pending bug #15952
+    else:
+      a0.add b0.toOpenArray(1,3)
+      doAssert a0 == "hioob"
+    proc fn(c: openArray[char]): string =
+      result.add c
+    doAssert fn("def") == "def"
+    doAssert fn(['d','\0', 'f'])[2] == 'f'
+
+static: main()
+main()
diff --git a/tests/stdlib/tstreams.nim b/tests/stdlib/tstreams.nim
index 640565a27..60c63b450 100644
--- a/tests/stdlib/tstreams.nim
+++ b/tests/stdlib/tstreams.nim
@@ -1,7 +1,107 @@
-import streams
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  input: "Arne"
+  output: '''
+Hello! What is your name?
+Nice name: Arne
+fs is: nil
+threw exception
+_heh_
+'''
+  nimout: '''
+I
+AM
+GROOT
+'''
+"""
 
-var outp = newFileStream(stdout)
-var inp = newFileStream(stdin)
-write(outp, "Hello! What is your name?")
-var line = readLine(inp)
-write(outp, "Nice name: " & line)
+
+import std/[syncio, streams, assertions]
+
+
+block tstreams:
+  var outp = newFileStream(stdout)
+  var inp = newFileStream(stdin)
+  writeLine(outp, "Hello! What is your name?")
+  var line = readLine(inp)
+  writeLine(outp, "Nice name: " & line)
+
+
+block tstreams2:
+  var
+    fs = newFileStream("amissingfile.txt")
+    line = ""
+  echo "fs is: ",repr(fs)
+  if not isNil(fs):
+    while fs.readLine(line):
+      echo line
+    fs.close()
+
+
+block tstreams3:
+  try:
+    var fs = openFileStream("shouldneverexist.txt")
+  except IOError:
+    echo "threw exception"
+
+  static:
+    var s = newStringStream("I\nAM\nGROOT")
+    for line in s.lines:
+      echo line
+    s.close
+
+
+block:
+  let fs = newFileStream("amissingfile.txt")
+  defer: fs.close()
+  doAssert isNil(fs)
+
+# bug #12410
+
+var a = newStringStream "hehohihahuhyh"
+a.readDataStrImpl = nil
+
+var buffer = "_ooo_"
+
+doAssert a.readDataStr(buffer, 1..3) == 3
+
+echo buffer
+
+
+block:
+  var ss = newStringStream("The quick brown fox jumped over the lazy dog.\nThe lazy dog ran")
+  doAssert(ss.getPosition == 0)
+  doAssert(ss.peekStr(5) == "The q")
+  doAssert(ss.getPosition == 0) # haven't moved
+  doAssert(ss.readStr(5) == "The q")
+  doAssert(ss.getPosition == 5) # did move
+  doAssert(ss.peekLine() == "uick brown fox jumped over the lazy dog.")
+  doAssert(ss.getPosition == 5) # haven't moved
+  var str = newString(100)
+  doAssert(ss.peekLine(str))
+  doAssert(str == "uick brown fox jumped over the lazy dog.")
+  doAssert(ss.getPosition == 5) # haven't moved
+  # bug #19707 - Ensure we dont error with writing over literals on arc/orc
+  ss.setPosition(0)
+  ss.write("hello")
+  ss.setPosition(0)
+  doAssert(ss.peekStr(5) == "hello")
+
+# bug #19716
+static: # Ensure streams it doesnt break with nimscript on arc/orc #19716
+  let s = newStringStream("a")
+  doAssert s.data == "a"
+
+static: # issue #24054, readStr
+  var s = newStringStream("foo bar baz")
+  doAssert s.readStr(3) == "foo"
+
+template main =
+  var strm = newStringStream("abcde")
+  var buffer = "12345"
+  doAssert strm.readDataStr(buffer, 0..3) == 4
+  doAssert buffer == "abcd5"
+  strm.close()
+
+static: main()
+main()
diff --git a/tests/stdlib/tstreams2.nim b/tests/stdlib/tstreams2.nim
deleted file mode 100644
index 90102d8e3..000000000
--- a/tests/stdlib/tstreams2.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-discard """
-  file: "tstreams2.nim"
-  output: '''fs is: nil'''
-"""
-import streams
-var
-  fs = newFileStream("amissingfile.txt")
-  line = ""
-echo "fs is: ",repr(fs)
-if not isNil(fs):
-  while fs.readLine(line):
-    echo line
-  fs.close()
diff --git a/tests/stdlib/tstreams3.nim b/tests/stdlib/tstreams3.nim
deleted file mode 100644
index b2c9170e3..000000000
--- a/tests/stdlib/tstreams3.nim
+++ /dev/null
@@ -1,10 +0,0 @@
-discard """
-  file: "tstreams3.nim"
-  output: "threw exception"
-"""
-import streams
-
-try:
-  var fs = openFileStream("shouldneverexist.txt")
-except IoError:
-  echo "threw exception"
diff --git a/tests/stdlib/tstrformat.nim b/tests/stdlib/tstrformat.nim
index db76899d4..ff406f898 100644
--- a/tests/stdlib/tstrformat.nim
+++ b/tests/stdlib/tstrformat.nim
@@ -1,56 +1,590 @@
 discard """
-    action: "run"
+  matrix: "--mm:refc; --mm:orc"
 """
 
-import strformat
-
-type Obj = object
-
-proc `$`(o: Obj): string = "foobar"
-
-var o: Obj
-doAssert fmt"{o}" == "foobar"
-doAssert fmt"{o:10}" == "foobar    "
-
-# see issue #7933
-var str = "abc"
-doAssert fmt">7.1 :: {str:>7.1}" == ">7.1 ::       a"
-doAssert fmt">7.2 :: {str:>7.2}" == ">7.2 ::      ab"
-doAssert fmt">7.3 :: {str:>7.3}" == ">7.3 ::     abc"
-doAssert fmt">7.9 :: {str:>7.9}" == ">7.9 ::     abc"
-doAssert fmt">7.0 :: {str:>7.0}" == ">7.0 ::        "
-doAssert fmt" 7.1 :: {str:7.1}" == " 7.1 :: a      "
-doAssert fmt" 7.2 :: {str:7.2}" == " 7.2 :: ab     "
-doAssert fmt" 7.3 :: {str:7.3}" == " 7.3 :: abc    "
-doAssert fmt" 7.9 :: {str:7.9}" == " 7.9 :: abc    "
-doAssert fmt" 7.0 :: {str:7.0}" == " 7.0 ::        "
-doAssert fmt"^7.1 :: {str:^7.1}" == "^7.1 ::    a   "
-doAssert fmt"^7.2 :: {str:^7.2}" == "^7.2 ::   ab   "
-doAssert fmt"^7.3 :: {str:^7.3}" == "^7.3 ::   abc  "
-doAssert fmt"^7.9 :: {str:^7.9}" == "^7.9 ::   abc  "
-doAssert fmt"^7.0 :: {str:^7.0}" == "^7.0 ::        "
-str = "äöüe\u0309\u0319o\u0307\u0359"
-doAssert fmt"^7.1 :: {str:^7.1}" == "^7.1 ::    ä   "
-doAssert fmt"^7.2 :: {str:^7.2}" == "^7.2 ::   äö   "
-doAssert fmt"^7.3 :: {str:^7.3}" == "^7.3 ::   äöü  "
-doAssert fmt"^7.0 :: {str:^7.0}" == "^7.0 ::        "
-# this is actually wrong, but the unicode module has no support for graphemes
-doAssert fmt"^7.4 :: {str:^7.4}" == "^7.4 ::  äöüe  "
-doAssert fmt"^7.9 :: {str:^7.9}" == "^7.9 :: äöüe\u0309\u0319o\u0307\u0359"
-
-# see issue #7932
-doAssert fmt"{15:08}" == "00000015" # int, works
-doAssert fmt"{1.5:08}" == "000001.5" # float, works
-doAssert fmt"{1.5:0>8}" == "000001.5" # workaround using fill char works for positive floats
-doAssert fmt"{-1.5:0>8}" == "0000-1.5" # even that does not work for negative floats  
-doAssert fmt"{-1.5:08}" == "-00001.5" # works
-doAssert fmt"{1.5:+08}" == "+00001.5" # works
-doAssert fmt"{1.5: 08}" == " 00001.5" # works
-
-# only add explicitly requested sign if value != -0.0 (neg zero)
-doAssert fmt"{-0.0:g}" == "-0"
-doassert fmt"{-0.0:+g}" == "-0"
-doassert fmt"{-0.0: g}" == "-0"
-doAssert fmt"{0.0:g}" == "0"
-doAssert fmt"{0.0:+g}" == "+0"
-doAssert fmt"{0.0: g}" == " 0"
+import genericstrformat
+import std/[strformat, strutils, times, tables, json]
+
+import std/[assertions, formatfloat]
+import std/objectdollar
+
+proc main() =
+  block: # issue #7632
+    doAssert works(5) == "formatted  5"
+    doAssert fails0(6) == "formatted  6"
+    doAssert fails(7) == "formatted  7"
+    doAssert fails2[0](8) == "formatted  8"
+
+  block: # other tests
+    type Obj = object
+
+    proc `$`(o: Obj): string = "foobar"
+
+    # for custom types, formatValue needs to be overloaded.
+    template formatValue(result: var string; value: Obj; specifier: string) =
+      result.formatValue($value, specifier)
+
+    var o: Obj
+    doAssert fmt"{o}" == "foobar"
+    doAssert fmt"{o:10}" == "foobar    "
+
+    doAssert fmt"{o=}" == "o=foobar"
+    doAssert fmt"{o=:10}" == "o=foobar    "
+
+  block: # see issue #7933
+    var str = "abc"
+    doAssert fmt">7.1 :: {str:>7.1}" == ">7.1 ::       a"
+    doAssert fmt">7.2 :: {str:>7.2}" == ">7.2 ::      ab"
+    doAssert fmt">7.3 :: {str:>7.3}" == ">7.3 ::     abc"
+    doAssert fmt">7.9 :: {str:>7.9}" == ">7.9 ::     abc"
+    doAssert fmt">7.0 :: {str:>7.0}" == ">7.0 ::        "
+    doAssert fmt" 7.1 :: {str:7.1}" == " 7.1 :: a      "
+    doAssert fmt" 7.2 :: {str:7.2}" == " 7.2 :: ab     "
+    doAssert fmt" 7.3 :: {str:7.3}" == " 7.3 :: abc    "
+    doAssert fmt" 7.9 :: {str:7.9}" == " 7.9 :: abc    "
+    doAssert fmt" 7.0 :: {str:7.0}" == " 7.0 ::        "
+    doAssert fmt"^7.1 :: {str:^7.1}" == "^7.1 ::    a   "
+    doAssert fmt"^7.2 :: {str:^7.2}" == "^7.2 ::   ab   "
+    doAssert fmt"^7.3 :: {str:^7.3}" == "^7.3 ::   abc  "
+    doAssert fmt"^7.9 :: {str:^7.9}" == "^7.9 ::   abc  "
+    doAssert fmt"^7.0 :: {str:^7.0}" == "^7.0 ::        "
+
+    doAssert fmt">7.1 :: {str=:>7.1}" == ">7.1 :: str=      a"
+    doAssert fmt">7.2 :: {str=:>7.2}" == ">7.2 :: str=     ab"
+    doAssert fmt">7.3 :: {str=:>7.3}" == ">7.3 :: str=    abc"
+    doAssert fmt">7.9 :: {str=:>7.9}" == ">7.9 :: str=    abc"
+    doAssert fmt">7.0 :: {str=:>7.0}" == ">7.0 :: str=       "
+    doAssert fmt" 7.1 :: {str=:7.1}" == " 7.1 :: str=a      "
+    doAssert fmt" 7.2 :: {str=:7.2}" == " 7.2 :: str=ab     "
+    doAssert fmt" 7.3 :: {str=:7.3}" == " 7.3 :: str=abc    "
+    doAssert fmt" 7.9 :: {str=:7.9}" == " 7.9 :: str=abc    "
+    doAssert fmt" 7.0 :: {str=:7.0}" == " 7.0 :: str=       "
+    doAssert fmt"^7.1 :: {str=:^7.1}" == "^7.1 :: str=   a   "
+    doAssert fmt"^7.2 :: {str=:^7.2}" == "^7.2 :: str=  ab   "
+    doAssert fmt"^7.3 :: {str=:^7.3}" == "^7.3 :: str=  abc  "
+    doAssert fmt"^7.9 :: {str=:^7.9}" == "^7.9 :: str=  abc  "
+    doAssert fmt"^7.0 :: {str=:^7.0}" == "^7.0 :: str=       "
+    str = "äöüe\u0309\u0319o\u0307\u0359"
+    doAssert fmt"^7.1 :: {str:^7.1}" == "^7.1 ::    ä   "
+    doAssert fmt"^7.2 :: {str:^7.2}" == "^7.2 ::   äö   "
+    doAssert fmt"^7.3 :: {str:^7.3}" == "^7.3 ::   äöü  "
+    doAssert fmt"^7.0 :: {str:^7.0}" == "^7.0 ::        "
+
+    doAssert fmt"^7.1 :: {str=:^7.1}" == "^7.1 :: str=   ä   "
+    doAssert fmt"^7.2 :: {str=:^7.2}" == "^7.2 :: str=  äö   "
+    doAssert fmt"^7.3 :: {str=:^7.3}" == "^7.3 :: str=  äöü  "
+    doAssert fmt"^7.0 :: {str=:^7.0}" == "^7.0 :: str=       "
+    # this is actually wrong, but the unicode module has no support for graphemes
+    doAssert fmt"^7.4 :: {str:^7.4}" == "^7.4 ::  äöüe  "
+    doAssert fmt"^7.9 :: {str:^7.9}" == "^7.9 :: äöüe\u0309\u0319o\u0307\u0359"
+
+    doAssert fmt"^7.4 :: {str=:^7.4}" == "^7.4 :: str= äöüe  "
+    doAssert fmt"^7.9 :: {str=:^7.9}" == "^7.9 :: str=äöüe\u0309\u0319o\u0307\u0359"
+
+  block: # see issue #7932
+    doAssert fmt"{15:08}" == "00000015" # int, works
+    doAssert fmt"{1.5:08}" == "000001.5" # float, works
+    doAssert fmt"{1.5:0>8}" == "000001.5" # workaround using fill char works for positive floats
+    doAssert fmt"{-1.5:0>8}" == "0000-1.5" # even that does not work for negative floats
+    doAssert fmt"{-1.5:08}" == "-00001.5" # works
+    doAssert fmt"{1.5:+08}" == "+00001.5" # works
+    doAssert fmt"{1.5: 08}" == " 00001.5" # works
+
+    doAssert fmt"{15=:08}" == "15=00000015" # int, works
+    doAssert fmt"{1.5=:08}" == "1.5=000001.5" # float, works
+    doAssert fmt"{1.5=:0>8}" == "1.5=000001.5" # workaround using fill char works for positive floats
+    doAssert fmt"{-1.5=:0>8}" == "-1.5=0000-1.5" # even that does not work for negative floats
+    doAssert fmt"{-1.5=:08}" == "-1.5=-00001.5" # works
+    doAssert fmt"{1.5=:+08}" == "1.5=+00001.5" # works
+    doAssert fmt"{1.5=: 08}" == "1.5= 00001.5" # works
+
+  block: # only add explicitly requested sign if value != -0.0 (neg zero)
+    doAssert fmt"{-0.0:g}" == "-0"
+    doAssert fmt"{-0.0:+g}" == "-0"
+    doAssert fmt"{-0.0: g}" == "-0"
+    doAssert fmt"{0.0:g}" == "0"
+    doAssert fmt"{0.0:+g}" == "+0"
+    doAssert fmt"{0.0: g}" == " 0"
+
+    doAssert fmt"{-0.0=:g}" == "-0.0=-0"
+    doAssert fmt"{-0.0=:+g}" == "-0.0=-0"
+    doAssert fmt"{-0.0=: g}" == "-0.0=-0"
+    doAssert fmt"{0.0=:g}" == "0.0=0"
+    doAssert fmt"{0.0=:+g}" == "0.0=+0"
+    doAssert fmt"{0.0=: g}" == "0.0= 0"
+
+  block: # seq format
+    let data1 = [1'i64, 10000'i64, 10000000'i64]
+    let data2 = [10000000'i64, 100'i64, 1'i64]
+
+    proc formatValue(result: var string; value: (array|seq|openArray); specifier: string) =
+      result.add "["
+      for i, it in value:
+        if i != 0:
+          result.add ", "
+        result.formatValue(it, specifier)
+      result.add "]"
+
+    doAssert fmt"data1: {data1:8} #" == "data1: [       1,    10000, 10000000] #"
+    doAssert fmt"data2: {data2:8} =" == "data2: [10000000,      100,        1] ="
+
+    doAssert fmt"data1: {data1=:8} #" == "data1: data1=[       1,    10000, 10000000] #"
+    doAssert fmt"data2: {data2=:8} =" == "data2: data2=[10000000,      100,        1] ="
+
+  block: # custom format Value
+    type
+      Vec2[T] = object
+        x,y: T
+
+    proc formatValue[T](result: var string; value: Vec2[T]; specifier: string) =
+      result.add '['
+      result.formatValue value.x, specifier
+      result.add ", "
+      result.formatValue value.y, specifier
+      result.add "]"
+
+    let v1 = Vec2[float32](x:1.0, y: 2.0)
+    let v2 = Vec2[int32](x:1, y: 1337)
+    doAssert fmt"v1: {v1:+08}  v2: {v2:>4}" == "v1: [+0000001, +0000002]  v2: [   1, 1337]"
+    doAssert fmt"v1: {v1=:+08}  v2: {v2=:>4}" == "v1: v1=[+0000001, +0000002]  v2: v2=[   1, 1337]"
+
+  block: # bug #11012
+    type
+      Animal = object
+        name, species: string
+      AnimalRef = ref Animal
+
+    proc print_object(animalAddr: AnimalRef): string =
+      fmt"Received {animalAddr[]}"
+
+    doAssert print_object(AnimalRef(name: "Foo", species: "Bar")) == """Received (name: "Foo", species: "Bar")"""
+
+  block: # bug #11723
+    let pos: Positive = 64
+    doAssert fmt"{pos:3}" == " 64"
+    doAssert fmt"{pos:3b}" == "1000000"
+    doAssert fmt"{pos:3d}" == " 64"
+    doAssert fmt"{pos:3o}" == "100"
+    doAssert fmt"{pos:3x}" == " 40"
+    doAssert fmt"{pos:3X}" == " 40"
+
+    doAssert fmt"{pos=:3}" == "pos= 64"
+    doAssert fmt"{pos=:3b}" == "pos=1000000"
+    doAssert fmt"{pos=:3d}" == "pos= 64"
+    doAssert fmt"{pos=:3o}" == "pos=100"
+    doAssert fmt"{pos=:3x}" == "pos= 40"
+    doAssert fmt"{pos=:3X}" == "pos= 40"
+
+    let nat: Natural = 64
+    doAssert fmt"{nat:3}" == " 64"
+    doAssert fmt"{nat:3b}" == "1000000"
+    doAssert fmt"{nat:3d}" == " 64"
+    doAssert fmt"{nat:3o}" == "100"
+    doAssert fmt"{nat:3x}" == " 40"
+    doAssert fmt"{nat:3X}" == " 40"
+
+    doAssert fmt"{nat=:3}" == "nat= 64"
+    doAssert fmt"{nat=:3b}" == "nat=1000000"
+    doAssert fmt"{nat=:3d}" == "nat= 64"
+    doAssert fmt"{nat=:3o}" == "nat=100"
+    doAssert fmt"{nat=:3x}" == "nat= 40"
+    doAssert fmt"{nat=:3X}" == "nat= 40"
+
+  block: # bug #12612
+    proc my_proc() =
+      const value = "value"
+      const a = &"{value}"
+      doAssert a == value
+
+      const b = &"{value=}"
+      doAssert b == "value=" & value
+
+    my_proc()
+
+  block:
+    template fmt(pattern: string; openCloseChar: char): untyped =
+      fmt(pattern, openCloseChar, openCloseChar)
+
+    let
+      testInt = 123
+      testStr = "foobar"
+      testFlt = 3.141592
+    doAssert ">><<".fmt('<', '>') == "><"
+    doAssert " >> << ".fmt('<', '>') == " > < "
+    doAssert "<<>>".fmt('<', '>') == "<>"
+    doAssert " << >> ".fmt('<', '>') == " < > "
+    doAssert "''".fmt('\'') == "'"
+    doAssert "''''".fmt('\'') == "''"
+    doAssert "'' ''".fmt('\'') == "' '"
+    doAssert "<testInt>".fmt('<', '>') == "123"
+    doAssert "<testInt>".fmt('<', '>') == "123"
+    doAssert "'testFlt:1.2f'".fmt('\'') == "3.14"
+    doAssert "<testInt><testStr>".fmt('<', '>') == "123foobar"
+    doAssert """ ""{"123+123"}"" """.fmt('"') == " \"{246}\" "
+    doAssert "(((testFlt:1.2f)))((111))".fmt('(', ')') == "(3.14)(111)"
+    doAssert """(()"foo" & "bar"())""".fmt(')', '(') == "(foobar)"
+    doAssert "{}abc`testStr' `testFlt:1.2f' `1+1' ``".fmt('`', '\'') == "{}abcfoobar 3.14 2 `"
+    doAssert """x = '"foo" & "bar"'
+                y = '123 + 111'
+                z = '3 in {2..7}'
+             """.fmt('\'') ==
+             """x = foobar
+                y = 234
+                z = true
+             """
+
+  block: # tests from the very own strformat documentation!
+    let msg = "hello"
+    doAssert fmt"{msg}\n" == "hello\\n"
+
+    doAssert &"{msg}\n" == "hello\n"
+
+    doAssert fmt"{msg}{'\n'}" == "hello\n"
+    doAssert fmt("{msg}\n") == "hello\n"
+    doAssert "{msg}\n".fmt == "hello\n"
+
+    doAssert fmt"{msg=}\n" == "msg=hello\\n"
+
+    doAssert &"{msg=}\n" == "msg=hello\n"
+
+    doAssert fmt"{msg=}{'\n'}" == "msg=hello\n"
+    doAssert fmt("{msg=}\n") == "msg=hello\n"
+    doAssert "{msg=}\n".fmt == "msg=hello\n"
+
+    doAssert &"""{"abc":>4}""" == " abc"
+    doAssert &"""{"abc":<4}""" == "abc "
+
+    doAssert fmt"{-12345:08}" == "-0012345"
+    doAssert fmt"{-1:3}" == " -1"
+    doAssert fmt"{-1:03}" == "-01"
+    doAssert fmt"{16:#X}" == "0x10"
+
+    doAssert fmt"{123.456}" == "123.456"
+    doAssert fmt"{123.456:>9.3f}" == "  123.456"
+    doAssert fmt"{123.456:9.3f}" == "  123.456"
+    doAssert fmt"{123.456:9.4f}" == " 123.4560"
+    doAssert fmt"{123.456:>9.0f}" == "     123."
+    doAssert fmt"{123.456:<9.4f}" == "123.4560 "
+
+    doAssert fmt"{123.456:e}" == "1.234560e+02"
+    doAssert fmt"{123.456:>13e}" == " 1.234560e+02"
+    doAssert fmt"{123.456:13e}" == " 1.234560e+02"
+
+    doAssert &"""{"abc"=:>4}""" == "\"abc\"= abc"
+    doAssert &"""{"abc"=:<4}""" == "\"abc\"=abc "
+
+    doAssert fmt"{-12345=:08}" == "-12345=-0012345"
+    doAssert fmt"{-1=:3}" == "-1= -1"
+    doAssert fmt"{-1=:03}" == "-1=-01"
+    doAssert fmt"{16=:#X}" == "16=0x10"
+
+    doAssert fmt"{123.456=}" == "123.456=123.456"
+    doAssert fmt"{123.456=:>9.3f}" == "123.456=  123.456"
+    doAssert fmt"{123.456=:9.3f}" == "123.456=  123.456"
+    doAssert fmt"{123.456=:9.4f}" == "123.456= 123.4560"
+    doAssert fmt"{123.456=:>9.0f}" == "123.456=     123."
+    doAssert fmt"{123.456=:<9.4f}" == "123.456=123.4560 "
+
+    doAssert fmt"{123.456=:e}" == "123.456=1.234560e+02"
+    doAssert fmt"{123.456=:>13e}" == "123.456= 1.234560e+02"
+    doAssert fmt"{123.456=:13e}" == "123.456= 1.234560e+02"
+
+    let x = 3.14
+    doAssert fmt"{(if x!=0: 1.0/x else: 0):.5}" == "0.31847"
+    doAssert fmt"""{(block:
+      var res: string
+      for i in 1..15:
+        res.add (if i mod 15 == 0: "FizzBuzz"
+          elif i mod 5 == 0: "Buzz"
+          elif i mod 3 == 0: "Fizz"
+          else: $i) & " "
+      res)}""" == "1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz "
+
+    doAssert fmt"""{ "\{(" & msg & ")\}" }""" == "{(hello)}"
+    doAssert fmt"""{{({ msg })}}""" == "{(hello)}"
+    doAssert fmt"""{ $(\{msg:1,"world":2\}) }""" == """[("hello", 1), ("world", 2)]"""
+  block: # tests for debug format string
+    var name = "hello"
+    let age = 21
+    const hobby = "swim"
+    doAssert fmt"{age*9 + 16=}" == "age*9 + 16=205"
+    doAssert &"name: {name    =}\nage: {  age  =: >7}\nhobby: {   hobby=  : 8}" ==
+          "name: name    =hello\nage:   age  =     21\nhobby:    hobby=  swim    "
+    doAssert fmt"{age  ==  12}" == "false"
+    doAssert fmt"{name.toUpperAscii() = }" == "name.toUpperAscii() = HELLO"
+    doAssert fmt"{name.toUpperAscii( ) =  }" == "name.toUpperAscii( ) =  HELLO"
+    doAssert fmt"{  toUpperAscii(  s  =  name  )  =   }" == "  toUpperAscii(  s  =  name  )  =   HELLO"
+    doAssert fmt"{  strutils.toUpperAscii(  s  =  name  )  =   }" == "  strutils.toUpperAscii(  s  =  name  )  =   HELLO"
+    doAssert fmt"{age==12}" == "false"
+    doAssert fmt"{age!= 12}" == "true"
+    doAssert fmt"{age  <=  12}" == "false"
+    for i in 1 .. 10:
+      doAssert fmt"{age.float =: .2f}" == "age.float = 21.00"
+    doAssert fmt"{age.float() =:.3f}" == "age.float() =21.000"
+    doAssert fmt"{float age=  :.3f}" == "float age=  21.000"
+    doAssert fmt"{12 == int(`!=`(age, 12))}" == "false"
+    doAssert fmt"{0==1}" == "false"
+
+  block: # It is space sensitive.
+    let x = "12"
+    doAssert fmt"{x=:}" == "x=12"
+    doAssert fmt"{x=}" == "x=12"
+    doAssert fmt"{x =:}" == "x =12"
+    doAssert fmt"{x =}" == "x =12"
+    doAssert fmt"{x= :}" == "x= 12"
+    doAssert fmt"{x= }" == "x= 12"
+    doAssert fmt"{x = :}" == "x = 12"
+    doAssert fmt"{x = }" == "x = 12"
+    doAssert fmt"{x   =  :}" == "x   =  12"
+    doAssert fmt"{x   =  }" == "x   =  12"
+
+  block:
+    let x = "hello"
+    doAssert fmt"{x=}" == "x=hello"
+    doAssert fmt"{x =}" == "x =hello"
+
+    let y = 3.1415926
+    doAssert fmt"{y=:.2f}" == fmt"y={y:.2f}"
+    doAssert fmt"{y=}" == fmt"y={y}"
+    doAssert fmt"{y = : <8}" == fmt"y = 3.14159 "
+
+    proc hello(a: string, b: float): int = 12
+    template foo(a: string, b: float): int = 18
+
+    doAssert fmt"{hello(x, y)=}" == "hello(x, y)=12"
+    doAssert fmt"{hello(x, y) =}" == "hello(x, y) =12"
+    doAssert fmt"{hello(x, y)= }" == "hello(x, y)= 12"
+    doAssert fmt"{hello(x, y) = }" == "hello(x, y) = 12"
+
+    doAssert fmt"{hello x, y=}" == "hello x, y=12"
+    doAssert fmt"{hello x, y =}" == "hello x, y =12"
+    doAssert fmt"{hello x, y= }" == "hello x, y= 12"
+    doAssert fmt"{hello x, y = }" == "hello x, y = 12"
+
+    doAssert fmt"{x.hello(y)=}" == "x.hello(y)=12"
+    doAssert fmt"{x.hello(y) =}" == "x.hello(y) =12"
+    doAssert fmt"{x.hello(y)= }" == "x.hello(y)= 12"
+    doAssert fmt"{x.hello(y) = }" == "x.hello(y) = 12"
+
+    doAssert fmt"{foo(x, y)=}" == "foo(x, y)=18"
+    doAssert fmt"{foo(x, y) =}" == "foo(x, y) =18"
+    doAssert fmt"{foo(x, y)= }" == "foo(x, y)= 18"
+    doAssert fmt"{foo(x, y) = }" == "foo(x, y) = 18"
+
+    doAssert fmt"{x.foo(y)=}" == "x.foo(y)=18"
+    doAssert fmt"{x.foo(y) =}" == "x.foo(y) =18"
+    doAssert fmt"{x.foo(y)= }" == "x.foo(y)= 18"
+    doAssert fmt"{x.foo(y) = }" == "x.foo(y) = 18"
+
+  block:
+    template check(actual, expected: string) =
+      doAssert actual == expected
+
+    # Basic tests
+    let s = "string"
+    check &"{0} {s}", "0 string"
+    check &"{s[0..2].toUpperAscii}", "STR"
+    check &"{-10:04}", "-010"
+    check &"{-10:<04}", "-010"
+    check &"{-10:>04}", "-010"
+    check &"0x{10:02X}", "0x0A"
+
+    check &"{10:#04X}", "0x0A"
+
+    check &"""{"test":#>5}""", "#test"
+    check &"""{"test":>5}""", " test"
+
+    check &"""{"test":#^7}""", "#test##"
+
+    check &"""{"test": <5}""", "test "
+    check &"""{"test":<5}""", "test "
+    check &"{1f:.3f}", "1.000"
+    check &"Hello, {s}!", "Hello, string!"
+
+    # Tests for identifiers without parenthesis
+    check &"{s} works{s}", "string worksstring"
+    check &"{s:>7}", " string"
+    doAssert(not compiles(&"{s_works}")) # parsed as identifier `s_works`
+
+    # Misc general tests
+    check &"{{}}", "{}"
+    check &"{0}%", "0%"
+    check &"{0}%asdf", "0%asdf"
+    check &("\n{\"\\n\"}\n"), "\n\n\n"
+    check &"""{"abc"}s""", "abcs"
+
+    # String tests
+    check &"""{"abc"}""", "abc"
+    check &"""{"abc":>4}""", " abc"
+    check &"""{"abc":<4}""", "abc "
+    check &"""{"":>4}""", "    "
+    check &"""{"":<4}""", "    "
+
+    # Int tests
+    check &"{12345}", "12345"
+    check &"{ - 12345}", "-12345"
+    check &"{12345:6}", " 12345"
+    check &"{12345:>6}", " 12345"
+    check &"{12345:4}", "12345"
+    check &"{12345:08}", "00012345"
+    check &"{-12345:08}", "-0012345"
+    check &"{0:0}", "0"
+    check &"{0:02}", "00"
+    check &"{-1:3}", " -1"
+    check &"{-1:03}", "-01"
+    check &"{10}", "10"
+    check &"{16:#X}", "0x10"
+    check &"{16:^#7X}", " 0x10  "
+    check &"{16:^+#7X}", " +0x10 "
+
+    # Hex tests
+    check &"{0:x}", "0"
+    check &"{-0:x}", "0"
+    check &"{255:x}", "ff"
+    check &"{255:X}", "FF"
+    check &"{-255:x}", "-ff"
+    check &"{-255:X}", "-FF"
+    check &"{255:x} uNaffeCteD CaSe", "ff uNaffeCteD CaSe"
+    check &"{255:X} uNaffeCteD CaSe", "FF uNaffeCteD CaSe"
+    check &"{255:4x}", "  ff"
+    check &"{255:04x}", "00ff"
+    check &"{-255:4x}", " -ff"
+    check &"{-255:04x}", "-0ff"
+
+    # Float tests
+    check &"{123.456}", "123.456"
+    check &"{-123.456}", "-123.456"
+    check &"{123.456:.3f}", "123.456"
+    check &"{123.456:+.3f}", "+123.456"
+    check &"{-123.456:+.3f}", "-123.456"
+    check &"{-123.456:.3f}", "-123.456"
+    check &"{123.456:1g}", "123.456"
+    check &"{123.456:.1f}", "123.5"
+    check &"{123.456:.0f}", "123."
+    check &"{123.456:>9.3f}", "  123.456"
+    check &"{123.456:9.3f}", "  123.456"
+    check &"{123.456:>9.4f}", " 123.4560"
+    check &"{123.456:>9.0f}", "     123."
+    check &"{123.456:<9.4f}", "123.4560 "
+
+    # Float (scientific) tests
+    check &"{123.456:e}", "1.234560e+02"
+    check &"{123.456:>13e}", " 1.234560e+02"
+    check &"{123.456:<13e}", "1.234560e+02 "
+    check &"{123.456:.1e}", "1.2e+02"
+    check &"{123.456:.2e}", "1.23e+02"
+    check &"{123.456:.3e}", "1.235e+02"
+
+    # Note: times.format adheres to the format protocol. Test that this
+    # works:
+    when nimvm:
+      discard
+    else:
+      var dt = dateTime(2000, mJan, 01, 00, 00, 00)
+      check &"{dt:yyyy-MM-dd}", "2000-01-01"
+
+      var tm = fromUnix(0)
+      discard &"{tm}"
+
+      var noww = now()
+      check &"{noww}", $noww
+
+    # Unicode string tests
+    check &"""{"αβγ"}""", "αβγ"
+    check &"""{"αβγ":>5}""", "  αβγ"
+    check &"""{"αβγ":<5}""", "αβγ  "
+    check &"""a{"a"}α{"α"}€{"€"}𐍈{"𐍈"}""", "aaαα€€𐍈𐍈"
+    check &"""a{"a":2}α{"α":2}€{"€":2}𐍈{"𐍈":2}""", "aa αα €€ 𐍈𐍈 "
+    # Invalid unicode sequences should be handled as plain strings.
+    # Invalid examples taken from: https://stackoverflow.com/a/3886015/1804173
+    let invalidUtf8 = [
+      "\xc3\x28", "\xa0\xa1",
+      "\xe2\x28\xa1", "\xe2\x82\x28",
+      "\xf0\x28\x8c\xbc", "\xf0\x90\x28\xbc", "\xf0\x28\x8c\x28"
+    ]
+    for s in invalidUtf8:
+      check &"{s:>5}", repeat(" ", 5-s.len) & s
+
+    # bug #11089
+    let flfoo: float = 1.0
+    check &"{flfoo}", "1.0"
+
+    # bug #11092
+    check &"{high(int64)}", "9223372036854775807"
+    check &"{low(int64)}", "-9223372036854775808"
+
+    doAssert fmt"{'a'} {'b'}" == "a b"
+
+  block: # test low(int64)
+    doAssert &"{low(int64):-}" == "-9223372036854775808"
+  block: #expressions plus formatting
+    doAssert fmt"{if true\: 123.456 else\: 0=:>9.3f}" == "if true: 123.456 else: 0=  123.456"
+    doAssert fmt"{(if true: 123.456 else: 0)=}" == "(if true: 123.456 else: 0)=123.456"
+    doAssert fmt"{if true\: 123.456 else\: 0=:9.3f}" == "if true: 123.456 else: 0=  123.456"
+    doAssert fmt"{(if true: 123.456 else: 0)=:9.4f}" == "(if true: 123.456 else: 0)= 123.4560"
+    doAssert fmt"{(if true: 123.456 else: 0)=:>9.0f}" == "(if true: 123.456 else: 0)=     123."
+    doAssert fmt"{if true\: 123.456 else\: 0=:<9.4f}" == "if true: 123.456 else: 0=123.4560 "
+
+    doAssert fmt"""{(case true
+      of false: 0.0
+      of true: 123.456)=:e}""" == """(case true
+      of false: 0.0
+      of true: 123.456)=1.234560e+02"""
+
+    doAssert fmt"""{block\:
+      var res = 0.000123456
+      for _ in 0..5\:
+        res *= 10
+      res=:>13e}""" == """block:
+      var res = 0.000123456
+      for _ in 0..5:
+        res *= 10
+      res= 1.234560e+02"""
+    #side effects
+    var x = 5
+    doAssert fmt"{(x=7;123.456)=:13e}" == "(x=7;123.456)= 1.234560e+02"
+    doAssert x==7
+  block: #curly bracket expressions and tuples
+    proc formatValue(result: var string; value:Table|bool|JsonNode; specifier:string) = result.add $value
+
+    doAssert fmt"""{\{"a"\:1,"b"\:2\}.toTable() = }""" == """{"a":1,"b":2}.toTable() = {"a": 1, "b": 2}"""
+    doAssert fmt"""{(\{3: (1,"hi",0.9),4: (4,"lo",1.1)\}).toTable()}""" == """{3: (1, "hi", 0.9), 4: (4, "lo", 1.1)}"""
+    doAssert fmt"""{ (%* \{"name": "Isaac", "books": ["Robot Dreams"]\}) }""" == """{"name":"Isaac","books":["Robot Dreams"]}"""
+    doAssert """%( \%\* {"name": "Isaac"})*""".fmt('%','*') == """{"name":"Isaac"}"""
+  block: #parens in quotes that fool my syntax highlighter
+    doAssert fmt"{(if true: ')' else: '(')}" == ")"
+    doAssert fmt"{(if true: ']' else: ')')}" == "]"
+    doAssert fmt"""{(if true: "\")\"" else: "\"(")}""" == """")""""
+    doAssert &"""{(if true: "\")" else: "")}""" == "\")"
+    doAssert &"{(if true: \"\\\")\" else: \"\")}" == "\")"
+    doAssert fmt"""{(if true: "')" else: "")}""" == "')"
+    doAssert fmt"""{(if true: "'" & "'" & ')' else: "")}""" == "'')"
+    doAssert &"""{(if true: "'" & "'" & ')' else: "")}""" == "'')"
+    doAssert &"{(if true: \"\'\" & \"'\" & ')' else: \"\")}" == "'')"
+    doAssert fmt"""{(if true: "'" & ')' else: "")}""" == "')"
+
+  block: # issue #20381
+    var ss: seq[string]
+    template myTemplate(s: string) =
+      ss.add s
+      ss.add s
+    proc foo() =
+      myTemplate fmt"hello"
+    foo()
+    doAssert ss == @["hello", "hello"]
+
+  block:
+    proc noraises() {.raises: [].} =
+      const
+        flt = 0.0
+        str = "str"
+
+      doAssert fmt"{flt} {str}" == "0.0 str"
+
+    noraises()
+
+  block:
+    doAssert not compiles(fmt"{formatting errors detected at compile time")
+
+static: main()
+main()
diff --git a/tests/stdlib/tstrformatlineinfo.nim b/tests/stdlib/tstrformatlineinfo.nim
new file mode 100644
index 000000000..3a7bf0d33
--- /dev/null
+++ b/tests/stdlib/tstrformatlineinfo.nim
@@ -0,0 +1,8 @@
+# issue #21759
+
+{.hint[ConvFromXToItselfNotNeeded]: on.}
+
+import std/strformat
+
+echo fmt"{string ""abc""}" #[tt.Hint
+        ^ conversion from string to itself is pointless]#
diff --git a/tests/stdlib/tstrimpl.nim b/tests/stdlib/tstrimpl.nim
new file mode 100644
index 000000000..a8933e53f
--- /dev/null
+++ b/tests/stdlib/tstrimpl.nim
@@ -0,0 +1,12 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/private/strimpl
+
+import std/assertions
+
+doAssert find(cstring"Hello Nim", cstring"Nim") == 6
+doAssert find(cstring"Hello Nim", cstring"N") == 6
+doAssert find(cstring"Hello Nim", cstring"I") == -1
+doAssert find(cstring"Hello Nim", cstring"O") == -1
diff --git a/tests/stdlib/tstring.nim b/tests/stdlib/tstring.nim
index 660746150..b9b3c78a3 100644
--- a/tests/stdlib/tstring.nim
+++ b/tests/stdlib/tstring.nim
@@ -1,79 +1,124 @@
 discard """
-  file: "tstring.nim"
-  output: "OK"
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c cpp js"
 """
-const characters = "abcdefghijklmnopqrstuvwxyz"
-const numbers = "1234567890"
-
-var s: string
-
-proc test_string_slice() =
-  # test "slice of length == len(characters)":
-  # replace characters completely by numbers
-  s = characters
-  s[0..^1] = numbers
-  doAssert s == numbers
-
-  # test "slice of length > len(numbers)":
-  # replace characters by slice of same length
-  s = characters
-  s[1..16] = numbers
-  doAssert s == "a1234567890rstuvwxyz"
-
-  # test "slice of length == len(numbers)":
-  # replace characters by slice of same length
-  s = characters
-  s[1..10] = numbers
-  doAssert s == "a1234567890lmnopqrstuvwxyz"
-
-  # test "slice of length < len(numbers)":
-  # replace slice of length. and insert remaining chars
-  s = characters
-  s[1..4] = numbers
-  doAssert s == "a1234567890fghijklmnopqrstuvwxyz"
-
-  # test "slice of length == 1":
-  # replace first character. and insert remaining 9 chars
-  s = characters
-  s[1..1] = numbers
-  doAssert s == "a1234567890cdefghijklmnopqrstuvwxyz"
-
-  # test "slice of length == 0":
-  # insert chars at slice start index
-  s = characters
-  s[2..1] = numbers
-  doAssert s == "ab1234567890cdefghijklmnopqrstuvwxyz"
-
-  # test "slice of negative length":
-  # same as slice of zero length
-  s = characters
-  s[2..0] = numbers
-  doAssert s == "ab1234567890cdefghijklmnopqrstuvwxyz"
-
-  # bug #6223
-  doAssertRaises(IndexError):
-    discard s[0..999]
-
-  echo("OK")
-
-proc test_string_cmp() =
-  let world = "hello\0world"
-  let earth = "hello\0earth"
-  let short = "hello\0"
-  let hello = "hello"
-  let goodbye = "goodbye"
-
-  doAssert world == world
-  doAssert world != earth
-  doAssert world != short
-  doAssert world != hello
-  doAssert world != goodbye
-
-  doAssert cmp(world, world) == 0
-  doAssert cmp(world, earth) > 0
-  doAssert cmp(world, short) > 0
-  doAssert cmp(world, hello) > 0
-  doAssert cmp(world, goodbye) > 0
-
-test_string_slice()
-test_string_cmp()
+
+from std/sequtils import toSeq, map
+from std/sugar import `=>`
+import std/assertions
+
+proc tester[T](x: T) =
+  let test = toSeq(0..4).map(i => newSeq[int]())
+  doAssert $test == "@[@[], @[], @[], @[], @[]]"
+
+func reverse*(a: string): string =
+  result = a
+  for i in 0 ..< a.len div 2:
+    swap(result[i], result[^(i + 1)])
+
+proc main() =
+  block: # ..
+    const
+      characters = "abcdefghijklmnopqrstuvwxyz"
+      numbers = "1234567890"
+
+    # test "slice of length == len(characters)":
+    # replace characters completely by numbers
+    var s: string
+    s = characters
+    s[0..^1] = numbers
+    doAssert s == numbers
+
+    # test "slice of length > len(numbers)":
+    # replace characters by slice of same length
+    s = characters
+    s[1..16] = numbers
+    doAssert s == "a1234567890rstuvwxyz"
+
+    # test "slice of length == len(numbers)":
+    # replace characters by slice of same length
+    s = characters
+    s[1..10] = numbers
+    doAssert s == "a1234567890lmnopqrstuvwxyz"
+
+    # test "slice of length < len(numbers)":
+    # replace slice of length. and insert remaining chars
+    s = characters
+    s[1..4] = numbers
+    doAssert s == "a1234567890fghijklmnopqrstuvwxyz"
+
+    # test "slice of length == 1":
+    # replace first character. and insert remaining 9 chars
+    s = characters
+    s[1..1] = numbers
+    doAssert s == "a1234567890cdefghijklmnopqrstuvwxyz"
+
+    # test "slice of length == 0":
+    # insert chars at slice start index
+    s = characters
+    s[2..1] = numbers
+    doAssert s == "ab1234567890cdefghijklmnopqrstuvwxyz"
+
+    # test "slice of negative length":
+    # same as slice of zero length
+    s = characters
+    s[2..0] = numbers
+    doAssert s == "ab1234567890cdefghijklmnopqrstuvwxyz"
+
+    when nimvm:
+      discard
+    else:
+      # bug #6223
+      doAssertRaises(IndexDefect):
+        discard s[0..999]
+
+  block: # ==, cmp
+    let world = "hello\0world"
+    let earth = "hello\0earth"
+    let short = "hello\0"
+    let hello = "hello"
+    let goodbye = "goodbye"
+
+    doAssert world == world
+    doAssert world != earth
+    doAssert world != short
+    doAssert world != hello
+    doAssert world != goodbye
+
+    doAssert cmp(world, world) == 0
+    doAssert cmp(world, earth) > 0
+    doAssert cmp(world, short) > 0
+    doAssert cmp(world, hello) > 0
+    doAssert cmp(world, goodbye) > 0
+
+  block: # bug #7816
+    tester(1)
+
+  block: # bug #14497, reverse
+    doAssert reverse("hello") == "olleh"
+
+  block: # len, high
+    var a = "ab\0cd"
+    var b = a.cstring
+    doAssert a.len == 5
+    block: # bug #16405
+      when defined(js):
+        when nimvm: doAssert b.len == 2
+        else: doAssert b.len == 5
+      else: doAssert b.len == 2
+
+    doAssert a.high == a.len - 1
+    doAssert b.high == b.len - 1
+
+    doAssert "".len == 0
+    doAssert "".high == -1
+    doAssert "".cstring.len == 0
+    doAssert "".cstring.high == -1
+
+    block: # bug #16674
+      var c: cstring = nil
+      doAssert c.len == 0
+      doAssert c.high == -1
+
+static: main()
+main()
diff --git a/tests/stdlib/tstrmiscs.nim b/tests/stdlib/tstrmiscs.nim
new file mode 100644
index 000000000..b42f2e1fe
--- /dev/null
+++ b/tests/stdlib/tstrmiscs.nim
@@ -0,0 +1,27 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/strmisc
+import std/assertions
+
+
+doAssert expandTabs("\t", 4) == "    "
+doAssert expandTabs("\tfoo\t", 4) == "    foo "
+doAssert expandTabs("\tfoo\tbar", 4) == "    foo bar"
+doAssert expandTabs("\tfoo\tbar\t", 4) == "    foo bar "
+doAssert expandTabs("", 4) == ""
+doAssert expandTabs("", 0) == ""
+doAssert expandTabs("\t\t\t", 0) == ""
+
+doAssert partition("foo:bar", ":") == ("foo", ":", "bar")
+doAssert partition("foobarbar", "bar") == ("foo", "bar", "bar")
+doAssert partition("foobarbar", "bank") == ("foobarbar", "", "")
+doAssert partition("foobarbar", "foo") == ("", "foo", "barbar")
+doAssert partition("foofoobar", "bar") == ("foofoo", "bar", "")
+
+doAssert rpartition("foo:bar", ":") == ("foo", ":", "bar")
+doAssert rpartition("foobarbar", "bar") == ("foobar", "bar", "")
+doAssert rpartition("foobarbar", "bank") == ("", "", "foobarbar")
+doAssert rpartition("foobarbar", "foo") == ("", "foo", "barbar")
+doAssert rpartition("foofoobar", "bar") == ("foofoo", "bar", "")
diff --git a/tests/stdlib/tstrscans.nim b/tests/stdlib/tstrscans.nim
new file mode 100644
index 000000000..ae7fd98ca
--- /dev/null
+++ b/tests/stdlib/tstrscans.nim
@@ -0,0 +1,288 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/[strscans, strutils, assertions]
+
+block ParsePasswd:
+  proc parsePasswd(content: string): seq[string] =
+    result = @[]
+    var idx = 0
+    while true:
+      var entry = ""
+      if scanp(content, idx, +(~{'\L', '\0'} -> entry.add($_)), '\L'):
+        result.add entry
+      else:
+        break
+
+  const etcPasswd = """root:x:0:0:root:/root:/bin/bash
+daemon:x:1:1:daemon:/usr/sbin:/bin/sh
+bin:x:2:2:bin:/bin:/bin/sh
+sys:x:3:3:sys:/dev:/bin/sh
+nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
+messagebus:x:103:107::/var/run/dbus:/bin/false
+"""
+
+  const parsedEtcPasswd = @[
+    "root:x:0:0:root:/root:/bin/bash",
+    "daemon:x:1:1:daemon:/usr/sbin:/bin/sh",
+    "bin:x:2:2:bin:/bin:/bin/sh",
+    "sys:x:3:3:sys:/dev:/bin/sh",
+    "nobody:x:65534:65534:nobody:/nonexistent:/bin/sh",
+    "messagebus:x:103:107::/var/run/dbus:/bin/false",
+    ]
+  doAssert etcPasswd.parsePasswd == parsedEtcPasswd
+
+block LastNot:
+  var idx : int
+
+  idx = 0
+  doAssert scanp("foo", idx,  'f', 'o', ~'a')
+
+  idx = 0
+  doAssert scanp("foo", idx,  'f', 'o', ~'o') == false
+
+  idx = 0
+  doAssert scanp("foox", idx,  'f', 'o', ~'o') == false
+
+  idx = 0
+  doAssert scanp("foox", idx,  'f', 'o', ~'a')
+
+block LastOptional:
+  var idx = 0
+  doAssert scanp("foo", idx, 'f', 'o', 'o', ?'o')
+
+block Tuple:
+  var idx = 0
+  doAssert scanp("foo", idx,  ('f', 'o', 'o'))
+
+block NotWithOptional:
+  var idx : int
+
+  idx = 0
+  doAssert scanp("bc", idx, ~(?'b', 'c')) == false
+
+  idx = 0
+  doAssert scanp("c", idx, ~(?'b', 'c')) == false
+
+  idx = 0
+  doAssert scanp("b", idx, ~(?'b', 'c'))
+
+block NotEmpty:
+  var idx = 0
+  doAssert scanp("", idx, ~()) == false
+
+block EmptyTuple:
+  var idx = 0
+  doAssert scanp("ab", idx, 'a', (), 'b')
+
+block Arrow:
+  let text = "foo;bar;baz;"
+  var idx = 0
+  doAssert scanp(text, idx, +(~{';','\0'} -> (discard $_)), ';')
+  doAssert scanp(text, idx, +(~{';','\0'} -> (discard $_)), ';')
+  doAssert scanp(text, idx, +(~{';','\0'} -> (discard $_)), ';')
+  doAssert scanp(text, idx, +(~{';','\0'} -> (discard $_)), ';') == false
+
+
+block issue15064:
+  var nick1, msg1: string
+  doAssert scanf("<abcd> a", "<$+> $+", nick1, msg1)
+  doAssert nick1 == "abcd"
+  doAssert msg1 == "a"
+
+  var nick2, msg2: string
+  doAssert(not scanf("<abcd> ", "<$+> $+", nick2, msg2))
+
+  var nick3, msg3: string
+  doAssert scanf("<abcd> ", "<$+> $*", nick3, msg3)
+  doAssert nick3 == "abcd"
+  doAssert msg3 == ""
+
+
+block:
+  proc twoDigits(input: string; x: var int; start: int): int =
+    if start+1 < input.len and input[start] == '0' and input[start+1] == '0':
+      result = 2
+      x = 13
+    else:
+      result = 0
+
+  proc someSep(input: string; start: int; seps: set[char] = {';', ',', '-', '.'}): int =
+    result = 0
+    while start+result < input.len and input[start+result] in seps: inc result
+
+  proc demangle(s: string; res: var string; start: int): int =
+    while result+start < s.len and s[result+start] in {'_', '@'}: inc result
+    res = ""
+    while result+start < s.len and s[result+start] > ' ' and s[result+start] != '_':
+      res.add s[result+start]
+      inc result
+    while result+start < s.len and s[result+start] > ' ':
+      inc result
+
+  proc parseGDB(resp: string): seq[string] =
+    const
+      digits = {'0'..'9'}
+      hexdigits = digits + {'a'..'f', 'A'..'F'}
+      whites = {' ', '\t', '\C', '\L'}
+    result = @[]
+    var idx = 0
+    while true:
+      var prc = ""
+      var info = ""
+      if scanp(resp, idx, *`whites`, '#', *`digits`, +`whites`, ?("0x", *`hexdigits`, " in "),
+               demangle($input, prc, $index), *`whites`, '(', * ~ ')', ')',
+                *`whites`, "at ", +(~{'\C', '\L'} -> info.add($_))):
+        result.add prc & " " & info
+      else:
+        break
+
+  var key, val: string
+  var intVal: int
+  var floatVal: float
+  doAssert scanf("abc:: xyz 89  33.25", "$w$s::$s$w$s$i  $f", key, val, intVal, floatVal)
+  doAssert key == "abc"
+  doAssert val == "xyz"
+  doAssert intVal == 89
+  doAssert floatVal == 33.25
+
+  var binVal: int
+  var octVal: int
+  var hexVal: int
+  doAssert scanf("0b0101 0o1234 0xabcd", "$b$s$o$s$h", binVal, octVal, hexVal)
+  doAssert binVal == 0b0101
+  doAssert octVal == 0o1234
+  doAssert hexVal == 0xabcd
+
+  let xx = scanf("$abc", "$$$i", intVal)
+  doAssert xx == false
+
+
+  let xx2 = scanf("$1234", "$$$i", intVal)
+  doAssert xx2
+
+  let yy = scanf(";.--Breakpoint00 [output]",
+      "$[someSep]Breakpoint${twoDigits}$[someSep({';','.','-'})] [$+]$.",
+      intVal, key)
+  doAssert yy
+  doAssert key == "output"
+  doAssert intVal == 13
+
+  var ident = ""
+  var idx = 0
+  let zz = scanp("foobar x x  x   xWZ", idx, +{'a'..'z'} -> add(ident, $_), *(*{
+      ' ', '\t'}, "x"), ~'U', "Z")
+  doAssert zz
+  doAssert ident == "foobar"
+
+  const digits = {'0'..'9'}
+  var year = 0
+  var idx2 = 0
+  if scanp("201655-8-9", idx2, `digits`{4, 6} -> (year = year * 10 + ord($_) -
+      ord('0')), "-8", "-9"):
+    doAssert year == 201655
+
+  const gdbOut = """
+      #0  @foo_96013_1208911747@8 (x0=...)
+          at c:/users/anwender/projects/nim/temp.nim:11
+      #1  0x00417754 in tempInit000 () at c:/users/anwender/projects/nim/temp.nim:13
+      #2  0x0041768d in NimMainInner ()
+          at c:/users/anwender/projects/nim/lib/system.nim:2605
+      #3  0x004176b1 in NimMain ()
+          at c:/users/anwender/projects/nim/lib/system.nim:2613
+      #4  0x004176db in main (argc=1, args=0x712cc8, env=0x711ca8)
+          at c:/users/anwender/projects/nim/lib/system.nim:2620"""
+  const result = @["foo c:/users/anwender/projects/nim/temp.nim:11",
+          "tempInit000 c:/users/anwender/projects/nim/temp.nim:13",
+          "NimMainInner c:/users/anwender/projects/nim/lib/system.nim:2605",
+          "NimMain c:/users/anwender/projects/nim/lib/system.nim:2613",
+          "main c:/users/anwender/projects/nim/lib/system.nim:2620"]
+  doAssert parseGDB(gdbOut) == result
+
+  # bug #6487
+  var count = 0
+
+  proc test(): string =
+    inc count
+    result = ",123123"
+
+  var a: int
+  discard scanf(test(), ",$i", a)
+  doAssert count == 1
+
+
+block:
+  let input = """1-3 s: abc
+15-18 9: def
+15-18 A: ghi
+15-18 _: jkl
+"""
+  var
+    lo, hi: int
+    w: string
+    c: char
+    res: int
+  for line in input.splitLines:
+    if line.scanf("$i-$i $c: $w", lo, hi, c, w):
+      inc res
+  doAssert res == 4
+
+block:
+  #whenscanf testing
+  let input = """1-3 s: abc
+15-18 9: def
+15-18 A: ghi
+15-18 _: jkl
+"""
+  proc twoDigits(input: string; x: var int; start: int): int =
+    if start+1 < input.len and input[start] == '0' and input[start+1] == '0':
+      result = 2
+      x = 13
+    else:
+      result = 0
+
+  proc someSep(input: string; start: int; seps: set[char] = {';', ',', '-', '.'}): int =
+    result = 0
+    while start+result < input.len and input[start+result] in seps: inc result
+
+  type
+    ScanRetType = tuple
+      success: bool
+      lo: int
+      hi: int
+      ch: char
+      word: string
+
+  var res = 0
+  for line in input.splitLines:
+    let ret: ScanRetType = scanTuple(line, "$i-$i $c: $w")
+    if ret.success:
+      inc res
+  doAssert res == 4
+
+  let (_, key, val, intVal, floatVal) = scanTuple("abc:: xyz 89  33.25", "$w$s::$s$w$s$i  $f")
+  doAssert key == "abc"
+  doAssert val == "xyz"
+  doAssert intVal == 89
+  doAssert floatVal == 33.25
+
+
+  let (_, binVal, octVal, hexVal) = scanTuple("0b0101 0o1234 0xabcd", "$b$s$o$s$h", binVal, octVal, hexVal)
+  doAssert binVal == 0b0101
+  doAssert octVal == 0o1234
+  doAssert hexVal == 0xabcd
+
+  var (xx,_) = scanTuple("$abc", "$$$i")
+  doAssert xx == false
+
+
+  let (xx2, _) = block: scanTuple("$1234", "$$$i")
+  doAssert xx2
+
+  var (yy, intVal2, key2) = scanTuple(";.--Breakpoint00 [output]",
+      "$[someSep]Breakpoint${twoDigits}$[someSep({';','.','-'})] [$+]$.",
+      int)
+  doAssert yy
+  doAssert key2 == "output"
+  doAssert intVal2 == 13
diff --git a/tests/stdlib/tstrset.nim b/tests/stdlib/tstrset.nim
index 1b253f862..bbb6c2677 100644
--- a/tests/stdlib/tstrset.nim
+++ b/tests/stdlib/tstrset.nim
@@ -1,12 +1,16 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 # test a simple yet highly efficient set of strings
 
 type
   TRadixNodeKind = enum rnLinear, rnFull, rnLeaf
   PRadixNode = ref TRadixNode
-  TRadixNode = object {.inheritable.}
+  TRadixNode {.inheritable.} = object
     kind: TRadixNodeKind
   TRadixNodeLinear = object of TRadixNode
-    len: int8
+    len: uint8
     keys: array[0..31, char]
     vals: array[0..31, PRadixNode]
   TRadixNodeFull = object of TRadixNode
@@ -24,7 +28,7 @@ proc search(r: PRadixNode, s: string): PRadixNode =
     case r.kind
     of rnLinear:
       var x = PRadixNodeLinear(r)
-      for j in 0..ze(x.len)-1:
+      for j in 0..int(x.len)-1:
         if x.keys[j] == s[i]:
           if s[i] == '\0': return r
           r = x.vals[j]
@@ -50,7 +54,7 @@ proc search(r: PRadixNode, s: string): PRadixNode =
 proc contains*(r: PRadixNode, s: string): bool =
   return search(r, s) != nil
 
-proc testOrincl*(r: var PRadixNode, s: string): bool =
+proc testOrIncl*(r: var PRadixNode, s: string): bool =
   nil
 
 proc incl*(r: var PRadixNode, s: string) = discard testOrIncl(r, s)
@@ -63,9 +67,9 @@ proc excl*(r: var PRadixNode, s: string) =
   of rnFull: PRadixNodeFull(x).b['\0'] = nil
   of rnLinear:
     var x = PRadixNodeLinear(x)
-    for i in 0..ze(x.len)-1:
+    for i in 0..int(x.len)-1:
       if x.keys[i] == '\0':
-        swap(x.keys[i], x.keys[ze(x.len)-1])
+        swap(x.keys[i], x.keys[int(x.len)-1])
         dec(x.len)
         break
 
diff --git a/tests/stdlib/tstrtabs.nim b/tests/stdlib/tstrtabs.nim
index a248cc3b2..d261abe76 100644
--- a/tests/stdlib/tstrtabs.nim
+++ b/tests/stdlib/tstrtabs.nim
@@ -1,4 +1,95 @@
-import strtabs
+discard """
+matrix: "--mm:refc; --mm:orc"
+sortoutput: true
+output: '''
+key1: value1
+key2: value2
+key_0: value0
+key_10: value10
+key_11: value11
+key_12: value12
+key_13: value13
+key_14: value14
+key_15: value15
+key_16: value16
+key_17: value17
+key_18: value18
+key_19: value19
+key_20: value20
+key_21: value21
+key_22: value22
+key_23: value23
+key_24: value24
+key_25: value25
+key_26: value26
+key_27: value27
+key_28: value28
+key_29: value29
+key_30: value30
+key_31: value31
+key_32: value32
+key_33: value33
+key_34: value34
+key_35: value35
+key_36: value36
+key_37: value37
+key_38: value38
+key_39: value39
+key_3: value3
+key_40: value40
+key_41: value41
+key_42: value42
+key_43: value43
+key_44: value44
+key_45: value45
+key_46: value46
+key_47: value47
+key_48: value48
+key_49: value49
+key_4: value4
+key_50: value50
+key_51: value51
+key_52: value52
+key_53: value53
+key_54: value54
+key_55: value55
+key_56: value56
+key_57: value57
+key_58: value58
+key_59: value59
+key_5: value5
+key_60: value60
+key_61: value61
+key_62: value62
+key_63: value63
+key_64: value64
+key_65: value65
+key_66: value66
+key_67: value67
+key_68: value68
+key_69: value69
+key_6: value6
+key_70: value70
+key_71: value71
+key_72: value72
+key_73: value73
+key_74: value74
+key_75: value75
+key_76: value76
+key_77: value77
+key_78: value78
+key_79: value79
+key_7: value7
+key_80: value80
+key_8: value8
+key_9: value9
+length of table 0
+length of table 81
+value1 = value2
+'''
+"""
+
+import std/[strtabs, assertions, syncio]
 
 var tab = newStringTable({"key1": "val1", "key2": "val2"},
                          modeStyleInsensitive)
@@ -9,4 +100,18 @@ for key, val in pairs(tab):
   writeLine(stdout, key, ": ", val)
 writeLine(stdout, "length of table ", $tab.len)
 
-writeLine(stdout, `%`("$key1 = $key2; ${PATH}", tab, {useEnvironment}))
+writeLine(stdout, `%`("$key1 = $key2", tab, {useEnvironment}))
+tab.clear
+writeLine(stdout, "length of table ", $tab.len)
+
+block:
+  var x = {"k": "v", "11": "22", "565": "67"}.newStringTable
+  doAssert x["k"] == "v"
+  doAssert x["11"] == "22"
+  doAssert x["565"] == "67"
+  x["11"] = "23"
+  doAssert x["11"] == "23"
+
+  x.clear(modeCaseInsensitive)
+  x["11"] = "22"
+  doAssert x["11"] == "22"
diff --git a/tests/stdlib/tstrtabs.nims b/tests/stdlib/tstrtabs.nims
new file mode 100644
index 000000000..3563ad0ad
--- /dev/null
+++ b/tests/stdlib/tstrtabs.nims
@@ -0,0 +1,5 @@
+import std/[strtabs, assertions]
+
+static:
+  let t = {"name": "John", "city": "Monaco"}.newStringTable
+  doAssert "${name} lives in ${city}" % t == "John lives in Monaco"
diff --git a/tests/stdlib/tstrtabs2.nim b/tests/stdlib/tstrtabs2.nim
new file mode 100644
index 000000000..a4030ec77
--- /dev/null
+++ b/tests/stdlib/tstrtabs2.nim
@@ -0,0 +1,32 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c cpp js"
+"""
+
+import std/strtabs
+import std/assertions
+
+macro m =
+  var t = {"name": "John"}.newStringTable
+  doAssert t["name"] == "John"
+
+block:
+  var t = {"name": "John"}.newStringTable
+  doAssert t["name"] == "John"
+
+m()
+
+proc fun()=
+  let ret = newStringTable(modeCaseSensitive)
+  ret["foo"] = "bar"
+
+  doAssert $ret == "{foo: bar}"
+
+  let b = ret["foo"]
+  doAssert b == "bar"
+
+proc main()=
+  static: fun()
+  fun()
+
+main()
diff --git a/tests/stdlib/tstrutil.nim b/tests/stdlib/tstrutil.nim
deleted file mode 100644
index 4d4081d39..000000000
--- a/tests/stdlib/tstrutil.nim
+++ /dev/null
@@ -1,306 +0,0 @@
-discard """
-  file: "tstrutil.nim"
-  output: "ha/home/a1xyz/usr/bin"
-"""
-# test the new strutils module
-
-import
-  strutils
-
-import macros
-
-template rejectParse(e) =
-  try:
-    discard e
-    raise newException(AssertionError, "This was supposed to fail: $#!" % astToStr(e))
-  except ValueError: discard
-
-proc testStrip() =
-  write(stdout, strip("  ha  "))
-
-proc testRemoveSuffix =
-  var s = "hello\n\r"
-  s.removeSuffix
-  assert s == "hello"
-  s.removeSuffix
-  assert s == "hello"
-
-  s = "hello\n\n"
-  s.removeSuffix
-  assert s == "hello"
-
-  s = "hello\r"
-  s.removeSuffix
-  assert s == "hello"
-
-  s = "hello \n there"
-  s.removeSuffix
-  assert s == "hello \n there"
-
-  s = "hello"
-  s.removeSuffix("llo")
-  assert s == "he"
-  s.removeSuffix('e')
-  assert s == "h"
-
-  s = "hellos"
-  s.removeSuffix({'s','z'})
-  assert s == "hello"
-  s.removeSuffix({'l','o'})
-  assert s == "he"
-
-  s = "aeiou"
-  s.removeSuffix("")
-  assert s == "aeiou"
-
-  s = ""
-  s.removeSuffix("")
-  assert s == ""
-
-  s = "  "
-  s.removeSuffix
-  assert s == "  "
-
-  s = "  "
-  s.removeSuffix("")
-  assert s == "  "
-
-  s = "    "
-  s.removeSuffix(" ")
-  assert s == "   "
-
-  s = "    "
-  s.removeSuffix(' ')
-  assert s == ""
-
-  # Contrary to Chomp in other languages
-  # empty string does not change behaviour
-  s = "hello\r\n\r\n"
-  s.removeSuffix("")
-  assert s == "hello\r\n\r\n"
-
-proc testRemovePrefix =
-  var s = "\n\rhello"
-  s.removePrefix
-  assert s == "hello"
-  s.removePrefix
-  assert s == "hello"
-
-  s = "\n\nhello"
-  s.removePrefix
-  assert s == "hello"
-
-  s = "\rhello"
-  s.removePrefix
-  assert s == "hello"
-
-  s = "hello \n there"
-  s.removePrefix
-  assert s == "hello \n there"
-
-  s = "hello"
-  s.removePrefix("hel")
-  assert s == "lo"
-  s.removePrefix('l')
-  assert s == "o"
-
-  s = "hellos"
-  s.removePrefix({'h','e'})
-  assert s == "llos"
-  s.removePrefix({'l','o'})
-  assert s == "s"
-
-  s = "aeiou"
-  s.removePrefix("")
-  assert s == "aeiou"
-
-  s = ""
-  s.removePrefix("")
-  assert s == ""
-
-  s = "  "
-  s.removePrefix
-  assert s == "  "
-
-  s = "  "
-  s.removePrefix("")
-  assert s == "  "
-
-  s = "    "
-  s.removePrefix(" ")
-  assert s == "   "
-
-  s = "    "
-  s.removePrefix(' ')
-  assert s == ""
-
-  # Contrary to Chomp in other languages
-  # empty string does not change behaviour
-  s = "\r\n\r\nhello"
-  s.removePrefix("")
-  assert s == "\r\n\r\nhello"
-
-proc main() =
-  testStrip()
-  testRemoveSuffix()
-  testRemovePrefix()
-  for p in split("/home/a1:xyz:/usr/bin", {':'}):
-    write(stdout, p)
-
-proc testDelete =
-  var s = "0123456789ABCDEFGH"
-  delete(s, 4, 5)
-  assert s == "01236789ABCDEFGH"
-  delete(s, s.len-1, s.len-1)
-  assert s == "01236789ABCDEFG"
-  delete(s, 0, 0)
-  assert s == "1236789ABCDEFG"
-
-proc testIsAlphaNumeric =
-  assert isAlphaNumeric("abcdABC1234") == true
-  assert isAlphaNumeric("a") == true
-  assert isAlphaNumeric("abcABC?1234") == false
-  assert isAlphaNumeric("abcABC 1234") == false
-  assert isAlphaNumeric(".") == false
-
-testIsAlphaNumeric()
-
-proc testIsDigit =
-  assert isDigit("1") == true
-  assert isDigit("1234") == true
-  assert isDigit("abcABC?1234") == false
-  assert isDigit(".") == false
-  assert isDigit(":") == false
-
-testIsDigit()
-
-proc testFind =
-  assert "0123456789ABCDEFGH".find('A') == 10
-  assert "0123456789ABCDEFGH".find('A', 5) == 10
-  assert "0123456789ABCDEFGH".find('A', 5, 10) == 10
-  assert "0123456789ABCDEFGH".find('A', 5, 9) == -1
-  assert "0123456789ABCDEFGH".find("A") == 10
-  assert "0123456789ABCDEFGH".find("A", 5) == 10
-  assert "0123456789ABCDEFGH".find("A", 5, 10) == 10
-  assert "0123456789ABCDEFGH".find("A", 5, 9) == -1
-  assert "0123456789ABCDEFGH".find({'A'..'C'}) == 10
-  assert "0123456789ABCDEFGH".find({'A'..'C'}, 5) == 10
-  assert "0123456789ABCDEFGH".find({'A'..'C'}, 5, 10) == 10
-  assert "0123456789ABCDEFGH".find({'A'..'C'}, 5, 9) == -1
-
-proc testRFind =
-  assert "0123456789ABCDEFGAH".rfind('A') == 17
-  assert "0123456789ABCDEFGAH".rfind('A', 13) == 10
-  assert "0123456789ABCDEFGAH".rfind('H', 13) == -1
-  assert "0123456789ABCDEFGAH".rfind("A") == 17
-  assert "0123456789ABCDEFGAH".rfind("A", 13) == 10
-  assert "0123456789ABCDEFGAH".rfind("H", 13) == -1
-  assert "0123456789ABCDEFGAH".rfind({'A'..'C'}) == 17
-  assert "0123456789ABCDEFGAH".rfind({'A'..'C'}, 13) == 12
-  assert "0123456789ABCDEFGAH".rfind({'G'..'H'}, 13) == -1
-
-proc testCountLines =
-  proc assertCountLines(s: string) = assert s.countLines == s.splitLines.len
-  assertCountLines("")
-  assertCountLines("\n")
-  assertCountLines("\n\n")
-  assertCountLines("abc")
-  assertCountLines("abc\n123")
-  assertCountLines("abc\n123\n")
-  assertCountLines("\nabc\n123")
-  assertCountLines("\nabc\n123\n")
-
-proc testParseInts =
-  # binary
-  assert "0b1111".parseBinInt == 15
-  assert "0B1111".parseBinInt == 15
-  assert "1111".parseBinInt == 15
-  assert "1110".parseBinInt == 14
-  assert "1_1_1_1".parseBinInt == 15
-  assert "0b1_1_1_1".parseBinInt == 15
-  rejectParse "".parseBinInt
-  rejectParse "_".parseBinInt
-  rejectParse "0b".parseBinInt
-  rejectParse "0b1234".parseBinInt
-  # hex
-  assert "0x72".parseHexInt == 114
-  assert "0X72".parseHexInt == 114
-  assert "#72".parseHexInt == 114
-  assert "72".parseHexInt == 114
-  assert "FF".parseHexInt == 255
-  assert "ff".parseHexInt == 255
-  assert "fF".parseHexInt == 255  
-  assert "0x7_2".parseHexInt == 114
-  rejectParse "".parseHexInt
-  rejectParse "_".parseHexInt
-  rejectParse "0x".parseHexInt
-  rejectParse "0xFFG".parseHexInt
-  rejectParse "reject".parseHexInt
-  # octal
-  assert "0o17".parseOctInt == 15
-  assert "0O17".parseOctInt == 15
-  assert "17".parseOctInt == 15
-  assert "10".parseOctInt == 8
-  assert "0o1_0_0".parseOctInt == 64
-  rejectParse "".parseOctInt
-  rejectParse "_".parseOctInt
-  rejectParse "0o".parseOctInt
-  rejectParse "9".parseOctInt
-  rejectParse "0o9".parseOctInt
-  rejectParse "reject".parseOctInt
-
-testDelete()
-testFind()
-testRFind()
-testCountLines()
-testParseInts()
-
-assert(insertSep($1000_000) == "1_000_000")
-assert(insertSep($232) == "232")
-assert(insertSep($12345, ',') == "12,345")
-assert(insertSep($0) == "0")
-
-assert(editDistance("prefix__hallo_suffix", "prefix__hallo_suffix") == 0)
-assert(editDistance("prefix__hallo_suffix", "prefix__hallo_suffi1") == 1)
-assert(editDistance("prefix__hallo_suffix", "prefix__HALLO_suffix") == 5)
-assert(editDistance("prefix__hallo_suffix", "prefix__ha_suffix") == 3)
-assert(editDistance("prefix__hallo_suffix", "prefix") == 14)
-assert(editDistance("prefix__hallo_suffix", "suffix") == 14)
-assert(editDistance("prefix__hallo_suffix", "prefix__hao_suffix") == 2)
-assert(editDistance("main", "malign") == 2)
-
-assert "/1/2/3".rfind('/') == 4
-assert "/1/2/3".rfind('/', 1) == 0
-assert "/1/2/3".rfind('0') == -1
-
-assert(toHex(100i16, 32) == "00000000000000000000000000000064")
-assert(toHex(-100i16, 32) == "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C")
-
-assert "".parseHexStr == ""
-assert "00Ff80".parseHexStr == "\0\xFF\x80"
-try:
-  discard "00Ff8".parseHexStr
-  assert false, "Should raise ValueError"
-except ValueError:
-  discard
-
-try:
-  discard "0k".parseHexStr
-  assert false, "Should raise ValueError"
-except ValueError:
-  discard
-
-assert "".toHex == ""
-assert "\x00\xFF\x80".toHex == "00FF80"
-assert "0123456789abcdef".parseHexStr.toHex == "0123456789ABCDEF"
-
-assert(' '.repeat(8)== "        ")
-assert(" ".repeat(8) == "        ")
-assert(spaces(8) == "        ")
-
-assert(' '.repeat(0) == "")
-assert(" ".repeat(0) == "")
-assert(spaces(0) == "")
-
-main()
-#OUT ha/home/a1xyz/usr/bin
diff --git a/tests/stdlib/tstrutils.nim b/tests/stdlib/tstrutils.nim
new file mode 100644
index 000000000..35f6bc669
--- /dev/null
+++ b/tests/stdlib/tstrutils.nim
@@ -0,0 +1,913 @@
+discard """
+  matrix: "--mm:refc; --mm:orc; --backend:cpp; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on"
+"""
+
+import std/strutils
+from stdtest/testutils import disableVm
+import std/assertions
+import std/private/jsutils
+# xxx each instance of `disableVm` and `when not defined js:` should eventually be fixed
+
+template rejectParse(e) =
+  try:
+    discard e
+    raise newException(AssertionDefect, "This was supposed to fail: $#!" % astToStr(e))
+  except ValueError: discard
+
+template main() =
+  block: # strip
+    doAssert strip("  ha  ") == "ha"
+    doAssert strip("  foofoofoo  ") == "foofoofoo"
+    doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo"
+    doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo"
+    doAssert strip("stripme but don't strip this stripme",
+                   chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) ==
+                   " but don't strip this "
+    doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo"
+    doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos"
+
+    block:
+      let a = "xxxxxx"
+      doAssert a.strip(chars={'x'}).len == 0
+
+    doAssert "".strip(chars={'x'}).len == 0
+    doAssert "         ".strip(chars={'x'}) == "         "
+    doAssert "xxx xxx".strip(chars={'x'}) == " "
+    doAssert "xxx  wind".strip(chars={'x'}) == "  wind"
+    doAssert "xxx  iii".strip(chars={'i'}) == "xxx  "
+    doAssert "x".strip(leading = false, chars={'x'}).len == 0
+    doAssert "x".strip(trailing = false, chars={'x'}).len == 0
+    doAssert "x".strip(leading = false, trailing = false, chars={'x'}) == "x"
+
+  block: # split
+    var ret: seq[string] # or use `toSeq` or `collect`
+    for p in split("/home/a1:xyz:/usr/bin", {':'}): ret.add p
+    doAssert ret == @["/home/a1", "xyz", "/usr/bin"]
+
+    let s = " this is an example  "
+    let s2 = ":this;is;an:example;;"
+
+    doAssert s.split() == @["", "this", "is", "an", "example", "", ""]
+    doAssert s2.split(seps = {':', ';'}) == @["", "this", "is", "an", "example",
+        "", ""]
+    doAssert s.split(maxsplit = 4) == @["", "this", "is", "an", "example  "]
+    doAssert s.split(' ', maxsplit = 1) == @["", "this is an example  "]
+    doAssert s.split(" ", maxsplit = 4) == @["", "this", "is", "an", "example  "]
+    # Empty string:
+    doAssert "".split() == @[""]
+    doAssert "".split(" ") == @[""]
+    doAssert "".split({' '}) == @[""]
+    # Empty separators:
+    doAssert "".split({}) == @[""]
+    doAssert "".split("") == @[""]
+    doAssert s.split({}) == @[s]
+    doAssert s.split("") == @[s]
+
+  block: # splitLines
+    let fixture = "a\nb\rc\r\nd"
+    doAssert len(fixture.splitLines) == 4
+    doAssert splitLines(fixture) == @["a", "b", "c", "d"]
+    doAssert splitLines(fixture, keepEol=true) == @["a\n", "b\r", "c\r\n", "d"]
+
+  block: # rsplit
+    doAssert rsplit("foo bar", seps = Whitespace) == @["foo", "bar"]
+    doAssert rsplit(" foo bar", seps = Whitespace, maxsplit = 1) == @[" foo", "bar"]
+    doAssert rsplit(" foo bar ", seps = Whitespace, maxsplit = 1) == @[" foo bar", ""]
+    doAssert rsplit(":foo:bar", sep = ':') == @["", "foo", "bar"]
+    doAssert rsplit(":foo:bar", sep = ':', maxsplit = 2) == @["", "foo", "bar"]
+    doAssert rsplit(":foo:bar", sep = ':', maxsplit = 3) == @["", "foo", "bar"]
+    doAssert rsplit("foothebar", sep = "the") == @["foo", "bar"]
+    # Empty string:
+    doAssert "".rsplit() == @[""]
+    doAssert "".rsplit(" ") == @[""]
+    doAssert "".rsplit({' '}) == @[""]
+    # Empty separators:
+    let s = " this is an example  "
+    doAssert "".rsplit({}) == @[""]
+    doAssert "".rsplit("") == @[""]
+    doAssert s.rsplit({}) == @[s]
+    doAssert s.rsplit("") == @[s]
+
+  block: # splitWhitespace
+    let s = " this is an example  "
+    doAssert s.splitWhitespace() == @["this", "is", "an", "example"]
+    doAssert s.splitWhitespace(maxsplit = 1) == @["this", "is an example  "]
+    doAssert s.splitWhitespace(maxsplit = 2) == @["this", "is", "an example  "]
+    doAssert s.splitWhitespace(maxsplit = 3) == @["this", "is", "an", "example  "]
+    doAssert s.splitWhitespace(maxsplit = 4) == @["this", "is", "an", "example"]
+
+  block: # removeSuffix
+    var s = "hello\n\r"
+    s.removeSuffix
+    doAssert s == "hello"
+    s.removeSuffix
+    doAssert s == "hello"
+
+    s = "hello\n\n"
+    s.removeSuffix
+    doAssert s == "hello"
+
+    s = "hello\r"
+    s.removeSuffix
+    doAssert s == "hello"
+
+    s = "hello \n there"
+    s.removeSuffix
+    doAssert s == "hello \n there"
+
+    s = "hello"
+    s.removeSuffix("llo")
+    doAssert s == "he"
+    s.removeSuffix('e')
+    doAssert s == "h"
+
+    s = "hellos"
+    s.removeSuffix({'s','z'})
+    doAssert s == "hello"
+    s.removeSuffix({'l','o'})
+    doAssert s == "he"
+
+    s = "aeiou"
+    s.removeSuffix("")
+    doAssert s == "aeiou"
+
+    s = ""
+    s.removeSuffix("")
+    doAssert s == ""
+
+    s = "  "
+    s.removeSuffix
+    doAssert s == "  "
+
+    s = "  "
+    s.removeSuffix("")
+    doAssert s == "  "
+
+    s = "    "
+    s.removeSuffix(" ")
+    doAssert s == "   "
+
+    s = "    "
+    s.removeSuffix(' ')
+    doAssert s == ""
+
+    # Contrary to Chomp in other languages
+    # empty string does not change behaviour
+    s = "hello\r\n\r\n"
+    s.removeSuffix("")
+    doAssert s == "hello\r\n\r\n"
+
+  block: # removePrefix
+    var s = "\n\rhello"
+    s.removePrefix
+    doAssert s == "hello"
+    s.removePrefix
+    doAssert s == "hello"
+
+    s = "\n\nhello"
+    s.removePrefix
+    doAssert s == "hello"
+
+    s = "\rhello"
+    s.removePrefix
+    doAssert s == "hello"
+
+    s = "hello \n there"
+    s.removePrefix
+    doAssert s == "hello \n there"
+
+    s = "hello"
+    s.removePrefix("hel")
+    doAssert s == "lo"
+    s.removePrefix('l')
+    doAssert s == "o"
+
+    s = "hellos"
+    s.removePrefix({'h','e'})
+    doAssert s == "llos"
+    s.removePrefix({'l','o'})
+    doAssert s == "s"
+
+    s = "aeiou"
+    s.removePrefix("")
+    doAssert s == "aeiou"
+
+    s = ""
+    s.removePrefix("")
+    doAssert s == ""
+
+    s = "  "
+    s.removePrefix
+    doAssert s == "  "
+
+    s = "  "
+    s.removePrefix("")
+    doAssert s == "  "
+
+    s = "    "
+    s.removePrefix(" ")
+    doAssert s == "   "
+
+    s = "    "
+    s.removePrefix(' ')
+    doAssert s == ""
+
+    # Contrary to Chomp in other languages
+    # empty string does not change behaviour
+    s = "\r\n\r\nhello"
+    s.removePrefix("")
+    doAssert s == "\r\n\r\nhello"
+
+  block: # delete(slice)
+    var s = "0123456789ABCDEFGH"
+    delete(s, 4 .. 5)
+    doAssert s == "01236789ABCDEFGH"
+    delete(s, s.len-1 .. s.len-1)
+    doAssert s == "01236789ABCDEFG"
+    delete(s, 0..0)
+    doAssert s == "1236789ABCDEFG"
+    s = ""
+    doAssertRaises(IndexDefect): delete(s, 0..0)
+    doAssert s == ""
+    s = "abc"
+    doAssertRaises(IndexDefect): delete(s, -1 .. -2)
+    doAssertRaises(IndexDefect): delete(s, 2..3)
+    doAssertRaises(IndexDefect): delete(s, 3..2)
+    delete(s, 2..2)
+    doAssert s == "ab"
+    delete(s, 1..0)
+    doAssert s == "ab"
+    delete(s, 0..0)
+    doAssert s == "b"
+
+  block: # delete(first, last)
+    {.push warning[deprecated]:off.}
+    var s = "0123456789ABCDEFGH"
+    delete(s, 4, 5)
+    doAssert s == "01236789ABCDEFGH"
+    delete(s, s.len-1, s.len-1)
+    doAssert s == "01236789ABCDEFG"
+    delete(s, 0, 0)
+    doAssert s == "1236789ABCDEFG"
+    {.pop.}
+
+  block: # find
+    const haystack: string = "0123456789ABCDEFGH"
+    doAssert haystack.find('A') == 10
+    doAssert haystack.find('A', 5) == 10
+    doAssert haystack.find('A', 5, 10) == 10
+    doAssert haystack.find('A', 5, 9) == -1
+    doAssert haystack.find("A") == 10
+    doAssert haystack.find("A", 5) == 10
+    doAssert haystack.find("A", 5, 10) == 10
+    doAssert haystack.find("A", 5, 9) == -1
+    doAssert haystack.find({'A'..'C'}) == 10
+    doAssert haystack.find({'A'..'C'}, 5) == 10
+    doAssert haystack.find({'A'..'C'}, 5, 10) == 10
+    doAssert haystack.find({'A'..'C'}, 5, 9) == -1
+    doAssert haystack.find('A', 0, 0) == -1 # search limited to the first char
+    doAssert haystack.find('A', 5, 0) == -1 # last < start
+    doAssert haystack.find('A', 5, 4) == -1 # last < start
+
+    block:
+      const haystack: string = "ABCABABABABCAB"
+      doAssert haystack.len == 14
+
+      # only last argument
+      doAssert haystack.find("ABC") == 0
+      doAssert haystack.find("ABC", last=13) == 0 # after the second ABC
+      doAssert haystack.find("ABC", last=5) == 0 # before the second ABC
+
+      # only start argument
+      doAssert haystack.find("ABC", start=0) == 0
+      doAssert haystack.find("ABC", start=1) == 9
+      doAssert haystack.find("ABC", start=9) == 9
+      doAssert haystack.find("ABC", start=10) == -1
+
+      # both start and last arguments
+      doAssert haystack.find("ABC", start=0, last=14) == 0
+      doAssert haystack.find("ABC", start=0, last=13) == 0
+      doAssert haystack.find("ABC", start=0, last=12) == 0
+      doAssert haystack.find("ABC", start=1, last=13) == 9
+      doAssert haystack.find("ABC", start=1, last=12) == 9
+      doAssert haystack.find("ABC", start=1, last=11) == 9
+      doAssert haystack.find("ABC", start=1, last=10) == -1
+
+    doAssert "".find("/") == -1
+    doAssert "/".find("/") == 0
+    doAssert "/".find("//") == -1
+    doAssert "///".find("//", start=3) == -1
+
+    # searching for empty string
+    doAssert "".find("") == 0
+    doAssert "abc".find("") == 0
+    doAssert "abc".find("", start=1) == 1
+    doAssert "abc".find("", start=2) == 2
+    doAssert "abc".find("", start=3) == 3
+    doAssert "abc".find("", start=4) == -1
+    doAssert "abc".find("", start=400) == -1
+    doAssert "abc".find("", start=1, last=3) == 1
+    doAssert "abc".find("", start=1, last=2) == 1
+    doAssert "abc".find("", start=1, last=1) == 1
+    doAssert "abc".find("", start=1, last=0) == 1
+    doAssert "abc".find("", start=1, last = -1) == 1
+
+    # when last <= start, searching for non-empty string
+    block:
+      let last: int = -1 # searching through whole line
+      doAssert "abcd".find("ab", start=0, last=last) == 0
+      doAssert "abcd".find("ab", start=1, last=last) == -1
+      doAssert "abcd".find("bc", start=1, last=last) == 1
+      doAssert "abcd".find("bc", start=2, last=last) == -1
+    block:
+      let last: int = 0
+      doAssert "abcd".find("ab", start=0, last=last) == -1
+      doAssert "abcd".find("ab", start=1, last=last) == -1
+      doAssert "abcd".find("bc", start=1, last=last) == -1
+      doAssert "abcd".find("bc", start=2, last=last) == -1
+    block:
+      let last: int = 1
+      doAssert "abcd".find("ab", start=0, last=last) == 0
+      doAssert "abcd".find("ab", start=1, last=last) == -1
+      doAssert "abcd".find("bc", start=1, last=last) == -1
+      doAssert "abcd".find("bc", start=2, last=last) == -1
+
+  block: # rfind
+    doAssert "0123456789ABCDEFGAH".rfind('A') == 17
+    doAssert "0123456789ABCDEFGAH".rfind('A', last=13) == 10
+    doAssert "0123456789ABCDEFGAH".rfind('H', last=13) == -1
+    doAssert "0123456789ABCDEFGAH".rfind("A") == 17
+    doAssert "0123456789ABCDEFGAH".rfind("A", last=13) == 10
+    doAssert "0123456789ABCDEFGAH".rfind("H", last=13) == -1
+    doAssert "0123456789ABCDEFGAH".rfind({'A'..'C'}) == 17
+    doAssert "0123456789ABCDEFGAH".rfind({'A'..'C'}, last=13) == 12
+    doAssert "0123456789ABCDEFGAH".rfind({'G'..'H'}, last=13) == -1
+    doAssert "0123456789ABCDEFGAH".rfind('A', start=18) == -1
+    doAssert "0123456789ABCDEFGAH".rfind('A', start=11, last=17) == 17
+    doAssert "0123456789ABCDEFGAH".rfind("0", start=0) == 0
+    doAssert "0123456789ABCDEFGAH".rfind("0", start=1) == -1
+    doAssert "0123456789ABCDEFGAH".rfind("H", start=11) == 18
+    doAssert "0123456789ABCDEFGAH".rfind({'0'..'9'}, start=5) == 9
+    doAssert "0123456789ABCDEFGAH".rfind({'0'..'9'}, start=10) == -1
+
+    doAssert "/1/2/3".rfind('/') == 4
+    doAssert "/1/2/3".rfind('/', last=1) == 0
+    doAssert "/1/2/3".rfind('0') == -1
+
+    block:
+      const haystack: string = "ABCABABABABCAB"
+      doAssert haystack.len == 14
+      doAssert haystack.rfind("ABC") == 9
+      doAssert haystack.rfind("ABC", last=13) == 9
+      doAssert haystack.rfind("ABC", last=12) == 9
+      doAssert haystack.rfind("ABC", last=11) == 9
+      doAssert haystack.rfind("ABC", last=10) == 0
+
+      doAssert haystack.rfind("ABC", start=0) == 9
+      doAssert haystack.rfind("ABC", start=1) == 9
+      doAssert haystack.rfind("ABC", start=9) == 9
+      doAssert haystack.rfind("ABC", start=10) == -1
+
+      doAssert haystack.rfind("ABC", start=0, last=13) == 9
+      doAssert haystack.rfind("ABC", start=0, last=12) == 9
+      doAssert haystack.rfind("ABC", start=0, last=11) == 9
+      doAssert haystack.rfind("ABC", start=0, last=10) == 0
+      doAssert haystack.rfind("ABC", start=1, last=10) == -1
+
+    doAssert "".rfind("/") == -1
+    doAssert "/".rfind("/") == 0
+    doAssert "/".rfind("//") == -1
+    doAssert "///".rfind("//", start=3) == -1
+
+    # searching for empty string
+    doAssert "".rfind("") == 0
+    doAssert "abc".rfind("") == 3
+    doAssert "abc".rfind("", start=1) == 3
+    doAssert "abc".rfind("", start=2) == 3
+    doAssert "abc".rfind("", start=3) == 3
+    doAssert "abc".rfind("", start=4) == 4
+    doAssert "abc".rfind("", start=400) == 400
+
+    doAssert "abc".rfind("", start=1, last=3) == 3
+    doAssert "abc".rfind("", start=1, last=2) == 2
+    doAssert "abc".rfind("", start=1, last=1) == 1
+    # This returns the start index instead of the last index
+    # because start > last
+    doAssert "abc".rfind("", start=1, last=0) == 1
+    doAssert "abc".rfind("", start=1, last = -1) == 3
+    
+    doAssert "abc".rfind("", start=0, last=0) == 0
+
+    # when last <= start, searching for non-empty string
+    block:
+      let last: int = -1
+      doAssert "abcd".rfind("ab", start=0, last=last) == 0
+      doAssert "abcd".rfind("ab", start=1, last=last) == -1
+      doAssert "abcd".rfind("bc", start=1, last=last) == 1
+      doAssert "abcd".rfind("bc", start=2, last=last) == -1
+    block:
+      let last: int = 0
+      doAssert "abcd".rfind("ab", start=0, last=last) == -1
+      doAssert "abcd".rfind("ab", start=1, last=last) == -1
+      doAssert "abcd".rfind("bc", start=1, last=last) == -1
+      doAssert "abcd".rfind("bc", start=2, last=last) == -1
+    block:
+      let last: int = 1
+      doAssert "abcd".rfind("ab", start=0, last=last) == 0
+      doAssert "abcd".rfind("ab", start=1, last=last) == -1
+      doAssert "abcd".rfind("bc", start=1, last=last) == -1
+      doAssert "abcd".rfind("bc", start=2, last=last) == -1
+
+  block: # trimZeros
+    var x = "1200"
+    x.trimZeros()
+    doAssert x == "1200"
+    x = "120.0"
+    x.trimZeros()
+    doAssert x == "120"
+    x = "0."
+    x.trimZeros()
+    doAssert x == "0"
+    x = "1.0e2"
+    x.trimZeros()
+    doAssert x == "1e2"
+    x = "78.90"
+    x.trimZeros()
+    doAssert x == "78.9"
+    x = "1.23e4"
+    x.trimZeros()
+    doAssert x == "1.23e4"
+    x = "1.01"
+    x.trimZeros()
+    doAssert x == "1.01"
+    x = "1.1001"
+    x.trimZeros()
+    doAssert x == "1.1001"
+    x = "0.0"
+    x.trimZeros()
+    doAssert x == "0"
+    x = "0.01"
+    x.trimZeros()
+    doAssert x == "0.01"
+    x = "1e0"
+    x.trimZeros()
+    doAssert x == "1e0"
+    x = "1.23"
+    x.trimZeros()
+    doAssert x == "1.23"
+
+  block: # countLines
+    proc assertCountLines(s: string) = doAssert s.countLines == s.splitLines.len
+    assertCountLines("")
+    assertCountLines("\n")
+    assertCountLines("\n\n")
+    assertCountLines("abc")
+    assertCountLines("abc\n123")
+    assertCountLines("abc\n123\n")
+    assertCountLines("\nabc\n123")
+    assertCountLines("\nabc\n123\n")
+
+  block: # parseBinInt, parseHexInt, parseOctInt
+    # binary
+    doAssert "0b1111".parseBinInt == 15
+    doAssert "0B1111".parseBinInt == 15
+    doAssert "1111".parseBinInt == 15
+    doAssert "1110".parseBinInt == 14
+    doAssert "1_1_1_1".parseBinInt == 15
+    doAssert "0b1_1_1_1".parseBinInt == 15
+    rejectParse "".parseBinInt
+    rejectParse "_".parseBinInt
+    rejectParse "0b".parseBinInt
+    rejectParse "0b1234".parseBinInt
+    # hex
+    doAssert "0x72".parseHexInt == 114
+    doAssert "0X72".parseHexInt == 114
+    doAssert "#72".parseHexInt == 114
+    doAssert "72".parseHexInt == 114
+    doAssert "FF".parseHexInt == 255
+    doAssert "ff".parseHexInt == 255
+    doAssert "fF".parseHexInt == 255
+    doAssert "0x7_2".parseHexInt == 114
+    rejectParse "".parseHexInt
+    rejectParse "_".parseHexInt
+    rejectParse "0x".parseHexInt
+    rejectParse "0xFFG".parseHexInt
+    rejectParse "reject".parseHexInt
+    # octal
+    doAssert "0o17".parseOctInt == 15
+    doAssert "0O17".parseOctInt == 15
+    doAssert "17".parseOctInt == 15
+    doAssert "10".parseOctInt == 8
+    doAssert "0o1_0_0".parseOctInt == 64
+    rejectParse "".parseOctInt
+    rejectParse "_".parseOctInt
+    rejectParse "0o".parseOctInt
+    rejectParse "9".parseOctInt
+    rejectParse "0o9".parseOctInt
+    rejectParse "reject".parseOctInt
+
+  block: # parseHexStr
+    doAssert "".parseHexStr == ""
+    doAssert "00Ff80".parseHexStr == "\0\xFF\x80"
+    try:
+      discard "00Ff8".parseHexStr
+      doAssert false, "Should raise ValueError"
+    except ValueError:
+      discard
+
+    try:
+      discard "0k".parseHexStr
+      doAssert false, "Should raise ValueError"
+    except ValueError:
+      discard
+
+    doAssert "".toHex == ""
+    doAssert "\x00\xFF\x80".toHex == "00FF80"
+    doAssert "0123456789abcdef".parseHexStr.toHex == "0123456789ABCDEF"
+
+  block: # toHex
+    doAssert(toHex(100i16, 32) == "00000000000000000000000000000064")
+    whenJsNoBigInt64: discard
+    do:
+      doAssert(toHex(-100i16, 32) == "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C")
+      doAssert(toHex(high(uint64)) == "FFFFFFFFFFFFFFFF")
+      doAssert(toHex(high(uint64), 16) == "FFFFFFFFFFFFFFFF")
+      doAssert(toHex(high(uint64), 32) == "0000000000000000FFFFFFFFFFFFFFFF")
+
+  block: # insertSep
+    doAssert(insertSep($1000_000) == "1_000_000")
+    doAssert(insertSep($232) == "232")
+    doAssert(insertSep($12345, ',') == "12,345")
+    doAssert(insertSep($0) == "0")
+
+  block: # repeat, spaces
+    doAssert(' '.repeat(8) == "        ")
+    doAssert(" ".repeat(8) == "        ")
+    doAssert(spaces(8) == "        ")
+
+    doAssert(' '.repeat(0) == "")
+    doAssert(" ".repeat(0) == "")
+    doAssert(spaces(0) == "")
+
+  block: # toBin, toOct
+    whenJsNoBigInt64: # bug #11369
+      discard
+    do:
+      var num: int64 = -1
+      doAssert num.toBin(64) == "1111111111111111111111111111111111111111111111111111111111111111"
+      doAssert num.toOct(24) == "001777777777777777777777"
+
+  block: # replace
+    doAssert "oo".replace("", "abc") == "oo"
+    # bug #8911
+    static:
+      let a = ""
+      let a2 = a.replace("\n", "\\n")
+
+    static:
+      let b = "b"
+      let b2 = b.replace("\n", "\\n")
+
+    block:
+      let c = ""
+      let c2 = c.replace("\n", "\\n")
+
+  block: # replaceWord
+    doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz "
+    doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz  abc"
+    doAssert "-lda-ldz -ld abc".replaceWord("") == "-lda-ldz -ld abc"
+
+  block: # multiReplace
+    doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab"
+    doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.",
+        "PEOPLE!")) == "HELLO PEOPLE!"
+    doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa"
+
+  # `parseEnum`, ref issue #14030
+  # check enum defined at top level # xxx this is probably irrelevant, and pollutes scope
+  # for remaining tests
+  type
+    Foo = enum
+      A = -10
+      B = "bb"
+      C = (-5, "ccc")
+      D = 15
+      E = "ee" # check that we count enum fields correctly
+
+  block: # parseEnum
+    block:
+      let a = parseEnum[Foo]("A")
+      let b = parseEnum[Foo]("bb")
+      let c = parseEnum[Foo]("ccc")
+      let d = parseEnum[Foo]("D")
+      let e = parseEnum[Foo]("ee")
+      doAssert a == A
+      doAssert b == B
+      doAssert c == C
+      doAssert d == D
+      doAssert e == E
+      try:
+        let f = parseEnum[Foo]("Bar")
+        doAssert false
+      except ValueError:
+        discard
+
+      # finally using default
+      let g = parseEnum[Foo]("Bar", A)
+      doAssert g == A
+
+    block: # bug #19463
+      const CAMPAIGN_TABLE = "wikientries_campaign"
+      const CHARACTER_TABLE = "wikientries_character"
+
+      type Tables = enum
+        a = CAMPAIGN_TABLE,
+        b = CHARACTER_TABLE,
+
+      let myA = CAMPAIGN_TABLE
+      doAssert $parseEnum[Tables](myA) == "wikientries_campaign"
+
+    block: # check enum defined in block
+      type
+        Bar = enum
+          V
+          W = "ww"
+          X = (3, "xx")
+          Y = 10
+          Z = "zz" # check that we count enum fields correctly
+
+      let a = parseEnum[Bar]("V")
+      let b = parseEnum[Bar]("ww")
+      let c = parseEnum[Bar]("xx")
+      let d = parseEnum[Bar]("Y")
+      let e = parseEnum[Bar]("zz")
+      doAssert a == V
+      doAssert b == W
+      doAssert c == X
+      doAssert d == Y
+      doAssert e == Z
+      try:
+        let f = parseEnum[Bar]("Baz")
+        doAssert false
+      except ValueError:
+        discard
+
+      # finally using default
+      let g = parseEnum[Bar]("Baz", V)
+      doAssert g == V
+
+    block: # check ambiguous enum fails to parse
+      type
+        Ambig = enum
+          f1 = "A"
+          f2 = "B"
+          f3 = "A"
+
+      doAssert not compiles((let a = parseEnum[Ambig]("A")))
+
+    block: # check almost ambiguous enum
+      type
+        AlmostAmbig = enum
+          f1 = "someA"
+          f2 = "someB"
+          f3 = "SomeA"
+
+      let a = parseEnum[AlmostAmbig]("someA")
+      let b = parseEnum[AlmostAmbig]("someB")
+      let c = parseEnum[AlmostAmbig]("SomeA")
+      doAssert a == f1
+      doAssert b == f2
+      doAssert c == f3
+
+    block:
+      type MyEnum = enum enA, enB, enC, enuD, enE
+      doAssert parseEnum[MyEnum]("enu_D") == enuD
+
+      doAssert parseEnum("invalid enum value", enC) == enC
+    
+    block: # issue #22726
+      type SomeEnum = enum A, B, C
+
+      proc assignEnum(dest: var enum, s: string) =
+        type ty = typeof(dest)
+        dest = parseEnum[ty](s)
+      
+      var v: SomeEnum
+      v.assignEnum("A")
+      doAssert v == A
+
+  block: # indentation
+    doAssert 0 == indentation """
+hey
+  low
+    there
+"""
+    doAssert 2 == indentation """
+  hey
+    low
+      there
+"""
+    doAssert 2 == indentation """  hey
+    low
+      there
+"""
+    doAssert 2 == indentation """  hey
+    low
+      there"""
+    doAssert 0 == indentation ""
+    doAssert 0 == indentation "  \n  \n"
+    doAssert 0 == indentation "    "
+
+  block: # indent
+    doAssert "  foo\n  bar".indent(4, "Q") == "QQQQ  foo\nQQQQ  bar"
+
+  block: # unindent
+    doAssert """~~!!foo
+~~!!bar
+~~!!baz""".unindent(2, "~~!!") == "foo\nbar\nbaz"
+
+    doAssert """~~!!foo
+~~!!bar
+~~!!baz""".unindent(2, "~~!!aa") == "~~!!foo\n~~!!bar\n~~!!baz"
+    doAssert """~~foo
+~~  bar
+~~  baz""".unindent(4, "~") == "foo\n  bar\n  baz"
+    doAssert """foo
+bar
+    baz
+  """.unindent(4) == "foo\nbar\nbaz\n"
+    doAssert """foo
+    bar
+    baz
+  """.unindent(2) == "foo\n  bar\n  baz\n"
+    doAssert """foo
+    bar
+    baz
+  """.unindent(100) == "foo\nbar\nbaz\n"
+
+    doAssert """foo
+    foo
+    bar
+  """.unindent() == "foo\nfoo\nbar\n"
+
+  block: # formatBiggestFloat
+    disableVm:
+      doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
+      when not defined(js):
+        doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235." # bugs 8242, 12586
+      doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6"
+      doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
+      doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in
+                                                      ["1,0e-11", "1,0e-011"]
+  block: # formatFloat
+    disableVm:
+      # bug #6589
+      when not defined(js):
+        doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02"
+
+  block: # `%`
+    doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
+    doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"
+    doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
+             "The cat eats fish."
+
+  block: # formatSize
+    disableVm:
+      whenJsNoBigInt64: discard
+      do:
+        doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB" # <=== bug #8231
+      doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
+      doAssert formatSize(4096) == "4KiB"
+      doAssert formatSize(4096, prefix = bpColloquial, includeSpace = true) == "4 kB"
+      doAssert formatSize(4096, includeSpace = true) == "4 KiB"
+      doAssert formatSize(5_378_934, prefix = bpColloquial, decimalSep = ',') == "5,13MB"
+
+  block: # formatEng
+    disableVm:
+      doAssert formatEng(0, 2, trim = false) == "0.00"
+      doAssert formatEng(0, 2) == "0"
+      doAssert formatEng(53, 2, trim = false) == "53.00"
+      doAssert formatEng(0.053, 2, trim = false) == "53.00e-3"
+      doAssert formatEng(0.053, 4, trim = false) == "53.0000e-3"
+      doAssert formatEng(0.053, 4, trim = true) == "53e-3"
+      doAssert formatEng(0.053, 0) == "53e-3"
+      doAssert formatEng(52731234) == "52.731234e6"
+      doAssert formatEng(-52731234) == "-52.731234e6"
+      doAssert formatEng(52731234, 1) == "52.7e6"
+      doAssert formatEng(-52731234, 1) == "-52.7e6"
+      doAssert formatEng(52731234, 1, decimalSep = ',') == "52,7e6"
+      doAssert formatEng(-52731234, 1, decimalSep = ',') == "-52,7e6"
+
+      doAssert formatEng(4100, siPrefix = true, unit = "V") == "4.1 kV"
+      doAssert formatEng(4.1, siPrefix = true, unit = "V",
+          useUnitSpace = true) == "4.1 V"
+      doAssert formatEng(4.1, siPrefix = true) == "4.1" # Note lack of space
+      doAssert formatEng(4100, siPrefix = true) == "4.1 k"
+      doAssert formatEng(4.1, siPrefix = true, unit = "",
+          useUnitSpace = true) == "4.1 " # Includes space
+      doAssert formatEng(4100, siPrefix = true, unit = "") == "4.1 k"
+      doAssert formatEng(4100) == "4.1e3"
+      doAssert formatEng(4100, unit = "V", useUnitSpace = true) == "4.1e3 V"
+      doAssert formatEng(4100, unit = "", useUnitSpace = true) == "4.1e3 "
+      # Don't use SI prefix as number is too big
+      doAssert formatEng(3.1e22, siPrefix = true, unit = "a",
+          useUnitSpace = true) == "31e21 a"
+      # Don't use SI prefix as number is too small
+      doAssert formatEng(3.1e-25, siPrefix = true, unit = "A",
+          useUnitSpace = true) == "310e-27 A"
+
+  block: # align
+    doAssert align("abc", 4) == " abc"
+    doAssert align("a", 0) == "a"
+    doAssert align("1232", 6) == "  1232"
+    doAssert align("1232", 6, '#') == "##1232"
+
+  block: # alignLeft
+    doAssert alignLeft("abc", 4) == "abc "
+    doAssert alignLeft("a", 0) == "a"
+    doAssert alignLeft("1232", 6) == "1232  "
+    doAssert alignLeft("1232", 6, '#') == "1232##"
+
+  block: # center
+    doAssert center("foo", 13) == "     foo     "
+    doAssert center("foo", 0) == "foo"
+    doAssert center("foo", 3, fillChar = 'a') == "foo"
+    doAssert center("foo", 10, fillChar = '\t') == "\t\t\tfoo\t\t\t\t"
+
+  block: # count
+    doAssert count("foofoofoo", "foofoo") == 1
+    doAssert count("foofoofoo", "foofoo", overlapping = true) == 2
+    doAssert count("foofoofoo", 'f') == 3
+    doAssert count("foofoofoobar", {'f', 'b'}) == 4
+
+  block: # isAlphaAscii
+    doAssert isAlphaAscii('r')
+    doAssert isAlphaAscii('A')
+    doAssert(not isAlphaAscii('$'))
+
+  block: # isAlphaNumeric
+    doAssert isAlphaNumeric('3')
+    doAssert isAlphaNumeric('R')
+    doAssert(not isAlphaNumeric('!'))
+
+  block: # isDigit
+    doAssert isDigit('3')
+    doAssert(not isDigit('a'))
+    doAssert(not isDigit('%'))
+
+  block: # isSpaceAscii
+    doAssert isSpaceAscii('\t')
+    doAssert isSpaceAscii('\l')
+    doAssert(not isSpaceAscii('A'))
+
+  block: # isEmptyOrWhitespace
+    doAssert(isEmptyOrWhitespace(""))
+    doAssert(isEmptyOrWhitespace("       "))
+    doAssert(isEmptyOrWhitespace("\t\l \v\r\f"))
+    doAssert(not isEmptyOrWhitespace("ABc   \td"))
+
+  block: # isLowerAscii
+    doAssert isLowerAscii('a')
+    doAssert isLowerAscii('z')
+    doAssert(not isLowerAscii('A'))
+    doAssert(not isLowerAscii('5'))
+    doAssert(not isLowerAscii('&'))
+    doAssert(not isLowerAscii(' '))
+
+  block: # isUpperAscii
+    doAssert isUpperAscii('A')
+    doAssert(not isUpperAscii('b'))
+    doAssert(not isUpperAscii('5'))
+    doAssert(not isUpperAscii('%'))
+
+  block: # unescape
+    doAssert(unescape(r"\x013", "", "") == "\x013")
+
+  block: # join
+    doAssert join(["foo", "bar", "baz"]) == "foobarbaz"
+    doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz"
+    doAssert join([1, 2, 3]) == "123"
+    doAssert join(@[1, 2, 3], ", ") == "1, 2, 3"
+
+  block: # startsWith / endsWith
+    var s = "abcdef"
+    doAssert s.startsWith('a')
+    doAssert s.startsWith('b') == false
+    doAssert s.endsWith('f')
+    doAssert s.endsWith('a') == false
+    doAssert s.endsWith('\0') == false
+
+  block: # nimIdentNormalize
+    doAssert nimIdentNormalize("") == ""
+    doAssert nimIdentNormalize("foo") == "foo"
+    doAssert nimIdentNormalize("foo_bar") == "foobar"
+    doAssert nimIdentNormalize("Foo_bar") == "Foobar"
+    doAssert nimIdentNormalize("_Foo_bar") == "_foobar"
+
+  block: # bug #19500
+    doAssert "abc \0 def".find("def") == 6
+    doAssert "abc \0 def".find('d') == 6
+
+
+static: main()
+main()
diff --git a/tests/stdlib/tsugar.nim b/tests/stdlib/tsugar.nim
new file mode 100644
index 000000000..2ea96cfbb
--- /dev/null
+++ b/tests/stdlib/tsugar.nim
@@ -0,0 +1,310 @@
+discard """
+  targets: "c js"
+  matrix: "--mm:refc; --mm:orc"
+  output: '''
+x + y = 30
+'''
+"""
+import std/[sugar, algorithm, random, sets, tables, strutils, sequtils]
+import std/[syncio, assertions]
+
+type # for capture test, ref #20679
+  FooCapture = ref object
+    x: int
+
+proc mainProc() =
+  block: # bug #16967
+    var s = newSeq[proc (): int](5)
+    {.push exportc.}
+    proc bar() =
+      for i in 0 ..< s.len:
+        let foo = i + 1
+        capture foo:
+          s[i] = proc(): int = foo
+    {.pop.}
+
+    bar()
+
+    for i, p in s.pairs:
+      let foo = i + 1
+      doAssert p() == foo
+
+template main() =
+  block: # `=>`
+    block:
+      let f1 = () => 42
+      doAssert f1() == 42
+
+      let f2 = (x: int) => x + 1
+      doAssert f2(42) == 43
+
+      let f3 = (x, y: int) => x + y
+      doAssert f3(1, 2) == 3
+
+      var x = 0
+      let f4 = () => (x = 12)
+      f4()
+      doAssert x == 12
+
+      let f5 = () => (discard) # simplest proc that returns void
+      f5()
+
+    block:
+      proc call1(f: () -> int): int = f()
+      doAssert call1(() => 12) == 12
+
+      proc call2(f: int -> int): int = f(42)
+      doAssert call2(x => x) == 42
+      doAssert call2((x) => x) == 42
+      doAssert call2((x: int) => x) == 42
+
+      proc call3(f: (int, int) -> int): int = f(1, 2)
+      doAssert call3((x, y) => x + y) == 3
+      doAssert call3((x, y: int) => x + y) == 3
+      doAssert call3((x: int, y: int) => x + y) == 3
+
+      var a = 0
+      proc call4(f: int -> void) = f(42)
+      call4((x: int) => (a = x))
+      doAssert a == 42
+
+      proc call5(f: (int {.noSideEffect.} -> int)): int = f(42)
+      doAssert call5(x {.noSideEffect.} => x + 1) == 43
+
+  block: # `->`
+    doAssert $(() -> int) == "proc (): int{.closure.}"
+    doAssert $(float -> int) == "proc (i0: float): int{.closure.}"
+    doAssert $((float) -> int) == "proc (i0: float): int{.closure.}"
+    doAssert $((float, bool) -> int) == "proc (i0: float, i1: bool): int{.closure.}"
+
+    doAssert $(() -> void) == "proc (){.closure.}"
+    doAssert $(float -> void) == "proc (i0: float){.closure.}"
+    doAssert $((float) -> void) == "proc (i0: float){.closure.}"
+    doAssert $((float, bool) -> void) == "proc (i0: float, i1: bool){.closure.}"
+
+    doAssert $(() {.inline.} -> int) == "proc (): int{.inline.}"
+    doAssert $(float {.inline.} -> int) == "proc (i0: float): int{.inline.}"
+    doAssert $((float) {.inline.} -> int) == "proc (i0: float): int{.inline.}"
+    doAssert $((float, bool) {.inline.} -> int) == "proc (i0: float, i1: bool): int{.inline.}"
+
+  block: # capture
+    var closure1: () -> int
+    for i in 0 .. 10:
+      if i == 5:
+        capture i:
+          closure1 = () => i
+    doAssert closure1() == 5
+
+    var closure2: () -> (int, int)
+    for i in 0 .. 10:
+      for j in 0 .. 10:
+        if i == 5 and j == 3:
+          capture i, j:
+            closure2 = () => (i, j)
+    doAssert closure2() == (5, 3)
+
+    block: # issue #20679
+      # this should compile. Previously was broken as `var int` is an `nnkHiddenDeref`
+      # which was not handled correctly
+
+      block:
+        var x = 5
+        var s1 = newSeq[proc (): int](2)
+        proc function(data: var int) =
+          for i in 0 ..< 2:
+            data = (i+1) * data
+            capture data:
+              s1[i] = proc(): int = data
+        function(x)
+        doAssert s1[0]() == 5
+        doAssert s1[1]() == 10
+
+
+      block:
+        var y = @[5, 10]
+        var s2 = newSeq[proc (): seq[int]](2)
+        proc functionS(data: var seq[int]) =
+          for i in 0 ..< 2:
+            data.add (i+1) * 5
+            capture data:
+              s2[i] = proc(): seq[int] = data
+        functionS(y)
+        doAssert s2[0]() == @[5, 10, 5]
+        doAssert s2[1]() == @[5, 10, 5, 10]
+
+
+      template typeT(typ, val: untyped): untyped =
+        var x = val
+        var s = newSeq[proc (): typ](2)
+
+        proc functionT[T](data: var T) =
+          for i in 0 ..< 2:
+            if i == 1:
+              data = default(T)
+            capture data:
+              s[i] = proc (): T = data
+
+        functionT(x)
+        doAssert s[0]() == val
+        doAssert s[1]() == x # == default
+        doAssert s[1]() == default(typ)
+
+      block:
+        var x = 1.1
+        typeT(float, x)
+      block:
+        var x = "hello"
+        typeT(string, x)
+      block:
+        var f = FooCapture(x: 5)
+        typeT(FooCapture, f)
+
+  block: # dup
+    block dup_with_field:
+      type
+        Foo = object
+          col, pos: int
+          name: string
+
+      proc inc_col(foo: var Foo) = inc(foo.col)
+      proc inc_pos(foo: var Foo) = inc(foo.pos)
+      proc name_append(foo: var Foo, s: string) = foo.name &= s
+
+      let a = Foo(col: 1, pos: 2, name: "foo")
+      block:
+        let b = a.dup(inc_col, inc_pos):
+          _.pos = 3
+          name_append("bar")
+          inc_pos
+
+        doAssert(b == Foo(col: 2, pos: 4, name: "foobar"))
+
+      block:
+        let b = a.dup(inc_col, pos = 3, name = "bar"):
+          name_append("bar")
+          inc_pos
+
+        doAssert(b == Foo(col: 2, pos: 4, name: "barbar"))
+
+    block:
+      var a = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
+      doAssert dup(a, sort(_)) == sorted(a)
+      doAssert a.dup(sort) == sorted(a)
+      # Chaining:
+      var aCopy = a
+      aCopy.insert(10)
+      doAssert a.dup(insert(10)).dup(sort()) == sorted(aCopy)
+
+    block:
+      when nimvm: discard
+      else:
+        const b = @[0, 1, 2]
+        discard b.dup shuffle()
+        doAssert b[0] == 0
+        doAssert b[1] == 1
+
+  block: # collect
+    let data = @["bird", "word"] # if this gets stuck in your head, its not my fault
+
+    doAssert collect(newSeq, for (i, d) in data.pairs: (if i mod 2 == 0: d)) == @["bird"]
+    doAssert collect(initTable(2), for (i, d) in data.pairs: {i: d}) ==
+      {0: "bird", 1: "word"}.toTable
+    doAssert collect(initHashSet(), for d in data.items: {d}) == data.toHashSet
+
+    block:
+      let x = collect(newSeqOfCap(4)):
+          for (i, d) in data.pairs:
+            if i mod 2 == 0: d
+      doAssert x == @["bird"]
+
+    block: # bug #12874
+      let bug = collect(
+          newSeq,
+          for (i, d) in data.pairs:(
+            block:
+              if i mod 2 == 0:
+                d
+              else:
+                d & d
+            )
+      )
+      doAssert bug == @["bird", "wordword"]
+
+    block:
+      let y = collect(newSeq):
+        for (i, d) in data.pairs:
+          try: parseInt(d) except: 0
+      doAssert y == @[0, 0]
+
+    block:
+      let z = collect(newSeq):
+        for (i, d) in data.pairs:
+          case d
+          of "bird": "word"
+          else: d
+      doAssert z == @["word", "word"]
+
+    block:
+      proc tforum(): seq[int] =
+        collect(newSeq):
+          for y in 0..10:
+            if y mod 5 == 2:
+              for x in 0..y:
+                x
+      doAssert tforum() == @[0, 1, 2, 0, 1, 2, 3, 4, 5, 6, 7]
+
+    block:
+      let x = collect:
+        for d in data.items:
+          when d is int: "word"
+          else: d
+      doAssert x == @["bird", "word"]
+
+    block:
+      doAssert collect(for (i, d) in pairs(data): (i, d)) == @[(0, "bird"), (1, "word")]
+      doAssert collect(for d in data.items: (try: parseInt(d) except: 0)) == @[0, 0]
+      doAssert collect(for (i, d) in pairs(data): {i: d}) ==
+        {1: "word", 0: "bird"}.toTable
+      doAssert collect(for d in data.items: {d}) == data.toHashSet
+
+    block: # bug #14332
+      template foo =
+        discard collect(newSeq, for i in 1..3: i)
+      foo()
+
+  block: # dump
+    # symbols in templates are gensym'd
+    let
+      x {.inject.} = 10
+      y {.inject.} = 20
+    dump(x + y) # x + y = 30
+
+  block: # dumpToString
+    template square(x): untyped = x * x
+    let x {.inject.} = 10
+    doAssert dumpToString(square(x)) == "square(x): x * x = 100"
+    let s = dumpToString(doAssert 1+1 == 2)
+    doAssert "failedAssertImpl" in s
+    let s2 = dumpToString:
+      doAssertRaises(AssertionDefect): doAssert false
+    doAssert "except AssertionDefect" in s2
+
+  block: # bug #20704
+    proc test() =
+      var xs, ys: seq[int]
+      for i in 0..5:
+        xs.add(i)
+
+      xs.apply(proc (d: auto) = ys.add(d))
+      # ^ can be turned into d => ys.add(d) when we can infer void return type, #16906
+      doAssert ys == @[0, 1, 2, 3, 4, 5]
+
+    test()
+
+  mainProc()
+
+when not defined(js): # TODO fixme JS VM
+  static:
+    main()
+
+main()
diff --git a/tests/stdlib/tsums.nim b/tests/stdlib/tsums.nim
new file mode 100644
index 000000000..cf410cddf
--- /dev/null
+++ b/tests/stdlib/tsums.nim
@@ -0,0 +1,27 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/sums
+from math import pow
+import std/assertions
+
+var epsilon = 1.0
+while 1.0 + epsilon != 1.0:
+  epsilon /= 2.0
+let data = @[1.0, epsilon, -epsilon]
+doAssert sumKbn(data) == 1.0
+# doAssert sumPairs(data) != 1.0 # known to fail in 64 bits
+doAssert (1.0 + epsilon) - epsilon != 1.0
+
+var tc1: seq[float]
+for n in 1 .. 1000:
+  tc1.add 1.0 / n.float
+doAssert sumKbn(tc1) == 7.485470860550345
+doAssert sumPairs(tc1) == 7.485470860550345
+
+var tc2: seq[float]
+for n in 1 .. 1000:
+  tc2.add pow(-1.0, n.float) / n.float
+doAssert sumKbn(tc2) == -0.6926474305598203
+doAssert sumPairs(tc2) == -0.6926474305598204
diff --git a/tests/stdlib/tsysrand.nim b/tests/stdlib/tsysrand.nim
new file mode 100644
index 000000000..7b7a0fc34
--- /dev/null
+++ b/tests/stdlib/tsysrand.nim
@@ -0,0 +1,34 @@
+discard """
+  targets: "c cpp js"
+  matrix: "--experimental:vmopsDanger; --experimental:vmopsDanger --mm:refc"
+"""
+
+import std/sysrand
+import std/assertions
+
+template main() =
+  block:
+    var x = array[5, byte].default
+    doAssert urandom(x)
+
+  block:
+    var x = newSeq[byte](5)
+    doAssert urandom(x)
+
+  block:
+    var x = @[byte(0), 0, 0, 0, 0]
+    doAssert urandom(x)
+
+  block:
+    var x = @[byte(1), 2, 3, 4, 5]
+    doAssert urandom(x)
+
+  block:
+    doAssert urandom(0).len == 0
+    doAssert urandom(10).len == 10
+    doAssert urandom(20).len == 20
+    doAssert urandom(120).len == 120
+    doAssert urandom(113).len == 113
+    doAssert urandom(1234) != urandom(1234) # unlikely to fail in practice
+
+main()
diff --git a/tests/stdlib/tsystem.nim b/tests/stdlib/tsystem.nim
new file mode 100644
index 000000000..f634ce0c2
--- /dev/null
+++ b/tests/stdlib/tsystem.nim
@@ -0,0 +1,200 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c cpp js"
+"""
+
+import stdtest/testutils
+import std/[assertions, formatfloat]
+
+# TODO: in future work move existing `system` tests here, where they belong
+
+
+template main =
+  block: # closure
+    proc outer() =
+      var a = 0
+      proc inner1 = a.inc
+      proc inner2 = discard
+      doAssert inner1 is "closure"
+      doAssert inner2 isnot "closure"
+      doAssert inner1 is (proc)
+      doAssert inner2 is (proc)
+      let inner1b = inner1
+      doAssert inner1b is "closure"
+      doAssert inner1b == inner1
+    outer()
+
+  block: # rawProc, rawProc, bug #17911
+    proc outer() =
+      var a = 0
+      var b = 0
+      proc inner1() = a.inc
+      proc inner2() = a += 2
+      proc inner3() = b.inc
+      let inner1b = inner1
+      doAssert inner2 != inner1
+      doAssert inner3 != inner1
+      whenVMorJs: discard
+      do:
+        doAssert rawProc(inner1b) == rawProc(inner1)
+        doAssert rawProc(inner2) != rawProc(inner1)
+        doAssert rawProc(inner3) != rawProc(inner1)
+
+        doAssert rawEnv(inner1b) == rawEnv(inner1)
+        doAssert rawEnv(inner2) == rawEnv(inner1) # because both use `a`
+        # doAssert rawEnv(inner3) != rawEnv(inner1) # because `a` vs `b` # this doesn't hold
+    outer()
+
+  block: # system.delete
+    block:
+      var s = @[1]
+      s.delete(0)
+      doAssert s == @[]
+
+    block:
+      var s = @["foo", "bar"]
+      s.delete(1)
+      doAssert s == @["foo"]
+
+    when false:
+      var s: seq[string]
+      doAssertRaises(IndexDefect):
+        s.delete(0)
+
+    block:
+      doAssert not compiles(@["foo"].delete(-1))
+
+    block: # bug #6710
+      var s = @["foo"]
+      s.delete(0)
+      doAssert s == @[]
+
+    when false: # bug #16544: deleting out of bounds index should raise
+      var s = @["foo"]
+      doAssertRaises(IndexDefect):
+        s.delete(1)
+
+static: main()
+main()
+
+# bug #19967
+block:
+  type
+    X = object
+      a: string
+      b: set[char]
+      c: int
+      d: float
+      e: int64
+
+
+  var x = X(b: {'a'}, e: 10)
+
+  var y = move x
+
+  doAssert x.a == ""
+  doAssert x.b == {}
+  doAssert x.c == 0
+  doAssert x.d == 0.0
+  doAssert x.e == 0
+
+  reset(y)
+
+  doAssert y.a == ""
+  doAssert y.b == {}
+  doAssert y.c == 0
+  doAssert y.d == 0.0
+  doAssert y.e == 0
+
+block:
+  var x = 2
+  var y = move x
+  doAssert y == 2
+  doAssert x == 0
+  reset y
+  doAssert y == 0
+
+block:
+  type
+    X = object
+      a: string
+      b: float
+
+  var y = X(b: 1314.521)
+
+  reset(y)
+
+  doAssert y.b == 0.0
+
+block:
+  type
+    X = object
+      a: string
+      b: string
+
+  var y = X(b: "1314")
+
+  reset(y)
+
+  doAssert y.b == ""
+
+block:
+  type
+    X = object
+      a: string
+      b: seq[int]
+
+  var y = X(b: @[1, 3])
+
+  reset(y)
+
+  doAssert y.b == @[]
+
+block:
+  type
+    X = object
+      a: string
+      b: tuple[a: int, b: string]
+
+  var y = X(b: (1, "cc"))
+
+  reset(y)
+
+  doAssert y.b == (0, "")
+
+block:
+  type
+    Color = enum
+      Red, Blue, Yellow
+    X = object
+      a: string
+      b: set[Color]
+
+  var y = X(b: {Red, Blue})
+
+  reset(y)
+  doAssert y.b == {}
+
+block: # bug #20516
+  type Foo = object
+    x {.bitsize:4.}: uint
+    y {.bitsize:4.}: uint
+
+  when not defined(js):
+    let a = create(Foo)
+
+block: # bug #6549
+  when not defined(js):
+    block:
+      const v = 18446744073709551615'u64
+
+      doAssert $v == "18446744073709551615"
+      doAssert $float32(v) == "1.8446744e+19", $float32(v)
+      doAssert $float64(v) == "1.8446744073709552e+19", $float64(v)
+
+    block:
+      let v = 18446744073709551615'u64
+
+      doAssert $v == "18446744073709551615"
+      doAssert $float32(v) == "1.8446744e+19"
+      doAssert $float64(v) == "1.8446744073709552e+19"
diff --git a/tests/stdlib/ttables.nim b/tests/stdlib/ttables.nim
new file mode 100644
index 000000000..c529aff9f
--- /dev/null
+++ b/tests/stdlib/ttables.nim
@@ -0,0 +1,316 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import tables, hashes
+import std/assertions
+
+type
+  Person = object
+    firstName, lastName: string
+
+proc hash(x: Person): Hash =
+  ## Piggyback on the already available string hash proc.
+  ##
+  ## Without this proc nothing works!
+  result = x.firstName.hash !& x.lastName.hash
+  result = !$result
+
+var
+  salaries = initTable[Person, int]()
+  p1, p2: Person
+p1.firstName = "Jon"
+p1.lastName = "Ross"
+salaries[p1] = 30_000
+p2.firstName = "소진"
+p2.lastName = "박"
+salaries[p2] = 45_000
+var
+  s2 = initOrderedTable[Person, int]()
+  s3 = initCountTable[Person]()
+s2[p1] = 30_000
+s2[p2] = 45_000
+s3[p1] = 30_000
+s3[p2] = 45_000
+
+block: # Ordered table should preserve order after deletion
+  var
+    s4 = initOrderedTable[int, int]()
+  s4[1] = 1
+  s4[2] = 2
+  s4[3] = 3
+
+  var prev = 0
+  for i in s4.values:
+    doAssert(prev < i)
+    prev = i
+
+  s4.del(2)
+  doAssert(2 notin s4)
+  doAssert(s4.len == 2)
+  prev = 0
+  for i in s4.values:
+    doAssert(prev < i)
+    prev = i
+
+block: # Deletion from OrderedTable should account for collision groups. See issue #5057.
+  # The bug is reproducible only with exact keys
+  const key1 = "boy_jackpot.inGamma"
+  const key2 = "boy_jackpot.outBlack"
+
+  var t = {
+      key1: 0,
+      key2: 0
+  }.toOrderedTable()
+
+  t.del(key1)
+  doAssert(t.len == 1)
+  doAssert(key2 in t)
+
+var
+  t1 = initCountTable[string]()
+  t2 = initCountTable[string]()
+t1.inc("foo")
+t1.inc("bar", 2)
+t1.inc("baz", 3)
+t2.inc("foo", 4)
+t2.inc("bar")
+t2.inc("baz", 11)
+merge(t1, t2)
+doAssert(t1["foo"] == 5)
+doAssert(t1["bar"] == 3)
+doAssert(t1["baz"] == 14)
+
+let
+  t1r = newCountTable[string]()
+  t2r = newCountTable[string]()
+t1r.inc("foo")
+t1r.inc("bar", 2)
+t1r.inc("baz", 3)
+t2r.inc("foo", 4)
+t2r.inc("bar")
+t2r.inc("baz", 11)
+merge(t1r, t2r)
+doAssert(t1r["foo"] == 5)
+doAssert(t1r["bar"] == 3)
+doAssert(t1r["baz"] == 14)
+
+var
+  t1l = initCountTable[string]()
+  t2l = initCountTable[string]()
+t1l.inc("foo")
+t1l.inc("bar", 2)
+t1l.inc("baz", 3)
+t2l.inc("foo", 4)
+t2l.inc("bar")
+t2l.inc("baz", 11)
+
+block:
+  const testKey = "TESTKEY"
+  let t: CountTableRef[string] = newCountTable[string]()
+
+  # Before, does not compile with error message:
+  #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[testKey]
+  t.inc(testKey, 3)
+  doAssert 3 == t[testKey]
+
+block:
+  # Clear tests
+  var clearTable = newTable[int, string]()
+  clearTable[42] = "asd"
+  clearTable[123123] = "piuyqwb "
+  doAssert clearTable[42] == "asd"
+  clearTable.clear()
+  doAssert(not clearTable.hasKey(123123))
+  doAssert clearTable.getOrDefault(42) == ""
+
+block: #5482
+  var a = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable()
+  var b = newOrderedTable[string, string](initialSize = 2)
+  b["wrong?"] = "foo"
+  b["wrong?"] = "foo2"
+  doAssert a == b
+
+block: #5482
+  var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
+  var b = newOrderedTable[string, string](initialSize = 2)
+  b["wrong?"] = "foo"
+  b["wrong?"] = "foo2"
+  doAssert a == b
+
+block: #5487
+  var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
+  var b = newOrderedTable[string, string]()         # notice, default size!
+  b["wrong?"] = "foo"
+  b["wrong?"] = "foo2"
+  doAssert a == b
+
+block: #5487
+  var a = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable()
+  var b = newOrderedTable[string, string]()         # notice, default size!
+  b["wrong?"] = "foo"
+  b["wrong?"] = "foo2"
+  doAssert a == b
+
+block:
+  var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
+  var b = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable()
+  var c = newOrderedTable[string, string]()         # notice, default size!
+  c["wrong?"] = "foo"
+  c["wrong?"] = "foo2"
+  doAssert a == b
+  doAssert a == c
+
+block: #6250
+  let
+    a = {3: 1}.toOrderedTable
+    b = {3: 2}.toOrderedTable
+  doAssert((a == b) == false)
+  doAssert((b == a) == false)
+
+block: #6250
+  let
+    a = {3: 2}.toOrderedTable
+    b = {3: 2}.toOrderedTable
+  doAssert((a == b) == true)
+  doAssert((b == a) == true)
+
+block: # CountTable.smallest
+  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: #12813 #13079
+  var t = toCountTable("abracadabra")
+  doAssert len(t) == 5
+
+  t['a'] = 0 # remove a key
+  doAssert len(t) == 4
+
+block:
+  var tp: Table[string, string] = initTable[string, string]()
+  doAssert "test1" == tp.getOrDefault("test1", "test1")
+  tp["test2"] = "test2"
+  doAssert "test2" == tp.getOrDefault("test2", "test1")
+  var tr: TableRef[string, string] = newTable[string, string]()
+  doAssert "test1" == tr.getOrDefault("test1", "test1")
+  tr["test2"] = "test2"
+  doAssert "test2" == tr.getOrDefault("test2", "test1")
+  var op: OrderedTable[string, string] = initOrderedTable[string, string]()
+  doAssert "test1" == op.getOrDefault("test1", "test1")
+  op["test2"] = "test2"
+  doAssert "test2" == op.getOrDefault("test2", "test1")
+  var orf: OrderedTableRef[string, string] = newOrderedTable[string, string]()
+  doAssert "test1" == orf.getOrDefault("test1", "test1")
+  orf["test2"] = "test2"
+  doAssert "test2" == orf.getOrDefault("test2", "test1")
+
+block tableWithoutInit:
+  var
+    a: Table[string, int]
+    b: Table[string, int]
+    c: Table[string, int]
+    d: Table[string, int]
+    e: Table[string, int]
+
+  a["a"] = 7
+  doAssert a.hasKey("a")
+  doAssert a.len == 1
+  doAssert a["a"] == 7
+  a["a"] = 9
+  doAssert a.len == 1
+  doAssert a["a"] == 9
+
+  doAssert b.hasKeyOrPut("b", 5) == false
+  doAssert b.hasKey("b")
+  doAssert b.hasKeyOrPut("b", 8)
+  doAssert b["b"] == 5
+
+  doAssert c.getOrDefault("a") == 0
+  doAssert c.getOrDefault("a", 3) == 3
+  c["a"] = 6
+  doAssert c.getOrDefault("a", 3) == 6
+
+  doAssert d.mgetOrPut("a", 3) == 3
+  doAssert d.mgetOrPut("a", 6) == 3
+
+  var x = 99
+  doAssert e.pop("a", x) == false
+  doAssert x == 99
+  e["a"] = 77
+  doAssert e.pop("a", x)
+  doAssert x == 77
+
+block orderedTableWithoutInit:
+  var
+    a: OrderedTable[string, int]
+    b: OrderedTable[string, int]
+    c: OrderedTable[string, int]
+    d: OrderedTable[string, int]
+
+  a["a"] = 7
+  doAssert a.hasKey("a")
+  doAssert a.len == 1
+  doAssert a["a"] == 7
+  a["a"] = 9
+  doAssert a.len == 1
+  doAssert a["a"] == 9
+
+  doAssert b.hasKeyOrPut("b", 5) == false
+  doAssert b.hasKey("b")
+  doAssert b.hasKeyOrPut("b", 8)
+  doAssert b["b"] == 5
+
+  doAssert c.getOrDefault("a") == 0
+  doAssert c.getOrDefault("a", 3) == 3
+  c["a"] = 6
+  doAssert c.getOrDefault("a", 3) == 6
+
+  doAssert d.mgetOrPut("a", 3) == 3
+  doAssert d.mgetOrPut("a", 6) == 3
+
+block countTableWithoutInit:
+  var
+    a: CountTable[string]
+    b: CountTable[string]
+    c: CountTable[string]
+    d: CountTable[string]
+    e: CountTable[string]
+
+  a["a"] = 7
+  doAssert a.hasKey("a")
+  doAssert a.len == 1
+  doAssert a["a"] == 7
+  a["a"] = 9
+  doAssert a.len == 1
+  doAssert a["a"] == 9
+
+  doAssert b["b"] == 0
+  b.inc("b")
+  doAssert b["b"] == 1
+
+  doAssert c.getOrDefault("a") == 0
+  doAssert c.getOrDefault("a", 3) == 3
+  c["a"] = 6
+  doAssert c.getOrDefault("a", 3) == 6
+
+  e["f"] = 3
+  merge(d, e)
+  doAssert d.hasKey("f")
+  d.inc("f")
+  merge(d, e)
+  doAssert d["f"] == 7
diff --git a/tests/stdlib/ttasks.nim b/tests/stdlib/ttasks.nim
new file mode 100644
index 000000000..ba65590d9
--- /dev/null
+++ b/tests/stdlib/ttasks.nim
@@ -0,0 +1,561 @@
+discard """
+  targets: "c cpp"
+  matrix: "--gc:orc --threads:off"
+"""
+
+import std/[tasks, strformat]
+import std/assertions
+
+block:
+  var s = ""
+  proc `+`(x: int, y: string) =
+    s.add $x & y
+
+  let literal = "Nim"
+  let t = toTask(521 + literal)
+  t.invoke()
+
+  doAssert s == "521Nim"
+
+block:
+  var s = ""
+  proc `!`(x: int) =
+    s.add $x
+
+  let t = toTask !12
+  t.invoke()
+
+  doAssert s == "12"
+
+
+block:
+  block:
+    var called = 0
+    proc hello(x: static range[1 .. 5]) =
+      called += x
+
+    let b = toTask hello(3)
+    b.invoke()
+    doAssert called == 3
+    b.invoke()
+    doAssert called == 6
+
+  block:
+    var called = 0
+    proc hello(x: range[1 .. 5]) =
+      called += x
+
+    let b = toTask hello(3)
+    b.invoke()
+    doAssert called == 3
+    b.invoke()
+    doAssert called == 6
+
+  block:
+    var called = 0
+    proc hello(x: 1 .. 5) =
+      called += x
+
+    let b = toTask hello(3)
+    b.invoke()
+    doAssert called == 3
+    b.invoke()
+    doAssert called == 6
+
+  block:
+    var temp = ""
+    proc hello(a: int or seq[string]) =
+      when a is seq[string]:
+        for s in a:
+          temp.add s
+      else:
+        temp.addInt a
+
+    let x = @["1", "2", "3", "4"]
+    let b = toTask hello(x)
+    b.invoke()
+    doAssert temp == "1234"
+    b.invoke()
+    doAssert temp == "12341234"
+
+
+  block:
+    var temp = ""
+
+    proc hello(a: int or string) =
+      when a is string:
+        temp.add a
+
+    let x = "!2"
+
+    let b = toTask hello(x)
+    b.invoke()
+    doAssert temp == x
+
+  block:
+    var temp = ""
+    proc hello(a: int or string) =
+      when a is string:
+        temp.add a
+
+    let x = "!2"
+    let b = toTask hello(x)
+    b.invoke()
+    doAssert temp == x
+
+  block:
+    var x = 0
+    proc hello(typ: typedesc) =
+      x += typ(12)
+
+    let b = toTask hello(int)
+    b.invoke()
+    doAssert x == 12
+
+  block:
+    var temp = ""
+    proc hello(a: int or seq[string]) =
+      when a is seq[string]:
+        for s in a:
+          temp.add s
+
+    let x = @["1", "2", "3", "4"]
+    let b = toTask hello(x)
+    b.invoke()
+    doAssert temp == "1234"
+
+  block:
+    var temp = ""
+    proc hello(a: int | string) =
+      when a is string:
+        temp.add a
+
+    let x = "!2"
+    let b = toTask hello(x)
+    b.invoke()
+    doAssert temp == x
+
+  block:
+    var x = 0
+    proc hello(a: int | string) =
+      when a is int:
+        x = a
+
+    let b = toTask hello(12)
+    b.invoke()
+    doAssert x == 12
+
+  block:
+    var a1: seq[int]
+    var a2 = 0
+    proc hello(c: seq[int], a: int) =
+      a1 = c
+      a2 = a
+
+    let x = 12
+    var y = @[1, 3, 1, 4, 5, x, 1]
+    let b = toTask hello(y, 12)
+    b.invoke()
+
+    doAssert a1 == y
+    doAssert a2 == x
+
+  block:
+    var a1: seq[int]
+    var a2 = 0
+    proc hello(c: seq[int], a: int) =
+      a1 = c
+      a2 = a
+    var x = 2
+    let b = toTask hello(@[1, 3, 1, 4, 5, x, 1], 12)
+    b.invoke()
+
+    doAssert a1 == @[1, 3, 1, 4, 5, x, 1]
+    doAssert a2 == 12
+
+  block:
+    var a1: array[7, int]
+    var a2 = 0
+    proc hello(c: array[7, int], a: int) =
+      a1 = c
+      a2 = a
+
+    let b = toTask hello([1, 3, 1, 4, 5, 2, 1], 12)
+    b.invoke()
+
+    doAssert a1 == [1, 3, 1, 4, 5, 2, 1]
+    doAssert a2 == 12
+
+  block:
+    var a1: seq[int]
+    var a2 = 0
+    proc hello(c: seq[int], a: int) =
+      a1 = c
+      a2 = a
+
+    let b = toTask hello(@[1, 3, 1, 4, 5, 2, 1], 12)
+    b.invoke()
+
+    doAssert a1 == @[1, 3, 1, 4, 5, 2, 1]
+    doAssert a2 == 12
+
+  block:
+    var a1: seq[int]
+    var a2 = 0
+    proc hello(a: int, c: seq[int]) =
+      a1 = c
+      a2 = a
+
+    let b = toTask hello(8, @[1, 3, 1, 4, 5, 2, 1])
+    b.invoke()
+
+    doAssert a1 == @[1, 3, 1, 4, 5, 2, 1]
+    doAssert a2 == 8
+
+    let c = toTask 8.hello(@[1, 3, 1, 4, 5, 2, 1])
+    c.invoke()
+
+    doAssert a1 == @[1, 3, 1, 4, 5, 2, 1]
+    doAssert a2 == 8
+
+  block:
+    var a1: seq[seq[int]]
+    var a2: int
+    proc hello(a: int, c: openArray[seq[int]]) =
+      a1 = @c
+      a2 = a
+
+    let b = toTask hello(8, @[@[3], @[4], @[5], @[6], @[12], @[7]])
+    b.invoke()
+
+    doAssert a1 ==  @[@[3], @[4], @[5], @[6], @[12], @[7]]
+    doAssert a2 == 8
+
+  block:
+    var a1: seq[int]
+    var a2: int
+    proc hello(a: int, c: openArray[int]) =
+      a1 = @c
+      a2 = a
+
+    let b = toTask hello(8, @[3, 4, 5, 6, 12, 7])
+    b.invoke()
+
+    doAssert a1 == @[3, 4, 5, 6, 12, 7]
+    doAssert a2 == 8
+
+  block:
+    var a1: seq[int]
+    var a2: int
+    proc hello(a: int, c: static varargs[int]) =
+      a1 = @c
+      a2 = a
+
+    let b = toTask hello(8, @[3, 4, 5, 6, 12, 7])
+    b.invoke()
+
+    doAssert a1 == @[3, 4, 5, 6, 12, 7]
+    doAssert a2 == 8
+
+  block:
+    var a1: seq[int]
+    var a2: int
+    proc hello(a: int, c: static varargs[int]) =
+      a1 = @c
+      a2 = a
+
+    let b = toTask hello(8, [3, 4, 5, 6, 12, 7])
+    b.invoke()
+
+    doAssert a1 == @[3, 4, 5, 6, 12, 7]
+    doAssert a2 == 8
+
+  block:
+    var a1: seq[int]
+    var a2: int
+    proc hello(a: int, c: varargs[int]) =
+      a1 = @c
+      a2 = a
+
+    let x = 12
+    let b = toTask hello(8, 3, 4, 5, 6, x, 7)
+    b.invoke()
+
+    doAssert a1 == @[3, 4, 5, 6, 12, 7]
+    doAssert a2 == 8
+
+  block:
+    var x = 12
+
+    proc hello(x: ptr int) =
+      x[] += 12
+
+    let b = toTask hello(addr x)
+    b.invoke()
+
+    doAssert x == 24
+
+    let c = toTask x.addr.hello
+    invoke(c)
+
+    doAssert x == 36
+  block:
+    type
+      Test = ref object
+        id: int
+
+    var x = 0
+    proc hello(a: int, c: static Test) =
+      x += a
+      x += c.id
+
+    let b = toTask hello(8, Test(id: 12))
+    b.invoke()
+
+    doAssert x == 20
+
+  block:
+    type
+      Test = object
+        id: int
+
+    var x = 0
+    proc hello(a: int, c: static Test) =
+      x += a
+      x += c.id
+
+    let b = toTask hello(8, Test(id: 12))
+    b.invoke()
+    doAssert x == 20
+
+  block:
+    var x = 0
+    proc hello(a: int, c: static seq[int]) =
+      x += a
+      for i in c:
+        x += i
+
+    let b = toTask hello(8, @[3, 4, 5, 6, 12, 7])
+    b.invoke()
+    doAssert x == 45
+
+  block:
+    var x = 0
+    proc hello(a: int, c: static array[5, int]) =
+      x += a
+      for i in c:
+        x += i
+
+    let b = toTask hello(8, [3, 4, 5, 6, 12])
+    b.invoke()
+    doAssert x == 38
+
+  block:
+    var aVal = 0
+    var cVal = ""
+
+    proc hello(a: int, c: static string) =
+      aVal += a
+      cVal.add c
+
+    var x = 1314
+    let b = toTask hello(x, "hello")
+    b.invoke()
+
+    doAssert aVal == x
+    doAssert cVal == "hello"
+
+  block:
+    var aVal = ""
+
+    proc hello(a: static string) =
+      aVal.add a
+    let b = toTask hello("hello")
+    b.invoke()
+
+    doAssert aVal == "hello"
+
+  block:
+    var aVal = 0
+    var cVal = ""
+
+    proc hello(a: static int, c: static string) =
+      aVal += a
+      cVal.add c
+    let b = toTask hello(8, "hello")
+    b.invoke()
+
+    doAssert aVal == 8
+    doAssert cVal == "hello"
+
+  block:
+    var aVal = 0
+    var cVal = 0
+
+    proc hello(a: static int, c: int) =
+      aVal += a
+      cVal += c
+
+    let b = toTask hello(c = 0, a = 8)
+    b.invoke()
+
+    doAssert aVal == 8
+    doAssert cVal == 0
+
+  block:
+    var aVal = 0
+    var cVal = 0
+
+    proc hello(a: int, c: static int) =
+      aVal += a
+      cVal += c
+
+    let b = toTask hello(c = 0, a = 8)
+    b.invoke()
+
+    doAssert aVal == 8
+    doAssert cVal == 0
+
+  block:
+    var aVal = 0
+    var cVal = 0
+
+    proc hello(a: static int, c: static int) =
+      aVal += a
+      cVal += c
+
+    let b = toTask hello(0, 8)
+    b.invoke()
+
+    doAssert aVal == 0
+    doAssert cVal == 8
+
+  block:
+    var temp = ""
+    proc hello(x: int, y: seq[string], d = 134) =
+      temp = fmt"{x=} {y=} {d=}"
+
+
+    proc main() =
+      var x = @["23456"]
+      let t = toTask hello(2233, x)
+      t.invoke()
+
+      doAssert temp == """x=2233 y=@["23456"] d=134"""
+
+    main()
+
+
+  block:
+    var temp = ""
+    proc hello(x: int, y: seq[string], d = 134) =
+      temp.add fmt"{x=} {y=} {d=}"
+
+    proc ok() =
+      temp = "ok"
+
+    proc main() =
+      var x = @["23456"]
+      let t = toTask hello(2233, x)
+      t.invoke()
+      t.invoke()
+
+      doAssert temp == """x=2233 y=@["23456"] d=134x=2233 y=@["23456"] d=134"""
+
+    main()
+
+    var x = @["4"]
+    let m = toTask hello(2233, x, 7)
+    m.invoke()
+
+    doAssert temp == """x=2233 y=@["23456"] d=134x=2233 y=@["23456"] d=134x=2233 y=@["4"] d=7"""
+
+    let n = toTask ok()
+    n.invoke()
+
+    doAssert temp == "ok"
+
+  block:
+    var called = 0
+    block:
+      proc hello() =
+        inc called
+
+      let a = toTask hello()
+      invoke(a)
+
+    doAssert called == 1
+
+    block:
+      proc hello(a: int) =
+        inc called, a
+
+      let b = toTask hello(13)
+      let c = toTask hello(a = 14)
+      b.invoke()
+      c.invoke()
+
+    doAssert called == 28
+
+    block:
+      proc hello(a: int, c: int) =
+        inc called, a
+
+      let b = toTask hello(c = 0, a = 8)
+      b.invoke()
+
+    doAssert called == 36
+
+  block:
+    proc returnsSomething(a, b: int): int = a + b
+
+    proc noArgsButReturnsSomething(): string = "abcdef"
+
+    proc testReturnValues() =
+      let t = toTask returnsSomething(2233, 11)
+      var res: int
+      t.invoke(addr res)
+      doAssert res == 2233+11
+
+      let tb = toTask noArgsButReturnsSomething()
+      var resB: string
+      tb.invoke(addr resB)
+      doAssert resB == "abcdef"
+
+    testReturnValues()
+
+
+block: # bug #23635
+  block:
+    type
+      Store = object
+        run: proc (a: int) {.nimcall, gcsafe.}
+
+    block:
+      var count = 0
+      proc hello(a: int) =
+        inc count, a
+
+      var store = Store()
+      store.run = hello
+
+      let b = toTask store.run(13)
+      b.invoke()
+      doAssert count == 13
+
+  block:
+    type
+      Store = object
+        run: proc () {.nimcall, gcsafe.}
+
+    block:
+      var count = 0
+      proc hello() =
+        inc count, 1
+
+      var store = Store()
+      store.run = hello
+
+      let b = toTask store.run()
+      b.invoke()
+      doAssert count == 1
diff --git a/tests/stdlib/ttempfiles.nim b/tests/stdlib/ttempfiles.nim
new file mode 100644
index 000000000..352788c42
--- /dev/null
+++ b/tests/stdlib/ttempfiles.nim
@@ -0,0 +1,51 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  joinable: false # not strictly necessary
+"""
+
+import std/tempfiles
+import std/[os, nre]
+import std/[assertions, syncio]
+
+const
+  prefix = "D20210502T100442" # safety precaution to only affect files/dirs with this prefix
+  suffix = ".tmp"
+
+block:
+  var t1 = createTempFile(prefix, suffix)
+  var t2 = createTempFile(prefix, suffix)
+  defer:
+    close(t1.cfile)
+    close(t2.cfile)
+    removeFile(t1.path)
+    removeFile(t2.path)
+
+  doAssert t1.path != t2.path
+
+  let s = "1234"
+  write(t1.cfile, s)
+  doAssert readAll(t2.cfile) == ""
+  doAssert readAll(t1.cfile) == ""
+  t1.cfile.setFilePos 0
+  doAssert readAll(t1.cfile) == s
+
+block: # createTempDir
+  doAssertRaises(OSError): discard createTempDir(prefix, suffix, "nonexistent")
+
+  block:
+    let dir1 = createTempDir(prefix, suffix)
+    let dir2 = createTempDir(prefix, suffix)
+    defer:
+      removeDir(dir1)
+      removeDir(dir2)
+    doAssert dir1 != dir2
+
+    doAssert dirExists(dir1)
+    doAssert dir1.lastPathPart.contains(re"^D20210502T100442(\w+).tmp$")
+    doAssert dir1.parentDir == getTempDir().normalizePathEnd()
+
+  block:
+    let dir3 = createTempDir(prefix, "_mytmp", ".")
+    doAssert dir3.lastPathPart.contains(re"^D20210502T100442(\w+)_mytmp$")
+    doAssert dir3.parentDir == "." # not getCurrentDir(): we honor the absolute/relative state of input `dir`
+    removeDir(dir3)
diff --git a/tests/stdlib/tterminal.nim b/tests/stdlib/tterminal.nim
new file mode 100644
index 000000000..16365e71c
--- /dev/null
+++ b/tests/stdlib/tterminal.nim
@@ -0,0 +1,7 @@
+discard """
+  action: compile
+"""
+import terminal, colors
+
+styledEcho fgColor, colRed, "Test"
+styledEcho bgColor, colBlue, "Test"
diff --git a/tests/stdlib/tterminal_12759.nim b/tests/stdlib/tterminal_12759.nim
new file mode 100644
index 000000000..e9ea3127c
--- /dev/null
+++ b/tests/stdlib/tterminal_12759.nim
@@ -0,0 +1,11 @@
+discard """
+  action: "compile"
+"""
+
+import terminal
+import std/syncio
+
+proc test() {.raises:[IOError, ValueError].} =
+  setBackgroundColor(stdout, bgRed)
+
+test()
diff --git a/tests/stdlib/tterminal_15874.nim b/tests/stdlib/tterminal_15874.nim
new file mode 100644
index 000000000..c3455c350
--- /dev/null
+++ b/tests/stdlib/tterminal_15874.nim
@@ -0,0 +1,8 @@
+discard """
+  cmd: "nim c --app:console $file"
+  action: "compile"
+"""
+
+import terminal
+
+writeStyled("hello", {styleBright})
diff --git a/tests/stdlib/ttestutils.nim b/tests/stdlib/ttestutils.nim
new file mode 100644
index 000000000..0f8bf16cf
--- /dev/null
+++ b/tests/stdlib/ttestutils.nim
@@ -0,0 +1,40 @@
+import stdtest/testutils
+import std/assertions
+
+block: # assertAll
+  assertAll:
+    1+1 == 2
+    var a = 3
+    a == 3
+
+  doAssertRaises(AssertionDefect):
+    assertAll:
+      1+1 == 2
+      var a = 3
+      a == 4
+
+block: # greedyOrderedSubsetLines
+  assertAll:
+    greedyOrderedSubsetLines("a1\na3", "a0\na1\na2\na3\na4")
+    not greedyOrderedSubsetLines("a3\na1", "a0\na1\na2\na3\na4") # out of order
+    not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4") # a5 not in lhs
+
+    not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4\nprefix:a5")
+    not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4\na5:suffix")
+    not greedyOrderedSubsetLines("a5", "a0\na1\na2\na3\na4\nprefix:a5")
+    not greedyOrderedSubsetLines("a5", "a0\na1\na2\na3\na4\na5:suffix")
+
+block: # greedyOrderedSubsetLines with allowPrefixMatch = true
+  template fn(a, b): bool =
+    greedyOrderedSubsetLines(a, b, allowPrefixMatch = true)
+  assertAll:
+    fn("a1\na3", "a0\na1\na2\na3_suffix\na4")
+    not fn("a1\na3", "a0\na1\na2\nprefix_a3\na4")
+    # these are same as above, could be refactored
+    not fn("a3\na1", "a0\na1\na2\na3\na4") # out of order
+    not fn("a1\na5", "a0\na1\na2\na3\na4") # a5 not in lhs
+
+    not fn("a1\na5", "a0\na1\na2\na3\na4\nprefix:a5")
+    fn("a1\na5", "a0\na1\na2\na3\na4\na5:suffix")
+    not fn("a5", "a0\na1\na2\na3\na4\nprefix:a5")
+    fn("a5", "a0\na1\na2\na3\na4\na5:suffix")
diff --git a/tests/stdlib/tthreadpool.nim b/tests/stdlib/tthreadpool.nim
new file mode 100644
index 000000000..1947074be
--- /dev/null
+++ b/tests/stdlib/tthreadpool.nim
@@ -0,0 +1,14 @@
+discard """
+  matrix: "--mm:arc; --mm:refc"
+  disabled: "freebsd"
+  output: "42"
+"""
+import std/assertions
+from std/threadpool import spawn, `^`, sync
+block: # bug #12005
+  proc doworkok(i: int) {.thread.} = echo i
+  spawn(doworkok(42))
+  sync() # this works when returning void!
+
+  proc doworkbad(i: int): int {.thread.} = i
+  doAssert ^spawn(doworkbad(42)) == 42 # bug was here
diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim
index 4ab3ba581..0f04168dc 100644
--- a/tests/stdlib/ttimes.nim
+++ b/tests/stdlib/ttimes.nim
@@ -1,123 +1,34 @@
-# test the new time module
 discard """
-  file: "ttimes.nim"
-  output: '''[Suite] ttimes
-'''
+  matrix: "--mm:refc; --mm:orc; --backend:js --jsbigint64:on; --backend:js --jsbigint64:off -d:nimStringHash2"
 """
 
-import
-  times, os, strutils, unittest
-
-# $ date --date='@2147483647'
-# Tue 19 Jan 03:14:07 GMT 2038
-
-proc checkFormat(t: DateTime, format, expected: string) =
-  let actual = t.format(format)
-  if actual != expected:
-    echo "Formatting failure!"
-    echo "expected: ", expected
-    echo "actual  : ", actual
-    doAssert false
-
-let t = fromUnix(2147483647).utc
-t.checkFormat("ddd dd MMM hh:mm:ss yyyy", "Tue 19 Jan 03:14:07 2038")
-t.checkFormat("ddd ddMMMhh:mm:ssyyyy", "Tue 19Jan03:14:072038")
-t.checkFormat("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
-  " ss t tt y yy yyy yyyy yyyyy z zz zzz",
-  "19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 +0 +00 +00:00")
-
-t.checkFormat("yyyyMMddhhmmss", "20380119031407")
-
-# issue 7620
-let t7620_am = parse("4/15/2017 12:01:02 AM +0", "M/d/yyyy' 'h:mm:ss' 'tt' 'z", utc())
-t7620_am.checkFormat("M/d/yyyy' 'h:mm:ss' 'tt' 'z", "4/15/2017 12:01:02 AM +0")
-let t7620_pm = parse("4/15/2017 12:01:02 PM +0", "M/d/yyyy' 'h:mm:ss' 'tt' 'z", utc())
-t7620_pm.checkFormat("M/d/yyyy' 'h:mm:ss' 'tt' 'z", "4/15/2017 12:01:02 PM +0")
-
-let t2 = fromUnix(160070789).utc # Mon 27 Jan 16:06:29 GMT 1975
-t2.checkFormat("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
-  " ss t tt y yy yyy yyyy yyyyy z zz zzz",
-  "27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 +0 +00 +00:00")
-
-var t4 = fromUnix(876124714).utc # Mon  6 Oct 08:58:34 BST 1997
-t4.checkFormat("M MM MMM MMMM", "10 10 Oct October")
-
-# Interval tests
-(t4 - initTimeInterval(years = 2)).checkFormat("yyyy", "1995")
-(t4 - initTimeInterval(years = 7, minutes = 34, seconds = 24)).checkFormat("yyyy mm ss", "1990 24 10")
-
-# checking dayOfWeek matches known days
-doAssert getDayOfWeek(01, mJan, 0000) == dSat
-doAssert getDayOfWeek(01, mJan, -0023) == dSat
-doAssert getDayOfWeek(21, mSep, 1900) == dFri
-doAssert getDayOfWeek(01, mJan, 1970) == dThu
-doAssert getDayOfWeek(21, mSep, 1970) == dMon
-doAssert getDayOfWeek(01, mJan, 2000) == dSat
-doAssert getDayOfWeek(01, mJan, 2021) == dFri
-
-# toUnix tests with GM timezone
-let t4L = fromUnix(876124714).utc
-doAssert toUnix(toTime(t4L)) == 876124714
-doAssert toUnix(toTime(t4L)) + t4L.utcOffset == toUnix(toTime(t4))
-
-# adding intervals
-var
-  a1L = toUnix(toTime(t4L + initTimeInterval(hours = 1))) + t4L.utcOffset
-  a1G = toUnix(toTime(t4)) + 60 * 60
-doAssert a1L == a1G
-
-# subtracting intervals
-a1L = toUnix(toTime(t4L - initTimeInterval(hours = 1))) + t4L.utcOffset
-a1G = toUnix(toTime(t4)) - (60 * 60)
-doAssert a1L == a1G
-
-# Comparison between Time objects should be detected by compiler
-# as 'noSideEffect'.
-proc cmpTimeNoSideEffect(t1: Time, t2: Time): bool {.noSideEffect.} =
-  result = t1 == t2
-doAssert cmpTimeNoSideEffect(0.fromUnix, 0.fromUnix)
-# Additionally `==` generic for seq[T] has explicit 'noSideEffect' pragma
-# so we can check above condition by comparing seq[Time] sequences
-let seqA: seq[Time] = @[]
-let seqB: seq[Time] = @[]
-doAssert seqA == seqB
-
-for tz in [
-    (0, "+0", "+00", "+00:00"), # UTC
-    (-3600, "+1", "+01", "+01:00"), # CET
-    (-39600, "+11", "+11", "+11:00"), # two digits
-    (-1800, "+0", "+00", "+00:30"), # half an hour
-    (7200, "-2", "-02", "-02:00"), # positive
-    (38700, "-10", "-10", "-10:45")]: # positive with three quaters hour
-  let ti = DateTime(month: mJan, monthday: 1, utcOffset: tz[0])
-  doAssert ti.format("z") == tz[1]
-  doAssert ti.format("zz") == tz[2]
-  doAssert ti.format("zzz") == tz[3]
-
-block countLeapYears:
-  # 1920, 2004 and 2020 are leap years, and should be counted starting at the following year
-  doAssert countLeapYears(1920) + 1 == countLeapYears(1921)
-  doAssert countLeapYears(2004) + 1 == countLeapYears(2005)
-  doAssert countLeapYears(2020) + 1 == countLeapYears(2021)
-
-block timezoneConversion:
-  var l = now()
-  let u = l.utc
-  l = u.local
-
-  doAssert l.timezone == local()
-  doAssert u.timezone == utc()
+import times, strutils, unittest
+import std/assertions
+
+when not defined(js):
+  import os
+
+proc staticTz(hours, minutes, seconds: int = 0): Timezone {.noSideEffect.} =
+  let offset = hours * 3600 + minutes * 60 + seconds
+
+  proc zonedTimeFromAdjTime(adjTime: Time): ZonedTime =
+    result.isDst = false
+    result.utcOffset = offset
+    result.time = adjTime + initDuration(seconds = offset)
+
+  proc zonedTimeFromTime(time: Time): ZonedTime =
+    result.isDst = false
+    result.utcOffset = offset
+    result.time = time
+
+  newTimezone("", zonedTimeFromTime, zonedTimeFromAdjTime)
 
 template parseTest(s, f, sExpected: string, ydExpected: int) =
   let
     parsed = s.parse(f, utc())
     parsedStr = $parsed
   check parsedStr == sExpected
-  if parsed.yearday != ydExpected:
-    echo s
-    echo parsed.repr
-    echo parsed.yearday, " exp: ", ydExpected
-  check(parsed.yearday == ydExpected)
+  check parsed.yearday == ydExpected
 
 template parseTestExcp(s, f: string) =
   expect ValueError:
@@ -130,55 +41,60 @@ template parseTestTimeOnly(s, f, sExpected: string) =
 # explicit timezone offsets in all tests.
 template runTimezoneTests() =
   parseTest("Tuesday at 09:04am on Dec 15, 2015 +0",
-      "dddd at hh:mmtt on MMM d, yyyy z", "2015-12-15T09:04:00+00:00", 348)
+      "dddd 'at' hh:mmtt 'on' MMM d, yyyy z", "2015-12-15T09:04:00Z", 348)
   # ANSIC       = "Mon Jan _2 15:04:05 2006"
   parseTest("Thu Jan 12 15:04:05 2006 +0", "ddd MMM dd HH:mm:ss yyyy z",
-      "2006-01-12T15:04:05+00:00", 11)
+      "2006-01-12T15:04:05Z", 11)
   # UnixDate    = "Mon Jan _2 15:04:05 MST 2006"
   parseTest("Thu Jan 12 15:04:05 2006 +0", "ddd MMM dd HH:mm:ss yyyy z",
-      "2006-01-12T15:04:05+00:00", 11)
+      "2006-01-12T15:04:05Z", 11)
   # RubyDate    = "Mon Jan 02 15:04:05 -0700 2006"
   parseTest("Mon Feb 29 15:04:05 -07:00 2016 +0", "ddd MMM dd HH:mm:ss zzz yyyy z",
-      "2016-02-29T15:04:05+00:00", 59) # leap day
+      "2016-02-29T15:04:05Z", 59) # leap day
   # RFC822      = "02 Jan 06 15:04 MST"
   parseTest("12 Jan 16 15:04 +0", "dd MMM yy HH:mm z",
-      "2016-01-12T15:04:00+00:00", 11)
+      "2016-01-12T15:04:00Z", 11)
   # RFC822Z     = "02 Jan 06 15:04 -0700" # RFC822 with numeric zone
   parseTest("01 Mar 16 15:04 -07:00", "dd MMM yy HH:mm zzz",
-      "2016-03-01T22:04:00+00:00", 60) # day after february in leap year
+      "2016-03-01T22:04:00Z", 60) # day after february in leap year
   # RFC850      = "Monday, 02-Jan-06 15:04:05 MST"
   parseTest("Monday, 12-Jan-06 15:04:05 +0", "dddd, dd-MMM-yy HH:mm:ss z",
-      "2006-01-12T15:04:05+00:00", 11)
+      "2006-01-12T15:04:05Z", 11)
   # RFC1123     = "Mon, 02 Jan 2006 15:04:05 MST"
   parseTest("Sun, 01 Mar 2015 15:04:05 +0", "ddd, dd MMM yyyy HH:mm:ss z",
-      "2015-03-01T15:04:05+00:00", 59) # day after february in non-leap year
+      "2015-03-01T15:04:05Z", 59) # day after february in non-leap year
   # RFC1123Z    = "Mon, 02 Jan 2006 15:04:05 -0700" # RFC1123 with numeric zone
   parseTest("Thu, 12 Jan 2006 15:04:05 -07:00", "ddd, dd MMM yyyy HH:mm:ss zzz",
-      "2006-01-12T22:04:05+00:00", 11)
+      "2006-01-12T22:04:05Z", 11)
   # RFC3339     = "2006-01-02T15:04:05Z07:00"
-  parseTest("2006-01-12T15:04:05Z-07:00", "yyyy-MM-ddTHH:mm:ssZzzz",
-      "2006-01-12T22:04:05+00:00", 11)
   parseTest("2006-01-12T15:04:05Z-07:00", "yyyy-MM-dd'T'HH:mm:ss'Z'zzz",
-      "2006-01-12T22:04:05+00:00", 11)
+      "2006-01-12T22:04:05Z", 11)
   # RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
   parseTest("2006-01-12T15:04:05.999999999Z-07:00",
-      "yyyy-MM-ddTHH:mm:ss.999999999Zzzz", "2006-01-12T22:04:05+00:00", 11)
+      "yyyy-MM-dd'T'HH:mm:ss.'999999999Z'zzz", "2006-01-12T22:04:05Z", 11)
   for tzFormat in ["z", "zz", "zzz"]:
     # formatting timezone as 'Z' for UTC
     parseTest("2001-01-12T22:04:05Z", "yyyy-MM-dd'T'HH:mm:ss" & tzFormat,
-        "2001-01-12T22:04:05+00:00", 11)
+        "2001-01-12T22:04:05Z", 11)
+  # timezone offset formats
+  parseTest("2001-01-12T15:04:05 +7", "yyyy-MM-dd'T'HH:mm:ss z",
+      "2001-01-12T08:04:05Z", 11)
+  parseTest("2001-01-12T15:04:05 +07", "yyyy-MM-dd'T'HH:mm:ss zz",
+      "2001-01-12T08:04:05Z", 11)
+  parseTest("2001-01-12T15:04:05 +07:00", "yyyy-MM-dd'T'HH:mm:ss zzz",
+      "2001-01-12T08:04:05Z", 11)
+  parseTest("2001-01-12T15:04:05 +07:30:59", "yyyy-MM-dd'T'HH:mm:ss zzzz",
+      "2001-01-12T07:33:06Z", 11)
+  parseTest("2001-01-12T15:04:05 +0700", "yyyy-MM-dd'T'HH:mm:ss ZZZ",
+      "2001-01-12T08:04:05Z", 11)
+  parseTest("2001-01-12T15:04:05 +073059", "yyyy-MM-dd'T'HH:mm:ss ZZZZ",
+      "2001-01-12T07:33:06Z", 11)
   # Kitchen     = "3:04PM"
   parseTestTimeOnly("3:04PM", "h:mmtt", "15:04:00")
-  #when not defined(testing):
-  #  echo "Kitchen: " & $s.parse(f)
-  #  var ti = timeToTimeInfo(getTime())
-  #  echo "Todays date after decoding: ", ti
-  #  var tint = timeToTimeInterval(getTime())
-  #  echo "Todays date after decoding to interval: ", tint
 
   # Bug with parse not setting DST properly if the current local DST differs from
   # the date being parsed. Need to test parse dates both in and out of DST. We
-  # are testing that be relying on the fact that tranforming a TimeInfo to a Time
+  # are testing that be relying on the fact that transforming a TimeInfo to a Time
   # and back again will correctly set the DST value. With the incorrect parse
   # behavior this will introduce a one hour offset from the named time and the
   # parsed time if the DST value differs between the current time and the date we
@@ -195,48 +111,65 @@ template runTimezoneTests() =
     let
       parsedJan = parse("2016-01-05 04:00:00+01:00", "yyyy-MM-dd HH:mm:sszzz")
       parsedJul = parse("2016-07-01 04:00:00+01:00", "yyyy-MM-dd HH:mm:sszzz")
-    doAssert toTime(parsedJan).toUnix == 1451962800
-    doAssert toTime(parsedJul).toUnix == 1467342000
+    check toTime(parsedJan).toUnix == 1451962800
+    check toTime(parsedJul).toUnix == 1467342000
+
+template usingTimezone(tz: string, body: untyped) =
+  when defined(linux) or defined(macosx):
+    let oldZone = getEnv("TZ")
+    putEnv("TZ", tz)
+    body
+    putEnv("TZ", oldZone)
 
-suite "ttimes":
+block: # ttimes
 
   # Generate tests for multiple timezone files where available
   # Set the TZ env var for each test
-  when defined(Linux) or defined(macosx):
-    const tz_dir = "/usr/share/zoneinfo"
+  when defined(linux) or defined(macosx):
+    let tz_dir = getEnv("TZDIR", "/usr/share/zoneinfo")
     const f = "yyyy-MM-dd HH:mm zzz"
-    
-    let orig_tz = getEnv("TZ")
+
     var tz_cnt = 0
-    for tz_fn in walkFiles(tz_dir & "/**/*"):
-      if symlinkExists(tz_fn) or tz_fn.endsWith(".tab") or
-          tz_fn.endsWith(".list"):
+    for timezone in walkFiles(tz_dir & "/**/*"):
+      if symlinkExists(timezone) or timezone.endsWith(".tab") or
+          timezone.endsWith(".list"):
         continue
 
-      test "test for " & tz_fn:
-        tz_cnt.inc
-        putEnv("TZ", tz_fn)
-        runTimezoneTests()
+      usingTimezone(timezone):
+        test "test for " & timezone:
+          tz_cnt.inc
+          runTimezoneTests()
 
     test "enough timezone files tested":
       check tz_cnt > 10
 
-    test "dst handling":
-      putEnv("TZ", "Europe/Stockholm")
-      # In case of an impossible time, the time is moved to after the impossible time period
-      check initDateTime(26, mMar, 2017, 02, 30, 00).format(f) == "2017-03-26 03:30 +02:00"
-      # In case of an ambiguous time, the earlier time is choosen
-      check initDateTime(29, mOct, 2017, 02, 00, 00).format(f) == "2017-10-29 02:00 +02:00"
+  else:
+    # not on Linux or macosx: run in the local timezone only
+    test "parseTest":
+      runTimezoneTests()
+
+  block: # dst handling
+    usingTimezone("Europe/Stockholm"):
+      # In case of an impossible time, the time is moved to after the
+      # impossible time period
+      check initDateTime(26, mMar, 2017, 02, 30, 00).format(f) ==
+        "2017-03-26 03:30 +02:00"
+      # In case of an ambiguous time, the earlier time is chosen
+      check initDateTime(29, mOct, 2017, 02, 00, 00).format(f) ==
+        "2017-10-29 02:00 +02:00"
       # These are just dates on either side of the dst switch
-      check initDateTime(29, mOct, 2017, 01, 00, 00).format(f) == "2017-10-29 01:00 +02:00"
+      check initDateTime(29, mOct, 2017, 01, 00, 00).format(f) ==
+        "2017-10-29 01:00 +02:00"
       check initDateTime(29, mOct, 2017, 01, 00, 00).isDst
-      check initDateTime(29, mOct, 2017, 03, 01, 00).format(f) == "2017-10-29 03:01 +01:00"
+      check initDateTime(29, mOct, 2017, 03, 01, 00).format(f) ==
+        "2017-10-29 03:01 +01:00"
       check (not initDateTime(29, mOct, 2017, 03, 01, 00).isDst)
-      
-      check initDateTime(21, mOct, 2017, 01, 00, 00).format(f) == "2017-10-21 01:00 +02:00"
 
-    test "issue #6520":
-      putEnv("TZ", "Europe/Stockholm")
+      check initDateTime(21, mOct, 2017, 01, 00, 00).format(f) ==
+        "2017-10-21 01:00 +02:00"
+
+  block: # issue #6520
+    usingTimezone("Europe/Stockholm"):
       var local = fromUnix(1469275200).local
       var utc = fromUnix(1469275200).utc
 
@@ -244,128 +177,138 @@ suite "ttimes":
       local.utcOffset = 0
       check claimedOffset == utc.toTime - local.toTime
 
-    test "issue #5704":
-      putEnv("TZ", "Asia/Seoul")
-      let diff = parse("19700101-000000", "yyyyMMdd-hhmmss").toTime - parse("19000101-000000", "yyyyMMdd-hhmmss").toTime
+  block: # issue #5704
+    usingTimezone("Asia/Seoul"):
+      let diff = parse("19700101-000000", "yyyyMMdd-hhmmss").toTime -
+        parse("19000101-000000", "yyyyMMdd-hhmmss").toTime
       check diff == initDuration(seconds = 2208986872)
 
-    test "issue #6465":
-      putEnv("TZ", "Europe/Stockholm")      
+  block: # issue #6465
+    usingTimezone("Europe/Stockholm"):
       let dt = parse("2017-03-25 12:00", "yyyy-MM-dd hh:mm")
       check $(dt + initTimeInterval(days = 1)) == "2017-03-26T12:00:00+02:00"
-      check $(dt + initDuration(days = 1)) == "2017-03-26T13:00:00+02:00"      
-
-    test "datetime before epoch":
-      check $fromUnix(-2147483648).utc == "1901-12-13T20:45:52+00:00"
-
-    test "adding/subtracting time across dst":
-      putenv("TZ", "Europe/Stockholm")
+      check $(dt + initDuration(days = 1)) == "2017-03-26T13:00:00+02:00"
 
+  block: # adding/subtracting time across dst
+    usingTimezone("Europe/Stockholm"):
       let dt1 = initDateTime(26, mMar, 2017, 03, 00, 00)
       check $(dt1 - 1.seconds) == "2017-03-26T01:59:59+01:00"
 
       var dt2 = initDateTime(29, mOct, 2017, 02, 59, 59)
       check  $(dt2 + 1.seconds) == "2017-10-29T02:00:00+01:00"
 
-    putEnv("TZ", orig_tz)
+  block: # datetime before epoch
+    check $fromUnix(-2147483648).utc == "1901-12-13T20:45:52Z"
 
-  else:
-    # not on Linux or macosx: run in the local timezone only
-    test "parseTest":
-      runTimezoneTests()
-
-  test "incorrect inputs: empty string":
+  block: # incorrect inputs: empty string
     parseTestExcp("", "yyyy-MM-dd")
 
-  test "incorrect inputs: year":
+  block: # incorrect inputs: year
     parseTestExcp("20-02-19", "yyyy-MM-dd")
 
-  test "incorrect inputs: month number":
+  block: # incorrect inputs: month number
     parseTestExcp("2018-2-19", "yyyy-MM-dd")
 
-  test "incorrect inputs: month name":
+  block: # incorrect inputs: month name
     parseTestExcp("2018-Fe", "yyyy-MMM-dd")
 
-  test "incorrect inputs: day":
+  block: # incorrect inputs: day
     parseTestExcp("2018-02-1", "yyyy-MM-dd")
 
-  test "incorrect inputs: day of week":
+  block: # incorrect inputs: day of week
     parseTestExcp("2018-Feb-Mo", "yyyy-MMM-ddd")
 
-  test "incorrect inputs: hour":
+  block: # incorrect inputs: hour
     parseTestExcp("2018-02-19 1:30", "yyyy-MM-dd hh:mm")
 
-  test "incorrect inputs: minute":
+  block: # incorrect inputs: minute
     parseTestExcp("2018-02-19 16:3", "yyyy-MM-dd hh:mm")
 
-  test "incorrect inputs: second":
+  block: # incorrect inputs: second
     parseTestExcp("2018-02-19 16:30:0", "yyyy-MM-dd hh:mm:ss")
 
-  test "incorrect inputs: timezone (z)":
+  block: # incorrect inputs: timezone (z)
     parseTestExcp("2018-02-19 16:30:00 ", "yyyy-MM-dd hh:mm:ss z")
 
-  test "incorrect inputs: timezone (zz) 1":
+  block: # incorrect inputs: timezone (zz) 1
     parseTestExcp("2018-02-19 16:30:00 ", "yyyy-MM-dd hh:mm:ss zz")
 
-  test "incorrect inputs: timezone (zz) 2":
+  block: # incorrect inputs: timezone (zz) 2
     parseTestExcp("2018-02-19 16:30:00 +1", "yyyy-MM-dd hh:mm:ss zz")
 
-  test "incorrect inputs: timezone (zzz) 1":
+  block: # incorrect inputs: timezone (zzz) 1
     parseTestExcp("2018-02-19 16:30:00 ", "yyyy-MM-dd hh:mm:ss zzz")
 
-  test "incorrect inputs: timezone (zzz) 2":
+  block: # incorrect inputs: timezone (zzz) 2
     parseTestExcp("2018-02-19 16:30:00 +01:", "yyyy-MM-dd hh:mm:ss zzz")
 
-  test "incorrect inputs: timezone (zzz) 3":
+  block: # incorrect inputs: timezone (zzz) 3
     parseTestExcp("2018-02-19 16:30:00 +01:0", "yyyy-MM-dd hh:mm:ss zzz")
 
-  test "dynamic timezone":
-    proc staticOffset(offset: int): Timezone =
-      proc zoneInfoFromTz(adjTime: Time): ZonedTime =
-          result.isDst = false
-          result.utcOffset = offset
-          result.adjTime = adjTime
-
-      proc zoneInfoFromUtc(time: Time): ZonedTime =
-          result.isDst = false
-          result.utcOffset = offset
-          result.adjTime = fromUnix(time.toUnix - offset)
-
-      result.name = ""
-      result.zoneInfoFromTz = zoneInfoFromTz
-      result.zoneInfoFromUtc = zoneInfoFromUtc
-
-    let tz = staticOffset(-9000)
+  block: # incorrect inputs: year (yyyy/uuuu)
+    parseTestExcp("-0001", "yyyy")
+    parseTestExcp("-0001", "YYYY")
+    parseTestExcp("1", "yyyy")
+    parseTestExcp("12345", "yyyy")
+    parseTestExcp("1", "uuuu")
+    parseTestExcp("12345", "uuuu")
+    parseTestExcp("-1 BC", "UUUU g")
+
+  block: # incorrect inputs: invalid sign
+    parseTestExcp("+1", "YYYY")
+    parseTestExcp("+1", "dd")
+    parseTestExcp("+1", "MM")
+    parseTestExcp("+1", "hh")
+    parseTestExcp("+1", "mm")
+    parseTestExcp("+1", "ss")
+
+  block: # _ as a separator
+    discard parse("2000_01_01", "YYYY'_'MM'_'dd")
+
+  block: # dynamic timezone
+    let tz = staticTz(seconds = -9000)
     let dt = initDateTime(1, mJan, 2000, 12, 00, 00, tz)
     check dt.utcOffset == -9000
     check dt.isDst == false
     check $dt == "2000-01-01T12:00:00+02:30"
-    check $dt.utc == "2000-01-01T09:30:00+00:00"
+    check $dt.utc == "2000-01-01T09:30:00Z"
     check $dt.utc.inZone(tz) == $dt
 
-  test "isLeapYear":
+  block: # isLeapYear
     check isLeapYear(2016)
     check (not isLeapYear(2015))
     check isLeapYear(2000)
     check (not isLeapYear(1900))
 
-  test "subtract months":
+  block: # TimeInterval
+    let t = fromUnix(876124714).utc # Mon 6 Oct 08:58:34 BST 1997
+    # Interval tests
+    let t2 = t - 2.years
+    check t2.year == 1995
+    let t3 = (t - 7.years - 34.minutes - 24.seconds)
+    check t3.year == 1990
+    check t3.minute == 24
+    check t3.second == 10
+    check (t + 1.hours).toTime.toUnix == t.toTime.toUnix + 60 * 60
+    check (t - 1.hours).toTime.toUnix == t.toTime.toUnix - 60 * 60
+
+  block: # TimeInterval - months
     var dt = initDateTime(1, mFeb, 2017, 00, 00, 00, utc())
-    check $(dt - initTimeInterval(months = 1)) == "2017-01-01T00:00:00+00:00"
+    check $(dt - initTimeInterval(months = 1)) == "2017-01-01T00:00:00Z"
     dt = initDateTime(15, mMar, 2017, 00, 00, 00, utc())
-    check $(dt - initTimeInterval(months = 1)) == "2017-02-15T00:00:00+00:00"
+    check $(dt - initTimeInterval(months = 1)) == "2017-02-15T00:00:00Z"
     dt = initDateTime(31, mMar, 2017, 00, 00, 00, utc())
     # This happens due to monthday overflow. It's consistent with Phobos.
-    check $(dt - initTimeInterval(months = 1)) == "2017-03-03T00:00:00+00:00"
+    check $(dt - initTimeInterval(months = 1)) == "2017-03-03T00:00:00Z"
 
-  test "duration":
+  block: # duration
     let d = initDuration
     check d(hours = 48) + d(days = 5) == d(weeks = 1)
     let dt = initDateTime(01, mFeb, 2000, 00, 00, 00, 0, utc()) + d(milliseconds = 1)
     check dt.nanosecond == convert(Milliseconds, Nanoseconds, 1)
     check d(seconds = 1, milliseconds = 500) * 2 == d(seconds = 3)
     check d(seconds = 3) div 2 == d(seconds = 1, milliseconds = 500)
-    check d(milliseconds = 1001).seconds == 1
+    check d(milliseconds = 1001).inSeconds == 1
     check d(seconds = 1, milliseconds = 500) - d(milliseconds = 1250) ==
       d(milliseconds = 250)
     check d(seconds = 1, milliseconds = 1) < d(seconds = 1, milliseconds = 2)
@@ -378,19 +321,19 @@ suite "ttimes":
     check (initDuration(seconds = 1, nanoseconds = 3) <=
       initDuration(seconds = 1, nanoseconds = 1)).not
 
-  test "large/small dates":
+  block: # large/small dates
     discard initDateTime(1, mJan, -35_000, 12, 00, 00, utc())
     # with local tz
     discard initDateTime(1, mJan, -35_000, 12, 00, 00)
     discard initDateTime(1, mJan,  35_000, 12, 00, 00)
     # with duration/timeinterval
-    let dt = initDateTime(1, mJan,  35_000, 12, 00, 00, utc()) +
+    let dt = initDateTime(1, mJan, -35_000, 12, 00, 00, utc()) +
       initDuration(seconds = 1)
     check dt.second == 1
     let dt2 = dt + 35_001.years
-    check $dt2 == "0001-01-01T12:00:01+00:00"
+    check $dt2 == "0001-01-01T12:00:01Z"
 
-  test "compare datetimes":
+  block: # compare datetimes
     var dt1 = now()
     var dt2 = dt1
     check dt1 == dt2
@@ -398,32 +341,444 @@ suite "ttimes":
     dt2 = dt2 + 1.seconds
     check dt1 < dt2
 
-  test "adding/subtracting TimeInterval":
+  block: # adding/subtracting TimeInterval
     # add/subtract TimeIntervals and Time/TimeInfo
     let now = getTime().utc
+    let isSpecial = now.isLeapDay
     check now + convert(Seconds, Nanoseconds, 1).nanoseconds == now + 1.seconds
     check now + 1.weeks == now + 7.days
     check now - 1.seconds == now - 3.seconds + 2.seconds
     check now + 65.seconds == now + 1.minutes + 5.seconds
     check now + 60.minutes == now + 1.hours
     check now + 24.hours == now + 1.days
-    check now + 13.months == now + 1.years + 1.months
+    if not isSpecial:
+      check now + 13.months == now + 1.years + 1.months
     check toUnix(fromUnix(0) + 2.seconds) == 2
     check toUnix(fromUnix(0) - 2.seconds) == -2
     var ti1 = now + 1.years
     ti1 = ti1 - 1.years
-    check ti1 == now
+    if not isSpecial:
+      check ti1 == now
     ti1 = ti1 + 1.days
-    check ti1 == now + 1.days
+    if not isSpecial:
+      check ti1 == now + 1.days
 
     # Bug with adding a day to a Time
     let day = 24.hours
     let tomorrow = now + day
     check tomorrow - now == initDuration(days = 1)
-  
-  test "fromWinTime/toWinTime":
-    check 0.fromUnix.toWinTime.fromWinTime.toUnix == 0
-    check (-1).fromWinTime.nanosecond == convert(Seconds, Nanoseconds, 1) - 100
-    check -1.fromWinTime.toWinTime == -1
-    # One nanosecond is discarded due to differences in time resolution
-    check initTime(0, 101).toWinTime.fromWinTime.nanosecond == 100 
\ No newline at end of file
+
+  # Disabled for JS because it fails due to precision errors
+  # (The JS target uses float64 for int64).
+  when not defined(js):
+    test "fromWinTime/toWinTime":
+      check 0.fromUnix.toWinTime.fromWinTime.toUnix == 0
+      check (-1).fromWinTime.nanosecond == convert(Seconds, Nanoseconds, 1) - 100
+      check (-1).fromWinTime.toWinTime == -1
+      # One nanosecond is discarded due to differences in time resolution
+      check initTime(0, 101).toWinTime.fromWinTime.nanosecond == 100
+      check initTime(0, 101).toWinTime.fromWinTime.nanosecond == 100
+
+  block: # issue 7620
+    let layout = "M/d/yyyy' 'h:mm:ss' 'tt' 'z"
+    let t7620_am = parse("4/15/2017 12:01:02 AM +0", layout, utc())
+    check t7620_am.format(layout) == "4/15/2017 12:01:02 AM Z"
+    let t7620_pm = parse("4/15/2017 12:01:02 PM +0", layout, utc())
+    check t7620_pm.format(layout) == "4/15/2017 12:01:02 PM Z"
+
+  block: # format
+    var dt = initDateTime(1, mJan, -0001,
+                          17, 01, 02, 123_456_789,
+                          staticTz(hours = 1, minutes = 2, seconds = 3))
+    check dt.format("d") == "1"
+    check dt.format("dd") == "01"
+    check dt.format("ddd") == "Fri"
+    check dt.format("dddd") == "Friday"
+    check dt.format("h") == "5"
+    check dt.format("hh") == "05"
+    check dt.format("H") == "17"
+    check dt.format("HH") == "17"
+    check dt.format("m") == "1"
+    check dt.format("mm") == "01"
+    check dt.format("M") == "1"
+    check dt.format("MM") == "01"
+    check dt.format("MMM") == "Jan"
+    check dt.format("MMMM") == "January"
+    check dt.format("s") == "2"
+    check dt.format("ss") == "02"
+    check dt.format("t") == "P"
+    check dt.format("tt") == "PM"
+    check dt.format("yy") == "02"
+    check dt.format("yyyy") == "0002"
+    check dt.format("YYYY") == "2"
+    check dt.format("uuuu") == "-0001"
+    check dt.format("UUUU") == "-1"
+    check dt.format("z") == "-1"
+    check dt.format("zz") == "-01"
+    check dt.format("zzz") == "-01:02"
+    check dt.format("zzzz") == "-01:02:03"
+    check dt.format("g") == "BC"
+
+    check dt.format("fff") == "123"
+    check dt.format("ffffff") == "123456"
+    check dt.format("fffffffff") == "123456789"
+    dt.nanosecond = 1
+    check dt.format("fff") == "000"
+    check dt.format("ffffff") == "000000"
+    check dt.format("fffffffff") == "000000001"
+
+    dt.year = 12345
+    check dt.format("yyyy") == "+12345"
+    check dt.format("uuuu") == "+12345"
+    dt.year = -12345
+    check dt.format("yyyy") == "+12346"
+    check dt.format("uuuu") == "-12345"
+
+    expect ValueError:
+      discard initTimeFormat("'")
+
+    expect ValueError:
+      discard initTimeFormat("'foo")
+
+    expect ValueError:
+      discard initTimeFormat("foo'")
+
+    for tz in [
+        (staticTz(seconds = 0), "+0", "+00", "+00:00"), # UTC
+        (staticTz(seconds = -3600), "+1", "+01", "+01:00"), # CET
+        (staticTz(seconds = -39600), "+11", "+11", "+11:00"), # two digits
+        (staticTz(seconds = -1800), "+0", "+00", "+00:30"), # half an hour
+        (staticTz(seconds = 7200), "-2", "-02", "-02:00"), # positive
+        (staticTz(seconds = 38700), "-10", "-10", "-10:45")]: # positive with three quaters hour
+      let dt = initDateTime(1, mJan, 2000, 00, 00, 00, tz[0])
+      doAssert dt.format("z") == tz[1]
+      doAssert dt.format("zz") == tz[2]
+      doAssert dt.format("zzz") == tz[3]
+
+  block: # format locale
+    let loc = DateTimeLocale(
+      MMM: ["Fir","Sec","Thi","Fou","Fif","Six","Sev","Eig","Nin","Ten","Ele","Twe"],
+      MMMM: ["Firsty", "Secondy", "Thirdy", "Fourthy", "Fifthy", "Sixthy", "Seventhy", "Eighthy", "Ninthy", "Tenthy", "Eleventhy", "Twelfthy"],
+      ddd: ["Red", "Ora.", "Yel.", "Gre.", "Blu.", "Vio.", "Whi."],
+      dddd: ["Red", "Orange", "Yellow", "Green", "Blue", "Violet", "White"],
+    )
+    var dt = initDateTime(5, mJan, 2010, 17, 01, 02, utc())
+    check dt.format("d", loc) == "5"
+    check dt.format("dd", loc) == "05"
+    check dt.format("ddd", loc) == "Ora."
+    check dt.format("dddd", loc) == "Orange"
+    check dt.format("M", loc) == "1"
+    check dt.format("MM", loc) == "01"
+    check dt.format("MMM", loc) == "Fir"
+    check dt.format("MMMM", loc) == "Firsty"
+
+  block: # parse
+    check $parse("20180101", "yyyyMMdd", utc()) == "2018-01-01T00:00:00Z"
+    parseTestExcp("+120180101", "yyyyMMdd")
+
+    check parse("1", "YYYY", utc()).year == 1
+    check parse("1 BC", "YYYY g", utc()).year == 0
+    check parse("0001 BC", "yyyy g", utc()).year == 0
+    check parse("+12345 BC", "yyyy g", utc()).year == -12344
+    check parse("1 AD", "YYYY g", utc()).year == 1
+    check parse("0001 AD", "yyyy g", utc()).year == 1
+    check parse("+12345 AD", "yyyy g", utc()).year == 12345
+
+    check parse("-1", "UUUU", utc()).year == -1
+    check parse("-0001", "uuuu", utc()).year == -1
+
+    discard parse("foobar", "'foobar'")
+    discard parse("foo'bar", "'foo''''bar'")
+    discard parse("'", "''")
+
+    parseTestExcp("2000 A", "yyyy g")
+
+  block: # parse locale
+    let loc = DateTimeLocale(
+      MMM: ["Fir","Sec","Thi","Fou","Fif","Six","Sev","Eig","Nin","Ten","Ele","Twe"],
+      MMMM: ["Firsty", "Secondy", "Thirdy", "Fourthy", "Fifthy", "Sixthy", "Seventhy", "Eighthy", "Ninthy", "Tenthy", "Eleventhy", "Twelfthy"],
+      ddd: ["Red", "Ora.", "Yel.", "Gre.", "Blu.", "Vio.", "Whi."],
+      dddd: ["Red", "Orange", "Yellow", "Green", "Blue", "Violet", "White"],
+    )
+    check $parse("02 Fir 2019", "dd MMM yyyy", utc(), loc) == "2019-01-02T00:00:00Z"
+    check $parse("Fourthy 6, 2017", "MMMM d, yyyy", utc(), loc) == "2017-04-06T00:00:00Z"
+
+  block: # timezoneConversion
+    var l = now()
+    let u = l.utc
+    l = u.local
+
+    check l.timezone == local()
+    check u.timezone == utc()
+
+  block: # getDayOfWeek
+    check getDayOfWeek(01, mJan, 0000) == dSat
+    check getDayOfWeek(01, mJan, -0023) == dSat
+    check getDayOfWeek(21, mSep, 1900) == dFri
+    check getDayOfWeek(01, mJan, 1970) == dThu
+    check getDayOfWeek(21, mSep, 1970) == dMon
+    check getDayOfWeek(01, mJan, 2000) == dSat
+    check getDayOfWeek(01, mJan, 2021) == dFri
+
+  block: # between - simple
+    let x = initDateTime(10, mJan, 2018, 13, 00, 00)
+    let y = initDateTime(11, mJan, 2018, 12, 00, 00)
+    doAssert x + between(x, y) == y
+
+  block: # between - dst start
+    usingTimezone("Europe/Stockholm"):
+      let x = initDateTime(25, mMar, 2018, 00, 00, 00)
+      let y = initDateTime(25, mMar, 2018, 04, 00, 00)
+      doAssert x + between(x, y) == y
+
+  block: # between - empty interval
+    let x = now()
+    let y = x
+    doAssert x + between(x, y) == y
+
+  block: # between - dst end
+    usingTimezone("Europe/Stockholm"):
+      let x = initDateTime(27, mOct, 2018, 02, 00, 00)
+      let y = initDateTime(28, mOct, 2018, 01, 00, 00)
+      doAssert x + between(x, y) == y
+
+  block: # between - long day
+    usingTimezone("Europe/Stockholm"):
+      # This day is 25 hours long in Europe/Stockholm
+      let x = initDateTime(28, mOct, 2018, 00, 30, 00)
+      let y = initDateTime(29, mOct, 2018, 00, 00, 00)
+      doAssert between(x, y) == 24.hours + 30.minutes
+      doAssert x + between(x, y) == y
+
+  block: # between - offset change edge case
+    # This test case is important because in this case
+    # `x + between(x.utc, y.utc) == y` is not true, which is very rare.
+    usingTimezone("America/Belem"):
+      let x = initDateTime(24, mOct, 1987, 00, 00, 00)
+      let y = initDateTime(26, mOct, 1987, 23, 00, 00)
+      doAssert x + between(x, y) == y
+      doAssert y + between(y, x) == x
+
+  block: # between - all units
+    let x = initDateTime(1, mJan, 2000, 00, 00, 00, utc())
+    let ti = initTimeInterval(1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
+    let y = x + ti
+    doAssert between(x, y) == ti
+    doAssert between(y, x) == -ti
+
+  block: # between - monthday overflow
+      let x = initDateTime(31, mJan, 2001, 00, 00, 00, utc())
+      let y = initDateTime(1, mMar, 2001, 00, 00, 00, utc())
+      doAssert x + between(x, y) == y
+
+  block: # between - misc
+    block:
+      let x = initDateTime(31, mDec, 2000, 12, 00, 00, utc())
+      let y = initDateTime(01, mJan, 2001, 00, 00, 00, utc())
+      doAssert between(x, y) == 12.hours
+
+    block:
+      let x = initDateTime(31, mDec, 2000, 12, 00, 00, utc())
+      let y = initDateTime(02, mJan, 2001, 00, 00, 00, utc())
+      doAssert between(x, y) == 1.days + 12.hours
+
+    block:
+      let x = initDateTime(31, mDec, 1995, 00, 00, 00, utc())
+      let y = initDateTime(01, mFeb, 2000, 00, 00, 00, utc())
+      doAssert x + between(x, y) == y
+
+    block:
+      let x = initDateTime(01, mDec, 1995, 00, 00, 00, utc())
+      let y = initDateTime(31, mJan, 2000, 00, 00, 00, utc())
+      doAssert x + between(x, y) == y
+
+    block:
+      let x = initDateTime(31, mJan, 2000, 00, 00, 00, utc())
+      let y = initDateTime(01, mFeb, 2000, 00, 00, 00, utc())
+      doAssert x + between(x, y) == y
+
+    block:
+      let x = initDateTime(01, mJan, 1995, 12, 00, 00, utc())
+      let y = initDateTime(01, mFeb, 1995, 00, 00, 00, utc())
+      doAssert between(x, y) == 4.weeks + 2.days + 12.hours
+
+    block:
+      let x = initDateTime(31, mJan, 1995, 00, 00, 00, utc())
+      let y = initDateTime(10, mFeb, 1995, 00, 00, 00, utc())
+      doAssert x + between(x, y) == y
+
+    block:
+      let x = initDateTime(31, mJan, 1995, 00, 00, 00, utc())
+      let y = initDateTime(10, mMar, 1995, 00, 00, 00, utc())
+      doAssert x + between(x, y) == y
+      doAssert between(x, y) == 1.months + 1.weeks
+
+  block: # default DateTime https://github.com/nim-lang/RFCs/issues/211
+    var num = 0
+    for ai in Month: num.inc
+    check num == 12
+
+    var a: DateTime
+    check a == DateTime.default
+    check not a.isInitialized
+    check $a == "Uninitialized DateTime"
+
+    expect(AssertionDefect): discard getDayOfWeek(a.monthday, a.month, a.year)
+    expect(AssertionDefect): discard a.toTime
+    expect(AssertionDefect): discard a.utc()
+    expect(AssertionDefect): discard a.local()
+    expect(AssertionDefect): discard a.inZone(utc())
+    expect(AssertionDefect): discard a + initDuration(seconds = 1)
+    expect(AssertionDefect): discard a + initTimeInterval(seconds = 1)
+    expect(AssertionDefect): discard a.isLeapDay
+    expect(AssertionDefect): discard a < a
+    expect(AssertionDefect): discard a <= a
+    expect(AssertionDefect): discard getDateStr(a)
+    expect(AssertionDefect): discard getClockStr(a)
+    expect(AssertionDefect): discard a.format "yyyy"
+    expect(AssertionDefect): discard a.format initTimeFormat("yyyy")
+    expect(AssertionDefect): discard between(a, a)
+
+  block: # inX procs
+    doAssert initDuration(seconds = 1).inSeconds == 1
+    doAssert initDuration(seconds = -1).inSeconds == -1
+    doAssert initDuration(seconds = -1, nanoseconds = 1).inSeconds == 0
+    doAssert initDuration(nanoseconds = -1).inSeconds == 0
+    doAssert initDuration(milliseconds = 500).inMilliseconds == 500
+    doAssert initDuration(milliseconds = -500).inMilliseconds == -500
+    doAssert initDuration(nanoseconds = -999999999).inMilliseconds == -999
+
+  block: # getIsoWeekAndYear
+    doAssert getIsoWeekAndYear(initDateTime(04, mNov, 2019, 00, 00, 00)) == (isoweek: 45.IsoWeekRange, isoyear: 2019.IsoYear)
+    doAssert initDateTime(dMon, 45, 2019.IsoYear, 00, 00, 00) == initDateTime(04, mNov, 2019, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(28, mDec, 2019, 00, 00, 00)) == (isoweek: 52.IsoWeekRange, isoyear: 2019.IsoYear)
+    doAssert initDateTime(dSat, 52, 2019.IsoYear, 00, 00, 00) == initDateTime(28, mDec, 2019, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(29, mDec, 2019, 00, 00, 00)) == (isoweek: 52.IsoWeekRange, isoyear: 2019.IsoYear)
+    doAssert initDateTime(dSun, 52, 2019.IsoYear, 00, 00, 00) == initDateTime(29, mDec, 2019, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(30, mDec, 2019, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear)
+    doAssert initDateTime(dMon, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(30, mDec, 2019, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(31, mDec, 2019, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear)
+    doAssert initDateTime(dTue, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(31, mDec, 2019, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(01, mJan, 2020, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear)
+    doAssert initDateTime(dWed, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(01, mJan, 2020, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(02, mJan, 2020, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear)
+    doAssert initDateTime(dThu, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(02, mJan, 2020, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(05, mApr, 2020, 00, 00, 00)) == (isoweek: 14.IsoWeekRange, isoyear: 2020.IsoYear)
+    doAssert initDateTime(dSun, 14, 2020.IsoYear, 00, 00, 00) == initDateTime(05, mApr, 2020, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(06, mApr, 2020, 00, 00, 00)) == (isoweek: 15.IsoWeekRange, isoyear: 2020.IsoYear)
+    doAssert initDateTime(dMon, 15, 2020.IsoYear, 00, 00, 00) == initDateTime(06, mApr, 2020, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(10, mApr, 2020, 00, 00, 00)) == (isoweek: 15.IsoWeekRange, isoyear: 2020.IsoYear)
+    doAssert initDateTime(dFri, 15, 2020.IsoYear, 00, 00, 00) == initDateTime(10, mApr, 2020, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(12, mApr, 2020, 00, 00, 00)) == (isoweek: 15.IsoWeekRange, isoyear: 2020.IsoYear)
+    doAssert initDateTime(dSun, 15, 2020.IsoYear, 00, 00, 00) == initDateTime(12, mApr, 2020, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(13, mApr, 2020, 00, 00, 00)) == (isoweek: 16.IsoWeekRange, isoyear: 2020.IsoYear)
+    doAssert initDateTime(dMon, 16, 2020.IsoYear, 00, 00, 00) == initDateTime(13, mApr, 2020, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(15, mApr, 2020, 00, 00, 00)) == (isoweek: 16.IsoWeekRange, isoyear: 2020.IsoYear)
+    doAssert initDateTime(dThu, 16, 2020.IsoYear, 00, 00, 00) == initDateTime(16, mApr, 2020, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(17, mJul, 2020, 00, 00, 00)) == (isoweek: 29.IsoWeekRange, isoyear: 2020.IsoYear)
+    doAssert initDateTime(dFri, 29, 2020.IsoYear, 00, 00, 00) == initDateTime(17, mJul, 2020, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(19, mJul, 2020, 00, 00, 00)) == (isoweek: 29.IsoWeekRange, isoyear: 2020.IsoYear)
+    doAssert initDateTime(dSun, 29, 2020.IsoYear, 00, 00, 00) == initDateTime(19, mJul, 2020, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(20, mJul, 2020, 00, 00, 00)) == (isoweek: 30.IsoWeekRange, isoyear: 2020.IsoYear)
+    doAssert initDateTime(dMon, 30, 2020.IsoYear, 00, 00, 00) == initDateTime(20, mJul, 2020, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(23, mJul, 2020, 00, 00, 00)) == (isoweek: 30.IsoWeekRange, isoyear: 2020.IsoYear)
+    doAssert initDateTime(dThu, 30, 2020.IsoYear, 00, 00, 00) == initDateTime(23, mJul, 2020, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(31, mDec, 2020, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear)
+    doAssert initDateTime(dThu, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(31, mDec, 2020, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(01, mJan, 2021, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear)
+    doAssert initDateTime(dFri, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(01, mJan, 2021, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(02, mJan, 2021, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear)
+    doAssert initDateTime(dSat, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(02, mJan, 2021, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(03, mJan, 2021, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear)
+    doAssert initDateTime(dSun, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(03, mJan, 2021, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(04, mJan, 2021, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2021.IsoYear)
+    doAssert initDateTime(dMon, 01, 2021.IsoYear, 00, 00, 00) == initDateTime(04, mJan, 2021, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(01, mFeb, 2021, 00, 00, 00)) == (isoweek: 05.IsoWeekRange, isoyear: 2021.IsoYear)
+    doAssert initDateTime(dMon, 05, 2021.IsoYear, 00, 00, 00) == initDateTime(01, mFeb, 2021, 00, 00, 00)
+
+    doAssert getIsoWeekAndYear(initDateTime(01, mFeb, 2021, 01, 02, 03, 400_000_000, staticTz(hours=1))) == (isoweek: 05.IsoWeekRange, isoyear: 2021.IsoYear)
+    doAssert initDateTime(dMon, 05, 2021.IsoYear, 01, 02, 03, 400_000_000, staticTz(hours=1)) == initDateTime(01, mFeb, 2021, 01, 02, 03, 400_000_000, staticTz(hours=1))
+
+    doAssert getIsoWeekAndYear(initDateTime(01, mApr, +0001, 00, 00, 00)) == (isoweek: 13.IsoWeekRange, isoyear: 0001.IsoYear)
+    doAssert initDateTime(dSun, 13, 0001.IsoYear, 00, 00, 00) == initDateTime(01, mApr, 0001, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(01, mApr, +0000, 00, 00, 00)) == (isoweek: 13.IsoWeekRange, isoyear: 0000.IsoYear)
+    doAssert initDateTime(dSat, 13, 0000.IsoYear, 00, 00, 00) == initDateTime(01, mApr, 0000, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(01, mApr, -0001, 00, 00, 00)) == (isoweek: 13.IsoWeekRange, isoyear: (-0001).IsoYear)
+    doAssert initDateTime(dThu, 13, (-0001).IsoYear, 00, 00, 00) == initDateTime(01, mApr, -0001, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(01, mApr, -0002, 00, 00, 00)) == (isoweek: 14.IsoWeekRange, isoyear: (-0002).IsoYear)
+    doAssert initDateTime(dWed, 14, (-0002).IsoYear, 00, 00, 00) == initDateTime(01, mApr, -0002, 00, 00, 00)
+    doAssert getIsoWeekAndYear(initDateTime(01, mApr, -0753, 00, 00, 00)) == (isoweek: 14.IsoWeekRange, isoyear: (-0753).IsoYear)
+    doAssert initDateTime(dMon, 14, (-0753).IsoYear, 00, 00, 00) == initDateTime(01, mApr, -0753, 00, 00, 00)
+
+  block: # getWeeksInIsoYear
+    doAssert getWeeksInIsoYear((-0014).IsoYear) == 52
+    doAssert getWeeksInIsoYear((-0013).IsoYear) == 53
+    doAssert getWeeksInIsoYear((-0012).IsoYear) == 52
+
+    doAssert getWeeksInIsoYear((-0009).IsoYear) == 52
+    doAssert getWeeksInIsoYear((-0008).IsoYear) == 53
+    doAssert getWeeksInIsoYear((-0007).IsoYear) == 52
+
+    doAssert getWeeksInIsoYear((-0003).IsoYear) == 52
+    doAssert getWeeksInIsoYear((-0002).IsoYear) == 53
+    doAssert getWeeksInIsoYear((-0001).IsoYear) == 52
+
+    doAssert getWeeksInIsoYear(0003.IsoYear) == 52
+    doAssert getWeeksInIsoYear(0004.IsoYear) == 53
+    doAssert getWeeksInIsoYear(0005.IsoYear) == 52
+
+    doAssert getWeeksInIsoYear(1653.IsoYear) == 52
+    doAssert getWeeksInIsoYear(1654.IsoYear) == 53
+    doAssert getWeeksInIsoYear(1655.IsoYear) == 52
+
+    doAssert getWeeksInIsoYear(1997.IsoYear) == 52
+    doAssert getWeeksInIsoYear(1998.IsoYear) == 53
+    doAssert getWeeksInIsoYear(1999.IsoYear) == 52
+
+    doAssert getWeeksInIsoYear(2008.IsoYear) == 52
+    doAssert getWeeksInIsoYear(2009.IsoYear) == 53
+    doAssert getWeeksInIsoYear(2010.IsoYear) == 52
+
+    doAssert getWeeksInIsoYear(2014.IsoYear) == 52
+    doAssert getWeeksInIsoYear(2015.IsoYear) == 53
+    doAssert getWeeksInIsoYear(2016.IsoYear) == 52
+
+  block: # parse and generate iso years
+    # short calendar week with text
+    parseTest("KW 23 2023", "'KW' VV GGGG",
+      "2023-06-05T00:00:00Z", 155)
+    parseTest("KW 5 2023", "'KW' V GGGG",
+      "2023-01-30T00:00:00Z", 29)
+    parseTest("KW 05 23 Saturday", "'KW' V GG dddd",
+      "2023-02-04T00:00:00Z", 34)
+    parseTest("KW 53 20 Fri", "'KW' VV GG ddd",
+      "2021-01-01T00:00:00Z", 0)
+
+    parseTestExcp("KW 23", "'KW' VV") # no year
+    parseTestExcp("KW 23", "'KW' V") # no year
+    parseTestExcp("KW 23", "'KW' GG") # no week
+    parseTestExcp("KW 2023", "'KW' GGGG") # no week
+    
+    var dt = initDateTime(5, mJan, 2023, 0, 0, 0, utc())
+    check dt.format("V") == "1"
+    check dt.format("VV") == "01"
+    check dt.format("GG") == "23"
+    check dt.format("GGGG") == "2023"
+    check dt.format("dddd 'KW'V GGGG") == "Thursday KW1 2023"
+
+  block: # Can be used inside gcsafe proc
+    proc test(): DateTime {.gcsafe.} =
+      result = "1970".parse("yyyy")
+    doAssert test().year == 1970
+
+  block: # test FormatLiterals
+    # since #23861
+    block:
+      let dt = dateTime(2024, mJul, 21, 17, 01, 02, 123_321_123, utc())
+      check dt.format("ss.fff") == "02.123"
+      check dt.format("fff.ffffff") == "123.123321"
+    block:
+      let dt = parse("2024.07.21", "yyyy.MM.dd")
+      check dt.year == 2024
+      check dt.month == mJul
+      check dt.monthday == 21
diff --git a/tests/stdlib/ttypeinfo.nim b/tests/stdlib/ttypeinfo.nim
new file mode 100644
index 000000000..9bbc2e92c
--- /dev/null
+++ b/tests/stdlib/ttypeinfo.nim
@@ -0,0 +1,93 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/typeinfo
+import std/assertions
+
+type
+  TE = enum
+    blah, blah2
+
+  TestObj = object
+    test, asd: int
+    case test2: TE
+    of blah:
+      help: string
+    else:
+      nil
+
+
+var test = @[0,1,2,3,4]
+var x = toAny(test)
+var y = 78
+x[4] = toAny(y)
+doAssert x[2].getInt == 2
+
+var test2: tuple[name: string, s: int] = ("test", 56)
+var x2 = toAny(test2)
+var i = 0
+for n, a in fields(x2):
+  case i
+  of 0: doAssert n == "Field0" and $a.kind == "akString"
+  of 1: doAssert n == "Field1" and $a.kind == "akInt"
+  else: doAssert false
+  inc i
+
+var test3: TestObj
+test3.test = 42
+test3 = TestObj(test2: blah2)
+var x3 = toAny(test3)
+i = 0
+for n, a in fields(x3):
+  case i
+  of 0: doAssert n == "test" and $a.kind == "akInt"
+  of 1: doAssert n == "asd" and $a.kind == "akInt"
+  of 2: doAssert n == "test2" and $a.kind == "akEnum"
+  else: doAssert false
+  inc i
+
+var test4: ref string
+new(test4)
+test4[] = "test"
+var x4 = toAny(test4)
+doAssert($x4[].kind() == "akString")
+
+block:
+  # gimme a new scope dammit
+  var myArr: array[0..4, array[0..4, string]] = [
+    ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
+    ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
+    ["test", "1", "2", "3", "4"]]
+  var m = toAny(myArr)
+  for i in 0 .. m.len-1:
+    for j in 0 .. m[i].len-1:
+      doAssert getString(m[i][j]) == myArr[i][j]
+
+block:
+  type
+    Test = enum
+      Hello, he_llo
+
+  var x = hello
+  var y = x.toAny
+
+  doAssert getEnumOrdinal(y, "Hello") == 0
+  doAssert getEnumOrdinal(y, "hello") == 1
+
+block: # bug #23556
+  proc test =
+    var
+      t: seq[int]
+      aseq = toAny(t)
+
+    invokeNewSeq(aseq, 0)
+
+    # Got random value only when loop 8 times.
+    for i in 1 .. 8:
+      extendSeq(aseq)
+
+    doAssert t == @[0, 0, 0, 0, 0, 0, 0, 0]
+
+  for i in 1 .. 7:
+    test()
diff --git a/tests/stdlib/ttypeinfo.nims b/tests/stdlib/ttypeinfo.nims
new file mode 100644
index 000000000..d31d0b26f
--- /dev/null
+++ b/tests/stdlib/ttypeinfo.nims
@@ -0,0 +1 @@
+--styleCheck:off
\ No newline at end of file
diff --git a/tests/stdlib/ttypetraits.nim b/tests/stdlib/ttypetraits.nim
new file mode 100644
index 000000000..6851b9220
--- /dev/null
+++ b/tests/stdlib/ttypetraits.nim
@@ -0,0 +1,94 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c cpp js"
+"""
+
+# xxx merge with tests/metatype/ttypetraits.nim
+
+import std/typetraits
+import std/assertions
+
+macro testClosure(fn: typed, flag: static bool) =
+  if flag:
+    doAssert hasClosure(fn)
+  else:
+    doAssert not hasClosure(fn)
+
+block:
+  proc h1() =
+    echo 1
+
+  testClosure(h1, false)
+
+  proc h2() {.nimcall.} =
+    echo 2
+
+  testClosure(h2, false)
+
+
+block:
+  proc fn(): auto =
+    proc hello() {.nimcall.} =
+      echo 3
+    hello
+
+  let name = fn()
+  testClosure(name, false)
+
+block:
+  proc fn(): auto =
+    proc hello() =
+      echo 3
+    hello
+
+  let name = fn()
+  testClosure(name, false)
+
+block:
+  proc fn(): auto =
+    var x = 0
+    proc hello() =
+      echo 3
+      inc x
+    hello
+
+  let name = fn()
+  testClosure(name, true)
+
+block:
+  proc fn(): auto =
+    var x = 0
+    proc hello() {.closure.} =
+      echo 3
+      inc x
+    hello
+
+  let name = fn()
+  testClosure(name, true)
+
+block:
+  proc fn(): auto =
+    var x = 0
+    proc hello() {.closure.} =
+      echo 3
+      inc x
+    hello
+
+  let name = fn()
+  testClosure(name, true)
+
+  let name2 = name
+  testClosure(name2, true)
+
+block:
+  iterator hello(): int =
+    yield 1
+
+  testClosure(hello, false)
+
+when not defined(js):
+  block:
+    iterator hello(): int {.closure.}=
+      yield 1
+
+    testClosure(hello, true)
diff --git a/tests/stdlib/tunicode.nim b/tests/stdlib/tunicode.nim
new file mode 100644
index 000000000..b9e68b15b
--- /dev/null
+++ b/tests/stdlib/tunicode.nim
@@ -0,0 +1,228 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/unicode
+import std/assertions
+
+proc asRune(s: static[string]): Rune =
+  ## Compile-time conversion proc for converting string literals to a Rune
+  ## value. Returns the first Rune of the specified string.
+  ##
+  ## Shortcuts code like ``"å".runeAt(0)`` to ``"å".asRune`` and returns a
+  ## compile-time constant.
+  if s.len == 0: Rune(0)
+  else: s.runeAt(0)
+
+let
+  someString = "öÑ"
+  someRunes = toRunes(someString)
+  compared = (someString == $someRunes)
+doAssert compared == true
+
+proc testReplacements(word: string): string =
+  case word
+  of "two":
+    return "2"
+  of "foo":
+    return "BAR"
+  of "βeta":
+    return "beta"
+  of "alpha":
+    return "αlpha"
+  else:
+    return "12345"
+
+doAssert translate("two not alpha foo βeta", testReplacements) == "2 12345 αlpha BAR beta"
+doAssert translate("  two not foo βeta  ", testReplacements) == "  2 12345 BAR beta  "
+
+doAssert title("foo bar") == "Foo Bar"
+doAssert title("αlpha βeta γamma") == "Αlpha Βeta Γamma"
+doAssert title("") == ""
+
+doAssert capitalize("βeta") == "Βeta"
+doAssert capitalize("foo") == "Foo"
+doAssert capitalize("") == ""
+
+doAssert swapCase("FooBar") == "fOObAR"
+doAssert swapCase(" ") == " "
+doAssert swapCase("Αlpha Βeta Γamma") == "αLPHA βETA γAMMA"
+doAssert swapCase("a✓B") == "A✓b"
+doAssert swapCase("Јамогујестистаклоитоминештети") == "јАМОГУЈЕСТИСТАКЛОИТОМИНЕШТЕТИ"
+doAssert swapCase("ὕαλονϕαγεῖνδύναμαιτοῦτοοὔμεβλάπτει") == "ὝΑΛΟΝΦΑΓΕῖΝΔΎΝΑΜΑΙΤΟῦΤΟΟὔΜΕΒΛΆΠΤΕΙ"
+doAssert swapCase("Կրնամապակիուտեևինծիանհանգիստչըներ") == "կՐՆԱՄԱՊԱԿԻՈՒՏԵևԻՆԾԻԱՆՀԱՆԳԻՍՏՉԸՆԵՐ"
+doAssert swapCase("") == ""
+
+doAssert isAlpha("r")
+doAssert isAlpha("α")
+doAssert isAlpha("ϙ")
+doAssert isAlpha("ஶ")
+doAssert isAlpha("网")
+doAssert(not isAlpha("$"))
+doAssert(not isAlpha(""))
+
+doAssert isAlpha("Βeta")
+doAssert isAlpha("Args")
+doAssert isAlpha("𐌼𐌰𐌲𐌲𐌻𐌴𐍃𐍄𐌰𐌽")
+doAssert isAlpha("ὕαλονϕαγεῖνδύναμαιτοῦτοοὔμεβλάπτει")
+doAssert isAlpha("Јамогујестистаклоитоминештети")
+doAssert isAlpha("Կրնամապակիուտեևինծիանհանգիստչըներ")
+doAssert isAlpha("编程语言")
+doAssert(not isAlpha("$Foo✓"))
+doAssert(not isAlpha("⠙⠕⠑⠎⠝⠞"))
+
+doAssert isSpace("\t")
+doAssert isSpace("\l")
+doAssert(not isSpace("Β"))
+doAssert(not isSpace("Βeta"))
+
+doAssert isSpace("\t\l \v\r\f")
+doAssert isSpace("       ")
+doAssert(not isSpace(""))
+doAssert(not isSpace("ΑΓc   \td"))
+
+doAssert(not isLower(' '.Rune))
+
+doAssert(not isUpper(' '.Rune))
+
+doAssert toUpper("Γ") == "Γ"
+doAssert toUpper("b") == "B"
+doAssert toUpper("α") == "Α"
+doAssert toUpper("✓") == "✓"
+doAssert toUpper("ϙ") == "Ϙ"
+doAssert toUpper("") == ""
+
+doAssert toUpper("ΑΒΓ") == "ΑΒΓ"
+doAssert toUpper("AAccβ") == "AACCΒ"
+doAssert toUpper("A✓$β") == "A✓$Β"
+
+doAssert toLower("a") == "a"
+doAssert toLower("γ") == "γ"
+doAssert toLower("Γ") == "γ"
+doAssert toLower("4") == "4"
+doAssert toLower("Ϙ") == "ϙ"
+doAssert toLower("") == ""
+
+doAssert toLower("abcdγ") == "abcdγ"
+doAssert toLower("abCDΓ") == "abcdγ"
+doAssert toLower("33aaΓ") == "33aaγ"
+
+doAssert reversed("Reverse this!") == "!siht esreveR"
+doAssert reversed("先秦兩漢") == "漢兩秦先"
+doAssert reversed("as⃝df̅") == "f̅ds⃝a"
+doAssert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞"
+doAssert reversed("ὕαλονϕαγεῖνδύναμαιτοῦτοοὔμεβλάπτει") == "ιετπάλβεμὔοοτῦοτιαμανύδνῖεγαϕνολαὕ"
+doAssert reversed("Јамогујестистаклоитоминештети") == "итетшенимотиолкатситсејугомаЈ"
+doAssert reversed("Կրնամապակիուտեևինծիանհանգիստչըներ") == "րենըչտսիգնահնաիծնիևետւոիկապամանրԿ"
+doAssert len(toRunes("as⃝df̅")) == runeLen("as⃝df̅")
+const test = "as⃝"
+doAssert lastRune(test, test.len-1)[1] == 3
+doAssert graphemeLen("è", 0) == 2
+
+# test for rune positioning and runeSubStr()
+let s = "Hänsel  ««: 10,00€"
+
+var t = ""
+for c in s.utf8:
+  t.add c
+
+doAssert(s == t)
+
+doAssert(runeReverseOffset(s, 1) == (20, 18))
+doAssert(runeReverseOffset(s, 19) == (-1, 18))
+
+doAssert(runeStrAtPos(s, 0) == "H")
+doAssert(runeSubStr(s, 0, 1) == "H")
+doAssert(runeStrAtPos(s, 10) == ":")
+doAssert(runeSubStr(s, 10, 1) == ":")
+doAssert(runeStrAtPos(s, 9) == "«")
+doAssert(runeSubStr(s, 9, 1) == "«")
+doAssert(runeStrAtPos(s, 17) == "€")
+doAssert(runeSubStr(s, 17, 1) == "€")
+# echo runeStrAtPos(s, 18) # index error
+
+doAssert(runeSubStr(s, 0) == "Hänsel  ««: 10,00€")
+doAssert(runeSubStr(s, -18) == "Hänsel  ««: 10,00€")
+doAssert(runeSubStr(s, 10) == ": 10,00€")
+doAssert(runeSubStr(s, 18) == "")
+doAssert(runeSubStr(s, 0, 10) == "Hänsel  ««")
+
+doAssert(runeSubStr(s, 12) == "10,00€")
+doAssert(runeSubStr(s, -6) == "10,00€")
+
+doAssert(runeSubStr(s, 12, 5) == "10,00")
+doAssert(runeSubStr(s, 12, -1) == "10,00")
+doAssert(runeSubStr(s, -6, 5) == "10,00")
+doAssert(runeSubStr(s, -6, -1) == "10,00")
+
+doAssert(runeSubStr(s, 0, 100) == "Hänsel  ««: 10,00€")
+doAssert(runeSubStr(s, -100, 100) == "Hänsel  ««: 10,00€")
+doAssert(runeSubStr(s, 0, -100) == "")
+doAssert(runeSubStr(s, 100, -100) == "")
+
+block splitTests:
+  let s = " this is an example  "
+  let s2 = ":this;is;an:example;;"
+  let s3 = ":this×is×an:example××"
+  doAssert s.split() == @["", "this", "is", "an", "example", "", ""]
+  doAssert s2.split(seps = [':'.Rune, ';'.Rune]) == @["", "this", "is", "an",
+      "example", "", ""]
+  doAssert s3.split(seps = [':'.Rune, "×".asRune]) == @["", "this", "is",
+      "an", "example", "", ""]
+  doAssert s.split(maxsplit = 4) == @["", "this", "is", "an", "example  "]
+  doAssert s.split(' '.Rune, maxsplit = 1) == @["", "this is an example  "]
+  doAssert s3.split("×".runeAt(0)) == @[":this", "is", "an:example", "", ""]
+
+block stripTests:
+  doAssert(strip("") == "")
+  doAssert(strip(" ") == "")
+  doAssert(strip("y") == "y")
+  doAssert(strip("  foofoofoo  ") == "foofoofoo")
+  doAssert(strip("sfoofoofoos", runes = ['s'.Rune]) == "foofoofoo")
+
+  block:
+    let stripTestRunes = ['b'.Rune, 'a'.Rune, 'r'.Rune]
+    doAssert(strip("barfoofoofoobar", runes = stripTestRunes) == "foofoofoo")
+  doAssert(strip("sfoofoofoos", leading = false, runes = ['s'.Rune]) == "sfoofoofoo")
+  doAssert(strip("sfoofoofoos", trailing = false, runes = ['s'.Rune]) == "foofoofoos")
+
+  block:
+    let stripTestRunes = ["«".asRune, "»".asRune]
+    doAssert(strip("«TEXT»", runes = stripTestRunes) == "TEXT")
+  doAssert(strip("copyright©", leading = false, runes = ["©".asRune]) == "copyright")
+  doAssert(strip("¿Question?", trailing = false, runes = ["¿".asRune]) == "Question?")
+  doAssert(strip("×text×", leading = false, runes = ["×".asRune]) == "×text")
+  doAssert(strip("×text×", trailing = false, runes = ["×".asRune]) == "text×")
+
+block repeatTests:
+  doAssert repeat('c'.Rune, 5) == "ccccc"
+  doAssert repeat("×".asRune, 5) == "×××××"
+
+block alignTests:
+  doAssert align("abc", 4) == " abc"
+  doAssert align("a", 0) == "a"
+  doAssert align("1232", 6) == "  1232"
+  doAssert align("1232", 6, '#'.Rune) == "##1232"
+  doAssert align("1232", 6, "×".asRune) == "××1232"
+  doAssert alignLeft("abc", 4) == "abc "
+  doAssert alignLeft("a", 0) == "a"
+  doAssert alignLeft("1232", 6) == "1232  "
+  doAssert alignLeft("1232", 6, '#'.Rune) == "1232##"
+  doAssert alignLeft("1232", 6, "×".asRune) == "1232××"
+
+block differentSizes:
+  # upper and lower variants have different number of bytes
+  doAssert toLower("AẞC") == "aßc"
+  doAssert toLower("ȺẞCD") == "ⱥßcd"
+  doAssert toUpper("ⱥbc") == "ȺBC"
+  doAssert toUpper("rsⱦuv") == "RSȾUV"
+  doAssert swapCase("ⱥbCd") == "ȺBcD"
+  doAssert swapCase("XyꟆaB") == "xYᶎAb"
+  doAssert swapCase("aᵹcᲈd") == "AꝽCꙊD"
+
+block: # bug #17768
+  let s1 = "abcdef"
+  let s2 = "abcdéf"
+
+  doAssert s1.runeSubStr(0, -1) == "abcde"
+  doAssert s2.runeSubStr(0, -1) == "abcdé"
diff --git a/tests/stdlib/tunidecode.nim b/tests/stdlib/tunidecode.nim
index 689453c76..653016ea9 100644
--- a/tests/stdlib/tunidecode.nim
+++ b/tests/stdlib/tunidecode.nim
@@ -1,12 +1,13 @@
 discard """
   cmd: "nim $target --hints:on -d:embedUnidecodeTable $options $file"
-  output: "Ausserst"
 """
 
 import unidecode
 
-loadUnidecodeTable("lib/pure/unidecode/unidecode.dat")
+import std/unidecode # #14112
+import std/assertions
 
-#assert unidecode("\x53\x17\x4E\xB0") == "Bei Jing"
-echo unidecode("Äußerst")
+loadUnidecodeTable("lib/pure/unidecode/unidecode.dat")
 
+doAssert unidecode("北京") == "Bei Jing "
+doAssert unidecode("Äußerst") == "Ausserst"
diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim
index 86b9fd037..0442c7863 100644
--- a/tests/stdlib/tunittest.nim
+++ b/tests/stdlib/tunittest.nim
@@ -1,5 +1,7 @@
 discard """
-  output: '''[Suite] suite with only teardown
+  output: '''
+
+[Suite] suite with only teardown
 
 [Suite] suite with only setup
 
@@ -13,12 +15,16 @@ discard """
 
 [Suite] bug #5784
 
-[Suite] test name filtering
+[Suite] test suite
 
+[Suite] test name filtering
 '''
+matrix: "--mm:refc; --mm:orc"
+targets: "c js"
 """
 
-import unittest, sequtils
+import std/[unittest, sequtils, assertions]
+from std/unittest {.all.} import matchFilter
 
 proc doThings(spuds: var int): int =
   spuds = 24
@@ -29,12 +35,12 @@ test "#964":
   check spuds == 24
 
 
-from strutils import toUpperAscii
+from std/strutils import toUpperAscii
 test "#1384":
   check(@["hello", "world"].map(toUpperAscii) == @["HELLO", "WORLD"])
 
 
-import options
+import std/options
 test "unittest typedescs":
   check(none(int) == none(int))
   check(none(int) != some(1))
@@ -45,17 +51,16 @@ test "unittest multiple requires":
   require(true)
 
 
-import math, random
-from strutils import parseInt
+import std/random
+from std/strutils import parseInt
 proc defectiveRobot() =
-  randomize()
-  case random(1..4)
+  case rand(1..4)
   of 1: raise newException(OSError, "CANNOT COMPUTE!")
   of 2: discard parseInt("Hello World!")
   of 3: raise newException(IOError, "I can't do that Dave.")
-  else: assert 2 + 2 == 5
+  else: doAssert 2 + 2 == 5
 test "unittest expect":
-  expect IOError, OSError, ValueError, AssertionError:
+  expect IOError, OSError, ValueError, AssertionDefect:
     defectiveRobot()
 
 var
@@ -123,38 +128,67 @@ suite "bug #5784":
     var obj: Obj
     check obj.isNil or obj.field == 0
 
-when defined(testing):
-  suite "test name filtering":
-    test "test name":
-      check matchFilter("suite1", "foo", "")
-      check matchFilter("suite1", "foo", "foo")
-      check matchFilter("suite1", "foo", "::")
-      check matchFilter("suite1", "foo", "*")
-      check matchFilter("suite1", "foo", "::foo")
-      check matchFilter("suite1", "::foo", "::foo")
-
-    test "test name - glob":
-      check matchFilter("suite1", "foo", "f*")
-      check matchFilter("suite1", "foo", "*oo")
-      check matchFilter("suite1", "12345", "12*345")
-      check matchFilter("suite1", "q*wefoo", "q*wefoo")
-      check false == matchFilter("suite1", "foo", "::x")
-      check false == matchFilter("suite1", "foo", "::x*")
-      check false == matchFilter("suite1", "foo", "::*x")
-      #  overlap
-      check false == matchFilter("suite1", "12345", "123*345")
-      check matchFilter("suite1", "ab*c::d*e::f", "ab*c::d*e::f")
-
-    test "suite name":
-      check matchFilter("suite1", "foo", "suite1::")
-      check false == matchFilter("suite1", "foo", "suite2::")
-      check matchFilter("suite1", "qwe::foo", "qwe::foo")
-      check matchFilter("suite1", "qwe::foo", "suite1::qwe::foo")
-
-    test "suite name - glob":
-      check matchFilter("suite1", "foo", "::*")
-      check matchFilter("suite1", "foo", "*::*")
-      check matchFilter("suite1", "foo", "*::foo")
-      check false == matchFilter("suite1", "foo", "*ite2::")
-      check matchFilter("suite1", "q**we::foo", "q**we::foo")
-      check matchFilter("suite1", "a::b*c::d*e", "a::b*c::d*e")
+type
+    SomeType = object
+        value: int
+        children: seq[SomeType]
+
+# bug #5252
+
+proc `==`(a, b: SomeType): bool =
+    return a.value == b.value
+
+suite "test suite":
+    test "test":
+        let a = SomeType(value: 10)
+        let b = SomeType(value: 10)
+
+        check(a == b)
+
+suite "test name filtering":
+  test "test name":
+    check matchFilter("suite1", "foo", "")
+    check matchFilter("suite1", "foo", "foo")
+    check matchFilter("suite1", "foo", "::")
+    check matchFilter("suite1", "foo", "*")
+    check matchFilter("suite1", "foo", "::foo")
+    check matchFilter("suite1", "::foo", "::foo")
+
+  test "test name - glob":
+    check matchFilter("suite1", "foo", "f*")
+    check matchFilter("suite1", "foo", "*oo")
+    check matchFilter("suite1", "12345", "12*345")
+    check matchFilter("suite1", "q*wefoo", "q*wefoo")
+    check false == matchFilter("suite1", "foo", "::x")
+    check false == matchFilter("suite1", "foo", "::x*")
+    check false == matchFilter("suite1", "foo", "::*x")
+    #  overlap
+    check false == matchFilter("suite1", "12345", "123*345")
+    check matchFilter("suite1", "ab*c::d*e::f", "ab*c::d*e::f")
+
+  test "suite name":
+    check matchFilter("suite1", "foo", "suite1::")
+    check false == matchFilter("suite1", "foo", "suite2::")
+    check matchFilter("suite1", "qwe::foo", "qwe::foo")
+    check matchFilter("suite1", "qwe::foo", "suite1::qwe::foo")
+
+  test "suite name - glob":
+    check matchFilter("suite1", "foo", "::*")
+    check matchFilter("suite1", "foo", "*::*")
+    check matchFilter("suite1", "foo", "*::foo")
+    check false == matchFilter("suite1", "foo", "*ite2::")
+    check matchFilter("suite1", "q**we::foo", "q**we::foo")
+    check matchFilter("suite1", "a::b*c::d*e", "a::b*c::d*e")
+
+
+block:
+  type MyFoo = object
+  var obj = MyFoo()
+  let check = 1
+  check(obj == obj)
+
+block:
+  let check = 123
+  var a = 1
+  var b = 1
+  check(a == b)
diff --git a/tests/stdlib/tunittest_error.nim b/tests/stdlib/tunittest_error.nim
new file mode 100644
index 000000000..7f05ec2a9
--- /dev/null
+++ b/tests/stdlib/tunittest_error.nim
@@ -0,0 +1,22 @@
+discard """
+  exitcode: 1
+  outputsub: "failed: 1 == 3"
+  matrix: "-d:case1; -d:case2"
+  targets: "c js"
+  joinable: false
+"""
+
+when defined case1:
+  import unittest
+  suite "Test":
+    test "test require":
+      check 1==2
+      check 1==3
+
+when defined case2:
+  import unittest
+  suite "Test":
+    test "test require":
+      require 1 == 3
+      if true:
+        quit 0 # intentional
diff --git a/tests/stdlib/tunittestpass.nim b/tests/stdlib/tunittestpass.nim
new file mode 100644
index 000000000..d8de277b7
--- /dev/null
+++ b/tests/stdlib/tunittestpass.nim
@@ -0,0 +1,20 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c js"
+"""
+
+
+import unittest
+
+block:
+  check (type(1.0)) is float
+  check type(1.0) is float
+  check (typeof(1)) isnot float
+  check typeof(1) isnot float
+
+  check 1.0 is float
+  check 1 isnot float
+
+  type T = type(0.1)
+  check T is float
+  check T isnot int
diff --git a/tests/stdlib/tunittesttemplate.nim b/tests/stdlib/tunittesttemplate.nim
new file mode 100644
index 000000000..c29e0de01
--- /dev/null
+++ b/tests/stdlib/tunittesttemplate.nim
@@ -0,0 +1,25 @@
+discard """
+  exitcode: 1
+  outputsub: '''
+    tunittesttemplate.nim(20, 12): Check failed: a.b == 2
+    a.b was 0
+  [FAILED] 1
+'''
+"""
+
+
+
+# bug #6736
+import std/unittest
+
+type
+  A = object
+    b: int
+
+template t: untyped =
+  check(a.b == 2)
+
+suite "1":
+  test "1":
+    var a = A(b: 0)
+    t()
diff --git a/tests/stdlib/tunixsocket.nim b/tests/stdlib/tunixsocket.nim
new file mode 100644
index 000000000..636fd08c6
--- /dev/null
+++ b/tests/stdlib/tunixsocket.nim
@@ -0,0 +1,35 @@
+import std/[assertions, net, os, osproc]
+
+# XXX: Make this test run on Windows too when we add support for Unix sockets on Windows
+when defined(posix) and not defined(nimNetLite):
+  const nim = getCurrentCompilerExe()
+  let
+    dir = currentSourcePath().parentDir()
+    serverPath = dir / "unixsockettest"
+
+  let (_, err) = execCmdEx(nim & " c " & quoteShell(dir / "unixsockettest.nim"))
+  doAssert err == 0
+
+  let svproc = startProcess(serverPath, workingDir = dir)
+  doAssert svproc.running()
+  # Wait for the server to open the socket and listen from it
+  sleep(400)
+
+  block unixSocketSendRecv:
+    let
+      unixSocketPath = dir / "usox"
+      socket = newSocket(AF_UNIX, SOCK_STREAM, IPPROTO_NONE)
+
+    socket.connectUnix(unixSocketPath)
+    # for a blocking Unix socket this should never fail
+    socket.send("data sent through the socket\c\l", maxRetries = 0)
+    var resp: string
+    socket.readLine(resp)
+    doAssert resp == "Hello from server"
+
+    socket.send("bye\c\l")
+    socket.readLine(resp)
+    doAssert resp == "bye"
+    socket.close()
+
+  svproc.close()
diff --git a/tests/stdlib/turi.nim b/tests/stdlib/turi.nim
new file mode 100644
index 000000000..9c717c5b1
--- /dev/null
+++ b/tests/stdlib/turi.nim
@@ -0,0 +1,323 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets:  "c js"
+"""
+
+import std/uri
+from std/uri {.all.} as uri2 import removeDotSegments
+from std/sequtils import toSeq
+import std/assertions
+
+template main() =
+  block: # encodeUrl, decodeUrl
+    const test1 = "abc\L+def xyz"
+    doAssert encodeUrl(test1) == "abc%0A%2Bdef+xyz"
+    doAssert decodeUrl(encodeUrl(test1)) == test1
+    doAssert encodeUrl(test1, false) == "abc%0A%2Bdef%20xyz"
+    doAssert decodeUrl(encodeUrl(test1, false), false) == test1
+    doAssert decodeUrl(encodeUrl(test1)) == test1
+
+  block: # removeDotSegments
+    doAssert removeDotSegments("/foo/bar/baz") == "/foo/bar/baz"
+    doAssert removeDotSegments("") == "" # empty test
+    doAssert removeDotSegments(".") == "." # trailing period
+    doAssert removeDotSegments("a1/a2/../a3/a4/a5/./a6/a7/././") == "a1/a3/a4/a5/a6/a7/"
+    doAssert removeDotSegments("https://a1/a2/../a3/a4/a5/./a6/a7/././") == "https://a1/a3/a4/a5/a6/a7/"
+    doAssert removeDotSegments("http://a1/a2") == "http://a1/a2"
+    doAssert removeDotSegments("http://www.ai.") == "http://www.ai."
+    when false: # xxx these cases are buggy
+      # this should work, refs https://webmasters.stackexchange.com/questions/73934/how-can-urls-have-a-dot-at-the-end-e-g-www-bla-de
+      doAssert removeDotSegments("http://www.ai./") == "http://www.ai./" # fails
+      echo removeDotSegments("http://www.ai./")  # http://www.ai/
+      echo removeDotSegments("a/b.../c") # b.c
+      echo removeDotSegments("a/b../c") # bc
+      echo removeDotSegments("a/.../c") # .c
+      echo removeDotSegments("a//../b") # a/b
+      echo removeDotSegments("a/b/c//") # a/b/c//
+
+  block: # parseUri
+    block:
+      let org = "udp://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080"
+      let url = parseUri(org)
+      doAssert url.hostname == "2001:0db8:85a3:0000:0000:8a2e:0370:7334" # true
+      let newUrl = parseUri($url)
+      doAssert newUrl.hostname == "2001:0db8:85a3:0000:0000:8a2e:0370:7334" # true
+
+    block:
+      let str = "http://localhost"
+      let test = parseUri(str)
+      doAssert test.path == ""
+
+    block:
+      let str = "http://localhost/"
+      let test = parseUri(str)
+      doAssert test.path == "/"
+
+    block:
+      let str = "http://localhost:8080/test"
+      let test = parseUri(str)
+      doAssert test.scheme == "http"
+      doAssert test.port == "8080"
+      doAssert test.path == "/test"
+      doAssert test.hostname == "localhost"
+      doAssert($test == str)
+
+    block:
+      let str = "foo://username:password@example.com:8042/over/there" &
+                "/index.dtb?type=animal&name=narwhal#nose"
+      let test = parseUri(str)
+      doAssert test.scheme == "foo"
+      doAssert test.username == "username"
+      doAssert test.password == "password"
+      doAssert test.hostname == "example.com"
+      doAssert test.port == "8042"
+      doAssert test.path == "/over/there/index.dtb"
+      doAssert test.query == "type=animal&name=narwhal"
+      doAssert test.anchor == "nose"
+      doAssert($test == str)
+
+    block:
+      # IPv6 address
+      let str = "foo://[::1]:1234/bar?baz=true&qux#quux"
+      let uri = parseUri(str)
+      doAssert uri.scheme == "foo"
+      doAssert uri.hostname == "::1"
+      doAssert uri.port == "1234"
+      doAssert uri.path == "/bar"
+      doAssert uri.query == "baz=true&qux"
+      doAssert uri.anchor == "quux"
+
+    block:
+      let str = "urn:example:animal:ferret:nose"
+      let test = parseUri(str)
+      doAssert test.scheme == "urn"
+      doAssert test.path == "example:animal:ferret:nose"
+      doAssert($test == str)
+
+    block:
+      let str = "mailto:username@example.com?subject=Topic"
+      let test = parseUri(str)
+      doAssert test.scheme == "mailto"
+      doAssert test.username == "username"
+      doAssert test.hostname == "example.com"
+      doAssert test.query == "subject=Topic"
+      doAssert($test == str)
+
+    block:
+      let str = "magnet:?xt=urn:sha1:72hsga62ba515sbd62&dn=foobar"
+      let test = parseUri(str)
+      doAssert test.scheme == "magnet"
+      doAssert test.query == "xt=urn:sha1:72hsga62ba515sbd62&dn=foobar"
+      doAssert($test == str)
+
+    block:
+      let str = "/test/foo/bar?q=2#asdf"
+      let test = parseUri(str)
+      doAssert test.scheme == ""
+      doAssert test.path == "/test/foo/bar"
+      doAssert test.query == "q=2"
+      doAssert test.anchor == "asdf"
+      doAssert($test == str)
+
+    block:
+      let str = "test/no/slash"
+      let test = parseUri(str)
+      doAssert test.path == "test/no/slash"
+      doAssert($test == str)
+
+    block:
+      let str = "//git@github.com:dom96/packages"
+      let test = parseUri(str)
+      doAssert test.scheme == ""
+      doAssert test.username == "git"
+      doAssert test.hostname == "github.com"
+      doAssert test.port == "dom96"
+      doAssert test.path == "/packages"
+
+    block:
+      let str = "file:///foo/bar/baz.txt"
+      let test = parseUri(str)
+      doAssert test.scheme == "file"
+      doAssert test.username == ""
+      doAssert test.hostname == ""
+      doAssert test.port == ""
+      doAssert test.path == "/foo/bar/baz.txt"
+
+  block: # combine
+    block:
+      let concat = combine(parseUri("http://google.com/foo/bar/"), parseUri("baz"))
+      doAssert concat.path == "/foo/bar/baz"
+      doAssert concat.hostname == "google.com"
+      doAssert concat.scheme == "http"
+
+    block:
+      let concat = combine(parseUri("http://google.com/foo"), parseUri("/baz"))
+      doAssert concat.path == "/baz"
+      doAssert concat.hostname == "google.com"
+      doAssert concat.scheme == "http"
+
+    block:
+      let concat = combine(parseUri("http://google.com/foo/test"), parseUri("bar"))
+      doAssert concat.path == "/foo/bar"
+
+    block:
+      let concat = combine(parseUri("http://google.com/foo/test"), parseUri("/bar"))
+      doAssert concat.path == "/bar"
+
+    block:
+      let concat = combine(parseUri("http://google.com/foo/test"), parseUri("bar"))
+      doAssert concat.path == "/foo/bar"
+
+    block:
+      let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar"))
+      doAssert concat.path == "/foo/test/bar"
+
+    block:
+      let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar/"))
+      doAssert concat.path == "/foo/test/bar/"
+
+    block:
+      let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar/"),
+                           parseUri("baz"))
+      doAssert concat.path == "/foo/test/bar/baz"
+
+  block: # `/`
+    block:
+      let test = parseUri("http://example.com/foo") / "bar/asd"
+      doAssert test.path == "/foo/bar/asd"
+
+    block:
+      let test = parseUri("http://example.com/foo/") / "/bar/asd"
+      doAssert test.path == "/foo/bar/asd"
+
+  block: # bug #3207
+    doAssert parseUri("http://qq/1").combine(parseUri("https://qqq")).`$` == "https://qqq"
+
+  block: # bug #4959
+    let foo = parseUri("http://example.com") / "/baz"
+    doAssert foo.path == "/baz"
+
+  block: # bug found on stream 13/10/17
+    let foo = parseUri("http://localhost:9515") / "status"
+    doAssert $foo == "http://localhost:9515/status"
+
+  block: # bug #6649 #6652
+    var foo = parseUri("http://example.com")
+    foo.hostname = "example.com"
+    foo.path = "baz"
+    doAssert $foo == "http://example.com/baz"
+
+    foo.hostname = "example.com/"
+    foo.path = "baz"
+    doAssert $foo == "http://example.com/baz"
+
+    foo.hostname = "example.com"
+    foo.path = "/baz"
+    doAssert $foo == "http://example.com/baz"
+
+    foo.hostname = "example.com/"
+    foo.path = "/baz"
+    doAssert $foo == "http://example.com/baz"
+
+    foo.hostname = "example.com/"
+    foo.port = "8000"
+    foo.path = "baz"
+    doAssert $foo == "http://example.com:8000/baz"
+
+    foo = parseUri("file:/dir/file")
+    foo.path = "relative"
+    doAssert $foo == "file:relative"
+
+  block: # isAbsolute tests
+    doAssert "www.google.com".parseUri().isAbsolute() == false
+    doAssert "http://www.google.com".parseUri().isAbsolute() == true
+    doAssert "file:/dir/file".parseUri().isAbsolute() == true
+    doAssert "file://localhost/dir/file".parseUri().isAbsolute() == true
+    doAssert "urn:ISSN:1535-3613".parseUri().isAbsolute() == true
+
+    # path-relative URL *relative
+    doAssert "about".parseUri().isAbsolute == false
+    doAssert "about/staff.html".parseUri().isAbsolute == false
+    doAssert "about/staff.html?".parseUri().isAbsolute == false
+    doAssert "about/staff.html?parameters".parseUri().isAbsolute == false
+
+    # absolute-path-relative URL *relative
+    doAssert "/".parseUri().isAbsolute == false
+    doAssert "/about".parseUri().isAbsolute == false
+    doAssert "/about/staff.html".parseUri().isAbsolute == false
+    doAssert "/about/staff.html?".parseUri().isAbsolute == false
+    doAssert "/about/staff.html?parameters".parseUri().isAbsolute == false
+
+    # scheme-relative URL *relative
+    doAssert "//username:password@example.com:8888".parseUri().isAbsolute == false
+    doAssert "//username@example.com".parseUri().isAbsolute == false
+    doAssert "//example.com".parseUri().isAbsolute == false
+    doAssert "//example.com/".parseUri().isAbsolute == false
+    doAssert "//example.com/about".parseUri().isAbsolute == false
+    doAssert "//example.com/about/staff.html".parseUri().isAbsolute == false
+    doAssert "//example.com/about/staff.html?".parseUri().isAbsolute == false
+    doAssert "//example.com/about/staff.html?parameters".parseUri().isAbsolute == false
+
+    # absolute URL *absolute
+    doAssert "https://username:password@example.com:8888".parseUri().isAbsolute == true
+    doAssert "https://username@example.com".parseUri().isAbsolute == true
+    doAssert "https://example.com".parseUri().isAbsolute == true
+    doAssert "https://example.com/".parseUri().isAbsolute == true
+    doAssert "https://example.com/about".parseUri().isAbsolute == true
+    doAssert "https://example.com/about/staff.html".parseUri().isAbsolute == true
+    doAssert "https://example.com/about/staff.html?".parseUri().isAbsolute == true
+    doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true
+
+  block: # encodeQuery tests
+    doAssert encodeQuery({:}) == ""
+    doAssert encodeQuery({"foo": "bar"}) == "foo=bar"
+    doAssert encodeQuery({"foo": "bar & baz"}) == "foo=bar+%26+baz"
+    doAssert encodeQuery({"foo": "bar & baz"}, usePlus = false) == "foo=bar%20%26%20baz"
+    doAssert encodeQuery({"foo": ""}) == "foo"
+    doAssert encodeQuery({"foo": ""}, omitEq = false) == "foo="
+    doAssert encodeQuery({"a": "1", "b": "", "c": "3"}) == "a=1&b&c=3"
+    doAssert encodeQuery({"a": "1", "b": "", "c": "3"}, sep = ';') == "a=1;b;c=3"
+    doAssert encodeQuery({"a": "1", "b": "", "c": "3"}, omitEq = false) == "a=1&b=&c=3"
+    doAssert encodeQuery({"a": "1", "b": "", "c": "3"}, omitEq = false, sep = ';') == "a=1;b=;c=3"
+
+  block: # `?`
+    block:
+      var foo = parseUri("http://example.com") / "foo" ? {"bar": "1", "baz": "qux"}
+      var foo1 = parseUri("http://example.com/foo?bar=1&baz=qux")
+      doAssert foo == foo1
+    block:
+      var foo = parseUri("http://example.com") / "foo" ? {"do": "do", "bar": ""}
+      var foo1 = parseUri("http://example.com/foo?do=do&bar")
+      doAssert foo == foo1
+
+  block: # getDataUri, dataUriBase64
+    doAssert getDataUri("", "text/plain") == "data:text/plain;charset=utf-8;base64,"
+    doAssert getDataUri(" ", "text/plain") == "data:text/plain;charset=utf-8;base64,IA=="
+    doAssert getDataUri("c\xf7>", "text/plain") == "data:text/plain;charset=utf-8;base64,Y/c+"
+    doAssert getDataUri("Hello World", "text/plain") == "data:text/plain;charset=utf-8;base64,SGVsbG8gV29ybGQ="
+    doAssert getDataUri("leasure.", "text/plain") == "data:text/plain;charset=utf-8;base64,bGVhc3VyZS4="
+    doAssert getDataUri("""!@#$%^&*()_+""", "text/plain") == "data:text/plain;charset=utf-8;base64,IUAjJCVeJiooKV8r"
+    doAssert(getDataUri("the quick brown dog jumps over the lazy fox", "text/plain") ==
+      "data:text/plain;charset=utf-8;base64,dGhlIHF1aWNrIGJyb3duIGRvZyBqdW1wcyBvdmVyIHRoZSBsYXp5IGZveA==")
+    doAssert(getDataUri("The present is theirs\n      The future, for which I really worked, is mine.", "text/plain") ==
+      "data:text/plain;charset=utf-8;base64,VGhlIHByZXNlbnQgaXMgdGhlaXJzCiAgICAgIFRoZSBmdXR1cmUsIGZvciB3aGljaCBJIHJlYWxseSB3b3JrZWQsIGlzIG1pbmUu")
+
+  block: # decodeQuery
+    doAssert toSeq(decodeQuery("a=1&b=0")) == @[("a", "1"), ("b", "0")]
+    doAssert toSeq(decodeQuery("a=1;b=0", sep = ';')) == @[("a", "1"), ("b", "0")]
+    doAssert toSeq(decodeQuery("a=1&b=2c=6")) == @[("a", "1"), ("b", "2c=6")]
+    doAssert toSeq(decodeQuery("a=1;b=2c=6", sep = ';')) == @[("a", "1"), ("b", "2c=6")]
+
+  block: # bug #17481
+    let u1 = parseUri("./")
+    let u2 = parseUri("./path")
+    let u3 = parseUri("a/path")
+    doAssert u1.scheme.len == 0
+    doAssert u1.path == "./"
+    doAssert u2.scheme.len == 0
+    doAssert u2.path == "./path"
+    doAssert u3.scheme.len == 0
+    doAssert u3.path == "a/path"
+
+static: main()
+main()
diff --git a/tests/stdlib/tuserlocks.nim b/tests/stdlib/tuserlocks.nim
new file mode 100644
index 000000000..927077120
--- /dev/null
+++ b/tests/stdlib/tuserlocks.nim
@@ -0,0 +1,21 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/rlocks
+import std/assertions
+
+var r: RLock
+r.initRLock()
+doAssert r.tryAcquire()
+doAssert r.tryAcquire()
+r.release()
+r.release()
+
+block:
+  var x = 12
+  withRLock r:
+    inc x
+  doAssert x == 13
+
+r.deinitRLock()
diff --git a/tests/stdlib/tvarargs.nim b/tests/stdlib/tvarargs.nim
new file mode 100644
index 000000000..2edc26264
--- /dev/null
+++ b/tests/stdlib/tvarargs.nim
@@ -0,0 +1,18 @@
+discard """
+  targets: "c js"
+  matrix: "--mm:refc; --mm:orc"
+"""
+import std/assertions
+
+template main =
+  proc hello(x: varargs[string]): seq[string] =
+    var s: seq[string]
+    s.add x
+    s
+
+  doAssert hello() == @[]
+  doAssert hello("a1") == @["a1"]
+  doAssert hello("a1", "a2") == @["a1", "a2"]
+
+static: main()
+main()
diff --git a/tests/stdlib/tvarints.nim b/tests/stdlib/tvarints.nim
new file mode 100644
index 000000000..f9624ee5b
--- /dev/null
+++ b/tests/stdlib/tvarints.nim
@@ -0,0 +1,73 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/varints
+import std/assertions
+
+# xxx doesn't work with js: tvarints.nim(18, 14) `wrLen == rdLen`  [AssertionDefect]
+
+block:
+  var dest: array[50, byte]
+  var got: uint64
+
+  for test in [0xFFFF_FFFF_FFFFF_FFFFu64, 77u64, 0u64, 10_000_000u64, uint64(high(int64)),
+              uint64(high(int32)), uint64(high(int32)), uint64(high(int64))]:
+    let wrLen = writeVu64(dest, test)
+    let rdLen = readVu64(dest, got)
+    doAssert wrLen == rdLen
+    doAssert got == test
+
+  for test in 0u64..300u64:
+    let wrLen = writeVu64(dest, test)
+    let rdLen = readVu64(dest, got)
+    doAssert wrLen == rdLen
+    doAssert got == test
+
+  # check this also works for floats:
+  for test in [0.0, 0.1, 2.0, +Inf, NegInf]:
+    let t = cast[uint64](test)
+    let wrLenB = writeVu64(dest, t)
+    let rdLenB = readVu64(dest, got)
+    doAssert wrLenB == rdLenB
+    doAssert cast[float64](got) == test
+
+block:
+  var hugeIntArray: array[9, byte]
+  var readedInt: uint64
+
+  template chk(a) =
+    let b = cast[uint64](a)
+    doAssert writeVu64(hugeIntArray, b) == readVu64(hugeIntArray, readedInt)
+    doAssert readedInt == b
+
+  chk 0
+  chk uint64.high
+  chk int64.high
+  chk int32.high
+  chk int16.high
+  chk int16.high
+  chk int8.high
+  chk 0.0
+  chk -0.0
+  chk 0.1
+  chk Inf
+  chk NegInf
+  chk NaN
+  chk 3.1415926535897932384626433
+
+block:
+  template chk(a) =
+    let b = cast[uint64](a)
+    doAssert encodeZigzag(decodeZigzag(b)) == b
+  chk 0
+  chk uint32.high
+  chk int32.high
+  chk int16.high
+  chk int8.high
+  chk 0.0
+  chk 0.1
+  chk 0.9555555555555555555555501
+  chk Inf
+  chk 3.1415926535897932384626433
+  chk 2.71828182845904523536028747
diff --git a/tests/stdlib/tvmutils.nim b/tests/stdlib/tvmutils.nim
new file mode 100644
index 000000000..63804c136
--- /dev/null
+++ b/tests/stdlib/tvmutils.nim
@@ -0,0 +1,31 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  joinable: false
+  nimout: '''
+0
+1
+2
+tvmutils.nim(28, 13) [opcLdImmInt]     if i == 4:
+tvmutils.nim(28, 10) [opcEqInt]     if i == 4:
+tvmutils.nim(28, 10) [opcFJmp]     if i == 4:
+tvmutils.nim(28, 13) [opcLdImmInt]     if i == 4:
+tvmutils.nim(28, 10) [opcEqInt]     if i == 4:
+tvmutils.nim(28, 10) [opcFJmp]     if i == 4:
+tvmutils.nim(29, 7) [opcLdConst]       vmTrace(false)
+tvmutils.nim(29, 15) [opcLdImmInt]       vmTrace(false)
+tvmutils.nim(29, 14) [opcIndCall]       vmTrace(false)
+5
+6
+'''
+"""
+# line 20 (only showing a subset of nimout to avoid making the test rigid)
+import std/vmutils
+proc main() =
+  for i in 0..<7:
+    echo i
+    if i == 2:
+      vmTrace(true)
+    if i == 4:
+      vmTrace(false)
+
+static: main()
diff --git a/tests/stdlib/tvolatile.nim b/tests/stdlib/tvolatile.nim
new file mode 100644
index 000000000..c097f9723
--- /dev/null
+++ b/tests/stdlib/tvolatile.nim
@@ -0,0 +1,15 @@
+import std/[volatile, assertions]
+
+var st: int
+var foo: ptr int = addr st
+volatileStore(foo, 12)
+doAssert volatileLoad(foo) == 12
+
+# bug #14623
+proc bar =
+  var st: int
+  var foo: ptr int = addr st
+  volatileStore(foo, 12)
+  doAssert volatileLoad(foo) == 12
+
+bar()
diff --git a/tests/stdlib/twalker.nim b/tests/stdlib/twalker.nim
deleted file mode 100644
index 91c97df01..000000000
--- a/tests/stdlib/twalker.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-# iterate over all files with a given filter:
-
-import
-  "../../lib/pure/os.nim", ../../ lib / pure / times
-
-proc main(filter: string) =
-  for filename in walkFiles(filter):
-    writeLine(stdout, filename)
-
-  for key, val in envPairs():
-    writeLine(stdout, key & '=' & val)
-
-main("*.nim")
diff --git a/tests/stdlib/twchartoutf8.nim b/tests/stdlib/twchartoutf8.nim
index a6602e3e3..e437177ac 100644
--- a/tests/stdlib/twchartoutf8.nim
+++ b/tests/stdlib/twchartoutf8.nim
@@ -1,13 +1,17 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   output: '''OK'''
 """
 
+import std/[syncio, assertions]
+
 #assume WideCharToMultiByte always produce correct result
 #windows only
 
 when not defined(windows):
   echo "OK"
 else:
+  import std/widestrs
   {.push gcsafe.}
 
   const CP_UTF8 = 65001'i32
@@ -66,8 +70,7 @@ else:
 
   #RFC-2781 "UTF-16, an encoding of ISO 10646"
 
-  var wc: WideCString
-  unsafeNew(wc, 1024 * 4 + 2)
+  var wc: WideCString = newWideCString(1024 * 2)
 
   #U+0000 to U+D7FF
   #skip the U+0000
diff --git a/tests/stdlib/twith.nim b/tests/stdlib/twith.nim
new file mode 100644
index 000000000..ceadbe7bf
--- /dev/null
+++ b/tests/stdlib/twith.nim
@@ -0,0 +1,44 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/with
+import std/[assertions, formatfloat]
+
+type
+  Foo = object
+    col, pos: string
+    name: string
+
+proc setColor(f: var Foo; r, g, b: int) = f.col = $(r, g, b)
+proc setPosition(f: var Foo; x, y: float) = f.pos = $(x, y)
+
+var f: Foo
+with(f, setColor(2, 3, 4), setPosition(0.0, 1.0))
+doAssert f.col == "(2, 3, 4)"
+doAssert f.pos == "(0.0, 1.0)"
+
+f = Foo()
+with f:
+  col = $(2, 3, 4)
+  pos = $(0.0, 1.0)
+  _.name = "bar"
+doAssert f.col == "(2, 3, 4)"
+doAssert f.pos == "(0.0, 1.0)"
+doAssert f.name == "bar"
+
+type
+  Baz* = object
+    a*, b*: int
+  Bar* = object
+    x*: int
+    baz*: Baz
+
+var bar: Bar
+with bar:
+  x = 1
+  with baz:
+    a = 2
+
+doAssert bar.x == 1
+doAssert bar.baz.a == 2
diff --git a/tests/stdlib/twordwrap.nim b/tests/stdlib/twordwrap.nim
new file mode 100644
index 000000000..5d49477d3
--- /dev/null
+++ b/tests/stdlib/twordwrap.nim
@@ -0,0 +1,48 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/wordwrap
+import std/assertions
+
+when true:
+  let
+    inp = """ this is a long text --  muchlongerthan10chars and here
+                it goes"""
+    outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
+  doAssert wrapWords(inp, 10, false) == outp
+
+  let
+    longInp = """ThisIsOneVeryLongStringWhichWeWillSplitIntoEightSeparatePartsNow"""
+    longOutp = "ThisIsOn\neVeryLon\ngStringW\nhichWeWi\nllSplitI\nntoEight\nSeparate\nPartsNow"
+  doAssert wrapWords(longInp, 8, true) == longOutp
+
+# test we don't break Umlauts into invalid bytes:
+let fies = "äöüöäöüöäöüöäöüööäöüöäößßßßüöäößßßßßß"
+let fiesRes = "ä\nö\nü\nö\nä\nö\nü\nö\nä\nö\nü\nö\nä\nö\nü\nö\nö\nä\nö\nü\nö\nä\nö\nß\nß\nß\nß\nü\nö\nä\nö\nß\nß\nß\nß\nß\nß"
+doAssert wrapWords(fies, 1, true) == fiesRes
+
+let longlongword = """abc uitdaeröägfßhydüäpydqfü,träpydqgpmüdträpydföägpydörztdüöäfguiaeowäzjdtrüöäp psnrtuiydrözenrüöäpyfdqazpesnrtulocjtüö
+äzydgyqgfqfgprtnwjlcydkqgfüöezmäzydydqüüöäpdtrnvwfhgckdumböäpydfgtdgfhtdrntdrntydfogiayqfguiatrnydrntüöärtniaoeydfgaoeiqfglwcßqfgxvlcwgtfhiaoen
+rsüöäapmböäptdrniaoydfglckqfhouenrtsüöäptrniaoeyqfgulocfqclgwxßqflgcwßqfxglcwrniatrnmüböäpmöäbpümöäbpüöämpbaoestnriaesnrtdiaesrtdniaesdrtnaetdr
+iaoenvlcyfglwckßqfgvwkßqgfvlwkßqfgvlwckßqvlwkgfUIαοιαοιαχολωχσωχνωκψρχκψρτιεαοσηζϵηζιοεννκεωνιαλωσωκνκψρκγτφγτχκγτεκργτιχνκιωχσιλωσλωχξλξλξωχωχ
+ξχλωωχαοεοιαεοαεοιαεοαεοιαοεσναοεκνρκψγκψφϵιηαααοε"""
+let longlongwordRes = """
+abc uitdaeröägfßhydüäpydqfü,träpydqgpmüdträpydföägpydörztdüöäfguiaeowäzjdtrüöäp
+psnrtuiydrözenrüöäpyfdqazpesnrtulocjtüöäzydgyqgfqfgprtnwjlcydkqgfüöezmäzydydqüü
+öäpdtrnvwfhgckdumböäpydfgtdgfhtdrntdrntydfogiayqfguiatrnydrntüöärtniaoeydfgaoeiq
+fglwcßqfgxvlcwgtfhiaoenrsüöäapmböäptdrniaoydfglckqfhouenrtsüöäptrniaoeyqfgulocf
+qclgwxßqflgcwßqfxglcwrniatrnmüböäpmöäbpümöäbpüöämpbaoestnriaesnrtdiaesrtdniaesdr
+tnaetdriaoenvlcyfglwckßqfgvwkßqgfvlwkßqfgvlwckßqvlwkgfUIαοιαοιαχολωχσωχνωκψρχκψ
+ρτιεαοσηζϵηζιοεννκεωνιαλωσωκνκψρκγτφγτχκγτεκργτιχνκιωχσιλωσλωχξλξλξωχωχ
+ξχλωωχαοεοιαεοαεοιαεοαεοιαοεσναοεκνρκψγκψφϵιηαααοε"""
+doAssert wrapWords(longlongword) == longlongwordRes
+
+# bug #14579
+const input60 = """
+This string is wrapped to 60 characters. If we call
+wrapwords on it it will be re-wrapped to 80 characters.
+"""
+const input60Res = """This string is wrapped to 60 characters. If we call wrapwords on it it will be
+re-wrapped to 80 characters."""
+doAssert wrapWords(input60) == input60Res
\ No newline at end of file
diff --git a/tests/stdlib/twrapnils.nim b/tests/stdlib/twrapnils.nim
new file mode 100644
index 000000000..3da230b5e
--- /dev/null
+++ b/tests/stdlib/twrapnils.nim
@@ -0,0 +1,224 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/wrapnils
+from std/options import get, isSome
+import std/assertions
+
+proc checkNotZero(x: float): float =
+  doAssert x != 0
+  x
+
+proc main() =
+  var witness = 0
+  block:
+    type Bar = object
+      b1: int
+      b2: ptr string
+
+    type Foo = ref object
+      x1: float
+      x2: Foo
+      x3: string
+      x4: Bar
+      x5: seq[int]
+      x6: ptr Bar
+      x7: array[2, string]
+      x8: seq[int]
+      x9: ref Bar
+
+    type Goo = ref object
+      foo: Foo
+
+    proc fun(a: Bar): auto = a.b2
+
+    var a: Foo
+
+    var x6: ptr Bar
+    when nimvm: discard # pending https://github.com/timotheecour/Nim/issues/568
+    else:
+      x6 = create(Bar)
+      x6.b1 = 42
+    var a2 = Foo(x1: 1.0, x5: @[10, 11], x6: x6)
+    var a3 = Foo(x1: 1.2, x3: "abc")
+    a3.x2 = a3
+
+    var goo = Goo(foo: a)
+
+    proc initFoo(x1: float): auto =
+      witness.inc
+      result = Foo(x1: x1)
+
+    doAssert ?.a.x2.x2.x1 == 0.0
+    doAssert ?.a3.x2.x2.x1 == 1.2
+    doAssert ?.a3.x2.x2.x3[1] == 'b'
+
+    doAssert ?.a3.x2.x2.x5.len == 0
+    doAssert a3.x2.x2.x3.len == 3
+
+    doAssert ?.a.x2.x2.x3[1] == default(char)
+    # here we only apply wrapnil around goo.foo, not goo (and assume goo is not nil)
+    doAssert ?.(goo.foo).x2.x2.x1 == 0.0
+
+    when nimvm: discard
+    else:
+      doAssert ?.a2.x6[] == Bar(b1: 42) # deref for ptr Bar
+
+    doAssert ?.a2.x1.checkNotZero == 1.0
+    doAssert a == nil
+    # shows that checkNotZero won't be called if a nil is found earlier in chain
+    doAssert ?.a.x1.checkNotZero == 0.0
+
+    when nimvm: discard
+    else:
+      # checks that a chain without nil but with an empty seq still raises
+      doAssertRaises(IndexDefect): discard ?.a2.x8[3]
+
+    # make sure no double evaluation bug
+    doAssert witness == 0
+    doAssert ?.initFoo(1.3).x1 == 1.3
+    doAssert witness == 1
+
+    # here, it's used twice, to deref `ref Bar` and then `ptr string`
+    doAssert ?.a.x9[].fun[] == ""
+
+    block: # `??.`
+      doAssert (??.a3.x2.x2.x3.len).get == 3
+      doAssert (??.a2.x4).isSome
+      doAssert not (??.a.x4).isSome
+
+  block:
+    type
+      A = object
+        b: B
+      B = object
+        c: C
+      C = object
+        d: D
+      D = ref object
+        e: E
+        e2: array[2, E]
+        e3: seq[E]
+        d3: D
+        i4: int
+      E = object
+        f: int
+        d2: D
+    proc identity[T](a: T): T = a
+    proc identity2[T](a: T, ignore: int): T = a
+    var a: A
+    doAssert ?.a.b.c.d.e.f == 0
+    doAssert ?.a.b.c.d.e.d2.d3[].d3.e.d2.e.f == 0
+    doAssert ?.a.b.c.d.d3[].e.f == 0
+    doAssert ?.a.b.c.d.e2[0].d2.e3[0].f == 0
+    doAssert ?.a == A.default
+    doAssert ?.a.b.c.d.e == E.default
+    doAssert ?.a.b.c.d.e.d2 == nil
+
+    doAssert ?.a.identity.b.c.identity2(12).d.d3.e.f == 0
+    doAssert ?.a.b.c.d.d3.e2[0].f == 0
+    a.b.c.d = D()
+    a.b.c.d.d3 = a.b.c.d
+    a.b.c.d.e2[0].f = 5
+    doAssert ?.a.b.c.d.d3.e2[0].f == 5
+
+    var d: D = nil
+    doAssert ?.d.identity.i4 == 0
+    doAssert ?.d.i4.identity == 0
+
+  block: # case objects
+    type
+      Kind = enum k0, k1, k2
+      V = object
+        case kind: Kind
+        of k0:
+          x0: int
+        of k1:
+          x1: int
+        of k2:
+          x2: int
+      A = object
+        v0: V
+
+    block:
+      var a = V(kind: k0, x0: 3)
+      doAssert ?.a.x0 == 3
+      doAssert ?.a.x1 == 0
+      a = V(kind: k1, x1: 5)
+      doAssert ?.a.x0 == 0
+      doAssert ?.a.x1 == 5
+
+    block:
+      var a = A(v0: V(kind: k0, x0: 10))
+      doAssert ?.a.v0.x0 == 10
+      doAssert ?.a.v0.x1 == 0
+      a.v0 = V(kind: k2, x2: 8)
+      doAssert ?.a.v0.x0 == 0
+      doAssert ?.a.v0.x1 == 0
+      doAssert ?.a.v0.x2 == 8
+
+  block: # `nnkCall`
+    type
+      A = object
+        a0: int
+        d: D
+      D = ref object
+        i4: int
+
+    proc identity[T](a: T): T = a
+    var d: D = nil
+    doAssert ?.d.i4.identity == 0
+    doAssert ?.identity(?.d.i4) == 0
+    doAssert ?.identity(d.i4) == 0
+    doAssert ?.identity(d) == nil
+    doAssert ?.identity(d[]) == default(typeof(d[]))
+    doAssert ?.identity(d[]).i4 == 0
+    var a: A
+    doAssert ?.identity(a) == default(A)
+    doAssert ?.identity(a.a0) == 0
+    doAssert ?.identity(a.d) == nil
+    doAssert ?.identity(a.d.i4) == 0
+
+  block: # lvalue semantic propagation
+    type
+      A = ref object
+        a0: A
+        a1: seq[A]
+        a2: int
+
+      B = object
+        b0: int
+        case cond: bool
+        of false: discard
+        of true:
+          b1: float
+
+    block:
+      var a: A
+      doAssert ?.a.a0.a1[0].a2.addr == nil
+      a = A(a2: 3)
+      doAssert ?.a.a0.a1[0].a2.addr == nil
+      a.a0 = a
+      a.a1 = @[a]
+      let p = ?.a.a0.a1[0].a2.addr
+      doAssert p != nil
+      p[] = 5
+      doAssert a.a2 == 5
+
+    block:
+      var b = B(cond: false, b0: 3)
+      let p = ?.b.b1.addr
+      doAssert p == nil
+      b = B(cond: true, b1: 4.5)
+      let p2 = ?.b.b1.addr
+      doAssert p2 != nil
+      p2[] = 4.6
+      doAssert b.b1 == 4.6
+      # useful pattern, impossible with Options
+      if (let p3 = ?.b.b1.addr; p3 != nil):
+        p3[] = 4.7
+      doAssert b.b1 == 4.7
+
+main()
+static: main()
diff --git a/tests/stdlib/twrongstattype.nim b/tests/stdlib/twrongstattype.nim
new file mode 100644
index 000000000..4a1fc30c6
--- /dev/null
+++ b/tests/stdlib/twrongstattype.nim
@@ -0,0 +1,14 @@
+# issue #24076
+
+when defined(macosx) or defined(freebsd) or defined(openbsd) or defined(netbsd):
+  import std/posix
+  proc uid(x: uint32): Uid = Uid(x)
+  var y: uint32
+  let myUid = geteuid()
+  discard myUid == uid(y)
+  proc dev(x: uint32): Dev = Dev(x)
+  let myDev = 1.Dev
+  discard myDev == dev(y)
+  proc nlink(x: uint32): Nlink = Nlink(x)
+  let myNlink = 1.Nlink
+  discard myNlink == nlink(y)
diff --git a/tests/stdlib/txmlgen.nim b/tests/stdlib/txmlgen.nim
deleted file mode 100644
index fa1dffe56..000000000
--- a/tests/stdlib/txmlgen.nim
+++ /dev/null
@@ -1,12 +0,0 @@
-discard """
-  file: "txmlgen.nim"
-  output: "<h1><a href=\"http://force7.de/nim\">Nim</a></h1>"
-"""
-import htmlgen
-
-var nim = "Nim"
-echo h1(a(href="http://force7.de/nim", nim))
-
-
-
-
diff --git a/tests/stdlib/txmltree.nim b/tests/stdlib/txmltree.nim
index a849859e3..add12a3fc 100644
--- a/tests/stdlib/txmltree.nim
+++ b/tests/stdlib/txmltree.nim
@@ -1,27 +1,120 @@
 discard """
-  file: "txmltree.nim"
-  output: '''true
-true
-true
-true
-true
-'''
+  matrix: "--mm:refc; --mm:orc"
 """
 
-import xmltree, strtabs
+import std/[xmltree, assertions, xmlparser]
 
-var x = <>a(href="nim.de", newText("www.nim-test.de"))
 
-echo($x == "<a href=\"nim.de\">www.nim-test.de</a>")
+block:
+  var
+    x: XmlNode
 
-echo(newText("foo").innerText == "foo")
-echo(newEntity("bar").innerText == "bar")
-echo(newComment("baz").innerText == "")
+  x = <>a(href = "http://nim-lang.org", newText("Nim rules."))
+  doAssert $x == """<a href="http://nim-lang.org">Nim rules.</a>"""
 
-let y = newXmlTree("x", [
-  newText("foo"),
-  newXmlTree("y", [
-    newText("bar")
+  x = <>outer(<>inner())
+  doAssert $x == """<outer>
+  <inner />
+</outer>"""
+
+  x = <>outer(<>middle(<>inner1(), <>inner2(), <>inner3(), <>inner4()))
+  doAssert $x == """<outer>
+  <middle>
+    <inner1 />
+    <inner2 />
+    <inner3 />
+    <inner4 />
+  </middle>
+</outer>"""
+
+  x = <>l0(<>l1(<>l2(<>l3(<>l4()))))
+  doAssert $x == """<l0>
+  <l1>
+    <l2>
+      <l3>
+        <l4 />
+      </l3>
+    </l2>
+  </l1>
+</l0>"""
+
+  x = <>l0(<>l1p1(), <>l1p2(), <>l1p3())
+  doAssert $x == """<l0>
+  <l1p1 />
+  <l1p2 />
+  <l1p3 />
+</l0>"""
+
+  x = <>l0(<>l1(<>l2p1(), <>l2p2()))
+  doAssert $x == """<l0>
+  <l1>
+    <l2p1 />
+    <l2p2 />
+  </l1>
+</l0>"""
+
+  x = <>l0(<>l1(<>l2_1(), <>l2_2(<>l3_1(), <>l3_2(), <>l3_3(<>l4_1(), <>l4_2(), <>l4_3())), <>l2_3(), <>l2_4()))
+  doAssert $x == """<l0>
+  <l1>
+    <l2_1 />
+    <l2_2>
+      <l3_1 />
+      <l3_2 />
+      <l3_3>
+        <l4_1 />
+        <l4_2 />
+        <l4_3 />
+      </l3_3>
+    </l2_2>
+    <l2_3 />
+    <l2_4 />
+  </l1>
+</l0>"""
+
+  let
+    innermost = newElement("innermost")
+    middle = newXmlTree("middle", [innermost])
+  innermost.add newText("innermost text")
+  x = newXmlTree("outer", [middle])
+  doAssert $x == """<outer>
+  <middle>
+    <innermost>innermost text</innermost>
+  </middle>
+</outer>"""
+
+  x = newElement("myTag")
+  x.add newText("my text")
+  x.add newElement("sonTag")
+  x.add newEntity("my entity")
+  doAssert $x == "<myTag>my text<sonTag />&my entity;</myTag>"
+
+block: # bug #21290
+  let x = newXmlTree("foo",[
+    newXmlTree("bar",[
+      newText("Hola"),
+      newXmlTree("qux",[
+        newXmlTree("plugh",[])
+      ])
+    ])
   ])
-])
-echo(y.innerText == "foobar")
+
+  let s = $x
+  doAssert $parseXml(s) == s
+  doAssert s == """<foo>
+  <bar>Hola<qux>    <plugh />  </qux></bar>
+</foo>"""
+
+block: #21541
+  let root = <>root()
+  root.add <>child(newText("hello"))
+  root.add <>more(newVerbatimText("hola"))
+  let s = $root
+  doAssert s == """<root>
+  <child>hello</child>
+  <more>hola</more>
+</root>"""
+
+  let temp = newVerbatimText("Hello!")
+  doAssert temp.text == "Hello!"
+  temp.text = "Hola!"
+  doAssert temp.text == "Hola!"
diff --git a/tests/stdlib/tyield.nim b/tests/stdlib/tyield.nim
new file mode 100644
index 000000000..f385ddd05
--- /dev/null
+++ b/tests/stdlib/tyield.nim
@@ -0,0 +1,258 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c cpp js"
+"""
+
+import std/[sugar, algorithm]
+import std/assertions
+
+block:
+  var x = @[(6.0, 6, '6'),
+            (5.0, 5, '5'),
+            (4.0, 4, '4'),
+            (3.0, 3, '3'),
+            (2.0, 2, '2'),
+            (1.0, 1, '1')]
+
+  let y = x.reversed
+
+  block:
+    let res = collect:
+      for (f, i, c) in x:
+        (f, i, c)
+
+    doAssert res == x
+
+  iterator popAscending[T](q: var seq[T]): T =
+    while q.len > 0: yield q.pop
+
+  block:
+    var res = collect:
+      for f, i, c in popAscending(x):
+        (f, i, c)
+
+    doAssert res == y
+
+    let z = reversed(res)
+    let res2 = collect:
+      for (f, i, c) in popAscending(res):
+        (f, i, c)
+
+    doAssert res2 == z
+
+
+block:
+  var visits = 0
+  block:
+    proc bar(): (int, int) =
+      inc visits
+      (visits, visits)
+
+    iterator foo(): (int, int) =
+      yield bar()
+
+    for a, b in foo():
+      doAssert a == b
+
+    doAssert visits == 1
+
+  block:
+    proc iterAux(a: seq[int], i: var int): (int, string) =
+      result = (a[i], $a[i])
+      inc i
+
+    iterator pairs(a: seq[int]): (int, string) =
+      var i = 0
+      while i < a.len:
+        yield iterAux(a, i)
+
+    var x = newSeq[int](10)
+    for i in 0 ..< x.len:
+      x[i] = i
+
+    let res = collect:
+      for k, v in x:
+        (k, v)
+
+    let expected = collect:
+      for i in 0 ..< x.len:
+        (i, $i)
+
+    doAssert res == expected
+
+  block:
+    proc bar(): (int, int, int) =
+      inc visits
+      (visits, visits, visits)
+
+    iterator foo(): (int, int, int) =
+      yield bar()
+
+    for a, b, c in foo():
+      doAssert a == b
+
+    doAssert visits == 2
+
+
+  block:
+
+    proc bar(): int =
+      inc visits
+      visits
+
+    proc car(): int =
+      inc visits
+      visits
+
+    iterator foo(): (int, int) =
+      yield (bar(), car())
+      yield (bar(), car())
+
+    for a, b in foo():
+      doAssert b == a + 1
+
+    doAssert visits == 6
+
+
+  block:
+    proc bar(): (int, int) =
+      inc visits
+      (visits, visits)
+
+    proc t2(): int = 99
+
+    iterator foo(): (int, int) =
+      yield (12, t2())
+      yield bar()
+
+    let res = collect:
+      for (a, b) in foo():
+        (a, b)
+
+    doAssert res == @[(12, 99), (7, 7)]
+    doAssert visits == 7
+
+  block:
+    proc bar(): (int, int) =
+      inc visits
+      (visits, visits)
+
+    proc t2(): int = 99
+
+    iterator foo(): (int, int) =
+      yield ((12, t2()))
+      yield (bar())
+
+    let res = collect:
+      for (a, b) in foo():
+        (a, b)
+
+    doAssert res == @[(12, 99), (8, 8)]
+    doAssert visits == 8
+
+  block:
+    proc bar(): (int, int) =
+      inc visits
+      (visits, visits)
+
+    proc t1(): int = 99
+    proc t2(): int = 99
+
+    iterator foo(): (int, int) =
+      yield (t1(), t2())
+      yield bar()
+
+    let res = collect:
+      for a, b in foo():
+        (a, b)
+
+    doAssert res == @[(99, 99), (9, 9)]
+    doAssert visits == 9
+
+
+  block:
+    proc bar(): ((int, int), string) =
+      inc visits
+      ((visits, visits), $visits)
+
+    proc t2(): int = 99
+
+    iterator foo(): ((int, int), string) =
+      yield ((1, 2), $t2())
+      yield bar()
+
+    let res = collect:
+      for a, b in foo():
+        (a, b)
+
+    doAssert res == @[((1, 2), "99"), ((10, 10), "10")]
+    doAssert visits == 10
+
+
+  block:
+    proc bar(): (int, int) =
+      inc visits
+      (visits, visits)
+
+    iterator foo(): (int, int) =
+      yield (for i in 0 ..< 10: discard bar(); bar())
+      yield (bar())
+
+    let res = collect:
+      for (a, b) in foo():
+        (a, b)
+
+    doAssert res == @[(21, 21), (22, 22)]
+
+  block:
+    proc bar(): (int, int) =
+      inc visits
+      (visits, visits)
+
+    proc t2(): int = 99
+
+    iterator foo(): (int, int) =
+      yield if true: bar() else: (t2(), t2())
+      yield (bar())
+
+    let res = collect:
+      for a, b in foo():
+        (a, b)
+
+    doAssert res == @[(23, 23), (24, 24)]
+
+
+block:
+  iterator foo(): (int, int, int) =
+    var time = 777
+    yield (1, time, 3)
+
+  let res = collect:
+    for a, b, c in foo():
+      (a, b, c)
+
+  doAssert res == @[(1, 777, 3)]
+
+block:
+  iterator foo(): (int, int, int) =
+    var time = 777
+    yield (1, time, 3)
+
+  let res = collect:
+    for t in foo():
+      (t[0], t[1], t[2])
+
+  doAssert res == @[(1, 777, 3)]
+
+
+block:
+  proc bar(): (int, int, int) =
+    (1, 2, 3)
+  iterator foo(): (int, int, int) =
+    yield bar()
+
+  let res = collect:
+    for a, b, c in foo():
+      (a, b, c)
+
+  doAssert res == @[(1, 2, 3)]
diff --git a/tests/stdlib/unixsockettest.nim b/tests/stdlib/unixsockettest.nim
new file mode 100644
index 000000000..8f95d0808
--- /dev/null
+++ b/tests/stdlib/unixsockettest.nim
@@ -0,0 +1,26 @@
+import std/[assertions, net, os]
+
+let unixSocketPath = getCurrentDir() / "usox"
+
+removeFile(unixSocketPath)
+
+let socket = newSocket(AF_UNIX, SOCK_STREAM, IPPROTO_NONE)
+socket.bindUnix(unixSocketPath)
+socket.listen()
+
+var
+  clientSocket: Socket
+  data: string
+
+socket.accept(clientSocket)
+clientSocket.readLine(data)
+doAssert data == "data sent through the socket"
+clientSocket.send("Hello from server\c\l")
+
+clientSocket.readLine(data)
+doAssert data == "bye"
+clientSocket.send("bye\c\l")
+
+clientSocket.close()
+socket.close()
+removeFile(unixSocketPath)
diff --git a/tests/stdlib/uselocks.nim b/tests/stdlib/uselocks.nim
index cde9641b2..f87623b5e 100644
--- a/tests/stdlib/uselocks.nim
+++ b/tests/stdlib/uselocks.nim
@@ -1,4 +1,5 @@
 import locks
+import std/assertions
 
 type MyType* [T] = object
   lock: Lock
@@ -9,3 +10,7 @@ proc createMyType*[T]: MyType[T] =
 proc use* (m: var MyType): int =
   withLock m.lock:
     result = 3
+
+block:
+  var l: Lock
+  doAssert $l == "()"
diff --git a/tests/stmt/tforloop_tuple_multiple_underscore.nim b/tests/stmt/tforloop_tuple_multiple_underscore.nim
new file mode 100644
index 000000000..96804df18
--- /dev/null
+++ b/tests/stmt/tforloop_tuple_multiple_underscore.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "the special identifier '_' is ignored in declarations and cannot be used"
+"""
+
+iterator iter(): (int, int, int) =
+  yield (1, 1, 2)
+
+
+for (_, i, _) in iter():
+  echo _
diff --git a/tests/stmt/tforloop_tuple_underscore.nim b/tests/stmt/tforloop_tuple_underscore.nim
new file mode 100644
index 000000000..eda42d527
--- /dev/null
+++ b/tests/stmt/tforloop_tuple_underscore.nim
@@ -0,0 +1,16 @@
+discard """
+  errormsg: "the special identifier '_' is ignored in declarations and cannot be used"
+"""
+
+iterator iter(): (int, int) =
+  yield (1, 2)
+  yield (3, 4)
+  yield (1, 2)
+  yield (3, 4)
+  yield (1, 2)
+  yield (3, 4)
+
+
+for (_, i) in iter():
+  echo _
+
diff --git a/tests/stmt/tforloop_underscore.nim b/tests/stmt/tforloop_underscore.nim
new file mode 100644
index 000000000..ce1c86386
--- /dev/null
+++ b/tests/stmt/tforloop_underscore.nim
@@ -0,0 +1,6 @@
+discard """
+  errormsg: "the special identifier '_' is ignored in declarations and cannot be used"
+"""
+
+for _ in ["a"]:
+  echo _
diff --git a/tests/stmt/tgenericsunderscore.nim b/tests/stmt/tgenericsunderscore.nim
new file mode 100644
index 000000000..be2b8ec78
--- /dev/null
+++ b/tests/stmt/tgenericsunderscore.nim
@@ -0,0 +1,4 @@
+# issue #21435
+
+proc foo[_](x: typedesc[_]): string = "BAR" #[tt.Error
+                        ^ the special identifier '_' is ignored in declarations and cannot be used]#
diff --git a/tests/stmt/tmiscunderscore.nim b/tests/stmt/tmiscunderscore.nim
new file mode 100644
index 000000000..c4bae1c3d
--- /dev/null
+++ b/tests/stmt/tmiscunderscore.nim
@@ -0,0 +1,15 @@
+import std/assertions
+
+block:
+  proc _() = echo "one"
+  doAssert not compiles(_())
+  proc _() = echo "two"
+  doAssert not compiles(_())
+
+block:
+  type _ = int
+  doAssert not (compiles do:
+    let x: _ = 3)
+  type _ = float
+  doAssert not (compiles do:
+    let x: _ = 3)
diff --git a/tests/strictnotnil/tnilcheck.nim b/tests/strictnotnil/tnilcheck.nim
new file mode 100644
index 000000000..b8057de74
--- /dev/null
+++ b/tests/strictnotnil/tnilcheck.nim
@@ -0,0 +1,381 @@
+discard """
+action: compile
+"""
+
+import tables
+
+{.experimental: "strictNotNil".}
+
+type
+  Nilable* = ref object
+    a*: int
+    field*: Nilable
+    
+  NonNilable* = Nilable not nil
+
+  Nilable2* = nil NonNilable
+
+
+# proc `[]`(a: Nilable, b: int): Nilable =
+#   nil
+
+
+# Nilable tests
+
+
+
+# test deref
+proc testDeref(a: Nilable) =
+  echo a.a > 0 #[tt.Warning
+       ^ can't deref a, it might be nil
+  ]#
+
+
+
+# # # test if else
+proc testIfElse(a: Nilable) =
+  if a.isNil:
+    echo a.a #[tt.Warning
+         ^ can't deref a, it is nil
+    ]#
+  else:
+    echo a.a # ok
+
+proc testIfNoElse(a: Nilable) =
+  if a.isNil:
+    echo a.a #[tt.Warning
+         ^ can't deref a, it is nil
+         ]#
+  echo a.a #[tt.Warning
+       ^ can't deref a, it might be nil
+   ]#
+
+proc testIfReturn(a: Nilable) =
+  if not a.isNil:
+    return
+  echo a.a #[tt.Warning
+       ^ can't deref a, it is nil
+  ]#
+
+proc testIfBreak(a: seq[Nilable]) =
+  for b in a:
+    if not b.isNil:
+      break
+    echo b.a #[tt.Warning
+         ^ can't deref b, it is nil
+    ]#
+
+proc testIfContinue(a: seq[Nilable]) =
+  for b in a:
+    if not b.isNil:
+      continue
+    echo b.a #[tt.Warning
+         ^ can't deref b, it is nil
+    ]#
+
+proc testIfRaise(a: Nilable) =
+  if not a.isNil:
+    raise newException(ValueError, "")
+  echo a.a #[tt.Warning
+       ^ can't deref a, it is nil
+  ]#
+
+proc testIfElif(a: Nilable) =
+  var c = 0
+  if c == 0:
+    echo a.a #[tt.Warning
+         ^ can't deref a, it might be nil
+      ]#
+  elif c == 1:
+    echo a.a #[tt.Warning
+         ^ can't deref a, it might be nil
+      ]#
+  elif not a.isNil:
+    echo a.a # ok
+  elif c == 2:
+    echo 0
+  else:
+    echo a.a #[tt.Warning
+         ^ can't deref a, it is nil
+    ]#
+
+proc testAssignUnify(a: Nilable, b: int) =
+  var a2 = a
+  if b == 0:
+    a2 = Nilable()
+  echo a2.a #[tt.Warning
+       ^ can't deref a2, it might be nil
+  ]#
+
+
+# # test assign in branch and unifiying that with the main block after end of branch
+proc testAssignUnifyNil(a: Nilable, b: int) =
+  var a2 = a
+  if b == 0:
+    a2 = nil
+  echo a2.a #[tt.Warning
+       ^ can't deref a2, it might be nil
+  ]#
+
+# test loop
+proc testForLoop(a: Nilable) =
+  var b = Nilable()
+  for i in 0 .. 5:
+    echo b.a #[tt.Warning
+         ^ can't deref b, it might be nil
+    ]#
+    if i == 2:
+      b = a
+  echo b.a #[tt.Warning
+       ^ can't deref b, it might be nil
+  ]#
+
+
+
+# # TODO implement this after discussion
+# # proc testResultCompoundNonNilableElement(a: Nilable): (NonNilable, NonNilable) = #[t t.Warning
+# #      ^ result might be not initialized, so it or an element might be nil
+# # ]#
+# #   if not a.isNil:
+# #     result[0] = a #[t t.Warning
+# #                 ^ can't assign nilable to non nilable: it might be nil
+# #     #]
+
+# # proc testNonNilDeref(a: NonNilable) =
+# #   echo a.a # ok
+
+
+
+# # # not only calls: we can use partitions for dependencies for field aliases
+# # # so we can detect on change what does this affect or was this mutated between us and the original field
+
+# # proc testRootAliasField(a: Nilable) =
+# #   var aliasA = a
+# #   if not a.isNil and not a.field.isNil:
+# #     aliasA.field = nil
+# #     # a.field = nil
+# #     # aliasA = nil 
+# #     echo a.field.a # [tt.Warning
+# #          ^ can't deref a.field, it might be nil
+# #     ]#
+
+
+proc testAliasChanging(a: Nilable) =
+  var b = a
+  var aliasA = b
+  b = Nilable()
+  if not b.isNil:
+    echo aliasA.a #[tt.Warning
+         ^ can't deref aliasA, it might be nil
+    ]#
+
+# # TODO
+# # proc testAliasUnion(a: Nilable) =
+# #   var a2 = a
+# #   var b = a2
+# #   if a.isNil:
+# #     b = Nilable()
+# #     a2 = nil
+# #   else:
+# #     a2 = Nilable()
+# #     b = a2
+# #   if not b.isNil:
+# #     echo a2.a #[ tt.Warning
+# #          ^ can't deref a2, it might be nil
+# #     ]#
+
+# # TODO after alias support
+# #proc callVar(a: var Nilable) =
+# #  a.field = nil
+
+
+# # TODO ptr support
+# # proc testPtrAlias(a: Nilable) =
+# #   # pointer to a: hm.
+# #   # alias to a?
+# #   var ptrA = a.addr # {0, 1} 
+# #   if not a.isNil: # {0, 1}
+# #     ptrA[] = nil # {0, 1} 0: MaybeNil 1: MaybeNil
+# #     echo a.a #[ tt.Warning
+# #          ^ can't deref a, it might be nil
+# #     ]#
+
+# # TODO field stuff
+# # currently it just doesnt support dot, so accidentally it shows a warning but because that
+# # not alias i think
+# # proc testFieldAlias(a: Nilable) =
+# #   var b = a # {0, 1} {2} 
+# #   if not a.isNil and not a.field.isNil: # {0, 1} {2}
+# #     callVar(b) # {0, 1} {2} 0: Safe 1: Safe
+# #     echo a.field.a #[ tt.Warning
+# #           ^ can't deref a.field, it might be nil
+# #     ]#
+# #
+# # proc testUniqueHashTree(a: Nilable): Nilable =
+# #   # TODO what would be a clash
+# #   var field = 0
+# #   if not a.isNil and not a.field.isNil:
+# #     # echo a.field.a
+# #     echo a[field].a
+# #   result = Nilable()
+  
+# # proc testSeparateShadowingResult(a: Nilable): Nilable =
+# #   result = Nilable()
+# #   if not a.isNil:
+# #     var result: Nilable = nil
+# #   echo result.a
+
+
+proc testCStringDeref(a: cstring) =
+  echo a[0] #[tt.Warning
+       ^ can't deref a, it might be nil
+  ]#
+
+
+proc testNilablePtr(a: ptr int) =
+  if not a.isNil:
+    echo a[] # ok
+  echo a[] #[tt.Warning
+       ^ can't deref a, it might be nil
+  ]#
+
+# # proc testNonNilPtr(a: ptr int not nil) =
+# #   echo a[] # ok
+
+proc raiseCall: NonNilable = #[tt.Warning
+^ return value is nil
+]#
+  raise newException(ValueError, "raise for test") 
+
+# proc testTryCatch(a: Nilable) =
+#   var other = a
+#   try:
+#     other = raiseCall()
+#   except:
+#     discard
+#   echo other.a #[ tt.Warning
+#             ^ can't deref other, it might be nil
+#   ]#
+
+# # proc testTryCatchDetectNoRaise(a: Nilable) =
+# #   var other = Nilable()
+# #   try:
+# #     other = nil
+# #     other = a
+# #     other = Nilable()
+# #   except:
+# #     other = nil
+# #   echo other.a # ok
+
+# # proc testTryCatchDetectFinally =
+# #   var other = Nilable()
+# #   try:
+# #     other = nil
+# #     other = Nilable()
+# #   except:
+# #     other = Nilable()
+# #   finally:
+# #     other = nil
+# #   echo other.a # can't deref other: it is nil
+
+# # proc testTryCatchDetectNilableWithRaise(b: bool) =
+# #   var other = Nilable()
+# #   try:
+# #     if b:
+# #       other = nil
+# #     else:
+# #       other = Nilable()
+# #       var other2 = raiseCall()
+# #   except:
+# #     echo other.a # ok
+
+# #   echo other.a # can't deref a: it might be nil
+
+# # proc testRaise(a: Nilable) =
+# #   if a.isNil:
+# #     raise newException(ValueError, "a == nil")
+# #   echo a.a # ok
+
+
+# # proc testBlockScope(a: Nilable) =
+# #   var other = a
+# #   block:
+# #     var other = Nilable()
+# #     echo other.a # ok
+# #   echo other.a # can't deref other: it might be nil
+
+# # ok we can't really get the nil value from here, so should be ok
+# # proc testDirectRaiseCall: NonNilable =
+# #   var a = raiseCall()
+# #   result = NonNilable()
+
+# # proc testStmtList =
+# #   var a = Nilable()
+# #   block:
+# #     a = nil
+# #     a = Nilable()
+# #   echo a.a # ok
+
+proc callChange(a: Nilable) =
+  if not a.isNil:
+    a.field = nil
+
+proc testCallChangeField =
+  var a = Nilable()
+  a.field = Nilable()
+  callChange(a)
+  echo a.field.a #[ tt.Warning
+        ^ can't deref a.field, it might be nil
+       ]#
+
+proc testReassignVarWithField =
+  var a = Nilable()
+  a.field = Nilable()
+  echo a.field.a # ok
+  a = Nilable()
+  echo a.field.a #[ tt.Warning
+        ^ can't deref a.field, it might be nil
+        ]#
+
+
+proc testItemDeref(a: var seq[Nilable]) =
+  echo a[0].a #[tt.Warning
+        ^ can't deref a[0], it might be nil
+       ]#
+  a[0] = Nilable() # good: now .. if we dont track, how do we know 
+  echo a[0].a # ok
+  echo a[1].a #[tt.Warning
+        ^ can't deref a[1], it might be nil
+  ]#
+  var b = 1
+  if a[b].isNil:
+    echo a[1].a #[tt.Warning
+          ^ can't deref a[1], it might be nil
+    ]#
+    var c = 0
+    echo a[c].a #[tt.Warning
+          ^ can't deref a[c], it might be nil
+    ]#
+
+  # known false positive
+  if not a[b].isNil:
+    echo a[b].a #[tt.Warning
+          ^ can't deref a[b], it might be nil
+    ]#
+
+  const c = 0
+  if a[c].isNil:
+    echo a[0].a #[tt.Warning
+          ^ can't deref a[0], it is nil
+    ]#
+  a[c] = Nilable()
+  echo a[0].a # ok
+
+
+
+# # # proc test10(a: Nilable) =
+# # #   if not a.isNil and not a.b.isNil:
+# # #     c_memset(globalA.addr, 0, globalA.sizeOf.csize_t)
+# # #     globalA = nil
+# # #     echo a.a # can't deref a: it might be nil
+
diff --git a/tests/strictnotnil/tnilcheck_no_warnings.nim b/tests/strictnotnil/tnilcheck_no_warnings.nim
new file mode 100644
index 000000000..5ec9bc575
--- /dev/null
+++ b/tests/strictnotnil/tnilcheck_no_warnings.nim
@@ -0,0 +1,182 @@
+discard """
+cmd: "nim check --warningAsError:StrictNotNil $file"
+action: "compile"
+"""
+
+import tables
+
+{.experimental: "strictNotNil".}
+
+type
+  Nilable* = ref object
+    a*: int
+    field*: Nilable
+    
+  NonNilable* = Nilable not nil
+
+  Nilable2* = nil NonNilable
+
+
+# proc `[]`(a: Nilable, b: int): Nilable =
+#   nil
+
+
+# Nilable tests
+
+
+
+# # test and
+proc testAnd(a: Nilable) =
+  echo not a.isNil and a.a > 0 # ok
+
+
+# test else branch and inferring not isNil
+# proc testElse(a: Nilable, b: int) =
+#   if a.isNil:
+#     echo 0
+#   else:
+#     echo a.a
+
+# test that here we can infer that n can't be nil anymore
+proc testNotNilAfterAssign(a: Nilable, b: int) =
+  var n = a # a: MaybeNil n: MaybeNil
+  if n.isNil: # n: Safe a: MaybeNil
+    n = Nilable() # n: Safe a: MaybeNil
+  echo n.a # ok
+
+proc callVar(a: var Nilable) =
+   a = nil
+
+proc testVarAlias(a: Nilable) = # a: 0 aliasA: 1 {0} {1} 
+  var aliasA = a # {0, 1} 0 MaybeNil 1 MaybeNil
+  if not a.isNil: # {0, 1} 0 Safe 1 Safe
+    callVar(aliasA) # {0, 1} 0 MaybeNil 1 MaybeNil
+    # if aliasA stops being in alias: it might be nil, but then a is still not nil
+    # if not: it cant be nil as it still points here
+    echo a.a # ok 
+
+proc testAliasCheck(a: Nilable) =
+  var aliasA = a
+  if not a.isNil:
+    echo aliasA.a # ok
+
+proc testFieldCheck(a: Nilable) =
+  if not a.isNil and not a.field.isNil:
+    echo a.field.a # ok
+
+proc testTrackField =
+  var a = Nilable(field: Nilable())
+  echo a.field.a # ok
+
+proc testNonNilDeref(a: NonNilable) =
+  echo a.a # ok
+
+# # not only calls: we can use partitions for dependencies for field aliases
+# # so we can detect on change what does this affect or was this mutated between us and the original field
+
+
+# proc testUniqueHashTree(a: Nilable): Nilable =
+#   # TODO what would be a clash
+#   var field = 0
+#   if not a.isNil and not a.field.isNil:
+#     # echo a.field.a
+#     echo a[field].a
+#   result = Nilable()
+  
+proc testSeparateShadowingResult(a: Nilable): Nilable =
+  result = Nilable()
+  if not a.isNil:
+    var result: Nilable = nil
+  echo result.a
+
+
+proc testNonNilCString(a: cstring not nil) =
+  echo a[0] # ok
+
+proc testNonNilPtr(a: ptr int not nil) =
+  echo a[] # ok
+
+
+# proc testTryCatchDetectNoRaise(a: Nilable) =
+#   var other = Nilable()
+#   try:
+#     other = nil
+#     other = a
+#     other = Nilable()
+#   except:
+#     other = nil
+#   echo other.a # ok
+
+# proc testTryCatchDetectFinally =
+#   var other = Nilable()
+#   try:
+#     other = nil
+#     other = Nilable()
+#   except:
+#     other = Nilable()
+#   finally:
+#     other = nil
+#   echo other.a # can't deref other: it is nil
+
+# proc testTryCatchDetectNilableWithRaise(b: bool) =
+#   var other = Nilable()
+#   try:
+#     if b:
+#       other = nil
+#     else:
+#       other = Nilable()
+#       var other2 = raiseCall()
+#   except:
+#     echo other.a # ok
+
+#   echo other.a # can't deref a: it might be nil
+
+proc testRaise(a: Nilable) =
+  if a.isNil:
+    raise newException(ValueError, "a == nil")
+  echo a.a # ok
+
+# proc testBlockScope(a: Nilable) =
+#   var other = a
+#   block:
+#     var other = Nilable()
+#     echo other.a # ok
+#   echo other.a # can't deref other: it might be nil
+
+# # (ask Araq about this: not supported yet) ok we can't really get the nil value from here, so should be ok
+# proc testDirectRaiseCall: NonNilable =
+#   var a = raiseCall()
+#   result = NonNilable()
+
+proc testStmtList =
+  var a = Nilable()
+  block:
+    a = nil
+    a = Nilable()
+  echo a.a # ok
+
+proc testItemDerefNoWarning(a: var seq[Nilable]) =
+  a[0] = Nilable() # good: now .. if we dont track, how do we know 
+  echo a[0].a # ok
+  var b = 1
+
+  const c = 0
+  a[c] = Nilable()
+  echo a[0].a # ok
+
+# proc callChange(a: Nilable) =
+#   a.field = nil
+
+# proc testCallAlias =
+#   var a = Nilable(field: Nilable())
+#   callChange(a)
+#   echo a.field.a # can't deref a.field, it might be nil
+
+# # proc test10(a: Nilable) =
+# #   if not a.isNil and not a.b.isNil:
+# #     c_memset(globalA.addr, 0, globalA.sizeOf.csize_t)
+# #     globalA = nil
+# #     echo a.a # can't deref a: it might be nil
+
+var nilable: Nilable
+var withField = Nilable(a: 0, field: Nilable())
diff --git a/tests/stylecheck/fileinfo.nim b/tests/stylecheck/fileinfo.nim
new file mode 100644
index 000000000..d6faf0c73
--- /dev/null
+++ b/tests/stylecheck/fileinfo.nim
@@ -0,0 +1,2 @@
+# fileinfo.nim
+type FileInfo* = object
\ No newline at end of file
diff --git a/tests/stylecheck/foreign_package/foreign_package.nim b/tests/stylecheck/foreign_package/foreign_package.nim
new file mode 100644
index 000000000..f95be006c
--- /dev/null
+++ b/tests/stylecheck/foreign_package/foreign_package.nim
@@ -0,0 +1 @@
+include ../thint
\ No newline at end of file
diff --git a/tests/stylecheck/foreign_package/foreign_package.nimble b/tests/stylecheck/foreign_package/foreign_package.nimble
new file mode 100644
index 000000000..a2c49e389
--- /dev/null
+++ b/tests/stylecheck/foreign_package/foreign_package.nimble
@@ -0,0 +1,2 @@
+# See `tstyleCheck`
+# Needed to mark `mstyleCheck` as a foreign package.
diff --git a/tests/stylecheck/t20397.nim b/tests/stylecheck/t20397.nim
new file mode 100644
index 000000000..486a97d73
--- /dev/null
+++ b/tests/stylecheck/t20397.nim
@@ -0,0 +1,4 @@
+{.hintAsError[Name]:on.}
+var a_b = 1
+discard a_b
+{.hintAsError[Name]:off.}
\ No newline at end of file
diff --git a/tests/stylecheck/t20397_1.nim b/tests/stylecheck/t20397_1.nim
new file mode 100644
index 000000000..76c03dca1
--- /dev/null
+++ b/tests/stylecheck/t20397_1.nim
@@ -0,0 +1,8 @@
+discard """
+  matrix: "--styleCheck:off --hint:Name:on"
+"""
+
+{.hintAsError[Name]:on.}
+var a_b = 1
+discard a_b
+{.hintAsError[Name]:off.}
diff --git a/tests/stylecheck/t20397_2.nim b/tests/stylecheck/t20397_2.nim
new file mode 100644
index 000000000..3b8e1c4d6
--- /dev/null
+++ b/tests/stylecheck/t20397_2.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "'a_b' should be: 'aB'"
+  matrix: "--styleCheck:error"
+"""
+
+var a_b = 1
+discard a_b
\ No newline at end of file
diff --git a/tests/stylecheck/taccept.nim b/tests/stylecheck/taccept.nim
new file mode 100644
index 000000000..43a9ab71f
--- /dev/null
+++ b/tests/stylecheck/taccept.nim
@@ -0,0 +1,22 @@
+discard """
+  matrix: "--styleCheck:error --styleCheck:usages"
+"""
+
+import std/[asyncdispatch, nre]
+
+type
+  Name = object
+    id: int
+
+template hello =
+  var iD = "string"
+  var name: Name
+  doAssert name.id == 0
+  doAssert iD == "string"
+
+hello()
+
+# bug #12955
+import os
+import fileinfo
+var xs: seq[fileinfo.FileInfo]
diff --git a/tests/stylecheck/tforeign_package.nim b/tests/stylecheck/tforeign_package.nim
new file mode 100644
index 000000000..8594ad802
--- /dev/null
+++ b/tests/stylecheck/tforeign_package.nim
@@ -0,0 +1,16 @@
+discard """
+  matrix: "--errorMax:0 --styleCheck:error"
+  action: compile
+"""
+
+import foreign_package/foreign_package
+
+# This call tests that:
+#   - an instantiation of a generic in a foreign package doesn't raise errors
+#     when the generic body contains:
+#     - definition and usage violations
+#     - builtin pragma usage violations
+#     - user pragma usage violations
+#   - definition violations in foreign packages are ignored
+#   - usage violations in foreign packages are ignored
+genericProc[int]()
diff --git a/tests/stylecheck/thint.nim b/tests/stylecheck/thint.nim
new file mode 100644
index 000000000..c19aac1b8
--- /dev/null
+++ b/tests/stylecheck/thint.nim
@@ -0,0 +1,43 @@
+discard """
+  matrix: "--styleCheck:hint"
+  action: compile
+"""
+
+# Test violating ident definition:
+{.pragma: user_pragma.} #[tt.Hint
+        ^ 'user_pragma' should be: 'userPragma' [Name] ]#
+
+# Test violating ident usage style matches definition style:
+{.userPragma.} #[tt.Hint
+  ^ 'userPragma' should be: 'user_pragma' [template declared in thint.nim(7, 9)] [Name] ]#
+
+# Test violating builtin pragma usage style:
+{.no_side_effect.}: #[tt.Hint
+  ^ 'no_side_effect' should be: 'noSideEffect' [Name] ]#
+  discard 0
+
+# Test:
+#  - definition style violation
+#  - user pragma usage style violation
+#  - builtin pragma usage style violation
+proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Hint
+     ^ 'generic_proc' should be: 'genericProc' [Name]; tt.Hint
+                        ^ 'no_destroy' should be: 'nodestroy' [Name]; tt.Hint
+                                    ^ 'userPragma' should be: 'user_pragma' [template declared in thint.nim(7, 9)] [Name] ]#
+  # Test definition style violation:
+  let snake_case = 0 #[tt.Hint
+      ^ 'snake_case' should be: 'snakeCase' [Name] ]#
+  # Test user pragma definition style violation:
+  {.pragma: another_user_pragma.} #[tt.Hint
+          ^ 'another_user_pragma' should be: 'anotherUserPragma' [Name] ]#
+  # Test user pragma usage style violation:
+  {.anotherUserPragma.} #[tt.Hint
+    ^ 'anotherUserPragma' should be: 'another_user_pragma' [template declared in thint.nim(31, 11)] [Name] ]#
+  # Test violating builtin pragma usage style:
+  {.no_side_effect.}: #[tt.Hint
+    ^ 'no_side_effect' should be: 'noSideEffect' [Name] ]#
+    # Test usage style violation:
+    discard snakeCase #[tt.Hint
+            ^ 'snakeCase' should be: 'snake_case' [let declared in thint.nim(28, 7)] [Name] ]#
+
+generic_proc[int]()
diff --git a/tests/stylecheck/treject.nim b/tests/stylecheck/treject.nim
new file mode 100644
index 000000000..458a2d039
--- /dev/null
+++ b/tests/stylecheck/treject.nim
@@ -0,0 +1,17 @@
+discard """
+  action: reject
+  nimout: '''treject.nim(14, 13) Error: 'iD' should be: 'id' [field declared in treject.nim(9, 5)]'''
+  matrix: "--styleCheck:error --styleCheck:usages  --hint:Name:on"
+"""
+
+type
+  Name = object
+    id: int
+
+template hello =
+  var iD = "string"
+  var name: Name
+  echo name.iD
+  echo iD
+
+hello()
diff --git a/tests/stylecheck/tusages.nim b/tests/stylecheck/tusages.nim
new file mode 100644
index 000000000..b689dabb3
--- /dev/null
+++ b/tests/stylecheck/tusages.nim
@@ -0,0 +1,22 @@
+discard """
+  action: reject
+  nimout: '''tusages.nim(20, 5) Error: 'BAD_STYLE' should be: 'BADSTYLE' [proc declared in tusages.nim(9, 6)]'''
+  matrix: "--styleCheck:error --styleCheck:usages --hint:all:off --hint:Name:on"
+"""
+
+import strutils
+
+proc BADSTYLE(c: char) = discard
+
+proc toSnakeCase(s: string): string =
+  result = newStringOfCap(s.len + 3)
+  for i in 0..<s.len:
+    if s[i] in {'A'..'Z'}:
+      if i > 0 and s[i-1] in {'a'..'z'}:
+        result.add '_'
+      result.add toLowerAscii(s[i])
+    else:
+      result.add s[i]
+    BAD_STYLE(s[i])
+
+echo toSnakeCase("fooBarBaz Yes")
diff --git a/tests/system/helpers/readall_echo.nim b/tests/system/helpers/readall_echo.nim
index 79937bf6f..2891ef3ae 100644
--- a/tests/system/helpers/readall_echo.nim
+++ b/tests/system/helpers/readall_echo.nim
@@ -1,2 +1,2 @@
-when isMainModule:
+when true:
   echo(stdin.readAll)
diff --git a/tests/system/t10307.nim b/tests/system/t10307.nim
new file mode 100644
index 000000000..b5a93c5c6
--- /dev/null
+++ b/tests/system/t10307.nim
@@ -0,0 +1,24 @@
+discard """
+  cmd: "nim c --mm:refc -d:useGcAssert $file"
+  output: '''running someProc(true)
+res: yes
+yes
+running someProc(false)
+res: 
+
+'''
+"""
+
+proc someProc(x:bool):cstring =
+  var res:string = ""
+  if x:
+    res = "yes"
+  echo "res: ", res
+  GC_ref(res)
+  result = res
+
+echo "running someProc(true)"
+echo someProc(true)
+
+echo "running someProc(false)"
+echo someProc(false)
diff --git a/tests/system/t20938.nim b/tests/system/t20938.nim
new file mode 100644
index 000000000..7341cbb91
--- /dev/null
+++ b/tests/system/t20938.nim
@@ -0,0 +1,12 @@
+discard """
+  cmd: "nim c --mm:refc $file"
+  action: "compile"
+"""
+
+template foo(x: typed) =
+  discard x
+
+foo:
+  var x = "hello"
+  x.shallowCopy("test")
+  true
diff --git a/tests/system/t7894.nim b/tests/system/t7894.nim
new file mode 100644
index 000000000..60dbe86cb
--- /dev/null
+++ b/tests/system/t7894.nim
@@ -0,0 +1,23 @@
+discard """
+disabled: true
+"""
+
+# CI integration servers are out of memory for this test
+
+const size = 250000000
+
+proc main() =
+
+  var saved = newSeq[seq[int8]]()
+
+  for i in 0..22:
+    # one of these is 0.25GB.
+    #echo i
+    var x = newSeq[int8](size)
+    saved.add(x)
+
+  for x in saved:
+    #echo x.len
+    doAssert x.len == size
+
+main()
diff --git a/tests/system/talloc.nim b/tests/system/talloc.nim
index 18396041d..a81fef481 100644
--- a/tests/system/talloc.nim
+++ b/tests/system/talloc.nim
@@ -1,54 +1,56 @@
+# was: appveyor is "out of memory"
+
 var x: ptr int
 
 x = cast[ptr int](alloc(7))
-assert x != nil
+doAssert x != nil
 x = cast[ptr int](x.realloc(2))
-assert x != nil
+doAssert x != nil
 x.dealloc()
 
 x = createU(int, 3)
-assert x != nil
+doAssert x != nil
 x.dealloc()
 
 x = create(int, 4)
-assert cast[ptr array[4, int]](x)[0] == 0
-assert cast[ptr array[4, int]](x)[1] == 0
-assert cast[ptr array[4, int]](x)[2] == 0
-assert cast[ptr array[4, int]](x)[3] == 0
+doAssert cast[ptr array[4, int]](x)[0] == 0
+doAssert cast[ptr array[4, int]](x)[1] == 0
+doAssert cast[ptr array[4, int]](x)[2] == 0
+doAssert cast[ptr array[4, int]](x)[3] == 0
 
 x = x.resize(4)
-assert x != nil
+doAssert x != nil
 x.dealloc()
 
 x = cast[ptr int](allocShared(100))
-assert x != nil
+doAssert x != nil
 deallocShared(x)
 
 x = createSharedU(int, 3)
-assert x != nil
+doAssert x != nil
 x.deallocShared()
 
 x = createShared(int, 3)
-assert x != nil
-assert cast[ptr array[3, int]](x)[0] == 0
-assert cast[ptr array[3, int]](x)[1] == 0
-assert cast[ptr array[3, int]](x)[2] == 0
+doAssert x != nil
+doAssert cast[ptr array[3, int]](x)[0] == 0
+doAssert cast[ptr array[3, int]](x)[1] == 0
+doAssert cast[ptr array[3, int]](x)[2] == 0
 
-assert x != nil
+doAssert x != nil
 x = cast[ptr int](x.resizeShared(2))
-assert x != nil
+doAssert x != nil
 x.deallocShared()
 
 x = create(int, 10)
-assert x != nil
+doAssert x != nil
 x = x.resize(12)
-assert x != nil
+doAssert x != nil
 x.dealloc()
 
 x = createShared(int, 1)
-assert x != nil
+doAssert x != nil
 x = x.resizeShared(1)
-assert x != nil
+doAssert x != nil
 x.deallocShared()
 
 x = cast[ptr int](alloc0(125 shl 23))
diff --git a/tests/system/talloc2.nim b/tests/system/talloc2.nim
index c8cab78a1..9d1687f34 100644
--- a/tests/system/talloc2.nim
+++ b/tests/system/talloc2.nim
@@ -1,3 +1,12 @@
+discard """
+disabled: "windows"
+disabled: "openbsd"
+joinable: false
+disabled: 32bit
+"""
+# no point to test this on system with smaller address space
+# was: appveyor is "out of memory"
+
 const
   nmax = 2*1024*1024*1024
 
diff --git a/tests/system/tatomics1.nim b/tests/system/tatomics1.nim
new file mode 100644
index 000000000..217fd07fa
--- /dev/null
+++ b/tests/system/tatomics1.nim
@@ -0,0 +1,9 @@
+discard """
+  targets: "c cpp js"
+"""
+
+var x = 10
+atomicInc(x)
+doAssert x == 11
+atomicDec(x)
+doAssert x == 10
diff --git a/tests/system/tcomparisons.nim b/tests/system/tcomparisons.nim
new file mode 100644
index 000000000..a661b14a1
--- /dev/null
+++ b/tests/system/tcomparisons.nim
@@ -0,0 +1,51 @@
+discard """
+  targets: "c cpp js"
+"""
+
+template main =
+  block: # proc equality
+    var prc: proc(): int {.closure.}
+    prc = nil
+    doAssert prc == nil
+    doAssert prc.isNil
+    prc = proc(): int =
+      result = 123
+    doAssert prc != nil
+    doAssert not prc.isNil
+    doAssert prc == prc
+    let prc2 = prc
+    doAssert prc == prc2
+    doAssert prc2 != nil
+    doAssert not prc2.isNil
+    doAssert not prc.isNil
+    prc = proc(): int =
+      result = 456
+    doAssert prc != nil
+    doAssert not prc.isNil
+    doAssert prc != prc2
+  block: # iterator equality
+    when nimvm: discard # vm does not support closure iterators
+    else:
+      when not defined(js): # js also does not support closure iterators
+        var iter: iterator(): int {.closure.}
+        iter = nil
+        doAssert iter == nil
+        doAssert iter.isNil
+        iter = iterator(): int =
+          yield 123
+        doAssert iter != nil
+        doAssert not iter.isNil
+        doAssert iter == iter
+        let iter2 = iter
+        doAssert iter == iter2
+        doAssert iter2 != nil
+        doAssert not iter2.isNil
+        doAssert not iter.isNil
+        iter = iterator(): int =
+          yield 456
+        doAssert iter != nil
+        doAssert not iter.isNil
+        doAssert iter != iter2
+
+static: main()
+main()
diff --git a/tests/concat/tconcat.nim b/tests/system/tconcat.nim
index fdce3ea00..fdce3ea00 100644
--- a/tests/concat/tconcat.nim
+++ b/tests/system/tconcat.nim
diff --git a/tests/system/tdeepcopy.nim b/tests/system/tdeepcopy.nim
index f7a6e87fa..92ba48115 100644
--- a/tests/system/tdeepcopy.nim
+++ b/tests/system/tdeepcopy.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc --deepcopy:on"
   output: "ok"
 """
 
@@ -65,7 +66,7 @@ proc main() =
     for val in table.values():
       if myObj2.isNil:
         myObj2 = val
-    assert(myObj == myObj2) # passes
+    doAssert(myObj == myObj2) # passes
 
     var tableCopy: ListTableRef[int, SomeObj]
     deepCopy(tableCopy, table)
@@ -80,7 +81,7 @@ proc main() =
     #echo cast[int](myObjCopy)
     #echo cast[int](myObjCopy2)
 
-    assert(myObjCopy == myObjCopy2) # fails
+    doAssert(myObjCopy == myObjCopy2) # fails
 
 
 type
@@ -88,7 +89,7 @@ type
     counter, max: int
     data: array[0..99, (pointer, pointer)]
 
-assert(sizeof(PtrTable) == 2*sizeof(int)+sizeof(pointer)*2*100)
+doAssert(sizeof(PtrTable) == 2*sizeof(int)+sizeof(pointer)*2*100)
 
 main()
 echo "ok"
diff --git a/tests/system/tdollars.nim b/tests/system/tdollars.nim
new file mode 100644
index 000000000..eabee81b3
--- /dev/null
+++ b/tests/system/tdollars.nim
@@ -0,0 +1,199 @@
+discard """
+  matrix: "--mm:refc; --mm:orc; --backend:cpp; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on"
+"""
+
+#[
+if https://github.com/nim-lang/Nim/pull/14043 is merged (or at least its
+tests/system/tostring.nim diff subset), merge
+tests/system/tostring.nim into this file, named after dollars.nim
+
+The goal is to increase test coverage across backends while minimizing test code
+duplication (which always results in weaker test coverage in practice).
+]#
+
+import std/unittest
+import std/private/jsutils
+template test[T](a: T, expected: string) =
+  check $a == expected
+  var b = a
+  check $b == expected
+  static:
+    doAssert $a == expected
+
+template testType(T: typedesc) =
+  when T is bool:
+    test true, "true"
+    test false, "false"
+  elif T is char:
+    test char, "\0"
+    test char.high, static($T.high)
+  else:
+    test T.default, "0"
+    test 1.T, "1"
+    test T.low, static($T.low)
+    test T.high, static($T.high)
+
+block: # `$`(SomeInteger)
+  # direct tests
+  check $0'u8 == "0"
+  check $255'u8 == "255"
+  check $(-127'i8) == "-127"
+
+  # known limitation: Error: number out of range: '128'i8',
+  # see https://github.com/timotheecour/Nim/issues/125
+  # check $(-128'i8) == "-128"
+
+  check $int8.low == "-128"
+  check $int8(-128) == "-128"
+  check $cast[int8](-128) == "-128"
+
+  var a = 12345'u16
+  check $a == "12345"
+  check $12345678'u64 == "12345678"
+  check $12345678'i64 == "12345678"
+  check $(-12345678'i64) == "-12345678"
+
+  # systematic tests
+  testType uint8
+  testType uint16
+  testType uint32
+  testType uint
+
+  testType int8
+  testType int16
+  testType int32
+
+  testType int
+  testType bool
+
+  whenJsNoBigInt64: discard
+  do:
+    testType uint64
+    testType int64
+    testType BiggestInt
+
+block: # #14350, #16674, #16686 for JS
+  var cstr: cstring
+  doAssert cstr == cstring(nil)
+  doAssert cstr == nil
+  doAssert cstr.isNil
+  doAssert cstr != cstring("")
+  doAssert cstr.len == 0
+
+  when defined(js):
+    cstr.add(cstring("abc"))
+    doAssert cstr == cstring("abc")
+
+    var nil1, nil2: cstring = nil
+
+    nil1.add(nil2)
+    doAssert nil1 == cstring(nil)
+    doAssert nil2 == cstring(nil)
+
+    nil1.add(cstring(""))
+    doAssert nil1 == cstring("")
+    doAssert nil2 == cstring(nil)
+
+    nil1.add(nil2)
+    doAssert nil1 == cstring("")
+    doAssert nil2 == cstring(nil)
+
+    nil2.add(nil1)
+    doAssert nil1 == cstring("")
+    doAssert nil2 == cstring("")
+
+block:
+  when defined(js): # bug #18591
+    let a1 = -1'i8
+    let a2 = uint8(a1)
+    # if `uint8(a1)` changes meaning to `cast[uint8](a1)` in future, update this test;
+    # until then, this is the correct semantics.
+    let a3 = $a2
+    doAssert a2 == 255'u8
+    doAssert a3 == "255"
+    proc intToStr(a: uint8): cstring {.importjs: "(# + \"\")".}
+    doAssert $intToStr(a2) == "255"
+  else:
+    block:
+      let x = -1'i8
+      let y = uint32(x)
+      doAssert $y == "4294967295"
+    block:
+      let x = -1'i16
+      let y = uint32(x)
+      doAssert $y == "4294967295"
+    block:
+      let x = -1'i32
+      let y = uint32(x)
+      doAssert $y == "4294967295"
+    block:
+      proc foo1(arg: int): string =
+        let x = uint32(arg)
+        $x
+      doAssert $foo1(-1) == "4294967295"
+
+  block:
+    let x = 4294967295'u32
+    doAssert $x == "4294967295"
+  block:
+    doAssert $(4294967295'u32) == "4294967295"
+
+proc main()=
+  block:
+    let a = -0.0
+    doAssert $a == "-0.0"
+    doAssert $(-0.0) == "-0.0"
+
+  block:
+    let a = 0.0
+    doAssert $a == "0.0"
+    doAssert $(0.0) == "0.0"
+
+  block:
+    let b = -0
+    doAssert $b == "0"
+    doAssert $(-0) == "0"
+
+  block:
+    let b = 0
+    doAssert $b == "0"
+    doAssert $(0) == "0"
+
+  doAssert $uint32.high == "4294967295"
+
+  block: # addInt
+    var res = newStringOfCap(24)
+    template test2(a, b) =
+      res.setLen(0)
+      res.addInt a
+      doAssert res == b
+
+    for i in 0 .. 9:
+      res.addInt int64(i)
+    doAssert res == "0123456789"
+
+    res.setLen(0)
+    for i in -9 .. 0:
+      res.addInt int64(i)
+    doAssert res == "-9-8-7-6-5-4-3-2-10"
+
+    whenJsNoBigInt64: discard
+    do:
+      test2 high(int64), "9223372036854775807"
+      test2 low(int64), "-9223372036854775808"
+    test2 high(int32), "2147483647"
+    test2 low(int32), "-2147483648"
+    test2 high(int16), "32767"
+    test2 low(int16), "-32768"
+    test2 high(int8), "127"
+    test2 low(int8), "-128"
+
+  block:
+    const
+      a: array[3, char] = ['N', 'i', 'm']
+      aStr = $(a)
+
+    doAssert aStr == """['N', 'i', 'm']"""
+
+static: main()
+main()
diff --git a/tests/system/techo_unicode.nim b/tests/system/techo_unicode.nim
new file mode 100644
index 000000000..cb7c49c68
--- /dev/null
+++ b/tests/system/techo_unicode.nim
@@ -0,0 +1,41 @@
+discard """
+  output: '''ÄhmÖÜ
+abasdfdsmÄhmaИ
+Иnastystring
+A你好
+ИnastystringA你好
+ÖÜhmabasdfdsmÄhmaИOK'''
+  disabled: "posix"
+  joinable: "false"
+"""
+
+import winlean
+
+echo "ÄhmÖÜ"
+echo "abasdfdsmÄhmaИ"
+echo "Иnastystring"
+echo "A你好"
+
+write stdout, "Иnastystring"
+writeLine stdout, "A你好"
+stdout.flushFile()
+
+let handle = getOsFileHandle(stdout)
+var a = "ÖÜhmabasdfdsmÄhmaИ"
+var ac = 0'i32
+discard writeFile(handle, addr a[0], int32(len(a)), addr ac, nil)
+stdout.flushFile()
+
+import os
+
+let str = "some nulls: \0\0\0 (three of them)"
+
+let fpath = getTempDir() / "file_with_nulls.bin"
+
+writeFile(fpath, str)
+
+doAssert(getFileSize(fpath) == 31)
+doAssert(readFile(fpath) == str)
+removeFile(fpath)
+
+echo "OK"
diff --git a/tests/system/temptyecho.nim b/tests/system/temptyecho.nim
new file mode 100644
index 000000000..a3c407897
--- /dev/null
+++ b/tests/system/temptyecho.nim
@@ -0,0 +1,6 @@
+discard """
+output: "\n"
+"""
+
+echo()
+
diff --git a/tests/system/tensuremove.nim b/tests/system/tensuremove.nim
new file mode 100644
index 000000000..668d5aed1
--- /dev/null
+++ b/tests/system/tensuremove.nim
@@ -0,0 +1,131 @@
+discard """
+  targets: "c js"
+  matrix: "--cursorinference:on; --cursorinference:off"
+"""
+
+block:
+  type
+    X = object
+      s: string
+
+  proc `=copy`(x: var X, y: X) =
+    x.s = "copied " & y.s
+
+  proc `=sink`(x: var X, y: X) =
+    `=destroy`(x)
+    wasMoved(x)
+    x.s = "moved " & y.s
+
+  proc consume(x: sink X) =
+    discard x.s
+
+  proc main =
+    let m = "abcdefg"
+    var x = X(s: ensureMove m)
+    consume(ensureMove x)
+
+  static: main()
+  main()
+
+block:
+  type
+    String = object
+      id: string
+
+  proc hello =
+    var s = String(id: "1")
+    var m = ensureMove s
+    doAssert m.id == "1"
+
+  hello()
+
+block:
+  type
+    String = object
+      id: string
+
+  proc hello =
+    var n = "1"
+    var s = String(id: ensureMove n)
+    var m = ensureMove s
+    doAssert m.id == "1"
+
+  hello()
+
+block:
+  type
+    String = object
+      id: string
+
+  proc hello =
+    var n = "1"
+    var s = [ensureMove n]
+    var m = ensureMove s
+    doAssert m[0] == "1"
+
+  hello()
+
+block:
+  type
+    String = object
+      id: string
+
+  proc hello =
+    var n = "1"
+    var s = @[ensureMove n]
+    var m = ensureMove s
+    doAssert m[0] == "1"
+
+  hello()
+
+block:
+  type
+    String = object
+      id: string
+
+  proc hello =
+    var s = String(id: "1")
+    var m = ensureMove s.id
+    doAssert m == "1"
+
+  hello()
+
+block:
+  proc foo =
+    var x = 1
+    let y = ensureMove x # move
+    when not defined(js):
+      doAssert (y, x) == (1, 0) # (1, 0)
+  foo()
+
+block:
+  proc foo =
+    var x = 1
+    let y = ensureMove x # move
+    doAssert y == 1
+  foo()
+
+block:
+  proc foo =
+    var x = @[1, 2, 3]
+    let y = ensureMove x[0] # move
+    doAssert y == 1
+    when not defined(js):
+      doAssert x == @[0, 2, 3]
+  foo()
+
+block:
+  proc foo =
+    var x = [1, 2, 3]
+    let y = ensureMove x[0] # move
+    doAssert y == 1
+    when not defined(js):
+      doAssert x == @[0, 2, 3]
+  foo()
+
+block:
+  proc foo =
+    var x = @["1", "2", "3"]
+    let y = ensureMove x[0] # move
+    doAssert y == "1"
+  foo()
diff --git a/tests/system/tensuremove1.nim b/tests/system/tensuremove1.nim
new file mode 100644
index 000000000..b7e19c4fb
--- /dev/null
+++ b/tests/system/tensuremove1.nim
@@ -0,0 +1,16 @@
+discard """
+  errormsg: "cannot move 's', which introduces an implicit copy"
+  matrix: "--cursorinference:on; --cursorinference:off"
+"""
+
+type
+  String = object
+    id: string
+
+proc hello =
+  var s = String(id: "1")
+  var m = ensureMove s
+  discard m
+  discard s
+
+hello()
\ No newline at end of file
diff --git a/tests/system/tensuremove2.nim b/tests/system/tensuremove2.nim
new file mode 100644
index 000000000..39bbeb22e
--- /dev/null
+++ b/tests/system/tensuremove2.nim
@@ -0,0 +1,15 @@
+discard """
+  errormsg: "Nested expressions cannot be moved: 'if true: s else: String()'"
+"""
+
+type
+  String = object
+    id: string
+
+proc hello =
+  var s = String(id: "1")
+  var m = ensureMove(if true: s else: String())
+  discard m
+  discard s
+
+hello()
\ No newline at end of file
diff --git a/tests/system/tensuremove3.nim b/tests/system/tensuremove3.nim
new file mode 100644
index 000000000..eca032673
--- /dev/null
+++ b/tests/system/tensuremove3.nim
@@ -0,0 +1,28 @@
+discard """
+  errormsg: "cannot move 'x', passing 'x' to a sink parameter introduces an implicit copy"
+  matrix: "--cursorinference:on; --cursorinference:off"
+"""
+
+block:
+  type
+    X = object
+      s: string
+
+  proc `=copy`(x: var X, y: X) =
+    x.s = "copied " & y.s
+
+  proc `=sink`(x: var X, y: X) =
+    `=destroy`(x)
+    wasMoved(x)
+    x.s = "moved " & y.s
+
+  proc consume(x: sink X) =
+    discard x.s
+
+  proc main =
+    var s = "abcdefg"
+    var x = X(s: ensureMove s)
+    consume(ensureMove x)
+    discard x
+
+  main()
\ No newline at end of file
diff --git a/tests/system/tenum_array_repr.nim b/tests/system/tenum_array_repr.nim
index 3634692e3..39b1a5f9a 100644
--- a/tests/system/tenum_array_repr.nim
+++ b/tests/system/tenum_array_repr.nim
@@ -2,10 +2,8 @@ discard """
   output: '''
 1
 [a, b]
-
 2
 [c, d]
-
 4
 [e, f]'''
 """
diff --git a/tests/system/tfielditerator.nim b/tests/system/tfielditerator.nim
new file mode 100644
index 000000000..7e063c6cf
--- /dev/null
+++ b/tests/system/tfielditerator.nim
@@ -0,0 +1,126 @@
+discard """
+  output: '''
+a char: true
+a char: false
+an int: 5
+an int: 6
+a string: abc
+false
+true
+true
+false
+true
+a: a
+b: b
+x: 5
+y: 6
+z: abc
+a char: true
+a char: false
+an int: 5
+an int: 6
+a string: abc
+a string: I'm root!
+CMP false
+CMP true
+CMP true
+CMP false
+CMP true
+CMP true
+a: a
+b: b
+x: 5
+y: 6
+z: abc
+thaRootMan: I'm root!
+myDisc: enC
+c: Z
+enC
+Z
+'''
+"""
+
+block titerator1:
+  type
+    TMyTuple = tuple[a, b: char, x, y: int, z: string]
+
+  proc p(x: char) = echo "a char: ", x <= 'a'
+  proc p(x: int) = echo "an int: ", x
+  proc p(x: string) = echo "a string: ", x
+
+  var x: TMyTuple = ('a', 'b', 5, 6, "abc")
+  var y: TMyTuple = ('A', 'b', 5, 9, "abc")
+
+  for f in fields(x):
+    p f
+
+  for a, b in fields(x, y):
+    echo a == b
+
+  for key, val in fieldPairs(x):
+    echo key, ": ", val
+
+  doAssert x != y
+  doAssert x == x
+  doAssert(not (x < x))
+  doAssert x <= x
+  doAssert y < x
+  doAssert y <= x
+
+
+block titerator2:
+  type
+    SomeRootObj = object of RootObj
+      thaRootMan: string
+    TMyObj = object of SomeRootObj
+      a, b: char
+      x, y: int
+      z: string
+
+    TEnum = enum enA, enB, enC
+    TMyCaseObj = object
+      case myDisc: TEnum
+      of enA: a: int
+      of enB: b: string
+      of enC: c: char
+
+  proc p(x: char) = echo "a char: ", x <= 'a'
+  proc p(x: int) = echo "an int: ", x
+  proc p(x: string) = echo "a string: ", x
+
+  proc myobj(a, b: char, x, y: int, z: string): TMyObj =
+    result.a = a; result.b = b; result.x = x; result.y = y; result.z = z
+    result.thaRootMan = "I'm root!"
+
+  var x = myobj('a', 'b', 5, 6, "abc")
+  var y = myobj('A', 'b', 5, 9, "abc")
+
+  for f in fields(x):
+    p f
+
+  for a, b in fields(x, y):
+    echo "CMP ", a == b
+
+  for key, val in fieldPairs(x):
+    echo key, ": ", val
+
+  var co = TMyCaseObj(myDisc: enC, c: 'Z')
+  for key, val in fieldPairs(co):
+    echo key, ": ", val
+
+  for val in fields(co):
+    echo val
+
+block:
+  type
+    Enum = enum A, B
+    Object = object
+      case a: Enum
+      of A:
+        integer: int
+      of B:
+        time: string
+
+  let x = A
+  let s = Object(a: x)
+  doAssert s.integer == 0
diff --git a/tests/system/tfields.nim b/tests/system/tfields.nim
new file mode 100644
index 000000000..0bf3a4e1a
--- /dev/null
+++ b/tests/system/tfields.nim
@@ -0,0 +1,108 @@
+discard """
+  output: '''
+n
+n
+(one: 1, two: 2, three: 3)
+1
+2
+3
+(one: 4, two: 5, three: 6)
+4
+(one: 7, two: 8, three: 9)
+7
+8
+9
+(foo: 38, other: "string here")
+43
+100
+90
+'''
+"""
+
+
+block tindex:
+  type
+    TMyTuple = tuple[a, b: int]
+
+  proc indexOf(t: typedesc, name: string): int =
+    ## takes a tuple and looks for the field by name.
+    ## returs index of that field.
+    var
+      d: t
+      i = 0
+    for n, x in fieldPairs(d):
+      if n == name: return i
+      i.inc
+    raise newException(ValueError, "No field " & name & " in type " &
+      astToStr(t))
+
+  doAssert TMyTuple.indexOf("b") == 1
+
+
+
+block ttemplate:
+  # bug #1902
+  # This works.
+  for name, value in (n: "v").fieldPairs:
+    echo name
+
+  template wrapper(): void =
+    for name, value in (n: "v").fieldPairs:
+      echo name
+  wrapper()
+
+
+
+block tbreak:
+  # bug #2134
+  type
+    TestType = object
+      one: int
+      two: int
+      three: int
+
+  var
+    ab = TestType(one:1, two:2, three:3)
+    ac = TestType(one:4, two:5, three:6)
+    ad = TestType(one:7, two:8, three:9)
+    tstSeq = [ab, ac, ad]
+
+  for tstElement in mitems(tstSeq):
+    echo tstElement
+    for tstField in fields(tstElement):
+      #for tstField in [1,2,4,6]:
+      echo tstField
+      if tstField == 4:
+        break
+
+
+
+block timplicit_with_partial:
+  type
+    Base = ref object of RootObj
+    Foo {.partial.} = ref object of Base
+
+  proc my(f: Foo) =
+    #var f.next = f
+    let f.foo = 38
+    let f.other = "string here"
+    echo f[]
+    echo f.foo + 5
+
+  var g: Foo
+  new(g)
+  my(g)
+
+  type
+    FooTask {.partial.} = ref object of RootObj
+
+  proc foo(t: FooTask) {.liftLocals: t.} =
+    var x = 90
+    if true:
+      var x = 10
+      while x < 100:
+        inc x
+      echo x
+    echo x
+
+  foo(FooTask())
\ No newline at end of file
diff --git a/tests/system/tgcnone.nim b/tests/system/tgcnone.nim
new file mode 100644
index 000000000..1ccb9e29c
--- /dev/null
+++ b/tests/system/tgcnone.nim
@@ -0,0 +1,7 @@
+discard """
+  matrix: "--mm:none -d:useMalloc"
+"""
+# bug #15617
+# bug #22262
+let x = 4
+doAssert x == 4
diff --git a/tests/system/tgcregions.nim b/tests/system/tgcregions.nim
new file mode 100644
index 000000000..e14865be3
--- /dev/null
+++ b/tests/system/tgcregions.nim
@@ -0,0 +1,6 @@
+discard """
+cmd: "nim c --gc:regions $file"
+"""
+
+# issue #12597
+# it just tests that --gc:regions compiles. Nothing else.   :'(
diff --git a/tests/system/tgogc.nim b/tests/system/tgogc.nim
new file mode 100644
index 000000000..fd45bb120
--- /dev/null
+++ b/tests/system/tgogc.nim
@@ -0,0 +1,7 @@
+discard """
+  disabled: "windows"
+  cmd: "nim c --gc:go $file"
+  action: "compile"
+"""
+# bug #11447
+echo "Go GC test"
diff --git a/tests/system/timmutableinc.nim b/tests/system/timmutableinc.nim
new file mode 100644
index 000000000..e857800b3
--- /dev/null
+++ b/tests/system/timmutableinc.nim
@@ -0,0 +1,8 @@
+discard """
+  errormsg: "type mismatch: got <int>"
+  file: "timmutableinc.nim"
+  line: 8
+"""
+var x = 0
+
+inc(x+1)
diff --git a/tests/system/tinvalidnot.nim b/tests/system/tinvalidnot.nim
new file mode 100644
index 000000000..df0291a8a
--- /dev/null
+++ b/tests/system/tinvalidnot.nim
@@ -0,0 +1,19 @@
+discard """
+  errormsg: "type mismatch"
+  file: "tinvalidnot.nim"
+  line: 14
+"""
+# BUG: following compiles, but should not:
+
+proc nodeOfDegree(x: int): bool =
+  result = false
+
+proc main =
+  for j in 0..2:
+    for i in 0..10:
+      if not nodeOfDegree(1) >= 0: #ERROR_MSG type mismatch
+        echo "Yes"
+      else:
+        echo "No"
+
+main()
diff --git a/tests/system/tio.nim b/tests/system/tio.nim
index 3d4df806b..52a21837a 100644
--- a/tests/system/tio.nim
+++ b/tests/system/tio.nim
@@ -1,11 +1,19 @@
+discard """
+outputsub: ""
+disabled: true
+"""
+
 import
-  unittest, osproc, streams, os, strformat
+  unittest, osproc, streams, os, strformat, strutils
 const STRING_DATA = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."
+
 const TEST_FILE = "tests/testdata/string.txt"
 
 proc echoLoop(str: string): string =
   result = ""
-  var process = startProcess(findExe("tests/system/helpers/readall_echo"))
+  let exe = findExe("tests/system/helpers/readall_echo")
+  echo "exe: ", exe
+  var process = startProcess(exe)
   var input = process.inputStream
   input.write(str)
   input.close()
@@ -14,15 +22,14 @@ proc echoLoop(str: string): string =
   while not output.atEnd:
     result.add(output.readLine)
 
-suite "io":
-  suite "readAll":
-    test "stdin":
+block: # io
+  block: # readAll
+    block: # stdin
       check:
         echoLoop(STRING_DATA) == STRING_DATA
-        echoLoop(STRING_DATA[0..3999]) == STRING_DATA[0..3999]
-    test "file":
+    block: # file
       check:
-        readFile(TEST_FILE) == STRING_DATA
+        readFile(TEST_FILE).strip == STRING_DATA
 
 
 proc verifyFileSize(sz: int64) =
@@ -36,7 +43,7 @@ proc verifyFileSize(sz: int64) =
     discard execProcess(&"dd if=/dev/zero of={fn} bs=1000000 count={size_in_mb}")
 
   doAssert os.getFileSize(fn) == sz # Verify OS filesize by string
-  
+
   var f = open(fn)
   doAssert f.getFileSize() == sz # Verify file handle filesize
   f.close()
diff --git a/tests/system/tlocals.nim b/tests/system/tlocals.nim
new file mode 100644
index 000000000..e59976102
--- /dev/null
+++ b/tests/system/tlocals.nim
@@ -0,0 +1,76 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  output: '''(x: "string here", a: 1)
+b is 5
+x is 12'''
+"""
+
+proc simple[T](a: T) =
+  var
+    x = "string here"
+  echo locals()
+
+simple(1)
+
+type Foo2[T]=object
+  a2: T
+
+proc numFields*(T: typedesc[tuple|object]): int=
+  var t:T
+  for _ in t.fields: inc result
+
+proc test(baz: int, qux: var int): int =
+  var foo: Foo2[int]
+  let bar = "abc"
+  let c1 = locals()
+  doAssert numFields(c1.foo.type) == 1
+  doAssert c1.bar == "abc"
+  doAssert c1.baz == 123
+  doAssert c1.result == 0
+  doAssert c1.qux == 456
+
+var x1 = 456
+discard test(123, x1)
+
+# bug #11958
+proc foo() =
+  var a = 5
+  proc bar() {.nimcall.} =
+    var b = 5
+    for k, v in fieldpairs(locals()):
+      echo k, " is ", v
+
+  bar()
+foo()
+
+
+proc foo2() =
+  var a = 5
+  proc bar2() {.nimcall.} =
+    for k, v in fieldpairs(locals()):
+      echo k, " is ", v
+
+  bar2()
+foo2()
+
+
+proc foo3[T](y: T) =
+  var a = 5
+  proc bar2[T](x: T) {.nimcall.} =
+    for k, v in fieldpairs(locals()):
+      echo k, " is ", v
+
+  bar2(y)
+
+foo3(12)
+
+block: # bug #12682
+  template foo(): untyped =
+    var c1 = locals()
+    1
+
+  proc testAll()=
+    doAssert foo() == 1
+    let c2=locals()
+
+  testAll()
diff --git a/tests/system/tlowhigh.nim b/tests/system/tlowhigh.nim
new file mode 100644
index 000000000..6ae871255
--- /dev/null
+++ b/tests/system/tlowhigh.nim
@@ -0,0 +1,32 @@
+discard """
+    action: run
+    output: '''
+18446744073709551615
+9223372036854775807
+4294967295
+0
+0
+'''
+"""
+
+var x: range[-1'f32..1'f32]
+doAssert x.low == -1'f32
+doAssert x.high == 1'f32
+doAssert x.type.low == -1'f32
+doAssert x.type.high == 1'f32
+var y: range[-1'f64..1'f64]
+doAssert y.low == -1'f64
+doAssert y.high == 1'f64
+doAssert y.type.low == -1'f64
+doAssert y.type.high == 1'f64
+
+# bug #11972
+var num: uint8
+doAssert num.high.float == 255.0
+
+echo high(uint64)
+echo high(int64)
+echo high(uint32)
+
+echo low(uint64)
+echo low(uint32)
diff --git a/tests/system/tmagics.nim b/tests/system/tmagics.nim
new file mode 100644
index 000000000..6088c9600
--- /dev/null
+++ b/tests/system/tmagics.nim
@@ -0,0 +1,62 @@
+discard """
+  matrix: "--mm:refc"
+  output: '''
+true
+true
+false
+true
+true
+false
+true
+'''
+joinable: false
+"""
+
+block tlowhigh:
+  type myEnum = enum e1, e2, e3, e4, e5
+  var a: array[myEnum, int]
+
+  for i in low(a) .. high(a):
+    a[i] = 0
+
+  proc sum(a: openArray[int]): int =
+    result = 0
+    for i in low(a)..high(a):
+      inc(result, a[i])
+
+  doAssert sum([1, 2, 3, 4]) == 10
+
+
+block t8693:
+  type Foo = int | float
+
+  proc bar(t1, t2: typedesc): bool =
+    echo (t1 is t2)
+    (t2 is t1)
+
+  proc bar[T](x: T, t2: typedesc): bool =
+    echo (T is t2)
+    (t2 is T)
+
+  doAssert bar(int, Foo) == false
+  doAssert bar(4, Foo) == false
+  doAssert bar(any, int)
+  doAssert bar(int, any) == false
+  doAssert bar(Foo, Foo)
+  doAssert bar(any, Foo)
+  doAssert bar(Foo, any) == false
+
+block t9442:
+  var v1: ref char
+  var v2: string
+  var v3: seq[char]
+  GC_ref(v1)
+  GC_unref(v1)
+  GC_ref(v2)
+  GC_unref(v2)
+  GC_ref(v3)
+  GC_unref(v3)
+
+block: # bug #12229
+  proc foo(T: typedesc) = discard
+  foo(ref)
diff --git a/tests/system/tmemory.nim b/tests/system/tmemory.nim
new file mode 100644
index 000000000..553037011
--- /dev/null
+++ b/tests/system/tmemory.nim
@@ -0,0 +1,16 @@
+import std/assertions
+
+block: # cmpMem
+  type
+    SomeHash = array[15, byte]
+
+  var
+    a: SomeHash
+    b: SomeHash
+
+  a[^1] = byte(1)
+  let c = a
+
+  doAssert cmpMem(a.addr, b.addr, sizeof(SomeHash)) > 0
+  doAssert cmpMem(b.addr, a.addr, sizeof(SomeHash)) < 0
+  doAssert cmpMem(a.addr, c.addr, sizeof(SomeHash)) == 0
diff --git a/tests/misc/tnew.nim b/tests/system/tnew.nim
index 89f34a621..c28c1187f 100644
--- a/tests/misc/tnew.nim
+++ b/tests/system/tnew.nim
@@ -1,3 +1,12 @@
+discard """
+matrix: "--mm:refc; --mm:orc"
+outputsub: '''
+Simple tree node allocation worked!
+Simple cycle allocation worked!
+'''
+joinable: false
+"""
+
 # Test the implementation of the new operator
 # and the code generation for gc walkers
 # (and the garbage collector):
@@ -16,7 +25,10 @@ proc finalizer(n: PNode) =
   write(stdout, " is now freed\n")
 
 proc newNode(data: int, le, ri: PNode): PNode =
-  new(result, finalizer)
+  when defined(gcDestructors): # using finalizer breaks the test for orc
+    new(result)
+  else:
+    new(result, finalizer)
   result.le = le
   result.ri = ri
   result.data = data
diff --git a/tests/misc/tnewderef.nim b/tests/system/tnewderef.nim
index 3394dbddf..3394dbddf 100644
--- a/tests/misc/tnewderef.nim
+++ b/tests/system/tnewderef.nim
diff --git a/tests/system/tnewstring_uninitialized.nim b/tests/system/tnewstring_uninitialized.nim
new file mode 100644
index 000000000..9bc0e1622
--- /dev/null
+++ b/tests/system/tnewstring_uninitialized.nim
@@ -0,0 +1,11 @@
+discard """
+  matrix: "--mm:refc;"
+"""
+
+# bug #22555
+var x = newStringUninit(10)
+doAssert x.len == 10
+for i in 0..<x.len:
+  x[i] = chr(ord('a') + i)
+
+doAssert x == "abcdefghij"
diff --git a/tests/system/tnilconcats.nim b/tests/system/tnilconcats.nim
index ce059b7b0..69fc3913c 100644
--- a/tests/system/tnilconcats.nim
+++ b/tests/system/tnilconcats.nim
@@ -1,5 +1,5 @@
 discard """
-  output: '''@[nil, nil, nil, nil, nil, nil, nil, "meh"]'''
+  output: '''@["", "", "", "", "", "", "", "meh"]'''
   exitcode: "0"
 """
 
@@ -23,3 +23,11 @@ when true:
   doAssert s == "fooabc"
 
   echo x
+
+  # casting an empty string as sequence with shallow() should not segfault
+  var s2: string
+  when defined(gcRefc):
+    shallow(s2)
+  s2 &= "foo"
+  doAssert s2 == "foo"
+
diff --git a/tests/system/tnim_stacktrace_override.nim b/tests/system/tnim_stacktrace_override.nim
new file mode 100644
index 000000000..c75da9d9b
--- /dev/null
+++ b/tests/system/tnim_stacktrace_override.nim
@@ -0,0 +1,18 @@
+discard """
+  cmd: "nim c -d:nimStacktraceOverride $file"
+  output: '''begin
+Traceback (most recent call last, using override)
+Error: unhandled exception: stack trace produced [ValueError]
+'''
+  exitcode: 1
+"""
+
+import asyncfutures
+
+proc main =
+  echo "begin"
+  if true:
+    raise newException(ValueError, "stack trace produced")
+  echo "unreachable"
+
+main()
diff --git a/tests/system/toString.nim b/tests/system/toString.nim
deleted file mode 100644
index ea10f998c..000000000
--- a/tests/system/toString.nim
+++ /dev/null
@@ -1,110 +0,0 @@
-discard """
-  output:""
-"""
-
-doAssert "@[23, 45]" == $(@[23, 45])
-doAssert "[32, 45]" == $([32, 45])
-doAssert """@["", "foo", "bar"]""" == $(@["", "foo", "bar"])
-doAssert """["", "foo", "bar"]""" ==  $(["", "foo", "bar"])
-doAssert """["", "foo", "bar"]""" ==  $(@["", "foo", "bar"].toOpenArray(0, 2))
-
-# bug #2395
-let alphaSet: set[char] = {'a'..'c'}
-doAssert "{'a', 'b', 'c'}" == $alphaSet
-doAssert "2.3242" == $(2.3242)
-doAssert "2.982" == $(2.982)
-doAssert "123912.1" == $(123912.1)
-doAssert "123912.1823" == $(123912.1823)
-doAssert "5.0" == $(5.0)
-doAssert "1e+100" == $(1e100)
-doAssert "inf" == $(1e1000000)
-doAssert "-inf" == $(-1e1000000)
-doAssert "nan" == $(0.0/0.0)
-
-# nil tests
-# maybe a bit inconsistent in types
-var x: seq[string]
-doAssert "nil" == $(x)
-
-var y: string = nil
-doAssert nil == $(y)
-
-type
-  Foo = object
-    a: int
-    b: string
-
-var foo1: Foo
-
-# nil string should be an some point in time equal to the empty string
-doAssert(($foo1)[0..9] == "(a: 0, b: ")
-
-const
-  data = @['a','b', '\0', 'c','d']
-  dataStr = $data
-
-# ensure same result when on VM or when at program execution
-doAssert dataStr == $data
-
-import strutils
-# array test
-
-let arr = ['H','e','l','l','o',' ','W','o','r','l','d','!','\0']
-doAssert $arr == "['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\\x00']"
-doAssert $cstring(unsafeAddr arr) == "Hello World!"
-
-proc takes(c: cstring) =
-  doAssert c == ""
-
-proc testm() =
-  var x: string
-  # nil is mapped to "":
-  takes(x)
-
-testm()
-
-# nil tests
-var xx: seq[string]
-var yy: string
-doAssert xx == @[]
-doAssert yy == ""
-
-proc bar(arg: cstring): void =
-  doAssert arg[0] == '\0'
-
-proc baz(arg: openarray[char]): void =
-  doAssert arg.len == 0
-
-proc stringCompare(): void =
-  var a,b,c,d,e,f,g: string
-  a.add 'a'
-  doAssert a == "a"
-  b.add "bee"
-  doAssert b == "bee"
-  b.add g
-  doAssert b == "bee"
-  c.add 123.456
-  doAssert c == "123.456"
-  d.add 123456
-  doAssert d == "123456"
-
-  doAssert e == ""
-  doAssert "" == e
-  doAssert nil == e
-  doAssert e == nil
-  doAssert f == g
-  doAssert "" == ""
-  doAssert "" == nil
-  doAssert nil == ""
-
-  g.setLen(10)
-  doAssert g == "\0\0\0\0\0\0\0\0\0\0"
-  doAssert "" != "\0\0\0\0\0\0\0\0\0\0"
-
-  var nilstring: string
-  bar(nilstring)
-  baz(nilstring)
-
-stringCompare()
-static:
-  stringCompare()
\ No newline at end of file
diff --git a/tests/system/tostring.nim b/tests/system/tostring.nim
new file mode 100644
index 000000000..bb6e453fb
--- /dev/null
+++ b/tests/system/tostring.nim
@@ -0,0 +1,125 @@
+doAssert "@[23, 45]" == $(@[23, 45])
+doAssert "[32, 45]" == $([32, 45])
+doAssert """@["", "foo", "bar"]""" == $(@["", "foo", "bar"])
+doAssert """["", "foo", "bar"]""" == $(["", "foo", "bar"])
+doAssert """["", "foo", "bar"]""" == $(@["", "foo", "bar"].toOpenArray(0, 2))
+
+# bug #2395
+let alphaSet: set[char] = {'a'..'c'}
+doAssert "{'a', 'b', 'c'}" == $alphaSet
+doAssert "2.3242" == $(2.3242)
+doAssert "2.982" == $(2.982)
+doAssert "123912.1" == $(123912.1)
+doAssert "123912.1823" == $(123912.1823)
+doAssert "5.0" == $(5.0)
+doAssert "1e+100" == $(1e100)
+doAssert "inf" == $(1e1000000)
+doAssert "-inf" == $(-1e1000000)
+doAssert "nan" == $(0.0/0.0)
+
+# nil tests
+# maybe a bit inconsistent in types
+var x: seq[string]
+doAssert "@[]" == $(x)
+
+var y: string
+doAssert "" == $(y)
+
+type
+  Foo = object
+    a: int
+    b: string
+
+var foo1: Foo
+
+# nil string should be an some point in time equal to the empty string
+doAssert(($foo1)[0..9] == "(a: 0, b: ")
+
+const
+  data = @['a','b', '\0', 'c','d']
+  dataStr = $data
+
+# ensure same result when on VM or when at program execution
+doAssert dataStr == $data
+
+import strutils
+# array test
+
+let arr = ['H','e','l','l','o',' ','W','o','r','l','d','!','\0']
+doAssert $arr == "['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\\x00']"
+doAssert $cast[cstring](addr arr) == "Hello World!"
+
+proc takes(c: cstring) =
+  doAssert c == cstring""
+
+proc testm() =
+  var x: string
+  # nil is mapped to "":
+  takes(x)
+
+testm()
+
+# nil tests
+var xx: seq[string]
+var yy: string
+doAssert xx == @[]
+doAssert yy == ""
+
+proc bar(arg: cstring) =
+  doAssert arg[0] == '\0'
+
+proc baz(arg: openArray[char]) =
+  doAssert arg.len == 0
+
+proc stringCompare() =
+  var a,b,c,d,e,f,g: string
+  a.add 'a'
+  doAssert a == "a"
+  b.add "bee"
+  doAssert b == "bee"
+  b.add g
+  doAssert b == "bee"
+  c.addFloat 123.456
+  doAssert c == "123.456"
+  d.addInt 123456
+  doAssert d == "123456"
+
+  doAssert e == ""
+  doAssert "" == e
+  doAssert f == g
+  doAssert "" == ""
+
+  g.setLen(10)
+  doAssert g == "\0\0\0\0\0\0\0\0\0\0"
+  doAssert "" != "\0\0\0\0\0\0\0\0\0\0"
+
+  var nilstring: string
+  #bar(nilstring)
+  baz(nilstring)
+
+stringCompare()
+var nilstring: string
+bar(nilstring)
+
+static:
+  stringCompare()
+
+# issue #8847
+var a2: cstring = "fo\"o2"
+
+block:
+  var s: string
+  s.addQuoted a2
+  doAssert s == "\"fo\\\"o2\""
+
+# issue #16650
+template fn() =
+  doAssert len(cstring"ab\0c") == 5
+  doAssert len(cstring("ab\0c")) == 2
+  when nimvm:
+    discard
+  else:
+    let c = cstring("ab\0c")
+    doAssert len(c) == 2
+fn()
+static: fn()
diff --git a/tests/system/tparams.nim b/tests/system/tparams.nim
index 1358212f2..b20cfce1e 100644
--- a/tests/system/tparams.nim
+++ b/tests/system/tparams.nim
@@ -1,18 +1,22 @@
+discard """
+joinable: false
+"""
+
+# not joinable because it executes itself with parameters
 import os
 import osproc
-import parseopt2
+import parseopt
 import sequtils
 
 let argv = commandLineParams()
 
 if argv == @[]:
   # this won't work with spaces
-  assert execShellCmd(getAppFilename() & " \"foo bar\" --aa:bar=a --a=c:d --ab -c --a[baz]:doo") == 0
+  doAssert execShellCmd(getAppFilename() & " \"foo bar\" --aa:bar=a --a=c:d --ab -c --a[baz]:doo") == 0
 else:
   let f = toSeq(getopt())
-  echo f.repr
-  assert f[0].kind == cmdArgument and f[0].key == "foo bar" and f[0].val == ""
-  assert f[1].kind == cmdLongOption and f[1].key == "aa" and f[1].val == "bar=a"
-  assert f[2].kind == cmdLongOption and f[2].key == "a=c" and f[2].val == "d"
-  assert f[3].kind == cmdLongOption and f[3].key == "ab" and f[3].val == ""
-  assert f[4].kind == cmdShortOption and f[4].key == "c" and f[4].val == ""
+  doAssert f[0].kind == cmdArgument and f[0].key == "foo bar" and f[0].val == ""
+  doAssert f[1].kind == cmdLongOption and f[1].key == "aa" and f[1].val == "bar=a"
+  doAssert f[2].kind == cmdLongOption and f[2].key == "a" and f[2].val == "c:d"
+  doAssert f[3].kind == cmdLongOption and f[3].key == "ab" and f[3].val == ""
+  doAssert f[4].kind == cmdShortOption and f[4].key == "c" and f[4].val == ""
diff --git a/tests/system/trealloc.nim b/tests/system/trealloc.nim
index dc5f712d6..1d3e00aff 100644
--- a/tests/system/trealloc.nim
+++ b/tests/system/trealloc.nim
@@ -1,5 +1,7 @@
 discard """
   output: '''success'''
+  joinable: false
+  disabled: "openbsd"
 """
 
 # bug #4818
@@ -10,12 +12,12 @@ const BUFFER_SIZE = 5000
 var buffer = cast[ptr uint16](alloc(BUFFER_SIZE))
 
 var total_size: int64 = 0
-for i in 0 .. 4000:
-    let size = BUFFER_SIZE * i
-    #echo "requesting ", size
-    total_size += size.int64
-    buffer = cast[ptr uint16](realloc(buffer, size))
-    #echo totalSize, " total: ", getTotalMem(), " occupied: ", getOccupiedMem(), " free: ", getFreeMem()
+for i in 0 .. 1000:
+  let size = BUFFER_SIZE * i
+  #echo "requesting ", size
+  total_size += size.int64
+  buffer = cast[ptr uint16](realloc(buffer, size))
+  #echo totalSize, " total: ", getTotalMem(), " occupied: ", getOccupiedMem(), " free: ", getFreeMem()
 
 dealloc(buffer)
 echo "success"
diff --git a/tests/system/trefs.nim b/tests/system/trefs.nim
new file mode 100644
index 000000000..8c36aec52
--- /dev/null
+++ b/tests/system/trefs.nim
@@ -0,0 +1,15 @@
+discard """
+  targets: "c js"
+"""
+
+# bug #21317
+proc parseHook*(v: var ref int) =
+  var a: ref int
+  new(a)
+  a[] = 123
+  v = a
+
+proc fromJson2*(): ref int =
+  parseHook(result)
+
+doAssert fromJson2()[] == 123
diff --git a/tests/system/tsigexitcode.nim b/tests/system/tsigexitcode.nim
new file mode 100644
index 000000000..249256b40
--- /dev/null
+++ b/tests/system/tsigexitcode.nim
@@ -0,0 +1,23 @@
+discard """
+  joinable: false
+  disabled: windows
+"""
+
+import os, osproc, posix, strutils
+
+proc main() =
+  if paramCount() > 0:
+    let signal = cint parseInt paramStr(1)
+    discard posix.raise(signal)
+  else:
+    # synchronize this list with lib/system/except.nim:registerSignalHandler()
+    let sigs = [SIGINT, SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, SIGPIPE]
+    for s in sigs:
+      let (_, exitCode) = execCmdEx(quoteShellCommand [getAppFilename(), $s])
+      if s == SIGPIPE:
+        # SIGPIPE should be ignored
+        doAssert exitCode == 0, $(exitCode, s)
+      else:
+        doAssert exitCode == 128+s, $(exitCode, s)
+
+main()
diff --git a/tests/system/tslices.nim b/tests/system/tslices.nim
new file mode 100644
index 000000000..d0c68f8cb
--- /dev/null
+++ b/tests/system/tslices.nim
@@ -0,0 +1,65 @@
+discard """
+output: '''
+456456
+456456
+456456
+Zugr5nd
+egerichtetd
+verichtetd
+'''
+"""
+
+# Test the new slices.
+
+var mystr = "Abgrund"
+# mystr[..1] = "Zu" # deprecated
+mystr[0..1] = "Zu"
+
+mystr[4..4] = "5"
+
+type
+  TEnum = enum e1, e2, e3, e4, e5, e6
+
+var myarr: array[TEnum, int] = [1, 2, 3, 4, 5, 6]
+myarr[e1..e3] = myarr[e4..e6]
+# myarr[..e3] = myarr[e4..e6] # deprecated
+myarr[0..e3] = myarr[e4..e6]
+
+for x in items(myarr): stdout.write(x)
+echo()
+
+var myarr2: array[0..5, int] = [1, 2, 3, 4, 5, 6]
+myarr2[0..2] = myarr2[3..5]
+
+for x in items(myarr2): stdout.write(x)
+echo()
+
+
+var myseq = @[1, 2, 3, 4, 5, 6]
+myseq[0..2] = myseq[^3 .. ^1]
+
+for x in items(myseq): stdout.write(x)
+echo()
+
+echo mystr
+
+mystr[4..4] = "u"
+
+# test full replacement
+# mystr[.. ^2] = "egerichtet"  # deprecated
+mystr[0 .. ^2] = "egerichtet"
+
+echo mystr
+
+mystr[0..2] = "ve"
+echo mystr
+
+var s = "abcdef"
+s[1 .. ^2] = "xyz"
+assert s == "axyzf"
+
+# issue mentioned in PR #19219
+type Foo = distinct uint64
+# < here calls `pred` which used to cause a codegen error
+# `pred` compiles because distinct ordinals are considered ordinal
+const slice = 0 ..< 42.Foo
diff --git a/tests/system/tslimsystem.nim b/tests/system/tslimsystem.nim
new file mode 100644
index 000000000..4815306b5
--- /dev/null
+++ b/tests/system/tslimsystem.nim
@@ -0,0 +1,6 @@
+discard """
+  output: "123"
+  matrix: "-d:nimPreviewSlimSystem --mm:refc; -d:nimPreviewSlimSystem --mm:arc"
+"""
+
+echo 123
\ No newline at end of file
diff --git a/tests/system/tstatic.nim b/tests/system/tstatic.nim
new file mode 100644
index 000000000..6e2893e2b
--- /dev/null
+++ b/tests/system/tstatic.nim
@@ -0,0 +1,58 @@
+discard """
+  targets: "c cpp js"
+"""
+
+import std/strutils
+
+# bug #6133
+template main() =
+  block:
+    block:
+      proc foo(q: string, a: int): int =
+        result = q.len
+
+      proc foo(q: static[string]): int =
+        result = foo(q, 5)
+
+      doAssert foo("123") == 3
+
+    block:
+      type E = enum A
+
+      if false:
+        var e = A
+        discard $e
+
+      proc foo(a: string): int =
+        len(a) # 16640
+
+      proc foo(a: static[bool]): int {.used.} =
+        discard
+
+      doAssert foo("") == 0
+
+    block:
+      proc foo(a: string): int =
+        len(a)
+
+      proc foo(a: static[bool]): int {.used.} =
+        discard
+
+      doAssert foo("abc") == 3
+
+    block:
+      proc parseInt(f: static[bool]): int {.used.} = discard
+
+      doAssert "123".parseInt == 123
+  block:
+    type
+      MyType = object
+        field: float32
+      AType[T: static MyType] = distinct range[0f32 .. T.field]
+    var a: AType[MyType(field: 5f32)]
+    proc n(S: static Slice[int]): range[S.a..S.b] = discard
+    assert typeof(n 1..2) is range[1..2]
+
+
+static: main()
+main()
diff --git a/tests/system/tstatic_callable.nim b/tests/system/tstatic_callable.nim
new file mode 100644
index 000000000..92d1fca8d
--- /dev/null
+++ b/tests/system/tstatic_callable.nim
@@ -0,0 +1,12 @@
+# bug #16987
+
+proc getNum(a: int): int = a
+
+# Below calls "doAssert getNum(123) == 123" at compile time.
+static:
+  doAssert getNum(123) == 123
+
+# Below calls evaluate the "getNum(123)" at compile time, but the
+# results of those calls get used at run time.
+doAssert (static getNum(123)) == 123
+doAssert (static(getNum(123))) == 123
diff --git a/tests/system/tstatic_callable_error.nim b/tests/system/tstatic_callable_error.nim
new file mode 100644
index 000000000..c6f1e3d07
--- /dev/null
+++ b/tests/system/tstatic_callable_error.nim
@@ -0,0 +1,14 @@
+# bug #16987
+
+discard """
+errormsg: "cannot evaluate at compile time: inp"
+nimout: '''
+tstatic_callable_error.nim(14, 21) Error: cannot evaluate at compile time: inp'''
+"""
+
+
+# line 10
+proc getNum(a: int): int = a
+
+let inp = 123
+echo (static getNum(inp))
diff --git a/tests/system/tsystem_misc.nim b/tests/system/tsystem_misc.nim
index 6d14aa68f..1debb7c48 100644
--- a/tests/system/tsystem_misc.nim
+++ b/tests/system/tsystem_misc.nim
@@ -15,9 +15,35 @@ discard """
 1
 2
 3
+2
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+2
 '''
 """
 
+
+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)
@@ -33,11 +59,6 @@ doAssert high(float) > low(float)
 doAssert high(float32) > low(float32)
 doAssert high(float64) > low(float64)
 
-# bug #6710
-var s = @[1]
-s.delete(0)
-
-
 proc foo(a: openArray[int]) =
   for x in a: echo x
 
@@ -55,13 +76,13 @@ foo(toOpenArray(seqq, 1, 3))
 # empty openArray issue #7904
 foo(toOpenArray(seqq, 0, -1))
 foo(toOpenArray(seqq, 1, 0))
-doAssertRaises(IndexError):
+doAssertRaises(IndexDefect):
   foo(toOpenArray(seqq, 0, -2))
 
 foo(toOpenArray(arr, 9, 8))
 foo(toOpenArray(arr, 0, -1))
 foo(toOpenArray(arr, 1, 0))
-doAssertRaises(IndexError):
+doAssertRaises(IndexDefect):
   foo(toOpenArray(arr, 10, 8))
 
 # test openArray of openArray
@@ -80,9 +101,127 @@ var arrNeg: array[-3 .. -1, int] = [1, 2, 3]
 foo(toOpenArray(arrNeg, -3, -1))
 foo(toOpenArray(arrNeg, 0, -1))
 foo(toOpenArray(arrNeg, -3, -4))
-doAssertRaises(IndexError):
+doAssertRaises(IndexDefect):
   foo(toOpenArray(arrNeg, -4, -1))
-doAssertRaises(IndexError):
+doAssertRaises(IndexDefect):
   foo(toOpenArray(arrNeg, -1, 0))
-doAssertRaises(IndexError):
+doAssertRaises(IndexDefect):
   foo(toOpenArray(arrNeg, -1, -3))
+doAssertRaises(Exception):
+  raise newException(Exception, "foo")
+
+block:
+  var didThrow = false
+  try:
+    doAssertRaises(IndexDefect): # should fail since it's wrong exception
+      raise newException(FieldDefect, "foo")
+  except AssertionDefect:
+    # ok, throwing was correct behavior
+    didThrow = true
+  doAssert didThrow
+
+type seqqType = ptr UncheckedArray[int]
+let qData = cast[seqqType](addr seqq[0])
+oaFirstElm(toOpenArray(qData, 1, 3))
+
+proc foo(a: openArray[byte]) =
+  for x in a: echo x
+
+let str = "0123456789"
+foo(toOpenArrayByte(str, 0, str.high))
+
+
+template boundedOpenArray[T](x: seq[T], first, last: int): openArray[T] =
+  toOpenarray(x, max(0, first), min(x.high, last))
+
+# bug #9281
+
+proc foo[T](x: openArray[T]) =
+  echo x.len
+
+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 $() == "()"
+
+# this is a call indirection to prevent `toInt` to be resolved at compile time.
+proc testToInt(arg: float64, a: int, b: BiggestInt) =
+  doAssert toInt(arg) == a
+  doAssert toBiggestInt(arg) == b
+
+testToInt(0.45, 0, 0)    # should round towards 0
+testToInt(-0.45, 0, 0)   # should round towards 0
+testToInt(0.5, 1, 1)     # should round away from 0
+testToInt(-0.5, -1, -1)  # should round away from 0
+testToInt(13.37, 13, 13)    # should round towards 0
+testToInt(-13.37, -13, -13) # should round towards 0
+testToInt(7.8, 8, 8)     # should round away from 0
+testToInt(-7.8, -8, -8)  # should round away from 0
+
+# test min/max for correct NaN handling
+
+proc testMinMax(a,b: float32) =
+  doAssert max(float32(a),float32(b)) == 0'f32
+  doAssert min(float32(a),float32(b)) == 0'f32
+  doAssert max(float64(a),float64(b)) == 0'f64
+  doAssert min(float64(a),float64(b)) == 0'f64
+
+testMinMax(0.0, NaN)
+testMinMax(NaN, 0.0)
+
+
+block:
+  type Foo = enum
+    k1, k2
+  var
+    a = {k1}
+    b = {k1,k2}
+  doAssert a < b
+
+
+block: # Ordinal
+  doAssert int is Ordinal
+  doAssert uint is Ordinal
+  doAssert int64 is Ordinal
+  doAssert uint64 is Ordinal
+  doAssert char is Ordinal
+  type Foo = enum k1, k2
+  doAssert Foo is Ordinal
+  doAssert Foo is SomeOrdinal
+  doAssert enum is SomeOrdinal
+
+  # these fail:
+  # doAssert enum is Ordinal # fails
+  # doAssert Ordinal is SomeOrdinal
+  # doAssert SomeOrdinal is Ordinal
+
+block:
+  proc p() = discard
+
+  doAssert not compiles(echo p.rawProc.repr)
+  doAssert not compiles(echo p.rawEnv.repr)
+  doAssert not compiles(echo p.finished)
+
+proc bug23223 = # bug #23223
+  var stuff = "hello"
+  stuff.insert ""
+  doAssert stuff == "hello"
+
+bug23223()
+
+block: # bug #23894
+  let v = high(uint) div 2
+  let s = v + 1 # 9223372036854775808
+  let m = succ v
+  doAssert s == m
diff --git a/tests/system/tuse_version16.nim b/tests/system/tuse_version16.nim
new file mode 100644
index 000000000..802b617ad
--- /dev/null
+++ b/tests/system/tuse_version16.nim
@@ -0,0 +1,49 @@
+discard """
+  matrix: "-d:NimMajor=1 -d:NimMinor=6 -d:NimPatch=100"
+"""
+
+{.warning[UnusedImport]: off.}
+
+import std/[
+  # Core:
+  bitops, typetraits, lenientops, macros, volatile,
+
+  # Algorithms:
+  algorithm, sequtils,
+
+  # Collections:
+  critbits, deques, heapqueue, intsets, lists, options, sets,
+  sharedlist, tables,
+
+  # Strings:
+  editdistance, wordwrap, parseutils, ropes,
+  pegs, strformat, strmisc, strscans, strtabs,
+  strutils, unicode, unidecode,
+
+  # Generic operator system services:
+  os, streams,
+
+  # Math libraries:
+  complex, math, mersenne, random, rationals, stats, sums,
+
+  # Internet protocols:
+  httpcore, mimetypes, uri,
+
+  # Parsers:
+  htmlparser, json, lexbase, parsecfg, parsecsv, parsesql, parsexml,
+
+  # XML processing:
+  xmltree, xmlparser,
+
+  # Generators:
+  htmlgen,
+
+  # Hashing:
+  base64, hashes,
+
+  # Miscellaneous:
+  colors, sugar, varints,
+]
+
+
+doAssert NimVersion == "1.6.100"
diff --git a/tests/system/tvarargslen.nim b/tests/system/tvarargslen.nim
new file mode 100644
index 000000000..24b54a1e0
--- /dev/null
+++ b/tests/system/tvarargslen.nim
@@ -0,0 +1,60 @@
+discard """
+  output: '''
+tvarargslen.nim:35:2 (1, 2)
+tvarargslen.nim:36:2 12
+tvarargslen.nim:37:2 1
+tvarargslen.nim:38:8 
+done
+'''
+"""
+## line 10
+
+template myecho*(a: varargs[untyped]) =
+  ## shows a useful debugging echo-like proc that is dependency-free (no dependency
+  ## on macros.nim) so can be used in more contexts
+  const info = instantiationInfo(-1, false)
+  const loc = info.filename & ":" & $info.line & ":" & $info.column & " "
+  when varargsLen(a) > 0:
+    echo(loc, a)
+  else:
+    echo(loc)
+
+template fun*(a: varargs[untyped]): untyped =
+  varargsLen(a)
+
+template fun2*(a: varargs[typed]): untyped =
+  a.varargsLen
+
+template fun3*(a: varargs[int]): untyped =
+  a.varargsLen
+
+template fun4*(a: varargs[untyped]): untyped =
+  len(a)
+
+proc main()=
+  myecho (1, 2)
+  myecho 1, 2
+  myecho 1
+  myecho()
+
+  doAssert fun() == 0
+  doAssert fun('a') == 1
+  doAssert fun("asdf", 1) == 2
+
+  doAssert fun2() == 0
+  doAssert fun2('a') == 1
+  doAssert fun2("asdf", 1) == 2
+
+  doAssert fun3() == 0
+  doAssert fun3(10) == 1
+  doAssert fun3(10, 11) == 2
+
+  ## shows why `varargsLen` can't be named `len`
+  doAssert fun4("abcdef") == len("abcdef")
+
+  ## workaround for BUG:D20191218T171447 whereby if testament expected output ends
+  ## in space, testament strips it from expected output but not actual output,
+  ## which leads to a mismatch when running test via megatest
+  echo "done"
+
+main()
diff --git a/tests/template/m1027a.nim b/tests/template/m1027a.nim
new file mode 100644
index 000000000..fad915a2f
--- /dev/null
+++ b/tests/template/m1027a.nim
@@ -0,0 +1 @@
+const version_str* = "mod a"
diff --git a/tests/template/m1027b.nim b/tests/template/m1027b.nim
new file mode 100644
index 000000000..5ff0b9714
--- /dev/null
+++ b/tests/template/m1027b.nim
@@ -0,0 +1 @@
+const version_str* = "mod b"
diff --git a/tests/template/m19277_1.nim b/tests/template/m19277_1.nim
new file mode 100644
index 000000000..840bd4767
--- /dev/null
+++ b/tests/template/m19277_1.nim
@@ -0,0 +1,2 @@
+template foo*(x: untyped) =
+  echo "got: ", x
diff --git a/tests/template/m19277_2.nim b/tests/template/m19277_2.nim
new file mode 100644
index 000000000..de72dad45
--- /dev/null
+++ b/tests/template/m19277_2.nim
@@ -0,0 +1,2 @@
+proc foo*(a: string) =
+  echo "got string: ", a
diff --git a/tests/template/mdotcall.nim b/tests/template/mdotcall.nim
new file mode 100644
index 000000000..fecd8ee26
--- /dev/null
+++ b/tests/template/mdotcall.nim
@@ -0,0 +1,82 @@
+# issue #20073
+
+type Foo = object
+proc foo(f: Foo) = discard
+
+template works*() =
+  var f: Foo
+  foo(f)
+
+template boom*() =
+  var f: Foo
+  f.foo() # Error: attempting to call undeclared routine: 'foo'
+  f.foo # Error: undeclared field: 'foo' for type a.Foo
+
+# issue #7085
+
+proc bar(a: string): string =
+  return a & "bar"
+
+template baz*(a: string): string =
+  var b = a.bar()
+  b
+
+# issue #7223
+
+import mdotcall2
+
+type
+  Bytes* = seq[byte]
+  
+  BytesRange* = object
+    bytes*: Bytes
+    ibegin*, iend*: int
+
+proc privateProc(r: BytesRange): int = r.ibegin
+
+template rangeBeginAddr*(r: BytesRange): pointer =
+  r.bytes.baseAddr.shift(r.privateProc)
+
+# issue #11733
+
+type ObjA* = object
+
+proc foo2(o: var ObjA) = discard
+proc bar2(o: var ObjA, arg: Natural) = discard
+
+template publicTemplateObjSyntax*(o: var ObjA, arg: Natural, doStuff: untyped) =
+  o.foo2()
+  doStuff
+  o.bar2(arg)
+
+# issue #15246
+import os
+
+template sourceBaseName*(): string =
+  bind splitFile
+  instantiationInfo().filename.splitFile().name
+
+# issue #12683
+
+import unicode
+template toRune(s: string): Rune = s.runeAt(0)
+proc heh*[T](x: Slice[T], chars: string) = discard chars.toRune
+
+# issue #7889
+
+from streams import newStringStream, readData, writeData
+
+template bindmeTemplate*(): untyped =
+  var tst = "sometext"
+  var ss = newStringStream("anothertext")
+  ss.writeData(tst[0].addr, 2)
+  discard ss.readData(tst[0].addr, 2) # <= comment this out to make compilation successful
+
+from macros import quote, newIdentNode
+
+macro bindmeQuote*(): untyped =
+  quote do:
+    var tst = "sometext"
+    var ss = newStringStream("anothertext")
+    ss.writeData(tst[0].addr, 2)
+    discard ss.readData(tst[0].addr, 2) # <= comment this out to make compilation successful
diff --git a/tests/template/mdotcall2.nim b/tests/template/mdotcall2.nim
new file mode 100644
index 000000000..e906ac9d6
--- /dev/null
+++ b/tests/template/mdotcall2.nim
@@ -0,0 +1,7 @@
+# imported by mdotcall
+
+proc baseAddr*[T](x: openarray[T]): pointer =
+  cast[pointer](x)
+
+proc shift*(p: pointer, delta: int): pointer =
+  cast[pointer](cast[int](p) + delta)
diff --git a/tests/template/mgensym_generic_cross_module.nim b/tests/template/mgensym_generic_cross_module.nim
index 80b681db4..ea88f67e6 100644
--- a/tests/template/mgensym_generic_cross_module.nim
+++ b/tests/template/mgensym_generic_cross_module.nim
@@ -1,6 +1,6 @@
 
-template makeDomElement(x: untyped, name: string = nil) =
-  const tag {.gensym.} = if name == nil: astToStr(x) else: name
+template makeDomElement(x: untyped, name: string = "") =
+  const tag {.gensym.} = if name.len == 0: astToStr(x) else: name
 
   proc x*(p: int|float) =
     echo tag, p
diff --git a/tests/template/mqualifiedtype1.nim b/tests/template/mqualifiedtype1.nim
new file mode 100644
index 000000000..46569107f
--- /dev/null
+++ b/tests/template/mqualifiedtype1.nim
@@ -0,0 +1,2 @@
+type A* = object
+  x*: int
diff --git a/tests/template/mqualifiedtype2.nim b/tests/template/mqualifiedtype2.nim
new file mode 100644
index 000000000..6a61c14bd
--- /dev/null
+++ b/tests/template/mqualifiedtype2.nim
@@ -0,0 +1,2 @@
+type A* = object
+  x*: array[1000, byte]
diff --git a/tests/template/mtempl5.nim b/tests/template/mtempl5.nim
index 3c2881764..2cc6f91bc 100644
--- a/tests/template/mtempl5.nim
+++ b/tests/template/mtempl5.nim
@@ -7,4 +7,18 @@ template templ*(): int =
   bind gx, gy
   gx + gy
 
+import json
+
+const
+  codeField = "foobar"
+  messageField = "more"
+
+template trap*(path: string, body: untyped): untyped =
+  #bind codeField, messageField
+  try:
+    body
+  except:
+    let msg = getCurrentExceptionMsg()
+    #debug "Error occurred within RPC ", path = path, errorMessage = msg
+    result = %*{codeField: "SERVER_ERROR", messageField: msg}
 
diff --git a/tests/template/otests.nim b/tests/template/otests.nim
index 120146343..9cb9d7fcb 100644
--- a/tests/template/otests.nim
+++ b/tests/template/otests.nim
@@ -163,7 +163,7 @@ when true: #embeddingTest
     """
 
     # Expression template
-    proc test_expression(nums: openarray[int] = []): string =
+    proc test_expression(nums: openArray[int] = []): string =
         var i = 2
         tmpli html"""
             $(no_substitution())
@@ -171,7 +171,7 @@ when true: #embeddingTest
             <div id="age">Age: $($nums[i] & "!!")</div>
         """
 
-    proc test_statements(nums: openarray[int] = []): string =
+    proc test_statements(nums: openArray[int] = []): string =
         tmpli html"""
             $(test_expression(nums))
             $if true {
diff --git a/tests/template/sunset.nimf b/tests/template/sunset.nimf
new file mode 100644
index 000000000..bd57bb8e1
--- /dev/null
+++ b/tests/template/sunset.nimf
@@ -0,0 +1,68 @@
+#? stdtmpl
+#proc sunsetTemplate*(current, ticker, content: string,
+#                     tabs: openArray[array[0..1, string]]): string = 
+#  result = ""
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+
+<head>
+  <title>Nimrod Programming System</title>
+  <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
+  <link rel="stylesheet" type="text/css" href="style/style.css" />
+</head>
+
+<body>
+  <div id="main">
+    <div id="links">
+      <!-- **** INSERT LINKS HERE **** -->
+    </div>
+    <div id="logo"><h1>Nimrod Programming System</h1></div>
+    <div id="content">
+      <div id="menu">
+        <ul>
+  #for item in items(tabs):
+    #var name = item[0]
+    #var t = item[1]
+    #if t == current:
+          <li><a id="selected" href="${t}.html" title = "Nimrod - $name">$name</a></li>
+    #else:
+          <li><a               href="${t}.html" title = "Nimrod - $name">$name</a></li>
+    #end if
+  #end for
+        </ul>
+      </div>
+      <div id="column1">
+        <div class="sidebaritem">
+          <div class="sbihead">
+            <h1>latest news</h1>
+          </div>
+          <div class="sbicontent">
+            $ticker
+          </div>
+        </div>
+        <div class="sidebaritem">
+          <div class="sbihead">
+            <h1>additional links</h1>
+          </div>
+          <div class="sbilinks">
+            <!-- **** INSERT ADDITIONAL LINKS HERE **** -->
+            <ul>
+              <li><a class="reference" href="http://llvm.org">LLVM</a></li>
+              <li><a class="reference" href="http://gcc.gnu.org">GCC</a></li>
+            </ul>
+          </div>
+        </div>
+      </div>
+      <div id="column2">
+      $content
+      </div>
+    </div>
+    <div id="footer">
+      copyright &copy; 2008 Andreas Rumpf | Last update: ${getDateStr()}
+      | <a class="reference" href="http://validator.w3.org/check?uri=referer">XHTML 1.1</a>
+      | <a class="reference" href="http://jigsaw.w3.org/css-validator/check/referer">CSS</a>
+      | <a class="reference" href="http://www.dcarter.co.uk">design by dcarter</a>
+    </div>
+  </div>
+</body>
+</html>
diff --git a/tests/template/sunset.tmpl b/tests/template/sunset.tmpl
deleted file mode 100644
index 465b12a5e..000000000
--- a/tests/template/sunset.tmpl
+++ /dev/null
@@ -1,68 +0,0 @@
-#? stdtmpl
-#proc sunsetTemplate*(current, ticker, content: string,
-#                     tabs: openarray[array[0..1, string]]): string = 
-#  result = ""
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
-
-<head>
-  <title>Nimrod Programming System</title>
-  <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
-  <link rel="stylesheet" type="text/css" href="style/style.css" />
-</head>
-
-<body>
-  <div id="main">
-    <div id="links">
-      <!-- **** INSERT LINKS HERE **** -->
-    </div>
-    <div id="logo"><h1>Nimrod Programming System</h1></div>
-    <div id="content">
-      <div id="menu">
-        <ul>
-  #for item in items(tabs):
-    #var name = item[0]
-    #var t = item[1]
-    #if t == current:
-          <li><a id="selected" href="${t}.html" title = "Nimrod - $name">$name</a></li>
-    #else:
-          <li><a               href="${t}.html" title = "Nimrod - $name">$name</a></li>
-    #end if
-  #end for
-        </ul>
-      </div>
-      <div id="column1">
-        <div class="sidebaritem">
-          <div class="sbihead">
-            <h1>latest news</h1>
-          </div>
-          <div class="sbicontent">
-            $ticker
-          </div>
-        </div>
-        <div class="sidebaritem">
-          <div class="sbihead">
-            <h1>additional links</h1>
-          </div>
-          <div class="sbilinks">
-            <!-- **** INSERT ADDITIONAL LINKS HERE **** -->
-            <ul>
-              <li><a class="reference" href="http://llvm.org">LLVM</a></li>
-              <li><a class="reference" href="http://gcc.gnu.org">GCC</a></li>
-            </ul>
-          </div>
-        </div>
-      </div>
-      <div id="column2">
-      $content
-      </div>
-    </div>
-    <div id="footer">
-      copyright &copy; 2008 Andreas Rumpf | Last update: ${getDateStr()}
-      | <a class="reference" href="http://validator.w3.org/check?uri=referer">XHTML 1.1</a>
-      | <a class="reference" href="http://jigsaw.w3.org/css-validator/check/referer">CSS</a>
-      | <a class="reference" href="http://www.dcarter.co.uk">design by dcarter</a>
-    </div>
-  </div>
-</body>
-</html>
diff --git a/tests/template/t1027.nim b/tests/template/t1027.nim
new file mode 100644
index 000000000..fe03c4ea9
--- /dev/null
+++ b/tests/template/t1027.nim
@@ -0,0 +1,22 @@
+discard """
+  cmd: "nim check --hints:off $file"
+  errormsg: ""
+  nimout: '''
+t1027.nim(20, 19) Error: ambiguous identifier: 'version_str' -- use one of the following:
+  m1027a.version_str: string
+  m1027b.version_str: string
+'''
+"""
+
+
+
+
+
+
+import m1027a, m1027b
+
+# bug #1027
+template wrap_me(stuff): untyped =
+  echo "Using " & version_str
+
+wrap_me("hey")
diff --git a/tests/template/t11705.nim b/tests/template/t11705.nim
new file mode 100644
index 000000000..65ddc7e6c
--- /dev/null
+++ b/tests/template/t11705.nim
@@ -0,0 +1,17 @@
+type RefObj = ref object
+
+proc `[]`(val: static[int]) = # works with different name/overload or without static arg
+  discard
+
+template noRef*(T: typedesc): typedesc = # works without template indirection
+  typeof(default(T)[])
+
+proc `=destroy`(x: var noRef(RefObj)) =
+  discard
+
+proc foo =
+  var x = new RefObj
+  doAssert $(x[]) == "()"
+
+# bug #11705
+foo()
diff --git a/tests/template/t13426.nim b/tests/template/t13426.nim
new file mode 100644
index 000000000..f7f44749c
--- /dev/null
+++ b/tests/template/t13426.nim
@@ -0,0 +1,87 @@
+discard """
+  cmd: "nim check --hints:off $file"
+  errormsg: ""
+  nimout: '''
+t13426.nim(81, 6) template/generic instantiation of `fun` from here
+t13426.nim(80, 24) Error: type mismatch: got <int> but expected 'string'
+t13426.nim(81, 6) template/generic instantiation of `fun` from here
+t13426.nim(80, 17) Error: type mismatch: got <uint, string>
+but expected one of:
+proc `and`(x, y: uint): uint
+  first type mismatch at position: 2
+  required type for y: uint
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: uint64): uint64
+  first type mismatch at position: 2
+  required type for y: uint64
+  but expression 'high(@[1])' is of type: string
+10 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them
+
+expression: 1'u and high(@[1])
+t13426.nim(81, 6) template/generic instantiation of `fun` from here
+t13426.nim(80, 17) Error: expression '' has no type (or is ambiguous)
+t13426.nim(87, 6) template/generic instantiation of `fun` from here
+t13426.nim(86, 22) Error: type mismatch: got <int> but expected 'string'
+t13426.nim(87, 6) template/generic instantiation of `fun` from here
+t13426.nim(86, 15) Error: type mismatch: got <int literal(1), string>
+but expected one of:
+proc `and`(x, y: int): int
+  first type mismatch at position: 2
+  required type for y: int
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: int16): int16
+  first type mismatch at position: 2
+  required type for y: int16
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: int32): int32
+  first type mismatch at position: 2
+  required type for y: int32
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: int64): int64
+  first type mismatch at position: 2
+  required type for y: int64
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: int8): int8
+  first type mismatch at position: 2
+  required type for y: int8
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: uint): uint
+  first type mismatch at position: 2
+  required type for y: uint
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: uint16): uint16
+  first type mismatch at position: 2
+  required type for y: uint16
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: uint32): uint32
+  first type mismatch at position: 2
+  required type for y: uint32
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: uint64): uint64
+  first type mismatch at position: 2
+  required type for y: uint64
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: uint8): uint8
+  first type mismatch at position: 2
+  required type for y: uint8
+  but expression 'high(@[1])' is of type: string
+2 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them
+
+expression: 1 and high(@[1])
+t13426.nim(87, 6) template/generic instantiation of `fun` from here
+t13426.nim(86, 15) Error: expression '' has no type (or is ambiguous)
+'''
+"""
+
+# bug # #13426
+block:
+  template bar(t): string = high(t)
+  proc fun[A](key: A) =
+    var h = 1'u and bar(@[1])
+  fun(0)
+
+block:
+  template bar(t): string = high(t)
+  proc fun[A](key: A) =
+    var h = 1 and bar(@[1])
+  fun(0)
diff --git a/tests/template/t17433.nim b/tests/template/t17433.nim
new file mode 100644
index 000000000..053d33ff0
--- /dev/null
+++ b/tests/template/t17433.nim
@@ -0,0 +1,16 @@
+# Inside template bodies, ensure return types referencing a param are replaced.
+# This helps guarantee that return parameter analysis happens after argument
+# analysis.
+ 
+# bug #17433
+
+from std/macros import expandMacros
+
+proc bar(a: typedesc): a = default(a)
+doAssert bar(float) == 0.0
+doAssert bar(string) == ""
+
+template main =
+  proc baz(a: typedesc): a = default(a)
+  doAssert baz(float) == 0.0
+main()
diff --git a/tests/template/t18113.nim b/tests/template/t18113.nim
new file mode 100644
index 000000000..a84b3fd0e
--- /dev/null
+++ b/tests/template/t18113.nim
@@ -0,0 +1,14 @@
+# ensure template pragma handling doesn't eagerly attempt to add an implicit
+# 'pushed' pragma to the evaluation of any intermediate AST prior to
+# substitution.
+ 
+# bug #18113
+
+import sequtils
+
+{.push raises: [Defect].}
+
+var a = toSeq([1, 2, 3, 5, 10]).filterIt(it > 5)
+
+doAssert a.len == 1
+doAssert a[0] == 10
diff --git a/tests/template/t19149.nim b/tests/template/t19149.nim
new file mode 100644
index 000000000..631e8fc30
--- /dev/null
+++ b/tests/template/t19149.nim
@@ -0,0 +1,19 @@
+type Foo = tuple[active: bool, index: int]
+
+
+var f: Foo
+
+# template result type during match stage
+# f:var Foo
+# a:Foo
+# tyVar
+# tyTuple
+# after change to proc
+# f:Foo
+# a:Foo
+# tyTuple
+# tyTuple
+
+template cursor(): var Foo = f
+discard cursor()
+
diff --git a/tests/template/t19277.nim b/tests/template/t19277.nim
new file mode 100644
index 000000000..16435a09c
--- /dev/null
+++ b/tests/template/t19277.nim
@@ -0,0 +1,19 @@
+discard """
+  output: '''
+got: 0
+'''
+"""
+
+# issue #19277
+
+import m19277_1, m19277_2
+
+template injector(val: untyped): untyped =
+  template subtemplate: untyped = val
+  subtemplate()
+
+template methodCall(val: untyped): untyped = val
+
+{.push raises: [Defect].}
+
+foo(injector(0).methodCall())
diff --git a/tests/template/t19700.nim b/tests/template/t19700.nim
new file mode 100644
index 000000000..cc2944944
--- /dev/null
+++ b/tests/template/t19700.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "type mismatch: got <Obj, Obj, template (x: untyped, y: untyped): untyped>"
+"""
+
+type Obj = object
+
+proc apply[T, R](a, b: T; f: proc(x, y: T): R): R = f(a, b)
+
+let a, b = Obj()
+discard apply(a, b, `!=`)
diff --git a/tests/template/t21231.nim b/tests/template/t21231.nim
new file mode 100644
index 000000000..51e96cdd6
--- /dev/null
+++ b/tests/template/t21231.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "undeclared identifier: 'x'"
+"""
+
+var x: int
+# bug #21231
+template f(y: untyped) = echo y.x
+for i in 1 .. 10:
+  x = i
+  f(system)
diff --git a/tests/template/t21532.nim b/tests/template/t21532.nim
new file mode 100644
index 000000000..3193b0dc3
--- /dev/null
+++ b/tests/template/t21532.nim
@@ -0,0 +1,8 @@
+
+template elementType(a: untyped): typedesc =
+  typeof(block: (for ai in a: ai))
+
+func fn[T](a: T) =
+  doAssert elementType(a) is int
+
+@[1,2,3].fn
\ No newline at end of file
diff --git a/tests/template/t24112.nim b/tests/template/t24112.nim
new file mode 100644
index 000000000..175fc7d5e
--- /dev/null
+++ b/tests/template/t24112.nim
@@ -0,0 +1,19 @@
+discard """
+  matrix: "--skipParentCfg --filenames:legacyRelProj --hints:off"
+  action: reject
+"""
+
+# issue #24112, needs --experimental:openSym disabled
+
+block: # simplified
+  type
+    SomeObj = ref object # Doesn't error if you make SomeObj be non-ref
+  template foo = yield SomeObj()
+  when compiles(foo): discard
+
+import std/asyncdispatch
+block:
+  proc someProc(): Future[void] {.async.} = discard
+  proc foo() =
+    await someProc() #[tt.Error
+                  ^ Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead]#
diff --git a/tests/template/t2416.nim b/tests/template/t2416.nim
deleted file mode 100644
index f73880718..000000000
--- a/tests/template/t2416.nim
+++ /dev/null
@@ -1,2 +0,0 @@
-import i2416
-i2416()
diff --git a/tests/template/t2do.nim b/tests/template/t2do.nim
deleted file mode 100644
index f5f6393dc..000000000
--- a/tests/template/t2do.nim
+++ /dev/null
@@ -1,23 +0,0 @@
-discard """
-  output: "8.0"
-"""
-
-# bug #2057
-
-proc mpf_get_d(x: int): float = float(x)
-proc mpf_cmp_d(a: int; b: float): int = 0
-
-template toFloatHelper(result, tooSmall, tooLarge: untyped) =
-  result = mpf_get_d(a)
-  if result == 0.0 and mpf_cmp_d(a,0.0) != 0:
-    tooSmall
-  if result == Inf:
-    tooLarge
-
-proc toFloat*(a: int): float =
-  toFloatHelper(result) do:
-    raise newException(ValueError, "number too small")
-  do:
-    raise newException(ValueError, "number too large")
-
-echo toFloat(8)
diff --git a/tests/template/t6217.nim b/tests/template/t6217.nim
new file mode 100644
index 000000000..b27b61881
--- /dev/null
+++ b/tests/template/t6217.nim
@@ -0,0 +1,19 @@
+discard """
+  output: '''
+start
+side effect!
+end
+'''
+"""
+
+# bug #6217
+
+template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a+a
+
+proc f(): int =
+  echo "side effect!"
+  result = 55
+
+echo "start"
+doAssert f() * 2 == 110
+echo "end"
diff --git a/tests/template/t9534.nim b/tests/template/t9534.nim
new file mode 100644
index 000000000..8d66f42a0
--- /dev/null
+++ b/tests/template/t9534.nim
@@ -0,0 +1,21 @@
+discard """
+  targets: "c cpp"
+"""
+
+# bug #9534
+type
+  Object = object
+    data: int
+
+template test() =
+  proc methodName(o: Object): int =
+    var p: pointer
+    doAssert o.data == 521
+    let f {.used.} = cast[proc (o: int): int {.nimcall.}](p)
+    doAssert o.data == 521
+    result = 1314
+
+  var a = Object(data: 521)
+  doAssert methodName(a) == 1314
+
+test()
diff --git a/tests/template/t_otemplates.nim b/tests/template/t_otemplates.nim
index 6597bd37a..b278bea0f 100644
--- a/tests/template/t_otemplates.nim
+++ b/tests/template/t_otemplates.nim
@@ -312,7 +312,7 @@ proc parse_until_symbol(node: NimNode, value: string, index: var int): bool {.co
 
 
 proc parse_template(node: NimNode, value: string) =
-    ## Parses through entire template, outputing valid
+    ## Parses through entire template, outputting valid
     ## Nim code into the input `node` AST.
     var index = 0
     while index < value.len and
@@ -340,6 +340,6 @@ macro tmpl*(body: untyped): untyped =
 
 
 # Run tests
-when isMainModule:
+when true:
     include otests
     echo "Success"
diff --git a/tests/template/taliassyntax.nim b/tests/template/taliassyntax.nim
new file mode 100644
index 000000000..2d8237d93
--- /dev/null
+++ b/tests/template/taliassyntax.nim
@@ -0,0 +1,74 @@
+type Foo = object
+  bar: int
+
+var foo = Foo(bar: 10)
+template bar: int = foo.bar
+doAssert bar == 10
+bar = 15
+doAssert bar == 15
+var foo2 = Foo(bar: -10)
+doAssert bar == 15
+# works in generics
+proc genericProc[T](x: T): string =
+  $(x, bar)
+doAssert genericProc(true) == "(true, 15)"
+# redefine
+template bar: int {.redefine.} = foo2.bar
+doAssert bar == -10
+
+block: # subscript
+  var bazVal = @[1, 2, 3]
+  template baz: seq[int] = bazVal
+  doAssert baz[1] == 2
+  proc genericProc2[T](x: T): string =
+    result = $(x, baz[1])
+    baz[1] = 7
+  doAssert genericProc2(true) == "(true, 2)"
+  doAssert baz[1] == 7
+  baz[1] = 14
+  doAssert baz[1] == 14
+
+block: # type alias
+  template Int2: untyped = int
+  let x: Int2 = 123
+  proc generic[T](): string =
+    template U: untyped = T
+    var x: U
+    result = $typeof(x)
+    doAssert result == $U
+    doAssert result == $T
+  doAssert generic[int]() == "int"
+  doAssert generic[Int2]() == "int"
+  doAssert generic[string]() == "string"
+  doAssert generic[seq[int]]() == "seq[int]"
+  doAssert generic[seq[Int2]]() == "seq[int]"
+  discard generic[123]()
+  proc genericStatic[X; T: static[X]](): string =
+    template U: untyped = T
+    result = $U
+    doAssert result == $T
+  doAssert genericStatic[int, 123]() == "123"
+  doAssert genericStatic[Int2, 123]() == "123"
+  doAssert genericStatic[(string, bool), ("a", true)]() == "(\"a\", true)"
+
+block: # issue #13515
+  template test: bool = true
+  # compiles:
+  if not test:
+    doAssert false
+  # does not compile:
+  template x =
+    if not test:
+      doAssert false
+  x
+
+import macros
+
+block: # issue #21727
+  template debugAnnotation(s: typed): string =
+    astToStr s
+
+  macro cpsJump(x: int): untyped =
+    result = newLit(debugAnnotation(cpsJump))
+  
+  doAssert cpsJump(13) == "cpsJump"
diff --git a/tests/template/taliassyntaxerrors.nim b/tests/template/taliassyntaxerrors.nim
new file mode 100644
index 000000000..f16acaf91
--- /dev/null
+++ b/tests/template/taliassyntaxerrors.nim
@@ -0,0 +1,28 @@
+discard """
+  cmd: "nim check --hints:off $file"
+"""
+
+block: # with params
+  type Foo = object
+    bar: int
+
+  var foo = Foo(bar: 10)
+  template bar(x: int): int = x + foo.bar
+  let a = bar #[tt.Error
+      ^ invalid type: 'template (x: int): int' for let. Did you mean to call the template with '()'?]#
+  bar = 15 #[tt.Error
+  ^ 'bar' cannot be assigned to]#
+
+block: # generic template
+  type Foo = object
+    bar: int
+
+  var foo = Foo(bar: 10)
+  template bar[T]: T = T(foo.bar)
+  let a = bar #[tt.Error
+      ^ invalid type: 'template (): T' for let. Did you mean to call the template with '()'?; tt.Error
+          ^ 'bar' has unspecified generic parameters]#
+  let b = bar[float]()
+  doAssert b == 10.0
+  bar = 15 #[tt.Error
+  ^ 'bar' cannot be assigned to]#
diff --git a/tests/template/tcallsitelineinfo.nim b/tests/template/tcallsitelineinfo.nim
new file mode 100644
index 000000000..5fed93363
--- /dev/null
+++ b/tests/template/tcallsitelineinfo.nim
@@ -0,0 +1,17 @@
+discard """
+  nimout: '''
+tcallsitelineinfo.nim(17, 4) Warning: abc [User]
+'''
+  exitcode: 1
+  outputsub: '''
+tcallsitelineinfo.nim(17) tcallsitelineinfo
+Error: unhandled exception: def [ValueError]
+'''
+"""
+
+template foo(): untyped {.callsite.} =
+  {.warning: "abc".}
+  raise newException(ValueError, "def")
+  echo "hello"
+
+foo()
diff --git a/tests/template/tcallsitelineinfo2.nim b/tests/template/tcallsitelineinfo2.nim
new file mode 100644
index 000000000..d5f257474
--- /dev/null
+++ b/tests/template/tcallsitelineinfo2.nim
@@ -0,0 +1,20 @@
+discard """
+  nimout: '''
+tcallsitelineinfo2.nim(18, 1) Warning: abc [User]
+tcallsitelineinfo2.nim(19, 12) Warning: def [User]
+'''
+  exitcode: 1
+  outputsub: '''
+tcallsitelineinfo2.nim(20) tcallsitelineinfo2
+Error: unhandled exception: ghi [ValueError]
+'''
+"""
+
+template foo(a: untyped): untyped {.callsite.} =
+  {.warning: "abc".}
+  a
+  echo "hello"
+
+foo: # with `{.line.}:`, the following do not keep their line information:
+  {.warning: "def".}
+  raise newException(ValueError, "ghi")
diff --git a/tests/template/tcan_access_hidden_field.nim b/tests/template/tcan_access_hidden_field.nim
deleted file mode 100644
index a6f6490cc..000000000
--- a/tests/template/tcan_access_hidden_field.nim
+++ /dev/null
@@ -1,9 +0,0 @@
-discard """
-  output: 33
-"""
-
-import mcan_access_hidden_field
-
-var myfoo = createFoo(33, 44)
-
-echo myfoo.geta
diff --git a/tests/template/tconfusinglocal.nim b/tests/template/tconfusinglocal.nim
index 50bf8f4b2..9f641e2bf 100644
--- a/tests/template/tconfusinglocal.nim
+++ b/tests/template/tconfusinglocal.nim
@@ -1,3 +1,7 @@
+discard """
+output: "0"
+"""
+
 
 # bug #5135
 proc fail*[E](e: E): void =
diff --git a/tests/template/tdefault_nil.nim b/tests/template/tdefault_nil.nim
deleted file mode 100644
index c5c372d9e..000000000
--- a/tests/template/tdefault_nil.nim
+++ /dev/null
@@ -1,14 +0,0 @@
-
-# bug #2629
-import sequtils, os
-
-template glob_rst(basedir: string = nil): untyped =
-  if baseDir.isNil:
-    to_seq(walk_files("*.rst"))
-  else:
-    to_seq(walk_files(basedir/"*.rst"))
-
-let
-  rst_files = concat(glob_rst(), glob_rst("docs"))
-
-when isMainModule: echo rst_files
diff --git a/tests/template/tdefaultparam.nim b/tests/template/tdefaultparam.nim
new file mode 100644
index 000000000..7ea0b2b25
--- /dev/null
+++ b/tests/template/tdefaultparam.nim
@@ -0,0 +1,56 @@
+block:
+  template foo(a: untyped, b: untyped = a(0)): untyped =
+    let x = a(0)
+    let y = b
+    (x, y)
+  proc bar(x: int): int = x + 1
+  doAssert foo(bar, b = bar(0)) == (1, 1)
+  doAssert foo(bar) == (1, 1)
+
+block: # issue #23506
+  var a: string
+  template foo(x: int; y = x) =
+    a = $($x, $y)
+  foo(1)
+  doAssert a == "(\"1\", \"1\")"
+
+block: # untyped params with default value
+  macro foo(x: typed): untyped =
+    result = x
+  template test(body: untyped, alt: untyped = (;), maxTries = 3): untyped {.foo.} =
+    body
+    alt
+  var s = "a"
+  test:
+    s.add "b"
+  do:
+    s.add "c"
+  doAssert s == "abc"
+  template test2(body: untyped, alt: untyped = s.add("e"), maxTries = 3): untyped =
+    body
+    alt
+  test2:
+    s.add "d"
+  doAssert s == "abcde"
+  template test3(body: untyped = willNotCompile) =
+    discard
+  test3()
+
+block: # typed params with `void` default value
+  macro foo(x: typed): untyped =
+    result = x
+  template test(body: untyped, alt: typed = (;), maxTries = 3): untyped {.foo.} =
+    body
+    alt
+  var s = "a"
+  test:
+    s.add "b"
+  do:
+    s.add "c"
+  doAssert s == "abc"
+  template test2(body: untyped, alt: typed = s.add("e"), maxTries = 3): untyped =
+    body
+    alt
+  test2:
+    s.add "d"
+  doAssert s == "abcde"
diff --git a/tests/template/tdotcall.nim b/tests/template/tdotcall.nim
new file mode 100644
index 000000000..dc97fd52e
--- /dev/null
+++ b/tests/template/tdotcall.nim
@@ -0,0 +1,32 @@
+import mdotcall
+
+block: # issue #20073
+  works()
+  boom()
+
+block: # issue #7085
+  doAssert baz("hello") == "hellobar"
+  doAssert baz"hello" == "hellobar"
+  doAssert "hello".baz == "hellobar"
+
+block: # issue #7223
+  var r = BytesRange(bytes: @[1.byte, 2, 3], ibegin: 0, iend: 2)
+  var a = r.rangeBeginAddr
+
+block: # issue #11733
+  var a: ObjA
+  var evaluated = false
+  a.publicTemplateObjSyntax(42): evaluated = true
+  doAssert evaluated
+
+block: # issue #15246
+  doAssert sourceBaseName() == "tdotcall"
+
+block: # issue #12683
+  heh(0..40, "|")
+
+block: # issue #7889
+  if false:
+    bindmeQuote()
+  if false:
+    bindmeTemplate()
diff --git a/tests/template/template_issues.nim b/tests/template/template_issues.nim
new file mode 100644
index 000000000..58c40941d
--- /dev/null
+++ b/tests/template/template_issues.nim
@@ -0,0 +1,304 @@
+discard """
+output: '''
+@[]
+5
+0
+a
+hi
+Hello, World!
+(e: 42)
+hey
+foo
+foo
+foo
+false
+true
+'''
+"""
+
+
+import macros, json
+
+
+block t2057:
+  proc mpf_get_d(x: int): float = float(x)
+  proc mpf_cmp_d(a: int; b: float): int = 0
+
+  template toFloatHelper(result, tooSmall, tooLarge: untyped) =
+    result = mpf_get_d(a)
+    if result == 0.0 and mpf_cmp_d(a,0.0) != 0:
+      tooSmall
+    if result == Inf:
+      tooLarge
+
+  proc toFloat(a: int): float =
+    toFloatHelper(result) do:
+      raise newException(ValueError, "number too small")
+    do:
+      raise newException(ValueError, "number too large")
+
+  doAssert toFloat(8) == 8.0
+
+
+
+import sequtils, os
+block t2629:
+  template glob_rst(basedir: string = ""): untyped =
+    if baseDir.len == 0:
+      to_seq(walk_files("*.rst"))
+    else:
+      to_seq(walk_files(basedir/"*.rst"))
+
+  let rst_files = concat(glob_rst(), glob_rst("docs"))
+
+  when true: echo rst_files
+
+
+block t5417:
+  macro genBody: untyped =
+    let sbx = genSym(nskLabel, "test")
+    when true:
+      result = quote do:
+        block `sbx`:
+          break `sbx`
+    else:
+      template foo(s1, s2) =
+        block s1:
+          break s2
+      result = getAst foo(sbx, sbx)
+
+  proc test() =
+    genBody()
+
+
+
+block t909:
+  template baz() =
+    proc bar() =
+      var x = 5
+      iterator foo(): int {.closure.} =
+        echo x
+      var y = foo
+      discard y()
+
+  macro test(): untyped =
+    result = getAst(baz())
+
+  test()
+  bar()
+
+
+
+block t993:
+  type PNode = ref object of RootObj
+
+  template litNode(name, ty)  =
+    type name = ref object of PNode
+      val: ty
+  litNode PIntNode, int
+
+  template withKey(j: JsonNode; key: string; varname,
+                    body: untyped): typed =
+    if j.hasKey(key):
+      let varname{.inject.}= j[key]
+      block:
+        body
+
+  var j = parsejson("{\"zzz\":1}")
+  withkey(j, "foo", x):
+    echo(x)
+
+
+
+
+block t1337:
+  template someIt(a, pred): untyped =
+    var it {.inject.} = 0
+    pred
+
+  proc aProc(n: auto) =
+    n.someIt(echo(it))
+
+  aProc(89)
+
+
+
+import mlt
+block t4564:
+  type Bar = ref object of RootObj
+  proc foo(a: Bar): int = 0
+  var a: Bar
+  let b = a.foo() > 0
+
+
+
+block t8052:
+  type
+    UintImpl[N: static[int], T: SomeUnsignedInt] = object
+      raw_data: array[N, T]
+
+  template genLoHi(TypeImpl: untyped): untyped =
+    template loImpl[N: static[int], T: SomeUnsignedInt](dst: TypeImpl[N div 2, T], src: TypeImpl[N, T]) =
+      let halfSize = N div 2
+      for i in 0 ..< halfSize:
+        dst.raw_data[i] = src.raw_data[i]
+
+    proc lo[N: static[int], T: SomeUnsignedInt](x: TypeImpl[N,T]): TypeImpl[N div 2, T] {.inline.}=
+      loImpl(result, x)
+
+  genLoHi(UintImpl)
+
+  var a: UintImpl[4, uint32]
+
+  a.raw_data = [1'u32, 2'u32, 3'u32, 4'u32]
+  doAssert a.lo.raw_data.len == 2
+  doAssert a.lo.raw_data[0] == 1
+  doAssert a.lo.raw_data[1] == 2
+
+
+
+block t2585:
+  type
+    RenderPass = object
+       state: ref int
+    RenderData = object
+        fb: int
+        walls: seq[RenderPass]
+    Mat2 = int
+    Vector2[T] = T
+    Pixels=int
+
+  template use(fb: int, st: untyped): untyped =
+      echo "a ", $fb
+      st
+      echo "a ", $fb
+
+  proc render(rdat: var RenderData; passes: var openArray[RenderPass]; proj: Mat2;
+              indexType = 1) =
+      for i in 0 ..< len(passes):
+          echo "blah ", repr(passes[i])
+
+  proc render2(rdat: var RenderData; screenSz: Vector2[Pixels]; proj: Mat2) =
+      use rdat.fb:
+          render(rdat, rdat.walls, proj, 1)
+
+
+
+block t4292:
+  template foo(s: string): string = s
+  proc variadicProc(v: varargs[string, foo]) = echo v[0]
+  variadicProc("a")
+
+
+
+block t2670:
+  template testTemplate(b: bool): typed =
+    when b:
+        var a = "hi"
+    else:
+        var a = 5
+    echo a
+  testTemplate(true)
+
+
+
+block t4097:
+  var i {.compileTime.} = 2
+
+  template defineId(t: typedesc) =
+    const id {.genSym.} = i
+    static: inc(i)
+    proc idFor(T: typedesc[t]): int {.inline, raises: [].} = id
+
+  defineId(int8)
+  defineId(int16)
+
+  doAssert idFor(int8) == 2
+  doAssert idFor(int16) == 3
+
+
+
+block t5235:
+  template outer(body: untyped) =
+    template test(val: string) =
+      const SomeConst: string = val
+      echo SomeConst
+    body
+
+  outer:
+    test("Hello, World!")
+
+
+# bug #11941
+type X = object
+  e: int
+
+proc works(T: type X, v: auto): T = T(e: v)
+template fails(T: type X, v: auto): T = T(e: v)
+
+var
+  w = X.works(42)
+  x = X.fails(42)
+
+echo x
+
+import mtempl5
+
+
+proc foo(): auto =
+  trap "foo":
+    echo "hey"
+
+discard foo()
+
+
+# bug #4722
+type
+  IteratorF*[In] = iterator() : In {.closure.}
+
+template foof(In: untyped) : untyped =
+  proc ggg*(arg: IteratorF[In]) =
+    for i in arg():
+      echo "foo"
+
+
+iterator hello() : int {.closure.} =
+  for i in 1 .. 3:
+    yield i
+
+foof(int)
+ggg(hello)
+
+
+# bug #2586
+var z = 10'u8
+echo z < 9 # Works
+echo z > 9 # Error: type mismatch
+
+
+# bug #5993
+template foo(p: proc) =
+  var bla = 5
+  p(bla)
+
+foo() do(t: var int):
+  discard
+  t = 5
+
+proc bar(t: var int) =
+  t = 5
+
+foo(bar)
+
+block: # bug #12595
+  template test() =
+    let i = 42
+    discard {i: ""}
+
+  test()
+
+block: # bug #21920
+  template t[T](): T =
+    discard
+
+  t[void]() # Error: expression has no type: discard
diff --git a/tests/template/template_pragmas.nim b/tests/template/template_pragmas.nim
new file mode 100644
index 000000000..da532b010
--- /dev/null
+++ b/tests/template/template_pragmas.nim
@@ -0,0 +1,9 @@
+proc output_x:string {.compileTime.} = "x"
+
+template t =
+  const x = output_x()
+  let
+    bar {.exportc:"bar" & x.} = 100
+
+static:
+  doAssert(compiles (t()))
diff --git a/tests/template/template_various.nim b/tests/template/template_various.nim
new file mode 100644
index 000000000..2088b1739
--- /dev/null
+++ b/tests/template/template_various.nim
@@ -0,0 +1,406 @@
+discard """
+output: '''
+i2416
+33
+foo55
+foo8.0
+fooaha
+bar7
+10
+4true
+132
+20
+1
+-1
+4
+11
+26
+57
+-1-1-1
+  4
+4
+  4
+  11
+11
+  4
+  11
+  26
+26
+  4
+  11
+  26
+  57
+57
+-1-1-1
+'''
+"""
+
+import macros
+
+
+
+
+import i2416
+i2416()
+
+
+import mcan_access_hidden_field
+var myfoo = createFoo(33, 44)
+echo myfoo.geta
+
+
+import mgensym_generic_cross_module
+foo(55)
+foo 8.0
+foo "aha"
+bar 7
+
+
+
+block generic_templates:
+  type
+    SomeObj = object of RootObj
+    Foo[T, U] = object
+      x: T
+      y: U
+
+  template someTemplate[T](): tuple[id: int32, obj: T] =
+    var result: tuple[id: int32, obj: T] = (0'i32, T())
+    result
+
+  let ret = someTemplate[SomeObj]()
+
+  # https://github.com/nim-lang/Nim/issues/7829
+  proc inner[T](): int =
+    discard
+
+  template outer[A](): untyped =
+    inner[A]()
+
+  template outer[B](x: int): untyped =
+    inner[B]()
+
+  var i1 = outer[int]()
+  var i2 = outer[int](i1)
+
+  # https://github.com/nim-lang/Nim/issues/7883
+  template t1[T: int|int64](s: string): T =
+     var t: T
+     t
+
+  template t1[T: int|int64](x: int, s: string): T =
+     var t: T
+     t
+
+  var i3: int = t1[int]("xx")
+
+from strutils import contains
+
+block tgetast_typeliar:
+  proc error(s: string) = quit s
+
+  macro assertOrReturn2(condition: bool; message: string) =
+    var line = condition.lineInfo()
+    result = quote do:
+      block:
+        if not likely(`condition`):
+          error("Assertion failed: " & $(`message`) & "\n" & `line`)
+          return
+
+  macro assertOrReturn(condition: bool) =
+    var message : NimNode = newLit(condition.repr)
+    # echo message
+    result = getAst assertOrReturn2(condition, message)
+    # echo result.repr
+    let s = result.repr
+    doAssert """error("Assertion failed:""" in s
+
+  proc point(size: int16): tuple[x, y: int16] =
+    # returns random point in square area with given `size`
+    assertOrReturn size > 0
+
+
+
+type
+  MyFloat = object
+    val: float
+converter to_myfloat(x: float): MyFloat {.inline.} =
+  MyFloat(val: x)
+
+block pattern_with_converter:
+  proc `+`(x1, x2: MyFloat): MyFloat =
+    MyFloat(val: x1.val + x2.val)
+
+  proc `*`(x1, x2: MyFloat): MyFloat =
+      MyFloat(val: x1.val * x2.val)
+
+  template optMul{`*`(a, 2.0)}(a: MyFloat): MyFloat =
+    a + a
+
+  func floatMyFloat(x: MyFloat): MyFloat =
+    result = x * 2.0
+
+  func floatDouble(x: float): float =
+    result = x * 2.0
+
+  doAssert floatDouble(5) == 10.0
+
+
+
+
+block procparshadow:
+  template something(name: untyped) =
+    proc name(x: int) =
+      var x = x # this one should not be rejected by the compiler (#5225)
+      echo x
+
+  something(what)
+  what(10)
+
+  # bug #4750
+  type
+    O = object
+      i: int
+    OP = ptr O
+
+  template alf(p: pointer): untyped =
+    cast[OP](p)
+
+  proc t1(al: pointer) =
+    var o = alf(al)
+
+  proc t2(alf: pointer) =
+    var x = alf
+    var o = alf(x)
+
+
+
+block symchoicefield:
+  type Foo = object
+    len: int
+
+  var f = Foo(len: 40)
+
+  template getLen(f: Foo): int = f.len
+
+  doAssert f.getLen == 40
+  # This fails, because `len` gets the nkOpenSymChoice
+  # treatment inside the template early pass and then
+  # it can't be recognized as a field anymore
+
+
+
+import os, times
+include "sunset.nimf"
+block ttempl:
+  const
+    tabs = [["home", "index"],
+            ["news", "news"],
+            ["documentation", "documentation"],
+            ["download", "download"],
+            ["FAQ", "question"],
+            ["links", "links"]]
+
+
+  var i = 0
+  for item in items(tabs):
+    var content = $i
+    var file: File
+    if open(file, changeFileExt(item[1], "html"), fmWrite):
+      write(file, sunsetTemplate(current=item[1], ticker="", content=content,
+                                  tabs=tabs))
+      close(file)
+    else:
+      write(stdout, "cannot open file for writing")
+    inc(i)
+
+
+
+block ttempl4:
+  template `:=`(name, val: untyped) =
+    var name = val
+
+  ha := 1 * 4
+  hu := "ta-da" == "ta-da"
+  echo ha, hu
+
+
+
+
+import mtempl5
+block ttempl5:
+  echo templ()
+
+  #bug #892
+  proc parse_to_close(value: string, index: int, open='(', close=')'): int =
+      discard
+
+  # Call parse_to_close
+  template get_next_ident =
+      discard "{something}".parse_to_close(0, open = '{', close = '}')
+
+  get_next_ident()
+
+  #identifier expected, but found '(open|open|open)'
+  #bug #880 (also example in the manual!)
+  template typedef(name: untyped, typ: typedesc) =
+    type
+      `T name` {.inject.} = typ
+      `P name` {.inject.} = ref `T name`
+
+  typedef(myint, int)
+  var x: PMyInt
+
+
+
+block templreturntype:
+  template `=~` (a: int, b: int): bool = false
+  var foo = 2 =~ 3
+
+# bug #7117
+template parse9(body: untyped): untyped =
+
+  template val9(arg: string): int {.inject.} =
+    var b: bool
+    if b: 10
+    else: 20
+
+  body
+
+parse9:
+  echo val9("1")
+
+
+block gensym1:
+  template x: untyped = -1
+  template t1() =
+    template x: untyped {.gensym, redefine.} = 1
+    echo x()  # 1
+  template t2() =
+    template x: untyped {.redefine.} = 1  # defaults to {.inject.}
+    echo x()  # -1  injected x not available during template definition
+  t1()
+  t2()
+
+block gensym2:
+  let x,y,z = -1
+  template `!`(xx,yy: typed): untyped =
+    template x: untyped {.gensym.} = xx
+    template y: untyped {.gensym.} = yy
+    let z = x + x + y
+    z
+  var
+    a = 1
+    b = 2
+    c = 3
+    d = 4
+    e = 5
+  echo a ! b
+  echo a ! b ! c
+  echo a ! b ! c ! d
+  echo a ! b ! c ! d ! e
+  echo x,y,z
+
+block gensym3:
+  macro liftStmts(body: untyped): auto =
+    # convert
+    #   template x: untyped {.gensym.} =
+    #     let z = a + a + b
+    #     echo z
+    #     z
+    # to
+    #   let z = a + a + b
+    #   echo z
+    #   template x: untyped {.gensym.} =
+    #     z
+    #echo body.repr
+    body.expectKind nnkStmtList
+    result = newNimNode nnkStmtList
+    for s in body:
+      s.expectKind nnkTemplateDef
+      var sle = s[6]
+      while sle.kind == nnkStmtList:
+        doAssert(sle.len==1)
+        sle = sle[0]
+      if sle.kind == nnkStmtListExpr:
+        let n = sle.len
+        for i in 0..(n-2):
+          result.add sle[i]
+        var td = newNimNode nnkTemplateDef
+        for i in 0..5:
+          td.add s[i]
+        td.add sle[n-1]
+        result.add td
+      else:
+        result.add s
+    #echo result.repr
+  let x,y,z = -1
+  template `!`(xx,yy: typed): untyped =
+    liftStmts:
+      template x: untyped {.gensym.} = xx
+      template y: untyped {.gensym.} = yy
+    let z = x + x + y
+    echo "  ", z
+    z
+  var
+    a = 1
+    b = 2
+    c = 3
+    d = 4
+    e = 5
+  echo a ! b
+  echo a ! b ! c
+  echo a ! b ! c ! d
+  echo a ! b ! c ! d ! e
+  echo x,y,z
+
+block: # issue #2465
+  template t() =
+    template declX(str: string) {.gensym.} =
+      var x {.inject.} : string = str
+
+  t()
+  doAssert not declared(declX)
+  doAssert not compiles(declX("a string"))
+
+  template t2() =
+    template fooGensym() {.gensym.} =
+      echo 42
+
+  t2()
+  doAssert not declared(fooGensym)
+  doAssert not compiles(fooGensym())
+
+
+block identifier_construction_with_overridden_symbol:
+  # could use add, but wanna make sure it's an override no matter what
+  func examplefn = discard
+  func examplefn(x: int) = discard
+
+  # the function our template wants to use
+  func examplefn1 = discard
+
+  template exampletempl(n) =
+    # attempt to build a name using the overridden symbol "examplefn"
+    `examplefn n`()
+
+  exampletempl(1)
+
+import typetraits
+
+block: # issue #4596
+  type
+    T0 = object
+    T1 = object
+
+  template printFuncsT() =
+    proc getV[A](a: typedesc[A]): string =
+      var s {. global .} = name(A)
+      return s
+
+  printFuncsT()
+
+  doAssert getV(T1) == "T1"
+  doAssert getV(T0) == "T0"
+  doAssert getV(T0) == "T0"
+  doAssert getV(T1) == "T1"
diff --git a/tests/template/texponential_eval.nim b/tests/template/texponential_eval.nim
index 32af9e8f7..b4e3faefb 100644
--- a/tests/template/texponential_eval.nim
+++ b/tests/template/texponential_eval.nim
@@ -1,13 +1,17 @@
 # bug #1940
 
 discard """
-  nimout: '''===
+nimout: '''
+===
 merge (A) with (B)
 merge (A B) with (C)
 merge (A B C) with (D)
 merge (A B C D) with (E)
 merge (A B C D E) with (F)
-==='''
+===
+'''
+
+output: "A B C D E F"
 """
 
 type SqlStmt = tuple
diff --git a/tests/template/tgenericparam.nim b/tests/template/tgenericparam.nim
new file mode 100644
index 000000000..becf75d36
--- /dev/null
+++ b/tests/template/tgenericparam.nim
@@ -0,0 +1,93 @@
+block: # basic template generic parameter substitution
+  block: # issue #13527
+    template typeNameTempl[T](a: T): string = $T
+    proc typeNameProc[T](a: T): string = $T
+    doAssert typeNameTempl(1) == typeNameProc(1)
+    doAssert typeNameTempl(true) == typeNameProc(true)
+    doAssert typeNameTempl(1.0) == typeNameProc(1.0)
+    doAssert typeNameTempl(1u8) == typeNameProc(1u8)
+
+    template isDefault[T](a: T): bool = a == default(T)
+    doAssert isDefault(0.0)
+
+  block: # issue #17240
+    func to(c: int, t: typedesc[float]): t = discard
+    template converted[I, T](i: seq[I], t: typedesc[T]): seq[T] =
+      var result = newSeq[T](2)
+      result[0] = i[0].to(T)
+      result
+    doAssert newSeq[int](3).converted(float) == @[0.0, 0.0]
+
+  block: # issue #6340
+    type A[T] = object
+      v: T
+    proc foo(x: int): string = "int"
+    proc foo(x: typedesc[int]): string = "typedesc[int]"
+    template fooT(x: int): string = "int"
+    template fooT(x: typedesc[int]): string = "typedesc[int]"
+    proc foo[T](x: A[T]): (string, string) =
+      (foo(T), fooT(T))
+    template fooT[T](x: A[T]): (string, string) =
+      (foo(T), fooT(T))
+    var x: A[int]
+    doAssert foo(x) == fooT(x)
+
+  block: # issue #20033
+    template run[T](): T = default(T)
+    doAssert run[int]() == 0
+
+import options, tables, typetraits
+
+block: # complex cases of above with imports
+  block: # issue #19576, complex case
+    type RegistryKey = object
+      key, val: string
+    var regKey = @[RegistryKey(key: "abc", val: "def")]
+    template findFirst[T](s: seq[T], pred: proc(x: T): bool): Option[T] =
+      var res = none(T) # important line
+      for x in s:
+        if pred(x):
+          res = some(x)
+          break
+      res
+    proc getval(searchKey: string): Option[string] =
+      let found = regKey.findFirst(proc (rk: RegistryKey): bool = rk.key == searchKey)
+      if found.isNone: none(string)
+      else: some(found.get().val)
+    doAssert getval("strange") == none(string)
+    doAssert getval("abc") == some("def")
+  block: # issue #19076
+    block: # case 1
+      var tested: Table[string,int]
+      template `[]`[V](t:Table[string,V],key:string):untyped =
+        $V
+      doAssert tested["abc"] == "int"
+      template `{}`[V](t:Table[string,V],key:string):untyped =
+        ($V, tables.`[]`(t, key))
+      doAssert (try: tested{"abc"} except KeyError: ("not there", 123)) == ("not there", 123)
+      tables.`[]=`(tested, "abc", 456)
+      doAssert tested["abc"] == "int"
+      doAssert tested{"abc"} == ("int", 456)
+    block: # case 2
+      type Foo[A,T] = object
+        t:T
+      proc init[A,T](f:type Foo,a:typedesc[A],t:T):Foo[A,T] = Foo[A,T](t:t)
+      template fromOption[A](o:Option[A]):auto =
+        when o.isSome:
+          Foo.init(A,35)
+        else:
+          Foo.init(A,"hi")
+      let op = fromOption(some(5))
+  block: # issue #7461
+    template p[T](): untyped = none(T)
+    doAssert p[int]() == none(int)
+  block: # issue #7995
+    var res: string
+    template copyRange[T](dest: seq[T], destOffset: int) =
+      when supportsCopyMem(T):
+        res = "A"
+      else:    
+        res = "B"
+    var a = @[1, 2, 3]
+    copyRange(a, 0)
+    doAssert res == "A"
diff --git a/tests/template/tgenerictemplates.nim b/tests/template/tgenerictemplates.nim
deleted file mode 100644
index 142505b1a..000000000
--- a/tests/template/tgenerictemplates.nim
+++ /dev/null
@@ -1,37 +0,0 @@
-type
-  SomeObj = object of RootObj
-
-  Foo[T, U] = object
-    x: T
-    y: U
-
-template someTemplate[T](): tuple[id: int32, obj: T] =
-  var result: tuple[id: int32, obj: T] = (0'i32, T())
-  result
-
-let ret = someTemplate[SomeObj]()
-
-# https://github.com/nim-lang/Nim/issues/7829
-proc inner*[T](): int =
-  discard
-
-template outer*[A](): untyped =
-  inner[A]()
-
-template outer*[B](x: int): untyped =
-  inner[B]()
-
-var i1 = outer[int]()
-var i2 = outer[int](i1)
-
-# https://github.com/nim-lang/Nim/issues/7883
-template t1[T: int|int64](s: string): T =
-   var t: T
-   t
-
-template t1[T: int|int64](x: int, s: string): T =
-   var t: T
-   t
-
-var i3: int = t1[int]("xx")
-
diff --git a/tests/template/tgensym_generic_cross_module.nim b/tests/template/tgensym_generic_cross_module.nim
deleted file mode 100644
index 856ab676d..000000000
--- a/tests/template/tgensym_generic_cross_module.nim
+++ /dev/null
@@ -1,14 +0,0 @@
-discard """
-  output: '''foo55
-foo8.0
-fooaha
-bar7'''
-"""
-# bug #5419
-import mgensym_generic_cross_module
-
-foo(55)
-foo 8.0
-foo "aha"
-bar 7
-
diff --git a/tests/template/tgensym_instantiationinfo.nim b/tests/template/tgensym_instantiationinfo.nim
new file mode 100644
index 000000000..4b997ed6a
--- /dev/null
+++ b/tests/template/tgensym_instantiationinfo.nim
@@ -0,0 +1,24 @@
+discard """
+  action: "compile"
+"""
+
+# bug #7937
+
+template printError(error: typed) =
+  # Error: inconsistent typing for reintroduced symbol 'instInfo': previous type was: tuple[filename: string, line: int, column: int]; new type is: (string, int, int)
+  let instInfo {.gensym.} = instantiationInfo()
+  echo "Error at ", instInfo.filename, ':', instInfo.line, ": ", error
+
+# Removing this overload fixes the error
+template someTemplate(someBool: bool, body) =
+  discard
+
+template someTemplate(body) =
+  body
+
+proc main() =
+  someTemplate:
+    printError("ERROR 1")
+    printError("ERROR 2")
+
+main()
diff --git a/tests/template/tgensym_label.nim b/tests/template/tgensym_label.nim
deleted file mode 100644
index fd3b0a1ee..000000000
--- a/tests/template/tgensym_label.nim
+++ /dev/null
@@ -1,18 +0,0 @@
-
-# bug #5417
-import macros
-
-macro genBody: untyped =
-  let sbx = genSym(nskLabel, "test")
-  when true:
-    result = quote do:
-      block `sbx`:
-        break `sbx`
-  else:
-    template foo(s1, s2) =
-      block s1:
-        break s2
-    result = getAst foo(sbx, sbx)
-
-proc test() =
-  genBody()
diff --git a/tests/template/tgensymhijack.nim b/tests/template/tgensymhijack.nim
new file mode 100644
index 000000000..72ff3d495
--- /dev/null
+++ b/tests/template/tgensymhijack.nim
@@ -0,0 +1,37 @@
+# issue #23326
+
+type Result*[E] = object
+  e*: E
+
+proc error*[E](v: Result[E]): E = discard
+
+template valueOr*[E](self: Result[E], def: untyped): int =
+  when E isnot void:
+    when false:
+      # Comment line below to make it work
+      template error(): E {.used, gensym.} = s.e
+      discard
+    else:
+      template error(): E {.used, inject.} =
+        self.e
+
+      def
+  else:
+    def
+
+
+block:
+  let rErr = Result[string](e: "a")
+  let rErrV = rErr.valueOr:
+    ord(error[0])
+
+block:
+  template foo(x: static bool): untyped =
+    when x:
+      let a = 123
+    else:
+      template a: untyped {.gensym.} = 456
+    a
+  
+  doAssert foo(false) == 456
+  doAssert foo(true) == 123
diff --git a/tests/template/tgensymregression.nim b/tests/template/tgensymregression.nim
index 4cc64a831..2a5dca934 100644
--- a/tests/template/tgensymregression.nim
+++ b/tests/template/tgensymregression.nim
@@ -1,8 +1,6 @@
 discard """
   output: '''[0.0, 0.0, 0.0]
-
 [0.0, 0.0, 0.0, 0.0]
-
 5050
 123'''
 """
@@ -15,7 +13,7 @@ template mathPerComponent(op: untyped): untyped =
 mathPerComponent(`***`)
 # bug #5285
 when true:
-  if isMainModule:
+  if true:
     var v1: array[3, float64]
     var v2: array[3, float64]
     echo repr(v1 *** v2)
@@ -57,7 +55,7 @@ converter int2string(x: int): string = $x
 template wrap(body: typed): untyped =
   body
 
-macro makeProc(): typed =
+macro makeProc() =
   # Make a template tree
   result = quote do:
     proc someProc* =
@@ -70,3 +68,21 @@ macro makeProc(): typed =
 makeProc()
 
 someProc()
+
+# bug #12193
+import macros, strutils
+
+macro gen(T: typedesc): untyped =
+  let typeSym = getTypeImpl(T)[1]
+  let param = genSym(nskParam, "s")
+  let value = nnkBracketExpr.newTree(param, newIntLitNode(0))
+  result = newProc(
+    name = ident"pack",
+    params = [typeSym,
+      newIdentDefs(param, nnkBracketExpr.newTree(ident"seq", ident"string"))],
+    body = newStmtList(newCall(typeSym, newCall(bindSym"parseInt", value))),
+    procType = nnkTemplateDef)
+  echo repr result
+
+gen(int)
+let i = pack(@["2"])
diff --git a/tests/template/tgetast_typeliar.nim b/tests/template/tgetast_typeliar.nim
deleted file mode 100644
index c9a612582..000000000
--- a/tests/template/tgetast_typeliar.nim
+++ /dev/null
@@ -1,23 +0,0 @@
-
-# just ensure this keeps compiling:
-
-import macros
-
-proc error(s: string) = quit s
-
-macro assertOrReturn*(condition: bool; message: string): typed =
-  var line = condition.lineInfo()
-  result = quote do:
-    block:
-      if not likely(`condition`):
-        error("Assertion failed: " & $(`message`) & "\n" & `line`)
-        return
-
-macro assertOrReturn*(condition: bool): typed =
-  var message = condition.toStrLit()
-  result = getAst assertOrReturn(condition, message)
-
-proc point*(size: int16): tuple[x, y: int16] =
-  # returns random point in square area with given `size`
-
-  assertOrReturn size > 0
diff --git a/tests/template/thygienictempl.nim b/tests/template/thygienictempl.nim
index de40450aa..9020c3e28 100644
--- a/tests/template/thygienictempl.nim
+++ b/tests/template/thygienictempl.nim
@@ -1,3 +1,7 @@
+discard """
+action: compile
+"""
+
 
 var
   e = "abc"
@@ -14,5 +18,5 @@ template test_in(a, b, c: untyped): bool {.dirty.} =
   var result {.gensym.}: bool = false
   false
 
-when isMainModule:
+when true:
   assert test_in(ret2, "test", str_val)
diff --git a/tests/template/tidentconcatenations.nim b/tests/template/tidentconcatenations.nim
new file mode 100644
index 000000000..ddd2e49cc
--- /dev/null
+++ b/tests/template/tidentconcatenations.nim
@@ -0,0 +1,31 @@
+type
+  Hash*[bits: static[int]] = object
+    data*: array[bits div 8, uint8]
+
+{.emit: """
+
+void sha_256(void* input, int input_len, void* output, int output_len) {}
+void sha_512(void* input, int input_len, void* output, int output_len) {}
+
+void keccak_256(void* input, int input_len, void* output, int output_len) {}
+void keccak_512(void* input, int input_len, void* output, int output_len) {}
+
+""".}
+
+template defineKeccak(bits: untyped) =
+  proc `extKeccak bits`(output: pointer, outSize: csize_t, input: pointer, inputSize: csize_t) {.nodecl, importc: "keccak_" & astToStr(bits).}
+
+template defineSha(bits: static[int]) =
+  proc `extSha bits`(output: pointer, outSize: csize_t, input: pointer, inputSize: csize_t) {.nodecl, importc: "sha_" & astToStr(bits).}
+
+template defineHashProcs(bits) =
+  defineSha(bits)
+  defineKeccak(bits)
+
+defineHashProcs(256)
+defineHashProcs(512)
+
+extSha256(nil, 0, nil, 0)
+extSha512(nil, 0, nil, 0)
+extKeccak256(nil, 0, nil, 0)
+extKeccak512(nil, 0, nil, 0)
diff --git a/tests/template/tinnerouterproc.nim b/tests/template/tinnerouterproc.nim
new file mode 100644
index 000000000..56e0d02df
--- /dev/null
+++ b/tests/template/tinnerouterproc.nim
@@ -0,0 +1,20 @@
+block: # #20002
+  proc bar(x: int): int = 10
+  template foo =
+    proc bar(x: int): int {.gensym.} = x + 2
+    doAssert bar(3) == 5
+    discard 3.bar # evaluates to 10 but only check if it compiles for now
+  block:
+    foo()
+
+block: # issue #23813
+  template r(body: untyped) =
+    proc x() {.gensym.} =
+      body
+  template g() =
+    r:
+      let y = 0
+    r:
+      proc y() = discard
+      y()
+  g()
diff --git a/tests/template/tissue909.nim b/tests/template/tissue909.nim
deleted file mode 100644
index 6786ff48c..000000000
--- a/tests/template/tissue909.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-import macros
-
-template baz() =
-  proc bar() =
-    var x = 5
-    iterator foo(): int {.closure.} =
-      echo x
-    var y = foo
-    discard y()
-
-macro test(): untyped =
-  result = getAst(baz())
-  echo(treeRepr(result))
-
-test()
-bar()
diff --git a/tests/template/tissue993.nim b/tests/template/tissue993.nim
deleted file mode 100644
index 552890bb4..000000000
--- a/tests/template/tissue993.nim
+++ /dev/null
@@ -1,20 +0,0 @@
-
-type PNode* = ref object of RootObj
-
-template litNode(name, ty)  =
-  type name* = ref object of PNode
-    val*: ty
-litNode PIntNode, int
-
-import json
-
-template withKey*(j: JsonNode; key: string; varname,
-                  body: untyped): typed =
-  if j.hasKey(key):
-    let varname{.inject.}= j[key]
-    block:
-      body
-
-var j = parsejson("{\"zzz\":1}")
-withkey(j, "foo", x):
-  echo(x)
diff --git a/tests/template/tit.nim b/tests/template/tit.nim
deleted file mode 100644
index 76b1d151b..000000000
--- a/tests/template/tit.nim
+++ /dev/null
@@ -1,11 +0,0 @@
-
-# bug #1337
-
-template someIt(a, pred): untyped =
-  var it {.inject.} = 0
-  pred
-
-proc aProc(n: auto) =
-  n.someIt(echo(it))
-
-aProc(89)
diff --git a/tests/template/tlt.nim b/tests/template/tlt.nim
deleted file mode 100644
index 75c7dd991..000000000
--- a/tests/template/tlt.nim
+++ /dev/null
@@ -1,7 +0,0 @@
-
-import mlt
-# bug #4564
-type Bar* = ref object of RootObj
-proc foo(a: Bar): int = 0
-var a: Bar
-let b = a.foo() > 0
diff --git a/tests/template/tmethodcall.nim b/tests/template/tmethodcall.nim
new file mode 100644
index 000000000..d209443c8
--- /dev/null
+++ b/tests/template/tmethodcall.nim
@@ -0,0 +1,24 @@
+# bug #5909
+type
+  Vec2[T] = tuple
+    x,y: T
+  Vec2f = Vec2[float32]
+
+proc vec2f(x,y: float): Vec2f =
+  result.x = x
+  result.y = y
+
+proc `-`[T](a,b: Vec2[T]): Vec2[T] =
+  result.x = a.x - b.x
+  result.y = a.y - b.y
+
+proc foo[T](a: Vec2[T]): Vec2[T] =
+  result = a
+
+block:
+ # this being called foo is a problem when calling .foo()
+  var foo = true
+
+  let a = vec2f(1.0,0.0)
+  let b = vec2f(3.0,1.0)
+  let c = (a - b).foo() # breaks
diff --git a/tests/template/tmodulealias.nim b/tests/template/tmodulealias.nim
index 6681379fb..79b5ec9c6 100644
--- a/tests/template/tmodulealias.nim
+++ b/tests/template/tmodulealias.nim
@@ -7,7 +7,7 @@ when defined(windows):
 else:
   import posix
 
-when defined(Windows):
+when defined(windows):
   template orig: expr =
     winlean
 else:
diff --git a/tests/template/tmore_regressions.nim b/tests/template/tmore_regressions.nim
new file mode 100644
index 000000000..8b4b5fa4c
--- /dev/null
+++ b/tests/template/tmore_regressions.nim
@@ -0,0 +1,44 @@
+discard """
+output: '''0
+
+0.0'''
+"""
+
+# bug #11494
+import macros
+
+macro staticForEach(arr: untyped, body: untyped): untyped =
+    result = newNimNode(nnkStmtList)
+
+    arr.expectKind(nnkBracket)
+    for n in arr:
+        let b = copyNimTree(body)
+        result.add quote do:
+            block:
+                type it {.inject.} = `n`
+                `b`
+
+template forEveryMatchingEntity*() =
+    staticForEach([int, string, float]):
+        var a: it
+        echo a
+
+forEveryMatchingEntity()
+
+
+# bug #11483
+proc main =
+  template first(body) =
+    template second: var int =
+      var o: int
+      var i  = addr(o)
+      i[]
+
+    body
+
+  first:
+    second = 5
+    second = 6
+
+main()
+
diff --git a/tests/template/tnested.nim b/tests/template/tnested.nim
new file mode 100644
index 000000000..81e416a76
--- /dev/null
+++ b/tests/template/tnested.nim
@@ -0,0 +1,38 @@
+block: # issue #22775
+  proc h(c: int) = discard
+  template k(v: int) =
+    template p() = v.h()
+    p()
+  let a = @[0]
+  k(0 and not a[0])
+
+block: # issue #22775 case 2
+  proc h(c: int, q: int) = discard
+  template k(v: int) =
+    template p() = h(v, v)
+    p()
+  let a = [0]
+  k(0 and not a[0])
+
+block: # issue #22775 minimal cases
+  proc h(c: int) = discard
+  template k(v: int) =
+    template p() = h(v)
+    p()
+  let a = [0]
+  k(not a[0])
+  block:
+    k(-a[0])
+  block:
+    proc f(x: int): int = x
+    k(f a[0])
+
+block: # bracket assignment case of above tests
+  proc h(c: int) = discard
+  template k(v: int) =
+    template p() = h(v)
+    p()
+  var a = [0]
+  k(not (block:
+    a[0] = 1
+    1))
diff --git a/tests/template/tobjectdeclfield.nim b/tests/template/tobjectdeclfield.nim
new file mode 100644
index 000000000..afce2cae8
--- /dev/null
+++ b/tests/template/tobjectdeclfield.nim
@@ -0,0 +1,21 @@
+block: # issue #16005
+  var x = 0
+
+  block:
+    type Foo = object
+      x: float # ok
+
+  template main() =
+    block:
+      type Foo = object
+        x: float # Error: cannot use symbol of kind 'var' as a 'field'
+
+  main()
+
+block: # issue #19552
+  template test =
+    type
+      test2 = ref object
+        reset: int
+
+  test()
diff --git a/tests/template/topensym.nim b/tests/template/topensym.nim
new file mode 100644
index 000000000..2f930407b
--- /dev/null
+++ b/tests/template/topensym.nim
@@ -0,0 +1,209 @@
+{.experimental: "openSym".}
+
+block: # issue #24002
+  type Result[T, E] = object
+  func value[T, E](self: Result[T, E]): T {.inline.} =
+    discard
+  func value[T: not void, E](self: var Result[T, E]): var T {.inline.} =
+    discard
+  template unrecognizedFieldWarning =
+    doAssert value == 123
+    let x = value
+    doAssert value == x
+  proc readValue(value: var int) =
+    unrecognizedFieldWarning()
+  var foo: int = 123
+  readValue(foo)
+
+block: # issue #22605 for templates, normal call syntax
+  const error = "bad"
+
+  template valueOr(self: int, def: untyped): untyped =
+    case false
+    of true: ""
+    of false:
+      template error: untyped {.used, inject.} = "good"
+      def
+
+  template g(T: type): string =
+    var res = "ok"
+    let x = valueOr 123:
+      res = $error
+      "dummy"
+    res
+
+  doAssert g(int) == "good"
+
+  template g2(T: type): string =
+    bind error # use the bad version on purpose
+    var res = "ok"
+    let x = valueOr 123:
+      res = $error
+      "dummy"
+    res
+
+  doAssert g2(int) == "bad"
+
+block: # issue #22605 for templates, method call syntax
+  const error = "bad"
+
+  template valueOr(self: int, def: untyped): untyped =
+    case false
+    of true: ""
+    of false:
+      template error: untyped {.used, inject.} = "good"
+      def
+
+  template g(T: type): string =
+    var res = "ok"
+    let x = 123.valueOr:
+      res = $error
+      "dummy"
+    res
+
+  doAssert g(int) == "good"
+
+  template g2(T: type): string =
+    bind error # use the bad version on purpose
+    var res = "ok"
+    let x = 123.valueOr:
+      res = $error
+      "dummy"
+    res
+
+  doAssert g2(int) == "bad"
+
+block: # issue #22605 for templates, original complex example
+  type Xxx = enum
+    error
+    value
+
+  type
+    Result[T, E] = object
+      when T is void:
+        when E is void:
+          oResultPrivate*: bool
+        else:
+          case oResultPrivate*: bool
+          of false:
+            eResultPrivate*: E
+          of true:
+            discard
+      else:
+        when E is void:
+          case oResultPrivate*: bool
+          of false:
+            discard
+          of true:
+            vResultPrivate*: T
+        else:
+          case oResultPrivate*: bool
+          of false:
+            eResultPrivate*: E
+          of true:
+            vResultPrivate*: T
+
+  template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped =
+    let s = (self) # TODO avoid copy
+    case s.oResultPrivate
+    of true:
+      s.vResultPrivate
+    of false:
+      when E isnot void:
+        template error: untyped {.used, inject.} = s.eResultPrivate
+      def
+
+  proc f(): Result[int, cstring] =
+    Result[int, cstring](oResultPrivate: false, eResultPrivate: "f")
+
+  template g(T: type): string =
+    var res = "ok"
+    let x = f().valueOr:
+      res = $error
+      123
+    res
+
+  doAssert g(int) == "f"
+
+  template g2(T: type): string =
+    bind error # use the bad version on purpose
+    var res = "ok"
+    let x = f().valueOr:
+      res = $error
+      123
+    res
+
+  doAssert g2(int) == "error"
+
+block: # issue #23865 for templates
+  type Xxx = enum
+    error
+    value
+
+  type
+    Result[T, E] = object
+      when T is void:
+        when E is void:
+          oResultPrivate: bool
+        else:
+          case oResultPrivate: bool
+          of false:
+            eResultPrivate: E
+          of true:
+            discard
+      else:
+        when E is void:
+          case oResultPrivate: bool
+          of false:
+            discard
+          of true:
+            vResultPrivate: T
+        else:
+          case oResultPrivate: bool
+          of false:
+            eResultPrivate: E
+          of true:
+            vResultPrivate: T
+
+  func error[T, E](self: Result[T, E]): E =
+    ## Fetch error of result if set, or raise Defect
+    case self.oResultPrivate
+    of true:
+      when T isnot void:
+        raiseResultDefect("Trying to access error when value is set", self.vResultPrivate)
+      else:
+        raiseResultDefect("Trying to access error when value is set")
+    of false:
+      when E isnot void:
+        self.eResultPrivate
+
+  template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped =
+    let s = (self) # TODO avoid copy
+    case s.oResultPrivate
+    of true:
+      s.vResultPrivate
+    of false:
+      when E isnot void:
+        template error: untyped {.used, inject.} = s.eResultPrivate
+      def
+  proc f(): Result[int, cstring] =
+    Result[int, cstring](oResultPrivate: false, eResultPrivate: "f")
+  template g(T: type): string =
+    var res = "ok"
+    let x = f().valueOr:
+      res = $error
+      123
+    res
+  doAssert g(int) == "f"
+
+import std/sequtils
+
+block: # issue #15314
+  var it: string
+  var nums = @[1,2,3]
+
+  template doubleNums() =
+    nums.applyIt(it * 2)
+
+  doubleNums()
+  doAssert nums == @[2, 4, 6]
diff --git a/tests/template/topensymoverride.nim b/tests/template/topensymoverride.nim
new file mode 100644
index 000000000..3d4bb59f1
--- /dev/null
+++ b/tests/template/topensymoverride.nim
@@ -0,0 +1,39 @@
+discard """
+  matrix: "--skipParentCfg --filenames:legacyRelProj"
+"""
+
+const value = "captured"
+template fooOld(x: int, body: untyped): untyped =
+  let value {.inject.} = "injected"
+  body
+template foo(x: int, body: untyped): untyped =
+  let value {.inject.} = "injected"
+  {.push experimental: "genericsOpenSym".}
+  body
+  {.pop.}
+
+proc old[T](): string =
+  fooOld(123):
+    return value
+doAssert old[int]() == "captured"
+
+template oldTempl(): string =
+  block:
+    var res: string
+    fooOld(123):
+      res = value
+    res
+doAssert oldTempl() == "captured"
+
+proc bar[T](): string =
+  foo(123):
+    return value
+doAssert bar[int]() == "injected"
+
+template barTempl(): string =
+  block:
+    var res: string
+    foo(123):
+      res = value
+    res
+doAssert barTempl() == "injected"
diff --git a/tests/template/topensymwarning.nim b/tests/template/topensymwarning.nim
new file mode 100644
index 000000000..0bbe0a9fb
--- /dev/null
+++ b/tests/template/topensymwarning.nim
@@ -0,0 +1,60 @@
+discard """
+  matrix: "--skipParentCfg --filenames:legacyRelProj"
+"""
+
+type Xxx = enum
+  error
+  value
+
+type
+  Result[T, E] = object
+    when T is void:
+      when E is void:
+        oResultPrivate*: bool
+      else:
+        case oResultPrivate*: bool
+        of false:
+          eResultPrivate*: E
+        of true:
+          discard
+    else:
+      when E is void:
+        case oResultPrivate*: bool
+        of false:
+          discard
+        of true:
+          vResultPrivate*: T
+      else:
+        case oResultPrivate*: bool
+        of false:
+          eResultPrivate*: E
+        of true:
+          vResultPrivate*: T
+
+template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped =
+  let s = (self) # TODO avoid copy
+  case s.oResultPrivate
+  of true:
+    s.vResultPrivate
+  of false:
+    when E isnot void:
+      template error: untyped {.used, inject.} = s.eResultPrivate
+    def
+
+proc f(): Result[int, cstring] =
+  Result[int, cstring](oResultPrivate: false, eResultPrivate: "f")
+
+template g(T: type): string =
+  var res = "ok"
+  let x = f().valueOr:
+    {.push warningAsError[IgnoredSymbolInjection]: on.}
+    # test spurious error
+    discard true
+    let _ = f
+    {.pop.}
+    res = $error #[tt.Warning
+           ^ a new symbol 'error' has been injected during template or generic instantiation, however 'error' [enumField declared in topensymwarning.nim(6, 3)] captured at the proc declaration will be used instead; either enable --experimental:openSym to use the injected symbol, or `bind` this captured symbol explicitly [IgnoredSymbolInjection]]#
+    123
+  res
+
+discard g(int)
diff --git a/tests/template/tparams_gensymed.nim b/tests/template/tparams_gensymed.nim
index 3fb0dd4a5..b559c2d9e 100644
--- a/tests/template/tparams_gensymed.nim
+++ b/tests/template/tparams_gensymed.nim
@@ -1,4 +1,24 @@
-
+discard """
+output: '''
+0
+1
+2
+3
+0
+1
+2
+3
+wth
+3
+2
+1
+0
+(total: 6)
+S1
+5
+abc
+'''
+"""
 # bug #1915
 
 import macros
@@ -53,9 +73,333 @@ proc concreteProc(x: int) =
   forStatic i, 0..3:
     echo i
 
-proc genericProc(x: any) =
+proc genericProc(x: auto) =
   forStatic i, 0..3:
     echo i
 
 concreteProc(7) # This works
 genericProc(7)  # This doesn't compile
+
+import tables
+
+# bug #9476
+proc getTypeInfo*(T: typedesc): pointer =
+  var dummy: T
+  getTypeInfo(dummy)
+
+
+macro implementUnary(op: untyped): untyped =
+  result = newStmtList()
+
+  template defineTable(tableSymbol) =
+    var tableSymbol = initTable[pointer, pointer]()
+  let tableSymbol = genSym(nskVar, "registeredProcs")
+  result.add(getAst(defineTable(tableSymbol)))
+
+  template defineRegisterInstantiation(tableSym, regTemplSym, instSym, op) =
+    template regTemplSym*(T: typedesc) =
+      let ti = getTypeInfo(T)
+
+      proc instSym(xOrig: int): int {.gensym, cdecl.} =
+        let x {.inject.} = xOrig
+        op
+
+      tableSym[ti] = cast[pointer](instSym)
+
+  let regTemplSymbol = ident("registerInstantiation")
+  let instSymbol = ident("instantiation")
+  result.add(getAst(defineRegisterInstantiation(
+    tableSymbol, regTemplSymbol, instSymbol, op
+  )))
+
+  echo result.repr
+
+
+implementUnary(): x*x
+
+registerInstantiation(int)
+registerInstantiation(float)
+
+# bug #10192
+template nest(body) {.dirty.} =
+  template p1(b1: untyped) {.dirty, used.} =
+    template implp1: untyped {.dirty.} = b1
+  template p2(b2: untyped) {.dirty, used.} =
+    template implp2: untyped {.dirty.} = b2
+
+  body
+  implp1
+  implp2
+
+template test() =
+  nest:
+    p1:
+      var foo = "bar"
+    p2:
+      doAssert(foo.len == 3)
+
+test()
+
+# regression found in PMunch's parser generator
+
+proc namedcall(arg: string) =
+  discard
+
+macro m(): untyped =
+  result = quote do:
+    (proc (arg: string) =
+      namedcall(arg = arg)
+      echo arg)
+
+let meh = m()
+meh("wth")
+
+
+macro foo(body: untyped): untyped =
+  result = body
+
+template baz(): untyped =
+  foo:
+    proc bar2(b: int): int =
+      echo b
+      if b > 0: b + bar2(b = b - 1)
+      else: 0
+  echo (total: bar2(3))
+
+baz()
+
+# bug #12121
+macro state_machine_enum(states: varargs[untyped]) =
+  result = nnkTypeSection.newTree(
+    nnkTypeDef.newTree(
+      nnkPragmaExpr.newTree(ident("State"), nnkPragma.newTree(ident("pure"))),
+      newEmptyNode(),
+      nnkEnumTy.newTree(newEmptyNode())
+    )
+  )
+
+  for s in states:
+    expectKind(s, nnkIdent)
+    result[0][2].add s
+
+template mystate_machine(body: untyped) =
+  state_machine_enum(S1, S2, S3)
+  var state_stack: seq[State]
+  template state_current(): State {.inject, used.} =
+    state_stack[^1]
+  template state_push(state_name) {.inject, used.} =
+    state_stack.add State.state_name
+  template state_pop(n = 1) {.inject, used.} =
+    state_stack.setLen(state_stack.len - n)
+  body
+
+mystate_machine:
+  state_push(S1)
+  echo state_current()
+  state_pop()
+
+# bug #15075
+block: #Doesn't work
+  template genGenTempl: untyped =
+    proc loop(locals: int)
+    proc loop(locals: int) = discard
+  genGenTempl()
+  let pool = loop
+
+block: #Doesn't work
+  macro genGenMacro: untyped =
+    quote do:
+      proc loop(locals: int)
+      proc loop(locals: int) = discard
+  genGenMacro()
+  let pool = loop
+
+block: #This works
+  proc loop(locals: int)
+  proc loop(locals: int) = discard
+  let pool = loop
+
+#Now somewhat recursive:
+type Cont = ref object of RootObj
+  fn*: proc(c: Cont): Cont {.nimcall.}
+
+block: #Doesn't work
+  template genGenTempl(): untyped =
+    proc loop(locals: Cont): Cont
+    proc loop(locals: Cont): Cont =
+      return Cont(fn: loop)
+    proc doServer(): Cont =
+      return Cont(fn: loop)
+  genGenTempl()
+  discard doServer()
+
+block: #Doesn't work
+  macro genGenMacro(): untyped =
+    quote:
+      proc loop(locals: Cont): Cont
+      proc loop(locals: Cont): Cont =
+        return Cont(fn: loop)
+      proc doServer(): Cont =
+        return Cont(fn: loop)
+  genGenMacro()
+  discard doServer()
+
+block: #This works
+  proc loop(locals: Cont): Cont
+  proc loop(locals: Cont): Cont =
+    return Cont(fn: loop)
+  proc doServer(): Cont =
+    return Cont(fn: loop)
+  discard doServer()
+
+#And fully recursive:
+block: #Doesn't work
+  template genGenTempl: untyped =
+    proc loop(locals: int)
+    proc loop(locals: int) = loop(locals)
+  genGenTempl()
+  let pool = loop
+
+block: #Doesn't work
+  macro genGenMacro: untyped =
+    quote do:
+      proc loop(locals: int)
+      proc loop(locals: int) = loop(locals)
+  genGenMacro()
+  let pool = loop
+
+block: #This works
+  proc loop(locals: int)
+  proc loop(locals: int) = loop(locals)
+  let pool = loop
+
+block:
+  template genAndCallLoop: untyped =
+    proc loop() {.gensym.}
+    proc loop() {.gensym.} =
+      discard
+    loop()
+  genAndCallLoop
+
+block: #Fully recursive and gensymmed:
+  template genGenTempl: untyped =
+    proc loop(locals: int) {.gensym.}
+    proc loop(locals: int) {.gensym.} = loop(locals)
+    let pool = loop
+  genGenTempl()
+
+block: #Make sure gensymmed symbol doesn't overwrite the forward decl
+  proc loop()
+  proc loop() = discard
+  template genAndCallLoop: untyped =
+    proc loop() {.gensym.} =
+      discard
+    loop()
+  genAndCallLoop()
+
+template genLoopDecl: untyped =
+  proc loop()
+template genLoopDef: untyped =
+  proc loop() = discard
+block:
+  genLoopDecl
+  genLoopDef
+  loop()
+block:
+  proc loop()
+  genLoopDef
+  loop()
+block:
+  genLoopDecl
+  proc loop() = discard
+  loop()
+
+block: #Gensymmed sym sharing forward decl
+  macro genGenMacro: untyped =
+    let sym = genSym(nskProc, "loop")
+    nnkStmtList.newTree(
+      newProc(sym, body = newEmptyNode()),
+      newCall(sym),
+      newProc(sym, body = newStmtList()),
+    )
+  genGenMacro
+
+# inject pragma on params
+
+template test(procname, body: untyped): untyped = 
+  proc procname(data {.inject.}: var int = 0) =
+    body
+
+test(hello):
+  echo data
+  data = 3
+
+var data = 5
+
+hello(data)
+
+# bug #5691
+
+template bar(x: typed) = discard
+macro barry(x: typed) = discard
+
+var a = 0
+
+bar:
+  var a = 10
+
+barry:
+  var a = 20
+
+bar:
+  var b = 10
+
+barry:
+  var b = 20
+
+var b = 30
+
+# template bar(x: static int) = discard
+#You may think that this should work:
+# bar((var c = 1; echo "hey"; c))
+# echo c
+#But it must not! Since this would be incorrect:
+# bar((var b = 3; const c = 1; echo "hey"; c))
+# echo b # <- b wouldn't exist
+
+discard not (let xx = 1; true)
+discard xx
+
+template barrel(a: typed): untyped = a
+
+barrel:
+  var aa* = 1
+  var bb = 3
+  export bb
+
+# Test declaredInScope within params
+template test1: untyped =
+  when not declaredInScope(thing):
+    var thing {.inject.}: int
+
+proc chunkedReadLoop =
+  test1
+  test1
+
+template test2: untyped =
+  when not not not declaredInScope(thing):
+    var thing {.inject.}: int
+
+proc chunkedReadLoop2 =
+  test2
+  test2
+
+test1(); test2()
+
+block: # bug #22846
+  template foo2(x: proc (y: string)) =
+    let f = x
+    f("abc")
+
+  foo2(proc (y: string) = echo y)
+
diff --git a/tests/template/tparamscope.nim b/tests/template/tparamscope.nim
new file mode 100644
index 000000000..177c682cf
--- /dev/null
+++ b/tests/template/tparamscope.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "undeclared identifier: 'a'"
+  line: 10
+"""
+
+
+template secondArg(a, b: typed): untyped =
+  b
+
+echo secondArg((var a = 1; 1), a)
diff --git a/tests/template/tpattern_with_converter.nim b/tests/template/tpattern_with_converter.nim
deleted file mode 100644
index e0632552b..000000000
--- a/tests/template/tpattern_with_converter.nim
+++ /dev/null
@@ -1,27 +0,0 @@
-discard """
-  output: 10.0
-"""
-
-type
-  MyFloat = object
-    val: float
-
-converter to_myfloat*(x: float): MyFloat {.inline.} =
-  MyFloat(val: x)
-
-proc `+`(x1, x2: MyFloat): MyFloat =
-  MyFloat(val: x1.val + x2.val)
-
-proc `*`(x1, x2: MyFloat): MyFloat =
-    MyFloat(val: x1.val * x2.val)
-
-template optMul{`*`(a, 2.0)}(a: MyFloat): MyFloat =
-  a + a
-
-func floatMyFloat(x: MyFloat): MyFloat =
-  result = x * 2.0
-
-func floatDouble(x: float): float =
-  result = x * 2.0
-
-echo floatDouble(5)
\ No newline at end of file
diff --git a/tests/template/tprefer_immediate.nim b/tests/template/tprefer_immediate.nim
deleted file mode 100644
index 3a4cfc07b..000000000
--- a/tests/template/tprefer_immediate.nim
+++ /dev/null
@@ -1,15 +0,0 @@
-discard """
-  output: '''immediate'''
-"""
-
-# Test that immediate templates are preferred over non-immediate templates
-
-template foo(a, b: untyped) = echo "foo expr"
-template foo(a, b: int) = echo "foo int"
-template foo(a, b: float) = echo "foo float"
-template foo(a, b: string) = echo "foo string"
-template foo(a, b: untyped) {.immediate.} = echo "immediate"
-template foo(a, b: bool) = echo "foo bool"
-template foo(a, b: char) = echo "foo char"
-
-foo(undeclaredIdentifier, undeclaredIdentifier2)
diff --git a/tests/template/tprocparshadow.nim b/tests/template/tprocparshadow.nim
deleted file mode 100644
index b99cd0b6c..000000000
--- a/tests/template/tprocparshadow.nim
+++ /dev/null
@@ -1,11 +0,0 @@
-discard """
-  output: "10"
-"""
-
-template something(name: untyped) =
-  proc name(x: int) =
-    var x = x # this one should not be rejected by the compiler (#5225)
-    echo x
-
-something(what)
-what(10)
diff --git a/tests/template/tqualifiedident.nim b/tests/template/tqualifiedident.nim
new file mode 100644
index 000000000..463b14ee7
--- /dev/null
+++ b/tests/template/tqualifiedident.nim
@@ -0,0 +1,8 @@
+block: # issue #19865
+  template f() = discard default(system.int)
+  f()
+
+# issue #21221, same as above
+type M = object
+template r() = discard default(tqualifiedident.M)
+r()
diff --git a/tests/template/tqualifiedtype.nim b/tests/template/tqualifiedtype.nim
new file mode 100644
index 000000000..6497af6ee
--- /dev/null
+++ b/tests/template/tqualifiedtype.nim
@@ -0,0 +1,25 @@
+# issue #19866
+
+# Switch module import order to switch which of last two
+# doAsserts fails
+import mqualifiedtype1
+import mqualifiedtype2
+
+# this isn't officially supported but needed to point out the issue:
+template f(moduleName: untyped): int = sizeof(`moduleName`.A)
+template g(someType:   untyped): int = sizeof(someType)
+
+# These are legitimately true.
+doAssert sizeof(mqualifiedtype1.A) != sizeof(mqualifiedtype2.A)
+doAssert g(mqualifiedtype1.A) != g(mqualifiedtype2.A)
+
+# Which means that this should not be true, but is in Nim 1.6
+doAssert f(`mqualifiedtype1`) != f(`mqualifiedtype2`)
+doAssert f(mqualifiedtype1) != f(mqualifiedtype2)
+
+# These should be true, but depending on import order, exactly one
+# fails in Nim 1.2, 1.6 and devel.
+doAssert f(`mqualifiedtype1`) == g(mqualifiedtype1.A)
+doAssert f(`mqualifiedtype2`) == g(mqualifiedtype2.A)
+doAssert f(mqualifiedtype1) == g(mqualifiedtype1.A)
+doAssert f(mqualifiedtype2) == g(mqualifiedtype2.A)
diff --git a/tests/template/tredefinition.nim b/tests/template/tredefinition.nim
new file mode 100644
index 000000000..8efc5ae2f
--- /dev/null
+++ b/tests/template/tredefinition.nim
@@ -0,0 +1,13 @@
+discard """
+  errormsg: "redefinition of 'a`gensym"
+  line: 9
+"""
+# bug #10180
+proc f() =
+  template t() =
+    var a = 1
+    var a = 2
+    echo a
+  t()
+
+f()
diff --git a/tests/template/tredefinition_override.nim b/tests/template/tredefinition_override.nim
new file mode 100644
index 000000000..7ae232bba
--- /dev/null
+++ b/tests/template/tredefinition_override.nim
@@ -0,0 +1,33 @@
+{.push warningAsError[ImplicitTemplateRedefinition]: on.}
+
+doAssert not (compiles do:
+  template foo(): int = 1
+  template foo(): int = 2)
+doAssert (compiles do:
+  template foo(): int = 1
+  template foo(): int {.redefine.} = 2)
+doAssert not (compiles do:
+  block:
+    template foo() =
+      template bar: string {.gensym.} = "a"
+      template bar: string {.gensym.} = "b"
+    foo())
+doAssert (compiles do:
+  block:
+    template foo() =
+      template bar: string {.gensym.} = "a"
+      template bar: string {.gensym, redefine.} = "b"
+    foo())
+
+block:
+  template foo(): int = 1
+  template foo(): int {.redefine.} = 2
+  doAssert foo() == 2
+block:
+  template foo(): string =
+    template bar: string {.gensym.} = "a"
+    template bar: string {.gensym, redefine.} = "b"
+    bar()
+  doAssert foo() == "b"
+
+{.pop.}
diff --git a/tests/template/tshadow.nim b/tests/template/tshadow.nim
new file mode 100644
index 000000000..a4de71592
--- /dev/null
+++ b/tests/template/tshadow.nim
@@ -0,0 +1,29 @@
+discard """
+  output: '''fish
+fish'''
+"""
+
+import macros
+
+block:
+  template init(initHook: proc(s: string)) =
+    proc dostuff =
+      var s = "fish"
+      initHook(s)
+    dostuff()
+
+  init do(s: string):
+    echo s
+
+block:
+  macro init(initHook: proc(s: string)) =
+    result = newStmtList(
+      newProc(name = ident("dostuff"), body = newStmtList(
+        newVarStmt(ident("s"), newStrLitNode("fish")),
+        newCall(initHook, ident("s"))
+      )),
+      newCall("dostuff")
+    )
+
+  init proc(s: string) =
+    echo s
diff --git a/tests/template/tsighash_regression.nim b/tests/template/tsighash_regression.nim
index bf1f4dfe4..f3a6b4833 100644
--- a/tests/template/tsighash_regression.nim
+++ b/tests/template/tsighash_regression.nim
@@ -1,5 +1,8 @@
+discard """
+exitcode: 1
+outputsub: "0"
+"""
 
 import tconfusinglocal
 
-
 fail "foo"
diff --git a/tests/template/tstmt_semchecked_twice.nim b/tests/template/tstmt_semchecked_twice.nim
deleted file mode 100644
index c6463ae06..000000000
--- a/tests/template/tstmt_semchecked_twice.nim
+++ /dev/null
@@ -1,30 +0,0 @@
-
-# bug #2585
-
-type
-    RenderPass = object
-       state: ref int
-
-    RenderData* = object
-        fb: int
-        walls: seq[RenderPass]
-
-    Mat2 = int
-    Vector2[T] = T
-    Pixels=int
-
-template use*(fb: int, st: untyped): untyped =
-    echo "a ", $fb
-    st
-    echo "a ", $fb
-
-proc render(rdat: var RenderData; passes: var openarray[RenderPass]; proj: Mat2;
-            indexType = 1) =
-    for i in 0 .. <len(passes):
-        echo "blah ", repr(passes[i])
-
-
-
-proc render2*(rdat: var RenderData; screenSz: Vector2[Pixels]; proj: Mat2) =
-    use rdat.fb:
-        render(rdat, rdat.walls, proj, 1)
diff --git a/tests/template/tsymchoicefield.nim b/tests/template/tsymchoicefield.nim
deleted file mode 100644
index 4483c2aa2..000000000
--- a/tests/template/tsymchoicefield.nim
+++ /dev/null
@@ -1,11 +0,0 @@
-type Foo = object
-  len: int
-
-var f = Foo(len: 40)
-
-template getLen(f: Foo): int = f.len
-
-echo f.getLen
-# This fails, because `len` gets the nkOpenSymChoice
-# treatment inside the template early pass and then
-# it can't be recognized as a field anymore
diff --git a/tests/template/ttemp_in_varargs.nim b/tests/template/ttemp_in_varargs.nim
deleted file mode 100644
index be78e6ef2..000000000
--- a/tests/template/ttemp_in_varargs.nim
+++ /dev/null
@@ -1,9 +0,0 @@
-discard """
-  output: '''a'''
-"""
-
-# bug #4292
-
-template foo(s: string): string = s
-proc variadicProc*(v: varargs[string, foo]) = echo v[0]
-variadicProc("a")
diff --git a/tests/template/ttempl.nim b/tests/template/ttempl.nim
deleted file mode 100644
index 5f1341990..000000000
--- a/tests/template/ttempl.nim
+++ /dev/null
@@ -1,27 +0,0 @@
-# Test the new template file mechanism
-
-import
-  os, times
-
-include "sunset.tmpl"
-
-const
-  tabs = [["home", "index"],
-          ["news", "news"],
-          ["documentation", "documentation"],
-          ["download", "download"],
-          ["FAQ", "question"],
-          ["links", "links"]]
-
-
-var i = 0
-for item in items(tabs):
-  var content = $i
-  var file: TFile
-  if open(file, changeFileExt(item[1], "html"), fmWrite):
-    write(file, sunsetTemplate(current=item[1], ticker="", content=content,
-                               tabs=tabs))
-    close(file)
-  else:
-    write(stdout, "cannot open file for writing")
-  inc(i)
diff --git a/tests/template/ttempl2.nim b/tests/template/ttempl2.nim
index aaa2f1344..e84e0630b 100644
--- a/tests/template/ttempl2.nim
+++ b/tests/template/ttempl2.nim
@@ -1,12 +1,12 @@
 discard """
+  errormsg: "undeclared identifier: \'b\'"
   file: "ttempl2.nim"
   line: 18
-  errormsg: "undeclared identifier: \'b\'"
 """
-template declareInScope(x: untyped, t: typeDesc): untyped {.immediate.} =
+template declareInScope(x: untyped, t: typeDesc): untyped =
   var x: t
 
-template declareInNewScope(x: untyped, t: typeDesc): untyped {.immediate.} =
+template declareInNewScope(x: untyped, t: typeDesc): untyped =
   # open a new scope:
   block:
     var x: t
@@ -16,4 +16,3 @@ a = 42  # works, `a` is known here
 
 declareInNewScope(b, int)
 b = 42  #ERROR_MSG undeclared identifier: 'b'
-
diff --git a/tests/template/ttempl3.nim b/tests/template/ttempl3.nim
index 91d416c48..17421cd87 100644
--- a/tests/template/ttempl3.nim
+++ b/tests/template/ttempl3.nim
@@ -1,9 +1,13 @@
+discard """
+action: compile
+"""
 
-template withOpenFile(f: untyped, filename: string, mode: TFileMode,
+
+template withOpenFile(f: untyped, filename: string, mode: FileMode,
                       actions: untyped): untyped =
   block:
     # test that 'f' is implicitly 'injecting':
-    var f: TFile
+    var f: File
     if open(f, filename, mode):
       try:
         actions
@@ -33,7 +37,7 @@ var `hu "XYZ"` = "yay"
 
 echo prefix(XYZ)
 
-template typedef(name: untyped, typ: typeDesc) {.immediate, dirty.} =
+template typedef(name: untyped, typ: typeDesc) {.dirty.} =
   type
     `T name`* = typ
     `P name`* = ref `T name`
@@ -56,3 +60,24 @@ template create(typ: typeDesc, arg: untyped): untyped = `init typ`(arg)
 var ff = Foo.create(12)
 
 echo ff.arg
+
+
+import macros
+
+# bug #11494
+macro staticForEach(arr: untyped, body: untyped): untyped =
+  result = newNimNode(nnkStmtList)
+  arr.expectKind(nnkBracket)
+  for n in arr:
+    let b = copyNimTree(body)
+    result.add quote do:
+      block:
+        type it {.inject.} = `n`
+        `b`
+
+template forEveryMatchingEntity*() =
+  staticForEach([int, string, float]):
+    var a {.inject.}: it
+    echo a
+
+forEveryMatchingEntity()
diff --git a/tests/template/ttempl4.nim b/tests/template/ttempl4.nim
deleted file mode 100644
index d1d26385f..000000000
--- a/tests/template/ttempl4.nim
+++ /dev/null
@@ -1,7 +0,0 @@
-
-template `:=`(name, val: untyped): typed =
-  var name = val
-
-ha := 1 * 4
-hu := "ta-da" == "ta-da"
-echo ha, hu
diff --git a/tests/template/ttempl5.nim b/tests/template/ttempl5.nim
deleted file mode 100644
index fd3ea0cad..000000000
--- a/tests/template/ttempl5.nim
+++ /dev/null
@@ -1,28 +0,0 @@
-
-import mtempl5
-
-echo templ()
-
-#bug #892
-
-proc parse_to_close(value: string, index: int, open='(', close=')'): int =
-    discard
-
-# Call parse_to_close
-template get_next_ident: typed =
-    discard "{something}".parse_to_close(0, open = '{', close = '}')
-
-get_next_ident()
-
-
-#identifier expected, but found '(open|open|open)'
-
-#bug #880 (also example in the manual!)
-
-template typedef(name: untyped, typ: typedesc) =
-  type
-    `T name`* {.inject.} = typ
-    `P name`* {.inject.} = ref `T name`
-
-typedef(myint, int)
-var x: PMyInt
diff --git a/tests/template/ttemplreturntype.nim b/tests/template/ttemplreturntype.nim
deleted file mode 100644
index 642fa1b72..000000000
--- a/tests/template/ttemplreturntype.nim
+++ /dev/null
@@ -1,4 +0,0 @@
-
-template `=~` (a: int, b: int): bool = false
-var foo = 2 =~ 3
-
diff --git a/tests/template/tunderscore1.nim b/tests/template/tunderscore1.nim
new file mode 100644
index 000000000..d74e5ba63
--- /dev/null
+++ b/tests/template/tunderscore1.nim
@@ -0,0 +1,11 @@
+discard """
+  errormsg: "the special identifier '_' is ignored in declarations and cannot be used"
+"""
+
+# issue #12094, #13804
+
+template foo =
+  let _ = 1
+  echo _
+
+foo()
diff --git a/tests/template/twhen_gensym.nim b/tests/template/twhen_gensym.nim
deleted file mode 100644
index f1a8d0eb7..000000000
--- a/tests/template/twhen_gensym.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-discard """
-  output: "hi"
-"""
-
-# bug #2670
-template testTemplate(b: bool): typed =
-    when b:
-        var a = "hi"
-    else:
-        var a = 5
-    echo a
-
-testTemplate(true)
diff --git a/tests/template/twhenintemplates.nim b/tests/template/twhenintemplates.nim
new file mode 100644
index 000000000..6fded856d
--- /dev/null
+++ b/tests/template/twhenintemplates.nim
@@ -0,0 +1,11 @@
+# bug #3670
+
+template someTempl(someConst: bool) =
+  when someConst:
+    var a : int
+  if true:
+    when not someConst:
+      var a : int
+    a = 5
+
+someTempl(true)
diff --git a/tests/template/twrongmapit.nim b/tests/template/twrongmapit.nim
index cbc63b9cd..2d53d03f5 100644
--- a/tests/template/twrongmapit.nim
+++ b/tests/template/twrongmapit.nim
@@ -1,17 +1,15 @@
 discard """
-  output: "####"
+  joinable: false
 """
-# unfortunately our tester doesn't support multiple lines of compiler
-# error messages yet...
 
 # bug #1562
 type Foo* {.pure, final.} = object
   elt: float
 
-template defineOpAssign(T, op: untyped) {.immediate.} =
-  proc op*(v: var T, w: T) {.inline.} =
+template defineOpAssign(T, op: untyped) =
+  proc `op`*(v: var T, w: T) {.inline.} =
     for i in 0..1:
-      op(v.elt, w.elt)
+      `op`(v.elt, w.elt)
 
 const ATTEMPT = 0
 
@@ -29,4 +27,4 @@ import sequtils
 
 (var i = @[""];i).applyIt(it)
 # now works:
-echo "##", i[0], "##"
+doAssert i[0] == ""
diff --git a/tests/template/twrongopensymchoice.nim b/tests/template/twrongopensymchoice.nim
index 360c92037..7a2bb48d3 100644
--- a/tests/template/twrongopensymchoice.nim
+++ b/tests/template/twrongopensymchoice.nim
@@ -20,5 +20,5 @@ proc main =
   var f = Foo.new()
   echo f.b
 
-when isMainModule:
+when true:
   main()
diff --git a/tests/template/typedescids.nim b/tests/template/typedescids.nim
deleted file mode 100644
index 1df2f69fb..000000000
--- a/tests/template/typedescids.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-discard """
-  output: '''2 3'''
-"""
-
-# bug #4097
-
-var i {.compileTime.} = 2
-
-template defineId*(t: typedesc) =
-  const id {.genSym.} = i
-  static: inc(i)
-  proc idFor*(T: typedesc[t]): int {.inline, raises: [].} = id
-
-defineId(int8)
-defineId(int16)
-
-echo idFor(int8), " ", idFor(int16)
diff --git a/tests/template/utemplates.nim b/tests/template/utemplates.nim
index 199268046..d70746578 100644
--- a/tests/template/utemplates.nim
+++ b/tests/template/utemplates.nim
@@ -3,11 +3,11 @@ import unittest
 template t(a: int): string = "int"
 template t(a: string): string = "string"
 
-test "templates can be overloaded":
+block: # templates can be overloaded
   check t(10) == "int"
   check t("test") == "string"
 
-test "previous definitions can be further overloaded or hidden in local scopes":
+block: # previous definitions can be further overloaded or hidden in local scopes
   template t(a: bool): string = "bool"
 
   check t(true) == "bool"
@@ -17,15 +17,20 @@ test "previous definitions can be further overloaded or hidden in local scopes":
   check t(10) == "inner int"
   check t("test") == "string"
 
-test "templates can be redefined multiple times":
-  template customAssert(cond: bool, msg: string): typed {.immediate, dirty.} =
+block: # templates can be redefined multiple times
+  template customAssert(cond: bool, msg: string): typed {.dirty.} =
     if not cond: fail(msg)
 
-  template assertion_failed(body: typed) {.immediate, dirty.} =
-    template fail(msg: string): typed = body
+  template assertionFailed(body: untyped) {.dirty.} =
+    template fail(msg: string): typed {.redefine.} =
+      body
+
+  assertionFailed:
+    check(msg == "first fail path")
 
-  assertion_failed: check msg == "first fail path"
   customAssert false, "first fail path"
 
-  assertion_failed: check msg == "second fail path"
+  assertionFailed:
+    check(msg == "second fail path")
+
   customAssert false, "second fail path"
diff --git a/tests/test_nimscript.nims b/tests/test_nimscript.nims
index d3eb9808e..32b7d1416 100644
--- a/tests/test_nimscript.nims
+++ b/tests/test_nimscript.nims
@@ -1,26 +1,138 @@
 # This nimscript is used to test if the following modules can be imported
 # http://nim-lang.org/docs/nims.html
 
-import algorithm
-import base64
-import colors
-import hashes
-import lists
-import math
-# import marshal
-import options
-import ospaths
-# import parsecfg
-# import parseopt
-import parseutils
-# import pegs
-import deques
-import sequtils
-import strutils
-import subexes
-import tables
-import unicode
-import uri
-import macros
+{.warning[UnusedImport]: off.}
+
+from stdtest/specialpaths import buildDir
+
+when defined(nimPreviewSlimSystem):
+  import std/[
+    syncio, assertions, formatfloat, objectdollar, widestrs
+  ]
+
+import std/[
+  # Core:
+  bitops, typetraits, lenientops, macros, volatile,
+  # fails due to FFI: typeinfo
+  # fails due to cstring cast/copyMem: endians
+  # works but uses FFI: cpuinfo, rlocks, locks
+
+  # Algorithms:
+  algorithm, enumutils, sequtils, setutils,
+
+  # Collections:
+  critbits, deques, heapqueue, intsets, lists, options, sets,
+  tables, packedsets,
+
+  # Strings:
+  editdistance, wordwrap, parseutils, ropes,
+  pegs, strformat, strmisc, strscans, strtabs,
+  strutils, unicode, unidecode, cstrutils,
+  # works but uses FFI: encodings
+
+  # Time handling:
+  # fails due to FFI: monotimes, times
+  # but times.getTime() implemented for VM
+
+  # Generic operator system services:
+  os, streams, distros,
+  # fails due to FFI: memfiles, osproc, terminal
+  # works but uses FFI: dynlib
+  # intentionally fails: marshal
+
+  # Math libraries:
+  complex, math, random, rationals, stats, sums,
+  # works but uses FFI: fenv, sysrand
+
+  # Internet protocols:
+  httpcore, mimetypes, uri,
+  # fails due to FFI: asyncdispatch, asyncfile, asyncftpclient, asynchttpserver,
+  # asyncnet, cgi, cookies, httpclient, nativesockets, net, selectors, smtp
+  # works but no need to test: asyncstreams, asyncfutures
+
+  # Threading:
+  # fails due to FFI: threadpool
+
+  # Parsers:
+  htmlparser, json, lexbase, parsecfg, parsecsv, parsesql, parsexml,
+  parseopt, jsonutils,
+
+  # XML processing:
+  xmltree, xmlparser,
+
+  # Generators:
+  htmlgen,
+
+  # Hashing:
+  base64, hashes,
+  # fails due to cstring cast/times import/endians import: oids
+  # fails due to copyMem/endians import: sha1
+
+  # Miscellaneous:
+  colors, sugar, varints, enumerate, with,
+  # fails due to FFI: browsers, coro, segfaults
+  # fails due to times import/methods: logging
+  # fails due to methods: unittest
+
+  # Modules for JS backend:
+  # fails intentionally: asyncjs, dom, jsconsole, jscore, jsffi, jsbigints,
+  # jsfetch, jsformdata, jsheaders
+
+  # Unlisted in lib.html:
+  decls, compilesettings, wrapnils, effecttraits, genasts,
+  importutils, isolation
+]
+
+# non-std imports
+import stdtest/testutils
+# tests (increase coverage via code reuse)
+import stdlib/trandom
+import stdlib/tosenv
 
 echo "Nimscript imports are successful."
+
+block:
+  doAssert "./foo//./bar/".normalizedPath == "foo/bar".unixToNativePath
+block:
+  doAssert $3'u == "3"
+  doAssert $3'u64 == "3"
+
+block: # #14142
+  discard dirExists("/usr")
+  discard fileExists("/usr/foo")
+  discard findExe("nim")
+
+block:
+  doAssertRaises(AssertionDefect): doAssert false
+  try: doAssert false
+  except Exception as e:
+    discard
+
+block:  # cpDir, cpFile, dirExists, fileExists, mkDir, mvDir, mvFile, rmDir, rmFile
+  const dname = buildDir/"D20210121T175016"
+  const subDir = dname/"sub"
+  const subDir2 = dname/"sub2"
+  const fpath = subDir/"f"
+  const fpath2 = subDir/"f2"
+  const fpath3 = subDir2/"f"
+  mkDir(subDir)
+  writeFile(fpath, "some text")
+  cpFile(fpath, fpath2)
+  doAssert fileExists(fpath2)
+  rmFile(fpath2)
+  cpDir(subDir, subDir2)
+  doAssert fileExists(fpath3)
+  rmDir(subDir2)
+  mvFile(fpath, fpath2)
+  doAssert not fileExists(fpath)
+  doAssert fileExists(fpath2)
+  mvFile(fpath2, fpath)
+  mvDir(subDir, subDir2)
+  doAssert not dirExists(subDir)
+  doAssert dirExists(subDir2)
+  mvDir(subDir2, subDir)
+  rmDir(dname)
+
+block:
+  # check parseopt can get command line:
+  discard initOptParser()
diff --git a/tests/testament/backend.nim b/tests/testament/backend.nim
deleted file mode 100644
index 385f1171c..000000000
--- a/tests/testament/backend.nim
+++ /dev/null
@@ -1,75 +0,0 @@
-#
-#
-#              The Nim Tester
-#        (c) Copyright 2017 Andreas Rumpf
-#
-#    Look at license.txt for more info.
-#    All rights reserved.
-
-import strutils, os, osproc, json
-
-type
-  MachineId* = distinct string
-  CommitId = distinct string
-
-proc `$`*(id: MachineId): string {.borrow.}
-#proc `$`(id: CommitId): string {.borrow.} # not used
-
-var
-  thisMachine: MachineId
-  thisCommit: CommitId
-  thisBranch: string
-
-{.experimental.}
-proc `()`(cmd: string{lit}): string = cmd.execProcess.string.strip
-
-proc getMachine*(): MachineId =
-  var name = "hostname"()
-  if name.len == 0:
-    name = when defined(posix): getenv"HOSTNAME".string
-           else: getenv"COMPUTERNAME".string
-  if name.len == 0:
-    quit "cannot determine the machine name"
-
-  result = MachineId(name)
-
-proc getCommit(): CommitId =
-  const commLen = "commit ".len
-  let hash = "git log -n 1"()[commLen..commLen+10]
-  thisBranch = "git symbolic-ref --short HEAD"()
-  if hash.len == 0 or thisBranch.len == 0: quit "cannot determine git HEAD"
-  result = CommitId(hash)
-
-var
-  results: File
-  currentCategory: string
-  entries: int
-
-proc writeTestResult*(name, category, target,
-                      action, result, expected, given: string) =
-  createDir("testresults")
-  if currentCategory != category:
-    if currentCategory.len > 0:
-      results.writeLine("]")
-      close(results)
-    currentCategory = category
-    results = open("testresults" / category.addFileExt"json", fmWrite)
-    results.writeLine("[")
-    entries = 0
-
-  let jentry = %*{"name": name, "category": category, "target": target,
-    "action": action, "result": result, "expected": expected, "given": given,
-    "machine": thisMachine.string, "commit": thisCommit.string, "branch": thisBranch}
-  if entries > 0:
-    results.writeLine(",")
-  results.write($jentry)
-  inc entries
-
-proc open*() =
-  thisMachine = getMachine()
-  thisCommit = getCommit()
-
-proc close*() =
-  if currentCategory.len > 0:
-    results.writeLine("]")
-    close(results)
diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim
deleted file mode 100644
index 9affbc159..000000000
--- a/tests/testament/categories.nim
+++ /dev/null
@@ -1,524 +0,0 @@
-#
-#
-#            Nim Tester
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Include for the tester that contains test suites that test special features
-## of the compiler.
-
-# included from tester.nim
-# ---------------- ROD file tests ---------------------------------------------
-
-const
-  rodfilesDir = "tests/rodfiles"
-
-proc delNimCache(filename, options: string) =
-  for target in low(TTarget)..high(TTarget):
-    let dir = nimcacheDir(filename, options, target)
-    try:
-      removeDir(dir)
-    except OSError:
-      echo "[Warning] could not delete: ", dir
-
-proc runRodFiles(r: var TResults, cat: Category, options: string) =
-  template test(filename: string, clearCacheFirst=false) =
-    if clearCacheFirst: delNimCache(filename, options)
-    testSpec r, makeTest(rodfilesDir / filename, options, cat, actionRun)
-
-
-  # test basic recompilation scheme:
-  test "hallo", true
-  test "hallo"
-  when false:
-    # test incremental type information:
-    test "hallo2"
-
-  # test type converters:
-  test "aconv", true
-  test "bconv"
-
-  # test G, A, B example from the documentation; test init sections:
-  test "deada", true
-  test "deada2"
-
-  when false:
-    # test method generation:
-    test "bmethods", true
-    test "bmethods2"
-
-    # test generics:
-    test "tgeneric1", true
-    test "tgeneric2"
-
-proc compileRodFiles(r: var TResults, cat: Category, options: string) =
-  template test(filename: untyped, clearCacheFirst=true) =
-    if clearCacheFirst: delNimCache(filename, options)
-    testSpec r, makeTest(rodfilesDir / filename, options, cat)
-
-  # test DLL interfacing:
-  test "gtkex1", true
-  test "gtkex2"
-
-# --------------------- flags tests -------------------------------------------
-
-proc flagTests(r: var TResults, cat: Category, options: string) =
-  # --genscript
-  const filename = "tests"/"flags"/"tgenscript"
-  const genopts = " --genscript"
-  let nimcache = nimcacheDir(filename, genopts, targetC)
-  testSpec r, makeTest(filename, genopts, cat)
-
-  when defined(windows):
-    testExec r, makeTest(filename, " cmd /c cd " & nimcache &
-                         " && compile_tgenscript.bat", cat)
-
-  when defined(linux):
-    testExec r, makeTest(filename, " sh -c \"cd " & nimcache &
-                         " && sh compile_tgenscript.sh\"", cat)
-
-  # Run
-  testExec r, makeTest(filename, " " & nimcache / "tgenscript", cat)
-
-# --------------------- DLL generation tests ----------------------------------
-
-proc safeCopyFile(src, dest: string) =
-  try:
-    copyFile(src, dest)
-  except OSError:
-    echo "[Warning] could not copy: ", src, " to ", dest
-
-proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) =
-  const rpath = when defined(macosx):
-      " --passL:-rpath --passL:@loader_path"
-    else:
-      ""
-
-  testSpec c, makeTest("lib/nimrtl.nim",
-    options & " --app:lib -d:createNimRtl --threads:on", cat)
-  testSpec c, makeTest("tests/dll/server.nim",
-    options & " --app:lib -d:useNimRtl --threads:on" & rpath, cat)
-
-
-  when defined(Windows):
-    # windows looks in the dir of the exe (yay!):
-    var nimrtlDll = DynlibFormat % "nimrtl"
-    safeCopyFile("lib" / nimrtlDll, "tests/dll" / nimrtlDll)
-  else:
-    # posix relies on crappy LD_LIBRARY_PATH (ugh!):
-    var libpath = getEnv"LD_LIBRARY_PATH".string
-    # Temporarily add the lib directory to LD_LIBRARY_PATH:
-    putEnv("LD_LIBRARY_PATH", "tests/dll" & (if libpath.len > 0: ":" & libpath else: ""))
-    defer: putEnv("LD_LIBRARY_PATH", libpath)
-    var nimrtlDll = DynlibFormat % "nimrtl"
-    safeCopyFile("lib" / nimrtlDll, "tests/dll" / nimrtlDll)
-
-  testSpec r, makeTest("tests/dll/client.nim", options & " -d:useNimRtl --threads:on" & rpath,
-                       cat, actionRun)
-
-proc dllTests(r: var TResults, cat: Category, options: string) =
-  # dummy compile result:
-  var c = initResults()
-
-  runBasicDLLTest c, r, cat, options
-  runBasicDLLTest c, r, cat, options & " -d:release"
-  when not defined(windows):
-    # still cannot find a recent Windows version of boehm.dll:
-    runBasicDLLTest c, r, cat, options & " --gc:boehm"
-    runBasicDLLTest c, r, cat, options & " -d:release --gc:boehm"
-
-# ------------------------------ GC tests -------------------------------------
-
-proc gcTests(r: var TResults, cat: Category, options: string) =
-  template testWithNone(filename: untyped) =
-    testSpec r, makeTest("tests/gc" / filename, options &
-                  " --gc:none", cat, actionRun)
-    testSpec r, makeTest("tests/gc" / filename, options &
-                  " -d:release --gc:none", cat, actionRun)
-
-  template testWithoutMs(filename: untyped) =
-    testSpec r, makeTest("tests/gc" / filename, options, cat, actionRun)
-    testSpec r, makeTest("tests/gc" / filename, options &
-                  " -d:release", cat, actionRun)
-    testSpec r, makeTest("tests/gc" / filename, options &
-                  " -d:release -d:useRealtimeGC", cat, actionRun)
-
-  template testWithoutBoehm(filename: untyped) =
-    testWithoutMs filename
-    testSpec r, makeTest("tests/gc" / filename, options &
-                  " --gc:markAndSweep", cat, actionRun)
-    testSpec r, makeTest("tests/gc" / filename, options &
-                  " -d:release --gc:markAndSweep", cat, actionRun)
-  template test(filename: untyped) =
-    testWithoutBoehm filename
-    when not defined(windows) and not defined(android):
-      # AR: cannot find any boehm.dll on the net, right now, so disabled
-      # for windows:
-      testSpec r, makeTest("tests/gc" / filename, options &
-                    " --gc:boehm", cat, actionRun)
-      testSpec r, makeTest("tests/gc" / filename, options &
-                    " -d:release --gc:boehm", cat, actionRun)
-
-  testWithoutBoehm "foreign_thr"
-  test "gcemscripten"
-  test "growobjcrash"
-  test "gcbench"
-  test "gcleak"
-  test "gcleak2"
-  testWithoutBoehm "gctest"
-  testWithNone "gctest"
-  test "gcleak3"
-  test "gcleak4"
-  # Disabled because it works and takes too long to run:
-  #test "gcleak5"
-  testWithoutBoehm "weakrefs"
-  test "cycleleak"
-  testWithoutBoehm "closureleak"
-  testWithoutMs "refarrayleak"
-
-  testWithoutBoehm "tlists"
-  testWithoutBoehm "thavlak"
-
-  test "stackrefleak"
-  test "cyclecollector"
-
-proc longGCTests(r: var TResults, cat: Category, options: string) =
-  when defined(windows):
-    let cOptions = "-ldl -DWIN"
-  else:
-    let cOptions = "-ldl"
-
-  var c = initResults()
-  # According to ioTests, this should compile the file
-  testNoSpec c, makeTest("tests/realtimeGC/shared", options, cat, actionCompile)
-  testC r, makeTest("tests/realtimeGC/cmain", cOptions, cat, actionRun)
-  testSpec r, makeTest("tests/realtimeGC/nmain", options & "--threads: on", cat, actionRun)
-
-# ------------------------- threading tests -----------------------------------
-
-proc threadTests(r: var TResults, cat: Category, options: string) =
-  template test(filename: untyped) =
-    testSpec r, makeTest(filename, options, cat, actionRun)
-    testSpec r, makeTest(filename, options & " -d:release", cat, actionRun)
-    testSpec r, makeTest(filename, options & " --tlsEmulation:on", cat, actionRun)
-  for t in os.walkFiles("tests/threads/t*.nim"):
-    test(t)
-
-# ------------------------- IO tests ------------------------------------------
-
-proc ioTests(r: var TResults, cat: Category, options: string) =
-  # We need readall_echo to be compiled for this test to run.
-  # dummy compile result:
-  var c = initResults()
-  testSpec c, makeTest("tests/system/helpers/readall_echo", options, cat)
-  testSpec r, makeTest("tests/system/io", options, cat)
-
-# ------------------------- async tests ---------------------------------------
-proc asyncTests(r: var TResults, cat: Category, options: string) =
-  template test(filename: untyped) =
-    testSpec r, makeTest(filename, options, cat)
-  for t in os.walkFiles("tests/async/t*.nim"):
-    test(t)
-
-# ------------------------- debugger tests ------------------------------------
-
-proc debuggerTests(r: var TResults, cat: Category, options: string) =
-  testNoSpec r, makeTest("tools/nimgrep", options & " --debugger:on", cat)
-
-# ------------------------- JS tests ------------------------------------------
-
-proc jsTests(r: var TResults, cat: Category, options: string) =
-  template test(filename: untyped) =
-    testSpec r, makeTest(filename, options & " -d:nodejs", cat,
-                         actionRun), targetJS
-    testSpec r, makeTest(filename, options & " -d:nodejs -d:release", cat,
-                         actionRun), targetJS
-
-  for t in os.walkFiles("tests/js/t*.nim"):
-    test(t)
-  for testfile in ["exception/texceptions", "exception/texcpt1",
-                   "exception/texcsub", "exception/tfinally",
-                   "exception/tfinally2", "exception/tfinally3",
-                   "exception/tunhandledexc",
-                   "actiontable/tactiontable", "method/tmultim1",
-                   "method/tmultim3", "method/tmultim4",
-                   "varres/tvarres0", "varres/tvarres3", "varres/tvarres4",
-                   "varres/tvartup", "misc/tints", "misc/tunsignedinc",
-                   "async/tjsandnativeasync"]:
-    test "tests/" & testfile & ".nim"
-
-  for testfile in ["strutils", "json", "random", "times", "logging"]:
-    test "lib/pure/" & testfile & ".nim"
-
-# ------------------------- nim in action -----------
-
-proc testNimInAction(r: var TResults, cat: Category, options: string) =
-  template test(filename: untyped, action: untyped) =
-    testSpec r, makeTest(filename, options, cat, action)
-
-  template testJS(filename: untyped) =
-    testSpec r, makeTest(filename, options, cat, actionCompile), targetJS
-
-  template testCPP(filename: untyped) =
-    testSpec r, makeTest(filename, options, cat, actionCompile), targetCPP
-
-  let tests = [
-    "niminaction/Chapter1/various1",
-    "niminaction/Chapter2/various2",
-    "niminaction/Chapter2/resultaccept",
-    "niminaction/Chapter2/resultreject",
-    "niminaction/Chapter2/explicit_discard",
-    "niminaction/Chapter2/no_def_eq",
-    "niminaction/Chapter2/no_iterator",
-    "niminaction/Chapter2/no_seq_type",
-    "niminaction/Chapter3/ChatApp/src/server",
-    "niminaction/Chapter3/ChatApp/src/client",
-    "niminaction/Chapter3/various3",
-    "niminaction/Chapter6/WikipediaStats/concurrency_regex",
-    "niminaction/Chapter6/WikipediaStats/concurrency",
-    "niminaction/Chapter6/WikipediaStats/naive",
-    "niminaction/Chapter6/WikipediaStats/parallel_counts",
-    "niminaction/Chapter6/WikipediaStats/race_condition",
-    "niminaction/Chapter6/WikipediaStats/sequential_counts",
-    "niminaction/Chapter6/WikipediaStats/unguarded_access",
-    "niminaction/Chapter7/Tweeter/src/tweeter",
-    "niminaction/Chapter7/Tweeter/src/createDatabase",
-    "niminaction/Chapter7/Tweeter/tests/database_test",
-    "niminaction/Chapter8/sdl/sdl_test"
-    ]
-
-  # Verify that the files have not been modified. Death shall fall upon
-  # whoever edits these hashes without dom96's permission, j/k. But please only
-  # edit when making a conscious breaking change, also please try to make your
-  # commit message clear and notify me so I can easily compile an errata later.
-  var testHashes: seq[string] = @[]
-
-  for test in tests:
-    testHashes.add(getMD5(readFile("tests" / test.addFileExt("nim")).string))
-
-  const refHashes = @[
-    "51afdfa84b3ca3d810809d6c4e5037ba", "30f07e4cd5eaec981f67868d4e91cfcf",
-    "d14e7c032de36d219c9548066a97e846", "2e40bfd5daadb268268727da91bb4e81",
-    "c5d3853ed0aba04bf6d35ba28a98dca0", "058603145ff92d46c009006b06e5b228",
-    "7b94a029b94ddb7efafddd546c965ff6", "586d74514394e49f2370dfc01dd9e830",
-    "e1901837b757c9357dc8d259fd0ef0f6", "097670c7ae12e825debaf8ec3995227b",
-    "a8cb7b78cc78d28535ab467361db5d6e", "bfaec2816a1848991b530c1ad17a0184",
-    "47cb71bb4c1198d6d29cdbee05aa10b9", "87e4436809f9d73324cfc4f57f116770",
-    "7b7db5cddc8cf8fa9b6776eef1d0a31d", "e6e40219f0f2b877869b738737b7685e",
-    "6532ee87d819f2605a443d5e94f9422a", "9a8fe78c588d08018843b64b57409a02",
-    "03a801275b8b76b4170c870cd0da079d", "20bb7d3e2d38d43b0cb5fcff4909a4a8",
-    "af6844598f534fab6942abfa4dfe9ab2", "2a7a17f84f6503d9bc89a5ab8feea127"
-  ]
-  doAssert testHashes == refHashes, "Nim in Action tests were changed."
-
-  # Run the tests.
-  for testfile in tests:
-    test "tests/" & testfile & ".nim", actionCompile
-
-  let jsFile = "tests/niminaction/Chapter8/canvas/canvas_test.nim"
-  testJS jsFile
-
-  let cppFile = "tests/niminaction/Chapter8/sfml/sfml_test.nim"
-  testCPP cppFile
-
-
-
-
-# ------------------------- manyloc -------------------------------------------
-#proc runSpecialTests(r: var TResults, options: string) =
-#  for t in ["lib/packages/docutils/highlite"]:
-#    testSpec(r, t, options)
-
-proc findMainFile(dir: string): string =
-  # finds the file belonging to ".nim.cfg"; if there is no such file
-  # it returns the some ".nim" file if there is only one:
-  const cfgExt = ".nim.cfg"
-  result = ""
-  var nimFiles = 0
-  for kind, file in os.walkDir(dir):
-    if kind == pcFile:
-      if file.endsWith(cfgExt): return file[.. ^(cfgExt.len+1)] & ".nim"
-      elif file.endsWith(".nim"):
-        if result.len == 0: result = file
-        inc nimFiles
-  if nimFiles != 1: result.setlen(0)
-
-proc manyLoc(r: var TResults, cat: Category, options: string) =
-  for kind, dir in os.walkDir("tests/manyloc"):
-    if kind == pcDir:
-      when defined(windows):
-        if dir.endsWith"nake": continue
-      let mainfile = findMainFile(dir)
-      if mainfile != "":
-        testNoSpec r, makeTest(mainfile, options, cat)
-
-proc compileExample(r: var TResults, pattern, options: string, cat: Category) =
-  for test in os.walkFiles(pattern):
-    testNoSpec r, makeTest(test, options, cat)
-
-proc testStdlib(r: var TResults, pattern, options: string, cat: Category) =
-  for test in os.walkFiles(pattern):
-    let name = extractFilename(test)
-    if name notin disabledFiles:
-      let contents = readFile(test).string
-      if contents.contains("when isMainModule"):
-        testSpec r, makeTest(test, options, cat, actionRunNoSpec)
-      else:
-        testNoSpec r, makeTest(test, options, cat, actionCompile)
-
-# ----------------------------- nimble ----------------------------------------
-type PackageFilter = enum
-  pfCoreOnly
-  pfExtraOnly
-  pfAll
-
-var nimbleDir = getEnv("NIMBLE_DIR").string
-if nimbleDir.len == 0: nimbleDir = getHomeDir() / ".nimble"
-let
-  nimbleExe = findExe("nimble")
-  #packageDir = nimbleDir / "pkgs" # not used
-  packageIndex = nimbleDir / "packages.json"
-
-proc waitForExitEx(p: Process): int =
-  var outp = outputStream(p)
-  var line = newStringOfCap(120).TaintedString
-  while true:
-    if outp.readLine(line):
-      discard
-    else:
-      result = peekExitCode(p)
-      if result != -1: break
-  close(p)
-
-proc getPackageDir(package: string): string =
-  ## TODO - Replace this with dom's version comparison magic.
-  var commandOutput = execCmdEx("nimble path $#" % package)
-  if commandOutput.exitCode != QuitSuccess:
-    return ""
-  else:
-    result = commandOutput[0].string
-
-iterator listPackages(filter: PackageFilter): tuple[name, url: string] =
-  let packageList = parseFile(packageIndex)
-
-  for package in packageList.items():
-    let
-      name = package["name"].str
-      url = package["url"].str
-      isCorePackage = "nim-lang" in normalize(url)
-    case filter:
-    of pfCoreOnly:
-      if isCorePackage:
-        yield (name, url)
-    of pfExtraOnly:
-      if not isCorePackage:
-        yield (name, url)
-    of pfAll:
-      yield (name, url)
-
-proc testNimblePackages(r: var TResults, cat: Category, filter: PackageFilter) =
-  if nimbleExe == "":
-    echo("[Warning] - Cannot run nimble tests: Nimble binary not found.")
-    return
-
-  if execCmd("$# update" % nimbleExe) == QuitFailure:
-    echo("[Warning] - Cannot run nimble tests: Nimble update failed.")
-    return
-
-  let packageFileTest = makeTest("PackageFileParsed", "", cat)
-  try:
-    for name, url in listPackages(filter):
-      var test = makeTest(name, "", cat)
-      echo(url)
-      let
-        installProcess = startProcess(nimbleExe, "", ["install", "-y", name])
-        installStatus = waitForExitEx(installProcess)
-      installProcess.close
-      if installStatus != QuitSuccess:
-        r.addResult(test, targetC, "", "", reInstallFailed)
-        continue
-
-      let
-        buildPath = getPackageDir(name).strip
-        buildProcess = startProcess(nimbleExe, buildPath, ["build"])
-        buildStatus = waitForExitEx(buildProcess)
-      buildProcess.close
-      if buildStatus != QuitSuccess:
-        r.addResult(test, targetC, "", "", reBuildFailed)
-      r.addResult(test, targetC, "", "", reSuccess)
-    r.addResult(packageFileTest, targetC, "", "", reSuccess)
-  except JsonParsingError:
-    echo("[Warning] - Cannot run nimble tests: Invalid package file.")
-    r.addResult(packageFileTest, targetC, "", "", reBuildFailed)
-
-
-# ----------------------------------------------------------------------------
-
-const AdditionalCategories = ["debugger", "examples", "lib"]
-
-proc `&.?`(a, b: string): string =
-  # candidate for the stdlib?
-  result = if b.startswith(a): b else: a & b
-
-#proc `&?.`(a, b: string): string = # not used
-  # candidate for the stdlib?
-  #result = if a.endswith(b): a else: a & b
-
-proc processSingleTest(r: var TResults, cat: Category, options, test: string) =
-  let test = "tests" & DirSep &.? cat.string / test
-  let target = if cat.string.normalize == "js": targetJS else: targetC
-
-  if existsFile(test): testSpec r, makeTest(test, options, cat), target
-  else: echo "[Warning] - ", test, " test does not exist"
-
-proc processCategory(r: var TResults, cat: Category, options: string) =
-  case cat.string.normalize
-  of "rodfiles":
-    when false:
-      compileRodFiles(r, cat, options)
-      runRodFiles(r, cat, options)
-  of "js":
-    # XXX JS doesn't need to be special anymore
-    jsTests(r, cat, options)
-  of "dll":
-    dllTests(r, cat, options)
-  of "flags":
-    flagTests(r, cat, options)
-  of "gc":
-    gcTests(r, cat, options)
-  of "longgc":
-    longGCTests(r, cat, options)
-  of "debugger":
-    debuggerTests(r, cat, options)
-  of "manyloc":
-    manyLoc r, cat, options
-  of "threads":
-    threadTests r, cat, options & " --threads:on"
-  of "io":
-    ioTests r, cat, options
-  of "async":
-    asyncTests r, cat, options
-  of "lib":
-    testStdlib(r, "lib/pure/*.nim", options, cat)
-    testStdlib(r, "lib/packages/docutils/highlite", options, cat)
-  of "examples":
-    compileExample(r, "examples/*.nim", options, cat)
-    compileExample(r, "examples/gtk/*.nim", options, cat)
-    compileExample(r, "examples/talk/*.nim", options, cat)
-  of "nimble-core":
-    testNimblePackages(r, cat, pfCoreOnly)
-  of "nimble-extra":
-    testNimblePackages(r, cat, pfExtraOnly)
-  of "nimble-all":
-    testNimblePackages(r, cat, pfAll)
-  of "niminaction":
-    testNimInAction(r, cat, options)
-  of "untestable":
-    # We can't test it because it depends on a third party.
-    discard # TODO: Move untestable tests to someplace else, i.e. nimble repo.
-  else:
-    for name in os.walkFiles("tests" & DirSep &.? cat.string / "t*.nim"):
-      testSpec r, makeTest(name, options, cat)
diff --git a/tests/testament/specs.nim b/tests/testament/specs.nim
deleted file mode 100644
index ac79e3942..000000000
--- a/tests/testament/specs.nim
+++ /dev/null
@@ -1,202 +0,0 @@
-#
-#
-#            Nim Tester
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-import parseutils, strutils, os, osproc, streams, parsecfg
-
-
-var compilerPrefix* = "compiler" / "nim "
-
-let isTravis = existsEnv("TRAVIS")
-let isAppVeyor = existsEnv("APPVEYOR")
-
-proc cmdTemplate*(): string =
-  compilerPrefix & "$target --lib:lib --hints:on -d:testing $options $file"
-
-type
-  TTestAction* = enum
-    actionCompile = "compile"
-    actionRun = "run"
-    actionReject = "reject"
-    actionRunNoSpec = "runNoSpec"
-  TResultEnum* = enum
-    reNimcCrash,     # nim compiler seems to have crashed
-    reMsgsDiffer,       # error messages differ
-    reFilesDiffer,      # expected and given filenames differ
-    reLinesDiffer,      # expected and given line numbers differ
-    reOutputsDiffer,
-    reExitcodesDiffer,
-    reInvalidPeg,
-    reCodegenFailure,
-    reCodeNotFound,
-    reExeNotFound,
-    reInstallFailed     # package installation failed
-    reBuildFailed       # package building failed
-    reIgnored,          # test is ignored
-    reSuccess           # test was successful
-  TTarget* = enum
-    targetC = "C"
-    targetCpp = "C++"
-    targetObjC = "ObjC"
-    targetJS = "JS"
-
-  TSpec* = object
-    action*: TTestAction
-    file*, cmd*: string
-    outp*: string
-    line*, column*: int
-    tfile*: string
-    tline*, tcolumn*: int
-    exitCode*: int
-    msg*: string
-    ccodeCheck*: string
-    maxCodeSize*: int
-    err*: TResultEnum
-    substr*, sortoutput*: bool
-    targets*: set[TTarget]
-    nimout*: string
-
-const
-  targetToExt*: array[TTarget, string] = ["c", "cpp", "m", "js"]
-  targetToCmd*: array[TTarget, string] = ["c", "cpp", "objc", "js"]
-
-when not declared(parseCfgBool):
-  # candidate for the stdlib:
-  proc parseCfgBool(s: string): bool =
-    case normalize(s)
-    of "y", "yes", "true", "1", "on": result = true
-    of "n", "no", "false", "0", "off": result = false
-    else: raise newException(ValueError, "cannot interpret as a bool: " & s)
-
-proc extractSpec(filename: string): string =
-  const tripleQuote = "\"\"\""
-  var x = readFile(filename).string
-  var a = x.find(tripleQuote)
-  var b = x.find(tripleQuote, a+3)
-  # look for """ only in the first section
-  if a >= 0 and b > a and a < 40:
-    result = x.substr(a+3, b-1).replace("'''", tripleQuote)
-  else:
-    #echo "warning: file does not contain spec: " & filename
-    result = ""
-
-when not defined(nimhygiene):
-  {.pragma: inject.}
-
-template parseSpecAux(fillResult: untyped) =
-  var ss = newStringStream(extractSpec(filename))
-  var p {.inject.}: CfgParser
-  open(p, ss, filename, 1)
-  while true:
-    var e {.inject.} = next(p)
-    case e.kind
-    of cfgEof: break
-    of cfgSectionStart, cfgOption, cfgError:
-      echo ignoreMsg(p, e)
-    of cfgKeyValuePair:
-      fillResult
-  close(p)
-
-proc specDefaults*(result: var TSpec) =
-  result.msg = ""
-  result.outp = ""
-  result.nimout = ""
-  result.ccodeCheck = ""
-  result.cmd = cmdTemplate()
-  result.line = 0
-  result.column = 0
-  result.tfile = ""
-  result.tline = 0
-  result.tcolumn = 0
-  result.maxCodeSize = 0
-
-proc parseTargets*(value: string): set[TTarget] =
-  for v in value.normalize.splitWhitespace:
-    case v
-    of "c": result.incl(targetC)
-    of "cpp", "c++": result.incl(targetCpp)
-    of "objc": result.incl(targetObjC)
-    of "js": result.incl(targetJS)
-    else: echo "target ignored: " & v
-
-proc parseSpec*(filename: string): TSpec =
-  specDefaults(result)
-  result.file = filename
-  parseSpecAux:
-    case normalize(e.key)
-    of "action":
-      case e.value.normalize
-      of "compile": result.action = actionCompile
-      of "run": result.action = actionRun
-      of "reject": result.action = actionReject
-      else: echo ignoreMsg(p, e)
-    of "file": result.file = e.value
-    of "line": discard parseInt(e.value, result.line)
-    of "column": discard parseInt(e.value, result.column)
-    of "tfile": result.tfile = e.value
-    of "tline": discard parseInt(e.value, result.tline)
-    of "tcolumn": discard parseInt(e.value, result.tcolumn)
-    of "output":
-      result.action = actionRun
-      result.outp = e.value
-    of "outputsub":
-      result.action = actionRun
-      result.outp = e.value
-      result.substr = true
-    of "sortoutput":
-      result.sortoutput = parseCfgBool(e.value)
-    of "exitcode":
-      discard parseInt(e.value, result.exitCode)
-      result.action = actionRun
-    of "msg":
-      result.msg = e.value
-      if result.action != actionRun:
-        result.action = actionCompile
-    of "errormsg", "errmsg":
-      result.msg = e.value
-      result.action = actionReject
-    of "nimout":
-      result.nimout = e.value
-    of "disabled":
-      case e.value.normalize
-      of "y", "yes", "true", "1", "on": result.err = reIgnored
-      of "n", "no", "false", "0", "off": discard
-      of "win", "windows":
-        when defined(windows): result.err = reIgnored
-      of "linux":
-        when defined(linux): result.err = reIgnored
-      of "bsd":
-        when defined(bsd): result.err = reIgnored
-      of "macosx":
-        when defined(macosx): result.err = reIgnored
-      of "unix":
-        when defined(unix): result.err = reIgnored
-      of "posix":
-        when defined(posix): result.err = reIgnored
-      of "travis":
-        if isTravis: result.err = reIgnored
-      of "appveyor":
-        if isAppVeyor: result.err = reIgnored
-      else:
-        raise newException(ValueError, "cannot interpret as a bool: " & e.value)
-    of "cmd":
-      if e.value.startsWith("nim "):
-        result.cmd = compilerPrefix & e.value[4..^1]
-      else:
-        result.cmd = e.value
-    of "ccodecheck": result.ccodeCheck = e.value
-    of "maxcodesize": discard parseInt(e.value, result.maxCodeSize)
-    of "target", "targets":
-      for v in e.value.normalize.splitWhitespace:
-        case v
-        of "c": result.targets.incl(targetC)
-        of "cpp", "c++": result.targets.incl(targetCpp)
-        of "objc": result.targets.incl(targetObjC)
-        of "js": result.targets.incl(targetJS)
-        else: echo ignoreMsg(p, e)
-    else: echo ignoreMsg(p, e)
diff --git a/tests/testament/t16576.nim b/tests/testament/t16576.nim
new file mode 100644
index 000000000..8d0dd57e3
--- /dev/null
+++ b/tests/testament/t16576.nim
@@ -0,0 +1,7 @@
+discard """
+  matrix:"-d:nimTest_t16576"
+"""
+
+# bug #16576
+doAssert defined(nimTest_t16576)
+doAssert not defined(nimMegatest)
diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim
deleted file mode 100644
index 0764b6363..000000000
--- a/tests/testament/tester.nim
+++ /dev/null
@@ -1,515 +0,0 @@
-#
-#
-#            Nim Tester
-#        (c) Copyright 2017 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This program verifies Nim against the testcases.
-
-import
-  parseutils, strutils, pegs, os, osproc, streams, parsecfg, json,
-  marshal, backend, parseopt, specs, htmlgen, browsers, terminal,
-  algorithm, compiler/nodejs, times, sets, md5
-
-const
-  resultsFile = "testresults.html"
-  #jsonFile = "testresults.json" # not used
-  Usage = """Usage:
-  tester [options] command [arguments]
-
-Command:
-  all                         run all tests
-  c|category <category>       run all the tests of a certain category
-  r|run <test>                run single test file
-  html                        generate $1 from the database
-Arguments:
-  arguments are passed to the compiler
-Options:
-  --print                   also print results to the console
-  --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)
-""" % resultsFile
-
-type
-  Category = distinct string
-  TResults = object
-    total, passed, skipped: int
-    data: string
-
-  TTest = object
-    name: string
-    cat: Category
-    options: string
-    action: TTestAction
-    startTime: float
-
-# ----------------------------------------------------------------------------
-
-let
-  pegLineError =
-    peg"{[^(]*} '(' {\d+} ', ' {\d+} ') ' ('Error') ':' \s* {.*}"
-  pegLineTemplate =
-    peg"{[^(]*} '(' {\d+} ', ' {\d+} ') ' 'template/generic instantiation from here'.*"
-  pegOtherError = peg"'Error:' \s* {.*}"
-  pegSuccess = peg"'Hint: operation successful'.*"
-  pegOfInterest = pegLineError / pegOtherError
-
-var targets = {low(TTarget)..high(TTarget)}
-
-proc normalizeMsg(s: string): string =
-  result = newStringOfCap(s.len+1)
-  for x in splitLines(s):
-    if result.len > 0: result.add '\L'
-    result.add x.strip
-
-proc getFileDir(filename: string): string =
-  result = filename.splitFile().dir
-  if not result.isAbsolute():
-    result = getCurrentDir() / result
-
-proc nimcacheDir(filename, options: string, target: TTarget): string =
-  ## Give each test a private nimcache dir so they don't clobber each other's.
-  let hashInput = options & $target
-  return "nimcache" / (filename & '_' & hashInput.getMD5)
-
-proc callCompiler(cmdTemplate, filename, options: string,
-                  target: TTarget, extraOptions=""): TSpec =
-  let nimcache = nimcacheDir(filename, options, target)
-  let options = options & " " & ("--nimCache:" & nimcache).quoteShell & extraOptions
-  let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
-                       "options", options, "file", filename.quoteShell,
-                       "filedir", filename.getFileDir()])
-  var p = startProcess(command=c[0], args=c[1.. ^1],
-                       options={poStdErrToStdOut, poUsePath})
-  let outp = p.outputStream
-  var suc = ""
-  var err = ""
-  var tmpl = ""
-  var x = newStringOfCap(120)
-  result.nimout = ""
-  while outp.readLine(x.TaintedString) or running(p):
-    result.nimout.add(x & "\n")
-    if x =~ pegOfInterest:
-      # `err` should contain the last error/warning message
-      err = x
-    elif x =~ pegLineTemplate and err == "":
-      # `tmpl` contains the last template expansion before the error
-      tmpl = x
-    elif x =~ pegSuccess:
-      suc = x
-  close(p)
-  result.msg = ""
-  result.file = ""
-  result.outp = ""
-  result.line = 0
-  result.column = 0
-  result.tfile = ""
-  result.tline = 0
-  result.tcolumn = 0
-  if tmpl =~ pegLineTemplate:
-    result.tfile = extractFilename(matches[0])
-    result.tline = parseInt(matches[1])
-    result.tcolumn = parseInt(matches[2])
-  if err =~ pegLineError:
-    result.file = extractFilename(matches[0])
-    result.line = parseInt(matches[1])
-    result.column = parseInt(matches[2])
-    result.msg = matches[3]
-  elif err =~ pegOtherError:
-    result.msg = matches[0]
-  elif suc =~ pegSuccess:
-    result.err = reSuccess
-
-proc callCCompiler(cmdTemplate, filename, options: string,
-                  target: TTarget): TSpec =
-  let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
-                       "options", options, "file", filename.quoteShell,
-                       "filedir", filename.getFileDir()])
-  var p = startProcess(command="gcc", args=c[5 .. ^1],
-                       options={poStdErrToStdOut, poUsePath})
-  let outp = p.outputStream
-  var x = newStringOfCap(120)
-  result.nimout = ""
-  result.msg = ""
-  result.file = ""
-  result.outp = ""
-  result.line = -1
-  while outp.readLine(x.TaintedString) or running(p):
-    result.nimout.add(x & "\n")
-  close(p)
-  if p.peekExitCode == 0:
-    result.err = reSuccess
-
-proc initResults: TResults =
-  result.total = 0
-  result.passed = 0
-  result.skipped = 0
-  result.data = ""
-
-#proc readResults(filename: string): TResults = # not used
-#  result = marshal.to[TResults](readFile(filename).string)
-
-#proc writeResults(filename: string, r: TResults) = # not used
-#  writeFile(filename, $$r)
-
-proc `$`(x: TResults): string =
-  result = ("Tests passed: $1 / $3 <br />\n" &
-            "Tests skipped: $2 / $3 <br />\n") %
-            [$x.passed, $x.skipped, $x.total]
-
-proc addResult(r: var TResults, test: TTest, target: TTarget,
-               expected, given: string, success: TResultEnum) =
-  let name = test.name.extractFilename & " " & $target & test.options
-  let duration = epochTime() - test.startTime
-  let durationStr = duration.formatFloat(ffDecimal, precision = 8)
-  backend.writeTestResult(name = name,
-                          category = test.cat.string,
-                          target = $target,
-                          action = $test.action,
-                          result = $success,
-                          expected = expected,
-                          given = given)
-  r.data.addf("$#\t$#\t$#\t$#", name, expected, given, $success)
-  if success == reSuccess:
-    styledEcho fgGreen, "PASS: ", fgCyan, alignLeft(name, 60), fgBlue, " (", durationStr, " secs)"
-  elif success == reIgnored:
-    styledEcho styleDim, fgYellow, "SKIP: ", styleBright, fgCyan, name
-  else:
-    styledEcho styleBright, fgRed, "FAIL: ", fgCyan, name
-    styledEcho styleBright, fgCyan, "Test \"", test.name, "\"", " in category \"", test.cat.string, "\""
-    styledEcho styleBright, fgRed, "Failure: ", $success
-    styledEcho fgYellow, "Expected:"
-    styledEcho styleBright, expected, "\n"
-    styledEcho fgYellow, "Gotten:"
-    styledEcho styleBright, given, "\n"
-
-  if existsEnv("APPVEYOR"):
-    let (outcome, msg) =
-      if success == reSuccess:
-        ("Passed", "")
-      elif success == reIgnored:
-        ("Skipped", "")
-      else:
-        ("Failed", "Failure: " & $success & "\nExpected:\n" & expected & "\n\n" & "Gotten:\n" & given)
-    var p = startProcess("appveyor", args=["AddTest", test.name.replace("\\", "/") & test.options,
-                         "-Framework", "nim-testament", "-FileName",
-                         test.cat.string,
-                         "-Outcome", outcome, "-ErrorMessage", msg,
-                         "-Duration", $(duration*1000).int],
-                         options={poStdErrToStdOut, poUsePath, poParentStreams})
-    discard waitForExit(p)
-    close(p)
-
-proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest, target: TTarget) =
-  if strip(expected.msg) notin strip(given.msg):
-    r.addResult(test, target, expected.msg, given.msg, reMsgsDiffer)
-  elif expected.nimout.len > 0 and expected.nimout.normalizeMsg notin given.nimout.normalizeMsg:
-    r.addResult(test, target, expected.nimout, given.nimout, reMsgsDiffer)
-  elif expected.tfile == "" and extractFilename(expected.file) != extractFilename(given.file) and
-      "internal error:" notin expected.msg:
-    r.addResult(test, target, expected.file, given.file, reFilesDiffer)
-  elif expected.line != given.line and expected.line != 0 or
-       expected.column != given.column and expected.column != 0:
-    r.addResult(test, target, $expected.line & ':' & $expected.column,
-                      $given.line & ':' & $given.column,
-                      reLinesDiffer)
-  elif expected.tfile != "" and extractFilename(expected.tfile) != extractFilename(given.tfile) and
-      "internal error:" notin expected.msg:
-    r.addResult(test, target, expected.tfile, given.tfile, reFilesDiffer)
-  elif expected.tline != given.tline and expected.tline != 0 or
-       expected.tcolumn != given.tcolumn and expected.tcolumn != 0:
-    r.addResult(test, target, $expected.tline & ':' & $expected.tcolumn,
-                      $given.tline & ':' & $given.tcolumn,
-                      reLinesDiffer)
-  else:
-    r.addResult(test, target, expected.msg, given.msg, reSuccess)
-    inc(r.passed)
-
-proc generatedFile(test: TTest, target: TTarget): string =
-  let (_, name, _) = test.name.splitFile
-  let ext = targetToExt[target]
-  result = nimcacheDir(test.name, test.options, target) /
-    (if target == targetJS: "" else: "compiler_") &
-    name.changeFileExt(ext)
-
-proc needsCodegenCheck(spec: TSpec): bool =
-  result = spec.maxCodeSize > 0 or spec.ccodeCheck.len > 0
-
-proc codegenCheck(test: TTest, target: TTarget, spec: TSpec, expectedMsg: var string,
-                  given: var TSpec) =
-  try:
-    let genFile = generatedFile(test, target)
-    let contents = readFile(genFile).string
-    let check = spec.ccodeCheck
-    if check.len > 0:
-      if check[0] == '\\':
-        # little hack to get 'match' support:
-        if not contents.match(check.peg):
-          given.err = reCodegenFailure
-      elif contents.find(check.peg) < 0:
-        given.err = reCodegenFailure
-      expectedMsg = check
-    if spec.maxCodeSize > 0 and contents.len > spec.maxCodeSize:
-      given.err = reCodegenFailure
-      given.msg = "generated code size: " & $contents.len
-      expectedMsg = "max allowed size: " & $spec.maxCodeSize
-  except ValueError:
-    given.err = reInvalidPeg
-    echo getCurrentExceptionMsg()
-  except IOError:
-    given.err = reCodeNotFound
-
-proc nimoutCheck(test: TTest; expectedNimout: string; given: var TSpec) =
-  let exp = expectedNimout.strip.replace("\C\L", "\L")
-  let giv = given.nimout.strip.replace("\C\L", "\L")
-  if exp notin giv:
-    given.err = reMsgsDiffer
-
-proc makeDeterministic(s: string): string =
-  var x = splitLines(s)
-  sort(x, system.cmp)
-  result = join(x, "\n")
-
-proc compilerOutputTests(test: TTest, target: TTarget, given: var TSpec,
-                         expected: TSpec; r: var TResults) =
-  var expectedmsg: string = ""
-  var givenmsg: string = ""
-  if given.err == reSuccess:
-    if expected.needsCodegenCheck:
-      codegenCheck(test, target, expected, expectedmsg, given)
-      givenmsg = given.msg
-    if expected.nimout.len > 0:
-      expectedmsg = expected.nimout
-      givenmsg = given.nimout.strip
-      nimoutCheck(test, expectedmsg, given)
-  else:
-    givenmsg = given.nimout.strip
-  if given.err == reSuccess: inc(r.passed)
-  r.addResult(test, target, expectedmsg, givenmsg, given.err)
-
-proc testSpec(r: var TResults, test: TTest, target = targetC) =
-  let tname = test.name.addFileExt(".nim")
-  #echo "TESTING ", tname
-  var expected: TSpec
-  if test.action != actionRunNoSpec:
-    expected = parseSpec(tname)
-  else:
-    specDefaults expected
-    expected.action = actionRunNoSpec
-
-  if expected.err == reIgnored:
-    r.addResult(test, target, "", "", reIgnored)
-    inc(r.skipped)
-    inc(r.total)
-    return
-
-  if expected.targets == {}:
-    expected.targets.incl(target)
-
-  for target in expected.targets:
-    inc(r.total)
-    if target notin targets:
-      r.addResult(test, target, "", "", reIgnored)
-      inc(r.skipped)
-      continue
-
-    case expected.action
-    of actionCompile:
-      var given = callCompiler(expected.cmd, test.name, test.options, target,
-        extraOptions=" --stdout --hint[Path]:off --hint[Processing]:off")
-      compilerOutputTests(test, target, given, expected, r)
-    of actionRun, actionRunNoSpec:
-      # In this branch of code "early return" pattern is clearer than deep
-      # nested conditionals - the empty rows in between to clarify the "danger"
-      var given = callCompiler(expected.cmd, test.name, test.options,
-                               target)
-
-      if given.err != reSuccess:
-        r.addResult(test, target, "", given.msg, given.err)
-        continue
-
-      let isJsTarget = target == targetJS
-      var exeFile: string
-      if isJsTarget:
-        let (_, file, _) = splitFile(tname)
-        exeFile = nimcacheDir(test.name, test.options, target) / file & ".js"
-      else:
-        exeFile = changeFileExt(tname, ExeExt)
-
-      if not existsFile(exeFile):
-        r.addResult(test, target, expected.outp, "executable not found", reExeNotFound)
-        continue
-
-      let nodejs = if isJsTarget: findNodeJs() else: ""
-      if isJsTarget and nodejs == "":
-        r.addResult(test, target, expected.outp, "nodejs binary not in PATH",
-                    reExeNotFound)
-        continue
-
-      let exeCmd = (if isJsTarget: nodejs & " " else: "") & exeFile
-      var (buf, exitCode) = execCmdEx(exeCmd, options = {poStdErrToStdOut})
-
-      # Treat all failure codes from nodejs as 1. Older versions of nodejs used
-      # to return other codes, but for us it is sufficient to know that it's not 0.
-      if exitCode != 0: exitCode = 1
-
-      let bufB = if expected.sortoutput: makeDeterministic(strip(buf.string))
-                 else: strip(buf.string)
-      let expectedOut = strip(expected.outp)
-
-      if exitCode != expected.exitCode:
-        r.addResult(test, target, "exitcode: " & $expected.exitCode,
-                          "exitcode: " & $exitCode & "\n\nOutput:\n" &
-                          bufB, reExitCodesDiffer)
-        continue
-
-      if bufB != expectedOut and expected.action != actionRunNoSpec:
-        if not (expected.substr and expectedOut in bufB):
-          given.err = reOutputsDiffer
-          r.addResult(test, target, expected.outp, bufB, reOutputsDiffer)
-          continue
-
-      compilerOutputTests(test, target, given, expected, r)
-      continue
-
-    of actionReject:
-      var given = callCompiler(expected.cmd, test.name, test.options,
-                               target)
-      cmpMsgs(r, expected, given, test, target)
-      continue
-
-proc testNoSpec(r: var TResults, test: TTest, target = targetC) =
-  # does not extract the spec because the file is not supposed to have any
-  #let tname = test.name.addFileExt(".nim")
-  inc(r.total)
-  let given = callCompiler(cmdTemplate(), test.name, test.options, target)
-  r.addResult(test, target, "", given.msg, given.err)
-  if given.err == reSuccess: inc(r.passed)
-
-proc testC(r: var TResults, test: TTest) =
-  # runs C code. Doesn't support any specs, just goes by exit code.
-  let tname = test.name.addFileExt(".c")
-  inc(r.total)
-  styledEcho "Processing ", fgCyan, extractFilename(tname)
-  var given = callCCompiler(cmdTemplate(), test.name & ".c", test.options, targetC)
-  if given.err != reSuccess:
-    r.addResult(test, targetC, "", given.msg, given.err)
-  elif test.action == actionRun:
-    let exeFile = changeFileExt(test.name, ExeExt)
-    var (_, exitCode) = execCmdEx(exeFile, options = {poStdErrToStdOut, poUsePath})
-    if exitCode != 0: given.err = reExitCodesDiffer
-  if given.err == reSuccess: inc(r.passed)
-
-proc testExec(r: var TResults, test: TTest) =
-  # runs executable or script, just goes by exit code
-  inc(r.total)
-  let (outp, errC) = execCmdEx(test.options.strip())
-  var given: TSpec
-  specDefaults(given)
-  if errC == 0:
-    given.err = reSuccess
-  else:
-    given.err = reExitCodesDiffer
-    given.msg = outp.string
-
-  if given.err == reSuccess: inc(r.passed)
-  r.addResult(test, targetC, "", given.msg, given.err)
-
-proc makeTest(test, options: string, cat: Category, action = actionCompile,
-              env: string = ""): TTest =
-  # start with 'actionCompile', will be overwritten in the spec:
-  result = TTest(cat: cat, name: test, options: options,
-                 action: action, startTime: epochTime())
-
-when defined(windows):
-  const
-    # array of modules disabled from compilation test of stdlib.
-    disabledFiles = ["coro.nim", "fsmonitor.nim"]
-else:
-  const
-    # array of modules disabled from compilation test of stdlib.
-    disabledFiles = ["-"]
-
-include categories
-
-# proc runCaasTests(r: var TResults) =
-#   for test, output, status, mode in caasTestsRunner():
-#     r.addResult(test, "", output & "-> " & $mode,
-#                 if status: reSuccess else: reOutputsDiffer)
-
-proc main() =
-  os.putenv "NIMTEST_COLOR", "never"
-  os.putenv "NIMTEST_OUTPUT_LVL", "PRINT_FAILURES"
-
-  backend.open()
-  var optPrintResults = false
-  var optFailing = false
-
-  var targetsStr = ""
-
-  var p = initOptParser()
-  p.next()
-  while p.kind == cmdLongoption:
-    case p.key.string.normalize
-    of "print", "verbose": optPrintResults = true
-    of "failing": optFailing = true
-    of "pedantic": discard "now always enabled"
-    of "targets":
-      targetsStr = p.val.string
-      targets = parseTargets(targetsStr)
-    of "nim": compilerPrefix = p.val.string
-    else: quit Usage
-    p.next()
-  if p.kind != cmdArgument: quit Usage
-  var action = p.key.string.normalize
-  p.next()
-  var r = initResults()
-  case action
-  of "all":
-    let testsDir = "tests" & DirSep
-    var myself = quoteShell(findExe("tests" / "testament" / "tester"))
-    if targetsStr.len > 0:
-      myself &= " " & quoteShell("--targets:" & targetsStr)
-
-    myself &= " " & quoteShell("--nim:" & compilerPrefix)
-
-    var cmds: 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 ["testament", "testdata", "nimcache"]:
-        cmds.add(myself & " cat " & cat & rest)
-    for cat in AdditionalCategories:
-      cmds.add(myself & " cat " & cat & rest)
-    quit osproc.execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams})
-  of "c", "cat", "category":
-    var cat = Category(p.key)
-    p.next
-    processCategory(r, cat, p.cmdLineRest.string)
-  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)
-  of "html":
-    generateHtml(resultsFile, optFailing)
-  else:
-    quit Usage
-
-  if optPrintResults:
-    if action == "html": openDefaultBrowser(resultsFile)
-    else: echo r, r.data
-  backend.close()
-  var failed = r.total - r.passed - r.skipped
-  if failed != 0:
-    echo "FAILURE! total: ", r.total, " passed: ", r.passed, " skipped: ", r.skipped
-    quit(QuitFailure)
-
-if paramCount() == 0:
-  quit Usage
-main()
diff --git a/tests/testament/tester.nim.cfg b/tests/testament/tester.nim.cfg
deleted file mode 100644
index 27fd67075..000000000
--- a/tests/testament/tester.nim.cfg
+++ /dev/null
@@ -1 +0,0 @@
-path = "$nim" # For compiler/nodejs
diff --git a/tests/testament/tinlinemsg.nim b/tests/testament/tinlinemsg.nim
new file mode 100644
index 000000000..199c263e9
--- /dev/null
+++ b/tests/testament/tinlinemsg.nim
@@ -0,0 +1,8 @@
+discard """
+  matrix: "--errorMax:0 --styleCheck:error"
+"""
+
+proc generic_proc*[T](a_a: int) = #[tt.Error
+     ^ 'generic_proc' should be: 'genericProc'; tt.Error
+                      ^ 'a_a' should be: 'aA' ]#
+  discard
diff --git a/tests/testament/tjoinable.nim b/tests/testament/tjoinable.nim
new file mode 100644
index 000000000..c651780de
--- /dev/null
+++ b/tests/testament/tjoinable.nim
@@ -0,0 +1,9 @@
+discard """
+  output: "ok"
+"""
+
+# checks that this is joinable
+# checks that megatest allows duplicate names, see also `tests/misc/tjoinable.nim`
+doAssert defined(testing)
+doAssert defined(nimMegatest)
+echo "ok" # intentional to make sure this doesn't prevent `isJoinableSpec`
diff --git a/tests/testament/treject.nim b/tests/testament/treject.nim
new file mode 100644
index 000000000..be2db86a9
--- /dev/null
+++ b/tests/testament/treject.nim
@@ -0,0 +1,6 @@
+discard """
+action: "reject"
+"""
+
+# this line does not compile, so the test should pass, since action="reject"
+let x: int = "type mismatch"
diff --git a/tests/testament/tshould_not_work.nim b/tests/testament/tshould_not_work.nim
new file mode 100644
index 000000000..8c99510a0
--- /dev/null
+++ b/tests/testament/tshould_not_work.nim
@@ -0,0 +1,53 @@
+discard """
+  joinable: false
+"""
+
+const expected = """
+FAIL: tests/shouldfail/tccodecheck.nim
+Failure: reCodegenFailure
+Expected:
+baz
+FAIL: tests/shouldfail/tcolumn.nim
+Failure: reLinesDiffer
+FAIL: tests/shouldfail/terrormsg.nim
+Failure: reMsgsDiffer
+FAIL: tests/shouldfail/texitcode1.nim
+Failure: reExitcodesDiffer
+FAIL: tests/shouldfail/tfile.nim
+Failure: reFilesDiffer
+FAIL: tests/shouldfail/tline.nim
+Failure: reLinesDiffer
+FAIL: tests/shouldfail/tmaxcodesize.nim
+Failure: reCodegenFailure
+max allowed size: 1
+FAIL: tests/shouldfail/tnimout.nim
+Failure: reMsgsDiffer
+FAIL: tests/shouldfail/tnimoutfull.nim
+Failure: reMsgsDiffer
+FAIL: tests/shouldfail/toutput.nim
+Failure: reOutputsDiffer
+FAIL: tests/shouldfail/toutputsub.nim
+Failure: reOutputsDiffer
+FAIL: tests/shouldfail/treject.nim
+Failure: reFilesDiffer
+FAIL: tests/shouldfail/tsortoutput.nim
+Failure: reOutputsDiffer
+FAIL: tests/shouldfail/ttimeout.nim
+Failure: reTimeout
+FAIL: tests/shouldfail/tvalgrind.nim
+Failure: reExitcodesDiffer
+"""
+
+import std/[os,strformat,osproc]
+import stdtest/testutils
+
+proc main =
+  const nim = getCurrentCompilerExe()
+  let testamentExe = "bin/testament"
+  let cmd = fmt"{testamentExe} --directory:testament --colors:off --backendLogging:off --nim:{nim} category shouldfail"
+  let (outp, status) = execCmdEx(cmd)
+  doAssert status == 1, $status
+
+  let ok = greedyOrderedSubsetLines(expected, outp, allowPrefixMatch = true)
+  doAssert ok, &"\nexpected:\n{expected}\noutp:\n{outp}"
+main()
diff --git a/tests/testament/tspecialpaths.nim b/tests/testament/tspecialpaths.nim
new file mode 100644
index 000000000..3c97dc88a
--- /dev/null
+++ b/tests/testament/tspecialpaths.nim
@@ -0,0 +1,9 @@
+import stdtest/specialpaths
+import std/os
+block: # splitTestFile
+  doAssert splitTestFile("tests/fakedir/tfakename.nim") == ("fakedir", "tests/fakedir/tfakename.nim".unixToNativePath)
+  doAssert splitTestFile("/pathto/tests/fakedir/tfakename.nim") == ("fakedir", "/pathto/tests/fakedir/tfakename.nim".unixToNativePath)
+  doAssert splitTestFile(getCurrentDir() / "tests/fakedir/tfakename.nim") == ("fakedir", "tests/fakedir/tfakename.nim".unixToNativePath)
+  doAssert splitTestFile(getCurrentDir() / "sub/tests/fakedir/tfakename.nim") == ("fakedir", "sub/tests/fakedir/tfakename.nim".unixToNativePath)
+  doAssertRaises(AssertionDefect): discard splitTestFile("testsbad/fakedir/tfakename.nim")
+  doAssertRaises(AssertionDefect): discard splitTestFile("tests/tfakename.nim")
diff --git a/tests/testdata/mycert.pem b/tests/testdata/mycert.pem
index 69039fe04..61dcb685c 100644
--- a/tests/testdata/mycert.pem
+++ b/tests/testdata/mycert.pem
@@ -1,32 +1,81 @@
 -----BEGIN PRIVATE KEY-----
-MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAL9QXX/nuiFbizdI
-Uhg1D9gG0GIANENvKwdWTlOlZAoOqvjXPFLGh+87yhvkq4f5FcICkSDao2SfeZcP
-JsgD7T01owt8x48898+d91i7nIpr6IXGPyBxHOuaxAITY1D+MbbkhIGUVrEqKEOm
-qfS9cqPZaDNkx8xVef0HPCmqEme9AgMBAAECgYBxqrQCvJFQJG3QiL2N+GjTdyj0
-MR7cOf6cu2CKPifz+ccHVgpXO/Gj6Cgq7nAjt5B/1rqXhI+zxzSc1bm6+OpIfakS
-E0DLCFacECmL0v3c+XLxTtMhFZF5u7Yq0UMsuWmDSfRb4sbRjC+s+c51i5N0485k
-b3un/MDI/i/jD/YZGQJBAPLtcuMIwEblUR1uw7NFezXdauXCRFkekoSlJNvpdM/Z
-XDRcuWioek5yD8FvMpTz7H2e26Ev645JT5lIuN4Eti8CQQDJm+Qt9NYUohRsU279
-GYI3vXsXKKqmA22at4I3KRXPSeYV1vtQLYWWqGAXzgGkUEVBY0chmHyDcNwkUsNw
-svHTAkEAwOTpD/vX6bOXOD7GqKgoULozcqNScE2FXExhuzliJtTakT17f+4fyABs
-IFWynXIevBUTIqeRbJcr3HRRTwIAwwJBAJQ8XkL4IaxcG/4mPpY0ek13sZiumwKj
-xKQcx869E78tS9LFFlW2kuHafYUjQIvLRZC1aWinUO3oPsUqYW9s82cCQFjoods5
-YsWEJB2RKCT5nhyAXEZLehxF+FXr+JjLMHkuEINKTnHHKjHJ7LbMcTCKUJAcKDTA
-qZFEq5N1aT6DrAU=
+MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDgpWhnNdNSUbwJ
+39mKf+PA879K1wyT23LC3Ipo5g5vaRjB4vrSh5vzrjK5Q/z42yRDLQ2ojEdUne19
+1xMaM+WqTXnfkf9sou4VJ0HsdQ6jOU1LDRPrPvCjHLZjyMu18sGvQf4FATBwWN3n
+QhmvcbleTeyv2pgMNeQDfjuIhRJ/aCIi/WYQ466+Rcj0y3/udYX1yPYf2mszXnSE
+i2iWgdsjx0qkDU/nJhnKXhKfucAm9Ds0YfQdZYN3rEzfGkJzZUhDG1n30ghu+vUg
+qqLi6HU/HqBxKxBlJf2HdMNK4VLkJuFC+wCI0JfF6VYhcNmtZzlQy3M9WjYvbXUY
+nvMkQzUECaSaAtmbhFFfgey1Q3IEQS/j1lsVxtuGUE3YaoyaK22/CkVInDz0aka9
+CKq77x7S/V5PP/wwPV4s8XsbT6/34KemuY3oU84+bf5SBuWdzBY0hnDO9c/FaOnZ
+4yjLMQi285HLpK417Z32DmzIkb7GjKtP9WLvsWGgbgquEjcFaIT+erkN09cDjsG/
+URWbKcsQkz8v9E4zj0rxdanXoJEtg1gm+dPsiXokZOLTeaMVtDoTkWeCM/VAp25J
+MTc656QRUTSzUODK5tXQ3JgE2XIPGV/PsMi7T/q9xFBQmhUk5kfLNlQ1cy/aiTWK
+8pjHcmxbyD121AimuTtv9CFYnYtzyQIDAQABAoICAAMYZFrfs/yzYZrlObMd1f6H
+nUAjvGmhIXCr50BQwywnz46EWR5jffOal9pTpH2tT0+ZpFGJNUZmMqqENyAqTOTO
+0noRIerWR9+EvfTLHBuFo5oAISEhqeEleSHg12W3ZZHLn/tjq84we0Y/c+kl8P7q
+pfM6WNP6Ph0KNTnJU5rrzWScBzb+XB8FCSLOVwHrHqBnV3TS5p07lPFqllNUkLdq
+fI3MHSi7LqnKKAmJXqtqvBIZs2pgRrJ0bk64pue+IoPCMbgnbbRRwuTjVQE5YLww
+6NcGV+B86IRgSHyUpDa+jmYE3VoFPcIdV/F4A5fPD0wcsYbL4mk+4dkn/4OlZWqV
+NZILp2IGejKaKtM1fr7fV2IRUbGUBN/+rX4I0SRnuq6Q4Ipx8VASbpgXQPBo9XTg
+SHHsFbEu2DL8BHVgXdOy4PW6jQ0Ux2LhuJk6AQ5nIlFBYA+c8rSlZQXJbEbk1VZu
+1i7iSOn/kx3ULMjUfhI/Ddm6rQqtiLbXSubXCzu1HMPT1FG9LUfAbq6EpiVkpAk1
+TqlciBHsyz//mk2RmIEx0Bt+0bX8FFGTIUiGyrp5s4hAHbgQZbXBAUYMRzWxhZ2Q
+G0KBXx46bv3hJUb0GOgbNVxcaPnyrXaS/Hafcbx2LXlEtKiwGnC/yKJ7Hmcrt+AQ
+RTaqNU1o/bkSYC7vHMZdAoIBAQD79uYPZPv5GLCKPZ64gc3+tbFXNqkmL7qv14yD
+Gr3VubRbJe3Fx+T1cS+t9cjgOofhjFnwsDaFRoyWOYqRV/znwFsvvsDhHOLBr1u6
+qWQiF2CT1uMdXR3P6KD8h2DUVNNccxKqqIJNCR5oD/ngnnByWkQobzlsnoIdXgZm
+ozBZjGr2XUMO5dJqUxaXZwY3j4I2hk/Ka9uroApyptl+DTVbPHvjk3MzU9QKUNor
+vXEtQ8EmM8Oy6v/33HBmNs6cF5fMpgWz6u+B357OTxAfu8B42jZ18OeLrvkHFxzu
+phOB1uXvqtQ0tdksSHHWj3IIZRK7GDGudnDEZ23vbCaxH4zbAoIBAQDkPn+5N3px
+7UAECWrvT10TD3xKeqMkFhqRA9gmDE4N9AdoN6T6PzD7Tr3gOgGLq1tXjCjBqAlx
+ZIDTnih3IK3xoRk2zmhq5+LfM8LQRAxAC8IsoQMXAsmW1KlS6MR70m50pFR8NK6r
+UmOdrwVUKp3K6Mecid3LmMVLXGMUKwIJc1k7LJHtwrfi0i5xfBtiqQeaR0lJg4Zr
++zEL/4rHfcq06/P3k0+4uLKZ1LGOvwLPiTA3DADPWZbzUXo7McKOFWF/ycGQBrJq
+AJikx15dVLnB16bnHXdxrlrd0LON2R+XfT4+dfRymqZLzrBI3L39t/elTmVYnD3P
+punkmZuVwNErAoIBAQD5xOiOPibh6S2n/CmI8XQImIgx0kefSRUhFuV9WVbxtOMq
+r9CijONUw1zmb40vahYk6gKGa8fAGg1nJadNKRHVkoNSMx/0h7PpGDIwOZa/jLj6
+FLyS8SmKXiqn6nN8SJI1RQUuE1kHkJCJy7yCg80oLn7+LjOYjxCgmAJ0YDSfsGif
+zBebfws0xyTP9RrenO4RqtcR7BWYbk+tE+Tp5aIMzUpqcFJ0gRbjGv8K+QJmQpIH
+kqzegcI4LFdnm9D4PxMFlVZ14eCGt+wuy4VKT84efwIZrDN77nmCI9FUaWFRBnxt
+NsShc9rS4QWoEg6Sb88/lF47ecGTkIwUGPvJ/WKdAoIBAQClF7/zDPn4Zg+j29wJ
+dXJxUwYoKUTP2V0l/43dF5Ft7lFdRMKEcCjR3kbhZZOwnyXW0X65dP4/kt7MMt46
+LN0kpc5DIlHM4iXsJNiJJG9n9BljhqNhhZajDvfbDJrypWdX33Vs0f511YZQjERi
+eODh4DZiOCbCGaK7u/u+ns0+YLzuXHLBc9Lmsfj+BTMZzgG9ykpsbkJQ4MS9VP3h
+BlAVRYaWUWucxZwKQRqdkfRKgYTqjDgZw0e4f/rVzkxX0YdQk3L65p0up3fB2KOd
+BqfGWmJTUbEP/XmkcE0wERkUznazX0aNjucydjJ0wZZ7axIp8+bCjWD4TldoDuPH
+Ek05AoIBAECgPfBHLQTAsI+wHbFsu/are28BOiJCSEXjRv88CbUbj/qgAppFuXbx
+900WwJ1rVWd5x3LFa3VfyuAqYMi5jzmX9kWgEsC0WhgfyIRFiynw45LlcT4u3fWg
+vJEx01lGgFVjnYfFUDS9d1MuiXGxIHrNhzHOP2x2CsS5vrFHav7iwG9YULEk8tJr
+My0wzjF3UJ2/5DjGK56WuzauLYKrQ6Faw8dWUy4e/bNYId8wglhQQW548JwJEGmq
+nq+EzTfEupXH57Bw7MGEOfdlhv98zNT9VcvBAN09vHeF3Hh6AM4aiGSUIt2HIkto
+zvw+fqZ2Sk9O5qva+KE1QMVtY1EICI8=
 -----END PRIVATE KEY-----
 -----BEGIN CERTIFICATE-----
-MIICgDCCAemgAwIBAgIJANpVfZSDAyNgMA0GCSqGSIb3DQEBBQUAMFkxCzAJBgNV
-BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
-aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xMjA2MDMxMjI4
-MDhaFw0xMzA2MDMxMjI4MDhaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l
-LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV
-BAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAv1Bdf+e6
-IVuLN0hSGDUP2AbQYgA0Q28rB1ZOU6VkCg6q+Nc8UsaH7zvKG+Srh/kVwgKRINqj
-ZJ95lw8myAPtPTWjC3zHjzz3z533WLucimvohcY/IHEc65rEAhNjUP4xtuSEgZRW
-sSooQ6ap9L1yo9loM2THzFV5/Qc8KaoSZ70CAwEAAaNQME4wHQYDVR0OBBYEFF2n
-Of61swO+XSNrYb4T02tGx8afMB8GA1UdIwQYMBaAFF2nOf61swO+XSNrYb4T02tG
-x8afMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAEPHofdf4acaph5/e
-+BzZGsMfRqdPgwp5sxjFKeTQI1A49VL7ykkb0iLKGfKZtvE8MjMrYjzt20E2bIZj
-8eCivT6TbNrVRoACCly/lH9fZfWOG6dBu/85IrTAhSKi8yjbRzmjWUkdrcEJ+ZtV
-1cahfFar4l4QwYgqp2pDd6ie+zE=
+MIIFCTCCAvGgAwIBAgIUKqDcJ71wiMObIQ5sga2sZItNseowDQYJKoZIhvcNAQEL
+BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIyMDUwNDExMzAwN1oXDTIyMDYw
+MzExMzAwN1owFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF
+AAOCAg8AMIICCgKCAgEA4KVoZzXTUlG8Cd/Zin/jwPO/StcMk9tywtyKaOYOb2kY
+weL60oeb864yuUP8+NskQy0NqIxHVJ3tfdcTGjPlqk1535H/bKLuFSdB7HUOozlN
+Sw0T6z7woxy2Y8jLtfLBr0H+BQEwcFjd50IZr3G5Xk3sr9qYDDXkA347iIUSf2gi
+Iv1mEOOuvkXI9Mt/7nWF9cj2H9prM150hItoloHbI8dKpA1P5yYZyl4Sn7nAJvQ7
+NGH0HWWDd6xM3xpCc2VIQxtZ99IIbvr1IKqi4uh1Px6gcSsQZSX9h3TDSuFS5Cbh
+QvsAiNCXxelWIXDZrWc5UMtzPVo2L211GJ7zJEM1BAmkmgLZm4RRX4HstUNyBEEv
+49ZbFcbbhlBN2GqMmittvwpFSJw89GpGvQiqu+8e0v1eTz/8MD1eLPF7G0+v9+Cn
+prmN6FPOPm3+UgblncwWNIZwzvXPxWjp2eMoyzEItvORy6SuNe2d9g5syJG+xoyr
+T/Vi77FhoG4KrhI3BWiE/nq5DdPXA47Bv1EVmynLEJM/L/ROM49K8XWp16CRLYNY
+JvnT7Il6JGTi03mjFbQ6E5FngjP1QKduSTE3OuekEVE0s1DgyubV0NyYBNlyDxlf
+z7DIu0/6vcRQUJoVJOZHyzZUNXMv2ok1ivKYx3JsW8g9dtQIprk7b/QhWJ2Lc8kC
+AwEAAaNTMFEwHQYDVR0OBBYEFEZcUeqH6MfIzC56BlD3NSs2mCgGMB8GA1UdIwQY
+MBaAFEZcUeqH6MfIzC56BlD3NSs2mCgGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
+hvcNAQELBQADggIBADGc7WONP6I6Trb7ici9fQ9qT3wh/RGDcmUDmDtARn9SFtOF
+hsbszOMZg1Flj10fuD6OYDonKz4rv+Ieo5VkAYXxxd3J+bx2x1pqd1YSIsvugTwv
+pnx39uBR9cjOmt4W7RyzhFnXoVfuSBE6LpkBUjcrqi5xwrQ31mOSCPwe8uDZYEWS
+pX49MiHXGTyjQ481QLiOtTBZJa5igfnHUJbJbyZMa86zBQ/clS7+OeDwkvaEpjov
+2VQf3QouVLghfLZYWSxWdEKD9+IWHn8rV6qksEb/Ogu4ZtzDRGqJow4j0DeSSEu7
+ns1YeT2mVTFwHjGXCWS+0iE885NDVX/b5YptlwH5PW7aqeXyCS9Hrd1C1GnXoXGp
+NHltYRTyNWm974xWg7eu2gbbB8Ng02chXysdkBq7l+7OyA0a2EfX3Cbz3/49+Mqn
+viqwNO5toSHVCdfV9Jd0p0CcqryYgyt2YNpJB+2nUQpiW4jviAs49PZg2PpCVw/2
+0cqtaPeUh26Si8UzDOuT697PIuGkZ9Q9QVwccVXtCyA0UpJ13P0fMrA+yEMhtwSs
+k1tRm0pUQa6t3v26/cAy+kMhviHBJFwi5dx+y3OMvqQqpQJrgfZawm/o2ZQHy1KP
+8m4ngrJzb13evKf216qCwllmQo6Ts4yeI1Ddx8UpdX7RUWpD8Uw4zSi7Th4r
 -----END CERTIFICATE-----
diff --git a/tests/threads/t7172.nim b/tests/threads/t7172.nim
new file mode 100644
index 000000000..87e89417b
--- /dev/null
+++ b/tests/threads/t7172.nim
@@ -0,0 +1,34 @@
+discard """
+  disabled: i386
+  output: '''
+In doStuff()
+In initProcess()
+TEST
+initProcess() done
+Crashes before getting here!
+'''
+  joinable: false
+"""
+
+import std/os
+import std/typedthreads
+
+proc whatever() {.thread, nimcall.} =
+  echo("TEST")
+
+proc initProcess(): void =
+  echo("In initProcess()")
+  var thread: Thread[void]
+  createThread(thread, whatever)
+  joinThread(thread)
+  echo("initProcess() done")
+
+proc doStuff(): void =
+  echo("In doStuff()")
+  # ...
+  initProcess()
+  sleep(500)
+  # ...
+  echo("Crashes before getting here!")
+
+doStuff()
diff --git a/tests/threads/t8535.nim b/tests/threads/t8535.nim
new file mode 100644
index 000000000..a4296df11
--- /dev/null
+++ b/tests/threads/t8535.nim
@@ -0,0 +1,30 @@
+discard """
+  disabled: i386
+  output: '''0
+hello'''
+"""
+
+type
+  CircAlloc*[Size: static[int], T] = tuple
+    baseArray: array[Size,T]
+    index: uint16
+
+type
+  Job = object of RootObj
+
+var foo {.threadvar.}: CircAlloc[1, Job]
+
+when true:
+  echo foo.index
+
+
+# bug #10795
+import asyncdispatch
+import threadpool
+
+proc f1() =
+  waitFor sleepAsync(20)
+  echo "hello"
+
+spawn f1()
+sync()
diff --git a/tests/threads/tactors.nim b/tests/threads/tactors.nim
deleted file mode 100644
index 45a03ebb7..000000000
--- a/tests/threads/tactors.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-discard """
-  outputsub: "150"
-"""
-
-import actors
-
-var
-  pool: TActorPool[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 b011ef140..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: TActorPool[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/threadex.nim b/tests/threads/threadex.nim
index 679bfcb12..90119aee7 100644
--- a/tests/threads/threadex.nim
+++ b/tests/threads/threadex.nim
@@ -1,5 +1,6 @@
 discard """
-  outputsub: "All rights reserved."
+  disabled: i386
+  outputsub: "Just a simple text for test"
 """
 
 type
@@ -28,13 +29,13 @@ proc consume() {.thread.} =
 proc produce() {.thread.} =
   prodId = getThreadId()
   var m: TMsg
-  var input = open("readme.txt")
+  var input = open("tests/dummy.txt")
   var line = ""
   while input.readLine(line):
     m.data = line
     chan.send(m)
   close(input)
-  m.k = mEof
+  m = TMsg(k: mEof)
   chan.send(m)
 
 open(chan)
diff --git a/tests/threads/tjsthreads.nim b/tests/threads/tjsthreads.nim
new file mode 100644
index 000000000..5122c9cd6
--- /dev/null
+++ b/tests/threads/tjsthreads.nim
@@ -0,0 +1,6 @@
+discard """
+  targets: "js"
+  matrix: "--threads:on"
+"""
+
+echo 123
diff --git a/tests/threads/tmanyjoin.nim b/tests/threads/tmanyjoin.nim
index 2c1cda494..af5bc460e 100644
--- a/tests/threads/tmanyjoin.nim
+++ b/tests/threads/tmanyjoin.nim
@@ -1,4 +1,5 @@
 discard """
+  disabled: i386
   outputsub: "129"
 """
 
@@ -12,7 +13,7 @@ type
 
 const
   ThreadsCount = 129
-  SleepTime = 1000
+  SleepTime = 250
 
 proc worker(p: Marker) {.thread.} =
   acquire(p.lock)
diff --git a/tests/threads/tmembug.nim b/tests/threads/tmembug.nim
new file mode 100644
index 000000000..3618f0ecc
--- /dev/null
+++ b/tests/threads/tmembug.nim
@@ -0,0 +1,51 @@
+
+import std / [atomics, strutils, sequtils]
+
+type
+  BackendMessage* = object
+    field*: seq[int]
+
+var
+  chan1: Channel[BackendMessage]
+  chan2: Channel[BackendMessage]
+
+chan1.open()
+chan2.open()
+
+proc routeMessage*(msg: BackendMessage) =
+  discard chan2.trySend(msg)
+
+var
+  recv: Thread[void]
+  stopToken: Atomic[bool]
+
+proc recvMsg() =
+  while not stopToken.load(moRelaxed):
+    let resp = chan1.tryRecv()
+    if resp.dataAvailable:
+      routeMessage(resp.msg)
+      echo "child consumes ", formatSize getOccupiedMem()
+
+createThread[void](recv, recvMsg)
+
+const MESSAGE_COUNT = 100
+
+proc main() =
+  let msg: BackendMessage = BackendMessage(field: (0..500).toSeq())
+  for j in 0..0: #100:
+    echo "New iteration"
+
+    for _ in 1..MESSAGE_COUNT:
+      chan1.send(msg)
+    echo "After sending"
+
+    var counter = 0
+    while counter < MESSAGE_COUNT:
+      let resp = recv(chan2)
+      counter.inc
+    echo "After receiving ", formatSize getOccupiedMem()
+
+  stopToken.store true, moRelaxed
+  joinThreads(recv)
+
+main()
diff --git a/tests/threads/tonthreadcreation.nim b/tests/threads/tonthreadcreation.nim
index f588a21c9..61529477d 100644
--- a/tests/threads/tonthreadcreation.nim
+++ b/tests/threads/tonthreadcreation.nim
@@ -1,4 +1,6 @@
 discard """
+  disabled: i386
+  matrix: "--mm:refc; --mm:orc --deepcopy:on"
   output: '''some string here
 dying some string here'''
 """
diff --git a/tests/threads/tracy_allocator.nim b/tests/threads/tracy_allocator.nim
index e8f0ec927..f3b39f4dc 100644
--- a/tests/threads/tracy_allocator.nim
+++ b/tests/threads/tracy_allocator.nim
@@ -1,4 +1,5 @@
 discard """
+  disabled: i386
   output: '''true'''
 """
 
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/threads/treusetvar.nim b/tests/threads/treusetvar.nim
index 672da6bdd..f0337801a 100644
--- a/tests/threads/treusetvar.nim
+++ b/tests/threads/treusetvar.nim
@@ -1,8 +1,9 @@
 discard """
-  outputsub: "129"
+  disabled: i386
+  outputsub: "65"
 """
 
-import os, locks
+import locks
 
 type
   MarkerObj = object
@@ -11,7 +12,7 @@ type
   Marker = ptr MarkerObj
 
 const
-  ThreadsCount = 129
+  ThreadsCount = 65
 
 proc worker(p: Marker) {.thread.} =
   acquire(p.lock)
diff --git a/tests/threads/tthreadanalysis.nim b/tests/threads/tthreadanalysis.nim
index 8aacc71d1..7fc3593c8 100644
--- a/tests/threads/tthreadanalysis.nim
+++ b/tests/threads/tthreadanalysis.nim
@@ -1,8 +1,6 @@
 discard """
-  disabled: yes
-  outputsub: "101"
   errormsg: "'threadFunc' is not GC-safe"
-  line: 39
+  line: 38
   cmd: "nim $target --hints:on --threads:on $options $file"
 """
 
@@ -15,7 +13,7 @@ proc doNothing() = discard
 
 type
   PNode = ref TNode
-  TNode = object {.pure.}
+  TNode {.pure.} = object
     le, ri: PNode
     data: string
 
diff --git a/tests/threads/tthreadanalysis2.nim b/tests/threads/tthreadanalysis2.nim
deleted file mode 100644
index c1ec3ae39..000000000
--- a/tests/threads/tthreadanalysis2.nim
+++ /dev/null
@@ -1,52 +0,0 @@
-discard """
-  file: "tthreadanalysis2.nim"
-  line: 37
-  errormsg: "'threadFunc' is not GC-safe"
-  cmd: "nim $target --hints:on --threads:on $options $file"
-"""
-
-import os
-
-var
-  thr: array[0..5, Thread[tuple[a, b: int]]]
-
-proc doNothing() = discard
-
-type
-  PNode = ref TNode
-  TNode = object {.pure.}
-    le, ri: PNode
-    data: string
-
-var
-  root: PNode
-
-proc buildTree(depth: int): PNode =
-  if depth == 3: return nil
-  new(result)
-  result.le = buildTree(depth-1)
-  result.ri = buildTree(depth-1)
-  result.data = $depth
-
-proc echoLeTree(n: PNode) =
-  var it = n
-  while it != nil:
-    echo it.data
-    it = it.le
-
-proc threadFunc(interval: tuple[a, b: int]) {.thread.} =
-  doNothing()
-  for i in interval.a..interval.b:
-    var r = buildTree(i)
-    echoLeTree(r) # for local data
-  root = buildTree(2) # BAD!
-  #echoLeTree(root) # and the same for foreign data :-)
-
-proc main =
-  root = buildTree(5)
-  for i in 0..high(thr):
-    createThread(thr[i], threadFunc, (i*100, i*100+50))
-  joinThreads(thr)
-
-main()
-
diff --git a/tests/threads/tthreadheapviolation1.nim b/tests/threads/tthreadheapviolation1.nim
index 59ecb742c..379bd55e6 100644
--- a/tests/threads/tthreadheapviolation1.nim
+++ b/tests/threads/tthreadheapviolation1.nim
@@ -1,6 +1,6 @@
 discard """
-  line: 11
   errormsg: "'horrible' is not GC-safe"
+  line: 11
   cmd: "nim $target --hints:on --threads:on $options $file"
 """
 
@@ -16,5 +16,3 @@ proc horrible() {.thread.} =
 
 createThread[void](t, horrible)
 joinThread(t)
-
-
diff --git a/tests/threads/tthreadvars.nim b/tests/threads/tthreadvars.nim
index 81aa2e5ec..745e3562c 100644
--- a/tests/threads/tthreadvars.nim
+++ b/tests/threads/tthreadvars.nim
@@ -1,4 +1,5 @@
 discard """
+disabled: i386
 output: '''
 10
 1111
diff --git a/tests/threads/ttryrecv.nim b/tests/threads/ttryrecv.nim
index 5ef1a3c10..fcff94e78 100644
--- a/tests/threads/ttryrecv.nim
+++ b/tests/threads/ttryrecv.nim
@@ -1,18 +1,18 @@
 discard """
-  disabled: yes
+  matrix: "--mm:refc"
   outputsub: "channel is empty"
 """
 
 # bug #1816
 
-from random import random
+from random import rand
 from os import sleep
 
 type PComm = ptr Channel[int]
 
 proc doAction(outC: PComm) {.thread.} =
-  for i in 0.. <5:
-    sleep(random(100))
+  for i in 0 ..< 5:
+    sleep(rand(50))
     send(outC[], i)
 
 var
diff --git a/tests/tools/compile/config.nims b/tests/tools/compile/config.nims
new file mode 100644
index 000000000..a19545668
--- /dev/null
+++ b/tests/tools/compile/config.nims
@@ -0,0 +1 @@
+switch("path", "$lib/../")
\ No newline at end of file
diff --git a/tests/tools/compile/readme.md b/tests/tools/compile/readme.md
new file mode 100644
index 000000000..cb5058e02
--- /dev/null
+++ b/tests/tools/compile/readme.md
@@ -0,0 +1 @@
+Test whether the tools compile.
\ No newline at end of file
diff --git a/tests/tools/compile/tdeps.nim b/tests/tools/compile/tdeps.nim
new file mode 100644
index 000000000..971ca1b8e
--- /dev/null
+++ b/tests/tools/compile/tdeps.nim
@@ -0,0 +1,5 @@
+discard """
+  action: compile
+"""
+
+include tools/deps
diff --git a/tests/tools/compile/tdetect.nim b/tests/tools/compile/tdetect.nim
new file mode 100644
index 000000000..253fc0d39
--- /dev/null
+++ b/tests/tools/compile/tdetect.nim
@@ -0,0 +1,5 @@
+discard """
+  action: compile
+"""
+
+include tools/detect/detect
diff --git a/tests/tools/compile/tkoch.nim b/tests/tools/compile/tkoch.nim
new file mode 100644
index 000000000..008721dc0
--- /dev/null
+++ b/tests/tools/compile/tkoch.nim
@@ -0,0 +1,5 @@
+discard """
+  action: compile
+"""
+
+include koch
\ No newline at end of file
diff --git a/tests/tools/compile/tniminst.nim b/tests/tools/compile/tniminst.nim
new file mode 100644
index 000000000..78c736af0
--- /dev/null
+++ b/tests/tools/compile/tniminst.nim
@@ -0,0 +1,5 @@
+discard """
+  action: compile
+"""
+
+include tools/niminst/niminst
\ No newline at end of file
diff --git a/tests/tools/config.nims b/tests/tools/config.nims
new file mode 100644
index 000000000..0f0cba8b4
--- /dev/null
+++ b/tests/tools/config.nims
@@ -0,0 +1,3 @@
+--d:nimPreviewSlimSystem
+--d:nimPreviewCstringConversion
+--d:nimPreviewProcConversion
diff --git a/tests/tools/dontmentionme.nim b/tests/tools/dontmentionme.nim
new file mode 100644
index 000000000..218823aca
--- /dev/null
+++ b/tests/tools/dontmentionme.nim
@@ -0,0 +1,3 @@
+{.used.}
+
+proc nothing* = echo "nothing to see here"
diff --git a/tests/tools/second.nim b/tests/tools/second.nim
new file mode 100644
index 000000000..ee94af25f
--- /dev/null
+++ b/tests/tools/second.nim
@@ -0,0 +1,3 @@
+import tables
+
+let dataEx* = {1: 2, 3: 4}.toTable
diff --git a/tests/tools/tlinter.nim b/tests/tools/tlinter.nim
new file mode 100644
index 000000000..16f67905e
--- /dev/null
+++ b/tests/tools/tlinter.nim
@@ -0,0 +1,40 @@
+discard """
+  cmd: '''nim c --styleCheck:hint $file'''
+  nimout: '''
+tlinter.nim(25, 1) Hint: 'tyPE' should be: 'type' [Name]
+tlinter.nim(21, 14) Hint: 'nosideeffect' should be: 'noSideEffect' [Name]
+tlinter.nim(21, 28) Hint: 'myown' should be: 'myOwn' [template declared in tlinter.nim(19, 9)] [Name]
+tlinter.nim(21, 35) Hint: 'inLine' should be: 'inline' [Name]
+tlinter.nim(23, 1) Hint: 'foO' should be: 'foo' [proc declared in tlinter.nim(21, 6)] [Name]
+tlinter.nim(27, 14) Hint: 'Foo_bar' should be: 'FooBar' [type declared in tlinter.nim(25, 6)] [Name]
+tlinter.nim(29, 6) Hint: 'someVAR' should be: 'someVar' [var declared in tlinter.nim(27, 5)] [Name]
+tlinter.nim(32, 7) Hint: 'i_fool' should be: 'iFool' [Name]
+tlinter.nim(39, 5) Hint: 'meh_field' should be: 'mehField' [Name]
+'''
+  action: "compile"
+"""
+
+
+
+{.pragma: myOwn.}
+
+proc foo() {.nosideeffect, myown, inLine.} = debugEcho "hi"
+
+foO()
+
+tyPE FooBar = string
+
+var someVar: Foo_bar = "a"
+
+echo someVAR
+
+proc main =
+  var i_fool = 34
+  echo i_fool
+
+main()
+
+type
+  Foo = object
+    meh_field: int
+
diff --git a/tests/tools/tnimgrep.nim b/tests/tools/tnimgrep.nim
new file mode 100644
index 000000000..890a36e79
--- /dev/null
+++ b/tests/tools/tnimgrep.nim
@@ -0,0 +1,404 @@
+discard """
+  output: '''
+
+[Suite] nimgrep filesystem
+
+[Suite] nimgrep contents filtering
+'''
+"""
+## Authors: quantimnot, a-mr
+
+import std/[osproc, os, streams, unittest, strutils]
+
+import std/syncio
+
+#=======
+# setup
+#=======
+
+var process: Process
+var ngStdOut, ngStdErr: string
+var ngExitCode: int
+let previousDir = getCurrentDir()
+let tempDir = getTempDir()
+let testFilesRoot = tempDir / "nimgrep_test_files"
+
+template nimgrep(optsAndArgs): untyped =
+  process = startProcess(previousDir / "bin/nimgrep " & optsAndArgs,
+                         options = {poEvalCommand})
+  ngExitCode = process.waitForExit
+  ngStdOut = process.outputStream.readAll
+  ngStdErr = process.errorStream.readAll
+
+func fixSlash(s: string): string =
+  if DirSep == '/':
+    result = s
+  else:  # on Windows
+    result = s.replace('/', DirSep)
+
+func initString(len = 1000, val = ' '): string =
+  result = newString(len)
+  for i in 0..<len:
+    result[i] = val
+
+# Create test file hierarchy.
+createDir testFilesRoot
+setCurrentDir testFilesRoot
+createDir "a" / "b"
+createDir "c" / "b"
+createDir ".hidden"
+writeFile("do_not_create_another_file_with_this_pattern_KJKJHSFSFKASHFBKAF", "PATTERN")
+writeFile("a" / "b" / "only_the_pattern", "PATTERN")
+writeFile("c" / "b" / "only_the_pattern", "PATTERN")
+writeFile(".hidden" / "only_the_pattern", "PATTERN")
+writeFile("null_in_first_1k", "\0PATTERN")
+writeFile("null_after_first_1k", initString(1000) & "\0")
+writeFile("empty", "")
+writeFile("context_match_filtering", """
+-
+CONTEXTPAT
+-
+PATTERN
+-
+-
+-
+
+-
+-
+-
+PATTERN
+-
+-
+-
+""")
+writeFile("only_the_pattern.txt", "PATTERN")
+writeFile("only_the_pattern.ascii", "PATTERN")
+
+
+#=======
+# tests
+#=======
+
+suite "nimgrep filesystem":
+
+  test "`--filename` with matching file":
+    nimgrep "-r --filename:KJKJHSFSFKASHFBKAF PATTERN"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == fixSlash dedent"""
+        ./do_not_create_another_file_with_this_pattern_KJKJHSFSFKASHFBKAF:1: PATTERN
+        1 matches
+        """
+
+
+  test "`--dirname` with matching dir":
+    nimgrep "-r --dirname:.hid PATTERN"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == fixSlash dedent"""
+        .hidden/only_the_pattern:1: PATTERN
+        1 matches
+        """
+
+  let only_the_pattern = fixSlash dedent"""
+        a/b/only_the_pattern:1: PATTERN
+        c/b/only_the_pattern:1: PATTERN
+        2 matches
+        """
+
+  let only_a = fixSlash dedent"""
+        a/b/only_the_pattern:1: PATTERN
+        1 matches
+        """
+
+  test "`--dirname` with matching grandparent path segment":
+    nimgrep "-r --dirname:a PATTERN"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == only_a
+
+  test "`--dirpath` with matching grandparent path segment":
+    nimgrep "-r --dirp:a PATTERN"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == only_a
+
+  test "`--dirpath` with matching grandparent path segment":
+    nimgrep "-r --dirpath:a/b PATTERN".fixSlash
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == only_a
+
+
+  test "`--dirname` with matching parent path segment":
+    nimgrep "-r --dirname:b PATTERN"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == only_the_pattern
+
+  test "`--dirpath` with matching parent path segment":
+    nimgrep "-r --dirpath:b PATTERN"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == only_the_pattern
+
+
+  let patterns_without_directory_a_b = fixSlash dedent"""
+        ./context_match_filtering:4: PATTERN
+        ./context_match_filtering:12: PATTERN
+        ./do_not_create_another_file_with_this_pattern_KJKJHSFSFKASHFBKAF:1: PATTERN
+        ./null_in_first_1k:1: """ & "\0PATTERN\n" & dedent"""
+        ./only_the_pattern.ascii:1: PATTERN
+        ./only_the_pattern.txt:1: PATTERN
+        .hidden/only_the_pattern:1: PATTERN
+        c/b/only_the_pattern:1: PATTERN
+        8 matches
+        """
+
+  let patterns_without_directory_b = fixSlash dedent"""
+        ./context_match_filtering:4: PATTERN
+        ./context_match_filtering:12: PATTERN
+        ./do_not_create_another_file_with_this_pattern_KJKJHSFSFKASHFBKAF:1: PATTERN
+        ./null_in_first_1k:1: """ & "\0PATTERN\n" & dedent"""
+        ./only_the_pattern.ascii:1: PATTERN
+        ./only_the_pattern.txt:1: PATTERN
+        .hidden/only_the_pattern:1: PATTERN
+        7 matches
+        """
+
+  test "`--ndirname` not matching grandparent path segment":
+    nimgrep "-r --ndirname:a PATTERN"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == patterns_without_directory_a_b
+
+  test "`--ndirname` not matching parent path segment":
+    nimgrep "-r --ndirname:b PATTERN"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == patterns_without_directory_b
+
+  test "`--notdirpath` not matching grandparent path segment":
+    nimgrep "-r --notdirpath:a PATTERN"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == patterns_without_directory_a_b
+
+  test "`--notdirpath` not matching parent path segment":
+    nimgrep "-r --ndirp:b PATTERN"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == patterns_without_directory_b
+
+  test "`--notdirpath` with matching grandparent/parent path segment":
+    nimgrep "-r --ndirp:a/b PATTERN".fixSlash
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == patterns_without_directory_a_b
+
+
+  test "`--text`, `-t`, `--bin:off` with file containing a null in first 1k chars":
+    nimgrep "-r --text PATTERN null_in_first_1k"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == "0 matches\n"
+    checkpoint "`--text`"
+    nimgrep "-r -t PATTERN null_in_first_1k"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == "0 matches\n"
+    checkpoint "`-t`"
+    nimgrep "-r --bin:off PATTERN null_in_first_1k"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == "0 matches\n"
+    checkpoint "`--binary:off`"
+
+
+  test "`--bin:only` with file containing a null in first 1k chars":
+    nimgrep "--bin:only -@ PATTERN null_in_first_1k null_after_first_1k only_the_pattern.txt"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == dedent"""
+        null_in_first_1k:1: ^@PATTERN
+        1 matches
+        """
+
+
+  test "`--bin:only` with file containing a null after first 1k chars":
+    nimgrep "--bin:only PATTERN null_after_first_1k only_the_pattern.txt"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == "0 matches\n"
+
+
+  # TODO: we need to throw a warning if e.g. both extension was provided and
+  # inappropriate filename was directly provided via command line
+  #
+  #  test "`--ext:doesnotexist` without a matching file":
+  #    # skip() # FIXME: this test fails
+  #    nimgrep "--ext:doesnotexist PATTERN context_match_filtering only_the_pattern.txt"
+  #    check ngExitCode == 0
+  #    check ngStdErr.len == 0
+  #    check ngStdOut == """
+  #0 matches
+  #"""
+  #
+  #
+  #  test "`--ext:txt` with a matching file":
+  #    nimgrep "--ext:txt PATTERN context_match_filtering only_the_pattern.txt"
+  #    check ngExitCode == 0
+  #    check ngStdErr.len == 0
+  #    check ngStdOut == """
+  #only_the_pattern.txt:1: PATTERN
+  #1 matches
+  #"""
+  #
+  #
+  #  test "`--ext:txt|doesnotexist` with some matching files":
+  #    nimgrep "--ext:txt|doesnotexist PATTERN context_match_filtering only_the_pattern.txt only_the_pattern.ascii"
+  #    check ngExitCode == 0
+  #    check ngStdErr.len == 0
+  #    check ngStdOut == """
+  #only_the_pattern.txt:1: PATTERN
+  #1 matches
+  #"""
+  #
+  #
+  #  test "`--ext` with some matching files":
+  #    nimgrep "--ext PATTERN context_match_filtering only_the_pattern.txt only_the_pattern.ascii"
+  #    check ngExitCode == 0
+  #    check ngStdErr.len == 0
+  #    check ngStdOut == """
+  #context_match_filtering:4: PATTERN
+  #context_match_filtering:12: PATTERN
+  #2 matches
+  #"""
+  #
+  #
+  #  test "`--ext:txt --ext` with some matching files":
+  #    nimgrep "--ext:txt --ext PATTERN context_match_filtering only_the_pattern.txt only_the_pattern.ascii"
+  #    check ngExitCode == 0
+  #    check ngStdErr.len == 0
+  #    check ngStdOut == """
+  #context_match_filtering:4: PATTERN
+  #context_match_filtering:12: PATTERN
+  #only_the_pattern.txt:1: PATTERN
+  #3 matches
+  #"""
+
+
+suite "nimgrep contents filtering":
+
+  test "`--inFile` with matching file":
+    nimgrep "-r --inf:CONTEXTPAT PATTERN"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == fixSlash dedent"""
+        ./context_match_filtering:4: PATTERN
+        ./context_match_filtering:12: PATTERN
+        2 matches
+        """
+
+
+  test "`--notinFile` with matching files":
+    nimgrep "-r --ninf:CONTEXTPAT PATTERN"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == fixSlash dedent"""
+        ./do_not_create_another_file_with_this_pattern_KJKJHSFSFKASHFBKAF:1: PATTERN
+        ./null_in_first_1k:1: """ & "\0PATTERN\n" & dedent"""
+        ./only_the_pattern.ascii:1: PATTERN
+        ./only_the_pattern.txt:1: PATTERN
+        .hidden/only_the_pattern:1: PATTERN
+        a/b/only_the_pattern:1: PATTERN
+        c/b/only_the_pattern:1: PATTERN
+        7 matches
+        """
+
+
+  test "`--inContext` with missing context option":
+    # Using `--inContext` implies default -c:1 is used
+    nimgrep "-r --inContext:CONTEXTPAT PATTERN"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == "0 matches\n"
+
+
+  test "`--inContext` with PAT matching PATTERN":
+    # This tests the scenario where PAT always matches PATTERN and thus
+    # has the same effect as excluding the `inContext` option.
+    # I'm not sure of the desired behaviour here.
+    nimgrep "--context:2 --inc:PAT PATTERN context_match_filtering"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == dedent"""
+        context_match_filtering:2  CONTEXTPAT
+        context_match_filtering:3  -
+        context_match_filtering:4: PATTERN
+        context_match_filtering:5  -
+        context_match_filtering:6  -
+
+        context_match_filtering:10  -
+        context_match_filtering:11  -
+        context_match_filtering:12: PATTERN
+        context_match_filtering:13  -
+        context_match_filtering:14  -
+
+        2 matches
+        """
+
+
+  test "`--inContext` with PAT in context":
+    nimgrep "--context:2 --inContext:CONTEXTPAT PATTERN context_match_filtering"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == dedent"""
+        context_match_filtering:2  CONTEXTPAT
+        context_match_filtering:3  -
+        context_match_filtering:4: PATTERN
+        context_match_filtering:5  -
+        context_match_filtering:6  -
+
+        1 matches
+        """
+
+
+  test "`--notinContext` with PAT matching some contexts":
+    nimgrep "--context:2 --ninContext:CONTEXTPAT PATTERN context_match_filtering"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == dedent"""
+        context_match_filtering:10  -
+        context_match_filtering:11  -
+        context_match_filtering:12: PATTERN
+        context_match_filtering:13  -
+        context_match_filtering:14  -
+
+        1 matches
+        """
+
+
+  test "`--notinContext` with PAT not matching any of the contexts":
+    nimgrep "--context:1 --ninc:CONTEXTPAT PATTERN context_match_filtering"
+    check ngExitCode == 0
+    check ngStdErr.len == 0
+    check ngStdOut == dedent"""
+        context_match_filtering:3  -
+        context_match_filtering:4: PATTERN
+        context_match_filtering:5  -
+
+        context_match_filtering:11  -
+        context_match_filtering:12: PATTERN
+        context_match_filtering:13  -
+
+        2 matches
+        """
+
+
+#=========
+# cleanup
+#=========
+
+setCurrentDir previousDir
+removeDir testFilesRoot
diff --git a/tests/tools/tnimscriptwithmacro.nims b/tests/tools/tnimscriptwithmacro.nims
new file mode 100644
index 000000000..8b97f0769
--- /dev/null
+++ b/tests/tools/tnimscriptwithmacro.nims
@@ -0,0 +1,22 @@
+discard """
+cmd: "nim e $file"
+output: '''
+foobar
+nothing
+hallo
+"""
+
+# this test ensures that the mode is resetted correctly to repr
+
+import macros
+
+macro foobar(): void =
+  result = newCall(bindSym"echo", newLit("nothing"))
+
+echo "foobar"
+
+let x = 123
+
+foobar()
+
+exec "echo hallo"
diff --git a/tests/tools/tunused_imports.nim b/tests/tools/tunused_imports.nim
new file mode 100644
index 000000000..539608ad6
--- /dev/null
+++ b/tests/tools/tunused_imports.nim
@@ -0,0 +1,41 @@
+discard """
+  cmd: '''nim c --hint:Processing:off $file'''
+  nimout: '''
+tunused_imports.nim(14, 10) Warning: BEGIN [User]
+tunused_imports.nim(41, 10) Warning: END [User]
+tunused_imports.nim(37, 8) Warning: imported and not used: 'strutils' [UnusedImport]
+tunused_imports.nim(38, 13) Warning: imported and not used: 'strtabs' [UnusedImport]
+tunused_imports.nim(38, 22) Warning: imported and not used: 'cstrutils' [UnusedImport]
+tunused_imports.nim(39, 12) Warning: imported and not used: 'macrocache' [UnusedImport]
+'''
+  action: "compile"
+"""
+
+{.warning: "BEGIN".}
+
+# bug #12885
+
+import tables, second
+
+template test(key: int): untyped =
+  `[]`(dataEx, key)
+
+echo test(1)
+
+import net, dontmentionme
+
+echo AF_UNIX
+
+import macros
+# bug #11809
+macro bar(): untyped =
+  template baz() = discard
+  result = getAst(baz())
+
+bar()
+
+import strutils
+import std/[strtabs, cstrutils]
+import std/macrocache
+
+{.warning: "END".}
diff --git a/tests/trmacros/targlist.nim b/tests/trmacros/targlist.nim
deleted file mode 100644
index 46235dab1..000000000
--- a/tests/trmacros/targlist.nim
+++ /dev/null
@@ -1,9 +0,0 @@
-discard """
-  output: "12false3ha"
-"""
-
-proc f(x: varargs[string, `$`]) = discard
-template optF{f(x)}(x: varargs[untyped]) =
-  writeLine(stdout, x)
-
-f 1, 2, false, 3, "ha"
diff --git a/tests/trmacros/tcse.nim b/tests/trmacros/tcse.nim
deleted file mode 100644
index 315570d8f..000000000
--- a/tests/trmacros/tcse.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-discard """
-  output: "4"
-"""
-
-template cse{f(a, a, x)}(a: typed{(nkDotExpr|call|nkBracketExpr)&noSideEffect},
-                         f: typed, x: varargs[typed]): untyped =
-  let aa = a
-  f(aa, aa, x)+4
-
-var
-  a: array[0..10, int]
-  i = 3
-echo a[i] + a[i]
diff --git a/tests/trmacros/tdisallowif.nim b/tests/trmacros/tdisallowif.nim
index 18dfd1c82..8d83f6ddf 100644
--- a/tests/trmacros/tdisallowif.nim
+++ b/tests/trmacros/tdisallowif.nim
@@ -1,7 +1,6 @@
 discard """
+  errormsg: "usage of 'disallowIf' is an {.error.} defined at tdisallowif.nim(10, 1)"
   line: 24
-  errormsg: "usage of 'disallowIf' is a user-defined error"
-  disabled: true
 """
 
 template optZero{x+x}(x: int): int = x*3
@@ -11,7 +10,7 @@ template optSubstr1{x = substr(x, 0, b)}(x: string, b: int) = setlen(x, b+1)
 template disallowIf{
   if cond: action
   else: action2
-}(cond: bool, action, action2: stmt) {.error.} = action
+}(cond: bool, action, action2: typed) {.error.} = action
 
 var y = 12
 echo y+y
diff --git a/tests/trmacros/thoist.nim b/tests/trmacros/thoist.nim
deleted file mode 100644
index 657f210a1..000000000
--- a/tests/trmacros/thoist.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-discard """
-  output: '''true
-true'''
-"""
-
-import pegs
-
-template optPeg{peg(pattern)}(pattern: string{lit}): Peg =
-  var gl {.global, gensym.} = peg(pattern)
-  gl
-
-echo match("(a b c)", peg"'(' @ ')'")
-echo match("W_HI_Le", peg"\y 'while'")
diff --git a/tests/trmacros/tmatrix.nim b/tests/trmacros/tmatrix.nim
deleted file mode 100644
index a14ad2db0..000000000
--- a/tests/trmacros/tmatrix.nim
+++ /dev/null
@@ -1,29 +0,0 @@
-discard """
-  output: "21"
-"""
-
-import macros
-
-type
-  TMat = object
-    dummy: int
-
-proc `*`(a, b: TMat): TMat = nil
-proc `+`(a, b: TMat): TMat = nil
-proc `-`(a, b: TMat): TMat = nil
-proc `$`(a: TMat): string = result = $a.dummy
-proc mat21(): TMat =
-  result.dummy = 21
-
-macro optOps{ (`+`|`-`|`*`) ** a }(a: TMat): untyped =
-  echo treeRepr(a)
-  result = newCall(bindSym"mat21")
-
-#macro optPlus{ `+` * a }(a: varargs[TMat]): expr =
-#  result = newIntLitNode(21)
-
-var x, y, z: TMat
-
-echo x + y * z - x
-
-#echo x + y + z
diff --git a/tests/trmacros/tnoalias.nim b/tests/trmacros/tnoalias.nim
deleted file mode 100644
index ec12d4712..000000000
--- a/tests/trmacros/tnoalias.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-discard """
-  output: "23"
-"""
-
-template optslice{a = b + c}(a: untyped{noalias}, b, c: untyped): typed =
-  a = b
-  inc a, c
-
-var
-  x = 12
-  y = 10
-  z = 13
-
-x = y+z
-
-echo x
diff --git a/tests/trmacros/tnoalias2.nim b/tests/trmacros/tnoalias2.nim
deleted file mode 100644
index 9362e764f..000000000
--- a/tests/trmacros/tnoalias2.nim
+++ /dev/null
@@ -1,19 +0,0 @@
-discard """
-  output: '''0'''
-"""
-
-# bug #206
-template optimizeOut{testFunc(a, b)}(a: int, b: int{alias}): untyped = 0
-
-proc testFunc(a, b: int): int = result = a + b
-var testVar = 1
-echo testFunc(testVar, testVar)
-
-
-template ex{a = b + c}(a : int{noalias}, b, c : int) =
-  a = b
-  inc a, b
-  echo "came here"
-
-var x = 5
-x = x + x
diff --git a/tests/trmacros/tnoendlessrec.nim b/tests/trmacros/tnoendlessrec.nim
deleted file mode 100644
index 508770ca7..000000000
--- a/tests/trmacros/tnoendlessrec.nim
+++ /dev/null
@@ -1,10 +0,0 @@
-discard """
-  output: "4"
-"""
-
-# test that an endless recursion is avoided:
-
-template optLen{len(x)}(x: typed): int = len(x)
-
-var s = "lala"
-echo len(s)
diff --git a/tests/trmacros/tnorewrite.nim b/tests/trmacros/tnorewrite.nim
new file mode 100644
index 000000000..e6769246f
--- /dev/null
+++ b/tests/trmacros/tnorewrite.nim
@@ -0,0 +1,20 @@
+block:
+  proc get(x: int): int = x
+
+  template t{get(a)}(a: int): int =
+    {.noRewrite.}:
+      get(a) + 1
+
+  doAssert get(0) == 1
+
+block:
+  var x: int
+
+  template asgn{a = b}(a: int{lvalue}, b: int) =
+    let newVal = b + 1
+    # ^ this is needed but should it be?
+    {.noRewrite.}:
+      a = newVal
+
+  x = 10
+  doAssert x == 11, $x
diff --git a/tests/trmacros/tor.nim b/tests/trmacros/tor.nim
index d698e928d..9defc4d1b 100644
--- a/tests/trmacros/tor.nim
+++ b/tests/trmacros/tor.nim
@@ -1,11 +1,23 @@
 discard """
-  output: '''3030
+  output: '''
+3
+30
 true
-3'''
+'''
 """
 
+
+# bug #798
+template t012{(0|1|2){x}}(x: untyped): untyped = x+1
+let z = 1
+# outputs 3 thanks to fixpoint iteration:
+echo z
+
+
 template arithOps: untyped = (`+` | `-` | `*`)
-template testOr{ (arithOps{f})(a, b) }(a, b, f: untyped): untyped = f(a+1, b)
+template testOr{ (arithOps{f})(a, b) }(a, b, f: untyped): untyped =
+  {.noRewrite.}:
+    f(a mod 10, b)
 
 let xx = 10
 echo 10*xx
@@ -20,9 +32,3 @@ var
   c = false
 a = b and a
 echo a
-
-# bug #798
-template t012{(0|1|2){x}}(x: untyped): untyped = x+1
-let z = 1
-# outputs 3 thanks to fixpoint iteration:
-echo z
diff --git a/tests/trmacros/tpartial.nim b/tests/trmacros/tpartial.nim
deleted file mode 100644
index c636684d7..000000000
--- a/tests/trmacros/tpartial.nim
+++ /dev/null
@@ -1,11 +0,0 @@
-discard """
-  output: '''-2'''
-"""
-
-proc p(x, y: int; cond: bool): int =
-  result = if cond: x + y else: x - y
-
-template optP{p(x, y, true)}(x, y): untyped = x - y
-template optP{p(x, y, false)}(x, y): untyped = x + y
-
-echo p(2, 4, true)
diff --git a/tests/trmacros/tpatterns.nim b/tests/trmacros/tpatterns.nim
deleted file mode 100644
index 907973637..000000000
--- a/tests/trmacros/tpatterns.nim
+++ /dev/null
@@ -1,23 +0,0 @@
-discard """
-  output: '''48
-hel
-lo'''
-"""
-
-template optZero{x+x}(x: int): int = x*3
-template andthen{`*`(x,3)}(x: int): int = x*4
-template optSubstr1{x = substr(x, a, b)}(x: string, a, b: int) = setlen(x, b+1)
-
-var y = 12
-echo y+y
-
-var s: array[0..2, string]
-s[0] = "hello"
-s[0] = substr(s[0], 0, 2)
-
-echo s[0]
-
-# Test varargs matching
-proc someVarargProc(k: varargs[string]) = doAssert(false) # this should not get called
-template someVarargProcSingleArg{someVarargProc([a])}(a: string) = echo a
-someVarargProc("lo")
diff --git a/tests/trmacros/trmacros_various.nim b/tests/trmacros/trmacros_various.nim
new file mode 100644
index 000000000..8fe51e548
--- /dev/null
+++ b/tests/trmacros/trmacros_various.nim
@@ -0,0 +1,111 @@
+discard """
+output: '''
+12false3ha
+21
+optimized
+'''
+"""
+
+import macros, pegs
+
+
+block arglist:
+  proc f(x: varargs[string, `$`]) = discard
+  template optF{f(x)}(x: varargs[untyped]) =
+    writeLine(stdout, x)
+
+  f 1, 2, false, 3, "ha"
+
+
+
+block tcse:
+  template cse{f(a, a, x)}(a: typed{(nkDotExpr|call|nkBracketExpr)&noSideEffect},
+                         f: typed, x: varargs[typed]): untyped =
+    let aa = a
+    f(aa, aa, x)+4
+
+  var
+    a: array[0..10, int]
+    i = 3
+  doAssert a[i] + a[i] == 4
+
+
+
+block hoist:
+  template optPeg{peg(pattern)}(pattern: string{lit}): Peg =
+    {.noRewrite.}:
+      var gl {.global, gensym.} = peg(pattern)
+    gl
+  doAssert match("(a b c)", peg"'(' @ ')'")
+  doAssert match("W_HI_Le", peg"\y 'while'")
+
+
+
+block tmatrix:
+  type
+    TMat = object
+      dummy: int
+
+  proc `*`(a, b: TMat): TMat = nil
+  proc `+`(a, b: TMat): TMat = nil
+  proc `-`(a, b: TMat): TMat = nil
+  proc `$`(a: TMat): string = result = $a.dummy
+  proc mat21(): TMat =
+    result.dummy = 21
+
+  macro optOps{ (`+`|`-`|`*`) ** a }(a: TMat): untyped =
+    result = newCall(bindSym"mat21")
+
+  #macro optPlus{ `+` * a }(a: varargs[TMat]): expr =
+  #  result = newIntLitNode(21)
+
+  var x, y, z: TMat
+  echo x + y * z - x
+
+
+
+block tnoalias:
+  template optslice{a = b + c}(a: untyped{noalias}, b, c: untyped): typed =
+    a = b
+    inc a, c
+  var
+    x = 12
+    y = 10
+    z = 13
+  x = y+z
+  doAssert x == 23
+
+
+
+block tnoendlessrec:
+  # test that an endless recursion is avoided:
+  template optLen{len(x)}(x: typed): int = len(x)
+
+  var s = "lala"
+  doAssert len(s) == 4
+
+
+
+block tstatic_t_bug:
+  # bug #4227
+  type Vector64[N: static[int]] = array[N, int]
+
+  proc `*`[N: static[int]](a: Vector64[N]; b: float64): Vector64[N] =
+    result = a
+
+  proc `+=`[N: static[int]](a: var Vector64[N]; b: Vector64[N]) =
+    echo "regular"
+
+  proc linearCombinationMut[N: static[int]](a: float64, v: var Vector64[N], w: Vector64[N])  {. inline .} =
+    echo "optimized"
+
+  template rewriteLinearCombinationMut{v += `*`(w, a)}(a: float64, v: var Vector64, w: Vector64): auto =
+    linearCombinationMut(a, v, w)
+
+  proc main() =
+    const scaleVal = 9.0
+    var a, b: Vector64[7]
+    a += b * scaleval
+
+  main()
+
diff --git a/tests/trmacros/trmacros_various2.nim b/tests/trmacros/trmacros_various2.nim
new file mode 100644
index 000000000..981df4ca6
--- /dev/null
+++ b/tests/trmacros/trmacros_various2.nim
@@ -0,0 +1,91 @@
+discard """
+output: '''
+0
+-2
+48
+hel
+lo
+my awesome concat
+'''
+"""
+
+
+block tnoalias2:
+  # bug #206
+  template optimizeOut{testFunc(a, b)}(a: int, b: int{alias}): untyped = 0
+
+  proc testFunc(a, b: int): int = result = a + b
+  var testVar = 1
+  echo testFunc(testVar, testVar)
+
+
+  template ex{a = b + c}(a : int{noalias}, b, c : int) =
+    a = b
+    inc a, b
+    echo "came here"
+
+  var x = 5
+  x = x + x
+
+
+
+block tpartial:
+  proc p(x, y: int; cond: bool): int =
+    result = if cond: x + y else: x - y
+
+  template optPTrue{p(x, y, true)}(x, y): untyped = x - y
+  template optPFalse{p(x, y, false)}(x, y): untyped = x + y
+
+  echo p(2, 4, true)
+
+
+
+block tpatterns:
+  template optZero{x+x}(x: int): int = x*3
+  template andthen{`*`(x,3)}(x: int): int = x*4
+  template optSubstr1{x = substr(x, a, b)}(x: string, a, b: int) = setlen(x, b+1)
+
+  var y = 12
+  echo y+y
+
+  var s: array[0..2, string]
+  s[0] = "hello"
+  s[0] = substr(s[0], 0, 2)
+
+  echo s[0]
+
+  # Test varargs matching
+  proc someVarargProc(k: varargs[string]) = doAssert(false) # this should not get called
+  template someVarargProcSingleArg{someVarargProc([a])}(a: string) = echo a
+  someVarargProc("lo")
+
+
+
+block tstar:
+  var
+    calls = 0
+
+  proc `&&`(s: varargs[string]): string =
+    result = s[0]
+    for i in 1..len(s)-1: result.add s[i]
+    inc calls
+
+  template optConc{ `&&` * a }(a: string): string =
+    {.noRewrite.}:
+      &&a
+
+  let space = " "
+  echo "my" && (space & "awe" && "some " ) && "concat"
+
+  # 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/trmacros/tstar.nim b/tests/trmacros/tstar.nim
deleted file mode 100644
index 86f698232..000000000
--- a/tests/trmacros/tstar.nim
+++ /dev/null
@@ -1,19 +0,0 @@
-discard """
-  output: "my awesome concat"
-"""
-
-var
-  calls = 0
-
-proc `&&`(s: varargs[string]): string =
-  result = s[0]
-  for i in 1..len(s)-1: result.add s[i]
-  inc calls
-
-template optConc{ `&&` * a }(a: string): string = &&a
-
-let space = " "
-echo "my" && (space & "awe" && "some " ) && "concat"
-
-# check that it's been optimized properly:
-doAssert calls == 1
diff --git a/tests/trmacros/tstatic_t_bug.nim b/tests/trmacros/tstatic_t_bug.nim
deleted file mode 100644
index cdfa53514..000000000
--- a/tests/trmacros/tstatic_t_bug.nim
+++ /dev/null
@@ -1,24 +0,0 @@
-discard """
-  output: "optimized"
-"""
-# bug #4227
-type Vector64[N: static[int]] = array[N, int]
-
-proc `*`*[N: static[int]](a: Vector64[N]; b: float64): Vector64[N] =
-  result = a
-
-proc `+=`*[N: static[int]](a: var Vector64[N]; b: Vector64[N]) =
-  echo "regular"
-
-proc linearCombinationMut[N: static[int]](a: float64, v: var Vector64[N], w: Vector64[N])  {. inline .} =
-  echo "optimized"
-
-template rewriteLinearCombinationMut*{v += `*`(w, a)}(a: float64, v: var Vector64, w: Vector64): auto =
-  linearCombinationMut(a, v, w)
-
-proc main() =
-  const scaleVal = 9.0
-  var a, b: Vector64[7]
-  a += b * scaleval
-
-main()
diff --git a/tests/trmacros/tstmtlist.nim b/tests/trmacros/tstmtlist.nim
index 751acb79a..8261e7c45 100644
--- a/tests/trmacros/tstmtlist.nim
+++ b/tests/trmacros/tstmtlist.nim
@@ -17,3 +17,18 @@ if true:
   writeLine stdout, "2"
   write stdout, "3"
   echo "4"
+
+# bug #7972
+
+template optimizeLogWrites*{
+  write(f, x)
+  write(f, y)
+}(x, y: string{lit}, f: File) =
+  write(f, x & y)
+
+proc foo() =
+  const N = 1
+  stdout.write("")
+  stdout.write("")
+
+foo()
diff --git a/tests/tuples/mnimsconstunpack.nim b/tests/tuples/mnimsconstunpack.nim
new file mode 100644
index 000000000..65fafc12f
--- /dev/null
+++ b/tests/tuples/mnimsconstunpack.nim
@@ -0,0 +1,4 @@
+proc foo(): tuple[a, b: string] =
+  result = ("a", "b")
+
+const (a, b*) = foo()
diff --git a/tests/tuples/t12892.nim b/tests/tuples/t12892.nim
new file mode 100644
index 000000000..d69e99c7f
--- /dev/null
+++ b/tests/tuples/t12892.nim
@@ -0,0 +1,8 @@
+discard """
+  disabled: i386
+"""
+
+template works[T](): auto = T.high - 1
+template breaks[T](): auto = (T.high - 1, true)
+doAssert $works[uint]() == "18446744073709551614"
+doAssert $breaks[uint]() == "(18446744073709551614, true)"
diff --git a/tests/tuples/t18125_1.nim b/tests/tuples/t18125_1.nim
new file mode 100644
index 000000000..74fdfe8f5
--- /dev/null
+++ b/tests/tuples/t18125_1.nim
@@ -0,0 +1,14 @@
+# issue #18125 solved with type inference
+
+type
+  Parent = ref object of RootObj
+
+  Child = ref object of Parent
+    c: char
+
+func foo(c: char): (Parent, int) =
+  # Works if you use (Parent(Child(c: c)), 0)
+  (Child(c: c), 0)
+
+let x = foo('x')[0]
+doAssert Child(x).c == 'x'
diff --git a/tests/tuples/t18125_2.nim b/tests/tuples/t18125_2.nim
new file mode 100644
index 000000000..fe0a4a8bb
--- /dev/null
+++ b/tests/tuples/t18125_2.nim
@@ -0,0 +1,20 @@
+discard """
+  errormsg: "type mismatch: got <(Child, int)> but expected '(Parent, int)'"
+  line: 17
+"""
+
+# issue #18125 solved with correct type relation
+
+type
+  Parent = ref object of RootObj
+
+  Child = ref object of Parent
+    c: char
+
+func foo(c: char): (Parent, int) =
+  # Works if you use (Parent(Child(c: c)), 0)
+  let x = (Child(c: c), 0)
+  x
+
+let x = foo('x')[0]
+doAssert Child(x).c == 'x'
diff --git a/tests/tuples/t7012.nim b/tests/tuples/t7012.nim
new file mode 100644
index 000000000..32d441ddd
--- /dev/null
+++ b/tests/tuples/t7012.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "illegal recursion in type 'Node'"
+"""
+
+type Node[T] = tuple
+    next: ref Node[T]
+var n: Node[int]
\ No newline at end of file
diff --git a/tests/tuples/t9177.nim b/tests/tuples/t9177.nim
new file mode 100644
index 000000000..e6dd0cb1d
--- /dev/null
+++ b/tests/tuples/t9177.nim
@@ -0,0 +1,15 @@
+discard """
+  action: run
+"""
+
+block:
+  var x = (a: 5, b: 1)
+  x = (3 * x.a + 2 * x.b, x.a + x.b)
+  doAssert x.a == 17
+  doAssert x.b == 6
+block:
+  # Transformation of a tuple constructor with named arguments
+  var x = (a: 5, b: 1)
+  x = (a: 3 * x.a + 2 * x.b, b: x.a + x.b)
+  doAssert x.a == 17
+  doAssert x.b == 6
diff --git a/tests/tuples/tanontuples.nim b/tests/tuples/tanontuples.nim
deleted file mode 100644
index f514670d3..000000000
--- a/tests/tuples/tanontuples.nim
+++ /dev/null
@@ -1,26 +0,0 @@
-discard """
-  output: '''61, 125
-(Field0: 0) (Field0: 13)'''
-"""
-
-import macros
-
-proc `^` (a, b: int): int =
-  result = 1
-  for i in 1..b: result = result * a
-
-var m = (0, 5)
-var n = (56, 3)
-
-m = (n[0] + m[1], m[1] ^ n[1])
-
-echo m[0], ", ", m[1]
-
-# also test we can produce unary anon tuples in a macro:
-macro mm(): untyped =
-  result = newTree(nnkTupleConstr, newLit(13))
-
-proc nowTuple(): (int,) =
-  result = (0,)
-
-echo nowTuple(), " ", mm()
diff --git a/tests/tuples/tconver_tuple.nim b/tests/tuples/tconver_tuple.nim
deleted file mode 100644
index 306da77fe..000000000
--- a/tests/tuples/tconver_tuple.nim
+++ /dev/null
@@ -1,23 +0,0 @@
-# Bug 4479
-
-type
-  MyTuple = tuple
-    num: int
-    strings: seq[string]
-    ints: seq[int]
-
-var foo = MyTuple((
-  num: 7,
-  strings: @[],
-  ints: @[],
-))
-
-var bar = (
-  num: 7,
-  strings: @[],
-  ints: @[],
-).MyTuple
-
-var fooUnnamed = MyTuple((7, @[], @[]))
-var n = 7
-var fooSym = MyTuple((num: n, strings: @[], ints: @[]))
diff --git a/tests/tuples/tdifferent_instantiations.nim b/tests/tuples/tdifferent_instantiations.nim
deleted file mode 100644
index 93b1777b5..000000000
--- a/tests/tuples/tdifferent_instantiations.nim
+++ /dev/null
@@ -1,9 +0,0 @@
-# bug #1910
-import tables
-
-var p: OrderedTable[tuple[a:int], int]
-var q: OrderedTable[tuple[x:int], int]
-for key in p.keys:
-  echo key.a
-for key in q.keys:
-  echo key.x
diff --git a/tests/tuples/tfortupleunpack.nim b/tests/tuples/tfortupleunpack.nim
new file mode 100644
index 000000000..e222a1ae6
--- /dev/null
+++ b/tests/tuples/tfortupleunpack.nim
@@ -0,0 +1,52 @@
+discard """
+output: '''
+123
+113283
+0
+123
+1
+113283
+@[(88, 99, 11), (88, 99, 11)]
+@[(7, 6, -28), (7, 6, -28)]
+12
+110100
+'''
+"""
+
+let t1 = (1, 2, 3)
+let t2 = (11, 32, 83)
+let s = @[t1, t2]
+
+for (a, b, c) in s:
+  echo a, b, c
+
+for i, (a, b, c) in s:
+  echo i
+  echo a, b, c
+
+var x = @[(1,2,3), (4,5,6)]
+
+for (a, b, c) in x.mitems:
+  a = 88
+  b = 99
+  c = 11
+echo x
+
+for i, (a, b, c) in x.mpairs:
+  a = 7
+  b = 6
+  c = -28
+echo x
+
+proc test[n]() =
+  for (a,b) in @[(1,2)]:
+    echo a,b
+test[string]()
+
+iterator tuples: (int, (int, int)) = yield (1,(10, 100))
+
+template t11164 =
+  for i, (a, b) in tuples():
+    echo i, a , b
+
+t11164()
diff --git a/tests/tuples/tgeneric_tuple.nim b/tests/tuples/tgeneric_tuple.nim
deleted file mode 100644
index 32f081596..000000000
--- a/tests/tuples/tgeneric_tuple.nim
+++ /dev/null
@@ -1,9 +0,0 @@
-# bug #2121
-
-type
-  Item[K,V] = tuple
-    key: K
-    value: V
-
-var q = newseq[Item[int,int]](0)
-let (x,y) = q[0]
diff --git a/tests/tuples/tgeneric_tuple2.nim b/tests/tuples/tgeneric_tuple2.nim
deleted file mode 100644
index c0c292388..000000000
--- a/tests/tuples/tgeneric_tuple2.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-
-# bug #2369
-
-type HashedElem[T] = tuple[num: int, storedVal: ref T]
-
-proc append[T](tab: var seq[HashedElem[T]], n: int, val: ref T) =
-    #tab.add((num: n, storedVal: val))
-    var he: HashedElem[T] = (num: n, storedVal: val)
-    #tab.add(he)
-
-var g: seq[HashedElem[int]] = @[]
-
-proc foo() =
-    var x: ref int
-    new(x)
-    x[] = 77
-    g.append(44, x)
diff --git a/tests/tuples/tinferred_generic_const.nim b/tests/tuples/tinferred_generic_const.nim
new file mode 100644
index 000000000..5ab730c38
--- /dev/null
+++ b/tests/tuples/tinferred_generic_const.nim
@@ -0,0 +1,14 @@
+discard """
+  action: run
+"""
+block:
+  proc something(a: string or int or float) =
+    const (c, d) = (default a.type, default a.type)
+
+block:
+  proc something(a: string or int) =
+    const c = default a.type
+
+block:
+  proc something(a: string or int) =
+    const (c, d, e) = (default a.type, default a.type, default a.type)
diff --git a/tests/tuples/tnimsconstunpack.nim b/tests/tuples/tnimsconstunpack.nim
new file mode 100644
index 000000000..7860fc0a4
--- /dev/null
+++ b/tests/tuples/tnimsconstunpack.nim
@@ -0,0 +1,8 @@
+discard """
+  action: compile
+  cmd: "nim e $file"
+"""
+
+import mnimsconstunpack
+
+doAssert b == "b"
diff --git a/tests/tuples/ttuples_issues.nim b/tests/tuples/ttuples_issues.nim
new file mode 100644
index 000000000..70defdfce
--- /dev/null
+++ b/tests/tuples/ttuples_issues.nim
@@ -0,0 +1,133 @@
+discard """
+  targets: "c cpp js"
+"""
+
+# targets include `cpp` because in the past, there were several cpp-specific bugs with tuples.
+
+import std/tables
+
+template main() =
+  block: # bug #4479
+    type
+      MyTuple = tuple
+        num: int
+        strings: seq[string]
+        ints: seq[int]
+
+    var foo = MyTuple((
+      num: 7,
+      strings: @[],
+      ints: @[],
+    ))
+
+    var bar = MyTuple (
+      num: 7,
+      strings: @[],
+      ints: @[],
+    )
+
+    var fooUnnamed = MyTuple((7, @[], @[]))
+    var n = 7
+    var fooSym = MyTuple((num: n, strings: @[], ints: @[]))
+
+  block: # bug #1910
+    var p = newOrderedTable[tuple[a:int], int]()
+    var q = newOrderedTable[tuple[x:int], int]()
+    for key in p.keys:
+      echo key.a
+    for key in q.keys:
+      echo key.x
+
+  block: # bug #2121
+    type
+      Item[K,V] = tuple
+        key: K
+        value: V
+
+    var q = newseq[Item[int,int]](1)
+    let (x,y) = q[0]
+
+  block: # bug #2369
+    type HashedElem[T] = tuple[num: int, storedVal: ref T]
+
+    proc append[T](tab: var seq[HashedElem[T]], n: int, val: ref T) =
+        #tab.add((num: n, storedVal: val))
+        var he: HashedElem[T] = (num: n, storedVal: val)
+        #tab.add(he)
+
+    var g: seq[HashedElem[int]] = @[]
+
+    proc foo() =
+        var x: ref int
+        new(x)
+        x[] = 77
+        g.append(44, x)
+
+  block: # bug #1986
+    proc test(): int64 =
+      return 0xdeadbeef.int64
+
+    const items = [
+      (var1: test(), var2: 100'u32),
+      (var1: test(), var2: 192'u32)
+    ]
+
+  block: # bug #14911
+    doAssert $(a: 1) == "(a: 1)" # works
+    doAssert $(`a`: 1) == "(a: 1)"  # works
+    doAssert $(`a`: 1, `b`: 2) == "(a: 1, b: 2)" # was: Error: named expression expected
+
+  block: # bug #16822
+    var scores: seq[(set[char], int)] = @{{'/'} : 10}
+
+    var x1: set[char]
+    for item in items(scores):
+      x1 = item[0]
+
+    doAssert x1 == {'/'}
+
+    var x2: set[char]
+    for (chars, value) in items(scores):
+      x2 = chars
+
+    doAssert x2 == {'/'}
+
+  block: # bug #14574
+    proc fn(): auto =
+      let a = @[("foo", (12, 13))]
+      for (k,v) in a:
+        return (k,v)
+    doAssert fn() == ("foo", (12, 13))
+
+  block: # bug #14574
+    iterator fn[T](a:T): lent T = yield a
+    let a = (10, (11,))
+    proc bar(): auto =
+      for (x,y) in fn(a):
+        return (x,y)
+    doAssert bar() == (10, (11,))
+
+  block: # bug #16331
+    type T1 = tuple[a, b: int]
+
+    proc p(b: bool): T1 =
+      var x: T1 = (10, 20)
+      x = if b: (x.b, x.a) else: (-x.b, -x.a)
+      x
+
+    doAssert p(false) == (-20, -10)
+    doAssert p(true) == (20, 10)
+
+
+proc mainProc() =
+  # other tests should be in `main`
+  block:
+    type A = tuple[x: int, y: int]
+    doAssert (x: 1, y: 2).A == A (x: 1, y: 2) # MCS => can't use a template
+
+static:
+  main()
+  mainProc()
+
+main()
+mainProc()
diff --git a/tests/tuples/ttuples_various.nim b/tests/tuples/ttuples_various.nim
new file mode 100644
index 000000000..e392731d2
--- /dev/null
+++ b/tests/tuples/ttuples_various.nim
@@ -0,0 +1,211 @@
+discard """
+output: '''
+it's nil
+@[1, 2, 3]
+'''
+"""
+
+import macros
+
+
+block anontuples:
+  proc `^` (a, b: int): int =
+    result = 1
+    for i in 1..b: result = result * a
+
+  var m = (0, 5)
+  var n = (56, 3)
+
+  m = (n[0] + m[1], m[1] ^ n[1])
+
+  doAssert m == (61, 125)
+
+  # also test we can produce unary anon tuples in a macro:
+  macro mm(): untyped =
+    result = newTree(nnkTupleConstr, newLit(13))
+
+  proc nowTuple(): (int,) =
+    result = (0,)
+
+  doAssert nowTuple() == (Field0: 0)
+  doAssert mm() == (Field0: 13)
+
+
+
+block unpack_asgn:
+  proc foobar(): (int, int) = (2, 4)
+
+  # test within a proc:
+  proc pp(x: var int) =
+    var y: int
+    (y, x) = foobar()
+
+  template pt(x) =
+    var y: int
+    (x, y) = foobar()
+
+  # test within a generic:
+  proc pg[T](x, y: var T) =
+    pt(x)
+
+  # test as a top level statement:
+  var x, y, a, b: int
+  # test for regression:
+  (x, y) = (1, 2)
+  (x, y) = fooBar()
+
+  doAssert x == 2
+  doAssert y == 4
+
+  pp(a)
+  doAssert a == 4
+
+  pg(a, b)
+  doAssert a == 2
+  doAssert b == 0
+
+
+
+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
+
+
+# bug #10724
+block unpack_const_named:
+  const (a, ) = (x: 1, )
+  doAssert a == 1
+
+  const (b, c) = (x: 2, y: 3)
+  doAssert b == 2
+  doAssert c == 3
+
+  const (d, e, f) = (x: 4, y: 5, z: 6)
+  doAssert d == 4
+  doAssert e == 5
+  doAssert f == 6
+
+block const_named:
+  const x = block:
+    (a: 1, b: 2, c: 3)
+  doAssert x.a == 1
+  doAssert x.b == 2
+  doAssert x.c == 3
+
+
+block tuple_subscript:
+  proc`[]` (t: tuple, key: string): string =
+    for name, field in fieldPairs(t):
+      if name == key:
+        return $field
+    return ""
+
+  proc`[]` [A,B](t: tuple, key: string, op: (proc(x: A): B)): B =
+    for name, field in fieldPairs(t):
+      when field is A:
+        if name == key:
+          return op(field)
+
+  proc`[]=`[T](t: var tuple, key: string, val: T) =
+    for name, field in fieldPairs(t):
+      when field is T:
+        if name == key:
+          field = val
+
+  var tt = (a: 1, b: "str1")
+
+  # test built in operator
+  tt[0] = 5
+
+  doAssert tt[0] == 5
+  doAssert `[]`(tt, 0) == 5
+
+  # test overloaded operator
+  tt["b"] = "str2"
+  doAssert tt["b"] == "str2"
+  doAssert `[]`(tt, "b") == "str2"
+  doAssert tt["b", proc(s: string): int = s.len] == 4
+
+
+
+block tuple_with_seq:
+  template foo(s: string = "") =
+    if s.len == 0:
+      echo "it's nil"
+    else:
+      echo s
+  foo
+
+  # bug #2632
+  proc takeTup(x: tuple[s: string;x: seq[int]]) =
+    discard
+  takeTup(("foo", @[]))
+
+  #proc foobar(): () =
+  proc f(xs: seq[int]) =
+    discard
+
+  proc g(t: tuple[n:int, xs:seq[int]]) =
+    discard
+
+  when true:
+    f(@[]) # OK
+    g((1,@[1])) # OK
+    g((0,@[])) # NG
+
+  # bug #2630
+  type T = tuple[a: seq[int], b: int]
+  var t: T = (@[1,2,3], 7)
+
+  proc test(s: seq[int]): T =
+    echo s
+    (s, 7)
+  t = test(t.a)
+
+block: # bug #22049
+  type A = object
+    field: tuple[a, b, c: seq[int]]
+
+  func value(v: var A): var tuple[a, b, c: seq[int]] =
+    v.field
+  template get(v: A): tuple[a, b, c: seq[int]] = v.value
+
+  var v = A(field: (@[1], @[2], @[3]))
+  var (a, b, c) = v.get()
+
+  doAssert a == @[1]
+  doAssert b == @[2]
+  doAssert c == @[3]
+
+block: # bug #22054
+  type A = object
+    field: tuple[a: int]
+
+  func value(v: var A): var tuple[a: int] =
+    v.field
+  template get(v: A): tuple[a: int] = v.value
+
+  var v = A(field: (a: 1314))
+  doAssert get(v)[0] == 1314
+
+block: # tuple unpacking assignment with underscore
+  var
+    a = 1
+    b = 2
+  doAssert (a, b) == (1, 2)
+  (a, _) = (3, 4)
+  doAssert (a, b) == (3, 2)
+  (_, a) = (5, 6)
+  doAssert (a, b) == (6, 2)
+  (b, _) = (7, 8)
+  doAssert (a, b) == (6, 7)
diff --git a/tests/tuples/ttypedesc_in_tuple_a.nim b/tests/tuples/ttypedesc_in_tuple_a.nim
new file mode 100644
index 000000000..7727bb35e
--- /dev/null
+++ b/tests/tuples/ttypedesc_in_tuple_a.nim
@@ -0,0 +1,5 @@
+discard """
+errormsg: "typedesc not allowed as tuple field."
+"""
+
+var bar = (a: int, b: 1)
diff --git a/tests/tuples/ttypedesc_in_tuple_b.nim b/tests/tuples/ttypedesc_in_tuple_b.nim
new file mode 100644
index 000000000..b393d877c
--- /dev/null
+++ b/tests/tuples/ttypedesc_in_tuple_b.nim
@@ -0,0 +1,5 @@
+discard """
+errormsg: "Mixing types and values in tuples is not allowed."
+"""
+
+var bar = (int, 1)
diff --git a/tests/tuples/tuint_tuple.nim b/tests/tuples/tuint_tuple.nim
deleted file mode 100644
index 24bcead5e..000000000
--- a/tests/tuples/tuint_tuple.nim
+++ /dev/null
@@ -1,10 +0,0 @@
-# bug #1986 found by gdmoore
-
-proc test(): int64 =
-  return 0xdeadbeef.int64
-
-const items = [
-  (var1: test(), var2: 100'u32),
-  (var1: test(), var2: 192'u32)
-]
-
diff --git a/tests/tuples/tunpack_asgn.nim b/tests/tuples/tunpack_asgn.nim
deleted file mode 100644
index 1dc7ff074..000000000
--- a/tests/tuples/tunpack_asgn.nim
+++ /dev/null
@@ -1,34 +0,0 @@
-discard """
-  output: '''2 4
-4
-2 0'''
-"""
-
-proc foobar(): (int, int) = (2, 4)
-
-# test within a proc:
-proc pp(x: var int) =
-  var y: int
-  (y, x) = foobar()
-
-template pt(x) =
-  var y: int
-  (x, y) = foobar()
-
-# test within a generic:
-proc pg[T](x, y: var T) =
-  pt(x)
-
-# test as a top level statement:
-var x, y, a, b: int
-# test for regression:
-(x, y) = (1, 2)
-(x, y) = fooBar()
-
-echo x, " ", y
-
-pp(a)
-echo a
-
-pg(a, b)
-echo a, " ", b
diff --git a/tests/tuples/tuple_subscript.nim b/tests/tuples/tuple_subscript.nim
deleted file mode 100644
index 021793dc3..000000000
--- a/tests/tuples/tuple_subscript.nim
+++ /dev/null
@@ -1,40 +0,0 @@
-discard """
-  output: '''5
-5
-str2
-str2
-4'''
-"""
-
-proc`[]` (t: tuple, key: string): string =
-  for name, field in fieldPairs(t):
-    if name == key: 
-      return $field
-  return ""
-
-
-proc`[]` [A,B](t: tuple, key: string, op: (proc(x: A): B)): B =
-  for name, field in fieldPairs(t):
-    when field is A:
-      if name == key: 
-        return op(field)
-
-proc`[]=`[T](t: var tuple, key: string, val: T) =
-  for name, field in fieldPairs(t):
-    when field is T:
-      if name == key: 
-        field = val
-
-var tt = (a: 1, b: "str1")
-
-# test built in operator
-tt[0] = 5
-echo tt[0] 
-echo `[]`(tt, 0)
-
-
-# test overloaded operator
-tt["b"] = "str2"
-echo tt["b"] 
-echo `[]`(tt, "b")
-echo tt["b", proc(s: string) : int = s.len]
\ No newline at end of file
diff --git a/tests/tuples/tuple_with_nil.nim b/tests/tuples/tuple_with_nil.nim
index ec48337bd..9cad6eccd 100644
--- a/tests/tuples/tuple_with_nil.nim
+++ b/tests/tuples/tuple_with_nil.nim
@@ -1,24 +1,16 @@
 import macros
-from strutils import IdentStartChars
 import parseutils
 import unicode
 import math
-import fenv
-#import unsigned
 import pegs
 import streams
 
 type
-  FormatError = object of Exception ## Error in the format string.
+  FormatError = object of CatchableError ## Error in the format string.
 
   Writer = concept W
     ## Writer to output a character `c`.
-    when (NimMajor, NimMinor, NimPatch) > (0, 10, 2):
-      write(W, 'c')
-    else:
-      block:
-        var x: W
-        write(x, char)
+    write(W, 'c')
 
   FmtAlign = enum ## Format alignment
     faDefault  ## default for given format type
@@ -50,7 +42,7 @@ type
     precision: int    ## floating point precision
     width: int        ## minimal width
     fill: string      ## the fill character, UTF8
-    align: FmtAlign  ## aligment
+    align: FmtAlign  ## alignment
     sign: FmtSign    ## sign notation
     baseprefix: bool  ## whether binary, octal, hex should be prefixed by 0b, 0x, 0o
     upcase: bool      ## upper case letters in hex or exponential formats
@@ -73,7 +65,7 @@ type
 
 const
   DefaultPrec = 6 ## Default precision for floating point numbers.
-  DefaultFmt: Format = (ftDefault, -1, -1, nil, faDefault, fsMinus, false, false, false, nil)
+  DefaultFmt: Format = (ftDefault, -1, -1, "", faDefault, fsMinus, false, false, false, "")
     ## Default format corresponding to the empty format string, i.e.
     ##   `x.format("") == x.format(DefaultFmt)`.
   round_nums = [0.5, 0.05, 0.005, 0.0005, 0.00005, 0.000005, 0.0000005, 0.00000005]
@@ -88,7 +80,7 @@ proc has(c: Captures; i: range[0..pegs.MaxSubpatterns-1]): bool {.nosideeffect,
   result = b.first <= b.last
 
 proc get(str: string; c: Captures; i: range[0..MaxSubpatterns-1]; def: char): char {.nosideeffect, inline.} =
-  ## If capture `i` is non-empty return that portion of `str` casted
+  ## If capture `i` is non-empty return that portion of `str` cast
   ## to `char`, otherwise return `def`.
   result = if c.has(i): str[c.bounds(i).first] else: def
 
@@ -124,7 +116,7 @@ proc parse(fmt: string): Format {.nosideeffect.} =
   if fmt.rawmatch(p, 0, caps) < 0:
     raise newException(FormatError, "Invalid format string")
 
-  result.fill = fmt.get(caps, 0, nil)
+  result.fill = fmt.get(caps, 0, "")
 
   case fmt.get(caps, 1, 0.char)
   of '<': result.align = faLeft
@@ -144,7 +136,7 @@ proc parse(fmt: string): Format {.nosideeffect.} =
   result.width = fmt.get(caps, 4, -1)
 
   if caps.has(4) and fmt[caps.bounds(4).first] == '0':
-    if result.fill != nil:
+    if result.fill != "":
       raise newException(FormatError, "Leading 0 in with not allowed with explicit fill character")
     if result.align != faDefault:
       raise newException(FormatError, "Leading 0 in with not allowed with explicit alignment")
@@ -171,7 +163,7 @@ proc parse(fmt: string): Format {.nosideeffect.} =
   of '%': result.typ = ftPercent
   else: result.typ = ftDefault
 
-  result.arysep = fmt.get(caps, 8, nil, 1)
+  result.arysep = fmt.get(caps, 8, "", 1)
 
 proc getalign(fmt: Format; defalign: FmtAlign; slen: int) : tuple[left, right:int] {.nosideeffect.} =
   ## Returns the number of left and right padding characters for a
@@ -208,7 +200,7 @@ proc writefill(o: var Writer; fmt: Format; n: int; signum: int = 0) =
   ## `add`
   ##   output function
   ## `fmt`
-  ##   format to be used (important for padding aligment)
+  ##   format to be used (important for padding alignment)
   ## `n`
   ##   the number of filling characters to be written
   ## `signum`
@@ -218,7 +210,7 @@ proc writefill(o: var Writer; fmt: Format; n: int; signum: int = 0) =
     elif fmt.sign == fsPlus: write(o, '+')
     elif fmt.sign == fsSpace: write(o, ' ')
 
-  if fmt.fill == nil:
+  if fmt.fill.len == 0:
     for i in 1..n: write(o, ' ')
   else:
     for i in 1..n:
@@ -345,7 +337,7 @@ proc writeformat(o: var Writer; p: pointer; fmt: Format) =
   ## Write pointer `i` according to format `fmt` using output object
   ## `o` and output function `add`.
   ##
-  ## Pointers are casted to unsigned int and formatted as hexadecimal
+  ## Pointers are cast to unsigned int and formatted as hexadecimal
   ## with prefix unless specified otherwise.
   var f = fmt
   if f.typ == 0.char:
@@ -485,7 +477,7 @@ proc writeformat(o: var Writer; b: bool; fmt: Format) =
   else:
     raise newException(FormatError, "Boolean values must of one of the following types: s,b,o,x,X,d,n")
 
-proc writeformat(o: var Writer; ary: openarray[system.any]; fmt: Format) =
+proc writeformat(o: var Writer; ary: openArray[system.any]; fmt: Format) =
   ## Write array `ary` according to format `fmt` using output object
   ## `o` and output function `add`.
   if ary.len == 0: return
@@ -626,19 +618,19 @@ proc splitfmt(s: string): seq[Part] {.compiletime, nosideeffect.} =
       else:
         lvl.dec
     let clpos = pos
-    var fmtpart = Part(kind: pkFmt, arg: -1, fmt: s.substr(oppos+1, clpos-1), field: nil, index: int.high, nested: nested)
+    var fmtpart = Part(kind: pkFmt, arg: -1, fmt: s.substr(oppos+1, clpos-1), field: "", index: int.high, nested: nested)
     if fmtpart.fmt.len > 0:
       var m: array[0..3, string]
       if not fmtpart.fmt.match(subpeg, m):
         error("invalid format string")
 
-      if m[1] != nil and m[1].len > 0:
+      if m[1].len > 0:
         fmtpart.field = m[1].substr(1)
-      if m[2] != nil and m[2].len > 0:
+      if m[2].len > 0:
         discard parseInt(m[2].substr(1, m[2].len-2), fmtpart.index)
 
       if m[0].len > 0: discard parseInt(m[0], fmtpart.arg)
-      if m[3] == nil or m[3].len == 0:
+      if m[3].len == 0:
         fmtpart.fmt = ""
       elif m[3][0] == ':':
         fmtpart.fmt = m[3].substr(1)
@@ -650,7 +642,7 @@ proc splitfmt(s: string): seq[Part] {.compiletime, nosideeffect.} =
 proc literal(s: string): NimNode {.compiletime, nosideeffect.} =
   ## Return the nim literal of string `s`. This handles the case if
   ## `s` is nil.
-  result = if s == nil: newNilLit() else: newLit(s)
+  result = newLit(s)
 
 proc literal(b: bool): NimNode {.compiletime, nosideeffect.} =
   ## Return the nim literal of boolean `b`. This is either `true`
@@ -665,7 +657,7 @@ proc literal[T](x: T): NimNode {.compiletime, nosideeffect.} =
     result = newLit(x)
 
 proc generatefmt(fmtstr: string;
-                 args: var openarray[tuple[arg:NimNode, cnt:int]];
+                 args: var openArray[tuple[arg:NimNode, cnt:int]];
                  arg: var int;): seq[tuple[val, fmt:NimNode]] {.compiletime.} =
   ## fmtstr
   ##   the format string
@@ -713,7 +705,7 @@ proc generatefmt(fmtstr: string;
           args[arg].cnt = args[arg].cnt + 1
           arg.inc
         # possible field access
-        if part.field != nil and part.field.len > 0:
+        if part.field.len > 0:
           argexpr = newDotExpr(argexpr, part.field.ident)
         # possible array access
         if part.index < int.high:
@@ -724,7 +716,7 @@ proc generatefmt(fmtstr: string;
           # nested format string. Compute the format string by
           # concatenating the parts of the substring.
           for e in generatefmt(part.fmt, args, arg):
-            var newexpr = if part.fmt == nil: e.val else: newCall(bindsym"format", e.val, e.fmt)
+            var newexpr = if part.fmt.len == 0: e.val else: newCall(bindsym"format", e.val, e.fmt)
             if fmtexpr != nil and fmtexpr.kind != nnkNilLit:
               fmtexpr = infix(fmtexpr, "&", newexpr)
             else:
diff --git a/tests/tuples/tuple_with_seq.nim b/tests/tuples/tuple_with_seq.nim
deleted file mode 100644
index 39edb500f..000000000
--- a/tests/tuples/tuple_with_seq.nim
+++ /dev/null
@@ -1,46 +0,0 @@
-discard """
-  output: '''it's nil
-@[1, 2, 3]'''
-"""
-
-template foo(s: string = nil) =
-  if isNil(s):
-    echo "it's nil"
-  else:
-    echo s
-
-foo
-
-
-# bug #2632
-
-proc takeTup(x: tuple[s: string;x: seq[int]]) =
-  discard
-
-takeTup(("foo", @[]))
-
-
-#proc foobar(): () =
-
-proc f(xs: seq[int]) =
-  discard
-
-proc g(t: tuple[n:int, xs:seq[int]]) =
-  discard
-
-when isMainModule:
-  f(@[]) # OK
-  g((1,@[1])) # OK
-  g((0,@[])) # NG
-
-
-# bug #2630
-type T = tuple[a: seq[int], b: int]
-
-var t: T = (@[1,2,3], 7)
-
-proc test(s: seq[int]): T =
-  echo s
-  (s, 7)
-
-t = test(t.a)
diff --git a/tests/tuples/twrongtupleaccess.nim b/tests/tuples/twrongtupleaccess.nim
index b1684b097..591716a2e 100644
--- a/tests/tuples/twrongtupleaccess.nim
+++ b/tests/tuples/twrongtupleaccess.nim
@@ -1,10 +1,9 @@
 discard """
+  errormsg: "attempting to call undeclared routine: \'setBLAH\'"
   file: "twrongtupleaccess.nim"
   line: 9
-  errormsg: "attempting to call undeclared routine: \'setBLAH\'"
 """
 # Bugfix
 
 var v = (5.0, 10.0)
 v.setBLAH(10)
-
diff --git a/tests/typerel/t2plus.nim b/tests/typerel/t2plus.nim
index 08378b804..4b18b6221 100644
--- a/tests/typerel/t2plus.nim
+++ b/tests/typerel/t2plus.nim
@@ -4,7 +4,7 @@ discard """
 
 {.warning[TypelessParam]: off.}
 
-import future
+import sugar
 
 # bug #3329
 
@@ -16,7 +16,7 @@ proc foldRight[T,U](lst: seq[T], v: U, f: (T, U) -> U): U =
 proc mean[T: SomeNumber](xs: seq[T]): T =
   xs.foldRight(0.T, (xBAZ: auto, yBAZ: auto) => xBAZ + yBAZ) / T(xs.len)
 
-when isMainModule:
+when true:
   let x = mean(@[1.float, 2, 3])
   echo x
 
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..03d2a0cfa 100644
--- a/tests/typerel/t4799_1.nim
+++ b/tests/typerel/t4799_1.nim
@@ -1,5 +1,7 @@
 discard """
-  outputsub: '''ObjectAssignmentError'''
+  matrix: "--mm:refc"
+  targets: "c cpp"
+  outputsub: '''ObjectAssignmentDefect'''
   exitcode: "1"
 """
 
diff --git a/tests/typerel/t4799_2.nim b/tests/typerel/t4799_2.nim
index cfd399a6e..ef8571f25 100644
--- a/tests/typerel/t4799_2.nim
+++ b/tests/typerel/t4799_2.nim
@@ -1,5 +1,7 @@
 discard """
-  outputsub: '''ObjectAssignmentError'''
+  matrix: "--mm:refc"
+  targets: "c cpp"
+  outputsub: '''ObjectAssignmentDefect'''
   exitcode: "1"
 """
 
diff --git a/tests/typerel/t4799_3.nim b/tests/typerel/t4799_3.nim
index 784eee8fc..26258b983 100644
--- a/tests/typerel/t4799_3.nim
+++ b/tests/typerel/t4799_3.nim
@@ -1,5 +1,7 @@
 discard """
-  outputsub: '''ObjectAssignmentError'''
+  matrix: "--mm:refc"
+  targets: "c cpp"
+  outputsub: '''ObjectAssignmentDefect'''
   exitcode: "1"
 """
 
diff --git a/tests/typerel/t7600_1.nim b/tests/typerel/t7600_1.nim
index e3a5fefa2..83f93ae7f 100644
--- a/tests/typerel/t7600_1.nim
+++ b/tests/typerel/t7600_1.nim
@@ -1,8 +1,11 @@
 discard """
 errormsg: "type mismatch: got <Thin[system.int]>"
-nimout: '''t7600_1.nim(18, 6) Error: type mismatch: got <Thin[system.int]>
+nimout: '''t7600_1.nim(21, 1) Error: type mismatch: got <Thin[system.int]>
 but expected one of:
 proc test[T](x: Paper[T])
+  first type mismatch at position: 1
+  required type for x: Paper[test.T]
+  but expression 'tn' is of type: Thin[system.int]
 
 expression: test tn'''
 """
diff --git a/tests/typerel/t7600_2.nim b/tests/typerel/t7600_2.nim
index 7badb69cf..9488a44bc 100644
--- a/tests/typerel/t7600_2.nim
+++ b/tests/typerel/t7600_2.nim
@@ -1,8 +1,11 @@
 discard """
 errormsg: "type mismatch: got <Thin>"
-nimout: '''t7600_2.nim(17, 6) Error: type mismatch: got <Thin>
+nimout: '''t7600_2.nim(20, 1) Error: type mismatch: got <Thin>
 but expected one of:
 proc test(x: Paper)
+  first type mismatch at position: 1
+  required type for x: Paper
+  but expression 'tn' is of type: Thin
 
 expression: test tn'''
 """
diff --git a/tests/typerel/t7734.nim b/tests/typerel/t7734.nim
new file mode 100644
index 000000000..1e8df2cf1
--- /dev/null
+++ b/tests/typerel/t7734.nim
@@ -0,0 +1,19 @@
+type
+  Foo[T: SomeFloat] = object
+    learning_rate: T
+
+  Bar[T: SomeFloat] = object
+    learning_rate: T
+    momentum: T
+
+  Model = object
+    weight: int
+
+  FooClass = Foo or Bar
+
+
+proc optimizer[M; T: SomeFloat](model: M, _: typedesc[Foo], learning_rate: T): Foo[T] =
+  result.learning_rate = learning_rate
+
+let a = Model(weight: 1)
+let opt = a.optimizer(Foo, 10.0)
diff --git a/tests/typerel/t8172.nim b/tests/typerel/t8172.nim
new file mode 100644
index 000000000..57b788250
--- /dev/null
+++ b/tests/typerel/t8172.nim
@@ -0,0 +1,11 @@
+discard """
+  errormsg: "cannot convert array[0..0, string] to varargs[string]"
+  line: 11
+"""
+
+proc f(v: varargs[string]) =
+  echo(v)
+
+f("b", "c")   # Works
+f(["b", "c"]) # Works
+f("b", ["c"]) # Fails
diff --git a/tests/typerel/t8905.nim b/tests/typerel/t8905.nim
new file mode 100644
index 000000000..9383962cf
--- /dev/null
+++ b/tests/typerel/t8905.nim
@@ -0,0 +1,7 @@
+type
+  Foo[T] = distinct seq[T]
+  Bar[T] = object
+
+proc newFoo[T](): Foo[T] = Foo[T](newSeq[T]())
+
+var x = newFoo[Bar[int]]()
diff --git a/tests/typerel/tcommontype.nim b/tests/typerel/tcommontype.nim
index 8215ebd5e..f32228177 100644
--- a/tests/typerel/tcommontype.nim
+++ b/tests/typerel/tcommontype.nim
@@ -1,5 +1,5 @@
 type
-  TAnimal=object {.inheritable.}
+  TAnimal{.inheritable.}=object
   PAnimal=ref TAnimal
 
   TDog=object of TAnimal
@@ -12,7 +12,7 @@ type
 
 proc newDog():PDog = new(result)
 proc newCat():PCat = new(result)
-proc test(a:openarray[PAnimal])=
+proc test(a:openArray[PAnimal])=
   echo("dummy")
 
 #test(newDog(),newCat()) #does not work
diff --git a/tests/typerel/temptynode.nim b/tests/typerel/temptynode.nim
index df308fbc2..8c71a6092 100644
--- a/tests/typerel/temptynode.nim
+++ b/tests/typerel/temptynode.nim
@@ -1,6 +1,6 @@
 discard """
-  line: 16
   errormsg: "type mismatch: got <void>"
+  line: 16
 """
 
 # bug #950
diff --git a/tests/typerel/texplicitcmp.nim b/tests/typerel/texplicitcmp.nim
index e91ac2ffe..57f1e81b6 100644
--- a/tests/typerel/texplicitcmp.nim
+++ b/tests/typerel/texplicitcmp.nim
@@ -8,7 +8,7 @@ discard """
 
 import json, tables, algorithm
 
-proc outp(a: openarray[int]) =
+proc outp(a: openArray[int]) =
   stdout.write "["
   for i in a: stdout.write($i & " ")
   stdout.write "]\n"
@@ -24,9 +24,8 @@ proc weird(json_params: Table) =
   sort(f, system.cmp[int])
   outp(f)
 
-when isMainModule:
-  var t = @[3, 2, 1]
-  sort(t, system.cmp[int])
-  outp(t)
-  works()
-  weird(initTable[string, JsonNode]())
+var t = @[3, 2, 1]
+sort(t, system.cmp[int])
+outp(t)
+works()
+weird(initTable[string, JsonNode]())
diff --git a/tests/typerel/tno_int_in_bool_context.nim b/tests/typerel/tno_int_in_bool_context.nim
index a4b4237d2..66e9da58a 100644
--- a/tests/typerel/tno_int_in_bool_context.nim
+++ b/tests/typerel/tno_int_in_bool_context.nim
@@ -1,8 +1,7 @@
 discard """
-  line: 6
   errormsg: "type mismatch: got <int literal(1)> but expected 'bool'"
+  line: 6
 """
 
 if 1:
   echo "wtf?"
-
diff --git a/tests/typerel/tnoargopenarray.nim b/tests/typerel/tnoargopenarray.nim
deleted file mode 100644
index 20ebe5ecc..000000000
--- a/tests/typerel/tnoargopenarray.nim
+++ /dev/null
@@ -1,7 +0,0 @@
-
-import db_sqlite
-
-var db: DbConn
-exec(db, sql"create table blabla()")
-
-
diff --git a/tests/typerel/tnocontains.nim b/tests/typerel/tnocontains.nim
index a93db2fc3..8bea8aa56 100644
--- a/tests/typerel/tnocontains.nim
+++ b/tests/typerel/tnocontains.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "type mismatch: got <string, string>"
   file: "tnocontains.nim"
   line: 10
-  errormsg: "type mismatch: got <string, string>"
 """
 
 # shouldn't compile since it doesn't do what you think it does without
diff --git a/tests/typerel/tproctypeclass.nim b/tests/typerel/tproctypeclass.nim
new file mode 100644
index 000000000..e8fab9780
--- /dev/null
+++ b/tests/typerel/tproctypeclass.nim
@@ -0,0 +1,89 @@
+import std/assertions
+
+proc main =
+  iterator closureIter(): int {.closure.} =
+    yield 1
+    yield 2
+  iterator inlineIter(): int {.inline.} =
+    yield 1
+    yield 2
+  proc procNotIter(): int = 1
+
+  doAssert closureIter is iterator
+  doAssert inlineIter is iterator
+  doAssert procNotIter isnot iterator
+
+  doAssert closureIter isnot proc
+  doAssert inlineIter isnot proc
+  doAssert procNotIter is proc
+
+  doAssert typeof(closureIter) is iterator
+  doAssert typeof(inlineIter) is iterator
+  doAssert typeof(procNotIter) isnot iterator
+
+  doAssert typeof(closureIter) isnot proc
+  doAssert typeof(inlineIter) isnot proc
+  doAssert typeof(procNotIter) is proc
+
+  block:
+    proc fn1(iter: iterator {.closure.}) = discard
+    proc fn2[T: iterator {.closure.}](iter: T) = discard
+
+    fn1(closureIter)
+    fn2(closureIter)
+
+    doAssert not compiles(fn1(procNotIter))
+    doAssert not compiles(fn2(procNotIter))
+
+    doAssert not compiles(fn1(inlineIter))
+    doAssert not compiles(fn2(inlineIter))
+
+  block: # concrete iterator type
+    proc fn1(iter: iterator(): int) = discard
+    proc fn2[T: iterator(): int](iter: T) = discard
+
+    fn1(closureIter)
+    fn2(closureIter)
+
+    doAssert not compiles(fn1(procNotIter))
+    doAssert not compiles(fn2(procNotIter))
+
+    doAssert not compiles(fn1(inlineIter))
+    doAssert not compiles(fn2(inlineIter))
+
+  proc takesNimcall[T: proc {.nimcall.}](p: T) = discard
+  proc takesClosure[T: proc {.closure.}](p: T) = discard
+  proc takesAnyProc[T: proc](p: T) = discard
+
+  proc nimcallProc(): int {.nimcall.} = 1
+  proc closureProc(): int {.closure.} = 2
+
+  doAssert nimcallProc is proc {.nimcall.}
+  takesNimcall(nimcallProc)
+  doAssert closureProc isnot proc {.nimcall.}
+  doAssert not compiles(takesNimcall(closureProc))
+
+  doAssert nimcallProc isnot proc {.closure.}
+  doAssert not compiles(takesClosure(nimcallProc))
+  doAssert closureProc is proc {.closure.}
+  takesClosure(closureProc)
+  
+  doAssert nimcallProc is proc
+  takesAnyProc(nimcallProc)
+  doAssert closureProc is proc
+  takesAnyProc(closureProc)
+
+  block: # supposed to test that sameType works 
+    template ensureNotRedefine(Ty): untyped =
+      proc foo[T: Ty](x: T) = discard
+      doAssert not (compiles do:
+        proc bar[T: Ty](x: T) = discard
+        proc bar[T: Ty](x: T) = discard)
+    ensureNotRedefine proc
+    ensureNotRedefine iterator
+    ensureNotRedefine proc {.nimcall.}
+    ensureNotRedefine iterator {.nimcall.}
+    ensureNotRedefine proc {.closure.}
+    ensureNotRedefine iterator {.closure.}
+
+main()
diff --git a/tests/typerel/tptrs.nim b/tests/typerel/tptrs.nim
new file mode 100644
index 000000000..3505a7736
--- /dev/null
+++ b/tests/typerel/tptrs.nim
@@ -0,0 +1,8 @@
+discard """
+  errormsg: "type mismatch: got <ptr int16> but expected 'ptr int'"
+  line: 8
+"""
+
+var
+  n: int16
+  p: ptr int = addr n
diff --git a/tests/typerel/trefs.nim b/tests/typerel/trefs.nim
index d4383c562..e9862bd0f 100644
--- a/tests/typerel/trefs.nim
+++ b/tests/typerel/trefs.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "type mismatch"
   file: "trefs.nim"
   line: 20
-  errormsg: "type mismatch"
 """
 # test for ref types (including refs to procs)
 
@@ -18,6 +18,3 @@ var p: TProc
 p = foo
 write(stdout, "success!")
 p = wrongfoo  #ERROR_MSG type mismatch
-
-
-
diff --git a/tests/typerel/tregionptrs.nim b/tests/typerel/tregionptrs.nim
deleted file mode 100644
index 9eeded18b..000000000
--- a/tests/typerel/tregionptrs.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-discard """
-  line: 16
-  errormsg: "type mismatch: got <BPtr> but expected 'APtr = ptr[RegionA, int]'"
-"""
-
-type
-  RegionA = object
-  APtr = RegionA ptr int
-  RegionB = object
-  BPtr = RegionB ptr int
-
-var x,xx: APtr
-var y: BPtr
-x = nil
-x = xx
-x = y
diff --git a/tests/typerel/trettypeinference.nim b/tests/typerel/trettypeinference.nim
index fa4e89cc8..aa0d66e5b 100644
--- a/tests/typerel/trettypeinference.nim
+++ b/tests/typerel/trettypeinference.nim
@@ -1,5 +1,5 @@
 discard """
-  msg:    "instantiated for string\ninstantiated for int\ninstantiated for bool"
+  nimout:    "instantiated for string\ninstantiated for int\ninstantiated for bool"
   output: "int\nseq[string]\nA\nB\n100\ntrue"
 """
 
diff --git a/tests/typerel/tsecondarrayproperty.nim b/tests/typerel/tsecondarrayproperty.nim
index 3a6879b16..315ad06bf 100644
--- a/tests/typerel/tsecondarrayproperty.nim
+++ b/tests/typerel/tsecondarrayproperty.nim
@@ -1,3 +1,7 @@
+discard """
+output: "4"
+"""
+
 
 type
   TFoo = object
@@ -25,4 +29,3 @@ echo f.second[1]
 
 #echo `second[]`(f,1)
 # this is the only way I could use it, but not what I expected
-
diff --git a/tests/typerel/tsigmatch.nim b/tests/typerel/tsigmatch.nim
new file mode 100644
index 000000000..7541f2028
--- /dev/null
+++ b/tests/typerel/tsigmatch.nim
@@ -0,0 +1,6 @@
+block: # bug #13618
+  proc test(x: Natural or BackwardsIndex): int =
+    int(x)
+
+  doAssert test(^1) == 1
+  doAssert test(1) == 1
diff --git a/tests/typerel/tstr_as_openarray.nim b/tests/typerel/tstr_as_openarray.nim
index fc28d6c93..14cd21479 100644
--- a/tests/typerel/tstr_as_openarray.nim
+++ b/tests/typerel/tstr_as_openarray.nim
@@ -3,18 +3,18 @@ discard """
 """
 var s = "HI"
 
-proc x (zz: openarray[char]) =
+proc x (zz: openArray[char]) =
   discard
 
 x s
 
-proc z [T] (zz: openarray[T]) =
+proc z [T] (zz: openArray[T]) =
   discard
 
 z s
 z([s,s,s])
 
-proc y [T] (arg: var openarray[T]) =
+proc y [T] (arg: var openArray[T]) =
   arg[0] = 'X'
 y s
 doAssert s == "XI"
diff --git a/tests/typerel/ttuple1.nim b/tests/typerel/ttuple1.nim
index d5c80800c..a03cc510e 100644
--- a/tests/typerel/ttuple1.nim
+++ b/tests/typerel/ttuple1.nim
@@ -1,3 +1,9 @@
+discard """
+output: '''
+M=1000, D=500, C=100, L=50, X=10, V=5, I=1
+'''
+"""
+
 const romanNumbers = [
     ("M", 1000), ("D", 500), ("C", 100),
     ("L", 50), ("X", 10), ("V", 5), ("I", 1) ]
@@ -12,5 +18,3 @@ for key, val in items(romanNumbers):
 proc PrintBiTuple(t: tuple[k: string, v: int]): int =
   stdout.write(t.k & "=" & $t.v & ", ")
   return 0
-
-
diff --git a/tests/typerel/ttynilinstantiation.nim b/tests/typerel/ttynilinstantiation.nim
new file mode 100644
index 000000000..303506689
--- /dev/null
+++ b/tests/typerel/ttynilinstantiation.nim
@@ -0,0 +1,7 @@
+proc foo[T: proc](x: T) =
+  # old error here:
+  let y = x
+  # invalid type: 'typeof(nil)' for let
+
+foo(nil) #[tt.Error
+   ^ type mismatch: got <typeof(nil)>]#
diff --git a/tests/typerel/ttypedesc_as_genericparam1.nim b/tests/typerel/ttypedesc_as_genericparam1.nim
index 9ae464842..8fdcf217e 100644
--- a/tests/typerel/ttypedesc_as_genericparam1.nim
+++ b/tests/typerel/ttypedesc_as_genericparam1.nim
@@ -1,6 +1,7 @@
 discard """
-  line: 6
-  errormsg: "type mismatch: got <type int>"
+  matrix: "--mm:refc"
+  errormsg: "type mismatch: got <typedesc[int]>"
+  line: 7
 """
 # bug #3079, #1146
-echo repr(int)
+echo repr(int)
\ No newline at end of file
diff --git a/tests/typerel/ttypedesc_as_genericparam1_orc.nim b/tests/typerel/ttypedesc_as_genericparam1_orc.nim
new file mode 100644
index 000000000..d528a7421
--- /dev/null
+++ b/tests/typerel/ttypedesc_as_genericparam1_orc.nim
@@ -0,0 +1,5 @@
+discard """
+  matrix: "--mm:orc"
+"""
+
+doAssert repr(int) == "int"
diff --git a/tests/typerel/ttypedesc_as_genericparam2.nim b/tests/typerel/ttypedesc_as_genericparam2.nim
index 0b4281269..882f66584 100644
--- a/tests/typerel/ttypedesc_as_genericparam2.nim
+++ b/tests/typerel/ttypedesc_as_genericparam2.nim
@@ -1,6 +1,7 @@
 discard """
-  line: 9
+  matrix: "--mm:refc"
   errormsg: "'repr' doesn't support 'void' type"
+  line: 10
 """
 
 # bug #2879
diff --git a/tests/typerel/ttypelessemptyset.nim b/tests/typerel/ttypelessemptyset.nim
index 5f49c33fd..a4b6f003a 100644
--- a/tests/typerel/ttypelessemptyset.nim
+++ b/tests/typerel/ttypelessemptyset.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "internal error: invalid kind for lastOrd(tyEmpty)"
+  errormsg: "statement returns no value that can be discarded"
 """
 var q = false
 discard (if q: {} else: {})
diff --git a/tests/typerel/ttypenoval.nim b/tests/typerel/ttypenoval.nim
index 720e5d662..ca6c920ec 100644
--- a/tests/typerel/ttypenoval.nim
+++ b/tests/typerel/ttypenoval.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "type mismatch: got <typedesc[int]> but expected 'int'"
   file: "ttypenoval.nim"
   line: 38
-  errormsg: "type mismatch: got <type int> but expected 'int'"
 """
 
 # A min-heap.
@@ -51,5 +51,3 @@ var
 newBinHeap(heap, 256)
 add(heap, 1, 100)
 print(heap)
-
-
diff --git a/tests/typerel/ttypenovalue.nim b/tests/typerel/ttypenovalue.nim
index b86baf8b4..4664253ea 100644
--- a/tests/typerel/ttypenovalue.nim
+++ b/tests/typerel/ttypenovalue.nim
@@ -1,7 +1,6 @@
 discard """
-  errormsg: "value expected, but got a type"
+  errormsg: "invalid type: 'typedesc[seq[tuple[title: string, body: string]]]' for var"
   line: 7
-  disabled: true
 """
 
 proc crashAndBurn() =
diff --git a/tests/typerel/tuncheckedarray_eq.nim b/tests/typerel/tuncheckedarray_eq.nim
new file mode 100644
index 000000000..c07696b5f
--- /dev/null
+++ b/tests/typerel/tuncheckedarray_eq.nim
@@ -0,0 +1,2 @@
+type p = ptr UncheckedArray[char]
+doAssert p is ptr UncheckedArray
\ No newline at end of file
diff --git a/tests/typerel/tvoid.nim b/tests/typerel/tvoid.nim
index 1e46d016e..8bb5691b8 100644
--- a/tests/typerel/tvoid.nim
+++ b/tests/typerel/tvoid.nim
@@ -35,3 +35,64 @@ ReturnT[void]()
 echo ReturnT[string]("abc")
 nothing()
 
+block: # typeof(stmt)
+  proc fn1(): auto =
+    discard
+  proc fn2(): auto =
+    1
+  doAssert type(fn1()) is void
+  doAssert typeof(fn1()) is void
+  doAssert typeof(fn1()) isnot int
+
+  doAssert type(fn2()) isnot void
+  doAssert typeof(fn2()) isnot void
+  when typeof(fn1()) is void: discard
+  else: doAssert false
+
+  doAssert typeof(1+1) is int
+  doAssert typeof((discard)) is void
+
+  type A1 = typeof(fn1())
+  doAssert A1 is void
+  type A2 = type(fn1())
+  doAssert A2 is void
+  doAssert A2 is A1
+
+  when false:
+    # xxx: MCS/UFCS doesn't work here: Error: expression 'fn1()' has no type (or is ambiguous)
+    type A3 = fn1().type
+  proc bar[T](a: T): string = $T
+  doAssert bar(1) == "int"
+  doAssert bar(fn1()) == "void"
+
+  proc bar2[T](a: T): bool = T is void
+  doAssert not bar2(1)
+  doAssert bar2(fn1())
+
+  block:
+    proc bar3[T](a: T): T = a
+    let a1 = bar3(1)
+    doAssert compiles(block:
+      let a1 = bar3(fn2()))
+    doAssert not compiles(block:
+      let a2 = bar3(fn1()))
+    doAssert compiles(block: bar3(fn1()))
+    doAssert compiles(bar3(fn1()))
+    doAssert typeof(bar3(fn1())) is void
+    doAssert not compiles(sizeof(bar3(fn1())))
+
+  block:
+    var a = 1
+    doAssert typeof((a = 2)) is void
+    doAssert typeof((a = 2; a = 3)) is void
+    doAssert typeof(block:
+      a = 2; a = 3) is void
+
+  block:
+    var a = 1
+    template bad1 = echo (a; a = 2)
+    doAssert not compiles(bad1())
+
+  block:
+    template bad2 = echo (nonexistent; discard)
+    doAssert not compiles(bad2())
diff --git a/tests/typerel/typedescs.nim b/tests/typerel/typedescs.nim
index 23b9ce64f..faf919e13 100644
--- a/tests/typerel/typedescs.nim
+++ b/tests/typerel/typedescs.nim
@@ -5,3 +5,18 @@ p(type((5, 6)))       # Compiles
 (type((5, 6))).p      # Doesn't compile (SIGSEGV: Illegal storage access.)
 type T = type((5, 6)) # Doesn't compile (SIGSEGV: Illegal storage access.)
 
+block: # issue #21677
+  type
+    Uints = uint16|uint32
+
+  template constructor(name: untyped, typ: typedesc[Uints], typ2: typedesc[Uints]) =
+    type
+      name = object
+        data: typ
+        data2: typ2
+
+    proc `init name`(data: typ, data2: typ2): name =
+      result.data = data
+      result.data2 = data2
+
+  constructor(Test, uint32, uint16)
diff --git a/tests/typerel/typedescs2.nim b/tests/typerel/typedescs2.nim
new file mode 100644
index 000000000..a0308719d
--- /dev/null
+++ b/tests/typerel/typedescs2.nim
@@ -0,0 +1,17 @@
+discard """
+  errormsg: "invalid type: 'typedesc[Table]' for const"
+  file: "typedescs2.nim"
+  line: 16
+"""
+
+# bug #9961
+
+import typetraits
+import tables
+
+proc test(v: typedesc) =
+  echo v.type.name
+
+# This crashes the compiler
+const b: typedesc = Table
+test b
diff --git a/tests/typerel/typredef.nim b/tests/typerel/typredef.nim
index 0b6aed875..c502a834c 100644
--- a/tests/typerel/typredef.nim
+++ b/tests/typerel/typredef.nim
@@ -1,8 +1,7 @@
 discard """
+  errormsg: "illegal recursion in type \'Uint8\'"
   file: "typredef.nim"
   line: 7
-  errormsg: "illegal recursion in type \'Uint8\'"
 """
 type
   Uint8 = Uint8 #ERROR_MSG illegal recursion in type 'Uint8'
-
diff --git a/tests/types/t1252.nim b/tests/types/t1252.nim
deleted file mode 100644
index c6a12e9f7..000000000
--- a/tests/types/t1252.nim
+++ /dev/null
@@ -1,12 +0,0 @@
-discard """
-  output: '''true
-true
-true
-true
-'''
-"""
-
-echo float32 isnot float64
-echo float32 isnot float
-echo int32 isnot int64
-echo int32 isnot int
diff --git a/tests/types/t14127_cast_number.nim b/tests/types/t14127_cast_number.nim
new file mode 100644
index 000000000..4bb23f22f
--- /dev/null
+++ b/tests/types/t14127_cast_number.nim
@@ -0,0 +1,30 @@
+discard """
+  targets: "c cpp js"
+"""
+block: # bug #14127
+  template int2uint(T) =
+    var a = -1
+    let b = cast[T](a)
+    doAssert b < 0
+    let c = b + 1
+    doAssert c is T
+    doAssert c == 0
+
+  int2uint(int8)
+  int2uint(int16)
+  int2uint(int32)
+  int2uint(int64)
+
+block: # maybe related
+  template uint2int(T) =
+    var a = 3
+    let b = cast[T](a)
+    doAssert b > 0
+    let c = b - 1
+    doAssert c is T
+    doAssert c == 2
+
+  uint2int(uint8)
+  uint2int(uint16)
+  uint2int(uint32)
+  uint2int(uint64)
diff --git a/tests/types/t15836.nim b/tests/types/t15836.nim
new file mode 100644
index 000000000..27d3ad0d0
--- /dev/null
+++ b/tests/types/t15836.nim
@@ -0,0 +1,11 @@
+discard """
+  errormsg: "type mismatch: got <int literal(1), proc (a: GenericParam): auto>"
+  line: 11
+""" 
+
+proc takesProc[T](x: T, f: proc(x: T): int) =
+    echo f(x) + 2
+
+takesProc(1, proc (a: int): int = 2) # ok, prints 4
+takesProc(1, proc (a: auto): auto = 2) # ok, prints 4
+takesProc(1, proc (a: auto): auto = "uh uh") # prints garbage
diff --git a/tests/types/t15836_2.nim b/tests/types/t15836_2.nim
new file mode 100644
index 000000000..6a16e2d22
--- /dev/null
+++ b/tests/types/t15836_2.nim
@@ -0,0 +1,21 @@
+import std/sugar
+
+type Tensor[T] = object
+  x: T
+
+proc numerical_gradient*[T](input: T, f: (proc(x: T): T), h = T(1e-5)): T {.inline.} =
+  result = default(T)
+
+proc numerical_gradient*[T](input: Tensor[T], f: (proc(x: Tensor[T]): T), h = T(1e-5)): Tensor[T] {.noinit.} =
+  result = default(Tensor[T])
+
+proc conv2d*[T](input: Tensor[T]): Tensor[T] {.inline.} =
+  result = default(Tensor[T])
+
+proc sum*[T](arg: Tensor[T]): T = default(T)
+
+proc sum*[T](arg: Tensor[T], axis: int): Tensor[T] {.noinit.} = default(Tensor[T])
+
+let dinput = Tensor[int](x: 1)
+let target_grad_input = dinput.numerical_gradient(
+    x => conv2d(x).sum())
diff --git a/tests/types/t21027.nim b/tests/types/t21027.nim
new file mode 100644
index 000000000..3a992177a
--- /dev/null
+++ b/tests/types/t21027.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "Invalid usage of cast, cast requires a type to convert to, e.g., cast[int](0d)."
+"""
+# bug #21027
+let x: uint64 = cast(5)
diff --git a/tests/types/t21260.nim b/tests/types/t21260.nim
new file mode 100644
index 000000000..90d6613c1
--- /dev/null
+++ b/tests/types/t21260.nim
@@ -0,0 +1,13 @@
+discard """
+  errormsg: "illegal recursion in type 'Foo'"
+  line: 8
+"""
+
+type
+  Kind = enum kA, kB
+  Foo = object
+    case k: Kind:
+    of kA:
+      foo: Foo
+    of kB:
+      discard
diff --git a/tests/types/t5640.nim b/tests/types/t5640.nim
deleted file mode 100644
index 5e1c99c4d..000000000
--- a/tests/types/t5640.nim
+++ /dev/null
@@ -1,6 +0,0 @@
-type
-  vecBase[I: static[int]] = distinct array[I, float32]
-  vec2* = vecBase[2]
-
-var v = vec2([0.0'f32, 0.0'f32])
-
diff --git a/tests/types/t5648.nim b/tests/types/t5648.nim
index c230cc12c..b3bd406b3 100644
--- a/tests/types/t5648.nim
+++ b/tests/types/t5648.nim
@@ -1,11 +1,17 @@
 discard """
-output: "ptr Foo"
+  output: '''
+ptr Foo
+'''
+joinable: false
 """
+# not joinable because it causes out of memory with --gc:boehm
+
+# issue #5648
 
 import typetraits
 
 type Foo = object
-  bar*: int
+  bar: int
 
 proc main() =
   var f = create(Foo)
@@ -17,5 +23,10 @@ proc main() =
   var g = Foo()
   g.bar = 3
 
-main()
+var
+  mainPtr = cast[pointer](main)
+  mainFromPtr = cast[typeof(main)](mainPtr)
 
+doAssert main == mainFromPtr
+
+main()
diff --git a/tests/types/t6456.nim b/tests/types/t6456.nim
new file mode 100644
index 000000000..19bbc2c02
--- /dev/null
+++ b/tests/types/t6456.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "type \'ptr void\' is not allowed"
+  line: 6
+"""
+
+proc foo(x: ptr void) =
+  discard
diff --git a/tests/types/t6969.nim b/tests/types/t6969.nim
new file mode 100644
index 000000000..14a8481cf
--- /dev/null
+++ b/tests/types/t6969.nim
@@ -0,0 +1,6 @@
+discard """
+errormsg: "invalid type: 'object' for var"
+line: 6
+"""
+
+var a: object
diff --git a/tests/types/t7905.nim b/tests/types/t7905.nim
deleted file mode 100644
index ef75bb86c..000000000
--- a/tests/types/t7905.nim
+++ /dev/null
@@ -1,33 +0,0 @@
-discard """
-  output: '''
-(member: "hello world")
-(member: 123.456)
-(member: "hello world", x: ...)
-(member: 123.456, x: ...)
-'''
-"""
-
-template foobar(arg: typed): untyped =
-  type
-    MyType = object
-      member: type(arg)
-
-  var myVar: MyType
-  myVar.member = arg
-  echo myVar
-
-foobar("hello world")
-foobar(123.456'f64)
-
-template foobarRec(arg: typed): untyped =
-  type
-    MyType = object
-      member: type(arg)
-      x: ref MyType
-
-  var myVar: MyType
-  myVar.member = arg
-  echo myVar
-
-foobarRec("hello world")
-foobarRec(123.456'f64)
diff --git a/tests/types/taliasbugs.nim b/tests/types/taliasbugs.nim
index 57254760a..f1b35edf6 100644
--- a/tests/types/taliasbugs.nim
+++ b/tests/types/taliasbugs.nim
@@ -1,5 +1,5 @@
 discard """
-  msg: '''true
+  nimout: '''true
 true
 true
 true
@@ -156,3 +156,14 @@ when true:
       Foo(i)
 
   var xx = xs.mapIt(asFoo($(it + 5)))
+
+
+block t4674:
+  type
+    FooObj[T] = object
+      v: T
+    Foo1[T] = FooObj[T]
+    Foo2 = FooObj
+    Foo1x = Foo1
+    Foo12x = Foo1 | Foo2
+    Foo2x = Foo2  # Error: illegal recursion in type 'Foo2x'
diff --git a/tests/types/tassignemptytuple.nim b/tests/types/tassignemptytuple.nim
index bdfc653a5..9d5a311ba 100644
--- a/tests/types/tassignemptytuple.nim
+++ b/tests/types/tassignemptytuple.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "cannot infer the type of the tuple"
   file: "tassignemptytuple.nim"
   line: 11
-  errormsg: "cannot infer the type of the tuple"
 """
 
 var
diff --git a/tests/types/tauto_canbe_void.nim b/tests/types/tauto_canbe_void.nim
index fd42cb701..c43215d1e 100644
--- a/tests/types/tauto_canbe_void.nim
+++ b/tests/types/tauto_canbe_void.nim
@@ -1,9 +1,15 @@
+discard """
+output: '''
+arg
+arg
+'''
+"""
 
-import future
+
+import sugar
 
 template tempo(s) =
   s("arg")
 
 tempo((s: string)->auto => echo(s))
 tempo((s: string) => echo(s))
-
diff --git a/tests/types/tcast1.nim b/tests/types/tcast1.nim
new file mode 100644
index 000000000..ab375b8b2
--- /dev/null
+++ b/tests/types/tcast1.nim
@@ -0,0 +1,63 @@
+discard """
+output: '''
+@[1.0, 2.0, 3.0]
+@[1.0, 2.0, 3.0]
+'''
+"""
+
+# bug #6406
+
+import sequtils
+
+proc remap1(s: seq[int], T: typedesc): seq[T] =
+  s.map do (x: int) -> T:
+    x.T
+
+proc remap2[T](s: seq[int], typ: typedesc[T]): seq[T] =
+  s.map do (x: int) -> T:
+    x.T
+
+echo remap1(@[1,2,3], float)
+echo remap2(@[1,2,3], float)
+
+
+#--------------------------------------------------------------------
+# conversion to bool, issue #13744
+proc test_conv_to_bool = 
+  var 
+    i0 = 0
+    i1 = 1
+    ih = high(uint)
+    il = low(int)
+
+    f0 = 0.0
+    f1 = 1.0
+    fh = Inf
+    fl = -Inf
+    f_nan = NaN
+
+  doAssert(bool(i0) == false)
+  doAssert(bool(i1) == true)
+  doAssert(bool(-i1) == true)
+  doAssert(bool(il) == true)
+  doAssert(bool(ih) == true)
+
+  doAssert(bool(f0) == false)
+  doAssert(bool(-f0) == false)
+  doAssert(bool(f1) == true)
+  doAssert(bool(-f1) == true)
+  doAssert(bool(fh) == true)
+  doAssert(bool(fl) == true)
+  doAssert(bool(fnan) == true) # NaN to bool gives true according to standard
+
+
+static:
+  doAssert(bool(0) == false)
+  doAssert(bool(-1) == true)
+  doAssert(bool(2) == true)
+  doAssert(bool(NaN) == true)
+  doAssert(bool(0.0) == false)
+  doAssert(bool(-0.0) == false)
+  test_conv_to_bool()
+test_conv_to_bool()
+
diff --git a/tests/types/tcyclic.nim b/tests/types/tcyclic.nim
new file mode 100644
index 000000000..651394c8b
--- /dev/null
+++ b/tests/types/tcyclic.nim
@@ -0,0 +1,135 @@
+## todo publish the `isCyclic` when it's mature.
+proc isCyclic(t: typedesc): bool {.magic: "TypeTrait".} =
+  ## Returns true if the type can potentially form a cyclic type
+
+template cyclicYes(x: typed) =
+  doAssert isCyclic(x)
+
+template cyclicNo(x: typed) =
+  doAssert not isCyclic(x)
+
+# atomic types are not cyclic
+cyclicNo(int)
+cyclicNo(float)
+cyclicNo(string)
+cyclicNo(char)
+cyclicNo(void)
+
+type
+  Object = object
+  Ref = ref object
+
+cyclicNo(Object)
+cyclicNo(Ref)
+
+type
+  Data1 = ref object
+  Data2 = ref object
+    id: Data1
+
+cyclicNo(Data2)
+
+type
+  Cyclone = ref object
+    data: Cyclone
+
+  Alias = Cyclone
+
+  Acyclic {.acyclic.} = ref object
+    data: Acyclic
+
+  LinkedNode = object
+    next: ref LinkedNode
+
+  LinkedNodeWithCursor = object
+    next {.cursor.} : ref LinkedNodeWithCursor
+
+cyclicYes(Cyclone)
+cyclicYes(Alias)
+cyclicNo(seq[Cyclone])
+cyclicNo((Cyclone, ))
+cyclicNo(Acyclic)
+
+cyclicYes(LinkedNode)
+cyclicNo(LinkedNodeWithCursor) # cursor doesn't increase rc, cannot form a cycle
+
+type
+  ObjectWithoutCycles = object
+    data: seq[ObjectWithoutCycles]
+
+cyclicNo(ObjectWithoutCycles)
+
+
+block:
+  type
+    Try = object
+      id: Best
+    Best = object
+      name: ref Try
+    Best2 = ref Best
+
+  cyclicYes(Best)
+  cyclicYes(Try)
+  cyclicNo(Best2)
+
+  type
+    Base = object
+      data: ref seq[Base]
+    Base2 = ref Base
+
+  cyclicYes(Base)
+  cyclicNo(Base2)
+
+
+  type
+    Base3 = ref object
+      id: Base3
+
+    Base4 = object
+      id: ref Base4
+
+  cyclicYes(Base3)
+  cyclicYes(Base4)
+  cyclicYes(ref Base4)
+
+block:
+  type Cyclic2 = object
+    x: ref (Cyclic2, int)
+
+  cyclicYes (Cyclic2, int)
+  cyclicYes (ref (Cyclic2, int))
+
+block:
+  type
+    myseq[T] = object
+      data: ptr UncheckedArray[T]
+    Node = ref object
+      kids: myseq[Node]
+
+  cyclicNo(Node)
+
+block:
+  type
+    myseq[T] = object
+      data: ptr UncheckedArray[T]
+    Node = ref object
+      kids: myseq[Node]
+
+  proc `=trace`(x: var myseq[Node]; env: pointer) = discard
+
+  cyclicYes(Node)
+
+block:
+  type
+    Foo = object
+      id: ptr ref Foo
+
+  cyclicNo(Foo)
+
+block:
+  type
+    InheritableObj = object of RootObj
+    InheritableRef = ref object of RootObj
+
+  cyclicYes(InheritableObj)
+  cyclicYes(InheritableRef)
diff --git a/tests/types/temptyseqs.nim b/tests/types/temptyseqs.nim
index 834f63729..6e7b58ced 100644
--- a/tests/types/temptyseqs.nim
+++ b/tests/types/temptyseqs.nim
@@ -22,7 +22,7 @@ when true:
   import os
 
   type
-    In_out = tuple[src, dest, options: string]
+    In_out = tuple[src, dest: string, options: ref int]
 
   let
     nil_var: In_out = ("hey"/"there", "something", nil)
@@ -75,16 +75,16 @@ varargso("foo", "bar", "baz")
 
 type
   Flago = enum
-    tfNeedsInit, tfNotNil
+    tfRequiresInit, tfNotNil
 
-var s: set[Flago] = {tfNeedsInit}
+var s: set[Flago] = {tfRequiresInit}
 
-if {tfNeedsInit, tfNotNil} * s != {}:
+if {tfRequiresInit, tfNotNil} * s != {}:
   echo "yes"
 else:
   echo "no"
 
-if {tfNeedsInit, tfNotNil} * s <= {tfNotNil}:
+if {tfRequiresInit, tfNotNil} * s <= {tfNotNil}:
   echo "yes"
 else:
   echo "no"
diff --git a/tests/types/texportgeneric.nim b/tests/types/texportgeneric.nim
new file mode 100644
index 000000000..9e6be2bb6
--- /dev/null
+++ b/tests/types/texportgeneric.nim
@@ -0,0 +1,8 @@
+discard """
+  errormsg: "{.exportc.} not allowed for generic types"
+  line: 6
+"""
+
+type Struct[T] {.exportc.} = object
+  a:int
+  b: T
\ No newline at end of file
diff --git a/tests/types/tfinalobj.nim b/tests/types/tfinalobj.nim
index 2fda73363..ad3085132 100644
--- a/tests/types/tfinalobj.nim
+++ b/tests/types/tfinalobj.nim
@@ -1,9 +1,9 @@
 discard """
-  output: "abc"
+  output: '''abc'''
 """
 
 type
-  TA = object {.pure, final.}
+  TA {.pure, final.} = object
     x: string
 
 var
@@ -13,4 +13,3 @@ a.x = "abc"
 doAssert TA.sizeof == string.sizeof
 
 echo a.x
-
diff --git a/tests/types/tillegalrecursion3.nim b/tests/types/tillegalrecursion3.nim
new file mode 100644
index 000000000..fb5c1641c
--- /dev/null
+++ b/tests/types/tillegalrecursion3.nim
@@ -0,0 +1,12 @@
+discard """
+  errormsg: "illegal recursion in type 'Foo'"
+"""
+
+type
+  Imported {.importc.} = object
+
+  Foo = object
+    b: Imported
+    a: Foo
+
+var myFoo: Foo
diff --git a/tests/types/tillegalseqrecursion.nim b/tests/types/tillegalseqrecursion.nim
new file mode 100644
index 000000000..9221bb38a
--- /dev/null
+++ b/tests/types/tillegalseqrecursion.nim
@@ -0,0 +1,6 @@
+discard """
+  errormsg: "illegal recursion in type 'CyclicSeq'"
+"""
+# issue #13715
+type
+  CyclicSeq = seq[ref CyclicSeq]
diff --git a/tests/types/tillegaltuplerecursion.nim b/tests/types/tillegaltuplerecursion.nim
new file mode 100644
index 000000000..cb75ad9c3
--- /dev/null
+++ b/tests/types/tillegaltuplerecursion.nim
@@ -0,0 +1,40 @@
+discard """
+  errormsg: "illegal recursion in type"
+"""
+
+# This is one big illegal type cycle. It doesn't really matter at
+# what line the error is reported, nor what name is picked to point
+# out the illegal recursion.
+
+type
+  MyType0 = ref tuple
+    children: MyType1
+
+  MyType1 = ref tuple
+    children: array[10, MyType2]
+
+  MyType2 = ref tuple
+    children: seq[MyType3]
+
+  MyType3 = ref tuple
+    children: UncheckedArray[MyType4]
+
+  MyType4 = ref tuple
+    children: MyType5
+
+  MyType5 = tuple
+    children: array[10, MyType6]
+
+  MyType6 = tuple
+    children: seq[MyType7]
+
+  MyType7 = tuple
+    children: UncheckedArray[MyType8]
+
+  MyType8 = tuple
+    children: ptr MyType9
+
+  MyType9 = tuple
+    children: MyType10
+
+  MyType10 = distinct seq[MyType0]
diff --git a/tests/types/tillegaltuplerecursiongeneric.nim b/tests/types/tillegaltuplerecursiongeneric.nim
new file mode 100644
index 000000000..da65d48eb
--- /dev/null
+++ b/tests/types/tillegaltuplerecursiongeneric.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "illegal recursion in type 'RefTree'"
+"""
+
+type
+  RefTree[T] = ref tuple[le, ri: RefTree[T]; data: T]
+  RefTreeInt = RefTree[int]
diff --git a/tests/types/tillegaltuplerecursiongeneric2.nim b/tests/types/tillegaltuplerecursiongeneric2.nim
new file mode 100644
index 000000000..36ebd63be
--- /dev/null
+++ b/tests/types/tillegaltuplerecursiongeneric2.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "illegal recursion in type 'TPearl'"
+"""
+
+type
+  TPearl[T] = tuple
+    next: TPearl[T]
+
+var x: TPearl[int]
diff --git a/tests/types/tillegaltyperecursion.nim b/tests/types/tillegaltyperecursion.nim
index 6ead902b7..372615c4d 100644
--- a/tests/types/tillegaltyperecursion.nim
+++ b/tests/types/tillegaltyperecursion.nim
@@ -1,66 +1,17 @@
 discard """
   cmd: "nim $target --threads:on $options $file"
   errormsg: "illegal recursion in type 'TIRC'"
-  line: 16
+  line: 12
 """
 
-import events
-import sockets
+import net
 import strutils
 import os
 
 type
-    TMessageReceivedEventArgs = object of EventArgs
-        Nick*: string
-        Message*: string
     TIRC = object
-        EventEmitter: EventEmitter
-        MessageReceivedHandler*: EventHandler
         Socket: Socket
         Thread: Thread[TIRC]
 
 proc initIRC*(): TIRC =
     result.Socket = socket()
-    result.EventEmitter = initEventEmitter()
-    result.MessageReceivedHandler = initEventHandler("MessageReceived")
-
-proc IsConnected*(irc: var TIRC): bool =
-    return running(irc.Thread)
-
-
-proc sendRaw*(irc: var TIRC, message: string) =
-    irc.Socket.send(message & "\r\L")
-proc handleData(irc: TIRC) {.thread.} =
-    var connected = False
-    while connected:
-        var tup = @[irc.Socket]
-        var o = select(tup, 200)
-        echo($o)
-        echo($len(tup))
-        if len(tup) == 1:
-            #Connected
-            connected = True
-
-            #Parse data here
-
-        else:
-            #Disconnected
-            connected = False
-            return
-
-proc Connect*(irc: var TIRC, nick: string, host: string, port: int = 6667) =
-    connect(irc.Socket, host, TPort(port), TDomain.AF_INET)
-    send(irc.Socket,"USER " & nick & " " & nick & " " & nick & " " & nick & "\r\L")
-    send(irc.Socket,"NICK " & nick & "\r\L")
-    var thread: Thread[TIRC]
-    createThread(thread, handleData, irc)
-    irc.Thread = thread
-
-
-
-
-when isMainModule:
-    var irc = initIRC()
-    irc.Connect("AmryBot[Nim]","irc.freenode.net",6667)
-    irc.sendRaw("JOIN #nim")
-    os.Sleep(4000)
diff --git a/tests/types/tillegaltyperecursion2.nim b/tests/types/tillegaltyperecursion2.nim
index b5ffdda72..c539a361d 100644
--- a/tests/types/tillegaltyperecursion2.nim
+++ b/tests/types/tillegaltyperecursion2.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "invalid recursion in type 'Executor'"
+  errormsg: "illegal recursion in type 'Executor'"
   line: 8
 """
 # bug reported by PR #5637
diff --git a/tests/types/tillegaltyperecursion3.nim b/tests/types/tillegaltyperecursion3.nim
new file mode 100644
index 000000000..8e1138329
--- /dev/null
+++ b/tests/types/tillegaltyperecursion3.nim
@@ -0,0 +1,10 @@
+discard """
+errormsg: "illegal recursion in type 'Weird'"
+"""
+
+# issue #3456
+
+import tables
+type
+  Weird = ref seq[Weird]
+var t = newTable[int, Weird]()
diff --git a/tests/types/tillrec.nim b/tests/types/tillrec.nim
index 18757140a..7584282b6 100644
--- a/tests/types/tillrec.nim
+++ b/tests/types/tillrec.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "illegal recursion in type \'TIllegal\'"
   file: "tillrec.nim"
   line: 13
-  errormsg: "illegal recursion in type \'TIllegal\'"
 """
 # test illegal recursive types
 
@@ -13,4 +13,3 @@ type
   TIllegal {.final.} = object  #ERROR_MSG illegal recursion in type 'TIllegal'
     y: int
     x: array[0..3, TIllegal]
-
diff --git a/tests/types/tinheritgenericparameter.nim b/tests/types/tinheritgenericparameter.nim
new file mode 100644
index 000000000..d0d313635
--- /dev/null
+++ b/tests/types/tinheritgenericparameter.nim
@@ -0,0 +1,39 @@
+discard """
+  cmd: "nim check --hints:off --warnings:off $file"
+  action: "reject"
+  nimout:'''
+tinheritgenericparameter.nim(36, 15) Error: Cannot inherit from: 'MyObject'
+tinheritgenericparameter.nim(36, 15) Error: Cannot inherit from: 'MyObject'
+tinheritgenericparameter.nim(36, 23) Error: object constructor needs an object type [error]
+tinheritgenericparameter.nim(36, 23) Error: expression '' has no type (or is ambiguous)
+tinheritgenericparameter.nim(37, 15) Error: Cannot inherit from: 'int'
+tinheritgenericparameter.nim(37, 15) Error: Cannot inherit from: 'int'
+tinheritgenericparameter.nim(37, 23) Error: object constructor needs an object type [error]
+tinheritgenericparameter.nim(37, 23) Error: expression '' has no type (or is ambiguous)
+'''
+"""
+
+type
+  MyObject = object
+  HorzLayout[Base, T] = ref object of Base
+    data: seq[T]
+  VertLayout[T, Base] = ref object of Base
+    data: seq[T]
+  UiElement = ref object of RootObj
+    a: int
+  MyType[T] = ref object of RootObj
+    data: seq[T]
+  OtherElement[T] = ref object of T
+  Child[T] = ref object of HorzLayout[UiElement, T]
+  Child2[T] = ref object of VertLayout[T, UiElement]
+  Child3[T] = ref object of HorzLayout[MyObject, T]
+  Child4[T] = ref object of HorzLayout[int, T]
+static:
+  var a = Child[int](a: 300, data: @[100, 200, 300])
+  assert a.a == 300
+  assert a.data == @[100, 200, 300]
+discard Child2[string]()
+discard Child3[string]()
+discard Child4[string]()
+discard OtherElement[MyType[int]]()
+
diff --git a/tests/types/tinheritref.nim b/tests/types/tinheritref.nim
index 00af0538d..c03e3bcab 100644
--- a/tests/types/tinheritref.nim
+++ b/tests/types/tinheritref.nim
@@ -1,9 +1,3 @@
-discard """
-  output: '''23
-1.5
-'''
-"""
-
 # bug #554, #179
 
 type T[E] =
@@ -13,14 +7,15 @@ type T[E] =
 var ob: T[int]
 
 ob = T[int](elem: 23)
-echo ob.elem
 
-type
-  TTreeIteratorA* = ref object {.inheritable.}
+doAssert ob.elem == 23
 
+type
   TKeysIteratorA* = ref object of TTreeIteratorA  #compiles
 
-  TTreeIterator* [T,D] = ref object {.inheritable.}
+  TTreeIteratorA* {.inheritable.} = ref object
+
+  TTreeIterator* [T,D] {.inheritable.} = ref object
 
   TKeysIterator* [T,D] = ref object of TTreeIterator[T,D]  #this not
 
@@ -47,4 +42,42 @@ type
 
 var x = Apple(kind: Smooth, skin: 1.5)
 var u = x.skin
-echo u
+
+doAssert u == 1.5
+
+type
+  BaseRef {.inheritable, pure.} = ref object
+    baseRef: int
+
+  SubRef = ref object of BaseRef
+
+  BasePtr {.inheritable, pure.} = ptr object
+    basePtr: int
+  SubPtr = ptr object of BasePtr
+
+  BaseObj {.inheritable, pure.} = object
+    baseObj: int
+
+  SubObj = object of BaseObj
+
+template baseObj[T](t: ptr T): untyped = T
+
+proc something123(): int =
+  var r : SubRef
+  r.new
+  var p : SubPtr
+  p = create(baseObj(p))
+  var r2 : ref BaseObj
+  r2.new
+
+  var accu = 0
+  # trigger code generation
+  accu += r.baseRef
+  accu += p.basePtr
+  accu += r2.baseObj
+
+  doAssert sizeof(r[]) == sizeof(int)
+  doAssert sizeof(baseObj(p)) == sizeof(int)
+  doAssert sizeof(r2[]) == sizeof(int)
+
+discard something123()
diff --git a/tests/types/tisop.nim b/tests/types/tisop.nim
deleted file mode 100644
index ad5928016..000000000
--- a/tests/types/tisop.nim
+++ /dev/null
@@ -1,48 +0,0 @@
-discard """
-  disabled: true
-"""
-
-import typetraits
-
-type
-  TRecord = (tuple) or (object)
-
-  TFoo[T, U] = object
-    x: int
-
-    when T is string:
-      y: float
-    else:
-      y: string
-
-    when U is TRecord:
-      z: float
-
-  E = enum A, B, C
-
-macro m(t: typedesc): typedesc =
-  if t is enum:
-    result = string
-  else:
-    result = int
-
-var f: TFoo[int, int]
-static: assert(f.y.type.name == "string")
-
-when compiles(f.z):
-  {.error: "Foo should not have a `z` field".}
-
-proc p(a, b: auto) =
-  when a.type is int:
-    static: assert false
-
-  var f: TFoo[m(a.type), b.type]
-  static:
-    assert f.x.type.name == "int"
-    echo  f.y.type.name
-    assert f.y.type.name == "float"
-    echo  f.z.type.name
-    assert f.z.type.name == "float"
-
-p(A, f)
-
diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim
index 2f9dbf245..c95b63c90 100644
--- a/tests/types/tisopr.nim
+++ b/tests/types/tisopr.nim
@@ -1,11 +1,11 @@
 discard """
   output: '''true true false yes
 false
-false
+true
 false
 true
 true
-no'''
+yes'''
 """
 
 proc IsVoid[T](): string =
@@ -68,8 +68,8 @@ type SeqOrSet[E] = seq[E] or set[E]
 type SeqOfInt = seq[int]
 type SeqOrSetOfInt = SeqOrSet[int]
 
-# This prints "false", which seems less correct that (1) printing "true" or (2)
-# raising a compiler error.
+# This prints "true", as expected. Previously "false" was returned and that
+# seemed less correct that (1) printing "true" or (2) raising a compiler error.
 echo seq is SeqOrSet
 
 # This prints "false", as expected.
@@ -89,3 +89,83 @@ proc test[T](x: T) =
     echo "no"
 
 test(7)
+
+block:
+  # bug #13066
+  type Bar[T1,T2] = object
+  type Foo[T1,T2] = object
+  type Foo2 = Foo
+  doAssert Foo2 is Foo
+  doAssert Foo is Foo2
+  doAssert Foo is Foo
+  doAssert Foo2 is Foo2
+  doAssert Foo2 isnot Bar
+  doAssert Foo[int,float] is Foo2[int,float]
+
+  # other
+  doAssert Foo[int,float] isnot Foo2[float,float]
+  doAssert Foo[int,float] is Foo2
+  doAssert Foo[int,float|int] is Foo2
+  doAssert Foo2[int,float|int] is Foo
+  doAssert Foo2[int,float|int] isnot Bar
+  doAssert int is (int|float)
+
+
+block:
+  # Slice[T] as static type issue
+  type
+    MyEnum = enum
+      x1, x2, x3, x4, x5, x6
+
+  proc enumGen[T: enum](s: static[Slice[T]]) = 
+    doAssert($s.a & "  " & $s.b == "x1  x3")
+
+  enumGen(x1..x3)
+
+block:
+  # issue #11142
+  type
+    MyObjParam[N: static int] = object
+      x: int
+
+    MyObj[P: static MyObjParam] = object
+      y: int
+
+  const P = MyObjParam[256](x: 2)
+  let Q = MyObj[P](y: 2)
+  doAssert($Q  == "(y: 2)")
+
+block: # previously tisop.nim
+  type
+    TRecord = (tuple) or (object)
+    TFoo[T, U] = object
+      x: int
+      when T is string:
+        y: float
+      else:
+        y: string
+      when U is TRecord:
+        z: float
+    E = enum A, B, C
+  template m(t: typedesc): typedesc =
+    when t is enum:
+      string
+    else:
+      int
+  var f: TFoo[int, int]
+  static: doAssert(typeof(f.y) is string)
+  when compiles(f.z):
+    {.error: "Foo should not have a `z` field".}
+  proc p(a, b: auto) =
+    when typeof(a) is int:
+      static: doAssert false
+    var f: TFoo[m(a.typeof), b.typeof]
+    static:
+      doAssert f.x.typeof is int
+      doAssert f.y.typeof is float
+      doAssert f.z.typeof is float
+  p(A, f)
+
+block: # issue #22850
+  doAssert not (type is int)
+  doAssert not (typedesc is int)
diff --git a/tests/types/tissues_types.nim b/tests/types/tissues_types.nim
new file mode 100644
index 000000000..6bb1258f4
--- /dev/null
+++ b/tests/types/tissues_types.nim
@@ -0,0 +1,118 @@
+discard """
+  output: '''true
+true
+true
+true
+(member: "hello world")
+(member: 123.456)
+(member: "hello world", x: ...)
+(member: 123.456, x: ...)
+0
+false
+'''
+"""
+
+block t1252:
+  echo float32 isnot float64
+  echo float32 isnot float
+  echo int32 isnot int64
+  echo int32 isnot int
+
+block t5640:
+  type
+    vecBase[I: static[int]] = distinct array[I, float32]
+    vec2 = vecBase[2]
+
+  var v = vec2([0.0'f32, 0.0'f32])
+
+block t7581:
+  discard int -1
+
+block t7905:
+  template foobar(arg: typed): untyped =
+    type
+      MyType = object
+        member: type(arg)
+
+    var myVar: MyType
+    myVar.member = arg
+    echo myVar
+
+  foobar("hello world")
+  foobar(123.456'f64)
+
+  template foobarRec(arg: typed): untyped =
+    type
+      MyType = object
+        member: type(arg)
+        x: ref MyType
+
+    var myVar: MyType
+    myVar.member = arg
+    echo myVar
+
+  foobarRec("hello world")
+  foobarRec(123.456'f64)
+
+# bug #5170
+
+when true:
+  type Foo = object
+    bar: bool
+
+  type Bar = object
+    sameBody: string
+
+  var b0: Bar
+  b0.sameBody = "abc"
+
+block:
+  type Foo = object
+    baz: int
+
+  type Bar = object
+    sameBody: string
+
+  var b1: Bar
+  b1.sameBody = "def"
+
+  var f2: Foo
+  echo f2.baz
+
+var f1: Foo
+echo f1.bar
+
+import macros
+
+block: # issue #12582
+  macro foo(T: type): type =
+    nnkBracketExpr.newTree(bindSym "array", newLit 1, T)
+  var
+    _: foo(int) # fine
+  type
+    Foo = object
+      x: foo(int) # fine
+    Bar = ref object
+      x: foo(int) # error
+  let b = Bar()
+  let b2 = Bar(x: [123])
+
+block:
+  when true: # bug #14710
+    type Foo[T] = object
+      x1: int
+      when T.sizeof == 4: discard # SIGSEGV
+      when sizeof(T) == 4: discard # ok
+    let t = Foo[float](x1: 1)
+    doAssert t.x1 == 1
+
+block:
+  template s(d: varargs[typed])=discard
+
+  proc something(x:float)=discard
+  proc something(x:int)=discard
+  proc otherthing()=discard
+
+  s(something)
+  s(otherthing, something)
+  s(something, otherthing)
diff --git a/tests/types/titerable.nim b/tests/types/titerable.nim
new file mode 100644
index 000000000..fe27de4fd
--- /dev/null
+++ b/tests/types/titerable.nim
@@ -0,0 +1,143 @@
+discard """
+  targets: "c js"
+"""
+
+from stdtest/testutils import accept, reject, whenVMorJs
+
+# toSeq-like templates
+
+template toSeq2(a: iterable): auto =
+  var ret: seq[typeof(a)]
+  for ai in a: ret.add ai
+  ret
+
+template toSeq3(a: iterable[string]): auto =
+  var ret: seq[typeof(a)]
+  for ai in a: ret.add ai
+  ret
+
+template toSeq4[T](a: iterable[T]): auto =
+  var ret: seq[typeof(a)]
+  for ai in a: ret.add ai
+  ret
+
+template toSeq5[T: SomeInteger](a: iterable[T]): auto =
+  var ret: seq[typeof(a)]
+  for ai in a: ret.add ai
+  ret
+
+template toSeq6(a: iterable[int | float]): auto =
+  var ret: seq[typeof(a)]
+  for ai in a: ret.add ai
+  ret
+
+template toSeq7(a: iterable[seq]): auto =
+  var ret: seq[typeof(a)]
+  for ai in a: ret.add ai
+  ret
+
+template fn7b(a: untyped) = discard
+template fn7c(a: typed) = discard
+template fn7d(a: auto) = discard
+template fn7e[T](a: T) = discard
+
+template fn8a(a: iterable) = discard
+template fn8b[T](a: iterable[T]) = discard
+template fn8c(a: iterable[int]) = discard
+
+template bad1 =
+  template fn4(a: int, b: iterable[float, int]) =
+    discard
+
+template bad2 =
+  proc fn4(a: iterable) = discard
+
+template bad3 =
+  proc fn4(a: iterable[int]) = discard
+
+template good4 =
+  template fn1(a: iterable) = discard
+  template fn2(a: iterable[int]) = discard
+
+# iterators
+iterator iota(n: int): auto =
+  for i in 0..<n: yield i
+
+iterator repeat[T](a: T, n: int): T =
+  for i in 0..<n:
+    yield a
+
+iterator myiter(n: int): auto =
+  for i in 0..<n: yield $(i*2)
+
+when not defined(js):
+  iterator iotaClosure(n: int): auto {.closure.} =
+    for i in 0..<n: yield i
+
+template main() =
+  let expected1 = @[0, 1, 2]
+  let expected2 = @["0", "2"]
+
+  doAssert toSeq2(myiter(2)) == expected2
+  doAssert toSeq2(iota(3)) == expected1
+  doAssert toSeq2(1.1.repeat(2)) == @[1.1, 1.1]
+
+  whenVMorJs: discard
+  do:
+    doAssert toSeq2(iotaClosure(3)) == expected1
+
+  when true:
+    # MCS/UFCS
+    doAssert iota(3).toSeq2() == expected1
+
+  doAssert toSeq3(myiter(2)) == expected2
+  accept toSeq3(myiter(2))
+  reject toSeq3(iota(3))
+
+  doAssert toSeq4(iota(3)) == expected1
+  doAssert toSeq4(myiter(2)) == expected2
+  
+  doAssert toSeq4(0..2) == expected1
+  doAssert toSeq4(items(0..2)) == expected1
+  doAssert toSeq4(items(@[0,1,2])) == expected1
+  reject toSeq4(@[0,1,2])
+  reject toSeq4(13)
+
+  block:
+    accept fn8a(iota(3))
+    accept fn7b(iota(3))
+    reject fn7c(iota(3))
+    reject fn7d(iota(3))
+    reject fn7e(iota(3))
+
+  block:
+    fn8a(iota(3))
+    reject fn8a(123)
+    reject fn8c(123)
+    reject fn8c(123.3)
+    accept fn8c(items(@[1,2]))
+
+  block:
+    # shows that iterable is more restrictive than untyped
+    reject fn8a(nonexistent)
+    accept fn7b(nonexistent)
+    reject fn7c(nonexistent)
+    reject fn7d(nonexistent)
+    reject fn7e(nonexistent)
+
+  doAssert toSeq5(iota(3)) == expected1
+  reject toSeq5(myiter(2))
+
+  doAssert toSeq6(iota(3)) == expected1
+  reject toSeq6(myiter(2))
+
+  doAssert toSeq2("abc".repeat(3)) == @["abc", "abc", "abc"]
+  doAssert toSeq2(@['x', 'y'].repeat(2)) == @[@['x', 'y'], @['x', 'y']]
+
+  reject bad1
+  reject bad2
+  reject bad3
+  accept good4
+
+static: main()
+main()
diff --git a/tests/types/tlent_var.nim b/tests/types/tlent_var.nim
new file mode 100644
index 000000000..73b5bef9b
--- /dev/null
+++ b/tests/types/tlent_var.nim
@@ -0,0 +1,25 @@
+discard """
+  output: ''''''
+"""
+
+type
+  MyObj = object
+    a: int
+
+proc test_lent(x: MyObj): lent int =
+  x.a
+
+proc test_var(x: var MyObj): var int =
+  x.a
+
+var x = MyObj(a: 5)
+
+doAssert: test_var(x).addr == x.a.addr
+doAssert: test_lent(x).addr == x.a.addr
+
+proc varProc(x: var int) =
+  x = 100
+
+doAssert: not compiles(test_lent(x) = 1)
+doAssert: not compiles(varProc(test_lent(x)))
+
diff --git a/tests/types/tnontype.nim b/tests/types/tnontype.nim
new file mode 100644
index 000000000..4e2bafb32
--- /dev/null
+++ b/tests/types/tnontype.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "expected type, but got: 3"
+"""
+
+type
+  Foo = (block:
+    int)
+  
+  Bar = 3
diff --git a/tests/types/told_pragma_syntax1.nim b/tests/types/told_pragma_syntax1.nim
new file mode 100644
index 000000000..49823662d
--- /dev/null
+++ b/tests/types/told_pragma_syntax1.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "invalid indentation"
+"""
+
+type Foo = object {.final.}
diff --git a/tests/types/told_pragma_syntax2.nim b/tests/types/told_pragma_syntax2.nim
new file mode 100644
index 000000000..eea185725
--- /dev/null
+++ b/tests/types/told_pragma_syntax2.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "invalid indentation"
+"""
+
+type Bar {.final.} [T] = object
diff --git a/tests/types/tparameterizedparent0.nim b/tests/types/tparameterizedparent0.nim
index 1b72a4e21..90e7a9c0c 100644
--- a/tests/types/tparameterizedparent0.nim
+++ b/tests/types/tparameterizedparent0.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "inheritance only works with non-final objects"
   file: "tparameterizedparent0.nim"
   line: 14
-  errormsg: "inheritance only works with non-final objects"
 """
 # bug #5264
 type
diff --git a/tests/types/tparameterizedparent1.nim b/tests/types/tparameterizedparent1.nim
index 24fb9a565..5da8189f4 100644
--- a/tests/types/tparameterizedparent1.nim
+++ b/tests/types/tparameterizedparent1.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "inheritance only works with non-final objects"
   file: "tparameterizedparent1.nim"
   line: 14
-  errormsg: "inheritance only works with non-final objects"
 """
 # bug #5264
 type
diff --git a/tests/types/tparameterizedparent3.nim b/tests/types/tparameterizedparent3.nim
index 58aaf80ea..fcca6453e 100644
--- a/tests/types/tparameterizedparent3.nim
+++ b/tests/types/tparameterizedparent3.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "attempt to redefine: 'color'"
   file: "tparameterizedparent3.nim"
   line: 13
-  errormsg: "attempt to redefine: 'color'"
 """
 # bug #5264
 type
diff --git a/tests/types/tparameterizedparent4.nim b/tests/types/tparameterizedparent4.nim
index a37461bb4..4759d9d9b 100644
--- a/tests/types/tparameterizedparent4.nim
+++ b/tests/types/tparameterizedparent4.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "attempt to redefine: 'grain'"
   file: "tparameterizedparent4.nim"
   line: 23
-  errormsg: "attempt to redefine: 'grain'"
 """
 # bug #5264
 type
diff --git a/tests/types/ttopdowninference.nim b/tests/types/ttopdowninference.nim
new file mode 100644
index 000000000..765761e99
--- /dev/null
+++ b/tests/types/ttopdowninference.nim
@@ -0,0 +1,333 @@
+block:
+  var s: seq[string] = (discard; @[])
+
+  var x: set[char] =
+    if true:
+      try:
+        case 1
+        of 1:
+          if false:
+            {'4'}
+          else:
+            block:
+              s.add "a"
+              {}
+        else: {'3'}
+      except: {'2'}
+    else: {'1'}
+  doAssert x is set[char]
+  doAssert x == {}
+  doAssert s == @["a"]
+
+  x = {'a', 'b'}
+  doAssert x == {'a', 'b'}
+
+  x = (s.add "b"; {})
+  doAssert x == {}
+  doAssert s == @["a", "b"]
+
+  let x2: set[byte] = {1}
+  doAssert x2 == {1u8}
+
+block:
+  let x3: array[0..2, byte] = [1, 2, 3]
+  #let x4: openarray[byte] = [1, 2, 3]
+  #let x5: openarray[byte] = @[1, 2, 3]
+  let x6: seq[byte] = @[1, 2, 3]
+  let x7: seq[seq[float32]] = @[@[1, 2, 3], @[4.3, 5, 6]]
+  type ABC = enum a, b, c
+  let x8: array[ABC, byte] = [1, 2, 3]
+  doAssert x8[a] == 1
+  doAssert x8[a] + x8[b] == x8[c]
+
+  const x9: array[-2..2, float] = [0, 1, 2, 3, 4]
+  let x10: array[ABC, byte] = block:
+    {.gcsafe.}:
+      [a: 1, b: 2, c: 3]
+  proc `@`(x: float): float = x + 1
+  doAssert @1 == 2
+  let x11: seq[byte] = system.`@`([1, 2, 3])
+
+block:
+  type Foo = object
+    x: BiggestInt
+  var foo: Foo
+  foo.x = case true
+  of true: ord(1)
+  else: 0
+  foo.x = if true: ord(1) else: 0
+
+block:
+  type Foo = object
+    x: (float, seq[(byte, seq[byte])])
+    
+  let foo = Foo(x: (1, @{2: @[], 3: @[4, 5]}))
+  doAssert foo.x == (1.0, @{2u8: @[], 3u8: @[4u8, 5]})
+
+block:
+  type Foo = object
+    x: tuple[a: float, b: seq[(byte, seq[byte])]]
+    
+  let foo = Foo(x: (a: 1, b: @{2: @[3, 4], 5: @[]}))
+  doAssert foo.x == (1.0, @{2u8: @[3u8, 4], 5u8: @[]})
+
+block:
+  proc foo(): seq[float] = @[1]
+
+  let fooLamb = proc(): seq[float] = @[1]
+
+  doAssert foo() == fooLamb()
+
+block:
+  type Foo[T] = float32
+
+  let x: seq[Foo[int32]] = @[1]
+
+block:
+  type Foo = ref object
+  type Bar[T] = ptr object
+
+  let x1: seq[Foo] = @[nil]
+  let x2: seq[Bar[int]] = @[nil]
+  let x3: seq[cstring] = @[nil]
+
+block:
+  let x: seq[cstring] = @["abc", nil, "def"]
+  doAssert x.len == 3
+  doAssert x[0] == cstring"abc"
+  doAssert x[1].isNil
+  doAssert x[2] == "def".cstring
+
+block:
+  type Foo = object
+    x: tuple[a: float, b: seq[(byte, seq[cstring])]]
+    
+  let foo = Foo(x: (a: 1, b: @{2: @[nil, "abc"]}))
+  doAssert foo.x == (1.0, @{2u8: @[cstring nil, cstring "abc"]})
+
+block:
+  type Foo = object
+    x: tuple[a: float, b: seq[(byte, seq[ptr int])]]
+    
+  let foo = Foo(x: (a: 1, b: @{2: @[nil, nil]}))
+  doAssert foo.x == (1.0, @{2u8: @[(ptr int)(nil), nil]})
+
+when false: # unsupported
+  block: # type conversion
+    let x = seq[(cstring, float32)](@{"abc": 1.0, "def": 2.0})
+    doAssert x[0] == (cstring"abc", 1.0'f32)
+    doAssert x[1] == (cstring"def", 2.0'f32)
+
+block: # enum
+  type Foo {.pure.} = enum a
+  type Bar {.pure.} = enum a, b, c
+
+  var s: seq[Bar] = @[a, b, c]
+
+block: # overload selection
+  proc foo(x, y: int): int = x + y + 1
+  proc foo(x: int): int = x - 1
+  var s: seq[proc (x, y: int): int] = @[nil, foo, foo]
+  var s2: seq[int]
+  for a in s:
+    if not a.isNil: s2.add(a(1, 2))
+  doAssert s2 == @[4, 4]
+
+block: # with generics?
+  proc foo(x, y: int): int = x + y + 1
+  proc foo(x: int): int = x - 1
+  proc bar[T](x, y: T): T = x - y
+  var s: seq[proc (x, y: int): int] = @[nil, foo, foo, bar]
+  var s2: seq[int]
+  for a in s:
+    if not a.isNil: s2.add(a(1, 2))
+  doAssert s2 == @[4, 4, -1]
+  proc foo(x, y: float): float = x + y + 1.0
+  var s3: seq[proc (x, y: float): float] = @[nil, foo, foo, bar]
+  var s4: seq[float]
+  for a in s3:
+    if not a.isNil: s4.add(a(1, 2))
+  doAssert s4 == @[4.0, 4, -1]
+
+block: # range types
+  block:
+    let x: set[range[1u8..5u8]] = {1, 3}
+    doAssert x == {range[1u8..5u8](1), 3}
+    doAssert $x == "{1, 3}"
+  block:
+    let x: seq[set[range[1u8..5u8]]] = @[{1, 3}]
+    doAssert x == @[{range[1u8..5u8](1), 3}]
+    doAssert $x[0] == "{1, 3}"
+  block:
+    let x: seq[range[1u8..5u8]] = @[1, 3]
+    doAssert x == @[range[1u8..5u8](1), 3]
+    doAssert $x == "@[1, 3]"
+  block: # already worked before, make sure it still works
+    let x: set[range['a'..'e']] = {'a', 'c'}
+    doAssert x == {range['a'..'e']('a'), 'c'}
+    doAssert $x == "{'a', 'c'}"
+  block: # extended
+    let x: seq[set[range['a'..'e']]] = @[{'a', 'c'}]
+    doAssert x[0] == {range['a'..'e']('a'), 'c'}
+    doAssert $x == "@[{'a', 'c'}]"
+  block:
+    type Foo = object
+      x: (range[1u8..5u8], seq[(range[1f32..5f32], seq[range['a'..'e']])])
+      
+    let foo = Foo(x: (1, @{2: @[], 3: @['c', 'd']}))
+    doAssert foo.x == (range[1u8..5u8](1u8), @{range[1f32..5f32](2f32): @[], 3f32: @[range['a'..'e']('c'), 'd']})
+  block:
+    type Foo = object
+      x: (range[1u8..5u8], seq[(range[1f32..5f32], seq[set[range['a'..'e']]])])
+      
+    let foo = Foo(x: (1, @{2: @[], 3: @[{'c', 'd'}]}))
+    doAssert foo.x == (range[1u8..5u8](1u8), @{range[1f32..5f32](2f32): @[], 3f32: @[{range['a'..'e']('c'), 'd'}]})
+
+block: # templates
+  template foo: untyped = (1, 2, "abc")
+  let x: (float, byte, cstring) = foo()
+  doAssert x[0] == float(1)
+  doAssert x[1] == byte(2)
+  doAssert x[2] == cstring("abc")
+  let (a, b, c) = x
+  doAssert a == float(1)
+  doAssert b == byte(2)
+  doAssert c == cstring("abc")
+
+
+proc foo(): set[char] = # bug #11259
+  discard "a"
+  {}
+
+discard foo()
+
+block: # bug #11085
+  const ok1: set[char] = {}
+  var ok1b: set[char] = {}
+
+  const ok2: set[char] = block:
+    {}
+
+  const ok3: set[char] = block:
+    var x: set[char] = {}
+    x
+  var ok3b: set[char] = block:
+    var x: set[char] = {}
+    x
+
+  var bad: set[char] = block:
+    {}
+
+# bug #6213
+block:
+  block:
+    type MyEnum = enum a, b
+    type MyTuple = tuple[x: set[MyEnum]]
+
+    var myVar: seq[MyTuple] = @[ (x: {}) ]
+    doAssert myVar.len == 1
+
+  block:
+    type
+      Foo = tuple
+        f: seq[string]
+        s: string
+
+    proc e(): seq[Foo] =
+      return @[
+        (@[], "asd")
+      ]
+
+    doAssert e()[0].f == @[]
+
+block: # bug #11777
+  type S = set[0..5]
+  var s: S = {1, 2}
+  doAssert 1 in s
+
+block: # bug #20807
+  var s: seq[string]
+  template fail =
+    s = @[]
+  template test(body: untyped) =
+    body
+  proc test(a: string) = discard
+  test: fail()
+  doAssert not (compiles do:
+    let x: seq[int] = `@`[string]([]))
+
+block: # bug #21377
+  proc b[T](v: T): seq[int] =
+    let x = 0
+    @[]
+
+  doAssert b(0) == @[]
+
+block: # bug #21377
+  proc b[T](v: T): seq[T] =
+    let x = 0
+    @[]
+
+  doAssert b(0) == @[]
+
+block: # bug #21377
+  proc b[T](v: T): set[bool] =
+    let x = 0
+    {}
+
+  doAssert b(0) == {}
+
+block: # bug #21377
+  proc b[T](v: T): array[0, int] =
+    let x = 0
+    []
+
+  doAssert b(0) == []
+
+block: # bug #21377
+  proc b[T](v: T): array[0, (string, string)] =
+    let x = 0
+    {:}
+
+  doAssert b(0) == {:}
+
+block: # bug #22180
+  type A = object
+  proc j() = discard
+
+  let x =
+    if false:
+      (ref A)(nil)
+    else:
+      if false:
+        quit 1
+      else:
+        if true:
+          j()
+          nil  # compiles with (ref A)(nil) here
+        else:
+          (ref A)(nil)
+  doAssert x.isNil
+  
+  let y =
+    case true
+    of false:
+      (ref A)(nil)
+    else:
+      case true
+      of false:
+        quit 1
+      else:
+        case true
+        of true:
+          j()
+          nil  # compiles with (ref A)(nil) here
+        else:
+          (ref A)(nil)
+  doAssert y.isNil
+
+block: # issue #24164, related regression
+  proc foo(x: proc ()) = discard
+  template bar(x: untyped = nil) =
+    foo(x)
+  bar()
diff --git a/tests/types/tyet_another_generic_regression.nim b/tests/types/tyet_another_generic_regression.nim
index 914166e06..2e5f9bb23 100644
--- a/tests/types/tyet_another_generic_regression.nim
+++ b/tests/types/tyet_another_generic_regression.nim
@@ -1,3 +1,7 @@
+discard """
+  output: ''''''
+"""
+
 import system
 
 type Bar[T] = ref object
@@ -11,3 +15,27 @@ proc Foo[T: Bar[types]](): T =
 
 discard Foo[Bar[int32]]()
 #bug #6073
+
+# bug #11479
+
+import tables
+
+proc test() =
+  discard readfile("temp.nim")
+  echo "ho"
+
+const
+  map = {
+    "test": test,
+  }.toTable
+
+#map["test"]()
+
+#-------------------------------------------------------------------
+# bug
+const val = 10
+ 
+type 
+  t = object
+    when val >= 10:
+      a: int
diff --git a/tests/untestable/gdb/gdb_pretty_printer_test.py b/tests/untestable/gdb/gdb_pretty_printer_test.py
new file mode 100644
index 000000000..aed0cfeb0
--- /dev/null
+++ b/tests/untestable/gdb/gdb_pretty_printer_test.py
@@ -0,0 +1,64 @@
+import gdb
+import re
+import sys
+# this test should test the gdb pretty printers of the nim
+# library. But be aware this test is not complete. It only tests the
+# command line version of gdb. It does not test anything for the
+# machine interface of gdb. This means if if this test passes gdb
+# frontends might still be broken.
+
+gdb.execute("set python print-stack full")
+gdb.execute("source ../../../tools/debug/nim-gdb.py")
+# debug all instances of the generic function `myDebug`, should be 14
+gdb.execute("rbreak myDebug")
+gdb.execute("run")
+
+outputs = [
+  'meTwo',
+  '""',
+  '"meTwo"',
+  '{meOne, meThree}',
+  'MyOtherEnum(1)',
+  '{MyOtherEnum(0), MyOtherEnum(2)}',
+  'array = {1, 2, 3, 4, 5}',
+  'seq(0, 0)',
+  'seq(0, 10)',
+  'array = {"one", "two"}',
+  'seq(3, 3) = {1, 2, 3}',
+  'seq(3, 3) = {"one", "two", "three"}',
+  'Table(3, 64) = {[4] = "four", [5] = "five", [6] = "six"}',
+  'Table(3, 8) = {["two"] = 2, ["three"] = 3, ["one"] = 1}',
+  '{a = 1, b = "some string"}',
+  '("hello", 42)'
+]
+
+argRegex = re.compile("^.* = (?:No suitable Nim \$ operator found for type: \w+\s*)*(.*)$")
+# Remove this error message which can pop up
+noSuitableRegex = re.compile("(No suitable Nim \$ operator found for type: \w+\s*)")
+
+for i, expected in enumerate(outputs):
+  gdb.write(f"\x1b[38;5;105m{i+1}) expecting: {expected}: \x1b[0m", gdb.STDLOG)
+  gdb.flush()
+  currFrame = gdb.selected_frame()
+  functionSymbol = currFrame.block().function
+  assert functionSymbol.line == 24, str(functionSymbol.line)
+  raw = ""
+  if i == 6:
+    # myArray is passed as pointer to int to myDebug. I look up myArray up in the stack
+    gdb.execute("up")
+    raw = gdb.parse_and_eval("myArray")    
+  elif i == 9:
+    # myOtherArray is passed as pointer to int to myDebug. I look up myOtherArray up in the stack
+    gdb.execute("up")
+    raw = gdb.parse_and_eval("myOtherArray")
+  else:
+    rawArg = re.sub(noSuitableRegex, "", gdb.execute("info args", to_string = True))
+    raw = rawArg.split("=", 1)[-1].strip()
+  output = str(raw)
+
+  if output != expected:
+    gdb.write(f"\x1b[38;5;196m ({output}) != expected: ({expected})\x1b[0m\n", gdb.STDERR)
+    gdb.execute("quit 1")
+  else:
+    gdb.write("\x1b[38;5;34mpassed\x1b[0m\n", gdb.STDLOG)
+  gdb.execute("continue")
diff --git a/tests/untestable/gdb/gdb_pretty_printer_test_program.nim b/tests/untestable/gdb/gdb_pretty_printer_test_program.nim
new file mode 100644
index 000000000..163c99860
--- /dev/null
+++ b/tests/untestable/gdb/gdb_pretty_printer_test_program.nim
@@ -0,0 +1,89 @@
+
+
+import tables
+
+type
+  MyEnum = enum
+    meOne,
+    meTwo,
+    meThree,
+    meFour,
+
+  MyOtherEnum = enum
+    moOne,
+    moTwo,
+    moThree,
+    moFoure,
+  
+  MyObj = object
+    a*: int
+    b*: string
+
+var counter = 0
+
+proc myDebug[T](arg: T): void =
+  counter += 1
+
+proc testProc(): void =
+  var myEnum = meTwo
+  myDebug(myEnum) #1
+  
+  # create a string, but don't allocate it
+  var myString: string
+  myDebug(myString) #2
+
+  # create a string object but also make the NTI for MyEnum is generated
+  myString = $myEnum
+  myDebug(myString) #3
+  
+  var mySet = {meOne,meThree}
+  myDebug(mySet) #4
+
+  # for MyOtherEnum there is no NTI. This tests the fallback for the pretty printer.
+  var moEnum = moTwo
+  myDebug(moEnum) #5
+
+  var moSet = {moOne,moThree}
+  myDebug(moSet) #6
+
+  let myArray = [1,2,3,4,5]
+  myDebug(myArray) #7
+
+  # implicitly initialized seq test
+  var mySeq: seq[string]
+  myDebug(mySeq) #8
+
+  # len not equal to capacity
+  let myOtherSeq = newSeqOfCap[string](10)
+  myDebug(myOtherSeq) #9
+
+  let myOtherArray = ["one","two"]
+  myDebug(myOtherArray) #10
+
+  # numeric sec
+  var mySeq3 = @[1,2,3]
+  myDebug(mySeq3) #11
+
+  # seq had to grow
+  var mySeq4 = @["one","two","three"]
+  myDebug(mySeq4) #12
+
+  var myTable = initTable[int, string]()
+  myTable[4] = "four"
+  myTable[5] = "five"
+  myTable[6] = "six"
+  myDebug(myTable) #13
+
+  var myOtherTable = {"one": 1, "two": 2, "three": 3}.toTable
+  myDebug(myOtherTable) #14
+
+  var obj = MyObj(a: 1, b: "some string")
+  myDebug(obj) #15
+
+  var tup = ("hello", 42)
+  myDebug(tup) # 16
+
+  assert counter == 16
+
+
+testProc()
diff --git a/tests/untestable/gdb/gdb_pretty_printer_test_run.sh b/tests/untestable/gdb/gdb_pretty_printer_test_run.sh
new file mode 100755
index 000000000..411c68435
--- /dev/null
+++ b/tests/untestable/gdb/gdb_pretty_printer_test_run.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+set -e
+# Compile the test project with fresh debug information.
+nim c --debugger:native --mm:orc --out:gdbNew gdb_pretty_printer_test_program.nim
+echo "Running new runtime tests..."
+# 2>&1 redirects stderr to stdout (all output in stdout)
+gdb -x gdb_pretty_printer_test.py --batch-silent --args gdbNew 2>&1
+
+
+# Do it all again, but with old runtime
+nim c --debugger:native --mm:refc --out:gdbOld gdb_pretty_printer_test_program.nim &> /dev/null
+echo "Running old runtime tests"
+gdb -x gdb_pretty_printer_test.py --batch-silent --args gdbOld 2>&1
diff --git a/tests/untestable/network/README.md b/tests/untestable/network/README.md
new file mode 100644
index 000000000..173cf105f
--- /dev/null
+++ b/tests/untestable/network/README.md
@@ -0,0 +1,8 @@
+This directory contains tests that require networking and cannot be run in CI.
+
+The tests can be run manually during development using:
+```nim
+./koch tests cat untestable/network/stdlib
+```
+
+The directory structure mimics tests/
diff --git a/tests/untestable/network/stdlib/tnet.nim b/tests/untestable/network/stdlib/tnet.nim
new file mode 100644
index 000000000..cb0f38944
--- /dev/null
+++ b/tests/untestable/network/stdlib/tnet.nim
@@ -0,0 +1,16 @@
+discard """
+outputsub: ""
+"""
+
+import net, nativesockets
+import unittest
+
+suite "getPrimaryIPAddr":
+  test "localhost v4":
+    check getPrimaryIPAddr(parseIpAddress("127.0.0.1")) == parseIpAddress("127.0.0.1")
+
+  test "localhost v6":
+    check getPrimaryIPAddr(parseIpAddress("::1")) == parseIpAddress("::1")
+
+  test "v4":
+    check getPrimaryIPAddr() != parseIpAddress("127.0.0.1")
diff --git a/tests/untestable/thttpclient_ssl_disabled.nim b/tests/untestable/thttpclient_ssl_disabled.nim
new file mode 100644
index 000000000..b95dad2c6
--- /dev/null
+++ b/tests/untestable/thttpclient_ssl_disabled.nim
@@ -0,0 +1,36 @@
+#
+#            Nim - SSL integration tests
+#        (c) Copyright 2017 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+## Compile and run with:
+## nim r --putenv:NIM_TESTAMENT_REMOTE_NETWORKING:1 -d:nimDisableCertificateValidation -d:ssl -p:. tests/untestable/thttpclient_ssl_disabled.nim
+
+from stdtest/testutils import enableRemoteNetworking
+when enableRemoteNetworking and (defined(nimTestsEnableFlaky) or not defined(openbsd)):
+  import httpclient, net, unittest
+
+  const expired = "https://expired.badssl.com/"
+
+  doAssert defined(nimDisableCertificateValidation)
+
+  suite "SSL certificate check - disabled":
+
+    test "httpclient in insecure mode":
+      var ctx = newContext(verifyMode = CVerifyPeer)
+      var client = newHttpClient(sslContext = ctx)
+      let a = $client.getContent(expired)
+
+    test "httpclient in insecure mode":
+      var ctx = newContext(verifyMode = CVerifyPeerUseEnvVars)
+      var client = newHttpClient(sslContext = ctx)
+      let a = $client.getContent(expired)
+
+    test "net socket in insecure mode":
+      var sock = newSocket()
+      var ctx = newContext(verifyMode = CVerifyPeerUseEnvVars)
+      ctx.wrapSocket(sock)
+      sock.connect("expired.badssl.com", 443.Port)
+      sock.close
diff --git a/tests/untestable/thttpclient_ssl_env_var.nim b/tests/untestable/thttpclient_ssl_env_var.nim
new file mode 100644
index 000000000..3f25a6ff4
--- /dev/null
+++ b/tests/untestable/thttpclient_ssl_env_var.nim
@@ -0,0 +1,74 @@
+#
+#            Nim - SSL integration tests
+#        (c) Copyright 2017 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+## Warning: this test performs external networking.
+## Compile with:
+## ./bin/nim c -d:ssl -p:. tests/untestable/thttpclient_ssl_env_var.nim
+##
+## Test with:
+##  SSL_CERT_FILE=BogusInexistentFileName tests/untestable/thttpclient_ssl_env_var
+##  SSL_CERT_DIR=BogusInexistentDirName tests/untestable/thttpclient_ssl_env_var
+
+import httpclient, unittest, os
+from net import newSocket, newContext, wrapSocket, connect, close, Port,
+  CVerifyPeerUseEnvVars
+from strutils import contains
+
+const
+  expired = "https://expired.badssl.com/"
+  good = "https://google.com/"
+
+
+suite "SSL certificate check":
+
+  test "httpclient with inexistent file":
+    if existsEnv("SSL_CERT_FILE"):
+      var ctx = newContext(verifyMode=CVerifyPeerUseEnvVars)
+      var client = newHttpClient(sslContext=ctx)
+      checkpoint("Client created")
+      check client.getContent("https://google.com").contains("doctype")
+      checkpoint("Google ok")
+      try:
+        let a = $client.getContent(good)
+        echo "Connection should have failed"
+        fail()
+      except:
+        echo getCurrentExceptionMsg()
+        check getCurrentExceptionMsg().contains("certificate verify failed")
+
+    elif existsEnv("SSL_CERT_DIR"):
+      try:
+        var ctx = newContext(verifyMode=CVerifyPeerUseEnvVars)
+        var client = newHttpClient(sslContext=ctx)
+        echo "Should have raised 'No SSL/TLS CA certificates found.'"
+        fail()
+      except:
+        check getCurrentExceptionMsg() ==
+          "No SSL/TLS CA certificates found."
+
+  test "net socket with inexistent file":
+    if existsEnv("SSL_CERT_FILE"):
+      var sock = newSocket()
+      var ctx = newContext(verifyMode=CVerifyPeerUseEnvVars)
+      ctx.wrapSocket(sock)
+      checkpoint("Socket created")
+      try:
+        sock.connect("expired.badssl.com", 443.Port)
+        fail()
+      except:
+        sock.close
+        check getCurrentExceptionMsg().contains("certificate verify failed")
+
+    elif existsEnv("SSL_CERT_DIR"):
+      var sock = newSocket()
+      checkpoint("Socket created")
+      try:
+        var ctx = newContext(verifyMode=CVerifyPeerUseEnvVars) # raises here
+        fail()
+      except:
+        check getCurrentExceptionMsg() ==
+          "No SSL/TLS CA certificates found."
diff --git a/tests/untestable/thttpclient_ssl_remotenetwork.nim b/tests/untestable/thttpclient_ssl_remotenetwork.nim
new file mode 100644
index 000000000..3cb759516
--- /dev/null
+++ b/tests/untestable/thttpclient_ssl_remotenetwork.nim
@@ -0,0 +1,230 @@
+#
+#
+#            Nim - SSL integration tests
+#        (c) Copyright 2017 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+## Test with:
+## nim r --putenv:NIM_TESTAMENT_REMOTE_NETWORKING:1 -d:ssl -p:. --threads:on tests/untestable/thttpclient_ssl_remotenetwork.nim
+##
+## See https://github.com/FedericoCeratto/ssl-comparison/blob/master/README.md
+## for a comparison with other clients.
+
+from stdtest/testutils import enableRemoteNetworking
+when enableRemoteNetworking and (defined(nimTestsEnableFlaky) or not defined(windows) and not defined(openbsd)):
+  # Not supported on Windows due to old openssl version
+  import
+    httpclient,
+    net,
+    strutils,
+    threadpool,
+    unittest
+
+
+  type
+    # bad and dubious tests should not pass SSL validation
+    # "_broken" mark the test as skipped. Some tests have different
+    # behavior depending on OS and SSL version!
+    # TODO: chase and fix the broken tests
+    Category = enum
+      good, bad, dubious, good_broken, bad_broken, dubious_broken
+    CertTest = tuple[url:string, category:Category, desc: string]
+
+  # badssl certs sometimes expire, set to false when that happens
+  when true:
+    const certificate_tests: array[0..54, CertTest] = [
+      ("https://wrong.host.badssl.com/", bad, "wrong.host"),
+      ("https://captive-portal.badssl.com/", bad, "captive-portal"),
+      ("https://expired.badssl.com/", bad, "expired"),
+      ("https://google.com/", good, "good"),
+      ("https://self-signed.badssl.com/", bad, "self-signed"),
+      ("https://untrusted-root.badssl.com/", bad, "untrusted-root"),
+      ("https://revoked.badssl.com/", bad_broken, "revoked"),
+      ("https://pinning-test.badssl.com/", bad_broken, "pinning-test"),
+      ("https://no-common-name.badssl.com/", bad, "no-common-name"),
+      ("https://no-subject.badssl.com/", bad, "no-subject"),
+      ("https://sha1-intermediate.badssl.com/", bad, "sha1-intermediate"),
+      ("https://sha256.badssl.com/", good, "sha256"),
+      ("https://sha384.badssl.com/", bad, "sha384"),
+      ("https://sha512.badssl.com/", bad, "sha512"),
+      ("https://1000-sans.badssl.com/", bad, "1000-sans"),
+      ("https://10000-sans.badssl.com/", good_broken, "10000-sans"),
+      ("https://ecc256.badssl.com/", good_broken, "ecc256"),
+      ("https://ecc384.badssl.com/", good_broken, "ecc384"),
+      ("https://rsa2048.badssl.com/", good, "rsa2048"),
+      ("https://rsa8192.badssl.com/", dubious_broken, "rsa8192"),
+      ("http://http.badssl.com/", good, "regular http"),
+      ("https://http.badssl.com/", bad_broken, "http on https URL"),  # FIXME
+      ("https://cbc.badssl.com/", dubious, "cbc"),
+      ("https://rc4-md5.badssl.com/", bad, "rc4-md5"),
+      ("https://rc4.badssl.com/", bad, "rc4"),
+      ("https://3des.badssl.com/", bad, "3des"),
+      ("https://null.badssl.com/", bad, "null"),
+      ("https://mozilla-old.badssl.com/", bad_broken, "mozilla-old"),
+      ("https://mozilla-intermediate.badssl.com/", dubious_broken, "mozilla-intermediate"),
+      ("https://mozilla-modern.badssl.com/", good, "mozilla-modern"),
+      ("https://dh480.badssl.com/", bad, "dh480"),
+      ("https://dh512.badssl.com/", bad, "dh512"),
+      ("https://dh1024.badssl.com/", dubious_broken, "dh1024"),
+      ("https://dh2048.badssl.com/", good, "dh2048"),
+      ("https://dh-small-subgroup.badssl.com/", bad_broken, "dh-small-subgroup"),
+      ("https://dh-composite.badssl.com/", bad_broken, "dh-composite"),
+      ("https://static-rsa.badssl.com/", dubious, "static-rsa"),
+      ("https://tls-v1-0.badssl.com:1010/", dubious, "tls-v1-0"),
+      ("https://tls-v1-1.badssl.com:1011/", dubious, "tls-v1-1"),
+      ("https://invalid-expected-sct.badssl.com/", bad, "invalid-expected-sct"),
+      ("https://hsts.badssl.com/", good, "hsts"),
+      ("https://upgrade.badssl.com/", good, "upgrade"),
+      ("https://preloaded-hsts.badssl.com/", good, "preloaded-hsts"),
+      ("https://subdomain.preloaded-hsts.badssl.com/", bad, "subdomain.preloaded-hsts"),
+      ("https://https-everywhere.badssl.com/", good, "https-everywhere"),
+      ("https://long-extended-subdomain-name-containing-many-letters-and-dashes.badssl.com/", good,
+        "long-extended-subdomain-name-containing-many-letters-and-dashes"),
+      ("https://longextendedsubdomainnamewithoutdashesinordertotestwordwrapping.badssl.com/", good,
+        "longextendedsubdomainnamewithoutdashesinordertotestwordwrapping"),
+      ("https://superfish.badssl.com/", bad, "(Lenovo) Superfish"),
+      ("https://edellroot.badssl.com/", bad, "(Dell) eDellRoot"),
+      ("https://dsdtestprovider.badssl.com/", bad, "(Dell) DSD Test Provider"),
+      ("https://preact-cli.badssl.com/", bad, "preact-cli"),
+      ("https://webpack-dev-server.badssl.com/", bad, "webpack-dev-server"),
+      ("https://mitm-software.badssl.com/", bad, "mitm-software"),
+      ("https://sha1-2016.badssl.com/", dubious, "sha1-2016"),
+      ("https://sha1-2017.badssl.com/", bad, "sha1-2017"),
+    ]
+  else:
+    const certificate_tests: array[0..0, CertTest] = [
+      ("https://google.com/", good, "good")
+    ]
+
+
+  template evaluate(exception_msg: string, category: Category, desc: string) =
+    # Evaluate test outcome. Tests flagged as `_broken` are evaluated and skipped
+    let raised = (exception_msg.len > 0)
+    let should_not_raise = category in {good, dubious_broken, bad_broken}
+    if should_not_raise xor raised:
+      # we are seeing a known behavior
+      if category in {good_broken, dubious_broken, bad_broken}:
+        skip()
+      if raised:
+        # check exception_msg == "No SSL certificate found." or
+        doAssert exception_msg == "No SSL certificate found." or
+          exception_msg == "SSL Certificate check failed." or
+          exception_msg.contains("certificate verify failed") or
+          exception_msg.contains("key too small") or
+          exception_msg.contains("alert handshake failure") or
+          exception_msg.contains("bad dh p length") or
+          # TODO: This one should only triggers for 10000-sans
+          exception_msg.contains("excessive message size"), exception_msg
+
+    else:
+      # this is unexpected
+      var fatal = true
+      var msg = ""
+      if raised:
+        msg = "         $# ($#) raised: $#" % [desc, $category, exception_msg]
+        if "500 Internal Server Error" in exception_msg:
+          # refs https://github.com/nim-lang/Nim/issues/16338#issuecomment-804300278
+          # we got: `good (good) raised: 500 Internal Server Error`
+          fatal = false
+          msg.add " (http 500 => assuming this is not our problem)"
+      else:
+        msg = "         $# ($#) did not raise" % [desc, $category]
+
+      if category in {good, dubious, bad} and fatal:
+        echo "D20210322T121353: error: " & msg
+        fail()
+      else:
+        echo "D20210322T121353: warning: " & msg
+
+
+  suite "SSL certificate check - httpclient":
+
+    for i, ct in certificate_tests:
+
+      test ct.desc:
+        var ctx = newContext(verifyMode=CVerifyPeer)
+        var client = newHttpClient(sslContext=ctx)
+        let exception_msg =
+          try:
+            let a = $client.getContent(ct.url)
+            ""
+          except:
+            getCurrentExceptionMsg()
+
+        evaluate(exception_msg, ct.category, ct.desc)
+
+
+
+  # threaded tests
+
+
+  type
+    TTOutcome = ref object
+      desc, exception_msg: string
+      category: Category
+
+  proc run_t_test(ct: CertTest): TTOutcome {.thread.} =
+    ## Run test in a {.thread.} - return by ref
+    result = TTOutcome(desc:ct.desc, exception_msg:"", category: ct.category)
+    try:
+      var ctx = newContext(verifyMode=CVerifyPeer)
+      var client = newHttpClient(sslContext=ctx)
+      let a = $client.getContent(ct.url)
+    except:
+      result.exception_msg = getCurrentExceptionMsg()
+
+
+  suite "SSL certificate check - httpclient - threaded":
+    when defined(nimTestsEnableFlaky) or not defined(linux): # xxx pending bug #16338
+      # Spawn threads before the "test" blocks
+      var outcomes = newSeq[FlowVar[TTOutcome]](certificate_tests.len)
+      for i, ct in certificate_tests:
+        let t = spawn run_t_test(ct)
+        outcomes[i] = t
+
+      # create "test" blocks and handle thread outputs
+      for t in outcomes:
+        let outcome = ^t  # wait for a thread to terminate
+        test outcome.desc:
+          evaluate(outcome.exception_msg, outcome.category, outcome.desc)
+    else:
+      echo "skipped test"
+
+  # net tests
+
+
+  type NetSocketTest = tuple[hostname: string, port: Port, category:Category, desc: string]
+  # badssl certs sometimes expire, set to false when that happens
+  when true:
+    const net_tests:array[0..3, NetSocketTest] = [
+      ("imap.gmail.com", 993.Port, good, "IMAP"),
+      ("wrong.host.badssl.com", 443.Port, bad, "wrong.host"),
+      ("captive-portal.badssl.com", 443.Port, bad, "captive-portal"),
+      ("expired.badssl.com", 443.Port, bad, "expired"),
+    ]
+  else:
+    const net_tests: array[0..0, NetSocketTest] = [
+      ("imap.gmail.com", 993.Port, good, "IMAP")
+    ]
+  # TODO: ("null.badssl.com", 443.Port, bad_broken, "null"),
+
+
+  suite "SSL certificate check - sockets":
+
+    for ct in net_tests:
+
+      test ct.desc:
+
+        var sock = newSocket()
+        var ctx = newContext()
+        ctx.wrapSocket(sock)
+        let exception_msg =
+          try:
+            sock.connect(ct.hostname, ct.port)
+            ""
+          except:
+            getCurrentExceptionMsg()
+
+        evaluate(exception_msg, ct.category, ct.desc)
diff --git a/tests/untestable/tpostgres.nim b/tests/untestable/tpostgres.nim
index d3397e53a..8b1378917 100644
--- a/tests/untestable/tpostgres.nim
+++ b/tests/untestable/tpostgres.nim
@@ -1,328 +1 @@
-import db_postgres, strutils
-
-
-let db = open("localhost", "dom", "", "test")
-db.exec(sql"DROP TABLE IF EXISTS myTable")
-db.exec(sql("""CREATE TABLE myTable (
-                  id integer PRIMARY KEY,
-                  name varchar(50) not null)"""))
-let name = "Dom"
-db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
-        name)
-doAssert db.getValue(sql"SELECT name FROM myTable") == name
-# Check issue #3513
-doAssert db.getValue(sql"SELECT name FROM myTable") == name
-
-
-# issue #3560
-proc addToDb(conn: DbConn, fileId: int, fileName: string): int64 =
-  result = conn.insertId(sql("INSERT into files (id, filename) VALUES (?, ?)"), fileId, fileName)
-
-db.exec(sql"DROP TABLE IF EXISTS files")
-db.exec(sql"DROP TABLE IF EXISTS fileobjects")
-db.exec(sql("""CREATE TABLE FILEOBJECTS(
-               ID             SERIAL PRIMARY KEY,
-               FILE_SIZE      INT,
-               MD5            CHAR(32)  NOT NULL UNIQUE
-            );"""))
-
-db.exec(sql("""CREATE TABLE FILES(
-               ID                  SERIAL PRIMARY KEY,
-               OBJECT_ID           INT,
-               FILENAME            TEXT NOT NULL,
-               URI                 TEXT,
-               SCHEME              CHAR(10),
-               PUBLIC              BOOLEAN DEFAULT FALSE,
-               CONSTRAINT fk1_fileobjs FOREIGN KEY (object_id)
-               REFERENCES fileobjects (id) MATCH SIMPLE
-               ON DELETE CASCADE
-            );"""))
-
-let f1 = db.addToDb(1, "hello.tmp")
-doAssert f1 == 1
-let f2 = db.addToDb(2, "hello2.tmp")
-doAssert f2 == 2
-
-# PreparedStmt vs. normal query
-try:
-  echo db.getValue(sql("select * from files where id = $1"), 1)
-  doAssert false, "Exception expected"
-except DbError:
-  let msg = getCurrentExceptionMsg().normalize
-  doAssert "expects" in msg
-  doAssert "?" in msg
-  doAssert "parameter substitution" in msg
-
-doAssert db.getValue(sql("select filename from files where id = ?"), 1) == "hello.tmp"
-
-var first = prepare(db, "one", sql"select filename from files where id = $1", 1)
-doAssert db.getValue(first, 1) == "hello.tmp"
-
-try:
-  var second = prepare(db, "two", sql"select filename from files where id = ?", 1)
-  doAssert false, "Exception expected"
-except:
-  let msg = getCurrentExceptionMsg().normalize
-  doAssert "expects" in msg
-  doAssert "$1" in msg
-  doAssert "parameter substitution" in msg
-
-# issue #3569
-db.exec(SqlQuery("DROP TABLE IF EXISTS tags"))
-db.exec(SqlQuery("CREATE TABLE tags(id serial UNIQUE, name varchar(255))"))
-
-for i in 1..10:
-  var name = "t" & $i
-  echo(name)
-  discard db.getRow(
-    SqlQuery("INSERT INTO tags(name) VALUES(\'$1\') RETURNING id" % [name]))
-    
-# get column details
-db.exec(SqlQuery("DROP TABLE IF EXISTS dbtypes;"))
-db.exec(SqlQuery("DROP TYPE IF EXISTS custom_enum;"))
-db.exec(SqlQuery("CREATE TYPE custom_enum AS ENUM ('1', '2', '3');"))
-db.exec(SqlQuery("DROP TYPE IF EXISTS custom_composite;"))
-db.exec(SqlQuery("CREATE TYPE custom_composite AS (r double precision, i double precision);"))
-db.exec(SqlQuery("""CREATE TABLE dbtypes(
-                    id serial UNIQUE,
-                    bytea_col bytea,
-                    smallint_col smallint,
-                    integer_col integer,
-                    bigint_col bigint,
-                    decimal_col decimal,
-                    numeric_col numeric,
-                    real_col real,
-                    double_precision_col double precision,
-                    smallserial_col smallserial,
-                    serial_col serial,
-                    bigserial_col bigserial,
-                    money_col money,
-                    varchar_col varchar(10),
-                    character_col character(1),
-                    text_col text,
-                    timestamp_col timestamp,
-                    date_col date,
-                    time_col time,
-                    interval_col interval,
-                    bool_col boolean,
-                    custom_enum_col custom_enum,
-                    point_col point,
-                    line_col line,
-                    lseg_col lseg,
-                    box_col box,
-                    path_col path,
-                    polygon_col polygon,
-                    circle_col circle,
-                    cidr_col cidr,
-                    inet_col inet,
-                    macaddr_col macaddr,
-                    bit_col bit,
-                    varbit_col bit(3),
-                    tsvector_col tsvector,
-                    tsquery_col tsquery,
-                    uuid_col uuid,
-                    xml_col xml,
-                    json_col json,
-                    array_col integer[],
-                    custom_composite_col custom_composite,
-                    range_col int4range
-                    );"""))
-db.exec(SqlQuery("INSERT INTO dbtypes (id) VALUES(0);"))
-
-var dbCols : DbColumns = @[]
-for row in db.instantRows(dbCols, sql"SELECT * FROM dbtypes"):
-  doAssert len(dbCols) == 42
-
-doAssert dbCols[0].name == "id"
-doAssert dbCols[0].typ.kind == DbTypeKind.dbInt
-doAssert dbCols[0].typ.name == "int4"
-doAssert dbCols[0].typ.size == 4
-
-doAssert dbCols[1].name == "bytea_col"
-doAssert dbCols[1].typ.kind == DbTypeKind.dbBlob
-doAssert dbCols[1].typ.name == "bytea"
-
-doAssert dbCols[2].name == "smallint_col"
-doAssert dbCols[2].typ.kind == DbTypeKind.dbInt
-doAssert dbCols[2].typ.name == "int2"
-doAssert dbCols[2].typ.size == 2
-
-doAssert dbCols[3].name == "integer_col"
-doAssert dbCols[3].typ.kind == DbTypeKind.dbInt
-doAssert dbCols[3].typ.name == "int4"
-doAssert dbCols[3].typ.size == 4
-
-doAssert dbCols[4].name == "bigint_col"
-doAssert dbCols[4].typ.kind == DbTypeKind.dbInt
-doAssert dbCols[4].typ.name == "int8"
-doAssert dbCols[4].typ.size == 8
-
-doAssert dbCols[5].name == "decimal_col"
-doAssert dbCols[5].typ.kind == DbTypeKind.dbDecimal
-doAssert dbCols[5].typ.name == "numeric"
-
-doAssert dbCols[6].name == "numeric_col"
-doAssert dbCols[6].typ.kind == DbTypeKind.dbDecimal
-doAssert dbCols[6].typ.name == "numeric"
-
-doAssert dbCols[7].name == "real_col"
-doAssert dbCols[7].typ.kind == DbTypeKind.dbFloat
-doAssert dbCols[7].typ.name == "float4"
-
-doAssert dbCols[8].name == "double_precision_col"
-doAssert dbCols[8].typ.kind == DbTypeKind.dbFloat
-doAssert dbCols[8].typ.name == "float8"
-
-doAssert dbCols[9].name == "smallserial_col"
-doAssert dbCols[9].typ.kind == DbTypeKind.dbInt
-doAssert dbCols[9].typ.name == "int2"
-
-doAssert dbCols[10].name == "serial_col"
-doAssert dbCols[10].typ.kind == DbTypeKind.dbInt
-doAssert dbCols[10].typ.name == "int4"
-
-doAssert dbCols[11].name == "bigserial_col"
-doAssert dbCols[11].typ.kind == DbTypeKind.dbInt
-doAssert dbCols[11].typ.name == "int8"
-
-doAssert dbCols[12].name == "money_col"
-doAssert dbCols[12].typ.kind == DbTypeKind.dbDecimal
-doAssert dbCols[12].typ.name == "money"    
-    
-doAssert dbCols[13].name == "varchar_col"
-doAssert dbCols[13].typ.kind == DbTypeKind.dbVarchar
-doAssert dbCols[13].typ.name == "varchar"
-
-doAssert dbCols[14].name == "character_col"
-doAssert dbCols[14].typ.kind == DbTypeKind.dbFixedChar
-doAssert dbCols[14].typ.name == "bpchar"
-
-doAssert dbCols[15].name == "text_col"
-doAssert dbCols[15].typ.kind == DbTypeKind.dbVarchar
-doAssert dbCols[15].typ.name == "text"
-
-doAssert dbCols[16].name == "timestamp_col"
-doAssert dbCols[16].typ.kind == DbTypeKind.dbTimestamp
-doAssert dbCols[16].typ.name == "timestamp"
-
-doAssert dbCols[17].name == "date_col"
-doAssert dbCols[17].typ.kind == DbTypeKind.dbDate
-doAssert dbCols[17].typ.name == "date"
-
-doAssert dbCols[18].name == "time_col"
-doAssert dbCols[18].typ.kind == DbTypeKind.dbTime
-doAssert dbCols[18].typ.name == "time"
-
-doAssert dbCols[19].name == "interval_col"
-doAssert dbCols[19].typ.kind == DbTypeKind.dbTimeInterval
-doAssert dbCols[19].typ.name == "interval"
-
-doAssert dbCols[20].name == "bool_col"
-doAssert dbCols[20].typ.kind == DbTypeKind.dbBool
-doAssert dbCols[20].typ.name == "bool"
-
-doAssert dbCols[21].name == "custom_enum_col"
-doAssert dbCols[21].typ.kind == DbTypeKind.dbUnknown
-doAssert parseInt(dbCols[21].typ.name) > 0
-    
-doAssert dbCols[22].name == "point_col"
-doAssert dbCols[22].typ.kind == DbTypeKind.dbPoint
-doAssert dbCols[22].typ.name == "point"    
-
-doAssert dbCols[23].name == "line_col"
-doAssert dbCols[23].typ.kind == DbTypeKind.dbLine
-doAssert dbCols[23].typ.name == "line"    
-
-doAssert dbCols[24].name == "lseg_col"
-doAssert dbCols[24].typ.kind == DbTypeKind.dbLseg
-doAssert dbCols[24].typ.name == "lseg"    
-
-doAssert dbCols[25].name == "box_col"
-doAssert dbCols[25].typ.kind == DbTypeKind.dbBox
-doAssert dbCols[25].typ.name == "box"    
-
-doAssert dbCols[26].name == "path_col"
-doAssert dbCols[26].typ.kind == DbTypeKind.dbPath
-doAssert dbCols[26].typ.name == "path"    
-
-doAssert dbCols[27].name == "polygon_col"
-doAssert dbCols[27].typ.kind == DbTypeKind.dbPolygon
-doAssert dbCols[27].typ.name == "polygon"
-
-doAssert dbCols[28].name == "circle_col"
-doAssert dbCols[28].typ.kind == DbTypeKind.dbCircle
-doAssert dbCols[28].typ.name == "circle"
-
-doAssert dbCols[29].name == "cidr_col"
-doAssert dbCols[29].typ.kind == DbTypeKind.dbInet
-doAssert dbCols[29].typ.name == "cidr"
-
-doAssert dbCols[30].name == "inet_col"
-doAssert dbCols[30].typ.kind == DbTypeKind.dbInet
-doAssert dbCols[30].typ.name == "inet"
-
-doAssert dbCols[31].name == "macaddr_col"
-doAssert dbCols[31].typ.kind == DbTypeKind.dbMacAddress
-doAssert dbCols[31].typ.name == "macaddr"
-
-doAssert dbCols[32].name == "bit_col"
-doAssert dbCols[32].typ.kind == DbTypeKind.dbBit
-doAssert dbCols[32].typ.name == "bit"
-
-doAssert dbCols[33].name == "varbit_col"
-doAssert dbCols[33].typ.kind == DbTypeKind.dbBit
-doAssert dbCols[33].typ.name == "bit"
-
-doAssert dbCols[34].name == "tsvector_col"
-doAssert dbCols[34].typ.kind == DbTypeKind.dbVarchar
-doAssert dbCols[34].typ.name == "tsvector"
-
-doAssert dbCols[35].name == "tsquery_col"
-doAssert dbCols[35].typ.kind == DbTypeKind.dbVarchar
-doAssert dbCols[35].typ.name == "tsquery"
-
-doAssert dbCols[36].name == "uuid_col"
-doAssert dbCols[36].typ.kind == DbTypeKind.dbVarchar
-doAssert dbCols[36].typ.name == "uuid"
-
-doAssert dbCols[37].name == "xml_col"
-doAssert dbCols[37].typ.kind == DbTypeKind.dbXml
-doAssert dbCols[37].typ.name == "xml"
-
-doAssert dbCols[38].name == "json_col"
-doAssert dbCols[38].typ.kind == DbTypeKind.dbJson
-doAssert dbCols[38].typ.name == "json"
-
-doAssert dbCols[39].name == "array_col"
-doAssert dbCols[39].typ.kind == DbTypeKind.dbArray
-doAssert dbCols[39].typ.name == "int4[]"
-
-doAssert dbCols[40].name == "custom_composite_col"
-doAssert dbCols[40].typ.kind == DbTypeKind.dbUnknown
-doAssert parseInt(dbCols[40].typ.name) > 0
-
-doAssert dbCols[41].name == "range_col"
-doAssert dbCols[41].typ.kind == DbTypeKind.dbComposite
-doAssert dbCols[41].typ.name == "int4range"
-
-# issue 6571
-db.exec(sql"DROP TABLE IF EXISTS DICTIONARY")
-db.exec(sql("""CREATE TABLE DICTIONARY(
-               id             SERIAL PRIMARY KEY,
-               entry      VARCHAR(1000) NOT NULL,
-               definition VARCHAR(4000) NOT NULL
-            );"""))
-var entry = "あっそ"
-var definition = "(int) (See ああそうそう) oh, really (uninterested)/oh yeah?/hmmmmm"
-discard db.getRow(
-  SqlQuery("INSERT INTO DICTIONARY(entry, definition) VALUES(\'$1\', \'$2\') RETURNING id" % [entry, definition]))
-doAssert db.getValue(sql"SELECT definition FROM DICTIONARY WHERE entry = ?", entry) == definition
-entry = "Format string entry"
-definition = "Format string definition"
-db.exec(sql"INSERT INTO DICTIONARY(entry, definition) VALUES (?, ?)", entry, definition)
-doAssert db.getValue(sql"SELECT definition FROM DICTIONARY WHERE entry = ?", entry) == definition
-
-echo("All tests succeeded!")
-
-db.close()
 
diff --git a/tests/untestable/tssl.nim b/tests/untestable/tssl.nim
index 664ad805c..fca6385f8 100644
--- a/tests/untestable/tssl.nim
+++ b/tests/untestable/tssl.nim
@@ -20,7 +20,7 @@ from strutils import contains, toHex
 
 from openssl import getOpenSSLVersion
 
-when isMainModule:
+when true:
   echo "version: 0x" & $getOpenSSLVersion().toHex()
 
   let client = newHttpClient()
diff --git a/tests/usingstmt/tthis.nim b/tests/usingstmt/tthis.nim
deleted file mode 100644
index 83d75d08c..000000000
--- a/tests/usingstmt/tthis.nim
+++ /dev/null
@@ -1,15 +0,0 @@
-
-# bug #4177
-
-type
-  Parent = object of RootObj
-    parentField: int
-  Child = object of Parent
-    childField: int
-
-{.this: self.}
-proc sumFields(self: Child): int =
-  result = parentField + childField # Error: undeclared identifier: 'parentField'
-
-proc sumFieldsWorks(self: Child): int =
-  result = self.parentField + childField
diff --git a/tests/usingstmt/tusingstatement.nim b/tests/usingstmt/tusingstatement.nim
deleted file mode 100644
index 8585bcc9e..000000000
--- a/tests/usingstmt/tusingstatement.nim
+++ /dev/null
@@ -1,87 +0,0 @@
-discard """
-  file: "tusingstatement.nim"
-  output: "Using test.Closing test."
-"""
-
-import
-  macros
-
-# This macro mimics the using statement from C#
-#
-# It's kept only as a test for the macro system
-# Nim's destructors offer a mechanism for automatic
-# disposal of resources.
-#
-macro autoClose(args: varargs[untyped]): untyped =
-  let e = callsite()
-  if e.len != 3:
-    error "Using statement: unexpected number of arguments. Got " &
-      $e.len & ", expected: 1 or more variable assignments and a block"
-
-  var args = e
-  var body = e[2]
-
-  var
-    variables : seq[NimNode]
-    closingCalls : seq[NimNode]
-
-  newSeq(variables, 0)
-  newSeq(closingCalls, 0)
-
-  for i in countup(1, args.len-2):
-    if args[i].kind == nnkExprEqExpr:
-      var varName = args[i][0]
-      var varValue = args[i][1]
-
-      var varAssignment = newNimNode(nnkIdentDefs)
-      varAssignment.add(varName)
-      varAssignment.add(newNimNode(nnkEmpty)) # empty means no type
-      varAssignment.add(varValue)
-      variables.add(varAssignment)
-
-      closingCalls.add(newCall(!"close", varName))
-    else:
-      error "Using statement: Unexpected expression. Got " &
-        $args[i].kind & " instead of assignment."
-
-  var varSection = newNimNode(nnkVarSection)
-  varSection.add(variables)
-
-  var finallyBlock = newNimNode(nnkStmtList)
-  finallyBlock.add(closingCalls)
-
-  # XXX: Use a template here once getAst is working properly
-  var targetAst = parseStmt"""block:
-    var
-      x = foo()
-      y = bar()
-
-    try:
-      body()
-
-    finally:
-      close x
-      close y
-  """
-
-  targetAst[0][1][0] = varSection
-  targetAst[0][1][1][0] = body
-  targetAst[0][1][1][1][0] = finallyBlock
-
-  result = targetAst
-
-type
-  TResource* = object
-    field*: string
-
-proc openResource(param: string): TResource =
-  result.field = param
-
-proc close(r: var TResource) =
-  write(stdout, "Closing " & r.field & ".")
-
-proc use(r: var TResource) =
-  write(stdout, "Using " & r.field & ".")
-
-autoClose(r = openResource("test")):
-  use r
diff --git a/tests/usingstmt/tusingstmt.nim b/tests/usingstmt/tusingstmt.nim
new file mode 100644
index 000000000..11803878e
--- /dev/null
+++ b/tests/usingstmt/tusingstmt.nim
@@ -0,0 +1,16 @@
+type
+  Foo = object
+
+using
+  c: Foo
+  x, y: int
+
+proc usesSig(c) = discard
+
+proc foobar(c, y) = discard
+
+usesSig(Foo())
+foobar(Foo(), 123)
+doAssert not compiles(usesSig(123))
+doAssert not compiles(foobar(Foo(), Foo()))
+doAssert not compiles(foobar(123, 123))
diff --git a/tests/valgrind/tbasic_valgrind.nim b/tests/valgrind/tbasic_valgrind.nim
new file mode 100644
index 000000000..455b1ff17
--- /dev/null
+++ b/tests/valgrind/tbasic_valgrind.nim
@@ -0,0 +1,6 @@
+discard """
+  valgrind: true
+  cmd: "nim c --gc:destructors $file"
+"""
+
+echo "hello world"
diff --git a/tests/valgrind/tleak_arc.nim b/tests/valgrind/tleak_arc.nim
new file mode 100644
index 000000000..c47ee137a
--- /dev/null
+++ b/tests/valgrind/tleak_arc.nim
@@ -0,0 +1,14 @@
+discard """
+valgrind: true
+cmd: "nim $target --gc:arc -d:useMalloc $options $file"
+exitcode: 1
+outputsub: "   definitely lost: 7 bytes in 2 blocks"
+disabled: "freebsd"
+disabled: "osx"
+disabled: "openbsd"
+disabled: "windows"
+disabled: "32bit"
+"""
+
+discard alloc(3)
+discard alloc(4)
diff --git a/tests/varres/tnewseq_on_result_vart.nim b/tests/varres/tnewseq_on_result_vart.nim
index 18935a1d1..4a700468f 100644
--- a/tests/varres/tnewseq_on_result_vart.nim
+++ b/tests/varres/tnewseq_on_result_vart.nim
@@ -1,7 +1,7 @@
 
 discard """
-  line: 9
   errormsg: "address of 'result' may not escape its stack frame"
+  line: 9
 """
 # bug #5113
 
diff --git a/tests/varres/tprevent_forloopvar_mutations.nim b/tests/varres/tprevent_forloopvar_mutations.nim
new file mode 100644
index 000000000..c9aeb94d8
--- /dev/null
+++ b/tests/varres/tprevent_forloopvar_mutations.nim
@@ -0,0 +1,16 @@
+discard """
+  errormsg: "type mismatch: got <int>"
+  nimout: '''tprevent_forloopvar_mutations.nim(16, 3) Error: type mismatch: got <int>
+but expected one of:
+proc inc[T, V: Ordinal](x: var T; y: V = 1)
+  first type mismatch at position: 1
+  required type for x: var T: Ordinal
+  but expression 'i' is immutable, not 'var'
+
+expression: inc i
+'''
+"""
+
+for i in 0..10:
+  echo i
+  inc i
diff --git a/tests/varres/tvarres0.nim b/tests/varres/tvarres0.nim
index fd10a73bd..94bdb4a06 100644
--- a/tests/varres/tvarres0.nim
+++ b/tests/varres/tvarres0.nim
@@ -4,6 +4,7 @@ discard """
 123
 1234
 12345
+123456
 '''
 """
 
@@ -28,3 +29,113 @@ getF().a = 1234
 echo getF().a
 getF() = Foo(a: 12345)
 echo getF().a
+(addr getF())[] = Foo(a: 123456)
+echo getF().a
+
+
+block: # #13848
+  template fun() =
+    block:
+      var m = 1
+
+      proc identity(o: var int): var int =
+        result = o
+        result += 5
+
+      identity(m) += 3
+      doAssert m == 5+4
+
+    block:
+      var m = 10
+      proc identity2(o: var int): var int =
+        result = m
+        result += 100
+
+      var ignored = 27
+      identity2(ignored) += 7
+      doAssert m == 10 + 100 + 7
+
+    block:
+      iterator test3(o: var int): var int = yield o
+      var m = 1
+      for m2 in test3(m): m2+=3
+      doAssert m == 4
+
+  static: fun()
+  fun()
+
+  template fun2() =
+    block:
+      var m = 1
+      var m2 = 1
+      iterator test3(o: var int): (var int, var int) =
+        yield (o, m2)
+
+      for ti in test3(m):
+        ti[0]+=3
+        ti[1]+=4
+
+      doAssert (m, m2) == (4, 5)
+  fun2()
+  # static: fun2() # BUG: Error: attempt to access a nil address kind: rkInt
+
+  template fun3() =
+    block:
+      proc test4[T1](o: var T1): var int = o[1]
+      block:
+        var m = @[1,2]
+        test4(m) += 10
+        doAssert m[1] == 2+10
+      block:
+        var m = [1,2]
+        test4(m) += 10
+        doAssert m[1] == 2+10
+      block:
+        var m = (1, 2)
+        test4(m) += 10
+        doAssert m[1] == 2+10
+
+      proc test5[T1](o: var T1): var int = o.x
+      block:
+        type Foo = object
+          x: int
+        var m = Foo(x: 2)
+        test5(m) += 10
+        doAssert m.x == 2+10
+      block:
+        type Foo = ref object
+          x: int
+        var m = Foo(x: 2)
+        test5(m) += 10
+        doAssert m.x == 2+10
+
+      proc test6[T1](o: T1): var int = o.x
+      block:
+        type Foo = ref object
+          x: int
+        var m = Foo(x: 2)
+        test6(m) += 10
+        doAssert m.x == 2+10
+
+  fun3()
+  static: fun3()
+
+  when false:
+    # BUG:
+    # c: SIGSEGV
+    # cpp: error: call to implicitly-deleted default constructor of 'tyTuple__ILZebuYefUeQLAzY85QkHA'
+    proc test7[T](o: var T): (var int,) =
+      (o[1], )
+    var m = @[1,2]
+    test7(m)[0] += 10
+
+block:
+  # example from #13848
+  type
+    MyType[T] = object
+      a,b: T
+    MyTypeAlias = MyType[float32]
+
+  var m: MyTypeAlias
+  proc identity(o: var MyTypeAlias): var MyTypeAlias = o
+  discard identity(m)
diff --git a/tests/varres/tvarres1.nim b/tests/varres/tvarres1.nim
index 5a5247142..e58d7f083 100644
--- a/tests/varres/tvarres1.nim
+++ b/tests/varres/tvarres1.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "'bla' escapes its stack frame; context: 'bla'"
   file: "tvarres1.nim"
   line: 12
-  errormsg: "'bla' escapes its stack frame; context: 'bla'"
 """
 
 var
@@ -14,4 +14,3 @@ proc p(): var int =
 p() = 45
 
 echo g
-
diff --git a/tests/varres/tvarres2.nim b/tests/varres/tvarres2.nim
index 53a57d882..4ec0bb05b 100644
--- a/tests/varres/tvarres2.nim
+++ b/tests/varres/tvarres2.nim
@@ -1,7 +1,7 @@
 discard """
+  errormsg: "expression has no address"
   file: "tvarres2.nim"
   line: 11
-  errormsg: "expression has no address"
 """
 
 var
@@ -13,4 +13,3 @@ proc p(): var int =
 p() = 45
 
 echo g
-
diff --git a/tests/varres/tvarres_via_forwarding.nim b/tests/varres/tvarres_via_forwarding.nim
index 8fd3dfcfd..fb7201ad2 100644
--- a/tests/varres/tvarres_via_forwarding.nim
+++ b/tests/varres/tvarres_via_forwarding.nim
@@ -1,6 +1,6 @@
 discard """
-  line: 10
   errormsg: "'y' escapes its stack frame; context: 'forward(y)'"
+  line: 10
 """
 
 proc forward(x: var int): var int = result = x
diff --git a/tests/varres/tvartup.nim b/tests/varres/tvartup.nim
index 1957a3e35..a8f15b232 100644
--- a/tests/varres/tvartup.nim
+++ b/tests/varres/tvartup.nim
@@ -1,5 +1,4 @@
 discard """
-  file: "tvartup.nim"
   output: "2 3"
 """
 # Test the new tuple unpacking
@@ -9,5 +8,3 @@ proc divmod(a, b: int): tuple[di, mo: int] =
 
 var (x, y) = divmod(15, 6)
 echo x, " ", y
-
-#OUT 2 3
diff --git a/tests/varres/twrong_parameter.nim b/tests/varres/twrong_parameter.nim
index 8a363dd19..58d01fd7e 100644
--- a/tests/varres/twrong_parameter.nim
+++ b/tests/varres/twrong_parameter.nim
@@ -1,6 +1,6 @@
 discard """
-  line: 10
   errormsg: "'x' is not the first parameter; context: 'x.field[0]'"
+  line: 10
 """
 
 type
diff --git a/tests/varstmt/tvardecl.nim b/tests/varstmt/tvardecl.nim
index a6b508295..d5ccfafb7 100644
--- a/tests/varstmt/tvardecl.nim
+++ b/tests/varstmt/tvardecl.nim
@@ -1,8 +1,8 @@
 discard """
-  file: "tvardecl.nim"
   output: "44"
 """
 # Test the new variable declaration syntax
+import std/sequtils
 
 var
   x = 0
@@ -10,6 +10,10 @@ var
   a, b: int = 4
 
 write(stdout, a)
-write(stdout, b) #OUT 44
+writeLine(stdout, b) #OUT 44
 
+proc p() = # bug #18104
+  var x, y = newSeqWith(10, newString(3))
+  discard (x, y)
 
+p()
diff --git a/tests/views/t19986.nim b/tests/views/t19986.nim
new file mode 100644
index 000000000..85a7cf97d
--- /dev/null
+++ b/tests/views/t19986.nim
@@ -0,0 +1,42 @@
+discard """
+  cmd: '''nim check --hints:off $file'''
+  action: reject
+nimout: '''
+t19986.nim(19, 7) Error: 'foo' borrows from the immutable location 'a' and attempts to mutate it
+t19986.nim(28, 7) Error: 'foo' borrows from the immutable location 'a' and attempts to mutate it
+t19986.nim(37, 7) Error: 'foo' borrows from the immutable location 'a' and attempts to mutate it
+'''
+"""
+
+{.experimental: "views".}
+
+type
+  Object = object
+    id: int
+
+proc foo() =
+  let a = Object(id: 3)
+  var foo: var Object = a
+
+  foo.id = 777
+  echo a
+
+foo()
+
+proc bar() =
+  let a = "123"
+  var foo: var string = a
+
+  foo[0] = '7'
+  echo a
+
+bar()
+
+proc main() =
+  let a = 3
+  var foo: var int = a
+
+  foo = 777
+  echo a
+
+main()
diff --git a/tests/views/tcan_compile_nim.nim b/tests/views/tcan_compile_nim.nim
new file mode 100644
index 000000000..e990606cd
--- /dev/null
+++ b/tests/views/tcan_compile_nim.nim
@@ -0,0 +1,4 @@
+discard """
+  cmd: "nim check --hints:on --experimental:strictFuncs --experimental:views compiler/nim.nim"
+  action: "compile"
+"""
diff --git a/tests/views/tcannot_borrow.nim b/tests/views/tcannot_borrow.nim
new file mode 100644
index 000000000..0b8793159
--- /dev/null
+++ b/tests/views/tcannot_borrow.nim
@@ -0,0 +1,22 @@
+discard """
+  errormsg: "cannot borrow"
+  nimout: '''tcannot_borrow.nim(20, 7) Error: cannot borrow meh; what it borrows from is potentially mutated
+tcannot_borrow.nim(21, 3) the mutation is here'''
+"""
+
+
+{.experimental: "views".}
+
+type
+  Foo = object
+    field: string
+
+proc valid(s: var seq[Foo]) =
+  let v: lent Foo = s[0]  # begin of borrow
+  echo v.field            # end of borrow
+  s.setLen 0  # valid because 'v' isn't used afterwards
+
+proc dangerous(s: var seq[Foo]) =
+  let meh: lent Foo = s[0]
+  s.setLen 0
+  echo meh.field
diff --git a/tests/views/tconst_views.nim b/tests/views/tconst_views.nim
new file mode 100644
index 000000000..a85b03864
--- /dev/null
+++ b/tests/views/tconst_views.nim
@@ -0,0 +1,37 @@
+discard """
+  cmd: "nim c --experimental:views $file"
+  output: '''(data: [1, 2, 3], other: 4)
+[1, 20, 3]'''
+"""
+
+type
+  Foo = object
+    data: openArray[int]
+    other: int
+
+const
+  c = Foo(data: [1, 2, 3], other: 4)
+
+  c2 = Foo(data: [1, 20, 3], other: 4)
+
+proc `$`(x: openArray[int]): string =
+  result = "["
+  for i in x:
+    if result.len > 1: result.add ", "
+    result.add $i
+  result.add "]"
+
+echo c
+echo c2.data
+
+
+type MyObj = object
+  data: openarray[char]
+
+const
+  val1 = Foo(data: toOpenArray([1, 2, 3], 1, 1))
+  val2 = Foo(data: toOpenArray([1, 2, 3], 0, 2))
+  val3 = MyObj(data: "Hello".toOpenArray(0, 2))
+assert val1.data == [2]
+assert val2.data == [1, 2, 3]
+assert val3.data == "Hel"
diff --git a/tests/views/tdont_mutate.nim b/tests/views/tdont_mutate.nim
new file mode 100644
index 000000000..eb5a82cbf
--- /dev/null
+++ b/tests/views/tdont_mutate.nim
@@ -0,0 +1,58 @@
+discard """
+  cmd: "nim check --hints:off $file"
+"""
+
+import tables
+
+{.experimental: "views".}
+
+const
+  Whitespace = {' ', '\t', '\n', '\r'}
+
+proc split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): Table[int, openArray[char]] #[tt.Error
+^ 'result' borrows from the immutable location 's' and attempts to mutate it
+    ]# =
+  var last = 0
+  var splits = maxsplit
+  result = initTable[int, openArray[char]]()
+
+  while last <= len(s):
+    var first = last
+    while last < len(s) and s[last] notin seps:
+      inc(last)
+    if splits == 0: last = len(s)
+    result[first] = toOpenArray(s, first, last-1)
+
+    result[first][0] = 'c'
+
+    if splits == 0: break
+    dec(splits)
+    inc(last)
+
+proc `$`(x: openArray[char]): string =
+  result = newString(x.len)
+  for i in 0..<x.len: result[i] = x[i]
+
+proc otherTest(x: int) =
+  var y: var int = x #[tt.Error
+      ^ 'y' borrows from the immutable location 'x' and attempts to mutate it
+  ]#
+  y = 3
+
+proc main() =
+  let words = split("asdf 231")
+  for i, x in words:
+    echo i, ": ", x
+
+main()
+
+# This has to continue to work:
+
+type
+  PNode = ref object
+  TSrcGen = object
+    comStack: seq[PNode]
+
+proc pushCom(g: var TSrcGen, n: PNode) =
+  setLen(g.comStack, g.comStack.len + 1)
+  g.comStack[^1] = n
diff --git a/tests/views/tmust_borrow_from_first_parameter.nim b/tests/views/tmust_borrow_from_first_parameter.nim
new file mode 100644
index 000000000..b2decfb38
--- /dev/null
+++ b/tests/views/tmust_borrow_from_first_parameter.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "'result' must borrow from the first parameter"
+  line: 9
+"""
+
+{.experimental: "views".}
+
+proc p(a, b: openArray[char]): openArray[char] =
+  result = b
diff --git a/tests/views/tsplit_into_openarray.nim b/tests/views/tsplit_into_openarray.nim
new file mode 100644
index 000000000..3ea290d89
--- /dev/null
+++ b/tests/views/tsplit_into_openarray.nim
@@ -0,0 +1,37 @@
+discard """
+  output: '''asdf
+231
+'''
+  cmd: "nim c --gc:arc -d:useMalloc -g $file"
+  valgrind: true
+"""
+
+{.experimental: "views".}
+
+const
+  Whitespace = {' ', '\t', '\n', '\r'}
+
+iterator split*(s: string, seps: set[char] = Whitespace,
+                maxsplit: int = -1): openArray[char] =
+  var last = 0
+  var splits = maxsplit
+
+  while last <= len(s):
+    var first = last
+    while last < len(s) and s[last] notin seps:
+      inc(last)
+    if splits == 0: last = len(s)
+    yield toOpenArray(s, first, last-1)
+    if splits == 0: break
+    dec(splits)
+    inc(last)
+
+proc `$`(x: openArray[char]): string =
+  result = newString(x.len)
+  for i in 0..<x.len: result[i] = x[i]
+
+proc main() =
+  for x in split("asdf 231"):
+    echo x
+
+main()
diff --git a/tests/views/tsplit_into_seq.nim b/tests/views/tsplit_into_seq.nim
new file mode 100644
index 000000000..a0861458b
--- /dev/null
+++ b/tests/views/tsplit_into_seq.nim
@@ -0,0 +1,41 @@
+discard """
+  output: '''asdf
+asdf
+231
+231
+'''
+  cmd: "nim c --gc:orc $file"
+"""
+
+{.experimental: "views".}
+
+const
+  Whitespace = {' ', '\t', '\n', '\r'}
+
+proc split*(s: string, seps: set[char] = Whitespace,
+                maxsplit: int = -1): seq[openArray[char]] =
+  var last = 0
+  var splits = maxsplit
+  result = @[]
+
+  while last <= len(s):
+    var first = last
+    while last < len(s) and s[last] notin seps:
+      inc(last)
+    if splits == 0: last = len(s)
+    result.add toOpenArray(s, first, last-1)
+    result.add toOpenArray(s, first, last-1)
+    if splits == 0: break
+    dec(splits)
+    inc(last)
+
+proc `$`(x: openArray[char]): string =
+  result = newString(x.len)
+  for i in 0..<x.len: result[i] = x[i]
+
+proc main() =
+  let words = split("asdf 231")
+  for x in words:
+    echo x
+
+main()
diff --git a/tests/views/tsplit_into_table.nim b/tests/views/tsplit_into_table.nim
new file mode 100644
index 000000000..49371b9a7
--- /dev/null
+++ b/tests/views/tsplit_into_table.nim
@@ -0,0 +1,40 @@
+discard """
+  output: '''5: 231
+0: asdf
+'''
+"""
+
+import tables
+
+{.experimental: "views".}
+
+const
+  Whitespace = {' ', '\t', '\n', '\r'}
+
+proc split*(s: string, seps: set[char] = Whitespace,
+                maxsplit: int = -1): Table[int, openArray[char]] =
+  var last = 0
+  var splits = maxsplit
+  result = initTable[int, openArray[char]]()
+
+  while last <= len(s):
+    var first = last
+    while last < len(s) and s[last] notin seps:
+      inc(last)
+    if splits == 0: last = len(s)
+    result[first] = toOpenArray(s, first, last-1)
+
+    if splits == 0: break
+    dec(splits)
+    inc(last)
+
+proc `$`(x: openArray[char]): string =
+  result = newString(x.len)
+  for i in 0..<x.len: result[i] = x[i]
+
+proc main() =
+  let words = split("asdf 231")
+  for i, x in words:
+    echo i, ": ", x
+
+main()
diff --git a/tests/views/tviews1.nim b/tests/views/tviews1.nim
new file mode 100644
index 000000000..9785d25e5
--- /dev/null
+++ b/tests/views/tviews1.nim
@@ -0,0 +1,136 @@
+discard """
+  output: '''11
+22
+33
+3
+2
+3
+3
+15
+(oa: [1, 3, 4])'''
+  targets: "c cpp"
+"""
+
+{.experimental: "views".}
+
+proc take(a: openArray[int]) =
+  echo a.len
+
+proc main(s: seq[int]) =
+  var x: openArray[int] = s
+  for i in 0 .. high(x):
+    echo x[i]
+  take(x)
+
+  take(x.toOpenArray(0, 1))
+  let y = x
+  take y
+  take x
+
+main(@[11, 22, 33])
+
+var x: int
+
+proc foo(x: var int): var int =
+  once: x = 42
+  return x
+
+var y: var int = foo(x)
+y = 15
+echo foo(x)
+# bug #16132
+
+# bug #18690
+
+type
+  F = object
+    oa: openArray[int]
+
+let s1 = @[1,3,4,5,6]
+var test = F(oa: toOpenArray(s1, 0, 2))
+echo test
+
+type
+  Foo = object
+    x: string
+    y: seq[int]
+    data: array[10000, byte]
+
+  View[T] = object
+    x: lent T
+
+proc mainB =
+  let f = Foo(y: @[1, 2, 3])
+  let foo = View[Foo](x: f)
+  assert foo.x.x == ""
+  assert foo.x.y == @[1, 2, 3]
+
+mainB()
+
+
+# bug #15897
+type Outer = ref object 
+  value: int
+type Inner = object
+  owner: var Outer
+  
+var o = Outer(value: 1234)
+var v = Inner(owner: o).owner.value
+doAssert v == 1234
+
+block: # bug #21674
+  type
+    Lent = object
+      data: lent int
+
+  proc foo(s: Lent) =
+    var m = 12
+    discard cast[lent int](m)
+
+  proc main =
+    var m1 = 123
+    var x = Lent(data: m1)
+    foo(x)
+
+  main()
+
+block: # bug #22117
+  proc main: int =
+    var a = 10
+    defer: discard a # extend a's lifetime
+
+    var aref: var int = a
+      #└──── 'aref' borrows from location 'a' which does not live long enough
+
+    result = aref
+
+  doAssert main() == 10
+
+type
+  Slice*[T] = object
+    first, last: int
+    p: ptr UncheckedArray[T]
+
+var i = 0
+
+converter autoToOpenArray*[T](s: Slice[T]): openArray[T] =
+  inc i
+  result = toOpenArray(s.p, s.first, s.last)
+
+proc acceptOpenArray(s: openArray[byte]) = discard
+
+proc bug22597 = # bug #22597
+  acceptOpenArray(Slice[byte]())
+  doAssert i == 1
+
+bug22597()
+
+block: # bug #20048
+  type
+    Test = object
+      tokens: openArray[string]
+
+  func init(Self: typedesc[Test], tokens: openArray[string]): Self = Self(tokens: tokens)
+
+  let data = Test.init(["123"])
+  doAssert @(data.tokens) == @["123"] 
diff --git a/tests/views/tviews2.nim b/tests/views/tviews2.nim
new file mode 100644
index 000000000..29aff76df
--- /dev/null
+++ b/tests/views/tviews2.nim
@@ -0,0 +1,90 @@
+discard """
+  targets: "c js"
+"""
+
+{.experimental: "views".}
+
+block:
+  type
+    Foo = object
+      id: openArray[char]
+
+  proc foo(): Foo =
+    var source = "1245"
+    result = Foo(id: source.toOpenArray(0, 1))
+
+  doAssert foo().id == @['1', '2']
+
+block: # bug #15778
+  type
+    Reader = object
+      data: openArray[char]
+      current: int
+
+  var count = 0
+
+  proc read(data: var Reader, length: int): openArray[char] =
+    inc count
+    let start = data.current
+    data.current.inc length
+    return data.data.toOpenArray(start, data.current-1)
+
+  var data = "hello there"
+  var reader = Reader(data: data.toOpenArray(0, data.len-1), current: 0)
+  doAssert @(reader.read(2)) == @['h', 'e']
+  doAssert @(reader.read(3)) == @['l', 'l', 'o']
+  doAssert count == 2
+
+block: # bug #16671
+  block:
+    type X = ref object of RootObj
+    type Y = ref object of X
+      field: openArray[int]
+
+    var s: seq[X]
+    proc f() =
+      s.add(Y(field: [1]))
+
+    f()
+
+  block:
+    type X = ref object of RootObj
+    type Y = ref object of X
+      field: openArray[int]
+
+    var s: seq[X]
+    proc f() =
+      s.add(Y(field: toOpenArray([1, 2, 3], 0, 1)))
+
+    f()
+
+block: # bug #15746
+  type
+    Reader = object
+      data: openArray[char]
+      current: int
+
+  proc initReader(data: openArray[char], offset = 0): Reader =
+    result = Reader(data: data, current: offset)
+
+  let s = "\x01\x00\x00\x00"
+  doAssert initReader(s).data[0].int == 1
+
+block:
+  proc foo(x: openArray[char]) =
+    discard x
+
+  foo("12254")
+  foo(@['a', 'b'])
+
+  var a1 = "12254"
+  foo(a1)
+
+  var a2 = @['a', 'b']
+  foo(a2)
+
+  var s = "138443"
+  var ooo: openArray[char] = s
+  var xxx: openArray[char] = ooo
+  foo(ooo)
+  foo(xxx)
diff --git a/tests/vm/meta.nim b/tests/vm/meta.nim
index 2aa01b5b3..32a441f27 100644
--- a/tests/vm/meta.nim
+++ b/tests/vm/meta.nim
@@ -39,7 +39,7 @@ proc newIdent*(node: NimNode): Ident =
       raise newException(ValueError, msg)
 
 proc render*(i: Ident): NimNode {.compileTime.} =
-  if i.name == nil:
+  if i.name == "":
     return newNimNode(nnkEmpty)
 
   if i.exported:
@@ -67,7 +67,7 @@ proc newBracket*(node: NimNode): Bracket =
       raise newException(ValueError, msg)
 
 # Field procs
-proc newField*(identifier: Ident, type_name: string, default: string = nil): Field =
+proc newField*(identifier: Ident, type_name: string, default: string = ""): Field =
   result.identifier = identifier
   result.type_name = type_name
   result.default = default
@@ -84,7 +84,7 @@ proc newField*(node: NimNode): Field =
         of nnkIdent:
           result.default = $(node[2])
         else:
-          result.default = nil
+          result.default = ""
     else:
       let msg = "newField cannot initialize from node kind: " & $(node.kind)
       raise newException(ValueError, msg)
@@ -102,7 +102,7 @@ proc newFieldSeq*(node: NimNode): FieldSeq =
         of nnkIdent:
           default = $(default_node)
         else:
-          default = nil
+          default = ""
       for i in 0..node.len - 3:
         let name = newIdent(node[i])
         result.add(newField(name, type_name, default))
@@ -115,8 +115,8 @@ proc newFieldSeq*(node: NimNode): FieldSeq =
 
 proc render*(f: Field): NimNode {.compileTime.} =
   let identifier = f.identifier.render()
-  let type_name = if f.type_name != nil: ident(f.type_name) else: newEmptyNode()
-  let default = if f.default != nil: ident(f.default) else: newEmptyNode()
+  let type_name = if f.type_name != "": ident(f.type_name) else: newEmptyNode()
+  let default = if f.default != "": ident(f.default) else: newEmptyNode()
   newIdentDefs(identifier, type_name, default)
 
 proc render*(fs: FieldSeq): NimNode {.compileTime.} =
@@ -127,7 +127,7 @@ proc render*(fs: FieldSeq): NimNode {.compileTime.} =
 # TypeDef procs
 proc newTypeDef*(identifier: Ident, is_ref = false,
                 object_type = "object",
-                base_type: string = nil): TypeDef {.compileTime.} =
+                base_type: string = ""): TypeDef {.compileTime.} =
   result.identifier = identifier
   result.fields = @[]
   result.is_ref = is_ref
@@ -165,7 +165,7 @@ proc render*(typedef: TypeDef): NimNode {.compileTime.} =
   result.add(newEmptyNode())
   let object_node = newNimNode(nnkObjectTy)
   object_node.add(newEmptyNode())
-  if typedef.base_type == nil:
+  if typedef.base_type == "":
     object_node.add(newEmptyNode())
   else:
     var base_type = newNimNode(nnkOfInherit)
@@ -195,8 +195,8 @@ proc render*(typeseq: TypeDefSeq): NimNode {.compileTime.} =
   for typedef in typeseq:
     result.add(typedef.render())
 
-proc newProc*(identifier: Ident, params: FieldSeq = nil,
-              returns: Ident, generics: FieldSeq = nil): Proc =
+proc newProc*(identifier: Ident, params: FieldSeq = @[],
+              returns: Ident, generics: FieldSeq = @[]): Proc =
   result.identifier = identifier
   result.params = params
   result.returns = returns
@@ -209,7 +209,7 @@ proc newProc*(node: NimNode): Proc =
       case node[2].kind:
         of nnkGenericParams:
           result.generics = newFieldSeq(node[2])
-        else: result.generics = nil
+        else: result.generics = @[]
       let formal_params = node[3]
       case formal_params[0].kind:
         of nnkIdent:
diff --git a/tests/vm/mevalffi.nim b/tests/vm/mevalffi.nim
new file mode 100644
index 000000000..ad8f8b62b
--- /dev/null
+++ b/tests/vm/mevalffi.nim
@@ -0,0 +1,71 @@
+# re-enable for windows once libffi can be installed in koch.nim
+# With win32 (not yet win64), libffi on windows works and this test passes.
+
+when defined(linux) or defined(bsd):
+  {.passL: "-lm".} # for exp
+proc c_exp(a: float64): float64 {.importc: "exp", header: "<math.h>".}
+
+proc c_printf(frmt: cstring): cint {.importc: "printf", header: "<stdio.h>", varargs, discardable.}
+
+const snprintfName = when defined(windows): "_snprintf" else: "snprintf"
+proc c_snprintf*(str: cstring, size: csize_t, format: cstring): cint {.importc: snprintfName, header: "<stdio.h>", varargs .}
+
+proc c_malloc(size: csize_t): pointer {.importc:"malloc", header: "<stdlib.h>".}
+proc c_free(p: pointer) {.importc:"free", header: "<stdlib.h>".}
+
+proc fun() =
+  block: # c_exp
+    var x = 0.3
+    let b = c_exp(x)
+    let b2 = int(b*1_000_000) # avoids floating point equality
+    doAssert b2 == 1349858
+    doAssert c_exp(0.3) == c_exp(x)
+    const x2 = 0.3
+    doAssert c_exp(x2) == c_exp(x)
+
+  block: # c_printf
+    c_printf("foo\n")
+    c_printf("foo:%d\n", 100)
+    c_printf("foo:%d\n", 101.cint)
+    c_printf("foo:%d:%d\n", 102.cint, 103.cint)
+    let temp = 104.cint
+    c_printf("foo:%d:%d:%d\n", 102.cint, 103.cint, temp)
+    var temp2 = 105.cint
+    c_printf("foo:%g:%s:%d:%d\n", 0.03, "asdf", 103.cint, temp2)
+
+  block: # c_snprintf, c_malloc, c_free
+    let n: uint = 50
+    var buffer2 = cstring(cast[ptr char](c_malloc(n)))
+
+    var s: cstring = "foobar"
+    var age: cint = 25
+    let num = c_snprintf(buffer2, n, "s1:%s s2:%s age:%d pi:%g", s, s, age, 3.14)
+    let numExp = 34 
+    doAssert num == numExp
+    c_printf("ret=[%s]\n", buffer2)
+    c_free(buffer2)
+
+  block: # c_printf bug
+    var a = 123
+    var a2 = a.addr
+    #[
+    bug: different behavior between CT RT in this case:
+    at CT, shows foo2:a=123
+    at RT, shows foo2:a=<address as int>
+    ]#
+    if false:
+      c_printf("foo2:a=%d\n", a2)
+
+
+static:
+  fun()
+fun()
+
+when not defined nimEvalffiStderrWorkaround:
+  import system/ansi_c
+  block:
+    proc fun2()=
+      c_fprintf(cstderr, "hello world stderr\n")
+      write(stderr, "hi stderr\n")
+    static: fun2()
+    fun2()
diff --git a/tests/vm/mscriptcompiletime.nim b/tests/vm/mscriptcompiletime.nim
new file mode 100644
index 000000000..ed7e7c029
--- /dev/null
+++ b/tests/vm/mscriptcompiletime.nim
@@ -0,0 +1,7 @@
+# bug.nim
+var bar* {.compileTime.} = 1
+
+proc dummy = discard
+
+static:
+  inc bar
\ No newline at end of file
diff --git a/tests/vm/t11637.nim b/tests/vm/t11637.nim
new file mode 100644
index 000000000..c061c6641
--- /dev/null
+++ b/tests/vm/t11637.nim
@@ -0,0 +1,52 @@
+type Foo = ref object
+  val: int
+
+proc `+`(a, b: Foo): Foo =
+  Foo(val: a.val + b.val)
+
+proc `*`(a: Foo, b: int): Foo =
+  Foo(val: a.val * b)
+
+proc `+=`(a: var Foo, b: Foo) =
+  a = Foo(
+    val: a.val + b.val
+  )
+
+proc foobar(a, b, c: Foo): tuple[bar, baz, buzz: Foo] =
+
+  let foo = a + b + c
+  result.bar = foo * 2
+
+  result.baz = foo * 3
+  result.buzz = result.baz
+
+  result.buzz += a * 10000
+  result.baz += b
+  result.buzz += b
+
+
+block: # Compile-Time
+  let
+    a {.compileTime.} = Foo(val: 1)
+    b {.compileTime.} = Foo(val: 2)
+    c {.compileTime.} = Foo(val: 3)
+    r {.compileTime.} = foobar(a, b, c)
+
+  static:
+    doAssert r.bar.val == 12
+    doAssert r.baz.val == 20
+    doAssert r.buzz.val == 10020
+
+####################################
+
+block: # Run-time
+  let
+    a = Foo(val: 1)
+    b = Foo(val: 2)
+    c = Foo(val: 3)
+    r = foobar(a, b, c)
+
+  # Expected values
+  doAssert r.bar.val == 12
+  doAssert r.baz.val == 20
+  doAssert r.buzz.val == 10020
diff --git a/tests/vm/t17039.nim b/tests/vm/t17039.nim
new file mode 100644
index 000000000..f92c93f30
--- /dev/null
+++ b/tests/vm/t17039.nim
@@ -0,0 +1,10 @@
+type
+  Obj1 = object
+    case kind: bool
+    of false:
+      field: seq[int]
+    else: discard
+
+static:
+  var obj1 = Obj1()
+  obj1.field.add(@[])
diff --git a/tests/vm/t17121.nim b/tests/vm/t17121.nim
new file mode 100644
index 000000000..bf2d6423f
--- /dev/null
+++ b/tests/vm/t17121.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "cannot 'importc' variable at compile time; c_printf"
+"""
+
+proc c_printf*(frmt: cstring): cint {.importc: "printf", header: "<stdio.h>", varargs, discardable.} =
+  ## foo bar
+  runnableExamples: discard
+static:
+  let a = c_printf("abc\n")
diff --git a/tests/vm/t18103.nim b/tests/vm/t18103.nim
new file mode 100644
index 000000000..8622ab290
--- /dev/null
+++ b/tests/vm/t18103.nim
@@ -0,0 +1,35 @@
+discard """
+  targets: "c cpp"
+  matrix: "--mm:refc; --mm:arc"
+"""
+
+import base64, complex, sequtils, math, sugar
+
+type
+
+  FP = float
+  T = object
+    index: int
+    arg: FP
+    val: Complex[FP]
+  M = object
+    alpha, beta: FP
+
+func a(s: openArray[T], model: M): seq[T] =
+  let f = (tn: int) => model.alpha + FP(tn) * model.beta;
+  return mapIt s:
+    block:
+      let s = it.val * rect(1.0, - f(it.index))
+      T(index: it.index, arg: phase(s), val: s)
+
+proc b(): float64 =
+  var s = toSeq(0..10).mapIt(T(index: it, arg: 1.0, val: complex.complex(1.0)))
+  discard a(s, M(alpha: 1, beta: 1))
+  return 1.0
+
+func cc(str: cstring, offset: ptr[cdouble]): cint {.exportc.} =
+  offset[] = b()
+  return 0
+
+static:
+  echo b()
diff --git a/tests/vm/t19075.nim b/tests/vm/t19075.nim
new file mode 100644
index 000000000..89ca9cb19
--- /dev/null
+++ b/tests/vm/t19075.nim
@@ -0,0 +1,19 @@
+discard """
+  timeout: 10
+  joinable: false
+"""
+
+# bug #19075
+const size = 50_000
+
+const stuff = block:
+    var a: array[size, int]
+    a
+
+const zeugs = block:
+    var zeugs: array[size, int]
+    for i in 0..<size:
+        zeugs[i] = stuff[i]
+    zeugs
+
+doAssert zeugs[0] == 0
\ No newline at end of file
diff --git a/tests/vm/t19199.nim b/tests/vm/t19199.nim
new file mode 100644
index 000000000..6ae48cb54
--- /dev/null
+++ b/tests/vm/t19199.nim
@@ -0,0 +1,6 @@
+# bug #19199
+proc mikasa(x: float) = doAssert x == 42
+
+static:
+  mikasa 42.uint.float
+mikasa 42.uint.float
diff --git a/tests/vm/t20746.nim b/tests/vm/t20746.nim
new file mode 100644
index 000000000..bfad269ef
--- /dev/null
+++ b/tests/vm/t20746.nim
@@ -0,0 +1,13 @@
+discard """
+  timeout: 10
+  joinable: false
+  output: "fine"
+"""
+
+func addString(): string =
+  let x = newString(1000000)
+  for i in 0..<1000000:
+    discard x[i]
+
+const translationTable = addString()
+echo "fine"
diff --git a/tests/vm/t21704.nim b/tests/vm/t21704.nim
new file mode 100644
index 000000000..27f4f5b06
--- /dev/null
+++ b/tests/vm/t21704.nim
@@ -0,0 +1,69 @@
+discard """
+matrix: "--hints:off"
+nimout: '''
+Found 2 tests to run.
+Found 3 benches to compile.
+ 
+  --passC:-Wno-stringop-overflow --passL:-Wno-stringop-overflow 
+ 
+  --passC:-Wno-stringop-overflow --passL:-Wno-stringop-overflow 
+ 
+  --passC:-Wno-stringop-overflow --passL:-Wno-stringop-overflow
+'''
+"""
+# bug #21704
+import std/strformat
+
+const testDesc: seq[string] = @[
+  "tests/t_hash_sha256_vs_openssl.nim",
+  "tests/t_cipher_chacha20.nim"
+]
+const benchDesc = [
+  "bench_sha256",
+  "bench_hash_to_curve",
+  "bench_ethereum_bls_signatures"
+]
+
+proc setupTestCommand(flags, path: string): string =
+  return "nim c -r " &
+    flags &
+    &" --nimcache:nimcache/{path} " & # Commenting this out also solves the issue
+    path
+
+proc testBatch(commands: var string, flags, path: string) =
+  commands &= setupTestCommand(flags, path) & '\n'
+
+proc setupBench(benchName: string): string =
+  var runFlags = if false: " -r "
+                 else: " " # taking this branch is needed to trigger the bug
+
+  echo runFlags # Somehow runflags isn't reset in corner cases
+  runFlags &= " --passC:-Wno-stringop-overflow --passL:-Wno-stringop-overflow "
+  echo runFlags
+
+  return "nim c " &
+       runFlags &
+       &" benchmarks/{benchName}.nim"
+
+proc buildBenchBatch(commands: var string, benchName: string) =
+  let command = setupBench(benchName)
+  commands &= command & '\n'
+
+proc addTestSet(cmdFile: var string) =
+  echo "Found " & $testDesc.len & " tests to run."
+
+  for path in testDesc:
+    var flags = "" # This is important
+    cmdFile.testBatch(flags, path)
+
+proc addBenchSet(cmdFile: var string) =
+  echo "Found " & $benchDesc.len & " benches to compile."
+  for bd in benchDesc:
+    cmdFile.buildBenchBatch(bd)
+
+proc task_bug() =
+  var cmdFile: string
+  cmdFile.addTestSet() # Comment this out and there is no bug
+  cmdFile.addBenchSet()
+
+static: task_bug()
diff --git a/tests/vm/t2574.nim b/tests/vm/t2574.nim
new file mode 100644
index 000000000..4332667b4
--- /dev/null
+++ b/tests/vm/t2574.nim
@@ -0,0 +1,14 @@
+discard """
+  errormsg: "cannot call method eval at compile time"
+  line: 14
+"""
+
+type
+  PExpr = ref object of RootObj
+
+method eval(e: PExpr): int =
+  discard
+
+static:
+  let x = PExpr()
+  discard x.eval
diff --git a/tests/vm/t9622.nim b/tests/vm/t9622.nim
new file mode 100644
index 000000000..fada8fe59
--- /dev/null
+++ b/tests/vm/t9622.nim
@@ -0,0 +1,30 @@
+discard """
+  targets: "c cpp"
+  matrix: "--mm:refc; --mm:arc"
+"""
+
+type
+  GlobNodeKind = enum
+    LiteralIdent,
+    Group
+
+  GlobNode = object
+    case kind: GlobNodeKind
+    of LiteralIdent:
+      value: string
+    of Group:
+      values: seq[string]
+
+  PathSegment = object
+    children: seq[GlobNode]
+
+  GlobPattern = seq[PathSegment]
+
+proc parseImpl(): GlobPattern =
+  if result.len == 0:
+    result.add PathSegment()
+  result[^1].children.add GlobNode(kind: LiteralIdent)
+
+block:
+  const pattern = parseImpl()
+  doAssert $pattern == """@[(children: @[(kind: LiteralIdent, value: "")])]"""
diff --git a/tests/vm/tableinstatic.nim b/tests/vm/tableinstatic.nim
index b0d24b477..934c3a8dd 100644
--- a/tests/vm/tableinstatic.nim
+++ b/tests/vm/tableinstatic.nim
@@ -2,7 +2,7 @@ discard """
   nimout: '''0
 0
 0
-{"hallo": "123", "welt": "456"}'''
+'''
 """
 
 import tables
@@ -35,4 +35,4 @@ static:
   otherTable["hallo"] = "123"
   otherTable["welt"]  = "456"
 
-  echo otherTable
+  doAssert otherTable == {"hallo": "123", "welt": "456"}.newTable
diff --git a/tests/vm/taddrof.nim b/tests/vm/taddrof.nim
new file mode 100644
index 000000000..bbe9345d2
--- /dev/null
+++ b/tests/vm/taddrof.nim
@@ -0,0 +1,110 @@
+discard """
+nimout: '''
+true
+true
+[nil, nil, nil, nil]
+[MyObjectRef(123, 321), nil, nil, nil]
+['A', '\x00', '\x00', '\x00']
+MyObjectRef(123, 321)
+(key: 8, val: 0)
+'''
+output: '''
+true
+true
+[nil, nil, nil, nil]
+[MyObjectRef(123, 321), nil, nil, nil]
+['A', '\x00', '\x00', '\x00']
+MyObjectRef(123, 321)
+'''
+"""
+
+type
+  MyObjectRef = ref object
+    a,b: int
+
+  MyContainerObject = ref object
+    member: MyObjectRef
+
+  MySuperContainerObject = ref object
+    member: MyContainerObject
+    arr: array[4, MyObjectRef]
+
+  MyOtherObject = ref object
+    case kind: bool
+    of true:
+      member: MyObjectRef
+    else:
+      discard
+
+proc `$`(arg: MyObjectRef): string =
+  result = "MyObjectRef("
+  result.addInt arg.a
+  result.add ", "
+  result.addInt arg.b
+  result.add ")"
+
+proc foobar(dst: var MyObjectRef) =
+  dst = new(MyObjectRef)
+  dst.a = 123
+  dst.b = 321
+
+proc changeChar(c: var char) =
+  c = 'A'
+
+proc test() =
+  # when it comes from a var, it works
+  var y: MyObjectRef
+  foobar(y)
+  echo y != nil
+  # when it comes from a member, it fails on VM
+  var x = new(MyContainerObject)
+  foobar(x.member)
+  echo x.member != nil
+
+  # when it comes from an array, it fails on VM
+  var arr: array[4, MyObjectRef]
+  echo arr
+  foobar(arr[0])
+  echo arr
+
+  var arr2: array[4, char]
+  changeChar(arr2[0])
+  echo arr2
+
+
+  var z = MyOtherObject(kind: true)
+  foobar(z.member)
+  echo z.member
+
+  # this still doesn't work
+  # var sc = new(MySuperContainerObject)
+  # sc.member = new(MyContainerObject)
+  # foobar(sc.member.member)
+  # echo sc.member.member
+  # foobar(sc.arr[1])
+  # echo sc.arr
+
+  #var str = "---"
+  #changeChar(str[1])
+  #echo str
+
+test()
+static:
+  test()
+
+type T = object
+  f: seq[tuple[key, val: int]]
+
+proc foo(s: var seq[tuple[key, val: int]]; i: int) =
+  s[i].key = 4*i
+  # r4 = addr(s[i])
+  # r4[0] = 4*i
+
+proc bar() =
+  var s: T
+  s.f = newSeq[tuple[key, val: int]](3)
+  foo(s.f, 2)
+  echo s.f[2]
+
+static:
+  bar()
diff --git a/tests/vm/tanonproc.nim b/tests/vm/tanonproc.nim
index c5cb57d75..1176c104e 100644
--- a/tests/vm/tanonproc.nim
+++ b/tests/vm/tanonproc.nim
@@ -4,7 +4,7 @@ discard """
 
 # bug #3561
 
-import macros, future, strutils
+import macros, sugar, strutils
 
 type
   Option[T] = ref object
@@ -42,7 +42,7 @@ proc getOrElse[T](o: Option[T], def: T): T =
 proc quoteStr(s: string): Option[string] =
   s.some.notEmpty.map(v => "`" & v & "`")
 
-macro str(s: string): typed =
+macro str(s: string): void =
   let x = s.strVal
   let y = quoteStr(x)
   let sn = newStrLitNode(y.getOrElse("NONE"))
diff --git a/tests/vm/tbitops.nim b/tests/vm/tbitops.nim
new file mode 100644
index 000000000..90d463ec9
--- /dev/null
+++ b/tests/vm/tbitops.nim
@@ -0,0 +1,45 @@
+discard """
+output: ""
+"""
+
+import strutils
+
+const x  = [1'i32, -1, -10, 10, -10, 10, -20, 30, -40, 50, 7 shl 28, -(7 shl 28), 7 shl 28, -(7 shl 28)]
+const y  = [-1'i32, 1, -10, -10, 10, 10, -20, -30, 40, 50, 1 shl 30, 1 shl 30, -(1 shl 30), -(1 shl 30)]
+
+const res_xor = block:
+  var tmp: seq[int64]
+  for i in 0 ..< x.len:
+    tmp.add(int64(x[i] xor y[i]))
+  tmp
+
+const res_and = block:
+  var tmp: seq[int64]
+  for i in 0 ..< x.len:
+    tmp.add(int64(x[i] and y[i]))
+  tmp
+
+const res_or = block:
+  var tmp: seq[int64]
+  for i in 0 ..< x.len:
+    tmp.add(int64(x[i] or y[i]))
+  tmp
+
+const res_not = block:
+  var tmp: seq[int64]
+  for i in 0 ..< x.len:
+    tmp.add(not x[i])
+  tmp
+
+let xx = x
+let yy = y
+
+for i in 0..<xx.len:
+  let z_xor = int64(xx[i] xor yy[i])
+  let z_and = int64(xx[i] and yy[i])
+  let z_or = int64(xx[i] or yy[i])
+  let z_not = int64(not xx[i])
+  doAssert(z_xor == res_xor[i], $i & ": " & $res_xor[i] & "  " & $z_xor)
+  doAssert(z_and == res_and[i], $i & ": " & $res_and[i] & "  " & $z_and)
+  doAssert(z_or == res_or[i], $i & ": " & $res_or[i] & "  " & $z_or)
+  doAssert(z_not == res_not[i], $i & ": " & $res_not[i] & "  " & $z_not)
diff --git a/tests/vm/tcastint.nim b/tests/vm/tcastint.nim
index 7b9ddd7d9..c306e4a31 100644
--- a/tests/vm/tcastint.nim
+++ b/tests/vm/tcastint.nim
@@ -1,8 +1,5 @@
-discard """
-  file: "tcastint.nim"
-  output: "OK"
-"""
-
+import macros
+from stdtest/testutils import disableVM
 type
   Dollar = distinct int
   XCoord = distinct int32
@@ -14,6 +11,12 @@ proc `==`(x, y: XCoord): bool {.borrow.}
 
 proc dummy[T](x: T): T = x
 
+template roundTrip(a, T) =
+  let a2 = a # sideeffect safe
+  let b = cast[T](a2)
+  let c = cast[type(a2)](b)
+  doAssert c == a2
+
 proc test() =
   let U8 = 0b1011_0010'u8
   let I8 = 0b1011_0010'i8
@@ -101,20 +104,206 @@ proc test() =
     doAssert(cast[int](digit) == raw)
     doAssert(cast[Digit](raw) == digit)
 
-  when defined nimvm:
-    doAssert(not compiles(cast[float](I64A)))
-    doAssert(not compiles(cast[float32](I64A)))
+  block:
+    roundTrip(I64A, float)
+    roundTrip(I8, uint16)
+    roundTrip(I8, uint32)
+    roundTrip(I8, uint64)
+    doAssert cast[uint16](I8) == 65458'u16
+    doAssert cast[uint32](I8) == 4294967218'u32
+    doAssert cast[uint64](I8) == 18446744073709551538'u64
+    doAssert cast[uint32](I64A) == 2571663889'u32
+    doAssert cast[uint16](I64A) == 31249
+    doAssert cast[char](I64A).ord == 17
+    doAssert compiles(cast[float32](I64A))
+
+  disableVM: # xxx Error: VM does not support 'cast' from tyInt64 to tyFloat32
+    doAssert cast[uint32](cast[float32](I64A)) == 2571663889'u32
+
+const prerecordedResults = [
+  # cast to char
+  "\0", "\255",
+  "\0", "\255",
+  "\0", "\255",
+  "\0", "\255",
+  "\0", "\255",
+  "\128", "\127",
+  "\0", "\255",
+  "\0", "\255",
+  "\0", "\255",
+  # cast to uint8
+  "0", "255",
+  "0", "255",
+  "0", "255",
+  "0", "255",
+  "0", "255",
+  "128", "127",
+  "0", "255",
+  "0", "255",
+  "0", "255",
+  # cast to uint16
+  "0", "255",
+  "0", "255",
+  "0", "65535",
+  "0", "65535",
+  "0", "65535",
+  "65408", "127",
+  "32768", "32767",
+  "0", "65535",
+  "0", "65535",
+  # cast to uint32
+  "0", "255",
+  "0", "255",
+  "0", "65535",
+  "0", "4294967295",
+  "0", "4294967295",
+  "4294967168", "127",
+  "4294934528", "32767",
+  "2147483648", "2147483647",
+  "0", "4294967295",
+  # cast to uint64
+  "0", "255",
+  "0", "255",
+  "0", "65535",
+  "0", "4294967295",
+  "0", "18446744073709551615",
+  "18446744073709551488", "127",
+  "18446744073709518848", "32767",
+  "18446744071562067968", "2147483647",
+  "9223372036854775808", "9223372036854775807",
+  # cast to int8
+  "0", "-1",
+  "0", "-1",
+  "0", "-1",
+  "0", "-1",
+  "0", "-1",
+  "-128", "127",
+  "0", "-1",
+  "0", "-1",
+  "0", "-1",
+  # cast to int16
+  "0", "255",
+  "0", "255",
+  "0", "-1",
+  "0", "-1",
+  "0", "-1",
+  "-128", "127",
+  "-32768", "32767",
+  "0", "-1",
+  "0", "-1",
+  # cast to int32
+  "0", "255",
+  "0", "255",
+  "0", "65535",
+  "0", "-1",
+  "0", "-1",
+  "-128", "127",
+  "-32768", "32767",
+  "-2147483648", "2147483647",
+  "0", "-1",
+  # cast to int64
+  "0", "255",
+  "0", "255",
+  "0", "65535",
+  "0", "4294967295",
+  "0", "-1",
+  "-128", "127",
+  "-32768", "32767",
+  "-2147483648", "2147483647",
+  "-9223372036854775808", "9223372036854775807",
+]
+
+proc free_integer_casting() =
+  # cast from every integer type to every type and ensure same
+  # behavior in vm and execution time.
+  macro bar(arg: untyped) =
+    result = newStmtList()
+    var i = 0
+    for it1 in arg:
+      let typA = it1[0]
+      for it2 in arg:
+        let lowB = it2[1]
+        let highB = it2[2]
+        let castExpr1 = nnkCast.newTree(typA, lowB)
+        let castExpr2 = nnkCast.newTree(typA, highB)
+        let lit1 = newLit(prerecordedResults[i*2])
+        let lit2 = newLit(prerecordedResults[i*2+1])
+        result.add quote do:
+          doAssert($(`castExpr1`) == `lit1`)
+          doAssert($(`castExpr2`) == `lit2`)
+        i += 1
+
+  bar([
+    (char, '\0', '\255'),
+    (uint8, 0'u8, 0xff'u8),
+    (uint16, 0'u16, 0xffff'u16),
+    (uint32, 0'u32, 0xffffffff'u32),
+    (uint64, 0'u64, 0xffffffffffffffff'u64),
+    (int8,  0x80'i8, 0x7f'i8),
+    (int16, 0x8000'i16, 0x7fff'i16),
+    (int32, 0x80000000'i32, 0x7fffffff'i32),
+    (int64, 0x8000000000000000'i64, 0x7fffffffffffffff'i64)
+  ])
+
+proc test_float_cast =
+
+  const
+    exp_bias = 1023'i64
+    exp_shift = 52
+    exp_mask = 0x7ff'i64 shl exp_shift
+    mantissa_mask = 0xfffffffffffff'i64
+
+  let f = 8.0
+  let fx = cast[int64](f)
+  let exponent = ((fx and exp_mask) shr exp_shift) - exp_bias
+  let mantissa = fx and mantissa_mask
+  doAssert(exponent == 3, $exponent)
+  doAssert(mantissa == 0, $mantissa)
+
+  # construct 2^N float, where N is integer
+  let x = -2'i64
+  let xx = (x + exp_bias) shl exp_shift
+  let xf = cast[float](xx)
+  doAssert(xf == 0.25, $xf)
+
+proc test_float32_cast =
+
+  const
+    exp_bias = 127'i32
+    exp_shift = 23
+    exp_mask = 0x7f800000'i32
+    mantissa_mask = 0x007ffff'i32
+
+  let f = -0.5'f32
+  let fx = cast[int32](f)
+  let exponent = ((fx and exp_mask) shr exp_shift) - exp_bias
+  let mantissa = fx and mantissa_mask
+  doAssert(exponent == -1, $exponent)
+  doAssert(mantissa == 0, $mantissa)
 
-    doAssert(not compiles(cast[char](I64A)))
-    doAssert(not compiles(cast[uint16](I64A)))
-    doAssert(not compiles(cast[uint32](I64A)))
+  # construct 2^N float32 where N is integer
+  let x = 4'i32
+  let xx = (x + exp_bias) shl exp_shift
+  let xf = cast[float32](xx)
+  doAssert(xf == 16.0'f32, $xf)
 
-    doAssert(not compiles(cast[uint16](I8)))
-    doAssert(not compiles(cast[uint32](I8)))
-    doAssert(not compiles(cast[uint64](I8)))
+proc test_float32_castB() =
+  let a: float32 = -123.125
+  let b = cast[int32](a)
+  let c = cast[uint32](a)
+  doAssert b == -1024049152
+  doAssert cast[uint64](b) == 18446744072685502464'u64
+  doAssert c == 3270918144'u32
+  # ensure the unused bits in the internal representation don't have
+  # any surprising content.
+  doAssert cast[uint64](c) == 3270918144'u64
 
-test()
-static:
+template main() =
   test()
+  test_float_cast()
+  test_float32_cast()
+  free_integer_casting()
+  test_float32_castB()
 
-echo "OK"
+static: main()
+main()
diff --git a/tests/vm/tcgemptycallarg.nim b/tests/vm/tcgemptycallarg.nim
new file mode 100644
index 000000000..1bedb5070
--- /dev/null
+++ b/tests/vm/tcgemptycallarg.nim
@@ -0,0 +1,3 @@
+static:
+  doAssertRaises(ValueError):
+    raise newException(ValueError, "Yes")
diff --git a/tests/vm/tclosureiterator.nim b/tests/vm/tclosureiterator.nim
new file mode 100644
index 000000000..c909392d5
--- /dev/null
+++ b/tests/vm/tclosureiterator.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "Closure iterators are not supported by VM!"
+"""
+
+iterator iter*(): int {.closure.} =
+  yield 3
+
+static:
+  var x = iter
diff --git a/tests/vm/tcnstseq.nim b/tests/vm/tcnstseq.nim
new file mode 100644
index 000000000..5679a6e37
--- /dev/null
+++ b/tests/vm/tcnstseq.nim
@@ -0,0 +1,68 @@
+discard """
+output: '''
+AngelikaAnneAnnaAnkaAnja
+AngelikaAnneAnnaAnkaAnja
+AngelikaAnneAnnaAnkaAnja
+onetwothree
+onetwothree
+onetwothree
+one1two2three3
+'''
+"""
+# Test the new implicit conversion from sequences to arrays in a constant
+# context.
+
+import strutils
+
+
+block t1:
+  const
+    myWords = "Angelika Anne Anna Anka Anja".split()
+
+  for x in items(myWords):
+    write(stdout, x) #OUT AngelikaAnneAnnaAnkaAnja
+  echo ""
+
+
+block t2:
+  const
+    myWords = @["Angelika", "Anne", "Anna", "Anka", "Anja"]
+
+  for i in 0 .. high(myWords):
+    write(stdout, myWords[i]) #OUT AngelikaAnneAnnaAnkaAnja
+  echo ""
+
+
+block t3:
+  for w in items(["Angelika", "Anne", "Anna", "Anka", "Anja"]):
+    write(stdout, w) #OUT AngelikaAnneAnnaAnkaAnja
+  echo ""
+
+
+block t2656:
+  iterator it1(args: seq[string]): string =
+    for s in args: yield s
+  iterator it2(args: seq[string]): string {.closure.} =
+    for s in args: yield s
+  iterator it3(args: openArray[string]): string {.closure.} =
+    for s in args: yield s
+  iterator it4(args: openArray[(string, string)]): string {.closure.} =
+    for s1, s2 in items(args): yield s1 & s2
+
+  block:
+    const myConstSeq = @["one", "two", "three"]
+    for s in it1(myConstSeq):
+      stdout.write s
+    echo ""
+    for s in it2(myConstSeq):
+      stdout.write s
+    echo ""
+    for s in it3(myConstSeq):
+      stdout.write s
+    echo ""
+
+  block:
+    const myConstSeq = @[("one", "1"), ("two", "2"), ("three", "3")]
+    for s in it4(myConstSeq):
+      stdout.write s
+    echo ""
diff --git a/tests/vm/tcompilesetting.nim b/tests/vm/tcompilesetting.nim
new file mode 100644
index 000000000..d6c08e70f
--- /dev/null
+++ b/tests/vm/tcompilesetting.nim
@@ -0,0 +1,18 @@
+discard """
+cmd: "nim c --nimcache:build/myNimCache --nimblePath:myNimblePath --gc:arc $file"
+joinable: false
+"""
+
+import std/[strutils,compilesettings]
+from std/os import fileExists, `/`
+
+template main =
+  doAssert querySetting(nimcacheDir) == nimcacheDir.querySetting
+  doAssert "myNimCache" in nimcacheDir.querySetting
+  doAssert "myNimblePath" in nimblePaths.querySettingSeq[0]
+  doAssert querySetting(backend) == "c"
+  doAssert fileExists(libPath.querySetting / "system.nim")
+  doAssert querySetting(mm) == "arc"
+
+static: main()
+main()
diff --git a/tests/vm/tcompiletimerange.nim b/tests/vm/tcompiletimerange.nim
new file mode 100644
index 000000000..cd675b4a3
--- /dev/null
+++ b/tests/vm/tcompiletimerange.nim
@@ -0,0 +1,28 @@
+discard """
+"""
+
+# issue #8199
+
+const rangesGCHoldEnabled = true # not defined(rangesDisableGCHold)
+
+type
+  # A view into immutable array
+  Range*[T] {.shallow.} = object
+    when rangesGCHoldEnabled:
+      gcHold: seq[T] # 0
+    start: ptr T # 1
+    mLen: int32 # 2
+
+type
+  BytesRange* = Range[byte]
+  NibblesRange* = object
+    bytes: BytesRange
+
+const
+  zeroBytesRange* = BytesRange()
+
+proc initNibbleRange*(bytes: BytesRange): NibblesRange =
+  result.bytes = bytes
+
+const
+  zeroNibblesRange* = initNibbleRange(zeroBytesRange)
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/tcompiletimetable.nim b/tests/vm/tcompiletimetable.nim
index e78c06536..1db490f1a 100644
--- a/tests/vm/tcompiletimetable.nim
+++ b/tests/vm/tcompiletimetable.nim
@@ -1,9 +1,16 @@
 discard """
-  nimout: '''2
+  nimout: '''
+2
 3
 4:2
 Got Hi
-Got Hey'''
+Got Hey
+'''
+  output:'''
+a
+b
+c
+'''
 """
 
 # bug #404
@@ -47,3 +54,9 @@ addStuff("Hey"): echo "Hey"
 addStuff("Hi"): echo "Hi"
 dump()
 
+# ensure .compileTime vars can be used at runtime:
+import macros
+
+var xzzzz {.compileTime.}: array[3, string] = ["a", "b", "c"]
+
+for i in 0..high(xzzzz): echo xzzzz[i]
diff --git a/tests/vm/tcomponent.nim b/tests/vm/tcomponent.nim
index e7962e7ab..b509bc5d7 100644
--- a/tests/vm/tcomponent.nim
+++ b/tests/vm/tcomponent.nim
@@ -11,7 +11,7 @@ FOO: blah'''
 
 import macros, sequtils, tables
 import strutils
-import future, meta
+import sugar, meta
 
 type
   Component = object
diff --git a/tests/vm/tconst.nim b/tests/vm/tconst.nim
new file mode 100644
index 000000000..5cfe7533e
--- /dev/null
+++ b/tests/vm/tconst.nim
@@ -0,0 +1,58 @@
+discard """
+  targets: "c cpp js"
+"""
+
+import std/strutils
+
+template forceConst(a: untyped): untyped =
+  ## Force evaluation at CT, but `static(a)` is simpler
+  const ret = a
+  ret
+
+proc isNimVm(): bool =
+  when nimvm: result = true
+  else: result = false
+
+block:
+  doAssert forceConst(isNimVm())
+  doAssert not isNimVm()
+  doAssert forceConst(isNimVm()) == static(isNimVm())
+  doAssert forceConst(isNimVm()) == isNimVm().static
+
+template main() =
+  # xxx merge more const related tests here
+  const ct = CompileTime
+    # refs https://github.com/timotheecour/Nim/issues/718, apparently `CompileTime`
+    # isn't cached, which seems surprising.
+  block:
+    const
+      a = """
+    Version $1|
+    Compiled at: $2, $3
+    """ % [NimVersion & spaces(44-len(NimVersion)), CompileDate, ct]
+    let b = $a
+    doAssert ct in b, $(b, ct)
+    doAssert NimVersion in b
+
+  block: # Test for fix on broken const unpacking
+    template mytemp() =
+      const
+        (x, increment) = (4, true)
+        a = 100
+      discard (x, increment, a)
+    mytemp()
+
+  block: # bug #12334
+    block:
+      const b: cstring = "foo"
+      var c = b
+      doAssert c == "foo"
+    block:
+      const a = "foo"
+      const b: cstring = a
+      var c = b
+      doAssert c == "foo"
+
+
+static: main()
+main()
diff --git a/tests/vm/tconstarrayresem.nim b/tests/vm/tconstarrayresem.nim
new file mode 100644
index 000000000..6701cfe4d
--- /dev/null
+++ b/tests/vm/tconstarrayresem.nim
@@ -0,0 +1,29 @@
+# issue #23010
+
+type
+  Result[T, E] = object
+    case oResult: bool
+    of false:
+      discard
+    of true:
+      vResult: T
+
+  Opt[T] = Result[T, void]
+
+template ok[T, E](R: type Result[T, E], x: untyped): R =
+  R(oResult: true, vResult: x)
+
+template c[T](v: T): Opt[T] = Opt[T].ok(v)
+
+type
+  FixedBytes[N: static[int]] = distinct array[N, byte]
+
+  H = object
+    d: FixedBytes[2]
+
+const b = default(H)
+template g(): untyped =
+  const t = default(H)
+  b
+
+discard c(g())
diff --git a/tests/vm/tconsteval.nim b/tests/vm/tconsteval.nim
deleted file mode 100644
index f4260495a..000000000
--- a/tests/vm/tconsteval.nim
+++ /dev/null
@@ -1,31 +0,0 @@
-discard """
-"""
-
-import strutils
-
-const
-  HelpText = """
-+-----------------------------------------------------------------+
-|         Maintenance program for Nim                             |
-|             Version $1|
-|             (c) 2012 Andreas Rumpf                              |
-+-----------------------------------------------------------------+
-Compiled at: $2, $3
-
-Usage:
-  koch [options] command [options for command]
-Options:
-  --force, -f, -B, -b      forces rebuild
-  --help, -h               shows this help and quits
-Possible Commands:
-  boot [options]           bootstraps with given command line options
-  clean                    cleans Nim project; removes generated files
-  web                      generates the website
-  csource [options]        builds the C sources for installation
-  zip                      builds the installation ZIP package
-  inno                     builds the Inno Setup installer
-""" % [NimVersion & spaces(44-len(NimVersion)),
-       CompileDate, CompileTime]
-
-echo HelpText
-
diff --git a/tests/vm/tconstobj.nim b/tests/vm/tconstobj.nim
index 38fcdd844..7dc20a0ba 100644
--- a/tests/vm/tconstobj.nim
+++ b/tests/vm/tconstobj.nim
@@ -1,10 +1,13 @@
 discard """
-  output: '''(name: "hello")
-(-1, 0)'''
+  output: '''
+(name: "hello")
+(-1, 0)
+(FirstName: "James", LastName: "Franco")
+[1, 2, 3]
+'''
 """
 
 # bug #2774, bug #3195
-
 type Foo = object
   name: string
 
@@ -14,7 +17,6 @@ const fooArray = [
 
 echo fooArray[0]
 
-
 type
     Position = object
         x, y: int
@@ -34,3 +36,60 @@ const
      ]
 
 echo offset[1]
+
+# bug #1547
+import tables
+
+type Person* = object
+    FirstName*: string
+    LastName*: string
+
+let people = {
+    "001": Person(FirstName: "James", LastName: "Franco")
+}.toTable()
+
+echo people["001"]
+
+# Object downconversion should not copy
+
+type
+  SomeBaseObj  {.inheritable.} = object of RootObj
+    txt : string
+  InheritedFromBase = object of SomeBaseObj
+    other : string
+
+proc initBase(sbo: var SomeBaseObj) =
+  sbo.txt = "Initialized string from base"
+
+static:
+  var ifb2: InheritedFromBase
+  initBase(SomeBaseObj(ifb2))
+  echo repr(ifb2)
+  doAssert(ifb2.txt == "Initialized string from base")
+
+static: # issue #11861
+  var ifb2: InheritedFromBase
+  initBase(ifb2)
+  doAssert(ifb2.txt == "Initialized string from base")
+
+
+static: # issue #15662
+  proc a(T: typedesc) = echo T.type
+  a((int, int))
+
+# bug #16069
+type
+  E = enum
+    val1, val2
+  Obj = object
+    case k: E
+    of val1:
+      x: array[3, int]
+    of val2:
+      y: uint32
+
+const
+  foo = [1, 2, 3]
+  arr = Obj(k: val1, x: foo)
+
+echo arr.x
diff --git a/tests/vm/tconstprocassignments.nim b/tests/vm/tconstprocassignments.nim
new file mode 100644
index 000000000..0e2d2ed16
--- /dev/null
+++ b/tests/vm/tconstprocassignments.nim
@@ -0,0 +1,18 @@
+discard """
+  output: '''
+100
+100
+'''
+"""
+
+proc f():int {.compileTime.} = 100
+
+const F = f
+echo F()
+
+const G = proc ():int =
+  let x = f
+  let y = x
+  y()
+
+echo G()
diff --git a/tests/vm/tconstresem.nim b/tests/vm/tconstresem.nim
new file mode 100644
index 000000000..4526cb891
--- /dev/null
+++ b/tests/vm/tconstresem.nim
@@ -0,0 +1,10 @@
+block: # issue #19849
+  type
+    Vec2[T] = object
+      x, y: T
+    Vec2i = Vec2[int]
+  template getX(p: Vec2i): int = p.x
+  let x = getX:
+    const t = Vec2i(x: 1, y: 2)
+    t
+  doAssert x == 1
diff --git a/tests/vm/tconstscope1.nim b/tests/vm/tconstscope1.nim
new file mode 100644
index 000000000..41c45a28f
--- /dev/null
+++ b/tests/vm/tconstscope1.nim
@@ -0,0 +1,5 @@
+# issue #5395
+
+const a = (var b = 3; b)
+echo b #[tt.Error
+     ^ undeclared identifier: 'b']#
diff --git a/tests/vm/tconstscope2.nim b/tests/vm/tconstscope2.nim
new file mode 100644
index 000000000..d858e96c2
--- /dev/null
+++ b/tests/vm/tconstscope2.nim
@@ -0,0 +1,5 @@
+const
+  a = (var x = 3; x)
+  # should we allow this?
+  b = x #[tt.Error
+      ^ undeclared identifier: 'x']#
diff --git a/tests/vm/tconsttable.nim b/tests/vm/tconsttable.nim
index 64a74a59d..152a33cba 100644
--- a/tests/vm/tconsttable.nim
+++ b/tests/vm/tconsttable.nim
@@ -17,3 +17,18 @@ x = "ah"
 echo foo[x]
 x = "possible."
 echo foo[x]
+
+block: # bug #19840
+  const testBytes = [byte 0xD8, 0x08, 0xDF, 0x45, 0x00, 0x3D, 0x00, 0x52, 0x00, 0x61]
+  var tempStr = "__________________"
+
+  tempStr.prepareMutation
+  copyMem(addr tempStr[0], addr testBytes[0], testBytes.len)
+
+block: # bug #22389
+  func foo(): ptr UncheckedArray[byte] =
+    const bar = [77.byte]
+    cast[ptr UncheckedArray[byte]](addr bar[0])
+
+  doAssert foo()[0] == 77
+
diff --git a/tests/vm/tconsttable2.nim b/tests/vm/tconsttable2.nim
index e07734eb5..5a392fb66 100644
--- a/tests/vm/tconsttable2.nim
+++ b/tests/vm/tconsttable2.nim
@@ -1,5 +1,5 @@
 discard """
-  msg: '''61'''
+  nimout: '''61'''
 """
 
 # bug #2297
diff --git a/tests/vm/tconvaddr.nim b/tests/vm/tconvaddr.nim
new file mode 100644
index 000000000..9762a9e59
--- /dev/null
+++ b/tests/vm/tconvaddr.nim
@@ -0,0 +1,49 @@
+block: # issue #24097
+  type Foo = distinct int
+  proc foo(x: var Foo) =
+    int(x) += 1
+  proc bar(x: var int) =
+    x += 1
+  static:
+    var x = Foo(1)
+    int(x) = int(x) + 1
+    doAssert x.int == 2
+    int(x) += 1
+    doAssert x.int == 3
+    foo(x)
+    doAssert x.int == 4
+    bar(int(x)) # need vmgen flags propagated for this
+    doAssert x.int == 5
+  type Bar = object
+    x: Foo
+  static:
+    var obj = Bar(x: Foo(1))
+    int(obj.x) = int(obj.x) + 1
+    doAssert obj.x.int == 2
+    int(obj.x) += 1
+    doAssert obj.x.int == 3
+    foo(obj.x)
+    doAssert obj.x.int == 4
+    bar(int(obj.x)) # need vmgen flags propagated for this
+    doAssert obj.x.int == 5
+  static:
+    var arr = @[Foo(1)]
+    int(arr[0]) = int(arr[0]) + 1
+    doAssert arr[0].int == 2
+    int(arr[0]) += 1
+    doAssert arr[0].int == 3
+    foo(arr[0])
+    doAssert arr[0].int == 4
+    bar(int(arr[0])) # need vmgen flags propagated for this
+    doAssert arr[0].int == 5
+  proc testResult(): Foo =
+    result = Foo(1)
+    int(result) = int(result) + 1
+    doAssert result.int == 2
+    int(result) += 1
+    doAssert result.int == 3
+    foo(result)
+    doAssert result.int == 4
+    bar(int(result)) # need vmgen flags propagated for this
+    doAssert result.int == 5
+  doAssert testResult().int == 5
diff --git a/tests/vm/teval1.nim b/tests/vm/teval1.nim
index 0eaa050da..0316ea238 100644
--- a/tests/vm/teval1.nim
+++ b/tests/vm/teval1.nim
@@ -1,3 +1,8 @@
+
+discard """
+nimout: "##"
+"""
+
 import macros
 
 proc testProc: string {.compileTime.} =
@@ -14,9 +19,24 @@ when true:
 const
   x = testProc()
 
-echo "##", x, "##"
+doAssert x == ""
 
 # bug #1310
 static:
-    var i, j: set[int8] = {}
-    var k = i + j
+  var i, j: set[int8] = {}
+  var k = i + j
+
+type
+  Obj = object
+    x: int
+
+converter toObj(x: int): Obj = Obj(x: x)
+
+# bug #10514
+block:
+  const
+    b: Obj = 42
+    bar = [b]
+
+  let i_runtime = 0
+  doAssert bar[i_runtime] == b
diff --git a/tests/vm/texception.nim b/tests/vm/texception.nim
new file mode 100644
index 000000000..65a781281
--- /dev/null
+++ b/tests/vm/texception.nim
@@ -0,0 +1,14 @@
+proc someFunc() =
+  try:
+    raise newException(ValueError, "message")
+  except ValueError as err:
+    doAssert err.name == "ValueError"
+    doAssert err.msg == "message"
+    raise
+
+static:
+  try:
+    someFunc()
+  except:
+    discard
+  
\ No newline at end of file
diff --git a/tests/vm/texcl.nim b/tests/vm/texcl.nim
index e23a423fe..41975f27b 100644
--- a/tests/vm/texcl.nim
+++ b/tests/vm/texcl.nim
@@ -18,7 +18,7 @@ proc initOpts(): set[nlOptions] =
 
 const cOpts = initOpts()
 
-macro nlo(): typed =
+macro nlo() =
   nlOpts.incl(nloNone)
   nlOpts.excl(nloDebug)
   result = newEmptyNode()
diff --git a/tests/vm/textensionmap.nim b/tests/vm/textensionmap.nim
index 5d4b25d01..7ada1880d 100644
--- a/tests/vm/textensionmap.nim
+++ b/tests/vm/textensionmap.nim
@@ -10,4 +10,4 @@ const EXTENSIONMAP = {
   "c": @["*.c", "*.h"],
 }.toTable()
 
-const EXTENSIONS = toSet(concat(toSeq(EXTENSIONMAP.values())))
+const EXTENSIONS = toHashSet(concat(toSeq(EXTENSIONMAP.values())))
diff --git a/tests/vm/tfarjump.nim b/tests/vm/tfarjump.nim
new file mode 100644
index 000000000..f5798b8d2
--- /dev/null
+++ b/tests/vm/tfarjump.nim
@@ -0,0 +1,14 @@
+# Test a VM relative jump with an offset larger then 32767 instructions.
+
+import macros
+
+static:
+  var a = 0
+  macro foo(): untyped =
+    let s = newStmtList()
+    for i in 1..6554:
+      s.add nnkCommand.newTree(ident("inc"), ident("a"))
+    quote do:
+      if true:
+        `s`
+  foo()
diff --git a/tests/vm/tfibconst.nim b/tests/vm/tfibconst.nim
new file mode 100644
index 000000000..cca6dd84f
--- /dev/null
+++ b/tests/vm/tfibconst.nim
@@ -0,0 +1,43 @@
+discard """
+  nimout: '''
+Fibonacci sequence: 0, 1, 1, 2, 3
+Sequence continues: 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610
+'''
+"""
+
+
+import strformat
+
+var fib_n {.compileTime.}: int
+var fib_prev {.compileTime.}: int
+var fib_prev_prev {.compileTime.}: int
+
+proc next_fib(): int {.compileTime.} =
+  let fib = if fib_n < 2:
+    fib_n
+  else:
+    fib_prev_prev + fib_prev
+  inc(fib_n)
+  fib_prev_prev = fib_prev
+  fib_prev = fib
+  fib
+
+const f0 = next_fib()
+const f1 = next_fib()
+const f2 = next_fib()
+const f3 = next_fib()
+const f4 = next_fib()
+
+static:
+  echo fmt"Fibonacci sequence: {f0}, {f1}, {f2}, {f3}, {f4}"
+
+const fib_continues = block:
+  var result = fmt"Sequence continues: "
+  for i in 0..10:
+    if i > 0:
+      add(result, ", ")
+    add(result, $next_fib())
+  result
+
+static:
+  echo fib_continues
\ No newline at end of file
diff --git a/tests/vm/tfile_rw.nim b/tests/vm/tfile_rw.nim
new file mode 100644
index 000000000..439886e49
--- /dev/null
+++ b/tests/vm/tfile_rw.nim
@@ -0,0 +1,22 @@
+# test file read write in vm
+
+import os, strutils
+
+const filename  = splitFile(currentSourcePath).dir / "tfile_rw.txt"
+
+const mytext = "line1\nline2\nline3"
+static:
+  writeFile(filename, mytext)
+const myfile_str = staticRead(filename)
+const myfile_str2 = readFile(filename)
+const myfile_str_seq = readLines(filename, 3)
+
+static:
+  doAssert myfile_str == mytext
+  doAssert myfile_str2 == mytext
+  doAssert myfile_str_seq[0] == "line1"
+  doAssert myfile_str_seq[1] == "line2"
+  doAssert myfile_str_seq.join("\n") == mytext
+
+
+removeFile(filename)
diff --git a/tests/vm/tforwardproc.nim b/tests/vm/tforwardproc.nim
index 727ac6641..bcd929f0e 100644
--- a/tests/vm/tforwardproc.nim
+++ b/tests/vm/tforwardproc.nim
@@ -14,4 +14,4 @@ proc initArray(): array[10, int] =
   for f in 0..<10:
     result[f] = 3
 
-when isMainModule: echo repr(someTable)
+when true: echo repr(someTable)
diff --git a/tests/vm/tgenericcompiletimeproc.nim b/tests/vm/tgenericcompiletimeproc.nim
new file mode 100644
index 000000000..08099ebbe
--- /dev/null
+++ b/tests/vm/tgenericcompiletimeproc.nim
@@ -0,0 +1,36 @@
+block: # issue #10753
+  proc foo(x: int): int {.compileTime.} = x
+  const a = foo(123)
+  doAssert foo(123) == a
+
+  proc bar[T](x: T): T {.compileTime.} = x
+  const b = bar(123)
+  doAssert bar(123) == b
+  const c = bar("abc")
+  doAssert bar("abc") == c
+
+block: # issue #22021
+  proc foo(x: static int): int {.compileTime.} = x + 1
+  doAssert foo(123) == 124
+
+block: # issue #19365
+  proc f[T](x: static T): T {.compileTime.} = x + x
+  doAssert f(123) == 246
+  doAssert f(1.0) == 2.0
+
+block:
+  # don't fold compile time procs in typeof
+  proc fail[T](x: T): T {.compileTime.} =
+    doAssert false
+    x
+  doAssert typeof(fail(123)) is typeof(123)
+  proc p(x: int): int = x
+
+  type Foo = typeof(p(fail(123)))
+
+block: # issue #24150, related regression
+  proc w(T: type): T {.compileTime.} = default(ptr T)[]
+  template y(v: auto): auto = typeof(v) is int
+  discard compiles(y(w int))
+  proc s(): int {.compileTime.} = discard
+  discard s()
diff --git a/tests/vm/tgloballetfrommacro.nim b/tests/vm/tgloballetfrommacro.nim
new file mode 100644
index 000000000..4be09b56e
--- /dev/null
+++ b/tests/vm/tgloballetfrommacro.nim
@@ -0,0 +1,13 @@
+discard """
+errormsg: "cannot evaluate at compile time: BUILTIN_NAMES"
+line: 11
+"""
+
+import sets
+
+let BUILTIN_NAMES = toHashSet(["int8", "int16", "int32", "int64"])
+
+macro test*(): bool =
+  echo "int64" notin BUILTIN_NAMES
+
+echo test()
diff --git a/tests/vm/tgorge.nim b/tests/vm/tgorge.nim
index 694754f41..1f77d2c95 100644
--- a/tests/vm/tgorge.nim
+++ b/tests/vm/tgorge.nim
@@ -1,8 +1,17 @@
+discard """
+disabled: "windows"
+"""
+
+# If your os is windows and this test fails for you locally, please
+# check what is going wrong.
+
 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/tgorge.sh b/tests/vm/tgorge.sh
index ba47afeae..ba47afeae 100644..100755
--- a/tests/vm/tgorge.sh
+++ b/tests/vm/tgorge.sh
diff --git a/tests/vm/tgorgeex.sh b/tests/vm/tgorgeex.sh
index 36ba0a02f..36ba0a02f 100644..100755
--- a/tests/vm/tgorgeex.sh
+++ b/tests/vm/tgorgeex.sh
diff --git a/tests/vm/tinheritance.nim b/tests/vm/tinheritance.nim
index d465e22b9..2a98ed923 100644
--- a/tests/vm/tinheritance.nim
+++ b/tests/vm/tinheritance.nim
@@ -1,6 +1,7 @@
 discard """
-  msg: '''Hello fred , managed by sally
-Hello sally , managed by bob'''
+  nimout: '''Hello fred, managed by sally
+Hello sally, managed by bob
+0'''
 """
 # bug #3973
 
@@ -10,20 +11,84 @@ type
     ecCode2
 
   Person* = object of RootObj
-    name* : string
+    name*: string
     last_name*: string
 
   Employee* = object of Person
-    empl_code* : EmployeeCode
-    mgr_name* : string
+    empl_code*: EmployeeCode
+    mgr_name*: string
 
 proc test() =
   var
     empl1 = Employee(name: "fred", last_name: "smith", mgr_name: "sally", empl_code: ecCode1)
     empl2 = Employee(name: "sally", last_name: "jones", mgr_name: "bob", empl_code: ecCode2)
 
-  echo "Hello ", empl1.name, " , managed by ", empl1.mgr_name
-  echo "Hello ", empl2.name, " , managed by ", empl2.mgr_name
+  echo "Hello ", empl1.name, ", managed by ", empl1.mgr_name
+  echo "Hello ", empl2.name, ", managed by ", empl2.mgr_name
 
 static:
   test()
+
+#----------------------------------------------
+# Bugs #9701 and #9702
+type
+  MyKind = enum
+    kA, kB, kC
+
+  Base = ref object of RootObj
+    x: string
+
+  A = ref object of Base
+    a: string
+
+  B = ref object of Base
+    b: string
+
+  C = ref object of B
+    c: string
+
+template check_templ(n: Base, k: MyKind) =
+  if k == kA: doAssert(n of A) else: doAssert(not (n of A))
+  if k in {kB, kC}: doAssert(n of B) else: doAssert(not (n of B))
+  if k == kC: doAssert(n of C) else: doAssert(not (n of C))
+  doAssert(n of Base)
+
+proc check_proc(n: Base, k: MyKind) =
+  if k == kA: doAssert(n of A) else: doAssert(not (n of A))
+  if k in {kB, kC}: doAssert(n of B) else: doAssert(not (n of B))
+  if k == kC: doAssert(n of C) else: doAssert(not (n of C))
+  doAssert(n of Base)
+
+static:
+  let aa = new(A)
+  check_templ(aa, kA)
+  check_proc(aa, kA)
+  let bb = new(B)
+  check_templ(bb, kB)
+  check_proc(bb, kB)
+  let cc = new(C)
+  check_templ(cc, kC)
+  check_proc(cc, kC)
+
+let aa = new(A)
+check_templ(aa, kA)
+check_proc(aa, kA)
+let bb = new(B)
+check_templ(bb, kB)
+check_proc(bb, kB)
+let cc = new(C)
+check_templ(cc, kC)
+check_proc(cc, kC)
+
+type
+  BBar = object of RootObj
+    bbarField: set[char]
+    xbarField: string
+    d, e: int
+  FooBar = object of BBar
+    a: int
+    b: string
+
+static:
+  var fb: FooBar
+  echo fb.a
diff --git a/tests/vm/tissues.nim b/tests/vm/tissues.nim
new file mode 100644
index 000000000..f0ae6c296
--- /dev/null
+++ b/tests/vm/tissues.nim
@@ -0,0 +1,76 @@
+import macros
+
+block t9043: # bug #9043
+  proc foo[N: static[int]](dims: array[N, int]): string =
+    const N1 = N
+    const N2 = dims.len
+    const ret = $(N, dims.len, N1, N2)
+    static: doAssert ret == $(N, dims.len, N1, N2)
+    ret
+
+  doAssert foo([1, 2]) == "(2, 2, 2, 2)"
+
+block t4952:
+  proc doCheck(tree: NimNode) =
+    let res: tuple[n: NimNode] = (n: tree)
+    assert: tree.kind == res.n.kind
+    for sub in tree:
+      doCheck(sub)
+
+  macro id(body: untyped): untyped =
+    doCheck(body)
+
+  id(foo((i: int)))
+
+  static:
+    let tree = newTree(nnkExprColonExpr)
+    let t = (n: tree)
+    doAssert: t.n.kind == tree.kind
+
+
+# bug #19909
+type
+  SinglyLinkedList[T] = ref object
+  SinglyLinkedListObj[T] = ref object
+
+
+proc addMoved[T](a, b: var SinglyLinkedList[T]) =
+  if a.addr != b.addr: discard
+
+proc addMoved[T](a, b: var SinglyLinkedListObj[T]) =
+  if a.addr != b.addr: discard
+
+proc main =
+  var a: SinglyLinkedList[int]; new a
+  var b: SinglyLinkedList[int]; new b
+  a.addMoved b
+
+  var a0: SinglyLinkedListObj[int]
+  var b0: SinglyLinkedListObj[int]
+  a0.addMoved b0
+
+static: main()
+
+
+# bug #18641
+
+type A = object
+  ha1: int
+static:
+  var a = A()
+  var a2 = a.addr
+  a2.ha1 = 11
+  doAssert a2.ha1 == 11
+  a.ha1 = 12
+  doAssert a.ha1 == 12
+  doAssert a2.ha1 == 12 # ok
+static:
+  proc fn() =
+    var a = A()
+    var a2 = a.addr
+    a2.ha1 = 11
+    doAssert a2.ha1 == 11
+    a.ha1 = 12
+    doAssert a.ha1 == 12
+    doAssert a2.ha1 == 12 # fails
+  fn()
diff --git a/tests/vm/tmanyregs.nim b/tests/vm/tmanyregs.nim
new file mode 100644
index 000000000..711c69285
--- /dev/null
+++ b/tests/vm/tmanyregs.nim
@@ -0,0 +1,16 @@
+import macros
+
+# Generate a proc with more then 255 registers. Should not generate an error at
+# compile time
+
+static:
+  macro mkFoo() =
+    let ss = newStmtList()
+    for i in 1..256:
+      ss.add parseStmt "var x" & $i & " = " & $i
+      ss.add parseStmt "inc x" & $i
+    quote do:
+      proc foo() =
+        `ss`
+  mkFoo()
+  foo()
diff --git a/tests/vm/tmaxloopiterations.nim b/tests/vm/tmaxloopiterations.nim
new file mode 100644
index 000000000..c256df518
--- /dev/null
+++ b/tests/vm/tmaxloopiterations.nim
@@ -0,0 +1,15 @@
+discard """
+errormsg: "interpretation requires too many iterations; if you are sure this is not a bug in your code"
+"""
+
+# issue #9829
+
+macro foo(): untyped  =
+  let lines = ["123", "5423"]
+  var idx = 0
+  while idx < lines.len():
+    if lines[idx].len() < 1:
+      inc(idx)
+      continue
+
+foo()
diff --git a/tests/vm/tmisc_vm.nim b/tests/vm/tmisc_vm.nim
new file mode 100644
index 000000000..1ad830b5f
--- /dev/null
+++ b/tests/vm/tmisc_vm.nim
@@ -0,0 +1,459 @@
+discard """
+  targets: "c js"
+  output: '''
+[127, 127, 0, 255][127, 127, 0, 255]
+(data: 1)
+(2, 1)
+(2, 1)
+(2, 1)
+(f0: 5)
+'''
+
+  nimout: '''caught Exception
+main:begin
+main:end
+@[{0}]
+(width: 0, height: 0, path: "")
+@[(width: 0, height: 0, path: ""), (width: 0, height: 0, path: "")]
+Done!
+foo4
+foo4
+foo4
+(a: 0, b: 0)
+(a: 0, b: 0)
+(a: 0, b: 0)
+z1 m: (lo: 12)
+z2 a: (lo: 3)
+x1 a: (lo: 3)
+x2 a: (lo: 6)
+x3 a: (lo: 0)
+z3 a: (lo: 3)
+x1 a: (lo: 3)
+x2 a: (lo: 6)
+x3 a: (lo: 0)
+(2, 1)
+(2, 1)
+(2, 1)
+(f0: 5)
+'''
+"""
+import std/sets
+
+#bug #1009
+type
+  TAggRgba8* = array[4, byte]
+
+template R*(self: TAggRgba8): byte = self[0]
+template G*(self: TAggRgba8): byte = self[1]
+template B*(self: TAggRgba8): byte = self[2]
+template A*(self: TAggRgba8): byte = self[3]
+
+template `R=`*(self: TAggRgba8, val: byte) =
+  self[0] = val
+template `G=`*(self: TAggRgba8, val: byte) =
+  self[1] = val
+template `B=`*(self: TAggRgba8, val: byte) =
+  self[2] = val
+template `A=`*(self: TAggRgba8, val: byte) =
+  self[3] = val
+
+proc ABGR*(val: int | int64): TAggRgba8 =
+  var V = val
+  result.R = byte(V and 0xFF)
+  V = V shr 8
+  result.G = byte(V and 0xFF)
+  V = V shr 8
+  result.B = byte(V and 0xFF)
+  result.A = byte((V shr 8) and 0xFF)
+
+const
+  c1 = ABGR(0xFF007F7F)
+echo ABGR(0xFF007F7F).repr, c1.repr
+
+
+# bug 8740
+
+static:
+  try:
+    raise newException(ValueError, "foo")
+  except Exception:
+    echo "caught Exception"
+  except Defect:
+    echo "caught Defect"
+  except ValueError:
+    echo "caught ValueError"
+
+# bug #10538
+
+block:
+  proc fun1(): seq[int] =
+    try:
+      try:
+        result.add(1)
+        return
+      except:
+        result.add(-1)
+      finally:
+        result.add(2)
+    finally:
+      result.add(3)
+    result.add(4)
+
+  let x1 = fun1()
+  const x2 = fun1()
+  doAssert(x1 == x2)
+
+# bug #11610
+proc simpleTryFinally()=
+  try:
+    echo "main:begin"
+  finally:
+    echo "main:end"
+
+static: simpleTryFinally()
+
+# bug #10981
+
+proc main =
+  for i in 0..<15:
+    var someSets = @[initHashSet[int]()]
+    someSets[^1].incl(0) # <-- segfaults
+    if i == 0:
+      echo someSets
+
+static:
+  main()
+
+# bug #7261
+const file = """
+sprites.png
+size: 1024,1024
+format: RGBA8888
+filter: Linear,Linear
+repeat: none
+char/slide_down
+  rotate: false
+  xy: 730, 810
+  size: 204, 116
+  orig: 204, 116
+  offset: 0, 0
+  index: -1
+"""
+
+type
+  AtlasPage = object
+    width, height: int
+    path: string
+
+  CtsStream = object
+    data: string
+    pos: int
+
+proc atEnd(stream: CtsStream): bool =
+  stream.pos >= stream.data.len
+
+proc readChar(stream: var CtsStream): char =
+  if stream.atEnd:
+    result = '\0'
+  else:
+    result = stream.data[stream.pos]
+    inc stream.pos
+
+proc readLine(s: var CtsStream, line: var string): bool =
+  # This is pretty much copied from the standard library:
+  line.setLen(0)
+  while true:
+    var c = readChar(s)
+    if c == '\c':
+      c = readChar(s)
+      break
+    elif c == '\L': break
+    elif c == '\0':
+      if line.len > 0: break
+      else: return false
+    line.add(c)
+  result = true
+
+proc peekLine(s: var CtsStream, line: var string): bool =
+  let oldPos = s.pos
+  result = s.readLine(line)
+  s.pos = oldPos
+
+proc initCtsStream(data: string): CtsStream =
+  CtsStream(
+    pos: 0,
+    data: data
+  )
+
+# ********************
+# Interesting stuff happens here:
+# ********************
+
+proc parseAtlas(stream: var CtsStream) =
+  var pages = @[AtlasPage(), AtlasPage()]
+  var line = ""
+
+  block:
+    let page = addr pages[^1]
+    discard stream.peekLine(line)
+    discard stream.peekLine(line)
+    echo page[]
+  echo pages
+
+static:
+  var stream = initCtsStream(file)
+  parseAtlas(stream)
+  echo "Done!"
+
+
+# bug #12244
+
+type
+  Apple = object
+    data: int
+
+func what(x: var Apple) =
+  x = Apple(data: 1)
+
+func oh_no(): Apple =
+  what(result)
+
+const
+  vmCrash = oh_no()
+
+debugEcho vmCrash
+
+
+# bug #12310
+
+proc someTransform(s: var array[8, uint64]) =
+  var s1 = 5982491417506315008'u64
+  s[1] += s1
+
+static:
+  var state: array[8, uint64]
+  state[1] = 7105036623409894663'u64
+  someTransform(state)
+
+  doAssert state[1] == 13087528040916209671'u64
+
+import macros
+# bug #12670
+
+macro fooImpl(arg: untyped) =
+  result = quote do:
+    `arg`
+
+proc foo(): string {.compileTime.} =
+  fooImpl:
+    result = "foo"
+    result.addInt 4
+
+static:
+  echo foo()
+  echo foo()
+  echo foo()
+
+# bug #12488
+type
+  MyObject = object
+    a,b: int
+  MyObjectRef = ref MyObject
+
+static:
+  let x1 = new(MyObject)
+  echo x1[]
+  let x2 = new(MyObjectRef)
+  echo x2[]
+  let x3 = new(ref MyObject) # cannot generate VM code for ref MyObject
+  echo x3[]
+
+# bug #19464
+type
+  Wrapper = object
+    inner: int
+
+proc assign(r: var Wrapper, a: Wrapper) =
+  r = a
+
+proc myEcho(a: Wrapper) =
+  var tmp = a
+  assign(tmp, Wrapper(inner: 0)) # this shouldn't modify `a`
+  doAssert a.inner == 1
+
+static:
+  var result: Wrapper
+  assign(result, Wrapper(inner: 1))
+  myEcho(result)
+
+when true:
+  # bug #15974
+  type Foo = object
+    f0: int
+
+  proc fn(a: var Foo) =
+    var s: Foo
+    a = Foo(f0: 2)
+    s = a
+    doAssert s.f0 == 2
+    a = Foo(f0: 3)
+    doAssert s.f0 == 2
+
+  proc test2()=
+    var a = Foo(f0: 1)
+    fn(a)
+
+  static: test2()
+  test2()
+
+# bug #12551
+type
+  StUint = object
+    lo: uint64
+
+func `+=`(x: var Stuint, y: Stuint) =
+  x.lo += y.lo
+
+func `-`(x, y: Stuint): Stuint =
+  result.lo = x.lo - y.lo
+
+func `+`(x, y: Stuint): Stuint =
+  result.lo = x.lo + y.lo
+
+func `-=`(x: var Stuint, y: Stuint) =
+  x = x - y
+
+func `<`(x, y: Stuint): bool=
+  x.lo < y.lo
+
+func `==`(x, y: Stuint): bool =
+  x.lo == y.lo
+
+func `<=`(x, y: Stuint): bool =
+  x.lo <= y.lo
+
+proc div3n2n(r: var Stuint, b: Stuint) =
+  var d: Stuint
+  r = d
+  r += b
+
+func div2n1n(r: var Stuint, b: Stuint) =
+  div3n2n(r, b)
+
+func divmodBZ(x, y: Stuint, r: var Stuint)=
+  div2n1n(r, y)
+  r.lo = 3
+
+func `mod`(x, y: Stuint): Stuint =
+  divmodBZ(x, y, result)
+
+func doublemod_internal(a, m: Stuint): Stuint =
+  result = a
+  if a >= m - a:
+    result -= m
+  result += a
+
+func mulmod_internal(a, b, m: Stuint): Stuint =
+  var (a, b) = (a, b)
+  swap(a, b)
+  debugEcho "x1 a: ", a
+  a = doublemod_internal(a, m)
+  debugEcho "x2 a: ", a
+  a = doublemod_internal(a, m)
+  debugEcho "x3 a: ", a
+
+func powmod_internal(a, m: Stuint): Stuint =
+  var a = a
+  debugEcho "z1 m: ", m
+  debugEcho "z2 a: ", a
+  result = mulmod_internal(result, a, m)
+  debugEcho "z3 a: ", a
+  a = mulmod_internal(a, a, m)
+
+func powmod*(a, m: Stuint) =
+  discard powmod_internal(a mod m, m)
+
+static:
+  var x = Stuint(lo: high(uint64))
+  var y = Stuint(lo: 12)
+
+  powmod(x, y)
+
+# bug #16780
+when true:
+  template swap*[T](a, b: var T) =
+    var a2 = addr(a)
+    var b2 = addr(b)
+    var aOld = a2[]
+    a2[] = b2[]
+    b2[] = aOld
+
+  proc rather =
+    block:
+      var a = 1
+      var b = 2
+      swap(a, b)
+      echo (a,b)
+
+    block:
+      type Foo = ref object
+        x: int
+      var a = Foo(x:1)
+      var b = Foo(x:2)
+      swap(a, b)
+      echo (a.x, b.x)
+
+    block:
+      type Foo = object
+        x: int
+      var a = Foo(x:1)
+      var b = Foo(x:2)
+      swap(a, b)
+      echo (a.x,b.x)
+
+  static: rather()
+  rather()
+
+# bug #16020
+when true:
+  block:
+    type Foo = object
+      f0: int
+    proc main=
+      var f = Foo(f0: 3)
+      var f2 = f.addr
+      f2[].f0 += 1
+      f2.f0 += 1
+      echo f
+    static: main()
+    main()
+
+import tables, strutils
+
+# bug #14553
+const PpcPatterns = @[("aaaa", "bbbb"), ("aaaaa", "bbbbb"), ("aaaaaa", "bbbbbb"), ("aaaaaaa", "bbbbbbb"), ("aaaaaaaa", "bbbbb")]
+
+static:
+    var
+        needSecondIdentifier = initTable[uint32, seq[(string, string)]]()
+
+    for (name, pattern) in PpcPatterns:
+        let
+            firstPart = 0'u32
+            lastPart = "test"
+
+        needSecondIdentifier.mgetOrPut(firstPart, @[]).add((name, pattern))
+
+    doAssert needSecondIdentifier[0] == @[("aaaa", "bbbb"), ("aaaaa", "bbbbb"), ("aaaaaa", "bbbbbb"), ("aaaaaaa", "bbbbbbb"), ("aaaaaaaa", "bbbbb")]
+
+# bug #17864
+macro transform*(fn: typed) =
+  quote do:
+    `fn`
+
+var map: Table[string, HashSet[string]]
+proc publish*(): void {.transform.} =
+  map["k"] = init_hash_set[string]()
+  map["k"].incl "d"
+
+publish()
diff --git a/tests/vm/tmitems.nim b/tests/vm/tmitems.nim
deleted file mode 100644
index a0e64d6aa..000000000
--- a/tests/vm/tmitems.nim
+++ /dev/null
@@ -1,45 +0,0 @@
-discard """
-  msg: '''13'''
-  output: '''3
-3
-3'''
-"""
-# bug #3731
-var list {.compileTime.} = newSeq[int]()
-
-macro calc*(): typed =
-  list.add(1)
-  for c in list.mitems:
-    c = 13
-
-  for c in list:
-    echo c
-
-calc()
-
-# bug #3859
-import macros
-macro m: typed =
-  var s = newseq[NimNode](3)
-  # var s: array[3,NimNode]                 # not working either
-  for i in 0..<s.len: s[i] = newLit(3)    # works
-  #for x in s.mitems: x = newLit(3)
-  result = newStmtList()
-  for i in s:
-    result.add newCall(bindsym"echo", i)
-
-m()
-
-# bug 4741 & 5013
-proc test() =
-  var s = [("baz", 42), ("bath", 42)]
-  for i in s.mitems:
-    i[1] = 3
-  doAssert(s == [("baz", 3), ("bath", 3)])
-
-static:
-  test()
-  var s = [("baz", 42), ("bath", 42)]
-  for i in s.mitems:
-    i[1] = 3
-  doAssert(s == [("baz", 3), ("bath", 3)])
diff --git a/tests/vm/tmitems_vm.nim b/tests/vm/tmitems_vm.nim
new file mode 100644
index 000000000..787d52cc6
--- /dev/null
+++ b/tests/vm/tmitems_vm.nim
@@ -0,0 +1,45 @@
+discard """
+  nimout: '''13'''
+  output: '''3
+3
+3'''
+"""
+# bug #3731
+var list {.compileTime.} = newSeq[int]()
+
+macro calc*(): void =
+  list.add(1)
+  for c in list.mitems:
+    c = 13
+
+  for c in list:
+    echo c
+
+calc()
+
+# bug #3859
+import macros
+macro m: void =
+  var s = newseq[NimNode](3)
+  # var s: array[3,NimNode]                 # not working either
+  for i in 0..<s.len: s[i] = newLit(3)    # works
+  #for x in s.mitems: x = newLit(3)
+  result = newStmtList()
+  for i in s:
+    result.add newCall(bindsym"echo", i)
+
+m()
+
+# bug 4741 & 5013
+proc test() =
+  var s = [("baz", 42), ("bath", 42)]
+  for i in s.mitems:
+    i[1] = 3
+  doAssert(s == [("baz", 3), ("bath", 3)])
+
+static:
+  test()
+  var s = [("baz", 42), ("bath", 42)]
+  for i in s.mitems:
+    i[1] = 3
+  doAssert(s == [("baz", 3), ("bath", 3)])
diff --git a/tests/vm/tnewseqofcap.nim b/tests/vm/tnewseqofcap.nim
new file mode 100644
index 000000000..a7dc07aa6
--- /dev/null
+++ b/tests/vm/tnewseqofcap.nim
@@ -0,0 +1,14 @@
+const
+  foo = @["aaa", "bbb", "ccc"]
+
+proc myTuple: tuple[n: int, bar: seq[string]] =
+  result.n = 42
+  result.bar = newSeqOfCap[string](foo.len)
+  for f in foo:
+    result.bar.add(f)
+
+# It works if you change the below `const` to `let`
+const
+  (n, bar) = myTuple()
+
+doAssert bar == @["aaa", "bbb", "ccc"]
\ No newline at end of file
diff --git a/tests/vm/tnilclosurecall.nim b/tests/vm/tnilclosurecall.nim
new file mode 100644
index 000000000..449865b9c
--- /dev/null
+++ b/tests/vm/tnilclosurecall.nim
@@ -0,0 +1,8 @@
+discard """
+  errormsg: "attempt to call nil closure"
+  line: 8
+"""
+
+static:
+  let x: proc () = nil
+  x()
diff --git a/tests/vm/tnilclosurecallstacktrace.nim b/tests/vm/tnilclosurecallstacktrace.nim
new file mode 100644
index 000000000..879060e8e
--- /dev/null
+++ b/tests/vm/tnilclosurecallstacktrace.nim
@@ -0,0 +1,23 @@
+discard """
+  action: reject
+  nimout: '''
+stack trace: (most recent call last)
+tnilclosurecallstacktrace.nim(23, 6) tnilclosurecallstacktrace
+tnilclosurecallstacktrace.nim(20, 6) baz
+tnilclosurecallstacktrace.nim(17, 6) bar
+tnilclosurecallstacktrace.nim(14, 4) foo
+tnilclosurecallstacktrace.nim(14, 4) Error: attempt to call nil closure
+'''
+"""
+
+proc foo(x: proc ()) =
+  x()
+
+proc bar(x: proc ()) =
+  foo(x)
+
+proc baz(x: proc ()) =
+  bar(x)
+
+static:
+  baz(nil)
diff --git a/tests/vm/tnilref.nim b/tests/vm/tnilref.nim
new file mode 100644
index 000000000..5e27cf0cb
--- /dev/null
+++ b/tests/vm/tnilref.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "attempt to access a nil address"
+"""
+
+static:
+    var s: ref int
+    s[] = 1
\ No newline at end of file
diff --git a/tests/vm/tnimnode.nim b/tests/vm/tnimnode.nim
index 188bac9bf..f1c4d5e5a 100644
--- a/tests/vm/tnimnode.nim
+++ b/tests/vm/tnimnode.nim
@@ -4,19 +4,17 @@ proc assertEq(arg0,arg1: string): void =
   if arg0 != arg1:
     raiseAssert("strings not equal:\n" & arg0 & "\n" & arg1)
 
-static:
-  # a simple assignment of stmtList to another variable
-  var node: NimNode
-  # an assignment of stmtList into an array
-  var nodeArray: array[1, NimNode]
-  # an assignment of stmtList into a seq
-  var nodeSeq = newSeq[NimNode](2)
-
+# a simple assignment of stmtList to another variable
+var node {.compileTime.}: NimNode
+# an assignment of stmtList into an array
+var nodeArray {.compileTime.}: array[1, NimNode]
+# an assignment of stmtList into a seq
+var nodeSeq {.compileTime.} = newSeq[NimNode](2)
 
 proc checkNode(arg: NimNode; name: string): void {. compileTime .} =
   echo "checking ", name
 
-  assertEq arg.lispRepr , "StmtList(DiscardStmt(Empty()))"
+  assertEq arg.lispRepr, """(StmtList (DiscardStmt (Empty)))"""
 
   node = arg
   nodeArray = [arg]
@@ -26,19 +24,19 @@ proc checkNode(arg: NimNode; name: string): void {. compileTime .} =
   seqAppend.add(arg)   # bit this creates a copy
   arg.add newCall(ident"echo", newLit("Hello World"))
 
-  assertEq arg.lispRepr          , """StmtList(DiscardStmt(Empty()), Call(Ident("echo"), StrLit("Hello World")))"""
-  assertEq node.lispRepr         , """StmtList(DiscardStmt(Empty()), Call(Ident("echo"), StrLit("Hello World")))"""
-  assertEq nodeArray[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident("echo"), StrLit("Hello World")))"""
-  assertEq nodeSeq[0].lispRepr   , """StmtList(DiscardStmt(Empty()), Call(Ident("echo"), StrLit("Hello World")))"""
-  assertEq seqAppend[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident("echo"), StrLit("Hello World")))"""
-  assertEq seqAppend[1].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident("echo"), StrLit("Hello World")))"""
+  assertEq arg.lispRepr,          """(StmtList (DiscardStmt (Empty)) (Call (Ident "echo") (StrLit "Hello World")))"""
+  assertEq node.lispRepr,         """(StmtList (DiscardStmt (Empty)) (Call (Ident "echo") (StrLit "Hello World")))"""
+  assertEq nodeArray[0].lispRepr, """(StmtList (DiscardStmt (Empty)) (Call (Ident "echo") (StrLit "Hello World")))"""
+  assertEq nodeSeq[0].lispRepr,   """(StmtList (DiscardStmt (Empty)) (Call (Ident "echo") (StrLit "Hello World")))"""
+  assertEq seqAppend[0].lispRepr, """(StmtList (DiscardStmt (Empty)) (Call (Ident "echo") (StrLit "Hello World")))"""
+  assertEq seqAppend[1].lispRepr, """(StmtList (DiscardStmt (Empty)) (Call (Ident "echo") (StrLit "Hello World")))"""
 
   echo "OK"
 
-static:
-  # the root node that is used to generate the Ast
-  var stmtList: NimNode
+# the root node that is used to generate the Ast
+var stmtList {.compileTime.}: NimNode
 
+static:
   stmtList = newStmtList(nnkDiscardStmt.newTree(newEmptyNode()))
 
   checkNode(stmtList, "direct construction")
diff --git a/tests/vm/tnocompiletimefunc.nim b/tests/vm/tnocompiletimefunc.nim
new file mode 100644
index 000000000..a95648c0f
--- /dev/null
+++ b/tests/vm/tnocompiletimefunc.nim
@@ -0,0 +1,14 @@
+discard """
+  errormsg: "request to generate code for .compileTime proc: foo"
+"""
+
+# ensure compileTime funcs can't be called from runtime
+
+func foo(a: int): int {.compileTime.} =
+  a * a
+
+proc doAThing(): int =
+  for i in 0..2:
+    result += foo(i)
+
+echo doAThing()
diff --git a/tests/vm/tnocompiletimefunclambda.nim b/tests/vm/tnocompiletimefunclambda.nim
new file mode 100644
index 000000000..d134eea40
--- /dev/null
+++ b/tests/vm/tnocompiletimefunclambda.nim
@@ -0,0 +1,6 @@
+discard """
+  errormsg: "request to generate code for .compileTime proc: :anonymous"
+"""
+
+let a = func(a: varargs[int]) {.compileTime, closure.} =
+  discard a[0]
\ No newline at end of file
diff --git a/tests/vm/tnoreturn.nim b/tests/vm/tnoreturn.nim
new file mode 100644
index 000000000..d4f8601a9
--- /dev/null
+++ b/tests/vm/tnoreturn.nim
@@ -0,0 +1,32 @@
+block: # issue #22216
+  type
+    Result[T, E] = object
+      case oVal: bool
+      of false:
+        eVal: E
+      of true:
+        vVal: T
+
+  func raiseResultDefect(m: string) {.noreturn, noinline.} =
+    raise (ref Defect)(msg: m)
+
+  template withAssertOk(self: Result, body: untyped): untyped =
+    case self.oVal
+    of false:
+      raiseResultDefect("Trying to access value with err Result")    
+    else:
+      body
+
+  func value[T, E](self: Result[T, E]): T {.inline.} =    
+    withAssertOk(self):  
+      self.vVal
+      
+  const
+    x = Result[int, string](oVal: true, vVal: 123)
+    z = x.value()
+    
+  let
+    xx = Result[int, string](oVal: true, vVal: 123)
+    zz = x.value()
+  
+  doAssert z == zz
diff --git a/tests/vm/topenarrays.nim b/tests/vm/topenarrays.nim
new file mode 100644
index 000000000..375d2523d
--- /dev/null
+++ b/tests/vm/topenarrays.nim
@@ -0,0 +1,89 @@
+proc mutate(a: var openarray[int]) =
+  var i = 0
+  for x in a.mitems:
+    x = i
+    inc i
+
+proc mutate(a: var openarray[char]) =
+  var i = 1
+  for ch in a.mitems:
+    ch = 'a'
+
+
+static:
+  var a = [10, 20, 30]
+  assert a.toOpenArray(1, 2).len == 2
+
+  mutate(a)
+  assert a.toOpenArray(0, 2) == [0, 1, 2]
+  assert a.toOpenArray(0, 0) == [0]
+  assert a.toOpenArray(1, 2) == [1, 2]
+  assert "Hello".toOpenArray(1, 4) == "ello"
+  var str = "Hello"
+  str.toOpenArray(2, 4).mutate()
+  assert str.toOpenArray(0, 4).len == 5
+  assert str.toOpenArray(0, 0).len == 1
+  assert str.toOpenArray(0, 0).high == 0
+  assert str == "Heaaa"
+  assert str.toOpenArray(0, 4) == "Heaaa"
+
+  var arr: array[3..4, int] = [1, 2]
+  assert arr.toOpenArray(3, 4) == [1, 2]
+  assert arr.toOpenArray(3, 4).len == 2
+  assert arr.toOpenArray(3, 3).high == 0
+
+  assert arr.toOpenArray(3, 4).toOpenArray(0, 0) == [1]
+
+
+proc doThing(s: static openArray[int]) = discard
+
+doThing([10, 20, 30].toOpenArray(0, 0))
+
+# bug #19969
+proc f(): array[1, byte] =
+  var a: array[1, byte]
+  result[0..0] = a.toOpenArray(0, 0)
+
+doAssert static(f()) == [byte(0)]
+
+
+# bug #15952
+proc main1[T](a: openArray[T]) = discard
+proc main2[T](a: var openArray[T]) = discard
+
+proc main =
+  var a = [1,2,3,4,5]
+  main1(a.toOpenArray(1,3))
+  main2(a.toOpenArray(1,3))
+static: main()
+main()
+
+# bug #16306
+{.experimental: "views".}
+proc test(x: openArray[int]): tuple[id: int] =
+  let y: openArray[int] = toOpenArray(x, 0, 2)
+  result = (y[0],)
+template fn=
+  doAssert test([0,1,2,3,4,5]).id == 0
+fn() # ok
+static: fn()
+
+
+block: # bug #22095
+  type
+    StUint = object
+      limbs: array[4, uint64]
+
+  func shlAddMod(a: var openArray[uint64]) =  
+    a[0] = 10
+
+  func divRem(r: var openArray[uint64]) =
+    shlAddMod(r.toOpenArray(0, 3))
+
+  func fn(): StUint =
+    divRem(result.limbs)
+
+  const
+    z = fn()
+
+  doAssert z.limbs[0] == 10
diff --git a/tests/vm/toverflowopcaddimmint.nim b/tests/vm/toverflowopcaddimmint.nim
index c36b9ed9b..4ff614e5b 100644
--- a/tests/vm/toverflowopcaddimmint.nim
+++ b/tests/vm/toverflowopcaddimmint.nim
@@ -7,5 +7,5 @@ static:
     var
       x = int64.high
     discard x + 1
-    assert false
+    doAssert false
   p()
diff --git a/tests/vm/toverflowopcaddint.nim b/tests/vm/toverflowopcaddint.nim
index 6d96afc78..d494245b1 100644
--- a/tests/vm/toverflowopcaddint.nim
+++ b/tests/vm/toverflowopcaddint.nim
@@ -8,5 +8,5 @@ static:
       x = int64.high
       y = 1
     discard x + y
-    assert false
+    doAssert false
   p()
diff --git a/tests/vm/toverflowopcmulint.nim b/tests/vm/toverflowopcmulint.nim
index 81b3234ba..936eea6c2 100644
--- a/tests/vm/toverflowopcmulint.nim
+++ b/tests/vm/toverflowopcmulint.nim
@@ -5,7 +5,7 @@ discard """
 static:
   proc p =
     var
-      x = 1 shl 62
+      x = 1'i64 shl 62
     discard x * 2
-    assert false
+    doAssert false
   p()
diff --git a/tests/vm/toverflowopcsubimmint.nim b/tests/vm/toverflowopcsubimmint.nim
index 09d6f745b..08356590c 100644
--- a/tests/vm/toverflowopcsubimmint.nim
+++ b/tests/vm/toverflowopcsubimmint.nim
@@ -6,5 +6,5 @@ static:
   proc p =
     var x = int64.low
     discard x - 1
-    assert false
+    doAssert false
   p()
diff --git a/tests/vm/toverflowopcsubint.nim b/tests/vm/toverflowopcsubint.nim
index 8d114f200..74e34c6a4 100644
--- a/tests/vm/toverflowopcsubint.nim
+++ b/tests/vm/toverflowopcsubint.nim
@@ -8,5 +8,5 @@ static:
       x = int64.low
       y = 1
     discard x - y
-    assert false
+    doAssert false
   p()
diff --git a/tests/vm/tquadplus.nim b/tests/vm/tquadplus.nim
index 552e8fef7..acf20cd96 100644
--- a/tests/vm/tquadplus.nim
+++ b/tests/vm/tquadplus.nim
@@ -1,8 +1,5 @@
 # bug #1023
 
-discard """
-  output: "1 == 1"
-"""
 
 type Quadruple = tuple[a, b, c, d: int]
 
@@ -14,4 +11,4 @@ const
   B = (a: 0, b: -2, c: 1, d: 0)
   C = A + B
 
-echo C.d, " == ", (A+B).d
+doAssert $C.d & " == " & $(A+B).d == "1 == 1"
diff --git a/tests/vm/tref.nim b/tests/vm/tref.nim
index 517a67fb0..e70305225 100644
--- a/tests/vm/tref.nim
+++ b/tests/vm/tref.nim
@@ -9,4 +9,63 @@ static:
 
   b[5] = 'c'
   doAssert a[] == "Hellocworld"
-  doAssert b[] == "Hellocworld"
\ No newline at end of file
+  doAssert b[] == "Hellocworld"
+
+  proc notGlobal() =
+    var
+      a: ref string
+      b: ref string
+    new a
+
+    a[] = "Hello world"
+    b = a
+
+    b[5] = 'c'
+    doAssert a[] == "Hellocworld"
+    doAssert b[] == "Hellocworld"
+  notGlobal()
+
+static: # bug 6081
+  block:
+    type Obj = object
+      field: ref int
+    var i: ref int
+    new(i)
+    var r = Obj(field: i)
+    var rr = r
+    r.field = nil
+    doAssert rr.field != nil
+
+  proc foo() = # Proc to avoid special global logic
+    var s: seq[ref int]
+    var i: ref int
+    new(i)
+    s.add(i)
+    var head = s[0]
+    s[0] = nil
+    doAssert head != nil
+
+  foo()
+
+static:
+
+  block: # global alias
+    var s: ref int
+    new(s)
+    var ss = s
+    s[] = 1
+    doAssert ss[] == 1
+
+static: # bug #8402
+  type R = ref object
+  var empty: R
+  let otherEmpty = empty
+
+block:
+  # fix https://github.com/timotheecour/Nim/issues/88
+  template fun() =
+    var s = @[10,11,12]
+    var a = s[0].addr
+    a[] += 100 # was giving SIGSEGV
+    doAssert a[] == 110
+  static: fun()
diff --git a/tests/vm/treset.nim b/tests/vm/treset.nim
new file mode 100644
index 000000000..32fbc7f04
--- /dev/null
+++ b/tests/vm/treset.nim
@@ -0,0 +1,50 @@
+static:
+  type Obj = object
+    field: int
+  var o = Obj(field: 1)
+  reset(o)
+  doAssert o.field == 0
+
+  var x = 4
+  reset(x)
+  doAssert x == 0
+
+static:
+  type ObjB = object
+    field: int
+  var o = ObjB(field: 1)
+  o = default(ObjB)
+  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()
+
+proc main =
+  var y = [1, 2, 3, 4]
+  y = default(array[4, int])
+  for a in y: doAssert(a == 0)
+
+  var x = 4
+  x = default(int)
+  doAssert x == 0
+
+main()
diff --git a/tests/vm/trgba.nim b/tests/vm/trgba.nim
deleted file mode 100644
index 923ea1b2e..000000000
--- a/tests/vm/trgba.nim
+++ /dev/null
@@ -1,36 +0,0 @@
-discard """
-  output: '''[127, 127, 0, 255]
-[127, 127, 0, 255]
-'''
-"""
-
-#bug #1009
-type
-  TAggRgba8* = array[4, byte]
-
-template R*(self: TAggRgba8): byte = self[0]
-template G*(self: TAggRgba8): byte = self[1]
-template B*(self: TAggRgba8): byte = self[2]
-template A*(self: TAggRgba8): byte = self[3]
-
-template `R=`*(self: TAggRgba8, val: byte) =
-  self[0] = val
-template `G=`*(self: TAggRgba8, val: byte) =
-  self[1] = val
-template `B=`*(self: TAggRgba8, val: byte) =
-  self[2] = val
-template `A=`*(self: TAggRgba8, val: byte) =
-  self[3] = val
-
-proc ABGR*(val: int| int64): TAggRgba8 =
-  var V = val
-  result.R = byte(V and 0xFF)
-  V = V shr 8
-  result.G = byte(V and 0xFF)
-  V = V shr 8
-  result.B = byte(V and 0xFF)
-  result.A = byte((V shr 8) and 0xFF)
-
-const
-  c1 = ABGR(0xFF007F7F)
-echo ABGR(0xFF007F7F).repr, c1.repr
diff --git a/tests/vm/triangle_array.nim b/tests/vm/triangle_array.nim
index 054c66f22..0704239c6 100644
--- a/tests/vm/triangle_array.nim
+++ b/tests/vm/triangle_array.nim
@@ -1,7 +1,3 @@
-discard """
-  output: "56"
-"""
-
 # bug #1781
 
 proc initCombinations: array[11, array[11, int]] =
@@ -14,4 +10,4 @@ proc initCombinations: array[11, array[11, int]] =
   result[6][6 .. 10] =             [52,53,54,55,56]
 
 const combinations = initCombinations()
-echo combinations[6][10]
+doAssert combinations[6][10] == 56
diff --git a/tests/vm/tscriptcompiletime.nims b/tests/vm/tscriptcompiletime.nims
new file mode 100644
index 000000000..daec54bf7
--- /dev/null
+++ b/tests/vm/tscriptcompiletime.nims
@@ -0,0 +1,9 @@
+discard """
+  cmd: "nim e $file"
+"""
+
+import mscriptcompiletime
+
+macro foo =
+  doAssert bar == 2
+foo()
diff --git a/tests/vm/tseq_badinit.nim b/tests/vm/tseq_badinit.nim
index 15889d60e..5fa223c85 100644
--- a/tests/vm/tseq_badinit.nim
+++ b/tests/vm/tseq_badinit.nim
@@ -22,14 +22,14 @@ template test(typename, default: untyped) =
     result = newSeq[typename]()
     result.add(default)
     result.setLen(3)
-    for i in 0 .. <2:
+    for i in 0 ..< 2:
       result[i] = default
 
   const constval = `abc typename`()
   doAssert(constval == `abc typename`())
 
   proc `arr typename`(): array[4, typename] =
-    for i in 0 .. <2:
+    for i in 0 ..< 2:
       result[i] = default
   const constarr = `arr typename`()
   doAssert(constarr == `arr typename`())
diff --git a/tests/vm/tsetlen.nim b/tests/vm/tsetlen.nim
new file mode 100644
index 000000000..9fd30f331
--- /dev/null
+++ b/tests/vm/tsetlen.nim
@@ -0,0 +1,30 @@
+type Foo = object
+  index: int
+
+block:
+  proc fun[T]() =
+    var foo: T
+    var n = 10
+
+    var foos: seq[T]
+    foos.setLen n
+
+    n.inc
+    foos.setLen n
+
+    for i in 0 ..< n:
+      let temp = foos[i]
+      when T is object:
+        doAssert temp.index == 0
+      when T is ref object:
+        doAssert temp == nil
+      doAssert temp == foo
+
+  static:
+    fun[Foo]()
+    fun[int]()
+    fun[float]()
+    fun[string]()
+    fun[(int, string)]()
+    fun[ref Foo]()
+    fun[seq[int]]()
diff --git a/tests/vm/tsignaturehash.nim b/tests/vm/tsignaturehash.nim
new file mode 100644
index 000000000..972ec6fb0
--- /dev/null
+++ b/tests/vm/tsignaturehash.nim
@@ -0,0 +1,20 @@
+# test sym digest is computable at compile time
+
+import macros, algorithm
+import ../../dist/checksums/src/checksums/md5
+
+macro testmacro(s: typed{nkSym}): string =
+  let s = getMD5(signaturehash(s) & " - " & symBodyHash(s))
+  result = newStrLitNode(s)
+
+macro testmacro(s: typed{nkOpenSymChoice|nkClosedSymChoice}): string =
+  var str = ""
+  for sym in s:
+    str &= symBodyHash(sym)
+  result = newStrLitNode(getMD5(str))
+
+# something recursive and/or generic
+discard testmacro(testmacro)
+discard testmacro(`[]`)
+discard testmacro(binarySearch)
+discard testmacro(sort)
diff --git a/tests/vm/tsimpleglobals.nim b/tests/vm/tsimpleglobals.nim
index 27bfdce50..7ab665070 100644
--- a/tests/vm/tsimpleglobals.nim
+++ b/tests/vm/tsimpleglobals.nim
@@ -1,5 +1,5 @@
 discard """
-  msg: "abc xyz bb"
+  nimout: "abc xyz bb"
 """
 
 # bug #2473
diff --git a/tests/vm/tslow_tables.nim b/tests/vm/tslow_tables.nim
new file mode 100644
index 000000000..e40187f18
--- /dev/null
+++ b/tests/vm/tslow_tables.nim
@@ -0,0 +1,30 @@
+discard """
+  timeout: "7"
+  action: "compile"
+  nimout: '''create
+search
+done'''
+"""
+
+# bug #12195
+
+import tables
+
+type Flop = object
+  a: array[128, int]  # <-- compile time is proportional to array size
+
+proc hop(): bool =
+  var v: Table[int, Flop]
+
+  echo "create"
+  for i in 1..1000:
+    v.add i, Flop()
+
+  echo "search"
+  for i in 1..1000:
+    discard contains(v, i)
+
+  echo "done"
+
+const r {.used.} = hop()
+
diff --git a/tests/vm/tslurp.nim b/tests/vm/tslurp.nim
index 027db45d6..d762eb079 100644
--- a/tests/vm/tslurp.nim
+++ b/tests/vm/tslurp.nim
@@ -4,9 +4,9 @@ template getScriptDir(): string =
   parentDir(instantiationInfo(-1, true).filename)
 
 const
-  relRes = slurp"../../readme.txt"
-  absRes = slurp(parentDir(parentDir(getScriptDir())) / "readme.txt")
-
-echo relRes
-echo absRes
+  relRes = slurp"./tslurp.nim"
+  absRes = slurp(getScriptDir() / "tslurp.nim")
 
+doAssert relRes.len > 200
+doAssert absRes.len > 200
+doAssert relRes == absRes
diff --git a/tests/vm/tstaticprintseq.nim b/tests/vm/tstaticprintseq.nim
index 246a0211b..f4aab6b7e 100644
--- a/tests/vm/tstaticprintseq.nim
+++ b/tests/vm/tstaticprintseq.nim
@@ -1,5 +1,5 @@
 discard """
-  msg: '''1
+  nimout: '''1
 2
 3
 1
@@ -24,7 +24,7 @@ bb
 
 const s = @[1,2,3]
 
-macro foo: typed =
+macro foo() =
   for e in s:
     echo e
 
@@ -55,7 +55,7 @@ static:
   var m2: TData = data
   for x in m2.numbers: echo x
 
-macro ff(d: static[TData]): typed =
+macro ff(d: static[TData]) =
   for x in d.letters:
     echo x
 
diff --git a/tests/vm/tstring_openarray.nim b/tests/vm/tstring_openarray.nim
index 1b8a1304c..9318344f8 100644
--- a/tests/vm/tstring_openarray.nim
+++ b/tests/vm/tstring_openarray.nim
@@ -12,16 +12,16 @@ proc set_all[T](s: var openArray[T]; val: T) =
   for i in 0..<s.len:
     s[i] = val
 
-proc test() =
-    var a0 = "hello_world"
-    var a1 = [1,2,3,4,5,6,7,8,9]
-    var a2 = @[1,2,3,4,5,6,7,8,9]
-    a0.set_all('i')
-    a1.set_all(4)
-    a2.set_all(4)
-    doAssert a0 == "iiiiiiiiiii"
-    doAssert a1 == [4,4,4,4,4,4,4,4,4]
-    doAssert a2 == @[4,4,4,4,4,4,4,4,4]
+proc main() =
+  var a0 = "hello_world"
+  var a1 = [1,2,3,4,5,6,7,8,9]
+  var a2 = @[1,2,3,4,5,6,7,8,9]
+  a0.set_all('i')
+  a1.set_all(4)
+  a2.set_all(4)
+  doAssert a0 == "iiiiiiiiiii"
+  doAssert a1 == [4,4,4,4,4,4,4,4,4]
+  doAssert a2 == @[4,4,4,4,4,4,4,4,4]
 
 const constval0 = "hello".map(proc(x: char): char = x)
 const constval1 = [1,2,3,4].map(proc(x: int): int = x)
@@ -29,6 +29,5 @@ const constval1 = [1,2,3,4].map(proc(x: int): int = x)
 doAssert("hello".map(proc(x: char): char = x) == constval0)
 doAssert([1,2,3,4].map(proc(x: int): int = x) == constval1)
 
-test()
-static:
-    test()
+static: main()
+main()
diff --git a/tests/vm/tstringnil.nim b/tests/vm/tstringnil.nim
index 39e4040dc..d5dd4f4c9 100644
--- a/tests/vm/tstringnil.nim
+++ b/tests/vm/tstringnil.nim
@@ -20,16 +20,16 @@ proc buildSuiteContents(suiteName, suiteDesc, suiteBloc: NimNode): tuple[tests:
 
       var testObj = SuiteTest()
       if suiteName.kind == nnkNilLit:
-        testObj.suiteName = nil
+        testObj.suiteName = ""
       else:
         testObj.suiteName = $suiteName
       if suiteDesc.kind == nnkNilLit:
-        testObj.suiteDesc = nil
+        testObj.suiteDesc = ""
       else:
         testObj.suiteDesc = suiteDesc.strVal
       testObj.testName = $child[1] # should not ever be nil
       if child[2].kind == nnkNilLit:
-        testObj.testDesc = nil
+        testObj.testDesc = ""
       else:
         testObj.testDesc = child[2].strVal
       testObj.testBlock = child[1]
@@ -46,5 +46,5 @@ macro suite(suiteName, suiteDesc, suiteBloc: untyped): typed =
 
 # Test above
 suite basics, "Description of such":
-  test(t5, nil):
-    assert false
+  test(t5, ""):
+    doAssert false
diff --git a/tests/vm/tswap.nim b/tests/vm/tswap.nim
index 2219be9ca..bdbe5528c 100644
--- a/tests/vm/tswap.nim
+++ b/tests/vm/tswap.nim
@@ -1,9 +1,11 @@
 discard """
-msg: '''
+nimout: '''
 x.data = @[10]
 y = @[11]
 x.data = @[11]
-y = @[10]'''
+y = @[10]
+@[3, 2, 1]
+'''
 """
 
 # bug #2946
@@ -22,3 +24,11 @@ proc testSwap(): int {.compiletime.} =
   result = 99
 
 const something = testSwap()
+
+# bug #15463
+block:
+  static:
+    var s = @[1, 2, 3]
+    swap(s[0], s[2])
+
+    echo s
diff --git a/tests/vm/ttouintconv.nim b/tests/vm/ttouintconv.nim
index 5f8884e80..8c43a3adb 100644
--- a/tests/vm/ttouintconv.nim
+++ b/tests/vm/ttouintconv.nim
@@ -1,7 +1,7 @@
 import macros
 
 discard """
-msg: '''
+nimout: '''
 8 9 17
 239 255
 61439 65534 65535
@@ -20,7 +20,7 @@ msg: '''
 
 #bug #2514
 
-macro foo(): typed =
+macro foo() =
   var x = 8'u8
   var y = 9'u16
   var z = 17'u32
@@ -58,7 +58,7 @@ macro foo(): typed =
   var zz = 0x7FFFFFFF'u32
   echo zz
 
-macro foo2(): typed =
+macro foo2() =
   var xx = 0x7FFFFFFFFFFFFFFF
   echo xx
 
@@ -69,9 +69,16 @@ macro foo2(): typed =
   echo zz
 
   var ww = -9
-  var vv = ww.uint
-  var kk = vv.uint32
+  var vv = cast[uint](ww)
+  var kk = cast[uint32](vv)
   echo kk
 
 foo()
 foo2()
+
+block:
+  const neg5VM = block:
+    let x = -5'i8
+    uint64(x)
+  let y = -5'i8
+  doAssert uint64(y) == neg5VM
diff --git a/tests/vm/ttypedesc.nim b/tests/vm/ttypedesc.nim
new file mode 100644
index 000000000..d799e5adb
--- /dev/null
+++ b/tests/vm/ttypedesc.nim
@@ -0,0 +1,31 @@
+block: # issue #15760
+  type
+    Banana = object
+    SpecialBanana = object
+    
+  proc getName(_: type Banana): string = "Banana"
+  proc getName(_: type SpecialBanana): string = "SpecialBanana"
+
+  proc x[T](): string = 
+    const n = getName(T) # this one works
+    result = n
+    
+  proc y(T: type): string =
+    const n = getName(T) # this one failed to compile
+    result = n
+
+  doAssert x[SpecialBanana]() == "SpecialBanana"
+  doAssert y(SpecialBanana) == "SpecialBanana"
+
+import macros
+
+block: # issue #23112
+  type Container = object
+    foo: string
+
+  proc canBeImplicit(t: typedesc) {.compileTime.} =
+    let tDesc = getType(t)
+    doAssert tDesc.kind == nnkObjectTy
+
+  static:
+    canBeImplicit(Container)
diff --git a/tests/vm/tunsupportedintfloatcast.nim b/tests/vm/tunsupportedintfloatcast.nim
new file mode 100644
index 000000000..d65f10d86
--- /dev/null
+++ b/tests/vm/tunsupportedintfloatcast.nim
@@ -0,0 +1,3 @@
+static:
+  echo cast[int32](12.0) #[tt.Error
+       ^ VM does not support 'cast' from tyFloat with size 8 to tyInt32 with size 4 due to different sizes]#
diff --git a/tests/vm/tvarsection.nim b/tests/vm/tvarsection.nim
new file mode 100644
index 000000000..cd34bd02e
--- /dev/null
+++ b/tests/vm/tvarsection.nim
@@ -0,0 +1,11 @@
+var
+  a {.compileTime.} = 2
+  b = -1
+  c {.compileTime.} = 3
+  d = "abc"
+
+static:
+  doAssert a == 2
+  doAssert c == 3
+
+doAssert ($b & $d) == "-1abc"
diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim
index 4af824cf4..6aeac5529 100644
--- a/tests/vm/tvmmisc.nim
+++ b/tests/vm/tvmmisc.nim
@@ -1,9 +1,7 @@
-# bug #4462
 import macros
 import os
-import ospaths
-import strutils
 
+# bug #4462
 block:
   proc foo(t: typedesc) {.compileTime.} =
     assert sameType(getType(t), getType(int))
@@ -11,27 +9,27 @@ block:
   static:
     foo(int)
 
-# #4412
+# bug #4412
 block:
   proc default[T](t: typedesc[T]): T {.inline.} = discard
 
   static:
     var x = default(type(0))
 
-# #6379
-static:
-  import algorithm
+# bug #6379
+import algorithm
 
+static:
   var numArray = [1, 2, 3, 4, -1]
   numArray.sort(cmp)
-  assert numArray == [-1, 1, 2, 3, 4]
+  doAssert numArray == [-1, 1, 2, 3, 4]
 
   var str = "cba"
   str.sort(cmp)
-  assert str == "abc"
+  doAssert str == "abc"
 
-# #6086
-import math, sequtils, future
+# bug #6086
+import math, sequtils, sugar
 
 block:
   proc f: int =
@@ -44,7 +42,7 @@ block:
   var a = f()
   const b = f()
 
-  assert a == b
+  doAssert a == b
 
 block:
   proc f(): seq[char] =
@@ -52,7 +50,7 @@ block:
 
   var runTime = f()
   const compTime = f()
-  assert runTime == compTime
+  doAssert runTime == compTime
 
 # #6083
 block:
@@ -66,27 +64,733 @@ block:
       result[i] = tmp
 
   const fact1000 = abc()
-  assert fact1000 == @[1, 2]
+  doAssert fact1000 == @[1, 2]
 
 # Tests for VM ops
 block:
   static:
-    assert "vm" in getProjectPath()
+    # for joint test, the project path is different, so I disabled it:
+    when false:
+      doAssert "vm" in getProjectPath()
 
     let b = getEnv("UNSETENVVAR")
-    assert b == ""
-    assert existsEnv("UNSERENVVAR") == false
+    doAssert b == ""
+    doAssert existsEnv("UNSERENVVAR") == false
     putEnv("UNSETENVVAR", "VALUE")
-    assert getEnv("UNSETENVVAR") == "VALUE"
-    assert existsEnv("UNSETENVVAR") == true
+    doAssert getEnv("UNSETENVVAR") == "VALUE"
+    doAssert existsEnv("UNSETENVVAR") == true
 
-    assert fileExists("MISSINGFILE") == false
-    assert dirExists("MISSINGDIR") == false
+    doAssert fileExists("MISSINGFILE") == false
+    doAssert dirExists("MISSINGDIR") == false
+    doAssert fileExists(currentSourcePath())
+    doAssert dirExists(currentSourcePath().parentDir)
 
-# #7210
+# bug #7210
 block:
   static:
     proc f(size: int): int =
       var some = newStringOfCap(size)
       result = size
-    doAssert f(4) == 4
\ No newline at end of file
+    doAssert f(4) == 4
+
+# bug #6689
+block:
+  static:
+    proc foo(): int = 0
+    var f: proc(): int
+    doAssert f.isNil
+    f = foo
+    doAssert(not f.isNil)
+
+block:
+  static:
+    var x: ref ref int
+    new(x)
+    doAssert(not x.isNil)
+
+# bug #7871
+static:
+  type Obj = object
+    field: int
+  var s = newSeq[Obj](1)
+  var o = Obj()
+  s[0] = o
+  o.field = 2
+  doAssert s[0].field == 0
+
+# bug #8125
+static:
+   let def_iter_var = ident("it")
+
+# bug #8142
+static:
+  type Obj = object
+    names: string
+
+  proc pushName(o: var Obj) =
+    var s = ""
+    s.add("FOOBAR")
+    o.names.add(s)
+
+  var o = Obj()
+  o.names = ""
+  o.pushName()
+  o.pushName()
+  doAssert o.names == "FOOBARFOOBAR"
+
+# bug #8154
+import parseutils
+
+static:
+  type Obj = object
+    i: int
+
+  proc foo(): Obj =
+    discard parseInt("1", result.i, 0)
+
+  static:
+    doAssert foo().i == 1
+
+# bug #10333
+block:
+  const
+    encoding: auto = [
+      ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"],
+      ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"],
+      ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"],
+      ["", "M", "MM", "MMM", "--", "-", "--", "---", "----", "--"],
+    ]
+  doAssert encoding.len == 4
+
+# bug #10886
+
+proc tor(): bool =
+  result = true
+  result = false or result
+
+proc tand(): bool =
+  result = false
+  result = true and result
+
+const
+  ctor = tor()
+  ctand = not tand()
+
+static:
+  doAssert ctor
+  doAssert ctand
+
+block: # bug #13081
+  type Kind = enum
+    k0, k1, k2, k3
+
+  type Foo = object
+    x0: float
+    case kind: Kind
+    of k0: discard
+    of k1: x1: int
+    of k2: x2: string
+    of k3: x3: string
+
+  const j1 = Foo(x0: 1.2, kind: k1, x1: 12)
+  const j2 = Foo(x0: 1.3, kind: k2, x2: "abc")
+  const j3 = Foo(x0: 1.3, kind: k3, x3: "abc2")
+  static:
+    doAssert $j1 == "(x0: 1.2, kind: k1, x1: 12)"
+    doAssert $j2 == """(x0: 1.3, kind: k2, x2: "abc")"""
+    doAssert $j3 == """(x0: 1.3, kind: k3, x3: "abc2")"""
+  doAssert $j1 == "(x0: 1.2, kind: k1, x1: 12)"
+  doAssert $j2 == """(x0: 1.3, kind: k2, x2: "abc")"""
+  doAssert $j3 == """(x0: 1.3, kind: k3, x3: "abc2")"""
+
+  doAssert j1.x1 == 12
+  static:
+    doAssert j1.x1 == 12
+
+block: # bug #15595
+  proc fn0()=echo 0
+  proc fn1()=discard
+  proc main=
+    var local = 0
+    proc fn2()=echo local
+    var a0 = fn0
+    var a1 = fn1
+    var a2 = fn2
+    var a3: proc()
+    var a4: proc()
+    doAssert a0 == fn0 # bugfix
+    doAssert a1 == fn1 # ditto
+    doAssert a2 == fn2 # ditto
+
+    doAssert fn0 != fn1
+
+    doAssert a2 != nil
+    doAssert a3 == nil # bugfix
+
+    doAssert a3 == a4 # bugfix
+  static: main()
+  main()
+
+block: # issue #20543
+  type F = proc()
+  const myArray = block:
+    var r: array[1, F]
+    r[0] = nil
+    r
+  doAssert isNil(myArray[0])
+
+# bug #15363
+import sequtils
+
+block:
+  func identity(a: bool): bool = a
+
+  var a: seq[bool] = static:
+      newSeq[bool](0).mapIt(it) # segfaults
+  var b: seq[bool] = static:
+      newSeq[bool](0).filterIt(it) # does not segfault
+  var c: seq[bool] = static:
+      newSeq[bool](0).map(identity) # does not segfault
+  var d: seq[bool] = static:
+      newSeq[bool](0).map(proc (a: bool): bool = false) # segfaults
+  var e: seq[bool] = static:
+      newSeq[bool](0).filter(identity) # does not segfault
+  var f: seq[bool] = static:
+      newSeq[bool](0).filter(proc (a: bool): bool = false) # segfaults
+
+  doAssert a == @[]
+  doAssert b == @[]
+  doAssert c == @[]
+  doAssert d == @[]
+  doAssert e == @[]
+  doAssert f == @[]
+
+
+block: # bug #18310
+  macro t() : untyped =
+    let
+      x = nnkTupleConstr.newTree(newLit(1))
+      y = nnkTupleConstr.newTree(newLit(2))
+    doAssert not (x == y) # not using != intentionally
+    doAssert not(cast[int](x) == cast[int](y))
+    doAssert not(system.`==`(x, y))
+    doAssert system.`==`(x, x)
+  t()
+
+block: # bug #10815
+  type
+    Opcode = enum
+      iChar, iSet
+
+    Inst = object
+      case code: Opcode
+        of iChar:
+          c: char
+        of iSet:
+          cs: set[char]
+
+    Patt = seq[Inst]
+
+
+  proc `$`(p: Patt): string =
+    discard
+
+  proc P(): Patt =
+    result.add Inst(code: iSet)
+
+  const a = P()
+  doAssert $a == ""
+
+import tables
+
+block: # bug #8007
+  type
+    CostKind = enum
+      Fixed,
+      Dynamic
+
+    Cost = object
+      case kind*: CostKind
+      of Fixed:
+        cost*: int
+      of Dynamic:
+        handler*: proc(value: int): int {.nimcall.}
+
+  proc foo(value: int): int {.nimcall.} =
+    sizeof(value)
+
+  const a: array[2, Cost] =[
+    Cost(kind: Fixed, cost: 999),
+    Cost(kind: Dynamic, handler: foo)
+  ]
+
+  # OK with arrays & object variants
+  doAssert $a == "[(kind: Fixed, cost: 999), (kind: Dynamic, handler: ...)]"
+
+  const b: Table[int, Cost] = {
+    0: Cost(kind: Fixed, cost: 999),
+    1: Cost(kind: Dynamic, handler: foo)
+  }.toTable
+
+  # KO with Tables & object variants
+  # echo b # {0: (kind: Fixed, cost: 0), 1: (kind: Dynamic, handler: ...)} # <----- wrong behaviour
+  doAssert $b == "{0: (kind: Fixed, cost: 999), 1: (kind: Dynamic, handler: ...)}"
+
+  const c: Table[int, int] = {
+    0: 100,
+    1: 999
+  }.toTable
+
+  # OK with Tables and primitive int
+  doAssert $c == "{0: 100, 1: 999}"
+
+  # For some reason the following gives
+  #    Error: invalid type for const: Cost
+  const d0 = Cost(kind: Fixed, cost: 999)
+
+  # OK with seq & object variants
+  const d = @[Cost(kind: Fixed, cost: 999), Cost(kind: Dynamic, handler: foo)]
+  doAssert $d == "@[(kind: Fixed, cost: 999), (kind: Dynamic, handler: ...)]"
+
+block: # bug #14340
+  block:
+    proc opl3EnvelopeCalcSin0() = discard
+    type EnvelopeSinfunc = proc() {.nimcall.} # todo: fixme 
+    # const EnvelopeCalcSin0 = opl3EnvelopeCalcSin0 # ok
+    const EnvelopeCalcSin0: EnvelopeSinfunc = opl3EnvelopeCalcSin0 # was bug
+    const envelopeSin = [EnvelopeCalcSin0]
+    var a = 0
+    envelopeSin[a]()
+
+  block:
+    type Mutator = proc() {.noSideEffect, gcsafe.}
+    proc mutator0() = discard
+    const mTable = [Mutator(mutator0)]
+    var i=0
+    mTable[i]()
+
+block: # VM wrong register free causes errors in unrelated code
+  block: # bug #15597
+    #[
+    Error: unhandled exception: 'sym' is not accessible using discriminant 'kind' of type 'TNode' [FieldDefect]
+    in /Users/timothee/git_clone/nim/Nim_prs/compiler/vm.nim(1176) rawExecute
+    in opcIndCall
+    in let prc = if not isClosure: bb.sym else: bb[0].sym
+    ]#
+    proc bar2(head: string): string = "asdf"
+    proc zook(u1: int) = discard
+
+    type PathEntry = object
+      kind: int
+      path: string
+
+    iterator globOpt(): int =
+      var u1: int
+
+      zook(u1)
+      zook(u1)
+      zook(u1)
+      zook(u1)
+      zook(u1)
+      zook(u1)
+      zook(u1)
+      zook(u1)
+      zook(u1)
+      zook(u1)
+      zook(u1)
+      zook(u1)
+      zook(u1)
+      zook(u1)
+
+      var entry = PathEntry()
+      entry.path = bar2("")
+      if false:
+        echo "here2"
+
+    proc processAux(a: float) = discard
+
+    template bar(iter: untyped): untyped =
+      var ret: float
+      for x in iter: break
+      ret
+
+    proc main() =
+      processAux(bar(globOpt()))
+    static: main()
+
+  block: # ditto
+    # D20201024T133245
+    type Deque = object
+    proc initDeque2(initialSize: int = 4): Deque = Deque()
+    proc len2(a: Deque): int = 2
+    proc baz(dir: string): bool = true
+    proc bar2(head: string): string = "asdf"
+    proc bar3(path: var string) = path = path
+
+    type PathEntry = object
+      kind: int
+      path: string
+
+    proc initGlobOpt(dir: string, a1=false,a2=false,a3=false,a4=false): string = dir
+
+    iterator globOpt(dir: string): int =
+      var stack = initDeque2()
+      doAssert baz("")
+      let z = stack.len2
+      if stack.len2 >= 0:
+        var entry = PathEntry()
+        let current = if true: stack.len2 else: stack.len2
+        entry.path = bar2("")
+        bar3(entry.path)
+      if false:
+        echo "here2" # comment here => you get same error as https://github.com/nim-lang/Nim/issues/15704
+
+    proc processAux(a: float) = discard
+
+    template bar(iter: untyped): untyped =
+      var ret: float
+      for x in iter: break
+      ret
+    proc main() =
+      processAux(bar(globOpt(initGlobOpt("."))))
+    static: main()
+
+  block: # bug #15704
+    #[
+    Error: attempt to access a nil address kind: rkFloat
+    ]#
+    type Deque = object
+    proc initDeque2(initialSize: int = 4): Deque = Deque()
+    proc len2(a: Deque): int = 2
+
+    proc baz(dir: string): bool = true
+    proc bar2(head: string): string = "asdf"
+    proc bar3(path: var string) = path = path
+
+    type PathEntry = object
+      kind: int
+      path: string
+      depth: int
+
+    proc initGlobOpt(dir: string, a1=false,a2=false,a3=false,a4=false): string =
+      dir
+
+    iterator globOpt(dir: string): int =
+      var stack = initDeque2()
+      doAssert baz("")
+      let z = stack.len2
+      var a5: int
+      if stack.len2 >= 0:
+        var entry = PathEntry()
+        if false:
+          echo "here"
+        let current = if true: stack.len2 else: stack.len2
+        entry.depth = 1
+        entry.path = bar2("")
+        bar3(entry.path)
+    proc processAux(a: float) = discard
+    template bar(iter: untyped): untyped =
+      var ret: float
+      for x in iter:
+        break
+      ret
+    const dir = "."
+    proc main() =
+      processAux(bar(globOpt(initGlobOpt(dir))))
+    static: main()
+
+block: # bug #8015
+  block:
+    type Foo = object
+      case b: bool
+      of false: v1: int
+      of true: v2: int
+    const t = [Foo(b: false, v1: 1), Foo(b: true, v2: 2)]
+    doAssert $t == "[(b: false, v1: 1), (b: true, v2: 2)]"
+    doAssert $t[0] == "(b: false, v1: 1)" # was failing
+
+  block:
+    type
+      CostKind = enum
+        Fixed,
+        Dynamic
+
+      Cost = object
+        case kind*: CostKind
+        of Fixed:
+          cost*: int
+        of Dynamic:
+          handler*: proc(): int {.nimcall.}
+
+    proc foo1(): int {.nimcall.} =
+      100
+
+    proc foo2(): int {.nimcall.} =
+      200
+
+    # Change to `let` and it doesn't crash
+    const costTable = [
+      0: Cost(kind: Fixed, cost: 999),
+      1: Cost(kind: Dynamic, handler: foo1),
+      2: Cost(kind: Dynamic, handler: foo2)
+    ]
+
+    doAssert $costTable[0] == "(kind: Fixed, cost: 999)"
+    doAssert costTable[1].handler() == 100
+    doAssert costTable[2].handler() == 200
+
+    # Now trying to carry the table as an object field
+    type
+      Wrapper = object
+        table: array[3, Cost]
+
+    proc procNewWrapper(): Wrapper =
+      result.table = costTable
+
+    # Alternatively, change to `const` and it doesn't crash
+    let viaProc = procNewWrapper()
+
+    doAssert viaProc.table[1].handler != nil
+    doAssert viaProc.table[2].handler != nil
+    doAssert $viaProc.table[0] == "(kind: Fixed, cost: 999)"
+    doAssert viaProc.table[1].handler() == 100
+    doAssert viaProc.table[2].handler() == 200
+
+
+# bug #19198
+
+block:
+  type
+    Foo[n: static int] = int
+
+block:
+  static:
+    let x = int 1
+    doAssert $(x.type) == "int"  # Foo
+
+block:
+  static:
+    let x = int 1
+    let y = x + 1
+    # Error: unhandled exception: value out of range: -8 notin 0 .. 65535 [RangeDefect]
+    doAssert y == 2
+
+
+type Atom* = object
+  bar: int
+
+proc main() = # bug #12994
+  var s: seq[Atom]
+  var atom: Atom
+  var checked = 0
+  for i in 0..<2:
+    atom.bar = 5
+    s.add atom
+    atom.reset
+    if i == 0:
+      checked += 1
+      doAssert $s == "@[(bar: 5)]"
+    else:
+      checked += 1
+      doAssert $s == "@[(bar: 5), (bar: 5)]"
+  doAssert checked == 2
+
+static: main()
+main()
+
+# bug #19201
+proc foo(s: sink string) = doAssert s.len == 3
+
+static:
+  foo("abc")
+
+
+static:
+  for i in '1' .. '2': # bug #10938
+    var s: set[char]
+    doAssert s == {}
+    incl(s, i)
+
+  for _ in 0 ..< 3: # bug #13312
+    var s: string
+    s.add("foo")
+    doAssert s == "foo"
+
+  for i in 1 .. 5: # bug #13918
+    var arr: array[3, int]
+    var val: int
+    doAssert arr == [0, 0, 0] and val == 0
+    for j in 0 ..< len(arr):
+      arr[j] = i
+      val = i
+
+# bug #20985
+let a = block:
+  var groups: seq[seq[int]]
+  for i in 0 ..< 3:
+    var group: seq[int]
+    for j in 0 ..< 3:
+      group.add j
+    groups.add group
+  groups
+
+const b = block:
+  var groups: seq[seq[int]]
+  for i in 0 ..< 3:
+    var group: seq[int]
+    for j in 0 ..< 3:
+      group.add j
+    groups.add group
+  groups
+
+doAssert a == @[@[0, 1, 2], @[0, 1, 2], @[0, 1, 2]]
+doAssert b == @[@[0, 1, 2], @[0, 1, 2], @[0, 1, 2]]
+
+macro m1(s: string): int =
+  var ProcID {.global, compileTime.}: int
+  inc(ProcID)
+  result = newLit(ProcID)
+
+proc macroGlobal =
+  doAssert m1("Macro argument") == 1
+  doAssert m1("Macro argument") == 2
+  doAssert m1("Macro argument") == 3
+
+static: macroGlobal()
+macroGlobal()
+
+block: # bug #10108
+  template reject(x) =
+    static: doAssert(not compiles(x))
+
+  static:
+    let x: int = 2
+    proc deliver_x(): int = x
+    var y2 = deliver_x()
+    discard y2
+    reject:
+      const c5 = deliver_x()
+
+block: # bug #7590
+  proc doInit[T]():auto=
+    var a: T
+    return a
+
+  proc fun2[T](tup1:T)=
+    const tup0=doInit[T]()
+
+    # var tup=tup0 #ok
+    const tup=tup0 #causes bug
+
+    doAssert tup is tuple
+    doAssert tup[0] is tuple
+    for ai in tup.fields:
+      doAssert ai is tuple, "BUG2"
+
+  # const c=(foo:(bar1: 0.0))
+  const c=(foo:(bar1:"foo1"))
+  fun2(c)
+
+block: # bug #21708
+  type
+    Tup = tuple[name: string]
+
+  const X: array[2, Tup] = [(name: "foo",), (name: "bar",)]
+
+  static:
+    let s = X[0]
+    doAssert s[0] == "foo"
+
+block:
+  proc swap[T](x: var T): T =
+    result = x
+    x = default(T)
+
+  proc merge[T](a, b: var openArray[T]) =
+    a[0] = swap b[0]
+
+  static:
+    var x = "abc"
+    var y = "356"
+    merge(x, y)
+    doAssert x == "3bc"
+
+block: # bug #22190
+  type
+    EVMFork = enum
+      Berlin
+      Istanbul
+      Shanghai
+
+  const
+    Vm2OpAllForks =
+      {EVMFork.low .. EVMFork.high}
+
+    vm2OpExecBlockData = [(forks: Vm2OpAllForks)]
+
+  proc mkOpTable(selected: EVMFork): bool =
+    selected notin vm2OpExecBlockData[0].forks
+
+  const
+    tab = mkOpTable(Berlin)
+
+  doAssert not tab
+
+block: # issue #22524
+  const cnst = cstring(nil)
+  doAssert cnst.isNil
+  doAssert cnst == nil
+  let b = cnst
+  doAssert b.isNil
+  doAssert b == nil
+
+  let a = static: cstring(nil)
+  doAssert a.isNil
+
+  static:
+    var x: cstring
+    doAssert x.isNil
+    doAssert x == nil
+    doAssert x != ""
+
+block: # issue #15730
+  const s: cstring = ""
+  doAssert s != nil
+
+  static:
+    let s: cstring = ""
+    doAssert not s.isNil
+    doAssert s != nil
+    doAssert s == ""
+
+static: # more nil cstring issues
+  let x = cstring(nil)
+  doAssert x.len == 0
+
+block: # bug #23925
+  type Foo = enum A = -1
+  proc foo =
+    doAssert cast[Foo](-1) == A
+    doAssert ord(A) == -1
+
+  static: foo()
+  foo()
+
+  type E = enum
+    e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 e10 e11 e12 e13 e14 e15 e16 e17 e18 e19 e20
+    e21 e22 e23 e24 e25 e26 e27 e28 e29 e30 e31 e32 e33 e34 e35 e36 e37 e38
+    e39 e40 e41 e42 e43 e44 e45 e46 e47 e48 e49 e50 e51 e52 e53 e54 e55 e56
+    e57 e58 e59 e60 e61 e62 e63 e64 e65 e66 e67 e68 e69 e70 e71 e72 e73 e74
+    e75 e76 e77 e78 e79 e80 e81 e82 e83 e84 e85 e86 e87 e88 e89 e90 e91 e92
+    e93 e94 e95 e96 e97 e98 e99 e100 e101 e102 e103 e104 e105 e106 e107 e108
+    e109 e110 e111 e112 e113 e114 e115 e116 e117 e118 e119 e120 e121 e122
+    e123 e124 e125 e126 e127 e128
+  proc bar =
+    doAssert cast[E](int(e128)) == e128
+
+  static: bar()
+  bar()
+
+static: # bug #21353
+  var s: proc () = default(proc ())
+  doAssert s == nil
diff --git a/tests/vm/tvmops.nim b/tests/vm/tvmops.nim
new file mode 100644
index 000000000..3d8d5a9ac
--- /dev/null
+++ b/tests/vm/tvmops.nim
@@ -0,0 +1,46 @@
+discard """
+  targets: "c cpp js"
+"""
+
+#[
+test for vmops.nim
+]#
+import os
+import math
+import strutils
+
+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 & " --nonxistent")
+    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 bitxor:
+    let x = -1'i32
+    let y = 1'i32
+    doAssert (x xor y) == -2
+
+block:
+  # Check against bugs like #9176
+  doAssert getCurrentCompilerExe() == getCurrentCompilerExe().static
+  if false: #pending #9176
+    doAssert gorgeEx("nonxistent") == gorgeEx("nonxistent").static
diff --git a/tests/vm/tvmopsDanger.nim b/tests/vm/tvmopsDanger.nim
new file mode 100644
index 000000000..966feffe6
--- /dev/null
+++ b/tests/vm/tvmopsDanger.nim
@@ -0,0 +1,13 @@
+discard """
+  cmd: "nim c --experimental:vmopsDanger -r $file"
+"""
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+import std/[times, os]
+
+const foo = getTime()
+let bar = foo
+doAssert bar > low(Time)
+
+static: # bug #23932
+  doAssert getCurrentDir().len > 0
diff --git a/tests/vm/twrong_concat.nim b/tests/vm/twrong_concat.nim
index 538ea2527..59a10bdb9 100644
--- a/tests/vm/twrong_concat.nim
+++ b/tests/vm/twrong_concat.nim
@@ -1,7 +1,3 @@
-discard """
-  output: '''success'''
-"""
-
 # bug #3804
 
 #import sequtils
@@ -23,6 +19,4 @@ static:
   sameBug(objs)
   # sameBug(objs)
   echo objs[0].field
-  assert(objs[0].field == "hello") # fails, because (objs[0].field == "hello bug") - mutated!
-
-echo "success"
+  doAssert(objs[0].field == "hello") # fails, because (objs[0].field == "hello bug") - mutated!
diff --git a/tests/vm/twrongarray.nim b/tests/vm/twrongarray.nim
index c1514d0e9..7f24290e2 100644
--- a/tests/vm/twrongarray.nim
+++ b/tests/vm/twrongarray.nim
@@ -14,4 +14,4 @@ when false:
 
 proc two(dummy: int, size: int) =
   var x: array[size * 1, int] # compiles, but shouldn't?
-  #assert(x.len == size) # just for fun
+  # doAssert(x.len == size) # just for fun
diff --git a/tests/vm/tyaytypedesc.nim b/tests/vm/tyaytypedesc.nim
index a3ad9b707..8cb402bff 100644
--- a/tests/vm/tyaytypedesc.nim
+++ b/tests/vm/tyaytypedesc.nim
@@ -1,7 +1,3 @@
-discard """
-  output: "ntWhitespace"
-"""
-
 # bug #3357
 
 type NodeType* = enum
@@ -10,7 +6,7 @@ type NodeType* = enum
 type TokenType* = enum
   ttWhitespace
 
-proc enumTable*[A, B, C](a: openarray[tuple[key: A, val: B]], ret: typedesc[C]): C =
+proc enumTable*[A, B, C](a: openArray[tuple[key: A, val: B]], ret: typedesc[C]): C =
   for item in a:
     result[item.key] = item.val
 
@@ -18,4 +14,4 @@ const tokenTypeToNodeType = {
   ttWhitespace: ntWhitespace,
 }.enumTable(array[ttWhitespace..ttWhitespace, NodeType])
 
-echo tokenTypeToNodeType[ttWhitespace]
+doAssert tokenTypeToNodeType[ttWhitespace] == ntWhitespace
diff --git a/tests/vm/tzero_extend.nim b/tests/vm/tzero_extend.nim
index a79105531..418dbc486 100644
--- a/tests/vm/tzero_extend.nim
+++ b/tests/vm/tzero_extend.nim
@@ -5,22 +5,22 @@ proc get_values(): (seq[int8], seq[int16], seq[int32]) =
   let i8 = -3'i8
   let i16 = -3'i16
   let i32 = -3'i32
-  doAssert i8.ze == 0xFD
-  doAssert i8.ze64 == 0xFD
-  doAssert i16.ze == 0xFFFD
-  doAssert i16.ze64 == 0xFFFD
+  doAssert int(cast[uint8](i8)) == 0xFD
+  doAssert int64(cast[uint8](i8)) == 0xFD
+  doAssert int(cast[uint16](i16)) == 0xFFFD
+  doAssert int64(cast[uint16](i16)) == 0xFFFD
 
   result[0] = @[]; result[1] = @[]; result[2] = @[]
 
   for offset in RANGE:
-    let i8 = -(1 shl 9) + offset
-    let i16 = -(1 shl 17) + offset
-    let i32 = -(1 shl 33) + offset
+    let i8  = -(1'i64 shl  9) + offset
+    let i16 = -(1'i64 shl 17) + offset
+    let i32 = -(1'i64 shl 33) + offset
 
     # higher bits are masked. these should be exactly equal to offset.
-    result[0].add i8.toU8
-    result[1].add i16.toU16
-    result[2].add i32.toU32
+    result[0].add cast[int8](cast[uint64](i8))
+    result[1].add cast[int16](cast[uint64](i16))
+    result[2].add cast[int32](cast[uint64](i32))
 
 
 # these values this computed by VM
diff --git a/tests/whenstmt/t12517.nim b/tests/whenstmt/t12517.nim
new file mode 100644
index 000000000..8be0171e1
--- /dev/null
+++ b/tests/whenstmt/t12517.nim
@@ -0,0 +1,21 @@
+# Test based on issue #12517
+
+discard """
+  nimout: '''
+nimvm
+both
+'''
+  output: '''
+both
+'''
+"""
+
+proc test() =
+  when nimvm:
+    echo "nimvm"
+  echo "both"
+
+static:
+  test()
+test()
+
diff --git a/tests/whenstmt/t19426.nim b/tests/whenstmt/t19426.nim
new file mode 100644
index 000000000..95fb54a9e
--- /dev/null
+++ b/tests/whenstmt/t19426.nim
@@ -0,0 +1,16 @@
+type
+  MyInt = object
+    bitWidth: int
+
+template toRealType*(t: MyInt): typedesc =
+  when t.bitWidth == 32: int32
+  elif t.bitWidth == 64: int64
+  else: {.error.}
+
+proc doFail(T: typedesc): T = default(T)
+
+proc test =
+  const myInt = MyInt(bitWidth:32)
+  discard doFail(toRealType(myInt))
+
+test()
\ No newline at end of file
diff --git a/tests/whenstmt/twhen.nim b/tests/whenstmt/twhen.nim
new file mode 100644
index 000000000..b155ddcfb
--- /dev/null
+++ b/tests/whenstmt/twhen.nim
@@ -0,0 +1,47 @@
+discard """
+  nimout: '''
+nimvm - when
+nimvm - whenElif
+nimvm - whenElse
+'''
+  output: '''
+when
+whenElif
+whenElse
+'''
+"""
+
+# test both when and when nimvm to ensure proper evaluation
+
+proc compileOrRuntimeProc(s: string) =
+  when nimvm:
+    echo "nimvm - " & s
+  else:
+     echo s
+
+template output(s: string) =
+  static:
+    compileOrRuntimeProc(s)
+  compileOrRuntimeProc(s)
+
+when compiles(1):
+  output("when")
+elif compiles(2):
+  output("fail - whenElif")
+else:
+  output("fail - whenElse")
+
+when compiles(nonexistent):
+  output("fail - when")
+elif compiles(1):
+  output("whenElif")
+else:
+  output("fail - whenElse")
+
+when compiles(nonexistent):
+  output("fail - when")
+elif compiles(nonexistent):
+  output("fail - whenElif")
+else:
+  output("whenElse")
+
diff --git a/tests/whenstmt/twhen_macro.nim b/tests/whenstmt/twhen_macro.nim
new file mode 100644
index 000000000..deb1dddc9
--- /dev/null
+++ b/tests/whenstmt/twhen_macro.nim
@@ -0,0 +1,18 @@
+import macros
+
+# test that when stmt works from within a macro
+
+macro output(s: string, xs: varargs[untyped]): auto =
+  result = quote do:
+    when compiles(`s`):
+      "when - " & `s`
+    elif compiles(`s`):
+      "elif - " & `s`
+      # should never get here so this should not break
+      broken.xs
+    else:
+      "else - " & `s`
+      # should never get here so this should not break
+      more.broken.xs
+
+doAssert output("test") == "when - test"
\ No newline at end of file
diff --git a/tests/xml/ttree_add.nim b/tests/xml/ttree_add.nim
new file mode 100644
index 000000000..4c6ef6cf9
--- /dev/null
+++ b/tests/xml/ttree_add.nim
@@ -0,0 +1,51 @@
+discard """
+  output: '''
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+'''
+"""
+
+# Test xmltree add/insert/delete/replace operations
+import xmlparser
+import xmltree
+var baseDocHead = """
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+</xml>
+"""
+var baseDocHeadTree = parseXml(baseDocHead)
+var baseDocBody = """
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+"""
+var baseDocBodyTree = parseXml(baseDocBody)
+
+proc test_add() =
+  var testDoc = baseDocHeadTree
+  var newBody = newElement("body")
+  for item in baseDocBodyTree.items():
+    newBody.add(item)
+  
+  echo $newBody
+  
+  testDoc.add(newBody)
+  echo $testDoc
+
+test_add()
diff --git a/tests/xml/ttree_add1.nim b/tests/xml/ttree_add1.nim
new file mode 100644
index 000000000..30ec83c02
--- /dev/null
+++ b/tests/xml/ttree_add1.nim
@@ -0,0 +1,53 @@
+discard """
+  output: '''
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+'''
+"""
+
+# Test xmltree add/insert/delete/replace operations
+import xmlparser
+import xmltree
+var baseDocHead = """
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+</xml>
+"""
+var baseDocHeadTree = parseXml(baseDocHead)
+var baseDocBody = """
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+"""
+var baseDocBodyTree = parseXml(baseDocBody)
+
+proc test_add() =
+  var testDoc = baseDocHeadTree
+  var newBody = newElement("body")
+  var bodyItems: seq[XmlNode] = @[]
+  for item in baseDocBodyTree.items():
+    bodyItems.add(item)
+  newBody.add(bodyItems)
+  
+  echo $newBody
+  
+  testDoc.add(newBody)
+  echo $testDoc
+
+test_add()
diff --git a/tests/xml/ttree_delete.nim b/tests/xml/ttree_delete.nim
new file mode 100644
index 000000000..32b477839
--- /dev/null
+++ b/tests/xml/ttree_delete.nim
@@ -0,0 +1,47 @@
+discard """
+  output: '''
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+'''
+"""
+
+# Test xmltree add/insert/delete/replace operations
+import xmlparser
+import xmltree
+let initialDocBase = """
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <tag>
+    <div>MORE TEXT </div>
+    <div>MORE TEXT Some more text</div>
+  </tag>
+  <tag>
+    <div>MORE TEXT </div>
+    <div>MORE TEXT Some more text</div>
+  </tag>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+"""
+var initialDocBaseTree = parseXml(initialDocBase)
+
+proc test_delete() =
+  var testDoc = initialDocBaseTree
+  
+  testDoc.delete(1..2)
+  echo $testDoc
+
+test_delete()
diff --git a/tests/xml/ttree_delete1.nim b/tests/xml/ttree_delete1.nim
new file mode 100644
index 000000000..a8442a093
--- /dev/null
+++ b/tests/xml/ttree_delete1.nim
@@ -0,0 +1,48 @@
+discard """
+  output: '''
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+'''
+"""
+
+# Test xmltree add/insert/delete/replace operations
+import xmlparser
+import xmltree
+let initialDocBase = """
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <tag>
+    <div>MORE TEXT </div>
+    <div>MORE TEXT Some more text</div>
+  </tag>
+  <tag>
+    <div>MORE TEXT </div>
+    <div>MORE TEXT Some more text</div>
+  </tag>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+"""
+var initialDocBaseTree = parseXml(initialDocBase)
+
+proc test_delete() =
+  var testDoc = initialDocBaseTree
+  
+  testDoc.delete(1)
+  testDoc.delete(1)
+  echo $testDoc
+
+test_delete()
diff --git a/tests/xml/ttree_insert.nim b/tests/xml/ttree_insert.nim
new file mode 100644
index 000000000..b2941395b
--- /dev/null
+++ b/tests/xml/ttree_insert.nim
@@ -0,0 +1,53 @@
+discard """
+  output: '''
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+'''
+"""
+
+# Test xmltree add/insert/delete/replace operations
+import xmlparser
+import xmltree
+var baseDocHead = """
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+</xml>
+"""
+var baseDocHeadTree = parseXml(baseDocHead)
+var baseDocBody = """
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+"""
+var baseDocBodyTree = parseXml(baseDocBody)
+
+proc test_insert() =
+  var testDoc = baseDocHeadTree
+  var newBody = newElement("body")
+  var bodyItems: seq[XmlNode] = @[]
+  for item in baseDocBodyTree.items():
+    bodyItems.insert(item, len(bodyItems))
+  newBody.insert(bodyItems, 1)
+  
+  echo $newBody
+  
+  testDoc.insert(newBody, 1)
+  echo $testDoc
+
+test_insert()
diff --git a/tests/xml/ttree_insert1.nim b/tests/xml/ttree_insert1.nim
new file mode 100644
index 000000000..9aa3faf69
--- /dev/null
+++ b/tests/xml/ttree_insert1.nim
@@ -0,0 +1,51 @@
+discard """
+  output: '''
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+'''
+"""
+
+# Test xmltree add/insert/delete/replace operations
+import xmlparser
+import xmltree
+var baseDocHead = """
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+</xml>
+"""
+var baseDocHeadTree = parseXml(baseDocHead)
+var baseDocBody = """
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+"""
+var baseDocBodyTree = parseXml(baseDocBody)
+
+proc test_insert() =
+  var testDoc = baseDocHeadTree
+  var newBody = newElement("body")
+  for item in baseDocBodyTree.items():
+    newBody.insert(item, len(newBody))
+  
+  echo $newBody
+  
+  testDoc.insert(newBody, 1)
+  echo $testDoc
+
+test_insert()
diff --git a/tests/xml/ttree_replace.nim b/tests/xml/ttree_replace.nim
new file mode 100644
index 000000000..97d2db638
--- /dev/null
+++ b/tests/xml/ttree_replace.nim
@@ -0,0 +1,46 @@
+discard """
+  output: '''
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+'''
+"""
+
+# Test xmltree add/insert/delete/replace operations
+import xmlparser
+import xmltree
+var baseDocBody = """
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+"""
+var baseDocBodyTree = parseXml(baseDocBody)
+let initialDocBase = """
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <body>
+    <div>Some text in body before replace </div>
+    <div>Some more text in body before replace </div>
+  </body>
+</xml>
+"""
+var initialDocBaseTree = parseXml(initialDocBase)
+
+proc test_replace() =
+  var testDoc = initialDocBaseTree
+  
+  testDoc.replace(1, @[baseDocBodyTree])
+  echo $testDoc
+
+test_replace()
diff --git a/tests/xml/ttree_replace1.nim b/tests/xml/ttree_replace1.nim
new file mode 100644
index 000000000..059ce2085
--- /dev/null
+++ b/tests/xml/ttree_replace1.nim
@@ -0,0 +1,53 @@
+discard """
+  output: '''
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+'''
+"""
+
+# Test xmltree add/insert/delete/replace operations
+import xmlparser
+import xmltree
+var baseDocHead = """
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+"""
+var baseDocHeadTree = parseXml(baseDocHead)
+var baseDocBody = """
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+"""
+var baseDocBodyTree = parseXml(baseDocBody)
+let initialDocBase = """
+<xml>
+  <head>
+    <div>Some text before replace </div>
+    <div>Some more text before replace </div>
+  </head>
+  <body>
+    <div>Some text in body before replace </div>
+    <div>Some more text in body before replace </div>
+  </body>
+</xml>
+"""
+var initialDocBaseTree = parseXml(initialDocBase)
+
+proc test_replace() =
+  var testDoc = initialDocBaseTree
+  
+  testDoc.replace(0..1, @[baseDocHeadTree, baseDocBodyTree])
+  echo $testDoc
+
+test_replace()
diff --git a/tinyc/arm-asm.c b/tinyc/arm-asm.c
deleted file mode 100644
index 3b5ae661c..000000000
--- a/tinyc/arm-asm.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/*************************************************************/
-/*
- *  ARM dummy assembler for TCC
- *
- */
-
-#ifdef TARGET_DEFS_ONLY
-
-#define CONFIG_TCC_ASM
-#define NB_ASM_REGS 16
-
-ST_FUNC void g(int c);
-ST_FUNC void gen_le16(int c);
-ST_FUNC void gen_le32(int c);
-
-/*************************************************************/
-#else
-/*************************************************************/
-
-#include "tcc.h"
-
-static void asm_error(void)
-{
-    tcc_error("ARM asm not implemented.");
-}
-
-/* XXX: make it faster ? */
-ST_FUNC void g(int c)
-{
-    int ind1;
-    if (nocode_wanted)
-        return;
-    ind1 = ind + 1;
-    if (ind1 > cur_text_section->data_allocated)
-        section_realloc(cur_text_section, ind1);
-    cur_text_section->data[ind] = c;
-    ind = ind1;
-}
-
-ST_FUNC void gen_le16 (int i)
-{
-    g(i);
-    g(i>>8);
-}
-
-ST_FUNC void gen_le32 (int i)
-{
-    gen_le16(i);
-    gen_le16(i>>16);
-}
-
-ST_FUNC void gen_expr32(ExprValue *pe)
-{
-    gen_le32(pe->v);
-}
-
-ST_FUNC void asm_opcode(TCCState *s1, int opcode)
-{
-    asm_error();
-}
-
-ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier)
-{
-    asm_error();
-}
-
-/* generate prolog and epilog code for asm statement */
-ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands,
-                         int nb_outputs, int is_output,
-                         uint8_t *clobber_regs,
-                         int out_reg)
-{
-}
-
-ST_FUNC void asm_compute_constraints(ASMOperand *operands,
-                                    int nb_operands, int nb_outputs,
-                                    const uint8_t *clobber_regs,
-                                    int *pout_reg)
-{
-}
-
-ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str)
-{
-    asm_error();
-}
-
-ST_FUNC int asm_parse_regvar (int t)
-{
-    asm_error();
-    return -1;
-}
-
-/*************************************************************/
-#endif /* ndef TARGET_DEFS_ONLY */
diff --git a/tinyc/arm-gen.c b/tinyc/arm-gen.c
deleted file mode 100644
index f535a09ce..000000000
--- a/tinyc/arm-gen.c
+++ /dev/null
@@ -1,2151 +0,0 @@
-/*
- *  ARMv4 code generator for TCC
- *
- *  Copyright (c) 2003 Daniel Glöckner
- *  Copyright (c) 2012 Thomas Preud'homme
- *
- *  Based on i386-gen.c by Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#ifdef TARGET_DEFS_ONLY
-
-#if defined(TCC_ARM_EABI) && !defined(TCC_ARM_VFP)
-#error "Currently TinyCC only supports float computation with VFP instructions"
-#endif
-
-/* number of available registers */
-#ifdef TCC_ARM_VFP
-#define NB_REGS            13
-#else
-#define NB_REGS             9
-#endif
-
-#ifndef TCC_CPU_VERSION
-# define TCC_CPU_VERSION 5
-#endif
-
-/* a register can belong to several classes. The classes must be
-   sorted from more general to more precise (see gv2() code which does
-   assumptions on it). */
-#define RC_INT     0x0001 /* generic integer register */
-#define RC_FLOAT   0x0002 /* generic float register */
-#define RC_R0      0x0004
-#define RC_R1      0x0008
-#define RC_R2      0x0010
-#define RC_R3      0x0020
-#define RC_R12     0x0040
-#define RC_F0      0x0080
-#define RC_F1      0x0100
-#define RC_F2      0x0200
-#define RC_F3      0x0400
-#ifdef TCC_ARM_VFP
-#define RC_F4      0x0800
-#define RC_F5      0x1000
-#define RC_F6      0x2000
-#define RC_F7      0x4000
-#endif
-#define RC_IRET    RC_R0  /* function return: integer register */
-#define RC_LRET    RC_R1  /* function return: second integer register */
-#define RC_FRET    RC_F0  /* function return: float register */
-
-/* pretty names for the registers */
-enum {
-    TREG_R0 = 0,
-    TREG_R1,
-    TREG_R2,
-    TREG_R3,
-    TREG_R12,
-    TREG_F0,
-    TREG_F1,
-    TREG_F2,
-    TREG_F3,
-#ifdef TCC_ARM_VFP
-    TREG_F4,
-    TREG_F5,
-    TREG_F6,
-    TREG_F7,
-#endif
-    TREG_SP = 13,
-    TREG_LR,
-};
-
-#ifdef TCC_ARM_VFP
-#define T2CPR(t) (((t) & VT_BTYPE) != VT_FLOAT ? 0x100 : 0)
-#endif
-
-/* return registers for function */
-#define REG_IRET TREG_R0 /* single word int return register */
-#define REG_LRET TREG_R1 /* second word return register (for long long) */
-#define REG_FRET TREG_F0 /* float return register */
-
-#ifdef TCC_ARM_EABI
-#define TOK___divdi3 TOK___aeabi_ldivmod
-#define TOK___moddi3 TOK___aeabi_ldivmod
-#define TOK___udivdi3 TOK___aeabi_uldivmod
-#define TOK___umoddi3 TOK___aeabi_uldivmod
-#endif
-
-/* defined if function parameters must be evaluated in reverse order */
-#define INVERT_FUNC_PARAMS
-
-/* defined if structures are passed as pointers. Otherwise structures
-   are directly pushed on stack. */
-/* #define FUNC_STRUCT_PARAM_AS_PTR */
-
-/* pointer size, in bytes */
-#define PTR_SIZE 4
-
-/* long double size and alignment, in bytes */
-#ifdef TCC_ARM_VFP
-#define LDOUBLE_SIZE  8
-#endif
-
-#ifndef LDOUBLE_SIZE
-#define LDOUBLE_SIZE  8
-#endif
-
-#ifdef TCC_ARM_EABI
-#define LDOUBLE_ALIGN 8
-#else
-#define LDOUBLE_ALIGN 4
-#endif
-
-/* maximum alignment (for aligned attribute support) */
-#define MAX_ALIGN     8
-
-#define CHAR_IS_UNSIGNED
-
-/******************************************************/
-#else /* ! TARGET_DEFS_ONLY */
-/******************************************************/
-#include "tcc.h"
-
-enum float_abi float_abi;
-
-ST_DATA const int reg_classes[NB_REGS] = {
-    /* r0 */ RC_INT | RC_R0,
-    /* r1 */ RC_INT | RC_R1,
-    /* r2 */ RC_INT | RC_R2,
-    /* r3 */ RC_INT | RC_R3,
-    /* r12 */ RC_INT | RC_R12,
-    /* f0 */ RC_FLOAT | RC_F0,
-    /* f1 */ RC_FLOAT | RC_F1,
-    /* f2 */ RC_FLOAT | RC_F2,
-    /* f3 */ RC_FLOAT | RC_F3,
-#ifdef TCC_ARM_VFP
- /* d4/s8 */ RC_FLOAT | RC_F4,
-/* d5/s10 */ RC_FLOAT | RC_F5,
-/* d6/s12 */ RC_FLOAT | RC_F6,
-/* d7/s14 */ RC_FLOAT | RC_F7,
-#endif
-};
-
-static int func_sub_sp_offset, last_itod_magic;
-static int leaffunc;
-
-#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP)
-static CType float_type, double_type, func_float_type, func_double_type;
-ST_FUNC void arm_init(struct TCCState *s)
-{
-    float_type.t = VT_FLOAT;
-    double_type.t = VT_DOUBLE;
-    func_float_type.t = VT_FUNC;
-    func_float_type.ref = sym_push(SYM_FIELD, &float_type, FUNC_CDECL, FUNC_OLD);
-    func_double_type.t = VT_FUNC;
-    func_double_type.ref = sym_push(SYM_FIELD, &double_type, FUNC_CDECL, FUNC_OLD);
-
-    float_abi = s->float_abi;
-#ifndef TCC_ARM_HARDFLOAT
-    tcc_warning("soft float ABI currently not supported: default to softfp");
-#endif
-}
-#else
-#define func_float_type func_old_type
-#define func_double_type func_old_type
-#define func_ldouble_type func_old_type
-ST_FUNC void arm_init(struct TCCState *s)
-{
-#if 0
-#if !defined (TCC_ARM_VFP)
-    tcc_warning("Support for FPA is deprecated and will be removed in next"
-                " release");
-#endif
-#if !defined (TCC_ARM_EABI)
-    tcc_warning("Support for OABI is deprecated and will be removed in next"
-                " release");
-#endif
-#endif
-}
-#endif
-
-static int two2mask(int a,int b) {
-  return (reg_classes[a]|reg_classes[b])&~(RC_INT|RC_FLOAT);
-}
-
-static int regmask(int r) {
-  return reg_classes[r]&~(RC_INT|RC_FLOAT);
-}
-
-/******************************************************/
-
-#if defined(TCC_ARM_EABI) && !defined(CONFIG_TCC_ELFINTERP)
-const char *default_elfinterp(struct TCCState *s)
-{
-    if (s->float_abi == ARM_HARD_FLOAT)
-        return "/lib/ld-linux-armhf.so.3";
-    else
-        return "/lib/ld-linux.so.3";
-}
-#endif
-
-void o(uint32_t i)
-{
-  /* this is a good place to start adding big-endian support*/
-  int ind1;
-  if (nocode_wanted)
-    return;
-  ind1 = ind + 4;
-  if (!cur_text_section)
-    tcc_error("compiler error! This happens f.ex. if the compiler\n"
-         "can't evaluate constant expressions outside of a function.");
-  if (ind1 > cur_text_section->data_allocated)
-    section_realloc(cur_text_section, ind1);
-  cur_text_section->data[ind++] = i&255;
-  i>>=8;
-  cur_text_section->data[ind++] = i&255;
-  i>>=8;
-  cur_text_section->data[ind++] = i&255;
-  i>>=8;
-  cur_text_section->data[ind++] = i;
-}
-
-static uint32_t stuff_const(uint32_t op, uint32_t c)
-{
-  int try_neg=0;
-  uint32_t nc = 0, negop = 0;
-
-  switch(op&0x1F00000)
-  {
-    case 0x800000: //add
-    case 0x400000: //sub
-      try_neg=1;
-      negop=op^0xC00000;
-      nc=-c;
-      break;
-    case 0x1A00000: //mov
-    case 0x1E00000: //mvn
-      try_neg=1;
-      negop=op^0x400000;
-      nc=~c;
-      break;
-    case 0x200000: //xor
-      if(c==~0)
-	return (op&0xF010F000)|((op>>16)&0xF)|0x1E00000;
-      break;
-    case 0x0: //and
-      if(c==~0)
-	return (op&0xF010F000)|((op>>16)&0xF)|0x1A00000;
-    case 0x1C00000: //bic
-      try_neg=1;
-      negop=op^0x1C00000;
-      nc=~c;
-      break;
-    case 0x1800000: //orr
-      if(c==~0)
-	return (op&0xFFF0FFFF)|0x1E00000;
-      break;
-  }
-  do {
-    uint32_t m;
-    int i;
-    if(c<256) /* catch undefined <<32 */
-      return op|c;
-    for(i=2;i<32;i+=2) {
-      m=(0xff>>i)|(0xff<<(32-i));
-      if(!(c&~m))
-	return op|(i<<7)|(c<<i)|(c>>(32-i));
-    }
-    op=negop;
-    c=nc;
-  } while(try_neg--);
-  return 0;
-}
-
-
-//only add,sub
-void stuff_const_harder(uint32_t op, uint32_t v) {
-  uint32_t x;
-  x=stuff_const(op,v);
-  if(x)
-    o(x);
-  else {
-    uint32_t a[16], nv, no, o2, n2;
-    int i,j,k;
-    a[0]=0xff;
-    o2=(op&0xfff0ffff)|((op&0xf000)<<4);;
-    for(i=1;i<16;i++)
-      a[i]=(a[i-1]>>2)|(a[i-1]<<30);
-    for(i=0;i<12;i++)
-      for(j=i<4?i+12:15;j>=i+4;j--)
-	if((v&(a[i]|a[j]))==v) {
-	  o(stuff_const(op,v&a[i]));
-	  o(stuff_const(o2,v&a[j]));
-	  return;
-	}
-    no=op^0xC00000;
-    n2=o2^0xC00000;
-    nv=-v;
-    for(i=0;i<12;i++)
-      for(j=i<4?i+12:15;j>=i+4;j--)
-	if((nv&(a[i]|a[j]))==nv) {
-	  o(stuff_const(no,nv&a[i]));
-	  o(stuff_const(n2,nv&a[j]));
-	  return;
-	}
-    for(i=0;i<8;i++)
-      for(j=i+4;j<12;j++)
-	for(k=i<4?i+12:15;k>=j+4;k--)
-	  if((v&(a[i]|a[j]|a[k]))==v) {
-	    o(stuff_const(op,v&a[i]));
-	    o(stuff_const(o2,v&a[j]));
-	    o(stuff_const(o2,v&a[k]));
-	    return;
-	  }
-    no=op^0xC00000;
-    nv=-v;
-    for(i=0;i<8;i++)
-      for(j=i+4;j<12;j++)
-	for(k=i<4?i+12:15;k>=j+4;k--)
-	  if((nv&(a[i]|a[j]|a[k]))==nv) {
-	    o(stuff_const(no,nv&a[i]));
-	    o(stuff_const(n2,nv&a[j]));
-	    o(stuff_const(n2,nv&a[k]));
-	    return;
-	  }
-    o(stuff_const(op,v&a[0]));
-    o(stuff_const(o2,v&a[4]));
-    o(stuff_const(o2,v&a[8]));
-    o(stuff_const(o2,v&a[12]));
-  }
-}
-
-uint32_t encbranch(int pos, int addr, int fail)
-{
-  addr-=pos+8;
-  addr/=4;
-  if(addr>=0x1000000 || addr<-0x1000000) {
-    if(fail)
-      tcc_error("FIXME: function bigger than 32MB");
-    return 0;
-  }
-  return 0x0A000000|(addr&0xffffff);
-}
-
-int decbranch(int pos)
-{
-  int x;
-  x=*(uint32_t *)(cur_text_section->data + pos);
-  x&=0x00ffffff;
-  if(x&0x800000)
-    x-=0x1000000;
-  return x*4+pos+8;
-}
-
-/* output a symbol and patch all calls to it */
-void gsym_addr(int t, int a)
-{
-  uint32_t *x;
-  int lt;
-  while(t) {
-    x=(uint32_t *)(cur_text_section->data + t);
-    t=decbranch(lt=t);
-    if(a==lt+4)
-      *x=0xE1A00000; // nop
-    else {
-      *x &= 0xff000000;
-      *x |= encbranch(lt,a,1);
-    }
-  }
-}
-
-void gsym(int t)
-{
-  gsym_addr(t, ind);
-}
-
-#ifdef TCC_ARM_VFP
-static uint32_t vfpr(int r)
-{
-  if(r<TREG_F0 || r>TREG_F7)
-    tcc_error("compiler error! register %i is no vfp register",r);
-  return r - TREG_F0;
-}
-#else
-static uint32_t fpr(int r)
-{
-  if(r<TREG_F0 || r>TREG_F3)
-    tcc_error("compiler error! register %i is no fpa register",r);
-  return r - TREG_F0;
-}
-#endif
-
-static uint32_t intr(int r)
-{
-  if(r == TREG_R12)
-    return 12;
-  if(r >= TREG_R0 && r <= TREG_R3)
-    return r - TREG_R0;
-  if (r >= TREG_SP && r <= TREG_LR)
-    return r + (13 - TREG_SP);
-  tcc_error("compiler error! register %i is no int register",r);
-}
-
-static void calcaddr(uint32_t *base, int *off, int *sgn, int maxoff, unsigned shift)
-{
-  if(*off>maxoff || *off&((1<<shift)-1)) {
-    uint32_t x, y;
-    x=0xE280E000;
-    if(*sgn)
-      x=0xE240E000;
-    x|=(*base)<<16;
-    *base=14; // lr
-    y=stuff_const(x,*off&~maxoff);
-    if(y) {
-      o(y);
-      *off&=maxoff;
-      return;
-    }
-    y=stuff_const(x,(*off+maxoff)&~maxoff);
-    if(y) {
-      o(y);
-      *sgn=!*sgn;
-      *off=((*off+maxoff)&~maxoff)-*off;
-      return;
-    }
-    stuff_const_harder(x,*off&~maxoff);
-    *off&=maxoff;
-  }
-}
-
-static uint32_t mapcc(int cc)
-{
-  switch(cc)
-  {
-    case TOK_ULT:
-      return 0x30000000; /* CC/LO */
-    case TOK_UGE:
-      return 0x20000000; /* CS/HS */
-    case TOK_EQ:
-      return 0x00000000; /* EQ */
-    case TOK_NE:
-      return 0x10000000; /* NE */
-    case TOK_ULE:
-      return 0x90000000; /* LS */
-    case TOK_UGT:
-      return 0x80000000; /* HI */
-    case TOK_Nset:
-      return 0x40000000; /* MI */
-    case TOK_Nclear:
-      return 0x50000000; /* PL */
-    case TOK_LT:
-      return 0xB0000000; /* LT */
-    case TOK_GE:
-      return 0xA0000000; /* GE */
-    case TOK_LE:
-      return 0xD0000000; /* LE */
-    case TOK_GT:
-      return 0xC0000000; /* GT */
-  }
-  tcc_error("unexpected condition code");
-  return 0xE0000000; /* AL */
-}
-
-static int negcc(int cc)
-{
-  switch(cc)
-  {
-    case TOK_ULT:
-      return TOK_UGE;
-    case TOK_UGE:
-      return TOK_ULT;
-    case TOK_EQ:
-      return TOK_NE;
-    case TOK_NE:
-      return TOK_EQ;
-    case TOK_ULE:
-      return TOK_UGT;
-    case TOK_UGT:
-      return TOK_ULE;
-    case TOK_Nset:
-      return TOK_Nclear;
-    case TOK_Nclear:
-      return TOK_Nset;
-    case TOK_LT:
-      return TOK_GE;
-    case TOK_GE:
-      return TOK_LT;
-    case TOK_LE:
-      return TOK_GT;
-    case TOK_GT:
-      return TOK_LE;
-  }
-  tcc_error("unexpected condition code");
-  return TOK_NE;
-}
-
-/* load 'r' from value 'sv' */
-void load(int r, SValue *sv)
-{
-  int v, ft, fc, fr, sign;
-  uint32_t op;
-  SValue v1;
-
-  fr = sv->r;
-  ft = sv->type.t;
-  fc = sv->c.i;
-
-  if(fc>=0)
-    sign=0;
-  else {
-    sign=1;
-    fc=-fc;
-  }
-
-  v = fr & VT_VALMASK;
-  if (fr & VT_LVAL) {
-    uint32_t base = 0xB; // fp
-    if(v == VT_LLOCAL) {
-      v1.type.t = VT_PTR;
-      v1.r = VT_LOCAL | VT_LVAL;
-      v1.c.i = sv->c.i;
-      load(TREG_LR, &v1);
-      base = 14; /* lr */
-      fc=sign=0;
-      v=VT_LOCAL;
-    } else if(v == VT_CONST) {
-      v1.type.t = VT_PTR;
-      v1.r = fr&~VT_LVAL;
-      v1.c.i = sv->c.i;
-      v1.sym=sv->sym;
-      load(TREG_LR, &v1);
-      base = 14; /* lr */
-      fc=sign=0;
-      v=VT_LOCAL;
-    } else if(v < VT_CONST) {
-      base=intr(v);
-      fc=sign=0;
-      v=VT_LOCAL;
-    }
-    if(v == VT_LOCAL) {
-      if(is_float(ft)) {
-	calcaddr(&base,&fc,&sign,1020,2);
-#ifdef TCC_ARM_VFP
-        op=0xED100A00; /* flds */
-        if(!sign)
-          op|=0x800000;
-        if ((ft & VT_BTYPE) != VT_FLOAT)
-          op|=0x100;   /* flds -> fldd */
-        o(op|(vfpr(r)<<12)|(fc>>2)|(base<<16));
-#else
-	op=0xED100100;
-	if(!sign)
-	  op|=0x800000;
-#if LDOUBLE_SIZE == 8
-	if ((ft & VT_BTYPE) != VT_FLOAT)
-	  op|=0x8000;
-#else
-	if ((ft & VT_BTYPE) == VT_DOUBLE)
-	  op|=0x8000;
-	else if ((ft & VT_BTYPE) == VT_LDOUBLE)
-	  op|=0x400000;
-#endif
-	o(op|(fpr(r)<<12)|(fc>>2)|(base<<16));
-#endif
-      } else if((ft & (VT_BTYPE|VT_UNSIGNED)) == VT_BYTE
-                || (ft & VT_BTYPE) == VT_SHORT) {
-	calcaddr(&base,&fc,&sign,255,0);
-	op=0xE1500090;
-	if ((ft & VT_BTYPE) == VT_SHORT)
-	  op|=0x20;
-	if ((ft & VT_UNSIGNED) == 0)
-	  op|=0x40;
-	if(!sign)
-	  op|=0x800000;
-	o(op|(intr(r)<<12)|(base<<16)|((fc&0xf0)<<4)|(fc&0xf));
-      } else {
-	calcaddr(&base,&fc,&sign,4095,0);
-	op=0xE5100000;
-	if(!sign)
-	  op|=0x800000;
-        if ((ft & VT_BTYPE) == VT_BYTE || (ft & VT_BTYPE) == VT_BOOL)
-          op|=0x400000;
-        o(op|(intr(r)<<12)|fc|(base<<16));
-      }
-      return;
-    }
-  } else {
-    if (v == VT_CONST) {
-      op=stuff_const(0xE3A00000|(intr(r)<<12),sv->c.i);
-      if (fr & VT_SYM || !op) {
-        o(0xE59F0000|(intr(r)<<12));
-        o(0xEA000000);
-        if(fr & VT_SYM)
-	  greloc(cur_text_section, sv->sym, ind, R_ARM_ABS32);
-        o(sv->c.i);
-      } else
-        o(op);
-      return;
-    } else if (v == VT_LOCAL) {
-      op=stuff_const(0xE28B0000|(intr(r)<<12),sv->c.i);
-      if (fr & VT_SYM || !op) {
-	o(0xE59F0000|(intr(r)<<12));
-	o(0xEA000000);
-	if(fr & VT_SYM) // needed ?
-	  greloc(cur_text_section, sv->sym, ind, R_ARM_ABS32);
-	o(sv->c.i);
-	o(0xE08B0000|(intr(r)<<12)|intr(r));
-      } else
-	o(op);
-      return;
-    } else if(v == VT_CMP) {
-      o(mapcc(sv->c.i)|0x3A00001|(intr(r)<<12));
-      o(mapcc(negcc(sv->c.i))|0x3A00000|(intr(r)<<12));
-      return;
-    } else if (v == VT_JMP || v == VT_JMPI) {
-      int t;
-      t = v & 1;
-      o(0xE3A00000|(intr(r)<<12)|t);
-      o(0xEA000000);
-      gsym(sv->c.i);
-      o(0xE3A00000|(intr(r)<<12)|(t^1));
-      return;
-    } else if (v < VT_CONST) {
-      if(is_float(ft))
-#ifdef TCC_ARM_VFP
-        o(0xEEB00A40|(vfpr(r)<<12)|vfpr(v)|T2CPR(ft)); /* fcpyX */
-#else
-	o(0xEE008180|(fpr(r)<<12)|fpr(v));
-#endif
-      else
-	o(0xE1A00000|(intr(r)<<12)|intr(v));
-      return;
-    }
-  }
-  tcc_error("load unimplemented!");
-}
-
-/* store register 'r' in lvalue 'v' */
-void store(int r, SValue *sv)
-{
-  SValue v1;
-  int v, ft, fc, fr, sign;
-  uint32_t op;
-
-  fr = sv->r;
-  ft = sv->type.t;
-  fc = sv->c.i;
-
-  if(fc>=0)
-    sign=0;
-  else {
-    sign=1;
-    fc=-fc;
-  }
-
-  v = fr & VT_VALMASK;
-  if (fr & VT_LVAL || fr == VT_LOCAL) {
-    uint32_t base = 0xb; /* fp */
-    if(v < VT_CONST) {
-      base=intr(v);
-      v=VT_LOCAL;
-      fc=sign=0;
-    } else if(v == VT_CONST) {
-      v1.type.t = ft;
-      v1.r = fr&~VT_LVAL;
-      v1.c.i = sv->c.i;
-      v1.sym=sv->sym;
-      load(TREG_LR, &v1);
-      base = 14; /* lr */
-      fc=sign=0;
-      v=VT_LOCAL;
-    }
-    if(v == VT_LOCAL) {
-       if(is_float(ft)) {
-	calcaddr(&base,&fc,&sign,1020,2);
-#ifdef TCC_ARM_VFP
-        op=0xED000A00; /* fsts */
-        if(!sign)
-          op|=0x800000;
-        if ((ft & VT_BTYPE) != VT_FLOAT)
-          op|=0x100;   /* fsts -> fstd */
-        o(op|(vfpr(r)<<12)|(fc>>2)|(base<<16));
-#else
-	op=0xED000100;
-	if(!sign)
-	  op|=0x800000;
-#if LDOUBLE_SIZE == 8
-	if ((ft & VT_BTYPE) != VT_FLOAT)
-	  op|=0x8000;
-#else
-	if ((ft & VT_BTYPE) == VT_DOUBLE)
-	  op|=0x8000;
-	if ((ft & VT_BTYPE) == VT_LDOUBLE)
-	  op|=0x400000;
-#endif
-	o(op|(fpr(r)<<12)|(fc>>2)|(base<<16));
-#endif
-	return;
-      } else if((ft & VT_BTYPE) == VT_SHORT) {
-	calcaddr(&base,&fc,&sign,255,0);
-	op=0xE14000B0;
-	if(!sign)
-	  op|=0x800000;
-	o(op|(intr(r)<<12)|(base<<16)|((fc&0xf0)<<4)|(fc&0xf));
-      } else {
-	calcaddr(&base,&fc,&sign,4095,0);
-	op=0xE5000000;
-	if(!sign)
-	  op|=0x800000;
-        if ((ft & VT_BTYPE) == VT_BYTE || (ft & VT_BTYPE) == VT_BOOL)
-          op|=0x400000;
-        o(op|(intr(r)<<12)|fc|(base<<16));
-      }
-      return;
-    }
-  }
-  tcc_error("store unimplemented");
-}
-
-static void gadd_sp(int val)
-{
-  stuff_const_harder(0xE28DD000,val);
-}
-
-/* 'is_jmp' is '1' if it is a jump */
-static void gcall_or_jmp(int is_jmp)
-{
-  int r;
-  if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
-    uint32_t x;
-    /* constant case */
-    x=encbranch(ind,ind+vtop->c.i,0);
-    if(x) {
-      if (vtop->r & VT_SYM) {
-	/* relocation case */
-	greloc(cur_text_section, vtop->sym, ind, R_ARM_PC24);
-      } else
-	put_elf_reloc(symtab_section, cur_text_section, ind, R_ARM_PC24, 0);
-      o(x|(is_jmp?0xE0000000:0xE1000000));
-    } else {
-      if(!is_jmp)
-	o(0xE28FE004); // add lr,pc,#4
-      o(0xE51FF004);   // ldr pc,[pc,#-4]
-      if (vtop->r & VT_SYM)
-	greloc(cur_text_section, vtop->sym, ind, R_ARM_ABS32);
-      o(vtop->c.i);
-    }
-  } else {
-    /* otherwise, indirect call */
-    r = gv(RC_INT);
-    if(!is_jmp)
-      o(0xE1A0E00F);       // mov lr,pc
-    o(0xE1A0F000|intr(r)); // mov pc,r
-  }
-}
-
-static int unalias_ldbl(int btype)
-{
-#if LDOUBLE_SIZE == 8
-    if (btype == VT_LDOUBLE)
-      btype = VT_DOUBLE;
-#endif
-    return btype;
-}
-
-/* Return whether a structure is an homogeneous float aggregate or not.
-   The answer is true if all the elements of the structure are of the same
-   primitive float type and there is less than 4 elements.
-
-   type: the type corresponding to the structure to be tested */
-static int is_hgen_float_aggr(CType *type)
-{
-  if ((type->t & VT_BTYPE) == VT_STRUCT) {
-    struct Sym *ref;
-    int btype, nb_fields = 0;
-
-    ref = type->ref->next;
-    btype = unalias_ldbl(ref->type.t & VT_BTYPE);
-    if (btype == VT_FLOAT || btype == VT_DOUBLE) {
-      for(; ref && btype == unalias_ldbl(ref->type.t & VT_BTYPE); ref = ref->next, nb_fields++);
-      return !ref && nb_fields <= 4;
-    }
-  }
-  return 0;
-}
-
-struct avail_regs {
-  signed char avail[3]; /* 3 holes max with only float and double alignments */
-  int first_hole; /* first available hole */
-  int last_hole; /* last available hole (none if equal to first_hole) */
-  int first_free_reg; /* next free register in the sequence, hole excluded */
-};
-
-#define AVAIL_REGS_INITIALIZER (struct avail_regs) { { 0, 0, 0}, 0, 0, 0 }
-
-/* Find suitable registers for a VFP Co-Processor Register Candidate (VFP CPRC
-   param) according to the rules described in the procedure call standard for
-   the ARM architecture (AAPCS). If found, the registers are assigned to this
-   VFP CPRC parameter. Registers are allocated in sequence unless a hole exists
-   and the parameter is a single float.
-
-   avregs: opaque structure to keep track of available VFP co-processor regs
-   align: alignment constraints for the param, as returned by type_size()
-   size: size of the parameter, as returned by type_size() */
-int assign_vfpreg(struct avail_regs *avregs, int align, int size)
-{
-  int first_reg = 0;
-
-  if (avregs->first_free_reg == -1)
-    return -1;
-  if (align >> 3) { /* double alignment */
-    first_reg = avregs->first_free_reg;
-    /* alignment constraint not respected so use next reg and record hole */
-    if (first_reg & 1)
-      avregs->avail[avregs->last_hole++] = first_reg++;
-  } else { /* no special alignment (float or array of float) */
-    /* if single float and a hole is available, assign the param to it */
-    if (size == 4 && avregs->first_hole != avregs->last_hole)
-      return avregs->avail[avregs->first_hole++];
-    else
-      first_reg = avregs->first_free_reg;
-  }
-  if (first_reg + size / 4 <= 16) {
-    avregs->first_free_reg = first_reg + size / 4;
-    return first_reg;
-  }
-  avregs->first_free_reg = -1;
-  return -1;
-}
-
-/* Returns whether all params need to be passed in core registers or not.
-   This is the case for function part of the runtime ABI. */
-int floats_in_core_regs(SValue *sval)
-{
-  if (!sval->sym)
-    return 0;
-
-  switch (sval->sym->v) {
-    case TOK___floatundisf:
-    case TOK___floatundidf:
-    case TOK___fixunssfdi:
-    case TOK___fixunsdfdi:
-#ifndef TCC_ARM_VFP
-    case TOK___fixunsxfdi:
-#endif
-    case TOK___floatdisf:
-    case TOK___floatdidf:
-    case TOK___fixsfdi:
-    case TOK___fixdfdi:
-      return 1;
-
-    default:
-      return 0;
-  }
-}
-
-/* Return the number of registers needed to return the struct, or 0 if
-   returning via struct pointer. */
-ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) {
-#ifdef TCC_ARM_EABI
-    int size, align;
-    size = type_size(vt, &align);
-    if (float_abi == ARM_HARD_FLOAT && !variadic &&
-        (is_float(vt->t) || is_hgen_float_aggr(vt))) {
-        *ret_align = 8;
-	*regsize = 8;
-        ret->ref = NULL;
-        ret->t = VT_DOUBLE;
-        return (size + 7) >> 3;
-    } else if (size <= 4) {
-        *ret_align = 4;
-	*regsize = 4;
-        ret->ref = NULL;
-        ret->t = VT_INT;
-        return 1;
-    } else
-        return 0;
-#else
-    return 0;
-#endif
-}
-
-/* Parameters are classified according to how they are copied to their final
-   destination for the function call. Because the copying is performed class
-   after class according to the order in the union below, it is important that
-   some constraints about the order of the members of this union are respected:
-   - CORE_STRUCT_CLASS must come after STACK_CLASS;
-   - CORE_CLASS must come after STACK_CLASS, CORE_STRUCT_CLASS and
-     VFP_STRUCT_CLASS;
-   - VFP_STRUCT_CLASS must come after VFP_CLASS.
-   See the comment for the main loop in copy_params() for the reason. */
-enum reg_class {
-	STACK_CLASS = 0,
-	CORE_STRUCT_CLASS,
-	VFP_CLASS,
-	VFP_STRUCT_CLASS,
-	CORE_CLASS,
-	NB_CLASSES
-};
-
-struct param_plan {
-    int start; /* first reg or addr used depending on the class */
-    int end; /* last reg used or next free addr depending on the class */
-    SValue *sval; /* pointer to SValue on the value stack */
-    struct param_plan *prev; /*  previous element in this class */
-};
-
-struct plan {
-    struct param_plan *pplans; /* array of all the param plans */
-    struct param_plan *clsplans[NB_CLASSES]; /* per class lists of param plans */
-};
-
-#define add_param_plan(plan,pplan,class)                        \
-    do {                                                        \
-        pplan.prev = plan->clsplans[class];                     \
-        plan->pplans[plan ## _nb] = pplan;                      \
-        plan->clsplans[class] = &plan->pplans[plan ## _nb++];   \
-    } while(0)
-
-/* Assign parameters to registers and stack with alignment according to the
-   rules in the procedure call standard for the ARM architecture (AAPCS).
-   The overall assignment is recorded in an array of per parameter structures
-   called parameter plans. The parameter plans are also further organized in a
-   number of linked lists, one per class of parameter (see the comment for the
-   definition of union reg_class).
-
-   nb_args: number of parameters of the function for which a call is generated
-   float_abi: float ABI in use for this function call
-   plan: the structure where the overall assignment is recorded
-   todo: a bitmap that record which core registers hold a parameter
-
-   Returns the amount of stack space needed for parameter passing
-
-   Note: this function allocated an array in plan->pplans with tcc_malloc. It
-   is the responsibility of the caller to free this array once used (ie not
-   before copy_params). */
-static int assign_regs(int nb_args, int float_abi, struct plan *plan, int *todo)
-{
-  int i, size, align;
-  int ncrn /* next core register number */, nsaa /* next stacked argument address*/;
-  int plan_nb = 0;
-  struct param_plan pplan;
-  struct avail_regs avregs = AVAIL_REGS_INITIALIZER;
-
-  ncrn = nsaa = 0;
-  *todo = 0;
-  plan->pplans = tcc_malloc(nb_args * sizeof(*plan->pplans));
-  memset(plan->clsplans, 0, sizeof(plan->clsplans));
-  for(i = nb_args; i-- ;) {
-    int j, start_vfpreg = 0;
-    CType type = vtop[-i].type;
-    type.t &= ~VT_ARRAY;
-    size = type_size(&type, &align);
-    size = (size + 3) & ~3;
-    align = (align + 3) & ~3;
-    switch(vtop[-i].type.t & VT_BTYPE) {
-      case VT_STRUCT:
-      case VT_FLOAT:
-      case VT_DOUBLE:
-      case VT_LDOUBLE:
-      if (float_abi == ARM_HARD_FLOAT) {
-        int is_hfa = 0; /* Homogeneous float aggregate */
-
-        if (is_float(vtop[-i].type.t)
-            || (is_hfa = is_hgen_float_aggr(&vtop[-i].type))) {
-          int end_vfpreg;
-
-          start_vfpreg = assign_vfpreg(&avregs, align, size);
-          end_vfpreg = start_vfpreg + ((size - 1) >> 2);
-          if (start_vfpreg >= 0) {
-            pplan = (struct param_plan) {start_vfpreg, end_vfpreg, &vtop[-i]};
-            if (is_hfa)
-              add_param_plan(plan, pplan, VFP_STRUCT_CLASS);
-            else
-              add_param_plan(plan, pplan, VFP_CLASS);
-            continue;
-          } else
-            break;
-        }
-      }
-      ncrn = (ncrn + (align-1)/4) & ~((align/4) - 1);
-      if (ncrn + size/4 <= 4 || (ncrn < 4 && start_vfpreg != -1)) {
-        /* The parameter is allocated both in core register and on stack. As
-	 * such, it can be of either class: it would either be the last of
-	 * CORE_STRUCT_CLASS or the first of STACK_CLASS. */
-        for (j = ncrn; j < 4 && j < ncrn + size / 4; j++)
-          *todo|=(1<<j);
-        pplan = (struct param_plan) {ncrn, j, &vtop[-i]};
-        add_param_plan(plan, pplan, CORE_STRUCT_CLASS);
-        ncrn += size/4;
-        if (ncrn > 4)
-          nsaa = (ncrn - 4) * 4;
-      } else {
-        ncrn = 4;
-        break;
-      }
-      continue;
-      default:
-      if (ncrn < 4) {
-        int is_long = (vtop[-i].type.t & VT_BTYPE) == VT_LLONG;
-
-        if (is_long) {
-          ncrn = (ncrn + 1) & -2;
-          if (ncrn == 4)
-            break;
-        }
-        pplan = (struct param_plan) {ncrn, ncrn, &vtop[-i]};
-        ncrn++;
-        if (is_long)
-          pplan.end = ncrn++;
-        add_param_plan(plan, pplan, CORE_CLASS);
-        continue;
-      }
-    }
-    nsaa = (nsaa + (align - 1)) & ~(align - 1);
-    pplan = (struct param_plan) {nsaa, nsaa + size, &vtop[-i]};
-    add_param_plan(plan, pplan, STACK_CLASS);
-    nsaa += size; /* size already rounded up before */
-  }
-  return nsaa;
-}
-
-#undef add_param_plan
-
-/* Copy parameters to their final destination (core reg, VFP reg or stack) for
-   function call.
-
-   nb_args: number of parameters the function take
-   plan: the overall assignment plan for parameters
-   todo: a bitmap indicating what core reg will hold a parameter
-
-   Returns the number of SValue added by this function on the value stack */
-static int copy_params(int nb_args, struct plan *plan, int todo)
-{
-  int size, align, r, i, nb_extra_sval = 0;
-  struct param_plan *pplan;
-  int pass = 0;
-
-   /* Several constraints require parameters to be copied in a specific order:
-      - structures are copied to the stack before being loaded in a reg;
-      - floats loaded to an odd numbered VFP reg are first copied to the
-        preceding even numbered VFP reg and then moved to the next VFP reg.
-
-      It is thus important that:
-      - structures assigned to core regs must be copied after parameters
-        assigned to the stack but before structures assigned to VFP regs because
-        a structure can lie partly in core registers and partly on the stack;
-      - parameters assigned to the stack and all structures be copied before
-        parameters assigned to a core reg since copying a parameter to the stack
-        require using a core reg;
-      - parameters assigned to VFP regs be copied before structures assigned to
-        VFP regs as the copy might use an even numbered VFP reg that already
-        holds part of a structure. */
-again:
-  for(i = 0; i < NB_CLASSES; i++) {
-    for(pplan = plan->clsplans[i]; pplan; pplan = pplan->prev) {
-
-      if (pass
-          && (i != CORE_CLASS || pplan->sval->r < VT_CONST))
-        continue;
-
-      vpushv(pplan->sval);
-      pplan->sval->r = pplan->sval->r2 = VT_CONST; /* disable entry */
-      switch(i) {
-        case STACK_CLASS:
-        case CORE_STRUCT_CLASS:
-        case VFP_STRUCT_CLASS:
-          if ((pplan->sval->type.t & VT_BTYPE) == VT_STRUCT) {
-            int padding = 0;
-            size = type_size(&pplan->sval->type, &align);
-            /* align to stack align size */
-            size = (size + 3) & ~3;
-            if (i == STACK_CLASS && pplan->prev)
-              padding = pplan->start - pplan->prev->end;
-            size += padding; /* Add padding if any */
-            /* allocate the necessary size on stack */
-            gadd_sp(-size);
-            /* generate structure store */
-            r = get_reg(RC_INT);
-            o(0xE28D0000|(intr(r)<<12)|padding); /* add r, sp, padding */
-            vset(&vtop->type, r | VT_LVAL, 0);
-            vswap();
-            vstore(); /* memcpy to current sp + potential padding */
-
-            /* Homogeneous float aggregate are loaded to VFP registers
-               immediately since there is no way of loading data in multiple
-               non consecutive VFP registers as what is done for other
-               structures (see the use of todo). */
-            if (i == VFP_STRUCT_CLASS) {
-              int first = pplan->start, nb = pplan->end - first + 1;
-              /* vpop.32 {pplan->start, ..., pplan->end} */
-              o(0xECBD0A00|(first&1)<<22|(first>>1)<<12|nb);
-              /* No need to write the register used to a SValue since VFP regs
-                 cannot be used for gcall_or_jmp */
-            }
-          } else {
-            if (is_float(pplan->sval->type.t)) {
-#ifdef TCC_ARM_VFP
-              r = vfpr(gv(RC_FLOAT)) << 12;
-              if ((pplan->sval->type.t & VT_BTYPE) == VT_FLOAT)
-                size = 4;
-              else {
-                size = 8;
-                r |= 0x101; /* vpush.32 -> vpush.64 */
-              }
-              o(0xED2D0A01 + r); /* vpush */
-#else
-              r = fpr(gv(RC_FLOAT)) << 12;
-              if ((pplan->sval->type.t & VT_BTYPE) == VT_FLOAT)
-                size = 4;
-              else if ((pplan->sval->type.t & VT_BTYPE) == VT_DOUBLE)
-                size = 8;
-              else
-                size = LDOUBLE_SIZE;
-
-              if (size == 12)
-                r |= 0x400000;
-              else if(size == 8)
-                r|=0x8000;
-
-              o(0xED2D0100|r|(size>>2)); /* some kind of vpush for FPA */
-#endif
-            } else {
-              /* simple type (currently always same size) */
-              /* XXX: implicit cast ? */
-              size=4;
-              if ((pplan->sval->type.t & VT_BTYPE) == VT_LLONG) {
-                lexpand_nr();
-                size = 8;
-                r = gv(RC_INT);
-                o(0xE52D0004|(intr(r)<<12)); /* push r */
-                vtop--;
-              }
-              r = gv(RC_INT);
-              o(0xE52D0004|(intr(r)<<12)); /* push r */
-            }
-            if (i == STACK_CLASS && pplan->prev)
-              gadd_sp(pplan->prev->end - pplan->start); /* Add padding if any */
-          }
-          break;
-
-        case VFP_CLASS:
-          gv(regmask(TREG_F0 + (pplan->start >> 1)));
-          if (pplan->start & 1) { /* Must be in upper part of double register */
-            o(0xEEF00A40|((pplan->start>>1)<<12)|(pplan->start>>1)); /* vmov.f32 s(n+1), sn */
-            vtop->r = VT_CONST; /* avoid being saved on stack by gv for next float */
-          }
-          break;
-
-        case CORE_CLASS:
-          if ((pplan->sval->type.t & VT_BTYPE) == VT_LLONG) {
-            lexpand_nr();
-            gv(regmask(pplan->end));
-            pplan->sval->r2 = vtop->r;
-            vtop--;
-          }
-          gv(regmask(pplan->start));
-          /* Mark register as used so that gcall_or_jmp use another one
-             (regs >=4 are free as never used to pass parameters) */
-          pplan->sval->r = vtop->r;
-          break;
-      }
-      vtop--;
-    }
-  }
-
-  /* second pass to restore registers that were saved on stack by accident.
-     Maybe redundant after the "lvalue_save" patch in tccgen.c:gv() */
-  if (++pass < 2)
-    goto again;
-
-  /* Manually free remaining registers since next parameters are loaded
-   * manually, without the help of gv(int). */
-  save_regs(nb_args);
-
-  if(todo) {
-    o(0xE8BD0000|todo); /* pop {todo} */
-    for(pplan = plan->clsplans[CORE_STRUCT_CLASS]; pplan; pplan = pplan->prev) {
-      int r;
-      pplan->sval->r = pplan->start;
-      /* An SValue can only pin 2 registers at best (r and r2) but a structure
-         can occupy more than 2 registers. Thus, we need to push on the value
-         stack some fake parameter to have on SValue for each registers used
-         by a structure (r2 is not used). */
-      for (r = pplan->start + 1; r <= pplan->end; r++) {
-        if (todo & (1 << r)) {
-          nb_extra_sval++;
-          vpushi(0);
-          vtop->r = r;
-        }
-      }
-    }
-  }
-  return nb_extra_sval;
-}
-
-/* Generate function call. The function address is pushed first, then
-   all the parameters in call order. This functions pops all the
-   parameters and the function address. */
-void gfunc_call(int nb_args)
-{
-  int r, args_size;
-  int def_float_abi = float_abi;
-  int todo;
-  struct plan plan;
-
-#ifdef TCC_ARM_EABI
-  int variadic;
-
-  if (float_abi == ARM_HARD_FLOAT) {
-    variadic = (vtop[-nb_args].type.ref->f.func_type == FUNC_ELLIPSIS);
-    if (variadic || floats_in_core_regs(&vtop[-nb_args]))
-      float_abi = ARM_SOFTFP_FLOAT;
-  }
-#endif
-  /* cannot let cpu flags if other instruction are generated. Also avoid leaving
-     VT_JMP anywhere except on the top of the stack because it would complicate
-     the code generator. */
-  r = vtop->r & VT_VALMASK;
-  if (r == VT_CMP || (r & ~1) == VT_JMP)
-    gv(RC_INT);
-
-  args_size = assign_regs(nb_args, float_abi, &plan, &todo);
-
-#ifdef TCC_ARM_EABI
-  if (args_size & 7) { /* Stack must be 8 byte aligned at fct call for EABI */
-    args_size = (args_size + 7) & ~7;
-    o(0xE24DD004); /* sub sp, sp, #4 */
-  }
-#endif
-
-  nb_args += copy_params(nb_args, &plan, todo);
-  tcc_free(plan.pplans);
-
-  /* Move fct SValue on top as required by gcall_or_jmp */
-  vrotb(nb_args + 1);
-  gcall_or_jmp(0);
-  if (args_size)
-      gadd_sp(args_size); /* pop all parameters passed on the stack */
-#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP)
-  if(float_abi == ARM_SOFTFP_FLOAT && is_float(vtop->type.ref->type.t)) {
-    if((vtop->type.ref->type.t & VT_BTYPE) == VT_FLOAT) {
-      o(0xEE000A10); /*vmov s0, r0 */
-    } else {
-      o(0xEE000B10); /* vmov.32 d0[0], r0 */
-      o(0xEE201B10); /* vmov.32 d0[1], r1 */
-    }
-  }
-#endif
-  vtop -= nb_args + 1; /* Pop all params and fct address from value stack */
-  leaffunc = 0; /* we are calling a function, so we aren't in a leaf function */
-  float_abi = def_float_abi;
-}
-
-/* generate function prolog of type 't' */
-void gfunc_prolog(CType *func_type)
-{
-  Sym *sym,*sym2;
-  int n, nf, size, align, rs, struct_ret = 0;
-  int addr, pn, sn; /* pn=core, sn=stack */
-  CType ret_type;
-
-#ifdef TCC_ARM_EABI
-  struct avail_regs avregs = AVAIL_REGS_INITIALIZER;
-#endif
-
-  sym = func_type->ref;
-  func_vt = sym->type;
-  func_var = (func_type->ref->f.func_type == FUNC_ELLIPSIS);
-
-  n = nf = 0;
-  if ((func_vt.t & VT_BTYPE) == VT_STRUCT &&
-      !gfunc_sret(&func_vt, func_var, &ret_type, &align, &rs))
-  {
-    n++;
-    struct_ret = 1;
-    func_vc = 12; /* Offset from fp of the place to store the result */
-  }
-  for(sym2 = sym->next; sym2 && (n < 4 || nf < 16); sym2 = sym2->next) {
-    size = type_size(&sym2->type, &align);
-#ifdef TCC_ARM_EABI
-    if (float_abi == ARM_HARD_FLOAT && !func_var &&
-        (is_float(sym2->type.t) || is_hgen_float_aggr(&sym2->type))) {
-      int tmpnf = assign_vfpreg(&avregs, align, size);
-      tmpnf += (size + 3) / 4;
-      nf = (tmpnf > nf) ? tmpnf : nf;
-    } else
-#endif
-    if (n < 4)
-      n += (size + 3) / 4;
-  }
-  o(0xE1A0C00D); /* mov ip,sp */
-  if (func_var)
-    n=4;
-  if (n) {
-    if(n>4)
-      n=4;
-#ifdef TCC_ARM_EABI
-    n=(n+1)&-2;
-#endif
-    o(0xE92D0000|((1<<n)-1)); /* save r0-r4 on stack if needed */
-  }
-  if (nf) {
-    if (nf>16)
-      nf=16;
-    nf=(nf+1)&-2; /* nf => HARDFLOAT => EABI */
-    o(0xED2D0A00|nf); /* save s0-s15 on stack if needed */
-  }
-  o(0xE92D5800); /* save fp, ip, lr */
-  o(0xE1A0B00D); /* mov fp, sp */
-  func_sub_sp_offset = ind;
-  o(0xE1A00000); /* nop, leave space for stack adjustment in epilog */
-
-#ifdef TCC_ARM_EABI
-  if (float_abi == ARM_HARD_FLOAT) {
-    func_vc += nf * 4;
-    avregs = AVAIL_REGS_INITIALIZER;
-  }
-#endif
-  pn = struct_ret, sn = 0;
-  while ((sym = sym->next)) {
-    CType *type;
-    type = &sym->type;
-    size = type_size(type, &align);
-    size = (size + 3) >> 2;
-    align = (align + 3) & ~3;
-#ifdef TCC_ARM_EABI
-    if (float_abi == ARM_HARD_FLOAT && !func_var && (is_float(sym->type.t)
-        || is_hgen_float_aggr(&sym->type))) {
-      int fpn = assign_vfpreg(&avregs, align, size << 2);
-      if (fpn >= 0)
-        addr = fpn * 4;
-      else
-        goto from_stack;
-    } else
-#endif
-    if (pn < 4) {
-#ifdef TCC_ARM_EABI
-        pn = (pn + (align-1)/4) & -(align/4);
-#endif
-      addr = (nf + pn) * 4;
-      pn += size;
-      if (!sn && pn > 4)
-        sn = (pn - 4);
-    } else {
-#ifdef TCC_ARM_EABI
-from_stack:
-        sn = (sn + (align-1)/4) & -(align/4);
-#endif
-      addr = (n + nf + sn) * 4;
-      sn += size;
-    }
-    sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | lvalue_type(type->t),
-             addr + 12);
-  }
-  last_itod_magic=0;
-  leaffunc = 1;
-  loc = 0;
-}
-
-/* generate function epilog */
-void gfunc_epilog(void)
-{
-  uint32_t x;
-  int diff;
-  /* Copy float return value to core register if base standard is used and
-     float computation is made with VFP */
-#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP)
-  if ((float_abi == ARM_SOFTFP_FLOAT || func_var) && is_float(func_vt.t)) {
-    if((func_vt.t & VT_BTYPE) == VT_FLOAT)
-      o(0xEE100A10); /* fmrs r0, s0 */
-    else {
-      o(0xEE100B10); /* fmrdl r0, d0 */
-      o(0xEE301B10); /* fmrdh r1, d0 */
-    }
-  }
-#endif
-  o(0xE89BA800); /* restore fp, sp, pc */
-  diff = (-loc + 3) & -4;
-#ifdef TCC_ARM_EABI
-  if(!leaffunc)
-    diff = ((diff + 11) & -8) - 4;
-#endif
-  if(diff > 0) {
-    x=stuff_const(0xE24BD000, diff); /* sub sp,fp,# */
-    if(x)
-      *(uint32_t *)(cur_text_section->data + func_sub_sp_offset) = x;
-    else {
-      int addr;
-      addr=ind;
-      o(0xE59FC004); /* ldr ip,[pc+4] */
-      o(0xE04BD00C); /* sub sp,fp,ip  */
-      o(0xE1A0F00E); /* mov pc,lr */
-      o(diff);
-      *(uint32_t *)(cur_text_section->data + func_sub_sp_offset) = 0xE1000000|encbranch(func_sub_sp_offset,addr,1);
-    }
-  }
-}
-
-/* generate a jump to a label */
-int gjmp(int t)
-{
-  int r;
-  if (nocode_wanted)
-    return t;
-  r=ind;
-  o(0xE0000000|encbranch(r,t,1));
-  return r;
-}
-
-/* generate a jump to a fixed address */
-void gjmp_addr(int a)
-{
-  gjmp(a);
-}
-
-/* generate a test. set 'inv' to invert test. Stack entry is popped */
-int gtst(int inv, int t)
-{
-  int v, r;
-  uint32_t op;
-
-  v = vtop->r & VT_VALMASK;
-  r=ind;
-
-  if (nocode_wanted) {
-    ;
-  } else if (v == VT_CMP) {
-    op=mapcc(inv?negcc(vtop->c.i):vtop->c.i);
-    op|=encbranch(r,t,1);
-    o(op);
-    t=r;
-  } else if (v == VT_JMP || v == VT_JMPI) {
-    if ((v & 1) == inv) {
-      if(!vtop->c.i)
-	vtop->c.i=t;
-      else {
-	uint32_t *x;
-	int p,lp;
-	if(t) {
-          p = vtop->c.i;
-          do {
-	    p = decbranch(lp=p);
-          } while(p);
-	  x = (uint32_t *)(cur_text_section->data + lp);
-	  *x &= 0xff000000;
-	  *x |= encbranch(lp,t,1);
-	}
-	t = vtop->c.i;
-      }
-    } else {
-      t = gjmp(t);
-      gsym(vtop->c.i);
-    }
-  }
-  vtop--;
-  return t;
-}
-
-/* generate an integer binary operation */
-void gen_opi(int op)
-{
-  int c, func = 0;
-  uint32_t opc = 0, r, fr;
-  unsigned short retreg = REG_IRET;
-
-  c=0;
-  switch(op) {
-    case '+':
-      opc = 0x8;
-      c=1;
-      break;
-    case TOK_ADDC1: /* add with carry generation */
-      opc = 0x9;
-      c=1;
-      break;
-    case '-':
-      opc = 0x4;
-      c=1;
-      break;
-    case TOK_SUBC1: /* sub with carry generation */
-      opc = 0x5;
-      c=1;
-      break;
-    case TOK_ADDC2: /* add with carry use */
-      opc = 0xA;
-      c=1;
-      break;
-    case TOK_SUBC2: /* sub with carry use */
-      opc = 0xC;
-      c=1;
-      break;
-    case '&':
-      opc = 0x0;
-      c=1;
-      break;
-    case '^':
-      opc = 0x2;
-      c=1;
-      break;
-    case '|':
-      opc = 0x18;
-      c=1;
-      break;
-    case '*':
-      gv2(RC_INT, RC_INT);
-      r = vtop[-1].r;
-      fr = vtop[0].r;
-      vtop--;
-      o(0xE0000090|(intr(r)<<16)|(intr(r)<<8)|intr(fr));
-      return;
-    case TOK_SHL:
-      opc = 0;
-      c=2;
-      break;
-    case TOK_SHR:
-      opc = 1;
-      c=2;
-      break;
-    case TOK_SAR:
-      opc = 2;
-      c=2;
-      break;
-    case '/':
-    case TOK_PDIV:
-      func=TOK___divsi3;
-      c=3;
-      break;
-    case TOK_UDIV:
-      func=TOK___udivsi3;
-      c=3;
-      break;
-    case '%':
-#ifdef TCC_ARM_EABI
-      func=TOK___aeabi_idivmod;
-      retreg=REG_LRET;
-#else
-      func=TOK___modsi3;
-#endif
-      c=3;
-      break;
-    case TOK_UMOD:
-#ifdef TCC_ARM_EABI
-      func=TOK___aeabi_uidivmod;
-      retreg=REG_LRET;
-#else
-      func=TOK___umodsi3;
-#endif
-      c=3;
-      break;
-    case TOK_UMULL:
-      gv2(RC_INT, RC_INT);
-      r=intr(vtop[-1].r2=get_reg(RC_INT));
-      c=vtop[-1].r;
-      vtop[-1].r=get_reg_ex(RC_INT,regmask(c));
-      vtop--;
-      o(0xE0800090|(r<<16)|(intr(vtop->r)<<12)|(intr(c)<<8)|intr(vtop[1].r));
-      return;
-    default:
-      opc = 0x15;
-      c=1;
-      break;
-  }
-  switch(c) {
-    case 1:
-      if((vtop[-1].r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
-	if(opc == 4 || opc == 5 || opc == 0xc) {
-	  vswap();
-	  opc|=2; // sub -> rsb
-	}
-      }
-      if ((vtop->r & VT_VALMASK) == VT_CMP ||
-          (vtop->r & (VT_VALMASK & ~1)) == VT_JMP)
-        gv(RC_INT);
-      vswap();
-      c=intr(gv(RC_INT));
-      vswap();
-      opc=0xE0000000|(opc<<20)|(c<<16);
-      if((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
-	uint32_t x;
-	x=stuff_const(opc|0x2000000,vtop->c.i);
-	if(x) {
-	  r=intr(vtop[-1].r=get_reg_ex(RC_INT,regmask(vtop[-1].r)));
-	  o(x|(r<<12));
-	  goto done;
-	}
-      }
-      fr=intr(gv(RC_INT));
-      r=intr(vtop[-1].r=get_reg_ex(RC_INT,two2mask(vtop->r,vtop[-1].r)));
-      o(opc|(r<<12)|fr);
-done:
-      vtop--;
-      if (op >= TOK_ULT && op <= TOK_GT) {
-        vtop->r = VT_CMP;
-        vtop->c.i = op;
-      }
-      break;
-    case 2:
-      opc=0xE1A00000|(opc<<5);
-      if ((vtop->r & VT_VALMASK) == VT_CMP ||
-          (vtop->r & (VT_VALMASK & ~1)) == VT_JMP)
-        gv(RC_INT);
-      vswap();
-      r=intr(gv(RC_INT));
-      vswap();
-      opc|=r;
-      if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
-	fr=intr(vtop[-1].r=get_reg_ex(RC_INT,regmask(vtop[-1].r)));
-	c = vtop->c.i & 0x1f;
-	o(opc|(c<<7)|(fr<<12));
-      } else {
-        fr=intr(gv(RC_INT));
-	c=intr(vtop[-1].r=get_reg_ex(RC_INT,two2mask(vtop->r,vtop[-1].r)));
-	o(opc|(c<<12)|(fr<<8)|0x10);
-      }
-      vtop--;
-      break;
-    case 3:
-      vpush_global_sym(&func_old_type, func);
-      vrott(3);
-      gfunc_call(2);
-      vpushi(0);
-      vtop->r = retreg;
-      break;
-    default:
-      tcc_error("gen_opi %i unimplemented!",op);
-  }
-}
-
-#ifdef TCC_ARM_VFP
-static int is_zero(int i)
-{
-  if((vtop[i].r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
-    return 0;
-  if (vtop[i].type.t == VT_FLOAT)
-    return (vtop[i].c.f == 0.f);
-  else if (vtop[i].type.t == VT_DOUBLE)
-    return (vtop[i].c.d == 0.0);
-  return (vtop[i].c.ld == 0.l);
-}
-
-/* generate a floating point operation 'v = t1 op t2' instruction. The
- *    two operands are guaranteed to have the same floating point type */
-void gen_opf(int op)
-{
-  uint32_t x;
-  int fneg=0,r;
-  x=0xEE000A00|T2CPR(vtop->type.t);
-  switch(op) {
-    case '+':
-      if(is_zero(-1))
-        vswap();
-      if(is_zero(0)) {
-        vtop--;
-        return;
-      }
-      x|=0x300000;
-      break;
-    case '-':
-      x|=0x300040;
-      if(is_zero(0)) {
-        vtop--;
-        return;
-      }
-      if(is_zero(-1)) {
-        x|=0x810000; /* fsubX -> fnegX */
-        vswap();
-        vtop--;
-        fneg=1;
-      }
-      break;
-    case '*':
-      x|=0x200000;
-      break;
-    case '/':
-      x|=0x800000;
-      break;
-    default:
-      if(op < TOK_ULT || op > TOK_GT) {
-        tcc_error("unknown fp op %x!",op);
-        return;
-      }
-      if(is_zero(-1)) {
-        vswap();
-        switch(op) {
-          case TOK_LT: op=TOK_GT; break;
-          case TOK_GE: op=TOK_ULE; break;
-          case TOK_LE: op=TOK_GE; break;
-          case TOK_GT: op=TOK_ULT; break;
-        }
-      }
-      x|=0xB40040; /* fcmpX */
-      if(op!=TOK_EQ && op!=TOK_NE)
-        x|=0x80; /* fcmpX -> fcmpeX */
-      if(is_zero(0)) {
-        vtop--;
-        o(x|0x10000|(vfpr(gv(RC_FLOAT))<<12)); /* fcmp(e)X -> fcmp(e)zX */
-      } else {
-        x|=vfpr(gv(RC_FLOAT));
-        vswap();
-        o(x|(vfpr(gv(RC_FLOAT))<<12));
-        vtop--;
-      }
-      o(0xEEF1FA10); /* fmstat */
-
-      switch(op) {
-        case TOK_LE: op=TOK_ULE; break;
-        case TOK_LT: op=TOK_ULT; break;
-        case TOK_UGE: op=TOK_GE; break;
-        case TOK_UGT: op=TOK_GT; break;
-      }
-
-      vtop->r = VT_CMP;
-      vtop->c.i = op;
-      return;
-  }
-  r=gv(RC_FLOAT);
-  x|=vfpr(r);
-  r=regmask(r);
-  if(!fneg) {
-    int r2;
-    vswap();
-    r2=gv(RC_FLOAT);
-    x|=vfpr(r2)<<16;
-    r|=regmask(r2);
-  }
-  vtop->r=get_reg_ex(RC_FLOAT,r);
-  if(!fneg)
-    vtop--;
-  o(x|(vfpr(vtop->r)<<12));
-}
-
-#else
-static uint32_t is_fconst()
-{
-  long double f;
-  uint32_t r;
-  if((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
-    return 0;
-  if (vtop->type.t == VT_FLOAT)
-    f = vtop->c.f;
-  else if (vtop->type.t == VT_DOUBLE)
-    f = vtop->c.d;
-  else
-    f = vtop->c.ld;
-  if(!ieee_finite(f))
-    return 0;
-  r=0x8;
-  if(f<0.0) {
-    r=0x18;
-    f=-f;
-  }
-  if(f==0.0)
-    return r;
-  if(f==1.0)
-    return r|1;
-  if(f==2.0)
-    return r|2;
-  if(f==3.0)
-    return r|3;
-  if(f==4.0)
-    return r|4;
-  if(f==5.0)
-    return r|5;
-  if(f==0.5)
-    return r|6;
-  if(f==10.0)
-    return r|7;
-  return 0;
-}
-
-/* generate a floating point operation 'v = t1 op t2' instruction. The
-   two operands are guaranteed to have the same floating point type */
-void gen_opf(int op)
-{
-  uint32_t x, r, r2, c1, c2;
-  //fputs("gen_opf\n",stderr);
-  vswap();
-  c1 = is_fconst();
-  vswap();
-  c2 = is_fconst();
-  x=0xEE000100;
-#if LDOUBLE_SIZE == 8
-  if ((vtop->type.t & VT_BTYPE) != VT_FLOAT)
-    x|=0x80;
-#else
-  if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE)
-    x|=0x80;
-  else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE)
-    x|=0x80000;
-#endif
-  switch(op)
-  {
-    case '+':
-      if(!c2) {
-	vswap();
-	c2=c1;
-      }
-      vswap();
-      r=fpr(gv(RC_FLOAT));
-      vswap();
-      if(c2) {
-	if(c2>0xf)
-	  x|=0x200000; // suf
-	r2=c2&0xf;
-      } else {
-	r2=fpr(gv(RC_FLOAT));
-      }
-      break;
-    case '-':
-      if(c2) {
-	if(c2<=0xf)
-	  x|=0x200000; // suf
-	r2=c2&0xf;
-	vswap();
-	r=fpr(gv(RC_FLOAT));
-	vswap();
-      } else if(c1 && c1<=0xf) {
-	x|=0x300000; // rsf
-	r2=c1;
-	r=fpr(gv(RC_FLOAT));
-	vswap();
-      } else {
-	x|=0x200000; // suf
-	vswap();
-	r=fpr(gv(RC_FLOAT));
-	vswap();
-	r2=fpr(gv(RC_FLOAT));
-      }
-      break;
-    case '*':
-      if(!c2 || c2>0xf) {
-	vswap();
-	c2=c1;
-      }
-      vswap();
-      r=fpr(gv(RC_FLOAT));
-      vswap();
-      if(c2 && c2<=0xf)
-	r2=c2;
-      else
-	r2=fpr(gv(RC_FLOAT));
-      x|=0x100000; // muf
-      break;
-    case '/':
-      if(c2 && c2<=0xf) {
-	x|=0x400000; // dvf
-	r2=c2;
-	vswap();
-	r=fpr(gv(RC_FLOAT));
-	vswap();
-      } else if(c1 && c1<=0xf) {
-	x|=0x500000; // rdf
-	r2=c1;
-	r=fpr(gv(RC_FLOAT));
-	vswap();
-      } else {
-	x|=0x400000; // dvf
-	vswap();
-	r=fpr(gv(RC_FLOAT));
-	vswap();
-	r2=fpr(gv(RC_FLOAT));
-      }
-      break;
-    default:
-      if(op >= TOK_ULT && op <= TOK_GT) {
-	x|=0xd0f110; // cmfe
-/* bug (intention?) in Linux FPU emulator
-   doesn't set carry if equal */
-	switch(op) {
-	  case TOK_ULT:
-	  case TOK_UGE:
-	  case TOK_ULE:
-	  case TOK_UGT:
-            tcc_error("unsigned comparison on floats?");
-	    break;
-	  case TOK_LT:
-            op=TOK_Nset;
-	    break;
-	  case TOK_LE:
-            op=TOK_ULE; /* correct in unordered case only if AC bit in FPSR set */
-	    break;
-	  case TOK_EQ:
-	  case TOK_NE:
-	    x&=~0x400000; // cmfe -> cmf
-	    break;
-	}
-	if(c1 && !c2) {
-	  c2=c1;
-	  vswap();
-	  switch(op) {
-            case TOK_Nset:
-              op=TOK_GT;
-	      break;
-            case TOK_GE:
-	      op=TOK_ULE;
-	      break;
-	    case TOK_ULE:
-              op=TOK_GE;
-	      break;
-            case TOK_GT:
-              op=TOK_Nset;
-	      break;
-	  }
-	}
-	vswap();
-	r=fpr(gv(RC_FLOAT));
-	vswap();
-	if(c2) {
-	  if(c2>0xf)
-	    x|=0x200000;
-	  r2=c2&0xf;
-	} else {
-	  r2=fpr(gv(RC_FLOAT));
-	}
-	vtop[-1].r = VT_CMP;
-	vtop[-1].c.i = op;
-      } else {
-        tcc_error("unknown fp op %x!",op);
-	return;
-      }
-  }
-  if(vtop[-1].r == VT_CMP)
-    c1=15;
-  else {
-    c1=vtop->r;
-    if(r2&0x8)
-      c1=vtop[-1].r;
-    vtop[-1].r=get_reg_ex(RC_FLOAT,two2mask(vtop[-1].r,c1));
-    c1=fpr(vtop[-1].r);
-  }
-  vtop--;
-  o(x|(r<<16)|(c1<<12)|r2);
-}
-#endif
-
-/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
-   and 'long long' cases. */
-ST_FUNC void gen_cvt_itof1(int t)
-{
-  uint32_t r, r2;
-  int bt;
-  bt=vtop->type.t & VT_BTYPE;
-  if(bt == VT_INT || bt == VT_SHORT || bt == VT_BYTE) {
-#ifndef TCC_ARM_VFP
-    uint32_t dsize = 0;
-#endif
-    r=intr(gv(RC_INT));
-#ifdef TCC_ARM_VFP
-    r2=vfpr(vtop->r=get_reg(RC_FLOAT));
-    o(0xEE000A10|(r<<12)|(r2<<16)); /* fmsr */
-    r2|=r2<<12;
-    if(!(vtop->type.t & VT_UNSIGNED))
-      r2|=0x80;                /* fuitoX -> fsituX */
-    o(0xEEB80A40|r2|T2CPR(t)); /* fYitoX*/
-#else
-    r2=fpr(vtop->r=get_reg(RC_FLOAT));
-    if((t & VT_BTYPE) != VT_FLOAT)
-      dsize=0x80;    /* flts -> fltd */
-    o(0xEE000110|dsize|(r2<<16)|(r<<12)); /* flts */
-    if((vtop->type.t & (VT_UNSIGNED|VT_BTYPE)) == (VT_UNSIGNED|VT_INT)) {
-      uint32_t off = 0;
-      o(0xE3500000|(r<<12));        /* cmp */
-      r=fpr(get_reg(RC_FLOAT));
-      if(last_itod_magic) {
-	off=ind+8-last_itod_magic;
-	off/=4;
-	if(off>255)
-	  off=0;
-      }
-      o(0xBD1F0100|(r<<12)|off);    /* ldflts */
-      if(!off) {
-        o(0xEA000000);              /* b */
-        last_itod_magic=ind;
-        o(0x4F800000);              /* 4294967296.0f */
-      }
-      o(0xBE000100|dsize|(r2<<16)|(r2<<12)|r); /* adflt */
-    }
-#endif
-    return;
-  } else if(bt == VT_LLONG) {
-    int func;
-    CType *func_type = 0;
-    if((t & VT_BTYPE) == VT_FLOAT) {
-      func_type = &func_float_type;
-      if(vtop->type.t & VT_UNSIGNED)
-        func=TOK___floatundisf;
-      else
-        func=TOK___floatdisf;
-#if LDOUBLE_SIZE != 8
-    } else if((t & VT_BTYPE) == VT_LDOUBLE) {
-      func_type = &func_ldouble_type;
-      if(vtop->type.t & VT_UNSIGNED)
-        func=TOK___floatundixf;
-      else
-        func=TOK___floatdixf;
-    } else if((t & VT_BTYPE) == VT_DOUBLE) {
-#else
-    } else if((t & VT_BTYPE) == VT_DOUBLE || (t & VT_BTYPE) == VT_LDOUBLE) {
-#endif
-      func_type = &func_double_type;
-      if(vtop->type.t & VT_UNSIGNED)
-        func=TOK___floatundidf;
-      else
-        func=TOK___floatdidf;
-    }
-    if(func_type) {
-      vpush_global_sym(func_type, func);
-      vswap();
-      gfunc_call(1);
-      vpushi(0);
-      vtop->r=TREG_F0;
-      return;
-    }
-  }
-  tcc_error("unimplemented gen_cvt_itof %x!",vtop->type.t);
-}
-
-/* convert fp to int 't' type */
-void gen_cvt_ftoi(int t)
-{
-  uint32_t r, r2;
-  int u, func = 0;
-  u=t&VT_UNSIGNED;
-  t&=VT_BTYPE;
-  r2=vtop->type.t & VT_BTYPE;
-  if(t==VT_INT) {
-#ifdef TCC_ARM_VFP
-    r=vfpr(gv(RC_FLOAT));
-    u=u?0:0x10000;
-    o(0xEEBC0AC0|(r<<12)|r|T2CPR(r2)|u); /* ftoXizY */
-    r2=intr(vtop->r=get_reg(RC_INT));
-    o(0xEE100A10|(r<<16)|(r2<<12));
-    return;
-#else
-    if(u) {
-      if(r2 == VT_FLOAT)
-        func=TOK___fixunssfsi;
-#if LDOUBLE_SIZE != 8
-      else if(r2 == VT_LDOUBLE)
-	func=TOK___fixunsxfsi;
-      else if(r2 == VT_DOUBLE)
-#else
-      else if(r2 == VT_LDOUBLE || r2 == VT_DOUBLE)
-#endif
-	func=TOK___fixunsdfsi;
-    } else {
-      r=fpr(gv(RC_FLOAT));
-      r2=intr(vtop->r=get_reg(RC_INT));
-      o(0xEE100170|(r2<<12)|r);
-      return;
-    }
-#endif
-  } else if(t == VT_LLONG) { // unsigned handled in gen_cvt_ftoi1
-    if(r2 == VT_FLOAT)
-      func=TOK___fixsfdi;
-#if LDOUBLE_SIZE != 8
-    else if(r2 == VT_LDOUBLE)
-      func=TOK___fixxfdi;
-    else if(r2 == VT_DOUBLE)
-#else
-    else if(r2 == VT_LDOUBLE || r2 == VT_DOUBLE)
-#endif
-      func=TOK___fixdfdi;
-  }
-  if(func) {
-    vpush_global_sym(&func_old_type, func);
-    vswap();
-    gfunc_call(1);
-    vpushi(0);
-    if(t == VT_LLONG)
-      vtop->r2 = REG_LRET;
-    vtop->r = REG_IRET;
-    return;
-  }
-  tcc_error("unimplemented gen_cvt_ftoi!");
-}
-
-/* convert from one floating point type to another */
-void gen_cvt_ftof(int t)
-{
-#ifdef TCC_ARM_VFP
-  if(((vtop->type.t & VT_BTYPE) == VT_FLOAT) != ((t & VT_BTYPE) == VT_FLOAT)) {
-    uint32_t r = vfpr(gv(RC_FLOAT));
-    o(0xEEB70AC0|(r<<12)|r|T2CPR(vtop->type.t));
-  }
-#else
-  /* all we have to do on i386 and FPA ARM is to put the float in a register */
-  gv(RC_FLOAT);
-#endif
-}
-
-/* computed goto support */
-void ggoto(void)
-{
-  gcall_or_jmp(1);
-  vtop--;
-}
-
-/* Save the stack pointer onto the stack and return the location of its address */
-ST_FUNC void gen_vla_sp_save(int addr) {
-    SValue v;
-    v.type.t = VT_PTR;
-    v.r = VT_LOCAL | VT_LVAL;
-    v.c.i = addr;
-    store(TREG_SP, &v);
-}
-
-/* Restore the SP from a location on the stack */
-ST_FUNC void gen_vla_sp_restore(int addr) {
-    SValue v;
-    v.type.t = VT_PTR;
-    v.r = VT_LOCAL | VT_LVAL;
-    v.c.i = addr;
-    load(TREG_SP, &v);
-}
-
-/* Subtract from the stack pointer, and push the resulting value onto the stack */
-ST_FUNC void gen_vla_alloc(CType *type, int align) {
-    int r = intr(gv(RC_INT));
-    o(0xE04D0000|(r<<12)|r); /* sub r, sp, r */
-#ifdef TCC_ARM_EABI
-    if (align < 8)
-        align = 8;
-#else
-    if (align < 4)
-        align = 4;
-#endif
-    if (align & (align - 1))
-        tcc_error("alignment is not a power of 2: %i", align);
-    o(stuff_const(0xE3C0D000|(r<<16), align - 1)); /* bic sp, r, #align-1 */
-    vpop();
-}
-
-/* end of ARM code generator */
-/*************************************************************/
-#endif
-/*************************************************************/
diff --git a/tinyc/arm-link.c b/tinyc/arm-link.c
deleted file mode 100644
index aee35afef..000000000
--- a/tinyc/arm-link.c
+++ /dev/null
@@ -1,392 +0,0 @@
-#ifdef TARGET_DEFS_ONLY
-
-#define EM_TCC_TARGET EM_ARM
-
-/* relocation type for 32 bit data relocation */
-#define R_DATA_32   R_ARM_ABS32
-#define R_DATA_PTR  R_ARM_ABS32
-#define R_JMP_SLOT  R_ARM_JUMP_SLOT
-#define R_GLOB_DAT  R_ARM_GLOB_DAT
-#define R_COPY      R_ARM_COPY
-#define R_RELATIVE  R_ARM_RELATIVE
-
-#define R_NUM       R_ARM_NUM
-
-#define ELF_START_ADDR 0x00008000
-#define ELF_PAGE_SIZE  0x1000
-
-#define PCRELATIVE_DLLPLT 1
-#define RELOCATE_DLLPLT 0
-
-enum float_abi {
-    ARM_SOFTFP_FLOAT,
-    ARM_HARD_FLOAT,
-};
-
-#else /* !TARGET_DEFS_ONLY */
-
-#include "tcc.h"
-
-/* Returns 1 for a code relocation, 0 for a data relocation. For unknown
-   relocations, returns -1. */
-int code_reloc (int reloc_type)
-{
-    switch (reloc_type) {
-	case R_ARM_MOVT_ABS:
-	case R_ARM_MOVW_ABS_NC:
-	case R_ARM_THM_MOVT_ABS:
-	case R_ARM_THM_MOVW_ABS_NC:
-	case R_ARM_ABS32:
-	case R_ARM_REL32:
-	case R_ARM_GOTPC:
-	case R_ARM_GOTOFF:
-	case R_ARM_GOT32:
-	case R_ARM_COPY:
-	case R_ARM_GLOB_DAT:
-	case R_ARM_NONE:
-            return 0;
-
-        case R_ARM_PC24:
-        case R_ARM_CALL:
-	case R_ARM_JUMP24:
-	case R_ARM_PLT32:
-	case R_ARM_THM_PC22:
-	case R_ARM_THM_JUMP24:
-	case R_ARM_PREL31:
-	case R_ARM_V4BX:
-	case R_ARM_JUMP_SLOT:
-            return 1;
-    }
-
-    tcc_error ("Unknown relocation type: %d", reloc_type);
-    return -1;
-}
-
-/* Returns an enumerator to describe whether and when the relocation needs a
-   GOT and/or PLT entry to be created. See tcc.h for a description of the
-   different values. */
-int gotplt_entry_type (int reloc_type)
-{
-    switch (reloc_type) {
-	case R_ARM_NONE:
-	case R_ARM_COPY:
-	case R_ARM_GLOB_DAT:
-	case R_ARM_JUMP_SLOT:
-            return NO_GOTPLT_ENTRY;
-
-        case R_ARM_PC24:
-        case R_ARM_CALL:
-	case R_ARM_JUMP24:
-	case R_ARM_PLT32:
-	case R_ARM_THM_PC22:
-	case R_ARM_THM_JUMP24:
-	case R_ARM_MOVT_ABS:
-	case R_ARM_MOVW_ABS_NC:
-	case R_ARM_THM_MOVT_ABS:
-	case R_ARM_THM_MOVW_ABS_NC:
-	case R_ARM_PREL31:
-	case R_ARM_ABS32:
-	case R_ARM_REL32:
-	case R_ARM_V4BX:
-            return AUTO_GOTPLT_ENTRY;
-
-	case R_ARM_GOTPC:
-	case R_ARM_GOTOFF:
-            return BUILD_GOT_ONLY;
-
-	case R_ARM_GOT32:
-            return ALWAYS_GOTPLT_ENTRY;
-    }
-
-    tcc_error ("Unknown relocation type: %d", reloc_type);
-    return -1;
-}
-
-ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr)
-{
-    Section *plt = s1->plt;
-    uint8_t *p;
-    unsigned plt_offset;
-
-    /* when building a DLL, GOT entry accesses must be done relative to
-       start of GOT (see x86_64 example above)  */
-    if (s1->output_type == TCC_OUTPUT_DLL)
-        tcc_error("DLLs unimplemented!");
-
-    /* empty PLT: create PLT0 entry that push address of call site and
-       jump to ld.so resolution routine (GOT + 8) */
-    if (plt->data_offset == 0) {
-        p = section_ptr_add(plt, 20);
-        write32le(p,    0xe52de004); /* push {lr}         */
-        write32le(p+4,  0xe59fe004); /* ldr lr, [pc, #4] */
-        write32le(p+8,  0xe08fe00e); /* add lr, pc, lr    */
-        write32le(p+12, 0xe5bef008); /* ldr pc, [lr, #8]! */
-        /* p+16 is set in relocate_plt */
-    }
-    plt_offset = plt->data_offset;
-
-    if (attr->plt_thumb_stub) {
-        p = section_ptr_add(plt, 4);
-        write32le(p,   0x4778); /* bx pc */
-        write32le(p+2, 0x46c0); /* nop   */
-    }
-    p = section_ptr_add(plt, 16);
-    /* Jump to GOT entry where ld.so initially put address of PLT0 */
-    write32le(p,   0xe59fc004); /* ldr ip, [pc, #4] */
-    write32le(p+4, 0xe08fc00c); /* add ip, pc, ip */
-    write32le(p+8, 0xe59cf000); /* ldr pc, [ip] */
-    /* p + 12 contains offset to GOT entry once patched by relocate_plt */
-    write32le(p+12, got_offset);
-    return plt_offset;
-}
-
-/* relocate the PLT: compute addresses and offsets in the PLT now that final
-   address for PLT and GOT are known (see fill_program_header) */
-ST_FUNC void relocate_plt(TCCState *s1)
-{
-    uint8_t *p, *p_end;
-
-    if (!s1->plt)
-      return;
-
-    p = s1->plt->data;
-    p_end = p + s1->plt->data_offset;
-
-    if (p < p_end) {
-        int x = s1->got->sh_addr - s1->plt->sh_addr - 12;
-        write32le(s1->plt->data + 16, x - 16);
-        p += 20;
-        while (p < p_end) {
-            if (read32le(p) == 0x46c04778) /* PLT Thumb stub present */
-                p += 4;
-            add32le(p + 12, x + s1->plt->data - p);
-            p += 16;
-        }
-    }
-}
-
-void relocate_init(Section *sr) {}
-
-void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val)
-{
-    ElfW(Sym) *sym;
-    int sym_index;
-
-    sym_index = ELFW(R_SYM)(rel->r_info);
-    sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
-
-    switch(type) {
-        case R_ARM_PC24:
-        case R_ARM_CALL:
-        case R_ARM_JUMP24:
-        case R_ARM_PLT32:
-            {
-                int x, is_thumb, is_call, h, blx_avail, is_bl, th_ko;
-                x = (*(int *) ptr) & 0xffffff;
-#ifdef DEBUG_RELOC
-		printf ("reloc %d: x=0x%x val=0x%x ", type, x, val);
-#endif
-                (*(int *)ptr) &= 0xff000000;
-                if (x & 0x800000)
-                    x -= 0x1000000;
-                x <<= 2;
-                blx_avail = (TCC_CPU_VERSION >= 5);
-                is_thumb = val & 1;
-                is_bl = (*(unsigned *) ptr) >> 24 == 0xeb;
-                is_call = (type == R_ARM_CALL || (type == R_ARM_PC24 && is_bl));
-                x += val - addr;
-#ifdef DEBUG_RELOC
-		printf (" newx=0x%x name=%s\n", x,
-			(char *) symtab_section->link->data + sym->st_name);
-#endif
-                h = x & 2;
-                th_ko = (x & 3) && (!blx_avail || !is_call);
-                if (th_ko || x >= 0x2000000 || x < -0x2000000)
-                    tcc_error("can't relocate value at %x,%d",addr, type);
-                x >>= 2;
-                x &= 0xffffff;
-                /* Only reached if blx is avail and it is a call */
-                if (is_thumb) {
-                    x |= h << 24;
-                    (*(int *)ptr) = 0xfa << 24; /* bl -> blx */
-                }
-                (*(int *) ptr) |= x;
-            }
-            return;
-        /* Since these relocations only concern Thumb-2 and blx instruction was
-           introduced before Thumb-2, we can assume blx is available and not
-           guard its use */
-        case R_ARM_THM_PC22:
-        case R_ARM_THM_JUMP24:
-            {
-                int x, hi, lo, s, j1, j2, i1, i2, imm10, imm11;
-                int to_thumb, is_call, to_plt, blx_bit = 1 << 12;
-                Section *plt;
-
-                /* weak reference */
-                if (sym->st_shndx == SHN_UNDEF &&
-                    ELFW(ST_BIND)(sym->st_info) == STB_WEAK)
-                    return;
-
-                /* Get initial offset */
-                hi = (*(uint16_t *)ptr);
-                lo = (*(uint16_t *)(ptr+2));
-                s = (hi >> 10) & 1;
-                j1 = (lo >> 13) & 1;
-                j2 = (lo >> 11) & 1;
-                i1 = (j1 ^ s) ^ 1;
-                i2 = (j2 ^ s) ^ 1;
-                imm10 = hi & 0x3ff;
-                imm11 = lo & 0x7ff;
-                x = (s << 24) | (i1 << 23) | (i2 << 22) |
-                    (imm10 << 12) | (imm11 << 1);
-                if (x & 0x01000000)
-                    x -= 0x02000000;
-
-                /* Relocation infos */
-                to_thumb = val & 1;
-                plt = s1->plt;
-                to_plt = (val >= plt->sh_addr) &&
-                         (val < plt->sh_addr + plt->data_offset);
-                is_call = (type == R_ARM_THM_PC22);
-
-                if (!to_thumb && !to_plt && !is_call) {
-                    int index;
-                    uint8_t *p;
-                    char *name, buf[1024];
-                    Section *text_section;
-
-                    name = (char *) symtab_section->link->data + sym->st_name;
-                    text_section = s1->sections[sym->st_shndx];
-                    /* Modify reloc to target a thumb stub to switch to ARM */
-                    snprintf(buf, sizeof(buf), "%s_from_thumb", name);
-                    index = put_elf_sym(symtab_section,
-                                        text_section->data_offset + 1,
-                                        sym->st_size, sym->st_info, 0,
-                                        sym->st_shndx, buf);
-                    to_thumb = 1;
-                    val = text_section->data_offset + 1;
-                    rel->r_info = ELFW(R_INFO)(index, type);
-                    /* Create a thumb stub function to switch to ARM mode */
-                    put_elf_reloc(symtab_section, text_section,
-                                  text_section->data_offset + 4, R_ARM_JUMP24,
-                                  sym_index);
-                    p = section_ptr_add(text_section, 8);
-                    write32le(p,   0x4778); /* bx pc */
-                    write32le(p+2, 0x46c0); /* nop   */
-                    write32le(p+4, 0xeafffffe); /* b $sym */
-                }
-
-                /* Compute final offset */
-                x += val - addr;
-                if (!to_thumb && is_call) {
-                    blx_bit = 0; /* bl -> blx */
-                    x = (x + 3) & -4; /* Compute offset from aligned PC */
-                }
-
-                /* Check that relocation is possible
-                   * offset must not be out of range
-                   * if target is to be entered in arm mode:
-                     - bit 1 must not set
-                     - instruction must be a call (bl) or a jump to PLT */
-                if (!to_thumb || x >= 0x1000000 || x < -0x1000000)
-                    if (to_thumb || (val & 2) || (!is_call && !to_plt))
-                        tcc_error("can't relocate value at %x,%d",addr, type);
-
-                /* Compute and store final offset */
-                s = (x >> 24) & 1;
-                i1 = (x >> 23) & 1;
-                i2 = (x >> 22) & 1;
-                j1 = s ^ (i1 ^ 1);
-                j2 = s ^ (i2 ^ 1);
-                imm10 = (x >> 12) & 0x3ff;
-                imm11 = (x >> 1) & 0x7ff;
-                (*(uint16_t *)ptr) = (uint16_t) ((hi & 0xf800) |
-                                     (s << 10) | imm10);
-                (*(uint16_t *)(ptr+2)) = (uint16_t) ((lo & 0xc000) |
-                                (j1 << 13) | blx_bit | (j2 << 11) |
-                                imm11);
-            }
-            return;
-        case R_ARM_MOVT_ABS:
-        case R_ARM_MOVW_ABS_NC:
-            {
-                int x, imm4, imm12;
-                if (type == R_ARM_MOVT_ABS)
-                    val >>= 16;
-                imm12 = val & 0xfff;
-                imm4 = (val >> 12) & 0xf;
-                x = (imm4 << 16) | imm12;
-                if (type == R_ARM_THM_MOVT_ABS)
-                    *(int *)ptr |= x;
-                else
-                    *(int *)ptr += x;
-            }
-            return;
-        case R_ARM_THM_MOVT_ABS:
-        case R_ARM_THM_MOVW_ABS_NC:
-            {
-                int x, i, imm4, imm3, imm8;
-                if (type == R_ARM_THM_MOVT_ABS)
-                    val >>= 16;
-                imm8 = val & 0xff;
-                imm3 = (val >> 8) & 0x7;
-                i = (val >> 11) & 1;
-                imm4 = (val >> 12) & 0xf;
-                x = (imm3 << 28) | (imm8 << 16) | (i << 10) | imm4;
-                if (type == R_ARM_THM_MOVT_ABS)
-                    *(int *)ptr |= x;
-                else
-                    *(int *)ptr += x;
-            }
-            return;
-        case R_ARM_PREL31:
-            {
-                int x;
-                x = (*(int *)ptr) & 0x7fffffff;
-                (*(int *)ptr) &= 0x80000000;
-                x = (x * 2) / 2;
-                x += val - addr;
-                if((x^(x>>1))&0x40000000)
-                    tcc_error("can't relocate value at %x,%d",addr, type);
-                (*(int *)ptr) |= x & 0x7fffffff;
-            }
-        case R_ARM_ABS32:
-            *(int *)ptr += val;
-            return;
-        case R_ARM_REL32:
-            *(int *)ptr += val - addr;
-            return;
-        case R_ARM_GOTPC:
-            *(int *)ptr += s1->got->sh_addr - addr;
-            return;
-        case R_ARM_GOTOFF:
-            *(int *)ptr += val - s1->got->sh_addr;
-            return;
-        case R_ARM_GOT32:
-            /* we load the got offset */
-            *(int *)ptr += s1->sym_attrs[sym_index].got_offset;
-            return;
-        case R_ARM_COPY:
-            return;
-        case R_ARM_V4BX:
-            /* trade Thumb support for ARMv4 support */
-            if ((0x0ffffff0 & *(int*)ptr) == 0x012FFF10)
-                *(int*)ptr ^= 0xE12FFF10 ^ 0xE1A0F000; /* BX Rm -> MOV PC, Rm */
-            return;
-        case R_ARM_GLOB_DAT:
-        case R_ARM_JUMP_SLOT:
-            *(addr_t *)ptr = val;
-            return;
-        case R_ARM_NONE:
-            /* Nothing to do.  Normally used to indicate a dependency
-               on a certain symbol (like for exception handling under EABI).  */
-            return;
-        default:
-            fprintf(stderr,"FIXME: handle reloc type %x at %x [%p] to %x\n",
-                type, (unsigned)addr, ptr, (unsigned)val);
-            return;
-    }
-}
-
-#endif /* !TARGET_DEFS_ONLY */
diff --git a/tinyc/arm64-gen.c b/tinyc/arm64-gen.c
deleted file mode 100644
index 86b3af73b..000000000
--- a/tinyc/arm64-gen.c
+++ /dev/null
@@ -1,1837 +0,0 @@
-/*
- *  A64 code generator for TCC
- *
- *  Copyright (c) 2014-2015 Edmund Grimley Evans
- *
- * Copying and distribution of this file, with or without modification,
- * are permitted in any medium without royalty provided the copyright
- * notice and this notice are preserved.  This file is offered as-is,
- * without any warranty.
- */
-
-#ifdef TARGET_DEFS_ONLY
-
-// Number of registers available to allocator:
-#define NB_REGS 28 // x0-x18, x30, v0-v7
-
-#define TREG_R(x) (x) // x = 0..18
-#define TREG_R30  19
-#define TREG_F(x) (x + 20) // x = 0..7
-
-// Register classes sorted from more general to more precise:
-#define RC_INT (1 << 0)
-#define RC_FLOAT (1 << 1)
-#define RC_R(x) (1 << (2 + (x))) // x = 0..18
-#define RC_R30  (1 << 21)
-#define RC_F(x) (1 << (22 + (x))) // x = 0..7
-
-#define RC_IRET (RC_R(0)) // int return register class
-#define RC_FRET (RC_F(0)) // float return register class
-
-#define REG_IRET (TREG_R(0)) // int return register number
-#define REG_FRET (TREG_F(0)) // float return register number
-
-#define PTR_SIZE 8
-
-#define LDOUBLE_SIZE 16
-#define LDOUBLE_ALIGN 16
-
-#define MAX_ALIGN 16
-
-#define CHAR_IS_UNSIGNED
-
-/******************************************************/
-#else /* ! TARGET_DEFS_ONLY */
-/******************************************************/
-#include "tcc.h"
-#include <assert.h>
-
-ST_DATA const int reg_classes[NB_REGS] = {
-  RC_INT | RC_R(0),
-  RC_INT | RC_R(1),
-  RC_INT | RC_R(2),
-  RC_INT | RC_R(3),
-  RC_INT | RC_R(4),
-  RC_INT | RC_R(5),
-  RC_INT | RC_R(6),
-  RC_INT | RC_R(7),
-  RC_INT | RC_R(8),
-  RC_INT | RC_R(9),
-  RC_INT | RC_R(10),
-  RC_INT | RC_R(11),
-  RC_INT | RC_R(12),
-  RC_INT | RC_R(13),
-  RC_INT | RC_R(14),
-  RC_INT | RC_R(15),
-  RC_INT | RC_R(16),
-  RC_INT | RC_R(17),
-  RC_INT | RC_R(18),
-  RC_R30, // not in RC_INT as we make special use of x30
-  RC_FLOAT | RC_F(0),
-  RC_FLOAT | RC_F(1),
-  RC_FLOAT | RC_F(2),
-  RC_FLOAT | RC_F(3),
-  RC_FLOAT | RC_F(4),
-  RC_FLOAT | RC_F(5),
-  RC_FLOAT | RC_F(6),
-  RC_FLOAT | RC_F(7)
-};
-
-#define IS_FREG(x) ((x) >= TREG_F(0))
-
-static uint32_t intr(int r)
-{
-    assert(TREG_R(0) <= r && r <= TREG_R30);
-    return r < TREG_R30 ? r : 30;
-}
-
-static uint32_t fltr(int r)
-{
-    assert(TREG_F(0) <= r && r <= TREG_F(7));
-    return r - TREG_F(0);
-}
-
-// Add an instruction to text section:
-ST_FUNC void o(unsigned int c)
-{
-    int ind1 = ind + 4;
-    if (nocode_wanted)
-        return;
-    if (ind1 > cur_text_section->data_allocated)
-        section_realloc(cur_text_section, ind1);
-    write32le(cur_text_section->data + ind, c);
-    ind = ind1;
-}
-
-static int arm64_encode_bimm64(uint64_t x)
-{
-    int neg = x & 1;
-    int rep, pos, len;
-
-    if (neg)
-        x = ~x;
-    if (!x)
-        return -1;
-
-    if (x >> 2 == (x & (((uint64_t)1 << (64 - 2)) - 1)))
-        rep = 2, x &= ((uint64_t)1 << 2) - 1;
-    else if (x >> 4 == (x & (((uint64_t)1 << (64 - 4)) - 1)))
-        rep = 4, x &= ((uint64_t)1 <<  4) - 1;
-    else if (x >> 8 == (x & (((uint64_t)1 << (64 - 8)) - 1)))
-        rep = 8, x &= ((uint64_t)1 <<  8) - 1;
-    else if (x >> 16 == (x & (((uint64_t)1 << (64 - 16)) - 1)))
-        rep = 16, x &= ((uint64_t)1 << 16) - 1;
-    else if (x >> 32 == (x & (((uint64_t)1 << (64 - 32)) - 1)))
-        rep = 32, x &= ((uint64_t)1 << 32) - 1;
-    else
-        rep = 64;
-
-    pos = 0;
-    if (!(x & (((uint64_t)1 << 32) - 1))) x >>= 32, pos += 32;
-    if (!(x & (((uint64_t)1 << 16) - 1))) x >>= 16, pos += 16;
-    if (!(x & (((uint64_t)1 <<  8) - 1))) x >>= 8, pos += 8;
-    if (!(x & (((uint64_t)1 <<  4) - 1))) x >>= 4, pos += 4;
-    if (!(x & (((uint64_t)1 <<  2) - 1))) x >>= 2, pos += 2;
-    if (!(x & (((uint64_t)1 <<  1) - 1))) x >>= 1, pos += 1;
-
-    len = 0;
-    if (!(~x & (((uint64_t)1 << 32) - 1))) x >>= 32, len += 32;
-    if (!(~x & (((uint64_t)1 << 16) - 1))) x >>= 16, len += 16;
-    if (!(~x & (((uint64_t)1 << 8) - 1))) x >>= 8, len += 8;
-    if (!(~x & (((uint64_t)1 << 4) - 1))) x >>= 4, len += 4;
-    if (!(~x & (((uint64_t)1 << 2) - 1))) x >>= 2, len += 2;
-    if (!(~x & (((uint64_t)1 << 1) - 1))) x >>= 1, len += 1;
-
-    if (x)
-        return -1;
-    if (neg) {
-        pos = (pos + len) & (rep - 1);
-        len = rep - len;
-    }
-    return ((0x1000 & rep << 6) | (((rep - 1) ^ 31) << 1 & 63) |
-            ((rep - pos) & (rep - 1)) << 6 | (len - 1));
-}
-
-static uint32_t arm64_movi(int r, uint64_t x)
-{
-    uint64_t m = 0xffff;
-    int e;
-    if (!(x & ~m))
-        return 0x52800000 | r | x << 5; // movz w(r),#(x)
-    if (!(x & ~(m << 16)))
-        return 0x52a00000 | r | x >> 11; // movz w(r),#(x >> 16),lsl #16
-    if (!(x & ~(m << 32)))
-        return 0xd2c00000 | r | x >> 27; // movz x(r),#(x >> 32),lsl #32
-    if (!(x & ~(m << 48)))
-        return 0xd2e00000 | r | x >> 43; // movz x(r),#(x >> 48),lsl #48
-    if ((x & ~m) == m << 16)
-        return (0x12800000 | r |
-                (~x << 5 & 0x1fffe0)); // movn w(r),#(~x)
-    if ((x & ~(m << 16)) == m)
-        return (0x12a00000 | r |
-                (~x >> 11 & 0x1fffe0)); // movn w(r),#(~x >> 16),lsl #16
-    if (!~(x | m))
-        return (0x92800000 | r |
-                (~x << 5 & 0x1fffe0)); // movn x(r),#(~x)
-    if (!~(x | m << 16))
-        return (0x92a00000 | r |
-                (~x >> 11 & 0x1fffe0)); // movn x(r),#(~x >> 16),lsl #16
-    if (!~(x | m << 32))
-        return (0x92c00000 | r |
-                (~x >> 27 & 0x1fffe0)); // movn x(r),#(~x >> 32),lsl #32
-    if (!~(x | m << 48))
-        return (0x92e00000 | r |
-                (~x >> 43 & 0x1fffe0)); // movn x(r),#(~x >> 32),lsl #32
-    if (!(x >> 32) && (e = arm64_encode_bimm64(x | x << 32)) >= 0)
-        return 0x320003e0 | r | (uint32_t)e << 10; // movi w(r),#(x)
-    if ((e = arm64_encode_bimm64(x)) >= 0)
-        return 0xb20003e0 | r | (uint32_t)e << 10; // movi x(r),#(x)
-    return 0;
-}
-
-static void arm64_movimm(int r, uint64_t x)
-{
-    uint32_t i;
-    if ((i = arm64_movi(r, x)))
-        o(i); // a single MOV
-    else {
-        // MOVZ/MOVN and 1-3 MOVKs
-        int z = 0, m = 0;
-        uint32_t mov1 = 0xd2800000; // movz
-        uint64_t x1 = x;
-        for (i = 0; i < 64; i += 16) {
-            z += !(x >> i & 0xffff);
-            m += !(~x >> i & 0xffff);
-        }
-        if (m > z) {
-            x1 = ~x;
-            mov1 = 0x92800000; // movn
-        }
-        for (i = 0; i < 64; i += 16)
-            if (x1 >> i & 0xffff) {
-                o(mov1 | r | (x1 >> i & 0xffff) << 5 | i << 17);
-                // movz/movn x(r),#(*),lsl #(i)
-                break;
-            }
-        for (i += 16; i < 64; i += 16)
-            if (x1 >> i & 0xffff)
-                o(0xf2800000 | r | (x >> i & 0xffff) << 5 | i << 17);
-                // movk x(r),#(*),lsl #(i)
-    }
-}
-
-// Patch all branches in list pointed to by t to branch to a:
-ST_FUNC void gsym_addr(int t_, int a_)
-{
-    uint32_t t = t_;
-    uint32_t a = a_;
-    while (t) {
-        unsigned char *ptr = cur_text_section->data + t;
-        uint32_t next = read32le(ptr);
-        if (a - t + 0x8000000 >= 0x10000000)
-            tcc_error("branch out of range");
-        write32le(ptr, (a - t == 4 ? 0xd503201f : // nop
-                        0x14000000 | ((a - t) >> 2 & 0x3ffffff))); // b
-        t = next;
-    }
-}
-
-// Patch all branches in list pointed to by t to branch to current location:
-ST_FUNC void gsym(int t)
-{
-    gsym_addr(t, ind);
-}
-
-static int arm64_type_size(int t)
-{
-    switch (t & VT_BTYPE) {
-    case VT_INT: return 2;
-    case VT_BYTE: return 0;
-    case VT_SHORT: return 1;
-    case VT_PTR: return 3;
-    case VT_FUNC: return 3;
-    case VT_FLOAT: return 2;
-    case VT_DOUBLE: return 3;
-    case VT_LDOUBLE: return 4;
-    case VT_BOOL: return 0;
-    case VT_LLONG: return 3;
-    }
-    assert(0);
-    return 0;
-}
-
-static void arm64_spoff(int reg, uint64_t off)
-{
-    uint32_t sub = off >> 63;
-    if (sub)
-        off = -off;
-    if (off < 4096)
-        o(0x910003e0 | sub << 30 | reg | off << 10);
-        // (add|sub) x(reg),sp,#(off)
-    else {
-        arm64_movimm(30, off); // use x30 for offset
-        o(0x8b3e63e0 | sub << 30 | reg); // (add|sub) x(reg),sp,x30
-    }
-}
-
-static void arm64_ldrx(int sg, int sz_, int dst, int bas, uint64_t off)
-{
-    uint32_t sz = sz_;
-    if (sz >= 2)
-        sg = 0;
-    if (!(off & ~((uint32_t)0xfff << sz)))
-        o(0x39400000 | dst | bas << 5 | off << (10 - sz) |
-          (uint32_t)!!sg << 23 | sz << 30); // ldr(*) x(dst),[x(bas),#(off)]
-    else if (off < 256 || -off <= 256)
-        o(0x38400000 | dst | bas << 5 | (off & 511) << 12 |
-          (uint32_t)!!sg << 23 | sz << 30); // ldur(*) x(dst),[x(bas),#(off)]
-    else {
-        arm64_movimm(30, off); // use x30 for offset
-        o(0x38206800 | dst | bas << 5 | (uint32_t)30 << 16 |
-          (uint32_t)(!!sg + 1) << 22 | sz << 30); // ldr(*) x(dst),[x(bas),x30]
-    }
-}
-
-static void arm64_ldrv(int sz_, int dst, int bas, uint64_t off)
-{
-    uint32_t sz = sz_;
-    if (!(off & ~((uint32_t)0xfff << sz)))
-        o(0x3d400000 | dst | bas << 5 | off << (10 - sz) |
-          (sz & 4) << 21 | (sz & 3) << 30); // ldr (s|d|q)(dst),[x(bas),#(off)]
-    else if (off < 256 || -off <= 256)
-        o(0x3c400000 | dst | bas << 5 | (off & 511) << 12 |
-          (sz & 4) << 21 | (sz & 3) << 30); // ldur (s|d|q)(dst),[x(bas),#(off)]
-    else {
-        arm64_movimm(30, off); // use x30 for offset
-        o(0x3c606800 | dst | bas << 5 | (uint32_t)30 << 16 |
-          sz << 30 | (sz & 4) << 21); // ldr (s|d|q)(dst),[x(bas),x30]
-    }
-}
-
-static void arm64_ldrs(int reg_, int size)
-{
-    uint32_t reg = reg_;
-    // Use x30 for intermediate value in some cases.
-    switch (size) {
-    default: assert(0); break;
-    case 1:
-        arm64_ldrx(0, 0, reg, reg, 0);
-        break;
-    case 2:
-        arm64_ldrx(0, 1, reg, reg, 0);
-        break;
-    case 3:
-        arm64_ldrx(0, 1, 30, reg, 0);
-        arm64_ldrx(0, 0, reg, reg, 2);
-        o(0x2a0043c0 | reg | reg << 16); // orr x(reg),x30,x(reg),lsl #16
-        break;
-    case 4:
-        arm64_ldrx(0, 2, reg, reg, 0);
-        break;
-    case 5:
-        arm64_ldrx(0, 2, 30, reg, 0);
-        arm64_ldrx(0, 0, reg, reg, 4);
-        o(0xaa0083c0 | reg | reg << 16); // orr x(reg),x30,x(reg),lsl #32
-        break;
-    case 6:
-        arm64_ldrx(0, 2, 30, reg, 0);
-        arm64_ldrx(0, 1, reg, reg, 4);
-        o(0xaa0083c0 | reg | reg << 16); // orr x(reg),x30,x(reg),lsl #32
-        break;
-    case 7:
-        arm64_ldrx(0, 2, 30, reg, 0);
-        arm64_ldrx(0, 2, reg, reg, 3);
-        o(0x53087c00 | reg | reg << 5); // lsr w(reg), w(reg), #8
-        o(0xaa0083c0 | reg | reg << 16); // orr x(reg),x30,x(reg),lsl #32
-        break;
-    case 8:
-        arm64_ldrx(0, 3, reg, reg, 0);
-        break;
-    case 9:
-        arm64_ldrx(0, 0, reg + 1, reg, 8);
-        arm64_ldrx(0, 3, reg, reg, 0);
-        break;
-    case 10:
-        arm64_ldrx(0, 1, reg + 1, reg, 8);
-        arm64_ldrx(0, 3, reg, reg, 0);
-        break;
-    case 11:
-        arm64_ldrx(0, 2, reg + 1, reg, 7);
-        o(0x53087c00 | (reg+1) | (reg+1) << 5); // lsr w(reg+1), w(reg+1), #8
-        arm64_ldrx(0, 3, reg, reg, 0);
-        break;
-    case 12:
-        arm64_ldrx(0, 2, reg + 1, reg, 8);
-        arm64_ldrx(0, 3, reg, reg, 0);
-        break;
-    case 13:
-        arm64_ldrx(0, 3, reg + 1, reg, 5);
-        o(0xd358fc00 | (reg+1) | (reg+1) << 5); // lsr x(reg+1), x(reg+1), #24
-        arm64_ldrx(0, 3, reg, reg, 0);
-        break;
-    case 14:
-        arm64_ldrx(0, 3, reg + 1, reg, 6);
-        o(0xd350fc00 | (reg+1) | (reg+1) << 5); // lsr x(reg+1), x(reg+1), #16
-        arm64_ldrx(0, 3, reg, reg, 0);
-        break;
-    case 15:
-        arm64_ldrx(0, 3, reg + 1, reg, 7);
-        o(0xd348fc00 | (reg+1) | (reg+1) << 5); // lsr x(reg+1), x(reg+1), #8
-        arm64_ldrx(0, 3, reg, reg, 0);
-        break;
-    case 16:
-        o(0xa9400000 | reg | (reg+1) << 10 | reg << 5);
-        // ldp x(reg),x(reg+1),[x(reg)]
-        break;
-    }
-}
-
-static void arm64_strx(int sz_, int dst, int bas, uint64_t off)
-{
-    uint32_t sz = sz_;
-    if (!(off & ~((uint32_t)0xfff << sz)))
-        o(0x39000000 | dst | bas << 5 | off << (10 - sz) | sz << 30);
-        // str(*) x(dst),[x(bas],#(off)]
-    else if (off < 256 || -off <= 256)
-        o(0x38000000 | dst | bas << 5 | (off & 511) << 12 | sz << 30);
-        // stur(*) x(dst),[x(bas],#(off)]
-    else {
-        arm64_movimm(30, off); // use x30 for offset
-        o(0x38206800 | dst | bas << 5 | (uint32_t)30 << 16 | sz << 30);
-        // str(*) x(dst),[x(bas),x30]
-    }
-}
-
-static void arm64_strv(int sz_, int dst, int bas, uint64_t off)
-{
-    uint32_t sz = sz_;
-    if (!(off & ~((uint32_t)0xfff << sz)))
-        o(0x3d000000 | dst | bas << 5 | off << (10 - sz) |
-          (sz & 4) << 21 | (sz & 3) << 30); // str (s|d|q)(dst),[x(bas),#(off)]
-    else if (off < 256 || -off <= 256)
-        o(0x3c000000 | dst | bas << 5 | (off & 511) << 12 |
-          (sz & 4) << 21 | (sz & 3) << 30); // stur (s|d|q)(dst),[x(bas),#(off)]
-    else {
-        arm64_movimm(30, off); // use x30 for offset
-        o(0x3c206800 | dst | bas << 5 | (uint32_t)30 << 16 |
-          sz << 30 | (sz & 4) << 21); // str (s|d|q)(dst),[x(bas),x30]
-    }
-}
-
-static void arm64_sym(int r, Sym *sym, unsigned long addend)
-{
-    // Currently TCC's linker does not generate COPY relocations for
-    // STT_OBJECTs when tcc is invoked with "-run". This typically
-    // results in "R_AARCH64_ADR_PREL_PG_HI21 relocation failed" when
-    // a program refers to stdin. A workaround is to avoid that
-    // relocation and use only relocations with unlimited range.
-    int avoid_adrp = 1;
-
-    if (avoid_adrp || sym->a.weak) {
-        // (GCC uses a R_AARCH64_ABS64 in this case.)
-        greloca(cur_text_section, sym, ind, R_AARCH64_MOVW_UABS_G0_NC, addend);
-        o(0xd2800000 | r); // mov x(rt),#0,lsl #0
-        greloca(cur_text_section, sym, ind, R_AARCH64_MOVW_UABS_G1_NC, addend);
-        o(0xf2a00000 | r); // movk x(rt),#0,lsl #16
-        greloca(cur_text_section, sym, ind, R_AARCH64_MOVW_UABS_G2_NC, addend);
-        o(0xf2c00000 | r); // movk x(rt),#0,lsl #32
-        greloca(cur_text_section, sym, ind, R_AARCH64_MOVW_UABS_G3, addend);
-        o(0xf2e00000 | r); // movk x(rt),#0,lsl #48
-    }
-    else {
-        greloca(cur_text_section, sym, ind, R_AARCH64_ADR_PREL_PG_HI21, addend);
-        o(0x90000000 | r);
-        greloca(cur_text_section, sym, ind, R_AARCH64_ADD_ABS_LO12_NC, addend);
-        o(0x91000000 | r | r << 5);
-    }
-}
-
-ST_FUNC void load(int r, SValue *sv)
-{
-    int svtt = sv->type.t;
-    int svr = sv->r & ~VT_LVAL_TYPE;
-    int svrv = svr & VT_VALMASK;
-    uint64_t svcul = (uint32_t)sv->c.i;
-    svcul = svcul >> 31 & 1 ? svcul - ((uint64_t)1 << 32) : svcul;
-
-    if (svr == (VT_LOCAL | VT_LVAL)) {
-        if (IS_FREG(r))
-            arm64_ldrv(arm64_type_size(svtt), fltr(r), 29, svcul);
-        else
-            arm64_ldrx(!(svtt & VT_UNSIGNED), arm64_type_size(svtt),
-                       intr(r), 29, svcul);
-        return;
-    }
-
-    if ((svr & ~VT_VALMASK) == VT_LVAL && svrv < VT_CONST) {
-        if (IS_FREG(r))
-            arm64_ldrv(arm64_type_size(svtt), fltr(r), intr(svrv), 0);
-        else
-            arm64_ldrx(!(svtt & VT_UNSIGNED), arm64_type_size(svtt),
-                       intr(r), intr(svrv), 0);
-        return;
-    }
-
-    if (svr == (VT_CONST | VT_LVAL | VT_SYM)) {
-        arm64_sym(30, sv->sym, svcul); // use x30 for address
-        if (IS_FREG(r))
-            arm64_ldrv(arm64_type_size(svtt), fltr(r), 30, 0);
-        else
-            arm64_ldrx(!(svtt & VT_UNSIGNED), arm64_type_size(svtt),
-                       intr(r), 30, 0);
-        return;
-    }
-
-    if (svr == (VT_CONST | VT_SYM)) {
-        arm64_sym(intr(r), sv->sym, svcul);
-        return;
-    }
-
-    if (svr == VT_CONST) {
-        if ((svtt & VT_BTYPE) != VT_VOID)
-            arm64_movimm(intr(r), arm64_type_size(svtt) == 3 ?
-                         sv->c.i : (uint32_t)svcul);
-        return;
-    }
-
-    if (svr < VT_CONST) {
-        if (IS_FREG(r) && IS_FREG(svr))
-            if (svtt == VT_LDOUBLE)
-                o(0x4ea01c00 | fltr(r) | fltr(svr) << 5);
-                    // mov v(r).16b,v(svr).16b
-            else
-                o(0x1e604000 | fltr(r) | fltr(svr) << 5); // fmov d(r),d(svr)
-        else if (!IS_FREG(r) && !IS_FREG(svr))
-            o(0xaa0003e0 | intr(r) | intr(svr) << 16); // mov x(r),x(svr)
-        else
-            assert(0);
-      return;
-    }
-
-    if (svr == VT_LOCAL) {
-        if (-svcul < 0x1000)
-            o(0xd10003a0 | intr(r) | -svcul << 10); // sub x(r),x29,#...
-        else {
-            arm64_movimm(30, -svcul); // use x30 for offset
-            o(0xcb0003a0 | intr(r) | (uint32_t)30 << 16); // sub x(r),x29,x30
-        }
-        return;
-    }
-
-    if (svr == VT_JMP || svr == VT_JMPI) {
-        int t = (svr == VT_JMPI);
-        arm64_movimm(intr(r), t);
-        o(0x14000002); // b .+8
-        gsym(svcul);
-        arm64_movimm(intr(r), t ^ 1);
-        return;
-    }
-
-    if (svr == (VT_LLOCAL | VT_LVAL)) {
-        arm64_ldrx(0, 3, 30, 29, svcul); // use x30 for offset
-        if (IS_FREG(r))
-            arm64_ldrv(arm64_type_size(svtt), fltr(r), 30, 0);
-        else
-            arm64_ldrx(!(svtt & VT_UNSIGNED), arm64_type_size(svtt),
-                       intr(r), 30, 0);
-        return;
-    }
-
-    printf("load(%x, (%x, %x, %llx))\n", r, svtt, sv->r, (long long)svcul);
-    assert(0);
-}
-
-ST_FUNC void store(int r, SValue *sv)
-{
-    int svtt = sv->type.t;
-    int svr = sv->r & ~VT_LVAL_TYPE;
-    int svrv = svr & VT_VALMASK;
-    uint64_t svcul = (uint32_t)sv->c.i;
-    svcul = svcul >> 31 & 1 ? svcul - ((uint64_t)1 << 32) : svcul;
-
-    if (svr == (VT_LOCAL | VT_LVAL)) {
-        if (IS_FREG(r))
-            arm64_strv(arm64_type_size(svtt), fltr(r), 29, svcul);
-        else
-            arm64_strx(arm64_type_size(svtt), intr(r), 29, svcul);
-        return;
-    }
-
-    if ((svr & ~VT_VALMASK) == VT_LVAL && svrv < VT_CONST) {
-        if (IS_FREG(r))
-            arm64_strv(arm64_type_size(svtt), fltr(r), intr(svrv), 0);
-        else
-            arm64_strx(arm64_type_size(svtt), intr(r), intr(svrv), 0);
-        return;
-    }
-
-    if (svr == (VT_CONST | VT_LVAL | VT_SYM)) {
-        arm64_sym(30, sv->sym, svcul); // use x30 for address
-        if (IS_FREG(r))
-            arm64_strv(arm64_type_size(svtt), fltr(r), 30, 0);
-        else
-            arm64_strx(arm64_type_size(svtt), intr(r), 30, 0);
-        return;
-    }
-
-    printf("store(%x, (%x, %x, %llx))\n", r, svtt, sv->r, (long long)svcul);
-    assert(0);
-}
-
-static void arm64_gen_bl_or_b(int b)
-{
-    if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
-        assert(!b && (vtop->r & VT_SYM));
-	greloca(cur_text_section, vtop->sym, ind, R_AARCH64_CALL26, 0);
-	o(0x94000000); // bl .
-    }
-    else
-        o(0xd61f0000 | (uint32_t)!b << 21 | intr(gv(RC_R30)) << 5); // br/blr
-}
-
-static int arm64_hfa_aux(CType *type, int *fsize, int num)
-{
-    if (is_float(type->t)) {
-        int a, n = type_size(type, &a);
-        if (num >= 4 || (*fsize && *fsize != n))
-            return -1;
-        *fsize = n;
-        return num + 1;
-    }
-    else if ((type->t & VT_BTYPE) == VT_STRUCT) {
-        int is_struct = 0; // rather than union
-        Sym *field;
-        for (field = type->ref->next; field; field = field->next)
-            if (field->c) {
-                is_struct = 1;
-                break;
-            }
-        if (is_struct) {
-            int num0 = num;
-            for (field = type->ref->next; field; field = field->next) {
-                if (field->c != (num - num0) * *fsize)
-                    return -1;
-                num = arm64_hfa_aux(&field->type, fsize, num);
-                if (num == -1)
-                    return -1;
-            }
-            if (type->ref->c != (num - num0) * *fsize)
-                return -1;
-            return num;
-        }
-        else { // union
-            int num0 = num;
-            for (field = type->ref->next; field; field = field->next) {
-                int num1 = arm64_hfa_aux(&field->type, fsize, num0);
-                if (num1 == -1)
-                    return -1;
-                num = num1 < num ? num : num1;
-            }
-            if (type->ref->c != (num - num0) * *fsize)
-                return -1;
-            return num;
-        }
-    }
-    else if (type->t & VT_ARRAY) {
-        int num1;
-        if (!type->ref->c)
-            return num;
-        num1 = arm64_hfa_aux(&type->ref->type, fsize, num);
-        if (num1 == -1 || (num1 != num && type->ref->c > 4))
-            return -1;
-        num1 = num + type->ref->c * (num1 - num);
-        if (num1 > 4)
-            return -1;
-        return num1;
-    }
-    return -1;
-}
-
-static int arm64_hfa(CType *type, int *fsize)
-{
-    if ((type->t & VT_BTYPE) == VT_STRUCT || (type->t & VT_ARRAY)) {
-        int sz = 0;
-        int n = arm64_hfa_aux(type, &sz, 0);
-        if (0 < n && n <= 4) {
-            if (fsize)
-                *fsize = sz;
-            return n;
-        }
-    }
-    return 0;
-}
-
-static unsigned long arm64_pcs_aux(int n, CType **type, unsigned long *a)
-{
-    int nx = 0; // next integer register
-    int nv = 0; // next vector register
-    unsigned long ns = 32; // next stack offset
-    int i;
-
-    for (i = 0; i < n; i++) {
-        int hfa = arm64_hfa(type[i], 0);
-        int size, align;
-
-        if ((type[i]->t & VT_ARRAY) ||
-            (type[i]->t & VT_BTYPE) == VT_FUNC)
-            size = align = 8;
-        else
-            size = type_size(type[i], &align);
-
-        if (hfa)
-            // B.2
-            ;
-        else if (size > 16) {
-            // B.3: replace with pointer
-            if (nx < 8)
-                a[i] = nx++ << 1 | 1;
-            else {
-                ns = (ns + 7) & ~7;
-                a[i] = ns | 1;
-                ns += 8;
-            }
-            continue;
-        }
-        else if ((type[i]->t & VT_BTYPE) == VT_STRUCT)
-            // B.4
-            size = (size + 7) & ~7;
-
-        // C.1
-        if (is_float(type[i]->t) && nv < 8) {
-            a[i] = 16 + (nv++ << 1);
-            continue;
-        }
-
-        // C.2
-        if (hfa && nv + hfa <= 8) {
-            a[i] = 16 + (nv << 1);
-            nv += hfa;
-            continue;
-        }
-
-        // C.3
-        if (hfa) {
-            nv = 8;
-            size = (size + 7) & ~7;
-        }
-
-        // C.4
-        if (hfa || (type[i]->t & VT_BTYPE) == VT_LDOUBLE) {
-            ns = (ns + 7) & ~7;
-            ns = (ns + align - 1) & -align;
-        }
-
-        // C.5
-        if ((type[i]->t & VT_BTYPE) == VT_FLOAT)
-            size = 8;
-
-        // C.6
-        if (hfa || is_float(type[i]->t)) {
-            a[i] = ns;
-            ns += size;
-            continue;
-        }
-
-        // C.7
-        if ((type[i]->t & VT_BTYPE) != VT_STRUCT && size <= 8 && nx < 8) {
-            a[i] = nx++ << 1;
-            continue;
-        }
-
-        // C.8
-        if (align == 16)
-            nx = (nx + 1) & ~1;
-
-        // C.9
-        if ((type[i]->t & VT_BTYPE) != VT_STRUCT && size == 16 && nx < 7) {
-            a[i] = nx << 1;
-            nx += 2;
-            continue;
-        }
-
-        // C.10
-        if ((type[i]->t & VT_BTYPE) == VT_STRUCT && size <= (8 - nx) * 8) {
-            a[i] = nx << 1;
-            nx += (size + 7) >> 3;
-            continue;
-        }
-
-        // C.11
-        nx = 8;
-
-        // C.12
-        ns = (ns + 7) & ~7;
-        ns = (ns + align - 1) & -align;
-
-        // C.13
-        if ((type[i]->t & VT_BTYPE) == VT_STRUCT) {
-            a[i] = ns;
-            ns += size;
-            continue;
-        }
-
-        // C.14
-        if (size < 8)
-            size = 8;
-
-        // C.15
-        a[i] = ns;
-        ns += size;
-    }
-
-    return ns - 32;
-}
-
-static unsigned long arm64_pcs(int n, CType **type, unsigned long *a)
-{
-    unsigned long stack;
-
-    // Return type:
-    if ((type[0]->t & VT_BTYPE) == VT_VOID)
-        a[0] = -1;
-    else {
-        arm64_pcs_aux(1, type, a);
-        assert(a[0] == 0 || a[0] == 1 || a[0] == 16);
-    }
-
-    // Argument types:
-    stack = arm64_pcs_aux(n, type + 1, a + 1);
-
-    if (0) {
-        int i;
-        for (i = 0; i <= n; i++) {
-            if (!i)
-                printf("arm64_pcs return: ");
-            else
-                printf("arm64_pcs arg %d: ", i);
-            if (a[i] == (unsigned long)-1)
-                printf("void\n");
-            else if (a[i] == 1 && !i)
-                printf("X8 pointer\n");
-            else if (a[i] < 16)
-                printf("X%lu%s\n", a[i] / 2, a[i] & 1 ? " pointer" : "");
-            else if (a[i] < 32)
-                printf("V%lu\n", a[i] / 2 - 8);
-            else
-                printf("stack %lu%s\n",
-                       (a[i] - 32) & ~1, a[i] & 1 ? " pointer" : "");
-        }
-    }
-
-    return stack;
-}
-
-ST_FUNC void gfunc_call(int nb_args)
-{
-    CType *return_type;
-    CType **t;
-    unsigned long *a, *a1;
-    unsigned long stack;
-    int i;
-
-    return_type = &vtop[-nb_args].type.ref->type;
-    if ((return_type->t & VT_BTYPE) == VT_STRUCT)
-        --nb_args;
-
-    t = tcc_malloc((nb_args + 1) * sizeof(*t));
-    a = tcc_malloc((nb_args + 1) * sizeof(*a));
-    a1 = tcc_malloc((nb_args + 1) * sizeof(*a1));
-
-    t[0] = return_type;
-    for (i = 0; i < nb_args; i++)
-        t[nb_args - i] = &vtop[-i].type;
-
-    stack = arm64_pcs(nb_args, t, a);
-
-    // Allocate space for structs replaced by pointer:
-    for (i = nb_args; i; i--)
-        if (a[i] & 1) {
-            SValue *arg = &vtop[i - nb_args];
-            int align, size = type_size(&arg->type, &align);
-            assert((arg->type.t & VT_BTYPE) == VT_STRUCT);
-            stack = (stack + align - 1) & -align;
-            a1[i] = stack;
-            stack += size;
-        }
-
-    stack = (stack + 15) >> 4 << 4;
-
-    assert(stack < 0x1000);
-    if (stack)
-        o(0xd10003ff | stack << 10); // sub sp,sp,#(n)
-
-    // First pass: set all values on stack
-    for (i = nb_args; i; i--) {
-        vpushv(vtop - nb_args + i);
-
-        if (a[i] & 1) {
-            // struct replaced by pointer
-            int r = get_reg(RC_INT);
-            arm64_spoff(intr(r), a1[i]);
-            vset(&vtop->type, r | VT_LVAL, 0);
-            vswap();
-            vstore();
-            if (a[i] >= 32) {
-                // pointer on stack
-                r = get_reg(RC_INT);
-                arm64_spoff(intr(r), a1[i]);
-                arm64_strx(3, intr(r), 31, (a[i] - 32) >> 1 << 1);
-            }
-        }
-        else if (a[i] >= 32) {
-            // value on stack
-            if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
-                int r = get_reg(RC_INT);
-                arm64_spoff(intr(r), a[i] - 32);
-                vset(&vtop->type, r | VT_LVAL, 0);
-                vswap();
-                vstore();
-            }
-            else if (is_float(vtop->type.t)) {
-                gv(RC_FLOAT);
-                arm64_strv(arm64_type_size(vtop[0].type.t),
-                           fltr(vtop[0].r), 31, a[i] - 32);
-            }
-            else {
-                gv(RC_INT);
-                arm64_strx(arm64_type_size(vtop[0].type.t),
-                           intr(vtop[0].r), 31, a[i] - 32);
-            }
-        }
-
-        --vtop;
-    }
-
-    // Second pass: assign values to registers
-    for (i = nb_args; i; i--, vtop--) {
-        if (a[i] < 16 && !(a[i] & 1)) {
-            // value in general-purpose registers
-            if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
-                int align, size = type_size(&vtop->type, &align);
-                vtop->type.t = VT_PTR;
-                gaddrof();
-                gv(RC_R(a[i] / 2));
-                arm64_ldrs(a[i] / 2, size);
-            }
-            else
-                gv(RC_R(a[i] / 2));
-        }
-        else if (a[i] < 16)
-            // struct replaced by pointer in register
-            arm64_spoff(a[i] / 2, a1[i]);
-        else if (a[i] < 32) {
-            // value in floating-point registers
-            if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
-                uint32_t j, sz, n = arm64_hfa(&vtop->type, &sz);
-                vtop->type.t = VT_PTR;
-                gaddrof();
-                gv(RC_R30);
-                for (j = 0; j < n; j++)
-                    o(0x3d4003c0 |
-                      (sz & 16) << 19 | -(sz & 8) << 27 | (sz & 4) << 29 |
-                      (a[i] / 2 - 8 + j) |
-                      j << 10); // ldr ([sdq])(*),[x30,#(j * sz)]
-            }
-            else
-                gv(RC_F(a[i] / 2 - 8));
-        }
-    }
-
-    if ((return_type->t & VT_BTYPE) == VT_STRUCT) {
-        if (a[0] == 1) {
-            // indirect return: set x8 and discard the stack value
-            gv(RC_R(8));
-            --vtop;
-        }
-        else
-            // return in registers: keep the address for after the call
-            vswap();
-    }
-
-    save_regs(0);
-    arm64_gen_bl_or_b(0);
-    --vtop;
-    if (stack)
-        o(0x910003ff | stack << 10); // add sp,sp,#(n)
-
-    {
-        int rt = return_type->t;
-        int bt = rt & VT_BTYPE;
-        if (bt == VT_BYTE || bt == VT_SHORT)
-            // Promote small integers:
-            o(0x13001c00 | (bt == VT_SHORT) << 13 |
-              (uint32_t)!!(rt & VT_UNSIGNED) << 30); // [su]xt[bh] w0,w0
-        else if (bt == VT_STRUCT && !(a[0] & 1)) {
-            // A struct was returned in registers, so write it out:
-            gv(RC_R(8));
-            --vtop;
-            if (a[0] == 0) {
-                int align, size = type_size(return_type, &align);
-                assert(size <= 16);
-                if (size > 8)
-                    o(0xa9000500); // stp x0,x1,[x8]
-                else if (size)
-                    arm64_strx(size > 4 ? 3 : size > 2 ? 2 : size > 1, 0, 8, 0);
-
-            }
-            else if (a[0] == 16) {
-                uint32_t j, sz, n = arm64_hfa(return_type, &sz);
-                for (j = 0; j < n; j++)
-                    o(0x3d000100 |
-                      (sz & 16) << 19 | -(sz & 8) << 27 | (sz & 4) << 29 |
-                      (a[i] / 2 - 8 + j) |
-                      j << 10); // str ([sdq])(*),[x8,#(j * sz)]
-            }
-        }
-    }
-
-    tcc_free(a1);
-    tcc_free(a);
-    tcc_free(t);
-}
-
-static unsigned long arm64_func_va_list_stack;
-static int arm64_func_va_list_gr_offs;
-static int arm64_func_va_list_vr_offs;
-static int arm64_func_sub_sp_offset;
-
-ST_FUNC void gfunc_prolog(CType *func_type)
-{
-    int n = 0;
-    int i = 0;
-    Sym *sym;
-    CType **t;
-    unsigned long *a;
-
-    // Why doesn't the caller (gen_function) set func_vt?
-    func_vt = func_type->ref->type;
-    func_vc = 144; // offset of where x8 is stored
-
-    for (sym = func_type->ref; sym; sym = sym->next)
-        ++n;
-    t = tcc_malloc(n * sizeof(*t));
-    a = tcc_malloc(n * sizeof(*a));
-
-    for (sym = func_type->ref; sym; sym = sym->next)
-        t[i++] = &sym->type;
-
-    arm64_func_va_list_stack = arm64_pcs(n - 1, t, a);
-
-    o(0xa9b27bfd); // stp x29,x30,[sp,#-224]!
-    o(0xad0087e0); // stp q0,q1,[sp,#16]
-    o(0xad018fe2); // stp q2,q3,[sp,#48]
-    o(0xad0297e4); // stp q4,q5,[sp,#80]
-    o(0xad039fe6); // stp q6,q7,[sp,#112]
-    o(0xa90923e8); // stp x8,x8,[sp,#144]
-    o(0xa90a07e0); // stp x0,x1,[sp,#160]
-    o(0xa90b0fe2); // stp x2,x3,[sp,#176]
-    o(0xa90c17e4); // stp x4,x5,[sp,#192]
-    o(0xa90d1fe6); // stp x6,x7,[sp,#208]
-
-    arm64_func_va_list_gr_offs = -64;
-    arm64_func_va_list_vr_offs = -128;
-
-    for (i = 1, sym = func_type->ref->next; sym; i++, sym = sym->next) {
-        int off = (a[i] < 16 ? 160 + a[i] / 2 * 8 :
-                   a[i] < 32 ? 16 + (a[i] - 16) / 2 * 16 :
-                   224 + ((a[i] - 32) >> 1 << 1));
-        sym_push(sym->v & ~SYM_FIELD, &sym->type,
-                 (a[i] & 1 ? VT_LLOCAL : VT_LOCAL) | lvalue_type(sym->type.t),
-                 off);
-
-        if (a[i] < 16) {
-            int align, size = type_size(&sym->type, &align);
-            arm64_func_va_list_gr_offs = (a[i] / 2 - 7 +
-                                          (!(a[i] & 1) && size > 8)) * 8;
-        }
-        else if (a[i] < 32) {
-            uint32_t hfa = arm64_hfa(&sym->type, 0);
-            arm64_func_va_list_vr_offs = (a[i] / 2 - 16 +
-                                          (hfa ? hfa : 1)) * 16;
-        }
-
-        // HFAs of float and double need to be written differently:
-        if (16 <= a[i] && a[i] < 32 && (sym->type.t & VT_BTYPE) == VT_STRUCT) {
-            uint32_t j, sz, k = arm64_hfa(&sym->type, &sz);
-            if (sz < 16)
-                for (j = 0; j < k; j++) {
-                    o(0x3d0003e0 | -(sz & 8) << 27 | (sz & 4) << 29 |
-                      ((a[i] - 16) / 2 + j) | (off / sz + j) << 10);
-                    // str ([sdq])(*),[sp,#(j * sz)]
-                }
-        }
-    }
-
-    tcc_free(a);
-    tcc_free(t);
-
-    o(0x910003fd); // mov x29,sp
-    arm64_func_sub_sp_offset = ind;
-    // In gfunc_epilog these will be replaced with code to decrement SP:
-    o(0xd503201f); // nop
-    o(0xd503201f); // nop
-    loc = 0;
-}
-
-ST_FUNC void gen_va_start(void)
-{
-    int r;
-    --vtop; // we don't need the "arg"
-    gaddrof();
-    r = intr(gv(RC_INT));
-
-    if (arm64_func_va_list_stack) {
-        //xx could use add (immediate) here
-        arm64_movimm(30, arm64_func_va_list_stack + 224);
-        o(0x8b1e03be); // add x30,x29,x30
-    }
-    else
-        o(0x910383be); // add x30,x29,#224
-    o(0xf900001e | r << 5); // str x30,[x(r)]
-
-    if (arm64_func_va_list_gr_offs) {
-        if (arm64_func_va_list_stack)
-            o(0x910383be); // add x30,x29,#224
-        o(0xf900041e | r << 5); // str x30,[x(r),#8]
-    }
-
-    if (arm64_func_va_list_vr_offs) {
-        o(0x910243be); // add x30,x29,#144
-        o(0xf900081e | r << 5); // str x30,[x(r),#16]
-    }
-
-    arm64_movimm(30, arm64_func_va_list_gr_offs);
-    o(0xb900181e | r << 5); // str w30,[x(r),#24]
-
-    arm64_movimm(30, arm64_func_va_list_vr_offs);
-    o(0xb9001c1e | r << 5); // str w30,[x(r),#28]
-
-    --vtop;
-}
-
-ST_FUNC void gen_va_arg(CType *t)
-{
-    int align, size = type_size(t, &align);
-    int fsize, hfa = arm64_hfa(t, &fsize);
-    uint32_t r0, r1;
-
-    if (is_float(t->t)) {
-        hfa = 1;
-        fsize = size;
-    }
-
-    gaddrof();
-    r0 = intr(gv(RC_INT));
-    r1 = get_reg(RC_INT);
-    vtop[0].r = r1 | lvalue_type(t->t);
-    r1 = intr(r1);
-
-    if (!hfa) {
-        uint32_t n = size > 16 ? 8 : (size + 7) & -8;
-        o(0xb940181e | r0 << 5); // ldr w30,[x(r0),#24] // __gr_offs
-        if (align == 16) {
-            assert(0); // this path untested but needed for __uint128_t
-            o(0x11003fde); // add w30,w30,#15
-            o(0x121c6fde); // and w30,w30,#-16
-        }
-        o(0x310003c0 | r1 | n << 10); // adds w(r1),w30,#(n)
-        o(0x540000ad); // b.le .+20
-        o(0xf9400000 | r1 | r0 << 5); // ldr x(r1),[x(r0)] // __stack
-        o(0x9100001e | r1 << 5 | n << 10); // add x30,x(r1),#(n)
-        o(0xf900001e | r0 << 5); // str x30,[x(r0)] // __stack
-        o(0x14000004); // b .+16
-        o(0xb9001800 | r1 | r0 << 5); // str w(r1),[x(r0),#24] // __gr_offs
-        o(0xf9400400 | r1 | r0 << 5); // ldr x(r1),[x(r0),#8] // __gr_top
-        o(0x8b3ec000 | r1 | r1 << 5); // add x(r1),x(r1),w30,sxtw
-        if (size > 16)
-            o(0xf9400000 | r1 | r1 << 5); // ldr x(r1),[x(r1)]
-    }
-    else {
-        uint32_t rsz = hfa << 4;
-        uint32_t ssz = (size + 7) & -(uint32_t)8;
-        uint32_t b1, b2;
-        o(0xb9401c1e | r0 << 5); // ldr w30,[x(r0),#28] // __vr_offs
-        o(0x310003c0 | r1 | rsz << 10); // adds w(r1),w30,#(rsz)
-        b1 = ind; o(0x5400000d); // b.le lab1
-        o(0xf9400000 | r1 | r0 << 5); // ldr x(r1),[x(r0)] // __stack
-        if (fsize == 16) {
-            o(0x91003c00 | r1 | r1 << 5); // add x(r1),x(r1),#15
-            o(0x927cec00 | r1 | r1 << 5); // and x(r1),x(r1),#-16
-        }
-        o(0x9100001e | r1 << 5 | ssz << 10); // add x30,x(r1),#(ssz)
-        o(0xf900001e | r0 << 5); // str x30,[x(r0)] // __stack
-        b2 = ind; o(0x14000000); // b lab2
-        // lab1:
-        write32le(cur_text_section->data + b1, 0x5400000d | (ind - b1) << 3);
-        o(0xb9001c00 | r1 | r0 << 5); // str w(r1),[x(r0),#28] // __vr_offs
-        o(0xf9400800 | r1 | r0 << 5); // ldr x(r1),[x(r0),#16] // __vr_top
-        if (hfa == 1 || fsize == 16)
-            o(0x8b3ec000 | r1 | r1 << 5); // add x(r1),x(r1),w30,sxtw
-        else {
-            // We need to change the layout of this HFA.
-            // Get some space on the stack using global variable "loc":
-            loc = (loc - size) & -(uint32_t)align;
-            o(0x8b3ec000 | 30 | r1 << 5); // add x30,x(r1),w30,sxtw
-            arm64_movimm(r1, loc);
-            o(0x8b0003a0 | r1 | r1 << 16); // add x(r1),x29,x(r1)
-            o(0x4c402bdc | (uint32_t)fsize << 7 |
-              (uint32_t)(hfa == 2) << 15 |
-              (uint32_t)(hfa == 3) << 14); // ld1 {v28.(4s|2d),...},[x30]
-            o(0x0d00801c | r1 << 5 | (fsize == 8) << 10 |
-              (uint32_t)(hfa != 2) << 13 |
-              (uint32_t)(hfa != 3) << 21); // st(hfa) {v28.(s|d),...}[0],[x(r1)]
-        }
-        // lab2:
-        write32le(cur_text_section->data + b2, 0x14000000 | (ind - b2) >> 2);
-    }
-}
-
-ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret,
-                       int *align, int *regsize)
-{
-    return 0;
-}
-
-ST_FUNC void gfunc_return(CType *func_type)
-{
-    CType *t = func_type;
-    unsigned long a;
-
-    arm64_pcs(0, &t, &a);
-    switch (a) {
-    case -1:
-        break;
-    case 0:
-        if ((func_type->t & VT_BTYPE) == VT_STRUCT) {
-            int align, size = type_size(func_type, &align);
-            gaddrof();
-            gv(RC_R(0));
-            arm64_ldrs(0, size);
-        }
-        else
-            gv(RC_IRET);
-        break;
-    case 1: {
-        CType type = *func_type;
-        mk_pointer(&type);
-        vset(&type, VT_LOCAL | VT_LVAL, func_vc);
-        indir();
-        vswap();
-        vstore();
-        break;
-    }
-    case 16:
-        if ((func_type->t & VT_BTYPE) == VT_STRUCT) {
-          uint32_t j, sz, n = arm64_hfa(&vtop->type, &sz);
-          gaddrof();
-          gv(RC_R(0));
-          for (j = 0; j < n; j++)
-              o(0x3d400000 |
-                (sz & 16) << 19 | -(sz & 8) << 27 | (sz & 4) << 29 |
-                j | j << 10); // ldr ([sdq])(*),[x0,#(j * sz)]
-        }
-        else
-            gv(RC_FRET);
-        break;
-    default:
-      assert(0);
-    }
-    vtop--;
-}
-
-ST_FUNC void gfunc_epilog(void)
-{
-    if (loc) {
-        // Insert instructions to subtract size of stack frame from SP.
-        unsigned char *ptr = cur_text_section->data + arm64_func_sub_sp_offset;
-        uint64_t diff = (-loc + 15) & ~15;
-        if (!(diff >> 24)) {
-            if (diff & 0xfff) // sub sp,sp,#(diff & 0xfff)
-                write32le(ptr, 0xd10003ff | (diff & 0xfff) << 10);
-            if (diff >> 12) // sub sp,sp,#(diff >> 12),lsl #12
-                write32le(ptr + 4, 0xd14003ff | (diff >> 12) << 10);
-        }
-        else {
-            // In this case we may subtract more than necessary,
-            // but always less than 17/16 of what we were aiming for.
-            int i = 0;
-            int j = 0;
-            while (diff >> 20) {
-                diff = (diff + 0xffff) >> 16;
-                ++i;
-            }
-            while (diff >> 16) {
-                diff = (diff + 1) >> 1;
-                ++j;
-            }
-            write32le(ptr, 0xd2800010 | diff << 5 | i << 21);
-            // mov x16,#(diff),lsl #(16 * i)
-            write32le(ptr + 4, 0xcb3063ff | j << 10);
-            // sub sp,sp,x16,lsl #(j)
-        }
-    }
-    o(0x910003bf); // mov sp,x29
-    o(0xa8ce7bfd); // ldp x29,x30,[sp],#224
-
-    o(0xd65f03c0); // ret
-}
-
-// Generate forward branch to label:
-ST_FUNC int gjmp(int t)
-{
-    int r = ind;
-    if (nocode_wanted)
-        return t;
-    o(t);
-    return r;
-}
-
-// Generate branch to known address:
-ST_FUNC void gjmp_addr(int a)
-{
-    assert(a - ind + 0x8000000 < 0x10000000);
-    o(0x14000000 | ((a - ind) >> 2 & 0x3ffffff));
-}
-
-ST_FUNC int gtst(int inv, int t)
-{
-    int bt = vtop->type.t & VT_BTYPE;
-    if (bt == VT_LDOUBLE) {
-        uint32_t a, b, f = fltr(gv(RC_FLOAT));
-        a = get_reg(RC_INT);
-        vpushi(0);
-        vtop[0].r = a;
-        b = get_reg(RC_INT);
-        a = intr(a);
-        b = intr(b);
-        o(0x4e083c00 | a | f << 5); // mov x(a),v(f).d[0]
-        o(0x4e183c00 | b | f << 5); // mov x(b),v(f).d[1]
-        o(0xaa000400 | a | a << 5 | b << 16); // orr x(a),x(a),x(b),lsl #1
-        o(0xb4000040 | a | !!inv << 24); // cbz/cbnz x(a),.+8
-        --vtop;
-    }
-    else if (bt == VT_FLOAT || bt == VT_DOUBLE) {
-        uint32_t a = fltr(gv(RC_FLOAT));
-        o(0x1e202008 | a << 5 | (bt != VT_FLOAT) << 22); // fcmp
-        o(0x54000040 | !!inv); // b.eq/b.ne .+8
-    }
-    else {
-        uint32_t ll = (bt == VT_PTR || bt == VT_LLONG);
-        uint32_t a = intr(gv(RC_INT));
-        o(0x34000040 | a | !!inv << 24 | ll << 31); // cbz/cbnz wA,.+8
-    }
-    --vtop;
-    return gjmp(t);
-}
-
-static int arm64_iconst(uint64_t *val, SValue *sv)
-{
-    if ((sv->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
-        return 0;
-    if (val) {
-        int t = sv->type.t;
-	int bt = t & VT_BTYPE;
-        *val = ((bt == VT_LLONG || bt == VT_PTR) ? sv->c.i :
-                (uint32_t)sv->c.i |
-                (t & VT_UNSIGNED ? 0 : -(sv->c.i & 0x80000000)));
-    }
-    return 1;
-}
-
-static int arm64_gen_opic(int op, uint32_t l, int rev, uint64_t val,
-                          uint32_t x, uint32_t a)
-{
-    if (op == '-' && !rev) {
-        val = -val;
-        op = '+';
-    }
-    val = l ? val : (uint32_t)val;
-
-    switch (op) {
-
-    case '+': {
-        uint32_t s = l ? val >> 63 : val >> 31;
-        val = s ? -val : val;
-        val = l ? val : (uint32_t)val;
-        if (!(val & ~(uint64_t)0xfff))
-            o(0x11000000 | l << 31 | s << 30 | x | a << 5 | val << 10);
-        else if (!(val & ~(uint64_t)0xfff000))
-            o(0x11400000 | l << 31 | s << 30 | x | a << 5 | val >> 12 << 10);
-        else {
-            arm64_movimm(30, val); // use x30
-            o(0x0b1e0000 | l << 31 | s << 30 | x | a << 5);
-        }
-        return 1;
-      }
-
-    case '-':
-        if (!val)
-            o(0x4b0003e0 | l << 31 | x | a << 16); // neg
-        else if (val == (l ? (uint64_t)-1 : (uint32_t)-1))
-            o(0x2a2003e0 | l << 31 | x | a << 16); // mvn
-        else {
-            arm64_movimm(30, val); // use x30
-            o(0x4b0003c0 | l << 31 | x | a << 16); // sub
-        }
-        return 1;
-
-    case '^':
-        if (val == -1 || (val == 0xffffffff && !l)) {
-            o(0x2a2003e0 | l << 31 | x | a << 16); // mvn
-            return 1;
-        }
-        // fall through
-    case '&':
-    case '|': {
-        int e = arm64_encode_bimm64(l ? val : val | val << 32);
-        if (e < 0)
-            return 0;
-        o((op == '&' ? 0x12000000 :
-           op == '|' ? 0x32000000 : 0x52000000) |
-          l << 31 | x | a << 5 | (uint32_t)e << 10);
-        return 1;
-    }
-
-    case TOK_SAR:
-    case TOK_SHL:
-    case TOK_SHR: {
-        uint32_t n = 32 << l;
-        val = val & (n - 1);
-        if (rev)
-            return 0;
-        if (!val)
-            assert(0);
-        else if (op == TOK_SHL)
-            o(0x53000000 | l << 31 | l << 22 | x | a << 5 |
-              (n - val) << 16 | (n - 1 - val) << 10); // lsl
-        else
-            o(0x13000000 | (op == TOK_SHR) << 30 | l << 31 | l << 22 |
-              x | a << 5 | val << 16 | (n - 1) << 10); // lsr/asr
-        return 1;
-    }
-
-    }
-    return 0;
-}
-
-static void arm64_gen_opil(int op, uint32_t l)
-{
-    uint32_t x, a, b;
-
-    // Special treatment for operations with a constant operand:
-    {
-        uint64_t val;
-        int rev = 1;
-
-        if (arm64_iconst(0, &vtop[0])) {
-            vswap();
-            rev = 0;
-        }
-        if (arm64_iconst(&val, &vtop[-1])) {
-            gv(RC_INT);
-            a = intr(vtop[0].r);
-            --vtop;
-            x = get_reg(RC_INT);
-            ++vtop;
-            if (arm64_gen_opic(op, l, rev, val, intr(x), a)) {
-                vtop[0].r = x;
-                vswap();
-                --vtop;
-                return;
-            }
-        }
-        if (!rev)
-            vswap();
-    }
-
-    gv2(RC_INT, RC_INT);
-    assert(vtop[-1].r < VT_CONST && vtop[0].r < VT_CONST);
-    a = intr(vtop[-1].r);
-    b = intr(vtop[0].r);
-    vtop -= 2;
-    x = get_reg(RC_INT);
-    ++vtop;
-    vtop[0].r = x;
-    x = intr(x);
-
-    switch (op) {
-    case '%':
-        // Use x30 for quotient:
-        o(0x1ac00c00 | l << 31 | 30 | a << 5 | b << 16); // sdiv
-        o(0x1b008000 | l << 31 | x | (uint32_t)30 << 5 |
-          b << 16 | a << 10); // msub
-        break;
-    case '&':
-        o(0x0a000000 | l << 31 | x | a << 5 | b << 16); // and
-        break;
-    case '*':
-        o(0x1b007c00 | l << 31 | x | a << 5 | b << 16); // mul
-        break;
-    case '+':
-        o(0x0b000000 | l << 31 | x | a << 5 | b << 16); // add
-        break;
-    case '-':
-        o(0x4b000000 | l << 31 | x | a << 5 | b << 16); // sub
-        break;
-    case '/':
-        o(0x1ac00c00 | l << 31 | x | a << 5 | b << 16); // sdiv
-        break;
-    case '^':
-        o(0x4a000000 | l << 31 | x | a << 5 | b << 16); // eor
-        break;
-    case '|':
-        o(0x2a000000 | l << 31 | x | a << 5 | b << 16); // orr
-        break;
-    case TOK_EQ:
-        o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
-        o(0x1a9f17e0 | x); // cset wA,eq
-        break;
-    case TOK_GE:
-        o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
-        o(0x1a9fb7e0 | x); // cset wA,ge
-        break;
-    case TOK_GT:
-        o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
-        o(0x1a9fd7e0 | x); // cset wA,gt
-        break;
-    case TOK_LE:
-        o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
-        o(0x1a9fc7e0 | x); // cset wA,le
-        break;
-    case TOK_LT:
-        o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
-        o(0x1a9fa7e0 | x); // cset wA,lt
-        break;
-    case TOK_NE:
-        o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
-        o(0x1a9f07e0 | x); // cset wA,ne
-        break;
-    case TOK_SAR:
-        o(0x1ac02800 | l << 31 | x | a << 5 | b << 16); // asr
-        break;
-    case TOK_SHL:
-        o(0x1ac02000 | l << 31 | x | a << 5 | b << 16); // lsl
-        break;
-    case TOK_SHR:
-        o(0x1ac02400 | l << 31 | x | a << 5 | b << 16); // lsr
-        break;
-    case TOK_UDIV:
-    case TOK_PDIV:
-        o(0x1ac00800 | l << 31 | x | a << 5 | b << 16); // udiv
-        break;
-    case TOK_UGE:
-        o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
-        o(0x1a9f37e0 | x); // cset wA,cs
-        break;
-    case TOK_UGT:
-        o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
-        o(0x1a9f97e0 | x); // cset wA,hi
-        break;
-    case TOK_ULT:
-        o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
-        o(0x1a9f27e0 | x); // cset wA,cc
-        break;
-    case TOK_ULE:
-        o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
-        o(0x1a9f87e0 | x); // cset wA,ls
-        break;
-    case TOK_UMOD:
-        // Use x30 for quotient:
-        o(0x1ac00800 | l << 31 | 30 | a << 5 | b << 16); // udiv
-        o(0x1b008000 | l << 31 | x | (uint32_t)30 << 5 |
-          b << 16 | a << 10); // msub
-        break;
-    default:
-        assert(0);
-    }
-}
-
-ST_FUNC void gen_opi(int op)
-{
-    arm64_gen_opil(op, 0);
-}
-
-ST_FUNC void gen_opl(int op)
-{
-    arm64_gen_opil(op, 1);
-}
-
-ST_FUNC void gen_opf(int op)
-{
-    uint32_t x, a, b, dbl;
-
-    if (vtop[0].type.t == VT_LDOUBLE) {
-        CType type = vtop[0].type;
-        int func = 0;
-        int cond = -1;
-        switch (op) {
-        case '*': func = TOK___multf3; break;
-        case '+': func = TOK___addtf3; break;
-        case '-': func = TOK___subtf3; break;
-        case '/': func = TOK___divtf3; break;
-        case TOK_EQ: func = TOK___eqtf2; cond = 1; break;
-        case TOK_NE: func = TOK___netf2; cond = 0; break;
-        case TOK_LT: func = TOK___lttf2; cond = 10; break;
-        case TOK_GE: func = TOK___getf2; cond = 11; break;
-        case TOK_LE: func = TOK___letf2; cond = 12; break;
-        case TOK_GT: func = TOK___gttf2; cond = 13; break;
-        default: assert(0); break;
-        }
-        vpush_global_sym(&func_old_type, func);
-        vrott(3);
-        gfunc_call(2);
-        vpushi(0);
-        vtop->r = cond < 0 ? REG_FRET : REG_IRET;
-        if (cond < 0)
-            vtop->type = type;
-        else {
-            o(0x7100001f); // cmp w0,#0
-            o(0x1a9f07e0 | (uint32_t)cond << 12); // cset w0,(cond)
-        }
-        return;
-    }
-
-    dbl = vtop[0].type.t != VT_FLOAT;
-    gv2(RC_FLOAT, RC_FLOAT);
-    assert(vtop[-1].r < VT_CONST && vtop[0].r < VT_CONST);
-    a = fltr(vtop[-1].r);
-    b = fltr(vtop[0].r);
-    vtop -= 2;
-    switch (op) {
-    case TOK_EQ: case TOK_NE:
-    case TOK_LT: case TOK_GE: case TOK_LE: case TOK_GT:
-        x = get_reg(RC_INT);
-        ++vtop;
-        vtop[0].r = x;
-        x = intr(x);
-        break;
-    default:
-        x = get_reg(RC_FLOAT);
-        ++vtop;
-        vtop[0].r = x;
-        x = fltr(x);
-        break;
-    }
-
-    switch (op) {
-    case '*':
-        o(0x1e200800 | dbl << 22 | x | a << 5 | b << 16); // fmul
-        break;
-    case '+':
-        o(0x1e202800 | dbl << 22 | x | a << 5 | b << 16); // fadd
-        break;
-    case '-':
-        o(0x1e203800 | dbl << 22 | x | a << 5 | b << 16); // fsub
-        break;
-    case '/':
-        o(0x1e201800 | dbl << 22 | x | a << 5 | b << 16); // fdiv
-        break;
-    case TOK_EQ:
-        o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp
-        o(0x1a9f17e0 | x); // cset w(x),eq
-        break;
-    case TOK_GE:
-        o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp
-        o(0x1a9fb7e0 | x); // cset w(x),ge
-        break;
-    case TOK_GT:
-        o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp
-        o(0x1a9fd7e0 | x); // cset w(x),gt
-        break;
-    case TOK_LE:
-        o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp
-        o(0x1a9f87e0 | x); // cset w(x),ls
-        break;
-    case TOK_LT:
-        o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp
-        o(0x1a9f57e0 | x); // cset w(x),mi
-        break;
-    case TOK_NE:
-        o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp
-        o(0x1a9f07e0 | x); // cset w(x),ne
-        break;
-    default:
-        assert(0);
-    }
-}
-
-// Generate sign extension from 32 to 64 bits:
-ST_FUNC void gen_cvt_sxtw(void)
-{
-    uint32_t r = intr(gv(RC_INT));
-    o(0x93407c00 | r | r << 5); // sxtw x(r),w(r)
-}
-
-ST_FUNC void gen_cvt_itof(int t)
-{
-    if (t == VT_LDOUBLE) {
-        int f = vtop->type.t;
-        int func = (f & VT_BTYPE) == VT_LLONG ?
-          (f & VT_UNSIGNED ? TOK___floatunditf : TOK___floatditf) :
-          (f & VT_UNSIGNED ? TOK___floatunsitf : TOK___floatsitf);
-        vpush_global_sym(&func_old_type, func);
-        vrott(2);
-        gfunc_call(1);
-        vpushi(0);
-        vtop->type.t = t;
-        vtop->r = REG_FRET;
-        return;
-    }
-    else {
-        int d, n = intr(gv(RC_INT));
-        int s = !(vtop->type.t & VT_UNSIGNED);
-        uint32_t l = ((vtop->type.t & VT_BTYPE) == VT_LLONG);
-        --vtop;
-        d = get_reg(RC_FLOAT);
-        ++vtop;
-        vtop[0].r = d;
-        o(0x1e220000 | (uint32_t)!s << 16 |
-          (uint32_t)(t != VT_FLOAT) << 22 | fltr(d) |
-          l << 31 | n << 5); // [us]cvtf [sd](d),[wx](n)
-    }
-}
-
-ST_FUNC void gen_cvt_ftoi(int t)
-{
-    if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
-        int func = (t & VT_BTYPE) == VT_LLONG ?
-          (t & VT_UNSIGNED ? TOK___fixunstfdi : TOK___fixtfdi) :
-          (t & VT_UNSIGNED ? TOK___fixunstfsi : TOK___fixtfsi);
-        vpush_global_sym(&func_old_type, func);
-        vrott(2);
-        gfunc_call(1);
-        vpushi(0);
-        vtop->type.t = t;
-        vtop->r = REG_IRET;
-        return;
-    }
-    else {
-        int d, n = fltr(gv(RC_FLOAT));
-        uint32_t l = ((vtop->type.t & VT_BTYPE) != VT_FLOAT);
-        --vtop;
-        d = get_reg(RC_INT);
-        ++vtop;
-        vtop[0].r = d;
-        o(0x1e380000 |
-          (uint32_t)!!(t & VT_UNSIGNED) << 16 |
-          (uint32_t)((t & VT_BTYPE) == VT_LLONG) << 31 | intr(d) |
-          l << 22 | n << 5); // fcvtz[su] [wx](d),[sd](n)
-    }
-}
-
-ST_FUNC void gen_cvt_ftof(int t)
-{
-    int f = vtop[0].type.t;
-    assert(t == VT_FLOAT || t == VT_DOUBLE || t == VT_LDOUBLE);
-    assert(f == VT_FLOAT || f == VT_DOUBLE || f == VT_LDOUBLE);
-    if (t == f)
-        return;
-
-    if (t == VT_LDOUBLE || f == VT_LDOUBLE) {
-        int func = (t == VT_LDOUBLE) ?
-            (f == VT_FLOAT ? TOK___extendsftf2 : TOK___extenddftf2) :
-            (t == VT_FLOAT ? TOK___trunctfsf2 : TOK___trunctfdf2);
-        vpush_global_sym(&func_old_type, func);
-        vrott(2);
-        gfunc_call(1);
-        vpushi(0);
-        vtop->type.t = t;
-        vtop->r = REG_FRET;
-    }
-    else {
-        int x, a;
-        gv(RC_FLOAT);
-        assert(vtop[0].r < VT_CONST);
-        a = fltr(vtop[0].r);
-        --vtop;
-        x = get_reg(RC_FLOAT);
-        ++vtop;
-        vtop[0].r = x;
-        x = fltr(x);
-
-        if (f == VT_FLOAT)
-            o(0x1e22c000 | x | a << 5); // fcvt d(x),s(a)
-        else
-            o(0x1e624000 | x | a << 5); // fcvt s(x),d(a)
-    }
-}
-
-ST_FUNC void ggoto(void)
-{
-    arm64_gen_bl_or_b(1);
-    --vtop;
-}
-
-ST_FUNC void gen_clear_cache(void)
-{
-    uint32_t beg, end, dsz, isz, p, lab1, b1;
-    gv2(RC_INT, RC_INT);
-    vpushi(0);
-    vtop->r = get_reg(RC_INT);
-    vpushi(0);
-    vtop->r = get_reg(RC_INT);
-    vpushi(0);
-    vtop->r = get_reg(RC_INT);
-    beg = intr(vtop[-4].r); // x0
-    end = intr(vtop[-3].r); // x1
-    dsz = intr(vtop[-2].r); // x2
-    isz = intr(vtop[-1].r); // x3
-    p = intr(vtop[0].r);    // x4
-    vtop -= 5;
-
-    o(0xd53b0020 | isz); // mrs x(isz),ctr_el0
-    o(0x52800080 | p); // mov w(p),#4
-    o(0x53104c00 | dsz | isz << 5); // ubfx w(dsz),w(isz),#16,#4
-    o(0x1ac02000 | dsz | p << 5 | dsz << 16); // lsl w(dsz),w(p),w(dsz)
-    o(0x12000c00 | isz | isz << 5); // and w(isz),w(isz),#15
-    o(0x1ac02000 | isz | p << 5 | isz << 16); // lsl w(isz),w(p),w(isz)
-    o(0x51000400 | p | dsz << 5); // sub w(p),w(dsz),#1
-    o(0x8a240004 | p | beg << 5 | p << 16); // bic x(p),x(beg),x(p)
-    b1 = ind; o(0x14000000); // b
-    lab1 = ind;
-    o(0xd50b7b20 | p); // dc cvau,x(p)
-    o(0x8b000000 | p | p << 5 | dsz << 16); // add x(p),x(p),x(dsz)
-    write32le(cur_text_section->data + b1, 0x14000000 | (ind - b1) >> 2);
-    o(0xeb00001f | p << 5 | end << 16); // cmp x(p),x(end)
-    o(0x54ffffa3 | ((lab1 - ind) << 3 & 0xffffe0)); // b.cc lab1
-    o(0xd5033b9f); // dsb ish
-    o(0x51000400 | p | isz << 5); // sub w(p),w(isz),#1
-    o(0x8a240004 | p | beg << 5 | p << 16); // bic x(p),x(beg),x(p)
-    b1 = ind; o(0x14000000); // b
-    lab1 = ind;
-    o(0xd50b7520 | p); // ic ivau,x(p)
-    o(0x8b000000 | p | p << 5 | isz << 16); // add x(p),x(p),x(isz)
-    write32le(cur_text_section->data + b1, 0x14000000 | (ind - b1) >> 2);
-    o(0xeb00001f | p << 5 | end << 16); // cmp x(p),x(end)
-    o(0x54ffffa3 | ((lab1 - ind) << 3 & 0xffffe0)); // b.cc lab1
-    o(0xd5033b9f); // dsb ish
-    o(0xd5033fdf); // isb
-}
-
-ST_FUNC void gen_vla_sp_save(int addr) {
-    uint32_t r = intr(get_reg(RC_INT));
-    o(0x910003e0 | r); // mov x(r),sp
-    arm64_strx(3, r, 29, addr);
-}
-
-ST_FUNC void gen_vla_sp_restore(int addr) {
-    // Use x30 because this function can be called when there
-    // is a live return value in x0 but there is nothing on
-    // the value stack to prevent get_reg from returning x0.
-    uint32_t r = 30;
-    arm64_ldrx(0, 3, r, 29, addr);
-    o(0x9100001f | r << 5); // mov sp,x(r)
-}
-
-ST_FUNC void gen_vla_alloc(CType *type, int align) {
-    uint32_t r = intr(gv(RC_INT));
-    o(0x91003c00 | r | r << 5); // add x(r),x(r),#15
-    o(0x927cec00 | r | r << 5); // bic x(r),x(r),#15
-    o(0xcb2063ff | r << 16); // sub sp,sp,x(r)
-    vpop();
-}
-
-/* end of A64 code generator */
-/*************************************************************/
-#endif
-/*************************************************************/
diff --git a/tinyc/arm64-link.c b/tinyc/arm64-link.c
deleted file mode 100644
index 73e503e6b..000000000
--- a/tinyc/arm64-link.c
+++ /dev/null
@@ -1,250 +0,0 @@
-#ifdef TARGET_DEFS_ONLY
-
-#define EM_TCC_TARGET EM_AARCH64
-
-#define R_DATA_32  R_AARCH64_ABS32
-#define R_DATA_PTR R_AARCH64_ABS64
-#define R_JMP_SLOT R_AARCH64_JUMP_SLOT
-#define R_GLOB_DAT R_AARCH64_GLOB_DAT
-#define R_COPY     R_AARCH64_COPY
-#define R_RELATIVE R_AARCH64_RELATIVE
-
-#define R_NUM      R_AARCH64_NUM
-
-#define ELF_START_ADDR 0x00400000
-#define ELF_PAGE_SIZE 0x1000
-
-#define PCRELATIVE_DLLPLT 1
-#define RELOCATE_DLLPLT 1
-
-#else /* !TARGET_DEFS_ONLY */
-
-#include "tcc.h"
-
-/* Returns 1 for a code relocation, 0 for a data relocation. For unknown
-   relocations, returns -1. */
-int code_reloc (int reloc_type)
-{
-    switch (reloc_type) {
-        case R_AARCH64_ABS32:
-        case R_AARCH64_ABS64:
-	case R_AARCH64_PREL32:
-        case R_AARCH64_MOVW_UABS_G0_NC:
-        case R_AARCH64_MOVW_UABS_G1_NC:
-        case R_AARCH64_MOVW_UABS_G2_NC:
-        case R_AARCH64_MOVW_UABS_G3:
-        case R_AARCH64_ADR_PREL_PG_HI21:
-        case R_AARCH64_ADD_ABS_LO12_NC:
-        case R_AARCH64_ADR_GOT_PAGE:
-        case R_AARCH64_LD64_GOT_LO12_NC:
-        case R_AARCH64_GLOB_DAT:
-        case R_AARCH64_COPY:
-            return 0;
-
-        case R_AARCH64_JUMP26:
-        case R_AARCH64_CALL26:
-        case R_AARCH64_JUMP_SLOT:
-            return 1;
-    }
-
-    tcc_error ("Unknown relocation type: %d", reloc_type);
-    return -1;
-}
-
-/* Returns an enumerator to describe whether and when the relocation needs a
-   GOT and/or PLT entry to be created. See tcc.h for a description of the
-   different values. */
-int gotplt_entry_type (int reloc_type)
-{
-    switch (reloc_type) {
-	case R_AARCH64_PREL32:
-        case R_AARCH64_MOVW_UABS_G0_NC:
-        case R_AARCH64_MOVW_UABS_G1_NC:
-        case R_AARCH64_MOVW_UABS_G2_NC:
-        case R_AARCH64_MOVW_UABS_G3:
-        case R_AARCH64_ADR_PREL_PG_HI21:
-        case R_AARCH64_ADD_ABS_LO12_NC:
-        case R_AARCH64_GLOB_DAT:
-        case R_AARCH64_JUMP_SLOT:
-        case R_AARCH64_COPY:
-            return NO_GOTPLT_ENTRY;
-
-        case R_AARCH64_ABS32:
-        case R_AARCH64_ABS64:
-        case R_AARCH64_JUMP26:
-        case R_AARCH64_CALL26:
-            return AUTO_GOTPLT_ENTRY;
-
-        case R_AARCH64_ADR_GOT_PAGE:
-        case R_AARCH64_LD64_GOT_LO12_NC:
-            return ALWAYS_GOTPLT_ENTRY;
-    }
-
-    tcc_error ("Unknown relocation type: %d", reloc_type);
-    return -1;
-}
-
-ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr)
-{
-    Section *plt = s1->plt;
-    uint8_t *p;
-    unsigned plt_offset;
-
-    if (s1->output_type == TCC_OUTPUT_DLL)
-        tcc_error("DLLs unimplemented!");
-
-    if (plt->data_offset == 0) {
-        section_ptr_add(plt, 32);
-    }
-    plt_offset = plt->data_offset;
-
-    p = section_ptr_add(plt, 16);
-    write32le(p, got_offset);
-    write32le(p + 4, (uint64_t) got_offset >> 32);
-    return plt_offset;
-}
-
-/* relocate the PLT: compute addresses and offsets in the PLT now that final
-   address for PLT and GOT are known (see fill_program_header) */
-ST_FUNC void relocate_plt(TCCState *s1)
-{
-    uint8_t *p, *p_end;
-
-    if (!s1->plt)
-      return;
-
-    p = s1->plt->data;
-    p_end = p + s1->plt->data_offset;
-
-    if (p < p_end) {
-        uint64_t plt = s1->plt->sh_addr;
-        uint64_t got = s1->got->sh_addr;
-        uint64_t off = (got >> 12) - (plt >> 12);
-        if ((off + ((uint32_t)1 << 20)) >> 21)
-            tcc_error("Failed relocating PLT (off=0x%lx, got=0x%lx, plt=0x%lx)", off, got, plt);
-        write32le(p, 0xa9bf7bf0); // stp x16,x30,[sp,#-16]!
-        write32le(p + 4, (0x90000010 | // adrp x16,...
-			  (off & 0x1ffffc) << 3 | (off & 3) << 29));
-        write32le(p + 8, (0xf9400211 | // ldr x17,[x16,#...]
-			  (got & 0xff8) << 7));
-        write32le(p + 12, (0x91000210 | // add x16,x16,#...
-			   (got & 0xfff) << 10));
-        write32le(p + 16, 0xd61f0220); // br x17
-        write32le(p + 20, 0xd503201f); // nop
-        write32le(p + 24, 0xd503201f); // nop
-        write32le(p + 28, 0xd503201f); // nop
-        p += 32;
-        while (p < p_end) {
-            uint64_t pc = plt + (p - s1->plt->data);
-            uint64_t addr = got + read64le(p);
-            uint64_t off = (addr >> 12) - (pc >> 12);
-            if ((off + ((uint32_t)1 << 20)) >> 21)
-                tcc_error("Failed relocating PLT (off=0x%lx, addr=0x%lx, pc=0x%lx)", off, addr, pc);
-            write32le(p, (0x90000010 | // adrp x16,...
-			  (off & 0x1ffffc) << 3 | (off & 3) << 29));
-            write32le(p + 4, (0xf9400211 | // ldr x17,[x16,#...]
-			      (addr & 0xff8) << 7));
-            write32le(p + 8, (0x91000210 | // add x16,x16,#...
-			      (addr & 0xfff) << 10));
-            write32le(p + 12, 0xd61f0220); // br x17
-            p += 16;
-        }
-    }
-}
-
-void relocate_init(Section *sr) {}
-
-void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val)
-{
-    int sym_index = ELFW(R_SYM)(rel->r_info);
-#ifdef DEBUG_RELOC
-    ElfW(Sym) *sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
-#endif
-
-    switch(type) {
-        case R_AARCH64_ABS64:
-            write64le(ptr, val);
-            return;
-        case R_AARCH64_ABS32:
-            write32le(ptr, val);
-            return;
-	case R_AARCH64_PREL32:
-	    write32le(ptr, val - addr);
-	    return;
-        case R_AARCH64_MOVW_UABS_G0_NC:
-            write32le(ptr, ((read32le(ptr) & 0xffe0001f) |
-                            (val & 0xffff) << 5));
-            return;
-        case R_AARCH64_MOVW_UABS_G1_NC:
-            write32le(ptr, ((read32le(ptr) & 0xffe0001f) |
-                            (val >> 16 & 0xffff) << 5));
-            return;
-        case R_AARCH64_MOVW_UABS_G2_NC:
-            write32le(ptr, ((read32le(ptr) & 0xffe0001f) |
-                            (val >> 32 & 0xffff) << 5));
-            return;
-        case R_AARCH64_MOVW_UABS_G3:
-            write32le(ptr, ((read32le(ptr) & 0xffe0001f) |
-                            (val >> 48 & 0xffff) << 5));
-            return;
-        case R_AARCH64_ADR_PREL_PG_HI21: {
-            uint64_t off = (val >> 12) - (addr >> 12);
-            if ((off + ((uint64_t)1 << 20)) >> 21)
-                tcc_error("R_AARCH64_ADR_PREL_PG_HI21 relocation failed");
-            write32le(ptr, ((read32le(ptr) & 0x9f00001f) |
-                            (off & 0x1ffffc) << 3 | (off & 3) << 29));
-            return;
-        }
-        case R_AARCH64_ADD_ABS_LO12_NC:
-            write32le(ptr, ((read32le(ptr) & 0xffc003ff) |
-                            (val & 0xfff) << 10));
-            return;
-        case R_AARCH64_JUMP26:
-        case R_AARCH64_CALL26:
-#ifdef DEBUG_RELOC
-	    printf ("reloc %d @ 0x%lx: val=0x%lx name=%s\n", type, addr, val,
-		    (char *) symtab_section->link->data + sym->st_name);
-#endif
-            if (((val - addr) + ((uint64_t)1 << 27)) & ~(uint64_t)0xffffffc)
-                tcc_error("R_AARCH64_(JUMP|CALL)26 relocation failed"
-                          " (val=%lx, addr=%lx)", val, addr);
-            write32le(ptr, (0x14000000 |
-                            (uint32_t)(type == R_AARCH64_CALL26) << 31 |
-                            ((val - addr) >> 2 & 0x3ffffff)));
-            return;
-        case R_AARCH64_ADR_GOT_PAGE: {
-            uint64_t off =
-                (((s1->got->sh_addr +
-                   s1->sym_attrs[sym_index].got_offset) >> 12) - (addr >> 12));
-            if ((off + ((uint64_t)1 << 20)) >> 21)
-                tcc_error("R_AARCH64_ADR_GOT_PAGE relocation failed");
-            write32le(ptr, ((read32le(ptr) & 0x9f00001f) |
-                            (off & 0x1ffffc) << 3 | (off & 3) << 29));
-            return;
-        }
-        case R_AARCH64_LD64_GOT_LO12_NC:
-            write32le(ptr,
-                      ((read32le(ptr) & 0xfff803ff) |
-                       ((s1->got->sh_addr +
-                         s1->sym_attrs[sym_index].got_offset) & 0xff8) << 7));
-            return;
-        case R_AARCH64_COPY:
-            return;
-        case R_AARCH64_GLOB_DAT:
-        case R_AARCH64_JUMP_SLOT:
-            /* They don't need addend */
-#ifdef DEBUG_RELOC
-	    printf ("reloc %d @ 0x%lx: val=0x%lx name=%s\n", type, addr,
-		    val - rel->r_addend,
-		    (char *) symtab_section->link->data + sym->st_name);
-#endif
-            write64le(ptr, val - rel->r_addend);
-            return;
-        default:
-            fprintf(stderr, "FIXME: handle reloc type %x at %x [%p] to %x\n",
-                    type, (unsigned)addr, ptr, (unsigned)val);
-            return;
-    }
-}
-
-#endif /* !TARGET_DEFS_ONLY */
diff --git a/tinyc/c67-gen.c b/tinyc/c67-gen.c
deleted file mode 100644
index bcb4b0e3e..000000000
--- a/tinyc/c67-gen.c
+++ /dev/null
@@ -1,2540 +0,0 @@
-/*
- *  TMS320C67xx code generator for TCC
- * 
- *  Copyright (c) 2001, 2002 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#ifdef TARGET_DEFS_ONLY
-
-/* #define ASSEMBLY_LISTING_C67 */
-
-/* number of available registers */
-#define NB_REGS            24
-
-/* a register can belong to several classes. The classes must be
-   sorted from more general to more precise (see gv2() code which does
-   assumptions on it). */
-#define RC_INT     0x0001	/* generic integer register */
-#define RC_FLOAT   0x0002	/* generic float register */
-#define RC_EAX     0x0004
-#define RC_ST0     0x0008
-#define RC_ECX     0x0010
-#define RC_EDX     0x0020
-#define RC_INT_BSIDE  0x00000040	/* generic integer register  on b side */
-#define RC_C67_A4     0x00000100
-#define RC_C67_A5     0x00000200
-#define RC_C67_B4     0x00000400
-#define RC_C67_B5     0x00000800
-#define RC_C67_A6     0x00001000
-#define RC_C67_A7     0x00002000
-#define RC_C67_B6     0x00004000
-#define RC_C67_B7     0x00008000
-#define RC_C67_A8     0x00010000
-#define RC_C67_A9     0x00020000
-#define RC_C67_B8     0x00040000
-#define RC_C67_B9     0x00080000
-#define RC_C67_A10    0x00100000
-#define RC_C67_A11    0x00200000
-#define RC_C67_B10    0x00400000
-#define RC_C67_B11    0x00800000
-#define RC_C67_A12    0x01000000
-#define RC_C67_A13    0x02000000
-#define RC_C67_B12    0x04000000
-#define RC_C67_B13    0x08000000
-#define RC_IRET    RC_C67_A4	/* function return: integer register */
-#define RC_LRET    RC_C67_A5	/* function return: second integer register */
-#define RC_FRET    RC_C67_A4	/* function return: float register */
-
-/* pretty names for the registers */
-enum {
-    TREG_EAX = 0,		// really A2
-    TREG_ECX,			// really A3
-    TREG_EDX,			// really B0
-    TREG_ST0,			// really B1
-    TREG_C67_A4,
-    TREG_C67_A5,
-    TREG_C67_B4,
-    TREG_C67_B5,
-    TREG_C67_A6,
-    TREG_C67_A7,
-    TREG_C67_B6,
-    TREG_C67_B7,
-    TREG_C67_A8,
-    TREG_C67_A9,
-    TREG_C67_B8,
-    TREG_C67_B9,
-    TREG_C67_A10,
-    TREG_C67_A11,
-    TREG_C67_B10,
-    TREG_C67_B11,
-    TREG_C67_A12,
-    TREG_C67_A13,
-    TREG_C67_B12,
-    TREG_C67_B13,
-};
-
-/* return registers for function */
-#define REG_IRET TREG_C67_A4	/* single word int return register */
-#define REG_LRET TREG_C67_A5	/* second word return register (for long long) */
-#define REG_FRET TREG_C67_A4	/* float return register */
-
-/* defined if function parameters must be evaluated in reverse order */
-/* #define INVERT_FUNC_PARAMS */
-
-/* defined if structures are passed as pointers. Otherwise structures
-   are directly pushed on stack. */
-/* #define FUNC_STRUCT_PARAM_AS_PTR */
-
-/* pointer size, in bytes */
-#define PTR_SIZE 4
-
-/* long double size and alignment, in bytes */
-#define LDOUBLE_SIZE  12
-#define LDOUBLE_ALIGN 4
-/* maximum alignment (for aligned attribute support) */
-#define MAX_ALIGN     8
-
-/******************************************************/
-#else /* ! TARGET_DEFS_ONLY */
-/******************************************************/
-#include "tcc.h"
-
-ST_DATA const int reg_classes[NB_REGS] = {
-    /* eax */ RC_INT | RC_FLOAT | RC_EAX, 
-    // only allow even regs for floats (allow for doubles)
-    /* ecx */ RC_INT | RC_ECX,
-    /* edx */ RC_INT | RC_INT_BSIDE | RC_FLOAT | RC_EDX,
-    // only allow even regs for floats (allow for doubles)
-    /* st0 */ RC_INT | RC_INT_BSIDE | RC_ST0,
-    /* A4  */ RC_C67_A4,
-    /* A5  */ RC_C67_A5,
-    /* B4  */ RC_C67_B4,
-    /* B5  */ RC_C67_B5,
-    /* A6  */ RC_C67_A6,
-    /* A7  */ RC_C67_A7,
-    /* B6  */ RC_C67_B6,
-    /* B7  */ RC_C67_B7,
-    /* A8  */ RC_C67_A8,
-    /* A9  */ RC_C67_A9,
-    /* B8  */ RC_C67_B8,
-    /* B9  */ RC_C67_B9,
-    /* A10  */ RC_C67_A10,
-    /* A11  */ RC_C67_A11,
-    /* B10  */ RC_C67_B10,
-    /* B11  */ RC_C67_B11,
-    /* A12  */ RC_C67_A10,
-    /* A13  */ RC_C67_A11,
-    /* B12  */ RC_C67_B10,
-    /* B13  */ RC_C67_B11
-};
-
-// although tcc thinks it is passing parameters on the stack,
-// the C67 really passes up to the first 10 params in special
-// regs or regs pairs (for 64 bit params).  So keep track of
-// the stack offsets so we can translate to the appropriate 
-// reg (pair)
-
-#define NoCallArgsPassedOnStack 10
-int NoOfCurFuncArgs;
-int TranslateStackToReg[NoCallArgsPassedOnStack];
-int ParamLocOnStack[NoCallArgsPassedOnStack];
-int TotalBytesPushedOnStack;
-
-#ifndef FALSE
-# define FALSE 0
-# define TRUE 1
-#endif
-
-#undef BOOL
-#define BOOL int
-
-#define ALWAYS_ASSERT(x) \
-do {\
-   if (!(x))\
-       tcc_error("internal compiler error file at %s:%d", __FILE__, __LINE__);\
-} while (0)
-
-/******************************************************/
-static unsigned long func_sub_sp_offset;
-static int func_ret_sub;
-
-static BOOL C67_invert_test;
-static int C67_compare_reg;
-
-#ifdef ASSEMBLY_LISTING_C67
-FILE *f = NULL;
-#endif
-
-void C67_g(int c)
-{
-    int ind1;
-    if (nocode_wanted)
-        return;
-#ifdef ASSEMBLY_LISTING_C67
-    fprintf(f, " %08X", c);
-#endif
-    ind1 = ind + 4;
-    if (ind1 > (int) cur_text_section->data_allocated)
-	section_realloc(cur_text_section, ind1);
-    cur_text_section->data[ind] = c & 0xff;
-    cur_text_section->data[ind + 1] = (c >> 8) & 0xff;
-    cur_text_section->data[ind + 2] = (c >> 16) & 0xff;
-    cur_text_section->data[ind + 3] = (c >> 24) & 0xff;
-    ind = ind1;
-}
-
-
-/* output a symbol and patch all calls to it */
-void gsym_addr(int t, int a)
-{
-    int n, *ptr;
-    while (t) {
-	ptr = (int *) (cur_text_section->data + t);
-	{
-	    Sym *sym;
-
-	    // extract 32 bit address from MVKH/MVKL
-	    n = ((*ptr >> 7) & 0xffff);
-	    n |= ((*(ptr + 1) >> 7) & 0xffff) << 16;
-
-	    // define a label that will be relocated
-
-	    sym = get_sym_ref(&char_pointer_type, cur_text_section, a, 0);
-	    greloc(cur_text_section, sym, t, R_C60LO16);
-	    greloc(cur_text_section, sym, t + 4, R_C60HI16);
-
-	    // clear out where the pointer was
-
-	    *ptr &= ~(0xffff << 7);
-	    *(ptr + 1) &= ~(0xffff << 7);
-	}
-	t = n;
-    }
-}
-
-void gsym(int t)
-{
-    gsym_addr(t, ind);
-}
-
-// these are regs that tcc doesn't really know about, 
-// but assign them unique values so the mapping routines
-// can distinguish them
-
-#define C67_A0 105
-#define C67_SP 106
-#define C67_B3 107
-#define C67_FP 108
-#define C67_B2 109
-#define C67_CREG_ZERO -1	/* Special code for no condition reg test */
-
-
-int ConvertRegToRegClass(int r)
-{
-    // only works for A4-B13
-
-    return RC_C67_A4 << (r - TREG_C67_A4);
-}
-
-
-// map TCC reg to C67 reg number
-
-int C67_map_regn(int r)
-{
-    if (r == 0)			// normal tcc regs
-	return 0x2;		// A2
-    else if (r == 1)		// normal tcc regs
-	return 3;		// A3
-    else if (r == 2)		// normal tcc regs
-	return 0;		// B0
-    else if (r == 3)		// normal tcc regs
-	return 1;		// B1
-    else if (r >= TREG_C67_A4 && r <= TREG_C67_B13)	// these form a pattern of alt pairs
-	return (((r & 0xfffffffc) >> 1) | (r & 1)) + 2;
-    else if (r == C67_A0)
-	return 0;		// set to A0 (offset reg)
-    else if (r == C67_B2)
-	return 2;		// set to B2 (offset reg)
-    else if (r == C67_B3)
-	return 3;		// set to B3 (return address reg)
-    else if (r == C67_SP)
-	return 15;		// set to SP (B15) (offset reg)
-    else if (r == C67_FP)
-	return 15;		// set to FP (A15) (offset reg)
-    else if (r == C67_CREG_ZERO)
-	return 0;		// Special code for no condition reg test
-    else
-	ALWAYS_ASSERT(FALSE);
-
-    return 0;
-}
-
-// mapping from tcc reg number to 
-// C67 register to condition code field
-//
-// valid condition code regs are:
-//
-// tcc reg 2 ->B0 -> 1
-// tcc reg 3 ->B1 -> 2
-// tcc reg 0 -> A2 -> 5
-// tcc reg 1 -> A3 -> X
-// tcc reg      B2 -> 3
-
-int C67_map_regc(int r)
-{
-    if (r == 0)			// normal tcc regs
-	return 0x5;
-    else if (r == 2)		// normal tcc regs
-	return 0x1;
-    else if (r == 3)		// normal tcc regs
-	return 0x2;
-    else if (r == C67_B2)	// normal tcc regs
-	return 0x3;
-    else if (r == C67_CREG_ZERO)
-	return 0;		// Special code for no condition reg test
-    else
-	ALWAYS_ASSERT(FALSE);
-
-    return 0;
-}
-
-
-// map TCC reg to C67 reg side A or B
-
-int C67_map_regs(int r)
-{
-    if (r == 0)			// normal tcc regs
-	return 0x0;
-    else if (r == 1)		// normal tcc regs
-	return 0x0;
-    else if (r == 2)		// normal tcc regs
-	return 0x1;
-    else if (r == 3)		// normal tcc regs
-	return 0x1;
-    else if (r >= TREG_C67_A4 && r <= TREG_C67_B13)	// these form a pattern of alt pairs
-	return (r & 2) >> 1;
-    else if (r == C67_A0)
-	return 0;		// set to A side 
-    else if (r == C67_B2)
-	return 1;		// set to B side 
-    else if (r == C67_B3)
-	return 1;		// set to B side
-    else if (r == C67_SP)
-	return 0x1;		// set to SP (B15) B side 
-    else if (r == C67_FP)
-	return 0x0;		// set to FP (A15) A side 
-    else
-	ALWAYS_ASSERT(FALSE);
-
-    return 0;
-}
-
-int C67_map_S12(char *s)
-{
-    if (strstr(s, ".S1") != NULL)
-	return 0;
-    else if (strcmp(s, ".S2"))
-	return 1;
-    else
-	ALWAYS_ASSERT(FALSE);
-
-    return 0;
-}
-
-int C67_map_D12(char *s)
-{
-    if (strstr(s, ".D1") != NULL)
-	return 0;
-    else if (strcmp(s, ".D2"))
-	return 1;
-    else
-	ALWAYS_ASSERT(FALSE);
-
-    return 0;
-}
-
-
-
-void C67_asm(char *s, int a, int b, int c)
-{
-    BOOL xpath;
-
-#ifdef ASSEMBLY_LISTING_C67
-    if (!f) {
-	f = fopen("TCC67_out.txt", "wt");
-    }
-    fprintf(f, "%04X ", ind);
-#endif
-
-    if (strstr(s, "MVKL") == s) {
-	C67_g((C67_map_regn(b) << 23) |
-	      ((a & 0xffff) << 7) | (0x0a << 2) | (C67_map_regs(b) << 1));
-    } else if (strstr(s, "MVKH") == s) {
-	C67_g((C67_map_regn(b) << 23) |
-	      (((a >> 16) & 0xffff) << 7) |
-	      (0x1a << 2) | (C67_map_regs(b) << 1));
-    } else if (strstr(s, "STW.D SP POST DEC") == s) {
-	C67_g((C67_map_regn(a) << 23) |	//src
-	      (15 << 18) |	//SP B15
-	      (2 << 13) |	//ucst5 (must keep 8 byte boundary !!)
-	      (0xa << 9) |	//mode a = post dec ucst
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (1 << 7) |	//y D1/D2 use B side
-	      (7 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(a) << 1) |	//side of src
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "STB.D *+SP[A0]") == s) {
-	C67_g((C67_map_regn(a) << 23) |	//src
-	      (15 << 18) |	//base reg A15
-	      (0 << 13) |	//offset reg A0
-	      (5 << 9) |	//mode 5 = pos offset, base reg + off reg
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (0 << 7) |	//y D1/D2 A side
-	      (3 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(a) << 1) |	//side of src
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "STH.D *+SP[A0]") == s) {
-	C67_g((C67_map_regn(a) << 23) |	//src
-	      (15 << 18) |	//base reg A15
-	      (0 << 13) |	//offset reg A0
-	      (5 << 9) |	//mode 5 = pos offset, base reg + off reg
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (0 << 7) |	//y D1/D2 A side
-	      (5 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(a) << 1) |	//side of src
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "STB.D *+SP[A0]") == s) {
-	C67_g((C67_map_regn(a) << 23) |	//src
-	      (15 << 18) |	//base reg A15
-	      (0 << 13) |	//offset reg A0
-	      (5 << 9) |	//mode 5 = pos offset, base reg + off reg
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (0 << 7) |	//y D1/D2 A side
-	      (3 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(a) << 1) |	//side of src
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "STH.D *+SP[A0]") == s) {
-	C67_g((C67_map_regn(a) << 23) |	//src
-	      (15 << 18) |	//base reg A15
-	      (0 << 13) |	//offset reg A0
-	      (5 << 9) |	//mode 5 = pos offset, base reg + off reg
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (0 << 7) |	//y D1/D2 A side
-	      (5 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(a) << 1) |	//side of src
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "STW.D *+SP[A0]") == s) {
-	C67_g((C67_map_regn(a) << 23) |	//src
-	      (15 << 18) |	//base reg A15
-	      (0 << 13) |	//offset reg A0
-	      (5 << 9) |	//mode 5 = pos offset, base reg + off reg
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (0 << 7) |	//y D1/D2 A side
-	      (7 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(a) << 1) |	//side of src
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "STW.D *") == s) {
-	C67_g((C67_map_regn(a) << 23) |	//src
-	      (C67_map_regn(b) << 18) |	//base reg A0
-	      (0 << 13) |	//cst5
-	      (1 << 9) |	//mode 1 = pos cst offset
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (C67_map_regs(b) << 7) |	//y D1/D2 base reg side
-	      (7 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(a) << 1) |	//side of src
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "STH.D *") == s) {
-	C67_g((C67_map_regn(a) << 23) |	//src
-	      (C67_map_regn(b) << 18) |	//base reg A0
-	      (0 << 13) |	//cst5
-	      (1 << 9) |	//mode 1 = pos cst offset
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (C67_map_regs(b) << 7) |	//y D1/D2 base reg side
-	      (5 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(a) << 1) |	//side of src
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "STB.D *") == s) {
-	C67_g((C67_map_regn(a) << 23) |	//src
-	      (C67_map_regn(b) << 18) |	//base reg A0
-	      (0 << 13) |	//cst5
-	      (1 << 9) |	//mode 1 = pos cst offset
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (C67_map_regs(b) << 7) |	//y D1/D2 base reg side
-	      (3 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(a) << 1) |	//side of src
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "STW.D +*") == s) {
-	ALWAYS_ASSERT(c < 32);
-	C67_g((C67_map_regn(a) << 23) |	//src
-	      (C67_map_regn(b) << 18) |	//base reg A0
-	      (c << 13) |	//cst5
-	      (1 << 9) |	//mode 1 = pos cst offset
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (C67_map_regs(b) << 7) |	//y D1/D2 base reg side
-	      (7 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(a) << 1) |	//side of src
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "LDW.D SP PRE INC") == s) {
-	C67_g((C67_map_regn(a) << 23) |	//dst
-	      (15 << 18) |	//base reg B15
-	      (2 << 13) |	//ucst5 (must keep 8 byte boundary)
-	      (9 << 9) |	//mode 9 = pre inc ucst5
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (1 << 7) |	//y D1/D2  B side
-	      (6 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(a) << 1) |	//side of dst
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "LDDW.D SP PRE INC") == s) {
-	C67_g((C67_map_regn(a) << 23) |	//dst
-	      (15 << 18) |	//base reg B15
-	      (1 << 13) |	//ucst5 (must keep 8 byte boundary)
-	      (9 << 9) |	//mode 9 = pre inc ucst5
-	      (1 << 8) |	//r (LDDW bit 1)
-	      (1 << 7) |	//y D1/D2  B side
-	      (6 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(a) << 1) |	//side of dst
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "LDW.D *+SP[A0]") == s) {
-	C67_g((C67_map_regn(a) << 23) |	//dst
-	      (15 << 18) |	//base reg A15
-	      (0 << 13) |	//offset reg A0
-	      (5 << 9) |	//mode 5 = pos offset, base reg + off reg
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (0 << 7) |	//y D1/D2  A side
-	      (6 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(a) << 1) |	//side of dst
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "LDDW.D *+SP[A0]") == s) {
-	C67_g((C67_map_regn(a) << 23) |	//dst
-	      (15 << 18) |	//base reg A15
-	      (0 << 13) |	//offset reg A0
-	      (5 << 9) |	//mode 5 = pos offset, base reg + off reg
-	      (1 << 8) |	//r (LDDW bit 1)
-	      (0 << 7) |	//y D1/D2  A side
-	      (6 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(a) << 1) |	//side of dst
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "LDH.D *+SP[A0]") == s) {
-	C67_g((C67_map_regn(a) << 23) |	//dst
-	      (15 << 18) |	//base reg A15
-	      (0 << 13) |	//offset reg A0
-	      (5 << 9) |	//mode 5 = pos offset, base reg + off reg
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (0 << 7) |	//y D1/D2  A side
-	      (4 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(a) << 1) |	//side of dst
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "LDB.D *+SP[A0]") == s) {
-	C67_g((C67_map_regn(a) << 23) |	//dst
-	      (15 << 18) |	//base reg A15
-	      (0 << 13) |	//offset reg A0
-	      (5 << 9) |	//mode 5 = pos offset, base reg + off reg
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (0 << 7) |	//y D1/D2  A side
-	      (2 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(a) << 1) |	//side of dst
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "LDHU.D *+SP[A0]") == s) {
-	C67_g((C67_map_regn(a) << 23) |	//dst
-	      (15 << 18) |	//base reg A15
-	      (0 << 13) |	//offset reg A0
-	      (5 << 9) |	//mode 5 = pos offset, base reg + off reg
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (0 << 7) |	//y D1/D2  A side
-	      (0 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(a) << 1) |	//side of dst
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "LDBU.D *+SP[A0]") == s) {
-	C67_g((C67_map_regn(a) << 23) |	//dst
-	      (15 << 18) |	//base reg A15
-	      (0 << 13) |	//offset reg A0
-	      (5 << 9) |	//mode 5 = pos offset, base reg + off reg
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (0 << 7) |	//y D1/D2  A side
-	      (1 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(a) << 1) |	//side of dst
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "LDW.D *") == s) {
-	C67_g((C67_map_regn(b) << 23) |	//dst
-	      (C67_map_regn(a) << 18) |	//base reg A15
-	      (0 << 13) |	//cst5
-	      (1 << 9) |	//mode 1 = pos cst offset
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (C67_map_regs(a) << 7) |	//y D1/D2  src side
-	      (6 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(b) << 1) |	//side of dst
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "LDDW.D *") == s) {
-	C67_g((C67_map_regn(b) << 23) |	//dst
-	      (C67_map_regn(a) << 18) |	//base reg A15
-	      (0 << 13) |	//cst5
-	      (1 << 9) |	//mode 1 = pos cst offset
-	      (1 << 8) |	//r (LDDW bit 1)
-	      (C67_map_regs(a) << 7) |	//y D1/D2  src side
-	      (6 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(b) << 1) |	//side of dst
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "LDH.D *") == s) {
-	C67_g((C67_map_regn(b) << 23) |	//dst
-	      (C67_map_regn(a) << 18) |	//base reg A15
-	      (0 << 13) |	//cst5
-	      (1 << 9) |	//mode 1 = pos cst offset
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (C67_map_regs(a) << 7) |	//y D1/D2  src side
-	      (4 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(b) << 1) |	//side of dst
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "LDB.D *") == s) {
-	C67_g((C67_map_regn(b) << 23) |	//dst
-	      (C67_map_regn(a) << 18) |	//base reg A15
-	      (0 << 13) |	//cst5
-	      (1 << 9) |	//mode 1 = pos cst offset
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (C67_map_regs(a) << 7) |	//y D1/D2  src side
-	      (2 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(b) << 1) |	//side of dst
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "LDHU.D *") == s) {
-	C67_g((C67_map_regn(b) << 23) |	//dst
-	      (C67_map_regn(a) << 18) |	//base reg A15
-	      (0 << 13) |	//cst5
-	      (1 << 9) |	//mode 1 = pos cst offset
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (C67_map_regs(a) << 7) |	//y D1/D2  src side
-	      (0 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(b) << 1) |	//side of dst
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "LDBU.D *") == s) {
-	C67_g((C67_map_regn(b) << 23) |	//dst
-	      (C67_map_regn(a) << 18) |	//base reg A15
-	      (0 << 13) |	//cst5
-	      (1 << 9) |	//mode 1 = pos cst offset
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (C67_map_regs(a) << 7) |	//y D1/D2  src side
-	      (1 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(b) << 1) |	//side of dst
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "LDW.D +*") == s) {
-	C67_g((C67_map_regn(b) << 23) |	//dst
-	      (C67_map_regn(a) << 18) |	//base reg A15
-	      (1 << 13) |	//cst5
-	      (1 << 9) |	//mode 1 = pos cst offset
-	      (0 << 8) |	//r (LDDW bit 0)
-	      (C67_map_regs(a) << 7) |	//y D1/D2  src side
-	      (6 << 4) |	//ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU 
-	      (1 << 2) |	//opcode
-	      (C67_map_regs(b) << 1) |	//side of dst
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "CMPLTSP") == s) {
-	xpath = C67_map_regs(a) ^ C67_map_regs(b);
-	ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
-
-	C67_g((C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2
-	      (C67_map_regn(a) << 13) |	//src1
-	      (xpath << 12) |	//x use cross path for src2
-	      (0x3a << 6) |	//opcode
-	      (0x8 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side for reg c
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "CMPGTSP") == s) {
-	xpath = C67_map_regs(a) ^ C67_map_regs(b);
-	ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
-
-	C67_g((C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2
-	      (C67_map_regn(a) << 13) |	//src1
-	      (xpath << 12) |	//x use cross path for src2
-	      (0x39 << 6) |	//opcode
-	      (0x8 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side for reg c
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "CMPEQSP") == s) {
-	xpath = C67_map_regs(a) ^ C67_map_regs(b);
-	ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
-
-	C67_g((C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2
-	      (C67_map_regn(a) << 13) |	//src1
-	      (xpath << 12) |	//x use cross path for src2
-	      (0x38 << 6) |	//opcode
-	      (0x8 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side for reg c
-	      (0 << 0));	//parallel
-    }
-
-    else if (strstr(s, "CMPLTDP") == s) {
-	xpath = C67_map_regs(a) ^ C67_map_regs(b);
-	ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
-
-	C67_g((C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2
-	      (C67_map_regn(a) << 13) |	//src1
-	      (xpath << 12) |	//x use cross path for src2
-	      (0x2a << 6) |	//opcode
-	      (0x8 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side for reg c
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "CMPGTDP") == s) {
-	xpath = C67_map_regs(a) ^ C67_map_regs(b);
-	ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
-
-	C67_g((C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2
-	      (C67_map_regn(a) << 13) |	//src1
-	      (xpath << 12) |	//x use cross path for src2
-	      (0x29 << 6) |	//opcode
-	      (0x8 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side for reg c
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "CMPEQDP") == s) {
-	xpath = C67_map_regs(a) ^ C67_map_regs(b);
-	ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
-
-	C67_g((C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2
-	      (C67_map_regn(a) << 13) |	//src1
-	      (xpath << 12) |	//x use cross path for src2
-	      (0x28 << 6) |	//opcode
-	      (0x8 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side for reg c
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "CMPLT") == s) {
-	xpath = C67_map_regs(a) ^ C67_map_regs(b);
-	ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
-
-	C67_g((C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2
-	      (C67_map_regn(a) << 13) |	//src1
-	      (xpath << 12) |	//x use cross path for src2
-	      (0x57 << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side for reg c
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "CMPGT") == s) {
-	xpath = C67_map_regs(a) ^ C67_map_regs(b);
-	ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
-
-	C67_g((C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2
-	      (C67_map_regn(a) << 13) |	//src1
-	      (xpath << 12) |	//x use cross path for src2
-	      (0x47 << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side for reg c
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "CMPEQ") == s) {
-	xpath = C67_map_regs(a) ^ C67_map_regs(b);
-	ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
-
-	C67_g((C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2
-	      (C67_map_regn(a) << 13) |	//src1
-	      (xpath << 12) |	//x use cross path for src2
-	      (0x53 << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side for reg c
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "CMPLTU") == s) {
-	xpath = C67_map_regs(a) ^ C67_map_regs(b);
-	ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
-
-	C67_g((C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2
-	      (C67_map_regn(a) << 13) |	//src1
-	      (xpath << 12) |	//x use cross path for src2
-	      (0x5f << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side for reg c
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "CMPGTU") == s) {
-	xpath = C67_map_regs(a) ^ C67_map_regs(b);
-	ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
-
-	C67_g((C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2
-	      (C67_map_regn(a) << 13) |	//src1
-	      (xpath << 12) |	//x use cross path for src2
-	      (0x4f << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side for reg c
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "B DISP") == s) {
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//z
-	      (a << 7) |	//cnst
-	      (0x4 << 2) |	//opcode fixed
-	      (0 << 1) |	//S0/S1
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "B.") == s) {
-	xpath = C67_map_regs(c) ^ 1;
-
-	C67_g((C67_map_regc(b) << 29) |	//creg
-	      (a << 28) |	//inv
-	      (0 << 23) |	//dst
-	      (C67_map_regn(c) << 18) |	//src2
-	      (0 << 13) |	//
-	      (xpath << 12) |	//x cross path if !B side
-	      (0xd << 6) |	//opcode
-	      (0x8 << 2) |	//opcode fixed
-	      (1 << 1) |	//must be S2
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "MV.L") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2
-	      (0 << 13) |	//src1 (cst5)
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x2 << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "SPTRUNC.L") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2
-	      (0 << 13) |	//src1 NA
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0xb << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "DPTRUNC.L") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      ((C67_map_regn(b) + 1) << 18) |	//src2   WEIRD CPU must specify odd reg for some reason
-	      (0 << 13) |	//src1 NA
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x1 << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "INTSP.L") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2   
-	      (0 << 13) |	//src1 NA
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x4a << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "INTSPU.L") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2  
-	      (0 << 13) |	//src1 NA
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x49 << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "INTDP.L") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2  
-	      (0 << 13) |	//src1 NA
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x39 << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "INTDPU.L") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      ((C67_map_regn(b) + 1) << 18) |	//src2   WEIRD CPU must specify odd reg for some reason
-	      (0 << 13) |	//src1 NA
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x3b << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "SPDP.L") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2
-	      (0 << 13) |	//src1 NA
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x2 << 6) |	//opcode
-	      (0x8 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "DPSP.L") == s) {
-	ALWAYS_ASSERT(C67_map_regs(b) == C67_map_regs(c));
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      ((C67_map_regn(b) + 1) << 18) |	//src2 WEIRD CPU must specify odd reg for some reason
-	      (0 << 13) |	//src1 NA
-	      (0 << 12) |	//x cross path if opposite sides
-	      (0x9 << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "ADD.L") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2 (possible x path)
-	      (C67_map_regn(a) << 13) |	//src1 
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x3 << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "SUB.L") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2 (possible x path)
-	      (C67_map_regn(a) << 13) |	//src1 
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x7 << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "OR.L") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2 (possible x path)
-	      (C67_map_regn(a) << 13) |	//src1 
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x7f << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "AND.L") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2 (possible x path)
-	      (C67_map_regn(a) << 13) |	//src1 
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x7b << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "XOR.L") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2 (possible x path)
-	      (C67_map_regn(a) << 13) |	//src1 
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x6f << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "ADDSP.L") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2 (possible x path)
-	      (C67_map_regn(a) << 13) |	//src1 
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x10 << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "ADDDP.L") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2 (possible x path)
-	      (C67_map_regn(a) << 13) |	//src1 
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x18 << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "SUBSP.L") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2 (possible x path)
-	      (C67_map_regn(a) << 13) |	//src1 
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x11 << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "SUBDP.L") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2 (possible x path)
-	      (C67_map_regn(a) << 13) |	//src1 
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x19 << 5) |	//opcode
-	      (0x6 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "MPYSP.M") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2 (possible x path)
-	      (C67_map_regn(a) << 13) |	//src1 
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x1c << 7) |	//opcode
-	      (0x0 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "MPYDP.M") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2 (possible x path)
-	      (C67_map_regn(a) << 13) |	//src1 
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x0e << 7) |	//opcode
-	      (0x0 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "MPYI.M") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2
-	      (C67_map_regn(a) << 13) |	//src1 (cst5)
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x4 << 7) |	//opcode
-	      (0x0 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "SHR.S") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2
-	      (C67_map_regn(a) << 13) |	//src1 
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x37 << 6) |	//opcode
-	      (0x8 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "SHRU.S") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2
-	      (C67_map_regn(a) << 13) |	//src1 
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x27 << 6) |	//opcode
-	      (0x8 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "SHL.S") == s) {
-	xpath = C67_map_regs(b) ^ C67_map_regs(c);
-
-	ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(c) << 23) |	//dst
-	      (C67_map_regn(b) << 18) |	//src2
-	      (C67_map_regn(a) << 13) |	//src1 
-	      (xpath << 12) |	//x cross path if opposite sides
-	      (0x33 << 6) |	//opcode
-	      (0x8 << 2) |	//opcode fixed
-	      (C67_map_regs(c) << 1) |	//side of dest
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "||ADDK") == s) {
-	xpath = 0;		// no xpath required just use the side of the src/dst
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(b) << 23) |	//dst
-	      (a << 07) |	//scst16
-	      (0x14 << 2) |	//opcode fixed
-	      (C67_map_regs(b) << 1) |	//side of dst
-	      (1 << 0));	//parallel
-    } else if (strstr(s, "ADDK") == s) {
-	xpath = 0;		// no xpath required just use the side of the src/dst
-
-	C67_g((0 << 29) |	//creg
-	      (0 << 28) |	//inv
-	      (C67_map_regn(b) << 23) |	//dst
-	      (a << 07) |	//scst16
-	      (0x14 << 2) |	//opcode fixed
-	      (C67_map_regs(b) << 1) |	//side of dst
-	      (0 << 0));	//parallel
-    } else if (strstr(s, "NOP") == s) {
-	C67_g(((a - 1) << 13) |	//no of cycles
-	      (0 << 0));	//parallel
-    } else
-	ALWAYS_ASSERT(FALSE);
-
-#ifdef ASSEMBLY_LISTING_C67
-    fprintf(f, " %s %d %d %d\n", s, a, b, c);
-#endif
-
-}
-
-//r=reg to load, fr=from reg, symbol for relocation, constant
-
-void C67_MVKL(int r, int fc)
-{
-    C67_asm("MVKL.", fc, r, 0);
-}
-
-void C67_MVKH(int r, int fc)
-{
-    C67_asm("MVKH.", fc, r, 0);
-}
-
-void C67_STB_SP_A0(int r)
-{
-    C67_asm("STB.D *+SP[A0]", r, 0, 0);	// STB  r,*+SP[A0]
-}
-
-void C67_STH_SP_A0(int r)
-{
-    C67_asm("STH.D *+SP[A0]", r, 0, 0);	// STH  r,*+SP[A0]
-}
-
-void C67_STW_SP_A0(int r)
-{
-    C67_asm("STW.D *+SP[A0]", r, 0, 0);	// STW  r,*+SP[A0]
-}
-
-void C67_STB_PTR(int r, int r2)
-{
-    C67_asm("STB.D *", r, r2, 0);	// STB  r, *r2
-}
-
-void C67_STH_PTR(int r, int r2)
-{
-    C67_asm("STH.D *", r, r2, 0);	// STH  r, *r2
-}
-
-void C67_STW_PTR(int r, int r2)
-{
-    C67_asm("STW.D *", r, r2, 0);	// STW  r, *r2
-}
-
-void C67_STW_PTR_PRE_INC(int r, int r2, int n)
-{
-    C67_asm("STW.D +*", r, r2, n);	// STW  r, *+r2
-}
-
-void C67_PUSH(int r)
-{
-    C67_asm("STW.D SP POST DEC", r, 0, 0);	// STW  r,*SP--
-}
-
-void C67_LDW_SP_A0(int r)
-{
-    C67_asm("LDW.D *+SP[A0]", r, 0, 0);	// LDW  *+SP[A0],r
-}
-
-void C67_LDDW_SP_A0(int r)
-{
-    C67_asm("LDDW.D *+SP[A0]", r, 0, 0);	// LDDW  *+SP[A0],r
-}
-
-void C67_LDH_SP_A0(int r)
-{
-    C67_asm("LDH.D *+SP[A0]", r, 0, 0);	// LDH  *+SP[A0],r
-}
-
-void C67_LDB_SP_A0(int r)
-{
-    C67_asm("LDB.D *+SP[A0]", r, 0, 0);	// LDB  *+SP[A0],r
-}
-
-void C67_LDHU_SP_A0(int r)
-{
-    C67_asm("LDHU.D *+SP[A0]", r, 0, 0);	// LDHU  *+SP[A0],r
-}
-
-void C67_LDBU_SP_A0(int r)
-{
-    C67_asm("LDBU.D *+SP[A0]", r, 0, 0);	// LDBU  *+SP[A0],r
-}
-
-void C67_LDW_PTR(int r, int r2)
-{
-    C67_asm("LDW.D *", r, r2, 0);	// LDW  *r,r2
-}
-
-void C67_LDDW_PTR(int r, int r2)
-{
-    C67_asm("LDDW.D *", r, r2, 0);	// LDDW  *r,r2
-}
-
-void C67_LDH_PTR(int r, int r2)
-{
-    C67_asm("LDH.D *", r, r2, 0);	// LDH  *r,r2
-}
-
-void C67_LDB_PTR(int r, int r2)
-{
-    C67_asm("LDB.D *", r, r2, 0);	// LDB  *r,r2
-}
-
-void C67_LDHU_PTR(int r, int r2)
-{
-    C67_asm("LDHU.D *", r, r2, 0);	// LDHU  *r,r2
-}
-
-void C67_LDBU_PTR(int r, int r2)
-{
-    C67_asm("LDBU.D *", r, r2, 0);	// LDBU  *r,r2
-}
-
-void C67_LDW_PTR_PRE_INC(int r, int r2)
-{
-    C67_asm("LDW.D +*", r, r2, 0);	// LDW  *+r,r2
-}
-
-void C67_POP(int r)
-{
-    C67_asm("LDW.D SP PRE INC", r, 0, 0);	// LDW  *++SP,r
-}
-
-void C67_POP_DW(int r)
-{
-    C67_asm("LDDW.D SP PRE INC", r, 0, 0);	// LDDW  *++SP,r
-}
-
-void C67_CMPLT(int s1, int s2, int dst)
-{
-    C67_asm("CMPLT.L1", s1, s2, dst);
-}
-
-void C67_CMPGT(int s1, int s2, int dst)
-{
-    C67_asm("CMPGT.L1", s1, s2, dst);
-}
-
-void C67_CMPEQ(int s1, int s2, int dst)
-{
-    C67_asm("CMPEQ.L1", s1, s2, dst);
-}
-
-void C67_CMPLTU(int s1, int s2, int dst)
-{
-    C67_asm("CMPLTU.L1", s1, s2, dst);
-}
-
-void C67_CMPGTU(int s1, int s2, int dst)
-{
-    C67_asm("CMPGTU.L1", s1, s2, dst);
-}
-
-
-void C67_CMPLTSP(int s1, int s2, int dst)
-{
-    C67_asm("CMPLTSP.S1", s1, s2, dst);
-}
-
-void C67_CMPGTSP(int s1, int s2, int dst)
-{
-    C67_asm("CMPGTSP.S1", s1, s2, dst);
-}
-
-void C67_CMPEQSP(int s1, int s2, int dst)
-{
-    C67_asm("CMPEQSP.S1", s1, s2, dst);
-}
-
-void C67_CMPLTDP(int s1, int s2, int dst)
-{
-    C67_asm("CMPLTDP.S1", s1, s2, dst);
-}
-
-void C67_CMPGTDP(int s1, int s2, int dst)
-{
-    C67_asm("CMPGTDP.S1", s1, s2, dst);
-}
-
-void C67_CMPEQDP(int s1, int s2, int dst)
-{
-    C67_asm("CMPEQDP.S1", s1, s2, dst);
-}
-
-
-void C67_IREG_B_REG(int inv, int r1, int r2)	// [!R] B  r2
-{
-    C67_asm("B.S2", inv, r1, r2);
-}
-
-
-// call with how many 32 bit words to skip
-// (0 would branch to the branch instruction)
-
-void C67_B_DISP(int disp)	//  B  +2  Branch with constant displacement
-{
-    // Branch point is relative to the 8 word fetch packet
-    //
-    // we will assume the text section always starts on an 8 word (32 byte boundary)
-    //
-    // so add in how many words into the fetch packet the branch is
-
-
-    C67_asm("B DISP", disp + ((ind & 31) >> 2), 0, 0);
-}
-
-void C67_NOP(int n)
-{
-    C67_asm("NOP", n, 0, 0);
-}
-
-void C67_ADDK(int n, int r)
-{
-    ALWAYS_ASSERT(abs(n) < 32767);
-
-    C67_asm("ADDK", n, r, 0);
-}
-
-void C67_ADDK_PARALLEL(int n, int r)
-{
-    ALWAYS_ASSERT(abs(n) < 32767);
-
-    C67_asm("||ADDK", n, r, 0);
-}
-
-void C67_Adjust_ADDK(int *inst, int n)
-{
-    ALWAYS_ASSERT(abs(n) < 32767);
-
-    *inst = (*inst & (~(0xffff << 7))) | ((n & 0xffff) << 7);
-}
-
-void C67_MV(int r, int v)
-{
-    C67_asm("MV.L", 0, r, v);
-}
-
-
-void C67_DPTRUNC(int r, int v)
-{
-    C67_asm("DPTRUNC.L", 0, r, v);
-}
-
-void C67_SPTRUNC(int r, int v)
-{
-    C67_asm("SPTRUNC.L", 0, r, v);
-}
-
-void C67_INTSP(int r, int v)
-{
-    C67_asm("INTSP.L", 0, r, v);
-}
-
-void C67_INTDP(int r, int v)
-{
-    C67_asm("INTDP.L", 0, r, v);
-}
-
-void C67_INTSPU(int r, int v)
-{
-    C67_asm("INTSPU.L", 0, r, v);
-}
-
-void C67_INTDPU(int r, int v)
-{
-    C67_asm("INTDPU.L", 0, r, v);
-}
-
-void C67_SPDP(int r, int v)
-{
-    C67_asm("SPDP.L", 0, r, v);
-}
-
-void C67_DPSP(int r, int v)	// note regs must be on the same side
-{
-    C67_asm("DPSP.L", 0, r, v);
-}
-
-void C67_ADD(int r, int v)
-{
-    C67_asm("ADD.L", v, r, v);
-}
-
-void C67_SUB(int r, int v)
-{
-    C67_asm("SUB.L", v, r, v);
-}
-
-void C67_AND(int r, int v)
-{
-    C67_asm("AND.L", v, r, v);
-}
-
-void C67_OR(int r, int v)
-{
-    C67_asm("OR.L", v, r, v);
-}
-
-void C67_XOR(int r, int v)
-{
-    C67_asm("XOR.L", v, r, v);
-}
-
-void C67_ADDSP(int r, int v)
-{
-    C67_asm("ADDSP.L", v, r, v);
-}
-
-void C67_SUBSP(int r, int v)
-{
-    C67_asm("SUBSP.L", v, r, v);
-}
-
-void C67_MPYSP(int r, int v)
-{
-    C67_asm("MPYSP.M", v, r, v);
-}
-
-void C67_ADDDP(int r, int v)
-{
-    C67_asm("ADDDP.L", v, r, v);
-}
-
-void C67_SUBDP(int r, int v)
-{
-    C67_asm("SUBDP.L", v, r, v);
-}
-
-void C67_MPYDP(int r, int v)
-{
-    C67_asm("MPYDP.M", v, r, v);
-}
-
-void C67_MPYI(int r, int v)
-{
-    C67_asm("MPYI.M", v, r, v);
-}
-
-void C67_SHL(int r, int v)
-{
-    C67_asm("SHL.S", r, v, v);
-}
-
-void C67_SHRU(int r, int v)
-{
-    C67_asm("SHRU.S", r, v, v);
-}
-
-void C67_SHR(int r, int v)
-{
-    C67_asm("SHR.S", r, v, v);
-}
-
-
-
-/* load 'r' from value 'sv' */
-void load(int r, SValue * sv)
-{
-    int v, t, ft, fc, fr, size = 0, element;
-    BOOL Unsigned = FALSE;
-    SValue v1;
-
-    fr = sv->r;
-    ft = sv->type.t;
-    fc = sv->c.i;
-
-    v = fr & VT_VALMASK;
-    if (fr & VT_LVAL) {
-	if (v == VT_LLOCAL) {
-	    v1.type.t = VT_INT;
-	    v1.r = VT_LOCAL | VT_LVAL;
-	    v1.c.i = fc;
-	    load(r, &v1);
-	    fr = r;
-	} else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
-	    tcc_error("long double not supported");
-	} else if ((ft & VT_TYPE) == VT_BYTE) {
-	    size = 1;
-	} else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) {
-	    size = 1;
-	    Unsigned = TRUE;
-	} else if ((ft & VT_TYPE) == VT_SHORT) {
-	    size = 2;
-	} else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) {
-	    size = 2;
-	    Unsigned = TRUE;
-	} else if ((ft & VT_BTYPE) == VT_DOUBLE) {
-	    size = 8;
-	} else {
-	    size = 4;
-	}
-
-	// check if fc is a positive reference on the stack, 
-	// if it is tcc is referencing what it thinks is a parameter
-	// on the stack, so check if it is really in a register.
-
-
-	if (v == VT_LOCAL && fc > 0) {
-	    int stack_pos = 8;
-
-	    for (t = 0; t < NoCallArgsPassedOnStack; t++) {
-		if (fc == stack_pos)
-		    break;
-
-		stack_pos += TranslateStackToReg[t];
-	    }
-
-	    // param has been pushed on stack, get it like a local var
-
-	    fc = ParamLocOnStack[t] - 8;
-	}
-
-	if ((fr & VT_VALMASK) < VT_CONST)	// check for pure indirect
-	{
-	    if (size == 1) {
-		if (Unsigned)
-		    C67_LDBU_PTR(v, r);	// LDBU  *v,r
-		else
-		    C67_LDB_PTR(v, r);	// LDB  *v,r
-	    } else if (size == 2) {
-		if (Unsigned)
-		    C67_LDHU_PTR(v, r);	// LDHU  *v,r
-		else
-		    C67_LDH_PTR(v, r);	// LDH  *v,r
-	    } else if (size == 4) {
-		C67_LDW_PTR(v, r);	// LDW  *v,r
-	    } else if (size == 8) {
-		C67_LDDW_PTR(v, r);	// LDDW  *v,r
-	    }
-
-	    C67_NOP(4);		// NOP 4
-	    return;
-	} else if (fr & VT_SYM) {
-	    greloc(cur_text_section, sv->sym, ind, R_C60LO16);	// rem the inst need to be patched
-	    greloc(cur_text_section, sv->sym, ind + 4, R_C60HI16);
-
-
-	    C67_MVKL(C67_A0, fc);	//r=reg to load,  constant
-	    C67_MVKH(C67_A0, fc);	//r=reg to load,  constant
-
-
-	    if (size == 1) {
-		if (Unsigned)
-		    C67_LDBU_PTR(C67_A0, r);	// LDBU  *A0,r
-		else
-		    C67_LDB_PTR(C67_A0, r);	// LDB  *A0,r
-	    } else if (size == 2) {
-		if (Unsigned)
-		    C67_LDHU_PTR(C67_A0, r);	// LDHU  *A0,r
-		else
-		    C67_LDH_PTR(C67_A0, r);	// LDH  *A0,r
-	    } else if (size == 4) {
-		C67_LDW_PTR(C67_A0, r);	// LDW  *A0,r
-	    } else if (size == 8) {
-		C67_LDDW_PTR(C67_A0, r);	// LDDW  *A0,r
-	    }
-
-	    C67_NOP(4);		// NOP 4
-	    return;
-	} else {
-	    element = size;
-
-	    // divide offset in bytes to create element index
-	    C67_MVKL(C67_A0, (fc / element) + 8 / element);	//r=reg to load,  constant
-	    C67_MVKH(C67_A0, (fc / element) + 8 / element);	//r=reg to load,  constant
-
-	    if (size == 1) {
-		if (Unsigned)
-		    C67_LDBU_SP_A0(r);	// LDBU  r, SP[A0]
-		else
-		    C67_LDB_SP_A0(r);	// LDB  r, SP[A0]
-	    } else if (size == 2) {
-		if (Unsigned)
-		    C67_LDHU_SP_A0(r);	// LDHU  r, SP[A0]
-		else
-		    C67_LDH_SP_A0(r);	// LDH  r, SP[A0]
-	    } else if (size == 4) {
-		C67_LDW_SP_A0(r);	// LDW  r, SP[A0]
-	    } else if (size == 8) {
-		C67_LDDW_SP_A0(r);	// LDDW  r, SP[A0]
-	    }
-
-
-	    C67_NOP(4);		// NOP 4
-	    return;
-	}
-    } else {
-	if (v == VT_CONST) {
-	    if (fr & VT_SYM) {
-		greloc(cur_text_section, sv->sym, ind, R_C60LO16);	// rem the inst need to be patched
-		greloc(cur_text_section, sv->sym, ind + 4, R_C60HI16);
-	    }
-	    C67_MVKL(r, fc);	//r=reg to load, constant
-	    C67_MVKH(r, fc);	//r=reg to load, constant
-	} else if (v == VT_LOCAL) {
-	    C67_MVKL(r, fc + 8);	//r=reg to load, constant C67 stack points to next free
-	    C67_MVKH(r, fc + 8);	//r=reg to load, constant
-	    C67_ADD(C67_FP, r);	// MV v,r   v -> r
-	} else if (v == VT_CMP) {
-	    C67_MV(C67_compare_reg, r);	// MV v,r   v -> r
-	} else if (v == VT_JMP || v == VT_JMPI) {
-	    t = v & 1;
-	    C67_B_DISP(4);	//  Branch with constant displacement, skip over this branch, load, nop, load
-	    C67_MVKL(r, t);	//  r=reg to load, 0 or 1 (do this while branching)
-	    C67_NOP(4);		//  NOP 4
-	    gsym(fc);		//  modifies other branches to branch here
-	    C67_MVKL(r, t ^ 1);	//  r=reg to load, 0 or 1
-	} else if (v != r) {
-	    C67_MV(v, r);	// MV v,r   v -> r
-
-	    if ((ft & VT_BTYPE) == VT_DOUBLE)
-		C67_MV(v + 1, r + 1);	// MV v,r   v -> r
-	}
-    }
-}
-
-
-/* store register 'r' in lvalue 'v' */
-void store(int r, SValue * v)
-{
-    int fr, bt, ft, fc, size, t, element;
-
-    ft = v->type.t;
-    fc = v->c.i;
-    fr = v->r & VT_VALMASK;
-    bt = ft & VT_BTYPE;
-    /* XXX: incorrect if float reg to reg */
-
-    if (bt == VT_LDOUBLE) {
-	tcc_error("long double not supported");
-    } else {
-	if (bt == VT_SHORT)
-	    size = 2;
-	else if (bt == VT_BYTE)
-	    size = 1;
-	else if (bt == VT_DOUBLE)
-	    size = 8;
-	else
-	    size = 4;
-
-	if ((v->r & VT_VALMASK) == VT_CONST) {
-	    /* constant memory reference */
-
-	    if (v->r & VT_SYM) {
-		greloc(cur_text_section, v->sym, ind, R_C60LO16);	// rem the inst need to be patched
-		greloc(cur_text_section, v->sym, ind + 4, R_C60HI16);
-	    }
-	    C67_MVKL(C67_A0, fc);	//r=reg to load,  constant
-	    C67_MVKH(C67_A0, fc);	//r=reg to load,  constant
-
-	    if (size == 1)
-		C67_STB_PTR(r, C67_A0);	// STB  r, *A0
-	    else if (size == 2)
-		C67_STH_PTR(r, C67_A0);	// STH  r, *A0
-	    else if (size == 4 || size == 8)
-		C67_STW_PTR(r, C67_A0);	// STW  r, *A0
-
-	    if (size == 8)
-		C67_STW_PTR_PRE_INC(r + 1, C67_A0, 1);	// STW  r, *+A0[1]
-	} else if ((v->r & VT_VALMASK) == VT_LOCAL) {
-	    // check case of storing to passed argument that
-	    // tcc thinks is on the stack but for C67 is
-	    // passed as a reg.  However it may have been
-	    // saved to the stack, if that reg was required
-	    // for a call to a child function
-
-	    if (fc > 0)		// argument ??
-	    {
-		// walk through sizes and figure which param
-
-		int stack_pos = 8;
-
-		for (t = 0; t < NoCallArgsPassedOnStack; t++) {
-		    if (fc == stack_pos)
-			break;
-
-		    stack_pos += TranslateStackToReg[t];
-		}
-
-		// param has been pushed on stack, get it like a local var
-		fc = ParamLocOnStack[t] - 8;
-	    }
-
-	    if (size == 8)
-		element = 4;
-	    else
-		element = size;
-
-	    // divide offset in bytes to create word index
-	    C67_MVKL(C67_A0, (fc / element) + 8 / element);	//r=reg to load,  constant
-	    C67_MVKH(C67_A0, (fc / element) + 8 / element);	//r=reg to load,  constant
-
-
-
-	    if (size == 1)
-		C67_STB_SP_A0(r);	// STB  r, SP[A0]
-	    else if (size == 2)
-		C67_STH_SP_A0(r);	// STH  r, SP[A0]
-	    else if (size == 4 || size == 8)
-		C67_STW_SP_A0(r);	// STW  r, SP[A0]
-
-	    if (size == 8) {
-		C67_ADDK(1, C67_A0);	//  ADDK 1,A0
-		C67_STW_SP_A0(r + 1);	//  STW  r, SP[A0]
-	    }
-	} else {
-	    if (size == 1)
-		C67_STB_PTR(r, fr);	// STB  r, *fr
-	    else if (size == 2)
-		C67_STH_PTR(r, fr);	// STH  r, *fr
-	    else if (size == 4 || size == 8)
-		C67_STW_PTR(r, fr);	// STW  r, *fr
-
-	    if (size == 8) {
-		C67_STW_PTR_PRE_INC(r + 1, fr, 1);	// STW  r, *+fr[1]
-	    }
-	}
-    }
-}
-
-/* 'is_jmp' is '1' if it is a jump */
-static void gcall_or_jmp(int is_jmp)
-{
-    int r;
-    Sym *sym;
-
-    if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
-	/* constant case */
-	if (vtop->r & VT_SYM) {
-	    /* relocation case */
-
-	    // get add into A0, then start the jump B3
-
-	    greloc(cur_text_section, vtop->sym, ind, R_C60LO16);	// rem the inst need to be patched
-	    greloc(cur_text_section, vtop->sym, ind + 4, R_C60HI16);
-
-	    C67_MVKL(C67_A0, 0);	//r=reg to load, constant
-	    C67_MVKH(C67_A0, 0);	//r=reg to load, constant
-	    C67_IREG_B_REG(0, C67_CREG_ZERO, C67_A0);	//  B.S2x  A0
-
-	    if (is_jmp) {
-		C67_NOP(5);	// simple jump, just put NOP
-	    } else {
-		// Call, must load return address into B3 during delay slots
-
-		sym = get_sym_ref(&char_pointer_type, cur_text_section, ind + 12, 0);	// symbol for return address
-		greloc(cur_text_section, sym, ind, R_C60LO16);	// rem the inst need to be patched
-		greloc(cur_text_section, sym, ind + 4, R_C60HI16);
-		C67_MVKL(C67_B3, 0);	//r=reg to load, constant
-		C67_MVKH(C67_B3, 0);	//r=reg to load, constant
-		C67_NOP(3);	// put remaining NOPs
-	    }
-	} else {
-	    /* put an empty PC32 relocation */
-	    ALWAYS_ASSERT(FALSE);
-	}
-    } else {
-	/* otherwise, indirect call */
-	r = gv(RC_INT);
-	C67_IREG_B_REG(0, C67_CREG_ZERO, r);	//  B.S2x  r
-
-	if (is_jmp) {
-	    C67_NOP(5);		// simple jump, just put NOP
-	} else {
-	    // Call, must load return address into B3 during delay slots
-
-	    sym = get_sym_ref(&char_pointer_type, cur_text_section, ind + 12, 0);	// symbol for return address
-	    greloc(cur_text_section, sym, ind, R_C60LO16);	// rem the inst need to be patched
-	    greloc(cur_text_section, sym, ind + 4, R_C60HI16);
-	    C67_MVKL(C67_B3, 0);	//r=reg to load, constant
-	    C67_MVKH(C67_B3, 0);	//r=reg to load, constant
-	    C67_NOP(3);		// put remaining NOPs
-	}
-    }
-}
-
-/* Return the number of registers needed to return the struct, or 0 if
-   returning via struct pointer. */
-ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) {
-    *ret_align = 1; // Never have to re-align return values for x86-64
-    return 0;
-}
-
-/* generate function call with address in (vtop->t, vtop->c) and free function
-   context. Stack entry is popped */
-void gfunc_call(int nb_args)
-{
-    int i, r, size = 0;
-    int args_sizes[NoCallArgsPassedOnStack];
-
-    if (nb_args > NoCallArgsPassedOnStack) {
-	tcc_error("more than 10 function params not currently supported");
-	// handle more than 10, put some on the stack
-    }
-
-    for (i = 0; i < nb_args; i++) {
-	if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
-	    ALWAYS_ASSERT(FALSE);
-	} else {
-	    /* simple type (currently always same size) */
-	    /* XXX: implicit cast ? */
-
-
-	    if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
-		tcc_error("long long not supported");
-	    } else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
-		tcc_error("long double not supported");
-	    } else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) {
-		size = 8;
-	    } else {
-		size = 4;
-	    }
-
-	    // put the parameter into the corresponding reg (pair)
-
-	    r = gv(RC_C67_A4 << (2 * i));
-
-	    // must put on stack because with 1 pass compiler , no way to tell
-	    // if an up coming nested call might overwrite these regs
-
-	    C67_PUSH(r);
-
-	    if (size == 8) {
-		C67_STW_PTR_PRE_INC(r + 1, C67_SP, 3);	// STW  r, *+SP[3] (go back and put the other)
-	    }
-	    args_sizes[i] = size;
-	}
-	vtop--;
-    }
-    // POP all the params on the stack into registers for the
-    // immediate call (in reverse order)
-
-    for (i = nb_args - 1; i >= 0; i--) {
-
-	if (args_sizes[i] == 8)
-	    C67_POP_DW(TREG_C67_A4 + i * 2);
-	else
-	    C67_POP(TREG_C67_A4 + i * 2);
-    }
-    gcall_or_jmp(0);
-    vtop--;
-}
-
-
-// to be compatible with Code Composer for the C67
-// the first 10 parameters must be passed in registers
-// (pairs for 64 bits) starting wit; A4:A5, then B4:B5 and
-// ending with B12:B13.
-//
-// When a call is made, if the caller has its parameters
-// in regs A4-B13 these must be saved before/as the call 
-// parameters are loaded and restored upon return (or if/when needed).
-
-/* generate function prolog of type 't' */
-void gfunc_prolog(CType * func_type)
-{
-    int addr, align, size, func_call, i;
-    Sym *sym;
-    CType *type;
-
-    sym = func_type->ref;
-    func_call = sym->f.func_call;
-    addr = 8;
-    /* if the function returns a structure, then add an
-       implicit pointer parameter */
-    func_vt = sym->type;
-    func_var = (sym->f.func_type == FUNC_ELLIPSIS);
-    if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
-	func_vc = addr;
-	addr += 4;
-    }
-
-    NoOfCurFuncArgs = 0;
-
-    /* define parameters */
-    while ((sym = sym->next) != NULL) {
-	type = &sym->type;
-	sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | lvalue_type(type->t), addr);
-	size = type_size(type, &align);
-	size = (size + 3) & ~3;
-
-	// keep track of size of arguments so
-	// we can translate where tcc thinks they
-	// are on the stack into the appropriate reg
-
-	TranslateStackToReg[NoOfCurFuncArgs] = size;
-	NoOfCurFuncArgs++;
-
-#ifdef FUNC_STRUCT_PARAM_AS_PTR
-	/* structs are passed as pointer */
-	if ((type->t & VT_BTYPE) == VT_STRUCT) {
-	    size = 4;
-	}
-#endif
-	addr += size;
-    }
-    func_ret_sub = 0;
-    /* pascal type call ? */
-    if (func_call == FUNC_STDCALL)
-	func_ret_sub = addr - 8;
-
-    C67_MV(C67_FP, C67_A0);	//  move FP -> A0
-    C67_MV(C67_SP, C67_FP);	//  move SP -> FP
-
-    // place all the args passed in regs onto the stack
-
-    loc = 0;
-    for (i = 0; i < NoOfCurFuncArgs; i++) {
-
-	ParamLocOnStack[i] = loc;	// remember where the param is
-	loc += -8;
-
-	C67_PUSH(TREG_C67_A4 + i * 2);
-
-	if (TranslateStackToReg[i] == 8) {
-	    C67_STW_PTR_PRE_INC(TREG_C67_A4 + i * 2 + 1, C67_SP, 3);	// STW  r, *+SP[1] (go back and put the other)
-	}
-    }
-
-    TotalBytesPushedOnStack = -loc;
-
-    func_sub_sp_offset = ind;	// remember where we put the stack instruction 
-    C67_ADDK(0, C67_SP);	//  ADDK.L2 loc,SP  (just put zero temporarily)
-
-    C67_PUSH(C67_A0);
-    C67_PUSH(C67_B3);
-}
-
-/* generate function epilog */
-void gfunc_epilog(void)
-{
-    {
-	int local = (-loc + 7) & -8;	// stack must stay aligned to 8 bytes for LDDW instr
-	C67_POP(C67_B3);
-	C67_NOP(4);		// NOP wait for load
-	C67_IREG_B_REG(0, C67_CREG_ZERO, C67_B3);	//  B.S2  B3
-	C67_POP(C67_FP);
-	C67_ADDK(local, C67_SP);	//  ADDK.L2 loc,SP  
-	C67_Adjust_ADDK((int *) (cur_text_section->data +
-				 func_sub_sp_offset),
-			-local + TotalBytesPushedOnStack);
-	C67_NOP(3);		// NOP 
-    }
-}
-
-/* generate a jump to a label */
-int gjmp(int t)
-{
-    int ind1 = ind;
-    if (nocode_wanted)
-        return t;
-
-    C67_MVKL(C67_A0, t);	//r=reg to load,  constant
-    C67_MVKH(C67_A0, t);	//r=reg to load,  constant
-    C67_IREG_B_REG(0, C67_CREG_ZERO, C67_A0);	// [!R] B.S2x  A0
-    C67_NOP(5);
-    return ind1;
-}
-
-/* generate a jump to a fixed address */
-void gjmp_addr(int a)
-{
-    Sym *sym;
-    // I guess this routine is used for relative short
-    // local jumps, for now just handle it as the general
-    // case
-
-    // define a label that will be relocated
-
-    sym = get_sym_ref(&char_pointer_type, cur_text_section, a, 0);
-    greloc(cur_text_section, sym, ind, R_C60LO16);
-    greloc(cur_text_section, sym, ind + 4, R_C60HI16);
-
-    gjmp(0);			// place a zero there later the symbol will be added to it
-}
-
-/* generate a test. set 'inv' to invert test. Stack entry is popped */
-int gtst(int inv, int t)
-{
-    int ind1, n;
-    int v, *p;
-
-    v = vtop->r & VT_VALMASK;
-    if (nocode_wanted) {
-        ;
-    } else if (v == VT_CMP) {
-	/* fast case : can jump directly since flags are set */
-	// C67 uses B2 sort of as flags register
-	ind1 = ind;
-	C67_MVKL(C67_A0, t);	//r=reg to load, constant
-	C67_MVKH(C67_A0, t);	//r=reg to load, constant
-
-	if (C67_compare_reg != TREG_EAX &&	// check if not already in a conditional test reg
-	    C67_compare_reg != TREG_EDX &&
-	    C67_compare_reg != TREG_ST0 && C67_compare_reg != C67_B2) {
-	    C67_MV(C67_compare_reg, C67_B2);
-	    C67_compare_reg = C67_B2;
-	}
-
-	C67_IREG_B_REG(C67_invert_test ^ inv, C67_compare_reg, C67_A0);	// [!R] B.S2x  A0
-	C67_NOP(5);
-	t = ind1;		//return where we need to patch
-
-    } else if (v == VT_JMP || v == VT_JMPI) {
-	/* && or || optimization */
-	if ((v & 1) == inv) {
-	    /* insert vtop->c jump list in t */
-
-	    // I guess the idea is to traverse to the
-	    // null at the end of the list and store t
-	    // there
-
-	    n = vtop->c.i;
-	    while (n != 0) {
-		p = (int *) (cur_text_section->data + n);
-
-		// extract 32 bit address from MVKH/MVKL
-		n = ((*p >> 7) & 0xffff);
-		n |= ((*(p + 1) >> 7) & 0xffff) << 16;
-	    }
-	    *p |= (t & 0xffff) << 7;
-	    *(p + 1) |= ((t >> 16) & 0xffff) << 7;
-	    t = vtop->c.i;
-
-	} else {
-	    t = gjmp(t);
-	    gsym(vtop->c.i);
-	}
-    }
-    vtop--;
-    return t;
-}
-
-/* generate an integer binary operation */
-void gen_opi(int op)
-{
-    int r, fr, opc, t;
-
-    switch (op) {
-    case '+':
-    case TOK_ADDC1:		/* add with carry generation */
-	opc = 0;
-      gen_op8:
-
-
-// C67 can't do const compares, must load into a reg
-// so just go to gv2 directly - tktk
-
-
-
-	if (op >= TOK_ULT && op <= TOK_GT)
-	    gv2(RC_INT_BSIDE, RC_INT);	// make sure r (src1) is on the B Side of CPU
-	else
-	    gv2(RC_INT, RC_INT);
-
-	r = vtop[-1].r;
-	fr = vtop[0].r;
-
-	C67_compare_reg = C67_B2;
-
-
-	if (op == TOK_LT) {
-	    C67_CMPLT(r, fr, C67_B2);
-	    C67_invert_test = FALSE;
-	} else if (op == TOK_GE) {
-	    C67_CMPLT(r, fr, C67_B2);
-	    C67_invert_test = TRUE;
-	} else if (op == TOK_GT) {
-	    C67_CMPGT(r, fr, C67_B2);
-	    C67_invert_test = FALSE;
-	} else if (op == TOK_LE) {
-	    C67_CMPGT(r, fr, C67_B2);
-	    C67_invert_test = TRUE;
-	} else if (op == TOK_EQ) {
-	    C67_CMPEQ(r, fr, C67_B2);
-	    C67_invert_test = FALSE;
-	} else if (op == TOK_NE) {
-	    C67_CMPEQ(r, fr, C67_B2);
-	    C67_invert_test = TRUE;
-	} else if (op == TOK_ULT) {
-	    C67_CMPLTU(r, fr, C67_B2);
-	    C67_invert_test = FALSE;
-	} else if (op == TOK_UGE) {
-	    C67_CMPLTU(r, fr, C67_B2);
-	    C67_invert_test = TRUE;
-	} else if (op == TOK_UGT) {
-	    C67_CMPGTU(r, fr, C67_B2);
-	    C67_invert_test = FALSE;
-	} else if (op == TOK_ULE) {
-	    C67_CMPGTU(r, fr, C67_B2);
-	    C67_invert_test = TRUE;
-	} else if (op == '+')
-	    C67_ADD(fr, r);	// ADD  r,fr,r
-	else if (op == '-')
-	    C67_SUB(fr, r);	// SUB  r,fr,r
-	else if (op == '&')
-	    C67_AND(fr, r);	// AND  r,fr,r
-	else if (op == '|')
-	    C67_OR(fr, r);	// OR  r,fr,r
-	else if (op == '^')
-	    C67_XOR(fr, r);	// XOR  r,fr,r
-	else
-	    ALWAYS_ASSERT(FALSE);
-
-	vtop--;
-	if (op >= TOK_ULT && op <= TOK_GT) {
-	    vtop->r = VT_CMP;
-	    vtop->c.i = op;
-	}
-	break;
-    case '-':
-    case TOK_SUBC1:		/* sub with carry generation */
-	opc = 5;
-	goto gen_op8;
-    case TOK_ADDC2:		/* add with carry use */
-	opc = 2;
-	goto gen_op8;
-    case TOK_SUBC2:		/* sub with carry use */
-	opc = 3;
-	goto gen_op8;
-    case '&':
-	opc = 4;
-	goto gen_op8;
-    case '^':
-	opc = 6;
-	goto gen_op8;
-    case '|':
-	opc = 1;
-	goto gen_op8;
-    case '*':
-    case TOK_UMULL:
-	gv2(RC_INT, RC_INT);
-	r = vtop[-1].r;
-	fr = vtop[0].r;
-	vtop--;
-	C67_MPYI(fr, r);	// 32 bit multiply  fr,r,fr
-	C67_NOP(8);		// NOP 8 for worst case
-	break;
-    case TOK_SHL:
-	gv2(RC_INT_BSIDE, RC_INT_BSIDE);	// shift amount must be on same side as dst
-	r = vtop[-1].r;
-	fr = vtop[0].r;
-	vtop--;
-	C67_SHL(fr, r);		// arithmetic/logical shift
-	break;
-
-    case TOK_SHR:
-	gv2(RC_INT_BSIDE, RC_INT_BSIDE);	// shift amount must be on same side as dst
-	r = vtop[-1].r;
-	fr = vtop[0].r;
-	vtop--;
-	C67_SHRU(fr, r);	// logical shift
-	break;
-
-    case TOK_SAR:
-	gv2(RC_INT_BSIDE, RC_INT_BSIDE);	// shift amount must be on same side as dst
-	r = vtop[-1].r;
-	fr = vtop[0].r;
-	vtop--;
-	C67_SHR(fr, r);		// arithmetic shift
-	break;
-
-    case '/':
-	t = TOK__divi;
-      call_func:
-	vswap();
-	/* call generic idiv function */
-	vpush_global_sym(&func_old_type, t);
-	vrott(3);
-	gfunc_call(2);
-	vpushi(0);
-	vtop->r = REG_IRET;
-	vtop->r2 = VT_CONST;
-	break;
-    case TOK_UDIV:
-    case TOK_PDIV:
-	t = TOK__divu;
-	goto call_func;
-    case '%':
-	t = TOK__remi;
-	goto call_func;
-    case TOK_UMOD:
-	t = TOK__remu;
-	goto call_func;
-
-    default:
-	opc = 7;
-	goto gen_op8;
-    }
-}
-
-/* generate a floating point operation 'v = t1 op t2' instruction. The
-   two operands are guaranteed to have the same floating point type */
-/* XXX: need to use ST1 too */
-void gen_opf(int op)
-{
-    int ft, fc, fr, r;
-
-    if (op >= TOK_ULT && op <= TOK_GT)
-	gv2(RC_EDX, RC_EAX);	// make sure src2 is on b side
-    else
-	gv2(RC_FLOAT, RC_FLOAT);	// make sure src2 is on b side
-
-    ft = vtop->type.t;
-    fc = vtop->c.i;
-    r = vtop->r;
-    fr = vtop[-1].r;
-
-
-    if ((ft & VT_BTYPE) == VT_LDOUBLE)
-	tcc_error("long doubles not supported");
-
-    if (op >= TOK_ULT && op <= TOK_GT) {
-
-	r = vtop[-1].r;
-	fr = vtop[0].r;
-
-	C67_compare_reg = C67_B2;
-
-	if (op == TOK_LT) {
-	    if ((ft & VT_BTYPE) == VT_DOUBLE)
-		C67_CMPLTDP(r, fr, C67_B2);
-	    else
-		C67_CMPLTSP(r, fr, C67_B2);
-
-	    C67_invert_test = FALSE;
-	} else if (op == TOK_GE) {
-	    if ((ft & VT_BTYPE) == VT_DOUBLE)
-		C67_CMPLTDP(r, fr, C67_B2);
-	    else
-		C67_CMPLTSP(r, fr, C67_B2);
-
-	    C67_invert_test = TRUE;
-	} else if (op == TOK_GT) {
-	    if ((ft & VT_BTYPE) == VT_DOUBLE)
-		C67_CMPGTDP(r, fr, C67_B2);
-	    else
-		C67_CMPGTSP(r, fr, C67_B2);
-
-	    C67_invert_test = FALSE;
-	} else if (op == TOK_LE) {
-	    if ((ft & VT_BTYPE) == VT_DOUBLE)
-		C67_CMPGTDP(r, fr, C67_B2);
-	    else
-		C67_CMPGTSP(r, fr, C67_B2);
-
-	    C67_invert_test = TRUE;
-	} else if (op == TOK_EQ) {
-	    if ((ft & VT_BTYPE) == VT_DOUBLE)
-		C67_CMPEQDP(r, fr, C67_B2);
-	    else
-		C67_CMPEQSP(r, fr, C67_B2);
-
-	    C67_invert_test = FALSE;
-	} else if (op == TOK_NE) {
-	    if ((ft & VT_BTYPE) == VT_DOUBLE)
-		C67_CMPEQDP(r, fr, C67_B2);
-	    else
-		C67_CMPEQSP(r, fr, C67_B2);
-
-	    C67_invert_test = TRUE;
-	} else {
-	    ALWAYS_ASSERT(FALSE);
-	}
-	vtop->r = VT_CMP;	// tell TCC that result is in "flags" actually B2
-    } else {
-	if (op == '+') {
-	    if ((ft & VT_BTYPE) == VT_DOUBLE) {
-		C67_ADDDP(r, fr);	// ADD  fr,r,fr
-		C67_NOP(6);
-	    } else {
-		C67_ADDSP(r, fr);	// ADD  fr,r,fr
-		C67_NOP(3);
-	    }
-	    vtop--;
-	} else if (op == '-') {
-	    if ((ft & VT_BTYPE) == VT_DOUBLE) {
-		C67_SUBDP(r, fr);	// SUB  fr,r,fr
-		C67_NOP(6);
-	    } else {
-		C67_SUBSP(r, fr);	// SUB  fr,r,fr
-		C67_NOP(3);
-	    }
-	    vtop--;
-	} else if (op == '*') {
-	    if ((ft & VT_BTYPE) == VT_DOUBLE) {
-		C67_MPYDP(r, fr);	// MPY  fr,r,fr
-		C67_NOP(9);
-	    } else {
-		C67_MPYSP(r, fr);	// MPY  fr,r,fr
-		C67_NOP(3);
-	    }
-	    vtop--;
-	} else if (op == '/') {
-	    if ((ft & VT_BTYPE) == VT_DOUBLE) {
-		// must call intrinsic DP floating point divide
-		vswap();
-		/* call generic idiv function */
-		vpush_global_sym(&func_old_type, TOK__divd);
-		vrott(3);
-		gfunc_call(2);
-		vpushi(0);
-		vtop->r = REG_FRET;
-		vtop->r2 = REG_LRET;
-
-	    } else {
-		// must call intrinsic SP floating point divide
-		vswap();
-		/* call generic idiv function */
-		vpush_global_sym(&func_old_type, TOK__divf);
-		vrott(3);
-		gfunc_call(2);
-		vpushi(0);
-		vtop->r = REG_FRET;
-		vtop->r2 = VT_CONST;
-	    }
-	} else
-	    ALWAYS_ASSERT(FALSE);
-
-
-    }
-}
-
-
-/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
-   and 'long long' cases. */
-void gen_cvt_itof(int t)
-{
-    int r;
-
-    gv(RC_INT);
-    r = vtop->r;
-
-    if ((t & VT_BTYPE) == VT_DOUBLE) {
-	if (t & VT_UNSIGNED)
-	    C67_INTDPU(r, r);
-	else
-	    C67_INTDP(r, r);
-
-	C67_NOP(4);
-	vtop->type.t = VT_DOUBLE;
-    } else {
-	if (t & VT_UNSIGNED)
-	    C67_INTSPU(r, r);
-	else
-	    C67_INTSP(r, r);
-	C67_NOP(3);
-	vtop->type.t = VT_FLOAT;
-    }
-
-}
-
-/* convert fp to int 't' type */
-/* XXX: handle long long case */
-void gen_cvt_ftoi(int t)
-{
-    int r;
-
-    gv(RC_FLOAT);
-    r = vtop->r;
-
-    if (t != VT_INT)
-	tcc_error("long long not supported");
-    else {
-	if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) {
-	    C67_DPTRUNC(r, r);
-	    C67_NOP(3);
-	} else {
-	    C67_SPTRUNC(r, r);
-	    C67_NOP(3);
-	}
-
-	vtop->type.t = VT_INT;
-
-    }
-}
-
-/* convert from one floating point type to another */
-void gen_cvt_ftof(int t)
-{
-    int r, r2;
-
-    if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE &&
-	(t & VT_BTYPE) == VT_FLOAT) {
-	// convert double to float
-
-	gv(RC_FLOAT);		// get it in a register pair
-
-	r = vtop->r;
-
-	C67_DPSP(r, r);		// convert it to SP same register
-	C67_NOP(3);
-
-	vtop->type.t = VT_FLOAT;
-	vtop->r2 = VT_CONST;	// set this as unused
-    } else if ((vtop->type.t & VT_BTYPE) == VT_FLOAT &&
-	       (t & VT_BTYPE) == VT_DOUBLE) {
-	// convert float to double
-
-	gv(RC_FLOAT);		// get it in a register
-
-	r = vtop->r;
-
-	if (r == TREG_EAX) {	// make sure the paired reg is avail
-	    r2 = get_reg(RC_ECX);
-	} else if (r == TREG_EDX) {
-	    r2 = get_reg(RC_ST0);
-	} else {
-	    ALWAYS_ASSERT(FALSE);
-            r2 = 0; /* avoid warning */
-        }
-
-	C67_SPDP(r, r);		// convert it to DP same register
-	C67_NOP(1);
-
-	vtop->type.t = VT_DOUBLE;
-	vtop->r2 = r2;		// set this as unused
-    } else {
-	ALWAYS_ASSERT(FALSE);
-    }
-}
-
-/* computed goto support */
-void ggoto(void)
-{
-    gcall_or_jmp(1);
-    vtop--;
-}
-
-/* Save the stack pointer onto the stack and return the location of its address */
-ST_FUNC void gen_vla_sp_save(int addr) {
-    tcc_error("variable length arrays unsupported for this target");
-}
-
-/* Restore the SP from a location on the stack */
-ST_FUNC void gen_vla_sp_restore(int addr) {
-    tcc_error("variable length arrays unsupported for this target");
-}
-
-/* Subtract from the stack pointer, and push the resulting value onto the stack */
-ST_FUNC void gen_vla_alloc(CType *type, int align) {
-    tcc_error("variable length arrays unsupported for this target");
-}
-
-/* end of C67 code generator */
-/*************************************************************/
-#endif
-/*************************************************************/
diff --git a/tinyc/c67-link.c b/tinyc/c67-link.c
deleted file mode 100644
index de72e442f..000000000
--- a/tinyc/c67-link.c
+++ /dev/null
@@ -1,131 +0,0 @@
-#ifdef TARGET_DEFS_ONLY
-
-#define EM_TCC_TARGET EM_C60
-
-/* relocation type for 32 bit data relocation */
-#define R_DATA_32   R_C60_32
-#define R_DATA_PTR  R_C60_32
-#define R_JMP_SLOT  R_C60_JMP_SLOT
-#define R_GLOB_DAT  R_C60_GLOB_DAT
-#define R_COPY      R_C60_COPY
-#define R_RELATIVE  R_C60_RELATIVE
-
-#define R_NUM       R_C60_NUM
-
-#define ELF_START_ADDR 0x00000400
-#define ELF_PAGE_SIZE  0x1000
-
-#define PCRELATIVE_DLLPLT 0
-#define RELOCATE_DLLPLT 0
-
-#else /* !TARGET_DEFS_ONLY */
-
-#include "tcc.h"
-
-/* Returns 1 for a code relocation, 0 for a data relocation. For unknown
-   relocations, returns -1. */
-int code_reloc (int reloc_type)
-{
-    switch (reloc_type) {
-        case R_C60_32:
-	case R_C60LO16:
-	case R_C60HI16:
-        case R_C60_GOT32:
-        case R_C60_GOTOFF:
-        case R_C60_GOTPC:
-        case R_C60_COPY:
-            return 0;
-
-        case R_C60_PLT32:
-            return 1;
-    }
-
-    tcc_error ("Unknown relocation type: %d", reloc_type);
-    return -1;
-}
-
-/* Returns an enumerator to describe whether and when the relocation needs a
-   GOT and/or PLT entry to be created. See tcc.h for a description of the
-   different values. */
-int gotplt_entry_type (int reloc_type)
-{
-    switch (reloc_type) {
-        case R_C60_32:
-	case R_C60LO16:
-	case R_C60HI16:
-        case R_C60_COPY:
-            return NO_GOTPLT_ENTRY;
-
-        case R_C60_GOTOFF:
-        case R_C60_GOTPC:
-            return BUILD_GOT_ONLY;
-
-        case R_C60_PLT32:
-        case R_C60_GOT32:
-            return ALWAYS_GOTPLT_ENTRY;
-    }
-
-    tcc_error ("Unknown relocation type: %d", reloc_type);
-    return -1;
-}
-
-ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr)
-{
-    tcc_error("C67 got not implemented");
-    return 0;
-}
-
-/* relocate the PLT: compute addresses and offsets in the PLT now that final
-   address for PLT and GOT are known (see fill_program_header) */
-ST_FUNC void relocate_plt(TCCState *s1)
-{
-    uint8_t *p, *p_end;
-
-    if (!s1->plt)
-      return;
-
-    p = s1->plt->data;
-    p_end = p + s1->plt->data_offset;
-
-    if (p < p_end) {
-        /* XXX: TODO */
-        while (p < p_end) {
-            /* XXX: TODO */
-        }
-   }
-}
-
-void relocate_init(Section *sr) {}
-
-void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val)
-{
-    switch(type) {
-        case R_C60_32:
-            *(int *)ptr += val;
-            break;
-        case R_C60LO16:
-            {
-                uint32_t orig;
-
-                /* put the low 16 bits of the absolute address add to what is
-                   already there */
-                orig  =   ((*(int *)(ptr  )) >> 7) & 0xffff;
-                orig |=  (((*(int *)(ptr+4)) >> 7) & 0xffff) << 16;
-
-                /* patch both at once - assumes always in pairs Low - High */
-                *(int *) ptr    = (*(int *) ptr    & (~(0xffff << 7)) ) |
-                                   (((val+orig)      & 0xffff) << 7);
-                *(int *)(ptr+4) = (*(int *)(ptr+4) & (~(0xffff << 7)) ) |
-                                  ((((val+orig)>>16) & 0xffff) << 7);
-            }
-            break;
-        case R_C60HI16:
-            break;
-        default:
-            fprintf(stderr,"FIXME: handle reloc type %x at %x [%p] to %x\n",
-                    type, (unsigned) addr, ptr, (unsigned) val);
-            break;
-    }
-}
-
-#endif /* !TARGET_DEFS_ONLY */
diff --git a/tinyc/coff.h b/tinyc/coff.h
deleted file mode 100644
index e8e6185a6..000000000
--- a/tinyc/coff.h
+++ /dev/null
@@ -1,446 +0,0 @@
-/**************************************************************************/
-/*  COFF.H                                                                */
-/*     COFF data structures and related definitions used by the linker    */
-/**************************************************************************/
-
-/*------------------------------------------------------------------------*/
-/*  COFF FILE HEADER                                                      */
-/*------------------------------------------------------------------------*/
-struct filehdr {
-        unsigned short  f_magic;        /* magic number */
-        unsigned short  f_nscns;        /* number of sections */
-        long            f_timdat;       /* time & date stamp */
-        long            f_symptr;       /* file pointer to symtab */
-        long            f_nsyms;        /* number of symtab entries */
-        unsigned short  f_opthdr;       /* sizeof(optional hdr) */
-        unsigned short  f_flags;        /* flags */
-        unsigned short  f_TargetID;     /* for C6x = 0x0099 */
-        };
-
-/*------------------------------------------------------------------------*/
-/*  File header flags                                                     */
-/*------------------------------------------------------------------------*/
-#define  F_RELFLG   0x01       /* relocation info stripped from file       */
-#define  F_EXEC     0x02       /* file is executable (no unresolved refs)  */
-#define  F_LNNO     0x04       /* line numbers stripped from file          */
-#define  F_LSYMS    0x08       /* local symbols stripped from file         */
-#define  F_GSP10    0x10       /* 34010 version                            */
-#define  F_GSP20    0x20       /* 34020 version                            */
-#define  F_SWABD    0x40       /* bytes swabbed (in names)                 */
-#define  F_AR16WR   0x80       /* byte ordering of an AR16WR (PDP-11)      */
-#define  F_LITTLE   0x100      /* byte ordering of an AR32WR (vax)         */
-#define  F_BIG      0x200      /* byte ordering of an AR32W (3B, maxi)     */
-#define  F_PATCH    0x400      /* contains "patch" list in optional header */
-#define  F_NODF     0x400   
-
-#define F_VERSION    (F_GSP10  | F_GSP20)   
-#define F_BYTE_ORDER (F_LITTLE | F_BIG)
-#define FILHDR  struct filehdr
-
-/* #define FILHSZ  sizeof(FILHDR)  */
-#define FILHSZ  22                /* above rounds to align on 4 bytes which causes problems */
-
-#define COFF_C67_MAGIC 0x00c2
-
-/*------------------------------------------------------------------------*/
-/*  Macros to recognize magic numbers                                     */
-/*------------------------------------------------------------------------*/
-#define ISMAGIC(x)      (((unsigned short)(x))==(unsigned short)magic)
-#define ISARCHIVE(x)    ((((unsigned short)(x))==(unsigned short)ARTYPE))
-#define BADMAGIC(x)     (((unsigned short)(x) & 0x8080) && !ISMAGIC(x))
-
-
-/*------------------------------------------------------------------------*/
-/*  OPTIONAL FILE HEADER                                                  */
-/*------------------------------------------------------------------------*/
-typedef struct aouthdr {
-        short   magic;          /* see magic.h                          */
-        short   vstamp;         /* version stamp                        */
-        long    tsize;          /* text size in bytes, padded to FW bdry*/
-        long    dsize;          /* initialized data "  "                */
-        long    bsize;          /* uninitialized data "   "             */
-        long    entrypt;        /* entry pt.                            */
-        long    text_start;     /* base of text used for this file      */
-        long    data_start;     /* base of data used for this file      */
-} AOUTHDR;
-
-#define AOUTSZ  sizeof(AOUTHDR)
-
-/*----------------------------------------------------------------------*/
-/*      When a UNIX aout header is to be built in the optional header,  */
-/*      the following magic numbers can appear in that header:          */ 
-/*                                                                      */
-/*              AOUT1MAGIC : default : readonly sharable text segment   */
-/*              AOUT2MAGIC:          : writable text segment            */
-/*              PAGEMAGIC  :         : configured for paging            */
-/*----------------------------------------------------------------------*/
-#define AOUT1MAGIC 0410
-#define AOUT2MAGIC 0407
-#define PAGEMAGIC  0413
-
-
-/*------------------------------------------------------------------------*/
-/*  COMMON ARCHIVE FILE STRUCTURES                                        */
-/*                                                                        */
-/*       ARCHIVE File Organization:                                       */
-/*       _______________________________________________                  */
-/*       |__________ARCHIVE_MAGIC_STRING_______________|                  */
-/*       |__________ARCHIVE_FILE_MEMBER_1______________|                  */
-/*       |                                             |                  */
-/*       |       Archive File Header "ar_hdr"          |                  */
-/*       |.............................................|                  */
-/*       |       Member Contents                       |                  */
-/*       |               1. External symbol directory  |                  */
-/*       |               2. Text file                  |                  */
-/*       |_____________________________________________|                  */
-/*       |________ARCHIVE_FILE_MEMBER_2________________|                  */
-/*       |               "ar_hdr"                      |                  */
-/*       |.............................................|                  */
-/*       |       Member Contents (.o or text file)     |                  */
-/*       |_____________________________________________|                  */
-/*       |       .               .               .     |                  */
-/*       |       .               .               .     |                  */
-/*       |       .               .               .     |                  */
-/*       |_____________________________________________|                  */
-/*       |________ARCHIVE_FILE_MEMBER_n________________|                  */
-/*       |               "ar_hdr"                      |                  */
-/*       |.............................................|                  */
-/*       |               Member Contents               |                  */
-/*       |_____________________________________________|                  */
-/*                                                                        */
-/*------------------------------------------------------------------------*/
-
-#define COFF_ARMAG   "!<arch>\n"
-#define SARMAG  8
-#define ARFMAG  "`\n"
-
-struct ar_hdr           /* archive file member header - printable ascii */
-{
-        char    ar_name[16];    /* file member name - `/' terminated */
-        char    ar_date[12];    /* file member date - decimal */
-        char    ar_uid[6];      /* file member user id - decimal */
-        char    ar_gid[6];      /* file member group id - decimal */
-        char    ar_mode[8];     /* file member mode - octal */
-        char    ar_size[10];    /* file member size - decimal */
-        char    ar_fmag[2];     /* ARFMAG - string to end header */
-};
-
-
-/*------------------------------------------------------------------------*/
-/*  SECTION HEADER                                                        */
-/*------------------------------------------------------------------------*/
-struct scnhdr {
-        char            s_name[8];      /* section name */
-        long            s_paddr;        /* physical address */
-        long            s_vaddr;        /* virtual address */
-        long            s_size;         /* section size */
-        long            s_scnptr;       /* file ptr to raw data for section */
-        long            s_relptr;       /* file ptr to relocation */
-        long            s_lnnoptr;      /* file ptr to line numbers */
-        unsigned int	s_nreloc;       /* number of relocation entries */
-        unsigned int	s_nlnno;        /* number of line number entries */
-        unsigned int	s_flags;        /* flags */
-		unsigned short	s_reserved;     /* reserved byte */
-		unsigned short  s_page;         /* memory page id */
-        };
-
-#define SCNHDR  struct scnhdr
-#define SCNHSZ  sizeof(SCNHDR)
-
-/*------------------------------------------------------------------------*/
-/* Define constants for names of "special" sections                       */
-/*------------------------------------------------------------------------*/
-/* #define _TEXT    ".text" */
-#define _DATA    ".data"
-#define _BSS     ".bss"
-#define _CINIT   ".cinit"
-#define _TV      ".tv"
-
-/*------------------------------------------------------------------------*/
-/* The low 4 bits of s_flags is used as a section "type"                  */
-/*------------------------------------------------------------------------*/
-#define STYP_REG    0x00  /* "regular" : allocated, relocated, loaded */
-#define STYP_DSECT  0x01  /* "dummy"   : not allocated, relocated, not loaded */
-#define STYP_NOLOAD 0x02  /* "noload"  : allocated, relocated, not loaded */
-#define STYP_GROUP  0x04  /* "grouped" : formed of input sections */
-#define STYP_PAD    0x08  /* "padding" : not allocated, not relocated, loaded */
-#define STYP_COPY   0x10  /* "copy"    : used for C init tables - 
-                                                not allocated, relocated,
-                                                loaded;  reloc & lineno
-                                                entries processed normally */
-#define STYP_TEXT   0x20   /* section contains text only */
-#define STYP_DATA   0x40   /* section contains data only */
-#define STYP_BSS    0x80   /* section contains bss only */
-
-#define STYP_ALIGN  0x100  /* align flag passed by old version assemblers */
-#define ALIGN_MASK  0x0F00 /* part of s_flags that is used for align vals */
-#define ALIGNSIZE(x) (1 << ((x & ALIGN_MASK) >> 8))
-
-
-/*------------------------------------------------------------------------*/
-/*  RELOCATION ENTRIES                                                    */
-/*------------------------------------------------------------------------*/
-struct reloc
-{
-   long            r_vaddr;        /* (virtual) address of reference */
-   short           r_symndx;       /* index into symbol table */
-   unsigned short  r_disp;         /* additional bits for address calculation */
-   unsigned short  r_type;         /* relocation type */
-};
-
-#define RELOC   struct reloc
-#define RELSZ   10                 /* sizeof(RELOC) */
-
-/*--------------------------------------------------------------------------*/
-/*   define all relocation types                                            */
-/*--------------------------------------------------------------------------*/
-
-#define R_ABS           0         /* absolute address - no relocation       */
-#define R_DIR16         01        /* UNUSED                                 */
-#define R_REL16         02        /* UNUSED                                 */
-#define R_DIR24         04        /* UNUSED                                 */
-#define R_REL24         05        /* 24 bits, direct                        */
-#define R_DIR32         06        /* UNUSED                                 */
-#define R_RELBYTE      017        /* 8 bits, direct                         */
-#define R_RELWORD      020        /* 16 bits, direct                        */
-#define R_RELLONG      021        /* 32 bits, direct                        */
-#define R_PCRBYTE      022        /* 8 bits, PC-relative                    */
-#define R_PCRWORD      023        /* 16 bits, PC-relative                   */
-#define R_PCRLONG      024        /* 32 bits, PC-relative                   */
-#define R_OCRLONG      030        /* GSP: 32 bits, one's complement direct  */
-#define R_GSPPCR16     031        /* GSP: 16 bits, PC relative (in words)   */
-#define R_GSPOPR32     032        /* GSP: 32 bits, direct big-endian        */
-#define R_PARTLS16     040        /* Brahma: 16 bit offset of 24 bit address*/
-#define R_PARTMS8      041        /* Brahma: 8 bit page of 24 bit address   */
-#define R_PARTLS7      050        /* DSP: 7 bit offset of 16 bit address    */
-#define R_PARTMS9      051        /* DSP: 9 bit page of 16 bit address      */
-#define R_REL13        052        /* DSP: 13 bits, direct                   */
-
-
-/*------------------------------------------------------------------------*/
-/*  LINE NUMBER ENTRIES                                                   */
-/*------------------------------------------------------------------------*/
-struct lineno
-{
-        union
-        {
-                long    l_symndx ;      /* sym. table index of function name
-                                                iff l_lnno == 0      */
-                long    l_paddr ;       /* (physical) address of line number */
-        }               l_addr ;
-        unsigned short  l_lnno ;        /* line number */
-};
-
-#define LINENO  struct lineno
-#define LINESZ  6       /* sizeof(LINENO) */
-
-
-/*------------------------------------------------------------------------*/
-/*   STORAGE CLASSES                                                      */
-/*------------------------------------------------------------------------*/
-#define  C_EFCN          -1    /* physical end of function */
-#define  C_NULL          0
-#define  C_AUTO          1     /* automatic variable */
-#define  C_EXT           2     /* external symbol */
-#define  C_STAT          3     /* static */
-#define  C_REG           4     /* register variable */
-#define  C_EXTDEF        5     /* external definition */
-#define  C_LABEL         6     /* label */
-#define  C_ULABEL        7     /* undefined label */
-#define  C_MOS           8     /* member of structure */
-#define  C_ARG           9     /* function argument */
-#define  C_STRTAG        10    /* structure tag */
-#define  C_MOU           11    /* member of union */
-#define  C_UNTAG         12    /* union tag */
-#define  C_TPDEF         13    /* type definition */
-#define C_USTATIC        14    /* undefined static */
-#define  C_ENTAG         15    /* enumeration tag */
-#define  C_MOE           16    /* member of enumeration */
-#define  C_REGPARM       17    /* register parameter */
-#define  C_FIELD         18    /* bit field */
-
-#define  C_BLOCK         100   /* ".bb" or ".eb" */
-#define  C_FCN           101   /* ".bf" or ".ef" */
-#define  C_EOS           102   /* end of structure */
-#define  C_FILE          103   /* file name */
-#define  C_LINE          104   /* dummy sclass for line number entry */
-#define  C_ALIAS         105   /* duplicate tag */
-#define  C_HIDDEN        106   /* special storage class for external */
-                               /* symbols in dmert public libraries  */
-
-/*------------------------------------------------------------------------*/
-/*  SYMBOL TABLE ENTRIES                                                  */
-/*------------------------------------------------------------------------*/
-
-#define  SYMNMLEN   8      /*  Number of characters in a symbol name */
-#define  FILNMLEN   14     /*  Number of characters in a file name */
-#define  DIMNUM     4      /*  Number of array dimensions in auxiliary entry */
-
-
-struct syment
-{
-        union
-        {
-                char            _n_name[SYMNMLEN];      /* old COFF version */
-                struct
-                {
-                        long    _n_zeroes;      /* new == 0 */
-                        long    _n_offset;      /* offset into string table */
-                } _n_n;
-                char            *_n_nptr[2];    /* allows for overlaying */
-        } _n;
-        long                    n_value;        /* value of symbol */
-        short                   n_scnum;        /* section number */
-        unsigned short          n_type;         /* type and derived type */
-        char                    n_sclass;       /* storage class */
-        char                    n_numaux;       /* number of aux. entries */
-};
-
-#define n_name          _n._n_name
-#define n_nptr          _n._n_nptr[1]
-#define n_zeroes        _n._n_n._n_zeroes
-#define n_offset        _n._n_n._n_offset
-
-/*------------------------------------------------------------------------*/
-/* Relocatable symbols have a section number of the                       */
-/* section in which they are defined.  Otherwise, section                 */
-/* numbers have the following meanings:                                   */
-/*------------------------------------------------------------------------*/
-#define  N_UNDEF  0                     /* undefined symbol */
-#define  N_ABS    -1                    /* value of symbol is absolute */
-#define  N_DEBUG  -2                    /* special debugging symbol  */
-#define  N_TV     (unsigned short)-3    /* needs transfer vector (preload) */
-#define  P_TV     (unsigned short)-4    /* needs transfer vector (postload) */
-
-
-/*------------------------------------------------------------------------*/
-/* The fundamental type of a symbol packed into the low                   */
-/* 4 bits of the word.                                                    */
-/*------------------------------------------------------------------------*/
-#define  _EF    ".ef"
-
-#define  T_NULL     0          /* no type info */
-#define  T_ARG      1          /* function argument (only used by compiler) */
-#define  T_CHAR     2          /* character */
-#define  T_SHORT    3          /* short integer */
-#define  T_INT      4          /* integer */
-#define  T_LONG     5          /* long integer */
-#define  T_FLOAT    6          /* floating point */
-#define  T_DOUBLE   7          /* double word */
-#define  T_STRUCT   8          /* structure  */
-#define  T_UNION    9          /* union  */
-#define  T_ENUM     10         /* enumeration  */
-#define  T_MOE      11         /* member of enumeration */
-#define  T_UCHAR    12         /* unsigned character */
-#define  T_USHORT   13         /* unsigned short */
-#define  T_UINT     14         /* unsigned integer */
-#define  T_ULONG    15         /* unsigned long */
-
-/*------------------------------------------------------------------------*/
-/* derived types are:                                                     */
-/*------------------------------------------------------------------------*/
-#define  DT_NON      0          /* no derived type */
-#define  DT_PTR      1          /* pointer */
-#define  DT_FCN      2          /* function */
-#define  DT_ARY      3          /* array */
-
-#define MKTYPE(basic, d1,d2,d3,d4,d5,d6) \
-       ((basic) | ((d1) <<  4) | ((d2) <<  6) | ((d3) <<  8) |\
-                  ((d4) << 10) | ((d5) << 12) | ((d6) << 14))
-
-/*------------------------------------------------------------------------*/
-/* type packing constants and macros                                      */
-/*------------------------------------------------------------------------*/
-#define  N_BTMASK_COFF     017
-#define  N_TMASK_COFF      060
-#define  N_TMASK1_COFF     0300
-#define  N_TMASK2_COFF     0360
-#define  N_BTSHFT_COFF     4
-#define  N_TSHIFT_COFF     2
-
-#define  BTYPE_COFF(x)  ((x) & N_BTMASK_COFF)  
-#define  ISINT(x)  (((x) >= T_CHAR && (x) <= T_LONG) ||   \
-		    ((x) >= T_UCHAR && (x) <= T_ULONG) || (x) == T_ENUM)
-#define  ISFLT_COFF(x)  ((x) == T_DOUBLE || (x) == T_FLOAT)
-#define  ISPTR_COFF(x)  (((x) & N_TMASK_COFF) == (DT_PTR << N_BTSHFT_COFF)) 
-#define  ISFCN_COFF(x)  (((x) & N_TMASK_COFF) == (DT_FCN << N_BTSHFT_COFF))
-#define  ISARY_COFF(x)  (((x) & N_TMASK_COFF) == (DT_ARY << N_BTSHFT_COFF))
-#define  ISTAG_COFF(x)  ((x)==C_STRTAG || (x)==C_UNTAG || (x)==C_ENTAG)
-
-#define  INCREF_COFF(x) ((((x)&~N_BTMASK_COFF)<<N_TSHIFT_COFF)|(DT_PTR<<N_BTSHFT_COFF)|(x&N_BTMASK_COFF))
-#define  DECREF_COFF(x) ((((x)>>N_TSHIFT_COFF)&~N_BTMASK_COFF)|((x)&N_BTMASK_COFF))
-
-
-/*------------------------------------------------------------------------*/
-/*  AUXILIARY SYMBOL ENTRY                                                */
-/*------------------------------------------------------------------------*/
-union auxent
-{
-	struct
-	{
-		long            x_tagndx;       /* str, un, or enum tag indx */
-		union
-		{
-			struct
-			{
-				unsigned short  x_lnno; /* declaration line number */
-				unsigned short  x_size; /* str, union, array size */
-			} x_lnsz;
-			long    x_fsize;        /* size of function */
-		} x_misc;
-		union
-		{
-			struct                  /* if ISFCN, tag, or .bb */
-			{
-				long    x_lnnoptr;      /* ptr to fcn line # */
-				long    x_endndx;       /* entry ndx past block end */
-			}       x_fcn;
-			struct                  /* if ISARY, up to 4 dimen. */
-			{
-				unsigned short  x_dimen[DIMNUM];
-			}       x_ary;
-		}               x_fcnary;
-		unsigned short  x_regcount;   /* number of registers used by func */
-	}       x_sym;
-	struct
-	{
-		char    x_fname[FILNMLEN];
-	}       x_file;
-	struct
-	{
-		long    x_scnlen;          /* section length */
-		unsigned short  x_nreloc;  /* number of relocation entries */
-		unsigned short  x_nlinno;  /* number of line numbers */
-	}       x_scn;
-};
-
-#define SYMENT  struct syment
-#define SYMESZ  18      /* sizeof(SYMENT) */
-
-#define AUXENT  union auxent
-#define AUXESZ  18      /* sizeof(AUXENT) */
-
-/*------------------------------------------------------------------------*/
-/*  NAMES OF "SPECIAL" SYMBOLS                                            */
-/*------------------------------------------------------------------------*/
-#define _STEXT          ".text"
-#define _ETEXT          "etext"
-#define _SDATA          ".data"
-#define _EDATA          "edata"
-#define _SBSS           ".bss"
-#define _END            "end"
-#define _CINITPTR       "cinit"
-
-/*--------------------------------------------------------------------------*/
-/*  ENTRY POINT SYMBOLS                                                     */
-/*--------------------------------------------------------------------------*/
-#define _START          "_start"
-#define _MAIN           "_main"
-    /*  _CSTART         "_c_int00"          (defined in params.h)  */
-
-
-#define _TVORIG         "_tvorig"
-#define _TORIGIN        "_torigin"
-#define _DORIGIN        "_dorigin"
-
-#define _SORIGIN        "_sorigin"
diff --git a/tinyc/config.h b/tinyc/config.h
deleted file mode 100644
index a27c7484a..000000000
--- a/tinyc/config.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* Modified to not rely on a configure script: */
-#define CONFIG_SYSROOT ""
-#define TCC_VERSION "0.9.27"
-
-#if defined(WIN32) || defined(_WIN32)
-#  define TCC_TARGET_PE   1
-#  define TCC_TARGET_I386

-#  define CONFIG_TCCDIR "."

-#elif defined(__i386__)
-#  define CONFIG_USE_LIBGCC
-#  define TCC_TARGET_I386
-#  define CONFIG_TCCDIR "/usr/local/lib/tcc"
-#  define GCC_MAJOR 4
-#  define HOST_I386 1
-#else
-#  define CONFIG_USE_LIBGCC
-#  define TCC_TARGET_X86_64
-#  define CONFIG_TCCDIR "/usr/local/lib/tcc"
-#  define GCC_MAJOR 4
-#  define HOST_X86_64 1
-#endif
-
diff --git a/tinyc/conftest.c b/tinyc/conftest.c
deleted file mode 100644
index 2824cc89d..000000000
--- a/tinyc/conftest.c
+++ /dev/null
@@ -1,87 +0,0 @@
-#include <stdio.h>
-
-/* Define architecture */
-#if defined(__i386__) || defined _M_IX86
-# define TRIPLET_ARCH "i386"
-#elif defined(__x86_64__) || defined _M_AMD64
-# define TRIPLET_ARCH "x86_64"
-#elif defined(__arm__)
-# define TRIPLET_ARCH "arm"
-#elif defined(__aarch64__)
-# define TRIPLET_ARCH "aarch64"
-#else
-# define TRIPLET_ARCH "unknown"
-#endif
-
-/* Define OS */
-#if defined (__linux__)
-# define TRIPLET_OS "linux"
-#elif defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
-# define TRIPLET_OS "kfreebsd"
-#elif defined _WIN32
-# define TRIPLET_OS "win32"
-#elif !defined (__GNU__)
-# define TRIPLET_OS "unknown"
-#endif
-
-/* Define calling convention and ABI */
-#if defined (__ARM_EABI__)
-# if defined (__ARM_PCS_VFP)
-#  define TRIPLET_ABI "gnueabihf"
-# else
-#  define TRIPLET_ABI "gnueabi"
-# endif
-#else
-# define TRIPLET_ABI "gnu"
-#endif
-
-#if defined _WIN32
-# define TRIPLET TRIPLET_ARCH "-" TRIPLET_OS
-#elif defined __GNU__
-# define TRIPLET TRIPLET_ARCH "-" TRIPLET_ABI
-#else
-# define TRIPLET TRIPLET_ARCH "-" TRIPLET_OS "-" TRIPLET_ABI
-#endif
-
-#if defined(_WIN32)
-int _CRT_glob = 0;
-#endif
-
-int main(int argc, char *argv[])
-{
-    switch(argc == 2 ? argv[1][0] : 0) {
-        case 'b':
-        {
-            volatile unsigned foo = 0x01234567;
-            puts(*(unsigned char*)&foo == 0x67 ? "no" : "yes");
-            break;
-        }
-#ifdef __GNUC__
-        case 'm':
-            printf("%d\n", __GNUC_MINOR__);
-            break;
-        case 'v':
-            printf("%d\n", __GNUC__);
-            break;
-#elif defined __TINYC__
-        case 'v':
-            puts("0");
-            break;
-        case 'm':
-            printf("%d\n", __TINYC__);
-            break;
-#else
-        case 'm':
-        case 'v':
-            puts("0");
-            break;
-#endif
-        case 't':
-            puts(TRIPLET);
-            break;
-
-        default:
-            break;
-    }
-    return 0;
-}
diff --git a/tinyc/elf.h b/tinyc/elf.h
deleted file mode 100644
index 9fed6eb73..000000000
--- a/tinyc/elf.h
+++ /dev/null
@@ -1,3237 +0,0 @@
-/* This file defines standard ELF types, structures, and macros.
-   Copyright (C) 1995-2012 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, see
-   <http://www.gnu.org/licenses/>.  */
-
-#ifndef _ELF_H
-#define	_ELF_H 1
-
-#ifndef _WIN32
-#include <inttypes.h>
-#else
-#ifndef __int8_t_defined
-#define __int8_t_defined
-typedef signed char int8_t;
-typedef short int int16_t;
-typedef int int32_t;
-typedef long long int int64_t;
-typedef unsigned char           uint8_t;
-typedef unsigned short int      uint16_t;
-typedef unsigned int            uint32_t;
-typedef unsigned long long int  uint64_t;
-#endif
-#endif
-
-/* Standard ELF types.  */
-
-/* Type for a 16-bit quantity.  */
-typedef uint16_t Elf32_Half;
-typedef uint16_t Elf64_Half;
-
-/* Types for signed and unsigned 32-bit quantities.  */
-typedef uint32_t Elf32_Word;
-typedef	int32_t  Elf32_Sword;
-typedef uint32_t Elf64_Word;
-typedef	int32_t  Elf64_Sword;
-
-/* Types for signed and unsigned 64-bit quantities.  */
-typedef uint64_t Elf32_Xword;
-typedef	int64_t  Elf32_Sxword;
-typedef uint64_t Elf64_Xword;
-typedef	int64_t  Elf64_Sxword;
-
-/* Type of addresses.  */
-typedef uint32_t Elf32_Addr;
-typedef uint64_t Elf64_Addr;
-
-/* Type of file offsets.  */
-typedef uint32_t Elf32_Off;
-typedef uint64_t Elf64_Off;
-
-/* Type for section indices, which are 16-bit quantities.  */
-typedef uint16_t Elf32_Section;
-typedef uint16_t Elf64_Section;
-
-/* Type for version symbol information.  */
-typedef Elf32_Half Elf32_Versym;
-typedef Elf64_Half Elf64_Versym;
-
-
-/* The ELF file header.  This appears at the start of every ELF file.  */
-
-#define EI_NIDENT (16)
-
-typedef struct
-{
-  unsigned char	e_ident[EI_NIDENT];	/* Magic number and other info */
-  Elf32_Half	e_type;			/* Object file type */
-  Elf32_Half	e_machine;		/* Architecture */
-  Elf32_Word	e_version;		/* Object file version */
-  Elf32_Addr	e_entry;		/* Entry point virtual address */
-  Elf32_Off	e_phoff;		/* Program header table file offset */
-  Elf32_Off	e_shoff;		/* Section header table file offset */
-  Elf32_Word	e_flags;		/* Processor-specific flags */
-  Elf32_Half	e_ehsize;		/* ELF header size in bytes */
-  Elf32_Half	e_phentsize;		/* Program header table entry size */
-  Elf32_Half	e_phnum;		/* Program header table entry count */
-  Elf32_Half	e_shentsize;		/* Section header table entry size */
-  Elf32_Half	e_shnum;		/* Section header table entry count */
-  Elf32_Half	e_shstrndx;		/* Section header string table index */
-} Elf32_Ehdr;
-
-typedef struct
-{
-  unsigned char	e_ident[EI_NIDENT];	/* Magic number and other info */
-  Elf64_Half	e_type;			/* Object file type */
-  Elf64_Half	e_machine;		/* Architecture */
-  Elf64_Word	e_version;		/* Object file version */
-  Elf64_Addr	e_entry;		/* Entry point virtual address */
-  Elf64_Off	e_phoff;		/* Program header table file offset */
-  Elf64_Off	e_shoff;		/* Section header table file offset */
-  Elf64_Word	e_flags;		/* Processor-specific flags */
-  Elf64_Half	e_ehsize;		/* ELF header size in bytes */
-  Elf64_Half	e_phentsize;		/* Program header table entry size */
-  Elf64_Half	e_phnum;		/* Program header table entry count */
-  Elf64_Half	e_shentsize;		/* Section header table entry size */
-  Elf64_Half	e_shnum;		/* Section header table entry count */
-  Elf64_Half	e_shstrndx;		/* Section header string table index */
-} Elf64_Ehdr;
-
-/* Fields in the e_ident array.  The EI_* macros are indices into the
-   array.  The macros under each EI_* macro are the values the byte
-   may have.  */
-
-#define EI_MAG0		0		/* File identification byte 0 index */
-#define ELFMAG0		0x7f		/* Magic number byte 0 */
-
-#define EI_MAG1		1		/* File identification byte 1 index */
-#define ELFMAG1		'E'		/* Magic number byte 1 */
-
-#define EI_MAG2		2		/* File identification byte 2 index */
-#define ELFMAG2		'L'		/* Magic number byte 2 */
-
-#define EI_MAG3		3		/* File identification byte 3 index */
-#define ELFMAG3		'F'		/* Magic number byte 3 */
-
-/* Conglomeration of the identification bytes, for easy testing as a word.  */
-#define	ELFMAG		"\177ELF"
-#define	SELFMAG		4
-
-#define EI_CLASS	4		/* File class byte index */
-#define ELFCLASSNONE	0		/* Invalid class */
-#define ELFCLASS32	1		/* 32-bit objects */
-#define ELFCLASS64	2		/* 64-bit objects */
-#define ELFCLASSNUM	3
-
-#define EI_DATA		5		/* Data encoding byte index */
-#define ELFDATANONE	0		/* Invalid data encoding */
-#define ELFDATA2LSB	1		/* 2's complement, little endian */
-#define ELFDATA2MSB	2		/* 2's complement, big endian */
-#define ELFDATANUM	3
-
-#define EI_VERSION	6		/* File version byte index */
-					/* Value must be EV_CURRENT */
-
-#define EI_OSABI	7		/* OS ABI identification */
-#define ELFOSABI_NONE		0	/* UNIX System V ABI */
-#define ELFOSABI_SYSV		0	/* Alias.  */
-#define ELFOSABI_HPUX		1	/* HP-UX */
-#define ELFOSABI_NETBSD		2	/* NetBSD.  */
-#define ELFOSABI_GNU		3	/* Object uses GNU ELF extensions.  */
-#define ELFOSABI_LINUX		ELFOSABI_GNU /* Compatibility alias.  */
-#define ELFOSABI_SOLARIS	6	/* Sun Solaris.  */
-#define ELFOSABI_AIX		7	/* IBM AIX.  */
-#define ELFOSABI_IRIX		8	/* SGI Irix.  */
-#define ELFOSABI_FREEBSD	9	/* FreeBSD.  */
-#define ELFOSABI_TRU64		10	/* Compaq TRU64 UNIX.  */
-#define ELFOSABI_MODESTO	11	/* Novell Modesto.  */
-#define ELFOSABI_OPENBSD	12	/* OpenBSD.  */
-#define ELFOSABI_ARM_AEABI	64	/* ARM EABI */
-#define ELFOSABI_ARM		97	/* ARM */
-#define ELFOSABI_STANDALONE	255	/* Standalone (embedded) application */
-
-#define EI_ABIVERSION	8		/* ABI version */
-
-#define EI_PAD		9		/* Byte index of padding bytes */
-
-/* Legal values for e_type (object file type).  */
-
-#define ET_NONE		0		/* No file type */
-#define ET_REL		1		/* Relocatable file */
-#define ET_EXEC		2		/* Executable file */
-#define ET_DYN		3		/* Shared object file */
-#define ET_CORE		4		/* Core file */
-#define	ET_NUM		5		/* Number of defined types */
-#define ET_LOOS		0xfe00		/* OS-specific range start */
-#define ET_HIOS		0xfeff		/* OS-specific range end */
-#define ET_LOPROC	0xff00		/* Processor-specific range start */
-#define ET_HIPROC	0xffff		/* Processor-specific range end */
-
-/* Legal values for e_machine (architecture).  */
-
-#define EM_NONE		 0		/* No machine */
-#define EM_M32		 1		/* AT&T WE 32100 */
-#define EM_SPARC	 2		/* SUN SPARC */
-#define EM_386		 3		/* Intel 80386 */
-#define EM_68K		 4		/* Motorola m68k family */
-#define EM_88K		 5		/* Motorola m88k family */
-#define EM_860		 7		/* Intel 80860 */
-#define EM_MIPS		 8		/* MIPS R3000 big-endian */
-#define EM_S370		 9		/* IBM System/370 */
-#define EM_MIPS_RS3_LE	10		/* MIPS R3000 little-endian */
-
-#define EM_PARISC	15		/* HPPA */
-#define EM_VPP500	17		/* Fujitsu VPP500 */
-#define EM_SPARC32PLUS	18		/* Sun's "v8plus" */
-#define EM_960		19		/* Intel 80960 */
-#define EM_PPC		20		/* PowerPC */
-#define EM_PPC64	21		/* PowerPC 64-bit */
-#define EM_S390		22		/* IBM S390 */
-
-#define EM_V800		36		/* NEC V800 series */
-#define EM_FR20		37		/* Fujitsu FR20 */
-#define EM_RH32		38		/* TRW RH-32 */
-#define EM_RCE		39		/* Motorola RCE */
-#define EM_ARM		40		/* ARM */
-#define EM_FAKE_ALPHA	41		/* Digital Alpha */
-#define EM_SH		42		/* Hitachi SH */
-#define EM_SPARCV9	43		/* SPARC v9 64-bit */
-#define EM_TRICORE	44		/* Siemens Tricore */
-#define EM_ARC		45		/* Argonaut RISC Core */
-#define EM_H8_300	46		/* Hitachi H8/300 */
-#define EM_H8_300H	47		/* Hitachi H8/300H */
-#define EM_H8S		48		/* Hitachi H8S */
-#define EM_H8_500	49		/* Hitachi H8/500 */
-#define EM_IA_64	50		/* Intel Merced */
-#define EM_MIPS_X	51		/* Stanford MIPS-X */
-#define EM_COLDFIRE	52		/* Motorola Coldfire */
-#define EM_68HC12	53		/* Motorola M68HC12 */
-#define EM_MMA		54		/* Fujitsu MMA Multimedia Accelerator*/
-#define EM_PCP		55		/* Siemens PCP */
-#define EM_NCPU		56		/* Sony nCPU embedded RISC */
-#define EM_NDR1		57		/* Denso NDR1 microprocessor */
-#define EM_STARCORE	58		/* Motorola Start*Core processor */
-#define EM_ME16		59		/* Toyota ME16 processor */
-#define EM_ST100	60		/* STMicroelectronic ST100 processor */
-#define EM_TINYJ	61		/* Advanced Logic Corp. Tinyj emb.fam*/
-#define EM_X86_64	62		/* AMD x86-64 architecture */
-#define EM_PDSP		63		/* Sony DSP Processor */
-
-#define EM_FX66		66		/* Siemens FX66 microcontroller */
-#define EM_ST9PLUS	67		/* STMicroelectronics ST9+ 8/16 mc */
-#define EM_ST7		68		/* STMicroelectronics ST7 8 bit mc */
-#define EM_68HC16	69		/* Motorola MC68HC16 microcontroller */
-#define EM_68HC11	70		/* Motorola MC68HC11 microcontroller */
-#define EM_68HC08	71		/* Motorola MC68HC08 microcontroller */
-#define EM_68HC05	72		/* Motorola MC68HC05 microcontroller */
-#define EM_SVX		73		/* Silicon Graphics SVx */
-#define EM_ST19		74		/* STMicroelectronics ST19 8 bit mc */
-#define EM_VAX		75		/* Digital VAX */
-#define EM_CRIS		76		/* Axis Communications 32-bit embedded processor */
-#define EM_JAVELIN	77		/* Infineon Technologies 32-bit embedded processor */
-#define EM_FIREPATH	78		/* Element 14 64-bit DSP Processor */
-#define EM_ZSP		79		/* LSI Logic 16-bit DSP Processor */
-#define EM_MMIX		80		/* Donald Knuth's educational 64-bit processor */
-#define EM_HUANY	81		/* Harvard University machine-independent object files */
-#define EM_PRISM	82		/* SiTera Prism */
-#define EM_AVR		83		/* Atmel AVR 8-bit microcontroller */
-#define EM_FR30		84		/* Fujitsu FR30 */
-#define EM_D10V		85		/* Mitsubishi D10V */
-#define EM_D30V		86		/* Mitsubishi D30V */
-#define EM_V850		87		/* NEC v850 */
-#define EM_M32R		88		/* Mitsubishi M32R */
-#define EM_MN10300	89		/* Matsushita MN10300 */
-#define EM_MN10200	90		/* Matsushita MN10200 */
-#define EM_PJ		91		/* picoJava */
-#define EM_OPENRISC	92		/* OpenRISC 32-bit embedded processor */
-#define EM_ARC_A5	93		/* ARC Cores Tangent-A5 */
-#define EM_XTENSA	94		/* Tensilica Xtensa Architecture */
-#define EM_AARCH64	183		/* ARM AARCH64 */
-#define EM_TILEPRO	188		/* Tilera TILEPro */
-#define EM_TILEGX	191		/* Tilera TILE-Gx */
-#define EM_NUM		192
-
-/* If it is necessary to assign new unofficial EM_* values, please
-   pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the
-   chances of collision with official or non-GNU unofficial values.  */
-
-#define EM_ALPHA	0x9026
-#define EM_C60		0x9c60
-
-/* Legal values for e_version (version).  */
-
-#define EV_NONE		0		/* Invalid ELF version */
-#define EV_CURRENT	1		/* Current version */
-#define EV_NUM		2
-
-/* Section header.  */
-
-typedef struct
-{
-  Elf32_Word	sh_name;		/* Section name (string tbl index) */
-  Elf32_Word	sh_type;		/* Section type */
-  Elf32_Word	sh_flags;		/* Section flags */
-  Elf32_Addr	sh_addr;		/* Section virtual addr at execution */
-  Elf32_Off	sh_offset;		/* Section file offset */
-  Elf32_Word	sh_size;		/* Section size in bytes */
-  Elf32_Word	sh_link;		/* Link to another section */
-  Elf32_Word	sh_info;		/* Additional section information */
-  Elf32_Word	sh_addralign;		/* Section alignment */
-  Elf32_Word	sh_entsize;		/* Entry size if section holds table */
-} Elf32_Shdr;
-
-typedef struct
-{
-  Elf64_Word	sh_name;		/* Section name (string tbl index) */
-  Elf64_Word	sh_type;		/* Section type */
-  Elf64_Xword	sh_flags;		/* Section flags */
-  Elf64_Addr	sh_addr;		/* Section virtual addr at execution */
-  Elf64_Off	sh_offset;		/* Section file offset */
-  Elf64_Xword	sh_size;		/* Section size in bytes */
-  Elf64_Word	sh_link;		/* Link to another section */
-  Elf64_Word	sh_info;		/* Additional section information */
-  Elf64_Xword	sh_addralign;		/* Section alignment */
-  Elf64_Xword	sh_entsize;		/* Entry size if section holds table */
-} Elf64_Shdr;
-
-/* Special section indices.  */
-
-#define SHN_UNDEF	0		/* Undefined section */
-#define SHN_LORESERVE	0xff00		/* Start of reserved indices */
-#define SHN_LOPROC	0xff00		/* Start of processor-specific */
-#define SHN_BEFORE	0xff00		/* Order section before all others
-					   (Solaris).  */
-#define SHN_AFTER	0xff01		/* Order section after all others
-					   (Solaris).  */
-#define SHN_HIPROC	0xff1f		/* End of processor-specific */
-#define SHN_LOOS	0xff20		/* Start of OS-specific */
-#define SHN_HIOS	0xff3f		/* End of OS-specific */
-#define SHN_ABS		0xfff1		/* Associated symbol is absolute */
-#define SHN_COMMON	0xfff2		/* Associated symbol is common */
-#define SHN_XINDEX	0xffff		/* Index is in extra table.  */
-#define SHN_HIRESERVE	0xffff		/* End of reserved indices */
-
-/* Legal values for sh_type (section type).  */
-
-#define SHT_NULL	  0		/* Section header table entry unused */
-#define SHT_PROGBITS	  1		/* Program data */
-#define SHT_SYMTAB	  2		/* Symbol table */
-#define SHT_STRTAB	  3		/* String table */
-#define SHT_RELA	  4		/* Relocation entries with addends */
-#define SHT_HASH	  5		/* Symbol hash table */
-#define SHT_DYNAMIC	  6		/* Dynamic linking information */
-#define SHT_NOTE	  7		/* Notes */
-#define SHT_NOBITS	  8		/* Program space with no data (bss) */
-#define SHT_REL		  9		/* Relocation entries, no addends */
-#define SHT_SHLIB	  10		/* Reserved */
-#define SHT_DYNSYM	  11		/* Dynamic linker symbol table */
-#define SHT_INIT_ARRAY	  14		/* Array of constructors */
-#define SHT_FINI_ARRAY	  15		/* Array of destructors */
-#define SHT_PREINIT_ARRAY 16		/* Array of pre-constructors */
-#define SHT_GROUP	  17		/* Section group */
-#define SHT_SYMTAB_SHNDX  18		/* Extended section indices */
-#define	SHT_NUM		  19		/* Number of defined types.  */
-#define SHT_LOOS	  0x60000000	/* Start OS-specific.  */
-#define SHT_GNU_ATTRIBUTES 0x6ffffff5	/* Object attributes.  */
-#define SHT_GNU_HASH	  0x6ffffff6	/* GNU-style hash table.  */
-#define SHT_GNU_LIBLIST	  0x6ffffff7	/* Prelink library list */
-#define SHT_CHECKSUM	  0x6ffffff8	/* Checksum for DSO content.  */
-#define SHT_LOSUNW	  0x6ffffffa	/* Sun-specific low bound.  */
-#define SHT_SUNW_move	  0x6ffffffa
-#define SHT_SUNW_COMDAT   0x6ffffffb
-#define SHT_SUNW_syminfo  0x6ffffffc
-#define SHT_GNU_verdef	  0x6ffffffd	/* Version definition section.  */
-#define SHT_GNU_verneed	  0x6ffffffe	/* Version needs section.  */
-#define SHT_GNU_versym	  0x6fffffff	/* Version symbol table.  */
-#define SHT_HISUNW	  0x6fffffff	/* Sun-specific high bound.  */
-#define SHT_HIOS	  0x6fffffff	/* End OS-specific type */
-#define SHT_LOPROC	  0x70000000	/* Start of processor-specific */
-#define SHT_HIPROC	  0x7fffffff	/* End of processor-specific */
-#define SHT_LOUSER	  0x80000000	/* Start of application-specific */
-#define SHT_HIUSER	  0x8fffffff	/* End of application-specific */
-
-/* Legal values for sh_flags (section flags).  */
-
-#define SHF_WRITE	     (1 << 0)	/* Writable */
-#define SHF_ALLOC	     (1 << 1)	/* Occupies memory during execution */
-#define SHF_EXECINSTR	     (1 << 2)	/* Executable */
-#define SHF_MERGE	     (1 << 4)	/* Might be merged */
-#define SHF_STRINGS	     (1 << 5)	/* Contains nul-terminated strings */
-#define SHF_INFO_LINK	     (1 << 6)	/* `sh_info' contains SHT index */
-#define SHF_LINK_ORDER	     (1 << 7)	/* Preserve order after combining */
-#define SHF_OS_NONCONFORMING (1 << 8)	/* Non-standard OS specific handling
-					   required */
-#define SHF_GROUP	     (1 << 9)	/* Section is member of a group.  */
-#define SHF_TLS		     (1 << 10)	/* Section hold thread-local data.  */
-#define SHF_COMPRESSED	     (1 << 11)	/* Section with compressed data. */
-#define SHF_MASKOS	     0x0ff00000	/* OS-specific.  */
-#define SHF_MASKPROC	     0xf0000000	/* Processor-specific */
-#define SHF_ORDERED	     (1 << 30)	/* Special ordering requirement
-					   (Solaris).  */
-#define SHF_EXCLUDE	     (1 << 31)	/* Section is excluded unless
-					   referenced or allocated (Solaris).*/
-
-/* Section group handling.  */
-#define GRP_COMDAT	0x1		/* Mark group as COMDAT.  */
-
-/* Symbol table entry.  */
-
-typedef struct
-{
-  Elf32_Word	st_name;		/* Symbol name (string tbl index) */
-  Elf32_Addr	st_value;		/* Symbol value */
-  Elf32_Word	st_size;		/* Symbol size */
-  unsigned char	st_info;		/* Symbol type and binding */
-  unsigned char	st_other;		/* Symbol visibility */
-  Elf32_Section	st_shndx;		/* Section index */
-} Elf32_Sym;
-
-typedef struct
-{
-  Elf64_Word	st_name;		/* Symbol name (string tbl index) */
-  unsigned char	st_info;		/* Symbol type and binding */
-  unsigned char st_other;		/* Symbol visibility */
-  Elf64_Section	st_shndx;		/* Section index */
-  Elf64_Addr	st_value;		/* Symbol value */
-  Elf64_Xword	st_size;		/* Symbol size */
-} Elf64_Sym;
-
-/* The syminfo section if available contains additional information about
-   every dynamic symbol.  */
-
-typedef struct
-{
-  Elf32_Half si_boundto;		/* Direct bindings, symbol bound to */
-  Elf32_Half si_flags;			/* Per symbol flags */
-} Elf32_Syminfo;
-
-typedef struct
-{
-  Elf64_Half si_boundto;		/* Direct bindings, symbol bound to */
-  Elf64_Half si_flags;			/* Per symbol flags */
-} Elf64_Syminfo;
-
-/* Possible values for si_boundto.  */
-#define SYMINFO_BT_SELF		0xffff	/* Symbol bound to self */
-#define SYMINFO_BT_PARENT	0xfffe	/* Symbol bound to parent */
-#define SYMINFO_BT_LOWRESERVE	0xff00	/* Beginning of reserved entries */
-
-/* Possible bitmasks for si_flags.  */
-#define SYMINFO_FLG_DIRECT	0x0001	/* Direct bound symbol */
-#define SYMINFO_FLG_PASSTHRU	0x0002	/* Pass-thru symbol for translator */
-#define SYMINFO_FLG_COPY	0x0004	/* Symbol is a copy-reloc */
-#define SYMINFO_FLG_LAZYLOAD	0x0008	/* Symbol bound to object to be lazy
-					   loaded */
-/* Syminfo version values.  */
-#define SYMINFO_NONE		0
-#define SYMINFO_CURRENT		1
-#define SYMINFO_NUM		2
-
-
-/* How to extract and insert information held in the st_info field.  */
-
-#define ELF32_ST_BIND(val)		(((unsigned char) (val)) >> 4)
-#define ELF32_ST_TYPE(val)		((val) & 0xf)
-#define ELF32_ST_INFO(bind, type)	(((bind) << 4) + ((type) & 0xf))
-
-/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field.  */
-#define ELF64_ST_BIND(val)		ELF32_ST_BIND (val)
-#define ELF64_ST_TYPE(val)		ELF32_ST_TYPE (val)
-#define ELF64_ST_INFO(bind, type)	ELF32_ST_INFO ((bind), (type))
-
-/* Legal values for ST_BIND subfield of st_info (symbol binding).  */
-
-#define STB_LOCAL	0		/* Local symbol */
-#define STB_GLOBAL	1		/* Global symbol */
-#define STB_WEAK	2		/* Weak symbol */
-#define	STB_NUM		3		/* Number of defined types.  */
-#define STB_LOOS	10		/* Start of OS-specific */
-#define STB_GNU_UNIQUE	10		/* Unique symbol.  */
-#define STB_HIOS	12		/* End of OS-specific */
-#define STB_LOPROC	13		/* Start of processor-specific */
-#define STB_HIPROC	15		/* End of processor-specific */
-
-/* Legal values for ST_TYPE subfield of st_info (symbol type).  */
-
-#define STT_NOTYPE	0		/* Symbol type is unspecified */
-#define STT_OBJECT	1		/* Symbol is a data object */
-#define STT_FUNC	2		/* Symbol is a code object */
-#define STT_SECTION	3		/* Symbol associated with a section */
-#define STT_FILE	4		/* Symbol's name is file name */
-#define STT_COMMON	5		/* Symbol is a common data object */
-#define STT_TLS		6		/* Symbol is thread-local data object*/
-#define	STT_NUM		7		/* Number of defined types.  */
-#define STT_LOOS	10		/* Start of OS-specific */
-#define STT_GNU_IFUNC	10		/* Symbol is indirect code object */
-#define STT_HIOS	12		/* End of OS-specific */
-#define STT_LOPROC	13		/* Start of processor-specific */
-#define STT_HIPROC	15		/* End of processor-specific */
-
-
-/* Symbol table indices are found in the hash buckets and chain table
-   of a symbol hash table section.  This special index value indicates
-   the end of a chain, meaning no further symbols are found in that bucket.  */
-
-#define STN_UNDEF	0		/* End of a chain.  */
-
-
-/* How to extract and insert information held in the st_other field.  */
-
-#define ELF32_ST_VISIBILITY(o)	((o) & 0x03)
-
-/* For ELF64 the definitions are the same.  */
-#define ELF64_ST_VISIBILITY(o)	ELF32_ST_VISIBILITY (o)
-
-/* Symbol visibility specification encoded in the st_other field.  */
-#define STV_DEFAULT	0		/* Default symbol visibility rules */
-#define STV_INTERNAL	1		/* Processor specific hidden class */
-#define STV_HIDDEN	2		/* Sym unavailable in other modules */
-#define STV_PROTECTED	3		/* Not preemptible, not exported */
-
-
-/* Relocation table entry without addend (in section of type SHT_REL).  */
-
-typedef struct
-{
-  Elf32_Addr	r_offset;		/* Address */
-  Elf32_Word	r_info;			/* Relocation type and symbol index */
-} Elf32_Rel;
-
-/* I have seen two different definitions of the Elf64_Rel and
-   Elf64_Rela structures, so we'll leave them out until Novell (or
-   whoever) gets their act together.  */
-/* The following, at least, is used on Sparc v9, MIPS, and Alpha.  */
-
-typedef struct
-{
-  Elf64_Addr	r_offset;		/* Address */
-  Elf64_Xword	r_info;			/* Relocation type and symbol index */
-} Elf64_Rel;
-
-/* Relocation table entry with addend (in section of type SHT_RELA).  */
-
-typedef struct
-{
-  Elf32_Addr	r_offset;		/* Address */
-  Elf32_Word	r_info;			/* Relocation type and symbol index */
-  Elf32_Sword	r_addend;		/* Addend */
-} Elf32_Rela;
-
-typedef struct
-{
-  Elf64_Addr	r_offset;		/* Address */
-  Elf64_Xword	r_info;			/* Relocation type and symbol index */
-  Elf64_Sxword	r_addend;		/* Addend */
-} Elf64_Rela;
-
-/* How to extract and insert information held in the r_info field.  */
-
-#define ELF32_R_SYM(val)		((val) >> 8)
-#define ELF32_R_TYPE(val)		((val) & 0xff)
-#define ELF32_R_INFO(sym, type)		(((sym) << 8) + ((type) & 0xff))
-
-#define ELF64_R_SYM(i)			((i) >> 32)
-#define ELF64_R_TYPE(i)			((i) & 0xffffffff)
-#define ELF64_R_INFO(sym,type)		((((Elf64_Xword) (sym)) << 32) + (type))
-
-/* Program segment header.  */
-
-typedef struct
-{
-  Elf32_Word	p_type;			/* Segment type */
-  Elf32_Off	p_offset;		/* Segment file offset */
-  Elf32_Addr	p_vaddr;		/* Segment virtual address */
-  Elf32_Addr	p_paddr;		/* Segment physical address */
-  Elf32_Word	p_filesz;		/* Segment size in file */
-  Elf32_Word	p_memsz;		/* Segment size in memory */
-  Elf32_Word	p_flags;		/* Segment flags */
-  Elf32_Word	p_align;		/* Segment alignment */
-} Elf32_Phdr;
-
-typedef struct
-{
-  Elf64_Word	p_type;			/* Segment type */
-  Elf64_Word	p_flags;		/* Segment flags */
-  Elf64_Off	p_offset;		/* Segment file offset */
-  Elf64_Addr	p_vaddr;		/* Segment virtual address */
-  Elf64_Addr	p_paddr;		/* Segment physical address */
-  Elf64_Xword	p_filesz;		/* Segment size in file */
-  Elf64_Xword	p_memsz;		/* Segment size in memory */
-  Elf64_Xword	p_align;		/* Segment alignment */
-} Elf64_Phdr;
-
-/* Special value for e_phnum.  This indicates that the real number of
-   program headers is too large to fit into e_phnum.  Instead the real
-   value is in the field sh_info of section 0.  */
-
-#define PN_XNUM		0xffff
-
-/* Legal values for p_type (segment type).  */
-
-#define	PT_NULL		0		/* Program header table entry unused */
-#define PT_LOAD		1		/* Loadable program segment */
-#define PT_DYNAMIC	2		/* Dynamic linking information */
-#define PT_INTERP	3		/* Program interpreter */
-#define PT_NOTE		4		/* Auxiliary information */
-#define PT_SHLIB	5		/* Reserved */
-#define PT_PHDR		6		/* Entry for header table itself */
-#define PT_TLS		7		/* Thread-local storage segment */
-#define	PT_NUM		8		/* Number of defined types */
-#define PT_LOOS		0x60000000	/* Start of OS-specific */
-#define PT_GNU_EH_FRAME	0x6474e550	/* GCC .eh_frame_hdr segment */
-#define PT_GNU_STACK	0x6474e551	/* Indicates stack executability */
-#define PT_GNU_RELRO	0x6474e552	/* Read-only after relocation */
-#define PT_LOSUNW	0x6ffffffa
-#define PT_SUNWBSS	0x6ffffffa	/* Sun Specific segment */
-#define PT_SUNWSTACK	0x6ffffffb	/* Stack segment */
-#define PT_HISUNW	0x6fffffff
-#define PT_HIOS		0x6fffffff	/* End of OS-specific */
-#define PT_LOPROC	0x70000000	/* Start of processor-specific */
-#define PT_HIPROC	0x7fffffff	/* End of processor-specific */
-
-/* Legal values for p_flags (segment flags).  */
-
-#define PF_X		(1 << 0)	/* Segment is executable */
-#define PF_W		(1 << 1)	/* Segment is writable */
-#define PF_R		(1 << 2)	/* Segment is readable */
-#define PF_MASKOS	0x0ff00000	/* OS-specific */
-#define PF_MASKPROC	0xf0000000	/* Processor-specific */
-
-/* Legal values for note segment descriptor types for core files. */
-
-#define NT_PRSTATUS	1		/* Contains copy of prstatus struct */
-#define NT_FPREGSET	2		/* Contains copy of fpregset struct */
-#define NT_PRPSINFO	3		/* Contains copy of prpsinfo struct */
-#define NT_PRXREG	4		/* Contains copy of prxregset struct */
-#define NT_TASKSTRUCT	4		/* Contains copy of task structure */
-#define NT_PLATFORM	5		/* String from sysinfo(SI_PLATFORM) */
-#define NT_AUXV		6		/* Contains copy of auxv array */
-#define NT_GWINDOWS	7		/* Contains copy of gwindows struct */
-#define NT_ASRS		8		/* Contains copy of asrset struct */
-#define NT_PSTATUS	10		/* Contains copy of pstatus struct */
-#define NT_PSINFO	13		/* Contains copy of psinfo struct */
-#define NT_PRCRED	14		/* Contains copy of prcred struct */
-#define NT_UTSNAME	15		/* Contains copy of utsname struct */
-#define NT_LWPSTATUS	16		/* Contains copy of lwpstatus struct */
-#define NT_LWPSINFO	17		/* Contains copy of lwpinfo struct */
-#define NT_PRFPXREG	20		/* Contains copy of fprxregset struct */
-#define NT_PRXFPREG	0x46e62b7f	/* Contains copy of user_fxsr_struct */
-#define NT_PPC_VMX	0x100		/* PowerPC Altivec/VMX registers */
-#define NT_PPC_SPE	0x101		/* PowerPC SPE/EVR registers */
-#define NT_PPC_VSX	0x102		/* PowerPC VSX registers */
-#define NT_386_TLS	0x200		/* i386 TLS slots (struct user_desc) */
-#define NT_386_IOPERM	0x201		/* x86 io permission bitmap (1=deny) */
-#define NT_X86_XSTATE	0x202		/* x86 extended state using xsave */
-#define NT_S390_HIGH_GPRS	0x300	/* s390 upper register halves */
-#define NT_S390_TIMER	0x301		/* s390 timer register */
-#define NT_S390_TODCMP	0x302		/* s390 TOD clock comparator register */
-#define NT_S390_TODPREG	0x303		/* s390 TOD programmable register */
-#define NT_S390_CTRS	0x304		/* s390 control registers */
-#define NT_S390_PREFIX	0x305		/* s390 prefix register */
-#define NT_S390_LAST_BREAK	0x306	/* s390 breaking event address */
-#define NT_S390_SYSTEM_CALL	0x307	/* s390 system call restart data */
-#define NT_ARM_VFP	0x400		/* ARM VFP/NEON registers */
-#define NT_ARM_TLS	0x401		/* ARM TLS register */
-#define NT_ARM_HW_BREAK	0x402		/* ARM hardware breakpoint registers */
-#define NT_ARM_HW_WATCH	0x403		/* ARM hardware watchpoint registers */
-
-/* Legal values for the note segment descriptor types for object files.  */
-
-#define NT_VERSION	1		/* Contains a version string.  */
-
-
-/* Dynamic section entry.  */
-
-typedef struct
-{
-  Elf32_Sword	d_tag;			/* Dynamic entry type */
-  union
-    {
-      Elf32_Word d_val;			/* Integer value */
-      Elf32_Addr d_ptr;			/* Address value */
-    } d_un;
-} Elf32_Dyn;
-
-typedef struct
-{
-  Elf64_Sxword	d_tag;			/* Dynamic entry type */
-  union
-    {
-      Elf64_Xword d_val;		/* Integer value */
-      Elf64_Addr d_ptr;			/* Address value */
-    } d_un;
-} Elf64_Dyn;
-
-/* Legal values for d_tag (dynamic entry type).  */
-
-#define DT_NULL		0		/* Marks end of dynamic section */
-#define DT_NEEDED	1		/* Name of needed library */
-#define DT_PLTRELSZ	2		/* Size in bytes of PLT relocs */
-#define DT_PLTGOT	3		/* Processor defined value */
-#define DT_HASH		4		/* Address of symbol hash table */
-#define DT_STRTAB	5		/* Address of string table */
-#define DT_SYMTAB	6		/* Address of symbol table */
-#define DT_RELA		7		/* Address of Rela relocs */
-#define DT_RELASZ	8		/* Total size of Rela relocs */
-#define DT_RELAENT	9		/* Size of one Rela reloc */
-#define DT_STRSZ	10		/* Size of string table */
-#define DT_SYMENT	11		/* Size of one symbol table entry */
-#define DT_INIT		12		/* Address of init function */
-#define DT_FINI		13		/* Address of termination function */
-#define DT_SONAME	14		/* Name of shared object */
-#define DT_RPATH	15		/* Library search path (deprecated) */
-#define DT_SYMBOLIC	16		/* Start symbol search here */
-#define DT_REL		17		/* Address of Rel relocs */
-#define DT_RELSZ	18		/* Total size of Rel relocs */
-#define DT_RELENT	19		/* Size of one Rel reloc */
-#define DT_PLTREL	20		/* Type of reloc in PLT */
-#define DT_DEBUG	21		/* For debugging; unspecified */
-#define DT_TEXTREL	22		/* Reloc might modify .text */
-#define DT_JMPREL	23		/* Address of PLT relocs */
-#define	DT_BIND_NOW	24		/* Process relocations of object */
-#define	DT_INIT_ARRAY	25		/* Array with addresses of init fct */
-#define	DT_FINI_ARRAY	26		/* Array with addresses of fini fct */
-#define	DT_INIT_ARRAYSZ	27		/* Size in bytes of DT_INIT_ARRAY */
-#define	DT_FINI_ARRAYSZ	28		/* Size in bytes of DT_FINI_ARRAY */
-#define DT_RUNPATH	29		/* Library search path */
-#define DT_FLAGS	30		/* Flags for the object being loaded */
-#define DT_ENCODING	32		/* Start of encoded range */
-#define DT_PREINIT_ARRAY 32		/* Array with addresses of preinit fct*/
-#define DT_PREINIT_ARRAYSZ 33		/* size in bytes of DT_PREINIT_ARRAY */
-#define	DT_NUM		34		/* Number used */
-#define DT_LOOS		0x6000000d	/* Start of OS-specific */
-#define DT_HIOS		0x6ffff000	/* End of OS-specific */
-#define DT_LOPROC	0x70000000	/* Start of processor-specific */
-#define DT_HIPROC	0x7fffffff	/* End of processor-specific */
-#define	DT_PROCNUM	DT_MIPS_NUM	/* Most used by any processor */
-
-/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the
-   Dyn.d_un.d_val field of the Elf*_Dyn structure.  This follows Sun's
-   approach.  */
-#define DT_VALRNGLO	0x6ffffd00
-#define DT_GNU_PRELINKED 0x6ffffdf5	/* Prelinking timestamp */
-#define DT_GNU_CONFLICTSZ 0x6ffffdf6	/* Size of conflict section */
-#define DT_GNU_LIBLISTSZ 0x6ffffdf7	/* Size of library list */
-#define DT_CHECKSUM	0x6ffffdf8
-#define DT_PLTPADSZ	0x6ffffdf9
-#define DT_MOVEENT	0x6ffffdfa
-#define DT_MOVESZ	0x6ffffdfb
-#define DT_FEATURE_1	0x6ffffdfc	/* Feature selection (DTF_*).  */
-#define DT_POSFLAG_1	0x6ffffdfd	/* Flags for DT_* entries, effecting
-					   the following DT_* entry.  */
-#define DT_SYMINSZ	0x6ffffdfe	/* Size of syminfo table (in bytes) */
-#define DT_SYMINENT	0x6ffffdff	/* Entry size of syminfo */
-#define DT_VALRNGHI	0x6ffffdff
-#define DT_VALTAGIDX(tag)	(DT_VALRNGHI - (tag))	/* Reverse order! */
-#define DT_VALNUM 12
-
-/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the
-   Dyn.d_un.d_ptr field of the Elf*_Dyn structure.
-
-   If any adjustment is made to the ELF object after it has been
-   built these entries will need to be adjusted.  */
-#define DT_ADDRRNGLO	0x6ffffe00
-#define DT_GNU_HASH	0x6ffffef5	/* GNU-style hash table.  */
-#define DT_TLSDESC_PLT	0x6ffffef6
-#define DT_TLSDESC_GOT	0x6ffffef7
-#define DT_GNU_CONFLICT	0x6ffffef8	/* Start of conflict section */
-#define DT_GNU_LIBLIST	0x6ffffef9	/* Library list */
-#define DT_CONFIG	0x6ffffefa	/* Configuration information.  */
-#define DT_DEPAUDIT	0x6ffffefb	/* Dependency auditing.  */
-#define DT_AUDIT	0x6ffffefc	/* Object auditing.  */
-#define	DT_PLTPAD	0x6ffffefd	/* PLT padding.  */
-#define	DT_MOVETAB	0x6ffffefe	/* Move table.  */
-#define DT_SYMINFO	0x6ffffeff	/* Syminfo table.  */
-#define DT_ADDRRNGHI	0x6ffffeff
-#define DT_ADDRTAGIDX(tag)	(DT_ADDRRNGHI - (tag))	/* Reverse order! */
-#define DT_ADDRNUM 11
-
-/* The versioning entry types.  The next are defined as part of the
-   GNU extension.  */
-#define DT_VERSYM	0x6ffffff0
-
-#define DT_RELACOUNT	0x6ffffff9
-#define DT_RELCOUNT	0x6ffffffa
-
-/* These were chosen by Sun.  */
-#define DT_FLAGS_1	0x6ffffffb	/* State flags, see DF_1_* below.  */
-#define	DT_VERDEF	0x6ffffffc	/* Address of version definition
-					   table */
-#define	DT_VERDEFNUM	0x6ffffffd	/* Number of version definitions */
-#define	DT_VERNEED	0x6ffffffe	/* Address of table with needed
-					   versions */
-#define	DT_VERNEEDNUM	0x6fffffff	/* Number of needed versions */
-#define DT_VERSIONTAGIDX(tag)	(DT_VERNEEDNUM - (tag))	/* Reverse order! */
-#define DT_VERSIONTAGNUM 16
-
-/* Sun added these machine-independent extensions in the "processor-specific"
-   range.  Be compatible.  */
-#define DT_AUXILIARY    0x7ffffffd      /* Shared object to load before self */
-#define DT_FILTER       0x7fffffff      /* Shared object to get values from */
-#define DT_EXTRATAGIDX(tag)	((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1)
-#define DT_EXTRANUM	3
-
-/* Values of `d_un.d_val' in the DT_FLAGS entry.  */
-#define DF_ORIGIN	0x00000001	/* Object may use DF_ORIGIN */
-#define DF_SYMBOLIC	0x00000002	/* Symbol resolutions starts here */
-#define DF_TEXTREL	0x00000004	/* Object contains text relocations */
-#define DF_BIND_NOW	0x00000008	/* No lazy binding for this object */
-#define DF_STATIC_TLS	0x00000010	/* Module uses the static TLS model */
-
-/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1
-   entry in the dynamic section.  */
-#define DF_1_NOW	0x00000001	/* Set RTLD_NOW for this object.  */
-#define DF_1_GLOBAL	0x00000002	/* Set RTLD_GLOBAL for this object.  */
-#define DF_1_GROUP	0x00000004	/* Set RTLD_GROUP for this object.  */
-#define DF_1_NODELETE	0x00000008	/* Set RTLD_NODELETE for this object.*/
-#define DF_1_LOADFLTR	0x00000010	/* Trigger filtee loading at runtime.*/
-#define DF_1_INITFIRST	0x00000020	/* Set RTLD_INITFIRST for this object*/
-#define DF_1_NOOPEN	0x00000040	/* Set RTLD_NOOPEN for this object.  */
-#define DF_1_ORIGIN	0x00000080	/* $ORIGIN must be handled.  */
-#define DF_1_DIRECT	0x00000100	/* Direct binding enabled.  */
-#define DF_1_TRANS	0x00000200
-#define DF_1_INTERPOSE	0x00000400	/* Object is used to interpose.  */
-#define DF_1_NODEFLIB	0x00000800	/* Ignore default lib search path.  */
-#define DF_1_NODUMP	0x00001000	/* Object can't be dldump'ed.  */
-#define DF_1_CONFALT	0x00002000	/* Configuration alternative created.*/
-#define DF_1_ENDFILTEE	0x00004000	/* Filtee terminates filters search. */
-#define	DF_1_DISPRELDNE	0x00008000	/* Disp reloc applied at build time. */
-#define	DF_1_DISPRELPND	0x00010000	/* Disp reloc applied at run-time.  */
-#define	DF_1_NODIRECT	0x00020000	/* Object has no-direct binding. */
-#define	DF_1_IGNMULDEF	0x00040000
-#define	DF_1_NOKSYMS	0x00080000
-#define	DF_1_NOHDR	0x00100000
-#define	DF_1_EDITED	0x00200000	/* Object is modified after built.  */
-#define	DF_1_NORELOC	0x00400000
-#define	DF_1_SYMINTPOSE	0x00800000	/* Object has individual interposers.  */
-#define	DF_1_GLOBAUDIT	0x01000000	/* Global auditing required.  */
-#define	DF_1_SINGLETON	0x02000000	/* Singleton symbols are used.  */
-
-/* Flags for the feature selection in DT_FEATURE_1.  */
-#define DTF_1_PARINIT	0x00000001
-#define DTF_1_CONFEXP	0x00000002
-
-/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry.  */
-#define DF_P1_LAZYLOAD	0x00000001	/* Lazyload following object.  */
-#define DF_P1_GROUPPERM	0x00000002	/* Symbols from next object are not
-					   generally available.  */
-
-/* Version definition sections.  */
-
-typedef struct
-{
-  Elf32_Half	vd_version;		/* Version revision */
-  Elf32_Half	vd_flags;		/* Version information */
-  Elf32_Half	vd_ndx;			/* Version Index */
-  Elf32_Half	vd_cnt;			/* Number of associated aux entries */
-  Elf32_Word	vd_hash;		/* Version name hash value */
-  Elf32_Word	vd_aux;			/* Offset in bytes to verdaux array */
-  Elf32_Word	vd_next;		/* Offset in bytes to next verdef
-					   entry */
-} Elf32_Verdef;
-
-typedef struct
-{
-  Elf64_Half	vd_version;		/* Version revision */
-  Elf64_Half	vd_flags;		/* Version information */
-  Elf64_Half	vd_ndx;			/* Version Index */
-  Elf64_Half	vd_cnt;			/* Number of associated aux entries */
-  Elf64_Word	vd_hash;		/* Version name hash value */
-  Elf64_Word	vd_aux;			/* Offset in bytes to verdaux array */
-  Elf64_Word	vd_next;		/* Offset in bytes to next verdef
-					   entry */
-} Elf64_Verdef;
-
-
-/* Legal values for vd_version (version revision).  */
-#define VER_DEF_NONE	0		/* No version */
-#define VER_DEF_CURRENT	1		/* Current version */
-#define VER_DEF_NUM	2		/* Given version number */
-
-/* Legal values for vd_flags (version information flags).  */
-#define VER_FLG_BASE	0x1		/* Version definition of file itself */
-#define VER_FLG_WEAK	0x2		/* Weak version identifier */
-
-/* Versym symbol index values.  */
-#define	VER_NDX_LOCAL		0	/* Symbol is local.  */
-#define	VER_NDX_GLOBAL		1	/* Symbol is global.  */
-#define	VER_NDX_LORESERVE	0xff00	/* Beginning of reserved entries.  */
-#define	VER_NDX_ELIMINATE	0xff01	/* Symbol is to be eliminated.  */
-
-/* Auxiliary version information.  */
-
-typedef struct
-{
-  Elf32_Word	vda_name;		/* Version or dependency names */
-  Elf32_Word	vda_next;		/* Offset in bytes to next verdaux
-					   entry */
-} Elf32_Verdaux;
-
-typedef struct
-{
-  Elf64_Word	vda_name;		/* Version or dependency names */
-  Elf64_Word	vda_next;		/* Offset in bytes to next verdaux
-					   entry */
-} Elf64_Verdaux;
-
-
-/* Version dependency section.  */
-
-typedef struct
-{
-  Elf32_Half	vn_version;		/* Version of structure */
-  Elf32_Half	vn_cnt;			/* Number of associated aux entries */
-  Elf32_Word	vn_file;		/* Offset of filename for this
-					   dependency */
-  Elf32_Word	vn_aux;			/* Offset in bytes to vernaux array */
-  Elf32_Word	vn_next;		/* Offset in bytes to next verneed
-					   entry */
-} Elf32_Verneed;
-
-typedef struct
-{
-  Elf64_Half	vn_version;		/* Version of structure */
-  Elf64_Half	vn_cnt;			/* Number of associated aux entries */
-  Elf64_Word	vn_file;		/* Offset of filename for this
-					   dependency */
-  Elf64_Word	vn_aux;			/* Offset in bytes to vernaux array */
-  Elf64_Word	vn_next;		/* Offset in bytes to next verneed
-					   entry */
-} Elf64_Verneed;
-
-
-/* Legal values for vn_version (version revision).  */
-#define VER_NEED_NONE	 0		/* No version */
-#define VER_NEED_CURRENT 1		/* Current version */
-#define VER_NEED_NUM	 2		/* Given version number */
-
-/* Auxiliary needed version information.  */
-
-typedef struct
-{
-  Elf32_Word	vna_hash;		/* Hash value of dependency name */
-  Elf32_Half	vna_flags;		/* Dependency specific information */
-  Elf32_Half	vna_other;		/* Unused */
-  Elf32_Word	vna_name;		/* Dependency name string offset */
-  Elf32_Word	vna_next;		/* Offset in bytes to next vernaux
-					   entry */
-} Elf32_Vernaux;
-
-typedef struct
-{
-  Elf64_Word	vna_hash;		/* Hash value of dependency name */
-  Elf64_Half	vna_flags;		/* Dependency specific information */
-  Elf64_Half	vna_other;		/* Unused */
-  Elf64_Word	vna_name;		/* Dependency name string offset */
-  Elf64_Word	vna_next;		/* Offset in bytes to next vernaux
-					   entry */
-} Elf64_Vernaux;
-
-
-/* Legal values for vna_flags.  */
-#define VER_FLG_WEAK	0x2		/* Weak version identifier */
-
-
-/* Auxiliary vector.  */
-
-/* This vector is normally only used by the program interpreter.  The
-   usual definition in an ABI supplement uses the name auxv_t.  The
-   vector is not usually defined in a standard <elf.h> file, but it
-   can't hurt.  We rename it to avoid conflicts.  The sizes of these
-   types are an arrangement between the exec server and the program
-   interpreter, so we don't fully specify them here.  */
-
-typedef struct
-{
-  uint32_t a_type;		/* Entry type */
-  union
-    {
-      uint32_t a_val;		/* Integer value */
-      /* We use to have pointer elements added here.  We cannot do that,
-	 though, since it does not work when using 32-bit definitions
-	 on 64-bit platforms and vice versa.  */
-    } a_un;
-} Elf32_auxv_t;
-
-typedef struct
-{
-  uint64_t a_type;		/* Entry type */
-  union
-    {
-      uint64_t a_val;		/* Integer value */
-      /* We use to have pointer elements added here.  We cannot do that,
-	 though, since it does not work when using 32-bit definitions
-	 on 64-bit platforms and vice versa.  */
-    } a_un;
-} Elf64_auxv_t;
-
-/* Legal values for a_type (entry type).  */
-
-#define AT_NULL		0		/* End of vector */
-#define AT_IGNORE	1		/* Entry should be ignored */
-#define AT_EXECFD	2		/* File descriptor of program */
-#define AT_PHDR		3		/* Program headers for program */
-#define AT_PHENT	4		/* Size of program header entry */
-#define AT_PHNUM	5		/* Number of program headers */
-#define AT_PAGESZ	6		/* System page size */
-#define AT_BASE		7		/* Base address of interpreter */
-#define AT_FLAGS	8		/* Flags */
-#define AT_ENTRY	9		/* Entry point of program */
-#define AT_NOTELF	10		/* Program is not ELF */
-#define AT_UID		11		/* Real uid */
-#define AT_EUID		12		/* Effective uid */
-#define AT_GID		13		/* Real gid */
-#define AT_EGID		14		/* Effective gid */
-#define AT_CLKTCK	17		/* Frequency of times() */
-
-/* Some more special a_type values describing the hardware.  */
-#define AT_PLATFORM	15		/* String identifying platform.  */
-#define AT_HWCAP	16		/* Machine dependent hints about
-					   processor capabilities.  */
-
-/* This entry gives some information about the FPU initialization
-   performed by the kernel.  */
-#define AT_FPUCW	18		/* Used FPU control word.  */
-
-/* Cache block sizes.  */
-#define AT_DCACHEBSIZE	19		/* Data cache block size.  */
-#define AT_ICACHEBSIZE	20		/* Instruction cache block size.  */
-#define AT_UCACHEBSIZE	21		/* Unified cache block size.  */
-
-/* A special ignored value for PPC, used by the kernel to control the
-   interpretation of the AUXV. Must be > 16.  */
-#define AT_IGNOREPPC	22		/* Entry should be ignored.  */
-
-#define	AT_SECURE	23		/* Boolean, was exec setuid-like?  */
-
-#define AT_BASE_PLATFORM 24		/* String identifying real platforms.*/
-
-#define AT_RANDOM	25		/* Address of 16 random bytes.  */
-
-#define AT_EXECFN	31		/* Filename of executable.  */
-
-/* Pointer to the global system page used for system calls and other
-   nice things.  */
-#define AT_SYSINFO	32
-#define AT_SYSINFO_EHDR	33
-
-/* Shapes of the caches.  Bits 0-3 contains associativity; bits 4-7 contains
-   log2 of line size; mask those to get cache size.  */
-#define AT_L1I_CACHESHAPE	34
-#define AT_L1D_CACHESHAPE	35
-#define AT_L2_CACHESHAPE	36
-#define AT_L3_CACHESHAPE	37
-
-/* Note section contents.  Each entry in the note section begins with
-   a header of a fixed form.  */
-
-typedef struct
-{
-  Elf32_Word n_namesz;			/* Length of the note's name.  */
-  Elf32_Word n_descsz;			/* Length of the note's descriptor.  */
-  Elf32_Word n_type;			/* Type of the note.  */
-} Elf32_Nhdr;
-
-typedef struct
-{
-  Elf64_Word n_namesz;			/* Length of the note's name.  */
-  Elf64_Word n_descsz;			/* Length of the note's descriptor.  */
-  Elf64_Word n_type;			/* Type of the note.  */
-} Elf64_Nhdr;
-
-/* Known names of notes.  */
-
-/* Solaris entries in the note section have this name.  */
-#define ELF_NOTE_SOLARIS	"SUNW Solaris"
-
-/* Note entries for GNU systems have this name.  */
-#define ELF_NOTE_GNU		"GNU"
-
-
-/* Defined types of notes for Solaris.  */
-
-/* Value of descriptor (one word) is desired pagesize for the binary.  */
-#define ELF_NOTE_PAGESIZE_HINT	1
-
-
-/* Defined note types for GNU systems.  */
-
-/* ABI information.  The descriptor consists of words:
-   word 0: OS descriptor
-   word 1: major version of the ABI
-   word 2: minor version of the ABI
-   word 3: subminor version of the ABI
-*/
-#define NT_GNU_ABI_TAG	1
-#define ELF_NOTE_ABI	NT_GNU_ABI_TAG /* Old name.  */
-
-/* Known OSes.  These values can appear in word 0 of an
-   NT_GNU_ABI_TAG note section entry.  */
-#define ELF_NOTE_OS_LINUX	0
-#define ELF_NOTE_OS_GNU		1
-#define ELF_NOTE_OS_SOLARIS2	2
-#define ELF_NOTE_OS_FREEBSD	3
-
-/* Synthetic hwcap information.  The descriptor begins with two words:
-   word 0: number of entries
-   word 1: bitmask of enabled entries
-   Then follow variable-length entries, one byte followed by a
-   '\0'-terminated hwcap name string.  The byte gives the bit
-   number to test if enabled, (1U << bit) & bitmask.  */
-#define NT_GNU_HWCAP	2
-
-/* Build ID bits as generated by ld --build-id.
-   The descriptor consists of any nonzero number of bytes.  */
-#define NT_GNU_BUILD_ID	3
-
-/* Version note generated by GNU gold containing a version string.  */
-#define NT_GNU_GOLD_VERSION	4
-
-
-/* Move records.  */
-typedef struct
-{
-  Elf32_Xword m_value;		/* Symbol value.  */
-  Elf32_Word m_info;		/* Size and index.  */
-  Elf32_Word m_poffset;		/* Symbol offset.  */
-  Elf32_Half m_repeat;		/* Repeat count.  */
-  Elf32_Half m_stride;		/* Stride info.  */
-} Elf32_Move;
-
-typedef struct
-{
-  Elf64_Xword m_value;		/* Symbol value.  */
-  Elf64_Xword m_info;		/* Size and index.  */
-  Elf64_Xword m_poffset;	/* Symbol offset.  */
-  Elf64_Half m_repeat;		/* Repeat count.  */
-  Elf64_Half m_stride;		/* Stride info.  */
-} Elf64_Move;
-
-/* Macro to construct move records.  */
-#define ELF32_M_SYM(info)	((info) >> 8)
-#define ELF32_M_SIZE(info)	((unsigned char) (info))
-#define ELF32_M_INFO(sym, size)	(((sym) << 8) + (unsigned char) (size))
-
-#define ELF64_M_SYM(info)	ELF32_M_SYM (info)
-#define ELF64_M_SIZE(info)	ELF32_M_SIZE (info)
-#define ELF64_M_INFO(sym, size)	ELF32_M_INFO (sym, size)
-
-
-/* Motorola 68k specific definitions.  */
-
-/* Values for Elf32_Ehdr.e_flags.  */
-#define EF_CPU32	0x00810000
-
-/* m68k relocs.  */
-
-#define R_68K_NONE	0		/* No reloc */
-#define R_68K_32	1		/* Direct 32 bit  */
-#define R_68K_16	2		/* Direct 16 bit  */
-#define R_68K_8		3		/* Direct 8 bit  */
-#define R_68K_PC32	4		/* PC relative 32 bit */
-#define R_68K_PC16	5		/* PC relative 16 bit */
-#define R_68K_PC8	6		/* PC relative 8 bit */
-#define R_68K_GOT32	7		/* 32 bit PC relative GOT entry */
-#define R_68K_GOT16	8		/* 16 bit PC relative GOT entry */
-#define R_68K_GOT8	9		/* 8 bit PC relative GOT entry */
-#define R_68K_GOT32O	10		/* 32 bit GOT offset */
-#define R_68K_GOT16O	11		/* 16 bit GOT offset */
-#define R_68K_GOT8O	12		/* 8 bit GOT offset */
-#define R_68K_PLT32	13		/* 32 bit PC relative PLT address */
-#define R_68K_PLT16	14		/* 16 bit PC relative PLT address */
-#define R_68K_PLT8	15		/* 8 bit PC relative PLT address */
-#define R_68K_PLT32O	16		/* 32 bit PLT offset */
-#define R_68K_PLT16O	17		/* 16 bit PLT offset */
-#define R_68K_PLT8O	18		/* 8 bit PLT offset */
-#define R_68K_COPY	19		/* Copy symbol at runtime */
-#define R_68K_GLOB_DAT	20		/* Create GOT entry */
-#define R_68K_JMP_SLOT	21		/* Create PLT entry */
-#define R_68K_RELATIVE	22		/* Adjust by program base */
-#define R_68K_TLS_GD32      25          /* 32 bit GOT offset for GD */
-#define R_68K_TLS_GD16      26          /* 16 bit GOT offset for GD */
-#define R_68K_TLS_GD8       27          /* 8 bit GOT offset for GD */
-#define R_68K_TLS_LDM32     28          /* 32 bit GOT offset for LDM */
-#define R_68K_TLS_LDM16     29          /* 16 bit GOT offset for LDM */
-#define R_68K_TLS_LDM8      30          /* 8 bit GOT offset for LDM */
-#define R_68K_TLS_LDO32     31          /* 32 bit module-relative offset */
-#define R_68K_TLS_LDO16     32          /* 16 bit module-relative offset */
-#define R_68K_TLS_LDO8      33          /* 8 bit module-relative offset */
-#define R_68K_TLS_IE32      34          /* 32 bit GOT offset for IE */
-#define R_68K_TLS_IE16      35          /* 16 bit GOT offset for IE */
-#define R_68K_TLS_IE8       36          /* 8 bit GOT offset for IE */
-#define R_68K_TLS_LE32      37          /* 32 bit offset relative to
-					   static TLS block */
-#define R_68K_TLS_LE16      38          /* 16 bit offset relative to
-					   static TLS block */
-#define R_68K_TLS_LE8       39          /* 8 bit offset relative to
-					   static TLS block */
-#define R_68K_TLS_DTPMOD32  40          /* 32 bit module number */
-#define R_68K_TLS_DTPREL32  41          /* 32 bit module-relative offset */
-#define R_68K_TLS_TPREL32   42          /* 32 bit TP-relative offset */
-/* Keep this the last entry.  */
-#define R_68K_NUM	43
-
-/* Intel 80386 specific definitions.  */
-
-/* i386 relocs.  */
-
-#define R_386_NONE	   0		/* No reloc */
-#define R_386_32	   1		/* Direct 32 bit  */
-#define R_386_PC32	   2		/* PC relative 32 bit */
-#define R_386_GOT32	   3		/* 32 bit GOT entry */
-#define R_386_PLT32	   4		/* 32 bit PLT address */
-#define R_386_COPY	   5		/* Copy symbol at runtime */
-#define R_386_GLOB_DAT	   6		/* Create GOT entry */
-#define R_386_JMP_SLOT	   7		/* Create PLT entry */
-#define R_386_RELATIVE	   8		/* Adjust by program base */
-#define R_386_GOTOFF	   9		/* 32 bit offset to GOT */
-#define R_386_GOTPC	   10		/* 32 bit PC relative offset to GOT */
-#define R_386_32PLT	   11
-#define R_386_TLS_TPOFF	   14		/* Offset in static TLS block */
-#define R_386_TLS_IE	   15		/* Address of GOT entry for static TLS
-					   block offset */
-#define R_386_TLS_GOTIE	   16		/* GOT entry for static TLS block
-					   offset */
-#define R_386_TLS_LE	   17		/* Offset relative to static TLS
-					   block */
-#define R_386_TLS_GD	   18		/* Direct 32 bit for GNU version of
-					   general dynamic thread local data */
-#define R_386_TLS_LDM	   19		/* Direct 32 bit for GNU version of
-					   local dynamic thread local data
-					   in LE code */
-#define R_386_16	   20
-#define R_386_PC16	   21
-#define R_386_8		   22
-#define R_386_PC8	   23
-#define R_386_TLS_GD_32	   24		/* Direct 32 bit for general dynamic
-					   thread local data */
-#define R_386_TLS_GD_PUSH  25		/* Tag for pushl in GD TLS code */
-#define R_386_TLS_GD_CALL  26		/* Relocation for call to
-					   __tls_get_addr() */
-#define R_386_TLS_GD_POP   27		/* Tag for popl in GD TLS code */
-#define R_386_TLS_LDM_32   28		/* Direct 32 bit for local dynamic
-					   thread local data in LE code */
-#define R_386_TLS_LDM_PUSH 29		/* Tag for pushl in LDM TLS code */
-#define R_386_TLS_LDM_CALL 30		/* Relocation for call to
-					   __tls_get_addr() in LDM code */
-#define R_386_TLS_LDM_POP  31		/* Tag for popl in LDM TLS code */
-#define R_386_TLS_LDO_32   32		/* Offset relative to TLS block */
-#define R_386_TLS_IE_32	   33		/* GOT entry for negated static TLS
-					   block offset */
-#define R_386_TLS_LE_32	   34		/* Negated offset relative to static
-					   TLS block */
-#define R_386_TLS_DTPMOD32 35		/* ID of module containing symbol */
-#define R_386_TLS_DTPOFF32 36		/* Offset in TLS block */
-#define R_386_TLS_TPOFF32  37		/* Negated offset in static TLS block */
-/* 38? */
-#define R_386_TLS_GOTDESC  39		/* GOT offset for TLS descriptor.  */
-#define R_386_TLS_DESC_CALL 40		/* Marker of call through TLS
-					   descriptor for
-					   relaxation.  */
-#define R_386_TLS_DESC     41		/* TLS descriptor containing
-					   pointer to code and to
-					   argument, returning the TLS
-					   offset for the symbol.  */
-#define R_386_IRELATIVE	   42		/* Adjust indirectly by program base */
-#define R_386_GOT32X       43		/* 32 bit GOT entry, relaxable */
-/* Keep this the last entry.  */
-#define R_386_NUM	   44
-
-/* SUN SPARC specific definitions.  */
-
-/* Legal values for ST_TYPE subfield of st_info (symbol type).  */
-
-#define STT_SPARC_REGISTER	13	/* Global register reserved to app. */
-
-/* Values for Elf64_Ehdr.e_flags.  */
-
-#define EF_SPARCV9_MM		3
-#define EF_SPARCV9_TSO		0
-#define EF_SPARCV9_PSO		1
-#define EF_SPARCV9_RMO		2
-#define EF_SPARC_LEDATA		0x800000 /* little endian data */
-#define EF_SPARC_EXT_MASK	0xFFFF00
-#define EF_SPARC_32PLUS		0x000100 /* generic V8+ features */
-#define EF_SPARC_SUN_US1	0x000200 /* Sun UltraSPARC1 extensions */
-#define EF_SPARC_HAL_R1		0x000400 /* HAL R1 extensions */
-#define EF_SPARC_SUN_US3	0x000800 /* Sun UltraSPARCIII extensions */
-
-/* SPARC relocs.  */
-
-#define R_SPARC_NONE		0	/* No reloc */
-#define R_SPARC_8		1	/* Direct 8 bit */
-#define R_SPARC_16		2	/* Direct 16 bit */
-#define R_SPARC_32		3	/* Direct 32 bit */
-#define R_SPARC_DISP8		4	/* PC relative 8 bit */
-#define R_SPARC_DISP16		5	/* PC relative 16 bit */
-#define R_SPARC_DISP32		6	/* PC relative 32 bit */
-#define R_SPARC_WDISP30		7	/* PC relative 30 bit shifted */
-#define R_SPARC_WDISP22		8	/* PC relative 22 bit shifted */
-#define R_SPARC_HI22		9	/* High 22 bit */
-#define R_SPARC_22		10	/* Direct 22 bit */
-#define R_SPARC_13		11	/* Direct 13 bit */
-#define R_SPARC_LO10		12	/* Truncated 10 bit */
-#define R_SPARC_GOT10		13	/* Truncated 10 bit GOT entry */
-#define R_SPARC_GOT13		14	/* 13 bit GOT entry */
-#define R_SPARC_GOT22		15	/* 22 bit GOT entry shifted */
-#define R_SPARC_PC10		16	/* PC relative 10 bit truncated */
-#define R_SPARC_PC22		17	/* PC relative 22 bit shifted */
-#define R_SPARC_WPLT30		18	/* 30 bit PC relative PLT address */
-#define R_SPARC_COPY		19	/* Copy symbol at runtime */
-#define R_SPARC_GLOB_DAT	20	/* Create GOT entry */
-#define R_SPARC_JMP_SLOT	21	/* Create PLT entry */
-#define R_SPARC_RELATIVE	22	/* Adjust by program base */
-#define R_SPARC_UA32		23	/* Direct 32 bit unaligned */
-
-/* Additional Sparc64 relocs.  */
-
-#define R_SPARC_PLT32		24	/* Direct 32 bit ref to PLT entry */
-#define R_SPARC_HIPLT22		25	/* High 22 bit PLT entry */
-#define R_SPARC_LOPLT10		26	/* Truncated 10 bit PLT entry */
-#define R_SPARC_PCPLT32		27	/* PC rel 32 bit ref to PLT entry */
-#define R_SPARC_PCPLT22		28	/* PC rel high 22 bit PLT entry */
-#define R_SPARC_PCPLT10		29	/* PC rel trunc 10 bit PLT entry */
-#define R_SPARC_10		30	/* Direct 10 bit */
-#define R_SPARC_11		31	/* Direct 11 bit */
-#define R_SPARC_64		32	/* Direct 64 bit */
-#define R_SPARC_OLO10		33	/* 10bit with secondary 13bit addend */
-#define R_SPARC_HH22		34	/* Top 22 bits of direct 64 bit */
-#define R_SPARC_HM10		35	/* High middle 10 bits of ... */
-#define R_SPARC_LM22		36	/* Low middle 22 bits of ... */
-#define R_SPARC_PC_HH22		37	/* Top 22 bits of pc rel 64 bit */
-#define R_SPARC_PC_HM10		38	/* High middle 10 bit of ... */
-#define R_SPARC_PC_LM22		39	/* Low middle 22 bits of ... */
-#define R_SPARC_WDISP16		40	/* PC relative 16 bit shifted */
-#define R_SPARC_WDISP19		41	/* PC relative 19 bit shifted */
-#define R_SPARC_GLOB_JMP	42	/* was part of v9 ABI but was removed */
-#define R_SPARC_7		43	/* Direct 7 bit */
-#define R_SPARC_5		44	/* Direct 5 bit */
-#define R_SPARC_6		45	/* Direct 6 bit */
-#define R_SPARC_DISP64		46	/* PC relative 64 bit */
-#define R_SPARC_PLT64		47	/* Direct 64 bit ref to PLT entry */
-#define R_SPARC_HIX22		48	/* High 22 bit complemented */
-#define R_SPARC_LOX10		49	/* Truncated 11 bit complemented */
-#define R_SPARC_H44		50	/* Direct high 12 of 44 bit */
-#define R_SPARC_M44		51	/* Direct mid 22 of 44 bit */
-#define R_SPARC_L44		52	/* Direct low 10 of 44 bit */
-#define R_SPARC_REGISTER	53	/* Global register usage */
-#define R_SPARC_UA64		54	/* Direct 64 bit unaligned */
-#define R_SPARC_UA16		55	/* Direct 16 bit unaligned */
-#define R_SPARC_TLS_GD_HI22	56
-#define R_SPARC_TLS_GD_LO10	57
-#define R_SPARC_TLS_GD_ADD	58
-#define R_SPARC_TLS_GD_CALL	59
-#define R_SPARC_TLS_LDM_HI22	60
-#define R_SPARC_TLS_LDM_LO10	61
-#define R_SPARC_TLS_LDM_ADD	62
-#define R_SPARC_TLS_LDM_CALL	63
-#define R_SPARC_TLS_LDO_HIX22	64
-#define R_SPARC_TLS_LDO_LOX10	65
-#define R_SPARC_TLS_LDO_ADD	66
-#define R_SPARC_TLS_IE_HI22	67
-#define R_SPARC_TLS_IE_LO10	68
-#define R_SPARC_TLS_IE_LD	69
-#define R_SPARC_TLS_IE_LDX	70
-#define R_SPARC_TLS_IE_ADD	71
-#define R_SPARC_TLS_LE_HIX22	72
-#define R_SPARC_TLS_LE_LOX10	73
-#define R_SPARC_TLS_DTPMOD32	74
-#define R_SPARC_TLS_DTPMOD64	75
-#define R_SPARC_TLS_DTPOFF32	76
-#define R_SPARC_TLS_DTPOFF64	77
-#define R_SPARC_TLS_TPOFF32	78
-#define R_SPARC_TLS_TPOFF64	79
-#define R_SPARC_GOTDATA_HIX22	80
-#define R_SPARC_GOTDATA_LOX10	81
-#define R_SPARC_GOTDATA_OP_HIX22	82
-#define R_SPARC_GOTDATA_OP_LOX10	83
-#define R_SPARC_GOTDATA_OP	84
-#define R_SPARC_H34		85
-#define R_SPARC_SIZE32		86
-#define R_SPARC_SIZE64		87
-#define R_SPARC_WDISP10		88
-#define R_SPARC_JMP_IREL	248
-#define R_SPARC_IRELATIVE	249
-#define R_SPARC_GNU_VTINHERIT	250
-#define R_SPARC_GNU_VTENTRY	251
-#define R_SPARC_REV32		252
-/* Keep this the last entry.  */
-#define R_SPARC_NUM		253
-
-/* For Sparc64, legal values for d_tag of Elf64_Dyn.  */
-
-#define DT_SPARC_REGISTER 0x70000001
-#define DT_SPARC_NUM	2
-
-/* MIPS R3000 specific definitions.  */
-
-/* Legal values for e_flags field of Elf32_Ehdr.  */
-
-#define EF_MIPS_NOREORDER   1		/* A .noreorder directive was used */
-#define EF_MIPS_PIC	    2		/* Contains PIC code */
-#define EF_MIPS_CPIC	    4		/* Uses PIC calling sequence */
-#define EF_MIPS_XGOT	    8
-#define EF_MIPS_64BIT_WHIRL 16
-#define EF_MIPS_ABI2	    32
-#define EF_MIPS_ABI_ON32    64
-#define EF_MIPS_ARCH	    0xf0000000	/* MIPS architecture level */
-
-/* Legal values for MIPS architecture level.  */
-
-#define EF_MIPS_ARCH_1	    0x00000000	/* -mips1 code.  */
-#define EF_MIPS_ARCH_2	    0x10000000	/* -mips2 code.  */
-#define EF_MIPS_ARCH_3	    0x20000000	/* -mips3 code.  */
-#define EF_MIPS_ARCH_4	    0x30000000	/* -mips4 code.  */
-#define EF_MIPS_ARCH_5	    0x40000000	/* -mips5 code.  */
-#define EF_MIPS_ARCH_32	    0x60000000	/* MIPS32 code.  */
-#define EF_MIPS_ARCH_64	    0x70000000	/* MIPS64 code.  */
-
-/* The following are non-official names and should not be used.  */
-
-#define E_MIPS_ARCH_1	  0x00000000	/* -mips1 code.  */
-#define E_MIPS_ARCH_2	  0x10000000	/* -mips2 code.  */
-#define E_MIPS_ARCH_3	  0x20000000	/* -mips3 code.  */
-#define E_MIPS_ARCH_4	  0x30000000	/* -mips4 code.  */
-#define E_MIPS_ARCH_5	  0x40000000	/* -mips5 code.  */
-#define E_MIPS_ARCH_32	  0x60000000	/* MIPS32 code.  */
-#define E_MIPS_ARCH_64	  0x70000000	/* MIPS64 code.  */
-
-/* Special section indices.  */
-
-#define SHN_MIPS_ACOMMON    0xff00	/* Allocated common symbols */
-#define SHN_MIPS_TEXT	    0xff01	/* Allocated test symbols.  */
-#define SHN_MIPS_DATA	    0xff02	/* Allocated data symbols.  */
-#define SHN_MIPS_SCOMMON    0xff03	/* Small common symbols */
-#define SHN_MIPS_SUNDEFINED 0xff04	/* Small undefined symbols */
-
-/* Legal values for sh_type field of Elf32_Shdr.  */
-
-#define SHT_MIPS_LIBLIST       0x70000000 /* Shared objects used in link */
-#define SHT_MIPS_MSYM	       0x70000001
-#define SHT_MIPS_CONFLICT      0x70000002 /* Conflicting symbols */
-#define SHT_MIPS_GPTAB	       0x70000003 /* Global data area sizes */
-#define SHT_MIPS_UCODE	       0x70000004 /* Reserved for SGI/MIPS compilers */
-#define SHT_MIPS_DEBUG	       0x70000005 /* MIPS ECOFF debugging information*/
-#define SHT_MIPS_REGINFO       0x70000006 /* Register usage information */
-#define SHT_MIPS_PACKAGE       0x70000007
-#define SHT_MIPS_PACKSYM       0x70000008
-#define SHT_MIPS_RELD	       0x70000009
-#define SHT_MIPS_IFACE         0x7000000b
-#define SHT_MIPS_CONTENT       0x7000000c
-#define SHT_MIPS_OPTIONS       0x7000000d /* Miscellaneous options.  */
-#define SHT_MIPS_SHDR	       0x70000010
-#define SHT_MIPS_FDESC	       0x70000011
-#define SHT_MIPS_EXTSYM	       0x70000012
-#define SHT_MIPS_DENSE	       0x70000013
-#define SHT_MIPS_PDESC	       0x70000014
-#define SHT_MIPS_LOCSYM	       0x70000015
-#define SHT_MIPS_AUXSYM	       0x70000016
-#define SHT_MIPS_OPTSYM	       0x70000017
-#define SHT_MIPS_LOCSTR	       0x70000018
-#define SHT_MIPS_LINE	       0x70000019
-#define SHT_MIPS_RFDESC	       0x7000001a
-#define SHT_MIPS_DELTASYM      0x7000001b
-#define SHT_MIPS_DELTAINST     0x7000001c
-#define SHT_MIPS_DELTACLASS    0x7000001d
-#define SHT_MIPS_DWARF         0x7000001e /* DWARF debugging information.  */
-#define SHT_MIPS_DELTADECL     0x7000001f
-#define SHT_MIPS_SYMBOL_LIB    0x70000020
-#define SHT_MIPS_EVENTS	       0x70000021 /* Event section.  */
-#define SHT_MIPS_TRANSLATE     0x70000022
-#define SHT_MIPS_PIXIE	       0x70000023
-#define SHT_MIPS_XLATE	       0x70000024
-#define SHT_MIPS_XLATE_DEBUG   0x70000025
-#define SHT_MIPS_WHIRL	       0x70000026
-#define SHT_MIPS_EH_REGION     0x70000027
-#define SHT_MIPS_XLATE_OLD     0x70000028
-#define SHT_MIPS_PDR_EXCEPTION 0x70000029
-
-/* Legal values for sh_flags field of Elf32_Shdr.  */
-
-#define SHF_MIPS_GPREL	 0x10000000	/* Must be part of global data area */
-#define SHF_MIPS_MERGE	 0x20000000
-#define SHF_MIPS_ADDR	 0x40000000
-#define SHF_MIPS_STRINGS 0x80000000
-#define SHF_MIPS_NOSTRIP 0x08000000
-#define SHF_MIPS_LOCAL	 0x04000000
-#define SHF_MIPS_NAMES	 0x02000000
-#define SHF_MIPS_NODUPE	 0x01000000
-
-
-/* Symbol tables.  */
-
-/* MIPS specific values for `st_other'.  */
-#define STO_MIPS_DEFAULT		0x0
-#define STO_MIPS_INTERNAL		0x1
-#define STO_MIPS_HIDDEN			0x2
-#define STO_MIPS_PROTECTED		0x3
-#define STO_MIPS_PLT			0x8
-#define STO_MIPS_SC_ALIGN_UNUSED	0xff
-
-/* MIPS specific values for `st_info'.  */
-#define STB_MIPS_SPLIT_COMMON		13
-
-/* Entries found in sections of type SHT_MIPS_GPTAB.  */
-
-typedef union
-{
-  struct
-    {
-      Elf32_Word gt_current_g_value;	/* -G value used for compilation */
-      Elf32_Word gt_unused;		/* Not used */
-    } gt_header;			/* First entry in section */
-  struct
-    {
-      Elf32_Word gt_g_value;		/* If this value were used for -G */
-      Elf32_Word gt_bytes;		/* This many bytes would be used */
-    } gt_entry;				/* Subsequent entries in section */
-} Elf32_gptab;
-
-/* Entry found in sections of type SHT_MIPS_REGINFO.  */
-
-typedef struct
-{
-  Elf32_Word	ri_gprmask;		/* General registers used */
-  Elf32_Word	ri_cprmask[4];		/* Coprocessor registers used */
-  Elf32_Sword	ri_gp_value;		/* $gp register value */
-} Elf32_RegInfo;
-
-/* Entries found in sections of type SHT_MIPS_OPTIONS.  */
-
-typedef struct
-{
-  unsigned char kind;		/* Determines interpretation of the
-				   variable part of descriptor.  */
-  unsigned char size;		/* Size of descriptor, including header.  */
-  Elf32_Section section;	/* Section header index of section affected,
-				   0 for global options.  */
-  Elf32_Word info;		/* Kind-specific information.  */
-} Elf_Options;
-
-/* Values for `kind' field in Elf_Options.  */
-
-#define ODK_NULL	0	/* Undefined.  */
-#define ODK_REGINFO	1	/* Register usage information.  */
-#define ODK_EXCEPTIONS	2	/* Exception processing options.  */
-#define ODK_PAD		3	/* Section padding options.  */
-#define ODK_HWPATCH	4	/* Hardware workarounds performed */
-#define ODK_FILL	5	/* record the fill value used by the linker. */
-#define ODK_TAGS	6	/* reserve space for desktop tools to write. */
-#define ODK_HWAND	7	/* HW workarounds.  'AND' bits when merging. */
-#define ODK_HWOR	8	/* HW workarounds.  'OR' bits when merging.  */
-
-/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries.  */
-
-#define OEX_FPU_MIN	0x1f	/* FPE's which MUST be enabled.  */
-#define OEX_FPU_MAX	0x1f00	/* FPE's which MAY be enabled.  */
-#define OEX_PAGE0	0x10000	/* page zero must be mapped.  */
-#define OEX_SMM		0x20000	/* Force sequential memory mode?  */
-#define OEX_FPDBUG	0x40000	/* Force floating point debug mode?  */
-#define OEX_PRECISEFP	OEX_FPDBUG
-#define OEX_DISMISS	0x80000	/* Dismiss invalid address faults?  */
-
-#define OEX_FPU_INVAL	0x10
-#define OEX_FPU_DIV0	0x08
-#define OEX_FPU_OFLO	0x04
-#define OEX_FPU_UFLO	0x02
-#define OEX_FPU_INEX	0x01
-
-/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry.  */
-
-#define OHW_R4KEOP	0x1	/* R4000 end-of-page patch.  */
-#define OHW_R8KPFETCH	0x2	/* may need R8000 prefetch patch.  */
-#define OHW_R5KEOP	0x4	/* R5000 end-of-page patch.  */
-#define OHW_R5KCVTL	0x8	/* R5000 cvt.[ds].l bug.  clean=1.  */
-
-#define OPAD_PREFIX	0x1
-#define OPAD_POSTFIX	0x2
-#define OPAD_SYMBOL	0x4
-
-/* Entry found in `.options' section.  */
-
-typedef struct
-{
-  Elf32_Word hwp_flags1;	/* Extra flags.  */
-  Elf32_Word hwp_flags2;	/* Extra flags.  */
-} Elf_Options_Hw;
-
-/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries.  */
-
-#define OHWA0_R4KEOP_CHECKED	0x00000001
-#define OHWA1_R4KEOP_CLEAN	0x00000002
-
-/* MIPS relocs.  */
-
-#define R_MIPS_NONE		0	/* No reloc */
-#define R_MIPS_16		1	/* Direct 16 bit */
-#define R_MIPS_32		2	/* Direct 32 bit */
-#define R_MIPS_REL32		3	/* PC relative 32 bit */
-#define R_MIPS_26		4	/* Direct 26 bit shifted */
-#define R_MIPS_HI16		5	/* High 16 bit */
-#define R_MIPS_LO16		6	/* Low 16 bit */
-#define R_MIPS_GPREL16		7	/* GP relative 16 bit */
-#define R_MIPS_LITERAL		8	/* 16 bit literal entry */
-#define R_MIPS_GOT16		9	/* 16 bit GOT entry */
-#define R_MIPS_PC16		10	/* PC relative 16 bit */
-#define R_MIPS_CALL16		11	/* 16 bit GOT entry for function */
-#define R_MIPS_GPREL32		12	/* GP relative 32 bit */
-
-#define R_MIPS_SHIFT5		16
-#define R_MIPS_SHIFT6		17
-#define R_MIPS_64		18
-#define R_MIPS_GOT_DISP		19
-#define R_MIPS_GOT_PAGE		20
-#define R_MIPS_GOT_OFST		21
-#define R_MIPS_GOT_HI16		22
-#define R_MIPS_GOT_LO16		23
-#define R_MIPS_SUB		24
-#define R_MIPS_INSERT_A		25
-#define R_MIPS_INSERT_B		26
-#define R_MIPS_DELETE		27
-#define R_MIPS_HIGHER		28
-#define R_MIPS_HIGHEST		29
-#define R_MIPS_CALL_HI16	30
-#define R_MIPS_CALL_LO16	31
-#define R_MIPS_SCN_DISP		32
-#define R_MIPS_REL16		33
-#define R_MIPS_ADD_IMMEDIATE	34
-#define R_MIPS_PJUMP		35
-#define R_MIPS_RELGOT		36
-#define R_MIPS_JALR		37
-#define R_MIPS_TLS_DTPMOD32	38	/* Module number 32 bit */
-#define R_MIPS_TLS_DTPREL32	39	/* Module-relative offset 32 bit */
-#define R_MIPS_TLS_DTPMOD64	40	/* Module number 64 bit */
-#define R_MIPS_TLS_DTPREL64	41	/* Module-relative offset 64 bit */
-#define R_MIPS_TLS_GD		42	/* 16 bit GOT offset for GD */
-#define R_MIPS_TLS_LDM		43	/* 16 bit GOT offset for LDM */
-#define R_MIPS_TLS_DTPREL_HI16	44	/* Module-relative offset, high 16 bits */
-#define R_MIPS_TLS_DTPREL_LO16	45	/* Module-relative offset, low 16 bits */
-#define R_MIPS_TLS_GOTTPREL	46	/* 16 bit GOT offset for IE */
-#define R_MIPS_TLS_TPREL32	47	/* TP-relative offset, 32 bit */
-#define R_MIPS_TLS_TPREL64	48	/* TP-relative offset, 64 bit */
-#define R_MIPS_TLS_TPREL_HI16	49	/* TP-relative offset, high 16 bits */
-#define R_MIPS_TLS_TPREL_LO16	50	/* TP-relative offset, low 16 bits */
-#define R_MIPS_GLOB_DAT		51
-#define R_MIPS_COPY		126
-#define R_MIPS_JUMP_SLOT        127
-/* Keep this the last entry.  */
-#define R_MIPS_NUM		128
-
-/* Legal values for p_type field of Elf32_Phdr.  */
-
-#define PT_MIPS_REGINFO	0x70000000	/* Register usage information */
-#define PT_MIPS_RTPROC  0x70000001	/* Runtime procedure table. */
-#define PT_MIPS_OPTIONS 0x70000002
-
-/* Special program header types.  */
-
-#define PF_MIPS_LOCAL	0x10000000
-
-/* Legal values for d_tag field of Elf32_Dyn.  */
-
-#define DT_MIPS_RLD_VERSION  0x70000001	/* Runtime linker interface version */
-#define DT_MIPS_TIME_STAMP   0x70000002	/* Timestamp */
-#define DT_MIPS_ICHECKSUM    0x70000003	/* Checksum */
-#define DT_MIPS_IVERSION     0x70000004	/* Version string (string tbl index) */
-#define DT_MIPS_FLAGS	     0x70000005	/* Flags */
-#define DT_MIPS_BASE_ADDRESS 0x70000006	/* Base address */
-#define DT_MIPS_MSYM	     0x70000007
-#define DT_MIPS_CONFLICT     0x70000008	/* Address of CONFLICT section */
-#define DT_MIPS_LIBLIST	     0x70000009	/* Address of LIBLIST section */
-#define DT_MIPS_LOCAL_GOTNO  0x7000000a	/* Number of local GOT entries */
-#define DT_MIPS_CONFLICTNO   0x7000000b	/* Number of CONFLICT entries */
-#define DT_MIPS_LIBLISTNO    0x70000010	/* Number of LIBLIST entries */
-#define DT_MIPS_SYMTABNO     0x70000011	/* Number of DYNSYM entries */
-#define DT_MIPS_UNREFEXTNO   0x70000012	/* First external DYNSYM */
-#define DT_MIPS_GOTSYM	     0x70000013	/* First GOT entry in DYNSYM */
-#define DT_MIPS_HIPAGENO     0x70000014	/* Number of GOT page table entries */
-#define DT_MIPS_RLD_MAP	     0x70000016	/* Address of run time loader map.  */
-#define DT_MIPS_DELTA_CLASS  0x70000017	/* Delta C++ class definition.  */
-#define DT_MIPS_DELTA_CLASS_NO    0x70000018 /* Number of entries in
-						DT_MIPS_DELTA_CLASS.  */
-#define DT_MIPS_DELTA_INSTANCE    0x70000019 /* Delta C++ class instances.  */
-#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in
-						DT_MIPS_DELTA_INSTANCE.  */
-#define DT_MIPS_DELTA_RELOC  0x7000001b /* Delta relocations.  */
-#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in
-					     DT_MIPS_DELTA_RELOC.  */
-#define DT_MIPS_DELTA_SYM    0x7000001d /* Delta symbols that Delta
-					   relocations refer to.  */
-#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in
-					   DT_MIPS_DELTA_SYM.  */
-#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the
-					     class declaration.  */
-#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in
-						DT_MIPS_DELTA_CLASSSYM.  */
-#define DT_MIPS_CXX_FLAGS    0x70000022 /* Flags indicating for C++ flavor.  */
-#define DT_MIPS_PIXIE_INIT   0x70000023
-#define DT_MIPS_SYMBOL_LIB   0x70000024
-#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025
-#define DT_MIPS_LOCAL_GOTIDX 0x70000026
-#define DT_MIPS_HIDDEN_GOTIDX 0x70000027
-#define DT_MIPS_PROTECTED_GOTIDX 0x70000028
-#define DT_MIPS_OPTIONS	     0x70000029 /* Address of .options.  */
-#define DT_MIPS_INTERFACE    0x7000002a /* Address of .interface.  */
-#define DT_MIPS_DYNSTR_ALIGN 0x7000002b
-#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */
-#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve
-						    function stored in GOT.  */
-#define DT_MIPS_PERF_SUFFIX  0x7000002e /* Default suffix of dso to be added
-					   by rld on dlopen() calls.  */
-#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */
-#define DT_MIPS_GP_VALUE     0x70000030 /* GP value for aux GOTs.  */
-#define DT_MIPS_AUX_DYNAMIC  0x70000031 /* Address of aux .dynamic.  */
-/* The address of .got.plt in an executable using the new non-PIC ABI.  */
-#define DT_MIPS_PLTGOT	     0x70000032
-/* The base of the PLT in an executable using the new non-PIC ABI if that
-   PLT is writable.  For a non-writable PLT, this is omitted or has a zero
-   value.  */
-#define DT_MIPS_RWPLT        0x70000034
-#define DT_MIPS_NUM	     0x35
-
-/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry.  */
-
-#define RHF_NONE		   0		/* No flags */
-#define RHF_QUICKSTART		   (1 << 0)	/* Use quickstart */
-#define RHF_NOTPOT		   (1 << 1)	/* Hash size not power of 2 */
-#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2)	/* Ignore LD_LIBRARY_PATH */
-#define RHF_NO_MOVE		   (1 << 3)
-#define RHF_SGI_ONLY		   (1 << 4)
-#define RHF_GUARANTEE_INIT	   (1 << 5)
-#define RHF_DELTA_C_PLUS_PLUS	   (1 << 6)
-#define RHF_GUARANTEE_START_INIT   (1 << 7)
-#define RHF_PIXIE		   (1 << 8)
-#define RHF_DEFAULT_DELAY_LOAD	   (1 << 9)
-#define RHF_REQUICKSTART	   (1 << 10)
-#define RHF_REQUICKSTARTED	   (1 << 11)
-#define RHF_CORD		   (1 << 12)
-#define RHF_NO_UNRES_UNDEF	   (1 << 13)
-#define RHF_RLD_ORDER_SAFE	   (1 << 14)
-
-/* Entries found in sections of type SHT_MIPS_LIBLIST.  */
-
-typedef struct
-{
-  Elf32_Word l_name;		/* Name (string table index) */
-  Elf32_Word l_time_stamp;	/* Timestamp */
-  Elf32_Word l_checksum;	/* Checksum */
-  Elf32_Word l_version;		/* Interface version */
-  Elf32_Word l_flags;		/* Flags */
-} Elf32_Lib;
-
-typedef struct
-{
-  Elf64_Word l_name;		/* Name (string table index) */
-  Elf64_Word l_time_stamp;	/* Timestamp */
-  Elf64_Word l_checksum;	/* Checksum */
-  Elf64_Word l_version;		/* Interface version */
-  Elf64_Word l_flags;		/* Flags */
-} Elf64_Lib;
-
-
-/* Legal values for l_flags.  */
-
-#define LL_NONE		  0
-#define LL_EXACT_MATCH	  (1 << 0)	/* Require exact match */
-#define LL_IGNORE_INT_VER (1 << 1)	/* Ignore interface version */
-#define LL_REQUIRE_MINOR  (1 << 2)
-#define LL_EXPORTS	  (1 << 3)
-#define LL_DELAY_LOAD	  (1 << 4)
-#define LL_DELTA	  (1 << 5)
-
-/* Entries found in sections of type SHT_MIPS_CONFLICT.  */
-
-typedef Elf32_Addr Elf32_Conflict;
-
-
-/* HPPA specific definitions.  */
-
-/* Legal values for e_flags field of Elf32_Ehdr.  */
-
-#define EF_PARISC_TRAPNIL	0x00010000 /* Trap nil pointer dereference.  */
-#define EF_PARISC_EXT		0x00020000 /* Program uses arch. extensions. */
-#define EF_PARISC_LSB		0x00040000 /* Program expects little endian. */
-#define EF_PARISC_WIDE		0x00080000 /* Program expects wide mode.  */
-#define EF_PARISC_NO_KABP	0x00100000 /* No kernel assisted branch
-					      prediction.  */
-#define EF_PARISC_LAZYSWAP	0x00400000 /* Allow lazy swapping.  */
-#define EF_PARISC_ARCH		0x0000ffff /* Architecture version.  */
-
-/* Defined values for `e_flags & EF_PARISC_ARCH' are:  */
-
-#define EFA_PARISC_1_0		    0x020b /* PA-RISC 1.0 big-endian.  */
-#define EFA_PARISC_1_1		    0x0210 /* PA-RISC 1.1 big-endian.  */
-#define EFA_PARISC_2_0		    0x0214 /* PA-RISC 2.0 big-endian.  */
-
-/* Additional section indices.  */
-
-#define SHN_PARISC_ANSI_COMMON	0xff00	   /* Section for tentatively declared
-					      symbols in ANSI C.  */
-#define SHN_PARISC_HUGE_COMMON	0xff01	   /* Common blocks in huge model.  */
-
-/* Legal values for sh_type field of Elf32_Shdr.  */
-
-#define SHT_PARISC_EXT		0x70000000 /* Contains product specific ext. */
-#define SHT_PARISC_UNWIND	0x70000001 /* Unwind information.  */
-#define SHT_PARISC_DOC		0x70000002 /* Debug info for optimized code. */
-
-/* Legal values for sh_flags field of Elf32_Shdr.  */
-
-#define SHF_PARISC_SHORT	0x20000000 /* Section with short addressing. */
-#define SHF_PARISC_HUGE		0x40000000 /* Section far from gp.  */
-#define SHF_PARISC_SBP		0x80000000 /* Static branch prediction code. */
-
-/* Legal values for ST_TYPE subfield of st_info (symbol type).  */
-
-#define STT_PARISC_MILLICODE	13	/* Millicode function entry point.  */
-
-#define STT_HP_OPAQUE		(STT_LOOS + 0x1)
-#define STT_HP_STUB		(STT_LOOS + 0x2)
-
-/* HPPA relocs.  */
-
-#define R_PARISC_NONE		0	/* No reloc.  */
-#define R_PARISC_DIR32		1	/* Direct 32-bit reference.  */
-#define R_PARISC_DIR21L		2	/* Left 21 bits of eff. address.  */
-#define R_PARISC_DIR17R		3	/* Right 17 bits of eff. address.  */
-#define R_PARISC_DIR17F		4	/* 17 bits of eff. address.  */
-#define R_PARISC_DIR14R		6	/* Right 14 bits of eff. address.  */
-#define R_PARISC_PCREL32	9	/* 32-bit rel. address.  */
-#define R_PARISC_PCREL21L	10	/* Left 21 bits of rel. address.  */
-#define R_PARISC_PCREL17R	11	/* Right 17 bits of rel. address.  */
-#define R_PARISC_PCREL17F	12	/* 17 bits of rel. address.  */
-#define R_PARISC_PCREL14R	14	/* Right 14 bits of rel. address.  */
-#define R_PARISC_DPREL21L	18	/* Left 21 bits of rel. address.  */
-#define R_PARISC_DPREL14R	22	/* Right 14 bits of rel. address.  */
-#define R_PARISC_GPREL21L	26	/* GP-relative, left 21 bits.  */
-#define R_PARISC_GPREL14R	30	/* GP-relative, right 14 bits.  */
-#define R_PARISC_LTOFF21L	34	/* LT-relative, left 21 bits.  */
-#define R_PARISC_LTOFF14R	38	/* LT-relative, right 14 bits.  */
-#define R_PARISC_SECREL32	41	/* 32 bits section rel. address.  */
-#define R_PARISC_SEGBASE	48	/* No relocation, set segment base.  */
-#define R_PARISC_SEGREL32	49	/* 32 bits segment rel. address.  */
-#define R_PARISC_PLTOFF21L	50	/* PLT rel. address, left 21 bits.  */
-#define R_PARISC_PLTOFF14R	54	/* PLT rel. address, right 14 bits.  */
-#define R_PARISC_LTOFF_FPTR32	57	/* 32 bits LT-rel. function pointer. */
-#define R_PARISC_LTOFF_FPTR21L	58	/* LT-rel. fct ptr, left 21 bits. */
-#define R_PARISC_LTOFF_FPTR14R	62	/* LT-rel. fct ptr, right 14 bits. */
-#define R_PARISC_FPTR64		64	/* 64 bits function address.  */
-#define R_PARISC_PLABEL32	65	/* 32 bits function address.  */
-#define R_PARISC_PLABEL21L	66	/* Left 21 bits of fdesc address.  */
-#define R_PARISC_PLABEL14R	70	/* Right 14 bits of fdesc address.  */
-#define R_PARISC_PCREL64	72	/* 64 bits PC-rel. address.  */
-#define R_PARISC_PCREL22F	74	/* 22 bits PC-rel. address.  */
-#define R_PARISC_PCREL14WR	75	/* PC-rel. address, right 14 bits.  */
-#define R_PARISC_PCREL14DR	76	/* PC rel. address, right 14 bits.  */
-#define R_PARISC_PCREL16F	77	/* 16 bits PC-rel. address.  */
-#define R_PARISC_PCREL16WF	78	/* 16 bits PC-rel. address.  */
-#define R_PARISC_PCREL16DF	79	/* 16 bits PC-rel. address.  */
-#define R_PARISC_DIR64		80	/* 64 bits of eff. address.  */
-#define R_PARISC_DIR14WR	83	/* 14 bits of eff. address.  */
-#define R_PARISC_DIR14DR	84	/* 14 bits of eff. address.  */
-#define R_PARISC_DIR16F		85	/* 16 bits of eff. address.  */
-#define R_PARISC_DIR16WF	86	/* 16 bits of eff. address.  */
-#define R_PARISC_DIR16DF	87	/* 16 bits of eff. address.  */
-#define R_PARISC_GPREL64	88	/* 64 bits of GP-rel. address.  */
-#define R_PARISC_GPREL14WR	91	/* GP-rel. address, right 14 bits.  */
-#define R_PARISC_GPREL14DR	92	/* GP-rel. address, right 14 bits.  */
-#define R_PARISC_GPREL16F	93	/* 16 bits GP-rel. address.  */
-#define R_PARISC_GPREL16WF	94	/* 16 bits GP-rel. address.  */
-#define R_PARISC_GPREL16DF	95	/* 16 bits GP-rel. address.  */
-#define R_PARISC_LTOFF64	96	/* 64 bits LT-rel. address.  */
-#define R_PARISC_LTOFF14WR	99	/* LT-rel. address, right 14 bits.  */
-#define R_PARISC_LTOFF14DR	100	/* LT-rel. address, right 14 bits.  */
-#define R_PARISC_LTOFF16F	101	/* 16 bits LT-rel. address.  */
-#define R_PARISC_LTOFF16WF	102	/* 16 bits LT-rel. address.  */
-#define R_PARISC_LTOFF16DF	103	/* 16 bits LT-rel. address.  */
-#define R_PARISC_SECREL64	104	/* 64 bits section rel. address.  */
-#define R_PARISC_SEGREL64	112	/* 64 bits segment rel. address.  */
-#define R_PARISC_PLTOFF14WR	115	/* PLT-rel. address, right 14 bits.  */
-#define R_PARISC_PLTOFF14DR	116	/* PLT-rel. address, right 14 bits.  */
-#define R_PARISC_PLTOFF16F	117	/* 16 bits LT-rel. address.  */
-#define R_PARISC_PLTOFF16WF	118	/* 16 bits PLT-rel. address.  */
-#define R_PARISC_PLTOFF16DF	119	/* 16 bits PLT-rel. address.  */
-#define R_PARISC_LTOFF_FPTR64	120	/* 64 bits LT-rel. function ptr.  */
-#define R_PARISC_LTOFF_FPTR14WR	123	/* LT-rel. fct. ptr., right 14 bits. */
-#define R_PARISC_LTOFF_FPTR14DR	124	/* LT-rel. fct. ptr., right 14 bits. */
-#define R_PARISC_LTOFF_FPTR16F	125	/* 16 bits LT-rel. function ptr.  */
-#define R_PARISC_LTOFF_FPTR16WF	126	/* 16 bits LT-rel. function ptr.  */
-#define R_PARISC_LTOFF_FPTR16DF	127	/* 16 bits LT-rel. function ptr.  */
-#define R_PARISC_LORESERVE	128
-#define R_PARISC_COPY		128	/* Copy relocation.  */
-#define R_PARISC_IPLT		129	/* Dynamic reloc, imported PLT */
-#define R_PARISC_EPLT		130	/* Dynamic reloc, exported PLT */
-#define R_PARISC_TPREL32	153	/* 32 bits TP-rel. address.  */
-#define R_PARISC_TPREL21L	154	/* TP-rel. address, left 21 bits.  */
-#define R_PARISC_TPREL14R	158	/* TP-rel. address, right 14 bits.  */
-#define R_PARISC_LTOFF_TP21L	162	/* LT-TP-rel. address, left 21 bits. */
-#define R_PARISC_LTOFF_TP14R	166	/* LT-TP-rel. address, right 14 bits.*/
-#define R_PARISC_LTOFF_TP14F	167	/* 14 bits LT-TP-rel. address.  */
-#define R_PARISC_TPREL64	216	/* 64 bits TP-rel. address.  */
-#define R_PARISC_TPREL14WR	219	/* TP-rel. address, right 14 bits.  */
-#define R_PARISC_TPREL14DR	220	/* TP-rel. address, right 14 bits.  */
-#define R_PARISC_TPREL16F	221	/* 16 bits TP-rel. address.  */
-#define R_PARISC_TPREL16WF	222	/* 16 bits TP-rel. address.  */
-#define R_PARISC_TPREL16DF	223	/* 16 bits TP-rel. address.  */
-#define R_PARISC_LTOFF_TP64	224	/* 64 bits LT-TP-rel. address.  */
-#define R_PARISC_LTOFF_TP14WR	227	/* LT-TP-rel. address, right 14 bits.*/
-#define R_PARISC_LTOFF_TP14DR	228	/* LT-TP-rel. address, right 14 bits.*/
-#define R_PARISC_LTOFF_TP16F	229	/* 16 bits LT-TP-rel. address.  */
-#define R_PARISC_LTOFF_TP16WF	230	/* 16 bits LT-TP-rel. address.  */
-#define R_PARISC_LTOFF_TP16DF	231	/* 16 bits LT-TP-rel. address.  */
-#define R_PARISC_GNU_VTENTRY	232
-#define R_PARISC_GNU_VTINHERIT	233
-#define R_PARISC_TLS_GD21L	234	/* GD 21-bit left.  */
-#define R_PARISC_TLS_GD14R	235	/* GD 14-bit right.  */
-#define R_PARISC_TLS_GDCALL	236	/* GD call to __t_g_a.  */
-#define R_PARISC_TLS_LDM21L	237	/* LD module 21-bit left.  */
-#define R_PARISC_TLS_LDM14R	238	/* LD module 14-bit right.  */
-#define R_PARISC_TLS_LDMCALL	239	/* LD module call to __t_g_a.  */
-#define R_PARISC_TLS_LDO21L	240	/* LD offset 21-bit left.  */
-#define R_PARISC_TLS_LDO14R	241	/* LD offset 14-bit right.  */
-#define R_PARISC_TLS_DTPMOD32	242	/* DTP module 32-bit.  */
-#define R_PARISC_TLS_DTPMOD64	243	/* DTP module 64-bit.  */
-#define R_PARISC_TLS_DTPOFF32	244	/* DTP offset 32-bit.  */
-#define R_PARISC_TLS_DTPOFF64	245	/* DTP offset 32-bit.  */
-#define R_PARISC_TLS_LE21L	R_PARISC_TPREL21L
-#define R_PARISC_TLS_LE14R	R_PARISC_TPREL14R
-#define R_PARISC_TLS_IE21L	R_PARISC_LTOFF_TP21L
-#define R_PARISC_TLS_IE14R	R_PARISC_LTOFF_TP14R
-#define R_PARISC_TLS_TPREL32	R_PARISC_TPREL32
-#define R_PARISC_TLS_TPREL64	R_PARISC_TPREL64
-#define R_PARISC_HIRESERVE	255
-
-/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr.  */
-
-#define PT_HP_TLS		(PT_LOOS + 0x0)
-#define PT_HP_CORE_NONE		(PT_LOOS + 0x1)
-#define PT_HP_CORE_VERSION	(PT_LOOS + 0x2)
-#define PT_HP_CORE_KERNEL	(PT_LOOS + 0x3)
-#define PT_HP_CORE_COMM		(PT_LOOS + 0x4)
-#define PT_HP_CORE_PROC		(PT_LOOS + 0x5)
-#define PT_HP_CORE_LOADABLE	(PT_LOOS + 0x6)
-#define PT_HP_CORE_STACK	(PT_LOOS + 0x7)
-#define PT_HP_CORE_SHM		(PT_LOOS + 0x8)
-#define PT_HP_CORE_MMF		(PT_LOOS + 0x9)
-#define PT_HP_PARALLEL		(PT_LOOS + 0x10)
-#define PT_HP_FASTBIND		(PT_LOOS + 0x11)
-#define PT_HP_OPT_ANNOT		(PT_LOOS + 0x12)
-#define PT_HP_HSL_ANNOT		(PT_LOOS + 0x13)
-#define PT_HP_STACK		(PT_LOOS + 0x14)
-
-#define PT_PARISC_ARCHEXT	0x70000000
-#define PT_PARISC_UNWIND	0x70000001
-
-/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr.  */
-
-#define PF_PARISC_SBP		0x08000000
-
-#define PF_HP_PAGE_SIZE		0x00100000
-#define PF_HP_FAR_SHARED	0x00200000
-#define PF_HP_NEAR_SHARED	0x00400000
-#define PF_HP_CODE		0x01000000
-#define PF_HP_MODIFY		0x02000000
-#define PF_HP_LAZYSWAP		0x04000000
-#define PF_HP_SBP		0x08000000
-
-
-/* Alpha specific definitions.  */
-
-/* Legal values for e_flags field of Elf64_Ehdr.  */
-
-#define EF_ALPHA_32BIT		1	/* All addresses must be < 2GB.  */
-#define EF_ALPHA_CANRELAX	2	/* Relocations for relaxing exist.  */
-
-/* Legal values for sh_type field of Elf64_Shdr.  */
-
-/* These two are primarily concerned with ECOFF debugging info.  */
-#define SHT_ALPHA_DEBUG		0x70000001
-#define SHT_ALPHA_REGINFO	0x70000002
-
-/* Legal values for sh_flags field of Elf64_Shdr.  */
-
-#define SHF_ALPHA_GPREL		0x10000000
-
-/* Legal values for st_other field of Elf64_Sym.  */
-#define STO_ALPHA_NOPV		0x80	/* No PV required.  */
-#define STO_ALPHA_STD_GPLOAD	0x88	/* PV only used for initial ldgp.  */
-
-/* Alpha relocs.  */
-
-#define R_ALPHA_NONE		0	/* No reloc */
-#define R_ALPHA_REFLONG		1	/* Direct 32 bit */
-#define R_ALPHA_REFQUAD		2	/* Direct 64 bit */
-#define R_ALPHA_GPREL32		3	/* GP relative 32 bit */
-#define R_ALPHA_LITERAL		4	/* GP relative 16 bit w/optimization */
-#define R_ALPHA_LITUSE		5	/* Optimization hint for LITERAL */
-#define R_ALPHA_GPDISP		6	/* Add displacement to GP */
-#define R_ALPHA_BRADDR		7	/* PC+4 relative 23 bit shifted */
-#define R_ALPHA_HINT		8	/* PC+4 relative 16 bit shifted */
-#define R_ALPHA_SREL16		9	/* PC relative 16 bit */
-#define R_ALPHA_SREL32		10	/* PC relative 32 bit */
-#define R_ALPHA_SREL64		11	/* PC relative 64 bit */
-#define R_ALPHA_GPRELHIGH	17	/* GP relative 32 bit, high 16 bits */
-#define R_ALPHA_GPRELLOW	18	/* GP relative 32 bit, low 16 bits */
-#define R_ALPHA_GPREL16		19	/* GP relative 16 bit */
-#define R_ALPHA_COPY		24	/* Copy symbol at runtime */
-#define R_ALPHA_GLOB_DAT	25	/* Create GOT entry */
-#define R_ALPHA_JMP_SLOT	26	/* Create PLT entry */
-#define R_ALPHA_RELATIVE	27	/* Adjust by program base */
-#define R_ALPHA_TLS_GD_HI	28
-#define R_ALPHA_TLSGD		29
-#define R_ALPHA_TLS_LDM		30
-#define R_ALPHA_DTPMOD64	31
-#define R_ALPHA_GOTDTPREL	32
-#define R_ALPHA_DTPREL64	33
-#define R_ALPHA_DTPRELHI	34
-#define R_ALPHA_DTPRELLO	35
-#define R_ALPHA_DTPREL16	36
-#define R_ALPHA_GOTTPREL	37
-#define R_ALPHA_TPREL64		38
-#define R_ALPHA_TPRELHI		39
-#define R_ALPHA_TPRELLO		40
-#define R_ALPHA_TPREL16		41
-/* Keep this the last entry.  */
-#define R_ALPHA_NUM		46
-
-/* Magic values of the LITUSE relocation addend.  */
-#define LITUSE_ALPHA_ADDR	0
-#define LITUSE_ALPHA_BASE	1
-#define LITUSE_ALPHA_BYTOFF	2
-#define LITUSE_ALPHA_JSR	3
-#define LITUSE_ALPHA_TLS_GD	4
-#define LITUSE_ALPHA_TLS_LDM	5
-
-/* Legal values for d_tag of Elf64_Dyn.  */
-#define DT_ALPHA_PLTRO		(DT_LOPROC + 0)
-#define DT_ALPHA_NUM		1
-
-/* PowerPC specific declarations */
-
-/* Values for Elf32/64_Ehdr.e_flags.  */
-#define EF_PPC_EMB		0x80000000	/* PowerPC embedded flag */
-
-/* Cygnus local bits below */
-#define EF_PPC_RELOCATABLE	0x00010000	/* PowerPC -mrelocatable flag*/
-#define EF_PPC_RELOCATABLE_LIB	0x00008000	/* PowerPC -mrelocatable-lib
-						   flag */
-
-/* PowerPC relocations defined by the ABIs */
-#define R_PPC_NONE		0
-#define R_PPC_ADDR32		1	/* 32bit absolute address */
-#define R_PPC_ADDR24		2	/* 26bit address, 2 bits ignored.  */
-#define R_PPC_ADDR16		3	/* 16bit absolute address */
-#define R_PPC_ADDR16_LO		4	/* lower 16bit of absolute address */
-#define R_PPC_ADDR16_HI		5	/* high 16bit of absolute address */
-#define R_PPC_ADDR16_HA		6	/* adjusted high 16bit */
-#define R_PPC_ADDR14		7	/* 16bit address, 2 bits ignored */
-#define R_PPC_ADDR14_BRTAKEN	8
-#define R_PPC_ADDR14_BRNTAKEN	9
-#define R_PPC_REL24		10	/* PC relative 26 bit */
-#define R_PPC_REL14		11	/* PC relative 16 bit */
-#define R_PPC_REL14_BRTAKEN	12
-#define R_PPC_REL14_BRNTAKEN	13
-#define R_PPC_GOT16		14
-#define R_PPC_GOT16_LO		15
-#define R_PPC_GOT16_HI		16
-#define R_PPC_GOT16_HA		17
-#define R_PPC_PLTREL24		18
-#define R_PPC_COPY		19
-#define R_PPC_GLOB_DAT		20
-#define R_PPC_JMP_SLOT		21
-#define R_PPC_RELATIVE		22
-#define R_PPC_LOCAL24PC		23
-#define R_PPC_UADDR32		24
-#define R_PPC_UADDR16		25
-#define R_PPC_REL32		26
-#define R_PPC_PLT32		27
-#define R_PPC_PLTREL32		28
-#define R_PPC_PLT16_LO		29
-#define R_PPC_PLT16_HI		30
-#define R_PPC_PLT16_HA		31
-#define R_PPC_SDAREL16		32
-#define R_PPC_SECTOFF		33
-#define R_PPC_SECTOFF_LO	34
-#define R_PPC_SECTOFF_HI	35
-#define R_PPC_SECTOFF_HA	36
-
-/* PowerPC relocations defined for the TLS access ABI.  */
-#define R_PPC_TLS		67 /* none	(sym+add)@tls */
-#define R_PPC_DTPMOD32		68 /* word32	(sym+add)@dtpmod */
-#define R_PPC_TPREL16		69 /* half16*	(sym+add)@tprel */
-#define R_PPC_TPREL16_LO	70 /* half16	(sym+add)@tprel@l */
-#define R_PPC_TPREL16_HI	71 /* half16	(sym+add)@tprel@h */
-#define R_PPC_TPREL16_HA	72 /* half16	(sym+add)@tprel@ha */
-#define R_PPC_TPREL32		73 /* word32	(sym+add)@tprel */
-#define R_PPC_DTPREL16		74 /* half16*	(sym+add)@dtprel */
-#define R_PPC_DTPREL16_LO	75 /* half16	(sym+add)@dtprel@l */
-#define R_PPC_DTPREL16_HI	76 /* half16	(sym+add)@dtprel@h */
-#define R_PPC_DTPREL16_HA	77 /* half16	(sym+add)@dtprel@ha */
-#define R_PPC_DTPREL32		78 /* word32	(sym+add)@dtprel */
-#define R_PPC_GOT_TLSGD16	79 /* half16*	(sym+add)@got@tlsgd */
-#define R_PPC_GOT_TLSGD16_LO	80 /* half16	(sym+add)@got@tlsgd@l */
-#define R_PPC_GOT_TLSGD16_HI	81 /* half16	(sym+add)@got@tlsgd@h */
-#define R_PPC_GOT_TLSGD16_HA	82 /* half16	(sym+add)@got@tlsgd@ha */
-#define R_PPC_GOT_TLSLD16	83 /* half16*	(sym+add)@got@tlsld */
-#define R_PPC_GOT_TLSLD16_LO	84 /* half16	(sym+add)@got@tlsld@l */
-#define R_PPC_GOT_TLSLD16_HI	85 /* half16	(sym+add)@got@tlsld@h */
-#define R_PPC_GOT_TLSLD16_HA	86 /* half16	(sym+add)@got@tlsld@ha */
-#define R_PPC_GOT_TPREL16	87 /* half16*	(sym+add)@got@tprel */
-#define R_PPC_GOT_TPREL16_LO	88 /* half16	(sym+add)@got@tprel@l */
-#define R_PPC_GOT_TPREL16_HI	89 /* half16	(sym+add)@got@tprel@h */
-#define R_PPC_GOT_TPREL16_HA	90 /* half16	(sym+add)@got@tprel@ha */
-#define R_PPC_GOT_DTPREL16	91 /* half16*	(sym+add)@got@dtprel */
-#define R_PPC_GOT_DTPREL16_LO	92 /* half16*	(sym+add)@got@dtprel@l */
-#define R_PPC_GOT_DTPREL16_HI	93 /* half16*	(sym+add)@got@dtprel@h */
-#define R_PPC_GOT_DTPREL16_HA	94 /* half16*	(sym+add)@got@dtprel@ha */
-
-/* The remaining relocs are from the Embedded ELF ABI, and are not
-   in the SVR4 ELF ABI.  */
-#define R_PPC_EMB_NADDR32	101
-#define R_PPC_EMB_NADDR16	102
-#define R_PPC_EMB_NADDR16_LO	103
-#define R_PPC_EMB_NADDR16_HI	104
-#define R_PPC_EMB_NADDR16_HA	105
-#define R_PPC_EMB_SDAI16	106
-#define R_PPC_EMB_SDA2I16	107
-#define R_PPC_EMB_SDA2REL	108
-#define R_PPC_EMB_SDA21		109	/* 16 bit offset in SDA */
-#define R_PPC_EMB_MRKREF	110
-#define R_PPC_EMB_RELSEC16	111
-#define R_PPC_EMB_RELST_LO	112
-#define R_PPC_EMB_RELST_HI	113
-#define R_PPC_EMB_RELST_HA	114
-#define R_PPC_EMB_BIT_FLD	115
-#define R_PPC_EMB_RELSDA	116	/* 16 bit relative offset in SDA */
-
-/* Diab tool relocations.  */
-#define R_PPC_DIAB_SDA21_LO	180	/* like EMB_SDA21, but lower 16 bit */
-#define R_PPC_DIAB_SDA21_HI	181	/* like EMB_SDA21, but high 16 bit */
-#define R_PPC_DIAB_SDA21_HA	182	/* like EMB_SDA21, adjusted high 16 */
-#define R_PPC_DIAB_RELSDA_LO	183	/* like EMB_RELSDA, but lower 16 bit */
-#define R_PPC_DIAB_RELSDA_HI	184	/* like EMB_RELSDA, but high 16 bit */
-#define R_PPC_DIAB_RELSDA_HA	185	/* like EMB_RELSDA, adjusted high 16 */
-
-/* GNU extension to support local ifunc.  */
-#define R_PPC_IRELATIVE		248
-
-/* GNU relocs used in PIC code sequences.  */
-#define R_PPC_REL16		249	/* half16   (sym+add-.) */
-#define R_PPC_REL16_LO		250	/* half16   (sym+add-.)@l */
-#define R_PPC_REL16_HI		251	/* half16   (sym+add-.)@h */
-#define R_PPC_REL16_HA		252	/* half16   (sym+add-.)@ha */
-
-/* This is a phony reloc to handle any old fashioned TOC16 references
-   that may still be in object files.  */
-#define R_PPC_TOC16		255
-
-/* PowerPC specific values for the Dyn d_tag field.  */
-#define DT_PPC_GOT		(DT_LOPROC + 0)
-#define DT_PPC_NUM		1
-
-/* PowerPC64 relocations defined by the ABIs */
-#define R_PPC64_NONE		R_PPC_NONE
-#define R_PPC64_ADDR32		R_PPC_ADDR32 /* 32bit absolute address */
-#define R_PPC64_ADDR24		R_PPC_ADDR24 /* 26bit address, word aligned */
-#define R_PPC64_ADDR16		R_PPC_ADDR16 /* 16bit absolute address */
-#define R_PPC64_ADDR16_LO	R_PPC_ADDR16_LO	/* lower 16bits of address */
-#define R_PPC64_ADDR16_HI	R_PPC_ADDR16_HI	/* high 16bits of address. */
-#define R_PPC64_ADDR16_HA	R_PPC_ADDR16_HA /* adjusted high 16bits.  */
-#define R_PPC64_ADDR14		R_PPC_ADDR14 /* 16bit address, word aligned */
-#define R_PPC64_ADDR14_BRTAKEN	R_PPC_ADDR14_BRTAKEN
-#define R_PPC64_ADDR14_BRNTAKEN	R_PPC_ADDR14_BRNTAKEN
-#define R_PPC64_REL24		R_PPC_REL24 /* PC-rel. 26 bit, word aligned */
-#define R_PPC64_REL14		R_PPC_REL14 /* PC relative 16 bit */
-#define R_PPC64_REL14_BRTAKEN	R_PPC_REL14_BRTAKEN
-#define R_PPC64_REL14_BRNTAKEN	R_PPC_REL14_BRNTAKEN
-#define R_PPC64_GOT16		R_PPC_GOT16
-#define R_PPC64_GOT16_LO	R_PPC_GOT16_LO
-#define R_PPC64_GOT16_HI	R_PPC_GOT16_HI
-#define R_PPC64_GOT16_HA	R_PPC_GOT16_HA
-
-#define R_PPC64_COPY		R_PPC_COPY
-#define R_PPC64_GLOB_DAT	R_PPC_GLOB_DAT
-#define R_PPC64_JMP_SLOT	R_PPC_JMP_SLOT
-#define R_PPC64_RELATIVE	R_PPC_RELATIVE
-
-#define R_PPC64_UADDR32		R_PPC_UADDR32
-#define R_PPC64_UADDR16		R_PPC_UADDR16
-#define R_PPC64_REL32		R_PPC_REL32
-#define R_PPC64_PLT32		R_PPC_PLT32
-#define R_PPC64_PLTREL32	R_PPC_PLTREL32
-#define R_PPC64_PLT16_LO	R_PPC_PLT16_LO
-#define R_PPC64_PLT16_HI	R_PPC_PLT16_HI
-#define R_PPC64_PLT16_HA	R_PPC_PLT16_HA
-
-#define R_PPC64_SECTOFF		R_PPC_SECTOFF
-#define R_PPC64_SECTOFF_LO	R_PPC_SECTOFF_LO
-#define R_PPC64_SECTOFF_HI	R_PPC_SECTOFF_HI
-#define R_PPC64_SECTOFF_HA	R_PPC_SECTOFF_HA
-#define R_PPC64_ADDR30		37 /* word30 (S + A - P) >> 2 */
-#define R_PPC64_ADDR64		38 /* doubleword64 S + A */
-#define R_PPC64_ADDR16_HIGHER	39 /* half16 #higher(S + A) */
-#define R_PPC64_ADDR16_HIGHERA	40 /* half16 #highera(S + A) */
-#define R_PPC64_ADDR16_HIGHEST	41 /* half16 #highest(S + A) */
-#define R_PPC64_ADDR16_HIGHESTA	42 /* half16 #highesta(S + A) */
-#define R_PPC64_UADDR64		43 /* doubleword64 S + A */
-#define R_PPC64_REL64		44 /* doubleword64 S + A - P */
-#define R_PPC64_PLT64		45 /* doubleword64 L + A */
-#define R_PPC64_PLTREL64	46 /* doubleword64 L + A - P */
-#define R_PPC64_TOC16		47 /* half16* S + A - .TOC */
-#define R_PPC64_TOC16_LO	48 /* half16 #lo(S + A - .TOC.) */
-#define R_PPC64_TOC16_HI	49 /* half16 #hi(S + A - .TOC.) */
-#define R_PPC64_TOC16_HA	50 /* half16 #ha(S + A - .TOC.) */
-#define R_PPC64_TOC		51 /* doubleword64 .TOC */
-#define R_PPC64_PLTGOT16	52 /* half16* M + A */
-#define R_PPC64_PLTGOT16_LO	53 /* half16 #lo(M + A) */
-#define R_PPC64_PLTGOT16_HI	54 /* half16 #hi(M + A) */
-#define R_PPC64_PLTGOT16_HA	55 /* half16 #ha(M + A) */
-
-#define R_PPC64_ADDR16_DS	56 /* half16ds* (S + A) >> 2 */
-#define R_PPC64_ADDR16_LO_DS	57 /* half16ds  #lo(S + A) >> 2 */
-#define R_PPC64_GOT16_DS	58 /* half16ds* (G + A) >> 2 */
-#define R_PPC64_GOT16_LO_DS	59 /* half16ds  #lo(G + A) >> 2 */
-#define R_PPC64_PLT16_LO_DS	60 /* half16ds  #lo(L + A) >> 2 */
-#define R_PPC64_SECTOFF_DS	61 /* half16ds* (R + A) >> 2 */
-#define R_PPC64_SECTOFF_LO_DS	62 /* half16ds  #lo(R + A) >> 2 */
-#define R_PPC64_TOC16_DS	63 /* half16ds* (S + A - .TOC.) >> 2 */
-#define R_PPC64_TOC16_LO_DS	64 /* half16ds  #lo(S + A - .TOC.) >> 2 */
-#define R_PPC64_PLTGOT16_DS	65 /* half16ds* (M + A) >> 2 */
-#define R_PPC64_PLTGOT16_LO_DS	66 /* half16ds  #lo(M + A) >> 2 */
-
-/* PowerPC64 relocations defined for the TLS access ABI.  */
-#define R_PPC64_TLS		67 /* none	(sym+add)@tls */
-#define R_PPC64_DTPMOD64	68 /* doubleword64 (sym+add)@dtpmod */
-#define R_PPC64_TPREL16		69 /* half16*	(sym+add)@tprel */
-#define R_PPC64_TPREL16_LO	70 /* half16	(sym+add)@tprel@l */
-#define R_PPC64_TPREL16_HI	71 /* half16	(sym+add)@tprel@h */
-#define R_PPC64_TPREL16_HA	72 /* half16	(sym+add)@tprel@ha */
-#define R_PPC64_TPREL64		73 /* doubleword64 (sym+add)@tprel */
-#define R_PPC64_DTPREL16	74 /* half16*	(sym+add)@dtprel */
-#define R_PPC64_DTPREL16_LO	75 /* half16	(sym+add)@dtprel@l */
-#define R_PPC64_DTPREL16_HI	76 /* half16	(sym+add)@dtprel@h */
-#define R_PPC64_DTPREL16_HA	77 /* half16	(sym+add)@dtprel@ha */
-#define R_PPC64_DTPREL64	78 /* doubleword64 (sym+add)@dtprel */
-#define R_PPC64_GOT_TLSGD16	79 /* half16*	(sym+add)@got@tlsgd */
-#define R_PPC64_GOT_TLSGD16_LO	80 /* half16	(sym+add)@got@tlsgd@l */
-#define R_PPC64_GOT_TLSGD16_HI	81 /* half16	(sym+add)@got@tlsgd@h */
-#define R_PPC64_GOT_TLSGD16_HA	82 /* half16	(sym+add)@got@tlsgd@ha */
-#define R_PPC64_GOT_TLSLD16	83 /* half16*	(sym+add)@got@tlsld */
-#define R_PPC64_GOT_TLSLD16_LO	84 /* half16	(sym+add)@got@tlsld@l */
-#define R_PPC64_GOT_TLSLD16_HI	85 /* half16	(sym+add)@got@tlsld@h */
-#define R_PPC64_GOT_TLSLD16_HA	86 /* half16	(sym+add)@got@tlsld@ha */
-#define R_PPC64_GOT_TPREL16_DS	87 /* half16ds*	(sym+add)@got@tprel */
-#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */
-#define R_PPC64_GOT_TPREL16_HI	89 /* half16	(sym+add)@got@tprel@h */
-#define R_PPC64_GOT_TPREL16_HA	90 /* half16	(sym+add)@got@tprel@ha */
-#define R_PPC64_GOT_DTPREL16_DS	91 /* half16ds*	(sym+add)@got@dtprel */
-#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */
-#define R_PPC64_GOT_DTPREL16_HI	93 /* half16	(sym+add)@got@dtprel@h */
-#define R_PPC64_GOT_DTPREL16_HA	94 /* half16	(sym+add)@got@dtprel@ha */
-#define R_PPC64_TPREL16_DS	95 /* half16ds*	(sym+add)@tprel */
-#define R_PPC64_TPREL16_LO_DS	96 /* half16ds	(sym+add)@tprel@l */
-#define R_PPC64_TPREL16_HIGHER	97 /* half16	(sym+add)@tprel@higher */
-#define R_PPC64_TPREL16_HIGHERA	98 /* half16	(sym+add)@tprel@highera */
-#define R_PPC64_TPREL16_HIGHEST	99 /* half16	(sym+add)@tprel@highest */
-#define R_PPC64_TPREL16_HIGHESTA 100 /* half16	(sym+add)@tprel@highesta */
-#define R_PPC64_DTPREL16_DS	101 /* half16ds* (sym+add)@dtprel */
-#define R_PPC64_DTPREL16_LO_DS	102 /* half16ds	(sym+add)@dtprel@l */
-#define R_PPC64_DTPREL16_HIGHER	103 /* half16	(sym+add)@dtprel@higher */
-#define R_PPC64_DTPREL16_HIGHERA 104 /* half16	(sym+add)@dtprel@highera */
-#define R_PPC64_DTPREL16_HIGHEST 105 /* half16	(sym+add)@dtprel@highest */
-#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16	(sym+add)@dtprel@highesta */
-
-/* GNU extension to support local ifunc.  */
-#define R_PPC64_JMP_IREL	247
-#define R_PPC64_IRELATIVE	248
-#define R_PPC64_REL16		249	/* half16   (sym+add-.) */
-#define R_PPC64_REL16_LO	250	/* half16   (sym+add-.)@l */
-#define R_PPC64_REL16_HI	251	/* half16   (sym+add-.)@h */
-#define R_PPC64_REL16_HA	252	/* half16   (sym+add-.)@ha */
-
-/* PowerPC64 specific values for the Dyn d_tag field.  */
-#define DT_PPC64_GLINK  (DT_LOPROC + 0)
-#define DT_PPC64_OPD	(DT_LOPROC + 1)
-#define DT_PPC64_OPDSZ	(DT_LOPROC + 2)
-#define DT_PPC64_NUM    3
-
-
-/* ARM specific declarations */
-
-/* Processor specific flags for the ELF header e_flags field.  */
-#define EF_ARM_RELEXEC		0x01
-#define EF_ARM_HASENTRY		0x02
-#define EF_ARM_INTERWORK	0x04
-#define EF_ARM_APCS_26		0x08
-#define EF_ARM_APCS_FLOAT	0x10
-#define EF_ARM_PIC		0x20
-#define EF_ARM_ALIGN8		0x40 /* 8-bit structure alignment is in use */
-#define EF_ARM_NEW_ABI		0x80
-#define EF_ARM_OLD_ABI		0x100
-#define EF_ARM_SOFT_FLOAT	0x200
-#define EF_ARM_VFP_FLOAT	0x400
-#define EF_ARM_MAVERICK_FLOAT	0x800
-
-#define EF_ARM_ABI_FLOAT_SOFT	0x200   /* NB conflicts with EF_ARM_SOFT_FLOAT */
-#define EF_ARM_ABI_FLOAT_HARD	0x400   /* NB conflicts with EF_ARM_VFP_FLOAT */
-
-
-/* Other constants defined in the ARM ELF spec. version B-01.  */
-/* NB. These conflict with values defined above.  */
-#define EF_ARM_SYMSARESORTED	0x04
-#define EF_ARM_DYNSYMSUSESEGIDX	0x08
-#define EF_ARM_MAPSYMSFIRST	0x10
-#define EF_ARM_EABIMASK		0XFF000000
-
-/* Constants defined in AAELF.  */
-#define EF_ARM_BE8	    0x00800000
-#define EF_ARM_LE8	    0x00400000
-
-#define EF_ARM_EABI_VERSION(flags)	((flags) & EF_ARM_EABIMASK)
-#define EF_ARM_EABI_UNKNOWN	0x00000000
-#define EF_ARM_EABI_VER1	0x01000000
-#define EF_ARM_EABI_VER2	0x02000000
-#define EF_ARM_EABI_VER3	0x03000000
-#define EF_ARM_EABI_VER4	0x04000000
-#define EF_ARM_EABI_VER5	0x05000000
-
-/* Additional symbol types for Thumb.  */
-#define STT_ARM_TFUNC		STT_LOPROC /* A Thumb function.  */
-#define STT_ARM_16BIT		STT_HIPROC /* A Thumb label.  */
-
-/* ARM-specific values for sh_flags */
-#define SHF_ARM_ENTRYSECT	0x10000000 /* Section contains an entry point */
-#define SHF_ARM_COMDEF		0x80000000 /* Section may be multiply defined
-					      in the input to a link step.  */
-
-/* ARM-specific program header flags */
-#define PF_ARM_SB		0x10000000 /* Segment contains the location
-					      addressed by the static base. */
-#define PF_ARM_PI		0x20000000 /* Position-independent segment.  */
-#define PF_ARM_ABS		0x40000000 /* Absolute segment.  */
-
-/* Processor specific values for the Phdr p_type field.  */
-#define PT_ARM_EXIDX		(PT_LOPROC + 1)	/* ARM unwind segment.  */
-
-/* Processor specific values for the Shdr sh_type field.  */
-#define SHT_ARM_EXIDX		(SHT_LOPROC + 1) /* ARM unwind section.  */
-#define SHT_ARM_PREEMPTMAP	(SHT_LOPROC + 2) /* Preemption details.  */
-#define SHT_ARM_ATTRIBUTES	(SHT_LOPROC + 3) /* ARM attributes section.  */
-
-
-/* AArch64 relocs.  */
-
-#define R_AARCH64_NONE            0	/* No relocation.  */
-#define R_AARCH64_ABS64         257	/* Direct 64 bit. */
-#define R_AARCH64_ABS32         258	/* Direct 32 bit.  */
-#define R_AARCH64_ABS16         259	/* Direct 16-bit.  */
-#define R_AARCH64_PREL64        260	/* PC-relative 64-bit.  */
-#define R_AARCH64_PREL32        261	/* PC-relative 32-bit.  */
-#define R_AARCH64_PREL16        262	/* PC-relative 16-bit.  */
-#define R_AARCH64_MOVW_UABS_G0  263	/* Dir. MOVZ imm. from bits 15:0.  */
-#define R_AARCH64_MOVW_UABS_G0_NC 264	/* Likewise for MOVK; no check.  */
-#define R_AARCH64_MOVW_UABS_G1  265	/* Dir. MOVZ imm. from bits 31:16.  */
-#define R_AARCH64_MOVW_UABS_G1_NC 266	/* Likewise for MOVK; no check.  */
-#define R_AARCH64_MOVW_UABS_G2  267	/* Dir. MOVZ imm. from bits 47:32.  */
-#define R_AARCH64_MOVW_UABS_G2_NC 268	/* Likewise for MOVK; no check.  */
-#define R_AARCH64_MOVW_UABS_G3  269	/* Dir. MOV{K,Z} imm. from 63:48.  */
-#define R_AARCH64_MOVW_SABS_G0  270	/* Dir. MOV{N,Z} imm. from 15:0.  */
-#define R_AARCH64_MOVW_SABS_G1  271	/* Dir. MOV{N,Z} imm. from 31:16.  */
-#define R_AARCH64_MOVW_SABS_G2  272	/* Dir. MOV{N,Z} imm. from 47:32.  */
-#define R_AARCH64_LD_PREL_LO19  273	/* PC-rel. LD imm. from bits 20:2.  */
-#define R_AARCH64_ADR_PREL_LO21 274	/* PC-rel. ADR imm. from bits 20:0.  */
-#define R_AARCH64_ADR_PREL_PG_HI21 275	/* Page-rel. ADRP imm. from 32:12.  */
-#define R_AARCH64_ADR_PREL_PG_HI21_NC 276	/* Likewise; no overflow check.  */
-#define R_AARCH64_ADD_ABS_LO12_NC 277	/* Dir. ADD imm. from bits 11:0.  */
-#define R_AARCH64_LDST8_ABS_LO12_NC 278	/* Likewise for LD/ST; no check. */
-#define R_AARCH64_TSTBR14       279	/* PC-rel. TBZ/TBNZ imm. from 15:2.  */
-#define R_AARCH64_CONDBR19      280	/* PC-rel. cond. br. imm. from 20:2. */
-#define R_AARCH64_JUMP26        282	/* PC-rel. B imm. from bits 27:2.  */
-#define R_AARCH64_CALL26        283	/* Likewise for CALL.  */
-#define R_AARCH64_LDST16_ABS_LO12_NC 284	/* Dir. ADD imm. from bits 11:1.  */
-#define R_AARCH64_LDST32_ABS_LO12_NC 285	/* Likewise for bits 11:2.  */
-#define R_AARCH64_LDST64_ABS_LO12_NC 286	/* Likewise for bits 11:3.  */
-#define R_AARCH64_MOVW_PREL_G0  287	/* PC-rel. MOV{N,Z} imm. from 15:0.  */
-#define R_AARCH64_MOVW_PREL_G0_NC 288	/* Likewise for MOVK; no check.  */
-#define R_AARCH64_MOVW_PREL_G1  289	/* PC-rel. MOV{N,Z} imm. from 31:16. */
-#define R_AARCH64_MOVW_PREL_G1_NC 290	/* Likewise for MOVK; no check.  */
-#define R_AARCH64_MOVW_PREL_G2  291	/* PC-rel. MOV{N,Z} imm. from 47:32. */
-#define R_AARCH64_MOVW_PREL_G2_NC 292	/* Likewise for MOVK; no check.  */
-#define R_AARCH64_MOVW_PREL_G3  293	/* PC-rel. MOV{N,Z} imm. from 63:48. */
-#define R_AARCH64_LDST128_ABS_LO12_NC 299	/* Dir. ADD imm. from bits 11:4.  */
-#define R_AARCH64_MOVW_GOTOFF_G0 300	/* GOT-rel. off. MOV{N,Z} imm. 15:0. */
-#define R_AARCH64_MOVW_GOTOFF_G0_NC 301	/* Likewise for MOVK; no check.  */
-#define R_AARCH64_MOVW_GOTOFF_G1 302	/* GOT-rel. o. MOV{N,Z} imm. 31:16.  */
-#define R_AARCH64_MOVW_GOTOFF_G1_NC 303	/* Likewise for MOVK; no check.  */
-#define R_AARCH64_MOVW_GOTOFF_G2 304	/* GOT-rel. o. MOV{N,Z} imm. 47:32.  */
-#define R_AARCH64_MOVW_GOTOFF_G2_NC 305	/* Likewise for MOVK; no check.  */
-#define R_AARCH64_MOVW_GOTOFF_G3 306	/* GOT-rel. o. MOV{N,Z} imm. 63:48.  */
-#define R_AARCH64_GOTREL64      307	/* GOT-relative 64-bit.  */
-#define R_AARCH64_GOTREL32      308	/* GOT-relative 32-bit.  */
-#define R_AARCH64_GOT_LD_PREL19 309	/* PC-rel. GOT off. load imm. 20:2.  */
-#define R_AARCH64_LD64_GOTOFF_LO15 310	/* GOT-rel. off. LD/ST imm. 14:3.  */
-#define R_AARCH64_ADR_GOT_PAGE  311	/* P-page-rel. GOT off. ADRP 32:12.  */
-#define R_AARCH64_LD64_GOT_LO12_NC 312	/* Dir. GOT off. LD/ST imm. 11:3.  */
-#define R_AARCH64_LD64_GOTPAGE_LO15 313	/* GOT-page-rel. GOT off. LD/ST 14:3 */
-#define R_AARCH64_TLSGD_ADR_PREL21 512	/* PC-relative ADR imm. 20:0.  */
-#define R_AARCH64_TLSGD_ADR_PAGE21 513	/* page-rel. ADRP imm. 32:12.  */
-#define R_AARCH64_TLSGD_ADD_LO12_NC 514	/* direct ADD imm. from 11:0.  */
-#define R_AARCH64_TLSGD_MOVW_G1 515	/* GOT-rel. MOV{N,Z} 31:16.  */
-#define R_AARCH64_TLSGD_MOVW_G0_NC 516	/* GOT-rel. MOVK imm. 15:0.  */
-#define R_AARCH64_TLSLD_ADR_PREL21 517	/* Like 512; local dynamic model.  */
-#define R_AARCH64_TLSLD_ADR_PAGE21 518	/* Like 513; local dynamic model.  */
-#define R_AARCH64_TLSLD_ADD_LO12_NC 519	/* Like 514; local dynamic model.  */
-#define R_AARCH64_TLSLD_MOVW_G1 520	/* Like 515; local dynamic model.  */
-#define R_AARCH64_TLSLD_MOVW_G0_NC 521	/* Like 516; local dynamic model.  */
-#define R_AARCH64_TLSLD_LD_PREL19 522	/* TLS PC-rel. load imm. 20:2.  */
-#define R_AARCH64_TLSLD_MOVW_DTPREL_G2 523	/* TLS DTP-rel. MOV{N,Z} 47:32.  */
-#define R_AARCH64_TLSLD_MOVW_DTPREL_G1 524	/* TLS DTP-rel. MOV{N,Z} 31:16.  */
-#define R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC 525	/* Likewise; MOVK; no check.  */
-#define R_AARCH64_TLSLD_MOVW_DTPREL_G0 526	/* TLS DTP-rel. MOV{N,Z} 15:0.  */
-#define R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC 527	/* Likewise; MOVK; no check.  */
-#define R_AARCH64_TLSLD_ADD_DTPREL_HI12 528	/* DTP-rel. ADD imm. from 23:12. */
-#define R_AARCH64_TLSLD_ADD_DTPREL_LO12 529	/* DTP-rel. ADD imm. from 11:0.  */
-#define R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC 530	/* Likewise; no ovfl. check.  */
-#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12 531	/* DTP-rel. LD/ST imm. 11:0.  */
-#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC 532	/* Likewise; no check.  */
-#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12 533	/* DTP-rel. LD/ST imm. 11:1.  */
-#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC 534	/* Likewise; no check.  */
-#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12 535	/* DTP-rel. LD/ST imm. 11:2.  */
-#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC 536	/* Likewise; no check.  */
-#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12 537	/* DTP-rel. LD/ST imm. 11:3.  */
-#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC 538	/* Likewise; no check.  */
-#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 539	/* GOT-rel. MOV{N,Z} 31:16.  */
-#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC 540	/* GOT-rel. MOVK 15:0.  */
-#define R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 541	/* Page-rel. ADRP 32:12.  */
-#define R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC 542	/* Direct LD off. 11:3.  */
-#define R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 543	/* PC-rel. load imm. 20:2.  */
-#define R_AARCH64_TLSLE_MOVW_TPREL_G2 544	/* TLS TP-rel. MOV{N,Z} 47:32.  */
-#define R_AARCH64_TLSLE_MOVW_TPREL_G1 545	/* TLS TP-rel. MOV{N,Z} 31:16.  */
-#define R_AARCH64_TLSLE_MOVW_TPREL_G1_NC 546	/* Likewise; MOVK; no check.  */
-#define R_AARCH64_TLSLE_MOVW_TPREL_G0 547	/* TLS TP-rel. MOV{N,Z} 15:0.  */
-#define R_AARCH64_TLSLE_MOVW_TPREL_G0_NC 548	/* Likewise; MOVK; no check.  */
-#define R_AARCH64_TLSLE_ADD_TPREL_HI12 549	/* TP-rel. ADD imm. 23:12.  */
-#define R_AARCH64_TLSLE_ADD_TPREL_LO12 550	/* TP-rel. ADD imm. 11:0.  */
-#define R_AARCH64_TLSLE_ADD_TPREL_LO12_NC 551	/* Likewise; no ovfl. check.  */
-#define R_AARCH64_TLSLE_LDST8_TPREL_LO12 552	/* TP-rel. LD/ST off. 11:0.  */
-#define R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC 553	/* Likewise; no ovfl. check. */
-#define R_AARCH64_TLSLE_LDST16_TPREL_LO12 554	/* TP-rel. LD/ST off. 11:1.  */
-#define R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC 555	/* Likewise; no check.  */
-#define R_AARCH64_TLSLE_LDST32_TPREL_LO12 556	/* TP-rel. LD/ST off. 11:2.  */
-#define R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC 557	/* Likewise; no check.  */
-#define R_AARCH64_TLSLE_LDST64_TPREL_LO12 558	/* TP-rel. LD/ST off. 11:3.  */
-#define R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC 559	/* Likewise; no check.  */
-#define R_AARCH64_TLSDESC_LD_PREL19 560	/* PC-rel. load immediate 20:2.  */
-#define R_AARCH64_TLSDESC_ADR_PREL21 561	/* PC-rel. ADR immediate 20:0.  */
-#define R_AARCH64_TLSDESC_ADR_PAGE21 562	/* Page-rel. ADRP imm. 32:12.  */
-#define R_AARCH64_TLSDESC_LD64_LO12 563	/* Direct LD off. from 11:3.  */
-#define R_AARCH64_TLSDESC_ADD_LO12 564	/* Direct ADD imm. from 11:0.  */
-#define R_AARCH64_TLSDESC_OFF_G1 565	/* GOT-rel. MOV{N,Z} imm. 31:16.  */
-#define R_AARCH64_TLSDESC_OFF_G0_NC 566	/* GOT-rel. MOVK imm. 15:0; no ck.  */
-#define R_AARCH64_TLSDESC_LDR   567	/* Relax LDR.  */
-#define R_AARCH64_TLSDESC_ADD   568	/* Relax ADD.  */
-#define R_AARCH64_TLSDESC_CALL  569	/* Relax BLR.  */
-#define R_AARCH64_TLSLE_LDST128_TPREL_LO12 570	/* TP-rel. LD/ST off. 11:4.  */
-#define R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC 571	/* Likewise; no check.  */
-#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12 572	/* DTP-rel. LD/ST imm. 11:4. */
-#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC 573	/* Likewise; no check.  */
-#define R_AARCH64_COPY         1024	/* Copy symbol at runtime.  */
-#define R_AARCH64_GLOB_DAT     1025	/* Create GOT entry.  */
-#define R_AARCH64_JUMP_SLOT    1026	/* Create PLT entry.  */
-#define R_AARCH64_RELATIVE     1027	/* Adjust by program base.  */
-#define R_AARCH64_TLS_DTPMOD64 1028	/* Module number, 64 bit.  */
-#define R_AARCH64_TLS_DTPREL64 1029	/* Module-relative offset, 64 bit.  */
-#define R_AARCH64_TLS_TPREL64  1030	/* TP-relative offset, 64 bit.  */
-#define R_AARCH64_TLSDESC      1031	/* TLS Descriptor.  */
-#define R_AARCH64_IRELATIVE    1032	/* STT_GNU_IFUNC relocation.  */
-/* Keep this the last entry.  */
-#define R_AARCH64_NUM          1033
-
-/* ARM relocs.  */
-
-#define R_ARM_NONE		0	/* No reloc */
-#define R_ARM_PC24		1	/* PC relative 26 bit branch */
-#define R_ARM_ABS32		2	/* Direct 32 bit  */
-#define R_ARM_REL32		3	/* PC relative 32 bit */
-#define R_ARM_PC13		4
-#define R_ARM_ABS16		5	/* Direct 16 bit */
-#define R_ARM_ABS12		6	/* Direct 12 bit */
-#define R_ARM_THM_ABS5		7
-#define R_ARM_ABS8		8	/* Direct 8 bit */
-#define R_ARM_SBREL32		9
-#define R_ARM_THM_PC22		10
-#define R_ARM_THM_PC8		11
-#define R_ARM_AMP_VCALL9	12
-#define R_ARM_SWI24		13	/* Obsolete static relocation.  */
-#define R_ARM_TLS_DESC		13      /* Dynamic relocation.  */
-#define R_ARM_THM_SWI8		14
-#define R_ARM_XPC25		15
-#define R_ARM_THM_XPC22		16
-#define R_ARM_TLS_DTPMOD32	17	/* ID of module containing symbol */
-#define R_ARM_TLS_DTPOFF32	18	/* Offset in TLS block */
-#define R_ARM_TLS_TPOFF32	19	/* Offset in static TLS block */
-#define R_ARM_COPY		20	/* Copy symbol at runtime */
-#define R_ARM_GLOB_DAT		21	/* Create GOT entry */
-#define R_ARM_JUMP_SLOT		22	/* Create PLT entry */
-#define R_ARM_RELATIVE		23	/* Adjust by program base */
-#define R_ARM_GOTOFF		24	/* 32 bit offset to GOT */
-#define R_ARM_GOTPC		25	/* 32 bit PC relative offset to GOT */
-#define R_ARM_GOT32		26	/* 32 bit GOT entry */
-#define R_ARM_PLT32		27	/* 32 bit PLT address */
-#define R_ARM_CALL		28
-#define R_ARM_JUMP24		29
-#define R_ARM_THM_JUMP24	30
-#define R_ARM_ALU_PCREL_7_0	32
-#define R_ARM_ALU_PCREL_15_8	33
-#define R_ARM_ALU_PCREL_23_15	34
-#define R_ARM_LDR_SBREL_11_0	35
-#define R_ARM_ALU_SBREL_19_12	36
-#define R_ARM_ALU_SBREL_27_20	37
-#define R_ARM_V4BX		40
-#define R_ARM_PREL31		42
-#define R_ARM_MOVW_ABS_NC	43
-#define R_ARM_MOVT_ABS		 44
-#define R_ARM_THM_MOVW_ABS_NC	47
-#define R_ARM_THM_MOVT_ABS	48
-#define R_ARM_TLS_GOTDESC	90
-#define R_ARM_TLS_CALL		91
-#define R_ARM_TLS_DESCSEQ	92
-#define R_ARM_THM_TLS_CALL	93
-#define R_ARM_GNU_VTENTRY	100
-#define R_ARM_GNU_VTINHERIT	101
-#define R_ARM_THM_PC11		102	/* thumb unconditional branch */
-#define R_ARM_THM_PC9		103	/* thumb conditional branch */
-#define R_ARM_TLS_GD32		104	/* PC-rel 32 bit for global dynamic
-					   thread local data */
-#define R_ARM_TLS_LDM32		105	/* PC-rel 32 bit for local dynamic
-					   thread local data */
-#define R_ARM_TLS_LDO32		106	/* 32 bit offset relative to TLS
-					   block */
-#define R_ARM_TLS_IE32		107	/* PC-rel 32 bit for GOT entry of
-					   static TLS block offset */
-#define R_ARM_TLS_LE32		108	/* 32 bit offset relative to static
-					   TLS block */
-#define	R_ARM_THM_TLS_DESCSEQ	129
-#define R_ARM_IRELATIVE		160
-#define R_ARM_RXPC25		249
-#define R_ARM_RSBREL32		250
-#define R_ARM_THM_RPC22		251
-#define R_ARM_RREL32		252
-#define R_ARM_RABS22		253
-#define R_ARM_RPC24		254
-#define R_ARM_RBASE		255
-/* Keep this the last entry.  */
-#define R_ARM_NUM		256
-
-/* TMS320C67xx specific declarations */
-
-/* XXX: no ELF standard yet*/
-
-/* TMS320C67xx relocs. */
-#define R_C60_32       1
-#define R_C60_GOT32     3               /* 32 bit GOT entry */
-#define R_C60_PLT32     4               /* 32 bit PLT address */
-#define R_C60_COPY      5               /* Copy symbol at runtime */
-#define R_C60_GLOB_DAT  6               /* Create GOT entry */
-#define R_C60_JMP_SLOT  7               /* Create PLT entry */
-#define R_C60_RELATIVE  8               /* Adjust by program base */
-#define R_C60_GOTOFF    9               /* 32 bit offset to GOT */
-#define R_C60_GOTPC     10              /* 32 bit PC relative offset to GOT */
-
-#define R_C60LO16      0x54       /* low 16 bit MVKL embedded */
-#define R_C60HI16      0x55       /* high 16 bit MVKH embedded */
-/* Keep this the last entry.  */
-#define R_C60_NUM      0x56
-
-/* IA-64 specific declarations.  */
-
-/* Processor specific flags for the Ehdr e_flags field.  */
-#define EF_IA_64_MASKOS		0x0000000f	/* os-specific flags */
-#define EF_IA_64_ABI64		0x00000010	/* 64-bit ABI */
-#define EF_IA_64_ARCH		0xff000000	/* arch. version mask */
-
-/* Processor specific values for the Phdr p_type field.  */
-#define PT_IA_64_ARCHEXT	(PT_LOPROC + 0)	/* arch extension bits */
-#define PT_IA_64_UNWIND		(PT_LOPROC + 1)	/* ia64 unwind bits */
-#define PT_IA_64_HP_OPT_ANOT	(PT_LOOS + 0x12)
-#define PT_IA_64_HP_HSL_ANOT	(PT_LOOS + 0x13)
-#define PT_IA_64_HP_STACK	(PT_LOOS + 0x14)
-
-/* Processor specific flags for the Phdr p_flags field.  */
-#define PF_IA_64_NORECOV	0x80000000	/* spec insns w/o recovery */
-
-/* Processor specific values for the Shdr sh_type field.  */
-#define SHT_IA_64_EXT		(SHT_LOPROC + 0) /* extension bits */
-#define SHT_IA_64_UNWIND	(SHT_LOPROC + 1) /* unwind bits */
-
-/* Processor specific flags for the Shdr sh_flags field.  */
-#define SHF_IA_64_SHORT		0x10000000	/* section near gp */
-#define SHF_IA_64_NORECOV	0x20000000	/* spec insns w/o recovery */
-
-/* Processor specific values for the Dyn d_tag field.  */
-#define DT_IA_64_PLT_RESERVE	(DT_LOPROC + 0)
-#define DT_IA_64_NUM		1
-
-/* IA-64 relocations.  */
-#define R_IA64_NONE		0x00	/* none */
-#define R_IA64_IMM14		0x21	/* symbol + addend, add imm14 */
-#define R_IA64_IMM22		0x22	/* symbol + addend, add imm22 */
-#define R_IA64_IMM64		0x23	/* symbol + addend, mov imm64 */
-#define R_IA64_DIR32MSB		0x24	/* symbol + addend, data4 MSB */
-#define R_IA64_DIR32LSB		0x25	/* symbol + addend, data4 LSB */
-#define R_IA64_DIR64MSB		0x26	/* symbol + addend, data8 MSB */
-#define R_IA64_DIR64LSB		0x27	/* symbol + addend, data8 LSB */
-#define R_IA64_GPREL22		0x2a	/* @gprel(sym + add), add imm22 */
-#define R_IA64_GPREL64I		0x2b	/* @gprel(sym + add), mov imm64 */
-#define R_IA64_GPREL32MSB	0x2c	/* @gprel(sym + add), data4 MSB */
-#define R_IA64_GPREL32LSB	0x2d	/* @gprel(sym + add), data4 LSB */
-#define R_IA64_GPREL64MSB	0x2e	/* @gprel(sym + add), data8 MSB */
-#define R_IA64_GPREL64LSB	0x2f	/* @gprel(sym + add), data8 LSB */
-#define R_IA64_LTOFF22		0x32	/* @ltoff(sym + add), add imm22 */
-#define R_IA64_LTOFF64I		0x33	/* @ltoff(sym + add), mov imm64 */
-#define R_IA64_PLTOFF22		0x3a	/* @pltoff(sym + add), add imm22 */
-#define R_IA64_PLTOFF64I	0x3b	/* @pltoff(sym + add), mov imm64 */
-#define R_IA64_PLTOFF64MSB	0x3e	/* @pltoff(sym + add), data8 MSB */
-#define R_IA64_PLTOFF64LSB	0x3f	/* @pltoff(sym + add), data8 LSB */
-#define R_IA64_FPTR64I		0x43	/* @fptr(sym + add), mov imm64 */
-#define R_IA64_FPTR32MSB	0x44	/* @fptr(sym + add), data4 MSB */
-#define R_IA64_FPTR32LSB	0x45	/* @fptr(sym + add), data4 LSB */
-#define R_IA64_FPTR64MSB	0x46	/* @fptr(sym + add), data8 MSB */
-#define R_IA64_FPTR64LSB	0x47	/* @fptr(sym + add), data8 LSB */
-#define R_IA64_PCREL60B		0x48	/* @pcrel(sym + add), brl */
-#define R_IA64_PCREL21B		0x49	/* @pcrel(sym + add), ptb, call */
-#define R_IA64_PCREL21M		0x4a	/* @pcrel(sym + add), chk.s */
-#define R_IA64_PCREL21F		0x4b	/* @pcrel(sym + add), fchkf */
-#define R_IA64_PCREL32MSB	0x4c	/* @pcrel(sym + add), data4 MSB */
-#define R_IA64_PCREL32LSB	0x4d	/* @pcrel(sym + add), data4 LSB */
-#define R_IA64_PCREL64MSB	0x4e	/* @pcrel(sym + add), data8 MSB */
-#define R_IA64_PCREL64LSB	0x4f	/* @pcrel(sym + add), data8 LSB */
-#define R_IA64_LTOFF_FPTR22	0x52	/* @ltoff(@fptr(s+a)), imm22 */
-#define R_IA64_LTOFF_FPTR64I	0x53	/* @ltoff(@fptr(s+a)), imm64 */
-#define R_IA64_LTOFF_FPTR32MSB	0x54	/* @ltoff(@fptr(s+a)), data4 MSB */
-#define R_IA64_LTOFF_FPTR32LSB	0x55	/* @ltoff(@fptr(s+a)), data4 LSB */
-#define R_IA64_LTOFF_FPTR64MSB	0x56	/* @ltoff(@fptr(s+a)), data8 MSB */
-#define R_IA64_LTOFF_FPTR64LSB	0x57	/* @ltoff(@fptr(s+a)), data8 LSB */
-#define R_IA64_SEGREL32MSB	0x5c	/* @segrel(sym + add), data4 MSB */
-#define R_IA64_SEGREL32LSB	0x5d	/* @segrel(sym + add), data4 LSB */
-#define R_IA64_SEGREL64MSB	0x5e	/* @segrel(sym + add), data8 MSB */
-#define R_IA64_SEGREL64LSB	0x5f	/* @segrel(sym + add), data8 LSB */
-#define R_IA64_SECREL32MSB	0x64	/* @secrel(sym + add), data4 MSB */
-#define R_IA64_SECREL32LSB	0x65	/* @secrel(sym + add), data4 LSB */
-#define R_IA64_SECREL64MSB	0x66	/* @secrel(sym + add), data8 MSB */
-#define R_IA64_SECREL64LSB	0x67	/* @secrel(sym + add), data8 LSB */
-#define R_IA64_REL32MSB		0x6c	/* data 4 + REL */
-#define R_IA64_REL32LSB		0x6d	/* data 4 + REL */
-#define R_IA64_REL64MSB		0x6e	/* data 8 + REL */
-#define R_IA64_REL64LSB		0x6f	/* data 8 + REL */
-#define R_IA64_LTV32MSB		0x74	/* symbol + addend, data4 MSB */
-#define R_IA64_LTV32LSB		0x75	/* symbol + addend, data4 LSB */
-#define R_IA64_LTV64MSB		0x76	/* symbol + addend, data8 MSB */
-#define R_IA64_LTV64LSB		0x77	/* symbol + addend, data8 LSB */
-#define R_IA64_PCREL21BI	0x79	/* @pcrel(sym + add), 21bit inst */
-#define R_IA64_PCREL22		0x7a	/* @pcrel(sym + add), 22bit inst */
-#define R_IA64_PCREL64I		0x7b	/* @pcrel(sym + add), 64bit inst */
-#define R_IA64_IPLTMSB		0x80	/* dynamic reloc, imported PLT, MSB */
-#define R_IA64_IPLTLSB		0x81	/* dynamic reloc, imported PLT, LSB */
-#define R_IA64_COPY		0x84	/* copy relocation */
-#define R_IA64_SUB		0x85	/* Addend and symbol difference */
-#define R_IA64_LTOFF22X		0x86	/* LTOFF22, relaxable.  */
-#define R_IA64_LDXMOV		0x87	/* Use of LTOFF22X.  */
-#define R_IA64_TPREL14		0x91	/* @tprel(sym + add), imm14 */
-#define R_IA64_TPREL22		0x92	/* @tprel(sym + add), imm22 */
-#define R_IA64_TPREL64I		0x93	/* @tprel(sym + add), imm64 */
-#define R_IA64_TPREL64MSB	0x96	/* @tprel(sym + add), data8 MSB */
-#define R_IA64_TPREL64LSB	0x97	/* @tprel(sym + add), data8 LSB */
-#define R_IA64_LTOFF_TPREL22	0x9a	/* @ltoff(@tprel(s+a)), imm2 */
-#define R_IA64_DTPMOD64MSB	0xa6	/* @dtpmod(sym + add), data8 MSB */
-#define R_IA64_DTPMOD64LSB	0xa7	/* @dtpmod(sym + add), data8 LSB */
-#define R_IA64_LTOFF_DTPMOD22	0xaa	/* @ltoff(@dtpmod(sym + add)), imm22 */
-#define R_IA64_DTPREL14		0xb1	/* @dtprel(sym + add), imm14 */
-#define R_IA64_DTPREL22		0xb2	/* @dtprel(sym + add), imm22 */
-#define R_IA64_DTPREL64I	0xb3	/* @dtprel(sym + add), imm64 */
-#define R_IA64_DTPREL32MSB	0xb4	/* @dtprel(sym + add), data4 MSB */
-#define R_IA64_DTPREL32LSB	0xb5	/* @dtprel(sym + add), data4 LSB */
-#define R_IA64_DTPREL64MSB	0xb6	/* @dtprel(sym + add), data8 MSB */
-#define R_IA64_DTPREL64LSB	0xb7	/* @dtprel(sym + add), data8 LSB */
-#define R_IA64_LTOFF_DTPREL22	0xba	/* @ltoff(@dtprel(s+a)), imm22 */
-
-/* SH specific declarations */
-
-/* Processor specific flags for the ELF header e_flags field.  */
-#define EF_SH_MACH_MASK		0x1f
-#define EF_SH_UNKNOWN		0x0
-#define EF_SH1			0x1
-#define EF_SH2			0x2
-#define EF_SH3			0x3
-#define EF_SH_DSP		0x4
-#define EF_SH3_DSP		0x5
-#define EF_SH4AL_DSP		0x6
-#define EF_SH3E			0x8
-#define EF_SH4			0x9
-#define EF_SH2E			0xb
-#define EF_SH4A			0xc
-#define EF_SH2A			0xd
-#define EF_SH4_NOFPU		0x10
-#define EF_SH4A_NOFPU		0x11
-#define EF_SH4_NOMMU_NOFPU	0x12
-#define EF_SH2A_NOFPU		0x13
-#define EF_SH3_NOMMU		0x14
-#define EF_SH2A_SH4_NOFPU	0x15
-#define EF_SH2A_SH3_NOFPU	0x16
-#define EF_SH2A_SH4		0x17
-#define EF_SH2A_SH3E		0x18
-
-/* SH relocs.  */
-#define	R_SH_NONE		0
-#define	R_SH_DIR32		1
-#define	R_SH_REL32		2
-#define	R_SH_DIR8WPN		3
-#define	R_SH_IND12W		4
-#define	R_SH_DIR8WPL		5
-#define	R_SH_DIR8WPZ		6
-#define	R_SH_DIR8BP		7
-#define	R_SH_DIR8W		8
-#define	R_SH_DIR8L		9
-#define	R_SH_SWITCH16		25
-#define	R_SH_SWITCH32		26
-#define	R_SH_USES		27
-#define	R_SH_COUNT		28
-#define	R_SH_ALIGN		29
-#define	R_SH_CODE		30
-#define	R_SH_DATA		31
-#define	R_SH_LABEL		32
-#define	R_SH_SWITCH8		33
-#define	R_SH_GNU_VTINHERIT	34
-#define	R_SH_GNU_VTENTRY	35
-#define	R_SH_TLS_GD_32		144
-#define	R_SH_TLS_LD_32		145
-#define	R_SH_TLS_LDO_32		146
-#define	R_SH_TLS_IE_32		147
-#define	R_SH_TLS_LE_32		148
-#define	R_SH_TLS_DTPMOD32	149
-#define	R_SH_TLS_DTPOFF32	150
-#define	R_SH_TLS_TPOFF32	151
-#define	R_SH_GOT32		160
-#define	R_SH_PLT32		161
-#define	R_SH_COPY		162
-#define	R_SH_GLOB_DAT		163
-#define	R_SH_JMP_SLOT		164
-#define	R_SH_RELATIVE		165
-#define	R_SH_GOTOFF		166
-#define	R_SH_GOTPC		167
-/* Keep this the last entry.  */
-#define	R_SH_NUM		256
-
-/* S/390 specific definitions.  */
-
-/* Valid values for the e_flags field.  */
-
-#define EF_S390_HIGH_GPRS    0x00000001  /* High GPRs kernel facility needed.  */
-
-/* Additional s390 relocs */
-
-#define R_390_NONE		0	/* No reloc.  */
-#define R_390_8			1	/* Direct 8 bit.  */
-#define R_390_12		2	/* Direct 12 bit.  */
-#define R_390_16		3	/* Direct 16 bit.  */
-#define R_390_32		4	/* Direct 32 bit.  */
-#define R_390_PC32		5	/* PC relative 32 bit.	*/
-#define R_390_GOT12		6	/* 12 bit GOT offset.  */
-#define R_390_GOT32		7	/* 32 bit GOT offset.  */
-#define R_390_PLT32		8	/* 32 bit PC relative PLT address.  */
-#define R_390_COPY		9	/* Copy symbol at runtime.  */
-#define R_390_GLOB_DAT		10	/* Create GOT entry.  */
-#define R_390_JMP_SLOT		11	/* Create PLT entry.  */
-#define R_390_RELATIVE		12	/* Adjust by program base.  */
-#define R_390_GOTOFF32		13	/* 32 bit offset to GOT.	 */
-#define R_390_GOTPC		14	/* 32 bit PC relative offset to GOT.  */
-#define R_390_GOT16		15	/* 16 bit GOT offset.  */
-#define R_390_PC16		16	/* PC relative 16 bit.	*/
-#define R_390_PC16DBL		17	/* PC relative 16 bit shifted by 1.  */
-#define R_390_PLT16DBL		18	/* 16 bit PC rel. PLT shifted by 1.  */
-#define R_390_PC32DBL		19	/* PC relative 32 bit shifted by 1.  */
-#define R_390_PLT32DBL		20	/* 32 bit PC rel. PLT shifted by 1.  */
-#define R_390_GOTPCDBL		21	/* 32 bit PC rel. GOT shifted by 1.  */
-#define R_390_64		22	/* Direct 64 bit.  */
-#define R_390_PC64		23	/* PC relative 64 bit.	*/
-#define R_390_GOT64		24	/* 64 bit GOT offset.  */
-#define R_390_PLT64		25	/* 64 bit PC relative PLT address.  */
-#define R_390_GOTENT		26	/* 32 bit PC rel. to GOT entry >> 1. */
-#define R_390_GOTOFF16		27	/* 16 bit offset to GOT. */
-#define R_390_GOTOFF64		28	/* 64 bit offset to GOT. */
-#define R_390_GOTPLT12		29	/* 12 bit offset to jump slot.	*/
-#define R_390_GOTPLT16		30	/* 16 bit offset to jump slot.	*/
-#define R_390_GOTPLT32		31	/* 32 bit offset to jump slot.	*/
-#define R_390_GOTPLT64		32	/* 64 bit offset to jump slot.	*/
-#define R_390_GOTPLTENT		33	/* 32 bit rel. offset to jump slot.  */
-#define R_390_PLTOFF16		34	/* 16 bit offset from GOT to PLT. */
-#define R_390_PLTOFF32		35	/* 32 bit offset from GOT to PLT. */
-#define R_390_PLTOFF64		36	/* 16 bit offset from GOT to PLT. */
-#define R_390_TLS_LOAD		37	/* Tag for load insn in TLS code.  */
-#define R_390_TLS_GDCALL	38	/* Tag for function call in general
-					   dynamic TLS code. */
-#define R_390_TLS_LDCALL	39	/* Tag for function call in local
-					   dynamic TLS code. */
-#define R_390_TLS_GD32		40	/* Direct 32 bit for general dynamic
-					   thread local data.  */
-#define R_390_TLS_GD64		41	/* Direct 64 bit for general dynamic
-					  thread local data.  */
-#define R_390_TLS_GOTIE12	42	/* 12 bit GOT offset for static TLS
-					   block offset.  */
-#define R_390_TLS_GOTIE32	43	/* 32 bit GOT offset for static TLS
-					   block offset.  */
-#define R_390_TLS_GOTIE64	44	/* 64 bit GOT offset for static TLS
-					   block offset. */
-#define R_390_TLS_LDM32		45	/* Direct 32 bit for local dynamic
-					   thread local data in LE code.  */
-#define R_390_TLS_LDM64		46	/* Direct 64 bit for local dynamic
-					   thread local data in LE code.  */
-#define R_390_TLS_IE32		47	/* 32 bit address of GOT entry for
-					   negated static TLS block offset.  */
-#define R_390_TLS_IE64		48	/* 64 bit address of GOT entry for
-					   negated static TLS block offset.  */
-#define R_390_TLS_IEENT		49	/* 32 bit rel. offset to GOT entry for
-					   negated static TLS block offset.  */
-#define R_390_TLS_LE32		50	/* 32 bit negated offset relative to
-					   static TLS block.  */
-#define R_390_TLS_LE64		51	/* 64 bit negated offset relative to
-					   static TLS block.  */
-#define R_390_TLS_LDO32		52	/* 32 bit offset relative to TLS
-					   block.  */
-#define R_390_TLS_LDO64		53	/* 64 bit offset relative to TLS
-					   block.  */
-#define R_390_TLS_DTPMOD	54	/* ID of module containing symbol.  */
-#define R_390_TLS_DTPOFF	55	/* Offset in TLS block.	 */
-#define R_390_TLS_TPOFF		56	/* Negated offset in static TLS
-					   block.  */
-#define R_390_20		57	/* Direct 20 bit.  */
-#define R_390_GOT20		58	/* 20 bit GOT offset.  */
-#define R_390_GOTPLT20		59	/* 20 bit offset to jump slot.  */
-#define R_390_TLS_GOTIE20	60	/* 20 bit GOT offset for static TLS
-					   block offset.  */
-#define R_390_IRELATIVE         61      /* STT_GNU_IFUNC relocation.  */
-/* Keep this the last entry.  */
-#define R_390_NUM		62
-
-
-/* CRIS relocations.  */
-#define R_CRIS_NONE		0
-#define R_CRIS_8		1
-#define R_CRIS_16		2
-#define R_CRIS_32		3
-#define R_CRIS_8_PCREL		4
-#define R_CRIS_16_PCREL		5
-#define R_CRIS_32_PCREL		6
-#define R_CRIS_GNU_VTINHERIT	7
-#define R_CRIS_GNU_VTENTRY	8
-#define R_CRIS_COPY		9
-#define R_CRIS_GLOB_DAT		10
-#define R_CRIS_JUMP_SLOT	11
-#define R_CRIS_RELATIVE		12
-#define R_CRIS_16_GOT		13
-#define R_CRIS_32_GOT		14
-#define R_CRIS_16_GOTPLT	15
-#define R_CRIS_32_GOTPLT	16
-#define R_CRIS_32_GOTREL	17
-#define R_CRIS_32_PLT_GOTREL	18
-#define R_CRIS_32_PLT_PCREL	19
-
-#define R_CRIS_NUM		20
-
-
-/* AMD x86-64 relocations.  */
-#define R_X86_64_NONE		0	/* No reloc */
-#define R_X86_64_64		1	/* Direct 64 bit  */
-#define R_X86_64_PC32		2	/* PC relative 32 bit signed */
-#define R_X86_64_GOT32		3	/* 32 bit GOT entry */
-#define R_X86_64_PLT32		4	/* 32 bit PLT address */
-#define R_X86_64_COPY		5	/* Copy symbol at runtime */
-#define R_X86_64_GLOB_DAT	6	/* Create GOT entry */
-#define R_X86_64_JUMP_SLOT	7	/* Create PLT entry */
-#define R_X86_64_RELATIVE	8	/* Adjust by program base */
-#define R_X86_64_GOTPCREL	9	/* 32 bit signed PC relative
-					   offset to GOT */
-#define R_X86_64_32		10	/* Direct 32 bit zero extended */
-#define R_X86_64_32S		11	/* Direct 32 bit sign extended */
-#define R_X86_64_16		12	/* Direct 16 bit zero extended */
-#define R_X86_64_PC16		13	/* 16 bit sign extended pc relative */
-#define R_X86_64_8		14	/* Direct 8 bit sign extended  */
-#define R_X86_64_PC8		15	/* 8 bit sign extended pc relative */
-#define R_X86_64_DTPMOD64	16	/* ID of module containing symbol */
-#define R_X86_64_DTPOFF64	17	/* Offset in module's TLS block */
-#define R_X86_64_TPOFF64	18	/* Offset in initial TLS block */
-#define R_X86_64_TLSGD		19	/* 32 bit signed PC relative offset
-					   to two GOT entries for GD symbol */
-#define R_X86_64_TLSLD		20	/* 32 bit signed PC relative offset
-					   to two GOT entries for LD symbol */
-#define R_X86_64_DTPOFF32	21	/* Offset in TLS block */
-#define R_X86_64_GOTTPOFF	22	/* 32 bit signed PC relative offset
-					   to GOT entry for IE symbol */
-#define R_X86_64_TPOFF32	23	/* Offset in initial TLS block */
-#define R_X86_64_PC64		24	/* PC relative 64 bit */
-#define R_X86_64_GOTOFF64	25	/* 64 bit offset to GOT */
-#define R_X86_64_GOTPC32	26	/* 32 bit signed pc relative
-					   offset to GOT */
-#define R_X86_64_GOT64		27	/* 64-bit GOT entry offset */
-#define R_X86_64_GOTPCREL64	28	/* 64-bit PC relative offset
-					   to GOT entry */
-#define R_X86_64_GOTPC64	29	/* 64-bit PC relative offset to GOT */
-#define R_X86_64_GOTPLT64	30 	/* like GOT64, says PLT entry needed */
-#define R_X86_64_PLTOFF64	31	/* 64-bit GOT relative offset
-					   to PLT entry */
-#define R_X86_64_SIZE32		32	/* Size of symbol plus 32-bit addend */
-#define R_X86_64_SIZE64		33	/* Size of symbol plus 64-bit addend */
-#define R_X86_64_GOTPC32_TLSDESC 34	/* GOT offset for TLS descriptor.  */
-#define R_X86_64_TLSDESC_CALL   35	/* Marker for call through TLS
-					   descriptor.  */
-#define R_X86_64_TLSDESC        36	/* TLS descriptor.  */
-#define R_X86_64_IRELATIVE	37	/* Adjust indirectly by program base */
-#define R_X86_64_RELATIVE64	38	/* 64-bit adjust by program base */
-#define R_X86_64_GOTPCRELX	41	/* like GOTPCREL, but optionally with
-					   linker optimizations */
-#define R_X86_64_REX_GOTPCRELX	42      /* like GOTPCRELX, but a REX prefix
-					   is present */
-
-#define R_X86_64_NUM		43
-
-
-/* AM33 relocations.  */
-#define R_MN10300_NONE		0	/* No reloc.  */
-#define R_MN10300_32		1	/* Direct 32 bit.  */
-#define R_MN10300_16		2	/* Direct 16 bit.  */
-#define R_MN10300_8		3	/* Direct 8 bit.  */
-#define R_MN10300_PCREL32	4	/* PC-relative 32-bit.  */
-#define R_MN10300_PCREL16	5	/* PC-relative 16-bit signed.  */
-#define R_MN10300_PCREL8	6	/* PC-relative 8-bit signed.  */
-#define R_MN10300_GNU_VTINHERIT	7	/* Ancient C++ vtable garbage... */
-#define R_MN10300_GNU_VTENTRY	8	/* ... collection annotation.  */
-#define R_MN10300_24		9	/* Direct 24 bit.  */
-#define R_MN10300_GOTPC32	10	/* 32-bit PCrel offset to GOT.  */
-#define R_MN10300_GOTPC16	11	/* 16-bit PCrel offset to GOT.  */
-#define R_MN10300_GOTOFF32	12	/* 32-bit offset from GOT.  */
-#define R_MN10300_GOTOFF24	13	/* 24-bit offset from GOT.  */
-#define R_MN10300_GOTOFF16	14	/* 16-bit offset from GOT.  */
-#define R_MN10300_PLT32		15	/* 32-bit PCrel to PLT entry.  */
-#define R_MN10300_PLT16		16	/* 16-bit PCrel to PLT entry.  */
-#define R_MN10300_GOT32		17	/* 32-bit offset to GOT entry.  */
-#define R_MN10300_GOT24		18	/* 24-bit offset to GOT entry.  */
-#define R_MN10300_GOT16		19	/* 16-bit offset to GOT entry.  */
-#define R_MN10300_COPY		20	/* Copy symbol at runtime.  */
-#define R_MN10300_GLOB_DAT	21	/* Create GOT entry.  */
-#define R_MN10300_JMP_SLOT	22	/* Create PLT entry.  */
-#define R_MN10300_RELATIVE	23	/* Adjust by program base.  */
-#define R_MN10300_TLS_GD	24	/* 32-bit offset for global dynamic.  */
-#define R_MN10300_TLS_LD	25	/* 32-bit offset for local dynamic.  */
-#define R_MN10300_TLS_LDO	26	/* Module-relative offset.  */
-#define R_MN10300_TLS_GOTIE	27	/* GOT offset for static TLS block
-					   offset.  */
-#define R_MN10300_TLS_IE	28	/* GOT address for static TLS block
-					   offset.  */
-#define R_MN10300_TLS_LE	29	/* Offset relative to static TLS
-					   block.  */
-#define R_MN10300_TLS_DTPMOD	30	/* ID of module containing symbol.  */
-#define R_MN10300_TLS_DTPOFF	31	/* Offset in module TLS block.  */
-#define R_MN10300_TLS_TPOFF	32	/* Offset in static TLS block.  */
-#define R_MN10300_SYM_DIFF	33	/* Adjustment for next reloc as needed
-					   by linker relaxation.  */
-#define R_MN10300_ALIGN		34	/* Alignment requirement for linker
-					   relaxation.  */
-#define R_MN10300_NUM		35
-
-
-/* M32R relocs.  */
-#define R_M32R_NONE		0	/* No reloc. */
-#define R_M32R_16		1	/* Direct 16 bit. */
-#define R_M32R_32		2	/* Direct 32 bit. */
-#define R_M32R_24		3	/* Direct 24 bit. */
-#define R_M32R_10_PCREL		4	/* PC relative 10 bit shifted. */
-#define R_M32R_18_PCREL		5	/* PC relative 18 bit shifted. */
-#define R_M32R_26_PCREL		6	/* PC relative 26 bit shifted. */
-#define R_M32R_HI16_ULO		7	/* High 16 bit with unsigned low. */
-#define R_M32R_HI16_SLO		8	/* High 16 bit with signed low. */
-#define R_M32R_LO16		9	/* Low 16 bit. */
-#define R_M32R_SDA16		10	/* 16 bit offset in SDA. */
-#define R_M32R_GNU_VTINHERIT	11
-#define R_M32R_GNU_VTENTRY	12
-/* M32R relocs use SHT_RELA.  */
-#define R_M32R_16_RELA		33	/* Direct 16 bit. */
-#define R_M32R_32_RELA		34	/* Direct 32 bit. */
-#define R_M32R_24_RELA		35	/* Direct 24 bit. */
-#define R_M32R_10_PCREL_RELA	36	/* PC relative 10 bit shifted. */
-#define R_M32R_18_PCREL_RELA	37	/* PC relative 18 bit shifted. */
-#define R_M32R_26_PCREL_RELA	38	/* PC relative 26 bit shifted. */
-#define R_M32R_HI16_ULO_RELA	39	/* High 16 bit with unsigned low */
-#define R_M32R_HI16_SLO_RELA	40	/* High 16 bit with signed low */
-#define R_M32R_LO16_RELA	41	/* Low 16 bit */
-#define R_M32R_SDA16_RELA	42	/* 16 bit offset in SDA */
-#define R_M32R_RELA_GNU_VTINHERIT	43
-#define R_M32R_RELA_GNU_VTENTRY	44
-#define R_M32R_REL32		45	/* PC relative 32 bit.  */
-
-#define R_M32R_GOT24		48	/* 24 bit GOT entry */
-#define R_M32R_26_PLTREL	49	/* 26 bit PC relative to PLT shifted */
-#define R_M32R_COPY		50	/* Copy symbol at runtime */
-#define R_M32R_GLOB_DAT		51	/* Create GOT entry */
-#define R_M32R_JMP_SLOT		52	/* Create PLT entry */
-#define R_M32R_RELATIVE		53	/* Adjust by program base */
-#define R_M32R_GOTOFF		54	/* 24 bit offset to GOT */
-#define R_M32R_GOTPC24		55	/* 24 bit PC relative offset to GOT */
-#define R_M32R_GOT16_HI_ULO	56	/* High 16 bit GOT entry with unsigned
-					   low */
-#define R_M32R_GOT16_HI_SLO	57	/* High 16 bit GOT entry with signed
-					   low */
-#define R_M32R_GOT16_LO		58	/* Low 16 bit GOT entry */
-#define R_M32R_GOTPC_HI_ULO	59	/* High 16 bit PC relative offset to
-					   GOT with unsigned low */
-#define R_M32R_GOTPC_HI_SLO	60	/* High 16 bit PC relative offset to
-					   GOT with signed low */
-#define R_M32R_GOTPC_LO		61	/* Low 16 bit PC relative offset to
-					   GOT */
-#define R_M32R_GOTOFF_HI_ULO	62	/* High 16 bit offset to GOT
-					   with unsigned low */
-#define R_M32R_GOTOFF_HI_SLO	63	/* High 16 bit offset to GOT
-					   with signed low */
-#define R_M32R_GOTOFF_LO	64	/* Low 16 bit offset to GOT */
-#define R_M32R_NUM		256	/* Keep this the last entry. */
-
-
-/* TILEPro relocations.  */
-#define R_TILEPRO_NONE		0	/* No reloc */
-#define R_TILEPRO_32		1	/* Direct 32 bit */
-#define R_TILEPRO_16		2	/* Direct 16 bit */
-#define R_TILEPRO_8		3	/* Direct 8 bit */
-#define R_TILEPRO_32_PCREL	4	/* PC relative 32 bit */
-#define R_TILEPRO_16_PCREL	5	/* PC relative 16 bit */
-#define R_TILEPRO_8_PCREL	6	/* PC relative 8 bit */
-#define R_TILEPRO_LO16		7	/* Low 16 bit */
-#define R_TILEPRO_HI16		8	/* High 16 bit */
-#define R_TILEPRO_HA16		9	/* High 16 bit, adjusted */
-#define R_TILEPRO_COPY		10	/* Copy relocation */
-#define R_TILEPRO_GLOB_DAT	11	/* Create GOT entry */
-#define R_TILEPRO_JMP_SLOT	12	/* Create PLT entry */
-#define R_TILEPRO_RELATIVE	13	/* Adjust by program base */
-#define R_TILEPRO_BROFF_X1	14	/* X1 pipe branch offset */
-#define R_TILEPRO_JOFFLONG_X1	15	/* X1 pipe jump offset */
-#define R_TILEPRO_JOFFLONG_X1_PLT 16	/* X1 pipe jump offset to PLT */
-#define R_TILEPRO_IMM8_X0	17	/* X0 pipe 8-bit */
-#define R_TILEPRO_IMM8_Y0	18	/* Y0 pipe 8-bit */
-#define R_TILEPRO_IMM8_X1	19	/* X1 pipe 8-bit */
-#define R_TILEPRO_IMM8_Y1	20	/* Y1 pipe 8-bit */
-#define R_TILEPRO_MT_IMM15_X1	21	/* X1 pipe mtspr */
-#define R_TILEPRO_MF_IMM15_X1	22	/* X1 pipe mfspr */
-#define R_TILEPRO_IMM16_X0	23	/* X0 pipe 16-bit */
-#define R_TILEPRO_IMM16_X1	24	/* X1 pipe 16-bit */
-#define R_TILEPRO_IMM16_X0_LO	25	/* X0 pipe low 16-bit */
-#define R_TILEPRO_IMM16_X1_LO	26	/* X1 pipe low 16-bit */
-#define R_TILEPRO_IMM16_X0_HI	27	/* X0 pipe high 16-bit */
-#define R_TILEPRO_IMM16_X1_HI	28	/* X1 pipe high 16-bit */
-#define R_TILEPRO_IMM16_X0_HA	29	/* X0 pipe high 16-bit, adjusted */
-#define R_TILEPRO_IMM16_X1_HA	30	/* X1 pipe high 16-bit, adjusted */
-#define R_TILEPRO_IMM16_X0_PCREL 31	/* X0 pipe PC relative 16 bit */
-#define R_TILEPRO_IMM16_X1_PCREL 32	/* X1 pipe PC relative 16 bit */
-#define R_TILEPRO_IMM16_X0_LO_PCREL 33	/* X0 pipe PC relative low 16 bit */
-#define R_TILEPRO_IMM16_X1_LO_PCREL 34	/* X1 pipe PC relative low 16 bit */
-#define R_TILEPRO_IMM16_X0_HI_PCREL 35	/* X0 pipe PC relative high 16 bit */
-#define R_TILEPRO_IMM16_X1_HI_PCREL 36	/* X1 pipe PC relative high 16 bit */
-#define R_TILEPRO_IMM16_X0_HA_PCREL 37	/* X0 pipe PC relative ha() 16 bit */
-#define R_TILEPRO_IMM16_X1_HA_PCREL 38	/* X1 pipe PC relative ha() 16 bit */
-#define R_TILEPRO_IMM16_X0_GOT	39	/* X0 pipe 16-bit GOT offset */
-#define R_TILEPRO_IMM16_X1_GOT	40	/* X1 pipe 16-bit GOT offset */
-#define R_TILEPRO_IMM16_X0_GOT_LO 41	/* X0 pipe low 16-bit GOT offset */
-#define R_TILEPRO_IMM16_X1_GOT_LO 42	/* X1 pipe low 16-bit GOT offset */
-#define R_TILEPRO_IMM16_X0_GOT_HI 43	/* X0 pipe high 16-bit GOT offset */
-#define R_TILEPRO_IMM16_X1_GOT_HI 44	/* X1 pipe high 16-bit GOT offset */
-#define R_TILEPRO_IMM16_X0_GOT_HA 45	/* X0 pipe ha() 16-bit GOT offset */
-#define R_TILEPRO_IMM16_X1_GOT_HA 46	/* X1 pipe ha() 16-bit GOT offset */
-#define R_TILEPRO_MMSTART_X0	47	/* X0 pipe mm "start" */
-#define R_TILEPRO_MMEND_X0	48	/* X0 pipe mm "end" */
-#define R_TILEPRO_MMSTART_X1	49	/* X1 pipe mm "start" */
-#define R_TILEPRO_MMEND_X1	50	/* X1 pipe mm "end" */
-#define R_TILEPRO_SHAMT_X0	51	/* X0 pipe shift amount */
-#define R_TILEPRO_SHAMT_X1	52	/* X1 pipe shift amount */
-#define R_TILEPRO_SHAMT_Y0	53	/* Y0 pipe shift amount */
-#define R_TILEPRO_SHAMT_Y1	54	/* Y1 pipe shift amount */
-#define R_TILEPRO_DEST_IMM8_X1	55	/* X1 pipe destination 8-bit */
-/* Relocs 56-59 are currently not defined.  */
-#define R_TILEPRO_TLS_GD_CALL	60	/* "jal" for TLS GD */
-#define R_TILEPRO_IMM8_X0_TLS_GD_ADD 61	/* X0 pipe "addi" for TLS GD */
-#define R_TILEPRO_IMM8_X1_TLS_GD_ADD 62	/* X1 pipe "addi" for TLS GD */
-#define R_TILEPRO_IMM8_Y0_TLS_GD_ADD 63	/* Y0 pipe "addi" for TLS GD */
-#define R_TILEPRO_IMM8_Y1_TLS_GD_ADD 64	/* Y1 pipe "addi" for TLS GD */
-#define R_TILEPRO_TLS_IE_LOAD	65	/* "lw_tls" for TLS IE */
-#define R_TILEPRO_IMM16_X0_TLS_GD 66	/* X0 pipe 16-bit TLS GD offset */
-#define R_TILEPRO_IMM16_X1_TLS_GD 67	/* X1 pipe 16-bit TLS GD offset */
-#define R_TILEPRO_IMM16_X0_TLS_GD_LO 68	/* X0 pipe low 16-bit TLS GD offset */
-#define R_TILEPRO_IMM16_X1_TLS_GD_LO 69	/* X1 pipe low 16-bit TLS GD offset */
-#define R_TILEPRO_IMM16_X0_TLS_GD_HI 70	/* X0 pipe high 16-bit TLS GD offset */
-#define R_TILEPRO_IMM16_X1_TLS_GD_HI 71	/* X1 pipe high 16-bit TLS GD offset */
-#define R_TILEPRO_IMM16_X0_TLS_GD_HA 72	/* X0 pipe ha() 16-bit TLS GD offset */
-#define R_TILEPRO_IMM16_X1_TLS_GD_HA 73	/* X1 pipe ha() 16-bit TLS GD offset */
-#define R_TILEPRO_IMM16_X0_TLS_IE 74	/* X0 pipe 16-bit TLS IE offset */
-#define R_TILEPRO_IMM16_X1_TLS_IE 75	/* X1 pipe 16-bit TLS IE offset */
-#define R_TILEPRO_IMM16_X0_TLS_IE_LO 76	/* X0 pipe low 16-bit TLS IE offset */
-#define R_TILEPRO_IMM16_X1_TLS_IE_LO 77	/* X1 pipe low 16-bit TLS IE offset */
-#define R_TILEPRO_IMM16_X0_TLS_IE_HI 78	/* X0 pipe high 16-bit TLS IE offset */
-#define R_TILEPRO_IMM16_X1_TLS_IE_HI 79	/* X1 pipe high 16-bit TLS IE offset */
-#define R_TILEPRO_IMM16_X0_TLS_IE_HA 80	/* X0 pipe ha() 16-bit TLS IE offset */
-#define R_TILEPRO_IMM16_X1_TLS_IE_HA 81	/* X1 pipe ha() 16-bit TLS IE offset */
-#define R_TILEPRO_TLS_DTPMOD32	82	/* ID of module containing symbol */
-#define R_TILEPRO_TLS_DTPOFF32	83	/* Offset in TLS block */
-#define R_TILEPRO_TLS_TPOFF32	84	/* Offset in static TLS block */
-#define R_TILEPRO_IMM16_X0_TLS_LE 85	/* X0 pipe 16-bit TLS LE offset */
-#define R_TILEPRO_IMM16_X1_TLS_LE 86	/* X1 pipe 16-bit TLS LE offset */
-#define R_TILEPRO_IMM16_X0_TLS_LE_LO 87	/* X0 pipe low 16-bit TLS LE offset */
-#define R_TILEPRO_IMM16_X1_TLS_LE_LO 88	/* X1 pipe low 16-bit TLS LE offset */
-#define R_TILEPRO_IMM16_X0_TLS_LE_HI 89	/* X0 pipe high 16-bit TLS LE offset */
-#define R_TILEPRO_IMM16_X1_TLS_LE_HI 90	/* X1 pipe high 16-bit TLS LE offset */
-#define R_TILEPRO_IMM16_X0_TLS_LE_HA 91	/* X0 pipe ha() 16-bit TLS LE offset */
-#define R_TILEPRO_IMM16_X1_TLS_LE_HA 92	/* X1 pipe ha() 16-bit TLS LE offset */
-
-#define R_TILEPRO_GNU_VTINHERIT	128	/* GNU C++ vtable hierarchy */
-#define R_TILEPRO_GNU_VTENTRY	129	/* GNU C++ vtable member usage */
-
-#define R_TILEPRO_NUM		130
-
-
-/* TILE-Gx relocations.  */
-#define R_TILEGX_NONE		0	/* No reloc */
-#define R_TILEGX_64		1	/* Direct 64 bit */
-#define R_TILEGX_32		2	/* Direct 32 bit */
-#define R_TILEGX_16		3	/* Direct 16 bit */
-#define R_TILEGX_8		4	/* Direct 8 bit */
-#define R_TILEGX_64_PCREL	5	/* PC relative 64 bit */
-#define R_TILEGX_32_PCREL	6	/* PC relative 32 bit */
-#define R_TILEGX_16_PCREL	7	/* PC relative 16 bit */
-#define R_TILEGX_8_PCREL	8	/* PC relative 8 bit */
-#define R_TILEGX_HW0		9	/* hword 0 16-bit */
-#define R_TILEGX_HW1		10	/* hword 1 16-bit */
-#define R_TILEGX_HW2		11	/* hword 2 16-bit */
-#define R_TILEGX_HW3		12	/* hword 3 16-bit */
-#define R_TILEGX_HW0_LAST	13	/* last hword 0 16-bit */
-#define R_TILEGX_HW1_LAST	14	/* last hword 1 16-bit */
-#define R_TILEGX_HW2_LAST	15	/* last hword 2 16-bit */
-#define R_TILEGX_COPY		16	/* Copy relocation */
-#define R_TILEGX_GLOB_DAT	17	/* Create GOT entry */
-#define R_TILEGX_JMP_SLOT	18	/* Create PLT entry */
-#define R_TILEGX_RELATIVE	19	/* Adjust by program base */
-#define R_TILEGX_BROFF_X1	20	/* X1 pipe branch offset */
-#define R_TILEGX_JUMPOFF_X1	21	/* X1 pipe jump offset */
-#define R_TILEGX_JUMPOFF_X1_PLT	22	/* X1 pipe jump offset to PLT */
-#define R_TILEGX_IMM8_X0	23	/* X0 pipe 8-bit */
-#define R_TILEGX_IMM8_Y0	24	/* Y0 pipe 8-bit */
-#define R_TILEGX_IMM8_X1	25	/* X1 pipe 8-bit */
-#define R_TILEGX_IMM8_Y1	26	/* Y1 pipe 8-bit */
-#define R_TILEGX_DEST_IMM8_X1	27	/* X1 pipe destination 8-bit */
-#define R_TILEGX_MT_IMM14_X1	28	/* X1 pipe mtspr */
-#define R_TILEGX_MF_IMM14_X1	29	/* X1 pipe mfspr */
-#define R_TILEGX_MMSTART_X0	30	/* X0 pipe mm "start" */
-#define R_TILEGX_MMEND_X0	31	/* X0 pipe mm "end" */
-#define R_TILEGX_SHAMT_X0	32	/* X0 pipe shift amount */
-#define R_TILEGX_SHAMT_X1	33	/* X1 pipe shift amount */
-#define R_TILEGX_SHAMT_Y0	34	/* Y0 pipe shift amount */
-#define R_TILEGX_SHAMT_Y1	35	/* Y1 pipe shift amount */
-#define R_TILEGX_IMM16_X0_HW0	36	/* X0 pipe hword 0 */
-#define R_TILEGX_IMM16_X1_HW0	37	/* X1 pipe hword 0 */
-#define R_TILEGX_IMM16_X0_HW1	38	/* X0 pipe hword 1 */
-#define R_TILEGX_IMM16_X1_HW1	39	/* X1 pipe hword 1 */
-#define R_TILEGX_IMM16_X0_HW2	40	/* X0 pipe hword 2 */
-#define R_TILEGX_IMM16_X1_HW2	41	/* X1 pipe hword 2 */
-#define R_TILEGX_IMM16_X0_HW3	42	/* X0 pipe hword 3 */
-#define R_TILEGX_IMM16_X1_HW3	43	/* X1 pipe hword 3 */
-#define R_TILEGX_IMM16_X0_HW0_LAST 44	/* X0 pipe last hword 0 */
-#define R_TILEGX_IMM16_X1_HW0_LAST 45	/* X1 pipe last hword 0 */
-#define R_TILEGX_IMM16_X0_HW1_LAST 46	/* X0 pipe last hword 1 */
-#define R_TILEGX_IMM16_X1_HW1_LAST 47	/* X1 pipe last hword 1 */
-#define R_TILEGX_IMM16_X0_HW2_LAST 48	/* X0 pipe last hword 2 */
-#define R_TILEGX_IMM16_X1_HW2_LAST 49	/* X1 pipe last hword 2 */
-#define R_TILEGX_IMM16_X0_HW0_PCREL 50	/* X0 pipe PC relative hword 0 */
-#define R_TILEGX_IMM16_X1_HW0_PCREL 51	/* X1 pipe PC relative hword 0 */
-#define R_TILEGX_IMM16_X0_HW1_PCREL 52	/* X0 pipe PC relative hword 1 */
-#define R_TILEGX_IMM16_X1_HW1_PCREL 53	/* X1 pipe PC relative hword 1 */
-#define R_TILEGX_IMM16_X0_HW2_PCREL 54	/* X0 pipe PC relative hword 2 */
-#define R_TILEGX_IMM16_X1_HW2_PCREL 55	/* X1 pipe PC relative hword 2 */
-#define R_TILEGX_IMM16_X0_HW3_PCREL 56	/* X0 pipe PC relative hword 3 */
-#define R_TILEGX_IMM16_X1_HW3_PCREL 57	/* X1 pipe PC relative hword 3 */
-#define R_TILEGX_IMM16_X0_HW0_LAST_PCREL 58 /* X0 pipe PC-rel last hword 0 */
-#define R_TILEGX_IMM16_X1_HW0_LAST_PCREL 59 /* X1 pipe PC-rel last hword 0 */
-#define R_TILEGX_IMM16_X0_HW1_LAST_PCREL 60 /* X0 pipe PC-rel last hword 1 */
-#define R_TILEGX_IMM16_X1_HW1_LAST_PCREL 61 /* X1 pipe PC-rel last hword 1 */
-#define R_TILEGX_IMM16_X0_HW2_LAST_PCREL 62 /* X0 pipe PC-rel last hword 2 */
-#define R_TILEGX_IMM16_X1_HW2_LAST_PCREL 63 /* X1 pipe PC-rel last hword 2 */
-#define R_TILEGX_IMM16_X0_HW0_GOT 64	/* X0 pipe hword 0 GOT offset */
-#define R_TILEGX_IMM16_X1_HW0_GOT 65	/* X1 pipe hword 0 GOT offset */
-#define R_TILEGX_IMM16_X0_HW0_PLT_PCREL 66 /* X0 pipe PC-rel PLT hword 0 */
-#define R_TILEGX_IMM16_X1_HW0_PLT_PCREL 67 /* X1 pipe PC-rel PLT hword 0 */
-#define R_TILEGX_IMM16_X0_HW1_PLT_PCREL 68 /* X0 pipe PC-rel PLT hword 1 */
-#define R_TILEGX_IMM16_X1_HW1_PLT_PCREL 69 /* X1 pipe PC-rel PLT hword 1 */
-#define R_TILEGX_IMM16_X0_HW2_PLT_PCREL 70 /* X0 pipe PC-rel PLT hword 2 */
-#define R_TILEGX_IMM16_X1_HW2_PLT_PCREL 71 /* X1 pipe PC-rel PLT hword 2 */
-#define R_TILEGX_IMM16_X0_HW0_LAST_GOT 72 /* X0 pipe last hword 0 GOT offset */
-#define R_TILEGX_IMM16_X1_HW0_LAST_GOT 73 /* X1 pipe last hword 0 GOT offset */
-#define R_TILEGX_IMM16_X0_HW1_LAST_GOT 74 /* X0 pipe last hword 1 GOT offset */
-#define R_TILEGX_IMM16_X1_HW1_LAST_GOT 75 /* X1 pipe last hword 1 GOT offset */
-#define R_TILEGX_IMM16_X0_HW3_PLT_PCREL 76 /* X0 pipe PC-rel PLT hword 3 */
-#define R_TILEGX_IMM16_X1_HW3_PLT_PCREL 77 /* X1 pipe PC-rel PLT hword 3 */
-#define R_TILEGX_IMM16_X0_HW0_TLS_GD 78	/* X0 pipe hword 0 TLS GD offset */
-#define R_TILEGX_IMM16_X1_HW0_TLS_GD 79	/* X1 pipe hword 0 TLS GD offset */
-#define R_TILEGX_IMM16_X0_HW0_TLS_LE 80	/* X0 pipe hword 0 TLS LE offset */
-#define R_TILEGX_IMM16_X1_HW0_TLS_LE 81	/* X1 pipe hword 0 TLS LE offset */
-#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE 82 /* X0 pipe last hword 0 LE off */
-#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE 83 /* X1 pipe last hword 0 LE off */
-#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE 84 /* X0 pipe last hword 1 LE off */
-#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE 85 /* X1 pipe last hword 1 LE off */
-#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD 86 /* X0 pipe last hword 0 GD off */
-#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD 87 /* X1 pipe last hword 0 GD off */
-#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD 88 /* X0 pipe last hword 1 GD off */
-#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD 89 /* X1 pipe last hword 1 GD off */
-/* Relocs 90-91 are currently not defined.  */
-#define R_TILEGX_IMM16_X0_HW0_TLS_IE 92	/* X0 pipe hword 0 TLS IE offset */
-#define R_TILEGX_IMM16_X1_HW0_TLS_IE 93	/* X1 pipe hword 0 TLS IE offset */
-#define R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL 94 /* X0 pipe PC-rel PLT last hword 0 */
-#define R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL 95 /* X1 pipe PC-rel PLT last hword 0 */
-#define R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL 96 /* X0 pipe PC-rel PLT last hword 1 */
-#define R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL 97 /* X1 pipe PC-rel PLT last hword 1 */
-#define R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL 98 /* X0 pipe PC-rel PLT last hword 2 */
-#define R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL 99 /* X1 pipe PC-rel PLT last hword 2 */
-#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE 100 /* X0 pipe last hword 0 IE off */
-#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE 101 /* X1 pipe last hword 0 IE off */
-#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE 102 /* X0 pipe last hword 1 IE off */
-#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE 103 /* X1 pipe last hword 1 IE off */
-/* Relocs 104-105 are currently not defined.  */
-#define R_TILEGX_TLS_DTPMOD64	106	/* 64-bit ID of symbol's module */
-#define R_TILEGX_TLS_DTPOFF64	107	/* 64-bit offset in TLS block */
-#define R_TILEGX_TLS_TPOFF64	108	/* 64-bit offset in static TLS block */
-#define R_TILEGX_TLS_DTPMOD32	109	/* 32-bit ID of symbol's module */
-#define R_TILEGX_TLS_DTPOFF32	110	/* 32-bit offset in TLS block */
-#define R_TILEGX_TLS_TPOFF32	111	/* 32-bit offset in static TLS block */
-#define R_TILEGX_TLS_GD_CALL	112	/* "jal" for TLS GD */
-#define R_TILEGX_IMM8_X0_TLS_GD_ADD 113	/* X0 pipe "addi" for TLS GD */
-#define R_TILEGX_IMM8_X1_TLS_GD_ADD 114	/* X1 pipe "addi" for TLS GD */
-#define R_TILEGX_IMM8_Y0_TLS_GD_ADD 115	/* Y0 pipe "addi" for TLS GD */
-#define R_TILEGX_IMM8_Y1_TLS_GD_ADD 116	/* Y1 pipe "addi" for TLS GD */
-#define R_TILEGX_TLS_IE_LOAD	117	/* "ld_tls" for TLS IE */
-#define R_TILEGX_IMM8_X0_TLS_ADD 118	/* X0 pipe "addi" for TLS GD/IE */
-#define R_TILEGX_IMM8_X1_TLS_ADD 119	/* X1 pipe "addi" for TLS GD/IE */
-#define R_TILEGX_IMM8_Y0_TLS_ADD 120	/* Y0 pipe "addi" for TLS GD/IE */
-#define R_TILEGX_IMM8_Y1_TLS_ADD 121	/* Y1 pipe "addi" for TLS GD/IE */
-
-#define R_TILEGX_GNU_VTINHERIT	128	/* GNU C++ vtable hierarchy */
-#define R_TILEGX_GNU_VTENTRY	129	/* GNU C++ vtable member usage */
-
-#define R_TILEGX_NUM		130
-
-
-#endif	/* elf.h */
diff --git a/tinyc/examples/ex1.c b/tinyc/examples/ex1.c
deleted file mode 100644
index 3d2a3e18f..000000000
--- a/tinyc/examples/ex1.c
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/local/bin/tcc -run
-#include <tcclib.h>
-
-int main()
-{
-    printf("Hello World\n");
-    return 0;
-}
diff --git a/tinyc/examples/ex2.c b/tinyc/examples/ex2.c
deleted file mode 100644
index d415e39d7..000000000
--- a/tinyc/examples/ex2.c
+++ /dev/null
@@ -1,98 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-
-#define N 20
-
-int nb_num;
-int tab[N];
-int stack_ptr;
-int stack_op[N];
-int stack_res[60];
-int result;
-
-int find(int n, int i1, int a, int b, int op)
-{
-    int i, j;
-    int c;
-
-    if (stack_ptr >= 0) {
-        stack_res[3*stack_ptr] = a;
-        stack_op[stack_ptr] = op;
-        stack_res[3*stack_ptr+1] = b;
-        stack_res[3*stack_ptr+2] = n;
-        if (n == result)
-            return 1;
-        tab[i1] = n;
-    }
-
-    for(i=0;i<nb_num;i++) {
-        for(j=i+1;j<nb_num;j++) {
-            a = tab[i];
-            b = tab[j];
-            if (a != 0 && b != 0) {
-
-                tab[j] = 0;
-                stack_ptr++;
-
-                if (find(a + b, i, a, b, '+'))
-                    return 1;
-                if (find(a - b, i, a, b, '-'))
-                    return 1;
-                if (find(b - a, i, b, a, '-'))
-                    return 1;
-                if (find(a * b, i, a, b, '*'))
-                    return 1;
-                if (b != 0) {
-                    c = a / b;
-                    if (find(c, i, a, b, '/'))
-                        return 1;
-                }
-
-                if (a != 0) {
-                    c = b / a;
-                    if (find(c, i, b, a, '/'))
-                        return 1;
-                }
-
-                stack_ptr--;
-                tab[i] = a;
-                tab[j] = b;
-            }
-        }
-    }
-
-    return 0;
-}
-
-int main(int argc, char **argv)
-{
-    int i, res, p;
-
-    if (argc < 3) {
-        printf("usage: %s: result numbers...\n"
-               "Try to find result from numbers with the 4 basic operations.\n", argv[0]);
-        exit(1);
-    }
-
-    p = 1;
-    result = atoi(argv[p]);
-    printf("result=%d\n", result);
-    nb_num = 0;
-    for(i=p+1;i<argc;i++) {
-        tab[nb_num++] = atoi(argv[i]);
-    }
-
-    stack_ptr = -1;
-    res = find(0, 0, 0, 0, ' ');
-    if (res) {
-        for(i=0;i<=stack_ptr;i++) {
-            printf("%d %c %d = %d\n",
-                   stack_res[3*i], stack_op[i],
-                   stack_res[3*i+1], stack_res[3*i+2]);
-        }
-        return 0;
-    } else {
-        printf("Impossible\n");
-        return 1;
-    }
-}
diff --git a/tinyc/examples/ex3.c b/tinyc/examples/ex3.c
deleted file mode 100644
index 5556a4bcd..000000000
--- a/tinyc/examples/ex3.c
+++ /dev/null
@@ -1,23 +0,0 @@
-#include <tcclib.h>
-
-int fib(n)
-{
-    if (n <= 2)
-        return 1;
-    else
-        return fib(n-1) + fib(n-2);
-}
-
-int main(int argc, char **argv)
-{
-    int n;
-    if (argc < 2) {
-        printf("usage: fib n\n"
-               "Compute nth Fibonacci number\n");
-        return 1;
-    }
-
-    n = atoi(argv[1]);
-    printf("fib(%d) = %d\n", n, fib(n, 2));
-    return 0;
-}
diff --git a/tinyc/examples/ex4.c b/tinyc/examples/ex4.c
deleted file mode 100644
index f92c0da9f..000000000
--- a/tinyc/examples/ex4.c
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/local/bin/tcc -run -L/usr/X11R6/lib -lX11
-#include <stdlib.h>
-#include <stdio.h>
-#include <X11/Xlib.h>
-
-/* Yes, TCC can use X11 too ! */
-
-int main(int argc, char **argv)
-{
-    Display *display;
-    Screen *screen;
-
-    display = XOpenDisplay("");
-    if (!display) {
-        fprintf(stderr, "Could not open X11 display\n");
-        exit(1);
-    }
-    printf("X11 display opened.\n");
-    screen = XScreenOfDisplay(display, 0);
-    printf("width = %d\nheight = %d\ndepth = %d\n",
-           screen->width,
-           screen->height,
-           screen->root_depth);
-    XCloseDisplay(display);
-    return 0;
-}
diff --git a/tinyc/examples/ex5.c b/tinyc/examples/ex5.c
deleted file mode 100644
index 156425e39..000000000
--- a/tinyc/examples/ex5.c
+++ /dev/null
@@ -1,8 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-
-int main()
-{
-    printf("Hello World\n");
-    return 0;
-}
diff --git a/tinyc/i386-asm.c b/tinyc/i386-asm.c
deleted file mode 100644
index 2e184974e..000000000
--- a/tinyc/i386-asm.c
+++ /dev/null
@@ -1,1714 +0,0 @@
-/*
- *  i386 specific functions for TCC assembler
- *
- *  Copyright (c) 2001, 2002 Fabrice Bellard
- *  Copyright (c) 2009 Frédéric Feret (x86_64 support)
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "tcc.h"
-
-#define MAX_OPERANDS 3
-
-#define TOK_ASM_first TOK_ASM_clc
-#define TOK_ASM_last TOK_ASM_emms
-#define TOK_ASM_alllast TOK_ASM_subps
-
-#define OPC_B          0x01  /* only used with OPC_WL */
-#define OPC_WL         0x02  /* accepts w, l or no suffix */
-#define OPC_BWL        (OPC_B | OPC_WL) /* accepts b, w, l or no suffix */
-#define OPC_REG        0x04 /* register is added to opcode */
-#define OPC_MODRM      0x08 /* modrm encoding */
-
-#define OPCT_MASK      0x70
-#define OPC_FWAIT      0x10 /* add fwait opcode */
-#define OPC_SHIFT      0x20 /* shift opcodes */
-#define OPC_ARITH      0x30 /* arithmetic opcodes */
-#define OPC_FARITH     0x40 /* FPU arithmetic opcodes */
-#define OPC_TEST       0x50 /* test opcodes */
-#define OPCT_IS(v,i) (((v) & OPCT_MASK) == (i))
-
-#define OPC_0F        0x100 /* Is secondary map (0x0f prefix) */
-#define OPC_48        0x200 /* Always has REX prefix */
-#ifdef TCC_TARGET_X86_64
-# define OPC_WLQ     0x1000  /* accepts w, l, q or no suffix */
-# define OPC_BWLQ    (OPC_B | OPC_WLQ) /* accepts b, w, l, q or no suffix */
-# define OPC_WLX     OPC_WLQ
-# define OPC_BWLX    OPC_BWLQ
-#else
-# define OPC_WLX     OPC_WL
-# define OPC_BWLX    OPC_BWL
-#endif
-
-#define OPC_GROUP_SHIFT 13
-
-/* in order to compress the operand type, we use specific operands and
-   we or only with EA  */
-enum {
-    OPT_REG8=0, /* warning: value is hardcoded from TOK_ASM_xxx */
-    OPT_REG16,  /* warning: value is hardcoded from TOK_ASM_xxx */
-    OPT_REG32,  /* warning: value is hardcoded from TOK_ASM_xxx */
-#ifdef TCC_TARGET_X86_64
-    OPT_REG64,  /* warning: value is hardcoded from TOK_ASM_xxx */
-#endif
-    OPT_MMX,    /* warning: value is hardcoded from TOK_ASM_xxx */
-    OPT_SSE,    /* warning: value is hardcoded from TOK_ASM_xxx */
-    OPT_CR,     /* warning: value is hardcoded from TOK_ASM_xxx */
-    OPT_TR,     /* warning: value is hardcoded from TOK_ASM_xxx */
-    OPT_DB,     /* warning: value is hardcoded from TOK_ASM_xxx */
-    OPT_SEG,
-    OPT_ST,
-#ifdef TCC_TARGET_X86_64
-    OPT_REG8_LOW, /* %spl,%bpl,%sil,%dil, encoded like ah,ch,dh,bh, but
-		     with REX prefix, not used in insn templates */
-#endif
-    OPT_IM8,
-    OPT_IM8S,
-    OPT_IM16,
-    OPT_IM32,
-#ifdef TCC_TARGET_X86_64
-    OPT_IM64,
-#endif
-    OPT_EAX,    /* %al, %ax, %eax or %rax register */
-    OPT_ST0,    /* %st(0) register */
-    OPT_CL,     /* %cl register */
-    OPT_DX,     /* %dx register */
-    OPT_ADDR,   /* OP_EA with only offset */
-    OPT_INDIR,  /* *(expr) */
-    /* composite types */
-    OPT_COMPOSITE_FIRST,
-    OPT_IM,     /* IM8 | IM16 | IM32 */
-    OPT_REG,    /* REG8 | REG16 | REG32 | REG64 */
-    OPT_REGW,   /* REG16 | REG32 | REG64 */
-    OPT_IMW,    /* IM16 | IM32 */
-    OPT_MMXSSE, /* MMX | SSE */
-    OPT_DISP,   /* Like OPT_ADDR, but emitted as displacement (for jumps) */
-    OPT_DISP8,  /* Like OPT_ADDR, but only 8bit (short jumps) */
-    /* can be ored with any OPT_xxx */
-    OPT_EA = 0x80
-};
-
-#define OP_REG8   (1 << OPT_REG8)
-#define OP_REG16  (1 << OPT_REG16)
-#define OP_REG32  (1 << OPT_REG32)
-#define OP_MMX    (1 << OPT_MMX)
-#define OP_SSE    (1 << OPT_SSE)
-#define OP_CR     (1 << OPT_CR)
-#define OP_TR     (1 << OPT_TR)
-#define OP_DB     (1 << OPT_DB)
-#define OP_SEG    (1 << OPT_SEG)
-#define OP_ST     (1 << OPT_ST)
-#define OP_IM8    (1 << OPT_IM8)
-#define OP_IM8S   (1 << OPT_IM8S)
-#define OP_IM16   (1 << OPT_IM16)
-#define OP_IM32   (1 << OPT_IM32)
-#define OP_EAX    (1 << OPT_EAX)
-#define OP_ST0    (1 << OPT_ST0)
-#define OP_CL     (1 << OPT_CL)
-#define OP_DX     (1 << OPT_DX)
-#define OP_ADDR   (1 << OPT_ADDR)
-#define OP_INDIR  (1 << OPT_INDIR)
-#ifdef TCC_TARGET_X86_64
-# define OP_REG64 (1 << OPT_REG64)
-# define OP_REG8_LOW (1 << OPT_REG8_LOW)
-# define OP_IM64  (1 << OPT_IM64)
-# define OP_EA32  (OP_EA << 1)
-#else
-# define OP_REG64 0
-# define OP_REG8_LOW 0
-# define OP_IM64  0
-# define OP_EA32  0
-#endif
-
-#define OP_EA     0x40000000
-#define OP_REG    (OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64)
-
-#ifdef TCC_TARGET_X86_64
-# define TREG_XAX   TREG_RAX
-# define TREG_XCX   TREG_RCX
-# define TREG_XDX   TREG_RDX
-#else
-# define TREG_XAX   TREG_EAX
-# define TREG_XCX   TREG_ECX
-# define TREG_XDX   TREG_EDX
-#endif
-
-typedef struct ASMInstr {
-    uint16_t sym;
-    uint16_t opcode;
-    uint16_t instr_type;
-    uint8_t nb_ops;
-    uint8_t op_type[MAX_OPERANDS]; /* see OP_xxx */
-} ASMInstr;
-
-typedef struct Operand {
-    uint32_t type;
-    int8_t  reg; /* register, -1 if none */
-    int8_t  reg2; /* second register, -1 if none */
-    uint8_t shift;
-    ExprValue e;
-} Operand;
-
-static const uint8_t reg_to_size[9] = {
-/*
-    [OP_REG8] = 0,
-    [OP_REG16] = 1,
-    [OP_REG32] = 2,
-#ifdef TCC_TARGET_X86_64
-    [OP_REG64] = 3,
-#endif
-*/
-    0, 0, 1, 0, 2, 0, 0, 0, 3
-};
-
-#define NB_TEST_OPCODES 30
-
-static const uint8_t test_bits[NB_TEST_OPCODES] = {
- 0x00, /* o */
- 0x01, /* no */
- 0x02, /* b */
- 0x02, /* c */
- 0x02, /* nae */
- 0x03, /* nb */
- 0x03, /* nc */
- 0x03, /* ae */
- 0x04, /* e */
- 0x04, /* z */
- 0x05, /* ne */
- 0x05, /* nz */
- 0x06, /* be */
- 0x06, /* na */
- 0x07, /* nbe */
- 0x07, /* a */
- 0x08, /* s */
- 0x09, /* ns */
- 0x0a, /* p */
- 0x0a, /* pe */
- 0x0b, /* np */
- 0x0b, /* po */
- 0x0c, /* l */
- 0x0c, /* nge */
- 0x0d, /* nl */
- 0x0d, /* ge */
- 0x0e, /* le */
- 0x0e, /* ng */
- 0x0f, /* nle */
- 0x0f, /* g */
-};
-
-static const uint8_t segment_prefixes[] = {
- 0x26, /* es */
- 0x2e, /* cs */
- 0x36, /* ss */
- 0x3e, /* ds */
- 0x64, /* fs */
- 0x65  /* gs */
-};
-
-static const ASMInstr asm_instrs[] = {
-#define ALT(x) x
-/* This removes a 0x0f in the second byte */
-#define O(o) ((uint64_t) ((((o) & 0xff00) == 0x0f00) ? ((((o) >> 8) & ~0xff) | ((o) & 0xff)) : (o)))
-/* This constructs instr_type from opcode, type and group.  */
-#define T(o,i,g) ((i) | ((g) << OPC_GROUP_SHIFT) | ((((o) & 0xff00) == 0x0f00) ? OPC_0F : 0))
-#define DEF_ASM_OP0(name, opcode)
-#define DEF_ASM_OP0L(name, opcode, group, instr_type) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 0, { 0 } },
-#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 1, { op0 }},
-#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 2, { op0, op1 }},
-#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 3, { op0, op1, op2 }},
-#ifdef TCC_TARGET_X86_64
-# include "x86_64-asm.h"
-#else
-# include "i386-asm.h"
-#endif
-    /* last operation */
-    { 0, },
-};
-
-static const uint16_t op0_codes[] = {
-#define ALT(x)
-#define DEF_ASM_OP0(x, opcode) opcode,
-#define DEF_ASM_OP0L(name, opcode, group, instr_type)
-#define DEF_ASM_OP1(name, opcode, group, instr_type, op0)
-#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1)
-#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2)
-#ifdef TCC_TARGET_X86_64
-# include "x86_64-asm.h"
-#else
-# include "i386-asm.h"
-#endif
-};
-
-static inline int get_reg_shift(TCCState *s1)
-{
-    int shift, v;
-    v = asm_int_expr(s1);
-    switch(v) {
-    case 1:
-        shift = 0;
-        break;
-    case 2:
-        shift = 1;
-        break;
-    case 4:
-        shift = 2;
-        break;
-    case 8:
-        shift = 3;
-        break;
-    default:
-        expect("1, 2, 4 or 8 constant");
-        shift = 0;
-        break;
-    }
-    return shift;
-}
-
-#ifdef TCC_TARGET_X86_64
-static int asm_parse_numeric_reg(int t, unsigned int *type)
-{
-    int reg = -1;
-    if (t >= TOK_IDENT && t < tok_ident) {
-	const char *s = table_ident[t - TOK_IDENT]->str;
-	char c;
-	*type = OP_REG64;
-	if (*s == 'c') {
-	    s++;
-	    *type = OP_CR;
-	}
-	if (*s++ != 'r')
-	  return -1;
-	/* Don't allow leading '0'.  */
-	if ((c = *s++) >= '1' && c <= '9')
-	  reg = c - '0';
-	else
-	  return -1;
-	if ((c = *s) >= '0' && c <= '5')
-	  s++, reg = reg * 10 + c - '0';
-	if (reg > 15)
-	  return -1;
-	if ((c = *s) == 0)
-	  ;
-	else if (*type != OP_REG64)
-	  return -1;
-	else if (c == 'b' && !s[1])
-	  *type = OP_REG8;
-	else if (c == 'w' && !s[1])
-	  *type = OP_REG16;
-	else if (c == 'd' && !s[1])
-	  *type = OP_REG32;
-	else
-	  return -1;
-    }
-    return reg;
-}
-#endif
-
-static int asm_parse_reg(unsigned int *type)
-{
-    int reg = 0;
-    *type = 0;
-    if (tok != '%')
-        goto error_32;
-    next();
-    if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) {
-        reg = tok - TOK_ASM_eax;
-	*type = OP_REG32;
-#ifdef TCC_TARGET_X86_64
-    } else if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) {
-        reg = tok - TOK_ASM_rax;
-	*type = OP_REG64;
-    } else if (tok == TOK_ASM_rip) {
-        reg = -2; /* Probably should use different escape code. */
-	*type = OP_REG64;
-    } else if ((reg = asm_parse_numeric_reg(tok, type)) >= 0
-	       && (*type == OP_REG32 || *type == OP_REG64)) {
-	;
-#endif
-    } else {
-    error_32:
-        expect("register");
-    }
-    next();
-    return reg;
-}
-
-static void parse_operand(TCCState *s1, Operand *op)
-{
-    ExprValue e;
-    int reg, indir;
-    const char *p;
-
-    indir = 0;
-    if (tok == '*') {
-        next();
-        indir = OP_INDIR;
-    }
-
-    if (tok == '%') {
-        next();
-        if (tok >= TOK_ASM_al && tok <= TOK_ASM_db7) {
-            reg = tok - TOK_ASM_al;
-            op->type = 1 << (reg >> 3); /* WARNING: do not change constant order */
-            op->reg = reg & 7;
-            if ((op->type & OP_REG) && op->reg == TREG_XAX)
-                op->type |= OP_EAX;
-            else if (op->type == OP_REG8 && op->reg == TREG_XCX)
-                op->type |= OP_CL;
-            else if (op->type == OP_REG16 && op->reg == TREG_XDX)
-                op->type |= OP_DX;
-        } else if (tok >= TOK_ASM_dr0 && tok <= TOK_ASM_dr7) {
-            op->type = OP_DB;
-            op->reg = tok - TOK_ASM_dr0;
-        } else if (tok >= TOK_ASM_es && tok <= TOK_ASM_gs) {
-            op->type = OP_SEG;
-            op->reg = tok - TOK_ASM_es;
-        } else if (tok == TOK_ASM_st) {
-            op->type = OP_ST;
-            op->reg = 0;
-            next();
-            if (tok == '(') {
-                next();
-                if (tok != TOK_PPNUM)
-                    goto reg_error;
-                p = tokc.str.data;
-                reg = p[0] - '0';
-                if ((unsigned)reg >= 8 || p[1] != '\0')
-                    goto reg_error;
-                op->reg = reg;
-                next();
-                skip(')');
-            }
-            if (op->reg == 0)
-                op->type |= OP_ST0;
-            goto no_skip;
-#ifdef TCC_TARGET_X86_64
-	} else if (tok >= TOK_ASM_spl && tok <= TOK_ASM_dil) {
-	    op->type = OP_REG8 | OP_REG8_LOW;
-	    op->reg = 4 + tok - TOK_ASM_spl;
-        } else if ((op->reg = asm_parse_numeric_reg(tok, &op->type)) >= 0) {
-	    ;
-#endif
-        } else {
-        reg_error:
-            tcc_error("unknown register %%%s", get_tok_str(tok, &tokc));
-        }
-        next();
-    no_skip: ;
-    } else if (tok == '$') {
-        /* constant value */
-        next();
-        asm_expr(s1, &e);
-        op->type = OP_IM32;
-        op->e = e;
-        if (!op->e.sym) {
-            if (op->e.v == (uint8_t)op->e.v)
-                op->type |= OP_IM8;
-            if (op->e.v == (int8_t)op->e.v)
-                op->type |= OP_IM8S;
-            if (op->e.v == (uint16_t)op->e.v)
-                op->type |= OP_IM16;
-#ifdef TCC_TARGET_X86_64
-            if (op->e.v != (int32_t)op->e.v && op->e.v != (uint32_t)op->e.v)
-                op->type = OP_IM64;
-#endif
-        }
-    } else {
-        /* address(reg,reg2,shift) with all variants */
-        op->type = OP_EA;
-        op->reg = -1;
-        op->reg2 = -1;
-        op->shift = 0;
-        if (tok != '(') {
-            asm_expr(s1, &e);
-            op->e = e;
-        } else {
-            next();
-            if (tok == '%') {
-                unget_tok('(');
-                op->e.v = 0;
-                op->e.sym = NULL;
-            } else {
-                /* bracketed offset expression */
-                asm_expr(s1, &e);
-                if (tok != ')')
-                    expect(")");
-                next();
-                op->e.v = e.v;
-                op->e.sym = e.sym;
-            }
-	    op->e.pcrel = 0;
-        }
-        if (tok == '(') {
-	    unsigned int type = 0;
-            next();
-            if (tok != ',') {
-                op->reg = asm_parse_reg(&type);
-            }
-            if (tok == ',') {
-                next();
-                if (tok != ',') {
-                    op->reg2 = asm_parse_reg(&type);
-                }
-                if (tok == ',') {
-                    next();
-                    op->shift = get_reg_shift(s1);
-                }
-            }
-	    if (type & OP_REG32)
-	        op->type |= OP_EA32;
-            skip(')');
-        }
-        if (op->reg == -1 && op->reg2 == -1)
-            op->type |= OP_ADDR;
-    }
-    op->type |= indir;
-}
-
-/* XXX: unify with C code output ? */
-ST_FUNC void gen_expr32(ExprValue *pe)
-{
-    if (pe->pcrel)
-        /* If PC-relative, always set VT_SYM, even without symbol,
-	   so as to force a relocation to be emitted.  */
-	gen_addrpc32(VT_SYM, pe->sym, pe->v);
-    else
-	gen_addr32(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
-}
-
-#ifdef TCC_TARGET_X86_64
-ST_FUNC void gen_expr64(ExprValue *pe)
-{
-    gen_addr64(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
-}
-#endif
-
-/* XXX: unify with C code output ? */
-static void gen_disp32(ExprValue *pe)
-{
-    Sym *sym = pe->sym;
-    if (sym && sym->r == cur_text_section->sh_num) {
-        /* same section: we can output an absolute value. Note
-           that the TCC compiler behaves differently here because
-           it always outputs a relocation to ease (future) code
-           elimination in the linker */
-        gen_le32(pe->v + sym->jnext - ind - 4);
-    } else {
-        if (sym && sym->type.t == VT_VOID) {
-            sym->type.t = VT_FUNC;
-            sym->type.ref = NULL;
-        }
-        gen_addrpc32(VT_SYM, sym, pe->v);
-    }
-}
-
-/* generate the modrm operand */
-static inline int asm_modrm(int reg, Operand *op)
-{
-    int mod, reg1, reg2, sib_reg1;
-
-    if (op->type & (OP_REG | OP_MMX | OP_SSE)) {
-        g(0xc0 + (reg << 3) + op->reg);
-    } else if (op->reg == -1 && op->reg2 == -1) {
-        /* displacement only */
-#ifdef TCC_TARGET_X86_64
-	g(0x04 + (reg << 3));
-	g(0x25);
-#else
-	g(0x05 + (reg << 3));
-#endif
-	gen_expr32(&op->e);
-#ifdef TCC_TARGET_X86_64
-    } else if (op->reg == -2) {
-        ExprValue *pe = &op->e;
-        g(0x05 + (reg << 3));
-        gen_addrpc32(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
-        return ind;
-#endif
-    } else {
-        sib_reg1 = op->reg;
-        /* fist compute displacement encoding */
-        if (sib_reg1 == -1) {
-            sib_reg1 = 5;
-            mod = 0x00;
-        } else if (op->e.v == 0 && !op->e.sym && op->reg != 5) {
-            mod = 0x00;
-        } else if (op->e.v == (int8_t)op->e.v && !op->e.sym) {
-            mod = 0x40;
-        } else {
-            mod = 0x80;
-        }
-        /* compute if sib byte needed */
-        reg1 = op->reg;
-        if (op->reg2 != -1)
-            reg1 = 4;
-        g(mod + (reg << 3) + reg1);
-        if (reg1 == 4) {
-            /* add sib byte */
-            reg2 = op->reg2;
-            if (reg2 == -1)
-                reg2 = 4; /* indicate no index */
-            g((op->shift << 6) + (reg2 << 3) + sib_reg1);
-        }
-        /* add offset */
-        if (mod == 0x40) {
-            g(op->e.v);
-        } else if (mod == 0x80 || op->reg == -1) {
-	    gen_expr32(&op->e);
-        }
-    }
-    return 0;
-}
-
-#ifdef TCC_TARGET_X86_64
-#define REX_W 0x48
-#define REX_R 0x44
-#define REX_X 0x42
-#define REX_B 0x41
-
-static void asm_rex(int width64, Operand *ops, int nb_ops, int *op_type,
-		    int regi, int rmi)
-{
-  unsigned char rex = width64 ? 0x48 : 0;
-  int saw_high_8bit = 0;
-  int i;
-  if (rmi == -1) {
-      /* No mod/rm byte, but we might have a register op nevertheless
-         (we will add it to the opcode later).  */
-      for(i = 0; i < nb_ops; i++) {
-	  if (op_type[i] & (OP_REG | OP_ST)) {
-	      if (ops[i].reg >= 8) {
-		  rex |= REX_B;
-		  ops[i].reg -= 8;
-	      } else if (ops[i].type & OP_REG8_LOW)
-		  rex |= 0x40;
-	      else if (ops[i].type & OP_REG8 && ops[i].reg >= 4)
-		  /* An 8 bit reg >= 4 without REG8 is ah/ch/dh/bh */
-		  saw_high_8bit = ops[i].reg;
-	      break;
-	  }
-      }
-  } else {
-      if (regi != -1) {
-	  if (ops[regi].reg >= 8) {
-	      rex |= REX_R;
-	      ops[regi].reg -= 8;
-	  } else if (ops[regi].type & OP_REG8_LOW)
-	      rex |= 0x40;
-	  else if (ops[regi].type & OP_REG8 && ops[regi].reg >= 4)
-	      /* An 8 bit reg >= 4 without REG8 is ah/ch/dh/bh */
-	      saw_high_8bit = ops[regi].reg;
-      }
-      if (ops[rmi].type & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_EA)) {
-	  if (ops[rmi].reg >= 8) {
-	      rex |= REX_B;
-	      ops[rmi].reg -= 8;
-	  } else if (ops[rmi].type & OP_REG8_LOW)
-	      rex |= 0x40;
-	  else if (ops[rmi].type & OP_REG8 && ops[rmi].reg >= 4)
-	      /* An 8 bit reg >= 4 without REG8 is ah/ch/dh/bh */
-	      saw_high_8bit = ops[rmi].reg;
-      }
-      if (ops[rmi].type & OP_EA && ops[rmi].reg2 >= 8) {
-	  rex |= REX_X;
-	  ops[rmi].reg2 -= 8;
-      }
-  }
-  if (rex) {
-      if (saw_high_8bit)
-	  tcc_error("can't encode register %%%ch when REX prefix is required",
-		    "acdb"[saw_high_8bit-4]);
-      g(rex);
-  }
-}
-#endif
-
-static void maybe_print_stats (void)
-{
-  static int already = 1;
-  if (!already)
-    /* print stats about opcodes */
-    {
-        const struct ASMInstr *pa;
-        int freq[4];
-        int op_vals[500];
-        int nb_op_vals, i, j;
-
-	already = 1;
-        nb_op_vals = 0;
-        memset(freq, 0, sizeof(freq));
-        for(pa = asm_instrs; pa->sym != 0; pa++) {
-            freq[pa->nb_ops]++;
-            //for(i=0;i<pa->nb_ops;i++) {
-                for(j=0;j<nb_op_vals;j++) {
-                    //if (pa->op_type[i] == op_vals[j])
-                    if (pa->instr_type == op_vals[j])
-                        goto found;
-                }
-                //op_vals[nb_op_vals++] = pa->op_type[i];
-                op_vals[nb_op_vals++] = pa->instr_type;
-            found: ;
-            //}
-        }
-        for(i=0;i<nb_op_vals;i++) {
-            int v = op_vals[i];
-            //if ((v & (v - 1)) != 0)
-                printf("%3d: %08x\n", i, v);
-        }
-        printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
-               (int)sizeof(asm_instrs),
-	       (int)sizeof(asm_instrs) / (int)sizeof(ASMInstr),
-               freq[0], freq[1], freq[2], freq[3]);
-    }
-}
-
-ST_FUNC void asm_opcode(TCCState *s1, int opcode)
-{
-    const ASMInstr *pa;
-    int i, modrm_index, modreg_index, reg, v, op1, seg_prefix, pc;
-    int nb_ops, s;
-    Operand ops[MAX_OPERANDS], *pop;
-    int op_type[3]; /* decoded op type */
-    int alltypes;   /* OR of all operand types */
-    int autosize;
-    int p66;
-#ifdef TCC_TARGET_X86_64
-    int rex64;
-#endif
-
-    maybe_print_stats();
-    /* force synthetic ';' after prefix instruction, so we can handle */
-    /* one-line things like "rep stosb" instead of only "rep\nstosb" */
-    if (opcode >= TOK_ASM_wait && opcode <= TOK_ASM_repnz)
-        unget_tok(';');
-
-    /* get operands */
-    pop = ops;
-    nb_ops = 0;
-    seg_prefix = 0;
-    alltypes = 0;
-    for(;;) {
-        if (tok == ';' || tok == TOK_LINEFEED)
-            break;
-        if (nb_ops >= MAX_OPERANDS) {
-            tcc_error("incorrect number of operands");
-        }
-        parse_operand(s1, pop);
-        if (tok == ':') {
-           if (pop->type != OP_SEG || seg_prefix)
-               tcc_error("incorrect prefix");
-           seg_prefix = segment_prefixes[pop->reg];
-           next();
-           parse_operand(s1, pop);
-           if (!(pop->type & OP_EA)) {
-               tcc_error("segment prefix must be followed by memory reference");
-           }
-        }
-        pop++;
-        nb_ops++;
-        if (tok != ',')
-            break;
-        next();
-    }
-
-    s = 0; /* avoid warning */
-
-    /* optimize matching by using a lookup table (no hashing is needed
-       !) */
-    for(pa = asm_instrs; pa->sym != 0; pa++) {
-	int it = pa->instr_type & OPCT_MASK;
-        s = 0;
-        if (it == OPC_FARITH) {
-            v = opcode - pa->sym;
-            if (!((unsigned)v < 8 * 6 && (v % 6) == 0))
-                continue;
-        } else if (it == OPC_ARITH) {
-            if (!(opcode >= pa->sym && opcode < pa->sym + 8*NBWLX))
-                continue;
-            s = (opcode - pa->sym) % NBWLX;
-	    if ((pa->instr_type & OPC_BWLX) == OPC_WLX)
-	      {
-		/* We need to reject the xxxb opcodes that we accepted above.
-		   Note that pa->sym for WLX opcodes is the 'w' token,
-		   to get the 'b' token subtract one.  */
-		if (((opcode - pa->sym + 1) % NBWLX) == 0)
-		    continue;
-	        s++;
-	      }
-        } else if (it == OPC_SHIFT) {
-            if (!(opcode >= pa->sym && opcode < pa->sym + 7*NBWLX))
-                continue;
-            s = (opcode - pa->sym) % NBWLX;
-        } else if (it == OPC_TEST) {
-            if (!(opcode >= pa->sym && opcode < pa->sym + NB_TEST_OPCODES))
-                continue;
-	    /* cmovxx is a test opcode but accepts multiple sizes.
-	       TCC doesn't accept the suffixed mnemonic, instead we 
-	       simply force size autodetection always.  */
-	    if (pa->instr_type & OPC_WLX)
-	        s = NBWLX - 1;
-        } else if (pa->instr_type & OPC_B) {
-#ifdef TCC_TARGET_X86_64
-	    /* Some instructions don't have the full size but only
-	       bwl form.  insb e.g. */
-	    if ((pa->instr_type & OPC_WLQ) != OPC_WLQ
-		&& !(opcode >= pa->sym && opcode < pa->sym + NBWLX-1))
-	        continue;
-#endif
-            if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX))
-                continue;
-            s = opcode - pa->sym;
-        } else if (pa->instr_type & OPC_WLX) {
-            if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX-1))
-                continue;
-            s = opcode - pa->sym + 1;
-        } else {
-            if (pa->sym != opcode)
-                continue;
-        }
-        if (pa->nb_ops != nb_ops)
-            continue;
-#ifdef TCC_TARGET_X86_64
-	/* Special case for moves.  Selecting the IM64->REG64 form
-	   should only be done if we really have an >32bit imm64, and that
-	   is hardcoded.  Ignore it here.  */
-	if (pa->opcode == 0xb0 && ops[0].type != OP_IM64
-	    && (ops[1].type & OP_REG) == OP_REG64
-	    && !(pa->instr_type & OPC_0F))
-	    continue;
-#endif
-        /* now decode and check each operand */
-	alltypes = 0;
-        for(i = 0; i < nb_ops; i++) {
-            int op1, op2;
-            op1 = pa->op_type[i];
-            op2 = op1 & 0x1f;
-            switch(op2) {
-            case OPT_IM:
-                v = OP_IM8 | OP_IM16 | OP_IM32;
-                break;
-            case OPT_REG:
-                v = OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64;
-                break;
-            case OPT_REGW:
-                v = OP_REG16 | OP_REG32 | OP_REG64;
-                break;
-            case OPT_IMW:
-                v = OP_IM16 | OP_IM32;
-                break;
-	    case OPT_MMXSSE:
-		v = OP_MMX | OP_SSE;
-		break;
-	    case OPT_DISP:
-	    case OPT_DISP8:
-		v = OP_ADDR;
-		break;
-            default:
-                v = 1 << op2;
-                break;
-            }
-            if (op1 & OPT_EA)
-                v |= OP_EA;
-	    op_type[i] = v;
-            if ((ops[i].type & v) == 0)
-                goto next;
-	    alltypes |= ops[i].type;
-        }
-        /* all is matching ! */
-        break;
-    next: ;
-    }
-    if (pa->sym == 0) {
-        if (opcode >= TOK_ASM_first && opcode <= TOK_ASM_last) {
-            int b;
-            b = op0_codes[opcode - TOK_ASM_first];
-            if (b & 0xff00) 
-                g(b >> 8);
-            g(b);
-            return;
-        } else if (opcode <= TOK_ASM_alllast) {
-            tcc_error("bad operand with opcode '%s'",
-                  get_tok_str(opcode, NULL));
-        } else {
-            tcc_error("unknown opcode '%s'",
-                  get_tok_str(opcode, NULL));
-        }
-    }
-    /* if the size is unknown, then evaluate it (OPC_B or OPC_WL case) */
-    autosize = NBWLX-1;
-#ifdef TCC_TARGET_X86_64
-    /* XXX the autosize should rather be zero, to not have to adjust this
-       all the time.  */
-    if ((pa->instr_type & OPC_BWLQ) == OPC_B)
-        autosize = NBWLX-2;
-#endif
-    if (s == autosize) {
-	/* Check for register operands providing hints about the size.
-	   Start from the end, i.e. destination operands.  This matters
-	   only for opcodes accepting different sized registers, lar and lsl
-	   are such opcodes.  */
-        for(i = nb_ops - 1; s == autosize && i >= 0; i--) {
-            if ((ops[i].type & OP_REG) && !(op_type[i] & (OP_CL | OP_DX)))
-                s = reg_to_size[ops[i].type & OP_REG];
-        }
-        if (s == autosize) {
-            if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) &&
-                (ops[0].type & (OP_SEG | OP_IM8S | OP_IM32)))
-                s = 2;
-	    else if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) &&
-		     (ops[0].type & OP_EA))
-	        s = NBWLX - 2;
-            else
-                tcc_error("cannot infer opcode suffix");
-        }
-    }
-
-#ifdef TCC_TARGET_X86_64
-    /* Generate addr32 prefix if needed */
-    for(i = 0; i < nb_ops; i++) {
-        if (ops[i].type & OP_EA32) {
-	    g(0x67);
-	    break;
-        }
-    }
-#endif
-    /* generate data16 prefix if needed */
-    p66 = 0;
-    if (s == 1)
-        p66 = 1;
-    else {
-	/* accepting mmx+sse in all operands --> needs 0x66 to
-	   switch to sse mode.  Accepting only sse in an operand --> is
-	   already SSE insn and needs 0x66/f2/f3 handling.  */
-        for (i = 0; i < nb_ops; i++)
-            if ((op_type[i] & (OP_MMX | OP_SSE)) == (OP_MMX | OP_SSE)
-	        && ops[i].type & OP_SSE)
-	        p66 = 1;
-    }
-    if (p66)
-        g(0x66);
-#ifdef TCC_TARGET_X86_64
-    rex64 = 0;
-    if (pa->instr_type & OPC_48)
-        rex64 = 1;
-    else if (s == 3 || (alltypes & OP_REG64)) {
-        /* generate REX prefix */
-	int default64 = 0;
-	for(i = 0; i < nb_ops; i++) {
-	    if (op_type[i] == OP_REG64 && pa->opcode != 0xb8) {
-		/* If only 64bit regs are accepted in one operand
-		   this is a default64 instruction without need for
-		   REX prefixes, except for movabs(0xb8).  */
-		default64 = 1;
-		break;
-	    }
-	}
-	/* XXX find better encoding for the default64 instructions.  */
-        if (((opcode != TOK_ASM_push && opcode != TOK_ASM_pop
-	      && opcode != TOK_ASM_pushw && opcode != TOK_ASM_pushl
-	      && opcode != TOK_ASM_pushq && opcode != TOK_ASM_popw
-	      && opcode != TOK_ASM_popl && opcode != TOK_ASM_popq
-	      && opcode != TOK_ASM_call && opcode != TOK_ASM_jmp))
-	    && !default64)
-            rex64 = 1;
-    }
-#endif
-
-    /* now generates the operation */
-    if (OPCT_IS(pa->instr_type, OPC_FWAIT))
-        g(0x9b);
-    if (seg_prefix)
-        g(seg_prefix);
-
-    v = pa->opcode;
-    if (pa->instr_type & OPC_0F)
-        v = ((v & ~0xff) << 8) | 0x0f00 | (v & 0xff);
-    if ((v == 0x69 || v == 0x6b) && nb_ops == 2) {
-        /* kludge for imul $im, %reg */
-        nb_ops = 3;
-        ops[2] = ops[1];
-        op_type[2] = op_type[1];
-    } else if (v == 0xcd && ops[0].e.v == 3 && !ops[0].e.sym) {
-        v--; /* int $3 case */
-        nb_ops = 0;
-    } else if ((v == 0x06 || v == 0x07)) {
-        if (ops[0].reg >= 4) {
-            /* push/pop %fs or %gs */
-            v = 0x0fa0 + (v - 0x06) + ((ops[0].reg - 4) << 3);
-        } else {
-            v += ops[0].reg << 3;
-        }
-        nb_ops = 0;
-    } else if (v <= 0x05) {
-        /* arith case */
-        v += ((opcode - TOK_ASM_addb) / NBWLX) << 3;
-    } else if ((pa->instr_type & (OPCT_MASK | OPC_MODRM)) == OPC_FARITH) {
-        /* fpu arith case */
-        v += ((opcode - pa->sym) / 6) << 3;
-    }
-
-    /* search which operand will be used for modrm */
-    modrm_index = -1;
-    modreg_index = -1;
-    if (pa->instr_type & OPC_MODRM) {
-	if (!nb_ops) {
-	    /* A modrm opcode without operands is a special case (e.g. mfence).
-	       It has a group and acts as if there's an register operand 0
-	       (ax).  */
-	    i = 0;
-	    ops[i].type = OP_REG;
-	    ops[i].reg = 0;
-	    goto modrm_found;
-	}
-        /* first look for an ea operand */
-        for(i = 0;i < nb_ops; i++) {
-            if (op_type[i] & OP_EA)
-                goto modrm_found;
-        }
-        /* then if not found, a register or indirection (shift instructions) */
-        for(i = 0;i < nb_ops; i++) {
-            if (op_type[i] & (OP_REG | OP_MMX | OP_SSE | OP_INDIR))
-                goto modrm_found;
-        }
-#ifdef ASM_DEBUG
-        tcc_error("bad op table");
-#endif
-    modrm_found:
-        modrm_index = i;
-        /* if a register is used in another operand then it is
-           used instead of group */
-        for(i = 0;i < nb_ops; i++) {
-            int t = op_type[i];
-            if (i != modrm_index &&
-                (t & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_TR | OP_DB | OP_SEG))) {
-                modreg_index = i;
-                break;
-            }
-        }
-    }
-#ifdef TCC_TARGET_X86_64
-    asm_rex (rex64, ops, nb_ops, op_type, modreg_index, modrm_index);
-#endif
-
-    if (pa->instr_type & OPC_REG) {
-        /* mov $im, %reg case */
-        if (v == 0xb0 && s >= 1)
-            v += 7;
-        for(i = 0; i < nb_ops; i++) {
-            if (op_type[i] & (OP_REG | OP_ST)) {
-                v += ops[i].reg;
-                break;
-            }
-        }
-    }
-    if (pa->instr_type & OPC_B)
-        v += s >= 1;
-    if (nb_ops == 1 && pa->op_type[0] == OPT_DISP8) {
-        Sym *sym;
-        int jmp_disp;
-
-        /* see if we can really generate the jump with a byte offset */
-        sym = ops[0].e.sym;
-        if (!sym)
-            goto no_short_jump;
-        if (sym->r != cur_text_section->sh_num)
-            goto no_short_jump;
-        jmp_disp = ops[0].e.v + sym->jnext - ind - 2 - (v >= 0xff);
-        if (jmp_disp == (int8_t)jmp_disp) {
-            /* OK to generate jump */
-	    ops[0].e.sym = 0;
-            ops[0].e.v = jmp_disp;
-	    op_type[0] = OP_IM8S;
-        } else {
-        no_short_jump:
-	    /* long jump will be allowed. need to modify the
-	       opcode slightly */
-	    if (v == 0xeb) /* jmp */
-	        v = 0xe9;
-	    else if (v == 0x70) /* jcc */
-	        v += 0x0f10;
-	    else
-	        tcc_error("invalid displacement");
-        }
-    }
-    if (OPCT_IS(pa->instr_type, OPC_TEST))
-        v += test_bits[opcode - pa->sym];
-    op1 = v >> 16;
-    if (op1)
-        g(op1);
-    op1 = (v >> 8) & 0xff;
-    if (op1)
-        g(op1);
-    g(v);
-
-    if (OPCT_IS(pa->instr_type, OPC_SHIFT)) {
-        reg = (opcode - pa->sym) / NBWLX;
-        if (reg == 6)
-            reg = 7;
-    } else if (OPCT_IS(pa->instr_type, OPC_ARITH)) {
-        reg = (opcode - pa->sym) / NBWLX;
-    } else if (OPCT_IS(pa->instr_type, OPC_FARITH)) {
-        reg = (opcode - pa->sym) / 6;
-    } else {
-        reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7;
-    }
-
-    pc = 0;
-    if (pa->instr_type & OPC_MODRM) {
-        /* if a register is used in another operand then it is
-           used instead of group */
-	if (modreg_index >= 0)
-	    reg = ops[modreg_index].reg;
-        pc = asm_modrm(reg, &ops[modrm_index]);
-    }
-
-    /* emit constants */
-#ifndef TCC_TARGET_X86_64
-    if (!(pa->instr_type & OPC_0F)
-	&& (pa->opcode == 0x9a || pa->opcode == 0xea)) {
-        /* ljmp or lcall kludge */
-	gen_expr32(&ops[1].e);
-        if (ops[0].e.sym)
-            tcc_error("cannot relocate");
-        gen_le16(ops[0].e.v);
-        return;
-    }
-#endif
-    for(i = 0;i < nb_ops; i++) {
-        v = op_type[i];
-        if (v & (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64 | OP_IM8S | OP_ADDR)) {
-            /* if multiple sizes are given it means we must look
-               at the op size */
-            if ((v | OP_IM8 | OP_IM64) == (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64)) {
-                if (s == 0)
-                    v = OP_IM8;
-                else if (s == 1)
-                    v = OP_IM16;
-                else if (s == 2 || (v & OP_IM64) == 0)
-                    v = OP_IM32;
-                else
-                    v = OP_IM64;
-            }
-
-            if ((v & (OP_IM8 | OP_IM8S | OP_IM16)) && ops[i].e.sym)
-                tcc_error("cannot relocate");
-
-            if (v & (OP_IM8 | OP_IM8S)) {
-                g(ops[i].e.v);
-            } else if (v & OP_IM16) {
-                gen_le16(ops[i].e.v);
-#ifdef TCC_TARGET_X86_64
-            } else if (v & OP_IM64) {
-                gen_expr64(&ops[i].e);
-#endif
-	    } else if (pa->op_type[i] == OPT_DISP || pa->op_type[i] == OPT_DISP8) {
-                gen_disp32(&ops[i].e);
-            } else {
-                gen_expr32(&ops[i].e);
-            }
-        }
-    }
-
-    /* after immediate operands, adjust pc-relative address */
-    if (pc)
-        add32le(cur_text_section->data + pc - 4, pc - ind);
-}
-
-/* return the constraint priority (we allocate first the lowest
-   numbered constraints) */
-static inline int constraint_priority(const char *str)
-{
-    int priority, c, pr;
-
-    /* we take the lowest priority */
-    priority = 0;
-    for(;;) {
-        c = *str;
-        if (c == '\0')
-            break;
-        str++;
-        switch(c) {
-        case 'A':
-            pr = 0;
-            break;
-        case 'a':
-        case 'b':
-        case 'c':
-        case 'd':
-        case 'S':
-        case 'D':
-            pr = 1;
-            break;
-        case 'q':
-            pr = 2;
-            break;
-        case 'r':
-	case 'R':
-	case 'p':
-            pr = 3;
-            break;
-        case 'N':
-        case 'M':
-        case 'I':
-	case 'e':
-        case 'i':
-        case 'm':
-        case 'g':
-            pr = 4;
-            break;
-        default:
-            tcc_error("unknown constraint '%c'", c);
-            pr = 0;
-        }
-        if (pr > priority)
-            priority = pr;
-    }
-    return priority;
-}
-
-static const char *skip_constraint_modifiers(const char *p)
-{
-    while (*p == '=' || *p == '&' || *p == '+' || *p == '%')
-        p++;
-    return p;
-}
-
-/* If T (a token) is of the form "%reg" returns the register
-   number and type, otherwise return -1.  */
-ST_FUNC int asm_parse_regvar (int t)
-{
-    const char *s;
-    Operand op;
-    if (t < TOK_IDENT)
-        return -1;
-    s = table_ident[t - TOK_IDENT]->str;
-    if (s[0] != '%')
-        return -1;
-    t = tok_alloc(s+1, strlen(s)-1)->tok;
-    unget_tok(t);
-    unget_tok('%');
-    parse_operand(tcc_state, &op);
-    /* Accept only integer regs for now.  */
-    if (op.type & OP_REG)
-        return op.reg;
-    else
-        return -1;
-}
-
-#define REG_OUT_MASK 0x01
-#define REG_IN_MASK  0x02
-
-#define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask)
-
-ST_FUNC void asm_compute_constraints(ASMOperand *operands,
-                                    int nb_operands, int nb_outputs,
-                                    const uint8_t *clobber_regs,
-                                    int *pout_reg)
-{
-    ASMOperand *op;
-    int sorted_op[MAX_ASM_OPERANDS];
-    int i, j, k, p1, p2, tmp, reg, c, reg_mask;
-    const char *str;
-    uint8_t regs_allocated[NB_ASM_REGS];
-
-    /* init fields */
-    for(i=0;i<nb_operands;i++) {
-        op = &operands[i];
-        op->input_index = -1;
-        op->ref_index = -1;
-        op->reg = -1;
-        op->is_memory = 0;
-        op->is_rw = 0;
-    }
-    /* compute constraint priority and evaluate references to output
-       constraints if input constraints */
-    for(i=0;i<nb_operands;i++) {
-        op = &operands[i];
-        str = op->constraint;
-        str = skip_constraint_modifiers(str);
-        if (isnum(*str) || *str == '[') {
-            /* this is a reference to another constraint */
-            k = find_constraint(operands, nb_operands, str, NULL);
-            if ((unsigned)k >= i || i < nb_outputs)
-                tcc_error("invalid reference in constraint %d ('%s')",
-                      i, str);
-            op->ref_index = k;
-            if (operands[k].input_index >= 0)
-                tcc_error("cannot reference twice the same operand");
-            operands[k].input_index = i;
-            op->priority = 5;
-	} else if ((op->vt->r & VT_VALMASK) == VT_LOCAL
-		   && op->vt->sym
-		   && (reg = op->vt->sym->r & VT_VALMASK) < VT_CONST) {
-	    op->priority = 1;
-	    op->reg = reg;
-        } else {
-            op->priority = constraint_priority(str);
-        }
-    }
-
-    /* sort operands according to their priority */
-    for(i=0;i<nb_operands;i++)
-        sorted_op[i] = i;
-    for(i=0;i<nb_operands - 1;i++) {
-        for(j=i+1;j<nb_operands;j++) {
-            p1 = operands[sorted_op[i]].priority;
-            p2 = operands[sorted_op[j]].priority;
-            if (p2 < p1) {
-                tmp = sorted_op[i];
-                sorted_op[i] = sorted_op[j];
-                sorted_op[j] = tmp;
-            }
-        }
-    }
-
-    for(i = 0;i < NB_ASM_REGS; i++) {
-        if (clobber_regs[i])
-            regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK;
-        else
-            regs_allocated[i] = 0;
-    }
-    /* esp cannot be used */
-    regs_allocated[4] = REG_IN_MASK | REG_OUT_MASK;
-    /* ebp cannot be used yet */
-    regs_allocated[5] = REG_IN_MASK | REG_OUT_MASK;
-
-    /* allocate registers and generate corresponding asm moves */
-    for(i=0;i<nb_operands;i++) {
-        j = sorted_op[i];
-        op = &operands[j];
-        str = op->constraint;
-        /* no need to allocate references */
-        if (op->ref_index >= 0)
-            continue;
-        /* select if register is used for output, input or both */
-        if (op->input_index >= 0) {
-            reg_mask = REG_IN_MASK | REG_OUT_MASK;
-        } else if (j < nb_outputs) {
-            reg_mask = REG_OUT_MASK;
-        } else {
-            reg_mask = REG_IN_MASK;
-        }
-	if (op->reg >= 0) {
-	    if (is_reg_allocated(op->reg))
-	        tcc_error("asm regvar requests register that's taken already");
-	    reg = op->reg;
-	    goto reg_found;
-	}
-    try_next:
-        c = *str++;
-        switch(c) {
-        case '=':
-            goto try_next;
-        case '+':
-            op->is_rw = 1;
-            /* FALL THRU */
-        case '&':
-            if (j >= nb_outputs)
-                tcc_error("'%c' modifier can only be applied to outputs", c);
-            reg_mask = REG_IN_MASK | REG_OUT_MASK;
-            goto try_next;
-        case 'A':
-            /* allocate both eax and edx */
-            if (is_reg_allocated(TREG_XAX) ||
-                is_reg_allocated(TREG_XDX))
-                goto try_next;
-            op->is_llong = 1;
-            op->reg = TREG_XAX;
-            regs_allocated[TREG_XAX] |= reg_mask;
-            regs_allocated[TREG_XDX] |= reg_mask;
-            break;
-        case 'a':
-            reg = TREG_XAX;
-            goto alloc_reg;
-        case 'b':
-            reg = 3;
-            goto alloc_reg;
-        case 'c':
-            reg = TREG_XCX;
-            goto alloc_reg;
-        case 'd':
-            reg = TREG_XDX;
-            goto alloc_reg;
-        case 'S':
-            reg = 6;
-            goto alloc_reg;
-        case 'D':
-            reg = 7;
-        alloc_reg:
-            if (is_reg_allocated(reg))
-                goto try_next;
-            goto reg_found;
-        case 'q':
-            /* eax, ebx, ecx or edx */
-            for(reg = 0; reg < 4; reg++) {
-                if (!is_reg_allocated(reg))
-                    goto reg_found;
-            }
-            goto try_next;
-        case 'r':
-	case 'R':
-	case 'p': /* A general address, for x86(64) any register is acceptable*/
-            /* any general register */
-            for(reg = 0; reg < 8; reg++) {
-                if (!is_reg_allocated(reg))
-                    goto reg_found;
-            }
-            goto try_next;
-        reg_found:
-            /* now we can reload in the register */
-            op->is_llong = 0;
-            op->reg = reg;
-            regs_allocated[reg] |= reg_mask;
-            break;
-	case 'e':
-        case 'i':
-            if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST))
-                goto try_next;
-            break;
-        case 'I':
-        case 'N':
-        case 'M':
-            if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST))
-                goto try_next;
-            break;
-        case 'm':
-        case 'g':
-            /* nothing special to do because the operand is already in
-               memory, except if the pointer itself is stored in a
-               memory variable (VT_LLOCAL case) */
-            /* XXX: fix constant case */
-            /* if it is a reference to a memory zone, it must lie
-               in a register, so we reserve the register in the
-               input registers and a load will be generated
-               later */
-            if (j < nb_outputs || c == 'm') {
-                if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
-                    /* any general register */
-                    for(reg = 0; reg < 8; reg++) {
-                        if (!(regs_allocated[reg] & REG_IN_MASK))
-                            goto reg_found1;
-                    }
-                    goto try_next;
-                reg_found1:
-                    /* now we can reload in the register */
-                    regs_allocated[reg] |= REG_IN_MASK;
-                    op->reg = reg;
-                    op->is_memory = 1;
-                }
-            }
-            break;
-        default:
-            tcc_error("asm constraint %d ('%s') could not be satisfied",
-                  j, op->constraint);
-            break;
-        }
-        /* if a reference is present for that operand, we assign it too */
-        if (op->input_index >= 0) {
-            operands[op->input_index].reg = op->reg;
-            operands[op->input_index].is_llong = op->is_llong;
-        }
-    }
-
-    /* compute out_reg. It is used to store outputs registers to memory
-       locations references by pointers (VT_LLOCAL case) */
-    *pout_reg = -1;
-    for(i=0;i<nb_operands;i++) {
-        op = &operands[i];
-        if (op->reg >= 0 &&
-            (op->vt->r & VT_VALMASK) == VT_LLOCAL  &&
-            !op->is_memory) {
-            for(reg = 0; reg < 8; reg++) {
-                if (!(regs_allocated[reg] & REG_OUT_MASK))
-                    goto reg_found2;
-            }
-            tcc_error("could not find free output register for reloading");
-        reg_found2:
-            *pout_reg = reg;
-            break;
-        }
-    }
-
-    /* print sorted constraints */
-#ifdef ASM_DEBUG
-    for(i=0;i<nb_operands;i++) {
-        j = sorted_op[i];
-        op = &operands[j];
-        printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n",
-               j,
-               op->id ? get_tok_str(op->id, NULL) : "",
-               op->constraint,
-               op->vt->r,
-               op->reg);
-    }
-    if (*pout_reg >= 0)
-        printf("out_reg=%d\n", *pout_reg);
-#endif
-}
-
-ST_FUNC void subst_asm_operand(CString *add_str,
-                              SValue *sv, int modifier)
-{
-    int r, reg, size, val;
-    char buf[64];
-
-    r = sv->r;
-    if ((r & VT_VALMASK) == VT_CONST) {
-        if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n' &&
-	    modifier != 'P')
-            cstr_ccat(add_str, '$');
-        if (r & VT_SYM) {
-	    const char *name = get_tok_str(sv->sym->v, NULL);
-	    if (sv->sym->v >= SYM_FIRST_ANOM) {
-		/* In case of anonymous symbols ("L.42", used
-		   for static data labels) we can't find them
-		   in the C symbol table when later looking up
-		   this name.  So enter them now into the asm label
-		   list when we still know the symbol.  */
-		get_asm_sym(tok_alloc(name, strlen(name))->tok, sv->sym);
-	    }
-            cstr_cat(add_str, name, -1);
-            if ((uint32_t)sv->c.i == 0)
-                goto no_offset;
-	    cstr_ccat(add_str, '+');
-        }
-        val = sv->c.i;
-        if (modifier == 'n')
-            val = -val;
-        snprintf(buf, sizeof(buf), "%d", (int)sv->c.i);
-        cstr_cat(add_str, buf, -1);
-    no_offset:;
-#ifdef TCC_TARGET_X86_64
-        if (r & VT_LVAL)
-            cstr_cat(add_str, "(%rip)", -1);
-#endif
-    } else if ((r & VT_VALMASK) == VT_LOCAL) {
-#ifdef TCC_TARGET_X86_64
-        snprintf(buf, sizeof(buf), "%d(%%rbp)", (int)sv->c.i);
-#else
-        snprintf(buf, sizeof(buf), "%d(%%ebp)", (int)sv->c.i);
-#endif
-        cstr_cat(add_str, buf, -1);
-    } else if (r & VT_LVAL) {
-        reg = r & VT_VALMASK;
-        if (reg >= VT_CONST)
-            tcc_error("internal compiler error");
-        snprintf(buf, sizeof(buf), "(%%%s)",
-#ifdef TCC_TARGET_X86_64
-                 get_tok_str(TOK_ASM_rax + reg, NULL)
-#else
-                 get_tok_str(TOK_ASM_eax + reg, NULL)
-#endif
-		 );
-        cstr_cat(add_str, buf, -1);
-    } else {
-        /* register case */
-        reg = r & VT_VALMASK;
-        if (reg >= VT_CONST)
-            tcc_error("internal compiler error");
-
-        /* choose register operand size */
-        if ((sv->type.t & VT_BTYPE) == VT_BYTE ||
-	    (sv->type.t & VT_BTYPE) == VT_BOOL)
-            size = 1;
-        else if ((sv->type.t & VT_BTYPE) == VT_SHORT)
-            size = 2;
-#ifdef TCC_TARGET_X86_64
-        else if ((sv->type.t & VT_BTYPE) == VT_LLONG ||
-		 (sv->type.t & VT_BTYPE) == VT_PTR)
-            size = 8;
-#endif
-        else
-            size = 4;
-        if (size == 1 && reg >= 4)
-            size = 4;
-
-        if (modifier == 'b') {
-            if (reg >= 4)
-                tcc_error("cannot use byte register");
-            size = 1;
-        } else if (modifier == 'h') {
-            if (reg >= 4)
-                tcc_error("cannot use byte register");
-            size = -1;
-        } else if (modifier == 'w') {
-            size = 2;
-        } else if (modifier == 'k') {
-            size = 4;
-#ifdef TCC_TARGET_X86_64
-        } else if (modifier == 'q') {
-            size = 8;
-#endif
-        }
-
-        switch(size) {
-        case -1:
-            reg = TOK_ASM_ah + reg;
-            break;
-        case 1:
-            reg = TOK_ASM_al + reg;
-            break;
-        case 2:
-            reg = TOK_ASM_ax + reg;
-            break;
-        default:
-            reg = TOK_ASM_eax + reg;
-            break;
-#ifdef TCC_TARGET_X86_64
-        case 8:
-            reg = TOK_ASM_rax + reg;
-            break;
-#endif
-        }
-        snprintf(buf, sizeof(buf), "%%%s", get_tok_str(reg, NULL));
-        cstr_cat(add_str, buf, -1);
-    }
-}
-
-/* generate prolog and epilog code for asm statement */
-ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands,
-                         int nb_outputs, int is_output,
-                         uint8_t *clobber_regs,
-                         int out_reg)
-{
-    uint8_t regs_allocated[NB_ASM_REGS];
-    ASMOperand *op;
-    int i, reg;
-
-    /* Strictly speaking %Xbp and %Xsp should be included in the
-       call-preserved registers, but currently it doesn't matter.  */
-#ifdef TCC_TARGET_X86_64
-#ifdef TCC_TARGET_PE
-    static uint8_t reg_saved[] = { 3, 6, 7, 12, 13, 14, 15 };
-#else
-    static uint8_t reg_saved[] = { 3, 12, 13, 14, 15 };
-#endif
-#else
-    static uint8_t reg_saved[] = { 3, 6, 7 };
-#endif
-
-    /* mark all used registers */
-    memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated));
-    for(i = 0; i < nb_operands;i++) {
-        op = &operands[i];
-        if (op->reg >= 0)
-            regs_allocated[op->reg] = 1;
-    }
-    if (!is_output) {
-        /* generate reg save code */
-        for(i = 0; i < sizeof(reg_saved)/sizeof(reg_saved[0]); i++) {
-            reg = reg_saved[i];
-            if (regs_allocated[reg]) {
-		if (reg >= 8)
-		  g(0x41), reg-=8;
-                g(0x50 + reg);
-            }
-        }
-
-        /* generate load code */
-        for(i = 0; i < nb_operands; i++) {
-            op = &operands[i];
-            if (op->reg >= 0) {
-                if ((op->vt->r & VT_VALMASK) == VT_LLOCAL &&
-                    op->is_memory) {
-                    /* memory reference case (for both input and
-                       output cases) */
-                    SValue sv;
-                    sv = *op->vt;
-                    sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL | VT_LVAL;
-                    sv.type.t = VT_PTR;
-                    load(op->reg, &sv);
-                } else if (i >= nb_outputs || op->is_rw) {
-                    /* load value in register */
-                    load(op->reg, op->vt);
-                    if (op->is_llong) {
-                        SValue sv;
-                        sv = *op->vt;
-                        sv.c.i += 4;
-                        load(TREG_XDX, &sv);
-                    }
-                }
-            }
-        }
-    } else {
-        /* generate save code */
-        for(i = 0 ; i < nb_outputs; i++) {
-            op = &operands[i];
-            if (op->reg >= 0) {
-                if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
-                    if (!op->is_memory) {
-                        SValue sv;
-                        sv = *op->vt;
-                        sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
-			sv.type.t = VT_PTR;
-                        load(out_reg, &sv);
-
-			sv = *op->vt;
-                        sv.r = (sv.r & ~VT_VALMASK) | out_reg;
-                        store(op->reg, &sv);
-                    }
-                } else {
-                    store(op->reg, op->vt);
-                    if (op->is_llong) {
-                        SValue sv;
-                        sv = *op->vt;
-                        sv.c.i += 4;
-                        store(TREG_XDX, &sv);
-                    }
-                }
-            }
-        }
-        /* generate reg restore code */
-        for(i = sizeof(reg_saved)/sizeof(reg_saved[0]) - 1; i >= 0; i--) {
-            reg = reg_saved[i];
-            if (regs_allocated[reg]) {
-		if (reg >= 8)
-		  g(0x41), reg-=8;
-                g(0x58 + reg);
-            }
-        }
-    }
-}
-
-ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str)
-{
-    int reg;
-    TokenSym *ts;
-#ifdef TCC_TARGET_X86_64
-    unsigned int type;
-#endif
-
-    if (!strcmp(str, "memory") ||
-        !strcmp(str, "cc") ||
-	!strcmp(str, "flags"))
-        return;
-    ts = tok_alloc(str, strlen(str));
-    reg = ts->tok;
-    if (reg >= TOK_ASM_eax && reg <= TOK_ASM_edi) {
-        reg -= TOK_ASM_eax;
-    } else if (reg >= TOK_ASM_ax && reg <= TOK_ASM_di) {
-        reg -= TOK_ASM_ax;
-#ifdef TCC_TARGET_X86_64
-    } else if (reg >= TOK_ASM_rax && reg <= TOK_ASM_rdi) {
-        reg -= TOK_ASM_rax;
-    } else if ((reg = asm_parse_numeric_reg(reg, &type)) >= 0) {
-	;
-#endif
-    } else {
-        tcc_error("invalid clobber register '%s'", str);
-    }
-    clobber_regs[reg] = 1;
-}
diff --git a/tinyc/i386-asm.h b/tinyc/i386-asm.h
deleted file mode 100644
index 65d517955..000000000
--- a/tinyc/i386-asm.h
+++ /dev/null
@@ -1,480 +0,0 @@
-     DEF_ASM_OP0(clc, 0xf8) /* must be first OP0 */
-     DEF_ASM_OP0(cld, 0xfc)
-     DEF_ASM_OP0(cli, 0xfa)
-     DEF_ASM_OP0(clts, 0x0f06)
-     DEF_ASM_OP0(cmc, 0xf5)
-     DEF_ASM_OP0(lahf, 0x9f)
-     DEF_ASM_OP0(sahf, 0x9e)
-     DEF_ASM_OP0(pusha, 0x60)
-     DEF_ASM_OP0(popa, 0x61)
-     DEF_ASM_OP0(pushfl, 0x9c)
-     DEF_ASM_OP0(popfl, 0x9d)
-     DEF_ASM_OP0(pushf, 0x9c)
-     DEF_ASM_OP0(popf, 0x9d)
-     DEF_ASM_OP0(stc, 0xf9)
-     DEF_ASM_OP0(std, 0xfd)
-     DEF_ASM_OP0(sti, 0xfb)
-     DEF_ASM_OP0(aaa, 0x37)
-     DEF_ASM_OP0(aas, 0x3f)
-     DEF_ASM_OP0(daa, 0x27)
-     DEF_ASM_OP0(das, 0x2f)
-     DEF_ASM_OP0(aad, 0xd50a)
-     DEF_ASM_OP0(aam, 0xd40a)
-     DEF_ASM_OP0(cbw, 0x6698)
-     DEF_ASM_OP0(cwd, 0x6699)
-     DEF_ASM_OP0(cwde, 0x98)
-     DEF_ASM_OP0(cdq, 0x99)
-     DEF_ASM_OP0(cbtw, 0x6698)
-     DEF_ASM_OP0(cwtl, 0x98)
-     DEF_ASM_OP0(cwtd, 0x6699)
-     DEF_ASM_OP0(cltd, 0x99)
-     DEF_ASM_OP0(int3, 0xcc)
-     DEF_ASM_OP0(into, 0xce)
-     DEF_ASM_OP0(iret, 0xcf)
-     DEF_ASM_OP0(rsm, 0x0faa)
-     DEF_ASM_OP0(hlt, 0xf4)
-     DEF_ASM_OP0(nop, 0x90)
-     DEF_ASM_OP0(pause, 0xf390)
-     DEF_ASM_OP0(xlat, 0xd7)
-
-     /* strings */
-ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWLX))
-ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWLX))
-
-ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWL))
-ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWL))
-
-ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWLX))
-ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWLX))
-
-ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWLX))
-ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWLX))
-
-ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWLX))
-ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWLX))
-
-ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWLX))
-ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWLX))
-
-     /* bits */
-     
-ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WLX, OPT_REGW | OPT_EA, OPT_REGW))
-ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WLX, OPT_REGW | OPT_EA, OPT_REGW))
-
-ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WLX, OPT_REGW, OPT_REGW | OPT_EA))
-ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WLX, OPT_IM8, OPT_REGW | OPT_EA))
-
-ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WLX, OPT_REGW, OPT_REGW | OPT_EA))
-ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WLX, OPT_IM8, OPT_REGW | OPT_EA))
-
-ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WLX, OPT_REGW, OPT_REGW | OPT_EA))
-ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WLX, OPT_IM8, OPT_REGW | OPT_EA))
-
-ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WLX, OPT_REGW, OPT_REGW | OPT_EA))
-ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WLX, OPT_IM8, OPT_REGW | OPT_EA))
-
-     /* prefixes */
-     DEF_ASM_OP0(wait, 0x9b)
-     DEF_ASM_OP0(fwait, 0x9b)
-     DEF_ASM_OP0(aword, 0x67)
-     DEF_ASM_OP0(addr16, 0x67)
-     ALT(DEF_ASM_OP0(word, 0x66))
-     DEF_ASM_OP0(data16, 0x66)
-     DEF_ASM_OP0(lock, 0xf0)
-     DEF_ASM_OP0(rep, 0xf3)
-     DEF_ASM_OP0(repe, 0xf3)
-     DEF_ASM_OP0(repz, 0xf3)
-     DEF_ASM_OP0(repne, 0xf2)
-     DEF_ASM_OP0(repnz, 0xf2)
-             
-     DEF_ASM_OP0(invd, 0x0f08)
-     DEF_ASM_OP0(wbinvd, 0x0f09)
-     DEF_ASM_OP0(cpuid, 0x0fa2)
-     DEF_ASM_OP0(wrmsr, 0x0f30)
-     DEF_ASM_OP0(rdtsc, 0x0f31)
-     DEF_ASM_OP0(rdmsr, 0x0f32)
-     DEF_ASM_OP0(rdpmc, 0x0f33)
-     DEF_ASM_OP0(ud2, 0x0f0b)
-
-     /* NOTE: we took the same order as gas opcode definition order */
-ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWLX, OPT_ADDR, OPT_EAX))
-ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWLX, OPT_EAX, OPT_ADDR))
-ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWLX, OPT_REG, OPT_EA | OPT_REG))
-ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWLX, OPT_EA | OPT_REG, OPT_REG))
-ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWLX, OPT_IM, OPT_REG))
-ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWLX, OPT_IM, OPT_REG | OPT_EA))
-
-ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WLX, OPT_SEG, OPT_EA | OPT_REG))
-ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WLX, OPT_EA | OPT_REG, OPT_SEG))
-
-ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WLX, OPT_CR, OPT_REG32))
-ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WLX, OPT_DB, OPT_REG32))
-ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WLX, OPT_TR, OPT_REG32))
-ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WLX, OPT_REG32, OPT_CR))
-ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WLX, OPT_REG32, OPT_DB))
-ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WLX, OPT_REG32, OPT_TR))
-
-ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32))
-ALT(DEF_ASM_OP2(movsbw, 0x660fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG16))
-ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
-ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WLX, OPT_REG8 | OPT_EA, OPT_REGW))
-ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
-
-ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WLX, OPT_REGW))
-ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WLX, OPT_REGW | OPT_EA))
-ALT(DEF_ASM_OP1(pushw, 0x6a, 0, OPC_WLX, OPT_IM8S))
-ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WLX, OPT_IM32))
-ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WLX, OPT_SEG))
-
-ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WLX, OPT_REGW))
-ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WLX, OPT_REGW | OPT_EA))
-ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WLX, OPT_SEG))
-
-ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WLX, OPT_REGW, OPT_EAX))
-ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WLX, OPT_EAX, OPT_REGW))
-ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWLX, OPT_REG, OPT_EA | OPT_REG))
-ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWLX, OPT_EA | OPT_REG, OPT_REG))
-
-ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX))
-ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8))
-ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX))
-ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX))
-
-ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8))
-ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8))
-ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX))
-ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX))
-
-ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WLX, OPT_EA, OPT_REG))
-
-ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
-ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
-ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32))
-ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
-ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
-
-     /* arith */
-ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWLX, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */
-ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWLX, OPT_EA | OPT_REG, OPT_REG))
-ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWLX, OPT_IM, OPT_EAX))
-ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WLX, OPT_IM8S, OPT_EA | OPT_REGW))
-ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWLX, OPT_IM, OPT_EA | OPT_REG))
-
-ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWLX, OPT_REG, OPT_EA | OPT_REG))
-ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWLX, OPT_EA | OPT_REG, OPT_REG))
-ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWLX, OPT_IM, OPT_EAX))
-ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWLX, OPT_IM, OPT_EA | OPT_REG))
-
-ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WLX, OPT_REGW))
-ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA))
-ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WLX, OPT_REGW))
-ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA))
-
-ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA))
-ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA))
-
-ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA))
-ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA))
-
-ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WLX, OPT_REG | OPT_EA, OPT_REG))
-ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WLX, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW))
-ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WLX, OPT_IM8S, OPT_REGW))
-ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WLX, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW))
-ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WLX, OPT_IMW, OPT_REGW))
-
-ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA))
-ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA, OPT_EAX))
-ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA))
-ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA, OPT_EAX))
-
-     /* shifts */
-ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWLX | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG))
-ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWLX | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG))
-ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWLX | OPC_SHIFT, OPT_EA | OPT_REG))
-
-ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WLX, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
-ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WLX, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
-ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WLX, OPT_REGW, OPT_EA | OPT_REGW))
-ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WLX, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
-ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WLX, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
-ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WLX, OPT_REGW, OPT_EA | OPT_REGW))
-
-ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR))
-ALT(DEF_ASM_OP1(call, 0xe8, 0, 0, OPT_DISP))
-ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR))
-ALT(DEF_ASM_OP1(jmp, 0xeb, 0, 0, OPT_DISP8))
-
-ALT(DEF_ASM_OP2(lcall, 0x9a, 0, 0, OPT_IM16, OPT_IM32))
-ALT(DEF_ASM_OP1(lcall, 0xff, 3, OPC_MODRM, OPT_EA))
-ALT(DEF_ASM_OP2(ljmp, 0xea, 0, 0, OPT_IM16, OPT_IM32))
-ALT(DEF_ASM_OP1(ljmp, 0xff, 5, OPC_MODRM, OPT_EA))
-
-ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8))
-ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
-ALT(DEF_ASM_OP1(setob, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
-    DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8)
-    DEF_ASM_OP0(leave, 0xc9)
-    DEF_ASM_OP0(ret, 0xc3)
-    DEF_ASM_OP0(retl,0xc3)
-ALT(DEF_ASM_OP1(retl,0xc2, 0, 0, OPT_IM16))
-ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16))
-    DEF_ASM_OP0(lret, 0xcb)
-ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
-
-ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_TEST, OPT_DISP8))
-    DEF_ASM_OP1(loopne, 0xe0, 0, 0, OPT_DISP8)
-    DEF_ASM_OP1(loopnz, 0xe0, 0, 0, OPT_DISP8)
-    DEF_ASM_OP1(loope, 0xe1, 0, 0, OPT_DISP8)
-    DEF_ASM_OP1(loopz, 0xe1, 0, 0, OPT_DISP8)
-    DEF_ASM_OP1(loop, 0xe2, 0, 0, OPT_DISP8)
-    DEF_ASM_OP1(jecxz, 0xe3, 0, 0, OPT_DISP8)
-     
-     /* float */
-     /* specific fcomp handling */
-ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0))
-
-ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST))
-ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
-ALT(DEF_ASM_OP2(fadd, 0xdcc0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
-ALT(DEF_ASM_OP2(fmul, 0xdcc8, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
-ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH))
-ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST))
-ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
-ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
-ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH))
-ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
-ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
-ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
-ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
-
-     DEF_ASM_OP0(fucompp, 0xdae9)
-     DEF_ASM_OP0(ftst, 0xd9e4)
-     DEF_ASM_OP0(fxam, 0xd9e5)
-     DEF_ASM_OP0(fld1, 0xd9e8)
-     DEF_ASM_OP0(fldl2t, 0xd9e9)
-     DEF_ASM_OP0(fldl2e, 0xd9ea)
-     DEF_ASM_OP0(fldpi, 0xd9eb)
-     DEF_ASM_OP0(fldlg2, 0xd9ec)
-     DEF_ASM_OP0(fldln2, 0xd9ed)
-     DEF_ASM_OP0(fldz, 0xd9ee)
-
-     DEF_ASM_OP0(f2xm1, 0xd9f0)
-     DEF_ASM_OP0(fyl2x, 0xd9f1)
-     DEF_ASM_OP0(fptan, 0xd9f2)
-     DEF_ASM_OP0(fpatan, 0xd9f3)
-     DEF_ASM_OP0(fxtract, 0xd9f4)
-     DEF_ASM_OP0(fprem1, 0xd9f5)
-     DEF_ASM_OP0(fdecstp, 0xd9f6)
-     DEF_ASM_OP0(fincstp, 0xd9f7)
-     DEF_ASM_OP0(fprem, 0xd9f8)
-     DEF_ASM_OP0(fyl2xp1, 0xd9f9)
-     DEF_ASM_OP0(fsqrt, 0xd9fa)
-     DEF_ASM_OP0(fsincos, 0xd9fb)
-     DEF_ASM_OP0(frndint, 0xd9fc)
-     DEF_ASM_OP0(fscale, 0xd9fd)
-     DEF_ASM_OP0(fsin, 0xd9fe)
-     DEF_ASM_OP0(fcos, 0xd9ff)
-     DEF_ASM_OP0(fchs, 0xd9e0)
-     DEF_ASM_OP0(fabs, 0xd9e1)
-     DEF_ASM_OP0(fninit, 0xdbe3)
-     DEF_ASM_OP0(fnclex, 0xdbe2)
-     DEF_ASM_OP0(fnop, 0xd9d0)
-
-    /* fp load */
-    DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST)
-    DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST)
-    DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA)
-ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA))
-    DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA)
-    DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA)
-    
-    /* fp store */
-    DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST)
-    DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST)
-    DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA)
-ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA))
-    DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA)
-
-    DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST)
-    DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA)
-
-    /* exchange */
-    DEF_ASM_OP0(fxch, 0xd9c9)
-ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST))
-
-    /* misc FPU */
-    DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST )
-    DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST )
-
-    DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT)
-    DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA )
-    DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA )
-    DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )
-    DEF_ASM_OP0(fnstsw, 0xdfe0)
-ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX ))
-ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA ))
-    DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX )
-ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT))
-ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ))
-    DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT)
-    DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA )
-    DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
-    DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA )
-    DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA )
-    DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
-    DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA )
-    DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST )
-    DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST )
-    DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA )
-    DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA )
-
-    /* segments */
-    DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA)
-ALT(DEF_ASM_OP2(larw, 0x0f02, 0, OPC_MODRM | OPC_WLX, OPT_REG | OPT_EA, OPT_REG))
-    DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG)
-    DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG)
-ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WLX, OPT_EA | OPT_REG, OPT_REG))
-    DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG)
-    DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA)
-    DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA)
-    DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA)
-    DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA)
-    DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA)
-
-    /* 486 */
-    DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 )
-ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWLX, OPT_REG, OPT_REG | OPT_EA ))
-ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWLX, OPT_REG, OPT_REG | OPT_EA ))
-    DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA )
-
-    DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA)
-    DEF_ASM_OP2(boundw, 0x6662, 0, OPC_MODRM, OPT_REG16, OPT_EA)
-
-    /* pentium */
-    DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA )
-    
-    /* pentium pro */
-ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST | OPC_WLX, OPT_REGW | OPT_EA, OPT_REGW))
-    DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 )
-
-    DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 )
-
-    /* mmx */
-    DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */
-    DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMXSSE )
-    DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
-ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMXSSE, OPT_EA | OPT_REG32 ))
-ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX ))
-ALT(DEF_ASM_OP2(movq, 0x660fd6, 0, OPC_MODRM, OPT_SSE, OPT_EA | OPT_SSE ))
-ALT(DEF_ASM_OP2(movq, 0xf30f7e, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE ))
-
-    DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMXSSE ))
-    DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMXSSE ))
-    DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMXSSE ))
-    DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMXSSE ))
-    DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMXSSE ))
-    DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMXSSE ))
-    DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMXSSE ))
-    DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMXSSE ))
-    DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-
-    /* sse */
-    DEF_ASM_OP2(movups, 0x0f10, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_SSE )
-ALT(DEF_ASM_OP2(movups, 0x0f11, 0, OPC_MODRM, OPT_SSE, OPT_EA | OPT_REG32 ))
-    DEF_ASM_OP2(movaps, 0x0f28, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_SSE )
-ALT(DEF_ASM_OP2(movaps, 0x0f29, 0, OPC_MODRM, OPT_SSE, OPT_EA | OPT_REG32 ))
-    DEF_ASM_OP2(movhps, 0x0f16, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_SSE )
-ALT(DEF_ASM_OP2(movhps, 0x0f17, 0, OPC_MODRM, OPT_SSE, OPT_EA | OPT_REG32 ))
-    DEF_ASM_OP2(addps, 0x0f58, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(cvtpi2ps, 0x0f2a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_SSE )
-    DEF_ASM_OP2(cvtps2pi, 0x0f2d, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_MMX )
-    DEF_ASM_OP2(cvttps2pi, 0x0f2c, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_MMX )
-    DEF_ASM_OP2(divps, 0x0f5e, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(maxps, 0x0f5f, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(minps, 0x0f5d, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(mulps, 0x0f59, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(pavgb, 0x0fe0, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(pavgw, 0x0fe3, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(pmaxsw, 0x0fee, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pmaxub, 0x0fde, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pminsw, 0x0fea, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pminub, 0x0fda, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(rcpss, 0x0f53, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(rsqrtps, 0x0f52, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(sqrtps, 0x0f51, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(subps, 0x0f5c, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-
-#undef ALT
-#undef DEF_ASM_OP0
-#undef DEF_ASM_OP0L
-#undef DEF_ASM_OP1
-#undef DEF_ASM_OP2
-#undef DEF_ASM_OP3
diff --git a/tinyc/i386-gen.c b/tinyc/i386-gen.c
deleted file mode 100644
index b9c3599fd..000000000
--- a/tinyc/i386-gen.c
+++ /dev/null
@@ -1,1164 +0,0 @@
-/*
- *  X86 code generator for TCC
- * 
- *  Copyright (c) 2001-2004 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#ifdef TARGET_DEFS_ONLY
-
-/* number of available registers */
-#define NB_REGS         5
-#define NB_ASM_REGS     8
-#define CONFIG_TCC_ASM
-
-/* a register can belong to several classes. The classes must be
-   sorted from more general to more precise (see gv2() code which does
-   assumptions on it). */
-#define RC_INT     0x0001 /* generic integer register */
-#define RC_FLOAT   0x0002 /* generic float register */
-#define RC_EAX     0x0004
-#define RC_ST0     0x0008 
-#define RC_ECX     0x0010
-#define RC_EDX     0x0020
-#define RC_EBX     0x0040
-
-#define RC_IRET    RC_EAX /* function return: integer register */
-#define RC_LRET    RC_EDX /* function return: second integer register */
-#define RC_FRET    RC_ST0 /* function return: float register */
-
-/* pretty names for the registers */
-enum {
-    TREG_EAX = 0,
-    TREG_ECX,
-    TREG_EDX,
-    TREG_EBX,
-    TREG_ST0,
-    TREG_ESP = 4
-};
-
-/* return registers for function */
-#define REG_IRET TREG_EAX /* single word int return register */
-#define REG_LRET TREG_EDX /* second word return register (for long long) */
-#define REG_FRET TREG_ST0 /* float return register */
-
-/* defined if function parameters must be evaluated in reverse order */
-#define INVERT_FUNC_PARAMS
-
-/* defined if structures are passed as pointers. Otherwise structures
-   are directly pushed on stack. */
-/* #define FUNC_STRUCT_PARAM_AS_PTR */
-
-/* pointer size, in bytes */
-#define PTR_SIZE 4
-
-/* long double size and alignment, in bytes */
-#define LDOUBLE_SIZE  12
-#define LDOUBLE_ALIGN 4
-/* maximum alignment (for aligned attribute support) */
-#define MAX_ALIGN     8
-
-/******************************************************/
-#else /* ! TARGET_DEFS_ONLY */
-/******************************************************/
-#include "tcc.h"
-
-/* define to 1/0 to [not] have EBX as 4th register */
-#define USE_EBX 0
-
-ST_DATA const int reg_classes[NB_REGS] = {
-    /* eax */ RC_INT | RC_EAX,
-    /* ecx */ RC_INT | RC_ECX,
-    /* edx */ RC_INT | RC_EDX,
-    /* ebx */ (RC_INT | RC_EBX) * USE_EBX,
-    /* st0 */ RC_FLOAT | RC_ST0,
-};
-
-static unsigned long func_sub_sp_offset;
-static int func_ret_sub;
-#ifdef CONFIG_TCC_BCHECK
-static addr_t func_bound_offset;
-static unsigned long func_bound_ind;
-#endif
-
-/* XXX: make it faster ? */
-ST_FUNC void g(int c)
-{
-    int ind1;
-    if (nocode_wanted)
-        return;
-    ind1 = ind + 1;
-    if (ind1 > cur_text_section->data_allocated)
-        section_realloc(cur_text_section, ind1);
-    cur_text_section->data[ind] = c;
-    ind = ind1;
-}
-
-ST_FUNC void o(unsigned int c)
-{
-    while (c) {
-        g(c);
-        c = c >> 8;
-    }
-}
-
-ST_FUNC void gen_le16(int v)
-{
-    g(v);
-    g(v >> 8);
-}
-
-ST_FUNC void gen_le32(int c)
-{
-    g(c);
-    g(c >> 8);
-    g(c >> 16);
-    g(c >> 24);
-}
-
-/* output a symbol and patch all calls to it */
-ST_FUNC void gsym_addr(int t, int a)
-{
-    while (t) {
-        unsigned char *ptr = cur_text_section->data + t;
-        uint32_t n = read32le(ptr); /* next value */
-        write32le(ptr, a - t - 4);
-        t = n;
-    }
-}
-
-ST_FUNC void gsym(int t)
-{
-    gsym_addr(t, ind);
-}
-
-/* instruction + 4 bytes data. Return the address of the data */
-static int oad(int c, int s)
-{
-    int t;
-    if (nocode_wanted)
-        return s;
-    o(c);
-    t = ind;
-    gen_le32(s);
-    return t;
-}
-
-/* generate jmp to a label */
-#define gjmp2(instr,lbl) oad(instr,lbl)
-
-/* output constant with relocation if 'r & VT_SYM' is true */
-ST_FUNC void gen_addr32(int r, Sym *sym, int c)
-{
-    if (r & VT_SYM)
-        greloc(cur_text_section, sym, ind, R_386_32);
-    gen_le32(c);
-}
-
-ST_FUNC void gen_addrpc32(int r, Sym *sym, int c)
-{
-    if (r & VT_SYM)
-        greloc(cur_text_section, sym, ind, R_386_PC32);
-    gen_le32(c - 4);
-}
-
-/* generate a modrm reference. 'op_reg' contains the additional 3
-   opcode bits */
-static void gen_modrm(int op_reg, int r, Sym *sym, int c)
-{
-    op_reg = op_reg << 3;
-    if ((r & VT_VALMASK) == VT_CONST) {
-        /* constant memory reference */
-        o(0x05 | op_reg);
-        gen_addr32(r, sym, c);
-    } else if ((r & VT_VALMASK) == VT_LOCAL) {
-        /* currently, we use only ebp as base */
-        if (c == (char)c) {
-            /* short reference */
-            o(0x45 | op_reg);
-            g(c);
-        } else {
-            oad(0x85 | op_reg, c);
-        }
-    } else {
-        g(0x00 | op_reg | (r & VT_VALMASK));
-    }
-}
-
-/* load 'r' from value 'sv' */
-ST_FUNC void load(int r, SValue *sv)
-{
-    int v, t, ft, fc, fr;
-    SValue v1;
-
-#ifdef TCC_TARGET_PE
-    SValue v2;
-    sv = pe_getimport(sv, &v2);
-#endif
-
-    fr = sv->r;
-    ft = sv->type.t & ~VT_DEFSIGN;
-    fc = sv->c.i;
-
-    ft &= ~(VT_VOLATILE | VT_CONSTANT);
-
-    v = fr & VT_VALMASK;
-    if (fr & VT_LVAL) {
-        if (v == VT_LLOCAL) {
-            v1.type.t = VT_INT;
-            v1.r = VT_LOCAL | VT_LVAL;
-            v1.c.i = fc;
-            fr = r;
-            if (!(reg_classes[fr] & RC_INT))
-                fr = get_reg(RC_INT);
-            load(fr, &v1);
-        }
-        if ((ft & VT_BTYPE) == VT_FLOAT) {
-            o(0xd9); /* flds */
-            r = 0;
-        } else if ((ft & VT_BTYPE) == VT_DOUBLE) {
-            o(0xdd); /* fldl */
-            r = 0;
-        } else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
-            o(0xdb); /* fldt */
-            r = 5;
-        } else if ((ft & VT_TYPE) == VT_BYTE || (ft & VT_TYPE) == VT_BOOL) {
-            o(0xbe0f);   /* movsbl */
-        } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) {
-            o(0xb60f);   /* movzbl */
-        } else if ((ft & VT_TYPE) == VT_SHORT) {
-            o(0xbf0f);   /* movswl */
-        } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) {
-            o(0xb70f);   /* movzwl */
-        } else {
-            o(0x8b);     /* movl */
-        }
-        gen_modrm(r, fr, sv->sym, fc);
-    } else {
-        if (v == VT_CONST) {
-            o(0xb8 + r); /* mov $xx, r */
-            gen_addr32(fr, sv->sym, fc);
-        } else if (v == VT_LOCAL) {
-            if (fc) {
-                o(0x8d); /* lea xxx(%ebp), r */
-                gen_modrm(r, VT_LOCAL, sv->sym, fc);
-            } else {
-                o(0x89);
-                o(0xe8 + r); /* mov %ebp, r */
-            }
-        } else if (v == VT_CMP) {
-            oad(0xb8 + r, 0); /* mov $0, r */
-            o(0x0f); /* setxx %br */
-            o(fc);
-            o(0xc0 + r);
-        } else if (v == VT_JMP || v == VT_JMPI) {
-            t = v & 1;
-            oad(0xb8 + r, t); /* mov $1, r */
-            o(0x05eb); /* jmp after */
-            gsym(fc);
-            oad(0xb8 + r, t ^ 1); /* mov $0, r */
-        } else if (v != r) {
-            o(0x89);
-            o(0xc0 + r + v * 8); /* mov v, r */
-        }
-    }
-}
-
-/* store register 'r' in lvalue 'v' */
-ST_FUNC void store(int r, SValue *v)
-{
-    int fr, bt, ft, fc;
-
-#ifdef TCC_TARGET_PE
-    SValue v2;
-    v = pe_getimport(v, &v2);
-#endif
-
-    ft = v->type.t;
-    fc = v->c.i;
-    fr = v->r & VT_VALMASK;
-    ft &= ~(VT_VOLATILE | VT_CONSTANT);
-    bt = ft & VT_BTYPE;
-    /* XXX: incorrect if float reg to reg */
-    if (bt == VT_FLOAT) {
-        o(0xd9); /* fsts */
-        r = 2;
-    } else if (bt == VT_DOUBLE) {
-        o(0xdd); /* fstpl */
-        r = 2;
-    } else if (bt == VT_LDOUBLE) {
-        o(0xc0d9); /* fld %st(0) */
-        o(0xdb); /* fstpt */
-        r = 7;
-    } else {
-        if (bt == VT_SHORT)
-            o(0x66);
-        if (bt == VT_BYTE || bt == VT_BOOL)
-            o(0x88);
-        else
-            o(0x89);
-    }
-    if (fr == VT_CONST ||
-        fr == VT_LOCAL ||
-        (v->r & VT_LVAL)) {
-        gen_modrm(r, v->r, v->sym, fc);
-    } else if (fr != r) {
-        o(0xc0 + fr + r * 8); /* mov r, fr */
-    }
-}
-
-static void gadd_sp(int val)
-{
-    if (val == (char)val) {
-        o(0xc483);
-        g(val);
-    } else {
-        oad(0xc481, val); /* add $xxx, %esp */
-    }
-}
-
-#if defined CONFIG_TCC_BCHECK || defined TCC_TARGET_PE
-static void gen_static_call(int v)
-{
-    Sym *sym;
-
-    sym = external_global_sym(v, &func_old_type, 0);
-    oad(0xe8, -4);
-    greloc(cur_text_section, sym, ind-4, R_386_PC32);
-}
-#endif
-
-/* 'is_jmp' is '1' if it is a jump */
-static void gcall_or_jmp(int is_jmp)
-{
-    int r;
-    if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST && (vtop->r & VT_SYM)) {
-        /* constant and relocation case */
-        greloc(cur_text_section, vtop->sym, ind + 1, R_386_PC32);
-        oad(0xe8 + is_jmp, vtop->c.i - 4); /* call/jmp im */
-    } else {
-        /* otherwise, indirect call */
-        r = gv(RC_INT);
-        o(0xff); /* call/jmp *r */
-        o(0xd0 + r + (is_jmp << 4));
-    }
-    if (!is_jmp) {
-        int rt;
-        /* extend the return value to the whole register if necessary
-           visual studio and gcc do not always set the whole eax register
-           when assigning the return value of a function  */
-        rt = vtop->type.ref->type.t;
-        switch (rt & VT_BTYPE) {
-            case VT_BYTE:
-                if (rt & VT_UNSIGNED) {
-                    o(0xc0b60f); /* movzx %al, %eax */
-                }
-                else {
-                    o(0xc0be0f); /* movsx %al, %eax */
-                }
-                break;
-            case VT_SHORT:
-                if (rt & VT_UNSIGNED) {
-                    o(0xc0b70f); /* movzx %ax, %eax */
-                }
-                else {
-                    o(0xc0bf0f); /* movsx %ax, %eax */
-                }
-                break;
-            default:
-                break;
-        }
-    }
-}
-
-static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX };
-static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX };
-
-/* Return the number of registers needed to return the struct, or 0 if
-   returning via struct pointer. */
-ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize)
-{
-#ifdef TCC_TARGET_PE
-    int size, align;
-    *ret_align = 1; // Never have to re-align return values for x86
-    *regsize = 4;
-    size = type_size(vt, &align);
-    if (size > 8 || (size & (size - 1)))
-        return 0;
-    if (size == 8)
-        ret->t = VT_LLONG;
-    else if (size == 4)
-        ret->t = VT_INT;
-    else if (size == 2)
-        ret->t = VT_SHORT;
-    else
-        ret->t = VT_BYTE;
-    ret->ref = NULL;
-    return 1;
-#else
-    *ret_align = 1; // Never have to re-align return values for x86
-    return 0;
-#endif
-}
-
-/* Generate function call. The function address is pushed first, then
-   all the parameters in call order. This functions pops all the
-   parameters and the function address. */
-ST_FUNC void gfunc_call(int nb_args)
-{
-    int size, align, r, args_size, i, func_call;
-    Sym *func_sym;
-    
-    args_size = 0;
-    for(i = 0;i < nb_args; i++) {
-        if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
-            size = type_size(&vtop->type, &align);
-            /* align to stack align size */
-            size = (size + 3) & ~3;
-            /* allocate the necessary size on stack */
-            oad(0xec81, size); /* sub $xxx, %esp */
-            /* generate structure store */
-            r = get_reg(RC_INT);
-            o(0x89); /* mov %esp, r */
-            o(0xe0 + r);
-            vset(&vtop->type, r | VT_LVAL, 0);
-            vswap();
-            vstore();
-            args_size += size;
-        } else if (is_float(vtop->type.t)) {
-            gv(RC_FLOAT); /* only one float register */
-            if ((vtop->type.t & VT_BTYPE) == VT_FLOAT)
-                size = 4;
-            else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE)
-                size = 8;
-            else
-                size = 12;
-            oad(0xec81, size); /* sub $xxx, %esp */
-            if (size == 12)
-                o(0x7cdb);
-            else
-                o(0x5cd9 + size - 4); /* fstp[s|l] 0(%esp) */
-            g(0x24);
-            g(0x00);
-            args_size += size;
-        } else {
-            /* simple type (currently always same size) */
-            /* XXX: implicit cast ? */
-            r = gv(RC_INT);
-            if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
-                size = 8;
-                o(0x50 + vtop->r2); /* push r */
-            } else {
-                size = 4;
-            }
-            o(0x50 + r); /* push r */
-            args_size += size;
-        }
-        vtop--;
-    }
-    save_regs(0); /* save used temporary registers */
-    func_sym = vtop->type.ref;
-    func_call = func_sym->f.func_call;
-    /* fast call case */
-    if ((func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) ||
-        func_call == FUNC_FASTCALLW) {
-        int fastcall_nb_regs;
-        uint8_t *fastcall_regs_ptr;
-        if (func_call == FUNC_FASTCALLW) {
-            fastcall_regs_ptr = fastcallw_regs;
-            fastcall_nb_regs = 2;
-        } else {
-            fastcall_regs_ptr = fastcall_regs;
-            fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1;
-        }
-        for(i = 0;i < fastcall_nb_regs; i++) {
-            if (args_size <= 0)
-                break;
-            o(0x58 + fastcall_regs_ptr[i]); /* pop r */
-            /* XXX: incorrect for struct/floats */
-            args_size -= 4;
-        }
-    }
-#ifndef TCC_TARGET_PE
-    else if ((vtop->type.ref->type.t & VT_BTYPE) == VT_STRUCT)
-        args_size -= 4;
-#endif
-    gcall_or_jmp(0);
-
-    if (args_size && func_call != FUNC_STDCALL && func_call != FUNC_FASTCALLW)
-        gadd_sp(args_size);
-    vtop--;
-}
-
-#ifdef TCC_TARGET_PE
-#define FUNC_PROLOG_SIZE (10 + USE_EBX)
-#else
-#define FUNC_PROLOG_SIZE (9 + USE_EBX)
-#endif
-
-/* generate function prolog of type 't' */
-ST_FUNC void gfunc_prolog(CType *func_type)
-{
-    int addr, align, size, func_call, fastcall_nb_regs;
-    int param_index, param_addr;
-    uint8_t *fastcall_regs_ptr;
-    Sym *sym;
-    CType *type;
-
-    sym = func_type->ref;
-    func_call = sym->f.func_call;
-    addr = 8;
-    loc = 0;
-    func_vc = 0;
-
-    if (func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) {
-        fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1;
-        fastcall_regs_ptr = fastcall_regs;
-    } else if (func_call == FUNC_FASTCALLW) {
-        fastcall_nb_regs = 2;
-        fastcall_regs_ptr = fastcallw_regs;
-    } else {
-        fastcall_nb_regs = 0;
-        fastcall_regs_ptr = NULL;
-    }
-    param_index = 0;
-
-    ind += FUNC_PROLOG_SIZE;
-    func_sub_sp_offset = ind;
-    /* if the function returns a structure, then add an
-       implicit pointer parameter */
-    func_vt = sym->type;
-    func_var = (sym->f.func_type == FUNC_ELLIPSIS);
-#ifdef TCC_TARGET_PE
-    size = type_size(&func_vt,&align);
-    if (((func_vt.t & VT_BTYPE) == VT_STRUCT)
-        && (size > 8 || (size & (size - 1)))) {
-#else
-    if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
-#endif
-        /* XXX: fastcall case ? */
-        func_vc = addr;
-        addr += 4;
-        param_index++;
-    }
-    /* define parameters */
-    while ((sym = sym->next) != NULL) {
-        type = &sym->type;
-        size = type_size(type, &align);
-        size = (size + 3) & ~3;
-#ifdef FUNC_STRUCT_PARAM_AS_PTR
-        /* structs are passed as pointer */
-        if ((type->t & VT_BTYPE) == VT_STRUCT) {
-            size = 4;
-        }
-#endif
-        if (param_index < fastcall_nb_regs) {
-            /* save FASTCALL register */
-            loc -= 4;
-            o(0x89);     /* movl */
-            gen_modrm(fastcall_regs_ptr[param_index], VT_LOCAL, NULL, loc);
-            param_addr = loc;
-        } else {
-            param_addr = addr;
-            addr += size;
-        }
-        sym_push(sym->v & ~SYM_FIELD, type,
-                 VT_LOCAL | lvalue_type(type->t), param_addr);
-        param_index++;
-    }
-    func_ret_sub = 0;
-    /* pascal type call or fastcall ? */
-    if (func_call == FUNC_STDCALL || func_call == FUNC_FASTCALLW)
-        func_ret_sub = addr - 8;
-#ifndef TCC_TARGET_PE
-    else if (func_vc)
-        func_ret_sub = 4;
-#endif
-
-#ifdef CONFIG_TCC_BCHECK
-    /* leave some room for bound checking code */
-    if (tcc_state->do_bounds_check) {
-        func_bound_offset = lbounds_section->data_offset;
-        func_bound_ind = ind;
-        oad(0xb8, 0); /* lbound section pointer */
-        oad(0xb8, 0); /* call to function */
-    }
-#endif
-}
-
-/* generate function epilog */
-ST_FUNC void gfunc_epilog(void)
-{
-    addr_t v, saved_ind;
-
-#ifdef CONFIG_TCC_BCHECK
-    if (tcc_state->do_bounds_check
-     && func_bound_offset != lbounds_section->data_offset) {
-        addr_t saved_ind;
-        addr_t *bounds_ptr;
-        Sym *sym_data;
-
-        /* add end of table info */
-        bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t));
-        *bounds_ptr = 0;
-
-        /* generate bound local allocation */
-        saved_ind = ind;
-        ind = func_bound_ind;
-        sym_data = get_sym_ref(&char_pointer_type, lbounds_section, 
-                               func_bound_offset, lbounds_section->data_offset);
-        greloc(cur_text_section, sym_data,
-               ind + 1, R_386_32);
-        oad(0xb8, 0); /* mov %eax, xxx */
-        gen_static_call(TOK___bound_local_new);
-        ind = saved_ind;
-
-        /* generate bound check local freeing */
-        o(0x5250); /* save returned value, if any */
-        greloc(cur_text_section, sym_data, ind + 1, R_386_32);
-        oad(0xb8, 0); /* mov %eax, xxx */
-        gen_static_call(TOK___bound_local_delete);
-        o(0x585a); /* restore returned value, if any */
-    }
-#endif
-
-    /* align local size to word & save local variables */
-    v = (-loc + 3) & -4;
-
-#if USE_EBX
-    o(0x8b);
-    gen_modrm(TREG_EBX, VT_LOCAL, NULL, -(v+4));
-#endif
-
-    o(0xc9); /* leave */
-    if (func_ret_sub == 0) {
-        o(0xc3); /* ret */
-    } else {
-        o(0xc2); /* ret n */
-        g(func_ret_sub);
-        g(func_ret_sub >> 8);
-    }
-    saved_ind = ind;
-    ind = func_sub_sp_offset - FUNC_PROLOG_SIZE;
-#ifdef TCC_TARGET_PE
-    if (v >= 4096) {
-        oad(0xb8, v); /* mov stacksize, %eax */
-        gen_static_call(TOK___chkstk); /* call __chkstk, (does the stackframe too) */
-    } else
-#endif
-    {
-        o(0xe58955);  /* push %ebp, mov %esp, %ebp */
-        o(0xec81);  /* sub esp, stacksize */
-        gen_le32(v);
-#ifdef TCC_TARGET_PE
-        o(0x90);  /* adjust to FUNC_PROLOG_SIZE */
-#endif
-    }
-    o(0x53 * USE_EBX); /* push ebx */
-    ind = saved_ind;
-}
-
-/* generate a jump to a label */
-ST_FUNC int gjmp(int t)
-{
-    return gjmp2(0xe9, t);
-}
-
-/* generate a jump to a fixed address */
-ST_FUNC void gjmp_addr(int a)
-{
-    int r;
-    r = a - ind - 2;
-    if (r == (char)r) {
-        g(0xeb);
-        g(r);
-    } else {
-        oad(0xe9, a - ind - 5);
-    }
-}
-
-ST_FUNC void gtst_addr(int inv, int a)
-{
-    int v = vtop->r & VT_VALMASK;
-    if (v == VT_CMP) {
-	inv ^= (vtop--)->c.i;
-	a -= ind + 2;
-	if (a == (char)a) {
-	    g(inv - 32);
-	    g(a);
-	} else {
-	    g(0x0f);
-	    oad(inv - 16, a - 4);
-	}
-    } else if ((v & ~1) == VT_JMP) {
-	if ((v & 1) != inv) {
-	    gjmp_addr(a);
-	    gsym(vtop->c.i);
-	} else {
-	    gsym(vtop->c.i);
-	    o(0x05eb);
-	    gjmp_addr(a);
-	}
-	vtop--;
-    }
-}
-
-/* generate a test. set 'inv' to invert test. Stack entry is popped */
-ST_FUNC int gtst(int inv, int t)
-{
-    int v = vtop->r & VT_VALMASK;
-    if (nocode_wanted) {
-        ;
-    } else if (v == VT_CMP) {
-        /* fast case : can jump directly since flags are set */
-        g(0x0f);
-        t = gjmp2((vtop->c.i - 16) ^ inv, t);
-    } else if (v == VT_JMP || v == VT_JMPI) {
-        /* && or || optimization */
-        if ((v & 1) == inv) {
-            /* insert vtop->c jump list in t */
-            uint32_t n1, n = vtop->c.i;
-            if (n) {
-                while ((n1 = read32le(cur_text_section->data + n)))
-                    n = n1;
-                write32le(cur_text_section->data + n, t);
-                t = vtop->c.i;
-            }
-        } else {
-            t = gjmp(t);
-            gsym(vtop->c.i);
-        }
-    }
-    vtop--;
-    return t;
-}
-
-/* generate an integer binary operation */
-ST_FUNC void gen_opi(int op)
-{
-    int r, fr, opc, c;
-
-    switch(op) {
-    case '+':
-    case TOK_ADDC1: /* add with carry generation */
-        opc = 0;
-    gen_op8:
-        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
-            /* constant case */
-            vswap();
-            r = gv(RC_INT);
-            vswap();
-            c = vtop->c.i;
-            if (c == (char)c) {
-                /* generate inc and dec for smaller code */
-                if (c==1 && opc==0 && op != TOK_ADDC1) {
-                    o (0x40 | r); // inc
-                } else if (c==1 && opc==5 && op != TOK_SUBC1) {
-                    o (0x48 | r); // dec
-                } else {
-                    o(0x83);
-                    o(0xc0 | (opc << 3) | r);
-                    g(c);
-                }
-            } else {
-                o(0x81);
-                oad(0xc0 | (opc << 3) | r, c);
-            }
-        } else {
-            gv2(RC_INT, RC_INT);
-            r = vtop[-1].r;
-            fr = vtop[0].r;
-            o((opc << 3) | 0x01);
-            o(0xc0 + r + fr * 8); 
-        }
-        vtop--;
-        if (op >= TOK_ULT && op <= TOK_GT) {
-            vtop->r = VT_CMP;
-            vtop->c.i = op;
-        }
-        break;
-    case '-':
-    case TOK_SUBC1: /* sub with carry generation */
-        opc = 5;
-        goto gen_op8;
-    case TOK_ADDC2: /* add with carry use */
-        opc = 2;
-        goto gen_op8;
-    case TOK_SUBC2: /* sub with carry use */
-        opc = 3;
-        goto gen_op8;
-    case '&':
-        opc = 4;
-        goto gen_op8;
-    case '^':
-        opc = 6;
-        goto gen_op8;
-    case '|':
-        opc = 1;
-        goto gen_op8;
-    case '*':
-        gv2(RC_INT, RC_INT);
-        r = vtop[-1].r;
-        fr = vtop[0].r;
-        vtop--;
-        o(0xaf0f); /* imul fr, r */
-        o(0xc0 + fr + r * 8);
-        break;
-    case TOK_SHL:
-        opc = 4;
-        goto gen_shift;
-    case TOK_SHR:
-        opc = 5;
-        goto gen_shift;
-    case TOK_SAR:
-        opc = 7;
-    gen_shift:
-        opc = 0xc0 | (opc << 3);
-        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
-            /* constant case */
-            vswap();
-            r = gv(RC_INT);
-            vswap();
-            c = vtop->c.i & 0x1f;
-            o(0xc1); /* shl/shr/sar $xxx, r */
-            o(opc | r);
-            g(c);
-        } else {
-            /* we generate the shift in ecx */
-            gv2(RC_INT, RC_ECX);
-            r = vtop[-1].r;
-            o(0xd3); /* shl/shr/sar %cl, r */
-            o(opc | r);
-        }
-        vtop--;
-        break;
-    case '/':
-    case TOK_UDIV:
-    case TOK_PDIV:
-    case '%':
-    case TOK_UMOD:
-    case TOK_UMULL:
-        /* first operand must be in eax */
-        /* XXX: need better constraint for second operand */
-        gv2(RC_EAX, RC_ECX);
-        r = vtop[-1].r;
-        fr = vtop[0].r;
-        vtop--;
-        save_reg(TREG_EDX);
-        /* save EAX too if used otherwise */
-        save_reg_upstack(TREG_EAX, 1);
-        if (op == TOK_UMULL) {
-            o(0xf7); /* mul fr */
-            o(0xe0 + fr);
-            vtop->r2 = TREG_EDX;
-            r = TREG_EAX;
-        } else {
-            if (op == TOK_UDIV || op == TOK_UMOD) {
-                o(0xf7d231); /* xor %edx, %edx, div fr, %eax */
-                o(0xf0 + fr);
-            } else {
-                o(0xf799); /* cltd, idiv fr, %eax */
-                o(0xf8 + fr);
-            }
-            if (op == '%' || op == TOK_UMOD)
-                r = TREG_EDX;
-            else
-                r = TREG_EAX;
-        }
-        vtop->r = r;
-        break;
-    default:
-        opc = 7;
-        goto gen_op8;
-    }
-}
-
-/* generate a floating point operation 'v = t1 op t2' instruction. The
-   two operands are guaranteed to have the same floating point type */
-/* XXX: need to use ST1 too */
-ST_FUNC void gen_opf(int op)
-{
-    int a, ft, fc, swapped, r;
-
-    /* convert constants to memory references */
-    if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
-        vswap();
-        gv(RC_FLOAT);
-        vswap();
-    }
-    if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST)
-        gv(RC_FLOAT);
-
-    /* must put at least one value in the floating point register */
-    if ((vtop[-1].r & VT_LVAL) &&
-        (vtop[0].r & VT_LVAL)) {
-        vswap();
-        gv(RC_FLOAT);
-        vswap();
-    }
-    swapped = 0;
-    /* swap the stack if needed so that t1 is the register and t2 is
-       the memory reference */
-    if (vtop[-1].r & VT_LVAL) {
-        vswap();
-        swapped = 1;
-    }
-    if (op >= TOK_ULT && op <= TOK_GT) {
-        /* load on stack second operand */
-        load(TREG_ST0, vtop);
-        save_reg(TREG_EAX); /* eax is used by FP comparison code */
-        if (op == TOK_GE || op == TOK_GT)
-            swapped = !swapped;
-        else if (op == TOK_EQ || op == TOK_NE)
-            swapped = 0;
-        if (swapped)
-            o(0xc9d9); /* fxch %st(1) */
-        if (op == TOK_EQ || op == TOK_NE)
-            o(0xe9da); /* fucompp */
-        else
-            o(0xd9de); /* fcompp */
-        o(0xe0df); /* fnstsw %ax */
-        if (op == TOK_EQ) {
-            o(0x45e480); /* and $0x45, %ah */
-            o(0x40fC80); /* cmp $0x40, %ah */
-        } else if (op == TOK_NE) {
-            o(0x45e480); /* and $0x45, %ah */
-            o(0x40f480); /* xor $0x40, %ah */
-            op = TOK_NE;
-        } else if (op == TOK_GE || op == TOK_LE) {
-            o(0x05c4f6); /* test $0x05, %ah */
-            op = TOK_EQ;
-        } else {
-            o(0x45c4f6); /* test $0x45, %ah */
-            op = TOK_EQ;
-        }
-        vtop--;
-        vtop->r = VT_CMP;
-        vtop->c.i = op;
-    } else {
-        /* no memory reference possible for long double operations */
-        if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
-            load(TREG_ST0, vtop);
-            swapped = !swapped;
-        }
-        
-        switch(op) {
-        default:
-        case '+':
-            a = 0;
-            break;
-        case '-':
-            a = 4;
-            if (swapped)
-                a++;
-            break;
-        case '*':
-            a = 1;
-            break;
-        case '/':
-            a = 6;
-            if (swapped)
-                a++;
-            break;
-        }
-        ft = vtop->type.t;
-        fc = vtop->c.i;
-        if ((ft & VT_BTYPE) == VT_LDOUBLE) {
-            o(0xde); /* fxxxp %st, %st(1) */
-            o(0xc1 + (a << 3));
-        } else {
-            /* if saved lvalue, then we must reload it */
-            r = vtop->r;
-            if ((r & VT_VALMASK) == VT_LLOCAL) {
-                SValue v1;
-                r = get_reg(RC_INT);
-                v1.type.t = VT_INT;
-                v1.r = VT_LOCAL | VT_LVAL;
-                v1.c.i = fc;
-                load(r, &v1);
-                fc = 0;
-            }
-
-            if ((ft & VT_BTYPE) == VT_DOUBLE)
-                o(0xdc);
-            else
-                o(0xd8);
-            gen_modrm(a, r, vtop->sym, fc);
-        }
-        vtop--;
-    }
-}
-
-/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
-   and 'long long' cases. */
-ST_FUNC void gen_cvt_itof(int t)
-{
-    save_reg(TREG_ST0);
-    gv(RC_INT);
-    if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
-        /* signed long long to float/double/long double (unsigned case
-           is handled generically) */
-        o(0x50 + vtop->r2); /* push r2 */
-        o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
-        o(0x242cdf); /* fildll (%esp) */
-        o(0x08c483); /* add $8, %esp */
-    } else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == 
-               (VT_INT | VT_UNSIGNED)) {
-        /* unsigned int to float/double/long double */
-        o(0x6a); /* push $0 */
-        g(0x00);
-        o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
-        o(0x242cdf); /* fildll (%esp) */
-        o(0x08c483); /* add $8, %esp */
-    } else {
-        /* int to float/double/long double */
-        o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
-        o(0x2404db); /* fildl (%esp) */
-        o(0x04c483); /* add $4, %esp */
-    }
-    vtop->r = TREG_ST0;
-}
-
-/* convert fp to int 't' type */
-ST_FUNC void gen_cvt_ftoi(int t)
-{
-    int bt = vtop->type.t & VT_BTYPE;
-    if (bt == VT_FLOAT)
-        vpush_global_sym(&func_old_type, TOK___fixsfdi);
-    else if (bt == VT_LDOUBLE)
-        vpush_global_sym(&func_old_type, TOK___fixxfdi);
-    else
-        vpush_global_sym(&func_old_type, TOK___fixdfdi);
-    vswap();
-    gfunc_call(1);
-    vpushi(0);
-    vtop->r = REG_IRET;
-    vtop->r2 = REG_LRET;
-}
-
-/* convert from one floating point type to another */
-ST_FUNC void gen_cvt_ftof(int t)
-{
-    /* all we have to do on i386 is to put the float in a register */
-    gv(RC_FLOAT);
-}
-
-/* computed goto support */
-ST_FUNC void ggoto(void)
-{
-    gcall_or_jmp(1);
-    vtop--;
-}
-
-/* bound check support functions */
-#ifdef CONFIG_TCC_BCHECK
-
-/* generate a bounded pointer addition */
-ST_FUNC void gen_bounded_ptr_add(void)
-{
-    /* prepare fast i386 function call (args in eax and edx) */
-    gv2(RC_EAX, RC_EDX);
-    /* save all temporary registers */
-    vtop -= 2;
-    save_regs(0);
-    /* do a fast function call */
-    gen_static_call(TOK___bound_ptr_add);
-    /* returned pointer is in eax */
-    vtop++;
-    vtop->r = TREG_EAX | VT_BOUNDED;
-    /* address of bounding function call point */
-    vtop->c.i = (cur_text_section->reloc->data_offset - sizeof(Elf32_Rel));
-}
-
-/* patch pointer addition in vtop so that pointer dereferencing is
-   also tested */
-ST_FUNC void gen_bounded_ptr_deref(void)
-{
-    addr_t func;
-    int  size, align;
-    Elf32_Rel *rel;
-    Sym *sym;
-
-    size = 0;
-    /* XXX: put that code in generic part of tcc */
-    if (!is_float(vtop->type.t)) {
-        if (vtop->r & VT_LVAL_BYTE)
-            size = 1;
-        else if (vtop->r & VT_LVAL_SHORT)
-            size = 2;
-    }
-    if (!size)
-        size = type_size(&vtop->type, &align);
-    switch(size) {
-    case  1: func = TOK___bound_ptr_indir1; break;
-    case  2: func = TOK___bound_ptr_indir2; break;
-    case  4: func = TOK___bound_ptr_indir4; break;
-    case  8: func = TOK___bound_ptr_indir8; break;
-    case 12: func = TOK___bound_ptr_indir12; break;
-    case 16: func = TOK___bound_ptr_indir16; break;
-    default:
-        tcc_error("unhandled size when dereferencing bounded pointer");
-        func = 0;
-        break;
-    }
-
-    /* patch relocation */
-    /* XXX: find a better solution ? */
-    rel = (Elf32_Rel *)(cur_text_section->reloc->data + vtop->c.i);
-    sym = external_global_sym(func, &func_old_type, 0);
-    if (!sym->c)
-        put_extern_sym(sym, NULL, 0, 0);
-    rel->r_info = ELF32_R_INFO(sym->c, ELF32_R_TYPE(rel->r_info));
-}
-#endif
-
-/* Save the stack pointer onto the stack */
-ST_FUNC void gen_vla_sp_save(int addr) {
-    /* mov %esp,addr(%ebp)*/
-    o(0x89);
-    gen_modrm(TREG_ESP, VT_LOCAL, NULL, addr);
-}
-
-/* Restore the SP from a location on the stack */
-ST_FUNC void gen_vla_sp_restore(int addr) {
-    o(0x8b);
-    gen_modrm(TREG_ESP, VT_LOCAL, NULL, addr);
-}
-
-/* Subtract from the stack pointer, and push the resulting value onto the stack */
-ST_FUNC void gen_vla_alloc(CType *type, int align) {
-#ifdef TCC_TARGET_PE
-    /* alloca does more than just adjust %rsp on Windows */
-    vpush_global_sym(&func_old_type, TOK_alloca);
-    vswap(); /* Move alloca ref past allocation size */
-    gfunc_call(1);
-#else
-    int r;
-    r = gv(RC_INT); /* allocation size */
-    /* sub r,%rsp */
-    o(0x2b);
-    o(0xe0 | r);
-    /* We align to 16 bytes rather than align */
-    /* and ~15, %esp */
-    o(0xf0e483);
-    vpop();
-#endif
-}
-
-/* end of X86 code generator */
-/*************************************************************/
-#endif
-/*************************************************************/
diff --git a/tinyc/i386-link.c b/tinyc/i386-link.c
deleted file mode 100644
index b44664404..000000000
--- a/tinyc/i386-link.c
+++ /dev/null
@@ -1,244 +0,0 @@
-#ifdef TARGET_DEFS_ONLY
-
-#define EM_TCC_TARGET EM_386
-
-/* relocation type for 32 bit data relocation */
-#define R_DATA_32   R_386_32
-#define R_DATA_PTR  R_386_32
-#define R_JMP_SLOT  R_386_JMP_SLOT
-#define R_GLOB_DAT  R_386_GLOB_DAT
-#define R_COPY      R_386_COPY
-#define R_RELATIVE  R_386_RELATIVE
-
-#define R_NUM       R_386_NUM
-
-#define ELF_START_ADDR 0x08048000
-#define ELF_PAGE_SIZE  0x1000
-
-#define PCRELATIVE_DLLPLT 0
-#define RELOCATE_DLLPLT 0
-
-#else /* !TARGET_DEFS_ONLY */
-
-#include "tcc.h"
-
-/* Returns 1 for a code relocation, 0 for a data relocation. For unknown
-   relocations, returns -1. */
-int code_reloc (int reloc_type)
-{
-    switch (reloc_type) {
-	case R_386_RELATIVE:
-	case R_386_16:
-        case R_386_32:
-	case R_386_GOTPC:
-	case R_386_GOTOFF:
-	case R_386_GOT32:
-	case R_386_GOT32X:
-	case R_386_GLOB_DAT:
-	case R_386_COPY:
-            return 0;
-
-	case R_386_PC16:
-	case R_386_PC32:
-	case R_386_PLT32:
-	case R_386_JMP_SLOT:
-            return 1;
-    }
-
-    tcc_error ("Unknown relocation type: %d", reloc_type);
-    return -1;
-}
-
-/* Returns an enumerator to describe whether and when the relocation needs a
-   GOT and/or PLT entry to be created. See tcc.h for a description of the
-   different values. */
-int gotplt_entry_type (int reloc_type)
-{
-    switch (reloc_type) {
-	case R_386_RELATIVE:
-	case R_386_16:
-	case R_386_GLOB_DAT:
-	case R_386_JMP_SLOT:
-	case R_386_COPY:
-            return NO_GOTPLT_ENTRY;
-
-        case R_386_32:
-	    /* This relocations shouldn't normally need GOT or PLT
-	       slots if it weren't for simplicity in the code generator.
-	       See our caller for comments.  */
-            return AUTO_GOTPLT_ENTRY;
-
-	case R_386_PC16:
-	case R_386_PC32:
-            return AUTO_GOTPLT_ENTRY;
-
-	case R_386_GOTPC:
-	case R_386_GOTOFF:
-            return BUILD_GOT_ONLY;
-
-	case R_386_GOT32:
-	case R_386_GOT32X:
-	case R_386_PLT32:
-            return ALWAYS_GOTPLT_ENTRY;
-    }
-
-    tcc_error ("Unknown relocation type: %d", reloc_type);
-    return -1;
-}
-
-ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr)
-{
-    Section *plt = s1->plt;
-    uint8_t *p;
-    int modrm;
-    unsigned plt_offset, relofs;
-
-    /* on i386 if we build a DLL, we add a %ebx offset */
-    if (s1->output_type == TCC_OUTPUT_DLL)
-        modrm = 0xa3;
-    else
-        modrm = 0x25;
-
-    /* empty PLT: create PLT0 entry that pushes the library identifier
-       (GOT + PTR_SIZE) and jumps to ld.so resolution routine
-       (GOT + 2 * PTR_SIZE) */
-    if (plt->data_offset == 0) {
-        p = section_ptr_add(plt, 16);
-        p[0] = 0xff; /* pushl got + PTR_SIZE */
-        p[1] = modrm + 0x10;
-        write32le(p + 2, PTR_SIZE);
-        p[6] = 0xff; /* jmp *(got + PTR_SIZE * 2) */
-        p[7] = modrm;
-        write32le(p + 8, PTR_SIZE * 2);
-    }
-    plt_offset = plt->data_offset;
-
-    /* The PLT slot refers to the relocation entry it needs via offset.
-       The reloc entry is created below, so its offset is the current
-       data_offset */
-    relofs = s1->got->reloc ? s1->got->reloc->data_offset : 0;
-
-    /* Jump to GOT entry where ld.so initially put the address of ip + 4 */
-    p = section_ptr_add(plt, 16);
-    p[0] = 0xff; /* jmp *(got + x) */
-    p[1] = modrm;
-    write32le(p + 2, got_offset);
-    p[6] = 0x68; /* push $xxx */
-    write32le(p + 7, relofs);
-    p[11] = 0xe9; /* jmp plt_start */
-    write32le(p + 12, -(plt->data_offset));
-    return plt_offset;
-}
-
-/* relocate the PLT: compute addresses and offsets in the PLT now that final
-   address for PLT and GOT are known (see fill_program_header) */
-ST_FUNC void relocate_plt(TCCState *s1)
-{
-    uint8_t *p, *p_end;
-
-    if (!s1->plt)
-      return;
-
-    p = s1->plt->data;
-    p_end = p + s1->plt->data_offset;
-
-    if (p < p_end) {
-        add32le(p + 2, s1->got->sh_addr);
-        add32le(p + 8, s1->got->sh_addr);
-        p += 16;
-        while (p < p_end) {
-            add32le(p + 2, s1->got->sh_addr);
-            p += 16;
-        }
-    }
-}
-
-static ElfW_Rel *qrel; /* ptr to next reloc entry reused */
-
-void relocate_init(Section *sr)
-{
-    qrel = (ElfW_Rel *) sr->data;
-}
-
-void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val)
-{
-    int sym_index, esym_index;
-
-    sym_index = ELFW(R_SYM)(rel->r_info);
-
-    switch (type) {
-        case R_386_32:
-            if (s1->output_type == TCC_OUTPUT_DLL) {
-                esym_index = s1->sym_attrs[sym_index].dyn_index;
-                qrel->r_offset = rel->r_offset;
-                if (esym_index) {
-                    qrel->r_info = ELFW(R_INFO)(esym_index, R_386_32);
-                    qrel++;
-                    return;
-                } else {
-                    qrel->r_info = ELFW(R_INFO)(0, R_386_RELATIVE);
-                    qrel++;
-                }
-            }
-            add32le(ptr, val);
-            return;
-        case R_386_PC32:
-            if (s1->output_type == TCC_OUTPUT_DLL) {
-                /* DLL relocation */
-                esym_index = s1->sym_attrs[sym_index].dyn_index;
-                if (esym_index) {
-                    qrel->r_offset = rel->r_offset;
-                    qrel->r_info = ELFW(R_INFO)(esym_index, R_386_PC32);
-                    qrel++;
-                    return;
-                }
-            }
-            add32le(ptr, val - addr);
-            return;
-        case R_386_PLT32:
-            add32le(ptr, val - addr);
-            return;
-        case R_386_GLOB_DAT:
-        case R_386_JMP_SLOT:
-            write32le(ptr, val);
-            return;
-        case R_386_GOTPC:
-            add32le(ptr, s1->got->sh_addr - addr);
-            return;
-        case R_386_GOTOFF:
-            add32le(ptr, val - s1->got->sh_addr);
-            return;
-        case R_386_GOT32:
-        case R_386_GOT32X:
-            /* we load the got offset */
-            add32le(ptr, s1->sym_attrs[sym_index].got_offset);
-            return;
-        case R_386_16:
-            if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY) {
-            output_file:
-                tcc_error("can only produce 16-bit binary files");
-            }
-            write16le(ptr, read16le(ptr) + val);
-            return;
-        case R_386_PC16:
-            if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY)
-                goto output_file;
-            write16le(ptr, read16le(ptr) + val - addr);
-            return;
-        case R_386_RELATIVE:
-            /* do nothing */
-            return;
-        case R_386_COPY:
-            /* This relocation must copy initialized data from the library
-            to the program .bss segment. Currently made like for ARM
-            (to remove noise of default case). Is this true?
-            */
-            return;
-        default:
-            fprintf(stderr,"FIXME: handle reloc type %d at %x [%p] to %x\n",
-                type, (unsigned)addr, ptr, (unsigned)val);
-            return;
-    }
-}
-
-#endif /* !TARGET_DEFS_ONLY */
diff --git a/tinyc/i386-tok.h b/tinyc/i386-tok.h
deleted file mode 100644
index 8c25af09d..000000000
--- a/tinyc/i386-tok.h
+++ /dev/null
@@ -1,253 +0,0 @@
-/* ------------------------------------------------------------------ */
-/* WARNING: relative order of tokens is important. */
-
-/* register */
- DEF_ASM(al)
- DEF_ASM(cl)
- DEF_ASM(dl)
- DEF_ASM(bl)
- DEF_ASM(ah)
- DEF_ASM(ch)
- DEF_ASM(dh)
- DEF_ASM(bh)
- DEF_ASM(ax)
- DEF_ASM(cx)
- DEF_ASM(dx)
- DEF_ASM(bx)
- DEF_ASM(sp)
- DEF_ASM(bp)
- DEF_ASM(si)
- DEF_ASM(di)
- DEF_ASM(eax)
- DEF_ASM(ecx)
- DEF_ASM(edx)
- DEF_ASM(ebx)
- DEF_ASM(esp)
- DEF_ASM(ebp)
- DEF_ASM(esi)
- DEF_ASM(edi)
-#ifdef TCC_TARGET_X86_64
- DEF_ASM(rax)
- DEF_ASM(rcx)
- DEF_ASM(rdx)
- DEF_ASM(rbx)
- DEF_ASM(rsp)
- DEF_ASM(rbp)
- DEF_ASM(rsi)
- DEF_ASM(rdi)
-#endif
- DEF_ASM(mm0)
- DEF_ASM(mm1)
- DEF_ASM(mm2)
- DEF_ASM(mm3)
- DEF_ASM(mm4)
- DEF_ASM(mm5)
- DEF_ASM(mm6)
- DEF_ASM(mm7)
- DEF_ASM(xmm0)
- DEF_ASM(xmm1)
- DEF_ASM(xmm2)
- DEF_ASM(xmm3)
- DEF_ASM(xmm4)
- DEF_ASM(xmm5)
- DEF_ASM(xmm6)
- DEF_ASM(xmm7)
- DEF_ASM(cr0)
- DEF_ASM(cr1)
- DEF_ASM(cr2)
- DEF_ASM(cr3)
- DEF_ASM(cr4)
- DEF_ASM(cr5)
- DEF_ASM(cr6)
- DEF_ASM(cr7)
- DEF_ASM(tr0)
- DEF_ASM(tr1)
- DEF_ASM(tr2)
- DEF_ASM(tr3)
- DEF_ASM(tr4)
- DEF_ASM(tr5)
- DEF_ASM(tr6)
- DEF_ASM(tr7)
- DEF_ASM(db0)
- DEF_ASM(db1)
- DEF_ASM(db2)
- DEF_ASM(db3)
- DEF_ASM(db4)
- DEF_ASM(db5)
- DEF_ASM(db6)
- DEF_ASM(db7)
- DEF_ASM(dr0)
- DEF_ASM(dr1)
- DEF_ASM(dr2)
- DEF_ASM(dr3)
- DEF_ASM(dr4)
- DEF_ASM(dr5)
- DEF_ASM(dr6)
- DEF_ASM(dr7)
- DEF_ASM(es)
- DEF_ASM(cs)
- DEF_ASM(ss)
- DEF_ASM(ds)
- DEF_ASM(fs)
- DEF_ASM(gs)
- DEF_ASM(st)
- DEF_ASM(rip)
-
-#ifdef TCC_TARGET_X86_64
- /* The four low parts of sp/bp/si/di that exist only on
-    x86-64 (encoding aliased to ah,ch,dh,dh when not using REX). */
- DEF_ASM(spl)
- DEF_ASM(bpl)
- DEF_ASM(sil)
- DEF_ASM(dil)
-#endif
- /* generic two operands */
- DEF_BWLX(mov)
-
- DEF_BWLX(add)
- DEF_BWLX(or)
- DEF_BWLX(adc)
- DEF_BWLX(sbb)
- DEF_BWLX(and)
- DEF_BWLX(sub)
- DEF_BWLX(xor)
- DEF_BWLX(cmp)
-
- /* unary ops */
- DEF_BWLX(inc)
- DEF_BWLX(dec)
- DEF_BWLX(not)
- DEF_BWLX(neg)
- DEF_BWLX(mul)
- DEF_BWLX(imul)
- DEF_BWLX(div)
- DEF_BWLX(idiv)
-
- DEF_BWLX(xchg)
- DEF_BWLX(test)
-
- /* shifts */
- DEF_BWLX(rol)
- DEF_BWLX(ror)
- DEF_BWLX(rcl)
- DEF_BWLX(rcr)
- DEF_BWLX(shl)
- DEF_BWLX(shr)
- DEF_BWLX(sar)
-
- DEF_WLX(shld)
- DEF_WLX(shrd)
-
- DEF_ASM(pushw)
- DEF_ASM(pushl)
-#ifdef TCC_TARGET_X86_64
- DEF_ASM(pushq)
-#endif
- DEF_ASM(push)
-
- DEF_ASM(popw)
- DEF_ASM(popl)
-#ifdef TCC_TARGET_X86_64
- DEF_ASM(popq)
-#endif
- DEF_ASM(pop)
-
- DEF_BWL(in)
- DEF_BWL(out)
-
- DEF_WLX(movzb)
- DEF_ASM(movzwl)
- DEF_ASM(movsbw)
- DEF_ASM(movsbl)
- DEF_ASM(movswl)
-#ifdef TCC_TARGET_X86_64
- DEF_ASM(movsbq)
- DEF_ASM(movswq)
- DEF_ASM(movzwq)
- DEF_ASM(movslq)
-#endif
-
- DEF_WLX(lea)
-
- DEF_ASM(les)
- DEF_ASM(lds)
- DEF_ASM(lss)
- DEF_ASM(lfs)
- DEF_ASM(lgs)
-
- DEF_ASM(call)
- DEF_ASM(jmp)
- DEF_ASM(lcall)
- DEF_ASM(ljmp)
-
- DEF_ASMTEST(j,)
-
- DEF_ASMTEST(set,)
- DEF_ASMTEST(set,b)
- DEF_ASMTEST(cmov,)
-
- DEF_WLX(bsf)
- DEF_WLX(bsr)
- DEF_WLX(bt)
- DEF_WLX(bts)
- DEF_WLX(btr)
- DEF_WLX(btc)
-
- DEF_WLX(lar)
- DEF_WLX(lsl)
-
- /* generic FP ops */
- DEF_FP(add)
- DEF_FP(mul)
-
- DEF_ASM(fcom)
- DEF_ASM(fcom_1) /* non existent op, just to have a regular table */
- DEF_FP1(com)
-
- DEF_FP(comp)
- DEF_FP(sub)
- DEF_FP(subr)
- DEF_FP(div)
- DEF_FP(divr)
-
- DEF_BWLX(xadd)
- DEF_BWLX(cmpxchg)
-
- /* string ops */
- DEF_BWLX(cmps)
- DEF_BWLX(scmp)
- DEF_BWL(ins)
- DEF_BWL(outs)
- DEF_BWLX(lods)
- DEF_BWLX(slod)
- DEF_BWLX(movs)
- DEF_BWLX(smov)
- DEF_BWLX(scas)
- DEF_BWLX(ssca)
- DEF_BWLX(stos)
- DEF_BWLX(ssto)
-
- /* generic asm ops */
-#define ALT(x)
-#define DEF_ASM_OP0(name, opcode) DEF_ASM(name)
-#define DEF_ASM_OP0L(name, opcode, group, instr_type)
-#define DEF_ASM_OP1(name, opcode, group, instr_type, op0)
-#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1)
-#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2)
-#ifdef TCC_TARGET_X86_64
-# include "x86_64-asm.h"
-#else
-# include "i386-asm.h"
-#endif
-
-#define ALT(x)
-#define DEF_ASM_OP0(name, opcode)
-#define DEF_ASM_OP0L(name, opcode, group, instr_type) DEF_ASM(name)
-#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) DEF_ASM(name)
-#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) DEF_ASM(name)
-#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) DEF_ASM(name)
-#ifdef TCC_TARGET_X86_64
-# include "x86_64-asm.h"
-#else
-# include "i386-asm.h"
-#endif
diff --git a/tinyc/il-gen.c b/tinyc/il-gen.c
deleted file mode 100644
index bb670ccb0..000000000
--- a/tinyc/il-gen.c
+++ /dev/null
@@ -1,657 +0,0 @@
-/*
- *  CIL code generator for TCC
- * 
- *  Copyright (c) 2002 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#error this code has bit-rotted since 2003
-
-/* number of available registers */
-#define NB_REGS             3
-
-/* a register can belong to several classes. The classes must be
-   sorted from more general to more precise (see gv2() code which does
-   assumptions on it). */
-#define RC_ST      0x0001  /* any stack entry */
-#define RC_ST0     0x0002  /* top of stack */
-#define RC_ST1     0x0004  /* top - 1 */
-
-#define RC_INT     RC_ST
-#define RC_FLOAT   RC_ST
-#define RC_IRET    RC_ST0 /* function return: integer register */
-#define RC_LRET    RC_ST0 /* function return: second integer register */
-#define RC_FRET    RC_ST0 /* function return: float register */
-
-/* pretty names for the registers */
-enum {
-    REG_ST0 = 0,
-    REG_ST1,
-    REG_ST2,
-};
-
-const int reg_classes[NB_REGS] = {
-    /* ST0 */ RC_ST | RC_ST0,
-    /* ST1 */ RC_ST | RC_ST1,
-    /* ST2 */ RC_ST,
-};
-
-/* return registers for function */
-#define REG_IRET REG_ST0 /* single word int return register */
-#define REG_LRET REG_ST0 /* second word return register (for long long) */
-#define REG_FRET REG_ST0 /* float return register */
-
-/* defined if function parameters must be evaluated in reverse order */
-/* #define INVERT_FUNC_PARAMS */
-
-/* defined if structures are passed as pointers. Otherwise structures
-   are directly pushed on stack. */
-/* #define FUNC_STRUCT_PARAM_AS_PTR */
-
-/* pointer size, in bytes */
-#define PTR_SIZE 4
-
-/* long double size and alignment, in bytes */
-#define LDOUBLE_SIZE  8
-#define LDOUBLE_ALIGN 8
-
-/* function call context */
-typedef struct GFuncContext {
-    int func_call; /* func call type (FUNC_STDCALL or FUNC_CDECL) */
-} GFuncContext;
-
-/******************************************************/
-/* opcode definitions */
-
-#define IL_OP_PREFIX 0xFE
-
-enum ILOPCodes {
-#define OP(name, str, n) IL_OP_ ## name = n,
-#include "il-opcodes.h"
-#undef OP
-};
-
-char *il_opcodes_str[] = {
-#define OP(name, str, n) [n] = str,
-#include "il-opcodes.h"
-#undef OP
-};
-
-/******************************************************/
-
-/* arguments variable numbers start from there */
-#define ARG_BASE 0x70000000
-
-static FILE *il_outfile;
-
-static void out_byte(int c)
-{
-    *(char *)ind++ = c;
-}
-
-static void out_le32(int c)
-{
-    out_byte(c);
-    out_byte(c >> 8);
-    out_byte(c >> 16);
-    out_byte(c >> 24);
-}
-
-static void init_outfile(void)
-{
-    if (!il_outfile) {
-        il_outfile = stdout;
-        fprintf(il_outfile, 
-                ".assembly extern mscorlib\n"
-                "{\n"
-                ".ver 1:0:2411:0\n"
-                "}\n\n");
-    }
-}
-
-static void out_op1(int op)
-{
-    if (op & 0x100)
-        out_byte(IL_OP_PREFIX);
-    out_byte(op & 0xff);
-}
-
-/* output an opcode with prefix */
-static void out_op(int op)
-{
-    out_op1(op);
-    fprintf(il_outfile, " %s\n", il_opcodes_str[op]);
-}
-
-static void out_opb(int op, int c)
-{
-    out_op1(op);
-    out_byte(c);
-    fprintf(il_outfile, " %s %d\n", il_opcodes_str[op], c);
-}
-
-static void out_opi(int op, int c)
-{
-    out_op1(op);
-    out_le32(c);
-    fprintf(il_outfile, " %s 0x%x\n", il_opcodes_str[op], c);
-}
-
-/* XXX: not complete */
-static void il_type_to_str(char *buf, int buf_size, 
-                           int t, const char *varstr)
-{
-    int bt;
-    Sym *s, *sa;
-    char buf1[256];
-    const char *tstr;
-
-    t = t & VT_TYPE;
-    bt = t & VT_BTYPE;
-    buf[0] = '\0';
-    if (t & VT_UNSIGNED)
-        pstrcat(buf, buf_size, "unsigned ");
-    switch(bt) {
-    case VT_VOID:
-        tstr = "void";
-        goto add_tstr;
-    case VT_BOOL:
-        tstr = "bool";
-        goto add_tstr;
-    case VT_BYTE:
-        tstr = "int8";
-        goto add_tstr;
-    case VT_SHORT:
-        tstr = "int16";
-        goto add_tstr;
-    case VT_ENUM:
-    case VT_INT:
-    case VT_LONG:
-        tstr = "int32";
-        goto add_tstr;
-    case VT_LLONG:
-        tstr = "int64";
-        goto add_tstr;
-    case VT_FLOAT:
-        tstr = "float32";
-        goto add_tstr;
-    case VT_DOUBLE:
-    case VT_LDOUBLE:
-        tstr = "float64";
-    add_tstr:
-        pstrcat(buf, buf_size, tstr);
-        break;
-    case VT_STRUCT:
-        tcc_error("structures not handled yet");
-        break;
-    case VT_FUNC:
-        s = sym_find((unsigned)t >> VT_STRUCT_SHIFT);
-        il_type_to_str(buf, buf_size, s->t, varstr);
-        pstrcat(buf, buf_size, "(");
-        sa = s->next;
-        while (sa != NULL) {
-            il_type_to_str(buf1, sizeof(buf1), sa->t, NULL);
-            pstrcat(buf, buf_size, buf1);
-            sa = sa->next;
-            if (sa)
-                pstrcat(buf, buf_size, ", ");
-        }
-        pstrcat(buf, buf_size, ")");
-        goto no_var;
-    case VT_PTR:
-        s = sym_find((unsigned)t >> VT_STRUCT_SHIFT);
-        pstrcpy(buf1, sizeof(buf1), "*");
-        if (varstr)
-            pstrcat(buf1, sizeof(buf1), varstr);
-        il_type_to_str(buf, buf_size, s->t, buf1);
-        goto no_var;
-    }
-    if (varstr) {
-        pstrcat(buf, buf_size, " ");
-        pstrcat(buf, buf_size, varstr);
-    }
- no_var: ;
-}
-
-
-/* patch relocation entry with value 'val' */
-void greloc_patch1(Reloc *p, int val)
-{
-}
-
-/* output a symbol and patch all calls to it */
-void gsym_addr(t, a)
-{
-}
-
-/* output jump and return symbol */
-static int out_opj(int op, int c)
-{
-    out_op1(op);
-    out_le32(0);
-    if (c == 0) {
-        c = ind - (int)cur_text_section->data;
-    }
-    fprintf(il_outfile, " %s L%d\n", il_opcodes_str[op], c);
-    return c;
-}
-
-void gsym(int t)
-{
-    fprintf(il_outfile, "L%d:\n", t);
-}
-
-/* load 'r' from value 'sv' */
-void load(int r, SValue *sv)
-{
-    int v, fc, ft;
-
-    v = sv->r & VT_VALMASK;
-    fc = sv->c.i;
-    ft = sv->t;
-
-    if (sv->r & VT_LVAL) {
-        if (v == VT_LOCAL) {
-            if (fc >= ARG_BASE) {
-                fc -= ARG_BASE;
-                if (fc >= 0 && fc <= 4) {
-                    out_op(IL_OP_LDARG_0 + fc);
-                } else if (fc <= 0xff) {
-                    out_opb(IL_OP_LDARG_S, fc);
-                } else {
-                    out_opi(IL_OP_LDARG, fc);
-                }
-            } else {
-                if (fc >= 0 && fc <= 4) {
-                    out_op(IL_OP_LDLOC_0 + fc);
-                } else if (fc <= 0xff) {
-                    out_opb(IL_OP_LDLOC_S, fc);
-                } else {
-                    out_opi(IL_OP_LDLOC, fc);
-                }
-            }
-        } else if (v == VT_CONST) {
-                /* XXX: handle globals */
-                out_opi(IL_OP_LDSFLD, 0);
-        } else {
-            if ((ft & VT_BTYPE) == VT_FLOAT) {
-                out_op(IL_OP_LDIND_R4);
-            } else if ((ft & VT_BTYPE) == VT_DOUBLE) {
-                out_op(IL_OP_LDIND_R8);
-            } else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
-                out_op(IL_OP_LDIND_R8);
-            } else if ((ft & VT_TYPE) == VT_BYTE)
-                out_op(IL_OP_LDIND_I1);
-            else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED))
-                out_op(IL_OP_LDIND_U1);
-            else if ((ft & VT_TYPE) == VT_SHORT)
-                out_op(IL_OP_LDIND_I2);
-            else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED))
-                out_op(IL_OP_LDIND_U2);
-            else
-                out_op(IL_OP_LDIND_I4);
-        } 
-    } else {
-        if (v == VT_CONST) {
-            /* XXX: handle globals */
-            if (fc >= -1 && fc <= 8) {
-                out_op(IL_OP_LDC_I4_M1 + fc + 1); 
-            } else {
-                out_opi(IL_OP_LDC_I4, fc);
-            }
-        } else if (v == VT_LOCAL) {
-            if (fc >= ARG_BASE) {
-                fc -= ARG_BASE;
-                if (fc <= 0xff) {
-                    out_opb(IL_OP_LDARGA_S, fc);
-                } else {
-                    out_opi(IL_OP_LDARGA, fc);
-                }
-            } else {
-                if (fc <= 0xff) {
-                    out_opb(IL_OP_LDLOCA_S, fc);
-                } else {
-                    out_opi(IL_OP_LDLOCA, fc);
-                }
-            }
-        } else {
-            /* XXX: do it */
-        }
-    }
-}
-
-/* store register 'r' in lvalue 'v' */
-void store(int r, SValue *sv)
-{
-    int v, fc, ft;
-
-    v = sv->r & VT_VALMASK;
-    fc = sv->c.i;
-    ft = sv->t;
-    if (v == VT_LOCAL) {
-        if (fc >= ARG_BASE) {
-            fc -= ARG_BASE;
-            /* XXX: check IL arg store semantics */
-            if (fc <= 0xff) {
-                out_opb(IL_OP_STARG_S, fc);
-            } else {
-                out_opi(IL_OP_STARG, fc);
-            }
-        } else {
-            if (fc >= 0 && fc <= 4) {
-                out_op(IL_OP_STLOC_0 + fc);
-            } else if (fc <= 0xff) {
-                out_opb(IL_OP_STLOC_S, fc);
-            } else {
-                out_opi(IL_OP_STLOC, fc);
-            }
-        }
-    } else if (v == VT_CONST) {
-        /* XXX: handle globals */
-        out_opi(IL_OP_STSFLD, 0);
-    } else {
-        if ((ft & VT_BTYPE) == VT_FLOAT)
-            out_op(IL_OP_STIND_R4);
-        else if ((ft & VT_BTYPE) == VT_DOUBLE)
-            out_op(IL_OP_STIND_R8);
-        else if ((ft & VT_BTYPE) == VT_LDOUBLE)
-            out_op(IL_OP_STIND_R8);
-        else if ((ft & VT_BTYPE) == VT_BYTE)
-            out_op(IL_OP_STIND_I1);
-        else if ((ft & VT_BTYPE) == VT_SHORT)
-            out_op(IL_OP_STIND_I2);
-        else
-            out_op(IL_OP_STIND_I4);
-    }
-}
-
-/* start function call and return function call context */
-void gfunc_start(GFuncContext *c, int func_call)
-{
-    c->func_call = func_call;
-}
-
-/* push function parameter which is in (vtop->t, vtop->c). Stack entry
-   is then popped. */
-void gfunc_param(GFuncContext *c)
-{
-    if ((vtop->t & VT_BTYPE) == VT_STRUCT) {
-        tcc_error("structures passed as value not handled yet");
-    } else {
-        /* simply push on stack */
-        gv(RC_ST0);
-    }
-    vtop--;
-}
-
-/* generate function call with address in (vtop->t, vtop->c) and free function
-   context. Stack entry is popped */
-void gfunc_call(GFuncContext *c)
-{
-    char buf[1024];
-
-    if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
-        /* XXX: more info needed from tcc */
-        il_type_to_str(buf, sizeof(buf), vtop->t, "xxx");
-        fprintf(il_outfile, " call %s\n", buf);
-    } else {
-        /* indirect call */
-        gv(RC_INT);
-        il_type_to_str(buf, sizeof(buf), vtop->t, NULL);
-        fprintf(il_outfile, " calli %s\n", buf);
-    }
-    vtop--;
-}
-
-/* generate function prolog of type 't' */
-void gfunc_prolog(int t)
-{
-    int addr, u, func_call;
-    Sym *sym;
-    char buf[1024];
-
-    init_outfile();
-
-    /* XXX: pass function name to gfunc_prolog */
-    il_type_to_str(buf, sizeof(buf), t, funcname);
-    fprintf(il_outfile, ".method static %s il managed\n", buf);
-    fprintf(il_outfile, "{\n");
-    /* XXX: cannot do better now */
-    fprintf(il_outfile, " .maxstack %d\n", NB_REGS);
-    fprintf(il_outfile, " .locals (int32, int32, int32, int32, int32, int32, int32, int32)\n");
-    
-    if (!strcmp(funcname, "main"))
-        fprintf(il_outfile, " .entrypoint\n");
-        
-    sym = sym_find((unsigned)t >> VT_STRUCT_SHIFT);
-    func_call = sym->r;
-
-    addr = ARG_BASE;
-    /* if the function returns a structure, then add an
-       implicit pointer parameter */
-    func_vt = sym->t;
-    func_var = (sym->c == FUNC_ELLIPSIS);
-    if ((func_vt & VT_BTYPE) == VT_STRUCT) {
-        func_vc = addr;
-        addr++;
-    }
-    /* define parameters */
-    while ((sym = sym->next) != NULL) {
-        u = sym->t;
-        sym_push(sym->v & ~SYM_FIELD, u,
-                 VT_LOCAL | lvalue_type(sym->type.t), addr);
-        addr++;
-    }
-}
-
-/* generate function epilog */
-void gfunc_epilog(void)
-{
-    out_op(IL_OP_RET);
-    fprintf(il_outfile, "}\n\n");
-}
-
-/* generate a jump to a label */
-int gjmp(int t)
-{
-    return out_opj(IL_OP_BR, t);
-}
-
-/* generate a jump to a fixed address */
-void gjmp_addr(int a)
-{
-    /* XXX: handle syms */
-    out_opi(IL_OP_BR, a);
-}
-
-/* generate a test. set 'inv' to invert test. Stack entry is popped */
-int gtst(int inv, int t)
-{
-    int v, *p, c;
-
-    v = vtop->r & VT_VALMASK;
-    if (v == VT_CMP) {
-        c = vtop->c.i ^ inv;
-        switch(c) {
-        case TOK_EQ:
-            c = IL_OP_BEQ;
-            break;
-        case TOK_NE:
-            c = IL_OP_BNE_UN;
-            break;
-        case TOK_LT:
-            c = IL_OP_BLT;
-            break;
-        case TOK_LE:
-            c = IL_OP_BLE;
-            break;
-        case TOK_GT:
-            c = IL_OP_BGT;
-            break;
-        case TOK_GE:
-            c = IL_OP_BGE;
-            break;
-        case TOK_ULT:
-            c = IL_OP_BLT_UN;
-            break;
-        case TOK_ULE:
-            c = IL_OP_BLE_UN;
-            break;
-        case TOK_UGT:
-            c = IL_OP_BGT_UN;
-            break;
-        case TOK_UGE:
-            c = IL_OP_BGE_UN;
-            break;
-        }
-        t = out_opj(c, t);
-    } else if (v == VT_JMP || v == VT_JMPI) {
-        /* && or || optimization */
-        if ((v & 1) == inv) {
-            /* insert vtop->c jump list in t */
-            p = &vtop->c.i;
-            while (*p != 0)
-                p = (int *)*p;
-            *p = t;
-            t = vtop->c.i;
-        } else {
-            t = gjmp(t);
-            gsym(vtop->c.i);
-        }
-    }
-    vtop--;
-    return t;
-}
-
-/* generate an integer binary operation */
-void gen_opi(int op)
-{
-    gv2(RC_ST1, RC_ST0);
-    switch(op) {
-    case '+':
-        out_op(IL_OP_ADD);
-        goto std_op;
-    case '-':
-        out_op(IL_OP_SUB);
-        goto std_op;
-    case '&':
-        out_op(IL_OP_AND);
-        goto std_op;
-    case '^':
-        out_op(IL_OP_XOR);
-        goto std_op;
-    case '|':
-        out_op(IL_OP_OR);
-        goto std_op;
-    case '*':
-        out_op(IL_OP_MUL);
-        goto std_op;
-    case TOK_SHL:
-        out_op(IL_OP_SHL);
-        goto std_op;
-    case TOK_SHR:
-        out_op(IL_OP_SHR_UN);
-        goto std_op;
-    case TOK_SAR:
-        out_op(IL_OP_SHR);
-        goto std_op;
-    case '/':
-    case TOK_PDIV:
-        out_op(IL_OP_DIV);
-        goto std_op;
-    case TOK_UDIV:
-        out_op(IL_OP_DIV_UN);
-        goto std_op;
-    case '%':
-        out_op(IL_OP_REM);
-        goto std_op;
-    case TOK_UMOD:
-        out_op(IL_OP_REM_UN);
-    std_op:
-        vtop--;
-        vtop[0].r = REG_ST0;
-        break;
-    case TOK_EQ:
-    case TOK_NE:
-    case TOK_LT:
-    case TOK_LE:
-    case TOK_GT:
-    case TOK_GE:
-    case TOK_ULT:
-    case TOK_ULE:
-    case TOK_UGT:
-    case TOK_UGE:
-        vtop--;
-        vtop[0].r = VT_CMP;
-        vtop[0].c.i = op;
-        break;
-    }
-}
-
-/* generate a floating point operation 'v = t1 op t2' instruction. The
-   two operands are guaranteed to have the same floating point type */
-void gen_opf(int op)
-{
-    /* same as integer */
-    gen_opi(op);
-}
-
-/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
-   and 'long long' cases. */
-void gen_cvt_itof(int t)
-{
-    gv(RC_ST0);
-    if (t == VT_FLOAT)
-        out_op(IL_OP_CONV_R4);
-    else
-        out_op(IL_OP_CONV_R8);
-}
-
-/* convert fp to int 't' type */
-/* XXX: handle long long case */
-void gen_cvt_ftoi(int t)
-{
-    gv(RC_ST0);
-    switch(t) {
-    case VT_INT | VT_UNSIGNED:
-        out_op(IL_OP_CONV_U4);
-        break;
-    case VT_LLONG:
-        out_op(IL_OP_CONV_I8);
-        break;
-    case VT_LLONG | VT_UNSIGNED:
-        out_op(IL_OP_CONV_U8);
-        break;
-    default:
-        out_op(IL_OP_CONV_I4);
-        break;
-    }
-}
-
-/* convert from one floating point type to another */
-void gen_cvt_ftof(int t)
-{
-    gv(RC_ST0);
-    if (t == VT_FLOAT) {
-        out_op(IL_OP_CONV_R4);
-    } else {
-        out_op(IL_OP_CONV_R8);
-    }
-}
-
-/* end of CIL code generator */
-/*************************************************************/
-
diff --git a/tinyc/il-opcodes.h b/tinyc/il-opcodes.h
deleted file mode 100644
index d53ffb2c5..000000000
--- a/tinyc/il-opcodes.h
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- *  CIL opcode definition
- * 
- *  Copyright (c) 2002 Fabrice Bellard
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-OP(NOP, "nop", 0x00)
-OP(BREAK, "break", 0x01)
-OP(LDARG_0, "ldarg.0", 0x02)
-OP(LDARG_1, "ldarg.1", 0x03)
-OP(LDARG_2, "ldarg.2", 0x04)
-OP(LDARG_3, "ldarg.3", 0x05)
-OP(LDLOC_0, "ldloc.0", 0x06)
-OP(LDLOC_1, "ldloc.1", 0x07)
-OP(LDLOC_2, "ldloc.2", 0x08)
-OP(LDLOC_3, "ldloc.3", 0x09)
-OP(STLOC_0, "stloc.0", 0x0a)
-OP(STLOC_1, "stloc.1", 0x0b)
-OP(STLOC_2, "stloc.2", 0x0c)
-OP(STLOC_3, "stloc.3", 0x0d)
-OP(LDARG_S, "ldarg.s", 0x0e)
-OP(LDARGA_S, "ldarga.s", 0x0f)
-OP(STARG_S, "starg.s", 0x10)
-OP(LDLOC_S, "ldloc.s", 0x11)
-OP(LDLOCA_S, "ldloca.s", 0x12)
-OP(STLOC_S, "stloc.s", 0x13)
-OP(LDNULL, "ldnull", 0x14)
-OP(LDC_I4_M1, "ldc.i4.m1", 0x15)
-OP(LDC_I4_0, "ldc.i4.0", 0x16)
-OP(LDC_I4_1, "ldc.i4.1", 0x17)
-OP(LDC_I4_2, "ldc.i4.2", 0x18)
-OP(LDC_I4_3, "ldc.i4.3", 0x19)
-OP(LDC_I4_4, "ldc.i4.4", 0x1a)
-OP(LDC_I4_5, "ldc.i4.5", 0x1b)
-OP(LDC_I4_6, "ldc.i4.6", 0x1c)
-OP(LDC_I4_7, "ldc.i4.7", 0x1d)
-OP(LDC_I4_8, "ldc.i4.8", 0x1e)
-OP(LDC_I4_S, "ldc.i4.s", 0x1f)
-OP(LDC_I4, "ldc.i4", 0x20)
-OP(LDC_I8, "ldc.i8", 0x21)
-OP(LDC_R4, "ldc.r4", 0x22)
-OP(LDC_R8, "ldc.r8", 0x23)
-OP(LDPTR, "ldptr", 0x24)
-OP(DUP, "dup", 0x25)
-OP(POP, "pop", 0x26)
-OP(JMP, "jmp", 0x27)
-OP(CALL, "call", 0x28)
-OP(CALLI, "calli", 0x29)
-OP(RET, "ret", 0x2a)
-OP(BR_S, "br.s", 0x2b)
-OP(BRFALSE_S, "brfalse.s", 0x2c)
-OP(BRTRUE_S, "brtrue.s", 0x2d)
-OP(BEQ_S, "beq.s", 0x2e)
-OP(BGE_S, "bge.s", 0x2f)
-OP(BGT_S, "bgt.s", 0x30)
-OP(BLE_S, "ble.s", 0x31)
-OP(BLT_S, "blt.s", 0x32)
-OP(BNE_UN_S, "bne.un.s", 0x33)
-OP(BGE_UN_S, "bge.un.s", 0x34)
-OP(BGT_UN_S, "bgt.un.s", 0x35)
-OP(BLE_UN_S, "ble.un.s", 0x36)
-OP(BLT_UN_S, "blt.un.s", 0x37)
-OP(BR, "br", 0x38)
-OP(BRFALSE, "brfalse", 0x39)
-OP(BRTRUE, "brtrue", 0x3a)
-OP(BEQ, "beq", 0x3b)
-OP(BGE, "bge", 0x3c)
-OP(BGT, "bgt", 0x3d)
-OP(BLE, "ble", 0x3e)
-OP(BLT, "blt", 0x3f)
-OP(BNE_UN, "bne.un", 0x40)
-OP(BGE_UN, "bge.un", 0x41)
-OP(BGT_UN, "bgt.un", 0x42)
-OP(BLE_UN, "ble.un", 0x43)
-OP(BLT_UN, "blt.un", 0x44)
-OP(SWITCH, "switch", 0x45)
-OP(LDIND_I1, "ldind.i1", 0x46)
-OP(LDIND_U1, "ldind.u1", 0x47)
-OP(LDIND_I2, "ldind.i2", 0x48)
-OP(LDIND_U2, "ldind.u2", 0x49)
-OP(LDIND_I4, "ldind.i4", 0x4a)
-OP(LDIND_U4, "ldind.u4", 0x4b)
-OP(LDIND_I8, "ldind.i8", 0x4c)
-OP(LDIND_I, "ldind.i", 0x4d)
-OP(LDIND_R4, "ldind.r4", 0x4e)
-OP(LDIND_R8, "ldind.r8", 0x4f)
-OP(LDIND_REF, "ldind.ref", 0x50)
-OP(STIND_REF, "stind.ref", 0x51)
-OP(STIND_I1, "stind.i1", 0x52)
-OP(STIND_I2, "stind.i2", 0x53)
-OP(STIND_I4, "stind.i4", 0x54)
-OP(STIND_I8, "stind.i8", 0x55)
-OP(STIND_R4, "stind.r4", 0x56)
-OP(STIND_R8, "stind.r8", 0x57)
-OP(ADD, "add", 0x58)
-OP(SUB, "sub", 0x59)
-OP(MUL, "mul", 0x5a)
-OP(DIV, "div", 0x5b)
-OP(DIV_UN, "div.un", 0x5c)
-OP(REM, "rem", 0x5d)
-OP(REM_UN, "rem.un", 0x5e)
-OP(AND, "and", 0x5f)
-OP(OR, "or", 0x60)
-OP(XOR, "xor", 0x61)
-OP(SHL, "shl", 0x62)
-OP(SHR, "shr", 0x63)
-OP(SHR_UN, "shr.un", 0x64)
-OP(NEG, "neg", 0x65)
-OP(NOT, "not", 0x66)
-OP(CONV_I1, "conv.i1", 0x67)
-OP(CONV_I2, "conv.i2", 0x68)
-OP(CONV_I4, "conv.i4", 0x69)
-OP(CONV_I8, "conv.i8", 0x6a)
-OP(CONV_R4, "conv.r4", 0x6b)
-OP(CONV_R8, "conv.r8", 0x6c)
-OP(CONV_U4, "conv.u4", 0x6d)
-OP(CONV_U8, "conv.u8", 0x6e)
-OP(CALLVIRT, "callvirt", 0x6f)
-OP(CPOBJ, "cpobj", 0x70)
-OP(LDOBJ, "ldobj", 0x71)
-OP(LDSTR, "ldstr", 0x72)
-OP(NEWOBJ, "newobj", 0x73)
-OP(CASTCLASS, "castclass", 0x74)
-OP(ISINST, "isinst", 0x75)
-OP(CONV_R_UN, "conv.r.un", 0x76)
-OP(ANN_DATA_S, "ann.data.s", 0x77)
-OP(UNBOX, "unbox", 0x79)
-OP(THROW, "throw", 0x7a)
-OP(LDFLD, "ldfld", 0x7b)
-OP(LDFLDA, "ldflda", 0x7c)
-OP(STFLD, "stfld", 0x7d)
-OP(LDSFLD, "ldsfld", 0x7e)
-OP(LDSFLDA, "ldsflda", 0x7f)
-OP(STSFLD, "stsfld", 0x80)
-OP(STOBJ, "stobj", 0x81)
-OP(CONV_OVF_I1_UN, "conv.ovf.i1.un", 0x82)
-OP(CONV_OVF_I2_UN, "conv.ovf.i2.un", 0x83)
-OP(CONV_OVF_I4_UN, "conv.ovf.i4.un", 0x84)
-OP(CONV_OVF_I8_UN, "conv.ovf.i8.un", 0x85)
-OP(CONV_OVF_U1_UN, "conv.ovf.u1.un", 0x86)
-OP(CONV_OVF_U2_UN, "conv.ovf.u2.un", 0x87)
-OP(CONV_OVF_U4_UN, "conv.ovf.u4.un", 0x88)
-OP(CONV_OVF_U8_UN, "conv.ovf.u8.un", 0x89)
-OP(CONV_OVF_I_UN, "conv.ovf.i.un", 0x8a)
-OP(CONV_OVF_U_UN, "conv.ovf.u.un", 0x8b)
-OP(BOX, "box", 0x8c)
-OP(NEWARR, "newarr", 0x8d)
-OP(LDLEN, "ldlen", 0x8e)
-OP(LDELEMA, "ldelema", 0x8f)
-OP(LDELEM_I1, "ldelem.i1", 0x90)
-OP(LDELEM_U1, "ldelem.u1", 0x91)
-OP(LDELEM_I2, "ldelem.i2", 0x92)
-OP(LDELEM_U2, "ldelem.u2", 0x93)
-OP(LDELEM_I4, "ldelem.i4", 0x94)
-OP(LDELEM_U4, "ldelem.u4", 0x95)
-OP(LDELEM_I8, "ldelem.i8", 0x96)
-OP(LDELEM_I, "ldelem.i", 0x97)
-OP(LDELEM_R4, "ldelem.r4", 0x98)
-OP(LDELEM_R8, "ldelem.r8", 0x99)
-OP(LDELEM_REF, "ldelem.ref", 0x9a)
-OP(STELEM_I, "stelem.i", 0x9b)
-OP(STELEM_I1, "stelem.i1", 0x9c)
-OP(STELEM_I2, "stelem.i2", 0x9d)
-OP(STELEM_I4, "stelem.i4", 0x9e)
-OP(STELEM_I8, "stelem.i8", 0x9f)
-OP(STELEM_R4, "stelem.r4", 0xa0)
-OP(STELEM_R8, "stelem.r8", 0xa1)
-OP(STELEM_REF, "stelem.ref", 0xa2)
-OP(CONV_OVF_I1, "conv.ovf.i1", 0xb3)
-OP(CONV_OVF_U1, "conv.ovf.u1", 0xb4)
-OP(CONV_OVF_I2, "conv.ovf.i2", 0xb5)
-OP(CONV_OVF_U2, "conv.ovf.u2", 0xb6)
-OP(CONV_OVF_I4, "conv.ovf.i4", 0xb7)
-OP(CONV_OVF_U4, "conv.ovf.u4", 0xb8)
-OP(CONV_OVF_I8, "conv.ovf.i8", 0xb9)
-OP(CONV_OVF_U8, "conv.ovf.u8", 0xba)
-OP(REFANYVAL, "refanyval", 0xc2)
-OP(CKFINITE, "ckfinite", 0xc3)
-OP(MKREFANY, "mkrefany", 0xc6)
-OP(ANN_CALL, "ann.call", 0xc7)
-OP(ANN_CATCH, "ann.catch", 0xc8)
-OP(ANN_DEAD, "ann.dead", 0xc9)
-OP(ANN_HOISTED, "ann.hoisted", 0xca)
-OP(ANN_HOISTED_CALL, "ann.hoisted.call", 0xcb)
-OP(ANN_LAB, "ann.lab", 0xcc)
-OP(ANN_DEF, "ann.def", 0xcd)
-OP(ANN_REF_S, "ann.ref.s", 0xce)
-OP(ANN_PHI, "ann.phi", 0xcf)
-OP(LDTOKEN, "ldtoken", 0xd0)
-OP(CONV_U2, "conv.u2", 0xd1)
-OP(CONV_U1, "conv.u1", 0xd2)
-OP(CONV_I, "conv.i", 0xd3)
-OP(CONV_OVF_I, "conv.ovf.i", 0xd4)
-OP(CONV_OVF_U, "conv.ovf.u", 0xd5)
-OP(ADD_OVF, "add.ovf", 0xd6)
-OP(ADD_OVF_UN, "add.ovf.un", 0xd7)
-OP(MUL_OVF, "mul.ovf", 0xd8)
-OP(MUL_OVF_UN, "mul.ovf.un", 0xd9)
-OP(SUB_OVF, "sub.ovf", 0xda)
-OP(SUB_OVF_UN, "sub.ovf.un", 0xdb)
-OP(ENDFINALLY, "endfinally", 0xdc)
-OP(LEAVE, "leave", 0xdd)
-OP(LEAVE_S, "leave.s", 0xde)
-OP(STIND_I, "stind.i", 0xdf)
-OP(CONV_U, "conv.u", 0xe0)
-
-/* prefix instructions. we use an opcode >= 256 to ease coding */
-
-OP(ARGLIST, "arglist", 0x100)
-OP(CEQ, "ceq", 0x101)
-OP(CGT, "cgt", 0x102)
-OP(CGT_UN, "cgt.un", 0x103)
-OP(CLT, "clt", 0x104)
-OP(CLT_UN, "clt.un", 0x105)
-OP(LDFTN, "ldftn", 0x106)
-OP(LDVIRTFTN, "ldvirtftn", 0x107)
-OP(JMPI, "jmpi", 0x108)
-OP(LDARG, "ldarg", 0x109)
-OP(LDARGA, "ldarga", 0x10a)
-OP(STARG, "starg", 0x10b)
-OP(LDLOC, "ldloc", 0x10c)
-OP(LDLOCA, "ldloca", 0x10d)
-OP(STLOC, "stloc", 0x10e)
-OP(LOCALLOC, "localloc", 0x10f)
-OP(ENDFILTER, "endfilter", 0x111)
-OP(UNALIGNED, "unaligned", 0x112)
-OP(VOLATILE, "volatile", 0x113)
-OP(TAIL, "tail", 0x114)
-OP(INITOBJ, "initobj", 0x115)
-OP(ANN_LIVE, "ann.live", 0x116)
-OP(CPBLK, "cpblk", 0x117)
-OP(INITBLK, "initblk", 0x118)
-OP(ANN_REF, "ann.ref", 0x119)
-OP(RETHROW, "rethrow", 0x11a)
-OP(SIZEOF, "sizeof", 0x11c)
-OP(REFANYTYPE, "refanytype", 0x11d)
-OP(ANN_DATA, "ann.data", 0x122)
-OP(ANN_ARG, "ann.arg", 0x123)
diff --git a/tinyc/include/float.h b/tinyc/include/float.h
deleted file mode 100644
index f16f1f0cb..000000000
--- a/tinyc/include/float.h
+++ /dev/null
@@ -1,57 +0,0 @@
-#ifndef _FLOAT_H_
-#define _FLOAT_H_
-
-#define FLT_RADIX 2
-
-/* IEEE float */
-#define FLT_MANT_DIG 24
-#define FLT_DIG 6
-#define FLT_ROUNDS 1
-#define FLT_EPSILON 1.19209290e-07F
-#define FLT_MIN_EXP (-125)
-#define FLT_MIN 1.17549435e-38F
-#define FLT_MIN_10_EXP (-37)
-#define FLT_MAX_EXP 128
-#define FLT_MAX 3.40282347e+38F
-#define FLT_MAX_10_EXP 38
-
-/* IEEE double */
-#define DBL_MANT_DIG 53
-#define DBL_DIG 15
-#define DBL_EPSILON 2.2204460492503131e-16
-#define DBL_MIN_EXP (-1021)
-#define DBL_MIN 2.2250738585072014e-308
-#define DBL_MIN_10_EXP (-307)
-#define DBL_MAX_EXP 1024
-#define DBL_MAX 1.7976931348623157e+308
-#define DBL_MAX_10_EXP 308
-
-/* horrible intel long double */
-#if defined __i386__ || defined __x86_64__
-
-#define LDBL_MANT_DIG 64
-#define LDBL_DIG 18
-#define LDBL_EPSILON 1.08420217248550443401e-19L
-#define LDBL_MIN_EXP (-16381)
-#define LDBL_MIN 3.36210314311209350626e-4932L
-#define LDBL_MIN_10_EXP (-4931)
-#define LDBL_MAX_EXP 16384
-#define LDBL_MAX 1.18973149535723176502e+4932L
-#define LDBL_MAX_10_EXP 4932
-
-#else
-
-/* same as IEEE double */
-#define LDBL_MANT_DIG 53
-#define LDBL_DIG 15
-#define LDBL_EPSILON 2.2204460492503131e-16
-#define LDBL_MIN_EXP (-1021)
-#define LDBL_MIN 2.2250738585072014e-308
-#define LDBL_MIN_10_EXP (-307)
-#define LDBL_MAX_EXP 1024
-#define LDBL_MAX 1.7976931348623157e+308
-#define LDBL_MAX_10_EXP 308
-
-#endif
-
-#endif /* _FLOAT_H_ */
diff --git a/tinyc/include/stdarg.h b/tinyc/include/stdarg.h
deleted file mode 100644
index 10ce733b4..000000000
--- a/tinyc/include/stdarg.h
+++ /dev/null
@@ -1,79 +0,0 @@
-#ifndef _STDARG_H
-#define _STDARG_H
-
-#ifdef __x86_64__
-#ifndef _WIN64
-
-//This should be in sync with the declaration on our lib/libtcc1.c
-/* GCC compatible definition of va_list. */
-typedef struct {
-    unsigned int gp_offset;
-    unsigned int fp_offset;
-    union {
-        unsigned int overflow_offset;
-        char *overflow_arg_area;
-    };
-    char *reg_save_area;
-} __va_list_struct;
-
-typedef __va_list_struct va_list[1];
-
-void __va_start(__va_list_struct *ap, void *fp);
-void *__va_arg(__va_list_struct *ap, int arg_type, int size, int align);
-
-#define va_start(ap, last) __va_start(ap, __builtin_frame_address(0))
-#define va_arg(ap, type)                                                \
-    (*(type *)(__va_arg(ap, __builtin_va_arg_types(type), sizeof(type), __alignof__(type))))
-#define va_copy(dest, src) (*(dest) = *(src))
-#define va_end(ap)
-
-/* avoid conflicting definition for va_list on Macs. */
-#define _VA_LIST_T
-
-#else /* _WIN64 */
-typedef char *va_list;
-#define va_start(ap,last) __builtin_va_start(ap,last)
-#define va_arg(ap, t) ((sizeof(t) > 8 || (sizeof(t) & (sizeof(t) - 1))) \
-	? **(t **)((ap += 8) - 8) : *(t  *)((ap += 8) - 8))
-#define va_copy(dest, src) ((dest) = (src))
-#define va_end(ap)
-#endif
-
-#elif __arm__
-typedef char *va_list;
-#define _tcc_alignof(type) ((int)&((struct {char c;type x;} *)0)->x)
-#define _tcc_align(addr,type) (((unsigned)addr + _tcc_alignof(type) - 1) \
-                               & ~(_tcc_alignof(type) - 1))
-#define va_start(ap,last) ap = ((char *)&(last)) + ((sizeof(last)+3)&~3)
-#define va_arg(ap,type) (ap = (void *) ((_tcc_align(ap,type)+sizeof(type)+3) \
-                        &~3), *(type *)(ap - ((sizeof(type)+3)&~3)))
-#define va_copy(dest, src) (dest) = (src)
-#define va_end(ap)
-
-#elif defined(__aarch64__)
-typedef struct {
-    void *__stack;
-    void *__gr_top;
-    void *__vr_top;
-    int   __gr_offs;
-    int   __vr_offs;
-} va_list;
-#define va_start(ap, last) __va_start(ap, last)
-#define va_arg(ap, type) __va_arg(ap, type)
-#define va_end(ap)
-#define va_copy(dest, src) ((dest) = (src))
-
-#else /* __i386__ */
-typedef char *va_list;
-/* only correct for i386 */
-#define va_start(ap,last) ap = ((char *)&(last)) + ((sizeof(last)+3)&~3)
-#define va_arg(ap,type) (ap += (sizeof(type)+3)&~3, *(type *)(ap - ((sizeof(type)+3)&~3)))
-#define va_copy(dest, src) (dest) = (src)
-#define va_end(ap)
-#endif
-
-/* fix a buggy dependency on GCC in libio.h */
-typedef va_list __gnuc_va_list;
-#define _VA_LIST_DEFINED
-
-#endif /* _STDARG_H */
diff --git a/tinyc/include/stdbool.h b/tinyc/include/stdbool.h
deleted file mode 100644
index d2ee446e7..000000000
--- a/tinyc/include/stdbool.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef _STDBOOL_H
-#define _STDBOOL_H
-
-/* ISOC99 boolean */
-
-#define bool	_Bool
-#define true	1
-#define false	0
-#define __bool_true_false_are_defined 1
-
-#endif /* _STDBOOL_H */
diff --git a/tinyc/include/stddef.h b/tinyc/include/stddef.h
deleted file mode 100644
index 694d50375..000000000
--- a/tinyc/include/stddef.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef _STDDEF_H
-#define _STDDEF_H
-
-typedef __SIZE_TYPE__ size_t;
-typedef __PTRDIFF_TYPE__ ssize_t;
-typedef __WCHAR_TYPE__ wchar_t;
-typedef __PTRDIFF_TYPE__ ptrdiff_t;
-typedef __PTRDIFF_TYPE__ intptr_t;
-typedef __SIZE_TYPE__ uintptr_t;
-
-#ifndef __int8_t_defined
-#define __int8_t_defined
-typedef signed char int8_t;
-typedef signed short int int16_t;
-typedef signed int int32_t;
-#ifdef __LP64__
-typedef signed long int int64_t;
-#else
-typedef signed long long int int64_t;
-#endif
-typedef unsigned char uint8_t;
-typedef unsigned short int uint16_t;
-typedef unsigned int uint32_t;
-#ifdef __LP64__
-typedef unsigned long int uint64_t;
-#else
-typedef unsigned long long int uint64_t;
-#endif
-#endif
-
-#ifndef NULL
-#define NULL ((void*)0)
-#endif
-
-#define offsetof(type, field) ((size_t)&((type *)0)->field)
-
-void *alloca(size_t size);
-
-#endif
-
-/* Older glibc require a wint_t from <stddef.h> (when requested
-   by __need_wint_t, as otherwise stddef.h isn't allowed to
-   define this type).   Note that this must be outside the normal
-   _STDDEF_H guard, so that it works even when we've included the file
-   already (without requiring wint_t).  Some other libs define _WINT_T
-   if they've already provided that type, so we can use that as guard.
-   TCC defines __WINT_TYPE__ for us.  */
-#if defined (__need_wint_t)
-#ifndef _WINT_T
-#define _WINT_T
-typedef __WINT_TYPE__ wint_t;
-#endif
-#undef __need_wint_t
-#endif
diff --git a/tinyc/include/varargs.h b/tinyc/include/varargs.h
deleted file mode 100644
index d614366ed..000000000
--- a/tinyc/include/varargs.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _VARARGS_H
-#define _VARARGS_H
-
-#error "TinyCC no longer implements <varargs.h>."
-#error "Revise your code to use <stdarg.h>."
-
-#endif
diff --git a/tinyc/lib/alloca-arm.S b/tinyc/lib/alloca-arm.S
deleted file mode 100644
index 68556e36d..000000000
--- a/tinyc/lib/alloca-arm.S
+++ /dev/null
@@ -1,17 +0,0 @@
-	.text
-	.align	2
-	.global	alloca
-	.type	alloca, %function
-alloca:
-#ifdef __TINYC__
-        .int 0xe060d00d
-        .int 0xe3cdd007
-        .int 0xe1a0000d
-        .int 0xe1a0f00e
-#else
-	rsb	sp, r0, sp
-	bic	sp, sp, #7
-	mov	r0, sp
-	mov	pc, lr
-#endif
-	.size	alloca, .-alloca
diff --git a/tinyc/lib/alloca86-bt.S b/tinyc/lib/alloca86-bt.S
deleted file mode 100644
index 4f95cf134..000000000
--- a/tinyc/lib/alloca86-bt.S
+++ /dev/null
@@ -1,47 +0,0 @@
-/* ---------------------------------------------- */
-/* alloca86-bt.S */
-
-.globl __bound_alloca
-
-__bound_alloca:
-    pop     %edx
-    pop     %eax
-    mov     %eax, %ecx
-    add     $3,%eax
-    and     $-4,%eax
-    jz      p6
-
-#ifdef _WIN32
-p4:
-    cmp     $4096,%eax
-    jbe     p5
-    test    %eax,-4096(%esp)
-    sub     $4096,%esp
-    sub     $4096,%eax
-    jmp p4
-
-p5:
-#endif
-
-    sub     %eax,%esp
-    mov     %esp,%eax
-
-    push    %edx
-    push    %eax
-    push    %ecx
-    push    %eax
-    call   __bound_new_region
-    add    $8, %esp
-    pop     %eax
-    pop     %edx
-
-p6:
-    push    %edx
-    push    %edx
-    ret
-
-/* mark stack as nonexecutable */
-#if defined __ELF__ && defined __linux__
-    .section    .note.GNU-stack,"",@progbits
-#endif
-/* ---------------------------------------------- */
diff --git a/tinyc/lib/alloca86.S b/tinyc/lib/alloca86.S
deleted file mode 100644
index bb7a2c24a..000000000
--- a/tinyc/lib/alloca86.S
+++ /dev/null
@@ -1,31 +0,0 @@
-/* ---------------------------------------------- */
-/* alloca86.S */
-
-.globl alloca
-
-alloca:
-    pop     %edx
-    pop     %eax
-    add     $3,%eax
-    and     $-4,%eax
-    jz      p3
-
-#ifdef _WIN32
-p1:
-    cmp     $4096,%eax
-    jbe     p2
-    test    %eax,-4096(%esp)
-    sub     $4096,%esp
-    sub     $4096,%eax
-    jmp p1
-p2:
-#endif
-
-    sub     %eax,%esp
-    mov     %esp,%eax
-p3:
-    push    %edx
-    push    %edx
-    ret
-
-/* ---------------------------------------------- */
diff --git a/tinyc/lib/alloca86_64-bt.S b/tinyc/lib/alloca86_64-bt.S
deleted file mode 100644
index 4cbad90f8..000000000
--- a/tinyc/lib/alloca86_64-bt.S
+++ /dev/null
@@ -1,56 +0,0 @@
-/* ---------------------------------------------- */
-/* alloca86_64.S */
-
-.globl __bound_alloca
-__bound_alloca:
-
-#ifdef _WIN32
-    # bound checking is not implemented
-    pop     %rdx
-    mov     %rcx,%rax
-    add     $15,%rax
-    and     $-16,%rax
-    jz      p3
-
-p1:
-    cmp     $4096,%rax
-    jbe     p2
-    test    %rax,-4096(%rsp)
-    sub     $4096,%rsp
-    sub     $4096,%rax
-    jmp p1
-p2:
-
-    sub     %rax,%rsp
-    mov     %rsp,%rax
-    add     $32,%rax
-
-p3:
-    push    %rdx
-    ret
-#else
-    pop     %rdx
-    mov     %rdi,%rax
-    mov     %rax,%rsi	# size, a second parm to the __bound_new_region
-
-    add     $15,%rax
-    and     $-16,%rax
-    jz      p3
-
-
-    sub     %rax,%rsp
-    mov     %rsp,%rdi	# pointer, a first parm to the __bound_new_region
-    mov     %rsp,%rax
-
-    push    %rdx
-    push    %rax
-    call   __bound_new_region
-    pop     %rax
-    pop     %rdx
-
-p3:
-    push    %rdx
-    ret
-#endif
-
-/* ---------------------------------------------- */
diff --git a/tinyc/lib/alloca86_64.S b/tinyc/lib/alloca86_64.S
deleted file mode 100644
index ae3c97de3..000000000
--- a/tinyc/lib/alloca86_64.S
+++ /dev/null
@@ -1,34 +0,0 @@
-/* ---------------------------------------------- */
-/* alloca86_64.S */
-
-.globl alloca
-
-alloca:
-    pop     %rdx
-#ifdef _WIN32
-    mov     %rcx,%rax
-#else
-    mov     %rdi,%rax
-#endif
-    add     $15,%rax
-    and     $-16,%rax
-    jz      p3
-
-#ifdef _WIN32
-p1:
-    cmp     $4096,%rax
-    jbe     p2
-    test    %rax,-4096(%rsp)
-    sub     $4096,%rsp
-    sub     $4096,%rax
-    jmp p1
-p2:
-#endif
-
-    sub     %rax,%rsp
-    mov     %rsp,%rax
-p3:
-    push    %rdx
-    ret
-
-/* ---------------------------------------------- */
diff --git a/tinyc/lib/armeabi.c b/tinyc/lib/armeabi.c
deleted file mode 100644
index a59640dd0..000000000
--- a/tinyc/lib/armeabi.c
+++ /dev/null
@@ -1,501 +0,0 @@
-/* TCC ARM runtime EABI
-   Copyright (C) 2013 Thomas Preud'homme
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.*/
-
-#ifdef __TINYC__
-#define INT_MIN (-2147483647 - 1)
-#define INT_MAX 2147483647
-#define UINT_MAX 0xffffffff
-#define LONG_MIN (-2147483647L - 1)
-#define LONG_MAX 2147483647L
-#define ULONG_MAX 0xffffffffUL
-#define LLONG_MAX 9223372036854775807LL
-#define LLONG_MIN (-9223372036854775807LL - 1)
-#define ULLONG_MAX 0xffffffffffffffffULL
-#else
-#include <limits.h>
-#endif
-
-/* We rely on the little endianness and EABI calling convention for this to
-   work */
-
-typedef struct double_unsigned_struct {
-    unsigned low;
-    unsigned high;
-} double_unsigned_struct;
-
-typedef struct unsigned_int_struct {
-    unsigned low;
-    int high;
-} unsigned_int_struct;
-
-#define REGS_RETURN(name, type) \
-    void name ## _return(type ret) {}
-
-
-/* Float helper functions */
-
-#define FLOAT_EXP_BITS 8
-#define FLOAT_FRAC_BITS 23
-
-#define DOUBLE_EXP_BITS 11
-#define DOUBLE_FRAC_BITS 52
-
-#define ONE_EXP(type) ((1 << (type ## _EXP_BITS - 1)) - 1)
-
-REGS_RETURN(unsigned_int_struct, unsigned_int_struct)
-REGS_RETURN(double_unsigned_struct, double_unsigned_struct)
-
-/* float -> integer: (sign) 1.fraction x 2^(exponent - exp_for_one) */
-
-
-/* float to [unsigned] long long conversion */
-#define DEFINE__AEABI_F2XLZ(name, with_sign)                                 \
-void __aeabi_ ## name(unsigned val)                                          \
-{                                                                            \
-    int exp, high_shift, sign;                                               \
-    double_unsigned_struct ret;                                              \
-                                                                             \
-    /* compute sign */                                                       \
-    sign = val >> 31;                                                        \
-                                                                             \
-    /* compute real exponent */                                              \
-    exp = val >> FLOAT_FRAC_BITS;                                            \
-    exp &= (1 << FLOAT_EXP_BITS) - 1;                                        \
-    exp -= ONE_EXP(FLOAT);                                                   \
-                                                                             \
-    /* undefined behavior if truncated value cannot be represented */        \
-    if (with_sign) {                                                         \
-        if (exp > 62) /* |val| too big, double cannot represent LLONG_MAX */ \
-            return;                                                          \
-    } else {                                                                 \
-        if ((sign && exp >= 0) || exp > 63) /* if val < 0 || val too big */  \
-            return;                                                          \
-    }                                                                        \
-                                                                             \
-    val &= (1 << FLOAT_FRAC_BITS) - 1;                                       \
-    if (exp >= 32) {                                                         \
-        ret.high = 1 << (exp - 32);                                          \
-        if (exp - 32 >= FLOAT_FRAC_BITS) {                                   \
-            ret.high |= val << (exp - 32 - FLOAT_FRAC_BITS);                 \
-            ret.low = 0;                                                     \
-        } else {                                                             \
-            high_shift = FLOAT_FRAC_BITS - (exp - 32);                       \
-            ret.high |= val >> high_shift;                                   \
-            ret.low = val << (32 - high_shift);                              \
-        }                                                                    \
-    } else {                                                                 \
-        ret.high = 0;                                                        \
-        ret.low = 1 << exp;                                                  \
-        if (exp > FLOAT_FRAC_BITS)                                           \
-            ret.low |= val << (exp - FLOAT_FRAC_BITS);                       \
-        else                                                                 \
-            ret.low |= val >> (FLOAT_FRAC_BITS - exp);                       \
-    }                                                                        \
-                                                                             \
-    /* encode negative integer using 2's complement */                       \
-    if (with_sign && sign) {                                                 \
-        ret.low = ~ret.low;                                                  \
-        ret.high = ~ret.high;                                                \
-        if (ret.low == UINT_MAX) {                                           \
-            ret.low = 0;                                                     \
-            ret.high++;                                                      \
-        } else                                                               \
-            ret.low++;                                                       \
-    }                                                                        \
-                                                                             \
-    double_unsigned_struct_return(ret);                                      \
-}
-
-/* float to unsigned long long conversion */
-DEFINE__AEABI_F2XLZ(f2ulz, 0)
-
-/* float to long long conversion */
-DEFINE__AEABI_F2XLZ(f2lz, 1)
-
-/* double to [unsigned] long long conversion */
-#define DEFINE__AEABI_D2XLZ(name, with_sign)                                 \
-void __aeabi_ ## name(double_unsigned_struct val)                            \
-{                                                                            \
-    int exp, high_shift, sign;                                               \
-    double_unsigned_struct ret;                                              \
-                                                                             \
-    /* compute sign */                                                       \
-    sign = val.high >> 31;                                                   \
-                                                                             \
-    /* compute real exponent */                                              \
-    exp = (val.high >> (DOUBLE_FRAC_BITS - 32));                             \
-    exp &= (1 << DOUBLE_EXP_BITS) - 1;                                       \
-    exp -= ONE_EXP(DOUBLE);                                                  \
-                                                                             \
-    /* undefined behavior if truncated value cannot be represented */        \
-    if (with_sign) {                                                         \
-        if (exp > 62) /* |val| too big, double cannot represent LLONG_MAX */ \
-            return;                                                          \
-    } else {                                                                 \
-        if ((sign && exp >= 0) || exp > 63) /* if val < 0 || val too big */  \
-            return;                                                          \
-    }                                                                        \
-                                                                             \
-    val.high &= (1 << (DOUBLE_FRAC_BITS - 32)) - 1;                          \
-    if (exp >= 32) {                                                         \
-        ret.high = 1 << (exp - 32);                                          \
-        if (exp >= DOUBLE_FRAC_BITS) {                                       \
-            high_shift = exp - DOUBLE_FRAC_BITS;                             \
-            ret.high |= val.high << high_shift;                              \
-            ret.high |= val.low >> (32 - high_shift);                        \
-            ret.low = val.low << high_shift;                                 \
-        } else {                                                             \
-            high_shift = DOUBLE_FRAC_BITS - exp;                             \
-            ret.high |= val.high >> high_shift;                              \
-            ret.low = val.high << (32 - high_shift);                         \
-            ret.low |= val.low >> high_shift;                                \
-        }                                                                    \
-    } else {                                                                 \
-        ret.high = 0;                                                        \
-        ret.low = 1 << exp;                                                  \
-        if (exp > DOUBLE_FRAC_BITS - 32) {                                   \
-            high_shift = exp - DOUBLE_FRAC_BITS - 32;                        \
-            ret.low |= val.high << high_shift;                               \
-            ret.low |= val.low >> (32 - high_shift);                         \
-        } else                                                               \
-            ret.low |= val.high >> (DOUBLE_FRAC_BITS - 32 - exp);            \
-    }                                                                        \
-                                                                             \
-    /* encode negative integer using 2's complement */                       \
-    if (with_sign && sign) {                                                 \
-        ret.low = ~ret.low;                                                  \
-        ret.high = ~ret.high;                                                \
-        if (ret.low == UINT_MAX) {                                           \
-            ret.low = 0;                                                     \
-            ret.high++;                                                      \
-        } else                                                               \
-            ret.low++;                                                       \
-    }                                                                        \
-                                                                             \
-    double_unsigned_struct_return(ret);                                      \
-}
-
-/* double to unsigned long long conversion */
-DEFINE__AEABI_D2XLZ(d2ulz, 0)
-
-/* double to long long conversion */
-DEFINE__AEABI_D2XLZ(d2lz, 1)
-
-/* long long to float conversion */
-#define DEFINE__AEABI_XL2F(name, with_sign)                             \
-unsigned __aeabi_ ## name(unsigned long long v)                         \
-{                                                                       \
-    int s /* shift */, flb /* first lost bit */, sign = 0;              \
-    unsigned p = 0 /* power */, ret;                                    \
-    double_unsigned_struct val;                                         \
-                                                                        \
-    /* fraction in negative float is encoded in 1's complement */       \
-    if (with_sign && (v & (1ULL << 63))) {                              \
-        sign = 1;                                                       \
-        v = ~v + 1;                                                     \
-    }                                                                   \
-    val.low = v;                                                        \
-    val.high = v >> 32;                                                 \
-    /* fill fraction bits */                                            \
-    for (s = 31, p = 1 << 31; p && !(val.high & p); s--, p >>= 1);      \
-    if (p) {                                                            \
-        ret = val.high & (p - 1);                                       \
-        if (s < FLOAT_FRAC_BITS) {                                      \
-            ret <<= FLOAT_FRAC_BITS - s;                                \
-            ret |= val.low >> (32 - (FLOAT_FRAC_BITS - s));             \
-            flb = (val.low >> (32 - (FLOAT_FRAC_BITS - s - 1))) & 1;    \
-        } else {                                                        \
-            flb = (ret >> (s - FLOAT_FRAC_BITS - 1)) & 1;               \
-            ret >>= s - FLOAT_FRAC_BITS;                                \
-        }                                                               \
-        s += 32;                                                        \
-    } else {                                                            \
-        for (s = 31, p = 1 << 31; p && !(val.low & p); s--, p >>= 1);   \
-        if (p) {                                                        \
-            ret = val.low & (p - 1);                                    \
-            if (s <= FLOAT_FRAC_BITS) {                                 \
-                ret <<= FLOAT_FRAC_BITS - s;                            \
-                flb = 0;                                                \
-	    } else {                                                    \
-                flb = (ret >> (s - FLOAT_FRAC_BITS - 1)) & 1;           \
-                ret >>= s - FLOAT_FRAC_BITS;                            \
-	    }                                                           \
-        } else                                                          \
-            return 0;                                                   \
-    }                                                                   \
-    if (flb)                                                            \
-        ret++;                                                          \
-                                                                        \
-    /* fill exponent bits */                                            \
-    ret |= (s + ONE_EXP(FLOAT)) << FLOAT_FRAC_BITS;                     \
-                                                                        \
-    /* fill sign bit */                                                 \
-    ret |= sign << 31;                                                  \
-                                                                        \
-    return ret;                                                         \
-}
-
-/* unsigned long long to float conversion */
-DEFINE__AEABI_XL2F(ul2f, 0)
-
-/* long long to float conversion */
-DEFINE__AEABI_XL2F(l2f, 1)
-
-/* long long to double conversion */
-#define __AEABI_XL2D(name, with_sign)                                   \
-void __aeabi_ ## name(unsigned long long v)                             \
-{                                                                       \
-    int s /* shift */, high_shift, sign = 0;                            \
-    unsigned tmp, p = 0;                                                \
-    double_unsigned_struct val, ret;                                    \
-                                                                        \
-    /* fraction in negative float is encoded in 1's complement */       \
-    if (with_sign && (v & (1ULL << 63))) {                              \
-        sign = 1;                                                       \
-        v = ~v + 1;                                                     \
-    }                                                                   \
-    val.low = v;                                                        \
-    val.high = v >> 32;                                                 \
-                                                                        \
-    /* fill fraction bits */                                            \
-    for (s = 31, p = 1 << 31; p && !(val.high & p); s--, p >>= 1);      \
-    if (p) {                                                            \
-        tmp = val.high & (p - 1);                                       \
-        if (s < DOUBLE_FRAC_BITS - 32) {                                \
-            high_shift = DOUBLE_FRAC_BITS - 32 - s;                     \
-            ret.high = tmp << high_shift;                               \
-            ret.high |= val.low >> (32 - high_shift);                   \
-            ret.low = val.low << high_shift;                            \
-        } else {                                                        \
-            high_shift = s - (DOUBLE_FRAC_BITS - 32);                   \
-            ret.high = tmp >> high_shift;                               \
-            ret.low = tmp << (32 - high_shift);                         \
-            ret.low |= val.low >> high_shift;                           \
-            if ((val.low >> (high_shift - 1)) & 1) {                    \
-                if (ret.low == UINT_MAX) {                              \
-                    ret.high++;                                         \
-                    ret.low = 0;                                        \
-		} else                                                  \
-                    ret.low++;                                          \
-            }                                                           \
-        }                                                               \
-        s += 32;                                                        \
-    } else {                                                            \
-        for (s = 31, p = 1 << 31; p && !(val.low & p); s--, p >>= 1);   \
-        if (p) {                                                        \
-            tmp = val.low & (p - 1);                                    \
-            if (s <= DOUBLE_FRAC_BITS - 32) {                           \
-                high_shift = DOUBLE_FRAC_BITS - 32 - s;                 \
-                ret.high = tmp << high_shift;                           \
-                ret.low = 0;                                            \
-	    } else {                                                    \
-                high_shift = s - (DOUBLE_FRAC_BITS - 32);               \
-                ret.high = tmp >> high_shift;                           \
-                ret.low = tmp << (32 - high_shift);                     \
-            }                                                           \
-        } else {                                                        \
-            ret.high = ret.low = 0;                                     \
-            double_unsigned_struct_return(ret);                         \
-        }                                                               \
-    }                                                                   \
-                                                                        \
-    /* fill exponent bits */                                            \
-    ret.high |= (s + ONE_EXP(DOUBLE)) << (DOUBLE_FRAC_BITS - 32);       \
-                                                                        \
-    /* fill sign bit */                                                 \
-    ret.high |= sign << 31;                                             \
-                                                                        \
-    double_unsigned_struct_return(ret);                                 \
-}
-
-/* unsigned long long to double conversion */
-__AEABI_XL2D(ul2d, 0)
-
-/* long long to double conversion */
-__AEABI_XL2D(l2d, 1)
-
-
-/* Long long helper functions */
-
-/* TODO: add error in case of den == 0 (see §4.3.1 and §4.3.2) */
-
-#define define_aeabi_xdivmod_signed_type(basetype, type) \
-typedef struct type {                                    \
-    basetype quot;                                       \
-    unsigned basetype rem;                               \
-} type
-
-#define define_aeabi_xdivmod_unsigned_type(basetype, type) \
-typedef struct type {                                      \
-    basetype quot;                                         \
-    basetype rem;                                          \
-} type
-
-#define AEABI_UXDIVMOD(name,type, rettype, typemacro)                     \
-static inline rettype aeabi_ ## name (type num, type den)                 \
-{                                                                         \
-    rettype ret;                                                          \
-    type quot = 0;                                                        \
-                                                                          \
-    /* Increase quotient while it is less than numerator */               \
-    while (num >= den) {                                                  \
-        type q = 1;                                                       \
-                                                                          \
-        /* Find closest power of two */                                   \
-        while ((q << 1) * den <= num && q * den <= typemacro ## _MAX / 2) \
-            q <<= 1;                                                      \
-                                                                          \
-        /* Compute difference between current quotient and numerator */   \
-        num -= q * den;                                                   \
-        quot += q;                                                        \
-    }                                                                     \
-    ret.quot = quot;                                                      \
-    ret.rem = num;                                                        \
-    return ret;                                                           \
-}
-
-#define __AEABI_XDIVMOD(name, type, uiname, rettype, urettype, typemacro)     \
-void __aeabi_ ## name(type numerator, type denominator)                       \
-{                                                                             \
-    unsigned type num, den;                                                   \
-    urettype uxdiv_ret;                                                       \
-    rettype ret;                                                              \
-                                                                              \
-    if (numerator >= 0)                                                       \
-      num = numerator;                                                        \
-    else                                                                      \
-      num = 0 - numerator;                                                    \
-    if (denominator >= 0)                                                     \
-      den = denominator;                                                      \
-    else                                                                      \
-      den = 0 - denominator;                                                  \
-    uxdiv_ret = aeabi_ ## uiname(num, den);                                   \
-    /* signs differ */                                                        \
-    if ((numerator & typemacro ## _MIN) != (denominator & typemacro ## _MIN)) \
-        ret.quot = 0 - uxdiv_ret.quot;                                        \
-    else                                                                      \
-        ret.quot = uxdiv_ret.quot;                                            \
-    if (numerator < 0)                                                        \
-        ret.rem = 0 - uxdiv_ret.rem;                                          \
-    else                                                                      \
-        ret.rem = uxdiv_ret.rem;                                              \
-                                                                              \
-    rettype ## _return(ret);                                                  \
-}
-
-define_aeabi_xdivmod_signed_type(long long, lldiv_t);
-define_aeabi_xdivmod_unsigned_type(unsigned long long, ulldiv_t);
-define_aeabi_xdivmod_signed_type(int, idiv_t);
-define_aeabi_xdivmod_unsigned_type(unsigned, uidiv_t);
-
-REGS_RETURN(lldiv_t, lldiv_t)
-REGS_RETURN(ulldiv_t, ulldiv_t)
-REGS_RETURN(idiv_t, idiv_t)
-REGS_RETURN(uidiv_t, uidiv_t)
-
-AEABI_UXDIVMOD(uldivmod, unsigned long long, ulldiv_t, ULLONG)
-
-__AEABI_XDIVMOD(ldivmod, long long, uldivmod, lldiv_t, ulldiv_t, LLONG)
-
-void __aeabi_uldivmod(unsigned long long num, unsigned long long den)
-{
-    ulldiv_t_return(aeabi_uldivmod(num, den));
-}
-
-void __aeabi_llsl(double_unsigned_struct val, int shift)
-{
-    double_unsigned_struct ret;
-
-    if (shift >= 32) {
-        val.high = val.low;
-        val.low = 0;
-        shift -= 32;
-    }
-    if (shift > 0) {
-        ret.low = val.low << shift;
-        ret.high = (val.high << shift) | (val.low >> (32 - shift));
-        double_unsigned_struct_return(ret);
-	return;
-    }
-    double_unsigned_struct_return(val);
-}
-
-#define aeabi_lsr(val, shift, fill, type)                          \
-    type ## _struct ret;                                           \
-                                                                   \
-    if (shift >= 32) {                                             \
-        val.low = val.high;                                        \
-        val.high = fill;                                           \
-        shift -= 32;                                               \
-    }                                                              \
-    if (shift > 0) {                                               \
-        ret.high = val.high >> shift;                              \
-        ret.low = (val.high << (32 - shift)) | (val.low >> shift); \
-        type ## _struct_return(ret);                               \
-	return;                                                    \
-    }                                                              \
-    type ## _struct_return(val);
-
-void __aeabi_llsr(double_unsigned_struct val, int shift)
-{
-    aeabi_lsr(val, shift, 0, double_unsigned);
-}
-
-void __aeabi_lasr(unsigned_int_struct val, int shift)
-{
-    aeabi_lsr(val, shift, val.high >> 31, unsigned_int);
-}
-
-
-/* Integer division functions */
-
-AEABI_UXDIVMOD(uidivmod, unsigned, uidiv_t, UINT)
-
-int __aeabi_idiv(int numerator, int denominator)
-{
-    unsigned num, den;
-    uidiv_t ret;
-
-    if (numerator >= 0)
-        num = numerator;
-    else
-        num = 0 - numerator;
-    if (denominator >= 0)
-        den = denominator;
-    else
-        den = 0 - denominator;
-    ret = aeabi_uidivmod(num, den);
-    if ((numerator & INT_MIN) != (denominator & INT_MIN)) /* signs differ */
-        ret.quot *= -1;
-    return ret.quot;
-}
-
-unsigned __aeabi_uidiv(unsigned num, unsigned den)
-{
-    return aeabi_uidivmod(num, den).quot;
-}
-
-__AEABI_XDIVMOD(idivmod, int, uidivmod, idiv_t, uidiv_t, INT)
-
-void __aeabi_uidivmod(unsigned num, unsigned den)
-{
-    uidiv_t_return(aeabi_uidivmod(num, den));
-}
diff --git a/tinyc/lib/armflush.c b/tinyc/lib/armflush.c
deleted file mode 100644
index eae32605f..000000000
--- a/tinyc/lib/armflush.c
+++ /dev/null
@@ -1,58 +0,0 @@
-/* armflush.c - flush the instruction cache
-
-   __clear_cache is used in tccrun.c,  It is a built-in
-   intrinsic with gcc.  However tcc in order to compile
-   itself needs this function */
-
-#ifdef __TINYC__
-
-/* syscall wrapper */
-unsigned syscall(unsigned syscall_nr, ...);
-
-/* arm-tcc supports only fake asm currently */
-__asm__(
-    ".global syscall\n"
-    "syscall:\n"
-    ".int 0xe92d4080\n"  // push    {r7, lr}
-    ".int 0xe1a07000\n"  // mov     r7, r0
-    ".int 0xe1a00001\n"  // mov     r0, r1
-    ".int 0xe1a01002\n"  // mov     r1, r2
-    ".int 0xe1a02003\n"  // mov     r2, r3
-    ".int 0xef000000\n"  // svc     0x00000000
-    ".int 0xe8bd8080\n"  // pop     {r7, pc}
-    );
-
-/* from unistd.h: */
-#if defined(__thumb__) || defined(__ARM_EABI__)
-# define __NR_SYSCALL_BASE      0x0
-#else
-# define __NR_SYSCALL_BASE      0x900000
-#endif
-#define __ARM_NR_BASE           (__NR_SYSCALL_BASE+0x0f0000)
-#define __ARM_NR_cacheflush     (__ARM_NR_BASE+2)
-
-#else
-
-#define _GNU_SOURCE
-#include <unistd.h>
-#include <sys/syscall.h>
-#include <stdio.h>
-
-#endif
-
-/* Flushing for tccrun */
-void __clear_cache(void *beginning, void *end)
-{
-/* __ARM_NR_cacheflush is kernel private and should not be used in user space.
- * However, there is no ARM asm parser in tcc so we use it for now */
-#if 1
-    syscall(__ARM_NR_cacheflush, beginning, end, 0);
-#else
-    __asm__ ("push {r7}\n\t"
-             "mov r7, #0xf0002\n\t"
-             "mov r2, #0\n\t"
-             "swi 0\n\t"
-             "pop {r7}\n\t"
-             "ret");
-#endif
-}
diff --git a/tinyc/lib/bcheck.c b/tinyc/lib/bcheck.c
deleted file mode 100644
index 90f0ad2c3..000000000
--- a/tinyc/lib/bcheck.c
+++ /dev/null
@@ -1,979 +0,0 @@
-/*
- *  Tiny C Memory and bounds checker
- * 
- *  Copyright (c) 2002 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-
-#if !defined(__FreeBSD__) \
- && !defined(__FreeBSD_kernel__) \
- && !defined(__DragonFly__) \
- && !defined(__OpenBSD__) \
- && !defined(__NetBSD__)
-#include <malloc.h>
-#endif
-
-#if !defined(_WIN32)
-#include <unistd.h>
-#endif
-
-/* #define BOUND_DEBUG */
-
-#ifdef BOUND_DEBUG
- #define dprintf(a...) fprintf(a)
-#else
- #define dprintf(a...)
-#endif
-
-/* define so that bound array is static (faster, but use memory if
-   bound checking not used) */
-/* #define BOUND_STATIC */
-
-/* use malloc hooks. Currently the code cannot be reliable if no hooks */
-#define CONFIG_TCC_MALLOC_HOOKS
-#define HAVE_MEMALIGN
-
-#if defined(__FreeBSD__) \
- || defined(__FreeBSD_kernel__) \
- || defined(__DragonFly__) \
- || defined(__OpenBSD__) \
- || defined(__NetBSD__) \
- || defined(__dietlibc__) \
- || defined(_WIN32)
-//#warning Bound checking does not support malloc (etc.) in this environment.
-#undef CONFIG_TCC_MALLOC_HOOKS
-#undef HAVE_MEMALIGN
-#endif
-
-#define BOUND_T1_BITS 13
-#define BOUND_T2_BITS 11
-#define BOUND_T3_BITS (sizeof(size_t)*8 - BOUND_T1_BITS - BOUND_T2_BITS)
-#define BOUND_E_BITS  (sizeof(size_t))
-
-#define BOUND_T1_SIZE ((size_t)1 << BOUND_T1_BITS)
-#define BOUND_T2_SIZE ((size_t)1 << BOUND_T2_BITS)
-#define BOUND_T3_SIZE ((size_t)1 << BOUND_T3_BITS)
-
-#define BOUND_T23_BITS (BOUND_T2_BITS + BOUND_T3_BITS)
-#define BOUND_T23_SIZE ((size_t)1 << BOUND_T23_BITS)
-
-
-/* this pointer is generated when bound check is incorrect */
-#define INVALID_POINTER ((void *)(-2))
-/* size of an empty region */
-#define EMPTY_SIZE  ((size_t)(-1))
-/* size of an invalid region */
-#define INVALID_SIZE      0
-
-typedef struct BoundEntry {
-    size_t start;
-    size_t size;
-    struct BoundEntry *next;
-    size_t is_invalid; /* true if pointers outside region are invalid */
-} BoundEntry;
-
-/* external interface */
-void __bound_init(void);
-void __bound_new_region(void *p, size_t size);
-int __bound_delete_region(void *p);
-
-#ifdef __attribute__
-  /* an __attribute__ macro is defined in the system headers */
-  #undef __attribute__ 
-#endif
-#define FASTCALL __attribute__((regparm(3)))
-
-void *__bound_malloc(size_t size, const void *caller);
-void *__bound_memalign(size_t size, size_t align, const void *caller);
-void __bound_free(void *ptr, const void *caller);
-void *__bound_realloc(void *ptr, size_t size, const void *caller);
-static void *libc_malloc(size_t size);
-static void libc_free(void *ptr);
-static void install_malloc_hooks(void);
-static void restore_malloc_hooks(void);
-
-#ifdef CONFIG_TCC_MALLOC_HOOKS
-static void *saved_malloc_hook;
-static void *saved_free_hook;
-static void *saved_realloc_hook;
-static void *saved_memalign_hook;
-#endif
-
-/* TCC definitions */
-extern char __bounds_start; /* start of static bounds table */
-/* error message, just for TCC */
-const char *__bound_error_msg;
-
-/* runtime error output */
-extern void rt_error(size_t pc, const char *fmt, ...);
-
-#ifdef BOUND_STATIC
-static BoundEntry *__bound_t1[BOUND_T1_SIZE]; /* page table */
-#else
-static BoundEntry **__bound_t1; /* page table */
-#endif
-static BoundEntry *__bound_empty_t2;   /* empty page, for unused pages */
-static BoundEntry *__bound_invalid_t2; /* invalid page, for invalid pointers */
-
-static BoundEntry *__bound_find_region(BoundEntry *e1, void *p)
-{
-    size_t addr, tmp;
-    BoundEntry *e;
-
-    e = e1;
-    while (e != NULL) {
-        addr = (size_t)p;
-        addr -= e->start;
-        if (addr <= e->size) {
-            /* put region at the head */
-            tmp = e1->start;
-            e1->start = e->start;
-            e->start = tmp;
-            tmp = e1->size;
-            e1->size = e->size;
-            e->size = tmp;
-            return e1;
-        }
-        e = e->next;
-    }
-    /* no entry found: return empty entry or invalid entry */
-    if (e1->is_invalid)
-        return __bound_invalid_t2;
-    else
-        return __bound_empty_t2;
-}
-
-/* print a bound error message */
-static void bound_error(const char *fmt, ...)
-{
-    __bound_error_msg = fmt;
-    fprintf(stderr,"%s %s: %s\n", __FILE__, __FUNCTION__, fmt);
-    *(void **)0 = 0; /* force a runtime error */
-}
-
-static void bound_alloc_error(void)
-{
-    bound_error("not enough memory for bound checking code");
-}
-
-/* return '(p + offset)' for pointer arithmetic (a pointer can reach
-   the end of a region in this case */
-void * FASTCALL __bound_ptr_add(void *p, size_t offset)
-{
-    size_t addr = (size_t)p;
-    BoundEntry *e;
-
-    dprintf(stderr, "%s %s: %p %x\n",
-        __FILE__, __FUNCTION__, p, (unsigned)offset);
-
-    __bound_init();
-
-    e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)];
-    e = (BoundEntry *)((char *)e + 
-                       ((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) & 
-                        ((BOUND_T2_SIZE - 1) << BOUND_E_BITS)));
-    addr -= e->start;
-    if (addr > e->size) {
-        e = __bound_find_region(e, p);
-        addr = (size_t)p - e->start;
-    }
-    addr += offset;
-    if (addr >= e->size) {
-	fprintf(stderr,"%s %s: %p is outside of the region\n",
-            __FILE__, __FUNCTION__, p + offset);
-        return INVALID_POINTER; /* return an invalid pointer */
-    }
-    return p + offset;
-}
-
-/* return '(p + offset)' for pointer indirection (the resulting must
-   be strictly inside the region */
-#define BOUND_PTR_INDIR(dsize)                                          \
-void * FASTCALL __bound_ptr_indir ## dsize (void *p, size_t offset)     \
-{                                                                       \
-    size_t addr = (size_t)p;                                            \
-    BoundEntry *e;                                                      \
-                                                                        \
-    dprintf(stderr, "%s %s: %p %x start\n",                             \
-        __FILE__, __FUNCTION__, p, (unsigned)offset);	                \
-									\
-    __bound_init();							\
-    e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)];            \
-    e = (BoundEntry *)((char *)e +                                      \
-                       ((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) &      \
-                        ((BOUND_T2_SIZE - 1) << BOUND_E_BITS)));        \
-    addr -= e->start;                                                   \
-    if (addr > e->size) {                                               \
-        e = __bound_find_region(e, p);                                  \
-        addr = (size_t)p - e->start;                                    \
-    }                                                                   \
-    addr += offset + dsize;                                             \
-    if (addr > e->size) {                                               \
-	fprintf(stderr,"%s %s: %p is outside of the region\n",          \
-            __FILE__, __FUNCTION__, p + offset);                        \
-        return INVALID_POINTER; /* return an invalid pointer */         \
-    }									\
-    dprintf(stderr, "%s %s: return p+offset = %p\n",                    \
-        __FILE__, __FUNCTION__, p + offset);                            \
-    return p + offset;                                                  \
-}
-
-BOUND_PTR_INDIR(1)
-BOUND_PTR_INDIR(2)
-BOUND_PTR_INDIR(4)
-BOUND_PTR_INDIR(8)
-BOUND_PTR_INDIR(12)
-BOUND_PTR_INDIR(16)
-
-#if defined(__GNUC__) && (__GNUC__ >= 6)
-/*
- * At least gcc 6.2 complains when __builtin_frame_address is used with
- * nonzero argument.
- */
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wframe-address"
-#endif
-
-/* return the frame pointer of the caller */
-#define GET_CALLER_FP(fp)\
-{\
-    fp = (size_t)__builtin_frame_address(1);\
-}
-
-/* called when entering a function to add all the local regions */
-void FASTCALL __bound_local_new(void *p1) 
-{
-    size_t addr, size, fp, *p = p1;
-
-    dprintf(stderr, "%s, %s start p1=%p\n", __FILE__, __FUNCTION__, p);
-    GET_CALLER_FP(fp);
-    for(;;) {
-        addr = p[0];
-        if (addr == 0)
-            break;
-        addr += fp;
-        size = p[1];
-        p += 2;
-        __bound_new_region((void *)addr, size);
-    }
-    dprintf(stderr, "%s, %s end\n", __FILE__, __FUNCTION__);
-}
-
-/* called when leaving a function to delete all the local regions */
-void FASTCALL __bound_local_delete(void *p1) 
-{
-    size_t addr, fp, *p = p1;
-    GET_CALLER_FP(fp);
-    for(;;) {
-        addr = p[0];
-        if (addr == 0)
-            break;
-        addr += fp;
-        p += 2;
-        __bound_delete_region((void *)addr);
-    }
-}
-
-#if defined(__GNUC__) && (__GNUC__ >= 6)
-#pragma GCC diagnostic pop
-#endif
-
-static BoundEntry *__bound_new_page(void)
-{
-    BoundEntry *page;
-    size_t i;
-
-    page = libc_malloc(sizeof(BoundEntry) * BOUND_T2_SIZE);
-    if (!page)
-        bound_alloc_error();
-    for(i=0;i<BOUND_T2_SIZE;i++) {
-        /* put empty entries */
-        page[i].start = 0;
-        page[i].size = EMPTY_SIZE;
-        page[i].next = NULL;
-        page[i].is_invalid = 0;
-    }
-    return page;
-}
-
-/* currently we use malloc(). Should use bound_new_page() */
-static BoundEntry *bound_new_entry(void)
-{
-    BoundEntry *e;
-    e = libc_malloc(sizeof(BoundEntry));
-    return e;
-}
-
-static void bound_free_entry(BoundEntry *e)
-{
-    libc_free(e);
-}
-
-static BoundEntry *get_page(size_t index)
-{
-    BoundEntry *page;
-    page = __bound_t1[index];
-    if (!page || page == __bound_empty_t2 || page == __bound_invalid_t2) {
-        /* create a new page if necessary */
-        page = __bound_new_page();
-        __bound_t1[index] = page;
-    }
-    return page;
-}
-
-/* mark a region as being invalid (can only be used during init) */
-static void mark_invalid(size_t addr, size_t size)
-{
-    size_t start, end;
-    BoundEntry *page;
-    size_t t1_start, t1_end, i, j, t2_start, t2_end;
-
-    start = addr;
-    end = addr + size;
-
-    t2_start = (start + BOUND_T3_SIZE - 1) >> BOUND_T3_BITS;
-    if (end != 0)
-        t2_end = end >> BOUND_T3_BITS;
-    else
-        t2_end = 1 << (BOUND_T1_BITS + BOUND_T2_BITS);
-
-#if 0
-    dprintf(stderr, "mark_invalid: start = %x %x\n", t2_start, t2_end);
-#endif
-    
-    /* first we handle full pages */
-    t1_start = (t2_start + BOUND_T2_SIZE - 1) >> BOUND_T2_BITS;
-    t1_end = t2_end >> BOUND_T2_BITS;
-
-    i = t2_start & (BOUND_T2_SIZE - 1);
-    j = t2_end & (BOUND_T2_SIZE - 1);
-    
-    if (t1_start == t1_end) {
-        page = get_page(t2_start >> BOUND_T2_BITS);
-        for(; i < j; i++) {
-            page[i].size = INVALID_SIZE;
-            page[i].is_invalid = 1;
-        }
-    } else {
-        if (i > 0) {
-            page = get_page(t2_start >> BOUND_T2_BITS);
-            for(; i < BOUND_T2_SIZE; i++) {
-                page[i].size = INVALID_SIZE;
-                page[i].is_invalid = 1;
-            }
-        }
-        for(i = t1_start; i < t1_end; i++) {
-            __bound_t1[i] = __bound_invalid_t2;
-        }
-        if (j != 0) {
-            page = get_page(t1_end);
-            for(i = 0; i < j; i++) {
-                page[i].size = INVALID_SIZE;
-                page[i].is_invalid = 1;
-            }
-        }
-    }
-}
-
-void __bound_init(void)
-{
-    size_t i;
-    BoundEntry *page;
-    size_t start, size;
-    size_t *p;
-
-    static int inited;
-    if (inited)
-	return;
-
-    inited = 1;
-
-    dprintf(stderr, "%s, %s() start\n", __FILE__, __FUNCTION__);
-
-    /* save malloc hooks and install bound check hooks */
-    install_malloc_hooks();
-
-#ifndef BOUND_STATIC
-    __bound_t1 = libc_malloc(BOUND_T1_SIZE * sizeof(BoundEntry *));
-    if (!__bound_t1)
-        bound_alloc_error();
-#endif
-    __bound_empty_t2 = __bound_new_page();
-    for(i=0;i<BOUND_T1_SIZE;i++) {
-        __bound_t1[i] = __bound_empty_t2;
-    }
-
-    page = __bound_new_page();
-    for(i=0;i<BOUND_T2_SIZE;i++) {
-        /* put invalid entries */
-        page[i].start = 0;
-        page[i].size = INVALID_SIZE;
-        page[i].next = NULL;
-        page[i].is_invalid = 1;
-    }
-    __bound_invalid_t2 = page;
-
-    /* invalid pointer zone */
-    start = (size_t)INVALID_POINTER & ~(BOUND_T23_SIZE - 1);
-    size = BOUND_T23_SIZE;
-    mark_invalid(start, size);
-
-#if defined(CONFIG_TCC_MALLOC_HOOKS)
-    /* malloc zone is also marked invalid. can only use that with
-     * hooks because all libs should use the same malloc. The solution
-     * would be to build a new malloc for tcc.
-     *
-     * usually heap (= malloc zone) comes right after bss, i.e. after _end, but
-     * not always - either if we are running from under `tcc -b -run`, or if
-     * address space randomization is turned on(a), heap start will be separated
-     * from bss end.
-     *
-     * So sbrk(0) will be a good approximation for start_brk:
-     *
-     *   - if we are a separately compiled program, __bound_init() runs early,
-     *     and sbrk(0) should be equal or very near to start_brk(b) (in case other
-     *     constructors malloc something), or
-     *
-     *   - if we are running from under `tcc -b -run`, sbrk(0) will return
-     *     start of heap portion which is under this program control, and not
-     *     mark as invalid earlier allocated memory.
-     *
-     *
-     * (a) /proc/sys/kernel/randomize_va_space = 2, on Linux;
-     *     usually turned on by default.
-     *
-     * (b) on Linux >= v3.3, the alternative is to read
-     *     start_brk from /proc/self/stat
-     */
-    start = (size_t)sbrk(0);
-    size = 128 * 0x100000;
-    mark_invalid(start, size);
-#endif
-
-    /* add all static bound check values */
-    p = (size_t *)&__bounds_start;
-    while (p[0] != 0) {
-        __bound_new_region((void *)p[0], p[1]);
-        p += 2;
-    }
-
-    dprintf(stderr, "%s, %s() end\n\n", __FILE__, __FUNCTION__);
-}
-
-void __bound_main_arg(void **p)
-{
-    void *start = p;
-    while (*p++);
-
-    dprintf(stderr, "%s, %s calling __bound_new_region(%p %x)\n",
-            __FILE__, __FUNCTION__, start, (unsigned)((void *)p - start));
-
-    __bound_new_region(start, (void *) p - start);
-}
-
-void __bound_exit(void)
-{
-    dprintf(stderr, "%s, %s()\n", __FILE__, __FUNCTION__);
-    restore_malloc_hooks();
-}
-
-static inline void add_region(BoundEntry *e, 
-                              size_t start, size_t size)
-{
-    BoundEntry *e1;
-    if (e->start == 0) {
-        /* no region : add it */
-        e->start = start;
-        e->size = size;
-    } else {
-        /* already regions in the list: add it at the head */
-        e1 = bound_new_entry();
-        e1->start = e->start;
-        e1->size = e->size;
-        e1->next = e->next;
-        e->start = start;
-        e->size = size;
-        e->next = e1;
-    }
-}
-
-/* create a new region. It should not already exist in the region list */
-void __bound_new_region(void *p, size_t size)
-{
-    size_t start, end;
-    BoundEntry *page, *e, *e2;
-    size_t t1_start, t1_end, i, t2_start, t2_end;
-
-    dprintf(stderr, "%s, %s(%p, %x) start\n",
-        __FILE__, __FUNCTION__, p, (unsigned)size);
-
-    __bound_init();
-
-    start = (size_t)p;
-    end = start + size;
-    t1_start = start >> (BOUND_T2_BITS + BOUND_T3_BITS);
-    t1_end = end >> (BOUND_T2_BITS + BOUND_T3_BITS);
-
-    /* start */
-    page = get_page(t1_start);
-    t2_start = (start >> (BOUND_T3_BITS - BOUND_E_BITS)) & 
-        ((BOUND_T2_SIZE - 1) << BOUND_E_BITS);
-    t2_end = (end >> (BOUND_T3_BITS - BOUND_E_BITS)) & 
-        ((BOUND_T2_SIZE - 1) << BOUND_E_BITS);
-
-
-    e = (BoundEntry *)((char *)page + t2_start);
-    add_region(e, start, size);
-
-    if (t1_end == t1_start) {
-        /* same ending page */
-        e2 = (BoundEntry *)((char *)page + t2_end);
-        if (e2 > e) {
-            e++;
-            for(;e<e2;e++) {
-                e->start = start;
-                e->size = size;
-            }
-            add_region(e, start, size);
-        }
-    } else {
-        /* mark until end of page */
-        e2 = page + BOUND_T2_SIZE;
-        e++;
-        for(;e<e2;e++) {
-            e->start = start;
-            e->size = size;
-        }
-        /* mark intermediate pages, if any */
-        for(i=t1_start+1;i<t1_end;i++) {
-            page = get_page(i);
-            e2 = page + BOUND_T2_SIZE;
-            for(e=page;e<e2;e++) {
-                e->start = start;
-                e->size = size;
-            }
-        }
-        /* last page */
-        page = get_page(t1_end);
-        e2 = (BoundEntry *)((char *)page + t2_end);
-        for(e=page;e<e2;e++) {
-            e->start = start;
-            e->size = size;
-        }
-        add_region(e, start, size);
-    }
-
-    dprintf(stderr, "%s, %s end\n", __FILE__, __FUNCTION__);
-}
-
-/* delete a region */
-static inline void delete_region(BoundEntry *e, void *p, size_t empty_size)
-{
-    size_t addr;
-    BoundEntry *e1;
-
-    addr = (size_t)p;
-    addr -= e->start;
-    if (addr <= e->size) {
-        /* region found is first one */
-        e1 = e->next;
-        if (e1 == NULL) {
-            /* no more region: mark it empty */
-            e->start = 0;
-            e->size = empty_size;
-        } else {
-            /* copy next region in head */
-            e->start = e1->start;
-            e->size = e1->size;
-            e->next = e1->next;
-            bound_free_entry(e1);
-        }
-    } else {
-        /* find the matching region */
-        for(;;) {
-            e1 = e;
-            e = e->next;
-            /* region not found: do nothing */
-            if (e == NULL)
-                break;
-            addr = (size_t)p - e->start;
-            if (addr <= e->size) {
-                /* found: remove entry */
-                e1->next = e->next;
-                bound_free_entry(e);
-                break;
-            }
-        }
-    }
-}
-
-/* WARNING: 'p' must be the starting point of the region. */
-/* return non zero if error */
-int __bound_delete_region(void *p)
-{
-    size_t start, end, addr, size, empty_size;
-    BoundEntry *page, *e, *e2;
-    size_t t1_start, t1_end, t2_start, t2_end, i;
-
-    dprintf(stderr, "%s %s() start\n", __FILE__, __FUNCTION__);
-
-    __bound_init();
-
-    start = (size_t)p;
-    t1_start = start >> (BOUND_T2_BITS + BOUND_T3_BITS);
-    t2_start = (start >> (BOUND_T3_BITS - BOUND_E_BITS)) & 
-        ((BOUND_T2_SIZE - 1) << BOUND_E_BITS);
-    
-    /* find region size */
-    page = __bound_t1[t1_start];
-    e = (BoundEntry *)((char *)page + t2_start);
-    addr = start - e->start;
-    if (addr > e->size)
-        e = __bound_find_region(e, p);
-    /* test if invalid region */
-    if (e->size == EMPTY_SIZE || (size_t)p != e->start) 
-        return -1;
-    /* compute the size we put in invalid regions */
-    if (e->is_invalid)
-        empty_size = INVALID_SIZE;
-    else
-        empty_size = EMPTY_SIZE;
-    size = e->size;
-    end = start + size;
-
-    /* now we can free each entry */
-    t1_end = end >> (BOUND_T2_BITS + BOUND_T3_BITS);
-    t2_end = (end >> (BOUND_T3_BITS - BOUND_E_BITS)) & 
-        ((BOUND_T2_SIZE - 1) << BOUND_E_BITS);
-
-    delete_region(e, p, empty_size);
-    if (t1_end == t1_start) {
-        /* same ending page */
-        e2 = (BoundEntry *)((char *)page + t2_end);
-        if (e2 > e) {
-            e++;
-            for(;e<e2;e++) {
-                e->start = 0;
-                e->size = empty_size;
-            }
-            delete_region(e, p, empty_size);
-        }
-    } else {
-        /* mark until end of page */
-        e2 = page + BOUND_T2_SIZE;
-        e++;
-        for(;e<e2;e++) {
-            e->start = 0;
-            e->size = empty_size;
-        }
-        /* mark intermediate pages, if any */
-        /* XXX: should free them */
-        for(i=t1_start+1;i<t1_end;i++) {
-            page = get_page(i);
-            e2 = page + BOUND_T2_SIZE;
-            for(e=page;e<e2;e++) {
-                e->start = 0;
-                e->size = empty_size;
-            }
-        }
-        /* last page */
-        page = get_page(t1_end);
-        e2 = (BoundEntry *)((char *)page + t2_end);
-        for(e=page;e<e2;e++) {
-            e->start = 0;
-            e->size = empty_size;
-        }
-        delete_region(e, p, empty_size);
-    }
-
-    dprintf(stderr, "%s %s() end\n", __FILE__, __FUNCTION__);
-
-    return 0;
-}
-
-/* return the size of the region starting at p, or EMPTY_SIZE if non
-   existent region. */
-static size_t get_region_size(void *p)
-{
-    size_t addr = (size_t)p;
-    BoundEntry *e;
-
-    e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)];
-    e = (BoundEntry *)((char *)e + 
-                       ((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) & 
-                        ((BOUND_T2_SIZE - 1) << BOUND_E_BITS)));
-    addr -= e->start;
-    if (addr > e->size)
-        e = __bound_find_region(e, p);
-    if (e->start != (size_t)p)
-        return EMPTY_SIZE;
-    return e->size;
-}
-
-/* patched memory functions */
-
-/* force compiler to perform stores coded up to this point */
-#define barrier()   __asm__ __volatile__ ("": : : "memory")
-
-static void install_malloc_hooks(void)
-{
-#ifdef CONFIG_TCC_MALLOC_HOOKS
-    saved_malloc_hook = __malloc_hook;
-    saved_free_hook = __free_hook;
-    saved_realloc_hook = __realloc_hook;
-    saved_memalign_hook = __memalign_hook;
-    __malloc_hook = __bound_malloc;
-    __free_hook = __bound_free;
-    __realloc_hook = __bound_realloc;
-    __memalign_hook = __bound_memalign;
-
-    barrier();
-#endif
-}
-
-static void restore_malloc_hooks(void)
-{
-#ifdef CONFIG_TCC_MALLOC_HOOKS
-    __malloc_hook = saved_malloc_hook;
-    __free_hook = saved_free_hook;
-    __realloc_hook = saved_realloc_hook;
-    __memalign_hook = saved_memalign_hook;
-
-    barrier();
-#endif
-}
-
-static void *libc_malloc(size_t size)
-{
-    void *ptr;
-    restore_malloc_hooks();
-    ptr = malloc(size);
-    install_malloc_hooks();
-    return ptr;
-}
-
-static void libc_free(void *ptr)
-{
-    restore_malloc_hooks();
-    free(ptr);
-    install_malloc_hooks();
-}
-
-/* XXX: we should use a malloc which ensure that it is unlikely that
-   two malloc'ed data have the same address if 'free' are made in
-   between. */
-void *__bound_malloc(size_t size, const void *caller)
-{
-    void *ptr;
-    
-    /* we allocate one more byte to ensure the regions will be
-       separated by at least one byte. With the glibc malloc, it may
-       be in fact not necessary */
-    ptr = libc_malloc(size + 1);
-    
-    if (!ptr)
-        return NULL;
-
-    dprintf(stderr, "%s, %s calling __bound_new_region(%p, %x)\n",
-           __FILE__, __FUNCTION__, ptr, (unsigned)size);
-
-    __bound_new_region(ptr, size);
-    return ptr;
-}
-
-void *__bound_memalign(size_t size, size_t align, const void *caller)
-{
-    void *ptr;
-
-    restore_malloc_hooks();
-
-#ifndef HAVE_MEMALIGN
-    if (align > 4) {
-        /* XXX: handle it ? */
-        ptr = NULL;
-    } else {
-        /* we suppose that malloc aligns to at least four bytes */
-        ptr = malloc(size + 1);
-    }
-#else
-    /* we allocate one more byte to ensure the regions will be
-       separated by at least one byte. With the glibc malloc, it may
-       be in fact not necessary */
-    ptr = memalign(size + 1, align);
-#endif
-    
-    install_malloc_hooks();
-    
-    if (!ptr)
-        return NULL;
-
-    dprintf(stderr, "%s, %s calling __bound_new_region(%p, %x)\n",
-           __FILE__, __FUNCTION__, ptr, (unsigned)size);
-
-    __bound_new_region(ptr, size);
-    return ptr;
-}
-
-void __bound_free(void *ptr, const void *caller)
-{
-    if (ptr == NULL)
-        return;
-    if (__bound_delete_region(ptr) != 0)
-        bound_error("freeing invalid region");
-
-    libc_free(ptr);
-}
-
-void *__bound_realloc(void *ptr, size_t size, const void *caller)
-{
-    void *ptr1;
-    size_t old_size;
-
-    if (size == 0) {
-        __bound_free(ptr, caller);
-        return NULL;
-    } else {
-        ptr1 = __bound_malloc(size, caller);
-        if (ptr == NULL || ptr1 == NULL)
-            return ptr1;
-        old_size = get_region_size(ptr);
-        if (old_size == EMPTY_SIZE)
-            bound_error("realloc'ing invalid pointer");
-        memcpy(ptr1, ptr, old_size);
-        __bound_free(ptr, caller);
-        return ptr1;
-    }
-}
-
-#ifndef CONFIG_TCC_MALLOC_HOOKS
-void *__bound_calloc(size_t nmemb, size_t size)
-{
-    void *ptr;
-    size = size * nmemb;
-    ptr = __bound_malloc(size, NULL);
-    if (!ptr)
-        return NULL;
-    memset(ptr, 0, size);
-    return ptr;
-}
-#endif
-
-#if 0
-static void bound_dump(void)
-{
-    BoundEntry *page, *e;
-    size_t i, j;
-
-    fprintf(stderr, "region dump:\n");
-    for(i=0;i<BOUND_T1_SIZE;i++) {
-        page = __bound_t1[i];
-        for(j=0;j<BOUND_T2_SIZE;j++) {
-            e = page + j;
-            /* do not print invalid or empty entries */
-            if (e->size != EMPTY_SIZE && e->start != 0) {
-                fprintf(stderr, "%08x:", 
-                       (i << (BOUND_T2_BITS + BOUND_T3_BITS)) + 
-                       (j << BOUND_T3_BITS));
-                do {
-                    fprintf(stderr, " %08lx:%08lx", e->start, e->start + e->size);
-                    e = e->next;
-                } while (e != NULL);
-                fprintf(stderr, "\n");
-            }
-        }
-    }
-}
-#endif
-
-/* some useful checked functions */
-
-/* check that (p ... p + size - 1) lies inside 'p' region, if any */
-static void __bound_check(const void *p, size_t size)
-{
-    if (size == 0)
-        return;
-    p = __bound_ptr_add((void *)p, size - 1);
-    if (p == INVALID_POINTER)
-        bound_error("invalid pointer");
-}
-
-void *__bound_memcpy(void *dst, const void *src, size_t size)
-{
-    void* p;
-
-    dprintf(stderr, "%s %s: start, dst=%p src=%p size=%x\n",
-            __FILE__, __FUNCTION__, dst, src, (unsigned)size);
-
-    __bound_check(dst, size);
-    __bound_check(src, size);
-    /* check also region overlap */
-    if (src >= dst && src < dst + size)
-        bound_error("overlapping regions in memcpy()");
-
-    p = memcpy(dst, src, size);
-
-    dprintf(stderr, "%s %s: end, p=%p\n", __FILE__, __FUNCTION__, p);
-    return p;
-}
-
-void *__bound_memmove(void *dst, const void *src, size_t size)
-{
-    __bound_check(dst, size);
-    __bound_check(src, size);
-    return memmove(dst, src, size);
-}
-
-void *__bound_memset(void *dst, int c, size_t size)
-{
-    __bound_check(dst, size);
-    return memset(dst, c, size);
-}
-
-/* XXX: could be optimized */
-int __bound_strlen(const char *s)
-{
-    const char *p;
-    size_t len;
-
-    len = 0;
-    for(;;) {
-        p = __bound_ptr_indir1((char *)s, len);
-        if (p == INVALID_POINTER)
-            bound_error("bad pointer in strlen()");
-        if (*p == '\0')
-            break;
-        len++;
-    }
-    return len;
-}
-
-char *__bound_strcpy(char *dst, const char *src)
-{
-    size_t len;
-    void *p;
-
-    dprintf(stderr, "%s %s: strcpy start, dst=%p src=%p\n",
-            __FILE__, __FUNCTION__, dst, src);
-    len = __bound_strlen(src);
-    p = __bound_memcpy(dst, src, len + 1);
-    dprintf(stderr, "%s %s: strcpy end, p = %p\n",
-            __FILE__, __FUNCTION__, p);
-    return p;
-}
diff --git a/tinyc/lib/lib-arm64.c b/tinyc/lib/lib-arm64.c
deleted file mode 100644
index b8fd9e85a..000000000
--- a/tinyc/lib/lib-arm64.c
+++ /dev/null
@@ -1,664 +0,0 @@
-/*
- *  TCC runtime library for arm64.
- *
- *  Copyright (c) 2015 Edmund Grimley Evans
- *
- * Copying and distribution of this file, with or without modification,
- * are permitted in any medium without royalty provided the copyright
- * notice and this notice are preserved.  This file is offered as-is,
- * without any warranty.
- */
-
-#ifdef __TINYC__
-typedef signed char int8_t;
-typedef unsigned char uint8_t;
-typedef short int16_t;
-typedef unsigned short uint16_t;
-typedef int int32_t;
-typedef unsigned uint32_t;
-typedef long long int64_t;
-typedef unsigned long long uint64_t;
-void *memcpy(void*,void*,__SIZE_TYPE__);
-#else
-#include <stdint.h>
-#include <string.h>
-#endif
-
-void __clear_cache(void *beg, void *end)
-{
-    __arm64_clear_cache(beg, end);
-}
-
-typedef struct {
-    uint64_t x0, x1;
-} u128_t;
-
-static long double f3_zero(int sgn)
-{
-    long double f;
-    u128_t x = { 0, (uint64_t)sgn << 63 };
-    memcpy(&f, &x, 16);
-    return f;
-}
-
-static long double f3_infinity(int sgn)
-{
-    long double f;
-    u128_t x = { 0, (uint64_t)sgn << 63 | 0x7fff000000000000 };
-    memcpy(&f, &x, 16);
-    return f;
-}
-
-static long double f3_NaN(void)
-{
-    long double f;
-#if 0
-    // ARM's default NaN usually has just the top fraction bit set:
-    u128_t x = {  0, 0x7fff800000000000 };
-#else
-    // GCC's library sets all fraction bits:
-    u128_t x = { -1, 0x7fffffffffffffff };
-#endif
-    memcpy(&f, &x, 16);
-    return f;
-}
-
-static int fp3_convert_NaN(long double *f, int sgn, u128_t mnt)
-{
-    u128_t x = { mnt.x0,
-                 mnt.x1 | 0x7fff800000000000 | (uint64_t)sgn << 63 };
-    memcpy(f, &x, 16);
-    return 1;
-}
-
-static int fp3_detect_NaNs(long double *f,
-                           int a_sgn, int a_exp, u128_t a,
-                           int b_sgn, int b_exp, u128_t b)
-{
-    // Detect signalling NaNs:
-    if (a_exp == 32767 && (a.x0 | a.x1 << 16) && !(a.x1 >> 47 & 1))
-        return fp3_convert_NaN(f, a_sgn, a);
-    if (b_exp == 32767 && (b.x0 | b.x1 << 16) && !(b.x1 >> 47 & 1))
-        return fp3_convert_NaN(f, b_sgn, b);
-
-    // Detect quiet NaNs:
-    if (a_exp == 32767 && (a.x0 | a.x1 << 16))
-        return fp3_convert_NaN(f, a_sgn, a);
-    if (b_exp == 32767 && (b.x0 | b.x1 << 16))
-        return fp3_convert_NaN(f, b_sgn, b);
-
-    return 0;
-}
-
-static void f3_unpack(int *sgn, int32_t *exp, u128_t *mnt, long double f)
-{
-    u128_t x;
-    memcpy(&x, &f, 16);
-    *sgn = x.x1 >> 63;
-    *exp = x.x1 >> 48 & 32767;
-    x.x1 = x.x1 << 16 >> 16;
-    if (*exp)
-        x.x1 |= (uint64_t)1 << 48;
-    else
-        *exp = 1;
-    *mnt = x;
-}
-
-static u128_t f3_normalise(int32_t *exp, u128_t mnt)
-{
-    int sh;
-    if (!(mnt.x0 | mnt.x1))
-        return mnt;
-    if (!mnt.x1) {
-        mnt.x1 = mnt.x0;
-        mnt.x0 = 0;
-        *exp -= 64;
-    }
-    for (sh = 32; sh; sh >>= 1) {
-        if (!(mnt.x1 >> (64 - sh))) {
-            mnt.x1 = mnt.x1 << sh | mnt.x0 >> (64 - sh);
-            mnt.x0 = mnt.x0 << sh;
-            *exp -= sh;
-        }
-    }
-    return mnt;
-}
-
-static u128_t f3_sticky_shift(int32_t sh, u128_t x)
-{
-  if (sh >= 128) {
-      x.x0 = !!(x.x0 | x.x1);
-      x.x1 = 0;
-      return x;
-  }
-  if (sh >= 64) {
-      x.x0 = x.x1 | !!x.x0;
-      x.x1 = 0;
-      sh -= 64;
-  }
-  if (sh > 0) {
-      x.x0 = x.x0 >> sh | x.x1 << (64 - sh) | !!(x.x0 << (64 - sh));
-      x.x1 = x.x1 >> sh;
-  }
-  return x;
-}
-
-static long double f3_round(int sgn, int32_t exp, u128_t x)
-{
-    long double f;
-    int error;
-
-    if (exp > 0) {
-        x = f3_sticky_shift(13, x);
-    }
-    else {
-        x = f3_sticky_shift(14 - exp, x);
-        exp = 0;
-    }
-
-    error = x.x0 & 3;
-    x.x0 = x.x0 >> 2 | x.x1 << 62;
-    x.x1 = x.x1 >> 2;
-
-    if (error == 3 || ((error == 2) & (x.x0 & 1))) {
-        if (!++x.x0) {
-            ++x.x1;
-            if (x.x1 == (uint64_t)1 << 48)
-                exp = 1;
-            else if (x.x1 == (uint64_t)1 << 49) {
-                ++exp;
-                x.x0 = x.x0 >> 1 | x.x1 << 63;
-                x.x1 = x.x1 >> 1;
-            }
-        }
-    }
-
-    if (exp >= 32767)
-        return f3_infinity(sgn);
-
-    x.x1 = x.x1 << 16 >> 16 | (uint64_t)exp << 48 | (uint64_t)sgn << 63;
-    memcpy(&f, &x, 16);
-    return f;
-}
-
-static long double f3_add(long double fa, long double fb, int neg)
-{
-    u128_t a, b, x;
-    int32_t a_exp, b_exp, x_exp;
-    int a_sgn, b_sgn, x_sgn;
-    long double fx;
-
-    f3_unpack(&a_sgn, &a_exp, &a, fa);
-    f3_unpack(&b_sgn, &b_exp, &b, fb);
-
-    if (fp3_detect_NaNs(&fx, a_sgn, a_exp, a, b_sgn, b_exp, b))
-        return fx;
-
-    b_sgn ^= neg;
-
-    // Handle infinities and zeroes:
-    if (a_exp == 32767 && b_exp == 32767 && a_sgn != b_sgn)
-        return f3_NaN();
-    if (a_exp == 32767)
-        return f3_infinity(a_sgn);
-    if (b_exp == 32767)
-        return f3_infinity(b_sgn);
-    if (!(a.x0 | a.x1 | b.x0 | b.x1))
-        return f3_zero(a_sgn & b_sgn);
-
-    a.x1 = a.x1 << 3 | a.x0 >> 61;
-    a.x0 = a.x0 << 3;
-    b.x1 = b.x1 << 3 | b.x0 >> 61;
-    b.x0 = b.x0 << 3;
-
-    if (a_exp <= b_exp) {
-        a = f3_sticky_shift(b_exp - a_exp, a);
-        a_exp = b_exp;
-    }
-    else {
-        b = f3_sticky_shift(a_exp - b_exp, b);
-        b_exp = a_exp;
-    }
-
-    x_sgn = a_sgn;
-    x_exp = a_exp;
-    if (a_sgn == b_sgn) {
-        x.x0 = a.x0 + b.x0;
-        x.x1 = a.x1 + b.x1 + (x.x0 < a.x0);
-    }
-    else {
-        x.x0 = a.x0 - b.x0;
-        x.x1 = a.x1 - b.x1 - (x.x0 > a.x0);
-        if (x.x1 >> 63) {
-            x_sgn ^= 1;
-            x.x0 = -x.x0;
-            x.x1 = -x.x1 - !!x.x0;
-        }
-    }
-
-    if (!(x.x0 | x.x1))
-        return f3_zero(0);
-
-    x = f3_normalise(&x_exp, x);
-
-    return f3_round(x_sgn, x_exp + 12, x);
-}
-
-long double __addtf3(long double a, long double b)
-{
-    return f3_add(a, b, 0);
-}
-
-long double __subtf3(long double a, long double b)
-{
-    return f3_add(a, b, 1);
-}
-
-long double __multf3(long double fa, long double fb)
-{
-    u128_t a, b, x;
-    int32_t a_exp, b_exp, x_exp;
-    int a_sgn, b_sgn, x_sgn;
-    long double fx;
-
-    f3_unpack(&a_sgn, &a_exp, &a, fa);
-    f3_unpack(&b_sgn, &b_exp, &b, fb);
-
-    if (fp3_detect_NaNs(&fx, a_sgn, a_exp, a, b_sgn, b_exp, b))
-        return fx;
-
-    // Handle infinities and zeroes:
-    if ((a_exp == 32767 && !(b.x0 | b.x1)) ||
-        (b_exp == 32767 && !(a.x0 | a.x1)))
-        return f3_NaN();
-    if (a_exp == 32767 || b_exp == 32767)
-        return f3_infinity(a_sgn ^ b_sgn);
-    if (!(a.x0 | a.x1) || !(b.x0 | b.x1))
-        return f3_zero(a_sgn ^ b_sgn);
-
-    a = f3_normalise(&a_exp, a);
-    b = f3_normalise(&b_exp, b);
-
-    x_sgn = a_sgn ^ b_sgn;
-    x_exp = a_exp + b_exp - 16352;
-
-    {
-        // Convert to base (1 << 30), discarding bottom 6 bits, which are zero,
-        // so there are (32, 30, 30, 30) bits in (a3, a2, a1, a0):
-        uint64_t a0 = a.x0 << 28 >> 34;
-        uint64_t b0 = b.x0 << 28 >> 34;
-        uint64_t a1 = a.x0 >> 36 | a.x1 << 62 >> 34;
-        uint64_t b1 = b.x0 >> 36 | b.x1 << 62 >> 34;
-        uint64_t a2 = a.x1 << 32 >> 34;
-        uint64_t b2 = b.x1 << 32 >> 34;
-        uint64_t a3 = a.x1 >> 32;
-        uint64_t b3 = b.x1 >> 32;
-        // Use 16 small multiplications and additions that do not overflow:
-        uint64_t x0 = a0 * b0;
-        uint64_t x1 = (x0 >> 30) + a0 * b1 + a1 * b0;
-        uint64_t x2 = (x1 >> 30) + a0 * b2 + a1 * b1 + a2 * b0;
-        uint64_t x3 = (x2 >> 30) + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0;
-        uint64_t x4 = (x3 >> 30) + a1 * b3 + a2 * b2 + a3 * b1;
-        uint64_t x5 = (x4 >> 30) + a2 * b3 + a3 * b2;
-        uint64_t x6 = (x5 >> 30) + a3 * b3;
-        // We now have (64, 30, 30, ...) bits in (x6, x5, x4, ...).
-        // Take the top 128 bits, setting bottom bit if any lower bits were set:
-        uint64_t y0 = (x5 << 34 | x4 << 34 >> 30 | x3 << 34 >> 60 |
-                       !!(x3 << 38 | (x2 | x1 | x0) << 34));
-        uint64_t y1 = x6;
-        // Top bit may be zero. Renormalise:
-        if (!(y1 >> 63)) {
-            y1 = y1 << 1 | y0 >> 63;
-            y0 = y0 << 1;
-            --x_exp;
-        }
-        x.x0 = y0;
-        x.x1 = y1;
-    }
-
-    return f3_round(x_sgn, x_exp, x);
-}
-
-long double __divtf3(long double fa, long double fb)
-{
-    u128_t a, b, x;
-    int32_t a_exp, b_exp, x_exp;
-    int a_sgn, b_sgn, x_sgn, i;
-    long double fx;
-
-    f3_unpack(&a_sgn, &a_exp, &a, fa);
-    f3_unpack(&b_sgn, &b_exp, &b, fb);
-
-    if (fp3_detect_NaNs(&fx, a_sgn, a_exp, a, b_sgn, b_exp, b))
-        return fx;
-
-    // Handle infinities and zeroes:
-    if ((a_exp == 32767 && b_exp == 32767) ||
-        (!(a.x0 | a.x1) && !(b.x0 | b.x1)))
-        return f3_NaN();
-    if (a_exp == 32767 || !(b.x0 | b.x1))
-        return f3_infinity(a_sgn ^ b_sgn);
-    if (!(a.x0 | a.x1) || b_exp == 32767)
-        return f3_zero(a_sgn ^ b_sgn);
-
-    a = f3_normalise(&a_exp, a);
-    b = f3_normalise(&b_exp, b);
-
-    x_sgn = a_sgn ^ b_sgn;
-    x_exp = a_exp - b_exp + 16395;
-
-    a.x0 = a.x0 >> 1 | a.x1 << 63;
-    a.x1 = a.x1 >> 1;
-    b.x0 = b.x0 >> 1 | b.x1 << 63;
-    b.x1 = b.x1 >> 1;
-    x.x0 = 0;
-    x.x1 = 0;
-    for (i = 0; i < 116; i++) {
-        x.x1 = x.x1 << 1 | x.x0 >> 63;
-        x.x0 = x.x0 << 1;
-        if (a.x1 > b.x1 || (a.x1 == b.x1 && a.x0 >= b.x0)) {
-            a.x1 = a.x1 - b.x1 - (a.x0 < b.x0);
-            a.x0 = a.x0 - b.x0;
-            x.x0 |= 1;
-        }
-        a.x1 = a.x1 << 1 | a.x0 >> 63;
-        a.x0 = a.x0 << 1;
-    }
-    x.x0 |= !!(a.x0 | a.x1);
-
-    x = f3_normalise(&x_exp, x);
-
-    return f3_round(x_sgn, x_exp, x);
-}
-
-long double __extendsftf2(float f)
-{
-    long double fx;
-    u128_t x;
-    uint32_t a;
-    uint64_t aa;
-    memcpy(&a, &f, 4);
-    aa = a;
-    x.x0 = 0;
-    if (!(a << 1))
-        x.x1 = aa << 32;
-    else if (a << 1 >> 24 == 255)
-        x.x1 = (0x7fff000000000000 | aa >> 31 << 63 | aa << 41 >> 16 |
-                (uint64_t)!!(a << 9) << 47);
-    else
-        x.x1 = (aa >> 31 << 63 | ((aa >> 23 & 255) + 16256) << 48 |
-                aa << 41 >> 16);
-    memcpy(&fx, &x, 16);
-    return fx;
-}
-
-long double __extenddftf2(double f)
-{
-    long double fx;
-    u128_t x;
-    uint64_t a;
-    memcpy(&a, &f, 8);
-    x.x0 = a << 60;
-    if (!(a << 1))
-        x.x1 = a;
-    else if (a << 1 >> 53 == 2047)
-        x.x1 = (0x7fff000000000000 | a >> 63 << 63 | a << 12 >> 16 |
-                (uint64_t)!!(a << 12) << 47);
-    else
-        x.x1 = a >> 63 << 63 | ((a >> 52 & 2047) + 15360) << 48 | a << 12 >> 16;
-    memcpy(&fx, &x, 16);
-    return fx;
-}
-
-float __trunctfsf2(long double f)
-{
-    u128_t mnt;
-    int32_t exp;
-    int sgn;
-    uint32_t x;
-    float fx;
-
-    f3_unpack(&sgn, &exp, &mnt, f);
-
-    if (exp == 32767 && (mnt.x0 | mnt.x1 << 16))
-        x = 0x7fc00000 | (uint32_t)sgn << 31 | (mnt.x1 >> 25 & 0x007fffff);
-    else if (exp > 16510)
-        x = 0x7f800000 | (uint32_t)sgn << 31;
-    else if (exp < 16233)
-        x = (uint32_t)sgn << 31;
-    else {
-        exp -= 16257;
-        x = mnt.x1 >> 23 | !!(mnt.x0 | mnt.x1 << 41);
-        if (exp < 0) {
-            x = x >> -exp | !!(x << (32 + exp));
-            exp = 0;
-        }
-        if ((x & 3) == 3 || (x & 7) == 6)
-            x += 4;
-        x = ((x >> 2) + (exp << 23)) | (uint32_t)sgn << 31;
-    }
-    memcpy(&fx, &x, 4);
-    return fx;
-}
-
-double __trunctfdf2(long double f)
-{
-    u128_t mnt;
-    int32_t exp;
-    int sgn;
-    uint64_t x;
-    double fx;
-
-    f3_unpack(&sgn, &exp, &mnt, f);
-
-    if (exp == 32767 && (mnt.x0 | mnt.x1 << 16))
-        x = (0x7ff8000000000000 | (uint64_t)sgn << 63 |
-             mnt.x1 << 16 >> 12 | mnt.x0 >> 60);
-    else if (exp > 17406)
-        x = 0x7ff0000000000000 | (uint64_t)sgn << 63;
-    else if (exp < 15308)
-        x = (uint64_t)sgn << 63;
-    else {
-        exp -= 15361;
-        x = mnt.x1 << 6 | mnt.x0 >> 58 | !!(mnt.x0 << 6);
-        if (exp < 0) {
-            x = x >> -exp | !!(x << (64 + exp));
-            exp = 0;
-        }
-        if ((x & 3) == 3 || (x & 7) == 6)
-            x += 4;
-        x = ((x >> 2) + ((uint64_t)exp << 52)) | (uint64_t)sgn << 63;
-    }
-    memcpy(&fx, &x, 8);
-    return fx;
-}
-
-int32_t __fixtfsi(long double fa)
-{
-    u128_t a;
-    int32_t a_exp;
-    int a_sgn;
-    int32_t x;
-    f3_unpack(&a_sgn, &a_exp, &a, fa);
-    if (a_exp < 16369)
-        return 0;
-    if (a_exp > 16413)
-        return a_sgn ? -0x80000000 : 0x7fffffff;
-    x = a.x1 >> (16431 - a_exp);
-    return a_sgn ? -x : x;
-}
-
-int64_t __fixtfdi(long double fa)
-{
-    u128_t a;
-    int32_t a_exp;
-    int a_sgn;
-    int64_t x;
-    f3_unpack(&a_sgn, &a_exp, &a, fa);
-    if (a_exp < 16383)
-        return 0;
-    if (a_exp > 16445)
-        return a_sgn ? -0x8000000000000000 : 0x7fffffffffffffff;
-    x = (a.x1 << 15 | a.x0 >> 49) >> (16446 - a_exp);
-    return a_sgn ? -x : x;
-}
-
-uint32_t __fixunstfsi(long double fa)
-{
-    u128_t a;
-    int32_t a_exp;
-    int a_sgn;
-    f3_unpack(&a_sgn, &a_exp, &a, fa);
-    if (a_sgn || a_exp < 16369)
-        return 0;
-    if (a_exp > 16414)
-        return -1;
-    return a.x1 >> (16431 - a_exp);
-}
-
-uint64_t __fixunstfdi(long double fa)
-{
-    u128_t a;
-    int32_t a_exp;
-    int a_sgn;
-    f3_unpack(&a_sgn, &a_exp, &a, fa);
-    if (a_sgn || a_exp < 16383)
-        return 0;
-    if (a_exp > 16446)
-        return -1;
-    return (a.x1 << 15 | a.x0 >> 49) >> (16446 - a_exp);
-}
-
-long double __floatsitf(int32_t a)
-{
-    int sgn = 0;
-    int exp = 16414;
-    uint32_t mnt = a;
-    u128_t x = { 0, 0 };
-    long double f;
-    int i;
-    if (a) {
-        if (a < 0) {
-            sgn = 1;
-            mnt = -mnt;
-        }
-        for (i = 16; i; i >>= 1)
-            if (!(mnt >> (32 - i))) {
-                mnt <<= i;
-                exp -= i;
-            }
-        x.x1 = ((uint64_t)sgn << 63 | (uint64_t)exp << 48 |
-                (uint64_t)(mnt << 1) << 16);
-    }
-    memcpy(&f, &x, 16);
-    return f;
-}
-
-long double __floatditf(int64_t a)
-{
-    int sgn = 0;
-    int exp = 16446;
-    uint64_t mnt = a;
-    u128_t x = { 0, 0 };
-    long double f;
-    int i;
-    if (a) {
-        if (a < 0) {
-            sgn = 1;
-            mnt = -mnt;
-        }
-        for (i = 32; i; i >>= 1)
-            if (!(mnt >> (64 - i))) {
-                mnt <<= i;
-                exp -= i;
-            }
-        x.x0 = mnt << 49;
-        x.x1 = (uint64_t)sgn << 63 | (uint64_t)exp << 48 | mnt << 1 >> 16;
-    }
-    memcpy(&f, &x, 16);
-    return f;
-}
-
-long double __floatunsitf(uint32_t a)
-{
-    int exp = 16414;
-    uint32_t mnt = a;
-    u128_t x = { 0, 0 };
-    long double f;
-    int i;
-    if (a) {
-        for (i = 16; i; i >>= 1)
-            if (!(mnt >> (32 - i))) {
-                mnt <<= i;
-                exp -= i;
-            }
-        x.x1 = (uint64_t)exp << 48 | (uint64_t)(mnt << 1) << 16;
-    }
-    memcpy(&f, &x, 16);
-    return f;
-}
-
-long double __floatunditf(uint64_t a)
-{
-    int exp = 16446;
-    uint64_t mnt = a;
-    u128_t x = { 0, 0 };
-    long double f;
-    int i;
-    if (a) {
-        for (i = 32; i; i >>= 1)
-            if (!(mnt >> (64 - i))) {
-                mnt <<= i;
-                exp -= i;
-            }
-        x.x0 = mnt << 49;
-        x.x1 = (uint64_t)exp << 48 | mnt << 1 >> 16;
-    }
-    memcpy(&f, &x, 16);
-    return f;
-}
-
-static int f3_cmp(long double fa, long double fb)
-{
-    u128_t a, b;
-    memcpy(&a, &fa, 16);
-    memcpy(&b, &fb, 16);
-    return (!(a.x0 | a.x1 << 1 | b.x0 | b.x1 << 1) ? 0 :
-            ((a.x1 << 1 >> 49 == 0x7fff && (a.x0 | a.x1 << 16)) ||
-             (b.x1 << 1 >> 49 == 0x7fff && (b.x0 | b.x1 << 16))) ? 2 :
-            a.x1 >> 63 != b.x1 >> 63 ? (int)(b.x1 >> 63) - (int)(a.x1 >> 63) :
-            a.x1 < b.x1 ? (int)(a.x1 >> 63 << 1) - 1 :
-            a.x1 > b.x1 ? 1 - (int)(a.x1 >> 63 << 1) :
-            a.x0 < b.x0 ? (int)(a.x1 >> 63 << 1) - 1 :
-            b.x0 < a.x0 ? 1 - (int)(a.x1 >> 63 << 1) : 0);
-}
-
-int __eqtf2(long double a, long double b)
-{
-    return !!f3_cmp(a, b);
-}
-
-int __netf2(long double a, long double b)
-{
-    return !!f3_cmp(a, b);
-}
-
-int __lttf2(long double a, long double b)
-{
-    return f3_cmp(a, b);
-}
-
-int __letf2(long double a, long double b)
-{
-    return f3_cmp(a, b);
-}
-
-int __gttf2(long double a, long double b)
-{
-    return -f3_cmp(b, a);
-}
-
-int __getf2(long double a, long double b)
-{
-    return -f3_cmp(b, a);
-}
diff --git a/tinyc/lib/libtcc1.c b/tinyc/lib/libtcc1.c
deleted file mode 100644
index 0e466180c..000000000
--- a/tinyc/lib/libtcc1.c
+++ /dev/null
@@ -1,622 +0,0 @@
-/* TCC runtime library. 
-   Parts of this code are (c) 2002 Fabrice Bellard 
-
-   Copyright (C) 1987, 1988, 1992, 1994, 1995 Free Software Foundation, Inc.
-
-This file is free software; you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by the
-Free Software Foundation; either version 2, or (at your option) any
-later version.
-
-In addition to the permissions in the GNU General Public License, the
-Free Software Foundation gives you unlimited permission to link the
-compiled version of this file into combinations with other programs,
-and to distribute those combinations without any restriction coming
-from the use of this file.  (The General Public License restrictions
-do apply in other respects; for example, they cover modification of
-the file, and distribution when not linked into a combine
-executable.)
-
-This file is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; see the file COPYING.  If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  
-*/
-
-#define W_TYPE_SIZE   32
-#define BITS_PER_UNIT 8
-
-typedef int Wtype;
-typedef unsigned int UWtype;
-typedef unsigned int USItype;
-typedef long long DWtype;
-typedef unsigned long long UDWtype;
-
-struct DWstruct {
-    Wtype low, high;
-};
-
-typedef union
-{
-  struct DWstruct s;
-  DWtype ll;
-} DWunion;
-
-typedef long double XFtype;
-#define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT)
-#define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE)
-
-/* the following deal with IEEE single-precision numbers */
-#define EXCESS		126
-#define SIGNBIT		0x80000000
-#define HIDDEN		(1 << 23)
-#define SIGN(fp)	((fp) & SIGNBIT)
-#define EXP(fp)		(((fp) >> 23) & 0xFF)
-#define MANT(fp)	(((fp) & 0x7FFFFF) | HIDDEN)
-#define PACK(s,e,m)	((s) | ((e) << 23) | (m))
-
-/* the following deal with IEEE double-precision numbers */
-#define EXCESSD		1022
-#define HIDDEND		(1 << 20)
-#define EXPD(fp)	(((fp.l.upper) >> 20) & 0x7FF)
-#define SIGND(fp)	((fp.l.upper) & SIGNBIT)
-#define MANTD(fp)	(((((fp.l.upper) & 0xFFFFF) | HIDDEND) << 10) | \
-				(fp.l.lower >> 22))
-#define HIDDEND_LL	((long long)1 << 52)
-#define MANTD_LL(fp)	((fp.ll & (HIDDEND_LL-1)) | HIDDEND_LL)
-#define PACKD_LL(s,e,m)	(((long long)((s)+((e)<<20))<<32)|(m))
-
-/* the following deal with x86 long double-precision numbers */
-#define EXCESSLD	16382
-#define EXPLD(fp)	(fp.l.upper & 0x7fff)
-#define SIGNLD(fp)	((fp.l.upper) & 0x8000)
-
-/* only for x86 */
-union ldouble_long {
-    long double ld;
-    struct {
-        unsigned long long lower;
-        unsigned short upper;
-    } l;
-};
-
-union double_long {
-    double d;
-#if 1
-    struct {
-        unsigned int lower;
-        int upper;
-    } l;
-#else
-    struct {
-        int upper;
-        unsigned int lower;
-    } l;
-#endif
-    long long ll;
-};
-
-union float_long {
-    float f;
-    unsigned int l;
-};
-
-/* XXX: we don't support several builtin supports for now */
-#if !defined __x86_64__ && !defined __arm__
-
-/* XXX: use gcc/tcc intrinsic ? */
-#if defined __i386__
-#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-  __asm__ ("subl %5,%1\n\tsbbl %3,%0"					\
-	   : "=r" ((USItype) (sh)),					\
-	     "=&r" ((USItype) (sl))					\
-	   : "0" ((USItype) (ah)),					\
-	     "g" ((USItype) (bh)),					\
-	     "1" ((USItype) (al)),					\
-	     "g" ((USItype) (bl)))
-#define umul_ppmm(w1, w0, u, v) \
-  __asm__ ("mull %3"							\
-	   : "=a" ((USItype) (w0)),					\
-	     "=d" ((USItype) (w1))					\
-	   : "%0" ((USItype) (u)),					\
-	     "rm" ((USItype) (v)))
-#define udiv_qrnnd(q, r, n1, n0, dv) \
-  __asm__ ("divl %4"							\
-	   : "=a" ((USItype) (q)),					\
-	     "=d" ((USItype) (r))					\
-	   : "0" ((USItype) (n0)),					\
-	     "1" ((USItype) (n1)),					\
-	     "rm" ((USItype) (dv)))
-#define count_leading_zeros(count, x) \
-  do {									\
-    USItype __cbtmp;							\
-    __asm__ ("bsrl %1,%0"						\
-	     : "=r" (__cbtmp) : "rm" ((USItype) (x)));			\
-    (count) = __cbtmp ^ 31;						\
-  } while (0)
-#else
-#error unsupported CPU type
-#endif
-
-/* most of this code is taken from libgcc2.c from gcc */
-
-static UDWtype __udivmoddi4 (UDWtype n, UDWtype d, UDWtype *rp)
-{
-  DWunion ww;
-  DWunion nn, dd;
-  DWunion rr;
-  UWtype d0, d1, n0, n1, n2;
-  UWtype q0, q1;
-  UWtype b, bm;
-
-  nn.ll = n;
-  dd.ll = d;
-
-  d0 = dd.s.low;
-  d1 = dd.s.high;
-  n0 = nn.s.low;
-  n1 = nn.s.high;
-
-#if !defined(UDIV_NEEDS_NORMALIZATION)
-  if (d1 == 0)
-    {
-      if (d0 > n1)
-	{
-	  /* 0q = nn / 0D */
-
-	  udiv_qrnnd (q0, n0, n1, n0, d0);
-	  q1 = 0;
-
-	  /* Remainder in n0.  */
-	}
-      else
-	{
-	  /* qq = NN / 0d */
-
-	  if (d0 == 0)
-	    d0 = 1 / d0;	/* Divide intentionally by zero.  */
-
-	  udiv_qrnnd (q1, n1, 0, n1, d0);
-	  udiv_qrnnd (q0, n0, n1, n0, d0);
-
-	  /* Remainder in n0.  */
-	}
-
-      if (rp != 0)
-	{
-	  rr.s.low = n0;
-	  rr.s.high = 0;
-	  *rp = rr.ll;
-	}
-    }
-
-#else /* UDIV_NEEDS_NORMALIZATION */
-
-  if (d1 == 0)
-    {
-      if (d0 > n1)
-	{
-	  /* 0q = nn / 0D */
-
-	  count_leading_zeros (bm, d0);
-
-	  if (bm != 0)
-	    {
-	      /* Normalize, i.e. make the most significant bit of the
-		 denominator set.  */
-
-	      d0 = d0 << bm;
-	      n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm));
-	      n0 = n0 << bm;
-	    }
-
-	  udiv_qrnnd (q0, n0, n1, n0, d0);
-	  q1 = 0;
-
-	  /* Remainder in n0 >> bm.  */
-	}
-      else
-	{
-	  /* qq = NN / 0d */
-
-	  if (d0 == 0)
-	    d0 = 1 / d0;	/* Divide intentionally by zero.  */
-
-	  count_leading_zeros (bm, d0);
-
-	  if (bm == 0)
-	    {
-	      /* From (n1 >= d0) /\ (the most significant bit of d0 is set),
-		 conclude (the most significant bit of n1 is set) /\ (the
-		 leading quotient digit q1 = 1).
-
-		 This special case is necessary, not an optimization.
-		 (Shifts counts of W_TYPE_SIZE are undefined.)  */
-
-	      n1 -= d0;
-	      q1 = 1;
-	    }
-	  else
-	    {
-	      /* Normalize.  */
-
-	      b = W_TYPE_SIZE - bm;
-
-	      d0 = d0 << bm;
-	      n2 = n1 >> b;
-	      n1 = (n1 << bm) | (n0 >> b);
-	      n0 = n0 << bm;
-
-	      udiv_qrnnd (q1, n1, n2, n1, d0);
-	    }
-
-	  /* n1 != d0...  */
-
-	  udiv_qrnnd (q0, n0, n1, n0, d0);
-
-	  /* Remainder in n0 >> bm.  */
-	}
-
-      if (rp != 0)
-	{
-	  rr.s.low = n0 >> bm;
-	  rr.s.high = 0;
-	  *rp = rr.ll;
-	}
-    }
-#endif /* UDIV_NEEDS_NORMALIZATION */
-
-  else
-    {
-      if (d1 > n1)
-	{
-	  /* 00 = nn / DD */
-
-	  q0 = 0;
-	  q1 = 0;
-
-	  /* Remainder in n1n0.  */
-	  if (rp != 0)
-	    {
-	      rr.s.low = n0;
-	      rr.s.high = n1;
-	      *rp = rr.ll;
-	    }
-	}
-      else
-	{
-	  /* 0q = NN / dd */
-
-	  count_leading_zeros (bm, d1);
-	  if (bm == 0)
-	    {
-	      /* From (n1 >= d1) /\ (the most significant bit of d1 is set),
-		 conclude (the most significant bit of n1 is set) /\ (the
-		 quotient digit q0 = 0 or 1).
-
-		 This special case is necessary, not an optimization.  */
-
-	      /* The condition on the next line takes advantage of that
-		 n1 >= d1 (true due to program flow).  */
-	      if (n1 > d1 || n0 >= d0)
-		{
-		  q0 = 1;
-		  sub_ddmmss (n1, n0, n1, n0, d1, d0);
-		}
-	      else
-		q0 = 0;
-
-	      q1 = 0;
-
-	      if (rp != 0)
-		{
-		  rr.s.low = n0;
-		  rr.s.high = n1;
-		  *rp = rr.ll;
-		}
-	    }
-	  else
-	    {
-	      UWtype m1, m0;
-	      /* Normalize.  */
-
-	      b = W_TYPE_SIZE - bm;
-
-	      d1 = (d1 << bm) | (d0 >> b);
-	      d0 = d0 << bm;
-	      n2 = n1 >> b;
-	      n1 = (n1 << bm) | (n0 >> b);
-	      n0 = n0 << bm;
-
-	      udiv_qrnnd (q0, n1, n2, n1, d1);
-	      umul_ppmm (m1, m0, q0, d0);
-
-	      if (m1 > n1 || (m1 == n1 && m0 > n0))
-		{
-		  q0--;
-		  sub_ddmmss (m1, m0, m1, m0, d1, d0);
-		}
-
-	      q1 = 0;
-
-	      /* Remainder in (n1n0 - m1m0) >> bm.  */
-	      if (rp != 0)
-		{
-		  sub_ddmmss (n1, n0, n1, n0, m1, m0);
-		  rr.s.low = (n1 << b) | (n0 >> bm);
-		  rr.s.high = n1 >> bm;
-		  *rp = rr.ll;
-		}
-	    }
-	}
-    }
-
-  ww.s.low = q0;
-  ww.s.high = q1;
-  return ww.ll;
-}
-
-#define __negdi2(a) (-(a))
-
-long long __divdi3(long long u, long long v)
-{
-    int c = 0;
-    DWunion uu, vv;
-    DWtype w;
-    
-    uu.ll = u;
-    vv.ll = v;
-    
-    if (uu.s.high < 0) {
-        c = ~c;
-        uu.ll = __negdi2 (uu.ll);
-    }
-    if (vv.s.high < 0) {
-        c = ~c;
-        vv.ll = __negdi2 (vv.ll);
-    }
-    w = __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) 0);
-    if (c)
-        w = __negdi2 (w);
-    return w;
-}
-
-long long __moddi3(long long u, long long v)
-{
-    int c = 0;
-    DWunion uu, vv;
-    DWtype w;
-    
-    uu.ll = u;
-    vv.ll = v;
-    
-    if (uu.s.high < 0) {
-        c = ~c;
-        uu.ll = __negdi2 (uu.ll);
-    }
-    if (vv.s.high < 0)
-        vv.ll = __negdi2 (vv.ll);
-    
-    __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) &w);
-    if (c)
-        w = __negdi2 (w);
-    return w;
-}
-
-unsigned long long __udivdi3(unsigned long long u, unsigned long long v)
-{
-    return __udivmoddi4 (u, v, (UDWtype *) 0);
-}
-
-unsigned long long __umoddi3(unsigned long long u, unsigned long long v)
-{
-    UDWtype w;
-    
-    __udivmoddi4 (u, v, &w);
-    return w;
-}
-
-/* XXX: fix tcc's code generator to do this instead */
-long long __ashrdi3(long long a, int b)
-{
-#ifdef __TINYC__
-    DWunion u;
-    u.ll = a;
-    if (b >= 32) {
-        u.s.low = u.s.high >> (b - 32);
-        u.s.high = u.s.high >> 31;
-    } else if (b != 0) {
-        u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b));
-        u.s.high = u.s.high >> b;
-    }
-    return u.ll;
-#else
-    return a >> b;
-#endif
-}
-
-/* XXX: fix tcc's code generator to do this instead */
-unsigned long long __lshrdi3(unsigned long long a, int b)
-{
-#ifdef __TINYC__
-    DWunion u;
-    u.ll = a;
-    if (b >= 32) {
-        u.s.low = (unsigned)u.s.high >> (b - 32);
-        u.s.high = 0;
-    } else if (b != 0) {
-        u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b));
-        u.s.high = (unsigned)u.s.high >> b;
-    }
-    return u.ll;
-#else
-    return a >> b;
-#endif
-}
-
-/* XXX: fix tcc's code generator to do this instead */
-long long __ashldi3(long long a, int b)
-{
-#ifdef __TINYC__
-    DWunion u;
-    u.ll = a;
-    if (b >= 32) {
-        u.s.high = (unsigned)u.s.low << (b - 32);
-        u.s.low = 0;
-    } else if (b != 0) {
-        u.s.high = ((unsigned)u.s.high << b) | ((unsigned)u.s.low >> (32 - b));
-        u.s.low = (unsigned)u.s.low << b;
-    }
-    return u.ll;
-#else
-    return a << b;
-#endif
-}
-
-#endif /* !__x86_64__ */
-
-/* XXX: fix tcc's code generator to do this instead */
-float __floatundisf(unsigned long long a)
-{
-    DWunion uu; 
-    XFtype r;
-
-    uu.ll = a;
-    if (uu.s.high >= 0) {
-        return (float)uu.ll;
-    } else {
-        r = (XFtype)uu.ll;
-        r += 18446744073709551616.0;
-        return (float)r;
-    }
-}
-
-double __floatundidf(unsigned long long a)
-{
-    DWunion uu; 
-    XFtype r;
-
-    uu.ll = a;
-    if (uu.s.high >= 0) {
-        return (double)uu.ll;
-    } else {
-        r = (XFtype)uu.ll;
-        r += 18446744073709551616.0;
-        return (double)r;
-    }
-}
-
-long double __floatundixf(unsigned long long a)
-{
-    DWunion uu; 
-    XFtype r;
-
-    uu.ll = a;
-    if (uu.s.high >= 0) {
-        return (long double)uu.ll;
-    } else {
-        r = (XFtype)uu.ll;
-        r += 18446744073709551616.0;
-        return (long double)r;
-    }
-}
-
-unsigned long long __fixunssfdi (float a1)
-{
-    register union float_long fl1;
-    register int exp;
-    register unsigned long l;
-
-    fl1.f = a1;
-
-    if (fl1.l == 0)
-	return (0);
-
-    exp = EXP (fl1.l) - EXCESS - 24;
-
-    l = MANT(fl1.l);
-    if (exp >= 41)
-	return (unsigned long long)-1;
-    else if (exp >= 0)
-        return (unsigned long long)l << exp;
-    else if (exp >= -23)
-        return l >> -exp;
-    else
-        return 0;
-}
-
-long long __fixsfdi (float a1)
-{
-    long long ret; int s;
-    ret = __fixunssfdi((s = a1 >= 0) ? a1 : -a1);
-    return s ? ret : -ret;
-}
-
-unsigned long long __fixunsdfdi (double a1)
-{
-    register union double_long dl1;
-    register int exp;
-    register unsigned long long l;
-
-    dl1.d = a1;
-
-    if (dl1.ll == 0)
-	return (0);
-
-    exp = EXPD (dl1) - EXCESSD - 53;
-
-    l = MANTD_LL(dl1);
-
-    if (exp >= 12)
-	return (unsigned long long)-1;
-    else if (exp >= 0)
-        return l << exp;
-    else if (exp >= -52)
-        return l >> -exp;
-    else
-        return 0;
-}
-
-long long __fixdfdi (double a1)
-{
-    long long ret; int s;
-    ret = __fixunsdfdi((s = a1 >= 0) ? a1 : -a1);
-    return s ? ret : -ret;
-}
-
-#ifndef __arm__
-unsigned long long __fixunsxfdi (long double a1)
-{
-    register union ldouble_long dl1;
-    register int exp;
-    register unsigned long long l;
-
-    dl1.ld = a1;
-
-    if (dl1.l.lower == 0 && dl1.l.upper == 0)
-	return (0);
-
-    exp = EXPLD (dl1) - EXCESSLD - 64;
-
-    l = dl1.l.lower;
-
-    if (exp > 0)
-	return (unsigned long long)-1;
-    else if (exp >= -63) 
-        return l >> -exp;
-    else
-        return 0;
-}
-
-long long __fixxfdi (long double a1)
-{
-    long long ret; int s;
-    ret = __fixunsxfdi((s = a1 >= 0) ? a1 : -a1);
-    return s ? ret : -ret;
-}
-#endif /* !ARM */
diff --git a/tinyc/lib/va_list.c b/tinyc/lib/va_list.c
deleted file mode 100644
index 8749f46f8..000000000
--- a/tinyc/lib/va_list.c
+++ /dev/null
@@ -1,65 +0,0 @@
-/* va_list.c - tinycc support for va_list on X86_64 */
-
-#if defined __x86_64__
-
-/* Avoid include files, they may not be available when cross compiling */
-extern void *memset(void *s, int c, __SIZE_TYPE__ n);
-extern void abort(void);
-
-/* This should be in sync with our include/stdarg.h */
-enum __va_arg_type {
-    __va_gen_reg, __va_float_reg, __va_stack
-};
-
-/* GCC compatible definition of va_list. */
-typedef struct {
-    unsigned int gp_offset;
-    unsigned int fp_offset;
-    union {
-        unsigned int overflow_offset;
-        char *overflow_arg_area;
-    };
-    char *reg_save_area;
-} __va_list_struct;
-
-void __va_start(__va_list_struct *ap, void *fp)
-{
-    memset(ap, 0, sizeof(__va_list_struct));
-    *ap = *(__va_list_struct *)((char *)fp - 16);
-    ap->overflow_arg_area = (char *)fp + ap->overflow_offset;
-    ap->reg_save_area = (char *)fp - 176 - 16;
-}
-
-void *__va_arg(__va_list_struct *ap,
-               enum __va_arg_type arg_type,
-               int size, int align)
-{
-    size = (size + 7) & ~7;
-    align = (align + 7) & ~7;
-    switch (arg_type) {
-    case __va_gen_reg:
-        if (ap->gp_offset + size <= 48) {
-            ap->gp_offset += size;
-            return ap->reg_save_area + ap->gp_offset - size;
-        }
-        goto use_overflow_area;
-
-    case __va_float_reg:
-        if (ap->fp_offset < 128 + 48) {
-            ap->fp_offset += 16;
-            return ap->reg_save_area + ap->fp_offset - 16;
-        }
-        size = 8;
-        goto use_overflow_area;
-
-    case __va_stack:
-    use_overflow_area:
-        ap->overflow_arg_area += size;
-        ap->overflow_arg_area = (char*)((long long)(ap->overflow_arg_area + align - 1) & -align);
-        return ap->overflow_arg_area - size;
-
-    default: /* should never happen */
-        abort();
-    }
-}
-#endif
diff --git a/tinyc/libtcc.c b/tinyc/libtcc.c
deleted file mode 100644
index 092c0ba32..000000000
--- a/tinyc/libtcc.c
+++ /dev/null
@@ -1,1982 +0,0 @@
-/*
- *  TCC - Tiny C Compiler
- *
- *  Copyright (c) 2001-2004 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "tcc.h"
-
-/********************************************************/
-/* global variables */
-
-/* use GNU C extensions */
-ST_DATA int gnu_ext = 1;
-
-/* use TinyCC extensions */
-ST_DATA int tcc_ext = 1;
-
-/* XXX: get rid of this ASAP */
-ST_DATA struct TCCState *tcc_state;
-
-static int nb_states;
-
-/********************************************************/
-
-#if ONE_SOURCE
-#include "tccpp.c"
-#include "tccgen.c"
-#include "tccelf.c"
-#include "tccrun.c"
-#ifdef TCC_TARGET_I386
-#include "i386-gen.c"
-#include "i386-link.c"
-#include "i386-asm.c"
-#endif
-#ifdef TCC_TARGET_ARM
-#include "arm-gen.c"
-#include "arm-link.c"
-#include "arm-asm.c"
-#endif
-#ifdef TCC_TARGET_ARM64
-#include "arm64-gen.c"
-#include "arm64-link.c"
-#endif
-#ifdef TCC_TARGET_C67
-#include "c67-gen.c"
-#include "c67-link.c"
-#include "tcccoff.c"
-#endif
-#ifdef TCC_TARGET_X86_64
-#include "x86_64-gen.c"
-#include "x86_64-link.c"
-#include "i386-asm.c"
-#endif
-#ifdef CONFIG_TCC_ASM
-#include "tccasm.c"
-#endif
-#ifdef TCC_TARGET_PE
-#include "tccpe.c"
-#endif
-#endif /* ONE_SOURCE */
-
-/********************************************************/
-#ifndef CONFIG_TCC_ASM
-ST_FUNC void asm_instr(void)
-{
-    tcc_error("inline asm() not supported");
-}
-ST_FUNC void asm_global_instr(void)
-{
-    tcc_error("inline asm() not supported");
-}
-#endif
-
-/********************************************************/
-#ifdef _WIN32
-ST_FUNC char *normalize_slashes(char *path)
-{
-    char *p;
-    for (p = path; *p; ++p)
-        if (*p == '\\')
-            *p = '/';
-    return path;
-}
-
-static HMODULE tcc_module;
-
-/* on win32, we suppose the lib and includes are at the location of 'tcc.exe' */
-static void tcc_set_lib_path_w32(TCCState *s)
-{
-    char path[1024], *p;
-    GetModuleFileNameA(tcc_module, path, sizeof path);
-    p = tcc_basename(normalize_slashes(strlwr(path)));
-    if (p > path)
-        --p;
-    *p = 0;
-    tcc_set_lib_path(s, path);
-}
-
-#ifdef TCC_TARGET_PE
-static void tcc_add_systemdir(TCCState *s)
-{
-    char buf[1000];
-    GetSystemDirectory(buf, sizeof buf);
-    tcc_add_library_path(s, normalize_slashes(buf));
-}
-#endif
-
-#ifdef LIBTCC_AS_DLL
-BOOL WINAPI DllMain (HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved)
-{
-    if (DLL_PROCESS_ATTACH == dwReason)
-        tcc_module = hDll;
-    return TRUE;
-}
-#endif
-#endif
-
-/********************************************************/
-/* copy a string and truncate it. */
-ST_FUNC char *pstrcpy(char *buf, int buf_size, const char *s)
-{
-    char *q, *q_end;
-    int c;
-
-    if (buf_size > 0) {
-        q = buf;
-        q_end = buf + buf_size - 1;
-        while (q < q_end) {
-            c = *s++;
-            if (c == '\0')
-                break;
-            *q++ = c;
-        }
-        *q = '\0';
-    }
-    return buf;
-}
-
-/* strcat and truncate. */
-ST_FUNC char *pstrcat(char *buf, int buf_size, const char *s)
-{
-    int len;
-    len = strlen(buf);
-    if (len < buf_size)
-        pstrcpy(buf + len, buf_size - len, s);
-    return buf;
-}
-
-ST_FUNC char *pstrncpy(char *out, const char *in, size_t num)
-{
-    memcpy(out, in, num);
-    out[num] = '\0';
-    return out;
-}
-
-/* extract the basename of a file */
-PUB_FUNC char *tcc_basename(const char *name)
-{
-    char *p = strchr(name, 0);
-    while (p > name && !IS_DIRSEP(p[-1]))
-        --p;
-    return p;
-}
-
-/* extract extension part of a file
- *
- * (if no extension, return pointer to end-of-string)
- */
-PUB_FUNC char *tcc_fileextension (const char *name)
-{
-    char *b = tcc_basename(name);
-    char *e = strrchr(b, '.');
-    return e ? e : strchr(b, 0);
-}
-
-/********************************************************/
-/* memory management */
-
-#undef free
-#undef malloc
-#undef realloc
-
-#ifndef MEM_DEBUG
-
-PUB_FUNC void tcc_free(void *ptr)
-{
-    free(ptr);
-}
-
-PUB_FUNC void *tcc_malloc(unsigned long size)
-{
-    void *ptr;
-    ptr = malloc(size);
-    if (!ptr && size)
-        tcc_error("memory full (malloc)");
-    return ptr;
-}
-
-PUB_FUNC void *tcc_mallocz(unsigned long size)
-{
-    void *ptr;
-    ptr = tcc_malloc(size);
-    memset(ptr, 0, size);
-    return ptr;
-}
-
-PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size)
-{
-    void *ptr1;
-    ptr1 = realloc(ptr, size);
-    if (!ptr1 && size)
-        tcc_error("memory full (realloc)");
-    return ptr1;
-}
-
-PUB_FUNC char *tcc_strdup(const char *str)
-{
-    char *ptr;
-    ptr = tcc_malloc(strlen(str) + 1);
-    strcpy(ptr, str);
-    return ptr;
-}
-
-PUB_FUNC void tcc_memcheck(void)
-{
-}
-
-#else
-
-#define MEM_DEBUG_MAGIC1 0xFEEDDEB1
-#define MEM_DEBUG_MAGIC2 0xFEEDDEB2
-#define MEM_DEBUG_MAGIC3 0xFEEDDEB3
-#define MEM_DEBUG_FILE_LEN 40
-#define MEM_DEBUG_CHECK3(header) \
-    ((mem_debug_header_t*)((char*)header + header->size))->magic3
-#define MEM_USER_PTR(header) \
-    ((char *)header + offsetof(mem_debug_header_t, magic3))
-#define MEM_HEADER_PTR(ptr) \
-    (mem_debug_header_t *)((char*)ptr - offsetof(mem_debug_header_t, magic3))
-
-struct mem_debug_header {
-    unsigned magic1;
-    unsigned size;
-    struct mem_debug_header *prev;
-    struct mem_debug_header *next;
-    int line_num;
-    char file_name[MEM_DEBUG_FILE_LEN + 1];
-    unsigned magic2;
-    ALIGNED(16) unsigned magic3;
-};
-
-typedef struct mem_debug_header mem_debug_header_t;
-
-static mem_debug_header_t *mem_debug_chain;
-static unsigned mem_cur_size;
-static unsigned mem_max_size;
-
-static mem_debug_header_t *malloc_check(void *ptr, const char *msg)
-{
-    mem_debug_header_t * header = MEM_HEADER_PTR(ptr);
-    if (header->magic1 != MEM_DEBUG_MAGIC1 ||
-        header->magic2 != MEM_DEBUG_MAGIC2 ||
-        MEM_DEBUG_CHECK3(header) != MEM_DEBUG_MAGIC3 ||
-        header->size == (unsigned)-1) {
-        fprintf(stderr, "%s check failed\n", msg);
-        if (header->magic1 == MEM_DEBUG_MAGIC1)
-            fprintf(stderr, "%s:%u: block allocated here.\n",
-                header->file_name, header->line_num);
-        exit(1);
-    }
-    return header;
-}
-
-PUB_FUNC void *tcc_malloc_debug(unsigned long size, const char *file, int line)
-{
-    int ofs;
-    mem_debug_header_t *header;
-
-    header = malloc(sizeof(mem_debug_header_t) + size);
-    if (!header)
-        tcc_error("memory full (malloc)");
-
-    header->magic1 = MEM_DEBUG_MAGIC1;
-    header->magic2 = MEM_DEBUG_MAGIC2;
-    header->size = size;
-    MEM_DEBUG_CHECK3(header) = MEM_DEBUG_MAGIC3;
-    header->line_num = line;
-    ofs = strlen(file) - MEM_DEBUG_FILE_LEN;
-    strncpy(header->file_name, file + (ofs > 0 ? ofs : 0), MEM_DEBUG_FILE_LEN);
-    header->file_name[MEM_DEBUG_FILE_LEN] = 0;
-
-    header->next = mem_debug_chain;
-    header->prev = NULL;
-    if (header->next)
-        header->next->prev = header;
-    mem_debug_chain = header;
-
-    mem_cur_size += size;
-    if (mem_cur_size > mem_max_size)
-        mem_max_size = mem_cur_size;
-
-    return MEM_USER_PTR(header);
-}
-
-PUB_FUNC void tcc_free_debug(void *ptr)
-{
-    mem_debug_header_t *header;
-    if (!ptr)
-        return;
-    header = malloc_check(ptr, "tcc_free");
-    mem_cur_size -= header->size;
-    header->size = (unsigned)-1;
-    if (header->next)
-        header->next->prev = header->prev;
-    if (header->prev)
-        header->prev->next = header->next;
-    if (header == mem_debug_chain)
-        mem_debug_chain = header->next;
-    free(header);
-}
-
-PUB_FUNC void *tcc_mallocz_debug(unsigned long size, const char *file, int line)
-{
-    void *ptr;
-    ptr = tcc_malloc_debug(size,file,line);
-    memset(ptr, 0, size);
-    return ptr;
-}
-
-PUB_FUNC void *tcc_realloc_debug(void *ptr, unsigned long size, const char *file, int line)
-{
-    mem_debug_header_t *header;
-    int mem_debug_chain_update = 0;
-    if (!ptr)
-        return tcc_malloc_debug(size, file, line);
-    header = malloc_check(ptr, "tcc_realloc");
-    mem_cur_size -= header->size;
-    mem_debug_chain_update = (header == mem_debug_chain);
-    header = realloc(header, sizeof(mem_debug_header_t) + size);
-    if (!header)
-        tcc_error("memory full (realloc)");
-    header->size = size;
-    MEM_DEBUG_CHECK3(header) = MEM_DEBUG_MAGIC3;
-    if (header->next)
-        header->next->prev = header;
-    if (header->prev)
-        header->prev->next = header;
-    if (mem_debug_chain_update)
-        mem_debug_chain = header;
-    mem_cur_size += size;
-    if (mem_cur_size > mem_max_size)
-        mem_max_size = mem_cur_size;
-    return MEM_USER_PTR(header);
-}
-
-PUB_FUNC char *tcc_strdup_debug(const char *str, const char *file, int line)
-{
-    char *ptr;
-    ptr = tcc_malloc_debug(strlen(str) + 1, file, line);
-    strcpy(ptr, str);
-    return ptr;
-}
-
-PUB_FUNC void tcc_memcheck(void)
-{
-    if (mem_cur_size) {
-        mem_debug_header_t *header = mem_debug_chain;
-        fprintf(stderr, "MEM_DEBUG: mem_leak= %d bytes, mem_max_size= %d bytes\n",
-            mem_cur_size, mem_max_size);
-        while (header) {
-            fprintf(stderr, "%s:%u: error: %u bytes leaked\n",
-                header->file_name, header->line_num, header->size);
-            header = header->next;
-        }
-#if MEM_DEBUG-0 == 2
-        exit(2);
-#endif
-    }
-}
-#endif /* MEM_DEBUG */
-
-#define free(p) use_tcc_free(p)
-#define malloc(s) use_tcc_malloc(s)
-#define realloc(p, s) use_tcc_realloc(p, s)
-
-/********************************************************/
-/* dynarrays */
-
-ST_FUNC void dynarray_add(void *ptab, int *nb_ptr, void *data)
-{
-    int nb, nb_alloc;
-    void **pp;
-
-    nb = *nb_ptr;
-    pp = *(void ***)ptab;
-    /* every power of two we double array size */
-    if ((nb & (nb - 1)) == 0) {
-        if (!nb)
-            nb_alloc = 1;
-        else
-            nb_alloc = nb * 2;
-        pp = tcc_realloc(pp, nb_alloc * sizeof(void *));
-        *(void***)ptab = pp;
-    }
-    pp[nb++] = data;
-    *nb_ptr = nb;
-}
-
-ST_FUNC void dynarray_reset(void *pp, int *n)
-{
-    void **p;
-    for (p = *(void***)pp; *n; ++p, --*n)
-        if (*p)
-            tcc_free(*p);
-    tcc_free(*(void**)pp);
-    *(void**)pp = NULL;
-}
-
-static void tcc_split_path(TCCState *s, void *p_ary, int *p_nb_ary, const char *in)
-{
-    const char *p;
-    do {
-        int c;
-        CString str;
-
-        cstr_new(&str);
-        for (p = in; c = *p, c != '\0' && c != PATHSEP[0]; ++p) {
-            if (c == '{' && p[1] && p[2] == '}') {
-                c = p[1], p += 2;
-                if (c == 'B')
-                    cstr_cat(&str, s->tcc_lib_path, -1);
-            } else {
-                cstr_ccat(&str, c);
-            }
-        }
-        if (str.size) {
-            cstr_ccat(&str, '\0');
-            dynarray_add(p_ary, p_nb_ary, tcc_strdup(str.data));
-        }
-        cstr_free(&str);
-        in = p+1;
-    } while (*p);
-}
-
-/********************************************************/
-
-static void strcat_vprintf(char *buf, int buf_size, const char *fmt, va_list ap)
-{
-    int len;
-    len = strlen(buf);
-    vsnprintf(buf + len, buf_size - len, fmt, ap);
-}
-
-static void strcat_printf(char *buf, int buf_size, const char *fmt, ...)
-{
-    va_list ap;
-    va_start(ap, fmt);
-    strcat_vprintf(buf, buf_size, fmt, ap);
-    va_end(ap);
-}
-
-static void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap)
-{
-    char buf[2048];
-    BufferedFile **pf, *f;
-
-    buf[0] = '\0';
-    /* use upper file if inline ":asm:" or token ":paste:" */
-    for (f = file; f && f->filename[0] == ':'; f = f->prev)
-     ;
-    if (f) {
-        for(pf = s1->include_stack; pf < s1->include_stack_ptr; pf++)
-            strcat_printf(buf, sizeof(buf), "In file included from %s:%d:\n",
-                (*pf)->filename, (*pf)->line_num);
-        if (f->line_num > 0) {
-            strcat_printf(buf, sizeof(buf), "%s:%d: ",
-                f->filename, f->line_num - !!(tok_flags & TOK_FLAG_BOL));
-        } else {
-            strcat_printf(buf, sizeof(buf), "%s: ",
-                f->filename);
-        }
-    } else {
-        strcat_printf(buf, sizeof(buf), "tcc: ");
-    }
-    if (is_warning)
-        strcat_printf(buf, sizeof(buf), "warning: ");
-    else
-        strcat_printf(buf, sizeof(buf), "error: ");
-    strcat_vprintf(buf, sizeof(buf), fmt, ap);
-
-    if (!s1->error_func) {
-        /* default case: stderr */
-        if (s1->output_type == TCC_OUTPUT_PREPROCESS && s1->ppfp == stdout)
-            /* print a newline during tcc -E */
-            printf("\n"), fflush(stdout);
-        fflush(stdout); /* flush -v output */
-        fprintf(stderr, "%s\n", buf);
-        fflush(stderr); /* print error/warning now (win32) */
-    } else {
-        s1->error_func(s1->error_opaque, buf);
-    }
-    if (!is_warning || s1->warn_error)
-        s1->nb_errors++;
-}
-
-LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque,
-                        void (*error_func)(void *opaque, const char *msg))
-{
-    s->error_opaque = error_opaque;
-    s->error_func = error_func;
-}
-
-/* error without aborting current compilation */
-PUB_FUNC void tcc_error_noabort(const char *fmt, ...)
-{
-    TCCState *s1 = tcc_state;
-    va_list ap;
-
-    va_start(ap, fmt);
-    error1(s1, 0, fmt, ap);
-    va_end(ap);
-}
-
-PUB_FUNC void tcc_error(const char *fmt, ...)
-{
-    TCCState *s1 = tcc_state;
-    va_list ap;
-
-    va_start(ap, fmt);
-    error1(s1, 0, fmt, ap);
-    va_end(ap);
-    /* better than nothing: in some cases, we accept to handle errors */
-    if (s1->error_set_jmp_enabled) {
-        longjmp(s1->error_jmp_buf, 1);
-    } else {
-        /* XXX: eliminate this someday */
-        exit(1);
-    }
-}
-
-PUB_FUNC void tcc_warning(const char *fmt, ...)
-{
-    TCCState *s1 = tcc_state;
-    va_list ap;
-
-    if (s1->warn_none)
-        return;
-
-    va_start(ap, fmt);
-    error1(s1, 1, fmt, ap);
-    va_end(ap);
-}
-
-/********************************************************/
-/* I/O layer */
-
-ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen)
-{
-    BufferedFile *bf;
-    int buflen = initlen ? initlen : IO_BUF_SIZE;
-
-    bf = tcc_mallocz(sizeof(BufferedFile) + buflen);
-    bf->buf_ptr = bf->buffer;
-    bf->buf_end = bf->buffer + initlen;
-    bf->buf_end[0] = CH_EOB; /* put eob symbol */
-    pstrcpy(bf->filename, sizeof(bf->filename), filename);
-    bf->true_filename = bf->filename;
-    bf->line_num = 1;
-    bf->ifdef_stack_ptr = s1->ifdef_stack_ptr;
-    bf->fd = -1;
-    bf->prev = file;
-    file = bf;
-    tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
-}
-
-ST_FUNC void tcc_close(void)
-{
-    BufferedFile *bf = file;
-    if (bf->fd > 0) {
-        close(bf->fd);
-        total_lines += bf->line_num;
-    }
-    if (bf->true_filename != bf->filename)
-        tcc_free(bf->true_filename);
-    file = bf->prev;
-    tcc_free(bf);
-}
-
-ST_FUNC int tcc_open(TCCState *s1, const char *filename)
-{
-    int fd;
-    if (strcmp(filename, "-") == 0)
-        fd = 0, filename = "<stdin>";
-    else
-        fd = open(filename, O_RDONLY | O_BINARY);
-    if ((s1->verbose == 2 && fd >= 0) || s1->verbose == 3)
-        printf("%s %*s%s\n", fd < 0 ? "nf":"->",
-               (int)(s1->include_stack_ptr - s1->include_stack), "", filename);
-    if (fd < 0)
-        return -1;
-    tcc_open_bf(s1, filename, 0);
-#ifdef _WIN32
-    normalize_slashes(file->filename);
-#endif
-    file->fd = fd;
-    return fd;
-}
-
-/* compile the file opened in 'file'. Return non zero if errors. */
-static int tcc_compile(TCCState *s1)
-{
-    Sym *define_start;
-    int filetype, is_asm;
-
-    define_start = define_stack;
-    filetype = s1->filetype;
-    is_asm = filetype == AFF_TYPE_ASM || filetype == AFF_TYPE_ASMPP;
-
-    if (setjmp(s1->error_jmp_buf) == 0) {
-        s1->nb_errors = 0;
-        s1->error_set_jmp_enabled = 1;
-
-        preprocess_start(s1, is_asm);
-        if (s1->output_type == TCC_OUTPUT_PREPROCESS) {
-            tcc_preprocess(s1);
-        } else if (is_asm) {
-#ifdef CONFIG_TCC_ASM
-            tcc_assemble(s1, filetype == AFF_TYPE_ASMPP);
-#else
-            tcc_error_noabort("asm not supported");
-#endif
-        } else {
-            tccgen_compile(s1);
-        }
-    }
-    s1->error_set_jmp_enabled = 0;
-
-    preprocess_end(s1);
-    free_inline_functions(s1);
-    /* reset define stack, but keep -D and built-ins */
-    free_defines(define_start);
-    sym_pop(&global_stack, NULL, 0);
-    sym_pop(&local_stack, NULL, 0);
-    return s1->nb_errors != 0 ? -1 : 0;
-}
-
-LIBTCCAPI int tcc_compile_string(TCCState *s, const char *str)
-{
-    int len, ret;
-
-    len = strlen(str);
-    tcc_open_bf(s, "<string>", len);
-    memcpy(file->buffer, str, len);
-    ret = tcc_compile(s);
-    tcc_close();
-    return ret;
-}
-
-/* define a preprocessor symbol. A value can also be provided with the '=' operator */
-LIBTCCAPI void tcc_define_symbol(TCCState *s1, const char *sym, const char *value)
-{
-    int len1, len2;
-    /* default value */
-    if (!value)
-        value = "1";
-    len1 = strlen(sym);
-    len2 = strlen(value);
-
-    /* init file structure */
-    tcc_open_bf(s1, "<define>", len1 + len2 + 1);
-    memcpy(file->buffer, sym, len1);
-    file->buffer[len1] = ' ';
-    memcpy(file->buffer + len1 + 1, value, len2);
-
-    /* parse with define parser */
-    next_nomacro();
-    parse_define();
-    tcc_close();
-}
-
-/* undefine a preprocessor symbol */
-LIBTCCAPI void tcc_undefine_symbol(TCCState *s1, const char *sym)
-{
-    TokenSym *ts;
-    Sym *s;
-    ts = tok_alloc(sym, strlen(sym));
-    s = define_find(ts->tok);
-    /* undefine symbol by putting an invalid name */
-    if (s)
-        define_undef(s);
-}
-
-/* cleanup all static data used during compilation */
-static void tcc_cleanup(void)
-{
-    if (NULL == tcc_state)
-        return;
-    while (file)
-        tcc_close();
-    tccpp_delete(tcc_state);
-    tcc_state = NULL;
-    /* free sym_pools */
-    dynarray_reset(&sym_pools, &nb_sym_pools);
-    /* reset symbol stack */
-    sym_free_first = NULL;
-}
-
-LIBTCCAPI TCCState *tcc_new(void)
-{
-    TCCState *s;
-
-    tcc_cleanup();
-
-    s = tcc_mallocz(sizeof(TCCState));
-    if (!s)
-        return NULL;
-    tcc_state = s;
-    ++nb_states;
-
-    s->alacarte_link = 1;
-    s->nocommon = 1;
-    s->warn_implicit_function_declaration = 1;
-    s->ms_extensions = 1;
-
-#ifdef CHAR_IS_UNSIGNED
-    s->char_is_unsigned = 1;
-#endif
-#ifdef TCC_TARGET_I386
-    s->seg_size = 32;
-#endif
-    /* enable this if you want symbols with leading underscore on windows: */
-#if 0 /* def TCC_TARGET_PE */
-    s->leading_underscore = 1;
-#endif
-#ifdef _WIN32
-    tcc_set_lib_path_w32(s);
-#else
-    tcc_set_lib_path(s, CONFIG_TCCDIR);
-#endif
-    tccelf_new(s);
-    tccpp_new(s);
-
-    /* we add dummy defines for some special macros to speed up tests
-       and to have working defined() */
-    define_push(TOK___LINE__, MACRO_OBJ, NULL, NULL);
-    define_push(TOK___FILE__, MACRO_OBJ, NULL, NULL);
-    define_push(TOK___DATE__, MACRO_OBJ, NULL, NULL);
-    define_push(TOK___TIME__, MACRO_OBJ, NULL, NULL);
-    define_push(TOK___COUNTER__, MACRO_OBJ, NULL, NULL);
-    {
-        /* define __TINYC__ 92X  */
-        char buffer[32]; int a,b,c;
-        sscanf(TCC_VERSION, "%d.%d.%d", &a, &b, &c);
-        sprintf(buffer, "%d", a*10000 + b*100 + c);
-        tcc_define_symbol(s, "__TINYC__", buffer);
-    }
-
-    /* standard defines */
-    tcc_define_symbol(s, "__STDC__", NULL);
-    tcc_define_symbol(s, "__STDC_VERSION__", "199901L");
-    tcc_define_symbol(s, "__STDC_HOSTED__", NULL);
-
-    /* target defines */
-#if defined(TCC_TARGET_I386)
-    tcc_define_symbol(s, "__i386__", NULL);
-    tcc_define_symbol(s, "__i386", NULL);
-    tcc_define_symbol(s, "i386", NULL);
-#elif defined(TCC_TARGET_X86_64)
-    tcc_define_symbol(s, "__x86_64__", NULL);
-#elif defined(TCC_TARGET_ARM)
-    tcc_define_symbol(s, "__ARM_ARCH_4__", NULL);
-    tcc_define_symbol(s, "__arm_elf__", NULL);
-    tcc_define_symbol(s, "__arm_elf", NULL);
-    tcc_define_symbol(s, "arm_elf", NULL);
-    tcc_define_symbol(s, "__arm__", NULL);
-    tcc_define_symbol(s, "__arm", NULL);
-    tcc_define_symbol(s, "arm", NULL);
-    tcc_define_symbol(s, "__APCS_32__", NULL);
-    tcc_define_symbol(s, "__ARMEL__", NULL);
-#if defined(TCC_ARM_EABI)
-    tcc_define_symbol(s, "__ARM_EABI__", NULL);
-#endif
-#if defined(TCC_ARM_HARDFLOAT)
-    s->float_abi = ARM_HARD_FLOAT;
-    tcc_define_symbol(s, "__ARM_PCS_VFP", NULL);
-#else
-    s->float_abi = ARM_SOFTFP_FLOAT;
-#endif
-#elif defined(TCC_TARGET_ARM64)
-    tcc_define_symbol(s, "__aarch64__", NULL);
-#elif defined TCC_TARGET_C67
-    tcc_define_symbol(s, "__C67__", NULL);
-#endif
-
-#ifdef TCC_TARGET_PE
-    tcc_define_symbol(s, "_WIN32", NULL);
-# ifdef TCC_TARGET_X86_64
-    tcc_define_symbol(s, "_WIN64", NULL);
-# endif
-#else
-    tcc_define_symbol(s, "__unix__", NULL);
-    tcc_define_symbol(s, "__unix", NULL);
-    tcc_define_symbol(s, "unix", NULL);
-# if defined(__linux__)
-    tcc_define_symbol(s, "__linux__", NULL);
-    tcc_define_symbol(s, "__linux", NULL);
-# endif
-# if defined(__FreeBSD__)
-    tcc_define_symbol(s, "__FreeBSD__", "__FreeBSD__");
-    /* No 'Thread Storage Local' on FreeBSD with tcc */
-    tcc_define_symbol(s, "__NO_TLS", NULL);
-# endif
-# if defined(__FreeBSD_kernel__)
-    tcc_define_symbol(s, "__FreeBSD_kernel__", NULL);
-# endif
-# if defined(__NetBSD__)
-    tcc_define_symbol(s, "__NetBSD__", "__NetBSD__");
-# endif
-# if defined(__OpenBSD__)
-    tcc_define_symbol(s, "__OpenBSD__", "__OpenBSD__");
-# endif
-#endif
-
-    /* TinyCC & gcc defines */
-#if PTR_SIZE == 4
-    /* 32bit systems. */
-    tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned int");
-    tcc_define_symbol(s, "__PTRDIFF_TYPE__", "int");
-    tcc_define_symbol(s, "__ILP32__", NULL);
-#elif LONG_SIZE == 4
-    /* 64bit Windows. */
-    tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long long");
-    tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long long");
-    tcc_define_symbol(s, "__LLP64__", NULL);
-#else
-    /* Other 64bit systems. */
-    tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long");
-    tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long");
-    tcc_define_symbol(s, "__LP64__", NULL);
-#endif
-
-#ifdef TCC_TARGET_PE
-    tcc_define_symbol(s, "__WCHAR_TYPE__", "unsigned short");
-    tcc_define_symbol(s, "__WINT_TYPE__", "unsigned short");
-#else
-    tcc_define_symbol(s, "__WCHAR_TYPE__", "int");
-    /* wint_t is unsigned int by default, but (signed) int on BSDs
-       and unsigned short on windows.  Other OSes might have still
-       other conventions, sigh.  */
-# if defined(__FreeBSD__) || defined (__FreeBSD_kernel__) \
-  || defined(__NetBSD__) || defined(__OpenBSD__)
-    tcc_define_symbol(s, "__WINT_TYPE__", "int");
-#  ifdef __FreeBSD__
-    /* define __GNUC__ to have some useful stuff from sys/cdefs.h
-       that are unconditionally used in FreeBSDs other system headers :/ */
-    tcc_define_symbol(s, "__GNUC__", "2");
-    tcc_define_symbol(s, "__GNUC_MINOR__", "7");
-    tcc_define_symbol(s, "__builtin_alloca", "alloca");
-#  endif
-# else
-    tcc_define_symbol(s, "__WINT_TYPE__", "unsigned int");
-    /* glibc defines */
-    tcc_define_symbol(s, "__REDIRECT(name, proto, alias)",
-        "name proto __asm__ (#alias)");
-    tcc_define_symbol(s, "__REDIRECT_NTH(name, proto, alias)",
-        "name proto __asm__ (#alias) __THROW");
-# endif
-# if defined(TCC_MUSL)
-    tcc_define_symbol(s, "__DEFINED_va_list", "");
-    tcc_define_symbol(s, "__DEFINED___isoc_va_list", "");
-    tcc_define_symbol(s, "__isoc_va_list", "void *");
-# endif /* TCC_MUSL */
-    /* Some GCC builtins that are simple to express as macros.  */
-    tcc_define_symbol(s, "__builtin_extract_return_addr(x)", "x");
-#endif /* ndef TCC_TARGET_PE */
-    return s;
-}
-
-LIBTCCAPI void tcc_delete(TCCState *s1)
-{
-    tcc_cleanup();
-
-    /* free sections */
-    tccelf_delete(s1);
-
-    /* free library paths */
-    dynarray_reset(&s1->library_paths, &s1->nb_library_paths);
-    dynarray_reset(&s1->crt_paths, &s1->nb_crt_paths);
-
-    /* free include paths */
-    dynarray_reset(&s1->cached_includes, &s1->nb_cached_includes);
-    dynarray_reset(&s1->include_paths, &s1->nb_include_paths);
-    dynarray_reset(&s1->sysinclude_paths, &s1->nb_sysinclude_paths);
-    dynarray_reset(&s1->cmd_include_files, &s1->nb_cmd_include_files);
-
-    tcc_free(s1->tcc_lib_path);
-    tcc_free(s1->soname);
-    tcc_free(s1->rpath);
-    tcc_free(s1->init_symbol);
-    tcc_free(s1->fini_symbol);
-    tcc_free(s1->outfile);
-    tcc_free(s1->deps_outfile);
-    dynarray_reset(&s1->files, &s1->nb_files);
-    dynarray_reset(&s1->target_deps, &s1->nb_target_deps);
-    dynarray_reset(&s1->pragma_libs, &s1->nb_pragma_libs);
-    dynarray_reset(&s1->argv, &s1->argc);
-
-#ifdef TCC_IS_NATIVE
-    /* free runtime memory */
-    tcc_run_free(s1);
-#endif
-
-    tcc_free(s1);
-    if (0 == --nb_states)
-        tcc_memcheck();
-}
-
-LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type)
-{
-    s->output_type = output_type;
-
-    /* always elf for objects */
-    if (output_type == TCC_OUTPUT_OBJ)
-        s->output_format = TCC_OUTPUT_FORMAT_ELF;
-
-    if (s->char_is_unsigned)
-        tcc_define_symbol(s, "__CHAR_UNSIGNED__", NULL);
-
-    if (!s->nostdinc) {
-        /* default include paths */
-        /* -isystem paths have already been handled */
-        tcc_add_sysinclude_path(s, CONFIG_TCC_SYSINCLUDEPATHS);
-    }
-
-#ifdef CONFIG_TCC_BCHECK
-    if (s->do_bounds_check) {
-        /* if bound checking, then add corresponding sections */
-        tccelf_bounds_new(s);
-        /* define symbol */
-        tcc_define_symbol(s, "__BOUNDS_CHECKING_ON", NULL);
-    }
-#endif
-    if (s->do_debug) {
-        /* add debug sections */
-        tccelf_stab_new(s);
-    }
-
-    tcc_add_library_path(s, CONFIG_TCC_LIBPATHS);
-
-#ifdef TCC_TARGET_PE
-# ifdef _WIN32
-    if (!s->nostdlib && output_type != TCC_OUTPUT_OBJ)
-        tcc_add_systemdir(s);
-# endif
-#else
-    /* paths for crt objects */
-    tcc_split_path(s, &s->crt_paths, &s->nb_crt_paths, CONFIG_TCC_CRTPREFIX);
-    /* add libc crt1/crti objects */
-    if ((output_type == TCC_OUTPUT_EXE || output_type == TCC_OUTPUT_DLL) &&
-        !s->nostdlib) {
-        if (output_type != TCC_OUTPUT_DLL)
-            tcc_add_crt(s, "crt1.o");
-        tcc_add_crt(s, "crti.o");
-    }
-#endif
-    return 0;
-}
-
-LIBTCCAPI int tcc_add_include_path(TCCState *s, const char *pathname)
-{
-    tcc_split_path(s, &s->include_paths, &s->nb_include_paths, pathname);
-    return 0;
-}
-
-LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s, const char *pathname)
-{
-    tcc_split_path(s, &s->sysinclude_paths, &s->nb_sysinclude_paths, pathname);
-    return 0;
-}
-
-ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags)
-{
-    int ret;
-
-    /* open the file */
-    ret = tcc_open(s1, filename);
-    if (ret < 0) {
-        if (flags & AFF_PRINT_ERROR)
-            tcc_error_noabort("file '%s' not found", filename);
-        return ret;
-    }
-
-    /* update target deps */
-    dynarray_add(&s1->target_deps, &s1->nb_target_deps,
-            tcc_strdup(filename));
-
-    if (flags & AFF_TYPE_BIN) {
-        ElfW(Ehdr) ehdr;
-        int fd, obj_type;
-
-        fd = file->fd;
-        obj_type = tcc_object_type(fd, &ehdr);
-        lseek(fd, 0, SEEK_SET);
-
-        /* do not display line number if error */
-        file->line_num = 0;
-
-#ifdef TCC_TARGET_MACHO
-        if (0 == obj_type && 0 == strcmp(tcc_fileextension(filename), ".dylib"))
-            obj_type = AFF_BINTYPE_DYN;
-#endif
-
-        switch (obj_type) {
-        case AFF_BINTYPE_REL:
-            ret = tcc_load_object_file(s1, fd, 0);
-            break;
-#ifndef TCC_TARGET_PE
-        case AFF_BINTYPE_DYN:
-            if (s1->output_type == TCC_OUTPUT_MEMORY) {
-                ret = 0;
-#ifdef TCC_IS_NATIVE
-                if (NULL == dlopen(filename, RTLD_GLOBAL | RTLD_LAZY))
-                    ret = -1;
-#endif
-            } else {
-                ret = tcc_load_dll(s1, fd, filename,
-                                   (flags & AFF_REFERENCED_DLL) != 0);
-            }
-            break;
-#endif
-        case AFF_BINTYPE_AR:
-            ret = tcc_load_archive(s1, fd);
-            break;
-#ifdef TCC_TARGET_COFF
-        case AFF_BINTYPE_C67:
-            ret = tcc_load_coff(s1, fd);
-            break;
-#endif
-        default:
-#ifdef TCC_TARGET_PE
-            ret = pe_load_file(s1, filename, fd);
-#else
-            /* as GNU ld, consider it is an ld script if not recognized */
-            ret = tcc_load_ldscript(s1);
-#endif
-            if (ret < 0)
-                tcc_error_noabort("unrecognized file type");
-            break;
-        }
-    } else {
-        ret = tcc_compile(s1);
-    }
-    tcc_close();
-    return ret;
-}
-
-LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename)
-{
-    int filetype = s->filetype;
-    int flags = AFF_PRINT_ERROR;
-    if (filetype == 0) {
-        /* use a file extension to detect a filetype */
-        const char *ext = tcc_fileextension(filename);
-        if (ext[0]) {
-            ext++;
-            if (!strcmp(ext, "S"))
-                filetype = AFF_TYPE_ASMPP;
-            else if (!strcmp(ext, "s"))
-                filetype = AFF_TYPE_ASM;
-            else if (!PATHCMP(ext, "c") || !PATHCMP(ext, "i"))
-                filetype = AFF_TYPE_C;
-            else
-                flags |= AFF_TYPE_BIN;
-        } else {
-            filetype = AFF_TYPE_C;
-        }
-        s->filetype = filetype;
-    }
-    return tcc_add_file_internal(s, filename, flags);
-}
-
-LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname)
-{
-    tcc_split_path(s, &s->library_paths, &s->nb_library_paths, pathname);
-    return 0;
-}
-
-static int tcc_add_library_internal(TCCState *s, const char *fmt,
-    const char *filename, int flags, char **paths, int nb_paths)
-{
-    char buf[1024];
-    int i;
-
-    for(i = 0; i < nb_paths; i++) {
-        snprintf(buf, sizeof(buf), fmt, paths[i], filename);
-        if (tcc_add_file_internal(s, buf, flags | AFF_TYPE_BIN) == 0)
-            return 0;
-    }
-    return -1;
-}
-
-/* find and load a dll. Return non zero if not found */
-/* XXX: add '-rpath' option support ? */
-ST_FUNC int tcc_add_dll(TCCState *s, const char *filename, int flags)
-{
-    return tcc_add_library_internal(s, "%s/%s", filename, flags,
-        s->library_paths, s->nb_library_paths);
-}
-
-ST_FUNC int tcc_add_crt(TCCState *s, const char *filename)
-{
-    if (-1 == tcc_add_library_internal(s, "%s/%s",
-        filename, 0, s->crt_paths, s->nb_crt_paths))
-        tcc_error_noabort("file '%s' not found", filename);
-    return 0;
-}
-
-/* the library name is the same as the argument of the '-l' option */
-LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname)
-{
-#if defined TCC_TARGET_PE
-    const char *libs[] = { "%s/%s.def", "%s/lib%s.def", "%s/%s.dll", "%s/lib%s.dll", "%s/lib%s.a", NULL };
-    const char **pp = s->static_link ? libs + 4 : libs;
-#elif defined TCC_TARGET_MACHO
-    const char *libs[] = { "%s/lib%s.dylib", "%s/lib%s.a", NULL };
-    const char **pp = s->static_link ? libs + 1 : libs;
-#else
-    const char *libs[] = { "%s/lib%s.so", "%s/lib%s.a", NULL };
-    const char **pp = s->static_link ? libs + 1 : libs;
-#endif
-    while (*pp) {
-        if (0 == tcc_add_library_internal(s, *pp,
-            libraryname, 0, s->library_paths, s->nb_library_paths))
-            return 0;
-        ++pp;
-    }
-    return -1;
-}
-
-PUB_FUNC int tcc_add_library_err(TCCState *s, const char *libname)
-{
-    int ret = tcc_add_library(s, libname);
-    if (ret < 0)
-        tcc_error_noabort("library '%s' not found", libname);
-    return ret;
-}
-
-/* handle #pragma comment(lib,) */
-ST_FUNC void tcc_add_pragma_libs(TCCState *s1)
-{
-    int i;
-    for (i = 0; i < s1->nb_pragma_libs; i++)
-        tcc_add_library_err(s1, s1->pragma_libs[i]);
-}
-
-LIBTCCAPI int tcc_add_symbol(TCCState *s, const char *name, const void *val)
-{
-#ifdef TCC_TARGET_PE
-    /* On x86_64 'val' might not be reachable with a 32bit offset.
-       So it is handled here as if it were in a DLL. */
-    pe_putimport(s, 0, name, (uintptr_t)val);
-#else
-    set_elf_sym(symtab_section, (uintptr_t)val, 0,
-        ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
-        SHN_ABS, name);
-#endif
-    return 0;
-}
-
-LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path)
-{
-    tcc_free(s->tcc_lib_path);
-    s->tcc_lib_path = tcc_strdup(path);
-}
-
-#define WD_ALL    0x0001 /* warning is activated when using -Wall */
-#define FD_INVERT 0x0002 /* invert value before storing */
-
-typedef struct FlagDef {
-    uint16_t offset;
-    uint16_t flags;
-    const char *name;
-} FlagDef;
-
-static int no_flag(const char **pp)
-{
-    const char *p = *pp;
-    if (*p != 'n' || *++p != 'o' || *++p != '-')
-        return 0;
-    *pp = p + 1;
-    return 1;
-}
-
-ST_FUNC int set_flag(TCCState *s, const FlagDef *flags, const char *name)
-{
-    int value, ret;
-    const FlagDef *p;
-    const char *r;
-
-    value = 1;
-    r = name;
-    if (no_flag(&r))
-        value = 0;
-
-    for (ret = -1, p = flags; p->name; ++p) {
-        if (ret) {
-            if (strcmp(r, p->name))
-                continue;
-        } else {
-            if (0 == (p->flags & WD_ALL))
-                continue;
-        }
-        if (p->offset) {
-            *(int*)((char *)s + p->offset) =
-                p->flags & FD_INVERT ? !value : value;
-            if (ret)
-                return 0;
-        } else {
-            ret = 0;
-        }
-    }
-    return ret;
-}
-
-static int strstart(const char *val, const char **str)
-{
-    const char *p, *q;
-    p = *str;
-    q = val;
-    while (*q) {
-        if (*p != *q)
-            return 0;
-        p++;
-        q++;
-    }
-    *str = p;
-    return 1;
-}
-
-/* Like strstart, but automatically takes into account that ld options can
- *
- * - start with double or single dash (e.g. '--soname' or '-soname')
- * - arguments can be given as separate or after '=' (e.g. '-Wl,-soname,x.so'
- *   or '-Wl,-soname=x.so')
- *
- * you provide `val` always in 'option[=]' form (no leading -)
- */
-static int link_option(const char *str, const char *val, const char **ptr)
-{
-    const char *p, *q;
-    int ret;
-
-    /* there should be 1 or 2 dashes */
-    if (*str++ != '-')
-        return 0;
-    if (*str == '-')
-        str++;
-
-    /* then str & val should match (potentially up to '=') */
-    p = str;
-    q = val;
-
-    ret = 1;
-    if (q[0] == '?') {
-        ++q;
-        if (no_flag(&p))
-            ret = -1;
-    }
-
-    while (*q != '\0' && *q != '=') {
-        if (*p != *q)
-            return 0;
-        p++;
-        q++;
-    }
-
-    /* '=' near eos means ',' or '=' is ok */
-    if (*q == '=') {
-        if (*p == 0)
-            *ptr = p;
-        if (*p != ',' && *p != '=')
-            return 0;
-        p++;
-    } else if (*p) {
-        return 0;
-    }
-    *ptr = p;
-    return ret;
-}
-
-static const char *skip_linker_arg(const char **str)
-{
-    const char *s1 = *str;
-    const char *s2 = strchr(s1, ',');
-    *str = s2 ? s2++ : (s2 = s1 + strlen(s1));
-    return s2;
-}
-
-static void copy_linker_arg(char **pp, const char *s, int sep)
-{
-    const char *q = s;
-    char *p = *pp;
-    int l = 0;
-    if (p && sep)
-        p[l = strlen(p)] = sep, ++l;
-    skip_linker_arg(&q);
-    pstrncpy(l + (*pp = tcc_realloc(p, q - s + l + 1)), s, q - s);
-}
-
-/* set linker options */
-static int tcc_set_linker(TCCState *s, const char *option)
-{
-    while (*option) {
-
-        const char *p = NULL;
-        char *end = NULL;
-        int ignoring = 0;
-        int ret;
-
-        if (link_option(option, "Bsymbolic", &p)) {
-            s->symbolic = 1;
-        } else if (link_option(option, "nostdlib", &p)) {
-            s->nostdlib = 1;
-        } else if (link_option(option, "fini=", &p)) {
-            copy_linker_arg(&s->fini_symbol, p, 0);
-            ignoring = 1;
-        } else if (link_option(option, "image-base=", &p)
-                || link_option(option, "Ttext=", &p)) {
-            s->text_addr = strtoull(p, &end, 16);
-            s->has_text_addr = 1;
-        } else if (link_option(option, "init=", &p)) {
-            copy_linker_arg(&s->init_symbol, p, 0);
-            ignoring = 1;
-        } else if (link_option(option, "oformat=", &p)) {
-#if defined(TCC_TARGET_PE)
-            if (strstart("pe-", &p)) {
-#elif PTR_SIZE == 8
-            if (strstart("elf64-", &p)) {
-#else
-            if (strstart("elf32-", &p)) {
-#endif
-                s->output_format = TCC_OUTPUT_FORMAT_ELF;
-            } else if (!strcmp(p, "binary")) {
-                s->output_format = TCC_OUTPUT_FORMAT_BINARY;
-#ifdef TCC_TARGET_COFF
-            } else if (!strcmp(p, "coff")) {
-                s->output_format = TCC_OUTPUT_FORMAT_COFF;
-#endif
-            } else
-                goto err;
-
-        } else if (link_option(option, "as-needed", &p)) {
-            ignoring = 1;
-        } else if (link_option(option, "O", &p)) {
-            ignoring = 1;
-        } else if (link_option(option, "export-all-symbols", &p)) {
-            s->rdynamic = 1;
-        } else if (link_option(option, "rpath=", &p)) {
-            copy_linker_arg(&s->rpath, p, ':');
-        } else if (link_option(option, "enable-new-dtags", &p)) {
-            s->enable_new_dtags = 1;
-        } else if (link_option(option, "section-alignment=", &p)) {
-            s->section_align = strtoul(p, &end, 16);
-        } else if (link_option(option, "soname=", &p)) {
-            copy_linker_arg(&s->soname, p, 0);
-#ifdef TCC_TARGET_PE
-        } else if (link_option(option, "large-address-aware", &p)) {
-            s->pe_characteristics |= 0x20;
-        } else if (link_option(option, "file-alignment=", &p)) {
-            s->pe_file_align = strtoul(p, &end, 16);
-        } else if (link_option(option, "stack=", &p)) {
-            s->pe_stack_size = strtoul(p, &end, 10);
-        } else if (link_option(option, "subsystem=", &p)) {
-#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
-            if (!strcmp(p, "native")) {
-                s->pe_subsystem = 1;
-            } else if (!strcmp(p, "console")) {
-                s->pe_subsystem = 3;
-            } else if (!strcmp(p, "gui") || !strcmp(p, "windows")) {
-                s->pe_subsystem = 2;
-            } else if (!strcmp(p, "posix")) {
-                s->pe_subsystem = 7;
-            } else if (!strcmp(p, "efiapp")) {
-                s->pe_subsystem = 10;
-            } else if (!strcmp(p, "efiboot")) {
-                s->pe_subsystem = 11;
-            } else if (!strcmp(p, "efiruntime")) {
-                s->pe_subsystem = 12;
-            } else if (!strcmp(p, "efirom")) {
-                s->pe_subsystem = 13;
-#elif defined(TCC_TARGET_ARM)
-            if (!strcmp(p, "wince")) {
-                s->pe_subsystem = 9;
-#endif
-            } else
-                goto err;
-#endif
-        } else if (ret = link_option(option, "?whole-archive", &p), ret) {
-            s->alacarte_link = ret < 0;
-        } else if (p) {
-            return 0;
-        } else {
-    err:
-            tcc_error("unsupported linker option '%s'", option);
-        }
-
-        if (ignoring && s->warn_unsupported)
-            tcc_warning("unsupported linker option '%s'", option);
-
-        option = skip_linker_arg(&p);
-    }
-    return 1;
-}
-
-typedef struct TCCOption {
-    const char *name;
-    uint16_t index;
-    uint16_t flags;
-} TCCOption;
-
-enum {
-    TCC_OPTION_HELP,
-    TCC_OPTION_HELP2,
-    TCC_OPTION_v,
-    TCC_OPTION_I,
-    TCC_OPTION_D,
-    TCC_OPTION_U,
-    TCC_OPTION_P,
-    TCC_OPTION_L,
-    TCC_OPTION_B,
-    TCC_OPTION_l,
-    TCC_OPTION_bench,
-    TCC_OPTION_bt,
-    TCC_OPTION_b,
-    TCC_OPTION_g,
-    TCC_OPTION_c,
-    TCC_OPTION_dumpversion,
-    TCC_OPTION_d,
-    TCC_OPTION_static,
-    TCC_OPTION_std,
-    TCC_OPTION_shared,
-    TCC_OPTION_soname,
-    TCC_OPTION_o,
-    TCC_OPTION_r,
-    TCC_OPTION_s,
-    TCC_OPTION_traditional,
-    TCC_OPTION_Wl,
-    TCC_OPTION_Wp,
-    TCC_OPTION_W,
-    TCC_OPTION_O,
-    TCC_OPTION_mfloat_abi,
-    TCC_OPTION_m,
-    TCC_OPTION_f,
-    TCC_OPTION_isystem,
-    TCC_OPTION_iwithprefix,
-    TCC_OPTION_include,
-    TCC_OPTION_nostdinc,
-    TCC_OPTION_nostdlib,
-    TCC_OPTION_print_search_dirs,
-    TCC_OPTION_rdynamic,
-    TCC_OPTION_param,
-    TCC_OPTION_pedantic,
-    TCC_OPTION_pthread,
-    TCC_OPTION_run,
-    TCC_OPTION_w,
-    TCC_OPTION_pipe,
-    TCC_OPTION_E,
-    TCC_OPTION_MD,
-    TCC_OPTION_MF,
-    TCC_OPTION_x,
-    TCC_OPTION_ar,
-    TCC_OPTION_impdef
-};
-
-#define TCC_OPTION_HAS_ARG 0x0001
-#define TCC_OPTION_NOSEP   0x0002 /* cannot have space before option and arg */
-
-static const TCCOption tcc_options[] = {
-    { "h", TCC_OPTION_HELP, 0 },
-    { "-help", TCC_OPTION_HELP, 0 },
-    { "?", TCC_OPTION_HELP, 0 },
-    { "hh", TCC_OPTION_HELP2, 0 },
-    { "v", TCC_OPTION_v, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
-    { "I", TCC_OPTION_I, TCC_OPTION_HAS_ARG },
-    { "D", TCC_OPTION_D, TCC_OPTION_HAS_ARG },
-    { "U", TCC_OPTION_U, TCC_OPTION_HAS_ARG },
-    { "P", TCC_OPTION_P, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
-    { "L", TCC_OPTION_L, TCC_OPTION_HAS_ARG },
-    { "B", TCC_OPTION_B, TCC_OPTION_HAS_ARG },
-    { "l", TCC_OPTION_l, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
-    { "bench", TCC_OPTION_bench, 0 },
-#ifdef CONFIG_TCC_BACKTRACE
-    { "bt", TCC_OPTION_bt, TCC_OPTION_HAS_ARG },
-#endif
-#ifdef CONFIG_TCC_BCHECK
-    { "b", TCC_OPTION_b, 0 },
-#endif
-    { "g", TCC_OPTION_g, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
-    { "c", TCC_OPTION_c, 0 },
-    { "dumpversion", TCC_OPTION_dumpversion, 0},
-    { "d", TCC_OPTION_d, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
-    { "static", TCC_OPTION_static, 0 },
-    { "std", TCC_OPTION_std, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
-    { "shared", TCC_OPTION_shared, 0 },
-    { "soname", TCC_OPTION_soname, TCC_OPTION_HAS_ARG },
-    { "o", TCC_OPTION_o, TCC_OPTION_HAS_ARG },
-    { "-param", TCC_OPTION_param, TCC_OPTION_HAS_ARG },
-    { "pedantic", TCC_OPTION_pedantic, 0},
-    { "pthread", TCC_OPTION_pthread, 0},
-    { "run", TCC_OPTION_run, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
-    { "rdynamic", TCC_OPTION_rdynamic, 0 },
-    { "r", TCC_OPTION_r, 0 },
-    { "s", TCC_OPTION_s, 0 },
-    { "traditional", TCC_OPTION_traditional, 0 },
-    { "Wl,", TCC_OPTION_Wl, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
-    { "Wp,", TCC_OPTION_Wp, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
-    { "W", TCC_OPTION_W, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
-    { "O", TCC_OPTION_O, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
-#ifdef TCC_TARGET_ARM
-    { "mfloat-abi", TCC_OPTION_mfloat_abi, TCC_OPTION_HAS_ARG },
-#endif
-    { "m", TCC_OPTION_m, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
-    { "f", TCC_OPTION_f, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
-    { "isystem", TCC_OPTION_isystem, TCC_OPTION_HAS_ARG },
-    { "include", TCC_OPTION_include, TCC_OPTION_HAS_ARG },
-    { "nostdinc", TCC_OPTION_nostdinc, 0 },
-    { "nostdlib", TCC_OPTION_nostdlib, 0 },
-    { "print-search-dirs", TCC_OPTION_print_search_dirs, 0 },
-    { "w", TCC_OPTION_w, 0 },
-    { "pipe", TCC_OPTION_pipe, 0},
-    { "E", TCC_OPTION_E, 0},
-    { "MD", TCC_OPTION_MD, 0},
-    { "MF", TCC_OPTION_MF, TCC_OPTION_HAS_ARG },
-    { "x", TCC_OPTION_x, TCC_OPTION_HAS_ARG },
-    { "ar", TCC_OPTION_ar, 0},
-#ifdef TCC_TARGET_PE
-    { "impdef", TCC_OPTION_impdef, 0},
-#endif
-    { NULL, 0, 0 },
-};
-
-static const FlagDef options_W[] = {
-    { 0, 0, "all" },
-    { offsetof(TCCState, warn_unsupported), 0, "unsupported" },
-    { offsetof(TCCState, warn_write_strings), 0, "write-strings" },
-    { offsetof(TCCState, warn_error), 0, "error" },
-    { offsetof(TCCState, warn_gcc_compat), 0, "gcc-compat" },
-    { offsetof(TCCState, warn_implicit_function_declaration), WD_ALL,
-      "implicit-function-declaration" },
-    { 0, 0, NULL }
-};
-
-static const FlagDef options_f[] = {
-    { offsetof(TCCState, char_is_unsigned), 0, "unsigned-char" },
-    { offsetof(TCCState, char_is_unsigned), FD_INVERT, "signed-char" },
-    { offsetof(TCCState, nocommon), FD_INVERT, "common" },
-    { offsetof(TCCState, leading_underscore), 0, "leading-underscore" },
-    { offsetof(TCCState, ms_extensions), 0, "ms-extensions" },
-    { offsetof(TCCState, dollars_in_identifiers), 0, "dollars-in-identifiers" },
-    { 0, 0, NULL }
-};
-
-static const FlagDef options_m[] = {
-    { offsetof(TCCState, ms_bitfields), 0, "ms-bitfields" },
-#ifdef TCC_TARGET_X86_64
-    { offsetof(TCCState, nosse), FD_INVERT, "sse" },
-#endif
-    { 0, 0, NULL }
-};
-
-static void parse_option_D(TCCState *s1, const char *optarg)
-{
-    char *sym = tcc_strdup(optarg);
-    char *value = strchr(sym, '=');
-    if (value)
-        *value++ = '\0';
-    tcc_define_symbol(s1, sym, value);
-    tcc_free(sym);
-}
-
-static void args_parser_add_file(TCCState *s, const char* filename, int filetype)
-{
-    struct filespec *f = tcc_malloc(sizeof *f + strlen(filename));
-    f->type = filetype;
-    f->alacarte = s->alacarte_link;
-    strcpy(f->name, filename);
-    dynarray_add(&s->files, &s->nb_files, f);
-}
-
-static int args_parser_make_argv(const char *r, int *argc, char ***argv)
-{
-    int ret = 0, q, c;
-    CString str;
-    for(;;) {
-        while (c = (unsigned char)*r, c && c <= ' ')
-	    ++r;
-        if (c == 0)
-            break;
-        q = 0;
-        cstr_new(&str);
-        while (c = (unsigned char)*r, c) {
-            ++r;
-            if (c == '\\' && (*r == '"' || *r == '\\')) {
-                c = *r++;
-            } else if (c == '"') {
-                q = !q;
-                continue;
-            } else if (q == 0 && c <= ' ') {
-                break;
-            }
-            cstr_ccat(&str, c);
-        }
-        cstr_ccat(&str, 0);
-        //printf("<%s>\n", str.data), fflush(stdout);
-        dynarray_add(argv, argc, tcc_strdup(str.data));
-        cstr_free(&str);
-        ++ret;
-    }
-    return ret;
-}
-
-/* read list file */
-static void args_parser_listfile(TCCState *s,
-    const char *filename, int optind, int *pargc, char ***pargv)
-{
-    int fd, i;
-    size_t len;
-    char *p;
-    int argc = 0;
-    char **argv = NULL;
-
-    fd = open(filename, O_RDONLY | O_BINARY);
-    if (fd < 0)
-        tcc_error("listfile '%s' not found", filename);
-
-    len = lseek(fd, 0, SEEK_END);
-    p = tcc_malloc(len + 1), p[len] = 0;
-    lseek(fd, 0, SEEK_SET), read(fd, p, len), close(fd);
-
-    for (i = 0; i < *pargc; ++i)
-        if (i == optind)
-            args_parser_make_argv(p, &argc, &argv);
-        else
-            dynarray_add(&argv, &argc, tcc_strdup((*pargv)[i]));
-
-    tcc_free(p);
-    dynarray_reset(&s->argv, &s->argc);
-    *pargc = s->argc = argc, *pargv = s->argv = argv;
-}
-
-PUB_FUNC int tcc_parse_args(TCCState *s, int *pargc, char ***pargv, int optind)
-{
-    const TCCOption *popt;
-    const char *optarg, *r;
-    const char *run = NULL;
-    int last_o = -1;
-    int x;
-    CString linker_arg; /* collect -Wl options */
-    int tool = 0, arg_start = 0, noaction = optind;
-    char **argv = *pargv;
-    int argc = *pargc;
-
-    cstr_new(&linker_arg);
-
-    while (optind < argc) {
-        r = argv[optind];
-        if (r[0] == '@' && r[1] != '\0') {
-            args_parser_listfile(s, r + 1, optind, &argc, &argv);
-	    continue;
-        }
-        optind++;
-        if (tool) {
-            if (r[0] == '-' && r[1] == 'v' && r[2] == 0)
-                ++s->verbose;
-            continue;
-        }
-reparse:
-        if (r[0] != '-' || r[1] == '\0') {
-            if (r[0] != '@') /* allow "tcc file(s) -run @ args ..." */
-                args_parser_add_file(s, r, s->filetype);
-            if (run) {
-                tcc_set_options(s, run);
-                arg_start = optind - 1;
-                break;
-            }
-            continue;
-        }
-
-        /* find option in table */
-        for(popt = tcc_options; ; ++popt) {
-            const char *p1 = popt->name;
-            const char *r1 = r + 1;
-            if (p1 == NULL)
-                tcc_error("invalid option -- '%s'", r);
-            if (!strstart(p1, &r1))
-                continue;
-            optarg = r1;
-            if (popt->flags & TCC_OPTION_HAS_ARG) {
-                if (*r1 == '\0' && !(popt->flags & TCC_OPTION_NOSEP)) {
-                    if (optind >= argc)
-                arg_err:
-                        tcc_error("argument to '%s' is missing", r);
-                    optarg = argv[optind++];
-                }
-            } else if (*r1 != '\0')
-                continue;
-            break;
-        }
-
-        switch(popt->index) {
-        case TCC_OPTION_HELP:
-            return OPT_HELP;
-        case TCC_OPTION_HELP2:
-            return OPT_HELP2;
-        case TCC_OPTION_I:
-            tcc_add_include_path(s, optarg);
-            break;
-        case TCC_OPTION_D:
-            parse_option_D(s, optarg);
-            break;
-        case TCC_OPTION_U:
-            tcc_undefine_symbol(s, optarg);
-            break;
-        case TCC_OPTION_L:
-            tcc_add_library_path(s, optarg);
-            break;
-        case TCC_OPTION_B:
-            /* set tcc utilities path (mainly for tcc development) */
-            tcc_set_lib_path(s, optarg);
-            break;
-        case TCC_OPTION_l:
-            args_parser_add_file(s, optarg, AFF_TYPE_LIB);
-            s->nb_libraries++;
-            break;
-        case TCC_OPTION_pthread:
-            parse_option_D(s, "_REENTRANT");
-            s->option_pthread = 1;
-            break;
-        case TCC_OPTION_bench:
-            s->do_bench = 1;
-            break;
-#ifdef CONFIG_TCC_BACKTRACE
-        case TCC_OPTION_bt:
-            tcc_set_num_callers(atoi(optarg));
-            break;
-#endif
-#ifdef CONFIG_TCC_BCHECK
-        case TCC_OPTION_b:
-            s->do_bounds_check = 1;
-            s->do_debug = 1;
-            break;
-#endif
-        case TCC_OPTION_g:
-            s->do_debug = 1;
-            break;
-        case TCC_OPTION_c:
-            x = TCC_OUTPUT_OBJ;
-        set_output_type:
-            if (s->output_type)
-                tcc_warning("-%s: overriding compiler action already specified", popt->name);
-            s->output_type = x;
-            break;
-        case TCC_OPTION_d:
-            if (*optarg == 'D')
-                s->dflag = 3;
-            else if (*optarg == 'M')
-                s->dflag = 7;
-            else if (*optarg == 't')
-                s->dflag = 16;
-            else if (isnum(*optarg))
-                g_debug = atoi(optarg);
-            else
-                goto unsupported_option;
-            break;
-        case TCC_OPTION_static:
-            s->static_link = 1;
-            break;
-        case TCC_OPTION_std:
-    	    /* silently ignore, a current purpose:
-    	       allow to use a tcc as a reference compiler for "make test" */
-            break;
-        case TCC_OPTION_shared:
-            x = TCC_OUTPUT_DLL;
-            goto set_output_type;
-        case TCC_OPTION_soname:
-            s->soname = tcc_strdup(optarg);
-            break;
-        case TCC_OPTION_o:
-            if (s->outfile) {
-                tcc_warning("multiple -o option");
-                tcc_free(s->outfile);
-            }
-            s->outfile = tcc_strdup(optarg);
-            break;
-        case TCC_OPTION_r:
-            /* generate a .o merging several output files */
-            s->option_r = 1;
-            x = TCC_OUTPUT_OBJ;
-            goto set_output_type;
-        case TCC_OPTION_isystem:
-            tcc_add_sysinclude_path(s, optarg);
-            break;
-	case TCC_OPTION_include:
-	    dynarray_add(&s->cmd_include_files,
-			 &s->nb_cmd_include_files, tcc_strdup(optarg));
-	    break;
-        case TCC_OPTION_nostdinc:
-            s->nostdinc = 1;
-            break;
-        case TCC_OPTION_nostdlib:
-            s->nostdlib = 1;
-            break;
-        case TCC_OPTION_run:
-#ifndef TCC_IS_NATIVE
-            tcc_error("-run is not available in a cross compiler");
-#endif
-            run = optarg;
-            x = TCC_OUTPUT_MEMORY;
-            goto set_output_type;
-        case TCC_OPTION_v:
-            do ++s->verbose; while (*optarg++ == 'v');
-            ++noaction;
-            break;
-        case TCC_OPTION_f:
-            if (set_flag(s, options_f, optarg) < 0)
-                goto unsupported_option;
-            break;
-#ifdef TCC_TARGET_ARM
-        case TCC_OPTION_mfloat_abi:
-            /* tcc doesn't support soft float yet */
-            if (!strcmp(optarg, "softfp")) {
-                s->float_abi = ARM_SOFTFP_FLOAT;
-                tcc_undefine_symbol(s, "__ARM_PCS_VFP");
-            } else if (!strcmp(optarg, "hard"))
-                s->float_abi = ARM_HARD_FLOAT;
-            else
-                tcc_error("unsupported float abi '%s'", optarg);
-            break;
-#endif
-        case TCC_OPTION_m:
-            if (set_flag(s, options_m, optarg) < 0) {
-                if (x = atoi(optarg), x != 32 && x != 64)
-                    goto unsupported_option;
-                if (PTR_SIZE != x/8)
-                    return x;
-                ++noaction;
-            }
-            break;
-        case TCC_OPTION_W:
-            if (set_flag(s, options_W, optarg) < 0)
-                goto unsupported_option;
-            break;
-        case TCC_OPTION_w:
-            s->warn_none = 1;
-            break;
-        case TCC_OPTION_rdynamic:
-            s->rdynamic = 1;
-            break;
-        case TCC_OPTION_Wl:
-            if (linker_arg.size)
-                --linker_arg.size, cstr_ccat(&linker_arg, ',');
-            cstr_cat(&linker_arg, optarg, 0);
-            if (tcc_set_linker(s, linker_arg.data))
-                cstr_free(&linker_arg);
-            break;
-	case TCC_OPTION_Wp:
-	    r = optarg;
-	    goto reparse;
-        case TCC_OPTION_E:
-            x = TCC_OUTPUT_PREPROCESS;
-            goto set_output_type;
-        case TCC_OPTION_P:
-            s->Pflag = atoi(optarg) + 1;
-            break;
-        case TCC_OPTION_MD:
-            s->gen_deps = 1;
-            break;
-        case TCC_OPTION_MF:
-            s->deps_outfile = tcc_strdup(optarg);
-            break;
-        case TCC_OPTION_dumpversion:
-            printf ("%s\n", TCC_VERSION);
-            exit(0);
-            break;
-        case TCC_OPTION_x:
-            if (*optarg == 'c')
-                s->filetype = AFF_TYPE_C;
-            else if (*optarg == 'a')
-                s->filetype = AFF_TYPE_ASMPP;
-            else if (*optarg == 'n')
-                s->filetype = AFF_TYPE_NONE;
-            else
-                tcc_warning("unsupported language '%s'", optarg);
-            break;
-        case TCC_OPTION_O:
-            last_o = atoi(optarg);
-            break;
-        case TCC_OPTION_print_search_dirs:
-            x = OPT_PRINT_DIRS;
-            goto extra_action;
-        case TCC_OPTION_impdef:
-            x = OPT_IMPDEF;
-            goto extra_action;
-        case TCC_OPTION_ar:
-            x = OPT_AR;
-        extra_action:
-            arg_start = optind - 1;
-            if (arg_start != noaction)
-                tcc_error("cannot parse %s here", r);
-            tool = x;
-            break;
-        case TCC_OPTION_traditional:
-        case TCC_OPTION_pedantic:
-        case TCC_OPTION_pipe:
-        case TCC_OPTION_s:
-            /* ignored */
-            break;
-        default:
-unsupported_option:
-            if (s->warn_unsupported)
-                tcc_warning("unsupported option '%s'", r);
-            break;
-        }
-    }
-    if (last_o > 0)
-        tcc_define_symbol(s, "__OPTIMIZE__", NULL);
-    if (linker_arg.size) {
-        r = linker_arg.data;
-        goto arg_err;
-    }
-    *pargc = argc - arg_start;
-    *pargv = argv + arg_start;
-    if (tool)
-        return tool;
-    if (optind != noaction)
-        return 0;
-    if (s->verbose == 2)
-        return OPT_PRINT_DIRS;
-    if (s->verbose)
-        return OPT_V;
-    return OPT_HELP;
-}
-
-LIBTCCAPI void tcc_set_options(TCCState *s, const char *r)
-{
-    char **argv = NULL;
-    int argc = 0;
-    args_parser_make_argv(r, &argc, &argv);
-    tcc_parse_args(s, &argc, &argv, 0);
-    dynarray_reset(&argv, &argc);
-}
-
-PUB_FUNC void tcc_print_stats(TCCState *s, unsigned total_time)
-{
-    if (total_time < 1)
-        total_time = 1;
-    if (total_bytes < 1)
-        total_bytes = 1;
-    fprintf(stderr, "* %d idents, %d lines, %d bytes\n"
-                    "* %0.3f s, %u lines/s, %0.1f MB/s\n",
-           tok_ident - TOK_IDENT, total_lines, total_bytes,
-           (double)total_time/1000,
-           (unsigned)total_lines*1000/total_time,
-           (double)total_bytes/1000/total_time);
-#ifdef MEM_DEBUG
-    fprintf(stderr, "* %d bytes memory used\n", mem_max_size);
-#endif
-}
diff --git a/tinyc/libtcc.h b/tinyc/libtcc.h
deleted file mode 100644
index a1b31e300..000000000
--- a/tinyc/libtcc.h
+++ /dev/null
@@ -1,100 +0,0 @@
-#ifndef LIBTCC_H
-#define LIBTCC_H
-
-#ifndef LIBTCCAPI
-# define LIBTCCAPI
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct TCCState;
-
-typedef struct TCCState TCCState;
-
-/* create a new TCC compilation context */
-LIBTCCAPI TCCState *tcc_new(void);
-
-/* free a TCC compilation context */
-LIBTCCAPI void tcc_delete(TCCState *s);
-
-/* set CONFIG_TCCDIR at runtime */
-LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path);
-
-/* set error/warning display callback */
-LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque,
-    void (*error_func)(void *opaque, const char *msg));
-
-/* set options as from command line (multiple supported) */
-LIBTCCAPI void tcc_set_options(TCCState *s, const char *str);
-
-/*****************************/
-/* preprocessor */
-
-/* add include path */
-LIBTCCAPI int tcc_add_include_path(TCCState *s, const char *pathname);
-
-/* add in system include path */
-LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s, const char *pathname);
-
-/* define preprocessor symbol 'sym'. Can put optional value */
-LIBTCCAPI void tcc_define_symbol(TCCState *s, const char *sym, const char *value);
-
-/* undefine preprocess symbol 'sym' */
-LIBTCCAPI void tcc_undefine_symbol(TCCState *s, const char *sym);
-
-/*****************************/
-/* compiling */
-
-/* add a file (C file, dll, object, library, ld script). Return -1 if error. */
-LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename);
-
-/* compile a string containing a C source. Return -1 if error. */
-LIBTCCAPI int tcc_compile_string(TCCState *s, const char *buf);
-
-/*****************************/
-/* linking commands */
-
-/* set output type. MUST BE CALLED before any compilation */
-LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type);
-#define TCC_OUTPUT_MEMORY   1 /* output will be run in memory (default) */
-#define TCC_OUTPUT_EXE      2 /* executable file */
-#define TCC_OUTPUT_DLL      3 /* dynamic library */
-#define TCC_OUTPUT_OBJ      4 /* object file */
-#define TCC_OUTPUT_PREPROCESS 5 /* only preprocess (used internally) */
-
-/* equivalent to -Lpath option */
-LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname);
-
-/* the library name is the same as the argument of the '-l' option */
-LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname);
-
-/* add a symbol to the compiled program */
-LIBTCCAPI int tcc_add_symbol(TCCState *s, const char *name, const void *val);
-
-/* output an executable, library or object file. DO NOT call
-   tcc_relocate() before. */
-LIBTCCAPI int tcc_output_file(TCCState *s, const char *filename);
-
-/* link and run main() function and return its value. DO NOT call
-   tcc_relocate() before. */
-LIBTCCAPI int tcc_run(TCCState *s, int argc, char **argv);
-
-/* do all relocations (needed before using tcc_get_symbol()) */
-LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr);
-/* possible values for 'ptr':
-   - TCC_RELOCATE_AUTO : Allocate and manage memory internally
-   - NULL              : return required memory size for the step below
-   - memory address    : copy code to memory passed by the caller
-   returns -1 if error. */
-#define TCC_RELOCATE_AUTO (void*)1
-
-/* return symbol value or NULL if not found */
-LIBTCCAPI void *tcc_get_symbol(TCCState *s, const char *name);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/tinyc/stab.def b/tinyc/stab.def
deleted file mode 100644
index 48ea231e6..000000000
--- a/tinyc/stab.def
+++ /dev/null
@@ -1,234 +0,0 @@
-/* Table of DBX symbol codes for the GNU system.
-   Copyright (C) 1988, 1997 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
-
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Library General Public License for more details.
-
-   You should have received a copy of the GNU Library General Public
-   License along with the GNU C Library; see the file COPYING.LIB.  If not,
-   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
-
-/* This contains contribution from Cygnus Support.  */
-
-/* Global variable.  Only the name is significant.
-   To find the address, look in the corresponding external symbol.  */
-__define_stab (N_GSYM, 0x20, "GSYM")
-
-/* Function name for BSD Fortran.  Only the name is significant.
-   To find the address, look in the corresponding external symbol.  */
-__define_stab (N_FNAME, 0x22, "FNAME")
-
-/* Function name or text-segment variable for C.  Value is its address.
-   Desc is supposedly starting line number, but GCC doesn't set it
-   and DBX seems not to miss it.  */
-__define_stab (N_FUN, 0x24, "FUN")
-
-/* Data-segment variable with internal linkage.  Value is its address.
-   "Static Sym".  */
-__define_stab (N_STSYM, 0x26, "STSYM")
-
-/* BSS-segment variable with internal linkage.  Value is its address.  */
-__define_stab (N_LCSYM, 0x28, "LCSYM")
-
-/* Name of main routine.  Only the name is significant.
-   This is not used in C.  */
-__define_stab (N_MAIN, 0x2a, "MAIN")
-
-/* Global symbol in Pascal.
-   Supposedly the value is its line number; I'm skeptical.  */
-__define_stab (N_PC, 0x30, "PC")
-
-/* Number of symbols:  0, files,,funcs,lines according to Ultrix V4.0. */
-__define_stab (N_NSYMS, 0x32, "NSYMS")
-
-/* "No DST map for sym: name, ,0,type,ignored"  according to Ultrix V4.0. */
-__define_stab (N_NOMAP, 0x34, "NOMAP")
-
-/* New stab from Solaris.  I don't know what it means, but it
-   don't seem to contain useful information.  */
-__define_stab (N_OBJ, 0x38, "OBJ")
-
-/* New stab from Solaris.  I don't know what it means, but it
-   don't seem to contain useful information.  Possibly related to the
-   optimization flags used in this module.  */
-__define_stab (N_OPT, 0x3c, "OPT")
-
-/* Register variable.  Value is number of register.  */
-__define_stab (N_RSYM, 0x40, "RSYM")
-
-/* Modula-2 compilation unit.  Can someone say what info it contains?  */
-__define_stab (N_M2C, 0x42, "M2C")
-
-/* Line number in text segment.  Desc is the line number;
-   value is corresponding address.  */
-__define_stab (N_SLINE, 0x44, "SLINE")
-
-/* Similar, for data segment.  */
-__define_stab (N_DSLINE, 0x46, "DSLINE")
-
-/* Similar, for bss segment.  */
-__define_stab (N_BSLINE, 0x48, "BSLINE")
-
-/* Sun's source-code browser stabs.  ?? Don't know what the fields are.
-   Supposedly the field is "path to associated .cb file".  THIS VALUE
-   OVERLAPS WITH N_BSLINE!  */
-__define_stab (N_BROWS, 0x48, "BROWS")
-
-/* GNU Modula-2 definition module dependency.  Value is the modification time
-   of the definition file.  Other is non-zero if it is imported with the
-   GNU M2 keyword %INITIALIZE.  Perhaps N_M2C can be used if there
-   are enough empty fields? */
-__define_stab(N_DEFD, 0x4a, "DEFD")
-
-/* THE FOLLOWING TWO STAB VALUES CONFLICT.  Happily, one is for Modula-2
-   and one is for C++.   Still,... */
-/* GNU C++ exception variable.  Name is variable name.  */
-__define_stab (N_EHDECL, 0x50, "EHDECL")
-/* Modula2 info "for imc":  name,,0,0,0  according to Ultrix V4.0.  */
-__define_stab (N_MOD2, 0x50, "MOD2")
-
-/* GNU C++ `catch' clause.  Value is its address.  Desc is nonzero if
-   this entry is immediately followed by a CAUGHT stab saying what exception
-   was caught.  Multiple CAUGHT stabs means that multiple exceptions
-   can be caught here.  If Desc is 0, it means all exceptions are caught
-   here.  */
-__define_stab (N_CATCH, 0x54, "CATCH")
-
-/* Structure or union element.  Value is offset in the structure.  */
-__define_stab (N_SSYM, 0x60, "SSYM")
-
-/* Name of main source file.
-   Value is starting text address of the compilation.  */
-__define_stab (N_SO, 0x64, "SO")
-
-/* Automatic variable in the stack.  Value is offset from frame pointer.
-   Also used for type descriptions.  */
-__define_stab (N_LSYM, 0x80, "LSYM")
-
-/* Beginning of an include file.  Only Sun uses this.
-   In an object file, only the name is significant.
-   The Sun linker puts data into some of the other fields.  */
-__define_stab (N_BINCL, 0x82, "BINCL")
-
-/* Name of sub-source file (#include file).
-   Value is starting text address of the compilation.  */
-__define_stab (N_SOL, 0x84, "SOL")
-
-/* Parameter variable.  Value is offset from argument pointer.
-   (On most machines the argument pointer is the same as the frame pointer.  */
-__define_stab (N_PSYM, 0xa0, "PSYM")
-
-/* End of an include file.  No name.
-   This and N_BINCL act as brackets around the file's output.
-   In an object file, there is no significant data in this entry.
-   The Sun linker puts data into some of the fields.  */
-__define_stab (N_EINCL, 0xa2, "EINCL")
-
-/* Alternate entry point.  Value is its address.  */
-__define_stab (N_ENTRY, 0xa4, "ENTRY")
-
-/* Beginning of lexical block.
-   The desc is the nesting level in lexical blocks.
-   The value is the address of the start of the text for the block.
-   The variables declared inside the block *precede* the N_LBRAC symbol.  */
-__define_stab (N_LBRAC, 0xc0, "LBRAC")
-
-/* Place holder for deleted include file.  Replaces a N_BINCL and everything
-   up to the corresponding N_EINCL.  The Sun linker generates these when
-   it finds multiple identical copies of the symbols from an include file.
-   This appears only in output from the Sun linker.  */
-__define_stab (N_EXCL, 0xc2, "EXCL")
-
-/* Modula-2 scope information.  Can someone say what info it contains?  */
-__define_stab (N_SCOPE, 0xc4, "SCOPE")
-
-/* End of a lexical block.  Desc matches the N_LBRAC's desc.
-   The value is the address of the end of the text for the block.  */
-__define_stab (N_RBRAC, 0xe0, "RBRAC")
-
-/* Begin named common block.  Only the name is significant.  */
-__define_stab (N_BCOMM, 0xe2, "BCOMM")
-
-/* End named common block.  Only the name is significant
-   (and it should match the N_BCOMM).  */
-__define_stab (N_ECOMM, 0xe4, "ECOMM")
-
-/* End common (local name): value is address.
-   I'm not sure how this is used.  */
-__define_stab (N_ECOML, 0xe8, "ECOML")
-
-/* These STAB's are used on Gould systems for Non-Base register symbols
-   or something like that.  FIXME.  I have assigned the values at random
-   since I don't have a Gould here.  Fixups from Gould folk welcome... */
-__define_stab (N_NBTEXT, 0xF0, "NBTEXT")
-__define_stab (N_NBDATA, 0xF2, "NBDATA")
-__define_stab (N_NBBSS,  0xF4, "NBBSS")
-__define_stab (N_NBSTS,  0xF6, "NBSTS")
-__define_stab (N_NBLCS,  0xF8, "NBLCS")
-
-/* Second symbol entry containing a length-value for the preceding entry.
-   The value is the length.  */
-__define_stab (N_LENG, 0xfe, "LENG")
-
-/* The above information, in matrix format.
-
-			STAB MATRIX
-	_________________________________________________
-	| 00 - 1F are not dbx stab symbols		|
-	| In most cases, the low bit is the EXTernal bit|
-
-	| 00 UNDEF  | 02 ABS	| 04 TEXT   | 06 DATA	|
-	| 01  |EXT  | 03  |EXT	| 05  |EXT  | 07  |EXT	|
-
-	| 08 BSS    | 0A INDR	| 0C FN_SEQ | 0E   	|
-	| 09  |EXT  | 0B 	| 0D	    | 0F	|
-
-	| 10 	    | 12 COMM	| 14 SETA   | 16 SETT	|
-	| 11	    | 13	| 15 	    | 17	|
-
-	| 18 SETD   | 1A SETB	| 1C SETV   | 1E WARNING|
-	| 19	    | 1B	| 1D 	    | 1F FN	|
-
-	|_______________________________________________|
-	| Debug entries with bit 01 set are unused.	|
-	| 20 GSYM   | 22 FNAME	| 24 FUN    | 26 STSYM	|
-	| 28 LCSYM  | 2A MAIN	| 2C	    | 2E	|
-	| 30 PC	    | 32 NSYMS	| 34 NOMAP  | 36	|
-	| 38 OBJ    | 3A	| 3C OPT    | 3E	|
-	| 40 RSYM   | 42 M2C	| 44 SLINE  | 46 DSLINE |
-	| 48 BSLINE*| 4A DEFD	| 4C        | 4E	|
-	| 50 EHDECL*| 52	| 54 CATCH  | 56        |
-	| 58        | 5A        | 5C        | 5E	|
-	| 60 SSYM   | 62	| 64 SO	    | 66 	|
-	| 68 	    | 6A	| 6C	    | 6E	|
-	| 70	    | 72	| 74	    | 76	|
-	| 78	    | 7A	| 7C	    | 7E	|
-	| 80 LSYM   | 82 BINCL	| 84 SOL    | 86	|
-	| 88	    | 8A	| 8C	    | 8E	|
-	| 90	    | 92	| 94	    | 96	|
-	| 98	    | 9A	| 9C	    | 9E	|
-	| A0 PSYM   | A2 EINCL	| A4 ENTRY  | A6	|
-	| A8	    | AA	| AC	    | AE	|
-	| B0	    | B2	| B4	    | B6	|
-	| B8	    | BA	| BC	    | BE	|
-	| C0 LBRAC  | C2 EXCL	| C4 SCOPE  | C6	|
-	| C8	    | CA	| CC	    | CE	|
-	| D0	    | D2	| D4	    | D6	|
-	| D8	    | DA	| DC	    | DE	|
-	| E0 RBRAC  | E2 BCOMM	| E4 ECOMM  | E6	|
-	| E8 ECOML  | EA	| EC	    | EE	|
-	| F0	    | F2	| F4	    | F6	|
-	| F8	    | FA	| FC	    | FE LENG	|
-	+-----------------------------------------------+
- * 50 EHDECL is also MOD2.
- * 48 BSLINE is also BROWS.
- */
diff --git a/tinyc/stab.h b/tinyc/stab.h
deleted file mode 100644
index 80bd594a3..000000000
--- a/tinyc/stab.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef __GNU_STAB__
-
-/* Indicate the GNU stab.h is in use.  */
-
-#define __GNU_STAB__
-
-#define __define_stab(NAME, CODE, STRING) NAME=CODE,
-
-enum __stab_debug_code
-{
-#include "stab.def"
-LAST_UNUSED_STAB_CODE
-};
-
-#undef __define_stab
-
-#endif /* __GNU_STAB_ */
diff --git a/tinyc/tcc-doc.texi b/tinyc/tcc-doc.texi
deleted file mode 100644
index 5e718a2c5..000000000
--- a/tinyc/tcc-doc.texi
+++ /dev/null
@@ -1,1326 +0,0 @@
-\input texinfo @c -*- texinfo -*-
-@c %**start of header
-@setfilename tcc-doc.info
-@settitle Tiny C Compiler Reference Documentation
-@dircategory Software development
-@direntry
-* TCC: (tcc-doc).               The Tiny C Compiler.
-@end direntry
-@c %**end of header
-
-@include config.texi
-
-@iftex
-@titlepage
-@afourpaper
-@sp 7
-@center @titlefont{Tiny C Compiler Reference Documentation}
-@sp 3
-@end titlepage
-@headings double
-@end iftex
-
-@contents
-
-@node Top, Introduction, (dir), (dir)
-@top Tiny C Compiler Reference Documentation
-
-This manual documents version @value{VERSION} of the Tiny C Compiler.
-
-@menu
-* Introduction::                Introduction to tcc.
-* Invoke::                      Invocation of tcc (command line, options).
-* Clang::                       ANSI C and extensions.
-* asm::                         Assembler syntax.
-* linker::                      Output file generation and supported targets.
-* Bounds::                      Automatic bounds-checking of C code.
-* Libtcc::                      The libtcc library.
-* devel::                       Guide for Developers.
-@end menu
-
-
-@node Introduction
-@chapter Introduction
-
-TinyCC (aka TCC) is a small but hyper fast C compiler. Unlike other C
-compilers, it is meant to be self-relying: you do not need an
-external assembler or linker because TCC does that for you.
-
-TCC compiles so @emph{fast} that even for big projects @code{Makefile}s may
-not be necessary.
-
-TCC not only supports ANSI C, but also most of the new ISO C99
-standard and many GNUC extensions including inline assembly.
-
-TCC can also be used to make @emph{C scripts}, i.e. pieces of C source
-that you run as a Perl or Python script. Compilation is so fast that
-your script will be as fast as if it was an executable.
-
-TCC can also automatically generate memory and bound checks
-(@pxref{Bounds}) while allowing all C pointers operations. TCC can do
-these checks even if non patched libraries are used.
-
-With @code{libtcc}, you can use TCC as a backend for dynamic code
-generation (@pxref{Libtcc}).
-
-TCC mainly supports the i386 target on Linux and Windows. There are alpha
-ports for the ARM (@code{arm-tcc}) and the TMS320C67xx targets
-(@code{c67-tcc}). More information about the ARM port is available at
-@url{http://lists.gnu.org/archive/html/tinycc-devel/2003-10/msg00044.html}.
-
-For usage on Windows, see also @url{tcc-win32.txt}.
-
-@node Invoke
-@chapter Command line invocation
-
-@section Quick start
-
-@example
-@c man begin SYNOPSIS
-usage: tcc [options] [@var{infile1} @var{infile2}@dots{}] [@option{-run} @var{infile} @var{args}@dots{}]
-@c man end
-@end example
-
-@noindent
-@c man begin DESCRIPTION
-TCC options are a very much like gcc options. The main difference is that TCC
-can also execute directly the resulting program and give it runtime
-arguments.
-
-Here are some examples to understand the logic:
-
-@table @code
-@item @samp{tcc -run a.c}
-Compile @file{a.c} and execute it directly
-
-@item @samp{tcc -run a.c arg1}
-Compile a.c and execute it directly. arg1 is given as first argument to
-the @code{main()} of a.c.
-
-@item @samp{tcc a.c -run b.c arg1}
-Compile @file{a.c} and @file{b.c}, link them together and execute them. arg1 is given
-as first argument to the @code{main()} of the resulting program. 
-@ignore 
-Because multiple C files are specified, @option{--} are necessary to clearly 
-separate the program arguments from the TCC options.
-@end ignore
-
-@item @samp{tcc -o myprog a.c b.c}
-Compile @file{a.c} and @file{b.c}, link them and generate the executable @file{myprog}.
-
-@item @samp{tcc -o myprog a.o b.o}
-link @file{a.o} and @file{b.o} together and generate the executable @file{myprog}.
-
-@item @samp{tcc -c a.c}
-Compile @file{a.c} and generate object file @file{a.o}.
-
-@item @samp{tcc -c asmfile.S}
-Preprocess with C preprocess and assemble @file{asmfile.S} and generate
-object file @file{asmfile.o}.
-
-@item @samp{tcc -c asmfile.s}
-Assemble (but not preprocess) @file{asmfile.s} and generate object file
-@file{asmfile.o}.
-
-@item @samp{tcc -r -o ab.o a.c b.c}
-Compile @file{a.c} and @file{b.c}, link them together and generate the object file @file{ab.o}.
-
-@end table
-
-Scripting:
-
-TCC can be invoked from @emph{scripts}, just as shell scripts. You just
-need to add @code{#!/usr/local/bin/tcc -run} at the start of your C source:
-
-@example
-#!/usr/local/bin/tcc -run
-#include <stdio.h>
-
-int main() 
-@{
-    printf("Hello World\n");
-    return 0;
-@}
-@end example
-
-TCC can read C source code from @emph{standard input} when @option{-} is used in 
-place of @option{infile}. Example:
-
-@example
-echo 'main()@{puts("hello");@}' | tcc -run -
-@end example
-@c man end
-
-@section Option summary
-
-General Options:
-
-@c man begin OPTIONS
-@table @option
-@item -c
-Generate an object file.
-
-@item -o outfile
-Put object file, executable, or dll into output file @file{outfile}.
-
-@item -run source [args...]
-Compile file @var{source} and run it with the command line arguments
-@var{args}. In order to be able to give more than one argument to a
-script, several TCC options can be given @emph{after} the
-@option{-run} option, separated by spaces:
-@example
-tcc "-run -L/usr/X11R6/lib -lX11" ex4.c
-@end example
-In a script, it gives the following header:
-@example
-#!/usr/local/bin/tcc -run -L/usr/X11R6/lib -lX11
-@end example
-
-@item -v
-Display TCC version.
-
-@item -vv
-Show included files.  As sole argument, print search dirs.  -vvv shows tries too.
-
-@item -bench
-Display compilation statistics.
-
-@end table
-
-Preprocessor options:
-
-@table @option
-@item -Idir
-Specify an additional include path. Include paths are searched in the
-order they are specified.
-
-System include paths are always searched after. The default system
-include paths are: @file{/usr/local/include}, @file{/usr/include}
-and @file{PREFIX/lib/tcc/include}. (@file{PREFIX} is usually
-@file{/usr} or @file{/usr/local}).
-
-@item -Dsym[=val]
-Define preprocessor symbol @samp{sym} to
-val. If val is not present, its value is @samp{1}. Function-like macros can
-also be defined: @option{-DF(a)=a+1}
-
-@item -Usym
-Undefine preprocessor symbol @samp{sym}.
-
-@item -E
-Preprocess only, to stdout or file (with -o).
-
-@end table
-
-Compilation flags:
-
-Note: each of the following options has a negative form beginning with
-@option{-fno-}.
-
-@table @option
-@item -funsigned-char
-Let the @code{char} type be unsigned.
-
-@item -fsigned-char
-Let the @code{char} type be signed.
-
-@item -fno-common
-Do not generate common symbols for uninitialized data.
-
-@item -fleading-underscore
-Add a leading underscore at the beginning of each C symbol.
-
-@item -fms-extensions
-Allow a MS C compiler extensions to the language. Currently this
-assumes a nested named structure declaration without an identifier
-behaves like an unnamed one.
-
-@item -fdollars-in-identifiers
-Allow dollar signs in identifiers
-
-@end table
-
-Warning options:
-
-@table @option
-@item -w
-Disable all warnings.
-
-@end table
-
-Note: each of the following warning options has a negative form beginning with
-@option{-Wno-}.
-
-@table @option
-@item -Wimplicit-function-declaration
-Warn about implicit function declaration.
-
-@item -Wunsupported
-Warn about unsupported GCC features that are ignored by TCC.
-
-@item -Wwrite-strings
-Make string constants be of type @code{const char *} instead of @code{char
-*}.
-
-@item -Werror
-Abort compilation if warnings are issued.
-
-@item -Wall 
-Activate all warnings, except @option{-Werror}, @option{-Wunusupported} and
-@option{-Wwrite-strings}.
-
-@end table
-
-Linker options:
-
-@table @option
-@item -Ldir
-Specify an additional static library path for the @option{-l} option. The
-default library paths are @file{/usr/local/lib}, @file{/usr/lib} and @file{/lib}.
-
-@item -lxxx
-Link your program with dynamic library libxxx.so or static library
-libxxx.a. The library is searched in the paths specified by the
-@option{-L} option and @env{LIBRARY_PATH} variable.
-
-@item -Bdir
-Set the path where the tcc internal libraries (and include files) can be
-found (default is @file{PREFIX/lib/tcc}).
-
-@item -shared
-Generate a shared library instead of an executable.
-
-@item -soname name
-set name for shared library to be used at runtime
-
-@item -static
-Generate a statically linked executable (default is a shared linked
-executable).
-
-@item -rdynamic
-Export global symbols to the dynamic linker. It is useful when a library
-opened with @code{dlopen()} needs to access executable symbols.
-
-@item -r
-Generate an object file combining all input files.
-
-@item -Wl,-rpath=path
-Put custom search path for dynamic libraries into executable.
-
-@item -Wl,--enable-new-dtags
-When putting a custom search path for dynamic libraries into the executable,
-create the new ELF dynamic tag DT_RUNPATH instead of the old legacy DT_RPATH.
-
-@item -Wl,--oformat=fmt
-Use @var{fmt} as output format. The supported output formats are:
-@table @code
-@item elf32-i386
-ELF output format (default)
-@item binary
-Binary image (only for executable output)
-@item coff
-COFF output format (only for executable output for TMS320C67xx target)
-@end table
-
-@item -Wl,-subsystem=console/gui/wince/...
-Set type for PE (Windows) executables.
-
-@item -Wl,-[Ttext=# | section-alignment=# | file-alignment=# | image-base=# | stack=#]
-Modify executable layout.
-
-@item -Wl,-Bsymbolic
-Set DT_SYMBOLIC tag.
-
-@item -Wl,-(no-)whole-archive
-Turn on/off linking of all objects in archives.
-
-@end table
-
-Debugger options:
-
-@table @option
-@item -g
-Generate run time debug information so that you get clear run time
-error messages: @code{ test.c:68: in function 'test5()': dereferencing
-invalid pointer} instead of the laconic @code{Segmentation
-fault}.
-
-@item -b
-Generate additional support code to check
-memory allocations and array/pointer bounds. @option{-g} is implied. Note
-that the generated code is slower and bigger in this case.
-
-Note: @option{-b} is only available on i386 when using libtcc for the moment.
-
-@item -bt N
-Display N callers in stack traces. This is useful with @option{-g} or
-@option{-b}.
-
-@end table
-
-Misc options:
-
-@table @option
-@item -MD
-Generate makefile fragment with dependencies.
-
-@item -MF depfile
-Use @file{depfile} as output for -MD.
-
-@item -print-search-dirs
-Print the configured installation directory and a list of library
-and include directories tcc will search.
-
-@item -dumpversion
-Print version.
-
-@end table
-
-Target specific options:
-
-@table @option
-@item -mms-bitfields
-Use an algorithm for bitfield alignment consistent with MSVC. Default is
-gcc's algorithm.
-
-@item -mfloat-abi (ARM only)
-Select the float ABI. Possible values: @code{softfp} and @code{hard}
-
-@item -mno-sse
-Do not use sse registers on x86_64
-
-@item -m32, -m64
-Pass command line to the i386/x86_64 cross compiler.
-
-@end table
-
-Note: GCC options @option{-Ox}, @option{-fx} and @option{-mx} are
-ignored.
-@c man end
-
-@c man begin ENVIRONMENT
-Environment variables that affect how tcc operates.
-
-@table @option
-
-@item CPATH
-@item C_INCLUDE_PATH
-A colon-separated list of directories searched for include files,
-directories given with @option{-I} are searched first.
-
-@item LIBRARY_PATH
-A colon-separated list of directories searched for libraries for the
-@option{-l} option, directories given with @option{-L} are searched first.
-
-@end table
-
-@c man end
-
-@ignore
-
-@setfilename tcc
-@settitle Tiny C Compiler
-
-@c man begin SEEALSO
-cpp(1),
-gcc(1)
-@c man end
-
-@c man begin AUTHOR
-Fabrice Bellard
-@c man end
-
-@end ignore
-
-@node Clang
-@chapter C language support
-
-@section ANSI C
-
-TCC implements all the ANSI C standard, including structure bit fields
-and floating point numbers (@code{long double}, @code{double}, and
-@code{float} fully supported).
-
-@section ISOC99 extensions
-
-TCC implements many features of the new C standard: ISO C99. Currently
-missing items are: complex and imaginary numbers.
-
-Currently implemented ISOC99 features:
-
-@itemize
-
-@item variable length arrays.
-
-@item 64 bit @code{long long} types are fully supported.
-
-@item The boolean type @code{_Bool} is supported.
-
-@item @code{__func__} is a string variable containing the current
-function name.
-
-@item Variadic macros: @code{__VA_ARGS__} can be used for
-   function-like macros:
-@example
-    #define dprintf(level, __VA_ARGS__) printf(__VA_ARGS__)
-@end example
-
-@noindent
-@code{dprintf} can then be used with a variable number of parameters.
-
-@item Declarations can appear anywhere in a block (as in C++).
-
-@item Array and struct/union elements can be initialized in any order by
-  using designators:
-@example
-    struct @{ int x, y; @} st[10] = @{ [0].x = 1, [0].y = 2 @};
-
-    int tab[10] = @{ 1, 2, [5] = 5, [9] = 9@};
-@end example
-    
-@item Compound initializers are supported:
-@example
-    int *p = (int [])@{ 1, 2, 3 @};
-@end example
-to initialize a pointer pointing to an initialized array. The same
-works for structures and strings.
-
-@item Hexadecimal floating point constants are supported:
-@example
-          double d = 0x1234p10;
-@end example
-
-@noindent
-is the same as writing 
-@example
-          double d = 4771840.0;
-@end example
-
-@item @code{inline} keyword is ignored.
-
-@item @code{restrict} keyword is ignored.
-@end itemize
-
-@section GNU C extensions
-
-TCC implements some GNU C extensions:
-
-@itemize
-
-@item array designators can be used without '=': 
-@example
-    int a[10] = @{ [0] 1, [5] 2, 3, 4 @};
-@end example
-
-@item Structure field designators can be a label: 
-@example
-    struct @{ int x, y; @} st = @{ x: 1, y: 1@};
-@end example
-instead of
-@example
-    struct @{ int x, y; @} st = @{ .x = 1, .y = 1@};
-@end example
-
-@item @code{\e} is ASCII character 27.
-
-@item case ranges : ranges can be used in @code{case}s:
-@example
-    switch(a) @{
-    case 1 @dots{} 9:
-          printf("range 1 to 9\n");
-          break;
-    default:
-          printf("unexpected\n");
-          break;
-    @}
-@end example
-
-@cindex aligned attribute
-@cindex packed attribute
-@cindex section attribute
-@cindex unused attribute
-@cindex cdecl attribute
-@cindex stdcall attribute
-@cindex regparm attribute
-@cindex dllexport attribute
-
-@item The keyword @code{__attribute__} is handled to specify variable or
-function attributes. The following attributes are supported:
-  @itemize
-
-  @item @code{aligned(n)}: align a variable or a structure field to n bytes
-(must be a power of two).
-
-  @item @code{packed}: force alignment of a variable or a structure field to
-  1.
-
-  @item @code{section(name)}: generate function or data in assembly section
-name (name is a string containing the section name) instead of the default
-section.
-
-  @item @code{unused}: specify that the variable or the function is unused.
-
-  @item @code{cdecl}: use standard C calling convention (default).
-
-  @item @code{stdcall}: use Pascal-like calling convention.
-
-  @item @code{regparm(n)}: use fast i386 calling convention. @var{n} must be
-between 1 and 3. The first @var{n} function parameters are respectively put in
-registers @code{%eax}, @code{%edx} and @code{%ecx}.
-
-  @item @code{dllexport}: export function from dll/executable (win32 only)
-
-  @end itemize
-
-Here are some examples:
-@example
-    int a __attribute__ ((aligned(8), section(".mysection")));
-@end example
-
-@noindent
-align variable @code{a} to 8 bytes and put it in section @code{.mysection}.
-
-@example
-    int my_add(int a, int b) __attribute__ ((section(".mycodesection"))) 
-    @{
-        return a + b;
-    @}
-@end example
-
-@noindent
-generate function @code{my_add} in section @code{.mycodesection}.
-
-@item GNU style variadic macros:
-@example
-    #define dprintf(fmt, args@dots{}) printf(fmt, ## args)
-
-    dprintf("no arg\n");
-    dprintf("one arg %d\n", 1);
-@end example
-
-@item @code{__FUNCTION__} is interpreted as C99 @code{__func__} 
-(so it has not exactly the same semantics as string literal GNUC
-where it is a string literal).
-
-@item The @code{__alignof__} keyword can be used as @code{sizeof} 
-to get the alignment of a type or an expression.
-
-@item The @code{typeof(x)} returns the type of @code{x}. 
-@code{x} is an expression or a type.
-
-@item Computed gotos: @code{&&label} returns a pointer of type 
-@code{void *} on the goto label @code{label}. @code{goto *expr} can be
-used to jump on the pointer resulting from @code{expr}.
-
-@item Inline assembly with asm instruction:
-@cindex inline assembly
-@cindex assembly, inline
-@cindex __asm__
-@example
-static inline void * my_memcpy(void * to, const void * from, size_t n)
-@{
-int d0, d1, d2;
-__asm__ __volatile__(
-        "rep ; movsl\n\t"
-        "testb $2,%b4\n\t"
-        "je 1f\n\t"
-        "movsw\n"
-        "1:\ttestb $1,%b4\n\t"
-        "je 2f\n\t"
-        "movsb\n"
-        "2:"
-        : "=&c" (d0), "=&D" (d1), "=&S" (d2)
-        :"0" (n/4), "q" (n),"1" ((long) to),"2" ((long) from)
-        : "memory");
-return (to);
-@}
-@end example
-
-@noindent
-@cindex gas
-TCC includes its own x86 inline assembler with a @code{gas}-like (GNU
-assembler) syntax. No intermediate files are generated. GCC 3.x named
-operands are supported.
-
-@item @code{__builtin_types_compatible_p()} and @code{__builtin_constant_p()} 
-are supported.
-
-@item @code{#pragma pack} is supported for win32 compatibility.
-
-@end itemize
-
-@section TinyCC extensions
-
-@itemize
-
-@item @code{__TINYC__} is a predefined macro to indicate that you use TCC.
-
-@item @code{#!} at the start of a line is ignored to allow scripting.
-
-@item Binary digits can be entered (@code{0b101} instead of
-@code{5}).
-
-@item @code{__BOUNDS_CHECKING_ON} is defined if bound checking is activated.
-
-@end itemize
-
-@node asm
-@chapter TinyCC Assembler
-
-Since version 0.9.16, TinyCC integrates its own assembler. TinyCC
-assembler supports a gas-like syntax (GNU assembler). You can
-deactivate assembler support if you want a smaller TinyCC executable
-(the C compiler does not rely on the assembler).
-
-TinyCC Assembler is used to handle files with @file{.S} (C
-preprocessed assembler) and @file{.s} extensions. It is also used to
-handle the GNU inline assembler with the @code{asm} keyword.
-
-@section Syntax
-
-TinyCC Assembler supports most of the gas syntax. The tokens are the
-same as C.
-
-@itemize
-
-@item C and C++ comments are supported.
-
-@item Identifiers are the same as C, so you cannot use '.' or '$'.
-
-@item Only 32 bit integer numbers are supported.
-
-@end itemize
-
-@section Expressions
-
-@itemize
-
-@item Integers in decimal, octal and hexa are supported.
-
-@item Unary operators: +, -, ~.
-
-@item Binary operators in decreasing priority order:
-
-@enumerate
-@item *, /, %
-@item &, |, ^
-@item +, -
-@end enumerate
-
-@item A value is either an absolute number or a label plus an offset. 
-All operators accept absolute values except '+' and '-'. '+' or '-' can be
-used to add an offset to a label. '-' supports two labels only if they
-are the same or if they are both defined and in the same section.
-
-@end itemize
-
-@section Labels
-
-@itemize
-
-@item All labels are considered as local, except undefined ones.
-
-@item Numeric labels can be used as local @code{gas}-like labels. 
-They can be defined several times in the same source. Use 'b'
-(backward) or 'f' (forward) as suffix to reference them:
-
-@example
- 1:
-      jmp 1b /* jump to '1' label before */
-      jmp 1f /* jump to '1' label after */
- 1:
-@end example
-
-@end itemize
-
-@section Directives
-@cindex assembler directives
-@cindex directives, assembler
-@cindex align directive
-@cindex skip directive
-@cindex space directive
-@cindex byte directive
-@cindex word directive
-@cindex short directive
-@cindex int directive
-@cindex long directive
-@cindex quad directive
-@cindex globl directive
-@cindex global directive
-@cindex section directive
-@cindex text directive
-@cindex data directive
-@cindex bss directive
-@cindex fill directive
-@cindex org directive
-@cindex previous directive
-@cindex string directive
-@cindex asciz directive
-@cindex ascii directive
-
-All directives are preceded by a '.'. The following directives are
-supported:
-
-@itemize
-@item .align n[,value]
-@item .skip n[,value]
-@item .space n[,value]
-@item .byte value1[,...]
-@item .word value1[,...]
-@item .short value1[,...]
-@item .int value1[,...]
-@item .long value1[,...]
-@item .quad immediate_value1[,...]
-@item .globl symbol
-@item .global symbol
-@item .section section
-@item .text
-@item .data
-@item .bss
-@item .fill repeat[,size[,value]]
-@item .org n
-@item .previous
-@item .string string[,...]
-@item .asciz string[,...]
-@item .ascii string[,...]
-@end itemize
-
-@section X86 Assembler
-@cindex assembler
-
-All X86 opcodes are supported. Only ATT syntax is supported (source
-then destination operand order). If no size suffix is given, TinyCC
-tries to guess it from the operand sizes.
-
-Currently, MMX opcodes are supported but not SSE ones.
-
-@node linker
-@chapter TinyCC Linker
-@cindex linker
-
-@section ELF file generation
-@cindex ELF
-
-TCC can directly output relocatable ELF files (object files),
-executable ELF files and dynamic ELF libraries without relying on an
-external linker.
-
-Dynamic ELF libraries can be output but the C compiler does not generate
-position independent code (PIC). It means that the dynamic library
-code generated by TCC cannot be factorized among processes yet.
-
-TCC linker eliminates unreferenced object code in libraries. A single pass is
-done on the object and library list, so the order in which object files and
-libraries are specified is important (same constraint as GNU ld). No grouping
-options (@option{--start-group} and @option{--end-group}) are supported.
-
-@section ELF file loader
-
-TCC can load ELF object files, archives (.a files) and dynamic
-libraries (.so).
-
-@section PE-i386 file generation
-@cindex PE-i386
-
-TCC for Windows supports the native Win32 executable file format (PE-i386).  It
-generates EXE files (console and gui) and DLL files.
-
-For usage on Windows, see also tcc-win32.txt.
-
-@section GNU Linker Scripts
-@cindex scripts, linker
-@cindex linker scripts
-@cindex GROUP, linker command
-@cindex FILE, linker command
-@cindex OUTPUT_FORMAT, linker command
-@cindex TARGET, linker command
-
-Because on many Linux systems some dynamic libraries (such as
-@file{/usr/lib/libc.so}) are in fact GNU ld link scripts (horrible!),
-the TCC linker also supports a subset of GNU ld scripts.
-
-The @code{GROUP} and @code{FILE} commands are supported. @code{OUTPUT_FORMAT}
-and @code{TARGET} are ignored.
-
-Example from @file{/usr/lib/libc.so}:
-@example
-/* GNU ld script
-   Use the shared library, but some functions are only in
-   the static library, so try that secondarily.  */
-GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a )
-@end example
-
-@node Bounds
-@chapter TinyCC Memory and Bound checks
-@cindex bound checks
-@cindex memory checks
-
-This feature is activated with the @option{-b} (@pxref{Invoke}).
-
-Note that pointer size is @emph{unchanged} and that code generated
-with bound checks is @emph{fully compatible} with unchecked
-code. When a pointer comes from unchecked code, it is assumed to be
-valid. Even very obscure C code with casts should work correctly.
-
-For more information about the ideas behind this method, see
-@url{http://www.doc.ic.ac.uk/~phjk/BoundsChecking.html}.
-
-Here are some examples of caught errors:
-
-@table @asis
-
-@item Invalid range with standard string function:
-@example
-@{
-    char tab[10];
-    memset(tab, 0, 11);
-@}
-@end example
-
-@item Out of bounds-error in global or local arrays:
-@example
-@{
-    int tab[10];
-    for(i=0;i<11;i++) @{
-        sum += tab[i];
-    @}
-@}
-@end example
-
-@item Out of bounds-error in malloc'ed data:
-@example
-@{
-    int *tab;
-    tab = malloc(20 * sizeof(int));
-    for(i=0;i<21;i++) @{
-        sum += tab4[i];
-    @}
-    free(tab);
-@}
-@end example
-
-@item Access of freed memory:
-@example
-@{
-    int *tab;
-    tab = malloc(20 * sizeof(int));
-    free(tab);
-    for(i=0;i<20;i++) @{
-        sum += tab4[i];
-    @}
-@}
-@end example
-
-@item Double free:
-@example
-@{
-    int *tab;
-    tab = malloc(20 * sizeof(int));
-    free(tab);
-    free(tab);
-@}
-@end example
-
-@end table
-
-@node Libtcc
-@chapter The @code{libtcc} library
-
-The @code{libtcc} library enables you to use TCC as a backend for
-dynamic code generation. 
-
-Read the @file{libtcc.h} to have an overview of the API. Read
-@file{libtcc_test.c} to have a very simple example.
-
-The idea consists in giving a C string containing the program you want
-to compile directly to @code{libtcc}. Then you can access to any global
-symbol (function or variable) defined.
-
-@node devel
-@chapter Developer's guide
-
-This chapter gives some hints to understand how TCC works. You can skip
-it if you do not intend to modify the TCC code.
-
-@section File reading
-
-The @code{BufferedFile} structure contains the context needed to read a
-file, including the current line number. @code{tcc_open()} opens a new
-file and @code{tcc_close()} closes it. @code{inp()} returns the next
-character.
-
-@section Lexer
-
-@code{next()} reads the next token in the current
-file. @code{next_nomacro()} reads the next token without macro
-expansion.
-
-@code{tok} contains the current token (see @code{TOK_xxx})
-constants. Identifiers and keywords are also keywords. @code{tokc}
-contains additional infos about the token (for example a constant value
-if number or string token).
-
-@section Parser
-
-The parser is hardcoded (yacc is not necessary). It does only one pass,
-except:
-
-@itemize
-
-@item For initialized arrays with unknown size, a first pass 
-is done to count the number of elements.
-
-@item For architectures where arguments are evaluated in 
-reverse order, a first pass is done to reverse the argument order.
-
-@end itemize
-
-@section Types
-
-The types are stored in a single 'int' variable. It was chosen in the
-first stages of development when tcc was much simpler. Now, it may not
-be the best solution.
-
-@example
-#define VT_INT        0  /* integer type */
-#define VT_BYTE       1  /* signed byte type */
-#define VT_SHORT      2  /* short type */
-#define VT_VOID       3  /* void type */
-#define VT_PTR        4  /* pointer */
-#define VT_ENUM       5  /* enum definition */
-#define VT_FUNC       6  /* function type */
-#define VT_STRUCT     7  /* struct/union definition */
-#define VT_FLOAT      8  /* IEEE float */
-#define VT_DOUBLE     9  /* IEEE double */
-#define VT_LDOUBLE   10  /* IEEE long double */
-#define VT_BOOL      11  /* ISOC99 boolean type */
-#define VT_LLONG     12  /* 64 bit integer */
-#define VT_LONG      13  /* long integer (NEVER USED as type, only
-                            during parsing) */
-#define VT_BTYPE      0x000f /* mask for basic type */
-#define VT_UNSIGNED   0x0010  /* unsigned type */
-#define VT_ARRAY      0x0020  /* array type (also has VT_PTR) */
-#define VT_VLA        0x20000 /* VLA type (also has VT_PTR and VT_ARRAY) */
-#define VT_BITFIELD   0x0040  /* bitfield modifier */
-#define VT_CONSTANT   0x0800  /* const modifier */
-#define VT_VOLATILE   0x1000  /* volatile modifier */
-#define VT_DEFSIGN    0x2000  /* signed type */
-
-#define VT_STRUCT_SHIFT 18   /* structure/enum name shift (14 bits left) */
-@end example
-
-When a reference to another type is needed (for pointers, functions and
-structures), the @code{32 - VT_STRUCT_SHIFT} high order bits are used to
-store an identifier reference.
-
-The @code{VT_UNSIGNED} flag can be set for chars, shorts, ints and long
-longs.
-
-Arrays are considered as pointers @code{VT_PTR} with the flag
-@code{VT_ARRAY} set. Variable length arrays are considered as special
-arrays and have flag @code{VT_VLA} set instead of @code{VT_ARRAY}.
-
-The @code{VT_BITFIELD} flag can be set for chars, shorts, ints and long
-longs. If it is set, then the bitfield position is stored from bits
-VT_STRUCT_SHIFT to VT_STRUCT_SHIFT + 5 and the bit field size is stored
-from bits VT_STRUCT_SHIFT + 6 to VT_STRUCT_SHIFT + 11.
-
-@code{VT_LONG} is never used except during parsing.
-
-During parsing, the storage of an object is also stored in the type
-integer:
-
-@example
-#define VT_EXTERN  0x00000080  /* extern definition */
-#define VT_STATIC  0x00000100  /* static variable */
-#define VT_TYPEDEF 0x00000200  /* typedef definition */
-#define VT_INLINE  0x00000400  /* inline definition */
-#define VT_IMPORT  0x00004000  /* win32: extern data imported from dll */
-#define VT_EXPORT  0x00008000  /* win32: data exported from dll */
-#define VT_WEAK    0x00010000  /* win32: data exported from dll */
-@end example
-
-@section Symbols
-
-All symbols are stored in hashed symbol stacks. Each symbol stack
-contains @code{Sym} structures.
-
-@code{Sym.v} contains the symbol name (remember
-an identifier is also a token, so a string is never necessary to store
-it). @code{Sym.t} gives the type of the symbol. @code{Sym.r} is usually
-the register in which the corresponding variable is stored. @code{Sym.c} is
-usually a constant associated to the symbol like its address for normal
-symbols, and the number of entries for symbols representing arrays.
-Variable length array types use @code{Sym.c} as a location on the stack
-which holds the runtime sizeof for the type.
-
-Four main symbol stacks are defined:
-
-@table @code
-
-@item define_stack
-for the macros (@code{#define}s).
-
-@item global_stack
-for the global variables, functions and types.
-
-@item local_stack
-for the local variables, functions and types.
-
-@item global_label_stack
-for the local labels (for @code{goto}).
-
-@item label_stack
-for GCC block local labels (see the @code{__label__} keyword).
-
-@end table
-
-@code{sym_push()} is used to add a new symbol in the local symbol
-stack. If no local symbol stack is active, it is added in the global
-symbol stack.
-
-@code{sym_pop(st,b)} pops symbols from the symbol stack @var{st} until
-the symbol @var{b} is on the top of stack. If @var{b} is NULL, the stack
-is emptied.
-
-@code{sym_find(v)} return the symbol associated to the identifier
-@var{v}. The local stack is searched first from top to bottom, then the
-global stack.
-
-@section Sections
-
-The generated code and data are written in sections. The structure
-@code{Section} contains all the necessary information for a given
-section. @code{new_section()} creates a new section. ELF file semantics
-is assumed for each section.
-
-The following sections are predefined:
-
-@table @code
-
-@item text_section
-is the section containing the generated code. @var{ind} contains the
-current position in the code section.
-
-@item data_section
-contains initialized data
-
-@item bss_section
-contains uninitialized data
-
-@item bounds_section
-@itemx lbounds_section
-are used when bound checking is activated
-
-@item stab_section
-@itemx stabstr_section
-are used when debugging is active to store debug information
-
-@item symtab_section
-@itemx strtab_section
-contain the exported symbols (currently only used for debugging).
-
-@end table
-
-@section Code generation
-@cindex code generation
-
-@subsection Introduction
-
-The TCC code generator directly generates linked binary code in one
-pass. It is rather unusual these days (see gcc for example which
-generates text assembly), but it can be very fast and surprisingly
-little complicated.
-
-The TCC code generator is register based. Optimization is only done at
-the expression level. No intermediate representation of expression is
-kept except the current values stored in the @emph{value stack}.
-
-On x86, three temporary registers are used. When more registers are
-needed, one register is spilled into a new temporary variable on the stack.
-
-@subsection The value stack
-@cindex value stack, introduction
-
-When an expression is parsed, its value is pushed on the value stack
-(@var{vstack}). The top of the value stack is @var{vtop}. Each value
-stack entry is the structure @code{SValue}.
-
-@code{SValue.t} is the type. @code{SValue.r} indicates how the value is
-currently stored in the generated code. It is usually a CPU register
-index (@code{REG_xxx} constants), but additional values and flags are
-defined:
-
-@example
-#define VT_CONST     0x00f0
-#define VT_LLOCAL    0x00f1
-#define VT_LOCAL     0x00f2
-#define VT_CMP       0x00f3
-#define VT_JMP       0x00f4
-#define VT_JMPI      0x00f5
-#define VT_LVAL      0x0100
-#define VT_SYM       0x0200
-#define VT_MUSTCAST  0x0400
-#define VT_MUSTBOUND 0x0800
-#define VT_BOUNDED   0x8000
-#define VT_LVAL_BYTE     0x1000
-#define VT_LVAL_SHORT    0x2000
-#define VT_LVAL_UNSIGNED 0x4000
-#define VT_LVAL_TYPE     (VT_LVAL_BYTE | VT_LVAL_SHORT | VT_LVAL_UNSIGNED)
-@end example
-
-@table @code
-
-@item VT_CONST
-indicates that the value is a constant. It is stored in the union
-@code{SValue.c}, depending on its type.
-
-@item VT_LOCAL
-indicates a local variable pointer at offset @code{SValue.c.i} in the
-stack.
-
-@item VT_CMP
-indicates that the value is actually stored in the CPU flags (i.e. the
-value is the consequence of a test). The value is either 0 or 1. The
-actual CPU flags used is indicated in @code{SValue.c.i}. 
-
-If any code is generated which destroys the CPU flags, this value MUST be
-put in a normal register.
-
-@item VT_JMP
-@itemx VT_JMPI
-indicates that the value is the consequence of a conditional jump. For VT_JMP,
-it is 1 if the jump is taken, 0 otherwise. For VT_JMPI it is inverted.
-
-These values are used to compile the @code{||} and @code{&&} logical
-operators.
-
-If any code is generated, this value MUST be put in a normal
-register. Otherwise, the generated code won't be executed if the jump is
-taken.
-
-@item VT_LVAL
-is a flag indicating that the value is actually an lvalue (left value of
-an assignment). It means that the value stored is actually a pointer to
-the wanted value. 
-
-Understanding the use @code{VT_LVAL} is very important if you want to
-understand how TCC works.
-
-@item VT_LVAL_BYTE
-@itemx VT_LVAL_SHORT
-@itemx VT_LVAL_UNSIGNED
-if the lvalue has an integer type, then these flags give its real
-type. The type alone is not enough in case of cast optimisations.
-
-@item VT_LLOCAL
-is a saved lvalue on the stack. @code{VT_LVAL} must also be set with
-@code{VT_LLOCAL}. @code{VT_LLOCAL} can arise when a @code{VT_LVAL} in
-a register has to be saved to the stack, or it can come from an
-architecture-specific calling convention.
-
-@item VT_MUSTCAST
-indicates that a cast to the value type must be performed if the value
-is used (lazy casting).
-
-@item VT_SYM
-indicates that the symbol @code{SValue.sym} must be added to the constant.
-
-@item VT_MUSTBOUND
-@itemx VT_BOUNDED
-are only used for optional bound checking.
-
-@end table
-
-@subsection Manipulating the value stack
-@cindex value stack
-
-@code{vsetc()} and @code{vset()} pushes a new value on the value
-stack. If the previous @var{vtop} was stored in a very unsafe place(for
-example in the CPU flags), then some code is generated to put the
-previous @var{vtop} in a safe storage.
-
-@code{vpop()} pops @var{vtop}. In some cases, it also generates cleanup
-code (for example if stacked floating point registers are used as on
-x86).
-
-The @code{gv(rc)} function generates code to evaluate @var{vtop} (the
-top value of the stack) into registers. @var{rc} selects in which
-register class the value should be put. @code{gv()} is the @emph{most
-important function} of the code generator.
-
-@code{gv2()} is the same as @code{gv()} but for the top two stack
-entries.
-
-@subsection CPU dependent code generation
-@cindex CPU dependent
-See the @file{i386-gen.c} file to have an example.
-
-@table @code
-
-@item load()
-must generate the code needed to load a stack value into a register.
-
-@item store()
-must generate the code needed to store a register into a stack value
-lvalue.
-
-@item gfunc_start()
-@itemx gfunc_param()
-@itemx gfunc_call()
-should generate a function call
-
-@item gfunc_prolog()
-@itemx gfunc_epilog()
-should generate a function prolog/epilog.
-
-@item gen_opi(op)
-must generate the binary integer operation @var{op} on the two top
-entries of the stack which are guaranteed to contain integer types.
-
-The result value should be put on the stack.
-
-@item gen_opf(op)
-same as @code{gen_opi()} for floating point operations. The two top
-entries of the stack are guaranteed to contain floating point values of
-same types.
-
-@item gen_cvt_itof()
-integer to floating point conversion.
-
-@item gen_cvt_ftoi()
-floating point to integer conversion.
-
-@item gen_cvt_ftof()
-floating point to floating point of different size conversion.
-
-@item gen_bounded_ptr_add()
-@item gen_bounded_ptr_deref()
-are only used for bounds checking.
-
-@end table
-
-@section Optimizations done
-@cindex optimizations
-@cindex constant propagation
-@cindex strength reduction
-@cindex comparison operators
-@cindex caching processor flags
-@cindex flags, caching
-@cindex jump optimization
-Constant propagation is done for all operations. Multiplications and
-divisions are optimized to shifts when appropriate. Comparison
-operators are optimized by maintaining a special cache for the
-processor flags. &&, || and ! are optimized by maintaining a special
-'jump target' value. No other jump optimization is currently performed
-because it would require to store the code in a more abstract fashion.
-
-@unnumbered Concept Index
-@printindex cp
-
-@bye
-
-@c Local variables:
-@c fill-column: 78
-@c texinfo-column-for-description: 32
-@c End:
diff --git a/tinyc/tcc.c b/tinyc/tcc.c
deleted file mode 100644
index c347a06ea..000000000
--- a/tinyc/tcc.c
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- *  TCC - Tiny C Compiler
- * 
- *  Copyright (c) 2001-2004 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "tcc.h"
-#if ONE_SOURCE
-# include "libtcc.c"
-#endif
-#include "tcctools.c"
-
-static const char help[] =
-    "Tiny C Compiler "TCC_VERSION" - Copyright (C) 2001-2006 Fabrice Bellard\n"
-    "Usage: tcc [options...] [-o outfile] [-c] infile(s)...\n"
-    "       tcc [options...] -run infile [arguments...]\n"
-    "General options:\n"
-    "  -c          compile only - generate an object file\n"
-    "  -o outfile  set output filename\n"
-    "  -run        run compiled source\n"
-    "  -fflag      set or reset (with 'no-' prefix) 'flag' (see tcc -hh)\n"
-    "  -Wwarning   set or reset (with 'no-' prefix) 'warning' (see tcc -hh)\n"
-    "  -w          disable all warnings\n"
-    "  -v -vv      show version, show search paths or loaded files\n"
-    "  -h -hh      show this, show more help\n"
-    "  -bench      show compilation statistics\n"
-    "  -           use stdin pipe as infile\n"
-    "  @listfile   read arguments from listfile\n"
-    "Preprocessor options:\n"
-    "  -Idir       add include path 'dir'\n"
-    "  -Dsym[=val] define 'sym' with value 'val'\n"
-    "  -Usym       undefine 'sym'\n"
-    "  -E          preprocess only\n"
-    "Linker options:\n"
-    "  -Ldir       add library path 'dir'\n"
-    "  -llib       link with dynamic or static library 'lib'\n"
-    "  -r          generate (relocatable) object file\n"
-    "  -shared     generate a shared library/dll\n"
-    "  -rdynamic   export all global symbols to dynamic linker\n"
-    "  -soname     set name for shared library to be used at runtime\n"
-    "  -Wl,-opt[=val]  set linker option (see tcc -hh)\n"
-    "Debugger options:\n"
-    "  -g          generate runtime debug info\n"
-#ifdef CONFIG_TCC_BCHECK
-    "  -b          compile with built-in memory and bounds checker (implies -g)\n"
-#endif
-#ifdef CONFIG_TCC_BACKTRACE
-    "  -bt N       show N callers in stack traces\n"
-#endif
-    "Misc. options:\n"
-    "  -x[c|a|n]   specify type of the next infile\n"
-    "  -nostdinc   do not use standard system include paths\n"
-    "  -nostdlib   do not link with standard crt and libraries\n"
-    "  -Bdir       set tcc's private include/library dir\n"
-    "  -MD         generate dependency file for make\n"
-    "  -MF file    specify dependency file name\n"
-    "  -m32/64     defer to i386/x86_64 cross compiler\n"
-    "Tools:\n"
-    "  create library  : tcc -ar [rcsv] lib.a files\n"
-#ifdef TCC_TARGET_PE
-    "  create def file : tcc -impdef lib.dll [-v] [-o lib.def]\n"
-#endif
-    ;
-
-static const char help2[] =
-    "Tiny C Compiler "TCC_VERSION" - More Options\n"
-    "Special options:\n"
-    "  -P -P1                        with -E: no/alternative #line output\n"
-    "  -dD -dM                       with -E: output #define directives\n"
-    "  -pthread                      same as -D_REENTRANT and -lpthread\n"
-    "  -On                           same as -D__OPTIMIZE__ for n > 0\n"
-    "  -Wp,-opt                      same as -opt\n"
-    "  -include file                 include 'file' above each input file\n"
-    "  -isystem dir                  add 'dir' to system include path\n"
-    "  -static                       link to static libraries (not recommended)\n"
-    "  -dumpversion                  print version\n"
-    "  -print-search-dirs            print search paths\n"
-    "  -dt                           with -run/-E: auto-define 'test_...' macros\n"
-    "Ignored options:\n"
-    "  --param  -pedantic  -pipe  -s  -std  -traditional\n"
-    "-W... warnings:\n"
-    "  all                           turn on some (*) warnings\n"
-    "  error                         stop after first warning\n"
-    "  unsupported                   warn about ignored options, pragmas, etc.\n"
-    "  write-strings                 strings are const\n"
-    "  implicit-function-declaration warn for missing prototype (*)\n"
-    "-f[no-]... flags:\n"
-    "  unsigned-char                 default char is unsigned\n"
-    "  signed-char                   default char is signed\n"
-    "  common                        use common section instead of bss\n"
-    "  leading-underscore            decorate extern symbols\n"
-    "  ms-extensions                 allow anonymous struct in struct\n"
-    "  dollars-in-identifiers        allow '$' in C symbols\n"
-    "-m... target specific options:\n"
-    "  ms-bitfields                  use MSVC bitfield layout\n"
-#ifdef TCC_TARGET_ARM
-    "  float-abi                     hard/softfp on arm\n"
-#endif
-#ifdef TCC_TARGET_X86_64
-    "  no-sse                        disable floats on x86_64\n"
-#endif
-    "-Wl,... linker options:\n"
-    "  -nostdlib                     do not link with standard crt/libs\n"
-    "  -[no-]whole-archive           load lib(s) fully/only as needed\n"
-    "  -export-all-symbols           same as -rdynamic\n"
-    "  -image-base= -Ttext=          set base address of executable\n"
-    "  -section-alignment=           set section alignment in executable\n"
-#ifdef TCC_TARGET_PE
-    "  -file-alignment=              set PE file alignment\n"
-    "  -stack=                       set PE stack reserve\n"
-    "  -large-address-aware          set related PE option\n"
-    "  -subsystem=[console/windows]  set PE subsystem\n"
-    "  -oformat=[pe-* binary]        set executable output format\n"
-    "Predefined macros:\n"
-    "  tcc -E -dM - < nul\n"
-#else
-    "  -rpath=                       set dynamic library search path\n"
-    "  -enable-new-dtags             set DT_RUNPATH instead of DT_RPATH\n"
-    "  -soname=                      set DT_SONAME elf tag\n"
-    "  -Bsymbolic                    set DT_SYMBOLIC elf tag\n"
-    "  -oformat=[elf32/64-* binary]  set executable output format\n"
-    "  -init= -fini= -as-needed -O   (ignored)\n"
-    "Predefined macros:\n"
-    "  tcc -E -dM - < /dev/null\n"
-#endif
-    "See also the manual for more details.\n"
-    ;
-
-static const char version[] =
-    "tcc version "TCC_VERSION" ("
-#ifdef TCC_TARGET_I386
-        "i386"
-#elif defined TCC_TARGET_X86_64
-        "x86_64"
-#elif defined TCC_TARGET_C67
-        "C67"
-#elif defined TCC_TARGET_ARM
-        "ARM"
-#elif defined TCC_TARGET_ARM64
-        "AArch64"
-#endif
-#ifdef TCC_ARM_HARDFLOAT
-        " Hard Float"
-#endif
-#ifdef TCC_TARGET_PE
-        " Windows"
-#elif defined(TCC_TARGET_MACHO)
-        " Darwin"
-#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-        " FreeBSD"
-#else
-        " Linux"
-#endif
-    ")\n"
-    ;
-
-static void print_dirs(const char *msg, char **paths, int nb_paths)
-{
-    int i;
-    printf("%s:\n%s", msg, nb_paths ? "" : "  -\n");
-    for(i = 0; i < nb_paths; i++)
-        printf("  %s\n", paths[i]);
-}
-
-static void print_search_dirs(TCCState *s)
-{
-    printf("install: %s\n", s->tcc_lib_path);
-    /* print_dirs("programs", NULL, 0); */
-    print_dirs("include", s->sysinclude_paths, s->nb_sysinclude_paths);
-    print_dirs("libraries", s->library_paths, s->nb_library_paths);
-#ifndef TCC_TARGET_PE
-    print_dirs("crt", s->crt_paths, s->nb_crt_paths);
-    printf("libtcc1:\n  %s/"TCC_LIBTCC1"\n", s->tcc_lib_path);
-    printf("elfinterp:\n  %s\n",  DEFAULT_ELFINTERP(s));
-#endif
-}
-
-static void set_environment(TCCState *s)
-{
-    char * path;
-
-    path = getenv("C_INCLUDE_PATH");
-    if(path != NULL) {
-        tcc_add_sysinclude_path(s, path);
-    }
-    path = getenv("CPATH");
-    if(path != NULL) {
-        tcc_add_include_path(s, path);
-    }
-    path = getenv("LIBRARY_PATH");
-    if(path != NULL) {
-        tcc_add_library_path(s, path);
-    }
-}
-
-static char *default_outputfile(TCCState *s, const char *first_file)
-{
-    char buf[1024];
-    char *ext;
-    const char *name = "a";
-
-    if (first_file && strcmp(first_file, "-"))
-        name = tcc_basename(first_file);
-    snprintf(buf, sizeof(buf), "%s", name);
-    ext = tcc_fileextension(buf);
-#ifdef TCC_TARGET_PE
-    if (s->output_type == TCC_OUTPUT_DLL)
-        strcpy(ext, ".dll");
-    else
-    if (s->output_type == TCC_OUTPUT_EXE)
-        strcpy(ext, ".exe");
-    else
-#endif
-    if (s->output_type == TCC_OUTPUT_OBJ && !s->option_r && *ext)
-        strcpy(ext, ".o");
-    else
-        strcpy(buf, "a.out");
-    return tcc_strdup(buf);
-}
-
-static unsigned getclock_ms(void)
-{
-#ifdef _WIN32
-    return GetTickCount();
-#else
-    struct timeval tv;
-    gettimeofday(&tv, NULL);
-    return tv.tv_sec*1000 + (tv.tv_usec+500)/1000;
-#endif
-}
-
-int main(int argc0, char **argv0)
-{
-    TCCState *s;
-    int ret, opt, n = 0, t = 0;
-    unsigned start_time = 0;
-    const char *first_file;
-    int argc; char **argv;
-    FILE *ppfp = stdout;
-
-redo:
-    argc = argc0, argv = argv0;
-    s = tcc_new();
-    opt = tcc_parse_args(s, &argc, &argv, 1);
-
-    if ((n | t) == 0) {
-        if (opt == OPT_HELP)
-            return printf(help), 1;
-        if (opt == OPT_HELP2)
-            return printf(help2), 1;
-        if (opt == OPT_M32 || opt == OPT_M64)
-            tcc_tool_cross(s, argv, opt); /* never returns */
-        if (s->verbose)
-            printf(version);
-        if (opt == OPT_AR)
-            return tcc_tool_ar(s, argc, argv);
-#ifdef TCC_TARGET_PE
-        if (opt == OPT_IMPDEF)
-            return tcc_tool_impdef(s, argc, argv);
-#endif
-        if (opt == OPT_V)
-            return 0;
-        if (opt == OPT_PRINT_DIRS) {
-            /* initialize search dirs */
-            set_environment(s);
-            tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
-            print_search_dirs(s);
-            return 0;
-        }
-
-        n = s->nb_files;
-        if (n == 0)
-            tcc_error("no input files\n");
-
-        if (s->output_type == TCC_OUTPUT_PREPROCESS) {
-            if (s->outfile) {
-                ppfp = fopen(s->outfile, "w");
-                if (!ppfp)
-                    tcc_error("could not write '%s'", s->outfile);
-            }
-        } else if (s->output_type == TCC_OUTPUT_OBJ && !s->option_r) {
-            if (s->nb_libraries)
-                tcc_error("cannot specify libraries with -c");
-            if (n > 1 && s->outfile)
-                tcc_error("cannot specify output file with -c many files");
-        } else {
-            if (s->option_pthread)
-                tcc_set_options(s, "-lpthread");
-        }
-
-        if (s->do_bench)
-            start_time = getclock_ms();
-    }
-
-    set_environment(s);
-    if (s->output_type == 0)
-        s->output_type = TCC_OUTPUT_EXE;
-    tcc_set_output_type(s, s->output_type);
-    s->ppfp = ppfp;
-
-    if ((s->output_type == TCC_OUTPUT_MEMORY
-      || s->output_type == TCC_OUTPUT_PREPROCESS) && (s->dflag & 16))
-        s->dflag |= t ? 32 : 0, s->run_test = ++t, n = s->nb_files;
-
-    /* compile or add each files or library */
-    for (first_file = NULL, ret = 0;;) {
-        struct filespec *f = s->files[s->nb_files - n];
-        s->filetype = f->type;
-        s->alacarte_link = f->alacarte;
-        if (f->type == AFF_TYPE_LIB) {
-            if (tcc_add_library_err(s, f->name) < 0)
-                ret = 1;
-        } else {
-            if (1 == s->verbose)
-                printf("-> %s\n", f->name);
-            if (!first_file)
-                first_file = f->name;
-            if (tcc_add_file(s, f->name) < 0)
-                ret = 1;
-        }
-        s->filetype = 0;
-        s->alacarte_link = 1;
-        if (--n == 0 || ret
-            || (s->output_type == TCC_OUTPUT_OBJ && !s->option_r))
-            break;
-    }
-
-    if (s->run_test) {
-        t = 0;
-    } else if (s->output_type == TCC_OUTPUT_PREPROCESS) {
-        ;
-    } else if (0 == ret) {
-        if (s->output_type == TCC_OUTPUT_MEMORY) {
-#ifdef TCC_IS_NATIVE
-            ret = tcc_run(s, argc, argv);
-#endif
-        } else {
-            if (!s->outfile)
-                s->outfile = default_outputfile(s, first_file);
-            if (tcc_output_file(s, s->outfile))
-                ret = 1;
-            else if (s->gen_deps)
-                gen_makedeps(s, s->outfile, s->deps_outfile);
-        }
-    }
-
-    if (s->do_bench && (n | t | ret) == 0)
-        tcc_print_stats(s, getclock_ms() - start_time);
-    tcc_delete(s);
-    if (ret == 0 && n)
-        goto redo; /* compile more files with -c */
-    if (t)
-        goto redo; /* run more tests with -dt -run */
-    if (ppfp && ppfp != stdout)
-        fclose(ppfp);
-    return ret;
-}
diff --git a/tinyc/tcc.h b/tinyc/tcc.h
deleted file mode 100644
index da26298fb..000000000
--- a/tinyc/tcc.h
+++ /dev/null
@@ -1,1657 +0,0 @@
-/*
- *  TCC - Tiny C Compiler
- *
- *  Copyright (c) 2001-2004 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#ifndef _TCC_H
-#define _TCC_H
-
-#define _GNU_SOURCE
-#include "config.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-#include <errno.h>
-#include <math.h>
-#include <fcntl.h>
-#include <setjmp.h>
-#include <time.h>
-
-#ifndef _WIN32
-# include <unistd.h>
-# include <sys/time.h>
-# ifndef CONFIG_TCC_STATIC
-#  include <dlfcn.h>
-# endif
-/* XXX: need to define this to use them in non ISOC99 context */
-extern float strtof (const char *__nptr, char **__endptr);
-extern long double strtold (const char *__nptr, char **__endptr);
-#endif
-
-#ifdef _WIN32
-# include <windows.h>
-# include <io.h> /* open, close etc. */
-# include <direct.h> /* getcwd */
-# ifdef __GNUC__
-#  include <stdint.h>
-# endif
-# define inline __inline
-# define snprintf _snprintf
-# define vsnprintf _vsnprintf
-# ifndef __GNUC__
-#  define strtold (long double)strtod
-#  define strtof (float)strtod
-#  define strtoll _strtoi64
-#  define strtoull _strtoui64
-# endif
-# ifdef LIBTCC_AS_DLL
-#  define LIBTCCAPI __declspec(dllexport)
-#  define PUB_FUNC LIBTCCAPI
-# endif
-# define inp next_inp /* inp is an intrinsic on msvc/mingw */
-# ifdef _MSC_VER
-#  pragma warning (disable : 4244)  // conversion from 'uint64_t' to 'int', possible loss of data
-#  pragma warning (disable : 4267)  // conversion from 'size_t' to 'int', possible loss of data
-#  pragma warning (disable : 4996)  // The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name
-#  pragma warning (disable : 4018)  // signed/unsigned mismatch
-#  pragma warning (disable : 4146)  // unary minus operator applied to unsigned type, result still unsigned
-#  define ssize_t intptr_t
-# endif
-# undef CONFIG_TCC_STATIC
-#endif
-
-#ifndef O_BINARY
-# define O_BINARY 0
-#endif
-
-#ifndef offsetof
-#define offsetof(type, field) ((size_t) &((type *)0)->field)
-#endif
-
-#ifndef countof
-#define countof(tab) (sizeof(tab) / sizeof((tab)[0]))
-#endif
-
-#ifdef _MSC_VER
-# define NORETURN __declspec(noreturn)
-# define ALIGNED(x) __declspec(align(x))
-#else
-# define NORETURN __attribute__((noreturn))
-# define ALIGNED(x) __attribute__((aligned(x)))
-#endif
-
-#ifdef _WIN32
-# define IS_DIRSEP(c) (c == '/' || c == '\\')
-# define IS_ABSPATH(p) (IS_DIRSEP(p[0]) || (p[0] && p[1] == ':' && IS_DIRSEP(p[2])))
-# define PATHCMP stricmp
-# define PATHSEP ";"
-#else
-# define IS_DIRSEP(c) (c == '/')
-# define IS_ABSPATH(p) IS_DIRSEP(p[0])
-# define PATHCMP strcmp
-# define PATHSEP ":"
-#endif
-
-/* -------------------------------------------- */
-
-/* parser debug */
-/* #define PARSE_DEBUG */
-/* preprocessor debug */
-/* #define PP_DEBUG */
-/* include file debug */
-/* #define INC_DEBUG */
-/* memory leak debug */
-/* #define MEM_DEBUG */
-/* assembler debug */
-/* #define ASM_DEBUG */
-
-/* target selection */
-/* #define TCC_TARGET_I386   *//* i386 code generator */
-/* #define TCC_TARGET_X86_64 *//* x86-64 code generator */
-/* #define TCC_TARGET_ARM    *//* ARMv4 code generator */
-/* #define TCC_TARGET_ARM64  *//* ARMv8 code generator */
-/* #define TCC_TARGET_C67    *//* TMS320C67xx code generator */
-
-/* default target is I386 */
-#if !defined(TCC_TARGET_I386) && !defined(TCC_TARGET_ARM) && \
-    !defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_C67) && \
-    !defined(TCC_TARGET_X86_64)
-# if defined __x86_64__ || defined _AMD64_
-#  define TCC_TARGET_X86_64
-# elif defined __arm__
-#  define TCC_TARGET_ARM
-#  define TCC_ARM_EABI
-#  define TCC_ARM_HARDFLOAT
-# elif defined __aarch64__
-#  define TCC_TARGET_ARM64
-# else
-#  define TCC_TARGET_I386
-# endif
-# ifdef _WIN32
-#  define TCC_TARGET_PE 1
-# endif
-#endif
-
-/* only native compiler supports -run */
-#if defined _WIN32 == defined TCC_TARGET_PE
-# if (defined __i386__ || defined _X86_) && defined TCC_TARGET_I386
-#  define TCC_IS_NATIVE
-# elif (defined __x86_64__ || defined _AMD64_) && defined TCC_TARGET_X86_64
-#  define TCC_IS_NATIVE
-# elif defined __arm__ && defined TCC_TARGET_ARM
-#  define TCC_IS_NATIVE
-# elif defined __aarch64__ && defined TCC_TARGET_ARM64
-#  define TCC_IS_NATIVE
-# endif
-#endif
-
-#if defined TCC_IS_NATIVE && !defined CONFIG_TCCBOOT
-# define CONFIG_TCC_BACKTRACE
-# if (defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64) \
-  && !defined TCC_UCLIBC && !defined TCC_MUSL
-# define CONFIG_TCC_BCHECK /* enable bound checking code */
-# endif
-#endif
-
-/* ------------ path configuration ------------ */
-
-#ifndef CONFIG_SYSROOT
-# define CONFIG_SYSROOT ""
-#endif
-#ifndef CONFIG_TCCDIR
-# define CONFIG_TCCDIR "/usr/local/lib/tcc"
-#endif
-#ifndef CONFIG_LDDIR
-# define CONFIG_LDDIR "lib"
-#endif
-#ifdef CONFIG_TRIPLET
-# define USE_TRIPLET(s) s "/" CONFIG_TRIPLET
-# define ALSO_TRIPLET(s) USE_TRIPLET(s) ":" s
-#else
-# define USE_TRIPLET(s) s
-# define ALSO_TRIPLET(s) s
-#endif
-
-/* path to find crt1.o, crti.o and crtn.o */
-#ifndef CONFIG_TCC_CRTPREFIX
-# define CONFIG_TCC_CRTPREFIX USE_TRIPLET(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR)
-#endif
-
-/* Below: {B} is substituted by CONFIG_TCCDIR (rsp. -B option) */
-
-/* system include paths */
-#ifndef CONFIG_TCC_SYSINCLUDEPATHS
-# ifdef TCC_TARGET_PE
-#  define CONFIG_TCC_SYSINCLUDEPATHS "{B}/include"PATHSEP"{B}/include/winapi"
-# else
-#  define CONFIG_TCC_SYSINCLUDEPATHS \
-        "{B}/include" \
-    ":" ALSO_TRIPLET(CONFIG_SYSROOT "/usr/local/include") \
-    ":" ALSO_TRIPLET(CONFIG_SYSROOT "/usr/include")
-# endif
-#endif
-
-/* library search paths */
-#ifndef CONFIG_TCC_LIBPATHS
-# ifdef TCC_TARGET_PE
-#  define CONFIG_TCC_LIBPATHS "{B}/lib"
-# else
-#  define CONFIG_TCC_LIBPATHS \
-        ALSO_TRIPLET(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR) \
-    ":" ALSO_TRIPLET(CONFIG_SYSROOT "/" CONFIG_LDDIR) \
-    ":" ALSO_TRIPLET(CONFIG_SYSROOT "/usr/local/" CONFIG_LDDIR)
-# endif
-#endif
-
-/* name of ELF interpreter */
-#ifndef CONFIG_TCC_ELFINTERP
-# if defined __FreeBSD__
-#  define CONFIG_TCC_ELFINTERP "/libexec/ld-elf.so.1"
-# elif defined __FreeBSD_kernel__
-#  if defined(TCC_TARGET_X86_64)
-#   define CONFIG_TCC_ELFINTERP "/lib/ld-kfreebsd-x86-64.so.1"
-#  else
-#   define CONFIG_TCC_ELFINTERP "/lib/ld.so.1"
-#  endif
-# elif defined __DragonFly__
-#  define CONFIG_TCC_ELFINTERP "/usr/libexec/ld-elf.so.2"
-# elif defined __NetBSD__
-#  define CONFIG_TCC_ELFINTERP "/usr/libexec/ld.elf_so"
-# elif defined __GNU__
-#  define CONFIG_TCC_ELFINTERP "/lib/ld.so"
-# elif defined(TCC_TARGET_PE)
-#  define CONFIG_TCC_ELFINTERP "-"
-# elif defined(TCC_UCLIBC)
-#  define CONFIG_TCC_ELFINTERP "/lib/ld-uClibc.so.0" /* is there a uClibc for x86_64 ? */
-# elif defined TCC_TARGET_ARM64
-#  if defined(TCC_MUSL)
-#   define CONFIG_TCC_ELFINTERP "/lib/ld-musl-aarch64.so.1"
-#  else
-#   define CONFIG_TCC_ELFINTERP "/lib/ld-linux-aarch64.so.1"
-#  endif
-# elif defined(TCC_TARGET_X86_64)
-#  if defined(TCC_MUSL)
-#   define CONFIG_TCC_ELFINTERP "/lib/ld-musl-x86_64.so.1"
-#  else
-#   define CONFIG_TCC_ELFINTERP "/lib64/ld-linux-x86-64.so.2"
-#  endif
-# elif !defined(TCC_ARM_EABI)
-#  if defined(TCC_MUSL)
-#   define CONFIG_TCC_ELFINTERP "/lib/ld-musl-arm.so.1"
-#  else
-#   define CONFIG_TCC_ELFINTERP "/lib/ld-linux.so.2"
-#  endif
-# endif
-#endif
-
-/* var elf_interp dans *-gen.c */
-#ifdef CONFIG_TCC_ELFINTERP
-# define DEFAULT_ELFINTERP(s) CONFIG_TCC_ELFINTERP
-#else
-# define DEFAULT_ELFINTERP(s) default_elfinterp(s)
-#endif
-
-/* (target specific) libtcc1.a */
-#ifndef TCC_LIBTCC1
-# define TCC_LIBTCC1 "libtcc1.a"
-#endif
-
-/* library to use with CONFIG_USE_LIBGCC instead of libtcc1.a */
-#if defined CONFIG_USE_LIBGCC && !defined TCC_LIBGCC
-#define TCC_LIBGCC USE_TRIPLET(CONFIG_SYSROOT "/" CONFIG_LDDIR) "/libgcc_s.so.1"
-#endif
-
-/* -------------------------------------------- */
-
-#include "libtcc.h"
-#include "elf.h"
-#include "stab.h"
-
-/* -------------------------------------------- */
-
-#ifndef PUB_FUNC /* functions used by tcc.c but not in libtcc.h */
-# define PUB_FUNC
-#endif
-
-#ifndef ONE_SOURCE
-# define ONE_SOURCE 1
-#endif
-
-#if ONE_SOURCE
-#define ST_INLN static inline
-#define ST_FUNC static
-#define ST_DATA static
-#else
-#define ST_INLN
-#define ST_FUNC
-#define ST_DATA extern
-#endif
-
-#ifdef TCC_PROFILE /* profile all functions */
-# define static
-#endif
-
-/* -------------------------------------------- */
-/* include the target specific definitions */
-
-#define TARGET_DEFS_ONLY
-#ifdef TCC_TARGET_I386
-# include "i386-gen.c"
-# include "i386-link.c"
-#endif
-#ifdef TCC_TARGET_X86_64
-# include "x86_64-gen.c"
-# include "x86_64-link.c"
-#endif
-#ifdef TCC_TARGET_ARM
-# include "arm-gen.c"
-# include "arm-link.c"
-# include "arm-asm.c"
-#endif
-#ifdef TCC_TARGET_ARM64
-# include "arm64-gen.c"
-# include "arm64-link.c"
-#endif
-#ifdef TCC_TARGET_C67
-# define TCC_TARGET_COFF
-# include "coff.h"
-# include "c67-gen.c"
-# include "c67-link.c"
-#endif
-#undef TARGET_DEFS_ONLY
-
-/* -------------------------------------------- */
-
-#if PTR_SIZE == 8
-# define ELFCLASSW ELFCLASS64
-# define ElfW(type) Elf##64##_##type
-# define ELFW(type) ELF##64##_##type
-# define ElfW_Rel ElfW(Rela)
-# define SHT_RELX SHT_RELA
-# define REL_SECTION_FMT ".rela%s"
-#else
-# define ELFCLASSW ELFCLASS32
-# define ElfW(type) Elf##32##_##type
-# define ELFW(type) ELF##32##_##type
-# define ElfW_Rel ElfW(Rel)
-# define SHT_RELX SHT_REL
-# define REL_SECTION_FMT ".rel%s"
-#endif
-/* target address type */
-#define addr_t ElfW(Addr)
-
-#if PTR_SIZE == 8 && !defined TCC_TARGET_PE
-# define LONG_SIZE 8
-#else
-# define LONG_SIZE 4
-#endif
-
-/* -------------------------------------------- */
-
-#define INCLUDE_STACK_SIZE  32
-#define IFDEF_STACK_SIZE    64
-#define VSTACK_SIZE         256
-#define STRING_MAX_SIZE     1024
-#define TOKSTR_MAX_SIZE     256
-#define PACK_STACK_SIZE     8
-
-#define TOK_HASH_SIZE       16384 /* must be a power of two */
-#define TOK_ALLOC_INCR      512  /* must be a power of two */
-#define TOK_MAX_SIZE        4 /* token max size in int unit when stored in string */
-
-/* token symbol management */
-typedef struct TokenSym {
-    struct TokenSym *hash_next;
-    struct Sym *sym_define; /* direct pointer to define */
-    struct Sym *sym_label; /* direct pointer to label */
-    struct Sym *sym_struct; /* direct pointer to structure */
-    struct Sym *sym_identifier; /* direct pointer to identifier */
-    int tok; /* token number */
-    int len;
-    char str[1];
-} TokenSym;
-
-#ifdef TCC_TARGET_PE
-typedef unsigned short nwchar_t;
-#else
-typedef int nwchar_t;
-#endif
-
-typedef struct CString {
-    int size; /* size in bytes */
-    void *data; /* either 'char *' or 'nwchar_t *' */
-    int size_allocated;
-} CString;
-
-/* type definition */
-typedef struct CType {
-    int t;
-    struct Sym *ref;
-} CType;
-
-/* constant value */
-typedef union CValue {
-    long double ld;
-    double d;
-    float f;
-    uint64_t i;
-    struct {
-        int size;
-        const void *data;
-    } str;
-    int tab[LDOUBLE_SIZE/4];
-} CValue;
-
-/* value on stack */
-typedef struct SValue {
-    CType type;      /* type */
-    unsigned short r;      /* register + flags */
-    unsigned short r2;     /* second register, used for 'long long'
-                              type. If not used, set to VT_CONST */
-    CValue c;              /* constant, if VT_CONST */
-    struct Sym *sym;       /* symbol, if (VT_SYM | VT_CONST), or if
-    			      result of unary() for an identifier. */
-} SValue;
-
-/* symbol attributes */
-struct SymAttr {
-    unsigned short
-    aligned     : 5, /* alignment as log2+1 (0 == unspecified) */
-    packed      : 1,
-    weak        : 1,
-    visibility  : 2,
-    dllexport   : 1,
-    dllimport   : 1,
-    unused      : 5;
-};
-
-/* function attributes or temporary attributes for parsing */
-struct FuncAttr {
-    unsigned
-    func_call   : 3, /* calling convention (0..5), see below */
-    func_type   : 2, /* FUNC_OLD/NEW/ELLIPSIS */
-    func_body   : 1, /* body was defined */
-    func_args   : 8; /* PE __stdcall args */
-};
-
-/* GNUC attribute definition */
-typedef struct AttributeDef {
-    struct SymAttr a;
-    struct FuncAttr f;
-    struct Section *section;
-    int alias_target; /* token */
-    int asm_label; /* associated asm label */
-    char attr_mode; /* __attribute__((__mode__(...))) */
-} AttributeDef;
-
-/* symbol management */
-typedef struct Sym {
-    int v; /* symbol token */
-    unsigned short r; /* associated register or VT_CONST/VT_LOCAL and LVAL type */
-    struct SymAttr a; /* symbol attributes */
-    union {
-        struct {
-            int c; /* associated number or Elf symbol index */
-            union {
-                int sym_scope; /* scope level for locals */
-                int jnext; /* next jump label */
-                struct FuncAttr f; /* function attributes */
-                int auxtype; /* bitfield access type */
-            };
-        };
-        long long enum_val; /* enum constant if IS_ENUM_VAL */
-        int *d; /* define token stream */
-    };
-    CType type; /* associated type */
-    union {
-        struct Sym *next; /* next related symbol (for fields and anoms) */
-        int asm_label; /* associated asm label */
-    };
-    struct Sym *prev; /* prev symbol in stack */
-    struct Sym *prev_tok; /* previous symbol for this token */
-} Sym;
-
-/* section definition */
-/* XXX: use directly ELF structure for parameters ? */
-/* special flag to indicate that the section should not be linked to
-   the other ones */
-#define SHF_PRIVATE 0x80000000
-
-/* special flag, too */
-#define SECTION_ABS ((void *)1)
-
-typedef struct Section {
-    unsigned long data_offset; /* current data offset */
-    unsigned char *data;       /* section data */
-    unsigned long data_allocated; /* used for realloc() handling */
-    int sh_name;             /* elf section name (only used during output) */
-    int sh_num;              /* elf section number */
-    int sh_type;             /* elf section type */
-    int sh_flags;            /* elf section flags */
-    int sh_info;             /* elf section info */
-    int sh_addralign;        /* elf section alignment */
-    int sh_entsize;          /* elf entry size */
-    unsigned long sh_size;   /* section size (only used during output) */
-    addr_t sh_addr;          /* address at which the section is relocated */
-    unsigned long sh_offset; /* file offset */
-    int nb_hashed_syms;      /* used to resize the hash table */
-    struct Section *link;    /* link to another section */
-    struct Section *reloc;   /* corresponding section for relocation, if any */
-    struct Section *hash;    /* hash table for symbols */
-    struct Section *prev;    /* previous section on section stack */
-    char name[1];           /* section name */
-} Section;
-
-typedef struct DLLReference {
-    int level;
-    void *handle;
-    char name[1];
-} DLLReference;
-
-/* -------------------------------------------------- */
-
-#define SYM_STRUCT     0x40000000 /* struct/union/enum symbol space */
-#define SYM_FIELD      0x20000000 /* struct/union field symbol space */
-#define SYM_FIRST_ANOM 0x10000000 /* first anonymous sym */
-
-/* stored in 'Sym->f.func_type' field */
-#define FUNC_NEW       1 /* ansi function prototype */
-#define FUNC_OLD       2 /* old function prototype */
-#define FUNC_ELLIPSIS  3 /* ansi function prototype with ... */
-
-/* stored in 'Sym->f.func_call' field */
-#define FUNC_CDECL     0 /* standard c call */
-#define FUNC_STDCALL   1 /* pascal c call */
-#define FUNC_FASTCALL1 2 /* first param in %eax */
-#define FUNC_FASTCALL2 3 /* first parameters in %eax, %edx */
-#define FUNC_FASTCALL3 4 /* first parameter in %eax, %edx, %ecx */
-#define FUNC_FASTCALLW 5 /* first parameter in %ecx, %edx */
-
-/* field 'Sym.t' for macros */
-#define MACRO_OBJ      0 /* object like macro */
-#define MACRO_FUNC     1 /* function like macro */
-
-/* field 'Sym.r' for C labels */
-#define LABEL_DEFINED  0 /* label is defined */
-#define LABEL_FORWARD  1 /* label is forward defined */
-#define LABEL_DECLARED 2 /* label is declared but never used */
-
-/* type_decl() types */
-#define TYPE_ABSTRACT  1 /* type without variable */
-#define TYPE_DIRECT    2 /* type with variable */
-
-#define IO_BUF_SIZE 8192
-
-typedef struct BufferedFile {
-    uint8_t *buf_ptr;
-    uint8_t *buf_end;
-    int fd;
-    struct BufferedFile *prev;
-    int line_num;    /* current line number - here to simplify code */
-    int line_ref;    /* tcc -E: last printed line */
-    int ifndef_macro;  /* #ifndef macro / #endif search */
-    int ifndef_macro_saved; /* saved ifndef_macro */
-    int *ifdef_stack_ptr; /* ifdef_stack value at the start of the file */
-    int include_next_index; /* next search path */
-    char filename[1024];    /* filename */
-    char *true_filename; /* filename not modified by # line directive */
-    unsigned char unget[4];
-    unsigned char buffer[1]; /* extra size for CH_EOB char */
-} BufferedFile;
-
-#define CH_EOB   '\\'       /* end of buffer or '\0' char in file */
-#define CH_EOF   (-1)   /* end of file */
-
-/* used to record tokens */
-typedef struct TokenString {
-    int *str;
-    int len;
-    int lastlen;
-    int allocated_len;
-    int last_line_num;
-    int save_line_num;
-    /* used to chain token-strings with begin/end_macro() */
-    struct TokenString *prev;
-    const int *prev_ptr;
-    char alloc;
-} TokenString;
-
-/* inline functions */
-typedef struct InlineFunc {
-    TokenString *func_str;
-    Sym *sym;
-    char filename[1];
-} InlineFunc;
-
-/* include file cache, used to find files faster and also to eliminate
-   inclusion if the include file is protected by #ifndef ... #endif */
-typedef struct CachedInclude {
-    int ifndef_macro;
-    int once;
-    int hash_next; /* -1 if none */
-    char filename[1]; /* path specified in #include */
-} CachedInclude;
-
-#define CACHED_INCLUDES_HASH_SIZE 32
-
-#ifdef CONFIG_TCC_ASM
-typedef struct ExprValue {
-    uint64_t v;
-    Sym *sym;
-    int pcrel;
-} ExprValue;
-
-#define MAX_ASM_OPERANDS 30
-typedef struct ASMOperand {
-    int id; /* GCC 3 optional identifier (0 if number only supported */
-    char *constraint;
-    char asm_str[16]; /* computed asm string for operand */
-    SValue *vt; /* C value of the expression */
-    int ref_index; /* if >= 0, gives reference to a output constraint */
-    int input_index; /* if >= 0, gives reference to an input constraint */
-    int priority; /* priority, used to assign registers */
-    int reg; /* if >= 0, register number used for this operand */
-    int is_llong; /* true if double register value */
-    int is_memory; /* true if memory operand */
-    int is_rw;     /* for '+' modifier */
-} ASMOperand;
-#endif
-
-/* extra symbol attributes (not in symbol table) */
-struct sym_attr {
-    unsigned got_offset;
-    unsigned plt_offset;
-    int plt_sym;
-    int dyn_index;
-#ifdef TCC_TARGET_ARM
-    unsigned char plt_thumb_stub:1;
-#endif
-};
-
-struct TCCState {
-
-    int verbose; /* if true, display some information during compilation */
-    int nostdinc; /* if true, no standard headers are added */
-    int nostdlib; /* if true, no standard libraries are added */
-    int nocommon; /* if true, do not use common symbols for .bss data */
-    int static_link; /* if true, static linking is performed */
-    int rdynamic; /* if true, all symbols are exported */
-    int symbolic; /* if true, resolve symbols in the current module first */
-    int alacarte_link; /* if true, only link in referenced objects from archive */
-
-    char *tcc_lib_path; /* CONFIG_TCCDIR or -B option */
-    char *soname; /* as specified on the command line (-soname) */
-    char *rpath; /* as specified on the command line (-Wl,-rpath=) */
-    int enable_new_dtags; /* ditto, (-Wl,--enable-new-dtags) */
-
-    /* output type, see TCC_OUTPUT_XXX */
-    int output_type;
-    /* output format, see TCC_OUTPUT_FORMAT_xxx */
-    int output_format;
-
-    /* C language options */
-    int char_is_unsigned;
-    int leading_underscore;
-    int ms_extensions;	/* allow nested named struct w/o identifier behave like unnamed */
-    int dollars_in_identifiers;	/* allows '$' char in identifiers */
-    int ms_bitfields; /* if true, emulate MS algorithm for aligning bitfields */
-
-    /* warning switches */
-    int warn_write_strings;
-    int warn_unsupported;
-    int warn_error;
-    int warn_none;
-    int warn_implicit_function_declaration;
-    int warn_gcc_compat;
-
-    /* compile with debug symbol (and use them if error during execution) */
-    int do_debug;
-#ifdef CONFIG_TCC_BCHECK
-    /* compile with built-in memory and bounds checker */
-    int do_bounds_check;
-#endif
-#ifdef TCC_TARGET_ARM
-    enum float_abi float_abi; /* float ABI of the generated code*/
-#endif
-    int run_test; /* nth test to run with -dt -run */
-
-    addr_t text_addr; /* address of text section */
-    int has_text_addr;
-
-    unsigned section_align; /* section alignment */
-
-    char *init_symbol; /* symbols to call at load-time (not used currently) */
-    char *fini_symbol; /* symbols to call at unload-time (not used currently) */
-
-#ifdef TCC_TARGET_I386
-    int seg_size; /* 32. Can be 16 with i386 assembler (.code16) */
-#endif
-#ifdef TCC_TARGET_X86_64
-    int nosse; /* For -mno-sse support. */
-#endif
-
-    /* array of all loaded dlls (including those referenced by loaded dlls) */
-    DLLReference **loaded_dlls;
-    int nb_loaded_dlls;
-
-    /* include paths */
-    char **include_paths;
-    int nb_include_paths;
-
-    char **sysinclude_paths;
-    int nb_sysinclude_paths;
-
-    /* library paths */
-    char **library_paths;
-    int nb_library_paths;
-
-    /* crt?.o object path */
-    char **crt_paths;
-    int nb_crt_paths;
-
-    /* -include files */
-    char **cmd_include_files;
-    int nb_cmd_include_files;
-
-    /* error handling */
-    void *error_opaque;
-    void (*error_func)(void *opaque, const char *msg);
-    int error_set_jmp_enabled;
-    jmp_buf error_jmp_buf;
-    int nb_errors;
-
-    /* output file for preprocessing (-E) */
-    FILE *ppfp;
-    enum {
-	LINE_MACRO_OUTPUT_FORMAT_GCC,
-	LINE_MACRO_OUTPUT_FORMAT_NONE,
-	LINE_MACRO_OUTPUT_FORMAT_STD,
-    LINE_MACRO_OUTPUT_FORMAT_P10 = 11
-    } Pflag; /* -P switch */
-    char dflag; /* -dX value */
-
-    /* for -MD/-MF: collected dependencies for this compilation */
-    char **target_deps;
-    int nb_target_deps;
-
-    /* compilation */
-    BufferedFile *include_stack[INCLUDE_STACK_SIZE];
-    BufferedFile **include_stack_ptr;
-
-    int ifdef_stack[IFDEF_STACK_SIZE];
-    int *ifdef_stack_ptr;
-
-    /* included files enclosed with #ifndef MACRO */
-    int cached_includes_hash[CACHED_INCLUDES_HASH_SIZE];
-    CachedInclude **cached_includes;
-    int nb_cached_includes;
-
-    /* #pragma pack stack */
-    int pack_stack[PACK_STACK_SIZE];
-    int *pack_stack_ptr;
-    char **pragma_libs;
-    int nb_pragma_libs;
-
-    /* inline functions are stored as token lists and compiled last
-       only if referenced */
-    struct InlineFunc **inline_fns;
-    int nb_inline_fns;
-
-    /* sections */
-    Section **sections;
-    int nb_sections; /* number of sections, including first dummy section */
-
-    Section **priv_sections;
-    int nb_priv_sections; /* number of private sections */
-
-    /* got & plt handling */
-    Section *got;
-    Section *plt;
-
-    /* temporary dynamic symbol sections (for dll loading) */
-    Section *dynsymtab_section;
-    /* exported dynamic symbol section */
-    Section *dynsym;
-    /* copy of the global symtab_section variable */
-    Section *symtab;
-    /* extra attributes (eg. GOT/PLT value) for symtab symbols */
-    struct sym_attr *sym_attrs;
-    int nb_sym_attrs;
-    /* tiny assembler state */
-    Sym *asm_labels;
-
-#ifdef TCC_TARGET_PE
-    /* PE info */
-    int pe_subsystem;
-    unsigned pe_characteristics;
-    unsigned pe_file_align;
-    unsigned pe_stack_size;
-# ifdef TCC_TARGET_X86_64
-    Section *uw_pdata;
-    int uw_sym;
-    unsigned uw_offs;
-# endif
-#endif
-
-#ifdef TCC_IS_NATIVE
-    const char *runtime_main;
-    void **runtime_mem;
-    int nb_runtime_mem;
-#endif
-
-    /* used by main and tcc_parse_args only */
-    struct filespec **files; /* files seen on command line */
-    int nb_files; /* number thereof */
-    int nb_libraries; /* number of libs thereof */
-    int filetype;
-    char *outfile; /* output filename */
-    int option_r; /* option -r */
-    int do_bench; /* option -bench */
-    int gen_deps; /* option -MD  */
-    char *deps_outfile; /* option -MF */
-    int option_pthread; /* -pthread option */
-    int argc;
-    char **argv;
-};
-
-struct filespec {
-    char type;
-    char alacarte;
-    char name[1];
-};
-
-/* The current value can be: */
-#define VT_VALMASK   0x003f  /* mask for value location, register or: */
-#define VT_CONST     0x0030  /* constant in vc (must be first non register value) */
-#define VT_LLOCAL    0x0031  /* lvalue, offset on stack */
-#define VT_LOCAL     0x0032  /* offset on stack */
-#define VT_CMP       0x0033  /* the value is stored in processor flags (in vc) */
-#define VT_JMP       0x0034  /* value is the consequence of jmp true (even) */
-#define VT_JMPI      0x0035  /* value is the consequence of jmp false (odd) */
-#define VT_LVAL      0x0100  /* var is an lvalue */
-#define VT_SYM       0x0200  /* a symbol value is added */
-#define VT_MUSTCAST  0x0400  /* value must be casted to be correct (used for
-                                char/short stored in integer registers) */
-#define VT_MUSTBOUND 0x0800  /* bound checking must be done before
-                                dereferencing value */
-#define VT_BOUNDED   0x8000  /* value is bounded. The address of the
-                                bounding function call point is in vc */
-#define VT_LVAL_BYTE     0x1000  /* lvalue is a byte */
-#define VT_LVAL_SHORT    0x2000  /* lvalue is a short */
-#define VT_LVAL_UNSIGNED 0x4000  /* lvalue is unsigned */
-#define VT_LVAL_TYPE     (VT_LVAL_BYTE | VT_LVAL_SHORT | VT_LVAL_UNSIGNED)
-
-/* types */
-#define VT_BTYPE       0x000f  /* mask for basic type */
-#define VT_VOID             0  /* void type */
-#define VT_BYTE             1  /* signed byte type */
-#define VT_SHORT            2  /* short type */
-#define VT_INT              3  /* integer type */
-#define VT_LLONG            4  /* 64 bit integer */
-#define VT_PTR              5  /* pointer */
-#define VT_FUNC             6  /* function type */
-#define VT_STRUCT           7  /* struct/union definition */
-#define VT_FLOAT            8  /* IEEE float */
-#define VT_DOUBLE           9  /* IEEE double */
-#define VT_LDOUBLE         10  /* IEEE long double */
-#define VT_BOOL            11  /* ISOC99 boolean type */
-#define VT_QLONG           13  /* 128-bit integer. Only used for x86-64 ABI */
-#define VT_QFLOAT          14  /* 128-bit float. Only used for x86-64 ABI */
-
-#define VT_UNSIGNED    0x0010  /* unsigned type */
-#define VT_DEFSIGN     0x0020  /* explicitly signed or unsigned */
-#define VT_ARRAY       0x0040  /* array type (also has VT_PTR) */
-#define VT_BITFIELD    0x0080  /* bitfield modifier */
-#define VT_CONSTANT    0x0100  /* const modifier */
-#define VT_VOLATILE    0x0200  /* volatile modifier */
-#define VT_VLA         0x0400  /* VLA type (also has VT_PTR and VT_ARRAY) */
-#define VT_LONG        0x0800  /* long type (also has VT_INT rsp. VT_LLONG) */
-
-/* storage */
-#define VT_EXTERN  0x00001000  /* extern definition */
-#define VT_STATIC  0x00002000  /* static variable */
-#define VT_TYPEDEF 0x00004000  /* typedef definition */
-#define VT_INLINE  0x00008000  /* inline definition */
-/* currently unused: 0x000[1248]0000  */
-
-#define VT_STRUCT_SHIFT 20     /* shift for bitfield shift values (32 - 2*6) */
-#define VT_STRUCT_MASK (((1 << (6+6)) - 1) << VT_STRUCT_SHIFT | VT_BITFIELD)
-#define BIT_POS(t) (((t) >> VT_STRUCT_SHIFT) & 0x3f)
-#define BIT_SIZE(t) (((t) >> (VT_STRUCT_SHIFT + 6)) & 0x3f)
-
-#define VT_UNION    (1 << VT_STRUCT_SHIFT | VT_STRUCT)
-#define VT_ENUM     (2 << VT_STRUCT_SHIFT) /* integral type is an enum really */
-#define VT_ENUM_VAL (3 << VT_STRUCT_SHIFT) /* integral type is an enum constant really */
-
-#define IS_ENUM(t) ((t & VT_STRUCT_MASK) == VT_ENUM)
-#define IS_ENUM_VAL(t) ((t & VT_STRUCT_MASK) == VT_ENUM_VAL)
-#define IS_UNION(t) ((t & (VT_STRUCT_MASK|VT_BTYPE)) == VT_UNION)
-
-/* type mask (except storage) */
-#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE)
-#define VT_TYPE (~(VT_STORAGE|VT_STRUCT_MASK))
-
-/* token values */
-
-/* warning: the following compare tokens depend on i386 asm code */
-#define TOK_ULT 0x92
-#define TOK_UGE 0x93
-#define TOK_EQ  0x94
-#define TOK_NE  0x95
-#define TOK_ULE 0x96
-#define TOK_UGT 0x97
-#define TOK_Nset 0x98
-#define TOK_Nclear 0x99
-#define TOK_LT  0x9c
-#define TOK_GE  0x9d
-#define TOK_LE  0x9e
-#define TOK_GT  0x9f
-
-#define TOK_LAND  0xa0
-#define TOK_LOR   0xa1
-#define TOK_DEC   0xa2
-#define TOK_MID   0xa3 /* inc/dec, to void constant */
-#define TOK_INC   0xa4
-#define TOK_UDIV  0xb0 /* unsigned division */
-#define TOK_UMOD  0xb1 /* unsigned modulo */
-#define TOK_PDIV  0xb2 /* fast division with undefined rounding for pointers */
-
-/* tokens that carry values (in additional token string space / tokc) --> */
-#define TOK_CCHAR   0xb3 /* char constant in tokc */
-#define TOK_LCHAR   0xb4
-#define TOK_CINT    0xb5 /* number in tokc */
-#define TOK_CUINT   0xb6 /* unsigned int constant */
-#define TOK_CLLONG  0xb7 /* long long constant */
-#define TOK_CULLONG 0xb8 /* unsigned long long constant */
-#define TOK_STR     0xb9 /* pointer to string in tokc */
-#define TOK_LSTR    0xba
-#define TOK_CFLOAT  0xbb /* float constant */
-#define TOK_CDOUBLE 0xbc /* double constant */
-#define TOK_CLDOUBLE 0xbd /* long double constant */
-#define TOK_PPNUM   0xbe /* preprocessor number */
-#define TOK_PPSTR   0xbf /* preprocessor string */
-#define TOK_LINENUM 0xc0 /* line number info */
-#define TOK_TWODOTS 0xa8 /* C++ token ? */
-/* <-- */
-
-#define TOK_UMULL    0xc2 /* unsigned 32x32 -> 64 mul */
-#define TOK_ADDC1    0xc3 /* add with carry generation */
-#define TOK_ADDC2    0xc4 /* add with carry use */
-#define TOK_SUBC1    0xc5 /* add with carry generation */
-#define TOK_SUBC2    0xc6 /* add with carry use */
-#define TOK_ARROW    0xc7
-#define TOK_DOTS     0xc8 /* three dots */
-#define TOK_SHR      0xc9 /* unsigned shift right */
-#define TOK_TWOSHARPS 0xca /* ## preprocessing token */
-#define TOK_PLCHLDR  0xcb /* placeholder token as defined in C99 */
-#define TOK_NOSUBST  0xcc /* means following token has already been pp'd */
-#define TOK_PPJOIN   0xcd /* A '##' in the right position to mean pasting */
-#define TOK_CLONG    0xce /* long constant */
-#define TOK_CULONG   0xcf /* unsigned long constant */
-
-#define TOK_SHL   0x01 /* shift left */
-#define TOK_SAR   0x02 /* signed shift right */
-
-/* assignment operators : normal operator or 0x80 */
-#define TOK_A_MOD 0xa5
-#define TOK_A_AND 0xa6
-#define TOK_A_MUL 0xaa
-#define TOK_A_ADD 0xab
-#define TOK_A_SUB 0xad
-#define TOK_A_DIV 0xaf
-#define TOK_A_XOR 0xde
-#define TOK_A_OR  0xfc
-#define TOK_A_SHL 0x81
-#define TOK_A_SAR 0x82
-
-#define TOK_EOF       (-1)  /* end of file */
-#define TOK_LINEFEED  10    /* line feed */
-
-/* all identifiers and strings have token above that */
-#define TOK_IDENT 256
-
-#define DEF_ASM(x) DEF(TOK_ASM_ ## x, #x)
-#define TOK_ASM_int TOK_INT
-#define DEF_ASMDIR(x) DEF(TOK_ASMDIR_ ## x, "." #x)
-#define TOK_ASMDIR_FIRST TOK_ASMDIR_byte
-#define TOK_ASMDIR_LAST TOK_ASMDIR_section
-
-#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
-/* only used for i386 asm opcodes definitions */
-#define DEF_BWL(x) \
- DEF(TOK_ASM_ ## x ## b, #x "b") \
- DEF(TOK_ASM_ ## x ## w, #x "w") \
- DEF(TOK_ASM_ ## x ## l, #x "l") \
- DEF(TOK_ASM_ ## x, #x)
-#define DEF_WL(x) \
- DEF(TOK_ASM_ ## x ## w, #x "w") \
- DEF(TOK_ASM_ ## x ## l, #x "l") \
- DEF(TOK_ASM_ ## x, #x)
-#ifdef TCC_TARGET_X86_64
-# define DEF_BWLQ(x) \
- DEF(TOK_ASM_ ## x ## b, #x "b") \
- DEF(TOK_ASM_ ## x ## w, #x "w") \
- DEF(TOK_ASM_ ## x ## l, #x "l") \
- DEF(TOK_ASM_ ## x ## q, #x "q") \
- DEF(TOK_ASM_ ## x, #x)
-# define DEF_WLQ(x) \
- DEF(TOK_ASM_ ## x ## w, #x "w") \
- DEF(TOK_ASM_ ## x ## l, #x "l") \
- DEF(TOK_ASM_ ## x ## q, #x "q") \
- DEF(TOK_ASM_ ## x, #x)
-# define DEF_BWLX DEF_BWLQ
-# define DEF_WLX DEF_WLQ
-/* number of sizes + 1 */
-# define NBWLX 5
-#else
-# define DEF_BWLX DEF_BWL
-# define DEF_WLX DEF_WL
-/* number of sizes + 1 */
-# define NBWLX 4
-#endif
-
-#define DEF_FP1(x) \
- DEF(TOK_ASM_ ## f ## x ## s, "f" #x "s") \
- DEF(TOK_ASM_ ## fi ## x ## l, "fi" #x "l") \
- DEF(TOK_ASM_ ## f ## x ## l, "f" #x "l") \
- DEF(TOK_ASM_ ## fi ## x ## s, "fi" #x "s")
-
-#define DEF_FP(x) \
- DEF(TOK_ASM_ ## f ## x, "f" #x ) \
- DEF(TOK_ASM_ ## f ## x ## p, "f" #x "p") \
- DEF_FP1(x)
-
-#define DEF_ASMTEST(x,suffix) \
- DEF_ASM(x ## o ## suffix) \
- DEF_ASM(x ## no ## suffix) \
- DEF_ASM(x ## b ## suffix) \
- DEF_ASM(x ## c ## suffix) \
- DEF_ASM(x ## nae ## suffix) \
- DEF_ASM(x ## nb ## suffix) \
- DEF_ASM(x ## nc ## suffix) \
- DEF_ASM(x ## ae ## suffix) \
- DEF_ASM(x ## e ## suffix) \
- DEF_ASM(x ## z ## suffix) \
- DEF_ASM(x ## ne ## suffix) \
- DEF_ASM(x ## nz ## suffix) \
- DEF_ASM(x ## be ## suffix) \
- DEF_ASM(x ## na ## suffix) \
- DEF_ASM(x ## nbe ## suffix) \
- DEF_ASM(x ## a ## suffix) \
- DEF_ASM(x ## s ## suffix) \
- DEF_ASM(x ## ns ## suffix) \
- DEF_ASM(x ## p ## suffix) \
- DEF_ASM(x ## pe ## suffix) \
- DEF_ASM(x ## np ## suffix) \
- DEF_ASM(x ## po ## suffix) \
- DEF_ASM(x ## l ## suffix) \
- DEF_ASM(x ## nge ## suffix) \
- DEF_ASM(x ## nl ## suffix) \
- DEF_ASM(x ## ge ## suffix) \
- DEF_ASM(x ## le ## suffix) \
- DEF_ASM(x ## ng ## suffix) \
- DEF_ASM(x ## nle ## suffix) \
- DEF_ASM(x ## g ## suffix)
-
-#endif /* defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 */
-
-enum tcc_token {
-    TOK_LAST = TOK_IDENT - 1
-#define DEF(id, str) ,id
-#include "tcctok.h"
-#undef DEF
-};
-
-/* keywords: tok >= TOK_IDENT && tok < TOK_UIDENT */
-#define TOK_UIDENT TOK_DEFINE
-
-/* ------------ libtcc.c ------------ */
-
-/* use GNU C extensions */
-ST_DATA int gnu_ext;
-/* use Tiny C extensions */
-ST_DATA int tcc_ext;
-/* XXX: get rid of this ASAP */
-ST_DATA struct TCCState *tcc_state;
-
-/* public functions currently used by the tcc main function */
-ST_FUNC char *pstrcpy(char *buf, int buf_size, const char *s);
-ST_FUNC char *pstrcat(char *buf, int buf_size, const char *s);
-ST_FUNC char *pstrncpy(char *out, const char *in, size_t num);
-PUB_FUNC char *tcc_basename(const char *name);
-PUB_FUNC char *tcc_fileextension (const char *name);
-
-#ifndef MEM_DEBUG
-PUB_FUNC void tcc_free(void *ptr);
-PUB_FUNC void *tcc_malloc(unsigned long size);
-PUB_FUNC void *tcc_mallocz(unsigned long size);
-PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size);
-PUB_FUNC char *tcc_strdup(const char *str);
-#else
-#define tcc_free(ptr)           tcc_free_debug(ptr)
-#define tcc_malloc(size)        tcc_malloc_debug(size, __FILE__, __LINE__)
-#define tcc_mallocz(size)       tcc_mallocz_debug(size, __FILE__, __LINE__)
-#define tcc_realloc(ptr,size)   tcc_realloc_debug(ptr, size, __FILE__, __LINE__)
-#define tcc_strdup(str)         tcc_strdup_debug(str, __FILE__, __LINE__)
-PUB_FUNC void tcc_free_debug(void *ptr);
-PUB_FUNC void *tcc_malloc_debug(unsigned long size, const char *file, int line);
-PUB_FUNC void *tcc_mallocz_debug(unsigned long size, const char *file, int line);
-PUB_FUNC void *tcc_realloc_debug(void *ptr, unsigned long size, const char *file, int line);
-PUB_FUNC char *tcc_strdup_debug(const char *str, const char *file, int line);
-#endif
-
-#define free(p) use_tcc_free(p)
-#define malloc(s) use_tcc_malloc(s)
-#define realloc(p, s) use_tcc_realloc(p, s)
-#undef strdup
-#define strdup(s) use_tcc_strdup(s)
-PUB_FUNC void tcc_memcheck(void);
-PUB_FUNC void tcc_error_noabort(const char *fmt, ...);
-PUB_FUNC NORETURN void tcc_error(const char *fmt, ...);
-PUB_FUNC void tcc_warning(const char *fmt, ...);
-
-/* other utilities */
-ST_FUNC void dynarray_add(void *ptab, int *nb_ptr, void *data);
-ST_FUNC void dynarray_reset(void *pp, int *n);
-ST_INLN void cstr_ccat(CString *cstr, int ch);
-ST_FUNC void cstr_cat(CString *cstr, const char *str, int len);
-ST_FUNC void cstr_wccat(CString *cstr, int ch);
-ST_FUNC void cstr_new(CString *cstr);
-ST_FUNC void cstr_free(CString *cstr);
-ST_FUNC void cstr_reset(CString *cstr);
-
-ST_INLN void sym_free(Sym *sym);
-ST_FUNC Sym *sym_push2(Sym **ps, int v, int t, int c);
-ST_FUNC Sym *sym_find2(Sym *s, int v);
-ST_FUNC Sym *sym_push(int v, CType *type, int r, int c);
-ST_FUNC void sym_pop(Sym **ptop, Sym *b, int keep);
-ST_INLN Sym *struct_find(int v);
-ST_INLN Sym *sym_find(int v);
-ST_FUNC Sym *global_identifier_push(int v, int t, int c);
-
-ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen);
-ST_FUNC int tcc_open(TCCState *s1, const char *filename);
-ST_FUNC void tcc_close(void);
-
-ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags);
-/* flags: */
-#define AFF_PRINT_ERROR     0x10 /* print error if file not found */
-#define AFF_REFERENCED_DLL  0x20 /* load a referenced dll from another dll */
-#define AFF_TYPE_BIN        0x40 /* file to add is binary */
-/* s->filetype: */
-#define AFF_TYPE_NONE   0
-#define AFF_TYPE_C      1
-#define AFF_TYPE_ASM    2
-#define AFF_TYPE_ASMPP  3
-#define AFF_TYPE_LIB    4
-/* values from tcc_object_type(...) */
-#define AFF_BINTYPE_REL 1
-#define AFF_BINTYPE_DYN 2
-#define AFF_BINTYPE_AR  3
-#define AFF_BINTYPE_C67 4
-
-
-ST_FUNC int tcc_add_crt(TCCState *s, const char *filename);
-ST_FUNC int tcc_add_dll(TCCState *s, const char *filename, int flags);
-ST_FUNC void tcc_add_pragma_libs(TCCState *s1);
-PUB_FUNC int tcc_add_library_err(TCCState *s, const char *f);
-PUB_FUNC void tcc_print_stats(TCCState *s, unsigned total_time);
-PUB_FUNC int tcc_parse_args(TCCState *s, int *argc, char ***argv, int optind);
-#ifdef _WIN32
-ST_FUNC char *normalize_slashes(char *path);
-#endif
-
-/* tcc_parse_args return codes: */
-#define OPT_HELP 1
-#define OPT_HELP2 2
-#define OPT_V 3
-#define OPT_PRINT_DIRS 4
-#define OPT_AR 5
-#define OPT_IMPDEF 6
-#define OPT_M32 32
-#define OPT_M64 64
-
-/* ------------ tccpp.c ------------ */
-
-ST_DATA struct BufferedFile *file;
-ST_DATA int ch, tok;
-ST_DATA CValue tokc;
-ST_DATA const int *macro_ptr;
-ST_DATA int parse_flags;
-ST_DATA int tok_flags;
-ST_DATA CString tokcstr; /* current parsed string, if any */
-
-/* display benchmark infos */
-ST_DATA int total_lines;
-ST_DATA int total_bytes;
-ST_DATA int tok_ident;
-ST_DATA TokenSym **table_ident;
-
-#define TOK_FLAG_BOL   0x0001 /* beginning of line before */
-#define TOK_FLAG_BOF   0x0002 /* beginning of file before */
-#define TOK_FLAG_ENDIF 0x0004 /* a endif was found matching starting #ifdef */
-#define TOK_FLAG_EOF   0x0008 /* end of file */
-
-#define PARSE_FLAG_PREPROCESS 0x0001 /* activate preprocessing */
-#define PARSE_FLAG_TOK_NUM    0x0002 /* return numbers instead of TOK_PPNUM */
-#define PARSE_FLAG_LINEFEED   0x0004 /* line feed is returned as a
-                                        token. line feed is also
-                                        returned at eof */
-#define PARSE_FLAG_ASM_FILE 0x0008 /* we processing an asm file: '#' can be used for line comment, etc. */
-#define PARSE_FLAG_SPACES     0x0010 /* next() returns space tokens (for -E) */
-#define PARSE_FLAG_ACCEPT_STRAYS 0x0020 /* next() returns '\\' token */
-#define PARSE_FLAG_TOK_STR    0x0040 /* return parsed strings instead of TOK_PPSTR */
-
-/* isidnum_table flags: */
-#define IS_SPC 1
-#define IS_ID  2
-#define IS_NUM 4
-
-ST_FUNC TokenSym *tok_alloc(const char *str, int len);
-ST_FUNC const char *get_tok_str(int v, CValue *cv);
-ST_FUNC void begin_macro(TokenString *str, int alloc);
-ST_FUNC void end_macro(void);
-ST_FUNC int set_idnum(int c, int val);
-ST_INLN void tok_str_new(TokenString *s);
-ST_FUNC TokenString *tok_str_alloc(void);
-ST_FUNC void tok_str_free(TokenString *s);
-ST_FUNC void tok_str_free_str(int *str);
-ST_FUNC void tok_str_add(TokenString *s, int t);
-ST_FUNC void tok_str_add_tok(TokenString *s);
-ST_INLN void define_push(int v, int macro_type, int *str, Sym *first_arg);
-ST_FUNC void define_undef(Sym *s);
-ST_INLN Sym *define_find(int v);
-ST_FUNC void free_defines(Sym *b);
-ST_FUNC Sym *label_find(int v);
-ST_FUNC Sym *label_push(Sym **ptop, int v, int flags);
-ST_FUNC void label_pop(Sym **ptop, Sym *slast, int keep);
-ST_FUNC void parse_define(void);
-ST_FUNC void preprocess(int is_bof);
-ST_FUNC void next_nomacro(void);
-ST_FUNC void next(void);
-ST_INLN void unget_tok(int last_tok);
-ST_FUNC void preprocess_start(TCCState *s1, int is_asm);
-ST_FUNC void preprocess_end(TCCState *s1);
-ST_FUNC void tccpp_new(TCCState *s);
-ST_FUNC void tccpp_delete(TCCState *s);
-ST_FUNC int tcc_preprocess(TCCState *s1);
-ST_FUNC void skip(int c);
-ST_FUNC NORETURN void expect(const char *msg);
-
-/* space excluding newline */
-static inline int is_space(int ch) {
-    return ch == ' ' || ch == '\t' || ch == '\v' || ch == '\f' || ch == '\r';
-}
-static inline int isid(int c) {
-    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
-}
-static inline int isnum(int c) {
-    return c >= '0' && c <= '9';
-}
-static inline int isoct(int c) {
-    return c >= '0' && c <= '7';
-}
-static inline int toup(int c) {
-    return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c;
-}
-
-/* ------------ tccgen.c ------------ */
-
-#define SYM_POOL_NB (8192 / sizeof(Sym))
-ST_DATA Sym *sym_free_first;
-ST_DATA void **sym_pools;
-ST_DATA int nb_sym_pools;
-
-ST_DATA Sym *global_stack;
-ST_DATA Sym *local_stack;
-ST_DATA Sym *local_label_stack;
-ST_DATA Sym *global_label_stack;
-ST_DATA Sym *define_stack;
-ST_DATA CType char_pointer_type, func_old_type, int_type, size_type;
-ST_DATA SValue __vstack[1+/*to make bcheck happy*/ VSTACK_SIZE], *vtop, *pvtop;
-#define vstack  (__vstack + 1)
-ST_DATA int rsym, anon_sym, ind, loc;
-
-ST_DATA int const_wanted; /* true if constant wanted */
-ST_DATA int nocode_wanted; /* true if no code generation wanted for an expression */
-ST_DATA int global_expr;  /* true if compound literals must be allocated globally (used during initializers parsing */
-ST_DATA CType func_vt; /* current function return type (used by return instruction) */
-ST_DATA int func_var; /* true if current function is variadic */
-ST_DATA int func_vc;
-ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */
-ST_DATA const char *funcname;
-ST_DATA int g_debug;
-
-ST_FUNC void tcc_debug_start(TCCState *s1);
-ST_FUNC void tcc_debug_end(TCCState *s1);
-ST_FUNC void tcc_debug_funcstart(TCCState *s1, Sym *sym);
-ST_FUNC void tcc_debug_funcend(TCCState *s1, int size);
-ST_FUNC void tcc_debug_line(TCCState *s1);
-
-ST_FUNC int tccgen_compile(TCCState *s1);
-ST_FUNC void free_inline_functions(TCCState *s);
-ST_FUNC void check_vstack(void);
-
-ST_INLN int is_float(int t);
-ST_FUNC int ieee_finite(double d);
-ST_FUNC void test_lvalue(void);
-ST_FUNC void vpushi(int v);
-ST_FUNC Sym *external_global_sym(int v, CType *type, int r);
-ST_FUNC void vset(CType *type, int r, int v);
-ST_FUNC void vswap(void);
-ST_FUNC void vpush_global_sym(CType *type, int v);
-ST_FUNC void vrote(SValue *e, int n);
-ST_FUNC void vrott(int n);
-ST_FUNC void vrotb(int n);
-#ifdef TCC_TARGET_ARM
-ST_FUNC int get_reg_ex(int rc, int rc2);
-ST_FUNC void lexpand_nr(void);
-#endif
-ST_FUNC void vpushv(SValue *v);
-ST_FUNC void save_reg(int r);
-ST_FUNC void save_reg_upstack(int r, int n);
-ST_FUNC int get_reg(int rc);
-ST_FUNC void save_regs(int n);
-ST_FUNC void gaddrof(void);
-ST_FUNC int gv(int rc);
-ST_FUNC void gv2(int rc1, int rc2);
-ST_FUNC void vpop(void);
-ST_FUNC void gen_op(int op);
-ST_FUNC int type_size(CType *type, int *a);
-ST_FUNC void mk_pointer(CType *type);
-ST_FUNC void vstore(void);
-ST_FUNC void inc(int post, int c);
-ST_FUNC void parse_mult_str (CString *astr, const char *msg);
-ST_FUNC void parse_asm_str(CString *astr);
-ST_FUNC int lvalue_type(int t);
-ST_FUNC void indir(void);
-ST_FUNC void unary(void);
-ST_FUNC void expr_prod(void);
-ST_FUNC void expr_sum(void);
-ST_FUNC void gexpr(void);
-ST_FUNC int expr_const(void);
-#if defined CONFIG_TCC_BCHECK || defined TCC_TARGET_C67
-ST_FUNC Sym *get_sym_ref(CType *type, Section *sec, unsigned long offset, unsigned long size);
-#endif
-#if defined TCC_TARGET_X86_64 && !defined TCC_TARGET_PE
-ST_FUNC int classify_x86_64_va_arg(CType *ty);
-#endif
-
-/* ------------ tccelf.c ------------ */
-
-#define TCC_OUTPUT_FORMAT_ELF    0 /* default output format: ELF */
-#define TCC_OUTPUT_FORMAT_BINARY 1 /* binary image output */
-#define TCC_OUTPUT_FORMAT_COFF   2 /* COFF */
-
-#define ARMAG  "!<arch>\012"    /* For COFF and a.out archives */
-
-typedef struct {
-    unsigned int n_strx;         /* index into string table of name */
-    unsigned char n_type;         /* type of symbol */
-    unsigned char n_other;        /* misc info (usually empty) */
-    unsigned short n_desc;        /* description field */
-    unsigned int n_value;        /* value of symbol */
-} Stab_Sym;
-
-ST_DATA Section *text_section, *data_section, *bss_section; /* predefined sections */
-ST_DATA Section *common_section;
-ST_DATA Section *cur_text_section; /* current section where function code is generated */
-#ifdef CONFIG_TCC_ASM
-ST_DATA Section *last_text_section; /* to handle .previous asm directive */
-#endif
-#ifdef CONFIG_TCC_BCHECK
-/* bound check related sections */
-ST_DATA Section *bounds_section; /* contains global data bound description */
-ST_DATA Section *lbounds_section; /* contains local data bound description */
-ST_FUNC void tccelf_bounds_new(TCCState *s);
-#endif
-/* symbol sections */
-ST_DATA Section *symtab_section, *strtab_section;
-/* debug sections */
-ST_DATA Section *stab_section, *stabstr_section;
-
-ST_FUNC void tccelf_new(TCCState *s);
-ST_FUNC void tccelf_delete(TCCState *s);
-ST_FUNC void tccelf_stab_new(TCCState *s);
-
-ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags);
-ST_FUNC void section_realloc(Section *sec, unsigned long new_size);
-ST_FUNC size_t section_add(Section *sec, addr_t size, int align);
-ST_FUNC void *section_ptr_add(Section *sec, addr_t size);
-ST_FUNC void section_reserve(Section *sec, unsigned long size);
-ST_FUNC Section *find_section(TCCState *s1, const char *name);
-ST_FUNC Section *new_symtab(TCCState *s1, const char *symtab_name, int sh_type, int sh_flags, const char *strtab_name, const char *hash_name, int hash_sh_flags);
-
-ST_FUNC void put_extern_sym2(Sym *sym, Section *section, addr_t value, unsigned long size, int can_add_underscore);
-ST_FUNC void put_extern_sym(Sym *sym, Section *section, addr_t value, unsigned long size);
-#if PTR_SIZE == 4
-ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type);
-#endif
-ST_FUNC void greloca(Section *s, Sym *sym, unsigned long offset, int type, addr_t addend);
-
-ST_FUNC int put_elf_str(Section *s, const char *sym);
-ST_FUNC int put_elf_sym(Section *s, addr_t value, unsigned long size, int info, int other, int shndx, const char *name);
-ST_FUNC int set_elf_sym(Section *s, addr_t value, unsigned long size, int info, int other, int shndx, const char *name);
-ST_FUNC int find_elf_sym(Section *s, const char *name);
-ST_FUNC void put_elf_reloc(Section *symtab, Section *s, unsigned long offset, int type, int symbol);
-ST_FUNC void put_elf_reloca(Section *symtab, Section *s, unsigned long offset, int type, int symbol, addr_t addend);
-
-ST_FUNC void put_stabs(const char *str, int type, int other, int desc, unsigned long value);
-ST_FUNC void put_stabs_r(const char *str, int type, int other, int desc, unsigned long value, Section *sec, int sym_index);
-ST_FUNC void put_stabn(int type, int other, int desc, int value);
-ST_FUNC void put_stabd(int type, int other, int desc);
-
-ST_FUNC void relocate_common_syms(void);
-ST_FUNC void relocate_syms(TCCState *s1, Section *symtab, int do_resolve);
-ST_FUNC void relocate_section(TCCState *s1, Section *s);
-
-ST_FUNC void tcc_add_linker_symbols(TCCState *s1);
-ST_FUNC int tcc_object_type(int fd, ElfW(Ehdr) *h);
-ST_FUNC int tcc_load_object_file(TCCState *s1, int fd, unsigned long file_offset);
-ST_FUNC int tcc_load_archive(TCCState *s1, int fd);
-ST_FUNC void tcc_add_bcheck(TCCState *s1);
-ST_FUNC void tcc_add_runtime(TCCState *s1);
-
-ST_FUNC void build_got_entries(TCCState *s1);
-ST_FUNC struct sym_attr *get_sym_attr(TCCState *s1, int index, int alloc);
-ST_FUNC void squeeze_multi_relocs(Section *sec, size_t oldrelocoffset);
-
-ST_FUNC addr_t get_elf_sym_addr(TCCState *s, const char *name, int err);
-#if defined TCC_IS_NATIVE || defined TCC_TARGET_PE
-ST_FUNC void *tcc_get_symbol_err(TCCState *s, const char *name);
-#endif
-
-#ifndef TCC_TARGET_PE
-ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level);
-ST_FUNC int tcc_load_ldscript(TCCState *s1);
-ST_FUNC uint8_t *parse_comment(uint8_t *p);
-ST_FUNC void minp(void);
-ST_INLN void inp(void);
-ST_FUNC int handle_eob(void);
-#endif
-
-/* ------------ xxx-link.c ------------ */
-
-/* Whether to generate a GOT/PLT entry and when. NO_GOTPLT_ENTRY is first so
-   that unknown relocation don't create a GOT or PLT entry */
-enum gotplt_entry {
-    NO_GOTPLT_ENTRY,	/* never generate (eg. GLOB_DAT & JMP_SLOT relocs) */
-    BUILD_GOT_ONLY,	/* only build GOT (eg. TPOFF relocs) */
-    AUTO_GOTPLT_ENTRY,	/* generate if sym is UNDEF */
-    ALWAYS_GOTPLT_ENTRY	/* always generate (eg. PLTOFF relocs) */
-};
-
-ST_FUNC int code_reloc (int reloc_type);
-ST_FUNC int gotplt_entry_type (int reloc_type);
-ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr);
-ST_FUNC void relocate_init(Section *sr);
-ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val);
-ST_FUNC void relocate_plt(TCCState *s1);
-
-/* ------------ xxx-gen.c ------------ */
-
-ST_DATA const int reg_classes[NB_REGS];
-
-ST_FUNC void gsym_addr(int t, int a);
-ST_FUNC void gsym(int t);
-ST_FUNC void load(int r, SValue *sv);
-ST_FUNC void store(int r, SValue *v);
-ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align, int *regsize);
-ST_FUNC void gfunc_call(int nb_args);
-ST_FUNC void gfunc_prolog(CType *func_type);
-ST_FUNC void gfunc_epilog(void);
-ST_FUNC int gjmp(int t);
-ST_FUNC void gjmp_addr(int a);
-ST_FUNC int gtst(int inv, int t);
-#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
-ST_FUNC void gtst_addr(int inv, int a);
-#else
-#define gtst_addr(inv, a) gsym_addr(gtst(inv, 0), a)
-#endif
-ST_FUNC void gen_opi(int op);
-ST_FUNC void gen_opf(int op);
-ST_FUNC void gen_cvt_ftoi(int t);
-ST_FUNC void gen_cvt_ftof(int t);
-ST_FUNC void ggoto(void);
-#ifndef TCC_TARGET_C67
-ST_FUNC void o(unsigned int c);
-#endif
-#ifndef TCC_TARGET_ARM
-ST_FUNC void gen_cvt_itof(int t);
-#endif
-ST_FUNC void gen_vla_sp_save(int addr);
-ST_FUNC void gen_vla_sp_restore(int addr);
-ST_FUNC void gen_vla_alloc(CType *type, int align);
-
-static inline uint16_t read16le(unsigned char *p) {
-    return p[0] | (uint16_t)p[1] << 8;
-}
-static inline void write16le(unsigned char *p, uint16_t x) {
-    p[0] = x & 255;  p[1] = x >> 8 & 255;
-}
-static inline uint32_t read32le(unsigned char *p) {
-  return read16le(p) | (uint32_t)read16le(p + 2) << 16;
-}
-static inline void write32le(unsigned char *p, uint32_t x) {
-    write16le(p, x);  write16le(p + 2, x >> 16);
-}
-static inline void add32le(unsigned char *p, int32_t x) {
-    write32le(p, read32le(p) + x);
-}
-static inline uint64_t read64le(unsigned char *p) {
-  return read32le(p) | (uint64_t)read32le(p + 4) << 32;
-}
-static inline void write64le(unsigned char *p, uint64_t x) {
-    write32le(p, x);  write32le(p + 4, x >> 32);
-}
-static inline void add64le(unsigned char *p, int64_t x) {
-    write64le(p, read64le(p) + x);
-}
-
-/* ------------ i386-gen.c ------------ */
-#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
-ST_FUNC void g(int c);
-ST_FUNC void gen_le16(int c);
-ST_FUNC void gen_le32(int c);
-ST_FUNC void gen_addr32(int r, Sym *sym, int c);
-ST_FUNC void gen_addrpc32(int r, Sym *sym, int c);
-#endif
-
-#ifdef CONFIG_TCC_BCHECK
-ST_FUNC void gen_bounded_ptr_add(void);
-ST_FUNC void gen_bounded_ptr_deref(void);
-#endif
-
-/* ------------ x86_64-gen.c ------------ */
-#ifdef TCC_TARGET_X86_64
-ST_FUNC void gen_addr64(int r, Sym *sym, int64_t c);
-ST_FUNC void gen_opl(int op);
-#endif
-
-/* ------------ arm-gen.c ------------ */
-#ifdef TCC_TARGET_ARM
-#if defined(TCC_ARM_EABI) && !defined(CONFIG_TCC_ELFINTERP)
-PUB_FUNC const char *default_elfinterp(struct TCCState *s);
-#endif
-ST_FUNC void arm_init(struct TCCState *s);
-ST_FUNC void gen_cvt_itof1(int t);
-#endif
-
-/* ------------ arm64-gen.c ------------ */
-#ifdef TCC_TARGET_ARM64
-ST_FUNC void gen_cvt_sxtw(void);
-ST_FUNC void gen_opl(int op);
-ST_FUNC void gfunc_return(CType *func_type);
-ST_FUNC void gen_va_start(void);
-ST_FUNC void gen_va_arg(CType *t);
-ST_FUNC void gen_clear_cache(void);
-#endif
-
-/* ------------ c67-gen.c ------------ */
-#ifdef TCC_TARGET_C67
-#endif
-
-/* ------------ tcccoff.c ------------ */
-
-#ifdef TCC_TARGET_COFF
-ST_FUNC int tcc_output_coff(TCCState *s1, FILE *f);
-ST_FUNC int tcc_load_coff(TCCState * s1, int fd);
-#endif
-
-/* ------------ tccasm.c ------------ */
-ST_FUNC void asm_instr(void);
-ST_FUNC void asm_global_instr(void);
-#ifdef CONFIG_TCC_ASM
-ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands, const char *name, const char **pp);
-ST_FUNC Sym* get_asm_sym(int name, Sym *csym);
-ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
-ST_FUNC int asm_int_expr(TCCState *s1);
-ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess);
-/* ------------ i386-asm.c ------------ */
-ST_FUNC void gen_expr32(ExprValue *pe);
-#ifdef TCC_TARGET_X86_64
-ST_FUNC void gen_expr64(ExprValue *pe);
-#endif
-ST_FUNC void asm_opcode(TCCState *s1, int opcode);
-ST_FUNC int asm_parse_regvar(int t);
-ST_FUNC void asm_compute_constraints(ASMOperand *operands, int nb_operands, int nb_outputs, const uint8_t *clobber_regs, int *pout_reg);
-ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier);
-ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, int nb_outputs, int is_output, uint8_t *clobber_regs, int out_reg);
-ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str);
-#endif
-
-/* ------------ tccpe.c -------------- */
-#ifdef TCC_TARGET_PE
-ST_FUNC int pe_load_file(struct TCCState *s1, const char *filename, int fd);
-ST_FUNC int pe_output_file(TCCState * s1, const char *filename);
-ST_FUNC int pe_putimport(TCCState *s1, int dllindex, const char *name, addr_t value);
-#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
-ST_FUNC SValue *pe_getimport(SValue *sv, SValue *v2);
-#endif
-#ifdef TCC_TARGET_X86_64
-ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack);
-#endif
-PUB_FUNC int tcc_get_dllexports(const char *filename, char **pp);
-/* symbol properties stored in Elf32_Sym->st_other */
-# define ST_PE_EXPORT 0x10
-# define ST_PE_IMPORT 0x20
-# define ST_PE_STDCALL 0x40
-#endif
-/* ------------ tccrun.c ----------------- */
-#ifdef TCC_IS_NATIVE
-#ifdef CONFIG_TCC_STATIC
-#define RTLD_LAZY       0x001
-#define RTLD_NOW        0x002
-#define RTLD_GLOBAL     0x100
-#define RTLD_DEFAULT    NULL
-/* dummy function for profiling */
-ST_FUNC void *dlopen(const char *filename, int flag);
-ST_FUNC void dlclose(void *p);
-ST_FUNC const char *dlerror(void);
-ST_FUNC void *dlsym(void *handle, const char *symbol);
-#endif
-#ifdef CONFIG_TCC_BACKTRACE
-ST_DATA int rt_num_callers;
-ST_DATA const char **rt_bound_error_msg;
-ST_DATA void *rt_prog_main;
-ST_FUNC void tcc_set_num_callers(int n);
-#endif
-ST_FUNC void tcc_run_free(TCCState *s1);
-#endif
-
-/* ------------ tcctools.c ----------------- */
-#if 0 /* included in tcc.c */
-ST_FUNC int tcc_tool_ar(TCCState *s, int argc, char **argv);
-#ifdef TCC_TARGET_PE
-ST_FUNC int tcc_tool_impdef(TCCState *s, int argc, char **argv);
-#endif
-ST_FUNC void tcc_tool_cross(TCCState *s, char **argv, int option);
-ST_FUNC void gen_makedeps(TCCState *s, const char *target, const char *filename);
-#endif
-
-/********************************************************/
-#undef ST_DATA
-#if ONE_SOURCE
-#define ST_DATA static
-#else
-#define ST_DATA
-#endif
-/********************************************************/
-#endif /* _TCC_H */
diff --git a/tinyc/tccasm.c b/tinyc/tccasm.c
deleted file mode 100644
index ac1618a52..000000000
--- a/tinyc/tccasm.c
+++ /dev/null
@@ -1,1303 +0,0 @@
-/*
- *  GAS like assembler for TCC
- * 
- *  Copyright (c) 2001-2004 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "tcc.h"
-#ifdef CONFIG_TCC_ASM
-
-ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n)
-{
-    char buf[64];
-    TokenSym *ts;
-
-    snprintf(buf, sizeof(buf), "L..%u", n);
-    ts = tok_alloc(buf, strlen(buf));
-    return ts->tok;
-}
-
-ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
-static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global);
-static Sym sym_dot;
-
-/* Return a symbol we can use inside the assembler, having name NAME.
-   The assembler symbol table is different from the C symbol table
-   (and the Sym members are used differently).  But we must be able
-   to look up file-global C symbols from inside the assembler, e.g.
-   for global asm blocks to be able to refer to defined C symbols.
-
-   This routine gives back either an existing asm-internal
-   symbol, or a new one.  In the latter case the new asm-internal
-   symbol is initialized with info from the C symbol table.
-   
-   If CSYM is non-null we take symbol info from it, otherwise
-   we look up NAME in the C symbol table and use that.  */
-ST_FUNC Sym* get_asm_sym(int name, Sym *csym)
-{
-    Sym *sym = label_find(name);
-    if (!sym) {
-	sym = label_push(&tcc_state->asm_labels, name, 0);
-	sym->type.t = VT_VOID | VT_EXTERN;
-	if (!csym) {
-	    csym = sym_find(name);
-	    /* We might be called for an asm block from inside a C routine
-	       and so might have local decls on the identifier stack.  Search
-	       for the first global one.  */
-	    while (csym && csym->sym_scope)
-	        csym = csym->prev_tok;
-	}
-	/* Now, if we have a defined global symbol copy over
-	   section and offset.  */
-	if (csym &&
-	    ((csym->r & (VT_SYM|VT_CONST)) == (VT_SYM|VT_CONST)) &&
-	    csym->c) {
-	    ElfW(Sym) *esym;
-	    esym = &((ElfW(Sym) *)symtab_section->data)[csym->c];
-	    sym->c = csym->c;
-	    sym->r = esym->st_shndx;
-	    sym->jnext = esym->st_value;
-	    /* XXX can't yet store st_size anywhere.  */
-	    sym->type.t &= ~VT_EXTERN;
-	    /* Mark that this asm symbol doesn't need to be fed back.  */
-	    sym->a.dllimport = 1;
-	} else {
-	    sym->type.t |= VT_STATIC;
-	}
-    }
-    return sym;
-}
-
-/* We do not use the C expression parser to handle symbols. Maybe the
-   C expression parser could be tweaked to do so. */
-
-static void asm_expr_unary(TCCState *s1, ExprValue *pe)
-{
-    Sym *sym;
-    int op, label;
-    uint64_t n;
-    const char *p;
-
-    switch(tok) {
-    case TOK_PPNUM:
-        p = tokc.str.data;
-        n = strtoull(p, (char **)&p, 0);
-        if (*p == 'b' || *p == 'f') {
-            /* backward or forward label */
-            label = asm_get_local_label_name(s1, n);
-            sym = label_find(label);
-            if (*p == 'b') {
-                /* backward : find the last corresponding defined label */
-                if (sym && sym->r == 0)
-                    sym = sym->prev_tok;
-                if (!sym)
-                    tcc_error("local label '%d' not found backward", n);
-            } else {
-                /* forward */
-                if (!sym || sym->r) {
-                    /* if the last label is defined, then define a new one */
-                    sym = label_push(&s1->asm_labels, label, 0);
-                    sym->type.t = VT_STATIC | VT_VOID | VT_EXTERN;
-                }
-            }
-	    pe->v = 0;
-	    pe->sym = sym;
-	    pe->pcrel = 0;
-        } else if (*p == '\0') {
-            pe->v = n;
-            pe->sym = NULL;
-	    pe->pcrel = 0;
-        } else {
-            tcc_error("invalid number syntax");
-        }
-        next();
-        break;
-    case '+':
-        next();
-        asm_expr_unary(s1, pe);
-        break;
-    case '-':
-    case '~':
-        op = tok;
-        next();
-        asm_expr_unary(s1, pe);
-        if (pe->sym)
-            tcc_error("invalid operation with label");
-        if (op == '-')
-            pe->v = -pe->v;
-        else
-            pe->v = ~pe->v;
-        break;
-    case TOK_CCHAR:
-    case TOK_LCHAR:
-	pe->v = tokc.i;
-	pe->sym = NULL;
-	pe->pcrel = 0;
-	next();
-	break;
-    case '(':
-        next();
-        asm_expr(s1, pe);
-        skip(')');
-        break;
-    case '.':
-        pe->v = 0;
-        pe->sym = &sym_dot;
-	pe->pcrel = 0;
-        sym_dot.type.t = VT_VOID | VT_STATIC;
-        sym_dot.r = cur_text_section->sh_num;
-        sym_dot.jnext = ind;
-        next();
-        break;
-    default:
-        if (tok >= TOK_IDENT) {
-            /* label case : if the label was not found, add one */
-	    sym = get_asm_sym(tok, NULL);
-            if (sym->r == SHN_ABS) {
-                /* if absolute symbol, no need to put a symbol value */
-                pe->v = sym->jnext;
-                pe->sym = NULL;
-		pe->pcrel = 0;
-            } else {
-                pe->v = 0;
-                pe->sym = sym;
-		pe->pcrel = 0;
-            }
-            next();
-        } else {
-            tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
-        }
-        break;
-    }
-}
-    
-static void asm_expr_prod(TCCState *s1, ExprValue *pe)
-{
-    int op;
-    ExprValue e2;
-
-    asm_expr_unary(s1, pe);
-    for(;;) {
-        op = tok;
-        if (op != '*' && op != '/' && op != '%' && 
-            op != TOK_SHL && op != TOK_SAR)
-            break;
-        next();
-        asm_expr_unary(s1, &e2);
-        if (pe->sym || e2.sym)
-            tcc_error("invalid operation with label");
-        switch(op) {
-        case '*':
-            pe->v *= e2.v;
-            break;
-        case '/':  
-            if (e2.v == 0) {
-            div_error:
-                tcc_error("division by zero");
-            }
-            pe->v /= e2.v;
-            break;
-        case '%':  
-            if (e2.v == 0)
-                goto div_error;
-            pe->v %= e2.v;
-            break;
-        case TOK_SHL:
-            pe->v <<= e2.v;
-            break;
-        default:
-        case TOK_SAR:
-            pe->v >>= e2.v;
-            break;
-        }
-    }
-}
-
-static void asm_expr_logic(TCCState *s1, ExprValue *pe)
-{
-    int op;
-    ExprValue e2;
-
-    asm_expr_prod(s1, pe);
-    for(;;) {
-        op = tok;
-        if (op != '&' && op != '|' && op != '^')
-            break;
-        next();
-        asm_expr_prod(s1, &e2);
-        if (pe->sym || e2.sym)
-            tcc_error("invalid operation with label");
-        switch(op) {
-        case '&':
-            pe->v &= e2.v;
-            break;
-        case '|':  
-            pe->v |= e2.v;
-            break;
-        default:
-        case '^':
-            pe->v ^= e2.v;
-            break;
-        }
-    }
-}
-
-static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
-{
-    int op;
-    ExprValue e2;
-
-    asm_expr_logic(s1, pe);
-    for(;;) {
-        op = tok;
-        if (op != '+' && op != '-')
-            break;
-        next();
-        asm_expr_logic(s1, &e2);
-        if (op == '+') {
-            if (pe->sym != NULL && e2.sym != NULL)
-                goto cannot_relocate;
-            pe->v += e2.v;
-            if (pe->sym == NULL && e2.sym != NULL)
-                pe->sym = e2.sym;
-        } else {
-            pe->v -= e2.v;
-            /* NOTE: we are less powerful than gas in that case
-               because we store only one symbol in the expression */
-	    if (!e2.sym) {
-		/* OK */
-	    } else if (pe->sym == e2.sym) { 
-		/* OK */
-		pe->sym = NULL; /* same symbols can be subtracted to NULL */
-	    } else if (pe->sym && pe->sym->r == e2.sym->r && pe->sym->r != 0) {
-		/* we also accept defined symbols in the same section */
-		pe->v += pe->sym->jnext - e2.sym->jnext;
-		pe->sym = NULL;
-	    } else if (e2.sym->r == cur_text_section->sh_num) {
-		/* When subtracting a defined symbol in current section
-		   this actually makes the value PC-relative.  */
-		pe->v -= e2.sym->jnext - ind - 4;
-		pe->pcrel = 1;
-		e2.sym = NULL;
-            } else {
-            cannot_relocate:
-                tcc_error("invalid operation with label");
-            }
-        }
-    }
-}
-
-static inline void asm_expr_cmp(TCCState *s1, ExprValue *pe)
-{
-    int op;
-    ExprValue e2;
-
-    asm_expr_sum(s1, pe);
-    for(;;) {
-        op = tok;
-	if (op != TOK_EQ && op != TOK_NE
-	    && (op > TOK_GT || op < TOK_ULE))
-            break;
-        next();
-        asm_expr_sum(s1, &e2);
-        if (pe->sym || e2.sym)
-            tcc_error("invalid operation with label");
-        switch(op) {
-	case TOK_EQ:
-	    pe->v = pe->v == e2.v;
-	    break;
-	case TOK_NE:
-	    pe->v = pe->v != e2.v;
-	    break;
-	case TOK_LT:
-	    pe->v = (int64_t)pe->v < (int64_t)e2.v;
-	    break;
-	case TOK_GE:
-	    pe->v = (int64_t)pe->v >= (int64_t)e2.v;
-	    break;
-	case TOK_LE:
-	    pe->v = (int64_t)pe->v <= (int64_t)e2.v;
-	    break;
-	case TOK_GT:
-	    pe->v = (int64_t)pe->v > (int64_t)e2.v;
-	    break;
-        default:
-            break;
-        }
-	/* GAS compare results are -1/0 not 1/0.  */
-	pe->v = -(int64_t)pe->v;
-    }
-}
-
-ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
-{
-    asm_expr_cmp(s1, pe);
-}
-
-ST_FUNC int asm_int_expr(TCCState *s1)
-{
-    ExprValue e;
-    asm_expr(s1, &e);
-    if (e.sym)
-        expect("constant");
-    return e.v;
-}
-
-/* NOTE: the same name space as C labels is used to avoid using too
-   much memory when storing labels in TokenStrings */
-static Sym* asm_new_label1(TCCState *s1, int label, int is_local,
-                           int sh_num, int value)
-{
-    Sym *sym;
-
-    sym = label_find(label);
-    if (sym) {
-	/* A VT_EXTERN symbol, even if it has a section is considered
-	   overridable.  This is how we "define" .set targets.  Real
-	   definitions won't have VT_EXTERN set.  */
-        if (sym->r && !(sym->type.t & VT_EXTERN)) {
-            /* the label is already defined */
-            if (!is_local) {
-                tcc_error("assembler label '%s' already defined", 
-                      get_tok_str(label, NULL));
-            } else {
-                /* redefinition of local labels is possible */
-                goto new_label;
-            }
-        }
-    } else {
-    new_label:
-        sym = label_push(&s1->asm_labels, label, 0);
-	/* If we need a symbol to hold a value, mark it as
-	   tentative only (for .set).  If this is for a real label
-	   we'll remove VT_EXTERN.  */
-        sym->type.t = VT_STATIC | VT_VOID | VT_EXTERN;
-    }
-    sym->r = sh_num;
-    sym->jnext = value;
-    return sym;
-}
-
-static Sym* asm_new_label(TCCState *s1, int label, int is_local)
-{
-    return asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
-}
-
-/* Set the value of LABEL to that of some expression (possibly
-   involving other symbols).  LABEL can be overwritten later still.  */
-static Sym* set_symbol(TCCState *s1, int label)
-{
-    long n;
-    ExprValue e;
-    next();
-    asm_expr(s1, &e);
-    n = e.v;
-    if (e.sym)
-	n += e.sym->jnext;
-    return asm_new_label1(s1, label, 0, e.sym ? e.sym->r : SHN_ABS, n);
-}
-
-static void asm_free_labels(TCCState *st)
-{
-    Sym *s, *s1;
-    Section *sec;
-
-    for(s = st->asm_labels; s != NULL; s = s1) {
-        s1 = s->prev;
-        /* define symbol value in object file */
-	s->type.t &= ~VT_EXTERN;
-        if (s->r && !s->a.dllimport) {
-            if (s->r == SHN_ABS)
-                sec = SECTION_ABS;
-            else
-                sec = st->sections[s->r];
-            put_extern_sym2(s, sec, s->jnext, 0, 0);
-        }
-        /* remove label */
-        table_ident[s->v - TOK_IDENT]->sym_label = NULL;
-        sym_free(s);
-    }
-    st->asm_labels = NULL;
-}
-
-static void use_section1(TCCState *s1, Section *sec)
-{
-    cur_text_section->data_offset = ind;
-    cur_text_section = sec;
-    ind = cur_text_section->data_offset;
-}
-
-static void use_section(TCCState *s1, const char *name)
-{
-    Section *sec;
-    sec = find_section(s1, name);
-    use_section1(s1, sec);
-}
-
-static void push_section(TCCState *s1, const char *name)
-{
-    Section *sec = find_section(s1, name);
-    sec->prev = cur_text_section;
-    use_section1(s1, sec);
-}
-
-static void pop_section(TCCState *s1)
-{
-    Section *prev = cur_text_section->prev;
-    if (!prev)
-        tcc_error(".popsection without .pushsection");
-    cur_text_section->prev = NULL;
-    use_section1(s1, prev);
-}
-
-static void asm_parse_directive(TCCState *s1, int global)
-{
-    int n, offset, v, size, tok1;
-    Section *sec;
-    uint8_t *ptr;
-
-    /* assembler directive */
-    sec = cur_text_section;
-    switch(tok) {
-    case TOK_ASMDIR_align:
-    case TOK_ASMDIR_balign:
-    case TOK_ASMDIR_p2align:
-    case TOK_ASMDIR_skip:
-    case TOK_ASMDIR_space:
-        tok1 = tok;
-        next();
-        n = asm_int_expr(s1);
-        if (tok1 == TOK_ASMDIR_p2align)
-        {
-            if (n < 0 || n > 30)
-                tcc_error("invalid p2align, must be between 0 and 30");
-            n = 1 << n;
-            tok1 = TOK_ASMDIR_align;
-        }
-        if (tok1 == TOK_ASMDIR_align || tok1 == TOK_ASMDIR_balign) {
-            if (n < 0 || (n & (n-1)) != 0)
-                tcc_error("alignment must be a positive power of two");
-            offset = (ind + n - 1) & -n;
-            size = offset - ind;
-            /* the section must have a compatible alignment */
-            if (sec->sh_addralign < n)
-                sec->sh_addralign = n;
-        } else {
-	    if (n < 0)
-	        n = 0;
-            size = n;
-        }
-        v = 0;
-        if (tok == ',') {
-            next();
-            v = asm_int_expr(s1);
-        }
-    zero_pad:
-        if (sec->sh_type != SHT_NOBITS) {
-            sec->data_offset = ind;
-            ptr = section_ptr_add(sec, size);
-            memset(ptr, v, size);
-        }
-        ind += size;
-        break;
-    case TOK_ASMDIR_quad:
-#ifdef TCC_TARGET_X86_64
-	size = 8;
-	goto asm_data;
-#else
-        next();
-        for(;;) {
-            uint64_t vl;
-            const char *p;
-
-            p = tokc.str.data;
-            if (tok != TOK_PPNUM) {
-            error_constant:
-                tcc_error("64 bit constant");
-            }
-            vl = strtoll(p, (char **)&p, 0);
-            if (*p != '\0')
-                goto error_constant;
-            next();
-            if (sec->sh_type != SHT_NOBITS) {
-                /* XXX: endianness */
-                gen_le32(vl);
-                gen_le32(vl >> 32);
-            } else {
-                ind += 8;
-            }
-            if (tok != ',')
-                break;
-            next();
-        }
-        break;
-#endif
-    case TOK_ASMDIR_byte:
-        size = 1;
-        goto asm_data;
-    case TOK_ASMDIR_word:
-    case TOK_ASMDIR_short:
-        size = 2;
-        goto asm_data;
-    case TOK_ASMDIR_long:
-    case TOK_ASMDIR_int:
-        size = 4;
-    asm_data:
-        next();
-        for(;;) {
-            ExprValue e;
-            asm_expr(s1, &e);
-            if (sec->sh_type != SHT_NOBITS) {
-                if (size == 4) {
-                    gen_expr32(&e);
-#ifdef TCC_TARGET_X86_64
-		} else if (size == 8) {
-		    gen_expr64(&e);
-#endif
-                } else {
-                    if (e.sym)
-                        expect("constant");
-                    if (size == 1)
-                        g(e.v);
-                    else
-                        gen_le16(e.v);
-                }
-            } else {
-                ind += size;
-            }
-            if (tok != ',')
-                break;
-            next();
-        }
-        break;
-    case TOK_ASMDIR_fill:
-        {
-            int repeat, size, val, i, j;
-            uint8_t repeat_buf[8];
-            next();
-            repeat = asm_int_expr(s1);
-            if (repeat < 0) {
-                tcc_error("repeat < 0; .fill ignored");
-                break;
-            }
-            size = 1;
-            val = 0;
-            if (tok == ',') {
-                next();
-                size = asm_int_expr(s1);
-                if (size < 0) {
-                    tcc_error("size < 0; .fill ignored");
-                    break;
-                }
-                if (size > 8)
-                    size = 8;
-                if (tok == ',') {
-                    next();
-                    val = asm_int_expr(s1);
-                }
-            }
-            /* XXX: endianness */
-            repeat_buf[0] = val;
-            repeat_buf[1] = val >> 8;
-            repeat_buf[2] = val >> 16;
-            repeat_buf[3] = val >> 24;
-            repeat_buf[4] = 0;
-            repeat_buf[5] = 0;
-            repeat_buf[6] = 0;
-            repeat_buf[7] = 0;
-            for(i = 0; i < repeat; i++) {
-                for(j = 0; j < size; j++) {
-                    g(repeat_buf[j]);
-                }
-            }
-        }
-        break;
-    case TOK_ASMDIR_rept:
-        {
-            int repeat;
-            TokenString *init_str;
-            next();
-            repeat = asm_int_expr(s1);
-            init_str = tok_str_alloc();
-            while (next(), tok != TOK_ASMDIR_endr) {
-                if (tok == CH_EOF)
-                    tcc_error("we at end of file, .endr not found");
-                tok_str_add_tok(init_str);
-            }
-            tok_str_add(init_str, -1);
-            tok_str_add(init_str, 0);
-            begin_macro(init_str, 1);
-            while (repeat-- > 0) {
-                tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS),
-				      global);
-                macro_ptr = init_str->str;
-            }
-            end_macro();
-            next();
-            break;
-        }
-    case TOK_ASMDIR_org:
-        {
-            unsigned long n;
-	    ExprValue e;
-            next();
-	    asm_expr(s1, &e);
-	    n = e.v;
-	    if (e.sym) {
-		if (e.sym->r != cur_text_section->sh_num)
-		  expect("constant or same-section symbol");
-		n += e.sym->jnext;
-	    }
-            if (n < ind)
-                tcc_error("attempt to .org backwards");
-            v = 0;
-            size = n - ind;
-            goto zero_pad;
-        }
-        break;
-    case TOK_ASMDIR_set:
-	next();
-	tok1 = tok;
-	next();
-	/* Also accept '.set stuff', but don't do anything with this.
-	   It's used in GAS to set various features like '.set mips16'.  */
-	if (tok == ',')
-	    set_symbol(s1, tok1);
-	break;
-    case TOK_ASMDIR_globl:
-    case TOK_ASMDIR_global:
-    case TOK_ASMDIR_weak:
-    case TOK_ASMDIR_hidden:
-	tok1 = tok;
-	do { 
-            Sym *sym;
-
-            next();
-            sym = get_asm_sym(tok, NULL);
-	    if (tok1 != TOK_ASMDIR_hidden)
-                sym->type.t &= ~VT_STATIC;
-            if (tok1 == TOK_ASMDIR_weak)
-                sym->a.weak = 1;
-	    else if (tok1 == TOK_ASMDIR_hidden)
-	        sym->a.visibility = STV_HIDDEN;
-            next();
-	} while (tok == ',');
-	break;
-    case TOK_ASMDIR_string:
-    case TOK_ASMDIR_ascii:
-    case TOK_ASMDIR_asciz:
-        {
-            const uint8_t *p;
-            int i, size, t;
-
-            t = tok;
-            next();
-            for(;;) {
-                if (tok != TOK_STR)
-                    expect("string constant");
-                p = tokc.str.data;
-                size = tokc.str.size;
-                if (t == TOK_ASMDIR_ascii && size > 0)
-                    size--;
-                for(i = 0; i < size; i++)
-                    g(p[i]);
-                next();
-                if (tok == ',') {
-                    next();
-                } else if (tok != TOK_STR) {
-                    break;
-                }
-            }
-	}
-	break;
-    case TOK_ASMDIR_text:
-    case TOK_ASMDIR_data:
-    case TOK_ASMDIR_bss:
-	{ 
-            char sname[64];
-            tok1 = tok;
-            n = 0;
-            next();
-            if (tok != ';' && tok != TOK_LINEFEED) {
-		n = asm_int_expr(s1);
-		next();
-            }
-            if (n)
-                sprintf(sname, "%s%d", get_tok_str(tok1, NULL), n);
-            else
-                sprintf(sname, "%s", get_tok_str(tok1, NULL));
-            use_section(s1, sname);
-	}
-	break;
-    case TOK_ASMDIR_file:
-        {
-            char filename[512];
-
-            filename[0] = '\0';
-            next();
-
-            if (tok == TOK_STR)
-                pstrcat(filename, sizeof(filename), tokc.str.data);
-            else
-                pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
-
-            if (s1->warn_unsupported)
-                tcc_warning("ignoring .file %s", filename);
-
-            next();
-        }
-        break;
-    case TOK_ASMDIR_ident:
-        {
-            char ident[256];
-
-            ident[0] = '\0';
-            next();
-
-            if (tok == TOK_STR)
-                pstrcat(ident, sizeof(ident), tokc.str.data);
-            else
-                pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
-
-            if (s1->warn_unsupported)
-                tcc_warning("ignoring .ident %s", ident);
-
-            next();
-        }
-        break;
-    case TOK_ASMDIR_size:
-        { 
-            Sym *sym;
-
-            next();
-            sym = label_find(tok);
-            if (!sym) {
-                tcc_error("label not found: %s", get_tok_str(tok, NULL));
-            }
-
-            /* XXX .size name,label2-label1 */
-            if (s1->warn_unsupported)
-                tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL));
-
-            next();
-            skip(',');
-            while (tok != TOK_LINEFEED && tok != ';' && tok != CH_EOF) {
-                next();
-            }
-        }
-        break;
-    case TOK_ASMDIR_type:
-        { 
-            Sym *sym;
-            const char *newtype;
-
-            next();
-            sym = get_asm_sym(tok, NULL);
-            next();
-            skip(',');
-            if (tok == TOK_STR) {
-                newtype = tokc.str.data;
-            } else {
-                if (tok == '@' || tok == '%')
-                    next();
-                newtype = get_tok_str(tok, NULL);
-            }
-
-            if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
-                sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
-            }
-            else if (s1->warn_unsupported)
-                tcc_warning("change type of '%s' from 0x%x to '%s' ignored", 
-                    get_tok_str(sym->v, NULL), sym->type.t, newtype);
-
-            next();
-        }
-        break;
-    case TOK_ASMDIR_pushsection:
-    case TOK_ASMDIR_section:
-        {
-            char sname[256];
-	    int old_nb_section = s1->nb_sections;
-
-	    tok1 = tok;
-            /* XXX: support more options */
-            next();
-            sname[0] = '\0';
-            while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
-                if (tok == TOK_STR)
-                    pstrcat(sname, sizeof(sname), tokc.str.data);
-                else
-                    pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
-                next();
-            }
-            if (tok == ',') {
-                /* skip section options */
-                next();
-                if (tok != TOK_STR)
-                    expect("string constant");
-                next();
-                if (tok == ',') {
-                    next();
-                    if (tok == '@' || tok == '%')
-                        next();
-                    next();
-                }
-            }
-            last_text_section = cur_text_section;
-	    if (tok1 == TOK_ASMDIR_section)
-	        use_section(s1, sname);
-	    else
-	        push_section(s1, sname);
-	    /* If we just allocated a new section reset its alignment to
-	       1.  new_section normally acts for GCC compatibility and
-	       sets alignment to PTR_SIZE.  The assembler behaves different. */
-	    if (old_nb_section != s1->nb_sections)
-	        cur_text_section->sh_addralign = 1;
-        }
-        break;
-    case TOK_ASMDIR_previous:
-        { 
-            Section *sec;
-            next();
-            if (!last_text_section)
-                tcc_error("no previous section referenced");
-            sec = cur_text_section;
-            use_section1(s1, last_text_section);
-            last_text_section = sec;
-        }
-        break;
-    case TOK_ASMDIR_popsection:
-	next();
-	pop_section(s1);
-	break;
-#ifdef TCC_TARGET_I386
-    case TOK_ASMDIR_code16:
-        {
-            next();
-            s1->seg_size = 16;
-        }
-        break;
-    case TOK_ASMDIR_code32:
-        {
-            next();
-            s1->seg_size = 32;
-        }
-        break;
-#endif
-#ifdef TCC_TARGET_X86_64
-    /* added for compatibility with GAS */
-    case TOK_ASMDIR_code64:
-        next();
-        break;
-#endif
-    default:
-        tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
-        break;
-    }
-}
-
-
-/* assemble a file */
-static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global)
-{
-    int opcode;
-    int saved_parse_flags = parse_flags;
-
-    /* XXX: undefine C labels */
-    parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
-    if (do_preprocess)
-        parse_flags |= PARSE_FLAG_PREPROCESS;
-    for(;;) {
-        next();
-        if (tok == TOK_EOF)
-            break;
-        /* generate line number info */
-        if (global && s1->do_debug)
-            tcc_debug_line(s1);
-        parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
-    redo:
-        if (tok == '#') {
-            /* horrible gas comment */
-            while (tok != TOK_LINEFEED)
-                next();
-        } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
-            asm_parse_directive(s1, global);
-        } else if (tok == TOK_PPNUM) {
-	    Sym *sym;
-            const char *p;
-            int n;
-            p = tokc.str.data;
-            n = strtoul(p, (char **)&p, 10);
-            if (*p != '\0')
-                expect("':'");
-            /* new local label */
-            sym = asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
-	    /* Remove the marker for tentative definitions.  */
-	    sym->type.t &= ~VT_EXTERN;
-            next();
-            skip(':');
-            goto redo;
-        } else if (tok >= TOK_IDENT) {
-            /* instruction or label */
-            opcode = tok;
-            next();
-            if (tok == ':') {
-                /* handle "extern void vide(void); __asm__("vide: ret");" as
-                "__asm__("globl vide\nvide: ret");" */
-                Sym *sym = sym_find(opcode);
-                if (sym && (sym->type.t & VT_EXTERN) && global) {
-                    sym = label_find(opcode);
-                    if (!sym) {
-                        sym = label_push(&s1->asm_labels, opcode, 0);
-                        sym->type.t = VT_VOID | VT_EXTERN;
-                    }
-                }
-                /* new label */
-                sym = asm_new_label(s1, opcode, 0);
-		sym->type.t &= ~VT_EXTERN;
-                next();
-                goto redo;
-            } else if (tok == '=') {
-		set_symbol(s1, opcode);
-                goto redo;
-            } else {
-                asm_opcode(s1, opcode);
-            }
-        }
-        /* end of line */
-        if (tok != ';' && tok != TOK_LINEFEED)
-            expect("end of line");
-        parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
-    }
-
-    asm_free_labels(s1);
-    parse_flags = saved_parse_flags;
-    return 0;
-}
-
-/* Assemble the current file */
-ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
-{
-    int ret;
-    tcc_debug_start(s1);
-    /* default section is text */
-    cur_text_section = text_section;
-    ind = cur_text_section->data_offset;
-    nocode_wanted = 0;
-    ret = tcc_assemble_internal(s1, do_preprocess, 1);
-    cur_text_section->data_offset = ind;
-    tcc_debug_end(s1);
-    return ret;
-}
-
-/********************************************************************/
-/* GCC inline asm support */
-
-/* assemble the string 'str' in the current C compilation unit without
-   C preprocessing. NOTE: str is modified by modifying the '\0' at the
-   end */
-static void tcc_assemble_inline(TCCState *s1, char *str, int len, int global)
-{
-    const int *saved_macro_ptr = macro_ptr;
-    int dotid = set_idnum('.', IS_ID);
-
-    tcc_open_bf(s1, ":asm:", len);
-    memcpy(file->buffer, str, len);
-    macro_ptr = NULL;
-    tcc_assemble_internal(s1, 0, global);
-    tcc_close();
-
-    set_idnum('.', dotid);
-    macro_ptr = saved_macro_ptr;
-}
-
-/* find a constraint by its number or id (gcc 3 extended
-   syntax). return -1 if not found. Return in *pp in char after the
-   constraint */
-ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands, 
-                           const char *name, const char **pp)
-{
-    int index;
-    TokenSym *ts;
-    const char *p;
-
-    if (isnum(*name)) {
-        index = 0;
-        while (isnum(*name)) {
-            index = (index * 10) + (*name) - '0';
-            name++;
-        }
-        if ((unsigned)index >= nb_operands)
-            index = -1;
-    } else if (*name == '[') {
-        name++;
-        p = strchr(name, ']');
-        if (p) {
-            ts = tok_alloc(name, p - name);
-            for(index = 0; index < nb_operands; index++) {
-                if (operands[index].id == ts->tok)
-                    goto found;
-            }
-            index = -1;
-        found:
-            name = p + 1;
-        } else {
-            index = -1;
-        }
-    } else {
-        index = -1;
-    }
-    if (pp)
-        *pp = name;
-    return index;
-}
-
-static void subst_asm_operands(ASMOperand *operands, int nb_operands, 
-                               CString *out_str, CString *in_str)
-{
-    int c, index, modifier;
-    const char *str;
-    ASMOperand *op;
-    SValue sv;
-
-    cstr_new(out_str);
-    str = in_str->data;
-    for(;;) {
-        c = *str++;
-        if (c == '%') {
-            if (*str == '%') {
-                str++;
-                goto add_char;
-            }
-            modifier = 0;
-            if (*str == 'c' || *str == 'n' ||
-                *str == 'b' || *str == 'w' || *str == 'h' || *str == 'k' ||
-		*str == 'q' ||
-		/* P in GCC would add "@PLT" to symbol refs in PIC mode,
-		   and make literal operands not be decorated with '$'.  */
-		*str == 'P')
-                modifier = *str++;
-            index = find_constraint(operands, nb_operands, str, &str);
-            if (index < 0)
-                tcc_error("invalid operand reference after %%");
-            op = &operands[index];
-            sv = *op->vt;
-            if (op->reg >= 0) {
-                sv.r = op->reg;
-                if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
-                    sv.r |= VT_LVAL;
-            }
-            subst_asm_operand(out_str, &sv, modifier);
-        } else {
-        add_char:
-            cstr_ccat(out_str, c);
-            if (c == '\0')
-                break;
-        }
-    }
-}
-
-
-static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
-                               int is_output)
-{
-    ASMOperand *op;
-    int nb_operands;
-
-    if (tok != ':') {
-        nb_operands = *nb_operands_ptr;
-        for(;;) {
-	    CString astr;
-            if (nb_operands >= MAX_ASM_OPERANDS)
-                tcc_error("too many asm operands");
-            op = &operands[nb_operands++];
-            op->id = 0;
-            if (tok == '[') {
-                next();
-                if (tok < TOK_IDENT)
-                    expect("identifier");
-                op->id = tok;
-                next();
-                skip(']');
-            }
-	    parse_mult_str(&astr, "string constant");
-            op->constraint = tcc_malloc(astr.size);
-            strcpy(op->constraint, astr.data);
-	    cstr_free(&astr);
-            skip('(');
-            gexpr();
-            if (is_output) {
-                if (!(vtop->type.t & VT_ARRAY))
-                    test_lvalue();
-            } else {
-                /* we want to avoid LLOCAL case, except when the 'm'
-                   constraint is used. Note that it may come from
-                   register storage, so we need to convert (reg)
-                   case */
-                if ((vtop->r & VT_LVAL) &&
-                    ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
-                     (vtop->r & VT_VALMASK) < VT_CONST) &&
-                    !strchr(op->constraint, 'm')) {
-                    gv(RC_INT);
-                }
-            }
-            op->vt = vtop;
-            skip(')');
-            if (tok == ',') {
-                next();
-            } else {
-                break;
-            }
-        }
-        *nb_operands_ptr = nb_operands;
-    }
-}
-
-/* parse the GCC asm() instruction */
-ST_FUNC void asm_instr(void)
-{
-    CString astr, astr1;
-    ASMOperand operands[MAX_ASM_OPERANDS];
-    int nb_outputs, nb_operands, i, must_subst, out_reg;
-    uint8_t clobber_regs[NB_ASM_REGS];
-
-    next();
-    /* since we always generate the asm() instruction, we can ignore
-       volatile */
-    if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
-        next();
-    }
-    parse_asm_str(&astr);
-    nb_operands = 0;
-    nb_outputs = 0;
-    must_subst = 0;
-    memset(clobber_regs, 0, sizeof(clobber_regs));
-    if (tok == ':') {
-        next();
-        must_subst = 1;
-        /* output args */
-        parse_asm_operands(operands, &nb_operands, 1);
-        nb_outputs = nb_operands;
-        if (tok == ':') {
-            next();
-            if (tok != ')') {
-                /* input args */
-                parse_asm_operands(operands, &nb_operands, 0);
-                if (tok == ':') {
-                    /* clobber list */
-                    /* XXX: handle registers */
-                    next();
-                    for(;;) {
-                        if (tok != TOK_STR)
-                            expect("string constant");
-                        asm_clobber(clobber_regs, tokc.str.data);
-                        next();
-                        if (tok == ',') {
-                            next();
-                        } else {
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-    }
-    skip(')');
-    /* NOTE: we do not eat the ';' so that we can restore the current
-       token after the assembler parsing */
-    if (tok != ';')
-        expect("';'");
-    
-    /* save all values in the memory */
-    save_regs(0);
-
-    /* compute constraints */
-    asm_compute_constraints(operands, nb_operands, nb_outputs, 
-                            clobber_regs, &out_reg);
-
-    /* substitute the operands in the asm string. No substitution is
-       done if no operands (GCC behaviour) */
-#ifdef ASM_DEBUG
-    printf("asm: \"%s\"\n", (char *)astr.data);
-#endif
-    if (must_subst) {
-        subst_asm_operands(operands, nb_operands, &astr1, &astr);
-        cstr_free(&astr);
-    } else {
-        astr1 = astr;
-    }
-#ifdef ASM_DEBUG
-    printf("subst_asm: \"%s\"\n", (char *)astr1.data);
-#endif
-
-    /* generate loads */
-    asm_gen_code(operands, nb_operands, nb_outputs, 0, 
-                 clobber_regs, out_reg);    
-
-    /* assemble the string with tcc internal assembler */
-    tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1, 0);
-
-    /* restore the current C token */
-    next();
-
-    /* store the output values if needed */
-    asm_gen_code(operands, nb_operands, nb_outputs, 1, 
-                 clobber_regs, out_reg);
-    
-    /* free everything */
-    for(i=0;i<nb_operands;i++) {
-        ASMOperand *op;
-        op = &operands[i];
-        tcc_free(op->constraint);
-        vpop();
-    }
-    cstr_free(&astr1);
-}
-
-ST_FUNC void asm_global_instr(void)
-{
-    CString astr;
-    int saved_nocode_wanted = nocode_wanted;
-
-    /* Global asm blocks are always emitted.  */
-    nocode_wanted = 0;
-    next();
-    parse_asm_str(&astr);
-    skip(')');
-    /* NOTE: we do not eat the ';' so that we can restore the current
-       token after the assembler parsing */
-    if (tok != ';')
-        expect("';'");
-    
-#ifdef ASM_DEBUG
-    printf("asm_global: \"%s\"\n", (char *)astr.data);
-#endif
-    cur_text_section = text_section;
-    ind = cur_text_section->data_offset;
-
-    /* assemble the string with tcc internal assembler */
-    tcc_assemble_inline(tcc_state, astr.data, astr.size - 1, 1);
-    
-    cur_text_section->data_offset = ind;
-
-    /* restore the current C token */
-    next();
-
-    cstr_free(&astr);
-    nocode_wanted = saved_nocode_wanted;
-}
-#endif /* CONFIG_TCC_ASM */
diff --git a/tinyc/tcccoff.c b/tinyc/tcccoff.c
deleted file mode 100644
index 1421ca265..000000000
--- a/tinyc/tcccoff.c
+++ /dev/null
@@ -1,948 +0,0 @@
-/*
- *  COFF file handling for TCC
- * 
- *  Copyright (c) 2003, 2004 TK
- *  Copyright (c) 2004 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "tcc.h"
-
-#define MAXNSCNS 255		/* MAXIMUM NUMBER OF SECTIONS         */
-#define MAX_STR_TABLE 1000000
-AOUTHDR o_filehdr;		/* OPTIONAL (A.OUT) FILE HEADER       */
-
-SCNHDR section_header[MAXNSCNS];
-
-#define MAX_FUNCS 1000
-#define MAX_FUNC_NAME_LENGTH 128
-
-int nFuncs;
-char Func[MAX_FUNCS][MAX_FUNC_NAME_LENGTH];
-char AssociatedFile[MAX_FUNCS][MAX_FUNC_NAME_LENGTH];
-int LineNoFilePtr[MAX_FUNCS];
-int EndAddress[MAX_FUNCS];
-int LastLineNo[MAX_FUNCS];
-int FuncEntries[MAX_FUNCS];
-
-int OutputTheSection(Section * sect);
-short int GetCoffFlags(const char *s);
-void SortSymbolTable(void);
-Section *FindSection(TCCState * s1, const char *sname);
-
-int C67_main_entry_point;
-
-int FindCoffSymbolIndex(const char *func_name);
-int nb_syms;
-
-typedef struct {
-    long tag;
-    long size;
-    long fileptr;
-    long nextsym;
-    short int dummy;
-} AUXFUNC;
-
-typedef struct {
-    long regmask;
-    unsigned short lineno;
-    unsigned short nentries;
-    int localframe;
-    int nextentry;
-    short int dummy;
-} AUXBF;
-
-typedef struct {
-    long dummy;
-    unsigned short lineno;
-    unsigned short dummy1;
-    int dummy2;
-    int dummy3;
-    unsigned short dummy4;
-} AUXEF;
-
-ST_FUNC int tcc_output_coff(TCCState *s1, FILE *f)
-{
-    Section *tcc_sect;
-    SCNHDR *coff_sec;
-    int file_pointer;
-    char *Coff_str_table, *pCoff_str_table;
-    int CoffTextSectionNo, coff_nb_syms;
-    FILHDR file_hdr;		/* FILE HEADER STRUCTURE              */
-    Section *stext, *sdata, *sbss;
-    int i, NSectionsToOutput = 0;
-
-    Coff_str_table = pCoff_str_table = NULL;
-
-    stext = FindSection(s1, ".text");
-    sdata = FindSection(s1, ".data");
-    sbss = FindSection(s1, ".bss");
-
-    nb_syms = symtab_section->data_offset / sizeof(Elf32_Sym);
-    coff_nb_syms = FindCoffSymbolIndex("XXXXXXXXXX1");
-
-    file_hdr.f_magic = COFF_C67_MAGIC;	/* magic number */
-    file_hdr.f_timdat = 0;	/* time & date stamp */
-    file_hdr.f_opthdr = sizeof(AOUTHDR);	/* sizeof(optional hdr) */
-    file_hdr.f_flags = 0x1143;	/* flags (copied from what code composer does) */
-    file_hdr.f_TargetID = 0x99;	/* for C6x = 0x0099 */
-
-    o_filehdr.magic = 0x0108;	/* see magic.h                          */
-    o_filehdr.vstamp = 0x0190;	/* version stamp                        */
-    o_filehdr.tsize = stext->data_offset;	/* text size in bytes, padded to FW bdry */
-    o_filehdr.dsize = sdata->data_offset;	/* initialized data "  "                */
-    o_filehdr.bsize = sbss->data_offset;	/* uninitialized data "   "             */
-    o_filehdr.entrypt = C67_main_entry_point;	/* entry pt.                          */
-    o_filehdr.text_start = stext->sh_addr;	/* base of text used for this file      */
-    o_filehdr.data_start = sdata->sh_addr;	/* base of data used for this file      */
-
-
-    // create all the section headers
-
-    file_pointer = FILHSZ + sizeof(AOUTHDR);
-
-    CoffTextSectionNo = -1;
-
-    for (i = 1; i < s1->nb_sections; i++) {
-	coff_sec = &section_header[i];
-	tcc_sect = s1->sections[i];
-
-	if (OutputTheSection(tcc_sect)) {
-	    NSectionsToOutput++;
-
-	    if (CoffTextSectionNo == -1 && tcc_sect == stext)
-		CoffTextSectionNo = NSectionsToOutput;	// rem which coff sect number the .text sect is
-
-	    strcpy(coff_sec->s_name, tcc_sect->name);	/* section name */
-
-	    coff_sec->s_paddr = tcc_sect->sh_addr;	/* physical address */
-	    coff_sec->s_vaddr = tcc_sect->sh_addr;	/* virtual address */
-	    coff_sec->s_size = tcc_sect->data_offset;	/* section size */
-	    coff_sec->s_scnptr = 0;	/* file ptr to raw data for section */
-	    coff_sec->s_relptr = 0;	/* file ptr to relocation */
-	    coff_sec->s_lnnoptr = 0;	/* file ptr to line numbers */
-	    coff_sec->s_nreloc = 0;	/* number of relocation entries */
-	    coff_sec->s_flags = GetCoffFlags(coff_sec->s_name);	/* flags */
-	    coff_sec->s_reserved = 0;	/* reserved byte */
-	    coff_sec->s_page = 0;	/* memory page id */
-
-	    file_pointer += sizeof(SCNHDR);
-	}
-    }
-
-    file_hdr.f_nscns = NSectionsToOutput;	/* number of sections */
-
-    // now loop through and determine file pointer locations
-    // for the raw data
-
-
-    for (i = 1; i < s1->nb_sections; i++) {
-	coff_sec = &section_header[i];
-	tcc_sect = s1->sections[i];
-
-	if (OutputTheSection(tcc_sect)) {
-	    // put raw data
-	    coff_sec->s_scnptr = file_pointer;	/* file ptr to raw data for section */
-	    file_pointer += coff_sec->s_size;
-	}
-    }
-
-    // now loop through and determine file pointer locations
-    // for the relocation data
-
-    for (i = 1; i < s1->nb_sections; i++) {
-	coff_sec = &section_header[i];
-	tcc_sect = s1->sections[i];
-
-	if (OutputTheSection(tcc_sect)) {
-	    // put relocations data
-	    if (coff_sec->s_nreloc > 0) {
-		coff_sec->s_relptr = file_pointer;	/* file ptr to relocation */
-		file_pointer += coff_sec->s_nreloc * sizeof(struct reloc);
-	    }
-	}
-    }
-
-    // now loop through and determine file pointer locations
-    // for the line number data
-
-    for (i = 1; i < s1->nb_sections; i++) {
-	coff_sec = &section_header[i];
-	tcc_sect = s1->sections[i];
-
-	coff_sec->s_nlnno = 0;
-	coff_sec->s_lnnoptr = 0;
-
-	if (s1->do_debug && tcc_sect == stext) {
-	    // count how many line nos data
-
-	    // also find association between source file name and function
-	    // so we can sort the symbol table
-
-
-	    Stab_Sym *sym, *sym_end;
-	    char func_name[MAX_FUNC_NAME_LENGTH],
-		last_func_name[MAX_FUNC_NAME_LENGTH];
-	    unsigned long func_addr, last_pc, pc;
-	    const char *incl_files[INCLUDE_STACK_SIZE];
-	    int incl_index, len, last_line_num;
-	    const char *str, *p;
-
-	    coff_sec->s_lnnoptr = file_pointer;	/* file ptr to linno */
-
-
-	    func_name[0] = '\0';
-	    func_addr = 0;
-	    incl_index = 0;
-	    last_func_name[0] = '\0';
-	    last_pc = 0xffffffff;
-	    last_line_num = 1;
-	    sym = (Stab_Sym *) stab_section->data + 1;
-	    sym_end =
-		(Stab_Sym *) (stab_section->data +
-			      stab_section->data_offset);
-
-	    nFuncs = 0;
-	    while (sym < sym_end) {
-		switch (sym->n_type) {
-		    /* function start or end */
-		case N_FUN:
-		    if (sym->n_strx == 0) {
-			// end of function
-
-			coff_sec->s_nlnno++;
-			file_pointer += LINESZ;
-
-			pc = sym->n_value + func_addr;
-			func_name[0] = '\0';
-			func_addr = 0;
-			EndAddress[nFuncs] = pc;
-			FuncEntries[nFuncs] =
-			    (file_pointer -
-			     LineNoFilePtr[nFuncs]) / LINESZ - 1;
-			LastLineNo[nFuncs++] = last_line_num + 1;
-		    } else {
-			// beginning of function
-
-			LineNoFilePtr[nFuncs] = file_pointer;
-			coff_sec->s_nlnno++;
-			file_pointer += LINESZ;
-
-			str =
-			    (const char *) stabstr_section->data +
-			    sym->n_strx;
-
-			p = strchr(str, ':');
-			if (!p) {
-			    pstrcpy(func_name, sizeof(func_name), str);
-			    pstrcpy(Func[nFuncs], sizeof(func_name), str);
-			} else {
-			    len = p - str;
-			    if (len > sizeof(func_name) - 1)
-				len = sizeof(func_name) - 1;
-			    memcpy(func_name, str, len);
-			    memcpy(Func[nFuncs], str, len);
-			    func_name[len] = '\0';
-			}
-
-			// save the file that it came in so we can sort later
-			pstrcpy(AssociatedFile[nFuncs], sizeof(func_name),
-				incl_files[incl_index - 1]);
-
-			func_addr = sym->n_value;
-		    }
-		    break;
-
-		    /* line number info */
-		case N_SLINE:
-		    pc = sym->n_value + func_addr;
-
-		    last_pc = pc;
-		    last_line_num = sym->n_desc;
-
-		    /* XXX: slow! */
-		    strcpy(last_func_name, func_name);
-
-		    coff_sec->s_nlnno++;
-		    file_pointer += LINESZ;
-		    break;
-		    /* include files */
-		case N_BINCL:
-		    str =
-			(const char *) stabstr_section->data + sym->n_strx;
-		  add_incl:
-		    if (incl_index < INCLUDE_STACK_SIZE) {
-			incl_files[incl_index++] = str;
-		    }
-		    break;
-		case N_EINCL:
-		    if (incl_index > 1)
-			incl_index--;
-		    break;
-		case N_SO:
-		    if (sym->n_strx == 0) {
-			incl_index = 0;	/* end of translation unit */
-		    } else {
-			str =
-			    (const char *) stabstr_section->data +
-			    sym->n_strx;
-			/* do not add path */
-			len = strlen(str);
-			if (len > 0 && str[len - 1] != '/')
-			    goto add_incl;
-		    }
-		    break;
-		}
-		sym++;
-	    }
-	}
-
-    }
-
-    file_hdr.f_symptr = file_pointer;	/* file pointer to symtab */
-
-    if (s1->do_debug)
-	file_hdr.f_nsyms = coff_nb_syms;	/* number of symtab entries */
-    else
-	file_hdr.f_nsyms = 0;
-
-    file_pointer += file_hdr.f_nsyms * SYMNMLEN;
-
-    // OK now we are all set to write the file
-
-
-    fwrite(&file_hdr, FILHSZ, 1, f);
-    fwrite(&o_filehdr, sizeof(o_filehdr), 1, f);
-
-    // write section headers
-    for (i = 1; i < s1->nb_sections; i++) {
-	coff_sec = &section_header[i];
-	tcc_sect = s1->sections[i];
-
-	if (OutputTheSection(tcc_sect)) {
-	    fwrite(coff_sec, sizeof(SCNHDR), 1, f);
-	}
-    }
-
-    // write raw data
-    for (i = 1; i < s1->nb_sections; i++) {
-	coff_sec = &section_header[i];
-	tcc_sect = s1->sections[i];
-
-	if (OutputTheSection(tcc_sect)) {
-	    fwrite(tcc_sect->data, tcc_sect->data_offset, 1, f);
-	}
-    }
-
-    // write relocation data
-    for (i = 1; i < s1->nb_sections; i++) {
-	coff_sec = &section_header[i];
-	tcc_sect = s1->sections[i];
-
-	if (OutputTheSection(tcc_sect)) {
-	    // put relocations data
-	    if (coff_sec->s_nreloc > 0) {
-		fwrite(tcc_sect->reloc,
-		       coff_sec->s_nreloc * sizeof(struct reloc), 1, f);
-	    }
-	}
-    }
-
-
-    // group the symbols in order of filename, func1, func2, etc
-    // finally global symbols
-
-    if (s1->do_debug)
-	SortSymbolTable();
-
-    // write line no data
-
-    for (i = 1; i < s1->nb_sections; i++) {
-	coff_sec = &section_header[i];
-	tcc_sect = s1->sections[i];
-
-	if (s1->do_debug && tcc_sect == stext) {
-	    // count how many line nos data
-
-
-	    Stab_Sym *sym, *sym_end;
-	    char func_name[128], last_func_name[128];
-	    unsigned long func_addr, last_pc, pc;
-	    const char *incl_files[INCLUDE_STACK_SIZE];
-	    int incl_index, len, last_line_num;
-	    const char *str, *p;
-
-	    LINENO CoffLineNo;
-
-	    func_name[0] = '\0';
-	    func_addr = 0;
-	    incl_index = 0;
-	    last_func_name[0] = '\0';
-	    last_pc = 0;
-	    last_line_num = 1;
-	    sym = (Stab_Sym *) stab_section->data + 1;
-	    sym_end =
-		(Stab_Sym *) (stab_section->data +
-			      stab_section->data_offset);
-
-	    while (sym < sym_end) {
-		switch (sym->n_type) {
-		    /* function start or end */
-		case N_FUN:
-		    if (sym->n_strx == 0) {
-			// end of function
-
-			CoffLineNo.l_addr.l_paddr = last_pc;
-			CoffLineNo.l_lnno = last_line_num + 1;
-			fwrite(&CoffLineNo, 6, 1, f);
-
-			pc = sym->n_value + func_addr;
-			func_name[0] = '\0';
-			func_addr = 0;
-		    } else {
-			// beginning of function
-
-			str =
-			    (const char *) stabstr_section->data +
-			    sym->n_strx;
-
-
-			p = strchr(str, ':');
-			if (!p) {
-			    pstrcpy(func_name, sizeof(func_name), str);
-			} else {
-			    len = p - str;
-			    if (len > sizeof(func_name) - 1)
-				len = sizeof(func_name) - 1;
-			    memcpy(func_name, str, len);
-			    func_name[len] = '\0';
-			}
-			func_addr = sym->n_value;
-			last_pc = func_addr;
-			last_line_num = -1;
-
-			// output a function begin
-
-			CoffLineNo.l_addr.l_symndx =
-			    FindCoffSymbolIndex(func_name);
-			CoffLineNo.l_lnno = 0;
-
-			fwrite(&CoffLineNo, 6, 1, f);
-		    }
-		    break;
-
-		    /* line number info */
-		case N_SLINE:
-		    pc = sym->n_value + func_addr;
-
-
-		    /* XXX: slow! */
-		    strcpy(last_func_name, func_name);
-
-		    // output a line reference
-
-		    CoffLineNo.l_addr.l_paddr = last_pc;
-
-		    if (last_line_num == -1) {
-			CoffLineNo.l_lnno = sym->n_desc;
-		    } else {
-			CoffLineNo.l_lnno = last_line_num + 1;
-		    }
-
-		    fwrite(&CoffLineNo, 6, 1, f);
-
-		    last_pc = pc;
-		    last_line_num = sym->n_desc;
-
-		    break;
-
-		    /* include files */
-		case N_BINCL:
-		    str =
-			(const char *) stabstr_section->data + sym->n_strx;
-		  add_incl2:
-		    if (incl_index < INCLUDE_STACK_SIZE) {
-			incl_files[incl_index++] = str;
-		    }
-		    break;
-		case N_EINCL:
-		    if (incl_index > 1)
-			incl_index--;
-		    break;
-		case N_SO:
-		    if (sym->n_strx == 0) {
-			incl_index = 0;	/* end of translation unit */
-		    } else {
-			str =
-			    (const char *) stabstr_section->data +
-			    sym->n_strx;
-			/* do not add path */
-			len = strlen(str);
-			if (len > 0 && str[len - 1] != '/')
-			    goto add_incl2;
-		    }
-		    break;
-		}
-		sym++;
-	    }
-	}
-    }
-
-    // write symbol table
-    if (s1->do_debug) {
-	int k;
-	struct syment csym;
-	AUXFUNC auxfunc;
-	AUXBF auxbf;
-	AUXEF auxef;
-	int i;
-	Elf32_Sym *p;
-	const char *name;
-	int nstr;
-	int n = 0;
-
-	Coff_str_table = (char *) tcc_malloc(MAX_STR_TABLE);
-	pCoff_str_table = Coff_str_table;
-	nstr = 0;
-
-	p = (Elf32_Sym *) symtab_section->data;
-
-
-	for (i = 0; i < nb_syms; i++) {
-
-	    name = symtab_section->link->data + p->st_name;
-
-	    for (k = 0; k < 8; k++)
-		csym._n._n_name[k] = 0;
-
-	    if (strlen(name) <= 8) {
-		strcpy(csym._n._n_name, name);
-	    } else {
-		if (pCoff_str_table - Coff_str_table + strlen(name) >
-		    MAX_STR_TABLE - 1)
-		    tcc_error("String table too large");
-
-		csym._n._n_n._n_zeroes = 0;
-		csym._n._n_n._n_offset =
-		    pCoff_str_table - Coff_str_table + 4;
-
-		strcpy(pCoff_str_table, name);
-		pCoff_str_table += strlen(name) + 1;	// skip over null
-		nstr++;
-	    }
-
-	    if (p->st_info == 4) {
-		// put a filename symbol
-		csym.n_value = 33;	// ?????
-		csym.n_scnum = N_DEBUG;
-		csym.n_type = 0;
-		csym.n_sclass = C_FILE;
-		csym.n_numaux = 0;
-		fwrite(&csym, 18, 1, f);
-		n++;
-
-	    } else if (p->st_info == 0x12) {
-		// find the function data
-
-		for (k = 0; k < nFuncs; k++) {
-		    if (strcmp(name, Func[k]) == 0)
-			break;
-		}
-
-		if (k >= nFuncs) {
-		    tcc_error("debug info can't find function: %s", name);
-		}
-		// put a Function Name
-
-		csym.n_value = p->st_value;	// physical address
-		csym.n_scnum = CoffTextSectionNo;
-		csym.n_type = MKTYPE(T_INT, DT_FCN, 0, 0, 0, 0, 0);
-		csym.n_sclass = C_EXT;
-		csym.n_numaux = 1;
-		fwrite(&csym, 18, 1, f);
-
-		// now put aux info
-
-		auxfunc.tag = 0;
-		auxfunc.size = EndAddress[k] - p->st_value;
-		auxfunc.fileptr = LineNoFilePtr[k];
-		auxfunc.nextsym = n + 6;	// tktk
-		auxfunc.dummy = 0;
-		fwrite(&auxfunc, 18, 1, f);
-
-		// put a .bf
-
-		strcpy(csym._n._n_name, ".bf");
-		csym.n_value = p->st_value;	// physical address
-		csym.n_scnum = CoffTextSectionNo;
-		csym.n_type = 0;
-		csym.n_sclass = C_FCN;
-		csym.n_numaux = 1;
-		fwrite(&csym, 18, 1, f);
-
-		// now put aux info
-
-		auxbf.regmask = 0;
-		auxbf.lineno = 0;
-		auxbf.nentries = FuncEntries[k];
-		auxbf.localframe = 0;
-		auxbf.nextentry = n + 6;
-		auxbf.dummy = 0;
-		fwrite(&auxbf, 18, 1, f);
-
-		// put a .ef
-
-		strcpy(csym._n._n_name, ".ef");
-		csym.n_value = EndAddress[k];	// physical address  
-		csym.n_scnum = CoffTextSectionNo;
-		csym.n_type = 0;
-		csym.n_sclass = C_FCN;
-		csym.n_numaux = 1;
-		fwrite(&csym, 18, 1, f);
-
-		// now put aux info
-
-		auxef.dummy = 0;
-		auxef.lineno = LastLineNo[k];
-		auxef.dummy1 = 0;
-		auxef.dummy2 = 0;
-		auxef.dummy3 = 0;
-		auxef.dummy4 = 0;
-		fwrite(&auxef, 18, 1, f);
-
-		n += 6;
-
-	    } else {
-		// try an put some type info
-
-		if ((p->st_other & VT_BTYPE) == VT_DOUBLE) {
-		    csym.n_type = T_DOUBLE;	// int
-		    csym.n_sclass = C_EXT;
-		} else if ((p->st_other & VT_BTYPE) == VT_FLOAT) {
-		    csym.n_type = T_FLOAT;
-		    csym.n_sclass = C_EXT;
-		} else if ((p->st_other & VT_BTYPE) == VT_INT) {
-		    csym.n_type = T_INT;	// int
-		    csym.n_sclass = C_EXT;
-		} else if ((p->st_other & VT_BTYPE) == VT_SHORT) {
-		    csym.n_type = T_SHORT;
-		    csym.n_sclass = C_EXT;
-		} else if ((p->st_other & VT_BTYPE) == VT_BYTE) {
-		    csym.n_type = T_CHAR;
-		    csym.n_sclass = C_EXT;
-		} else {
-		    csym.n_type = T_INT;	// just mark as a label
-		    csym.n_sclass = C_LABEL;
-		}
-
-
-		csym.n_value = p->st_value;
-		csym.n_scnum = 2;
-		csym.n_numaux = 1;
-		fwrite(&csym, 18, 1, f);
-
-		auxfunc.tag = 0;
-		auxfunc.size = 0x20;
-		auxfunc.fileptr = 0;
-		auxfunc.nextsym = 0;
-		auxfunc.dummy = 0;
-		fwrite(&auxfunc, 18, 1, f);
-		n++;
-		n++;
-
-	    }
-
-	    p++;
-	}
-    }
-
-    if (s1->do_debug) {
-	// write string table
-
-	// first write the size
-	i = pCoff_str_table - Coff_str_table;
-	fwrite(&i, 4, 1, f);
-
-	// then write the strings
-	fwrite(Coff_str_table, i, 1, f);
-
-	tcc_free(Coff_str_table);
-    }
-
-    return 0;
-}
-
-
-
-// group the symbols in order of filename, func1, func2, etc
-// finally global symbols
-
-void SortSymbolTable(void)
-{
-    int i, j, k, n = 0;
-    Elf32_Sym *p, *p2, *NewTable;
-    char *name, *name2;
-
-    NewTable = (Elf32_Sym *) tcc_malloc(nb_syms * sizeof(Elf32_Sym));
-
-    p = (Elf32_Sym *) symtab_section->data;
-
-
-    // find a file symbol, copy it over
-    // then scan the whole symbol list and copy any function
-    // symbols that match the file association
-
-    for (i = 0; i < nb_syms; i++) {
-	if (p->st_info == 4) {
-	    name = (char *) symtab_section->link->data + p->st_name;
-
-	    // this is a file symbol, copy it over
-
-	    NewTable[n++] = *p;
-
-	    p2 = (Elf32_Sym *) symtab_section->data;
-
-	    for (j = 0; j < nb_syms; j++) {
-		if (p2->st_info == 0x12) {
-		    // this is a func symbol
-
-		    name2 =
-			(char *) symtab_section->link->data + p2->st_name;
-
-		    // find the function data index
-
-		    for (k = 0; k < nFuncs; k++) {
-			if (strcmp(name2, Func[k]) == 0)
-			    break;
-		    }
-
-		    if (k >= nFuncs) {
-                        tcc_error("debug (sort) info can't find function: %s", name2);
-		    }
-
-		    if (strcmp(AssociatedFile[k], name) == 0) {
-			// yes they match copy it over
-
-			NewTable[n++] = *p2;
-		    }
-		}
-		p2++;
-	    }
-	}
-	p++;
-    }
-
-    // now all the filename and func symbols should have been copied over
-    // copy all the rest over (all except file and funcs)
-
-    p = (Elf32_Sym *) symtab_section->data;
-    for (i = 0; i < nb_syms; i++) {
-	if (p->st_info != 4 && p->st_info != 0x12) {
-	    NewTable[n++] = *p;
-	}
-	p++;
-    }
-
-    if (n != nb_syms)
-	tcc_error("Internal Compiler error, debug info");
-
-    // copy it all back
-
-    p = (Elf32_Sym *) symtab_section->data;
-    for (i = 0; i < nb_syms; i++) {
-	*p++ = NewTable[i];
-    }
-
-    tcc_free(NewTable);
-}
-
-
-int FindCoffSymbolIndex(const char *func_name)
-{
-    int i, n = 0;
-    Elf32_Sym *p;
-    char *name;
-
-    p = (Elf32_Sym *) symtab_section->data;
-
-    for (i = 0; i < nb_syms; i++) {
-
-	name = (char *) symtab_section->link->data + p->st_name;
-
-	if (p->st_info == 4) {
-	    // put a filename symbol
-	    n++;
-	} else if (p->st_info == 0x12) {
-
-	    if (strcmp(func_name, name) == 0)
-		return n;
-
-	    n += 6;
-
-	    // put a Function Name
-
-	    // now put aux info
-
-	    // put a .bf
-
-	    // now put aux info
-
-	    // put a .ef
-
-	    // now put aux info
-
-	} else {
-	    n += 2;
-	}
-
-	p++;
-    }
-
-    return n;			// total number of symbols
-}
-
-int OutputTheSection(Section * sect)
-{
-    const char *s = sect->name;
-
-    if (!strcmp(s, ".text"))
-	return 1;
-    else if (!strcmp(s, ".data"))
-	return 1;
-    else
-	return 0;
-}
-
-short int GetCoffFlags(const char *s)
-{
-    if (!strcmp(s, ".text"))
-	return STYP_TEXT | STYP_DATA | STYP_ALIGN | 0x400;
-    else if (!strcmp(s, ".data"))
-	return STYP_DATA;
-    else if (!strcmp(s, ".bss"))
-	return STYP_BSS;
-    else if (!strcmp(s, ".stack"))
-	return STYP_BSS | STYP_ALIGN | 0x200;
-    else if (!strcmp(s, ".cinit"))
-	return STYP_COPY | STYP_DATA | STYP_ALIGN | 0x200;
-    else
-	return 0;
-}
-
-Section *FindSection(TCCState * s1, const char *sname)
-{
-    Section *s;
-    int i;
-
-    for (i = 1; i < s1->nb_sections; i++) {
-	s = s1->sections[i];
-
-	if (!strcmp(sname, s->name))
-	    return s;
-    }
-
-    tcc_error("could not find section %s", sname);
-    return 0;
-}
-
-ST_FUNC int tcc_load_coff(TCCState * s1, int fd)
-{
-// tktk TokenSym *ts;
-
-    FILE *f;
-    unsigned int str_size;
-    char *Coff_str_table, *name;
-    int i, k;
-    struct syment csym;
-    char name2[9];
-    FILHDR file_hdr;		/* FILE HEADER STRUCTURE              */
-
-    f = fdopen(fd, "rb");
-    if (!f) {
-	tcc_error("Unable to open .out file for input");
-    }
-
-    if (fread(&file_hdr, FILHSZ, 1, f) != 1)
-	tcc_error("error reading .out file for input");
-
-    if (fread(&o_filehdr, sizeof(o_filehdr), 1, f) != 1)
-	tcc_error("error reading .out file for input");
-
-    // first read the string table
-
-    if (fseek(f, file_hdr.f_symptr + file_hdr.f_nsyms * SYMESZ, SEEK_SET))
-	tcc_error("error reading .out file for input");
-
-    if (fread(&str_size, sizeof(int), 1, f) != 1)
-	tcc_error("error reading .out file for input");
-
-
-    Coff_str_table = (char *) tcc_malloc(str_size);
-
-    if (fread(Coff_str_table, str_size - 4, 1, f) != 1)
-	tcc_error("error reading .out file for input");
-
-    // read/process all the symbols
-
-    // seek back to symbols
-
-    if (fseek(f, file_hdr.f_symptr, SEEK_SET))
-	tcc_error("error reading .out file for input");
-
-    for (i = 0; i < file_hdr.f_nsyms; i++) {
-	if (fread(&csym, SYMESZ, 1, f) != 1)
-	    tcc_error("error reading .out file for input");
-
-	if (csym._n._n_n._n_zeroes == 0) {
-	    name = Coff_str_table + csym._n._n_n._n_offset - 4;
-	} else {
-	    name = csym._n._n_name;
-
-	    if (name[7] != 0) {
-		for (k = 0; k < 8; k++)
-		    name2[k] = name[k];
-
-		name2[8] = 0;
-
-		name = name2;
-	    }
-	}
-//              if (strcmp("_DAC_Buffer",name)==0)  // tktk
-//                      name[0]=0;
-
-	if (((csym.n_type & 0x30) == 0x20 && csym.n_sclass == 0x2) || ((csym.n_type & 0x30) == 0x30 && csym.n_sclass == 0x2) || (csym.n_type == 0x4 && csym.n_sclass == 0x2) || (csym.n_type == 0x8 && csym.n_sclass == 0x2) ||	// structures
-	    (csym.n_type == 0x18 && csym.n_sclass == 0x2) ||	// pointer to structure
-	    (csym.n_type == 0x7 && csym.n_sclass == 0x2) ||	// doubles
-	    (csym.n_type == 0x6 && csym.n_sclass == 0x2))	// floats
-	{
-	    // strip off any leading underscore (except for other main routine)
-
-	    if (name[0] == '_' && strcmp(name, "_main") != 0)
-		name++;
-
-	    tcc_add_symbol(s1, name, (void*)(uintptr_t)csym.n_value);
-	}
-	// skip any aux records
-
-	if (csym.n_numaux == 1) {
-	    if (fread(&csym, SYMESZ, 1, f) != 1)
-		tcc_error("error reading .out file for input");
-	    i++;
-	}
-    }
-
-    return 0;
-}
diff --git a/tinyc/tccelf.c b/tinyc/tccelf.c
deleted file mode 100644
index 22ce5df9c..000000000
--- a/tinyc/tccelf.c
+++ /dev/null
@@ -1,2994 +0,0 @@
-/*
- *  ELF file handling for TCC
- *
- *  Copyright (c) 2001-2004 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "tcc.h"
-
-/* Define this to get some debug output during relocation processing.  */
-#undef DEBUG_RELOC
-
-/********************************************************/
-/* global variables */
-
-ST_DATA Section *text_section, *data_section, *bss_section; /* predefined sections */
-ST_DATA Section *common_section;
-ST_DATA Section *cur_text_section; /* current section where function code is generated */
-#ifdef CONFIG_TCC_ASM
-ST_DATA Section *last_text_section; /* to handle .previous asm directive */
-#endif
-#ifdef CONFIG_TCC_BCHECK
-/* bound check related sections */
-ST_DATA Section *bounds_section; /* contains global data bound description */
-ST_DATA Section *lbounds_section; /* contains local data bound description */
-#endif
-/* symbol sections */
-ST_DATA Section *symtab_section, *strtab_section;
-/* debug sections */
-ST_DATA Section *stab_section, *stabstr_section;
-
-/* XXX: avoid static variable */
-static int new_undef_sym = 0; /* Is there a new undefined sym since last new_undef_sym() */
-
-/* ------------------------------------------------------------------------- */
-
-ST_FUNC void tccelf_new(TCCState *s)
-{
-    /* no section zero */
-    dynarray_add(&s->sections, &s->nb_sections, NULL);
-
-    /* create standard sections */
-    text_section = new_section(s, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR);
-    data_section = new_section(s, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
-    bss_section = new_section(s, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE);
-    common_section = new_section(s, ".common", SHT_NOBITS, SHF_PRIVATE);
-    common_section->sh_num = SHN_COMMON;
-
-    /* symbols are always generated for linking stage */
-    symtab_section = new_symtab(s, ".symtab", SHT_SYMTAB, 0,
-                                ".strtab",
-                                ".hashtab", SHF_PRIVATE);
-    strtab_section = symtab_section->link;
-    s->symtab = symtab_section;
-
-    /* private symbol table for dynamic symbols */
-    s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE,
-                                      ".dynstrtab",
-                                      ".dynhashtab", SHF_PRIVATE);
-    get_sym_attr(s, 0, 1);
-}
-
-#ifdef CONFIG_TCC_BCHECK
-ST_FUNC void tccelf_bounds_new(TCCState *s)
-{
-    /* create bounds sections */
-    bounds_section = new_section(s, ".bounds",
-                                 SHT_PROGBITS, SHF_ALLOC);
-    lbounds_section = new_section(s, ".lbounds",
-                                  SHT_PROGBITS, SHF_ALLOC);
-}
-#endif
-
-ST_FUNC void tccelf_stab_new(TCCState *s)
-{
-    stab_section = new_section(s, ".stab", SHT_PROGBITS, 0);
-    stab_section->sh_entsize = sizeof(Stab_Sym);
-    stabstr_section = new_section(s, ".stabstr", SHT_STRTAB, 0);
-    put_elf_str(stabstr_section, "");
-    stab_section->link = stabstr_section;
-    /* put first entry */
-    put_stabs("", 0, 0, 0, 0);
-}
-
-static void free_section(Section *s)
-{
-    tcc_free(s->data);
-}
-
-ST_FUNC void tccelf_delete(TCCState *s1)
-{
-    int i;
-
-    /* free all sections */
-    for(i = 1; i < s1->nb_sections; i++)
-        free_section(s1->sections[i]);
-    dynarray_reset(&s1->sections, &s1->nb_sections);
-
-    for(i = 0; i < s1->nb_priv_sections; i++)
-        free_section(s1->priv_sections[i]);
-    dynarray_reset(&s1->priv_sections, &s1->nb_priv_sections);
-
-    /* free any loaded DLLs */
-#ifdef TCC_IS_NATIVE
-    for ( i = 0; i < s1->nb_loaded_dlls; i++) {
-        DLLReference *ref = s1->loaded_dlls[i];
-        if ( ref->handle )
-# ifdef _WIN32
-            FreeLibrary((HMODULE)ref->handle);
-# else
-            dlclose(ref->handle);
-# endif
-    }
-#endif
-    /* free loaded dlls array */
-    dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls);
-    tcc_free(s1->sym_attrs);
-}
-
-ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags)
-{
-    Section *sec;
-
-    sec = tcc_mallocz(sizeof(Section) + strlen(name));
-    strcpy(sec->name, name);
-    sec->sh_type = sh_type;
-    sec->sh_flags = sh_flags;
-    switch(sh_type) {
-    case SHT_HASH:
-    case SHT_REL:
-    case SHT_RELA:
-    case SHT_DYNSYM:
-    case SHT_SYMTAB:
-    case SHT_DYNAMIC:
-        sec->sh_addralign = 4;
-        break;
-    case SHT_STRTAB:
-        sec->sh_addralign = 1;
-        break;
-    default:
-        sec->sh_addralign =  PTR_SIZE; /* gcc/pcc default alignment */
-        break;
-    }
-
-    if (sh_flags & SHF_PRIVATE) {
-        dynarray_add(&s1->priv_sections, &s1->nb_priv_sections, sec);
-    } else {
-        sec->sh_num = s1->nb_sections;
-        dynarray_add(&s1->sections, &s1->nb_sections, sec);
-    }
-
-    return sec;
-}
-
-ST_FUNC Section *new_symtab(TCCState *s1,
-                           const char *symtab_name, int sh_type, int sh_flags,
-                           const char *strtab_name,
-                           const char *hash_name, int hash_sh_flags)
-{
-    Section *symtab, *strtab, *hash;
-    int *ptr, nb_buckets;
-
-    symtab = new_section(s1, symtab_name, sh_type, sh_flags);
-    symtab->sh_entsize = sizeof(ElfW(Sym));
-    strtab = new_section(s1, strtab_name, SHT_STRTAB, sh_flags);
-    put_elf_str(strtab, "");
-    symtab->link = strtab;
-    put_elf_sym(symtab, 0, 0, 0, 0, 0, NULL);
-
-    nb_buckets = 1;
-
-    hash = new_section(s1, hash_name, SHT_HASH, hash_sh_flags);
-    hash->sh_entsize = sizeof(int);
-    symtab->hash = hash;
-    hash->link = symtab;
-
-    ptr = section_ptr_add(hash, (2 + nb_buckets + 1) * sizeof(int));
-    ptr[0] = nb_buckets;
-    ptr[1] = 1;
-    memset(ptr + 2, 0, (nb_buckets + 1) * sizeof(int));
-    return symtab;
-}
-
-/* realloc section and set its content to zero */
-ST_FUNC void section_realloc(Section *sec, unsigned long new_size)
-{
-    unsigned long size;
-    unsigned char *data;
-
-    size = sec->data_allocated;
-    if (size == 0)
-        size = 1;
-    while (size < new_size)
-        size = size * 2;
-    data = tcc_realloc(sec->data, size);
-    memset(data + sec->data_allocated, 0, size - sec->data_allocated);
-    sec->data = data;
-    sec->data_allocated = size;
-}
-
-/* reserve at least 'size' bytes aligned per 'align' in section
-   'sec' from current offset, and return the aligned offset */
-ST_FUNC size_t section_add(Section *sec, addr_t size, int align)
-{
-    size_t offset, offset1;
-
-    offset = (sec->data_offset + align - 1) & -align;
-    offset1 = offset + size;
-    if (sec->sh_type != SHT_NOBITS && offset1 > sec->data_allocated)
-        section_realloc(sec, offset1);
-    sec->data_offset = offset1;
-    if (align > sec->sh_addralign)
-        sec->sh_addralign = align;
-    return offset;
-}
-
-/* reserve at least 'size' bytes in section 'sec' from
-   sec->data_offset. */
-ST_FUNC void *section_ptr_add(Section *sec, addr_t size)
-{
-    size_t offset = section_add(sec, size, 1);
-    return sec->data + offset;
-}
-
-/* reserve at least 'size' bytes from section start */
-ST_FUNC void section_reserve(Section *sec, unsigned long size)
-{
-    if (size > sec->data_allocated)
-        section_realloc(sec, size);
-    if (size > sec->data_offset)
-        sec->data_offset = size;
-}
-
-/* return a reference to a section, and create it if it does not
-   exists */
-ST_FUNC Section *find_section(TCCState *s1, const char *name)
-{
-    Section *sec;
-    int i;
-    for(i = 1; i < s1->nb_sections; i++) {
-        sec = s1->sections[i];
-        if (!strcmp(name, sec->name))
-            return sec;
-    }
-    /* sections are created as PROGBITS */
-    return new_section(s1, name, SHT_PROGBITS, SHF_ALLOC);
-}
-
-/* ------------------------------------------------------------------------- */
-
-ST_FUNC int put_elf_str(Section *s, const char *sym)
-{
-    int offset, len;
-    char *ptr;
-
-    len = strlen(sym) + 1;
-    offset = s->data_offset;
-    ptr = section_ptr_add(s, len);
-    memcpy(ptr, sym, len);
-    return offset;
-}
-
-/* elf symbol hashing function */
-static unsigned long elf_hash(const unsigned char *name)
-{
-    unsigned long h = 0, g;
-
-    while (*name) {
-        h = (h << 4) + *name++;
-        g = h & 0xf0000000;
-        if (g)
-            h ^= g >> 24;
-        h &= ~g;
-    }
-    return h;
-}
-
-/* rebuild hash table of section s */
-/* NOTE: we do factorize the hash table code to go faster */
-static void rebuild_hash(Section *s, unsigned int nb_buckets)
-{
-    ElfW(Sym) *sym;
-    int *ptr, *hash, nb_syms, sym_index, h;
-    unsigned char *strtab;
-
-    strtab = s->link->data;
-    nb_syms = s->data_offset / sizeof(ElfW(Sym));
-
-    s->hash->data_offset = 0;
-    ptr = section_ptr_add(s->hash, (2 + nb_buckets + nb_syms) * sizeof(int));
-    ptr[0] = nb_buckets;
-    ptr[1] = nb_syms;
-    ptr += 2;
-    hash = ptr;
-    memset(hash, 0, (nb_buckets + 1) * sizeof(int));
-    ptr += nb_buckets + 1;
-
-    sym = (ElfW(Sym) *)s->data + 1;
-    for(sym_index = 1; sym_index < nb_syms; sym_index++) {
-        if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
-            h = elf_hash(strtab + sym->st_name) % nb_buckets;
-            *ptr = hash[h];
-            hash[h] = sym_index;
-        } else {
-            *ptr = 0;
-        }
-        ptr++;
-        sym++;
-    }
-}
-
-/* return the symbol number */
-ST_FUNC int put_elf_sym(Section *s, addr_t value, unsigned long size,
-    int info, int other, int shndx, const char *name)
-{
-    int name_offset, sym_index;
-    int nbuckets, h;
-    ElfW(Sym) *sym;
-    Section *hs;
-
-    sym = section_ptr_add(s, sizeof(ElfW(Sym)));
-    if (name)
-        name_offset = put_elf_str(s->link, name);
-    else
-        name_offset = 0;
-    /* XXX: endianness */
-    sym->st_name = name_offset;
-    sym->st_value = value;
-    sym->st_size = size;
-    sym->st_info = info;
-    sym->st_other = other;
-    sym->st_shndx = shndx;
-    sym_index = sym - (ElfW(Sym) *)s->data;
-    hs = s->hash;
-    if (hs) {
-        int *ptr, *base;
-        ptr = section_ptr_add(hs, sizeof(int));
-        base = (int *)hs->data;
-        /* only add global or weak symbols */
-        if (ELFW(ST_BIND)(info) != STB_LOCAL) {
-            /* add another hashing entry */
-            nbuckets = base[0];
-            h = elf_hash((unsigned char *) name) % nbuckets;
-            *ptr = base[2 + h];
-            base[2 + h] = sym_index;
-            base[1]++;
-            /* we resize the hash table */
-            hs->nb_hashed_syms++;
-            if (hs->nb_hashed_syms > 2 * nbuckets) {
-                rebuild_hash(s, 2 * nbuckets);
-            }
-        } else {
-            *ptr = 0;
-            base[1]++;
-        }
-    }
-    return sym_index;
-}
-
-/* find global ELF symbol 'name' and return its index. Return 0 if not
-   found. */
-ST_FUNC int find_elf_sym(Section *s, const char *name)
-{
-    ElfW(Sym) *sym;
-    Section *hs;
-    int nbuckets, sym_index, h;
-    const char *name1;
-
-    hs = s->hash;
-    if (!hs)
-        return 0;
-    nbuckets = ((int *)hs->data)[0];
-    h = elf_hash((unsigned char *) name) % nbuckets;
-    sym_index = ((int *)hs->data)[2 + h];
-    while (sym_index != 0) {
-        sym = &((ElfW(Sym) *)s->data)[sym_index];
-        name1 = (char *) s->link->data + sym->st_name;
-        if (!strcmp(name, name1))
-            return sym_index;
-        sym_index = ((int *)hs->data)[2 + nbuckets + sym_index];
-    }
-    return 0;
-}
-
-/* return elf symbol value, signal error if 'err' is nonzero */
-ST_FUNC addr_t get_elf_sym_addr(TCCState *s, const char *name, int err)
-{
-    int sym_index;
-    ElfW(Sym) *sym;
-
-    sym_index = find_elf_sym(s->symtab, name);
-    sym = &((ElfW(Sym) *)s->symtab->data)[sym_index];
-    if (!sym_index || sym->st_shndx == SHN_UNDEF) {
-        if (err)
-            tcc_error("%s not defined", name);
-        return 0;
-    }
-    return sym->st_value;
-}
-
-/* return elf symbol value */
-LIBTCCAPI void *tcc_get_symbol(TCCState *s, const char *name)
-{
-    return (void*)(uintptr_t)get_elf_sym_addr(s, name, 0);
-}
-
-#if defined TCC_IS_NATIVE || defined TCC_TARGET_PE
-/* return elf symbol value or error */
-ST_FUNC void* tcc_get_symbol_err(TCCState *s, const char *name)
-{
-    return (void*)(uintptr_t)get_elf_sym_addr(s, name, 1);
-}
-#endif
-
-/* add an elf symbol : check if it is already defined and patch
-   it. Return symbol index. NOTE that sh_num can be SHN_UNDEF. */
-ST_FUNC int set_elf_sym(Section *s, addr_t value, unsigned long size,
-                       int info, int other, int shndx, const char *name)
-{
-    ElfW(Sym) *esym;
-    int sym_bind, sym_index, sym_type, esym_bind;
-    unsigned char sym_vis, esym_vis, new_vis;
-
-    sym_bind = ELFW(ST_BIND)(info);
-    sym_type = ELFW(ST_TYPE)(info);
-    sym_vis = ELFW(ST_VISIBILITY)(other);
-
-    sym_index = find_elf_sym(s, name);
-    esym = &((ElfW(Sym) *)s->data)[sym_index];
-    if (sym_index && esym->st_value == value && esym->st_size == size
-	&& esym->st_info == info && esym->st_other == other
-	&& esym->st_shndx == shndx)
-        return sym_index;
-
-    if (sym_bind != STB_LOCAL) {
-        /* we search global or weak symbols */
-        if (!sym_index)
-            goto do_def;
-        if (esym->st_shndx != SHN_UNDEF) {
-            esym_bind = ELFW(ST_BIND)(esym->st_info);
-            /* propagate the most constraining visibility */
-            /* STV_DEFAULT(0)<STV_PROTECTED(3)<STV_HIDDEN(2)<STV_INTERNAL(1) */
-            esym_vis = ELFW(ST_VISIBILITY)(esym->st_other);
-            if (esym_vis == STV_DEFAULT) {
-                new_vis = sym_vis;
-            } else if (sym_vis == STV_DEFAULT) {
-                new_vis = esym_vis;
-            } else {
-                new_vis = (esym_vis < sym_vis) ? esym_vis : sym_vis;
-            }
-            esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1))
-                             | new_vis;
-            other = esym->st_other; /* in case we have to patch esym */
-            if (shndx == SHN_UNDEF) {
-                /* ignore adding of undefined symbol if the
-                   corresponding symbol is already defined */
-            } else if (sym_bind == STB_GLOBAL && esym_bind == STB_WEAK) {
-                /* global overrides weak, so patch */
-                goto do_patch;
-            } else if (sym_bind == STB_WEAK && esym_bind == STB_GLOBAL) {
-                /* weak is ignored if already global */
-            } else if (sym_bind == STB_WEAK && esym_bind == STB_WEAK) {
-                /* keep first-found weak definition, ignore subsequents */
-            } else if (sym_vis == STV_HIDDEN || sym_vis == STV_INTERNAL) {
-                /* ignore hidden symbols after */
-            } else if ((esym->st_shndx == SHN_COMMON
-                            || esym->st_shndx == bss_section->sh_num)
-                        && (shndx < SHN_LORESERVE
-                            && shndx != bss_section->sh_num)) {
-                /* data symbol gets precedence over common/bss */
-                goto do_patch;
-            } else if (shndx == SHN_COMMON || shndx == bss_section->sh_num) {
-                /* data symbol keeps precedence over common/bss */
-            } else if (s == tcc_state->dynsymtab_section) {
-                /* we accept that two DLL define the same symbol */
-            } else {
-#if 0
-                printf("new_bind=%x new_shndx=%x new_vis=%x old_bind=%x old_shndx=%x old_vis=%x\n",
-                       sym_bind, shndx, new_vis, esym_bind, esym->st_shndx, esym_vis);
-#endif
-                tcc_error_noabort("'%s' defined twice", name);
-            }
-        } else {
-        do_patch:
-            esym->st_info = ELFW(ST_INFO)(sym_bind, sym_type);
-            esym->st_shndx = shndx;
-            new_undef_sym = 1;
-            esym->st_value = value;
-            esym->st_size = size;
-            esym->st_other = other;
-        }
-    } else {
-    do_def:
-        sym_index = put_elf_sym(s, value, size,
-                                ELFW(ST_INFO)(sym_bind, sym_type), other,
-                                shndx, name);
-    }
-    return sym_index;
-}
-
-/* put relocation */
-ST_FUNC void put_elf_reloca(Section *symtab, Section *s, unsigned long offset,
-                            int type, int symbol, addr_t addend)
-{
-    char buf[256];
-    Section *sr;
-    ElfW_Rel *rel;
-
-    sr = s->reloc;
-    if (!sr) {
-        /* if no relocation section, create it */
-        snprintf(buf, sizeof(buf), REL_SECTION_FMT, s->name);
-        /* if the symtab is allocated, then we consider the relocation
-           are also */
-        sr = new_section(tcc_state, buf, SHT_RELX, symtab->sh_flags);
-        sr->sh_entsize = sizeof(ElfW_Rel);
-        sr->link = symtab;
-        sr->sh_info = s->sh_num;
-        s->reloc = sr;
-    }
-    rel = section_ptr_add(sr, sizeof(ElfW_Rel));
-    rel->r_offset = offset;
-    rel->r_info = ELFW(R_INFO)(symbol, type);
-#if SHT_RELX == SHT_RELA
-    rel->r_addend = addend;
-#else
-    if (addend)
-        tcc_error("non-zero addend on REL architecture");
-#endif
-}
-
-ST_FUNC void put_elf_reloc(Section *symtab, Section *s, unsigned long offset,
-                           int type, int symbol)
-{
-    put_elf_reloca(symtab, s, offset, type, symbol, 0);
-}
-
-/* Remove relocations for section S->reloc starting at oldrelocoffset
-   that are to the same place, retaining the last of them.  As side effect
-   the relocations are sorted.  Possibly reduces the number of relocs.  */
-ST_FUNC void squeeze_multi_relocs(Section *s, size_t oldrelocoffset)
-{
-    Section *sr = s->reloc;
-    ElfW_Rel *r, *dest;
-    ssize_t a;
-    ElfW(Addr) addr;
-
-    if (oldrelocoffset + sizeof(*r) >= sr->data_offset)
-      return;
-    /* The relocs we're dealing with are the result of initializer parsing.
-       So they will be mostly in order and there aren't many of them.
-       Secondly we need a stable sort (which qsort isn't).  We use
-       a simple insertion sort.  */
-    for (a = oldrelocoffset + sizeof(*r); a < sr->data_offset; a += sizeof(*r)) {
-	ssize_t i = a - sizeof(*r);
-	addr = ((ElfW_Rel*)(sr->data + a))->r_offset;
-	for (; i >= (ssize_t)oldrelocoffset &&
-	       ((ElfW_Rel*)(sr->data + i))->r_offset > addr; i -= sizeof(*r)) {
-	    ElfW_Rel tmp = *(ElfW_Rel*)(sr->data + a);
-	    *(ElfW_Rel*)(sr->data + a) = *(ElfW_Rel*)(sr->data + i);
-	    *(ElfW_Rel*)(sr->data + i) = tmp;
-	}
-    }
-
-    r = (ElfW_Rel*)(sr->data + oldrelocoffset);
-    dest = r;
-    for (; r < (ElfW_Rel*)(sr->data + sr->data_offset); r++) {
-	if (dest->r_offset != r->r_offset)
-	  dest++;
-	*dest = *r;
-    }
-    sr->data_offset = (unsigned char*)dest - sr->data + sizeof(*r);
-}
-
-/* put stab debug information */
-
-ST_FUNC void put_stabs(const char *str, int type, int other, int desc,
-                      unsigned long value)
-{
-    Stab_Sym *sym;
-
-    sym = section_ptr_add(stab_section, sizeof(Stab_Sym));
-    if (str) {
-        sym->n_strx = put_elf_str(stabstr_section, str);
-    } else {
-        sym->n_strx = 0;
-    }
-    sym->n_type = type;
-    sym->n_other = other;
-    sym->n_desc = desc;
-    sym->n_value = value;
-}
-
-ST_FUNC void put_stabs_r(const char *str, int type, int other, int desc,
-                        unsigned long value, Section *sec, int sym_index)
-{
-    put_stabs(str, type, other, desc, value);
-    put_elf_reloc(symtab_section, stab_section,
-                  stab_section->data_offset - sizeof(unsigned int),
-                  R_DATA_32, sym_index);
-}
-
-ST_FUNC void put_stabn(int type, int other, int desc, int value)
-{
-    put_stabs(NULL, type, other, desc, value);
-}
-
-ST_FUNC void put_stabd(int type, int other, int desc)
-{
-    put_stabs(NULL, type, other, desc, 0);
-}
-
-ST_FUNC struct sym_attr *get_sym_attr(TCCState *s1, int index, int alloc)
-{
-    int n;
-    struct sym_attr *tab;
-
-    if (index >= s1->nb_sym_attrs) {
-        if (!alloc)
-            return s1->sym_attrs;
-        /* find immediately bigger power of 2 and reallocate array */
-        n = 1;
-        while (index >= n)
-            n *= 2;
-        tab = tcc_realloc(s1->sym_attrs, n * sizeof(*s1->sym_attrs));
-        s1->sym_attrs = tab;
-        memset(s1->sym_attrs + s1->nb_sym_attrs, 0,
-               (n - s1->nb_sym_attrs) * sizeof(*s1->sym_attrs));
-        s1->nb_sym_attrs = n;
-    }
-    return &s1->sym_attrs[index];
-}
-
-/* Browse each elem of type <type> in section <sec> starting at elem <startoff>
-   using variable <elem> */
-#define for_each_elem(sec, startoff, elem, type) \
-    for (elem = (type *) sec->data + startoff; \
-         elem < (type *) (sec->data + sec->data_offset); elem++)
-
-/* In an ELF file symbol table, the local symbols must appear below
-   the global and weak ones. Since TCC cannot sort it while generating
-   the code, we must do it after. All the relocation tables are also
-   modified to take into account the symbol table sorting */
-static void sort_syms(TCCState *s1, Section *s)
-{
-    int *old_to_new_syms;
-    ElfW(Sym) *new_syms;
-    int nb_syms, i;
-    ElfW(Sym) *p, *q;
-    ElfW_Rel *rel;
-    Section *sr;
-    int type, sym_index;
-
-    nb_syms = s->data_offset / sizeof(ElfW(Sym));
-    new_syms = tcc_malloc(nb_syms * sizeof(ElfW(Sym)));
-    old_to_new_syms = tcc_malloc(nb_syms * sizeof(int));
-
-    /* first pass for local symbols */
-    p = (ElfW(Sym) *)s->data;
-    q = new_syms;
-    for(i = 0; i < nb_syms; i++) {
-        if (ELFW(ST_BIND)(p->st_info) == STB_LOCAL) {
-            old_to_new_syms[i] = q - new_syms;
-            *q++ = *p;
-        }
-        p++;
-    }
-    /* save the number of local symbols in section header */
-    if( s->sh_size )    /* this 'if' makes IDA happy */
-        s->sh_info = q - new_syms;
-
-    /* then second pass for non local symbols */
-    p = (ElfW(Sym) *)s->data;
-    for(i = 0; i < nb_syms; i++) {
-        if (ELFW(ST_BIND)(p->st_info) != STB_LOCAL) {
-            old_to_new_syms[i] = q - new_syms;
-            *q++ = *p;
-        }
-        p++;
-    }
-
-    /* we copy the new symbols to the old */
-    memcpy(s->data, new_syms, nb_syms * sizeof(ElfW(Sym)));
-    tcc_free(new_syms);
-
-    /* now we modify all the relocations */
-    for(i = 1; i < s1->nb_sections; i++) {
-        sr = s1->sections[i];
-        if (sr->sh_type == SHT_RELX && sr->link == s) {
-            for_each_elem(sr, 0, rel, ElfW_Rel) {
-                sym_index = ELFW(R_SYM)(rel->r_info);
-                type = ELFW(R_TYPE)(rel->r_info);
-                sym_index = old_to_new_syms[sym_index];
-                rel->r_info = ELFW(R_INFO)(sym_index, type);
-            }
-        }
-    }
-
-    tcc_free(old_to_new_syms);
-}
-
-/* relocate common symbols in the .bss section */
-ST_FUNC void relocate_common_syms(void)
-{
-    ElfW(Sym) *sym;
-
-    for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
-        if (sym->st_shndx == SHN_COMMON) {
-            /* symbol alignment is in st_value for SHN_COMMONs */
-	    sym->st_value = section_add(bss_section, sym->st_size,
-					sym->st_value);
-            sym->st_shndx = bss_section->sh_num;
-        }
-    }
-}
-
-/* relocate symbol table, resolve undefined symbols if do_resolve is
-   true and output error if undefined symbol. */
-ST_FUNC void relocate_syms(TCCState *s1, Section *symtab, int do_resolve)
-{
-    ElfW(Sym) *sym;
-    int sym_bind, sh_num;
-    const char *name;
-
-    for_each_elem(symtab, 1, sym, ElfW(Sym)) {
-        sh_num = sym->st_shndx;
-        if (sh_num == SHN_UNDEF) {
-            name = (char *) strtab_section->data + sym->st_name;
-            /* Use ld.so to resolve symbol for us (for tcc -run) */
-            if (do_resolve) {
-#if defined TCC_IS_NATIVE && !defined TCC_TARGET_PE
-                void *addr = dlsym(RTLD_DEFAULT, name);
-                if (addr) {
-                    sym->st_value = (addr_t) addr;
-#ifdef DEBUG_RELOC
-		    printf ("relocate_sym: %s -> 0x%lx\n", name, sym->st_value);
-#endif
-                    goto found;
-                }
-#endif
-            /* if dynamic symbol exist, it will be used in relocate_section */
-            } else if (s1->dynsym && find_elf_sym(s1->dynsym, name))
-                goto found;
-            /* XXX: _fp_hw seems to be part of the ABI, so we ignore
-               it */
-            if (!strcmp(name, "_fp_hw"))
-                goto found;
-            /* only weak symbols are accepted to be undefined. Their
-               value is zero */
-            sym_bind = ELFW(ST_BIND)(sym->st_info);
-            if (sym_bind == STB_WEAK)
-                sym->st_value = 0;
-            else
-                tcc_error_noabort("undefined symbol '%s'", name);
-        } else if (sh_num < SHN_LORESERVE) {
-            /* add section base */
-            sym->st_value += s1->sections[sym->st_shndx]->sh_addr;
-        }
-    found: ;
-    }
-}
-
-/* relocate a given section (CPU dependent) by applying the relocations
-   in the associated relocation section */
-ST_FUNC void relocate_section(TCCState *s1, Section *s)
-{
-    Section *sr = s->reloc;
-    ElfW_Rel *rel;
-    ElfW(Sym) *sym;
-    int type, sym_index;
-    unsigned char *ptr;
-    addr_t tgt, addr;
-
-    relocate_init(sr);
-
-    for_each_elem(sr, 0, rel, ElfW_Rel) {
-        ptr = s->data + rel->r_offset;
-        sym_index = ELFW(R_SYM)(rel->r_info);
-        sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
-        type = ELFW(R_TYPE)(rel->r_info);
-        tgt = sym->st_value;
-#if SHT_RELX == SHT_RELA
-        tgt += rel->r_addend;
-#endif
-        addr = s->sh_addr + rel->r_offset;
-        relocate(s1, rel, type, ptr, addr, tgt);
-    }
-    /* if the relocation is allocated, we change its symbol table */
-    if (sr->sh_flags & SHF_ALLOC)
-        sr->link = s1->dynsym;
-}
-
-/* relocate relocation table in 'sr' */
-static void relocate_rel(TCCState *s1, Section *sr)
-{
-    Section *s;
-    ElfW_Rel *rel;
-
-    s = s1->sections[sr->sh_info];
-    for_each_elem(sr, 0, rel, ElfW_Rel)
-        rel->r_offset += s->sh_addr;
-}
-
-/* count the number of dynamic relocations so that we can reserve
-   their space */
-static int prepare_dynamic_rel(TCCState *s1, Section *sr)
-{
-    ElfW_Rel *rel;
-    int sym_index, type, count;
-
-    count = 0;
-    for_each_elem(sr, 0, rel, ElfW_Rel) {
-        sym_index = ELFW(R_SYM)(rel->r_info);
-        type = ELFW(R_TYPE)(rel->r_info);
-        switch(type) {
-#if defined(TCC_TARGET_I386)
-        case R_386_32:
-            if (!get_sym_attr(s1, sym_index, 0)->dyn_index
-                && ((ElfW(Sym)*)symtab_section->data + sym_index)->st_shndx == SHN_UNDEF) {
-                /* don't fixup unresolved (weak) symbols */
-                rel->r_info = ELFW(R_INFO)(sym_index, R_386_RELATIVE);
-                break;
-            }
-#elif defined(TCC_TARGET_X86_64)
-        case R_X86_64_32:
-        case R_X86_64_32S:
-        case R_X86_64_64:
-#endif
-            count++;
-            break;
-#if defined(TCC_TARGET_I386)
-        case R_386_PC32:
-#elif defined(TCC_TARGET_X86_64)
-        case R_X86_64_PC32:
-#endif
-            if (get_sym_attr(s1, sym_index, 0)->dyn_index)
-                count++;
-            break;
-        default:
-            break;
-        }
-    }
-    if (count) {
-        /* allocate the section */
-        sr->sh_flags |= SHF_ALLOC;
-        sr->sh_size = count * sizeof(ElfW_Rel);
-    }
-    return count;
-}
-
-static void build_got(TCCState *s1)
-{
-    /* if no got, then create it */
-    s1->got = new_section(s1, ".got", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
-    s1->got->sh_entsize = 4;
-    set_elf_sym(symtab_section, 0, 4, ELFW(ST_INFO)(STB_GLOBAL, STT_OBJECT),
-                0, s1->got->sh_num, "_GLOBAL_OFFSET_TABLE_");
-    /* keep space for _DYNAMIC pointer and two dummy got entries */
-    section_ptr_add(s1->got, 3 * PTR_SIZE);
-}
-
-/* Create a GOT and (for function call) a PLT entry corresponding to a symbol
-   in s1->symtab. When creating the dynamic symbol table entry for the GOT
-   relocation, use 'size' and 'info' for the corresponding symbol metadata.
-   Returns the offset of the GOT or (if any) PLT entry. */
-static struct sym_attr * put_got_entry(TCCState *s1, int dyn_reloc_type,
-                                       unsigned long size,
-                                       int info, int sym_index)
-{
-    int need_plt_entry;
-    const char *name;
-    ElfW(Sym) *sym;
-    struct sym_attr *attr;
-    unsigned got_offset;
-    char plt_name[100];
-    int len;
-
-    need_plt_entry = (dyn_reloc_type == R_JMP_SLOT);
-    attr = get_sym_attr(s1, sym_index, 1);
-
-    /* In case a function is both called and its address taken 2 GOT entries
-       are created, one for taking the address (GOT) and the other for the PLT
-       entry (PLTGOT).  */
-    if (need_plt_entry ? attr->plt_offset : attr->got_offset)
-        return attr;
-
-    /* create the GOT entry */
-    got_offset = s1->got->data_offset;
-    section_ptr_add(s1->got, PTR_SIZE);
-
-    /* Create the GOT relocation that will insert the address of the object or
-       function of interest in the GOT entry. This is a static relocation for
-       memory output (dlsym will give us the address of symbols) and dynamic
-       relocation otherwise (executable and DLLs). The relocation should be
-       done lazily for GOT entry with *_JUMP_SLOT relocation type (the one
-       associated to a PLT entry) but is currently done at load time for an
-       unknown reason. */
-
-    sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
-    name = (char *) symtab_section->link->data + sym->st_name;
-
-    if (s1->dynsym) {
-	if (ELFW(ST_BIND)(sym->st_info) == STB_LOCAL) {
-	    /* Hack alarm.  We don't want to emit dynamic symbols
-	       and symbol based relocs for STB_LOCAL symbols, but rather
-	       want to resolve them directly.  At this point the symbol
-	       values aren't final yet, so we must defer this.  We will later
-	       have to create a RELATIVE reloc anyway, so we misuse the
-	       relocation slot to smuggle the symbol reference until
-	       fill_local_got_entries.  Not that the sym_index is
-	       relative to symtab_section, not s1->dynsym!  Nevertheless
-	       we use s1->dyn_sym so that if this is the first call
-	       that got->reloc is correctly created.  Also note that
-	       RELATIVE relocs are not normally created for the .got,
-	       so the types serves as a marker for later (and is retained
-	       also for the final output, which is okay because then the
-	       got is just normal data).  */
-	    put_elf_reloc(s1->dynsym, s1->got, got_offset, R_RELATIVE,
-			  sym_index);
-	} else {
-	    if (0 == attr->dyn_index)
-                attr->dyn_index = set_elf_sym(s1->dynsym, sym->st_value, size,
-					      info, 0, sym->st_shndx, name);
-	    put_elf_reloc(s1->dynsym, s1->got, got_offset, dyn_reloc_type,
-			  attr->dyn_index);
-	}
-    } else {
-        put_elf_reloc(symtab_section, s1->got, got_offset, dyn_reloc_type,
-                      sym_index);
-    }
-
-    if (need_plt_entry) {
-        if (!s1->plt) {
-    	    s1->plt = new_section(s1, ".plt", SHT_PROGBITS,
-    			          SHF_ALLOC | SHF_EXECINSTR);
-    	    s1->plt->sh_entsize = 4;
-        }
-
-        attr->plt_offset = create_plt_entry(s1, got_offset, attr);
-
-        /* create a symbol 'sym@plt' for the PLT jump vector */
-        len = strlen(name);
-        if (len > sizeof plt_name - 5)
-            len = sizeof plt_name - 5;
-        memcpy(plt_name, name, len);
-        strcpy(plt_name + len, "@plt");
-        attr->plt_sym = put_elf_sym(s1->symtab, attr->plt_offset, sym->st_size,
-            ELFW(ST_INFO)(STB_GLOBAL, STT_FUNC), 0, s1->plt->sh_num, plt_name);
-
-    } else {
-        attr->got_offset = got_offset;
-    }
-
-    return attr;
-}
-
-/* build GOT and PLT entries */
-ST_FUNC void build_got_entries(TCCState *s1)
-{
-    Section *s;
-    ElfW_Rel *rel;
-    ElfW(Sym) *sym;
-    int i, type, gotplt_entry, reloc_type, sym_index;
-    struct sym_attr *attr;
-
-    for(i = 1; i < s1->nb_sections; i++) {
-        s = s1->sections[i];
-        if (s->sh_type != SHT_RELX)
-            continue;
-        /* no need to handle got relocations */
-        if (s->link != symtab_section)
-            continue;
-        for_each_elem(s, 0, rel, ElfW_Rel) {
-            type = ELFW(R_TYPE)(rel->r_info);
-            gotplt_entry = gotplt_entry_type(type);
-            sym_index = ELFW(R_SYM)(rel->r_info);
-            sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
-
-            if (gotplt_entry == NO_GOTPLT_ENTRY) {
-                continue;
-            }
-
-            /* Automatically create PLT/GOT [entry] if it is an undefined
-	       reference (resolved at runtime), or the symbol is absolute,
-	       probably created by tcc_add_symbol, and thus on 64-bit
-	       targets might be too far from application code.  */
-            if (gotplt_entry == AUTO_GOTPLT_ENTRY) {
-                if (sym->st_shndx == SHN_UNDEF) {
-                    ElfW(Sym) *esym;
-		    int dynindex;
-                    if (s1->output_type == TCC_OUTPUT_DLL && ! PCRELATIVE_DLLPLT)
-                        continue;
-		    /* Relocations for UNDEF symbols would normally need
-		       to be transferred into the executable or shared object.
-		       If that were done AUTO_GOTPLT_ENTRY wouldn't exist.
-		       But TCC doesn't do that (at least for exes), so we
-		       need to resolve all such relocs locally.  And that
-		       means PLT slots for functions in DLLs and COPY relocs for
-		       data symbols.  COPY relocs were generated in
-		       bind_exe_dynsyms (and the symbol adjusted to be defined),
-		       and for functions we were generated a dynamic symbol
-		       of function type.  */
-		    if (s1->dynsym) {
-			/* dynsym isn't set for -run :-/  */
-			dynindex = get_sym_attr(s1, sym_index, 0)->dyn_index;
-			esym = (ElfW(Sym) *)s1->dynsym->data + dynindex;
-			if (dynindex
-			    && (ELFW(ST_TYPE)(esym->st_info) == STT_FUNC
-				|| (ELFW(ST_TYPE)(esym->st_info) == STT_NOTYPE
-				    && ELFW(ST_TYPE)(sym->st_info) == STT_FUNC)))
-			    goto jmp_slot;
-		    }
-                } else if (!(sym->st_shndx == SHN_ABS
-#ifndef TCC_TARGET_ARM
-			&& PTR_SIZE == 8
-#endif
-			))
-                    continue;
-            }
-
-#ifdef TCC_TARGET_X86_64
-            if ((type == R_X86_64_PLT32 || type == R_X86_64_PC32) &&
-                (ELFW(ST_VISIBILITY)(sym->st_other) != STV_DEFAULT ||
-		 ELFW(ST_BIND)(sym->st_info) == STB_LOCAL)) {
-                rel->r_info = ELFW(R_INFO)(sym_index, R_X86_64_PC32);
-                continue;
-            }
-#endif
-            if (code_reloc(type)) {
-            jmp_slot:
-                reloc_type = R_JMP_SLOT;
-            } else
-                reloc_type = R_GLOB_DAT;
-
-            if (!s1->got)
-                build_got(s1);
-
-            if (gotplt_entry == BUILD_GOT_ONLY)
-                continue;
-
-            attr = put_got_entry(s1, reloc_type, sym->st_size, sym->st_info,
-                                 sym_index);
-
-            if (reloc_type == R_JMP_SLOT)
-                rel->r_info = ELFW(R_INFO)(attr->plt_sym, type);
-        }
-    }
-}
-
-/* put dynamic tag */
-static void put_dt(Section *dynamic, int dt, addr_t val)
-{
-    ElfW(Dyn) *dyn;
-    dyn = section_ptr_add(dynamic, sizeof(ElfW(Dyn)));
-    dyn->d_tag = dt;
-    dyn->d_un.d_val = val;
-}
-
-#ifndef TCC_TARGET_PE
-static void add_init_array_defines(TCCState *s1, const char *section_name)
-{
-    Section *s;
-    long end_offset;
-    char sym_start[1024];
-    char sym_end[1024];
-
-    snprintf(sym_start, sizeof(sym_start), "__%s_start", section_name + 1);
-    snprintf(sym_end, sizeof(sym_end), "__%s_end", section_name + 1);
-
-    s = find_section(s1, section_name);
-    if (!s) {
-        end_offset = 0;
-        s = data_section;
-    } else {
-        end_offset = s->data_offset;
-    }
-
-    set_elf_sym(symtab_section,
-                0, 0,
-                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
-                s->sh_num, sym_start);
-    set_elf_sym(symtab_section,
-                end_offset, 0,
-                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
-                s->sh_num, sym_end);
-}
-#endif
-
-static int tcc_add_support(TCCState *s1, const char *filename)
-{
-    char buf[1024];
-    snprintf(buf, sizeof(buf), "%s/%s", s1->tcc_lib_path, filename);
-    return tcc_add_file(s1, buf);
-}
-
-ST_FUNC void tcc_add_bcheck(TCCState *s1)
-{
-#ifdef CONFIG_TCC_BCHECK
-    addr_t *ptr;
-    int sym_index;
-
-    if (0 == s1->do_bounds_check)
-        return;
-    /* XXX: add an object file to do that */
-    ptr = section_ptr_add(bounds_section, sizeof(*ptr));
-    *ptr = 0;
-    set_elf_sym(symtab_section, 0, 0,
-                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
-                bounds_section->sh_num, "__bounds_start");
-    /* pull bcheck.o from libtcc1.a */
-    sym_index = set_elf_sym(symtab_section, 0, 0,
-                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
-                SHN_UNDEF, "__bound_init");
-    if (s1->output_type != TCC_OUTPUT_MEMORY) {
-        /* add 'call __bound_init()' in .init section */
-        Section *init_section = find_section(s1, ".init");
-        unsigned char *pinit = section_ptr_add(init_section, 5);
-        pinit[0] = 0xe8;
-        write32le(pinit + 1, -4);
-        put_elf_reloc(symtab_section, init_section,
-            init_section->data_offset - 4, R_386_PC32, sym_index);
-            /* R_386_PC32 = R_X86_64_PC32 = 2 */
-    }
-#endif
-}
-
-/* add tcc runtime libraries */
-ST_FUNC void tcc_add_runtime(TCCState *s1)
-{
-    tcc_add_bcheck(s1);
-    tcc_add_pragma_libs(s1);
-    /* add libc */
-    if (!s1->nostdlib) {
-        tcc_add_library_err(s1, "c");
-#ifdef TCC_LIBGCC
-        if (!s1->static_link) {
-            if (TCC_LIBGCC[0] == '/')
-                tcc_add_file(s1, TCC_LIBGCC);
-            else
-                tcc_add_dll(s1, TCC_LIBGCC, 0);
-        }
-#endif
-        tcc_add_support(s1, TCC_LIBTCC1);
-        /* add crt end if not memory output */
-        if (s1->output_type != TCC_OUTPUT_MEMORY)
-            tcc_add_crt(s1, "crtn.o");
-    }
-}
-
-/* add various standard linker symbols (must be done after the
-   sections are filled (for example after allocating common
-   symbols)) */
-ST_FUNC void tcc_add_linker_symbols(TCCState *s1)
-{
-    char buf[1024];
-    int i;
-    Section *s;
-
-    set_elf_sym(symtab_section,
-                text_section->data_offset, 0,
-                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
-                text_section->sh_num, "_etext");
-    set_elf_sym(symtab_section,
-                data_section->data_offset, 0,
-                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
-                data_section->sh_num, "_edata");
-    set_elf_sym(symtab_section,
-                bss_section->data_offset, 0,
-                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
-                bss_section->sh_num, "_end");
-#ifndef TCC_TARGET_PE
-    /* horrible new standard ldscript defines */
-    add_init_array_defines(s1, ".preinit_array");
-    add_init_array_defines(s1, ".init_array");
-    add_init_array_defines(s1, ".fini_array");
-#endif
-
-    /* add start and stop symbols for sections whose name can be
-       expressed in C */
-    for(i = 1; i < s1->nb_sections; i++) {
-        s = s1->sections[i];
-        if (s->sh_type == SHT_PROGBITS &&
-            (s->sh_flags & SHF_ALLOC)) {
-            const char *p;
-            int ch;
-
-            /* check if section name can be expressed in C */
-            p = s->name;
-            for(;;) {
-                ch = *p;
-                if (!ch)
-                    break;
-                if (!isid(ch) && !isnum(ch))
-                    goto next_sec;
-                p++;
-            }
-            snprintf(buf, sizeof(buf), "__start_%s", s->name);
-            set_elf_sym(symtab_section,
-                        0, 0,
-                        ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
-                        s->sh_num, buf);
-            snprintf(buf, sizeof(buf), "__stop_%s", s->name);
-            set_elf_sym(symtab_section,
-                        s->data_offset, 0,
-                        ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
-                        s->sh_num, buf);
-        }
-    next_sec: ;
-    }
-}
-
-static void tcc_output_binary(TCCState *s1, FILE *f,
-                              const int *sec_order)
-{
-    Section *s;
-    int i, offset, size;
-
-    offset = 0;
-    for(i=1;i<s1->nb_sections;i++) {
-        s = s1->sections[sec_order[i]];
-        if (s->sh_type != SHT_NOBITS &&
-            (s->sh_flags & SHF_ALLOC)) {
-            while (offset < s->sh_offset) {
-                fputc(0, f);
-                offset++;
-            }
-            size = s->sh_size;
-            fwrite(s->data, 1, size, f);
-            offset += size;
-        }
-    }
-}
-
-ST_FUNC void fill_got_entry(TCCState *s1, ElfW_Rel *rel)
-{
-    int sym_index = ELFW(R_SYM) (rel->r_info);
-    ElfW(Sym) *sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
-    struct sym_attr *attr = get_sym_attr(s1, sym_index, 0);
-    unsigned offset = attr->got_offset;
-
-    if (0 == offset)
-        return;
-    section_reserve(s1->got, offset + PTR_SIZE);
-#ifdef TCC_TARGET_X86_64
-    write64le(s1->got->data + offset, sym->st_value);
-#else
-    write32le(s1->got->data + offset, sym->st_value);
-#endif
-}
-
-/* Perform relocation to GOT or PLT entries */
-ST_FUNC void fill_got(TCCState *s1)
-{
-    Section *s;
-    ElfW_Rel *rel;
-    int i;
-
-    for(i = 1; i < s1->nb_sections; i++) {
-        s = s1->sections[i];
-        if (s->sh_type != SHT_RELX)
-            continue;
-        /* no need to handle got relocations */
-        if (s->link != symtab_section)
-            continue;
-        for_each_elem(s, 0, rel, ElfW_Rel) {
-            switch (ELFW(R_TYPE) (rel->r_info)) {
-                case R_X86_64_GOT32:
-                case R_X86_64_GOTPCREL:
-		case R_X86_64_GOTPCRELX:
-		case R_X86_64_REX_GOTPCRELX:
-                case R_X86_64_PLT32:
-                    fill_got_entry(s1, rel);
-                    break;
-            }
-        }
-    }
-}
-
-/* See put_got_entry for a description.  This is the second stage
-   where GOT references to local defined symbols are rewritten.  */
-static void fill_local_got_entries(TCCState *s1)
-{
-    ElfW_Rel *rel;
-    for_each_elem(s1->got->reloc, 0, rel, ElfW_Rel) {
-	if (ELFW(R_TYPE)(rel->r_info) == R_RELATIVE) {
-	    int sym_index = ELFW(R_SYM) (rel->r_info);
-	    ElfW(Sym) *sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
-	    struct sym_attr *attr = get_sym_attr(s1, sym_index, 0);
-	    unsigned offset = attr->got_offset;
-	    if (offset != rel->r_offset - s1->got->sh_addr)
-	      tcc_error_noabort("huh");
-	    rel->r_info = ELFW(R_INFO)(0, R_RELATIVE);
-#if SHT_RELX == SHT_RELA
-	    rel->r_addend = sym->st_value;
-#else
-	    /* All our REL architectures also happen to be 32bit LE.  */
-	    write32le(s1->got->data + offset, sym->st_value);
-#endif
-	}
-    }
-}
-
-/* Bind symbols of executable: resolve undefined symbols from exported symbols
-   in shared libraries and export non local defined symbols to shared libraries
-   if -rdynamic switch was given on command line */
-static void bind_exe_dynsyms(TCCState *s1)
-{
-    const char *name;
-    int sym_index, index;
-    ElfW(Sym) *sym, *esym;
-    int type;
-
-    /* Resolve undefined symbols from dynamic symbols. When there is a match:
-       - if STT_FUNC or STT_GNU_IFUNC symbol -> add it in PLT
-       - if STT_OBJECT symbol -> add it in .bss section with suitable reloc */
-    for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
-        if (sym->st_shndx == SHN_UNDEF) {
-            name = (char *) symtab_section->link->data + sym->st_name;
-            sym_index = find_elf_sym(s1->dynsymtab_section, name);
-            if (sym_index) {
-                esym = &((ElfW(Sym) *)s1->dynsymtab_section->data)[sym_index];
-                type = ELFW(ST_TYPE)(esym->st_info);
-                if ((type == STT_FUNC) || (type == STT_GNU_IFUNC)) {
-                    /* Indirect functions shall have STT_FUNC type in executable
-                     * dynsym section. Indeed, a dlsym call following a lazy
-                     * resolution would pick the symbol value from the
-                     * executable dynsym entry which would contain the address
-                     * of the function wanted by the caller of dlsym instead of
-                     * the address of the function that would return that
-                     * address */
-                    int dynindex
-		      = put_elf_sym(s1->dynsym, 0, esym->st_size,
-				    ELFW(ST_INFO)(STB_GLOBAL,STT_FUNC), 0, 0,
-				    name);
-		    int index = sym - (ElfW(Sym) *) symtab_section->data;
-		    get_sym_attr(s1, index, 1)->dyn_index = dynindex;
-                } else if (type == STT_OBJECT) {
-                    unsigned long offset;
-                    ElfW(Sym) *dynsym;
-                    offset = bss_section->data_offset;
-                    /* XXX: which alignment ? */
-                    offset = (offset + 16 - 1) & -16;
-                    set_elf_sym (s1->symtab, offset, esym->st_size,
-                                 esym->st_info, 0, bss_section->sh_num, name);
-                    index = put_elf_sym(s1->dynsym, offset, esym->st_size,
-                                        esym->st_info, 0, bss_section->sh_num,
-                                        name);
-
-                    /* Ensure R_COPY works for weak symbol aliases */
-                    if (ELFW(ST_BIND)(esym->st_info) == STB_WEAK) {
-                        for_each_elem(s1->dynsymtab_section, 1, dynsym, ElfW(Sym)) {
-                            if ((dynsym->st_value == esym->st_value)
-                                && (ELFW(ST_BIND)(dynsym->st_info) == STB_GLOBAL)) {
-                                char *dynname = (char *) s1->dynsymtab_section->link->data
-                                                + dynsym->st_name;
-                                put_elf_sym(s1->dynsym, offset, dynsym->st_size,
-                                            dynsym->st_info, 0,
-                                            bss_section->sh_num, dynname);
-                                break;
-                            }
-                        }
-                    }
-
-                    put_elf_reloc(s1->dynsym, bss_section,
-                                  offset, R_COPY, index);
-                    offset += esym->st_size;
-                    bss_section->data_offset = offset;
-                }
-            } else {
-                /* STB_WEAK undefined symbols are accepted */
-                /* XXX: _fp_hw seems to be part of the ABI, so we ignore it */
-                if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK ||
-                    !strcmp(name, "_fp_hw")) {
-                } else {
-                    tcc_error_noabort("undefined symbol '%s'", name);
-                }
-            }
-        } else if (s1->rdynamic && ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
-            /* if -rdynamic option, then export all non local symbols */
-            name = (char *) symtab_section->link->data + sym->st_name;
-            set_elf_sym(s1->dynsym, sym->st_value, sym->st_size, sym->st_info,
-                        0, sym->st_shndx, name);
-        }
-    }
-}
-
-/* Bind symbols of libraries: export all non local symbols of executable that
-   are referenced by shared libraries. The reason is that the dynamic loader
-   search symbol first in executable and then in libraries. Therefore a
-   reference to a symbol already defined by a library can still be resolved by
-   a symbol in the executable. */
-static void bind_libs_dynsyms(TCCState *s1)
-{
-    const char *name;
-    int sym_index;
-    ElfW(Sym) *sym, *esym;
-
-    for_each_elem(s1->dynsymtab_section, 1, esym, ElfW(Sym)) {
-        name = (char *) s1->dynsymtab_section->link->data + esym->st_name;
-        sym_index = find_elf_sym(symtab_section, name);
-        sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
-        if (sym_index && sym->st_shndx != SHN_UNDEF
-            && ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
-            set_elf_sym(s1->dynsym, sym->st_value, sym->st_size,
-                sym->st_info, 0, sym->st_shndx, name);
-        } else if (esym->st_shndx == SHN_UNDEF) {
-            /* weak symbols can stay undefined */
-            if (ELFW(ST_BIND)(esym->st_info) != STB_WEAK)
-                tcc_warning("undefined dynamic symbol '%s'", name);
-        }
-    }
-}
-
-/* Export all non local symbols. This is used by shared libraries so that the
-   non local symbols they define can resolve a reference in another shared
-   library or in the executable. Correspondingly, it allows undefined local
-   symbols to be resolved by other shared libraries or by the executable. */
-static void export_global_syms(TCCState *s1)
-{
-    int dynindex, index;
-    const char *name;
-    ElfW(Sym) *sym;
-
-    for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
-        if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
-	    name = (char *) symtab_section->link->data + sym->st_name;
-	    dynindex = put_elf_sym(s1->dynsym, sym->st_value, sym->st_size,
-				   sym->st_info, 0, sym->st_shndx, name);
-	    index = sym - (ElfW(Sym) *) symtab_section->data;
-            get_sym_attr(s1, index, 1)->dyn_index = dynindex;
-        }
-    }
-}
-
-/* Allocate strings for section names and decide if an unallocated section
-   should be output.
-   NOTE: the strsec section comes last, so its size is also correct ! */
-static int alloc_sec_names(TCCState *s1, int file_type, Section *strsec)
-{
-    int i;
-    Section *s;
-    int textrel = 0;
-
-    /* Allocate strings for section names */
-    for(i = 1; i < s1->nb_sections; i++) {
-        s = s1->sections[i];
-        /* when generating a DLL, we include relocations but we may
-           patch them */
-        if (file_type == TCC_OUTPUT_DLL &&
-            s->sh_type == SHT_RELX &&
-            !(s->sh_flags & SHF_ALLOC) &&
-            (s1->sections[s->sh_info]->sh_flags & SHF_ALLOC) &&
-            prepare_dynamic_rel(s1, s)) {
-                if (s1->sections[s->sh_info]->sh_flags & SHF_EXECINSTR)
-                    textrel = 1;
-        } else if (s1->do_debug ||
-            file_type == TCC_OUTPUT_OBJ ||
-            (s->sh_flags & SHF_ALLOC) ||
-	    i == (s1->nb_sections - 1)) {
-            /* we output all sections if debug or object file */
-            s->sh_size = s->data_offset;
-        }
-	if (s->sh_size || (s->sh_flags & SHF_ALLOC))
-            s->sh_name = put_elf_str(strsec, s->name);
-    }
-    strsec->sh_size = strsec->data_offset;
-    return textrel;
-}
-
-/* Info to be copied in dynamic section */
-struct dyn_inf {
-    Section *dynamic;
-    Section *dynstr;
-    unsigned long data_offset;
-    addr_t rel_addr;
-    addr_t rel_size;
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-    addr_t bss_addr;
-    addr_t bss_size;
-#endif
-};
-
-/* Assign sections to segments and decide how are sections laid out when loaded
-   in memory. This function also fills corresponding program headers. */
-static int layout_sections(TCCState *s1, ElfW(Phdr) *phdr, int phnum,
-                           Section *interp, Section* strsec,
-                           struct dyn_inf *dyninf, int *sec_order)
-{
-    int i, j, k, file_type, sh_order_index, file_offset;
-    unsigned long s_align;
-    long long tmp;
-    addr_t addr;
-    ElfW(Phdr) *ph;
-    Section *s;
-
-    file_type = s1->output_type;
-    sh_order_index = 1;
-    file_offset = 0;
-    if (s1->output_format == TCC_OUTPUT_FORMAT_ELF)
-        file_offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr));
-    s_align = ELF_PAGE_SIZE;
-    if (s1->section_align)
-        s_align = s1->section_align;
-
-    if (phnum > 0) {
-        if (s1->has_text_addr) {
-            int a_offset, p_offset;
-            addr = s1->text_addr;
-            /* we ensure that (addr % ELF_PAGE_SIZE) == file_offset %
-               ELF_PAGE_SIZE */
-            a_offset = (int) (addr & (s_align - 1));
-            p_offset = file_offset & (s_align - 1);
-            if (a_offset < p_offset)
-                a_offset += s_align;
-            file_offset += (a_offset - p_offset);
-        } else {
-            if (file_type == TCC_OUTPUT_DLL)
-                addr = 0;
-            else
-                addr = ELF_START_ADDR;
-            /* compute address after headers */
-            addr += (file_offset & (s_align - 1));
-        }
-
-        ph = &phdr[0];
-        /* Leave one program headers for the program interpreter and one for
-           the program header table itself if needed. These are done later as
-           they require section layout to be done first. */
-        if (interp)
-            ph += 2;
-
-        /* dynamic relocation table information, for .dynamic section */
-        dyninf->rel_addr = dyninf->rel_size = 0;
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-        dyninf->bss_addr = dyninf->bss_size = 0;
-#endif
-
-        for(j = 0; j < 2; j++) {
-            ph->p_type = PT_LOAD;
-            if (j == 0)
-                ph->p_flags = PF_R | PF_X;
-            else
-                ph->p_flags = PF_R | PF_W;
-            ph->p_align = s_align;
-
-            /* Decide the layout of sections loaded in memory. This must
-               be done before program headers are filled since they contain
-               info about the layout. We do the following ordering: interp,
-               symbol tables, relocations, progbits, nobits */
-            /* XXX: do faster and simpler sorting */
-            for(k = 0; k < 5; k++) {
-                for(i = 1; i < s1->nb_sections; i++) {
-                    s = s1->sections[i];
-                    /* compute if section should be included */
-                    if (j == 0) {
-                        if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) !=
-                            SHF_ALLOC)
-                            continue;
-                    } else {
-                        if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) !=
-                            (SHF_ALLOC | SHF_WRITE))
-                            continue;
-                    }
-                    if (s == interp) {
-                        if (k != 0)
-                            continue;
-                    } else if (s->sh_type == SHT_DYNSYM ||
-                               s->sh_type == SHT_STRTAB ||
-                               s->sh_type == SHT_HASH) {
-                        if (k != 1)
-                            continue;
-                    } else if (s->sh_type == SHT_RELX) {
-                        if (k != 2)
-                            continue;
-                    } else if (s->sh_type == SHT_NOBITS) {
-                        if (k != 4)
-                            continue;
-                    } else {
-                        if (k != 3)
-                            continue;
-                    }
-                    sec_order[sh_order_index++] = i;
-
-                    /* section matches: we align it and add its size */
-                    tmp = addr;
-                    addr = (addr + s->sh_addralign - 1) &
-                        ~(s->sh_addralign - 1);
-                    file_offset += (int) ( addr - tmp );
-                    s->sh_offset = file_offset;
-                    s->sh_addr = addr;
-
-                    /* update program header infos */
-                    if (ph->p_offset == 0) {
-                        ph->p_offset = file_offset;
-                        ph->p_vaddr = addr;
-                        ph->p_paddr = ph->p_vaddr;
-                    }
-                    /* update dynamic relocation infos */
-                    if (s->sh_type == SHT_RELX) {
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-                        if (!strcmp(strsec->data + s->sh_name, ".rel.got")) {
-                            dyninf->rel_addr = addr;
-                            dyninf->rel_size += s->sh_size; /* XXX only first rel. */
-                        }
-                        if (!strcmp(strsec->data + s->sh_name, ".rel.bss")) {
-                            dyninf->bss_addr = addr;
-                            dyninf->bss_size = s->sh_size; /* XXX only first rel. */
-                        }
-#else
-                        if (dyninf->rel_size == 0)
-                            dyninf->rel_addr = addr;
-                        dyninf->rel_size += s->sh_size;
-#endif
-                    }
-                    addr += s->sh_size;
-                    if (s->sh_type != SHT_NOBITS)
-                        file_offset += s->sh_size;
-                }
-            }
-	    if (j == 0) {
-		/* Make the first PT_LOAD segment include the program
-		   headers itself (and the ELF header as well), it'll
-		   come out with same memory use but will make various
-		   tools like binutils strip work better.  */
-		ph->p_offset &= ~(ph->p_align - 1);
-		ph->p_vaddr &= ~(ph->p_align - 1);
-		ph->p_paddr &= ~(ph->p_align - 1);
-	    }
-            ph->p_filesz = file_offset - ph->p_offset;
-            ph->p_memsz = addr - ph->p_vaddr;
-            ph++;
-            if (j == 0) {
-                if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) {
-                    /* if in the middle of a page, we duplicate the page in
-                       memory so that one copy is RX and the other is RW */
-                    if ((addr & (s_align - 1)) != 0)
-                        addr += s_align;
-                } else {
-                    addr = (addr + s_align - 1) & ~(s_align - 1);
-                    file_offset = (file_offset + s_align - 1) & ~(s_align - 1);
-                }
-            }
-        }
-    }
-
-    /* all other sections come after */
-    for(i = 1; i < s1->nb_sections; i++) {
-        s = s1->sections[i];
-        if (phnum > 0 && (s->sh_flags & SHF_ALLOC))
-            continue;
-        sec_order[sh_order_index++] = i;
-
-        file_offset = (file_offset + s->sh_addralign - 1) &
-            ~(s->sh_addralign - 1);
-        s->sh_offset = file_offset;
-        if (s->sh_type != SHT_NOBITS)
-            file_offset += s->sh_size;
-    }
-
-    return file_offset;
-}
-
-static void fill_unloadable_phdr(ElfW(Phdr) *phdr, int phnum, Section *interp,
-                                 Section *dynamic)
-{
-    ElfW(Phdr) *ph;
-
-    /* if interpreter, then add corresponding program header */
-    if (interp) {
-        ph = &phdr[0];
-
-        ph->p_type = PT_PHDR;
-        ph->p_offset = sizeof(ElfW(Ehdr));
-        ph->p_filesz = ph->p_memsz = phnum * sizeof(ElfW(Phdr));
-        ph->p_vaddr = interp->sh_addr - ph->p_filesz;
-        ph->p_paddr = ph->p_vaddr;
-        ph->p_flags = PF_R | PF_X;
-        ph->p_align = 4; /* interp->sh_addralign; */
-        ph++;
-
-        ph->p_type = PT_INTERP;
-        ph->p_offset = interp->sh_offset;
-        ph->p_vaddr = interp->sh_addr;
-        ph->p_paddr = ph->p_vaddr;
-        ph->p_filesz = interp->sh_size;
-        ph->p_memsz = interp->sh_size;
-        ph->p_flags = PF_R;
-        ph->p_align = interp->sh_addralign;
-    }
-
-    /* if dynamic section, then add corresponding program header */
-    if (dynamic) {
-        ph = &phdr[phnum - 1];
-
-        ph->p_type = PT_DYNAMIC;
-        ph->p_offset = dynamic->sh_offset;
-        ph->p_vaddr = dynamic->sh_addr;
-        ph->p_paddr = ph->p_vaddr;
-        ph->p_filesz = dynamic->sh_size;
-        ph->p_memsz = dynamic->sh_size;
-        ph->p_flags = PF_R | PF_W;
-        ph->p_align = dynamic->sh_addralign;
-    }
-}
-
-/* Fill the dynamic section with tags describing the address and size of
-   sections */
-static void fill_dynamic(TCCState *s1, struct dyn_inf *dyninf)
-{
-    Section *dynamic = dyninf->dynamic;
-
-    /* put dynamic section entries */
-    put_dt(dynamic, DT_HASH, s1->dynsym->hash->sh_addr);
-    put_dt(dynamic, DT_STRTAB, dyninf->dynstr->sh_addr);
-    put_dt(dynamic, DT_SYMTAB, s1->dynsym->sh_addr);
-    put_dt(dynamic, DT_STRSZ, dyninf->dynstr->data_offset);
-    put_dt(dynamic, DT_SYMENT, sizeof(ElfW(Sym)));
-#if PTR_SIZE == 8
-    put_dt(dynamic, DT_RELA, dyninf->rel_addr);
-    put_dt(dynamic, DT_RELASZ, dyninf->rel_size);
-    put_dt(dynamic, DT_RELAENT, sizeof(ElfW_Rel));
-#else
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-    put_dt(dynamic, DT_PLTGOT, s1->got->sh_addr);
-    put_dt(dynamic, DT_PLTRELSZ, dyninf->rel_size);
-    put_dt(dynamic, DT_JMPREL, dyninf->rel_addr);
-    put_dt(dynamic, DT_PLTREL, DT_REL);
-    put_dt(dynamic, DT_REL, dyninf->bss_addr);
-    put_dt(dynamic, DT_RELSZ, dyninf->bss_size);
-#else
-    put_dt(dynamic, DT_REL, dyninf->rel_addr);
-    put_dt(dynamic, DT_RELSZ, dyninf->rel_size);
-    put_dt(dynamic, DT_RELENT, sizeof(ElfW_Rel));
-#endif
-#endif
-    if (s1->do_debug)
-        put_dt(dynamic, DT_DEBUG, 0);
-    put_dt(dynamic, DT_NULL, 0);
-}
-
-/* Relocate remaining sections and symbols (that is those not related to
-   dynamic linking) */
-static int final_sections_reloc(TCCState *s1)
-{
-    int i;
-    Section *s;
-
-    relocate_syms(s1, s1->symtab, 0);
-
-    if (s1->nb_errors != 0)
-        return -1;
-
-    /* relocate sections */
-    /* XXX: ignore sections with allocated relocations ? */
-    for(i = 1; i < s1->nb_sections; i++) {
-        s = s1->sections[i];
-#if defined(TCC_TARGET_I386) || defined(TCC_MUSL)
-        if (s->reloc && s != s1->got && (s->sh_flags & SHF_ALLOC)) //gr
-        /* On X86 gdb 7.3 works in any case but gdb 6.6 will crash if SHF_ALLOC
-        checking is removed */
-#else
-        if (s->reloc && s != s1->got)
-        /* On X86_64 gdb 7.3 will crash if SHF_ALLOC checking is present */
-#endif
-            relocate_section(s1, s);
-    }
-
-    /* relocate relocation entries if the relocation tables are
-       allocated in the executable */
-    for(i = 1; i < s1->nb_sections; i++) {
-        s = s1->sections[i];
-        if ((s->sh_flags & SHF_ALLOC) &&
-            s->sh_type == SHT_RELX) {
-            relocate_rel(s1, s);
-        }
-    }
-    return 0;
-}
-
-/* Create an ELF file on disk.
-   This function handle ELF specific layout requirements */
-static void tcc_output_elf(TCCState *s1, FILE *f, int phnum, ElfW(Phdr) *phdr,
-                           int file_offset, int *sec_order)
-{
-    int i, shnum, offset, size, file_type;
-    Section *s;
-    ElfW(Ehdr) ehdr;
-    ElfW(Shdr) shdr, *sh;
-
-    file_type = s1->output_type;
-    shnum = s1->nb_sections;
-
-    memset(&ehdr, 0, sizeof(ehdr));
-
-    if (phnum > 0) {
-        ehdr.e_phentsize = sizeof(ElfW(Phdr));
-        ehdr.e_phnum = phnum;
-        ehdr.e_phoff = sizeof(ElfW(Ehdr));
-    }
-
-    /* align to 4 */
-    file_offset = (file_offset + 3) & -4;
-
-    /* fill header */
-    ehdr.e_ident[0] = ELFMAG0;
-    ehdr.e_ident[1] = ELFMAG1;
-    ehdr.e_ident[2] = ELFMAG2;
-    ehdr.e_ident[3] = ELFMAG3;
-    ehdr.e_ident[4] = ELFCLASSW;
-    ehdr.e_ident[5] = ELFDATA2LSB;
-    ehdr.e_ident[6] = EV_CURRENT;
-#if !defined(TCC_TARGET_PE) && (defined(__FreeBSD__) || defined(__FreeBSD_kernel__))
-    /* FIXME: should set only for freebsd _target_, but we exclude only PE target */
-    ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD;
-#endif
-#ifdef TCC_TARGET_ARM
-#ifdef TCC_ARM_EABI
-    ehdr.e_ident[EI_OSABI] = 0;
-    ehdr.e_flags = EF_ARM_EABI_VER4;
-    if (file_type == TCC_OUTPUT_EXE || file_type == TCC_OUTPUT_DLL)
-        ehdr.e_flags |= EF_ARM_HASENTRY;
-    if (s1->float_abi == ARM_HARD_FLOAT)
-        ehdr.e_flags |= EF_ARM_VFP_FLOAT;
-    else
-        ehdr.e_flags |= EF_ARM_SOFT_FLOAT;
-#else
-    ehdr.e_ident[EI_OSABI] = ELFOSABI_ARM;
-#endif
-#endif
-    switch(file_type) {
-    default:
-    case TCC_OUTPUT_EXE:
-        ehdr.e_type = ET_EXEC;
-        ehdr.e_entry = get_elf_sym_addr(s1, "_start", 1);
-        break;
-    case TCC_OUTPUT_DLL:
-        ehdr.e_type = ET_DYN;
-        ehdr.e_entry = text_section->sh_addr; /* XXX: is it correct ? */
-        break;
-    case TCC_OUTPUT_OBJ:
-        ehdr.e_type = ET_REL;
-        break;
-    }
-    ehdr.e_machine = EM_TCC_TARGET;
-    ehdr.e_version = EV_CURRENT;
-    ehdr.e_shoff = file_offset;
-    ehdr.e_ehsize = sizeof(ElfW(Ehdr));
-    ehdr.e_shentsize = sizeof(ElfW(Shdr));
-    ehdr.e_shnum = shnum;
-    ehdr.e_shstrndx = shnum - 1;
-
-    fwrite(&ehdr, 1, sizeof(ElfW(Ehdr)), f);
-    fwrite(phdr, 1, phnum * sizeof(ElfW(Phdr)), f);
-    offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr));
-
-    sort_syms(s1, symtab_section);
-    for(i = 1; i < s1->nb_sections; i++) {
-        s = s1->sections[sec_order[i]];
-        if (s->sh_type != SHT_NOBITS) {
-            while (offset < s->sh_offset) {
-                fputc(0, f);
-                offset++;
-            }
-            size = s->sh_size;
-            if (size)
-                fwrite(s->data, 1, size, f);
-            offset += size;
-        }
-    }
-
-    /* output section headers */
-    while (offset < ehdr.e_shoff) {
-        fputc(0, f);
-        offset++;
-    }
-
-    for(i = 0; i < s1->nb_sections; i++) {
-        sh = &shdr;
-        memset(sh, 0, sizeof(ElfW(Shdr)));
-        s = s1->sections[i];
-        if (s) {
-            sh->sh_name = s->sh_name;
-            sh->sh_type = s->sh_type;
-            sh->sh_flags = s->sh_flags;
-            sh->sh_entsize = s->sh_entsize;
-            sh->sh_info = s->sh_info;
-            if (s->link)
-                sh->sh_link = s->link->sh_num;
-            sh->sh_addralign = s->sh_addralign;
-            sh->sh_addr = s->sh_addr;
-            sh->sh_offset = s->sh_offset;
-            sh->sh_size = s->sh_size;
-        }
-        fwrite(sh, 1, sizeof(ElfW(Shdr)), f);
-    }
-}
-
-/* Write an elf, coff or "binary" file */
-static int tcc_write_elf_file(TCCState *s1, const char *filename, int phnum,
-                              ElfW(Phdr) *phdr, int file_offset, int *sec_order)
-{
-    int fd, mode, file_type;
-    FILE *f;
-
-    file_type = s1->output_type;
-    if (file_type == TCC_OUTPUT_OBJ)
-        mode = 0666;
-    else
-        mode = 0777;
-    unlink(filename);
-    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode);
-    if (fd < 0) {
-        tcc_error_noabort("could not write '%s'", filename);
-        return -1;
-    }
-    f = fdopen(fd, "wb");
-    if (s1->verbose)
-        printf("<- %s\n", filename);
-
-#ifdef TCC_TARGET_COFF
-    if (s1->output_format == TCC_OUTPUT_FORMAT_COFF)
-        tcc_output_coff(s1, f);
-    else
-#endif
-    if (s1->output_format == TCC_OUTPUT_FORMAT_ELF)
-        tcc_output_elf(s1, f, phnum, phdr, file_offset, sec_order);
-    else
-        tcc_output_binary(s1, f, sec_order);
-    fclose(f);
-
-    return 0;
-}
-
-/* Sort section headers by assigned sh_addr, remove sections
-   that we aren't going to output.  */
-static void tidy_section_headers(TCCState *s1, int *sec_order)
-{
-    int i, nnew, l, *backmap;
-    Section **snew, *s;
-    ElfW(Sym) *sym;
-
-    snew = tcc_malloc(s1->nb_sections * sizeof(snew[0]));
-    backmap = tcc_malloc(s1->nb_sections * sizeof(backmap[0]));
-    for (i = 0, nnew = 0, l = s1->nb_sections; i < s1->nb_sections; i++) {
-	s = s1->sections[sec_order[i]];
-	if (!i || s->sh_name) {
-	    backmap[sec_order[i]] = nnew;
-	    snew[nnew] = s;
-	    ++nnew;
-	} else {
-	    backmap[sec_order[i]] = 0;
-	    snew[--l] = s;
-	}
-    }
-    for (i = 0; i < nnew; i++) {
-	s = snew[i];
-	if (s) {
-	    s->sh_num = i;
-            if (s->sh_type == SHT_RELX)
-		s->sh_info = backmap[s->sh_info];
-	}
-    }
-
-    for_each_elem(symtab_section, 1, sym, ElfW(Sym))
-	if (sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE)
-	    sym->st_shndx = backmap[sym->st_shndx];
-    if( !s1->static_link ) {
-        for_each_elem(s1->dynsym, 1, sym, ElfW(Sym))
-	    if (sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE)
-	        sym->st_shndx = backmap[sym->st_shndx];
-    }
-    for (i = 0; i < s1->nb_sections; i++)
-	sec_order[i] = i;
-    tcc_free(s1->sections);
-    s1->sections = snew;
-    s1->nb_sections = nnew;
-    tcc_free(backmap);
-}
-
-/* Output an elf, coff or binary file */
-/* XXX: suppress unneeded sections */
-static int elf_output_file(TCCState *s1, const char *filename)
-{
-    int i, ret, phnum, shnum, file_type, file_offset, *sec_order;
-    struct dyn_inf dyninf = {0};
-    ElfW(Phdr) *phdr;
-    ElfW(Sym) *sym;
-    Section *strsec, *interp, *dynamic, *dynstr;
-    int textrel;
-
-    file_type = s1->output_type;
-    s1->nb_errors = 0;
-    ret = -1;
-    phdr = NULL;
-    sec_order = NULL;
-    interp = dynamic = dynstr = NULL; /* avoid warning */
-    textrel = 0;
-
-    if (file_type != TCC_OUTPUT_OBJ) {
-        /* if linking, also link in runtime libraries (libc, libgcc, etc.) */
-        tcc_add_runtime(s1);
-        relocate_common_syms();
-        tcc_add_linker_symbols(s1);
-
-        if (!s1->static_link) {
-            if (file_type == TCC_OUTPUT_EXE) {
-                char *ptr;
-                /* allow override the dynamic loader */
-                const char *elfint = getenv("LD_SO");
-                if (elfint == NULL)
-                    elfint = DEFAULT_ELFINTERP(s1);
-                /* add interpreter section only if executable */
-                interp = new_section(s1, ".interp", SHT_PROGBITS, SHF_ALLOC);
-                interp->sh_addralign = 1;
-                ptr = section_ptr_add(interp, 1 + strlen(elfint));
-                strcpy(ptr, elfint);
-            }
-
-            /* add dynamic symbol table */
-            s1->dynsym = new_symtab(s1, ".dynsym", SHT_DYNSYM, SHF_ALLOC,
-                                    ".dynstr",
-                                    ".hash", SHF_ALLOC);
-            dynstr = s1->dynsym->link;
-
-            /* add dynamic section */
-            dynamic = new_section(s1, ".dynamic", SHT_DYNAMIC,
-                                  SHF_ALLOC | SHF_WRITE);
-            dynamic->link = dynstr;
-            dynamic->sh_entsize = sizeof(ElfW(Dyn));
-
-            build_got(s1);
-
-            if (file_type == TCC_OUTPUT_EXE) {
-                bind_exe_dynsyms(s1);
-                if (s1->nb_errors)
-                    goto the_end;
-                bind_libs_dynsyms(s1);
-            } else {
-                /* shared library case: simply export all global symbols */
-                export_global_syms(s1);
-            }
-        }
-        build_got_entries(s1);
-    }
-
-    /* we add a section for symbols */
-    strsec = new_section(s1, ".shstrtab", SHT_STRTAB, 0);
-    put_elf_str(strsec, "");
-
-    /* Allocate strings for section names */
-    textrel = alloc_sec_names(s1, file_type, strsec);
-
-    if (dynamic) {
-        /* add a list of needed dlls */
-        for(i = 0; i < s1->nb_loaded_dlls; i++) {
-            DLLReference *dllref = s1->loaded_dlls[i];
-            if (dllref->level == 0)
-                put_dt(dynamic, DT_NEEDED, put_elf_str(dynstr, dllref->name));
-        }
-
-        if (s1->rpath)
-            put_dt(dynamic, s1->enable_new_dtags ? DT_RUNPATH : DT_RPATH,
-                   put_elf_str(dynstr, s1->rpath));
-
-        if (file_type == TCC_OUTPUT_DLL) {
-            if (s1->soname)
-                put_dt(dynamic, DT_SONAME, put_elf_str(dynstr, s1->soname));
-            /* XXX: currently, since we do not handle PIC code, we
-               must relocate the readonly segments */
-            if (textrel)
-                put_dt(dynamic, DT_TEXTREL, 0);
-        }
-
-        if (s1->symbolic)
-            put_dt(dynamic, DT_SYMBOLIC, 0);
-
-        dyninf.dynamic = dynamic;
-        dyninf.dynstr = dynstr;
-        /* remember offset and reserve space for 2nd call below */
-        dyninf.data_offset = dynamic->data_offset;
-        fill_dynamic(s1, &dyninf);
-        dynamic->sh_size = dynamic->data_offset;
-        dynstr->sh_size = dynstr->data_offset;
-    }
-
-    /* compute number of program headers */
-    if (file_type == TCC_OUTPUT_OBJ)
-        phnum = 0;
-    else if (file_type == TCC_OUTPUT_DLL)
-        phnum = 3;
-    else if (s1->static_link)
-        phnum = 2;
-    else
-        phnum = 5;
-
-    /* allocate program segment headers */
-    phdr = tcc_mallocz(phnum * sizeof(ElfW(Phdr)));
-
-    /* compute number of sections */
-    shnum = s1->nb_sections;
-
-    /* this array is used to reorder sections in the output file */
-    sec_order = tcc_malloc(sizeof(int) * shnum);
-    sec_order[0] = 0;
-
-    /* compute section to program header mapping */
-    file_offset = layout_sections(s1, phdr, phnum, interp, strsec, &dyninf,
-                                  sec_order);
-
-    /* Fill remaining program header and finalize relocation related to dynamic
-       linking. */
-    if (file_type != TCC_OUTPUT_OBJ) {
-        fill_unloadable_phdr(phdr, phnum, interp, dynamic);
-        if (dynamic) {
-            dynamic->data_offset = dyninf.data_offset;
-            fill_dynamic(s1, &dyninf);
-
-            /* put in GOT the dynamic section address and relocate PLT */
-            write32le(s1->got->data, dynamic->sh_addr);
-            if (file_type == TCC_OUTPUT_EXE
-                || (RELOCATE_DLLPLT && file_type == TCC_OUTPUT_DLL))
-                relocate_plt(s1);
-
-            /* relocate symbols in .dynsym now that final addresses are known */
-            for_each_elem(s1->dynsym, 1, sym, ElfW(Sym)) {
-                if (sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE) {
-                    /* do symbol relocation */
-                    sym->st_value += s1->sections[sym->st_shndx]->sh_addr;
-                }
-            }
-        }
-
-        /* if building executable or DLL, then relocate each section
-           except the GOT which is already relocated */
-        ret = final_sections_reloc(s1);
-        if (ret)
-            goto the_end;
-	tidy_section_headers(s1, sec_order);
-
-        /* Perform relocation to GOT or PLT entries */
-        if (file_type == TCC_OUTPUT_EXE && s1->static_link)
-            fill_got(s1);
-        else if (s1->got)
-            fill_local_got_entries(s1);
-    }
-
-    /* Create the ELF file with name 'filename' */
-    ret = tcc_write_elf_file(s1, filename, phnum, phdr, file_offset, sec_order);
-    s1->nb_sections = shnum;
- the_end:
-    tcc_free(sec_order);
-    tcc_free(phdr);
-    return ret;
-}
-
-LIBTCCAPI int tcc_output_file(TCCState *s, const char *filename)
-{
-    int ret;
-#ifdef TCC_TARGET_PE
-    if (s->output_type != TCC_OUTPUT_OBJ) {
-        ret = pe_output_file(s, filename);
-    } else
-#endif
-        ret = elf_output_file(s, filename);
-    return ret;
-}
-
-static void *load_data(int fd, unsigned long file_offset, unsigned long size)
-{
-    void *data;
-
-    data = tcc_malloc(size);
-    lseek(fd, file_offset, SEEK_SET);
-    read(fd, data, size);
-    return data;
-}
-
-typedef struct SectionMergeInfo {
-    Section *s;            /* corresponding existing section */
-    unsigned long offset;  /* offset of the new section in the existing section */
-    uint8_t new_section;       /* true if section 's' was added */
-    uint8_t link_once;         /* true if link once section */
-} SectionMergeInfo;
-
-ST_FUNC int tcc_object_type(int fd, ElfW(Ehdr) *h)
-{
-    int size = read(fd, h, sizeof *h);
-    if (size == sizeof *h && 0 == memcmp(h, ELFMAG, 4)) {
-        if (h->e_type == ET_REL)
-            return AFF_BINTYPE_REL;
-        if (h->e_type == ET_DYN)
-            return AFF_BINTYPE_DYN;
-    } else if (size >= 8) {
-        if (0 == memcmp(h, ARMAG, 8))
-            return AFF_BINTYPE_AR;
-#ifdef TCC_TARGET_COFF
-        if (((struct filehdr*)h)->f_magic == COFF_C67_MAGIC)
-            return AFF_BINTYPE_C67;
-#endif
-    }
-    return 0;
-}
-
-/* load an object file and merge it with current files */
-/* XXX: handle correctly stab (debug) info */
-ST_FUNC int tcc_load_object_file(TCCState *s1,
-                                int fd, unsigned long file_offset)
-{
-    ElfW(Ehdr) ehdr;
-    ElfW(Shdr) *shdr, *sh;
-    int size, i, j, offset, offseti, nb_syms, sym_index, ret, seencompressed;
-    unsigned char *strsec, *strtab;
-    int *old_to_new_syms;
-    char *sh_name, *name;
-    SectionMergeInfo *sm_table, *sm;
-    ElfW(Sym) *sym, *symtab;
-    ElfW_Rel *rel;
-    Section *s;
-
-    int stab_index;
-    int stabstr_index;
-
-    stab_index = stabstr_index = 0;
-
-    lseek(fd, file_offset, SEEK_SET);
-    if (tcc_object_type(fd, &ehdr) != AFF_BINTYPE_REL)
-        goto fail1;
-    /* test CPU specific stuff */
-    if (ehdr.e_ident[5] != ELFDATA2LSB ||
-        ehdr.e_machine != EM_TCC_TARGET) {
-    fail1:
-        tcc_error_noabort("invalid object file");
-        return -1;
-    }
-    /* read sections */
-    shdr = load_data(fd, file_offset + ehdr.e_shoff,
-                     sizeof(ElfW(Shdr)) * ehdr.e_shnum);
-    sm_table = tcc_mallocz(sizeof(SectionMergeInfo) * ehdr.e_shnum);
-
-    /* load section names */
-    sh = &shdr[ehdr.e_shstrndx];
-    strsec = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
-
-    /* load symtab and strtab */
-    old_to_new_syms = NULL;
-    symtab = NULL;
-    strtab = NULL;
-    nb_syms = 0;
-    seencompressed = 0;
-    for(i = 1; i < ehdr.e_shnum; i++) {
-        sh = &shdr[i];
-        if (sh->sh_type == SHT_SYMTAB) {
-            if (symtab) {
-                tcc_error_noabort("object must contain only one symtab");
-            fail:
-                ret = -1;
-                goto the_end;
-            }
-            nb_syms = sh->sh_size / sizeof(ElfW(Sym));
-            symtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
-            sm_table[i].s = symtab_section;
-
-            /* now load strtab */
-            sh = &shdr[sh->sh_link];
-            strtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
-        }
-	if (sh->sh_flags & SHF_COMPRESSED)
-	    seencompressed = 1;
-    }
-
-    /* now examine each section and try to merge its content with the
-       ones in memory */
-    for(i = 1; i < ehdr.e_shnum; i++) {
-        /* no need to examine section name strtab */
-        if (i == ehdr.e_shstrndx)
-            continue;
-        sh = &shdr[i];
-        sh_name = (char *) strsec + sh->sh_name;
-        /* ignore sections types we do not handle */
-        if (sh->sh_type != SHT_PROGBITS &&
-            sh->sh_type != SHT_RELX &&
-#ifdef TCC_ARM_EABI
-            sh->sh_type != SHT_ARM_EXIDX &&
-#endif
-            sh->sh_type != SHT_NOBITS &&
-            sh->sh_type != SHT_PREINIT_ARRAY &&
-            sh->sh_type != SHT_INIT_ARRAY &&
-            sh->sh_type != SHT_FINI_ARRAY &&
-            strcmp(sh_name, ".stabstr")
-            )
-            continue;
-	if (seencompressed
-	    && (!strncmp(sh_name, ".debug_", sizeof(".debug_")-1)
-		|| (sh->sh_type == SHT_RELX
-		    && !strncmp((char*)strsec + shdr[sh->sh_info].sh_name,
-			        ".debug_", sizeof(".debug_")-1))))
-	  continue;
-        if (sh->sh_addralign < 1)
-            sh->sh_addralign = 1;
-        /* find corresponding section, if any */
-        for(j = 1; j < s1->nb_sections;j++) {
-            s = s1->sections[j];
-            if (!strcmp(s->name, sh_name)) {
-                if (!strncmp(sh_name, ".gnu.linkonce",
-                             sizeof(".gnu.linkonce") - 1)) {
-                    /* if a 'linkonce' section is already present, we
-                       do not add it again. It is a little tricky as
-                       symbols can still be defined in
-                       it. */
-                    sm_table[i].link_once = 1;
-                    goto next;
-                } else {
-                    goto found;
-                }
-            }
-        }
-        /* not found: create new section */
-        s = new_section(s1, sh_name, sh->sh_type, sh->sh_flags & ~SHF_GROUP);
-        /* take as much info as possible from the section. sh_link and
-           sh_info will be updated later */
-        s->sh_addralign = sh->sh_addralign;
-        s->sh_entsize = sh->sh_entsize;
-        sm_table[i].new_section = 1;
-    found:
-        if (sh->sh_type != s->sh_type) {
-            tcc_error_noabort("invalid section type");
-            goto fail;
-        }
-
-        /* align start of section */
-        offset = s->data_offset;
-
-        if (0 == strcmp(sh_name, ".stab")) {
-            stab_index = i;
-            goto no_align;
-        }
-        if (0 == strcmp(sh_name, ".stabstr")) {
-            stabstr_index = i;
-            goto no_align;
-        }
-
-        size = sh->sh_addralign - 1;
-        offset = (offset + size) & ~size;
-        if (sh->sh_addralign > s->sh_addralign)
-            s->sh_addralign = sh->sh_addralign;
-        s->data_offset = offset;
-    no_align:
-        sm_table[i].offset = offset;
-        sm_table[i].s = s;
-        /* concatenate sections */
-        size = sh->sh_size;
-        if (sh->sh_type != SHT_NOBITS) {
-            unsigned char *ptr;
-            lseek(fd, file_offset + sh->sh_offset, SEEK_SET);
-            ptr = section_ptr_add(s, size);
-            read(fd, ptr, size);
-        } else {
-            s->data_offset += size;
-        }
-    next: ;
-    }
-
-    /* gr relocate stab strings */
-    if (stab_index && stabstr_index) {
-        Stab_Sym *a, *b;
-        unsigned o;
-        s = sm_table[stab_index].s;
-        a = (Stab_Sym *)(s->data + sm_table[stab_index].offset);
-        b = (Stab_Sym *)(s->data + s->data_offset);
-        o = sm_table[stabstr_index].offset;
-        while (a < b)
-            a->n_strx += o, a++;
-    }
-
-    /* second short pass to update sh_link and sh_info fields of new
-       sections */
-    for(i = 1; i < ehdr.e_shnum; i++) {
-        s = sm_table[i].s;
-        if (!s || !sm_table[i].new_section)
-            continue;
-        sh = &shdr[i];
-        if (sh->sh_link > 0)
-            s->link = sm_table[sh->sh_link].s;
-        if (sh->sh_type == SHT_RELX) {
-            s->sh_info = sm_table[sh->sh_info].s->sh_num;
-            /* update backward link */
-            s1->sections[s->sh_info]->reloc = s;
-        }
-    }
-    sm = sm_table;
-
-    /* resolve symbols */
-    old_to_new_syms = tcc_mallocz(nb_syms * sizeof(int));
-
-    sym = symtab + 1;
-    for(i = 1; i < nb_syms; i++, sym++) {
-        if (sym->st_shndx != SHN_UNDEF &&
-            sym->st_shndx < SHN_LORESERVE) {
-            sm = &sm_table[sym->st_shndx];
-            if (sm->link_once) {
-                /* if a symbol is in a link once section, we use the
-                   already defined symbol. It is very important to get
-                   correct relocations */
-                if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
-                    name = (char *) strtab + sym->st_name;
-                    sym_index = find_elf_sym(symtab_section, name);
-                    if (sym_index)
-                        old_to_new_syms[i] = sym_index;
-                }
-                continue;
-            }
-            /* if no corresponding section added, no need to add symbol */
-            if (!sm->s)
-                continue;
-            /* convert section number */
-            sym->st_shndx = sm->s->sh_num;
-            /* offset value */
-            sym->st_value += sm->offset;
-        }
-        /* add symbol */
-        name = (char *) strtab + sym->st_name;
-        sym_index = set_elf_sym(symtab_section, sym->st_value, sym->st_size,
-                                sym->st_info, sym->st_other,
-                                sym->st_shndx, name);
-        old_to_new_syms[i] = sym_index;
-    }
-
-    /* third pass to patch relocation entries */
-    for(i = 1; i < ehdr.e_shnum; i++) {
-        s = sm_table[i].s;
-        if (!s)
-            continue;
-        sh = &shdr[i];
-        offset = sm_table[i].offset;
-        switch(s->sh_type) {
-        case SHT_RELX:
-            /* take relocation offset information */
-            offseti = sm_table[sh->sh_info].offset;
-            for_each_elem(s, (offset / sizeof(*rel)), rel, ElfW_Rel) {
-                int type;
-                unsigned sym_index;
-                /* convert symbol index */
-                type = ELFW(R_TYPE)(rel->r_info);
-                sym_index = ELFW(R_SYM)(rel->r_info);
-                /* NOTE: only one symtab assumed */
-                if (sym_index >= nb_syms)
-                    goto invalid_reloc;
-                sym_index = old_to_new_syms[sym_index];
-                /* ignore link_once in rel section. */
-                if (!sym_index && !sm->link_once
-#ifdef TCC_TARGET_ARM
-                    && type != R_ARM_V4BX
-#endif
-                   ) {
-                invalid_reloc:
-                    tcc_error_noabort("Invalid relocation entry [%2d] '%s' @ %.8x",
-                        i, strsec + sh->sh_name, rel->r_offset);
-                    goto fail;
-                }
-                rel->r_info = ELFW(R_INFO)(sym_index, type);
-                /* offset the relocation offset */
-                rel->r_offset += offseti;
-#ifdef TCC_TARGET_ARM
-                /* Jumps and branches from a Thumb code to a PLT entry need
-                   special handling since PLT entries are ARM code.
-                   Unconditional bl instructions referencing PLT entries are
-                   handled by converting these instructions into blx
-                   instructions. Other case of instructions referencing a PLT
-                   entry require to add a Thumb stub before the PLT entry to
-                   switch to ARM mode. We set bit plt_thumb_stub of the
-                   attribute of a symbol to indicate such a case. */
-                if (type == R_ARM_THM_JUMP24)
-                    get_sym_attr(s1, sym_index, 1)->plt_thumb_stub = 1;
-#endif
-            }
-            break;
-        default:
-            break;
-        }
-    }
-
-    ret = 0;
- the_end:
-    tcc_free(symtab);
-    tcc_free(strtab);
-    tcc_free(old_to_new_syms);
-    tcc_free(sm_table);
-    tcc_free(strsec);
-    tcc_free(shdr);
-    return ret;
-}
-
-typedef struct ArchiveHeader {
-    char ar_name[16];           /* name of this member */
-    char ar_date[12];           /* file mtime */
-    char ar_uid[6];             /* owner uid; printed as decimal */
-    char ar_gid[6];             /* owner gid; printed as decimal */
-    char ar_mode[8];            /* file mode, printed as octal   */
-    char ar_size[10];           /* file size, printed as decimal */
-    char ar_fmag[2];            /* should contain ARFMAG */
-} ArchiveHeader;
-
-static int get_be32(const uint8_t *b)
-{
-    return b[3] | (b[2] << 8) | (b[1] << 16) | (b[0] << 24);
-}
-
-static long get_be64(const uint8_t *b)
-{
-  long long ret = get_be32(b);
-  ret = (ret << 32) | (unsigned)get_be32(b+4);
-  return (long)ret;
-}
-
-/* load only the objects which resolve undefined symbols */
-static int tcc_load_alacarte(TCCState *s1, int fd, int size, int entrysize)
-{
-    long i, bound, nsyms, sym_index, off, ret;
-    uint8_t *data;
-    const char *ar_names, *p;
-    const uint8_t *ar_index;
-    ElfW(Sym) *sym;
-
-    data = tcc_malloc(size);
-    if (read(fd, data, size) != size)
-        goto fail;
-    nsyms = entrysize == 4 ? get_be32(data) : get_be64(data);
-    ar_index = data + entrysize;
-    ar_names = (char *) ar_index + nsyms * entrysize;
-
-    do {
-        bound = 0;
-        for(p = ar_names, i = 0; i < nsyms; i++, p += strlen(p)+1) {
-            sym_index = find_elf_sym(symtab_section, p);
-            if(sym_index) {
-                sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
-                if(sym->st_shndx == SHN_UNDEF) {
-                    off = (entrysize == 4
-			   ? get_be32(ar_index + i * 4)
-			   : get_be64(ar_index + i * 8))
-			  + sizeof(ArchiveHeader);
-                    ++bound;
-                    if(tcc_load_object_file(s1, fd, off) < 0) {
-                    fail:
-                        ret = -1;
-                        goto the_end;
-                    }
-                }
-            }
-        }
-    } while(bound);
-    ret = 0;
- the_end:
-    tcc_free(data);
-    return ret;
-}
-
-/* load a '.a' file */
-ST_FUNC int tcc_load_archive(TCCState *s1, int fd)
-{
-    ArchiveHeader hdr;
-    char ar_size[11];
-    char ar_name[17];
-    char magic[8];
-    int size, len, i;
-    unsigned long file_offset;
-
-    /* skip magic which was already checked */
-    read(fd, magic, sizeof(magic));
-
-    for(;;) {
-        len = read(fd, &hdr, sizeof(hdr));
-        if (len == 0)
-            break;
-        if (len != sizeof(hdr)) {
-            tcc_error_noabort("invalid archive");
-            return -1;
-        }
-        memcpy(ar_size, hdr.ar_size, sizeof(hdr.ar_size));
-        ar_size[sizeof(hdr.ar_size)] = '\0';
-        size = strtol(ar_size, NULL, 0);
-        memcpy(ar_name, hdr.ar_name, sizeof(hdr.ar_name));
-        for(i = sizeof(hdr.ar_name) - 1; i >= 0; i--) {
-            if (ar_name[i] != ' ')
-                break;
-        }
-        ar_name[i + 1] = '\0';
-        file_offset = lseek(fd, 0, SEEK_CUR);
-        /* align to even */
-        size = (size + 1) & ~1;
-        if (!strcmp(ar_name, "/")) {
-            /* coff symbol table : we handle it */
-            if(s1->alacarte_link)
-                return tcc_load_alacarte(s1, fd, size, 4);
-	} else if (!strcmp(ar_name, "/SYM64/")) {
-            if(s1->alacarte_link)
-                return tcc_load_alacarte(s1, fd, size, 8);
-        } else {
-            ElfW(Ehdr) ehdr;
-            if (tcc_object_type(fd, &ehdr) == AFF_BINTYPE_REL) {
-                if (tcc_load_object_file(s1, fd, file_offset) < 0)
-                    return -1;
-            }
-        }
-        lseek(fd, file_offset + size, SEEK_SET);
-    }
-    return 0;
-}
-
-#ifndef TCC_TARGET_PE
-/* load a DLL and all referenced DLLs. 'level = 0' means that the DLL
-   is referenced by the user (so it should be added as DT_NEEDED in
-   the generated ELF file) */
-ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level)
-{
-    ElfW(Ehdr) ehdr;
-    ElfW(Shdr) *shdr, *sh, *sh1;
-    int i, j, nb_syms, nb_dts, sym_bind, ret;
-    ElfW(Sym) *sym, *dynsym;
-    ElfW(Dyn) *dt, *dynamic;
-    unsigned char *dynstr;
-    const char *name, *soname;
-    DLLReference *dllref;
-
-    read(fd, &ehdr, sizeof(ehdr));
-
-    /* test CPU specific stuff */
-    if (ehdr.e_ident[5] != ELFDATA2LSB ||
-        ehdr.e_machine != EM_TCC_TARGET) {
-        tcc_error_noabort("bad architecture");
-        return -1;
-    }
-
-    /* read sections */
-    shdr = load_data(fd, ehdr.e_shoff, sizeof(ElfW(Shdr)) * ehdr.e_shnum);
-
-    /* load dynamic section and dynamic symbols */
-    nb_syms = 0;
-    nb_dts = 0;
-    dynamic = NULL;
-    dynsym = NULL; /* avoid warning */
-    dynstr = NULL; /* avoid warning */
-    for(i = 0, sh = shdr; i < ehdr.e_shnum; i++, sh++) {
-        switch(sh->sh_type) {
-        case SHT_DYNAMIC:
-            nb_dts = sh->sh_size / sizeof(ElfW(Dyn));
-            dynamic = load_data(fd, sh->sh_offset, sh->sh_size);
-            break;
-        case SHT_DYNSYM:
-            nb_syms = sh->sh_size / sizeof(ElfW(Sym));
-            dynsym = load_data(fd, sh->sh_offset, sh->sh_size);
-            sh1 = &shdr[sh->sh_link];
-            dynstr = load_data(fd, sh1->sh_offset, sh1->sh_size);
-            break;
-        default:
-            break;
-        }
-    }
-
-    /* compute the real library name */
-    soname = tcc_basename(filename);
-
-    for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) {
-        if (dt->d_tag == DT_SONAME) {
-            soname = (char *) dynstr + dt->d_un.d_val;
-        }
-    }
-
-    /* if the dll is already loaded, do not load it */
-    for(i = 0; i < s1->nb_loaded_dlls; i++) {
-        dllref = s1->loaded_dlls[i];
-        if (!strcmp(soname, dllref->name)) {
-            /* but update level if needed */
-            if (level < dllref->level)
-                dllref->level = level;
-            ret = 0;
-            goto the_end;
-        }
-    }
-
-    /* add the dll and its level */
-    dllref = tcc_mallocz(sizeof(DLLReference) + strlen(soname));
-    dllref->level = level;
-    strcpy(dllref->name, soname);
-    dynarray_add(&s1->loaded_dlls, &s1->nb_loaded_dlls, dllref);
-
-    /* add dynamic symbols in dynsym_section */
-    for(i = 1, sym = dynsym + 1; i < nb_syms; i++, sym++) {
-        sym_bind = ELFW(ST_BIND)(sym->st_info);
-        if (sym_bind == STB_LOCAL)
-            continue;
-        name = (char *) dynstr + sym->st_name;
-        set_elf_sym(s1->dynsymtab_section, sym->st_value, sym->st_size,
-                    sym->st_info, sym->st_other, sym->st_shndx, name);
-    }
-
-    /* load all referenced DLLs */
-    for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) {
-        switch(dt->d_tag) {
-        case DT_NEEDED:
-            name = (char *) dynstr + dt->d_un.d_val;
-            for(j = 0; j < s1->nb_loaded_dlls; j++) {
-                dllref = s1->loaded_dlls[j];
-                if (!strcmp(name, dllref->name))
-                    goto already_loaded;
-            }
-            if (tcc_add_dll(s1, name, AFF_REFERENCED_DLL) < 0) {
-                tcc_error_noabort("referenced dll '%s' not found", name);
-                ret = -1;
-                goto the_end;
-            }
-        already_loaded:
-            break;
-        }
-    }
-    ret = 0;
- the_end:
-    tcc_free(dynstr);
-    tcc_free(dynsym);
-    tcc_free(dynamic);
-    tcc_free(shdr);
-    return ret;
-}
-
-#define LD_TOK_NAME 256
-#define LD_TOK_EOF  (-1)
-
-/* return next ld script token */
-static int ld_next(TCCState *s1, char *name, int name_size)
-{
-    int c;
-    char *q;
-
- redo:
-    switch(ch) {
-    case ' ':
-    case '\t':
-    case '\f':
-    case '\v':
-    case '\r':
-    case '\n':
-        inp();
-        goto redo;
-    case '/':
-        minp();
-        if (ch == '*') {
-            file->buf_ptr = parse_comment(file->buf_ptr);
-            ch = file->buf_ptr[0];
-            goto redo;
-        } else {
-            q = name;
-            *q++ = '/';
-            goto parse_name;
-        }
-        break;
-    case '\\':
-        ch = handle_eob();
-        if (ch != '\\')
-            goto redo;
-        /* fall through */
-    /* case 'a' ... 'z': */
-    case 'a':
-       case 'b':
-       case 'c':
-       case 'd':
-       case 'e':
-       case 'f':
-       case 'g':
-       case 'h':
-       case 'i':
-       case 'j':
-       case 'k':
-       case 'l':
-       case 'm':
-       case 'n':
-       case 'o':
-       case 'p':
-       case 'q':
-       case 'r':
-       case 's':
-       case 't':
-       case 'u':
-       case 'v':
-       case 'w':
-       case 'x':
-       case 'y':
-       case 'z':
-    /* case 'A' ... 'z': */
-    case 'A':
-       case 'B':
-       case 'C':
-       case 'D':
-       case 'E':
-       case 'F':
-       case 'G':
-       case 'H':
-       case 'I':
-       case 'J':
-       case 'K':
-       case 'L':
-       case 'M':
-       case 'N':
-       case 'O':
-       case 'P':
-       case 'Q':
-       case 'R':
-       case 'S':
-       case 'T':
-       case 'U':
-       case 'V':
-       case 'W':
-       case 'X':
-       case 'Y':
-       case 'Z':
-    case '_':
-    case '.':
-    case '$':
-    case '~':
-        q = name;
-    parse_name:
-        for(;;) {
-            if (!((ch >= 'a' && ch <= 'z') ||
-                  (ch >= 'A' && ch <= 'Z') ||
-                  (ch >= '0' && ch <= '9') ||
-                  strchr("/.-_+=$:\\,~", ch)))
-                break;
-            if ((q - name) < name_size - 1) {
-                *q++ = ch;
-            }
-            minp();
-        }
-        *q = '\0';
-        c = LD_TOK_NAME;
-        break;
-    case CH_EOF:
-        c = LD_TOK_EOF;
-        break;
-    default:
-        c = ch;
-        inp();
-        break;
-    }
-    return c;
-}
-
-static int ld_add_file(TCCState *s1, const char filename[])
-{
-    if (filename[0] == '/') {
-        if (CONFIG_SYSROOT[0] == '\0'
-            && tcc_add_file_internal(s1, filename, AFF_TYPE_BIN) == 0)
-            return 0;
-        filename = tcc_basename(filename);
-    }
-    return tcc_add_dll(s1, filename, 0);
-}
-
-static inline int new_undef_syms(void)
-{
-    int ret = 0;
-    ret = new_undef_sym;
-    new_undef_sym = 0;
-    return ret;
-}
-
-static int ld_add_file_list(TCCState *s1, const char *cmd, int as_needed)
-{
-    char filename[1024], libname[1024];
-    int t, group, nblibs = 0, ret = 0;
-    char **libs = NULL;
-
-    group = !strcmp(cmd, "GROUP");
-    if (!as_needed)
-        new_undef_syms();
-    t = ld_next(s1, filename, sizeof(filename));
-    if (t != '(')
-        expect("(");
-    t = ld_next(s1, filename, sizeof(filename));
-    for(;;) {
-        libname[0] = '\0';
-        if (t == LD_TOK_EOF) {
-            tcc_error_noabort("unexpected end of file");
-            ret = -1;
-            goto lib_parse_error;
-        } else if (t == ')') {
-            break;
-        } else if (t == '-') {
-            t = ld_next(s1, filename, sizeof(filename));
-            if ((t != LD_TOK_NAME) || (filename[0] != 'l')) {
-                tcc_error_noabort("library name expected");
-                ret = -1;
-                goto lib_parse_error;
-            }
-            pstrcpy(libname, sizeof libname, &filename[1]);
-            if (s1->static_link) {
-                snprintf(filename, sizeof filename, "lib%s.a", libname);
-            } else {
-                snprintf(filename, sizeof filename, "lib%s.so", libname);
-            }
-        } else if (t != LD_TOK_NAME) {
-            tcc_error_noabort("filename expected");
-            ret = -1;
-            goto lib_parse_error;
-        }
-        if (!strcmp(filename, "AS_NEEDED")) {
-            ret = ld_add_file_list(s1, cmd, 1);
-            if (ret)
-                goto lib_parse_error;
-        } else {
-            /* TODO: Implement AS_NEEDED support. Ignore it for now */
-            if (!as_needed) {
-                ret = ld_add_file(s1, filename);
-                if (ret)
-                    goto lib_parse_error;
-                if (group) {
-                    /* Add the filename *and* the libname to avoid future conversions */
-                    dynarray_add(&libs, &nblibs, tcc_strdup(filename));
-                    if (libname[0] != '\0')
-                        dynarray_add(&libs, &nblibs, tcc_strdup(libname));
-                }
-            }
-        }
-        t = ld_next(s1, filename, sizeof(filename));
-        if (t == ',') {
-            t = ld_next(s1, filename, sizeof(filename));
-        }
-    }
-    if (group && !as_needed) {
-        while (new_undef_syms()) {
-            int i;
-
-            for (i = 0; i < nblibs; i ++)
-                ld_add_file(s1, libs[i]);
-        }
-    }
-lib_parse_error:
-    dynarray_reset(&libs, &nblibs);
-    return ret;
-}
-
-/* interpret a subset of GNU ldscripts to handle the dummy libc.so
-   files */
-ST_FUNC int tcc_load_ldscript(TCCState *s1)
-{
-    char cmd[64];
-    char filename[1024];
-    int t, ret;
-
-    ch = handle_eob();
-    for(;;) {
-        t = ld_next(s1, cmd, sizeof(cmd));
-        if (t == LD_TOK_EOF)
-            return 0;
-        else if (t != LD_TOK_NAME)
-            return -1;
-        if (!strcmp(cmd, "INPUT") ||
-            !strcmp(cmd, "GROUP")) {
-            ret = ld_add_file_list(s1, cmd, 0);
-            if (ret)
-                return ret;
-        } else if (!strcmp(cmd, "OUTPUT_FORMAT") ||
-                   !strcmp(cmd, "TARGET")) {
-            /* ignore some commands */
-            t = ld_next(s1, cmd, sizeof(cmd));
-            if (t != '(')
-                expect("(");
-            for(;;) {
-                t = ld_next(s1, filename, sizeof(filename));
-                if (t == LD_TOK_EOF) {
-                    tcc_error_noabort("unexpected end of file");
-                    return -1;
-                } else if (t == ')') {
-                    break;
-                }
-            }
-        } else {
-            return -1;
-        }
-    }
-    return 0;
-}
-#endif /* !TCC_TARGET_PE */
diff --git a/tinyc/tccgen.c b/tinyc/tccgen.c
deleted file mode 100644
index 7d554b5b1..000000000
--- a/tinyc/tccgen.c
+++ /dev/null
@@ -1,7369 +0,0 @@
-/*
- *  TCC - Tiny C Compiler
- * 
- *  Copyright (c) 2001-2004 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "tcc.h"
-
-/********************************************************/
-/* global variables */
-
-/* loc : local variable index
-   ind : output code index
-   rsym: return symbol
-   anon_sym: anonymous symbol index
-*/
-ST_DATA int rsym, anon_sym, ind, loc;
-
-ST_DATA Sym *sym_free_first;
-ST_DATA void **sym_pools;
-ST_DATA int nb_sym_pools;
-
-ST_DATA Sym *global_stack;
-ST_DATA Sym *local_stack;
-ST_DATA Sym *define_stack;
-ST_DATA Sym *global_label_stack;
-ST_DATA Sym *local_label_stack;
-static int local_scope;
-static int in_sizeof;
-static int section_sym;
-
-ST_DATA int vlas_in_scope; /* number of VLAs that are currently in scope */
-ST_DATA int vla_sp_root_loc; /* vla_sp_loc for SP before any VLAs were pushed */
-ST_DATA int vla_sp_loc; /* Pointer to variable holding location to store stack pointer on the stack when modifying stack pointer */
-
-ST_DATA SValue __vstack[1+VSTACK_SIZE], *vtop, *pvtop;
-
-ST_DATA int const_wanted; /* true if constant wanted */
-ST_DATA int nocode_wanted; /* no code generation wanted */
-#define NODATA_WANTED (nocode_wanted > 0) /* no static data output wanted either */
-#define STATIC_DATA_WANTED (nocode_wanted & 0xC0000000) /* only static data output */
-ST_DATA int global_expr;  /* true if compound literals must be allocated globally (used during initializers parsing */
-ST_DATA CType func_vt; /* current function return type (used by return instruction) */
-ST_DATA int func_var; /* true if current function is variadic (used by return instruction) */
-ST_DATA int func_vc;
-ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */
-ST_DATA const char *funcname;
-ST_DATA int g_debug;
-
-ST_DATA CType char_pointer_type, func_old_type, int_type, size_type, ptrdiff_type;
-
-ST_DATA struct switch_t {
-    struct case_t {
-        int64_t v1, v2;
-	int sym;
-    } **p; int n; /* list of case ranges */
-    int def_sym; /* default symbol */
-} *cur_switch; /* current switch */
-
-/* ------------------------------------------------------------------------- */
-
-static void gen_cast(CType *type);
-static void gen_cast_s(int t);
-static inline CType *pointed_type(CType *type);
-static int is_compatible_types(CType *type1, CType *type2);
-static int parse_btype(CType *type, AttributeDef *ad);
-static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td);
-static void parse_expr_type(CType *type);
-static void init_putv(CType *type, Section *sec, unsigned long c);
-static void decl_initializer(CType *type, Section *sec, unsigned long c, int first, int size_only);
-static void block(int *bsym, int *csym, int is_expr);
-static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, int scope);
-static void decl(int l);
-static int decl0(int l, int is_for_loop_init, Sym *);
-static void expr_eq(void);
-static void vla_runtime_type_size(CType *type, int *a);
-static void vla_sp_restore(void);
-static void vla_sp_restore_root(void);
-static int is_compatible_unqualified_types(CType *type1, CType *type2);
-static inline int64_t expr_const64(void);
-static void vpush64(int ty, unsigned long long v);
-static void vpush(CType *type);
-static int gvtst(int inv, int t);
-static void gen_inline_functions(TCCState *s);
-static void skip_or_save_block(TokenString **str);
-static void gv_dup(void);
-
-ST_INLN int is_float(int t)
-{
-    int bt;
-    bt = t & VT_BTYPE;
-    return bt == VT_LDOUBLE || bt == VT_DOUBLE || bt == VT_FLOAT || bt == VT_QFLOAT;
-}
-
-/* we use our own 'finite' function to avoid potential problems with
-   non standard math libs */
-/* XXX: endianness dependent */
-ST_FUNC int ieee_finite(double d)
-{
-    int p[4];
-    memcpy(p, &d, sizeof(double));
-    return ((unsigned)((p[1] | 0x800fffff) + 1)) >> 31;
-}
-
-/* compiling intel long double natively */
-#if (defined __i386__ || defined __x86_64__) \
-    && (defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64)
-# define TCC_IS_NATIVE_387
-#endif
-
-ST_FUNC void test_lvalue(void)
-{
-    if (!(vtop->r & VT_LVAL))
-        expect("lvalue");
-}
-
-ST_FUNC void check_vstack(void)
-{
-    if (pvtop != vtop)
-        tcc_error("internal compiler error: vstack leak (%d)", vtop - pvtop);
-}
-
-/* ------------------------------------------------------------------------- */
-/* vstack debugging aid */
-
-#if 0
-void pv (const char *lbl, int a, int b)
-{
-    int i;
-    for (i = a; i < a + b; ++i) {
-        SValue *p = &vtop[-i];
-        printf("%s vtop[-%d] : type.t:%04x  r:%04x  r2:%04x  c.i:%d\n",
-            lbl, i, p->type.t, p->r, p->r2, (int)p->c.i);
-    }
-}
-#endif
-
-/* ------------------------------------------------------------------------- */
-/* start of translation unit info */
-ST_FUNC void tcc_debug_start(TCCState *s1)
-{
-    if (s1->do_debug) {
-        char buf[512];
-
-        /* file info: full path + filename */
-        section_sym = put_elf_sym(symtab_section, 0, 0,
-                                  ELFW(ST_INFO)(STB_LOCAL, STT_SECTION), 0,
-                                  text_section->sh_num, NULL);
-        getcwd(buf, sizeof(buf));
-#ifdef _WIN32
-        normalize_slashes(buf);
-#endif
-        pstrcat(buf, sizeof(buf), "/");
-        put_stabs_r(buf, N_SO, 0, 0,
-                    text_section->data_offset, text_section, section_sym);
-        put_stabs_r(file->filename, N_SO, 0, 0,
-                    text_section->data_offset, text_section, section_sym);
-        last_ind = 0;
-        last_line_num = 0;
-    }
-
-    /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
-       symbols can be safely used */
-    put_elf_sym(symtab_section, 0, 0,
-                ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
-                SHN_ABS, file->filename);
-}
-
-/* put end of translation unit info */
-ST_FUNC void tcc_debug_end(TCCState *s1)
-{
-    if (!s1->do_debug)
-        return;
-    put_stabs_r(NULL, N_SO, 0, 0,
-        text_section->data_offset, text_section, section_sym);
-
-}
-
-/* generate line number info */
-ST_FUNC void tcc_debug_line(TCCState *s1)
-{
-    if (!s1->do_debug)
-        return;
-    if ((last_line_num != file->line_num || last_ind != ind)) {
-        put_stabn(N_SLINE, 0, file->line_num, ind - func_ind);
-        last_ind = ind;
-        last_line_num = file->line_num;
-    }
-}
-
-/* put function symbol */
-ST_FUNC void tcc_debug_funcstart(TCCState *s1, Sym *sym)
-{
-    char buf[512];
-
-    if (!s1->do_debug)
-        return;
-
-    /* stabs info */
-    /* XXX: we put here a dummy type */
-    snprintf(buf, sizeof(buf), "%s:%c1",
-             funcname, sym->type.t & VT_STATIC ? 'f' : 'F');
-    put_stabs_r(buf, N_FUN, 0, file->line_num, 0,
-                cur_text_section, sym->c);
-    /* //gr gdb wants a line at the function */
-    put_stabn(N_SLINE, 0, file->line_num, 0);
-
-    last_ind = 0;
-    last_line_num = 0;
-}
-
-/* put function size */
-ST_FUNC void tcc_debug_funcend(TCCState *s1, int size)
-{
-    if (!s1->do_debug)
-        return;
-    put_stabn(N_FUN, 0, 0, size);
-}
-
-/* ------------------------------------------------------------------------- */
-ST_FUNC int tccgen_compile(TCCState *s1)
-{
-    cur_text_section = NULL;
-    funcname = "";
-    anon_sym = SYM_FIRST_ANOM;
-    section_sym = 0;
-    const_wanted = 0;
-    nocode_wanted = 0x80000000;
-
-    /* define some often used types */
-    int_type.t = VT_INT;
-    char_pointer_type.t = VT_BYTE;
-    mk_pointer(&char_pointer_type);
-#if PTR_SIZE == 4
-    size_type.t = VT_INT | VT_UNSIGNED;
-    ptrdiff_type.t = VT_INT;
-#elif LONG_SIZE == 4
-    size_type.t = VT_LLONG | VT_UNSIGNED;
-    ptrdiff_type.t = VT_LLONG;
-#else
-    size_type.t = VT_LONG | VT_LLONG | VT_UNSIGNED;
-    ptrdiff_type.t = VT_LONG | VT_LLONG;
-#endif
-    func_old_type.t = VT_FUNC;
-    func_old_type.ref = sym_push(SYM_FIELD, &int_type, 0, 0);
-    func_old_type.ref->f.func_call = FUNC_CDECL;
-    func_old_type.ref->f.func_type = FUNC_OLD;
-
-    tcc_debug_start(s1);
-
-#ifdef TCC_TARGET_ARM
-    arm_init(s1);
-#endif
-
-#ifdef INC_DEBUG
-    printf("%s: **** new file\n", file->filename);
-#endif
-
-    parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM | PARSE_FLAG_TOK_STR;
-    next();
-    decl(VT_CONST);
-    gen_inline_functions(s1);
-    check_vstack();
-    /* end of translation unit info */
-    tcc_debug_end(s1);
-    return 0;
-}
-
-/* ------------------------------------------------------------------------- */
-/* apply storage attributes to Elf symbol */
-
-static void update_storage(Sym *sym)
-{
-    ElfW(Sym) *esym;
-    if (0 == sym->c)
-        return;
-    esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
-    if (sym->a.visibility)
-        esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1))
-            | sym->a.visibility;
-    if (sym->a.weak)
-        esym->st_info = ELFW(ST_INFO)(STB_WEAK, ELFW(ST_TYPE)(esym->st_info));
-#ifdef TCC_TARGET_PE
-    if (sym->a.dllimport)
-        esym->st_other |= ST_PE_IMPORT;
-    if (sym->a.dllexport)
-        esym->st_other |= ST_PE_EXPORT;
-#endif
-#if 0
-    printf("storage %s: vis=%d weak=%d exp=%d imp=%d\n",
-        get_tok_str(sym->v, NULL),
-        sym->a.visibility,
-        sym->a.weak,
-        sym->a.dllexport,
-        sym->a.dllimport
-        );
-#endif
-}
-
-/* ------------------------------------------------------------------------- */
-/* update sym->c so that it points to an external symbol in section
-   'section' with value 'value' */
-
-ST_FUNC void put_extern_sym2(Sym *sym, Section *section,
-                            addr_t value, unsigned long size,
-                            int can_add_underscore)
-{
-    int sym_type, sym_bind, sh_num, info, other, t;
-    ElfW(Sym) *esym;
-    const char *name;
-    char buf1[256];
-#ifdef CONFIG_TCC_BCHECK
-    char buf[32];
-#endif
-
-    if (section == NULL)
-        sh_num = SHN_UNDEF;
-    else if (section == SECTION_ABS)
-        sh_num = SHN_ABS;
-    else
-        sh_num = section->sh_num;
-
-    if (!sym->c) {
-        name = get_tok_str(sym->v, NULL);
-#ifdef CONFIG_TCC_BCHECK
-        if (tcc_state->do_bounds_check) {
-            /* XXX: avoid doing that for statics ? */
-            /* if bound checking is activated, we change some function
-               names by adding the "__bound" prefix */
-            switch(sym->v) {
-#ifdef TCC_TARGET_PE
-            /* XXX: we rely only on malloc hooks */
-            case TOK_malloc:
-            case TOK_free:
-            case TOK_realloc:
-            case TOK_memalign:
-            case TOK_calloc:
-#endif
-            case TOK_memcpy:
-            case TOK_memmove:
-            case TOK_memset:
-            case TOK_strlen:
-            case TOK_strcpy:
-            case TOK_alloca:
-                strcpy(buf, "__bound_");
-                strcat(buf, name);
-                name = buf;
-                break;
-            }
-        }
-#endif
-        t = sym->type.t;
-        if ((t & VT_BTYPE) == VT_FUNC) {
-            sym_type = STT_FUNC;
-        } else if ((t & VT_BTYPE) == VT_VOID) {
-            sym_type = STT_NOTYPE;
-        } else {
-            sym_type = STT_OBJECT;
-        }
-        if (t & VT_STATIC)
-            sym_bind = STB_LOCAL;
-        else
-            sym_bind = STB_GLOBAL;
-        other = 0;
-#ifdef TCC_TARGET_PE
-        if (sym_type == STT_FUNC && sym->type.ref) {
-            Sym *ref = sym->type.ref;
-            if (ref->f.func_call == FUNC_STDCALL && can_add_underscore) {
-                sprintf(buf1, "_%s@%d", name, ref->f.func_args * PTR_SIZE);
-                name = buf1;
-                other |= ST_PE_STDCALL;
-                can_add_underscore = 0;
-            }
-        }
-#endif
-        if (tcc_state->leading_underscore && can_add_underscore) {
-            buf1[0] = '_';
-            pstrcpy(buf1 + 1, sizeof(buf1) - 1, name);
-            name = buf1;
-        }
-        if (sym->asm_label)
-            name = get_tok_str(sym->asm_label, NULL);
-        info = ELFW(ST_INFO)(sym_bind, sym_type);
-        sym->c = set_elf_sym(symtab_section, value, size, info, other, sh_num, name);
-    } else {
-        esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
-        esym->st_value = value;
-        esym->st_size = size;
-        esym->st_shndx = sh_num;
-    }
-    update_storage(sym);
-}
-
-ST_FUNC void put_extern_sym(Sym *sym, Section *section,
-                           addr_t value, unsigned long size)
-{
-    put_extern_sym2(sym, section, value, size, 1);
-}
-
-/* add a new relocation entry to symbol 'sym' in section 's' */
-ST_FUNC void greloca(Section *s, Sym *sym, unsigned long offset, int type,
-                     addr_t addend)
-{
-    int c = 0;
-
-    if (nocode_wanted && s == cur_text_section)
-        return;
-
-    if (sym) {
-        if (0 == sym->c)
-            put_extern_sym(sym, NULL, 0, 0);
-        c = sym->c;
-    }
-
-    /* now we can add ELF relocation info */
-    put_elf_reloca(symtab_section, s, offset, type, c, addend);
-}
-
-#if PTR_SIZE == 4
-ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type)
-{
-    greloca(s, sym, offset, type, 0);
-}
-#endif
-
-/* ------------------------------------------------------------------------- */
-/* symbol allocator */
-static Sym *__sym_malloc(void)
-{
-    Sym *sym_pool, *sym, *last_sym;
-    int i;
-
-    sym_pool = tcc_malloc(SYM_POOL_NB * sizeof(Sym));
-    dynarray_add(&sym_pools, &nb_sym_pools, sym_pool);
-
-    last_sym = sym_free_first;
-    sym = sym_pool;
-    for(i = 0; i < SYM_POOL_NB; i++) {
-        sym->next = last_sym;
-        last_sym = sym;
-        sym++;
-    }
-    sym_free_first = last_sym;
-    return last_sym;
-}
-
-static inline Sym *sym_malloc(void)
-{
-    Sym *sym;
-#ifndef SYM_DEBUG
-    sym = sym_free_first;
-    if (!sym)
-        sym = __sym_malloc();
-    sym_free_first = sym->next;
-    return sym;
-#else
-    sym = tcc_malloc(sizeof(Sym));
-    return sym;
-#endif
-}
-
-ST_INLN void sym_free(Sym *sym)
-{
-#ifndef SYM_DEBUG
-    sym->next = sym_free_first;
-    sym_free_first = sym;
-#else
-    tcc_free(sym);
-#endif
-}
-
-/* push, without hashing */
-ST_FUNC Sym *sym_push2(Sym **ps, int v, int t, int c)
-{
-    Sym *s;
-
-    s = sym_malloc();
-    memset(s, 0, sizeof *s);
-    s->v = v;
-    s->type.t = t;
-    s->c = c;
-    /* add in stack */
-    s->prev = *ps;
-    *ps = s;
-    return s;
-}
-
-/* find a symbol and return its associated structure. 's' is the top
-   of the symbol stack */
-ST_FUNC Sym *sym_find2(Sym *s, int v)
-{
-    while (s) {
-        if (s->v == v)
-            return s;
-        else if (s->v == -1)
-            return NULL;
-        s = s->prev;
-    }
-    return NULL;
-}
-
-/* structure lookup */
-ST_INLN Sym *struct_find(int v)
-{
-    v -= TOK_IDENT;
-    if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
-        return NULL;
-    return table_ident[v]->sym_struct;
-}
-
-/* find an identifier */
-ST_INLN Sym *sym_find(int v)
-{
-    v -= TOK_IDENT;
-    if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
-        return NULL;
-    return table_ident[v]->sym_identifier;
-}
-
-/* push a given symbol on the symbol stack */
-ST_FUNC Sym *sym_push(int v, CType *type, int r, int c)
-{
-    Sym *s, **ps;
-    TokenSym *ts;
-
-    if (local_stack)
-        ps = &local_stack;
-    else
-        ps = &global_stack;
-    s = sym_push2(ps, v, type->t, c);
-    s->type.ref = type->ref;
-    s->r = r;
-    /* don't record fields or anonymous symbols */
-    /* XXX: simplify */
-    if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
-        /* record symbol in token array */
-        ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT];
-        if (v & SYM_STRUCT)
-            ps = &ts->sym_struct;
-        else
-            ps = &ts->sym_identifier;
-        s->prev_tok = *ps;
-        *ps = s;
-        s->sym_scope = local_scope;
-        if (s->prev_tok && s->prev_tok->sym_scope == s->sym_scope)
-            tcc_error("redeclaration of '%s'",
-                get_tok_str(v & ~SYM_STRUCT, NULL));
-    }
-    return s;
-}
-
-/* push a global identifier */
-ST_FUNC Sym *global_identifier_push(int v, int t, int c)
-{
-    Sym *s, **ps;
-    s = sym_push2(&global_stack, v, t, c);
-    /* don't record anonymous symbol */
-    if (v < SYM_FIRST_ANOM) {
-        ps = &table_ident[v - TOK_IDENT]->sym_identifier;
-        /* modify the top most local identifier, so that
-           sym_identifier will point to 's' when popped */
-        while (*ps != NULL)
-            ps = &(*ps)->prev_tok;
-        s->prev_tok = NULL;
-        *ps = s;
-    }
-    return s;
-}
-
-/* pop symbols until top reaches 'b'.  If KEEP is non-zero don't really
-   pop them yet from the list, but do remove them from the token array.  */
-ST_FUNC void sym_pop(Sym **ptop, Sym *b, int keep)
-{
-    Sym *s, *ss, **ps;
-    TokenSym *ts;
-    int v;
-
-    s = *ptop;
-    while(s != b) {
-        ss = s->prev;
-        v = s->v;
-        /* remove symbol in token array */
-        /* XXX: simplify */
-        if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
-            ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT];
-            if (v & SYM_STRUCT)
-                ps = &ts->sym_struct;
-            else
-                ps = &ts->sym_identifier;
-            *ps = s->prev_tok;
-        }
-	if (!keep)
-	    sym_free(s);
-        s = ss;
-    }
-    if (!keep)
-	*ptop = b;
-}
-
-/* ------------------------------------------------------------------------- */
-
-static void vsetc(CType *type, int r, CValue *vc)
-{
-    int v;
-
-    if (vtop >= vstack + (VSTACK_SIZE - 1))
-        tcc_error("memory full (vstack)");
-    /* cannot let cpu flags if other instruction are generated. Also
-       avoid leaving VT_JMP anywhere except on the top of the stack
-       because it would complicate the code generator.
-
-       Don't do this when nocode_wanted.  vtop might come from
-       !nocode_wanted regions (see 88_codeopt.c) and transforming
-       it to a register without actually generating code is wrong
-       as their value might still be used for real.  All values
-       we push under nocode_wanted will eventually be popped
-       again, so that the VT_CMP/VT_JMP value will be in vtop
-       when code is unsuppressed again.
-
-       Same logic below in vswap(); */
-    if (vtop >= vstack && !nocode_wanted) {
-        v = vtop->r & VT_VALMASK;
-        if (v == VT_CMP || (v & ~1) == VT_JMP)
-            gv(RC_INT);
-    }
-
-    vtop++;
-    vtop->type = *type;
-    vtop->r = r;
-    vtop->r2 = VT_CONST;
-    vtop->c = *vc;
-    vtop->sym = NULL;
-}
-
-ST_FUNC void vswap(void)
-{
-    SValue tmp;
-    /* cannot vswap cpu flags. See comment at vsetc() above */
-    if (vtop >= vstack && !nocode_wanted) {
-        int v = vtop->r & VT_VALMASK;
-        if (v == VT_CMP || (v & ~1) == VT_JMP)
-            gv(RC_INT);
-    }
-    tmp = vtop[0];
-    vtop[0] = vtop[-1];
-    vtop[-1] = tmp;
-}
-
-/* pop stack value */
-ST_FUNC void vpop(void)
-{
-    int v;
-    v = vtop->r & VT_VALMASK;
-#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
-    /* for x86, we need to pop the FP stack */
-    if (v == TREG_ST0) {
-        o(0xd8dd); /* fstp %st(0) */
-    } else
-#endif
-    if (v == VT_JMP || v == VT_JMPI) {
-        /* need to put correct jump if && or || without test */
-        gsym(vtop->c.i);
-    }
-    vtop--;
-}
-
-/* push constant of type "type" with useless value */
-ST_FUNC void vpush(CType *type)
-{
-    vset(type, VT_CONST, 0);
-}
-
-/* push integer constant */
-ST_FUNC void vpushi(int v)
-{
-    CValue cval;
-    cval.i = v;
-    vsetc(&int_type, VT_CONST, &cval);
-}
-
-/* push a pointer sized constant */
-static void vpushs(addr_t v)
-{
-  CValue cval;
-  cval.i = v;
-  vsetc(&size_type, VT_CONST, &cval);
-}
-
-/* push arbitrary 64bit constant */
-ST_FUNC void vpush64(int ty, unsigned long long v)
-{
-    CValue cval;
-    CType ctype;
-    ctype.t = ty;
-    ctype.ref = NULL;
-    cval.i = v;
-    vsetc(&ctype, VT_CONST, &cval);
-}
-
-/* push long long constant */
-static inline void vpushll(long long v)
-{
-    vpush64(VT_LLONG, v);
-}
-
-ST_FUNC void vset(CType *type, int r, int v)
-{
-    CValue cval;
-
-    cval.i = v;
-    vsetc(type, r, &cval);
-}
-
-static void vseti(int r, int v)
-{
-    CType type;
-    type.t = VT_INT;
-    type.ref = NULL;
-    vset(&type, r, v);
-}
-
-ST_FUNC void vpushv(SValue *v)
-{
-    if (vtop >= vstack + (VSTACK_SIZE - 1))
-        tcc_error("memory full (vstack)");
-    vtop++;
-    *vtop = *v;
-}
-
-static void vdup(void)
-{
-    vpushv(vtop);
-}
-
-/* rotate n first stack elements to the bottom
-   I1 ... In -> I2 ... In I1 [top is right]
-*/
-ST_FUNC void vrotb(int n)
-{
-    int i;
-    SValue tmp;
-
-    tmp = vtop[-n + 1];
-    for(i=-n+1;i!=0;i++)
-        vtop[i] = vtop[i+1];
-    vtop[0] = tmp;
-}
-
-/* rotate the n elements before entry e towards the top
-   I1 ... In ... -> In I1 ... I(n-1) ... [top is right]
- */
-ST_FUNC void vrote(SValue *e, int n)
-{
-    int i;
-    SValue tmp;
-
-    tmp = *e;
-    for(i = 0;i < n - 1; i++)
-        e[-i] = e[-i - 1];
-    e[-n + 1] = tmp;
-}
-
-/* rotate n first stack elements to the top
-   I1 ... In -> In I1 ... I(n-1)  [top is right]
- */
-ST_FUNC void vrott(int n)
-{
-    vrote(vtop, n);
-}
-
-/* push a symbol value of TYPE */
-static inline void vpushsym(CType *type, Sym *sym)
-{
-    CValue cval;
-    cval.i = 0;
-    vsetc(type, VT_CONST | VT_SYM, &cval);
-    vtop->sym = sym;
-}
-
-/* Return a static symbol pointing to a section */
-ST_FUNC Sym *get_sym_ref(CType *type, Section *sec, unsigned long offset, unsigned long size)
-{
-    int v;
-    Sym *sym;
-
-    v = anon_sym++;
-    sym = global_identifier_push(v, type->t | VT_STATIC, 0);
-    sym->type.ref = type->ref;
-    sym->r = VT_CONST | VT_SYM;
-    put_extern_sym(sym, sec, offset, size);
-    return sym;
-}
-
-/* push a reference to a section offset by adding a dummy symbol */
-static void vpush_ref(CType *type, Section *sec, unsigned long offset, unsigned long size)
-{
-    vpushsym(type, get_sym_ref(type, sec, offset, size));  
-}
-
-/* define a new external reference to a symbol 'v' of type 'u' */
-ST_FUNC Sym *external_global_sym(int v, CType *type, int r)
-{
-    Sym *s;
-
-    s = sym_find(v);
-    if (!s) {
-        /* push forward reference */
-        s = global_identifier_push(v, type->t | VT_EXTERN, 0);
-        s->type.ref = type->ref;
-        s->r = r | VT_CONST | VT_SYM;
-    }
-    return s;
-}
-
-/* Merge some storage attributes.  */
-static void patch_storage(Sym *sym, AttributeDef *ad, CType *type)
-{
-    if (type && !is_compatible_types(&sym->type, type))
-        tcc_error("incompatible types for redefinition of '%s'",
-            get_tok_str(sym->v, NULL));
-#ifdef TCC_TARGET_PE
-    if (sym->a.dllimport != ad->a.dllimport)
-        tcc_error("incompatible dll linkage for redefinition of '%s'",
-            get_tok_str(sym->v, NULL));
-#endif
-    sym->a.dllexport |= ad->a.dllexport;
-    sym->a.weak |= ad->a.weak;
-    if (ad->a.visibility) {
-        int vis = sym->a.visibility;
-        int vis2 = ad->a.visibility;
-        if (vis == STV_DEFAULT)
-            vis = vis2;
-        else if (vis2 != STV_DEFAULT)
-            vis = (vis < vis2) ? vis : vis2;
-        sym->a.visibility = vis;
-    }
-    if (ad->a.aligned)
-        sym->a.aligned = ad->a.aligned;
-    if (ad->asm_label)
-        sym->asm_label = ad->asm_label;
-    update_storage(sym);
-}
-
-/* define a new external reference to a symbol 'v' */
-static Sym *external_sym(int v, CType *type, int r, AttributeDef *ad)
-{
-    Sym *s;
-    s = sym_find(v);
-    if (!s) {
-        /* push forward reference */
-        s = sym_push(v, type, r | VT_CONST | VT_SYM, 0);
-        s->type.t |= VT_EXTERN;
-        s->a = ad->a;
-        s->sym_scope = 0;
-    } else {
-        if (s->type.ref == func_old_type.ref) {
-            s->type.ref = type->ref;
-            s->r = r | VT_CONST | VT_SYM;
-            s->type.t |= VT_EXTERN;
-        }
-        patch_storage(s, ad, type);
-    }
-    return s;
-}
-
-/* push a reference to global symbol v */
-ST_FUNC void vpush_global_sym(CType *type, int v)
-{
-    vpushsym(type, external_global_sym(v, type, 0));
-}
-
-/* save registers up to (vtop - n) stack entry */
-ST_FUNC void save_regs(int n)
-{
-    SValue *p, *p1;
-    for(p = vstack, p1 = vtop - n; p <= p1; p++)
-        save_reg(p->r);
-}
-
-/* save r to the memory stack, and mark it as being free */
-ST_FUNC void save_reg(int r)
-{
-    save_reg_upstack(r, 0);
-}
-
-/* save r to the memory stack, and mark it as being free,
-   if seen up to (vtop - n) stack entry */
-ST_FUNC void save_reg_upstack(int r, int n)
-{
-    int l, saved, size, align;
-    SValue *p, *p1, sv;
-    CType *type;
-
-    if ((r &= VT_VALMASK) >= VT_CONST)
-        return;
-    if (nocode_wanted)
-        return;
-
-    /* modify all stack values */
-    saved = 0;
-    l = 0;
-    for(p = vstack, p1 = vtop - n; p <= p1; p++) {
-        if ((p->r & VT_VALMASK) == r ||
-            ((p->type.t & VT_BTYPE) == VT_LLONG && (p->r2 & VT_VALMASK) == r)) {
-            /* must save value on stack if not already done */
-            if (!saved) {
-                /* NOTE: must reload 'r' because r might be equal to r2 */
-                r = p->r & VT_VALMASK;
-                /* store register in the stack */
-                type = &p->type;
-                if ((p->r & VT_LVAL) ||
-                    (!is_float(type->t) && (type->t & VT_BTYPE) != VT_LLONG))
-#if PTR_SIZE == 8
-                    type = &char_pointer_type;
-#else
-                    type = &int_type;
-#endif
-                size = type_size(type, &align);
-                loc = (loc - size) & -align;
-                sv.type.t = type->t;
-                sv.r = VT_LOCAL | VT_LVAL;
-                sv.c.i = loc;
-                store(r, &sv);
-#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
-                /* x86 specific: need to pop fp register ST0 if saved */
-                if (r == TREG_ST0) {
-                    o(0xd8dd); /* fstp %st(0) */
-                }
-#endif
-#if PTR_SIZE == 4
-                /* special long long case */
-                if ((type->t & VT_BTYPE) == VT_LLONG) {
-                    sv.c.i += 4;
-                    store(p->r2, &sv);
-                }
-#endif
-                l = loc;
-                saved = 1;
-            }
-            /* mark that stack entry as being saved on the stack */
-            if (p->r & VT_LVAL) {
-                /* also clear the bounded flag because the
-                   relocation address of the function was stored in
-                   p->c.i */
-                p->r = (p->r & ~(VT_VALMASK | VT_BOUNDED)) | VT_LLOCAL;
-            } else {
-                p->r = lvalue_type(p->type.t) | VT_LOCAL;
-            }
-            p->r2 = VT_CONST;
-            p->c.i = l;
-        }
-    }
-}
-
-#ifdef TCC_TARGET_ARM
-/* find a register of class 'rc2' with at most one reference on stack.
- * If none, call get_reg(rc) */
-ST_FUNC int get_reg_ex(int rc, int rc2)
-{
-    int r;
-    SValue *p;
-    
-    for(r=0;r<NB_REGS;r++) {
-        if (reg_classes[r] & rc2) {
-            int n;
-            n=0;
-            for(p = vstack; p <= vtop; p++) {
-                if ((p->r & VT_VALMASK) == r ||
-                    (p->r2 & VT_VALMASK) == r)
-                    n++;
-            }
-            if (n <= 1)
-                return r;
-        }
-    }
-    return get_reg(rc);
-}
-#endif
-
-/* find a free register of class 'rc'. If none, save one register */
-ST_FUNC int get_reg(int rc)
-{
-    int r;
-    SValue *p;
-
-    /* find a free register */
-    for(r=0;r<NB_REGS;r++) {
-        if (reg_classes[r] & rc) {
-            if (nocode_wanted)
-                return r;
-            for(p=vstack;p<=vtop;p++) {
-                if ((p->r & VT_VALMASK) == r ||
-                    (p->r2 & VT_VALMASK) == r)
-                    goto notfound;
-            }
-            return r;
-        }
-    notfound: ;
-    }
-    
-    /* no register left : free the first one on the stack (VERY
-       IMPORTANT to start from the bottom to ensure that we don't
-       spill registers used in gen_opi()) */
-    for(p=vstack;p<=vtop;p++) {
-        /* look at second register (if long long) */
-        r = p->r2 & VT_VALMASK;
-        if (r < VT_CONST && (reg_classes[r] & rc))
-            goto save_found;
-        r = p->r & VT_VALMASK;
-        if (r < VT_CONST && (reg_classes[r] & rc)) {
-        save_found:
-            save_reg(r);
-            return r;
-        }
-    }
-    /* Should never comes here */
-    return -1;
-}
-
-/* move register 's' (of type 't') to 'r', and flush previous value of r to memory
-   if needed */
-static void move_reg(int r, int s, int t)
-{
-    SValue sv;
-
-    if (r != s) {
-        save_reg(r);
-        sv.type.t = t;
-        sv.type.ref = NULL;
-        sv.r = s;
-        sv.c.i = 0;
-        load(r, &sv);
-    }
-}
-
-/* get address of vtop (vtop MUST BE an lvalue) */
-ST_FUNC void gaddrof(void)
-{
-    vtop->r &= ~VT_LVAL;
-    /* tricky: if saved lvalue, then we can go back to lvalue */
-    if ((vtop->r & VT_VALMASK) == VT_LLOCAL)
-        vtop->r = (vtop->r & ~(VT_VALMASK | VT_LVAL_TYPE)) | VT_LOCAL | VT_LVAL;
-
-
-}
-
-#ifdef CONFIG_TCC_BCHECK
-/* generate lvalue bound code */
-static void gbound(void)
-{
-    int lval_type;
-    CType type1;
-
-    vtop->r &= ~VT_MUSTBOUND;
-    /* if lvalue, then use checking code before dereferencing */
-    if (vtop->r & VT_LVAL) {
-        /* if not VT_BOUNDED value, then make one */
-        if (!(vtop->r & VT_BOUNDED)) {
-            lval_type = vtop->r & (VT_LVAL_TYPE | VT_LVAL);
-            /* must save type because we must set it to int to get pointer */
-            type1 = vtop->type;
-            vtop->type.t = VT_PTR;
-            gaddrof();
-            vpushi(0);
-            gen_bounded_ptr_add();
-            vtop->r |= lval_type;
-            vtop->type = type1;
-        }
-        /* then check for dereferencing */
-        gen_bounded_ptr_deref();
-    }
-}
-#endif
-
-static void incr_bf_adr(int o)
-{
-    vtop->type = char_pointer_type;
-    gaddrof();
-    vpushi(o);
-    gen_op('+');
-    vtop->type.t = (vtop->type.t & ~(VT_BTYPE|VT_DEFSIGN))
-        | (VT_BYTE|VT_UNSIGNED);
-    vtop->r = (vtop->r & ~VT_LVAL_TYPE)
-        | (VT_LVAL_BYTE|VT_LVAL_UNSIGNED|VT_LVAL);
-}
-
-/* single-byte load mode for packed or otherwise unaligned bitfields */
-static void load_packed_bf(CType *type, int bit_pos, int bit_size)
-{
-    int n, o, bits;
-    save_reg_upstack(vtop->r, 1);
-    vpush64(type->t & VT_BTYPE, 0); // B X
-    bits = 0, o = bit_pos >> 3, bit_pos &= 7;
-    do {
-        vswap(); // X B
-        incr_bf_adr(o);
-        vdup(); // X B B
-        n = 8 - bit_pos;
-        if (n > bit_size)
-            n = bit_size;
-        if (bit_pos)
-            vpushi(bit_pos), gen_op(TOK_SHR), bit_pos = 0; // X B Y
-        if (n < 8)
-            vpushi((1 << n) - 1), gen_op('&');
-        gen_cast(type);
-        if (bits)
-            vpushi(bits), gen_op(TOK_SHL);
-        vrotb(3); // B Y X
-        gen_op('|'); // B X
-        bits += n, bit_size -= n, o = 1;
-    } while (bit_size);
-    vswap(), vpop();
-    if (!(type->t & VT_UNSIGNED)) {
-        n = ((type->t & VT_BTYPE) == VT_LLONG ? 64 : 32) - bits;
-        vpushi(n), gen_op(TOK_SHL);
-        vpushi(n), gen_op(TOK_SAR);
-    }
-}
-
-/* single-byte store mode for packed or otherwise unaligned bitfields */
-static void store_packed_bf(int bit_pos, int bit_size)
-{
-    int bits, n, o, m, c;
-
-    c = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
-    vswap(); // X B
-    save_reg_upstack(vtop->r, 1);
-    bits = 0, o = bit_pos >> 3, bit_pos &= 7;
-    do {
-        incr_bf_adr(o); // X B
-        vswap(); //B X
-        c ? vdup() : gv_dup(); // B V X
-        vrott(3); // X B V
-        if (bits)
-            vpushi(bits), gen_op(TOK_SHR);
-        if (bit_pos)
-            vpushi(bit_pos), gen_op(TOK_SHL);
-        n = 8 - bit_pos;
-        if (n > bit_size)
-            n = bit_size;
-        if (n < 8) {
-            m = ((1 << n) - 1) << bit_pos;
-            vpushi(m), gen_op('&'); // X B V1
-            vpushv(vtop-1); // X B V1 B
-            vpushi(m & 0x80 ? ~m & 0x7f : ~m);
-            gen_op('&'); // X B V1 B1
-            gen_op('|'); // X B V2
-        }
-        vdup(), vtop[-1] = vtop[-2]; // X B B V2
-        vstore(), vpop(); // X B
-        bits += n, bit_size -= n, bit_pos = 0, o = 1;
-    } while (bit_size);
-    vpop(), vpop();
-}
-
-static int adjust_bf(SValue *sv, int bit_pos, int bit_size)
-{
-    int t;
-    if (0 == sv->type.ref)
-        return 0;
-    t = sv->type.ref->auxtype;
-    if (t != -1 && t != VT_STRUCT) {
-        sv->type.t = (sv->type.t & ~VT_BTYPE) | t;
-        sv->r = (sv->r & ~VT_LVAL_TYPE) | lvalue_type(sv->type.t);
-    }
-    return t;
-}
-
-/* store vtop a register belonging to class 'rc'. lvalues are
-   converted to values. Cannot be used if cannot be converted to
-   register value (such as structures). */
-ST_FUNC int gv(int rc)
-{
-    int r, bit_pos, bit_size, size, align, rc2;
-
-    /* NOTE: get_reg can modify vstack[] */
-    if (vtop->type.t & VT_BITFIELD) {
-        CType type;
-
-        bit_pos = BIT_POS(vtop->type.t);
-        bit_size = BIT_SIZE(vtop->type.t);
-        /* remove bit field info to avoid loops */
-        vtop->type.t &= ~VT_STRUCT_MASK;
-
-        type.ref = NULL;
-        type.t = vtop->type.t & VT_UNSIGNED;
-        if ((vtop->type.t & VT_BTYPE) == VT_BOOL)
-            type.t |= VT_UNSIGNED;
-
-        r = adjust_bf(vtop, bit_pos, bit_size);
-
-        if ((vtop->type.t & VT_BTYPE) == VT_LLONG)
-            type.t |= VT_LLONG;
-        else
-            type.t |= VT_INT;
-
-        if (r == VT_STRUCT) {
-            load_packed_bf(&type, bit_pos, bit_size);
-        } else {
-            int bits = (type.t & VT_BTYPE) == VT_LLONG ? 64 : 32;
-            /* cast to int to propagate signedness in following ops */
-            gen_cast(&type);
-            /* generate shifts */
-            vpushi(bits - (bit_pos + bit_size));
-            gen_op(TOK_SHL);
-            vpushi(bits - bit_size);
-            /* NOTE: transformed to SHR if unsigned */
-            gen_op(TOK_SAR);
-        }
-        r = gv(rc);
-    } else {
-        if (is_float(vtop->type.t) && 
-            (vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
-            unsigned long offset;
-            /* CPUs usually cannot use float constants, so we store them
-               generically in data segment */
-            size = type_size(&vtop->type, &align);
-            if (NODATA_WANTED)
-                size = 0, align = 1;
-            offset = section_add(data_section, size, align);
-            vpush_ref(&vtop->type, data_section, offset, size);
-	    vswap();
-	    init_putv(&vtop->type, data_section, offset);
-	    vtop->r |= VT_LVAL;
-        }
-#ifdef CONFIG_TCC_BCHECK
-        if (vtop->r & VT_MUSTBOUND) 
-            gbound();
-#endif
-
-        r = vtop->r & VT_VALMASK;
-        rc2 = (rc & RC_FLOAT) ? RC_FLOAT : RC_INT;
-#ifndef TCC_TARGET_ARM64
-        if (rc == RC_IRET)
-            rc2 = RC_LRET;
-#ifdef TCC_TARGET_X86_64
-        else if (rc == RC_FRET)
-            rc2 = RC_QRET;
-#endif
-#endif
-        /* need to reload if:
-           - constant
-           - lvalue (need to dereference pointer)
-           - already a register, but not in the right class */
-        if (r >= VT_CONST
-         || (vtop->r & VT_LVAL)
-         || !(reg_classes[r] & rc)
-#if PTR_SIZE == 8
-         || ((vtop->type.t & VT_BTYPE) == VT_QLONG && !(reg_classes[vtop->r2] & rc2))
-         || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT && !(reg_classes[vtop->r2] & rc2))
-#else
-         || ((vtop->type.t & VT_BTYPE) == VT_LLONG && !(reg_classes[vtop->r2] & rc2))
-#endif
-            )
-        {
-            r = get_reg(rc);
-#if PTR_SIZE == 8
-            if (((vtop->type.t & VT_BTYPE) == VT_QLONG) || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT)) {
-                int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE;
-#else
-            if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
-                int addr_type = VT_INT, load_size = 4, load_type = VT_INT;
-                unsigned long long ll;
-#endif
-                int r2, original_type;
-                original_type = vtop->type.t;
-                /* two register type load : expand to two words
-                   temporarily */
-#if PTR_SIZE == 4
-                if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
-                    /* load constant */
-                    ll = vtop->c.i;
-                    vtop->c.i = ll; /* first word */
-                    load(r, vtop);
-                    vtop->r = r; /* save register value */
-                    vpushi(ll >> 32); /* second word */
-                } else
-#endif
-                if (vtop->r & VT_LVAL) {
-                    /* We do not want to modifier the long long
-                       pointer here, so the safest (and less
-                       efficient) is to save all the other registers
-                       in the stack. XXX: totally inefficient. */
-               #if 0
-                    save_regs(1);
-               #else
-                    /* lvalue_save: save only if used further down the stack */
-                    save_reg_upstack(vtop->r, 1);
-               #endif
-                    /* load from memory */
-                    vtop->type.t = load_type;
-                    load(r, vtop);
-                    vdup();
-                    vtop[-1].r = r; /* save register value */
-                    /* increment pointer to get second word */
-                    vtop->type.t = addr_type;
-                    gaddrof();
-                    vpushi(load_size);
-                    gen_op('+');
-                    vtop->r |= VT_LVAL;
-                    vtop->type.t = load_type;
-                } else {
-                    /* move registers */
-                    load(r, vtop);
-                    vdup();
-                    vtop[-1].r = r; /* save register value */
-                    vtop->r = vtop[-1].r2;
-                }
-                /* Allocate second register. Here we rely on the fact that
-                   get_reg() tries first to free r2 of an SValue. */
-                r2 = get_reg(rc2);
-                load(r2, vtop);
-                vpop();
-                /* write second register */
-                vtop->r2 = r2;
-                vtop->type.t = original_type;
-            } else if ((vtop->r & VT_LVAL) && !is_float(vtop->type.t)) {
-                int t1, t;
-                /* lvalue of scalar type : need to use lvalue type
-                   because of possible cast */
-                t = vtop->type.t;
-                t1 = t;
-                /* compute memory access type */
-                if (vtop->r & VT_LVAL_BYTE)
-                    t = VT_BYTE;
-                else if (vtop->r & VT_LVAL_SHORT)
-                    t = VT_SHORT;
-                if (vtop->r & VT_LVAL_UNSIGNED)
-                    t |= VT_UNSIGNED;
-                vtop->type.t = t;
-                load(r, vtop);
-                /* restore wanted type */
-                vtop->type.t = t1;
-            } else {
-                /* one register type load */
-                load(r, vtop);
-            }
-        }
-        vtop->r = r;
-#ifdef TCC_TARGET_C67
-        /* uses register pairs for doubles */
-        if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) 
-            vtop->r2 = r+1;
-#endif
-    }
-    return r;
-}
-
-/* generate vtop[-1] and vtop[0] in resp. classes rc1 and rc2 */
-ST_FUNC void gv2(int rc1, int rc2)
-{
-    int v;
-
-    /* generate more generic register first. But VT_JMP or VT_CMP
-       values must be generated first in all cases to avoid possible
-       reload errors */
-    v = vtop[0].r & VT_VALMASK;
-    if (v != VT_CMP && (v & ~1) != VT_JMP && rc1 <= rc2) {
-        vswap();
-        gv(rc1);
-        vswap();
-        gv(rc2);
-        /* test if reload is needed for first register */
-        if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) {
-            vswap();
-            gv(rc1);
-            vswap();
-        }
-    } else {
-        gv(rc2);
-        vswap();
-        gv(rc1);
-        vswap();
-        /* test if reload is needed for first register */
-        if ((vtop[0].r & VT_VALMASK) >= VT_CONST) {
-            gv(rc2);
-        }
-    }
-}
-
-#ifndef TCC_TARGET_ARM64
-/* wrapper around RC_FRET to return a register by type */
-static int rc_fret(int t)
-{
-#ifdef TCC_TARGET_X86_64
-    if (t == VT_LDOUBLE) {
-        return RC_ST0;
-    }
-#endif
-    return RC_FRET;
-}
-#endif
-
-/* wrapper around REG_FRET to return a register by type */
-static int reg_fret(int t)
-{
-#ifdef TCC_TARGET_X86_64
-    if (t == VT_LDOUBLE) {
-        return TREG_ST0;
-    }
-#endif
-    return REG_FRET;
-}
-
-#if PTR_SIZE == 4
-/* expand 64bit on stack in two ints */
-static void lexpand(void)
-{
-    int u, v;
-    u = vtop->type.t & (VT_DEFSIGN | VT_UNSIGNED);
-    v = vtop->r & (VT_VALMASK | VT_LVAL);
-    if (v == VT_CONST) {
-        vdup();
-        vtop[0].c.i >>= 32;
-    } else if (v == (VT_LVAL|VT_CONST) || v == (VT_LVAL|VT_LOCAL)) {
-        vdup();
-        vtop[0].c.i += 4;
-    } else {
-        gv(RC_INT);
-        vdup();
-        vtop[0].r = vtop[-1].r2;
-        vtop[0].r2 = vtop[-1].r2 = VT_CONST;
-    }
-    vtop[0].type.t = vtop[-1].type.t = VT_INT | u;
-}
-#endif
-
-#ifdef TCC_TARGET_ARM
-/* expand long long on stack */
-ST_FUNC void lexpand_nr(void)
-{
-    int u,v;
-
-    u = vtop->type.t & (VT_DEFSIGN | VT_UNSIGNED);
-    vdup();
-    vtop->r2 = VT_CONST;
-    vtop->type.t = VT_INT | u;
-    v=vtop[-1].r & (VT_VALMASK | VT_LVAL);
-    if (v == VT_CONST) {
-      vtop[-1].c.i = vtop->c.i;
-      vtop->c.i = vtop->c.i >> 32;
-      vtop->r = VT_CONST;
-    } else if (v == (VT_LVAL|VT_CONST) || v == (VT_LVAL|VT_LOCAL)) {
-      vtop->c.i += 4;
-      vtop->r = vtop[-1].r;
-    } else if (v > VT_CONST) {
-      vtop--;
-      lexpand();
-    } else
-      vtop->r = vtop[-1].r2;
-    vtop[-1].r2 = VT_CONST;
-    vtop[-1].type.t = VT_INT | u;
-}
-#endif
-
-#if PTR_SIZE == 4
-/* build a long long from two ints */
-static void lbuild(int t)
-{
-    gv2(RC_INT, RC_INT);
-    vtop[-1].r2 = vtop[0].r;
-    vtop[-1].type.t = t;
-    vpop();
-}
-#endif
-
-/* convert stack entry to register and duplicate its value in another
-   register */
-static void gv_dup(void)
-{
-    int rc, t, r, r1;
-    SValue sv;
-
-    t = vtop->type.t;
-#if PTR_SIZE == 4
-    if ((t & VT_BTYPE) == VT_LLONG) {
-        if (t & VT_BITFIELD) {
-            gv(RC_INT);
-            t = vtop->type.t;
-        }
-        lexpand();
-        gv_dup();
-        vswap();
-        vrotb(3);
-        gv_dup();
-        vrotb(4);
-        /* stack: H L L1 H1 */
-        lbuild(t);
-        vrotb(3);
-        vrotb(3);
-        vswap();
-        lbuild(t);
-        vswap();
-    } else
-#endif
-    {
-        /* duplicate value */
-        rc = RC_INT;
-        sv.type.t = VT_INT;
-        if (is_float(t)) {
-            rc = RC_FLOAT;
-#ifdef TCC_TARGET_X86_64
-            if ((t & VT_BTYPE) == VT_LDOUBLE) {
-                rc = RC_ST0;
-            }
-#endif
-            sv.type.t = t;
-        }
-        r = gv(rc);
-        r1 = get_reg(rc);
-        sv.r = r;
-        sv.c.i = 0;
-        load(r1, &sv); /* move r to r1 */
-        vdup();
-        /* duplicates value */
-        if (r != r1)
-            vtop->r = r1;
-    }
-}
-
-/* Generate value test
- *
- * Generate a test for any value (jump, comparison and integers) */
-ST_FUNC int gvtst(int inv, int t)
-{
-    int v = vtop->r & VT_VALMASK;
-    if (v != VT_CMP && v != VT_JMP && v != VT_JMPI) {
-        vpushi(0);
-        gen_op(TOK_NE);
-    }
-    if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
-        /* constant jmp optimization */
-        if ((vtop->c.i != 0) != inv)
-            t = gjmp(t);
-        vtop--;
-        return t;
-    }
-    return gtst(inv, t);
-}
-
-#if PTR_SIZE == 4
-/* generate CPU independent (unsigned) long long operations */
-static void gen_opl(int op)
-{
-    int t, a, b, op1, c, i;
-    int func;
-    unsigned short reg_iret = REG_IRET;
-    unsigned short reg_lret = REG_LRET;
-    SValue tmp;
-
-    switch(op) {
-    case '/':
-    case TOK_PDIV:
-        func = TOK___divdi3;
-        goto gen_func;
-    case TOK_UDIV:
-        func = TOK___udivdi3;
-        goto gen_func;
-    case '%':
-        func = TOK___moddi3;
-        goto gen_mod_func;
-    case TOK_UMOD:
-        func = TOK___umoddi3;
-    gen_mod_func:
-#ifdef TCC_ARM_EABI
-        reg_iret = TREG_R2;
-        reg_lret = TREG_R3;
-#endif
-    gen_func:
-        /* call generic long long function */
-        vpush_global_sym(&func_old_type, func);
-        vrott(3);
-        gfunc_call(2);
-        vpushi(0);
-        vtop->r = reg_iret;
-        vtop->r2 = reg_lret;
-        break;
-    case '^':
-    case '&':
-    case '|':
-    case '*':
-    case '+':
-    case '-':
-        //pv("gen_opl A",0,2);
-        t = vtop->type.t;
-        vswap();
-        lexpand();
-        vrotb(3);
-        lexpand();
-        /* stack: L1 H1 L2 H2 */
-        tmp = vtop[0];
-        vtop[0] = vtop[-3];
-        vtop[-3] = tmp;
-        tmp = vtop[-2];
-        vtop[-2] = vtop[-3];
-        vtop[-3] = tmp;
-        vswap();
-        /* stack: H1 H2 L1 L2 */
-        //pv("gen_opl B",0,4);
-        if (op == '*') {
-            vpushv(vtop - 1);
-            vpushv(vtop - 1);
-            gen_op(TOK_UMULL);
-            lexpand();
-            /* stack: H1 H2 L1 L2 ML MH */
-            for(i=0;i<4;i++)
-                vrotb(6);
-            /* stack: ML MH H1 H2 L1 L2 */
-            tmp = vtop[0];
-            vtop[0] = vtop[-2];
-            vtop[-2] = tmp;
-            /* stack: ML MH H1 L2 H2 L1 */
-            gen_op('*');
-            vrotb(3);
-            vrotb(3);
-            gen_op('*');
-            /* stack: ML MH M1 M2 */
-            gen_op('+');
-            gen_op('+');
-        } else if (op == '+' || op == '-') {
-            /* XXX: add non carry method too (for MIPS or alpha) */
-            if (op == '+')
-                op1 = TOK_ADDC1;
-            else
-                op1 = TOK_SUBC1;
-            gen_op(op1);
-            /* stack: H1 H2 (L1 op L2) */
-            vrotb(3);
-            vrotb(3);
-            gen_op(op1 + 1); /* TOK_xxxC2 */
-        } else {
-            gen_op(op);
-            /* stack: H1 H2 (L1 op L2) */
-            vrotb(3);
-            vrotb(3);
-            /* stack: (L1 op L2) H1 H2 */
-            gen_op(op);
-            /* stack: (L1 op L2) (H1 op H2) */
-        }
-        /* stack: L H */
-        lbuild(t);
-        break;
-    case TOK_SAR:
-    case TOK_SHR:
-    case TOK_SHL:
-        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
-            t = vtop[-1].type.t;
-            vswap();
-            lexpand();
-            vrotb(3);
-            /* stack: L H shift */
-            c = (int)vtop->c.i;
-            /* constant: simpler */
-            /* NOTE: all comments are for SHL. the other cases are
-               done by swapping words */
-            vpop();
-            if (op != TOK_SHL)
-                vswap();
-            if (c >= 32) {
-                /* stack: L H */
-                vpop();
-                if (c > 32) {
-                    vpushi(c - 32);
-                    gen_op(op);
-                }
-                if (op != TOK_SAR) {
-                    vpushi(0);
-                } else {
-                    gv_dup();
-                    vpushi(31);
-                    gen_op(TOK_SAR);
-                }
-                vswap();
-            } else {
-                vswap();
-                gv_dup();
-                /* stack: H L L */
-                vpushi(c);
-                gen_op(op);
-                vswap();
-                vpushi(32 - c);
-                if (op == TOK_SHL)
-                    gen_op(TOK_SHR);
-                else
-                    gen_op(TOK_SHL);
-                vrotb(3);
-                /* stack: L L H */
-                vpushi(c);
-                if (op == TOK_SHL)
-                    gen_op(TOK_SHL);
-                else
-                    gen_op(TOK_SHR);
-                gen_op('|');
-            }
-            if (op != TOK_SHL)
-                vswap();
-            lbuild(t);
-        } else {
-            /* XXX: should provide a faster fallback on x86 ? */
-            switch(op) {
-            case TOK_SAR:
-                func = TOK___ashrdi3;
-                goto gen_func;
-            case TOK_SHR:
-                func = TOK___lshrdi3;
-                goto gen_func;
-            case TOK_SHL:
-                func = TOK___ashldi3;
-                goto gen_func;
-            }
-        }
-        break;
-    default:
-        /* compare operations */
-        t = vtop->type.t;
-        vswap();
-        lexpand();
-        vrotb(3);
-        lexpand();
-        /* stack: L1 H1 L2 H2 */
-        tmp = vtop[-1];
-        vtop[-1] = vtop[-2];
-        vtop[-2] = tmp;
-        /* stack: L1 L2 H1 H2 */
-        /* compare high */
-        op1 = op;
-        /* when values are equal, we need to compare low words. since
-           the jump is inverted, we invert the test too. */
-        if (op1 == TOK_LT)
-            op1 = TOK_LE;
-        else if (op1 == TOK_GT)
-            op1 = TOK_GE;
-        else if (op1 == TOK_ULT)
-            op1 = TOK_ULE;
-        else if (op1 == TOK_UGT)
-            op1 = TOK_UGE;
-        a = 0;
-        b = 0;
-        gen_op(op1);
-        if (op == TOK_NE) {
-            b = gvtst(0, 0);
-        } else {
-            a = gvtst(1, 0);
-            if (op != TOK_EQ) {
-                /* generate non equal test */
-                vpushi(TOK_NE);
-                vtop->r = VT_CMP;
-                b = gvtst(0, 0);
-            }
-        }
-        /* compare low. Always unsigned */
-        op1 = op;
-        if (op1 == TOK_LT)
-            op1 = TOK_ULT;
-        else if (op1 == TOK_LE)
-            op1 = TOK_ULE;
-        else if (op1 == TOK_GT)
-            op1 = TOK_UGT;
-        else if (op1 == TOK_GE)
-            op1 = TOK_UGE;
-        gen_op(op1);
-        a = gvtst(1, a);
-        gsym(b);
-        vseti(VT_JMPI, a);
-        break;
-    }
-}
-#endif
-
-static uint64_t gen_opic_sdiv(uint64_t a, uint64_t b)
-{
-    uint64_t x = (a >> 63 ? -a : a) / (b >> 63 ? -b : b);
-    return (a ^ b) >> 63 ? -x : x;
-}
-
-static int gen_opic_lt(uint64_t a, uint64_t b)
-{
-    return (a ^ (uint64_t)1 << 63) < (b ^ (uint64_t)1 << 63);
-}
-
-/* handle integer constant optimizations and various machine
-   independent opt */
-static void gen_opic(int op)
-{
-    SValue *v1 = vtop - 1;
-    SValue *v2 = vtop;
-    int t1 = v1->type.t & VT_BTYPE;
-    int t2 = v2->type.t & VT_BTYPE;
-    int c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
-    int c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
-    uint64_t l1 = c1 ? v1->c.i : 0;
-    uint64_t l2 = c2 ? v2->c.i : 0;
-    int shm = (t1 == VT_LLONG) ? 63 : 31;
-
-    if (t1 != VT_LLONG && (PTR_SIZE != 8 || t1 != VT_PTR))
-        l1 = ((uint32_t)l1 |
-              (v1->type.t & VT_UNSIGNED ? 0 : -(l1 & 0x80000000)));
-    if (t2 != VT_LLONG && (PTR_SIZE != 8 || t2 != VT_PTR))
-        l2 = ((uint32_t)l2 |
-              (v2->type.t & VT_UNSIGNED ? 0 : -(l2 & 0x80000000)));
-
-    if (c1 && c2) {
-        switch(op) {
-        case '+': l1 += l2; break;
-        case '-': l1 -= l2; break;
-        case '&': l1 &= l2; break;
-        case '^': l1 ^= l2; break;
-        case '|': l1 |= l2; break;
-        case '*': l1 *= l2; break;
-
-        case TOK_PDIV:
-        case '/':
-        case '%':
-        case TOK_UDIV:
-        case TOK_UMOD:
-            /* if division by zero, generate explicit division */
-            if (l2 == 0) {
-                if (const_wanted)
-                    tcc_error("division by zero in constant");
-                goto general_case;
-            }
-            switch(op) {
-            default: l1 = gen_opic_sdiv(l1, l2); break;
-            case '%': l1 = l1 - l2 * gen_opic_sdiv(l1, l2); break;
-            case TOK_UDIV: l1 = l1 / l2; break;
-            case TOK_UMOD: l1 = l1 % l2; break;
-            }
-            break;
-        case TOK_SHL: l1 <<= (l2 & shm); break;
-        case TOK_SHR: l1 >>= (l2 & shm); break;
-        case TOK_SAR:
-            l1 = (l1 >> 63) ? ~(~l1 >> (l2 & shm)) : l1 >> (l2 & shm);
-            break;
-            /* tests */
-        case TOK_ULT: l1 = l1 < l2; break;
-        case TOK_UGE: l1 = l1 >= l2; break;
-        case TOK_EQ: l1 = l1 == l2; break;
-        case TOK_NE: l1 = l1 != l2; break;
-        case TOK_ULE: l1 = l1 <= l2; break;
-        case TOK_UGT: l1 = l1 > l2; break;
-        case TOK_LT: l1 = gen_opic_lt(l1, l2); break;
-        case TOK_GE: l1 = !gen_opic_lt(l1, l2); break;
-        case TOK_LE: l1 = !gen_opic_lt(l2, l1); break;
-        case TOK_GT: l1 = gen_opic_lt(l2, l1); break;
-            /* logical */
-        case TOK_LAND: l1 = l1 && l2; break;
-        case TOK_LOR: l1 = l1 || l2; break;
-        default:
-            goto general_case;
-        }
-	if (t1 != VT_LLONG && (PTR_SIZE != 8 || t1 != VT_PTR))
-	    l1 = ((uint32_t)l1 |
-		(v1->type.t & VT_UNSIGNED ? 0 : -(l1 & 0x80000000)));
-        v1->c.i = l1;
-        vtop--;
-    } else {
-        /* if commutative ops, put c2 as constant */
-        if (c1 && (op == '+' || op == '&' || op == '^' || 
-                   op == '|' || op == '*')) {
-            vswap();
-            c2 = c1; //c = c1, c1 = c2, c2 = c;
-            l2 = l1; //l = l1, l1 = l2, l2 = l;
-        }
-        if (!const_wanted &&
-            c1 && ((l1 == 0 &&
-                    (op == TOK_SHL || op == TOK_SHR || op == TOK_SAR)) ||
-                   (l1 == -1 && op == TOK_SAR))) {
-            /* treat (0 << x), (0 >> x) and (-1 >> x) as constant */
-            vtop--;
-        } else if (!const_wanted &&
-                   c2 && ((l2 == 0 && (op == '&' || op == '*')) ||
-                          (op == '|' &&
-                            (l2 == -1 || (l2 == 0xFFFFFFFF && t2 != VT_LLONG))) ||
-                          (l2 == 1 && (op == '%' || op == TOK_UMOD)))) {
-            /* treat (x & 0), (x * 0), (x | -1) and (x % 1) as constant */
-            if (l2 == 1)
-                vtop->c.i = 0;
-            vswap();
-            vtop--;
-        } else if (c2 && (((op == '*' || op == '/' || op == TOK_UDIV ||
-                          op == TOK_PDIV) &&
-                           l2 == 1) ||
-                          ((op == '+' || op == '-' || op == '|' || op == '^' ||
-                            op == TOK_SHL || op == TOK_SHR || op == TOK_SAR) &&
-                           l2 == 0) ||
-                          (op == '&' &&
-                            (l2 == -1 || (l2 == 0xFFFFFFFF && t2 != VT_LLONG))))) {
-            /* filter out NOP operations like x*1, x-0, x&-1... */
-            vtop--;
-        } else if (c2 && (op == '*' || op == TOK_PDIV || op == TOK_UDIV)) {
-            /* try to use shifts instead of muls or divs */
-            if (l2 > 0 && (l2 & (l2 - 1)) == 0) {
-                int n = -1;
-                while (l2) {
-                    l2 >>= 1;
-                    n++;
-                }
-                vtop->c.i = n;
-                if (op == '*')
-                    op = TOK_SHL;
-                else if (op == TOK_PDIV)
-                    op = TOK_SAR;
-                else
-                    op = TOK_SHR;
-            }
-            goto general_case;
-        } else if (c2 && (op == '+' || op == '-') &&
-                   (((vtop[-1].r & (VT_VALMASK | VT_LVAL | VT_SYM)) == (VT_CONST | VT_SYM))
-                    || (vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_LOCAL)) {
-            /* symbol + constant case */
-            if (op == '-')
-                l2 = -l2;
-	    l2 += vtop[-1].c.i;
-	    /* The backends can't always deal with addends to symbols
-	       larger than +-1<<31.  Don't construct such.  */
-	    if ((int)l2 != l2)
-	        goto general_case;
-            vtop--;
-            vtop->c.i = l2;
-        } else {
-        general_case:
-                /* call low level op generator */
-                if (t1 == VT_LLONG || t2 == VT_LLONG ||
-                    (PTR_SIZE == 8 && (t1 == VT_PTR || t2 == VT_PTR)))
-                    gen_opl(op);
-                else
-                    gen_opi(op);
-        }
-    }
-}
-
-/* generate a floating point operation with constant propagation */
-static void gen_opif(int op)
-{
-    int c1, c2;
-    SValue *v1, *v2;
-#if defined _MSC_VER && defined _AMD64_
-    /* avoid bad optimization with f1 -= f2 for f1:-0.0, f2:0.0 */
-    volatile
-#endif
-    long double f1, f2;
-
-    v1 = vtop - 1;
-    v2 = vtop;
-    /* currently, we cannot do computations with forward symbols */
-    c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
-    c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
-    if (c1 && c2) {
-        if (v1->type.t == VT_FLOAT) {
-            f1 = v1->c.f;
-            f2 = v2->c.f;
-        } else if (v1->type.t == VT_DOUBLE) {
-            f1 = v1->c.d;
-            f2 = v2->c.d;
-        } else {
-            f1 = v1->c.ld;
-            f2 = v2->c.ld;
-        }
-
-        /* NOTE: we only do constant propagation if finite number (not
-           NaN or infinity) (ANSI spec) */
-        if (!ieee_finite(f1) || !ieee_finite(f2))
-            goto general_case;
-
-        switch(op) {
-        case '+': f1 += f2; break;
-        case '-': f1 -= f2; break;
-        case '*': f1 *= f2; break;
-        case '/': 
-            if (f2 == 0.0) {
-                if (const_wanted)
-                    tcc_error("division by zero in constant");
-                goto general_case;
-            }
-            f1 /= f2; 
-            break;
-            /* XXX: also handles tests ? */
-        default:
-            goto general_case;
-        }
-        /* XXX: overflow test ? */
-        if (v1->type.t == VT_FLOAT) {
-            v1->c.f = f1;
-        } else if (v1->type.t == VT_DOUBLE) {
-            v1->c.d = f1;
-        } else {
-            v1->c.ld = f1;
-        }
-        vtop--;
-    } else {
-    general_case:
-        gen_opf(op);
-    }
-}
-
-static int pointed_size(CType *type)
-{
-    int align;
-    return type_size(pointed_type(type), &align);
-}
-
-static void vla_runtime_pointed_size(CType *type)
-{
-    int align;
-    vla_runtime_type_size(pointed_type(type), &align);
-}
-
-static inline int is_null_pointer(SValue *p)
-{
-    if ((p->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
-        return 0;
-    return ((p->type.t & VT_BTYPE) == VT_INT && (uint32_t)p->c.i == 0) ||
-        ((p->type.t & VT_BTYPE) == VT_LLONG && p->c.i == 0) ||
-        ((p->type.t & VT_BTYPE) == VT_PTR &&
-         (PTR_SIZE == 4 ? (uint32_t)p->c.i == 0 : p->c.i == 0));
-}
-
-static inline int is_integer_btype(int bt)
-{
-    return (bt == VT_BYTE || bt == VT_SHORT || 
-            bt == VT_INT || bt == VT_LLONG);
-}
-
-/* check types for comparison or subtraction of pointers */
-static void check_comparison_pointer_types(SValue *p1, SValue *p2, int op)
-{
-    CType *type1, *type2, tmp_type1, tmp_type2;
-    int bt1, bt2;
-    
-    /* null pointers are accepted for all comparisons as gcc */
-    if (is_null_pointer(p1) || is_null_pointer(p2))
-        return;
-    type1 = &p1->type;
-    type2 = &p2->type;
-    bt1 = type1->t & VT_BTYPE;
-    bt2 = type2->t & VT_BTYPE;
-    /* accept comparison between pointer and integer with a warning */
-    if ((is_integer_btype(bt1) || is_integer_btype(bt2)) && op != '-') {
-        if (op != TOK_LOR && op != TOK_LAND )
-            tcc_warning("comparison between pointer and integer");
-        return;
-    }
-
-    /* both must be pointers or implicit function pointers */
-    if (bt1 == VT_PTR) {
-        type1 = pointed_type(type1);
-    } else if (bt1 != VT_FUNC) 
-        goto invalid_operands;
-
-    if (bt2 == VT_PTR) {
-        type2 = pointed_type(type2);
-    } else if (bt2 != VT_FUNC) { 
-    invalid_operands:
-        tcc_error("invalid operands to binary %s", get_tok_str(op, NULL));
-    }
-    if ((type1->t & VT_BTYPE) == VT_VOID || 
-        (type2->t & VT_BTYPE) == VT_VOID)
-        return;
-    tmp_type1 = *type1;
-    tmp_type2 = *type2;
-    tmp_type1.t &= ~(VT_DEFSIGN | VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE);
-    tmp_type2.t &= ~(VT_DEFSIGN | VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE);
-    if (!is_compatible_types(&tmp_type1, &tmp_type2)) {
-        /* gcc-like error if '-' is used */
-        if (op == '-')
-            goto invalid_operands;
-        else
-            tcc_warning("comparison of distinct pointer types lacks a cast");
-    }
-}
-
-/* generic gen_op: handles types problems */
-ST_FUNC void gen_op(int op)
-{
-    int u, t1, t2, bt1, bt2, t;
-    CType type1;
-
-redo:
-    t1 = vtop[-1].type.t;
-    t2 = vtop[0].type.t;
-    bt1 = t1 & VT_BTYPE;
-    bt2 = t2 & VT_BTYPE;
-        
-    if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) {
-        tcc_error("operation on a struct");
-    } else if (bt1 == VT_FUNC || bt2 == VT_FUNC) {
-	if (bt2 == VT_FUNC) {
-	    mk_pointer(&vtop->type);
-	    gaddrof();
-	}
-	if (bt1 == VT_FUNC) {
-	    vswap();
-	    mk_pointer(&vtop->type);
-	    gaddrof();
-	    vswap();
-	}
-	goto redo;
-    } else if (bt1 == VT_PTR || bt2 == VT_PTR) {
-        /* at least one operand is a pointer */
-        /* relational op: must be both pointers */
-        if (op >= TOK_ULT && op <= TOK_LOR) {
-            check_comparison_pointer_types(vtop - 1, vtop, op);
-            /* pointers are handled are unsigned */
-#if PTR_SIZE == 8
-            t = VT_LLONG | VT_UNSIGNED;
-#else
-            t = VT_INT | VT_UNSIGNED;
-#endif
-            goto std_op;
-        }
-        /* if both pointers, then it must be the '-' op */
-        if (bt1 == VT_PTR && bt2 == VT_PTR) {
-            if (op != '-')
-                tcc_error("cannot use pointers here");
-            check_comparison_pointer_types(vtop - 1, vtop, op);
-            /* XXX: check that types are compatible */
-            if (vtop[-1].type.t & VT_VLA) {
-                vla_runtime_pointed_size(&vtop[-1].type);
-            } else {
-                vpushi(pointed_size(&vtop[-1].type));
-            }
-            vrott(3);
-            gen_opic(op);
-            vtop->type.t = ptrdiff_type.t;
-            vswap();
-            gen_op(TOK_PDIV);
-        } else {
-            /* exactly one pointer : must be '+' or '-'. */
-            if (op != '-' && op != '+')
-                tcc_error("cannot use pointers here");
-            /* Put pointer as first operand */
-            if (bt2 == VT_PTR) {
-                vswap();
-                t = t1, t1 = t2, t2 = t;
-            }
-#if PTR_SIZE == 4
-            if ((vtop[0].type.t & VT_BTYPE) == VT_LLONG)
-                /* XXX: truncate here because gen_opl can't handle ptr + long long */
-                gen_cast_s(VT_INT);
-#endif
-            type1 = vtop[-1].type;
-            type1.t &= ~VT_ARRAY;
-            if (vtop[-1].type.t & VT_VLA)
-                vla_runtime_pointed_size(&vtop[-1].type);
-            else {
-                u = pointed_size(&vtop[-1].type);
-                if (u < 0)
-                    tcc_error("unknown array element size");
-#if PTR_SIZE == 8
-                vpushll(u);
-#else
-                /* XXX: cast to int ? (long long case) */
-                vpushi(u);
-#endif
-            }
-            gen_op('*');
-#if 0
-/* #ifdef CONFIG_TCC_BCHECK
-    The main reason to removing this code:
-	#include <stdio.h>
-	int main ()
-	{
-	    int v[10];
-	    int i = 10;
-	    int j = 9;
-	    fprintf(stderr, "v+i-j  = %p\n", v+i-j);
-	    fprintf(stderr, "v+(i-j)  = %p\n", v+(i-j));
-	}
-    When this code is on. then the output looks like 
-	v+i-j = 0xfffffffe
-	v+(i-j) = 0xbff84000
-    */
-            /* if evaluating constant expression, no code should be
-               generated, so no bound check */
-            if (tcc_state->do_bounds_check && !const_wanted) {
-                /* if bounded pointers, we generate a special code to
-                   test bounds */
-                if (op == '-') {
-                    vpushi(0);
-                    vswap();
-                    gen_op('-');
-                }
-                gen_bounded_ptr_add();
-            } else
-#endif
-            {
-                gen_opic(op);
-            }
-            /* put again type if gen_opic() swaped operands */
-            vtop->type = type1;
-        }
-    } else if (is_float(bt1) || is_float(bt2)) {
-        /* compute bigger type and do implicit casts */
-        if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) {
-            t = VT_LDOUBLE;
-        } else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) {
-            t = VT_DOUBLE;
-        } else {
-            t = VT_FLOAT;
-        }
-        /* floats can only be used for a few operations */
-        if (op != '+' && op != '-' && op != '*' && op != '/' &&
-            (op < TOK_ULT || op > TOK_GT))
-            tcc_error("invalid operands for binary operation");
-        goto std_op;
-    } else if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL) {
-        t = bt1 == VT_LLONG ? VT_LLONG : VT_INT;
-        if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (t | VT_UNSIGNED))
-          t |= VT_UNSIGNED;
-        t |= (VT_LONG & t1);
-        goto std_op;
-    } else if (bt1 == VT_LLONG || bt2 == VT_LLONG) {
-        /* cast to biggest op */
-        t = VT_LLONG | VT_LONG;
-        if (bt1 == VT_LLONG)
-            t &= t1;
-        if (bt2 == VT_LLONG)
-            t &= t2;
-        /* convert to unsigned if it does not fit in a long long */
-        if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED) ||
-            (t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED))
-            t |= VT_UNSIGNED;
-        goto std_op;
-    } else {
-        /* integer operations */
-        t = VT_INT | (VT_LONG & (t1 | t2));
-        /* convert to unsigned if it does not fit in an integer */
-        if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED) ||
-            (t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED))
-            t |= VT_UNSIGNED;
-    std_op:
-        /* XXX: currently, some unsigned operations are explicit, so
-           we modify them here */
-        if (t & VT_UNSIGNED) {
-            if (op == TOK_SAR)
-                op = TOK_SHR;
-            else if (op == '/')
-                op = TOK_UDIV;
-            else if (op == '%')
-                op = TOK_UMOD;
-            else if (op == TOK_LT)
-                op = TOK_ULT;
-            else if (op == TOK_GT)
-                op = TOK_UGT;
-            else if (op == TOK_LE)
-                op = TOK_ULE;
-            else if (op == TOK_GE)
-                op = TOK_UGE;
-        }
-        vswap();
-        type1.t = t;
-        type1.ref = NULL;
-        gen_cast(&type1);
-        vswap();
-        /* special case for shifts and long long: we keep the shift as
-           an integer */
-        if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL)
-            type1.t = VT_INT;
-        gen_cast(&type1);
-        if (is_float(t))
-            gen_opif(op);
-        else
-            gen_opic(op);
-        if (op >= TOK_ULT && op <= TOK_GT) {
-            /* relational op: the result is an int */
-            vtop->type.t = VT_INT;
-        } else {
-            vtop->type.t = t;
-        }
-    }
-    // Make sure that we have converted to an rvalue:
-    if (vtop->r & VT_LVAL)
-        gv(is_float(vtop->type.t & VT_BTYPE) ? RC_FLOAT : RC_INT);
-}
-
-#ifndef TCC_TARGET_ARM
-/* generic itof for unsigned long long case */
-static void gen_cvt_itof1(int t)
-{
-#ifdef TCC_TARGET_ARM64
-    gen_cvt_itof(t);
-#else
-    if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == 
-        (VT_LLONG | VT_UNSIGNED)) {
-
-        if (t == VT_FLOAT)
-            vpush_global_sym(&func_old_type, TOK___floatundisf);
-#if LDOUBLE_SIZE != 8
-        else if (t == VT_LDOUBLE)
-            vpush_global_sym(&func_old_type, TOK___floatundixf);
-#endif
-        else
-            vpush_global_sym(&func_old_type, TOK___floatundidf);
-        vrott(2);
-        gfunc_call(1);
-        vpushi(0);
-        vtop->r = reg_fret(t);
-    } else {
-        gen_cvt_itof(t);
-    }
-#endif
-}
-#endif
-
-/* generic ftoi for unsigned long long case */
-static void gen_cvt_ftoi1(int t)
-{
-#ifdef TCC_TARGET_ARM64
-    gen_cvt_ftoi(t);
-#else
-    int st;
-
-    if (t == (VT_LLONG | VT_UNSIGNED)) {
-        /* not handled natively */
-        st = vtop->type.t & VT_BTYPE;
-        if (st == VT_FLOAT)
-            vpush_global_sym(&func_old_type, TOK___fixunssfdi);
-#if LDOUBLE_SIZE != 8
-        else if (st == VT_LDOUBLE)
-            vpush_global_sym(&func_old_type, TOK___fixunsxfdi);
-#endif
-        else
-            vpush_global_sym(&func_old_type, TOK___fixunsdfdi);
-        vrott(2);
-        gfunc_call(1);
-        vpushi(0);
-        vtop->r = REG_IRET;
-        vtop->r2 = REG_LRET;
-    } else {
-        gen_cvt_ftoi(t);
-    }
-#endif
-}
-
-/* force char or short cast */
-static void force_charshort_cast(int t)
-{
-    int bits, dbt;
-
-    /* cannot cast static initializers */
-    if (STATIC_DATA_WANTED)
-	return;
-
-    dbt = t & VT_BTYPE;
-    /* XXX: add optimization if lvalue : just change type and offset */
-    if (dbt == VT_BYTE)
-        bits = 8;
-    else
-        bits = 16;
-    if (t & VT_UNSIGNED) {
-        vpushi((1 << bits) - 1);
-        gen_op('&');
-    } else {
-        if ((vtop->type.t & VT_BTYPE) == VT_LLONG)
-            bits = 64 - bits;
-        else
-            bits = 32 - bits;
-        vpushi(bits);
-        gen_op(TOK_SHL);
-        /* result must be signed or the SAR is converted to an SHL
-           This was not the case when "t" was a signed short
-           and the last value on the stack was an unsigned int */
-        vtop->type.t &= ~VT_UNSIGNED;
-        vpushi(bits);
-        gen_op(TOK_SAR);
-    }
-}
-
-/* cast 'vtop' to 'type'. Casting to bitfields is forbidden. */
-static void gen_cast_s(int t)
-{
-    CType type;
-    type.t = t;
-    type.ref = NULL;
-    gen_cast(&type);
-}
-
-static void gen_cast(CType *type)
-{
-    int sbt, dbt, sf, df, c, p;
-
-    /* special delayed cast for char/short */
-    /* XXX: in some cases (multiple cascaded casts), it may still
-       be incorrect */
-    if (vtop->r & VT_MUSTCAST) {
-        vtop->r &= ~VT_MUSTCAST;
-        force_charshort_cast(vtop->type.t);
-    }
-
-    /* bitfields first get cast to ints */
-    if (vtop->type.t & VT_BITFIELD) {
-        gv(RC_INT);
-    }
-
-    dbt = type->t & (VT_BTYPE | VT_UNSIGNED);
-    sbt = vtop->type.t & (VT_BTYPE | VT_UNSIGNED);
-
-    if (sbt != dbt) {
-        sf = is_float(sbt);
-        df = is_float(dbt);
-        c = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
-        p = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == (VT_CONST | VT_SYM);
-#if !defined TCC_IS_NATIVE && !defined TCC_IS_NATIVE_387
-        c &= dbt != VT_LDOUBLE;
-#endif
-        if (c) {
-            /* constant case: we can do it now */
-            /* XXX: in ISOC, cannot do it if error in convert */
-            if (sbt == VT_FLOAT)
-                vtop->c.ld = vtop->c.f;
-            else if (sbt == VT_DOUBLE)
-                vtop->c.ld = vtop->c.d;
-
-            if (df) {
-                if ((sbt & VT_BTYPE) == VT_LLONG) {
-                    if ((sbt & VT_UNSIGNED) || !(vtop->c.i >> 63))
-                        vtop->c.ld = vtop->c.i;
-                    else
-                        vtop->c.ld = -(long double)-vtop->c.i;
-                } else if(!sf) {
-                    if ((sbt & VT_UNSIGNED) || !(vtop->c.i >> 31))
-                        vtop->c.ld = (uint32_t)vtop->c.i;
-                    else
-                        vtop->c.ld = -(long double)-(uint32_t)vtop->c.i;
-                }
-
-                if (dbt == VT_FLOAT)
-                    vtop->c.f = (float)vtop->c.ld;
-                else if (dbt == VT_DOUBLE)
-                    vtop->c.d = (double)vtop->c.ld;
-            } else if (sf && dbt == (VT_LLONG|VT_UNSIGNED)) {
-                vtop->c.i = vtop->c.ld;
-            } else if (sf && dbt == VT_BOOL) {
-                vtop->c.i = (vtop->c.ld != 0);
-            } else {
-                if(sf)
-                    vtop->c.i = vtop->c.ld;
-                else if (sbt == (VT_LLONG|VT_UNSIGNED))
-                    ;
-                else if (sbt & VT_UNSIGNED)
-                    vtop->c.i = (uint32_t)vtop->c.i;
-#if PTR_SIZE == 8
-                else if (sbt == VT_PTR)
-                    ;
-#endif
-                else if (sbt != VT_LLONG)
-                    vtop->c.i = ((uint32_t)vtop->c.i |
-                                  -(vtop->c.i & 0x80000000));
-
-                if (dbt == (VT_LLONG|VT_UNSIGNED))
-                    ;
-                else if (dbt == VT_BOOL)
-                    vtop->c.i = (vtop->c.i != 0);
-#if PTR_SIZE == 8
-                else if (dbt == VT_PTR)
-                    ;
-#endif
-                else if (dbt != VT_LLONG) {
-                    uint32_t m = ((dbt & VT_BTYPE) == VT_BYTE ? 0xff :
-                                  (dbt & VT_BTYPE) == VT_SHORT ? 0xffff :
-                                  0xffffffff);
-                    vtop->c.i &= m;
-                    if (!(dbt & VT_UNSIGNED))
-                        vtop->c.i |= -(vtop->c.i & ((m >> 1) + 1));
-                }
-            }
-        } else if (p && dbt == VT_BOOL) {
-            vtop->r = VT_CONST;
-            vtop->c.i = 1;
-        } else {
-            /* non constant case: generate code */
-            if (sf && df) {
-                /* convert from fp to fp */
-                gen_cvt_ftof(dbt);
-            } else if (df) {
-                /* convert int to fp */
-                gen_cvt_itof1(dbt);
-            } else if (sf) {
-                /* convert fp to int */
-                if (dbt == VT_BOOL) {
-                     vpushi(0);
-                     gen_op(TOK_NE);
-                } else {
-                    /* we handle char/short/etc... with generic code */
-                    if (dbt != (VT_INT | VT_UNSIGNED) &&
-                        dbt != (VT_LLONG | VT_UNSIGNED) &&
-                        dbt != VT_LLONG)
-                        dbt = VT_INT;
-                    gen_cvt_ftoi1(dbt);
-                    if (dbt == VT_INT && (type->t & (VT_BTYPE | VT_UNSIGNED)) != dbt) {
-                        /* additional cast for char/short... */
-                        vtop->type.t = dbt;
-                        gen_cast(type);
-                    }
-                }
-#if PTR_SIZE == 4
-            } else if ((dbt & VT_BTYPE) == VT_LLONG) {
-                if ((sbt & VT_BTYPE) != VT_LLONG) {
-                    /* scalar to long long */
-                    /* machine independent conversion */
-                    gv(RC_INT);
-                    /* generate high word */
-                    if (sbt == (VT_INT | VT_UNSIGNED)) {
-                        vpushi(0);
-                        gv(RC_INT);
-                    } else {
-                        if (sbt == VT_PTR) {
-                            /* cast from pointer to int before we apply
-                               shift operation, which pointers don't support*/
-                            gen_cast_s(VT_INT);
-                        }
-                        gv_dup();
-                        vpushi(31);
-                        gen_op(TOK_SAR);
-                    }
-                    /* patch second register */
-                    vtop[-1].r2 = vtop->r;
-                    vpop();
-                }
-#else
-            } else if ((dbt & VT_BTYPE) == VT_LLONG ||
-                       (dbt & VT_BTYPE) == VT_PTR ||
-                       (dbt & VT_BTYPE) == VT_FUNC) {
-                if ((sbt & VT_BTYPE) != VT_LLONG &&
-                    (sbt & VT_BTYPE) != VT_PTR &&
-                    (sbt & VT_BTYPE) != VT_FUNC) {
-                    /* need to convert from 32bit to 64bit */
-                    gv(RC_INT);
-                    if (sbt != (VT_INT | VT_UNSIGNED)) {
-#if defined(TCC_TARGET_ARM64)
-                        gen_cvt_sxtw();
-#elif defined(TCC_TARGET_X86_64)
-                        int r = gv(RC_INT);
-                        /* x86_64 specific: movslq */
-                        o(0x6348);
-                        o(0xc0 + (REG_VALUE(r) << 3) + REG_VALUE(r));
-#else
-#error
-#endif
-                    }
-                }
-#endif
-            } else if (dbt == VT_BOOL) {
-                /* scalar to bool */
-                vpushi(0);
-                gen_op(TOK_NE);
-            } else if ((dbt & VT_BTYPE) == VT_BYTE || 
-                       (dbt & VT_BTYPE) == VT_SHORT) {
-                if (sbt == VT_PTR) {
-                    vtop->type.t = VT_INT;
-                    tcc_warning("nonportable conversion from pointer to char/short");
-                }
-                force_charshort_cast(dbt);
-#if PTR_SIZE == 4
-            } else if ((dbt & VT_BTYPE) == VT_INT) {
-                /* scalar to int */
-                if ((sbt & VT_BTYPE) == VT_LLONG) {
-                    /* from long long: just take low order word */
-                    lexpand();
-                    vpop();
-                } 
-                /* if lvalue and single word type, nothing to do because
-                   the lvalue already contains the real type size (see
-                   VT_LVAL_xxx constants) */
-#endif
-            }
-        }
-    } else if ((dbt & VT_BTYPE) == VT_PTR && !(vtop->r & VT_LVAL)) {
-        /* if we are casting between pointer types,
-           we must update the VT_LVAL_xxx size */
-        vtop->r = (vtop->r & ~VT_LVAL_TYPE)
-                  | (lvalue_type(type->ref->type.t) & VT_LVAL_TYPE);
-    }
-    vtop->type = *type;
-}
-
-/* return type size as known at compile time. Put alignment at 'a' */
-ST_FUNC int type_size(CType *type, int *a)
-{
-    Sym *s;
-    int bt;
-
-    bt = type->t & VT_BTYPE;
-    if (bt == VT_STRUCT) {
-        /* struct/union */
-        s = type->ref;
-        *a = s->r;
-        return s->c;
-    } else if (bt == VT_PTR) {
-        if (type->t & VT_ARRAY) {
-            int ts;
-
-            s = type->ref;
-            ts = type_size(&s->type, a);
-
-            if (ts < 0 && s->c < 0)
-                ts = -ts;
-
-            return ts * s->c;
-        } else {
-            *a = PTR_SIZE;
-            return PTR_SIZE;
-        }
-    } else if (IS_ENUM(type->t) && type->ref->c == -1) {
-        return -1; /* incomplete enum */
-    } else if (bt == VT_LDOUBLE) {
-        *a = LDOUBLE_ALIGN;
-        return LDOUBLE_SIZE;
-    } else if (bt == VT_DOUBLE || bt == VT_LLONG) {
-#ifdef TCC_TARGET_I386
-#ifdef TCC_TARGET_PE
-        *a = 8;
-#else
-        *a = 4;
-#endif
-#elif defined(TCC_TARGET_ARM)
-#ifdef TCC_ARM_EABI
-        *a = 8; 
-#else
-        *a = 4;
-#endif
-#else
-        *a = 8;
-#endif
-        return 8;
-    } else if (bt == VT_INT || bt == VT_FLOAT) {
-        *a = 4;
-        return 4;
-    } else if (bt == VT_SHORT) {
-        *a = 2;
-        return 2;
-    } else if (bt == VT_QLONG || bt == VT_QFLOAT) {
-        *a = 8;
-        return 16;
-    } else {
-        /* char, void, function, _Bool */
-        *a = 1;
-        return 1;
-    }
-}
-
-/* push type size as known at runtime time on top of value stack. Put
-   alignment at 'a' */
-ST_FUNC void vla_runtime_type_size(CType *type, int *a)
-{
-    if (type->t & VT_VLA) {
-        type_size(&type->ref->type, a);
-        vset(&int_type, VT_LOCAL|VT_LVAL, type->ref->c);
-    } else {
-        vpushi(type_size(type, a));
-    }
-}
-
-static void vla_sp_restore(void) {
-    if (vlas_in_scope) {
-        gen_vla_sp_restore(vla_sp_loc);
-    }
-}
-
-static void vla_sp_restore_root(void) {
-    if (vlas_in_scope) {
-        gen_vla_sp_restore(vla_sp_root_loc);
-    }
-}
-
-/* return the pointed type of t */
-static inline CType *pointed_type(CType *type)
-{
-    return &type->ref->type;
-}
-
-/* modify type so that its it is a pointer to type. */
-ST_FUNC void mk_pointer(CType *type)
-{
-    Sym *s;
-    s = sym_push(SYM_FIELD, type, 0, -1);
-    type->t = VT_PTR | (type->t & VT_STORAGE);
-    type->ref = s;
-}
-
-/* compare function types. OLD functions match any new functions */
-static int is_compatible_func(CType *type1, CType *type2)
-{
-    Sym *s1, *s2;
-
-    s1 = type1->ref;
-    s2 = type2->ref;
-    if (!is_compatible_types(&s1->type, &s2->type))
-        return 0;
-    /* check func_call */
-    if (s1->f.func_call != s2->f.func_call)
-        return 0;
-    /* XXX: not complete */
-    if (s1->f.func_type == FUNC_OLD || s2->f.func_type == FUNC_OLD)
-        return 1;
-    if (s1->f.func_type != s2->f.func_type)
-        return 0;
-    while (s1 != NULL) {
-        if (s2 == NULL)
-            return 0;
-        if (!is_compatible_unqualified_types(&s1->type, &s2->type))
-            return 0;
-        s1 = s1->next;
-        s2 = s2->next;
-    }
-    if (s2)
-        return 0;
-    return 1;
-}
-
-/* return true if type1 and type2 are the same.  If unqualified is
-   true, qualifiers on the types are ignored.
-
-   - enums are not checked as gcc __builtin_types_compatible_p () 
- */
-static int compare_types(CType *type1, CType *type2, int unqualified)
-{
-    int bt1, t1, t2;
-
-    t1 = type1->t & VT_TYPE;
-    t2 = type2->t & VT_TYPE;
-    if (unqualified) {
-        /* strip qualifiers before comparing */
-        t1 &= ~(VT_CONSTANT | VT_VOLATILE);
-        t2 &= ~(VT_CONSTANT | VT_VOLATILE);
-    }
-
-    /* Default Vs explicit signedness only matters for char */
-    if ((t1 & VT_BTYPE) != VT_BYTE) {
-        t1 &= ~VT_DEFSIGN;
-        t2 &= ~VT_DEFSIGN;
-    }
-    /* XXX: bitfields ? */
-    if (t1 != t2)
-        return 0;
-    /* test more complicated cases */
-    bt1 = t1 & VT_BTYPE;
-    if (bt1 == VT_PTR) {
-        type1 = pointed_type(type1);
-        type2 = pointed_type(type2);
-        return is_compatible_types(type1, type2);
-    } else if (bt1 == VT_STRUCT) {
-        return (type1->ref == type2->ref);
-    } else if (bt1 == VT_FUNC) {
-        return is_compatible_func(type1, type2);
-    } else {
-        return 1;
-    }
-}
-
-/* return true if type1 and type2 are exactly the same (including
-   qualifiers). 
-*/
-static int is_compatible_types(CType *type1, CType *type2)
-{
-    return compare_types(type1,type2,0);
-}
-
-/* return true if type1 and type2 are the same (ignoring qualifiers).
-*/
-static int is_compatible_unqualified_types(CType *type1, CType *type2)
-{
-    return compare_types(type1,type2,1);
-}
-
-/* print a type. If 'varstr' is not NULL, then the variable is also
-   printed in the type */
-/* XXX: union */
-/* XXX: add array and function pointers */
-static void type_to_str(char *buf, int buf_size, 
-                 CType *type, const char *varstr)
-{
-    int bt, v, t;
-    Sym *s, *sa;
-    char buf1[256];
-    const char *tstr;
-
-    t = type->t;
-    bt = t & VT_BTYPE;
-    buf[0] = '\0';
-
-    if (t & VT_EXTERN)
-        pstrcat(buf, buf_size, "extern ");
-    if (t & VT_STATIC)
-        pstrcat(buf, buf_size, "static ");
-    if (t & VT_TYPEDEF)
-        pstrcat(buf, buf_size, "typedef ");
-    if (t & VT_INLINE)
-        pstrcat(buf, buf_size, "inline ");
-    if (t & VT_VOLATILE)
-        pstrcat(buf, buf_size, "volatile ");
-    if (t & VT_CONSTANT)
-        pstrcat(buf, buf_size, "const ");
-
-    if (((t & VT_DEFSIGN) && bt == VT_BYTE)
-        || ((t & VT_UNSIGNED)
-            && (bt == VT_SHORT || bt == VT_INT || bt == VT_LLONG)
-            && !IS_ENUM(t)
-            ))
-        pstrcat(buf, buf_size, (t & VT_UNSIGNED) ? "unsigned " : "signed ");
-
-    buf_size -= strlen(buf);
-    buf += strlen(buf);
-
-    switch(bt) {
-    case VT_VOID:
-        tstr = "void";
-        goto add_tstr;
-    case VT_BOOL:
-        tstr = "_Bool";
-        goto add_tstr;
-    case VT_BYTE:
-        tstr = "char";
-        goto add_tstr;
-    case VT_SHORT:
-        tstr = "short";
-        goto add_tstr;
-    case VT_INT:
-        tstr = "int";
-        goto maybe_long;
-    case VT_LLONG:
-        tstr = "long long";
-    maybe_long:
-        if (t & VT_LONG)
-            tstr = "long";
-        if (!IS_ENUM(t))
-            goto add_tstr;
-        tstr = "enum ";
-        goto tstruct;
-    case VT_FLOAT:
-        tstr = "float";
-        goto add_tstr;
-    case VT_DOUBLE:
-        tstr = "double";
-        goto add_tstr;
-    case VT_LDOUBLE:
-        tstr = "long double";
-    add_tstr:
-        pstrcat(buf, buf_size, tstr);
-        break;
-    case VT_STRUCT:
-        tstr = "struct ";
-        if (IS_UNION(t))
-            tstr = "union ";
-    tstruct:
-        pstrcat(buf, buf_size, tstr);
-        v = type->ref->v & ~SYM_STRUCT;
-        if (v >= SYM_FIRST_ANOM)
-            pstrcat(buf, buf_size, "<anonymous>");
-        else
-            pstrcat(buf, buf_size, get_tok_str(v, NULL));
-        break;
-    case VT_FUNC:
-        s = type->ref;
-        type_to_str(buf, buf_size, &s->type, varstr);
-        pstrcat(buf, buf_size, "(");
-        sa = s->next;
-        while (sa != NULL) {
-            type_to_str(buf1, sizeof(buf1), &sa->type, NULL);
-            pstrcat(buf, buf_size, buf1);
-            sa = sa->next;
-            if (sa)
-                pstrcat(buf, buf_size, ", ");
-        }
-        pstrcat(buf, buf_size, ")");
-        goto no_var;
-    case VT_PTR:
-        s = type->ref;
-        if (t & VT_ARRAY) {
-            snprintf(buf1, sizeof(buf1), "%s[%d]", varstr ? varstr : "", s->c);
-            type_to_str(buf, buf_size, &s->type, buf1);
-            goto no_var;
-        }
-        pstrcpy(buf1, sizeof(buf1), "*");
-        if (t & VT_CONSTANT)
-            pstrcat(buf1, buf_size, "const ");
-        if (t & VT_VOLATILE)
-            pstrcat(buf1, buf_size, "volatile ");
-        if (varstr)
-            pstrcat(buf1, sizeof(buf1), varstr);
-        type_to_str(buf, buf_size, &s->type, buf1);
-        goto no_var;
-    }
-    if (varstr) {
-        pstrcat(buf, buf_size, " ");
-        pstrcat(buf, buf_size, varstr);
-    }
- no_var: ;
-}
-
-/* verify type compatibility to store vtop in 'dt' type, and generate
-   casts if needed. */
-static void gen_assign_cast(CType *dt)
-{
-    CType *st, *type1, *type2;
-    char buf1[256], buf2[256];
-    int dbt, sbt;
-
-    st = &vtop->type; /* source type */
-    dbt = dt->t & VT_BTYPE;
-    sbt = st->t & VT_BTYPE;
-    if (sbt == VT_VOID || dbt == VT_VOID) {
-	if (sbt == VT_VOID && dbt == VT_VOID)
-	    ; /*
-	      It is Ok if both are void
-	      A test program:
-	        void func1() {}
-		void func2() {
-		  return func1();
-		}
-	      gcc accepts this program
-	      */
-	else
-    	    tcc_error("cannot cast from/to void");
-    }
-    if (dt->t & VT_CONSTANT)
-        tcc_warning("assignment of read-only location");
-    switch(dbt) {
-    case VT_PTR:
-        /* special cases for pointers */
-        /* '0' can also be a pointer */
-        if (is_null_pointer(vtop))
-            goto type_ok;
-        /* accept implicit pointer to integer cast with warning */
-        if (is_integer_btype(sbt)) {
-            tcc_warning("assignment makes pointer from integer without a cast");
-            goto type_ok;
-        }
-        type1 = pointed_type(dt);
-        /* a function is implicitly a function pointer */
-        if (sbt == VT_FUNC) {
-            if ((type1->t & VT_BTYPE) != VT_VOID &&
-                !is_compatible_types(pointed_type(dt), st))
-                tcc_warning("assignment from incompatible pointer type");
-            goto type_ok;
-        }
-        if (sbt != VT_PTR)
-            goto error;
-        type2 = pointed_type(st);
-        if ((type1->t & VT_BTYPE) == VT_VOID || 
-            (type2->t & VT_BTYPE) == VT_VOID) {
-            /* void * can match anything */
-        } else {
-            //printf("types %08x %08x\n", type1->t, type2->t);
-            /* exact type match, except for qualifiers */
-            if (!is_compatible_unqualified_types(type1, type2)) {
-		/* Like GCC don't warn by default for merely changes
-		   in pointer target signedness.  Do warn for different
-		   base types, though, in particular for unsigned enums
-		   and signed int targets.  */
-		if ((type1->t & (VT_BTYPE|VT_LONG)) != (type2->t & (VT_BTYPE|VT_LONG))
-                    || IS_ENUM(type1->t) || IS_ENUM(type2->t)
-                    )
-		    tcc_warning("assignment from incompatible pointer type");
-	    }
-        }
-        /* check const and volatile */
-        if ((!(type1->t & VT_CONSTANT) && (type2->t & VT_CONSTANT)) ||
-            (!(type1->t & VT_VOLATILE) && (type2->t & VT_VOLATILE)))
-            tcc_warning("assignment discards qualifiers from pointer target type");
-        break;
-    case VT_BYTE:
-    case VT_SHORT:
-    case VT_INT:
-    case VT_LLONG:
-        if (sbt == VT_PTR || sbt == VT_FUNC) {
-            tcc_warning("assignment makes integer from pointer without a cast");
-        } else if (sbt == VT_STRUCT) {
-            goto case_VT_STRUCT;
-        }
-        /* XXX: more tests */
-        break;
-    case VT_STRUCT:
-    case_VT_STRUCT:
-        if (!is_compatible_unqualified_types(dt, st)) {
-        error:
-            type_to_str(buf1, sizeof(buf1), st, NULL);
-            type_to_str(buf2, sizeof(buf2), dt, NULL);
-            tcc_error("cannot cast '%s' to '%s'", buf1, buf2);
-        }
-        break;
-    }
- type_ok:
-    gen_cast(dt);
-}
-
-/* store vtop in lvalue pushed on stack */
-ST_FUNC void vstore(void)
-{
-    int sbt, dbt, ft, r, t, size, align, bit_size, bit_pos, rc, delayed_cast;
-
-    ft = vtop[-1].type.t;
-    sbt = vtop->type.t & VT_BTYPE;
-    dbt = ft & VT_BTYPE;
-    if ((((sbt == VT_INT || sbt == VT_SHORT) && dbt == VT_BYTE) ||
-         (sbt == VT_INT && dbt == VT_SHORT))
-	&& !(vtop->type.t & VT_BITFIELD)) {
-        /* optimize char/short casts */
-        delayed_cast = VT_MUSTCAST;
-        vtop->type.t = ft & VT_TYPE;
-        /* XXX: factorize */
-        if (ft & VT_CONSTANT)
-            tcc_warning("assignment of read-only location");
-    } else {
-        delayed_cast = 0;
-        if (!(ft & VT_BITFIELD))
-            gen_assign_cast(&vtop[-1].type);
-    }
-
-    if (sbt == VT_STRUCT) {
-        /* if structure, only generate pointer */
-        /* structure assignment : generate memcpy */
-        /* XXX: optimize if small size */
-            size = type_size(&vtop->type, &align);
-
-            /* destination */
-            vswap();
-            vtop->type.t = VT_PTR;
-            gaddrof();
-
-            /* address of memcpy() */
-#ifdef TCC_ARM_EABI
-            if(!(align & 7))
-                vpush_global_sym(&func_old_type, TOK_memcpy8);
-            else if(!(align & 3))
-                vpush_global_sym(&func_old_type, TOK_memcpy4);
-            else
-#endif
-            /* Use memmove, rather than memcpy, as dest and src may be same: */
-            vpush_global_sym(&func_old_type, TOK_memmove);
-
-            vswap();
-            /* source */
-            vpushv(vtop - 2);
-            vtop->type.t = VT_PTR;
-            gaddrof();
-            /* type size */
-            vpushi(size);
-            gfunc_call(3);
-
-        /* leave source on stack */
-    } else if (ft & VT_BITFIELD) {
-        /* bitfield store handling */
-
-        /* save lvalue as expression result (example: s.b = s.a = n;) */
-        vdup(), vtop[-1] = vtop[-2];
-
-        bit_pos = BIT_POS(ft);
-        bit_size = BIT_SIZE(ft);
-        /* remove bit field info to avoid loops */
-        vtop[-1].type.t = ft & ~VT_STRUCT_MASK;
-
-        if ((ft & VT_BTYPE) == VT_BOOL) {
-            gen_cast(&vtop[-1].type);
-            vtop[-1].type.t = (vtop[-1].type.t & ~VT_BTYPE) | (VT_BYTE | VT_UNSIGNED);
-        }
-
-        r = adjust_bf(vtop - 1, bit_pos, bit_size);
-        if (r == VT_STRUCT) {
-            gen_cast_s((ft & VT_BTYPE) == VT_LLONG ? VT_LLONG : VT_INT);
-            store_packed_bf(bit_pos, bit_size);
-        } else {
-            unsigned long long mask = (1ULL << bit_size) - 1;
-            if ((ft & VT_BTYPE) != VT_BOOL) {
-                /* mask source */
-                if ((vtop[-1].type.t & VT_BTYPE) == VT_LLONG)
-                    vpushll(mask);
-                else
-                    vpushi((unsigned)mask);
-                gen_op('&');
-            }
-            /* shift source */
-            vpushi(bit_pos);
-            gen_op(TOK_SHL);
-            vswap();
-            /* duplicate destination */
-            vdup();
-            vrott(3);
-            /* load destination, mask and or with source */
-            if ((vtop->type.t & VT_BTYPE) == VT_LLONG)
-                vpushll(~(mask << bit_pos));
-            else
-                vpushi(~((unsigned)mask << bit_pos));
-            gen_op('&');
-            gen_op('|');
-            /* store result */
-            vstore();
-            /* ... and discard */
-            vpop();
-        }
-    } else if (dbt == VT_VOID) {
-        --vtop;
-    } else {
-#ifdef CONFIG_TCC_BCHECK
-            /* bound check case */
-            if (vtop[-1].r & VT_MUSTBOUND) {
-                vswap();
-                gbound();
-                vswap();
-            }
-#endif
-            rc = RC_INT;
-            if (is_float(ft)) {
-                rc = RC_FLOAT;
-#ifdef TCC_TARGET_X86_64
-                if ((ft & VT_BTYPE) == VT_LDOUBLE) {
-                    rc = RC_ST0;
-                } else if ((ft & VT_BTYPE) == VT_QFLOAT) {
-                    rc = RC_FRET;
-                }
-#endif
-            }
-            r = gv(rc);  /* generate value */
-            /* if lvalue was saved on stack, must read it */
-            if ((vtop[-1].r & VT_VALMASK) == VT_LLOCAL) {
-                SValue sv;
-                t = get_reg(RC_INT);
-#if PTR_SIZE == 8
-                sv.type.t = VT_PTR;
-#else
-                sv.type.t = VT_INT;
-#endif
-                sv.r = VT_LOCAL | VT_LVAL;
-                sv.c.i = vtop[-1].c.i;
-                load(t, &sv);
-                vtop[-1].r = t | VT_LVAL;
-            }
-            /* two word case handling : store second register at word + 4 (or +8 for x86-64)  */
-#if PTR_SIZE == 8
-            if (((ft & VT_BTYPE) == VT_QLONG) || ((ft & VT_BTYPE) == VT_QFLOAT)) {
-                int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE;
-#else
-            if ((ft & VT_BTYPE) == VT_LLONG) {
-                int addr_type = VT_INT, load_size = 4, load_type = VT_INT;
-#endif
-                vtop[-1].type.t = load_type;
-                store(r, vtop - 1);
-                vswap();
-                /* convert to int to increment easily */
-                vtop->type.t = addr_type;
-                gaddrof();
-                vpushi(load_size);
-                gen_op('+');
-                vtop->r |= VT_LVAL;
-                vswap();
-                vtop[-1].type.t = load_type;
-                /* XXX: it works because r2 is spilled last ! */
-                store(vtop->r2, vtop - 1);
-            } else {
-                store(r, vtop - 1);
-            }
-
-        vswap();
-        vtop--; /* NOT vpop() because on x86 it would flush the fp stack */
-        vtop->r |= delayed_cast;
-    }
-}
-
-/* post defines POST/PRE add. c is the token ++ or -- */
-ST_FUNC void inc(int post, int c)
-{
-    test_lvalue();
-    vdup(); /* save lvalue */
-    if (post) {
-        gv_dup(); /* duplicate value */
-        vrotb(3);
-        vrotb(3);
-    }
-    /* add constant */
-    vpushi(c - TOK_MID); 
-    gen_op('+');
-    vstore(); /* store value */
-    if (post)
-        vpop(); /* if post op, return saved value */
-}
-
-ST_FUNC void parse_mult_str (CString *astr, const char *msg)
-{
-    /* read the string */
-    if (tok != TOK_STR)
-        expect(msg);
-    cstr_new(astr);
-    while (tok == TOK_STR) {
-        /* XXX: add \0 handling too ? */
-        cstr_cat(astr, tokc.str.data, -1);
-        next();
-    }
-    cstr_ccat(astr, '\0');
-}
-
-/* If I is >= 1 and a power of two, returns log2(i)+1.
-   If I is 0 returns 0.  */
-static int exact_log2p1(int i)
-{
-  int ret;
-  if (!i)
-    return 0;
-  for (ret = 1; i >= 1 << 8; ret += 8)
-    i >>= 8;
-  if (i >= 1 << 4)
-    ret += 4, i >>= 4;
-  if (i >= 1 << 2)
-    ret += 2, i >>= 2;
-  if (i >= 1 << 1)
-    ret++;
-  return ret;
-}
-
-/* Parse __attribute__((...)) GNUC extension. */
-static void parse_attribute(AttributeDef *ad)
-{
-    int t, n;
-    CString astr;
-    
-redo:
-    if (tok != TOK_ATTRIBUTE1 && tok != TOK_ATTRIBUTE2)
-        return;
-    next();
-    skip('(');
-    skip('(');
-    while (tok != ')') {
-        if (tok < TOK_IDENT)
-            expect("attribute name");
-        t = tok;
-        next();
-        switch(t) {
-        case TOK_SECTION1:
-        case TOK_SECTION2:
-            skip('(');
-	    parse_mult_str(&astr, "section name");
-            ad->section = find_section(tcc_state, (char *)astr.data);
-            skip(')');
-	    cstr_free(&astr);
-            break;
-        case TOK_ALIAS1:
-        case TOK_ALIAS2:
-            skip('(');
-	    parse_mult_str(&astr, "alias(\"target\")");
-            ad->alias_target = /* save string as token, for later */
-              tok_alloc((char*)astr.data, astr.size-1)->tok;
-            skip(')');
-	    cstr_free(&astr);
-            break;
-	case TOK_VISIBILITY1:
-	case TOK_VISIBILITY2:
-            skip('(');
-	    parse_mult_str(&astr,
-			   "visibility(\"default|hidden|internal|protected\")");
-	    if (!strcmp (astr.data, "default"))
-	        ad->a.visibility = STV_DEFAULT;
-	    else if (!strcmp (astr.data, "hidden"))
-	        ad->a.visibility = STV_HIDDEN;
-	    else if (!strcmp (astr.data, "internal"))
-	        ad->a.visibility = STV_INTERNAL;
-	    else if (!strcmp (astr.data, "protected"))
-	        ad->a.visibility = STV_PROTECTED;
-	    else
-                expect("visibility(\"default|hidden|internal|protected\")");
-            skip(')');
-	    cstr_free(&astr);
-            break;
-        case TOK_ALIGNED1:
-        case TOK_ALIGNED2:
-            if (tok == '(') {
-                next();
-                n = expr_const();
-                if (n <= 0 || (n & (n - 1)) != 0) 
-                    tcc_error("alignment must be a positive power of two");
-                skip(')');
-            } else {
-                n = MAX_ALIGN;
-            }
-            ad->a.aligned = exact_log2p1(n);
-	    if (n != 1 << (ad->a.aligned - 1))
-	      tcc_error("alignment of %d is larger than implemented", n);
-            break;
-        case TOK_PACKED1:
-        case TOK_PACKED2:
-            ad->a.packed = 1;
-            break;
-        case TOK_WEAK1:
-        case TOK_WEAK2:
-            ad->a.weak = 1;
-            break;
-        case TOK_UNUSED1:
-        case TOK_UNUSED2:
-            /* currently, no need to handle it because tcc does not
-               track unused objects */
-            break;
-        case TOK_NORETURN1:
-        case TOK_NORETURN2:
-            /* currently, no need to handle it because tcc does not
-               track unused objects */
-            break;
-        case TOK_CDECL1:
-        case TOK_CDECL2:
-        case TOK_CDECL3:
-            ad->f.func_call = FUNC_CDECL;
-            break;
-        case TOK_STDCALL1:
-        case TOK_STDCALL2:
-        case TOK_STDCALL3:
-            ad->f.func_call = FUNC_STDCALL;
-            break;
-#ifdef TCC_TARGET_I386
-        case TOK_REGPARM1:
-        case TOK_REGPARM2:
-            skip('(');
-            n = expr_const();
-            if (n > 3) 
-                n = 3;
-            else if (n < 0)
-                n = 0;
-            if (n > 0)
-                ad->f.func_call = FUNC_FASTCALL1 + n - 1;
-            skip(')');
-            break;
-        case TOK_FASTCALL1:
-        case TOK_FASTCALL2:
-        case TOK_FASTCALL3:
-            ad->f.func_call = FUNC_FASTCALLW;
-            break;            
-#endif
-        case TOK_MODE:
-            skip('(');
-            switch(tok) {
-                case TOK_MODE_DI:
-                    ad->attr_mode = VT_LLONG + 1;
-                    break;
-                case TOK_MODE_QI:
-                    ad->attr_mode = VT_BYTE + 1;
-                    break;
-                case TOK_MODE_HI:
-                    ad->attr_mode = VT_SHORT + 1;
-                    break;
-                case TOK_MODE_SI:
-                case TOK_MODE_word:
-                    ad->attr_mode = VT_INT + 1;
-                    break;
-                default:
-                    tcc_warning("__mode__(%s) not supported\n", get_tok_str(tok, NULL));
-                    break;
-            }
-            next();
-            skip(')');
-            break;
-        case TOK_DLLEXPORT:
-            ad->a.dllexport = 1;
-            break;
-        case TOK_DLLIMPORT:
-            ad->a.dllimport = 1;
-            break;
-        default:
-            if (tcc_state->warn_unsupported)
-                tcc_warning("'%s' attribute ignored", get_tok_str(t, NULL));
-            /* skip parameters */
-            if (tok == '(') {
-                int parenthesis = 0;
-                do {
-                    if (tok == '(') 
-                        parenthesis++;
-                    else if (tok == ')') 
-                        parenthesis--;
-                    next();
-                } while (parenthesis && tok != -1);
-            }
-            break;
-        }
-        if (tok != ',')
-            break;
-        next();
-    }
-    skip(')');
-    skip(')');
-    goto redo;
-}
-
-static Sym * find_field (CType *type, int v)
-{
-    Sym *s = type->ref;
-    v |= SYM_FIELD;
-    while ((s = s->next) != NULL) {
-	if ((s->v & SYM_FIELD) &&
-	    (s->type.t & VT_BTYPE) == VT_STRUCT &&
-	    (s->v & ~SYM_FIELD) >= SYM_FIRST_ANOM) {
-	    Sym *ret = find_field (&s->type, v);
-	    if (ret)
-	        return ret;
-	}
-	if (s->v == v)
-	  break;
-    }
-    return s;
-}
-
-static void struct_add_offset (Sym *s, int offset)
-{
-    while ((s = s->next) != NULL) {
-	if ((s->v & SYM_FIELD) &&
-	    (s->type.t & VT_BTYPE) == VT_STRUCT &&
-	    (s->v & ~SYM_FIELD) >= SYM_FIRST_ANOM) {
-	    struct_add_offset(s->type.ref, offset);
-	} else
-	  s->c += offset;
-    }
-}
-
-static void struct_layout(CType *type, AttributeDef *ad)
-{
-    int size, align, maxalign, offset, c, bit_pos, bit_size;
-    int packed, a, bt, prevbt, prev_bit_size;
-    int pcc = !tcc_state->ms_bitfields;
-    int pragma_pack = *tcc_state->pack_stack_ptr;
-    Sym *f;
-
-    maxalign = 1;
-    offset = 0;
-    c = 0;
-    bit_pos = 0;
-    prevbt = VT_STRUCT; /* make it never match */
-    prev_bit_size = 0;
-
-//#define BF_DEBUG
-
-    for (f = type->ref->next; f; f = f->next) {
-        if (f->type.t & VT_BITFIELD)
-            bit_size = BIT_SIZE(f->type.t);
-        else
-            bit_size = -1;
-        size = type_size(&f->type, &align);
-        a = f->a.aligned ? 1 << (f->a.aligned - 1) : 0;
-        packed = 0;
-
-        if (pcc && bit_size == 0) {
-            /* in pcc mode, packing does not affect zero-width bitfields */
-
-        } else {
-            /* in pcc mode, attribute packed overrides if set. */
-            if (pcc && (f->a.packed || ad->a.packed))
-                align = packed = 1;
-
-            /* pragma pack overrides align if lesser and packs bitfields always */
-            if (pragma_pack) {
-                packed = 1;
-                if (pragma_pack < align)
-                    align = pragma_pack;
-                /* in pcc mode pragma pack also overrides individual align */
-                if (pcc && pragma_pack < a)
-                    a = 0;
-            }
-        }
-        /* some individual align was specified */
-        if (a)
-            align = a;
-
-        if (type->ref->type.t == VT_UNION) {
-	    if (pcc && bit_size >= 0)
-	        size = (bit_size + 7) >> 3;
-	    offset = 0;
-	    if (size > c)
-	        c = size;
-
-	} else if (bit_size < 0) {
-            if (pcc)
-                c += (bit_pos + 7) >> 3;
-	    c = (c + align - 1) & -align;
-	    offset = c;
-	    if (size > 0)
-	        c += size;
-	    bit_pos = 0;
-	    prevbt = VT_STRUCT;
-	    prev_bit_size = 0;
-
-	} else {
-	    /* A bit-field.  Layout is more complicated.  There are two
-	       options: PCC (GCC) compatible and MS compatible */
-            if (pcc) {
-		/* In PCC layout a bit-field is placed adjacent to the
-                   preceding bit-fields, except if:
-                   - it has zero-width
-                   - an individual alignment was given
-                   - it would overflow its base type container and
-                     there is no packing */
-                if (bit_size == 0) {
-            new_field:
-		    c = (c + ((bit_pos + 7) >> 3) + align - 1) & -align;
-		    bit_pos = 0;
-                } else if (f->a.aligned) {
-                    goto new_field;
-                } else if (!packed) {
-                    int a8 = align * 8;
-	            int ofs = ((c * 8 + bit_pos) % a8 + bit_size + a8 - 1) / a8;
-                    if (ofs > size / align)
-                        goto new_field;
-                }
-
-                /* in pcc mode, long long bitfields have type int if they fit */
-                if (size == 8 && bit_size <= 32)
-                    f->type.t = (f->type.t & ~VT_BTYPE) | VT_INT, size = 4;
-
-                while (bit_pos >= align * 8)
-                    c += align, bit_pos -= align * 8;
-                offset = c;
-
-		/* In PCC layout named bit-fields influence the alignment
-		   of the containing struct using the base types alignment,
-		   except for packed fields (which here have correct align).  */
-		if (f->v & SYM_FIRST_ANOM
-                    // && bit_size // ??? gcc on ARM/rpi does that
-                    )
-		    align = 1;
-
-	    } else {
-		bt = f->type.t & VT_BTYPE;
-		if ((bit_pos + bit_size > size * 8)
-                    || (bit_size > 0) == (bt != prevbt)
-                    ) {
-		    c = (c + align - 1) & -align;
-		    offset = c;
-		    bit_pos = 0;
-		    /* In MS bitfield mode a bit-field run always uses
-		       at least as many bits as the underlying type.
-		       To start a new run it's also required that this
-		       or the last bit-field had non-zero width.  */
-		    if (bit_size || prev_bit_size)
-		        c += size;
-		}
-		/* In MS layout the records alignment is normally
-		   influenced by the field, except for a zero-width
-		   field at the start of a run (but by further zero-width
-		   fields it is again).  */
-		if (bit_size == 0 && prevbt != bt)
-		    align = 1;
-		prevbt = bt;
-                prev_bit_size = bit_size;
-	    }
-
-	    f->type.t = (f->type.t & ~(0x3f << VT_STRUCT_SHIFT))
-		        | (bit_pos << VT_STRUCT_SHIFT);
-	    bit_pos += bit_size;
-	}
-	if (align > maxalign)
-	    maxalign = align;
-
-#ifdef BF_DEBUG
-	printf("set field %s offset %-2d size %-2d align %-2d",
-	       get_tok_str(f->v & ~SYM_FIELD, NULL), offset, size, align);
-	if (f->type.t & VT_BITFIELD) {
-	    printf(" pos %-2d bits %-2d",
-                    BIT_POS(f->type.t),
-                    BIT_SIZE(f->type.t)
-                    );
-	}
-	printf("\n");
-#endif
-
-	if (f->v & SYM_FIRST_ANOM && (f->type.t & VT_BTYPE) == VT_STRUCT) {
-	    Sym *ass;
-	    /* An anonymous struct/union.  Adjust member offsets
-	       to reflect the real offset of our containing struct.
-	       Also set the offset of this anon member inside
-	       the outer struct to be zero.  Via this it
-	       works when accessing the field offset directly
-	       (from base object), as well as when recursing
-	       members in initializer handling.  */
-	    int v2 = f->type.ref->v;
-	    if (!(v2 & SYM_FIELD) &&
-		(v2 & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
-		Sym **pps;
-		/* This happens only with MS extensions.  The
-		   anon member has a named struct type, so it
-		   potentially is shared with other references.
-		   We need to unshare members so we can modify
-		   them.  */
-		ass = f->type.ref;
-		f->type.ref = sym_push(anon_sym++ | SYM_FIELD,
-				       &f->type.ref->type, 0,
-				       f->type.ref->c);
-		pps = &f->type.ref->next;
-		while ((ass = ass->next) != NULL) {
-		    *pps = sym_push(ass->v, &ass->type, 0, ass->c);
-		    pps = &((*pps)->next);
-		}
-		*pps = NULL;
-	    }
-	    struct_add_offset(f->type.ref, offset);
-	    f->c = 0;
-	} else {
-	    f->c = offset;
-	}
-
-	f->r = 0;
-    }
-
-    if (pcc)
-        c += (bit_pos + 7) >> 3;
-
-    /* store size and alignment */
-    a = bt = ad->a.aligned ? 1 << (ad->a.aligned - 1) : 1;
-    if (a < maxalign)
-        a = maxalign;
-    type->ref->r = a;
-    if (pragma_pack && pragma_pack < maxalign && 0 == pcc) {
-        /* can happen if individual align for some member was given.  In
-           this case MSVC ignores maxalign when aligning the size */
-        a = pragma_pack;
-        if (a < bt)
-            a = bt;
-    }
-    c = (c + a - 1) & -a;
-    type->ref->c = c;
-
-#ifdef BF_DEBUG
-    printf("struct size %-2d align %-2d\n\n", c, a), fflush(stdout);
-#endif
-
-    /* check whether we can access bitfields by their type */
-    for (f = type->ref->next; f; f = f->next) {
-        int s, px, cx, c0;
-        CType t;
-
-        if (0 == (f->type.t & VT_BITFIELD))
-            continue;
-        f->type.ref = f;
-        f->auxtype = -1;
-        bit_size = BIT_SIZE(f->type.t);
-        if (bit_size == 0)
-            continue;
-        bit_pos = BIT_POS(f->type.t);
-        size = type_size(&f->type, &align);
-        if (bit_pos + bit_size <= size * 8 && f->c + size <= c)
-            continue;
-
-        /* try to access the field using a different type */
-        c0 = -1, s = align = 1;
-        for (;;) {
-            px = f->c * 8 + bit_pos;
-            cx = (px >> 3) & -align;
-            px = px - (cx << 3);
-            if (c0 == cx)
-                break;
-            s = (px + bit_size + 7) >> 3;
-            if (s > 4) {
-                t.t = VT_LLONG;
-            } else if (s > 2) {
-                t.t = VT_INT;
-            } else if (s > 1) {
-                t.t = VT_SHORT;
-            } else {
-                t.t = VT_BYTE;
-            }
-            s = type_size(&t, &align);
-            c0 = cx;
-        }
-
-        if (px + bit_size <= s * 8 && cx + s <= c) {
-            /* update offset and bit position */
-            f->c = cx;
-            bit_pos = px;
-	    f->type.t = (f->type.t & ~(0x3f << VT_STRUCT_SHIFT))
-		        | (bit_pos << VT_STRUCT_SHIFT);
-            if (s != size)
-                f->auxtype = t.t;
-#ifdef BF_DEBUG
-            printf("FIX field %s offset %-2d size %-2d align %-2d "
-                "pos %-2d bits %-2d\n",
-                get_tok_str(f->v & ~SYM_FIELD, NULL),
-                cx, s, align, px, bit_size);
-#endif
-        } else {
-            /* fall back to load/store single-byte wise */
-            f->auxtype = VT_STRUCT;
-#ifdef BF_DEBUG
-            printf("FIX field %s : load byte-wise\n",
-                 get_tok_str(f->v & ~SYM_FIELD, NULL));
-#endif
-        }
-    }
-}
-
-/* enum/struct/union declaration. u is VT_ENUM/VT_STRUCT/VT_UNION */
-static void struct_decl(CType *type, int u)
-{
-    int v, c, size, align, flexible;
-    int bit_size, bsize, bt;
-    Sym *s, *ss, **ps;
-    AttributeDef ad, ad1;
-    CType type1, btype;
-
-    memset(&ad, 0, sizeof ad);
-    next();
-    parse_attribute(&ad);
-    if (tok != '{') {
-        v = tok;
-        next();
-        /* struct already defined ? return it */
-        if (v < TOK_IDENT)
-            expect("struct/union/enum name");
-        s = struct_find(v);
-        if (s && (s->sym_scope == local_scope || tok != '{')) {
-            if (u == s->type.t)
-                goto do_decl;
-            if (u == VT_ENUM && IS_ENUM(s->type.t))
-                goto do_decl;
-            tcc_error("redefinition of '%s'", get_tok_str(v, NULL));
-        }
-    } else {
-        v = anon_sym++;
-    }
-    /* Record the original enum/struct/union token.  */
-    type1.t = u == VT_ENUM ? u | VT_INT | VT_UNSIGNED : u;
-    type1.ref = NULL;
-    /* we put an undefined size for struct/union */
-    s = sym_push(v | SYM_STRUCT, &type1, 0, -1);
-    s->r = 0; /* default alignment is zero as gcc */
-do_decl:
-    type->t = s->type.t;
-    type->ref = s;
-
-    if (tok == '{') {
-        next();
-        if (s->c != -1)
-            tcc_error("struct/union/enum already defined");
-        /* cannot be empty */
-        /* non empty enums are not allowed */
-        ps = &s->next;
-        if (u == VT_ENUM) {
-            long long ll = 0, pl = 0, nl = 0;
-	    CType t;
-            t.ref = s;
-            /* enum symbols have static storage */
-            t.t = VT_INT|VT_STATIC|VT_ENUM_VAL;
-            for(;;) {
-                v = tok;
-                if (v < TOK_UIDENT)
-                    expect("identifier");
-                ss = sym_find(v);
-                if (ss && !local_stack)
-                    tcc_error("redefinition of enumerator '%s'",
-                              get_tok_str(v, NULL));
-                next();
-                if (tok == '=') {
-                    next();
-		    ll = expr_const64();
-                }
-                ss = sym_push(v, &t, VT_CONST, 0);
-                ss->enum_val = ll;
-                *ps = ss, ps = &ss->next;
-                if (ll < nl)
-                    nl = ll;
-                if (ll > pl)
-                    pl = ll;
-                if (tok != ',')
-                    break;
-                next();
-                ll++;
-                /* NOTE: we accept a trailing comma */
-                if (tok == '}')
-                    break;
-            }
-            skip('}');
-            /* set integral type of the enum */
-            t.t = VT_INT;
-            if (nl >= 0) {
-                if (pl != (unsigned)pl)
-                    t.t = (LONG_SIZE==8 ? VT_LLONG|VT_LONG : VT_LLONG);
-                t.t |= VT_UNSIGNED;
-            } else if (pl != (int)pl || nl != (int)nl)
-                t.t = (LONG_SIZE==8 ? VT_LLONG|VT_LONG : VT_LLONG);
-            s->type.t = type->t = t.t | VT_ENUM;
-            s->c = 0;
-            /* set type for enum members */
-            for (ss = s->next; ss; ss = ss->next) {
-                ll = ss->enum_val;
-                if (ll == (int)ll) /* default is int if it fits */
-                    continue;
-                if (t.t & VT_UNSIGNED) {
-                    ss->type.t |= VT_UNSIGNED;
-                    if (ll == (unsigned)ll)
-                        continue;
-                }
-                ss->type.t = (ss->type.t & ~VT_BTYPE)
-                    | (LONG_SIZE==8 ? VT_LLONG|VT_LONG : VT_LLONG);
-            }
-        } else {
-            c = 0;
-            flexible = 0;
-            while (tok != '}') {
-                if (!parse_btype(&btype, &ad1)) {
-		    skip(';');
-		    continue;
-		}
-                while (1) {
-		    if (flexible)
-		        tcc_error("flexible array member '%s' not at the end of struct",
-                              get_tok_str(v, NULL));
-                    bit_size = -1;
-                    v = 0;
-                    type1 = btype;
-                    if (tok != ':') {
-			if (tok != ';')
-                            type_decl(&type1, &ad1, &v, TYPE_DIRECT);
-                        if (v == 0) {
-                    	    if ((type1.t & VT_BTYPE) != VT_STRUCT)
-                        	expect("identifier");
-                    	    else {
-				int v = btype.ref->v;
-				if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
-				    if (tcc_state->ms_extensions == 0)
-                        		expect("identifier");
-				}
-                    	    }
-                        }
-                        if (type_size(&type1, &align) < 0) {
-			    if ((u == VT_STRUCT) && (type1.t & VT_ARRAY) && c)
-			        flexible = 1;
-			    else
-			        tcc_error("field '%s' has incomplete type",
-                                      get_tok_str(v, NULL));
-                        }
-                        if ((type1.t & VT_BTYPE) == VT_FUNC ||
-                            (type1.t & VT_STORAGE))
-                            tcc_error("invalid type for '%s'", 
-                                  get_tok_str(v, NULL));
-                    }
-                    if (tok == ':') {
-                        next();
-                        bit_size = expr_const();
-                        /* XXX: handle v = 0 case for messages */
-                        if (bit_size < 0)
-                            tcc_error("negative width in bit-field '%s'", 
-                                  get_tok_str(v, NULL));
-                        if (v && bit_size == 0)
-                            tcc_error("zero width for bit-field '%s'", 
-                                  get_tok_str(v, NULL));
-			parse_attribute(&ad1);
-                    }
-                    size = type_size(&type1, &align);
-                    if (bit_size >= 0) {
-                        bt = type1.t & VT_BTYPE;
-                        if (bt != VT_INT && 
-                            bt != VT_BYTE && 
-                            bt != VT_SHORT &&
-                            bt != VT_BOOL &&
-                            bt != VT_LLONG)
-                            tcc_error("bitfields must have scalar type");
-                        bsize = size * 8;
-                        if (bit_size > bsize) {
-                            tcc_error("width of '%s' exceeds its type",
-                                  get_tok_str(v, NULL));
-                        } else if (bit_size == bsize
-                                    && !ad.a.packed && !ad1.a.packed) {
-                            /* no need for bit fields */
-                            ;
-                        } else if (bit_size == 64) {
-                            tcc_error("field width 64 not implemented");
-                        } else {
-                            type1.t = (type1.t & ~VT_STRUCT_MASK)
-                                | VT_BITFIELD
-                                | (bit_size << (VT_STRUCT_SHIFT + 6));
-                        }
-                    }
-                    if (v != 0 || (type1.t & VT_BTYPE) == VT_STRUCT) {
-                        /* Remember we've seen a real field to check
-			   for placement of flexible array member. */
-			c = 1;
-                    }
-		    /* If member is a struct or bit-field, enforce
-		       placing into the struct (as anonymous).  */
-                    if (v == 0 &&
-			((type1.t & VT_BTYPE) == VT_STRUCT ||
-			 bit_size >= 0)) {
-		        v = anon_sym++;
-		    }
-                    if (v) {
-                        ss = sym_push(v | SYM_FIELD, &type1, 0, 0);
-                        ss->a = ad1.a;
-                        *ps = ss;
-                        ps = &ss->next;
-                    }
-                    if (tok == ';' || tok == TOK_EOF)
-                        break;
-                    skip(',');
-                }
-                skip(';');
-            }
-            skip('}');
-	    parse_attribute(&ad);
-	    struct_layout(type, &ad);
-        }
-    }
-}
-
-static void sym_to_attr(AttributeDef *ad, Sym *s)
-{
-    if (s->a.aligned && 0 == ad->a.aligned)
-        ad->a.aligned = s->a.aligned;
-    if (s->f.func_call && 0 == ad->f.func_call)
-        ad->f.func_call = s->f.func_call;
-    if (s->f.func_type && 0 == ad->f.func_type)
-        ad->f.func_type = s->f.func_type;
-    if (s->a.packed)
-        ad->a.packed = 1;
-}
-
-/* Add type qualifiers to a type. If the type is an array then the qualifiers
-   are added to the element type, copied because it could be a typedef. */
-static void parse_btype_qualify(CType *type, int qualifiers)
-{
-    while (type->t & VT_ARRAY) {
-        type->ref = sym_push(SYM_FIELD, &type->ref->type, 0, type->ref->c);
-        type = &type->ref->type;
-    }
-    type->t |= qualifiers;
-}
-
-/* return 0 if no type declaration. otherwise, return the basic type
-   and skip it. 
- */
-static int parse_btype(CType *type, AttributeDef *ad)
-{
-    int t, u, bt, st, type_found, typespec_found, g;
-    Sym *s;
-    CType type1;
-
-    memset(ad, 0, sizeof(AttributeDef));
-    type_found = 0;
-    typespec_found = 0;
-    t = VT_INT;
-    bt = st = -1;
-    type->ref = NULL;
-
-    while(1) {
-        switch(tok) {
-        case TOK_EXTENSION:
-            /* currently, we really ignore extension */
-            next();
-            continue;
-
-            /* basic types */
-        case TOK_CHAR:
-            u = VT_BYTE;
-        basic_type:
-            next();
-        basic_type1:
-            if (u == VT_SHORT || u == VT_LONG) {
-                if (st != -1 || (bt != -1 && bt != VT_INT))
-                    tmbt: tcc_error("too many basic types");
-                st = u;
-            } else {
-                if (bt != -1 || (st != -1 && u != VT_INT))
-                    goto tmbt;
-                bt = u;
-            }
-            if (u != VT_INT)
-                t = (t & ~(VT_BTYPE|VT_LONG)) | u;
-            typespec_found = 1;
-            break;
-        case TOK_VOID:
-            u = VT_VOID;
-            goto basic_type;
-        case TOK_SHORT:
-            u = VT_SHORT;
-            goto basic_type;
-        case TOK_INT:
-            u = VT_INT;
-            goto basic_type;
-        case TOK_LONG:
-            if ((t & VT_BTYPE) == VT_DOUBLE) {
-                t = (t & ~(VT_BTYPE|VT_LONG)) | VT_LDOUBLE;
-            } else if ((t & (VT_BTYPE|VT_LONG)) == VT_LONG) {
-                t = (t & ~(VT_BTYPE|VT_LONG)) | VT_LLONG;
-            } else {
-                u = VT_LONG;
-                goto basic_type;
-            }
-            next();
-            break;
-#ifdef TCC_TARGET_ARM64
-        case TOK_UINT128:
-            /* GCC's __uint128_t appears in some Linux header files. Make it a
-               synonym for long double to get the size and alignment right. */
-            u = VT_LDOUBLE;
-            goto basic_type;
-#endif
-        case TOK_BOOL:
-            u = VT_BOOL;
-            goto basic_type;
-        case TOK_FLOAT:
-            u = VT_FLOAT;
-            goto basic_type;
-        case TOK_DOUBLE:
-            if ((t & (VT_BTYPE|VT_LONG)) == VT_LONG) {
-                t = (t & ~(VT_BTYPE|VT_LONG)) | VT_LDOUBLE;
-            } else {
-                u = VT_DOUBLE;
-                goto basic_type;
-            }
-            next();
-            break;
-        case TOK_ENUM:
-            struct_decl(&type1, VT_ENUM);
-        basic_type2:
-            u = type1.t;
-            type->ref = type1.ref;
-            goto basic_type1;
-        case TOK_STRUCT:
-            struct_decl(&type1, VT_STRUCT);
-            goto basic_type2;
-        case TOK_UNION:
-            struct_decl(&type1, VT_UNION);
-            goto basic_type2;
-
-            /* type modifiers */
-        case TOK_CONST1:
-        case TOK_CONST2:
-        case TOK_CONST3:
-            type->t = t;
-            parse_btype_qualify(type, VT_CONSTANT);
-            t = type->t;
-            next();
-            break;
-        case TOK_VOLATILE1:
-        case TOK_VOLATILE2:
-        case TOK_VOLATILE3:
-            type->t = t;
-            parse_btype_qualify(type, VT_VOLATILE);
-            t = type->t;
-            next();
-            break;
-        case TOK_SIGNED1:
-        case TOK_SIGNED2:
-        case TOK_SIGNED3:
-            if ((t & (VT_DEFSIGN|VT_UNSIGNED)) == (VT_DEFSIGN|VT_UNSIGNED))
-                tcc_error("signed and unsigned modifier");
-            t |= VT_DEFSIGN;
-            next();
-            typespec_found = 1;
-            break;
-        case TOK_REGISTER:
-        case TOK_AUTO:
-        case TOK_RESTRICT1:
-        case TOK_RESTRICT2:
-        case TOK_RESTRICT3:
-            next();
-            break;
-        case TOK_UNSIGNED:
-            if ((t & (VT_DEFSIGN|VT_UNSIGNED)) == VT_DEFSIGN)
-                tcc_error("signed and unsigned modifier");
-            t |= VT_DEFSIGN | VT_UNSIGNED;
-            next();
-            typespec_found = 1;
-            break;
-
-            /* storage */
-        case TOK_EXTERN:
-            g = VT_EXTERN;
-            goto storage;
-        case TOK_STATIC:
-            g = VT_STATIC;
-            goto storage;
-        case TOK_TYPEDEF:
-            g = VT_TYPEDEF;
-            goto storage;
-       storage:
-            if (t & (VT_EXTERN|VT_STATIC|VT_TYPEDEF) & ~g)
-                tcc_error("multiple storage classes");
-            t |= g;
-            next();
-            break;
-        case TOK_INLINE1:
-        case TOK_INLINE2:
-        case TOK_INLINE3:
-            t |= VT_INLINE;
-            next();
-            break;
-
-            /* GNUC attribute */
-        case TOK_ATTRIBUTE1:
-        case TOK_ATTRIBUTE2:
-            parse_attribute(ad);
-            if (ad->attr_mode) {
-                u = ad->attr_mode -1;
-                t = (t & ~(VT_BTYPE|VT_LONG)) | u;
-            }
-            break;
-            /* GNUC typeof */
-        case TOK_TYPEOF1:
-        case TOK_TYPEOF2:
-        case TOK_TYPEOF3:
-            next();
-            parse_expr_type(&type1);
-            /* remove all storage modifiers except typedef */
-            type1.t &= ~(VT_STORAGE&~VT_TYPEDEF);
-	    if (type1.ref)
-                sym_to_attr(ad, type1.ref);
-            goto basic_type2;
-        default:
-            if (typespec_found)
-                goto the_end;
-            s = sym_find(tok);
-            if (!s || !(s->type.t & VT_TYPEDEF))
-                goto the_end;
-            t &= ~(VT_BTYPE|VT_LONG);
-            u = t & ~(VT_CONSTANT | VT_VOLATILE), t ^= u;
-            type->t = (s->type.t & ~VT_TYPEDEF) | u;
-            type->ref = s->type.ref;
-            if (t)
-                parse_btype_qualify(type, t);
-            t = type->t;
-            /* get attributes from typedef */
-            sym_to_attr(ad, s);
-            next();
-            typespec_found = 1;
-            st = bt = -2;
-            break;
-        }
-        type_found = 1;
-    }
-the_end:
-    if (tcc_state->char_is_unsigned) {
-        if ((t & (VT_DEFSIGN|VT_BTYPE)) == VT_BYTE)
-            t |= VT_UNSIGNED;
-    }
-    /* VT_LONG is used just as a modifier for VT_INT / VT_LLONG */
-    bt = t & (VT_BTYPE|VT_LONG);
-    if (bt == VT_LONG)
-        t |= LONG_SIZE == 8 ? VT_LLONG : VT_INT;
-#ifdef TCC_TARGET_PE
-    if (bt == VT_LDOUBLE)
-        t = (t & ~(VT_BTYPE|VT_LONG)) | VT_DOUBLE;
-#endif
-    type->t = t;
-    return type_found;
-}
-
-/* convert a function parameter type (array to pointer and function to
-   function pointer) */
-static inline void convert_parameter_type(CType *pt)
-{
-    /* remove const and volatile qualifiers (XXX: const could be used
-       to indicate a const function parameter */
-    pt->t &= ~(VT_CONSTANT | VT_VOLATILE);
-    /* array must be transformed to pointer according to ANSI C */
-    pt->t &= ~VT_ARRAY;
-    if ((pt->t & VT_BTYPE) == VT_FUNC) {
-        mk_pointer(pt);
-    }
-}
-
-ST_FUNC void parse_asm_str(CString *astr)
-{
-    skip('(');
-    parse_mult_str(astr, "string constant");
-}
-
-/* Parse an asm label and return the token */
-static int asm_label_instr(void)
-{
-    int v;
-    CString astr;
-
-    next();
-    parse_asm_str(&astr);
-    skip(')');
-#ifdef ASM_DEBUG
-    printf("asm_alias: \"%s\"\n", (char *)astr.data);
-#endif
-    v = tok_alloc(astr.data, astr.size - 1)->tok;
-    cstr_free(&astr);
-    return v;
-}
-
-static int post_type(CType *type, AttributeDef *ad, int storage, int td)
-{
-    int n, l, t1, arg_size, align;
-    Sym **plast, *s, *first;
-    AttributeDef ad1;
-    CType pt;
-
-    if (tok == '(') {
-        /* function type, or recursive declarator (return if so) */
-        next();
-	if (td && !(td & TYPE_ABSTRACT))
-	  return 0;
-	if (tok == ')')
-	  l = 0;
-	else if (parse_btype(&pt, &ad1))
-	  l = FUNC_NEW;
-	else if (td)
-	  return 0;
-	else
-	  l = FUNC_OLD;
-        first = NULL;
-        plast = &first;
-        arg_size = 0;
-        if (l) {
-            for(;;) {
-                /* read param name and compute offset */
-                if (l != FUNC_OLD) {
-                    if ((pt.t & VT_BTYPE) == VT_VOID && tok == ')')
-                        break;
-                    type_decl(&pt, &ad1, &n, TYPE_DIRECT | TYPE_ABSTRACT);
-                    if ((pt.t & VT_BTYPE) == VT_VOID)
-                        tcc_error("parameter declared as void");
-                    arg_size += (type_size(&pt, &align) + PTR_SIZE - 1) / PTR_SIZE;
-                } else {
-                    n = tok;
-                    if (n < TOK_UIDENT)
-                        expect("identifier");
-                    pt.t = VT_VOID; /* invalid type */
-                    next();
-                }
-                convert_parameter_type(&pt);
-                s = sym_push(n | SYM_FIELD, &pt, 0, 0);
-                *plast = s;
-                plast = &s->next;
-                if (tok == ')')
-                    break;
-                skip(',');
-                if (l == FUNC_NEW && tok == TOK_DOTS) {
-                    l = FUNC_ELLIPSIS;
-                    next();
-                    break;
-                }
-		if (l == FUNC_NEW && !parse_btype(&pt, &ad1))
-		    tcc_error("invalid type");
-            }
-        } else
-            /* if no parameters, then old type prototype */
-            l = FUNC_OLD;
-        skip(')');
-        /* NOTE: const is ignored in returned type as it has a special
-           meaning in gcc / C++ */
-        type->t &= ~VT_CONSTANT; 
-        /* some ancient pre-K&R C allows a function to return an array
-           and the array brackets to be put after the arguments, such 
-           that "int c()[]" means something like "int[] c()" */
-        if (tok == '[') {
-            next();
-            skip(']'); /* only handle simple "[]" */
-            mk_pointer(type);
-        }
-        /* we push a anonymous symbol which will contain the function prototype */
-        ad->f.func_args = arg_size;
-        ad->f.func_type = l;
-        s = sym_push(SYM_FIELD, type, 0, 0);
-        s->a = ad->a;
-        s->f = ad->f;
-        s->next = first;
-        type->t = VT_FUNC;
-        type->ref = s;
-    } else if (tok == '[') {
-	int saved_nocode_wanted = nocode_wanted;
-        /* array definition */
-        next();
-        if (tok == TOK_RESTRICT1)
-            next();
-        n = -1;
-        t1 = 0;
-        if (tok != ']') {
-            if (!local_stack || (storage & VT_STATIC))
-                vpushi(expr_const());
-            else {
-		/* VLAs (which can only happen with local_stack && !VT_STATIC)
-		   length must always be evaluated, even under nocode_wanted,
-		   so that its size slot is initialized (e.g. under sizeof
-		   or typeof).  */
-		nocode_wanted = 0;
-		gexpr();
-	    }
-            if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
-                n = vtop->c.i;
-                if (n < 0)
-                    tcc_error("invalid array size");
-            } else {
-                if (!is_integer_btype(vtop->type.t & VT_BTYPE))
-                    tcc_error("size of variable length array should be an integer");
-                t1 = VT_VLA;
-            }
-        }
-        skip(']');
-        /* parse next post type */
-        post_type(type, ad, storage, 0);
-        if (type->t == VT_FUNC)
-            tcc_error("declaration of an array of functions");
-        t1 |= type->t & VT_VLA;
-        
-        if (t1 & VT_VLA) {
-            loc -= type_size(&int_type, &align);
-            loc &= -align;
-            n = loc;
-
-            vla_runtime_type_size(type, &align);
-            gen_op('*');
-            vset(&int_type, VT_LOCAL|VT_LVAL, n);
-            vswap();
-            vstore();
-        }
-        if (n != -1)
-            vpop();
-	nocode_wanted = saved_nocode_wanted;
-                
-        /* we push an anonymous symbol which will contain the array
-           element type */
-        s = sym_push(SYM_FIELD, type, 0, n);
-        type->t = (t1 ? VT_VLA : VT_ARRAY) | VT_PTR;
-        type->ref = s;
-    }
-    return 1;
-}
-
-/* Parse a type declarator (except basic type), and return the type
-   in 'type'. 'td' is a bitmask indicating which kind of type decl is
-   expected. 'type' should contain the basic type. 'ad' is the
-   attribute definition of the basic type. It can be modified by
-   type_decl().  If this (possibly abstract) declarator is a pointer chain
-   it returns the innermost pointed to type (equals *type, but is a different
-   pointer), otherwise returns type itself, that's used for recursive calls.  */
-static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td)
-{
-    CType *post, *ret;
-    int qualifiers, storage;
-
-    /* recursive type, remove storage bits first, apply them later again */
-    storage = type->t & VT_STORAGE;
-    type->t &= ~VT_STORAGE;
-    post = ret = type;
-
-    while (tok == '*') {
-        qualifiers = 0;
-    redo:
-        next();
-        switch(tok) {
-        case TOK_CONST1:
-        case TOK_CONST2:
-        case TOK_CONST3:
-            qualifiers |= VT_CONSTANT;
-            goto redo;
-        case TOK_VOLATILE1:
-        case TOK_VOLATILE2:
-        case TOK_VOLATILE3:
-            qualifiers |= VT_VOLATILE;
-            goto redo;
-        case TOK_RESTRICT1:
-        case TOK_RESTRICT2:
-        case TOK_RESTRICT3:
-            goto redo;
-	/* XXX: clarify attribute handling */
-	case TOK_ATTRIBUTE1:
-	case TOK_ATTRIBUTE2:
-	    parse_attribute(ad);
-	    break;
-        }
-        mk_pointer(type);
-        type->t |= qualifiers;
-	if (ret == type)
-	    /* innermost pointed to type is the one for the first derivation */
-	    ret = pointed_type(type);
-    }
-
-    if (tok == '(') {
-	/* This is possibly a parameter type list for abstract declarators
-	   ('int ()'), use post_type for testing this.  */
-	if (!post_type(type, ad, 0, td)) {
-	    /* It's not, so it's a nested declarator, and the post operations
-	       apply to the innermost pointed to type (if any).  */
-	    /* XXX: this is not correct to modify 'ad' at this point, but
-	       the syntax is not clear */
-	    parse_attribute(ad);
-	    post = type_decl(type, ad, v, td);
-	    skip(')');
-	}
-    } else if (tok >= TOK_IDENT && (td & TYPE_DIRECT)) {
-	/* type identifier */
-	*v = tok;
-	next();
-    } else {
-	if (!(td & TYPE_ABSTRACT))
-	  expect("identifier");
-	*v = 0;
-    }
-    post_type(post, ad, storage, 0);
-    parse_attribute(ad);
-    type->t |= storage;
-    return ret;
-}
-
-/* compute the lvalue VT_LVAL_xxx needed to match type t. */
-ST_FUNC int lvalue_type(int t)
-{
-    int bt, r;
-    r = VT_LVAL;
-    bt = t & VT_BTYPE;
-    if (bt == VT_BYTE || bt == VT_BOOL)
-        r |= VT_LVAL_BYTE;
-    else if (bt == VT_SHORT)
-        r |= VT_LVAL_SHORT;
-    else
-        return r;
-    if (t & VT_UNSIGNED)
-        r |= VT_LVAL_UNSIGNED;
-    return r;
-}
-
-/* indirection with full error checking and bound check */
-ST_FUNC void indir(void)
-{
-    if ((vtop->type.t & VT_BTYPE) != VT_PTR) {
-        if ((vtop->type.t & VT_BTYPE) == VT_FUNC)
-            return;
-        expect("pointer");
-    }
-    if (vtop->r & VT_LVAL)
-        gv(RC_INT);
-    vtop->type = *pointed_type(&vtop->type);
-    /* Arrays and functions are never lvalues */
-    if (!(vtop->type.t & VT_ARRAY) && !(vtop->type.t & VT_VLA)
-        && (vtop->type.t & VT_BTYPE) != VT_FUNC) {
-        vtop->r |= lvalue_type(vtop->type.t);
-        /* if bound checking, the referenced pointer must be checked */
-#ifdef CONFIG_TCC_BCHECK
-        if (tcc_state->do_bounds_check)
-            vtop->r |= VT_MUSTBOUND;
-#endif
-    }
-}
-
-/* pass a parameter to a function and do type checking and casting */
-static void gfunc_param_typed(Sym *func, Sym *arg)
-{
-    int func_type;
-    CType type;
-
-    func_type = func->f.func_type;
-    if (func_type == FUNC_OLD ||
-        (func_type == FUNC_ELLIPSIS && arg == NULL)) {
-        /* default casting : only need to convert float to double */
-        if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) {
-            gen_cast_s(VT_DOUBLE);
-        } else if (vtop->type.t & VT_BITFIELD) {
-            type.t = vtop->type.t & (VT_BTYPE | VT_UNSIGNED);
-	    type.ref = vtop->type.ref;
-            gen_cast(&type);
-        }
-    } else if (arg == NULL) {
-        tcc_error("too many arguments to function");
-    } else {
-        type = arg->type;
-        type.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */
-        gen_assign_cast(&type);
-    }
-}
-
-/* parse an expression and return its type without any side effect. */
-static void expr_type(CType *type, void (*expr_fn)(void))
-{
-    nocode_wanted++;
-    expr_fn();
-    *type = vtop->type;
-    vpop();
-    nocode_wanted--;
-}
-
-/* parse an expression of the form '(type)' or '(expr)' and return its
-   type */
-static void parse_expr_type(CType *type)
-{
-    int n;
-    AttributeDef ad;
-
-    skip('(');
-    if (parse_btype(type, &ad)) {
-        type_decl(type, &ad, &n, TYPE_ABSTRACT);
-    } else {
-        expr_type(type, gexpr);
-    }
-    skip(')');
-}
-
-static void parse_type(CType *type)
-{
-    AttributeDef ad;
-    int n;
-
-    if (!parse_btype(type, &ad)) {
-        expect("type");
-    }
-    type_decl(type, &ad, &n, TYPE_ABSTRACT);
-}
-
-static void parse_builtin_params(int nc, const char *args)
-{
-    char c, sep = '(';
-    CType t;
-    if (nc)
-        nocode_wanted++;
-    next();
-    while ((c = *args++)) {
-	skip(sep);
-	sep = ',';
-	switch (c) {
-	    case 'e': expr_eq(); continue;
-	    case 't': parse_type(&t); vpush(&t); continue;
-	    default: tcc_error("internal error"); break;
-	}
-    }
-    skip(')');
-    if (nc)
-        nocode_wanted--;
-}
-
-ST_FUNC void unary(void)
-{
-    int n, t, align, size, r, sizeof_caller;
-    CType type;
-    Sym *s;
-    AttributeDef ad;
-
-    sizeof_caller = in_sizeof;
-    in_sizeof = 0;
-    type.ref = NULL;
-    /* XXX: GCC 2.95.3 does not generate a table although it should be
-       better here */
- tok_next:
-    switch(tok) {
-    case TOK_EXTENSION:
-        next();
-        goto tok_next;
-    case TOK_LCHAR:
-#ifdef TCC_TARGET_PE
-        t = VT_SHORT|VT_UNSIGNED;
-        goto push_tokc;
-#endif
-    case TOK_CINT:
-    case TOK_CCHAR: 
-	t = VT_INT;
- push_tokc:
-	type.t = t;
-	vsetc(&type, VT_CONST, &tokc);
-        next();
-        break;
-    case TOK_CUINT:
-        t = VT_INT | VT_UNSIGNED;
-        goto push_tokc;
-    case TOK_CLLONG:
-        t = VT_LLONG;
-	goto push_tokc;
-    case TOK_CULLONG:
-        t = VT_LLONG | VT_UNSIGNED;
-	goto push_tokc;
-    case TOK_CFLOAT:
-        t = VT_FLOAT;
-	goto push_tokc;
-    case TOK_CDOUBLE:
-        t = VT_DOUBLE;
-	goto push_tokc;
-    case TOK_CLDOUBLE:
-        t = VT_LDOUBLE;
-	goto push_tokc;
-    case TOK_CLONG:
-        t = (LONG_SIZE == 8 ? VT_LLONG : VT_INT) | VT_LONG;
-	goto push_tokc;
-    case TOK_CULONG:
-        t = (LONG_SIZE == 8 ? VT_LLONG : VT_INT) | VT_LONG | VT_UNSIGNED;
-	goto push_tokc;
-    case TOK___FUNCTION__:
-        if (!gnu_ext)
-            goto tok_identifier;
-        /* fall thru */
-    case TOK___FUNC__:
-        {
-            void *ptr;
-            int len;
-            /* special function name identifier */
-            len = strlen(funcname) + 1;
-            /* generate char[len] type */
-            type.t = VT_BYTE;
-            mk_pointer(&type);
-            type.t |= VT_ARRAY;
-            type.ref->c = len;
-            vpush_ref(&type, data_section, data_section->data_offset, len);
-            if (!NODATA_WANTED) {
-                ptr = section_ptr_add(data_section, len);
-                memcpy(ptr, funcname, len);
-            }
-            next();
-        }
-        break;
-    case TOK_LSTR:
-#ifdef TCC_TARGET_PE
-        t = VT_SHORT | VT_UNSIGNED;
-#else
-        t = VT_INT;
-#endif
-        goto str_init;
-    case TOK_STR:
-        /* string parsing */
-        t = VT_BYTE;
-        if (tcc_state->char_is_unsigned)
-            t = VT_BYTE | VT_UNSIGNED;
-    str_init:
-        if (tcc_state->warn_write_strings)
-            t |= VT_CONSTANT;
-        type.t = t;
-        mk_pointer(&type);
-        type.t |= VT_ARRAY;
-        memset(&ad, 0, sizeof(AttributeDef));
-        decl_initializer_alloc(&type, &ad, VT_CONST, 2, 0, 0);
-        break;
-    case '(':
-        next();
-        /* cast ? */
-        if (parse_btype(&type, &ad)) {
-            type_decl(&type, &ad, &n, TYPE_ABSTRACT);
-            skip(')');
-            /* check ISOC99 compound literal */
-            if (tok == '{') {
-                    /* data is allocated locally by default */
-                if (global_expr)
-                    r = VT_CONST;
-                else
-                    r = VT_LOCAL;
-                /* all except arrays are lvalues */
-                if (!(type.t & VT_ARRAY))
-                    r |= lvalue_type(type.t);
-                memset(&ad, 0, sizeof(AttributeDef));
-                decl_initializer_alloc(&type, &ad, r, 1, 0, 0);
-            } else {
-                if (sizeof_caller) {
-                    vpush(&type);
-                    return;
-                }
-                unary();
-                gen_cast(&type);
-            }
-        } else if (tok == '{') {
-	    int saved_nocode_wanted = nocode_wanted;
-            if (const_wanted)
-                tcc_error("expected constant");
-            /* save all registers */
-            save_regs(0);
-            /* statement expression : we do not accept break/continue
-               inside as GCC does.  We do retain the nocode_wanted state,
-	       as statement expressions can't ever be entered from the
-	       outside, so any reactivation of code emission (from labels
-	       or loop heads) can be disabled again after the end of it. */
-            block(NULL, NULL, 1);
-	    nocode_wanted = saved_nocode_wanted;
-            skip(')');
-        } else {
-            gexpr();
-            skip(')');
-        }
-        break;
-    case '*':
-        next();
-        unary();
-        indir();
-        break;
-    case '&':
-        next();
-        unary();
-        /* functions names must be treated as function pointers,
-           except for unary '&' and sizeof. Since we consider that
-           functions are not lvalues, we only have to handle it
-           there and in function calls. */
-        /* arrays can also be used although they are not lvalues */
-        if ((vtop->type.t & VT_BTYPE) != VT_FUNC &&
-            !(vtop->type.t & VT_ARRAY))
-            test_lvalue();
-        mk_pointer(&vtop->type);
-        gaddrof();
-        break;
-    case '!':
-        next();
-        unary();
-        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
-            gen_cast_s(VT_BOOL);
-            vtop->c.i = !vtop->c.i;
-        } else if ((vtop->r & VT_VALMASK) == VT_CMP)
-            vtop->c.i ^= 1;
-        else {
-            save_regs(1);
-            vseti(VT_JMP, gvtst(1, 0));
-        }
-        break;
-    case '~':
-        next();
-        unary();
-        vpushi(-1);
-        gen_op('^');
-        break;
-    case '+':
-        next();
-        unary();
-        if ((vtop->type.t & VT_BTYPE) == VT_PTR)
-            tcc_error("pointer not accepted for unary plus");
-        /* In order to force cast, we add zero, except for floating point
-	   where we really need an noop (otherwise -0.0 will be transformed
-	   into +0.0).  */
-	if (!is_float(vtop->type.t)) {
-	    vpushi(0);
-	    gen_op('+');
-	}
-        break;
-    case TOK_SIZEOF:
-    case TOK_ALIGNOF1:
-    case TOK_ALIGNOF2:
-        t = tok;
-        next();
-        in_sizeof++;
-        expr_type(&type, unary); /* Perform a in_sizeof = 0; */
-        s = vtop[1].sym; /* hack: accessing previous vtop */
-        size = type_size(&type, &align);
-        if (s && s->a.aligned)
-            align = 1 << (s->a.aligned - 1);
-        if (t == TOK_SIZEOF) {
-            if (!(type.t & VT_VLA)) {
-                if (size < 0)
-                    tcc_error("sizeof applied to an incomplete type");
-                vpushs(size);
-            } else {
-                vla_runtime_type_size(&type, &align);
-            }
-        } else {
-            vpushs(align);
-        }
-        vtop->type.t |= VT_UNSIGNED;
-        break;
-
-    case TOK_builtin_expect:
-	/* __builtin_expect is a no-op for now */
-	parse_builtin_params(0, "ee");
-	vpop();
-        break;
-    case TOK_builtin_types_compatible_p:
-	parse_builtin_params(0, "tt");
-	vtop[-1].type.t &= ~(VT_CONSTANT | VT_VOLATILE);
-	vtop[0].type.t &= ~(VT_CONSTANT | VT_VOLATILE);
-	n = is_compatible_types(&vtop[-1].type, &vtop[0].type);
-	vtop -= 2;
-	vpushi(n);
-        break;
-    case TOK_builtin_choose_expr:
-	{
-	    int64_t c;
-	    next();
-	    skip('(');
-	    c = expr_const64();
-	    skip(',');
-	    if (!c) {
-		nocode_wanted++;
-	    }
-	    expr_eq();
-	    if (!c) {
-		vpop();
-		nocode_wanted--;
-	    }
-	    skip(',');
-	    if (c) {
-		nocode_wanted++;
-	    }
-	    expr_eq();
-	    if (c) {
-		vpop();
-		nocode_wanted--;
-	    }
-	    skip(')');
-	}
-        break;
-    case TOK_builtin_constant_p:
-	parse_builtin_params(1, "e");
-	n = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
-	vtop--;
-	vpushi(n);
-        break;
-    case TOK_builtin_frame_address:
-    case TOK_builtin_return_address:
-        {
-            int tok1 = tok;
-            int level;
-            next();
-            skip('(');
-            if (tok != TOK_CINT) {
-                tcc_error("%s only takes positive integers",
-                          tok1 == TOK_builtin_return_address ?
-                          "__builtin_return_address" :
-                          "__builtin_frame_address");
-            }
-            level = (uint32_t)tokc.i;
-            next();
-            skip(')');
-            type.t = VT_VOID;
-            mk_pointer(&type);
-            vset(&type, VT_LOCAL, 0);       /* local frame */
-            while (level--) {
-                mk_pointer(&vtop->type);
-                indir();                    /* -> parent frame */
-            }
-            if (tok1 == TOK_builtin_return_address) {
-                // assume return address is just above frame pointer on stack
-                vpushi(PTR_SIZE);
-                gen_op('+');
-                mk_pointer(&vtop->type);
-                indir();
-            }
-        }
-        break;
-#ifdef TCC_TARGET_X86_64
-#ifdef TCC_TARGET_PE
-    case TOK_builtin_va_start:
-	parse_builtin_params(0, "ee");
-        r = vtop->r & VT_VALMASK;
-        if (r == VT_LLOCAL)
-            r = VT_LOCAL;
-        if (r != VT_LOCAL)
-            tcc_error("__builtin_va_start expects a local variable");
-        vtop->r = r;
-	vtop->type = char_pointer_type;
-	vtop->c.i += 8;
-	vstore();
-        break;
-#else
-    case TOK_builtin_va_arg_types:
-	parse_builtin_params(0, "t");
-	vpushi(classify_x86_64_va_arg(&vtop->type));
-	vswap();
-	vpop();
-        break;
-#endif
-#endif
-
-#ifdef TCC_TARGET_ARM64
-    case TOK___va_start: {
-	parse_builtin_params(0, "ee");
-        //xx check types
-        gen_va_start();
-        vpushi(0);
-        vtop->type.t = VT_VOID;
-        break;
-    }
-    case TOK___va_arg: {
-	parse_builtin_params(0, "et");
-	type = vtop->type;
-	vpop();
-        //xx check types
-        gen_va_arg(&type);
-        vtop->type = type;
-        break;
-    }
-    case TOK___arm64_clear_cache: {
-	parse_builtin_params(0, "ee");
-        gen_clear_cache();
-        vpushi(0);
-        vtop->type.t = VT_VOID;
-        break;
-    }
-#endif
-    /* pre operations */
-    case TOK_INC:
-    case TOK_DEC:
-        t = tok;
-        next();
-        unary();
-        inc(0, t);
-        break;
-    case '-':
-        next();
-        unary();
-        t = vtop->type.t & VT_BTYPE;
-	if (is_float(t)) {
-            /* In IEEE negate(x) isn't subtract(0,x), but rather
-	       subtract(-0, x).  */
-	    vpush(&vtop->type);
-	    if (t == VT_FLOAT)
-	        vtop->c.f = -1.0 * 0.0;
-	    else if (t == VT_DOUBLE)
-	        vtop->c.d = -1.0 * 0.0;
-	    else
-	        vtop->c.ld = -1.0 * 0.0;
-	} else
-	    vpushi(0);
-	vswap();
-	gen_op('-');
-        break;
-    case TOK_LAND:
-        if (!gnu_ext)
-            goto tok_identifier;
-        next();
-        /* allow to take the address of a label */
-        if (tok < TOK_UIDENT)
-            expect("label identifier");
-        s = label_find(tok);
-        if (!s) {
-            s = label_push(&global_label_stack, tok, LABEL_FORWARD);
-        } else {
-            if (s->r == LABEL_DECLARED)
-                s->r = LABEL_FORWARD;
-        }
-        if (!s->type.t) {
-            s->type.t = VT_VOID;
-            mk_pointer(&s->type);
-            s->type.t |= VT_STATIC;
-        }
-        vpushsym(&s->type, s);
-        next();
-        break;
-
-    case TOK_GENERIC:
-    {
-	CType controlling_type;
-	int has_default = 0;
-	int has_match = 0;
-	int learn = 0;
-	TokenString *str = NULL;
-
-	next();
-	skip('(');
-	expr_type(&controlling_type, expr_eq);
-	controlling_type.t &= ~(VT_CONSTANT | VT_VOLATILE | VT_ARRAY);
-	for (;;) {
-	    learn = 0;
-	    skip(',');
-	    if (tok == TOK_DEFAULT) {
-		if (has_default)
-		    tcc_error("too many 'default'");
-		has_default = 1;
-		if (!has_match)
-		    learn = 1;
-		next();
-	    } else {
-	        AttributeDef ad_tmp;
-		int itmp;
-	        CType cur_type;
-		parse_btype(&cur_type, &ad_tmp);
-		type_decl(&cur_type, &ad_tmp, &itmp, TYPE_ABSTRACT);
-		if (compare_types(&controlling_type, &cur_type, 0)) {
-		    if (has_match) {
-		      tcc_error("type match twice");
-		    }
-		    has_match = 1;
-		    learn = 1;
-		}
-	    }
-	    skip(':');
-	    if (learn) {
-		if (str)
-		    tok_str_free(str);
-		skip_or_save_block(&str);
-	    } else {
-		skip_or_save_block(NULL);
-	    }
-	    if (tok == ')')
-		break;
-	}
-	if (!str) {
-	    char buf[60];
-	    type_to_str(buf, sizeof buf, &controlling_type, NULL);
-	    tcc_error("type '%s' does not match any association", buf);
-	}
-	begin_macro(str, 1);
-	next();
-	expr_eq();
-	if (tok != TOK_EOF)
-	    expect(",");
-	end_macro();
-        next();
-	break;
-    }
-    // special qnan , snan and infinity values
-    case TOK___NAN__:
-        vpush64(VT_DOUBLE, 0x7ff8000000000000ULL);
-        next();
-        break;
-    case TOK___SNAN__:
-        vpush64(VT_DOUBLE, 0x7ff0000000000001ULL);
-        next();
-        break;
-    case TOK___INF__:
-        vpush64(VT_DOUBLE, 0x7ff0000000000000ULL);
-        next();
-        break;
-
-    default:
-    tok_identifier:
-        t = tok;
-        next();
-        if (t < TOK_UIDENT)
-            expect("identifier");
-        s = sym_find(t);
-        if (!s) {
-            const char *name = get_tok_str(t, NULL);
-            if (tok != '(')
-                tcc_error("'%s' undeclared", name);
-            /* for simple function calls, we tolerate undeclared
-               external reference to int() function */
-            if (tcc_state->warn_implicit_function_declaration
-#ifdef TCC_TARGET_PE
-                /* people must be warned about using undeclared WINAPI functions
-                   (which usually start with uppercase letter) */
-                || (name[0] >= 'A' && name[0] <= 'Z')
-#endif
-            )
-                tcc_warning("implicit declaration of function '%s'", name);
-            s = external_global_sym(t, &func_old_type, 0); 
-        }
-
-        r = s->r;
-        /* A symbol that has a register is a local register variable,
-           which starts out as VT_LOCAL value.  */
-        if ((r & VT_VALMASK) < VT_CONST)
-            r = (r & ~VT_VALMASK) | VT_LOCAL;
-
-        vset(&s->type, r, s->c);
-        /* Point to s as backpointer (even without r&VT_SYM).
-	   Will be used by at least the x86 inline asm parser for
-	   regvars.  */
-	vtop->sym = s;
-
-        if (r & VT_SYM) {
-            vtop->c.i = 0;
-        } else if (r == VT_CONST && IS_ENUM_VAL(s->type.t)) {
-            vtop->c.i = s->enum_val;
-        }
-        break;
-    }
-    
-    /* post operations */
-    while (1) {
-        if (tok == TOK_INC || tok == TOK_DEC) {
-            inc(1, tok);
-            next();
-        } else if (tok == '.' || tok == TOK_ARROW || tok == TOK_CDOUBLE) {
-            int qualifiers;
-            /* field */ 
-            if (tok == TOK_ARROW) 
-                indir();
-            qualifiers = vtop->type.t & (VT_CONSTANT | VT_VOLATILE);
-            test_lvalue();
-            gaddrof();
-            /* expect pointer on structure */
-            if ((vtop->type.t & VT_BTYPE) != VT_STRUCT)
-                expect("struct or union");
-            if (tok == TOK_CDOUBLE)
-                expect("field name");
-            next();
-            if (tok == TOK_CINT || tok == TOK_CUINT)
-                expect("field name");
-	    s = find_field(&vtop->type, tok);
-            if (!s)
-                tcc_error("field not found: %s",  get_tok_str(tok & ~SYM_FIELD, &tokc));
-            /* add field offset to pointer */
-            vtop->type = char_pointer_type; /* change type to 'char *' */
-            vpushi(s->c);
-            gen_op('+');
-            /* change type to field type, and set to lvalue */
-            vtop->type = s->type;
-            vtop->type.t |= qualifiers;
-            /* an array is never an lvalue */
-            if (!(vtop->type.t & VT_ARRAY)) {
-                vtop->r |= lvalue_type(vtop->type.t);
-#ifdef CONFIG_TCC_BCHECK
-                /* if bound checking, the referenced pointer must be checked */
-                if (tcc_state->do_bounds_check && (vtop->r & VT_VALMASK) != VT_LOCAL)
-                    vtop->r |= VT_MUSTBOUND;
-#endif
-            }
-            next();
-        } else if (tok == '[') {
-            next();
-            gexpr();
-            gen_op('+');
-            indir();
-            skip(']');
-        } else if (tok == '(') {
-            SValue ret;
-            Sym *sa;
-            int nb_args, ret_nregs, ret_align, regsize, variadic;
-
-            /* function call  */
-            if ((vtop->type.t & VT_BTYPE) != VT_FUNC) {
-                /* pointer test (no array accepted) */
-                if ((vtop->type.t & (VT_BTYPE | VT_ARRAY)) == VT_PTR) {
-                    vtop->type = *pointed_type(&vtop->type);
-                    if ((vtop->type.t & VT_BTYPE) != VT_FUNC)
-                        goto error_func;
-                } else {
-                error_func:
-                    expect("function pointer");
-                }
-            } else {
-                vtop->r &= ~VT_LVAL; /* no lvalue */
-            }
-            /* get return type */
-            s = vtop->type.ref;
-            next();
-            sa = s->next; /* first parameter */
-            nb_args = regsize = 0;
-            ret.r2 = VT_CONST;
-            /* compute first implicit argument if a structure is returned */
-            if ((s->type.t & VT_BTYPE) == VT_STRUCT) {
-                variadic = (s->f.func_type == FUNC_ELLIPSIS);
-                ret_nregs = gfunc_sret(&s->type, variadic, &ret.type,
-                                       &ret_align, &regsize);
-                if (!ret_nregs) {
-                    /* get some space for the returned structure */
-                    size = type_size(&s->type, &align);
-#ifdef TCC_TARGET_ARM64
-                /* On arm64, a small struct is return in registers.
-                   It is much easier to write it to memory if we know
-                   that we are allowed to write some extra bytes, so
-                   round the allocated space up to a power of 2: */
-                if (size < 16)
-                    while (size & (size - 1))
-                        size = (size | (size - 1)) + 1;
-#endif
-                    loc = (loc - size) & -align;
-                    ret.type = s->type;
-                    ret.r = VT_LOCAL | VT_LVAL;
-                    /* pass it as 'int' to avoid structure arg passing
-                       problems */
-                    vseti(VT_LOCAL, loc);
-                    ret.c = vtop->c;
-                    nb_args++;
-                }
-            } else {
-                ret_nregs = 1;
-                ret.type = s->type;
-            }
-
-            if (ret_nregs) {
-                /* return in register */
-                if (is_float(ret.type.t)) {
-                    ret.r = reg_fret(ret.type.t);
-#ifdef TCC_TARGET_X86_64
-                    if ((ret.type.t & VT_BTYPE) == VT_QFLOAT)
-                      ret.r2 = REG_QRET;
-#endif
-                } else {
-#ifndef TCC_TARGET_ARM64
-#ifdef TCC_TARGET_X86_64
-                    if ((ret.type.t & VT_BTYPE) == VT_QLONG)
-#else
-                    if ((ret.type.t & VT_BTYPE) == VT_LLONG)
-#endif
-                        ret.r2 = REG_LRET;
-#endif
-                    ret.r = REG_IRET;
-                }
-                ret.c.i = 0;
-            }
-            if (tok != ')') {
-                for(;;) {
-                    expr_eq();
-                    gfunc_param_typed(s, sa);
-                    nb_args++;
-                    if (sa)
-                        sa = sa->next;
-                    if (tok == ')')
-                        break;
-                    skip(',');
-                }
-            }
-            if (sa)
-                tcc_error("too few arguments to function");
-            skip(')');
-            gfunc_call(nb_args);
-
-            /* return value */
-            for (r = ret.r + ret_nregs + !ret_nregs; r-- > ret.r;) {
-                vsetc(&ret.type, r, &ret.c);
-                vtop->r2 = ret.r2; /* Loop only happens when r2 is VT_CONST */
-            }
-
-            /* handle packed struct return */
-            if (((s->type.t & VT_BTYPE) == VT_STRUCT) && ret_nregs) {
-                int addr, offset;
-
-                size = type_size(&s->type, &align);
-		/* We're writing whole regs often, make sure there's enough
-		   space.  Assume register size is power of 2.  */
-		if (regsize > align)
-		  align = regsize;
-                loc = (loc - size) & -align;
-                addr = loc;
-                offset = 0;
-                for (;;) {
-                    vset(&ret.type, VT_LOCAL | VT_LVAL, addr + offset);
-                    vswap();
-                    vstore();
-                    vtop--;
-                    if (--ret_nregs == 0)
-                        break;
-                    offset += regsize;
-                }
-                vset(&s->type, VT_LOCAL | VT_LVAL, addr);
-            }
-        } else {
-            break;
-        }
-    }
-}
-
-ST_FUNC void expr_prod(void)
-{
-    int t;
-
-    unary();
-    while (tok == '*' || tok == '/' || tok == '%') {
-        t = tok;
-        next();
-        unary();
-        gen_op(t);
-    }
-}
-
-ST_FUNC void expr_sum(void)
-{
-    int t;
-
-    expr_prod();
-    while (tok == '+' || tok == '-') {
-        t = tok;
-        next();
-        expr_prod();
-        gen_op(t);
-    }
-}
-
-static void expr_shift(void)
-{
-    int t;
-
-    expr_sum();
-    while (tok == TOK_SHL || tok == TOK_SAR) {
-        t = tok;
-        next();
-        expr_sum();
-        gen_op(t);
-    }
-}
-
-static void expr_cmp(void)
-{
-    int t;
-
-    expr_shift();
-    while ((tok >= TOK_ULE && tok <= TOK_GT) ||
-           tok == TOK_ULT || tok == TOK_UGE) {
-        t = tok;
-        next();
-        expr_shift();
-        gen_op(t);
-    }
-}
-
-static void expr_cmpeq(void)
-{
-    int t;
-
-    expr_cmp();
-    while (tok == TOK_EQ || tok == TOK_NE) {
-        t = tok;
-        next();
-        expr_cmp();
-        gen_op(t);
-    }
-}
-
-static void expr_and(void)
-{
-    expr_cmpeq();
-    while (tok == '&') {
-        next();
-        expr_cmpeq();
-        gen_op('&');
-    }
-}
-
-static void expr_xor(void)
-{
-    expr_and();
-    while (tok == '^') {
-        next();
-        expr_and();
-        gen_op('^');
-    }
-}
-
-static void expr_or(void)
-{
-    expr_xor();
-    while (tok == '|') {
-        next();
-        expr_xor();
-        gen_op('|');
-    }
-}
-
-static void expr_land(void)
-{
-    expr_or();
-    if (tok == TOK_LAND) {
-	int t = 0;
-	for(;;) {
-	    if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
-                gen_cast_s(VT_BOOL);
-		if (vtop->c.i) {
-		    vpop();
-		} else {
-		    nocode_wanted++;
-		    while (tok == TOK_LAND) {
-			next();
-			expr_or();
-			vpop();
-		    }
-		    nocode_wanted--;
-		    if (t)
-		      gsym(t);
-		    gen_cast_s(VT_INT);
-		    break;
-		}
-	    } else {
-		if (!t)
-		  save_regs(1);
-		t = gvtst(1, t);
-	    }
-	    if (tok != TOK_LAND) {
-		if (t)
-		  vseti(VT_JMPI, t);
-		else
-		  vpushi(1);
-		break;
-	    }
-	    next();
-	    expr_or();
-	}
-    }
-}
-
-static void expr_lor(void)
-{
-    expr_land();
-    if (tok == TOK_LOR) {
-	int t = 0;
-	for(;;) {
-	    if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
-                gen_cast_s(VT_BOOL);
-		if (!vtop->c.i) {
-		    vpop();
-		} else {
-		    nocode_wanted++;
-		    while (tok == TOK_LOR) {
-			next();
-			expr_land();
-			vpop();
-		    }
-		    nocode_wanted--;
-		    if (t)
-		      gsym(t);
-		    gen_cast_s(VT_INT);
-		    break;
-		}
-	    } else {
-		if (!t)
-		  save_regs(1);
-		t = gvtst(0, t);
-	    }
-	    if (tok != TOK_LOR) {
-		if (t)
-		  vseti(VT_JMP, t);
-		else
-		  vpushi(0);
-		break;
-	    }
-	    next();
-	    expr_land();
-	}
-    }
-}
-
-/* Assuming vtop is a value used in a conditional context
-   (i.e. compared with zero) return 0 if it's false, 1 if
-   true and -1 if it can't be statically determined.  */
-static int condition_3way(void)
-{
-    int c = -1;
-    if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST &&
-	(!(vtop->r & VT_SYM) || !vtop->sym->a.weak)) {
-	vdup();
-        gen_cast_s(VT_BOOL);
-	c = vtop->c.i;
-	vpop();
-    }
-    return c;
-}
-
-static void expr_cond(void)
-{
-    int tt, u, r1, r2, rc, t1, t2, bt1, bt2, islv, c, g;
-    SValue sv;
-    CType type, type1, type2;
-
-    expr_lor();
-    if (tok == '?') {
-        next();
-	c = condition_3way();
-        g = (tok == ':' && gnu_ext);
-        if (c < 0) {
-            /* needed to avoid having different registers saved in
-               each branch */
-            if (is_float(vtop->type.t)) {
-                rc = RC_FLOAT;
-#ifdef TCC_TARGET_X86_64
-                if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
-                    rc = RC_ST0;
-                }
-#endif
-            } else
-                rc = RC_INT;
-            gv(rc);
-            save_regs(1);
-            if (g)
-                gv_dup();
-            tt = gvtst(1, 0);
-
-        } else {
-            if (!g)
-                vpop();
-            tt = 0;
-        }
-
-        if (1) {
-            if (c == 0)
-                nocode_wanted++;
-            if (!g)
-                gexpr();
-
-            type1 = vtop->type;
-            sv = *vtop; /* save value to handle it later */
-            vtop--; /* no vpop so that FP stack is not flushed */
-            skip(':');
-
-            u = 0;
-            if (c < 0)
-                u = gjmp(0);
-            gsym(tt);
-
-            if (c == 0)
-                nocode_wanted--;
-            if (c == 1)
-                nocode_wanted++;
-            expr_cond();
-            if (c == 1)
-                nocode_wanted--;
-
-            type2 = vtop->type;
-            t1 = type1.t;
-            bt1 = t1 & VT_BTYPE;
-            t2 = type2.t;
-            bt2 = t2 & VT_BTYPE;
-            type.ref = NULL;
-
-            /* cast operands to correct type according to ISOC rules */
-            if (is_float(bt1) || is_float(bt2)) {
-                if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) {
-                    type.t = VT_LDOUBLE;
-
-                } else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) {
-                    type.t = VT_DOUBLE;
-                } else {
-                    type.t = VT_FLOAT;
-                }
-            } else if (bt1 == VT_LLONG || bt2 == VT_LLONG) {
-                /* cast to biggest op */
-                type.t = VT_LLONG | VT_LONG;
-                if (bt1 == VT_LLONG)
-                    type.t &= t1;
-                if (bt2 == VT_LLONG)
-                    type.t &= t2;
-                /* convert to unsigned if it does not fit in a long long */
-                if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED) ||
-                    (t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED))
-                    type.t |= VT_UNSIGNED;
-            } else if (bt1 == VT_PTR || bt2 == VT_PTR) {
-		/* If one is a null ptr constant the result type
-		   is the other.  */
-		if (is_null_pointer (vtop))
-		  type = type1;
-		else if (is_null_pointer (&sv))
-		  type = type2;
-                /* XXX: test pointer compatibility, C99 has more elaborate
-		   rules here.  */
-		else
-		  type = type1;
-            } else if (bt1 == VT_FUNC || bt2 == VT_FUNC) {
-                /* XXX: test function pointer compatibility */
-                type = bt1 == VT_FUNC ? type1 : type2;
-            } else if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) {
-                /* XXX: test structure compatibility */
-                type = bt1 == VT_STRUCT ? type1 : type2;
-            } else if (bt1 == VT_VOID || bt2 == VT_VOID) {
-                /* NOTE: as an extension, we accept void on only one side */
-                type.t = VT_VOID;
-            } else {
-                /* integer operations */
-                type.t = VT_INT | (VT_LONG & (t1 | t2));
-                /* convert to unsigned if it does not fit in an integer */
-                if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED) ||
-                    (t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED))
-                    type.t |= VT_UNSIGNED;
-            }
-            /* keep structs lvalue by transforming `(expr ? a : b)` to `*(expr ? &a : &b)` so
-               that `(expr ? a : b).mem` does not error  with "lvalue expected" */
-            islv = (vtop->r & VT_LVAL) && (sv.r & VT_LVAL) && VT_STRUCT == (type.t & VT_BTYPE);
-            islv &= c < 0;
-
-            /* now we convert second operand */
-            if (c != 1) {
-                gen_cast(&type);
-                if (islv) {
-                    mk_pointer(&vtop->type);
-                    gaddrof();
-                } else if (VT_STRUCT == (vtop->type.t & VT_BTYPE))
-                    gaddrof();
-            }
-
-            rc = RC_INT;
-            if (is_float(type.t)) {
-                rc = RC_FLOAT;
-#ifdef TCC_TARGET_X86_64
-                if ((type.t & VT_BTYPE) == VT_LDOUBLE) {
-                    rc = RC_ST0;
-                }
-#endif
-            } else if ((type.t & VT_BTYPE) == VT_LLONG) {
-                /* for long longs, we use fixed registers to avoid having
-                   to handle a complicated move */
-                rc = RC_IRET;
-            }
-
-            tt = r2 = 0;
-            if (c < 0) {
-                r2 = gv(rc);
-                tt = gjmp(0);
-            }
-            gsym(u);
-
-            /* this is horrible, but we must also convert first
-               operand */
-            if (c != 0) {
-                *vtop = sv;
-                gen_cast(&type);
-                if (islv) {
-                    mk_pointer(&vtop->type);
-                    gaddrof();
-                } else if (VT_STRUCT == (vtop->type.t & VT_BTYPE))
-                    gaddrof();
-            }
-
-            if (c < 0) {
-                r1 = gv(rc);
-                move_reg(r2, r1, type.t);
-                vtop->r = r2;
-                gsym(tt);
-                if (islv)
-                    indir();
-            }
-        }
-    }
-}
-
-static void expr_eq(void)
-{
-    int t;
-    
-    expr_cond();
-    if (tok == '=' ||
-        (tok >= TOK_A_MOD && tok <= TOK_A_DIV) ||
-        tok == TOK_A_XOR || tok == TOK_A_OR ||
-        tok == TOK_A_SHL || tok == TOK_A_SAR) {
-        test_lvalue();
-        t = tok;
-        next();
-        if (t == '=') {
-            expr_eq();
-        } else {
-            vdup();
-            expr_eq();
-            gen_op(t & 0x7f);
-        }
-        vstore();
-    }
-}
-
-ST_FUNC void gexpr(void)
-{
-    while (1) {
-        expr_eq();
-        if (tok != ',')
-            break;
-        vpop();
-        next();
-    }
-}
-
-/* parse a constant expression and return value in vtop.  */
-static void expr_const1(void)
-{
-    const_wanted++;
-    nocode_wanted++;
-    expr_cond();
-    nocode_wanted--;
-    const_wanted--;
-}
-
-/* parse an integer constant and return its value. */
-static inline int64_t expr_const64(void)
-{
-    int64_t c;
-    expr_const1();
-    if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
-        expect("constant expression");
-    c = vtop->c.i;
-    vpop();
-    return c;
-}
-
-/* parse an integer constant and return its value.
-   Complain if it doesn't fit 32bit (signed or unsigned).  */
-ST_FUNC int expr_const(void)
-{
-    int c;
-    int64_t wc = expr_const64();
-    c = wc;
-    if (c != wc && (unsigned)c != wc)
-        tcc_error("constant exceeds 32 bit");
-    return c;
-}
-
-/* return the label token if current token is a label, otherwise
-   return zero */
-static int is_label(void)
-{
-    int last_tok;
-
-    /* fast test first */
-    if (tok < TOK_UIDENT)
-        return 0;
-    /* no need to save tokc because tok is an identifier */
-    last_tok = tok;
-    next();
-    if (tok == ':') {
-        return last_tok;
-    } else {
-        unget_tok(last_tok);
-        return 0;
-    }
-}
-
-#ifndef TCC_TARGET_ARM64
-static void gfunc_return(CType *func_type)
-{
-    if ((func_type->t & VT_BTYPE) == VT_STRUCT) {
-        CType type, ret_type;
-        int ret_align, ret_nregs, regsize;
-        ret_nregs = gfunc_sret(func_type, func_var, &ret_type,
-                               &ret_align, &regsize);
-        if (0 == ret_nregs) {
-            /* if returning structure, must copy it to implicit
-               first pointer arg location */
-            type = *func_type;
-            mk_pointer(&type);
-            vset(&type, VT_LOCAL | VT_LVAL, func_vc);
-            indir();
-            vswap();
-            /* copy structure value to pointer */
-            vstore();
-        } else {
-            /* returning structure packed into registers */
-            int r, size, addr, align;
-            size = type_size(func_type,&align);
-            if ((vtop->r != (VT_LOCAL | VT_LVAL) ||
-                 (vtop->c.i & (ret_align-1)))
-                && (align & (ret_align-1))) {
-                loc = (loc - size) & -ret_align;
-                addr = loc;
-                type = *func_type;
-                vset(&type, VT_LOCAL | VT_LVAL, addr);
-                vswap();
-                vstore();
-                vpop();
-                vset(&ret_type, VT_LOCAL | VT_LVAL, addr);
-            }
-            vtop->type = ret_type;
-            if (is_float(ret_type.t))
-                r = rc_fret(ret_type.t);
-            else
-                r = RC_IRET;
-
-            if (ret_nregs == 1)
-                gv(r);
-            else {
-                for (;;) {
-                    vdup();
-                    gv(r);
-                    vpop();
-                    if (--ret_nregs == 0)
-                      break;
-                    /* We assume that when a structure is returned in multiple
-                       registers, their classes are consecutive values of the
-                       suite s(n) = 2^n */
-                    r <<= 1;
-                    vtop->c.i += regsize;
-                }
-            }
-        }
-    } else if (is_float(func_type->t)) {
-        gv(rc_fret(func_type->t));
-    } else {
-        gv(RC_IRET);
-    }
-    vtop--; /* NOT vpop() because on x86 it would flush the fp stack */
-}
-#endif
-
-static int case_cmp(const void *pa, const void *pb)
-{
-    int64_t a = (*(struct case_t**) pa)->v1;
-    int64_t b = (*(struct case_t**) pb)->v1;
-    return a < b ? -1 : a > b;
-}
-
-static void gcase(struct case_t **base, int len, int *bsym)
-{
-    struct case_t *p;
-    int e;
-    int ll = (vtop->type.t & VT_BTYPE) == VT_LLONG;
-    gv(RC_INT);
-    while (len > 4) {
-        /* binary search */
-        p = base[len/2];
-        vdup();
-	if (ll)
-	    vpushll(p->v2);
-	else
-	    vpushi(p->v2);
-        gen_op(TOK_LE);
-        e = gtst(1, 0);
-        vdup();
-	if (ll)
-	    vpushll(p->v1);
-	else
-	    vpushi(p->v1);
-        gen_op(TOK_GE);
-        gtst_addr(0, p->sym); /* v1 <= x <= v2 */
-        /* x < v1 */
-        gcase(base, len/2, bsym);
-        if (cur_switch->def_sym)
-            gjmp_addr(cur_switch->def_sym);
-        else
-            *bsym = gjmp(*bsym);
-        /* x > v2 */
-        gsym(e);
-        e = len/2 + 1;
-        base += e; len -= e;
-    }
-    /* linear scan */
-    while (len--) {
-        p = *base++;
-        vdup();
-	if (ll)
-	    vpushll(p->v2);
-	else
-	    vpushi(p->v2);
-        if (p->v1 == p->v2) {
-            gen_op(TOK_EQ);
-            gtst_addr(0, p->sym);
-        } else {
-            gen_op(TOK_LE);
-            e = gtst(1, 0);
-            vdup();
-	    if (ll)
-	        vpushll(p->v1);
-	    else
-	        vpushi(p->v1);
-            gen_op(TOK_GE);
-            gtst_addr(0, p->sym);
-            gsym(e);
-        }
-    }
-}
-
-static void block(int *bsym, int *csym, int is_expr)
-{
-    int a, b, c, d, cond;
-    Sym *s;
-
-    /* generate line number info */
-    if (tcc_state->do_debug)
-        tcc_debug_line(tcc_state);
-
-    if (is_expr) {
-        /* default return value is (void) */
-        vpushi(0);
-        vtop->type.t = VT_VOID;
-    }
-
-    if (tok == TOK_IF) {
-        /* if test */
-	int saved_nocode_wanted = nocode_wanted;
-        next();
-        skip('(');
-        gexpr();
-        skip(')');
-	cond = condition_3way();
-        if (cond == 1)
-            a = 0, vpop();
-        else
-            a = gvtst(1, 0);
-        if (cond == 0)
-	    nocode_wanted |= 0x20000000;
-        block(bsym, csym, 0);
-	if (cond != 1)
-	    nocode_wanted = saved_nocode_wanted;
-        c = tok;
-        if (c == TOK_ELSE) {
-            next();
-            d = gjmp(0);
-            gsym(a);
-	    if (cond == 1)
-	        nocode_wanted |= 0x20000000;
-            block(bsym, csym, 0);
-            gsym(d); /* patch else jmp */
-	    if (cond != 0)
-		nocode_wanted = saved_nocode_wanted;
-        } else
-            gsym(a);
-    } else if (tok == TOK_WHILE) {
-	int saved_nocode_wanted;
-	nocode_wanted &= ~0x20000000;
-        next();
-        d = ind;
-        vla_sp_restore();
-        skip('(');
-        gexpr();
-        skip(')');
-        a = gvtst(1, 0);
-        b = 0;
-        ++local_scope;
-	saved_nocode_wanted = nocode_wanted;
-        block(&a, &b, 0);
-	nocode_wanted = saved_nocode_wanted;
-        --local_scope;
-        gjmp_addr(d);
-        gsym(a);
-        gsym_addr(b, d);
-    } else if (tok == '{') {
-        Sym *llabel;
-        int block_vla_sp_loc = vla_sp_loc, saved_vlas_in_scope = vlas_in_scope;
-
-        next();
-        /* record local declaration stack position */
-        s = local_stack;
-        llabel = local_label_stack;
-        ++local_scope;
-        
-        /* handle local labels declarations */
-        if (tok == TOK_LABEL) {
-            next();
-            for(;;) {
-                if (tok < TOK_UIDENT)
-                    expect("label identifier");
-                label_push(&local_label_stack, tok, LABEL_DECLARED);
-                next();
-                if (tok == ',') {
-                    next();
-                } else {
-                    skip(';');
-                    break;
-                }
-            }
-        }
-        while (tok != '}') {
-	    if ((a = is_label()))
-		unget_tok(a);
-	    else
-	        decl(VT_LOCAL);
-            if (tok != '}') {
-                if (is_expr)
-                    vpop();
-                block(bsym, csym, is_expr);
-            }
-        }
-        /* pop locally defined labels */
-        label_pop(&local_label_stack, llabel, is_expr);
-        /* pop locally defined symbols */
-        --local_scope;
-	/* In the is_expr case (a statement expression is finished here),
-	   vtop might refer to symbols on the local_stack.  Either via the
-	   type or via vtop->sym.  We can't pop those nor any that in turn
-	   might be referred to.  To make it easier we don't roll back
-	   any symbols in that case; some upper level call to block() will
-	   do that.  We do have to remove such symbols from the lookup
-	   tables, though.  sym_pop will do that.  */
-	sym_pop(&local_stack, s, is_expr);
-
-        /* Pop VLA frames and restore stack pointer if required */
-        if (vlas_in_scope > saved_vlas_in_scope) {
-            vla_sp_loc = saved_vlas_in_scope ? block_vla_sp_loc : vla_sp_root_loc;
-            vla_sp_restore();
-        }
-        vlas_in_scope = saved_vlas_in_scope;
-        
-        next();
-    } else if (tok == TOK_RETURN) {
-        next();
-        if (tok != ';') {
-            gexpr();
-            gen_assign_cast(&func_vt);
-            if ((func_vt.t & VT_BTYPE) == VT_VOID)
-                vtop--;
-            else
-                gfunc_return(&func_vt);
-        }
-        skip(';');
-        /* jump unless last stmt in top-level block */
-        if (tok != '}' || local_scope != 1)
-            rsym = gjmp(rsym);
-	nocode_wanted |= 0x20000000;
-    } else if (tok == TOK_BREAK) {
-        /* compute jump */
-        if (!bsym)
-            tcc_error("cannot break");
-        *bsym = gjmp(*bsym);
-        next();
-        skip(';');
-	nocode_wanted |= 0x20000000;
-    } else if (tok == TOK_CONTINUE) {
-        /* compute jump */
-        if (!csym)
-            tcc_error("cannot continue");
-        vla_sp_restore_root();
-        *csym = gjmp(*csym);
-        next();
-        skip(';');
-    } else if (tok == TOK_FOR) {
-        int e;
-	int saved_nocode_wanted;
-	nocode_wanted &= ~0x20000000;
-        next();
-        skip('(');
-        s = local_stack;
-        ++local_scope;
-        if (tok != ';') {
-            /* c99 for-loop init decl? */
-            if (!decl0(VT_LOCAL, 1, NULL)) {
-                /* no, regular for-loop init expr */
-                gexpr();
-                vpop();
-            }
-        }
-        skip(';');
-        d = ind;
-        c = ind;
-        vla_sp_restore();
-        a = 0;
-        b = 0;
-        if (tok != ';') {
-            gexpr();
-            a = gvtst(1, 0);
-        }
-        skip(';');
-        if (tok != ')') {
-            e = gjmp(0);
-            c = ind;
-            vla_sp_restore();
-            gexpr();
-            vpop();
-            gjmp_addr(d);
-            gsym(e);
-        }
-        skip(')');
-	saved_nocode_wanted = nocode_wanted;
-        block(&a, &b, 0);
-	nocode_wanted = saved_nocode_wanted;
-        gjmp_addr(c);
-        gsym(a);
-        gsym_addr(b, c);
-        --local_scope;
-        sym_pop(&local_stack, s, 0);
-
-    } else 
-    if (tok == TOK_DO) {
-	int saved_nocode_wanted;
-	nocode_wanted &= ~0x20000000;
-        next();
-        a = 0;
-        b = 0;
-        d = ind;
-        vla_sp_restore();
-	saved_nocode_wanted = nocode_wanted;
-        block(&a, &b, 0);
-        skip(TOK_WHILE);
-        skip('(');
-        gsym(b);
-	gexpr();
-	c = gvtst(0, 0);
-	gsym_addr(c, d);
-	nocode_wanted = saved_nocode_wanted;
-        skip(')');
-        gsym(a);
-        skip(';');
-    } else
-    if (tok == TOK_SWITCH) {
-        struct switch_t *saved, sw;
-	int saved_nocode_wanted = nocode_wanted;
-	SValue switchval;
-        next();
-        skip('(');
-        gexpr();
-        skip(')');
-	switchval = *vtop--;
-        a = 0;
-        b = gjmp(0); /* jump to first case */
-        sw.p = NULL; sw.n = 0; sw.def_sym = 0;
-        saved = cur_switch;
-        cur_switch = &sw;
-        block(&a, csym, 0);
-	nocode_wanted = saved_nocode_wanted;
-        a = gjmp(a); /* add implicit break */
-        /* case lookup */
-        gsym(b);
-        qsort(sw.p, sw.n, sizeof(void*), case_cmp);
-        for (b = 1; b < sw.n; b++)
-            if (sw.p[b - 1]->v2 >= sw.p[b]->v1)
-                tcc_error("duplicate case value");
-        /* Our switch table sorting is signed, so the compared
-           value needs to be as well when it's 64bit.  */
-        if ((switchval.type.t & VT_BTYPE) == VT_LLONG)
-            switchval.type.t &= ~VT_UNSIGNED;
-        vpushv(&switchval);
-        gcase(sw.p, sw.n, &a);
-        vpop();
-        if (sw.def_sym)
-          gjmp_addr(sw.def_sym);
-        dynarray_reset(&sw.p, &sw.n);
-        cur_switch = saved;
-        /* break label */
-        gsym(a);
-    } else
-    if (tok == TOK_CASE) {
-        struct case_t *cr = tcc_malloc(sizeof(struct case_t));
-        if (!cur_switch)
-            expect("switch");
-	nocode_wanted &= ~0x20000000;
-        next();
-        cr->v1 = cr->v2 = expr_const64();
-        if (gnu_ext && tok == TOK_DOTS) {
-            next();
-            cr->v2 = expr_const64();
-            if (cr->v2 < cr->v1)
-                tcc_warning("empty case range");
-        }
-        cr->sym = ind;
-        dynarray_add(&cur_switch->p, &cur_switch->n, cr);
-        skip(':');
-        is_expr = 0;
-        goto block_after_label;
-    } else 
-    if (tok == TOK_DEFAULT) {
-        next();
-        skip(':');
-        if (!cur_switch)
-            expect("switch");
-        if (cur_switch->def_sym)
-            tcc_error("too many 'default'");
-        cur_switch->def_sym = ind;
-        is_expr = 0;
-        goto block_after_label;
-    } else
-    if (tok == TOK_GOTO) {
-        next();
-        if (tok == '*' && gnu_ext) {
-            /* computed goto */
-            next();
-            gexpr();
-            if ((vtop->type.t & VT_BTYPE) != VT_PTR)
-                expect("pointer");
-            ggoto();
-        } else if (tok >= TOK_UIDENT) {
-            s = label_find(tok);
-            /* put forward definition if needed */
-            if (!s) {
-                s = label_push(&global_label_stack, tok, LABEL_FORWARD);
-            } else {
-                if (s->r == LABEL_DECLARED)
-                    s->r = LABEL_FORWARD;
-            }
-            vla_sp_restore_root();
-	    if (s->r & LABEL_FORWARD)
-                s->jnext = gjmp(s->jnext);
-            else
-                gjmp_addr(s->jnext);
-            next();
-        } else {
-            expect("label identifier");
-        }
-        skip(';');
-    } else if (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3) {
-        asm_instr();
-    } else {
-        b = is_label();
-        if (b) {
-            /* label case */
-	    next();
-            s = label_find(b);
-            if (s) {
-                if (s->r == LABEL_DEFINED)
-                    tcc_error("duplicate label '%s'", get_tok_str(s->v, NULL));
-                gsym(s->jnext);
-                s->r = LABEL_DEFINED;
-            } else {
-                s = label_push(&global_label_stack, b, LABEL_DEFINED);
-            }
-            s->jnext = ind;
-            vla_sp_restore();
-            /* we accept this, but it is a mistake */
-        block_after_label:
-	    nocode_wanted &= ~0x20000000;
-            if (tok == '}') {
-                tcc_warning("deprecated use of label at end of compound statement");
-            } else {
-                if (is_expr)
-                    vpop();
-                block(bsym, csym, is_expr);
-            }
-        } else {
-            /* expression case */
-            if (tok != ';') {
-                if (is_expr) {
-                    vpop();
-                    gexpr();
-                } else {
-                    gexpr();
-                    vpop();
-                }
-            }
-            skip(';');
-        }
-    }
-}
-
-/* This skips over a stream of tokens containing balanced {} and ()
-   pairs, stopping at outer ',' ';' and '}' (or matching '}' if we started
-   with a '{').  If STR then allocates and stores the skipped tokens
-   in *STR.  This doesn't check if () and {} are nested correctly,
-   i.e. "({)}" is accepted.  */
-static void skip_or_save_block(TokenString **str)
-{
-    int braces = tok == '{';
-    int level = 0;
-    if (str)
-      *str = tok_str_alloc();
-
-    while ((level > 0 || (tok != '}' && tok != ',' && tok != ';' && tok != ')'))) {
-	int t;
-	if (tok == TOK_EOF) {
-	     if (str || level > 0)
-	       tcc_error("unexpected end of file");
-	     else
-	       break;
-	}
-	if (str)
-	  tok_str_add_tok(*str);
-	t = tok;
-	next();
-	if (t == '{' || t == '(') {
-	    level++;
-	} else if (t == '}' || t == ')') {
-	    level--;
-	    if (level == 0 && braces && t == '}')
-	      break;
-	}
-    }
-    if (str) {
-	tok_str_add(*str, -1);
-	tok_str_add(*str, 0);
-    }
-}
-
-#define EXPR_CONST 1
-#define EXPR_ANY   2
-
-static void parse_init_elem(int expr_type)
-{
-    int saved_global_expr;
-    switch(expr_type) {
-    case EXPR_CONST:
-        /* compound literals must be allocated globally in this case */
-        saved_global_expr = global_expr;
-        global_expr = 1;
-        expr_const1();
-        global_expr = saved_global_expr;
-        /* NOTE: symbols are accepted, as well as lvalue for anon symbols
-	   (compound literals).  */
-        if (((vtop->r & (VT_VALMASK | VT_LVAL)) != VT_CONST
-	     && ((vtop->r & (VT_SYM|VT_LVAL)) != (VT_SYM|VT_LVAL)
-		 || vtop->sym->v < SYM_FIRST_ANOM))
-#ifdef TCC_TARGET_PE
-                 || ((vtop->r & VT_SYM) && vtop->sym->a.dllimport)
-#endif
-            )
-            tcc_error("initializer element is not constant");
-        break;
-    case EXPR_ANY:
-        expr_eq();
-        break;
-    }
-}
-
-/* put zeros for variable based init */
-static void init_putz(Section *sec, unsigned long c, int size)
-{
-    if (sec) {
-        /* nothing to do because globals are already set to zero */
-    } else {
-        vpush_global_sym(&func_old_type, TOK_memset);
-        vseti(VT_LOCAL, c);
-#ifdef TCC_TARGET_ARM
-        vpushs(size);
-        vpushi(0);
-#else
-        vpushi(0);
-        vpushs(size);
-#endif
-        gfunc_call(3);
-    }
-}
-
-/* t is the array or struct type. c is the array or struct
-   address. cur_field is the pointer to the current
-   field, for arrays the 'c' member contains the current start
-   index.  'size_only' is true if only size info is needed (only used
-   in arrays).  al contains the already initialized length of the
-   current container (starting at c).  This returns the new length of that.  */
-static int decl_designator(CType *type, Section *sec, unsigned long c,
-                           Sym **cur_field, int size_only, int al)
-{
-    Sym *s, *f;
-    int index, index_last, align, l, nb_elems, elem_size;
-    unsigned long corig = c;
-
-    elem_size = 0;
-    nb_elems = 1;
-    if (gnu_ext && (l = is_label()) != 0)
-        goto struct_field;
-    /* NOTE: we only support ranges for last designator */
-    while (nb_elems == 1 && (tok == '[' || tok == '.')) {
-        if (tok == '[') {
-            if (!(type->t & VT_ARRAY))
-                expect("array type");
-            next();
-            index = index_last = expr_const();
-            if (tok == TOK_DOTS && gnu_ext) {
-                next();
-                index_last = expr_const();
-            }
-            skip(']');
-            s = type->ref;
-	    if (index < 0 || (s->c >= 0 && index_last >= s->c) ||
-		index_last < index)
-	        tcc_error("invalid index");
-            if (cur_field)
-		(*cur_field)->c = index_last;
-            type = pointed_type(type);
-            elem_size = type_size(type, &align);
-            c += index * elem_size;
-            nb_elems = index_last - index + 1;
-        } else {
-            next();
-            l = tok;
-        struct_field:
-            next();
-            if ((type->t & VT_BTYPE) != VT_STRUCT)
-                expect("struct/union type");
-	    f = find_field(type, l);
-            if (!f)
-                expect("field");
-            if (cur_field)
-                *cur_field = f;
-	    type = &f->type;
-            c += f->c;
-        }
-        cur_field = NULL;
-    }
-    if (!cur_field) {
-        if (tok == '=') {
-            next();
-        } else if (!gnu_ext) {
-	    expect("=");
-        }
-    } else {
-        if (type->t & VT_ARRAY) {
-	    index = (*cur_field)->c;
-	    if (type->ref->c >= 0 && index >= type->ref->c)
-	        tcc_error("index too large");
-            type = pointed_type(type);
-            c += index * type_size(type, &align);
-        } else {
-            f = *cur_field;
-	    while (f && (f->v & SYM_FIRST_ANOM) && (f->type.t & VT_BITFIELD))
-	        *cur_field = f = f->next;
-            if (!f)
-                tcc_error("too many field init");
-	    type = &f->type;
-            c += f->c;
-        }
-    }
-    /* must put zero in holes (note that doing it that way
-       ensures that it even works with designators) */
-    if (!size_only && c - corig > al)
-	init_putz(sec, corig + al, c - corig - al);
-    decl_initializer(type, sec, c, 0, size_only);
-
-    /* XXX: make it more general */
-    if (!size_only && nb_elems > 1) {
-        unsigned long c_end;
-        uint8_t *src, *dst;
-        int i;
-
-        if (!sec) {
-	    vset(type, VT_LOCAL|VT_LVAL, c);
-	    for (i = 1; i < nb_elems; i++) {
-		vset(type, VT_LOCAL|VT_LVAL, c + elem_size * i);
-		vswap();
-		vstore();
-	    }
-	    vpop();
-        } else if (!NODATA_WANTED) {
-	    c_end = c + nb_elems * elem_size;
-	    if (c_end > sec->data_allocated)
-	        section_realloc(sec, c_end);
-	    src = sec->data + c;
-	    dst = src;
-	    for(i = 1; i < nb_elems; i++) {
-		dst += elem_size;
-		memcpy(dst, src, elem_size);
-	    }
-	}
-    }
-    c += nb_elems * type_size(type, &align);
-    if (c - corig > al)
-      al = c - corig;
-    return al;
-}
-
-/* store a value or an expression directly in global data or in local array */
-static void init_putv(CType *type, Section *sec, unsigned long c)
-{
-    int bt;
-    void *ptr;
-    CType dtype;
-
-    dtype = *type;
-    dtype.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */
-
-    if (sec) {
-	int size, align;
-        /* XXX: not portable */
-        /* XXX: generate error if incorrect relocation */
-        gen_assign_cast(&dtype);
-        bt = type->t & VT_BTYPE;
-
-        if ((vtop->r & VT_SYM)
-            && bt != VT_PTR
-            && bt != VT_FUNC
-            && (bt != (PTR_SIZE == 8 ? VT_LLONG : VT_INT)
-                || (type->t & VT_BITFIELD))
-            && !((vtop->r & VT_CONST) && vtop->sym->v >= SYM_FIRST_ANOM)
-            )
-            tcc_error("initializer element is not computable at load time");
-
-        if (NODATA_WANTED) {
-            vtop--;
-            return;
-        }
-
-	size = type_size(type, &align);
-	section_reserve(sec, c + size);
-        ptr = sec->data + c;
-
-        /* XXX: make code faster ? */
-	if ((vtop->r & (VT_SYM|VT_CONST)) == (VT_SYM|VT_CONST) &&
-	    vtop->sym->v >= SYM_FIRST_ANOM &&
-	    /* XXX This rejects compound literals like
-	       '(void *){ptr}'.  The problem is that '&sym' is
-	       represented the same way, which would be ruled out
-	       by the SYM_FIRST_ANOM check above, but also '"string"'
-	       in 'char *p = "string"' is represented the same
-	       with the type being VT_PTR and the symbol being an
-	       anonymous one.  That is, there's no difference in vtop
-	       between '(void *){x}' and '&(void *){x}'.  Ignore
-	       pointer typed entities here.  Hopefully no real code
-	       will every use compound literals with scalar type.  */
-	    (vtop->type.t & VT_BTYPE) != VT_PTR) {
-	    /* These come from compound literals, memcpy stuff over.  */
-	    Section *ssec;
-	    ElfW(Sym) *esym;
-	    ElfW_Rel *rel;
-	    esym = &((ElfW(Sym) *)symtab_section->data)[vtop->sym->c];
-	    ssec = tcc_state->sections[esym->st_shndx];
-	    memmove (ptr, ssec->data + esym->st_value, size);
-	    if (ssec->reloc) {
-		/* We need to copy over all memory contents, and that
-		   includes relocations.  Use the fact that relocs are
-		   created it order, so look from the end of relocs
-		   until we hit one before the copied region.  */
-		int num_relocs = ssec->reloc->data_offset / sizeof(*rel);
-		rel = (ElfW_Rel*)(ssec->reloc->data + ssec->reloc->data_offset);
-		while (num_relocs--) {
-		    rel--;
-		    if (rel->r_offset >= esym->st_value + size)
-		      continue;
-		    if (rel->r_offset < esym->st_value)
-		      break;
-		    /* Note: if the same fields are initialized multiple
-		       times (possible with designators) then we possibly
-		       add multiple relocations for the same offset here.
-		       That would lead to wrong code, the last reloc needs
-		       to win.  We clean this up later after the whole
-		       initializer is parsed.  */
-		    put_elf_reloca(symtab_section, sec,
-				   c + rel->r_offset - esym->st_value,
-				   ELFW(R_TYPE)(rel->r_info),
-				   ELFW(R_SYM)(rel->r_info),
-#if PTR_SIZE == 8
-				   rel->r_addend
-#else
-				   0
-#endif
-				  );
-		}
-	    }
-	} else {
-            if (type->t & VT_BITFIELD) {
-                int bit_pos, bit_size, bits, n;
-                unsigned char *p, v, m;
-                bit_pos = BIT_POS(vtop->type.t);
-                bit_size = BIT_SIZE(vtop->type.t);
-                p = (unsigned char*)ptr + (bit_pos >> 3);
-                bit_pos &= 7, bits = 0;
-                while (bit_size) {
-                    n = 8 - bit_pos;
-                    if (n > bit_size)
-                        n = bit_size;
-                    v = vtop->c.i >> bits << bit_pos;
-                    m = ((1 << n) - 1) << bit_pos;
-                    *p = (*p & ~m) | (v & m);
-                    bits += n, bit_size -= n, bit_pos = 0, ++p;
-                }
-            } else
-            switch(bt) {
-		/* XXX: when cross-compiling we assume that each type has the
-		   same representation on host and target, which is likely to
-		   be wrong in the case of long double */
-	    case VT_BOOL:
-		vtop->c.i = vtop->c.i != 0;
-	    case VT_BYTE:
-		*(char *)ptr |= vtop->c.i;
-		break;
-	    case VT_SHORT:
-		*(short *)ptr |= vtop->c.i;
-		break;
-	    case VT_FLOAT:
-		*(float*)ptr = vtop->c.f;
-		break;
-	    case VT_DOUBLE:
-		*(double *)ptr = vtop->c.d;
-		break;
-	    case VT_LDOUBLE:
-#if defined TCC_IS_NATIVE_387
-                if (sizeof (long double) >= 10) /* zero pad ten-byte LD */
-                    memcpy(ptr, &vtop->c.ld, 10);
-#ifdef __TINYC__
-                else if (sizeof (long double) == sizeof (double))
-                    __asm__("fldl %1\nfstpt %0\n" : "=m" (ptr) : "m" (vtop->c.ld));
-#endif
-                else
-#endif
-                if (sizeof(long double) == LDOUBLE_SIZE)
-		    *(long double*)ptr = vtop->c.ld;
-                else if (sizeof(double) == LDOUBLE_SIZE)
-		    *(double *)ptr = (double)vtop->c.ld;
-                else
-                    tcc_error("can't cross compile long double constants");
-		break;
-#if PTR_SIZE != 8
-	    case VT_LLONG:
-		*(long long *)ptr |= vtop->c.i;
-		break;
-#else
-	    case VT_LLONG:
-#endif
-	    case VT_PTR:
-		{
-		    addr_t val = vtop->c.i;
-#if PTR_SIZE == 8
-		    if (vtop->r & VT_SYM)
-		      greloca(sec, vtop->sym, c, R_DATA_PTR, val);
-		    else
-		      *(addr_t *)ptr |= val;
-#else
-		    if (vtop->r & VT_SYM)
-		      greloc(sec, vtop->sym, c, R_DATA_PTR);
-		    *(addr_t *)ptr |= val;
-#endif
-		    break;
-		}
-	    default:
-		{
-		    int val = vtop->c.i;
-#if PTR_SIZE == 8
-		    if (vtop->r & VT_SYM)
-		      greloca(sec, vtop->sym, c, R_DATA_PTR, val);
-		    else
-		      *(int *)ptr |= val;
-#else
-		    if (vtop->r & VT_SYM)
-		      greloc(sec, vtop->sym, c, R_DATA_PTR);
-		    *(int *)ptr |= val;
-#endif
-		    break;
-		}
-	    }
-	}
-        vtop--;
-    } else {
-        vset(&dtype, VT_LOCAL|VT_LVAL, c);
-        vswap();
-        vstore();
-        vpop();
-    }
-}
-
-/* 't' contains the type and storage info. 'c' is the offset of the
-   object in section 'sec'. If 'sec' is NULL, it means stack based
-   allocation. 'first' is true if array '{' must be read (multi
-   dimension implicit array init handling). 'size_only' is true if
-   size only evaluation is wanted (only for arrays). */
-static void decl_initializer(CType *type, Section *sec, unsigned long c, 
-                             int first, int size_only)
-{
-    int len, n, no_oblock, nb, i;
-    int size1, align1;
-    int have_elem;
-    Sym *s, *f;
-    Sym indexsym;
-    CType *t1;
-
-    /* If we currently are at an '}' or ',' we have read an initializer
-       element in one of our callers, and not yet consumed it.  */
-    have_elem = tok == '}' || tok == ',';
-    if (!have_elem && tok != '{' &&
-	/* In case of strings we have special handling for arrays, so
-	   don't consume them as initializer value (which would commit them
-	   to some anonymous symbol).  */
-	tok != TOK_LSTR && tok != TOK_STR &&
-	!size_only) {
-	parse_init_elem(!sec ? EXPR_ANY : EXPR_CONST);
-	have_elem = 1;
-    }
-
-    if (have_elem &&
-	!(type->t & VT_ARRAY) &&
-	/* Use i_c_parameter_t, to strip toplevel qualifiers.
-	   The source type might have VT_CONSTANT set, which is
-	   of course assignable to non-const elements.  */
-	is_compatible_unqualified_types(type, &vtop->type)) {
-        init_putv(type, sec, c);
-    } else if (type->t & VT_ARRAY) {
-        s = type->ref;
-        n = s->c;
-        t1 = pointed_type(type);
-        size1 = type_size(t1, &align1);
-
-        no_oblock = 1;
-        if ((first && tok != TOK_LSTR && tok != TOK_STR) || 
-            tok == '{') {
-            if (tok != '{')
-                tcc_error("character array initializer must be a literal,"
-                    " optionally enclosed in braces");
-            skip('{');
-            no_oblock = 0;
-        }
-
-        /* only parse strings here if correct type (otherwise: handle
-           them as ((w)char *) expressions */
-        if ((tok == TOK_LSTR && 
-#ifdef TCC_TARGET_PE
-             (t1->t & VT_BTYPE) == VT_SHORT && (t1->t & VT_UNSIGNED)
-#else
-             (t1->t & VT_BTYPE) == VT_INT
-#endif
-            ) || (tok == TOK_STR && (t1->t & VT_BTYPE) == VT_BYTE)) {
-	    len = 0;
-            while (tok == TOK_STR || tok == TOK_LSTR) {
-                int cstr_len, ch;
-
-                /* compute maximum number of chars wanted */
-                if (tok == TOK_STR)
-                    cstr_len = tokc.str.size;
-                else
-                    cstr_len = tokc.str.size / sizeof(nwchar_t);
-                cstr_len--;
-                nb = cstr_len;
-                if (n >= 0 && nb > (n - len))
-                    nb = n - len;
-                if (!size_only) {
-                    if (cstr_len > nb)
-                        tcc_warning("initializer-string for array is too long");
-                    /* in order to go faster for common case (char
-                       string in global variable, we handle it
-                       specifically */
-                    if (sec && tok == TOK_STR && size1 == 1) {
-                        if (!NODATA_WANTED)
-                            memcpy(sec->data + c + len, tokc.str.data, nb);
-                    } else {
-                        for(i=0;i<nb;i++) {
-                            if (tok == TOK_STR)
-                                ch = ((unsigned char *)tokc.str.data)[i];
-                            else
-                                ch = ((nwchar_t *)tokc.str.data)[i];
-			    vpushi(ch);
-                            init_putv(t1, sec, c + (len + i) * size1);
-                        }
-                    }
-                }
-                len += nb;
-                next();
-            }
-            /* only add trailing zero if enough storage (no
-               warning in this case since it is standard) */
-            if (n < 0 || len < n) {
-                if (!size_only) {
-		    vpushi(0);
-                    init_putv(t1, sec, c + (len * size1));
-                }
-                len++;
-            }
-	    len *= size1;
-        } else {
-	    indexsym.c = 0;
-	    f = &indexsym;
-
-          do_init_list:
-	    len = 0;
-	    while (tok != '}' || have_elem) {
-		len = decl_designator(type, sec, c, &f, size_only, len);
-		have_elem = 0;
-		if (type->t & VT_ARRAY) {
-		    ++indexsym.c;
-		    /* special test for multi dimensional arrays (may not
-		       be strictly correct if designators are used at the
-		       same time) */
-		    if (no_oblock && len >= n*size1)
-		        break;
-		} else {
-		    if (s->type.t == VT_UNION)
-		        f = NULL;
-		    else
-		        f = f->next;
-		    if (no_oblock && f == NULL)
-		        break;
-		}
-
-		if (tok == '}')
-		    break;
-		skip(',');
-	    }
-        }
-        /* put zeros at the end */
-	if (!size_only && len < n*size1)
-	    init_putz(sec, c + len, n*size1 - len);
-        if (!no_oblock)
-            skip('}');
-        /* patch type size if needed, which happens only for array types */
-        if (n < 0)
-            s->c = size1 == 1 ? len : ((len + size1 - 1)/size1);
-    } else if ((type->t & VT_BTYPE) == VT_STRUCT) {
-	size1 = 1;
-        no_oblock = 1;
-        if (first || tok == '{') {
-            skip('{');
-            no_oblock = 0;
-        }
-        s = type->ref;
-        f = s->next;
-        n = s->c;
-	goto do_init_list;
-    } else if (tok == '{') {
-        next();
-        decl_initializer(type, sec, c, first, size_only);
-        skip('}');
-    } else if (size_only) {
-	/* If we supported only ISO C we wouldn't have to accept calling
-	   this on anything than an array size_only==1 (and even then
-	   only on the outermost level, so no recursion would be needed),
-	   because initializing a flex array member isn't supported.
-	   But GNU C supports it, so we need to recurse even into
-	   subfields of structs and arrays when size_only is set.  */
-        /* just skip expression */
-        skip_or_save_block(NULL);
-    } else {
-	if (!have_elem) {
-	    /* This should happen only when we haven't parsed
-	       the init element above for fear of committing a
-	       string constant to memory too early.  */
-	    if (tok != TOK_STR && tok != TOK_LSTR)
-	      expect("string constant");
-	    parse_init_elem(!sec ? EXPR_ANY : EXPR_CONST);
-	}
-        init_putv(type, sec, c);
-    }
-}
-
-/* parse an initializer for type 't' if 'has_init' is non zero, and
-   allocate space in local or global data space ('r' is either
-   VT_LOCAL or VT_CONST). If 'v' is non zero, then an associated
-   variable 'v' of scope 'scope' is declared before initializers
-   are parsed. If 'v' is zero, then a reference to the new object
-   is put in the value stack. If 'has_init' is 2, a special parsing
-   is done to handle string constants. */
-static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, 
-                                   int has_init, int v, int scope)
-{
-    int size, align, addr;
-    TokenString *init_str = NULL;
-
-    Section *sec;
-    Sym *flexible_array;
-    Sym *sym = NULL;
-    int saved_nocode_wanted = nocode_wanted;
-#ifdef CONFIG_TCC_BCHECK
-    int bcheck = tcc_state->do_bounds_check && !NODATA_WANTED;
-#endif
-
-    if (type->t & VT_STATIC)
-        nocode_wanted |= NODATA_WANTED ? 0x40000000 : 0x80000000;
-
-    flexible_array = NULL;
-    if ((type->t & VT_BTYPE) == VT_STRUCT) {
-        Sym *field = type->ref->next;
-        if (field) {
-            while (field->next)
-                field = field->next;
-            if (field->type.t & VT_ARRAY && field->type.ref->c < 0)
-                flexible_array = field;
-        }
-    }
-
-    size = type_size(type, &align);
-    /* If unknown size, we must evaluate it before
-       evaluating initializers because
-       initializers can generate global data too
-       (e.g. string pointers or ISOC99 compound
-       literals). It also simplifies local
-       initializers handling */
-    if (size < 0 || (flexible_array && has_init)) {
-        if (!has_init) 
-            tcc_error("unknown type size");
-        /* get all init string */
-        if (has_init == 2) {
-	    init_str = tok_str_alloc();
-            /* only get strings */
-            while (tok == TOK_STR || tok == TOK_LSTR) {
-                tok_str_add_tok(init_str);
-                next();
-            }
-	    tok_str_add(init_str, -1);
-	    tok_str_add(init_str, 0);
-        } else {
-	    skip_or_save_block(&init_str);
-        }
-        unget_tok(0);
-
-        /* compute size */
-        begin_macro(init_str, 1);
-        next();
-        decl_initializer(type, NULL, 0, 1, 1);
-        /* prepare second initializer parsing */
-        macro_ptr = init_str->str;
-        next();
-        
-        /* if still unknown size, error */
-        size = type_size(type, &align);
-        if (size < 0) 
-            tcc_error("unknown type size");
-    }
-    /* If there's a flex member and it was used in the initializer
-       adjust size.  */
-    if (flexible_array &&
-	flexible_array->type.ref->c > 0)
-        size += flexible_array->type.ref->c
-	        * pointed_size(&flexible_array->type);
-    /* take into account specified alignment if bigger */
-    if (ad->a.aligned) {
-	int speca = 1 << (ad->a.aligned - 1);
-        if (speca > align)
-            align = speca;
-    } else if (ad->a.packed) {
-        align = 1;
-    }
-
-    if (NODATA_WANTED)
-        size = 0, align = 1;
-
-    if ((r & VT_VALMASK) == VT_LOCAL) {
-        sec = NULL;
-#ifdef CONFIG_TCC_BCHECK
-        if (bcheck && (type->t & VT_ARRAY)) {
-            loc--;
-        }
-#endif
-        loc = (loc - size) & -align;
-        addr = loc;
-#ifdef CONFIG_TCC_BCHECK
-        /* handles bounds */
-        /* XXX: currently, since we do only one pass, we cannot track
-           '&' operators, so we add only arrays */
-        if (bcheck && (type->t & VT_ARRAY)) {
-            addr_t *bounds_ptr;
-            /* add padding between regions */
-            loc--;
-            /* then add local bound info */
-            bounds_ptr = section_ptr_add(lbounds_section, 2 * sizeof(addr_t));
-            bounds_ptr[0] = addr;
-            bounds_ptr[1] = size;
-        }
-#endif
-        if (v) {
-            /* local variable */
-#ifdef CONFIG_TCC_ASM
-	    if (ad->asm_label) {
-		int reg = asm_parse_regvar(ad->asm_label);
-		if (reg >= 0)
-		    r = (r & ~VT_VALMASK) | reg;
-	    }
-#endif
-            sym = sym_push(v, type, r, addr);
-            sym->a = ad->a;
-        } else {
-            /* push local reference */
-            vset(type, r, addr);
-        }
-    } else {
-        if (v && scope == VT_CONST) {
-            /* see if the symbol was already defined */
-            sym = sym_find(v);
-            if (sym) {
-                patch_storage(sym, ad, type);
-                if (sym->type.t & VT_EXTERN) {
-                    /* if the variable is extern, it was not allocated */
-                    sym->type.t &= ~VT_EXTERN;
-                    /* set array size if it was omitted in extern
-                       declaration */
-                    if ((sym->type.t & VT_ARRAY) && 
-                        sym->type.ref->c < 0 &&
-                        type->ref->c >= 0)
-                        sym->type.ref->c = type->ref->c;
-                } else if (!has_init) {
-                    /* we accept several definitions of the same
-                       global variable. this is tricky, because we
-                       must play with the SHN_COMMON type of the symbol */
-                    /* no init data, we won't add more to the symbol */
-                    goto no_alloc;
-                } else if (sym->c) {
-                    ElfW(Sym) *esym;
-                    esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
-                    if (esym->st_shndx == data_section->sh_num)
-                        tcc_error("redefinition of '%s'", get_tok_str(v, NULL));
-                }
-            }
-        }
-
-        /* allocate symbol in corresponding section */
-        sec = ad->section;
-        if (!sec) {
-            if (has_init)
-                sec = data_section;
-            else if (tcc_state->nocommon)
-                sec = bss_section;
-        }
-
-        if (sec) {
-	    addr = section_add(sec, size, align);
-#ifdef CONFIG_TCC_BCHECK
-            /* add padding if bound check */
-            if (bcheck)
-                section_add(sec, 1, 1);
-#endif
-        } else {
-            addr = align; /* SHN_COMMON is special, symbol value is align */
-	    sec = common_section;
-        }
-
-        if (v) {
-            if (!sym) {
-                sym = sym_push(v, type, r | VT_SYM, 0);
-                patch_storage(sym, ad, NULL);
-            }
-            /* Local statics have a scope until now (for
-               warnings), remove it here.  */
-            sym->sym_scope = 0;
-            /* update symbol definition */
-	    put_extern_sym(sym, sec, addr, size);
-        } else {
-            /* push global reference */
-            sym = get_sym_ref(type, sec, addr, size);
-	    vpushsym(type, sym);
-	    vtop->r |= r;
-        }
-
-#ifdef CONFIG_TCC_BCHECK
-        /* handles bounds now because the symbol must be defined
-           before for the relocation */
-        if (bcheck) {
-            addr_t *bounds_ptr;
-
-            greloca(bounds_section, sym, bounds_section->data_offset, R_DATA_PTR, 0);
-            /* then add global bound info */
-            bounds_ptr = section_ptr_add(bounds_section, 2 * sizeof(addr_t));
-            bounds_ptr[0] = 0; /* relocated */
-            bounds_ptr[1] = size;
-        }
-#endif
-    }
-
-    if (type->t & VT_VLA) {
-        int a;
-
-        if (NODATA_WANTED)
-            goto no_alloc;
-
-        /* save current stack pointer */
-        if (vlas_in_scope == 0) {
-            if (vla_sp_root_loc == -1)
-                vla_sp_root_loc = (loc -= PTR_SIZE);
-            gen_vla_sp_save(vla_sp_root_loc);
-        }
-
-        vla_runtime_type_size(type, &a);
-        gen_vla_alloc(type, a);
-        gen_vla_sp_save(addr);
-        vla_sp_loc = addr;
-        vlas_in_scope++;
-
-    } else if (has_init) {
-	size_t oldreloc_offset = 0;
-	if (sec && sec->reloc)
-	  oldreloc_offset = sec->reloc->data_offset;
-        decl_initializer(type, sec, addr, 1, 0);
-	if (sec && sec->reloc)
-	  squeeze_multi_relocs(sec, oldreloc_offset);
-        /* patch flexible array member size back to -1, */
-        /* for possible subsequent similar declarations */
-        if (flexible_array)
-            flexible_array->type.ref->c = -1;
-    }
-
- no_alloc:
-    /* restore parse state if needed */
-    if (init_str) {
-        end_macro();
-        next();
-    }
-
-    nocode_wanted = saved_nocode_wanted;
-}
-
-/* parse a function defined by symbol 'sym' and generate its code in
-   'cur_text_section' */
-static void gen_function(Sym *sym)
-{
-    nocode_wanted = 0;
-    ind = cur_text_section->data_offset;
-    /* NOTE: we patch the symbol size later */
-    put_extern_sym(sym, cur_text_section, ind, 0);
-    funcname = get_tok_str(sym->v, NULL);
-    func_ind = ind;
-    /* Initialize VLA state */
-    vla_sp_loc = -1;
-    vla_sp_root_loc = -1;
-    /* put debug symbol */
-    tcc_debug_funcstart(tcc_state, sym);
-    /* push a dummy symbol to enable local sym storage */
-    sym_push2(&local_stack, SYM_FIELD, 0, 0);
-    local_scope = 1; /* for function parameters */
-    gfunc_prolog(&sym->type);
-    local_scope = 0;
-    rsym = 0;
-    block(NULL, NULL, 0);
-    nocode_wanted = 0;
-    gsym(rsym);
-    gfunc_epilog();
-    cur_text_section->data_offset = ind;
-    label_pop(&global_label_stack, NULL, 0);
-    /* reset local stack */
-    local_scope = 0;
-    sym_pop(&local_stack, NULL, 0);
-    /* end of function */
-    /* patch symbol size */
-    ((ElfW(Sym) *)symtab_section->data)[sym->c].st_size = 
-        ind - func_ind;
-    tcc_debug_funcend(tcc_state, ind - func_ind);
-    /* It's better to crash than to generate wrong code */
-    cur_text_section = NULL;
-    funcname = ""; /* for safety */
-    func_vt.t = VT_VOID; /* for safety */
-    func_var = 0; /* for safety */
-    ind = 0; /* for safety */
-    nocode_wanted = 0x80000000;
-    check_vstack();
-}
-
-static void gen_inline_functions(TCCState *s)
-{
-    Sym *sym;
-    int inline_generated, i, ln;
-    struct InlineFunc *fn;
-
-    ln = file->line_num;
-    /* iterate while inline function are referenced */
-    do {
-        inline_generated = 0;
-        for (i = 0; i < s->nb_inline_fns; ++i) {
-            fn = s->inline_fns[i];
-            sym = fn->sym;
-            if (sym && sym->c) {
-                /* the function was used: generate its code and
-                   convert it to a normal function */
-                fn->sym = NULL;
-                if (file)
-                    pstrcpy(file->filename, sizeof file->filename, fn->filename);
-                sym->type.t &= ~VT_INLINE;
-
-                begin_macro(fn->func_str, 1);
-                next();
-                cur_text_section = text_section;
-                gen_function(sym);
-                end_macro();
-
-                inline_generated = 1;
-            }
-        }
-    } while (inline_generated);
-    file->line_num = ln;
-}
-
-ST_FUNC void free_inline_functions(TCCState *s)
-{
-    int i;
-    /* free tokens of unused inline functions */
-    for (i = 0; i < s->nb_inline_fns; ++i) {
-        struct InlineFunc *fn = s->inline_fns[i];
-        if (fn->sym)
-            tok_str_free(fn->func_str);
-    }
-    dynarray_reset(&s->inline_fns, &s->nb_inline_fns);
-}
-
-/* 'l' is VT_LOCAL or VT_CONST to define default storage type, or VT_CMP
-   if parsing old style parameter decl list (and FUNC_SYM is set then) */
-static int decl0(int l, int is_for_loop_init, Sym *func_sym)
-{
-    int v, has_init, r;
-    CType type, btype;
-    Sym *sym;
-    AttributeDef ad;
-
-    while (1) {
-        if (!parse_btype(&btype, &ad)) {
-            if (is_for_loop_init)
-                return 0;
-            /* skip redundant ';' if not in old parameter decl scope */
-            if (tok == ';' && l != VT_CMP) {
-                next();
-                continue;
-            }
-            if (l != VT_CONST)
-                break;
-            if (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3) {
-                /* global asm block */
-                asm_global_instr();
-                continue;
-            }
-            if (tok >= TOK_UIDENT) {
-               /* special test for old K&R protos without explicit int
-                  type. Only accepted when defining global data */
-                btype.t = VT_INT;
-            } else {
-                if (tok != TOK_EOF)
-                    expect("declaration");
-                break;
-            }
-        }
-        if (tok == ';') {
-	    if ((btype.t & VT_BTYPE) == VT_STRUCT) {
-		int v = btype.ref->v;
-		if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) >= SYM_FIRST_ANOM)
-        	    tcc_warning("unnamed struct/union that defines no instances");
-                next();
-                continue;
-	    }
-            if (IS_ENUM(btype.t)) {
-                next();
-                continue;
-            }
-        }
-        while (1) { /* iterate thru each declaration */
-            type = btype;
-	    /* If the base type itself was an array type of unspecified
-	       size (like in 'typedef int arr[]; arr x = {1};') then
-	       we will overwrite the unknown size by the real one for
-	       this decl.  We need to unshare the ref symbol holding
-	       that size.  */
-	    if ((type.t & VT_ARRAY) && type.ref->c < 0) {
-		type.ref = sym_push(SYM_FIELD, &type.ref->type, 0, type.ref->c);
-	    }
-            type_decl(&type, &ad, &v, TYPE_DIRECT);
-#if 0
-            {
-                char buf[500];
-                type_to_str(buf, sizeof(buf), &type, get_tok_str(v, NULL));
-                printf("type = '%s'\n", buf);
-            }
-#endif
-            if ((type.t & VT_BTYPE) == VT_FUNC) {
-                if ((type.t & VT_STATIC) && (l == VT_LOCAL)) {
-                    tcc_error("function without file scope cannot be static");
-                }
-                /* if old style function prototype, we accept a
-                   declaration list */
-                sym = type.ref;
-                if (sym->f.func_type == FUNC_OLD && l == VT_CONST)
-                    decl0(VT_CMP, 0, sym);
-            }
-
-            if (gnu_ext && (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3)) {
-                ad.asm_label = asm_label_instr();
-                /* parse one last attribute list, after asm label */
-                parse_attribute(&ad);
-                if (tok == '{')
-                    expect(";");
-            }
-
-#ifdef TCC_TARGET_PE
-            if (ad.a.dllimport || ad.a.dllexport) {
-                if (type.t & (VT_STATIC|VT_TYPEDEF))
-                    tcc_error("cannot have dll linkage with static or typedef");
-                if (ad.a.dllimport) {
-                    if ((type.t & VT_BTYPE) == VT_FUNC)
-                        ad.a.dllimport = 0;
-                    else
-                        type.t |= VT_EXTERN;
-                }
-            }
-#endif
-            if (tok == '{') {
-                if (l != VT_CONST)
-                    tcc_error("cannot use local functions");
-                if ((type.t & VT_BTYPE) != VT_FUNC)
-                    expect("function definition");
-
-                /* reject abstract declarators in function definition
-		   make old style params without decl have int type */
-                sym = type.ref;
-                while ((sym = sym->next) != NULL) {
-                    if (!(sym->v & ~SYM_FIELD))
-                        expect("identifier");
-		    if (sym->type.t == VT_VOID)
-		        sym->type = int_type;
-		}
-                
-                /* XXX: cannot do better now: convert extern line to static inline */
-                if ((type.t & (VT_EXTERN | VT_INLINE)) == (VT_EXTERN | VT_INLINE))
-                    type.t = (type.t & ~VT_EXTERN) | VT_STATIC;
-                
-                sym = sym_find(v);
-                if (sym) {
-                    Sym *ref;
-                    if ((sym->type.t & VT_BTYPE) != VT_FUNC)
-                        goto func_error1;
-
-                    ref = sym->type.ref;
-
-                    /* use func_call from prototype if not defined */
-                    if (ref->f.func_call != FUNC_CDECL
-                     && type.ref->f.func_call == FUNC_CDECL)
-                        type.ref->f.func_call = ref->f.func_call;
-
-                    /* use static from prototype */
-                    if (sym->type.t & VT_STATIC)
-                        type.t = (type.t & ~VT_EXTERN) | VT_STATIC;
-
-		    /* If the definition has no visibility use the
-		       one from prototype.  */
-		    if (!type.ref->a.visibility)
-                        type.ref->a.visibility = ref->a.visibility;
-                    /* apply other storage attributes from prototype */
-                    type.ref->a.dllexport |= ref->a.dllexport;
-                    type.ref->a.weak |= ref->a.weak;
-
-                    if (!is_compatible_types(&sym->type, &type)) {
-                    func_error1:
-                        tcc_error("incompatible types for redefinition of '%s'", 
-                              get_tok_str(v, NULL));
-                    }
-                    if (ref->f.func_body)
-                        tcc_error("redefinition of '%s'", get_tok_str(v, NULL));
-                    /* if symbol is already defined, then put complete type */
-                    sym->type = type;
-
-                } else {
-                    /* put function symbol */
-                    sym = global_identifier_push(v, type.t, 0);
-                    sym->type.ref = type.ref;
-                }
-
-                sym->type.ref->f.func_body = 1;
-                sym->r = VT_SYM | VT_CONST;
-                patch_storage(sym, &ad, NULL);
-
-                /* static inline functions are just recorded as a kind
-                   of macro. Their code will be emitted at the end of
-                   the compilation unit only if they are used */
-                if ((type.t & (VT_INLINE | VT_STATIC)) == 
-                    (VT_INLINE | VT_STATIC)) {
-                    struct InlineFunc *fn;
-                    const char *filename;
-                           
-                    filename = file ? file->filename : "";
-                    fn = tcc_malloc(sizeof *fn + strlen(filename));
-                    strcpy(fn->filename, filename);
-                    fn->sym = sym;
-		    skip_or_save_block(&fn->func_str);
-                    dynarray_add(&tcc_state->inline_fns,
-				 &tcc_state->nb_inline_fns, fn);
-                } else {
-                    /* compute text section */
-                    cur_text_section = ad.section;
-                    if (!cur_text_section)
-                        cur_text_section = text_section;
-                    gen_function(sym);
-                }
-                break;
-            } else {
-		if (l == VT_CMP) {
-		    /* find parameter in function parameter list */
-		    for (sym = func_sym->next; sym; sym = sym->next)
-			if ((sym->v & ~SYM_FIELD) == v)
-			    goto found;
-		    tcc_error("declaration for parameter '%s' but no such parameter",
-			      get_tok_str(v, NULL));
-found:
-		    if (type.t & VT_STORAGE) /* 'register' is okay */
-		        tcc_error("storage class specified for '%s'",
-				  get_tok_str(v, NULL));
-		    if (sym->type.t != VT_VOID)
-		        tcc_error("redefinition of parameter '%s'",
-				  get_tok_str(v, NULL));
-		    convert_parameter_type(&type);
-		    sym->type = type;
-		} else if (type.t & VT_TYPEDEF) {
-                    /* save typedefed type  */
-                    /* XXX: test storage specifiers ? */
-                    sym = sym_find(v);
-                    if (sym && sym->sym_scope == local_scope) {
-                        if (!is_compatible_types(&sym->type, &type)
-                            || !(sym->type.t & VT_TYPEDEF))
-                            tcc_error("incompatible redefinition of '%s'",
-                                get_tok_str(v, NULL));
-                        sym->type = type;
-                    } else {
-                        sym = sym_push(v, &type, 0, 0);
-                    }
-                    sym->a = ad.a;
-                    sym->f = ad.f;
-                } else {
-                    r = 0;
-                    if ((type.t & VT_BTYPE) == VT_FUNC) {
-                        /* external function definition */
-                        /* specific case for func_call attribute */
-                        type.ref->f = ad.f;
-                    } else if (!(type.t & VT_ARRAY)) {
-                        /* not lvalue if array */
-                        r |= lvalue_type(type.t);
-                    }
-                    has_init = (tok == '=');
-                    if (has_init && (type.t & VT_VLA))
-                        tcc_error("variable length array cannot be initialized");
-                    if (((type.t & VT_EXTERN) && (!has_init || l != VT_CONST)) ||
-			((type.t & VT_BTYPE) == VT_FUNC) ||
-                        ((type.t & VT_ARRAY) && (type.t & VT_STATIC) &&
-                         !has_init && l == VT_CONST && type.ref->c < 0)) {
-                        /* external variable or function */
-                        /* NOTE: as GCC, uninitialized global static
-                           arrays of null size are considered as
-                           extern */
-                        sym = external_sym(v, &type, r, &ad);
-                        if (ad.alias_target) {
-                            Section tsec;
-                            ElfW(Sym) *esym;
-                            Sym *alias_target;
-                            alias_target = sym_find(ad.alias_target);
-                            if (!alias_target || !alias_target->c)
-                                tcc_error("unsupported forward __alias__ attribute");
-                            esym = &((ElfW(Sym) *)symtab_section->data)[alias_target->c];
-                            tsec.sh_num = esym->st_shndx;
-                            /* Local statics have a scope until now (for
-                               warnings), remove it here.  */
-                            sym->sym_scope = 0;
-                            put_extern_sym2(sym, &tsec, esym->st_value, esym->st_size, 0);
-                        }
-                    } else {
-                        if (type.t & VT_STATIC)
-                            r |= VT_CONST;
-                        else
-                            r |= l;
-                        if (has_init)
-                            next();
-                        decl_initializer_alloc(&type, &ad, r, has_init, v, l);
-                    }
-                }
-                if (tok != ',') {
-                    if (is_for_loop_init)
-                        return 1;
-                    skip(';');
-                    break;
-                }
-                next();
-            }
-            ad.a.aligned = 0;
-        }
-    }
-    return 0;
-}
-
-static void decl(int l)
-{
-    decl0(l, 0, NULL);
-}
-
-/* ------------------------------------------------------------------------- */
diff --git a/tinyc/tcclib.h b/tinyc/tcclib.h
deleted file mode 100644
index 8d59e4c9a..000000000
--- a/tinyc/tcclib.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/* Simple libc header for TCC 
- * 
- * Add any function you want from the libc there. This file is here
- * only for your convenience so that you do not need to put the whole
- * glibc include files on your floppy disk 
- */
-#ifndef _TCCLIB_H
-#define _TCCLIB_H
-
-#include <stddef.h>
-#include <stdarg.h>
-
-/* stdlib.h */
-void *calloc(size_t nmemb, size_t size);
-void *malloc(size_t size);
-void free(void *ptr);
-void *realloc(void *ptr, size_t size);
-int atoi(const char *nptr);
-long int strtol(const char *nptr, char **endptr, int base);
-unsigned long int strtoul(const char *nptr, char **endptr, int base);
-void exit(int);
-
-/* stdio.h */
-typedef struct __FILE FILE;
-#define EOF (-1)
-extern FILE *stdin;
-extern FILE *stdout;
-extern FILE *stderr;
-FILE *fopen(const char *path, const char *mode);
-FILE *fdopen(int fildes, const char *mode);
-FILE *freopen(const  char *path, const char *mode, FILE *stream);
-int fclose(FILE *stream);
-size_t  fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
-size_t  fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);
-int fgetc(FILE *stream);
-char *fgets(char *s, int size, FILE *stream);
-int getc(FILE *stream);
-int getchar(void);
-char *gets(char *s);
-int ungetc(int c, FILE *stream);
-int fflush(FILE *stream);
-int putchar (int c);
-
-int printf(const char *format, ...);
-int fprintf(FILE *stream, const char *format, ...);
-int sprintf(char *str, const char *format, ...);
-int snprintf(char *str, size_t size, const  char  *format, ...);
-int asprintf(char **strp, const char *format, ...);
-int dprintf(int fd, const char *format, ...);
-int vprintf(const char *format, va_list ap);
-int vfprintf(FILE  *stream,  const  char *format, va_list ap);
-int vsprintf(char *str, const char *format, va_list ap);
-int vsnprintf(char *str, size_t size, const char  *format, va_list ap);
-int vasprintf(char  **strp,  const  char *format, va_list ap);
-int vdprintf(int fd, const char *format, va_list ap);
-
-void perror(const char *s);
-
-/* string.h */
-char *strcat(char *dest, const char *src);
-char *strchr(const char *s, int c);
-char *strrchr(const char *s, int c);
-char *strcpy(char *dest, const char *src);
-void *memcpy(void *dest, const void *src, size_t n);
-void *memmove(void *dest, const void *src, size_t n);
-void *memset(void *s, int c, size_t n);
-char *strdup(const char *s);
-size_t strlen(const char *s);
-
-/* dlfcn.h */
-#define RTLD_LAZY       0x001
-#define RTLD_NOW        0x002
-#define RTLD_GLOBAL     0x100
-
-void *dlopen(const char *filename, int flag);
-const char *dlerror(void);
-void *dlsym(void *handle, char *symbol);
-int dlclose(void *handle);
-
-#endif /* _TCCLIB_H */
diff --git a/tinyc/tccpe.c b/tinyc/tccpe.c
deleted file mode 100644
index a67023dd2..000000000
--- a/tinyc/tccpe.c
+++ /dev/null
@@ -1,2014 +0,0 @@
-/*
- *  TCCPE.C - PE file output for the Tiny C Compiler
- *
- *  Copyright (c) 2005-2007 grischka
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "tcc.h"
-
-#define PE_MERGE_DATA
-/* #define PE_PRINT_SECTIONS */
-
-#ifndef _WIN32
-#define stricmp strcasecmp
-#define strnicmp strncasecmp
-#include <sys/stat.h> /* chmod() */
-#endif
-
-#ifdef TCC_TARGET_X86_64
-# define ADDR3264 ULONGLONG
-# define PE_IMAGE_REL IMAGE_REL_BASED_DIR64
-# define REL_TYPE_DIRECT R_X86_64_64
-# define R_XXX_THUNKFIX R_X86_64_PC32
-# define R_XXX_RELATIVE R_X86_64_RELATIVE
-# define IMAGE_FILE_MACHINE 0x8664
-# define RSRC_RELTYPE 3
-
-#elif defined TCC_TARGET_ARM
-# define ADDR3264 DWORD
-# define PE_IMAGE_REL IMAGE_REL_BASED_HIGHLOW
-# define REL_TYPE_DIRECT R_ARM_ABS32
-# define R_XXX_THUNKFIX R_ARM_ABS32
-# define R_XXX_RELATIVE R_ARM_RELATIVE
-# define IMAGE_FILE_MACHINE 0x01C0
-# define RSRC_RELTYPE 7 /* ??? (not tested) */
-
-#elif defined TCC_TARGET_I386
-# define ADDR3264 DWORD
-# define PE_IMAGE_REL IMAGE_REL_BASED_HIGHLOW
-# define REL_TYPE_DIRECT R_386_32
-# define R_XXX_THUNKFIX R_386_32
-# define R_XXX_RELATIVE R_386_RELATIVE
-# define IMAGE_FILE_MACHINE 0x014C
-# define RSRC_RELTYPE 7 /* DIR32NB */
-
-#endif
-
-#ifndef IMAGE_NT_SIGNATURE
-/* ----------------------------------------------------------- */
-/* definitions below are from winnt.h */
-
-typedef unsigned char BYTE;
-typedef unsigned short WORD;
-typedef unsigned int DWORD;
-typedef unsigned long long ULONGLONG;
-#pragma pack(push, 1)
-
-typedef struct _IMAGE_DOS_HEADER {  /* DOS .EXE header */
-    WORD e_magic;         /* Magic number */
-    WORD e_cblp;          /* Bytes on last page of file */
-    WORD e_cp;            /* Pages in file */
-    WORD e_crlc;          /* Relocations */
-    WORD e_cparhdr;       /* Size of header in paragraphs */
-    WORD e_minalloc;      /* Minimum extra paragraphs needed */
-    WORD e_maxalloc;      /* Maximum extra paragraphs needed */
-    WORD e_ss;            /* Initial (relative) SS value */
-    WORD e_sp;            /* Initial SP value */
-    WORD e_csum;          /* Checksum */
-    WORD e_ip;            /* Initial IP value */
-    WORD e_cs;            /* Initial (relative) CS value */
-    WORD e_lfarlc;        /* File address of relocation table */
-    WORD e_ovno;          /* Overlay number */
-    WORD e_res[4];        /* Reserved words */
-    WORD e_oemid;         /* OEM identifier (for e_oeminfo) */
-    WORD e_oeminfo;       /* OEM information; e_oemid specific */
-    WORD e_res2[10];      /* Reserved words */
-    DWORD e_lfanew;        /* File address of new exe header */
-} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
-
-#define IMAGE_NT_SIGNATURE  0x00004550  /* PE00 */
-#define SIZE_OF_NT_SIGNATURE 4
-
-typedef struct _IMAGE_FILE_HEADER {
-    WORD    Machine;
-    WORD    NumberOfSections;
-    DWORD   TimeDateStamp;
-    DWORD   PointerToSymbolTable;
-    DWORD   NumberOfSymbols;
-    WORD    SizeOfOptionalHeader;
-    WORD    Characteristics;
-} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
-
-
-#define IMAGE_SIZEOF_FILE_HEADER 20
-
-typedef struct _IMAGE_DATA_DIRECTORY {
-    DWORD   VirtualAddress;
-    DWORD   Size;
-} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
-
-
-typedef struct _IMAGE_OPTIONAL_HEADER {
-    /* Standard fields. */
-    WORD    Magic;
-    BYTE    MajorLinkerVersion;
-    BYTE    MinorLinkerVersion;
-    DWORD   SizeOfCode;
-    DWORD   SizeOfInitializedData;
-    DWORD   SizeOfUninitializedData;
-    DWORD   AddressOfEntryPoint;
-    DWORD   BaseOfCode;
-#ifndef TCC_TARGET_X86_64
-    DWORD   BaseOfData;
-#endif
-    /* NT additional fields. */
-    ADDR3264 ImageBase;
-    DWORD   SectionAlignment;
-    DWORD   FileAlignment;
-    WORD    MajorOperatingSystemVersion;
-    WORD    MinorOperatingSystemVersion;
-    WORD    MajorImageVersion;
-    WORD    MinorImageVersion;
-    WORD    MajorSubsystemVersion;
-    WORD    MinorSubsystemVersion;
-    DWORD   Win32VersionValue;
-    DWORD   SizeOfImage;
-    DWORD   SizeOfHeaders;
-    DWORD   CheckSum;
-    WORD    Subsystem;
-    WORD    DllCharacteristics;
-    ADDR3264 SizeOfStackReserve;
-    ADDR3264 SizeOfStackCommit;
-    ADDR3264 SizeOfHeapReserve;
-    ADDR3264 SizeOfHeapCommit;
-    DWORD   LoaderFlags;
-    DWORD   NumberOfRvaAndSizes;
-    IMAGE_DATA_DIRECTORY DataDirectory[16];
-} IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, IMAGE_OPTIONAL_HEADER;
-
-#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   /* Export Directory */
-#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   /* Import Directory */
-#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   /* Resource Directory */
-#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   /* Exception Directory */
-#define IMAGE_DIRECTORY_ENTRY_SECURITY        4   /* Security Directory */
-#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   /* Base Relocation Table */
-#define IMAGE_DIRECTORY_ENTRY_DEBUG           6   /* Debug Directory */
-/*      IMAGE_DIRECTORY_ENTRY_COPYRIGHT       7      (X86 usage) */
-#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   /* Architecture Specific Data */
-#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   /* RVA of GP */
-#define IMAGE_DIRECTORY_ENTRY_TLS             9   /* TLS Directory */
-#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   /* Load Configuration Directory */
-#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   /* Bound Import Directory in headers */
-#define IMAGE_DIRECTORY_ENTRY_IAT            12   /* Import Address Table */
-#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   /* Delay Load Import Descriptors */
-#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   /* COM Runtime descriptor */
-
-/* Section header format. */
-#define IMAGE_SIZEOF_SHORT_NAME         8
-
-typedef struct _IMAGE_SECTION_HEADER {
-    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
-    union {
-            DWORD   PhysicalAddress;
-            DWORD   VirtualSize;
-    } Misc;
-    DWORD   VirtualAddress;
-    DWORD   SizeOfRawData;
-    DWORD   PointerToRawData;
-    DWORD   PointerToRelocations;
-    DWORD   PointerToLinenumbers;
-    WORD    NumberOfRelocations;
-    WORD    NumberOfLinenumbers;
-    DWORD   Characteristics;
-} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
-
-#define IMAGE_SIZEOF_SECTION_HEADER     40
-
-typedef struct _IMAGE_EXPORT_DIRECTORY {
-    DWORD Characteristics;
-    DWORD TimeDateStamp;
-    WORD MajorVersion;
-    WORD MinorVersion;
-    DWORD Name;
-    DWORD Base;
-    DWORD NumberOfFunctions;
-    DWORD NumberOfNames;
-    DWORD AddressOfFunctions;
-    DWORD AddressOfNames;
-    DWORD AddressOfNameOrdinals;
-} IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY;
-
-typedef struct _IMAGE_IMPORT_DESCRIPTOR {
-    union {
-        DWORD Characteristics;
-        DWORD OriginalFirstThunk;
-    };
-    DWORD TimeDateStamp;
-    DWORD ForwarderChain;
-    DWORD Name;
-    DWORD FirstThunk;
-} IMAGE_IMPORT_DESCRIPTOR;
-
-typedef struct _IMAGE_BASE_RELOCATION {
-    DWORD   VirtualAddress;
-    DWORD   SizeOfBlock;
-//  WORD    TypeOffset[1];
-} IMAGE_BASE_RELOCATION;
-
-#define IMAGE_SIZEOF_BASE_RELOCATION     8
-
-#define IMAGE_REL_BASED_ABSOLUTE         0
-#define IMAGE_REL_BASED_HIGH             1
-#define IMAGE_REL_BASED_LOW              2
-#define IMAGE_REL_BASED_HIGHLOW          3
-#define IMAGE_REL_BASED_HIGHADJ          4
-#define IMAGE_REL_BASED_MIPS_JMPADDR     5
-#define IMAGE_REL_BASED_SECTION          6
-#define IMAGE_REL_BASED_REL32            7
-#define IMAGE_REL_BASED_DIR64           10
-
-#pragma pack(pop)
-
-/* ----------------------------------------------------------- */
-#endif /* ndef IMAGE_NT_SIGNATURE */
-/* ----------------------------------------------------------- */
-
-#ifndef IMAGE_REL_BASED_DIR64
-# define IMAGE_REL_BASED_DIR64 10
-#endif
-
-#pragma pack(push, 1)
-struct pe_header
-{
-    IMAGE_DOS_HEADER doshdr;
-    BYTE dosstub[0x40];
-    DWORD nt_sig;
-    IMAGE_FILE_HEADER filehdr;
-#ifdef TCC_TARGET_X86_64
-    IMAGE_OPTIONAL_HEADER64 opthdr;
-#else
-#ifdef _WIN64
-    IMAGE_OPTIONAL_HEADER32 opthdr;
-#else
-    IMAGE_OPTIONAL_HEADER opthdr;
-#endif
-#endif
-};
-
-struct pe_reloc_header {
-    DWORD offset;
-    DWORD size;
-};
-
-struct pe_rsrc_header {
-    struct _IMAGE_FILE_HEADER filehdr;
-    struct _IMAGE_SECTION_HEADER sectionhdr;
-};
-
-struct pe_rsrc_reloc {
-    DWORD offset;
-    DWORD size;
-    WORD type;
-};
-#pragma pack(pop)
-
-/* ------------------------------------------------------------- */
-/* internal temporary structures */
-
-/*
-#define IMAGE_SCN_CNT_CODE                  0x00000020
-#define IMAGE_SCN_CNT_INITIALIZED_DATA      0x00000040
-#define IMAGE_SCN_CNT_UNINITIALIZED_DATA    0x00000080
-#define IMAGE_SCN_MEM_DISCARDABLE           0x02000000
-#define IMAGE_SCN_MEM_SHARED                0x10000000
-#define IMAGE_SCN_MEM_EXECUTE               0x20000000
-#define IMAGE_SCN_MEM_READ                  0x40000000
-#define IMAGE_SCN_MEM_WRITE                 0x80000000
-*/
-
-enum {
-    sec_text = 0,
-    sec_data ,
-    sec_bss ,
-    sec_idata ,
-    sec_pdata ,
-    sec_other ,
-    sec_rsrc ,
-    sec_stab ,
-    sec_reloc ,
-    sec_last
-};
-
-static const DWORD pe_sec_flags[] = {
-    0x60000020, /* ".text"     , */
-    0xC0000040, /* ".data"     , */
-    0xC0000080, /* ".bss"      , */
-    0x40000040, /* ".idata"    , */
-    0x40000040, /* ".pdata"    , */
-    0xE0000060, /* < other >   , */
-    0x40000040, /* ".rsrc"     , */
-    0x42000802, /* ".stab"     , */
-    0x42000040, /* ".reloc"    , */
-};
-
-struct section_info {
-    int cls, ord;
-    char name[32];
-    DWORD sh_addr;
-    DWORD sh_size;
-    DWORD sh_flags;
-    unsigned char *data;
-    DWORD data_size;
-    IMAGE_SECTION_HEADER ish;
-};
-
-struct import_symbol {
-    int sym_index;
-    int iat_index;
-    int thk_offset;
-};
-
-struct pe_import_info {
-    int dll_index;
-    int sym_count;
-    struct import_symbol **symbols;
-};
-
-struct pe_info {
-    TCCState *s1;
-    Section *reloc;
-    Section *thunk;
-    const char *filename;
-    int type;
-    DWORD sizeofheaders;
-    ADDR3264 imagebase;
-    const char *start_symbol;
-    DWORD start_addr;
-    DWORD imp_offs;
-    DWORD imp_size;
-    DWORD iat_offs;
-    DWORD iat_size;
-    DWORD exp_offs;
-    DWORD exp_size;
-    int subsystem;
-    DWORD section_align;
-    DWORD file_align;
-    struct section_info *sec_info;
-    int sec_count;
-    struct pe_import_info **imp_info;
-    int imp_count;
-};
-
-#define PE_NUL 0
-#define PE_DLL 1
-#define PE_GUI 2
-#define PE_EXE 3
-#define PE_RUN 4
-
-/* --------------------------------------------*/
-
-static const char *pe_export_name(TCCState *s1, ElfW(Sym) *sym)
-{
-    const char *name = (char*)symtab_section->link->data + sym->st_name;
-    if (s1->leading_underscore && name[0] == '_' && !(sym->st_other & ST_PE_STDCALL))
-        return name + 1;
-    return name;
-}
-
-static int pe_find_import(TCCState * s1, ElfW(Sym) *sym)
-{
-    char buffer[200];
-    const char *s, *p;
-    int sym_index = 0, n = 0;
-    int a, err = 0;
-
-    do {
-        s = pe_export_name(s1, sym);
-        a = 0;
-        if (n) {
-            /* second try: */
-	    if (sym->st_other & ST_PE_STDCALL) {
-                /* try w/0 stdcall deco (windows API convention) */
-	        p = strrchr(s, '@');
-	        if (!p || s[0] != '_')
-	            break;
-	        strcpy(buffer, s+1)[p-s-1] = 0;
-	    } else if (s[0] != '_') { /* try non-ansi function */
-	        buffer[0] = '_', strcpy(buffer + 1, s);
-	    } else if (0 == memcmp(s, "__imp_", 6)) { /* mingw 2.0 */
-	        strcpy(buffer, s + 6), a = 1;
-	    } else if (0 == memcmp(s, "_imp__", 6)) { /* mingw 3.7 */
-	        strcpy(buffer, s + 6), a = 1;
-	    } else {
-	        continue;
-	    }
-	    s = buffer;
-        }
-        sym_index = find_elf_sym(s1->dynsymtab_section, s);
-        // printf("find (%d) %d %s\n", n, sym_index, s);
-        if (sym_index
-            && ELFW(ST_TYPE)(sym->st_info) == STT_OBJECT
-            && 0 == (sym->st_other & ST_PE_IMPORT)
-            && 0 == a
-            ) err = -1, sym_index = 0;
-    } while (0 == sym_index && ++n < 2);
-    return n == 2 ? err : sym_index;
-}
-
-/*----------------------------------------------------------------------------*/
-
-static int dynarray_assoc(void **pp, int n, int key)
-{
-    int i;
-    for (i = 0; i < n; ++i, ++pp)
-    if (key == **(int **) pp)
-        return i;
-    return -1;
-}
-
-#if 0
-ST_FN DWORD umin(DWORD a, DWORD b)
-{
-    return a < b ? a : b;
-}
-#endif
-
-static DWORD umax(DWORD a, DWORD b)
-{
-    return a < b ? b : a;
-}
-
-static DWORD pe_file_align(struct pe_info *pe, DWORD n)
-{
-    return (n + (pe->file_align - 1)) & ~(pe->file_align - 1);
-}
-
-static DWORD pe_virtual_align(struct pe_info *pe, DWORD n)
-{
-    return (n + (pe->section_align - 1)) & ~(pe->section_align - 1);
-}
-
-static void pe_align_section(Section *s, int a)
-{
-    int i = s->data_offset & (a-1);
-    if (i)
-        section_ptr_add(s, a - i);
-}
-
-static void pe_set_datadir(struct pe_header *hdr, int dir, DWORD addr, DWORD size)
-{
-    hdr->opthdr.DataDirectory[dir].VirtualAddress = addr;
-    hdr->opthdr.DataDirectory[dir].Size = size;
-}
-
-static int pe_fwrite(void *data, unsigned len, FILE *fp, DWORD *psum)
-{
-    if (psum) {
-        DWORD sum = *psum;
-        WORD *p = data;
-        int i;
-        for (i = len; i > 0; i -= 2) {
-            sum += (i >= 2) ? *p++ : *(BYTE*)p;
-            sum = (sum + (sum >> 16)) & 0xFFFF;
-        }
-        *psum = sum;
-    }
-    return len == fwrite(data, 1, len, fp) ? 0 : -1;
-}
-
-static void pe_fpad(FILE *fp, DWORD new_pos)
-{
-    DWORD pos = ftell(fp);
-    while (++pos <= new_pos)
-        fputc(0, fp);
-}
-
-/*----------------------------------------------------------------------------*/
-static int pe_write(struct pe_info *pe)
-{
-    static const struct pe_header pe_template = {
-    {
-    /* IMAGE_DOS_HEADER doshdr */
-    0x5A4D, /*WORD e_magic;         Magic number */
-    0x0090, /*WORD e_cblp;          Bytes on last page of file */
-    0x0003, /*WORD e_cp;            Pages in file */
-    0x0000, /*WORD e_crlc;          Relocations */
-
-    0x0004, /*WORD e_cparhdr;       Size of header in paragraphs */
-    0x0000, /*WORD e_minalloc;      Minimum extra paragraphs needed */
-    0xFFFF, /*WORD e_maxalloc;      Maximum extra paragraphs needed */
-    0x0000, /*WORD e_ss;            Initial (relative) SS value */
-
-    0x00B8, /*WORD e_sp;            Initial SP value */
-    0x0000, /*WORD e_csum;          Checksum */
-    0x0000, /*WORD e_ip;            Initial IP value */
-    0x0000, /*WORD e_cs;            Initial (relative) CS value */
-    0x0040, /*WORD e_lfarlc;        File address of relocation table */
-    0x0000, /*WORD e_ovno;          Overlay number */
-    {0,0,0,0}, /*WORD e_res[4];     Reserved words */
-    0x0000, /*WORD e_oemid;         OEM identifier (for e_oeminfo) */
-    0x0000, /*WORD e_oeminfo;       OEM information; e_oemid specific */
-    {0,0,0,0,0,0,0,0,0,0}, /*WORD e_res2[10];      Reserved words */
-    0x00000080  /*DWORD   e_lfanew;        File address of new exe header */
-    },{
-    /* BYTE dosstub[0x40] */
-    /* 14 code bytes + "This program cannot be run in DOS mode.\r\r\n$" + 6 * 0x00 */
-    0x0e,0x1f,0xba,0x0e,0x00,0xb4,0x09,0xcd,0x21,0xb8,0x01,0x4c,0xcd,0x21,0x54,0x68,
-    0x69,0x73,0x20,0x70,0x72,0x6f,0x67,0x72,0x61,0x6d,0x20,0x63,0x61,0x6e,0x6e,0x6f,
-    0x74,0x20,0x62,0x65,0x20,0x72,0x75,0x6e,0x20,0x69,0x6e,0x20,0x44,0x4f,0x53,0x20,
-    0x6d,0x6f,0x64,0x65,0x2e,0x0d,0x0d,0x0a,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-    },
-    0x00004550, /* DWORD nt_sig = IMAGE_NT_SIGNATURE */
-    {
-    /* IMAGE_FILE_HEADER filehdr */
-    IMAGE_FILE_MACHINE, /*WORD    Machine; */
-    0x0003, /*WORD    NumberOfSections; */
-    0x00000000, /*DWORD   TimeDateStamp; */
-    0x00000000, /*DWORD   PointerToSymbolTable; */
-    0x00000000, /*DWORD   NumberOfSymbols; */
-#if defined(TCC_TARGET_X86_64)
-    0x00F0, /*WORD    SizeOfOptionalHeader; */
-    0x022F  /*WORD    Characteristics; */
-#define CHARACTERISTICS_DLL 0x222E
-#elif defined(TCC_TARGET_I386)
-    0x00E0, /*WORD    SizeOfOptionalHeader; */
-    0x030F  /*WORD    Characteristics; */
-#define CHARACTERISTICS_DLL 0x230E
-#elif defined(TCC_TARGET_ARM)
-    0x00E0, /*WORD    SizeOfOptionalHeader; */
-    0x010F, /*WORD    Characteristics; */
-#define CHARACTERISTICS_DLL 0x230F
-#endif
-},{
-    /* IMAGE_OPTIONAL_HEADER opthdr */
-    /* Standard fields. */
-#ifdef TCC_TARGET_X86_64
-    0x020B, /*WORD    Magic; */
-#else
-    0x010B, /*WORD    Magic; */
-#endif
-    0x06, /*BYTE    MajorLinkerVersion; */
-    0x00, /*BYTE    MinorLinkerVersion; */
-    0x00000000, /*DWORD   SizeOfCode; */
-    0x00000000, /*DWORD   SizeOfInitializedData; */
-    0x00000000, /*DWORD   SizeOfUninitializedData; */
-    0x00000000, /*DWORD   AddressOfEntryPoint; */
-    0x00000000, /*DWORD   BaseOfCode; */
-#ifndef TCC_TARGET_X86_64
-    0x00000000, /*DWORD   BaseOfData; */
-#endif
-    /* NT additional fields. */
-#if defined(TCC_TARGET_ARM)
-    0x00100000,	    /*DWORD   ImageBase; */
-#else
-    0x00400000,	    /*DWORD   ImageBase; */
-#endif
-    0x00001000, /*DWORD   SectionAlignment; */
-    0x00000200, /*DWORD   FileAlignment; */
-    0x0004, /*WORD    MajorOperatingSystemVersion; */
-    0x0000, /*WORD    MinorOperatingSystemVersion; */
-    0x0000, /*WORD    MajorImageVersion; */
-    0x0000, /*WORD    MinorImageVersion; */
-    0x0004, /*WORD    MajorSubsystemVersion; */
-    0x0000, /*WORD    MinorSubsystemVersion; */
-    0x00000000, /*DWORD   Win32VersionValue; */
-    0x00000000, /*DWORD   SizeOfImage; */
-    0x00000200, /*DWORD   SizeOfHeaders; */
-    0x00000000, /*DWORD   CheckSum; */
-    0x0002, /*WORD    Subsystem; */
-    0x0000, /*WORD    DllCharacteristics; */
-    0x00100000, /*DWORD   SizeOfStackReserve; */
-    0x00001000, /*DWORD   SizeOfStackCommit; */
-    0x00100000, /*DWORD   SizeOfHeapReserve; */
-    0x00001000, /*DWORD   SizeOfHeapCommit; */
-    0x00000000, /*DWORD   LoaderFlags; */
-    0x00000010, /*DWORD   NumberOfRvaAndSizes; */
-
-    /* IMAGE_DATA_DIRECTORY DataDirectory[16]; */
-    {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},
-     {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}}
-    }};
-
-    struct pe_header pe_header = pe_template;
-
-    int i;
-    FILE *op;
-    DWORD file_offset, sum;
-    struct section_info *si;
-    IMAGE_SECTION_HEADER *psh;
-
-    op = fopen(pe->filename, "wb");
-    if (NULL == op) {
-        tcc_error_noabort("could not write '%s': %s", pe->filename, strerror(errno));
-        return -1;
-    }
-
-    pe->sizeofheaders = pe_file_align(pe,
-        sizeof (struct pe_header)
-        + pe->sec_count * sizeof (IMAGE_SECTION_HEADER)
-        );
-
-    file_offset = pe->sizeofheaders;
-
-    if (2 == pe->s1->verbose)
-        printf("-------------------------------"
-               "\n  virt   file   size  section" "\n");
-    for (i = 0; i < pe->sec_count; ++i) {
-        DWORD addr, size;
-        const char *sh_name;
-
-        si = pe->sec_info + i;
-        sh_name = si->name;
-        addr = si->sh_addr - pe->imagebase;
-        size = si->sh_size;
-        psh = &si->ish;
-
-        if (2 == pe->s1->verbose)
-            printf("%6x %6x %6x  %s\n",
-                (unsigned)addr, (unsigned)file_offset, (unsigned)size, sh_name);
-
-        switch (si->cls) {
-            case sec_text:
-                pe_header.opthdr.BaseOfCode = addr;
-                break;
-
-            case sec_data:
-#ifndef TCC_TARGET_X86_64
-                pe_header.opthdr.BaseOfData = addr;
-#endif
-                break;
-
-            case sec_bss:
-                break;
-
-            case sec_reloc:
-                pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_BASERELOC, addr, size);
-                break;
-
-            case sec_rsrc:
-                pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_RESOURCE, addr, size);
-                break;
-
-            case sec_pdata:
-                pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_EXCEPTION, addr, size);
-                break;
-
-            case sec_stab:
-                break;
-        }
-
-        if (pe->thunk == pe->s1->sections[si->ord]) {
-            if (pe->imp_size) {
-                pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_IMPORT,
-                    pe->imp_offs + addr, pe->imp_size);
-                pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_IAT,
-                    pe->iat_offs + addr, pe->iat_size);
-            }
-            if (pe->exp_size) {
-                pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_EXPORT,
-                    pe->exp_offs + addr, pe->exp_size);
-            }
-        }
-
-        strncpy((char*)psh->Name, sh_name, sizeof psh->Name);
-
-        psh->Characteristics = pe_sec_flags[si->cls];
-        psh->VirtualAddress = addr;
-        psh->Misc.VirtualSize = size;
-        pe_header.opthdr.SizeOfImage =
-            umax(pe_virtual_align(pe, size + addr), pe_header.opthdr.SizeOfImage);
-
-        if (si->data_size) {
-            psh->PointerToRawData = file_offset;
-            file_offset = pe_file_align(pe, file_offset + si->data_size);
-            psh->SizeOfRawData = file_offset - psh->PointerToRawData;
-            if (si->cls == sec_text)
-                pe_header.opthdr.SizeOfCode += psh->SizeOfRawData;
-            else
-                pe_header.opthdr.SizeOfInitializedData += psh->SizeOfRawData;
-        }
-    }
-
-    //pe_header.filehdr.TimeDateStamp = time(NULL);
-    pe_header.filehdr.NumberOfSections = pe->sec_count;
-    pe_header.opthdr.AddressOfEntryPoint = pe->start_addr;
-    pe_header.opthdr.SizeOfHeaders = pe->sizeofheaders;
-    pe_header.opthdr.ImageBase = pe->imagebase;
-    pe_header.opthdr.Subsystem = pe->subsystem;
-    if (pe->s1->pe_stack_size)
-        pe_header.opthdr.SizeOfStackReserve = pe->s1->pe_stack_size;
-    if (PE_DLL == pe->type)
-        pe_header.filehdr.Characteristics = CHARACTERISTICS_DLL;
-    pe_header.filehdr.Characteristics |= pe->s1->pe_characteristics;
-
-    sum = 0;
-    pe_fwrite(&pe_header, sizeof pe_header, op, &sum);
-    for (i = 0; i < pe->sec_count; ++i)
-        pe_fwrite(&pe->sec_info[i].ish, sizeof(IMAGE_SECTION_HEADER), op, &sum);
-    pe_fpad(op, pe->sizeofheaders);
-    for (i = 0; i < pe->sec_count; ++i) {
-        si = pe->sec_info + i;
-        psh = &si->ish;
-        if (si->data_size) {
-            pe_fwrite(si->data, si->data_size, op, &sum);
-            file_offset = psh->PointerToRawData + psh->SizeOfRawData;
-            pe_fpad(op, file_offset);
-        }
-    }
-
-    pe_header.opthdr.CheckSum = sum + file_offset;
-    fseek(op, offsetof(struct pe_header, opthdr.CheckSum), SEEK_SET);
-    pe_fwrite(&pe_header.opthdr.CheckSum, sizeof pe_header.opthdr.CheckSum, op, NULL);
-    fclose (op);
-#ifndef _WIN32
-    chmod(pe->filename, 0777);
-#endif
-
-    if (2 == pe->s1->verbose)
-        printf("-------------------------------\n");
-    if (pe->s1->verbose)
-        printf("<- %s (%u bytes)\n", pe->filename, (unsigned)file_offset);
-
-    return 0;
-}
-
-/*----------------------------------------------------------------------------*/
-
-static struct import_symbol *pe_add_import(struct pe_info *pe, int sym_index)
-{
-    int i;
-    int dll_index;
-    struct pe_import_info *p;
-    struct import_symbol *s;
-    ElfW(Sym) *isym;
-
-    isym = (ElfW(Sym) *)pe->s1->dynsymtab_section->data + sym_index;
-    dll_index = isym->st_size;
-
-    i = dynarray_assoc ((void**)pe->imp_info, pe->imp_count, dll_index);
-    if (-1 != i) {
-        p = pe->imp_info[i];
-        goto found_dll;
-    }
-    p = tcc_mallocz(sizeof *p);
-    p->dll_index = dll_index;
-    dynarray_add(&pe->imp_info, &pe->imp_count, p);
-
-found_dll:
-    i = dynarray_assoc ((void**)p->symbols, p->sym_count, sym_index);
-    if (-1 != i)
-        return p->symbols[i];
-
-    s = tcc_mallocz(sizeof *s);
-    dynarray_add(&p->symbols, &p->sym_count, s);
-    s->sym_index = sym_index;
-    return s;
-}
-
-void pe_free_imports(struct pe_info *pe)
-{
-    int i;
-    for (i = 0; i < pe->imp_count; ++i) {
-        struct pe_import_info *p = pe->imp_info[i];
-        dynarray_reset(&p->symbols, &p->sym_count);
-    }
-    dynarray_reset(&pe->imp_info, &pe->imp_count);
-}
-
-/*----------------------------------------------------------------------------*/
-static void pe_build_imports(struct pe_info *pe)
-{
-    int thk_ptr, ent_ptr, dll_ptr, sym_cnt, i;
-    DWORD rva_base = pe->thunk->sh_addr - pe->imagebase;
-    int ndlls = pe->imp_count;
-
-    for (sym_cnt = i = 0; i < ndlls; ++i)
-        sym_cnt += pe->imp_info[i]->sym_count;
-
-    if (0 == sym_cnt)
-        return;
-
-    pe_align_section(pe->thunk, 16);
-
-    pe->imp_offs = dll_ptr = pe->thunk->data_offset;
-    pe->imp_size = (ndlls + 1) * sizeof(IMAGE_IMPORT_DESCRIPTOR);
-    pe->iat_offs = dll_ptr + pe->imp_size;
-    pe->iat_size = (sym_cnt + ndlls) * sizeof(ADDR3264);
-    section_ptr_add(pe->thunk, pe->imp_size + 2*pe->iat_size);
-
-    thk_ptr = pe->iat_offs;
-    ent_ptr = pe->iat_offs + pe->iat_size;
-
-    for (i = 0; i < pe->imp_count; ++i) {
-        IMAGE_IMPORT_DESCRIPTOR *hdr;
-        int k, n, dllindex;
-        ADDR3264 v;
-        struct pe_import_info *p = pe->imp_info[i];
-        const char *name;
-        DLLReference *dllref;
-
-        dllindex = p->dll_index;
-        if (dllindex)
-            name = (dllref = pe->s1->loaded_dlls[dllindex-1])->name;
-        else
-            name = "", dllref = NULL;
-
-        /* put the dll name into the import header */
-        v = put_elf_str(pe->thunk, name);
-        hdr = (IMAGE_IMPORT_DESCRIPTOR*)(pe->thunk->data + dll_ptr);
-        hdr->FirstThunk = thk_ptr + rva_base;
-        hdr->OriginalFirstThunk = ent_ptr + rva_base;
-        hdr->Name = v + rva_base;
-
-        for (k = 0, n = p->sym_count; k <= n; ++k) {
-            if (k < n) {
-                int iat_index = p->symbols[k]->iat_index;
-                int sym_index = p->symbols[k]->sym_index;
-                ElfW(Sym) *imp_sym = (ElfW(Sym) *)pe->s1->dynsymtab_section->data + sym_index;
-                ElfW(Sym) *org_sym = (ElfW(Sym) *)symtab_section->data + iat_index;
-                const char *name = (char*)pe->s1->dynsymtab_section->link->data + imp_sym->st_name;
-                int ordinal;
-
-                org_sym->st_value = thk_ptr;
-                org_sym->st_shndx = pe->thunk->sh_num;
-
-                if (dllref)
-                    v = 0, ordinal = imp_sym->st_value; /* ordinal from pe_load_def */
-                else
-                    ordinal = 0, v = imp_sym->st_value; /* address from tcc_add_symbol() */
-
-#ifdef TCC_IS_NATIVE
-                if (pe->type == PE_RUN) {
-                    if (dllref) {
-                        if ( !dllref->handle )
-                            dllref->handle = LoadLibrary(dllref->name);
-                        v = (ADDR3264)GetProcAddress(dllref->handle, ordinal?(char*)0+ordinal:name);
-                    }
-                    if (!v)
-                        tcc_error_noabort("can't build symbol '%s'", name);
-                } else
-#endif
-                if (ordinal) {
-                    v = ordinal | (ADDR3264)1 << (sizeof(ADDR3264)*8 - 1);
-                } else {
-                    v = pe->thunk->data_offset + rva_base;
-                    section_ptr_add(pe->thunk, sizeof(WORD)); /* hint, not used */
-                    put_elf_str(pe->thunk, name);
-                }
-
-            } else {
-                v = 0; /* last entry is zero */
-            }
-
-            *(ADDR3264*)(pe->thunk->data+thk_ptr) =
-            *(ADDR3264*)(pe->thunk->data+ent_ptr) = v;
-            thk_ptr += sizeof (ADDR3264);
-            ent_ptr += sizeof (ADDR3264);
-        }
-        dll_ptr += sizeof(IMAGE_IMPORT_DESCRIPTOR);
-    }
-}
-
-/* ------------------------------------------------------------- */
-
-struct pe_sort_sym
-{
-    int index;
-    const char *name;
-};
-
-static int sym_cmp(const void *va, const void *vb)
-{
-    const char *ca = (*(struct pe_sort_sym**)va)->name;
-    const char *cb = (*(struct pe_sort_sym**)vb)->name;
-    return strcmp(ca, cb);
-}
-
-static void pe_build_exports(struct pe_info *pe)
-{
-    ElfW(Sym) *sym;
-    int sym_index, sym_end;
-    DWORD rva_base, func_o, name_o, ord_o, str_o;
-    IMAGE_EXPORT_DIRECTORY *hdr;
-    int sym_count, ord;
-    struct pe_sort_sym **sorted, *p;
-
-    FILE *op;
-    char buf[260];
-    const char *dllname;
-    const char *name;
-
-    rva_base = pe->thunk->sh_addr - pe->imagebase;
-    sym_count = 0, sorted = NULL, op = NULL;
-
-    sym_end = symtab_section->data_offset / sizeof(ElfW(Sym));
-    for (sym_index = 1; sym_index < sym_end; ++sym_index) {
-        sym = (ElfW(Sym)*)symtab_section->data + sym_index;
-        name = pe_export_name(pe->s1, sym);
-        if ((sym->st_other & ST_PE_EXPORT)
-            /* export only symbols from actually written sections */
-            && pe->s1->sections[sym->st_shndx]->sh_addr) {
-            p = tcc_malloc(sizeof *p);
-            p->index = sym_index;
-            p->name = name;
-            dynarray_add(&sorted, &sym_count, p);
-        }
-#if 0
-        if (sym->st_other & ST_PE_EXPORT)
-            printf("export: %s\n", name);
-        if (sym->st_other & ST_PE_STDCALL)
-            printf("stdcall: %s\n", name);
-#endif
-    }
-
-    if (0 == sym_count)
-        return;
-
-    qsort (sorted, sym_count, sizeof *sorted, sym_cmp);
-
-    pe_align_section(pe->thunk, 16);
-    dllname = tcc_basename(pe->filename);
-
-    pe->exp_offs = pe->thunk->data_offset;
-    func_o = pe->exp_offs + sizeof(IMAGE_EXPORT_DIRECTORY);
-    name_o = func_o + sym_count * sizeof (DWORD);
-    ord_o = name_o + sym_count * sizeof (DWORD);
-    str_o = ord_o + sym_count * sizeof(WORD);
-
-    hdr = section_ptr_add(pe->thunk, str_o - pe->exp_offs);
-    hdr->Characteristics        = 0;
-    hdr->Base                   = 1;
-    hdr->NumberOfFunctions      = sym_count;
-    hdr->NumberOfNames          = sym_count;
-    hdr->AddressOfFunctions     = func_o + rva_base;
-    hdr->AddressOfNames         = name_o + rva_base;
-    hdr->AddressOfNameOrdinals  = ord_o + rva_base;
-    hdr->Name                   = str_o + rva_base;
-    put_elf_str(pe->thunk, dllname);
-
-#if 1
-    /* automatically write exports to <output-filename>.def */
-    pstrcpy(buf, sizeof buf, pe->filename);
-    strcpy(tcc_fileextension(buf), ".def");
-    op = fopen(buf, "w");
-    if (NULL == op) {
-        tcc_error_noabort("could not create '%s': %s", buf, strerror(errno));
-    } else {
-        fprintf(op, "LIBRARY %s\n\nEXPORTS\n", dllname);
-        if (pe->s1->verbose)
-            printf("<- %s (%d symbol%s)\n", buf, sym_count, &"s"[sym_count < 2]);
-    }
-#endif
-
-    for (ord = 0; ord < sym_count; ++ord)
-    {
-        p = sorted[ord], sym_index = p->index, name = p->name;
-        /* insert actual address later in pe_relocate_rva */
-        put_elf_reloc(symtab_section, pe->thunk,
-            func_o, R_XXX_RELATIVE, sym_index);
-        *(DWORD*)(pe->thunk->data + name_o)
-            = pe->thunk->data_offset + rva_base;
-        *(WORD*)(pe->thunk->data + ord_o)
-            = ord;
-        put_elf_str(pe->thunk, name);
-        func_o += sizeof (DWORD);
-        name_o += sizeof (DWORD);
-        ord_o += sizeof (WORD);
-        if (op)
-            fprintf(op, "%s\n", name);
-    }
-    pe->exp_size = pe->thunk->data_offset - pe->exp_offs;
-    dynarray_reset(&sorted, &sym_count);
-    if (op)
-        fclose(op);
-}
-
-/* ------------------------------------------------------------- */
-static void pe_build_reloc (struct pe_info *pe)
-{
-    DWORD offset, block_ptr, addr;
-    int count, i;
-    ElfW_Rel *rel, *rel_end;
-    Section *s = NULL, *sr;
-
-    offset = addr = block_ptr = count = i = 0;
-    rel = rel_end = NULL;
-
-    for(;;) {
-        if (rel < rel_end) {
-            int type = ELFW(R_TYPE)(rel->r_info);
-            addr = rel->r_offset + s->sh_addr;
-            ++ rel;
-            if (type != REL_TYPE_DIRECT)
-                continue;
-            if (count == 0) { /* new block */
-                block_ptr = pe->reloc->data_offset;
-                section_ptr_add(pe->reloc, sizeof(struct pe_reloc_header));
-                offset = addr & 0xFFFFFFFF<<12;
-            }
-            if ((addr -= offset)  < (1<<12)) { /* one block spans 4k addresses */
-                WORD *wp = section_ptr_add(pe->reloc, sizeof (WORD));
-                *wp = addr | PE_IMAGE_REL<<12;
-                ++count;
-                continue;
-            }
-            -- rel;
-
-        } else if (i < pe->sec_count) {
-            sr = (s = pe->s1->sections[pe->sec_info[i++].ord])->reloc;
-            if (sr) {
-                rel = (ElfW_Rel *)sr->data;
-                rel_end = (ElfW_Rel *)(sr->data + sr->data_offset);
-            }
-            continue;
-        }
-
-        if (count) {
-            /* store the last block and ready for a new one */
-            struct pe_reloc_header *hdr;
-            if (count & 1) /* align for DWORDS */
-                section_ptr_add(pe->reloc, sizeof(WORD)), ++count;
-            hdr = (struct pe_reloc_header *)(pe->reloc->data + block_ptr);
-            hdr -> offset = offset - pe->imagebase;
-            hdr -> size = count * sizeof(WORD) + sizeof(struct pe_reloc_header);
-            count = 0;
-        }
-
-        if (rel >= rel_end)
-            break;
-    }
-}
-
-/* ------------------------------------------------------------- */
-static int pe_section_class(Section *s)
-{
-    int type, flags;
-    const char *name;
-
-    type = s->sh_type;
-    flags = s->sh_flags;
-    name = s->name;
-    if (flags & SHF_ALLOC) {
-        if (type == SHT_PROGBITS) {
-            if (flags & SHF_EXECINSTR)
-                return sec_text;
-            if (flags & SHF_WRITE)
-                return sec_data;
-            if (0 == strcmp(name, ".rsrc"))
-                return sec_rsrc;
-            if (0 == strcmp(name, ".iedat"))
-                return sec_idata;
-            if (0 == strcmp(name, ".pdata"))
-                return sec_pdata;
-            return sec_other;
-        } else if (type == SHT_NOBITS) {
-            if (flags & SHF_WRITE)
-                return sec_bss;
-        }
-    } else {
-        if (0 == strcmp(name, ".reloc"))
-            return sec_reloc;
-        if (0 == strncmp(name, ".stab", 5)) /* .stab and .stabstr */
-            return sec_stab;
-    }
-    return -1;
-}
-
-static int pe_assign_addresses (struct pe_info *pe)
-{
-    int i, k, o, c;
-    DWORD addr;
-    int *section_order;
-    struct section_info *si;
-    Section *s;
-
-    if (PE_DLL == pe->type)
-        pe->reloc = new_section(pe->s1, ".reloc", SHT_PROGBITS, 0);
-
-    // pe->thunk = new_section(pe->s1, ".iedat", SHT_PROGBITS, SHF_ALLOC);
-
-    section_order = tcc_malloc(pe->s1->nb_sections * sizeof (int));
-    for (o = k = 0 ; k < sec_last; ++k) {
-        for (i = 1; i < pe->s1->nb_sections; ++i) {
-            s = pe->s1->sections[i];
-            if (k == pe_section_class(s)) {
-                // printf("%s %d\n", s->name, k);
-                s->sh_addr = pe->imagebase;
-                section_order[o++] = i;
-            }
-        }
-    }
-
-    pe->sec_info = tcc_mallocz(o * sizeof (struct section_info));
-    addr = pe->imagebase + 1;
-
-    for (i = 0; i < o; ++i)
-    {
-        k = section_order[i];
-        s = pe->s1->sections[k];
-        c = pe_section_class(s);
-        si = &pe->sec_info[pe->sec_count];
-
-#ifdef PE_MERGE_DATA
-        if (c == sec_bss && pe->sec_count && si[-1].cls == sec_data) {
-            /* append .bss to .data */
-            s->sh_addr = addr = ((addr-1) | (s->sh_addralign-1)) + 1;
-            addr += s->data_offset;
-            si[-1].sh_size = addr - si[-1].sh_addr;
-            continue;
-        }
-#endif
-        if (c == sec_stab && 0 == pe->s1->do_debug)
-            continue;
-
-        strcpy(si->name, s->name);
-        si->cls = c;
-        si->ord = k;
-        si->sh_addr = s->sh_addr = addr = pe_virtual_align(pe, addr);
-        si->sh_flags = s->sh_flags;
-
-        if (c == sec_data && NULL == pe->thunk)
-            pe->thunk = s;
-
-        if (s == pe->thunk) {
-            pe_build_imports(pe);
-            pe_build_exports(pe);
-        }
-
-        if (c == sec_reloc)
-            pe_build_reloc (pe);
-
-        if (s->data_offset)
-        {
-            if (s->sh_type != SHT_NOBITS) {
-                si->data = s->data;
-                si->data_size = s->data_offset;
-            }
-
-            addr += s->data_offset;
-            si->sh_size = s->data_offset;
-            ++pe->sec_count;
-        }
-        // printf("%08x %05x %s\n", si->sh_addr, si->sh_size, si->name);
-    }
-
-#if 0
-    for (i = 1; i < pe->s1->nb_sections; ++i) {
-        Section *s = pe->s1->sections[i];
-        int type = s->sh_type;
-        int flags = s->sh_flags;
-        printf("section %-16s %-10s %5x %s,%s,%s\n",
-            s->name,
-            type == SHT_PROGBITS ? "progbits" :
-            type == SHT_NOBITS ? "nobits" :
-            type == SHT_SYMTAB ? "symtab" :
-            type == SHT_STRTAB ? "strtab" :
-            type == SHT_RELX ? "rel" : "???",
-            s->data_offset,
-            flags & SHF_ALLOC ? "alloc" : "",
-            flags & SHF_WRITE ? "write" : "",
-            flags & SHF_EXECINSTR ? "exec" : ""
-            );
-    }
-    pe->s1->verbose = 2;
-#endif
-
-    tcc_free(section_order);
-    return 0;
-}
-
-/* ------------------------------------------------------------- */
-static void pe_relocate_rva (struct pe_info *pe, Section *s)
-{
-    Section *sr = s->reloc;
-    ElfW_Rel *rel, *rel_end;
-    rel_end = (ElfW_Rel *)(sr->data + sr->data_offset);
-    for(rel = (ElfW_Rel *)sr->data; rel < rel_end; rel++) {
-        if (ELFW(R_TYPE)(rel->r_info) == R_XXX_RELATIVE) {
-            int sym_index = ELFW(R_SYM)(rel->r_info);
-            DWORD addr = s->sh_addr;
-            if (sym_index) {
-                ElfW(Sym) *sym = (ElfW(Sym) *)symtab_section->data + sym_index;
-                addr = sym->st_value;
-            }
-            // printf("reloc rva %08x %08x %s\n", (DWORD)rel->r_offset, addr, s->name);
-            *(DWORD*)(s->data + rel->r_offset) += addr - pe->imagebase;
-        }
-    }
-}
-
-/*----------------------------------------------------------------------------*/
-
-static int pe_isafunc(int sym_index)
-{
-    Section *sr = text_section->reloc;
-    ElfW_Rel *rel, *rel_end;
-    Elf32_Word info = ELF32_R_INFO(sym_index, R_386_PC32);
-    if (!sr)
-        return 0;
-    rel_end = (ElfW_Rel *)(sr->data + sr->data_offset);
-    for (rel = (ElfW_Rel *)sr->data; rel < rel_end; rel++)
-        if (rel->r_info == info)
-            return 1;
-    return 0;
-}
-
-/*----------------------------------------------------------------------------*/
-static int pe_check_symbols(struct pe_info *pe)
-{
-    ElfW(Sym) *sym;
-    int sym_index, sym_end;
-    int ret = 0;
-
-    pe_align_section(text_section, 8);
-
-    sym_end = symtab_section->data_offset / sizeof(ElfW(Sym));
-    for (sym_index = 1; sym_index < sym_end; ++sym_index) {
-
-        sym = (ElfW(Sym) *)symtab_section->data + sym_index;
-        if (sym->st_shndx == SHN_UNDEF) {
-
-            const char *name = (char*)symtab_section->link->data + sym->st_name;
-            unsigned type = ELFW(ST_TYPE)(sym->st_info);
-            int imp_sym = pe_find_import(pe->s1, sym);
-            struct import_symbol *is;
-
-            if (imp_sym <= 0)
-                goto not_found;
-
-            if (type == STT_NOTYPE) {
-                /* symbols from assembler have no type, find out which */
-                if (pe_isafunc(sym_index))
-                    type = STT_FUNC;
-                else
-                    type = STT_OBJECT;
-            }
-
-            is = pe_add_import(pe, imp_sym);
-
-            if (type == STT_FUNC) {
-                unsigned long offset = is->thk_offset;
-                if (offset) {
-                    /* got aliased symbol, like stricmp and _stricmp */
-
-                } else {
-                    char buffer[100];
-                    WORD *p;
-
-                    offset = text_section->data_offset;
-                    /* add the 'jmp IAT[x]' instruction */
-#ifdef TCC_TARGET_ARM
-                    p = section_ptr_add(text_section, 8+4); // room for code and address
-                    (*(DWORD*)(p)) = 0xE59FC000; // arm code ldr ip, [pc] ; PC+8+0 = 0001xxxx
-                    (*(DWORD*)(p+2)) = 0xE59CF000; // arm code ldr pc, [ip]
-#else
-                    p = section_ptr_add(text_section, 8);
-                    *p = 0x25FF;
-#ifdef TCC_TARGET_X86_64
-                    *(DWORD*)(p+1) = (DWORD)-4;
-#endif
-#endif
-                    /* add a helper symbol, will be patched later in
-                       pe_build_imports */
-                    sprintf(buffer, "IAT.%s", name);
-                    is->iat_index = put_elf_sym(
-                        symtab_section, 0, sizeof(DWORD),
-                        ELFW(ST_INFO)(STB_GLOBAL, STT_OBJECT),
-                        0, SHN_UNDEF, buffer);
-#ifdef TCC_TARGET_ARM
-                    put_elf_reloc(symtab_section, text_section,
-                        offset + 8, R_XXX_THUNKFIX, is->iat_index); // offset to IAT position
-#else
-                    put_elf_reloc(symtab_section, text_section, 
-                        offset + 2, R_XXX_THUNKFIX, is->iat_index);
-#endif
-                    is->thk_offset = offset;
-                }
-
-                /* tcc_realloc might have altered sym's address */
-                sym = (ElfW(Sym) *)symtab_section->data + sym_index;
-
-                /* patch the original symbol */
-                sym->st_value = offset;
-                sym->st_shndx = text_section->sh_num;
-                sym->st_other &= ~ST_PE_EXPORT; /* do not export */
-                continue;
-            }
-
-            if (type == STT_OBJECT) { /* data, ptr to that should be */
-                if (0 == is->iat_index) {
-                    /* original symbol will be patched later in pe_build_imports */
-                    is->iat_index = sym_index;
-                    continue;
-                }
-            }
-
-        not_found:
-            if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK)
-                /* STB_WEAK undefined symbols are accepted */
-                continue;
-            tcc_error_noabort("undefined symbol '%s'%s", name,
-                imp_sym < 0 ? ", missing __declspec(dllimport)?":"");
-            ret = -1;
-
-        } else if (pe->s1->rdynamic
-                   && ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
-            /* if -rdynamic option, then export all non local symbols */
-            sym->st_other |= ST_PE_EXPORT;
-        }
-    }
-    return ret;
-}
-
-/*----------------------------------------------------------------------------*/
-#ifdef PE_PRINT_SECTIONS
-static void pe_print_section(FILE * f, Section * s)
-{
-    /* just if you're curious */
-    BYTE *p, *e, b;
-    int i, n, l, m;
-    p = s->data;
-    e = s->data + s->data_offset;
-    l = e - p;
-
-    fprintf(f, "section  \"%s\"", s->name);
-    if (s->link)
-        fprintf(f, "\nlink     \"%s\"", s->link->name);
-    if (s->reloc)
-        fprintf(f, "\nreloc    \"%s\"", s->reloc->name);
-    fprintf(f, "\nv_addr   %08X", (unsigned)s->sh_addr);
-    fprintf(f, "\ncontents %08X", (unsigned)l);
-    fprintf(f, "\n\n");
-
-    if (s->sh_type == SHT_NOBITS)
-        return;
-
-    if (0 == l)
-        return;
-
-    if (s->sh_type == SHT_SYMTAB)
-        m = sizeof(ElfW(Sym));
-    else if (s->sh_type == SHT_RELX)
-        m = sizeof(ElfW_Rel);
-    else
-        m = 16;
-
-    fprintf(f, "%-8s", "offset");
-    for (i = 0; i < m; ++i)
-        fprintf(f, " %02x", i);
-    n = 56;
-
-    if (s->sh_type == SHT_SYMTAB || s->sh_type == SHT_RELX) {
-        const char *fields1[] = {
-            "name",
-            "value",
-            "size",
-            "bind",
-            "type",
-            "other",
-            "shndx",
-            NULL
-        };
-
-        const char *fields2[] = {
-            "offs",
-            "type",
-            "symb",
-            NULL
-        };
-
-        const char **p;
-
-        if (s->sh_type == SHT_SYMTAB)
-            p = fields1, n = 106;
-        else
-            p = fields2, n = 58;
-
-        for (i = 0; p[i]; ++i)
-            fprintf(f, "%6s", p[i]);
-        fprintf(f, "  symbol");
-    }
-
-    fprintf(f, "\n");
-    for (i = 0; i < n; ++i)
-        fprintf(f, "-");
-    fprintf(f, "\n");
-
-    for (i = 0; i < l;)
-    {
-        fprintf(f, "%08X", i);
-        for (n = 0; n < m; ++n) {
-            if (n + i < l)
-                fprintf(f, " %02X", p[i + n]);
-            else
-                fprintf(f, "   ");
-        }
-
-        if (s->sh_type == SHT_SYMTAB) {
-            ElfW(Sym) *sym = (ElfW(Sym) *) (p + i);
-            const char *name = s->link->data + sym->st_name;
-            fprintf(f, "  %04X  %04X  %04X   %02X    %02X    %02X   %04X  \"%s\"",
-                    (unsigned)sym->st_name,
-                    (unsigned)sym->st_value,
-                    (unsigned)sym->st_size,
-                    (unsigned)ELFW(ST_BIND)(sym->st_info),
-                    (unsigned)ELFW(ST_TYPE)(sym->st_info),
-                    (unsigned)sym->st_other,
-                    (unsigned)sym->st_shndx,
-                    name);
-
-        } else if (s->sh_type == SHT_RELX) {
-            ElfW_Rel *rel = (ElfW_Rel *) (p + i);
-            ElfW(Sym) *sym =
-                (ElfW(Sym) *) s->link->data + ELFW(R_SYM)(rel->r_info);
-            const char *name = s->link->link->data + sym->st_name;
-            fprintf(f, "  %04X   %02X   %04X  \"%s\"",
-                    (unsigned)rel->r_offset,
-                    (unsigned)ELFW(R_TYPE)(rel->r_info),
-                    (unsigned)ELFW(R_SYM)(rel->r_info),
-                    name);
-        } else {
-            fprintf(f, "   ");
-            for (n = 0; n < m; ++n) {
-                if (n + i < l) {
-                    b = p[i + n];
-                    if (b < 32 || b >= 127)
-                        b = '.';
-                    fprintf(f, "%c", b);
-                }
-            }
-        }
-        i += m;
-        fprintf(f, "\n");
-    }
-    fprintf(f, "\n\n");
-}
-
-static void pe_print_sections(TCCState *s1, const char *fname)
-{
-    Section *s;
-    FILE *f;
-    int i;
-    f = fopen(fname, "w");
-    for (i = 1; i < s1->nb_sections; ++i) {
-        s = s1->sections[i];
-        pe_print_section(f, s);
-    }
-    pe_print_section(f, s1->dynsymtab_section);
-    fclose(f);
-}
-#endif
-
-/* ------------------------------------------------------------- */
-/* helper function for load/store to insert one more indirection */
-
-#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
-ST_FUNC SValue *pe_getimport(SValue *sv, SValue *v2)
-{
-    int r2;
-    if ((sv->r & (VT_VALMASK|VT_SYM)) != (VT_CONST|VT_SYM) || (sv->r2 != VT_CONST))
-        return sv;
-    if (!sv->sym->a.dllimport)
-        return sv;
-    // printf("import %04x %04x %04x %s\n", sv->type.t, sv->sym->type.t, sv->r, get_tok_str(sv->sym->v, NULL));
-    memset(v2, 0, sizeof *v2);
-    v2->type.t = VT_PTR;
-    v2->r = VT_CONST | VT_SYM | VT_LVAL;
-    v2->sym = sv->sym;
-
-    r2 = get_reg(RC_INT);
-    load(r2, v2);
-    v2->r = r2;
-    if ((uint32_t)sv->c.i) {
-        vpushv(v2);
-        vpushi(sv->c.i);
-        gen_opi('+');
-        *v2 = *vtop--;
-    }
-    v2->type.t = sv->type.t;
-    v2->r |= sv->r & VT_LVAL;
-    return v2;
-}
-#endif
-
-ST_FUNC int pe_putimport(TCCState *s1, int dllindex, const char *name, addr_t value)
-{
-    return set_elf_sym(
-        s1->dynsymtab_section,
-        value,
-        dllindex, /* st_size */
-        ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE),
-        0,
-        value ? SHN_ABS : SHN_UNDEF,
-        name
-        );
-}
-
-static int add_dllref(TCCState *s1, const char *dllname)
-{
-    DLLReference *dllref;
-    int i;
-    for (i = 0; i < s1->nb_loaded_dlls; ++i)
-        if (0 == strcmp(s1->loaded_dlls[i]->name, dllname))
-            return i + 1;
-    dllref = tcc_mallocz(sizeof(DLLReference) + strlen(dllname));
-    strcpy(dllref->name, dllname);
-    dynarray_add(&s1->loaded_dlls, &s1->nb_loaded_dlls, dllref);
-    return s1->nb_loaded_dlls;
-}
-
-/* ------------------------------------------------------------- */
-
-static int read_mem(int fd, unsigned offset, void *buffer, unsigned len)
-{
-    lseek(fd, offset, SEEK_SET);
-    return len == read(fd, buffer, len);
-}
-
-/* ------------------------------------------------------------- */
-
-PUB_FUNC int tcc_get_dllexports(const char *filename, char **pp)
-{
-    int l, i, n, n0, ret;
-    char *p;
-    int fd;
-
-    IMAGE_SECTION_HEADER ish;
-    IMAGE_EXPORT_DIRECTORY ied;
-    IMAGE_DOS_HEADER dh;
-    IMAGE_FILE_HEADER ih;
-    DWORD sig, ref, addr, ptr, namep;
-
-    int pef_hdroffset, opt_hdroffset, sec_hdroffset;
-
-    n = n0 = 0;
-    p = NULL;
-    ret = -1;
-
-    fd = open(filename, O_RDONLY | O_BINARY);
-    if (fd < 0)
-        goto the_end_1;
-    ret = 1;
-    if (!read_mem(fd, 0, &dh, sizeof dh))
-        goto the_end;
-    if (!read_mem(fd, dh.e_lfanew, &sig, sizeof sig))
-        goto the_end;
-    if (sig != 0x00004550)
-        goto the_end;
-    pef_hdroffset = dh.e_lfanew + sizeof sig;
-    if (!read_mem(fd, pef_hdroffset, &ih, sizeof ih))
-        goto the_end;
-    opt_hdroffset = pef_hdroffset + sizeof ih;
-    if (ih.Machine == 0x014C) {
-        IMAGE_OPTIONAL_HEADER32 oh;
-        sec_hdroffset = opt_hdroffset + sizeof oh;
-        if (!read_mem(fd, opt_hdroffset, &oh, sizeof oh))
-            goto the_end;
-        if (IMAGE_DIRECTORY_ENTRY_EXPORT >= oh.NumberOfRvaAndSizes)
-            goto the_end_0;
-        addr = oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
-    } else if (ih.Machine == 0x8664) {
-        IMAGE_OPTIONAL_HEADER64 oh;
-        sec_hdroffset = opt_hdroffset + sizeof oh;
-        if (!read_mem(fd, opt_hdroffset, &oh, sizeof oh))
-            goto the_end;
-        if (IMAGE_DIRECTORY_ENTRY_EXPORT >= oh.NumberOfRvaAndSizes)
-            goto the_end_0;
-        addr = oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
-    } else
-        goto the_end;
-
-    //printf("addr: %08x\n", addr);
-    for (i = 0; i < ih.NumberOfSections; ++i) {
-        if (!read_mem(fd, sec_hdroffset + i * sizeof ish, &ish, sizeof ish))
-            goto the_end;
-        //printf("vaddr: %08x\n", ish.VirtualAddress);
-        if (addr >= ish.VirtualAddress && addr < ish.VirtualAddress + ish.SizeOfRawData)
-            goto found;
-    }
-    goto the_end_0;
-
-found:
-    ref = ish.VirtualAddress - ish.PointerToRawData;
-    if (!read_mem(fd, addr - ref, &ied, sizeof ied))
-        goto the_end;
-
-    namep = ied.AddressOfNames - ref;
-    for (i = 0; i < ied.NumberOfNames; ++i) {
-        if (!read_mem(fd, namep, &ptr, sizeof ptr))
-            goto the_end;
-        namep += sizeof ptr;
-        for (l = 0;;) {
-            if (n+1 >= n0)
-                p = tcc_realloc(p, n0 = n0 ? n0 * 2 : 256);
-            if (!read_mem(fd, ptr - ref + l++, p + n, 1)) {
-                tcc_free(p), p = NULL;
-                goto the_end;
-            }
-            if (p[n++] == 0)
-                break;
-        }
-    }
-    if (p)
-        p[n] = 0;
-the_end_0:
-    ret = 0;
-the_end:
-    close(fd);
-the_end_1:
-    *pp = p;
-    return ret;
-}
-
-/* -------------------------------------------------------------
- *  This is for compiled windows resources in 'coff' format
- *  as generated by 'windres.exe -O coff ...'.
- */
-
-static int pe_load_res(TCCState *s1, int fd)
-{
-    struct pe_rsrc_header hdr;
-    Section *rsrc_section;
-    int i, ret = -1;
-    BYTE *ptr;
-    unsigned offs;
-
-    if (!read_mem(fd, 0, &hdr, sizeof hdr))
-        goto quit;
-
-    if (hdr.filehdr.Machine != IMAGE_FILE_MACHINE
-        || hdr.filehdr.NumberOfSections != 1
-        || strcmp((char*)hdr.sectionhdr.Name, ".rsrc") != 0)
-        goto quit;
-
-    rsrc_section = new_section(s1, ".rsrc", SHT_PROGBITS, SHF_ALLOC);
-    ptr = section_ptr_add(rsrc_section, hdr.sectionhdr.SizeOfRawData);
-    offs = hdr.sectionhdr.PointerToRawData;
-    if (!read_mem(fd, offs, ptr, hdr.sectionhdr.SizeOfRawData))
-        goto quit;
-    offs = hdr.sectionhdr.PointerToRelocations;
-    for (i = 0; i < hdr.sectionhdr.NumberOfRelocations; ++i)
-    {
-        struct pe_rsrc_reloc rel;
-        if (!read_mem(fd, offs, &rel, sizeof rel))
-            goto quit;
-        // printf("rsrc_reloc: %x %x %x\n", rel.offset, rel.size, rel.type);
-        if (rel.type != RSRC_RELTYPE)
-            goto quit;
-        put_elf_reloc(symtab_section, rsrc_section,
-            rel.offset, R_XXX_RELATIVE, 0);
-        offs += sizeof rel;
-    }
-    ret = 0;
-quit:
-    return ret;
-}
-
-/* ------------------------------------------------------------- */
-
-static char *trimfront(char *p)
-{
-    while (*p && (unsigned char)*p <= ' ')
-	++p;
-    return p;
-}
-
-static char *trimback(char *a, char *e)
-{
-    while (e > a && (unsigned char)e[-1] <= ' ')
-	--e;
-    *e = 0;;
-    return a;
-}
-
-/* ------------------------------------------------------------- */
-static int pe_load_def(TCCState *s1, int fd)
-{
-    int state = 0, ret = -1, dllindex = 0, ord;
-    char line[400], dllname[80], *p, *x;
-    FILE *fp;
-
-    fp = fdopen(dup(fd), "rb");
-    while (fgets(line, sizeof line, fp))
-    {
-        p = trimfront(trimback(line, strchr(line, 0)));
-        if (0 == *p || ';' == *p)
-            continue;
-
-        switch (state) {
-        case 0:
-            if (0 != strnicmp(p, "LIBRARY", 7))
-                goto quit;
-            pstrcpy(dllname, sizeof dllname, trimfront(p+7));
-            ++state;
-            continue;
-
-        case 1:
-            if (0 != stricmp(p, "EXPORTS"))
-                goto quit;
-            ++state;
-            continue;
-
-        case 2:
-            dllindex = add_dllref(s1, dllname);
-            ++state;
-            /* fall through */
-        default:
-            /* get ordinal and will store in sym->st_value */
-            ord = 0;
-            x = strchr(p, ' ');
-            if (x) {
-                *x = 0, x = strrchr(x + 1, '@');
-                if (x) {
-                    char *d;
-                    ord = (int)strtol(x + 1, &d, 10);
-                    if (*d)
-                        ord = 0;
-                }
-            }
-            pe_putimport(s1, dllindex, p, ord);
-            continue;
-        }
-    }
-    ret = 0;
-quit:
-    fclose(fp);
-    return ret;
-}
-
-/* ------------------------------------------------------------- */
-static int pe_load_dll(TCCState *s1, const char *filename)
-{
-    char *p, *q;
-    int index, ret;
-
-    ret = tcc_get_dllexports(filename, &p);
-    if (ret) {
-        return -1;
-    } else if (p) {
-        index = add_dllref(s1, tcc_basename(filename));
-        for (q = p; *q; q += 1 + strlen(q))
-            pe_putimport(s1, index, q, 0);
-        tcc_free(p);
-    }
-    return 0;
-}
-
-/* ------------------------------------------------------------- */
-ST_FUNC int pe_load_file(struct TCCState *s1, const char *filename, int fd)
-{
-    int ret = -1;
-    char buf[10];
-    if (0 == strcmp(tcc_fileextension(filename), ".def"))
-        ret = pe_load_def(s1, fd);
-    else if (pe_load_res(s1, fd) == 0)
-        ret = 0;
-    else if (read_mem(fd, 0, buf, 4) && 0 == memcmp(buf, "MZ\220", 4))
-        ret = pe_load_dll(s1, filename);
-    return ret;
-}
-
-/* ------------------------------------------------------------- */
-#ifdef TCC_TARGET_X86_64
-static unsigned pe_add_uwwind_info(TCCState *s1)
-{
-    if (NULL == s1->uw_pdata) {
-        s1->uw_pdata = find_section(tcc_state, ".pdata");
-        s1->uw_pdata->sh_addralign = 4;
-        s1->uw_sym = put_elf_sym(symtab_section, 0, 0, 0, 0, text_section->sh_num, NULL);
-    }
-
-    if (0 == s1->uw_offs) {
-        /* As our functions all have the same stackframe, we use one entry for all */
-        static const unsigned char uw_info[] = {
-            0x01, // UBYTE: 3 Version , UBYTE: 5 Flags
-            0x04, // UBYTE Size of prolog
-            0x02, // UBYTE Count of unwind codes
-            0x05, // UBYTE: 4 Frame Register (rbp), UBYTE: 4 Frame Register offset (scaled)
-            // USHORT * n Unwind codes array
-            // 0x0b, 0x01, 0xff, 0xff, // stack size
-            0x04, 0x03, // set frame ptr (mov rsp -> rbp)
-            0x01, 0x50  // push reg (rbp)
-        };
-
-        Section *s = text_section;
-        unsigned char *p;
-
-        section_ptr_add(s, -s->data_offset & 3); /* align */
-        s1->uw_offs = s->data_offset;
-        p = section_ptr_add(s, sizeof uw_info);
-        memcpy(p, uw_info, sizeof uw_info);
-    }
-
-    return s1->uw_offs;
-}
-
-ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack)
-{
-    TCCState *s1 = tcc_state;
-    Section *pd;
-    unsigned o, n, d;
-    struct /* _RUNTIME_FUNCTION */ {
-      DWORD BeginAddress;
-      DWORD EndAddress;
-      DWORD UnwindData;
-    } *p;
-
-    d = pe_add_uwwind_info(s1);
-    pd = s1->uw_pdata;
-    o = pd->data_offset;
-    p = section_ptr_add(pd, sizeof *p);
-
-    /* record this function */
-    p->BeginAddress = start;
-    p->EndAddress = end;
-    p->UnwindData = d;
-
-    /* put relocations on it */
-    for (n = o + sizeof *p; o < n; o += sizeof p->BeginAddress)
-        put_elf_reloc(symtab_section, pd, o,  R_X86_64_RELATIVE, s1->uw_sym);
-}
-#endif
-/* ------------------------------------------------------------- */
-#ifdef TCC_TARGET_X86_64
-#define PE_STDSYM(n,s) n
-#else
-#define PE_STDSYM(n,s) "_" n s
-#endif
-
-static void pe_add_runtime(TCCState *s1, struct pe_info *pe)
-{
-    const char *start_symbol;
-    int pe_type = 0;
-    int unicode_entry = 0;
-
-    if (find_elf_sym(symtab_section, PE_STDSYM("WinMain","@16")))
-        pe_type = PE_GUI;
-    else
-    if (find_elf_sym(symtab_section, PE_STDSYM("wWinMain","@16"))) {
-        pe_type = PE_GUI;
-        unicode_entry = PE_GUI;
-    }
-    else
-    if (TCC_OUTPUT_DLL == s1->output_type) {
-        pe_type = PE_DLL;
-        /* need this for 'tccelf.c:relocate_section()' */
-        s1->output_type = TCC_OUTPUT_EXE;
-    }
-    else {
-        pe_type = PE_EXE;
-        if (find_elf_sym(symtab_section, "wmain"))
-            unicode_entry = PE_EXE;
-    }
-
-    start_symbol =
-        TCC_OUTPUT_MEMORY == s1->output_type
-        ? PE_GUI == pe_type ? (unicode_entry ? "__runwwinmain" : "__runwinmain")
-            : (unicode_entry ? "__runwmain" : "__runmain")
-        : PE_DLL == pe_type ? PE_STDSYM("__dllstart","@12")
-            : PE_GUI == pe_type ? (unicode_entry ? "__wwinstart": "__winstart")
-                : (unicode_entry ? "__wstart" : "__start")
-        ;
-
-    if (!s1->leading_underscore || strchr(start_symbol, '@'))
-        ++start_symbol;
-
-    /* grab the startup code from libtcc1 */
-#ifdef TCC_IS_NATIVE
-    if (TCC_OUTPUT_MEMORY != s1->output_type || s1->runtime_main)
-#endif
-    set_elf_sym(symtab_section,
-        0, 0,
-        ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
-        SHN_UNDEF, start_symbol);
-
-    tcc_add_pragma_libs(s1);
-
-    if (0 == s1->nostdlib) {
-        static const char *libs[] = {
-            TCC_LIBTCC1, "msvcrt", "kernel32", "", "user32", "gdi32", NULL
-        };
-        const char **pp, *p;
-        for (pp = libs; 0 != (p = *pp); ++pp) {
-            if (0 == *p) {
-                if (PE_DLL != pe_type && PE_GUI != pe_type)
-                    break;
-            } else if (pp == libs && tcc_add_dll(s1, p, 0) >= 0) {
-                continue;
-            } else {
-                tcc_add_library_err(s1, p);
-            }
-        }
-    }
-
-    if (TCC_OUTPUT_MEMORY == s1->output_type)
-        pe_type = PE_RUN;
-    pe->type = pe_type;
-    pe->start_symbol = start_symbol;
-}
-
-static void pe_set_options(TCCState * s1, struct pe_info *pe)
-{
-    if (PE_DLL == pe->type) {
-        /* XXX: check if is correct for arm-pe target */
-        pe->imagebase = 0x10000000;
-    } else {
-#if defined(TCC_TARGET_ARM)
-        pe->imagebase = 0x00010000;
-#else
-        pe->imagebase = 0x00400000;
-#endif
-    }
-
-#if defined(TCC_TARGET_ARM)
-    /* we use "console" subsystem by default */
-    pe->subsystem = 9;
-#else
-    if (PE_DLL == pe->type || PE_GUI == pe->type)
-        pe->subsystem = 2;
-    else
-        pe->subsystem = 3;
-#endif
-    /* Allow override via -Wl,-subsystem=... option */
-    if (s1->pe_subsystem != 0)
-        pe->subsystem = s1->pe_subsystem;
-
-    /* set default file/section alignment */
-    if (pe->subsystem == 1) {
-        pe->section_align = 0x20;
-        pe->file_align = 0x20;
-    } else {
-        pe->section_align = 0x1000;
-        pe->file_align = 0x200;
-    }
-
-    if (s1->section_align != 0)
-        pe->section_align = s1->section_align;
-    if (s1->pe_file_align != 0)
-        pe->file_align = s1->pe_file_align;
-
-    if ((pe->subsystem >= 10) && (pe->subsystem <= 12))
-        pe->imagebase = 0;
-
-    if (s1->has_text_addr)
-        pe->imagebase = s1->text_addr;
-}
-
-ST_FUNC int pe_output_file(TCCState *s1, const char *filename)
-{
-    int ret;
-    struct pe_info pe;
-    int i;
-
-    memset(&pe, 0, sizeof pe);
-    pe.filename = filename;
-    pe.s1 = s1;
-
-    tcc_add_bcheck(s1);
-    pe_add_runtime(s1, &pe);
-    relocate_common_syms(); /* assign bss addresses */
-    tcc_add_linker_symbols(s1);
-    pe_set_options(s1, &pe);
-
-    ret = pe_check_symbols(&pe);
-    if (ret)
-        ;
-    else if (filename) {
-        pe_assign_addresses(&pe);
-        relocate_syms(s1, s1->symtab, 0);
-        for (i = 1; i < s1->nb_sections; ++i) {
-            Section *s = s1->sections[i];
-            if (s->reloc) {
-                relocate_section(s1, s);
-                pe_relocate_rva(&pe, s);
-            }
-        }
-        pe.start_addr = (DWORD)
-            ((uintptr_t)tcc_get_symbol_err(s1, pe.start_symbol)
-                - pe.imagebase);
-        if (s1->nb_errors)
-            ret = -1;
-        else
-            ret = pe_write(&pe);
-        tcc_free(pe.sec_info);
-    } else {
-#ifdef TCC_IS_NATIVE
-        pe.thunk = data_section;
-        pe_build_imports(&pe);
-        s1->runtime_main = pe.start_symbol;
-#endif
-    }
-
-    pe_free_imports(&pe);
-
-#ifdef PE_PRINT_SECTIONS
-    pe_print_sections(s1, "tcc.log");
-#endif
-    return ret;
-}
-
-/* ------------------------------------------------------------- */
diff --git a/tinyc/tccpp.c b/tinyc/tccpp.c
deleted file mode 100644
index 76f9e428e..000000000
--- a/tinyc/tccpp.c
+++ /dev/null
@@ -1,3903 +0,0 @@
-/*
- *  TCC - Tiny C Compiler
- * 
- *  Copyright (c) 2001-2004 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "tcc.h"
-
-/********************************************************/
-/* global variables */
-
-ST_DATA int tok_flags;
-ST_DATA int parse_flags;
-
-ST_DATA struct BufferedFile *file;
-ST_DATA int ch, tok;
-ST_DATA CValue tokc;
-ST_DATA const int *macro_ptr;
-ST_DATA CString tokcstr; /* current parsed string, if any */
-
-/* display benchmark infos */
-ST_DATA int total_lines;
-ST_DATA int total_bytes;
-ST_DATA int tok_ident;
-ST_DATA TokenSym **table_ident;
-
-/* ------------------------------------------------------------------------- */
-
-static TokenSym *hash_ident[TOK_HASH_SIZE];
-static char token_buf[STRING_MAX_SIZE + 1];
-static CString cstr_buf;
-static CString macro_equal_buf;
-static TokenString tokstr_buf;
-static unsigned char isidnum_table[256 - CH_EOF];
-static int pp_debug_tok, pp_debug_symv;
-static int pp_once;
-static int pp_expr;
-static int pp_counter;
-static void tok_print(const char *msg, const int *str);
-
-static struct TinyAlloc *toksym_alloc;
-static struct TinyAlloc *tokstr_alloc;
-static struct TinyAlloc *cstr_alloc;
-
-static TokenString *macro_stack;
-
-static const char tcc_keywords[] = 
-#define DEF(id, str) str "\0"
-#include "tcctok.h"
-#undef DEF
-;
-
-/* WARNING: the content of this string encodes token numbers */
-static const unsigned char tok_two_chars[] =
-/* outdated -- gr
-    "<=\236>=\235!=\225&&\240||\241++\244--\242==\224<<\1>>\2+=\253"
-    "-=\255*=\252/=\257%=\245&=\246^=\336|=\374->\313..\250##\266";
-*/{
-    '<','=', TOK_LE,
-    '>','=', TOK_GE,
-    '!','=', TOK_NE,
-    '&','&', TOK_LAND,
-    '|','|', TOK_LOR,
-    '+','+', TOK_INC,
-    '-','-', TOK_DEC,
-    '=','=', TOK_EQ,
-    '<','<', TOK_SHL,
-    '>','>', TOK_SAR,
-    '+','=', TOK_A_ADD,
-    '-','=', TOK_A_SUB,
-    '*','=', TOK_A_MUL,
-    '/','=', TOK_A_DIV,
-    '%','=', TOK_A_MOD,
-    '&','=', TOK_A_AND,
-    '^','=', TOK_A_XOR,
-    '|','=', TOK_A_OR,
-    '-','>', TOK_ARROW,
-    '.','.', TOK_TWODOTS,
-    '#','#', TOK_TWOSHARPS,
-    0
-};
-
-static void next_nomacro_spc(void);
-
-ST_FUNC void skip(int c)
-{
-    if (tok != c)
-        tcc_error("'%c' expected (got \"%s\")", c, get_tok_str(tok, &tokc));
-    next();
-}
-
-ST_FUNC void expect(const char *msg)
-{
-    tcc_error("%s expected", msg);
-}
-
-/* ------------------------------------------------------------------------- */
-/* Custom allocator for tiny objects */
-
-#define USE_TAL
-
-#ifndef USE_TAL
-#define tal_free(al, p) tcc_free(p)
-#define tal_realloc(al, p, size) tcc_realloc(p, size)
-#define tal_new(a,b,c)
-#define tal_delete(a)
-#else
-#if !defined(MEM_DEBUG)
-#define tal_free(al, p) tal_free_impl(al, p)
-#define tal_realloc(al, p, size) tal_realloc_impl(&al, p, size)
-#define TAL_DEBUG_PARAMS
-#else
-#define TAL_DEBUG 1
-//#define TAL_INFO 1 /* collect and dump allocators stats */
-#define tal_free(al, p) tal_free_impl(al, p, __FILE__, __LINE__)
-#define tal_realloc(al, p, size) tal_realloc_impl(&al, p, size, __FILE__, __LINE__)
-#define TAL_DEBUG_PARAMS , const char *file, int line
-#define TAL_DEBUG_FILE_LEN 40
-#endif
-
-#define TOKSYM_TAL_SIZE     (768 * 1024) /* allocator for tiny TokenSym in table_ident */
-#define TOKSTR_TAL_SIZE     (768 * 1024) /* allocator for tiny TokenString instances */
-#define CSTR_TAL_SIZE       (256 * 1024) /* allocator for tiny CString instances */
-#define TOKSYM_TAL_LIMIT    256 /* prefer unique limits to distinguish allocators debug msgs */
-#define TOKSTR_TAL_LIMIT    128 /* 32 * sizeof(int) */
-#define CSTR_TAL_LIMIT      1024
-
-typedef struct TinyAlloc {
-    unsigned  limit;
-    unsigned  size;
-    uint8_t *buffer;
-    uint8_t *p;
-    unsigned  nb_allocs;
-    struct TinyAlloc *next, *top;
-#ifdef TAL_INFO
-    unsigned  nb_peak;
-    unsigned  nb_total;
-    unsigned  nb_missed;
-    uint8_t *peak_p;
-#endif
-} TinyAlloc;
-
-typedef struct tal_header_t {
-    unsigned  size;
-#ifdef TAL_DEBUG
-    int     line_num; /* negative line_num used for double free check */
-    char    file_name[TAL_DEBUG_FILE_LEN + 1];
-#endif
-} tal_header_t;
-
-/* ------------------------------------------------------------------------- */
-
-static TinyAlloc *tal_new(TinyAlloc **pal, unsigned limit, unsigned size)
-{
-    TinyAlloc *al = tcc_mallocz(sizeof(TinyAlloc));
-    al->p = al->buffer = tcc_malloc(size);
-    al->limit = limit;
-    al->size = size;
-    if (pal) *pal = al;
-    return al;
-}
-
-static void tal_delete(TinyAlloc *al)
-{
-    TinyAlloc *next;
-
-tail_call:
-    if (!al)
-        return;
-#ifdef TAL_INFO
-    fprintf(stderr, "limit=%5d, size=%5g MB, nb_peak=%6d, nb_total=%8d, nb_missed=%6d, usage=%5.1f%%\n",
-            al->limit, al->size / 1024.0 / 1024.0, al->nb_peak, al->nb_total, al->nb_missed,
-            (al->peak_p - al->buffer) * 100.0 / al->size);
-#endif
-#ifdef TAL_DEBUG
-    if (al->nb_allocs > 0) {
-        uint8_t *p;
-        fprintf(stderr, "TAL_DEBUG: memory leak %d chunk(s) (limit= %d)\n",
-                al->nb_allocs, al->limit);
-        p = al->buffer;
-        while (p < al->p) {
-            tal_header_t *header = (tal_header_t *)p;
-            if (header->line_num > 0) {
-                fprintf(stderr, "%s:%d: chunk of %d bytes leaked\n",
-                        header->file_name, header->line_num, header->size);
-            }
-            p += header->size + sizeof(tal_header_t);
-        }
-#if MEM_DEBUG-0 == 2
-        exit(2);
-#endif
-    }
-#endif
-    next = al->next;
-    tcc_free(al->buffer);
-    tcc_free(al);
-    al = next;
-    goto tail_call;
-}
-
-static void tal_free_impl(TinyAlloc *al, void *p TAL_DEBUG_PARAMS)
-{
-    if (!p)
-        return;
-tail_call:
-    if (al->buffer <= (uint8_t *)p && (uint8_t *)p < al->buffer + al->size) {
-#ifdef TAL_DEBUG
-        tal_header_t *header = (((tal_header_t *)p) - 1);
-        if (header->line_num < 0) {
-            fprintf(stderr, "%s:%d: TAL_DEBUG: double frees chunk from\n",
-                    file, line);
-            fprintf(stderr, "%s:%d: %d bytes\n",
-                    header->file_name, (int)-header->line_num, (int)header->size);
-        } else
-            header->line_num = -header->line_num;
-#endif
-        al->nb_allocs--;
-        if (!al->nb_allocs)
-            al->p = al->buffer;
-    } else if (al->next) {
-        al = al->next;
-        goto tail_call;
-    }
-    else
-        tcc_free(p);
-}
-
-static void *tal_realloc_impl(TinyAlloc **pal, void *p, unsigned size TAL_DEBUG_PARAMS)
-{
-    tal_header_t *header;
-    void *ret;
-    int is_own;
-    unsigned adj_size = (size + 3) & -4;
-    TinyAlloc *al = *pal;
-
-tail_call:
-    is_own = (al->buffer <= (uint8_t *)p && (uint8_t *)p < al->buffer + al->size);
-    if ((!p || is_own) && size <= al->limit) {
-        if (al->p + adj_size + sizeof(tal_header_t) < al->buffer + al->size) {
-            header = (tal_header_t *)al->p;
-            header->size = adj_size;
-#ifdef TAL_DEBUG
-            { int ofs = strlen(file) - TAL_DEBUG_FILE_LEN;
-            strncpy(header->file_name, file + (ofs > 0 ? ofs : 0), TAL_DEBUG_FILE_LEN);
-            header->file_name[TAL_DEBUG_FILE_LEN] = 0;
-            header->line_num = line; }
-#endif
-            ret = al->p + sizeof(tal_header_t);
-            al->p += adj_size + sizeof(tal_header_t);
-            if (is_own) {
-                header = (((tal_header_t *)p) - 1);
-                memcpy(ret, p, header->size);
-#ifdef TAL_DEBUG
-                header->line_num = -header->line_num;
-#endif
-            } else {
-                al->nb_allocs++;
-            }
-#ifdef TAL_INFO
-            if (al->nb_peak < al->nb_allocs)
-                al->nb_peak = al->nb_allocs;
-            if (al->peak_p < al->p)
-                al->peak_p = al->p;
-            al->nb_total++;
-#endif
-            return ret;
-        } else if (is_own) {
-            al->nb_allocs--;
-            ret = tal_realloc(*pal, 0, size);
-            header = (((tal_header_t *)p) - 1);
-            memcpy(ret, p, header->size);
-#ifdef TAL_DEBUG
-            header->line_num = -header->line_num;
-#endif
-            return ret;
-        }
-        if (al->next) {
-            al = al->next;
-        } else {
-            TinyAlloc *bottom = al, *next = al->top ? al->top : al;
-
-            al = tal_new(pal, next->limit, next->size * 2);
-            al->next = next;
-            bottom->top = al;
-        }
-        goto tail_call;
-    }
-    if (is_own) {
-        al->nb_allocs--;
-        ret = tcc_malloc(size);
-        header = (((tal_header_t *)p) - 1);
-        memcpy(ret, p, header->size);
-#ifdef TAL_DEBUG
-        header->line_num = -header->line_num;
-#endif
-    } else if (al->next) {
-        al = al->next;
-        goto tail_call;
-    } else
-        ret = tcc_realloc(p, size);
-#ifdef TAL_INFO
-    al->nb_missed++;
-#endif
-    return ret;
-}
-
-#endif /* USE_TAL */
-
-/* ------------------------------------------------------------------------- */
-/* CString handling */
-static void cstr_realloc(CString *cstr, int new_size)
-{
-    int size;
-
-    size = cstr->size_allocated;
-    if (size < 8)
-        size = 8; /* no need to allocate a too small first string */
-    while (size < new_size)
-        size = size * 2;
-    cstr->data = tal_realloc(cstr_alloc, cstr->data, size);
-    cstr->size_allocated = size;
-}
-
-/* add a byte */
-ST_INLN void cstr_ccat(CString *cstr, int ch)
-{
-    int size;
-    size = cstr->size + 1;
-    if (size > cstr->size_allocated)
-        cstr_realloc(cstr, size);
-    ((unsigned char *)cstr->data)[size - 1] = ch;
-    cstr->size = size;
-}
-
-ST_FUNC void cstr_cat(CString *cstr, const char *str, int len)
-{
-    int size;
-    if (len <= 0)
-        len = strlen(str) + 1 + len;
-    size = cstr->size + len;
-    if (size > cstr->size_allocated)
-        cstr_realloc(cstr, size);
-    memmove(((unsigned char *)cstr->data) + cstr->size, str, len);
-    cstr->size = size;
-}
-
-/* add a wide char */
-ST_FUNC void cstr_wccat(CString *cstr, int ch)
-{
-    int size;
-    size = cstr->size + sizeof(nwchar_t);
-    if (size > cstr->size_allocated)
-        cstr_realloc(cstr, size);
-    *(nwchar_t *)(((unsigned char *)cstr->data) + size - sizeof(nwchar_t)) = ch;
-    cstr->size = size;
-}
-
-ST_FUNC void cstr_new(CString *cstr)
-{
-    memset(cstr, 0, sizeof(CString));
-}
-
-/* free string and reset it to NULL */
-ST_FUNC void cstr_free(CString *cstr)
-{
-    tal_free(cstr_alloc, cstr->data);
-    cstr_new(cstr);
-}
-
-/* reset string to empty */
-ST_FUNC void cstr_reset(CString *cstr)
-{
-    cstr->size = 0;
-}
-
-/* XXX: unicode ? */
-static void add_char(CString *cstr, int c)
-{
-    if (c == '\'' || c == '\"' || c == '\\') {
-        /* XXX: could be more precise if char or string */
-        cstr_ccat(cstr, '\\');
-    }
-    if (c >= 32 && c <= 126) {
-        cstr_ccat(cstr, c);
-    } else {
-        cstr_ccat(cstr, '\\');
-        if (c == '\n') {
-            cstr_ccat(cstr, 'n');
-        } else {
-            cstr_ccat(cstr, '0' + ((c >> 6) & 7));
-            cstr_ccat(cstr, '0' + ((c >> 3) & 7));
-            cstr_ccat(cstr, '0' + (c & 7));
-        }
-    }
-}
-
-/* ------------------------------------------------------------------------- */
-/* allocate a new token */
-static TokenSym *tok_alloc_new(TokenSym **pts, const char *str, int len)
-{
-    TokenSym *ts, **ptable;
-    int i;
-
-    if (tok_ident >= SYM_FIRST_ANOM) 
-        tcc_error("memory full (symbols)");
-
-    /* expand token table if needed */
-    i = tok_ident - TOK_IDENT;
-    if ((i % TOK_ALLOC_INCR) == 0) {
-        ptable = tcc_realloc(table_ident, (i + TOK_ALLOC_INCR) * sizeof(TokenSym *));
-        table_ident = ptable;
-    }
-
-    ts = tal_realloc(toksym_alloc, 0, sizeof(TokenSym) + len);
-    table_ident[i] = ts;
-    ts->tok = tok_ident++;
-    ts->sym_define = NULL;
-    ts->sym_label = NULL;
-    ts->sym_struct = NULL;
-    ts->sym_identifier = NULL;
-    ts->len = len;
-    ts->hash_next = NULL;
-    memcpy(ts->str, str, len);
-    ts->str[len] = '\0';
-    *pts = ts;
-    return ts;
-}
-
-#define TOK_HASH_INIT 1
-#define TOK_HASH_FUNC(h, c) ((h) + ((h) << 5) + ((h) >> 27) + (c))
-
-
-/* find a token and add it if not found */
-ST_FUNC TokenSym *tok_alloc(const char *str, int len)
-{
-    TokenSym *ts, **pts;
-    int i;
-    unsigned int h;
-    
-    h = TOK_HASH_INIT;
-    for(i=0;i<len;i++)
-        h = TOK_HASH_FUNC(h, ((unsigned char *)str)[i]);
-    h &= (TOK_HASH_SIZE - 1);
-
-    pts = &hash_ident[h];
-    for(;;) {
-        ts = *pts;
-        if (!ts)
-            break;
-        if (ts->len == len && !memcmp(ts->str, str, len))
-            return ts;
-        pts = &(ts->hash_next);
-    }
-    return tok_alloc_new(pts, str, len);
-}
-
-/* XXX: buffer overflow */
-/* XXX: float tokens */
-ST_FUNC const char *get_tok_str(int v, CValue *cv)
-{
-    char *p;
-    int i, len;
-
-    cstr_reset(&cstr_buf);
-    p = cstr_buf.data;
-
-    switch(v) {
-    case TOK_CINT:
-    case TOK_CUINT:
-    case TOK_CLONG:
-    case TOK_CULONG:
-    case TOK_CLLONG:
-    case TOK_CULLONG:
-        /* XXX: not quite exact, but only useful for testing  */
-#ifdef _WIN32
-        sprintf(p, "%u", (unsigned)cv->i);
-#else
-        sprintf(p, "%llu", (unsigned long long)cv->i);
-#endif
-        break;
-    case TOK_LCHAR:
-        cstr_ccat(&cstr_buf, 'L');
-    case TOK_CCHAR:
-        cstr_ccat(&cstr_buf, '\'');
-        add_char(&cstr_buf, cv->i);
-        cstr_ccat(&cstr_buf, '\'');
-        cstr_ccat(&cstr_buf, '\0');
-        break;
-    case TOK_PPNUM:
-    case TOK_PPSTR:
-        return (char*)cv->str.data;
-    case TOK_LSTR:
-        cstr_ccat(&cstr_buf, 'L');
-    case TOK_STR:
-        cstr_ccat(&cstr_buf, '\"');
-        if (v == TOK_STR) {
-            len = cv->str.size - 1;
-            for(i=0;i<len;i++)
-                add_char(&cstr_buf, ((unsigned char *)cv->str.data)[i]);
-        } else {
-            len = (cv->str.size / sizeof(nwchar_t)) - 1;
-            for(i=0;i<len;i++)
-                add_char(&cstr_buf, ((nwchar_t *)cv->str.data)[i]);
-        }
-        cstr_ccat(&cstr_buf, '\"');
-        cstr_ccat(&cstr_buf, '\0');
-        break;
-
-    case TOK_CFLOAT:
-        cstr_cat(&cstr_buf, "<float>", 0);
-        break;
-    case TOK_CDOUBLE:
-	cstr_cat(&cstr_buf, "<double>", 0);
-	break;
-    case TOK_CLDOUBLE:
-	cstr_cat(&cstr_buf, "<long double>", 0);
-	break;
-    case TOK_LINENUM:
-	cstr_cat(&cstr_buf, "<linenumber>", 0);
-	break;
-
-    /* above tokens have value, the ones below don't */
-    case TOK_LT:
-        v = '<';
-        goto addv;
-    case TOK_GT:
-        v = '>';
-        goto addv;
-    case TOK_DOTS:
-        return strcpy(p, "...");
-    case TOK_A_SHL:
-        return strcpy(p, "<<=");
-    case TOK_A_SAR:
-        return strcpy(p, ">>=");
-    case TOK_EOF:
-        return strcpy(p, "<eof>");
-    default:
-        if (v < TOK_IDENT) {
-            /* search in two bytes table */
-            const unsigned char *q = tok_two_chars;
-            while (*q) {
-                if (q[2] == v) {
-                    *p++ = q[0];
-                    *p++ = q[1];
-                    *p = '\0';
-                    return cstr_buf.data;
-                }
-                q += 3;
-            }
-        if (v >= 127) {
-            sprintf(cstr_buf.data, "<%02x>", v);
-            return cstr_buf.data;
-        }
-        addv:
-            *p++ = v;
-            *p = '\0';
-        } else if (v < tok_ident) {
-            return table_ident[v - TOK_IDENT]->str;
-        } else if (v >= SYM_FIRST_ANOM) {
-            /* special name for anonymous symbol */
-            sprintf(p, "L.%u", v - SYM_FIRST_ANOM);
-        } else {
-            /* should never happen */
-            return NULL;
-        }
-        break;
-    }
-    return cstr_buf.data;
-}
-
-/* return the current character, handling end of block if necessary
-   (but not stray) */
-ST_FUNC int handle_eob(void)
-{
-    BufferedFile *bf = file;
-    int len;
-
-    /* only tries to read if really end of buffer */
-    if (bf->buf_ptr >= bf->buf_end) {
-        if (bf->fd >= 0) {
-#if defined(PARSE_DEBUG)
-            len = 1;
-#else
-            len = IO_BUF_SIZE;
-#endif
-            len = read(bf->fd, bf->buffer, len);
-            if (len < 0)
-                len = 0;
-        } else {
-            len = 0;
-        }
-        total_bytes += len;
-        bf->buf_ptr = bf->buffer;
-        bf->buf_end = bf->buffer + len;
-        *bf->buf_end = CH_EOB;
-    }
-    if (bf->buf_ptr < bf->buf_end) {
-        return bf->buf_ptr[0];
-    } else {
-        bf->buf_ptr = bf->buf_end;
-        return CH_EOF;
-    }
-}
-
-/* read next char from current input file and handle end of input buffer */
-ST_INLN void inp(void)
-{
-    ch = *(++(file->buf_ptr));
-    /* end of buffer/file handling */
-    if (ch == CH_EOB)
-        ch = handle_eob();
-}
-
-/* handle '\[\r]\n' */
-static int handle_stray_noerror(void)
-{
-    while (ch == '\\') {
-        inp();
-        if (ch == '\n') {
-            file->line_num++;
-            inp();
-        } else if (ch == '\r') {
-            inp();
-            if (ch != '\n')
-                goto fail;
-            file->line_num++;
-            inp();
-        } else {
-        fail:
-            return 1;
-        }
-    }
-    return 0;
-}
-
-static void handle_stray(void)
-{
-    if (handle_stray_noerror())
-        tcc_error("stray '\\' in program");
-}
-
-/* skip the stray and handle the \\n case. Output an error if
-   incorrect char after the stray */
-static int handle_stray1(uint8_t *p)
-{
-    int c;
-
-    file->buf_ptr = p;
-    if (p >= file->buf_end) {
-        c = handle_eob();
-        if (c != '\\')
-            return c;
-        p = file->buf_ptr;
-    }
-    ch = *p;
-    if (handle_stray_noerror()) {
-        if (!(parse_flags & PARSE_FLAG_ACCEPT_STRAYS))
-            tcc_error("stray '\\' in program");
-        *--file->buf_ptr = '\\';
-    }
-    p = file->buf_ptr;
-    c = *p;
-    return c;
-}
-
-/* handle just the EOB case, but not stray */
-#define PEEKC_EOB(c, p)\
-{\
-    p++;\
-    c = *p;\
-    if (c == '\\') {\
-        file->buf_ptr = p;\
-        c = handle_eob();\
-        p = file->buf_ptr;\
-    }\
-}
-
-/* handle the complicated stray case */
-#define PEEKC(c, p)\
-{\
-    p++;\
-    c = *p;\
-    if (c == '\\') {\
-        c = handle_stray1(p);\
-        p = file->buf_ptr;\
-    }\
-}
-
-/* input with '\[\r]\n' handling. Note that this function cannot
-   handle other characters after '\', so you cannot call it inside
-   strings or comments */
-ST_FUNC void minp(void)
-{
-    inp();
-    if (ch == '\\') 
-        handle_stray();
-}
-
-/* single line C++ comments */
-static uint8_t *parse_line_comment(uint8_t *p)
-{
-    int c;
-
-    p++;
-    for(;;) {
-        c = *p;
-    redo:
-        if (c == '\n' || c == CH_EOF) {
-            break;
-        } else if (c == '\\') {
-            file->buf_ptr = p;
-            c = handle_eob();
-            p = file->buf_ptr;
-            if (c == '\\') {
-                PEEKC_EOB(c, p);
-                if (c == '\n') {
-                    file->line_num++;
-                    PEEKC_EOB(c, p);
-                } else if (c == '\r') {
-                    PEEKC_EOB(c, p);
-                    if (c == '\n') {
-                        file->line_num++;
-                        PEEKC_EOB(c, p);
-                    }
-                }
-            } else {
-                goto redo;
-            }
-        } else {
-            p++;
-        }
-    }
-    return p;
-}
-
-/* C comments */
-ST_FUNC uint8_t *parse_comment(uint8_t *p)
-{
-    int c;
-
-    p++;
-    for(;;) {
-        /* fast skip loop */
-        for(;;) {
-            c = *p;
-            if (c == '\n' || c == '*' || c == '\\')
-                break;
-            p++;
-            c = *p;
-            if (c == '\n' || c == '*' || c == '\\')
-                break;
-            p++;
-        }
-        /* now we can handle all the cases */
-        if (c == '\n') {
-            file->line_num++;
-            p++;
-        } else if (c == '*') {
-            p++;
-            for(;;) {
-                c = *p;
-                if (c == '*') {
-                    p++;
-                } else if (c == '/') {
-                    goto end_of_comment;
-                } else if (c == '\\') {
-                    file->buf_ptr = p;
-                    c = handle_eob();
-                    p = file->buf_ptr;
-                    if (c == CH_EOF)
-                        tcc_error("unexpected end of file in comment");
-                    if (c == '\\') {
-                        /* skip '\[\r]\n', otherwise just skip the stray */
-                        while (c == '\\') {
-                            PEEKC_EOB(c, p);
-                            if (c == '\n') {
-                                file->line_num++;
-                                PEEKC_EOB(c, p);
-                            } else if (c == '\r') {
-                                PEEKC_EOB(c, p);
-                                if (c == '\n') {
-                                    file->line_num++;
-                                    PEEKC_EOB(c, p);
-                                }
-                            } else {
-                                goto after_star;
-                            }
-                        }
-                    }
-                } else {
-                    break;
-                }
-            }
-        after_star: ;
-        } else {
-            /* stray, eob or eof */
-            file->buf_ptr = p;
-            c = handle_eob();
-            p = file->buf_ptr;
-            if (c == CH_EOF) {
-                tcc_error("unexpected end of file in comment");
-            } else if (c == '\\') {
-                p++;
-            }
-        }
-    }
- end_of_comment:
-    p++;
-    return p;
-}
-
-ST_FUNC int set_idnum(int c, int val)
-{
-    int prev = isidnum_table[c - CH_EOF];
-    isidnum_table[c - CH_EOF] = val;
-    return prev;
-}
-
-#define cinp minp
-
-static inline void skip_spaces(void)
-{
-    while (isidnum_table[ch - CH_EOF] & IS_SPC)
-        cinp();
-}
-
-static inline int check_space(int t, int *spc) 
-{
-    if (t < 256 && (isidnum_table[t - CH_EOF] & IS_SPC)) {
-        if (*spc) 
-            return 1;
-        *spc = 1;
-    } else 
-        *spc = 0;
-    return 0;
-}
-
-/* parse a string without interpreting escapes */
-static uint8_t *parse_pp_string(uint8_t *p,
-                                int sep, CString *str)
-{
-    int c;
-    p++;
-    for(;;) {
-        c = *p;
-        if (c == sep) {
-            break;
-        } else if (c == '\\') {
-            file->buf_ptr = p;
-            c = handle_eob();
-            p = file->buf_ptr;
-            if (c == CH_EOF) {
-            unterminated_string:
-                /* XXX: indicate line number of start of string */
-                tcc_error("missing terminating %c character", sep);
-            } else if (c == '\\') {
-                /* escape : just skip \[\r]\n */
-                PEEKC_EOB(c, p);
-                if (c == '\n') {
-                    file->line_num++;
-                    p++;
-                } else if (c == '\r') {
-                    PEEKC_EOB(c, p);
-                    if (c != '\n')
-                        expect("'\n' after '\r'");
-                    file->line_num++;
-                    p++;
-                } else if (c == CH_EOF) {
-                    goto unterminated_string;
-                } else {
-                    if (str) {
-                        cstr_ccat(str, '\\');
-                        cstr_ccat(str, c);
-                    }
-                    p++;
-                }
-            }
-        } else if (c == '\n') {
-            file->line_num++;
-            goto add_char;
-        } else if (c == '\r') {
-            PEEKC_EOB(c, p);
-            if (c != '\n') {
-                if (str)
-                    cstr_ccat(str, '\r');
-            } else {
-                file->line_num++;
-                goto add_char;
-            }
-        } else {
-        add_char:
-            if (str)
-                cstr_ccat(str, c);
-            p++;
-        }
-    }
-    p++;
-    return p;
-}
-
-/* skip block of text until #else, #elif or #endif. skip also pairs of
-   #if/#endif */
-static void preprocess_skip(void)
-{
-    int a, start_of_line, c, in_warn_or_error;
-    uint8_t *p;
-
-    p = file->buf_ptr;
-    a = 0;
-redo_start:
-    start_of_line = 1;
-    in_warn_or_error = 0;
-    for(;;) {
-    redo_no_start:
-        c = *p;
-        switch(c) {
-        case ' ':
-        case '\t':
-        case '\f':
-        case '\v':
-        case '\r':
-            p++;
-            goto redo_no_start;
-        case '\n':
-            file->line_num++;
-            p++;
-            goto redo_start;
-        case '\\':
-            file->buf_ptr = p;
-            c = handle_eob();
-            if (c == CH_EOF) {
-                expect("#endif");
-            } else if (c == '\\') {
-                ch = file->buf_ptr[0];
-                handle_stray_noerror();
-            }
-            p = file->buf_ptr;
-            goto redo_no_start;
-        /* skip strings */
-        case '\"':
-        case '\'':
-            if (in_warn_or_error)
-                goto _default;
-            p = parse_pp_string(p, c, NULL);
-            break;
-        /* skip comments */
-        case '/':
-            if (in_warn_or_error)
-                goto _default;
-            file->buf_ptr = p;
-            ch = *p;
-            minp();
-            p = file->buf_ptr;
-            if (ch == '*') {
-                p = parse_comment(p);
-            } else if (ch == '/') {
-                p = parse_line_comment(p);
-            }
-            break;
-        case '#':
-            p++;
-            if (start_of_line) {
-                file->buf_ptr = p;
-                next_nomacro();
-                p = file->buf_ptr;
-                if (a == 0 && 
-                    (tok == TOK_ELSE || tok == TOK_ELIF || tok == TOK_ENDIF))
-                    goto the_end;
-                if (tok == TOK_IF || tok == TOK_IFDEF || tok == TOK_IFNDEF)
-                    a++;
-                else if (tok == TOK_ENDIF)
-                    a--;
-                else if( tok == TOK_ERROR || tok == TOK_WARNING)
-                    in_warn_or_error = 1;
-                else if (tok == TOK_LINEFEED)
-                    goto redo_start;
-                else if (parse_flags & PARSE_FLAG_ASM_FILE)
-                    p = parse_line_comment(p - 1);
-            } else if (parse_flags & PARSE_FLAG_ASM_FILE)
-                p = parse_line_comment(p - 1);
-            break;
-_default:
-        default:
-            p++;
-            break;
-        }
-        start_of_line = 0;
-    }
- the_end: ;
-    file->buf_ptr = p;
-}
-
-#if 0
-/* return the number of additional 'ints' necessary to store the
-   token */
-static inline int tok_size(const int *p)
-{
-    switch(*p) {
-        /* 4 bytes */
-    case TOK_CINT:
-    case TOK_CUINT:
-    case TOK_CCHAR:
-    case TOK_LCHAR:
-    case TOK_CFLOAT:
-    case TOK_LINENUM:
-        return 1 + 1;
-    case TOK_STR:
-    case TOK_LSTR:
-    case TOK_PPNUM:
-    case TOK_PPSTR:
-        return 1 + ((sizeof(CString) + ((CString *)(p+1))->size + 3) >> 2);
-    case TOK_CLONG:
-    case TOK_CULONG:
-	return 1 + LONG_SIZE / 4;
-    case TOK_CDOUBLE:
-    case TOK_CLLONG:
-    case TOK_CULLONG:
-        return 1 + 2;
-    case TOK_CLDOUBLE:
-        return 1 + LDOUBLE_SIZE / 4;
-    default:
-        return 1 + 0;
-    }
-}
-#endif
-
-/* token string handling */
-ST_INLN void tok_str_new(TokenString *s)
-{
-    s->str = NULL;
-    s->len = s->lastlen = 0;
-    s->allocated_len = 0;
-    s->last_line_num = -1;
-}
-
-ST_FUNC TokenString *tok_str_alloc(void)
-{
-    TokenString *str = tal_realloc(tokstr_alloc, 0, sizeof *str);
-    tok_str_new(str);
-    return str;
-}
-
-ST_FUNC int *tok_str_dup(TokenString *s)
-{
-    int *str;
-
-    str = tal_realloc(tokstr_alloc, 0, s->len * sizeof(int));
-    memcpy(str, s->str, s->len * sizeof(int));
-    return str;
-}
-
-ST_FUNC void tok_str_free_str(int *str)
-{
-    tal_free(tokstr_alloc, str);
-}
-
-ST_FUNC void tok_str_free(TokenString *str)
-{
-    tok_str_free_str(str->str);
-    tal_free(tokstr_alloc, str);
-}
-
-ST_FUNC int *tok_str_realloc(TokenString *s, int new_size)
-{
-    int *str, size;
-
-    size = s->allocated_len;
-    if (size < 16)
-        size = 16;
-    while (size < new_size)
-        size = size * 2;
-    if (size > s->allocated_len) {
-        str = tal_realloc(tokstr_alloc, s->str, size * sizeof(int));
-        s->allocated_len = size;
-        s->str = str;
-    }
-    return s->str;
-}
-
-ST_FUNC void tok_str_add(TokenString *s, int t)
-{
-    int len, *str;
-
-    len = s->len;
-    str = s->str;
-    if (len >= s->allocated_len)
-        str = tok_str_realloc(s, len + 1);
-    str[len++] = t;
-    s->len = len;
-}
-
-ST_FUNC void begin_macro(TokenString *str, int alloc)
-{
-    str->alloc = alloc;
-    str->prev = macro_stack;
-    str->prev_ptr = macro_ptr;
-    str->save_line_num = file->line_num;
-    macro_ptr = str->str;
-    macro_stack = str;
-}
-
-ST_FUNC void end_macro(void)
-{
-    TokenString *str = macro_stack;
-    macro_stack = str->prev;
-    macro_ptr = str->prev_ptr;
-    file->line_num = str->save_line_num;
-    if (str->alloc == 2) {
-        str->alloc = 3; /* just mark as finished */
-    } else {
-        tok_str_free(str);
-    }
-}
-
-static void tok_str_add2(TokenString *s, int t, CValue *cv)
-{
-    int len, *str;
-
-    len = s->lastlen = s->len;
-    str = s->str;
-
-    /* allocate space for worst case */
-    if (len + TOK_MAX_SIZE >= s->allocated_len)
-        str = tok_str_realloc(s, len + TOK_MAX_SIZE + 1);
-    str[len++] = t;
-    switch(t) {
-    case TOK_CINT:
-    case TOK_CUINT:
-    case TOK_CCHAR:
-    case TOK_LCHAR:
-    case TOK_CFLOAT:
-    case TOK_LINENUM:
-#if LONG_SIZE == 4
-    case TOK_CLONG:
-    case TOK_CULONG:
-#endif
-        str[len++] = cv->tab[0];
-        break;
-    case TOK_PPNUM:
-    case TOK_PPSTR:
-    case TOK_STR:
-    case TOK_LSTR:
-        {
-            /* Insert the string into the int array. */
-            size_t nb_words =
-                1 + (cv->str.size + sizeof(int) - 1) / sizeof(int);
-            if (len + nb_words >= s->allocated_len)
-                str = tok_str_realloc(s, len + nb_words + 1);
-            str[len] = cv->str.size;
-            memcpy(&str[len + 1], cv->str.data, cv->str.size);
-            len += nb_words;
-        }
-        break;
-    case TOK_CDOUBLE:
-    case TOK_CLLONG:
-    case TOK_CULLONG:
-#if LONG_SIZE == 8
-    case TOK_CLONG:
-    case TOK_CULONG:
-#endif
-#if LDOUBLE_SIZE == 8
-    case TOK_CLDOUBLE:
-#endif
-        str[len++] = cv->tab[0];
-        str[len++] = cv->tab[1];
-        break;
-#if LDOUBLE_SIZE == 12
-    case TOK_CLDOUBLE:
-        str[len++] = cv->tab[0];
-        str[len++] = cv->tab[1];
-        str[len++] = cv->tab[2];
-#elif LDOUBLE_SIZE == 16
-    case TOK_CLDOUBLE:
-        str[len++] = cv->tab[0];
-        str[len++] = cv->tab[1];
-        str[len++] = cv->tab[2];
-        str[len++] = cv->tab[3];
-#elif LDOUBLE_SIZE != 8
-#error add long double size support
-#endif
-        break;
-    default:
-        break;
-    }
-    s->len = len;
-}
-
-/* add the current parse token in token string 's' */
-ST_FUNC void tok_str_add_tok(TokenString *s)
-{
-    CValue cval;
-
-    /* save line number info */
-    if (file->line_num != s->last_line_num) {
-        s->last_line_num = file->line_num;
-        cval.i = s->last_line_num;
-        tok_str_add2(s, TOK_LINENUM, &cval);
-    }
-    tok_str_add2(s, tok, &tokc);
-}
-
-/* get a token from an integer array and increment pointer
-   accordingly. we code it as a macro to avoid pointer aliasing. */
-static inline void TOK_GET(int *t, const int **pp, CValue *cv)
-{
-    const int *p = *pp;
-    int n, *tab;
-
-    tab = cv->tab;
-    switch(*t = *p++) {
-#if LONG_SIZE == 4
-    case TOK_CLONG:
-#endif
-    case TOK_CINT:
-    case TOK_CCHAR:
-    case TOK_LCHAR:
-    case TOK_LINENUM:
-        cv->i = *p++;
-        break;
-#if LONG_SIZE == 4
-    case TOK_CULONG:
-#endif
-    case TOK_CUINT:
-        cv->i = (unsigned)*p++;
-        break;
-    case TOK_CFLOAT:
-	tab[0] = *p++;
-	break;
-    case TOK_STR:
-    case TOK_LSTR:
-    case TOK_PPNUM:
-    case TOK_PPSTR:
-        cv->str.size = *p++;
-        cv->str.data = p;
-        p += (cv->str.size + sizeof(int) - 1) / sizeof(int);
-        break;
-    case TOK_CDOUBLE:
-    case TOK_CLLONG:
-    case TOK_CULLONG:
-#if LONG_SIZE == 8
-    case TOK_CLONG:
-    case TOK_CULONG:
-#endif
-        n = 2;
-        goto copy;
-    case TOK_CLDOUBLE:
-#if LDOUBLE_SIZE == 16
-        n = 4;
-#elif LDOUBLE_SIZE == 12
-        n = 3;
-#elif LDOUBLE_SIZE == 8
-        n = 2;
-#else
-# error add long double size support
-#endif
-    copy:
-        do
-            *tab++ = *p++;
-        while (--n);
-        break;
-    default:
-        break;
-    }
-    *pp = p;
-}
-
-static int macro_is_equal(const int *a, const int *b)
-{
-    CValue cv;
-    int t;
-
-    if (!a || !b)
-        return 1;
-
-    while (*a && *b) {
-        /* first time preallocate macro_equal_buf, next time only reset position to start */
-        cstr_reset(&macro_equal_buf);
-        TOK_GET(&t, &a, &cv);
-        cstr_cat(&macro_equal_buf, get_tok_str(t, &cv), 0);
-        TOK_GET(&t, &b, &cv);
-        if (strcmp(macro_equal_buf.data, get_tok_str(t, &cv)))
-            return 0;
-    }
-    return !(*a || *b);
-}
-
-/* defines handling */
-ST_INLN void define_push(int v, int macro_type, int *str, Sym *first_arg)
-{
-    Sym *s, *o;
-
-    o = define_find(v);
-    s = sym_push2(&define_stack, v, macro_type, 0);
-    s->d = str;
-    s->next = first_arg;
-    table_ident[v - TOK_IDENT]->sym_define = s;
-
-    if (o && !macro_is_equal(o->d, s->d))
-	tcc_warning("%s redefined", get_tok_str(v, NULL));
-}
-
-/* undefined a define symbol. Its name is just set to zero */
-ST_FUNC void define_undef(Sym *s)
-{
-    int v = s->v;
-    if (v >= TOK_IDENT && v < tok_ident)
-        table_ident[v - TOK_IDENT]->sym_define = NULL;
-}
-
-ST_INLN Sym *define_find(int v)
-{
-    v -= TOK_IDENT;
-    if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
-        return NULL;
-    return table_ident[v]->sym_define;
-}
-
-/* free define stack until top reaches 'b' */
-ST_FUNC void free_defines(Sym *b)
-{
-    while (define_stack != b) {
-        Sym *top = define_stack;
-        define_stack = top->prev;
-        tok_str_free_str(top->d);
-        define_undef(top);
-        sym_free(top);
-    }
-
-    /* restore remaining (-D or predefined) symbols if they were
-       #undef'd in the file */
-    while (b) {
-        int v = b->v;
-        if (v >= TOK_IDENT && v < tok_ident) {
-            Sym **d = &table_ident[v - TOK_IDENT]->sym_define;
-            if (!*d)
-                *d = b;
-        }
-        b = b->prev;
-    }
-}
-
-/* label lookup */
-ST_FUNC Sym *label_find(int v)
-{
-    v -= TOK_IDENT;
-    if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
-        return NULL;
-    return table_ident[v]->sym_label;
-}
-
-ST_FUNC Sym *label_push(Sym **ptop, int v, int flags)
-{
-    Sym *s, **ps;
-    s = sym_push2(ptop, v, 0, 0);
-    s->r = flags;
-    ps = &table_ident[v - TOK_IDENT]->sym_label;
-    if (ptop == &global_label_stack) {
-        /* modify the top most local identifier, so that
-           sym_identifier will point to 's' when popped */
-        while (*ps != NULL)
-            ps = &(*ps)->prev_tok;
-    }
-    s->prev_tok = *ps;
-    *ps = s;
-    return s;
-}
-
-/* pop labels until element last is reached. Look if any labels are
-   undefined. Define symbols if '&&label' was used. */
-ST_FUNC void label_pop(Sym **ptop, Sym *slast, int keep)
-{
-    Sym *s, *s1;
-    for(s = *ptop; s != slast; s = s1) {
-        s1 = s->prev;
-        if (s->r == LABEL_DECLARED) {
-            tcc_warning("label '%s' declared but not used", get_tok_str(s->v, NULL));
-        } else if (s->r == LABEL_FORWARD) {
-                tcc_error("label '%s' used but not defined",
-                      get_tok_str(s->v, NULL));
-        } else {
-            if (s->c) {
-                /* define corresponding symbol. A size of
-                   1 is put. */
-                put_extern_sym(s, cur_text_section, s->jnext, 1);
-            }
-        }
-        /* remove label */
-        table_ident[s->v - TOK_IDENT]->sym_label = s->prev_tok;
-        if (!keep)
-            sym_free(s);
-    }
-    if (!keep)
-        *ptop = slast;
-}
-
-/* fake the nth "#if defined test_..." for tcc -dt -run */
-static void maybe_run_test(TCCState *s)
-{
-    const char *p;
-    if (s->include_stack_ptr != s->include_stack)
-        return;
-    p = get_tok_str(tok, NULL);
-    if (0 != memcmp(p, "test_", 5))
-        return;
-    if (0 != --s->run_test)
-        return;
-    fprintf(s->ppfp, "\n[%s]\n" + !(s->dflag & 32), p), fflush(s->ppfp);
-    define_push(tok, MACRO_OBJ, NULL, NULL);
-}
-
-/* eval an expression for #if/#elif */
-static int expr_preprocess(void)
-{
-    int c, t;
-    TokenString *str;
-    
-    str = tok_str_alloc();
-    pp_expr = 1;
-    while (tok != TOK_LINEFEED && tok != TOK_EOF) {
-        next(); /* do macro subst */
-        if (tok == TOK_DEFINED) {
-            next_nomacro();
-            t = tok;
-            if (t == '(') 
-                next_nomacro();
-            if (tok < TOK_IDENT)
-                expect("identifier");
-            if (tcc_state->run_test)
-                maybe_run_test(tcc_state);
-            c = define_find(tok) != 0;
-            if (t == '(') {
-                next_nomacro();
-                if (tok != ')')
-                    expect("')'");
-            }
-            tok = TOK_CINT;
-            tokc.i = c;
-        } else if (tok >= TOK_IDENT) {
-            /* if undefined macro */
-            tok = TOK_CINT;
-            tokc.i = 0;
-        }
-        tok_str_add_tok(str);
-    }
-    pp_expr = 0;
-    tok_str_add(str, -1); /* simulate end of file */
-    tok_str_add(str, 0);
-    /* now evaluate C constant expression */
-    begin_macro(str, 1);
-    next();
-    c = expr_const();
-    end_macro();
-    return c != 0;
-}
-
-
-/* parse after #define */
-ST_FUNC void parse_define(void)
-{
-    Sym *s, *first, **ps;
-    int v, t, varg, is_vaargs, spc;
-    int saved_parse_flags = parse_flags;
-
-    v = tok;
-    if (v < TOK_IDENT || v == TOK_DEFINED)
-        tcc_error("invalid macro name '%s'", get_tok_str(tok, &tokc));
-    /* XXX: should check if same macro (ANSI) */
-    first = NULL;
-    t = MACRO_OBJ;
-    /* We have to parse the whole define as if not in asm mode, in particular
-       no line comment with '#' must be ignored.  Also for function
-       macros the argument list must be parsed without '.' being an ID
-       character.  */
-    parse_flags = ((parse_flags & ~PARSE_FLAG_ASM_FILE) | PARSE_FLAG_SPACES);
-    /* '(' must be just after macro definition for MACRO_FUNC */
-    next_nomacro_spc();
-    if (tok == '(') {
-        int dotid = set_idnum('.', 0);
-        next_nomacro();
-        ps = &first;
-        if (tok != ')') for (;;) {
-            varg = tok;
-            next_nomacro();
-            is_vaargs = 0;
-            if (varg == TOK_DOTS) {
-                varg = TOK___VA_ARGS__;
-                is_vaargs = 1;
-            } else if (tok == TOK_DOTS && gnu_ext) {
-                is_vaargs = 1;
-                next_nomacro();
-            }
-            if (varg < TOK_IDENT)
-        bad_list:
-                tcc_error("bad macro parameter list");
-            s = sym_push2(&define_stack, varg | SYM_FIELD, is_vaargs, 0);
-            *ps = s;
-            ps = &s->next;
-            if (tok == ')')
-                break;
-            if (tok != ',' || is_vaargs)
-                goto bad_list;
-            next_nomacro();
-        }
-        next_nomacro_spc();
-        t = MACRO_FUNC;
-        set_idnum('.', dotid);
-    }
-
-    tokstr_buf.len = 0;
-    spc = 2;
-    parse_flags |= PARSE_FLAG_ACCEPT_STRAYS | PARSE_FLAG_SPACES | PARSE_FLAG_LINEFEED;
-    /* The body of a macro definition should be parsed such that identifiers
-       are parsed like the file mode determines (i.e. with '.' being an
-       ID character in asm mode).  But '#' should be retained instead of
-       regarded as line comment leader, so still don't set ASM_FILE
-       in parse_flags. */
-    while (tok != TOK_LINEFEED && tok != TOK_EOF) {
-        /* remove spaces around ## and after '#' */
-        if (TOK_TWOSHARPS == tok) {
-            if (2 == spc)
-                goto bad_twosharp;
-            if (1 == spc)
-                --tokstr_buf.len;
-            spc = 3;
-	    tok = TOK_PPJOIN;
-        } else if ('#' == tok) {
-            spc = 4;
-        } else if (check_space(tok, &spc)) {
-            goto skip;
-        }
-        tok_str_add2(&tokstr_buf, tok, &tokc);
-    skip:
-        next_nomacro_spc();
-    }
-
-    parse_flags = saved_parse_flags;
-    if (spc == 1)
-        --tokstr_buf.len; /* remove trailing space */
-    tok_str_add(&tokstr_buf, 0);
-    if (3 == spc)
-bad_twosharp:
-        tcc_error("'##' cannot appear at either end of macro");
-    define_push(v, t, tok_str_dup(&tokstr_buf), first);
-}
-
-static CachedInclude *search_cached_include(TCCState *s1, const char *filename, int add)
-{
-    const unsigned char *s;
-    unsigned int h;
-    CachedInclude *e;
-    int i;
-
-    h = TOK_HASH_INIT;
-    s = (unsigned char *) filename;
-    while (*s) {
-#ifdef _WIN32
-        h = TOK_HASH_FUNC(h, toup(*s));
-#else
-        h = TOK_HASH_FUNC(h, *s);
-#endif
-        s++;
-    }
-    h &= (CACHED_INCLUDES_HASH_SIZE - 1);
-
-    i = s1->cached_includes_hash[h];
-    for(;;) {
-        if (i == 0)
-            break;
-        e = s1->cached_includes[i - 1];
-        if (0 == PATHCMP(e->filename, filename))
-            return e;
-        i = e->hash_next;
-    }
-    if (!add)
-        return NULL;
-
-    e = tcc_malloc(sizeof(CachedInclude) + strlen(filename));
-    strcpy(e->filename, filename);
-    e->ifndef_macro = e->once = 0;
-    dynarray_add(&s1->cached_includes, &s1->nb_cached_includes, e);
-    /* add in hash table */
-    e->hash_next = s1->cached_includes_hash[h];
-    s1->cached_includes_hash[h] = s1->nb_cached_includes;
-#ifdef INC_DEBUG
-    printf("adding cached '%s'\n", filename);
-#endif
-    return e;
-}
-
-static void pragma_parse(TCCState *s1)
-{
-    next_nomacro();
-    if (tok == TOK_push_macro || tok == TOK_pop_macro) {
-        int t = tok, v;
-        Sym *s;
-
-        if (next(), tok != '(')
-            goto pragma_err;
-        if (next(), tok != TOK_STR)
-            goto pragma_err;
-        v = tok_alloc(tokc.str.data, tokc.str.size - 1)->tok;
-        if (next(), tok != ')')
-            goto pragma_err;
-        if (t == TOK_push_macro) {
-            while (NULL == (s = define_find(v)))
-                define_push(v, 0, NULL, NULL);
-            s->type.ref = s; /* set push boundary */
-        } else {
-            for (s = define_stack; s; s = s->prev)
-                if (s->v == v && s->type.ref == s) {
-                    s->type.ref = NULL;
-                    break;
-                }
-        }
-        if (s)
-            table_ident[v - TOK_IDENT]->sym_define = s->d ? s : NULL;
-        else
-            tcc_warning("unbalanced #pragma pop_macro");
-        pp_debug_tok = t, pp_debug_symv = v;
-
-    } else if (tok == TOK_once) {
-        search_cached_include(s1, file->filename, 1)->once = pp_once;
-
-    } else if (s1->output_type == TCC_OUTPUT_PREPROCESS) {
-        /* tcc -E: keep pragmas below unchanged */
-        unget_tok(' ');
-        unget_tok(TOK_PRAGMA);
-        unget_tok('#');
-        unget_tok(TOK_LINEFEED);
-
-    } else if (tok == TOK_pack) {
-        /* This may be:
-           #pragma pack(1) // set
-           #pragma pack() // reset to default
-           #pragma pack(push,1) // push & set
-           #pragma pack(pop) // restore previous */
-        next();
-        skip('(');
-        if (tok == TOK_ASM_pop) {
-            next();
-            if (s1->pack_stack_ptr <= s1->pack_stack) {
-            stk_error:
-                tcc_error("out of pack stack");
-            }
-            s1->pack_stack_ptr--;
-        } else {
-            int val = 0;
-            if (tok != ')') {
-                if (tok == TOK_ASM_push) {
-                    next();
-                    if (s1->pack_stack_ptr >= s1->pack_stack + PACK_STACK_SIZE - 1)
-                        goto stk_error;
-                    s1->pack_stack_ptr++;
-                    skip(',');
-                }
-                if (tok != TOK_CINT)
-                    goto pragma_err;
-                val = tokc.i;
-                if (val < 1 || val > 16 || (val & (val - 1)) != 0)
-                    goto pragma_err;
-                next();
-            }
-            *s1->pack_stack_ptr = val;
-        }
-        if (tok != ')')
-            goto pragma_err;
-
-    } else if (tok == TOK_comment) {
-        char *p; int t;
-        next();
-        skip('(');
-        t = tok;
-        next();
-        skip(',');
-        if (tok != TOK_STR)
-            goto pragma_err;
-        p = tcc_strdup((char *)tokc.str.data);
-        next();
-        if (tok != ')')
-            goto pragma_err;
-        if (t == TOK_lib) {
-            dynarray_add(&s1->pragma_libs, &s1->nb_pragma_libs, p);
-        } else {
-            if (t == TOK_option)
-                tcc_set_options(s1, p);
-            tcc_free(p);
-        }
-
-    } else if (s1->warn_unsupported) {
-        tcc_warning("#pragma %s is ignored", get_tok_str(tok, &tokc));
-    }
-    return;
-
-pragma_err:
-    tcc_error("malformed #pragma directive");
-    return;
-}
-
-/* is_bof is true if first non space token at beginning of file */
-ST_FUNC void preprocess(int is_bof)
-{
-    TCCState *s1 = tcc_state;
-    int i, c, n, saved_parse_flags;
-    char buf[1024], *q;
-    Sym *s;
-
-    saved_parse_flags = parse_flags;
-    parse_flags = PARSE_FLAG_PREPROCESS
-        | PARSE_FLAG_TOK_NUM
-        | PARSE_FLAG_TOK_STR
-        | PARSE_FLAG_LINEFEED
-        | (parse_flags & PARSE_FLAG_ASM_FILE)
-        ;
-
-    next_nomacro();
- redo:
-    switch(tok) {
-    case TOK_DEFINE:
-        pp_debug_tok = tok;
-        next_nomacro();
-        pp_debug_symv = tok;
-        parse_define();
-        break;
-    case TOK_UNDEF:
-        pp_debug_tok = tok;
-        next_nomacro();
-        pp_debug_symv = tok;
-        s = define_find(tok);
-        /* undefine symbol by putting an invalid name */
-        if (s)
-            define_undef(s);
-        break;
-    case TOK_INCLUDE:
-    case TOK_INCLUDE_NEXT:
-        ch = file->buf_ptr[0];
-        /* XXX: incorrect if comments : use next_nomacro with a special mode */
-        skip_spaces();
-        if (ch == '<') {
-            c = '>';
-            goto read_name;
-        } else if (ch == '\"') {
-            c = ch;
-        read_name:
-            inp();
-            q = buf;
-            while (ch != c && ch != '\n' && ch != CH_EOF) {
-                if ((q - buf) < sizeof(buf) - 1)
-                    *q++ = ch;
-                if (ch == '\\') {
-                    if (handle_stray_noerror() == 0)
-                        --q;
-                } else
-                    inp();
-            }
-            *q = '\0';
-            minp();
-#if 0
-            /* eat all spaces and comments after include */
-            /* XXX: slightly incorrect */
-            while (ch1 != '\n' && ch1 != CH_EOF)
-                inp();
-#endif
-        } else {
-	    int len;
-            /* computed #include : concatenate everything up to linefeed,
-	       the result must be one of the two accepted forms.
-	       Don't convert pp-tokens to tokens here.  */
-	    parse_flags = (PARSE_FLAG_PREPROCESS
-			   | PARSE_FLAG_LINEFEED
-			   | (parse_flags & PARSE_FLAG_ASM_FILE));
-            next();
-            buf[0] = '\0';
-	    while (tok != TOK_LINEFEED) {
-		pstrcat(buf, sizeof(buf), get_tok_str(tok, &tokc));
-		next();
-	    }
-	    len = strlen(buf);
-	    /* check syntax and remove '<>|""' */
-	    if ((len < 2 || ((buf[0] != '"' || buf[len-1] != '"') &&
-			     (buf[0] != '<' || buf[len-1] != '>'))))
-	        tcc_error("'#include' expects \"FILENAME\" or <FILENAME>");
-	    c = buf[len-1];
-	    memmove(buf, buf + 1, len - 2);
-	    buf[len - 2] = '\0';
-        }
-
-        if (s1->include_stack_ptr >= s1->include_stack + INCLUDE_STACK_SIZE)
-            tcc_error("#include recursion too deep");
-        /* store current file in stack, but increment stack later below */
-        *s1->include_stack_ptr = file;
-        i = tok == TOK_INCLUDE_NEXT ? file->include_next_index : 0;
-        n = 2 + s1->nb_include_paths + s1->nb_sysinclude_paths;
-        for (; i < n; ++i) {
-            char buf1[sizeof file->filename];
-            CachedInclude *e;
-            const char *path;
-
-            if (i == 0) {
-                /* check absolute include path */
-                if (!IS_ABSPATH(buf))
-                    continue;
-                buf1[0] = 0;
-
-            } else if (i == 1) {
-                /* search in file's dir if "header.h" */
-                if (c != '\"')
-                    continue;
-                /* https://savannah.nongnu.org/bugs/index.php?50847 */
-                path = file->true_filename;
-                pstrncpy(buf1, path, tcc_basename(path) - path);
-
-            } else {
-                /* search in all the include paths */
-                int j = i - 2, k = j - s1->nb_include_paths;
-                path = k < 0 ? s1->include_paths[j] : s1->sysinclude_paths[k];
-                pstrcpy(buf1, sizeof(buf1), path);
-                pstrcat(buf1, sizeof(buf1), "/");
-            }
-
-            pstrcat(buf1, sizeof(buf1), buf);
-            e = search_cached_include(s1, buf1, 0);
-            if (e && (define_find(e->ifndef_macro) || e->once == pp_once)) {
-                /* no need to parse the include because the 'ifndef macro'
-                   is defined (or had #pragma once) */
-#ifdef INC_DEBUG
-                printf("%s: skipping cached %s\n", file->filename, buf1);
-#endif
-                goto include_done;
-            }
-
-            if (tcc_open(s1, buf1) < 0)
-                continue;
-
-            file->include_next_index = i + 1;
-#ifdef INC_DEBUG
-            printf("%s: including %s\n", file->prev->filename, file->filename);
-#endif
-            /* update target deps */
-            dynarray_add(&s1->target_deps, &s1->nb_target_deps,
-                    tcc_strdup(buf1));
-            /* push current file in stack */
-            ++s1->include_stack_ptr;
-            /* add include file debug info */
-            if (s1->do_debug)
-                put_stabs(file->filename, N_BINCL, 0, 0, 0);
-            tok_flags |= TOK_FLAG_BOF | TOK_FLAG_BOL;
-            ch = file->buf_ptr[0];
-            goto the_end;
-        }
-        tcc_error("include file '%s' not found", buf);
-include_done:
-        break;
-    case TOK_IFNDEF:
-        c = 1;
-        goto do_ifdef;
-    case TOK_IF:
-        c = expr_preprocess();
-        goto do_if;
-    case TOK_IFDEF:
-        c = 0;
-    do_ifdef:
-        next_nomacro();
-        if (tok < TOK_IDENT)
-            tcc_error("invalid argument for '#if%sdef'", c ? "n" : "");
-        if (is_bof) {
-            if (c) {
-#ifdef INC_DEBUG
-                printf("#ifndef %s\n", get_tok_str(tok, NULL));
-#endif
-                file->ifndef_macro = tok;
-            }
-        }
-        c = (define_find(tok) != 0) ^ c;
-    do_if:
-        if (s1->ifdef_stack_ptr >= s1->ifdef_stack + IFDEF_STACK_SIZE)
-            tcc_error("memory full (ifdef)");
-        *s1->ifdef_stack_ptr++ = c;
-        goto test_skip;
-    case TOK_ELSE:
-        if (s1->ifdef_stack_ptr == s1->ifdef_stack)
-            tcc_error("#else without matching #if");
-        if (s1->ifdef_stack_ptr[-1] & 2)
-            tcc_error("#else after #else");
-        c = (s1->ifdef_stack_ptr[-1] ^= 3);
-        goto test_else;
-    case TOK_ELIF:
-        if (s1->ifdef_stack_ptr == s1->ifdef_stack)
-            tcc_error("#elif without matching #if");
-        c = s1->ifdef_stack_ptr[-1];
-        if (c > 1)
-            tcc_error("#elif after #else");
-        /* last #if/#elif expression was true: we skip */
-        if (c == 1) {
-            c = 0;
-        } else {
-            c = expr_preprocess();
-            s1->ifdef_stack_ptr[-1] = c;
-        }
-    test_else:
-        if (s1->ifdef_stack_ptr == file->ifdef_stack_ptr + 1)
-            file->ifndef_macro = 0;
-    test_skip:
-        if (!(c & 1)) {
-            preprocess_skip();
-            is_bof = 0;
-            goto redo;
-        }
-        break;
-    case TOK_ENDIF:
-        if (s1->ifdef_stack_ptr <= file->ifdef_stack_ptr)
-            tcc_error("#endif without matching #if");
-        s1->ifdef_stack_ptr--;
-        /* '#ifndef macro' was at the start of file. Now we check if
-           an '#endif' is exactly at the end of file */
-        if (file->ifndef_macro &&
-            s1->ifdef_stack_ptr == file->ifdef_stack_ptr) {
-            file->ifndef_macro_saved = file->ifndef_macro;
-            /* need to set to zero to avoid false matches if another
-               #ifndef at middle of file */
-            file->ifndef_macro = 0;
-            while (tok != TOK_LINEFEED)
-                next_nomacro();
-            tok_flags |= TOK_FLAG_ENDIF;
-            goto the_end;
-        }
-        break;
-    case TOK_PPNUM:
-        n = strtoul((char*)tokc.str.data, &q, 10);
-        goto _line_num;
-    case TOK_LINE:
-        next();
-        if (tok != TOK_CINT)
-    _line_err:
-            tcc_error("wrong #line format");
-        n = tokc.i;
-    _line_num:
-        next();
-        if (tok != TOK_LINEFEED) {
-            if (tok == TOK_STR) {
-                if (file->true_filename == file->filename)
-                    file->true_filename = tcc_strdup(file->filename);
-                pstrcpy(file->filename, sizeof(file->filename), (char *)tokc.str.data);
-            } else if (parse_flags & PARSE_FLAG_ASM_FILE)
-                break;
-            else
-                goto _line_err;
-            --n;
-        }
-        if (file->fd > 0)
-            total_lines += file->line_num - n;
-        file->line_num = n;
-        if (s1->do_debug)
-    	    put_stabs(file->filename, N_BINCL, 0, 0, 0);
-        break;
-    case TOK_ERROR:
-    case TOK_WARNING:
-        c = tok;
-        ch = file->buf_ptr[0];
-        skip_spaces();
-        q = buf;
-        while (ch != '\n' && ch != CH_EOF) {
-            if ((q - buf) < sizeof(buf) - 1)
-                *q++ = ch;
-            if (ch == '\\') {
-                if (handle_stray_noerror() == 0)
-                    --q;
-            } else
-                inp();
-        }
-        *q = '\0';
-        if (c == TOK_ERROR)
-            tcc_error("#error %s", buf);
-        else
-            tcc_warning("#warning %s", buf);
-        break;
-    case TOK_PRAGMA:
-        pragma_parse(s1);
-        break;
-    case TOK_LINEFEED:
-        goto the_end;
-    default:
-        /* ignore gas line comment in an 'S' file. */
-        if (saved_parse_flags & PARSE_FLAG_ASM_FILE)
-            goto ignore;
-        if (tok == '!' && is_bof)
-            /* '!' is ignored at beginning to allow C scripts. */
-            goto ignore;
-        tcc_warning("Ignoring unknown preprocessing directive #%s", get_tok_str(tok, &tokc));
-    ignore:
-        file->buf_ptr = parse_line_comment(file->buf_ptr - 1);
-        goto the_end;
-    }
-    /* ignore other preprocess commands or #! for C scripts */
-    while (tok != TOK_LINEFEED)
-        next_nomacro();
- the_end:
-    parse_flags = saved_parse_flags;
-}
-
-/* evaluate escape codes in a string. */
-static void parse_escape_string(CString *outstr, const uint8_t *buf, int is_long)
-{
-    int c, n;
-    const uint8_t *p;
-
-    p = buf;
-    for(;;) {
-        c = *p;
-        if (c == '\0')
-            break;
-        if (c == '\\') {
-            p++;
-            /* escape */
-            c = *p;
-            switch(c) {
-            case '0': case '1': case '2': case '3':
-            case '4': case '5': case '6': case '7':
-                /* at most three octal digits */
-                n = c - '0';
-                p++;
-                c = *p;
-                if (isoct(c)) {
-                    n = n * 8 + c - '0';
-                    p++;
-                    c = *p;
-                    if (isoct(c)) {
-                        n = n * 8 + c - '0';
-                        p++;
-                    }
-                }
-                c = n;
-                goto add_char_nonext;
-            case 'x':
-            case 'u':
-            case 'U':
-                p++;
-                n = 0;
-                for(;;) {
-                    c = *p;
-                    if (c >= 'a' && c <= 'f')
-                        c = c - 'a' + 10;
-                    else if (c >= 'A' && c <= 'F')
-                        c = c - 'A' + 10;
-                    else if (isnum(c))
-                        c = c - '0';
-                    else
-                        break;
-                    n = n * 16 + c;
-                    p++;
-                }
-                c = n;
-                goto add_char_nonext;
-            case 'a':
-                c = '\a';
-                break;
-            case 'b':
-                c = '\b';
-                break;
-            case 'f':
-                c = '\f';
-                break;
-            case 'n':
-                c = '\n';
-                break;
-            case 'r':
-                c = '\r';
-                break;
-            case 't':
-                c = '\t';
-                break;
-            case 'v':
-                c = '\v';
-                break;
-            case 'e':
-                if (!gnu_ext)
-                    goto invalid_escape;
-                c = 27;
-                break;
-            case '\'':
-            case '\"':
-            case '\\': 
-            case '?':
-                break;
-            default:
-            invalid_escape:
-                if (c >= '!' && c <= '~')
-                    tcc_warning("unknown escape sequence: \'\\%c\'", c);
-                else
-                    tcc_warning("unknown escape sequence: \'\\x%x\'", c);
-                break;
-            }
-        } else if (is_long && c >= 0x80) {
-            /* assume we are processing UTF-8 sequence */
-            /* reference: The Unicode Standard, Version 10.0, ch3.9 */
-
-            int cont; /* count of continuation bytes */
-            int skip; /* how many bytes should skip when error occurred */
-            int i;
-
-            /* decode leading byte */
-            if (c < 0xC2) {
-	            skip = 1; goto invalid_utf8_sequence;
-            } else if (c <= 0xDF) {
-	            cont = 1; n = c & 0x1f;
-            } else if (c <= 0xEF) {
-	            cont = 2; n = c & 0xf;
-            } else if (c <= 0xF4) {
-	            cont = 3; n = c & 0x7;
-            } else {
-	            skip = 1; goto invalid_utf8_sequence;
-            }
-
-            /* decode continuation bytes */
-            for (i = 1; i <= cont; i++) {
-                int l = 0x80, h = 0xBF;
-
-                /* adjust limit for second byte */
-                if (i == 1) {
-                    switch (c) {
-                    case 0xE0: l = 0xA0; break;
-                    case 0xED: h = 0x9F; break;
-                    case 0xF0: l = 0x90; break;
-                    case 0xF4: h = 0x8F; break;
-                    }
-                }
-
-                if (p[i] < l || p[i] > h) {
-                    skip = i; goto invalid_utf8_sequence;
-                }
-
-                n = (n << 6) | (p[i] & 0x3f);
-            }
-
-            /* advance pointer */
-            p += 1 + cont;
-            c = n;
-            goto add_char_nonext;
-
-            /* error handling */
-        invalid_utf8_sequence:
-            tcc_warning("ill-formed UTF-8 subsequence starting with: \'\\x%x\'", c);
-            c = 0xFFFD;
-            p += skip;
-            goto add_char_nonext;
-
-        }
-        p++;
-    add_char_nonext:
-        if (!is_long)
-            cstr_ccat(outstr, c);
-        else {
-#ifdef TCC_TARGET_PE
-            /* store as UTF-16 */
-            if (c < 0x10000) {
-                cstr_wccat(outstr, c);
-            } else {
-                c -= 0x10000;
-                cstr_wccat(outstr, (c >> 10) + 0xD800);
-                cstr_wccat(outstr, (c & 0x3FF) + 0xDC00);
-            }
-#else
-            cstr_wccat(outstr, c);
-#endif
-        }
-    }
-    /* add a trailing '\0' */
-    if (!is_long)
-        cstr_ccat(outstr, '\0');
-    else
-        cstr_wccat(outstr, '\0');
-}
-
-static void parse_string(const char *s, int len)
-{
-    uint8_t buf[1000], *p = buf;
-    int is_long, sep;
-
-    if ((is_long = *s == 'L'))
-        ++s, --len;
-    sep = *s++;
-    len -= 2;
-    if (len >= sizeof buf)
-        p = tcc_malloc(len + 1);
-    memcpy(p, s, len);
-    p[len] = 0;
-
-    cstr_reset(&tokcstr);
-    parse_escape_string(&tokcstr, p, is_long);
-    if (p != buf)
-        tcc_free(p);
-
-    if (sep == '\'') {
-        int char_size, i, n, c;
-        /* XXX: make it portable */
-        if (!is_long)
-            tok = TOK_CCHAR, char_size = 1;
-        else
-            tok = TOK_LCHAR, char_size = sizeof(nwchar_t);
-        n = tokcstr.size / char_size - 1;
-        if (n < 1)
-            tcc_error("empty character constant");
-        if (n > 1)
-            tcc_warning("multi-character character constant");
-        for (c = i = 0; i < n; ++i) {
-            if (is_long)
-                c = ((nwchar_t *)tokcstr.data)[i];
-            else
-                c = (c << 8) | ((char *)tokcstr.data)[i];
-        }
-        tokc.i = c;
-    } else {
-        tokc.str.size = tokcstr.size;
-        tokc.str.data = tokcstr.data;
-        if (!is_long)
-            tok = TOK_STR;
-        else
-            tok = TOK_LSTR;
-    }
-}
-
-/* we use 64 bit numbers */
-#define BN_SIZE 2
-
-/* bn = (bn << shift) | or_val */
-static void bn_lshift(unsigned int *bn, int shift, int or_val)
-{
-    int i;
-    unsigned int v;
-    for(i=0;i<BN_SIZE;i++) {
-        v = bn[i];
-        bn[i] = (v << shift) | or_val;
-        or_val = v >> (32 - shift);
-    }
-}
-
-static void bn_zero(unsigned int *bn)
-{
-    int i;
-    for(i=0;i<BN_SIZE;i++) {
-        bn[i] = 0;
-    }
-}
-
-/* parse number in null terminated string 'p' and return it in the
-   current token */
-static void parse_number(const char *p)
-{
-    int b, t, shift, frac_bits, s, exp_val, ch;
-    char *q;
-    unsigned int bn[BN_SIZE];
-    double d;
-
-    /* number */
-    q = token_buf;
-    ch = *p++;
-    t = ch;
-    ch = *p++;
-    *q++ = t;
-    b = 10;
-    if (t == '.') {
-        goto float_frac_parse;
-    } else if (t == '0') {
-        if (ch == 'x' || ch == 'X') {
-            q--;
-            ch = *p++;
-            b = 16;
-        } else if (tcc_ext && (ch == 'b' || ch == 'B')) {
-            q--;
-            ch = *p++;
-            b = 2;
-        }
-    }
-    /* parse all digits. cannot check octal numbers at this stage
-       because of floating point constants */
-    while (1) {
-        if (ch >= 'a' && ch <= 'f')
-            t = ch - 'a' + 10;
-        else if (ch >= 'A' && ch <= 'F')
-            t = ch - 'A' + 10;
-        else if (isnum(ch))
-            t = ch - '0';
-        else
-            break;
-        if (t >= b)
-            break;
-        if (q >= token_buf + STRING_MAX_SIZE) {
-        num_too_long:
-            tcc_error("number too long");
-        }
-        *q++ = ch;
-        ch = *p++;
-    }
-    if (ch == '.' ||
-        ((ch == 'e' || ch == 'E') && b == 10) ||
-        ((ch == 'p' || ch == 'P') && (b == 16 || b == 2))) {
-        if (b != 10) {
-            /* NOTE: strtox should support that for hexa numbers, but
-               non ISOC99 libcs do not support it, so we prefer to do
-               it by hand */
-            /* hexadecimal or binary floats */
-            /* XXX: handle overflows */
-            *q = '\0';
-            if (b == 16)
-                shift = 4;
-            else 
-                shift = 1;
-            bn_zero(bn);
-            q = token_buf;
-            while (1) {
-                t = *q++;
-                if (t == '\0') {
-                    break;
-                } else if (t >= 'a') {
-                    t = t - 'a' + 10;
-                } else if (t >= 'A') {
-                    t = t - 'A' + 10;
-                } else {
-                    t = t - '0';
-                }
-                bn_lshift(bn, shift, t);
-            }
-            frac_bits = 0;
-            if (ch == '.') {
-                ch = *p++;
-                while (1) {
-                    t = ch;
-                    if (t >= 'a' && t <= 'f') {
-                        t = t - 'a' + 10;
-                    } else if (t >= 'A' && t <= 'F') {
-                        t = t - 'A' + 10;
-                    } else if (t >= '0' && t <= '9') {
-                        t = t - '0';
-                    } else {
-                        break;
-                    }
-                    if (t >= b)
-                        tcc_error("invalid digit");
-                    bn_lshift(bn, shift, t);
-                    frac_bits += shift;
-                    ch = *p++;
-                }
-            }
-            if (ch != 'p' && ch != 'P')
-                expect("exponent");
-            ch = *p++;
-            s = 1;
-            exp_val = 0;
-            if (ch == '+') {
-                ch = *p++;
-            } else if (ch == '-') {
-                s = -1;
-                ch = *p++;
-            }
-            if (ch < '0' || ch > '9')
-                expect("exponent digits");
-            while (ch >= '0' && ch <= '9') {
-                exp_val = exp_val * 10 + ch - '0';
-                ch = *p++;
-            }
-            exp_val = exp_val * s;
-            
-            /* now we can generate the number */
-            /* XXX: should patch directly float number */
-            d = (double)bn[1] * 4294967296.0 + (double)bn[0];
-            d = ldexp(d, exp_val - frac_bits);
-            t = toup(ch);
-            if (t == 'F') {
-                ch = *p++;
-                tok = TOK_CFLOAT;
-                /* float : should handle overflow */
-                tokc.f = (float)d;
-            } else if (t == 'L') {
-                ch = *p++;
-#ifdef TCC_TARGET_PE
-                tok = TOK_CDOUBLE;
-                tokc.d = d;
-#else
-                tok = TOK_CLDOUBLE;
-                /* XXX: not large enough */
-                tokc.ld = (long double)d;
-#endif
-            } else {
-                tok = TOK_CDOUBLE;
-                tokc.d = d;
-            }
-        } else {
-            /* decimal floats */
-            if (ch == '.') {
-                if (q >= token_buf + STRING_MAX_SIZE)
-                    goto num_too_long;
-                *q++ = ch;
-                ch = *p++;
-            float_frac_parse:
-                while (ch >= '0' && ch <= '9') {
-                    if (q >= token_buf + STRING_MAX_SIZE)
-                        goto num_too_long;
-                    *q++ = ch;
-                    ch = *p++;
-                }
-            }
-            if (ch == 'e' || ch == 'E') {
-                if (q >= token_buf + STRING_MAX_SIZE)
-                    goto num_too_long;
-                *q++ = ch;
-                ch = *p++;
-                if (ch == '-' || ch == '+') {
-                    if (q >= token_buf + STRING_MAX_SIZE)
-                        goto num_too_long;
-                    *q++ = ch;
-                    ch = *p++;
-                }
-                if (ch < '0' || ch > '9')
-                    expect("exponent digits");
-                while (ch >= '0' && ch <= '9') {
-                    if (q >= token_buf + STRING_MAX_SIZE)
-                        goto num_too_long;
-                    *q++ = ch;
-                    ch = *p++;
-                }
-            }
-            *q = '\0';
-            t = toup(ch);
-            errno = 0;
-            if (t == 'F') {
-                ch = *p++;
-                tok = TOK_CFLOAT;
-                tokc.f = strtof(token_buf, NULL);
-            } else if (t == 'L') {
-                ch = *p++;
-#ifdef TCC_TARGET_PE
-                tok = TOK_CDOUBLE;
-                tokc.d = strtod(token_buf, NULL);
-#else
-                tok = TOK_CLDOUBLE;
-                tokc.ld = strtold(token_buf, NULL);
-#endif
-            } else {
-                tok = TOK_CDOUBLE;
-                tokc.d = strtod(token_buf, NULL);
-            }
-        }
-    } else {
-        unsigned long long n, n1;
-        int lcount, ucount, ov = 0;
-        const char *p1;
-
-        /* integer number */
-        *q = '\0';
-        q = token_buf;
-        if (b == 10 && *q == '0') {
-            b = 8;
-            q++;
-        }
-        n = 0;
-        while(1) {
-            t = *q++;
-            /* no need for checks except for base 10 / 8 errors */
-            if (t == '\0')
-                break;
-            else if (t >= 'a')
-                t = t - 'a' + 10;
-            else if (t >= 'A')
-                t = t - 'A' + 10;
-            else
-                t = t - '0';
-            if (t >= b)
-                tcc_error("invalid digit");
-            n1 = n;
-            n = n * b + t;
-            /* detect overflow */
-            if (n1 >= 0x1000000000000000ULL && n / b != n1)
-                ov = 1;
-        }
-
-        /* Determine the characteristics (unsigned and/or 64bit) the type of
-           the constant must have according to the constant suffix(es) */
-        lcount = ucount = 0;
-        p1 = p;
-        for(;;) {
-            t = toup(ch);
-            if (t == 'L') {
-                if (lcount >= 2)
-                    tcc_error("three 'l's in integer constant");
-                if (lcount && *(p - 1) != ch)
-                    tcc_error("incorrect integer suffix: %s", p1);
-                lcount++;
-                ch = *p++;
-            } else if (t == 'U') {
-                if (ucount >= 1)
-                    tcc_error("two 'u's in integer constant");
-                ucount++;
-                ch = *p++;
-            } else {
-                break;
-            }
-        }
-
-        /* Determine if it needs 64 bits and/or unsigned in order to fit */
-        if (ucount == 0 && b == 10) {
-            if (lcount <= (LONG_SIZE == 4)) {
-                if (n >= 0x80000000U)
-                    lcount = (LONG_SIZE == 4) + 1;
-            }
-            if (n >= 0x8000000000000000ULL)
-                ov = 1, ucount = 1;
-        } else {
-            if (lcount <= (LONG_SIZE == 4)) {
-                if (n >= 0x100000000ULL)
-                    lcount = (LONG_SIZE == 4) + 1;
-                else if (n >= 0x80000000U)
-                    ucount = 1;
-            }
-            if (n >= 0x8000000000000000ULL)
-                ucount = 1;
-        }
-
-        if (ov)
-            tcc_warning("integer constant overflow");
-
-        tok = TOK_CINT;
-	if (lcount) {
-            tok = TOK_CLONG;
-            if (lcount == 2)
-                tok = TOK_CLLONG;
-	}
-	if (ucount)
-	    ++tok; /* TOK_CU... */
-        tokc.i = n;
-    }
-    if (ch)
-        tcc_error("invalid number\n");
-}
-
-
-#define PARSE2(c1, tok1, c2, tok2)              \
-    case c1:                                    \
-        PEEKC(c, p);                            \
-        if (c == c2) {                          \
-            p++;                                \
-            tok = tok2;                         \
-        } else {                                \
-            tok = tok1;                         \
-        }                                       \
-        break;
-
-/* return next token without macro substitution */
-static inline void next_nomacro1(void)
-{
-    int t, c, is_long, len;
-    TokenSym *ts;
-    uint8_t *p, *p1;
-    unsigned int h;
-
-    p = file->buf_ptr;
- redo_no_start:
-    c = *p;
-    switch(c) {
-    case ' ':
-    case '\t':
-        tok = c;
-        p++;
-        if (parse_flags & PARSE_FLAG_SPACES)
-            goto keep_tok_flags;
-        while (isidnum_table[*p - CH_EOF] & IS_SPC)
-            ++p;
-        goto redo_no_start;
-    case '\f':
-    case '\v':
-    case '\r':
-        p++;
-        goto redo_no_start;
-    case '\\':
-        /* first look if it is in fact an end of buffer */
-        c = handle_stray1(p);
-        p = file->buf_ptr;
-        if (c == '\\')
-            goto parse_simple;
-        if (c != CH_EOF)
-            goto redo_no_start;
-        {
-            TCCState *s1 = tcc_state;
-            if ((parse_flags & PARSE_FLAG_LINEFEED)
-                && !(tok_flags & TOK_FLAG_EOF)) {
-                tok_flags |= TOK_FLAG_EOF;
-                tok = TOK_LINEFEED;
-                goto keep_tok_flags;
-            } else if (!(parse_flags & PARSE_FLAG_PREPROCESS)) {
-                tok = TOK_EOF;
-            } else if (s1->ifdef_stack_ptr != file->ifdef_stack_ptr) {
-                tcc_error("missing #endif");
-            } else if (s1->include_stack_ptr == s1->include_stack) {
-                /* no include left : end of file. */
-                tok = TOK_EOF;
-            } else {
-                tok_flags &= ~TOK_FLAG_EOF;
-                /* pop include file */
-                
-                /* test if previous '#endif' was after a #ifdef at
-                   start of file */
-                if (tok_flags & TOK_FLAG_ENDIF) {
-#ifdef INC_DEBUG
-                    printf("#endif %s\n", get_tok_str(file->ifndef_macro_saved, NULL));
-#endif
-                    search_cached_include(s1, file->filename, 1)
-                        ->ifndef_macro = file->ifndef_macro_saved;
-                    tok_flags &= ~TOK_FLAG_ENDIF;
-                }
-
-                /* add end of include file debug info */
-                if (tcc_state->do_debug) {
-                    put_stabd(N_EINCL, 0, 0);
-                }
-                /* pop include stack */
-                tcc_close();
-                s1->include_stack_ptr--;
-                p = file->buf_ptr;
-                if (p == file->buffer)
-                    tok_flags = TOK_FLAG_BOF|TOK_FLAG_BOL;
-                goto redo_no_start;
-            }
-        }
-        break;
-
-    case '\n':
-        file->line_num++;
-        tok_flags |= TOK_FLAG_BOL;
-        p++;
-maybe_newline:
-        if (0 == (parse_flags & PARSE_FLAG_LINEFEED))
-            goto redo_no_start;
-        tok = TOK_LINEFEED;
-        goto keep_tok_flags;
-
-    case '#':
-        /* XXX: simplify */
-        PEEKC(c, p);
-        if ((tok_flags & TOK_FLAG_BOL) && 
-            (parse_flags & PARSE_FLAG_PREPROCESS)) {
-            file->buf_ptr = p;
-            preprocess(tok_flags & TOK_FLAG_BOF);
-            p = file->buf_ptr;
-            goto maybe_newline;
-        } else {
-            if (c == '#') {
-                p++;
-                tok = TOK_TWOSHARPS;
-            } else {
-                if (parse_flags & PARSE_FLAG_ASM_FILE) {
-                    p = parse_line_comment(p - 1);
-                    goto redo_no_start;
-                } else {
-                    tok = '#';
-                }
-            }
-        }
-        break;
-    
-    /* dollar is allowed to start identifiers when not parsing asm */
-    case '$':
-        if (!(isidnum_table[c - CH_EOF] & IS_ID)
-         || (parse_flags & PARSE_FLAG_ASM_FILE))
-            goto parse_simple;
-
-    case 'a': case 'b': case 'c': case 'd':
-    case 'e': case 'f': case 'g': case 'h':
-    case 'i': case 'j': case 'k': case 'l':
-    case 'm': case 'n': case 'o': case 'p':
-    case 'q': case 'r': case 's': case 't':
-    case 'u': case 'v': case 'w': case 'x':
-    case 'y': case 'z': 
-    case 'A': case 'B': case 'C': case 'D':
-    case 'E': case 'F': case 'G': case 'H':
-    case 'I': case 'J': case 'K': 
-    case 'M': case 'N': case 'O': case 'P':
-    case 'Q': case 'R': case 'S': case 'T':
-    case 'U': case 'V': case 'W': case 'X':
-    case 'Y': case 'Z': 
-    case '_':
-    parse_ident_fast:
-        p1 = p;
-        h = TOK_HASH_INIT;
-        h = TOK_HASH_FUNC(h, c);
-        while (c = *++p, isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))
-            h = TOK_HASH_FUNC(h, c);
-        len = p - p1;
-        if (c != '\\') {
-            TokenSym **pts;
-
-            /* fast case : no stray found, so we have the full token
-               and we have already hashed it */
-            h &= (TOK_HASH_SIZE - 1);
-            pts = &hash_ident[h];
-            for(;;) {
-                ts = *pts;
-                if (!ts)
-                    break;
-                if (ts->len == len && !memcmp(ts->str, p1, len))
-                    goto token_found;
-                pts = &(ts->hash_next);
-            }
-            ts = tok_alloc_new(pts, (char *) p1, len);
-        token_found: ;
-        } else {
-            /* slower case */
-            cstr_reset(&tokcstr);
-            cstr_cat(&tokcstr, (char *) p1, len);
-            p--;
-            PEEKC(c, p);
-        parse_ident_slow:
-            while (isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))
-            {
-                cstr_ccat(&tokcstr, c);
-                PEEKC(c, p);
-            }
-            ts = tok_alloc(tokcstr.data, tokcstr.size);
-        }
-        tok = ts->tok;
-        break;
-    case 'L':
-        t = p[1];
-        if (t != '\\' && t != '\'' && t != '\"') {
-            /* fast case */
-            goto parse_ident_fast;
-        } else {
-            PEEKC(c, p);
-            if (c == '\'' || c == '\"') {
-                is_long = 1;
-                goto str_const;
-            } else {
-                cstr_reset(&tokcstr);
-                cstr_ccat(&tokcstr, 'L');
-                goto parse_ident_slow;
-            }
-        }
-        break;
-
-    case '0': case '1': case '2': case '3':
-    case '4': case '5': case '6': case '7':
-    case '8': case '9':
-        t = c;
-        PEEKC(c, p);
-        /* after the first digit, accept digits, alpha, '.' or sign if
-           prefixed by 'eEpP' */
-    parse_num:
-        cstr_reset(&tokcstr);
-        for(;;) {
-            cstr_ccat(&tokcstr, t);
-            if (!((isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))
-                  || c == '.'
-                  || ((c == '+' || c == '-')
-                      && (((t == 'e' || t == 'E')
-                            && !(parse_flags & PARSE_FLAG_ASM_FILE
-                                /* 0xe+1 is 3 tokens in asm */
-                                && ((char*)tokcstr.data)[0] == '0'
-                                && toup(((char*)tokcstr.data)[1]) == 'X'))
-                          || t == 'p' || t == 'P'))))
-                break;
-            t = c;
-            PEEKC(c, p);
-        }
-        /* We add a trailing '\0' to ease parsing */
-        cstr_ccat(&tokcstr, '\0');
-        tokc.str.size = tokcstr.size;
-        tokc.str.data = tokcstr.data;
-        tok = TOK_PPNUM;
-        break;
-
-    case '.':
-        /* special dot handling because it can also start a number */
-        PEEKC(c, p);
-        if (isnum(c)) {
-            t = '.';
-            goto parse_num;
-        } else if ((isidnum_table['.' - CH_EOF] & IS_ID)
-                   && (isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))) {
-            *--p = c = '.';
-            goto parse_ident_fast;
-        } else if (c == '.') {
-            PEEKC(c, p);
-            if (c == '.') {
-                p++;
-                tok = TOK_DOTS;
-            } else {
-                *--p = '.'; /* may underflow into file->unget[] */
-                tok = '.';
-            }
-        } else {
-            tok = '.';
-        }
-        break;
-    case '\'':
-    case '\"':
-        is_long = 0;
-    str_const:
-        cstr_reset(&tokcstr);
-        if (is_long)
-            cstr_ccat(&tokcstr, 'L');
-        cstr_ccat(&tokcstr, c);
-        p = parse_pp_string(p, c, &tokcstr);
-        cstr_ccat(&tokcstr, c);
-        cstr_ccat(&tokcstr, '\0');
-        tokc.str.size = tokcstr.size;
-        tokc.str.data = tokcstr.data;
-        tok = TOK_PPSTR;
-        break;
-
-    case '<':
-        PEEKC(c, p);
-        if (c == '=') {
-            p++;
-            tok = TOK_LE;
-        } else if (c == '<') {
-            PEEKC(c, p);
-            if (c == '=') {
-                p++;
-                tok = TOK_A_SHL;
-            } else {
-                tok = TOK_SHL;
-            }
-        } else {
-            tok = TOK_LT;
-        }
-        break;
-    case '>':
-        PEEKC(c, p);
-        if (c == '=') {
-            p++;
-            tok = TOK_GE;
-        } else if (c == '>') {
-            PEEKC(c, p);
-            if (c == '=') {
-                p++;
-                tok = TOK_A_SAR;
-            } else {
-                tok = TOK_SAR;
-            }
-        } else {
-            tok = TOK_GT;
-        }
-        break;
-        
-    case '&':
-        PEEKC(c, p);
-        if (c == '&') {
-            p++;
-            tok = TOK_LAND;
-        } else if (c == '=') {
-            p++;
-            tok = TOK_A_AND;
-        } else {
-            tok = '&';
-        }
-        break;
-        
-    case '|':
-        PEEKC(c, p);
-        if (c == '|') {
-            p++;
-            tok = TOK_LOR;
-        } else if (c == '=') {
-            p++;
-            tok = TOK_A_OR;
-        } else {
-            tok = '|';
-        }
-        break;
-
-    case '+':
-        PEEKC(c, p);
-        if (c == '+') {
-            p++;
-            tok = TOK_INC;
-        } else if (c == '=') {
-            p++;
-            tok = TOK_A_ADD;
-        } else {
-            tok = '+';
-        }
-        break;
-        
-    case '-':
-        PEEKC(c, p);
-        if (c == '-') {
-            p++;
-            tok = TOK_DEC;
-        } else if (c == '=') {
-            p++;
-            tok = TOK_A_SUB;
-        } else if (c == '>') {
-            p++;
-            tok = TOK_ARROW;
-        } else {
-            tok = '-';
-        }
-        break;
-
-    PARSE2('!', '!', '=', TOK_NE)
-    PARSE2('=', '=', '=', TOK_EQ)
-    PARSE2('*', '*', '=', TOK_A_MUL)
-    PARSE2('%', '%', '=', TOK_A_MOD)
-    PARSE2('^', '^', '=', TOK_A_XOR)
-        
-        /* comments or operator */
-    case '/':
-        PEEKC(c, p);
-        if (c == '*') {
-            p = parse_comment(p);
-            /* comments replaced by a blank */
-            tok = ' ';
-            goto keep_tok_flags;
-        } else if (c == '/') {
-            p = parse_line_comment(p);
-            tok = ' ';
-            goto keep_tok_flags;
-        } else if (c == '=') {
-            p++;
-            tok = TOK_A_DIV;
-        } else {
-            tok = '/';
-        }
-        break;
-        
-        /* simple tokens */
-    case '(':
-    case ')':
-    case '[':
-    case ']':
-    case '{':
-    case '}':
-    case ',':
-    case ';':
-    case ':':
-    case '?':
-    case '~':
-    case '@': /* only used in assembler */
-    parse_simple:
-        tok = c;
-        p++;
-        break;
-    default:
-        if (c >= 0x80 && c <= 0xFF) /* utf8 identifiers */
-	    goto parse_ident_fast;
-        if (parse_flags & PARSE_FLAG_ASM_FILE)
-            goto parse_simple;
-        tcc_error("unrecognized character \\x%02x", c);
-        break;
-    }
-    tok_flags = 0;
-keep_tok_flags:
-    file->buf_ptr = p;
-#if defined(PARSE_DEBUG)
-    printf("token = %d %s\n", tok, get_tok_str(tok, &tokc));
-#endif
-}
-
-/* return next token without macro substitution. Can read input from
-   macro_ptr buffer */
-static void next_nomacro_spc(void)
-{
-    if (macro_ptr) {
-    redo:
-        tok = *macro_ptr;
-        if (tok) {
-            TOK_GET(&tok, &macro_ptr, &tokc);
-            if (tok == TOK_LINENUM) {
-                file->line_num = tokc.i;
-                goto redo;
-            }
-        }
-    } else {
-        next_nomacro1();
-    }
-    //printf("token = %s\n", get_tok_str(tok, &tokc));
-}
-
-ST_FUNC void next_nomacro(void)
-{
-    do {
-        next_nomacro_spc();
-    } while (tok < 256 && (isidnum_table[tok - CH_EOF] & IS_SPC));
-}
- 
-
-static void macro_subst(
-    TokenString *tok_str,
-    Sym **nested_list,
-    const int *macro_str
-    );
-
-/* substitute arguments in replacement lists in macro_str by the values in
-   args (field d) and return allocated string */
-static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args)
-{
-    int t, t0, t1, spc;
-    const int *st;
-    Sym *s;
-    CValue cval;
-    TokenString str;
-    CString cstr;
-
-    tok_str_new(&str);
-    t0 = t1 = 0;
-    while(1) {
-        TOK_GET(&t, &macro_str, &cval);
-        if (!t)
-            break;
-        if (t == '#') {
-            /* stringize */
-            TOK_GET(&t, &macro_str, &cval);
-            if (!t)
-                goto bad_stringy;
-            s = sym_find2(args, t);
-            if (s) {
-                cstr_new(&cstr);
-                cstr_ccat(&cstr, '\"');
-                st = s->d;
-                spc = 0;
-                while (*st >= 0) {
-                    TOK_GET(&t, &st, &cval);
-                    if (t != TOK_PLCHLDR
-                     && t != TOK_NOSUBST
-                     && 0 == check_space(t, &spc)) {
-                        const char *s = get_tok_str(t, &cval);
-                        while (*s) {
-                            if (t == TOK_PPSTR && *s != '\'')
-                                add_char(&cstr, *s);
-                            else
-                                cstr_ccat(&cstr, *s);
-                            ++s;
-                        }
-                    }
-                }
-                cstr.size -= spc;
-                cstr_ccat(&cstr, '\"');
-                cstr_ccat(&cstr, '\0');
-#ifdef PP_DEBUG
-                printf("\nstringize: <%s>\n", (char *)cstr.data);
-#endif
-                /* add string */
-                cval.str.size = cstr.size;
-                cval.str.data = cstr.data;
-                tok_str_add2(&str, TOK_PPSTR, &cval);
-                cstr_free(&cstr);
-            } else {
-        bad_stringy:
-                expect("macro parameter after '#'");
-            }
-        } else if (t >= TOK_IDENT) {
-            s = sym_find2(args, t);
-            if (s) {
-                int l0 = str.len;
-                st = s->d;
-                /* if '##' is present before or after, no arg substitution */
-                if (*macro_str == TOK_PPJOIN || t1 == TOK_PPJOIN) {
-                    /* special case for var arg macros : ## eats the ','
-                       if empty VA_ARGS variable. */
-                    if (t1 == TOK_PPJOIN && t0 == ',' && gnu_ext && s->type.t) {
-                        if (*st <= 0) {
-                            /* suppress ',' '##' */
-                            str.len -= 2;
-                        } else {
-                            /* suppress '##' and add variable */
-                            str.len--;
-                            goto add_var;
-                        }
-                    }
-                } else {
-            add_var:
-		    if (!s->next) {
-			/* Expand arguments tokens and store them.  In most
-			   cases we could also re-expand each argument if
-			   used multiple times, but not if the argument
-			   contains the __COUNTER__ macro.  */
-			TokenString str2;
-			sym_push2(&s->next, s->v, s->type.t, 0);
-			tok_str_new(&str2);
-			macro_subst(&str2, nested_list, st);
-			tok_str_add(&str2, 0);
-			s->next->d = str2.str;
-		    }
-		    st = s->next->d;
-                }
-                for(;;) {
-                    int t2;
-                    TOK_GET(&t2, &st, &cval);
-                    if (t2 <= 0)
-                        break;
-                    tok_str_add2(&str, t2, &cval);
-                }
-                if (str.len == l0) /* expanded to empty string */
-                    tok_str_add(&str, TOK_PLCHLDR);
-            } else {
-                tok_str_add(&str, t);
-            }
-        } else {
-            tok_str_add2(&str, t, &cval);
-        }
-        t0 = t1, t1 = t;
-    }
-    tok_str_add(&str, 0);
-    return str.str;
-}
-
-static char const ab_month_name[12][4] =
-{
-    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-};
-
-static int paste_tokens(int t1, CValue *v1, int t2, CValue *v2)
-{
-    CString cstr;
-    int n, ret = 1;
-
-    cstr_new(&cstr);
-    if (t1 != TOK_PLCHLDR)
-        cstr_cat(&cstr, get_tok_str(t1, v1), -1);
-    n = cstr.size;
-    if (t2 != TOK_PLCHLDR)
-        cstr_cat(&cstr, get_tok_str(t2, v2), -1);
-    cstr_ccat(&cstr, '\0');
-
-    tcc_open_bf(tcc_state, ":paste:", cstr.size);
-    memcpy(file->buffer, cstr.data, cstr.size);
-    tok_flags = 0;
-    for (;;) {
-        next_nomacro1();
-        if (0 == *file->buf_ptr)
-            break;
-        if (is_space(tok))
-            continue;
-        tcc_warning("pasting \"%.*s\" and \"%s\" does not give a valid"
-            " preprocessing token", n, cstr.data, (char*)cstr.data + n);
-        ret = 0;
-        break;
-    }
-    tcc_close();
-    //printf("paste <%s>\n", (char*)cstr.data);
-    cstr_free(&cstr);
-    return ret;
-}
-
-/* handle the '##' operator. Return NULL if no '##' seen. Otherwise
-   return the resulting string (which must be freed). */
-static inline int *macro_twosharps(const int *ptr0)
-{
-    int t;
-    CValue cval;
-    TokenString macro_str1;
-    int start_of_nosubsts = -1;
-    const int *ptr;
-
-    /* we search the first '##' */
-    for (ptr = ptr0;;) {
-        TOK_GET(&t, &ptr, &cval);
-        if (t == TOK_PPJOIN)
-            break;
-        if (t == 0)
-            return NULL;
-    }
-
-    tok_str_new(&macro_str1);
-
-    //tok_print(" $$$", ptr0);
-    for (ptr = ptr0;;) {
-        TOK_GET(&t, &ptr, &cval);
-        if (t == 0)
-            break;
-        if (t == TOK_PPJOIN)
-            continue;
-        while (*ptr == TOK_PPJOIN) {
-            int t1; CValue cv1;
-            /* given 'a##b', remove nosubsts preceding 'a' */
-            if (start_of_nosubsts >= 0)
-                macro_str1.len = start_of_nosubsts;
-            /* given 'a##b', remove nosubsts preceding 'b' */
-            while ((t1 = *++ptr) == TOK_NOSUBST)
-                ;
-            if (t1 && t1 != TOK_PPJOIN) {
-                TOK_GET(&t1, &ptr, &cv1);
-                if (t != TOK_PLCHLDR || t1 != TOK_PLCHLDR) {
-                    if (paste_tokens(t, &cval, t1, &cv1)) {
-                        t = tok, cval = tokc;
-                    } else {
-                        tok_str_add2(&macro_str1, t, &cval);
-                        t = t1, cval = cv1;
-                    }
-                }
-            }
-        }
-        if (t == TOK_NOSUBST) {
-            if (start_of_nosubsts < 0)
-                start_of_nosubsts = macro_str1.len;
-        } else {
-            start_of_nosubsts = -1;
-        }
-        tok_str_add2(&macro_str1, t, &cval);
-    }
-    tok_str_add(&macro_str1, 0);
-    //tok_print(" ###", macro_str1.str);
-    return macro_str1.str;
-}
-
-/* peek or read [ws_str == NULL] next token from function macro call,
-   walking up macro levels up to the file if necessary */
-static int next_argstream(Sym **nested_list, TokenString *ws_str)
-{
-    int t;
-    const int *p;
-    Sym *sa;
-
-    for (;;) {
-        if (macro_ptr) {
-            p = macro_ptr, t = *p;
-            if (ws_str) {
-                while (is_space(t) || TOK_LINEFEED == t || TOK_PLCHLDR == t)
-                    tok_str_add(ws_str, t), t = *++p;
-            }
-            if (t == 0) {
-                end_macro();
-                /* also, end of scope for nested defined symbol */
-                sa = *nested_list;
-                while (sa && sa->v == 0)
-                    sa = sa->prev;
-                if (sa)
-                    sa->v = 0;
-                continue;
-            }
-        } else {
-            ch = handle_eob();
-            if (ws_str) {
-                while (is_space(ch) || ch == '\n' || ch == '/') {
-                    if (ch == '/') {
-                        int c;
-                        uint8_t *p = file->buf_ptr;
-                        PEEKC(c, p);
-                        if (c == '*') {
-                            p = parse_comment(p);
-                            file->buf_ptr = p - 1;
-                        } else if (c == '/') {
-                            p = parse_line_comment(p);
-                            file->buf_ptr = p - 1;
-                        } else
-                            break;
-                        ch = ' ';
-                    }
-                    if (ch == '\n')
-                        file->line_num++;
-                    if (!(ch == '\f' || ch == '\v' || ch == '\r'))
-                        tok_str_add(ws_str, ch);
-                    cinp();
-                }
-            }
-            t = ch;
-        }
-
-        if (ws_str)
-            return t;
-        next_nomacro_spc();
-        return tok;
-    }
-}
-
-/* do macro substitution of current token with macro 's' and add
-   result to (tok_str,tok_len). 'nested_list' is the list of all
-   macros we got inside to avoid recursing. Return non zero if no
-   substitution needs to be done */
-static int macro_subst_tok(
-    TokenString *tok_str,
-    Sym **nested_list,
-    Sym *s)
-{
-    Sym *args, *sa, *sa1;
-    int parlevel, t, t1, spc;
-    TokenString str;
-    char *cstrval;
-    CValue cval;
-    CString cstr;
-    char buf[32];
-
-    /* if symbol is a macro, prepare substitution */
-    /* special macros */
-    if (tok == TOK___LINE__ || tok == TOK___COUNTER__) {
-        t = tok == TOK___LINE__ ? file->line_num : pp_counter++;
-        snprintf(buf, sizeof(buf), "%d", t);
-        cstrval = buf;
-        t1 = TOK_PPNUM;
-        goto add_cstr1;
-    } else if (tok == TOK___FILE__) {
-        cstrval = file->filename;
-        goto add_cstr;
-    } else if (tok == TOK___DATE__ || tok == TOK___TIME__) {
-        time_t ti;
-        struct tm *tm;
-
-        time(&ti);
-        tm = localtime(&ti);
-        if (tok == TOK___DATE__) {
-            snprintf(buf, sizeof(buf), "%s %2d %d", 
-                     ab_month_name[tm->tm_mon], tm->tm_mday, tm->tm_year + 1900);
-        } else {
-            snprintf(buf, sizeof(buf), "%02d:%02d:%02d", 
-                     tm->tm_hour, tm->tm_min, tm->tm_sec);
-        }
-        cstrval = buf;
-    add_cstr:
-        t1 = TOK_STR;
-    add_cstr1:
-        cstr_new(&cstr);
-        cstr_cat(&cstr, cstrval, 0);
-        cval.str.size = cstr.size;
-        cval.str.data = cstr.data;
-        tok_str_add2(tok_str, t1, &cval);
-        cstr_free(&cstr);
-    } else if (s->d) {
-        int saved_parse_flags = parse_flags;
-	int *joined_str = NULL;
-        int *mstr = s->d;
-
-        if (s->type.t == MACRO_FUNC) {
-            /* whitespace between macro name and argument list */
-            TokenString ws_str;
-            tok_str_new(&ws_str);
-
-            spc = 0;
-            parse_flags |= PARSE_FLAG_SPACES | PARSE_FLAG_LINEFEED
-                | PARSE_FLAG_ACCEPT_STRAYS;
-
-            /* get next token from argument stream */
-            t = next_argstream(nested_list, &ws_str);
-            if (t != '(') {
-                /* not a macro substitution after all, restore the
-                 * macro token plus all whitespace we've read.
-                 * whitespace is intentionally not merged to preserve
-                 * newlines. */
-                parse_flags = saved_parse_flags;
-                tok_str_add(tok_str, tok);
-                if (parse_flags & PARSE_FLAG_SPACES) {
-                    int i;
-                    for (i = 0; i < ws_str.len; i++)
-                        tok_str_add(tok_str, ws_str.str[i]);
-                }
-                tok_str_free_str(ws_str.str);
-                return 0;
-            } else {
-                tok_str_free_str(ws_str.str);
-            }
-	    do {
-		next_nomacro(); /* eat '(' */
-	    } while (tok == TOK_PLCHLDR);
-
-            /* argument macro */
-            args = NULL;
-            sa = s->next;
-            /* NOTE: empty args are allowed, except if no args */
-            for(;;) {
-                do {
-                    next_argstream(nested_list, NULL);
-                } while (is_space(tok) || TOK_LINEFEED == tok);
-    empty_arg:
-                /* handle '()' case */
-                if (!args && !sa && tok == ')')
-                    break;
-                if (!sa)
-                    tcc_error("macro '%s' used with too many args",
-                          get_tok_str(s->v, 0));
-                tok_str_new(&str);
-                parlevel = spc = 0;
-                /* NOTE: non zero sa->t indicates VA_ARGS */
-                while ((parlevel > 0 || 
-                        (tok != ')' && 
-                         (tok != ',' || sa->type.t)))) {
-                    if (tok == TOK_EOF || tok == 0)
-                        break;
-                    if (tok == '(')
-                        parlevel++;
-                    else if (tok == ')')
-                        parlevel--;
-                    if (tok == TOK_LINEFEED)
-                        tok = ' ';
-                    if (!check_space(tok, &spc))
-                        tok_str_add2(&str, tok, &tokc);
-                    next_argstream(nested_list, NULL);
-                }
-                if (parlevel)
-                    expect(")");
-                str.len -= spc;
-                tok_str_add(&str, -1);
-                tok_str_add(&str, 0);
-                sa1 = sym_push2(&args, sa->v & ~SYM_FIELD, sa->type.t, 0);
-                sa1->d = str.str;
-                sa = sa->next;
-                if (tok == ')') {
-                    /* special case for gcc var args: add an empty
-                       var arg argument if it is omitted */
-                    if (sa && sa->type.t && gnu_ext)
-                        goto empty_arg;
-                    break;
-                }
-                if (tok != ',')
-                    expect(",");
-            }
-            if (sa) {
-                tcc_error("macro '%s' used with too few args",
-                      get_tok_str(s->v, 0));
-            }
-
-            parse_flags = saved_parse_flags;
-
-            /* now subst each arg */
-            mstr = macro_arg_subst(nested_list, mstr, args);
-            /* free memory */
-            sa = args;
-            while (sa) {
-                sa1 = sa->prev;
-                tok_str_free_str(sa->d);
-                if (sa->next) {
-                    tok_str_free_str(sa->next->d);
-                    sym_free(sa->next);
-                }
-                sym_free(sa);
-                sa = sa1;
-            }
-        }
-
-        sym_push2(nested_list, s->v, 0, 0);
-        parse_flags = saved_parse_flags;
-        joined_str = macro_twosharps(mstr);
-        macro_subst(tok_str, nested_list, joined_str ? joined_str : mstr);
-
-        /* pop nested defined symbol */
-        sa1 = *nested_list;
-        *nested_list = sa1->prev;
-        sym_free(sa1);
-	if (joined_str)
-	    tok_str_free_str(joined_str);
-        if (mstr != s->d)
-            tok_str_free_str(mstr);
-    }
-    return 0;
-}
-
-/* do macro substitution of macro_str and add result to
-   (tok_str,tok_len). 'nested_list' is the list of all macros we got
-   inside to avoid recursing. */
-static void macro_subst(
-    TokenString *tok_str,
-    Sym **nested_list,
-    const int *macro_str
-    )
-{
-    Sym *s;
-    int t, spc, nosubst;
-    CValue cval;
-    
-    spc = nosubst = 0;
-
-    while (1) {
-        TOK_GET(&t, &macro_str, &cval);
-        if (t <= 0)
-            break;
-
-        if (t >= TOK_IDENT && 0 == nosubst) {
-            s = define_find(t);
-            if (s == NULL)
-                goto no_subst;
-
-            /* if nested substitution, do nothing */
-            if (sym_find2(*nested_list, t)) {
-                /* and mark it as TOK_NOSUBST, so it doesn't get subst'd again */
-                tok_str_add2(tok_str, TOK_NOSUBST, NULL);
-                goto no_subst;
-            }
-
-            {
-                TokenString str;
-                str.str = (int*)macro_str;
-                begin_macro(&str, 2);
-
-                tok = t;
-                macro_subst_tok(tok_str, nested_list, s);
-
-                if (str.alloc == 3) {
-                    /* already finished by reading function macro arguments */
-                    break;
-                }
-
-                macro_str = macro_ptr;
-                end_macro ();
-            }
-            if (tok_str->len)
-                spc = is_space(t = tok_str->str[tok_str->lastlen]);
-        } else {
-            if (t == '\\' && !(parse_flags & PARSE_FLAG_ACCEPT_STRAYS))
-                tcc_error("stray '\\' in program");
-no_subst:
-            if (!check_space(t, &spc))
-                tok_str_add2(tok_str, t, &cval);
-
-            if (nosubst) {
-                if (nosubst > 1 && (spc || (++nosubst == 3 && t == '(')))
-                    continue;
-                nosubst = 0;
-            }
-            if (t == TOK_NOSUBST)
-                nosubst = 1;
-        }
-        /* GCC supports 'defined' as result of a macro substitution */
-        if (t == TOK_DEFINED && pp_expr)
-            nosubst = 2;
-    }
-}
-
-/* return next token with macro substitution */
-ST_FUNC void next(void)
-{
- redo:
-    if (parse_flags & PARSE_FLAG_SPACES)
-        next_nomacro_spc();
-    else
-        next_nomacro();
-
-    if (macro_ptr) {
-        if (tok == TOK_NOSUBST || tok == TOK_PLCHLDR) {
-        /* discard preprocessor markers */
-            goto redo;
-        } else if (tok == 0) {
-            /* end of macro or unget token string */
-            end_macro();
-            goto redo;
-        }
-    } else if (tok >= TOK_IDENT && (parse_flags & PARSE_FLAG_PREPROCESS)) {
-        Sym *s;
-        /* if reading from file, try to substitute macros */
-        s = define_find(tok);
-        if (s) {
-            Sym *nested_list = NULL;
-            tokstr_buf.len = 0;
-            macro_subst_tok(&tokstr_buf, &nested_list, s);
-            tok_str_add(&tokstr_buf, 0);
-            begin_macro(&tokstr_buf, 2);
-            goto redo;
-        }
-    }
-    /* convert preprocessor tokens into C tokens */
-    if (tok == TOK_PPNUM) {
-        if  (parse_flags & PARSE_FLAG_TOK_NUM)
-            parse_number((char *)tokc.str.data);
-    } else if (tok == TOK_PPSTR) {
-        if (parse_flags & PARSE_FLAG_TOK_STR)
-            parse_string((char *)tokc.str.data, tokc.str.size - 1);
-    }
-}
-
-/* push back current token and set current token to 'last_tok'. Only
-   identifier case handled for labels. */
-ST_INLN void unget_tok(int last_tok)
-{
-
-    TokenString *str = tok_str_alloc();
-    tok_str_add2(str, tok, &tokc);
-    tok_str_add(str, 0);
-    begin_macro(str, 1);
-    tok = last_tok;
-}
-
-ST_FUNC void preprocess_start(TCCState *s1, int is_asm)
-{
-    CString cstr;
-    int i;
-
-    s1->include_stack_ptr = s1->include_stack;
-    s1->ifdef_stack_ptr = s1->ifdef_stack;
-    file->ifdef_stack_ptr = s1->ifdef_stack_ptr;
-    pp_expr = 0;
-    pp_counter = 0;
-    pp_debug_tok = pp_debug_symv = 0;
-    pp_once++;
-    pvtop = vtop = vstack - 1;
-    s1->pack_stack[0] = 0;
-    s1->pack_stack_ptr = s1->pack_stack;
-
-    set_idnum('$', s1->dollars_in_identifiers ? IS_ID : 0);
-    set_idnum('.', is_asm ? IS_ID : 0);
-
-    cstr_new(&cstr);
-    cstr_cat(&cstr, "\"", -1);
-    cstr_cat(&cstr, file->filename, -1);
-    cstr_cat(&cstr, "\"", 0);
-    tcc_define_symbol(s1, "__BASE_FILE__", cstr.data);
-
-    cstr_reset(&cstr);
-    for (i = 0; i < s1->nb_cmd_include_files; i++) {
-        cstr_cat(&cstr, "#include \"", -1);
-        cstr_cat(&cstr, s1->cmd_include_files[i], -1);
-        cstr_cat(&cstr, "\"\n", -1);
-    }
-    if (cstr.size) {
-        *s1->include_stack_ptr++ = file;
-	tcc_open_bf(s1, "<command line>", cstr.size);
-	memcpy(file->buffer, cstr.data, cstr.size);
-    }
-    cstr_free(&cstr);
-
-    if (is_asm)
-        tcc_define_symbol(s1, "__ASSEMBLER__", NULL);
-
-    parse_flags = is_asm ? PARSE_FLAG_ASM_FILE : 0;
-    tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
-}
-
-/* cleanup from error/setjmp */
-ST_FUNC void preprocess_end(TCCState *s1)
-{
-    while (macro_stack)
-        end_macro();
-    macro_ptr = NULL;
-}
-
-ST_FUNC void tccpp_new(TCCState *s)
-{
-    int i, c;
-    const char *p, *r;
-
-    /* might be used in error() before preprocess_start() */
-    s->include_stack_ptr = s->include_stack;
-    s->ppfp = stdout;
-
-    /* init isid table */
-    for(i = CH_EOF; i<128; i++)
-        set_idnum(i,
-            is_space(i) ? IS_SPC
-            : isid(i) ? IS_ID
-            : isnum(i) ? IS_NUM
-            : 0);
-
-    for(i = 128; i<256; i++)
-        set_idnum(i, IS_ID);
-
-    /* init allocators */
-    tal_new(&toksym_alloc, TOKSYM_TAL_LIMIT, TOKSYM_TAL_SIZE);
-    tal_new(&tokstr_alloc, TOKSTR_TAL_LIMIT, TOKSTR_TAL_SIZE);
-    tal_new(&cstr_alloc, CSTR_TAL_LIMIT, CSTR_TAL_SIZE);
-
-    memset(hash_ident, 0, TOK_HASH_SIZE * sizeof(TokenSym *));
-    cstr_new(&cstr_buf);
-    cstr_realloc(&cstr_buf, STRING_MAX_SIZE);
-    tok_str_new(&tokstr_buf);
-    tok_str_realloc(&tokstr_buf, TOKSTR_MAX_SIZE);
-    
-    tok_ident = TOK_IDENT;
-    p = tcc_keywords;
-    while (*p) {
-        r = p;
-        for(;;) {
-            c = *r++;
-            if (c == '\0')
-                break;
-        }
-        tok_alloc(p, r - p - 1);
-        p = r;
-    }
-}
-
-ST_FUNC void tccpp_delete(TCCState *s)
-{
-    int i, n;
-
-    /* free -D and compiler defines */
-    free_defines(NULL);
-
-    /* free tokens */
-    n = tok_ident - TOK_IDENT;
-    for(i = 0; i < n; i++)
-        tal_free(toksym_alloc, table_ident[i]);
-    tcc_free(table_ident);
-    table_ident = NULL;
-
-    /* free static buffers */
-    cstr_free(&tokcstr);
-    cstr_free(&cstr_buf);
-    cstr_free(&macro_equal_buf);
-    tok_str_free_str(tokstr_buf.str);
-
-    /* free allocators */
-    tal_delete(toksym_alloc);
-    toksym_alloc = NULL;
-    tal_delete(tokstr_alloc);
-    tokstr_alloc = NULL;
-    tal_delete(cstr_alloc);
-    cstr_alloc = NULL;
-}
-
-/* ------------------------------------------------------------------------- */
-/* tcc -E [-P[1]] [-dD} support */
-
-static void tok_print(const char *msg, const int *str)
-{
-    FILE *fp;
-    int t, s = 0;
-    CValue cval;
-
-    fp = tcc_state->ppfp;
-    fprintf(fp, "%s", msg);
-    while (str) {
-	TOK_GET(&t, &str, &cval);
-	if (!t)
-	    break;
-	fprintf(fp, " %s" + s, get_tok_str(t, &cval)), s = 1;
-    }
-    fprintf(fp, "\n");
-}
-
-static void pp_line(TCCState *s1, BufferedFile *f, int level)
-{
-    int d = f->line_num - f->line_ref;
-
-    if (s1->dflag & 4)
-	return;
-
-    if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_NONE) {
-        ;
-    } else if (level == 0 && f->line_ref && d < 8) {
-	while (d > 0)
-	    fputs("\n", s1->ppfp), --d;
-    } else if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_STD) {
-	fprintf(s1->ppfp, "#line %d \"%s\"\n", f->line_num, f->filename);
-    } else {
-	fprintf(s1->ppfp, "# %d \"%s\"%s\n", f->line_num, f->filename,
-	    level > 0 ? " 1" : level < 0 ? " 2" : "");
-    }
-    f->line_ref = f->line_num;
-}
-
-static void define_print(TCCState *s1, int v)
-{
-    FILE *fp;
-    Sym *s;
-
-    s = define_find(v);
-    if (NULL == s || NULL == s->d)
-        return;
-
-    fp = s1->ppfp;
-    fprintf(fp, "#define %s", get_tok_str(v, NULL));
-    if (s->type.t == MACRO_FUNC) {
-        Sym *a = s->next;
-        fprintf(fp,"(");
-        if (a)
-            for (;;) {
-                fprintf(fp,"%s", get_tok_str(a->v & ~SYM_FIELD, NULL));
-                if (!(a = a->next))
-                    break;
-                fprintf(fp,",");
-            }
-        fprintf(fp,")");
-    }
-    tok_print("", s->d);
-}
-
-static void pp_debug_defines(TCCState *s1)
-{
-    int v, t;
-    const char *vs;
-    FILE *fp;
-
-    t = pp_debug_tok;
-    if (t == 0)
-        return;
-
-    file->line_num--;
-    pp_line(s1, file, 0);
-    file->line_ref = ++file->line_num;
-
-    fp = s1->ppfp;
-    v = pp_debug_symv;
-    vs = get_tok_str(v, NULL);
-    if (t == TOK_DEFINE) {
-        define_print(s1, v);
-    } else if (t == TOK_UNDEF) {
-        fprintf(fp, "#undef %s\n", vs);
-    } else if (t == TOK_push_macro) {
-        fprintf(fp, "#pragma push_macro(\"%s\")\n", vs);
-    } else if (t == TOK_pop_macro) {
-        fprintf(fp, "#pragma pop_macro(\"%s\")\n", vs);
-    }
-    pp_debug_tok = 0;
-}
-
-static void pp_debug_builtins(TCCState *s1)
-{
-    int v;
-    for (v = TOK_IDENT; v < tok_ident; ++v)
-        define_print(s1, v);
-}
-
-/* Add a space between tokens a and b to avoid unwanted textual pasting */
-static int pp_need_space(int a, int b)
-{
-    return 'E' == a ? '+' == b || '-' == b
-        : '+' == a ? TOK_INC == b || '+' == b
-        : '-' == a ? TOK_DEC == b || '-' == b
-        : a >= TOK_IDENT ? b >= TOK_IDENT
-	: a == TOK_PPNUM ? b >= TOK_IDENT
-        : 0;
-}
-
-/* maybe hex like 0x1e */
-static int pp_check_he0xE(int t, const char *p)
-{
-    if (t == TOK_PPNUM && toup(strchr(p, 0)[-1]) == 'E')
-        return 'E';
-    return t;
-}
-
-/* Preprocess the current file */
-ST_FUNC int tcc_preprocess(TCCState *s1)
-{
-    BufferedFile **iptr;
-    int token_seen, spcs, level;
-    const char *p;
-    char white[400];
-
-    parse_flags = PARSE_FLAG_PREPROCESS
-                | (parse_flags & PARSE_FLAG_ASM_FILE)
-                | PARSE_FLAG_LINEFEED
-                | PARSE_FLAG_SPACES
-                | PARSE_FLAG_ACCEPT_STRAYS
-                ;
-    /* Credits to Fabrice Bellard's initial revision to demonstrate its
-       capability to compile and run itself, provided all numbers are
-       given as decimals. tcc -E -P10 will do. */
-    if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_P10)
-        parse_flags |= PARSE_FLAG_TOK_NUM, s1->Pflag = 1;
-
-#ifdef PP_BENCH
-    /* for PP benchmarks */
-    do next(); while (tok != TOK_EOF);
-    return 0;
-#endif
-
-    if (s1->dflag & 1) {
-        pp_debug_builtins(s1);
-        s1->dflag &= ~1;
-    }
-
-    token_seen = TOK_LINEFEED, spcs = 0;
-    pp_line(s1, file, 0);
-    for (;;) {
-        iptr = s1->include_stack_ptr;
-        next();
-        if (tok == TOK_EOF)
-            break;
-
-        level = s1->include_stack_ptr - iptr;
-        if (level) {
-            if (level > 0)
-                pp_line(s1, *iptr, 0);
-            pp_line(s1, file, level);
-        }
-        if (s1->dflag & 7) {
-            pp_debug_defines(s1);
-            if (s1->dflag & 4)
-                continue;
-        }
-
-        if (is_space(tok)) {
-            if (spcs < sizeof white - 1)
-                white[spcs++] = tok;
-            continue;
-        } else if (tok == TOK_LINEFEED) {
-            spcs = 0;
-            if (token_seen == TOK_LINEFEED)
-                continue;
-            ++file->line_ref;
-        } else if (token_seen == TOK_LINEFEED) {
-            pp_line(s1, file, 0);
-        } else if (spcs == 0 && pp_need_space(token_seen, tok)) {
-            white[spcs++] = ' ';
-        }
-
-        white[spcs] = 0, fputs(white, s1->ppfp), spcs = 0;
-        fputs(p = get_tok_str(tok, &tokc), s1->ppfp);
-        token_seen = pp_check_he0xE(tok, p);
-    }
-    return 0;
-}
-
-/* ------------------------------------------------------------------------- */
diff --git a/tinyc/tccrun.c b/tinyc/tccrun.c
deleted file mode 100644
index b9a052b19..000000000
--- a/tinyc/tccrun.c
+++ /dev/null
@@ -1,840 +0,0 @@
-/*
- *  TCC - Tiny C Compiler - Support for -run switch
- *
- *  Copyright (c) 2001-2004 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "tcc.h"
-
-/* only native compiler supports -run */
-#ifdef TCC_IS_NATIVE
-
-#ifndef _WIN32
-# include <sys/mman.h>
-#endif
-
-#ifdef CONFIG_TCC_BACKTRACE
-# ifndef _WIN32
-#  include <signal.h>
-#  ifndef __OpenBSD__
-#   include <sys/ucontext.h>
-#  endif
-# else
-#  define ucontext_t CONTEXT
-# endif
-ST_DATA int rt_num_callers = 6;
-ST_DATA const char **rt_bound_error_msg;
-ST_DATA void *rt_prog_main;
-static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level);
-static void rt_error(ucontext_t *uc, const char *fmt, ...);
-static void set_exception_handler(void);
-#endif
-
-static void set_pages_executable(void *ptr, unsigned long length);
-static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff);
-
-#ifdef _WIN64
-static void *win64_add_function_table(TCCState *s1);
-static void win64_del_function_table(void *);
-#endif
-
-/* ------------------------------------------------------------- */
-/* Do all relocations (needed before using tcc_get_symbol())
-   Returns -1 on error. */
-
-LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr)
-{
-    int size;
-    addr_t ptr_diff = 0;
-
-    if (TCC_RELOCATE_AUTO != ptr)
-        return tcc_relocate_ex(s1, ptr, 0);
-
-    size = tcc_relocate_ex(s1, NULL, 0);
-    if (size < 0)
-        return -1;
-
-#ifdef HAVE_SELINUX
-{
-    /* Using mmap instead of malloc */
-    void *prx;
-    char tmpfname[] = "/tmp/.tccrunXXXXXX";
-    int fd = mkstemp(tmpfname);
-    unlink(tmpfname);
-    ftruncate(fd, size);
-
-    ptr = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
-    prx = mmap (NULL, size, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0);
-    if (ptr == MAP_FAILED || prx == MAP_FAILED)
-	tcc_error("tccrun: could not map memory");
-    dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)size);
-    dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, prx);
-    ptr_diff = (char*)prx - (char*)ptr;
-}
-#else
-    ptr = tcc_malloc(size);
-#endif
-    tcc_relocate_ex(s1, ptr, ptr_diff); /* no more errors expected */
-    dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, ptr);
-    return 0;
-}
-
-ST_FUNC void tcc_run_free(TCCState *s1)
-{
-    int i;
-
-    for (i = 0; i < s1->nb_runtime_mem; ++i) {
-#ifdef HAVE_SELINUX
-        unsigned size = (unsigned)(addr_t)s1->runtime_mem[i++];
-        munmap(s1->runtime_mem[i++], size);
-        munmap(s1->runtime_mem[i], size);
-#else
-#ifdef _WIN64
-        win64_del_function_table(*(void**)s1->runtime_mem[i]);
-#endif
-        tcc_free(s1->runtime_mem[i]);
-#endif
-    }
-    tcc_free(s1->runtime_mem);
-}
-
-/* launch the compiled program with the given arguments */
-LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
-{
-    int (*prog_main)(int, char **);
-
-    s1->runtime_main = "main";
-    if ((s1->dflag & 16) && !find_elf_sym(s1->symtab, s1->runtime_main))
-        return 0;
-    if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0)
-        return -1;
-    prog_main = tcc_get_symbol_err(s1, s1->runtime_main);
-
-#ifdef CONFIG_TCC_BACKTRACE
-    if (s1->do_debug) {
-        set_exception_handler();
-        rt_prog_main = prog_main;
-    }
-#endif
-
-    errno = 0; /* clean errno value */
-
-#ifdef CONFIG_TCC_BCHECK
-    if (s1->do_bounds_check) {
-        void (*bound_init)(void);
-        void (*bound_exit)(void);
-        void (*bound_new_region)(void *p, addr_t size);
-        int  (*bound_delete_region)(void *p);
-        int i, ret;
-
-        /* set error function */
-        rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg");
-        /* XXX: use .init section so that it also work in binary ? */
-        bound_init = tcc_get_symbol_err(s1, "__bound_init");
-        bound_exit = tcc_get_symbol_err(s1, "__bound_exit");
-        bound_new_region = tcc_get_symbol_err(s1, "__bound_new_region");
-        bound_delete_region = tcc_get_symbol_err(s1, "__bound_delete_region");
-
-        bound_init();
-        /* mark argv area as valid */
-        bound_new_region(argv, argc*sizeof(argv[0]));
-        for (i=0; i<argc; ++i)
-            bound_new_region(argv[i], strlen(argv[i]) + 1);
-
-        ret = (*prog_main)(argc, argv);
-
-        /* unmark argv area */
-        for (i=0; i<argc; ++i)
-            bound_delete_region(argv[i]);
-        bound_delete_region(argv);
-        bound_exit();
-        return ret;
-    }
-#endif
-    return (*prog_main)(argc, argv);
-}
-
-#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
- #define RUN_SECTION_ALIGNMENT 63
-#else
- #define RUN_SECTION_ALIGNMENT 15
-#endif
-
-/* relocate code. Return -1 on error, required size if ptr is NULL,
-   otherwise copy code into buffer passed by the caller */
-static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff)
-{
-    Section *s;
-    unsigned offset, length, fill, i, k;
-    addr_t mem;
-
-    if (NULL == ptr) {
-        s1->nb_errors = 0;
-#ifdef TCC_TARGET_PE
-        pe_output_file(s1, NULL);
-#else
-        tcc_add_runtime(s1);
-        relocate_common_syms();
-        tcc_add_linker_symbols(s1);
-        build_got_entries(s1);
-#endif
-        if (s1->nb_errors)
-            return -1;
-    }
-
-    offset = 0, mem = (addr_t)ptr;
-    fill = -mem & RUN_SECTION_ALIGNMENT;
-#ifdef _WIN64
-    offset += sizeof (void*);
-#endif
-    for (k = 0; k < 2; ++k) {
-        for(i = 1; i < s1->nb_sections; i++) {
-            s = s1->sections[i];
-            if (0 == (s->sh_flags & SHF_ALLOC))
-                continue;
-            if (k != !(s->sh_flags & SHF_EXECINSTR))
-                continue;
-            offset += fill;
-            if (!mem)
-                s->sh_addr = 0;
-            else if (s->sh_flags & SHF_EXECINSTR)
-                s->sh_addr = mem + offset + ptr_diff;
-            else
-                s->sh_addr = mem + offset;
-#if 0
-            if (mem)
-                printf("%-16s +%02lx %p %04x\n",
-                    s->name, fill, (void*)s->sh_addr, (unsigned)s->data_offset);
-#endif
-            offset += s->data_offset;
-            fill = -(mem + offset) & 15;
-        }
-#if RUN_SECTION_ALIGNMENT > 15
-        /* To avoid that x86 processors would reload cached instructions each time
-           when data is written in the near, we need to make sure that code and data
-           do not share the same 64 byte unit */
-        fill = -(mem + offset) & RUN_SECTION_ALIGNMENT;
-#endif
-    }
-
-    /* relocate symbols */
-    relocate_syms(s1, s1->symtab, 1);
-    if (s1->nb_errors)
-        return -1;
-
-    if (0 == mem)
-        return offset + RUN_SECTION_ALIGNMENT;
-
-    /* relocate each section */
-    for(i = 1; i < s1->nb_sections; i++) {
-        s = s1->sections[i];
-        if (s->reloc)
-            relocate_section(s1, s);
-    }
-    relocate_plt(s1);
-
-#ifdef _WIN64
-    *(void**)ptr = win64_add_function_table(s1);
-#endif
-
-    for(i = 1; i < s1->nb_sections; i++) {
-        s = s1->sections[i];
-        if (0 == (s->sh_flags & SHF_ALLOC))
-            continue;
-        length = s->data_offset;
-        ptr = (void*)s->sh_addr;
-        if (s->sh_flags & SHF_EXECINSTR)
-            ptr = (char*)ptr - ptr_diff;
-        if (NULL == s->data || s->sh_type == SHT_NOBITS)
-            memset(ptr, 0, length);
-        else
-            memcpy(ptr, s->data, length);
-        /* mark executable sections as executable in memory */
-        if (s->sh_flags & SHF_EXECINSTR)
-            set_pages_executable((char*)ptr + ptr_diff, length);
-    }
-    return 0;
-}
-
-/* ------------------------------------------------------------- */
-/* allow to run code in memory */
-
-static void set_pages_executable(void *ptr, unsigned long length)
-{
-#ifdef _WIN32
-    unsigned long old_protect;
-    VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
-#else
-    void __clear_cache(void *beginning, void *end);
-# ifndef HAVE_SELINUX
-    addr_t start, end;
-#  ifndef PAGESIZE
-#   define PAGESIZE 4096
-#  endif
-    start = (addr_t)ptr & ~(PAGESIZE - 1);
-    end = (addr_t)ptr + length;
-    end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
-    if (mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC))
-        tcc_error("mprotect failed: did you mean to configure --with-selinux?");
-# endif
-# if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
-    __clear_cache(ptr, (char *)ptr + length);
-# endif
-#endif
-}
-
-#ifdef _WIN64
-static void *win64_add_function_table(TCCState *s1)
-{
-    void *p = NULL;
-    if (s1->uw_pdata) {
-        p = (void*)s1->uw_pdata->sh_addr;
-        RtlAddFunctionTable(
-            (RUNTIME_FUNCTION*)p,
-            s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
-            text_section->sh_addr
-            );
-        s1->uw_pdata = NULL;
-    }
-    return p;;
-}
-
-static void win64_del_function_table(void *p)
-{
-    if (p) {
-        RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
-    }
-}
-#endif
-
-/* ------------------------------------------------------------- */
-#ifdef CONFIG_TCC_BACKTRACE
-
-ST_FUNC void tcc_set_num_callers(int n)
-{
-    rt_num_callers = n;
-}
-
-/* print the position in the source file of PC value 'pc' by reading
-   the stabs debug information */
-static addr_t rt_printline(addr_t wanted_pc, const char *msg)
-{
-    char func_name[128], last_func_name[128];
-    addr_t func_addr, last_pc, pc;
-    const char *incl_files[INCLUDE_STACK_SIZE];
-    int incl_index, len, last_line_num, i;
-    const char *str, *p;
-
-    Stab_Sym *stab_sym = NULL, *stab_sym_end, *sym;
-    int stab_len = 0;
-    char *stab_str = NULL;
-
-    if (stab_section) {
-        stab_len = stab_section->data_offset;
-        stab_sym = (Stab_Sym *)stab_section->data;
-        stab_str = (char *) stabstr_section->data;
-    }
-
-    func_name[0] = '\0';
-    func_addr = 0;
-    incl_index = 0;
-    last_func_name[0] = '\0';
-    last_pc = (addr_t)-1;
-    last_line_num = 1;
-
-    if (!stab_sym)
-        goto no_stabs;
-
-    stab_sym_end = (Stab_Sym*)((char*)stab_sym + stab_len);
-    for (sym = stab_sym + 1; sym < stab_sym_end; ++sym) {
-        switch(sym->n_type) {
-            /* function start or end */
-        case N_FUN:
-            if (sym->n_strx == 0) {
-                /* we test if between last line and end of function */
-                pc = sym->n_value + func_addr;
-                if (wanted_pc >= last_pc && wanted_pc < pc)
-                    goto found;
-                func_name[0] = '\0';
-                func_addr = 0;
-            } else {
-                str = stab_str + sym->n_strx;
-                p = strchr(str, ':');
-                if (!p) {
-                    pstrcpy(func_name, sizeof(func_name), str);
-                } else {
-                    len = p - str;
-                    if (len > sizeof(func_name) - 1)
-                        len = sizeof(func_name) - 1;
-                    memcpy(func_name, str, len);
-                    func_name[len] = '\0';
-                }
-                func_addr = sym->n_value;
-            }
-            break;
-            /* line number info */
-        case N_SLINE:
-            pc = sym->n_value + func_addr;
-            if (wanted_pc >= last_pc && wanted_pc < pc)
-                goto found;
-            last_pc = pc;
-            last_line_num = sym->n_desc;
-            /* XXX: slow! */
-            strcpy(last_func_name, func_name);
-            break;
-            /* include files */
-        case N_BINCL:
-            str = stab_str + sym->n_strx;
-        add_incl:
-            if (incl_index < INCLUDE_STACK_SIZE) {
-                incl_files[incl_index++] = str;
-            }
-            break;
-        case N_EINCL:
-            if (incl_index > 1)
-                incl_index--;
-            break;
-        case N_SO:
-            if (sym->n_strx == 0) {
-                incl_index = 0; /* end of translation unit */
-            } else {
-                str = stab_str + sym->n_strx;
-                /* do not add path */
-                len = strlen(str);
-                if (len > 0 && str[len - 1] != '/')
-                    goto add_incl;
-            }
-            break;
-        }
-    }
-
-no_stabs:
-    /* second pass: we try symtab symbols (no line number info) */
-    incl_index = 0;
-    if (symtab_section)
-    {
-        ElfW(Sym) *sym, *sym_end;
-        int type;
-
-        sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
-        for(sym = (ElfW(Sym) *)symtab_section->data + 1;
-            sym < sym_end;
-            sym++) {
-            type = ELFW(ST_TYPE)(sym->st_info);
-            if (type == STT_FUNC || type == STT_GNU_IFUNC) {
-                if (wanted_pc >= sym->st_value &&
-                    wanted_pc < sym->st_value + sym->st_size) {
-                    pstrcpy(last_func_name, sizeof(last_func_name),
-                            (char *) strtab_section->data + sym->st_name);
-                    func_addr = sym->st_value;
-                    goto found;
-                }
-            }
-        }
-    }
-    /* did not find any info: */
-    fprintf(stderr, "%s %p ???\n", msg, (void*)wanted_pc);
-    fflush(stderr);
-    return 0;
- found:
-    i = incl_index;
-    if (i > 0)
-        fprintf(stderr, "%s:%d: ", incl_files[--i], last_line_num);
-    fprintf(stderr, "%s %p", msg, (void*)wanted_pc);
-    if (last_func_name[0] != '\0')
-        fprintf(stderr, " %s()", last_func_name);
-    if (--i >= 0) {
-        fprintf(stderr, " (included from ");
-        for (;;) {
-            fprintf(stderr, "%s", incl_files[i]);
-            if (--i < 0)
-                break;
-            fprintf(stderr, ", ");
-        }
-        fprintf(stderr, ")");
-    }
-    fprintf(stderr, "\n");
-    fflush(stderr);
-    return func_addr;
-}
-
-/* emit a run time error at position 'pc' */
-static void rt_error(ucontext_t *uc, const char *fmt, ...)
-{
-    va_list ap;
-    addr_t pc;
-    int i;
-
-    fprintf(stderr, "Runtime error: ");
-    va_start(ap, fmt);
-    vfprintf(stderr, fmt, ap);
-    va_end(ap);
-    fprintf(stderr, "\n");
-
-    for(i=0;i<rt_num_callers;i++) {
-        if (rt_get_caller_pc(&pc, uc, i) < 0)
-            break;
-        pc = rt_printline(pc, i ? "by" : "at");
-        if (pc == (addr_t)rt_prog_main && pc)
-            break;
-    }
-}
-
-/* ------------------------------------------------------------- */
-#ifndef _WIN32
-
-/* signal handler for fatal errors */
-static void sig_error(int signum, siginfo_t *siginf, void *puc)
-{
-    ucontext_t *uc = puc;
-
-    switch(signum) {
-    case SIGFPE:
-        switch(siginf->si_code) {
-        case FPE_INTDIV:
-        case FPE_FLTDIV:
-            rt_error(uc, "division by zero");
-            break;
-        default:
-            rt_error(uc, "floating point exception");
-            break;
-        }
-        break;
-    case SIGBUS:
-    case SIGSEGV:
-        if (rt_bound_error_msg && *rt_bound_error_msg)
-            rt_error(uc, *rt_bound_error_msg);
-        else
-            rt_error(uc, "dereferencing invalid pointer");
-        break;
-    case SIGILL:
-        rt_error(uc, "illegal instruction");
-        break;
-    case SIGABRT:
-        rt_error(uc, "abort() called");
-        break;
-    default:
-        rt_error(uc, "caught signal %d", signum);
-        break;
-    }
-    exit(255);
-}
-
-#ifndef SA_SIGINFO
-# define SA_SIGINFO 0x00000004u
-#endif
-
-/* Generate a stack backtrace when a CPU exception occurs. */
-static void set_exception_handler(void)
-{
-    struct sigaction sigact;
-    /* install TCC signal handlers to print debug info on fatal
-       runtime errors */
-    sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
-    sigact.sa_sigaction = sig_error;
-    sigemptyset(&sigact.sa_mask);
-    sigaction(SIGFPE, &sigact, NULL);
-    sigaction(SIGILL, &sigact, NULL);
-    sigaction(SIGSEGV, &sigact, NULL);
-    sigaction(SIGBUS, &sigact, NULL);
-    sigaction(SIGABRT, &sigact, NULL);
-}
-
-/* ------------------------------------------------------------- */
-#ifdef __i386__
-
-/* fix for glibc 2.1 */
-#ifndef REG_EIP
-#define REG_EIP EIP
-#define REG_EBP EBP
-#endif
-
-/* return the PC at frame level 'level'. Return negative if not found */
-static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
-{
-    addr_t fp;
-    int i;
-
-    if (level == 0) {
-#if defined(__APPLE__)
-        *paddr = uc->uc_mcontext->__ss.__eip;
-#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
-        *paddr = uc->uc_mcontext.mc_eip;
-#elif defined(__dietlibc__)
-        *paddr = uc->uc_mcontext.eip;
-#elif defined(__NetBSD__)
-        *paddr = uc->uc_mcontext.__gregs[_REG_EIP];
-#elif defined(__OpenBSD__)
-        *paddr = uc->sc_eip;
-#else
-        *paddr = uc->uc_mcontext.gregs[REG_EIP];
-#endif
-        return 0;
-    } else {
-#if defined(__APPLE__)
-        fp = uc->uc_mcontext->__ss.__ebp;
-#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
-        fp = uc->uc_mcontext.mc_ebp;
-#elif defined(__dietlibc__)
-        fp = uc->uc_mcontext.ebp;
-#elif defined(__NetBSD__)
-        fp = uc->uc_mcontext.__gregs[_REG_EBP];
-#elif defined(__OpenBSD__)
-        *paddr = uc->sc_ebp;
-#else
-        fp = uc->uc_mcontext.gregs[REG_EBP];
-#endif
-        for(i=1;i<level;i++) {
-            /* XXX: check address validity with program info */
-            if (fp <= 0x1000 || fp >= 0xc0000000)
-                return -1;
-            fp = ((addr_t *)fp)[0];
-        }
-        *paddr = ((addr_t *)fp)[1];
-        return 0;
-    }
-}
-
-/* ------------------------------------------------------------- */
-#elif defined(__x86_64__)
-
-/* return the PC at frame level 'level'. Return negative if not found */
-static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
-{
-    addr_t fp;
-    int i;
-
-    if (level == 0) {
-        /* XXX: only support linux */
-#if defined(__APPLE__)
-        *paddr = uc->uc_mcontext->__ss.__rip;
-#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
-        *paddr = uc->uc_mcontext.mc_rip;
-#elif defined(__NetBSD__)
-        *paddr = uc->uc_mcontext.__gregs[_REG_RIP];
-#else
-        *paddr = uc->uc_mcontext.gregs[REG_RIP];
-#endif
-        return 0;
-    } else {
-#if defined(__APPLE__)
-        fp = uc->uc_mcontext->__ss.__rbp;
-#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
-        fp = uc->uc_mcontext.mc_rbp;
-#elif defined(__NetBSD__)
-        fp = uc->uc_mcontext.__gregs[_REG_RBP];
-#else
-        fp = uc->uc_mcontext.gregs[REG_RBP];
-#endif
-        for(i=1;i<level;i++) {
-            /* XXX: check address validity with program info */
-            if (fp <= 0x1000)
-                return -1;
-            fp = ((addr_t *)fp)[0];
-        }
-        *paddr = ((addr_t *)fp)[1];
-        return 0;
-    }
-}
-
-/* ------------------------------------------------------------- */
-#elif defined(__arm__)
-
-/* return the PC at frame level 'level'. Return negative if not found */
-static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
-{
-    addr_t fp, sp;
-    int i;
-
-    if (level == 0) {
-        /* XXX: only supports linux */
-#if defined(__linux__)
-        *paddr = uc->uc_mcontext.arm_pc;
-#else
-        return -1;
-#endif
-        return 0;
-    } else {
-#if defined(__linux__)
-        fp = uc->uc_mcontext.arm_fp;
-        sp = uc->uc_mcontext.arm_sp;
-        if (sp < 0x1000)
-            sp = 0x1000;
-#else
-        return -1;
-#endif
-        /* XXX: specific to tinycc stack frames */
-        if (fp < sp + 12 || fp & 3)
-            return -1;
-        for(i = 1; i < level; i++) {
-            sp = ((addr_t *)fp)[-2];
-            if (sp < fp || sp - fp > 16 || sp & 3)
-                return -1;
-            fp = ((addr_t *)fp)[-3];
-            if (fp <= sp || fp - sp < 12 || fp & 3)
-                return -1;
-        }
-        /* XXX: check address validity with program info */
-        *paddr = ((addr_t *)fp)[-1];
-        return 0;
-    }
-}
-
-/* ------------------------------------------------------------- */
-#elif defined(__aarch64__)
-
-static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
-{
-    if (level < 0)
-        return -1;
-    else if (level == 0) {
-        *paddr = uc->uc_mcontext.pc;
-        return 0;
-    }
-    else {
-        addr_t *fp = (addr_t *)uc->uc_mcontext.regs[29];
-        int i;
-        for (i = 1; i < level; i++)
-            fp = (addr_t *)fp[0];
-        *paddr = fp[1];
-        return 0;
-    }
-}
-
-/* ------------------------------------------------------------- */
-#else
-
-#warning add arch specific rt_get_caller_pc()
-static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
-{
-    return -1;
-}
-
-#endif /* !__i386__ */
-
-/* ------------------------------------------------------------- */
-#else /* WIN32 */
-
-static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
-{
-    EXCEPTION_RECORD *er = ex_info->ExceptionRecord;
-    CONTEXT *uc = ex_info->ContextRecord;
-    switch (er->ExceptionCode) {
-    case EXCEPTION_ACCESS_VIOLATION:
-        if (rt_bound_error_msg && *rt_bound_error_msg)
-            rt_error(uc, *rt_bound_error_msg);
-        else
-	    rt_error(uc, "access violation");
-        break;
-    case EXCEPTION_STACK_OVERFLOW:
-        rt_error(uc, "stack overflow");
-        break;
-    case EXCEPTION_INT_DIVIDE_BY_ZERO:
-        rt_error(uc, "division by zero");
-        break;
-    default:
-        rt_error(uc, "exception caught");
-        break;
-    }
-    return EXCEPTION_EXECUTE_HANDLER;
-}
-
-/* Generate a stack backtrace when a CPU exception occurs. */
-static void set_exception_handler(void)
-{
-    SetUnhandledExceptionFilter(cpu_exception_handler);
-}
-
-/* return the PC at frame level 'level'. Return non zero if not found */
-static int rt_get_caller_pc(addr_t *paddr, CONTEXT *uc, int level)
-{
-    addr_t fp, pc;
-    int i;
-#ifdef _WIN64
-    pc = uc->Rip;
-    fp = uc->Rbp;
-#else
-    pc = uc->Eip;
-    fp = uc->Ebp;
-#endif
-    if (level > 0) {
-        for(i=1;i<level;i++) {
-	    /* XXX: check address validity with program info */
-	    if (fp <= 0x1000 || fp >= 0xc0000000)
-		return -1;
-	    fp = ((addr_t*)fp)[0];
-	}
-        pc = ((addr_t*)fp)[1];
-    }
-    *paddr = pc;
-    return 0;
-}
-
-#endif /* _WIN32 */
-#endif /* CONFIG_TCC_BACKTRACE */
-/* ------------------------------------------------------------- */
-#ifdef CONFIG_TCC_STATIC
-
-/* dummy function for profiling */
-ST_FUNC void *dlopen(const char *filename, int flag)
-{
-    return NULL;
-}
-
-ST_FUNC void dlclose(void *p)
-{
-}
-
-ST_FUNC const char *dlerror(void)
-{
-    return "error";
-}
-
-typedef struct TCCSyms {
-    char *str;
-    void *ptr;
-} TCCSyms;
-
-
-/* add the symbol you want here if no dynamic linking is done */
-static TCCSyms tcc_syms[] = {
-#if !defined(CONFIG_TCCBOOT)
-#define TCCSYM(a) { #a, &a, },
-    TCCSYM(printf)
-    TCCSYM(fprintf)
-    TCCSYM(fopen)
-    TCCSYM(fclose)
-#undef TCCSYM
-#endif
-    { NULL, NULL },
-};
-
-ST_FUNC void *dlsym(void *handle, const char *symbol)
-{
-    TCCSyms *p;
-    p = tcc_syms;
-    while (p->str != NULL) {
-        if (!strcmp(p->str, symbol))
-            return p->ptr;
-        p++;
-    }
-    return NULL;
-}
-
-#endif /* CONFIG_TCC_STATIC */
-#endif /* TCC_IS_NATIVE */
-/* ------------------------------------------------------------- */
diff --git a/tinyc/tcctok.h b/tinyc/tcctok.h
deleted file mode 100644
index 317f64cd7..000000000
--- a/tinyc/tcctok.h
+++ /dev/null
@@ -1,350 +0,0 @@
-/* keywords */
-     DEF(TOK_INT, "int")
-     DEF(TOK_VOID, "void")
-     DEF(TOK_CHAR, "char")
-     DEF(TOK_IF, "if")
-     DEF(TOK_ELSE, "else")
-     DEF(TOK_WHILE, "while")
-     DEF(TOK_BREAK, "break")
-     DEF(TOK_RETURN, "return")
-     DEF(TOK_FOR, "for")
-     DEF(TOK_EXTERN, "extern")
-     DEF(TOK_STATIC, "static")
-     DEF(TOK_UNSIGNED, "unsigned")
-     DEF(TOK_GOTO, "goto")
-     DEF(TOK_DO, "do")
-     DEF(TOK_CONTINUE, "continue")
-     DEF(TOK_SWITCH, "switch")
-     DEF(TOK_CASE, "case")
-
-     DEF(TOK_CONST1, "const")
-     DEF(TOK_CONST2, "__const") /* gcc keyword */
-     DEF(TOK_CONST3, "__const__") /* gcc keyword */
-     DEF(TOK_VOLATILE1, "volatile")
-     DEF(TOK_VOLATILE2, "__volatile") /* gcc keyword */
-     DEF(TOK_VOLATILE3, "__volatile__") /* gcc keyword */
-     DEF(TOK_LONG, "long")
-     DEF(TOK_REGISTER, "register")
-     DEF(TOK_SIGNED1, "signed")
-     DEF(TOK_SIGNED2, "__signed") /* gcc keyword */
-     DEF(TOK_SIGNED3, "__signed__") /* gcc keyword */
-     DEF(TOK_AUTO, "auto")
-     DEF(TOK_INLINE1, "inline")
-     DEF(TOK_INLINE2, "__inline") /* gcc keyword */
-     DEF(TOK_INLINE3, "__inline__") /* gcc keyword */
-     DEF(TOK_RESTRICT1, "restrict")
-     DEF(TOK_RESTRICT2, "__restrict")
-     DEF(TOK_RESTRICT3, "__restrict__")
-     DEF(TOK_EXTENSION, "__extension__") /* gcc keyword */
-
-     DEF(TOK_GENERIC, "_Generic")
-
-     DEF(TOK_FLOAT, "float")
-     DEF(TOK_DOUBLE, "double")
-     DEF(TOK_BOOL, "_Bool")
-     DEF(TOK_SHORT, "short")
-     DEF(TOK_STRUCT, "struct")
-     DEF(TOK_UNION, "union")
-     DEF(TOK_TYPEDEF, "typedef")
-     DEF(TOK_DEFAULT, "default")
-     DEF(TOK_ENUM, "enum")
-     DEF(TOK_SIZEOF, "sizeof")
-     DEF(TOK_ATTRIBUTE1, "__attribute")
-     DEF(TOK_ATTRIBUTE2, "__attribute__")
-     DEF(TOK_ALIGNOF1, "__alignof")
-     DEF(TOK_ALIGNOF2, "__alignof__")
-     DEF(TOK_TYPEOF1, "typeof")
-     DEF(TOK_TYPEOF2, "__typeof")
-     DEF(TOK_TYPEOF3, "__typeof__")
-     DEF(TOK_LABEL, "__label__")
-     DEF(TOK_ASM1, "asm")
-     DEF(TOK_ASM2, "__asm")
-     DEF(TOK_ASM3, "__asm__")
-
-#ifdef TCC_TARGET_ARM64
-     DEF(TOK_UINT128, "__uint128_t")
-#endif
-
-/*********************************************************************/
-/* the following are not keywords. They are included to ease parsing */
-/* preprocessor only */
-     DEF(TOK_DEFINE, "define")
-     DEF(TOK_INCLUDE, "include")
-     DEF(TOK_INCLUDE_NEXT, "include_next")
-     DEF(TOK_IFDEF, "ifdef")
-     DEF(TOK_IFNDEF, "ifndef")
-     DEF(TOK_ELIF, "elif")
-     DEF(TOK_ENDIF, "endif")
-     DEF(TOK_DEFINED, "defined")
-     DEF(TOK_UNDEF, "undef")
-     DEF(TOK_ERROR, "error")
-     DEF(TOK_WARNING, "warning")
-     DEF(TOK_LINE, "line")
-     DEF(TOK_PRAGMA, "pragma")
-     DEF(TOK___LINE__, "__LINE__")
-     DEF(TOK___FILE__, "__FILE__")
-     DEF(TOK___DATE__, "__DATE__")
-     DEF(TOK___TIME__, "__TIME__")
-     DEF(TOK___FUNCTION__, "__FUNCTION__")
-     DEF(TOK___VA_ARGS__, "__VA_ARGS__")
-     DEF(TOK___COUNTER__, "__COUNTER__")
-
-/* special identifiers */
-     DEF(TOK___FUNC__, "__func__")
-
-/* special floating point values */
-     DEF(TOK___NAN__, "__nan__")
-     DEF(TOK___SNAN__, "__snan__")
-     DEF(TOK___INF__, "__inf__")
-
-/* attribute identifiers */
-/* XXX: handle all tokens generically since speed is not critical */
-     DEF(TOK_SECTION1, "section")
-     DEF(TOK_SECTION2, "__section__")
-     DEF(TOK_ALIGNED1, "aligned")
-     DEF(TOK_ALIGNED2, "__aligned__")
-     DEF(TOK_PACKED1, "packed")
-     DEF(TOK_PACKED2, "__packed__")
-     DEF(TOK_WEAK1, "weak")
-     DEF(TOK_WEAK2, "__weak__")
-     DEF(TOK_ALIAS1, "alias")
-     DEF(TOK_ALIAS2, "__alias__")
-     DEF(TOK_UNUSED1, "unused")
-     DEF(TOK_UNUSED2, "__unused__")
-     DEF(TOK_CDECL1, "cdecl")
-     DEF(TOK_CDECL2, "__cdecl")
-     DEF(TOK_CDECL3, "__cdecl__")
-     DEF(TOK_STDCALL1, "stdcall")
-     DEF(TOK_STDCALL2, "__stdcall")
-     DEF(TOK_STDCALL3, "__stdcall__")
-     DEF(TOK_FASTCALL1, "fastcall")
-     DEF(TOK_FASTCALL2, "__fastcall")
-     DEF(TOK_FASTCALL3, "__fastcall__")
-     DEF(TOK_REGPARM1, "regparm")
-     DEF(TOK_REGPARM2, "__regparm__")
-
-     DEF(TOK_MODE, "__mode__")
-     DEF(TOK_MODE_QI, "__QI__")
-     DEF(TOK_MODE_DI, "__DI__")
-     DEF(TOK_MODE_HI, "__HI__")
-     DEF(TOK_MODE_SI, "__SI__")
-     DEF(TOK_MODE_word, "__word__")
-
-     DEF(TOK_DLLEXPORT, "dllexport")
-     DEF(TOK_DLLIMPORT, "dllimport")
-     DEF(TOK_NORETURN1, "noreturn")
-     DEF(TOK_NORETURN2, "__noreturn__")
-     DEF(TOK_VISIBILITY1, "visibility")
-     DEF(TOK_VISIBILITY2, "__visibility__")
-
-     DEF(TOK_builtin_types_compatible_p, "__builtin_types_compatible_p")
-     DEF(TOK_builtin_choose_expr, "__builtin_choose_expr")
-     DEF(TOK_builtin_constant_p, "__builtin_constant_p")
-     DEF(TOK_builtin_frame_address, "__builtin_frame_address")
-     DEF(TOK_builtin_return_address, "__builtin_return_address")
-     DEF(TOK_builtin_expect, "__builtin_expect")
-     /*DEF(TOK_builtin_va_list, "__builtin_va_list")*/
-#if defined TCC_TARGET_PE && defined TCC_TARGET_X86_64
-     DEF(TOK_builtin_va_start, "__builtin_va_start")
-#elif defined TCC_TARGET_X86_64
-     DEF(TOK_builtin_va_arg_types, "__builtin_va_arg_types")
-#elif defined TCC_TARGET_ARM64
-     DEF(TOK___va_start, "__va_start")
-     DEF(TOK___va_arg, "__va_arg")
-#endif
-
-/* pragma */
-     DEF(TOK_pack, "pack")
-#if !defined(TCC_TARGET_I386) && !defined(TCC_TARGET_X86_64)
-     /* already defined for assembler */
-     DEF(TOK_ASM_push, "push")
-     DEF(TOK_ASM_pop, "pop")
-#endif
-     DEF(TOK_comment, "comment")
-     DEF(TOK_lib, "lib")
-     DEF(TOK_push_macro, "push_macro")
-     DEF(TOK_pop_macro, "pop_macro")
-     DEF(TOK_once, "once")
-     DEF(TOK_option, "option")
-
-/* builtin functions or variables */
-#ifndef TCC_ARM_EABI
-     DEF(TOK_memcpy, "memcpy")
-     DEF(TOK_memmove, "memmove")
-     DEF(TOK_memset, "memset")
-     DEF(TOK___divdi3, "__divdi3")
-     DEF(TOK___moddi3, "__moddi3")
-     DEF(TOK___udivdi3, "__udivdi3")
-     DEF(TOK___umoddi3, "__umoddi3")
-     DEF(TOK___ashrdi3, "__ashrdi3")
-     DEF(TOK___lshrdi3, "__lshrdi3")
-     DEF(TOK___ashldi3, "__ashldi3")
-     DEF(TOK___floatundisf, "__floatundisf")
-     DEF(TOK___floatundidf, "__floatundidf")
-# ifndef TCC_ARM_VFP
-     DEF(TOK___floatundixf, "__floatundixf")
-     DEF(TOK___fixunsxfdi, "__fixunsxfdi")
-# endif
-     DEF(TOK___fixunssfdi, "__fixunssfdi")
-     DEF(TOK___fixunsdfdi, "__fixunsdfdi")
-#endif
-
-#if defined TCC_TARGET_ARM
-# ifdef TCC_ARM_EABI
-     DEF(TOK_memcpy, "__aeabi_memcpy")
-     DEF(TOK_memcpy4, "__aeabi_memcpy4")
-     DEF(TOK_memcpy8, "__aeabi_memcpy8")
-     DEF(TOK_memmove, "__aeabi_memmove")
-     DEF(TOK_memset, "__aeabi_memset")
-     DEF(TOK___aeabi_ldivmod, "__aeabi_ldivmod")
-     DEF(TOK___aeabi_uldivmod, "__aeabi_uldivmod")
-     DEF(TOK___aeabi_idivmod, "__aeabi_idivmod")
-     DEF(TOK___aeabi_uidivmod, "__aeabi_uidivmod")
-     DEF(TOK___divsi3, "__aeabi_idiv")
-     DEF(TOK___udivsi3, "__aeabi_uidiv")
-     DEF(TOK___floatdisf, "__aeabi_l2f")
-     DEF(TOK___floatdidf, "__aeabi_l2d")
-     DEF(TOK___fixsfdi, "__aeabi_f2lz")
-     DEF(TOK___fixdfdi, "__aeabi_d2lz")
-     DEF(TOK___ashrdi3, "__aeabi_lasr")
-     DEF(TOK___lshrdi3, "__aeabi_llsr")
-     DEF(TOK___ashldi3, "__aeabi_llsl")
-     DEF(TOK___floatundisf, "__aeabi_ul2f")
-     DEF(TOK___floatundidf, "__aeabi_ul2d")
-     DEF(TOK___fixunssfdi, "__aeabi_f2ulz")
-     DEF(TOK___fixunsdfdi, "__aeabi_d2ulz")
-# else
-     DEF(TOK___modsi3, "__modsi3")
-     DEF(TOK___umodsi3, "__umodsi3")
-     DEF(TOK___divsi3, "__divsi3")
-     DEF(TOK___udivsi3, "__udivsi3")
-     DEF(TOK___floatdisf, "__floatdisf")
-     DEF(TOK___floatdidf, "__floatdidf")
-#  ifndef TCC_ARM_VFP
-     DEF(TOK___floatdixf, "__floatdixf")
-     DEF(TOK___fixunssfsi, "__fixunssfsi")
-     DEF(TOK___fixunsdfsi, "__fixunsdfsi")
-     DEF(TOK___fixunsxfsi, "__fixunsxfsi")
-     DEF(TOK___fixxfdi, "__fixxfdi")
-#  endif
-     DEF(TOK___fixsfdi, "__fixsfdi")
-     DEF(TOK___fixdfdi, "__fixdfdi")
-# endif
-#endif
-
-#if defined TCC_TARGET_C67
-     DEF(TOK__divi, "_divi")
-     DEF(TOK__divu, "_divu")
-     DEF(TOK__divf, "_divf")
-     DEF(TOK__divd, "_divd")
-     DEF(TOK__remi, "_remi")
-     DEF(TOK__remu, "_remu")
-#endif
-
-#if defined TCC_TARGET_I386
-     DEF(TOK___fixsfdi, "__fixsfdi")
-     DEF(TOK___fixdfdi, "__fixdfdi")
-     DEF(TOK___fixxfdi, "__fixxfdi")
-#endif
-
-#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
-     DEF(TOK_alloca, "alloca")
-#endif
-
-#if defined TCC_TARGET_PE
-     DEF(TOK___chkstk, "__chkstk")
-#endif
-#ifdef TCC_TARGET_ARM64
-     DEF(TOK___arm64_clear_cache, "__arm64_clear_cache")
-     DEF(TOK___addtf3, "__addtf3")
-     DEF(TOK___subtf3, "__subtf3")
-     DEF(TOK___multf3, "__multf3")
-     DEF(TOK___divtf3, "__divtf3")
-     DEF(TOK___extendsftf2, "__extendsftf2")
-     DEF(TOK___extenddftf2, "__extenddftf2")
-     DEF(TOK___trunctfsf2, "__trunctfsf2")
-     DEF(TOK___trunctfdf2, "__trunctfdf2")
-     DEF(TOK___fixtfsi, "__fixtfsi")
-     DEF(TOK___fixtfdi, "__fixtfdi")
-     DEF(TOK___fixunstfsi, "__fixunstfsi")
-     DEF(TOK___fixunstfdi, "__fixunstfdi")
-     DEF(TOK___floatsitf, "__floatsitf")
-     DEF(TOK___floatditf, "__floatditf")
-     DEF(TOK___floatunsitf, "__floatunsitf")
-     DEF(TOK___floatunditf, "__floatunditf")
-     DEF(TOK___eqtf2, "__eqtf2")
-     DEF(TOK___netf2, "__netf2")
-     DEF(TOK___lttf2, "__lttf2")
-     DEF(TOK___letf2, "__letf2")
-     DEF(TOK___gttf2, "__gttf2")
-     DEF(TOK___getf2, "__getf2")
-#endif
-
-/* bound checking symbols */
-#ifdef CONFIG_TCC_BCHECK
-     DEF(TOK___bound_ptr_add, "__bound_ptr_add")
-     DEF(TOK___bound_ptr_indir1, "__bound_ptr_indir1")
-     DEF(TOK___bound_ptr_indir2, "__bound_ptr_indir2")
-     DEF(TOK___bound_ptr_indir4, "__bound_ptr_indir4")
-     DEF(TOK___bound_ptr_indir8, "__bound_ptr_indir8")
-     DEF(TOK___bound_ptr_indir12, "__bound_ptr_indir12")
-     DEF(TOK___bound_ptr_indir16, "__bound_ptr_indir16")
-     DEF(TOK___bound_main_arg, "__bound_main_arg")
-     DEF(TOK___bound_local_new, "__bound_local_new")
-     DEF(TOK___bound_local_delete, "__bound_local_delete")
-# ifdef TCC_TARGET_PE
-     DEF(TOK_malloc, "malloc")
-     DEF(TOK_free, "free")
-     DEF(TOK_realloc, "realloc")
-     DEF(TOK_memalign, "memalign")
-     DEF(TOK_calloc, "calloc")
-# endif
-     DEF(TOK_strlen, "strlen")
-     DEF(TOK_strcpy, "strcpy")
-#endif
-
-/* Tiny Assembler */
- DEF_ASMDIR(byte)              /* must be first directive */
- DEF_ASMDIR(word)
- DEF_ASMDIR(align)
- DEF_ASMDIR(balign)
- DEF_ASMDIR(p2align)
- DEF_ASMDIR(set)
- DEF_ASMDIR(skip)
- DEF_ASMDIR(space)
- DEF_ASMDIR(string)
- DEF_ASMDIR(asciz)
- DEF_ASMDIR(ascii)
- DEF_ASMDIR(file)
- DEF_ASMDIR(globl)
- DEF_ASMDIR(global)
- DEF_ASMDIR(weak)
- DEF_ASMDIR(hidden)
- DEF_ASMDIR(ident)
- DEF_ASMDIR(size)
- DEF_ASMDIR(type)
- DEF_ASMDIR(text)
- DEF_ASMDIR(data)
- DEF_ASMDIR(bss)
- DEF_ASMDIR(previous)
- DEF_ASMDIR(pushsection)
- DEF_ASMDIR(popsection)
- DEF_ASMDIR(fill)
- DEF_ASMDIR(rept)
- DEF_ASMDIR(endr)
- DEF_ASMDIR(org)
- DEF_ASMDIR(quad)
-#if defined(TCC_TARGET_I386)
- DEF_ASMDIR(code16)
- DEF_ASMDIR(code32)
-#elif defined(TCC_TARGET_X86_64)
- DEF_ASMDIR(code64)
-#endif
- DEF_ASMDIR(short)
- DEF_ASMDIR(long)
- DEF_ASMDIR(int)
- DEF_ASMDIR(section)            /* must be last directive */
-
-#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
-#include "i386-tok.h"
-#endif
diff --git a/tinyc/tcctools.c b/tinyc/tcctools.c
deleted file mode 100644
index 53d88be6a..000000000
--- a/tinyc/tcctools.c
+++ /dev/null
@@ -1,546 +0,0 @@
-/* -------------------------------------------------------------- */
-/*
- *  TCC - Tiny C Compiler
- *
- *  tcctools.c - extra tools and and -m32/64 support
- *
- */
-
-/* -------------------------------------------------------------- */
-/*
- * This program is for making libtcc1.a without ar
- * tiny_libmaker - tiny elf lib maker
- * usage: tiny_libmaker [lib] files...
- * Copyright (c) 2007 Timppa
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include "tcc.h"
-
-//#define ARMAG  "!<arch>\n"
-#define ARFMAG "`\n"
-
-typedef struct {
-    char ar_name[16];
-    char ar_date[12];
-    char ar_uid[6];
-    char ar_gid[6];
-    char ar_mode[8];
-    char ar_size[10];
-    char ar_fmag[2];
-} ArHdr;
-
-static unsigned long le2belong(unsigned long ul) {
-    return ((ul & 0xFF0000)>>8)+((ul & 0xFF000000)>>24) +
-        ((ul & 0xFF)<<24)+((ul & 0xFF00)<<8);
-}
-
-/* Returns 1 if s contains any of the chars of list, else 0 */
-static int contains_any(const char *s, const char *list) {
-  const char *l;
-  for (; *s; s++) {
-      for (l = list; *l; l++) {
-          if (*s == *l)
-              return 1;
-      }
-  }
-  return 0;
-}
-
-static int ar_usage(int ret) {
-    fprintf(stderr, "usage: tcc -ar [rcsv] lib file...\n");
-    fprintf(stderr, "create library ([abdioptxN] not supported).\n");
-    return ret;
-}
-
-ST_FUNC int tcc_tool_ar(TCCState *s1, int argc, char **argv)
-{
-    static ArHdr arhdr = {
-        "/               ",
-        "            ",
-        "0     ",
-        "0     ",
-        "0       ",
-        "          ",
-        ARFMAG
-        };
-
-    static ArHdr arhdro = {
-        "                ",
-        "            ",
-        "0     ",
-        "0     ",
-        "0       ",
-        "          ",
-        ARFMAG
-        };
-
-    FILE *fi, *fh = NULL, *fo = NULL;
-    ElfW(Ehdr) *ehdr;
-    ElfW(Shdr) *shdr;
-    ElfW(Sym) *sym;
-    int i, fsize, i_lib, i_obj;
-    char *buf, *shstr, *symtab = NULL, *strtab = NULL;
-    int symtabsize = 0;//, strtabsize = 0;
-    char *anames = NULL;
-    int *afpos = NULL;
-    int istrlen, strpos = 0, fpos = 0, funccnt = 0, funcmax, hofs;
-    char tfile[260], stmp[20];
-    char *file, *name;
-    int ret = 2;
-    const char *ops_conflict = "habdioptxN";  // unsupported but destructive if ignored.
-    int verbose = 0;
-
-    i_lib = 0; i_obj = 0;  // will hold the index of the lib and first obj
-    for (i = 1; i < argc; i++) {
-        const char *a = argv[i];
-        if (*a == '-' && strstr(a, "."))
-            ret = 1; // -x.y is always invalid (same as gnu ar)
-        if ((*a == '-') || (i == 1 && !strstr(a, "."))) {  // options argument
-            if (contains_any(a, ops_conflict))
-                ret = 1;
-            if (strstr(a, "v"))
-                verbose = 1;
-        } else {  // lib or obj files: don't abort - keep validating all args.
-            if (!i_lib)  // first file is the lib
-                i_lib = i;
-            else if (!i_obj)  // second file is the first obj
-                i_obj = i;
-        }
-    }
-
-    if (!i_obj)  // i_obj implies also i_lib. we require both.
-        ret = 1;
-
-    if (ret == 1)
-        return ar_usage(ret);
-
-    if ((fh = fopen(argv[i_lib], "wb")) == NULL)
-    {
-        fprintf(stderr, "tcc: ar: can't open file %s \n", argv[i_lib]);
-        goto the_end;
-    }
-
-    sprintf(tfile, "%s.tmp", argv[i_lib]);
-    if ((fo = fopen(tfile, "wb+")) == NULL)
-    {
-        fprintf(stderr, "tcc: ar: can't create temporary file %s\n", tfile);
-        goto the_end;
-    }
-
-    funcmax = 250;
-    afpos = tcc_realloc(NULL, funcmax * sizeof *afpos); // 250 func
-    memcpy(&arhdro.ar_mode, "100666", 6);
-
-    // i_obj = first input object file
-    while (i_obj < argc)
-    {
-        if (*argv[i_obj] == '-') {  // by now, all options start with '-'
-            i_obj++;
-            continue;
-        }
-        if ((fi = fopen(argv[i_obj], "rb")) == NULL) {
-            fprintf(stderr, "tcc: ar: can't open file %s \n", argv[i_obj]);
-            goto the_end;
-        }
-        if (verbose)
-            printf("a - %s\n", argv[i_obj]);
-
-        fseek(fi, 0, SEEK_END);
-        fsize = ftell(fi);
-        fseek(fi, 0, SEEK_SET);
-        buf = tcc_malloc(fsize + 1);
-        fread(buf, fsize, 1, fi);
-        fclose(fi);
-
-        // elf header
-        ehdr = (ElfW(Ehdr) *)buf;
-        if (ehdr->e_ident[4] != ELFCLASSW)
-        {
-            fprintf(stderr, "tcc: ar: Unsupported Elf Class: %s\n", argv[i_obj]);
-            goto the_end;
-        }
-
-        shdr = (ElfW(Shdr) *) (buf + ehdr->e_shoff + ehdr->e_shstrndx * ehdr->e_shentsize);
-        shstr = (char *)(buf + shdr->sh_offset);
-        for (i = 0; i < ehdr->e_shnum; i++)
-        {
-            shdr = (ElfW(Shdr) *) (buf + ehdr->e_shoff + i * ehdr->e_shentsize);
-            if (!shdr->sh_offset)
-                continue;
-            if (shdr->sh_type == SHT_SYMTAB)
-            {
-                symtab = (char *)(buf + shdr->sh_offset);
-                symtabsize = shdr->sh_size;
-            }
-            if (shdr->sh_type == SHT_STRTAB)
-            {
-                if (!strcmp(shstr + shdr->sh_name, ".strtab"))
-                {
-                    strtab = (char *)(buf + shdr->sh_offset);
-                    //strtabsize = shdr->sh_size;
-                }
-            }
-        }
-
-        if (symtab && symtabsize)
-        {
-            int nsym = symtabsize / sizeof(ElfW(Sym));
-            //printf("symtab: info size shndx name\n");
-            for (i = 1; i < nsym; i++)
-            {
-                sym = (ElfW(Sym) *) (symtab + i * sizeof(ElfW(Sym)));
-                if (sym->st_shndx &&
-                    (sym->st_info == 0x10
-                    || sym->st_info == 0x11
-                    || sym->st_info == 0x12
-                    )) {
-                    //printf("symtab: %2Xh %4Xh %2Xh %s\n", sym->st_info, sym->st_size, sym->st_shndx, strtab + sym->st_name);
-                    istrlen = strlen(strtab + sym->st_name)+1;
-                    anames = tcc_realloc(anames, strpos+istrlen);
-                    strcpy(anames + strpos, strtab + sym->st_name);
-                    strpos += istrlen;
-                    if (++funccnt >= funcmax) {
-                        funcmax += 250;
-                        afpos = tcc_realloc(afpos, funcmax * sizeof *afpos); // 250 func more
-                    }
-                    afpos[funccnt] = fpos;
-                }
-            }
-        }
-
-        file = argv[i_obj];
-        for (name = strchr(file, 0);
-             name > file && name[-1] != '/' && name[-1] != '\\';
-             --name);
-        istrlen = strlen(name);
-        if (istrlen >= sizeof(arhdro.ar_name))
-            istrlen = sizeof(arhdro.ar_name) - 1;
-        memset(arhdro.ar_name, ' ', sizeof(arhdro.ar_name));
-        memcpy(arhdro.ar_name, name, istrlen);
-        arhdro.ar_name[istrlen] = '/';
-        sprintf(stmp, "%-10d", fsize);
-        memcpy(&arhdro.ar_size, stmp, 10);
-        fwrite(&arhdro, sizeof(arhdro), 1, fo);
-        fwrite(buf, fsize, 1, fo);
-        tcc_free(buf);
-        i_obj++;
-        fpos += (fsize + sizeof(arhdro));
-    }
-    hofs = 8 + sizeof(arhdr) + strpos + (funccnt+1) * sizeof(int);
-    fpos = 0;
-    if ((hofs & 1)) // align
-        hofs++, fpos = 1;
-    // write header
-    fwrite("!<arch>\n", 8, 1, fh);
-    sprintf(stmp, "%-10d", (int)(strpos + (funccnt+1) * sizeof(int)));
-    memcpy(&arhdr.ar_size, stmp, 10);
-    fwrite(&arhdr, sizeof(arhdr), 1, fh);
-    afpos[0] = le2belong(funccnt);
-    for (i=1; i<=funccnt; i++)
-        afpos[i] = le2belong(afpos[i] + hofs);
-    fwrite(afpos, (funccnt+1) * sizeof(int), 1, fh);
-    fwrite(anames, strpos, 1, fh);
-    if (fpos)
-        fwrite("", 1, 1, fh);
-    // write objects
-    fseek(fo, 0, SEEK_END);
-    fsize = ftell(fo);
-    fseek(fo, 0, SEEK_SET);
-    buf = tcc_malloc(fsize + 1);
-    fread(buf, fsize, 1, fo);
-    fwrite(buf, fsize, 1, fh);
-    tcc_free(buf);
-    ret = 0;
-the_end:
-    if (anames)
-        tcc_free(anames);
-    if (afpos)
-        tcc_free(afpos);
-    if (fh)
-        fclose(fh);
-    if (fo)
-        fclose(fo), remove(tfile);
-    return ret;
-}
-
-/* -------------------------------------------------------------- */
-/*
- * tiny_impdef creates an export definition file (.def) from a dll
- * on MS-Windows. Usage: tiny_impdef library.dll [-o outputfile]"
- *
- *  Copyright (c) 2005,2007 grischka
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifdef TCC_TARGET_PE
-
-ST_FUNC int tcc_tool_impdef(TCCState *s1, int argc, char **argv)
-{
-    int ret, v, i;
-    char infile[260];
-    char outfile[260];
-
-    const char *file;
-    char *p, *q;
-    FILE *fp, *op;
-
-#ifdef _WIN32
-    char path[260];
-#endif
-
-    infile[0] = outfile[0] = 0;
-    fp = op = NULL;
-    ret = 1;
-    p = NULL;
-    v = 0;
-
-    for (i = 1; i < argc; ++i) {
-        const char *a = argv[i];
-        if ('-' == a[0]) {
-            if (0 == strcmp(a, "-v")) {
-                v = 1;
-            } else if (0 == strcmp(a, "-o")) {
-                if (++i == argc)
-                    goto usage;
-                strcpy(outfile, argv[i]);
-            } else
-                goto usage;
-        } else if (0 == infile[0])
-            strcpy(infile, a);
-        else
-            goto usage;
-    }
-
-    if (0 == infile[0]) {
-usage:
-        fprintf(stderr,
-            "usage: tcc -impdef library.dll [-v] [-o outputfile]\n"
-            "create export definition file (.def) from dll\n"
-            );
-        goto the_end;
-    }
-
-    if (0 == outfile[0]) {
-        strcpy(outfile, tcc_basename(infile));
-        q = strrchr(outfile, '.');
-        if (NULL == q)
-            q = strchr(outfile, 0);
-        strcpy(q, ".def");
-    }
-
-    file = infile;
-#ifdef _WIN32
-    if (SearchPath(NULL, file, ".dll", sizeof path, path, NULL))
-        file = path;
-#endif
-    ret = tcc_get_dllexports(file, &p);
-    if (ret || !p) {
-        fprintf(stderr, "tcc: impdef: %s '%s'\n",
-            ret == -1 ? "can't find file" :
-            ret ==  1 ? "can't read symbols" :
-            ret ==  0 ? "no symbols found in" :
-            "unknown file type", file);
-        ret = 1;
-        goto the_end;
-    }
-
-    if (v)
-        printf("-> %s\n", file);
-
-    op = fopen(outfile, "w");
-    if (NULL == op) {
-        fprintf(stderr, "tcc: impdef: could not create output file: %s\n", outfile);
-        goto the_end;
-    }
-
-    fprintf(op, "LIBRARY %s\n\nEXPORTS\n", tcc_basename(file));
-    for (q = p, i = 0; *q; ++i) {
-        fprintf(op, "%s\n", q);
-        q += strlen(q) + 1;
-    }
-
-    if (v)
-        printf("<- %s (%d symbol%s)\n", outfile, i, &"s"[i<2]);
-
-    ret = 0;
-
-the_end:
-    /* cannot free memory received from tcc_get_dllexports
-       if it came from a dll */
-    /* if (p)
-        tcc_free(p); */
-    if (fp)
-        fclose(fp);
-    if (op)
-        fclose(op);
-    return ret;
-}
-
-#endif /* TCC_TARGET_PE */
-
-/* -------------------------------------------------------------- */
-/*
- *  TCC - Tiny C Compiler
- *
- *  Copyright (c) 2001-2004 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/* re-execute the i386/x86_64 cross-compilers with tcc -m32/-m64: */
-
-#if !defined TCC_TARGET_I386 && !defined TCC_TARGET_X86_64
-
-ST_FUNC void tcc_tool_cross(TCCState *s, char **argv, int option)
-{
-    tcc_error("-m%d not implemented.", option);
-}
-
-#else
-#ifdef _WIN32
-#include <process.h>
-
-static char *str_replace(const char *str, const char *p, const char *r)
-{
-    const char *s, *s0;
-    char *d, *d0;
-    int sl, pl, rl;
-
-    sl = strlen(str);
-    pl = strlen(p);
-    rl = strlen(r);
-    for (d0 = NULL;; d0 = tcc_malloc(sl + 1)) {
-        for (d = d0, s = str; s0 = s, s = strstr(s, p), s; s += pl) {
-            if (d) {
-                memcpy(d, s0, sl = s - s0), d += sl;
-                memcpy(d, r, rl), d += rl;
-            } else
-                sl += rl - pl;
-        }
-        if (d) {
-            strcpy(d, s0);
-            return d0;
-        }
-    }
-}
-
-static int execvp_win32(const char *prog, char **argv)
-{
-    int ret; char **p;
-    /* replace all " by \" */
-    for (p = argv; *p; ++p)
-        if (strchr(*p, '"'))
-            *p = str_replace(*p, "\"", "\\\"");
-    ret = _spawnvp(P_NOWAIT, prog, (const char *const*)argv);
-    if (-1 == ret)
-        return ret;
-    _cwait(&ret, ret, WAIT_CHILD);
-    exit(ret);
-}
-#define execvp execvp_win32
-#endif /* _WIN32 */
-
-ST_FUNC void tcc_tool_cross(TCCState *s, char **argv, int target)
-{
-    char program[4096];
-    char *a0 = argv[0];
-    int prefix = tcc_basename(a0) - a0;
-
-    snprintf(program, sizeof program,
-        "%.*s%s"
-#ifdef TCC_TARGET_PE
-        "-win32"
-#endif
-        "-tcc"
-#ifdef _WIN32
-        ".exe"
-#endif
-        , prefix, a0, target == 64 ? "x86_64" : "i386");
-
-    if (strcmp(a0, program))
-        execvp(argv[0] = program, argv);
-    tcc_error("could not run '%s'", program);
-}
-
-#endif /* TCC_TARGET_I386 && TCC_TARGET_X86_64 */
-/* -------------------------------------------------------------- */
-/* enable commandline wildcard expansion (tcc -o x.exe *.c) */
-
-#ifdef _WIN32
-int _CRT_glob = 1;
-#ifndef _CRT_glob
-int _dowildcard = 1;
-#endif
-#endif
-
-/* -------------------------------------------------------------- */
-/* generate xxx.d file */
-
-ST_FUNC void gen_makedeps(TCCState *s, const char *target, const char *filename)
-{
-    FILE *depout;
-    char buf[1024];
-    int i;
-
-    if (!filename) {
-        /* compute filename automatically: dir/file.o -> dir/file.d */
-        snprintf(buf, sizeof buf, "%.*s.d",
-            (int)(tcc_fileextension(target) - target), target);
-        filename = buf;
-    }
-
-    if (s->verbose)
-        printf("<- %s\n", filename);
-
-    /* XXX return err codes instead of error() ? */
-    depout = fopen(filename, "w");
-    if (!depout)
-        tcc_error("could not open '%s'", filename);
-
-    fprintf(depout, "%s: \\\n", target);
-    for (i=0; i<s->nb_target_deps; ++i)
-        fprintf(depout, " %s \\\n", s->target_deps[i]);
-    fprintf(depout, "\n");
-    fclose(depout);
-}
-
-/* -------------------------------------------------------------- */
diff --git a/tinyc/tests/42test.h b/tinyc/tests/42test.h
deleted file mode 100644
index 5db7d1c47..000000000
--- a/tinyc/tests/42test.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/* This file is to test compute #include directives.  It's named so
-   that it starts with a pre-processing number which isn't a valid
-   number (42test.h).  Including this must work.  */
-#ifndef INC42_FIRST
-int have_included_42test_h;
-#define INC42_FIRST
-#elif !defined INC42_SECOND
-#define INC42_SECOND
-int have_included_42test_h_second;
-#else
-#define INC42_THIRD
-int have_included_42test_h_third;
-#endif
diff --git a/tinyc/tests/abitest.c b/tinyc/tests/abitest.c
deleted file mode 100644
index 4a192bd23..000000000
--- a/tinyc/tests/abitest.c
+++ /dev/null
@@ -1,691 +0,0 @@
-#include <libtcc.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
-
-// MinGW has 80-bit rather than 64-bit long double which isn't compatible with TCC or MSVC
-#if defined(_WIN32) && defined(__GNUC__)
-#define LONG_DOUBLE double
-#define LONG_DOUBLE_LITERAL(x) x
-#else
-#define LONG_DOUBLE long double
-#define LONG_DOUBLE_LITERAL(x) x ## L
-#endif
-
-static int g_argc;
-static char **g_argv;
-
-static void set_options(TCCState *s, int argc, char **argv)
-{
-    int i;
-    for (i = 1; i < argc; ++i) {
-        char *a = argv[i];
-        if (a[0] == '-') {
-            if (a[1] == 'B')
-                tcc_set_lib_path(s, a+2);
-            else if (a[1] == 'I')
-                tcc_add_include_path(s, a+2);
-            else if (a[1] == 'L')
-                tcc_add_library_path(s, a+2);
-        }
-    }
-}
-
-typedef int (*callback_type) (void*);
-
-/*
- * Compile source code and call a callback with a pointer to the symbol "f".
- */
-static int run_callback(const char *src, callback_type callback) {
-  TCCState *s;
-  int result;
-  void *ptr;
-  
-  s = tcc_new();
-  if (!s)
-    return -1;
-
-  set_options(s, g_argc, g_argv);
-
-  if (tcc_set_output_type(s, TCC_OUTPUT_MEMORY) == -1)
-    return -1;
-  if (tcc_compile_string(s, src) == -1)
-    return -1;
-  if (tcc_relocate(s, TCC_RELOCATE_AUTO) == -1)
-    return -1;
-  
-  ptr = tcc_get_symbol(s, "f");
-  if (!ptr)
-    return -1;
-  result = callback(ptr);
-  
-  tcc_delete(s);
-  
-  return result;
-}
-
-#define STR2(x) #x
-#define STR(x) STR2(x)
-
-#define RET_PRIMITIVE_TEST(name, type, val) \
-  static int ret_ ## name ## _test_callback(void *ptr) { \
-    type (*callback) (type) = (type(*)(type))ptr; \
-    type x = val; \
-    type y = callback(x); \
-    return (y == x+x) ? 0 : -1; \
-  } \
-  \
-  static int ret_ ## name ## _test(void) { \
-    const char *src = STR(type) " f(" STR(type) " x) {return x+x;}"; \
-    return run_callback(src, ret_ ## name ## _test_callback); \
-  }
-
-RET_PRIMITIVE_TEST(int, int, 70000)
-RET_PRIMITIVE_TEST(longlong, long long, 4333369356528LL)
-RET_PRIMITIVE_TEST(float, float, 63.0)
-RET_PRIMITIVE_TEST(double, double, 14789798.0)
-RET_PRIMITIVE_TEST(longdouble, LONG_DOUBLE, LONG_DOUBLE_LITERAL(378943892.0))
-
-/*
- * ret_2float_test:
- * 
- * On x86-64, a struct with 2 floats should be packed into a single
- * SSE register (VT_DOUBLE is used for this purpose).
- */
-typedef struct ret_2float_test_type_s {float x, y;} ret_2float_test_type;
-typedef ret_2float_test_type (*ret_2float_test_function_type) (ret_2float_test_type);
-
-static int ret_2float_test_callback(void *ptr) {
-  ret_2float_test_function_type f = (ret_2float_test_function_type)ptr;
-  ret_2float_test_type a = {10, 35};
-  ret_2float_test_type r;
-  r = f(a);
-  return ((r.x == a.x*5) && (r.y == a.y*3)) ? 0 : -1;
-}
-
-static int ret_2float_test(void) {
-  const char *src =
-  "typedef struct ret_2float_test_type_s {float x, y;} ret_2float_test_type;"
-  "ret_2float_test_type f(ret_2float_test_type a) {\n"
-  "  ret_2float_test_type r = {a.x*5, a.y*3};\n"
-  "  return r;\n"
-  "}\n";
-
-  return run_callback(src, ret_2float_test_callback);
-}
-
-/*
- * ret_2double_test:
- * 
- * On x86-64, a struct with 2 doubles should be passed in two SSE
- * registers.
- */
-typedef struct ret_2double_test_type_s {double x, y;} ret_2double_test_type;
-typedef ret_2double_test_type (*ret_2double_test_function_type) (ret_2double_test_type);
-
-static int ret_2double_test_callback(void *ptr) {
-  ret_2double_test_function_type f = (ret_2double_test_function_type)ptr;
-  ret_2double_test_type a = {10, 35};
-  ret_2double_test_type r;
-  r = f(a);
-  return ((r.x == a.x*5) && (r.y == a.y*3)) ? 0 : -1;
-}
-
-static int ret_2double_test(void) {
-  const char *src =
-  "typedef struct ret_2double_test_type_s {double x, y;} ret_2double_test_type;"
-  "ret_2double_test_type f(ret_2double_test_type a) {\n"
-  "  ret_2double_test_type r = {a.x*5, a.y*3};\n"
-  "  return r;\n"
-  "}\n";
-
-  return run_callback(src, ret_2double_test_callback);
-}
-
-/*
- * ret_8plus2double_test:
- *
- * This catches a corner case in the x86_64 ABI code: the first 7
- * arguments fit into registers, the 8th doesn't, but the 9th argument
- * fits into the 8th XMM register.
- *
- * Note that the purpose of the 10th argument is to avoid a situation
- * in which gcc would accidentally put the double at the right
- * address, thus causing a success message even though TCC actually
- * generated incorrect code.
- */
-typedef ret_2double_test_type (*ret_8plus2double_test_function_type) (double, double, double, double, double, double, double, ret_2double_test_type, double, double);
-
-static int ret_8plus2double_test_callback(void *ptr) {
-  ret_8plus2double_test_function_type f = (ret_8plus2double_test_function_type)ptr;
-  ret_2double_test_type a = {10, 35};
-  ret_2double_test_type r;
-  r = f(0, 0, 0, 0, 0, 0, 0, a, 37, 38);
-  return ((r.x == 37) && (r.y == 37)) ? 0 : -1;
-}
-
-static int ret_8plus2double_test(void) {
-  const char *src =
-  "typedef struct ret_2double_test_type_s {double x, y;} ret_2double_test_type;"
-  "ret_2double_test_type f(double x1, double x2, double x3, double x4, double x5, double x6, double x7, ret_2double_test_type a, double x8, double x9) {\n"
-  "  ret_2double_test_type r = { x8, x8 };\n"
-  "  return r;\n"
-  "}\n";
-
-  return run_callback(src, ret_8plus2double_test_callback);
-}
-
-/*
- * ret_mixed_test:
- *
- * On x86-64, a struct with a double and a 64-bit integer should be
- * passed in one SSE register and one integer register.
- */
-typedef struct ret_mixed_test_type_s {double x; long long y;} ret_mixed_test_type;
-typedef ret_mixed_test_type (*ret_mixed_test_function_type) (ret_mixed_test_type);
-
-static int ret_mixed_test_callback(void *ptr) {
-  ret_mixed_test_function_type f = (ret_mixed_test_function_type)ptr;
-  ret_mixed_test_type a = {10, 35};
-  ret_mixed_test_type r;
-  r = f(a);
-  return ((r.x == a.x*5) && (r.y == a.y*3)) ? 0 : -1;
-}
-
-static int ret_mixed_test(void) {
-  const char *src =
-  "typedef struct ret_mixed_test_type_s {double x; long long y;} ret_mixed_test_type;"
-  "ret_mixed_test_type f(ret_mixed_test_type a) {\n"
-  "  ret_mixed_test_type r = {a.x*5, a.y*3};\n"
-  "  return r;\n"
-  "}\n";
-
-  return run_callback(src, ret_mixed_test_callback);
-}
-
-/*
- * ret_mixed2_test:
- *
- * On x86-64, a struct with two floats and two 32-bit integers should
- * be passed in one SSE register and one integer register.
- */
-typedef struct ret_mixed2_test_type_s {float x,x2; int y,y2;} ret_mixed2_test_type;
-typedef ret_mixed2_test_type (*ret_mixed2_test_function_type) (ret_mixed2_test_type);
-
-static int ret_mixed2_test_callback(void *ptr) {
-  ret_mixed2_test_function_type f = (ret_mixed2_test_function_type)ptr;
-  ret_mixed2_test_type a = {10, 5, 35, 7 };
-  ret_mixed2_test_type r;
-  r = f(a);
-  return ((r.x == a.x*5) && (r.y == a.y*3)) ? 0 : -1;
-}
-
-static int ret_mixed2_test(void) {
-  const char *src =
-  "typedef struct ret_mixed2_test_type_s {float x, x2; int y,y2;} ret_mixed2_test_type;"
-  "ret_mixed2_test_type f(ret_mixed2_test_type a) {\n"
-  "  ret_mixed2_test_type r = {a.x*5, 0, a.y*3, 0};\n"
-  "  return r;\n"
-  "}\n";
-
-  return run_callback(src, ret_mixed2_test_callback);
-}
-
-/*
- * ret_mixed3_test:
- *
- * On x86-64, this struct should be passed in two integer registers.
- */
-typedef struct ret_mixed3_test_type_s {float x; int y; float x2; int y2;} ret_mixed3_test_type;
-typedef ret_mixed3_test_type (*ret_mixed3_test_function_type) (ret_mixed3_test_type);
-
-static int ret_mixed3_test_callback(void *ptr) {
-  ret_mixed3_test_function_type f = (ret_mixed3_test_function_type)ptr;
-  ret_mixed3_test_type a = {10, 5, 35, 7 };
-  ret_mixed3_test_type r;
-  r = f(a);
-  return ((r.x == a.x*5) && (r.y2 == a.y*3)) ? 0 : -1;
-}
-
-static int ret_mixed3_test(void) {
-  const char *src =
-  "typedef struct ret_mixed3_test_type_s {float x; int y; float x2; int y2;} ret_mixed3_test_type;"
-  "ret_mixed3_test_type f(ret_mixed3_test_type a) {\n"
-  "  ret_mixed3_test_type r = {a.x*5, 0, 0, a.y*3};\n"
-  "  return r;\n"
-  "}\n";
-
-  return run_callback(src, ret_mixed3_test_callback);
-}
-
-/*
- * reg_pack_test: return a small struct which should be packed into
- * registers (Win32) during return.
- */
-typedef struct reg_pack_test_type_s {int x, y;} reg_pack_test_type;
-typedef reg_pack_test_type (*reg_pack_test_function_type) (reg_pack_test_type);
-
-static int reg_pack_test_callback(void *ptr) {
-  reg_pack_test_function_type f = (reg_pack_test_function_type)ptr;
-  reg_pack_test_type a = {10, 35};
-  reg_pack_test_type r;
-  r = f(a);
-  return ((r.x == a.x*5) && (r.y == a.y*3)) ? 0 : -1;
-}
-
-static int reg_pack_test(void) {
-  const char *src =
-  "typedef struct reg_pack_test_type_s {int x, y;} reg_pack_test_type;"
-  "reg_pack_test_type f(reg_pack_test_type a) {\n"
-  "  reg_pack_test_type r = {a.x*5, a.y*3};\n"
-  "  return r;\n"
-  "}\n";
-  
-  return run_callback(src, reg_pack_test_callback);
-}
-
-/*
- * reg_pack_longlong_test: return a small struct which should be packed into
- * registers (x86-64) during return.
- */
-typedef struct reg_pack_longlong_test_type_s {long long x, y;} reg_pack_longlong_test_type;
-typedef reg_pack_longlong_test_type (*reg_pack_longlong_test_function_type) (reg_pack_longlong_test_type);
-
-static int reg_pack_longlong_test_callback(void *ptr) {
-  reg_pack_longlong_test_function_type f = (reg_pack_longlong_test_function_type)ptr;
-  reg_pack_longlong_test_type a = {10, 35};
-  reg_pack_longlong_test_type r;
-  r = f(a);
-  return ((r.x == a.x*5) && (r.y == a.y*3)) ? 0 : -1;
-}
-
-static int reg_pack_longlong_test(void) {
-  const char *src =
-  "typedef struct reg_pack_longlong_test_type_s {long long x, y;} reg_pack_longlong_test_type;"
-  "reg_pack_longlong_test_type f(reg_pack_longlong_test_type a) {\n"
-  "  reg_pack_longlong_test_type r = {a.x*5, a.y*3};\n"
-  "  return r;\n"
-  "}\n";
-  
-  return run_callback(src, reg_pack_longlong_test_callback);
-}
-
-/*
- * ret_6plus2longlong_test:
- *
- * This catches a corner case in the x86_64 ABI code: the first 5
- * arguments fit into registers, the 6th doesn't, but the 7th argument
- * fits into the 6th argument integer register, %r9.
- *
- * Note that the purpose of the 10th argument is to avoid a situation
- * in which gcc would accidentally put the longlong at the right
- * address, thus causing a success message even though TCC actually
- * generated incorrect code.
- */
-typedef reg_pack_longlong_test_type (*ret_6plus2longlong_test_function_type) (long long, long long, long long, long long, long long, reg_pack_longlong_test_type, long long, long long);
-
-static int ret_6plus2longlong_test_callback(void *ptr) {
-  ret_6plus2longlong_test_function_type f = (ret_6plus2longlong_test_function_type)ptr;
-  reg_pack_longlong_test_type a = {10, 35};
-  reg_pack_longlong_test_type r;
-  r = f(0, 0, 0, 0, 0, a, 37, 38);
-  return ((r.x == 37) && (r.y == 37)) ? 0 : -1;
-}
-
-static int ret_6plus2longlong_test(void) {
-  const char *src =
-  "typedef struct reg_pack_longlong_test_type_s {long long x, y;} reg_pack_longlong_test_type;"
-  "reg_pack_longlong_test_type f(long long x1, long long x2, long long x3, long long x4, long long x5, reg_pack_longlong_test_type a, long long x8, long long x9) {\n"
-  "  reg_pack_longlong_test_type r = { x8, x8 };\n"
-  "  return r;\n"
-  "}\n";
-
-  return run_callback(src, ret_6plus2longlong_test_callback);
-}
-
-/*
- * sret_test: Create a struct large enough to be returned via sret
- * (hidden pointer as first function argument)
- */
-typedef struct sret_test_type_s {long long a, b, c;} sret_test_type;
-typedef sret_test_type (*sret_test_function_type) (sret_test_type);
-
-static int sret_test_callback(void *ptr) {
-  sret_test_function_type f = (sret_test_function_type)(ptr);
-  sret_test_type x = {5436LL, 658277698LL, 43878957LL};
-  sret_test_type r = f(x);
-  return ((r.a==x.a*35)&&(r.b==x.b*19)&&(r.c==x.c*21)) ? 0 : -1;
-}
-
-static int sret_test(void) {
-  const char *src =
-  "typedef struct sret_test_type_s {long long a, b, c;} sret_test_type;\n"
-  "sret_test_type f(sret_test_type x) {\n"
-  "  sret_test_type r = {x.a*35, x.b*19, x.c*21};\n"
-  "  return r;\n"
-  "}\n";
-  
-  return run_callback(src, sret_test_callback);
-}
-
-/*
- * one_member_union_test:
- * 
- * In the x86-64 ABI a union should always be passed on the stack. However
- * it appears that a single member union is treated by GCC as its member.
- */
-typedef union one_member_union_test_type_u {int x;} one_member_union_test_type;
-typedef one_member_union_test_type (*one_member_union_test_function_type) (one_member_union_test_type);
-
-static int one_member_union_test_callback(void *ptr) {
-  one_member_union_test_function_type f = (one_member_union_test_function_type)ptr;
-  one_member_union_test_type a, b;
-  a.x = 34;
-  b = f(a);
-  return (b.x == a.x*2) ? 0 : -1;
-}
-
-static int one_member_union_test(void) {
-  const char *src =
-  "typedef union one_member_union_test_type_u {int x;} one_member_union_test_type;\n"
-  "one_member_union_test_type f(one_member_union_test_type a) {\n"
-  "  one_member_union_test_type b;\n"
-  "  b.x = a.x * 2;\n"
-  "  return b;\n"
-  "}\n";
-  return run_callback(src, one_member_union_test_callback);
-}
-
-/*
- * two_member_union_test:
- * 
- * In the x86-64 ABI a union should always be passed on the stack.
- */
-typedef union two_member_union_test_type_u {int x; long y;} two_member_union_test_type;
-typedef two_member_union_test_type (*two_member_union_test_function_type) (two_member_union_test_type);
-
-static int two_member_union_test_callback(void *ptr) {
-  two_member_union_test_function_type f = (two_member_union_test_function_type)ptr;
-  two_member_union_test_type a, b;
-  a.x = 34;
-  b = f(a);
-  return (b.x == a.x*2) ? 0 : -1;
-}
-
-static int two_member_union_test(void) {
-  const char *src =
-  "typedef union two_member_union_test_type_u {int x; long y;} two_member_union_test_type;\n"
-  "two_member_union_test_type f(two_member_union_test_type a) {\n"
-  "  two_member_union_test_type b;\n"
-  "  b.x = a.x * 2;\n"
-  "  return b;\n"
-  "}\n";
-  return run_callback(src, two_member_union_test_callback);
-}
-
-/*
- * Win64 calling convention test.
- */
-
-typedef struct many_struct_test_type_s {long long a, b, c;} many_struct_test_type;
-typedef many_struct_test_type (*many_struct_test_function_type) (many_struct_test_type,many_struct_test_type,many_struct_test_type,many_struct_test_type,many_struct_test_type,many_struct_test_type);
- 
-static int many_struct_test_callback(void *ptr) {
-  many_struct_test_function_type f = (many_struct_test_function_type)ptr;
-  many_struct_test_type v = {1, 2, 3};
-  many_struct_test_type r = f(v,v,v,v,v,v);
-  return ((r.a == 6) && (r.b == 12) && (r.c == 18))?0:-1;
-}
-
-static int many_struct_test(void) {
-  const char *src =
-  "typedef struct many_struct_test_type_s {long long a, b, c;} many_struct_test_type;\n"
-  "many_struct_test_type f(many_struct_test_type x1, many_struct_test_type x2, many_struct_test_type x3, many_struct_test_type x4, many_struct_test_type x5, many_struct_test_type x6) {\n"
-  "  many_struct_test_type y;\n"
-  "  y.a = x1.a + x2.a + x3.a + x4.a + x5.a + x6.a;\n"
-  "  y.b = x1.b + x2.b + x3.b + x4.b + x5.b + x6.b;\n"
-  "  y.c = x1.c + x2.c + x3.c + x4.c + x5.c + x6.c;\n"
-  "  return y;\n"
-  "}\n";
-  return run_callback(src, many_struct_test_callback);
-}
-
-/*
- * Win64 calling convention test.
- */
-
-typedef struct many_struct_test_2_type_s {int a, b;} many_struct_test_2_type;
-typedef many_struct_test_2_type (*many_struct_test_2_function_type) (many_struct_test_2_type,many_struct_test_2_type,many_struct_test_2_type,many_struct_test_2_type,many_struct_test_2_type,many_struct_test_2_type);
- 
-static int many_struct_test_2_callback(void *ptr) {
-  many_struct_test_2_function_type f = (many_struct_test_2_function_type)ptr;
-  many_struct_test_2_type v = {1,2};
-  many_struct_test_2_type r = f(v,v,v,v,v,v);
-  return ((r.a == 6) && (r.b == 12))?0:-1;
-}
-
-static int many_struct_test_2(void) {
-  const char *src =
-  "typedef struct many_struct_test_2_type_s {int a, b;} many_struct_test_2_type;\n"
-  "many_struct_test_2_type f(many_struct_test_2_type x1, many_struct_test_2_type x2, many_struct_test_2_type x3, many_struct_test_2_type x4, many_struct_test_2_type x5, many_struct_test_2_type x6) {\n"
-  "  many_struct_test_2_type y;\n"
-  "  y.a = x1.a + x2.a + x3.a + x4.a + x5.a + x6.a;\n"
-  "  y.b = x1.b + x2.b + x3.b + x4.b + x5.b + x6.b;\n"
-  "  return y;\n"
-  "}\n";
-  return run_callback(src, many_struct_test_2_callback);
-}
-
-/*
- * Win64 calling convention test.
- */
-
-typedef struct many_struct_test_3_type_s {int a, b;} many_struct_test_3_type;
-typedef many_struct_test_3_type (*many_struct_test_3_function_type) (many_struct_test_3_type,many_struct_test_3_type,many_struct_test_3_type,many_struct_test_3_type,many_struct_test_3_type,many_struct_test_3_type, ...);
-typedef struct many_struct_test_3_struct_type { many_struct_test_3_function_type f; many_struct_test_3_function_type *f2; } many_struct_test_3_struct_type;
-
-static void many_struct_test_3_dummy(double d, ...)
-{
-  volatile double x = d;
-}
-
-static int many_struct_test_3_callback(void *ptr) {
-  many_struct_test_3_struct_type s = { ptr, };
-  many_struct_test_3_struct_type *s2 = &s;
-  s2->f2 = &s2->f;
-  many_struct_test_3_dummy(1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, &s2);
-  many_struct_test_3_function_type f = *(s2->f2);
-  many_struct_test_3_type v = {1,2};
-  many_struct_test_3_type r = (*((s2->f2=&f)+0))(v,v,v,v,v,v,1.0);
-  return ((r.a == 6) && (r.b == 12))?0:-1;
-}
-
-static int many_struct_test_3(void) {
-  const char *src =
-  "typedef struct many_struct_test_3_type_s {int a, b;} many_struct_test_3_type;\n"
-  "many_struct_test_3_type f(many_struct_test_3_type x1, many_struct_test_3_type x2, many_struct_test_3_type x3, many_struct_test_3_type x4, many_struct_test_3_type x5, many_struct_test_3_type x6, ...) {\n"
-  "  many_struct_test_3_type y;\n"
-  "  y.a = x1.a + x2.a + x3.a + x4.a + x5.a + x6.a;\n"
-  "  y.b = x1.b + x2.b + x3.b + x4.b + x5.b + x6.b;\n"
-  "  return y;\n"
-  "}\n";
-  return run_callback(src, many_struct_test_3_callback);
-}
-
-/*
- * stdarg_test: Test variable argument list ABI
- */
-
-typedef struct {long long a, b, c;} stdarg_test_struct_type;
-typedef void (*stdarg_test_function_type) (int,int,int,...);
-
-static int stdarg_test_callback(void *ptr) {
-  stdarg_test_function_type f = (stdarg_test_function_type)ptr;
-  int x;
-  double y;
-  stdarg_test_struct_type z = {1, 2, 3}, w;
-  f(10, 10, 5,
-    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, &x,
-    1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, &y,
-    z, z, z, z, z, &w);
-  return ((x == 55) && (y == 55) && (w.a == 5) && (w.b == 10) && (w.c == 15)) ? 0 : -1;
-}
-
-static int stdarg_test(void) {
-  const char *src =
-  "#include <stdarg.h>\n"
-  "typedef struct {long long a, b, c;} stdarg_test_struct_type;\n"
-  "void f(int n_int, int n_float, int n_struct, ...) {\n"
-  "  int i, ti = 0;\n"
-  "  double td = 0.0;\n"
-  "  stdarg_test_struct_type ts = {0,0,0}, tmp;\n"
-  "  va_list ap;\n"
-  "  va_start(ap, n_struct);\n"
-  "  for (i = 0, ti = 0; i < n_int; ++i)\n"
-  "    ti += va_arg(ap, int);\n"
-  "  *va_arg(ap, int*) = ti;\n"
-  "  for (i = 0, td = 0; i < n_float; ++i)\n"
-  "    td += va_arg(ap, double);\n"
-  "  *va_arg(ap, double*) = td;\n"
-  "  for (i = 0; i < n_struct; ++i) {\n"
-  "    tmp = va_arg(ap, stdarg_test_struct_type);\n"
-  "    ts.a += tmp.a; ts.b += tmp.b; ts.c += tmp.c;"
-  "  }\n"
-  "  *va_arg(ap, stdarg_test_struct_type*) = ts;\n"
-  "  va_end(ap);"
-  "}\n";
-  return run_callback(src, stdarg_test_callback);
-}
-
-typedef struct {long long a, b;} stdarg_many_test_struct_type;
-typedef void (*stdarg_many_test_function_type) (int, int, int, int, int,
-						stdarg_many_test_struct_type,
-						int, int, ...);
-
-static int stdarg_many_test_callback(void *ptr)
-{
-  stdarg_many_test_function_type f = (stdarg_many_test_function_type)ptr;
-  int x;
-  stdarg_many_test_struct_type l = {10, 11};
-  f(1, 2, 3, 4, 5, l, 6, 7, &x, 44);
-  return x == 44 ? 0 : -1;
-}
-
-static int stdarg_many_test(void)
-{
-  const char *src =
-  "#include <stdarg.h>\n"
-  "typedef struct {long long a, b;} stdarg_many_test_struct_type;\n"
-  "void f (int a, int b, int c, int d, int e, stdarg_many_test_struct_type l, int f, int g, ...){\n"
-  "  va_list ap;\n"
-  "  int *p;\n"
-  "  va_start (ap, g);\n"
-  "  p = va_arg(ap, int*);\n"
-  "  *p = va_arg(ap, int);\n"
-  "  va_end (ap);\n"
-  "}\n";
-  return run_callback(src, stdarg_many_test_callback);
-}
-
-/*
- * Test Win32 stdarg handling, since the calling convention will pass a pointer
- * to the struct and the stdarg pointer must point to that pointer initially.
- */
-
-typedef struct {long long a, b, c;} stdarg_struct_test_struct_type;
-typedef int (*stdarg_struct_test_function_type) (stdarg_struct_test_struct_type a, ...);
-
-static int stdarg_struct_test_callback(void *ptr) {
-  stdarg_struct_test_function_type f = (stdarg_struct_test_function_type)ptr;
-  stdarg_struct_test_struct_type v = {10, 35, 99};
-  int x = f(v, 234);
-  return (x == 378) ? 0 : -1;
-}
-
-static int stdarg_struct_test(void) {
-  const char *src =
-  "#include <stdarg.h>\n"
-  "typedef struct {long long a, b, c;} stdarg_struct_test_struct_type;\n"
-  "int f(stdarg_struct_test_struct_type a, ...) {\n"
-  "  va_list ap;\n"
-  "  va_start(ap, a);\n"
-  "  int z = va_arg(ap, int);\n"
-  "  va_end(ap);\n"
-  "  return z + a.a + a.b + a.c;\n"
-  "}\n";
-  return run_callback(src, stdarg_struct_test_callback);
-}
-
-/* Test that x86-64 arranges the stack correctly for arguments with alignment >8 bytes */
-
-typedef LONG_DOUBLE (*arg_align_test_callback_type) (LONG_DOUBLE,int,LONG_DOUBLE,int,LONG_DOUBLE);
-
-static int arg_align_test_callback(void *ptr) {
-  arg_align_test_callback_type f = (arg_align_test_callback_type)ptr;
-  long double x = f(12, 0, 25, 0, 37);
-  return (x == 74) ? 0 : -1;
-}
-
-static int arg_align_test(void) {
-  const char *src = 
-  "long double f(long double a, int b, long double c, int d, long double e) {\n"
-  "  return a + c + e;\n"
-  "}\n";
-  return run_callback(src, arg_align_test_callback);
-}
-
-#define RUN_TEST(t) \
-  if (!testname || (strcmp(#t, testname) == 0)) { \
-    fputs(#t "... ", stdout); \
-    fflush(stdout); \
-    if (t() == 0) { \
-      fputs("success\n", stdout); \
-    } else { \
-      fputs("failure\n", stdout); \
-      retval = EXIT_FAILURE; \
-    } \
-  }
-
-int main(int argc, char **argv) {
-  int i;
-  const char *testname = NULL;
-  int retval = EXIT_SUCCESS;
-  
-  /* if tcclib.h and libtcc1.a are not installed, where can we find them */
-  for (i = 1; i < argc; ++i) {
-    if (!memcmp(argv[i], "run_test=", 9))
-      testname = argv[i] + 9;
-  }
-
-  g_argv = argv, g_argc = argc;
-
-  RUN_TEST(ret_int_test);
-  RUN_TEST(ret_longlong_test);
-  RUN_TEST(ret_float_test);
-  RUN_TEST(ret_double_test);
-  RUN_TEST(ret_longdouble_test);
-  RUN_TEST(ret_2float_test);
-  RUN_TEST(ret_2double_test);
-  RUN_TEST(ret_8plus2double_test);
-  RUN_TEST(ret_6plus2longlong_test);
-#if !defined __x86_64__ || defined _WIN32
-  /* currently broken on x86_64 linux */
-  RUN_TEST(ret_mixed_test);
-  RUN_TEST(ret_mixed2_test);
-#endif
-  RUN_TEST(ret_mixed3_test);
-  RUN_TEST(reg_pack_test);
-  RUN_TEST(reg_pack_longlong_test);
-  RUN_TEST(sret_test);
-  RUN_TEST(one_member_union_test);
-  RUN_TEST(two_member_union_test);
-  RUN_TEST(many_struct_test);
-  RUN_TEST(many_struct_test_2);
-  RUN_TEST(many_struct_test_3);
-  RUN_TEST(stdarg_test);
-  RUN_TEST(stdarg_many_test);
-  RUN_TEST(stdarg_struct_test);
-  RUN_TEST(arg_align_test);
-  return retval;
-}
diff --git a/tinyc/tests/asmtest.S b/tinyc/tests/asmtest.S
deleted file mode 100644
index 55787054a..000000000
--- a/tinyc/tests/asmtest.S
+++ /dev/null
@@ -1,975 +0,0 @@
-# gas comment with ``gnu'' style quotes
-
-/* some directive tests */
-
-   .byte 0xff
-   .byte 1, 2, 3
-   .short 1, 2, 3
-   .word 1, 2, 3
-   .long 1, 2, 3
-   .int 1, 2, 3
-   .align 8
-   .byte 1
-/* .align 16, 0x90 gas is too clever for us with 0x90 fill */
-   .balign 4, 0x92
-   .align 16, 0x91 /* 0x91 tests the non-clever behaviour */
-   .skip 3
-   .skip 15, 0x90
-   .string "hello\0world"
-/* Macro expansion should work like with C, the #n shouldn't be parsed
-   as asm line comment */
-#define __stringify(n) #n
-#define stringify(n) __stringify(n)
-   .skip 8,0x90
-   .asciz stringify(BLA)
-   .skip 8,0x90
-
-# 28 "asmtest.S"        # a line directive (and a line comment)
-        movl %eax, %ebx # some more asm comment
-/* some label tests */
-L1:
-        movl %eax, %ebx
-        mov 0x10000, %eax
-L2:
-        movl $L2 - L1, %ecx
-var1:
-        nop ; nop ; nop ; nop
-
-        mov var1, %eax
-
-/* instruction tests */
-movl %eax, %ebx
-mov 0x10000, %eax
-mov 0x10000, %ax
-mov 0x10000, %al
-mov %al, 0x10000
-                
-mov $1, %edx
-mov $1, %dx
-mov $1, %cl
-movb $2, 0x100(%ebx,%edx,2)
-movw $2, 0x100(%ebx,%edx,2)
-movl $2, 0x100(%ebx,%edx,2)
-movl %eax, 0x100(%ebx,%edx,2)
-movl 0x100(%ebx,%edx,2), %edx
-movw %ax, 0x100(%ebx,%edx,2)
-
-movw $0x1122,%si
-movl $0x112233,%edx
-movl $0x80000000, %esi
-movl $-0x7fffffff, %edi
-#ifdef __x86_64__
-mov $0x11223344,%rbx
-movq $0x11223344,%rbx
-mov $0x1122334455,%rbx
-movq $0x1122334455,%rbx
-movl $0x11334455,(%rbx)
-#endif
-
-mov %eax, 0x12(,%edx,2)
-        
-#ifdef __i386__
-mov %cr3, %edx
-mov %ecx, %cr3
-movl %cr3, %eax
-movl %tr3, %eax
-movl %db3, %ebx
-movl %dr6, %eax
-#else
-mov %cr3, %rdx
-mov %rcx, %cr3
-movq %cr3, %rax
-movq %db3, %rbx
-movq %dr6, %rax
-mov %cr8, %rsi
-mov %rdi, %cr8
-#endif
-movl %fs, %ecx
-movl %ebx, %fs
-
-#ifdef __x86_64__
-movq %r8, %r9
-movq %r10, %r11
-movq %r12, %r13
-movq %r14, %r15
-movq %rax, %r9
-movq %r15, %rsi
-inc %r9b
-dec %r10w
-not %r11d
-negq %r12
-decb %r13b
-incw %r14w
-notl %r15d
-#endif
-
-     movsbl 0x1000, %eax
-     movsbw 0x1000, %ax
-     movswl 0x1000, %eax
-
-     movzbl 0x1000, %eax
-     movzbw 0x1000, %ax
-     movzwl 0x1000, %eax
-            
-     movzb 0x1000, %eax
-     movzb 0x1000, %ax
-                
-     mov $0x12345678,%eax
-
-#ifdef __x86_64__
-     movzb 0x1000, %rax
-     movzbq 0x1000, %rbx
-     movsbq 0x1000, %rdx
-     movzwq 0x1000, %rdi
-     movswq 0x1000, %rdx
-     movslq %eax, %rcx
-     mov $0x12345678,%rax
-     mov $0x12345678,%rdx
-     mov $0x12345678,%r10
-     mov $0x123456789abcdef0,%rax
-     mov $0x123456789abcdef0,%rcx
-     mov $0x123456789abcdef0,%r11
-#endif
-        
-#ifdef __i386__
-  pushl %eax
-  push %eax
-  push %cs
-#else
-  pushq %rax
-  push %rax
-#endif
-  pushw %ax
-  push %gs
-  push $1
-  push $100
-  push 0x42(%eax)
-  pop 0x43(%esi)
-                                                
-#ifdef __i386__
-  popl %eax
-  pop %eax
-  pop %ds
-#else
-  popq %rax
-  pop %rax
-#endif
-  popw %ax
-  pop %fs
-          
-  xchg %eax, %ecx
-  xchg %edx, %eax
-  xchg %bx, 0x10000
-  xchg 0x10000, %ebx
-  xchg 0x10000, %dl
-
-  in $100, %al               
-  in $100, %ax               
-  in $100, %eax
-  in %dx, %al
-  in %dx, %ax               
-  in %dx, %eax
-  inb %dx
-  inw %dx               
-  inl %dx
-
-  out %al, $100                       
-  out %ax, $100                       
-  out %eax, $100                       
-
-  /* NOTE: gas is bugged here, so size must be added */
-  outb %al, %dx                       
-  outw %ax, %dx                       
-  outl %eax, %dx                       
-
-  leal 0x1000(%ebx), %ecx
-  lea 0x1000(%ebx), %ecx
-
-#ifdef __i386__
-  les 0x2000, %eax
-  lds 0x2000, %ebx
-  lss 0x2000, %edx
-#endif
-  lfs 0x2000, %ecx
-  lgs 0x2000, %edx
-
-addl $0x123, %eax
-add $0x123, %ebx
-add $-16, %ecx
-add $-0x123, %esi
-add $1, %bx
-add $1, %ebx
-add $-1, %bx
-add $-1, %ebx
-add $127, %bx
-addl $127, %ebx
-addl $-128, %ebx
-addl $-128, %ebx
-addl $-129, %ebx
-addl $128, %ebx
-addl $255, %ebx
-addl $256, %ebx
-andb $0xf, %ah
-andb $-15, %cl
-xorb $127, %dh
-cmpb $42, (%eax)
-addl $0x123, 0x100
-addl $0x123, 0x100(%ebx)
-addl $0x123, 0x100(%ebx,%edx,2)
-addl $0x123, 0x100(%esp)
-addl $0x123, (3*8)(%esp)
-addl $0x123, (%ebp)
-addl $0x123, (%esp)
-cmpl $0x123, (%esp)
-
-#ifdef __x86_64__
-xor %bl,%ah
-xor %bl,%r8b
-xor %r9b,%bl
-xor %sil,%cl
-add %eax,(%r8d)
-add %ebx,(%r9)
-add %edx,(%r10d,%r11d)
-add %ecx,(%r12,%r13)
-add %esi,(%r14,%r15,4)
-add %edi,0x1000(%rbx,%r12,8)
-add %r11,0x1000(%ebp,%r9d,8)
-movb $12, %ah
-movb $13, %bpl
-movb $14, %dil
-movb $15, %r12b
-#endif
-
-add %eax, (%ebx)
-add (%ebx), %eax
-                
-or %dx, (%ebx)
-or (%ebx), %si
-        
-add %cl, (%ebx)
-add (%ebx), %dl
-
-    inc %edx
-    incl 0x10000
-    incb 0x10000
-    dec %dx
-  
-  test $1, %al
-  test $1, %cl
-
-  testl $1, 0x1000
-  testb $1, 0x1000
-  testw $1, 0x1000
-  test %eax, %ebx
-  test %eax, 0x1000
-  test 0x1000, %edx
-
-    not %edx
-    notw 0x10000
-    notl 0x10000
-    notb 0x10000
-
-    neg %edx
-    negw 0x10000
-    negl 0x10000
-    negb 0x10000
-
-    imul %ecx
-    mul %edx
-    mulb %cl
-
-    imul %eax, %ecx
-    imul 0x1000, %cx
-    imul $10, %eax, %ecx
-    imul $10, %ax, %cx
-    imul $10, %eax
-    imul $0x1100000, %eax
-    imul $1, %eax
-    
-    idivw 0x1000
-    div %ecx
-    div %bl
-    div %ecx, %eax
-
-and $15,%bx
-and $-20,%edx
-
-shl %edx
-shl $10, %edx
-shl %cl, %edx
-
-shld $1, %eax, %edx
-shld %cl, %eax, %edx
-shld %eax, %edx
-
-shrd $1, %eax, %edx
-shrd %cl, %eax, %edx
-shrd %eax, %edx
-
-L4:
-call 0x1000
-call L4
-#ifdef __i386__
-call *%eax
-#else
-call *%rax
-#endif
-call *0x1000
-call func1
-
-.global L5,L6
-
-L5:
-L6:
-
-#ifdef __i386__
-lcall $0x100, $0x1000
-#else
-lcall *0x100
-lcall *(%rax)
-#endif
-
-jmp 0x1000
-jmp *(%edi)
-#ifdef __i386__
-jmp *%eax
-#else
-jmp *%rax
-#endif
-jmp *0x1000
-
-#ifdef __i386__
-ljmp $0x100, $0x1000
-#else
-ljmp *0x100
-ljmp *(%rdi)
-ljmpl *(%esi)
-ljmpw *(%esi)
-#endif
-
-ret
-ret $10
-#ifdef __i386__
-retl
-retl $10
-#else
-retq
-retq $10
-#endif
-
-lret
-
-lret $10
-
-enter $1234, $10
-
-L3:
- jo 0x1000
- jnp 0x1001
- jne 0x1002
- jg 0x1003
-
- jo L3
- jnp L3
- jne L3
- jg L3
-
- loopne L3
- loopnz L3
- loope L3
- loopz L3
- loop L3
- jecxz L3
-
-        
- seto %al
- setc %al
- setcb %al
- setnp 0x1000
- setl 0xaaaa
- setg %dl
-
- fadd
- fadd %st(1), %st
- fadd %st(0), %st(1)
- fadd %st(3)
-
- fmul %st(0),%st(0)
- fmul %st(0),%st(1)
-
- faddp %st(5)
- faddp
- faddp %st(1), %st
-
- fadds 0x1000
- fiadds 0x1002
- faddl 0x1004
- fiaddl 0x1006
-
- fmul
- fmul %st(1), %st
- fmul %st(3)
-
- fmulp %st(5)
- fmulp
- fmulp %st(1), %st
-
- fmuls 0x1000
- fimuls 0x1002
- fmull 0x1004
- fimull 0x1006
-
- fsub
- fsub %st(1), %st
- fsub %st(3)
-
- fsubp %st(5)
- fsubp
- fsubp %st(1), %st
-
- fsubs 0x1000
- fisubs 0x1002
- fsubl 0x1004
- fisubl 0x1006
-
- fsubr
- fsubr %st(1), %st
- fsubr %st(3)
-
- fsubrp %st(5)
- fsubrp
- fsubrp %st(1), %st
-
- fsubrs 0x1000
- fisubrs 0x1002
- fsubrl 0x1004
- fisubrl 0x1006
-
- fdiv
- fdiv %st(1), %st
- fdiv %st(3)
-
- fdivp %st(5)
- fdivp
- fdivp %st(1), %st
-
- fdivs 0x1000
- fidivs 0x1002
- fdivl 0x1004
- fidivl 0x1006
-
- fcom %st(3)
-
- fcoms 0x1000
- ficoms 0x1002
- fcoml 0x1004
- ficoml 0x1006
-
- fcomp %st(5)
- fcomp
- fcompp
-
- fcomps 0x1000
- ficomps 0x1002
- fcompl 0x1004
- ficompl 0x1006
-
- fld %st(5)
- fldl 0x1000
- flds 0x1002
- fildl 0x1004
- fst %st(4)
- fstp %st(6)
- fstpt 0x1006
- fbstp 0x1008
-
- fxch
- fxch %st(4)
-
- fucom %st(6)
- fucomp %st(3)
- fucompp
-
- finit
- fninit
- fldcw 0x1000
- fnstcw 0x1002
- fstcw 0x1002
- fnstsw 0x1004
- fnstsw (%eax)
- fstsw 0x1004
- fstsw (%eax)
- fnclex
- fclex
- fnstenv 0x1000
- fstenv 0x1000
- fldenv 0x1000
- fnsave 0x1002
- fsave 0x1000
- frstor 0x1000
- ffree %st(7)
- ffreep %st(6)
- 
-    ftst
-    fxam
-    fld1
-    fldl2t
-    fldl2e
-    fldpi
-    fldlg2
-    fldln2
-    fldz
-
-    f2xm1
-    fyl2x
-    fptan
-    fpatan
-    fxtract
-    fprem1
-    fdecstp
-    fincstp
-    fprem
-    fyl2xp1
-    fsqrt
-    fsincos
-    frndint
-    fscale
-    fsin
-    fcos
-    fchs
-    fabs
-    fnop
-    fwait
-
-bswap %edx
-bswapl %ecx
-xadd %ecx, %edx
-xaddb %dl, 0x1000
-xaddw %ax, 0x1000
-xaddl %eax, 0x1000
-cmpxchg %ecx, %edx
-cmpxchgb %dl, 0x1000
-cmpxchgw %ax, 0x1000
-cmpxchgl %eax, 0x1000
-invlpg 0x1000
-cmpxchg8b 0x1002
-#ifdef __x86_64__
-cmpxchg16b (%rax)
-cmpxchg16b (%r10,%r11)
-#endif
-
-fcmovb %st(5), %st
-fcmove %st(5), %st
-fcmovbe %st(5), %st
-fcmovu %st(5), %st
-fcmovnb %st(5), %st
-fcmovne %st(5), %st
-fcmovnbe %st(5), %st
-fcmovnu %st(5), %st
-fcomi %st(5), %st
-fucomi %st(5), %st
-fcomip %st(5), %st
-fucomip %st(5), %st
-
-
-
- cmovo 0x1000, %eax
- cmovs 0x1000, %eax
- cmovns %edx, %edi
- cmovne %ax, %si
-#ifdef __x86_64__
- bswapq %rsi
- bswapq %r10
- cmovz %rdi,%rbx
-#endif
-
-int $3
-int $0x10
-
-#ifdef __i386__
-    pusha
-    popa
-#endif
-    clc # another comment
-    cld # a comment with embedded ' tick
-    cli
-    clts
-    cmc
-    lahf
-    sahf
-#ifdef __i386__
-    pushfl
-    popfl
-#else
-    pushfq
-    popfq
-#endif
-    pushf
-    popf
-    stc
-    std
-    sti
-#ifdef __i386__
-    aaa
-    aas
-    daa
-    das
-    aad
-    aam
-    into
-#endif
-    cbw
-    cwd
-    cwde
-    cdq
-    cbtw
-    cwtd
-    cwtl
-    cltd
-    leave
-    int3
-    iret
-    rsm
-    hlt
-    wait
-    nop
-
-    /* XXX: handle prefixes */
-#if 0
-    aword
-    addr16
-#endif
-    lock
-    rep
-    repe
-    repz
-    repne
-    repnz
-    nop
-
-    lock ;negl (%eax)
-    wait ;pushf
-    rep  ;stosb
-    repe ;lodsb
-    repz ;cmpsb
-    repne;movsb
-    repnz;outsb
-
-    /* handle one-line prefix + ops */
-    lock  negl (%eax)
-    wait  pushf
-    rep   stosb
-    repe  lodsb
-    repz  cmpsb
-    repne movsb
-    repnz outsb
-    
-    invd
-    wbinvd
-    cpuid
-    wrmsr
-    rdtsc
-    rdmsr
-    rdpmc
-    ud2
-#ifdef __x86_64__
-    syscall
-    sysret
-    sysretq
-    lfence
-    mfence
-    sfence
-    prefetchnta 0x18(%rdx)
-    prefetcht0 (%rcx)
-    prefetcht1 (%rsi)
-    prefetcht2 (%rdi)
-    prefetchw (%rdi)
-    clflush 0x1000(%rax,%rcx)
-    fxsaveq (%rdx)
-    fxsaveq (%r11)
-    fxrstorq (%rcx)
-    fxrstorq (%r10)
-
-#endif
-
-    lar %ax,%dx
-    lar %eax,%dx
-    lar %ax,%edx
-    lar %eax,%edx
-#ifdef __x86_64__
-    lar %ax,%rdx
-    lar %eax,%rdx
-#endif
-    emms
-    movd %edx, %mm3
-    movd 0x1000, %mm2
-    movd %mm4, %ecx
-    movd %mm5, 0x1000
-                    
-    movq 0x1000, %mm2
-    movq %mm4, 0x1000
-    
-    pand 0x1000, %mm3
-    pand %mm4, %mm5
-    
-    psllw $1, %mm6
-    psllw 0x1000, %mm7
-    psllw %mm2, %mm7
-
-    xlat
-    cmpsb
-    scmpw
-    insl
-    outsw
-    lodsb
-    slodl
-    movsb
-    movsl
-    smovb
-    scasb
-    sscaw
-    stosw
-    sstol
-
-    bsf 0x1000, %ebx
-    bsr 0x1000, %ebx
-    bt %edx, 0x1000
-    btl $2, 0x1000
-    btc %edx, 0x1000
-    btcl $2, 0x1000
-    btr %edx, 0x1000
-    btrl $2, 0x1000
-    bts %edx, 0x1000
-    btsl $2, 0x1000
-
-        
-        
-#ifdef __i386__
-    boundl %edx, 0x10000
-    boundw %bx, 0x1000
-
-    arpl %bx, 0x1000
-#endif
-    lar 0x1000, %eax
-    lgdt 0x1000
-    lidt 0x1000
-    lldt 0x1000
-    sgdt 0x1000
-    sidt 0x1000
-    sldt 0x1000
-#ifdef __x86_64__
-    lgdtq 0x1000
-    lidtq 0x1000
-    sgdtq 0x1000
-    sidtq 0x1000
-
-    swapgs
-
-    str %rdx
-    str %r9
-#endif
-
-    lmsw 0x1000
-    lsl 0x1000, %ecx
-    ltr 0x1000
-    ltr %si
-    smsw 0x1000
-    str 0x1000
-    str %ecx
-    str %dx
-    
-    verr 0x1000
-    verw 0x1000
-  
-#ifdef __i386__
-    push %ds
-    pushw %ds
-    pushl %ds
-    pop %ds
-    popw %ds
-    popl %ds
-#endif
-    fxsave 1(%ebx)
-    fxrstor 1(%ecx)
-#ifdef __i386__
-    pushl $1
-#else
-    pushq $1
-#endif
-    pushw $1
-    push $1
-
-#ifdef __ASSEMBLER__ // should be defined, for S files
-    inc %eax
-#endif
-
-#ifndef _WIN32
-ft1: ft2: ft3: ft4: ft5: ft6: ft7: ft8: ft9:
-    xor %eax, %eax
-    ret
-
-.type ft1,STT_FUNC
-.type ft2,@STT_FUNC
-.type ft3,%STT_FUNC
-.type ft4,"STT_FUNC"
-.type ft5,function
-.type ft6,@function
-.type ft7,%function
-.type ft8,"function"
-#endif
-
-    pause
-.rept 6
-    nop
-.endr
-.fill 4,1,0x90
-
-.section .text.one,"ax"
-nop
-.previous
-.pushsection .text.one,"ax"
-nop
-.pushsection .text.two,"ax"
-nop
-.popsection
-.popsection
-
-1: ud2
-.pushsection __bug_table,"a"
-.align 8
-2: .long 1b - 2b
-   .long 0x600000 - 2b
-   .long 1b + 42
-   .long 43 + 1b
-   .long 2b + 144
-   .long 145 + 2b
-   .word 164, 0
-   .org 2b+32
-#ifdef __x86_64__
-   .quad 1b
-#else
-   .long 1b
-#endif
-.popsection
-3: mov %eax,%ecx
-4:
-.pushsection .text.three, "ax"
-nop
-.skip (-((4b-3b) > 0) * 2) , 0x90
-.popsection
-
-.globl overrideme
-.weak overrideme
-  nop
-.globl notimplemented
-notimplemented:
-  ret
-.set overrideme, notimplemented
-overrideme = notimplemented
-overrideme:
-  ret
-
-    movd %esi, %mm1
-    movd %edi, %xmm2
-    movd (%ebx), %mm3
-    movd (%ebx), %xmm3
-    movd %mm1, %esi
-    movd %xmm2, %edi
-    movd %mm3, (%edx)
-    movd %xmm3, (%edx)
-#ifdef __x86_64__
-    movd %rsi, %mm1
-    movd %rdi, %xmm2
-    movd (%rbx), %mm3
-    movd (%rbx), %xmm3
-    movd %mm1, %r12
-    movd %xmm2, %rdi
-    movd %mm3, (%r8)
-    movd %xmm3, (%r13)
-#endif
-
-    movq (%ebp), %mm1
-    movq %mm2, (%edi)
-    movq (%edi), %xmm3
-    movq %mm4, %mm5
-#ifdef __x86_64__
-    movq %rcx, %mm1
-    movq %rdx, %xmm2
-    movq %r13, %xmm3
-    /* movq mem64->xmm is encoded as f30f7e by GAS, but as
-       660f6e by tcc (which really is a movd and would need 
-       a REX.W prefix to be movq).  */
-    movq (%rsi), %xmm3
-    movq %mm1, %rdx
-    movq %xmm3, %rcx
-    movq %xmm4, (%rsi)
-#endif
-
-#define TEST_MMX_SSE(insn) \
-    insn %mm1, %mm2; \
-    insn %xmm2, %xmm3; \
-    insn (%ebx), %xmm3;
-#define TEST_MMX_SSE_I8(insn) \
-    TEST_MMX_SSE(insn) \
-    insn $0x42, %mm4; \
-    insn $0x42, %xmm4;
-
-    TEST_MMX_SSE(packssdw)
-    TEST_MMX_SSE(packsswb)
-    TEST_MMX_SSE(packuswb)
-    TEST_MMX_SSE(paddb)
-    TEST_MMX_SSE(paddw)
-    TEST_MMX_SSE(paddd)
-    TEST_MMX_SSE(paddsb)
-    TEST_MMX_SSE(paddsw)
-    TEST_MMX_SSE(paddusb)
-    TEST_MMX_SSE(paddusw)
-    TEST_MMX_SSE(pand)
-    TEST_MMX_SSE(pandn)
-    TEST_MMX_SSE(pcmpeqb)
-    TEST_MMX_SSE(pcmpeqw)
-    TEST_MMX_SSE(pcmpeqd)
-    TEST_MMX_SSE(pcmpgtb)
-    TEST_MMX_SSE(pcmpgtw)
-    TEST_MMX_SSE(pcmpgtd)
-    TEST_MMX_SSE(pmaddwd)
-    TEST_MMX_SSE(pmulhw)
-    TEST_MMX_SSE(pmullw)
-    TEST_MMX_SSE(por)
-    TEST_MMX_SSE(psllw)
-TEST_MMX_SSE_I8(psllw)
-    TEST_MMX_SSE(pslld)
-TEST_MMX_SSE_I8(pslld)
-    TEST_MMX_SSE(psllq)
-TEST_MMX_SSE_I8(psllq)
-    TEST_MMX_SSE(psraw)
-TEST_MMX_SSE_I8(psraw)
-    TEST_MMX_SSE(psrad)
-TEST_MMX_SSE_I8(psrad)
-    TEST_MMX_SSE(psrlw)
-TEST_MMX_SSE_I8(psrlw)
-    TEST_MMX_SSE(psrld)
-TEST_MMX_SSE_I8(psrld)
-    TEST_MMX_SSE(psrlq)
-TEST_MMX_SSE_I8(psrlq)
-    TEST_MMX_SSE(psubb)
-    TEST_MMX_SSE(psubw)
-    TEST_MMX_SSE(psubd)
-    TEST_MMX_SSE(psubsb)
-    TEST_MMX_SSE(psubsw)
-    TEST_MMX_SSE(psubusb)
-    TEST_MMX_SSE(psubusw)
-    TEST_MMX_SSE(punpckhbw)
-    TEST_MMX_SSE(punpckhwd)
-    TEST_MMX_SSE(punpckhdq)
-    TEST_MMX_SSE(punpcklbw)
-    TEST_MMX_SSE(punpcklwd)
-    TEST_MMX_SSE(punpckldq)
-    TEST_MMX_SSE(pxor)
-
-    cvtpi2ps %mm1, %xmm2
-    cvtpi2ps (%ebx), %xmm2
-    TEST_MMX_SSE(pmaxsw)
-    TEST_MMX_SSE(pmaxub)
-    TEST_MMX_SSE(pminsw)
-    TEST_MMX_SSE(pminub)
diff --git a/tinyc/tests/boundtest.c b/tinyc/tests/boundtest.c
deleted file mode 100644
index 15bffb4ed..000000000
--- a/tinyc/tests/boundtest.c
+++ /dev/null
@@ -1,285 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#define NB_ITS 1000000
-//#define NB_ITS 1
-#define TAB_SIZE 100
-
-int tab[TAB_SIZE];
-int ret_sum;
-char tab3[256];
-
-int test1(void)
-{
-    int i, sum = 0;
-    for(i=0;i<TAB_SIZE;i++) {
-        sum += tab[i];
-    }
-    return sum;
-}
-
-/* error */
-int test2(void)
-{
-    int i, sum = 0;
-    for(i=0;i<TAB_SIZE + 1;i++) {
-        sum += tab[i];
-    }
-    return sum;
-}
-
-/* actually, profiling test */
-int test3(void)
-{
-    int sum;
-    int i, it;
-
-    sum = 0;
-    for(it=0;it<NB_ITS;it++) {
-        for(i=0;i<TAB_SIZE;i++) {
-            sum += tab[i];
-        }
-    }
-    return sum;
-}
-
-/* ok */
-int test4(void)
-{
-    int i, sum = 0;
-    int *tab4;
-
-    fprintf(stderr, "%s start\n", __FUNCTION__);
-
-    tab4 = malloc(20 * sizeof(int));
-    for(i=0;i<20;i++) {
-        sum += tab4[i];
-    }
-    free(tab4);
-
-    fprintf(stderr, "%s end\n", __FUNCTION__);
-    return sum;
-}
-
-/* error */
-int test5(void)
-{
-    int i, sum = 0;
-    int *tab4;
-
-    fprintf(stderr, "%s start\n", __FUNCTION__);
-
-    tab4 = malloc(20 * sizeof(int));
-    for(i=0;i<21;i++) {
-        sum += tab4[i];
-    }
-    free(tab4);
-
-    fprintf(stderr, "%s end\n", __FUNCTION__);
-    return sum;
-}
-
-/* error */
-/* XXX: currently: bug */
-int test6(void)
-{
-    int i, sum = 0;
-    int *tab4;
-    
-    tab4 = malloc(20 * sizeof(int));
-    free(tab4);
-    for(i=0;i<21;i++) {
-        sum += tab4[i];
-    }
-
-    return sum;
-}
-
-/* error */
-int test7(void)
-{
-    int i, sum = 0;
-    int *p;
-
-    for(i=0;i<TAB_SIZE + 1;i++) {
-        p = &tab[i];
-        if (i == TAB_SIZE)
-            printf("i=%d %x\n", i, p);
-        sum += *p;
-    }
-    return sum;
-}
-
-/* ok */
-int test8(void)
-{
-    int i, sum = 0;
-    int tab[10];
-
-    for(i=0;i<10;i++) {
-        sum += tab[i];
-    }
-    return sum;
-}
-
-/* error */
-int test9(void)
-{
-    int i, sum = 0;
-    char tab[10];
-
-    for(i=0;i<11;i++) {
-        sum += tab[i];
-    }
-    return sum;
-}
-
-/* ok */
-int test10(void)
-{
-    char tab[10];
-    char tab1[10];
-
-    memset(tab, 0, 10);
-    memcpy(tab, tab1, 10);
-    memmove(tab, tab1, 10);
-    return 0;
-}
-
-/* error */
-int test11(void)
-{
-    char tab[10];
-
-    memset(tab, 0, 11);
-    return 0;
-}
-
-/* error */
-int test12(void)
-{
-    void *ptr;
-    ptr = malloc(10);
-    free(ptr);
-    free(ptr);
-    return 0;
-}
-
-/* error */
-int test13(void)
-{
-    char pad1 = 0;
-    char tab[10];
-    char pad2 = 0;
-    memset(tab, 'a', sizeof(tab));
-    return strlen(tab);
-}
-
-int test14(void)
-{
-    char *p = alloca(TAB_SIZE);
-    memset(p, 'a', TAB_SIZE);
-    p[TAB_SIZE-1] = 0;
-    return strlen(p);
-}
-
-/* error */
-int test15(void)
-{
-    char *p = alloca(TAB_SIZE-1);
-    memset(p, 'a', TAB_SIZE);
-    p[TAB_SIZE-1] = 0;
-    return strlen(p);
-}
-
-/* ok */
-int test16()
-{
-    char *demo = "This is only a test.";
-    char *p;
-
-    fprintf(stderr, "%s start\n", __FUNCTION__);
-
-    p = alloca(16);
-    strcpy(p,"12345678901234");
-    printf("alloca: p is %s\n", p);
-
-    /* Test alloca embedded in a larger expression */
-    printf("alloca: %s\n", strcpy(alloca(strlen(demo)+1),demo) );
-
-    fprintf(stderr, "%s end\n", __FUNCTION__);
-}
-
-/* error */
-int test17()
-{
-    char *demo = "This is only a test.";
-    char *p;
-
-    fprintf(stderr, "%s start\n", __FUNCTION__);
-
-    p = alloca(16);
-    strcpy(p,"12345678901234");
-    printf("alloca: p is %s\n", p);
-
-    /* Test alloca embedded in a larger expression */
-    printf("alloca: %s\n", strcpy(alloca(strlen(demo)),demo) );
-
-    fprintf(stderr, "%s end\n", __FUNCTION__);
-}
-
-int (*table_test[])(void) = {
-    test1,
-    test2,
-    test3,
-    test4,
-    test5,
-    test6,
-    test7,
-    test8,
-    test9,
-    test10,
-    test11,
-    test12,
-    test13,
-    test14,
-    test15,
-    test16,
-    test17,
-};
-
-int main(int argc, char **argv)
-{
-    int index;
-    int (*ftest)(void);
-    int index_max = sizeof(table_test)/sizeof(table_test[0]);
-
-    if (argc < 2) {
-        printf(
-    	    "test TCC bound checking system\n"
-	    "usage: boundtest N\n"
-            "  1 <= N <= %d\n", index_max);
-        exit(1);
-    }
-
-    index = 0;
-    if (argc >= 2)
-        index = atoi(argv[1]) - 1;
-
-    if ((index < 0) || (index >= index_max)) {
-        printf("N is outside of the valid range (%d)\n", index);
-        exit(2);
-    }
-
-    /* well, we also use bounds on this ! */
-    ftest = table_test[index];
-    ftest();
-
-    return 0;
-}
-
-/*
- * without bound   0.77 s
- * with bounds    4.73
- */  
diff --git a/tinyc/tests/gcctestsuite.sh b/tinyc/tests/gcctestsuite.sh
deleted file mode 100644
index f3cc538de..000000000
--- a/tinyc/tests/gcctestsuite.sh
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/sh
-
-TESTSUITE_PATH=$HOME/gcc/gcc-3.2/gcc/testsuite/gcc.c-torture
-TCC="./tcc -B. -I. -DNO_TRAMPOLINES" 
-rm -f tcc.sum tcc.log
-nb_failed="0"
-
-for src in $TESTSUITE_PATH/compile/*.c ; do
-  echo $TCC -o /tmp/test.o -c $src 
-  $TCC -o /tmp/test.o -c $src >> tcc.log 2>&1
-  if [ "$?" = "0" ] ; then
-     result="PASS"
-  else
-     result="FAIL"
-     nb_failed=$(( $nb_failed + 1 ))
-  fi
-  echo "$result: $src"  >> tcc.sum
-done
-
-for src in $TESTSUITE_PATH/execute/*.c ; do
-  echo $TCC $src 
-  $TCC $src >> tcc.log 2>&1
-  if [ "$?" = "0" ] ; then
-     result="PASS"
-  else
-     result="FAIL"
-     nb_failed=$(( $nb_failed + 1 ))
-  fi
-  echo "$result: $src"  >> tcc.sum
-done
-
-echo "$nb_failed test(s) failed." >> tcc.sum
-echo "$nb_failed test(s) failed."
diff --git a/tinyc/tests/libtcc_test.c b/tinyc/tests/libtcc_test.c
deleted file mode 100644
index 480d31488..000000000
--- a/tinyc/tests/libtcc_test.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Simple Test program for libtcc
- *
- * libtcc can be useful to use tcc as a "backend" for a code generator.
- */
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "libtcc.h"
-
-/* this function is called by the generated code */
-int add(int a, int b)
-{
-    return a + b;
-}
-
-/* this strinc is referenced by the generated code */
-const char hello[] = "Hello World!";
-
-char my_program[] =
-"#include <tcclib.h>\n" /* include the "Simple libc header for TCC" */
-"extern int add(int a, int b);\n"
-"#ifdef _WIN32\n" /* dynamically linked data needs 'dllimport' */
-" __attribute__((dllimport))\n"
-"#endif\n"
-"extern const char hello[];\n"
-"int fib(int n)\n"
-"{\n"
-"    if (n <= 2)\n"
-"        return 1;\n"
-"    else\n"
-"        return fib(n-1) + fib(n-2);\n"
-"}\n"
-"\n"
-"int foo(int n)\n"
-"{\n"
-"    printf(\"%s\\n\", hello);\n"
-"    printf(\"fib(%d) = %d\\n\", n, fib(n));\n"
-"    printf(\"add(%d, %d) = %d\\n\", n, 2 * n, add(n, 2 * n));\n"
-"    return 0;\n"
-"}\n";
-
-int main(int argc, char **argv)
-{
-    TCCState *s;
-    int i;
-    int (*func)(int);
-
-    s = tcc_new();
-    if (!s) {
-        fprintf(stderr, "Could not create tcc state\n");
-        exit(1);
-    }
-
-    /* if tcclib.h and libtcc1.a are not installed, where can we find them */
-    for (i = 1; i < argc; ++i) {
-        char *a = argv[i];
-        if (a[0] == '-') {
-            if (a[1] == 'B')
-                tcc_set_lib_path(s, a+2);
-            else if (a[1] == 'I')
-                tcc_add_include_path(s, a+2);
-            else if (a[1] == 'L')
-                tcc_add_library_path(s, a+2);
-        }
-    }
-
-    /* MUST BE CALLED before any compilation */
-    tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
-
-    if (tcc_compile_string(s, my_program) == -1)
-        return 1;
-
-    /* as a test, we add symbols that the compiled program can use.
-       You may also open a dll with tcc_add_dll() and use symbols from that */
-    tcc_add_symbol(s, "add", add);
-    tcc_add_symbol(s, "hello", hello);
-
-    /* relocate the code */
-    if (tcc_relocate(s, TCC_RELOCATE_AUTO) < 0)
-        return 1;
-
-    /* get entry symbol */
-    func = tcc_get_symbol(s, "foo");
-    if (!func)
-        return 1;
-
-    /* run the code */
-    func(32);
-
-    /* delete the state */
-    tcc_delete(s);
-
-    return 0;
-}
diff --git a/tinyc/tests/pp/01.c b/tinyc/tests/pp/01.c
deleted file mode 100644
index 2fc3d7960..000000000
--- a/tinyc/tests/pp/01.c
+++ /dev/null
@@ -1,6 +0,0 @@
-#define hash_hash # ## #
-#define mkstr(a) # a
-#define in_between(a) mkstr(a)
-#define join(c, d) in_between(c hash_hash d)
-char p[] = join(x, y);
-// char p[] = "x ## y";
diff --git a/tinyc/tests/pp/01.expect b/tinyc/tests/pp/01.expect
deleted file mode 100644
index cf5b15340..000000000
--- a/tinyc/tests/pp/01.expect
+++ /dev/null
@@ -1 +0,0 @@
-char p[] = "x ## y";
diff --git a/tinyc/tests/pp/02.c b/tinyc/tests/pp/02.c
deleted file mode 100644
index feb1254e3..000000000
--- a/tinyc/tests/pp/02.c
+++ /dev/null
@@ -1,28 +0,0 @@
-#define x 3
-#define f(a) f(x * (a))
-#undef x
-#define x 2
-#define g f
-#define z z[0]
-#define h g(~
-#define m(a) a(w)
-#define w 0,1
-#define t(a) a
-#define p() int
-#define q(x) x
-#define r(x,y) x ## y
-#define str(x) # x
-f(y+1) + f(f(z)) % t(t(g)(0) + t)(1);
-g(x+(3,4)-w) | h 5) & m
-(f)^m(m);
-char c[2][6] = { str(hello), str() };
-/*
- * f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1);
- * f(2 * (2+(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1);
- * char c[2][6] = { "hello", "" };
- */
-#define L21 f(y+1) + f(f(z)) % t(t(g)(0) + t)(1);
-#define L22 g(x+(3,4)-w) | h 5) & m\
-(f)^m(m);
-L21
-L22
diff --git a/tinyc/tests/pp/02.expect b/tinyc/tests/pp/02.expect
deleted file mode 100644
index 8ae2eb9ea..000000000
--- a/tinyc/tests/pp/02.expect
+++ /dev/null
@@ -1,5 +0,0 @@
-f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1);
-f(2 * (2 +(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1);
-char c[2][6] = { "hello", "" };
-f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1);
-f(2 * (2 +(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1);
diff --git a/tinyc/tests/pp/03.c b/tinyc/tests/pp/03.c
deleted file mode 100644
index a659245ec..000000000
--- a/tinyc/tests/pp/03.c
+++ /dev/null
@@ -1,15 +0,0 @@
-#define str(s) # s
-#define xstr(s) str(s)
-#define debug(s, t) printf("x" # s "= %d, x" # t "= %s", \
-	x ## s, x ## t)
-#define INCFILE(n) vers ## n
-#define glue(a, b) a ## b
-#define xglue(a, b) glue(a, b)
-#define HIGHLOW "hello"
-#define LOW LOW ", world"
-debug(1, 2);
-fputs(str(strncmp("abc\0d", "abc", '\4') // this goes away
-	== 0) str(: @\n), s);
-\#include xstr(INCFILE(2).h)
-glue(HIGH, LOW);
-xglue(HIGH, LOW)
diff --git a/tinyc/tests/pp/03.expect b/tinyc/tests/pp/03.expect
deleted file mode 100644
index 44aad0ae1..000000000
--- a/tinyc/tests/pp/03.expect
+++ /dev/null
@@ -1,5 +0,0 @@
-printf("x" "1" "= %d, x" "2" "= %s", x1, x2);
-fputs("strncmp(\"abc\\0d\", \"abc\", '\\4') == 0" ": @\n", s);
-\#include "vers2.h"
-"hello";
-"hello" ", world"
diff --git a/tinyc/tests/pp/04.c b/tinyc/tests/pp/04.c
deleted file mode 100644
index 0068f3751..000000000
--- a/tinyc/tests/pp/04.c
+++ /dev/null
@@ -1,4 +0,0 @@
-#define foobar 1
-#define C(x,y) x##y
-#define D(x) (C(x,bar))
-D(foo)
diff --git a/tinyc/tests/pp/04.expect b/tinyc/tests/pp/04.expect
deleted file mode 100644
index 7c67b0104..000000000
--- a/tinyc/tests/pp/04.expect
+++ /dev/null
@@ -1 +0,0 @@
-(1)
diff --git a/tinyc/tests/pp/05.c b/tinyc/tests/pp/05.c
deleted file mode 100644
index 72749416c..000000000
--- a/tinyc/tests/pp/05.c
+++ /dev/null
@@ -1,7 +0,0 @@
-#define t(x,y,z) x ## y ## z
-#define xxx(s) int s[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,), \
-        t(10,,), t(,11,), t(,,12), t(,,) };
-
-int j[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,),
-	t(10,,), t(,11,), t(,,12), t(,,) };
-xxx(j)
diff --git a/tinyc/tests/pp/05.expect b/tinyc/tests/pp/05.expect
deleted file mode 100644
index 9f9be378f..000000000
--- a/tinyc/tests/pp/05.expect
+++ /dev/null
@@ -1,3 +0,0 @@
-int j[] = { 123, 45, 67, 89,
- 10, 11, 12, };
-int j[] = { 123, 45, 67, 89, 10, 11, 12, };
diff --git a/tinyc/tests/pp/06.c b/tinyc/tests/pp/06.c
deleted file mode 100644
index 28cfddece..000000000
--- a/tinyc/tests/pp/06.c
+++ /dev/null
@@ -1,5 +0,0 @@
-#define X(a,b, \
-	c,d) \
-	foo
-
-X(1,2,3,4)
diff --git a/tinyc/tests/pp/06.expect b/tinyc/tests/pp/06.expect
deleted file mode 100644
index 257cc5642..000000000
--- a/tinyc/tests/pp/06.expect
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tinyc/tests/pp/07.c b/tinyc/tests/pp/07.c
deleted file mode 100644
index b22b22bbc..000000000
--- a/tinyc/tests/pp/07.c
+++ /dev/null
@@ -1,4 +0,0 @@
-#define a() YES
-#define b() a
-b()
-b()()
diff --git a/tinyc/tests/pp/07.expect b/tinyc/tests/pp/07.expect
deleted file mode 100644
index ad0e20a66..000000000
--- a/tinyc/tests/pp/07.expect
+++ /dev/null
@@ -1,2 +0,0 @@
-a
-YES
diff --git a/tinyc/tests/pp/08.c b/tinyc/tests/pp/08.c
deleted file mode 100644
index 93018e11f..000000000
--- a/tinyc/tests/pp/08.c
+++ /dev/null
@@ -1,4 +0,0 @@
-// test macro expansion in arguments
-#define s_pos              s_s.s_pos
-#define foo(x) (x)
-foo(hej.s_pos)
diff --git a/tinyc/tests/pp/08.expect b/tinyc/tests/pp/08.expect
deleted file mode 100644
index 2b2e3eebd..000000000
--- a/tinyc/tests/pp/08.expect
+++ /dev/null
@@ -1 +0,0 @@
-(hej.s_s.s_pos)
diff --git a/tinyc/tests/pp/09.c b/tinyc/tests/pp/09.c
deleted file mode 100644
index 315b297cd..000000000
--- a/tinyc/tests/pp/09.c
+++ /dev/null
@@ -1,4 +0,0 @@
-#define C(a,b,c) a##b##c
-#define N(x,y) C(x,_,y)
-#define A_O aaaaoooo
-N(A,O)
diff --git a/tinyc/tests/pp/09.expect b/tinyc/tests/pp/09.expect
deleted file mode 100644
index adce0f95e..000000000
--- a/tinyc/tests/pp/09.expect
+++ /dev/null
@@ -1 +0,0 @@
-aaaaoooo
diff --git a/tinyc/tests/pp/10.c b/tinyc/tests/pp/10.c
deleted file mode 100644
index f180eff1e..000000000
--- a/tinyc/tests/pp/10.c
+++ /dev/null
@@ -1,10 +0,0 @@
-#define f(x) x
-#define g(x) f(x) f(x
-#define i(x) g(x)) g(x
-#define h(x) i(x))) i(x
-#define k(x) i(x))) i(x))))
-f(x)
-g(x))
-i(x)))
-h(x))))
-k(x))))
diff --git a/tinyc/tests/pp/10.expect b/tinyc/tests/pp/10.expect
deleted file mode 100644
index bd18e18e5..000000000
--- a/tinyc/tests/pp/10.expect
+++ /dev/null
@@ -1,5 +0,0 @@
-x
-x x
-x x x x
-x x x x x x x x
-x x x x x x x x))))
diff --git a/tinyc/tests/pp/11.c b/tinyc/tests/pp/11.c
deleted file mode 100644
index c0edf629c..000000000
--- a/tinyc/tests/pp/11.c
+++ /dev/null
@@ -1,31 +0,0 @@
-#define D1(s, ...) s
-#define D2(s, ...) s D1(__VA_ARGS__)
-#define D3(s, ...) s D2(__VA_ARGS__)
-#define D4(s, ...) s D3(__VA_ARGS__)
-
-D1(a)
-D2(a, b)
-D3(a, b, c)
-D4(a, b, c, d)
-
-x D4(a, b, c, d) y
-x D4(a, b, c) y
-x D4(a, b) y
-x D4(a) y
-x D4() y
-
-#define GNU_COMMA(X,Y...) X,## Y
-
-x GNU_COMMA(A,B,C) y
-x GNU_COMMA(A,B) y
-x GNU_COMMA(A) y
-x GNU_COMMA() y
-
-#define __sun_attr___noreturn__ __attribute__((__noreturn__))
-#define ___sun_attr_inner(__a) __sun_attr_##__a
-#define __sun_attr__(__a) ___sun_attr_inner __a
-#define __NORETURN __sun_attr__((__noreturn__))
-__NORETURN
-#define X(...)
-#define Y(...)  1 __VA_ARGS__ 2
-Y(X X() ())
diff --git a/tinyc/tests/pp/11.expect b/tinyc/tests/pp/11.expect
deleted file mode 100644
index 6b9806c6c..000000000
--- a/tinyc/tests/pp/11.expect
+++ /dev/null
@@ -1,15 +0,0 @@
-a
-a b
-a b c
-a b c d
-x a b c d y
-x a b c y
-x a b y
-x a y
-x y
-x A,B,C y
-x A,B y
-x A y
-x y
-__attribute__((__noreturn__))
-1 2
diff --git a/tinyc/tests/pp/12.S b/tinyc/tests/pp/12.S
deleted file mode 100644
index 597ccb4cb..000000000
--- a/tinyc/tests/pp/12.S
+++ /dev/null
@@ -1,8 +0,0 @@
-#define SRC(y...)                       \
-        9999: y;                        \
-        .section __ex_table, "a";       \
-        .long 9999b, 6001f      ;       \
-        // .previous
-
-    SRC(1: movw (%esi), %bx)
-6001:
diff --git a/tinyc/tests/pp/12.expect b/tinyc/tests/pp/12.expect
deleted file mode 100644
index 17a861c25..000000000
--- a/tinyc/tests/pp/12.expect
+++ /dev/null
@@ -1,2 +0,0 @@
-    9999: 1: movw (%esi), %bx; .section __ex_table, "a"; .long 9999b, 6001f ;
-6001:
diff --git a/tinyc/tests/pp/13.S b/tinyc/tests/pp/13.S
deleted file mode 100644
index bf0b525f9..000000000
--- a/tinyc/tests/pp/13.S
+++ /dev/null
@@ -1,6 +0,0 @@
-# `modelist' label. Each video mode record looks like:
-#ifdef AAA
-# modelist' label. Each video mode record looks like:
-#endif
-.text
-endtext:
diff --git a/tinyc/tests/pp/13.expect b/tinyc/tests/pp/13.expect
deleted file mode 100644
index c7a32305f..000000000
--- a/tinyc/tests/pp/13.expect
+++ /dev/null
@@ -1,2 +0,0 @@
-.text
-endtext:
diff --git a/tinyc/tests/pp/14.c b/tinyc/tests/pp/14.c
deleted file mode 100644
index e15468c5f..000000000
--- a/tinyc/tests/pp/14.c
+++ /dev/null
@@ -1,13 +0,0 @@
-#define W Z
-#define Z(X) W(X,2)
-#define Y(X) Z(X)
-#define X Y
-return X(X(1));
-
-#define P Q
-#define Q(n) P(n,2)
-return P(1);
-
-#define A (B * B)
-#define B (A + A)
-return A + B;
diff --git a/tinyc/tests/pp/14.expect b/tinyc/tests/pp/14.expect
deleted file mode 100644
index 37860440c..000000000
--- a/tinyc/tests/pp/14.expect
+++ /dev/null
@@ -1,3 +0,0 @@
-return Z(Z(1,2),2);
-return Q(1,2);
-return ((A + A) * (A + A)) + ((B * B) + (B * B));
diff --git a/tinyc/tests/pp/15.c b/tinyc/tests/pp/15.c
deleted file mode 100644
index cf13f9a89..000000000
--- a/tinyc/tests/pp/15.c
+++ /dev/null
@@ -1,18 +0,0 @@
-// insert a space between two tokens if otherwise they
-// would form a single token when read back
-
-#define n(x) x
-
-return (n(long)n(double))d;
-return n(A)n(++)n(+)n(B);
-return n(A)n(+)n(++)n(B);
-return n(A)n(++)n(+)n(+)n(B);
-
-// not a hex float
-return n(0x1E)n(-1);
-
-// unlike gcc but correct
-// XXX: return n(x)+n(x)-n(1)+n(1)-2;
-
-// unlike gcc, but cannot appear in valid C
-// XXX: return n(x)n(x)n(1)n(2)n(x);
diff --git a/tinyc/tests/pp/15.expect b/tinyc/tests/pp/15.expect
deleted file mode 100644
index b4f885e95..000000000
--- a/tinyc/tests/pp/15.expect
+++ /dev/null
@@ -1,5 +0,0 @@
-return (long double)d;
-return A+++B;
-return A+ ++B;
-return A+++ +B;
-return 0x1E -1;
diff --git a/tinyc/tests/pp/16.c b/tinyc/tests/pp/16.c
deleted file mode 100644
index 8b5b6428c..000000000
--- a/tinyc/tests/pp/16.c
+++ /dev/null
@@ -1,3 +0,0 @@
-/* The following should warn */
-#define A ...
-#define A <<=
diff --git a/tinyc/tests/pp/16.expect b/tinyc/tests/pp/16.expect
deleted file mode 100644
index 695d6d41c..000000000
--- a/tinyc/tests/pp/16.expect
+++ /dev/null
@@ -1,2 +0,0 @@
-
-16.c:3: warning: A redefined
diff --git a/tinyc/tests/pp/17.c b/tinyc/tests/pp/17.c
deleted file mode 100644
index f12196fe8..000000000
--- a/tinyc/tests/pp/17.c
+++ /dev/null
@@ -1,14 +0,0 @@
-#define STR1(u) # u
-#define pass(a) a
-#define __ASM_REG(reg)         STR1(one##reg)
-#define _ASM_DX         __ASM_REG(tok)
-X162 pass(__ASM_REG(tok))
-X161 pass(_ASM_DX)
-X163 pass(STR1(one##tok))
-
-X170 pass(x ## y)
-X171 pass(x pass(##) y)
-
-#define Y(x) Z(x)
-#define X Y
-X180 return X(X(1));
diff --git a/tinyc/tests/pp/17.expect b/tinyc/tests/pp/17.expect
deleted file mode 100644
index e95aafef8..000000000
--- a/tinyc/tests/pp/17.expect
+++ /dev/null
@@ -1,6 +0,0 @@
-X162 "onetok"
-X161 "onetok"
-X163 "one##tok"
-X170 x ## y
-X171 x ## y
-X180 return Z(Z(1));
diff --git a/tinyc/tests/pp/18.c b/tinyc/tests/pp/18.c
deleted file mode 100644
index 0961426ac..000000000
--- a/tinyc/tests/pp/18.c
+++ /dev/null
@@ -1,15 +0,0 @@
-#define M_RETI_ARG27(x,y,z,aa, ...)    aa
-#define M_RET_ARG27(...)        M_RETI_ARG27(__VA_ARGS__)
-#define M_COMMA_P(...)          M_RET_ARG27(__VA_ARGS__, 1, 1, 0, useless)
-#define M_EMPTYI_DETECT(...)    0, 1,
-#define M_EMPTYI_P_C1(...)      M_COMMA_P(M_EMPTYI_DETECT __VA_ARGS__ () )
-#define EX
-#define empty(x)
-#define fnlike(x) yeah x
-/* If the following macro is called with empty arg (X183), the use
-   of 'x' between fnlike and '(' doesn't hinder the recognition of this
-   being a further fnlike macro invocation.  */
-#define usefnlike(x) fnlike x (x)
-X181 M_EMPTYI_P_C1()
-X182 M_EMPTYI_P_C1(x)
-X183 usefnlike()
diff --git a/tinyc/tests/pp/18.expect b/tinyc/tests/pp/18.expect
deleted file mode 100644
index 447a9b2ed..000000000
--- a/tinyc/tests/pp/18.expect
+++ /dev/null
@@ -1,3 +0,0 @@
-X181 1
-X182 0
-X183 yeah
diff --git a/tinyc/tests/pp/19.c b/tinyc/tests/pp/19.c
deleted file mode 100644
index aa91abe0d..000000000
--- a/tinyc/tests/pp/19.c
+++ /dev/null
@@ -1,101 +0,0 @@
-#define M_C2I(a, ...)       a ## __VA_ARGS__
-#define M_C(a, ...)         M_C2I(a, __VA_ARGS__)
-#define M_C3I(a, b, ...)    a ## b ## __VA_ARGS__
-#define M_C3(a, b, ...)     M_C3I(a ,b, __VA_ARGS__)
-
-#define M_RETI_ARG2(a, b, ...)  b
-#define M_RET_ARG2(...)         M_RETI_ARG2(__VA_ARGS__)
-#define M_RETI_ARG27(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,aa, ...)    aa
-#define M_RET_ARG27(...)        M_RETI_ARG27(__VA_ARGS__)
-
-#define M_TOBOOLI_0                 1, 0,
-#define M_BOOL(x)                   M_RET_ARG2(M_C(M_TOBOOLI_, x), 1, useless)
-
-#define M_IFI_0(true_macro, ...)    __VA_ARGS__
-#define M_IFI_1(true_macro, ...)    true_macro
-#define M_IF(c)                     M_C(M_IFI_, M_BOOL(c))
-
-#define M_FLAT(...)                 __VA_ARGS__
-#define M_INVI_0                    1
-#define M_INVI_1                    0
-#define M_INV(x)                    M_C(M_INVI_, x)
-
-#define M_ANDI_00                   0
-#define M_ANDI_01                   0
-#define M_ANDI_10                   0
-#define M_ANDI_11                   1
-#define M_AND(x,y)                  M_C3(M_ANDI_, x, y)
-
-#define M_ORI_00                    0
-#define M_ORI_01                    1
-#define M_ORI_10                    1
-#define M_ORI_11                    1
-#define M_OR(x,y)                   M_C3(M_ORI_, x, y)
-
-#define M_COMMA_P(...)              M_RET_ARG27(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, useless)
-
-#define M_EMPTYI_DETECT(...)        0, 1,
-#define M_EMPTYI_P_C1(...)          M_COMMA_P(M_EMPTYI_DETECT __VA_ARGS__ ())
-#define M_EMPTYI_P_C2(...)          M_COMMA_P(M_EMPTYI_DETECT __VA_ARGS__)
-#define M_EMPTYI_P_C3(...)          M_COMMA_P(__VA_ARGS__ () )
-#define M_EMPTY_P(...)              M_AND(M_EMPTYI_P_C1(__VA_ARGS__), M_INV(M_OR(M_OR(M_EMPTYI_P_C2(__VA_ARGS__), M_COMMA_P(__VA_ARGS__)),M_EMPTYI_P_C3(__VA_ARGS__))))
-#define M_APPLY_FUNC2B(func, arg1, arg2)        \
-  M_IF(M_EMPTY_P(arg2))(,func(arg1, arg2))
-#define M_MAP2B_0(func, data, 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,...) \
-  M_APPLY_FUNC2B(func, data, a) M_APPLY_FUNC2B(func, data, b) M_APPLY_FUNC2B(func, data, c) \
-  M_APPLY_FUNC2B(func, data, d) M_APPLY_FUNC2B(func, data, e) M_APPLY_FUNC2B(func, data, f) \
-  M_APPLY_FUNC2B(func, data, g) M_APPLY_FUNC2B(func, data, h) M_APPLY_FUNC2B(func, data, i) \
-  M_APPLY_FUNC2B(func, data, j) M_APPLY_FUNC2B(func, data, k) M_APPLY_FUNC2B(func, data, l) \
-  M_APPLY_FUNC2B(func, data, m) M_APPLY_FUNC2B(func, data, n) M_APPLY_FUNC2B(func, data, o) \
-  M_APPLY_FUNC2B(func, data, p) M_APPLY_FUNC2B(func, data, q) M_APPLY_FUNC2B(func, data, r) \
-  M_APPLY_FUNC2B(func, data, s) M_APPLY_FUNC2B(func, data, t) M_APPLY_FUNC2B(func, data, u) \
-  M_APPLY_FUNC2B(func, data, v) M_APPLY_FUNC2B(func, data, w) M_APPLY_FUNC2B(func, data, x) \
-  M_APPLY_FUNC2B(func, data, y) M_APPLY_FUNC2B(func, data, z)
-#define M_MAP2B(f, ...) M_MAP2B_0(f, __VA_ARGS__, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , )
-#define M_INIT_INIT(a)           ,a,
-
-#define M_GET_METHOD(method, method_default, ...)                       \
-  M_RET_ARG2 (M_MAP2B(M_C, M_C3(M_, method, _), __VA_ARGS__), method_default,)
-
-#define M_TEST_METHOD_P(method, oplist)                         \
-  M_BOOL(M_GET_METHOD (method, 0, M_FLAT oplist))
-
-#define TRUE 1
-#define TEST1(n)                                \
-  M_IF(n)(ok,nok)
-#define TEST2(op)                               \
-  M_TEST_METHOD_P(INIT, op)
-#define TEST3(op)                               \
-  M_IF(M_TEST_METHOD_P(INIT, op))(ok, nok)
-#define TEST4(op) \
-  TEST1(TEST2(op))
-#define KO(a) ((void)1)
-
-/* This checks that the various expansions that ultimately lead to
-   something like 'KO(arg,arg)', where 'KO' comes from a macro
-   expansion reducing from a large macro chain do not are regarded
-   as funclike macro invocation of KO.  E.g. X93 and X94 expand to 'KO',
-   but X95 must not consume the (a,b) arguments outside the M_IF()
-   invocation to reduce the 'KO' macro to an invocation.  Instead
-   X95 should reduce via M_IF(KO)(a,b) to 'a'. 
-   
-   The other lines here are variations on this scheme, with X1 to
-   X6 coming from the bug report at
-   http://lists.nongnu.org/archive/html/tinycc-devel/2017-07/msg00017.html */
-X92 M_IF(KO)
-X93 M_GET_METHOD(INIT, 0, INIT(KO))
-X94 M_GET_METHOD(INIT, 0, M_FLAT (INIT(KO)))
-X95 M_IF(M_GET_METHOD(INIT, 0, INIT(KO)))(a,b)
-X96 M_IF(M_GET_METHOD(INIT, 0, M_FLAT (INIT(KO))))
-X97 M_IF(M_GET_METHOD(INIT, 0, M_FLAT (INIT(KO))))(ok,nok)
-X98 (M_TEST_METHOD_P(INIT, (INIT(KO))))(ok, nok)
-X99 M_IF(M_TEST_METHOD_P(INIT, (INIT(KO))))(ok, nok)
-// test begins
-X1 TEST1(TRUE)          // ==> expect ok, get ok
-// First test with a token which is not a macro
-X2 TEST2((INIT(ok)))    // ==> expect 1, get 1
-X3 TEST3((INIT(ok)))    // ==> expect ok, get ok
-// Then test with a token which is a macro, but should not be expanded.
-X4 TEST2((INIT(KO)))    // ==> expect 1, get 1
-X5 TEST4(INIT(KO))
-X6 TEST3((INIT(KO)))    // ==> expect ok, get "error: macro 'KO' used with too many args"
diff --git a/tinyc/tests/pp/19.expect b/tinyc/tests/pp/19.expect
deleted file mode 100644
index 08c08581e..000000000
--- a/tinyc/tests/pp/19.expect
+++ /dev/null
@@ -1,14 +0,0 @@
-X92 M_IFI_1
-X93 KO
-X94 KO
-X95 a
-X96 M_IFI_1
-X97 ok
-X98 (1)(ok, nok)
-X99 ok
-X1 ok
-X2 1
-X3 ok
-X4 1
-X5 nok
-X6 ok
diff --git a/tinyc/tests/pp/20.c b/tinyc/tests/pp/20.c
deleted file mode 100644
index 7944d62bc..000000000
--- a/tinyc/tests/pp/20.c
+++ /dev/null
@@ -1,13 +0,0 @@
-/* Various things I encountered while hacking the pre processor */
-#define wrap(x) x
-#define pr_warning(fmt, ...) printk(KERN_WARNING fmt, ##__VA_ARGS__)
-#define pr_warn(x,y) pr_warning(x,y)
-#define net_ratelimited_function(function, ...) function(__VA_ARGS__)
-X1 net_ratelimited_function(pr_warn, "pipapo", bla);
-X2 net_ratelimited_function(wrap(pr_warn), "bla", foo);
-#define two m n
-#define chain4(a,b,c,d) a ## b ## c ## d
-X2 chain4(two,o,p,q)
-X3 chain4(o,two,p,q)
-X4 chain4(o,p,two,q)
-X5 chain4(o,p,q,two)
diff --git a/tinyc/tests/pp/20.expect b/tinyc/tests/pp/20.expect
deleted file mode 100644
index d19405d74..000000000
--- a/tinyc/tests/pp/20.expect
+++ /dev/null
@@ -1,6 +0,0 @@
-X1 printk(KERN_WARNING "pipapo",bla);
-X2 printk(KERN_WARNING "bla",foo);
-X2 twoopq
-X3 otwopq
-X4 optwoq
-X5 opqtwo
diff --git a/tinyc/tests/pp/21.c b/tinyc/tests/pp/21.c
deleted file mode 100644
index 1316226a2..000000000
--- a/tinyc/tests/pp/21.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* accept 'defined' as result of substitution */
-
------ 1 ------
-#define AAA 2
-#define BBB
-#define CCC (defined ( AAA ) && AAA > 1 && !defined BBB)
-#if !CCC
-OK
-#else
-NOT OK
-#endif
-
------ 2 ------
-#undef BBB
-#if CCC
-OK
-#else
-NOT OK
-#endif
-
------ 3 ------
-#define DEFINED defined
-#define DDD (DEFINED ( AAA ) && AAA > 1 && !DEFINED BBB)
-#if (DDD)
-OK
-#else
-NOT OK
-#endif
-
------ 4 ------
-#undef AAA
-#if !(DDD)
-OK
-#else
-NOT OK
-#endif
diff --git a/tinyc/tests/pp/21.expect b/tinyc/tests/pp/21.expect
deleted file mode 100644
index 5a1376bb1..000000000
--- a/tinyc/tests/pp/21.expect
+++ /dev/null
@@ -1,8 +0,0 @@
------ 1 ------
-OK
------ 2 ------
-OK
------ 3 ------
-OK
------ 4 ------
-OK
diff --git a/tinyc/tests/pp/pp-counter.c b/tinyc/tests/pp/pp-counter.c
deleted file mode 100644
index 3978e1a7e..000000000
--- a/tinyc/tests/pp/pp-counter.c
+++ /dev/null
@@ -1,27 +0,0 @@
-X1 __COUNTER__
-X2 __COUNTER__
-#if __COUNTER__
-X3 __COUNTER__
-#endif
-#define pass(x) x
-#define a x __COUNTER__ y
-#define a2 pass(__COUNTER__)
-#define f(c) c __COUNTER__
-#define apply(d) d d __COUNTER__ x2 f(d) y2 __COUNTER__
-#define _paste(a,b) a ## b
-#define paste(a,b) _paste(a,b)
-#define _paste3(a,b,c) a ## b ## c
-#define doublepaste(a,b) _paste3(a,b,b)
-#define str(x) #x
-X4 a
-X5 f(a)
-X6 f(b)
-X7 f(__COUNTER__)
-X8 apply(a)
-X9 apply(f(a))
-X10 apply(__COUNTER__)
-X11 apply(a2)
-X12 str(__COUNTER__)
-X13 paste(x,__COUNTER__)
-X14 _paste(x,__COUNTER__)
-X15 doublepaste(x,__COUNTER__)
diff --git a/tinyc/tests/pp/pp-counter.expect b/tinyc/tests/pp/pp-counter.expect
deleted file mode 100644
index 02fc535bc..000000000
--- a/tinyc/tests/pp/pp-counter.expect
+++ /dev/null
@@ -1,15 +0,0 @@
-X1 0
-X2 1
-X3 3
-X4 x 4 y
-X5 x 5 y 6
-X6 b 7
-X7 8 9
-X8 x 10 y x 10 y 11 x2 x 10 y 12 y2 13
-X9 x 14 y 15 x 14 y 15 16 x2 x 14 y 15 17 y2 18
-X10 19 19 20 x2 19 21 y2 22
-X11 23 23 24 x2 23 25 y2 26
-X12 "__COUNTER__"
-X13 x27
-X14 x__COUNTER__
-X15 x2828
diff --git a/tinyc/tests/tcctest.c b/tinyc/tests/tcctest.c
deleted file mode 100644
index 099fc3544..000000000
--- a/tinyc/tests/tcctest.c
+++ /dev/null
@@ -1,3813 +0,0 @@
-/*
- * TCC auto test program
- */
-#include "config.h"
-
-#if GCC_MAJOR >= 3
-
-/* Unfortunately, gcc version < 3 does not handle that! */
-#define ALL_ISOC99
-
-/* only gcc 3 handles _Bool correctly */
-#define BOOL_ISOC99
-
-/* gcc 2.95.3 does not handle correctly CR in strings or after strays */
-#define CORRECT_CR_HANDLING
-
-#endif
-
-#if defined(_WIN32)
-#define LONG_LONG_FORMAT "%lld"
-#define ULONG_LONG_FORMAT "%llu"
-#else
-#define LONG_LONG_FORMAT "%Ld"
-#define ULONG_LONG_FORMAT "%Lu"
-#endif
-
-// MinGW has 80-bit rather than 64-bit long double which isn't compatible with TCC or MSVC
-#if defined(_WIN32) && defined(__GNUC__)
-#define LONG_DOUBLE double
-#define LONG_DOUBLE_LITERAL(x) x
-#else
-#define LONG_DOUBLE long double
-#define LONG_DOUBLE_LITERAL(x) x ## L
-#endif
-
-/* deprecated and no longer supported in gcc 3.3 */
-//#define ACCEPT_CR_IN_STRINGS
-
-/* __VA_ARGS__ and __func__ support */
-#define C99_MACROS
-
-/* test various include syntaxes */
-
-#define TCCLIB_INC <tcclib.h>
-#define TCCLIB_INC1 <tcclib
-#define TCCLIB_INC2 h>
-#define TCCLIB_INC3 "tcclib.h"
-
-#include TCCLIB_INC
-
-#include TCCLIB_INC1.TCCLIB_INC2
-
-#include TCCLIB_INC1.h>
-
-#include TCCLIB_INC3
-
-#include <tcclib.h>
-
-#include "tcclib.h"
-
-#include "tcctest.h"
-
-/* Test two more ways to include a file named like a pp-number */
-#define INC(name) <tests/name.h>
-#define funnyname 42test.h
-#define incdir tests/
-#define incname < incdir funnyname >
-#define __stringify(x) #x
-#define stringify(x) __stringify(x)
-#include INC(42test)
-#include incname
-#include stringify(funnyname)
-
-void intdiv_test();
-void string_test();
-void expr_test();
-void macro_test();
-void recursive_macro_test();
-void scope_test();
-void forward_test();
-void funcptr_test();
-void loop_test();
-void switch_test();
-void goto_test();
-void enum_test();
-void typedef_test();
-void struct_test();
-void array_test();
-void expr_ptr_test();
-void bool_test();
-void optimize_out();
-void expr2_test();
-void constant_expr_test();
-void expr_cmp_test();
-void char_short_test();
-void init_test(void);
-void compound_literal_test(void);
-int kr_test();
-void struct_assign_test(void);
-void cast_test(void);
-void bitfield_test(void);
-void c99_bool_test(void);
-void float_test(void);
-void longlong_test(void);
-void manyarg_test(void);
-void stdarg_test(void);
-void whitespace_test(void);
-void relocation_test(void);
-void old_style_function(void);
-void alloca_test(void);
-void c99_vla_test(int size1, int size2);
-void sizeof_test(void);
-void typeof_test(void);
-void local_label_test(void);
-void statement_expr_test(void);
-void asm_test(void);
-void builtin_test(void);
-void weak_test(void);
-void global_data_test(void);
-void cmp_comparison_test(void);
-void math_cmp_test(void);
-void callsave_test(void);
-void builtin_frame_address_test(void);
-void attrib_test(void);
-
-int fib(int n);
-void num(int n);
-void forward_ref(void);
-int isid(int c);
-
-/* Line joining happens before tokenization, so the following
-   must be parsed as ellipsis.  */
-void funny_line_continuation (int, ..\
-. );
-
-char via_volatile (char);
-
-#define A 2
-#define N 1234 + A
-#define pf printf
-#define M1(a, b)  (a) + (b)
-
-#define str\
-(s) # s
-#define glue(a, b) a ## b
-#define xglue(a, b) glue(a, b)
-#define HIGHLOW "hello"
-#define LOW LOW ", world"
-
-static int onetwothree = 123;
-#define onetwothree4 onetwothree
-#define onetwothree xglue(onetwothree,4)
-
-#define min(a, b) ((a) < (b) ? (a) : (b))
-
-#ifdef C99_MACROS
-#define dprintf(level,...) printf(__VA_ARGS__)
-#endif
-
-/* gcc vararg macros */
-#define dprintf1(level, fmt, args...) printf(fmt, ## args)
-
-#define MACRO_NOARGS()
-
-#define AAA 3
-#undef AAA
-#define AAA 4
-
-#if 1
-#define B3 1
-#elif 1
-#define B3 2
-#elif 0
-#define B3 3
-#else
-#define B3 4
-#endif
-
-#ifdef __TINYC__
-/* We try to handle this syntax.  Make at least sure it doesn't segfault.  */
-char invalid_function_def()[] {}
-#endif
-
-#define __INT64_C(c)	c ## LL
-#define INT64_MIN	(-__INT64_C(9223372036854775807)-1)
-
-int qq(int x)
-{
-    return x + 40;
-}
-#define qq(x) x
-
-#define spin_lock(lock) do { } while (0)
-#define wq_spin_lock spin_lock
-#define TEST2() wq_spin_lock(a)
-
-#define UINT_MAX ((unsigned) -1)
-
-void intdiv_test(void)
-{
-    printf("18/21=%u\n", 18/21);
-    printf("18%%21=%u\n", 18%21);
-    printf("41/21=%u\n", 41/21);
-    printf("41%%21=%u\n", 41%21);
-    printf("42/21=%u\n", 42/21);
-    printf("42%%21=%u\n", 42%21);
-    printf("43/21=%u\n", 43/21);
-    printf("43%%21=%u\n", 43%21);
-    printf("126/21=%u\n", 126/21);
-    printf("126%%21=%u\n", 126%21);
-    printf("131/21=%u\n", 131/21);
-    printf("131%%21=%u\n", 131%21);
-    printf("(UINT_MAX/2+3)/2=%u\n", (UINT_MAX/2+3)/2);
-    printf("(UINT_MAX/2+3)%%2=%u\n", (UINT_MAX/2+3)%2);
-
-    printf("18/-21=%u\n", 18/-21);
-    printf("18%%-21=%u\n", 18%-21);
-    printf("41/-21=%u\n", 41/-21);
-    printf("41%%-21=%u\n", 41%-21);
-    printf("42/-21=%u\n", 42/-21);
-    printf("42%%-21=%u\n", 42%-21);
-    printf("43/-21=%u\n", 43/-21);
-    printf("43%%-21=%u\n", 43%-21);
-    printf("126/-21=%u\n", 126/-21);
-    printf("126%%-21=%u\n", 126%-21);
-    printf("131/-21=%u\n", 131/-21);
-    printf("131%%-21=%u\n", 131%-21);
-    printf("(UINT_MAX/2+3)/-2=%u\n", (UINT_MAX/2+3)/-2);
-    printf("(UINT_MAX/2+3)%%-2=%u\n", (UINT_MAX/2+3)%-2);
-
-    printf("-18/21=%u\n", -18/21);
-    printf("-18%%21=%u\n", -18%21);
-    printf("-41/21=%u\n", -41/21);
-    printf("-41%%21=%u\n", -41%21);
-    printf("-42/21=%u\n", -42/21);
-    printf("-42%%21=%u\n", -42%21);
-    printf("-43/21=%u\n", -43/21);
-    printf("-43%%21=%u\n", -43%21);
-    printf("-126/21=%u\n", -126/21);
-    printf("-126%%21=%u\n", -126%21);
-    printf("-131/21=%u\n", -131/21);
-    printf("-131%%21=%u\n", -131%21);
-    printf("-(UINT_MAX/2+3)/2=%u\n", (0-(UINT_MAX/2+3))/2);
-    printf("-(UINT_MAX/2+3)%%2=%u\n", (0-(UINT_MAX/2+3))%2);
-
-    printf("-18/-21=%u\n", -18/-21);
-    printf("-18%%-21=%u\n", -18%-21);
-    printf("-41/-21=%u\n", -41/-21);
-    printf("-41%%-21=%u\n", -41%-21);
-    printf("-42/-21=%u\n", -42/-21);
-    printf("-42%%-21=%u\n", -42%-21);
-    printf("-43/-21=%u\n", -43/-21);
-    printf("-43%%-21=%u\n", -43%-21);
-    printf("-126/-21=%u\n", -126/-21);
-    printf("-126%%-21=%u\n", -126%-21);
-    printf("-131/-21=%u\n", -131/-21);
-    printf("-131%%-21=%u\n", -131%-21);
-    printf("-(UINT_MAX/2+3)/-2=%u\n", (0-(UINT_MAX/2+3))/-2);
-    printf("-(UINT_MAX/2+3)%%-2=%u\n", (0-(UINT_MAX/2+3))%-2);
-}
-
-void macro_test(void)
-{
-    printf("macro:\n");

-    pf("N=%d\n", N);
-    printf("aaa=%d\n", AAA);
-
-    printf("min=%d\n", min(1, min(2, -1)));
-
-    printf("s1=%s\n", glue(HIGH, LOW));
-    printf("s2=%s\n", xglue(HIGH, LOW));
-    printf("s3=%s\n", str("c"));
-    printf("s4=%s\n", str(a1));
-    printf("B3=%d\n", B3);
-
-    printf("onetwothree=%d\n", onetwothree);
-
-#ifdef A
-    printf("A defined\n");
-#endif
-#ifdef B
-    printf("B defined\n");
-#endif
-#ifdef A
-    printf("A defined\n");
-#else
-    printf("A not defined\n");
-#endif
-#ifdef B
-    printf("B defined\n");
-#else
-    printf("B not defined\n");
-#endif
-
-#ifdef A
-    printf("A defined\n");
-#ifdef B
-    printf("B1 defined\n");
-#else
-    printf("B1 not defined\n");
-#endif
-#else
-    printf("A not defined\n");
-#ifdef B
-    printf("B2 defined\n");
-#else
-    printf("B2 not defined\n");
-#endif
-#endif
-
-#if 1+1
-    printf("test true1\n");
-#endif
-#if 0
-    printf("test true2\n");
-#endif
-#if 1-1
-    printf("test true3\n");
-#endif
-#if defined(A)
-    printf("test trueA\n");
-#endif
-#if defined(B)
-    printf("test trueB\n");
-#endif
-
-#if 0
-    printf("test 0\n");
-#elif 0
-    printf("test 1\n");
-#elif 2
-    printf("test 2\n");
-#else
-    printf("test 3\n");
-#endif
-
-    MACRO_NOARGS();
-
-#ifdef __LINE__
-    printf("__LINE__ defined\n");
-#endif
-
-    printf("__LINE__=%d __FILE__=%s\n",
-           __LINE__, __FILE__);
-#if 0
-#line 200
-    printf("__LINE__=%d __FILE__=%s\n",
-           __LINE__, __FILE__);
-#line 203 "test" 
-    printf("__LINE__=%d __FILE__=%s\n",
-           __LINE__, __FILE__);
-#line 227 "tcctest.c"
-#endif
-
-    /* not strictly preprocessor, but we test it there */
-#ifdef C99_MACROS
-    printf("__func__ = %s\n", __func__);
-    dprintf(1, "vaarg=%d\n", 1);
-#endif
-    dprintf1(1, "vaarg1\n");
-    dprintf1(1, "vaarg1=%d\n", 2);
-    dprintf1(1, "vaarg1=%d %d\n", 1, 2);
-
-    /* gcc extension */
-    printf("func='%s'\n", __FUNCTION__);
-
-    /* complicated macros in glibc */
-    printf("INT64_MIN=" LONG_LONG_FORMAT "\n", INT64_MIN);
-    {
-        int a;
-        a = 1;
-        glue(a+, +);
-        printf("a=%d\n", a);
-        glue(a <, <= 2);
-        printf("a=%d\n", a);
-    }
-    
-    /* macro function with argument outside the macro string */
-#define MF_s MF_hello
-#define MF_hello(msg) printf("%s\n",msg)
-
-#define MF_t printf("tralala\n"); MF_hello
-
-    MF_s("hi");
-    MF_t("hi");
-    
-    /* test macro substitution inside args (should not eat stream) */
-    printf("qq=%d\n", qq(qq)(2));
-
-    /* test zero argument case. NOTE: gcc 2.95.x does not accept a
-       null argument without a space. gcc 3.2 fixes that. */
-
-#define qq1(x) 1
-    printf("qq1=%d\n", qq1( ));
-
-    /* comment with stray handling *\
-/
-       /* this is a valid *\/ comment */
-       /* this is a valid comment *\*/
-    //  this is a valid\
-comment
-
-    /* test function macro substitution when the function name is
-       substituted */
-    TEST2();
-
-    /* And again when the name and parentheses are separated by a
-       comment.  */
-    TEST2 /* the comment */ ();
-
-    printf("%s\n", get_basefile_from_header());
-    printf("%s\n", __BASE_FILE__);
-    printf("%s\n", get_file_from_header());
-    printf("%s\n", __FILE__);
-
-    /* Check that funnily named include was in fact included */
-    have_included_42test_h = 1;
-    have_included_42test_h_second = 1;
-    have_included_42test_h_third = 1;
-}
-
-
-static void print_num(char *fn, int line, int num) {
-    printf("fn %s, line %d, num %d\n", fn, line, num);
-}
-
-void recursive_macro_test(void)
-{
-
-#define ELF32_ST_TYPE(val)              ((val) & 0xf)
-#define ELF32_ST_INFO(bind, type)       (((bind) << 4) + ((type) & 0xf))
-#define STB_WEAK        2               /* Weak symbol */
-#define ELFW(type) ELF##32##_##type
-    printf("%d\n", ELFW(ST_INFO)(STB_WEAK, ELFW(ST_TYPE)(123)));
-
-#define WRAP(x) x
-    
-#define print_num(x) print_num(__FILE__,__LINE__,x)
-    print_num(123);
-    WRAP(print_num(123));
-    WRAP(WRAP(print_num(123)));
-
-static struct recursive_macro { int rm_field; } G;
-#define rm_field (G.rm_field)
-    printf("rm_field = %d\n", rm_field);
-    printf("rm_field = %d\n", WRAP(rm_field));
-    WRAP((printf("rm_field = %d %d\n", rm_field, WRAP(rm_field))));
-}
-
-int op(a,b)
-{
-    return a / b;
-}
-
-int ret(a)
-{
-    if (a == 2)
-        return 1;
-    if (a == 3)
-        return 2;
-    return 0;
-}
-
-void ps(const char *s)
-{
-    int c;
-    while (1) {
-        c = *s;
-        if (c == 0)
-            break;
-        printf("%c", c);
-        s++;
-    }
-}
-
-const char foo1_string[] = "\
-bar\n\
-test\14\
-1";
-
-void string_test()
-{
-    unsigned int b;
-    printf("string:\n");
-    printf("\141\1423\143\n");/* dezdez test */
-    printf("\x41\x42\x43\x3a\n");
-    printf("c=%c\n", 'r');
-    printf("wc=%C 0x%lx %C\n", L'a', L'\x1234', L'c');
-    printf("foo1_string='%s'\n", foo1_string);
-#if 0
-    printf("wstring=%S\n", L"abc");
-    printf("wstring=%S\n", L"abc" L"def" "ghi");
-    printf("'\\377'=%d '\\xff'=%d\n", '\377', '\xff');
-    printf("L'\\377'=%d L'\\xff'=%d\n", L'\377', L'\xff');
-#endif
-    ps("test\n");
-    b = 32;
-    while ((b = b + 1) < 96) {
-        printf("%c", b);
-    }
-    printf("\n");
-    printf("fib=%d\n", fib(33));
-    b = 262144;
-    while (b != 0x80000000) {
-        num(b);
-        b = b * 2;
-    }
-}
-
-void loop_test()
-{
-    int i;
-    i = 0;
-    while (i < 10)
-        printf("%d", i++);
-    printf("\n");
-    for(i = 0; i < 10;i++)
-        printf("%d", i);
-    printf("\n");
-    i = 0;
-    do {
-        printf("%d", i++);
-    } while (i < 10);
-    printf("\n");
-
-    char count = 123;
-    /* c99 for loop init test */
-    for (size_t count = 1; count < 3; count++)
-        printf("count=%d\n", count);
-    printf("count = %d\n", count);
-
-    /* break/continue tests */
-    i = 0;
-    while (1) {
-        if (i == 6)
-            break;
-        i++;
-        if (i == 3)
-            continue;
-        printf("%d", i);
-    }
-    printf("\n");
-
-    /* break/continue tests */
-    i = 0;
-    do {
-        if (i == 6)
-            break;
-        i++;
-        if (i == 3)
-            continue;
-        printf("%d", i);
-    } while(1);
-    printf("\n");
-
-    for(i = 0;i < 10;i++) {
-        if (i == 3)
-            continue;
-        printf("%d", i);
-    }
-    printf("\n");
-}
-
-typedef int typedef_and_label;
-
-void goto_test()
-{
-    int i;
-    static void *label_table[3] = { &&label1, &&label2, &&label3 };
-
-    printf("goto:\n");
-    i = 0;
-    /* This needs to parse as label, not as start of decl.  */
- typedef_and_label:
- s_loop:
-    if (i >= 10) 
-        goto s_end;
-    printf("%d", i);
-    i++;
-    goto s_loop;
- s_end:
-    printf("\n");
-
-    /* we also test computed gotos (GCC extension) */
-    for(i=0;i<3;i++) {
-        goto *label_table[i];
-    label1:
-        printf("label1\n");
-        goto next;
-    label2:
-        printf("label2\n");
-        goto next;
-    label3:
-        printf("label3\n");
-    next: ;
-    }
-}
-
-enum {
-    E0,
-    E1 = 2,
-    E2 = 4,
-    E3,
-    E4,
-};
-
-enum test {
-    E5 = 1000,
-};
-
-struct S_enum {
-    enum {E6 = 42, E7, E8} e:8;
-};
-
-enum ELong {
-    /* This is either 0 on L32 machines, or a large number
-       on L64 machines.  We should be able to store this.  */
-    EL_large = ((unsigned long)0xf000 << 31) << 1,
-};
-
-enum { BIASU = -1U<<31 };
-enum { BIASS = -1 << 31 };
-
-static int getint(int i)
-{
-  if (i)
-    return 0;
-  else
-    return (int)(-1U << 31);
-}
-
-void enum_test()
-{
-    enum test b1;
-    /* The following should give no warning */
-    unsigned *p = &b1;
-    struct S_enum s = {E7};
-    printf("enum: %d\n", s.e);
-    printf("enum:\n%d %d %d %d %d %d\n",
-           E0, E1, E2, E3, E4, E5);
-    b1 = 1;
-    printf("b1=%d\n", b1);
-    printf("enum large: %ld\n", EL_large);
-
-    if (getint(0) == BIASU)
-      printf("enum unsigned: ok\n");
-    else
-      printf("enum unsigned: wrong\n");
-    if (getint(0) == BIASS)
-      printf("enum unsigned: ok\n");
-    else
-      printf("enum unsigned: wrong\n");
-}
-
-typedef int *my_ptr;
-
-typedef int mytype1;
-typedef int mytype2;
-
-void typedef_test()
-{
-    my_ptr a;
-    mytype1 mytype2;
-    int b;
-
-    a = &b;
-    *a = 1234;
-    printf("typedef:\n");
-    printf("a=%d\n", *a);
-    mytype2 = 2;
-    printf("mytype2=%d\n", mytype2);
-}
-
-void forward_test()
-{
-    printf("forward:\n");
-    forward_ref();
-    forward_ref();
-}
-
-
-void forward_ref(void)
-{
-    printf("forward ok\n");
-}
-
-typedef struct struct1 {
-    int f1;
-    int f2, f3;
-    union union1 {
-        int v1;
-        int v2;
-    } u;
-    char str[3];
-} struct1;
-
-struct struct2 {
-    int a;
-    char b;
-};
-
-union union2 {
-    int w1;
-    int w2;
-};
-
-struct struct1 st1, st2;
-
-struct empty_mem {
-    /* nothing */ ;
-    int x;
-};
-
-int main(int argc, char **argv)
-{
-    string_test();
-    expr_test();
-    macro_test();
-    recursive_macro_test();
-    scope_test();
-    forward_test();
-    funcptr_test();
-    loop_test();
-    switch_test();
-    goto_test();
-    enum_test();
-    typedef_test();
-    struct_test();
-    array_test();
-    expr_ptr_test();
-    bool_test();
-    optimize_out();
-    expr2_test();
-    constant_expr_test();
-    expr_cmp_test();
-    char_short_test();
-    init_test();
-    compound_literal_test();
-    kr_test();
-    struct_assign_test();
-    cast_test();
-    bitfield_test();
-    c99_bool_test();
-    float_test();
-    longlong_test();
-    manyarg_test();
-    stdarg_test();
-    whitespace_test();
-    relocation_test();
-    old_style_function();
-    alloca_test();
-    c99_vla_test(5, 2);
-    sizeof_test();
-    typeof_test();
-    statement_expr_test();
-    local_label_test();
-    asm_test();
-    builtin_test();
-#ifndef _WIN32
-    weak_test();
-#endif
-    global_data_test();
-    cmp_comparison_test();
-    math_cmp_test();
-    callsave_test();
-    builtin_frame_address_test();
-    intdiv_test();
-    if (via_volatile (42) != 42)
-      printf ("via_volatile broken\n");
-    attrib_test();
-    return 0; 
-}
-
-int tab[3];
-int tab2[3][2];
-
-int g;
-
-void f1(g)
-{
-    printf("g1=%d\n", g);
-}
-
-void scope_test()
-{
-    printf("scope:\n");
-    g = 2;
-    f1(1);
-    printf("g2=%d\n", g);
-    {
-        int g;
-        g = 3;
-        printf("g3=%d\n", g);
-        {
-            int g;
-            g = 4;
-            printf("g4=%d\n", g);
-        }
-    }
-    printf("g5=%d\n", g);
-}
-
-void array_test()
-{
-    int i, j, a[4];
-
-    printf("array:\n");
-    printf("sizeof(a) = %d\n", sizeof(a));
-    printf("sizeof(\"a\") = %d\n", sizeof("a"));
-#ifdef C99_MACROS
-    printf("sizeof(__func__) = %d\n", sizeof(__func__));
-#endif
-    printf("sizeof tab %d\n", sizeof(tab));
-    printf("sizeof tab2 %d\n", sizeof tab2);
-    tab[0] = 1;
-    tab[1] = 2;
-    tab[2] = 3;
-    printf("%d %d %d\n", tab[0], tab[1], tab[2]);
-    for(i=0;i<3;i++)
-        for(j=0;j<2;j++)
-            tab2[i][j] = 10 * i + j;
-    for(i=0;i<3*2;i++) {
-        printf(" %3d", ((int *)tab2)[i]);
-    }
-    printf("\n");
-    printf("sizeof(size_t)=%d\n", sizeof(size_t));
-    printf("sizeof(ptrdiff_t)=%d\n", sizeof(ptrdiff_t));
-}
-
-void expr_test()
-{
-    int a, b;
-    a = 0;
-    printf("%d\n", a += 1);
-    printf("%d\n", a -= 2);
-    printf("%d\n", a *= 31232132);
-    printf("%d\n", a /= 4);
-    printf("%d\n", a %= 20);
-    printf("%d\n", a &= 6);
-    printf("%d\n", a ^= 7);
-    printf("%d\n", a |= 8);
-    printf("%d\n", a >>= 3);
-    printf("%d\n", a <<= 4);
-
-    a = 22321;
-    b = -22321;
-    printf("%d\n", a + 1);
-    printf("%d\n", a - 2);
-    printf("%d\n", a * 312);
-    printf("%d\n", a / 4);
-    printf("%d\n", b / 4);
-    printf("%d\n", (unsigned)b / 4);
-    printf("%d\n", a % 20);
-    printf("%d\n", b % 20);
-    printf("%d\n", (unsigned)b % 20);
-    printf("%d\n", a & 6);
-    printf("%d\n", a ^ 7);
-    printf("%d\n", a | 8);
-    printf("%d\n", a >> 3);
-    printf("%d\n", b >> 3);
-    printf("%d\n", (unsigned)b >> 3);
-    printf("%d\n", a << 4);
-    printf("%d\n", ~a);
-    printf("%d\n", -a);
-    printf("%d\n", +a);
-
-    printf("%d\n", 12 + 1);
-    printf("%d\n", 12 - 2);
-    printf("%d\n", 12 * 312);
-    printf("%d\n", 12 / 4);
-    printf("%d\n", 12 % 20);
-    printf("%d\n", 12 & 6);
-    printf("%d\n", 12 ^ 7);
-    printf("%d\n", 12 | 8);
-    printf("%d\n", 12 >> 2);
-    printf("%d\n", 12 << 4);
-    printf("%d\n", ~12);
-    printf("%d\n", -12);
-    printf("%d\n", +12);
-    printf("%d %d %d %d\n", 
-           isid('a'), 
-           isid('g'), 
-           isid('T'), 
-           isid('('));
-}
-
-int isid(int c)
-{
-    return (c >= 'a' & c <= 'z') | (c >= 'A' & c <= 'Z') | c == '_';
-}
-
-/**********************/
-
-int vstack[10], *vstack_ptr;
-
-void vpush(int vt, int vc)
-{
-    *vstack_ptr++ = vt;
-    *vstack_ptr++ = vc;
-}
-
-void vpop(int *ft, int *fc)
-{
-    *fc = *--vstack_ptr;
-    *ft = *--vstack_ptr;
-}
-
-void expr2_test()
-{
-    int a, b;
-
-    printf("expr2:\n");
-    vstack_ptr = vstack;
-    vpush(1432432, 2);
-    vstack_ptr[-2] &= ~0xffffff80;
-    vpop(&a, &b);
-    printf("res= %d %d\n", a, b);
-}
-
-void constant_expr_test()
-{
-    int a;
-    printf("constant_expr:\n");
-    a = 3;
-    printf("%d\n", a * 16);
-    printf("%d\n", a * 1);
-    printf("%d\n", a + 0);
-}
-
-int tab4[10];
-
-void expr_ptr_test()
-{
-    int *p, *q;
-    int i = -1;
-
-    printf("expr_ptr:\n");
-    p = tab4;
-    q = tab4 + 10;
-    printf("diff=%d\n", q - p);
-    p++;
-    printf("inc=%d\n", p - tab4);
-    p--;
-    printf("dec=%d\n", p - tab4);
-    ++p;
-    printf("inc=%d\n", p - tab4);
-    --p;
-    printf("dec=%d\n", p - tab4);
-    printf("add=%d\n", p + 3 - tab4);
-    printf("add=%d\n", 3 + p - tab4);
-
-    /* check if 64bit support is ok */
-    q = p = 0;
-    q += i;
-    printf("%p %p %ld\n", q, p, p-q);
-    printf("%d %d %d %d %d %d\n",
-           p == q, p != q, p < q, p <= q, p >= q, p > q);
-    i = 0xf0000000;
-    p += i;
-    printf("%p %p %ld\n", q, p, p-q);
-    printf("%d %d %d %d %d %d\n",
-           p == q, p != q, p < q, p <= q, p >= q, p > q);
-    p = (int *)((char *)p + 0xf0000000);
-    printf("%p %p %ld\n", q, p, p-q);
-    printf("%d %d %d %d %d %d\n",
-           p == q, p != q, p < q, p <= q, p >= q, p > q);
-    p += 0xf0000000;
-    printf("%p %p %ld\n", q, p, p-q);
-    printf("%d %d %d %d %d %d\n",
-           p == q, p != q, p < q, p <= q, p >= q, p > q);
-    {
-        struct size12 {
-            int i, j, k;
-        };
-        struct size12 s[2], *sp = s;
-        int i, j;
-        sp->i = 42;
-        sp++;
-        j = -1;
-        printf("%d\n", sp[j].i);
-    }
-#ifdef __LP64__
-    i = 1;
-    p = (int*)0x100000000UL + i;
-    i = ((long)p) >> 32;
-    printf("largeptr: %p %d\n", p, i);
-#endif
-}
-
-void expr_cmp_test()
-{
-    int a, b;
-    printf("constant_expr:\n");
-    a = -1;
-    b = 1;
-    printf("%d\n", a == a);
-    printf("%d\n", a != a);
-
-    printf("%d\n", a < b);
-    printf("%d\n", a <= b);
-    printf("%d\n", a <= a);
-    printf("%d\n", b >= a);
-    printf("%d\n", a >= a);
-    printf("%d\n", b > a);
-
-    printf("%d\n", (unsigned)a < b);
-    printf("%d\n", (unsigned)a <= b);
-    printf("%d\n", (unsigned)a <= a);
-    printf("%d\n", (unsigned)b >= a);
-    printf("%d\n", (unsigned)a >= a);
-    printf("%d\n", (unsigned)b > a);
-}
-
-struct empty {
-};
-
-struct aligntest1 {
-    char a[10];
-};
-
-struct aligntest2 {
-    int a;
-    char b[10];
-};
-
-struct aligntest3 {
-    double a, b;
-};
-
-struct aligntest4 {
-    double a[0];
-};
-
-struct __attribute__((aligned(16))) aligntest5
-{
-    int i;
-};
-struct aligntest6
-{
-    int i;
-} __attribute__((aligned(16)));
-struct aligntest7
-{
-    int i;
-};
-struct aligntest5 altest5[2];
-struct aligntest6 altest6[2];
-int pad1;
-/* altest7 is correctly aligned to 16 bytes also with TCC,
-   but __alignof__ returns the wrong result (4) because we
-   can't store the alignment yet when specified on symbols
-   directly (it's stored in the type so we'd need to make
-   a copy of it). -- FIXED */
-struct aligntest7 altest7[2] __attribute__((aligned(16)));
-
-struct aligntest8
-{
-  int i;
-} __attribute__((aligned(4096)));
-
-struct Large {
-    unsigned long flags;
-    union {
-	void *u1;
-	int *u2;
-    };
-
-    struct {
-	union {
-	    unsigned long index;
-	    void *freelist;
-	};
-	union {
-	    unsigned long counters;
-	    struct {
-		int bla;
-	    };
-	};
-    };
-
-    union {
-	struct {
-	    long u3;
-	    long u4;
-	};
-	void *u5;
-	struct {
-	    unsigned long compound_head;
-	    unsigned int compound_dtor;
-	    unsigned int compound_order;
-	};
-    };
-} __attribute__((aligned(2 * sizeof(long))));
-
-typedef unsigned long long __attribute__((aligned(4))) unaligned_u64;
-
-struct aligntest9 {
-    unsigned int buf_nr;
-    unaligned_u64 start_lba;
-};
-
-struct aligntest10 {
-    unsigned int buf_nr;
-    unsigned long long start_lba;
-};
-
-void struct_test()
-{
-    struct1 *s;
-    union union2 u;
-    struct Large ls;
-
-    printf("struct:\n");
-    printf("sizes: %d %d %d %d\n",
-           sizeof(struct struct1),
-           sizeof(struct struct2),
-           sizeof(union union1),
-           sizeof(union union2));
-    printf("offsets: %d\n", (int)((char*)&st1.u.v1 - (char*)&st1));
-    st1.f1 = 1;
-    st1.f2 = 2;
-    st1.f3 = 3;
-    printf("st1: %d %d %d\n",
-           st1.f1, st1.f2, st1.f3);
-    st1.u.v1 = 1;
-    st1.u.v2 = 2;
-    printf("union1: %d\n", st1.u.v1);
-    u.w1 = 1;
-    u.w2 = 2;
-    printf("union2: %d\n", u.w1);
-    s = &st2;
-    s->f1 = 3;
-    s->f2 = 2;
-    s->f3 = 1;
-    printf("st2: %d %d %d\n",
-           s->f1, s->f2, s->f3);
-    printf("str_addr=%x\n", (int)st1.str - (int)&st1.f1);
-
-    /* align / size tests */
-    printf("aligntest1 sizeof=%d alignof=%d\n",
-           sizeof(struct aligntest1), __alignof__(struct aligntest1));
-    printf("aligntest2 sizeof=%d alignof=%d\n",
-           sizeof(struct aligntest2), __alignof__(struct aligntest2));
-    printf("aligntest3 sizeof=%d alignof=%d\n",
-           sizeof(struct aligntest3), __alignof__(struct aligntest3));
-    printf("aligntest4 sizeof=%d alignof=%d\n",
-           sizeof(struct aligntest4), __alignof__(struct aligntest4));
-    printf("aligntest5 sizeof=%d alignof=%d\n",
-           sizeof(struct aligntest5), __alignof__(struct aligntest5));
-    printf("aligntest6 sizeof=%d alignof=%d\n",
-           sizeof(struct aligntest6), __alignof__(struct aligntest6));
-    printf("aligntest7 sizeof=%d alignof=%d\n",
-           sizeof(struct aligntest7), __alignof__(struct aligntest7));
-    printf("aligntest8 sizeof=%d alignof=%d\n",
-           sizeof(struct aligntest8), __alignof__(struct aligntest8));
-    printf("aligntest9 sizeof=%d alignof=%d\n",
-           sizeof(struct aligntest9), __alignof__(struct aligntest9));
-    printf("aligntest10 sizeof=%d alignof=%d\n",
-           sizeof(struct aligntest10), __alignof__(struct aligntest10));
-    printf("altest5 sizeof=%d alignof=%d\n",
-           sizeof(altest5), __alignof__(altest5));
-    printf("altest6 sizeof=%d alignof=%d\n",
-           sizeof(altest6), __alignof__(altest6));
-    printf("altest7 sizeof=%d alignof=%d\n",
-           sizeof(altest7), __alignof__(altest7));
-           
-    /* empty structures (GCC extension) */
-    printf("sizeof(struct empty) = %d\n", sizeof(struct empty));
-    printf("alignof(struct empty) = %d\n", __alignof__(struct empty));
-
-    printf("Large: sizeof=%d\n", sizeof(ls));
-    memset(&ls, 0, sizeof(ls));
-    ls.compound_head = 42;
-    printf("Large: offsetof(compound_head)=%d\n", (int)((char*)&ls.compound_head - (char*)&ls));
-}
-
-/* XXX: depend on endianness */
-void char_short_test()
-{
-    int var1, var2;
-
-    printf("char_short:\n");
-
-    var1 = 0x01020304;
-    var2 = 0xfffefdfc;
-    printf("s8=%d %d\n", 
-           *(char *)&var1, *(char *)&var2);
-    printf("u8=%d %d\n", 
-           *(unsigned char *)&var1, *(unsigned char *)&var2);
-    printf("s16=%d %d\n", 
-           *(short *)&var1, *(short *)&var2);
-    printf("u16=%d %d\n", 
-           *(unsigned short *)&var1, *(unsigned short *)&var2);
-    printf("s32=%d %d\n", 
-           *(int *)&var1, *(int *)&var2);
-    printf("u32=%d %d\n", 
-           *(unsigned int *)&var1, *(unsigned int *)&var2);
-    *(char *)&var1 = 0x08;
-    printf("var1=%x\n", var1);
-    *(short *)&var1 = 0x0809;
-    printf("var1=%x\n", var1);
-    *(int *)&var1 = 0x08090a0b;
-    printf("var1=%x\n", var1);
-}
-
-/******************/
-
-typedef struct Sym {
-    int v;
-    int t;
-    int c;
-    struct Sym *next;
-    struct Sym *prev;
-} Sym;
-
-#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
-#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
-
-static int toupper1(int a)
-{
-    return TOUPPER(a);
-}
-
-static unsigned int calc_vm_flags(unsigned int prot)
-{
-  unsigned int prot_bits;
-  /* This used to segfault in some revisions: */
-  prot_bits = ((0x1==0x00000001)?(prot&0x1):(prot&0x1)?0x00000001:0);
-  return prot_bits;
-}
-
-void bool_test()
-{
-    int *s, a, b, t, f, i;
-
-    a = 0;
-    s = (void*)0;
-    printf("!s=%d\n", !s);
-
-    if (!s || !s[0])
-        a = 1;
-    printf("a=%d\n", a);
-
-    printf("a=%d %d %d\n", 0 || 0, 0 || 1, 1 || 1);
-    printf("a=%d %d %d\n", 0 && 0, 0 && 1, 1 && 1);
-    printf("a=%d %d\n", 1 ? 1 : 0, 0 ? 1 : 0);
-#if 1 && 1
-    printf("a1\n");
-#endif
-#if 1 || 0
-    printf("a2\n");
-#endif
-#if 1 ? 0 : 1
-    printf("a3\n");
-#endif
-#if 0 ? 0 : 1
-    printf("a4\n");
-#endif
-
-    a = 4;
-    printf("b=%d\n", a + (0 ? 1 : a / 2));
-
-    /* test register spilling */
-    a = 10;
-    b = 10;
-    a = (a + b) * ((a < b) ?
-                   ((b - a) * (a - b)): a + b);
-    printf("a=%d\n", a);
-
-    /* test complex || or && expressions */
-    t = 1;
-    f = 0;
-    a = 32;
-    printf("exp=%d\n", f == (32 <= a && a <= 3));
-    printf("r=%d\n", (t || f) + (t && f));
-
-    /* test ? : cast */
-    {
-        int aspect_on;
-        int aspect_native = 65536;
-        double bfu_aspect = 1.0;
-        int aspect;
-        for(aspect_on = 0; aspect_on < 2; aspect_on++) {
-            aspect=aspect_on?(aspect_native*bfu_aspect+0.5):65535UL;
-            printf("aspect=%d\n", aspect);
-        }
-    }
-
-    /* test ? : GCC extension */
-    {
-        static int v1 = 34 ? : -1; /* constant case */
-        static int v2 = 0 ? : -1; /* constant case */
-        int a = 30;
-        
-        printf("%d %d\n", v1, v2);
-        printf("%d %d\n", a - 30 ? : a * 2, a + 1 ? : a * 2);
-    }
-
-    /* again complex expression */
-    for(i=0;i<256;i++) {
-        if (toupper1 (i) != TOUPPER (i))
-            printf("error %d\n", i);
-    }
-    printf ("bits = 0x%x\n", calc_vm_flags (0x1));
-}
-
-extern int undefined_function(void);
-extern int defined_function(void);
-
-static inline void refer_to_undefined(void)
-{
-  undefined_function();
-}
-
-void optimize_out(void)
-{
-  int i = 0 ? undefined_function() : defined_function();
-  printf ("oo:%d\n", i);
-  int j = 1 ? defined_function() : undefined_function();
-  printf ("oo:%d\n", j);
-  if (0)
-    printf("oo:%d\n", undefined_function());
-  else
-    printf("oo:%d\n", defined_function());
-  if (1)
-    printf("oo:%d\n", defined_function());
-  else
-    printf("oo:%d\n", undefined_function());
-  while (1) {
-      printf("oow:%d\n", defined_function());
-      break;
-      printf("oow:%d\n", undefined_function());
-  }
-  j = 1;
-  /* Following is a switch without {} block intentionally.  */
-  switch (j)
-    case 1: break;
-  printf ("oos:%d\n", defined_function());
-  /* The following break shouldn't lead to disabled code after
-     the while.  */
-  while (1)
-    break;
-  printf ("ool1:%d\n", defined_function());
-  /* Same for the other types of loops.  */
-  do
-    break;
-  while (1);
-  printf ("ool2:%d\n", defined_function());
-  for (;;)
-    break;
-  printf ("ool3:%d\n", defined_function());
-  /* Normal {} blocks without controlling statements
-     shouldn't reactivate code emission */
-  while (1) {
-	{
-	  break;
-	}
-      printf ("ool4:%d\n", undefined_function());
-  }
-  j = 1;
-  while (j) {
-      if (j == 0)
-	break; /* this break shouldn't disable code outside the if. */
-      printf("ool5:%d\n", defined_function());
-      j--;
-  }
-
-  j = 1;
-  while (j) {
-      if (1)
-	j--;
-      else
-	breakhere: break;
-      printf("ool6:%d\n", defined_function());
-      goto breakhere;
-  }
-
-  /* Test that constants in logical && are optimized: */
-  i = 0 && undefined_function();
-  i = defined_function() && 0 && undefined_function();
-  if (0 && undefined_function())
-    undefined_function();
-  if (defined_function() && 0)
-    undefined_function();
-  if (0 && 0)
-    undefined_function();
-  if (defined_function() && 0 && undefined_function())
-    undefined_function();
-  /* The same for || : */
-  i = 1 || undefined_function();
-  i = defined_function() || 1 || undefined_function();
-  if (1 || undefined_function())
-    ;
-  else
-    undefined_function();
-  if (defined_function() || 1)
-    ;
-  else
-    undefined_function();
-  if (1 || 1)
-    ;
-  else
-    undefined_function();
-  if (defined_function() || 1 || undefined_function())
-    ;
-  else
-    undefined_function();
-
-  if (defined_function() && 0)
-    refer_to_undefined();
-
-  if (0) {
-      (void)sizeof( ({
-		     do { } while (0);
-		     0;
-		     }) );
-      undefined_function();
-  }
-
-  /* Leave the "if(1)return; printf()" in this order and last in the function */
-  if (1)
-    return;
-  printf ("oor:%d\n", undefined_function());
-}
-
-int defined_function(void)
-{
-  static int i = 40;
-  return i++;
-}
-
-/* GCC accepts that */
-static int tab_reinit[];
-static int tab_reinit[10];
-
-//int cinit1; /* a global variable can be defined several times without error ! */
-int cinit1; 
-int cinit1; 
-int cinit1 = 0;
-int *cinit2 = (int []){3, 2, 1};
-
-void compound_literal_test(void)
-{
-    int *p, i;
-    char *q, *q3;
-
-    printf("compound_test:\n");
-
-    p = (int []){1, 2, 3};
-    for(i=0;i<3;i++)
-        printf(" %d", p[i]);
-    printf("\n");
-
-    for(i=0;i<3;i++)
-        printf("%d", cinit2[i]);
-    printf("\n");
-
-    q = "tralala1";
-    printf("q1=%s\n", q);
-
-    q = (char *){ "tralala2" };
-    printf("q2=%s\n", q);
-
-    q3 = (char *){ q };
-    printf("q3=%s\n", q3);
-
-    q = (char []){ "tralala3" };
-    printf("q4=%s\n", q);
-
-#ifdef ALL_ISOC99
-    p = (int []){1, 2, cinit1 + 3};
-    for(i=0;i<3;i++)
-        printf(" %d", p[i]);
-    printf("\n");
-
-    for(i=0;i<3;i++) {
-        p = (int []){1, 2, 4 + i};
-        printf("%d %d %d\n", 
-               p[0],
-               p[1],
-               p[2]);
-    }
-#endif
-}
-
-/* K & R protos */
-
-kr_func1(a, b)
-{
-    return a + b;
-}
-
-int kr_func2(a, b)
-{
-    return a + b;
-}
-
-kr_test()
-{
-    printf("kr_test:\n");
-    printf("func1=%d\n", kr_func1(3, 4));
-    printf("func2=%d\n", kr_func2(3, 4));
-    return 0;
-}
-
-void num(int n)
-{
-    char *tab, *p;
-    tab = (char*)malloc(20); 
-    p = tab;
-    while (1) {
-        *p = 48 + (n % 10);
-        p++;
-        n = n / 10;
-        if (n == 0)
-            break;
-    }
-    while (p != tab) {
-        p--;
-        printf("%c", *p);
-    }
-    printf("\n");
-    free(tab);
-}
-
-/* structure assignment tests */
-struct structa1 {
-    int f1;
-    char f2;
-};
-
-struct structa1 ssta1;
-
-void struct_assign_test1(struct structa1 s1, int t, float f)
-{
-    printf("%d %d %d %f\n", s1.f1, s1.f2, t, f);
-}
-
-struct structa1 struct_assign_test2(struct structa1 s1, int t)
-{
-    s1.f1 += t;
-    s1.f2 -= t;
-    return s1;
-}
-
-void struct_assign_test(void)
-{
-    struct S {
-      struct structa1 lsta1, lsta2;
-      int i;
-    } s, *ps;
-    
-    ps = &s;
-    ps->i = 4;
-#if 0
-    printf("struct_assign_test:\n");
-
-    s.lsta1.f1 = 1;
-    s.lsta1.f2 = 2;
-    printf("%d %d\n", s.lsta1.f1, s.lsta1.f2);
-    s.lsta2 = s.lsta1;
-    printf("%d %d\n", s.lsta2.f1, s.lsta2.f2);
-#else
-    s.lsta2.f1 = 1;
-    s.lsta2.f2 = 2;
-#endif
-    struct_assign_test1(ps->lsta2, 3, 4.5);
-    
-    printf("before call: %d %d\n", s.lsta2.f1, s.lsta2.f2);
-    ps->lsta2 = struct_assign_test2(ps->lsta2, ps->i);
-    printf("after call: %d %d\n", ps->lsta2.f1, ps->lsta2.f2);
-
-    static struct {
-        void (*elem)();
-    } t[] = {
-        /* XXX: we should allow this even without braces */
-        { struct_assign_test }
-    };
-    printf("%d\n", struct_assign_test == t[0].elem);
-}
-
-/* casts to short/char */
-
-void cast1(char a, short b, unsigned char c, unsigned short d)
-{
-    printf("%d %d %d %d\n", a, b, c, d);
-}
-
-char bcast;
-short scast;
-
-void cast_test()
-{
-    int a;
-    char c;
-    char tab[10];
-    unsigned b,d;
-    short s;
-    char *p = NULL;
-    p -= 0x700000000042;
-
-    printf("cast_test:\n");
-    a = 0xfffff;
-    cast1(a, a, a, a);
-    a = 0xffffe;
-    printf("%d %d %d %d\n",
-           (char)(a + 1),
-           (short)(a + 1),
-           (unsigned char)(a + 1),
-           (unsigned short)(a + 1));
-    printf("%d %d %d %d\n",
-           (char)0xfffff,
-           (short)0xfffff,
-           (unsigned char)0xfffff,
-           (unsigned short)0xfffff);
-
-    a = (bcast = 128) + 1;
-    printf("%d\n", a);
-    a = (scast = 65536) + 1;
-    printf("%d\n", a);
-    
-    printf("sizeof(c) = %d, sizeof((int)c) = %d\n", sizeof(c), sizeof((int)c));
-    
-    /* test cast from unsigned to signed short to int */
-    b = 0xf000;
-    d = (short)b;
-    printf("((unsigned)(short)0x%08x) = 0x%08x\n", b, d);
-    b = 0xf0f0;
-    d = (char)b;
-    printf("((unsigned)(char)0x%08x) = 0x%08x\n", b, d);
-    
-    /* test implicit int casting for array accesses */
-    c = 0;
-    tab[1] = 2;
-    tab[c] = 1;
-    printf("%d %d\n", tab[0], tab[1]);
-
-    /* test implicit casting on some operators */
-    printf("sizeof(+(char)'a') = %d\n", sizeof(+(char)'a'));
-    printf("sizeof(-(char)'a') = %d\n", sizeof(-(char)'a'));
-    printf("sizeof(~(char)'a') = %d\n", sizeof(-(char)'a'));
-
-    /* from pointer to integer types */
-    printf("%d %d %ld %ld %lld %lld\n",
-           (int)p, (unsigned int)p,
-           (long)p, (unsigned long)p,
-           (long long)p, (unsigned long long)p);
-
-    /* from integers to pointers */
-    printf("%p %p %p %p\n",
-           (void *)a, (void *)b, (void *)c, (void *)d);
-}
-
-/* initializers tests */
-struct structinit1 {
-    int f1;
-    char f2;
-    short f3;
-    int farray[3];
-};
-
-int sinit1 = 2;
-int sinit2 = { 3 };
-int sinit3[3] = { 1, 2, {{3}}, };
-int sinit4[3][2] = { {1, 2}, {3, 4}, {5, 6} };
-int sinit5[3][2] = { 1, 2, 3, 4, 5, 6 };
-int sinit6[] = { 1, 2, 3 };
-int sinit7[] = { [2] = 3, [0] = 1, 2 };
-char sinit8[] = "hello" "trala";
-
-struct structinit1 sinit9 = { 1, 2, 3 };
-struct structinit1 sinit10 = { .f2 = 2, 3, .f1 = 1 };
-struct structinit1 sinit11 = { .f2 = 2, 3, .f1 = 1, 
-#ifdef ALL_ISOC99
-                               .farray[0] = 10,
-                               .farray[1] = 11,
-                               .farray[2] = 12,
-#endif
-};
-
-char *sinit12 = "hello world";
-char *sinit13[] = {
-    "test1",
-    "test2",
-    "test3",
-};
-char sinit14[10] = { "abc" };
-int sinit15[3] = { sizeof(sinit15), 1, 2 };
-
-struct { int a[3], b; } sinit16[] = { { 1 }, 2 };
-
-struct bar {
-        char *s;
-        int len;
-} sinit17[] = {
-        "a1", 4,
-        "a2", 1
-};
-
-int sinit18[10] = {
-    [2 ... 5] = 20,
-    2,
-    [8] = 10,
-};
-
-struct complexinit0 {
-    int a;
-    int b;
-};
-
-struct complexinit {
-    int a;
-    const struct complexinit0 *b;
-};
-
-const static struct complexinit cix[] = {
-    [0] = {
-	.a = 2000,
-	.b = (const struct complexinit0[]) {
-		{ 2001, 2002 },
-		{ 2003, 2003 },
-		{}
-	}
-    }
-};
-
-struct complexinit2 {
-	int a;
-	int b[];
-};
-
-struct complexinit2 cix20;
-
-struct complexinit2 cix21 = {
-	.a = 3000,
-	.b = { 3001, 3002, 3003 }
-};
-
-struct complexinit2 cix22 = {
-	.a = 4000,
-	.b = { 4001, 4002, 4003, 4004, 4005, 4006 }
-};
-
-typedef int arrtype1[];
-arrtype1 sinit19 = {1};
-arrtype1 sinit20 = {2,3};
-typedef int arrtype2[3];
-arrtype2 sinit21 = {4};
-arrtype2 sinit22 = {5,6,7};
-
-/* Address comparisons of non-weak symbols with zero can be const-folded */
-int sinit23[2] = { "astring" ? sizeof("astring") : -1,
-		   &sinit23 ? 42 : -1 };
-
-extern int external_inited = 42;
-
-void init_test(void)
-{
-    int linit1 = 2;
-    int linit2 = { 3 };
-    int linit4[3][2] = { {1, 2}, {3, 4}, {5, 6} };
-    int linit6[] = { 1, 2, 3 };
-    int i, j;
-    char linit8[] = "hello" "trala";
-    int linit12[10] = { 1, 2 };
-    int linit13[10] = { 1, 2, [7] = 3, [3] = 4, };
-    char linit14[10] = "abc";
-    int linit15[10] = { linit1, linit1 + 1, [6] = linit1 + 2, };
-    struct linit16 { int a1, a2, a3, a4; } linit16 = { 1, .a3 = 2 };
-    int linit17 = sizeof(linit17);
-    int zero = 0;
-    /* Addresses on non-weak symbols are non-zero, but not the access itself */
-    int linit18[2] = {&zero ? 1 : -1, zero ? -1 : 1 };
-    
-    printf("init_test:\n");
-
-    printf("sinit1=%d\n", sinit1);
-    printf("sinit2=%d\n", sinit2);
-    printf("sinit3=%d %d %d %d\n", 
-           sizeof(sinit3),
-           sinit3[0],
-           sinit3[1],
-           sinit3[2]
-           );
-    printf("sinit6=%d\n", sizeof(sinit6));
-    printf("sinit7=%d %d %d %d\n", 
-           sizeof(sinit7),
-           sinit7[0],
-           sinit7[1],
-           sinit7[2]
-           );
-    printf("sinit8=%s\n", sinit8);
-    printf("sinit9=%d %d %d\n", 
-           sinit9.f1,
-           sinit9.f2,
-           sinit9.f3
-           );
-    printf("sinit10=%d %d %d\n", 
-           sinit10.f1,
-           sinit10.f2,
-           sinit10.f3
-           );
-    printf("sinit11=%d %d %d %d %d %d\n", 
-           sinit11.f1,
-           sinit11.f2,
-           sinit11.f3,
-           sinit11.farray[0],
-           sinit11.farray[1],
-           sinit11.farray[2]
-           );
-
-    for(i=0;i<3;i++)
-        for(j=0;j<2;j++)
-            printf("[%d][%d] = %d %d %d\n", 
-                   i, j, sinit4[i][j], sinit5[i][j], linit4[i][j]);
-    printf("linit1=%d\n", linit1);
-    printf("linit2=%d\n", linit2);
-    printf("linit6=%d\n", sizeof(linit6));
-    printf("linit8=%d %s\n", sizeof(linit8), linit8);
-
-    printf("sinit12=%s\n", sinit12);
-    printf("sinit13=%d %s %s %s\n",
-           sizeof(sinit13), 
-           sinit13[0],
-           sinit13[1],
-           sinit13[2]);
-    printf("sinit14=%s\n", sinit14);
-
-    for(i=0;i<10;i++) printf(" %d", linit12[i]);
-    printf("\n");
-    for(i=0;i<10;i++) printf(" %d", linit13[i]);
-    printf("\n");
-    for(i=0;i<10;i++) printf(" %d", linit14[i]);
-    printf("\n");
-    for(i=0;i<10;i++) printf(" %d", linit15[i]);
-    printf("\n");
-    printf("%d %d %d %d\n", 
-           linit16.a1,
-           linit16.a2,
-           linit16.a3,
-           linit16.a4);
-    /* test that initialisation is done after variable declare */
-    printf("linit17=%d\n", linit17);
-    printf("sinit15=%d\n", sinit15[0]);
-    printf("sinit16=%d %d\n", sinit16[0].a[0], sinit16[1].a[0]);
-    printf("sinit17=%s %d %s %d\n",
-           sinit17[0].s, sinit17[0].len,
-           sinit17[1].s, sinit17[1].len);
-    for(i=0;i<10;i++)
-        printf("%x ", sinit18[i]);
-    printf("\n");
-    /* complex init check */
-    printf("cix: %d %d %d %d %d %d %d\n",
-	cix[0].a,
-	cix[0].b[0].a, cix[0].b[0].b,
-	cix[0].b[1].a, cix[0].b[1].b,
-	cix[0].b[2].a, cix[0].b[2].b);
-    printf("cix2: %d %d\n", cix21.b[2], cix22.b[5]);
-    printf("sizeof cix20 %d, cix21 %d, sizeof cix22 %d\n", sizeof cix20, sizeof cix21, sizeof cix22);
-
-    printf("arrtype1: %d %d %d\n", sinit19[0], sinit20[0], sinit20[1]);
-    printf("arrtype2: %d %d\n", sizeof(sinit19), sizeof(sinit20));
-    printf("arrtype3: %d %d %d\n", sinit21[0], sinit21[1], sinit21[2]);
-    printf("arrtype4: %d %d %d\n", sinit22[0], sinit22[1], sinit22[2]);
-    printf("arrtype5: %d %d\n", sizeof(sinit21), sizeof(sinit22));
-    printf("arrtype6: %d\n", sizeof(arrtype2));
-
-    printf("sinit23= %d %d\n", sinit23[0], sinit23[1]);
-    printf("linit18= %d %d\n", linit18[0], linit18[1]);
-}
-
-void switch_uc(unsigned char uc)
-{
-	switch (uc) {
-	    case 0xfb ... 0xfe:
-		printf("ucsw:1\n");
-		break;
-	    case 0xff:
-		printf("ucsw:2\n");
-		break;
-	    case 0 ... 5:
-		printf("ucsw:3\n");
-		break;
-	    default:
-		printf("ucsw: broken!\n");
-	}
-}
-
-void switch_sc(signed char sc)
-{
-	switch (sc) {
-	    case -5 ... -2:
-		printf("scsw:1\n");
-		break;
-	    case -1:
-		printf("scsw:2\n");
-		break;
-	    case 0 ... 5:
-		printf("scsw:3\n");
-		break;
-	    default:
-		printf("scsw: broken!\n");
-	}
-}
-
-void switch_test()
-{
-    int i;
-    unsigned long long ull;
-    long long ll;
-
-    for(i=0;i<15;i++) {
-        switch(i) {
-        case 0:
-        case 1:
-            printf("a");
-            break;
-        default:
-            printf("%d", i);
-            break;
-        case 8 ... 12:
-            printf("c");
-            break;
-        case 3:
-            printf("b");
-            break;
-        case 0xc33c6b9fU:
-        case 0x7c9eeeb9U:
-            break;
-        }
-    }
-    printf("\n");
-
-    for (i = 1; i <= 5; i++) {
-	ull = (unsigned long long)i << 61;
-	switch (ull) {
-	    case 1ULL << 61:
-		printf("ullsw:1\n");
-		break;
-	    case 2ULL << 61:
-		printf("ullsw:2\n");
-		break;
-	    case 3ULL << 61:
-		printf("ullsw:3\n");
-		break;
-	    case 4ULL << 61:
-		printf("ullsw:4\n");
-		break;
-	    case 5ULL << 61:
-		printf("ullsw:5\n");
-		break;
-	    default:
-		printf("ullsw: broken!\n");
-	}
-    }
-
-    for (i = 1; i <= 5; i++) {
-	ll = (long long)i << 61;
-	switch (ll) {
-	    case 1LL << 61:
-		printf("llsw:1\n");
-		break;
-	    case 2LL << 61:
-		printf("llsw:2\n");
-		break;
-	    case 3LL << 61:
-		printf("llsw:3\n");
-		break;
-	    case 4LL << 61:
-		printf("llsw:4\n");
-		break;
-	    case 5LL << 61:
-		printf("llsw:5\n");
-		break;
-	    default:
-		printf("llsw: broken!\n");
-	}
-    }
-
-    for (i = -5; i <= 5; i++) {
-	switch_uc((unsigned char)i);
-    }
-
-    for (i = -5; i <= 5; i++) {
-	switch_sc ((signed char)i);
-    }
-}
-
-/* ISOC99 _Bool type */
-void c99_bool_test(void)
-{
-#ifdef BOOL_ISOC99
-    int a;
-    _Bool b;
-
-    printf("bool_test:\n");
-    printf("sizeof(_Bool) = %d\n", sizeof(_Bool));
-    a = 3;
-    printf("cast: %d %d %d\n", (_Bool)10, (_Bool)0, (_Bool)a);
-    b = 3;
-    printf("b = %d\n", b);
-    b++;
-    printf("b = %d\n", b);
-#endif
-}
-
-void bitfield_test(void)
-{
-    int a;
-    short sa;
-    unsigned char ca;
-    struct sbf1 {
-        int f1 : 3;
-        int : 2;
-        int f2 : 1;
-        int : 0;
-        int f3 : 5;
-        int f4 : 7;
-        unsigned int f5 : 7;
-    } st1;
-    printf("bitfield_test:");
-    printf("sizeof(st1) = %d\n", sizeof(st1));
-
-    st1.f1 = 3;
-    st1.f2 = 1;
-    st1.f3 = 15;
-    a = 120;
-    st1.f4 = a;
-    st1.f5 = a;
-    st1.f5++;
-    printf("%d %d %d %d %d\n",
-           st1.f1, st1.f2, st1.f3, st1.f4, st1.f5);
-    sa = st1.f5;
-    ca = st1.f5;
-    printf("%d %d\n", sa, ca);
-
-    st1.f1 = 7;
-    if (st1.f1 == -1) 
-        printf("st1.f1 == -1\n");
-    else 
-        printf("st1.f1 != -1\n");
-    if (st1.f2 == -1) 
-        printf("st1.f2 == -1\n");
-    else 
-        printf("st1.f2 != -1\n");
-
-    struct sbf2 {
-        long long f1 : 45;
-        long long : 2;
-        long long f2 : 35;
-        unsigned long long f3 : 38;
-    } st2;
-    st2.f1 = 0x123456789ULL;
-    a = 120;
-    st2.f2 = (long long)a << 25;
-    st2.f3 = a;
-    st2.f2++;
-    printf("%lld %lld %lld\n", st2.f1, st2.f2, st2.f3);
-
-#if 0
-    Disabled for now until further clarification re GCC compatibility
-    struct sbf3 {
-        int f1 : 7;
-        int f2 : 1;
-        char f3;
-        int f4 : 8;
-        int f5 : 1;
-        int f6 : 16;
-    } st3;
-    printf("sizeof(st3) = %d\n", sizeof(st3));
-#endif
-
-    struct sbf4 {
-	int x : 31;
-	char y : 2;
-    } st4;
-    st4.y = 1;
-    printf("st4.y == %d\n", st4.y);
-    struct sbf5 {
-	int a;
-	char b;
-	int x : 12, y : 4, : 0, : 4, z : 3;
-	char c;
-    } st5 = { 1, 2, 3, 4, -3, 6 };
-    printf("st5 = %d %d %d %d %d %d\n", st5.a, st5.b, st5.x, st5.y, st5.z, st5.c);
-    struct sbf6 {
-	short x : 12;
-	unsigned char y : 2;
-    } st6;
-    st6.y = 1;
-    printf("st6.y == %d\n", st6.y);
-}
-
-#ifdef __x86_64__
-#define FLOAT_FMT "%f\n"
-#else
-/* x86's float isn't compatible with GCC */
-#define FLOAT_FMT "%.5f\n"
-#endif
-
-/* declare strto* functions as they are C99 */
-double strtod(const char *nptr, char **endptr);
-
-#if defined(_WIN32)
-float strtof(const char *nptr, char **endptr) {return (float)strtod(nptr, endptr);}
-LONG_DOUBLE strtold(const char *nptr, char **endptr) {return (LONG_DOUBLE)strtod(nptr, endptr);}
-#else
-float strtof(const char *nptr, char **endptr);
-LONG_DOUBLE strtold(const char *nptr, char **endptr);
-#endif
-
-#define FTEST(prefix, typename, type, fmt)\
-void prefix ## cmp(type a, type b)\
-{\
-    printf("%d %d %d %d %d %d\n",\
-           a == b,\
-           a != b,\
-           a < b,\
-           a > b,\
-           a >= b,\
-           a <= b);\
-    printf(fmt " " fmt " " fmt " " fmt " " fmt " " fmt " " fmt "\n",\
-           a,\
-           b,\
-           a + b,\
-           a - b,\
-           a * b,\
-           a / b,\
-           -a);\
-    printf(fmt "\n", ++a);\
-    printf(fmt "\n", a++);\
-    printf(fmt "\n", a);\
-    b = 0;\
-    printf("%d %d\n", !a, !b);\
-}\
-void prefix ## fcast(type a)\
-{\
-    float fa;\
-    double da;\
-    LONG_DOUBLE la;\
-    int ia;\
-    long long llia;\
-    unsigned int ua;\
-    unsigned long long llua;\
-    type b;\
-    fa = a;\
-    da = a;\
-    la = a;\
-    printf("ftof: %f %f %Lf\n", fa, da, la);\
-    ia = (int)a;\
-    llia = (long long)a;\
-    a = (a >= 0) ? a : -a;\
-    ua = (unsigned int)a;\
-    llua = (unsigned long long)a;\
-    printf("ftoi: %d %u %lld %llu\n", ia, ua, llia, llua);\
-    ia = -1234;\
-    ua = 0x81234500;\
-    llia = -0x123456789012345LL;\
-    llua = 0xf123456789012345LLU;\
-    b = ia;\
-    printf("itof: " fmt "\n", b);\
-    b = ua;\
-    printf("utof: " fmt "\n", b);\
-    b = llia;\
-    printf("lltof: " fmt "\n", b);\
-    b = llua;\
-    printf("ulltof: " fmt "\n", b);\
-}\
-\
-float prefix ## retf(type a) { return a; }\
-double prefix ## retd(type a) { return a; }\
-LONG_DOUBLE prefix ## retld(type a) { return a; }\
-\
-void prefix ## call(void)\
-{\
-    printf("float: " FLOAT_FMT, prefix ## retf(42.123456789));\
-    printf("double: %f\n", prefix ## retd(42.123456789));\
-    printf("long double: %Lf\n", prefix ## retld(42.123456789));\
-    printf("strto%s: %f\n", #prefix, (double)strto ## prefix("1.2", NULL));\
-}\
-\
-void prefix ## signed_zeros(void) \
-{\
-  type x = 0.0, y = -0.0, n, p;\
-  if (x == y)\
-    printf ("Test 1.0 / x != 1.0 / y  returns %d (should be 1).\n",\
-            1.0 / x != 1.0 / y);\
-  else\
-    printf ("x != y; this is wrong!\n");\
-\
-  n = -x;\
-  if (x == n)\
-    printf ("Test 1.0 / x != 1.0 / -x returns %d (should be 1).\n",\
-            1.0 / x != 1.0 / n);\
-  else\
-    printf ("x != -x; this is wrong!\n");\
-\
-  p = +y;\
-  if (x == p)\
-    printf ("Test 1.0 / x != 1.0 / +y returns %d (should be 1).\n",\
-            1.0 / x != 1.0 / p);\
-  else\
-    printf ("x != +y; this is wrong!\n");\
-  p = -y;\
-  if (x == p)\
-    printf ("Test 1.0 / x != 1.0 / -y returns %d (should be 0).\n",\
-            1.0 / x != 1.0 / p);\
-  else\
-    printf ("x != -y; this is wrong!\n");\
-}\
-void prefix ## test(void)\
-{\
-    printf("testing '%s'\n", #typename);\
-    prefix ## cmp(1, 2.5);\
-    prefix ## cmp(2, 1.5);\
-    prefix ## cmp(1, 1);\
-    prefix ## fcast(234.6);\
-    prefix ## fcast(-2334.6);\
-    prefix ## call();\
-    prefix ## signed_zeros();\
-}
-
-FTEST(f, float, float, "%f")
-FTEST(d, double, double, "%f")
-FTEST(ld, long double, LONG_DOUBLE, "%Lf")
-
-double ftab1[3] = { 1.2, 3.4, -5.6 };
-
-
-void float_test(void)
-{
-#if !defined(__arm__) || defined(__ARM_PCS_VFP)
-    float fa, fb;
-    double da, db;
-    int a;
-    unsigned int b;
-
-    printf("float_test:\n");
-    printf("sizeof(float) = %d\n", sizeof(float));
-    printf("sizeof(double) = %d\n", sizeof(double));
-    printf("sizeof(long double) = %d\n", sizeof(LONG_DOUBLE));
-    ftest();
-    dtest();
-    ldtest();
-    printf("%f %f %f\n", ftab1[0], ftab1[1], ftab1[2]);
-    printf("%f %f %f\n", 2.12, .5, 2.3e10);
-    //    printf("%f %f %f\n", 0x1234p12, 0x1e23.23p10, 0x12dp-10);
-    da = 123;
-    printf("da=%f\n", da);
-    fa = 123;
-    printf("fa=%f\n", fa);
-    a = 4000000000;
-    da = a;
-    printf("da = %f\n", da);
-    b = 4000000000;
-    db = b;
-    printf("db = %f\n", db);
-#endif
-}
-
-int fib(int n)
-{
-    if (n <= 2)
-        return 1;
-    else
-        return fib(n-1) + fib(n-2);
-}
-
-void funcptr_test()
-{
-    void (*func)(int);
-    int a;
-    struct {
-        int dummy;
-        void (*func)(int);
-    } st1;
-    long diff;
-
-    printf("funcptr:\n");
-    func = &num;
-    (*func)(12345);
-    func = num;
-    a = 1;
-    a = 1;
-    func(12345);
-    /* more complicated pointer computation */
-    st1.func = num;
-    st1.func(12346);
-    printf("sizeof1 = %d\n", sizeof(funcptr_test));
-    printf("sizeof2 = %d\n", sizeof funcptr_test);
-    printf("sizeof3 = %d\n", sizeof(&funcptr_test));
-    printf("sizeof4 = %d\n", sizeof &funcptr_test);
-    a = 0;
-    func = num + a;
-    diff = func - num;
-    func(42);
-    (func + diff)(42);
-    (num + a)(43);
-}
-
-void lloptest(long long a, long long b)
-{
-    unsigned long long ua, ub;
-
-    ua = a;
-    ub = b;
-    /* arith */
-    printf("arith: " LONG_LONG_FORMAT " " LONG_LONG_FORMAT " " LONG_LONG_FORMAT "\n",
-           a + b,
-           a - b,
-           a * b);
-    
-    if (b != 0) {
-        printf("arith1: " LONG_LONG_FORMAT " " LONG_LONG_FORMAT "\n",
-           a / b,
-           a % b);
-    }
-
-    /* binary */
-    printf("bin: " LONG_LONG_FORMAT " " LONG_LONG_FORMAT " " LONG_LONG_FORMAT "\n",
-           a & b,
-           a | b,
-           a ^ b);
-
-    /* tests */
-    printf("test: %d %d %d %d %d %d\n",
-           a == b,
-           a != b,
-           a < b,
-           a > b,
-           a >= b,
-           a <= b);
-    
-    printf("utest: %d %d %d %d %d %d\n",
-           ua == ub,
-           ua != ub,
-           ua < ub,
-           ua > ub,
-           ua >= ub,
-           ua <= ub);
-
-    /* arith2 */
-    a++;
-    b++;
-    printf("arith2: " LONG_LONG_FORMAT " " LONG_LONG_FORMAT "\n", a, b);
-    printf("arith2: " LONG_LONG_FORMAT " " LONG_LONG_FORMAT "\n", a++, b++);
-    printf("arith2: " LONG_LONG_FORMAT " " LONG_LONG_FORMAT "\n", --a, --b);
-    printf("arith2: " LONG_LONG_FORMAT " " LONG_LONG_FORMAT "\n", a, b);
-    b = ub = 0;
-    printf("not: %d %d %d %d\n", !a, !ua, !b, !ub);
-}
-
-void llshift(long long a, int b)
-{
-    printf("shift: " LONG_LONG_FORMAT " " LONG_LONG_FORMAT " " LONG_LONG_FORMAT "\n",
-           (unsigned long long)a >> b,
-           a >> b,
-           a << b);
-    printf("shiftc: " LONG_LONG_FORMAT " " LONG_LONG_FORMAT " " LONG_LONG_FORMAT "\n",
-           (unsigned long long)a >> 3,
-           a >> 3,
-           a << 3);
-    printf("shiftc: " LONG_LONG_FORMAT " " LONG_LONG_FORMAT " " LONG_LONG_FORMAT "\n",
-           (unsigned long long)a >> 35,
-           a >> 35,
-           a << 35);
-}
-
-void llfloat(void)
-{
-    float fa;
-    double da;
-    LONG_DOUBLE lda;
-    long long la, lb, lc;
-    unsigned long long ula, ulb, ulc;
-    la = 0x12345678;
-    ula = 0x72345678;
-    la = (la << 20) | 0x12345;
-    ula = ula << 33;
-    printf("la=" LONG_LONG_FORMAT " ula=" ULONG_LONG_FORMAT "\n", la, ula);
-
-    fa = la;
-    da = la;
-    lda = la;
-    printf("lltof: %f %f %Lf\n", fa, da, lda);
-
-    la = fa;
-    lb = da;
-    lc = lda;
-    printf("ftoll: " LONG_LONG_FORMAT " " LONG_LONG_FORMAT " " LONG_LONG_FORMAT "\n", la, lb, lc);
-
-    fa = ula;
-    da = ula;
-    lda = ula;
-    printf("ulltof: %f %f %Lf\n", fa, da, lda);
-
-    ula = fa;
-    ulb = da;
-    ulc = lda;
-    printf("ftoull: " ULONG_LONG_FORMAT " " ULONG_LONG_FORMAT " " ULONG_LONG_FORMAT "\n", ula, ulb, ulc);
-}
-
-long long llfunc1(int a)
-{
-    return a * 2;
-}
-
-struct S {
-    int id; 
-    char item;
-};
-
-long long int value(struct S *v)
-{
-    return ((long long int)v->item);
-}
-
-long long llfunc2(long long x, long long y, int z)
-{
-    return x * y * z;
-}
-
-void longlong_test(void)
-{
-    long long a, b, c;
-    int ia;
-    unsigned int ua;
-    printf("longlong_test:\n");
-    printf("sizeof(long long) = %d\n", sizeof(long long));
-    ia = -1;
-    ua = -2;
-    a = ia;
-    b = ua;
-    printf(LONG_LONG_FORMAT " " LONG_LONG_FORMAT "\n", a, b);
-    printf(LONG_LONG_FORMAT " " LONG_LONG_FORMAT " " LONG_LONG_FORMAT " %Lx\n", 
-           (long long)1, 
-           (long long)-2,
-           1LL,
-           0x1234567812345679);
-    a = llfunc1(-3);
-    printf(LONG_LONG_FORMAT "\n", a);
-
-    lloptest(1000, 23);
-    lloptest(0xff, 0x1234);
-    b = 0x72345678 << 10;
-    lloptest(-3, b);
-    llshift(0x123, 5);
-    llshift(-23, 5);
-    b = 0x72345678LL << 10;
-    llshift(b, 47);
-
-    llfloat();
-#if 1
-    b = 0x12345678;
-    a = -1;
-    c = a + b;
-    printf("%Lx\n", c);
-#endif
-
-    /* long long reg spill test */
-    {
-          struct S a;
-
-          a.item = 3;
-          printf("%lld\n", value(&a));
-    }
-    lloptest(0x80000000, 0);
-
-    {
-        long long *p, v, **pp;
-        v = 1;
-        p = &v;
-        p[0]++;
-        printf("another long long spill test : %lld\n", *p);
-        pp = &p;
-
-        v = llfunc2(**pp, **pp, ia);
-        printf("a long long function (arm-)reg-args test : %lld\n", v);
-    }
-    a = 68719476720LL;
-    b = 4294967295LL;
-    printf("%d %d %d %d\n", a > b, a < b, a >= b, a <= b);
-
-    printf(LONG_LONG_FORMAT "\n", 0x123456789LLU);
-
-    /* long long pointer deref in argument passing test */
-    a = 0x123;
-    long long *p = &a;
-    llshift(*p, 5);
-}
-
-void manyarg_test(void)
-{
-    LONG_DOUBLE ld = 1234567891234LL;
-    printf("manyarg_test:\n");
-    printf("%d %d %d %d %d %d %d %d %f %f %f %f %f %f %f %f %f %f\n",
-           1, 2, 3, 4, 5, 6, 7, 8,
-           0.1, 1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8, 8.9, 9.0);
-    printf("%d %d %d %d %d %d %d %d %f %f %f %f %f %f %f %f %f %f "
-           LONG_LONG_FORMAT " " LONG_LONG_FORMAT " %f %f\n",
-           1, 2, 3, 4, 5, 6, 7, 8,
-           0.1, 1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8, 8.9, 9.0,
-           1234567891234LL, 987654321986LL,
-           42.0, 43.0);
-    printf("%Lf %d %d %d %d %d %d %d %d %f %f %f %f %f %f %f %f %f %f "
-           LONG_LONG_FORMAT " " LONG_LONG_FORMAT " %f %f\n",
-           ld, 1, 2, 3, 4, 5, 6, 7, 8,
-           0.1, 1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8, 8.9, 9.0,
-           1234567891234LL, 987654321986LL,
-           42.0, 43.0);
-    printf("%d %d %d %d %d %d %d %d %Lf\n",
-           1, 2, 3, 4, 5, 6, 7, 8, ld);
-    printf("%d %d %d %d %d %d %d %d %f %f %f %f %f %f %f %f %f %f "
-           LONG_LONG_FORMAT " " LONG_LONG_FORMAT "%f %f %Lf\n",
-           1, 2, 3, 4, 5, 6, 7, 8,
-           0.1, 1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8, 8.9, 9.0,
-           1234567891234LL, 987654321986LL,
-           42.0, 43.0, ld);
-    printf("%d %d %d %d %d %d %d %d %f %f %f %f %f %f %f %f %f %f "
-           "%Lf " LONG_LONG_FORMAT " " LONG_LONG_FORMAT " %f %f %Lf\n",
-           1, 2, 3, 4, 5, 6, 7, 8,
-           0.1, 1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8, 8.9, 9.0,
-           ld, 1234567891234LL, 987654321986LL,
-           42.0, 43.0, ld);
-}
-
-void vprintf1(const char *fmt, ...)
-{
-    va_list ap, aq;
-    const char *p;
-    int c, i;
-    double d;
-    long long ll;
-    LONG_DOUBLE ld;
-
-    va_start(aq, fmt);
-    va_copy(ap, aq);
-    
-    p = fmt;
-    for(;;) {
-        c = *p;
-        if (c == '\0')
-            break;
-        p++;
-        if (c == '%') {
-            c = *p;
-            switch(c) {
-            case '\0':
-                goto the_end;
-            case 'd':
-                i = va_arg(ap, int);
-                printf("%d", i);
-                break;
-            case 'f':
-                d = va_arg(ap, double);
-                printf("%f", d);
-                break;
-            case 'l':
-                ll = va_arg(ap, long long);
-                printf(LONG_LONG_FORMAT, ll);
-                break;
-            case 'F':
-                ld = va_arg(ap, LONG_DOUBLE);
-                printf("%Lf", ld);
-                break;
-            }
-            p++;
-        } else {
-            putchar(c);
-        }
-    }
- the_end:
-    va_end(aq);
-    va_end(ap);
-}
-
-struct myspace {
-    short int profile;
-};
-
-void stdarg_for_struct(struct myspace bob, ...)
-{
-    struct myspace george, bill;
-    va_list ap;
-    short int validate;
-
-    va_start(ap, bob);
-    bill     = va_arg(ap, struct myspace);
-    george   = va_arg(ap, struct myspace);
-    validate = va_arg(ap, int);
-    printf("stdarg_for_struct: %d %d %d %d\n",
-           bob.profile, bill.profile, george.profile, validate);
-    va_end(ap);
-}
-
-void stdarg_for_libc(const char *fmt, ...)
-{
-    va_list args;
-    va_start(args, fmt);
-    vprintf(fmt, args);
-    va_end(args);
-}
-
-void stdarg_test(void)
-{
-    LONG_DOUBLE ld = 1234567891234LL;
-    struct myspace bob;
-
-    vprintf1("%d %d %d\n", 1, 2, 3);
-    vprintf1("%f %d %f\n", 1.0, 2, 3.0);
-    vprintf1("%l %l %d %f\n", 1234567891234LL, 987654321986LL, 3, 1234.0);
-    vprintf1("%F %F %F\n", LONG_DOUBLE_LITERAL(1.2), LONG_DOUBLE_LITERAL(2.3), LONG_DOUBLE_LITERAL(3.4));
-    vprintf1("%d %f %l %F %d %f %l %F\n",
-             1, 1.2, 3LL, LONG_DOUBLE_LITERAL(4.5), 6, 7.8, 9LL, LONG_DOUBLE_LITERAL(0.1));
-    vprintf1("%d %d %d %d %d %d %d %d %f %f %f %f %f %f %f %f\n",
-             1, 2, 3, 4, 5, 6, 7, 8,
-             0.1, 1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8);
-    vprintf1("%d %d %d %d %d %d %d %d %f %f %f %f %f %f %f %f %f %f\n",
-             1, 2, 3, 4, 5, 6, 7, 8,
-             0.1, 1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8, 8.9, 9.0);
-    vprintf1("%d %d %d %d %d %d %d %d %f %f %f %f %f %f %f %f %f %f "
-             "%l %l %f %f\n",
-             1, 2, 3, 4, 5, 6, 7, 8,
-             0.1, 1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8, 8.9, 9.0,
-             1234567891234LL, 987654321986LL,
-             42.0, 43.0);
-    vprintf1("%F %d %d %d %d %d %d %d %d %f %f %f %f %f %f %f %f %f %f "
-             "%l %l %f %f\n",
-             ld, 1, 2, 3, 4, 5, 6, 7, 8,
-             0.1, 1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8, 8.9, 9.0,
-             1234567891234LL, 987654321986LL,
-             42.0, 43.0);
-    vprintf1("%d %d %d %d %d %d %d %d %F\n",
-             1, 2, 3, 4, 5, 6, 7, 8, ld);
-    vprintf1("%d %d %d %d %d %d %d %d %f %f %f %f %f %f %f %f %f %f "
-             "%l %l %f %f %F\n",
-             1, 2, 3, 4, 5, 6, 7, 8,
-             0.1, 1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8, 8.9, 9.0,
-             1234567891234LL, 987654321986LL,
-             42.0, 43.0, ld);
-    vprintf1("%d %d %d %d %d %d %d %d %f %f %f %f %f %f %f %f %f %f "
-             "%F %l %l %f %f %F\n",
-             1, 2, 3, 4, 5, 6, 7, 8,
-             0.1, 1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8, 8.9, 9.0,
-             ld, 1234567891234LL, 987654321986LL,
-             42.0, 43.0, ld);
-
-    bob.profile = 42;
-    stdarg_for_struct(bob, bob, bob, bob.profile);
-    stdarg_for_libc("stdarg_for_libc: %s %.2f %d\n", "string", 1.23, 456);
-}
-
-void whitespace_test(void)
-{
-    char *str;
-
-
#if 1
-    pri\
-ntf("whitspace:\n");

-#endif
-    pf("N=%d\n", 2);
-
-#ifdef CORRECT_CR_HANDLING
-    pri\

-ntf("aaa=%d\n", 3);
-#endif
-
-    pri\
-\
-ntf("min=%d\n", 4);
-
-#ifdef ACCEPT_CR_IN_STRINGS
-    printf("len1=%d\n", strlen("
-"));
-#ifdef CORRECT_CR_HANDLING
-    str = "

-";
-    printf("len1=%d str[0]=%d\n", strlen(str), str[0]);
-#endif
-    printf("len1=%d\n", strlen("
a
-"));
-#endif /* ACCEPT_CR_IN_STRINGS */
-}
-
-int reltab[3] = { 1, 2, 3 };
-
-int *rel1 = &reltab[1];
-int *rel2 = &reltab[2];
-
-#ifdef _WIN64
-void relocation_test(void) {}
-#else
-void getmyaddress(void)
-{
-    printf("in getmyaddress\n");
-}
-
-#ifdef __LP64__
-long __pa_symbol(void)
-{
-    /* This 64bit constant was handled incorrectly, it was used as addend
-       (which can hold 64bit just fine) in connection with a symbol,
-       and TCC generates wrong code for that (displacements are 32bit only).
-       This effectively is "+ 0x80000000", and if addresses of globals
-       are below 2GB the result should be a number without high 32 bits set.  */
-       return ((long)(((unsigned long)(&rel1))) - (0xffffffff80000000UL));
-}
-#endif
-
-unsigned long theaddress = (unsigned long)getmyaddress;
-void relocation_test(void)
-{
-    void (*fptr)(void) = (void (*)(void))theaddress;
-    printf("*rel1=%d\n", *rel1);
-    printf("*rel2=%d\n", *rel2);
-    fptr();
-#ifdef __LP64__
-    printf("pa_symbol=0x%lx\n", __pa_symbol() >> 63);
-#endif
-}
-#endif
-
-void old_style_f(a,b,c)
-     int a, b;
-     double c;
-{
-    printf("a=%d b=%d b=%f\n", a, b, c);
-}
-
-void decl_func1(int cmpfn())
-{
-    printf("cmpfn=%lx\n", (long)cmpfn);
-}
-
-void decl_func2(cmpfn)
-int cmpfn();
-{
-    printf("cmpfn=%lx\n", (long)cmpfn);
-}
-
-void old_style_function(void)
-{
-    old_style_f((void *)1, 2, 3.0);
-    decl_func1(NULL);
-    decl_func2(NULL);
-}
-
-void alloca_test()
-{
-#if defined __i386__ || defined __x86_64__ || defined __arm__
-    char *p = alloca(16);
-    strcpy(p,"123456789012345");
-    printf("alloca: p is %s\n", p);
-    char *demo = "This is only a test.\n";
-    /* Test alloca embedded in a larger expression */
-    printf("alloca: %s\n", strcpy(alloca(strlen(demo)+1),demo) );
-#endif
-}
-
-void *bounds_checking_is_enabled()
-{
-    char ca[10], *cp = ca-1;
-    return (ca != cp + 1) ? cp : NULL;
-}
-
-typedef int constant_negative_array_size_as_compile_time_assertion_idiom[(1 ? 2 : 0) - 1];
-
-void c99_vla_test(int size1, int size2)
-{
-#if defined __i386__ || defined __x86_64__
-    int size = size1 * size2;
-    int tab1[size][2], tab2[10][2];
-    void *tab1_ptr, *tab2_ptr, *bad_ptr;
-
-    /* "size" should have been 'captured' at tab1 declaration, 
-        so modifying it should have no effect on VLA behaviour. */
-    size = size-1;
-    
-    printf("Test C99 VLA 1 (sizeof): ");
-    printf("%s\n", (sizeof tab1 == size1 * size2 * 2 * sizeof(int)) ? "PASSED" : "FAILED");
-    tab1_ptr = tab1;
-    tab2_ptr = tab2;
-    printf("Test C99 VLA 2 (ptrs subtract): ");
-    printf("%s\n", (tab2 - tab1 == (tab2_ptr - tab1_ptr) / (sizeof(int) * 2)) ? "PASSED" : "FAILED");
-    printf("Test C99 VLA 3 (ptr add): ");
-    printf("%s\n", &tab1[5][1] == (tab1_ptr + (5 * 2 + 1) * sizeof(int)) ? "PASSED" : "FAILED");
-    printf("Test C99 VLA 4 (ptr access): ");
-    tab1[size1][1] = 42;
-    printf("%s\n", (*((int *) (tab1_ptr + (size1 * 2 + 1) * sizeof(int))) == 42) ? "PASSED" : "FAILED");
-
-    printf("Test C99 VLA 5 (bounds checking (might be disabled)): ");
-    if (bad_ptr = bounds_checking_is_enabled()) {
-        int *t1 = &tab1[size1 * size2 - 1][3];
-        int *t2 = &tab2[9][3];
-        printf("%s ", bad_ptr == t1 ? "PASSED" : "FAILED");
-        printf("%s ", bad_ptr == t2 ? "PASSED" : "FAILED");
-
-        char*c1 = 1 + sizeof(tab1) + (char*)tab1;
-        char*c2 = 1 + sizeof(tab2) + (char*)tab2;
-        printf("%s ", bad_ptr == c1 ? "PASSED" : "FAILED");
-        printf("%s ", bad_ptr == c2 ? "PASSED" : "FAILED");
-
-        int *i1 = tab1[-1];
-        int *i2 = tab2[-1];
-        printf("%s ", bad_ptr == i1 ? "PASSED" : "FAILED");
-        printf("%s ", bad_ptr == i2 ? "PASSED" : "FAILED");
-
-        int *x1 = tab1[size1 * size2 + 1];
-        int *x2 = tab2[10 + 1];
-        printf("%s ", bad_ptr == x1 ? "PASSED" : "FAILED");
-        printf("%s ", bad_ptr == x2 ? "PASSED" : "FAILED");
-    } else {
-        printf("PASSED PASSED PASSED PASSED PASSED PASSED PASSED PASSED ");
-    }
-    printf("\n");
-#endif
-}
-
-#ifndef __TINYC__
-typedef __SIZE_TYPE__ uintptr_t;
-#endif
-
-void sizeof_test(void)
-{
-    int a;
-    int **ptr;
-
-    printf("sizeof(int) = %d\n", sizeof(int));
-    printf("sizeof(unsigned int) = %d\n", sizeof(unsigned int));
-    printf("sizeof(long) = %d\n", sizeof(long));
-    printf("sizeof(unsigned long) = %d\n", sizeof(unsigned long));
-    printf("sizeof(short) = %d\n", sizeof(short));
-    printf("sizeof(unsigned short) = %d\n", sizeof(unsigned short));
-    printf("sizeof(char) = %d\n", sizeof(char));
-    printf("sizeof(unsigned char) = %d\n", sizeof(unsigned char));
-    printf("sizeof(func) = %d\n", sizeof sizeof_test());
-    a = 1;
-    printf("sizeof(a++) = %d\n", sizeof a++);
-    printf("a=%d\n", a);
-    ptr = NULL;
-    printf("sizeof(**ptr) = %d\n", sizeof (**ptr));
-
-    /* The type of sizeof should be as large as a pointer, actually
-       it should be size_t.  */
-    printf("sizeof(sizeof(int) = %d\n", sizeof(sizeof(int)));
-    uintptr_t t = 1;
-    uintptr_t t2;
-    /* Effectively <<32, but defined also on 32bit machines.  */
-    t <<= 16;
-    t <<= 16;
-    t++;
-    /* This checks that sizeof really can be used to manipulate 
-       uintptr_t objects, without truncation.  */
-    t2 = t & -sizeof(uintptr_t);
-    printf ("%lu %lu\n", t, t2);
-
-    /* some alignof tests */
-    printf("__alignof__(int) = %d\n", __alignof__(int));
-    printf("__alignof__(unsigned int) = %d\n", __alignof__(unsigned int));
-    printf("__alignof__(short) = %d\n", __alignof__(short));
-    printf("__alignof__(unsigned short) = %d\n", __alignof__(unsigned short));
-    printf("__alignof__(char) = %d\n", __alignof__(char));
-    printf("__alignof__(unsigned char) = %d\n", __alignof__(unsigned char));
-    printf("__alignof__(func) = %d\n", __alignof__ sizeof_test());
-
-    /* sizes of VLAs need to be evaluated even inside sizeof: */
-    a = 2;
-    printf("sizeof(char[1+2*a]) = %d\n", sizeof(char[1+2*a]));
-    /* And checking if sizeof compound literal works.  Parenthesized: */
-    printf("sizeof( (struct {int i; int j;}){4,5} ) = %d\n",
-	   sizeof( (struct {int i; int j;}){4,5} ));
-    /* And as direct sizeof argument (as unary expression): */
-    printf("sizeof (struct {short i; short j;}){4,5} = %d\n",
-	   sizeof (struct {short i; short j;}){4,5} );
-
-    /* sizeof(x && y) should be sizeof(int), even if constant
-       evaluating is possible. */
-    printf("sizeof(t && 0) = %d\n", sizeof(t && 0));
-    printf("sizeof(1 && 1) = %d\n", sizeof(1 && 1));
-    printf("sizeof(t || 1) = %d\n", sizeof(t || 1));
-    printf("sizeof(0 || 0) = %d\n", sizeof(0 || 0));
-}
-
-void typeof_test(void)
-{
-    double a;
-    typeof(a) b;
-    typeof(float) c;
-
-    a = 1.5;
-    b = 2.5;
-    c = 3.5;
-    printf("a=%f b=%f c=%f\n", a, b, c);
-}
-
-
-struct hlist_node;
-struct hlist_head {
-    struct hlist_node *first, *last;
-};
-
-void consume_ulong (unsigned long i)
-{
-    i = 0;
-}
-
-void statement_expr_test(void)
-{
-    int a, i;
-
-    /* Basic stmt expr test */
-    a = 0;
-    for(i=0;i<10;i++) {
-        a += 1 + 
-            ( { int b, j; 
-                b = 0; 
-                for(j=0;j<5;j++) 
-                    b += j; b; 
-            } );
-    }
-    printf("a=%d\n", a);
-    
-    /* Test that symbols aren't freed prematurely.
-       With SYM_DEBUG valgrind will show a read from a freed
-       symbol, and tcc will show an (invalid) warning on the initialization
-       of 'ptr' below, if symbols are popped after the stmt expr.  */
-    void *v = (void*)39;
-    typeof(({
-	    (struct hlist_node *)v;
-	    })) x;
-    typeof (x)
-	ptr = (struct hlist_node *)v;
-
-    /* This part used to segfault when symbols were popped prematurely.
-       The symbols for the static local would be overwritten with
-       helper symbols from the pre-processor expansions in between.  */
-#define some_attr     __attribute__((aligned(1)))
-#define tps(str) ({                  \
-            static const char *t some_attr = str; \
-            t;                                    \
-          })
-    printf ("stmtexpr: %s %s\n",
-	    tps("somerandomlongstring"),
-	    tps("anotherlongstring"));
-
-    /* Test that the three decls of 't' don't interact.  */
-    int t = 40;
-    int b = ({ int t = 41; t; });
-    int c = ({ int t = 42; t; });
-
-    /* Test that aggregate return values work.  */
-    struct hlist_head h
-	= ({
-	   typedef struct hlist_head T;
-	   long pre = 48;
-	   T t = { (void*)43, (void*)44 };
-	   long post = 49;
-	   t;
-	   });
-    printf ("stmtexpr: %d %d %d\n", t, b, c);
-    printf ("stmtexpr: %ld %ld\n", (long)h.first, (long)h.last);
-
-    /* Test that we can give out addresses of local labels.  */
-    consume_ulong(({ __label__ __here; __here: (unsigned long)&&__here; }));
-}
-
-void local_label_test(void)
-{
-    int a;
-    goto l1;
- l2:
-    a = 1 + ({
-        __label__ l1, l2, l3, l4;
-        goto l1;
-    l4:
-        printf("aa1\n");
-        goto l3;
-    l2:
-        printf("aa3\n");
-        goto l4;
-    l1:
-        printf("aa2\n");
-        goto l2;
-    l3:;
-        1;
-    });
-    printf("a=%d\n", a);
-    return;
- l4:
-    printf("bb1\n");
-    goto l2;
- l1:
-    printf("bb2\n");
-    goto l4;
-}
-
-/* inline assembler test */
-#if defined(__i386__) || defined(__x86_64__)
-
-/* from linux kernel */
-static char * strncat1(char * dest,const char * src,size_t count)
-{
-long d0, d1, d2, d3;
-__asm__ __volatile__(
-	"repne\n\t"
-	"scasb\n\t"
-	"dec %1\n\t"
-	"mov %8,%3\n"
-	"1:\tdec %3\n\t"
-	"js 2f\n\t"
-	"lodsb\n\t"
-	"stosb\n\t"
-	"testb %%al,%%al\n\t"
-	"jne 1b\n"
-	"2:\txor %2,%2\n\t"
-	"stosb"
-	: "=&S" (d0), "=&D" (d1), "=&a" (d2), "=&c" (d3)
-	: "0" (src),"1" (dest),"2" (0),"3" (0xffffffff), "g" (count)
-	: "memory");
-return dest;
-}
-
-static char * strncat2(char * dest,const char * src,size_t count)
-{
-long d0, d1, d2, d3;
-__asm__ __volatile__(
-	"repne scasb\n\t" /* one-line repne prefix + string op */
-	"dec %1\n\t"
-	"mov %8,%3\n"
-	"1:\tdec %3\n\t"
-	"js 2f\n\t"
-	"lodsb\n\t"
-	"stosb\n\t"
-	"testb %%al,%%al\n\t"
-	"jne 1b\n"
-	"2:\txor %2,%2\n\t"
-	"stosb"
-	: "=&S" (d0), "=&D" (d1), "=&a" (d2), "=&c" (d3)
-	: "0" (src),"1" (dest),"2" (0),"3" (0xffffffff), "g" (count)
-	: "memory");
-return dest;
-}
-
-static inline void * memcpy1(void * to, const void * from, size_t n)
-{
-long d0, d1, d2;
-__asm__ __volatile__(
-	"rep ; movsl\n\t"
-	"testb $2,%b4\n\t"
-	"je 1f\n\t"
-	"movsw\n"
-	"1:\ttestb $1,%b4\n\t"
-	"je 2f\n\t"
-	"movsb\n"
-	"2:"
-	: "=&c" (d0), "=&D" (d1), "=&S" (d2)
-	:"0" (n/4), "q" (n),"1" ((long) to),"2" ((long) from)
-	: "memory");
-return (to);
-}
-
-static inline void * memcpy2(void * to, const void * from, size_t n)
-{
-long d0, d1, d2;
-__asm__ __volatile__(
-	"rep movsl\n\t"  /* one-line rep prefix + string op */
-	"testb $2,%b4\n\t"
-	"je 1f\n\t"
-	"movsw\n"
-	"1:\ttestb $1,%b4\n\t"
-	"je 2f\n\t"
-	"movsb\n"
-	"2:"
-	: "=&c" (d0), "=&D" (d1), "=&S" (d2)
-	:"0" (n/4), "q" (n),"1" ((long) to),"2" ((long) from)
-	: "memory");
-return (to);
-}
-
-static __inline__ void sigaddset1(unsigned int *set, int _sig)
-{
-	__asm__("btsl %1,%0" : "=m"(*set) : "Ir"(_sig - 1) : "cc");
-}
-
-static __inline__ void sigdelset1(unsigned int *set, int _sig)
-{
-	asm("btrl %1,%0" : "=m"(*set) : "Ir"(_sig - 1) : "cc", "flags");
-}
-
-static __inline__ __const__ unsigned int swab32(unsigned int x)
-{
-	__asm__("xchgb %b0,%h0\n\t"	/* swap lower bytes	*/
-		"rorl $16,%0\n\t"	/* swap words		*/
-		"xchgb %b0,%h0"		/* swap higher bytes	*/
-		:"=" "q" (x)
-		: "0" (x));
-	return x;
-}
-
-static __inline__ unsigned long long mul64(unsigned int a, unsigned int b)
-{
-    unsigned long long res;
-#ifdef __x86_64__
-    /* Using the A constraint is wrong (it means rdx:rax, which is too large)
-       but still test the 32bit->64bit mull.  */
-    unsigned int resh, resl;
-    __asm__("mull %2" : "=a" (resl), "=d" (resh) : "a" (a), "r" (b));
-    res = ((unsigned long long)resh << 32) | resl;
-#else
-    __asm__("mull %2" : "=A" (res) : "a" (a), "r" (b));
-#endif
-    return res;
-}
-
-static __inline__ unsigned long long inc64(unsigned long long a)
-{
-    unsigned long long res;
-#ifdef __x86_64__
-    /* Using the A constraint is wrong, and increments are tested
-       elsewhere.  */
-    res = a + 1;
-#else
-    __asm__("addl $1, %%eax ; adcl $0, %%edx" : "=A" (res) : "A" (a));
-#endif
-    return res;
-}
-
-struct struct123 {
-    int a;
-    int b;
-};
-struct struct1231 {
-    unsigned long addr;
-};
-
-unsigned long mconstraint_test(struct struct1231 *r)
-{
-    unsigned long ret;
-    unsigned int a[2];
-    a[0] = 0;
-    __asm__ volatile ("lea %2,%0; movl 4(%0),%k0; addl %2,%k0; movl $51,%2; movl $52,4%2; movl $63,%1"
-	             : "=&r" (ret), "=m" (a)
-	             : "m" (*(struct struct123 *)r->addr));
-    return ret + a[0];
-}
-
-#ifdef __x86_64__
-int fls64(unsigned long long x)
-{
-  int bitpos = -1;
-  asm("bsrq %1,%q0"
-      : "+r" (bitpos)
-      : "rm" (x));
-  return bitpos + 1;
-}
-#endif
-
-void other_constraints_test(void)
-{
-    unsigned long ret;
-    int var;
-#ifndef _WIN64
-    __asm__ volatile ("mov %P1,%0" : "=r" (ret) : "p" (&var));
-    printf ("oc1: %d\n", ret == (unsigned long)&var);
-#endif
-}
-
-#ifndef _WIN32
-/* Test global asm blocks playing with aliases.  */
-void base_func(void)
-{
-  printf ("asmc: base\n");
-}
-
-extern void override_func1 (void);
-extern void override_func2 (void);
-
-asm(".weak override_func1\n.set override_func1, base_func");
-asm(".set override_func1, base_func");
-asm(".set override_func2, base_func");
-
-void override_func2 (void)
-{
-  printf ("asmc: override2\n");
-}
-
-/* This checks a construct used by the linux kernel to encode
-   references to strings by PC relative references.  */
-extern int bug_table[] __attribute__((section("__bug_table")));
-char * get_asm_string (void)
-{
-  extern int some_symbol;
-  asm volatile (".globl some_symbol\n"
-		"jmp .+6\n"
-		"1:\n"
-		"some_symbol: .long 0\n"
-		".pushsection __bug_table, \"a\"\n"
-		".globl bug_table\n"
-		"bug_table:\n"
-		/* The first entry (1b-2b) is unused in this test,
-		   but we include it to check if cross-section
-		   PC-relative references work.  */
-		"2:\t.long 1b - 2b, %c0 - 2b\n"
-		".popsection\n" : : "i" ("A string"));
-  char * str = ((char*)bug_table) + bug_table[1];
-  return str;
-}
-
-/* This checks another constructs with local labels.  */
-extern unsigned char alld_stuff[];
-asm(".data\n"
-    ".byte 41\n"
-    "alld_stuff:\n"
-    "661:\n"
-    ".byte 42\n"
-    "662:\n"
-    ".pushsection .data.ignore\n"
-    ".long 661b - .\n" /* This reference to 661 generates an external sym
-                          which shouldn't somehow overwrite the offset that's
-                          already determined for it.  */
-    ".popsection\n"
-    ".byte 662b - 661b\n"  /* So that this value is undeniably 1.  */);
-
-void asm_local_label_diff (void)
-{
-  printf ("asm_local_label_diff: %d %d\n", alld_stuff[0], alld_stuff[1]);
-}
-
-/* This checks that static local variables are available from assembler.  */
-void asm_local_statics (void)
-{
-  static int localint = 41;
-  asm("incl %0" : "+m" (localint));
-  printf ("asm_local_statics: %d\n", localint);
-}
-#endif
-
-static
-unsigned int set;
-
-void fancy_copy (unsigned *in, unsigned *out)
-{
-  asm volatile ("" : "=r" (*out) : "0" (*in));
-}
-
-void fancy_copy2 (unsigned *in, unsigned *out)
-{
-  asm volatile ("mov %0,(%1)" : : "r" (*in), "r" (out) : "memory");
-}
-
-#if defined __x86_64__ && !defined _WIN64
-void clobber_r12(void)
-{
-    asm volatile("mov $1, %%r12" ::: "r12");
-}
-#endif
-
-void test_high_clobbers(void)
-{
-#if defined __x86_64__ && !defined _WIN64
-    register long val asm("r12");
-    long val2;
-    /* This tests if asm clobbers correctly save/restore callee saved
-       registers if they are clobbered and if it's the high 8 x86-64
-       registers.  This is fragile for GCC as the constraints do not
-       correctly capture the data flow, but good enough for us.  */
-    asm volatile("mov $0x4542, %%r12" : "=r" (val):: "memory");
-    clobber_r12();
-    asm volatile("mov %%r12, %0" : "=r" (val2) : "r" (val): "memory");
-    printf("asmhc: 0x%x\n", val2);
-#endif
-}
-
-static long cpu_number;
-void trace_console(long len, long len2)
-{
-#ifdef __x86_64__
-    /* This generated invalid code when the emission of the switch
-       table isn't disabled.  The asms are necessary to show the bug,
-       normal statements don't work (they need to generate some code
-       even under nocode_wanted, which normal statements don't do,
-       but asms do).  Also at least these number of cases is necessary
-       to generate enough "random" bytes.  They ultimately are enough
-       to create invalid instruction patterns to which the first
-       skip-to-decision-table jump jumps.  If decision table emission
-       is disabled all of this is no problem.
-
-       It also is necessary that the switches are in a statement expression
-       (which has the property of not being enterable from outside. no
-       matter what).  */
-    if (0
-        &&
-            ({
-              long pscr_ret__;
-              switch(len) {
-                case 4:
-                    {
-                       long pfo_ret__;
-                       switch (len2) {
-                         case 8:	printf("bla"); pfo_ret__ = 42; break;
-                       }
-                       pscr_ret__ = pfo_ret__;
-                    }
-                  break;
-                case 8:
-                    {
-                       long pfo_ret__;
-                       switch (len2) {
-                         case 1:asm("movq %1,%0": "=r" (pfo_ret__)	: "m" (cpu_number));	break;
-                         case 2:asm("movq %1,%0": "=r" (pfo_ret__)	: "m" (cpu_number));	break;
-                         case 4:asm("movq %1,%0": "=r" (pfo_ret__)	: "m" (cpu_number));	break;
-                         case 8:asm("movq %1,%0": "=r" (pfo_ret__)	: "m" (cpu_number));	break;
-                         default: printf("impossible\n");
-                       }
-                       pscr_ret__ = pfo_ret__;
-                    };
-                  break;
-              }
-              pscr_ret__;
-            }))
-      {
-        printf("huh?\n");
-      }
-#endif
-}
-
-void test_asm_dead_code(void)
-{
-  long rdi;
-  /* Try to make sure that xdi contains a zero, and hence will
-     lead to a segfault if the next asm is evaluated without
-     arguments being set up.  */
-  asm volatile ("" : "=D" (rdi) : "0" (0));
-  (void)sizeof (({
-      int var;
-      /* This shouldn't trigger a segfault, either the argument
-         registers need to be set up and the asm emitted despite
-	 this being in an unevaluated context, or both the argument
-	 setup _and_ the asm emission need to be suppressed.  The latter
-	 is better.  Disabling asm code gen when suppression is on
-	 also fixes the above trace_console bug, but that came earlier
-	 than asm suppression.  */
-      asm volatile ("movl $0,(%0)" : : "D" (&var) : "memory");
-      var;
-  }));
-}
-
-void asm_test(void)
-{
-    char buf[128];
-    unsigned int val, val2;
-    struct struct123 s1;
-    struct struct1231 s2 = { (unsigned long)&s1 };
-    /* Hide the outer base_func, but check later that the inline
-       asm block gets the outer one.  */
-    int base_func = 42;
-    void override_func3 (void);
-    unsigned long asmret;
-#ifdef BOOL_ISOC99
-    _Bool somebool;
-#endif
-    register int regvar asm("%esi");
-
-    printf("inline asm:\n");
-
-    // parse 0x1E-1 as 3 tokens in asm mode
-    asm volatile ("mov $0x1E-1,%eax");
-
-    /* test the no operand case */
-    asm volatile ("xorl %eax, %eax");
-
-    memcpy1(buf, "hello", 6);
-    strncat1(buf, " worldXXXXX", 3);
-    printf("%s\n", buf);
-
-    memcpy2(buf, "hello", 6);
-    strncat2(buf, " worldXXXXX", 3);
-    printf("%s\n", buf);
-
-    /* 'A' constraint test */
-    printf("mul64=0x%Lx\n", mul64(0x12345678, 0xabcd1234));
-    printf("inc64=0x%Lx\n", inc64(0x12345678ffffffff));
-
-    s1.a = 42;
-    s1.b = 43;
-    printf("mconstraint: %d", mconstraint_test(&s2));
-    printf(" %d %d\n", s1.a, s1.b);
-    other_constraints_test();
-    set = 0xff;
-    sigdelset1(&set, 2);
-    sigaddset1(&set, 16);
-    /* NOTE: we test here if C labels are correctly restored after the
-       asm statement */
-    goto label1;
- label2:
-    __asm__("btsl %1,%0" : "=m"(set) : "Ir"(20) : "cc");
-    printf("set=0x%x\n", set);
-    val = 0x01020304;
-    printf("swab32(0x%08x) = 0x%0x\n", val, swab32(val));
-#ifndef _WIN32
-    override_func1();
-    override_func2();
-    /* The base_func ref from the following inline asm should find
-       the global one, not the local decl from this function.  */
-    asm volatile(".weak override_func3\n.set override_func3, base_func");
-    override_func3();
-    printf("asmstr: %s\n", get_asm_string());
-    asm_local_label_diff();
-    asm_local_statics();
-#endif
-    /* Check that we can also load structs of appropriate layout
-       into registers.  */
-    asm volatile("" : "=r" (asmret) : "0"(s2));
-    if (asmret != s2.addr)
-      printf("asmstr: failed\n");
-#ifdef BOOL_ISOC99
-    /* Check that the typesize correctly sets the register size to
-       8 bit.  */
-    asm volatile("cmp %1,%2; sete %0" : "=a"(somebool) : "r"(1), "r"(2));
-    if (!somebool)
-      printf("asmbool: failed\n");
-#endif
-    val = 43;
-    fancy_copy (&val, &val2);
-    printf ("fancycpy(%d)=%d\n", val, val2);
-    val = 44;
-    fancy_copy2 (&val, &val2);
-    printf ("fancycpy2(%d)=%d\n", val, val2);
-    asm volatile ("mov $0x4243, %%esi" : "=r" (regvar));
-    printf ("regvar=%x\n", regvar);
-    test_high_clobbers();
-    trace_console(8, 8);
-    test_asm_dead_code();
-    return;
- label1:
-    goto label2;
-}
-
-#else
-
-void asm_test(void)
-{
-}
-
-#endif
-
-#define COMPAT_TYPE(type1, type2) \
-{\
-    printf("__builtin_types_compatible_p(%s, %s) = %d\n", #type1, #type2, \
-           __builtin_types_compatible_p (type1, type2));\
-}
-
-int constant_p_var;
-
-void builtin_test(void)
-{
-    short s;
-    int i;
-    long long ll;
-#if GCC_MAJOR >= 3
-    COMPAT_TYPE(int, int);
-    COMPAT_TYPE(int, unsigned int);
-    COMPAT_TYPE(int, char);
-    COMPAT_TYPE(int, const int);
-    COMPAT_TYPE(int, volatile int);
-    COMPAT_TYPE(int *, int *);
-    COMPAT_TYPE(int *, void *);
-    COMPAT_TYPE(int *, const int *);
-    COMPAT_TYPE(char *, unsigned char *);
-    COMPAT_TYPE(char *, signed char *);
-    COMPAT_TYPE(char *, char *);
-/* space is needed because tcc preprocessor introduces a space between each token */
-    COMPAT_TYPE(char * *, void *); 
-#endif
-    printf("res = %d\n", __builtin_constant_p(1));
-    printf("res = %d\n", __builtin_constant_p(1 + 2));
-    printf("res = %d\n", __builtin_constant_p(&constant_p_var));
-    printf("res = %d\n", __builtin_constant_p(constant_p_var));
-    printf("res = %d\n", __builtin_constant_p(100000 / constant_p_var));
-    s = 1;
-    ll = 2;
-    i = __builtin_choose_expr (1 != 0, ll, s);
-    printf("bce: %d\n", i);
-    i = __builtin_choose_expr (1 != 1, ll, s);
-    printf("bce: %d\n", i);
-    i = sizeof (__builtin_choose_expr (1, ll, s));
-    printf("bce: %d\n", i);
-    i = sizeof (__builtin_choose_expr (0, ll, s));
-    printf("bce: %d\n", i);
-
-    //printf("bera: %p\n", __builtin_extract_return_addr((void*)43));
-}
-
-#ifndef _WIN32
-extern int __attribute__((weak)) weak_f1(void);
-extern int __attribute__((weak)) weak_f2(void);
-extern int                       weak_f3(void);
-extern int __attribute__((weak)) weak_v1;
-extern int __attribute__((weak)) weak_v2;
-extern int                       weak_v3;
-
-extern int                           (*weak_fpa)() __attribute__((weak));
-extern int __attribute__((weak))     (*weak_fpb)();
-extern     __attribute__((weak)) int (*weak_fpc)();
-
-extern int                     weak_asm_f1(void) asm("weak_asm_f1x") __attribute((weak));
-extern int __attribute((weak)) weak_asm_f2(void) asm("weak_asm_f2x")                    ;
-extern int __attribute((weak)) weak_asm_f3(void) asm("weak_asm_f3x") __attribute((weak));
-extern int                     weak_asm_v1       asm("weak_asm_v1x") __attribute((weak));
-extern int __attribute((weak)) weak_asm_v2       asm("weak_asm_v2x")                    ;
-extern int __attribute((weak)) weak_asm_v3(void) asm("weak_asm_v3x") __attribute((weak));
-
-static const size_t dummy = 0;
-extern __typeof(dummy) weak_dummy1 __attribute__((weak, alias("dummy")));
-extern __typeof(dummy) __attribute__((weak, alias("dummy"))) weak_dummy2;
-extern __attribute__((weak, alias("dummy"))) __typeof(dummy) weak_dummy3;
-
-int some_lib_func(void);
-int dummy_impl_of_slf(void) { return 444; }
-int some_lib_func(void) __attribute__((weak, alias("dummy_impl_of_slf")));
-
-int weak_toolate() __attribute__((weak));
-int weak_toolate() { return 0; }
-
-void __attribute__((weak)) weak_test(void)
-{
-	printf("weak_f1=%d\n", weak_f1 ? weak_f1() : 123);
-	printf("weak_f2=%d\n", weak_f2 ? weak_f2() : 123);
-	printf("weak_f3=%d\n", weak_f3 ? weak_f3() : 123);
-	printf("weak_v1=%d\n",&weak_v1 ? weak_v1   : 123);
-	printf("weak_v2=%d\n",&weak_v2 ? weak_v2   : 123);
-	printf("weak_v3=%d\n",&weak_v3 ? weak_v3   : 123);
-
-	printf("weak_fpa=%d\n",&weak_fpa ? weak_fpa() : 123);
-	printf("weak_fpb=%d\n",&weak_fpb ? weak_fpb() : 123);
-	printf("weak_fpc=%d\n",&weak_fpc ? weak_fpc() : 123);
-	
-	printf("weak_asm_f1=%d\n", weak_asm_f1 != NULL);
-	printf("weak_asm_f2=%d\n", weak_asm_f2 != NULL);
-	printf("weak_asm_f3=%d\n", weak_asm_f3 != NULL);
-	printf("weak_asm_v1=%d\n",&weak_asm_v1 != NULL);
-	printf("weak_asm_v2=%d\n",&weak_asm_v2 != NULL);
-	printf("weak_asm_v3=%d\n",&weak_asm_v3 != NULL);
-	printf("some_lib_func=%d\n", &some_lib_func ? some_lib_func() : 0);
-}
-
-int __attribute__((weak)) weak_f2() { return 222; }
-int __attribute__((weak)) weak_f3() { return 333; }
-int __attribute__((weak)) weak_v2 = 222;
-int __attribute__((weak)) weak_v3 = 333;
-#endif
-
-void const_func(const int a)
-{
-}
-
-void const_warn_test(void)
-{
-    const_func(1);
-}
-
-struct condstruct {
-  int i;
-};
-
-int getme (struct condstruct *s, int i)
-{
-  int i1 = (i == 0 ? 0 : s)->i;
-  int i2 = (i == 0 ? s : 0)->i;
-  int i3 = (i == 0 ? (void*)0 : s)->i;
-  int i4 = (i == 0 ? s : (void*)0)->i;
-  return i1 + i2 + i3 + i4;
-}
-
-struct global_data
-{
-  int a[40];
-  int *b[40];
-};
-
-struct global_data global_data;
-
-int global_data_getstuff (int *, int);
-
-void global_data_callit (int i)
-{
-  *global_data.b[i] = global_data_getstuff (global_data.b[i], 1);
-}
-
-int global_data_getstuff (int *p, int i)
-{
-  return *p + i;
-}
-
-void global_data_test (void)
-{
-  global_data.a[0] = 42;
-  global_data.b[0] = &global_data.a[0];
-  global_data_callit (0);
-  printf ("%d\n", global_data.a[0]);
-}
-
-struct cmpcmpS
-{
-  unsigned char fill : 3;
-  unsigned char b1 : 1;
-  unsigned char b2 : 1;
-  unsigned char fill2 : 3;
-};
-
-int glob1, glob2, glob3;
-
-void compare_comparisons (struct cmpcmpS *s)
-{
-  if (s->b1 != (glob1 == glob2)
-      || (s->b2 != (glob1 == glob3)))
-    printf ("comparing comparisons broken\n");
-}
-
-void cmp_comparison_test(void)
-{
-  struct cmpcmpS s;
-  s.b1 = 1;
-  glob1 = 42; glob2 = 42;
-  s.b2 = 0;
-  glob3 = 43;
-  compare_comparisons (&s);
-}
-
-int fcompare (double a, double b, int code)
-{
-  switch (code) {
-    case 0: return a == b;
-    case 1: return a != b;
-    case 2: return a < b;
-    case 3: return a >= b;
-    case 4: return a > b;
-    case 5: return a <= b;
-  }
-}
-
-void math_cmp_test(void)
-{
-  double nan = 0.0/0.0;
-  double one = 1.0;
-  double two = 2.0;
-  int comp = 0;
-#define bug(a,b,op,iop,part) printf("Test broken: %s %s %s %s %d\n", #a, #b, #op, #iop, part)
-
-  /* This asserts that "a op b" is _not_ true, but "a iop b" is true.
-     And it does this in various ways so that all code generation paths
-     are checked (generating inverted tests, or non-inverted tests, or
-     producing a 0/1 value without jumps (that's done in the fcompare
-     function).  */
-#define FCMP(a,b,op,iop,code) \
-  if (fcompare (a,b,code))    \
-    bug (a,b,op,iop,1); \
-  if (a op b) \
-    bug (a,b,op,iop,2); \
-  if (a iop b) \
-    ; \
-  else \
-    bug (a,b,op,iop,3); \
-  if ((a op b) || comp) \
-    bug (a,b,op,iop,4); \
-  if ((a iop b) || comp) \
-    ; \
-  else \
-    bug (a,b,op,iop,5);
-
-  /* Equality tests.  */
-  FCMP(nan, nan, ==, !=, 0);
-  FCMP(one, two, ==, !=, 0);
-  FCMP(one, one, !=, ==, 1);
-  /* Non-equality is a bit special.  */
-  if (!fcompare (nan, nan, 1))
-    bug (nan, nan, !=, ==, 6);
-
-  /* Relational tests on numbers.  */
-  FCMP(two, one, <, >=, 2);
-  FCMP(one, two, >=, <, 3);
-  FCMP(one, two, >, <=, 4);
-  FCMP(two, one, <=, >, 5);
-
-  /* Relational tests on NaNs.  Note that the inverse op here is
-     always !=, there's no operator in C that is equivalent to !(a < b),
-     when NaNs are involved, same for the other relational ops.  */
-  FCMP(nan, nan, <, !=, 2);
-  FCMP(nan, nan, >=, !=, 3);
-  FCMP(nan, nan, >, !=, 4);
-  FCMP(nan, nan, <=, !=, 5);
-}
-
-double get100 () { return 100.0; }
-
-void callsave_test(void)
-{
-#if defined __i386__ || defined __x86_64__ || defined __arm__
-  int i, s; double *d; double t;
-  s = sizeof (double);
-  printf ("callsavetest: %d\n", s);
-  d = alloca (sizeof(double));
-  d[0] = 10.0;
-  /* x86-64 had a bug were the next call to get100 would evict
-     the lvalue &d[0] as VT_LLOCAL, and the reload would be done
-     in int type, not pointer type.  When alloca returns a pointer
-     with the high 32 bit set (which is likely on x86-64) the access
-     generates a segfault.  */
-  i = d[0] > get100 ();
-  printf ("%d\n", i);
-#endif
-}
-
-
-void bfa3(ptrdiff_t str_offset)
-{
-    printf("bfa3: %s\n", (char *)__builtin_frame_address(3) + str_offset);
-}
-void bfa2(ptrdiff_t str_offset)
-{
-    printf("bfa2: %s\n", (char *)__builtin_frame_address(2) + str_offset);
-    bfa3(str_offset);
-}
-void bfa1(ptrdiff_t str_offset)
-{
-    printf("bfa1: %s\n", (char *)__builtin_frame_address(1) + str_offset);
-    bfa2(str_offset);
-}
-
-void builtin_frame_address_test(void)
-{
-/* builtin_frame_address fails on ARM with gcc which make test3 fail */
-#ifndef __arm__
-    char str[] = "__builtin_frame_address";
-    char *fp0 = __builtin_frame_address(0);
-
-    printf("str: %s\n", str);
-    bfa1(str-fp0);
-#endif
-}
-
-char via_volatile (char i)
-{
-  char volatile vi;
-  vi = i;
-  return vi;
-}
-
-struct __attribute__((__packed__)) Spacked {
-    char a;
-    short b;
-    int c;
-};
-struct Spacked spacked;
-typedef struct __attribute__((__packed__)) {
-    char a;
-    short b;
-    int c;
-} Spacked2;
-Spacked2 spacked2;
-typedef struct Spacked3_s {
-    char a;
-    short b;
-    int c;
-} __attribute__((__packed__)) Spacked3;
-Spacked3 spacked3;
-struct gate_struct64 {
-    unsigned short offset_low;
-    unsigned short segment;
-    unsigned ist : 3, zero0 : 5, type : 5, dpl : 2, p : 1;
-    unsigned short offset_middle;
-    unsigned offset_high;
-    unsigned zero1;
-} __attribute__((packed));
-typedef struct gate_struct64 gate_desc;
-gate_desc a_gate_desc;
-void attrib_test(void)
-{
-#ifndef _WIN32
-  printf("attr: %d %d %d %d\n", sizeof(struct Spacked),
-	 sizeof(spacked), sizeof(Spacked2), sizeof(spacked2));
-  printf("attr: %d %d\n", sizeof(Spacked3), sizeof(spacked3));
-  printf("attr: %d %d\n", sizeof(gate_desc), sizeof(a_gate_desc));
-#endif
-}
-extern __attribute__((__unused__)) char * __attribute__((__unused__)) *
-strange_attrib_placement (void);
-
-void * __attribute__((__unused__)) get_void_ptr (void *a)
-{
-  return a;
-}
-
-/* This part checks for a bug in TOK_GET (used for inline expansion),
-   where the large long long constant left the the high bits set for
-   the integer constant token.  */
-static inline
-int __get_order(unsigned long long size)
-{
-  int order;
-  size -= 0xffff880000000000ULL; // this const left high bits set in the token
-    {
-      struct S { int i : 1; } s; // constructed for this '1'
-    }
-  order = size;
-  return order;
-}
-
-/* This just forces the above inline function to be actually emitted.  */
-int force_get_order(unsigned long s)
-{
-    return __get_order(s);
-}
diff --git a/tinyc/tests/tcctest.h b/tinyc/tests/tcctest.h
deleted file mode 100644
index b301c7c16..000000000
--- a/tinyc/tests/tcctest.h
+++ /dev/null
@@ -1,9 +0,0 @@
-static inline const char *get_basefile_from_header(void)
-{
-  return __BASE_FILE__;
-}
-
-static inline const char *get_file_from_header(void)
-{
-  return __FILE__;
-}
diff --git a/tinyc/tests/testfp.c b/tinyc/tests/testfp.c
deleted file mode 100644
index 63342b422..000000000
--- a/tinyc/tests/testfp.c
+++ /dev/null
@@ -1,510 +0,0 @@
-/*
- *  Test 128-bit floating-point arithmetic on arm64:
- *  build with two different compilers and compare the output.
- *
- *  Copyright (c) 2015 Edmund Grimley Evans
- *
- * Copying and distribution of this file, with or without modification,
- * are permitted in any medium without royalty provided the copyright
- * notice and this notice are preserved.  This file is offered as-is,
- * without any warranty.
- */
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define check(x) ((x) ? (void)0 : check_fail(#x, __FILE__, __LINE__))
-
-void check_fail(const char *assertion, const char *file, unsigned int line)
-{
-    printf("%s:%d: Check (%s) failed.", file, line, assertion);
-    exit(1);
-}
-
-typedef struct {
-    unsigned long long x0, x1;
-} u128_t;
-
-float copy_fi(uint32_t x)
-{
-    float f;
-    memcpy(&f, &x, 4);
-    return f;
-}
-
-double copy_di(uint64_t x)
-{
-    double f;
-    memcpy(&f, &x, 8);
-    return f;
-}
-
-long double copy_ldi(u128_t x)
-{
-    long double f;
-    memcpy(&f, &x, 16);
-    return f;
-}
-
-uint32_t copy_if(float f)
-{
-    uint32_t x;
-    memcpy(&x, &f, 4);
-    return x;
-}
-
-uint64_t copy_id(double f)
-{
-    uint64_t x;
-    memcpy(&x, &f, 8);
-    return x;
-}
-
-u128_t copy_ild(long double f)
-{
-    u128_t x;
-    memcpy(&x, &f, 16);
-    return x;
-}
-
-long double make(int sgn, int exp, uint64_t high, uint64_t low)
-{
-    u128_t x = { low,
-                 (0x0000ffffffffffff & high) |
-                 (0x7fff000000000000 & (uint64_t)exp << 48) |
-                 (0x8000000000000000 & (uint64_t)sgn << 63) };
-    return copy_ldi(x);
-}
-
-void cmp(long double a, long double b)
-{
-    u128_t ax = copy_ild(a);
-    u128_t bx = copy_ild(b);
-    int eq = (a == b);
-    int ne = (a != b);
-    int lt = (a < b);
-    int le = (a <= b);
-    int gt = (a > b);
-    int ge = (a >= b);
-
-    check(eq == 0 || eq == 1);
-    check(lt == 0 || lt == 1);
-    check(gt == 0 || gt == 1);
-    check(ne == !eq && le == (lt | eq) && ge == (gt | eq));
-    check(eq + lt + gt < 2);
-
-    printf("cmp %016llx%016llx %016llx%016llx %d %d %d\n",
-           ax.x1, ax.x0, bx.x1, bx.x0, lt, eq, gt);
-}
-
-void cmps(void)
-{
-    int i, j;
-
-    for (i = 0; i < 2; i++)
-        for (j = 0; j < 2; j++)
-            cmp(make(i, 0, 0, 0), make(j, 0, 0, 0));
-
-    for (i = 0; i < 2; i++) {
-        for (j = 0; j < 64; j++) {
-            long double f1 = make(i, 32767, (uint64_t)1 << j, 0);
-            long double f2 = make(i, 32767, 0, (uint64_t)1 << j);
-            cmp(f1, 0);
-            cmp(f2, 0);
-            cmp(0, f1);
-            cmp(0, f2);
-        }
-    }
-
-    for (i = 0; i < 6; i++)
-        for (j = 0; j < 6; j++)
-            cmp(make(i & 1, i >> 1, 0, 0),
-                make(j & 1, j >> 1, 0, 0));
-
-    for (i = 0; i < 2; i++) {
-        for (j = 0; j < 2; j++) {
-            int a, b;
-            for (a = 0; a < 2; a++) {
-                for (b = 0; b < 2; b++) {
-                    cmp(make(i, j, a, b), make(i, j, 0, 0));
-                    cmp(make(i, j, 0, 0), make(i, j, a, b));
-                }
-            }
-        }
-    }
-}
-
-void xop(const char *name, long double a, long double b, long double c)
-{
-    u128_t ax = copy_ild(a);
-    u128_t bx = copy_ild(b);
-    u128_t cx = copy_ild(c);
-    printf("%s %016llx%016llx %016llx%016llx %016llx%016llx\n",
-           name, ax.x1, ax.x0, bx.x1, bx.x0, cx.x1, cx.x0);
-}
-
-void fadd(long double a, long double b)
-{
-    xop("add", a, b, a + b);
-}
-
-void fsub(long double a, long double b)
-{
-    xop("sub", a, b, a - b);
-}
-
-void fmul(long double a, long double b)
-{
-    xop("mul", a, b, a * b);
-}
-
-void fdiv(long double a, long double b)
-{
-    xop("div", a, b, a / b);
-}
-
-void nanz(void)
-{
-    // Check NaNs:
-    {
-        long double x[7];
-        int i, j, n = 0;
-        x[n++] = make(0, 32000, 0x95132b76effc, 0xd79035214b4f8d53);
-        x[n++] = make(1, 32001, 0xbe71d7a51587, 0x30601c6815d6c3ac);
-        x[n++] = make(0, 32767, 0, 1);
-        x[n++] = make(0, 32767, (uint64_t)1 << 46, 0);
-        x[n++] = make(1, 32767, (uint64_t)1 << 47, 0);
-        x[n++] = make(1, 32767, 0x7596c7099ad5, 0xe25fed2c58f73fc9);
-        x[n++] = make(0, 32767, 0x835d143360f9, 0x5e315efb35630666);
-        check(n == sizeof(x) / sizeof(*x));
-        for (i = 0; i < n; i++) {
-            for (j = 0; j < n; j++) {
-                fadd(x[i], x[j]);
-                fsub(x[i], x[j]);
-                fmul(x[i], x[j]);
-                fdiv(x[i], x[j]);
-            }
-        }
-    }
-
-    // Check infinities and zeroes:
-    {
-        long double x[6];
-        int i, j, n = 0;
-        x[n++] = make(1, 32000, 0x62acda85f700, 0x47b6c9f35edc4044);
-        x[n++] = make(0, 32001, 0x94b7abf55af7, 0x9f425fe354428e19);
-        x[n++] = make(0, 32767, 0, 0);
-        x[n++] = make(1, 32767, 0, 0);
-        x[n++] = make(0, 0, 0, 0);
-        x[n++] = make(1, 0, 0, 0);
-        check(n == sizeof(x) / sizeof(*x));
-        for (i = 0; i < n; i++) {
-            for (j = 0; j < n; j++) {
-                fadd(x[i], x[j]);
-                fsub(x[i], x[j]);
-                fmul(x[i], x[j]);
-                fdiv(x[i], x[j]);
-            }
-        }
-    }
-}
-
-void adds(void)
-{
-    // Check shifting and add/sub:
-    {
-        int i;
-        for (i = -130; i <= 130; i++) {
-            int s1 = (uint32_t)i % 3 < 1;
-            int s2 = (uint32_t)i % 5 < 2;
-            fadd(make(s1, 16384    , 0x502c065e4f71a65d, 0xd2f9bdb031f4f031),
-                 make(s2, 16384 + i, 0xae267395a9bc1033, 0xb56b5800da1ba448));
-        }
-    }
-
-    // Check normalisation:
-    {
-        uint64_t a0 = 0xc6bab0a6afbef5ed;
-        uint64_t a1 = 0x4f84136c4a2e9b52;
-        int ee[] = { 0, 1, 10000 };
-        int e, i;
-        for (e = 0; e < sizeof(ee) / sizeof(*ee); e++) {
-            int exp = ee[e];
-            fsub(make(0, exp, a1, a0), make(0, 0, 0, 0));
-            for (i = 63; i >= 0; i--)
-                fsub(make(0, exp, a1 | (uint64_t)1 << i >> 1, a0),
-                     make(0, exp, a1 >> i << i, 0));
-            for (i = 63; i >=0; i--)
-                fsub(make(0, exp, a1, a0 | (uint64_t)1 << i >> 1),
-                     make(0, exp, a1, a0 >> i << i));
-        }
-    }
-
-    // Carry/overflow from rounding:
-    {
-        fadd(make(0, 114, -1, -1), make(0, 1, 0, 0));
-        fadd(make(0, 32766, -1, -1), make(0, 32653, 0, 0));
-        fsub(make(1, 32766, -1, -1), make(0, 32653, 0, 0));
-    }
-}
-
-void muls(void)
-{
-    int i, j;
-
-    {
-        long double max = make(0, 32766, -1, -1);
-        long double min = make(0, 0, 0, 1);
-        fmul(max, max);
-        fmul(max, min);
-        fmul(min, min);
-    }
-
-    for (i = 117; i > 0; i--)
-        fmul(make(0, 16268, 0x643dcea76edc, 0xe0877a598403627a),
-             make(i & 1, i, 0, 0));
-
-    fmul(make(0, 16383, -1, -3), make(0, 16383, 0, 1));
-    // Round to next exponent:
-    fmul(make(0, 16383, -1, -2), make(0, 16383, 0, 1));
-    // Round from subnormal to normal:
-    fmul(make(0, 1, -1, -1), make(0, 16382, 0, 0));
-
-    for (i = 0; i < 2; i++)
-        for (j = 0; j < 112; j++)
-            fmul(make(0, 16383, (uint64_t)1 << i, 0),
-                 make(0, 16383,
-                      j < 64 ? 0 : (uint64_t)1 << (j - 64),
-                      j < 64 ? (uint64_t)1 << j : 0));
-}
-
-void divs(void)
-{
-    int i;
-
-    {
-        long double max = make(0, 32766, -1, -1);
-        long double min = make(0, 0, 0, 1);
-        fdiv(max, max);
-        fdiv(max, min);
-        fdiv(min, max);
-        fdiv(min, min);
-    }
-
-    for (i = 0; i < 64; i++)
-        fdiv(make(0, 16383, -1, -1), make(0, 16383, -1, -(uint64_t)1 << i));
-    for (i = 0; i < 48; i++)
-        fdiv(make(0, 16383, -1, -1), make(0, 16383, -(uint64_t)1 << i, 0));
-}
-
-void cvtlsw(int32_t a)
-{
-    long double f = a;
-    u128_t x = copy_ild(f);
-    printf("cvtlsw %08lx %016llx%016llx\n", (long)(uint32_t)a, x.x1, x.x0);
-}
-
-void cvtlsx(int64_t a)
-{
-    long double f = a;
-    u128_t x = copy_ild(f);
-    printf("cvtlsx %016llx %016llx%016llx\n",
-           (long long)(uint64_t)a, x.x1, x.x0);
-}
-
-void cvtluw(uint32_t a)
-{
-    long double f = a;
-    u128_t x = copy_ild(f);
-    printf("cvtluw %08lx %016llx%016llx\n", (long)a, x.x1, x.x0);
-}
-
-void cvtlux(uint64_t a)
-{
-    long double f = a;
-    u128_t x = copy_ild(f);
-    printf("cvtlux %016llx %016llx%016llx\n", (long long)a, x.x1, x.x0);
-}
-
-void cvtil(long double a)
-{
-    u128_t x = copy_ild(a);
-    int32_t b1 = a;
-    int64_t b2 = a;
-    uint32_t b3 = a;
-    uint64_t b4 = a;
-    printf("cvtswl %016llx%016llx %08lx\n",
-           x.x1, x.x0, (long)(uint32_t)b1);
-    printf("cvtsxl %016llx%016llx %016llx\n",
-           x.x1, x.x0, (long long)(uint64_t)b2);
-    printf("cvtuwl %016llx%016llx %08lx\n",
-           x.x1, x.x0, (long)b3);
-    printf("cvtuxl %016llx%016llx %016llx\n",
-           x.x1, x.x0, (long long)b4);
-}
-
-void cvtlf(float a)
-{
-    uint32_t ax = copy_if(a);
-    long double b = a;
-    u128_t bx = copy_ild(b);
-    printf("cvtlf %08lx %016llx%016llx\n", (long)ax, bx.x1, bx.x0);
-}
-
-void cvtld(double a)
-{
-    uint64_t ax = copy_id(a);
-    long double b = a;
-    u128_t bx = copy_ild(b);
-    printf("cvtld %016llx %016llx%016llx\n", (long long)ax, bx.x1, bx.x0);
-}
-
-void cvtfl(long double a)
-{
-    u128_t ax = copy_ild(a);
-    float b = a;
-    uint32_t bx = copy_if(b);
-    printf("cvtfl %016llx%016llx %08lx\n", ax.x1, ax.x0, (long)bx);
-}
-
-void cvtdl(long double a)
-{
-    u128_t ax = copy_ild(a);
-    double b = a;
-    uint64_t bx = copy_id(b);
-    printf("cvtdl %016llx%016llx %016llx\n", ax.x1, ax.x0, (long long)bx);
-}
-
-void cvts(void)
-{
-    int i, j;
-
-    {
-        uint32_t x = 0xad040c5b;
-        cvtlsw(0);
-        for (i = 0; i < 31; i++)
-            cvtlsw(x >> (31 - i));
-        for (i = 0; i < 31; i++)
-            cvtlsw(-(x >> (31 - i)));
-        cvtlsw(0x80000000);
-    }
-    {
-        uint64_t x = 0xb630a248cad9afd2;
-        cvtlsx(0);
-        for (i = 0; i < 63; i++)
-            cvtlsx(x >> (63 - i));
-        for (i = 0; i < 63; i++)
-            cvtlsx(-(x >> (63 - i)));
-        cvtlsx(0x8000000000000000);
-    }
-    {
-        uint32_t x = 0xad040c5b;
-        cvtluw(0);
-        for (i = 0; i < 32; i++)
-            cvtluw(x >> (31 - i));
-    }
-    {
-        uint64_t x = 0xb630a248cad9afd2;
-        cvtlux(0);
-        for (i = 0; i < 64; i++)
-            cvtlux(x >> (63 - i));
-    }
-
-    for (i = 0; i < 2; i++) {
-        cvtil(make(i, 32767, 0, 1));
-        cvtil(make(i, 32767, (uint64_t)1 << 47, 0));
-        cvtil(make(i, 32767, 123, 456));
-        cvtil(make(i, 32767, 0, 0));
-        cvtil(make(i, 16382, -1, -1));
-        cvtil(make(i, 16383, -1, -1));
-        cvtil(make(i, 16384, 0x7fffffffffff, -1));
-        cvtil(make(i, 16384, 0x800000000000, 0));
-        for (j = 0; j < 68; j++)
-            cvtil(make(i, 16381 + j, 0xd4822c0a10ec, 0x1fe2f8b2669f5c9d));
-    }
-
-    cvtlf(copy_fi(0x00000000));
-    cvtlf(copy_fi(0x456789ab));
-    cvtlf(copy_fi(0x7f800000));
-    cvtlf(copy_fi(0x7f923456));
-    cvtlf(copy_fi(0x7fdbcdef));
-    cvtlf(copy_fi(0x80000000));
-    cvtlf(copy_fi(0xabcdef12));
-    cvtlf(copy_fi(0xff800000));
-    cvtlf(copy_fi(0xff923456));
-    cvtlf(copy_fi(0xffdbcdef));
-
-    cvtld(copy_di(0x0000000000000000));
-    cvtld(copy_di(0x456789abcdef0123));
-    cvtld(copy_di(0x7ff0000000000000));
-    cvtld(copy_di(0x7ff123456789abcd));
-    cvtld(copy_di(0x7ffabcdef1234567));
-    cvtld(copy_di(0x8000000000000000));
-    cvtld(copy_di(0xcdef123456789abc));
-    cvtld(copy_di(0xfff0000000000000));
-    cvtld(copy_di(0xfff123456789abcd));
-    cvtld(copy_di(0xfffabcdef1234567));
-
-    for (i = 0; i < 2; i++) {                   \
-        cvtfl(make(i, 0, 0, 0));
-        cvtfl(make(i, 16232, -1, -1));
-        cvtfl(make(i, 16233, 0, 0));
-        cvtfl(make(i, 16233, 0, 1));
-        cvtfl(make(i, 16383, 0xab0ffd000000, 0));
-        cvtfl(make(i, 16383, 0xab0ffd000001, 0));
-        cvtfl(make(i, 16383, 0xab0ffeffffff, 0));
-        cvtfl(make(i, 16383, 0xab0fff000000, 0));
-        cvtfl(make(i, 16383, 0xab0fff000001, 0));
-        cvtfl(make(i, 16510, 0xfffffeffffff, -1));
-        cvtfl(make(i, 16510, 0xffffff000000, 0));
-        cvtfl(make(i, 16511, 0, 0));
-        cvtfl(make(i, 32767, 0, 0));
-        cvtfl(make(i, 32767, 0, 1));
-        cvtfl(make(i, 32767, 0x4cbe01ac5f40, 0x75cee3c6afbb00b5));
-        cvtfl(make(i, 32767, 0x800000000000, 1));
-        cvtfl(make(i, 32767, 0xa11caaaf6a52, 0x696033e871eab099));
-    }
-
-    for (i = 0; i < 2; i++) {
-        cvtdl(make(i, 0, 0, 0));
-        cvtdl(make(i, 15307, -1, -1));
-        cvtdl(make(i, 15308, 0, 0));
-        cvtdl(make(i, 15308, 0, 1));
-        cvtdl(make(i, 16383, 0xabc123abc0ff, 0xe800000000000000));
-        cvtdl(make(i, 16383, 0xabc123abc0ff, 0xe800000000000001));
-        cvtdl(make(i, 16383, 0xabc123abc0ff, 0xf7ffffffffffffff));
-        cvtdl(make(i, 16383, 0xabc123abc0ff, 0xf800000000000000));
-        cvtdl(make(i, 16383, 0xabc123abc0ff, 0xf800000000000001));
-        cvtdl(make(i, 17406, 0xffffffffffff, 0xf7ffffffffffffff));
-        cvtdl(make(i, 17406, 0xffffffffffff, 0xf800000000000000));
-        cvtdl(make(i, 17407, 0, 0));
-        cvtdl(make(i, 32767, 0, 0));
-        cvtdl(make(i, 32767, 0, 1));
-        cvtdl(make(i, 32767, 0x4cbe01ac5f40, 0x75cee3c6afbb00b5));
-        cvtdl(make(i, 32767, 0x800000000000, 1));
-        cvtdl(make(i, 32767, 0xa11caaaf6a52, 0x696033e871eab099));
-    }
-}
-
-void tests(void)
-{
-    cmps();
-    nanz();
-    adds();
-    muls();
-    divs();
-    cvts();
-}
-
-int main()
-{
-#ifdef __aarch64__
-    tests();
-#else
-    printf("This test program is intended for a little-endian architecture\n"
-           "with an IEEE-standard 128-bit long double.\n");
-#endif
-    return 0;
-}
diff --git a/tinyc/tests/tests2/00_assignment.c b/tinyc/tests/tests2/00_assignment.c
deleted file mode 100644
index c96109fdc..000000000
--- a/tinyc/tests/tests2/00_assignment.c
+++ /dev/null
@@ -1,18 +0,0 @@
-#include <stdio.h>
-
-int main() 
-{
-   int a;
-   a = 42;
-   printf("%d\n", a);
-
-   int b = 64;
-   printf("%d\n", b);
-
-   int c = 12, d = 34;
-   printf("%d, %d\n", c, d);
-
-   return 0;
-}
-
-// vim: set expandtab ts=4 sw=3 sts=3 tw=80 :
diff --git a/tinyc/tests/tests2/00_assignment.expect b/tinyc/tests/tests2/00_assignment.expect
deleted file mode 100644
index d4407f3fe..000000000
--- a/tinyc/tests/tests2/00_assignment.expect
+++ /dev/null
@@ -1,3 +0,0 @@
-42
-64
-12, 34
diff --git a/tinyc/tests/tests2/01_comment.c b/tinyc/tests/tests2/01_comment.c
deleted file mode 100644
index a2e6bc63d..000000000
--- a/tinyc/tests/tests2/01_comment.c
+++ /dev/null
@@ -1,14 +0,0 @@
-#include <stdio.h>
-
-int main() 
-{
-   printf("Hello\n");
-   printf("Hello\n"); /* this is a comment */ printf("Hello\n");
-   printf("Hello\n");
-   // this is also a comment sayhello();
-   printf("Hello\n");
-
-   return 0;
-}
-
-// vim: set expandtab ts=4 sw=3 sts=3 tw=80 :
diff --git a/tinyc/tests/tests2/01_comment.expect b/tinyc/tests/tests2/01_comment.expect
deleted file mode 100644
index b1387ad09..000000000
--- a/tinyc/tests/tests2/01_comment.expect
+++ /dev/null
@@ -1,5 +0,0 @@
-Hello
-Hello
-Hello
-Hello
-Hello
diff --git a/tinyc/tests/tests2/02_printf.c b/tinyc/tests/tests2/02_printf.c
deleted file mode 100644
index 4c34dd874..000000000
--- a/tinyc/tests/tests2/02_printf.c
+++ /dev/null
@@ -1,18 +0,0 @@
-#include <stdio.h>
-
-int main() 
-{
-   printf("Hello world\n");
-
-   int Count;
-   for (Count = -5; Count <= 5; Count++)
-      printf("Count = %d\n", Count);
-
-   printf("String 'hello', 'there' is '%s', '%s'\n", "hello", "there");
-   printf("Character 'A' is '%c'\n", 65);
-   printf("Character 'a' is '%c'\n", 'a');
-
-   return 0;
-}
-
-// vim: set expandtab ts=4 sw=3 sts=3 tw=80 :
diff --git a/tinyc/tests/tests2/02_printf.expect b/tinyc/tests/tests2/02_printf.expect
deleted file mode 100644
index f67a0f6e3..000000000
--- a/tinyc/tests/tests2/02_printf.expect
+++ /dev/null
@@ -1,15 +0,0 @@
-Hello world
-Count = -5
-Count = -4
-Count = -3
-Count = -2
-Count = -1
-Count = 0
-Count = 1
-Count = 2
-Count = 3
-Count = 4
-Count = 5
-String 'hello', 'there' is 'hello', 'there'
-Character 'A' is 'A'
-Character 'a' is 'a'
diff --git a/tinyc/tests/tests2/03_struct.c b/tinyc/tests/tests2/03_struct.c
deleted file mode 100644
index c5d48c5ab..000000000
--- a/tinyc/tests/tests2/03_struct.c
+++ /dev/null
@@ -1,31 +0,0 @@
-#include <stdio.h>
-
-struct fred
-{
-   int boris;
-   int natasha;
-};
-
-int main()
-{
-   struct fred bloggs;
-
-   bloggs.boris = 12;
-   bloggs.natasha = 34;
-
-   printf("%d\n", bloggs.boris);
-   printf("%d\n", bloggs.natasha);
-
-   struct fred jones[2];
-   jones[0].boris = 12;
-   jones[0].natasha = 34;
-   jones[1].boris = 56;
-   jones[1].natasha = 78;
-
-   printf("%d\n", jones[0].boris);
-   printf("%d\n", jones[0].natasha);
-   printf("%d\n", jones[1].boris);
-   printf("%d\n", jones[1].natasha);
-
-   return 0;
-}
diff --git a/tinyc/tests/tests2/03_struct.expect b/tinyc/tests/tests2/03_struct.expect
deleted file mode 100644
index ecbf58937..000000000
--- a/tinyc/tests/tests2/03_struct.expect
+++ /dev/null
@@ -1,6 +0,0 @@
-12
-34
-12
-34
-56
-78
diff --git a/tinyc/tests/tests2/04_for.c b/tinyc/tests/tests2/04_for.c
deleted file mode 100644
index 312fed855..000000000
--- a/tinyc/tests/tests2/04_for.c
+++ /dev/null
@@ -1,15 +0,0 @@
-#include <stdio.h>
-
-int main() 
-{
-   int Count;
-
-   for (Count = 1; Count <= 10; Count++)
-   {
-      printf("%d\n", Count);
-   }
-
-   return 0;
-}
-
-// vim: set expandtab ts=4 sw=3 sts=3 tw=80 :
diff --git a/tinyc/tests/tests2/04_for.expect b/tinyc/tests/tests2/04_for.expect
deleted file mode 100644
index f00c965d8..000000000
--- a/tinyc/tests/tests2/04_for.expect
+++ /dev/null
@@ -1,10 +0,0 @@
-1
-2
-3
-4
-5
-6
-7
-8
-9
-10
diff --git a/tinyc/tests/tests2/05_array.c b/tinyc/tests/tests2/05_array.c
deleted file mode 100644
index c218f3163..000000000
--- a/tinyc/tests/tests2/05_array.c
+++ /dev/null
@@ -1,21 +0,0 @@
-#include <stdio.h>
-
-int main() 
-{
-   int Count;
-   int Array[10];
-
-   for (Count = 1; Count <= 10; Count++)
-   {
-      Array[Count-1] = Count * Count;
-   }
-
-   for (Count = 0; Count < 10; Count++)
-   {
-      printf("%d\n", Array[Count]);
-   }
-
-   return 0;
-}
-
-// vim: set expandtab ts=4 sw=3 sts=3 tw=80 :
diff --git a/tinyc/tests/tests2/05_array.expect b/tinyc/tests/tests2/05_array.expect
deleted file mode 100644
index bc7257c0f..000000000
--- a/tinyc/tests/tests2/05_array.expect
+++ /dev/null
@@ -1,10 +0,0 @@
-1
-4
-9
-16
-25
-36
-49
-64
-81
-100
diff --git a/tinyc/tests/tests2/06_case.c b/tinyc/tests/tests2/06_case.c
deleted file mode 100644
index c0191e2b0..000000000
--- a/tinyc/tests/tests2/06_case.c
+++ /dev/null
@@ -1,29 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   int Count;
-
-   for (Count = 0; Count < 4; Count++)
-   {
-      printf("%d\n", Count);
-      switch (Count)
-      {
-         case 1:
-            printf("%d\n", 1);
-            break;
-
-         case 2:
-            printf("%d\n", 2);
-            break;
-
-         default:
-            printf("%d\n", 0);
-            break;
-      }
-   }
-
-   return 0;
-}
-
-// vim: set expandtab ts=4 sw=3 sts=3 tw=80 :
diff --git a/tinyc/tests/tests2/06_case.expect b/tinyc/tests/tests2/06_case.expect
deleted file mode 100644
index fab2c201a..000000000
--- a/tinyc/tests/tests2/06_case.expect
+++ /dev/null
@@ -1,8 +0,0 @@
-0
-0
-1
-1
-2
-2
-3
-0
diff --git a/tinyc/tests/tests2/07_function.c b/tinyc/tests/tests2/07_function.c
deleted file mode 100644
index 0477ce14e..000000000
--- a/tinyc/tests/tests2/07_function.c
+++ /dev/null
@@ -1,30 +0,0 @@
-#include <stdio.h>
-
-int myfunc(int x)
-{
-   return x * x;
-}
-
-void vfunc(int a)
-{
-   printf("a=%d\n", a);
-}
-
-void qfunc()
-{
-   printf("qfunc()\n");
-}
-
-int main()
-{
-   printf("%d\n", myfunc(3));
-   printf("%d\n", myfunc(4));
-
-   vfunc(1234);
-
-   qfunc();
-
-   return 0;
-}
-
-// vim: set expandtab ts=4 sw=3 sts=3 tw=80 :
diff --git a/tinyc/tests/tests2/07_function.expect b/tinyc/tests/tests2/07_function.expect
deleted file mode 100644
index 8ffb0a702..000000000
--- a/tinyc/tests/tests2/07_function.expect
+++ /dev/null
@@ -1,4 +0,0 @@
-9
-16
-a=1234
-qfunc()
diff --git a/tinyc/tests/tests2/08_while.c b/tinyc/tests/tests2/08_while.c
deleted file mode 100644
index 602ffc737..000000000
--- a/tinyc/tests/tests2/08_while.c
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   int a;
-   int p;
-   int t;
-
-   a = 1;
-   p = 0;
-   t = 0;
-
-   while (a < 100)
-   {
-      printf("%d\n", a);
-      t = a;
-      a = t + p;
-      p = t;
-   }
-
-   return 0;
-}
-
-// vim: set expandtab ts=4 sw=3 sts=3 tw=80 :
diff --git a/tinyc/tests/tests2/08_while.expect b/tinyc/tests/tests2/08_while.expect
deleted file mode 100644
index 702d4c0fc..000000000
--- a/tinyc/tests/tests2/08_while.expect
+++ /dev/null
@@ -1,11 +0,0 @@
-1
-1
-2
-3
-5
-8
-13
-21
-34
-55
-89
diff --git a/tinyc/tests/tests2/09_do_while.c b/tinyc/tests/tests2/09_do_while.c
deleted file mode 100644
index 1d3315d3a..000000000
--- a/tinyc/tests/tests2/09_do_while.c
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   int a;
-   int p;
-   int t;
-
-   a = 1;
-   p = 0;
-   t = 0;
-
-   do
-   {
-      printf("%d\n", a);
-      t = a;
-      a = t + p;
-      p = t;
-   } while (a < 100);
-
-   return 0;
-}
-
-// vim: set expandtab ts=4 sw=3 sts=3 tw=80 :
diff --git a/tinyc/tests/tests2/09_do_while.expect b/tinyc/tests/tests2/09_do_while.expect
deleted file mode 100644
index 702d4c0fc..000000000
--- a/tinyc/tests/tests2/09_do_while.expect
+++ /dev/null
@@ -1,11 +0,0 @@
-1
-1
-2
-3
-5
-8
-13
-21
-34
-55
-89
diff --git a/tinyc/tests/tests2/10_pointer.c b/tinyc/tests/tests2/10_pointer.c
deleted file mode 100644
index 0177f4d81..000000000
--- a/tinyc/tests/tests2/10_pointer.c
+++ /dev/null
@@ -1,40 +0,0 @@
-#include <stdio.h>
-
-struct ziggy
-{
-   int a;
-   int b;
-   int c;
-} bolshevic;
-
-int main()
-{
-   int a;
-   int *b;
-   int c;
-
-   a = 42;
-   b = &a;
-   printf("a = %d\n", *b);
-
-   bolshevic.a = 12;
-   bolshevic.b = 34;
-   bolshevic.c = 56;
-
-   printf("bolshevic.a = %d\n", bolshevic.a);
-   printf("bolshevic.b = %d\n", bolshevic.b);
-   printf("bolshevic.c = %d\n", bolshevic.c);
-
-   struct ziggy *tsar = &bolshevic;
-
-   printf("tsar->a = %d\n", tsar->a);
-   printf("tsar->b = %d\n", tsar->b);
-   printf("tsar->c = %d\n", tsar->c);
-
-   b = &(bolshevic.b);
-   printf("bolshevic.b = %d\n", *b);
-
-   return 0;
-}
-
-// vim: set expandtab ts=4 sw=3 sts=3 tw=80 :
diff --git a/tinyc/tests/tests2/10_pointer.expect b/tinyc/tests/tests2/10_pointer.expect
deleted file mode 100644
index 1e3c473f0..000000000
--- a/tinyc/tests/tests2/10_pointer.expect
+++ /dev/null
@@ -1,8 +0,0 @@
-a = 42
-bolshevic.a = 12
-bolshevic.b = 34
-bolshevic.c = 56
-tsar->a = 12
-tsar->b = 34
-tsar->c = 56
-bolshevic.b = 34
diff --git a/tinyc/tests/tests2/11_precedence.c b/tinyc/tests/tests2/11_precedence.c
deleted file mode 100644
index db2049dfe..000000000
--- a/tinyc/tests/tests2/11_precedence.c
+++ /dev/null
@@ -1,40 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   int a;
-   int b;
-   int c;
-   int d;
-   int e;
-   int f;
-   int x;
-   int y;
-
-   a = 12;
-   b = 34;
-   c = 56;
-   d = 78;
-   e = 0;
-   f = 1;
-
-   printf("%d\n", c + d);
-   printf("%d\n", (y = c + d));
-   printf("%d\n", e || e && f);
-   printf("%d\n", e || f && f);
-   printf("%d\n", e && e || f);
-   printf("%d\n", e && f || f);
-   printf("%d\n", a && f | f);
-   printf("%d\n", a | b ^ c & d);
-   printf("%d, %d\n", a == a, a == b);
-   printf("%d, %d\n", a != a, a != b);
-   printf("%d\n", a != b && c != d);
-   printf("%d\n", a + b * c / f);
-   printf("%d\n", a + b * c / f);
-   printf("%d\n", (4 << 4));
-   printf("%d\n", (64 >> 4));
-
-   return 0;
-}
-
-// vim: set expandtab ts=4 sw=3 sts=3 tw=80 :
diff --git a/tinyc/tests/tests2/11_precedence.expect b/tinyc/tests/tests2/11_precedence.expect
deleted file mode 100644
index b6923966e..000000000
--- a/tinyc/tests/tests2/11_precedence.expect
+++ /dev/null
@@ -1,15 +0,0 @@
-134
-134
-0
-1
-1
-1
-1
-46
-1, 0
-0, 1
-1
-1916
-1916
-64
-4
diff --git a/tinyc/tests/tests2/12_hashdefine.c b/tinyc/tests/tests2/12_hashdefine.c
deleted file mode 100644
index 5c521e02b..000000000
--- a/tinyc/tests/tests2/12_hashdefine.c
+++ /dev/null
@@ -1,14 +0,0 @@
-#include <stdio.h>
-
-#define FRED 12
-#define BLOGGS(x) (12*(x))
-
-int main()
-{
-   printf("%d\n", FRED);
-   printf("%d, %d, %d\n", BLOGGS(1), BLOGGS(2), BLOGGS(3));
-
-   return 0;
-}
-
-// vim: set expandtab ts=4 sw=3 sts=3 tw=80 :
diff --git a/tinyc/tests/tests2/12_hashdefine.expect b/tinyc/tests/tests2/12_hashdefine.expect
deleted file mode 100644
index 99f2ed5d9..000000000
--- a/tinyc/tests/tests2/12_hashdefine.expect
+++ /dev/null
@@ -1,2 +0,0 @@
-12
-12, 24, 36
diff --git a/tinyc/tests/tests2/13_integer_literals.c b/tinyc/tests/tests2/13_integer_literals.c
deleted file mode 100644
index 7cee98b1f..000000000
--- a/tinyc/tests/tests2/13_integer_literals.c
+++ /dev/null
@@ -1,20 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   int a = 24680;
-   int b = 01234567;
-   int c = 0x2468ac;
-   int d = 0x2468AC;
-   int e = 0b010101010101;
-
-   printf("%d\n", a);
-   printf("%d\n", b);
-   printf("%d\n", c);
-   printf("%d\n", d);
-   printf("%d\n", e);
-
-   return 0;
-}
-
-// vim: set expandtab ts=4 sw=3 sts=3 tw=80 :
diff --git a/tinyc/tests/tests2/13_integer_literals.expect b/tinyc/tests/tests2/13_integer_literals.expect
deleted file mode 100644
index f5aca06f7..000000000
--- a/tinyc/tests/tests2/13_integer_literals.expect
+++ /dev/null
@@ -1,5 +0,0 @@
-24680
-342391
-2386092
-2386092
-1365
diff --git a/tinyc/tests/tests2/14_if.c b/tinyc/tests/tests2/14_if.c
deleted file mode 100644
index 2bd255051..000000000
--- a/tinyc/tests/tests2/14_if.c
+++ /dev/null
@@ -1,21 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   int a = 1;
-
-   if (a)
-      printf("a is true\n");
-   else
-      printf("a is false\n");
-
-   int b = 0;
-   if (b)
-      printf("b is true\n");
-   else
-      printf("b is false\n");
-
-   return 0;
-}
-
-// vim: set expandtab ts=4 sw=3 sts=3 tw=80 :
diff --git a/tinyc/tests/tests2/14_if.expect b/tinyc/tests/tests2/14_if.expect
deleted file mode 100644
index c32c4159c..000000000
--- a/tinyc/tests/tests2/14_if.expect
+++ /dev/null
@@ -1,2 +0,0 @@
-a is true
-b is false
diff --git a/tinyc/tests/tests2/15_recursion.c b/tinyc/tests/tests2/15_recursion.c
deleted file mode 100644
index f79a00df5..000000000
--- a/tinyc/tests/tests2/15_recursion.c
+++ /dev/null
@@ -1,21 +0,0 @@
-#include <stdio.h>
-
-int factorial(int i) 
-{
-   if (i < 2)
-      return i;
-   else
-      return i * factorial(i - 1);
-}
-
-int main()
-{
-   int Count;
-
-   for (Count = 1; Count <= 10; Count++)
-      printf("%d\n", factorial(Count));
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/15_recursion.expect b/tinyc/tests/tests2/15_recursion.expect
deleted file mode 100644
index db47b283d..000000000
--- a/tinyc/tests/tests2/15_recursion.expect
+++ /dev/null
@@ -1,10 +0,0 @@
-1
-2
-6
-24
-120
-720
-5040
-40320
-362880
-3628800
diff --git a/tinyc/tests/tests2/16_nesting.c b/tinyc/tests/tests2/16_nesting.c
deleted file mode 100644
index 2b72cc028..000000000
--- a/tinyc/tests/tests2/16_nesting.c
+++ /dev/null
@@ -1,21 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   int x, y, z;
-
-   for (x = 0; x < 2; x++)
-   {
-      for (y = 0; y < 3; y++)
-      {
-         for (z = 0; z < 3; z++)
-         {
-            printf("%d %d %d\n", x, y, z);
-         }
-      }
-   }
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/16_nesting.expect b/tinyc/tests/tests2/16_nesting.expect
deleted file mode 100644
index 5a3431ee0..000000000
--- a/tinyc/tests/tests2/16_nesting.expect
+++ /dev/null
@@ -1,18 +0,0 @@
-0 0 0
-0 0 1
-0 0 2
-0 1 0
-0 1 1
-0 1 2
-0 2 0
-0 2 1
-0 2 2
-1 0 0
-1 0 1
-1 0 2
-1 1 0
-1 1 1
-1 1 2
-1 2 0
-1 2 1
-1 2 2
diff --git a/tinyc/tests/tests2/17_enum.c b/tinyc/tests/tests2/17_enum.c
deleted file mode 100644
index e2bc73629..000000000
--- a/tinyc/tests/tests2/17_enum.c
+++ /dev/null
@@ -1,72 +0,0 @@
-#include <stdio.h>
-
-enum fred
-{
-   a,
-   b,
-   c,
-   d,
-   e = 54,
-   f = 73,
-   g,
-   h
-};
-
-/* All following uses of enum efoo should compile
-   without warning.  While forward enums aren't ISO C,
-   it's accepted by GCC also in strict mode, and only warned
-   about with -pedantic.  This happens in the real world.  */
-/* Strict ISO C doesn't allow this kind of forward declaration of
-   enums, but GCC accepts it (and gives only pedantic warning), and
-   it occurs in the wild.  */
-enum efoo;
-struct Sforward_use {
-    int (*fmember) (enum efoo x);
-};
-
-extern enum efoo it_real_fn(void);
-enum efoo {
-  ONE,
-  TWO,
-};
-struct S2 {
-  enum efoo (*f2) (void);
-};
-void should_compile(struct S2 *s)
-{
-  s->f2 = it_real_fn;
-}
-
-enum efoo it_real_fn(void)
-{
-  return TWO;
-}
-
-static unsigned int deref_uintptr(unsigned int *p)
-{
-  return *p;
-}
-
-enum Epositive {
-    epos_one, epos_two
-};
-
-int main()
-{
-   enum fred frod;
-   enum Epositive epos = epos_two;
-
-   printf("%d %d %d %d %d %d %d %d\n", a, b, c, d, e, f, g, h);
-   /* printf("%d\n", frod); */
-   frod = 12;
-   printf("%d\n", frod);
-   frod = e;
-   printf("%d\n", frod);
-
-   /* Following should compile without warning.  */
-   printf ("enum to int: %u\n", deref_uintptr(&epos));
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/17_enum.expect b/tinyc/tests/tests2/17_enum.expect
deleted file mode 100644
index d453a617b..000000000
--- a/tinyc/tests/tests2/17_enum.expect
+++ /dev/null
@@ -1,4 +0,0 @@
-0 1 2 3 54 73 74 75
-12
-54
-enum to int: 1
diff --git a/tinyc/tests/tests2/18_include.c b/tinyc/tests/tests2/18_include.c
deleted file mode 100644
index dbae3aa2e..000000000
--- a/tinyc/tests/tests2/18_include.c
+++ /dev/null
@@ -1,12 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   printf("including\n");
-#include "18_include.h"
-   printf("done\n");
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/18_include.expect b/tinyc/tests/tests2/18_include.expect
deleted file mode 100644
index 58c6d29a0..000000000
--- a/tinyc/tests/tests2/18_include.expect
+++ /dev/null
@@ -1,3 +0,0 @@
-including
-included
-done
diff --git a/tinyc/tests/tests2/18_include.h b/tinyc/tests/tests2/18_include.h
deleted file mode 100644
index dc8608025..000000000
--- a/tinyc/tests/tests2/18_include.h
+++ /dev/null
@@ -1 +0,0 @@
-printf("included\n");
diff --git a/tinyc/tests/tests2/19_pointer_arithmetic.c b/tinyc/tests/tests2/19_pointer_arithmetic.c
deleted file mode 100644
index aff65e5b5..000000000
--- a/tinyc/tests/tests2/19_pointer_arithmetic.c
+++ /dev/null
@@ -1,28 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   int a;
-   int *b;
-   int *c;
-
-   a = 42;
-   b = &a;
-   c = NULL;
-
-   printf("%d\n", *b);
-
-   if (b == NULL)
-      printf("b is NULL\n");
-   else
-      printf("b is not NULL\n");
-
-   if (c == NULL)
-      printf("c is NULL\n");
-   else
-      printf("c is not NULL\n");
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/19_pointer_arithmetic.expect b/tinyc/tests/tests2/19_pointer_arithmetic.expect
deleted file mode 100644
index 0cf781b9e..000000000
--- a/tinyc/tests/tests2/19_pointer_arithmetic.expect
+++ /dev/null
@@ -1,3 +0,0 @@
-42
-b is not NULL
-c is NULL
diff --git a/tinyc/tests/tests2/20_pointer_comparison.c b/tinyc/tests/tests2/20_pointer_comparison.c
deleted file mode 100644
index 825f7787d..000000000
--- a/tinyc/tests/tests2/20_pointer_comparison.c
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   int a;
-   int b;
-   int *d;
-   int *e;
-   d = &a;
-   e = &b;
-   a = 12;
-   b = 34;
-   printf("%d\n", *d);
-   printf("%d\n", *e);
-   printf("%d\n", d == e);
-   printf("%d\n", d != e);
-   d = e;
-   printf("%d\n", d == e);
-   printf("%d\n", d != e);
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/20_pointer_comparison.expect b/tinyc/tests/tests2/20_pointer_comparison.expect
deleted file mode 100644
index 5d1e5f5c7..000000000
--- a/tinyc/tests/tests2/20_pointer_comparison.expect
+++ /dev/null
@@ -1,6 +0,0 @@
-12
-34
-0
-1
-1
-0
diff --git a/tinyc/tests/tests2/21_char_array.c b/tinyc/tests/tests2/21_char_array.c
deleted file mode 100644
index f22f5275c..000000000
--- a/tinyc/tests/tests2/21_char_array.c
+++ /dev/null
@@ -1,33 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   int x = 'a';
-   char y = x;
-
-   char *a = "hello";
-
-   printf("%s\n", a);
-
-   int c;
-   c = *a;
-
-   char *b;
-   for (b = a; *b != 0; b++)
-      printf("%c: %d\n", *b, *b);
-
-   char destarray[10];
-   char *dest = &destarray[0];
-   char *src = a;
-
-   while (*src != 0)
-      *dest++ = *src++;
-
-   *dest = 0;
-
-   printf("copied string is %s\n", destarray);
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/21_char_array.expect b/tinyc/tests/tests2/21_char_array.expect
deleted file mode 100644
index dbc60683c..000000000
--- a/tinyc/tests/tests2/21_char_array.expect
+++ /dev/null
@@ -1,7 +0,0 @@
-hello
-h: 104
-e: 101
-l: 108
-l: 108
-o: 111
-copied string is hello
diff --git a/tinyc/tests/tests2/22_floating_point.c b/tinyc/tests/tests2/22_floating_point.c
deleted file mode 100644
index e3491f5c6..000000000
--- a/tinyc/tests/tests2/22_floating_point.c
+++ /dev/null
@@ -1,50 +0,0 @@
-#include <stdio.h>
-#include <math.h>
-
-int main()
-{
-   // variables
-   float a = 12.34 + 56.78;
-   printf("%f\n", a);
-
-   // infix operators
-   printf("%f\n", 12.34 + 56.78);
-   printf("%f\n", 12.34 - 56.78);
-   printf("%f\n", 12.34 * 56.78);
-   printf("%f\n", 12.34 / 56.78);
-
-   // comparison operators
-   printf("%d %d %d %d %d %d\n", 12.34 < 56.78, 12.34 <= 56.78, 12.34 == 56.78, 12.34 >= 56.78, 12.34 > 56.78, 12.34 != 56.78);
-   printf("%d %d %d %d %d %d\n", 12.34 < 12.34, 12.34 <= 12.34, 12.34 == 12.34, 12.34 >= 12.34, 12.34 > 12.34, 12.34 != 12.34);
-   printf("%d %d %d %d %d %d\n", 56.78 < 12.34, 56.78 <= 12.34, 56.78 == 12.34, 56.78 >= 12.34, 56.78 > 12.34, 56.78 != 12.34);
-
-   // assignment operators
-   a = 12.34;
-   a += 56.78;
-   printf("%f\n", a);
-
-   a = 12.34;
-   a -= 56.78;
-   printf("%f\n", a);
-
-   a = 12.34;
-   a *= 56.78;
-   printf("%f\n", a);
-
-   a = 12.34;
-   a /= 56.78;
-   printf("%f\n", a);
-
-   // prefix operators
-   printf("%f\n", +12.34);
-   printf("%f\n", -12.34);
-
-   // type coercion
-   a = 2;
-   printf("%f\n", a);
-   printf("%f\n", sin(2));
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/22_floating_point.expect b/tinyc/tests/tests2/22_floating_point.expect
deleted file mode 100644
index 75ea3a7e2..000000000
--- a/tinyc/tests/tests2/22_floating_point.expect
+++ /dev/null
@@ -1,16 +0,0 @@
-69.120003
-69.120000
--44.440000
-700.665200
-0.217330
-1 1 0 0 0 1
-0 1 1 1 0 0
-0 0 0 1 1 1
-69.120003
--44.439999
-700.665222
-0.217330
-12.340000
--12.340000
-2.000000
-0.909297
diff --git a/tinyc/tests/tests2/23_type_coercion.c b/tinyc/tests/tests2/23_type_coercion.c
deleted file mode 100644
index 1fcc33519..000000000
--- a/tinyc/tests/tests2/23_type_coercion.c
+++ /dev/null
@@ -1,54 +0,0 @@
-#include <stdio.h>
-
-void charfunc(char a)
-{
-   printf("char: %c\n", a);
-}
-
-void intfunc(int a)
-{
-   printf("int: %d\n", a);
-}
-
-void floatfunc(float a)
-{
-   printf("float: %f\n", a);
-}
-
-int main()
-{
-   charfunc('a');
-   charfunc(98);
-   charfunc(99.0);
-
-   intfunc('a');
-   intfunc(98);
-   intfunc(99.0);
-
-   floatfunc('a');
-   floatfunc(98);
-   floatfunc(99.0);
-
-   /* printf("%c %d %f\n", 'a', 'b', 'c'); */
-   /* printf("%c %d %f\n", 97, 98, 99); */
-   /* printf("%c %d %f\n", 97.0, 98.0, 99.0); */
-
-   char b = 97;
-   char c = 97.0;
-
-   printf("%d %d\n", b, c);
-
-   int d = 'a';
-   int e = 97.0;
-
-   printf("%d %d\n", d, e);
-
-   float f = 'a';
-   float g = 97;
-
-   printf("%f %f\n", f, g);
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/23_type_coercion.expect b/tinyc/tests/tests2/23_type_coercion.expect
deleted file mode 100644
index d9076f018..000000000
--- a/tinyc/tests/tests2/23_type_coercion.expect
+++ /dev/null
@@ -1,12 +0,0 @@
-char: a
-char: b
-char: c
-int: 97
-int: 98
-int: 99
-float: 97.000000
-float: 98.000000
-float: 99.000000
-97 97
-97 97
-97.000000 97.000000
diff --git a/tinyc/tests/tests2/24_math_library.c b/tinyc/tests/tests2/24_math_library.c
deleted file mode 100644
index 514a25f63..000000000
--- a/tinyc/tests/tests2/24_math_library.c
+++ /dev/null
@@ -1,30 +0,0 @@
-#define _ISOC99_SOURCE 1
-
-#include <stdio.h>
-#include <math.h>
-
-int main()
-{
-   printf("%f\n", sin(0.12));
-   printf("%f\n", cos(0.12));
-   printf("%f\n", tan(0.12));
-   printf("%f\n", asin(0.12));
-   printf("%f\n", acos(0.12));
-   printf("%f\n", atan(0.12));
-   printf("%f\n", sinh(0.12));
-   printf("%f\n", cosh(0.12));
-   printf("%f\n", tanh(0.12));
-   printf("%f\n", exp(0.12));
-   printf("%f\n", fabs(-0.12));
-   printf("%f\n", log(0.12));
-   printf("%f\n", log10(0.12));
-   printf("%f\n", pow(0.12, 0.12));
-   printf("%f\n", sqrt(0.12));
-   printf("%f\n", round(12.34));
-   printf("%f\n", ceil(12.34));
-   printf("%f\n", floor(12.34));
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/24_math_library.expect b/tinyc/tests/tests2/24_math_library.expect
deleted file mode 100644
index 99f7299cc..000000000
--- a/tinyc/tests/tests2/24_math_library.expect
+++ /dev/null
@@ -1,18 +0,0 @@
-0.119712
-0.992809
-0.120579
-0.120290
-1.450506
-0.119429
-0.120288
-1.007209
-0.119427
-1.127497
-0.120000
--2.120264
--0.920819
-0.775357
-0.346410
-12.000000
-13.000000
-12.000000
diff --git a/tinyc/tests/tests2/25_quicksort.c b/tinyc/tests/tests2/25_quicksort.c
deleted file mode 100644
index 5cc08bd9d..000000000
--- a/tinyc/tests/tests2/25_quicksort.c
+++ /dev/null
@@ -1,83 +0,0 @@
-#include <stdio.h>
-
-int array[16];
-
-//Swap integer values by array indexes
-void swap(int a, int b)
-{
-   int tmp  = array[a];
-   array[a] = array[b];
-   array[b] = tmp;
-}
-
-//Partition the array into two halves and return the
-//index about which the array is partitioned
-int partition(int left, int right)
-{
-   int pivotIndex = left;
-   int pivotValue = array[pivotIndex];
-   int index = left;
-   int i;
-
-   swap(pivotIndex, right);
-   for(i = left; i < right; i++)
-   {
-      if(array[i] < pivotValue)
-      {
-         swap(i, index);
-         index += 1;
-      }
-   }
-   swap(right, index);
-
-   return index;
-}
-
-//Quicksort the array
-void quicksort(int left, int right)
-{
-   if(left >= right)
-      return;
-
-   int index = partition(left, right);
-   quicksort(left, index - 1);
-   quicksort(index + 1, right);
-}
-
-int main()
-{
-   int i;
-
-   array[0] = 62;
-   array[1] = 83;
-   array[2] = 4;
-   array[3] = 89;
-   array[4] = 36;
-   array[5] = 21;
-   array[6] = 74;
-   array[7] = 37;
-   array[8] = 65;
-   array[9] = 33;
-   array[10] = 96;
-   array[11] = 38;
-   array[12] = 53;
-   array[13] = 16;
-   array[14] = 74;
-   array[15] = 55;
-
-   for (i = 0; i < 16; i++)
-      printf("%d ", array[i]);
-
-   printf("\n");
-
-   quicksort(0, 15);
-
-   for (i = 0; i < 16; i++)
-      printf("%d ", array[i]);
-
-   printf("\n");
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/25_quicksort.expect b/tinyc/tests/tests2/25_quicksort.expect
deleted file mode 100644
index 2d39cd327..000000000
--- a/tinyc/tests/tests2/25_quicksort.expect
+++ /dev/null
@@ -1,2 +0,0 @@
-62 83 4 89 36 21 74 37 65 33 96 38 53 16 74 55 
-4 16 21 33 36 37 38 53 55 62 65 74 74 83 89 96 
diff --git a/tinyc/tests/tests2/26_character_constants.c b/tinyc/tests/tests2/26_character_constants.c
deleted file mode 100644
index 95c442371..000000000
--- a/tinyc/tests/tests2/26_character_constants.c
+++ /dev/null
@@ -1,17 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   printf("%d\n", '\1');
-   printf("%d\n", '\10');
-   printf("%d\n", '\100');
-   printf("%d\n", '\x01');
-   printf("%d\n", '\x0e');
-   printf("%d\n", '\x10');
-   printf("%d\n", '\x40');
-   printf("test \x40\n");
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/26_character_constants.expect b/tinyc/tests/tests2/26_character_constants.expect
deleted file mode 100644
index 8f8bfa48c..000000000
--- a/tinyc/tests/tests2/26_character_constants.expect
+++ /dev/null
@@ -1,8 +0,0 @@
-1
-8
-64
-1
-14
-16
-64
-test @
diff --git a/tinyc/tests/tests2/27_sizeof.c b/tinyc/tests/tests2/27_sizeof.c
deleted file mode 100644
index 5ae0ede54..000000000
--- a/tinyc/tests/tests2/27_sizeof.c
+++ /dev/null
@@ -1,18 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   char a;
-   int b;
-   double c;
-
-   printf("%d\n", sizeof(a));
-   printf("%d\n", sizeof(b));
-   printf("%d\n", sizeof(c));
-
-   printf("%d\n", sizeof(!a));
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/27_sizeof.expect b/tinyc/tests/tests2/27_sizeof.expect
deleted file mode 100644
index a47ea3a40..000000000
--- a/tinyc/tests/tests2/27_sizeof.expect
+++ /dev/null
@@ -1,4 +0,0 @@
-1
-4
-8
-4
diff --git a/tinyc/tests/tests2/28_strings.c b/tinyc/tests/tests2/28_strings.c
deleted file mode 100644
index 2db22986e..000000000
--- a/tinyc/tests/tests2/28_strings.c
+++ /dev/null
@@ -1,45 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-
-int main()
-{
-   char a[10];
-
-   strcpy(a, "hello");
-   printf("%s\n", a);
-
-   strncpy(a, "gosh", 2);
-   printf("%s\n", a);
-
-   printf("%d\n", strcmp(a, "apple") > 0);
-   printf("%d\n", strcmp(a, "goere") > 0);
-   printf("%d\n", strcmp(a, "zebra") < 0);
-
-   printf("%d\n", strlen(a));
-
-   strcat(a, "!");
-   printf("%s\n", a);
-
-   printf("%d\n", strncmp(a, "apple", 2) > 0);
-   printf("%d\n", strncmp(a, "goere", 2) == 0);
-   printf("%d\n", strncmp(a, "goerg", 2) == 0);
-   printf("%d\n", strncmp(a, "zebra", 2) < 0);
-
-   printf("%s\n", strchr(a, 'o'));
-   printf("%s\n", strrchr(a, 'l'));
-   printf("%d\n", strrchr(a, 'x') == NULL);
-
-   memset(&a[1], 'r', 4);
-   printf("%s\n", a);
-
-   memcpy(&a[2], a, 2);
-   printf("%s\n", a);
-
-   printf("%d\n", memcmp(a, "apple", 4) > 0);
-   printf("%d\n", memcmp(a, "grgr", 4) == 0);
-   printf("%d\n", memcmp(a, "zebra", 4) < 0);
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/28_strings.expect b/tinyc/tests/tests2/28_strings.expect
deleted file mode 100644
index fd9217a22..000000000
--- a/tinyc/tests/tests2/28_strings.expect
+++ /dev/null
@@ -1,19 +0,0 @@
-hello
-gollo
-1
-1
-1
-5
-gollo!
-1
-1
-1
-1
-ollo!
-lo!
-1
-grrrr!
-grgrr!
-1
-1
-1
diff --git a/tinyc/tests/tests2/29_array_address.c b/tinyc/tests/tests2/29_array_address.c
deleted file mode 100644
index bda5ddde2..000000000
--- a/tinyc/tests/tests2/29_array_address.c
+++ /dev/null
@@ -1,13 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-
-int main()
-{
-   char a[10];
-   strcpy(a, "abcdef");
-   printf("%s\n", &a[1]);
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/29_array_address.expect b/tinyc/tests/tests2/29_array_address.expect
deleted file mode 100644
index 9bc868324..000000000
--- a/tinyc/tests/tests2/29_array_address.expect
+++ /dev/null
@@ -1 +0,0 @@
-bcdef
diff --git a/tinyc/tests/tests2/30_hanoi.c b/tinyc/tests/tests2/30_hanoi.c
deleted file mode 100644
index 7c0893b1a..000000000
--- a/tinyc/tests/tests2/30_hanoi.c
+++ /dev/null
@@ -1,122 +0,0 @@
-/* example from http://barnyard.syr.edu/quickies/hanoi.c */
-
-/* hanoi.c: solves the tower of hanoi problem. (Programming exercise.) */
-/* By Terry R. McConnell (12/2/97) */
-/* Compile: cc -o hanoi hanoi.c */
-
-/* This program does no error checking. But then, if it's right, 
-   it's right ... right ? */
-
-
-/* The original towers of hanoi problem seems to have been originally posed
-   by one M. Claus in 1883. There is a popular legend that goes along with
-   it that has been often repeated and paraphrased. It goes something like this:
-   In the great temple at Benares there are 3 golden spikes. On one of them,
-   God placed 64 disks increasing in size from bottom to top, at the beginning
-   of time. Since then, and to this day, the priest on duty constantly transfers
-   disks, one at a time, in such a way that no larger disk is ever put on top
-   of a smaller one. When the disks have been transferred entirely to another
-   spike the Universe will come to an end in a large thunderclap.
-
-   This paraphrases the original legend due to DeParville, La Nature, Paris 1884,
-   Part I, 285-286. For this and further information see: Mathematical 
-   Recreations & Essays, W.W. Rouse Ball, MacMillan, NewYork, 11th Ed. 1967,
-   303-305.
- *
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#define TRUE 1
-#define FALSE 0
-
-/* This is the number of "disks" on tower A initially. Taken to be 64 in the
- * legend. The number of moves required, in general, is 2^N - 1. For N = 64,
- * this is 18,446,744,073,709,551,615 */
-#define N 4
-
-/* These are the three towers. For example if the state of A is 0,1,3,4, that
- * means that there are three discs on A of sizes 1, 3, and 4. (Think of right
- * as being the "down" direction.) */
-int A[N], B[N], C[N]; 
-
-void Hanoi(int,int*,int*,int*);
-
-/* Print the current configuration of A, B, and C to the screen */
-void PrintAll()
-{
-   int i;
-
-   printf("A: ");
-   for(i=0;i<N;i++)printf(" %d ",A[i]);
-   printf("\n");
-
-   printf("B: ");
-   for(i=0;i<N;i++)printf(" %d ",B[i]);
-   printf("\n");
-
-   printf("C: ");
-   for(i=0;i<N;i++)printf(" %d ",C[i]);
-   printf("\n");
-   printf("------------------------------------------\n");
-   return;
-}
-
-/* Move the leftmost nonzero element of source to dest, leave behind 0. */
-/* Returns the value moved (not used.) */
-int Move(int *source, int *dest)
-{
-   int i = 0, j = 0;
-
-   while (i<N && (source[i])==0) i++;
-   while (j<N && (dest[j])==0) j++;
-
-   dest[j-1] = source[i];
-   source[i] = 0;
-   PrintAll();       /* Print configuration after each move. */
-   return dest[j-1];
-}
-
-
-/* Moves first n nonzero numbers from source to dest using the rules of Hanoi.
-   Calls itself recursively.
-   */
-void Hanoi(int n,int *source, int *dest, int *spare)
-{
-   int i;
-   if(n==1){
-      Move(source,dest);
-      return;
-   }
-
-   Hanoi(n-1,source,spare,dest);
-   Move(source,dest);
-   Hanoi(n-1,spare,dest,source);	
-   return;
-}
-
-int main()
-{
-   int i;
-
-   /* initialize the towers */
-   for(i=0;i<N;i++)A[i]=i+1;
-   for(i=0;i<N;i++)B[i]=0;
-   for(i=0;i<N;i++)C[i]=0;
-
-   printf("Solution of Tower of Hanoi Problem with %d Disks\n\n",N);
-
-   /* Print the starting state */
-   printf("Starting state:\n");
-   PrintAll();
-   printf("\n\nSubsequent states:\n\n");
-
-   /* Do it! Use A = Source, B = Destination, C = Spare */
-   Hanoi(N,A,B,C);
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/30_hanoi.expect b/tinyc/tests/tests2/30_hanoi.expect
deleted file mode 100644
index 7798ee051..000000000
--- a/tinyc/tests/tests2/30_hanoi.expect
+++ /dev/null
@@ -1,71 +0,0 @@
-Solution of Tower of Hanoi Problem with 4 Disks
-
-Starting state:
-A:  1  2  3  4 
-B:  0  0  0  0 
-C:  0  0  0  0 
-------------------------------------------
-
-
-Subsequent states:
-
-A:  0  2  3  4 
-B:  0  0  0  0 
-C:  0  0  0  1 
-------------------------------------------
-A:  0  0  3  4 
-B:  0  0  0  2 
-C:  0  0  0  1 
-------------------------------------------
-A:  0  0  3  4 
-B:  0  0  1  2 
-C:  0  0  0  0 
-------------------------------------------
-A:  0  0  0  4 
-B:  0  0  1  2 
-C:  0  0  0  3 
-------------------------------------------
-A:  0  0  1  4 
-B:  0  0  0  2 
-C:  0  0  0  3 
-------------------------------------------
-A:  0  0  1  4 
-B:  0  0  0  0 
-C:  0  0  2  3 
-------------------------------------------
-A:  0  0  0  4 
-B:  0  0  0  0 
-C:  0  1  2  3 
-------------------------------------------
-A:  0  0  0  0 
-B:  0  0  0  4 
-C:  0  1  2  3 
-------------------------------------------
-A:  0  0  0  0 
-B:  0  0  1  4 
-C:  0  0  2  3 
-------------------------------------------
-A:  0  0  0  2 
-B:  0  0  1  4 
-C:  0  0  0  3 
-------------------------------------------
-A:  0  0  1  2 
-B:  0  0  0  4 
-C:  0  0  0  3 
-------------------------------------------
-A:  0  0  1  2 
-B:  0  0  3  4 
-C:  0  0  0  0 
-------------------------------------------
-A:  0  0  0  2 
-B:  0  0  3  4 
-C:  0  0  0  1 
-------------------------------------------
-A:  0  0  0  0 
-B:  0  2  3  4 
-C:  0  0  0  1 
-------------------------------------------
-A:  0  0  0  0 
-B:  1  2  3  4 
-C:  0  0  0  0 
-------------------------------------------
diff --git a/tinyc/tests/tests2/31_args.c b/tinyc/tests/tests2/31_args.c
deleted file mode 100644
index dcafed52b..000000000
--- a/tinyc/tests/tests2/31_args.c
+++ /dev/null
@@ -1,14 +0,0 @@
-#include <stdio.h>
-
-int main(int argc, char **argv)
-{
-   int Count;
-
-   printf("hello world %d\n", argc);
-   for (Count = 1; Count < argc; Count++)
-      printf("arg %d: %s\n", Count, argv[Count]);
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/31_args.expect b/tinyc/tests/tests2/31_args.expect
deleted file mode 100644
index 8c60bfc5b..000000000
--- a/tinyc/tests/tests2/31_args.expect
+++ /dev/null
@@ -1,6 +0,0 @@
-hello world 6
-arg 1: arg1
-arg 2: arg2
-arg 3: arg3
-arg 4: arg4
-arg 5: arg5
diff --git a/tinyc/tests/tests2/32_led.c b/tinyc/tests/tests2/32_led.c
deleted file mode 100644
index 5596cbfd3..000000000
--- a/tinyc/tests/tests2/32_led.c
+++ /dev/null
@@ -1,266 +0,0 @@
-/* example from http://barnyard.syr.edu/quickies/led.c */
-
-/* led.c: print out number as if on 7 line led display. I.e., write integer
-   given on command line like this:  
-      _   _       _  
-   |  _|  _| |_| |_  
-   | |_   _|   |  _| etc.
-
-   We assume the terminal behaves like a classical teletype. So the top
-   lines of all digits have to be printed first, then the middle lines of
-   all digits, etc.
-
-   By Terry R. McConnell
-
-compile: cc -o led led.c
-
-If you just want to link in the subroutine print_led that does all the
-work, compile with -DNO_MAIN, and declare the following in any source file
-that uses the call:
-
-extern void print_led(unsigned long x, char *buf);
-
-Bug: you cannot call repeatedly to print more than one number to a line.
-That would require curses or some other terminal API that allows moving the
-cursor to a previous line.
-
-*/
-
-
-
-#include <stdlib.h>
-#include <stdio.h>
-
-#define MAX_DIGITS 32
-#define NO_MAIN
-
-
-/* Print the top line of the digit d into buffer. 
-   Does not null terminate buffer. */
-
-void topline(int d, char *p){
-
-   *p++ = ' ';
-   switch(d){
-
-      /* all these have _ on top line */
-
-      case 0:
-      case 2:
-      case 3:
-      case 5:
-      case 7:
-      case 8:
-      case 9:
-         *p++ = '_';
-         break;
-      default:
-         *p++=' ';
-
-   }
-   *p++=' ';
-}
-
-/* Print the middle line of the digit d into the buffer. 
-   Does not null terminate. */
-
-void midline(int d, char *p){
-
-   switch(d){
-
-      /* those that have leading | on middle line */
-
-      case 0:
-      case 4:
-      case 5:
-      case 6:
-      case 8:
-      case 9:
-         *p++='|';
-         break;
-      default:
-         *p++=' ';	
-   }
-   switch(d){
-
-      /* those that have _ on middle line */
-
-      case 2:
-      case 3:
-      case 4:
-      case 5:
-      case 6:
-      case 8:
-      case 9:
-         *p++='_';
-         break;
-      default:
-         *p++=' ';
-
-   }
-   switch(d){
-
-      /* those that have closing | on middle line */
-
-      case 0:
-      case 1:
-      case 2:
-      case 3:
-      case 4:
-      case 7:
-      case 8:
-      case 9:
-         *p++='|';
-         break;
-      default:
-         *p++=' ';
-
-   }
-}
-
-/* Print the bottom line of the digit d. Does not null terminate. */
-
-void botline(int d, char *p){
-
-
-   switch(d){
-
-      /* those that have leading | on bottom line */
-
-      case 0:
-      case 2:
-      case 6:
-      case 8:
-         *p++='|';
-         break;
-      default:
-         *p++=' ';	
-   }
-   switch(d){
-
-      /* those that have _ on bottom line */
-
-      case 0:
-      case 2:
-      case 3:
-      case 5:
-      case 6:
-      case 8:
-         *p++='_';
-         break;
-      default:
-         *p++=' ';
-
-   }
-   switch(d){
-
-      /* those that have closing | on bottom line */
-
-      case 0:
-      case 1:
-      case 3:
-      case 4:
-      case 5:
-      case 6:
-      case 7:
-      case 8:
-      case 9:
-         *p++='|';
-         break;
-      default:
-         *p++=' ';
-
-   }
-}
-
-/* Write the led representation of integer to string buffer. */
-
-void print_led(unsigned long x, char *buf)
-{
-
-   int i=0,n;
-   static int d[MAX_DIGITS];
-
-
-   /* extract digits from x */
-
-   n = ( x == 0L ? 1 : 0 );  /* 0 is a digit, hence a special case */
-
-   while(x){
-      d[n++] = (int)(x%10L);
-      if(n >= MAX_DIGITS)break;
-      x = x/10L;
-   }
-
-   /* print top lines of all digits */
-
-   for(i=n-1;i>=0;i--){
-      topline(d[i],buf);
-      buf += 3;
-      *buf++=' ';
-   }
-   *buf++='\n'; /* move teletype to next line */
-
-   /* print middle lines of all digits */
-
-   for(i=n-1;i>=0;i--){
-      midline(d[i],buf);
-      buf += 3;
-      *buf++=' ';
-   }
-   *buf++='\n';
-
-   /* print bottom lines of all digits */
-
-   for(i=n-1;i>=0;i--){
-      botline(d[i],buf);
-      buf += 3;
-      *buf++=' ';
-   }
-   *buf++='\n';
-   *buf='\0';
-}
-
-int main()
-{
-   char buf[5*MAX_DIGITS];
-   print_led(1234567, buf);
-   printf("%s\n",buf);
-
-   return 0;
-}
-
-#ifndef NO_MAIN
-int main(int argc, char **argv)
-{
-
-   int i=0,n;
-   long x;
-   static int d[MAX_DIGITS];
-   char buf[5*MAX_DIGITS];
-
-   if(argc != 2){
-      fprintf(stderr,"led: usage: led integer\n");
-      return 1;
-   }
-
-   /* fetch argument from command line */
-
-   x = atol(argv[1]);
-
-   /* sanity check */
-
-   if(x<0){
-      fprintf(stderr,"led: %d must be non-negative\n",x);
-      return 1;
-   }
-
-   print_led(x,buf);
-   printf("%s\n",buf);
-
-   return 0;
-
-}
-#endif
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/32_led.expect b/tinyc/tests/tests2/32_led.expect
deleted file mode 100644
index c53b58ae0..000000000
--- a/tinyc/tests/tests2/32_led.expect
+++ /dev/null
@@ -1,4 +0,0 @@
-     _   _       _       _  
-  |  _|  _| |_| |_  |_    | 
-  | |_   _|   |  _| |_|   | 
-
diff --git a/tinyc/tests/tests2/33_ternary_op.c b/tinyc/tests/tests2/33_ternary_op.c
deleted file mode 100644
index 8579b5029..000000000
--- a/tinyc/tests/tests2/33_ternary_op.c
+++ /dev/null
@@ -1,15 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   int Count;
-
-   for (Count = 0; Count < 10; Count++)
-   {
-      printf("%d\n", (Count < 5) ? (Count*Count) : (Count * 3));
-   }
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/33_ternary_op.expect b/tinyc/tests/tests2/33_ternary_op.expect
deleted file mode 100644
index 45ea507a5..000000000
--- a/tinyc/tests/tests2/33_ternary_op.expect
+++ /dev/null
@@ -1,10 +0,0 @@
-0
-1
-4
-9
-16
-15
-18
-21
-24
-27
diff --git a/tinyc/tests/tests2/34_array_assignment.c b/tinyc/tests/tests2/34_array_assignment.c
deleted file mode 100644
index 5885c9732..000000000
--- a/tinyc/tests/tests2/34_array_assignment.c
+++ /dev/null
@@ -1,23 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   int a[4];
-
-   a[0] = 12;
-   a[1] = 23;
-   a[2] = 34;
-   a[3] = 45;
-
-   printf("%d %d %d %d\n", a[0], a[1], a[2], a[3]);
-
-   int b[4];
-
-   b = a;
-
-   printf("%d %d %d %d\n", b[0], b[1], b[2], b[3]);
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/34_array_assignment.expect b/tinyc/tests/tests2/34_array_assignment.expect
deleted file mode 100644
index 9736bf508..000000000
--- a/tinyc/tests/tests2/34_array_assignment.expect
+++ /dev/null
@@ -1,2 +0,0 @@
-12 23 34 45
-12 23 34 45
diff --git a/tinyc/tests/tests2/35_sizeof.c b/tinyc/tests/tests2/35_sizeof.c
deleted file mode 100644
index 672e87e7f..000000000
--- a/tinyc/tests/tests2/35_sizeof.c
+++ /dev/null
@@ -1,14 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   char a;
-   short b;
-
-   printf("%d %d\n", sizeof(char), sizeof(a));
-   printf("%d %d\n", sizeof(short), sizeof(b));
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/35_sizeof.expect b/tinyc/tests/tests2/35_sizeof.expect
deleted file mode 100644
index 534fb831b..000000000
--- a/tinyc/tests/tests2/35_sizeof.expect
+++ /dev/null
@@ -1,2 +0,0 @@
-1 1
-2 2
diff --git a/tinyc/tests/tests2/36_array_initialisers.c b/tinyc/tests/tests2/36_array_initialisers.c
deleted file mode 100644
index 1bc8ee088..000000000
--- a/tinyc/tests/tests2/36_array_initialisers.c
+++ /dev/null
@@ -1,21 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   int Count;
-
-   int Array[10] = { 12, 34, 56, 78, 90, 123, 456, 789, 8642, 9753 };
-
-   for (Count = 0; Count < 10; Count++)
-      printf("%d: %d\n", Count, Array[Count]);
-
-   int Array2[10] = { 12, 34, 56, 78, 90, 123, 456, 789, 8642, 9753, };
-
-   for (Count = 0; Count < 10; Count++)
-      printf("%d: %d\n", Count, Array2[Count]);
-
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/36_array_initialisers.expect b/tinyc/tests/tests2/36_array_initialisers.expect
deleted file mode 100644
index 3ac6c7729..000000000
--- a/tinyc/tests/tests2/36_array_initialisers.expect
+++ /dev/null
@@ -1,20 +0,0 @@
-0: 12
-1: 34
-2: 56
-3: 78
-4: 90
-5: 123
-6: 456
-7: 789
-8: 8642
-9: 9753
-0: 12
-1: 34
-2: 56
-3: 78
-4: 90
-5: 123
-6: 456
-7: 789
-8: 8642
-9: 9753
diff --git a/tinyc/tests/tests2/37_sprintf.c b/tinyc/tests/tests2/37_sprintf.c
deleted file mode 100644
index 1dd1dce28..000000000
--- a/tinyc/tests/tests2/37_sprintf.c
+++ /dev/null
@@ -1,17 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   char Buf[100];
-   int Count;
-
-   for (Count = 1; Count <= 20; Count++)
-   {
-      sprintf(Buf, "->%02d<-\n", Count);
-      printf("%s", Buf);
-   }
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/37_sprintf.expect b/tinyc/tests/tests2/37_sprintf.expect
deleted file mode 100644
index a643da842..000000000
--- a/tinyc/tests/tests2/37_sprintf.expect
+++ /dev/null
@@ -1,20 +0,0 @@
-->01<-
-->02<-
-->03<-
-->04<-
-->05<-
-->06<-
-->07<-
-->08<-
-->09<-
-->10<-
-->11<-
-->12<-
-->13<-
-->14<-
-->15<-
-->16<-
-->17<-
-->18<-
-->19<-
-->20<-
diff --git a/tinyc/tests/tests2/38_multiple_array_index.c b/tinyc/tests/tests2/38_multiple_array_index.c
deleted file mode 100644
index 4e1868efe..000000000
--- a/tinyc/tests/tests2/38_multiple_array_index.c
+++ /dev/null
@@ -1,32 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   int a[4][4];
-   int b = 0;
-   int x;
-   int y;
-
-   for (x = 0; x < 4; x++)
-   {
-      for (y = 0; y < 4; y++)
-      {
-         b++;
-         a[x][y] = b;
-      }
-   }
-
-   for (x = 0; x < 4; x++)
-   {
-      printf("x=%d: ", x);
-      for (y = 0; y < 4; y++)
-      {
-         printf("%d ", a[x][y]);
-      }
-      printf("\n");
-   }
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/38_multiple_array_index.expect b/tinyc/tests/tests2/38_multiple_array_index.expect
deleted file mode 100644
index 747ad751d..000000000
--- a/tinyc/tests/tests2/38_multiple_array_index.expect
+++ /dev/null
@@ -1,4 +0,0 @@
-x=0: 1 2 3 4
-x=1: 5 6 7 8
-x=2: 9 10 11 12
-x=3: 13 14 15 16 
diff --git a/tinyc/tests/tests2/39_typedef.c b/tinyc/tests/tests2/39_typedef.c
deleted file mode 100644
index da73f71e6..000000000
--- a/tinyc/tests/tests2/39_typedef.c
+++ /dev/null
@@ -1,65 +0,0 @@
-#include <stdio.h>
-
-typedef int MyInt;
-
-struct FunStruct
-{
-   int i;
-   int j;
-};
-
-typedef struct FunStruct MyFunStruct;
-
-typedef MyFunStruct *MoreFunThanEver;
-
-int main()
-{
-   MyInt a = 1;
-   printf("%d\n", a);
-
-   MyFunStruct b;
-   b.i = 12;
-   b.j = 34;
-   printf("%d,%d\n", b.i, b.j);
-
-   MoreFunThanEver c = &b;
-   printf("%d,%d\n", c->i, c->j);
-
-   return 0;
-}
-
-/* "If the specification of an array type includes any type qualifiers,
-   the element type is so-qualified, not the array type." */
-
-typedef int A[3];
-extern A const ca;
-extern const A ca;
-extern const int ca[3];
-
-typedef A B[1][2];
-extern B const cb;
-extern const B cb;
-extern const int cb[1][2][3];
-
-extern B b;
-extern int b[1][2][3];
-
-/* Funny but valid function declaration.  */
-typedef int functype (int);
-extern functype func;
-int func(int i)
-{
-   return i + 1;
-}
-
-/* Even funnier function decl and definition using typeof.  */
-int set_anon_super(void);
-int set_anon_super(void)
-{
-   return 42;
-}
-typedef int sas_type (void);
-extern typeof(set_anon_super) set_anon_super;
-extern sas_type set_anon_super;
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/39_typedef.expect b/tinyc/tests/tests2/39_typedef.expect
deleted file mode 100644
index b9050a9b6..000000000
--- a/tinyc/tests/tests2/39_typedef.expect
+++ /dev/null
@@ -1,3 +0,0 @@
-1
-12,34
-12,34
diff --git a/tinyc/tests/tests2/40_stdio.c b/tinyc/tests/tests2/40_stdio.c
deleted file mode 100644
index b98609302..000000000
--- a/tinyc/tests/tests2/40_stdio.c
+++ /dev/null
@@ -1,52 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   FILE *f = fopen("fred.txt", "w");
-   fwrite("hello\nhello\n", 1, 12, f);
-   fclose(f);
-
-   char freddy[7];
-   f = fopen("fred.txt", "r");
-   if (fread(freddy, 1, 6, f) != 6)
-      printf("couldn't read fred.txt\n");
-
-   freddy[6] = '\0';
-   fclose(f);
-
-   printf("%s", freddy);
-
-   int InChar;
-   char ShowChar;
-   f = fopen("fred.txt", "r");
-   while ( (InChar = fgetc(f)) != EOF)
-   {
-      ShowChar = InChar;
-      if (ShowChar < ' ')
-         ShowChar = '.';
-
-      printf("ch: %d '%c'\n", InChar, ShowChar);
-   }
-   fclose(f);
-
-   f = fopen("fred.txt", "r");
-   while ( (InChar = getc(f)) != EOF)
-   {
-      ShowChar = InChar;
-      if (ShowChar < ' ')
-         ShowChar = '.';
-
-      printf("ch: %d '%c'\n", InChar, ShowChar);
-   }
-   fclose(f);
-
-   f = fopen("fred.txt", "r");
-   while (fgets(freddy, sizeof(freddy), f) != NULL)
-      printf("x: %s", freddy);
-
-   fclose(f);
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/40_stdio.expect b/tinyc/tests/tests2/40_stdio.expect
deleted file mode 100644
index e08167a6e..000000000
--- a/tinyc/tests/tests2/40_stdio.expect
+++ /dev/null
@@ -1,27 +0,0 @@
-hello
-ch: 104 'h'
-ch: 101 'e'
-ch: 108 'l'
-ch: 108 'l'
-ch: 111 'o'
-ch: 10 '.'
-ch: 104 'h'
-ch: 101 'e'
-ch: 108 'l'
-ch: 108 'l'
-ch: 111 'o'
-ch: 10 '.'
-ch: 104 'h'
-ch: 101 'e'
-ch: 108 'l'
-ch: 108 'l'
-ch: 111 'o'
-ch: 10 '.'
-ch: 104 'h'
-ch: 101 'e'
-ch: 108 'l'
-ch: 108 'l'
-ch: 111 'o'
-ch: 10 '.'
-x: hello
-x: hello
diff --git a/tinyc/tests/tests2/41_hashif.c b/tinyc/tests/tests2/41_hashif.c
deleted file mode 100644
index cb37b9eb0..000000000
--- a/tinyc/tests/tests2/41_hashif.c
+++ /dev/null
@@ -1,85 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   printf("#include test\n");
-
-#if 1
-#if 0
-   printf("a\n");
-#else
-   printf("b\n");
-#endif
-#else
-#if 0
-   printf("c\n");
-#else
-   printf("d\n");
-#endif
-#endif
-
-#if 0
-#if 1
-   printf("e\n");
-#else
-   printf("f\n");
-#endif
-#else
-#if 1
-   printf("g\n");
-#else
-   printf("h\n");
-#endif
-#endif
-
-#define DEF
-
-#ifdef DEF
-#ifdef DEF
-   printf("i\n");
-#else
-   printf("j\n");
-#endif
-#else
-#ifdef DEF
-   printf("k\n");
-#else
-   printf("l\n");
-#endif
-#endif
-
-#ifndef DEF
-#ifndef DEF
-   printf("m\n");
-#else
-   printf("n\n");
-#endif
-#else
-#ifndef DEF
-   printf("o\n");
-#else
-   printf("p\n");
-#endif
-#endif
-
-#define ONE 1
-#define ZERO 0
-
-#if ONE
-#if ZERO
-   printf("q\n");
-#else
-   printf("r\n");
-#endif
-#else
-#if ZERO
-   printf("s\n");
-#else
-   printf("t\n");
-#endif
-#endif
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/41_hashif.expect b/tinyc/tests/tests2/41_hashif.expect
deleted file mode 100644
index 5fd414bd0..000000000
--- a/tinyc/tests/tests2/41_hashif.expect
+++ /dev/null
@@ -1,6 +0,0 @@
-#include test
-b
-g
-i
-p
-r
diff --git a/tinyc/tests/tests2/42_function_pointer.c b/tinyc/tests/tests2/42_function_pointer.c
deleted file mode 100644
index 697bd79a6..000000000
--- a/tinyc/tests/tests2/42_function_pointer.c
+++ /dev/null
@@ -1,22 +0,0 @@
-#include <stdio.h>
-
-int fred(int p)
-{
-   printf("yo %d\n", p);
-   return 42;
-}
-
-int (*f)(int) = &fred;
-
-/* To test what this is supposed to test the destination function
-   (fprint here) must not be called directly anywhere in the test.  */
-int (*fprintfptr)(FILE *, const char *, ...) = &fprintf;
-
-int main()
-{
-   fprintfptr(stdout, "%d\n", (*f)(24));
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/42_function_pointer.expect b/tinyc/tests/tests2/42_function_pointer.expect
deleted file mode 100644
index 6c8b6ce45..000000000
--- a/tinyc/tests/tests2/42_function_pointer.expect
+++ /dev/null
@@ -1,2 +0,0 @@
-yo 24
-42
diff --git a/tinyc/tests/tests2/43_void_param.c b/tinyc/tests/tests2/43_void_param.c
deleted file mode 100644
index de17098b2..000000000
--- a/tinyc/tests/tests2/43_void_param.c
+++ /dev/null
@@ -1,15 +0,0 @@
-#include <stdio.h>
-
-void fred(void)
-{
-   printf("yo\n");
-}
-
-int main()
-{
-   fred();
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/43_void_param.expect b/tinyc/tests/tests2/43_void_param.expect
deleted file mode 100644
index 092bfb9bd..000000000
--- a/tinyc/tests/tests2/43_void_param.expect
+++ /dev/null
@@ -1 +0,0 @@
-yo
diff --git a/tinyc/tests/tests2/44_scoped_declarations.c b/tinyc/tests/tests2/44_scoped_declarations.c
deleted file mode 100644
index f38664fc5..000000000
--- a/tinyc/tests/tests2/44_scoped_declarations.c
+++ /dev/null
@@ -1,17 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   int a;
-
-   for (a = 0; a < 2; a++)
-   {
-      int b = a;
-   }
-
-   printf("it's all good\n");
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/44_scoped_declarations.expect b/tinyc/tests/tests2/44_scoped_declarations.expect
deleted file mode 100644
index 231ccc0c5..000000000
--- a/tinyc/tests/tests2/44_scoped_declarations.expect
+++ /dev/null
@@ -1 +0,0 @@
-it's all good
diff --git a/tinyc/tests/tests2/45_empty_for.c b/tinyc/tests/tests2/45_empty_for.c
deleted file mode 100644
index 7cef513f8..000000000
--- a/tinyc/tests/tests2/45_empty_for.c
+++ /dev/null
@@ -1,18 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   int Count = 0;
-
-   for (;;)
-   {
-      Count++;
-      printf("%d\n", Count);
-      if (Count >= 10)
-         break;
-   }
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/45_empty_for.expect b/tinyc/tests/tests2/45_empty_for.expect
deleted file mode 100644
index f00c965d8..000000000
--- a/tinyc/tests/tests2/45_empty_for.expect
+++ /dev/null
@@ -1,10 +0,0 @@
-1
-2
-3
-4
-5
-6
-7
-8
-9
-10
diff --git a/tinyc/tests/tests2/46_grep.c b/tinyc/tests/tests2/46_grep.c
deleted file mode 100644
index 049dfb15e..000000000
--- a/tinyc/tests/tests2/46_grep.c
+++ /dev/null
@@ -1,568 +0,0 @@
-/*
- * The  information  in  this  document  is  subject  to  change
- * without  notice  and  should not be construed as a commitment
- * by Digital Equipment Corporation or by DECUS.
- *
- * Neither Digital Equipment Corporation, DECUS, nor the authors
- * assume any responsibility for the use or reliability of  this
- * document or the described software.
- *
- *      Copyright (C) 1980, DECUS
- *
- * General permission to copy or modify, but not for profit,  is
- * hereby  granted,  provided that the above copyright notice is
- * included and reference made to  the  fact  that  reproduction
- * privileges were granted by DECUS.
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>	// tolower()
-
-/*
- * grep
- *
- * Runs on the Decus compiler or on vms, On vms, define as:
- *      grep :== "$disk:[account]grep"      (native)
- *      grep :== "$disk:[account]grep grep" (Decus)
- * See below for more information.
- */
-
-char    *documentation[] = {
-   "grep searches a file for a given pattern.  Execute by",
-   "   grep [flags] regular_expression file_list\n",
-   "Flags are single characters preceded by '-':",
-   "   -c      Only a count of matching lines is printed",
-   "   -f      Print file name for matching lines switch, see below",
-   "   -n      Each line is preceded by its line number",
-   "   -v      Only print non-matching lines\n",
-   "The file_list is a list of files (wildcards are acceptable on RSX modes).",
-   "\nThe file name is normally printed if there is a file given.",
-   "The -f flag reverses this action (print name no file, not if more).\n",
-   0 };
-
-char    *patdoc[] = {
-   "The regular_expression defines the pattern to search for.  Upper- and",
-   "lower-case are always ignored.  Blank lines never match.  The expression",
-   "should be quoted to prevent file-name translation.",
-   "x      An ordinary character (not mentioned below) matches that character.",
-   "'\\'    The backslash quotes any character.  \"\\$\" matches a dollar-sign.",
-   "'^'    A circumflex at the beginning of an expression matches the",
-   "       beginning of a line.",
-   "'$'    A dollar-sign at the end of an expression matches the end of a line.",
-   "'.'    A period matches any character except \"new-line\".",
-   "':a'   A colon matches a class of characters described by the following",
-   "':d'     character.  \":a\" matches any alphabetic, \":d\" matches digits,",
-   "':n'     \":n\" matches alphanumerics, \": \" matches spaces, tabs, and",
-   "': '     other control characters, such as new-line.",
-   "'*'    An expression followed by an asterisk matches zero or more",
-   "       occurrences of that expression: \"fo*\" matches \"f\", \"fo\"",
-   "       \"foo\", etc.",
-   "'+'    An expression followed by a plus sign matches one or more",
-   "       occurrences of that expression: \"fo+\" matches \"fo\", etc.",
-   "'-'    An expression followed by a minus sign optionally matches",
-   "       the expression.",
-   "'[]'   A string enclosed in square brackets matches any character in",
-   "       that string, but no others.  If the first character in the",
-   "       string is a circumflex, the expression matches any character",
-   "       except \"new-line\" and the characters in the string.  For",
-   "       example, \"[xyz]\" matches \"xx\" and \"zyx\", while \"[^xyz]\"",
-   "       matches \"abc\" but not \"axb\".  A range of characters may be",
-   "       specified by two characters separated by \"-\".  Note that,",
-   "       [a-z] matches alphabetics, while [z-a] never matches.",
-   "The concatenation of regular expressions is a regular expression.",
-   0};
-
-#define LMAX    512
-#define PMAX    256
-
-#define CHAR    1
-#define BOL     2
-#define EOL     3
-#define ANY     4
-#define CLASS   5
-#define NCLASS  6
-#define STAR    7
-#define PLUS    8
-#define MINUS   9
-#define ALPHA   10
-#define DIGIT   11
-#define NALPHA  12
-#define PUNCT   13
-#define RANGE   14
-#define ENDPAT  15
-
-int cflag=0, fflag=0, nflag=0, vflag=0, nfile=0, debug=0;
-
-char *pp, lbuf[LMAX], pbuf[PMAX];
-
-char *cclass();
-char *pmatch();
-void store(int);
-void error(char *);
-void badpat(char *, char *, char *);
-int match(void);
-
-
-/*** Display a file name *******************************/
-void file(char *s)
-{
-   printf("File %s:\n", s);
-}
-
-/*** Report unopenable file ****************************/
-void cant(char *s)
-{
-   fprintf(stderr, "%s: cannot open\n", s);
-}
-
-/*** Give good help ************************************/
-void help(char **hp)
-{
-   char   **dp;
-
-   for (dp = hp; *dp; ++dp)
-      printf("%s\n", *dp);
-}
-
-/*** Display usage summary *****************************/
-void usage(char *s)
-{
-   fprintf(stderr, "?GREP-E-%s\n", s);
-   fprintf(stderr,
-         "Usage: grep [-cfnv] pattern [file ...].  grep ? for help\n");
-   exit(1);
-}
-
-/*** Compile the pattern into global pbuf[] ************/
-void compile(char *source)
-{
-   char  *s;         /* Source string pointer     */
-   char  *lp;        /* Last pattern pointer      */
-   int   c;          /* Current character         */
-   int            o;          /* Temp                      */
-   char           *spp;       /* Save beginning of pattern */
-
-   s = source;
-   if (debug)
-      printf("Pattern = \"%s\"\n", s);
-   pp = pbuf;
-   while (c = *s++) {
-      /*
-       * STAR, PLUS and MINUS are special.
-       */
-      if (c == '*' || c == '+' || c == '-') {
-         if (pp == pbuf ||
-               (o=pp[-1]) == BOL ||
-               o == EOL ||
-               o == STAR ||
-               o == PLUS ||
-               o == MINUS)
-            badpat("Illegal occurrence op.", source, s);
-         store(ENDPAT);
-         store(ENDPAT);
-         spp = pp;               /* Save pattern end     */
-         while (--pp > lp)       /* Move pattern down    */
-            *pp = pp[-1];        /* one byte             */
-         *pp =   (c == '*') ? STAR :
-            (c == '-') ? MINUS : PLUS;
-         pp = spp;               /* Restore pattern end  */
-         continue;
-      }
-      /*
-       * All the rest.
-       */
-      lp = pp;         /* Remember start       */
-      switch(c) {
-
-         case '^':
-            store(BOL);
-            break;
-
-         case '$':
-            store(EOL);
-            break;
-
-         case '.':
-            store(ANY);
-            break;
-
-         case '[':
-            s = cclass(source, s);
-            break;
-
-         case ':':
-            if (*s) {
-               switch(tolower(c = *s++)) {
-
-                  case 'a':
-                  case 'A':
-                     store(ALPHA);
-                     break;
-
-                  case 'd':
-                  case 'D':
-                     store(DIGIT);
-                     break;
-
-                  case 'n':
-                  case 'N':
-                     store(NALPHA);
-                     break;
-
-                  case ' ':
-                     store(PUNCT);
-                     break;
-
-                  default:
-                     badpat("Unknown : type", source, s);
-
-               }
-               break;
-            }
-            else    badpat("No : type", source, s);
-
-         case '\\':
-            if (*s)
-               c = *s++;
-
-         default:
-            store(CHAR);
-            store(tolower(c));
-      }
-   }
-   store(ENDPAT);
-   store(0);                /* Terminate string     */
-   if (debug) {
-      for (lp = pbuf; lp < pp;) {
-         if ((c = (*lp++ & 0377)) < ' ')
-            printf("\\%o ", c);
-         else    printf("%c ", c);
-      }
-      printf("\n");
-   }
-}
-
-/*** Compile a class (within []) ***********************/
-char *cclass(char *source, char *src)
-   /* char       *source;   // Pattern start -- for error msg. */
-   /* char       *src;      // Class start */
-{
-   char   *s;        /* Source pointer    */
-   char   *cp;       /* Pattern start     */
-   int    c;         /* Current character */
-   int             o;         /* Temp              */
-
-   s = src;
-   o = CLASS;
-   if (*s == '^') {
-      ++s;
-      o = NCLASS;
-   }
-   store(o);
-   cp = pp;
-   store(0);                          /* Byte count      */
-   while ((c = *s++) && c!=']') {
-      if (c == '\\') {                /* Store quoted char    */
-         if ((c = *s++) == '\0')      /* Gotta get something  */
-            badpat("Class terminates badly", source, s);
-         else    store(tolower(c));
-      }
-      else if (c == '-' &&
-            (pp - cp) > 1 && *s != ']' && *s != '\0') {
-         c = pp[-1];             /* Range start     */
-         pp[-1] = RANGE;         /* Range signal    */
-         store(c);               /* Re-store start  */
-         c = *s++;               /* Get end char and*/
-         store(tolower(c));      /* Store it        */
-      }
-      else {
-         store(tolower(c));      /* Store normal char */
-      }
-   }
-   if (c != ']')
-      badpat("Unterminated class", source, s);
-   if ((c = (pp - cp)) >= 256)
-      badpat("Class too large", source, s);
-   if (c == 0)
-      badpat("Empty class", source, s);
-   *cp = c;
-   return(s);
-}
-
-/*** Store an entry in the pattern buffer **************/
-void store(int op)
-{
-   if (pp >= &pbuf[PMAX])
-      error("Pattern too complex\n");
-   *pp++ = op;
-}
-
-/*** Report a bad pattern specification ****************/
-void badpat(char *message, char *source, char *stop)
-   /* char  *message;       // Error message */
-   /* char  *source;        // Pattern start */
-   /* char  *stop;          // Pattern end   */
-{
-   fprintf(stderr, "-GREP-E-%s, pattern is\"%s\"\n", message, source);
-   fprintf(stderr, "-GREP-E-Stopped at byte %ld, '%c'\n",
-         stop-source, stop[-1]);
-   error("?GREP-E-Bad pattern\n");
-}
-
-/*** Scan the file for the pattern in pbuf[] ***********/
-void grep(FILE *fp, char *fn)
-   /* FILE       *fp;       // File to process            */
-   /* char       *fn;       // File name (for -f option)  */
-{
-   int lno, count, m;
-
-   lno = 0;
-   count = 0;
-   while (fgets(lbuf, LMAX, fp)) {
-      ++lno;
-      m = match();
-      if ((m && !vflag) || (!m && vflag)) {
-         ++count;
-         if (!cflag) {
-            if (fflag && fn) {
-               file(fn);
-               fn = 0;
-            }
-            if (nflag)
-               printf("%d\t", lno);
-            printf("%s\n", lbuf);
-         }
-      }
-   }
-   if (cflag) {
-      if (fflag && fn)
-         file(fn);
-      printf("%d\n", count);
-   }
-}
-
-/*** Match line (lbuf) with pattern (pbuf) return 1 if match ***/
-int match()
-{
-   char   *l;        /* Line pointer       */
-
-   for (l = lbuf; *l; ++l) {
-      if (pmatch(l, pbuf))
-         return(1);
-   }
-   return(0);
-}
-
-/*** Match partial line with pattern *******************/
-char *pmatch(char *line, char *pattern)
-   /* char               *line;     // (partial) line to match      */
-   /* char               *pattern;  // (partial) pattern to match   */
-{
-   char   *l;        /* Current line pointer         */
-   char   *p;        /* Current pattern pointer      */
-   char   c;         /* Current character            */
-   char            *e;        /* End for STAR and PLUS match  */
-   int             op;        /* Pattern operation            */
-   int             n;         /* Class counter                */
-   char            *are;      /* Start of STAR match          */
-
-   l = line;
-   if (debug > 1)
-      printf("pmatch(\"%s\")\n", line);
-   p = pattern;
-   while ((op = *p++) != ENDPAT) {
-      if (debug > 1)
-         printf("byte[%ld] = 0%o, '%c', op = 0%o\n",
-               l-line, *l, *l, op);
-      switch(op) {
-
-         case CHAR:
-            if (tolower(*l++) != *p++)
-               return(0);
-            break;
-
-         case BOL:
-            if (l != lbuf)
-               return(0);
-            break;
-
-         case EOL:
-            if (*l != '\0')
-               return(0);
-            break;
-
-         case ANY:
-            if (*l++ == '\0')
-               return(0);
-            break;
-
-         case DIGIT:
-            if ((c = *l++) < '0' || (c > '9'))
-               return(0);
-            break;
-
-         case ALPHA:
-            c = tolower(*l++);
-            if (c < 'a' || c > 'z')
-               return(0);
-            break;
-
-         case NALPHA:
-            c = tolower(*l++);
-            if (c >= 'a' && c <= 'z')
-               break;
-            else if (c < '0' || c > '9')
-               return(0);
-            break;
-
-         case PUNCT:
-            c = *l++;
-            if (c == 0 || c > ' ')
-               return(0);
-            break;
-
-         case CLASS:
-         case NCLASS:
-            c = tolower(*l++);
-            n = *p++ & 0377;
-            do {
-               if (*p == RANGE) {
-                  p += 3;
-                  n -= 2;
-                  if (c >= p[-2] && c <= p[-1])
-                     break;
-               }
-               else if (c == *p++)
-                  break;
-            } while (--n > 1);
-            if ((op == CLASS) == (n <= 1))
-               return(0);
-            if (op == CLASS)
-               p += n - 2;
-            break;
-
-         case MINUS:
-            e = pmatch(l, p);       /* Look for a match    */
-            while (*p++ != ENDPAT); /* Skip over pattern   */
-            if (e)                  /* Got a match?        */
-               l = e;               /* Yes, update string  */
-            break;                  /* Always succeeds     */
-
-         case PLUS:                 /* One or more ...     */
-            if ((l = pmatch(l, p)) == 0)
-               return(0);           /* Gotta have a match  */
-         case STAR:                 /* Zero or more ...    */
-            are = l;                /* Remember line start */
-            while (*l && (e = pmatch(l, p)))
-               l = e;               /* Get longest match   */
-            while (*p++ != ENDPAT); /* Skip over pattern   */
-            while (l >= are) {      /* Try to match rest   */
-               if (e = pmatch(l, p))
-                  return(e);
-               --l;                 /* Nope, try earlier   */
-            }
-            return(0);              /* Nothing else worked */
-
-         default:
-            printf("Bad op code %d\n", op);
-            error("Cannot happen -- match\n");
-      }
-   }
-   return(l);
-}
-
-/*** Report an error ***********************************/
-void error(char *s)
-{
-   fprintf(stderr, "%s", s);
-   exit(1);
-}
-
-/*** Main program - parse arguments & grep *************/
-int main(int argc, char **argv)
-{
-   char   *p;
-   int    c, i;
-   int             gotpattern;
-
-   FILE            *f;
-
-   if (argc <= 1)
-      usage("No arguments");
-   if (argc == 2 && argv[1][0] == '?' && argv[1][1] == 0) {
-      help(documentation);
-      help(patdoc);
-      return 0;
-   }
-   nfile = argc-1;
-   gotpattern = 0;
-   for (i=1; i < argc; ++i) {
-      p = argv[i];
-      if (*p == '-') {
-         ++p;
-         while (c = *p++) {
-            switch(tolower(c)) {
-
-               case '?':
-                  help(documentation);
-                  break;
-
-               case 'C':
-               case 'c':
-                  ++cflag;
-                  break;
-
-               case 'D':
-               case 'd':
-                  ++debug;
-                  break;
-
-               case 'F':
-               case 'f':
-                  ++fflag;
-                  break;
-
-               case 'n':
-               case 'N':
-                  ++nflag;
-                  break;
-
-               case 'v':
-               case 'V':
-                  ++vflag;
-                  break;
-
-               default:
-                  usage("Unknown flag");
-            }
-         }
-         argv[i] = 0;
-         --nfile;
-      } else if (!gotpattern) {
-         compile(p);
-         argv[i] = 0;
-         ++gotpattern;
-         --nfile;
-      }
-   }
-   if (!gotpattern)
-      usage("No pattern");
-   if (nfile == 0)
-      grep(stdin, 0);
-   else {
-      fflag = fflag ^ (nfile > 0);
-      for (i=1; i < argc; ++i) {
-         if (p = argv[i]) {
-            if ((f=fopen(p, "r")) == NULL)
-               cant(p);
-            else {
-               grep(f, p);
-               fclose(f);
-            }
-         }
-      }
-   }
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/46_grep.expect b/tinyc/tests/tests2/46_grep.expect
deleted file mode 100644
index e8a67916b..000000000
--- a/tinyc/tests/tests2/46_grep.expect
+++ /dev/null
@@ -1,3 +0,0 @@
-File 46_grep.c:
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
-
diff --git a/tinyc/tests/tests2/47_switch_return.c b/tinyc/tests/tests2/47_switch_return.c
deleted file mode 100644
index 1ec7924c4..000000000
--- a/tinyc/tests/tests2/47_switch_return.c
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <stdio.h>
-
-void fred(int x)
-{
-   switch (x)
-   {
-      case 1: printf("1\n"); return;
-      case 2: printf("2\n"); break;
-      case 3: printf("3\n"); return;
-   }
-
-   printf("out\n");
-}
-
-int main()
-{
-   fred(1);
-   fred(2);
-   fred(3);
-
-   return 0;
-}    
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/47_switch_return.expect b/tinyc/tests/tests2/47_switch_return.expect
deleted file mode 100644
index b6deb7e9e..000000000
--- a/tinyc/tests/tests2/47_switch_return.expect
+++ /dev/null
@@ -1,4 +0,0 @@
-1
-2
-out
-3
diff --git a/tinyc/tests/tests2/48_nested_break.c b/tinyc/tests/tests2/48_nested_break.c
deleted file mode 100644
index 5bc5ba47f..000000000
--- a/tinyc/tests/tests2/48_nested_break.c
+++ /dev/null
@@ -1,26 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-   int a;
-   char b;
-
-   a = 0;
-   while (a < 2)
-   {
-      printf("%d", a++);
-      break;
-
-      b = 'A';
-      while (b < 'C')
-      {
-         printf("%c", b++);
-      }
-      printf("e");
-   }
-   printf("\n");
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/48_nested_break.expect b/tinyc/tests/tests2/48_nested_break.expect
deleted file mode 100644
index 573541ac9..000000000
--- a/tinyc/tests/tests2/48_nested_break.expect
+++ /dev/null
@@ -1 +0,0 @@
-0
diff --git a/tinyc/tests/tests2/49_bracket_evaluation.c b/tinyc/tests/tests2/49_bracket_evaluation.c
deleted file mode 100644
index 0cbe57d62..000000000
--- a/tinyc/tests/tests2/49_bracket_evaluation.c
+++ /dev/null
@@ -1,23 +0,0 @@
-#include <stdio.h>
-
-struct point
-{
-   double x;
-   double y;
-};
-
-struct point point_array[100];
-
-int main()
-{
-   int my_point = 10;
-
-   point_array[my_point].x = 12.34;
-   point_array[my_point].y = 56.78;
-
-   printf("%f, %f\n", point_array[my_point].x, point_array[my_point].y);
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/49_bracket_evaluation.expect b/tinyc/tests/tests2/49_bracket_evaluation.expect
deleted file mode 100644
index 1da66dbeb..000000000
--- a/tinyc/tests/tests2/49_bracket_evaluation.expect
+++ /dev/null
@@ -1 +0,0 @@
-12.340000, 56.780000
diff --git a/tinyc/tests/tests2/50_logical_second_arg.c b/tinyc/tests/tests2/50_logical_second_arg.c
deleted file mode 100644
index ddec08c87..000000000
--- a/tinyc/tests/tests2/50_logical_second_arg.c
+++ /dev/null
@@ -1,29 +0,0 @@
-#include <stdio.h>
-
-int fred()
-{
-   printf("fred\n");
-   return 0;
-}
-
-int joe()
-{
-   printf("joe\n");
-   return 1;
-}
-
-int main()
-{
-   printf("%d\n", fred() && joe());
-   printf("%d\n", fred() || joe());
-   printf("%d\n", joe() && fred());
-   printf("%d\n", joe() || fred());
-   printf("%d\n", fred() && (1 + joe()));
-   printf("%d\n", fred() || (0 + joe()));
-   printf("%d\n", joe() && (0 + fred()));
-   printf("%d\n", joe() || (1 + fred()));
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/50_logical_second_arg.expect b/tinyc/tests/tests2/50_logical_second_arg.expect
deleted file mode 100644
index d6174ae7d..000000000
--- a/tinyc/tests/tests2/50_logical_second_arg.expect
+++ /dev/null
@@ -1,20 +0,0 @@
-fred
-0
-fred
-joe
-1
-joe
-fred
-0
-joe
-1
-fred
-0
-fred
-joe
-1
-joe
-fred
-0
-joe
-1
diff --git a/tinyc/tests/tests2/51_static.c b/tinyc/tests/tests2/51_static.c
deleted file mode 100644
index d6c0917cf..000000000
--- a/tinyc/tests/tests2/51_static.c
+++ /dev/null
@@ -1,30 +0,0 @@
-#include <stdio.h>
-
-static int fred = 1234;
-static int joe;
-
-void henry()
-{
-   static int fred = 4567;
-
-   printf("%d\n", fred);
-   fred++;
-}
-
-int main()
-{
-   printf("%d\n", fred);
-   henry();
-   henry();
-   henry();
-   henry();
-   printf("%d\n", fred);
-   fred = 8901;
-   joe = 2345;
-   printf("%d\n", fred);
-   printf("%d\n", joe);
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/51_static.expect b/tinyc/tests/tests2/51_static.expect
deleted file mode 100644
index 18224fa3e..000000000
--- a/tinyc/tests/tests2/51_static.expect
+++ /dev/null
@@ -1,8 +0,0 @@
-1234
-4567
-4568
-4569
-4570
-1234
-8901
-2345
diff --git a/tinyc/tests/tests2/52_unnamed_enum.c b/tinyc/tests/tests2/52_unnamed_enum.c
deleted file mode 100644
index d0395b202..000000000
--- a/tinyc/tests/tests2/52_unnamed_enum.c
+++ /dev/null
@@ -1,27 +0,0 @@
-#include <stdio.h>
-
-enum fred { a, b, c };
-
-int main()
-{
-   printf("a=%d\n", a);
-   printf("b=%d\n", b);
-   printf("c=%d\n", c);
-
-   enum fred d;
-
-   typedef enum { e, f, g } h;
-   typedef enum { i, j, k } m;
-
-   printf("e=%d\n", e);
-   printf("f=%d\n", f);
-   printf("g=%d\n", g);
-
-   printf("i=%d\n", i);
-   printf("j=%d\n", j);
-   printf("k=%d\n", k);
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/52_unnamed_enum.expect b/tinyc/tests/tests2/52_unnamed_enum.expect
deleted file mode 100644
index 84f2ac846..000000000
--- a/tinyc/tests/tests2/52_unnamed_enum.expect
+++ /dev/null
@@ -1,9 +0,0 @@
-a=0
-b=1
-c=2
-e=0
-f=1
-g=2
-i=0
-j=1
-k=2
diff --git a/tinyc/tests/tests2/54_goto.c b/tinyc/tests/tests2/54_goto.c
deleted file mode 100644
index 2e151bb76..000000000
--- a/tinyc/tests/tests2/54_goto.c
+++ /dev/null
@@ -1,56 +0,0 @@
-#include <stdio.h>
-
-void fred()
-{
-   printf("In fred()\n");
-   goto done;
-   printf("In middle\n");
-done:
-   printf("At end\n");
-}
-
-void joe()
-{
-   int b = 5678;
-
-   printf("In joe()\n");
-
-   {
-      int c = 1234;
-      printf("c = %d\n", c);
-      goto outer;
-      printf("uh-oh\n");
-   }
-
-outer:    
-
-   printf("done\n");
-}
-
-void henry()
-{
-   int a;
-
-   printf("In henry()\n");
-   goto inner;
-
-   {
-      int b;
-inner:    
-      b = 1234;
-      printf("b = %d\n", b);
-   }
-
-   printf("done\n");
-}
-
-int main()
-{
-   fred();
-   joe();
-   henry();
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/54_goto.expect b/tinyc/tests/tests2/54_goto.expect
deleted file mode 100644
index 8e553fa62..000000000
--- a/tinyc/tests/tests2/54_goto.expect
+++ /dev/null
@@ -1,8 +0,0 @@
-In fred()
-At end
-In joe()
-c = 1234
-done
-In henry()
-b = 1234
-done
diff --git a/tinyc/tests/tests2/55_lshift_type.c b/tinyc/tests/tests2/55_lshift_type.c
deleted file mode 100644
index aa3e51a16..000000000
--- a/tinyc/tests/tests2/55_lshift_type.c
+++ /dev/null
@@ -1,52 +0,0 @@
-/* $Id: lshift-type.c 53089 2012-07-06 11:18:26Z vinc17/ypig $
-
-Tests on left-shift type, written by Vincent Lefevre <vincent@vinc17.net>.
-
-ISO C99 TC3 says: [6.5.7#3] "The integer promotions are performed on
-each of the operands. The type of the result is that of the promoted
-left operand."
-*/
-
-#include <stdio.h>
-
-#define PTYPE(M) ((M) < 0 || -(M) < 0 ? -1 : 1) * (int) sizeof((M)+0)
-#define CHECK(X,T) check(#X, PTYPE(X), PTYPE((X) << (T) 1))
-#define TEST1(X,T) do { CHECK(X,T); CHECK(X,unsigned T); } while (0)
-#define TEST2(X)                 \
-  do                             \
-    {                            \
-      TEST1((X),short);          \
-      TEST1((X),int);            \
-      TEST1((X),long);           \
-      TEST1((X),long long);      \
-    }                            \
-  while (0)
-#define TEST3(X,T) do { TEST2((T)(X)); TEST2((unsigned T)(X)); } while (0)
-#define TEST4(X)                 \
-  do                             \
-    {                            \
-      TEST3((X),short);          \
-      TEST3((X),int);            \
-      TEST3((X),long);           \
-      TEST3((X),long long);      \
-    }                            \
- while (0)
-
-static int debug, nfailed = 0;
-
-static void check (const char *s, int arg1, int shift)
-{
-  int failed = arg1 != shift;
-  if (debug || failed)
-    printf ("%s %d %d\n", s, arg1, shift);
-  nfailed += failed;
-}
-
-int main (int argc, char **argv)
-{
-  debug = argc > 1;
-  TEST4(1);
-  TEST4(-1);
-  printf ("%d test(s) failed\n", nfailed);
-  return nfailed != 0;
-}
diff --git a/tinyc/tests/tests2/55_lshift_type.expect b/tinyc/tests/tests2/55_lshift_type.expect
deleted file mode 100644
index 8523767af..000000000
--- a/tinyc/tests/tests2/55_lshift_type.expect
+++ /dev/null
@@ -1 +0,0 @@
-0 test(s) failed
diff --git a/tinyc/tests/tests2/60_errors_and_warnings.c b/tinyc/tests/tests2/60_errors_and_warnings.c
deleted file mode 100644
index 0028caffb..000000000
--- a/tinyc/tests/tests2/60_errors_and_warnings.c
+++ /dev/null
@@ -1,51 +0,0 @@
-#if defined test_56_btype_excess_1
-struct A {} int i;
-
-#elif defined test_57_btype_excess_2
-char int i;
-
-#elif defined test_58_function_redefinition
-int f(void) { return 0; }
-int f(void) { return 1; }
-
-#elif defined test_global_redefinition
-int xxx = 1;
-int xxx;
-int xxx = 2;
-
-#elif defined test_59_function_array
-int (*fct)[42](int x);
-
-#elif defined test_60_enum_redefinition
-enum color { RED, GREEN, BLUE };
-enum color { R, G, B };
-enum color c;
-
-#elif defined test_62_enumerator_redefinition
-enum color { RED, GREEN, BLUE };
-enum rgb { RED, G, B};
-enum color c = RED;
-
-#elif defined test_63_local_enumerator_redefinition
-enum {
-    FOO,
-    BAR
-};
-
-int main(void)
-{
-    enum {
-        FOO = 2,
-        BAR
-    };
-
-    return BAR - FOO;
-}
-
-#elif defined test_61_undefined_enum
-enum rgb3 c = 42;
-
-#elif defined test_74_non_const_init
-int i = i++;
-
-#endif
diff --git a/tinyc/tests/tests2/60_errors_and_warnings.expect b/tinyc/tests/tests2/60_errors_and_warnings.expect
deleted file mode 100644
index ed6a69019..000000000
--- a/tinyc/tests/tests2/60_errors_and_warnings.expect
+++ /dev/null
@@ -1,28 +0,0 @@
-[test_56_btype_excess_1]
-60_errors_and_warnings.c:2: error: too many basic types
-
-[test_57_btype_excess_2]
-60_errors_and_warnings.c:5: error: too many basic types
-
-[test_58_function_redefinition]
-60_errors_and_warnings.c:9: error: redefinition of 'f'
-
-[test_global_redefinition]
-60_errors_and_warnings.c:14: error: redefinition of 'xxx'
-
-[test_59_function_array]
-60_errors_and_warnings.c:17: error: declaration of an array of functions
-
-[test_60_enum_redefinition]
-60_errors_and_warnings.c:21: error: struct/union/enum already defined
-
-[test_62_enumerator_redefinition]
-60_errors_and_warnings.c:26: error: redefinition of enumerator 'RED'
-
-[test_63_local_enumerator_redefinition]
-
-[test_61_undefined_enum]
-60_errors_and_warnings.c:46: error: unknown type size
-
-[test_74_non_const_init]
-60_errors_and_warnings.c:49: error: initializer element is not constant
diff --git a/tinyc/tests/tests2/64_macro_nesting.c b/tinyc/tests/tests2/64_macro_nesting.c
deleted file mode 100644
index 676e5d3ef..000000000
--- a/tinyc/tests/tests2/64_macro_nesting.c
+++ /dev/null
@@ -1,12 +0,0 @@
-#include <stdio.h>	// printf()
-
-#define CAT2(a,b) a##b
-#define CAT(a,b) CAT2(a,b)
-#define AB(x) CAT(x,y)
-
-int main(void)
-{
-  int xy = 42;
-  printf("%d\n", CAT(A,B)(x));
-  return 0;
-}
diff --git a/tinyc/tests/tests2/64_macro_nesting.expect b/tinyc/tests/tests2/64_macro_nesting.expect
deleted file mode 100644
index d81cc0710..000000000
--- a/tinyc/tests/tests2/64_macro_nesting.expect
+++ /dev/null
@@ -1 +0,0 @@
-42
diff --git a/tinyc/tests/tests2/67_macro_concat.c b/tinyc/tests/tests2/67_macro_concat.c
deleted file mode 100644
index c580d3a64..000000000
--- a/tinyc/tests/tests2/67_macro_concat.c
+++ /dev/null
@@ -1,14 +0,0 @@
-#include <stdio.h>
-
-#define P(A,B) A ## B ; bob
-#define Q(A,B) A ## B+
-
-int main(void)
-{
-    int bob, jim = 21;
-    bob = P(jim,) *= 2;
-    printf("jim: %d, bob: %d\n", jim, bob);
-    jim = 60 Q(+,)3;
-    printf("jim: %d\n", jim);
-    return 0;
-}
diff --git a/tinyc/tests/tests2/67_macro_concat.expect b/tinyc/tests/tests2/67_macro_concat.expect
deleted file mode 100644
index 8386c2d6c..000000000
--- a/tinyc/tests/tests2/67_macro_concat.expect
+++ /dev/null
@@ -1,2 +0,0 @@
-jim: 21, bob: 42
-jim: 63
diff --git a/tinyc/tests/tests2/70_floating_point_literals.c b/tinyc/tests/tests2/70_floating_point_literals.c
deleted file mode 100644
index 012fb4fab..000000000
--- a/tinyc/tests/tests2/70_floating_point_literals.c
+++ /dev/null
@@ -1,77 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-    /* decimal floating constant */
-    float fa0 = .123f;
-    float fa1 = .123E12F;
-    float fa2 = .123e-12f;
-    float fa3 = .123e+12f;
-    printf("%f\n%f\n%f\n%f\n\n", fa0, fa1, fa2, fa3);
-
-    float fb0 = 123.123f;
-    float fb1 = 123.123E12F;
-    float fb2 = 123.123e-12f;
-    float fb3 = 123.123e+12f;
-    printf("%f\n%f\n%f\n%f\n\n", fb0, fb1, fb2, fb3);
-
-    float fc0 = 123.f;
-    float fc1 = 123.E12F;
-    float fc2 = 123.e-12f;
-    float fc3 = 123.e+12f;
-    printf("%f\n%f\n%f\n%f\n\n", fc0, fc1, fc2, fc3);
-
-    float fd0 = 123E12F;
-    float fd1 = 123e-12f;
-    float fd2 = 123e+12f;
-    printf("%f\n%f\n%f\n\n", fd0, fd1, fd2);
-    printf("\n");
-
-    /* hexadecimal floating constant */
-    double da0 = 0X.1ACP12;
-    double da1 = 0x.1acp-12;
-    double da2 = 0x.1acp+12;
-    printf("%f\n%f\n%f\n\n", da0, da1, da2);
-
-    double db0 = 0X1AC.BDP12;
-    double db1 = 0x1ac.bdp-12;
-    double db2 = 0x1ac.dbp+12;
-    printf("%f\n%f\n%f\n\n", db0, db1, db2);
-
-    double dc0 = 0X1AC.P12;
-    double dc1 = 0x1ac.p-12;
-    double dc2 = 0x1ac.p+12;
-    printf("%f\n%f\n%f\n\n", dc0, dc1, dc2);
-
-    double dd0 = 0X1ACP12;
-    double dd1 = 0x1acp-12;
-    double dd2 = 0x1acp+12;
-    printf("%f\n%f\n%f\n\n", dd0, dd1, dd2);
-    printf("\n");
-
-#ifdef __TINYC__
-    /* TCC extension
-       binary floating constant */
-    long double la0 = 0B.110101100P12L;
-    long double la1 = 0b.110101100p-12l;
-    long double la2 = 0b.110101100p+12l;
-    printf("%Lf\n%Lf\n%Lf\n\n", la0, la1, la2);
-
-    long double lb0 = 0B110101100.10111101P12L;
-    long double lb1 = 0b110101100.10111101p-12l;
-    long double lb2 = 0b110101100.10111101p+12l;
-    printf("%Lf\n%Lf\n%Lf\n\n", lb0, lb1, lb2);
-
-    long double lc0 = 0B110101100.P12L;
-    long double lc1 = 0b110101100.p-12l;
-    long double lc2 = 0b110101100.p+12l;
-    printf("%Lf\n%Lf\n%Lf\n\n", lc0, lc1, lc2);
-
-    long double ld0 = 0B110101100P12L;
-    long double ld1 = 0b110101100p-12l;
-    long double ld2 = 0b110101100p+12l;
-    printf("%Lf\n%Lf\n%Lf\n\n", ld0, ld1, ld2);
-#endif
-
-    return 0;
-}
diff --git a/tinyc/tests/tests2/70_floating_point_literals.expect b/tinyc/tests/tests2/70_floating_point_literals.expect
deleted file mode 100644
index 7eb1efb9e..000000000
--- a/tinyc/tests/tests2/70_floating_point_literals.expect
+++ /dev/null
@@ -1,53 +0,0 @@
-0.123000
-122999996416.000000
-0.000000
-122999996416.000000
-
-123.123001
-123122997002240.000000
-0.000000
-123122997002240.000000
-
-123.000000
-123000003231744.000000
-0.000000
-123000003231744.000000
-
-123000003231744.000000
-0.000000
-123000003231744.000000
-
-
-428.000000
-0.000026
-428.000000
-
-1756112.000000
-0.104672
-1756592.000000
-
-1753088.000000
-0.104492
-1753088.000000
-
-1753088.000000
-0.104492
-1753088.000000
-
-
-3424.000000
-0.000204
-3424.000000
-
-1756112.000000
-0.104672
-1756112.000000
-
-1753088.000000
-0.104492
-1753088.000000
-
-1753088.000000
-0.104492
-1753088.000000
-
diff --git a/tinyc/tests/tests2/71_macro_empty_arg.c b/tinyc/tests/tests2/71_macro_empty_arg.c
deleted file mode 100644
index f0d3511b9..000000000
--- a/tinyc/tests/tests2/71_macro_empty_arg.c
+++ /dev/null
@@ -1,9 +0,0 @@
-#include <stdio.h>
-
-#define T(a,b,c) a b c
-
-int main(void)
-{
-    printf("%d", T(1,+,2) T(+,,) T(,2,*) T(,7,) T(,,));
-    return 0;
-}
diff --git a/tinyc/tests/tests2/71_macro_empty_arg.expect b/tinyc/tests/tests2/71_macro_empty_arg.expect
deleted file mode 100644
index 98d9bcb75..000000000
--- a/tinyc/tests/tests2/71_macro_empty_arg.expect
+++ /dev/null
@@ -1 +0,0 @@
-17
diff --git a/tinyc/tests/tests2/72_long_long_constant.c b/tinyc/tests/tests2/72_long_long_constant.c
deleted file mode 100644
index 66082133d..000000000
--- a/tinyc/tests/tests2/72_long_long_constant.c
+++ /dev/null
@@ -1,19 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-    long long int res = 0;
-
-    if (res < -2147483648LL) {
-        printf("Error: 0 < -2147483648\n");
-        return 1;
-    }
-    else
-    if (2147483647LL < res) {
-        printf("Error: 2147483647 < 0\n");
-        return 2;
-    }
-    else
-        printf("long long constant test ok.\n");
-    return 0;
-}
diff --git a/tinyc/tests/tests2/72_long_long_constant.expect b/tinyc/tests/tests2/72_long_long_constant.expect
deleted file mode 100644
index dda9e660d..000000000
--- a/tinyc/tests/tests2/72_long_long_constant.expect
+++ /dev/null
@@ -1 +0,0 @@
-long long constant test ok.
diff --git a/tinyc/tests/tests2/73_arm64.c b/tinyc/tests/tests2/73_arm64.c
deleted file mode 100644
index 8de61b388..000000000
--- a/tinyc/tests/tests2/73_arm64.c
+++ /dev/null
@@ -1,527 +0,0 @@
-// This program is designed to test some arm64-specific things, such as the
-// calling convention, but should give the same results on any architecture.
-
-#include <stdarg.h>
-#include <stdint.h>
-#include <stdio.h>
-
-struct s1 { char x[1]; } s1 = { "0" };
-struct s2 { char x[2]; } s2 = { "12" };
-struct s3 { char x[3]; } s3 = { "345" };
-struct s4 { char x[4]; } s4 = { "6789" };
-struct s5 { char x[5]; } s5 = { "abcde" };
-struct s6 { char x[6]; } s6 = { "fghijk" };
-struct s7 { char x[7]; } s7 = { "lmnopqr" };
-struct s8 { char x[8]; } s8 = { "stuvwxyz" };
-struct s9 { char x[9]; } s9 = { "ABCDEFGHI" };
-struct s10 { char x[10]; } s10 = { "JKLMNOPQRS" };
-struct s11 { char x[11]; } s11 = { "TUVWXYZ0123" };
-struct s12 { char x[12]; } s12 = { "456789abcdef" };
-struct s13 { char x[13]; } s13 = { "ghijklmnopqrs" };
-struct s14 { char x[14]; } s14 = { "tuvwxyzABCDEFG" };
-struct s15 { char x[15]; } s15 = { "HIJKLMNOPQRSTUV" };
-struct s16 { char x[16]; } s16 = { "WXYZ0123456789ab" };
-struct s17 { char x[17]; } s17 = { "cdefghijklmnopqrs" };
-
-struct hfa11 { float a; } hfa11 = { 11.1 };
-struct hfa12 { float a, b; } hfa12 = { 12.1, 12.2 };
-struct hfa13 { float a, b, c; } hfa13 = { 13.1, 13.2, 13.3 };
-struct hfa14 { float a, b, c, d; } hfa14 = { 14.1, 14.2, 14.3, 14.4 };
-
-struct hfa21 { double a; } hfa21 = { 21.1 };
-struct hfa22 { double a, b; } hfa22 = { 22.1, 22.2 };
-struct hfa23 { double a, b, c; } hfa23 = { 23.1, 23.2, 23.3 };
-struct hfa24 { double a, b, c, d; } hfa24 = { 24.1, 24.2, 24.3, 24.4 };
-
-struct hfa31 { long double a; } hfa31 = { 31.1 };
-struct hfa32 { long double a, b; } hfa32 = { 32.1, 32.2 };
-struct hfa33 { long double a, b, c; } hfa33 = { 33.1, 33.2, 33.3 };
-struct hfa34 { long double a, b, c, d; } hfa34 = { 34.1, 34.2, 34.3, 34.4 };
-
-void fa_s1(struct s1 a) { printf("%.1s\n", a.x); }
-void fa_s2(struct s2 a) { printf("%.2s\n", a.x); }
-void fa_s3(struct s3 a) { printf("%.3s\n", a.x); }
-void fa_s4(struct s4 a) { printf("%.4s\n", a.x); }
-void fa_s5(struct s5 a) { printf("%.5s\n", a.x); }
-void fa_s6(struct s6 a) { printf("%.6s\n", a.x); }
-void fa_s7(struct s7 a) { printf("%.7s\n", a.x); }
-void fa_s8(struct s8 a) { printf("%.8s\n", a.x); }
-void fa_s9(struct s9 a) { printf("%.9s\n", a.x); }
-void fa_s10(struct s10 a) { printf("%.10s\n", a.x); }
-void fa_s11(struct s11 a) { printf("%.11s\n", a.x); }
-void fa_s12(struct s12 a) { printf("%.12s\n", a.x); }
-void fa_s13(struct s13 a) { printf("%.13s\n", a.x); }
-void fa_s14(struct s14 a) { printf("%.14s\n", a.x); }
-void fa_s15(struct s15 a) { printf("%.15s\n", a.x); }
-void fa_s16(struct s16 a) { printf("%.16s\n", a.x); }
-void fa_s17(struct s17 a) { printf("%.17s\n", a.x); }
-
-void fa_hfa11(struct hfa11 a)
-{ printf("%.1f\n", a.a); }
-void fa_hfa12(struct hfa12 a)
-{ printf("%.1f %.1f\n", a.a, a.a); }
-void fa_hfa13(struct hfa13 a)
-{ printf("%.1f %.1f %.1f\n", a.a, a.b, a.c); }
-void fa_hfa14(struct hfa14 a)
-{ printf("%.1f %.1f %.1f %.1f\n", a.a, a.b, a.c, a.d); }
-
-void fa_hfa21(struct hfa21 a)
-{ printf("%.1f\n", a.a); }
-void fa_hfa22(struct hfa22 a)
-{ printf("%.1f %.1f\n", a.a, a.a); }
-void fa_hfa23(struct hfa23 a)
-{ printf("%.1f %.1f %.1f\n", a.a, a.b, a.c); }
-void fa_hfa24(struct hfa24 a)
-{ printf("%.1f %.1f %.1f %.1f\n", a.a, a.b, a.c, a.d); }
-
-void fa_hfa31(struct hfa31 a)
-{ printf("%.1Lf\n", a.a); }
-void fa_hfa32(struct hfa32 a)
-{ printf("%.1Lf %.1Lf\n", a.a, a.a); }
-void fa_hfa33(struct hfa33 a)
-{ printf("%.1Lf %.1Lf %.1Lf\n", a.a, a.b, a.c); }
-void fa_hfa34(struct hfa34 a)
-{ printf("%.1Lf %.1Lf %.1Lf %.1Lf\n", a.a, a.b, a.c, a.d); }
-
-void fa1(struct s8 a, struct s9 b, struct s10 c, struct s11 d,
-         struct s12 e, struct s13 f)
-{
-    printf("%.3s %.3s %.3s %.3s %.3s %.3s\n", a.x, b.x, c.x, d.x, e.x, f.x);
-}
-
-void fa2(struct s9 a, struct s10 b, struct s11 c, struct s12 d,
-         struct s13 e, struct s14 f)
-{
-    printf("%.3s %.3s %.3s %.3s %.3s %.3s\n", a.x, b.x, c.x, d.x, e.x, f.x);
-}
-
-void fa3(struct hfa14 a, struct hfa23 b, struct hfa32 c)
-{
-    printf("%.1f %.1f %.1f %.1f %.1Lf %.1Lf\n",
-           a.a, a.d, b.a, b.c, c.a, c.b);
-}
-
-void fa4(struct s1 a, struct hfa14 b, struct s2 c, struct hfa24 d,
-         struct s3 e, struct hfa34 f)
-{
-    printf("%.1s %.1f %.1f %.2s %.1f %.1f %.3s %.1Lf %.1Lf\n",
-           a.x, b.a, b.d, c.x, d.a, d.d, e.x, f.a, f.d);
-}
-
-void arg(void)
-{
-    printf("Arguments:\n");
-    fa_s1(s1);
-    fa_s2(s2);
-    fa_s3(s3);
-    fa_s4(s4);
-    fa_s5(s5);
-    fa_s6(s6);
-    fa_s7(s7);
-    fa_s8(s8);
-    fa_s9(s9);
-    fa_s10(s10);
-    fa_s11(s11);
-    fa_s12(s12);
-    fa_s13(s13);
-    fa_s14(s14);
-    fa_s15(s15);
-    fa_s16(s16);
-    fa_s17(s17);
-    fa_hfa11(hfa11);
-    fa_hfa12(hfa12);
-    fa_hfa13(hfa13);
-    fa_hfa14(hfa14);
-    fa_hfa21(hfa21);
-    fa_hfa22(hfa22);
-    fa_hfa23(hfa23);
-    fa_hfa24(hfa24);
-    fa_hfa31(hfa31);
-    fa_hfa32(hfa32);
-    fa_hfa33(hfa33);
-    fa_hfa34(hfa34);
-    fa1(s8, s9, s10, s11, s12, s13);
-    fa2(s9, s10, s11, s12, s13, s14);
-    fa3(hfa14, hfa23, hfa32);
-    fa4(s1, hfa14, s2, hfa24, s3, hfa34);
-}
-
-struct s1 fr_s1(void) { return s1; }
-struct s2 fr_s2(void) { return s2; }
-struct s3 fr_s3(void) { return s3; }
-struct s4 fr_s4(void) { return s4; }
-struct s5 fr_s5(void) { return s5; }
-struct s6 fr_s6(void) { return s6; }
-struct s7 fr_s7(void) { return s7; }
-struct s8 fr_s8(void) { return s8; }
-struct s9 fr_s9(void) { return s9; }
-struct s10 fr_s10(void) { return s10; }
-struct s11 fr_s11(void) { return s11; }
-struct s12 fr_s12(void) { return s12; }
-struct s13 fr_s13(void) { return s13; }
-struct s14 fr_s14(void) { return s14; }
-struct s15 fr_s15(void) { return s15; }
-struct s16 fr_s16(void) { return s16; }
-struct s17 fr_s17(void) { return s17; }
-
-struct hfa11 fr_hfa11(void) { return hfa11; }
-struct hfa12 fr_hfa12(void) { return hfa12; }
-struct hfa13 fr_hfa13(void) { return hfa13; }
-struct hfa14 fr_hfa14(void) { return hfa14; }
-
-struct hfa21 fr_hfa21(void) { return hfa21; }
-struct hfa22 fr_hfa22(void) { return hfa22; }
-struct hfa23 fr_hfa23(void) { return hfa23; }
-struct hfa24 fr_hfa24(void) { return hfa24; }
-
-struct hfa31 fr_hfa31(void) { return hfa31; }
-struct hfa32 fr_hfa32(void) { return hfa32; }
-struct hfa33 fr_hfa33(void) { return hfa33; }
-struct hfa34 fr_hfa34(void) { return hfa34; }
-
-void ret(void)
-{
-    struct s1 t1 = fr_s1();
-    struct s2 t2 = fr_s2();
-    struct s3 t3 = fr_s3();
-    struct s4 t4 = fr_s4();
-    struct s5 t5 = fr_s5();
-    struct s6 t6 = fr_s6();
-    struct s7 t7 = fr_s7();
-    struct s8 t8 = fr_s8();
-    struct s9 t9 = fr_s9();
-    struct s10 t10 = fr_s10();
-    struct s11 t11 = fr_s11();
-    struct s12 t12 = fr_s12();
-    struct s13 t13 = fr_s13();
-    struct s14 t14 = fr_s14();
-    struct s15 t15 = fr_s15();
-    struct s16 t16 = fr_s16();
-    struct s17 t17 = fr_s17();
-    printf("Return values:\n");
-    printf("%.1s\n", t1.x);
-    printf("%.2s\n", t2.x);
-    printf("%.3s\n", t3.x);
-    printf("%.4s\n", t4.x);
-    printf("%.5s\n", t5.x);
-    printf("%.6s\n", t6.x);
-    printf("%.7s\n", t7.x);
-    printf("%.8s\n", t8.x);
-    printf("%.9s\n", t9.x);
-    printf("%.10s\n", t10.x);
-    printf("%.11s\n", t11.x);
-    printf("%.12s\n", t12.x);
-    printf("%.13s\n", t13.x);
-    printf("%.14s\n", t14.x);
-    printf("%.15s\n", t15.x);
-    printf("%.16s\n", t16.x);
-    printf("%.17s\n", t17.x);
-    printf("%.1f\n", fr_hfa11().a);
-    printf("%.1f %.1f\n", fr_hfa12().a, fr_hfa12().b);
-    printf("%.1f %.1f\n", fr_hfa13().a, fr_hfa13().c);
-    printf("%.1f %.1f\n", fr_hfa14().a, fr_hfa14().d);
-    printf("%.1f\n", fr_hfa21().a);
-    printf("%.1f %.1f\n", fr_hfa22().a, fr_hfa22().b);
-    printf("%.1f %.1f\n", fr_hfa23().a, fr_hfa23().c);
-    printf("%.1f %.1f\n", fr_hfa24().a, fr_hfa24().d);
-    printf("%.1Lf\n", fr_hfa31().a);
-    printf("%.1Lf %.1Lf\n", fr_hfa32().a, fr_hfa32().b);
-    printf("%.1Lf %.1Lf\n", fr_hfa33().a, fr_hfa33().c);
-    printf("%.1Lf %.1Lf\n", fr_hfa34().a, fr_hfa34().d);
-}
-
-int match(const char **s, const char *f)
-{
-    const char *p = *s;
-    for (p = *s; *f && *f == *p; f++, p++)
-        ;
-    if (!*f) {
-        *s = p - 1;
-        return 1;
-    }
-    return 0;
-}
-
-void myprintf(const char *format, ...)
-{
-    const char *s;
-    va_list ap;
-    va_start(ap, format);
-    for (s = format; *s; s++) {
-        if (match(&s, "%7s")) {
-            struct s7 t7 = va_arg(ap, struct s7);
-            printf("%.7s", t7.x);
-        }
-        else if (match(&s, "%9s")) {
-            struct s9 t9 = va_arg(ap, struct s9);
-            printf("%.9s", t9.x);
-        }
-        else if (match(&s, "%hfa11")) {
-            struct hfa11 x = va_arg(ap, struct hfa11);
-            printf("%.1f,%.1f", x.a, x.a);
-        }
-        else if (match(&s, "%hfa12")) {
-            struct hfa12 x = va_arg(ap, struct hfa12);
-            printf("%.1f,%.1f", x.a, x.b);
-        }
-        else if (match(&s, "%hfa13")) {
-            struct hfa13 x = va_arg(ap, struct hfa13);
-            printf("%.1f,%.1f", x.a, x.c);
-        }
-        else if (match(&s, "%hfa14")) {
-            struct hfa14 x = va_arg(ap, struct hfa14);
-            printf("%.1f,%.1f", x.a, x.d);
-        }
-        else if (match(&s, "%hfa21")) {
-            struct hfa21 x = va_arg(ap, struct hfa21);
-            printf("%.1f,%.1f", x.a, x.a);
-        }
-        else if (match(&s, "%hfa22")) {
-            struct hfa22 x = va_arg(ap, struct hfa22);
-            printf("%.1f,%.1f", x.a, x.b);
-        }
-        else if (match(&s, "%hfa23")) {
-            struct hfa23 x = va_arg(ap, struct hfa23);
-            printf("%.1f,%.1f", x.a, x.c);
-        }
-        else if (match(&s, "%hfa24")) {
-            struct hfa24 x = va_arg(ap, struct hfa24);
-            printf("%.1f,%.1f", x.a, x.d);
-        }
-        else if (match(&s, "%hfa31")) {
-            struct hfa31 x = va_arg(ap, struct hfa31);
-            printf("%.1Lf,%.1Lf", x.a, x.a);
-        }
-        else if (match(&s, "%hfa32")) {
-            struct hfa32 x = va_arg(ap, struct hfa32);
-            printf("%.1Lf,%.1Lf", x.a, x.b);
-        }
-        else if (match(&s, "%hfa33")) {
-            struct hfa33 x = va_arg(ap, struct hfa33);
-            printf("%.1Lf,%.1Lf", x.a, x.c);
-        }
-        else if (match(&s, "%hfa34")) {
-            struct hfa34 x = va_arg(ap, struct hfa34);
-            printf("%.1Lf,%.1Lf", x.a, x.d);
-        }
-        else
-            putchar(*s);
-    }
-    putchar('\n');
-}
-
-void stdarg(void)
-{
-    printf("stdarg:\n");
-    myprintf("%9s %9s %9s %9s %9s %9s", s9, s9, s9, s9, s9, s9);
-    myprintf("%7s %9s %9s %9s %9s %9s", s7, s9, s9, s9, s9, s9);
-
-    myprintf("HFA long double:");
-    myprintf("%hfa34 %hfa34 %hfa34 %hfa34", hfa34, hfa34, hfa34, hfa34);
-    myprintf("%hfa33 %hfa34 %hfa34 %hfa34", hfa33, hfa34, hfa34, hfa34);
-    myprintf("%hfa32 %hfa34 %hfa34 %hfa34", hfa32, hfa34, hfa34, hfa34);
-    myprintf("%hfa31 %hfa34 %hfa34 %hfa34", hfa31, hfa34, hfa34, hfa34);
-
-    myprintf("%hfa32 %hfa33 %hfa33 %hfa33 %hfa33",
-             hfa32, hfa33, hfa33, hfa33, hfa33);
-    myprintf("%hfa31 %hfa33 %hfa33 %hfa33 %hfa33",
-             hfa31, hfa33, hfa33, hfa33, hfa33);
-    myprintf("%hfa33 %hfa33 %hfa33 %hfa33",
-             hfa33, hfa33, hfa33, hfa33);
-
-    myprintf("%hfa34 %hfa32 %hfa32 %hfa32 %hfa32",
-             hfa34, hfa32, hfa32, hfa32, hfa32);
-    myprintf("%hfa33 %hfa32 %hfa32 %hfa32 %hfa32",
-             hfa33, hfa32, hfa32, hfa32, hfa32);
-
-    myprintf("%hfa34 %hfa32 %hfa31 %hfa31 %hfa31 %hfa31",
-             hfa34, hfa32, hfa31, hfa31, hfa31, hfa31);
-
-    myprintf("HFA double:");
-    myprintf("%hfa24 %hfa24 %hfa24 %hfa24", hfa24, hfa24, hfa24, hfa24);
-    myprintf("%hfa23 %hfa24 %hfa24 %hfa24", hfa23, hfa24, hfa24, hfa24);
-    myprintf("%hfa22 %hfa24 %hfa24 %hfa24", hfa22, hfa24, hfa24, hfa24);
-    myprintf("%hfa21 %hfa24 %hfa24 %hfa24", hfa21, hfa24, hfa24, hfa24);
-
-    myprintf("%hfa22 %hfa23 %hfa23 %hfa23 %hfa23",
-             hfa22, hfa23, hfa23, hfa23, hfa23);
-    myprintf("%hfa21 %hfa23 %hfa23 %hfa23 %hfa23",
-             hfa21, hfa23, hfa23, hfa23, hfa23);
-    myprintf("%hfa23 %hfa23 %hfa23 %hfa23",
-             hfa23, hfa23, hfa23, hfa23);
-
-    myprintf("%hfa24 %hfa22 %hfa22 %hfa22 %hfa22",
-             hfa24, hfa22, hfa22, hfa22, hfa22);
-    myprintf("%hfa23 %hfa22 %hfa22 %hfa22 %hfa22",
-             hfa23, hfa22, hfa22, hfa22, hfa22);
-
-    myprintf("%hfa24 %hfa22 %hfa21 %hfa21 %hfa21 %hfa21",
-             hfa24, hfa22, hfa21, hfa21, hfa21, hfa21);
-
-    myprintf("HFA float:");
-    myprintf("%hfa14 %hfa14 %hfa14 %hfa14", hfa14, hfa14, hfa14, hfa14);
-    myprintf("%hfa13 %hfa14 %hfa14 %hfa14", hfa13, hfa14, hfa14, hfa14);
-    myprintf("%hfa12 %hfa14 %hfa14 %hfa14", hfa12, hfa14, hfa14, hfa14);
-    myprintf("%hfa11 %hfa14 %hfa14 %hfa14", hfa11, hfa14, hfa14, hfa14);
-
-    myprintf("%hfa12 %hfa13 %hfa13 %hfa13 %hfa13",
-             hfa12, hfa13, hfa13, hfa13, hfa13);
-    myprintf("%hfa11 %hfa13 %hfa13 %hfa13 %hfa13",
-             hfa11, hfa13, hfa13, hfa13, hfa13);
-    myprintf("%hfa13 %hfa13 %hfa13 %hfa13",
-             hfa13, hfa13, hfa13, hfa13);
-
-    myprintf("%hfa14 %hfa12 %hfa12 %hfa12 %hfa12",
-             hfa14, hfa12, hfa12, hfa12, hfa12);
-    myprintf("%hfa13 %hfa12 %hfa12 %hfa12 %hfa12",
-             hfa13, hfa12, hfa12, hfa12, hfa12);
-
-    myprintf("%hfa14 %hfa12 %hfa11 %hfa11 %hfa11 %hfa11",
-             hfa14, hfa12, hfa11, hfa11, hfa11, hfa11);
-}
-
-void pll(unsigned long long x)
-{
-    printf("%llx\n", x);
-}
-
-void movi(void)
-{
-    printf("MOVI:\n");
-    pll(0);
-    pll(0xabcd);
-    pll(0xabcd0000);
-    pll(0xabcd00000000);
-    pll(0xabcd000000000000);
-    pll(0xffffabcd);
-    pll(0xabcdffff);
-    pll(0xffffffffffffabcd);
-    pll(0xffffffffabcdffff);
-    pll(0xffffabcdffffffff);
-    pll(0xabcdffffffffffff);
-    pll(0xaaaaaaaa);
-    pll(0x5555555555555555);
-    pll(0x77777777);
-    pll(0x3333333333333333);
-    pll(0xf8f8f8f8);
-    pll(0x1e1e1e1e1e1e1e1e);
-    pll(0x3f803f80);
-    pll(0x01ff01ff01ff01ff);
-    pll(0x007fffc0);
-    pll(0x03fff80003fff800);
-    pll(0x0007fffffffffe00);
-
-    pll(0xabcd1234);
-    pll(0xabcd00001234);
-    pll(0xabcd000000001234);
-    pll(0xabcd12340000);
-    pll(0xabcd000012340000);
-    pll(0xabcd123400000000);
-    pll(0xffffffffabcd1234);
-    pll(0xffffabcdffff1234);
-    pll(0xabcdffffffff1234);
-    pll(0xffffabcd1234ffff);
-    pll(0xabcdffff1234ffff);
-    pll(0xabcd1234ffffffff);
-
-    pll(0xffffef0123456789);
-    pll(0xabcdef012345ffff);
-
-    pll(0xabcdef0123456789);
-}
-
-static uint32_t addip0(uint32_t x) { return x + 0; }
-static uint64_t sublp0(uint64_t x) { return x - 0; }
-static uint32_t addip123(uint32_t x) { return x + 123; }
-static uint64_t addlm123(uint64_t x) { return x + -123; }
-static uint64_t sublp4095(uint64_t x) { return x - 4095; }
-static uint32_t subim503808(uint32_t x) { return x - -503808; }
-static uint64_t addp12345(uint64_t x) { return x + 12345; }
-static uint32_t subp12345(uint32_t x) { return x - 12345; }
-
-static uint32_t mvni(uint32_t x) { return 0xffffffff - x; }
-static uint64_t negl(uint64_t x) { return 0 - x; }
-static uint32_t rsbi123(uint32_t x) { return 123 - x; }
-static uint64_t rsbl123(uint64_t x) { return 123 - x; }
-
-static uint32_t andi0(uint32_t x) { return x & 0; }
-static uint64_t andlm1(uint64_t x) { return x & -1; }
-static uint64_t orrl0(uint64_t x) { return x | 0; }
-static uint32_t orrim1(uint32_t x) { return x | -1; }
-static uint32_t eori0(uint32_t x) { return x ^ 0; }
-static uint64_t eorlm1(uint64_t x) { return x ^ -1; }
-static uint32_t and0xf0(uint32_t x) { return x & 0xf0; }
-static uint64_t orr0xf0(uint64_t x) { return x | 0xf0; }
-static uint64_t eor0xf0(uint64_t x) { return x ^ 0xf0; }
-
-static uint32_t lsli0(uint32_t x) { return x << 0; }
-static uint32_t lsri0(uint32_t x) { return x >> 0; }
-static int64_t asrl0(int64_t x) { return x >> 0; }
-static uint32_t lsli1(uint32_t x) { return x << 1; }
-static uint32_t lsli31(uint32_t x) { return x << 31; }
-static uint64_t lsll1(uint64_t x) { return x << 1; }
-static uint64_t lsll63(uint64_t x) { return x << 63; }
-static uint32_t lsri1(uint32_t x) { return x >> 1; }
-static uint32_t lsri31(uint32_t x) { return x >> 31; }
-static uint64_t lsrl1(uint64_t x) { return x >> 1; }
-static uint64_t lsrl63(uint64_t x) { return x >> 63; }
-static int32_t asri1(int32_t x) { return x >> 1; }
-static int32_t asri31(int32_t x) { return x >> 31; }
-static int64_t asrl1(int64_t x) { return x >> 1; }
-static int64_t asrl63(int64_t x) { return x >> 63; }
-
-void opi(void)
-{
-    int x = 1000;
-    pll(addip0(x));
-    pll(sublp0(x));
-    pll(addip123(x));
-    pll(addlm123(x));
-    pll(sublp4095(x));
-    pll(subim503808(x));
-    pll(addp12345(x));
-    pll(subp12345(x));
-    pll(mvni(x));
-    pll(negl(x));
-    pll(rsbi123(x));
-    pll(rsbl123(x));
-    pll(andi0(x));
-    pll(andlm1(x));
-    pll(orrl0(x));
-    pll(orrim1(x));
-    pll(eori0(x));
-    pll(eorlm1(x));
-    pll(and0xf0(x));
-    pll(orr0xf0(x));
-    pll(eor0xf0(x));
-    pll(lsli0(x));
-    pll(lsri0(x));
-    pll(asrl0(x));
-    pll(lsli1(x));
-    pll(lsli31(x));
-    pll(lsll1(x));
-    pll(lsll63(x));
-    pll(lsri1(x));
-    pll(lsri31(x));
-    pll(lsrl1(x));
-    pll(lsrl63(x));
-    pll(asri1(x));
-    pll(asri31(x));
-    pll(asrl1(x));
-    pll(asrl63(x));
-}
-
-void pcs(void)
-{
-    arg();
-    ret();
-    stdarg();
-    movi();
-    opi();
-}
-
-int main()
-{
-    pcs();
-    return 0;
-}
diff --git a/tinyc/tests/tests2/73_arm64.expect b/tinyc/tests/tests2/73_arm64.expect
deleted file mode 100644
index 7bdebd307..000000000
--- a/tinyc/tests/tests2/73_arm64.expect
+++ /dev/null
@@ -1,174 +0,0 @@
-Arguments:
-0
-12
-345
-6789
-abcde
-fghijk
-lmnopqr
-stuvwxyz
-ABCDEFGHI
-JKLMNOPQRS
-TUVWXYZ0123
-456789abcdef
-ghijklmnopqrs
-tuvwxyzABCDEFG
-HIJKLMNOPQRSTUV
-WXYZ0123456789ab
-cdefghijklmnopqrs
-11.1
-12.1 12.1
-13.1 13.2 13.3
-14.1 14.2 14.3 14.4
-21.1
-22.1 22.1
-23.1 23.2 23.3
-24.1 24.2 24.3 24.4
-31.1
-32.1 32.1
-33.1 33.2 33.3
-34.1 34.2 34.3 34.4
-stu ABC JKL TUV 456 ghi
-ABC JKL TUV 456 ghi tuv
-14.1 14.4 23.1 23.3 32.1 32.2
-0 14.1 14.4 12 24.1 24.4 345 34.1 34.4
-Return values:
-0
-12
-345
-6789
-abcde
-fghijk
-lmnopqr
-stuvwxyz
-ABCDEFGHI
-JKLMNOPQRS
-TUVWXYZ0123
-456789abcdef
-ghijklmnopqrs
-tuvwxyzABCDEFG
-HIJKLMNOPQRSTUV
-WXYZ0123456789ab
-cdefghijklmnopqrs
-11.1
-12.1 12.2
-13.1 13.3
-14.1 14.4
-21.1
-22.1 22.2
-23.1 23.3
-24.1 24.4
-31.1
-32.1 32.2
-33.1 33.3
-34.1 34.4
-stdarg:
-ABCDEFGHI ABCDEFGHI ABCDEFGHI ABCDEFGHI ABCDEFGHI ABCDEFGHI
-lmnopqr ABCDEFGHI ABCDEFGHI ABCDEFGHI ABCDEFGHI ABCDEFGHI
-HFA long double:
-34.1,34.4 34.1,34.4 34.1,34.4 34.1,34.4
-33.1,33.3 34.1,34.4 34.1,34.4 34.1,34.4
-32.1,32.2 34.1,34.4 34.1,34.4 34.1,34.4
-31.1,31.1 34.1,34.4 34.1,34.4 34.1,34.4
-32.1,32.2 33.1,33.3 33.1,33.3 33.1,33.3 33.1,33.3
-31.1,31.1 33.1,33.3 33.1,33.3 33.1,33.3 33.1,33.3
-33.1,33.3 33.1,33.3 33.1,33.3 33.1,33.3
-34.1,34.4 32.1,32.2 32.1,32.2 32.1,32.2 32.1,32.2
-33.1,33.3 32.1,32.2 32.1,32.2 32.1,32.2 32.1,32.2
-34.1,34.4 32.1,32.2 31.1,31.1 31.1,31.1 31.1,31.1 31.1,31.1
-HFA double:
-24.1,24.4 24.1,24.4 24.1,24.4 24.1,24.4
-23.1,23.3 24.1,24.4 24.1,24.4 24.1,24.4
-22.1,22.2 24.1,24.4 24.1,24.4 24.1,24.4
-21.1,21.1 24.1,24.4 24.1,24.4 24.1,24.4
-22.1,22.2 23.1,23.3 23.1,23.3 23.1,23.3 23.1,23.3
-21.1,21.1 23.1,23.3 23.1,23.3 23.1,23.3 23.1,23.3
-23.1,23.3 23.1,23.3 23.1,23.3 23.1,23.3
-24.1,24.4 22.1,22.2 22.1,22.2 22.1,22.2 22.1,22.2
-23.1,23.3 22.1,22.2 22.1,22.2 22.1,22.2 22.1,22.2
-24.1,24.4 22.1,22.2 21.1,21.1 21.1,21.1 21.1,21.1 21.1,21.1
-HFA float:
-14.1,14.4 14.1,14.4 14.1,14.4 14.1,14.4
-13.1,13.3 14.1,14.4 14.1,14.4 14.1,14.4
-12.1,12.2 14.1,14.4 14.1,14.4 14.1,14.4
-11.1,11.1 14.1,14.4 14.1,14.4 14.1,14.4
-12.1,12.2 13.1,13.3 13.1,13.3 13.1,13.3 13.1,13.3
-11.1,11.1 13.1,13.3 13.1,13.3 13.1,13.3 13.1,13.3
-13.1,13.3 13.1,13.3 13.1,13.3 13.1,13.3
-14.1,14.4 12.1,12.2 12.1,12.2 12.1,12.2 12.1,12.2
-13.1,13.3 12.1,12.2 12.1,12.2 12.1,12.2 12.1,12.2
-14.1,14.4 12.1,12.2 11.1,11.1 11.1,11.1 11.1,11.1 11.1,11.1
-MOVI:
-0
-abcd
-abcd0000
-abcd00000000
-abcd000000000000
-ffffabcd
-abcdffff
-ffffffffffffabcd
-ffffffffabcdffff
-ffffabcdffffffff
-abcdffffffffffff
-aaaaaaaa
-5555555555555555
-77777777
-3333333333333333
-f8f8f8f8
-1e1e1e1e1e1e1e1e
-3f803f80
-1ff01ff01ff01ff
-7fffc0
-3fff80003fff800
-7fffffffffe00
-abcd1234
-abcd00001234
-abcd000000001234
-abcd12340000
-abcd000012340000
-abcd123400000000
-ffffffffabcd1234
-ffffabcdffff1234
-abcdffffffff1234
-ffffabcd1234ffff
-abcdffff1234ffff
-abcd1234ffffffff
-ffffef0123456789
-abcdef012345ffff
-abcdef0123456789
-3e8
-3e8
-463
-36d
-fffffffffffff3e9
-7b3e8
-3421
-ffffd3af
-fffffc17
-fffffffffffffc18
-fffffc93
-fffffffffffffc93
-0
-3e8
-3e8
-ffffffff
-3e8
-fffffffffffffc17
-e0
-3f8
-318
-3e8
-3e8
-3e8
-7d0
-0
-7d0
-0
-1f4
-0
-1f4
-0
-1f4
-0
-1f4
-0
diff --git a/tinyc/tests/tests2/75_array_in_struct_init.c b/tinyc/tests/tests2/75_array_in_struct_init.c
deleted file mode 100644
index 234e3c4fa..000000000
--- a/tinyc/tests/tests2/75_array_in_struct_init.c
+++ /dev/null
@@ -1,33 +0,0 @@
-#include <stdio.h>
-
-/* This test is a snippet from the J interpreter */
-
-typedef long I;
-typedef struct{I c[4];I b,e,k;} PT;
-
-PT cases[] = {
- ((I)4194304L +(I)2097152L +(I)67108864L), (I)262144L, (((I)1L +(I)256L +(I)4L +(I)8L +(I)16L +(I)64L +(I)128L +(I)268435456L +(I)536870912L +(I)1024L +(I)4096L +(I)8192L +(I)16384L)+((I)2L +(I)131072L +(I)2048L)+(I)32L +(I)32768L +(I)65536L), -1L, 1,2,1,
- ((I)+4194304L +(I)2097152L +(I)67108864L)+( (I)524288L +(I)262144L +(((I)1L +(I)256L +(I)4L +(I)8L +(I)16L +(I)64L +(I)128L +(I)268435456L +(I)536870912L +(I)1024L +(I)4096L +(I)8192L +(I)16384L)+((I)2L +(I)131072L +(I)2048L)+(I)32L +(I)32768L +(I)65536L)), (I)262144L, (I)262144L, (((I)1L +(I)256L +(I)4L +(I)8L +(I)16L +(I)64L +(I)128L +(I)268435456L +(I)536870912L +(I)1024L +(I)4096L +(I)8192L +(I)16384L)+((I)2L +(I)131072L +(I)2048L)+(I)32L +(I)32768L +(I)65536L), 2,3,2,
- ((I)4194304L +(I)2097152L +(I)67108864L)+( (I)524288L +(I)262144L +(((I)1L +(I)256L +(I)4L +(I)8L +(I)16L +(I)64L +(I)128L +(I)268435456L +(I)536870912L +(I)1024L +(I)4096L +(I)8192L +(I)16384L)+((I)2L +(I)131072L +(I)2048L)+(I)32L +(I)32768L +(I)65536L)), (((I)1L +(I)256L +(I)4L +(I)8L +(I)16L +(I)64L +(I)128L +(I)268435456L +(I)536870912L +(I)1024L +(I)4096L +(I)8192L +(I)16384L)+((I)2L +(I)131072L +(I)2048L)+(I)32L +(I)32768L +(I)65536L), (I)262144L, (((I)1L +(I)256L +(I)4L +(I)8L +(I)16L +(I)64L +(I)128L +(I)268435456L +(I)536870912L +(I)1024L +(I)4096L +(I)8192L +(I)16384L)+((I)2L +(I)131072L +(I)2048L)+(I)32L +(I)32768L +(I)65536L), 1,3,2,
- ((I)4194304L +(I)2097152L +(I)67108864L)+( (I)524288L +(I)262144L +(((I)1L +(I)256L +(I)4L +(I)8L +(I)16L +(I)64L +(I)128L +(I)268435456L +(I)536870912L +(I)1024L +(I)4096L +(I)8192L +(I)16384L)+((I)2L +(I)131072L +(I)2048L)+(I)32L +(I)32768L +(I)65536L)), (I)262144L +(((I)1L +(I)256L +(I)4L +(I)8L +(I)16L +(I)64L +(I)128L +(I)268435456L +(I)536870912L +(I)1024L +(I)4096L +(I)8192L +(I)16384L)+((I)2L +(I)131072L +(I)2048L)+(I)32L +(I)32768L +(I)65536L), (I)524288L, -1L, 1,2,1,
- ((I)4194304L +(I)2097152L +(I)67108864L)+( (I)524288L +(I)262144L +(((I)1L +(I)256L +(I)4L +(I)8L +(I)16L +(I)64L +(I)128L +(I)268435456L +(I)536870912L +(I)1024L +(I)4096L +(I)8192L +(I)16384L)+((I)2L +(I)131072L +(I)2048L)+(I)32L +(I)32768L +(I)65536L)), (I)262144L +(((I)1L +(I)256L +(I)4L +(I)8L +(I)16L +(I)64L +(I)128L +(I)268435456L +(I)536870912L +(I)1024L +(I)4096L +(I)8192L +(I)16384L)+((I)2L +(I)131072L +(I)2048L)+(I)32L +(I)32768L +(I)65536L), (I)1048576L, (I)262144L +(((I)1L +(I)256L +(I)4L +(I)8L +(I)16L +(I)64L +(I)128L +(I)268435456L +(I)536870912L +(I)1024L +(I)4096L +(I)8192L +(I)16384L)+((I)2L +(I)131072L +(I)2048L)+(I)32L +(I)32768L +(I)65536L), 1,3,1,
- ((I)4194304L +(I)2097152L +(I)67108864L)+( (I)524288L +(I)262144L +(((I)1L +(I)256L +(I)4L +(I)8L +(I)16L +(I)64L +(I)128L +(I)268435456L +(I)536870912L +(I)1024L +(I)4096L +(I)8192L +(I)16384L)+((I)2L +(I)131072L +(I)2048L)+(I)32L +(I)32768L +(I)65536L)), (I)262144L +(((I)1L +(I)256L +(I)4L +(I)8L +(I)16L +(I)64L +(I)128L +(I)268435456L +(I)536870912L +(I)1024L +(I)4096L +(I)8192L +(I)16384L)+((I)2L +(I)131072L +(I)2048L)+(I)32L +(I)32768L +(I)65536L), (I)262144L, (I)262144L, 1,3,1,
- ((I)4194304L +(I)2097152L +(I)67108864L), ((I)1048576L +(I)524288L +(I)262144L +(((I)1L +(I)256L +(I)4L +(I)8L +(I)16L +(I)64L +(I)128L +(I)268435456L +(I)536870912L +(I)1024L +(I)4096L +(I)8192L +(I)16384L)+((I)2L +(I)131072L +(I)2048L)+(I)32L +(I)32768L +(I)65536L)), ((I)1048576L +(I)524288L +(I)262144L +(((I)1L +(I)256L +(I)4L +(I)8L +(I)16L +(I)64L +(I)128L +(I)268435456L +(I)536870912L +(I)1024L +(I)4096L +(I)8192L +(I)16384L)+((I)2L +(I)131072L +(I)2048L)+(I)32L +(I)32768L +(I)65536L)), -1L, 1,2,1,
- (I)33554432L +(((I)1L +(I)256L +(I)4L +(I)8L +(I)16L +(I)64L +(I)128L +(I)268435456L +(I)536870912L +(I)1024L +(I)4096L +(I)8192L +(I)16384L)+((I)2L +(I)131072L +(I)2048L)+(I)32L +(I)32768L +(I)65536L), (I)2097152L, ((I)1048576L +(I)524288L +(I)262144L +(((I)1L +(I)256L +(I)4L +(I)8L +(I)16L +(I)64L +(I)128L +(I)268435456L +(I)536870912L +(I)1024L +(I)4096L +(I)8192L +(I)16384L)+((I)2L +(I)131072L +(I)2048L)+(I)32L +(I)32768L +(I)65536L)), -1L, 0,2,1,
- (I)67108864L, ((I)1048576L +(I)524288L +(I)262144L +(((I)1L +(I)256L +(I)4L +(I)8L +(I)16L +(I)64L +(I)128L +(I)268435456L +(I)536870912L +(I)1024L +(I)4096L +(I)8192L +(I)16384L)+((I)2L +(I)131072L +(I)2048L)+(I)32L +(I)32768L +(I)65536L)), (I)134217728L, -1L, 0,2,0,
-};
-
-int main() {
-    int i, j;
-
-    for(j=0; j < sizeof(cases)/sizeof(cases[0]); j++) {
-	for(i=0; i < sizeof(cases->c)/sizeof(cases->c[0]); i++)
-	    printf("cases[%d].c[%d]=%ld\n", j, i, cases[j].c[i]);
-
-	printf("cases[%d].b=%ld\n", j, cases[j].b);
-	printf("cases[%d].e=%ld\n", j, cases[j].e);
-	printf("cases[%d].k=%ld\n", j, cases[j].k);
-	printf("\n");
-    }
-    return 0;
-}
diff --git a/tinyc/tests/tests2/75_array_in_struct_init.expect b/tinyc/tests/tests2/75_array_in_struct_init.expect
deleted file mode 100644
index 2b75aa59d..000000000
--- a/tinyc/tests/tests2/75_array_in_struct_init.expect
+++ /dev/null
@@ -1,72 +0,0 @@
-cases[0].c[0]=73400320
-cases[0].c[1]=262144
-cases[0].c[2]=805567999
-cases[0].c[3]=-1
-cases[0].b=1
-cases[0].e=2
-cases[0].k=1
-
-cases[1].c[0]=879754751
-cases[1].c[1]=262144
-cases[1].c[2]=262144
-cases[1].c[3]=805567999
-cases[1].b=2
-cases[1].e=3
-cases[1].k=2
-
-cases[2].c[0]=879754751
-cases[2].c[1]=805567999
-cases[2].c[2]=262144
-cases[2].c[3]=805567999
-cases[2].b=1
-cases[2].e=3
-cases[2].k=2
-
-cases[3].c[0]=879754751
-cases[3].c[1]=805830143
-cases[3].c[2]=524288
-cases[3].c[3]=-1
-cases[3].b=1
-cases[3].e=2
-cases[3].k=1
-
-cases[4].c[0]=879754751
-cases[4].c[1]=805830143
-cases[4].c[2]=1048576
-cases[4].c[3]=805830143
-cases[4].b=1
-cases[4].e=3
-cases[4].k=1
-
-cases[5].c[0]=879754751
-cases[5].c[1]=805830143
-cases[5].c[2]=262144
-cases[5].c[3]=262144
-cases[5].b=1
-cases[5].e=3
-cases[5].k=1
-
-cases[6].c[0]=73400320
-cases[6].c[1]=807403007
-cases[6].c[2]=807403007
-cases[6].c[3]=-1
-cases[6].b=1
-cases[6].e=2
-cases[6].k=1
-
-cases[7].c[0]=839122431
-cases[7].c[1]=2097152
-cases[7].c[2]=807403007
-cases[7].c[3]=-1
-cases[7].b=0
-cases[7].e=2
-cases[7].k=1
-
-cases[8].c[0]=67108864
-cases[8].c[1]=807403007
-cases[8].c[2]=134217728
-cases[8].c[3]=-1
-cases[8].b=0
-cases[8].e=2
-cases[8].k=0
-
diff --git a/tinyc/tests/tests2/76_dollars_in_identifiers.c b/tinyc/tests/tests2/76_dollars_in_identifiers.c
deleted file mode 100644
index c5fcf99e0..000000000
--- a/tinyc/tests/tests2/76_dollars_in_identifiers.c
+++ /dev/null
@@ -1,41 +0,0 @@
-#include <stdio.h>
-
-#define $(x) x
-#define $fred 10
-#define joe$ 20
-#define hen$y 30
-
-#define $10(x) x*10
-#define _$10(x) x/10
-
-int main()
-{
-   printf("fred=%d\n", $fred);
-   printf("joe=%d\n", joe$);
-   printf("henry=%d\n", hen$y);
-
-   printf("fred2=%d\n", $($fred));
-   printf("joe2=%d\n", $(joe$));
-   printf("henry2=%d\n", $(hen$y));
-
-   printf("fred10=%d\n", $10($fred));
-   printf("joe_10=%d\n", _$10(joe$));
-
-   int $ = 10;
-   int a100$ = 100;
-   int a$$ = 1000;
-   int a$c$b = 2121;
-   int $100 = 10000;
-   const char *$$$ = "money";
-
-   printf("local=%d\n", $);
-   printf("a100$=%d\n", a100$);
-   printf("a$$=%d\n", a$$);
-   printf("a$c$b=%d\n", a$c$b);
-   printf("$100=%d\n", $100);
-   printf("$$$=%s", $$$);
-
-   return 0;
-}
-
-/* vim: set expandtab ts=4 sw=3 sts=3 tw=80 :*/
diff --git a/tinyc/tests/tests2/76_dollars_in_identifiers.expect b/tinyc/tests/tests2/76_dollars_in_identifiers.expect
deleted file mode 100644
index 4a20a52e6..000000000
--- a/tinyc/tests/tests2/76_dollars_in_identifiers.expect
+++ /dev/null
@@ -1,14 +0,0 @@
-fred=10
-joe=20
-henry=30
-fred2=10
-joe2=20
-henry2=30
-fred10=100
-joe_10=2
-local=10
-a100$=100
-a$$=1000
-a$c$b=2121
-$100=10000
-$$$=money
diff --git a/tinyc/tests/tests2/77_push_pop_macro.c b/tinyc/tests/tests2/77_push_pop_macro.c
deleted file mode 100644
index d38e0bfe3..000000000
--- a/tinyc/tests/tests2/77_push_pop_macro.c
+++ /dev/null
@@ -1,30 +0,0 @@
-#include <stdio.h>
-
-int main()
-{
-    /* must not affect how #pragma ppop_macro works */
-    #define pop_macro foobar1
-
-    /* must not affect how #pragma push_macro works */
-    #define push_macro foobar2
-
-    #undef abort
-    #define abort "111"
-    printf("abort = %s\n", abort);
-
-    #pragma push_macro("abort")
-    #undef abort
-    #define abort "222"
-    printf("abort = %s\n", abort);
-
-    #pragma push_macro("abort")
-    #undef abort
-    #define abort "333"
-    printf("abort = %s\n", abort);
-
-    #pragma pop_macro("abort")
-    printf("abort = %s\n", abort);
-
-    #pragma pop_macro("abort")
-    printf("abort = %s\n", abort);
-}
diff --git a/tinyc/tests/tests2/77_push_pop_macro.expect b/tinyc/tests/tests2/77_push_pop_macro.expect
deleted file mode 100644
index d8a55305a..000000000
--- a/tinyc/tests/tests2/77_push_pop_macro.expect
+++ /dev/null
@@ -1,5 +0,0 @@
-abort = 111
-abort = 222
-abort = 333
-abort = 222
-abort = 111
diff --git a/tinyc/tests/tests2/78_vla_label.c b/tinyc/tests/tests2/78_vla_label.c
deleted file mode 100644
index 4096495d9..000000000
--- a/tinyc/tests/tests2/78_vla_label.c
+++ /dev/null
@@ -1,45 +0,0 @@
-#include <stdio.h>
-
-/* This test segfaults as of April 27, 2015. */
-void f1(int argc)
-{
-  char test[argc];
-  if(0)
-  label:
-    printf("boom!\n");
-  if(argc-- == 0)
-    return;
-  goto label;
-}
-
-/* This segfaulted on 2015-11-19. */
-void f2(void)
-{
-    goto start;
-    {
-        int a[1 && 1]; /* not a variable-length array */
-        int b[1 || 1]; /* not a variable-length array */
-        int c[1 ? 1 : 1]; /* not a variable-length array */
-    start:
-        a[0] = 0;
-        b[0] = 0;
-        c[0] = 0;
-    }
-}
-
-void f3(void)
-{
-    printf("%d\n", 0 ? printf("x1\n") : 11);
-    printf("%d\n", 1 ? 12 : printf("x2\n"));
-    printf("%d\n", 0 && printf("x3\n"));
-    printf("%d\n", 1 || printf("x4\n"));
-}
-
-int main()
-{
-  f1(2);
-  f2();
-  f3();
-
-  return 0;
-}
diff --git a/tinyc/tests/tests2/78_vla_label.expect b/tinyc/tests/tests2/78_vla_label.expect
deleted file mode 100644
index 3f4063bec..000000000
--- a/tinyc/tests/tests2/78_vla_label.expect
+++ /dev/null
@@ -1,6 +0,0 @@
-boom!
-boom!
-11
-12
-0
-1
diff --git a/tinyc/tests/tests2/79_vla_continue.c b/tinyc/tests/tests2/79_vla_continue.c
deleted file mode 100644
index 91215c99e..000000000
--- a/tinyc/tests/tests2/79_vla_continue.c
+++ /dev/null
@@ -1,116 +0,0 @@
-#include <stdio.h>
-
-int f(void)
-{
-  return 5;
-}
-
-void test1()
-{
-  int count = 10;
-  void *addr[10];
-  for(;count--;) {
-    int a[f()];
-
-    addr[count] = a;
-
-    continue;
-  }
-
-  if(addr[9] == addr[0]) {
-    printf("OK\n");
-  } else {
-    printf("NOT OK\n");
-  }
-}
-
-void test2()
-{
-  int count = 10;
-  void *addr[count];
-  for(;count--;) {
-    int a[f()];
-
-    addr[count] = a;
-
-    continue;
-  }
-
-  if(addr[9] == addr[0]) {
-    printf("OK\n");
-  } else {
-    printf("NOT OK\n");
-  }
-}
-
-void test3()
-{
-  int count = 10;
-  void *addr[count];
-  while(count--) {
-    int a[f()];
-
-    addr[count] = a;
-
-    continue;
-  }
-
-  if(addr[9] == addr[0]) {
-    printf("OK\n");
-  } else {
-    printf("NOT OK\n");
-  }
-}
-
-void test4()
-{
-  int count = 10;
-  void *addr[count];
-  do {
-    int a[f()];
-
-    addr[--count] = a;
-
-    continue;
-  } while (count);
-
-  if(addr[9] == addr[0]) {
-    printf("OK\n");
-  } else {
-    printf("NOT OK\n");
-  }
-}
-
-void test5()
-{
-  int count = 10;
-  int a[f()];
-  int c[f()];
-
-  c[0] = 42;
-
-  for(;count--;) {
-    int b[f()];
-    int i;
-    for (i=0; i<f(); i++) {
-      b[i] = count;
-    }
-  }
-
-  if (c[0] == 42) {
-    printf("OK\n");
-  } else {
-    printf("NOT OK\n");
-  }
-}
-
-int main(void)
-{
-  test1();
-  test2();
-  test3();
-  test4();
-  test5();
-
-  return 0;
-}
diff --git a/tinyc/tests/tests2/79_vla_continue.expect b/tinyc/tests/tests2/79_vla_continue.expect
deleted file mode 100644
index 21da4d2be..000000000
--- a/tinyc/tests/tests2/79_vla_continue.expect
+++ /dev/null
@@ -1,5 +0,0 @@
-OK
-OK
-OK
-OK
-OK
diff --git a/tinyc/tests/tests2/80_flexarray.c b/tinyc/tests/tests2/80_flexarray.c
deleted file mode 100644
index 1fc1a60a7..000000000
--- a/tinyc/tests/tests2/80_flexarray.c
+++ /dev/null
@@ -1,25 +0,0 @@
-#include <stdio.h>
-struct wchar {
-    char *data; char mem[];
-};
-struct wint {
-    char *data; int mem[];
-};
-int f1char (void) {
-    char s[9]="nonono";
-    struct wchar q = {"bugs"};
-    return !s[0];
-}
-int f1int (void) {
-    char s[9]="nonono";
-    struct wint q = {"bugs"};
-    return !s[0];
-}
-int main (void) {
-   char s[9]="nonono";
-   static struct wchar q = {"bugs", {'c'}};
-   //printf ("tcc has %s %s\n", s, q.data);
-   if (f1char() || f1int())
-     printf ("bla\n");
-   return !s[0];
-}
diff --git a/tinyc/tests/tests2/81_types.c b/tinyc/tests/tests2/81_types.c
deleted file mode 100644
index fd6d71b0c..000000000
--- a/tinyc/tests/tests2/81_types.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/* The following are all valid decls, even though some subtypes
-   are incomplete.  */
-enum E *e;
-const enum E *e1;
-enum E const *e2;
-struct S *s;
-const struct S *s1;
-struct S const *s2;
-
-/* Various strangely looking declarators, which are all valid
-   and have to map to the same numbered typedefs. */
-typedef int (*fptr1)();
-int f1 (int (), int);
-typedef int (*fptr2)(int x);
-int f2 (int (int x), int);
-typedef int (*fptr3)(int);
-int f3 (int (int), int);
-typedef int (*fptr4[4])(int);
-int f4 (int (*[4])(int), int);
-typedef int (*fptr5)(fptr1);
-int f5 (int (int()), fptr1);
-int f1 (fptr1 fp, int i)
-{
-  return (*fp)(i);
-}
-int f2 (fptr2 fp, int i)
-{
-  return (*fp)(i);
-}
-int f3 (fptr3 fp, int i)
-{
-  return (*fp)(i);
-}
-int f4 (fptr4 fp, int i)
-{
-  return (*fp[i])(i);
-}
-int f5 (fptr5 fp, fptr1 i)
-{
-  return fp(i);
-}
-int f8 (int ([4]), int);
-int main () { return 0; }
diff --git a/tinyc/tests/tests2/82_attribs_position.c b/tinyc/tests/tests2/82_attribs_position.c
deleted file mode 100644
index 7c9f98726..000000000
--- a/tinyc/tests/tests2/82_attribs_position.c
+++ /dev/null
@@ -1,19 +0,0 @@
-typedef unsigned short uint16_t;
-typedef unsigned char uint8_t;
-
-typedef union Unaligned16a {
-  uint16_t u;
-  uint8_t b[2];
-} __attribute__((packed)) Unaligned16a;
-
-typedef union __attribute__((packed)) Unaligned16b {
-  uint16_t u;
-  uint8_t b[2];
-} Unaligned16b;
-
-extern void foo (void) __attribute__((stdcall));
-void __attribute__((stdcall)) foo (void)
-{
-}
-
-int main () { return 0; }
diff --git a/tinyc/tests/tests2/83_utf8_in_identifiers.c b/tinyc/tests/tests2/83_utf8_in_identifiers.c
deleted file mode 100644
index 1f8609525..000000000
--- a/tinyc/tests/tests2/83_utf8_in_identifiers.c
+++ /dev/null
@@ -1,9 +0,0 @@
-#include <stdio.h>
-double привет=0.1;
-int Lefèvre=2;
-int main(){
-    printf("привет=%g\n",привет);
-    printf("Lefèvre=%d\n",Lefèvre);
-    return 0;
-}
-// pcc & tcc only
diff --git a/tinyc/tests/tests2/83_utf8_in_identifiers.expect b/tinyc/tests/tests2/83_utf8_in_identifiers.expect
deleted file mode 100644
index 1553f5f6f..000000000
--- a/tinyc/tests/tests2/83_utf8_in_identifiers.expect
+++ /dev/null
@@ -1,2 +0,0 @@
-привет=0.1
-Lefèvre=2
diff --git a/tinyc/tests/tests2/84_hex-float.c b/tinyc/tests/tests2/84_hex-float.c
deleted file mode 100644
index 0ef09bfc2..000000000
--- a/tinyc/tests/tests2/84_hex-float.c
+++ /dev/null
@@ -1,12 +0,0 @@
-extern int printf(const char *format, ...);
-
-#define ACPI_TYPE_INVALID       0x1E
-#define NUM_NS_TYPES            ACPI_TYPE_INVALID+1
-int array[NUM_NS_TYPES];
-
-#define n 0xe
-int main()
-{
-    printf("n+1 = %d\n", n+1);
-//    printf("n+1 = %d\n", 0xe+1);
-}
diff --git a/tinyc/tests/tests2/84_hex-float.expect b/tinyc/tests/tests2/84_hex-float.expect
deleted file mode 100644
index 2175385aa..000000000
--- a/tinyc/tests/tests2/84_hex-float.expect
+++ /dev/null
@@ -1 +0,0 @@
-n+1 = 15
diff --git a/tinyc/tests/tests2/85_asm-outside-function.c b/tinyc/tests/tests2/85_asm-outside-function.c
deleted file mode 100644
index dc5639a46..000000000
--- a/tinyc/tests/tests2/85_asm-outside-function.c
+++ /dev/null
@@ -1,9 +0,0 @@
-extern int printf (const char *, ...);
-extern void vide(void);
-__asm__("vide: ret");
-
-int main() {
-    vide();
-    printf ("okay\n");
-    return 0;
-}
diff --git a/tinyc/tests/tests2/85_asm-outside-function.expect b/tinyc/tests/tests2/85_asm-outside-function.expect
deleted file mode 100644
index dcf02b2fb..000000000
--- a/tinyc/tests/tests2/85_asm-outside-function.expect
+++ /dev/null
@@ -1 +0,0 @@
-okay
diff --git a/tinyc/tests/tests2/86_memory-model.c b/tinyc/tests/tests2/86_memory-model.c
deleted file mode 100644
index 744c3e207..000000000
--- a/tinyc/tests/tests2/86_memory-model.c
+++ /dev/null
@@ -1,38 +0,0 @@
-#include <stdio.h>
-
-int
-main()
-{
-#if defined(__LLP64__)
-	if (sizeof(short) == 2
-	    && sizeof(int) == 4
-	    && sizeof(long int) == 4
-	    && sizeof(long long int) == 8
-	    && sizeof(void*) == 8) {
-		(void)printf("Ok\n");
-	} else {
-		(void)printf("KO __LLP64__\n");
-	}
-#elif defined(__LP64__)
-	if (sizeof(short) == 2
-	    && sizeof(int) == 4
-	    && sizeof(long int) == 8
-	    && sizeof(long long int) == 8
-	    && sizeof(void*) == 8) {
-		(void)printf("Ok\n");
-	} else {
-		(void)printf("KO __LP64__\n");
-	}
-#elif defined(__ILP32__)
-	if (sizeof(short) == 2
-	    && sizeof(int) == 4
-	    && sizeof(long int) == 4
-	    && sizeof(void*) == 4) {
-		(void)printf("Ok\n");
-	} else {
-		(void)printf("KO __ILP32__\n");
-	}
-#else
-	(void)printf("KO no __*LP*__ defined.\n");
-#endif
-}
diff --git a/tinyc/tests/tests2/86_memory-model.expect b/tinyc/tests/tests2/86_memory-model.expect
deleted file mode 100644
index 7326d9603..000000000
--- a/tinyc/tests/tests2/86_memory-model.expect
+++ /dev/null
@@ -1 +0,0 @@
-Ok
diff --git a/tinyc/tests/tests2/87_dead_code.c b/tinyc/tests/tests2/87_dead_code.c
deleted file mode 100644
index 98d4566c5..000000000
--- a/tinyc/tests/tests2/87_dead_code.c
+++ /dev/null
@@ -1,122 +0,0 @@
-/* This checks various ways of dead code inside if statements
-   where there are non-obvious ways of how the code is actually
-   not dead due to reachable by labels.  */
-extern int printf (const char *, ...);
-static void kb_wait_1(void)
-{
-  unsigned long timeout = 2;
-  do {
-      /* Here the else arm is a statement expression that's supposed
-         to be suppressed.  The label inside the while would unsuppress
-	 code generation again if not handled correctly.  And that
-	 would wreak havoc to the cond-expression because there's no
-	 jump-around emitted, the whole statement expression really
-	 needs to not generate code (perhaps except useless forward jumps).  */
-      (1 ? 
-       printf("timeout=%ld\n", timeout) :
-       ({
-	int i = 1;
-	while (1)
-	  while (i--)
-	    some_label:
-	      printf("error\n");
-	goto some_label;
-	})
-      );
-      timeout--;
-  } while (timeout);
-}
-int main (void)
-{
-  int i = 1;
-  kb_wait_1();
-
-  /* Simple test of dead code at first sight which isn't actually dead. */
-  if (0) {
-yeah:
-      printf ("yeah\n");
-  } else {
-      printf ("boo\n");
-  }
-  if (i--)
-    goto yeah;
-
-  /* Some more non-obvious uses where the problems are loops, so that even
-     the first loop statements aren't actually dead.  */
-  i = 1;
-  if (0) {
-      while (i--) {
-	  printf ("once\n");
-enterloop:
-	  printf ("twice\n");
-      }
-  }
-  if (i >= 0)
-    goto enterloop;
-
-  /* The same with statement expressions.  One might be tempted to
-     handle them specially by counting if inside statement exprs and
-     not unsuppressing code at loops at all then.
-     See kb_wait_1 for the other side of the medal where that wouldn't work.  */
-  i = ({
-      int j = 1;
-      if (0) {
-	  while (j--) {
-	      printf ("SEonce\n");
-    enterexprloop:
-	      printf ("SEtwice\n");
-	  }
-      }
-      if (j >= 0)
-	goto enterexprloop;
-      j; });
-
-  /* The other two loop forms: */
-  i = 1;
-  if (0) {
-      for (i = 1; i--;) {
-	  printf ("once2\n");
-enterloop2:
-	  printf ("twice2\n");
-      }
-  }
-  if (i > 0)
-    goto enterloop2;
-
-  i = 1;
-  if (0) {
-      do {
-	  printf ("once3\n");
-enterloop3:
-	  printf ("twice3\n");
-      } while (i--);
-  }
-  if (i > 0)
-    goto enterloop3;
-
-  /* And check that case and default labels have the same effect
-     of disabling code suppression.  */
-  i = 41;
-  switch (i) {
-      if (0) {
-	  printf ("error\n");
-      case 42:
-	  printf ("error2\n");
-      case 41:
-	  printf ("caseok\n");
-      }
-  }
-
-  i = 41;
-  switch (i) {
-      if (0) {
-	  printf ("error3\n");
-      default:
-	  printf ("caseok2\n");
-	  break;
-      case 42:
-	  printf ("error4\n");
-      }
-  }
-  return 0;
-}
diff --git a/tinyc/tests/tests2/87_dead_code.expect b/tinyc/tests/tests2/87_dead_code.expect
deleted file mode 100644
index 0b3ec1d9d..000000000
--- a/tinyc/tests/tests2/87_dead_code.expect
+++ /dev/null
@@ -1,18 +0,0 @@
-timeout=2
-timeout=1
-boo
-yeah
-twice
-once
-twice
-SEtwice
-SEonce
-SEtwice
-twice2
-once2
-twice2
-twice3
-once3
-twice3
-caseok
-caseok2
diff --git a/tinyc/tests/tests2/88_codeopt.c b/tinyc/tests/tests2/88_codeopt.c
deleted file mode 100644
index 647626f9e..000000000
--- a/tinyc/tests/tests2/88_codeopt.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/* Check some way in where code suppression caused various
-   miscompilations.  */
-extern int printf (const char *, ...);
-typedef unsigned long size_t;
-
-size_t _brk_start, _brk_end;
-void * extend_brk(size_t size, size_t align)
-{
-    size_t mask = align - 1;
-    void *ret = 0;
-
-     do {
-	 if (__builtin_expect(!!(_brk_start == 0), 0))
-	   do {
-	       printf("wrong1\n");
-	   } while (0);
-     } while (0);
-     _brk_end = (_brk_end + mask) & ~mask;
-     ret = (void *)_brk_end;
-     _brk_end += size;
-
-     return ret;
-}
-
-static void get_args (int a, int b)
-{
-  if (a != 1)
-    printf("wrong2\n");
-  else
-    printf("okay\n");
-}
-
-void bla(void)
-{
-  int __ret = 42;
-  ({
-    if (__builtin_expect(!!(0), 0)) {
-      if (__builtin_expect(!!__ret, 0))
-        printf("wrong3\n");
-      int x = !!(__ret);
-    }
-    __ret;
-  });
-  get_args(!!__ret, sizeof(__ret));
-}
-
-_Bool chk(unsigned long addr, unsigned long limit, unsigned long size)
-{
-  _Bool ret;
-  /* This just needs to compile, no runtime test.  (And it doesn't compile
-     only with certain internal checking added that's not committed).  */
-  if (0)
-    ret = 0 != (!!(addr > limit - size));
-}
-
-int main()
-{
-  void *r;
-  _brk_start = 1024;
-  _brk_end = 1024;
-  r = extend_brk (4096, 16);
-  if (!r)
-    printf("wrong4\n");
-  else
-    printf("okay\n");
-  bla();
-  return 0;
-}
diff --git a/tinyc/tests/tests2/88_codeopt.expect b/tinyc/tests/tests2/88_codeopt.expect
deleted file mode 100644
index 439edfd8f..000000000
--- a/tinyc/tests/tests2/88_codeopt.expect
+++ /dev/null
@@ -1,2 +0,0 @@
-okay
-okay
diff --git a/tinyc/tests/tests2/89_nocode_wanted.c b/tinyc/tests/tests2/89_nocode_wanted.c
deleted file mode 100644
index 73e0a4bc8..000000000
--- a/tinyc/tests/tests2/89_nocode_wanted.c
+++ /dev/null
@@ -1,112 +0,0 @@
-extern int printf(const char *format, ...);
-static void kb_wait_1(void)
-{
-    unsigned long timeout = 2;
-    do {
-        (1 ?
-            printf("timeout=%ld\n", timeout) :
-            ({
-                while (1)
-                    printf("error\n");
-            })
-        );
-        timeout--;
-    } while (timeout);
-}
-static void kb_wait_2(void)
-{
-    unsigned long timeout = 2;
-    do {
-        (1 ?
-            printf("timeout=%ld\n", timeout) :
-            ({
-                for (;;)
-                    printf("error\n");
-            })
-        );
-        timeout--;
-    } while (timeout);
-}
-static void kb_wait_2_1(void)
-{
-    unsigned long timeout = 2;
-    do {
-        (1 ?
-            printf("timeout=%ld\n", timeout) :
-            ({
-                do {
-                    printf("error\n");
-		} while (1);
-            })
-        );
-        timeout--;
-    } while (timeout);
-}
-static void kb_wait_2_2(void)
-{
-    unsigned long timeout = 2;
-    do {
-        (1 ?
-            printf("timeout=%ld\n", timeout) :
-            ({
-                label:
-                    printf("error\n");
-		goto label;
-            })
-        );
-        timeout--;
-    } while (timeout);
-}
-static void kb_wait_3(void)
-{
-    unsigned long timeout = 2;
-    do {
-        (1 ?
-            printf("timeout=%ld\n", timeout) :
-            ({
-                int i = 1;
-                goto label;
-                i = i + 2;
-            label:
-                i = i + 3;
-            })
-        );
-        timeout--;
-    } while (timeout);
-}
-static void kb_wait_4(void)
-{
-    unsigned long timeout = 2;
-    do {
-        (1 ?
-            printf("timeout=%ld\n", timeout) :
-            ({
-                switch(timeout) {
-                    case 2:
-                        printf("timeout is 2");
-                        break;
-                    case 1:
-                        printf("timeout is 1");
-                        break;
-                    default:
-                        printf("timeout is 0?");
-                        break;
-                };
-                // return;
-            })
-        );
-        timeout--;
-    } while (timeout);
-}
-int main()
-{
-    printf("begin\n");
-    kb_wait_1();
-    kb_wait_2();
-    kb_wait_2_1();
-    kb_wait_2_2();
-    kb_wait_3();
-    kb_wait_4();
-    printf("end\n");
-    return 0;
-}
diff --git a/tinyc/tests/tests2/89_nocode_wanted.expect b/tinyc/tests/tests2/89_nocode_wanted.expect
deleted file mode 100644
index c44d4ea6c..000000000
--- a/tinyc/tests/tests2/89_nocode_wanted.expect
+++ /dev/null
@@ -1,14 +0,0 @@
-begin
-timeout=2
-timeout=1
-timeout=2
-timeout=1
-timeout=2
-timeout=1
-timeout=2
-timeout=1
-timeout=2
-timeout=1
-timeout=2
-timeout=1
-end
diff --git a/tinyc/tests/tests2/90_struct-init.c b/tinyc/tests/tests2/90_struct-init.c
deleted file mode 100644
index d931e2378..000000000
--- a/tinyc/tests/tests2/90_struct-init.c
+++ /dev/null
@@ -1,282 +0,0 @@
-typedef unsigned char u8;
-typedef struct {} empty_s;
-struct contains_empty {
-    u8 a;
-    empty_s empty;
-    u8 b;
-};
-struct contains_empty ce = { { (1) }, (empty_s){}, 022, };
-/* The following decl of 'q' would demonstrate the TCC bug in init_putv when
-   handling copying compound literals.  (Compound literals
-   aren't acceptable constant initializers in isoc99, but
-   we accept them like gcc, except for this case)
-//char *q = (char *){ "trara" }; */
-struct SS {u8 a[3], b; };
-struct SS sinit16[] = { { 1 }, 2 };
-struct S
-{
-  u8 a,b;
-  u8 c[2];
-};
-
-struct T
-{
-  u8 s[16];
-  u8 a;
-};
-
-struct U
-{
-  u8 a;
-  struct S s;
-  u8 b;
-  struct T t;
-};
-
-struct V
-{
-  struct S s;
-  struct T t;
-  u8 a;
-};
-
-struct W
-{
-  struct V t;
-  struct S s[];
-};
-
-struct S gs = ((struct S){1, 2, 3, 4});
-struct S gs2 = {1, 2, {3, 4}};
-struct T gt = {"hello", 42};
-struct U gu = {3, 5,6,7,8, 4, "huhu", 43};
-struct U gu2 = {3, {5,6,7,8}, 4, {"huhu", 43}};
-/* Optional braces around scalar initializers.  Accepted, but with
-   a warning.  */
-struct U gu3 = { {3}, {5,6,7,8,}, 4, {"huhu", 43}};
-/* Many superfluous braces and leaving out one initializer for U.s.c[1] */
-struct U gu4 = { 3, {5,6,7,},  5, { "bla", {44}} };
-/* Superfluous braces and useless parens around values */
-struct S gs3 = { (1), {(2)}, {(((3))), {4}}};
-/* Superfluous braces, and leaving out braces for V.t, plus cast */
-struct V gv = {{{3},4,{5,6}}, "haha", (u8)45, 46};
-/* Compound literal */
-struct V gv2 = {(struct S){7,8,{9,10}}, {"hihi", 47}, 48};
-/* Parens around compound literal */
-struct V gv3 = {((struct S){7,8,{9,10}}), {"hoho", 49}, 50};
-/* Initialization of a flex array member (warns in GCC) */
-struct W gw = {{1,2,3,4}, {1,2,3,4,5}};
-
-union UU {
-    u8 a;
-    u8 b;
-};
-struct SU {
-    union UU u;
-    u8 c;
-};
-struct SU gsu = {5,6};
-
-/* Unnamed struct/union members aren't ISO C, but it's a widely accepted
-   extension.  See below for further extensions to that under -fms-extension.*/
-union UV {
-    struct {u8 a,b;};
-    struct S s;
-};
-union UV guv = {{6,5}};
-union UV guv2 = {{.b = 7, .a = 8}};
-union UV guv3 = {.b = 8, .a = 7};
-
-/* Under -fms-extensions also the following is valid:
-union UV2 {
-    struct Anon {u8 a,b;};    // unnamed member, but tagged struct, ...
-    struct S s;
-};
-struct Anon gan = { 10, 11 }; // ... which makes it available here.
-union UV2 guv4 = {{4,3}};     // and the other inits from above as well
-*/
-
-struct in6_addr {
-    union {
-	u8 u6_addr8[16];
-	unsigned short u6_addr16[8];
-    } u;
-};
-struct flowi6 {
-    struct in6_addr saddr, daddr;
-};
-struct pkthdr {
-    struct in6_addr daddr, saddr;
-};
-struct pkthdr phdr = { { { 6,5,4,3 } }, { { 9,8,7,6 } } };
-
-struct Wrap {
-    void *func;
-};
-int global;
-void inc_global (void)
-{
-  global++;
-}
-
-struct Wrap global_wrap[] = {
-    ((struct Wrap) {inc_global}),
-    inc_global,
-};
-
-#include <stdio.h>
-void print_ (const char *name, const u8 *p, long size)
-{
-  printf ("%s:", name);
-  while (size--) {
-      printf (" %x", *p++);
-  }
-  printf ("\n");
-}
-#define print(x) print_(#x, (u8*)&x, sizeof (x))
-#if 1
-void foo (struct W *w, struct pkthdr *phdr_)
-{
-  struct S ls = {1, 2, 3, 4};
-  struct S ls2 = {1, 2, {3, 4}};
-  struct T lt = {"hello", 42};
-  struct U lu = {3, 5,6,7,8, 4, "huhu", 43};
-  struct U lu1 = {3, ls, 4, {"huhu", 43}};
-  struct U lu2 = {3, (ls), 4, {"huhu", 43}};
-  const struct S *pls = &ls;
-  struct S ls21 = *pls;
-  struct U lu22 = {3, *pls, 4, {"huhu", 43}};
-  /* Incomplete bracing.  */
-  struct U lu21 = {3, ls, 4, "huhu", 43};
-  /* Optional braces around scalar initializers.  Accepted, but with
-     a warning.  */
-  struct U lu3 = { 3, {5,6,7,8,}, 4, {"huhu", 43}};
-  /* Many superfluous braces and leaving out one initializer for U.s.c[1] */
-  struct U lu4 = { 3, {5,6,7,},  5, { "bla", 44} };
-  /* Superfluous braces and useless parens around values */
-  struct S ls3 = { (1), (2), {(((3))), 4}};
-  /* Superfluous braces, and leaving out braces for V.t, plus cast */
-  struct V lv = {{3,4,{5,6}}, "haha", (u8)45, 46};
-  /* Compound literal */
-  struct V lv2 = {(struct S)w->t.s, {"hihi", 47}, 48};
-  /* Parens around compound literal */
-  struct V lv3 = {((struct S){7,8,{9,10}}), ((const struct W *)w)->t.t, 50};
-  const struct pkthdr *phdr = phdr_;
-  struct flowi6 flow = { .daddr = phdr->daddr, .saddr = phdr->saddr };
-  int elt = 0x42;
-  /* Range init, overlapping */
-  struct T lt2 = { { [1 ... 5] = 9, [6 ... 10] = elt, [4 ... 7] = elt+1 }, 1 };
-  print(ls);
-  print(ls2);
-  print(lt);
-  print(lu);
-  print(lu1);
-  print(lu2);
-  print(ls21);
-  print(lu21);
-  print(lu22);
-  print(lu3);
-  print(lu4);
-  print(ls3);
-  print(lv);
-  print(lv2);
-  print(lv3);
-  print(lt2);
-  print(flow);
-}
-#endif
-
-void test_compound_with_relocs (void)
-{
-  struct Wrap local_wrap[] = {
-      ((struct Wrap) {inc_global}),
-      inc_global,
-  };
-  void (*p)(void);
-  p = global_wrap[0].func; p();
-  p = global_wrap[1].func; p();
-  p = local_wrap[0].func; p();
-  p = local_wrap[1].func; p();
-}
-
-void sys_ni(void) { printf("ni\n"); }
-void sys_one(void) { printf("one\n"); }
-void sys_two(void) { printf("two\n"); }
-void sys_three(void) { printf("three\n"); }
-typedef void (*fptr)(void);
-const fptr table[3] = {
-    [0 ... 2] = &sys_ni,
-    [0] = sys_one,
-    [1] = sys_two,
-    [2] = sys_three,
-};
-
-void test_multi_relocs(void)
-{
-  int i;
-  for (i = 0; i < sizeof(table)/sizeof(table[0]); i++)
-    table[i]();
-}
-
-/* Following is from GCC gcc.c-torture/execute/20050613-1.c.  */
-
-struct SEA { int i; int j; int k; int l; };
-struct SEB { struct SEA a; int r[1]; };
-struct SEC { struct SEA a; int r[0]; };
-struct SED { struct SEA a; int r[]; };
-
-static void
-test_correct_filling (struct SEA *x)
-{
-  static int i;
-  if (x->i != 0 || x->j != 5 || x->k != 0 || x->l != 0)
-    printf("sea_fill%d: wrong\n", i);
-  else
-    printf("sea_fill%d: okay\n", i);
-  i++;
-}
-
-int
-test_zero_init (void)
-{
-  /* The peculiarity here is that only a.j is initialized.  That
-     means that all other members must be zero initialized.  TCC
-     once didn't do that for sub-level designators.  */
-  struct SEB b = { .a.j = 5 };
-  struct SEC c = { .a.j = 5 };
-  struct SED d = { .a.j = 5 };
-  test_correct_filling (&b.a);
-  test_correct_filling (&c.a);
-  test_correct_filling (&d.a);
-  return 0;
-}
-
-int main()
-{
-  print(ce);
-  print(gs);
-  print(gs2);
-  print(gt);
-  print(gu);
-  print(gu2);
-  print(gu3);
-  print(gu4);
-  print(gs3);
-  print(gv);
-  print(gv2);
-  print(gv3);
-  print(sinit16);
-  print(gw);
-  print(gsu);
-  print(guv);
-  print(guv.b);
-  print(guv2);
-  print(guv3);
-  print(phdr);
-  foo(&gw, &phdr);
-  //printf("q: %s\n", q);
-  test_compound_with_relocs();
-  test_multi_relocs();
-  test_zero_init();
-  return 0;
-}
diff --git a/tinyc/tests/tests2/90_struct-init.expect b/tinyc/tests/tests2/90_struct-init.expect
deleted file mode 100644
index e366121a5..000000000
--- a/tinyc/tests/tests2/90_struct-init.expect
+++ /dev/null
@@ -1,43 +0,0 @@
-ce: 1 12
-gs: 1 2 3 4
-gs2: 1 2 3 4
-gt: 68 65 6c 6c 6f 0 0 0 0 0 0 0 0 0 0 0 2a
-gu: 3 5 6 7 8 4 68 75 68 75 0 0 0 0 0 0 0 0 0 0 0 0 2b
-gu2: 3 5 6 7 8 4 68 75 68 75 0 0 0 0 0 0 0 0 0 0 0 0 2b
-gu3: 3 5 6 7 8 4 68 75 68 75 0 0 0 0 0 0 0 0 0 0 0 0 2b
-gu4: 3 5 6 7 0 5 62 6c 61 0 0 0 0 0 0 0 0 0 0 0 0 0 2c
-gs3: 1 2 3 4
-gv: 3 4 5 6 68 61 68 61 0 0 0 0 0 0 0 0 0 0 0 0 2d 2e
-gv2: 7 8 9 a 68 69 68 69 0 0 0 0 0 0 0 0 0 0 0 0 2f 30
-gv3: 7 8 9 a 68 6f 68 6f 0 0 0 0 0 0 0 0 0 0 0 0 31 32
-sinit16: 1 0 0 0 2 0 0 0
-gw: 1 2 3 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-gsu: 5 6
-guv: 6 5 0 0
-guv.b: 5
-guv2: 8 7 0 0
-guv3: 7 8 0 0
-phdr: 6 5 4 3 0 0 0 0 0 0 0 0 0 0 0 0 9 8 7 6 0 0 0 0 0 0 0 0 0 0 0 0
-ls: 1 2 3 4
-ls2: 1 2 3 4
-lt: 68 65 6c 6c 6f 0 0 0 0 0 0 0 0 0 0 0 2a
-lu: 3 5 6 7 8 4 68 75 68 75 0 0 0 0 0 0 0 0 0 0 0 0 2b
-lu1: 3 1 2 3 4 4 68 75 68 75 0 0 0 0 0 0 0 0 0 0 0 0 2b
-lu2: 3 1 2 3 4 4 68 75 68 75 0 0 0 0 0 0 0 0 0 0 0 0 2b
-ls21: 1 2 3 4
-lu21: 3 1 2 3 4 4 68 75 68 75 0 0 0 0 0 0 0 0 0 0 0 0 2b
-lu22: 3 1 2 3 4 4 68 75 68 75 0 0 0 0 0 0 0 0 0 0 0 0 2b
-lu3: 3 5 6 7 8 4 68 75 68 75 0 0 0 0 0 0 0 0 0 0 0 0 2b
-lu4: 3 5 6 7 0 5 62 6c 61 0 0 0 0 0 0 0 0 0 0 0 0 0 2c
-ls3: 1 2 3 4
-lv: 3 4 5 6 68 61 68 61 0 0 0 0 0 0 0 0 0 0 0 0 2d 2e
-lv2: 1 2 3 4 68 69 68 69 0 0 0 0 0 0 0 0 0 0 0 0 2f 30
-lv3: 7 8 9 a 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 32
-lt2: 0 9 9 9 43 43 43 43 42 42 42 0 0 0 0 0 1
-flow: 9 8 7 6 0 0 0 0 0 0 0 0 0 0 0 0 6 5 4 3 0 0 0 0 0 0 0 0 0 0 0 0
-one
-two
-three
-sea_fill0: okay
-sea_fill1: okay
-sea_fill2: okay
diff --git a/tinyc/tests/tests2/91_ptr_longlong_arith32.c b/tinyc/tests/tests2/91_ptr_longlong_arith32.c
deleted file mode 100644
index bf07915ab..000000000
--- a/tinyc/tests/tests2/91_ptr_longlong_arith32.c
+++ /dev/null
@@ -1,15 +0,0 @@
-int printf(const char *, ...);
-char t[] = "012345678";
-
-int main(void)
-{
-    char *data = t;
-    unsigned long long r = 4;
-    unsigned a = 5;
-    unsigned long long b = 12;
-
-    *(unsigned*)(data + r) += a - b;
-
-    printf("data = \"%s\"\n", data);
-    return 0;
-}
diff --git a/tinyc/tests/tests2/91_ptr_longlong_arith32.expect b/tinyc/tests/tests2/91_ptr_longlong_arith32.expect
deleted file mode 100644
index f91e4b420..000000000
--- a/tinyc/tests/tests2/91_ptr_longlong_arith32.expect
+++ /dev/null
@@ -1 +0,0 @@
-data = "0123-5678"
diff --git a/tinyc/tests/tests2/92_enum_bitfield.c b/tinyc/tests/tests2/92_enum_bitfield.c
deleted file mode 100644
index bb6dc35d2..000000000
--- a/tinyc/tests/tests2/92_enum_bitfield.c
+++ /dev/null
@@ -1,57 +0,0 @@
-/* This checks if enums needing 8 bit but only having positive
-   values are correctly zero extended (instead of sign extended)
-   when stored into/loaded from a 8 bit bit-field of enum type (which
-   itself is implementation defined, so isn't necessarily supported by all
-   other compilers).  */
-enum tree_code {
-  SOME_CODE = 148, /* has bit 7 set, and hence all further enum values as well */
-  LAST_AND_UNUSED_TREE_CODE
-};
-typedef union tree_node *tree;
-struct tree_common
-{
-  union tree_node *chain;
-  union tree_node *type;
-  enum tree_code code : 8;
-  unsigned side_effects_flag : 1;
-};
-union tree_node
-{
-  struct tree_common common;
- };
-enum c_tree_code {
-  C_DUMMY_TREE_CODE = LAST_AND_UNUSED_TREE_CODE,
-  STMT_EXPR,
-  LAST_C_TREE_CODE
-};
-enum cplus_tree_code {
-  CP_DUMMY_TREE_CODE = LAST_C_TREE_CODE,
-  AMBIG_CONV,
-  LAST_CPLUS_TREE_CODE
-};
-
-extern int printf(const char *, ...);
-int blah(){return 0;}
-
-int convert_like_real (tree convs)
-{
-  switch (((enum tree_code) (convs)->common.code))
-    {
-    case AMBIG_CONV: /* This has bit 7 set, which must not be the sign
-			bit in tree_common.code, i.e. the bitfield must
-			be somehow marked unsigned.  */
-      return blah();
-    default:
-      break;
-    };
-   printf("unsigned enum bit-fields broken\n");
-}
-
-int main()
-{
-  union tree_node convs;
-
-  convs.common.code = AMBIG_CONV;
-  convert_like_real (&convs);
-  return 0;
-}
diff --git a/tinyc/tests/tests2/93_integer_promotion.c b/tinyc/tests/tests2/93_integer_promotion.c
deleted file mode 100644
index a1176fc6b..000000000
--- a/tinyc/tests/tests2/93_integer_promotion.c
+++ /dev/null
@@ -1,71 +0,0 @@
-/* integer promotion */
-
-int printf(const char*, ...);
-#define promote(s) printf(" %ssigned : %s\n", (s) - 100 < 0 ? "  " : "un", #s);
-
-int main (void)
-{
-    struct {
-        unsigned ub:3;
-        unsigned u:32;
-        unsigned long long ullb:35;
-        unsigned long long ull:64;
-        unsigned char c;
-    } s = { 1, 1, 1 };
-
-    promote(s.ub);
-    promote(s.u);
-    promote(s.ullb);
-    promote(s.ull);
-    promote(s.c);
-    printf("\n");
-
-    promote((1 ? s.ub : 1));
-    promote((1 ? s.u : 1));
-    promote((1 ? s.ullb : 1));
-    promote((1 ? s.ull : 1));
-    promote((1 ? s.c : 1));
-    printf("\n");
-
-    promote(s.ub << 1);
-    promote(s.u << 1);
-    promote(s.ullb << 1);
-    promote(s.ull << 1);
-    promote(s.c << 1);
-    printf("\n");
-
-    promote(+s.ub);
-    promote(+s.u);
-    promote(+s.ullb);
-    promote(+s.ull);
-    promote(+s.c);
-    printf("\n");
-
-    promote(-s.ub);
-    promote(-s.u);
-    promote(-s.ullb);
-    promote(-s.ull);
-    promote(-s.c);
-    printf("\n");
-
-    promote(~s.ub);
-    promote(~s.u);
-    promote(~s.ullb);
-    promote(~s.ull);
-    promote(~s.c);
-    printf("\n");
-
-    promote(!s.ub);
-    promote(!s.u);
-    promote(!s.ullb);
-    promote(!s.ull);
-    promote(!s.c);
-    printf("\n");
-
-    promote(+(unsigned)s.ub);
-    promote(-(unsigned)s.ub);
-    promote(~(unsigned)s.ub);
-    promote(!(unsigned)s.ub);
-
-    return 0;
-}
diff --git a/tinyc/tests/tests2/93_integer_promotion.expect b/tinyc/tests/tests2/93_integer_promotion.expect
deleted file mode 100644
index 34b9c145c..000000000
--- a/tinyc/tests/tests2/93_integer_promotion.expect
+++ /dev/null
@@ -1,46 +0,0 @@
-   signed : s.ub
- unsigned : s.u
-   signed : s.ullb
- unsigned : s.ull
-   signed : s.c
-
-   signed : (1 ? s.ub : 1)
- unsigned : (1 ? s.u : 1)
-   signed : (1 ? s.ullb : 1)
- unsigned : (1 ? s.ull : 1)
-   signed : (1 ? s.c : 1)
-
-   signed : s.ub << 1
- unsigned : s.u << 1
-   signed : s.ullb << 1
- unsigned : s.ull << 1
-   signed : s.c << 1
-
-   signed : +s.ub
- unsigned : +s.u
-   signed : +s.ullb
- unsigned : +s.ull
-   signed : +s.c
-
-   signed : -s.ub
- unsigned : -s.u
-   signed : -s.ullb
- unsigned : -s.ull
-   signed : -s.c
-
-   signed : ~s.ub
- unsigned : ~s.u
-   signed : ~s.ullb
- unsigned : ~s.ull
-   signed : ~s.c
-
-   signed : !s.ub
-   signed : !s.u
-   signed : !s.ullb
-   signed : !s.ull
-   signed : !s.c
-
- unsigned : +(unsigned)s.ub
- unsigned : -(unsigned)s.ub
- unsigned : ~(unsigned)s.ub
-   signed : !(unsigned)s.ub
diff --git a/tinyc/tests/tests2/94_generic.c b/tinyc/tests/tests2/94_generic.c
deleted file mode 100644
index d7fb5fc3b..000000000
--- a/tinyc/tests/tests2/94_generic.c
+++ /dev/null
@@ -1,64 +0,0 @@
-#include <stdio.h>
-
-const int a = 0;
-
-struct a {
-	int a;
-};
-
-struct b {
-	int a;
-};
-
-int a_f()
-{
-	return 20;
-}
-
-int b_f()
-{
-	return 10;
-}
-
-typedef int int_type1;
-
-#define gen_sw(a) _Generic(a, const char *: 1, default: 8, int: 123);
-
-int main()
-{
-	int i = 0;
-	signed long int l = 2;
-	struct b titi;
-	const int * const ptr;
-	const char *ti;
-	int_type1 i2;
-
-	i = _Generic(a, int: a_f, const int: b_f)();
-	printf("%d\n", i);
-	i = _Generic(a, int: a_f() / 2, const int: b_f() / 2);
-	printf("%d\n", i);
-	i = _Generic(ptr, int *:1, int * const:2, default:20);
-	printf("%d\n", i);
-	i = gen_sw(a);
-	printf("%d\n", i);
-	i = _Generic(titi, struct a:1, struct b:2, default:20);
-	printf("%d\n", i);
-	i = _Generic(i2, char: 1, int : 0);
-	printf("%d\n", i);
-	i = _Generic(a, char:1, int[4]:2, default:5);
-	printf("%d\n", i);
-	i = _Generic(17, int :1, int **:2);
-	printf("%d\n", i);
-	i = _Generic(17L, int :1, long :2, long long : 3);
-	printf("%d\n", i);
-	i = _Generic("17, io", char *: 3, const char *: 1);
-	printf("%d\n", i);
-	i = _Generic(ti, const unsigned char *:1, const char *:4, char *:3,
-		     const signed char *:2);
-	printf("%d\n", i);
-	printf("%s\n", _Generic(i + 2L, long: "long", int: "int",
-				long long: "long long"));
-	i = _Generic(l, long: 1, int: 2);
-	printf("%d\n", i);
-	return 0;
-}
diff --git a/tinyc/tests/tests2/94_generic.expect b/tinyc/tests/tests2/94_generic.expect
deleted file mode 100644
index 9aa927530..000000000
--- a/tinyc/tests/tests2/94_generic.expect
+++ /dev/null
@@ -1,13 +0,0 @@
-20
-10
-20
-123
-2
-0
-5
-1
-2
-3
-4
-long
-1
\ No newline at end of file
diff --git a/tinyc/tests/tests2/95_bitfields.c b/tinyc/tests/tests2/95_bitfields.c
deleted file mode 100644
index f025c575d..000000000
--- a/tinyc/tests/tests2/95_bitfields.c
+++ /dev/null
@@ -1,218 +0,0 @@
-/* ----------------------------------------------------------------------- */
-#if TEST == 1
-{
-    struct M P A __s
-    {
-        unsigned x : 12;
-        unsigned char y : 7;
-        unsigned z : 28;
-        unsigned a: 4;
-        unsigned b: 5;
-    };
-    TEST_STRUCT(0x333,0x44,0x555555,6,7);
-}
-
-/* ----------------------------------------------------------------------- */
-#elif TEST == 2
-{
-    struct M P __s
-    {
-        int x: 12;
-        char y: 6;
-        long long z:63;
-        A char a:4;
-        long long b:2;
-
-    };
-    TEST_STRUCT(3,30,0x123456789abcdef0LL,5,2);
-}
-
-/* ----------------------------------------------------------------------- */
-#elif TEST == 3
-{
-    struct M P __s
-    {
-        unsigned x:5, y:5, :0, z:5; char a:5; A short b:5;
-    };
-    TEST_STRUCT(21,23,25,6,14);
-}
-
-/* ----------------------------------------------------------------------- */
-#elif TEST == 4
-{
-    struct M P __s {
-        int x : 3;
-        int : 2;
-        int y : 1;
-        int : 0;
-        int z : 5;
-        int a : 7;
-        unsigned int b : 7;
-    };
-    TEST_STRUCT(3,1,15,120,120);
-}
-
-/* ----------------------------------------------------------------------- */
-#elif TEST == 5
-{
-    struct M P __s {
-        long long x : 45;
-        long long : 2;
-        long long y : 30;
-        unsigned long long z : 38;
-        char a; short b;
-    };
-    TEST_STRUCT(0x123456789ULL, 120<<25, 120, 0x44, 0x77);
-}
-
-/* ----------------------------------------------------------------------- */
-#elif TEST == 6
-{
-    struct M P __s {
-	int a;
-	signed char b;
-	int x : 12, y : 4, : 0, : 4, z : 3;
-	char d;
-    };
-    TEST_STRUCT(1,2,3,4,-3);
-}
-
-/* ----------------------------------------------------------------------- */
-#elif defined PACK
-
-#if PACK
-# pragma pack(push,1)
-# define P //_P
-#else
-# define P
-#endif
-
-printf("\n\n" + 2*top);
-#define TEST 1
-#include SELF
-top = 0;
-#define TEST 2
-#include SELF
-#define TEST 3
-#include SELF
-#define TEST 4
-#include SELF
-#define TEST 5
-#include SELF
-#define TEST 6
-#include SELF
-
-#if PACK
-# pragma pack(pop)
-#endif
-
-#undef P
-#undef PACK
-
-/* ----------------------------------------------------------------------- */
-#elif defined ALIGN
-
-#if ALIGN
-# define A _A(16)
-#else
-# define A
-#endif
-
-#define PACK 0
-#include SELF
-#define PACK 1
-#include SELF
-
-#undef A
-#undef ALIGN
-
-/* ----------------------------------------------------------------------- */
-#elif defined MS_BF
-
-#if MS_BF
-# ifdef __TINYC__
-#  pragma comment(option, "-mms-bitfields")
-# elif defined __GNUC__
-#  define M __attribute__((ms_struct))
-# endif
-#else
-# ifdef __TINYC__
-#  pragma comment(option, "-mno-ms-bitfields")
-# elif defined __GNUC__
-#  define M __attribute__((gcc_struct))
-# endif
-#endif
-#ifndef M
-# define M
-#endif
-
-#define ALIGN 0
-#include SELF
-#define ALIGN 1
-#include SELF
-
-#undef M
-#undef MS_BF
-
-/* ----------------------------------------------------------------------- */
-#else
-
-#include <stdio.h>
-#include <string.h>
-/* some gcc headers #define __attribute__ to empty if it's not gcc */
-#undef __attribute__
-
-void dump(void *p, int s)
-{
-    int i;
-    for (i = s; --i >= 0;)
-        printf("%02X", ((unsigned char*)p)[i]);
-    printf("\n");
-}
-
-#define pv(m) \
-    printf(sizeof (s->m + 0) == 8 ? " %016llx" : " %02x", s->m)
-
-#define TEST_STRUCT(v1,v2,v3,v4,v5) { \
-        struct __s _s, *s = & _s; \
-        printf("\n---- TEST %d%s%s%s ----\n" + top, \
-            TEST, MS_BF?" - MS-BITFIELDS":"", \
-            PACK?" - PACKED":"", \
-            ALIGN?" - WITH ALIGN":""); \
-        memset(s, 0, sizeof *s); \
-        s->x = -1, s->y = -1, s->z = -1, s->a = -1, s->b = -1; \
-        printf("bits in use : "), dump(s, sizeof *s); \
-        s->x = v1, s->y = v2, s->z = v3, s->a += v4, ++s->a, s->b = v5; \
-        printf("bits as set : "), dump(s, sizeof *s); \
-        printf("values      :"), pv(x), pv(y), pv(z), pv(a), pv(b), printf("\n"); \
-        printf("align/size  : %d %d\n", alignof(struct __s),sizeof(struct __s)); \
-    }
-
-#ifdef _MSC_VER
-# define _A(n) __declspec(align(n))
-# define _P
-# define alignof(x) __alignof(x)
-#else
-# define _A(n) __attribute__((aligned(n)))
-# define _P __attribute__((packed))
-# define alignof(x) __alignof__(x)
-#endif
-
-#ifndef MS_BITFIELDS
-# define MS_BITFIELDS 0
-#endif
-
-#define SELF "95_bitfields.c"
-
-int top = 1;
-
-int main()
-{
-#define MS_BF MS_BITFIELDS
-#include SELF
-    return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-#endif
-#undef TEST
diff --git a/tinyc/tests/tests2/95_bitfields.expect b/tinyc/tests/tests2/95_bitfields.expect
deleted file mode 100644
index 6a8fd9abd..000000000
--- a/tinyc/tests/tests2/95_bitfields.expect
+++ /dev/null
@@ -1,149 +0,0 @@
----- TEST 1 ----
-bits in use : 0000001FFFFFFFFF007F0FFF
-bits as set : 000000076055555500440333
-values      : 333 44 555555 06 07
-align/size  : 4 12
-
----- TEST 2 ----
-bits in use : 000000000000003F7FFFFFFFFFFFFFFF00000000003F0FFF
-bits as set : 0000000000000025123456789ABCDEF000000000001E0003
-values      : 03 1e 123456789abcdef0 05 fffffffe
-align/size  : 8 24
-
----- TEST 3 ----
-bits in use : 001F1F1F000003FF
-bits as set : 000E0619000002F5
-values      : 15 17 19 06 0e
-align/size  : 4 8
-
----- TEST 4 ----
-bits in use : 0007FFFF00000027
-bits as set : 00078F0F00000023
-values      : 03 ffffffff 0f fffffff8 78
-align/size  : 4 8
-
----- TEST 5 ----
-bits in use : FFFFFF3FFFFFFFFF000000003FFFFFFF00001FFFFFFFFFFF
-bits as set : 007744000000007800000000300000000000000123456789
-values      : 0000000123456789 f0000000 0000000000000078 44 77
-align/size  : 8 24
-
----- TEST 6 ----
-bits in use : 0000007000FFFFFFFFFFFFFF
-bits as set : 00000030002001FD00000004
-values      : 01 02 03 04 fffffffd
-align/size  : 4 12
-
-
-
----- TEST 1 - PACKED ----
-bits in use : FFFFFFFFFFFFFF
-bits as set : 3B02AAAAAC4333
-values      : 333 44 555555 06 07
-align/size  : 1 7
-
----- TEST 2 - PACKED ----
-bits in use : 7FFFFFFFFFFFFFFFFFFFFF
-bits as set : 4A48D159E26AF37BC1E003
-values      : 03 1e 123456789abcdef0 05 fffffffe
-align/size  : 1 11
-
----- TEST 3 - PACKED ----
-bits in use : 7FFF000003FF
-bits as set : 38D9000002F5
-values      : 15 17 19 06 0e
-align/size  : 1 6
-
----- TEST 4 - PACKED ----
-bits in use : 07FFFF00000027
-bits as set : 078F0F00000023
-values      : 03 ffffffff 0f fffffff8 78
-align/size  : 1 7
-
----- TEST 5 - PACKED ----
-bits in use : FFFFFF07FFFFFFFFFFFFFFFF9FFFFFFFFFFF
-bits as set : 007744000000000F18000000000123456789
-values      : 0000000123456789 f0000000 0000000000000078 44 77
-align/size  : 1 18
-
----- TEST 6 - PACKED ----
-bits in use : 007000FFFFFFFFFFFFFF
-bits as set : 0030002001FD00000004
-values      : 01 02 03 04 fffffffd
-align/size  : 1 10
-
-
-
----- TEST 1 - WITH ALIGN ----
-bits in use : 000000000000001FFFFFFFFF007F0FFF
-bits as set : 00000000000000076055555500440333
-values      : 333 44 555555 06 07
-align/size  : 16 16
-
----- TEST 2 - WITH ALIGN ----
-bits in use : 0000000000000000000000000000003F7FFFFFFFFFFFFFFF00000000003F0FFF
-bits as set : 00000000000000000000000000000025123456789ABCDEF000000000001E0003
-values      : 03 1e 123456789abcdef0 05 fffffffe
-align/size  : 16 32
-
----- TEST 3 - WITH ALIGN ----
-bits in use : 0000000000000000000000000000001F000000000000000000001F1F000003FF
-bits as set : 0000000000000000000000000000000E000000000000000000000619000002F5
-values      : 15 17 19 06 0e
-align/size  : 16 32
-
----- TEST 4 - WITH ALIGN ----
-bits in use : 0007FFFF00000027
-bits as set : 00078F0F00000023
-values      : 03 ffffffff 0f fffffff8 78
-align/size  : 4 8
-
----- TEST 5 - WITH ALIGN ----
-bits in use : FFFFFF3FFFFFFFFF000000003FFFFFFF00001FFFFFFFFFFF
-bits as set : 007744000000007800000000300000000000000123456789
-values      : 0000000123456789 f0000000 0000000000000078 44 77
-align/size  : 8 24
-
----- TEST 6 - WITH ALIGN ----
-bits in use : 0000007000FFFFFFFFFFFFFF
-bits as set : 00000030002001FD00000004
-values      : 01 02 03 04 fffffffd
-align/size  : 4 12
-
-
-
----- TEST 1 - PACKED - WITH ALIGN ----
-bits in use : 000000000000000000FFFFFFFFFFFFFF
-bits as set : 0000000000000000003B02AAAAAC4333
-values      : 333 44 555555 06 07
-align/size  : 16 16
-
----- TEST 2 - PACKED - WITH ALIGN ----
-bits in use : 3F01FFFFFFFFFFFFFFFFFFFF
-bits as set : 250048D159E26AF37BC1E003
-values      : 03 1e 123456789abcdef0 05 fffffffe
-align/size  : 1 12
-
----- TEST 3 - PACKED - WITH ALIGN ----
-bits in use : 1F03FF000003FF
-bits as set : 0E00D9000002F5
-values      : 15 17 19 06 0e
-align/size  : 1 7
-
----- TEST 4 - PACKED - WITH ALIGN ----
-bits in use : 07FFFF00000027
-bits as set : 078F0F00000023
-values      : 03 ffffffff 0f fffffff8 78
-align/size  : 1 7
-
----- TEST 5 - PACKED - WITH ALIGN ----
-bits in use : FFFFFF07FFFFFFFFFFFFFFFF9FFFFFFFFFFF
-bits as set : 007744000000000F18000000000123456789
-values      : 0000000123456789 f0000000 0000000000000078 44 77
-align/size  : 1 18
-
----- TEST 6 - PACKED - WITH ALIGN ----
-bits in use : 007000FFFFFFFFFFFFFF
-bits as set : 0030002001FD00000004
-values      : 01 02 03 04 fffffffd
-align/size  : 1 10
diff --git a/tinyc/tests/tests2/95_bitfields_ms.c b/tinyc/tests/tests2/95_bitfields_ms.c
deleted file mode 100644
index b196fbd66..000000000
--- a/tinyc/tests/tests2/95_bitfields_ms.c
+++ /dev/null
@@ -1,2 +0,0 @@
-#define MS_BITFIELDS 1
-#include "95_bitfields.c"
diff --git a/tinyc/tests/tests2/95_bitfields_ms.expect b/tinyc/tests/tests2/95_bitfields_ms.expect
deleted file mode 100644
index 8ccafb78d..000000000
--- a/tinyc/tests/tests2/95_bitfields_ms.expect
+++ /dev/null
@@ -1,149 +0,0 @@
----- TEST 1 - MS-BITFIELDS ----
-bits in use : 0000001FFFFFFFFF0000007F00000FFF
-bits as set : 00000007605555550000004400000333
-values      : 333 44 555555 06 07
-align/size  : 4 16
-
----- TEST 2 - MS-BITFIELDS ----
-bits in use : 0000000000000003000000000000000F7FFFFFFFFFFFFFFF0000003F00000FFF
-bits as set : 00000000000000020000000000000005123456789ABCDEF00000001E00000003
-values      : 03 1e 123456789abcdef0 05 fffffffffffffffe
-align/size  : 8 32
-
----- TEST 3 - MS-BITFIELDS ----
-bits in use : 001F001F0000001F000003FF
-bits as set : 000E000600000019000002F5
-values      : 15 17 19 06 0e
-align/size  : 4 12
-
----- TEST 4 - MS-BITFIELDS ----
-bits in use : 0007FFFF00000027
-bits as set : 00078F0F00000023
-values      : 03 ffffffff 0f fffffff8 78
-align/size  : 4 8
-
----- TEST 5 - MS-BITFIELDS ----
-bits in use : 00000000FFFF00FF0000003FFFFFFFFF000000003FFFFFFF00001FFFFFFFFFFF
-bits as set : 0000000000770044000000000000007800000000300000000000000123456789
-values      : 0000000123456789 fffffffff0000000 0000000000000078 44 77
-align/size  : 8 32
-
----- TEST 6 - MS-BITFIELDS ----
-bits in use : 00000000000000700000FFFF000000FFFFFFFFFF
-bits as set : 000000000000003000002001000000FD00000004
-values      : 01 02 03 04 fffffffd
-align/size  : 4 20
-
-
-
----- TEST 1 - MS-BITFIELDS - PACKED ----
-bits in use : 0000001FFFFFFFFF7F00000FFF
-bits as set : 00000007605555554400000333
-values      : 333 44 555555 06 07
-align/size  : 1 13
-
----- TEST 2 - MS-BITFIELDS - PACKED ----
-bits in use : 00000000000000030F7FFFFFFFFFFFFFFF3F00000FFF
-bits as set : 000000000000000205123456789ABCDEF01E00000003
-values      : 03 1e 123456789abcdef0 05 fffffffffffffffe
-align/size  : 1 22
-
----- TEST 3 - MS-BITFIELDS - PACKED ----
-bits in use : 001F1F0000001F000003FF
-bits as set : 000E0600000019000002F5
-values      : 15 17 19 06 0e
-align/size  : 1 11
-
----- TEST 4 - MS-BITFIELDS - PACKED ----
-bits in use : 0007FFFF00000027
-bits as set : 00078F0F00000023
-values      : 03 ffffffff 0f fffffff8 78
-align/size  : 1 8
-
----- TEST 5 - MS-BITFIELDS - PACKED ----
-bits in use : FFFFFF0000003FFFFFFFFF000000003FFFFFFF00001FFFFFFFFFFF
-bits as set : 007744000000000000007800000000300000000000000123456789
-values      : 0000000123456789 fffffffff0000000 0000000000000078 44 77
-align/size  : 1 27
-
----- TEST 6 - MS-BITFIELDS - PACKED ----
-bits in use : 00000000700000FFFFFFFFFFFFFF
-bits as set : 000000003000002001FD00000004
-values      : 01 02 03 04 fffffffd
-align/size  : 1 14
-
-
-
----- TEST 1 - MS-BITFIELDS - WITH ALIGN ----
-bits in use : 0000001FFFFFFFFF0000007F00000FFF
-bits as set : 00000007605555550000004400000333
-values      : 333 44 555555 06 07
-align/size  : 16 16
-
----- TEST 2 - MS-BITFIELDS - WITH ALIGN ----
-bits in use : 0000000000000003000000000000000F7FFFFFFFFFFFFFFF0000003F00000FFF
-bits as set : 00000000000000020000000000000005123456789ABCDEF00000001E00000003
-values      : 03 1e 123456789abcdef0 05 fffffffffffffffe
-align/size  : 16 32
-
----- TEST 3 - MS-BITFIELDS - WITH ALIGN ----
-bits in use : 0000000000000000000000000000001F000000000000001F0000001F000003FF
-bits as set : 0000000000000000000000000000000E000000000000000600000019000002F5
-values      : 15 17 19 06 0e
-align/size  : 16 32
-
----- TEST 4 - MS-BITFIELDS - WITH ALIGN ----
-bits in use : 0007FFFF00000027
-bits as set : 00078F0F00000023
-values      : 03 ffffffff 0f fffffff8 78
-align/size  : 4 8
-
----- TEST 5 - MS-BITFIELDS - WITH ALIGN ----
-bits in use : 00000000FFFF00FF0000003FFFFFFFFF000000003FFFFFFF00001FFFFFFFFFFF
-bits as set : 0000000000770044000000000000007800000000300000000000000123456789
-values      : 0000000123456789 fffffffff0000000 0000000000000078 44 77
-align/size  : 8 32
-
----- TEST 6 - MS-BITFIELDS - WITH ALIGN ----
-bits in use : 00000000000000700000FFFF000000FFFFFFFFFF
-bits as set : 000000000000003000002001000000FD00000004
-values      : 01 02 03 04 fffffffd
-align/size  : 4 20
-
-
-
----- TEST 1 - MS-BITFIELDS - PACKED - WITH ALIGN ----
-bits in use : 0000000000001FFFFFFFFF7F00000FFF
-bits as set : 00000000000007605555554400000333
-values      : 333 44 555555 06 07
-align/size  : 16 16
-
----- TEST 2 - MS-BITFIELDS - PACKED - WITH ALIGN ----
-bits in use : 00000000000000030F0000007FFFFFFFFFFFFFFF3F00000FFF
-bits as set : 000000000000000205000000123456789ABCDEF01E00000003
-values      : 03 1e 123456789abcdef0 05 fffffffffffffffe
-align/size  : 16 25
-
----- TEST 3 - MS-BITFIELDS - PACKED - WITH ALIGN ----
-bits in use : 001F000000000000001F0000001F000003FF
-bits as set : 000E000000000000000600000019000002F5
-values      : 15 17 19 06 0e
-align/size  : 16 18
-
----- TEST 4 - MS-BITFIELDS - PACKED - WITH ALIGN ----
-bits in use : 0007FFFF00000027
-bits as set : 00078F0F00000023
-values      : 03 ffffffff 0f fffffff8 78
-align/size  : 1 8
-
----- TEST 5 - MS-BITFIELDS - PACKED - WITH ALIGN ----
-bits in use : FFFFFF0000003FFFFFFFFF000000003FFFFFFF00001FFFFFFFFFFF
-bits as set : 007744000000000000007800000000300000000000000123456789
-values      : 0000000123456789 fffffffff0000000 0000000000000078 44 77
-align/size  : 1 27
-
----- TEST 6 - MS-BITFIELDS - PACKED - WITH ALIGN ----
-bits in use : 00000000700000FFFFFFFFFFFFFF
-bits as set : 000000003000002001FD00000004
-values      : 01 02 03 04 fffffffd
-align/size  : 1 14
diff --git a/tinyc/tests/tests2/96_nodata_wanted.c b/tinyc/tests/tests2/96_nodata_wanted.c
deleted file mode 100644
index cc211d36b..000000000
--- a/tinyc/tests/tests2/96_nodata_wanted.c
+++ /dev/null
@@ -1,84 +0,0 @@
-/*****************************************************************************/
-/* test 'nodata_wanted' data output suppression */
-
-#if defined test_static_data_error
-void foo() {
-    if (1) {
-	static short w = (int)&foo; /* initializer not computable */
-    }
-}
-
-#elif defined test_static_nodata_error
-void foo() {
-    if (0) {
-	static short w = (int)&foo; /* initializer not computable */
-    }
-}
-
-#elif defined test_global_data_error
-void foo();
-static short w = (int)&foo; /* initializer not computable */
-
-
-#elif defined test_local_data_noerror
-void foo() {
-    short w = &foo; /* 2 cast warnings */
-}
-
-#elif defined test_data_suppression_off || defined test_data_suppression_on
-
-#if defined test_data_suppression_on
-# define SKIP 1
-#else
-# define SKIP 0
-#endif
-
-#include <stdio.h>
-/* some gcc headers #define __attribute__ to empty if it's not gcc */
-#undef __attribute__
-
-int main()
-{
-    __label__ ts0, te0, ts1, te1;
-    int tl, dl;
-
-    static char ds0 = 0;
-    static char de0 = 0;
-    /* get reference size of empty jmp */
-ts0:;
-    if (!SKIP) {}
-te0:;
-    dl = -(&de0 - &ds0);
-    tl = -(&&te0 - &&ts0);
-
-    /* test data and code suppression */
-    static char ds1 = 0;
-ts1:;
-    if (!SKIP) {
-        static void *p = (void*)&main;
-        static char cc[] = "static string";
-        static double d = 8.0;
-
-        static struct __attribute__((packed)) {
-            unsigned x : 12;
-            unsigned char y : 7;
-            unsigned z : 28, a: 4, b: 5;
-        } s = { 0x333,0x44,0x555555,6,7 };
-
-        printf("data:\n");
-        printf("  %d - %.1f - %.1f - %s - %s\n",
-            sizeof 8.0, 8.0, d, __FUNCTION__, cc);
-        printf("  %x %x %x %x %x\n",
-            s.x, s.y, s.z, s.a, s.b);
-    }
-te1:;
-    static char de1 = 0;
-
-    dl += &de1 - &ds1;
-    tl += &&te1 - &&ts1;
-    printf("size of data/text:\n  %s/%s\n",
-        dl ? "non-zero":"zero", tl ? "non-zero":"zero");
-    /*printf("# %d/%d\n", dl, tl);*/
-}
-
-#endif
diff --git a/tinyc/tests/tests2/96_nodata_wanted.expect b/tinyc/tests/tests2/96_nodata_wanted.expect
deleted file mode 100644
index 2749109a0..000000000
--- a/tinyc/tests/tests2/96_nodata_wanted.expect
+++ /dev/null
@@ -1,23 +0,0 @@
-[test_static_data_error]
-96_nodata_wanted.c:7: error: initializer element is not computable at load time
-
-[test_static_nodata_error]
-96_nodata_wanted.c:14: error: initializer element is not computable at load time
-
-[test_global_data_error]
-96_nodata_wanted.c:20: error: initializer element is not computable at load time
-
-[test_local_data_noerror]
-96_nodata_wanted.c:25: warning: assignment makes integer from pointer without a cast
-96_nodata_wanted.c:25: warning: nonportable conversion from pointer to char/short
-
-[test_data_suppression_off]
-data:
-  8 - 8.0 - 8.0 - main - static string
-  333 44 555555 6 7
-size of data/text:
-  non-zero/non-zero
-
-[test_data_suppression_on]
-size of data/text:
-  zero/zero
diff --git a/tinyc/tests/tests2/97_utf8_string_literal.c b/tinyc/tests/tests2/97_utf8_string_literal.c
deleted file mode 100644
index 96fbab0d1..000000000
--- a/tinyc/tests/tests2/97_utf8_string_literal.c
+++ /dev/null
@@ -1,12 +0,0 @@
-// this file contains BMP chars encoded in UTF-8
-#include <stdio.h>
-#include <wchar.h>
-
-int main()
-{
-    wchar_t s[] = L"hello$$你好¢¢世界€€world";
-    wchar_t *p;
-    for (p = s; *p; p++) printf("%04X ", (unsigned) *p);
-    printf("\n");
-    return 0;
-}
diff --git a/tinyc/tests/tests2/97_utf8_string_literal.expect b/tinyc/tests/tests2/97_utf8_string_literal.expect
deleted file mode 100644
index 9a1593cdd..000000000
--- a/tinyc/tests/tests2/97_utf8_string_literal.expect
+++ /dev/null
@@ -1 +0,0 @@
-0068 0065 006C 006C 006F 0024 0024 4F60 597D 00A2 00A2 4E16 754C 20AC 20AC 0077 006F 0072 006C 0064 
diff --git a/tinyc/tests/tests2/98_al_ax_extend.c b/tinyc/tests/tests2/98_al_ax_extend.c
deleted file mode 100644
index 9b4e02fe7..000000000
--- a/tinyc/tests/tests2/98_al_ax_extend.c
+++ /dev/null
@@ -1,41 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-asm (
-    ".text;"
-    ".globl _us;.globl _ss;.globl _uc;.globl _sc;"
-    "_us:;_ss:;_uc:;_sc:;"
-    "movl $0x1234ABCD, %eax;"
-	"ret;"
-);
-
-#if 1
-#define us _us
-#define ss _ss
-#define uc _uc
-#define sc _sc
-#endif
-
-int main()
-{
-    unsigned short us(void);
-    short ss(void);
-    unsigned char uc(void);
-    signed char sc(void);
-
-    unsigned short (*fpus)(void) = us;
-    short (*fpss)(void) = ss;
-    unsigned char (*fpuc)(void) = uc;
-    signed char (*fpsc)(void) = sc;
-
-    printf("%08X %08X\n", us() + 1, fpus() + 1);
-    printf("%08X %08X\n", ss() + 1, fpss() + 1);
-    printf("%08X %08X\n", uc() + 1, fpuc() + 1);
-    printf("%08X %08X\n", sc() + 1, fpsc() + 1);
-    printf("\n");
-    printf("%08X %08X\n", fpus() + 1, us() + 1);
-    printf("%08X %08X\n", fpss() + 1, ss() + 1);
-    printf("%08X %08X\n", fpuc() + 1, uc() + 1);
-    printf("%08X %08X\n", fpsc() + 1, sc() + 1);
-
-    return 0;
-}
diff --git a/tinyc/tests/tests2/98_al_ax_extend.expect b/tinyc/tests/tests2/98_al_ax_extend.expect
deleted file mode 100644
index c5752e843..000000000
--- a/tinyc/tests/tests2/98_al_ax_extend.expect
+++ /dev/null
@@ -1,9 +0,0 @@
-0000ABCE 0000ABCE
-FFFFABCE FFFFABCE
-000000CE 000000CE
-FFFFFFCE FFFFFFCE
-
-0000ABCE 0000ABCE
-FFFFABCE FFFFABCE
-000000CE 000000CE
-FFFFFFCE FFFFFFCE
diff --git a/tinyc/tests/tests2/99_fastcall.c b/tinyc/tests/tests2/99_fastcall.c
deleted file mode 100644
index ee4b67d20..000000000
--- a/tinyc/tests/tests2/99_fastcall.c
+++ /dev/null
@@ -1,276 +0,0 @@
-#include <stdio.h>
-#include <assert.h>
-
-#ifndef _WIN32
-#define __fastcall __attribute((fastcall))
-#endif
-
-#if 1
-#define SYMBOL(x) _##x
-#else
-#define SYMBOL(x) x
-#endif
-
-/////////////////////////////////////////////////////////////////////////
-//////////                TRAP FRAMEWORK
-/////////////////////////////////////////////////////////////////////////
-// if you cast 'TRAP' to a function pointer and call it,
-//   it will save all 8 registers,
-//   and jump into C-code (previously set using 'SET_TRAP_HANDLER(x)'),
-//   in C-code you can pop DWORDs from stack and modify registers
-//
-
-void *SYMBOL(trap_handler);
-
-extern unsigned char SYMBOL(trap)[];
-asm (
-    ".text;"
-    "_trap:;"
-    "pushl %esp;"
-    "pusha;"
-    "addl $0x4, 0xc(%esp);"
-    "pushl %esp;"
-    "call *_trap_handler;"
-    "addl $0x4, %esp;"
-    "movl 0xc(%esp), %eax;"
-    "movl %eax, 0x20(%esp);"
-    "popa;"
-    "popl %esp;"
-	"ret;"
-);
-
-struct trapframe {
-    unsigned edi, esi, ebp, esp, ebx, edx, ecx, eax;
-};
-
-
-#define M_FLOAT(addr) (*(float *)(addr))
-#define M_DWORD(addr) (*(unsigned *)(addr))
-#define M_WORD(addr) (*(unsigned short *)(addr))
-#define M_BYTE(addr) (*(unsigned char *)(addr))
-#define R_EAX ((tf)->eax)
-#define R_ECX ((tf)->ecx)
-#define R_EDX ((tf)->edx)
-#define R_EBX ((tf)->ebx)
-#define R_ESP ((tf)->esp)
-#define R_EBP ((tf)->ebp)
-#define R_ESI ((tf)->esi)
-#define R_EDI ((tf)->edi)
-
-#define ARG(x) (M_DWORD(R_ESP + (x) * 4))
-
-#define RETN(x) do { \
-    M_DWORD(R_ESP + (x)) = M_DWORD(R_ESP); \
-    R_ESP += (x); \
-} while (0)
-
-#define DUMP() do { \
-    unsigned i; \
-    printf("EAX: %08X\n", R_EAX); \
-    printf("ECX: %08X\n", R_ECX); \
-    printf("EDX: %08X\n", R_EDX); \
-    printf("EBX: %08X\n", R_EBX); \
-    printf("ESP: %08X\n", R_ESP); \
-    printf("EBP: %08X\n", R_EBP); \
-    printf("ESI: %08X\n", R_ESI); \
-    printf("EDI: %08X\n", R_EDI); \
-    printf("\n"); \
-    printf("[RETADDR]: %08X\n", M_DWORD(R_ESP)); \
-    for (i = 1; i <= 8; i++) { \
-        printf("[ARG%4d]: %08X\n", i, ARG(i)); \
-    } \
-} while (0)
-
-#define SET_TRAP_HANDLER(x) ((SYMBOL(trap_handler)) = (x))
-#define TRAP ((void *) &SYMBOL(trap))
-
-
-
-/////////////////////////////////////////////////////////////////////////
-//////////                SAFECALL FRAMEWORK
-/////////////////////////////////////////////////////////////////////////
-// this framework will convert any calling convention to cdecl
-// usage: first set call target with 'SET_SAFECALL_TARGET(x)'
-//        then cast 'SAFECALL' to target function pointer type and invoke it
-//        after calling, 'ESPDIFF' is the difference of old and new esp
-
-void *SYMBOL(sc_call_target);
-unsigned SYMBOL(sc_retn_addr);
-unsigned SYMBOL(sc_old_esp);
-unsigned SYMBOL(sc_new_esp);
-
-extern unsigned char SYMBOL(safecall)[];
-asm (
-    ".text;"
-    "_safecall:;"
-    "popl _sc_retn_addr;"
-    "movl %esp, _sc_old_esp;"
-    "call *_sc_call_target;"
-    "movl %esp, _sc_new_esp;"
-    "movl _sc_old_esp, %esp;"
-	"jmp *_sc_retn_addr;"
-);
-
-#define SET_SAFECALL_TARGET(x) ((SYMBOL(sc_call_target)) = (x))
-#define SAFECALL ((void *) &SYMBOL(safecall))
-#define ESPDIFF (SYMBOL(sc_new_esp) - SYMBOL(sc_old_esp))
-
-
-/////////////////////////////////////////////////////////////////////////
-//////////                TEST FASTCALL INVOKE
-/////////////////////////////////////////////////////////////////////////
-
-void check_fastcall_invoke_0(struct trapframe *tf)
-{
-    //DUMP();
-    RETN(0);
-}
-
-void check_fastcall_invoke_1(struct trapframe *tf)
-{
-    //DUMP();
-    assert(R_ECX == 0x11111111);
-    RETN(0);
-}
-void check_fastcall_invoke_2(struct trapframe *tf)
-{
-    //DUMP();
-    assert(R_ECX == 0x11111111);
-    assert(R_EDX == 0x22222222);
-    RETN(0);
-}
-void check_fastcall_invoke_3(struct trapframe *tf)
-{
-    //DUMP();
-    assert(R_ECX == 0x11111111);
-    assert(R_EDX == 0x22222222);
-    assert(ARG(1) == 0x33333333);
-    RETN(1*4);
-}
-void check_fastcall_invoke_4(struct trapframe *tf)
-{
-    //DUMP();
-    assert(R_ECX == 0x11111111);
-    assert(R_EDX == 0x22222222);
-    assert(ARG(1) == 0x33333333);
-    assert(ARG(2) == 0x44444444);
-    RETN(2*4);
-}
-
-void check_fastcall_invoke_5(struct trapframe *tf)
-{
-    //DUMP();
-    assert(R_ECX == 0x11111111);
-    assert(R_EDX == 0x22222222);
-    assert(ARG(1) == 0x33333333);
-    assert(ARG(2) == 0x44444444);
-    assert(ARG(3) == 0x55555555);
-    RETN(3*4);
-}
-
-void test_fastcall_invoke()
-{
-    SET_TRAP_HANDLER(check_fastcall_invoke_0);
-    ((void __fastcall (*)(void)) TRAP)();
-
-    SET_TRAP_HANDLER(check_fastcall_invoke_1);
-    ((void __fastcall (*)(unsigned)) TRAP)(0x11111111);
-
-    SET_TRAP_HANDLER(check_fastcall_invoke_2);
-    ((void __fastcall (*)(unsigned, unsigned)) TRAP)(0x11111111, 0x22222222);
-
-    SET_TRAP_HANDLER(check_fastcall_invoke_3);
-    ((void __fastcall (*)(unsigned, unsigned, unsigned)) TRAP)(0x11111111, 0x22222222, 0x33333333);
-
-    SET_TRAP_HANDLER(check_fastcall_invoke_4);
-    ((void __fastcall (*)(unsigned, unsigned, unsigned, unsigned)) TRAP)(0x11111111, 0x22222222, 0x33333333, 0x44444444);
-
-    SET_TRAP_HANDLER(check_fastcall_invoke_5);
-    ((void __fastcall (*)(unsigned, unsigned, unsigned, unsigned, unsigned)) TRAP)(0x11111111, 0x22222222, 0x33333333, 0x44444444, 0x55555555);
-}
-
-
-/////////////////////////////////////////////////////////////////////////
-//////////                TEST FUNCTION CODE GENERATION
-/////////////////////////////////////////////////////////////////////////
-
-int __fastcall check_fastcall_espdiff_0(void)
-{
-    return 0;
-}
-
-int __fastcall check_fastcall_espdiff_1(int a)
-{
-    return a;
-}
-
-int __fastcall check_fastcall_espdiff_2(int a, int b)
-{
-    return a + b;
-}
-
-int __fastcall check_fastcall_espdiff_3(int a, int b, int c)
-{
-    return a + b + c;
-}
-
-int __fastcall check_fastcall_espdiff_4(int a, int b, int c, int d)
-{
-    return a + b + c + d;
-}
-
-int __fastcall check_fastcall_espdiff_5(int a, int b, int c, int d, int e)
-{
-    return a + b + c + d + e;
-}
-
-void test_fastcall_espdiff()
-{
-    int x;
-    SET_SAFECALL_TARGET(check_fastcall_espdiff_0);
-    x = ((typeof(&check_fastcall_espdiff_0))SAFECALL)();
-    assert(x == 0);
-    assert(ESPDIFF == 0);
-
-    SET_SAFECALL_TARGET(check_fastcall_espdiff_1);
-    x = ((typeof(&check_fastcall_espdiff_1))SAFECALL)(1);
-    assert(x == 1);
-    assert(ESPDIFF == 0);
-
-    SET_SAFECALL_TARGET(check_fastcall_espdiff_2);
-    x = ((typeof(&check_fastcall_espdiff_2))SAFECALL)(1, 2);
-    assert(x == 1 + 2);
-    assert(ESPDIFF == 0);
-
-    SET_SAFECALL_TARGET(check_fastcall_espdiff_3);
-    x = ((typeof(&check_fastcall_espdiff_3))SAFECALL)(1, 2, 3);
-    assert(x == 1 + 2 + 3);
-    assert(ESPDIFF == 1*4);
-
-    SET_SAFECALL_TARGET(check_fastcall_espdiff_4);
-    x = ((typeof(&check_fastcall_espdiff_4))SAFECALL)(1, 2, 3, 4);
-    assert(x == 1 + 2 + 3 + 4);
-    assert(ESPDIFF == 2*4);
-
-    SET_SAFECALL_TARGET(check_fastcall_espdiff_5);
-    x = ((typeof(&check_fastcall_espdiff_5))SAFECALL)(1, 2, 3, 4, 5);
-    assert(x == 1 + 2 + 3 + 4 + 5);
-    assert(ESPDIFF == 3*4);
-}
-
-int main()
-{
-#define N 10000
-    int i;
-
-    for (i = 1; i <= N; i++) {
-        test_fastcall_espdiff();
-    }
-
-    for (i = 1; i <= N; i++) {
-        test_fastcall_invoke();
-    }
-
-    puts("TEST OK");
-    return 0;
-}
diff --git a/tinyc/tests/tests2/99_fastcall.expect b/tinyc/tests/tests2/99_fastcall.expect
deleted file mode 100644
index 3835d63df..000000000
--- a/tinyc/tests/tests2/99_fastcall.expect
+++ /dev/null
@@ -1 +0,0 @@
-TEST OK
diff --git a/tinyc/tests/vla_test.c b/tinyc/tests/vla_test.c
deleted file mode 100644
index 3616c46d4..000000000
--- a/tinyc/tests/vla_test.c
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Test that allocating a variable length array in a loop
- * does not use up a linear amount of memory
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#define LOOP_COUNT 1000
-#define ARRAY_SIZE 100
-
-/* Overwrite a VLA. This will overwrite the return address if SP is incorrect */
-void smash(char *p, int n) {
-  memset(p, 0, n);
-}
-
-int test1(int n) {
-  int i;
-  char *array_ptrs[LOOP_COUNT];
-  
-  for (i = 0; i < LOOP_COUNT; ++i) {
-    char test[n];
-    smash(test, n);
-    array_ptrs[i] = test;
-  }
-  
-  return (array_ptrs[0]-array_ptrs[LOOP_COUNT-1] < n) ? 0 : 1;
-}
-
-/* ensure goto does not circumvent array free */
-int test2(int n) {
-  char *array_ptrs[LOOP_COUNT];
-
-  int i = 0;
-loop:;
-  char test[n];
-  smash(test, n);
-  if (i >= LOOP_COUNT)
-    goto end;
-  array_ptrs[i] = test;
-  ++i;
-  goto loop;
-
-end:
-  smash(test, n);
-  char test2[n];
-  smash(test2, n);
-  return (array_ptrs[0] - array_ptrs[LOOP_COUNT-1] < n) ? 0 : 1;
-}
-
-int test3(int n) {
-  char test[n];
-  smash(test, n);
-  goto label;
-label:
-  smash(test, n);
-  char test2[n];
-  smash(test2, n);
-  return (test-test2 >= n) ? 0 : 1;
-}
-
-#define RUN_TEST(t) \
-  if (!testname || (strcmp(#t, testname) == 0)) { \
-    fputs(#t "... ", stdout); \
-    fflush(stdout); \
-    if (t(ARRAY_SIZE) == 0) { \
-      fputs("success\n", stdout); \
-    } else { \
-      fputs("failure\n", stdout); \
-      retval = EXIT_FAILURE; \
-    } \
-  }
-
-int main(int argc, char **argv) {
-  const char *testname = NULL;
-  int retval = EXIT_SUCCESS;
-  if (argc > 1)
-    testname = argv[1];
-  RUN_TEST(test1)
-  RUN_TEST(test2)
-  RUN_TEST(test3)
-  return retval;
-}
diff --git a/tinyc/texi2pod.pl b/tinyc/texi2pod.pl
deleted file mode 100644
index d86e176f1..000000000
--- a/tinyc/texi2pod.pl
+++ /dev/null
@@ -1,427 +0,0 @@
-#! /usr/bin/perl -w
-
-#   Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
-
-# This file is part of GNU CC.
-
-# GNU CC is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2, or (at your option)
-# any later version.
-
-# GNU CC is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with GNU CC; see the file COPYING.  If not, write to
-# the Free Software Foundation, 59 Temple Place - Suite 330,
-# Boston MA 02111-1307, USA.
-
-# This does trivial (and I mean _trivial_) conversion of Texinfo
-# markup to Perl POD format.  It's intended to be used to extract
-# something suitable for a manpage from a Texinfo document.
-
-$output = 0;
-$skipping = 0;
-%sects = ();
-$section = "";
-@icstack = ();
-@endwstack = ();
-@skstack = ();
-@instack = ();
-$shift = "";
-%defs = ();
-$fnno = 1;
-$inf = "";
-$ibase = "";
-
-while ($_ = shift) {
-    if (/^-D(.*)$/) {
-	if ($1 ne "") {
-	    $flag = $1;
-	} else {
-	    $flag = shift;
-	}
-	$value = "";
-	($flag, $value) = ($flag =~ /^([^=]+)(?:=(.+))?/);
-	die "no flag specified for -D\n"
-	    unless $flag ne "";
-	die "flags may only contain letters, digits, hyphens, dashes and underscores\n"
-	    unless $flag =~ /^[a-zA-Z0-9_-]+$/;
-	$defs{$flag} = $value;
-    } elsif (/^-/) {
-	usage();
-    } else {
-	$in = $_, next unless defined $in;
-	$out = $_, next unless defined $out;
-	usage();
-    }
-}
-
-if (defined $in) {
-    $inf = gensym();
-    open($inf, "<$in") or die "opening \"$in\": $!\n";
-    $ibase = $1 if $in =~ m|^(.+)/[^/]+$|;
-} else {
-    $inf = \*STDIN;
-}
-
-if (defined $out) {
-    open(STDOUT, ">$out") or die "opening \"$out\": $!\n";
-}
-
-while(defined $inf) {
-while(<$inf>) {
-    # Certain commands are discarded without further processing.
-    /^\@(?:
-	 [a-z]+index		# @*index: useful only in complete manual
-	 |need			# @need: useful only in printed manual
-	 |(?:end\s+)?group	# @group .. @end group: ditto
-	 |page			# @page: ditto
-	 |node			# @node: useful only in .info file
-	 |(?:end\s+)?ifnottex   # @ifnottex .. @end ifnottex: use contents
-	)\b/x and next;
-
-    chomp;
-
-    # Look for filename and title markers.
-    /^\@setfilename\s+([^.]+)/ and $fn = $1, next;
-    /^\@settitle\s+([^.]+)/ and $tl = postprocess($1), next;
-
-    # Identify a man title but keep only the one we are interested in.
-    /^\@c\s+man\s+title\s+([A-Za-z0-9-]+)\s+(.+)/ and do {
-	if (exists $defs{$1}) {
-	    $fn = $1;
-	    $tl = postprocess($2);
-	}
-	next;
-    };
-
-    # Look for blocks surrounded by @c man begin SECTION ... @c man end.
-    # This really oughta be @ifman ... @end ifman and the like, but such
-    # would require rev'ing all other Texinfo translators.
-    /^\@c\s+man\s+begin\s+([A-Z]+)\s+([A-Za-z0-9-]+)/ and do {
-	$output = 1 if exists $defs{$2};
-        $sect = $1;
-	next;
-    };
-    /^\@c\s+man\s+begin\s+([A-Z]+)/ and $sect = $1, $output = 1, next;
-    /^\@c\s+man\s+end/ and do {
-	$sects{$sect} = "" unless exists $sects{$sect};
-	$sects{$sect} .= postprocess($section);
-	$section = "";
-	$output = 0;
-	next;
-    };
-
-    # handle variables
-    /^\@set\s+([a-zA-Z0-9_-]+)\s*(.*)$/ and do {
-	$defs{$1} = $2;
-	next;
-    };
-    /^\@clear\s+([a-zA-Z0-9_-]+)/ and do {
-	delete $defs{$1};
-	next;
-    };
-
-    next unless $output;
-
-    # Discard comments.  (Can't do it above, because then we'd never see
-    # @c man lines.)
-    /^\@c\b/ and next;
-
-    # End-block handler goes up here because it needs to operate even
-    # if we are skipping.
-    /^\@end\s+([a-z]+)/ and do {
-	# Ignore @end foo, where foo is not an operation which may
-	# cause us to skip, if we are presently skipping.
-	my $ended = $1;
-	next if $skipping && $ended !~ /^(?:ifset|ifclear|ignore|menu|iftex)$/;
-
-	die "\@end $ended without \@$ended at line $.\n" unless defined $endw;
-	die "\@$endw ended by \@end $ended at line $.\n" unless $ended eq $endw;
-
-	$endw = pop @endwstack;
-
-	if ($ended =~ /^(?:ifset|ifclear|ignore|menu|iftex)$/) {
-	    $skipping = pop @skstack;
-	    next;
-	} elsif ($ended =~ /^(?:example|smallexample|display)$/) {
-	    $shift = "";
-	    $_ = "";	# need a paragraph break
-	} elsif ($ended =~ /^(?:itemize|enumerate|[fv]?table)$/) {
-	    $_ = "\n=back\n";
-	    $ic = pop @icstack;
-	} else {
-	    die "unknown command \@end $ended at line $.\n";
-	}
-    };
-
-    # We must handle commands which can cause skipping even while we
-    # are skipping, otherwise we will not process nested conditionals
-    # correctly.
-    /^\@ifset\s+([a-zA-Z0-9_-]+)/ and do {
-	push @endwstack, $endw;
-	push @skstack, $skipping;
-	$endw = "ifset";
-	$skipping = 1 unless exists $defs{$1};
-	next;
-    };
-
-    /^\@ifclear\s+([a-zA-Z0-9_-]+)/ and do {
-	push @endwstack, $endw;
-	push @skstack, $skipping;
-	$endw = "ifclear";
-	$skipping = 1 if exists $defs{$1};
-	next;
-    };
-
-    /^\@(ignore|menu|iftex)\b/ and do {
-	push @endwstack, $endw;
-	push @skstack, $skipping;
-	$endw = $1;
-	$skipping = 1;
-	next;
-    };
-
-    next if $skipping;
-
-    # Character entities.  First the ones that can be replaced by raw text
-    # or discarded outright:
-    s/\@copyright\{\}/(c)/g;
-    s/\@dots\{\}/.../g;
-    s/\@enddots\{\}/..../g;
-    s/\@([.!? ])/$1/g;
-    s/\@[:-]//g;
-    s/\@bullet(?:\{\})?/*/g;
-    s/\@TeX\{\}/TeX/g;
-    s/\@pounds\{\}/\#/g;
-    s/\@minus(?:\{\})?/-/g;
-    s/\\,/,/g;
-
-    # Now the ones that have to be replaced by special escapes
-    # (which will be turned back into text by unmunge())
-    s/&/&amp;/g;
-    s/\@\{/&lbrace;/g;
-    s/\@\}/&rbrace;/g;
-    s/\@\@/&at;/g;
-
-    # Inside a verbatim block, handle @var specially.
-    if ($shift ne "") {
-	s/\@var\{([^\}]*)\}/<$1>/g;
-    }
-
-    # POD doesn't interpret E<> inside a verbatim block.
-    if ($shift eq "") {
-	s/</&lt;/g;
-	s/>/&gt;/g;
-    } else {
-	s/</&LT;/g;
-	s/>/&GT;/g;
-    }
-
-    # Single line command handlers.
-
-    /^\@include\s+(.+)$/ and do {
-	push @instack, $inf;
-	$inf = gensym();
-
-	# Try cwd and $ibase.
-	open($inf, "<" . $1) 
-	    or open($inf, "<" . $ibase . "/" . $1)
-		or die "cannot open $1 or $ibase/$1: $!\n";
-	next;
-    };
-
-    /^\@(?:section|unnumbered|unnumberedsec|center)\s+(.+)$/
-	and $_ = "\n=head2 $1\n";
-    /^\@subsection\s+(.+)$/
-	and $_ = "\n=head3 $1\n";
-
-    # Block command handlers:
-    /^\@itemize\s+(\@[a-z]+|\*|-)/ and do {
-	push @endwstack, $endw;
-	push @icstack, $ic;
-	$ic = $1;
-	$_ = "\n=over 4\n";
-	$endw = "itemize";
-    };
-
-    /^\@enumerate(?:\s+([a-zA-Z0-9]+))?/ and do {
-	push @endwstack, $endw;
-	push @icstack, $ic;
-	if (defined $1) {
-	    $ic = $1 . ".";
-	} else {
-	    $ic = "1.";
-	}
-	$_ = "\n=over 4\n";
-	$endw = "enumerate";
-    };
-
-    /^\@([fv]?table)\s+(\@[a-z]+)/ and do {
-	push @endwstack, $endw;
-	push @icstack, $ic;
-	$endw = $1;
-	$ic = $2;
-	$ic =~ s/\@(?:samp|strong|key|gcctabopt|option|env)/B/;
-	$ic =~ s/\@(?:code|kbd)/C/;
-	$ic =~ s/\@(?:dfn|var|emph|cite|i)/I/;
-	$ic =~ s/\@(?:file)/F/;
-	$_ = "\n=over 4\n";
-    };
-
-    /^\@((?:small)?example|display)/ and do {
-	push @endwstack, $endw;
-	$endw = $1;
-	$shift = "\t";
-	$_ = "";	# need a paragraph break
-    };
-
-    /^\@itemx?\s*(.+)?$/ and do {
-	if (defined $1) {
-	    # Entity escapes prevent munging by the <> processing below.
-	    $_ = "\n=item $ic\&LT;$1\&GT;\n";
-	} else {
-	    $_ = "\n=item $ic\n";
-	    $ic =~ y/A-Ya-y/B-Zb-z/;
-	    $ic =~ s/(\d+)/$1 + 1/eg;
-	}
-    };
-
-    $section .= $shift.$_."\n";
-}
-# End of current file.
-close($inf);
-$inf = pop @instack;
-}
-
-die "No filename or title\n" unless defined $fn && defined $tl;
-
-$sects{NAME} = "$fn \- $tl\n";
-$sects{FOOTNOTES} .= "=back\n" if exists $sects{FOOTNOTES};
-
-for $sect (qw(NAME SYNOPSIS DESCRIPTION OPTIONS ENVIRONMENT FILES
-	      BUGS NOTES FOOTNOTES SEEALSO AUTHOR COPYRIGHT)) {
-    if(exists $sects{$sect}) {
-	$head = $sect;
-	$head =~ s/SEEALSO/SEE ALSO/;
-	print "=head1 $head\n\n";
-	print scalar unmunge ($sects{$sect});
-	print "\n";
-    }
-}
-
-sub usage
-{
-    die "usage: $0 [-D toggle...] [infile [outfile]]\n";
-}
-
-sub postprocess
-{
-    local $_ = $_[0];
-
-    # @value{foo} is replaced by whatever 'foo' is defined as.
-    while (m/(\@value\{([a-zA-Z0-9_-]+)\})/g) {
-	if (! exists $defs{$2}) {
-	    print STDERR "Option $2 not defined\n";
-	    s/\Q$1\E//;
-	} else {
-	    $value = $defs{$2};
-	    s/\Q$1\E/$value/;
-	}
-    }
-
-    # Formatting commands.
-    # Temporary escape for @r.
-    s/\@r\{([^\}]*)\}/R<$1>/g;
-    s/\@(?:dfn|var|emph|cite|i)\{([^\}]*)\}/I<$1>/g;
-    s/\@(?:code|kbd)\{([^\}]*)\}/C<$1>/g;
-    s/\@(?:gccoptlist|samp|strong|key|option|env|command|b)\{([^\}]*)\}/B<$1>/g;
-    s/\@sc\{([^\}]*)\}/\U$1/g;
-    s/\@file\{([^\}]*)\}/F<$1>/g;
-    s/\@w\{([^\}]*)\}/S<$1>/g;
-    s/\@(?:dmn|math)\{([^\}]*)\}/$1/g;
-
-    # Cross references are thrown away, as are @noindent and @refill.
-    # (@noindent is impossible in .pod, and @refill is unnecessary.)
-    # @* is also impossible in .pod; we discard it and any newline that
-    # follows it.  Similarly, our macro @gol must be discarded.
-
-    s/\(?\@xref\{(?:[^\}]*)\}(?:[^.<]|(?:<[^<>]*>))*\.\)?//g;
-    s/\s+\(\@pxref\{(?:[^\}]*)\}\)//g;
-    s/;\s+\@pxref\{(?:[^\}]*)\}//g;
-    s/\@noindent\s*//g;
-    s/\@refill//g;
-    s/\@gol//g;
-    s/\@\*\s*\n?//g;
-
-    # @uref can take one, two, or three arguments, with different
-    # semantics each time.  @url and @email are just like @uref with
-    # one argument, for our purposes.
-    s/\@(?:uref|url|email)\{([^\},]*)\}/&lt;B<$1>&gt;/g;
-    s/\@uref\{([^\},]*),([^\},]*)\}/$2 (C<$1>)/g;
-    s/\@uref\{([^\},]*),([^\},]*),([^\},]*)\}/$3/g;
-
-    # Turn B<blah I<blah> blah> into B<blah> I<blah> B<blah> to
-    # match Texinfo semantics of @emph inside @samp.  Also handle @r
-    # inside bold.
-    s/&LT;/</g;
-    s/&GT;/>/g;
-    1 while s/B<((?:[^<>]|I<[^<>]*>)*)R<([^>]*)>/B<$1>${2}B</g;
-    1 while (s/B<([^<>]*)I<([^>]+)>/B<$1>I<$2>B</g);
-    1 while (s/I<([^<>]*)B<([^>]+)>/I<$1>B<$2>I</g);
-    s/[BI]<>//g;
-    s/([BI])<(\s+)([^>]+)>/$2$1<$3>/g;
-    s/([BI])<([^>]+?)(\s+)>/$1<$2>$3/g;
-
-    # Extract footnotes.  This has to be done after all other
-    # processing because otherwise the regexp will choke on formatting
-    # inside @footnote.
-    while (/\@footnote/g) {
-	s/\@footnote\{([^\}]+)\}/[$fnno]/;
-	add_footnote($1, $fnno);
-	$fnno++;
-    }
-
-    return $_;
-}
-
-sub unmunge
-{
-    # Replace escaped symbols with their equivalents.
-    local $_ = $_[0];
-
-    s/&lt;/E<lt>/g;
-    s/&gt;/E<gt>/g;
-    s/&lbrace;/\{/g;
-    s/&rbrace;/\}/g;
-    s/&at;/\@/g;
-    s/&amp;/&/g;
-    return $_;
-}
-
-sub add_footnote
-{
-    unless (exists $sects{FOOTNOTES}) {
-	$sects{FOOTNOTES} = "\n=over 4\n\n";
-    }
-
-    $sects{FOOTNOTES} .= "=item $fnno.\n\n"; $fnno++;
-    $sects{FOOTNOTES} .= $_[0];
-    $sects{FOOTNOTES} .= "\n\n";
-}
-
-# stolen from Symbol.pm
-{
-    my $genseq = 0;
-    sub gensym
-    {
-	my $name = "GEN" . $genseq++;
-	my $ref = \*{$name};
-	delete $::{$name};
-	return $ref;
-    }
-}
diff --git a/tinyc/win32/build-tcc.bat b/tinyc/win32/build-tcc.bat
deleted file mode 100644
index ceb4411ff..000000000
--- a/tinyc/win32/build-tcc.bat
+++ /dev/null
@@ -1,189 +0,0 @@
-@rem ------------------------------------------------------
-@rem batch file to build tcc using mingw, msvc or tcc itself
-@rem ------------------------------------------------------
-
-@echo off
-setlocal
-if (%1)==(-clean) goto :cleanup
-set CC=gcc
-set /p VERSION= < ..\VERSION
-set INST=
-set BIN=
-set DOC=no
-set EXES_ONLY=no
-goto :a0
-:a2
-shift
-:a3
-shift
-:a0
-if not (%1)==(-c) goto :a1
-set CC=%~2
-if (%2)==(cl) set CC=@call :cl
-goto :a2
-:a1
-if (%1)==(-t) set T=%2&& goto :a2
-if (%1)==(-v) set VERSION=%~2&& goto :a2
-if (%1)==(-i) set INST=%2&& goto :a2
-if (%1)==(-b) set BIN=%2&& goto :a2
-if (%1)==(-d) set DOC=yes&& goto :a3
-if (%1)==(-x) set EXES_ONLY=yes&& goto :a3
-if (%1)==() goto :p1
-:usage
-echo usage: build-tcc.bat [ options ... ]
-echo options:
-echo   -c prog              use prog (gcc/tcc/cl) to compile tcc
-echo   -c "prog options"    use prog with options to compile tcc
-echo   -t 32/64             force 32/64 bit default target
-echo   -v "version"         set tcc version
-echo   -i tccdir            install tcc into tccdir
-echo   -b bindir            optionally install binaries into bindir elsewhere
-echo   -d                   create tcc-doc.html too (needs makeinfo)
-echo   -x                   just create the executables
-echo   -clean               delete all previously produced files and directories
-exit /B 1
-
-@rem ------------------------------------------------------
-@rem sub-routines
-
-:cleanup
-set LOG=echo
-%LOG% removing files:
-for %%f in (*tcc.exe libtcc.dll lib\*.a) do call :del_file %%f
-for %%f in (..\config.h ..\config.texi) do call :del_file %%f
-for %%f in (include\*.h) do @if exist ..\%%f call :del_file %%f
-for %%f in (include\tcclib.h examples\libtcc_test.c) do call :del_file %%f
-for %%f in (*.o *.obj *.def *.pdb *.lib *.exp *.ilk) do call :del_file %%f
-%LOG% removing directories:
-for %%f in (doc libtcc) do call :del_dir %%f
-%LOG% done.
-exit /B 0
-:del_file
-if exist %1 del %1 && %LOG%   %1
-exit /B 0
-:del_dir
-if exist %1 rmdir /Q/S %1 && %LOG%   %1
-exit /B 0
-
-:cl
-@echo off
-set CMD=cl
-:c0
-set ARG=%1
-set ARG=%ARG:.dll=.lib%
-if (%1)==(-shared) set ARG=-LD
-if (%1)==(-o) shift && set ARG=-Fe%2
-set CMD=%CMD% %ARG%
-shift
-if not (%1)==() goto :c0
-echo on
-%CMD% -O1 -W2 -Zi -MT -GS- -nologo -link -opt:ref,icf
-@exit /B %ERRORLEVEL%
-
-@rem ------------------------------------------------------
-@rem main program
-
-:p1
-if not %T%_==_ goto :p2
-set T=32
-if %PROCESSOR_ARCHITECTURE%_==AMD64_ set T=64
-if %PROCESSOR_ARCHITEW6432%_==AMD64_ set T=64
-:p2
-if "%CC:~-3%"=="gcc" set CC=%CC% -Os -s -static
-set D32=-DTCC_TARGET_PE -DTCC_TARGET_I386
-set D64=-DTCC_TARGET_PE -DTCC_TARGET_X86_64
-set P32=i386-win32
-set P64=x86_64-win32
-if %T%==64 goto :t64
-set D=%D32%
-set DX=%D64%
-set PX=%P64%
-goto :p3
-:t64
-set D=%D64%
-set DX=%D32%
-set PX=%P32%
-goto :p3
-
-:p3
-@echo on
-
-:config.h
-echo>..\config.h #define TCC_VERSION "%VERSION%"
-echo>> ..\config.h #ifdef TCC_TARGET_X86_64
-echo>> ..\config.h #define TCC_LIBTCC1 "libtcc1-64.a"
-echo>> ..\config.h #else
-echo>> ..\config.h #define TCC_LIBTCC1 "libtcc1-32.a"
-echo>> ..\config.h #endif
-
-for %%f in (*tcc.exe *tcc.dll) do @del %%f
-
-:compiler
-%CC% -o libtcc.dll -shared ..\libtcc.c %D% -DLIBTCC_AS_DLL
-@if errorlevel 1 goto :the_end
-%CC% -o tcc.exe ..\tcc.c libtcc.dll %D% -DONE_SOURCE"=0"
-%CC% -o %PX%-tcc.exe ..\tcc.c %DX%
-
-@if (%EXES_ONLY%)==(yes) goto :files-done
-
-if not exist libtcc mkdir libtcc
-if not exist doc mkdir doc
-copy>nul ..\include\*.h include
-copy>nul ..\tcclib.h include
-copy>nul ..\libtcc.h libtcc
-copy>nul ..\tests\libtcc_test.c examples
-copy>nul tcc-win32.txt doc
-
-.\tcc -impdef libtcc.dll -o libtcc\libtcc.def
-@if errorlevel 1 goto :the_end
-
-:libtcc1.a
-@set O1=libtcc1.o crt1.o crt1w.o wincrt1.o wincrt1w.o dllcrt1.o dllmain.o chkstk.o bcheck.o
-.\tcc -m32 -c ../lib/libtcc1.c
-.\tcc -m32 -c lib/crt1.c
-.\tcc -m32 -c lib/crt1w.c
-.\tcc -m32 -c lib/wincrt1.c
-.\tcc -m32 -c lib/wincrt1w.c
-.\tcc -m32 -c lib/dllcrt1.c
-.\tcc -m32 -c lib/dllmain.c
-.\tcc -m32 -c lib/chkstk.S
-.\tcc -m32 -w -c ../lib/bcheck.c
-.\tcc -m32 -c ../lib/alloca86.S
-.\tcc -m32 -c ../lib/alloca86-bt.S
-.\tcc -m32 -ar lib/libtcc1-32.a %O1% alloca86.o alloca86-bt.o
-@if errorlevel 1 goto :the_end
-.\tcc -m64 -c ../lib/libtcc1.c
-.\tcc -m64 -c lib/crt1.c
-.\tcc -m64 -c lib/crt1w.c
-.\tcc -m64 -c lib/wincrt1.c
-.\tcc -m64 -c lib/wincrt1w.c
-.\tcc -m64 -c lib/dllcrt1.c
-.\tcc -m64 -c lib/dllmain.c
-.\tcc -m64 -c lib/chkstk.S
-.\tcc -m64 -w -c ../lib/bcheck.c
-.\tcc -m64 -c ../lib/alloca86_64.S
-.\tcc -m64 -c ../lib/alloca86_64-bt.S
-.\tcc -m64 -ar lib/libtcc1-64.a %O1% alloca86_64.o alloca86_64-bt.o
-@if errorlevel 1 goto :the_end
-
-:tcc-doc.html
-@if not (%DOC%)==(yes) goto :doc-done
-echo>..\config.texi @set VERSION %VERSION%
-cmd /c makeinfo --html --no-split ../tcc-doc.texi -o doc/tcc-doc.html
-:doc-done
-
-:files-done
-for %%f in (*.o *.def) do @del %%f
-
-:copy-install
-@if (%INST%)==() goto :the_end
-if not exist %INST% mkdir %INST%
-@if (%BIN%)==() set BIN=%INST%
-if not exist %BIN% mkdir %BIN%
-for %%f in (*tcc.exe *tcc.dll) do @copy>nul %%f %BIN%\%%f
-@if not exist %INST%\lib mkdir %INST%\lib
-for %%f in (lib\*.a lib\*.def) do @copy>nul %%f %INST%\%%f
-for %%f in (include examples libtcc doc) do @xcopy>nul /s/i/q/y %%f %INST%\%%f
-
-:the_end
-exit /B %ERRORLEVEL%
diff --git a/tinyc/win32/examples/dll.c b/tinyc/win32/examples/dll.c
deleted file mode 100644
index 052a05644..000000000
--- a/tinyc/win32/examples/dll.c
+++ /dev/null
@@ -1,13 +0,0 @@
-//+---------------------------------------------------------------------------
-//
-//  dll.c - Windows DLL example - dynamically linked part
-//
-
-#include <windows.h>
-
-__declspec(dllexport) const char *hello_data = "(not set)";
-
-__declspec(dllexport) void hello_func (void)
-{
-    MessageBox (0, hello_data, "From DLL", MB_ICONINFORMATION);
-}
diff --git a/tinyc/win32/examples/fib.c b/tinyc/win32/examples/fib.c
deleted file mode 100644
index 8da26bce6..000000000
--- a/tinyc/win32/examples/fib.c
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>	// atoi()
-
-int fib(n)
-{
-	if (n <= 2)
-		return 1;
-	else
-		return fib(n-1) + fib(n-2);
-}
-
-int main(int argc, char **argv) 
-{
-	int n;
-	if (argc < 2) {
-		printf("usage: fib n\n"
-			   "Compute nth Fibonacci number\n");
-		return 1;
-	}
-		
-	n = atoi(argv[1]);
-	printf("fib(%d) = %d\n", n, fib(n));
-	return 0;
-}
diff --git a/tinyc/win32/examples/hello_dll.c b/tinyc/win32/examples/hello_dll.c
deleted file mode 100644
index 4813c5b94..000000000
--- a/tinyc/win32/examples/hello_dll.c
+++ /dev/null
@@ -1,20 +0,0 @@
-//+---------------------------------------------------------------------------
-//
-//  HELLO_DLL.C - Windows DLL example - main application part
-//
-
-#include <windows.h>
-
-void hello_func (void);
-__declspec(dllimport) extern const char *hello_data;
-
-int WINAPI WinMain(
-    HINSTANCE hInstance,
-    HINSTANCE hPrevInstance,
-    LPSTR     lpCmdLine,
-    int       nCmdShow)
-{
-    hello_data = "Hello World!";
-    hello_func();
-    return 0;
-}
diff --git a/tinyc/win32/examples/hello_win.c b/tinyc/win32/examples/hello_win.c
deleted file mode 100644
index 96546e415..000000000
--- a/tinyc/win32/examples/hello_win.c
+++ /dev/null
@@ -1,163 +0,0 @@
-//+---------------------------------------------------------------------------
-//
-//  HELLO_WIN.C - Windows GUI 'Hello World!' Example
-//
-//+---------------------------------------------------------------------------
-
-#include <windows.h>
-
-#define APPNAME "HELLO_WIN"
-
-char szAppName[] = APPNAME; // The name of this application
-char szTitle[]   = APPNAME; // The title bar text
-const char *pWindowText;
-
-void CenterWindow(HWND hWnd);
-
-//+---------------------------------------------------------------------------
-//
-//  Function:   WndProc
-//
-//  Synopsis:   very unusual type of function - gets called by system to
-//              process windows messages.
-//
-//  Arguments:  same as always.
-//----------------------------------------------------------------------------
-
-LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
-{
-    switch (message) {
-
-        // ----------------------- first and last
-        case WM_CREATE:
-            CenterWindow(hwnd);
-            break;
-
-        case WM_DESTROY:
-            PostQuitMessage(0);
-            break;
-
-        // ----------------------- get out of it...
-        case WM_RBUTTONUP:
-            DestroyWindow(hwnd);
-            break;
-
-        case WM_KEYDOWN:
-            if (VK_ESCAPE == wParam)
-                DestroyWindow(hwnd);
-            break;
-
-        // ----------------------- display our minimal info
-        case WM_PAINT:
-        {
-            PAINTSTRUCT ps;
-            HDC         hdc;
-            RECT        rc;
-            hdc = BeginPaint(hwnd, &ps);
-
-            GetClientRect(hwnd, &rc);
-            SetTextColor(hdc, RGB(240,240,96));
-            SetBkMode(hdc, TRANSPARENT);
-            DrawText(hdc, pWindowText, -1, &rc, DT_CENTER|DT_SINGLELINE|DT_VCENTER);
-
-            EndPaint(hwnd, &ps);
-            break;
-        }
-
-        // ----------------------- let windows do all other stuff
-        default:
-            return DefWindowProc(hwnd, message, wParam, lParam);
-    }
-    return 0;
-}
-
-//+---------------------------------------------------------------------------
-//
-//  Function:   WinMain
-//
-//  Synopsis:   standard entrypoint for GUI Win32 apps
-//
-//----------------------------------------------------------------------------
-int APIENTRY WinMain(
-        HINSTANCE hInstance,
-        HINSTANCE hPrevInstance,
-        LPSTR lpCmdLine,
-        int nCmdShow
-        )
-{
-    MSG msg;
-    WNDCLASS wc;
-    HWND hwnd;
-
-    pWindowText = lpCmdLine[0] ? lpCmdLine : "Hello Windows!";
-
-    // Fill in window class structure with parameters that describe
-    // the main window.
-
-    ZeroMemory(&wc, sizeof wc);
-    wc.hInstance     = hInstance;
-    wc.lpszClassName = szAppName;
-    wc.lpfnWndProc   = (WNDPROC)WndProc;
-    wc.style         = CS_DBLCLKS|CS_VREDRAW|CS_HREDRAW;
-    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
-    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
-    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
-
-    if (FALSE == RegisterClass(&wc))
-        return 0;
-
-    // create the browser
-    hwnd = CreateWindow(
-        szAppName,
-        szTitle,
-        WS_OVERLAPPEDWINDOW|WS_VISIBLE,
-        CW_USEDEFAULT,
-        CW_USEDEFAULT,
-        360,//CW_USEDEFAULT,
-        240,//CW_USEDEFAULT,
-        0,
-        0,
-        hInstance,
-        0);
-
-    if (NULL == hwnd)
-        return 0;
-
-    // Main message loop:
-    while (GetMessage(&msg, NULL, 0, 0) > 0) {
-        TranslateMessage(&msg);
-        DispatchMessage(&msg);
-    }
-
-    return msg.wParam;
-}
-
-//+---------------------------------------------------------------------------
-
-//+---------------------------------------------------------------------------
-
-void CenterWindow(HWND hwnd_self)
-{
-    HWND hwnd_parent;
-    RECT rw_self, rc_parent, rw_parent;
-    int xpos, ypos;
-
-    hwnd_parent = GetParent(hwnd_self);
-    if (NULL == hwnd_parent)
-        hwnd_parent = GetDesktopWindow();
-
-    GetWindowRect(hwnd_parent, &rw_parent);
-    GetClientRect(hwnd_parent, &rc_parent);
-    GetWindowRect(hwnd_self, &rw_self);
-
-    xpos = rw_parent.left + (rc_parent.right + rw_self.left - rw_self.right) / 2;
-    ypos = rw_parent.top + (rc_parent.bottom + rw_self.top - rw_self.bottom) / 2;
-
-    SetWindowPos(
-        hwnd_self, NULL,
-        xpos, ypos, 0, 0,
-        SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE
-        );
-}
-
-//+---------------------------------------------------------------------------
diff --git a/tinyc/win32/include/_mingw.h b/tinyc/win32/include/_mingw.h
deleted file mode 100644
index 2fc979872..000000000
--- a/tinyc/win32/include/_mingw.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * _mingw.h
- *
- *  This file is for TinyCC and not part of the Mingw32 package.
- *
- *  THIS SOFTWARE IS NOT COPYRIGHTED
- *
- *  This source code is offered for use in the public domain. You may
- *  use, modify or distribute it freely.
- *
- *  This code is distributed in the hope that it will be useful but
- *  WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
- *  DISCLAIMED. This includes but is not limited to warranties of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
- */
-
-#ifndef __MINGW_H
-#define __MINGW_H
-
-/* some winapi files define these before including _mingw.h --> */
-#undef __cdecl
-#undef _X86_
-#undef WIN32
-/* <-- */
-
-#include <stddef.h>
-#include <stdarg.h>
-
-#define __int8 char
-#define __int16 short
-#define __int32 int
-#define __int64 long long
-#define _HAVE_INT64
-
-#define __cdecl
-#define __declspec(x) __attribute__((x))
-#define __unaligned __attribute__((packed))
-#define __fastcall __attribute__((fastcall))
-
-#define __MSVCRT__ 1
-#undef _MSVCRT_
-#define __MINGW_IMPORT extern __declspec(dllimport)
-#define __MINGW_ATTRIB_NORETURN
-#define __MINGW_ATTRIB_CONST
-#define __MINGW_ATTRIB_DEPRECATED
-#define __MINGW_ATTRIB_MALLOC
-#define __MINGW_ATTRIB_PURE
-#define __MINGW_ATTRIB_NONNULL(arg)
-#define __MINGW_NOTHROW
-#define __GNUC_VA_LIST
-
-#define _CRTIMP extern
-#define __CRT_INLINE extern __inline__
-
-#define _CRT_ALIGN(x) __attribute__((aligned(x)))
-#define DECLSPEC_ALIGN(x) __attribute__((aligned(x)))
-#define _CRT_PACKING 8
-#define __CRT_UNALIGNED
-#define _CONST_RETURN
-
-#ifndef _TRUNCATE
-#define _TRUNCATE ((size_t)-1)
-#endif
-
-#define __CRT_STRINGIZE(_Value) #_Value
-#define _CRT_STRINGIZE(_Value) __CRT_STRINGIZE(_Value)
-#define __CRT_WIDE(_String) L ## _String
-#define _CRT_WIDE(_String) __CRT_WIDE(_String)
-
-#ifdef _WIN64
-#define __stdcall
-#define _AMD64_ 1
-#define __x86_64 1
-#define _M_X64 100 /* Visual Studio */
-#define _M_AMD64 100 /* Visual Studio */
-#define USE_MINGW_SETJMP_TWO_ARGS
-#define mingw_getsp tinyc_getbp
-#define __TRY__
-#else
-#define __stdcall __attribute__((__stdcall__))
-#define _X86_ 1
-#define _M_IX86 300 /* Visual Studio */
-#define WIN32 1
-#define _USE_32BIT_TIME_T
-#ifdef __arm__
-#define __TRY__
-#else
-#define __TRY__ void __try__(void**), *_sehrec[6]; __try__(_sehrec);
-#endif
-#endif
-
-/* in stddef.h */
-#define _SIZE_T_DEFINED
-#define _SSIZE_T_DEFINED
-#define _PTRDIFF_T_DEFINED
-#define _WCHAR_T_DEFINED
-#define _UINTPTR_T_DEFINED
-#define _INTPTR_T_DEFINED
-#define _INTEGRAL_MAX_BITS 64
-
-#ifndef _TIME32_T_DEFINED
-#define _TIME32_T_DEFINED
-typedef long __time32_t;
-#endif
-
-#ifndef _TIME64_T_DEFINED
-#define _TIME64_T_DEFINED
-typedef long long __time64_t;
-#endif
-
-#ifndef _TIME_T_DEFINED
-#define _TIME_T_DEFINED
-#ifdef _USE_32BIT_TIME_T
-typedef __time32_t time_t;
-#else
-typedef __time64_t time_t;
-#endif
-#endif
-
-#ifndef _WCTYPE_T_DEFINED
-#define _WCTYPE_T_DEFINED
-typedef wchar_t wctype_t;
-#endif
-
-#ifndef _WINT_T
-#define _WINT_T
-typedef __WINT_TYPE__ wint_t;
-#endif
-
-typedef int errno_t;
-#define _ERRCODE_DEFINED
-
-typedef struct threadlocaleinfostruct *pthreadlocinfo;
-typedef struct threadmbcinfostruct *pthreadmbcinfo;
-typedef struct localeinfo_struct _locale_tstruct,*_locale_t;
-
-/* for winapi */
-#define _ANONYMOUS_UNION
-#define _ANONYMOUS_STRUCT
-#define DECLSPEC_NORETURN
-#define DECLARE_STDCALL_P(type) __stdcall type
-#define NOSERVICE 1
-#define NOMCX 1
-#define NOIME 1
-#define __INTRIN_H_
-#ifndef DUMMYUNIONNAME
-#  define DUMMYUNIONNAME
-#  define DUMMYUNIONNAME1
-#  define DUMMYUNIONNAME2
-#  define DUMMYUNIONNAME3
-#  define DUMMYUNIONNAME4
-#  define DUMMYUNIONNAME5
-#endif
-#ifndef DUMMYSTRUCTNAME
-#  define DUMMYSTRUCTNAME
-#endif
-#ifndef WINVER
-# define WINVER 0x0502
-#endif
-#ifndef _WIN32_WINNT
-# define _WIN32_WINNT 0x502
-#endif
-
-#define __C89_NAMELESS
-#define __MINGW_EXTENSION
-#define WINAPI_FAMILY_PARTITION(X) 1
-#define MINGW_HAS_SECURE_API
-
-#endif /* __MINGW_H */
diff --git a/tinyc/win32/include/assert.h b/tinyc/win32/include/assert.h
deleted file mode 100644
index 466d4571a..000000000
--- a/tinyc/win32/include/assert.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef __ASSERT_H_
-#define __ASSERT_H_
-
-#include <_mingw.h>
-#ifdef __cplusplus
-#include <stdlib.h>
-#endif
-
-#ifdef NDEBUG
-#ifndef assert
-#define assert(_Expression) ((void)0)
-#endif
-#else
-
-#ifndef _CRT_TERMINATE_DEFINED
-#define _CRT_TERMINATE_DEFINED
-  void __cdecl __MINGW_NOTHROW exit(int _Code) __MINGW_ATTRIB_NORETURN;
- _CRTIMP void __cdecl __MINGW_NOTHROW _exit(int _Code) __MINGW_ATTRIB_NORETURN;
-#if !defined __NO_ISOCEXT /* extern stub in static libmingwex.a */
-/* C99 function name */
-void __cdecl _Exit(int) __MINGW_ATTRIB_NORETURN;
-__CRT_INLINE __MINGW_ATTRIB_NORETURN void __cdecl _Exit(int status)
-{  _exit(status); }
-#endif
-
-#pragma push_macro("abort")
-#undef abort
-  void __cdecl __declspec(noreturn) abort(void);
-#pragma pop_macro("abort")
-
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-extern void __cdecl _wassert(const wchar_t *_Message,const wchar_t *_File,unsigned _Line);
-extern void __cdecl _assert(const char *, const char *, unsigned);
-
-#ifdef __cplusplus
-}
-#endif
-
-#ifndef assert
-//#define assert(_Expression) (void)((!!(_Expression)) || (_wassert(_CRT_WIDE(#_Expression),_CRT_WIDE(__FILE__),__LINE__),0))
-#define assert(e) ((e) ? (void)0 : _assert(#e, __FILE__, __LINE__))
-#endif
-
-#endif
-
-#endif
diff --git a/tinyc/win32/include/conio.h b/tinyc/win32/include/conio.h
deleted file mode 100644
index 39f779eb0..000000000
--- a/tinyc/win32/include/conio.h
+++ /dev/null
@@ -1,409 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_CONIO
-#define _INC_CONIO
-
-#include <_mingw.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  _CRTIMP char *_cgets(char *_Buffer);
-  _CRTIMP int __cdecl _cprintf(const char *_Format,...);
-  _CRTIMP int __cdecl _cputs(const char *_Str);
-  _CRTIMP int __cdecl _cscanf(const char *_Format,...);
-  _CRTIMP int __cdecl _cscanf_l(const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _getch(void);
-  _CRTIMP int __cdecl _getche(void);
-  _CRTIMP int __cdecl _vcprintf(const char *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _cprintf_p(const char *_Format,...);
-  _CRTIMP int __cdecl _vcprintf_p(const char *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _cprintf_l(const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vcprintf_l(const char *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _cprintf_p_l(const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vcprintf_p_l(const char *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _kbhit(void);
-
-#if defined(_X86_) && !defined(__x86_64)
-  int __cdecl _inp(unsigned short);
-  unsigned short __cdecl _inpw(unsigned short);
-  unsigned long __cdecl _inpd(unsigned short);
-  int __cdecl _outp(unsigned short,int);
-  unsigned short __cdecl _outpw(unsigned short,unsigned short);
-  unsigned long __cdecl _outpd(unsigned short,unsigned long);
-#endif
-
-  _CRTIMP int __cdecl _putch(int _Ch);
-  _CRTIMP int __cdecl _ungetch(int _Ch);
-  _CRTIMP int __cdecl _getch_nolock(void);
-  _CRTIMP int __cdecl _getche_nolock(void);
-  _CRTIMP int __cdecl _putch_nolock(int _Ch);
-  _CRTIMP int __cdecl _ungetch_nolock(int _Ch);
-
-#ifndef _WCONIO_DEFINED
-#define _WCONIO_DEFINED
-
-#ifndef WEOF
-#define WEOF (wint_t)(0xFFFF)
-#endif
-
-  _CRTIMP wchar_t *_cgetws(wchar_t *_Buffer);
-  _CRTIMP wint_t __cdecl _getwch(void);
-  _CRTIMP wint_t __cdecl _getwche(void);
-  _CRTIMP wint_t __cdecl _putwch(wchar_t _WCh);
-  _CRTIMP wint_t __cdecl _ungetwch(wint_t _WCh);
-  _CRTIMP int __cdecl _cputws(const wchar_t *_String);
-  _CRTIMP int __cdecl _cwprintf(const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _cwscanf(const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _cwscanf_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vcwprintf(const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _cwprintf_p(const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _vcwprintf_p(const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _cwprintf_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vcwprintf_l(const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _cwprintf_p_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vcwprintf_p_l(const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP wint_t __cdecl _putwch_nolock(wchar_t _WCh);
-  _CRTIMP wint_t __cdecl _getwch_nolock(void);
-  _CRTIMP wint_t __cdecl _getwche_nolock(void);
-  _CRTIMP wint_t __cdecl _ungetwch_nolock(wint_t _WCh);
-#endif
-
-#ifndef	NO_OLDNAMES
-  char *__cdecl cgets(char *_Buffer);
-  int __cdecl cprintf(const char *_Format,...);
-  int __cdecl cputs(const char *_Str);
-  int __cdecl cscanf(const char *_Format,...);
-  int __cdecl getch(void);
-  int __cdecl getche(void);
-  int __cdecl kbhit(void);
-  int __cdecl putch(int _Ch);
-  int __cdecl ungetch(int _Ch);
-
-#if (defined(_X86_) && !defined(__x86_64))
-  int __cdecl inp(unsigned short);
-  unsigned short __cdecl inpw(unsigned short);
-  int __cdecl outp(unsigned short,int);
-  unsigned short __cdecl outpw(unsigned short,unsigned short);
-#endif
-
-  /* I/O intrin functions.  */
-  __CRT_INLINE unsigned char __inbyte(unsigned short Port)
-  {
-      unsigned char value;
-      __asm__ __volatile__ ("inb %w1,%b0"
-          : "=a" (value)
-          : "Nd" (Port));
-      return value;
-  }
-  __CRT_INLINE unsigned short __inword(unsigned short Port)
-  {
-      unsigned short value;
-      __asm__ __volatile__ ("inw %w1,%w0"
-          : "=a" (value)
-          : "Nd" (Port));
-      return value;
-  }
-  __CRT_INLINE unsigned long __indword(unsigned short Port)
-  {
-      unsigned long value;
-      __asm__ __volatile__ ("inl %w1,%0"
-          : "=a" (value)
-          : "Nd" (Port));
-      return value;
-  }
-  __CRT_INLINE void __outbyte(unsigned short Port,unsigned char Data)
-  {
-      __asm__ __volatile__ ("outb %b0,%w1"
-          :
-          : "a" (Data), "Nd" (Port));
-  }
-  __CRT_INLINE void __outword(unsigned short Port,unsigned short Data)
-  {
-      __asm__ __volatile__ ("outw %w0,%w1"
-          :
-          : "a" (Data), "Nd" (Port));
-  }
-  __CRT_INLINE void __outdword(unsigned short Port,unsigned long Data)
-  {
-      __asm__ __volatile__ ("outl %0,%w1"
-          :
-          : "a" (Data), "Nd" (Port));
-  }
-  __CRT_INLINE void __inbytestring(unsigned short Port,unsigned char *Buffer,unsigned long Count)
-  {
-	__asm__ __volatile__ (
-		"cld ; rep ; insb " 
-		: "=D" (Buffer), "=c" (Count)
-		: "d"(Port), "0"(Buffer), "1" (Count)
-		);
-  }
-  __CRT_INLINE void __inwordstring(unsigned short Port,unsigned short *Buffer,unsigned long Count)
-  {
-	__asm__ __volatile__ (
-		"cld ; rep ; insw " 
-		: "=D" (Buffer), "=c" (Count)
-		: "d"(Port), "0"(Buffer), "1" (Count)
-		);
-  }
-  __CRT_INLINE void __indwordstring(unsigned short Port,unsigned long *Buffer,unsigned long Count)
-  {
-	__asm__ __volatile__ (
-		"cld ; rep ; insl " 
-		: "=D" (Buffer), "=c" (Count)
-		: "d"(Port), "0"(Buffer), "1" (Count)
-		);
-  }
-
-  __CRT_INLINE void __outbytestring(unsigned short Port,unsigned char *Buffer,unsigned long Count)
-  {
-      __asm__ __volatile__ (
-          "cld ; rep ; outsb " 
-          : "=S" (Buffer), "=c" (Count)
-          : "d"(Port), "0"(Buffer), "1" (Count)
-          );
-  }
-  __CRT_INLINE void __outwordstring(unsigned short Port,unsigned short *Buffer,unsigned long Count)
-  {
-      __asm__ __volatile__ (
-          "cld ; rep ; outsw " 
-          : "=S" (Buffer), "=c" (Count)
-          : "d"(Port), "0"(Buffer), "1" (Count)
-          );
-  }
-  __CRT_INLINE void __outdwordstring(unsigned short Port,unsigned long *Buffer,unsigned long Count)
-  {
-      __asm__ __volatile__ (
-          "cld ; rep ; outsl " 
-          : "=S" (Buffer), "=c" (Count)
-          : "d"(Port), "0"(Buffer), "1" (Count)
-          );
-  }
-
-  __CRT_INLINE unsigned __int64 __readcr0(void)
-  {
-      unsigned __int64 value;
-      __asm__ __volatile__ (
-          "mov %%cr0, %[value]" 
-          : [value] "=q" (value));
-      return value;
-  }
- 
-  /* Register sizes are different between 32/64 bit mode. So we have to do this for _WIN64 and _WIN32
-     separately.  */
- 
-#ifdef _WIN64
-  __CRT_INLINE void __writecr0(unsigned __int64 Data)
-  {
-   __asm__ __volatile__ (
-       "mov %[Data], %%cr0"
-       :
-       : [Data] "q" (Data)
-       : "memory");
-  }
- 
-  __CRT_INLINE unsigned __int64 __readcr2(void)
-  {
-      unsigned __int64 value;
-      __asm__ __volatile__ (
-          "mov %%cr2, %[value]" 
-          : [value] "=q" (value));
-      return value;
-  }
-
- __CRT_INLINE void __writecr2(unsigned __int64 Data)
- {
-   __asm__ __volatile__ (
-       "mov %[Data], %%cr2"
-       :
-       : [Data] "q" (Data)
-       : "memory");
- }
- 
-  __CRT_INLINE unsigned __int64 __readcr3(void)
-  {
-      unsigned __int64 value;
-      __asm__ __volatile__ (
-          "mov %%cr3, %[value]" 
-          : [value] "=q" (value));
-      return value;
-  }
-
- __CRT_INLINE void __writecr3(unsigned __int64 Data)
- {
-   __asm__ __volatile__ (
-       "mov %[Data], %%cr3"
-       :
-       : [Data] "q" (Data)
-       : "memory");
- }
- 
-  __CRT_INLINE unsigned __int64 __readcr4(void)
-  {
-      unsigned __int64 value;
-      __asm__ __volatile__ (
-          "mov %%cr4, %[value]" 
-          : [value] "=q" (value));
-      return value;
-  }
-
- __CRT_INLINE void __writecr4(unsigned __int64 Data)
- {
-     __asm__ __volatile__ (
-         "mov %[Data], %%cr4"
-         :
-         : [Data] "q" (Data)
-         : "memory");
- }
- 
-  __CRT_INLINE unsigned __int64 __readcr8(void)
-  {
-      unsigned __int64 value;
-      __asm__ __volatile__ (
-          "mov %%cr8, %[value]" 
-          : [value] "=q" (value));
-      return value;
-  }
-
- __CRT_INLINE void __writecr8(unsigned __int64 Data)
- {
-   __asm__ __volatile__ (
-       "mov %[Data], %%cr8"
-       :
-       : [Data] "q" (Data)
-       : "memory");
- }
- 
-#elif defined(_WIN32)
-
-  __CRT_INLINE void __writecr0(unsigned Data)
-  {
-    __asm__ __volatile__ (
-       "mov %[Data], %%cr0"
-       :
-       : [Data] "q" (Data)
-       : "memory");
-  }
- 
-  __CRT_INLINE unsigned long __readcr2(void)
-  {
-      unsigned long value;
-      __asm__ __volatile__ (
-          "mov %%cr2, %[value]" 
-          : [value] "=q" (value));
-      return value;
-  }
-
- __CRT_INLINE void __writecr2(unsigned Data)
- {
-   __asm__ __volatile__ (
-       "mov %[Data], %%cr2"
-       :
-       : [Data] "q" (Data)
-       : "memory");
- }
- 
-  __CRT_INLINE unsigned long __readcr3(void)
-  {
-      unsigned long value;
-      __asm__ __volatile__ (
-          "mov %%cr3, %[value]" 
-          : [value] "=q" (value));
-      return value;
-  }
-
- __CRT_INLINE void __writecr3(unsigned Data)
- {
-   __asm__ __volatile__ (
-       "mov %[Data], %%cr3"
-       :
-       : [Data] "q" (Data)
-       : "memory");
- }
- 
-  __CRT_INLINE unsigned long __readcr4(void)
-  {
-      unsigned long value;
-      __asm__ __volatile__ (
-          "mov %%cr4, %[value]" 
-          : [value] "=q" (value));
-      return value;
-  }
-
- __CRT_INLINE void __writecr4(unsigned Data)
- {
-     __asm__ __volatile__ (
-         "mov %[Data], %%cr4"
-         :
-         : [Data] "q" (Data)
-         : "memory");
- }
- 
- __CRT_INLINE unsigned long __readcr8(void)
- {
-   unsigned long value;      __asm__ __volatile__ (
-          "mov %%cr8, %[value]" 
-          : [value] "=q" (value));
-     return value;
- }
-
- __CRT_INLINE void __writecr8(unsigned Data)
- {
-   __asm__ __volatile__ (
-       "mov %[Data], %%cr8"
-       :
-       : [Data] "q" (Data)
-       : "memory");
- }
- 
-#endif
-
-  __CRT_INLINE unsigned __int64 __readmsr(unsigned long msr)
-  {
-      unsigned __int64 val1, val2;
-       __asm__ __volatile__(
-           "rdmsr"
-           : "=a" (val1), "=d" (val2)
-           : "c" (msr));
-      return val1 | (val2 << 32);
-  }
-
- __CRT_INLINE void __writemsr (unsigned long msr, unsigned __int64 Value)
- {
-    unsigned long val1 = Value, val2 = Value >> 32;
-   __asm__ __volatile__ (
-       "wrmsr"
-       :
-       : "c" (msr), "a" (val1), "d" (val2));
- }
- 
-  __CRT_INLINE unsigned __int64 __rdtsc(void)
-  {
-      unsigned __int64 val1, val2;
-      __asm__ __volatile__ (
-          "rdtsc" 
-          : "=a" (val1), "=d" (val2));
-      return val1 | (val2 << 32);
-  }
-
-  __CRT_INLINE void __cpuid(int CPUInfo[4], int InfoType)
-  {
-      __asm__ __volatile__ (
-          "cpuid"
-          : "=a" (CPUInfo [0]), "=b" (CPUInfo [1]), "=c" (CPUInfo [2]), "=d" (CPUInfo [3])
-          : "a" (InfoType));
-  }
-
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#include <sec_api/conio_s.h>
-
-#endif
diff --git a/tinyc/win32/include/ctype.h b/tinyc/win32/include/ctype.h
deleted file mode 100644
index 7e9010026..000000000
--- a/tinyc/win32/include/ctype.h
+++ /dev/null
@@ -1,281 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_CTYPE
-#define _INC_CTYPE
-
-#include <_mingw.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef WEOF
-#define WEOF (wint_t)(0xFFFF)
-#endif
-
-#ifndef _CRT_CTYPEDATA_DEFINED
-#define _CRT_CTYPEDATA_DEFINED
-#ifndef _CTYPE_DISABLE_MACROS
-
-#ifndef __PCTYPE_FUNC
-#define __PCTYPE_FUNC __pctype_func()
-#ifdef _MSVCRT_
-#define __pctype_func()	(_pctype)
-#else
-#define __pctype_func()	(*_imp___pctype)
-#endif
-#endif
-
-#ifndef _pctype
-#ifdef _MSVCRT_
-  extern unsigned short *_pctype;
-#else
-  extern unsigned short **_imp___pctype;
-#define _pctype (*_imp___pctype)
-#endif
-#endif
-
-#endif
-#endif
-
-#ifndef _CRT_WCTYPEDATA_DEFINED
-#define _CRT_WCTYPEDATA_DEFINED
-#ifndef _CTYPE_DISABLE_MACROS
-#ifndef _wctype
-#ifdef _MSVCRT_
-  extern unsigned short *_wctype;
-#else
-  extern unsigned short **_imp___wctype;
-#define _wctype (*_imp___wctype)
-#endif
-#endif
-#ifdef _MSVCRT_
-#define __pwctype_func() (_pwctype)
-#ifndef _pwctype
-  extern unsigned short *_pwctype;
-#endif
-#else
-#define __pwctype_func() (*_imp___pwctype)
-#ifndef _pwctype
-  extern unsigned short **_imp___pwctype;
-#define _pwctype (*_imp___pwctype)
-#endif
-#endif
-#endif
-#endif
-
-  /* CRT stuff */
-#if 1
-  extern const unsigned char __newclmap[];
-  extern const unsigned char __newcumap[];
-  extern pthreadlocinfo __ptlocinfo;
-  extern pthreadmbcinfo __ptmbcinfo;
-  extern int __globallocalestatus;
-  extern int __locale_changed;
-  extern struct threadlocaleinfostruct __initiallocinfo;
-  extern _locale_tstruct __initiallocalestructinfo;
-  pthreadlocinfo __cdecl __updatetlocinfo(void);
-  pthreadmbcinfo __cdecl __updatetmbcinfo(void);
-#endif
-
-#define _UPPER 0x1
-#define _LOWER 0x2
-#define _DIGIT 0x4
-#define _SPACE 0x8
-
-#define _PUNCT 0x10
-#define _CONTROL 0x20
-#define _BLANK 0x40
-#define _HEX 0x80
-
-#define _LEADBYTE 0x8000
-#define _ALPHA (0x0100|_UPPER|_LOWER)
-
-#ifndef _CTYPE_DEFINED
-#define _CTYPE_DEFINED
-
-  _CRTIMP int __cdecl _isctype(int _C,int _Type);
-  _CRTIMP int __cdecl _isctype_l(int _C,int _Type,_locale_t _Locale);
-  _CRTIMP int __cdecl isalpha(int _C);
-  _CRTIMP int __cdecl _isalpha_l(int _C,_locale_t _Locale);
-  _CRTIMP int __cdecl isupper(int _C);
-  _CRTIMP int __cdecl _isupper_l(int _C,_locale_t _Locale);
-  _CRTIMP int __cdecl islower(int _C);
-  _CRTIMP int __cdecl _islower_l(int _C,_locale_t _Locale);
-  _CRTIMP int __cdecl isdigit(int _C);
-  _CRTIMP int __cdecl _isdigit_l(int _C,_locale_t _Locale);
-  _CRTIMP int __cdecl isxdigit(int _C);
-  _CRTIMP int __cdecl _isxdigit_l(int _C,_locale_t _Locale);
-  _CRTIMP int __cdecl isspace(int _C);
-  _CRTIMP int __cdecl _isspace_l(int _C,_locale_t _Locale);
-  _CRTIMP int __cdecl ispunct(int _C);
-  _CRTIMP int __cdecl _ispunct_l(int _C,_locale_t _Locale);
-  _CRTIMP int __cdecl isalnum(int _C);
-  _CRTIMP int __cdecl _isalnum_l(int _C,_locale_t _Locale);
-  _CRTIMP int __cdecl isprint(int _C);
-  _CRTIMP int __cdecl _isprint_l(int _C,_locale_t _Locale);
-  _CRTIMP int __cdecl isgraph(int _C);
-  _CRTIMP int __cdecl _isgraph_l(int _C,_locale_t _Locale);
-  _CRTIMP int __cdecl iscntrl(int _C);
-  _CRTIMP int __cdecl _iscntrl_l(int _C,_locale_t _Locale);
-  _CRTIMP int __cdecl toupper(int _C);
-  _CRTIMP int __cdecl tolower(int _C);
-  _CRTIMP int __cdecl _tolower(int _C);
-  _CRTIMP int __cdecl _tolower_l(int _C,_locale_t _Locale);
-  _CRTIMP int __cdecl _toupper(int _C);
-  _CRTIMP int __cdecl _toupper_l(int _C,_locale_t _Locale);
-  _CRTIMP int __cdecl __isascii(int _C);
-  _CRTIMP int __cdecl __toascii(int _C);
-  _CRTIMP int __cdecl __iscsymf(int _C);
-  _CRTIMP int __cdecl __iscsym(int _C);
-
-#if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || !defined (NO_OLDNAMES)
-int __cdecl isblank(int _C);
-#endif
-#endif
-
-#ifndef _WCTYPE_DEFINED
-#define _WCTYPE_DEFINED
-
-  int __cdecl iswalpha(wint_t _C);
-  _CRTIMP int __cdecl _iswalpha_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswupper(wint_t _C);
-  _CRTIMP int __cdecl _iswupper_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswlower(wint_t _C);
-  _CRTIMP int __cdecl _iswlower_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswdigit(wint_t _C);
-  _CRTIMP int __cdecl _iswdigit_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswxdigit(wint_t _C);
-  _CRTIMP int __cdecl _iswxdigit_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswspace(wint_t _C);
-  _CRTIMP int __cdecl _iswspace_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswpunct(wint_t _C);
-  _CRTIMP int __cdecl _iswpunct_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswalnum(wint_t _C);
-  _CRTIMP int __cdecl _iswalnum_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswprint(wint_t _C);
-  _CRTIMP int __cdecl _iswprint_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswgraph(wint_t _C);
-  _CRTIMP int __cdecl _iswgraph_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswcntrl(wint_t _C);
-  _CRTIMP int __cdecl _iswcntrl_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswascii(wint_t _C);
-  int __cdecl isleadbyte(int _C);
-  _CRTIMP int __cdecl _isleadbyte_l(int _C,_locale_t _Locale);
-  wint_t __cdecl towupper(wint_t _C);
-  _CRTIMP wint_t __cdecl _towupper_l(wint_t _C,_locale_t _Locale);
-  wint_t __cdecl towlower(wint_t _C);
-  _CRTIMP wint_t __cdecl _towlower_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswctype(wint_t _C,wctype_t _Type);
-  _CRTIMP int __cdecl _iswctype_l(wint_t _C,wctype_t _Type,_locale_t _Locale);
-  _CRTIMP int __cdecl __iswcsymf(wint_t _C);
-  _CRTIMP int __cdecl _iswcsymf_l(wint_t _C,_locale_t _Locale);
-  _CRTIMP int __cdecl __iswcsym(wint_t _C);
-  _CRTIMP int __cdecl _iswcsym_l(wint_t _C,_locale_t _Locale);
-  int __cdecl is_wctype(wint_t _C,wctype_t _Type);
-
-#if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || !defined (NO_OLDNAMES)
-int __cdecl iswblank(wint_t _C);
-#endif
-#endif
-
-#ifndef _CTYPE_DISABLE_MACROS
-
-#ifndef MB_CUR_MAX
-#define MB_CUR_MAX ___mb_cur_max_func()
-#ifndef __mb_cur_max
-#ifdef _MSVCRT_
-  extern int __mb_cur_max;
-#else
-#define __mb_cur_max	(*_imp____mb_cur_max)
-  extern int *_imp____mb_cur_max;
-#endif
-#endif
-#ifdef _MSVCRT_
-#define ___mb_cur_max_func() (__mb_cur_max)
-#else
-#define ___mb_cur_max_func() (*_imp____mb_cur_max)
-#endif
-#endif
-
-#define __chvalidchk(a,b) (__PCTYPE_FUNC[(a)] & (b))
-#define _chvalidchk_l(_Char,_Flag,_Locale) (!_Locale ? __chvalidchk(_Char,_Flag) : ((_locale_t)_Locale)->locinfo->pctype[_Char] & (_Flag))
-#define _ischartype_l(_Char,_Flag,_Locale) (((_Locale)!=NULL && (((_locale_t)(_Locale))->locinfo->mb_cur_max) > 1) ? _isctype_l(_Char,(_Flag),_Locale) : _chvalidchk_l(_Char,_Flag,_Locale))
-#define _isalpha_l(_Char,_Locale) _ischartype_l(_Char,_ALPHA,_Locale)
-#define _isupper_l(_Char,_Locale) _ischartype_l(_Char,_UPPER,_Locale)
-#define _islower_l(_Char,_Locale) _ischartype_l(_Char,_LOWER,_Locale)
-#define _isdigit_l(_Char,_Locale) _ischartype_l(_Char,_DIGIT,_Locale)
-#define _isxdigit_l(_Char,_Locale) _ischartype_l(_Char,_HEX,_Locale)
-#define _isspace_l(_Char,_Locale) _ischartype_l(_Char,_SPACE,_Locale)
-#define _ispunct_l(_Char,_Locale) _ischartype_l(_Char,_PUNCT,_Locale)
-#define _isalnum_l(_Char,_Locale) _ischartype_l(_Char,_ALPHA|_DIGIT,_Locale)
-#define _isprint_l(_Char,_Locale) _ischartype_l(_Char,_BLANK|_PUNCT|_ALPHA|_DIGIT,_Locale)
-#define _isgraph_l(_Char,_Locale) _ischartype_l(_Char,_PUNCT|_ALPHA|_DIGIT,_Locale)
-#define _iscntrl_l(_Char,_Locale) _ischartype_l(_Char,_CONTROL,_Locale)
-#define _tolower(_Char) ((_Char)-'A'+'a')
-#define _toupper(_Char) ((_Char)-'a'+'A')
-#define __isascii(_Char) ((unsigned)(_Char) < 0x80)
-#define __toascii(_Char) ((_Char) & 0x7f)
-
-#ifndef _WCTYPE_INLINE_DEFINED
-#define _WCTYPE_INLINE_DEFINED
-
-#undef _CRT_WCTYPE_NOINLINE
-#ifndef __cplusplus
-#define iswalpha(_c) (iswctype(_c,_ALPHA))
-#define iswupper(_c) (iswctype(_c,_UPPER))
-#define iswlower(_c) (iswctype(_c,_LOWER))
-#define iswdigit(_c) (iswctype(_c,_DIGIT))
-#define iswxdigit(_c) (iswctype(_c,_HEX))
-#define iswspace(_c) (iswctype(_c,_SPACE))
-#define iswpunct(_c) (iswctype(_c,_PUNCT))
-#define iswalnum(_c) (iswctype(_c,_ALPHA|_DIGIT))
-#define iswprint(_c) (iswctype(_c,_BLANK|_PUNCT|_ALPHA|_DIGIT))
-#define iswgraph(_c) (iswctype(_c,_PUNCT|_ALPHA|_DIGIT))
-#define iswcntrl(_c) (iswctype(_c,_CONTROL))
-#define iswascii(_c) ((unsigned)(_c) < 0x80)
-#define _iswalpha_l(_c,_p) (_iswctype_l(_c,_ALPHA,_p))
-#define _iswupper_l(_c,_p) (_iswctype_l(_c,_UPPER,_p))
-#define _iswlower_l(_c,_p) (_iswctype_l(_c,_LOWER,_p))
-#define _iswdigit_l(_c,_p) (_iswctype_l(_c,_DIGIT,_p))
-#define _iswxdigit_l(_c,_p) (_iswctype_l(_c,_HEX,_p))
-#define _iswspace_l(_c,_p) (_iswctype_l(_c,_SPACE,_p))
-#define _iswpunct_l(_c,_p) (_iswctype_l(_c,_PUNCT,_p))
-#define _iswalnum_l(_c,_p) (_iswctype_l(_c,_ALPHA|_DIGIT,_p))
-#define _iswprint_l(_c,_p) (_iswctype_l(_c,_BLANK|_PUNCT|_ALPHA|_DIGIT,_p))
-#define _iswgraph_l(_c,_p) (_iswctype_l(_c,_PUNCT|_ALPHA|_DIGIT,_p))
-#define _iswcntrl_l(_c,_p) (_iswctype_l(_c,_CONTROL,_p))
-#endif
-#endif
-
-#define __iscsymf(_c) (isalpha(_c) || ((_c)=='_'))
-#define __iscsym(_c) (isalnum(_c) || ((_c)=='_'))
-#define __iswcsymf(_c) (iswalpha(_c) || ((_c)=='_'))
-#define __iswcsym(_c) (iswalnum(_c) || ((_c)=='_'))
-#define _iscsymf_l(_c,_p) (_isalpha_l(_c,_p) || ((_c)=='_'))
-#define _iscsym_l(_c,_p) (_isalnum_l(_c,_p) || ((_c)=='_'))
-#define _iswcsymf_l(_c,_p) (_iswalpha_l(_c,_p) || ((_c)=='_'))
-#define _iswcsym_l(_c,_p) (_iswalnum_l(_c,_p) || ((_c)=='_'))
-#endif
-
-#ifndef	NO_OLDNAMES
-#ifndef _CTYPE_DEFINED
-  int __cdecl isascii(int _C);
-  int __cdecl toascii(int _C);
-  int __cdecl iscsymf(int _C);
-  int __cdecl iscsym(int _C);
-#else
-#define isascii __isascii
-#define toascii __toascii
-#define iscsymf __iscsymf
-#define iscsym __iscsym
-#endif
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/tinyc/win32/include/dir.h b/tinyc/win32/include/dir.h
deleted file mode 100644
index f38f750d2..000000000
--- a/tinyc/win32/include/dir.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-/* 
- * dir.h
- *
- * This file OBSOLESCENT and only provided for backward compatibility.
- * Please use io.h instead.
- *
- * This file is part of the Mingw32 package.
- *
- * Contributors:
- *  Created by Colin Peters <colin@bird.fu.is.saga-u.ac.jp>
- *             Mumit Khan <khan@xraylith.wisc.edu>
- *
- *  THIS SOFTWARE IS NOT COPYRIGHTED
- *
- *  This source code is offered for use in the public domain. You may
- *  use, modify or distribute it freely.
- *
- *  This code is distributed in the hope that it will be useful but
- *  WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
- *  DISCLAIMED. This includes but is not limited to warranties of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
- */
-
-#include <io.h>
-
diff --git a/tinyc/win32/include/direct.h b/tinyc/win32/include/direct.h
deleted file mode 100644
index 99ce69db7..000000000
--- a/tinyc/win32/include/direct.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_DIRECT
-#define _INC_DIRECT
-
-#include <_mingw.h>
-#include <io.h>
-
-#pragma pack(push,_CRT_PACKING)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _DISKFREE_T_DEFINED
-#define _DISKFREE_T_DEFINED
-  struct _diskfree_t {
-    unsigned total_clusters;
-    unsigned avail_clusters;
-    unsigned sectors_per_cluster;
-    unsigned bytes_per_sector;
-  };
-#endif
-
-  _CRTIMP char *__cdecl _getcwd(char *_DstBuf,int _SizeInBytes);
-  _CRTIMP char *__cdecl _getdcwd(int _Drive,char *_DstBuf,int _SizeInBytes);
-  char *__cdecl _getdcwd_nolock(int _Drive,char *_DstBuf,int _SizeInBytes);
-  _CRTIMP int __cdecl _chdir(const char *_Path);
-  _CRTIMP int __cdecl _mkdir(const char *_Path);
-  _CRTIMP int __cdecl _rmdir(const char *_Path);
-  _CRTIMP int __cdecl _chdrive(int _Drive);
-  _CRTIMP int __cdecl _getdrive(void);
-  _CRTIMP unsigned long __cdecl _getdrives(void);
-
-#ifndef _GETDISKFREE_DEFINED
-#define _GETDISKFREE_DEFINED
-  _CRTIMP unsigned __cdecl _getdiskfree(unsigned _Drive,struct _diskfree_t *_DiskFree);
-#endif
-
-#ifndef _WDIRECT_DEFINED
-#define _WDIRECT_DEFINED
-  _CRTIMP wchar_t *__cdecl _wgetcwd(wchar_t *_DstBuf,int _SizeInWords);
-  _CRTIMP wchar_t *__cdecl _wgetdcwd(int _Drive,wchar_t *_DstBuf,int _SizeInWords);
-  wchar_t *__cdecl _wgetdcwd_nolock(int _Drive,wchar_t *_DstBuf,int _SizeInWords);
-  _CRTIMP int __cdecl _wchdir(const wchar_t *_Path);
-  _CRTIMP int __cdecl _wmkdir(const wchar_t *_Path);
-  _CRTIMP int __cdecl _wrmdir(const wchar_t *_Path);
-#endif
-
-#ifndef	NO_OLDNAMES
-
-#define diskfree_t _diskfree_t
-
-  char *__cdecl getcwd(char *_DstBuf,int _SizeInBytes);
-  int __cdecl chdir(const char *_Path);
-  int __cdecl mkdir(const char *_Path);
-  int __cdecl rmdir(const char *_Path);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#pragma pack(pop)
-#endif
diff --git a/tinyc/win32/include/dirent.h b/tinyc/win32/include/dirent.h
deleted file mode 100644
index cd31f59e0..000000000
--- a/tinyc/win32/include/dirent.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-/* All the headers include this file. */
-#include <_mingw.h>
-
-#ifndef	__STRICT_ANSI__
-
-#ifndef _DIRENT_H_
-#define _DIRENT_H_
-
-
-#pragma pack(push,_CRT_PACKING)
-
-#include <io.h>
-
-#ifndef RC_INVOKED
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  struct dirent
-  {
-    long		d_ino;		/* Always zero. */
-    unsigned short	d_reclen;	/* Always zero. */
-    unsigned short	d_namlen;	/* Length of name in d_name. */
-    char*		d_name;		/* File name. */
-    /* NOTE: The name in the dirent structure points to the name in the
-    *       finddata_t structure in the DIR. */
-  };
-
-  /*
-  * This is an internal data structure. Good programmers will not use it
-  * except as an argument to one of the functions below.
-  * dd_stat field is now int (was short in older versions).
-  */
-  typedef struct
-  {
-    /* disk transfer area for this dir */
-    struct _finddata_t	dd_dta;
-
-    /* dirent struct to return from dir (NOTE: this makes this thread
-    * safe as long as only one thread uses a particular DIR struct at
-    * a time) */
-    struct dirent		dd_dir;
-
-    /* _findnext handle */
-    long			dd_handle;
-
-    /*
-    * Status of search:
-    *   0 = not started yet (next entry to read is first entry)
-    *  -1 = off the end
-    *   positive = 0 based index of next entry
-    */
-    int			dd_stat;
-
-    /* given path for dir with search pattern (struct is extended) */
-    char			dd_name[1];
-  } DIR;
-
-  DIR* __cdecl opendir (const char*);
-  struct dirent* __cdecl readdir (DIR*);
-  int __cdecl closedir (DIR*);
-  void __cdecl rewinddir (DIR*);
-  long __cdecl telldir (DIR*);
-  void __cdecl seekdir (DIR*, long);
-
-
-  /* wide char versions */
-
-  struct _wdirent
-  {
-    long		d_ino;		/* Always zero. */
-    unsigned short	d_reclen;	/* Always zero. */
-    unsigned short	d_namlen;	/* Length of name in d_name. */
-    wchar_t*	d_name;		/* File name. */
-    /* NOTE: The name in the dirent structure points to the name in the	 *       wfinddata_t structure in the _WDIR. */
-  };
-
-  /*
-  * This is an internal data structure. Good programmers will not use it
-  * except as an argument to one of the functions below.
-  */
-  typedef struct
-  {
-    /* disk transfer area for this dir */
-    struct _wfinddata_t	dd_dta;
-
-    /* dirent struct to return from dir (NOTE: this makes this thread
-    * safe as long as only one thread uses a particular DIR struct at
-    * a time) */
-    struct _wdirent		dd_dir;
-
-    /* _findnext handle */
-    long			dd_handle;
-
-    /*
-    * Status of search:
-    *   0 = not started yet (next entry to read is first entry)
-    *  -1 = off the end
-    *   positive = 0 based index of next entry
-    */
-    int			dd_stat;
-
-    /* given path for dir with search pattern (struct is extended) */
-    wchar_t			dd_name[1];
-  } _WDIR;
-
-
-
-  _WDIR* __cdecl _wopendir (const wchar_t*);
-  struct _wdirent*  __cdecl _wreaddir (_WDIR*);
-  int __cdecl _wclosedir (_WDIR*);
-  void __cdecl _wrewinddir (_WDIR*);
-  long __cdecl _wtelldir (_WDIR*);
-  void __cdecl _wseekdir (_WDIR*, long);
-
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* Not RC_INVOKED */
-
-#pragma pack(pop)
-
-#endif	/* Not _DIRENT_H_ */
-
-
-#endif	/* Not __STRICT_ANSI__ */
-
diff --git a/tinyc/win32/include/dos.h b/tinyc/win32/include/dos.h
deleted file mode 100644
index 294e8fe1b..000000000
--- a/tinyc/win32/include/dos.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_DOS
-#define _INC_DOS
-
-#include <_mingw.h>
-#include <io.h>
-
-#pragma pack(push,_CRT_PACKING)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _DISKFREE_T_DEFINED
-#define _DISKFREE_T_DEFINED
-
-  struct _diskfree_t {
-    unsigned total_clusters;
-    unsigned avail_clusters;
-    unsigned sectors_per_cluster;
-    unsigned bytes_per_sector;
-  };
-#endif
-
-#define _A_NORMAL 0x00
-#define _A_RDONLY 0x01
-#define _A_HIDDEN 0x02
-#define _A_SYSTEM 0x04
-#define _A_SUBDIR 0x10
-#define _A_ARCH 0x20
-
-#ifndef _GETDISKFREE_DEFINED
-#define _GETDISKFREE_DEFINED
-  _CRTIMP unsigned __cdecl _getdiskfree(unsigned _Drive,struct _diskfree_t *_DiskFree);
-#endif
-
-#if (defined(_X86_) && !defined(__x86_64))
-  void __cdecl _disable(void);
-  void __cdecl _enable(void);
-#endif
-
-#ifndef	NO_OLDNAMES
-#define diskfree_t _diskfree_t
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#pragma pack(pop)
-#endif
diff --git a/tinyc/win32/include/errno.h b/tinyc/win32/include/errno.h
deleted file mode 100644
index c2df01581..000000000
--- a/tinyc/win32/include/errno.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_ERRNO
-#define _INC_ERRNO
-
-#include <_mingw.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _CRT_ERRNO_DEFINED
-#define _CRT_ERRNO_DEFINED
-  _CRTIMP extern int *__cdecl _errno(void);
-#define errno (*_errno())
-
-  errno_t __cdecl _set_errno(int _Value);
-  errno_t __cdecl _get_errno(int *_Value);
-#endif
-
-#define EPERM 1
-#define ENOENT 2
-#define ESRCH 3
-#define EINTR 4
-#define EIO 5
-#define ENXIO 6
-#define E2BIG 7
-#define ENOEXEC 8
-#define EBADF 9
-#define ECHILD 10
-#define EAGAIN 11
-#define ENOMEM 12
-#define EACCES 13
-#define EFAULT 14
-#define EBUSY 16
-#define EEXIST 17
-#define EXDEV 18
-#define ENODEV 19
-#define ENOTDIR 20
-#define EISDIR 21
-#define ENFILE 23
-#define EMFILE 24
-#define ENOTTY 25
-#define EFBIG 27
-#define ENOSPC 28
-#define ESPIPE 29
-#define EROFS 30
-#define EMLINK 31
-#define EPIPE 32
-#define EDOM 33
-#define EDEADLK 36
-#define ENAMETOOLONG 38
-#define ENOLCK 39
-#define ENOSYS 40
-#define ENOTEMPTY 41
-
-#ifndef RC_INVOKED
-#if !defined(_SECURECRT_ERRCODE_VALUES_DEFINED)
-#define _SECURECRT_ERRCODE_VALUES_DEFINED
-#define EINVAL 22
-#define ERANGE 34
-#define EILSEQ 42
-#define STRUNCATE 80
-#endif
-#endif
-
-#define EDEADLOCK EDEADLK
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/tinyc/win32/include/excpt.h b/tinyc/win32/include/excpt.h
deleted file mode 100644
index 26cc9437c..000000000
--- a/tinyc/win32/include/excpt.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_EXCPT
-#define _INC_EXCPT
-
-#include <_mingw.h>
-
-#pragma pack(push,_CRT_PACKING)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  struct _EXCEPTION_POINTERS;
-
-#ifndef EXCEPTION_DISPOSITION
-#define EXCEPTION_DISPOSITION   int
-#endif
-#define ExceptionContinueExecution 0
-#define ExceptionContinueSearch 1
-#define ExceptionNestedException 2
-#define ExceptionCollidedUnwind 3
-
-#if (defined(_X86_) && !defined(__x86_64))
-  struct _EXCEPTION_RECORD;
-  struct _CONTEXT;
-
-  EXCEPTION_DISPOSITION __cdecl _except_handler(struct _EXCEPTION_RECORD *_ExceptionRecord,void *_EstablisherFrame,struct _CONTEXT *_ContextRecord,void *_DispatcherContext);
-#elif defined(__ia64__)
-
-  typedef struct _EXCEPTION_POINTERS *Exception_info_ptr;
-  struct _EXCEPTION_RECORD;
-  struct _CONTEXT;
-  struct _DISPATCHER_CONTEXT;
-
-  _CRTIMP EXCEPTION_DISPOSITION __cdecl __C_specific_handler (struct _EXCEPTION_RECORD *_ExceptionRecord,unsigned __int64 _MemoryStackFp,unsigned __int64 _BackingStoreFp,struct _CONTEXT *_ContextRecord,struct _DISPATCHER_CONTEXT *_DispatcherContext,unsigned __int64 _GlobalPointer);
-#elif defined(__x86_64)
-
-  struct _EXCEPTION_RECORD;
-  struct _CONTEXT;
-#endif
-
-#define GetExceptionCode _exception_code
-#define exception_code _exception_code
-#define GetExceptionInformation (struct _EXCEPTION_POINTERS *)_exception_info
-#define exception_info (struct _EXCEPTION_POINTERS *)_exception_info
-#define AbnormalTermination _abnormal_termination
-#define abnormal_termination _abnormal_termination
-
-  unsigned long __cdecl _exception_code(void);
-  void *__cdecl _exception_info(void);
-  int __cdecl _abnormal_termination(void);
-
-#define EXCEPTION_EXECUTE_HANDLER 1
-#define EXCEPTION_CONTINUE_SEARCH 0
-#define EXCEPTION_CONTINUE_EXECUTION -1
-
-  /* CRT stuff */
-  typedef void (__cdecl * _PHNDLR)(int);
-
-  struct _XCPT_ACTION {
-    unsigned long XcptNum;
-    int SigNum;
-    _PHNDLR XcptAction;
-  };
-
-  extern struct _XCPT_ACTION _XcptActTab[];
-  extern int _XcptActTabCount;
-  extern int _XcptActTabSize;
-  extern int _First_FPE_Indx;
-  extern int _Num_FPE;
-
-  int __cdecl __CppXcptFilter(unsigned long _ExceptionNum,struct _EXCEPTION_POINTERS * _ExceptionPtr);
-  int __cdecl _XcptFilter(unsigned long _ExceptionNum,struct _EXCEPTION_POINTERS * _ExceptionPtr);
-
-  /*
-  * The type of function that is expected as an exception handler to be
-  * installed with _try1.
-  */
-  typedef EXCEPTION_DISPOSITION (*PEXCEPTION_HANDLER)(struct _EXCEPTION_RECORD*, void*, struct _CONTEXT*, void*);
-
-#ifndef HAVE_NO_SEH
-  /*
-  * This is not entirely necessary, but it is the structure installed by
-  * the _try1 primitive below.
-  */
-  typedef struct _EXCEPTION_REGISTRATION {
-    struct _EXCEPTION_REGISTRATION *prev;
-    EXCEPTION_DISPOSITION (*handler)(struct _EXCEPTION_RECORD*, void*, struct _CONTEXT*, void*);
-  } EXCEPTION_REGISTRATION, *PEXCEPTION_REGISTRATION;
-
-  typedef EXCEPTION_REGISTRATION EXCEPTION_REGISTRATION_RECORD;
-  typedef PEXCEPTION_REGISTRATION PEXCEPTION_REGISTRATION_RECORD;
-#endif
-
-#if (defined(_X86_) && !defined(__x86_64))
-#define __try1(pHandler) \
-  __asm__ ("pushl %0;pushl %%fs:0;movl %%esp,%%fs:0;" : : "g" (pHandler));
-
-#define	__except1	\
-  __asm__ ("movl (%%esp),%%eax;movl %%eax,%%fs:0;addl $8,%%esp;" \
-  : : : "%eax");
-#elif defined(__x86_64)
-#define __try1(pHandler) \
-  __asm__ ("pushq %0;pushq %%gs:0;movq %%rsp,%%gs:0;" : : "g" (pHandler));
-
-#define	__except1	\
-  __asm__ ("movq (%%rsp),%%rax;movq %%rax,%%gs:0;addq $16,%%rsp;" \
-  : : : "%rax");
-#else
-#define __try1(pHandler)
-#define __except1
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#pragma pack(pop)
-#endif
diff --git a/tinyc/win32/include/fcntl.h b/tinyc/win32/include/fcntl.h
deleted file mode 100644
index 9202b08cc..000000000
--- a/tinyc/win32/include/fcntl.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#include <_mingw.h>
-
-#include <io.h>
-
-#ifndef _INC_FCNTL
-#define _INC_FCNTL
-
-#define _O_RDONLY 0x0000
-#define _O_WRONLY 0x0001
-#define _O_RDWR 0x0002
-#define _O_APPEND 0x0008
-#define _O_CREAT 0x0100
-#define _O_TRUNC 0x0200
-#define _O_EXCL 0x0400
-#define _O_TEXT 0x4000
-#define _O_BINARY 0x8000
-#define _O_WTEXT 0x10000
-#define _O_U16TEXT 0x20000
-#define _O_U8TEXT 0x40000
-#define _O_ACCMODE (_O_RDONLY|_O_WRONLY|_O_RDWR)
-
-#define _O_RAW _O_BINARY
-#define _O_NOINHERIT 0x0080
-#define _O_TEMPORARY 0x0040
-#define _O_SHORT_LIVED 0x1000
-
-#define _O_SEQUENTIAL 0x0020
-#define _O_RANDOM 0x0010
-
-#if !defined(NO_OLDNAMES) || defined(_POSIX)
-#define O_RDONLY _O_RDONLY
-#define O_WRONLY _O_WRONLY
-#define O_RDWR _O_RDWR
-#define O_APPEND _O_APPEND
-#define O_CREAT _O_CREAT
-#define O_TRUNC _O_TRUNC
-#define O_EXCL _O_EXCL
-#define O_TEXT _O_TEXT
-#define O_BINARY _O_BINARY
-#define O_RAW _O_BINARY
-#define O_TEMPORARY _O_TEMPORARY
-#define O_NOINHERIT _O_NOINHERIT
-#define O_SEQUENTIAL _O_SEQUENTIAL
-#define O_RANDOM _O_RANDOM
-#define O_ACCMODE _O_ACCMODE
-#endif
-#endif
diff --git a/tinyc/win32/include/fenv.h b/tinyc/win32/include/fenv.h
deleted file mode 100644
index 258f3a5d9..000000000
--- a/tinyc/win32/include/fenv.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _FENV_H_
-#define _FENV_H_
-
-#include <_mingw.h>
-
-/* FPU status word exception flags */
-#define FE_INVALID	0x01
-#define FE_DENORMAL	0x02
-#define FE_DIVBYZERO	0x04
-#define FE_OVERFLOW	0x08
-#define FE_UNDERFLOW	0x10
-#define FE_INEXACT	0x20
-#define FE_ALL_EXCEPT (FE_INVALID | FE_DENORMAL | FE_DIVBYZERO \
-		       | FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT)
-
-/* FPU control word rounding flags */
-#define FE_TONEAREST	0x0000
-#define FE_DOWNWARD	0x0400
-#define FE_UPWARD	0x0800
-#define FE_TOWARDZERO	0x0c00
-
-/* The MXCSR exception flags are the same as the
-   FE flags. */
-#define __MXCSR_EXCEPT_FLAG_SHIFT 0
-
-/* How much to shift FE status word exception flags
-   to get MXCSR rounding flags,  */
-#define __MXCSR_ROUND_FLAG_SHIFT 3
-
-#ifndef RC_INVOKED
-/*
-  For now, support only for the basic abstraction of flags that are
-  either set or clear. fexcept_t could be  structure that holds more
-  info about the fp environment.
-*/
-typedef unsigned short fexcept_t;
-
-/* This 32-byte struct represents the entire floating point
-   environment as stored by fnstenv or fstenv, augmented by
-   the  contents of the MXCSR register, as stored by stmxcsr
-   (if CPU supports it). */
-typedef struct
-{
-  unsigned short __control_word;
-  unsigned short __unused0;
-  unsigned short __status_word;
-  unsigned short __unused1;
-  unsigned short __tag_word;
-  unsigned short __unused2;  
-  unsigned int	 __ip_offset;    /* instruction pointer offset */
-  unsigned short __ip_selector;  
-  unsigned short __opcode;
-  unsigned int	 __data_offset;
-  unsigned short __data_selector;  
-  unsigned short __unused3;
-  unsigned int   __mxcsr; /* contents of the MXCSR register  */
-} fenv_t;
-
-
-/*The C99 standard (7.6.9) allows us to define implementation-specific macros for
-  different fp environments */
-  
-/* The default Intel x87 floating point environment (64-bit mantissa) */
-#define FE_PC64_ENV ((const fenv_t *)-1)
-
-/* The floating point environment set by MSVCRT _fpreset (53-bit mantissa) */
-#define FE_PC53_ENV ((const fenv_t *)-2)
-
-/* The FE_DFL_ENV macro is required by standard.
-  fesetenv will use the environment set at app startup.*/
-#define FE_DFL_ENV ((const fenv_t *) 0)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*TODO: Some of these could be inlined */
-/* 7.6.2 Exception */
-
-extern int __cdecl feclearexcept (int);
-extern int __cdecl fegetexceptflag (fexcept_t * flagp, int excepts);
-extern int __cdecl feraiseexcept (int excepts );
-extern int __cdecl fesetexceptflag (const fexcept_t *, int);
-extern int __cdecl fetestexcept (int excepts);
-
-/* 7.6.3 Rounding */
-
-extern int __cdecl fegetround (void);
-extern int __cdecl fesetround (int mode);
-
-/* 7.6.4 Environment */
-
-extern int __cdecl fegetenv(fenv_t * envp);
-extern int __cdecl fesetenv(const fenv_t * );
-extern int __cdecl feupdateenv(const fenv_t *);
-extern int __cdecl feholdexcept(fenv_t *);
-
-#ifdef __cplusplus
-}
-#endif
-#endif	/* Not RC_INVOKED */
-
-#endif /* ndef _FENV_H */
diff --git a/tinyc/win32/include/inttypes.h b/tinyc/win32/include/inttypes.h
deleted file mode 100644
index 736009195..000000000
--- a/tinyc/win32/include/inttypes.h
+++ /dev/null
@@ -1,297 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-/* 7.8 Format conversion of integer types <inttypes.h> */
-
-#ifndef _INTTYPES_H_
-#define _INTTYPES_H_
-
-#include <_mingw.h>
-#include <stdint.h>
-#define __need_wchar_t
-#include <stddef.h>
-
-#ifdef	__cplusplus
-extern	"C"	{
-#endif
-
-typedef struct {
-	intmax_t quot;
-	intmax_t rem;
-	} imaxdiv_t;
-
-#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS)
-
-/* 7.8.1 Macros for format specifiers
- * 
- * MS runtime does not yet understand C9x standard "ll"
- * length specifier. It appears to treat "ll" as "l".
- * The non-standard I64 length specifier causes warning in GCC,
- * but understood by MS runtime functions.
- */
-
-/* fprintf macros for signed types */
-#define PRId8 "d"
-#define PRId16 "d"
-#define PRId32 "d"
-#define PRId64 "I64d"
-
-#define PRIdLEAST8 "d"
-#define PRIdLEAST16 "d"
-#define PRIdLEAST32 "d"
-#define PRIdLEAST64 "I64d"
-
-#define PRIdFAST8 "d"
-#define PRIdFAST16 "d"
-#define PRIdFAST32 "d"
-#define PRIdFAST64 "I64d"
-
-#define PRIdMAX "I64d"
-
-#define PRIi8 "i"
-#define PRIi16 "i"
-#define PRIi32 "i"
-#define PRIi64 "I64i"
-
-#define PRIiLEAST8 "i"
-#define PRIiLEAST16 "i"
-#define PRIiLEAST32 "i"
-#define PRIiLEAST64 "I64i"
-
-#define PRIiFAST8 "i"
-#define PRIiFAST16 "i"
-#define PRIiFAST32 "i"
-#define PRIiFAST64 "I64i"
-
-#define PRIiMAX "I64i"
-
-#define PRIo8 "o"
-#define PRIo16 "o"
-#define PRIo32 "o"
-#define PRIo64 "I64o"
-
-#define PRIoLEAST8 "o"
-#define PRIoLEAST16 "o"
-#define PRIoLEAST32 "o"
-#define PRIoLEAST64 "I64o"
-
-#define PRIoFAST8 "o"
-#define PRIoFAST16 "o"
-#define PRIoFAST32 "o"
-#define PRIoFAST64 "I64o"
-
-#define PRIoMAX "I64o"
-
-/* fprintf macros for unsigned types */
-#define PRIu8 "u"
-#define PRIu16 "u"
-#define PRIu32 "u"
-#define PRIu64 "I64u"
-
-
-#define PRIuLEAST8 "u"
-#define PRIuLEAST16 "u"
-#define PRIuLEAST32 "u"
-#define PRIuLEAST64 "I64u"
-
-#define PRIuFAST8 "u"
-#define PRIuFAST16 "u"
-#define PRIuFAST32 "u"
-#define PRIuFAST64 "I64u"
-
-#define PRIuMAX "I64u"
-
-#define PRIx8 "x"
-#define PRIx16 "x"
-#define PRIx32 "x"
-#define PRIx64 "I64x"
-
-#define PRIxLEAST8 "x"
-#define PRIxLEAST16 "x"
-#define PRIxLEAST32 "x"
-#define PRIxLEAST64 "I64x"
-
-#define PRIxFAST8 "x"
-#define PRIxFAST16 "x"
-#define PRIxFAST32 "x"
-#define PRIxFAST64 "I64x"
-
-#define PRIxMAX "I64x"
-
-#define PRIX8 "X"
-#define PRIX16 "X"
-#define PRIX32 "X"
-#define PRIX64 "I64X"
-
-#define PRIXLEAST8 "X"
-#define PRIXLEAST16 "X"
-#define PRIXLEAST32 "X"
-#define PRIXLEAST64 "I64X"
-
-#define PRIXFAST8 "X"
-#define PRIXFAST16 "X"
-#define PRIXFAST32 "X"
-#define PRIXFAST64 "I64X"
-
-#define PRIXMAX "I64X"
-
-/*
- *   fscanf macros for signed int types
- *   NOTE: if 32-bit int is used for int_fast8_t and int_fast16_t
- *   (see stdint.h, 7.18.1.3), FAST8 and FAST16 should have
- *   no length identifiers
- */
-
-#define SCNd16 "hd"
-#define SCNd32 "d"
-#define SCNd64 "I64d"
-
-#define SCNdLEAST16 "hd"
-#define SCNdLEAST32 "d"
-#define SCNdLEAST64 "I64d"
-
-#define SCNdFAST16 "hd"
-#define SCNdFAST32 "d"
-#define SCNdFAST64 "I64d"
-
-#define SCNdMAX "I64d"
-
-#define SCNi16 "hi"
-#define SCNi32 "i"
-#define SCNi64 "I64i"
-
-#define SCNiLEAST16 "hi"
-#define SCNiLEAST32 "i"
-#define SCNiLEAST64 "I64i"
-
-#define SCNiFAST16 "hi"
-#define SCNiFAST32 "i"
-#define SCNiFAST64 "I64i"
-
-#define SCNiMAX "I64i"
-
-#define SCNo16 "ho"
-#define SCNo32 "o"
-#define SCNo64 "I64o"
-
-#define SCNoLEAST16 "ho"
-#define SCNoLEAST32 "o"
-#define SCNoLEAST64 "I64o"
-
-#define SCNoFAST16 "ho"
-#define SCNoFAST32 "o"
-#define SCNoFAST64 "I64o"
-
-#define SCNoMAX "I64o"
-
-#define SCNx16 "hx"
-#define SCNx32 "x"
-#define SCNx64 "I64x"
-
-#define SCNxLEAST16 "hx"
-#define SCNxLEAST32 "x"
-#define SCNxLEAST64 "I64x"
-
-#define SCNxFAST16 "hx"
-#define SCNxFAST32 "x"
-#define SCNxFAST64 "I64x"
-
-#define SCNxMAX "I64x"
-
-/* fscanf macros for unsigned int types */
-
-#define SCNu16 "hu"
-#define SCNu32 "u"
-#define SCNu64 "I64u"
-
-#define SCNuLEAST16 "hu"
-#define SCNuLEAST32 "u"
-#define SCNuLEAST64 "I64u"
-
-#define SCNuFAST16 "hu"
-#define SCNuFAST32 "u"
-#define SCNuFAST64 "I64u"
-
-#define SCNuMAX "I64u"
-
-#ifdef _WIN64
-#define PRIdPTR "I64d"
-#define PRIiPTR "I64i"
-#define PRIoPTR "I64o"
-#define PRIuPTR "I64u"
-#define PRIxPTR "I64x"
-#define PRIXPTR "I64X"
-#define SCNdPTR "I64d"
-#define SCNiPTR "I64i"
-#define SCNoPTR "I64o"
-#define SCNxPTR "I64x"
-#define SCNuPTR "I64u"
-#else
-#define PRIdPTR "d"
-#define PRIiPTR "i"
-#define PRIoPTR "o"
-#define PRIuPTR "u"
-#define PRIxPTR "x"
-#define PRIXPTR "X"
-#define SCNdPTR "d"
-#define SCNiPTR "i"
-#define SCNoPTR "o"
-#define SCNxPTR "x"
-#define SCNuPTR "u"
-#endif
-
-#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
-/*
- * no length modifier for char types prior to C9x
- * MS runtime  scanf appears to treat "hh" as "h" 
- */
-
-/* signed char */
-#define SCNd8 "hhd"
-#define SCNdLEAST8 "hhd"
-#define SCNdFAST8 "hhd"
-
-#define SCNi8 "hhi"
-#define SCNiLEAST8 "hhi"
-#define SCNiFAST8 "hhi"
-
-#define SCNo8 "hho"
-#define SCNoLEAST8 "hho"
-#define SCNoFAST8 "hho"
-
-#define SCNx8 "hhx"
-#define SCNxLEAST8 "hhx"
-#define SCNxFAST8 "hhx"
-
-/* unsigned char */
-#define SCNu8 "hhu"
-#define SCNuLEAST8 "hhu"
-#define SCNuFAST8 "hhu"
-#endif /* __STDC_VERSION__ >= 199901 */
-
-#endif	/* !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) */
-
-intmax_t __cdecl imaxabs (intmax_t j);
-__CRT_INLINE intmax_t __cdecl imaxabs (intmax_t j)
-	{return	(j >= 0 ? j : -j);}
-imaxdiv_t __cdecl imaxdiv (intmax_t numer, intmax_t denom);
-
-/* 7.8.2 Conversion functions for greatest-width integer types */
-
-intmax_t __cdecl strtoimax (const char* __restrict__ nptr,
-                            char** __restrict__ endptr, int base);
-uintmax_t __cdecl strtoumax (const char* __restrict__ nptr,
-			     char** __restrict__ endptr, int base);
-
-intmax_t __cdecl wcstoimax (const wchar_t* __restrict__ nptr,
-                            wchar_t** __restrict__ endptr, int base);
-uintmax_t __cdecl wcstoumax (const wchar_t* __restrict__ nptr,
-			     wchar_t** __restrict__ endptr, int base);
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif /* ndef _INTTYPES_H */
diff --git a/tinyc/win32/include/io.h b/tinyc/win32/include/io.h
deleted file mode 100644
index e2aeec3db..000000000
--- a/tinyc/win32/include/io.h
+++ /dev/null
@@ -1,418 +0,0 @@
-
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _IO_H_
-#define _IO_H_
-
-#include <_mingw.h>
-#include <string.h>
-
-#pragma pack(push,_CRT_PACKING)
-
-#ifndef _POSIX_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-_CRTIMP char* __cdecl _getcwd (char*, int);
-#ifndef _FSIZE_T_DEFINED
-  typedef unsigned long _fsize_t;
-#define _FSIZE_T_DEFINED
-#endif
-
-#ifndef _FINDDATA_T_DEFINED
-
-  struct _finddata32_t {
-    unsigned attrib;
-    __time32_t time_create;
-    __time32_t time_access;
-    __time32_t time_write;
-    _fsize_t size;
-    char name[260];
-  };
-
-/*#if _INTEGRAL_MAX_BITS >= 64*/
-
-  struct _finddata32i64_t {
-    unsigned attrib;
-    __time32_t time_create;
-    __time32_t time_access;
-    __time32_t time_write;
-    __int64 size;
-    char name[260];
-  };
-
-  struct _finddata64i32_t {
-    unsigned attrib;
-    __time64_t time_create;
-    __time64_t time_access;
-    __time64_t time_write;
-    _fsize_t size;
-    char name[260];
-  };
-
-  struct __finddata64_t {
-    unsigned attrib;
-    __time64_t time_create;
-    __time64_t time_access;
-    __time64_t time_write;
-    __int64 size;
-    char name[260];
-  };
-/* #endif */
-
-#ifdef _USE_32BIT_TIME_T
-#define _finddata_t _finddata32_t
-#define _finddatai64_t _finddata32i64_t
-
-#ifdef _WIN64
-#define _findfirst _findfirst32
-#define _findnext _findnext32
-#else
-#define _findfirst32 _findfirst
-#define _findnext32 _findnext
-#endif
-#define _findfirsti64 _findfirst32i64
-#define _findnexti64 _findnext32i64
-#else
-#define _finddata_t _finddata64i32_t
-#define _finddatai64_t __finddata64_t
-
-#define _findfirst _findfirst64i32
-#define _findnext _findnext64i32
-#define _findfirsti64 _findfirst64
-#define _findnexti64 _findnext64
-#endif
-
-#define _FINDDATA_T_DEFINED
-#endif
-
-#ifndef _WFINDDATA_T_DEFINED
-
-  struct _wfinddata32_t {
-    unsigned attrib;
-    __time32_t time_create;
-    __time32_t time_access;
-    __time32_t time_write;
-    _fsize_t size;
-    wchar_t name[260];
-  };
-
-/* #if _INTEGRAL_MAX_BITS >= 64 */
-
-  struct _wfinddata32i64_t {
-    unsigned attrib;
-    __time32_t time_create;
-    __time32_t time_access;
-    __time32_t time_write;
-    __int64 size;
-    wchar_t name[260];
-  };
-
-  struct _wfinddata64i32_t {
-    unsigned attrib;
-    __time64_t time_create;
-    __time64_t time_access;
-    __time64_t time_write;
-    _fsize_t size;
-    wchar_t name[260];
-  };
-
-  struct _wfinddata64_t {
-    unsigned attrib;
-    __time64_t time_create;
-    __time64_t time_access;
-    __time64_t time_write;
-    __int64 size;
-    wchar_t name[260];
-  };
-/* #endif */
-
-#ifdef _USE_32BIT_TIME_T
-#define _wfinddata_t _wfinddata32_t
-#define _wfinddatai64_t _wfinddata32i64_t
-
-#define _wfindfirst _wfindfirst32
-#define _wfindnext _wfindnext32
-#define _wfindfirsti64 _wfindfirst32i64
-#define _wfindnexti64 _wfindnext32i64
-#else
-#define _wfinddata_t _wfinddata64i32_t
-#define _wfinddatai64_t _wfinddata64_t
-
-#define _wfindfirst _wfindfirst64i32
-#define _wfindnext _wfindnext64i32
-#define _wfindfirsti64 _wfindfirst64
-#define _wfindnexti64 _wfindnext64
-#endif
-
-#define _WFINDDATA_T_DEFINED
-#endif
-
-#define _A_NORMAL 0x00
-#define _A_RDONLY 0x01
-#define _A_HIDDEN 0x02
-#define _A_SYSTEM 0x04
-#define _A_SUBDIR 0x10
-#define _A_ARCH 0x20
-
-#ifndef _SIZE_T_DEFINED
-#define _SIZE_T_DEFINED
-#undef size_t
-#ifdef _WIN64
-#if defined(__GNUC__) && defined(__STRICT_ANSI__)
-  typedef unsigned int size_t __attribute__ ((mode (DI)));
-#else
-  typedef unsigned __int64 size_t;
-#endif
-#else
-  typedef unsigned int size_t;
-#endif
-#endif
-
-#ifndef _SSIZE_T_DEFINED
-#define _SSIZE_T_DEFINED
-#undef ssize_t
-#ifdef _WIN64
-#if defined(__GNUC__) && defined(__STRICT_ANSI__)
-  typedef int ssize_t __attribute__ ((mode (DI)));
-#else
-  typedef __int64 ssize_t;
-#endif
-#else
-  typedef int ssize_t;
-#endif
-#endif
-
-#ifndef _OFF_T_DEFINED
-#define _OFF_T_DEFINED
-#ifndef _OFF_T_
-#define _OFF_T_
-  typedef long _off_t;
-#if !defined(NO_OLDNAMES) || defined(_POSIX)
-  typedef long off_t;
-#endif
-#endif
-#endif
-
-#ifndef _OFF64_T_DEFINED
-#define _OFF64_T_DEFINED
-#if defined(__GNUC__) && defined(__STRICT_ANSI__)
-  typedef int _off64_t __attribute__ ((mode (DI)));
-#if !defined(NO_OLDNAMES) || defined(_POSIX)
-  typedef int off64_t __attribute__ ((mode (DI)));
-#endif
-#else
-  typedef long long _off64_t;
-#if !defined(NO_OLDNAMES) || defined(_POSIX)
-  typedef long long off64_t;
-#endif
-#endif
-#endif
-
-  /* Some defines for _access nAccessMode (MS doesn't define them, but
-  * it doesn't seem to hurt to add them). */
-#define	F_OK	0	/* Check for file existence */
-#define	X_OK	1	/* Check for execute permission. */
-#define	W_OK	2	/* Check for write permission */
-#define	R_OK	4	/* Check for read permission */
-
-  _CRTIMP int __cdecl _access(const char *_Filename,int _AccessMode);
-  _CRTIMP int __cdecl _chmod(const char *_Filename,int _Mode);
-  _CRTIMP int __cdecl _chsize(int _FileHandle,long _Size);
-  _CRTIMP int __cdecl _close(int _FileHandle);
-  _CRTIMP int __cdecl _commit(int _FileHandle);
-  _CRTIMP int __cdecl _creat(const char *_Filename,int _PermissionMode);
-  _CRTIMP int __cdecl _dup(int _FileHandle);
-  _CRTIMP int __cdecl _dup2(int _FileHandleSrc,int _FileHandleDst);
-  _CRTIMP int __cdecl _eof(int _FileHandle);
-  _CRTIMP long __cdecl _filelength(int _FileHandle);
-  _CRTIMP intptr_t __cdecl _findfirst32(const char *_Filename,struct _finddata32_t *_FindData);
-  _CRTIMP int __cdecl _findnext32(intptr_t _FindHandle,struct _finddata32_t *_FindData);
-  _CRTIMP int __cdecl _findclose(intptr_t _FindHandle);
-  _CRTIMP int __cdecl _isatty(int _FileHandle);
-  _CRTIMP int __cdecl _locking(int _FileHandle,int _LockMode,long _NumOfBytes);
-  _CRTIMP long __cdecl _lseek(int _FileHandle,long _Offset,int _Origin);
-  _off64_t lseek64(int fd,_off64_t offset, int whence);
-  _CRTIMP char *__cdecl _mktemp(char *_TemplateName);
-  _CRTIMP int __cdecl _pipe(int *_PtHandles,unsigned int _PipeSize,int _TextMode);
-  _CRTIMP int __cdecl _read(int _FileHandle,void *_DstBuf,unsigned int _MaxCharCount);
-
-#ifndef _CRT_DIRECTORY_DEFINED
-#define _CRT_DIRECTORY_DEFINED
-  int __cdecl remove(const char *_Filename);
-  int __cdecl rename(const char *_OldFilename,const char *_NewFilename);
-  _CRTIMP int __cdecl _unlink(const char *_Filename);
-#ifndef	NO_OLDNAMES
-  int __cdecl unlink(const char *_Filename);
-#endif
-#endif
-
-  _CRTIMP int __cdecl _setmode(int _FileHandle,int _Mode);
-  _CRTIMP long __cdecl _tell(int _FileHandle);
-  _CRTIMP int __cdecl _umask(int _Mode);
-  _CRTIMP int __cdecl _write(int _FileHandle,const void *_Buf,unsigned int _MaxCharCount);
-
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP __int64 __cdecl _filelengthi64(int _FileHandle);
-  _CRTIMP intptr_t __cdecl _findfirst32i64(const char *_Filename,struct _finddata32i64_t *_FindData);
-  _CRTIMP intptr_t __cdecl _findfirst64(const char *_Filename,struct __finddata64_t *_FindData);
-#ifdef __cplusplus
-#include <string.h>
-#endif
-  intptr_t __cdecl _findfirst64i32(const char *_Filename,struct _finddata64i32_t *_FindData);
-  __CRT_INLINE intptr_t __cdecl _findfirst64i32(const char *_Filename,struct _finddata64i32_t *_FindData)
-  {
-    struct __finddata64_t fd;
-    intptr_t ret = _findfirst64(_Filename,&fd);
-    _FindData->attrib=fd.attrib;
-    _FindData->time_create=fd.time_create;
-    _FindData->time_access=fd.time_access;
-    _FindData->time_write=fd.time_write;
-    _FindData->size=(_fsize_t) fd.size;
-    strncpy(_FindData->name,fd.name,260);
-    return ret;
-  }
-  _CRTIMP int __cdecl _findnext32i64(intptr_t _FindHandle,struct _finddata32i64_t *_FindData);
-  _CRTIMP int __cdecl _findnext64(intptr_t _FindHandle,struct __finddata64_t *_FindData);
-  int __cdecl _findnext64i32(intptr_t _FindHandle,struct _finddata64i32_t *_FindData);
-  __CRT_INLINE int __cdecl _findnext64i32(intptr_t _FindHandle,struct _finddata64i32_t *_FindData)
-  {
-    struct __finddata64_t fd;
-    int ret = _findnext64(_FindHandle,&fd);
-    _FindData->attrib=fd.attrib;
-    _FindData->time_create=fd.time_create;
-    _FindData->time_access=fd.time_access;
-    _FindData->time_write=fd.time_write;
-    _FindData->size=(_fsize_t) fd.size;
-    strncpy(_FindData->name,fd.name,260);
-    return ret;
-  }
-  __int64 __cdecl _lseeki64(int _FileHandle,__int64 _Offset,int _Origin);
-  __int64 __cdecl _telli64(int _FileHandle);
-#endif
-#ifndef NO_OLDNAMES
-
-#ifndef _UWIN
-  int __cdecl chdir (const char *);
-  char *__cdecl getcwd (char *, int);
-  int __cdecl mkdir (const char *);
-  char *__cdecl mktemp(char *);
-  int __cdecl rmdir (const char*);
-  int __cdecl chmod (const char *, int);
-#endif /* _UWIN */
-
-#endif /* Not NO_OLDNAMES */
-
-  _CRTIMP errno_t __cdecl _sopen_s(int *_FileHandle,const char *_Filename,int _OpenFlag,int _ShareFlag,int _PermissionMode);
-
-#ifndef __cplusplus
-  _CRTIMP int __cdecl _open(const char *_Filename,int _OpenFlag,...);
-  _CRTIMP int __cdecl _sopen(const char *_Filename,int _OpenFlag,int _ShareFlag,...);
-#else
-  extern "C++" _CRTIMP int __cdecl _open(const char *_Filename,int _Openflag,int _PermissionMode = 0);
-  extern "C++" _CRTIMP int __cdecl _sopen(const char *_Filename,int _Openflag,int _ShareFlag,int _PermissionMode = 0);
-#endif
-
-#ifndef _WIO_DEFINED
-#define _WIO_DEFINED
-  _CRTIMP int __cdecl _waccess(const wchar_t *_Filename,int _AccessMode);
-  _CRTIMP int __cdecl _wchmod(const wchar_t *_Filename,int _Mode);
-  _CRTIMP int __cdecl _wcreat(const wchar_t *_Filename,int _PermissionMode);
-  _CRTIMP intptr_t __cdecl _wfindfirst32(const wchar_t *_Filename,struct _wfinddata32_t *_FindData);
-  _CRTIMP int __cdecl _wfindnext32(intptr_t _FindHandle,struct _wfinddata32_t *_FindData);
-  _CRTIMP int __cdecl _wunlink(const wchar_t *_Filename);
-  _CRTIMP int __cdecl _wrename(const wchar_t *_NewFilename,const wchar_t *_OldFilename);
-  _CRTIMP wchar_t *__cdecl _wmktemp(wchar_t *_TemplateName);
-
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP intptr_t __cdecl _wfindfirst32i64(const wchar_t *_Filename,struct _wfinddata32i64_t *_FindData);
-  intptr_t __cdecl _wfindfirst64i32(const wchar_t *_Filename,struct _wfinddata64i32_t *_FindData);
-  _CRTIMP intptr_t __cdecl _wfindfirst64(const wchar_t *_Filename,struct _wfinddata64_t *_FindData);
-  _CRTIMP int __cdecl _wfindnext32i64(intptr_t _FindHandle,struct _wfinddata32i64_t *_FindData);
-  int __cdecl _wfindnext64i32(intptr_t _FindHandle,struct _wfinddata64i32_t *_FindData);
-  _CRTIMP int __cdecl _wfindnext64(intptr_t _FindHandle,struct _wfinddata64_t *_FindData);
-#endif
-
-  _CRTIMP errno_t __cdecl _wsopen_s(int *_FileHandle,const wchar_t *_Filename,int _OpenFlag,int _ShareFlag,int _PermissionFlag);
-
-#if !defined(__cplusplus) || !(defined(_X86_) && !defined(__x86_64))
-  _CRTIMP int __cdecl _wopen(const wchar_t *_Filename,int _OpenFlag,...);
-  _CRTIMP int __cdecl _wsopen(const wchar_t *_Filename,int _OpenFlag,int _ShareFlag,...);
-#else
-  extern "C++" _CRTIMP int __cdecl _wopen(const wchar_t *_Filename,int _OpenFlag,int _PermissionMode = 0);
-  extern "C++" _CRTIMP int __cdecl _wsopen(const wchar_t *_Filename,int _OpenFlag,int _ShareFlag,int _PermissionMode = 0);
-#endif
-
-#endif
-
-  int __cdecl __lock_fhandle(int _Filehandle);
-  void __cdecl _unlock_fhandle(int _Filehandle);
-  _CRTIMP intptr_t __cdecl _get_osfhandle(int _FileHandle);
-  _CRTIMP int __cdecl _open_osfhandle(intptr_t _OSFileHandle,int _Flags);
-
-#ifndef	NO_OLDNAMES
-  int __cdecl access(const char *_Filename,int _AccessMode);
-  int __cdecl chmod(const char *_Filename,int _AccessMode);
-  int __cdecl chsize(int _FileHandle,long _Size);
-  int __cdecl close(int _FileHandle);
-  int __cdecl creat(const char *_Filename,int _PermissionMode);
-  int __cdecl dup(int _FileHandle);
-  int __cdecl dup2(int _FileHandleSrc,int _FileHandleDst);
-  int __cdecl eof(int _FileHandle);
-  long __cdecl filelength(int _FileHandle);
-  int __cdecl isatty(int _FileHandle);
-  int __cdecl locking(int _FileHandle,int _LockMode,long _NumOfBytes);
-  long __cdecl lseek(int _FileHandle,long _Offset,int _Origin);
-  char *__cdecl mktemp(char *_TemplateName);
-  int __cdecl open(const char *_Filename,int _OpenFlag,...);
-  int __cdecl read(int _FileHandle,void *_DstBuf,unsigned int _MaxCharCount);
-  int __cdecl setmode(int _FileHandle,int _Mode);
-  int __cdecl sopen(const char *_Filename,int _OpenFlag,int _ShareFlag,...);
-  long __cdecl tell(int _FileHandle);
-  int __cdecl umask(int _Mode);
-  int __cdecl write(int _Filehandle,const void *_Buf,unsigned int _MaxCharCount);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Misc stuff */
-char *getlogin(void);
-#ifdef __USE_MINGW_ALARM
-unsigned int alarm(unsigned int seconds);
-#endif
-
-#ifdef __USE_MINGW_ACCESS
-/*  Old versions of MSVCRT access() just ignored X_OK, while the version
-    shipped with Vista, returns an error code.  This will restore the
-    old behaviour  */
-static inline int __mingw_access (const char *__fname, int __mode) {
-  return  _access (__fname, __mode & ~X_OK);
-}
-
-#define access(__f,__m)  __mingw_access (__f, __m)
-#endif
-
-
-#ifdef __cplusplus
-}
-#endif
-
-
-#pragma pack(pop)
-
-#include <sec_api/io_s.h>
-
-#endif /* End _IO_H_ */
-
diff --git a/tinyc/win32/include/limits.h b/tinyc/win32/include/limits.h
deleted file mode 100644
index fafb04a46..000000000
--- a/tinyc/win32/include/limits.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#include <_mingw.h>
-
-#ifndef _INC_LIMITS
-#define _INC_LIMITS
-
-/*
-* File system limits
-*
-* TODO: NAME_MAX and OPEN_MAX are file system limits or not? Are they the
-*       same as FILENAME_MAX and FOPEN_MAX from stdio.h?
-* NOTE: Apparently the actual size of PATH_MAX is 260, but a space is
-*       required for the NUL. TODO: Test?
-*/
-#define PATH_MAX	(259)
-
-#define CHAR_BIT 8
-#define SCHAR_MIN (-128)
-#define SCHAR_MAX 127
-#define UCHAR_MAX 0xff
-
-#define CHAR_MIN SCHAR_MIN
-#define CHAR_MAX SCHAR_MAX
-
-#define MB_LEN_MAX 5
-#define SHRT_MIN (-32768)
-#define SHRT_MAX 32767
-#define USHRT_MAX 0xffff
-#define INT_MIN (-2147483647 - 1)
-#define INT_MAX 2147483647
-#define UINT_MAX 0xffffffff
-#define LONG_MIN (-2147483647L - 1)
-#define LONG_MAX 2147483647L
-#define ULONG_MAX 0xffffffffUL
-#define LLONG_MAX 9223372036854775807ll
-#define LLONG_MIN (-9223372036854775807ll - 1)
-#define ULLONG_MAX 0xffffffffffffffffull
-
-#if _INTEGRAL_MAX_BITS >= 8
-#define _I8_MIN (-127 - 1)
-#define _I8_MAX 127i8
-#define _UI8_MAX 0xffu
-#endif
-
-#if _INTEGRAL_MAX_BITS >= 16
-#define _I16_MIN (-32767 - 1)
-#define _I16_MAX 32767i16
-#define _UI16_MAX 0xffffu
-#endif
-
-#if _INTEGRAL_MAX_BITS >= 32
-#define _I32_MIN (-2147483647 - 1)
-#define _I32_MAX 2147483647
-#define _UI32_MAX 0xffffffffu
-#endif
-
-#if defined(__GNUC__)
-#undef LONG_LONG_MAX
-#define LONG_LONG_MAX 9223372036854775807ll
-#undef LONG_LONG_MIN
-#define LONG_LONG_MIN (-LONG_LONG_MAX-1)
-#undef ULONG_LONG_MAX
-#define ULONG_LONG_MAX (2ull * LONG_LONG_MAX + 1ull)
-#endif
-
-#if _INTEGRAL_MAX_BITS >= 64
-#define _I64_MIN (-9223372036854775807ll - 1)
-#define _I64_MAX 9223372036854775807ll
-#define _UI64_MAX 0xffffffffffffffffull
-#endif
-
-#ifndef SIZE_MAX
-#ifdef _WIN64
-#define SIZE_MAX _UI64_MAX
-#else
-#define SIZE_MAX UINT_MAX
-#endif
-#endif
-
-#ifdef _POSIX_
-#define _POSIX_ARG_MAX 4096
-#define _POSIX_CHILD_MAX 6
-#define _POSIX_LINK_MAX 8
-#define _POSIX_MAX_CANON 255
-#define _POSIX_MAX_INPUT 255
-#define _POSIX_NAME_MAX 14
-#define _POSIX_NGROUPS_MAX 0
-#define _POSIX_OPEN_MAX 16
-#define _POSIX_PATH_MAX 255
-#define _POSIX_PIPE_BUF 512
-#define _POSIX_SSIZE_MAX 32767
-#define _POSIX_STREAM_MAX 8
-#define _POSIX_TZNAME_MAX 3
-#define ARG_MAX 14500
-#define LINK_MAX 1024
-#define MAX_CANON _POSIX_MAX_CANON
-#define MAX_INPUT _POSIX_MAX_INPUT
-#define NAME_MAX 255
-#define NGROUPS_MAX 16
-#define OPEN_MAX 32
-#define PATH_MAX 512
-#define PIPE_BUF _POSIX_PIPE_BUF
-#define SSIZE_MAX _POSIX_SSIZE_MAX
-#define STREAM_MAX 20
-#define TZNAME_MAX 10
-#endif
-#endif
diff --git a/tinyc/win32/include/locale.h b/tinyc/win32/include/locale.h
deleted file mode 100644
index 686aa9bae..000000000
--- a/tinyc/win32/include/locale.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_LOCALE
-#define _INC_LOCALE
-
-#include <_mingw.h>
-
-#pragma pack(push,_CRT_PACKING)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef NULL
-#ifdef __cplusplus
-#define NULL 0
-#else
-#define NULL ((void *)0)
-#endif
-#endif
-
-#define LC_ALL 0
-#define LC_COLLATE 1
-#define LC_CTYPE 2
-#define LC_MONETARY 3
-#define LC_NUMERIC 4
-#define LC_TIME 5
-
-#define LC_MIN LC_ALL
-#define LC_MAX LC_TIME
-
-#ifndef _LCONV_DEFINED
-#define _LCONV_DEFINED
-  struct lconv {
-    char *decimal_point;
-    char *thousands_sep;
-    char *grouping;
-    char *int_curr_symbol;
-    char *currency_symbol;
-    char *mon_decimal_point;
-    char *mon_thousands_sep;
-    char *mon_grouping;
-    char *positive_sign;
-    char *negative_sign;
-    char int_frac_digits;
-    char frac_digits;
-    char p_cs_precedes;
-    char p_sep_by_space;
-    char n_cs_precedes;
-    char n_sep_by_space;
-    char p_sign_posn;
-    char n_sign_posn;
-  };
-#endif
-
-#ifndef _CONFIG_LOCALE_SWT
-#define _CONFIG_LOCALE_SWT
-
-#define _ENABLE_PER_THREAD_LOCALE 0x1
-#define _DISABLE_PER_THREAD_LOCALE 0x2
-#define _ENABLE_PER_THREAD_LOCALE_GLOBAL 0x10
-#define _DISABLE_PER_THREAD_LOCALE_GLOBAL 0x20
-#define _ENABLE_PER_THREAD_LOCALE_NEW 0x100
-#define _DISABLE_PER_THREAD_LOCALE_NEW 0x200
-
-#endif
-
-  int __cdecl _configthreadlocale(int _Flag);
-  char *__cdecl setlocale(int _Category,const char *_Locale);
-  _CRTIMP struct lconv *__cdecl localeconv(void);
-  _locale_t __cdecl _get_current_locale(void);
-  _locale_t __cdecl _create_locale(int _Category,const char *_Locale);
-  void __cdecl _free_locale(_locale_t _Locale);
-  _locale_t __cdecl __get_current_locale(void);
-  _locale_t __cdecl __create_locale(int _Category,const char *_Locale);
-  void __cdecl __free_locale(_locale_t _Locale);
-
-#ifndef _WLOCALE_DEFINED
-#define _WLOCALE_DEFINED
-  _CRTIMP wchar_t *__cdecl _wsetlocale(int _Category,const wchar_t *_Locale);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#pragma pack(pop)
-#endif
diff --git a/tinyc/win32/include/malloc.h b/tinyc/win32/include/malloc.h
deleted file mode 100644
index fc783a8e3..000000000
--- a/tinyc/win32/include/malloc.h
+++ /dev/null
@@ -1,181 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _MALLOC_H_
-#define _MALLOC_H_
-
-#include <_mingw.h>
-
-#pragma pack(push,_CRT_PACKING)
-
-#ifndef _MM_MALLOC_H_INCLUDED
-#define _MM_MALLOC_H_INCLUDED
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifdef _WIN64
-#define _HEAP_MAXREQ 0xFFFFFFFFFFFFFFE0
-#else
-#define _HEAP_MAXREQ 0xFFFFFFE0
-#endif
-
-#ifndef _STATIC_ASSERT
-#define _STATIC_ASSERT(expr) extern void __static_assert_t(int [(expr)?1:-1])
-#endif
-
-/* Return codes for _heapwalk()  */
-#define _HEAPEMPTY (-1)
-#define _HEAPOK (-2)
-#define _HEAPBADBEGIN (-3)
-#define _HEAPBADNODE (-4)
-#define _HEAPEND (-5)
-#define _HEAPBADPTR (-6)
-
-/* Values for _heapinfo.useflag */
-#define _FREEENTRY 0
-#define _USEDENTRY 1
-
-#ifndef _HEAPINFO_DEFINED
-#define _HEAPINFO_DEFINED
- /* The structure used to walk through the heap with _heapwalk.  */
-  typedef struct _heapinfo {
-    int *_pentry;
-    size_t _size;
-    int _useflag;
-  } _HEAPINFO;
-#endif
-
-  extern unsigned int _amblksiz;
-
-#define _mm_free(a) _aligned_free(a)
-#define _mm_malloc(a,b) _aligned_malloc(a,b)
-
-#ifndef _CRT_ALLOCATION_DEFINED
-#define _CRT_ALLOCATION_DEFINED
-  void *__cdecl calloc(size_t _NumOfElements,size_t _SizeOfElements);
-  void __cdecl free(void *_Memory);
-  void *__cdecl malloc(size_t _Size);
-  void *__cdecl realloc(void *_Memory,size_t _NewSize);
-  _CRTIMP void *__cdecl _recalloc(void *_Memory,size_t _Count,size_t _Size);
-  /*	_CRTIMP void __cdecl _aligned_free(void *_Memory);
-  _CRTIMP void *__cdecl _aligned_malloc(size_t _Size,size_t _Alignment); */
-  _CRTIMP void *__cdecl _aligned_offset_malloc(size_t _Size,size_t _Alignment,size_t _Offset);
-  _CRTIMP void *__cdecl _aligned_realloc(void *_Memory,size_t _Size,size_t _Alignment);
-  _CRTIMP void *__cdecl _aligned_recalloc(void *_Memory,size_t _Count,size_t _Size,size_t _Alignment);
-  _CRTIMP void *__cdecl _aligned_offset_realloc(void *_Memory,size_t _Size,size_t _Alignment,size_t _Offset);
-  _CRTIMP void *__cdecl _aligned_offset_recalloc(void *_Memory,size_t _Count,size_t _Size,size_t _Alignment,size_t _Offset);
-#endif
-
-#define _MAX_WAIT_MALLOC_CRT 60000
-
-  _CRTIMP int __cdecl _resetstkoflw (void);
-  _CRTIMP unsigned long __cdecl _set_malloc_crt_max_wait(unsigned long _NewValue);
-
-  _CRTIMP void *__cdecl _expand(void *_Memory,size_t _NewSize);
-  _CRTIMP size_t __cdecl _msize(void *_Memory);
-#ifdef __GNUC__
-#undef _alloca
-#define _alloca(x) __builtin_alloca((x))
-#else
-  /* tcc implements alloca internally and exposes it (since commit d778bde7).
-  /* alloca is declared at include/stddef.h (which is distributed with tcc).
-   */
-#ifdef _alloca
-#undef _alloca
-#endif
-#define _alloca(x) alloca((x))
-#endif
-  _CRTIMP size_t __cdecl _get_sbh_threshold(void);
-  _CRTIMP int __cdecl _set_sbh_threshold(size_t _NewValue);
-  _CRTIMP errno_t __cdecl _set_amblksiz(size_t _Value);
-  _CRTIMP errno_t __cdecl _get_amblksiz(size_t *_Value);
-  _CRTIMP int __cdecl _heapadd(void *_Memory,size_t _Size);
-  _CRTIMP int __cdecl _heapchk(void);
-  _CRTIMP int __cdecl _heapmin(void);
-  _CRTIMP int __cdecl _heapset(unsigned int _Fill);
-  _CRTIMP int __cdecl _heapwalk(_HEAPINFO *_EntryInfo);
-  _CRTIMP size_t __cdecl _heapused(size_t *_Used,size_t *_Commit);
-  _CRTIMP intptr_t __cdecl _get_heap_handle(void);
-
-#define _ALLOCA_S_THRESHOLD 1024
-#define _ALLOCA_S_STACK_MARKER 0xCCCC
-#define _ALLOCA_S_HEAP_MARKER 0xDDDD
-
-#if(defined(_X86_) && !defined(__x86_64))
-#define _ALLOCA_S_MARKER_SIZE 8
-#elif defined(__ia64__) || defined(__x86_64)
-#define _ALLOCA_S_MARKER_SIZE 16
-#endif
-
-#if !defined(RC_INVOKED)
-  static __inline void *_MarkAllocaS(void *_Ptr,unsigned int _Marker) {
-    if(_Ptr) {
-      *((unsigned int*)_Ptr) = _Marker;
-      _Ptr = (char*)_Ptr + _ALLOCA_S_MARKER_SIZE;
-    }
-    return _Ptr;
-  }
-#endif
-
-#undef _malloca
-#define _malloca(size) \
-  ((((size) + _ALLOCA_S_MARKER_SIZE) <= _ALLOCA_S_THRESHOLD) ? \
-    _MarkAllocaS(_alloca((size) + _ALLOCA_S_MARKER_SIZE),_ALLOCA_S_STACK_MARKER) : \
-    _MarkAllocaS(malloc((size) + _ALLOCA_S_MARKER_SIZE),_ALLOCA_S_HEAP_MARKER))
-#undef _FREEA_INLINE
-#define _FREEA_INLINE
-
-#ifndef RC_INVOKED
-#undef _freea
-  static __inline void __cdecl _freea(void *_Memory) {
-    unsigned int _Marker;
-    if(_Memory) {
-      _Memory = (char*)_Memory - _ALLOCA_S_MARKER_SIZE;
-      _Marker = *(unsigned int *)_Memory;
-      if(_Marker==_ALLOCA_S_HEAP_MARKER) {
-	free(_Memory);
-      }
-#ifdef _ASSERTE
-      else if(_Marker!=_ALLOCA_S_STACK_MARKER) {
-	_ASSERTE(("Corrupted pointer passed to _freea",0));
-      }
-#endif
-    }
-  }
-#endif /* RC_INVOKED */
-
-#ifndef	NO_OLDNAMES
-#ifdef __GNUC__
-#undef alloca
-#define alloca(x) __builtin_alloca((x))
-#endif
-#endif
-
-#ifdef HEAPHOOK
-#ifndef _HEAPHOOK_DEFINED
-#define _HEAPHOOK_DEFINED
-  typedef int (__cdecl *_HEAPHOOK)(int,size_t,void *,void **);
-#endif
-
-  _CRTIMP _HEAPHOOK __cdecl _setheaphook(_HEAPHOOK _NewHook);
-
-#define _HEAP_MALLOC 1
-#define _HEAP_CALLOC 2
-#define _HEAP_FREE 3
-#define _HEAP_REALLOC 4
-#define _HEAP_MSIZE 5
-#define _HEAP_EXPAND 6
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#pragma pack(pop)
-
-#endif /* _MALLOC_H_ */
diff --git a/tinyc/win32/include/math.h b/tinyc/win32/include/math.h
deleted file mode 100644
index 74add20e9..000000000
--- a/tinyc/win32/include/math.h
+++ /dev/null
@@ -1,737 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _MATH_H_
-#define _MATH_H_
-
-#if __GNUC__ >= 3
-#pragma GCC system_header
-#endif
-
-#include <_mingw.h>
-
-struct exception;
-
-#pragma pack(push,_CRT_PACKING)
-
-#define _DOMAIN 1
-#define _SING 2
-#define _OVERFLOW 3
-#define _UNDERFLOW 4
-#define _TLOSS 5
-#define _PLOSS 6
-
-#ifndef __STRICT_ANSI__
-#ifndef	NO_OLDNAMES
-#define DOMAIN _DOMAIN
-#define SING _SING
-#define OVERFLOW _OVERFLOW
-#define UNDERFLOW _UNDERFLOW
-#define TLOSS _TLOSS
-#define PLOSS _PLOSS
-#endif
-#endif
-
-#ifndef __STRICT_ANSI__
-#define M_E 2.71828182845904523536
-#define M_LOG2E 1.44269504088896340736
-#define M_LOG10E 0.434294481903251827651
-#define M_LN2 0.693147180559945309417
-#define M_LN10 2.30258509299404568402
-#define M_PI 3.14159265358979323846
-#define M_PI_2 1.57079632679489661923
-#define M_PI_4 0.785398163397448309616
-#define M_1_PI 0.318309886183790671538
-#define M_2_PI 0.636619772367581343076
-#define M_2_SQRTPI 1.12837916709551257390
-#define M_SQRT2 1.41421356237309504880
-#define M_SQRT1_2 0.707106781186547524401
-#endif
-
-#ifndef __STRICT_ANSI__
-/* See also float.h  */
-#ifndef __MINGW_FPCLASS_DEFINED
-#define __MINGW_FPCLASS_DEFINED 1
-#define	_FPCLASS_SNAN	0x0001	/* Signaling "Not a Number" */
-#define	_FPCLASS_QNAN	0x0002	/* Quiet "Not a Number" */
-#define	_FPCLASS_NINF	0x0004	/* Negative Infinity */
-#define	_FPCLASS_NN	0x0008	/* Negative Normal */
-#define	_FPCLASS_ND	0x0010	/* Negative Denormal */
-#define	_FPCLASS_NZ	0x0020	/* Negative Zero */
-#define	_FPCLASS_PZ	0x0040	/* Positive Zero */
-#define	_FPCLASS_PD	0x0080	/* Positive Denormal */
-#define	_FPCLASS_PN	0x0100	/* Positive Normal */
-#define	_FPCLASS_PINF	0x0200	/* Positive Infinity */
-#endif
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _EXCEPTION_DEFINED
-#define _EXCEPTION_DEFINED
-  struct _exception {
-    int type;
-    char *name;
-    double arg1;
-    double arg2;
-    double retval;
-  };
-#endif
-
-#ifndef _COMPLEX_DEFINED
-#define _COMPLEX_DEFINED
-  struct _complex {
-    double x,y;
-  };
-#endif
-
-#define EDOM 33
-#define ERANGE 34
-
-#ifndef _HUGE
-#ifdef _MSVCRT_
-  extern double *_HUGE;
-#else
-  extern double *_imp___HUGE;
-#define _HUGE	(*_imp___HUGE)
-#endif
-#endif
-
-#define HUGE_VAL _HUGE
-
-#ifndef _CRT_ABS_DEFINED
-#define _CRT_ABS_DEFINED
-  int __cdecl abs(int _X);
-  long __cdecl labs(long _X);
-#endif
-  double __cdecl acos(double _X);
-  double __cdecl asin(double _X);
-  double __cdecl atan(double _X);
-  double __cdecl atan2(double _Y,double _X);
-#ifndef _SIGN_DEFINED
-#define _SIGN_DEFINED
-  _CRTIMP double __cdecl _copysign (double _Number,double _Sign);
-  _CRTIMP double __cdecl _chgsign (double _X);
-#endif
-  double __cdecl cos(double _X);
-  double __cdecl cosh(double _X);
-  double __cdecl exp(double _X);
-  double __cdecl expm1(double _X);
-  double __cdecl fabs(double _X);
-  double __cdecl fmod(double _X,double _Y);
-  double __cdecl log(double _X);
-  double __cdecl log10(double _X);
-  double __cdecl pow(double _X,double _Y);
-  double __cdecl sin(double _X);
-  double __cdecl sinh(double _X);
-  double __cdecl tan(double _X);
-  double __cdecl tanh(double _X);
-  double __cdecl sqrt(double _X);
-#ifndef _CRT_ATOF_DEFINED
-#define _CRT_ATOF_DEFINED
-  double __cdecl atof(const char *_String);
-  double __cdecl _atof_l(const char *_String,_locale_t _Locale);
-#endif
-
-  _CRTIMP double __cdecl _cabs(struct _complex _ComplexA);
-  double __cdecl ceil(double _X);
-  double __cdecl floor(double _X);
-  double __cdecl frexp(double _X,int *_Y);
-  double __cdecl _hypot(double _X,double _Y);
-  _CRTIMP double __cdecl _j0(double _X);
-  _CRTIMP double __cdecl _j1(double _X);
-  _CRTIMP double __cdecl _jn(int _X,double _Y);
-  double __cdecl ldexp(double _X,int _Y);
-#ifndef _CRT_MATHERR_DEFINED
-#define _CRT_MATHERR_DEFINED
-  int __cdecl _matherr(struct _exception *_Except);
-#endif
-  double __cdecl modf(double _X,double *_Y);
-  _CRTIMP double __cdecl _y0(double _X);
-  _CRTIMP double __cdecl _y1(double _X);
-  _CRTIMP double __cdecl _yn(int _X,double _Y);
-
-#if(defined(_X86_) && !defined(__x86_64))
-  _CRTIMP int __cdecl _set_SSE2_enable(int _Flag);
-  /* from libmingwex */
-  float __cdecl _hypotf(float _X,float _Y);
-#endif
-
-  float frexpf(float _X,int *_Y);
-  float __cdecl ldexpf(float _X,int _Y);
-  long double __cdecl ldexpl(long double _X,int _Y);
-  float __cdecl acosf(float _X);
-  float __cdecl asinf(float _X);
-   float __cdecl atanf(float _X);
-   float __cdecl atan2f(float _X,float _Y);
-   float __cdecl cosf(float _X);
-   float __cdecl sinf(float _X);
-   float __cdecl tanf(float _X);
-   float __cdecl coshf(float _X);
-   float __cdecl sinhf(float _X);
-   float __cdecl tanhf(float _X);
-   float __cdecl expf(float _X);
-   float __cdecl expm1f(float _X);
-   float __cdecl logf(float _X);
-   float __cdecl log10f(float _X);
-   float __cdecl modff(float _X,float *_Y);
-   float __cdecl powf(float _X,float _Y);
-   float __cdecl sqrtf(float _X);
-   float __cdecl ceilf(float _X);
-   float __cdecl floorf(float _X);
-  float __cdecl fmodf(float _X,float _Y);
-   float __cdecl _hypotf(float _X,float _Y);
-  float __cdecl fabsf(float _X);
-#if !defined(__ia64__)
-   /* from libmingwex */
-   float __cdecl _copysignf (float _Number,float _Sign);
-   float __cdecl _chgsignf (float _X);
-   float __cdecl _logbf(float _X);
-   float __cdecl _nextafterf(float _X,float _Y);
-   int __cdecl _finitef(float _X);
-   int __cdecl _isnanf(float _X);
-   int __cdecl _fpclassf(float _X);
-#endif
-
-#ifndef __cplusplus
-  __CRT_INLINE long double __cdecl fabsl (long double x)
-  {
-    long double res;
-    __asm__ ("fabs;" : "=t" (res) : "0" (x));
-    return res;
-  }
-#define _hypotl(x,y) ((long double)_hypot((double)(x),(double)(y)))
-#define _matherrl _matherr
-  __CRT_INLINE long double _chgsignl(long double _Number) { return _chgsign((double)(_Number)); }
-  __CRT_INLINE long double _copysignl(long double _Number,long double _Sign) { return _copysign((double)(_Number),(double)(_Sign)); }
-  __CRT_INLINE float frexpf(float _X,int *_Y) { return ((float)frexp((double)_X,_Y)); }
-
-#if !defined (__ia64__)
-  __CRT_INLINE float __cdecl fabsf (float x)
-  {
-    float res;
-    __asm__ ("fabs;" : "=t" (res) : "0" (x));
-    return res;
-  }
-
-  __CRT_INLINE float __cdecl ldexpf (float x, int expn) { return (float) ldexp (x, expn); }
-#endif
-#else
-  // cplusplus
-  __CRT_INLINE long double __cdecl fabsl (long double x)
-  {
-    long double res;
-    __asm__ ("fabs;" : "=t" (res) : "0" (x));
-    return res;
-  }
-  __CRT_INLINE long double modfl(long double _X,long double *_Y) {
-    double _Di,_Df = modf((double)_X,&_Di);
-    *_Y = (long double)_Di;
-    return (_Df);
-  }
-  __CRT_INLINE long double _chgsignl(long double _Number) { return _chgsign(static_cast<double>(_Number)); }
-  __CRT_INLINE long double _copysignl(long double _Number,long double _Sign) { return _copysign(static_cast<double>(_Number),static_cast<double>(_Sign)); }
-  __CRT_INLINE float frexpf(float _X,int *_Y) { return ((float)frexp((double)_X,_Y)); }
-#ifndef __ia64__
-  __CRT_INLINE float __cdecl fabsf (float x)
-  {
-    float res;
-    __asm__ ("fabs;" : "=t" (res) : "0" (x));
-    return res;
-  }
-  __CRT_INLINE float __cdecl ldexpf (float x, int expn) { return (float) ldexp (x, expn); }
-#ifndef __x86_64
-  __CRT_INLINE float acosf(float _X) { return ((float)acos((double)_X)); }
-  __CRT_INLINE float asinf(float _X) { return ((float)asin((double)_X)); }
-  __CRT_INLINE float atanf(float _X) { return ((float)atan((double)_X)); }
-  __CRT_INLINE float atan2f(float _X,float _Y) { return ((float)atan2((double)_X,(double)_Y)); }
-  __CRT_INLINE float ceilf(float _X) { return ((float)ceil((double)_X)); }
-  __CRT_INLINE float cosf(float _X) { return ((float)cos((double)_X)); }
-  __CRT_INLINE float coshf(float _X) { return ((float)cosh((double)_X)); }
-  __CRT_INLINE float expf(float _X) { return ((float)exp((double)_X)); }
-  __CRT_INLINE float floorf(float _X) { return ((float)floor((double)_X)); }
-  __CRT_INLINE float fmodf(float _X,float _Y) { return ((float)fmod((double)_X,(double)_Y)); }
-  __CRT_INLINE float logf(float _X) { return ((float)log((double)_X)); }
-  __CRT_INLINE float log10f(float _X) { return ((float)log10((double)_X)); }
-  __CRT_INLINE float modff(float _X,float *_Y) {
-    double _Di,_Df = modf((double)_X,&_Di);
-    *_Y = (float)_Di;
-    return ((float)_Df);
-  }
-  __CRT_INLINE float powf(float _X,float _Y) { return ((float)pow((double)_X,(double)_Y)); }
-  __CRT_INLINE float sinf(float _X) { return ((float)sin((double)_X)); }
-  __CRT_INLINE float sinhf(float _X) { return ((float)sinh((double)_X)); }
-  __CRT_INLINE float sqrtf(float _X) { return ((float)sqrt((double)_X)); }
-  __CRT_INLINE float tanf(float _X) { return ((float)tan((double)_X)); }
-  __CRT_INLINE float tanhf(float _X) { return ((float)tanh((double)_X)); }
-#endif
-#endif
-#endif
-
-#ifndef	NO_OLDNAMES
-#define matherr _matherr
-
-#define HUGE	_HUGE
-  /*	double __cdecl cabs(struct _complex _X); */
-  double __cdecl hypot(double _X,double _Y);
-  _CRTIMP double __cdecl j0(double _X);
-  _CRTIMP double __cdecl j1(double _X);
-  _CRTIMP double __cdecl jn(int _X,double _Y);
-  _CRTIMP double __cdecl y0(double _X);
-  _CRTIMP double __cdecl y1(double _X);
-  _CRTIMP double __cdecl yn(int _X,double _Y);
-#endif
-
-#ifndef __NO_ISOCEXT
-#if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) \
-  || !defined __STRICT_ANSI__ || defined __GLIBCPP__
-
-#define NAN (0.0F/0.0F)
-#define HUGE_VALF (1.0F/0.0F)
-#define HUGE_VALL (1.0L/0.0L)
-#define INFINITY (1.0F/0.0F)
-
-
-#define FP_NAN		0x0100
-#define FP_NORMAL	0x0400
-#define FP_INFINITE	(FP_NAN | FP_NORMAL)
-#define FP_ZERO		0x4000
-#define FP_SUBNORMAL	(FP_NORMAL | FP_ZERO)
-  /* 0x0200 is signbit mask */
-
-
-  /*
-  We can't __CRT_INLINE float or double, because we want to ensure truncation
-  to semantic type before classification. 
-  (A normal long double value might become subnormal when 
-  converted to double, and zero when converted to float.)
-  */
-
-  extern int __cdecl __fpclassifyf (float);
-  extern int __cdecl __fpclassify (double);
-  extern int __cdecl __fpclassifyl (long double);
-
-/* Implemented at tcc/tcc_libm.h */
-#define fpclassify(x) (sizeof (x) == sizeof (float) ? __fpclassifyf (x)	  \
-  : sizeof (x) == sizeof (double) ? __fpclassify (x) \
-  : __fpclassifyl (x))
-
-  /* 7.12.3.2 */
-#define isfinite(x) ((fpclassify(x) & FP_NAN) == 0)
-
-  /* 7.12.3.3 */
-#define isinf(x) (fpclassify(x) == FP_INFINITE)
-
-  /* 7.12.3.4 */
-  /* We don't need to worry about truncation here:
-  A NaN stays a NaN. */
-#define isnan(x) (fpclassify(x) == FP_NAN)
-
-  /* 7.12.3.5 */
-#define isnormal(x) (fpclassify(x) == FP_NORMAL)
-
-  /* 7.12.3.6 The signbit macro */
-
-  extern int __cdecl __signbitf (float);
-  extern int __cdecl __signbit (double);
-  extern int __cdecl __signbitl (long double);
-
-/* Implemented at tcc/tcc_libm.h */
-#define signbit(x) (sizeof (x) == sizeof (float) ? __signbitf (x)	\
-  : sizeof (x) == sizeof (double) ? __signbit (x)	\
-  : __signbitl (x))
-
-  extern double __cdecl exp2(double);
-  extern float __cdecl exp2f(float);
-  extern long double __cdecl exp2l(long double);
-
-#define FP_ILOGB0 ((int)0x80000000)
-#define FP_ILOGBNAN ((int)0x80000000)
-  extern int __cdecl ilogb (double);
-  extern int __cdecl ilogbf (float);
-  extern int __cdecl ilogbl (long double);
-
-  extern double __cdecl log1p(double);
-  extern float __cdecl log1pf(float);
-  extern long double __cdecl log1pl(long double);
-
-  extern double __cdecl log2 (double);
-  extern float __cdecl log2f (float);
-  extern long double __cdecl log2l (long double);
-
-  extern double __cdecl logb (double);
-  extern float __cdecl logbf (float);
-  extern long double __cdecl logbl (long double);
-
-  __CRT_INLINE double __cdecl logb (double x)
-  {
-    double res;
-    __asm__ ("fxtract\n\t"
-      "fstp	%%st" : "=t" (res) : "0" (x));
-    return res;
-  }
-
-  __CRT_INLINE float __cdecl logbf (float x)
-  {
-    float res;
-    __asm__ ("fxtract\n\t"
-      "fstp	%%st" : "=t" (res) : "0" (x));
-    return res;
-  }
-
-  __CRT_INLINE long double __cdecl logbl (long double x)
-  {
-    long double res;
-    __asm__ ("fxtract\n\t"
-      "fstp	%%st" : "=t" (res) : "0" (x));
-    return res;
-  }
-
-  extern long double __cdecl modfl (long double, long double*);
-
-  /* 7.12.6.13 */
-  extern double __cdecl scalbn (double, int);
-  extern float __cdecl scalbnf (float, int);
-  extern long double __cdecl scalbnl (long double, int);
-
-  extern double __cdecl scalbln (double, long);
-  extern float __cdecl scalblnf (float, long);
-  extern long double __cdecl scalblnl (long double, long);
-
-  /* 7.12.7.1 */
-  /* Implementations adapted from Cephes versions */ 
-  extern double __cdecl cbrt (double);
-  extern float __cdecl cbrtf (float);
-  extern long double __cdecl cbrtl (long double);
-
-  __CRT_INLINE float __cdecl hypotf (float x, float y)
-  { return (float) hypot (x, y);}
-  extern long double __cdecl hypotl (long double, long double);
-
-  extern long double __cdecl powl (long double, long double);
-  extern long double __cdecl expl(long double);
-  extern long double __cdecl expm1l(long double);
-  extern long double __cdecl coshl(long double);
-  extern long double __cdecl fabsl (long double);
-  extern long double __cdecl acosl(long double);
-  extern long double __cdecl asinl(long double);
-  extern long double __cdecl atanl(long double);
-  extern long double __cdecl atan2l(long double,long double);
-  extern long double __cdecl sinhl(long double);
-  extern long double __cdecl tanhl(long double);
-
-  /* 7.12.8.1 The erf functions  */
-  extern double __cdecl erf (double);
-  extern float __cdecl erff (float);
-  /* TODO
-  extern long double __cdecl erfl (long double);
-  */ 
-
-  /* 7.12.8.2 The erfc functions  */
-  extern double __cdecl erfc (double);
-  extern float __cdecl erfcf (float);
-  /* TODO
-  extern long double __cdecl erfcl (long double);
-  */ 
-
-  /* 7.12.8.3 The lgamma functions */
-  extern double __cdecl lgamma (double);
-  extern float __cdecl lgammaf (float);
-  extern long double __cdecl lgammal (long double);
-
-  /* 7.12.8.4 The tgamma functions */
-  extern double __cdecl tgamma (double);
-  extern float __cdecl tgammaf (float);
-  extern long double __cdecl tgammal (long double);
-
-  extern long double __cdecl ceill (long double);
-  extern long double __cdecl floorl (long double);
-  extern long double __cdecl frexpl(long double,int *);
-  extern long double __cdecl log10l(long double);
-  extern long double __cdecl logl(long double);
-  extern long double __cdecl cosl(long double);
-  extern long double __cdecl sinl(long double);
-  extern long double __cdecl tanl(long double);
-  extern long double sqrtl(long double);
-
-  /* 7.12.9.3 */
-  extern double __cdecl nearbyint ( double);
-  extern float __cdecl nearbyintf (float);
-  extern long double __cdecl nearbyintl (long double);
-
-  /* 7.12.9.4 */
-  /* round, using fpu control word settings */
-  __CRT_INLINE double __cdecl rint (double x)
-  {
-    double retval;
-    __asm__ (
-      "fldl    %1\n"
-      "frndint   \n"
-      "fstl    %0\n" : "=m" (retval) : "m" (x));
-    return retval;
-  }
-
-  __CRT_INLINE float __cdecl rintf (float x)
-  {
-    float retval;
-    __asm__ (
-      "flds    %1\n"
-      "frndint   \n"
-      "fsts    %0\n" : "=m" (retval) : "m" (x));
-    return retval;
-  }
-
-  __CRT_INLINE long double __cdecl rintl (long double x)
-  {
-    long double retval;
-    __asm__ (
-      "fldt    %1\n"
-      "frndint   \n"
-      "fstt    %0\n" : "=m" (retval) : "m" (x));
-    return retval;
-  }
-
-  /* 7.12.9.5 */
-  __CRT_INLINE long __cdecl lrint (double x) 
-  {
-    long retval;  
-    __asm__ __volatile__                         \
-      ("fldl   %1\n"                             \
-       "fistpl %0"  : "=m" (retval) : "m" (x));  \
-      return retval;
-  }
-
-  __CRT_INLINE long __cdecl lrintf (float x) 
-  {
-    long retval;
-    __asm__ __volatile__                         \
-      ("flds   %1\n"                             \
-       "fistpl %0"  : "=m" (retval) : "m" (x));  \
-      return retval;
-  }
-
-  __CRT_INLINE long __cdecl lrintl (long double x) 
-  {
-    long retval;
-    __asm__ __volatile__                         \
-      ("fldt   %1\n"                             \
-       "fistpl %0"  : "=m" (retval) : "m" (x));  \
-      return retval;
-  }
-
-  __CRT_INLINE long long __cdecl llrint (double x) 
-  {
-    long long retval;
-    __asm__ __volatile__                         \
-      ("fldl    %1\n"                            \
-       "fistpll %0"  : "=m" (retval) : "m" (x)); \
-      return retval;
-  }
-
-  __CRT_INLINE long long __cdecl llrintf (float x) 
-  {
-    long long retval;
-    __asm__ __volatile__                         \
-      ("flds   %1\n"                             \
-       "fistpll %0"  : "=m" (retval) : "m" (x)); \
-      return retval;
-  }
-
-  __CRT_INLINE long long __cdecl llrintl (long double x) 
-  {
-    long long retval;
-    __asm__ __volatile__                         \
-      ("fldt    %1\n"                            \
-       "fistpll %0"  : "=m" (retval) : "m" (x)); \
-      return retval;
-  }
-
-  #define FE_TONEAREST	0x0000
-  #define FE_DOWNWARD	0x0400
-  #define FE_UPWARD	0x0800
-  #define FE_TOWARDZERO	0x0c00
-
-  __CRT_INLINE double trunc (double _x)
-  {
-    double retval;
-    unsigned short saved_cw;
-    unsigned short tmp_cw;
-    __asm__ ("fnstcw %0;" : "=m" (saved_cw)); /* save FPU control word */
-    tmp_cw = (saved_cw & ~(FE_TONEAREST | FE_DOWNWARD | FE_UPWARD | FE_TOWARDZERO))
-	    | FE_TOWARDZERO;
-    __asm__ ("fldcw %0;" : : "m" (tmp_cw));
-    __asm__ ("fldl  %1;"
-             "frndint;"
-             "fstl  %0;" : "=m" (retval)  : "m" (_x)); /* round towards zero */
-    __asm__ ("fldcw %0;" : : "m" (saved_cw) ); /* restore saved control word */
-    return retval;
-  }
-
-  /* 7.12.9.6 */
-  /* round away from zero, regardless of fpu control word settings */
-  extern double __cdecl round (double);
-  extern float __cdecl roundf (float);
-  extern long double __cdecl roundl (long double);
-
-  /* 7.12.9.7  */
-  extern long __cdecl lround (double);
-  extern long __cdecl lroundf (float);
-  extern long __cdecl lroundl (long double);
-
-  extern long long __cdecl llround (double);
-  extern long long __cdecl llroundf (float);
-  extern long long __cdecl llroundl (long double);
-
-  /* 7.12.9.8 */
-  /* round towards zero, regardless of fpu control word settings */
-  extern double __cdecl trunc (double);
-  extern float __cdecl truncf (float);
-  extern long double __cdecl truncl (long double);
-
-  extern long double __cdecl fmodl (long double, long double);
-
-  /* 7.12.10.2 */ 
-  extern double __cdecl remainder (double, double);
-  extern float __cdecl remainderf (float, float);
-  extern long double __cdecl remainderl (long double, long double);
-
-  /* 7.12.10.3 */
-  extern double __cdecl remquo(double, double, int *);
-  extern float __cdecl remquof(float, float, int *);
-  extern long double __cdecl remquol(long double, long double, int *);
-
-  /* 7.12.11.1 */
-  extern double __cdecl copysign (double, double); /* in libmoldname.a */
-  extern float __cdecl copysignf (float, float);
-  extern long double __cdecl copysignl (long double, long double);
-
-  /* 7.12.11.2 Return a NaN */
-  extern double __cdecl nan(const char *tagp);
-  extern float __cdecl nanf(const char *tagp);
-  extern long double __cdecl nanl(const char *tagp);
-
-#ifndef __STRICT_ANSI__
-#define _nan() nan("")
-#define _nanf() nanf("")
-#define _nanl() nanl("")
-#endif
-
-  /* 7.12.11.3 */
-  extern double __cdecl nextafter (double, double); /* in libmoldname.a */
-  extern float __cdecl nextafterf (float, float);
-  extern long double __cdecl nextafterl (long double, long double);
-
-  /* 7.12.11.4 The nexttoward functions: TODO */
-
-  /* 7.12.12.1 */
-  /*  x > y ? (x - y) : 0.0  */
-  extern double __cdecl fdim (double x, double y);
-  extern float __cdecl fdimf (float x, float y);
-  extern long double __cdecl fdiml (long double x, long double y);
-
-  /* fmax and fmin.
-  NaN arguments are treated as missing data: if one argument is a NaN
-  and the other numeric, then these functions choose the numeric
-  value. */
-
-  /* 7.12.12.2 */
-  extern double __cdecl fmax  (double, double);
-  extern float __cdecl fmaxf (float, float);
-  extern long double __cdecl fmaxl (long double, long double);
-
-  /* 7.12.12.3 */
-  extern double __cdecl fmin (double, double);
-  extern float __cdecl fminf (float, float);
-  extern long double __cdecl fminl (long double, long double);
-
-  /* 7.12.13.1 */
-  /* return x * y + z as a ternary op */ 
-  extern double __cdecl fma (double, double, double);
-  extern float __cdecl fmaf (float, float, float);
-  extern long double __cdecl fmal (long double, long double, long double);
-
-
-#if 0 // gr: duplicate, see below
-  /* 7.12.14 */
-  /* 
-  *  With these functions, comparisons involving quiet NaNs set the FP
-  *  condition code to "unordered".  The IEEE floating-point spec
-  *  dictates that the result of floating-point comparisons should be
-  *  false whenever a NaN is involved, with the exception of the != op, 
-  *  which always returns true: yes, (NaN != NaN) is true).
-  */
-
-#if __GNUC__ >= 3
-
-#define isgreater(x, y) __builtin_isgreater(x, y)
-#define isgreaterequal(x, y) __builtin_isgreaterequal(x, y)
-#define isless(x, y) __builtin_isless(x, y)
-#define islessequal(x, y) __builtin_islessequal(x, y)
-#define islessgreater(x, y) __builtin_islessgreater(x, y)
-#define isunordered(x, y) __builtin_isunordered(x, y)
-
-#else
-  /*  helper  */
-  __CRT_INLINE int  __cdecl
-    __fp_unordered_compare (long double x, long double y){
-      unsigned short retval;
-      __asm__ ("fucom %%st(1);"
-	"fnstsw;": "=a" (retval) : "t" (x), "u" (y));
-      return retval;
-  }
-
-#define isgreater(x, y) ((__fp_unordered_compare(x, y) \
-  & 0x4500) == 0)
-#define isless(x, y) ((__fp_unordered_compare (y, x) \
-  & 0x4500) == 0)
-#define isgreaterequal(x, y) ((__fp_unordered_compare (x, y) \
-  & FP_INFINITE) == 0)
-#define islessequal(x, y) ((__fp_unordered_compare(y, x) \
-  & FP_INFINITE) == 0)
-#define islessgreater(x, y) ((__fp_unordered_compare(x, y) \
-  & FP_SUBNORMAL) == 0)
-#define isunordered(x, y) ((__fp_unordered_compare(x, y) \
-  & 0x4500) == 0x4500)
-
-#endif
-#endif //0
-
-
-#endif /* __STDC_VERSION__ >= 199901L */
-#endif /* __NO_ISOCEXT */
-
-#ifdef __cplusplus
-}
-extern "C++" {
-  template<class _Ty> inline _Ty _Pow_int(_Ty _X,int _Y) {
-    unsigned int _N;
-    if(_Y >= 0) _N = (unsigned int)_Y;
-    else _N = (unsigned int)(-_Y);
-    for(_Ty _Z = _Ty(1);;_X *= _X) {
-      if((_N & 1)!=0) _Z *= _X;
-      if((_N >>= 1)==0) return (_Y < 0 ? _Ty(1) / _Z : _Z); 
-    }
-  }
-}
-#endif
-
-#pragma pack(pop)
-
-/* 7.12.14 */
-/* 
- *  With these functions, comparisons involving quiet NaNs set the FP
- *  condition code to "unordered".  The IEEE floating-point spec
- *  dictates that the result of floating-point comparisons should be
- *  false whenever a NaN is involved, with the exception of the != op, 
- *  which always returns true: yes, (NaN != NaN) is true).
- */
-
-/* Mini libm (inline __fpclassify*, __signbit* and variants) */
-#include "tcc/tcc_libm.h"
-
-#endif /* End _MATH_H_ */
-
diff --git a/tinyc/win32/include/mem.h b/tinyc/win32/include/mem.h
deleted file mode 100644
index 255202359..000000000
--- a/tinyc/win32/include/mem.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-/*
- * This file is part of the Mingw32 package.
- *
- * mem.h maps to string.h
- */
-#ifndef	__STRICT_ANSI__
-#include <string.h>
-#endif
diff --git a/tinyc/win32/include/memory.h b/tinyc/win32/include/memory.h
deleted file mode 100644
index 90d88aedd..000000000
--- a/tinyc/win32/include/memory.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_MEMORY
-#define _INC_MEMORY
-
-#include <_mingw.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _CONST_RETURN
-#define _CONST_RETURN
-#endif
-
-#define _WConst_return _CONST_RETURN
-
-#ifndef _CRT_MEMORY_DEFINED
-#define _CRT_MEMORY_DEFINED
-  _CRTIMP void *__cdecl _memccpy(void *_Dst,const void *_Src,int _Val,size_t _MaxCount);
-  _CONST_RETURN void *__cdecl memchr(const void *_Buf ,int _Val,size_t _MaxCount);
-  _CRTIMP int __cdecl _memicmp(const void *_Buf1,const void *_Buf2,size_t _Size);
-  _CRTIMP int __cdecl _memicmp_l(const void *_Buf1,const void *_Buf2,size_t _Size,_locale_t _Locale);
-  int __cdecl memcmp(const void *_Buf1,const void *_Buf2,size_t _Size);
-  void *__cdecl memcpy(void *_Dst,const void *_Src,size_t _Size);
-  void *__cdecl memset(void *_Dst,int _Val,size_t _Size);
-
-#ifndef	NO_OLDNAMES
-  void *__cdecl memccpy(void *_Dst,const void *_Src,int _Val,size_t _Size);
-  int __cdecl memicmp(const void *_Buf1,const void *_Buf2,size_t _Size);
-#endif
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/tinyc/win32/include/process.h b/tinyc/win32/include/process.h
deleted file mode 100644
index dadaf2b7d..000000000
--- a/tinyc/win32/include/process.h
+++ /dev/null
@@ -1,176 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_PROCESS
-#define _INC_PROCESS
-
-#include <_mingw.h>
-
-/* Includes a definition of _pid_t and pid_t */
-#include <sys/types.h>
-
-#ifndef _POSIX_
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define _P_WAIT 0
-#define _P_NOWAIT 1
-#define _OLD_P_OVERLAY 2
-#define _P_NOWAITO 3
-#define _P_DETACH 4
-#define _P_OVERLAY 2
-
-#define _WAIT_CHILD 0
-#define _WAIT_GRANDCHILD 1
-
-  _CRTIMP uintptr_t __cdecl _beginthread(void (__cdecl *_StartAddress) (void *),unsigned _StackSize,void *_ArgList);
-  _CRTIMP void __cdecl _endthread(void);
-  _CRTIMP uintptr_t __cdecl _beginthreadex(void *_Security,unsigned _StackSize,unsigned (__stdcall *_StartAddress) (void *),void *_ArgList,unsigned _InitFlag,unsigned *_ThrdAddr);
-  _CRTIMP void __cdecl _endthreadex(unsigned _Retval);
-
-#ifndef _CRT_TERMINATE_DEFINED
-#define _CRT_TERMINATE_DEFINED
-  void __cdecl __MINGW_NOTHROW exit(int _Code) __MINGW_ATTRIB_NORETURN;
-  _CRTIMP void __cdecl __MINGW_NOTHROW _exit(int _Code) __MINGW_ATTRIB_NORETURN;
-
-#pragma push_macro("abort")
-#undef abort
-  void __cdecl __declspec(noreturn) abort(void);
-#pragma pop_macro("abort")
-
-#endif
-
-  _CRTIMP void __cdecl __MINGW_NOTHROW _cexit(void);
-  _CRTIMP void __cdecl __MINGW_NOTHROW _c_exit(void);
-  _CRTIMP int __cdecl _getpid(void);
-  _CRTIMP intptr_t __cdecl _cwait(int *_TermStat,intptr_t _ProcHandle,int _Action);
-  _CRTIMP intptr_t __cdecl _execl(const char *_Filename,const char *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _execle(const char *_Filename,const char *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _execlp(const char *_Filename,const char *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _execlpe(const char *_Filename,const char *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _execv(const char *_Filename,const char *const *_ArgList);
-  _CRTIMP intptr_t __cdecl _execve(const char *_Filename,const char *const *_ArgList,const char *const *_Env);
-  _CRTIMP intptr_t __cdecl _execvp(const char *_Filename,const char *const *_ArgList);
-  _CRTIMP intptr_t __cdecl _execvpe(const char *_Filename,const char *const *_ArgList,const char *const *_Env);
-  _CRTIMP intptr_t __cdecl _spawnl(int _Mode,const char *_Filename,const char *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _spawnle(int _Mode,const char *_Filename,const char *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _spawnlp(int _Mode,const char *_Filename,const char *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _spawnlpe(int _Mode,const char *_Filename,const char *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _spawnv(int _Mode,const char *_Filename,const char *const *_ArgList);
-  _CRTIMP intptr_t __cdecl _spawnve(int _Mode,const char *_Filename,const char *const *_ArgList,const char *const *_Env);
-  _CRTIMP intptr_t __cdecl _spawnvp(int _Mode,const char *_Filename,const char *const *_ArgList);
-  _CRTIMP intptr_t __cdecl _spawnvpe(int _Mode,const char *_Filename,const char *const *_ArgList,const char *const *_Env);
-
-#ifndef _CRT_SYSTEM_DEFINED
-#define _CRT_SYSTEM_DEFINED
-  int __cdecl system(const char *_Command);
-#endif
-
-#ifndef _WPROCESS_DEFINED
-#define _WPROCESS_DEFINED
-  _CRTIMP intptr_t __cdecl _wexecl(const wchar_t *_Filename,const wchar_t *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _wexecle(const wchar_t *_Filename,const wchar_t *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _wexeclp(const wchar_t *_Filename,const wchar_t *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _wexeclpe(const wchar_t *_Filename,const wchar_t *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _wexecv(const wchar_t *_Filename,const wchar_t *const *_ArgList);
-  _CRTIMP intptr_t __cdecl _wexecve(const wchar_t *_Filename,const wchar_t *const *_ArgList,const wchar_t *const *_Env);
-  _CRTIMP intptr_t __cdecl _wexecvp(const wchar_t *_Filename,const wchar_t *const *_ArgList);
-  _CRTIMP intptr_t __cdecl _wexecvpe(const wchar_t *_Filename,const wchar_t *const *_ArgList,const wchar_t *const *_Env);
-  _CRTIMP intptr_t __cdecl _wspawnl(int _Mode,const wchar_t *_Filename,const wchar_t *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _wspawnle(int _Mode,const wchar_t *_Filename,const wchar_t *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _wspawnlp(int _Mode,const wchar_t *_Filename,const wchar_t *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _wspawnlpe(int _Mode,const wchar_t *_Filename,const wchar_t *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _wspawnv(int _Mode,const wchar_t *_Filename,const wchar_t *const *_ArgList);
-  _CRTIMP intptr_t __cdecl _wspawnve(int _Mode,const wchar_t *_Filename,const wchar_t *const *_ArgList,const wchar_t *const *_Env);
-  _CRTIMP intptr_t __cdecl _wspawnvp(int _Mode,const wchar_t *_Filename,const wchar_t *const *_ArgList);
-  _CRTIMP intptr_t __cdecl _wspawnvpe(int _Mode,const wchar_t *_Filename,const wchar_t *const *_ArgList,const wchar_t *const *_Env);
-#ifndef _CRT_WSYSTEM_DEFINED
-#define _CRT_WSYSTEM_DEFINED
-  _CRTIMP int __cdecl _wsystem(const wchar_t *_Command);
-#endif
-#endif
-
-  void __cdecl __security_init_cookie(void);
-#if (defined(_X86_) && !defined(__x86_64))
-  void __fastcall __security_check_cookie(uintptr_t _StackCookie);
-  __declspec(noreturn) void __cdecl __report_gsfailure(void);
-#else
-  void __cdecl __security_check_cookie(uintptr_t _StackCookie);
-  __declspec(noreturn) void __cdecl __report_gsfailure(uintptr_t _StackCookie);
-#endif
-  extern uintptr_t __security_cookie;
-
-  intptr_t __cdecl _loaddll(char *_Filename);
-  int __cdecl _unloaddll(intptr_t _Handle);
-  int (__cdecl *__cdecl _getdllprocaddr(intptr_t _Handle,char *_ProcedureName,intptr_t _Ordinal))(void);
-
-#ifdef _DECL_DLLMAIN
-#ifdef _WINDOWS_
-  WINBOOL WINAPI DllMain(HANDLE _HDllHandle,DWORD _Reason,LPVOID _Reserved);
-  WINBOOL WINAPI _CRT_INIT(HANDLE _HDllHandle,DWORD _Reason,LPVOID _Reserved);
-  WINBOOL WINAPI _wCRT_INIT(HANDLE _HDllHandle,DWORD _Reason,LPVOID _Reserved);
-  extern WINBOOL (WINAPI *const _pRawDllMain)(HANDLE,DWORD,LPVOID);
-#else
-  int __stdcall DllMain(void *_HDllHandle,unsigned _Reason,void *_Reserved);
-  int __stdcall _CRT_INIT(void *_HDllHandle,unsigned _Reason,void *_Reserved);
-  int __stdcall _wCRT_INIT(void *_HDllHandle,unsigned _Reason,void *_Reserved);
-  extern int (__stdcall *const _pRawDllMain)(void *,unsigned,void *);
-#endif
-#endif
-
-#ifndef	NO_OLDNAMES
-#define P_WAIT _P_WAIT
-#define P_NOWAIT _P_NOWAIT
-#define P_OVERLAY _P_OVERLAY
-#define OLD_P_OVERLAY _OLD_P_OVERLAY
-#define P_NOWAITO _P_NOWAITO
-#define P_DETACH _P_DETACH
-#define WAIT_CHILD _WAIT_CHILD
-#define WAIT_GRANDCHILD _WAIT_GRANDCHILD
-
-  intptr_t __cdecl cwait(int *_TermStat,intptr_t _ProcHandle,int _Action);
-#ifdef __GNUC__
-  int __cdecl execl(const char *_Filename,const char *_ArgList,...);
-  int __cdecl execle(const char *_Filename,const char *_ArgList,...);
-  int __cdecl execlp(const char *_Filename,const char *_ArgList,...);
-  int __cdecl execlpe(const char *_Filename,const char *_ArgList,...);
-#else
-    intptr_t __cdecl execl(const char *_Filename,const char *_ArgList,...);
-  intptr_t __cdecl execle(const char *_Filename,const char *_ArgList,...);
-  intptr_t __cdecl execlp(const char *_Filename,const char *_ArgList,...);
-  intptr_t __cdecl execlpe(const char *_Filename,const char *_ArgList,...);
-#endif
-  intptr_t __cdecl spawnl(int,const char *_Filename,const char *_ArgList,...);
-  intptr_t __cdecl spawnle(int,const char *_Filename,const char *_ArgList,...);
-  intptr_t __cdecl spawnlp(int,const char *_Filename,const char *_ArgList,...);
-  intptr_t __cdecl spawnlpe(int,const char *_Filename,const char *_ArgList,...);
-  int __cdecl getpid(void);
-#ifdef __GNUC__
-  /* Those methods are predefined by gcc builtins to return int. So to prevent
-     stupid warnings, define them in POSIX way.  This is save, because those
-     methods do not return in success case, so that the return value is not
-     really dependent to its scalar width.  */
-  int __cdecl execv(const char *_Filename,const char *const _ArgList[]);
-  int __cdecl execve(const char *_Filename,const char *const _ArgList[],const char *const _Env[]);
-  int __cdecl execvp(const char *_Filename,const char *const _ArgList[]);
-  int __cdecl execvpe(const char *_Filename,const char *const _ArgList[],const char *const _Env[]);
-#else
-  intptr_t __cdecl execv(const char *_Filename,const char *const _ArgList[]);
-  intptr_t __cdecl execve(const char *_Filename,const char *const _ArgList[],const char *const _Env[]);
-  intptr_t __cdecl execvp(const char *_Filename,const char *const _ArgList[]);
-  intptr_t __cdecl execvpe(const char *_Filename,const char *const _ArgList[],const char *const _Env[]);
-#endif
-  intptr_t __cdecl spawnv(int,const char *_Filename,const char *const _ArgList[]);
-  intptr_t __cdecl spawnve(int,const char *_Filename,const char *const _ArgList[],const char *const _Env[]);
-  intptr_t __cdecl spawnvp(int,const char *_Filename,const char *const _ArgList[]);
-  intptr_t __cdecl spawnvpe(int,const char *_Filename,const char *const _ArgList[],char *const _Env[]);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-#endif
-#endif
diff --git a/tinyc/win32/include/sec_api/conio_s.h b/tinyc/win32/include/sec_api/conio_s.h
deleted file mode 100644
index 98d97ba2e..000000000
--- a/tinyc/win32/include/sec_api/conio_s.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-
-#ifndef _INC_CONIO_S
-#define _INC_CONIO_S
-
-#include <conio.h>
-
-#if defined(MINGW_HAS_SECURE_API)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  _CRTIMP errno_t __cdecl _cgets_s(char *_Buffer,size_t _Size,size_t *_SizeRead);
-  _CRTIMP int __cdecl _cprintf_s(const char *_Format,...);
-  _CRTIMP int __cdecl _cscanf_s(const char *_Format,...);
-  _CRTIMP int __cdecl _cscanf_s_l(const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vcprintf_s(const char *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _cprintf_s_l(const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vcprintf_s_l(const char *_Format,_locale_t _Locale,va_list _ArgList);
-
-#ifndef _WCONIO_DEFINED_S
-#define _WCONIO_DEFINED_S
-  _CRTIMP errno_t __cdecl _cgetws_s(wchar_t *_Buffer,size_t _SizeInWords,size_t *_SizeRead);
-  _CRTIMP int __cdecl _cwprintf_s(const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _cwscanf_s(const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _cwscanf_s_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vcwprintf_s(const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _cwprintf_s_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vcwprintf_s_l(const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-#endif
diff --git a/tinyc/win32/include/sec_api/crtdbg_s.h b/tinyc/win32/include/sec_api/crtdbg_s.h
deleted file mode 100644
index 4598b4f7c..000000000
--- a/tinyc/win32/include/sec_api/crtdbg_s.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-
-#ifndef _INC_CRTDBG_S
-#define _INC_CRTDBG_S
-
-#include <crtdbg.h>
-
-#if defined(MINGW_HAS_SECURE_API)
-
-#define _dupenv_s_dbg(ps1,size,s2,t,f,l) _dupenv_s(ps1,size,s2)
-#define _wdupenv_s_dbg(ps1,size,s2,t,f,l) _wdupenv_s(ps1,size,s2)
-
-#endif
-
-#endif
diff --git a/tinyc/win32/include/sec_api/io_s.h b/tinyc/win32/include/sec_api/io_s.h
deleted file mode 100644
index ec565a617..000000000
--- a/tinyc/win32/include/sec_api/io_s.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_IO_S
-#define _INC_IO_S
-
-#include <io.h>
-
-#if defined(MINGW_HAS_SECURE_API)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  _CRTIMP errno_t __cdecl _access_s(const char *_Filename,int _AccessMode);
-  _CRTIMP errno_t __cdecl _chsize_s(int _FileHandle,__int64 _Size);
-  _CRTIMP errno_t __cdecl _mktemp_s(char *_TemplateName,size_t _Size);
-  _CRTIMP errno_t __cdecl _umask_s(int _NewMode,int *_OldMode);
-
-#ifndef _WIO_S_DEFINED
-#define _WIO_S_DEFINED
-  _CRTIMP errno_t __cdecl _waccess_s(const wchar_t *_Filename,int _AccessMode);
-  _CRTIMP errno_t __cdecl _wmktemp_s(wchar_t *_TemplateName,size_t _SizeInWords);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-#endif
diff --git a/tinyc/win32/include/sec_api/mbstring_s.h b/tinyc/win32/include/sec_api/mbstring_s.h
deleted file mode 100644
index 6b2b188f8..000000000
--- a/tinyc/win32/include/sec_api/mbstring_s.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_MBSTRING_S
-#define _INC_MBSTRING_S
-
-#include <mbstring.h>
-
-#if defined(MINGW_HAS_SECURE_API)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _MBSTRING_S_DEFINED
-#define _MBSTRING_S_DEFINED
-  _CRTIMP errno_t __cdecl _mbscat_s(unsigned char *_Dst,size_t _DstSizeInBytes,const unsigned char *_Src);
-  _CRTIMP errno_t __cdecl _mbscat_s_l(unsigned char *_Dst,size_t _DstSizeInBytes,const unsigned char *_Src,_locale_t _Locale);
-  _CRTIMP errno_t __cdecl _mbscpy_s(unsigned char *_Dst,size_t _DstSizeInBytes,const unsigned char *_Src);
-  _CRTIMP errno_t __cdecl _mbscpy_s_l(unsigned char *_Dst,size_t _DstSizeInBytes,const unsigned char *_Src,_locale_t _Locale);
-  _CRTIMP errno_t __cdecl _mbslwr_s(unsigned char *_Str,size_t _SizeInBytes);
-  _CRTIMP errno_t __cdecl _mbslwr_s_l(unsigned char *_Str,size_t _SizeInBytes,_locale_t _Locale);
-  _CRTIMP errno_t __cdecl _mbsnbcat_s(unsigned char *_Dst,size_t _DstSizeInBytes,const unsigned char *_Src,size_t _MaxCount);
-  _CRTIMP errno_t __cdecl _mbsnbcat_s_l(unsigned char *_Dst,size_t _DstSizeInBytes,const unsigned char *_Src,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP errno_t __cdecl _mbsnbcpy_s(unsigned char *_Dst,size_t _DstSizeInBytes,const unsigned char *_Src,size_t _MaxCount);
-  _CRTIMP errno_t __cdecl _mbsnbcpy_s_l(unsigned char *_Dst,size_t _DstSizeInBytes,const unsigned char *_Src,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP errno_t __cdecl _mbsnbset_s(unsigned char *_Dst,size_t _DstSizeInBytes,unsigned int _Ch,size_t _MaxCount);
-  _CRTIMP errno_t __cdecl _mbsnbset_s_l(unsigned char *_Dst,size_t _DstSizeInBytes,unsigned int _Ch,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP errno_t __cdecl _mbsncat_s(unsigned char *_Dst,size_t _DstSizeInBytes,const unsigned char *_Src,size_t _MaxCount);
-  _CRTIMP errno_t __cdecl _mbsncat_s_l(unsigned char *_Dst,size_t _DstSizeInBytes,const unsigned char *_Src,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP errno_t __cdecl _mbsncpy_s(unsigned char *_Dst,size_t _DstSizeInBytes,const unsigned char *_Src,size_t _MaxCount);
-  _CRTIMP errno_t __cdecl _mbsncpy_s_l(unsigned char *_Dst,size_t _DstSizeInBytes,const unsigned char *_Src,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP errno_t __cdecl _mbsnset_s(unsigned char *_Dst,size_t _DstSizeInBytes,unsigned int _Val,size_t _MaxCount);
-  _CRTIMP errno_t __cdecl _mbsnset_s_l(unsigned char *_Dst,size_t _DstSizeInBytes,unsigned int _Val,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP errno_t __cdecl _mbsset_s(unsigned char *_Dst,size_t _DstSizeInBytes,unsigned int _Val);
-  _CRTIMP errno_t __cdecl _mbsset_s_l(unsigned char *_Dst,size_t _DstSizeInBytes,unsigned int _Val,_locale_t _Locale);
-  _CRTIMP unsigned char *__cdecl _mbstok_s(unsigned char *_Str,const unsigned char *_Delim,unsigned char **_Context);
-  _CRTIMP unsigned char *__cdecl _mbstok_s_l(unsigned char *_Str,const unsigned char *_Delim,unsigned char **_Context,_locale_t _Locale);
-  _CRTIMP errno_t __cdecl _mbsupr_s(unsigned char *_Str,size_t _SizeInBytes);
-  _CRTIMP errno_t __cdecl _mbsupr_s_l(unsigned char *_Str,size_t _SizeInBytes,_locale_t _Locale);
-  _CRTIMP errno_t __cdecl _mbccpy_s(unsigned char *_Dst,size_t _DstSizeInBytes,int *_PCopied,const unsigned char *_Src);
-  _CRTIMP errno_t __cdecl _mbccpy_s_l(unsigned char *_Dst,size_t _DstSizeInBytes,int *_PCopied,const unsigned char *_Src,_locale_t _Locale);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-#endif
diff --git a/tinyc/win32/include/sec_api/search_s.h b/tinyc/win32/include/sec_api/search_s.h
deleted file mode 100644
index cae899898..000000000
--- a/tinyc/win32/include/sec_api/search_s.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_SEARCH_S
-#define _INC_SEARCH_S
-
-#include <search.h>
-
-#if defined(MINGW_HAS_SECURE_API)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  _CRTIMP void *__cdecl _lfind_s(const void *_Key,const void *_Base,unsigned int *_NumOfElements,size_t _SizeOfElements,int (__cdecl *_PtFuncCompare)(void *,const void *,const void *),void *_Context);
-  _CRTIMP void *__cdecl _lsearch_s(const void *_Key,void *_Base,unsigned int *_NumOfElements,size_t _SizeOfElements,int (__cdecl *_PtFuncCompare)(void *,const void *,const void *),void *_Context);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-#endif
diff --git a/tinyc/win32/include/sec_api/stdio_s.h b/tinyc/win32/include/sec_api/stdio_s.h
deleted file mode 100644
index c9b803b12..000000000
--- a/tinyc/win32/include/sec_api/stdio_s.h
+++ /dev/null
@@ -1,145 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_STDIO_S
-#define _INC_STDIO_S
-
-#include <stdio.h>
-
-#if defined(MINGW_HAS_SECURE_API)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _STDIO_S_DEFINED
-#define _STDIO_S_DEFINED
-  _CRTIMP errno_t __cdecl clearerr_s(FILE *_File);
-  int __cdecl fprintf_s(FILE *_File,const char *_Format,...);
-  size_t __cdecl fread_s(void *_DstBuf,size_t _DstSize,size_t _ElementSize,size_t _Count,FILE *_File);
-  _CRTIMP int __cdecl _fscanf_s_l(FILE *_File,const char *_Format,_locale_t _Locale,...);
-  int __cdecl printf_s(const char *_Format,...);
-  _CRTIMP int __cdecl _scanf_l(const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _scanf_s_l(const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _snprintf_s(char *_DstBuf,size_t _DstSize,size_t _MaxCount,const char *_Format,...);
-  _CRTIMP int __cdecl _snprintf_c(char *_DstBuf,size_t _MaxCount,const char *_Format,...);
-  _CRTIMP int __cdecl _vsnprintf_c(char *_DstBuf,size_t _MaxCount,const char *_Format,va_list _ArgList);
-  int __cdecl sprintf_s(char *_DstBuf,size_t _DstSize,const char *_Format,...);
-  _CRTIMP int __cdecl _fscanf_l(FILE *_File,const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _sscanf_l(const char *_Src,const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _sscanf_s_l(const char *_Src,const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _snscanf_s(const char *_Src,size_t _MaxCount,const char *_Format,...);
-  _CRTIMP int __cdecl _snscanf_l(const char *_Src,size_t _MaxCount,const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _snscanf_s_l(const char *_Src,size_t _MaxCount,const char *_Format,_locale_t _Locale,...);
-  int __cdecl vfprintf_s(FILE *_File,const char *_Format,va_list _ArgList);
-  int __cdecl vprintf_s(const char *_Format,va_list _ArgList);
-  int __cdecl vsnprintf_s(char *_DstBuf,size_t _DstSize,size_t _MaxCount,const char *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _vsnprintf_s(char *_DstBuf,size_t _DstSize,size_t _MaxCount,const char *_Format,va_list _ArgList);
-  int __cdecl vsprintf_s(char *_DstBuf,size_t _Size,const char *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _fprintf_p(FILE *_File,const char *_Format,...);
-  _CRTIMP int __cdecl _printf_p(const char *_Format,...);
-  _CRTIMP int __cdecl _sprintf_p(char *_Dst,size_t _MaxCount,const char *_Format,...);
-  _CRTIMP int __cdecl _vfprintf_p(FILE *_File,const char *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _vprintf_p(const char *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _vsprintf_p(char *_Dst,size_t _MaxCount,const char *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _scprintf_p(const char *_Format,...);
-  _CRTIMP int __cdecl _vscprintf_p(const char *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _printf_l(const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _printf_p_l(const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vprintf_l(const char *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _vprintf_p_l(const char *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _fprintf_l(FILE *_File,const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _fprintf_p_l(FILE *_File,const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vfprintf_l(FILE *_File,const char *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _vfprintf_p_l(FILE *_File,const char *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _sprintf_l(char *_DstBuf,const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _sprintf_p_l(char *_DstBuf,size_t _MaxCount,const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vsprintf_l(char *_DstBuf,const char *_Format,_locale_t,va_list _ArgList);
-  _CRTIMP int __cdecl _vsprintf_p_l(char *_DstBuf,size_t _MaxCount,const char *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _scprintf_l(const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _scprintf_p_l(const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vscprintf_l(const char *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _vscprintf_p_l(const char *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _printf_s_l(const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vprintf_s_l(const char *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _fprintf_s_l(FILE *_File,const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vfprintf_s_l(FILE *_File,const char *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _sprintf_s_l(char *_DstBuf,size_t _DstSize,const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vsprintf_s_l(char *_DstBuf,size_t _DstSize,const char *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _snprintf_s_l(char *_DstBuf,size_t _DstSize,size_t _MaxCount,const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vsnprintf_s_l(char *_DstBuf,size_t _DstSize,size_t _MaxCount,const char *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _snprintf_l(char *_DstBuf,size_t _MaxCount,const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _snprintf_c_l(char *_DstBuf,size_t _MaxCount,const char *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vsnprintf_l(char *_DstBuf,size_t _MaxCount,const char *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _vsnprintf_c_l(char *_DstBuf,size_t _MaxCount,const char *,_locale_t _Locale,va_list _ArgList);
-
-#ifndef _WSTDIO_S_DEFINED
-#define _WSTDIO_S_DEFINED
-  _CRTIMP wchar_t *__cdecl _getws_s(wchar_t *_Str,size_t _SizeInWords);
-  int __cdecl fwprintf_s(FILE *_File,const wchar_t *_Format,...);
-  int __cdecl wprintf_s(const wchar_t *_Format,...);
-  int __cdecl vwprintf_s(const wchar_t *_Format,va_list _ArgList);
-  int __cdecl swprintf_s(wchar_t *_Dst,size_t _SizeInWords,const wchar_t *_Format,...);
-  int __cdecl vswprintf_s(wchar_t *_Dst,size_t _SizeInWords,const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _snwprintf_s(wchar_t *_DstBuf,size_t _DstSizeInWords,size_t _MaxCount,const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _vsnwprintf_s(wchar_t *_DstBuf,size_t _DstSizeInWords,size_t _MaxCount,const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _wprintf_s_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vwprintf_s_l(const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _fwprintf_s_l(FILE *_File,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vfwprintf_s_l(FILE *_File,const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _swprintf_s_l(wchar_t *_DstBuf,size_t _DstSize,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vswprintf_s_l(wchar_t *_DstBuf,size_t _DstSize,const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _snwprintf_s_l(wchar_t *_DstBuf,size_t _DstSize,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vsnwprintf_s_l(wchar_t *_DstBuf,size_t _DstSize,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _fwscanf_s_l(FILE *_File,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _swscanf_s_l(const wchar_t *_Src,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _snwscanf_s(const wchar_t *_Src,size_t _MaxCount,const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _snwscanf_s_l(const wchar_t *_Src,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _wscanf_s_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP errno_t __cdecl _wfopen_s(FILE **_File,const wchar_t *_Filename,const wchar_t *_Mode);
-  _CRTIMP errno_t __cdecl _wfreopen_s(FILE **_File,const wchar_t *_Filename,const wchar_t *_Mode,FILE *_OldFile);
-  _CRTIMP errno_t __cdecl _wtmpnam_s(wchar_t *_DstBuf,size_t _SizeInWords);
-  _CRTIMP int __cdecl _fwprintf_p(FILE *_File,const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _wprintf_p(const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _vfwprintf_p(FILE *_File,const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _vwprintf_p(const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _swprintf_p(wchar_t *_DstBuf,size_t _MaxCount,const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _vswprintf_p(wchar_t *_DstBuf,size_t _MaxCount,const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _scwprintf_p(const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _vscwprintf_p(const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _wprintf_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _wprintf_p_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vwprintf_l(const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _vwprintf_p_l(const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _fwprintf_l(FILE *_File,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _fwprintf_p_l(FILE *_File,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vfwprintf_l(FILE *_File,const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _vfwprintf_p_l(FILE *_File,const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _swprintf_c_l(wchar_t *_DstBuf,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _swprintf_p_l(wchar_t *_DstBuf,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vswprintf_c_l(wchar_t *_DstBuf,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _vswprintf_p_l(wchar_t *_DstBuf,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _scwprintf_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _scwprintf_p_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vscwprintf_p_l(const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _snwprintf_l(wchar_t *_DstBuf,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vsnwprintf_l(wchar_t *_DstBuf,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl __swprintf_l(wchar_t *_Dest,const wchar_t *_Format,_locale_t _Plocinfo,...);
-  _CRTIMP int __cdecl __vswprintf_l(wchar_t *_Dest,const wchar_t *_Format,_locale_t _Plocinfo,va_list _Args);
-  _CRTIMP int __cdecl _vscwprintf_l(const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _fwscanf_l(FILE *_File,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _swscanf_l(const wchar_t *_Src,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _snwscanf_l(const wchar_t *_Src,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _wscanf_l(const wchar_t *_Format,_locale_t _Locale,...);
-#endif
-#endif
-
-  _CRTIMP size_t __cdecl _fread_nolock_s(void *_DstBuf,size_t _DstSize,size_t _ElementSize,size_t _Count,FILE *_File);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
-#endif
diff --git a/tinyc/win32/include/sec_api/stdlib_s.h b/tinyc/win32/include/sec_api/stdlib_s.h
deleted file mode 100644
index f98262ccc..000000000
--- a/tinyc/win32/include/sec_api/stdlib_s.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_STDLIB_S
-#define _INC_STDLIB_S
-
-#include <stdlib.h>
-
-#if defined(MINGW_HAS_SECURE_API)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  _CRTIMP errno_t __cdecl _dupenv_s(char **_PBuffer,size_t *_PBufferSizeInBytes,const char *_VarName);
-  _CRTIMP errno_t __cdecl _itoa_s(int _Value,char *_DstBuf,size_t _Size,int _Radix);
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP errno_t __cdecl _i64toa_s(__int64 _Val,char *_DstBuf,size_t _Size,int _Radix);
-  _CRTIMP errno_t __cdecl _ui64toa_s(unsigned __int64 _Val,char *_DstBuf,size_t _Size,int _Radix);
-#endif
-  _CRTIMP errno_t __cdecl _ltoa_s(long _Val,char *_DstBuf,size_t _Size,int _Radix);
-  _CRTIMP errno_t __cdecl mbstowcs_s(size_t *_PtNumOfCharConverted,wchar_t *_DstBuf,size_t _SizeInWords,const char *_SrcBuf,size_t _MaxCount);
-  _CRTIMP errno_t __cdecl _mbstowcs_s_l(size_t *_PtNumOfCharConverted,wchar_t *_DstBuf,size_t _SizeInWords,const char *_SrcBuf,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP errno_t __cdecl _ultoa_s(unsigned long _Val,char *_DstBuf,size_t _Size,int _Radix);
-  _CRTIMP errno_t __cdecl _wctomb_s_l(int *_SizeConverted,char *_MbCh,size_t _SizeInBytes,wchar_t _WCh,_locale_t _Locale);
-  _CRTIMP errno_t __cdecl wcstombs_s(size_t *_PtNumOfCharConverted,char *_Dst,size_t _DstSizeInBytes,const wchar_t *_Src,size_t _MaxCountInBytes);
-  _CRTIMP errno_t __cdecl _wcstombs_s_l(size_t *_PtNumOfCharConverted,char *_Dst,size_t _DstSizeInBytes,const wchar_t *_Src,size_t _MaxCountInBytes,_locale_t _Locale);
-
-#ifndef _WSTDLIB_S_DEFINED
-#define _WSTDLIB_S_DEFINED
-  _CRTIMP errno_t __cdecl _itow_s (int _Val,wchar_t *_DstBuf,size_t _SizeInWords,int _Radix);
-  _CRTIMP errno_t __cdecl _ltow_s (long _Val,wchar_t *_DstBuf,size_t _SizeInWords,int _Radix);
-  _CRTIMP errno_t __cdecl _ultow_s (unsigned long _Val,wchar_t *_DstBuf,size_t _SizeInWords,int _Radix);
-  _CRTIMP errno_t __cdecl _wgetenv_s(size_t *_ReturnSize,wchar_t *_DstBuf,size_t _DstSizeInWords,const wchar_t *_VarName);
-  _CRTIMP errno_t __cdecl _wdupenv_s(wchar_t **_Buffer,size_t *_BufferSizeInWords,const wchar_t *_VarName);
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP errno_t __cdecl _i64tow_s(__int64 _Val,wchar_t *_DstBuf,size_t _SizeInWords,int _Radix);
-  _CRTIMP errno_t __cdecl _ui64tow_s(unsigned __int64 _Val,wchar_t *_DstBuf,size_t _SizeInWords,int _Radix);
-#endif
-#endif
-
-#ifndef _POSIX_
-  _CRTIMP errno_t __cdecl _ecvt_s(char *_DstBuf,size_t _Size,double _Val,int _NumOfDights,int *_PtDec,int *_PtSign);
-  _CRTIMP errno_t __cdecl _fcvt_s(char *_DstBuf,size_t _Size,double _Val,int _NumOfDec,int *_PtDec,int *_PtSign);
-  _CRTIMP errno_t __cdecl _gcvt_s(char *_DstBuf,size_t _Size,double _Val,int _NumOfDigits);
-  _CRTIMP errno_t __cdecl _makepath_s(char *_PathResult,size_t _Size,const char *_Drive,const char *_Dir,const char *_Filename,const char *_Ext);
-  _CRTIMP errno_t __cdecl _putenv_s(const char *_Name,const char *_Value);
-  _CRTIMP errno_t __cdecl _searchenv_s(const char *_Filename,const char *_EnvVar,char *_ResultPath,size_t _SizeInBytes);
-  _CRTIMP errno_t __cdecl _splitpath_s(const char *_FullPath,char *_Drive,size_t _DriveSize,char *_Dir,size_t _DirSize,char *_Filename,size_t _FilenameSize,char *_Ext,size_t _ExtSize);
-
-#ifndef _WSTDLIBP_S_DEFINED
-#define _WSTDLIBP_S_DEFINED
-  _CRTIMP errno_t __cdecl _wmakepath_s(wchar_t *_PathResult,size_t _SizeInWords,const wchar_t *_Drive,const wchar_t *_Dir,const wchar_t *_Filename,const wchar_t *_Ext);
-  _CRTIMP errno_t __cdecl _wputenv_s(const wchar_t *_Name,const wchar_t *_Value);
-  _CRTIMP errno_t __cdecl _wsearchenv_s(const wchar_t *_Filename,const wchar_t *_EnvVar,wchar_t *_ResultPath,size_t _SizeInWords);
-  _CRTIMP errno_t __cdecl _wsplitpath_s(const wchar_t *_FullPath,wchar_t *_Drive,size_t _DriveSizeInWords,wchar_t *_Dir,size_t _DirSizeInWords,wchar_t *_Filename,size_t _FilenameSizeInWords,wchar_t *_Ext,size_t _ExtSizeInWords);
-#endif
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-#endif
diff --git a/tinyc/win32/include/sec_api/stralign_s.h b/tinyc/win32/include/sec_api/stralign_s.h
deleted file mode 100644
index 5b78f5860..000000000
--- a/tinyc/win32/include/sec_api/stralign_s.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef __STRALIGN_H_S_
-#define __STRALIGN_H_S_
-
-#include <stralign.h>
-
-#if defined(MINGW_HAS_SECURE_API)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if !defined(I_X86_) && defined(_WSTRING_S_DEFINED)
-#if defined(__cplusplus) && defined(_WConst_Return)
-  static __inline PUWSTR ua_wcscpy_s(PUWSTR Destination,size_t DestinationSize,PCUWSTR Source) {
-    if(WSTR_ALIGNED(Source) && WSTR_ALIGNED(Destination)) return (wcscpy_s((PWSTR)Destination,DestinationSize,(PCWSTR)Source)==0 ? Destination : NULL);
-    return uaw_wcscpy((PCUWSTR)String,Character);
-  }
-#endif
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-#endif
-#endif
diff --git a/tinyc/win32/include/sec_api/string_s.h b/tinyc/win32/include/sec_api/string_s.h
deleted file mode 100644
index 9db70e77d..000000000
--- a/tinyc/win32/include/sec_api/string_s.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_STRING_S
-#define _INC_STRING_S
-
-#include <string.h>
-
-#if defined(MINGW_HAS_SECURE_API)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  _CRTIMP errno_t __cdecl _strset_s(char *_Dst,size_t _DstSize,int _Value);
-  _CRTIMP errno_t __cdecl _strerror_s(char *_Buf,size_t _SizeInBytes,const char *_ErrMsg);
-  _CRTIMP errno_t __cdecl _strlwr_s(char *_Str,size_t _Size);
-  _CRTIMP errno_t __cdecl _strlwr_s_l(char *_Str,size_t _Size,_locale_t _Locale);
-  _CRTIMP errno_t __cdecl _strnset_s(char *_Str,size_t _Size,int _Val,size_t _MaxCount);
-  _CRTIMP errno_t __cdecl _strupr_s(char *_Str,size_t _Size);
-  _CRTIMP errno_t __cdecl _strupr_s_l(char *_Str,size_t _Size,_locale_t _Locale);
-#ifndef _WSTRING_S_DEFINED
-#define _WSTRING_S_DEFINED
-  _CRTIMP wchar_t *__cdecl wcstok_s(wchar_t *_Str,const wchar_t *_Delim,wchar_t **_Context);
-  _CRTIMP errno_t __cdecl _wcserror_s(wchar_t *_Buf,size_t _SizeInWords,int _ErrNum);
-  _CRTIMP errno_t __cdecl __wcserror_s(wchar_t *_Buffer,size_t _SizeInWords,const wchar_t *_ErrMsg);
-  _CRTIMP errno_t __cdecl _wcsnset_s(wchar_t *_Dst,size_t _DstSizeInWords,wchar_t _Val,size_t _MaxCount);
-  _CRTIMP errno_t __cdecl _wcsset_s(wchar_t *_Str,size_t _SizeInWords,wchar_t _Val);
-  _CRTIMP errno_t __cdecl _wcslwr_s(wchar_t *_Str,size_t _SizeInWords);
-  _CRTIMP errno_t __cdecl _wcslwr_s_l(wchar_t *_Str,size_t _SizeInWords,_locale_t _Locale);
-  _CRTIMP errno_t __cdecl _wcsupr_s(wchar_t *_Str,size_t _Size);
-  _CRTIMP errno_t __cdecl _wcsupr_s_l(wchar_t *_Str,size_t _Size,_locale_t _Locale);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-#endif
-#endif
diff --git a/tinyc/win32/include/sec_api/sys/timeb_s.h b/tinyc/win32/include/sec_api/sys/timeb_s.h
deleted file mode 100644
index af5ef098f..000000000
--- a/tinyc/win32/include/sec_api/sys/timeb_s.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-
-#ifndef _TIMEB_H_S
-#define _TIMEB_H_S
-
-#include <sys/timeb.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if defined(MINGW_HAS_SECURE_API)
-
-#ifdef _USE_32BIT_TIME_T
-#define _ftime_s _ftime32_s
-#else
-#define _ftime_s _ftime64_s
-#endif
-
-  _CRTIMP errno_t __cdecl _ftime32_s(struct __timeb32 *_Time);
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP errno_t __cdecl _ftime64_s(struct __timeb64 *_Time);
-#endif
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/tinyc/win32/include/sec_api/tchar_s.h b/tinyc/win32/include/sec_api/tchar_s.h
deleted file mode 100644
index 343d348eb..000000000
--- a/tinyc/win32/include/sec_api/tchar_s.h
+++ /dev/null
@@ -1,266 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_TCHAR_S
-#define _INC_TCHAR_S
-
-#include <tchar.h>
-
-#if defined(MINGW_HAS_SECURE_API)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifdef _UNICODE
-
-#define _tprintf_s wprintf_s
-#define _tprintf_s_l _wprintf_s_l
-#define _tcprintf_s _cwprintf_s
-#define _tcprintf_s_l _cwprintf_s_l
-#define _vtcprintf_s _vcwprintf_s
-#define _vtcprintf_s_l _vcwprintf_s_l
-#define _ftprintf_s fwprintf_s
-#define _ftprintf_s_l _fwprintf_s_l
-#define _stprintf_s swprintf_s
-#define _stprintf_s_l _swprintf_s_l
-#define _sntprintf_s _snwprintf_s
-#define _sntprintf_s_l _snwprintf_s_l
-#define _vtprintf_s vwprintf_s
-#define _vtprintf_s_l _vwprintf_s_l
-#define _vftprintf_s vfwprintf_s
-#define _vftprintf_s_l _vfwprintf_s_l
-#define _vstprintf_s vswprintf_s
-#define _vstprintf_s_l _vswprintf_s_l
-#define _vsntprintf_s _vsnwprintf_s
-#define _vsntprintf_s_l _vsnwprintf_s_l
-
-#define _tscanf_s wscanf_s
-#define _tscanf_s_l _wscanf_s_l
-#define _tcscanf_s _cwscanf_s
-#define _tcscanf_s_l _cwscanf_s_l
-#define _ftscanf_s fwscanf_s
-#define _ftscanf_s_l _fwscanf_s_l
-#define _stscanf_s swscanf_s
-#define _stscanf_s_l _swscanf_s_l
-#define _sntscanf_s _snwscanf_s
-#define _sntscanf_s_l _snwscanf_s_l
-
-#define _cgetts_s _cgetws_s
-#define _getts_s _getws_s
-
-#define _itot_s _itow_s
-#define _ltot_s _ltow_s
-#define _ultot_s _ultow_s
-#define _i64tot_s _i64tow_s
-#define _ui64tot_s _ui64tow_s
-
-#define _tcscat_s wcscat_s
-#define _tcscpy_s wcscpy_s
-#define _tcsncat_s wcsncat_s
-#define _tcsncat_s_l _wcsncat_s_l
-#define _tcsncpy_s wcsncpy_s
-#define _tcsncpy_s_l _wcsncpy_s_l
-#define _tcstok_s wcstok_s
-#define _tcstok_s_l _wcstok_s_l
-#define _tcserror_s _wcserror_s
-#define __tcserror_s __wcserror_s
-
-#define _tcsnset_s _wcsnset_s
-#define _tcsnset_s_l _wcsnset_s_l
-#define _tcsset_s _wcsset_s
-#define _tcsset_s_l _wcsset_s_l
-
-#define _tasctime_s _wasctime_s
-#define _tctime_s _wctime_s
-#define _tctime32_s _wctime32_s
-#define _tctime64_s _wctime64_s
-#define _tstrdate_s _wstrdate_s
-#define _tstrtime_s _wstrtime_s
-
-#define _tgetenv_s _wgetenv_s
-#define _tdupenv_s _wdupenv_s
-#define _tmakepath_s _wmakepath_s
-#define _tputenv_s _wputenv_s
-#define _tsearchenv_s _wsearchenv_s
-#define _tsplitpath_s _wsplitpath_s
-
-#define _tfopen_s _wfopen_s
-#define _tfreopen_s _wfreopen_s
-#define _ttmpnam_s _wtmpnam_s
-#define _taccess_s _waccess_s
-#define _tmktemp_s _wmktemp_s
-
-#define _tcsnccat_s wcsncat_s
-#define _tcsnccat_s_l _wcsncat_s_l
-#define _tcsnccpy_s wcsncpy_s
-#define _tcsnccpy_s_l _wcsncpy_s_l
-
-#define _tcslwr_s _wcslwr_s
-#define _tcslwr_s_l _wcslwr_s_l
-#define _tcsupr_s _wcsupr_s
-#define _tcsupr_s_l _wcsupr_s_l
-
-#define _wcstok_s_l(_String,_Delimiters,_Current_position,_Locale) (wcstok_s(_String,_Delimiters,_Current_position))
-#define _wcsnset_s_l(_Destination,_Destination_size_chars,_Value,_Count,_Locale) (_wcsnset_s(_Destination,_Destination_size_chars,_Value,_Count))
-#define _wcsset_s_l(_Destination,_Destination_size_chars,_Value,_Locale) (_wcsset_s(_Destination,_Destination_size_chars,_Value))
-
-#else
-
-#define _tprintf_s printf_s
-#define _tprintf_s_l _printf_s_l
-#define _tcprintf_s _cprintf_s
-#define _tcprintf_s_l _cprintf_s_l
-#define _vtcprintf_s _vcprintf_s
-#define _vtcprintf_s_l _vcprintf_s_l
-#define _ftprintf_s fprintf_s
-#define _ftprintf_s_l _fprintf_s_l
-#define _stprintf_s sprintf_s
-#define _stprintf_s_l _sprintf_s_l
-#define _sntprintf_s _snprintf_s
-#define _sntprintf_s_l _snprintf_s_l
-#define _vtprintf_s vprintf_s
-#define _vtprintf_s_l _vprintf_s_l
-#define _vftprintf_s vfprintf_s
-#define _vftprintf_s_l _vfprintf_s_l
-#define _vstprintf_s vsprintf_s
-#define _vstprintf_s_l _vsprintf_s_l
-#define _vsntprintf_s _vsnprintf_s
-#define _vsntprintf_s_l _vsnprintf_s_l
-#define _tscanf_s scanf_s
-#define _tscanf_s_l _scanf_s_l
-#define _tcscanf_s _cscanf_s
-#define _tcscanf_s_l _cscanf_s_l
-#define _ftscanf_s fscanf_s
-#define _ftscanf_s_l _fscanf_s_l
-#define _stscanf_s sscanf_s
-#define _stscanf_s_l _sscanf_s_l
-#define _sntscanf_s _snscanf_s
-#define _sntscanf_s_l _snscanf_s_l
-
-#define _getts_s gets_s
-#define _cgetts_s _cgets_s
-#define _itot_s _itoa_s
-#define _ltot_s _ltoa_s
-#define _ultot_s _ultoa_s
-#define _i64tot_s _i64toa_s
-#define _ui64tot_s _ui64toa_s
-
-#define _tcscat_s strcat_s
-#define _tcscpy_s strcpy_s
-#define _tcserror_s strerror_s
-#define __tcserror_s _strerror_s
-
-#define _tasctime_s asctime_s
-#define _tctime_s ctime_s
-#define _tctime32_s _ctime32_s
-#define _tctime64_s _ctime64_s
-#define _tstrdate_s _strdate_s
-#define _tstrtime_s _strtime_s
-
-#define _tgetenv_s getenv_s
-#define _tdupenv_s _dupenv_s
-#define _tmakepath_s _makepath_s
-#define _tputenv_s _putenv_s
-#define _tsearchenv_s _searchenv_s
-#define _tsplitpath_s _splitpath_s
-
-#define _tfopen_s fopen_s
-#define _tfreopen_s freopen_s
-#define _ttmpnam_s tmpnam_s
-#define _tmktemp_s _mktemp_s
-
-#ifndef _POSIX_
-#define _taccess_s _access_s
-#endif
-
-#define _tsopen_s _sopen_s
-
-#ifdef _MBCS
-
-#ifdef _MB_MAP_DIRECT
-
-#define _tcsncat_s _mbsnbcat_s
-#define _tcsncat_s_l _mbsnbcat_s_l
-#define _tcsncpy_s _mbsnbcpy_s
-#define _tcsncpy_s_l _mbsnbcpy_s_l
-#define _tcstok_s _mbstok_s
-#define _tcstok_s_l _mbstok_s_l
-
-#define _tcsnset_s _mbsnbset_s
-#define _tcsnset_s_l _mbsnbset_s_l
-#define _tcsset_s _mbsset_s
-#define _tcsset_s_l _mbsset_s_l
-
-#define _tcsnccat_s _mbsncat_s
-#define _tcsnccat_s_l _mbsncat_s_l
-#define _tcsnccpy_s _mbsncpy_s
-#define _tcsnccpy_s_l _mbsncpy_s_l
-#define _tcsncset_s _mbsnset_s
-#define _tcsncset_s_l _mbsnset_s_l
-
-#define _tcslwr_s _mbslwr_s
-#define _tcslwr_s_l _mbslwr_s_l
-#define _tcsupr_s _mbsupr_s
-#define _tcsupr_s_l _mbsupr_s_l
-
-#define _tccpy_s _mbccpy_s
-#define _tccpy_s_l _mbccpy_s_l
-#else
-
-  _CRTIMP char *__cdecl _tcsncat_s(char *_Dst,size_t _DstSizeInChars,const char *_Src,size_t _MaxCount);
-  _CRTIMP char *__cdecl _tcsncat_s_l(char *_Dst,size_t _DstSizeInChars,const char *_Src,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP char *__cdecl _tcsncpy_s(char *_Dst,size_t _DstSizeInChars,const char *_Src,size_t _MaxCount);
-  _CRTIMP char *__cdecl _tcsncpy_s_l(char *_Dst,size_t _DstSizeInChars,const char *_Src,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP char *__cdecl _tcstok_s(char *_Str,const char *_Delim,char **_Context);
-  _CRTIMP char *__cdecl _tcstok_s_l(char *_Str,const char *_Delim,char **_Context,_locale_t _Locale);
-  _CRTIMP errno_t __cdecl _tcsset_s(char *_Str,size_t _SizeInChars,unsigned int _Val);
-  _CRTIMP errno_t __cdecl _tcsset_s_l(char *_Str,size_t _SizeInChars,unsigned int,_locale_t _Locale);
-  _CRTIMP char *__cdecl _tcsnccat_s(char *_Dst,size_t _DstSizeInChars,const char *_Src,size_t _MaxCount);
-  _CRTIMP char *__cdecl _tcsnccat_s_l(char *_Dst,size_t _DstSizeInChars,const char *_Src,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP char *__cdecl _tcsnccpy_s(char *_Dst,size_t _DstSizeInChars,const char *_Src,size_t _MaxCount);
-  _CRTIMP char *__cdecl _tcsnccpy_s_l(char *_Dst,size_t _DstSizeInChars,const char *_Src,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP char *__cdecl _tcslwr_s(char *_Str,size_t _SizeInChars);
-  _CRTIMP char *__cdecl _tcslwr_s_l(char *_Str,size_t _SizeInChars,_locale_t _Locale);
-  _CRTIMP char *__cdecl _tcsupr_s(char *_Str,size_t _SizeInChars);
-  _CRTIMP char *__cdecl _tcsupr_s_l(char *_Str,size_t _SizeInChars,_locale_t _Locale);
-
-#endif
-
-#else
-
-#define _tcsncat_s strncat_s
-#define _tcsncat_s_l _strncat_s_l
-#define _tcsncpy_s strncpy_s
-#define _tcsncpy_s_l _strncpy_s_l
-#define _tcstok_s strtok_s
-#define _tcstok_s_l _strtok_s_l
-
-#define _tcsnset_s _strnset_s
-#define _tcsnset_s_l _strnset_s_l
-#define _tcsset_s _strset_s
-#define _tcsset_s _strset_s
-#define _tcsset_s_l _strset_s_l
-
-#define _tcsnccat_s strncat_s
-#define _tcsnccat_s_l _strncat_s_l
-#define _tcsnccpy_s strncpy_s
-#define _tcsnccpy_s_l _strncpy_s_l
-
-#define _tcslwr_s _strlwr_s
-#define _tcslwr_s_l _strlwr_s_l
-#define _tcsupr_s _strupr_s
-#define _tcsupr_s_l _strupr_s_l
-
-#define _strnset_s_l(_Destination,_Destination_size_chars,_Value,_Count,_Locale) (_strnset_s(_Destination,_Destination_size_chars,_Value,_Count))
-#define _strset_s_l(_Destination,_Destination_size_chars,_Value,_Locale) (_strset_s(_Destination,_Destination_size_chars,_Value))
-#endif
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-#endif
-#endif
diff --git a/tinyc/win32/include/sec_api/time_s.h b/tinyc/win32/include/sec_api/time_s.h
deleted file mode 100644
index 9603b94fc..000000000
--- a/tinyc/win32/include/sec_api/time_s.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _TIME_H__S
-#define _TIME_H__S
-
-#include <time.h>
-
-#if defined(MINGW_HAS_SECURE_API)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  _CRTIMP errno_t __cdecl _ctime32_s(char *_Buf,size_t _SizeInBytes,const __time32_t *_Time);
-  _CRTIMP errno_t __cdecl _gmtime32_s(struct tm *_Tm,const __time32_t *_Time);
-  _CRTIMP errno_t __cdecl _localtime32_s(struct tm *_Tm,const __time32_t *_Time);
-  _CRTIMP errno_t __cdecl _strdate_s(char *_Buf,size_t _SizeInBytes);
-  _CRTIMP errno_t __cdecl _strtime_s(char *_Buf ,size_t _SizeInBytes);
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP errno_t __cdecl _ctime64_s(char *_Buf,size_t _SizeInBytes,const __time64_t *_Time);
-  _CRTIMP errno_t __cdecl _gmtime64_s(struct tm *_Tm,const __time64_t *_Time);
-  _CRTIMP errno_t __cdecl _localtime64_s(struct tm *_Tm,const __time64_t *_Time);
-#endif
-
-#ifndef _WTIME_S_DEFINED
-#define _WTIME_S_DEFINED
-  _CRTIMP errno_t __cdecl _wasctime_s(wchar_t *_Buf,size_t _SizeInWords,const struct tm *_Tm);
-  _CRTIMP errno_t __cdecl _wctime32_s(wchar_t *_Buf,size_t _SizeInWords,const __time32_t *_Time);
-  _CRTIMP errno_t __cdecl _wstrdate_s(wchar_t *_Buf,size_t _SizeInWords);
-  _CRTIMP errno_t __cdecl _wstrtime_s(wchar_t *_Buf,size_t _SizeInWords);
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP errno_t __cdecl _wctime64_s(wchar_t *_Buf,size_t _SizeInWords,const __time64_t *_Time);
-#endif
-
-#if !defined (RC_INVOKED) && !defined (_INC_WTIME_S_INL)
-#define _INC_WTIME_S_INL
-#ifdef _USE_32BIT_TIME_T
-__CRT_INLINE errno_t __cdecl _wctime_s(wchar_t *_Buffer,size_t _SizeInWords,const time_t *_Time) { return _wctime32_s(_Buffer,_SizeInWords,_Time); }
-#else
-__CRT_INLINE errno_t __cdecl _wctime_s(wchar_t *_Buffer,size_t _SizeInWords,const time_t *_Time) { return _wctime64_s(_Buffer,_SizeInWords,_Time); }
-#endif
-#endif
-#endif
-
-#ifndef RC_INVOKED
-#ifdef _USE_32BIT_TIME_T
-__CRT_INLINE errno_t __cdecl localtime_s(struct tm *_Tm,const time_t *_Time) { return _localtime32_s(_Tm,_Time); }
-#else
-__CRT_INLINE errno_t __cdecl localtime_s(struct tm *_Tm,const time_t *_Time) { return _localtime64_s(_Tm,_Time); }
-#endif
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-#endif
diff --git a/tinyc/win32/include/sec_api/wchar_s.h b/tinyc/win32/include/sec_api/wchar_s.h
deleted file mode 100644
index 94251aa8b..000000000
--- a/tinyc/win32/include/sec_api/wchar_s.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_WCHAR_S
-#define _INC_WCHAR_S
-
-#include <wchar.h>
-
-#if defined(MINGW_HAS_SECURE_API)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _WIO_S_DEFINED
-#define _WIO_S_DEFINED
-  _CRTIMP errno_t __cdecl _waccess_s(const wchar_t *_Filename,int _AccessMode);
-  _CRTIMP errno_t __cdecl _wmktemp_s(wchar_t *_TemplateName,size_t _SizeInWords);
-#endif
-
-#ifndef _WCONIO_S_DEFINED
-#define _WCONIO_S_DEFINED
-  _CRTIMP errno_t __cdecl _cgetws_s(wchar_t *_Buffer,size_t _SizeInWords,size_t *_SizeRead);
-  _CRTIMP int __cdecl _cwprintf_s(const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _cwscanf_s(const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _cwscanf_s_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vcwprintf_s(const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _cwprintf_s_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vcwprintf_s_l(const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-#endif
-
-#ifndef _WSTDIO_S_DEFINED
-#define _WSTDIO_S_DEFINED
-  _CRTIMP wchar_t *__cdecl _getws_s(wchar_t *_Str,size_t _SizeInWords);
-  int __cdecl fwprintf_s(FILE *_File,const wchar_t *_Format,...);
-  int __cdecl wprintf_s(const wchar_t *_Format,...);
-  int __cdecl vfwprintf_s(FILE *_File,const wchar_t *_Format,va_list _ArgList);
-  int __cdecl vwprintf_s(const wchar_t *_Format,va_list _ArgList);
-  int __cdecl swprintf_s(wchar_t *_Dst,size_t _SizeInWords,const wchar_t *_Format,...);
-  int __cdecl vswprintf_s(wchar_t *_Dst,size_t _SizeInWords,const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _snwprintf_s(wchar_t *_DstBuf,size_t _DstSizeInWords,size_t _MaxCount,const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _vsnwprintf_s(wchar_t *_DstBuf,size_t _DstSizeInWords,size_t _MaxCount,const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _wprintf_s_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vwprintf_s_l(const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _fwprintf_s_l(FILE *_File,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vfwprintf_s_l(FILE *_File,const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _swprintf_s_l(wchar_t *_DstBuf,size_t _DstSize,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vswprintf_s_l(wchar_t *_DstBuf,size_t _DstSize,const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _snwprintf_s_l(wchar_t *_DstBuf,size_t _DstSize,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vsnwprintf_s_l(wchar_t *_DstBuf,size_t _DstSize,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _fwscanf_s_l(FILE *_File,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _swscanf_s_l(const wchar_t *_Src,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _snwscanf_s(const wchar_t *_Src,size_t _MaxCount,const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _snwscanf_s_l(const wchar_t *_Src,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _wscanf_s_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP errno_t __cdecl _wfopen_s(FILE **_File,const wchar_t *_Filename,const wchar_t *_Mode);
-  _CRTIMP errno_t __cdecl _wfreopen_s(FILE **_File,const wchar_t *_Filename,const wchar_t *_Mode,FILE *_OldFile);
-  _CRTIMP errno_t __cdecl _wtmpnam_s(wchar_t *_DstBuf,size_t _SizeInWords);
-#endif
-
-#ifndef _WSTDLIB_S_DEFINED
-#define _WSTDLIB_S_DEFINED
-  _CRTIMP errno_t __cdecl _itow_s (int _Val,wchar_t *_DstBuf,size_t _SizeInWords,int _Radix);
-  _CRTIMP errno_t __cdecl _ltow_s (long _Val,wchar_t *_DstBuf,size_t _SizeInWords,int _Radix);
-  _CRTIMP errno_t __cdecl _ultow_s (unsigned long _Val,wchar_t *_DstBuf,size_t _SizeInWords,int _Radix);
-  _CRTIMP errno_t __cdecl _wgetenv_s(size_t *_ReturnSize,wchar_t *_DstBuf,size_t _DstSizeInWords,const wchar_t *_VarName);
-  _CRTIMP errno_t __cdecl _wdupenv_s(wchar_t **_Buffer,size_t *_BufferSizeInWords,const wchar_t *_VarName);
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP errno_t __cdecl _i64tow_s(__int64 _Val,wchar_t *_DstBuf,size_t _SizeInWords,int _Radix);
-  _CRTIMP errno_t __cdecl _ui64tow_s(unsigned __int64 _Val,wchar_t *_DstBuf,size_t _SizeInWords,int _Radix);
-#endif
-#endif
-
-#ifndef _POSIX_
-#ifndef _WSTDLIBP_S_DEFINED
-#define _WSTDLIBP_S_DEFINED
-  _CRTIMP errno_t __cdecl _wmakepath_s(wchar_t *_PathResult,size_t _SizeInWords,const wchar_t *_Drive,const wchar_t *_Dir,const wchar_t *_Filename,const wchar_t *_Ext);
-  _CRTIMP errno_t __cdecl _wputenv_s(const wchar_t *_Name,const wchar_t *_Value);
-  _CRTIMP errno_t __cdecl _wsearchenv_s(const wchar_t *_Filename,const wchar_t *_EnvVar,wchar_t *_ResultPath,size_t _SizeInWords);
-  _CRTIMP errno_t __cdecl _wsplitpath_s(const wchar_t *_FullPath,wchar_t *_Drive,size_t _DriveSizeInWords,wchar_t *_Dir,size_t _DirSizeInWords,wchar_t *_Filename,size_t _FilenameSizeInWords,wchar_t *_Ext,size_t _ExtSizeInWords);
-#endif
-#endif
-
-#ifndef _WSTRING_S_DEFINED
-#define _WSTRING_S_DEFINED
-  _CRTIMP wchar_t *__cdecl wcstok_s(wchar_t *_Str,const wchar_t *_Delim,wchar_t **_Context);
-  _CRTIMP errno_t __cdecl _wcserror_s(wchar_t *_Buf,size_t _SizeInWords,int _ErrNum);
-  _CRTIMP errno_t __cdecl __wcserror_s(wchar_t *_Buffer,size_t _SizeInWords,const wchar_t *_ErrMsg);
-  _CRTIMP errno_t __cdecl _wcsnset_s(wchar_t *_Dst,size_t _DstSizeInWords,wchar_t _Val,size_t _MaxCount);
-  _CRTIMP errno_t __cdecl _wcsset_s(wchar_t *_Str,size_t _SizeInWords,wchar_t _Val);
-  _CRTIMP errno_t __cdecl _wcslwr_s(wchar_t *_Str,size_t _SizeInWords);
-  _CRTIMP errno_t __cdecl _wcslwr_s_l(wchar_t *_Str,size_t _SizeInWords,_locale_t _Locale);
-  _CRTIMP errno_t __cdecl _wcsupr_s(wchar_t *_Str,size_t _Size);
-  _CRTIMP errno_t __cdecl _wcsupr_s_l(wchar_t *_Str,size_t _Size,_locale_t _Locale);
-#endif
-
-#ifndef _WTIME_S_DEFINED
-#define _WTIME_S_DEFINED
-  _CRTIMP errno_t __cdecl _wasctime_s(wchar_t *_Buf,size_t _SizeInWords,const struct tm *_Tm);
-  _CRTIMP errno_t __cdecl _wctime32_s(wchar_t *_Buf,size_t _SizeInWords,const __time32_t *_Time);
-  _CRTIMP errno_t __cdecl _wstrdate_s(wchar_t *_Buf,size_t _SizeInWords);
-  _CRTIMP errno_t __cdecl _wstrtime_s(wchar_t *_Buf,size_t _SizeInWords);
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP errno_t __cdecl _wctime64_s(wchar_t *_Buf,size_t _SizeInWords,const __time64_t *_Time);
-#endif
-
-#if !defined (RC_INVOKED) && !defined (_INC_WTIME_S_INL)
-#define _INC_WTIME_S_INL
-#ifdef _USE_32BIT_TIME_T
-__CRT_INLINE errno_t __cdecl _wctime_s(wchar_t *_Buffer,size_t _SizeInWords,const time_t *_Time) { return _wctime32_s(_Buffer,_SizeInWords,_Time); }
-#else
-__CRT_INLINE errno_t __cdecl _wctime_s(wchar_t *_Buffer,size_t _SizeInWords,const time_t *_Time) { return _wctime64_s(_Buffer,_SizeInWords,_Time); }
-#endif
-#endif
-#endif
-
-  _CRTIMP errno_t __cdecl mbsrtowcs_s(size_t *_Retval,wchar_t *_Dst,size_t _SizeInWords,const char **_PSrc,size_t _N,mbstate_t *_State);
-  _CRTIMP errno_t __cdecl wcrtomb_s(size_t *_Retval,char *_Dst,size_t _SizeInBytes,wchar_t _Ch,mbstate_t *_State);
-  _CRTIMP errno_t __cdecl wcsrtombs_s(size_t *_Retval,char *_Dst,size_t _SizeInBytes,const wchar_t **_Src,size_t _Size,mbstate_t *_State);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-#endif
diff --git a/tinyc/win32/include/setjmp.h b/tinyc/win32/include/setjmp.h
deleted file mode 100644
index e4f142a3f..000000000
--- a/tinyc/win32/include/setjmp.h
+++ /dev/null
@@ -1,160 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_SETJMP
-#define _INC_SETJMP
-
-#include <_mingw.h>
-
-#pragma pack(push,_CRT_PACKING)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if (defined(_X86_) && !defined(__x86_64))
-
-#define _JBLEN 16
-#define _JBTYPE int
-
-  typedef struct __JUMP_BUFFER {
-    unsigned long Ebp;
-    unsigned long Ebx;
-    unsigned long Edi;
-    unsigned long Esi;
-    unsigned long Esp;
-    unsigned long Eip;
-    unsigned long Registration;
-    unsigned long TryLevel;
-    unsigned long Cookie;
-    unsigned long UnwindFunc;
-    unsigned long UnwindData[6];
-  } _JUMP_BUFFER;
-#elif defined(__ia64__)
-  typedef _CRT_ALIGN(16) struct _SETJMP_FLOAT128 {
-    __int64 LowPart;
-    __int64 HighPart;
-  } SETJMP_FLOAT128;
-
-#define _JBLEN 33
-  typedef SETJMP_FLOAT128 _JBTYPE;
-
-  typedef struct __JUMP_BUFFER {
-
-    unsigned long iAReserved[6];
-
-    unsigned long Registration;
-    unsigned long TryLevel;
-    unsigned long Cookie;
-    unsigned long UnwindFunc;
-
-    unsigned long UnwindData[6];
-
-    SETJMP_FLOAT128 FltS0;
-    SETJMP_FLOAT128 FltS1;
-    SETJMP_FLOAT128 FltS2;
-    SETJMP_FLOAT128 FltS3;
-    SETJMP_FLOAT128 FltS4;
-    SETJMP_FLOAT128 FltS5;
-    SETJMP_FLOAT128 FltS6;
-    SETJMP_FLOAT128 FltS7;
-    SETJMP_FLOAT128 FltS8;
-    SETJMP_FLOAT128 FltS9;
-    SETJMP_FLOAT128 FltS10;
-    SETJMP_FLOAT128 FltS11;
-    SETJMP_FLOAT128 FltS12;
-    SETJMP_FLOAT128 FltS13;
-    SETJMP_FLOAT128 FltS14;
-    SETJMP_FLOAT128 FltS15;
-    SETJMP_FLOAT128 FltS16;
-    SETJMP_FLOAT128 FltS17;
-    SETJMP_FLOAT128 FltS18;
-    SETJMP_FLOAT128 FltS19;
-    __int64 FPSR;
-    __int64 StIIP;
-    __int64 BrS0;
-    __int64 BrS1;
-    __int64 BrS2;
-    __int64 BrS3;
-    __int64 BrS4;
-    __int64 IntS0;
-    __int64 IntS1;
-    __int64 IntS2;
-    __int64 IntS3;
-    __int64 RsBSP;
-    __int64 RsPFS;
-    __int64 ApUNAT;
-    __int64 ApLC;
-    __int64 IntSp;
-    __int64 IntNats;
-    __int64 Preds;
-
-  } _JUMP_BUFFER;
-#elif defined(__x86_64)
-  typedef _CRT_ALIGN(16) struct _SETJMP_FLOAT128 {
-    unsigned __int64 Part[2];
-  } SETJMP_FLOAT128;
-
-#define _JBLEN 16
-  typedef SETJMP_FLOAT128 _JBTYPE;
-
-  typedef struct _JUMP_BUFFER {
-    unsigned __int64 Frame;
-    unsigned __int64 Rbx;
-    unsigned __int64 Rsp;
-    unsigned __int64 Rbp;
-    unsigned __int64 Rsi;
-    unsigned __int64 Rdi;
-    unsigned __int64 R12;
-    unsigned __int64 R13;
-    unsigned __int64 R14;
-    unsigned __int64 R15;
-    unsigned __int64 Rip;
-    unsigned __int64 Spare;
-    SETJMP_FLOAT128 Xmm6;
-    SETJMP_FLOAT128 Xmm7;
-    SETJMP_FLOAT128 Xmm8;
-    SETJMP_FLOAT128 Xmm9;
-    SETJMP_FLOAT128 Xmm10;
-    SETJMP_FLOAT128 Xmm11;
-    SETJMP_FLOAT128 Xmm12;
-    SETJMP_FLOAT128 Xmm13;
-    SETJMP_FLOAT128 Xmm14;
-    SETJMP_FLOAT128 Xmm15;
-  } _JUMP_BUFFER;
-#endif
-#ifndef _JMP_BUF_DEFINED
-  typedef _JBTYPE jmp_buf[_JBLEN];
-#define _JMP_BUF_DEFINED
-#endif
-
-  void * __cdecl __attribute__ ((__nothrow__)) mingw_getsp(void);
-
-#ifdef USE_MINGW_SETJMP_TWO_ARGS
-#ifndef _INC_SETJMPEX
-#define setjmp(BUF) _setjmp((BUF),mingw_getsp())
-  int __cdecl __attribute__ ((__nothrow__)) _setjmp(jmp_buf _Buf,void *_Ctx);
-#else
-#undef setjmp
-#define setjmp(BUF) _setjmpex((BUF),mingw_getsp())
-#define setjmpex(BUF) _setjmpex((BUF),mingw_getsp())
-  int __cdecl __attribute__ ((__nothrow__)) _setjmpex(jmp_buf _Buf,void *_Ctx);
-#endif
-#else
-#ifndef _INC_SETJMPEX
-#define setjmp _setjmp
-#endif
-  int __cdecl __attribute__ ((__nothrow__)) setjmp(jmp_buf _Buf);
-#endif
-
-  __declspec(noreturn) __attribute__ ((__nothrow__)) void __cdecl ms_longjmp(jmp_buf _Buf,int _Value)/* throw(...)*/;
-  __declspec(noreturn) __attribute__ ((__nothrow__)) void __cdecl longjmp(jmp_buf _Buf,int _Value);
-
-#ifdef __cplusplus
-}
-#endif
-
-#pragma pack(pop)
-#endif
diff --git a/tinyc/win32/include/share.h b/tinyc/win32/include/share.h
deleted file mode 100644
index 358855fe4..000000000
--- a/tinyc/win32/include/share.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_SHARE
-#define _INC_SHARE
-
-#ifndef _WIN32
-#error Only Win32 target is supported!
-#endif
-
-#define _SH_COMPAT 0x00
-#define _SH_DENYRW 0x10
-#define _SH_DENYWR 0x20
-#define _SH_DENYRD 0x30
-#define _SH_DENYNO 0x40
-#define _SH_SECURE 0x80
-
-#ifndef	NO_OLDNAMES
-#define SH_COMPAT _SH_COMPAT
-#define SH_DENYRW _SH_DENYRW
-#define SH_DENYWR _SH_DENYWR
-#define SH_DENYRD _SH_DENYRD
-#define SH_DENYNO _SH_DENYNO
-#endif
-
-#endif
diff --git a/tinyc/win32/include/signal.h b/tinyc/win32/include/signal.h
deleted file mode 100644
index a518f6b31..000000000
--- a/tinyc/win32/include/signal.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_SIGNAL
-#define _INC_SIGNAL
-
-#include <_mingw.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _SIG_ATOMIC_T_DEFINED
-#define _SIG_ATOMIC_T_DEFINED
-  typedef int sig_atomic_t;
-#endif
-
-#define NSIG 23
-
-#define	SIGHUP	1	/* hangup */
-#define SIGINT 2
-#define	SIGQUIT	3	/* quit */
-#define SIGILL 4
-#define	SIGTRAP	5	/* trace trap (not reset when caught) */
-#define	SIGIOT	6	/* IOT instruction */
-#define	SIGABRT 6	/* used by abort, replace SIGIOT in the future */
-#define	SIGEMT	7	/* EMT instruction */
-#define SIGFPE 8
-#define	SIGKILL	9	/* kill (cannot be caught or ignored) */
-#define	SIGBUS	10	/* bus error */
-#define SIGSEGV 11
-#define	SIGSYS	12	/* bad argument to system call */
-#define	SIGPIPE	13	/* write on a pipe with no one to read it */
-#ifdef __USE_MINGW_ALARM
-#define	SIGALRM	14	/* alarm clock */
-#endif
-#define SIGTERM 15
-#define SIGBREAK 21
-#define SIGABRT2 22
-
-#define SIGABRT_COMPAT 6
-
-  typedef	void (*__p_sig_fn_t)(int);
-
-#define SIG_DFL (__p_sig_fn_t)0
-#define SIG_IGN (__p_sig_fn_t)1
-#define SIG_GET (__p_sig_fn_t)2
-#define SIG_SGE (__p_sig_fn_t)3
-#define SIG_ACK (__p_sig_fn_t)4
-#define SIG_ERR (__p_sig_fn_t)-1
-
-  extern void **__cdecl __pxcptinfoptrs(void);
-#define _pxcptinfoptrs (*__pxcptinfoptrs())
-
-  __p_sig_fn_t __cdecl signal(int _SigNum,__p_sig_fn_t _Func);
-  int __cdecl raise(int _SigNum);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/tinyc/win32/include/stdint.h b/tinyc/win32/include/stdint.h
deleted file mode 100644
index cde32b6e3..000000000
--- a/tinyc/win32/include/stdint.h
+++ /dev/null
@@ -1,212 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-/* ISO C9x  7.18  Integer types <stdint.h>
- * Based on ISO/IEC SC22/WG14 9899 Committee draft (SC22 N2794)
- *
- *  THIS SOFTWARE IS NOT COPYRIGHTED
- *
- *  Contributor: Danny Smith <danny_r_smith_2001@yahoo.co.nz>
- *
- *  This source code is offered for use in the public domain. You may
- *  use, modify or distribute it freely.
- *
- *  This code is distributed in the hope that it will be useful but
- *  WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
- *  DISCLAIMED. This includes but is not limited to warranties of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
- *  Date: 2000-12-02
- */
-
-
-#ifndef _STDINT_H
-#define _STDINT_H
-
-#include <_mingw.h>
-
-#define __need_wint_t
-#define __need_wchar_t
-#include "stddef.h"
-
-#ifndef __int8_t_defined
-#define __int8_t_defined
-/* 7.18.1.1  Exact-width integer types */
-typedef signed char int8_t;
-typedef unsigned char   uint8_t;
-typedef short  int16_t;
-typedef unsigned short  uint16_t;
-typedef int  int32_t;
-typedef unsigned   uint32_t;
-typedef long long  int64_t;
-typedef unsigned long long   uint64_t;
-#endif
-
-/* 7.18.1.2  Minimum-width integer types */
-typedef signed char int_least8_t;
-typedef unsigned char   uint_least8_t;
-typedef short  int_least16_t;
-typedef unsigned short  uint_least16_t;
-typedef int  int_least32_t;
-typedef unsigned   uint_least32_t;
-typedef long long  int_least64_t;
-typedef unsigned long long   uint_least64_t;
-
-/*  7.18.1.3  Fastest minimum-width integer types
- *  Not actually guaranteed to be fastest for all purposes
- *  Here we use the exact-width types for 8 and 16-bit ints.
- */
-typedef char int_fast8_t;
-typedef unsigned char uint_fast8_t;
-typedef short  int_fast16_t;
-typedef unsigned short  uint_fast16_t;
-typedef int  int_fast32_t;
-typedef unsigned  int  uint_fast32_t;
-typedef long long  int_fast64_t;
-typedef unsigned long long   uint_fast64_t;
-
-/* 7.18.1.5  Greatest-width integer types */
-typedef long long  intmax_t;
-typedef unsigned long long   uintmax_t;
-
-/* 7.18.2  Limits of specified-width integer types */
-#if !defined ( __cplusplus) || defined (__STDC_LIMIT_MACROS)
-
-/* 7.18.2.1  Limits of exact-width integer types */
-#define INT8_MIN (-128)
-#define INT16_MIN (-32768)
-#define INT32_MIN (-2147483647 - 1)
-#define INT64_MIN  (-9223372036854775807LL - 1)
-
-#define INT8_MAX 127
-#define INT16_MAX 32767
-#define INT32_MAX 2147483647
-#define INT64_MAX 9223372036854775807LL
-
-#define UINT8_MAX 0xff /* 255U */
-#define UINT16_MAX 0xffff /* 65535U */
-#define UINT32_MAX 0xffffffff  /* 4294967295U */
-#define UINT64_MAX 0xffffffffffffffffULL /* 18446744073709551615ULL */
-
-/* 7.18.2.2  Limits of minimum-width integer types */
-#define INT_LEAST8_MIN INT8_MIN
-#define INT_LEAST16_MIN INT16_MIN
-#define INT_LEAST32_MIN INT32_MIN
-#define INT_LEAST64_MIN INT64_MIN
-
-#define INT_LEAST8_MAX INT8_MAX
-#define INT_LEAST16_MAX INT16_MAX
-#define INT_LEAST32_MAX INT32_MAX
-#define INT_LEAST64_MAX INT64_MAX
-
-#define UINT_LEAST8_MAX UINT8_MAX
-#define UINT_LEAST16_MAX UINT16_MAX
-#define UINT_LEAST32_MAX UINT32_MAX
-#define UINT_LEAST64_MAX UINT64_MAX
-
-/* 7.18.2.3  Limits of fastest minimum-width integer types */
-#define INT_FAST8_MIN INT8_MIN
-#define INT_FAST16_MIN INT16_MIN
-#define INT_FAST32_MIN INT32_MIN
-#define INT_FAST64_MIN INT64_MIN
-
-#define INT_FAST8_MAX INT8_MAX
-#define INT_FAST16_MAX INT16_MAX
-#define INT_FAST32_MAX INT32_MAX
-#define INT_FAST64_MAX INT64_MAX
-
-#define UINT_FAST8_MAX UINT8_MAX
-#define UINT_FAST16_MAX UINT16_MAX
-#define UINT_FAST32_MAX UINT32_MAX
-#define UINT_FAST64_MAX UINT64_MAX
-
-/* 7.18.2.4  Limits of integer types capable of holding
-    object pointers */
-#ifdef _WIN64
-#define INTPTR_MIN INT64_MIN
-#define INTPTR_MAX INT64_MAX
-#define UINTPTR_MAX UINT64_MAX
-#else
-#define INTPTR_MIN INT32_MIN
-#define INTPTR_MAX INT32_MAX
-#define UINTPTR_MAX UINT32_MAX
-#endif
-
-/* 7.18.2.5  Limits of greatest-width integer types */
-#define INTMAX_MIN INT64_MIN
-#define INTMAX_MAX INT64_MAX
-#define UINTMAX_MAX UINT64_MAX
-
-/* 7.18.3  Limits of other integer types */
-#ifdef _WIN64
-#define PTRDIFF_MIN INT64_MIN
-#define PTRDIFF_MAX INT64_MAX
-#else
-#define PTRDIFF_MIN INT32_MIN
-#define PTRDIFF_MAX INT32_MAX
-#endif
-
-#define SIG_ATOMIC_MIN INT32_MIN
-#define SIG_ATOMIC_MAX INT32_MAX
-
-#ifndef SIZE_MAX
-#ifdef _WIN64
-#define SIZE_MAX UINT64_MAX
-#else
-#define SIZE_MAX UINT32_MAX
-#endif
-#endif
-
-#ifndef WCHAR_MIN  /* also in wchar.h */
-#define WCHAR_MIN 0
-#define WCHAR_MAX ((wchar_t)-1) /* UINT16_MAX */
-#endif
-
-/*
- * wint_t is unsigned short for compatibility with MS runtime
- */
-#define WINT_MIN 0
-#define WINT_MAX ((wint_t)-1) /* UINT16_MAX */
-
-#endif /* !defined ( __cplusplus) || defined __STDC_LIMIT_MACROS */
-
-
-/* 7.18.4  Macros for integer constants */
-#if !defined ( __cplusplus) || defined (__STDC_CONSTANT_MACROS)
-
-/* 7.18.4.1  Macros for minimum-width integer constants
-
-    According to Douglas Gwyn <gwyn@arl.mil>:
-	"This spec was changed in ISO/IEC 9899:1999 TC1; in ISO/IEC
-	9899:1999 as initially published, the expansion was required
-	to be an integer constant of precisely matching type, which
-	is impossible to accomplish for the shorter types on most
-	platforms, because C99 provides no standard way to designate
-	an integer constant with width less than that of type int.
-	TC1 changed this to require just an integer constant
-	*expression* with *promoted* type."
-
-	The trick used here is from Clive D W Feather.
-*/
-
-#define INT8_C(val) (INT_LEAST8_MAX-INT_LEAST8_MAX+(val))
-#define INT16_C(val) (INT_LEAST16_MAX-INT_LEAST16_MAX+(val))
-#define INT32_C(val) (INT_LEAST32_MAX-INT_LEAST32_MAX+(val))
-/*  The 'trick' doesn't work in C89 for long long because, without
-    suffix, (val) will be evaluated as int, not intmax_t */
-#define INT64_C(val) val##LL
-
-#define UINT8_C(val) (UINT_LEAST8_MAX-UINT_LEAST8_MAX+(val))
-#define UINT16_C(val) (UINT_LEAST16_MAX-UINT_LEAST16_MAX+(val))
-#define UINT32_C(val) (UINT_LEAST32_MAX-UINT_LEAST32_MAX+(val))
-#define UINT64_C(val) val##ULL
-
-/* 7.18.4.2  Macros for greatest-width integer constants */
-#define INTMAX_C(val) val##LL
-#define UINTMAX_C(val) val##ULL
-
-#endif  /* !defined ( __cplusplus) || defined __STDC_CONSTANT_MACROS */
-
-#endif
diff --git a/tinyc/win32/include/stdio.h b/tinyc/win32/include/stdio.h
deleted file mode 100644
index da887936a..000000000
--- a/tinyc/win32/include/stdio.h
+++ /dev/null
@@ -1,429 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_STDIO
-#define _INC_STDIO
-
-#include <_mingw.h>
-
-#pragma pack(push,_CRT_PACKING)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define BUFSIZ 512
-#define _NFILE _NSTREAM_
-#define _NSTREAM_ 512
-#define _IOB_ENTRIES 20
-#define EOF (-1)
-
-#ifndef _FILE_DEFINED
-  struct _iobuf {
-    char *_ptr;
-    int _cnt;
-    char *_base;
-    int _flag;
-    int _file;
-    int _charbuf;
-    int _bufsiz;
-    char *_tmpfname;
-  };
-  typedef struct _iobuf FILE;
-#define _FILE_DEFINED
-#endif
-
-#ifdef _POSIX_
-#define _P_tmpdir "/"
-#define _wP_tmpdir L"/"
-#else
-#define _P_tmpdir "\\"
-#define _wP_tmpdir L"\\"
-#endif
-
-#define L_tmpnam (sizeof(_P_tmpdir) + 12)
-
-#ifdef _POSIX_
-#define L_ctermid 9
-#define L_cuserid 32
-#endif
-
-#define SEEK_CUR 1
-#define SEEK_END 2
-#define SEEK_SET 0
-
-#define STDIN_FILENO    0
-#define STDOUT_FILENO   1
-#define STDERR_FILENO   2
-
-#define FILENAME_MAX 260
-#define FOPEN_MAX 20
-#define _SYS_OPEN 20
-#define TMP_MAX 32767
-
-#ifndef NULL
-#ifdef __cplusplus
-#define NULL 0
-#else
-#define NULL ((void *)0)
-#endif
-#endif
-
-#ifndef _OFF_T_DEFINED
-#define _OFF_T_DEFINED
-#ifndef _OFF_T_
-#define _OFF_T_
-  typedef long _off_t;
-#if !defined(NO_OLDNAMES) || defined(_POSIX)
-  typedef long off_t;
-#endif
-#endif
-#endif
-
-#ifndef _OFF64_T_DEFINED
-#define _OFF64_T_DEFINED
-  typedef long long _off64_t;
-#if !defined(NO_OLDNAMES) || defined(_POSIX)
-  typedef long long off64_t;
-#endif
-#endif
-
-#ifndef _STDIO_DEFINED
-#ifdef _WIN64
-  _CRTIMP FILE *__cdecl __iob_func(void);
-#else
-#ifdef _MSVCRT_
-extern FILE _iob[];     /* A pointer to an array of FILE */
-#define __iob_func()    (_iob)
-#else
-extern FILE (*_imp___iob)[];    /* A pointer to an array of FILE */
-#define __iob_func()    (*_imp___iob)
-#define _iob __iob_func()
-#endif
-#endif
-#endif
-
-#ifndef _FPOS_T_DEFINED
-#define _FPOS_T_DEFINED
-#undef _FPOSOFF
-
-#if (!defined(NO_OLDNAMES) || defined(__GNUC__)) && _INTEGRAL_MAX_BITS >= 64
-  typedef __int64 fpos_t;
-#define _FPOSOFF(fp) ((long)(fp))
-#else
-  typedef long long fpos_t;
-#define _FPOSOFF(fp) ((long)(fp))
-#endif
-
-#endif
-
-#ifndef _STDSTREAM_DEFINED
-#define _STDSTREAM_DEFINED
-
-#define stdin (&__iob_func()[0])
-#define stdout (&__iob_func()[1])
-#define stderr (&__iob_func()[2])
-#endif
-
-#define _IOREAD 0x0001
-#define _IOWRT 0x0002
-
-#define _IOFBF 0x0000
-#define _IOLBF 0x0040
-#define _IONBF 0x0004
-
-#define _IOMYBUF 0x0008
-#define _IOEOF 0x0010
-#define _IOERR 0x0020
-#define _IOSTRG 0x0040
-#define _IORW 0x0080
-#ifdef _POSIX_
-#define _IOAPPEND 0x0200
-#endif
-
-#define _TWO_DIGIT_EXPONENT 0x1
-
-#ifndef _STDIO_DEFINED
-
-  _CRTIMP int __cdecl _filbuf(FILE *_File);
-  _CRTIMP int __cdecl _flsbuf(int _Ch,FILE *_File);
-#ifdef _POSIX_
-  _CRTIMP FILE *__cdecl _fsopen(const char *_Filename,const char *_Mode);
-#else
-  _CRTIMP FILE *__cdecl _fsopen(const char *_Filename,const char *_Mode,int _ShFlag);
-#endif
-  void __cdecl clearerr(FILE *_File);
-  int __cdecl fclose(FILE *_File);
-  _CRTIMP int __cdecl _fcloseall(void);
-#ifdef _POSIX_
-  FILE *__cdecl fdopen(int _FileHandle,const char *_Mode);
-#else
-  _CRTIMP FILE *__cdecl _fdopen(int _FileHandle,const char *_Mode);
-#endif
-  int __cdecl feof(FILE *_File);
-  int __cdecl ferror(FILE *_File);
-  int __cdecl fflush(FILE *_File);
-  int __cdecl fgetc(FILE *_File);
-  _CRTIMP int __cdecl _fgetchar(void);
-  int __cdecl fgetpos(FILE *_File ,fpos_t *_Pos);
-  char *__cdecl fgets(char *_Buf,int _MaxCount,FILE *_File);
-#ifdef _POSIX_
-  int __cdecl fileno(FILE *_File);
-#else
-  _CRTIMP int __cdecl _fileno(FILE *_File);
-#endif
-  _CRTIMP char *__cdecl _tempnam(const char *_DirName,const char *_FilePrefix);
-  _CRTIMP int __cdecl _flushall(void);
-  FILE *__cdecl fopen(const char *_Filename,const char *_Mode);
-  FILE *fopen64(const char *filename,const char *mode);
-  int __cdecl fprintf(FILE *_File,const char *_Format,...);
-  int __cdecl fputc(int _Ch,FILE *_File);
-  _CRTIMP int __cdecl _fputchar(int _Ch);
-  int __cdecl fputs(const char *_Str,FILE *_File);
-  size_t __cdecl fread(void *_DstBuf,size_t _ElementSize,size_t _Count,FILE *_File);
-  FILE *__cdecl freopen(const char *_Filename,const char *_Mode,FILE *_File);
-  int __cdecl fscanf(FILE *_File,const char *_Format,...);
-  int __cdecl fsetpos(FILE *_File,const fpos_t *_Pos);
-  int __cdecl fseek(FILE *_File,long _Offset,int _Origin);
-   int fseeko64(FILE* stream, _off64_t offset, int whence);
-  long __cdecl ftell(FILE *_File);
-  _off64_t ftello64(FILE * stream);
-  int __cdecl _fseeki64(FILE *_File,__int64 _Offset,int _Origin);
-  __int64 __cdecl _ftelli64(FILE *_File);
-  size_t __cdecl fwrite(const void *_Str,size_t _Size,size_t _Count,FILE *_File);
-  int __cdecl getc(FILE *_File);
-  int __cdecl getchar(void);
-  _CRTIMP int __cdecl _getmaxstdio(void);
-  char *__cdecl gets(char *_Buffer);
-  int __cdecl _getw(FILE *_File);
-#ifndef _CRT_PERROR_DEFINED
-#define _CRT_PERROR_DEFINED
-  void __cdecl perror(const char *_ErrMsg);
-#endif
-  _CRTIMP int __cdecl _pclose(FILE *_File);
-  _CRTIMP FILE *__cdecl _popen(const char *_Command,const char *_Mode);
-#if !defined(NO_OLDNAMES) && !defined(popen)
-#define popen   _popen
-#define pclose  _pclose
-#endif
-  int __cdecl printf(const char *_Format,...);
-  int __cdecl putc(int _Ch,FILE *_File);
-  int __cdecl putchar(int _Ch);
-  int __cdecl puts(const char *_Str);
-  _CRTIMP int __cdecl _putw(int _Word,FILE *_File);
-#ifndef _CRT_DIRECTORY_DEFINED
-#define _CRT_DIRECTORY_DEFINED
-  int __cdecl remove(const char *_Filename);
-  int __cdecl rename(const char *_OldFilename,const char *_NewFilename);
-  _CRTIMP int __cdecl _unlink(const char *_Filename);
-#ifndef NO_OLDNAMES
-  int __cdecl unlink(const char *_Filename);
-#endif
-#endif
-  void __cdecl rewind(FILE *_File);
-  _CRTIMP int __cdecl _rmtmp(void);
-  int __cdecl scanf(const char *_Format,...);
-  void __cdecl setbuf(FILE *_File,char *_Buffer);
-  _CRTIMP int __cdecl _setmaxstdio(int _Max);
-  _CRTIMP unsigned int __cdecl _set_output_format(unsigned int _Format);
-  _CRTIMP unsigned int __cdecl _get_output_format(void);
-  int __cdecl setvbuf(FILE *_File,char *_Buf,int _Mode,size_t _Size);
-  _CRTIMP int __cdecl _scprintf(const char *_Format,...);
-  int __cdecl sscanf(const char *_Src,const char *_Format,...);
-  _CRTIMP int __cdecl _snscanf(const char *_Src,size_t _MaxCount,const char *_Format,...);
-  FILE *__cdecl tmpfile(void);
-  char *__cdecl tmpnam(char *_Buffer);
-  int __cdecl ungetc(int _Ch,FILE *_File);
-  int __cdecl vfprintf(FILE *_File,const char *_Format,va_list _ArgList);
-  int __cdecl vprintf(const char *_Format,va_list _ArgList);
-  /* Make sure macros are not defined.  */
-#pragma push_macro("vsnprintf")
-#pragma push_macro("snprintf")
-# undef vsnprintf
-# undef snprintf
-  extern
-  __attribute__((format(gnu_printf, 3, 0))) __attribute__((nonnull (3)))
-  int __mingw_vsnprintf(char *_DstBuf,size_t _MaxCount,const char *_Format,va_list _ArgList);
-  extern
-  __attribute__((format(gnu_printf, 3, 4))) __attribute__((nonnull (3)))
-  int __mingw_snprintf(char* s, size_t n, const char*  format, ...);
-  int __cdecl vsnprintf(char *_DstBuf,size_t _MaxCount,const char *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _snprintf(char *_Dest,size_t _Count,const char *_Format,...);
-  _CRTIMP int __cdecl _vsnprintf(char *_Dest,size_t _Count,const char *_Format,va_list _Args);
-  int __cdecl sprintf(char *_Dest,const char *_Format,...);
-  int __cdecl vsprintf(char *_Dest,const char *_Format,va_list _Args);
-#ifndef __NO_ISOCEXT  /* externs in libmingwex.a */
-  int __cdecl snprintf(char* s, size_t n, const char*  format, ...);
-  __CRT_INLINE int __cdecl vsnprintf (char* s, size_t n, const char* format,va_list arg) {
-    return _vsnprintf ( s, n, format, arg);
-  }
-  int __cdecl vscanf(const char * Format, va_list argp);
-  int __cdecl vfscanf (FILE * fp, const char * Format,va_list argp);
-  int __cdecl vsscanf (const char * _Str,const char * Format,va_list argp);
-#endif
-/* Restore may prior defined macros snprintf/vsnprintf.  */
-#pragma pop_macro("snprintf")
-#pragma pop_macro("vsnprintf")
-/* Check if vsnprintf and snprintf are defaulting to gnu-style.  */
-# if defined(USE_MINGW_GNU_SNPRINTF) && USE_MINGW_GNU_SNPRINTF
-# ifndef vsnprint
-# define vsnprintf __mingw_vsnprintf
-# endif
-# ifndef snprintf
-# define snprintf __mingw_snprintf
-# endif
-# endif
-  _CRTIMP int __cdecl _vscprintf(const char *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _set_printf_count_output(int _Value);
-  _CRTIMP int __cdecl _get_printf_count_output(void);
-
-#ifndef _WSTDIO_DEFINED
-
-#ifndef WEOF
-#define WEOF (wint_t)(0xFFFF)
-#endif
-
-#ifdef _POSIX_
-  _CRTIMP FILE *__cdecl _wfsopen(const wchar_t *_Filename,const wchar_t *_Mode);
-#else
-  _CRTIMP FILE *__cdecl _wfsopen(const wchar_t *_Filename,const wchar_t *_Mode,int _ShFlag);
-#endif
-  wint_t __cdecl fgetwc(FILE *_File);
-  _CRTIMP wint_t __cdecl _fgetwchar(void);
-  wint_t __cdecl fputwc(wchar_t _Ch,FILE *_File);
-  _CRTIMP wint_t __cdecl _fputwchar(wchar_t _Ch);
-  wint_t __cdecl getwc(FILE *_File);
-  wint_t __cdecl getwchar(void);
-  wint_t __cdecl putwc(wchar_t _Ch,FILE *_File);
-  wint_t __cdecl putwchar(wchar_t _Ch);
-  wint_t __cdecl ungetwc(wint_t _Ch,FILE *_File);
-  wchar_t *__cdecl fgetws(wchar_t *_Dst,int _SizeInWords,FILE *_File);
-  int __cdecl fputws(const wchar_t *_Str,FILE *_File);
-  _CRTIMP wchar_t *__cdecl _getws(wchar_t *_String);
-  _CRTIMP int __cdecl _putws(const wchar_t *_Str);
-  int __cdecl fwprintf(FILE *_File,const wchar_t *_Format,...);
-  int __cdecl wprintf(const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _scwprintf(const wchar_t *_Format,...);
-  int __cdecl vfwprintf(FILE *_File,const wchar_t *_Format,va_list _ArgList);
-  int __cdecl vwprintf(const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl swprintf(wchar_t*, const wchar_t*, ...);
-  _CRTIMP int __cdecl vswprintf(wchar_t*, const wchar_t*,va_list);
-  _CRTIMP int __cdecl _swprintf_c(wchar_t *_DstBuf,size_t _SizeInWords,const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _vswprintf_c(wchar_t *_DstBuf,size_t _SizeInWords,const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _snwprintf(wchar_t *_Dest,size_t _Count,const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _vsnwprintf(wchar_t *_Dest,size_t _Count,const wchar_t *_Format,va_list _Args);
-#ifndef __NO_ISOCEXT  /* externs in libmingwex.a */
-  int __cdecl snwprintf (wchar_t* s, size_t n, const wchar_t*  format, ...);
-  __CRT_INLINE int __cdecl vsnwprintf (wchar_t* s, size_t n, const wchar_t* format, va_list arg) { return _vsnwprintf(s,n,format,arg); }
-  int __cdecl vwscanf (const wchar_t *, va_list);
-  int __cdecl vfwscanf (FILE *,const wchar_t *,va_list);
-  int __cdecl vswscanf (const wchar_t *,const wchar_t *,va_list);
-#endif
-  _CRTIMP int __cdecl _swprintf(wchar_t *_Dest,const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _vswprintf(wchar_t *_Dest,const wchar_t *_Format,va_list _Args);
-
-#ifndef RC_INVOKED
-#include <vadefs.h>
-#endif
-
-#ifdef _CRT_NON_CONFORMING_SWPRINTFS
-#ifndef __cplusplus
-#define swprintf _swprintf
-#define vswprintf _vswprintf
-#define _swprintf_l __swprintf_l
-#define _vswprintf_l __vswprintf_l
-#endif
-#endif
-
-  _CRTIMP wchar_t *__cdecl _wtempnam(const wchar_t *_Directory,const wchar_t *_FilePrefix);
-  _CRTIMP int __cdecl _vscwprintf(const wchar_t *_Format,va_list _ArgList);
-  int __cdecl fwscanf(FILE *_File,const wchar_t *_Format,...);
-  int __cdecl swscanf(const wchar_t *_Src,const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _snwscanf(const wchar_t *_Src,size_t _MaxCount,const wchar_t *_Format,...);
-  int __cdecl wscanf(const wchar_t *_Format,...);
-  _CRTIMP FILE *__cdecl _wfdopen(int _FileHandle ,const wchar_t *_Mode);
-  _CRTIMP FILE *__cdecl _wfopen(const wchar_t *_Filename,const wchar_t *_Mode);
-  _CRTIMP FILE *__cdecl _wfreopen(const wchar_t *_Filename,const wchar_t *_Mode,FILE *_OldFile);
-#ifndef _CRT_WPERROR_DEFINED
-#define _CRT_WPERROR_DEFINED
-  _CRTIMP void __cdecl _wperror(const wchar_t *_ErrMsg);
-#endif
-  _CRTIMP FILE *__cdecl _wpopen(const wchar_t *_Command,const wchar_t *_Mode);
-#if !defined(NO_OLDNAMES) && !defined(wpopen)
-#define wpopen  _wpopen
-#endif
-  _CRTIMP int __cdecl _wremove(const wchar_t *_Filename);
-  _CRTIMP wchar_t *__cdecl _wtmpnam(wchar_t *_Buffer);
-  _CRTIMP wint_t __cdecl _fgetwc_nolock(FILE *_File);
-  _CRTIMP wint_t __cdecl _fputwc_nolock(wchar_t _Ch,FILE *_File);
-  _CRTIMP wint_t __cdecl _ungetwc_nolock(wint_t _Ch,FILE *_File);
-
-#undef _CRT_GETPUTWCHAR_NOINLINE
-
-#if !defined(__cplusplus) || defined(_CRT_GETPUTWCHAR_NOINLINE)
-#define getwchar() fgetwc(stdin)
-#define putwchar(_c) fputwc((_c),stdout)
-#else
-  __CRT_INLINE wint_t __cdecl getwchar() { return (fgetwc(stdin)); }
-  __CRT_INLINE wint_t __cdecl putwchar(wchar_t _C) { return (fputwc(_C,stdout)); }
-#endif
-
-#define getwc(_stm) fgetwc(_stm)
-#define putwc(_c,_stm) fputwc(_c,_stm)
-#define _putwc_nolock(_c,_stm) _fputwc_nolock(_c,_stm)
-#define _getwc_nolock(_stm) _fgetwc_nolock(_stm)
-
-#define _WSTDIO_DEFINED
-#endif
-
-#define _STDIO_DEFINED
-#endif
-
-#define _fgetc_nolock(_stream) (--(_stream)->_cnt >= 0 ? 0xff & *(_stream)->_ptr++ : _filbuf(_stream))
-#define _fputc_nolock(_c,_stream) (--(_stream)->_cnt >= 0 ? 0xff & (*(_stream)->_ptr++ = (char)(_c)) : _flsbuf((_c),(_stream)))
-#define _getc_nolock(_stream) _fgetc_nolock(_stream)
-#define _putc_nolock(_c,_stream) _fputc_nolock(_c,_stream)
-#define _getchar_nolock() _getc_nolock(stdin)
-#define _putchar_nolock(_c) _putc_nolock((_c),stdout)
-#define _getwchar_nolock() _getwc_nolock(stdin)
-#define _putwchar_nolock(_c) _putwc_nolock((_c),stdout)
-
-  _CRTIMP void __cdecl _lock_file(FILE *_File);
-  _CRTIMP void __cdecl _unlock_file(FILE *_File);
-  _CRTIMP int __cdecl _fclose_nolock(FILE *_File);
-  _CRTIMP int __cdecl _fflush_nolock(FILE *_File);
-  _CRTIMP size_t __cdecl _fread_nolock(void *_DstBuf,size_t _ElementSize,size_t _Count,FILE *_File);
-  _CRTIMP int __cdecl _fseek_nolock(FILE *_File,long _Offset,int _Origin);
-  _CRTIMP long __cdecl _ftell_nolock(FILE *_File);
-  _CRTIMP int __cdecl _fseeki64_nolock(FILE *_File,__int64 _Offset,int _Origin);
-  _CRTIMP __int64 __cdecl _ftelli64_nolock(FILE *_File);
-  _CRTIMP size_t __cdecl _fwrite_nolock(const void *_DstBuf,size_t _Size,size_t _Count,FILE *_File);
-  _CRTIMP int __cdecl _ungetc_nolock(int _Ch,FILE *_File);
-
-#if !defined(NO_OLDNAMES) || !defined(_POSIX)
-#define P_tmpdir _P_tmpdir
-#define SYS_OPEN _SYS_OPEN
-
-  char *__cdecl tempnam(const char *_Directory,const char *_FilePrefix);
-  int __cdecl fcloseall(void);
-  FILE *__cdecl fdopen(int _FileHandle,const char *_Format);
-  int __cdecl fgetchar(void);
-  int __cdecl fileno(FILE *_File);
-  int __cdecl flushall(void);
-  int __cdecl fputchar(int _Ch);
-  int __cdecl getw(FILE *_File);
-  int __cdecl putw(int _Ch,FILE *_File);
-  int __cdecl rmtmp(void);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#pragma pack(pop)
-
-#include <sec_api/stdio_s.h>
-
-#endif
diff --git a/tinyc/win32/include/stdlib.h b/tinyc/win32/include/stdlib.h
deleted file mode 100644
index 96765b29e..000000000
--- a/tinyc/win32/include/stdlib.h
+++ /dev/null
@@ -1,580 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_STDLIB
-#define _INC_STDLIB
-
-#include <_mingw.h>
-#include <limits.h>
-
-#pragma pack(push,_CRT_PACKING)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef NULL
-#ifdef __cplusplus
-#define NULL 0
-#else
-#define NULL ((void *)0)
-#endif
-#endif
-
-#define EXIT_SUCCESS 0
-#define EXIT_FAILURE 1
-
-#ifndef _ONEXIT_T_DEFINED
-#define _ONEXIT_T_DEFINED
-
-  typedef int (__cdecl *_onexit_t)(void);
-
-#ifndef NO_OLDNAMES
-#define onexit_t _onexit_t
-#endif
-#endif
-
-#ifndef _DIV_T_DEFINED
-#define _DIV_T_DEFINED
-
-  typedef struct _div_t {
-    int quot;
-    int rem;
-  } div_t;
-
-  typedef struct _ldiv_t {
-    long quot;
-    long rem;
-  } ldiv_t;
-#endif
-
-#ifndef _CRT_DOUBLE_DEC
-#define _CRT_DOUBLE_DEC
-
-#pragma pack(4)
-  typedef struct {
-    unsigned char ld[10];
-  } _LDOUBLE;
-#pragma pack()
-
-#define _PTR_LD(x) ((unsigned char *)(&(x)->ld))
-
-  typedef struct {
-    double x;
-  } _CRT_DOUBLE;
-
-  typedef struct {
-    float f;
-  } _CRT_FLOAT;
-
-#pragma push_macro("long")
-#undef long
-
-  typedef struct {
-    long double x;
-  } _LONGDOUBLE;
-
-#pragma pop_macro("long")
-
-#pragma pack(4)
-  typedef struct {
-    unsigned char ld12[12];
-  } _LDBL12;
-#pragma pack()
-#endif
-
-#define RAND_MAX 0x7fff
-
-#ifndef MB_CUR_MAX
-#define MB_CUR_MAX ___mb_cur_max_func()
-#ifndef __mb_cur_max
-#ifdef _MSVCRT_
-  extern int __mb_cur_max;
-#else
-#define __mb_cur_max    (*_imp____mb_cur_max)
-  extern int *_imp____mb_cur_max;
-#endif
-#endif
-#ifdef _MSVCRT_
-  extern int __mbcur_max;
-#define ___mb_cur_max_func() (__mb_cur_max)
-#else
-  extern int* _imp____mbcur_max;
-#define ___mb_cur_max_func() (*_imp____mb_cur_max)
-#endif
-#endif
-
-#define __max(a,b) (((a) > (b)) ? (a) : (b))
-#define __min(a,b) (((a) < (b)) ? (a) : (b))
-
-#define _MAX_PATH 260
-#define _MAX_DRIVE 3
-#define _MAX_DIR 256
-#define _MAX_FNAME 256
-#define _MAX_EXT 256
-
-#define _OUT_TO_DEFAULT 0
-#define _OUT_TO_STDERR 1
-#define _OUT_TO_MSGBOX 2
-#define _REPORT_ERRMODE 3
-
-#define _WRITE_ABORT_MSG 0x1
-#define _CALL_REPORTFAULT 0x2
-
-#define _MAX_ENV 32767
-
-  typedef void (__cdecl *_purecall_handler)(void);
-
-  _CRTIMP _purecall_handler __cdecl _set_purecall_handler(_purecall_handler _Handler);
-  _CRTIMP _purecall_handler __cdecl _get_purecall_handler(void);
-
-  typedef void (__cdecl *_invalid_parameter_handler)(const wchar_t *,const wchar_t *,const wchar_t *,unsigned int,uintptr_t);
-  _invalid_parameter_handler __cdecl _set_invalid_parameter_handler(_invalid_parameter_handler _Handler);
-  _invalid_parameter_handler __cdecl _get_invalid_parameter_handler(void);
-
-#ifndef _CRT_ERRNO_DEFINED
-#define _CRT_ERRNO_DEFINED
-  _CRTIMP extern int *__cdecl _errno(void);
-#define errno (*_errno())
-  errno_t __cdecl _set_errno(int _Value);
-  errno_t __cdecl _get_errno(int *_Value);
-#endif
-  _CRTIMP unsigned long *__cdecl __doserrno(void);
-#define _doserrno (*__doserrno())
-  errno_t __cdecl _set_doserrno(unsigned long _Value);
-  errno_t __cdecl _get_doserrno(unsigned long *_Value);
-#ifdef _MSVCRT_
-  extern char *_sys_errlist[];
-  extern int _sys_nerr;
-#else
-  _CRTIMP char *_sys_errlist[1];
-  _CRTIMP int _sys_nerr;
-#endif
-#if (defined(_X86_) && !defined(__x86_64))
-  _CRTIMP int *__cdecl __p___argc(void);
-  _CRTIMP char ***__cdecl __p___argv(void);
-  _CRTIMP wchar_t ***__cdecl __p___wargv(void);
-  _CRTIMP char ***__cdecl __p__environ(void);
-  _CRTIMP wchar_t ***__cdecl __p__wenviron(void);
-  _CRTIMP char **__cdecl __p__pgmptr(void);
-  _CRTIMP wchar_t **__cdecl __p__wpgmptr(void);
-#endif
-#ifndef __argc
-#ifdef _MSVCRT_
-  extern int __argc;
-#else
-#define __argc (*_imp____argc)
-  extern int *_imp____argc;
-#endif
-#endif
-#ifndef __argv
-#ifdef _MSVCRT_
-  extern char **__argv;
-#else
-#define __argv  (*_imp____argv)
-  extern char ***_imp____argv;
-#endif
-#endif
-#ifndef __wargv
-#ifdef _MSVCRT_
-  extern wchar_t **__wargv;
-#else
-#define __wargv (*_imp____wargv)
-  extern wchar_t ***_imp____wargv;
-#endif
-#endif
-
-#ifdef _POSIX_
-  extern char **environ;
-#else
-#ifndef _environ
-#ifdef _MSVCRT_
-  extern char **_environ;
-#else
-#define _environ (*_imp___environ)
-  extern char ***_imp___environ;
-#endif
-#endif
-
-#ifndef _wenviron
-#ifdef _MSVCRT_
-  extern wchar_t **_wenviron;
-#else
-#define _wenviron       (*_imp___wenviron)
-  extern wchar_t ***_imp___wenviron;
-#endif
-#endif
-#endif
-#ifndef _pgmptr
-#ifdef _MSVCRT_
-  extern char *_pgmptr;
-#else
-#define _pgmptr (*_imp___pgmptr)
-  extern char **_imp___pgmptr;
-#endif
-#endif
-
-#ifndef _wpgmptr
-#ifdef _MSVCRT_
-  extern wchar_t *_wpgmptr;
-#else
-#define _wpgmptr        (*_imp___wpgmptr)
-  extern wchar_t **_imp___wpgmptr;
-#endif
-#endif
-  errno_t __cdecl _get_pgmptr(char **_Value);
-  errno_t __cdecl _get_wpgmptr(wchar_t **_Value);
-#ifndef _fmode
-#ifdef _MSVCRT_
-  extern int _fmode;
-#else
-#define _fmode  (*_imp___fmode)
-  extern int *_imp___fmode;
-#endif
-#endif
-  _CRTIMP errno_t __cdecl _set_fmode(int _Mode);
-  _CRTIMP errno_t __cdecl _get_fmode(int *_PMode);
-
-#ifndef _osplatform
-#ifdef _MSVCRT_
-  extern unsigned int _osplatform;
-#else
-#define _osplatform (*_imp___osplatform)
-  extern unsigned int *_imp___osplatform;
-#endif
-#endif
-
-#ifndef _osver
-#ifdef _MSVCRT_
-  extern unsigned int _osver;
-#else
-#define _osver  (*_imp___osver)
-  extern unsigned int *_imp___osver;
-#endif
-#endif
-
-#ifndef _winver
-#ifdef _MSVCRT_
-  extern unsigned int _winver;
-#else
-#define _winver (*_imp___winver)
-  extern unsigned int *_imp___winver;
-#endif
-#endif
-
-#ifndef _winmajor
-#ifdef _MSVCRT_
-  extern unsigned int _winmajor;
-#else
-#define _winmajor       (*_imp___winmajor)
-  extern unsigned int *_imp___winmajor;
-#endif
-#endif
-
-#ifndef _winminor
-#ifdef _MSVCRT_
-  extern unsigned int _winminor;
-#else
-#define _winminor       (*_imp___winminor)
-  extern unsigned int *_imp___winminor;
-#endif
-#endif
-
-  errno_t __cdecl _get_osplatform(unsigned int *_Value);
-  errno_t __cdecl _get_osver(unsigned int *_Value);
-  errno_t __cdecl _get_winver(unsigned int *_Value);
-  errno_t __cdecl _get_winmajor(unsigned int *_Value);
-  errno_t __cdecl _get_winminor(unsigned int *_Value);
-#ifndef _countof
-#ifndef __cplusplus
-#define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))
-#else
-  extern "C++" {
-    template <typename _CountofType,size_t _SizeOfArray> char (*__countof_helper(UNALIGNED _CountofType (&_Array)[_SizeOfArray]))[_SizeOfArray];
-#define _countof(_Array) sizeof(*__countof_helper(_Array))
-  }
-#endif
-#endif
-
-#ifndef _CRT_TERMINATE_DEFINED
-#define _CRT_TERMINATE_DEFINED
-  void __cdecl __MINGW_NOTHROW exit(int _Code) __MINGW_ATTRIB_NORETURN;
-  _CRTIMP void __cdecl __MINGW_NOTHROW _exit(int _Code) __MINGW_ATTRIB_NORETURN;
-#if !defined __NO_ISOCEXT /* extern stub in static libmingwex.a */
-  /* C99 function name */
-  void __cdecl _Exit(int) __MINGW_ATTRIB_NORETURN;
-  __CRT_INLINE __MINGW_ATTRIB_NORETURN void  __cdecl _Exit(int status)
-  {  _exit(status); }
-#endif
-
-#pragma push_macro("abort")
-#undef abort
-  void __cdecl __declspec(noreturn) abort(void);
-#pragma pop_macro("abort")
-
-#endif
-
-  _CRTIMP unsigned int __cdecl _set_abort_behavior(unsigned int _Flags,unsigned int _Mask);
-
-#ifndef _CRT_ABS_DEFINED
-#define _CRT_ABS_DEFINED
-  int __cdecl abs(int _X);
-  long __cdecl labs(long _X);
-#endif
-
-#if _INTEGRAL_MAX_BITS >= 64
-  __int64 __cdecl _abs64(__int64);
-#endif
-  int __cdecl atexit(void (__cdecl *)(void));
-#ifndef _CRT_ATOF_DEFINED
-#define _CRT_ATOF_DEFINED
-  double __cdecl atof(const char *_String);
-  double __cdecl _atof_l(const char *_String,_locale_t _Locale);
-#endif
-  int __cdecl atoi(const char *_Str);
-  _CRTIMP int __cdecl _atoi_l(const char *_Str,_locale_t _Locale);
-  long __cdecl atol(const char *_Str);
-  _CRTIMP long __cdecl _atol_l(const char *_Str,_locale_t _Locale);
-#ifndef _CRT_ALGO_DEFINED
-#define _CRT_ALGO_DEFINED
-  void *__cdecl bsearch(const void *_Key,const void *_Base,size_t _NumOfElements,size_t _SizeOfElements,int (__cdecl *_PtFuncCompare)(const void *,const void *));
-  void __cdecl qsort(void *_Base,size_t _NumOfElements,size_t _SizeOfElements,int (__cdecl *_PtFuncCompare)(const void *,const void *));
-#endif
-  unsigned short __cdecl _byteswap_ushort(unsigned short _Short);
-  /*unsigned long __cdecl _byteswap_ulong (unsigned long _Long); */
-#if _INTEGRAL_MAX_BITS >= 64
-  unsigned __int64 __cdecl _byteswap_uint64(unsigned __int64 _Int64);
-#endif
-  div_t __cdecl div(int _Numerator,int _Denominator);
-  char *__cdecl getenv(const char *_VarName);
-  _CRTIMP char *__cdecl _itoa(int _Value,char *_Dest,int _Radix);
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP char *__cdecl _i64toa(__int64 _Val,char *_DstBuf,int _Radix);
-  _CRTIMP char *__cdecl _ui64toa(unsigned __int64 _Val,char *_DstBuf,int _Radix);
-  _CRTIMP __int64 __cdecl _atoi64(const char *_String);
-  _CRTIMP __int64 __cdecl _atoi64_l(const char *_String,_locale_t _Locale);
-  _CRTIMP __int64 __cdecl _strtoi64(const char *_String,char **_EndPtr,int _Radix);
-  _CRTIMP __int64 __cdecl _strtoi64_l(const char *_String,char **_EndPtr,int _Radix,_locale_t _Locale);
-  _CRTIMP unsigned __int64 __cdecl _strtoui64(const char *_String,char **_EndPtr,int _Radix);
-  _CRTIMP unsigned __int64 __cdecl _strtoui64_l(const char *_String,char **_EndPtr,int _Radix,_locale_t _Locale);
-#endif
-  ldiv_t __cdecl ldiv(long _Numerator,long _Denominator);
-  _CRTIMP char *__cdecl _ltoa(long _Value,char *_Dest,int _Radix);
-  int __cdecl mblen(const char *_Ch,size_t _MaxCount);
-  _CRTIMP int __cdecl _mblen_l(const char *_Ch,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP size_t __cdecl _mbstrlen(const char *_Str);
-  _CRTIMP size_t __cdecl _mbstrlen_l(const char *_Str,_locale_t _Locale);
-  _CRTIMP size_t __cdecl _mbstrnlen(const char *_Str,size_t _MaxCount);
-  _CRTIMP size_t __cdecl _mbstrnlen_l(const char *_Str,size_t _MaxCount,_locale_t _Locale);
-  int __cdecl mbtowc(wchar_t *_DstCh,const char *_SrcCh,size_t _SrcSizeInBytes);
-  _CRTIMP int __cdecl _mbtowc_l(wchar_t *_DstCh,const char *_SrcCh,size_t _SrcSizeInBytes,_locale_t _Locale);
-  size_t __cdecl mbstowcs(wchar_t *_Dest,const char *_Source,size_t _MaxCount);
-  _CRTIMP size_t __cdecl _mbstowcs_l(wchar_t *_Dest,const char *_Source,size_t _MaxCount,_locale_t _Locale);
-  int __cdecl rand(void);
-  _CRTIMP int __cdecl _set_error_mode(int _Mode);
-  void __cdecl srand(unsigned int _Seed);
-  double __cdecl strtod(const char *_Str,char **_EndPtr);
-  float __cdecl strtof(const char *nptr, char **endptr);
-#if !defined __NO_ISOCEXT  /* in libmingwex.a */
-  float __cdecl strtof (const char * __restrict__, char ** __restrict__);
-  long double __cdecl strtold(const char * __restrict__, char ** __restrict__);
-#endif /* __NO_ISOCEXT */
-  _CRTIMP double __cdecl _strtod_l(const char *_Str,char **_EndPtr,_locale_t _Locale);
-  long __cdecl strtol(const char *_Str,char **_EndPtr,int _Radix);
-  _CRTIMP long __cdecl _strtol_l(const char *_Str,char **_EndPtr,int _Radix,_locale_t _Locale);
-  unsigned long __cdecl strtoul(const char *_Str,char **_EndPtr,int _Radix);
-  _CRTIMP unsigned long __cdecl _strtoul_l(const char *_Str,char **_EndPtr,int _Radix,_locale_t _Locale);
-#ifndef _CRT_SYSTEM_DEFINED
-#define _CRT_SYSTEM_DEFINED
-  int __cdecl system(const char *_Command);
-#endif
-  _CRTIMP char *__cdecl _ultoa(unsigned long _Value,char *_Dest,int _Radix);
-  int __cdecl wctomb(char *_MbCh,wchar_t _WCh);
-  _CRTIMP int __cdecl _wctomb_l(char *_MbCh,wchar_t _WCh,_locale_t _Locale);
-  size_t __cdecl wcstombs(char *_Dest,const wchar_t *_Source,size_t _MaxCount);
-  _CRTIMP size_t __cdecl _wcstombs_l(char *_Dest,const wchar_t *_Source,size_t _MaxCount,_locale_t _Locale);
-
-#ifndef _CRT_ALLOCATION_DEFINED
-#define _CRT_ALLOCATION_DEFINED
-  void *__cdecl calloc(size_t _NumOfElements,size_t _SizeOfElements);
-  void __cdecl free(void *_Memory);
-  void *__cdecl malloc(size_t _Size);
-  void *__cdecl realloc(void *_Memory,size_t _NewSize);
-  _CRTIMP void *__cdecl _recalloc(void *_Memory,size_t _Count,size_t _Size);
-  //_CRTIMP void __cdecl _aligned_free(void *_Memory);
-  //_CRTIMP void *__cdecl _aligned_malloc(size_t _Size,size_t _Alignment);
-  _CRTIMP void *__cdecl _aligned_offset_malloc(size_t _Size,size_t _Alignment,size_t _Offset);
-  _CRTIMP void *__cdecl _aligned_realloc(void *_Memory,size_t _Size,size_t _Alignment);
-  _CRTIMP void *__cdecl _aligned_recalloc(void *_Memory,size_t _Count,size_t _Size,size_t _Alignment);
-  _CRTIMP void *__cdecl _aligned_offset_realloc(void *_Memory,size_t _Size,size_t _Alignment,size_t _Offset);
-  _CRTIMP void *__cdecl _aligned_offset_recalloc(void *_Memory,size_t _Count,size_t _Size,size_t _Alignment,size_t _Offset);
-#endif
-
-#ifndef _WSTDLIB_DEFINED
-#define _WSTDLIB_DEFINED
-
-  _CRTIMP wchar_t *__cdecl _itow(int _Value,wchar_t *_Dest,int _Radix);
-  _CRTIMP wchar_t *__cdecl _ltow(long _Value,wchar_t *_Dest,int _Radix);
-  _CRTIMP wchar_t *__cdecl _ultow(unsigned long _Value,wchar_t *_Dest,int _Radix);
-  double __cdecl wcstod(const wchar_t *_Str,wchar_t **_EndPtr);
-  float __cdecl wcstof(const wchar_t *nptr, wchar_t **endptr);
-#if !defined __NO_ISOCEXT /* in libmingwex.a */
-  float __cdecl wcstof( const wchar_t * __restrict__, wchar_t ** __restrict__);
-  long double __cdecl wcstold(const wchar_t * __restrict__, wchar_t ** __restrict__);
-#endif /* __NO_ISOCEXT */
-  _CRTIMP double __cdecl _wcstod_l(const wchar_t *_Str,wchar_t **_EndPtr,_locale_t _Locale);
-  long __cdecl wcstol(const wchar_t *_Str,wchar_t **_EndPtr,int _Radix);
-  _CRTIMP long __cdecl _wcstol_l(const wchar_t *_Str,wchar_t **_EndPtr,int _Radix,_locale_t _Locale);
-  unsigned long __cdecl wcstoul(const wchar_t *_Str,wchar_t **_EndPtr,int _Radix);
-  _CRTIMP unsigned long __cdecl _wcstoul_l(const wchar_t *_Str,wchar_t **_EndPtr,int _Radix,_locale_t _Locale);
-  _CRTIMP wchar_t *__cdecl _wgetenv(const wchar_t *_VarName);
-#ifndef _CRT_WSYSTEM_DEFINED
-#define _CRT_WSYSTEM_DEFINED
-  _CRTIMP int __cdecl _wsystem(const wchar_t *_Command);
-#endif
-  _CRTIMP double __cdecl _wtof(const wchar_t *_Str);
-  _CRTIMP double __cdecl _wtof_l(const wchar_t *_Str,_locale_t _Locale);
-  _CRTIMP int __cdecl _wtoi(const wchar_t *_Str);
-  _CRTIMP int __cdecl _wtoi_l(const wchar_t *_Str,_locale_t _Locale);
-  _CRTIMP long __cdecl _wtol(const wchar_t *_Str);
-  _CRTIMP long __cdecl _wtol_l(const wchar_t *_Str,_locale_t _Locale);
-
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP wchar_t *__cdecl _i64tow(__int64 _Val,wchar_t *_DstBuf,int _Radix);
-  _CRTIMP wchar_t *__cdecl _ui64tow(unsigned __int64 _Val,wchar_t *_DstBuf,int _Radix);
-  _CRTIMP __int64 __cdecl _wtoi64(const wchar_t *_Str);
-  _CRTIMP __int64 __cdecl _wtoi64_l(const wchar_t *_Str,_locale_t _Locale);
-  _CRTIMP __int64 __cdecl _wcstoi64(const wchar_t *_Str,wchar_t **_EndPtr,int _Radix);
-  _CRTIMP __int64 __cdecl _wcstoi64_l(const wchar_t *_Str,wchar_t **_EndPtr,int _Radix,_locale_t _Locale);
-  _CRTIMP unsigned __int64 __cdecl _wcstoui64(const wchar_t *_Str,wchar_t **_EndPtr,int _Radix);
-  _CRTIMP unsigned __int64 __cdecl _wcstoui64_l(const wchar_t *_Str ,wchar_t **_EndPtr,int _Radix,_locale_t _Locale);
-#endif
-#endif
-
-#ifndef _POSIX_
-#define _CVTBUFSIZE (309+40)
-  _CRTIMP char *__cdecl _fullpath(char *_FullPath,const char *_Path,size_t _SizeInBytes);
-  _CRTIMP char *__cdecl _ecvt(double _Val,int _NumOfDigits,int *_PtDec,int *_PtSign);
-  _CRTIMP char *__cdecl _fcvt(double _Val,int _NumOfDec,int *_PtDec,int *_PtSign);
-  _CRTIMP char *__cdecl _gcvt(double _Val,int _NumOfDigits,char *_DstBuf);
-  _CRTIMP int __cdecl _atodbl(_CRT_DOUBLE *_Result,char *_Str);
-  _CRTIMP int __cdecl _atoldbl(_LDOUBLE *_Result,char *_Str);
-  _CRTIMP int __cdecl _atoflt(_CRT_FLOAT *_Result,char *_Str);
-  _CRTIMP int __cdecl _atodbl_l(_CRT_DOUBLE *_Result,char *_Str,_locale_t _Locale);
-  _CRTIMP int __cdecl _atoldbl_l(_LDOUBLE *_Result,char *_Str,_locale_t _Locale);
-  _CRTIMP int __cdecl _atoflt_l(_CRT_FLOAT *_Result,char *_Str,_locale_t _Locale);
-  unsigned long __cdecl _lrotl(unsigned long _Val,int _Shift);
-  unsigned long __cdecl _lrotr(unsigned long _Val,int _Shift);
-  _CRTIMP void __cdecl _makepath(char *_Path,const char *_Drive,const char *_Dir,const char *_Filename,const char *_Ext);
-  _onexit_t __cdecl _onexit(_onexit_t _Func);
-
-#ifndef _CRT_PERROR_DEFINED
-#define _CRT_PERROR_DEFINED
-  void __cdecl perror(const char *_ErrMsg);
-#endif
-  _CRTIMP int __cdecl _putenv(const char *_EnvString);
-  unsigned int __cdecl _rotl(unsigned int _Val,int _Shift);
-#if _INTEGRAL_MAX_BITS >= 64
-  unsigned __int64 __cdecl _rotl64(unsigned __int64 _Val,int _Shift);
-#endif
-  unsigned int __cdecl _rotr(unsigned int _Val,int _Shift);
-#if _INTEGRAL_MAX_BITS >= 64
-  unsigned __int64 __cdecl _rotr64(unsigned __int64 _Val,int _Shift);
-#endif
-  _CRTIMP void __cdecl _searchenv(const char *_Filename,const char *_EnvVar,char *_ResultPath);
-  _CRTIMP void __cdecl _splitpath(const char *_FullPath,char *_Drive,char *_Dir,char *_Filename,char *_Ext);
-  _CRTIMP void __cdecl _swab(char *_Buf1,char *_Buf2,int _SizeInBytes);
-
-#ifndef _WSTDLIBP_DEFINED
-#define _WSTDLIBP_DEFINED
-  _CRTIMP wchar_t *__cdecl _wfullpath(wchar_t *_FullPath,const wchar_t *_Path,size_t _SizeInWords);
-  _CRTIMP void __cdecl _wmakepath(wchar_t *_ResultPath,const wchar_t *_Drive,const wchar_t *_Dir,const wchar_t *_Filename,const wchar_t *_Ext);
-#ifndef _CRT_WPERROR_DEFINED
-#define _CRT_WPERROR_DEFINED
-  _CRTIMP void __cdecl _wperror(const wchar_t *_ErrMsg);
-#endif
-  _CRTIMP int __cdecl _wputenv(const wchar_t *_EnvString);
-  _CRTIMP void __cdecl _wsearchenv(const wchar_t *_Filename,const wchar_t *_EnvVar,wchar_t *_ResultPath);
-  _CRTIMP void __cdecl _wsplitpath(const wchar_t *_FullPath,wchar_t *_Drive,wchar_t *_Dir,wchar_t *_Filename,wchar_t *_Ext);
-#endif
-
-  _CRTIMP void __cdecl _beep(unsigned _Frequency,unsigned _Duration) __MINGW_ATTRIB_DEPRECATED;
-  /* Not to be confused with  _set_error_mode (int).  */
-  _CRTIMP void __cdecl _seterrormode(int _Mode) __MINGW_ATTRIB_DEPRECATED;
-  _CRTIMP void __cdecl _sleep(unsigned long _Duration) __MINGW_ATTRIB_DEPRECATED;
-#endif
-
-#ifndef NO_OLDNAMES
-#ifndef _POSIX_
-#if 0
-#ifndef __cplusplus
-#ifndef NOMINMAX
-#ifndef max
-#define max(a,b) (((a) > (b)) ? (a) : (b))
-#endif
-#ifndef min
-#define min(a,b) (((a) < (b)) ? (a) : (b))
-#endif
-#endif
-#endif
-#endif
-
-#define sys_errlist _sys_errlist
-#define sys_nerr _sys_nerr
-#define environ _environ
-  char *__cdecl ecvt(double _Val,int _NumOfDigits,int *_PtDec,int *_PtSign);
-  char *__cdecl fcvt(double _Val,int _NumOfDec,int *_PtDec,int *_PtSign);
-  char *__cdecl gcvt(double _Val,int _NumOfDigits,char *_DstBuf);
-  char *__cdecl itoa(int _Val,char *_DstBuf,int _Radix);
-  char *__cdecl ltoa(long _Val,char *_DstBuf,int _Radix);
-  int __cdecl putenv(const char *_EnvString);
-  void __cdecl swab(char *_Buf1,char *_Buf2,int _SizeInBytes);
-  char *__cdecl ultoa(unsigned long _Val,char *_Dstbuf,int _Radix);
-  onexit_t __cdecl onexit(onexit_t _Func);
-#endif
-#endif
-
-#if !defined __NO_ISOCEXT /* externs in static libmingwex.a */
-
-  typedef struct { long long quot, rem; } lldiv_t;
-
-  lldiv_t __cdecl lldiv(long long, long long);
-
-  __CRT_INLINE long long __cdecl llabs(long long _j) { return (_j >= 0 ? _j : -_j); }
-
-  long long  __cdecl strtoll(const char* __restrict__, char** __restrict, int);
-  unsigned long long  __cdecl strtoull(const char* __restrict__, char** __restrict__, int);
-
-  /* these are stubs for MS _i64 versions */
-  long long  __cdecl atoll (const char *);
-
-#ifndef __STRICT_ANSI__
-  long long  __cdecl wtoll (const wchar_t *);
-  char *__cdecl lltoa (long long, char *, int);
-  char *__cdecl ulltoa (unsigned long long , char *, int);
-  wchar_t *__cdecl lltow (long long, wchar_t *, int);
-  wchar_t *__cdecl ulltow (unsigned long long, wchar_t *, int);
-
-  /* __CRT_INLINE using non-ansi functions */
-  __CRT_INLINE long long  __cdecl atoll (const char * _c) { return _atoi64 (_c); }
-  __CRT_INLINE char *__cdecl lltoa (long long _n, char * _c, int _i) { return _i64toa (_n, _c, _i); }
-  __CRT_INLINE char *__cdecl ulltoa (unsigned long long _n, char * _c, int _i) { return _ui64toa (_n, _c, _i); }
-  __CRT_INLINE long long  __cdecl wtoll (const wchar_t * _w) { return _wtoi64 (_w); }
-  __CRT_INLINE wchar_t *__cdecl lltow (long long _n, wchar_t * _w, int _i) { return _i64tow (_n, _w, _i); }
-  __CRT_INLINE wchar_t *__cdecl ulltow (unsigned long long _n, wchar_t * _w, int _i) { return _ui64tow (_n, _w, _i); }
-#endif /* (__STRICT_ANSI__)  */
-
-#endif /* !__NO_ISOCEXT */
-
-#ifdef __cplusplus
-}
-#endif
-
-#pragma pack(pop)
-
-#include <sec_api/stdlib_s.h>
-#include <malloc.h>
-
-#endif
diff --git a/tinyc/win32/include/string.h b/tinyc/win32/include/string.h
deleted file mode 100644
index 3249dc3ba..000000000
--- a/tinyc/win32/include/string.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_STRING
-#define _INC_STRING
-
-#include <_mingw.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _NLSCMP_DEFINED
-#define _NLSCMP_DEFINED
-#define _NLSCMPERROR 2147483647
-#endif
-
-#ifndef NULL
-#ifdef __cplusplus
-#define NULL 0
-#else
-#define NULL ((void *)0)
-#endif
-#endif
-
-#define _WConst_return _CONST_RETURN
-
-#ifndef _CRT_MEMORY_DEFINED
-#define _CRT_MEMORY_DEFINED
-  _CRTIMP void *__cdecl _memccpy(void *_Dst,const void *_Src,int _Val,size_t _MaxCount);
-  _CONST_RETURN void *__cdecl memchr(const void *_Buf ,int _Val,size_t _MaxCount);
-  _CRTIMP int __cdecl _memicmp(const void *_Buf1,const void *_Buf2,size_t _Size);
-  _CRTIMP int __cdecl _memicmp_l(const void *_Buf1,const void *_Buf2,size_t _Size,_locale_t _Locale);
-  int __cdecl memcmp(const void *_Buf1,const void *_Buf2,size_t _Size);
-  void *__cdecl memcpy(void *_Dst,const void *_Src,size_t _Size);
-  void *__cdecl memset(void *_Dst,int _Val,size_t _Size);
-#ifndef	NO_OLDNAMES
-  void *__cdecl memccpy(void *_Dst,const void *_Src,int _Val,size_t _Size);
-  int __cdecl memicmp(const void *_Buf1,const void *_Buf2,size_t _Size);
-#endif
-#endif
-  char *__cdecl _strset(char *_Str,int _Val);
-  char *__cdecl strcpy(char *_Dest,const char *_Source);
-  char *__cdecl strcat(char *_Dest,const char *_Source);
-  int __cdecl strcmp(const char *_Str1,const char *_Str2);
-  size_t __cdecl strlen(const char *_Str);
-#if 0
-  size_t __cdecl strnlen(const char *_Str,size_t _MaxCount);
-#endif
-  void *__cdecl memmove(void *_Dst,const void *_Src,size_t _Size);
-  _CRTIMP char *__cdecl _strdup(const char *_Src);
-  _CONST_RETURN char *__cdecl strchr(const char *_Str,int _Val);
-  _CRTIMP int __cdecl _stricmp(const char *_Str1,const char *_Str2);
-  _CRTIMP int __cdecl _strcmpi(const char *_Str1,const char *_Str2);
-  _CRTIMP int __cdecl _stricmp_l(const char *_Str1,const char *_Str2,_locale_t _Locale);
-  int __cdecl strcoll(const char *_Str1,const char *_Str2);
-  _CRTIMP int __cdecl _strcoll_l(const char *_Str1,const char *_Str2,_locale_t _Locale);
-  _CRTIMP int __cdecl _stricoll(const char *_Str1,const char *_Str2);
-  _CRTIMP int __cdecl _stricoll_l(const char *_Str1,const char *_Str2,_locale_t _Locale);
-  _CRTIMP int __cdecl _strncoll (const char *_Str1,const char *_Str2,size_t _MaxCount);
-  _CRTIMP int __cdecl _strncoll_l(const char *_Str1,const char *_Str2,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP int __cdecl _strnicoll (const char *_Str1,const char *_Str2,size_t _MaxCount);
-  _CRTIMP int __cdecl _strnicoll_l(const char *_Str1,const char *_Str2,size_t _MaxCount,_locale_t _Locale);
-  size_t __cdecl strcspn(const char *_Str,const char *_Control);
-  _CRTIMP char *__cdecl _strerror(const char *_ErrMsg);
-  char *__cdecl strerror(int);
-  _CRTIMP char *__cdecl _strlwr(char *_String);
-  char *strlwr_l(char *_String,_locale_t _Locale);
-  char *__cdecl strncat(char *_Dest,const char *_Source,size_t _Count);
-  int __cdecl strncmp(const char *_Str1,const char *_Str2,size_t _MaxCount);
-  _CRTIMP int __cdecl _strnicmp(const char *_Str1,const char *_Str2,size_t _MaxCount);
-  _CRTIMP int __cdecl _strnicmp_l(const char *_Str1,const char *_Str2,size_t _MaxCount,_locale_t _Locale);
-  char *strncpy(char *_Dest,const char *_Source,size_t _Count);
-  _CRTIMP char *__cdecl _strnset(char *_Str,int _Val,size_t _MaxCount);
-  _CONST_RETURN char *__cdecl strpbrk(const char *_Str,const char *_Control);
-  _CONST_RETURN char *__cdecl strrchr(const char *_Str,int _Ch);
-  _CRTIMP char *__cdecl _strrev(char *_Str);
-  size_t __cdecl strspn(const char *_Str,const char *_Control);
-  _CONST_RETURN char *__cdecl strstr(const char *_Str,const char *_SubStr);
-  char *__cdecl strtok(char *_Str,const char *_Delim);
-  _CRTIMP char *__cdecl _strupr(char *_String);
-  _CRTIMP char *_strupr_l(char *_String,_locale_t _Locale);
-  size_t __cdecl strxfrm(char *_Dst,const char *_Src,size_t _MaxCount);
-  _CRTIMP size_t __cdecl _strxfrm_l(char *_Dst,const char *_Src,size_t _MaxCount,_locale_t _Locale);
-
-#ifndef	NO_OLDNAMES
-  char *__cdecl strdup(const char *_Src);
-  int __cdecl strcmpi(const char *_Str1,const char *_Str2);
-  int __cdecl stricmp(const char *_Str1,const char *_Str2);
-  char *__cdecl strlwr(char *_Str);
-  int __cdecl strnicmp(const char *_Str1,const char *_Str,size_t _MaxCount);
-  __CRT_INLINE int __cdecl strncasecmp (const char *__sz1, const char *__sz2, size_t __sizeMaxCompare) { return _strnicmp (__sz1, __sz2, __sizeMaxCompare); }
-  __CRT_INLINE int __cdecl strcasecmp (const char *__sz1, const char *__sz2) { return _stricmp (__sz1, __sz2); }
-  char *__cdecl strnset(char *_Str,int _Val,size_t _MaxCount);
-  char *__cdecl strrev(char *_Str);
-  char *__cdecl strset(char *_Str,int _Val);
-  char *__cdecl strupr(char *_Str);
-#endif
-
-#ifndef _WSTRING_DEFINED
-#define _WSTRING_DEFINED
-
-  _CRTIMP wchar_t *__cdecl _wcsdup(const wchar_t *_Str);
-  wchar_t *__cdecl wcscat(wchar_t *_Dest,const wchar_t *_Source);
-  _CONST_RETURN wchar_t *__cdecl wcschr(const wchar_t *_Str,wchar_t _Ch);
-  int __cdecl wcscmp(const wchar_t *_Str1,const wchar_t *_Str2);
-  wchar_t *__cdecl wcscpy(wchar_t *_Dest,const wchar_t *_Source);
-  size_t __cdecl wcscspn(const wchar_t *_Str,const wchar_t *_Control);
-  size_t __cdecl wcslen(const wchar_t *_Str);
-  size_t __cdecl wcsnlen(const wchar_t *_Src,size_t _MaxCount);
-  wchar_t *wcsncat(wchar_t *_Dest,const wchar_t *_Source,size_t _Count);
-  int __cdecl wcsncmp(const wchar_t *_Str1,const wchar_t *_Str2,size_t _MaxCount);
-  wchar_t *wcsncpy(wchar_t *_Dest,const wchar_t *_Source,size_t _Count);
-  _CONST_RETURN wchar_t *__cdecl wcspbrk(const wchar_t *_Str,const wchar_t *_Control);
-  _CONST_RETURN wchar_t *__cdecl wcsrchr(const wchar_t *_Str,wchar_t _Ch);
-  size_t __cdecl wcsspn(const wchar_t *_Str,const wchar_t *_Control);
-  _CONST_RETURN wchar_t *__cdecl wcsstr(const wchar_t *_Str,const wchar_t *_SubStr);
-  wchar_t *__cdecl wcstok(wchar_t *_Str,const wchar_t *_Delim);
-  _CRTIMP wchar_t *__cdecl _wcserror(int _ErrNum);
-  _CRTIMP wchar_t *__cdecl __wcserror(const wchar_t *_Str);
-  _CRTIMP int __cdecl _wcsicmp(const wchar_t *_Str1,const wchar_t *_Str2);
-  _CRTIMP int __cdecl _wcsicmp_l(const wchar_t *_Str1,const wchar_t *_Str2,_locale_t _Locale);
-  _CRTIMP int __cdecl _wcsnicmp(const wchar_t *_Str1,const wchar_t *_Str2,size_t _MaxCount);
-  _CRTIMP int __cdecl _wcsnicmp_l(const wchar_t *_Str1,const wchar_t *_Str2,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP wchar_t *__cdecl _wcsnset(wchar_t *_Str,wchar_t _Val,size_t _MaxCount);
-  _CRTIMP wchar_t *__cdecl _wcsrev(wchar_t *_Str);
-  _CRTIMP wchar_t *__cdecl _wcsset(wchar_t *_Str,wchar_t _Val);
-  _CRTIMP wchar_t *__cdecl _wcslwr(wchar_t *_String);
-  _CRTIMP wchar_t *_wcslwr_l(wchar_t *_String,_locale_t _Locale);
-  _CRTIMP wchar_t *__cdecl _wcsupr(wchar_t *_String);
-  _CRTIMP wchar_t *_wcsupr_l(wchar_t *_String,_locale_t _Locale);
-  size_t __cdecl wcsxfrm(wchar_t *_Dst,const wchar_t *_Src,size_t _MaxCount);
-  _CRTIMP size_t __cdecl _wcsxfrm_l(wchar_t *_Dst,const wchar_t *_Src,size_t _MaxCount,_locale_t _Locale);
-  int __cdecl wcscoll(const wchar_t *_Str1,const wchar_t *_Str2);
-  _CRTIMP int __cdecl _wcscoll_l(const wchar_t *_Str1,const wchar_t *_Str2,_locale_t _Locale);
-  _CRTIMP int __cdecl _wcsicoll(const wchar_t *_Str1,const wchar_t *_Str2);
-  _CRTIMP int __cdecl _wcsicoll_l(const wchar_t *_Str1,const wchar_t *_Str2,_locale_t _Locale);
-  _CRTIMP int __cdecl _wcsncoll(const wchar_t *_Str1,const wchar_t *_Str2,size_t _MaxCount);
-  _CRTIMP int __cdecl _wcsncoll_l(const wchar_t *_Str1,const wchar_t *_Str2,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP int __cdecl _wcsnicoll(const wchar_t *_Str1,const wchar_t *_Str2,size_t _MaxCount);
-  _CRTIMP int __cdecl _wcsnicoll_l(const wchar_t *_Str1,const wchar_t *_Str2,size_t _MaxCount,_locale_t _Locale);
-
-#ifndef	NO_OLDNAMES
-  wchar_t *__cdecl wcsdup(const wchar_t *_Str);
-#define wcswcs wcsstr
-  int __cdecl wcsicmp(const wchar_t *_Str1,const wchar_t *_Str2);
-  int __cdecl wcsnicmp(const wchar_t *_Str1,const wchar_t *_Str2,size_t _MaxCount);
-  wchar_t *__cdecl wcsnset(wchar_t *_Str,wchar_t _Val,size_t _MaxCount);
-  wchar_t *__cdecl wcsrev(wchar_t *_Str);
-  wchar_t *__cdecl wcsset(wchar_t *_Str,wchar_t _Val);
-  wchar_t *__cdecl wcslwr(wchar_t *_Str);
-  wchar_t *__cdecl wcsupr(wchar_t *_Str);
-  int __cdecl wcsicoll(const wchar_t *_Str1,const wchar_t *_Str2);
-#endif
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#include <sec_api/string_s.h>
-#endif
diff --git a/tinyc/win32/include/sys/fcntl.h b/tinyc/win32/include/sys/fcntl.h
deleted file mode 100644
index 29fd55a1f..000000000
--- a/tinyc/win32/include/sys/fcntl.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-/*
- * This file is part of the Mingw32 package.
- *
- * This fcntl.h maps to the root fcntl.h
- */
-#ifndef __STRICT_ANSI__
-#include <fcntl.h>
-#endif
diff --git a/tinyc/win32/include/sys/file.h b/tinyc/win32/include/sys/file.h
deleted file mode 100644
index 370f352d6..000000000
--- a/tinyc/win32/include/sys/file.h
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-/*
- * This file is part of the Mingw32 package.
- *
- * This file.h maps to the root fcntl.h
- * TODO?
- */
-#ifndef __STRICT_ANSI__
-#include <fcntl.h>
-#endif
diff --git a/tinyc/win32/include/sys/locking.h b/tinyc/win32/include/sys/locking.h
deleted file mode 100644
index e3fc85b3a..000000000
--- a/tinyc/win32/include/sys/locking.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_LOCKING
-#define _INC_LOCKING
-
-#ifndef _WIN32
-#error Only Win32 target is supported!
-#endif
-
-/* All the headers include this file. */
-#include <_mingw.h>
-
-#define _LK_UNLCK 0
-#define _LK_LOCK 1
-#define _LK_NBLCK 2
-#define _LK_RLCK 3
-#define _LK_NBRLCK 4
-
-#ifndef	NO_OLDNAMES
-#define LK_UNLCK _LK_UNLCK
-#define LK_LOCK _LK_LOCK
-#define LK_NBLCK _LK_NBLCK
-#define LK_RLCK _LK_RLCK
-#define LK_NBRLCK _LK_NBRLCK
-#endif
-
-#endif
diff --git a/tinyc/win32/include/sys/stat.h b/tinyc/win32/include/sys/stat.h
deleted file mode 100644
index 344d4a249..000000000
--- a/tinyc/win32/include/sys/stat.h
+++ /dev/null
@@ -1,290 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_STAT
-#define _INC_STAT
-
-#ifndef _WIN32
-#error Only Win32 target is supported!
-#endif
-
-#include <_mingw.h>
-#include <io.h>
-
-#pragma pack(push,_CRT_PACKING)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _CRTIMP
-#define _CRTIMP __declspec(dllimport)
-#endif
-
-#include <sys/types.h>
-
-#ifndef __TINYC__ /* gr */
-#ifdef _USE_32BIT_TIME_T
-#ifdef _WIN64
-#undef _USE_32BIT_TIME_T
-#endif
-#else
-#if _INTEGRAL_MAX_BITS < 64
-#define _USE_32BIT_TIME_T
-#endif
-#endif
-#endif
-
-#ifndef _TIME32_T_DEFINED
-  typedef long __time32_t;
-#define _TIME32_T_DEFINED
-#endif
-
-#ifndef _TIME64_T_DEFINED
-#if _INTEGRAL_MAX_BITS >= 64
-  typedef __int64 __time64_t;
-#endif
-#define _TIME64_T_DEFINED
-#endif
-
-#ifndef _TIME_T_DEFINED
-#ifdef _USE_32BIT_TIME_T
-  typedef __time32_t time_t;
-#else
-  typedef __time64_t time_t;
-#endif
-#define _TIME_T_DEFINED
-#endif
-
-#ifndef _WCHAR_T_DEFINED
-  typedef unsigned short wchar_t;
-#define _WCHAR_T_DEFINED
-#endif
-
-#ifndef _STAT_DEFINED
-
-#ifdef _USE_32BIT_TIME_T
-#ifndef _WIN64
-#define _fstat32 _fstat
-#define _stat32 _stat
-#define _wstat32 _wstat
-#else
-#define _fstat _fstat32
-#define _stat _stat32
-#define _wstat _wstat32
-#endif
-#define _fstati64 _fstat32i64
-#define _stati64 _stat32i64
-#define _wstati64 _wstat32i64
-#else
-#define _fstat _fstat64i32
-#define _fstati64 _fstat64
-#define _stat _stat64i32
-#define _stati64 _stat64
-#define _wstat _wstat64i32
-#define _wstati64 _wstat64
-#endif
-
-  struct _stat32 {
-    _dev_t st_dev;
-    _ino_t st_ino;
-    unsigned short st_mode;
-    short st_nlink;
-    short st_uid;
-    short st_gid;
-    _dev_t st_rdev;
-    _off_t st_size;
-    __time32_t st_atime;
-    __time32_t st_mtime;
-    __time32_t st_ctime;
-  };
-
-#ifndef	NO_OLDNAMES
-  struct stat {
-    _dev_t st_dev;
-    _ino_t st_ino;
-    unsigned short st_mode;
-    short st_nlink;
-    short st_uid;
-    short st_gid;
-    _dev_t st_rdev;
-    _off_t st_size;
-    time_t st_atime;
-    time_t st_mtime;
-    time_t st_ctime;
-  };
-#endif
-
-#if _INTEGRAL_MAX_BITS >= 64
-  struct _stat32i64 {
-    _dev_t st_dev;
-    _ino_t st_ino;
-    unsigned short st_mode;
-    short st_nlink;
-    short st_uid;
-    short st_gid;
-    _dev_t st_rdev;
-    __int64 st_size;
-    __time32_t st_atime;
-    __time32_t st_mtime;
-    __time32_t st_ctime;
-  };
-
-  struct _stat64i32 {
-    _dev_t st_dev;
-    _ino_t st_ino;
-    unsigned short st_mode;
-    short st_nlink;
-    short st_uid;
-    short st_gid;
-    _dev_t st_rdev;
-    _off_t st_size;
-    __time64_t st_atime;
-    __time64_t st_mtime;
-    __time64_t st_ctime;
-  };
-
-  struct _stat64 {
-    _dev_t st_dev;
-    _ino_t st_ino;
-    unsigned short st_mode;
-    short st_nlink;
-    short st_uid;
-    short st_gid;
-    _dev_t st_rdev;
-    __int64 st_size;
-    __time64_t st_atime;
-    __time64_t st_mtime;
-    __time64_t st_ctime;
-  };
-#endif
-
-#define __stat64 _stat64
-
-#define _STAT_DEFINED
-#endif
-
-#define _S_IFMT 0xF000
-#define _S_IFDIR 0x4000
-#define _S_IFCHR 0x2000
-#define _S_IFIFO 0x1000
-#define _S_IFREG 0x8000
-#define _S_IREAD 0x0100
-#define _S_IWRITE 0x0080
-#define _S_IEXEC 0x0040
-
-  _CRTIMP int __cdecl _fstat32(int _FileDes,struct _stat32 *_Stat);
-  _CRTIMP int __cdecl _stat32(const char *_Name,struct _stat32 *_Stat);
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP int __cdecl _fstat64(int _FileDes,struct _stat64 *_Stat);
-  _CRTIMP int __cdecl _fstat32i64(int _FileDes,struct _stat32i64 *_Stat);
-  int __cdecl _fstat64i32(int _FileDes,struct _stat64i32 *_Stat);
-  __CRT_INLINE int __cdecl _fstat64i32(int _FileDes,struct _stat64i32 *_Stat)
-  {
-    struct _stat64 st;
-    int ret=_fstat64(_FileDes,&st);
-    _Stat->st_dev=st.st_dev;
-    _Stat->st_ino=st.st_ino;
-    _Stat->st_mode=st.st_mode;
-    _Stat->st_nlink=st.st_nlink;
-    _Stat->st_uid=st.st_uid;
-    _Stat->st_gid=st.st_gid;
-    _Stat->st_rdev=st.st_rdev;
-    _Stat->st_size=(_off_t) st.st_size;
-    _Stat->st_atime=st.st_atime;
-    _Stat->st_mtime=st.st_mtime;
-    _Stat->st_ctime=st.st_ctime;
-    return ret;
-  }
-  _CRTIMP int __cdecl _stat64(const char *_Name,struct _stat64 *_Stat);
-  _CRTIMP int __cdecl _stat32i64(const char *_Name,struct _stat32i64 *_Stat);
-  int __cdecl _stat64i32(const char *_Name,struct _stat64i32 *_Stat);
-  __CRT_INLINE int __cdecl _stat64i32(const char *_Name,struct _stat64i32 *_Stat)
-  {
-    struct _stat64 st;
-    int ret=_stat64(_Name,&st);
-    _Stat->st_dev=st.st_dev;
-    _Stat->st_ino=st.st_ino;
-    _Stat->st_mode=st.st_mode;
-    _Stat->st_nlink=st.st_nlink;
-    _Stat->st_uid=st.st_uid;
-    _Stat->st_gid=st.st_gid;
-    _Stat->st_rdev=st.st_rdev;
-    _Stat->st_size=(_off_t) st.st_size;
-    _Stat->st_atime=st.st_atime;
-    _Stat->st_mtime=st.st_mtime;
-    _Stat->st_ctime=st.st_ctime;
-    return ret;
-  }
-#endif
-
-#ifndef _WSTAT_DEFINED
-#define _WSTAT_DEFINED
-  _CRTIMP int __cdecl _wstat32(const wchar_t *_Name,struct _stat32 *_Stat);
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP int __cdecl _wstat32i64(const wchar_t *_Name,struct _stat32i64 *_Stat);
-  int __cdecl _wstat64i32(const wchar_t *_Name,struct _stat64i32 *_Stat);
-  _CRTIMP int __cdecl _wstat64(const wchar_t *_Name,struct _stat64 *_Stat);
-#endif
-#endif
-
-#ifndef	NO_OLDNAMES
-#define	_S_IFBLK	0x3000	/* Block: Is this ever set under w32? */
-
-#define S_IFMT _S_IFMT
-#define S_IFDIR _S_IFDIR
-#define S_IFCHR _S_IFCHR
-#define S_IFREG _S_IFREG
-#define S_IREAD _S_IREAD
-#define S_IWRITE _S_IWRITE
-#define S_IEXEC _S_IEXEC
-#define	S_IFIFO		_S_IFIFO
-#define	S_IFBLK		_S_IFBLK
-
-#define	_S_IRWXU	(_S_IREAD | _S_IWRITE | _S_IEXEC)
-#define	_S_IXUSR	_S_IEXEC
-#define	_S_IWUSR	_S_IWRITE
-
-#define	S_IRWXU		_S_IRWXU
-#define	S_IXUSR		_S_IXUSR
-#define	S_IWUSR		_S_IWUSR
-#define	S_IRUSR		_S_IRUSR
-#define	_S_IRUSR	_S_IREAD
-
-#define	S_ISDIR(m)	(((m) & S_IFMT) == S_IFDIR)
-#define	S_ISFIFO(m)	(((m) & S_IFMT) == S_IFIFO)
-#define	S_ISCHR(m)	(((m) & S_IFMT) == S_IFCHR)
-#define	S_ISBLK(m)	(((m) & S_IFMT) == S_IFBLK)
-#define	S_ISREG(m)	(((m) & S_IFMT) == S_IFREG)
-
-#endif
-
-#if !defined (RC_INVOKED) && !defined (NO_OLDNAMES)
-int __cdecl stat(const char *_Filename,struct stat *_Stat);
-int __cdecl fstat(int _Desc,struct stat *_Stat);
-int __cdecl wstat(const wchar_t *_Filename,struct stat *_Stat);
-#ifdef _USE_32BIT_TIME_T
-__CRT_INLINE int __cdecl fstat(int _Desc,struct stat *_Stat) {
-  return _fstat32(_Desc,(struct _stat32 *)_Stat);
-}
-__CRT_INLINE int __cdecl stat(const char *_Filename,struct stat *_Stat) {
-  return _stat32(_Filename,(struct _stat32 *)_Stat);
-}
-#else
-__CRT_INLINE int __cdecl fstat(int _Desc,struct stat *_Stat) {
-  return _fstat64i32(_Desc,(struct _stat64i32 *)_Stat);
-}
-__CRT_INLINE int __cdecl stat(const char *_Filename,struct stat *_Stat) {
-  return _stat64i32(_Filename,(struct _stat64i32 *)_Stat);
-}
-#endif
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#pragma pack(pop)
-#endif
diff --git a/tinyc/win32/include/sys/time.h b/tinyc/win32/include/sys/time.h
deleted file mode 100644
index 8ccab8319..000000000
--- a/tinyc/win32/include/sys/time.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-
-#ifndef _SYS_TIME_H_
-#define _SYS_TIME_H_
-
-#include <time.h>
-
-#ifdef  __cplusplus
-extern "C" {
-#endif
-
-#ifndef __STRICT_ANSI__
-#ifndef _TIMEVAL_DEFINED /* also in winsock[2].h */
-#define _TIMEVAL_DEFINED
-struct timeval {
-  long tv_sec;
-  long tv_usec;
-};
-#define timerisset(tvp)	 ((tvp)->tv_sec || (tvp)->tv_usec)
-#define timercmp(tvp, uvp, cmp) \
-  (((tvp)->tv_sec != (uvp)->tv_sec) ? \
-  ((tvp)->tv_sec cmp (uvp)->tv_sec) : \
-  ((tvp)->tv_usec cmp (uvp)->tv_usec))
-#define timerclear(tvp)	 (tvp)->tv_sec = (tvp)->tv_usec = 0
-#endif /* _TIMEVAL_DEFINED */
-
-#ifndef _TIMEZONE_DEFINED /* also in sys/time.h */
-#define _TIMEZONE_DEFINED
-/* Provided for compatibility with code that assumes that
-   the presence of gettimeofday function implies a definition
-   of struct timezone. */
-struct timezone
-{
-  int tz_minuteswest; /* of Greenwich */
-  int tz_dsttime;     /* type of dst correction to apply */
-};
-
-  extern int __cdecl mingw_gettimeofday (struct timeval *p, struct timezone *z);
-
-#endif
-
-/*
-   Implementation as per:
-   The Open Group Base Specifications, Issue 6
-   IEEE Std 1003.1, 2004 Edition
-
-   The timezone pointer arg is ignored.  Errors are ignored.
-*/
-#ifndef _GETTIMEOFDAY_DEFINED
-#define _GETTIMEOFDAY_DEFINED
-int __cdecl gettimeofday(struct timeval *__restrict__,
-                         void *__restrict__  /* tzp (unused) */);
-#endif
-
-#endif /* __STRICT_ANSI__ */
-
-#ifdef  __cplusplus
-}
-#endif
-
-/* Adding timespec definition.  */
-#include <sys/timeb.h>
-
-
-#endif /* _SYS_TIME_H_ */
diff --git a/tinyc/win32/include/sys/timeb.h b/tinyc/win32/include/sys/timeb.h
deleted file mode 100644
index 34837738b..000000000
--- a/tinyc/win32/include/sys/timeb.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _TIMEB_H_
-#define _TIMEB_H_
-
-#include <_mingw.h>
-
-#ifndef _WIN32
-#error Only Win32 target is supported!
-#endif
-
-#pragma pack(push,_CRT_PACKING)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _CRTIMP
-#define _CRTIMP __declspec(dllimport)
-#endif
-
-#ifndef __TINYC__ /* gr */
-#ifdef _USE_32BIT_TIME_T
-#ifdef _WIN64
-#undef _USE_32BIT_TIME_T
-#endif
-#else
-#if _INTEGRAL_MAX_BITS < 64
-#define _USE_32BIT_TIME_T
-#endif
-#endif
-#endif
-
-#ifndef _TIME32_T_DEFINED
-  typedef long __time32_t;
-#define _TIME32_T_DEFINED
-#endif
-
-#ifndef _TIME64_T_DEFINED
-#if _INTEGRAL_MAX_BITS >= 64
-  typedef __int64 __time64_t;
-#endif
-#define _TIME64_T_DEFINED
-#endif
-
-#ifndef _TIME_T_DEFINED
-#ifdef _USE_32BIT_TIME_T
-  typedef __time32_t time_t;
-#else
-  typedef __time64_t time_t;
-#endif
-#define _TIME_T_DEFINED
-#endif
-
-#ifndef _TIMEB_DEFINED
-#define _TIMEB_DEFINED
-
-  struct __timeb32 {
-    __time32_t time;
-    unsigned short millitm;
-    short timezone;
-    short dstflag;
-  };
-
-#ifndef	NO_OLDNAMES
-  struct timeb {
-    time_t time;
-    unsigned short millitm;
-    short timezone;
-    short dstflag;
-  };
-#endif
-
-#if _INTEGRAL_MAX_BITS >= 64
-  struct __timeb64 {
-    __time64_t time;
-    unsigned short millitm;
-    short timezone;
-    short dstflag;
-  };
-#endif
-
-#ifdef _USE_32BIT_TIME_T
-#define _timeb __timeb32
-//gr #define _ftime _ftime32
-#define _ftime32 _ftime
-#else
-#define _timeb __timeb64
-#define _ftime _ftime64
-#endif
-#endif
-
-  _CRTIMP void __cdecl _ftime32(struct __timeb32 *_Time);
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP void __cdecl _ftime64(struct __timeb64 *_Time);
-#endif
-
-#ifndef _TIMESPEC_DEFINED
-#define _TIMESPEC_DEFINED
-struct timespec {
-  time_t  tv_sec;   /* Seconds */
-  long    tv_nsec;  /* Nanoseconds */
-};
-
-struct itimerspec {
-  struct timespec  it_interval;  /* Timer period */
-  struct timespec  it_value;     /* Timer expiration */
-};
-#endif
-
-#if !defined (RC_INVOKED) && !defined (NO_OLDNAMES)
-#ifdef _USE_32BIT_TIME_T
-__CRT_INLINE void __cdecl ftime(struct timeb *_Tmb) {
-  _ftime32((struct __timeb32 *)_Tmb);
-}
-#else
-__CRT_INLINE void __cdecl ftime(struct timeb *_Tmb) {
-  _ftime64((struct __timeb64 *)_Tmb);
-}
-#endif
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#pragma pack(pop)
-
-#include <sec_api/sys/timeb_s.h>
-#endif
diff --git a/tinyc/win32/include/sys/types.h b/tinyc/win32/include/sys/types.h
deleted file mode 100644
index 7379b0f1e..000000000
--- a/tinyc/win32/include/sys/types.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_TYPES
-#define _INC_TYPES
-
-#ifndef _WIN32
-#error Only Win32 target is supported!
-#endif
-
-#include <_mingw.h>
-
-#ifndef __TINYC__ /* gr */
-#ifdef _USE_32BIT_TIME_T
-#ifdef _WIN64
-#undef _USE_32BIT_TIME_T
-#endif
-#else
-#if _INTEGRAL_MAX_BITS < 64
-#define _USE_32BIT_TIME_T
-#endif
-#endif
-#endif
-
-#ifndef _TIME32_T_DEFINED
-#define _TIME32_T_DEFINED
-typedef long __time32_t;
-#endif
-
-#ifndef _TIME64_T_DEFINED
-#define _TIME64_T_DEFINED
-#if _INTEGRAL_MAX_BITS >= 64
-typedef __int64 __time64_t;
-#endif
-#endif
-
-#ifndef _TIME_T_DEFINED
-#define _TIME_T_DEFINED
-#ifdef _USE_32BIT_TIME_T
-typedef __time32_t time_t;
-#else
-typedef __time64_t time_t;
-#endif
-#endif
-
-#ifndef _INO_T_DEFINED
-#define _INO_T_DEFINED
-typedef unsigned short _ino_t;
-#ifndef	NO_OLDNAMES
-typedef unsigned short ino_t;
-#endif
-#endif
-
-#ifndef _DEV_T_DEFINED
-#define _DEV_T_DEFINED
-typedef unsigned int _dev_t;
-#ifndef	NO_OLDNAMES
-typedef unsigned int dev_t;
-#endif
-#endif
-
-#ifndef _PID_T_
-#define	_PID_T_
-#ifndef _WIN64
-typedef int	_pid_t;
-#else
-typedef __int64	_pid_t;
-#endif
-
-#ifndef	NO_OLDNAMES
-typedef _pid_t	pid_t;
-#endif
-#endif	/* Not _PID_T_ */
-
-#ifndef _MODE_T_
-#define	_MODE_T_
-typedef unsigned short _mode_t;
-
-#ifndef	NO_OLDNAMES
-typedef _mode_t	mode_t;
-#endif
-#endif	/* Not _MODE_T_ */
-
-#ifndef _OFF_T_DEFINED
-#define _OFF_T_DEFINED
-#ifndef _OFF_T_
-#define _OFF_T_
-  typedef long _off_t;
-#if !defined(NO_OLDNAMES) || defined(_POSIX)
-  typedef long off_t;
-#endif
-#endif
-#endif
-
-#ifndef _OFF64_T_DEFINED
-#define _OFF64_T_DEFINED
-  typedef long long _off64_t;
-#if !defined(NO_OLDNAMES) || defined(_POSIX)
-  typedef long long off64_t;
-#endif
-#endif
-
-#ifndef _TIMESPEC_DEFINED
-#define _TIMESPEC_DEFINED
-struct timespec {
-  time_t  tv_sec;   /* Seconds */
-  long    tv_nsec;  /* Nanoseconds */
-};
-
-struct itimerspec {
-  struct timespec  it_interval;  /* Timer period */
-  struct timespec  it_value;     /* Timer expiration */
-};
-#endif
-
-#endif
diff --git a/tinyc/win32/include/sys/unistd.h b/tinyc/win32/include/sys/unistd.h
deleted file mode 100644
index 31006d325..000000000
--- a/tinyc/win32/include/sys/unistd.h
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-/*
- * This file is part of the Mingw32 package.
- *
- * unistd.h maps (roughly) to io.h
- */
-#ifndef __STRICT_ANSI__
-#include <io.h>
-#endif
-
diff --git a/tinyc/win32/include/sys/utime.h b/tinyc/win32/include/sys/utime.h
deleted file mode 100644
index fec8304ff..000000000
--- a/tinyc/win32/include/sys/utime.h
+++ /dev/null
@@ -1,146 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_UTIME
-#define _INC_UTIME
-
-#ifndef _WIN32
-#error Only Win32 target is supported!
-#endif
-
-#include <_mingw.h>
-
-#pragma pack(push,_CRT_PACKING)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _CRTIMP
-#define _CRTIMP __declspec(dllimport)
-#endif
-
-#ifndef _WCHAR_T_DEFINED
-  typedef unsigned short wchar_t;
-#define _WCHAR_T_DEFINED
-#endif
-
-#ifndef __TINYC__ /* gr */
-#ifdef _USE_32BIT_TIME_T
-#ifdef _WIN64
-#undef _USE_32BIT_TIME_T
-#endif
-#else
-#if _INTEGRAL_MAX_BITS < 64
-#define _USE_32BIT_TIME_T
-#endif
-#endif
-#endif
-
-#ifndef _TIME32_T_DEFINED
-#define _TIME32_T_DEFINED
-  typedef long __time32_t;
-#endif
-
-#ifndef _TIME64_T_DEFINED
-#define _TIME64_T_DEFINED
-#if _INTEGRAL_MAX_BITS >= 64
-  typedef __int64 __time64_t;
-#endif
-#endif
-
-#ifndef _TIME_T_DEFINED
-#define _TIME_T_DEFINED
-#ifdef _USE_32BIT_TIME_T
-  typedef __time32_t time_t;
-#else
-  typedef __time64_t time_t;
-#endif
-#endif
-
-#ifndef _UTIMBUF_DEFINED
-#define _UTIMBUF_DEFINED
-
-  struct _utimbuf {
-    time_t actime;
-    time_t modtime;
-  };
-
-  struct __utimbuf32 {
-    __time32_t actime;
-    __time32_t modtime;
-  };
-
-#if _INTEGRAL_MAX_BITS >= 64
-  struct __utimbuf64 {
-    __time64_t actime;
-    __time64_t modtime;
-  };
-#endif
-
-#ifndef	NO_OLDNAMES
-  struct utimbuf {
-    time_t actime;
-    time_t modtime;
-  };
-
-  struct utimbuf32 {
-    __time32_t actime;
-    __time32_t modtime;
-  };
-#endif
-#endif
-
-  _CRTIMP int __cdecl _utime32(const char *_Filename,struct __utimbuf32 *_Time);
-  _CRTIMP int __cdecl _futime32(int _FileDes,struct __utimbuf32 *_Time);
-  _CRTIMP int __cdecl _wutime32(const wchar_t *_Filename,struct __utimbuf32 *_Time);
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP int __cdecl _utime64(const char *_Filename,struct __utimbuf64 *_Time);
-  _CRTIMP int __cdecl _futime64(int _FileDes,struct __utimbuf64 *_Time);
-  _CRTIMP int __cdecl _wutime64(const wchar_t *_Filename,struct __utimbuf64 *_Time);
-#endif
-
-#ifndef RC_INVOKED
-#ifdef _USE_32BIT_TIME_T
-__CRT_INLINE int __cdecl _utime(const char *_Filename,struct _utimbuf *_Utimbuf) {
-  return _utime32(_Filename,(struct __utimbuf32 *)_Utimbuf);
-}
-__CRT_INLINE int __cdecl _futime(int _Desc,struct _utimbuf *_Utimbuf) {
-  return _futime32(_Desc,(struct __utimbuf32 *)_Utimbuf);
-}
-__CRT_INLINE int __cdecl _wutime(const wchar_t *_Filename,struct _utimbuf *_Utimbuf) {
-  return _wutime32(_Filename,(struct __utimbuf32 *)_Utimbuf);
-}
-#else
-__CRT_INLINE int __cdecl _utime(const char *_Filename,struct _utimbuf *_Utimbuf) {
-  return _utime64(_Filename,(struct __utimbuf64 *)_Utimbuf);
-}
-__CRT_INLINE int __cdecl _futime(int _Desc,struct _utimbuf *_Utimbuf) {
-  return _futime64(_Desc,(struct __utimbuf64 *)_Utimbuf);
-}
-__CRT_INLINE int __cdecl _wutime(const wchar_t *_Filename,struct _utimbuf *_Utimbuf) {
-  return _wutime64(_Filename,(struct __utimbuf64 *)_Utimbuf);
-}
-#endif
-
-#ifndef	NO_OLDNAMES
-#ifdef _USE_32BIT_TIME_T
-__CRT_INLINE int __cdecl utime(const char *_Filename,struct utimbuf *_Utimbuf) {
-  return _utime32(_Filename,(struct __utimbuf32 *)_Utimbuf);
-}
-#else
-__CRT_INLINE int __cdecl utime(const char *_Filename,struct utimbuf *_Utimbuf) {
-  return _utime64(_Filename,(struct __utimbuf64 *)_Utimbuf);
-}
-#endif
-#endif
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#pragma pack(pop)
-#endif
diff --git a/tinyc/win32/include/tcc/tcc_libm.h b/tinyc/win32/include/tcc/tcc_libm.h
deleted file mode 100644
index 0a62e6fb7..000000000
--- a/tinyc/win32/include/tcc/tcc_libm.h
+++ /dev/null
@@ -1,201 +0,0 @@
-#ifndef _TCC_LIBM_H_
-#define _TCC_LIBM_H_
-
-#include "../math.h"
-
-/* TCC uses 8 bytes for double and long double, so effectively the l variants
- * are never used. For now, they just run the normal (double) variant.
- */
-
-/*
- * most of the code in this file is taken from MUSL rs-1.0 (MIT license)
- * - musl-libc: http://git.musl-libc.org/cgit/musl/tree/src/math?h=rs-1.0
- * - License:   http://git.musl-libc.org/cgit/musl/tree/COPYRIGHT?h=rs-1.0
- */
-
-/*******************************************************************************
-  Start of code based on MUSL
-*******************************************************************************/
-/*
-musl as a whole is licensed under the following standard MIT license:
-
-----------------------------------------------------------------------
-Copyright © 2005-2014 Rich Felker, et al.
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-----------------------------------------------------------------------
-*/
-
-/* fpclassify */
-
-__CRT_INLINE int __cdecl __fpclassify (double x) {
-  union {double f; uint64_t i;} u = {x};
-  int e = u.i>>52 & 0x7ff;
-  if (!e) return u.i<<1 ? FP_SUBNORMAL : FP_ZERO;
-  if (e==0x7ff) return u.i<<12 ? FP_NAN : FP_INFINITE;
-  return FP_NORMAL;
-}
-
-__CRT_INLINE int __cdecl __fpclassifyf (float x) {
-  union {float f; uint32_t i;} u = {x};
-  int e = u.i>>23 & 0xff;
-  if (!e) return u.i<<1 ? FP_SUBNORMAL : FP_ZERO;
-  if (e==0xff) return u.i<<9 ? FP_NAN : FP_INFINITE;
-  return FP_NORMAL;
-}
-
-__CRT_INLINE int __cdecl __fpclassifyl (long double x) {
-  return __fpclassify(x);
-}
-
-
-/* signbit */
-
-__CRT_INLINE int __cdecl __signbit (double x) {
-  union {double d; uint64_t i;} y = { x };
-  return y.i>>63;
-}
-
-__CRT_INLINE int __cdecl __signbitf (float x) {
-  union {float f; uint32_t i; } y = { x };
-  return y.i>>31;
-}
-
-__CRT_INLINE int __cdecl __signbitl (long double x) {
-  return __signbit(x);
-}
-
-
-/* fmin*, fmax* */
-
-#define TCCFP_FMIN_EVAL (isnan(x) ? y :                                      \
-                         isnan(y) ? x :                                      \
-                         (signbit(x) != signbit(y)) ? (signbit(x) ? x : y) : \
-                         x < y ? x : y)
-
-__CRT_INLINE double __cdecl fmin (double x, double y) {
-  return TCCFP_FMIN_EVAL;
-}
-
-__CRT_INLINE float __cdecl fminf (float x, float y) {
-  return TCCFP_FMIN_EVAL;
-}
-
-__CRT_INLINE long double __cdecl fminl (long double x, long double y) {
-  return TCCFP_FMIN_EVAL;
-}
-
-#define TCCFP_FMAX_EVAL (isnan(x) ? y :                                      \
-                         isnan(y) ? x :                                      \
-                         (signbit(x) != signbit(y)) ? (signbit(x) ? y : x) : \
-                         x < y ? y : x)
-
-__CRT_INLINE double __cdecl fmax (double x, double y) {
-  return TCCFP_FMAX_EVAL;
-}
-
-__CRT_INLINE float __cdecl fmaxf (float x, float y) {
-  return TCCFP_FMAX_EVAL;
-}
-
-__CRT_INLINE long double __cdecl fmaxl (long double x, long double y) {
-  return TCCFP_FMAX_EVAL;
-}
-
-
-/* *round* */
-
-#define TCCFP_FORCE_EVAL(x) do {            \
-if (sizeof(x) == sizeof(float)) {           \
-  volatile float __x;                       \
-  __x = (x);                                \
-} else if (sizeof(x) == sizeof(double)) {   \
-  volatile double __x;                      \
-  __x = (x);                                \
-} else {                                    \
-  volatile long double __x;                 \
-  __x = (x);                                \
-}                                           \
-} while(0)
-
-__CRT_INLINE double __cdecl round (double x) {
-  union {double f; uint64_t i;} u = {x};
-  int e = u.i >> 52 & 0x7ff;
-  double y;
-
-  if (e >= 0x3ff+52)
-    return x;
-  if (u.i >> 63)
-    x = -x;
-  if (e < 0x3ff-1) {
-    /* raise inexact if x!=0 */
-    TCCFP_FORCE_EVAL(x + 0x1p52);
-    return 0*u.f;
-  }
-  y = (double)(x + 0x1p52) - 0x1p52 - x;
-  if (y > 0.5)
-    y = y + x - 1;
-  else if (y <= -0.5)
-    y = y + x + 1;
-  else
-    y = y + x;
-  if (u.i >> 63)
-    y = -y;
-  return y;
-}
-
-__CRT_INLINE long __cdecl lround (double x) {
-  return round(x);
-}
-
-__CRT_INLINE long long __cdecl llround (double x) {
-  return round(x);
-}
-
-__CRT_INLINE float __cdecl roundf (float x) {
-  return round(x);
-}
-
-__CRT_INLINE long __cdecl lroundf (float x) {
-  return round(x);
-}
-
-__CRT_INLINE long long __cdecl llroundf (float x) {
-  return round(x);
-}
-
-__CRT_INLINE long double __cdecl roundl (long double x) {
-  return round(x);
-}
-
-__CRT_INLINE long __cdecl lroundl (long double x) {
-  return round(x);
-}
-
-__CRT_INLINE long long __cdecl llroundl (long double x) {
-  return round(x);
-}
-
-
-/*******************************************************************************
-  End of code based on MUSL
-*******************************************************************************/
-
-#endif /* _TCC_LIBM_H_ */
diff --git a/tinyc/win32/include/tchar.h b/tinyc/win32/include/tchar.h
deleted file mode 100644
index cd44beca1..000000000
--- a/tinyc/win32/include/tchar.h
+++ /dev/null
@@ -1,1102 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#include <_mingw.h>
-
-#ifndef _INC_TCHAR
-#define _INC_TCHAR
-
-#ifdef _STRSAFE_H_INCLUDED_
-#error Need to include strsafe.h after tchar.h
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define _ftcscat _tcscat
-#define _ftcschr _tcschr
-#define _ftcscpy _tcscpy
-#define _ftcscspn _tcscspn
-#define _ftcslen _tcslen
-#define _ftcsncat _tcsncat
-#define _ftcsncpy _tcsncpy
-#define _ftcspbrk _tcspbrk
-#define _ftcsrchr _tcsrchr
-#define _ftcsspn _tcsspn
-#define _ftcsstr _tcsstr
-#define _ftcstok _tcstok
-
-#define _ftcsdup _tcsdup
-#define _ftcsnset _tcsnset
-#define _ftcsrev _tcsrev
-#define _ftcsset _tcsset
-
-#define _ftcscmp _tcscmp
-#define _ftcsicmp _tcsicmp
-#define _ftcsnccmp _tcsnccmp
-#define _ftcsncmp _tcsncmp
-#define _ftcsncicmp _tcsncicmp
-#define _ftcsnicmp _tcsnicmp
-
-#define _ftcscoll _tcscoll
-#define _ftcsicoll _tcsicoll
-#define _ftcsnccoll _tcsnccoll
-#define _ftcsncoll _tcsncoll
-#define _ftcsncicoll _tcsncicoll
-#define _ftcsnicoll _tcsnicoll
-
-#define _ftcsclen _tcsclen
-#define _ftcsnccat _tcsnccat
-#define _ftcsnccpy _tcsnccpy
-#define _ftcsncset _tcsncset
-
-#define _ftcsdec _tcsdec
-#define _ftcsinc _tcsinc
-#define _ftcsnbcnt _tcsnbcnt
-#define _ftcsnccnt _tcsnccnt
-#define _ftcsnextc _tcsnextc
-#define _ftcsninc _tcsninc
-#define _ftcsspnp _tcsspnp
-
-#define _ftcslwr _tcslwr
-#define _ftcsupr _tcsupr
-
-#define _ftclen _tclen
-#define _ftccpy _tccpy
-#define _ftccmp _tccmp
-
-#ifndef _CONST_RETURN
-#ifdef __cplusplus
-#define _CONST_RETURN const
-#define _CRT_CONST_CORRECT_OVERLOADS
-#else
-#define _CONST_RETURN
-#endif
-#endif
-
-#define _WConst_return _CONST_RETURN
-
-#ifdef _UNICODE
-
-#ifdef __cplusplus
-}
-#endif
-
-#include <wchar.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _WCTYPE_T_DEFINED
-#define _WCTYPE_T_DEFINED
-  typedef unsigned short wint_t;
-  typedef unsigned short wctype_t;
-#endif
-
-#ifndef __TCHAR_DEFINED
-#define __TCHAR_DEFINED
-  typedef wchar_t _TCHAR;
-  typedef wchar_t _TSCHAR;
-  typedef wchar_t _TUCHAR;
-  typedef wchar_t _TXCHAR;
-  typedef wint_t _TINT;
-#endif
-
-#ifndef _TCHAR_DEFINED
-#define _TCHAR_DEFINED
-#ifndef	NO_OLDNAMES
-  typedef wchar_t TCHAR;
-#endif
-#endif
-
-#define _TEOF WEOF
-
-#define __T(x) L##x
-
-#define _tmain wmain
-#define _tWinMain wWinMain
-#define _tenviron _wenviron
-#define __targv __wargv
-
-#define _tprintf wprintf
-#define _tprintf_l _wprintf_l
-#define _tprintf_p _wprintf_p
-#define _tprintf_p_l _wprintf_p_l
-#define _tcprintf _cwprintf
-#define _tcprintf_l _cwprintf_l
-#define _tcprintf_p _cwprintf_p
-#define _tcprintf_p_l _cwprintf_p_l
-#define _vtcprintf _vcwprintf
-#define _vtcprintf_l _vcwprintf_l
-#define _vtcprintf_p _vcwprintf_p
-#define _vtcprintf_p_l _vcwprintf_p_l
-#define _ftprintf fwprintf
-#define _ftprintf_l _fwprintf_l
-#define _ftprintf_p _fwprintf_p
-#define _ftprintf_p_l _fwprintf_p_l
-#define _stprintf swprintf
-#define _stprintf_l __swprintf_l
-#define _stprintf_p _swprintf_p
-#define _stprintf_p_l _swprintf_p_l
-#define _sctprintf _scwprintf
-#define _sctprintf_l _scwprintf_l
-#define _sctprintf_p _scwprintf_p
-#define _sctprintf_p_l _scwprintf_p_l
-#define _sntprintf _snwprintf
-#define _sntprintf_l _snwprintf_l
-#define _vtprintf vwprintf
-#define _vtprintf_l _vwprintf_l
-#define _vtprintf_p _vwprintf_p
-#define _vtprintf_p_l _vwprintf_p_l
-#define _vftprintf vfwprintf
-#define _vftprintf_l _vfwprintf_l
-#define _vftprintf_p _vfwprintf_p
-#define _vftprintf_p_l _vfwprintf_p_l
-#define _vstprintf vswprintf
-#define _vstprintf_l _vswprintf_l
-#define _vstprintf_p _vswprintf_p
-#define _vstprintf_p_l _vswprintf_p_l
-#define _vsctprintf _vscwprintf
-#define _vsctprintf_l _vscwprintf_l
-#define _vsctprintf_p _vscwprintf_p
-#define _vsctprintf_p_l _vscwprintf_p_l
-#define _vsntprintf _vsnwprintf
-#define _vsntprintf_l _vsnwprintf_l
-
-#define _tscanf wscanf
-#define _tscanf_l _wscanf_l
-#define _tcscanf _cwscanf
-#define _tcscanf_l _cwscanf_l
-#define _ftscanf fwscanf
-#define _ftscanf_l _fwscanf_l
-#define _stscanf swscanf
-#define _stscanf_l _swscanf_l
-#define _sntscanf _snwscanf
-#define _sntscanf_l _snwscanf_l
-
-#define _fgettc fgetwc
-#define _fgettc_nolock _fgetwc_nolock
-#define _fgettchar _fgetwchar
-#define _fgetts fgetws
-#define _fputtc fputwc
-#define _fputtc_nolock _fputwc_nolock
-#define _fputtchar _fputwchar
-#define _fputts fputws
-#define _cputts _cputws
-#define _cgetts _cgetws
-#define _gettc getwc
-#define _gettc_nolock _getwc_nolock
-#define _gettch _getwch
-#define _gettch_nolock _getwch_nolock
-#define _gettche _getwche
-#define _gettche_nolock _getwche_nolock
-#define _gettchar getwchar
-#define _gettchar_nolock _getwchar_nolock
-#define _getts _getws
-#define _puttc putwc
-#define _puttc_nolock _putwc_nolock
-#define _puttchar putwchar
-#define _puttchar_nolock _putwchar_nolock
-#define _puttch _putwch
-#define _puttch_nolock _putwch_nolock
-#define _putts _putws
-#define _ungettc ungetwc
-#define _ungettc_nolock _ungetwc_nolock
-#define _ungettch _ungetwch
-#define _ungettch_nolock _ungetwch_nolock
-
-#define _tcstod wcstod
-#define _tcstol wcstol
-#define _tcstoul wcstoul
-#define _tcstoi64 _wcstoi64
-#define _tcstoui64 _wcstoui64
-#define _tstof _wtof
-#define _tstol _wtol
-#define _tstoi _wtoi
-#define _tstoi64 _wtoi64
-#define _tcstod_l _wcstod_l
-#define _tcstol_l _wcstol_l
-#define _tcstoul_l _wcstoul_l
-#define _tcstoi64_l _wcstoi64_l
-#define _tcstoui64_l _wcstoui64_l
-#define _tstof_l _wtof_l
-#define _tstol_l _wtol_l
-#define _tstoi_l _wtoi_l
-#define _tstoi64_l _wtoi64_l
-
-#define _itot _itow
-#define _ltot _ltow
-#define _ultot _ultow
-#define _ttoi _wtoi
-#define _ttol _wtol
-
-#define _ttoi64 _wtoi64
-#define _i64tot _i64tow
-#define _ui64tot _ui64tow
-
-#define _tcscat wcscat
-#define _tcschr wcschr
-#define _tcscpy wcscpy
-#define _tcscspn wcscspn
-#define _tcslen wcslen
-#define _tcsnlen wcsnlen
-#define _tcsncat wcsncat
-#define _tcsncat_l _wcsncat_l
-#define _tcsncpy wcsncpy
-#define _tcsncpy_l _wcsncpy_l
-#define _tcspbrk wcspbrk
-#define _tcsrchr wcsrchr
-#define _tcsspn wcsspn
-#define _tcsstr wcsstr
-#define _tcstok wcstok
-#define _tcstok_l _wcstok_l
-#define _tcserror _wcserror
-#define __tcserror __wcserror
-
-#define _tcsdup _wcsdup
-#define _tcsnset _wcsnset
-#define _tcsnset_l _wcsnset_l
-#define _tcsrev _wcsrev
-#define _tcsset _wcsset
-#define _tcsset_l _wcsset_l
-
-#define _tcscmp wcscmp
-#define _tcsicmp _wcsicmp
-#define _tcsicmp_l _wcsicmp_l
-#define _tcsnccmp wcsncmp
-#define _tcsncmp wcsncmp
-#define _tcsncicmp _wcsnicmp
-#define _tcsncicmp_l _wcsnicmp_l
-#define _tcsnicmp _wcsnicmp
-#define _tcsnicmp_l _wcsnicmp_l
-
-#define _tcscoll wcscoll
-#define _tcscoll_l _wcscoll_l
-#define _tcsicoll _wcsicoll
-#define _tcsicoll_l _wcsicoll_l
-#define _tcsnccoll _wcsncoll
-#define _tcsnccoll_l _wcsncoll_l
-#define _tcsncoll _wcsncoll
-#define _tcsncoll_l _wcsncoll_l
-#define _tcsncicoll _wcsnicoll
-#define _tcsncicoll_l _wcsnicoll_l
-#define _tcsnicoll _wcsnicoll
-#define _tcsnicoll_l _wcsnicoll_l
-
-#define _texecl _wexecl
-#define _texecle _wexecle
-#define _texeclp _wexeclp
-#define _texeclpe _wexeclpe
-#define _texecv _wexecv
-#define _texecve _wexecve
-#define _texecvp _wexecvp
-#define _texecvpe _wexecvpe
-
-#define _tspawnl _wspawnl
-#define _tspawnle _wspawnle
-#define _tspawnlp _wspawnlp
-#define _tspawnlpe _wspawnlpe
-#define _tspawnv _wspawnv
-#define _tspawnve _wspawnve
-#define _tspawnvp _wspawnvp
-#define _tspawnvp _wspawnvp
-#define _tspawnvpe _wspawnvpe
-
-#define _tsystem _wsystem
-
-#define _tasctime _wasctime
-#define _tctime _wctime
-#define _tctime32 _wctime32
-#define _tctime64 _wctime64
-#define _tstrdate _wstrdate
-#define _tstrtime _wstrtime
-#define _tutime _wutime
-#define _tutime32 _wutime32
-#define _tutime64 _wutime64
-#define _tcsftime wcsftime
-#define _tcsftime_l _wcsftime_l
-
-#define _tchdir _wchdir
-#define _tgetcwd _wgetcwd
-#define _tgetdcwd _wgetdcwd
-#define _tgetdcwd_nolock _wgetdcwd_nolock
-#define _tmkdir _wmkdir
-#define _trmdir _wrmdir
-
-#define _tfullpath _wfullpath
-#define _tgetenv _wgetenv
-#define _tmakepath _wmakepath
-#define _tpgmptr _wpgmptr
-#define _get_tpgmptr _get_wpgmptr
-#define _tputenv _wputenv
-#define _tsearchenv _wsearchenv
-#define _tsplitpath _wsplitpath
-
-#define _tfdopen _wfdopen
-#define _tfsopen _wfsopen
-#define _tfopen _wfopen
-#define _tfreopen _wfreopen
-#define _tperror _wperror
-#define _tpopen _wpopen
-#define _ttempnam _wtempnam
-#define _ttmpnam _wtmpnam
-
-#define _taccess _waccess
-#define _tchmod _wchmod
-#define _tcreat _wcreat
-#define _tfindfirst _wfindfirst
-#define _tfindfirst32 _wfindfirst32
-#define _tfindfirst64 _wfindfirst64
-#define _tfindfirsti64 _wfindfirsti64
-#define _tfindfirst32i64 _wfindfirst32i64
-#define _tfindfirst64i32 _wfindfirst64i32
-#define _tfindnext _wfindnext
-#define _tfindnext32 _wfindnext32
-#define _tfindnext64 _wfindnext64
-#define _tfindnexti64 _wfindnexti64
-#define _tfindnext32i64 _wfindnext32i64
-#define _tfindnext64i32 _wfindnext64i32
-#define _tmktemp _wmktemp
-#define _topen _wopen
-#define _tremove _wremove
-#define _trename _wrename
-#define _tsopen _wsopen
-#define _tunlink _wunlink
-
-#define _tfinddata_t _wfinddata_t
-#define _tfinddata32_t _wfinddata32_t
-#define _tfinddata64_t _wfinddata64_t
-#define _tfinddatai64_t _wfinddatai64_t
-#define _tfinddata32i64_t _wfinddata32i64_t
-#define _tfinddata64i32_t _wfinddata64i32_t
-
-#define _tstat _wstat
-#define _tstat32 _wstat32
-#define _tstat32i64 _wstat32i64
-#define _tstat64 _wstat64
-#define _tstat64i32 _wstat64i32
-#define _tstati64 _wstati64
-
-#define _tsetlocale _wsetlocale
-
-#define _tcsclen wcslen
-#define _tcscnlen wcsnlen
-#define _tcsclen_l(_String,_Locale) wcslen(_String)
-#define _tcscnlen_l(_String,_Max_count,_Locale) wcsnlen_l((_String),(_Max_count))
-#define _tcsnccat wcsncat
-#define _tcsnccat_l _wcsncat_l
-#define _tcsnccpy wcsncpy
-#define _tcsnccpy_l _wcsncpy_l
-#define _tcsncset _wcsnset
-
-#define _tcsdec _wcsdec
-#define _tcsinc _wcsinc
-#define _tcsnbcnt _wcsncnt
-#define _tcsnccnt _wcsncnt
-#define _tcsnextc _wcsnextc
-#define _tcsninc _wcsninc
-#define _tcsspnp _wcsspnp
-
-#define _tcslwr _wcslwr
-#define _tcslwr_l _wcslwr_l
-#define _tcsupr _wcsupr
-#define _tcsupr_l _wcsupr_l
-#define _tcsxfrm wcsxfrm
-#define _tcsxfrm_l _wcsxfrm_l
-
-#define _tclen(_pc) (1)
-#define _tccpy(_pc1,_cpc2) ((*(_pc1) = *(_cpc2)))
-#define _tccmp(_cpc1,_cpc2) ((*(_cpc1))-(*(_cpc2)))
-
-#define _istalnum iswalnum
-#define _istalnum_l _iswalnum_l
-#define _istalpha iswalpha
-#define _istalpha_l _iswalpha_l
-#define _istascii iswascii
-#define _istcntrl iswcntrl
-#define _istcntrl_l _iswcntrl_l
-#define _istdigit iswdigit
-#define _istdigit_l _iswdigit_l
-#define _istgraph iswgraph
-#define _istgraph_l _iswgraph_l
-#define _istlower iswlower
-#define _istlower_l _iswlower_l
-#define _istprint iswprint
-#define _istprint_l _iswprint_l
-#define _istpunct iswpunct
-#define _istpunct_l _iswpunct_l
-#define _istspace iswspace
-#define _istspace_l _iswspace_l
-#define _istupper iswupper
-#define _istupper_l _iswupper_l
-#define _istxdigit iswxdigit
-#define _istxdigit_l _iswxdigit_l
-
-#define _totupper towupper
-#define _totupper_l _towupper_l
-#define _totlower towlower
-#define _totlower_l _towlower_l
-
-#define _istlegal(_Char) (1)
-#define _istlead(_Char) (0)
-#define _istleadbyte(_Char) (0)
-#define _istleadbyte_l(_Char,_Locale) (0)
-
-#define _wcsdec(_cpc1,_cpc2) ((_cpc1)>=(_cpc2) ? NULL : (_cpc2)-1)
-#define _wcsinc(_pc) ((_pc)+1)
-#define _wcsnextc(_cpc) ((unsigned int) *(_cpc))
-#define _wcsninc(_pc,_sz) (((_pc)+(_sz)))
-  _CRTIMP size_t __cdecl __wcsncnt(const wchar_t *_Str,size_t _MaxCount);
-#define _wcsncnt(_cpc,_sz) (__wcsncnt(_cpc,_sz))
-#define _wcsspnp(_cpc1,_cpc2) (!_cpc1 ? NULL : ((*((_cpc1)+wcsspn(_cpc1,_cpc2))) ? ((_cpc1)+wcsspn(_cpc1,_cpc2)) : NULL))
-#define _wcsncpy_l(_Destination,_Source,_Count,_Locale) (wcsncpy(_Destination,_Source,_Count))
-#define _wcsncat_l(_Destination,_Source,_Count,_Locale) (wcsncat(_Destination,_Source,_Count))
-#define _wcstok_l(_String,_Delimiters,_Locale) (wcstok(_String,_Delimiters))
-#define _wcsnset_l(_Destination,_Value,_Count,_Locale) (_wcsnset(_Destination,_Value,_Count))
-#define _wcsset_l(_Destination,_Value,_Locale) (_wcsset(_Destination,_Value))
-
-  /* dirent structures and functions */
-#define _tdirent	_wdirent
-#define _TDIR 		_WDIR
-#define _topendir	_wopendir
-#define _tclosedir	_wclosedir
-#define _treaddir	_wreaddir
-#define _trewinddir	_wrewinddir
-#define _ttelldir	_wtelldir
-#define _tseekdir	_wseekdir
-
-#else
-
-#ifdef __cplusplus
-}
-#endif
-
-#include <string.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define _TEOF EOF
-
-#define __T(x) x
-
-#define _tmain main
-#define _tWinMain WinMain
-#ifdef _POSIX_
-#define _tenviron environ
-#else
-#define _tenviron _environ
-#endif
-#define __targv __argv
-
-#define _tprintf printf
-#define _tprintf_l _printf_l
-#define _tprintf_p _printf_p
-#define _tprintf_p_l _printf_p_l
-#define _tcprintf _cprintf
-#define _tcprintf_l _cprintf_l
-#define _tcprintf_p _cprintf_p
-#define _tcprintf_p_l _cprintf_p_l
-#define _vtcprintf _vcprintf
-#define _vtcprintf_l _vcprintf_l
-#define _vtcprintf_p _vcprintf_p
-#define _vtcprintf_p_l _vcprintf_p_l
-#define _ftprintf fprintf
-#define _ftprintf_l _fprintf_l
-#define _ftprintf_p _fprintf_p
-#define _ftprintf_p_l _fprintf_p_l
-#define _stprintf sprintf
-#define _stprintf_l _sprintf_l
-#define _stprintf_p _sprintf_p
-#define _stprintf_p_l _sprintf_p_l
-#define _sctprintf _scprintf
-#define _sctprintf_l _scprintf_l
-#define _sctprintf_p _scprintf_p
-#define _sctprintf_p_l _scprintf_p_l
-#define _sntprintf _snprintf
-#define _sntprintf_l _snprintf_l
-#define _vtprintf vprintf
-#define _vtprintf_l _vprintf_l
-#define _vtprintf_p _vprintf_p
-#define _vtprintf_p_l _vprintf_p_l
-#define _vftprintf vfprintf
-#define _vftprintf_l _vfprintf_l
-#define _vftprintf_p _vfprintf_p
-#define _vftprintf_p_l _vfprintf_p_l
-#define _vstprintf vsprintf
-#define _vstprintf_l _vsprintf_l
-#define _vstprintf_p _vsprintf_p
-#define _vstprintf_p_l _vsprintf_p_l
-#define _vsctprintf _vscprintf
-#define _vsctprintf_l _vscprintf_l
-#define _vsctprintf_p _vscprintf_p
-#define _vsctprintf_p_l _vscprintf_p_l
-#define _vsntprintf _vsnprintf
-#define _vsntprintf_l _vsnprintf_l
-
-#define _tscanf scanf
-#define _tscanf_l _scanf_l
-#define _tcscanf _cscanf
-#define _tcscanf_l _cscanf_l
-#define _ftscanf fscanf
-#define _ftscanf_l _fscanf_l
-#define _stscanf sscanf
-#define _stscanf_l _sscanf_l
-#define _sntscanf _snscanf
-#define _sntscanf_l _snscanf_l
-
-#define _fgettc fgetc
-#define _fgettc_nolock _fgetc_nolock
-#define _fgettchar _fgetchar
-#define _fgetts fgets
-#define _fputtc fputc
-#define _fputtc_nolock _fputc_nolock
-#define _fputtchar _fputchar
-#define _fputts fputs
-#define _cputts _cputs
-#define _gettc getc
-#define _gettc_nolock _getc_nolock
-#define _gettch _getch
-#define _gettch_nolock _getch_nolock
-#define _gettche _getche
-#define _gettche_nolock _getche_nolock
-#define _gettchar getchar
-#define _gettchar_nolock _getchar_nolock
-#define _getts gets
-#define _cgetts _cgets
-#define _puttc putc
-#define _puttc_nolock _putc_nolock
-#define _puttchar putchar
-#define _puttchar_nolock _putchar_nolock
-#define _puttch _putch
-#define _puttch_nolock _putch_nolock
-#define _putts puts
-#define _ungettc ungetc
-#define _ungettc_nolock _ungetc_nolock
-#define _ungettch _ungetch
-#define _ungettch_nolock _ungetch_nolock
-
-#define _tcstod strtod
-#define _tcstol strtol
-#define _tcstoul strtoul
-#define _tstof atof
-#define _tstol atol
-#define _tstoi atoi
-#define _tstoi64 _atoi64
-#define _tcstod_l _strtod_l
-#define _tcstol_l _strtol_l
-#define _tcstoul_l _strtoul_l
-#define _tstof_l _atof_l
-#define _tstol_l _atol_l
-#define _tstoi_l _atoi_l
-#define _tstoi64_l _atoi64_l
-
-#define _itot _itoa
-#define _ltot _ltoa
-#define _ultot _ultoa
-#define _ttoi atoi
-#define _ttol atol
-
-#define _ttoi64 _atoi64
-#define _tcstoi64 _strtoi64
-#define _tcstoi64_l _strtoi64_l
-#define _tcstoui64 _strtoui64
-#define _tcstoui64_l _strtoui64_l
-#define _i64tot _i64toa
-#define _ui64tot _ui64toa
-
-#define _tcscat strcat
-#define _tcscpy strcpy
-#define _tcsdup _strdup
-#define _tcslen strlen
-#if 0
-#define _tcsnlen strnlen
-#endif
-#define _tcsxfrm strxfrm
-#define _tcsxfrm_l _strxfrm_l
-#define _tcserror strerror
-#define __tcserror _strerror
-
-#define _texecl _execl
-#define _texecle _execle
-#define _texeclp _execlp
-#define _texeclpe _execlpe
-#define _texecv _execv
-#define _texecve _execve
-#define _texecvp _execvp
-#define _texecvpe _execvpe
-
-#define _tspawnl _spawnl
-#define _tspawnle _spawnle
-#define _tspawnlp _spawnlp
-#define _tspawnlpe _spawnlpe
-#define _tspawnv _spawnv
-#define _tspawnve _spawnve
-#define _tspawnvp _spawnvp
-#define _tspawnvpe _spawnvpe
-
-#define _tsystem system
-
-#define _tasctime asctime
-#define _tctime ctime
-#define _tctime32 _ctime32
-#define _tctime64 _ctime64
-#define _tstrdate _strdate
-#define _tstrtime _strtime
-#define _tutime _utime
-#define _tutime32 _utime32
-#define _tutime64 _utime64
-#define _tcsftime strftime
-#define _tcsftime_l _strftime_l
-
-#define _tchdir _chdir
-#define _tgetcwd _getcwd
-#define _tgetdcwd _getdcwd
-#define _tgetdcwd_nolock _getdcwd_nolock
-#define _tmkdir _mkdir
-#define _trmdir _rmdir
-
-#define _tfullpath _fullpath
-#define _tgetenv getenv
-#define _tmakepath _makepath
-#define _tpgmptr _pgmptr
-#define _get_tpgmptr _get_pgmptr
-#define _tputenv _putenv
-#define _tsearchenv _searchenv
-#define _tsplitpath _splitpath
-
-#ifdef _POSIX_
-#define _tfdopen fdopen
-#else
-#define _tfdopen _fdopen
-#endif
-#define _tfsopen _fsopen
-#define _tfopen fopen
-#define _tfreopen freopen
-#define _tperror perror
-#define _tpopen _popen
-#define _ttempnam _tempnam
-#define _ttmpnam tmpnam
-
-#define _tchmod _chmod
-#define _tcreat _creat
-#define _tfindfirst _findfirst
-#define _tfindfirst32 _findfirst32
-#define _tfindfirst64 _findfirst64
-#define _tfindfirsti64 _findfirsti64
-#define _tfindfirst32i64 _findfirst32i64
-#define _tfindfirst64i32 _findfirst64i32
-#define _tfindnext _findnext
-#define _tfindnext32 _findnext32
-#define _tfindnext64 _findnext64
-#define _tfindnexti64 _findnexti64
-#define _tfindnext32i64 _findnext32i64
-#define _tfindnext64i32 _findnext64i32
-#define _tmktemp _mktemp
-
-#ifdef _POSIX_
-#define _topen open
-#define _taccess access
-#else
-#define _topen _open
-#define _taccess _access
-#endif
-
-#define _tremove remove
-#define _trename rename
-#define _tsopen _sopen
-#define _tunlink _unlink
-
-#define _tfinddata_t _finddata_t
-#define _tfinddata32_t _finddata32_t
-#define _tfinddata64_t __finddata64_t
-#define _tfinddatai64_t _finddatai64_t
-#define _tfinddata32i64_t _finddata32i64_t
-#define _tfinddata64i32_t _finddata64i32_t
-
-#define _istascii __isascii
-#define _istcntrl iscntrl
-#define _istcntrl_l _iscntrl_l
-#define _istxdigit isxdigit
-#define _istxdigit_l _isxdigit_l
-
-#define _tstat _stat
-#define _tstat32 _stat32
-#define _tstat32i64 _stat32i64
-#define _tstat64 _stat64
-#define _tstat64i32 _stat64i32
-#define _tstati64 _stati64
-
-#define _tsetlocale setlocale
-
-#ifdef _MBCS
-
-#ifdef __cplusplus
-}
-#endif
-
-#include <mbstring.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef __TCHAR_DEFINED
-  typedef char _TCHAR;
-  typedef signed char _TSCHAR;
-  typedef unsigned char _TUCHAR;
-  typedef unsigned char _TXCHAR;
-  typedef unsigned int _TINT;
-#define __TCHAR_DEFINED
-#endif
-
-#ifndef _TCHAR_DEFINED
-#ifndef	NO_OLDNAMES
-  typedef char TCHAR;
-#endif
-#define _TCHAR_DEFINED
-#endif
-
-#ifdef _MB_MAP_DIRECT
-
-#define _tcschr _mbschr
-#define _tcscspn _mbscspn
-#define _tcsncat _mbsnbcat
-#define _tcsncat_l _mbsnbcat_l
-#define _tcsncpy _mbsnbcpy
-#define _tcsncpy_l _mbsnbcpy_l
-#define _tcspbrk _mbspbrk
-#define _tcsrchr _mbsrchr
-#define _tcsspn _mbsspn
-#define _tcsstr _mbsstr
-#define _tcstok _mbstok
-#define _tcstok_l _mbstok_l
-
-#define _tcsnset _mbsnbset
-#define _tcsnset_l _mbsnbset_l
-#define _tcsrev _mbsrev
-#define _tcsset _mbsset
-#define _tcsset_l _mbsset_l
-
-#define _tcscmp _mbscmp
-#define _tcsicmp _mbsicmp
-#define _tcsicmp_l _mbsicmp_l
-#define _tcsnccmp _mbsncmp
-#define _tcsncmp _mbsnbcmp
-#define _tcsncicmp _mbsnicmp
-#define _tcsncicmp_l _mbsnicmp_l
-#define _tcsnicmp _mbsnbicmp
-#define _tcsnicmp_l _mbsnbicmp_l
-
-#define _tcscoll _mbscoll
-#define _tcscoll_l _mbscoll_l
-#define _tcsicoll _mbsicoll
-#define _tcsicoll_l _mbsicoll_l
-#define _tcsnccoll _mbsncoll
-#define _tcsnccoll_l _mbsncoll_l
-#define _tcsncoll _mbsnbcoll
-#define _tcsncoll_l _mbsnbcoll_l
-#define _tcsncicoll _mbsnicoll
-#define _tcsncicoll_l _mbsnicoll_l
-#define _tcsnicoll _mbsnbicoll
-#define _tcsnicoll_l _mbsnbicoll_l
-
-#define _tcsclen _mbslen
-#define _tcscnlen _mbsnlen
-#define _tcsclen_l _mbslen_l
-#define _tcscnlen_l _mbsnlen_l
-#define _tcsnccat _mbsncat
-#define _tcsnccat_l _mbsncat_l
-#define _tcsnccpy _mbsncpy
-#define _tcsnccpy_l _mbsncpy_l
-#define _tcsncset _mbsnset
-#define _tcsncset_l _mbsnset_l
-
-#define _tcsdec _mbsdec
-#define _tcsinc _mbsinc
-#define _tcsnbcnt _mbsnbcnt
-#define _tcsnccnt _mbsnccnt
-#define _tcsnextc _mbsnextc
-#define _tcsninc _mbsninc
-#define _tcsspnp _mbsspnp
-
-#define _tcslwr _mbslwr
-#define _tcslwr_l _mbslwr_l
-#define _tcsupr _mbsupr
-#define _tcsupr_l _mbsupr_l
-
-#define _tclen _mbclen
-#define _tccpy _mbccpy
-#define _tccpy_l _mbccpy_l
-#else
-
-  _CRTIMP _CONST_RETURN char *__cdecl _tcschr(const char *_Str,unsigned int _Val);
-  _CRTIMP size_t __cdecl _tcscspn(const char *_Str,const char *_Control);
-  _CRTIMP char *__cdecl _tcsncat(char *_Dst,const char *_Src,size_t _MaxCount);
-  _CRTIMP char *__cdecl _tcsncat_l(char *_Dst,const char *_Src,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP char *__cdecl _tcsncpy(char *_Dst,const char *_Src,size_t _MaxCount);
-  _CRTIMP char *__cdecl _tcsncpy_l(char *_Dst,const char *_Src,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP _CONST_RETURN char *__cdecl _tcspbrk(const char *_Str,const char *_Control);
-  _CRTIMP _CONST_RETURN char *__cdecl _tcsrchr(const char *_Str,unsigned int _Ch);
-  _CRTIMP size_t __cdecl _tcsspn(const char *_Str,const char *_Control);
-  _CRTIMP _CONST_RETURN char *__cdecl _tcsstr(const char *_Str,const char *_Substr);
-  _CRTIMP char *__cdecl _tcstok(char *_Str,const char *_Delim);
-  _CRTIMP char *__cdecl _tcstok_l(char *_Str,const char *_Delim,_locale_t _Locale);
-  _CRTIMP char *__cdecl _tcsnset(char *_Str,unsigned int _Val,size_t _MaxCount);
-  _CRTIMP char *__cdecl _tcsrev(char *_Str);
-  _CRTIMP char *__cdecl _tcsset(char *_Str,unsigned int _Val);
-  _CRTIMP char *__cdecl _tcsset_l(char *_Str,unsigned int _Val,_locale_t _Locale);
-  _CRTIMP int __cdecl _tcscmp(const char *_Str1,const char *_Str);
-  _CRTIMP int __cdecl _tcsicmp(const char *_Str1,const char *_Str2);
-  _CRTIMP int __cdecl _tcsicmp_l(const char *_Str1,const char *_Str2,_locale_t _Locale);
-  _CRTIMP int __cdecl _tcsnccmp(const char *_Str1,const char *_Str2,size_t _MaxCount);
-  _CRTIMP int __cdecl _tcsncmp(const char *_Str1,const char *_Str2,size_t _MaxCount);
-  _CRTIMP int __cdecl _tcsncicmp(const char *_Str1,const char *_Str2,size_t _MaxCount);
-  _CRTIMP int __cdecl _tcsncicmp_l(const char *_Str1,const char *_Str2,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP int __cdecl _tcsnicmp(const char *_Str1,const char *_Str2,size_t _MaxCount);
-  _CRTIMP int __cdecl _tcsnicmp_l(const char *_Str1,const char *_Str2,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP int __cdecl _tcscoll(const char *_Str1,const char *_Str2);
-  _CRTIMP int __cdecl _tcscoll_l(const char *_Str1,const char *_Str2,_locale_t _Locale);
-  _CRTIMP int __cdecl _tcsicoll(const char *_Str1,const char *_Str2);
-  _CRTIMP int __cdecl _tcsicoll_l(const char *_Str1,const char *_Str2,_locale_t _Locale);
-  _CRTIMP int __cdecl _tcsnccoll(const char *_Str1,const char *_Str2,size_t _MaxCount);
-  _CRTIMP int __cdecl _tcsnccoll_l(const char *_Str1,const char *_Str2,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP int __cdecl _tcsncoll(const char *_Str1,const char *_Str2,size_t _MaxCount);
-  _CRTIMP int __cdecl _tcsncoll_l(const char *_Str1,const char *_Str2,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP int __cdecl _tcsncicoll(const char *_Str1,const char *_Str2,size_t _MaxCount);
-  _CRTIMP int __cdecl _tcsncicoll_l(const char *_Str1,const char *_Str2,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP int __cdecl _tcsnicoll(const char *_Str1,const char *_Str2,size_t _MaxCount);
-  _CRTIMP int __cdecl _tcsnicoll_l(const char *_Str1,const char *_Str2,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP size_t __cdecl _tcsclen(const char *_Str);
-  _CRTIMP size_t __cdecl _tcscnlen(const char *_Str,size_t _MaxCount);
-  _CRTIMP size_t __cdecl _tcsclen_l(const char *_Str,_locale_t _Locale);
-  _CRTIMP size_t __cdecl _tcscnlen_l(const char *_Str,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP char *__cdecl _tcsnccat(char *_Dst,const char *_Src,size_t _MaxCount);
-  _CRTIMP char *__cdecl _tcsnccat_l(char *_Dst,const char *_Src,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP char *__cdecl _tcsnccpy(char *_Dst,const char *_Src,size_t _MaxCount);
-  _CRTIMP char *__cdecl _tcsnccpy_l(char *_Dst,const char *_Src,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP char *__cdecl _tcsncset(char *_Str,unsigned int _Val,size_t _MaxCount);
-  _CRTIMP char *__cdecl _tcsdec(const char *_Start,const char *_Pos);
-  _CRTIMP char *__cdecl _tcsinc(const char *_Ptr);
-  _CRTIMP size_t __cdecl _tcsnbcnt(const char *_Str,size_t _MaxCount);
-  _CRTIMP size_t __cdecl _tcsnccnt(const char *_Str,size_t _MaxCount);
-  _CRTIMP unsigned int __cdecl _tcsnextc (const char *_Str);
-  _CRTIMP char *__cdecl _tcsninc(const char *_Ptr,size_t _Count);
-  _CRTIMP char *__cdecl _tcsspnp(const char *_Str1,const char *_Str2);
-  _CRTIMP char *__cdecl _tcslwr(char *_Str);
-  _CRTIMP char *__cdecl _tcslwr_l(char *_Str,_locale_t _Locale);
-  _CRTIMP char *__cdecl _tcsupr(char *_Str);
-  _CRTIMP char *__cdecl _tcsupr_l(char *_Str,_locale_t _Locale);
-  _CRTIMP size_t __cdecl _tclen(const char *_Str);
-  _CRTIMP void __cdecl _tccpy(char *_DstCh,const char *_SrcCh);
-
-#ifdef __cplusplus
-#ifndef _CPP_TCHAR_INLINES_DEFINED
-#define _CPP_TCHAR_INLINES_DEFINED
-  extern "C++" {
-    extern inline char *__cdecl _tcschr(char *_S,unsigned int _C) { return ((char *)_tcschr((const char *)_S,_C)); }
-    extern inline char *__cdecl _tcspbrk(char *_S,const char *_P) { return ((char *)_tcspbrk((const char *)_S,_P)); }
-    extern inline char *__cdecl _tcsrchr(char *_S,unsigned int _C) { return ((char *)_tcsrchr((const char *)_S,_C)); }
-    extern inline char *__cdecl _tcsstr(char *_S,const char *_P) { return ((char *)_tcsstr((const char *)_S,_P)); }
-  }
-#endif
-#endif
-#endif
-
-#define _tccmp(_cp1,_cp2) _tcsnccmp(_cp1,_cp2,1)
-
-#define _istalnum _ismbcalnum
-#define _istalnum_l _ismbcalnum_l
-#define _istalpha _ismbcalpha
-#define _istalpha_l _ismbcalpha_l
-#define _istdigit _ismbcdigit
-#define _istdigit_l _ismbcdigit_l
-#define _istgraph _ismbcgraph
-#define _istgraph_l _ismbcgraph_l
-#define _istlegal _ismbclegal
-#define _istlegal_l _ismbclegal_l
-#define _istlower _ismbclower
-#define _istlower_l _ismbclower_l
-#define _istprint _ismbcprint
-#define _istprint_l _ismbcprint_l
-#define _istpunct _ismbcpunct
-#define _istpunct_l _ismbcpunct_l
-#define _istspace _ismbcspace
-#define _istspace_l _ismbcspace_l
-#define _istupper _ismbcupper
-#define _istupper_l _ismbcupper_l
-
-#define _totupper _mbctoupper
-#define _totupper_l _mbctoupper_l
-#define _totlower _mbctolower
-#define _totlower_l _mbctolower_l
-
-#define _istlead _ismbblead
-#define _istleadbyte isleadbyte
-#define _istleadbyte_l _isleadbyte_l
-#else
-
-#ifndef __TCHAR_DEFINED
-#define __TCHAR_DEFINED
-  typedef char _TCHAR;
-  typedef signed char _TSCHAR;
-  typedef unsigned char _TUCHAR;
-  typedef char _TXCHAR;
-  typedef int _TINT;
-#endif
-
-#ifndef _TCHAR_DEFINED
-#define _TCHAR_DEFINED
-#ifndef	NO_OLDNAMES
-  typedef char TCHAR;
-#endif
-#endif
-
-#define _tcschr strchr
-#define _tcscspn strcspn
-#define _tcsncat strncat
-#define _tcsncat_l _strncat_l
-#define _tcsncpy strncpy
-#define _tcsncpy_l _strncpy_l
-#define _tcspbrk strpbrk
-#define _tcsrchr strrchr
-#define _tcsspn strspn
-#define _tcsstr strstr
-#define _tcstok strtok
-#define _tcstok_l _strtok_l
-
-#define _tcsnset _strnset
-#define _tcsnset_l _strnset_l
-#define _tcsrev _strrev
-#define _tcsset _strset
-
-#define _tcscmp strcmp
-#define _tcsicmp _stricmp
-#define _tcsicmp_l _stricmp_l
-#define _tcsnccmp strncmp
-#define _tcsncmp strncmp
-#define _tcsncicmp _strnicmp
-#define _tcsncicmp_l _strnicmp_l
-#define _tcsnicmp _strnicmp
-#define _tcsnicmp_l _strnicmp_l
-
-#define _tcscoll strcoll
-#define _tcscoll_l _strcoll_l
-#define _tcsicoll _stricoll
-#define _tcsicoll_l _stricoll_l
-#define _tcsnccoll _strncoll
-#define _tcsnccoll_l _strncoll_l
-#define _tcsncoll _strncoll
-#define _tcsncoll_l _strncoll_l
-#define _tcsncicoll _strnicoll
-#define _tcsncicoll_l _strnicoll_l
-#define _tcsnicoll _strnicoll
-#define _tcsnicoll_l _strnicoll_l
-
-#define _tcsclen strlen
-#define _tcscnlen strnlen
-#define _tcsclen_l(_String,_Locale) strlen(_String)
-#define _tcscnlen_l(_String,_Max_count,_Locale) strnlen_l((_String),(_Max_count))
-#define _tcsnccat strncat
-#define _tcsnccat_l _strncat_l
-#define _tcsnccpy strncpy
-#define _tcsnccpy_l _strncpy_l
-#define _tcsncset _strnset
-
-#define _tcsdec _strdec
-#define _tcsinc _strinc
-#define _tcsnbcnt _strncnt
-#define _tcsnccnt _strncnt
-#define _tcsnextc _strnextc
-#define _tcsninc _strninc
-#define _tcsspnp _strspnp
-
-#define _tcslwr _strlwr
-#define _tcslwr_l _strlwr_l
-#define _tcsupr _strupr
-#define _tcsupr_l _strupr_l
-#define _tcsxfrm strxfrm
-#define _tcsxfrm_l _strxfrm_l
-
-#define _istlead(_Char) (0)
-#define _istleadbyte(_Char) (0)
-#define _istleadbyte_l(_Char,_Locale) (0)
-
-#define _tclen(_pc) (1)
-#define _tccpy(_pc1,_cpc2) (*(_pc1) = *(_cpc2))
-#define _tccmp(_cpc1,_cpc2) (((unsigned char)*(_cpc1))-((unsigned char)*(_cpc2)))
-
-  /* dirent structures and functions */
-#define _tdirent	dirent
-#define _TDIR 		DIR
-#define _topendir	opendir
-#define _tclosedir	closedir
-#define _treaddir	readdir
-#define _trewinddir	rewinddir
-#define _ttelldir	telldir
-#define _tseekdir	seekdir
-
-#define _istalnum isalnum
-#define _istalnum_l _isalnum_l
-#define _istalpha isalpha
-#define _istalpha_l _isalpha_l
-#define _istdigit isdigit
-#define _istdigit_l _isdigit_l
-#define _istgraph isgraph
-#define _istgraph_l _isgraph_l
-#define _istlower islower
-#define _istlower_l _islower_l
-#define _istprint isprint
-#define _istprint_l _isprint_l
-#define _istpunct ispunct
-#define _istpunct_l _ispunct_l
-#define _istspace isspace
-#define _istspace_l _isspace_l
-#define _istupper isupper
-#define _istupper_l _isupper_l
-
-#define _totupper toupper
-#define _totupper_l _toupper_l
-#define _totlower tolower
-#define _totlower_l _tolower_l
-
-#define _istlegal(_c) (1)
-
-#ifndef NULL
-#ifdef __cplusplus
-#define NULL 0
-#else
-#define NULL ((void *)0)
-#endif
-#endif
-
-#define _strdec(_cpc1,_cpc2) ((_cpc1)>=(_cpc2) ? NULL : (_cpc2)-1)
-#define _strinc(_pc) ((_pc)+1)
-#define _strnextc(_cpc) ((unsigned int) *(const unsigned char *)(_cpc))
-#define _strninc(_pc,_sz) (((_pc)+(_sz)))
-  _CRTIMP size_t __cdecl __strncnt(const char *_Str,size_t _Cnt);
-#define _strncnt(_cpc,_sz) (__strncnt(_cpc,_sz))
-#define _strspnp(_cpc1,_cpc2) (!_cpc1 ? NULL : ((*((_cpc1)+strspn(_cpc1,_cpc2))) ? ((_cpc1)+strspn(_cpc1,_cpc2)) : NULL))
-
-#define _strncpy_l(_Destination,_Source,_Count,_Locale) (strncpy(_Destination,_Source,_Count))
-#define _strncat_l(_Destination,_Source,_Count,_Locale) (strncat(_Destination,_Source,_Count))
-#define _strtok_l(_String,_Delimiters,_Locale) (strtok(_String,_Delimiters))
-#define _strnset_l(_Destination,_Value,_Count,_Locale) (_strnset(_Destination,_Value,_Count))
-#define _strset_l(_Destination,_Value,_Locale) (_strset(_Destination,_Value))
-#endif
-#endif
-
-#define _T(x) __T(x)
-#define _TEXT(x) __T(x)
-
-#ifdef __cplusplus
-}
-#endif
-
-#include <sec_api/tchar_s.h>
-#endif
diff --git a/tinyc/win32/include/time.h b/tinyc/win32/include/time.h
deleted file mode 100644
index 6c72e2660..000000000
--- a/tinyc/win32/include/time.h
+++ /dev/null
@@ -1,287 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _TIME_H_
-#define _TIME_H_
-
-#include <_mingw.h>
-
-#ifndef _WIN32
-#error Only Win32 target is supported!
-#endif
-
-#pragma pack(push,_CRT_PACKING)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _CRTIMP
-#define _CRTIMP __declspec(dllimport)
-#endif
-
-#ifndef _WCHAR_T_DEFINED
-#define _WCHAR_T_DEFINED
-  typedef unsigned short wchar_t;
-#endif
-
-#ifndef _TIME32_T_DEFINED
-#define _TIME32_T_DEFINED
-  typedef long __time32_t;
-#endif
-
-#ifndef _TIME64_T_DEFINED
-#define _TIME64_T_DEFINED
-#if _INTEGRAL_MAX_BITS >= 64
-#if defined(__GNUC__) && defined(__STRICT_ANSI__)
-  typedef int _time64_t __attribute__ ((mode (DI)));
-#else
-  typedef __int64 __time64_t;
-#endif
-#endif
-#endif
-
-#ifndef _TIME_T_DEFINED
-#define _TIME_T_DEFINED
-#ifdef _USE_32BIT_TIME_T
-  typedef __time32_t time_t;
-#else
-  typedef __time64_t time_t;
-#endif
-#endif
-
-#ifndef _CLOCK_T_DEFINED
-#define _CLOCK_T_DEFINED
-  typedef long clock_t;
-#endif
-
-#ifndef _SIZE_T_DEFINED
-#define _SIZE_T_DEFINED
-#undef size_t
-#ifdef _WIN64
-#if defined(__GNUC__) && defined(__STRICT_ANSI__)
-  typedef unsigned int size_t __attribute__ ((mode (DI)));
-#else
-  typedef unsigned __int64 size_t;
-#endif
-#else
-  typedef unsigned int size_t;
-#endif
-#endif
-
-#ifndef _SSIZE_T_DEFINED
-#define _SSIZE_T_DEFINED
-#undef ssize_t
-#ifdef _WIN64
-#if defined(__GNUC__) && defined(__STRICT_ANSI__)
-  typedef int ssize_t __attribute__ ((mode (DI)));
-#else
-  typedef __int64 ssize_t;
-#endif
-#else
-  typedef int ssize_t;
-#endif
-#endif
-
-#ifndef NULL
-#ifdef __cplusplus
-#define NULL 0
-#else
-#define NULL ((void *)0)
-#endif
-#endif
-
-#ifdef _USE_32BIT_TIME_T
-#define _localtime32 localtime
-#define _difftime32     difftime
-#define _ctime32        ctime
-#define _gmtime32       gmtime
-#define _mktime32       mktime
-#define _time32 time
-#endif
-
-#ifndef _TM_DEFINED
-#define _TM_DEFINED
-  struct tm {
-    int tm_sec;
-    int tm_min;
-    int tm_hour;
-    int tm_mday;
-    int tm_mon;
-    int tm_year;
-    int tm_wday;
-    int tm_yday;
-    int tm_isdst;
-  };
-#endif
-
-#define CLOCKS_PER_SEC 1000
-
-  __MINGW_IMPORT int _daylight;
-  __MINGW_IMPORT long _dstbias;
-  __MINGW_IMPORT long _timezone;
-  __MINGW_IMPORT char * _tzname[2];
-  _CRTIMP errno_t __cdecl _get_daylight(int *_Daylight);
-  _CRTIMP errno_t __cdecl _get_dstbias(long *_Daylight_savings_bias);
-  _CRTIMP errno_t __cdecl _get_timezone(long *_Timezone);
-  _CRTIMP errno_t __cdecl _get_tzname(size_t *_ReturnValue,char *_Buffer,size_t _SizeInBytes,int _Index);
-  char *__cdecl asctime(const struct tm *_Tm);
-  _CRTIMP char *__cdecl _ctime32(const __time32_t *_Time);
-  clock_t __cdecl clock(void);
-  _CRTIMP double __cdecl _difftime32(__time32_t _Time1,__time32_t _Time2);
-  _CRTIMP struct tm *__cdecl _gmtime32(const __time32_t *_Time);
-  _CRTIMP struct tm *__cdecl _localtime32(const __time32_t *_Time);
-  size_t __cdecl strftime(char *_Buf,size_t _SizeInBytes,const char *_Format,const struct tm *_Tm);
-  _CRTIMP size_t __cdecl _strftime_l(char *_Buf,size_t _Max_size,const char *_Format,const struct tm *_Tm,_locale_t _Locale);
-  _CRTIMP char *__cdecl _strdate(char *_Buffer);
-  _CRTIMP char *__cdecl _strtime(char *_Buffer);
-  _CRTIMP __time32_t __cdecl _time32(__time32_t *_Time);
-  _CRTIMP __time32_t __cdecl _mktime32(struct tm *_Tm);
-  _CRTIMP __time32_t __cdecl _mkgmtime32(struct tm *_Tm);
-#if defined (_POSIX_) || defined(__GNUC__)
-  void __cdecl tzset(void);
-#else
-  _CRTIMP void __cdecl _tzset(void);
-#endif
-
-#if _INTEGRAL_MAX_BITS >= 64
-  double __cdecl _difftime64(__time64_t _Time1,__time64_t _Time2);
-  _CRTIMP char *__cdecl _ctime64(const __time64_t *_Time);
-  _CRTIMP struct tm *__cdecl _gmtime64(const __time64_t *_Time);
-  _CRTIMP struct tm *__cdecl _localtime64(const __time64_t *_Time);
-  _CRTIMP __time64_t __cdecl _mktime64(struct tm *_Tm);
-  _CRTIMP __time64_t __cdecl _mkgmtime64(struct tm *_Tm);
-  _CRTIMP __time64_t __cdecl _time64(__time64_t *_Time);
-#endif
-  unsigned __cdecl _getsystime(struct tm *_Tm);
-  unsigned __cdecl _setsystime(struct tm *_Tm,unsigned _MilliSec);
-
-#ifndef _SIZE_T_DEFINED
-#define _SIZE_T_DEFINED
-#ifdef _WIN64
-#if defined(__GNUC__) && defined(__STRICT_ANSI__)
-  typedef unsigned int size_t __attribute__ ((mode (DI)));
-#else
-  typedef unsigned __int64 size_t;
-#endif
-#else
-  typedef unsigned long size_t;
-#endif
-#endif
-
-#ifndef _SSIZE_T_DEFINED
-#define _SSIZE_T_DEFINED
-#ifdef _WIN64
-#if defined(__GNUC__) && defined(__STRICT_ANSI__)
-  typedef int ssize_t __attribute__ ((mode (DI)));
-#else
-  typedef __int64 ssize_t;
-#endif
-#else
-  typedef long ssize_t;
-#endif
-#endif
-
-#ifndef _WTIME_DEFINED
-  _CRTIMP wchar_t *__cdecl _wasctime(const struct tm *_Tm);
-  _CRTIMP wchar_t *__cdecl _wctime32(const __time32_t *_Time);
-  size_t __cdecl wcsftime(wchar_t *_Buf,size_t _SizeInWords,const wchar_t *_Format,const struct tm *_Tm);
-  _CRTIMP size_t __cdecl _wcsftime_l(wchar_t *_Buf,size_t _SizeInWords,const wchar_t *_Format,const struct tm *_Tm,_locale_t _Locale);
-  _CRTIMP wchar_t *__cdecl _wstrdate(wchar_t *_Buffer);
-  _CRTIMP wchar_t *__cdecl _wstrtime(wchar_t *_Buffer);
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP wchar_t *__cdecl _wctime64(const __time64_t *_Time);
-#endif
-
-#if !defined (RC_INVOKED) && !defined (_INC_WTIME_INL)
-#define _INC_WTIME_INL
-#ifdef _USE_32BIT_TIME_T
-__CRT_INLINE wchar_t *__cdecl _wctime(const time_t *_Time) { return _wctime32(_Time); }
-#else
-__CRT_INLINE wchar_t *__cdecl _wctime(const time_t *_Time) { return _wctime64(_Time); }
-#endif
-#endif
-
-#define _WTIME_DEFINED
-#endif
-
-#ifndef RC_INVOKED
-double __cdecl difftime(time_t _Time1,time_t _Time2);
-char *__cdecl ctime(const time_t *_Time);
-struct tm *__cdecl gmtime(const time_t *_Time);
-struct tm *__cdecl localtime(const time_t *_Time);
-struct tm *__cdecl localtime_r(const time_t *_Time,struct tm *);
-
-time_t __cdecl mktime(struct tm *_Tm);
-time_t __cdecl _mkgmtime(struct tm *_Tm);
-time_t __cdecl time(time_t *_Time);
-
-#ifdef _USE_32BIT_TIME_T
-#if 0
-__CRT_INLINE double __cdecl difftime(time_t _Time1,time_t _Time2) { return _difftime32(_Time1,_Time2); }
-__CRT_INLINE char *__cdecl ctime(const time_t *_Time) { return _ctime32(_Time); }
-__CRT_INLINE struct tm *__cdecl gmtime(const time_t *_Time) { return _gmtime32(_Time); }
-__CRT_INLINE struct tm *__cdecl localtime(const time_t *_Time) { return _localtime32(_Time); }
-__CRT_INLINE time_t __cdecl mktime(struct tm *_Tm) { return _mktime32(_Tm); }
-__CRT_INLINE time_t __cdecl _mkgmtime(struct tm *_Tm) { return _mkgmtime32(_Tm); }
-__CRT_INLINE time_t __cdecl time(time_t *_Time) { return _time32(_Time); }
-#endif
-#else
-__CRT_INLINE double __cdecl difftime(time_t _Time1,time_t _Time2) { return _difftime64(_Time1,_Time2); }
-__CRT_INLINE char *__cdecl ctime(const time_t *_Time) { return _ctime64(_Time); }
-__CRT_INLINE struct tm *__cdecl gmtime(const time_t *_Time) { return _gmtime64(_Time); }
-__CRT_INLINE struct tm *__cdecl localtime(const time_t *_Time) { return _localtime64(_Time); }
-__CRT_INLINE time_t __cdecl mktime(struct tm *_Tm) { return _mktime64(_Tm); }
-__CRT_INLINE time_t __cdecl _mkgmtime(struct tm *_Tm) { return _mkgmtime64(_Tm); }
-__CRT_INLINE time_t __cdecl time(time_t *_Time) { return _time64(_Time); }
-#endif
-#endif
-
-#if !defined(NO_OLDNAMES) || defined(_POSIX)
-#define CLK_TCK CLOCKS_PER_SEC
-
-  __MINGW_IMPORT int daylight;
-  __MINGW_IMPORT long dstbias;
-  __MINGW_IMPORT long timezone;
-  __MINGW_IMPORT char *tzname[2];
-  void __cdecl tzset(void);
-#endif
-
-#ifndef _TIMEVAL_DEFINED /* also in winsock[2].h */
-#define _TIMEVAL_DEFINED
-struct timeval {
-  long tv_sec;
-  long tv_usec;
-};
-#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
-#define timercmp(tvp,uvp,cmp) ((tvp)->tv_sec cmp (uvp)->tv_sec || (tvp)->tv_sec==(uvp)->tv_sec && (tvp)->tv_usec cmp (uvp)->tv_usec)
-#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0
-#endif /* _TIMEVAL_DEFINED */
-
-#ifndef __STRICT_ANSI__
-#ifndef _TIMEZONE_DEFINED /* also in sys/time.h */
-#define _TIMEZONE_DEFINED
-struct timezone {
-  int tz_minuteswest;
-  int tz_dsttime;
-};
-
-  extern int __cdecl mingw_gettimeofday (struct timeval *p, struct timezone *z);
-#endif
-#endif /* __STRICT_ANSI__ */
-
-#ifdef __cplusplus
-}
-#endif
-
-#pragma pack(pop)
-
-#include <sec_api/time_s.h>
-
-/* Adding timespec definition.  */
-#include <sys/timeb.h>
-
-#endif /* End _TIME_H_ */
-
diff --git a/tinyc/win32/include/vadefs.h b/tinyc/win32/include/vadefs.h
deleted file mode 100644
index 749b0bdde..000000000
--- a/tinyc/win32/include/vadefs.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_VADEFS
-#define _INC_VADEFS
-
-//!__TINYC__: GNUC specific stuff removed
-
-#endif
diff --git a/tinyc/win32/include/values.h b/tinyc/win32/include/values.h
deleted file mode 100644
index 1cd643cef..000000000
--- a/tinyc/win32/include/values.h
+++ /dev/null
@@ -1,4 +0,0 @@
-/*
- * TODO: Nothing here yet. Should provide UNIX compatibility constants
- * comparable to those in limits.h and float.h.
- */
diff --git a/tinyc/win32/include/wchar.h b/tinyc/win32/include/wchar.h
deleted file mode 100644
index 389196faf..000000000
--- a/tinyc/win32/include/wchar.h
+++ /dev/null
@@ -1,873 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_WCHAR
-#define _INC_WCHAR
-
-#include <_mingw.h>
-
-#pragma pack(push,_CRT_PACKING)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef WCHAR_MIN  /* also at stdint.h */
-#define WCHAR_MIN 0
-#define WCHAR_MAX ((wchar_t) -1) /* UINT16_MAX */
-#endif
-
-#ifndef __GNUC_VA_LIST
-#define __GNUC_VA_LIST
-  typedef __builtin_va_list __gnuc_va_list;
-#endif
-
-#ifndef _VA_LIST_DEFINED
-#define _VA_LIST_DEFINED
-  typedef __gnuc_va_list va_list;
-#endif
-
-#ifndef WEOF
-#define WEOF (wint_t)(0xFFFF)
-#endif
-
-#ifndef _FILE_DEFINED
-  struct _iobuf {
-    char *_ptr;
-    int _cnt;
-    char *_base;
-    int _flag;
-    int _file;
-    int _charbuf;
-    int _bufsiz;
-    char *_tmpfname;
-  };
-  typedef struct _iobuf FILE;
-#define _FILE_DEFINED
-#endif
-
-#ifndef _STDIO_DEFINED
-#ifdef _WIN64
-  _CRTIMP FILE *__cdecl __iob_func(void);
-#else
-#ifdef _MSVCRT_
-extern FILE _iob[];	/* A pointer to an array of FILE */
-#define __iob_func()	(_iob)
-#else
-extern FILE (*_imp___iob)[];	/* A pointer to an array of FILE */
-#define __iob_func()	(*_imp___iob)
-#define _iob __iob_func()
-#endif
-#endif
-
-#define _iob __iob_func()
-#endif
-
-#ifndef _STDSTREAM_DEFINED
-#define stdin (&__iob_func()[0])
-#define stdout (&__iob_func()[1])
-#define stderr (&__iob_func()[2])
-#define _STDSTREAM_DEFINED
-#endif
-
-#ifndef _FSIZE_T_DEFINED
-  typedef unsigned long _fsize_t;
-#define _FSIZE_T_DEFINED
-#endif
-
-#ifndef _WFINDDATA_T_DEFINED
-  struct _wfinddata32_t {
-    unsigned attrib;
-    __time32_t time_create;
-    __time32_t time_access;
-    __time32_t time_write;
-    _fsize_t size;
-    wchar_t name[260];
-  };
-
-/* #if _INTEGRAL_MAX_BITS >= 64 */
-
-  struct _wfinddata32i64_t {
-    unsigned attrib;
-    __time32_t time_create;
-    __time32_t time_access;
-    __time32_t time_write;
-    __int64 size;
-    wchar_t name[260];
-  };
-
-  struct _wfinddata64i32_t {
-    unsigned attrib;
-    __time64_t time_create;
-    __time64_t time_access;
-    __time64_t time_write;
-    _fsize_t size;
-    wchar_t name[260];
-  };
-
-  struct _wfinddata64_t {
-    unsigned attrib;
-    __time64_t time_create;
-    __time64_t time_access;
-    __time64_t time_write;
-    __int64 size;
-    wchar_t name[260];
-  };
-/* #endif */
-
-#ifdef _USE_32BIT_TIME_T
-#define _wfinddata_t _wfinddata32_t
-#define _wfinddatai64_t _wfinddata32i64_t
-
-#define _wfindfirst _wfindfirst32
-#define _wfindnext _wfindnext32
-#define _wfindfirsti64 _wfindfirst32i64
-#define _wfindnexti64 _wfindnext32i64
-#else
-#define _wfinddata_t _wfinddata64i32_t
-#define _wfinddatai64_t _wfinddata64_t
-
-#define _wfindfirst _wfindfirst64i32
-#define _wfindnext _wfindnext64i32
-#define _wfindfirsti64 _wfindfirst64
-#define _wfindnexti64 _wfindnext64
-#endif
-
-#define _WFINDDATA_T_DEFINED
-#endif
-
-#ifndef NULL
-#ifdef __cplusplus
-#define NULL 0
-#else
-#define NULL ((void *)0)
-#endif
-#endif
-
-#ifndef _CONST_RETURN
-#define _CONST_RETURN
-#endif
-
-#define _WConst_return _CONST_RETURN
-
-#ifndef _CRT_CTYPEDATA_DEFINED
-#define _CRT_CTYPEDATA_DEFINED
-#ifndef _CTYPE_DISABLE_MACROS
-
-#ifndef __PCTYPE_FUNC
-#define __PCTYPE_FUNC __pctype_func()
-#ifdef _MSVCRT_
-#define __pctype_func() (_pctype)
-#else
-#define __pctype_func() (*_imp___pctype)
-#endif
-#endif
-
-#ifndef _pctype
-#ifdef _MSVCRT_
-  extern unsigned short *_pctype;
-#else
-  extern unsigned short **_imp___pctype;
-#define _pctype (*_imp___pctype)
-#endif
-#endif
-#endif
-#endif
-
-#ifndef _CRT_WCTYPEDATA_DEFINED
-#define _CRT_WCTYPEDATA_DEFINED
-#ifndef _CTYPE_DISABLE_MACROS
-#ifndef _wctype
-#ifdef _MSVCRT_
-  extern unsigned short *_wctype;
-#else
-  extern unsigned short **_imp___wctype;
-#define _wctype (*_imp___wctype)
-#endif
-#endif
-
-#ifdef _MSVCRT_
-#define __pwctype_func() (_pwctype)
-#else
-#define __pwctype_func() (*_imp___pwctype)
-#endif
-
-#ifndef _pwctype
-#ifdef _MSVCRT_
-  extern unsigned short *_pwctype;
-#else
-  extern unsigned short **_imp___pwctype;
-#define _pwctype (*_imp___pwctype)
-#endif
-#endif
-
-#endif
-#endif
-
-#define _UPPER 0x1
-#define _LOWER 0x2
-#define _DIGIT 0x4
-#define _SPACE 0x8
-
-#define _PUNCT 0x10
-#define _CONTROL 0x20
-#define _BLANK 0x40
-#define _HEX 0x80
-
-#define _LEADBYTE 0x8000
-#define _ALPHA (0x0100|_UPPER|_LOWER)
-
-#ifndef _WCTYPE_DEFINED
-#define _WCTYPE_DEFINED
-
-  int __cdecl iswalpha(wint_t _C);
-  _CRTIMP int __cdecl _iswalpha_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswupper(wint_t _C);
-  _CRTIMP int __cdecl _iswupper_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswlower(wint_t _C);
-  _CRTIMP int __cdecl _iswlower_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswdigit(wint_t _C);
-  _CRTIMP int __cdecl _iswdigit_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswxdigit(wint_t _C);
-  _CRTIMP int __cdecl _iswxdigit_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswspace(wint_t _C);
-  _CRTIMP int __cdecl _iswspace_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswpunct(wint_t _C);
-  _CRTIMP int __cdecl _iswpunct_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswalnum(wint_t _C);
-  _CRTIMP int __cdecl _iswalnum_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswprint(wint_t _C);
-  _CRTIMP int __cdecl _iswprint_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswgraph(wint_t _C);
-  _CRTIMP int __cdecl _iswgraph_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswcntrl(wint_t _C);
-  _CRTIMP int __cdecl _iswcntrl_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswascii(wint_t _C);
-  int __cdecl isleadbyte(int _C);
-  _CRTIMP int __cdecl _isleadbyte_l(int _C,_locale_t _Locale);
-  wint_t __cdecl towupper(wint_t _C);
-  _CRTIMP wint_t __cdecl _towupper_l(wint_t _C,_locale_t _Locale);
-  wint_t __cdecl towlower(wint_t _C);
-  _CRTIMP wint_t __cdecl _towlower_l(wint_t _C,_locale_t _Locale);
-  int __cdecl iswctype(wint_t _C,wctype_t _Type);
-  _CRTIMP int __cdecl _iswctype_l(wint_t _C,wctype_t _Type,_locale_t _Locale);
-  _CRTIMP int __cdecl __iswcsymf(wint_t _C);
-  _CRTIMP int __cdecl _iswcsymf_l(wint_t _C,_locale_t _Locale);
-  _CRTIMP int __cdecl __iswcsym(wint_t _C);
-  _CRTIMP int __cdecl _iswcsym_l(wint_t _C,_locale_t _Locale);
-  int __cdecl is_wctype(wint_t _C,wctype_t _Type);
-#endif
-
-#ifndef _WDIRECT_DEFINED
-#define _WDIRECT_DEFINED
-
-  _CRTIMP wchar_t *__cdecl _wgetcwd(wchar_t *_DstBuf,int _SizeInWords);
-  _CRTIMP wchar_t *__cdecl _wgetdcwd(int _Drive,wchar_t *_DstBuf,int _SizeInWords);
-  wchar_t *__cdecl _wgetdcwd_nolock(int _Drive,wchar_t *_DstBuf,int _SizeInWords);
-  _CRTIMP int __cdecl _wchdir(const wchar_t *_Path);
-  _CRTIMP int __cdecl _wmkdir(const wchar_t *_Path);
-  _CRTIMP int __cdecl _wrmdir(const wchar_t *_Path);
-#endif
-
-#ifndef _WIO_DEFINED
-#define _WIO_DEFINED
-
-  _CRTIMP int __cdecl _waccess(const wchar_t *_Filename,int _AccessMode);
-  _CRTIMP int __cdecl _wchmod(const wchar_t *_Filename,int _Mode);
-  _CRTIMP int __cdecl _wcreat(const wchar_t *_Filename,int _PermissionMode);
-  _CRTIMP intptr_t __cdecl _wfindfirst32(const wchar_t *_Filename,struct _wfinddata32_t *_FindData);
-  _CRTIMP int __cdecl _wfindnext32(intptr_t _FindHandle,struct _wfinddata32_t *_FindData);
-  _CRTIMP int __cdecl _wunlink(const wchar_t *_Filename);
-  _CRTIMP int __cdecl _wrename(const wchar_t *_NewFilename,const wchar_t *_OldFilename);
-  _CRTIMP wchar_t *__cdecl _wmktemp(wchar_t *_TemplateName);
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP intptr_t __cdecl _wfindfirst32i64(const wchar_t *_Filename,struct _wfinddata32i64_t *_FindData);
-  intptr_t __cdecl _wfindfirst64i32(const wchar_t *_Filename,struct _wfinddata64i32_t *_FindData);
-  _CRTIMP intptr_t __cdecl _wfindfirst64(const wchar_t *_Filename,struct _wfinddata64_t *_FindData);
-  _CRTIMP int __cdecl _wfindnext32i64(intptr_t _FindHandle,struct _wfinddata32i64_t *_FindData);
-  int __cdecl _wfindnext64i32(intptr_t _FindHandle,struct _wfinddata64i32_t *_FindData);
-  _CRTIMP int __cdecl _wfindnext64(intptr_t _FindHandle,struct _wfinddata64_t *_FindData);
-#endif
-  _CRTIMP errno_t __cdecl _wsopen_s(int *_FileHandle,const wchar_t *_Filename,int _OpenFlag,int _ShareFlag,int _PermissionFlag);
-#if !defined(__cplusplus) || !(defined(_X86_) && !defined(__x86_64))
-  _CRTIMP int __cdecl _wopen(const wchar_t *_Filename,int _OpenFlag,...);
-  _CRTIMP int __cdecl _wsopen(const wchar_t *_Filename,int _OpenFlag,int _ShareFlag,...);
-#else
-  extern "C++" _CRTIMP int __cdecl _wopen(const wchar_t *_Filename,int _OpenFlag,int _PermissionMode = 0);
-  extern "C++" _CRTIMP int __cdecl _wsopen(const wchar_t *_Filename,int _OpenFlag,int _ShareFlag,int _PermissionMode = 0);
-#endif
-#endif
-
-#ifndef _WLOCALE_DEFINED
-#define _WLOCALE_DEFINED
-  _CRTIMP wchar_t *__cdecl _wsetlocale(int _Category,const wchar_t *_Locale);
-#endif
-
-#ifndef _WPROCESS_DEFINED
-#define _WPROCESS_DEFINED
-
-  _CRTIMP intptr_t __cdecl _wexecl(const wchar_t *_Filename,const wchar_t *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _wexecle(const wchar_t *_Filename,const wchar_t *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _wexeclp(const wchar_t *_Filename,const wchar_t *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _wexeclpe(const wchar_t *_Filename,const wchar_t *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _wexecv(const wchar_t *_Filename,const wchar_t *const *_ArgList);
-  _CRTIMP intptr_t __cdecl _wexecve(const wchar_t *_Filename,const wchar_t *const *_ArgList,const wchar_t *const *_Env);
-  _CRTIMP intptr_t __cdecl _wexecvp(const wchar_t *_Filename,const wchar_t *const *_ArgList);
-  _CRTIMP intptr_t __cdecl _wexecvpe(const wchar_t *_Filename,const wchar_t *const *_ArgList,const wchar_t *const *_Env);
-  _CRTIMP intptr_t __cdecl _wspawnl(int _Mode,const wchar_t *_Filename,const wchar_t *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _wspawnle(int _Mode,const wchar_t *_Filename,const wchar_t *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _wspawnlp(int _Mode,const wchar_t *_Filename,const wchar_t *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _wspawnlpe(int _Mode,const wchar_t *_Filename,const wchar_t *_ArgList,...);
-  _CRTIMP intptr_t __cdecl _wspawnv(int _Mode,const wchar_t *_Filename,const wchar_t *const *_ArgList);
-  _CRTIMP intptr_t __cdecl _wspawnve(int _Mode,const wchar_t *_Filename,const wchar_t *const *_ArgList,const wchar_t *const *_Env);
-  _CRTIMP intptr_t __cdecl _wspawnvp(int _Mode,const wchar_t *_Filename,const wchar_t *const *_ArgList);
-  _CRTIMP intptr_t __cdecl _wspawnvpe(int _Mode,const wchar_t *_Filename,const wchar_t *const *_ArgList,const wchar_t *const *_Env);
-#ifndef _CRT_WSYSTEM_DEFINED
-#define _CRT_WSYSTEM_DEFINED
-  _CRTIMP int __cdecl _wsystem(const wchar_t *_Command);
-#endif
-#endif
-
-#ifndef _WCTYPE_INLINE_DEFINED
-#undef _CRT_WCTYPE_NOINLINE
-#if !defined(__cplusplus) || defined(_CRT_WCTYPE_NOINLINE)
-#define iswalpha(_c) (iswctype(_c,_ALPHA))
-#define iswupper(_c) (iswctype(_c,_UPPER))
-#define iswlower(_c) (iswctype(_c,_LOWER))
-#define iswdigit(_c) (iswctype(_c,_DIGIT))
-#define iswxdigit(_c) (iswctype(_c,_HEX))
-#define iswspace(_c) (iswctype(_c,_SPACE))
-#define iswpunct(_c) (iswctype(_c,_PUNCT))
-#define iswalnum(_c) (iswctype(_c,_ALPHA|_DIGIT))
-#define iswprint(_c) (iswctype(_c,_BLANK|_PUNCT|_ALPHA|_DIGIT))
-#define iswgraph(_c) (iswctype(_c,_PUNCT|_ALPHA|_DIGIT))
-#define iswcntrl(_c) (iswctype(_c,_CONTROL))
-#define iswascii(_c) ((unsigned)(_c) < 0x80)
-
-#define _iswalpha_l(_c,_p) (_iswctype_l(_c,_ALPHA,_p))
-#define _iswupper_l(_c,_p) (_iswctype_l(_c,_UPPER,_p))
-#define _iswlower_l(_c,_p) (_iswctype_l(_c,_LOWER,_p))
-#define _iswdigit_l(_c,_p) (_iswctype_l(_c,_DIGIT,_p))
-#define _iswxdigit_l(_c,_p) (_iswctype_l(_c,_HEX,_p))
-#define _iswspace_l(_c,_p) (_iswctype_l(_c,_SPACE,_p))
-#define _iswpunct_l(_c,_p) (_iswctype_l(_c,_PUNCT,_p))
-#define _iswalnum_l(_c,_p) (_iswctype_l(_c,_ALPHA|_DIGIT,_p))
-#define _iswprint_l(_c,_p) (_iswctype_l(_c,_BLANK|_PUNCT|_ALPHA|_DIGIT,_p))
-#define _iswgraph_l(_c,_p) (_iswctype_l(_c,_PUNCT|_ALPHA|_DIGIT,_p))
-#define _iswcntrl_l(_c,_p) (_iswctype_l(_c,_CONTROL,_p))
-#ifndef _CTYPE_DISABLE_MACROS
-#define isleadbyte(_c) (__PCTYPE_FUNC[(unsigned char)(_c)] & _LEADBYTE)
-#endif
-#endif
-#define _WCTYPE_INLINE_DEFINED
-#endif
-
-#if !defined(_POSIX_) || defined(__GNUC__)
-#ifndef _INO_T_DEFINED
-#define _INO_T_DEFINED
-  typedef unsigned short _ino_t;
-#ifndef	NO_OLDNAMES
-  typedef unsigned short ino_t;
-#endif
-#endif
-
-#ifndef _DEV_T_DEFINED
-#define _DEV_T_DEFINED
-  typedef unsigned int _dev_t;
-#ifndef	NO_OLDNAMES
-  typedef unsigned int dev_t;
-#endif
-#endif
-
-#ifndef _OFF_T_DEFINED
-#define _OFF_T_DEFINED
-#ifndef _OFF_T_
-#define _OFF_T_
-  typedef long _off_t;
-#if !defined(NO_OLDNAMES) || defined(_POSIX)
-  typedef long off_t;
-#endif
-#endif
-#endif
-
-#ifndef _OFF64_T_DEFINED
-#define _OFF64_T_DEFINED
-  typedef long long _off64_t;
-#if !defined(NO_OLDNAMES) || defined(_POSIX)
-  typedef long long off64_t;
-#endif
-#endif
-
-#ifndef _STAT_DEFINED
-#define _STAT_DEFINED
-
-#ifdef _USE_32BIT_TIME_T
-#ifdef WIN64
-#define _fstat _fstat32
-#define _stat _stat32
-#define _wstat _wstat32
-#else
-#define _fstat32 _fstat
-#define _stat32 _stat
-#define _wstat32 _wstat
-#endif
-#define _fstati64 _fstat32i64
-#define _stati64 _stat32i64
-#define _wstati64 _wstat32i64
-#else
-#define _fstat _fstat64i32
-#define _fstati64 _fstat64
-#define _stat _stat64i32
-#define _stati64 _stat64
-#define _wstat _wstat64i32
-#define _wstati64 _wstat64
-#endif
-
-  struct _stat32 {
-    _dev_t st_dev;
-    _ino_t st_ino;
-    unsigned short st_mode;
-    short st_nlink;
-    short st_uid;
-    short st_gid;
-    _dev_t st_rdev;
-    _off_t st_size;
-    __time32_t st_atime;
-    __time32_t st_mtime;
-    __time32_t st_ctime;
-  };
-
-#ifndef	NO_OLDNAMES
-  struct stat {
-    _dev_t st_dev;
-    _ino_t st_ino;
-    unsigned short st_mode;
-    short st_nlink;
-    short st_uid;
-    short st_gid;
-    _dev_t st_rdev;
-    _off_t st_size;
-    time_t st_atime;
-    time_t st_mtime;
-    time_t st_ctime;
-  };
-#endif
-
-#if _INTEGRAL_MAX_BITS >= 64
-
-  struct _stat32i64 {
-    _dev_t st_dev;
-    _ino_t st_ino;
-    unsigned short st_mode;
-    short st_nlink;
-    short st_uid;
-    short st_gid;
-    _dev_t st_rdev;
-    __int64 st_size;
-    __time32_t st_atime;
-    __time32_t st_mtime;
-    __time32_t st_ctime;
-  };
-
-  struct _stat64i32 {
-    _dev_t st_dev;
-    _ino_t st_ino;
-    unsigned short st_mode;
-    short st_nlink;
-    short st_uid;
-    short st_gid;
-    _dev_t st_rdev;
-    _off_t st_size;
-    __time64_t st_atime;
-    __time64_t st_mtime;
-    __time64_t st_ctime;
-  };
-
-  struct _stat64 {
-    _dev_t st_dev;
-    _ino_t st_ino;
-    unsigned short st_mode;
-    short st_nlink;
-    short st_uid;
-    short st_gid;
-    _dev_t st_rdev;
-    __int64 st_size;
-    __time64_t st_atime;
-    __time64_t st_mtime;
-    __time64_t st_ctime;
-  };
-#endif
-
-#define __stat64 _stat64
-
-#endif
-
-#ifndef _WSTAT_DEFINED
-#define _WSTAT_DEFINED
-
-  _CRTIMP int __cdecl _wstat32(const wchar_t *_Name,struct _stat32 *_Stat);
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP int __cdecl _wstat32i64(const wchar_t *_Name,struct _stat32i64 *_Stat);
-  int __cdecl _wstat64i32(const wchar_t *_Name,struct _stat64i32 *_Stat);
-  _CRTIMP int __cdecl _wstat64(const wchar_t *_Name,struct _stat64 *_Stat);
-#endif
-#endif
-#endif
-
-#ifndef _WCONIO_DEFINED
-#define _WCONIO_DEFINED
-
-#ifndef WEOF
-#define WEOF (wint_t)(0xFFFF)
-#endif
-
-  _CRTIMP wchar_t *_cgetws(wchar_t *_Buffer);
-  _CRTIMP wint_t __cdecl _getwch(void);
-  _CRTIMP wint_t __cdecl _getwche(void);
-  _CRTIMP wint_t __cdecl _putwch(wchar_t _WCh);
-  _CRTIMP wint_t __cdecl _ungetwch(wint_t _WCh);
-  _CRTIMP int __cdecl _cputws(const wchar_t *_String);
-  _CRTIMP int __cdecl _cwprintf(const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _cwscanf(const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _cwscanf_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vcwprintf(const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _cwprintf_p(const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _vcwprintf_p(const wchar_t *_Format,va_list _ArgList);
-
-  _CRTIMP int __cdecl _cwprintf_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vcwprintf_l(const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _cwprintf_p_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vcwprintf_p_l(const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  wint_t __cdecl _putwch_nolock(wchar_t _WCh);
-  wint_t __cdecl _getwch_nolock(void);
-  wint_t __cdecl _getwche_nolock(void);
-  wint_t __cdecl _ungetwch_nolock(wint_t _WCh);
-#endif
-
-#ifndef _WSTDIO_DEFINED
-#define _WSTDIO_DEFINED
-
-#ifndef WEOF
-#define WEOF (wint_t)(0xFFFF)
-#endif
-
-#ifdef _POSIX_
-  _CRTIMP FILE *__cdecl _wfsopen(const wchar_t *_Filename,const wchar_t *_Mode);
-#else
-  _CRTIMP FILE *__cdecl _wfsopen(const wchar_t *_Filename,const wchar_t *_Mode,int _ShFlag);
-#endif
-
-  wint_t __cdecl fgetwc(FILE *_File);
-  _CRTIMP wint_t __cdecl _fgetwchar(void);
-  wint_t __cdecl fputwc(wchar_t _Ch,FILE *_File);
-  _CRTIMP wint_t __cdecl _fputwchar(wchar_t _Ch);
-  wint_t __cdecl getwc(FILE *_File);
-  wint_t __cdecl getwchar(void);
-  wint_t __cdecl putwc(wchar_t _Ch,FILE *_File);
-  wint_t __cdecl putwchar(wchar_t _Ch);
-  wint_t __cdecl ungetwc(wint_t _Ch,FILE *_File);
-  wchar_t *__cdecl fgetws(wchar_t *_Dst,int _SizeInWords,FILE *_File);
-  int __cdecl fputws(const wchar_t *_Str,FILE *_File);
-  _CRTIMP wchar_t *__cdecl _getws(wchar_t *_String);
-  _CRTIMP int __cdecl _putws(const wchar_t *_Str);
-  int __cdecl fwprintf(FILE *_File,const wchar_t *_Format,...);
-  int __cdecl wprintf(const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _scwprintf(const wchar_t *_Format,...);
-  int __cdecl vfwprintf(FILE *_File,const wchar_t *_Format,va_list _ArgList);
-  int __cdecl vwprintf(const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl swprintf(wchar_t*, const wchar_t*, ...);
-  _CRTIMP int __cdecl vswprintf(wchar_t*, const wchar_t*,va_list);
-  _CRTIMP int __cdecl _swprintf_c(wchar_t *_DstBuf,size_t _SizeInWords,const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _vswprintf_c(wchar_t *_DstBuf,size_t _SizeInWords,const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _snwprintf(wchar_t *_Dest,size_t _Count,const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _vsnwprintf(wchar_t *_Dest,size_t _Count,const wchar_t *_Format,va_list _Args);
-#ifndef __NO_ISOCEXT  /* externs in libmingwex.a */
-  int __cdecl snwprintf (wchar_t *s, size_t n, const wchar_t * format, ...);
-  __CRT_INLINE int __cdecl vsnwprintf (wchar_t *s, size_t n, const wchar_t *format, va_list arg) { return _vsnwprintf(s,n,format,arg); }
-  int __cdecl vwscanf (const wchar_t *, va_list);
-  int __cdecl vfwscanf (FILE *,const wchar_t *,va_list);
-  int __cdecl vswscanf (const wchar_t *,const wchar_t *,va_list);
-#endif
-  _CRTIMP int __cdecl _fwprintf_p(FILE *_File,const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _wprintf_p(const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _vfwprintf_p(FILE *_File,const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _vwprintf_p(const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _swprintf_p(wchar_t *_DstBuf,size_t _MaxCount,const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _vswprintf_p(wchar_t *_DstBuf,size_t _MaxCount,const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _scwprintf_p(const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _vscwprintf_p(const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _wprintf_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _wprintf_p_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vwprintf_l(const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _vwprintf_p_l(const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _fwprintf_l(FILE *_File,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _fwprintf_p_l(FILE *_File,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vfwprintf_l(FILE *_File,const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _vfwprintf_p_l(FILE *_File,const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _swprintf_c_l(wchar_t *_DstBuf,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _swprintf_p_l(wchar_t *_DstBuf,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vswprintf_c_l(wchar_t *_DstBuf,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _vswprintf_p_l(wchar_t *_DstBuf,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _scwprintf_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _scwprintf_p_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vscwprintf_p_l(const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _snwprintf_l(wchar_t *_DstBuf,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _vsnwprintf_l(wchar_t *_DstBuf,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  _CRTIMP int __cdecl _swprintf(wchar_t *_Dest,const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _vswprintf(wchar_t *_Dest,const wchar_t *_Format,va_list _Args);
-  _CRTIMP int __cdecl __swprintf_l(wchar_t *_Dest,const wchar_t *_Format,_locale_t _Plocinfo,...);
-  _CRTIMP int __cdecl __vswprintf_l(wchar_t *_Dest,const wchar_t *_Format,_locale_t _Plocinfo,va_list _Args);
-#ifndef RC_INVOKED
-#include <vadefs.h>
-#endif
-
-#ifdef _CRT_NON_CONFORMING_SWPRINTFS
-#ifndef __cplusplus
-#define swprintf _swprintf
-#define vswprintf _vswprintf
-#define _swprintf_l __swprintf_l
-#define _vswprintf_l __vswprintf_l
-#endif
-#endif
-
-  _CRTIMP wchar_t *__cdecl _wtempnam(const wchar_t *_Directory,const wchar_t *_FilePrefix);
-  _CRTIMP int __cdecl _vscwprintf(const wchar_t *_Format,va_list _ArgList);
-  _CRTIMP int __cdecl _vscwprintf_l(const wchar_t *_Format,_locale_t _Locale,va_list _ArgList);
-  int __cdecl fwscanf(FILE *_File,const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _fwscanf_l(FILE *_File,const wchar_t *_Format,_locale_t _Locale,...);
-  int __cdecl swscanf(const wchar_t *_Src,const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _swscanf_l(const wchar_t *_Src,const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP int __cdecl _snwscanf(const wchar_t *_Src,size_t _MaxCount,const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _snwscanf_l(const wchar_t *_Src,size_t _MaxCount,const wchar_t *_Format,_locale_t _Locale,...);
-  int __cdecl wscanf(const wchar_t *_Format,...);
-  _CRTIMP int __cdecl _wscanf_l(const wchar_t *_Format,_locale_t _Locale,...);
-  _CRTIMP FILE *__cdecl _wfdopen(int _FileHandle ,const wchar_t *_Mode);
-  _CRTIMP FILE *__cdecl _wfopen(const wchar_t *_Filename,const wchar_t *_Mode);
-  _CRTIMP FILE *__cdecl _wfreopen(const wchar_t *_Filename,const wchar_t *_Mode,FILE *_OldFile);
-
-#ifndef _CRT_WPERROR_DEFINED
-#define _CRT_WPERROR_DEFINED
-  _CRTIMP void __cdecl _wperror(const wchar_t *_ErrMsg);
-#endif
-  _CRTIMP FILE *__cdecl _wpopen(const wchar_t *_Command,const wchar_t *_Mode);
-#if !defined(NO_OLDNAMES) && !defined(wpopen)
-#define wpopen	_wpopen
-#endif
-  _CRTIMP int __cdecl _wremove(const wchar_t *_Filename);
-  _CRTIMP wchar_t *__cdecl _wtmpnam(wchar_t *_Buffer);
-  _CRTIMP wint_t __cdecl _fgetwc_nolock(FILE *_File);
-  _CRTIMP wint_t __cdecl _fputwc_nolock(wchar_t _Ch,FILE *_File);
-  _CRTIMP wint_t __cdecl _ungetwc_nolock(wint_t _Ch,FILE *_File);
-
-#undef _CRT_GETPUTWCHAR_NOINLINE
-
-#if !defined(__cplusplus) || defined(_CRT_GETPUTWCHAR_NOINLINE)
-#define getwchar() fgetwc(stdin)
-#define putwchar(_c) fputwc((_c),stdout)
-#else
-  __CRT_INLINE wint_t __cdecl getwchar() {return (fgetwc(stdin)); }
-  __CRT_INLINE wint_t __cdecl putwchar(wchar_t _C) {return (fputwc(_C,stdout)); }
-#endif
-
-#define getwc(_stm) fgetwc(_stm)
-#define putwc(_c,_stm) fputwc(_c,_stm)
-#define _putwc_nolock(_c,_stm) _fputwc_nolock(_c,_stm)
-#define _getwc_nolock(_c) _fgetwc_nolock(_c)
-#endif
-
-#ifndef _WSTDLIB_DEFINED
-#define _WSTDLIB_DEFINED
-
-  _CRTIMP wchar_t *__cdecl _itow(int _Value,wchar_t *_Dest,int _Radix);
-  _CRTIMP wchar_t *__cdecl _ltow(long _Value,wchar_t *_Dest,int _Radix);
-  _CRTIMP wchar_t *__cdecl _ultow(unsigned long _Value,wchar_t *_Dest,int _Radix);
-  double __cdecl wcstod(const wchar_t *_Str,wchar_t **_EndPtr);
-  _CRTIMP double __cdecl _wcstod_l(const wchar_t *_Str,wchar_t **_EndPtr,_locale_t _Locale);
-  float __cdecl wcstof( const wchar_t *nptr, wchar_t **endptr);
-#if !defined __NO_ISOCEXT /* in libmingwex.a */
-  float __cdecl wcstof (const wchar_t * __restrict__, wchar_t ** __restrict__);
-  long double __cdecl wcstold (const wchar_t * __restrict__, wchar_t ** __restrict__);
-#endif /* __NO_ISOCEXT */
-  long __cdecl wcstol(const wchar_t *_Str,wchar_t **_EndPtr,int _Radix);
-  _CRTIMP long __cdecl _wcstol_l(const wchar_t *_Str,wchar_t **_EndPtr,int _Radix,_locale_t _Locale);
-  unsigned long __cdecl wcstoul(const wchar_t *_Str,wchar_t **_EndPtr,int _Radix);
-  _CRTIMP unsigned long __cdecl _wcstoul_l(const wchar_t *_Str,wchar_t **_EndPtr,int _Radix,_locale_t _Locale);
-  _CRTIMP wchar_t *__cdecl _wgetenv(const wchar_t *_VarName);
-#ifndef _CRT_WSYSTEM_DEFINED
-#define _CRT_WSYSTEM_DEFINED
-  _CRTIMP int __cdecl _wsystem(const wchar_t *_Command);
-#endif
-  _CRTIMP double __cdecl _wtof(const wchar_t *_Str);
-  _CRTIMP double __cdecl _wtof_l(const wchar_t *_Str,_locale_t _Locale);
-  _CRTIMP int __cdecl _wtoi(const wchar_t *_Str);
-  _CRTIMP int __cdecl _wtoi_l(const wchar_t *_Str,_locale_t _Locale);
-  _CRTIMP long __cdecl _wtol(const wchar_t *_Str);
-  _CRTIMP long __cdecl _wtol_l(const wchar_t *_Str,_locale_t _Locale);
-
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP wchar_t *__cdecl _i64tow(__int64 _Val,wchar_t *_DstBuf,int _Radix);
-  _CRTIMP wchar_t *__cdecl _ui64tow(unsigned __int64 _Val,wchar_t *_DstBuf,int _Radix);
-  _CRTIMP __int64 __cdecl _wtoi64(const wchar_t *_Str);
-  _CRTIMP __int64 __cdecl _wtoi64_l(const wchar_t *_Str,_locale_t _Locale);
-  _CRTIMP __int64 __cdecl _wcstoi64(const wchar_t *_Str,wchar_t **_EndPtr,int _Radix);
-  _CRTIMP __int64 __cdecl _wcstoi64_l(const wchar_t *_Str,wchar_t **_EndPtr,int _Radix,_locale_t _Locale);
-  _CRTIMP unsigned __int64 __cdecl _wcstoui64(const wchar_t *_Str,wchar_t **_EndPtr,int _Radix);
-  _CRTIMP unsigned __int64 __cdecl _wcstoui64_l(const wchar_t *_Str,wchar_t **_EndPtr,int _Radix,_locale_t _Locale);
-#endif
-#endif
-
-#ifndef _POSIX_
-#ifndef _WSTDLIBP_DEFINED
-#define _WSTDLIBP_DEFINED
-  _CRTIMP wchar_t *__cdecl _wfullpath(wchar_t *_FullPath,const wchar_t *_Path,size_t _SizeInWords);
-  _CRTIMP void __cdecl _wmakepath(wchar_t *_ResultPath,const wchar_t *_Drive,const wchar_t *_Dir,const wchar_t *_Filename,const wchar_t *_Ext);
-#ifndef _CRT_WPERROR_DEFINED
-#define _CRT_WPERROR_DEFINED
-  _CRTIMP void __cdecl _wperror(const wchar_t *_ErrMsg);
-#endif
-  _CRTIMP int __cdecl _wputenv(const wchar_t *_EnvString);
-  _CRTIMP void __cdecl _wsearchenv(const wchar_t *_Filename,const wchar_t *_EnvVar,wchar_t *_ResultPath);
-  _CRTIMP void __cdecl _wsplitpath(const wchar_t *_FullPath,wchar_t *_Drive,wchar_t *_Dir,wchar_t *_Filename,wchar_t *_Ext);
-#endif
-#endif
-
-#ifndef _WSTRING_DEFINED
-#define _WSTRING_DEFINED
-  _CRTIMP wchar_t *__cdecl _wcsdup(const wchar_t *_Str);
-  wchar_t *__cdecl wcscat(wchar_t *_Dest,const wchar_t *_Source);
-  _CONST_RETURN wchar_t *__cdecl wcschr(const wchar_t *_Str,wchar_t _Ch);
-  int __cdecl wcscmp(const wchar_t *_Str1,const wchar_t *_Str2);
-  wchar_t *__cdecl wcscpy(wchar_t *_Dest,const wchar_t *_Source);
-  size_t __cdecl wcscspn(const wchar_t *_Str,const wchar_t *_Control);
-  size_t __cdecl wcslen(const wchar_t *_Str);
-  size_t __cdecl wcsnlen(const wchar_t *_Src,size_t _MaxCount);
-  wchar_t *__cdecl wcsncat(wchar_t *_Dest,const wchar_t *_Source,size_t _Count);
-  int __cdecl wcsncmp(const wchar_t *_Str1,const wchar_t *_Str2,size_t _MaxCount);
-  wchar_t *__cdecl wcsncpy(wchar_t *_Dest,const wchar_t *_Source,size_t _Count);
-  _CONST_RETURN wchar_t *__cdecl wcspbrk(const wchar_t *_Str,const wchar_t *_Control);
-  _CONST_RETURN wchar_t *__cdecl wcsrchr(const wchar_t *_Str,wchar_t _Ch);
-  size_t __cdecl wcsspn(const wchar_t *_Str,const wchar_t *_Control);
-  _CONST_RETURN wchar_t *__cdecl wcsstr(const wchar_t *_Str,const wchar_t *_SubStr);
-  wchar_t *__cdecl wcstok(wchar_t *_Str,const wchar_t *_Delim);
-  _CRTIMP wchar_t *__cdecl _wcserror(int _ErrNum);
-  _CRTIMP wchar_t *__cdecl __wcserror(const wchar_t *_Str);
-  _CRTIMP int __cdecl _wcsicmp(const wchar_t *_Str1,const wchar_t *_Str2);
-  _CRTIMP int __cdecl _wcsicmp_l(const wchar_t *_Str1,const wchar_t *_Str2,_locale_t _Locale);
-  _CRTIMP int __cdecl _wcsnicmp(const wchar_t *_Str1,const wchar_t *_Str2,size_t _MaxCount);
-  _CRTIMP int __cdecl _wcsnicmp_l(const wchar_t *_Str1,const wchar_t *_Str2,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP wchar_t *__cdecl _wcsnset(wchar_t *_Str,wchar_t _Val,size_t _MaxCount);
-  _CRTIMP wchar_t *__cdecl _wcsrev(wchar_t *_Str);
-  _CRTIMP wchar_t *__cdecl _wcsset(wchar_t *_Str,wchar_t _Val);
-  _CRTIMP wchar_t *__cdecl _wcslwr(wchar_t *_String);
-  _CRTIMP wchar_t *_wcslwr_l(wchar_t *_String,_locale_t _Locale);
-  _CRTIMP wchar_t *__cdecl _wcsupr(wchar_t *_String);
-  _CRTIMP wchar_t *_wcsupr_l(wchar_t *_String,_locale_t _Locale);
-  size_t __cdecl wcsxfrm(wchar_t *_Dst,const wchar_t *_Src,size_t _MaxCount);
-  _CRTIMP size_t __cdecl _wcsxfrm_l(wchar_t *_Dst,const wchar_t *_Src,size_t _MaxCount,_locale_t _Locale);
-  int __cdecl wcscoll(const wchar_t *_Str1,const wchar_t *_Str2);
-  _CRTIMP int __cdecl _wcscoll_l(const wchar_t *_Str1,const wchar_t *_Str2,_locale_t _Locale);
-  _CRTIMP int __cdecl _wcsicoll(const wchar_t *_Str1,const wchar_t *_Str2);
-  _CRTIMP int __cdecl _wcsicoll_l(const wchar_t *_Str1,const wchar_t *_Str2,_locale_t _Locale);
-  _CRTIMP int __cdecl _wcsncoll(const wchar_t *_Str1,const wchar_t *_Str2,size_t _MaxCount);
-  _CRTIMP int __cdecl _wcsncoll_l(const wchar_t *_Str1,const wchar_t *_Str2,size_t _MaxCount,_locale_t _Locale);
-  _CRTIMP int __cdecl _wcsnicoll(const wchar_t *_Str1,const wchar_t *_Str2,size_t _MaxCount);
-  _CRTIMP int __cdecl _wcsnicoll_l(const wchar_t *_Str1,const wchar_t *_Str2,size_t _MaxCount,_locale_t _Locale);
-
-#ifndef	NO_OLDNAMES
-  wchar_t *__cdecl wcsdup(const wchar_t *_Str);
-#define wcswcs wcsstr
-  int __cdecl wcsicmp(const wchar_t *_Str1,const wchar_t *_Str2);
-  int __cdecl wcsnicmp(const wchar_t *_Str1,const wchar_t *_Str2,size_t _MaxCount);
-  wchar_t *__cdecl wcsnset(wchar_t *_Str,wchar_t _Val,size_t _MaxCount);
-  wchar_t *__cdecl wcsrev(wchar_t *_Str);
-  wchar_t *__cdecl wcsset(wchar_t *_Str,wchar_t _Val);
-  wchar_t *__cdecl wcslwr(wchar_t *_Str);
-  wchar_t *__cdecl wcsupr(wchar_t *_Str);
-  int __cdecl wcsicoll(const wchar_t *_Str1,const wchar_t *_Str2);
-#endif
-#endif
-
-#ifndef _TM_DEFINED
-#define _TM_DEFINED
-  struct tm {
-    int tm_sec;
-    int tm_min;
-    int tm_hour;
-    int tm_mday;
-    int tm_mon;
-    int tm_year;
-    int tm_wday;
-    int tm_yday;
-    int tm_isdst;
-  };
-#endif
-
-#ifndef _WTIME_DEFINED
-#define _WTIME_DEFINED
-
-  _CRTIMP wchar_t *__cdecl _wasctime(const struct tm *_Tm);
-  _CRTIMP wchar_t *__cdecl _wctime32(const __time32_t *_Time);
-  size_t __cdecl wcsftime(wchar_t *_Buf,size_t _SizeInWords,const wchar_t *_Format,const struct tm *_Tm);
-  _CRTIMP size_t __cdecl _wcsftime_l(wchar_t *_Buf,size_t _SizeInWords,const wchar_t *_Format,const struct tm *_Tm,_locale_t _Locale);
-  _CRTIMP wchar_t *__cdecl _wstrdate(wchar_t *_Buffer);
-  _CRTIMP wchar_t *__cdecl _wstrtime(wchar_t *_Buffer);
-#if _INTEGRAL_MAX_BITS >= 64
-  _CRTIMP wchar_t *__cdecl _wctime64(const __time64_t *_Time);
-#endif
-
-#if !defined (RC_INVOKED) && !defined (_INC_WTIME_INL)
-#define _INC_WTIME_INL
-#ifdef _USE_32BIT_TIME_T
-__CRT_INLINE wchar_t *__cdecl _wctime(const time_t *_Time) { return _wctime32(_Time); }
-#else
-__CRT_INLINE wchar_t *__cdecl _wctime(const time_t *_Time) { return _wctime64(_Time); }
-#endif
-#endif
-#endif
-
-  typedef int mbstate_t;
-  typedef wchar_t _Wint_t;
-
-  wint_t __cdecl btowc(int);
-  size_t __cdecl mbrlen(const char *_Ch,size_t _SizeInBytes,mbstate_t *_State);
-  size_t __cdecl mbrtowc(wchar_t *_DstCh,const char *_SrcCh,size_t _SizeInBytes,mbstate_t *_State);
-  size_t __cdecl mbsrtowcs(wchar_t *_Dest,const char **_PSrc,size_t _Count,mbstate_t *_State);
-  size_t __cdecl wcrtomb(char *_Dest,wchar_t _Source,mbstate_t *_State);
-  size_t __cdecl wcsrtombs(char *_Dest,const wchar_t **_PSource,size_t _Count,mbstate_t *_State);
-  int __cdecl wctob(wint_t _WCh);
-
-#ifndef __NO_ISOCEXT /* these need static lib libmingwex.a */
-  wchar_t *__cdecl wmemset(wchar_t *s, wchar_t c, size_t n);
-  _CONST_RETURN wchar_t *__cdecl wmemchr(const wchar_t *s, wchar_t c, size_t n);
-  int wmemcmp(const wchar_t *s1, const wchar_t *s2,size_t n);
-  wchar_t *__cdecl wmemcpy(wchar_t *s1,const wchar_t *s2,size_t n);
-  wchar_t *__cdecl wmemmove(wchar_t *s1, const wchar_t *s2, size_t n);
-  long long __cdecl wcstoll(const wchar_t *nptr,wchar_t **endptr, int base);
-  unsigned long long __cdecl wcstoull(const wchar_t *nptr,wchar_t **endptr, int base);
-#endif /* __NO_ISOCEXT */
-
-  void *__cdecl memmove(void *_Dst,const void *_Src,size_t _MaxCount);
-  void *__cdecl memcpy(void *_Dst,const void *_Src,size_t _MaxCount);
-  __CRT_INLINE int __cdecl fwide(FILE *_F,int _M) { (void)_F; return (_M); }
-  __CRT_INLINE int __cdecl mbsinit(const mbstate_t *_P) { return (!_P || *_P==0); }
-  __CRT_INLINE _CONST_RETURN wchar_t *__cdecl wmemchr(const wchar_t *_S,wchar_t _C,size_t _N) { for (;0<_N;++_S,--_N) if (*_S==_C) return (_CONST_RETURN wchar_t *)(_S); return (0); }
-  __CRT_INLINE int __cdecl wmemcmp(const wchar_t *_S1,const wchar_t *_S2,size_t _N) { for (; 0 < _N; ++_S1,++_S2,--_N) if (*_S1!=*_S2) return (*_S1 < *_S2 ? -1 : +1); return (0); }
-  __CRT_INLINE wchar_t *__cdecl wmemcpy(wchar_t *_S1,const wchar_t *_S2,size_t _N) { return (wchar_t *)memcpy(_S1,_S2,_N*sizeof(wchar_t)); }
-  __CRT_INLINE wchar_t *__cdecl wmemmove(wchar_t *_S1,const wchar_t *_S2,size_t _N) { return (wchar_t *)memmove(_S1,_S2,_N*sizeof(wchar_t)); }
-  __CRT_INLINE wchar_t *__cdecl wmemset(wchar_t *_S,wchar_t _C,size_t _N) {
-    wchar_t *_Su = _S;
-    for (;0<_N;++_Su,--_N) {
-      *_Su = _C;
-    }
-    return (_S);
-  }
-#ifdef __cplusplus
-}
-#endif
-
-#pragma pack(pop)
-
-#include <sec_api/wchar_s.h>
-#endif
diff --git a/tinyc/win32/include/wctype.h b/tinyc/win32/include/wctype.h
deleted file mode 100644
index a44cb384a..000000000
--- a/tinyc/win32/include/wctype.h
+++ /dev/null
@@ -1,172 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _INC_WCTYPE
-#define _INC_WCTYPE
-
-#ifndef _WIN32
-#error Only Win32 target is supported!
-#endif
-
-#include <_mingw.h>
-
-#pragma pack(push,_CRT_PACKING)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _CRTIMP
-#define _CRTIMP __declspec(dllimport)
-#endif
-
-#ifndef _WCHAR_T_DEFINED
-  typedef unsigned short wchar_t;
-#define _WCHAR_T_DEFINED
-#endif
-
-#ifndef _WCTYPE_T_DEFINED
-  typedef unsigned short wint_t;
-  typedef unsigned short wctype_t;
-#define _WCTYPE_T_DEFINED
-#endif
-
-#ifndef WEOF
-#define WEOF (wint_t)(0xFFFF)
-#endif
-
-#ifndef _CRT_CTYPEDATA_DEFINED
-#define _CRT_CTYPEDATA_DEFINED
-#ifndef _CTYPE_DISABLE_MACROS
-
-#ifndef __PCTYPE_FUNC
-#define __PCTYPE_FUNC __pctype_func()
-#ifdef _MSVCRT_
-#define __pctype_func() (_pctype)
-#else
-#define __pctype_func() (*_imp___pctype)
-#endif
-#endif
-
-#ifndef _pctype
-#ifdef _MSVCRT_
-  extern unsigned short *_pctype;
-#else
-  extern unsigned short **_imp___pctype;
-#define _pctype (*_imp___pctype)
-#endif
-#endif
-
-#endif
-#endif
-
-#ifndef _CRT_WCTYPEDATA_DEFINED
-#define _CRT_WCTYPEDATA_DEFINED
-#ifndef _CTYPE_DISABLE_MACROS
-#ifndef _wctype
-#ifdef _MSVCRT_
-  extern unsigned short *_wctype;
-#else
-  extern unsigned short **_imp___wctype;
-#define _wctype (*_imp___wctype)
-#endif
-#endif
-
-#ifndef _pwctype
-#ifdef _MSVCRT_
-  extern unsigned short *_pwctype;
-#else
-  extern unsigned short **_imp___pwctype;
-#define _pwctype (*_imp___pwctype)
-#define __pwctype_func() (*_imp___pwctype)
-#endif
-#endif
-#endif
-#endif
-
-#define _UPPER 0x1
-#define _LOWER 0x2
-#define _DIGIT 0x4
-#define _SPACE 0x8
-
-#define _PUNCT 0x10
-#define _CONTROL 0x20
-#define _BLANK 0x40
-#define _HEX 0x80
-
-#define _LEADBYTE 0x8000
-#define _ALPHA (0x0100|_UPPER|_LOWER)
-
-#ifndef _WCTYPE_DEFINED
-#define _WCTYPE_DEFINED
-
-  int __cdecl iswalpha(wint_t);
-  int __cdecl iswupper(wint_t);
-  int __cdecl iswlower(wint_t);
-  int __cdecl iswdigit(wint_t);
-  int __cdecl iswxdigit(wint_t);
-  int __cdecl iswspace(wint_t);
-  int __cdecl iswpunct(wint_t);
-  int __cdecl iswalnum(wint_t);
-  int __cdecl iswprint(wint_t);
-  int __cdecl iswgraph(wint_t);
-  int __cdecl iswcntrl(wint_t);
-  int __cdecl iswascii(wint_t);
-  int __cdecl isleadbyte(int);
-  wint_t __cdecl towupper(wint_t);
-  wint_t __cdecl towlower(wint_t);
-  int __cdecl iswctype(wint_t,wctype_t);
-  _CRTIMP int __cdecl __iswcsymf(wint_t);
-  _CRTIMP int __cdecl __iswcsym(wint_t);
-  int __cdecl is_wctype(wint_t,wctype_t);
-#if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || !defined (NO_OLDNAMES)
-int __cdecl isblank(int _C);
-#endif
-#endif
-
-#ifndef _WCTYPE_INLINE_DEFINED
-#define _WCTYPE_INLINE_DEFINED
-#ifndef __cplusplus
-#define iswalpha(_c) (iswctype(_c,_ALPHA))
-#define iswupper(_c) (iswctype(_c,_UPPER))
-#define iswlower(_c) (iswctype(_c,_LOWER))
-#define iswdigit(_c) (iswctype(_c,_DIGIT))
-#define iswxdigit(_c) (iswctype(_c,_HEX))
-#define iswspace(_c) (iswctype(_c,_SPACE))
-#define iswpunct(_c) (iswctype(_c,_PUNCT))
-#define iswalnum(_c) (iswctype(_c,_ALPHA|_DIGIT))
-#define iswprint(_c) (iswctype(_c,_BLANK|_PUNCT|_ALPHA|_DIGIT))
-#define iswgraph(_c) (iswctype(_c,_PUNCT|_ALPHA|_DIGIT))
-#define iswcntrl(_c) (iswctype(_c,_CONTROL))
-#define iswascii(_c) ((unsigned)(_c) < 0x80)
-#define isleadbyte(c) (__pctype_func()[(unsigned char)(c)] & _LEADBYTE)
-#else
-  __CRT_INLINE int __cdecl iswalpha(wint_t _C) {return (iswctype(_C,_ALPHA)); }
-  __CRT_INLINE int __cdecl iswupper(wint_t _C) {return (iswctype(_C,_UPPER)); }
-  __CRT_INLINE int __cdecl iswlower(wint_t _C) {return (iswctype(_C,_LOWER)); }
-  __CRT_INLINE int __cdecl iswdigit(wint_t _C) {return (iswctype(_C,_DIGIT)); }
-  __CRT_INLINE int __cdecl iswxdigit(wint_t _C) {return (iswctype(_C,_HEX)); }
-  __CRT_INLINE int __cdecl iswspace(wint_t _C) {return (iswctype(_C,_SPACE)); }
-  __CRT_INLINE int __cdecl iswpunct(wint_t _C) {return (iswctype(_C,_PUNCT)); }
-  __CRT_INLINE int __cdecl iswalnum(wint_t _C) {return (iswctype(_C,_ALPHA|_DIGIT)); }
-  __CRT_INLINE int __cdecl iswprint(wint_t _C) {return (iswctype(_C,_BLANK|_PUNCT|_ALPHA|_DIGIT)); }
-  __CRT_INLINE int __cdecl iswgraph(wint_t _C) {return (iswctype(_C,_PUNCT|_ALPHA|_DIGIT)); }
-  __CRT_INLINE int __cdecl iswcntrl(wint_t _C) {return (iswctype(_C,_CONTROL)); }
-  __CRT_INLINE int __cdecl iswascii(wint_t _C) {return ((unsigned)(_C) < 0x80); }
-  __CRT_INLINE int __cdecl isleadbyte(int _C) {return (__pctype_func()[(unsigned char)(_C)] & _LEADBYTE); }
-#endif
-#endif
-
-  typedef wchar_t wctrans_t;
-  wint_t __cdecl towctrans(wint_t,wctrans_t);
-  wctrans_t __cdecl wctrans(const char *);
-  wctype_t __cdecl wctype(const char *);
-
-#ifdef __cplusplus
-}
-#endif
-
-#pragma pack(pop)
-#endif
diff --git a/tinyc/win32/include/winapi/basetsd.h b/tinyc/win32/include/winapi/basetsd.h
deleted file mode 100644
index 47d78c4c3..000000000
--- a/tinyc/win32/include/winapi/basetsd.h
+++ /dev/null
@@ -1,149 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _BASETSD_H_
-#define _BASETSD_H_
-
-#if (defined(__x86_64) || defined(__ia64__)) && !defined(RC_INVOKED)
-typedef unsigned __int64 POINTER_64_INT;
-#else
-typedef unsigned long POINTER_64_INT;
-#endif
-
-#define POINTER_32
-#define POINTER_64
-#define FIRMWARE_PTR
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  typedef signed char INT8,*PINT8;
-  typedef signed short INT16,*PINT16;
-  typedef signed int INT32,*PINT32;
-  typedef signed __int64 INT64,*PINT64;
-  typedef unsigned char UINT8,*PUINT8;
-  typedef unsigned short UINT16,*PUINT16;
-  typedef unsigned int UINT32,*PUINT32;
-  typedef unsigned __int64 UINT64,*PUINT64;
-  typedef signed int LONG32,*PLONG32;
-  typedef unsigned int ULONG32,*PULONG32;
-  typedef unsigned int DWORD32,*PDWORD32;
-
-#ifndef _W64
-#define _W64
-#endif
-
-#ifdef _WIN64
-  typedef __int64 INT_PTR,*PINT_PTR;
-  typedef unsigned __int64 UINT_PTR,*PUINT_PTR;
-  typedef __int64 LONG_PTR,*PLONG_PTR;
-  typedef unsigned __int64 ULONG_PTR,*PULONG_PTR;
-#define __int3264 __int64
-#else
-  typedef int INT_PTR,*PINT_PTR;
-  typedef unsigned int UINT_PTR,*PUINT_PTR;
-  typedef long LONG_PTR,*PLONG_PTR;
-  typedef unsigned long ULONG_PTR,*PULONG_PTR;
-#define __int3264 __int32
-#endif
-
-#ifdef _WIN64
-#define ADDRESS_TAG_BIT 0x40000000000ULL
-  typedef __int64 SHANDLE_PTR;
-  typedef unsigned __int64 HANDLE_PTR;
-  typedef unsigned int UHALF_PTR,*PUHALF_PTR;
-  typedef int HALF_PTR,*PHALF_PTR;
-
-  static __inline unsigned long HandleToULong(const void *h) { return((unsigned long) (ULONG_PTR) h); }
-  static __inline long HandleToLong(const void *h) { return((long) (LONG_PTR) h); }
-  static __inline void *ULongToHandle(const unsigned long h) { return((void *) (UINT_PTR) h); }
-  static __inline void *LongToHandle(const long h) { return((void *) (INT_PTR) h); }
-  static __inline unsigned long PtrToUlong(const void *p) { return((unsigned long) (ULONG_PTR) p); }
-  static __inline unsigned int PtrToUint(const void *p) { return((unsigned int) (UINT_PTR) p); }
-  static __inline unsigned short PtrToUshort(const void *p) { return((unsigned short) (unsigned long) (ULONG_PTR) p); }
-  static __inline long PtrToLong(const void *p) { return((long) (LONG_PTR) p); }
-  static __inline int PtrToInt(const void *p) { return((int) (INT_PTR) p); }
-  static __inline short PtrToShort(const void *p) { return((short) (long) (LONG_PTR) p); }
-  static __inline void *IntToPtr(const int i) { return((void *)(INT_PTR)i); }
-  static __inline void *UIntToPtr(const unsigned int ui) { return((void *)(UINT_PTR)ui); }
-  static __inline void *LongToPtr(const long l) { return((void *)(LONG_PTR)l); }
-  static __inline void *ULongToPtr(const unsigned long ul) { return((void *)(ULONG_PTR)ul); }
-
-#define PtrToPtr64(p) ((void *) p)
-#define Ptr64ToPtr(p) ((void *) p)
-#define HandleToHandle64(h) (PtrToPtr64(h))
-#define Handle64ToHandle(h) (Ptr64ToPtr(h))
-
-  static __inline void *Ptr32ToPtr(const void *p) { return (void *)p; }
-  static __inline void *Handle32ToHandle(const void *h) { return((void *) h); }
-  static __inline void *PtrToPtr32(const void *p) { return((void *) (ULONG_PTR) p); }
-
-#define HandleToHandle32(h) (PtrToPtr32(h))
-#else
-
-#define ADDRESS_TAG_BIT 0x80000000UL
-
-  typedef unsigned short UHALF_PTR,*PUHALF_PTR;
-  typedef short HALF_PTR,*PHALF_PTR;
-  typedef long SHANDLE_PTR;
-  typedef unsigned long HANDLE_PTR;
-
-#define HandleToULong(h) ((ULONG)(ULONG_PTR)(h))
-#define HandleToLong(h) ((LONG)(LONG_PTR) (h))
-#define ULongToHandle(ul) ((HANDLE)(ULONG_PTR) (ul))
-#define LongToHandle(h) ((HANDLE)(LONG_PTR) (h))
-#define PtrToUlong(p) ((ULONG)(ULONG_PTR) (p))
-#define PtrToLong(p) ((LONG)(LONG_PTR) (p))
-#define PtrToUint(p) ((UINT)(UINT_PTR) (p))
-#define PtrToInt(p) ((INT)(INT_PTR) (p))
-#define PtrToUshort(p) ((unsigned short)(ULONG_PTR)(p))
-#define PtrToShort(p) ((short)(LONG_PTR)(p))
-#define IntToPtr(i) ((VOID *)(INT_PTR)((int)i))
-#define UIntToPtr(ui) ((VOID *)(UINT_PTR)((unsigned int)ui))
-#define LongToPtr(l) ((VOID *)(LONG_PTR)((long)l))
-#define ULongToPtr(ul) ((VOID *)(ULONG_PTR)((unsigned long)ul))
-
-  static __inline void *PtrToPtr64(const void *p) { return((void *) (ULONG_PTR)p); }
-  static __inline void *Ptr64ToPtr(const void *p) { return((void *) (ULONG_PTR) p); }
-  static __inline void *HandleToHandle64(const void *h) { return((void *) h); }
-  static __inline void *Handle64ToHandle(const void *h) { return((void *) (ULONG_PTR) h); }
-
-#define Ptr32ToPtr(p) ((void *) p)
-#define Handle32ToHandle(h) (Ptr32ToPtr(h))
-#define PtrToPtr32(p) ((void *) p)
-#define HandleToHandle32(h) (PtrToPtr32(h))
-#endif
-
-#define HandleToUlong(h) HandleToULong(h)
-#define UlongToHandle(ul) ULongToHandle(ul)
-#define UlongToPtr(ul) ULongToPtr(ul)
-#define UintToPtr(ui) UIntToPtr(ui)
-
-#define MAXUINT_PTR (~((UINT_PTR)0))
-#define MAXINT_PTR ((INT_PTR)(MAXUINT_PTR >> 1))
-#define MININT_PTR (~MAXINT_PTR)
-
-#define MAXULONG_PTR (~((ULONG_PTR)0))
-#define MAXLONG_PTR ((LONG_PTR)(MAXULONG_PTR >> 1))
-#define MINLONG_PTR (~MAXLONG_PTR)
-
-#define MAXUHALF_PTR ((UHALF_PTR)~0)
-#define MAXHALF_PTR ((HALF_PTR)(MAXUHALF_PTR >> 1))
-#define MINHALF_PTR (~MAXHALF_PTR)
-
-  typedef ULONG_PTR SIZE_T,*PSIZE_T;
-  typedef LONG_PTR SSIZE_T,*PSSIZE_T;
-  typedef ULONG_PTR DWORD_PTR,*PDWORD_PTR;
-  typedef __int64 LONG64,*PLONG64;
-  typedef unsigned __int64 ULONG64,*PULONG64;
-  typedef unsigned __int64 DWORD64,*PDWORD64;
-  typedef ULONG_PTR KAFFINITY;
-  typedef KAFFINITY *PKAFFINITY;
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/tinyc/win32/include/winapi/basetyps.h b/tinyc/win32/include/winapi/basetyps.h
deleted file mode 100644
index 376665e79..000000000
--- a/tinyc/win32/include/winapi/basetyps.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#if !defined(_BASETYPS_H_)
-#define _BASETYPS_H_
-
-#ifdef __cplusplus
-#define EXTERN_C extern "C"
-#else
-#define EXTERN_C extern
-#endif
-
-#define STDMETHODCALLTYPE WINAPI
-#define STDMETHODVCALLTYPE __cdecl
-
-#define STDAPICALLTYPE WINAPI
-#define STDAPIVCALLTYPE __cdecl
-
-#define STDAPI EXTERN_C HRESULT WINAPI
-#define STDAPI_(type) EXTERN_C type WINAPI
-
-#define STDMETHODIMP HRESULT WINAPI
-#define STDMETHODIMP_(type) type WINAPI
-
-#define STDAPIV EXTERN_C HRESULT STDAPIVCALLTYPE
-#define STDAPIV_(type) EXTERN_C type STDAPIVCALLTYPE
-
-#define STDMETHODIMPV HRESULT STDMETHODVCALLTYPE
-#define STDMETHODIMPV_(type) type STDMETHODVCALLTYPE
-
-#if defined(__cplusplus) && !defined(CINTERFACE)
-
-#define __STRUCT__ struct
-#define STDMETHOD(method) virtual HRESULT WINAPI method
-#define STDMETHOD_(type,method) virtual type WINAPI method
-#define STDMETHODV(method) virtual HRESULT STDMETHODVCALLTYPE method
-#define STDMETHODV_(type,method) virtual type STDMETHODVCALLTYPE method
-#define PURE = 0
-#define THIS_
-#define THIS void
-#define DECLARE_INTERFACE(iface) __STRUCT__ iface
-#define DECLARE_INTERFACE_(iface,baseiface) __STRUCT__ iface : public baseiface
-#else
-
-#ifndef __OBJC__
-#define interface struct
-#endif
-
-#define STDMETHOD(method) HRESULT (WINAPI *method)
-#define STDMETHOD_(type,method) type (WINAPI *method)
-#define STDMETHODV(method) HRESULT (STDMETHODVCALLTYPE *method)
-#define STDMETHODV_(type,method) type (STDMETHODVCALLTYPE *method)
-
-#define PURE
-#define THIS_ INTERFACE *This,
-#define THIS INTERFACE *This
-#ifdef CONST_VTABLE
-#define DECLARE_INTERFACE(iface) typedef struct iface { \
-  const struct iface##Vtbl *lpVtbl; } iface; \
-  typedef const struct iface##Vtbl iface##Vtbl; \
-  const struct iface##Vtbl
-#else
-#define DECLARE_INTERFACE(iface) typedef struct iface { \
-    struct iface##Vtbl *lpVtbl; \
-  } iface; \
-  typedef struct iface##Vtbl iface##Vtbl; \
-  struct iface##Vtbl
-#endif
-#define DECLARE_INTERFACE_(iface,baseiface) DECLARE_INTERFACE(iface)
-#endif
-
-#include <guiddef.h>
-
-#ifndef _ERROR_STATUS_T_DEFINED
-#define _ERROR_STATUS_T_DEFINED
-typedef unsigned long error_status_t;
-#endif
-
-#ifndef _WCHAR_T_DEFINED
-typedef unsigned short wchar_t;
-#define _WCHAR_T_DEFINED
-#endif
-#endif
diff --git a/tinyc/win32/include/winapi/guiddef.h b/tinyc/win32/include/winapi/guiddef.h
deleted file mode 100644
index 4e7909a97..000000000
--- a/tinyc/win32/include/winapi/guiddef.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef GUID_DEFINED
-#define GUID_DEFINED
-typedef struct _GUID {
-  unsigned long Data1;
-  unsigned short Data2;
-  unsigned short Data3;
-  unsigned char Data4[8 ];
-} GUID;
-#endif
-
-#ifndef UUID_DEFINED
-#define UUID_DEFINED
-typedef GUID UUID;
-#endif
-
-#ifndef FAR
-#define FAR
-#endif
-
-#ifndef DECLSPEC_SELECTANY
-#define DECLSPEC_SELECTANY __declspec(selectany)
-#endif
-
-#ifndef EXTERN_C
-#ifdef __cplusplus
-#define EXTERN_C extern "C"
-#else
-#define EXTERN_C extern
-#endif
-#endif
-
-#ifdef DEFINE_GUID
-#undef DEFINE_GUID
-#endif
-
-#ifdef INITGUID
-#ifdef __cplusplus
-#define DEFINE_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) EXTERN_C const GUID DECLSPEC_SELECTANY name = { l,w1,w2,{ b1,b2,b3,b4,b5,b6,b7,b8 } }
-#else
-#define DEFINE_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) const GUID DECLSPEC_SELECTANY name = { l,w1,w2,{ b1,b2,b3,b4,b5,b6,b7,b8 } }
-#endif
-#else
-#define DEFINE_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) EXTERN_C const GUID name
-#endif
-
-#define DEFINE_OLEGUID(name,l,w1,w2) DEFINE_GUID(name,l,w1,w2,0xC0,0,0,0,0,0,0,0x46)
-
-#ifndef _GUIDDEF_H_
-#define _GUIDDEF_H_
-
-#ifndef __LPGUID_DEFINED__
-#define __LPGUID_DEFINED__
-typedef GUID *LPGUID;
-#endif
-
-#ifndef __LPCGUID_DEFINED__
-#define __LPCGUID_DEFINED__
-typedef const GUID *LPCGUID;
-#endif
-
-#ifndef __IID_DEFINED__
-#define __IID_DEFINED__
-
-typedef GUID IID;
-typedef IID *LPIID;
-#define IID_NULL GUID_NULL
-#define IsEqualIID(riid1,riid2) IsEqualGUID(riid1,riid2)
-typedef GUID CLSID;
-typedef CLSID *LPCLSID;
-#define CLSID_NULL GUID_NULL
-#define IsEqualCLSID(rclsid1,rclsid2) IsEqualGUID(rclsid1,rclsid2)
-typedef GUID FMTID;
-typedef FMTID *LPFMTID;
-#define FMTID_NULL GUID_NULL
-#define IsEqualFMTID(rfmtid1,rfmtid2) IsEqualGUID(rfmtid1,rfmtid2)
-
-#ifdef __midl_proxy
-#define __MIDL_CONST
-#else
-#define __MIDL_CONST const
-#endif
-
-#ifndef _REFGUID_DEFINED
-#define _REFGUID_DEFINED
-#ifdef __cplusplus
-#define REFGUID const GUID &
-#else
-#define REFGUID const GUID *__MIDL_CONST
-#endif
-#endif
-
-#ifndef _REFIID_DEFINED
-#define _REFIID_DEFINED
-#ifdef __cplusplus
-#define REFIID const IID &
-#else
-#define REFIID const IID *__MIDL_CONST
-#endif
-#endif
-
-#ifndef _REFCLSID_DEFINED
-#define _REFCLSID_DEFINED
-#ifdef __cplusplus
-#define REFCLSID const IID &
-#else
-#define REFCLSID const IID *__MIDL_CONST
-#endif
-#endif
-
-#ifndef _REFFMTID_DEFINED
-#define _REFFMTID_DEFINED
-#ifdef __cplusplus
-#define REFFMTID const IID &
-#else
-#define REFFMTID const IID *__MIDL_CONST
-#endif
-#endif
-#endif
-
-#ifndef _SYS_GUID_OPERATORS_
-#define _SYS_GUID_OPERATORS_
-#include <string.h>
-
-#ifdef __cplusplus
-__inline int InlineIsEqualGUID(REFGUID rguid1,REFGUID rguid2) {
-  return (((unsigned long *) &rguid1)[0]==((unsigned long *) &rguid2)[0] && ((unsigned long *) &rguid1)[1]==((unsigned long *) &rguid2)[1] &&
-    ((unsigned long *) &rguid1)[2]==((unsigned long *) &rguid2)[2] && ((unsigned long *) &rguid1)[3]==((unsigned long *) &rguid2)[3]);
-}
-__inline int IsEqualGUID(REFGUID rguid1,REFGUID rguid2) { return !memcmp(&rguid1,&rguid2,sizeof(GUID)); }
-#else
-#define InlineIsEqualGUID(rguid1,rguid2) (((unsigned long *) rguid1)[0]==((unsigned long *) rguid2)[0] && ((unsigned long *) rguid1)[1]==((unsigned long *) rguid2)[1] && ((unsigned long *) rguid1)[2]==((unsigned long *) rguid2)[2] && ((unsigned long *) rguid1)[3]==((unsigned long *) rguid2)[3])
-#define IsEqualGUID(rguid1,rguid2) (!memcmp(rguid1,rguid2,sizeof(GUID)))
-#endif
-
-#ifdef __INLINE_ISEQUAL_GUID
-#undef IsEqualGUID
-#define IsEqualGUID(rguid1,rguid2) InlineIsEqualGUID(rguid1,rguid2)
-#endif
-
-#define IsEqualIID(riid1,riid2) IsEqualGUID(riid1,riid2)
-#define IsEqualCLSID(rclsid1,rclsid2) IsEqualGUID(rclsid1,rclsid2)
-
-#if !defined _SYS_GUID_OPERATOR_EQ_ && !defined _NO_SYS_GUID_OPERATOR_EQ_
-#define _SYS_GUID_OPERATOR_EQ_
-#ifdef __cplusplus
-__inline int operator==(REFGUID guidOne,REFGUID guidOther) { return IsEqualGUID(guidOne,guidOther); }
-__inline int operator!=(REFGUID guidOne,REFGUID guidOther) { return !(guidOne==guidOther); }
-#endif
-#endif
-#endif
-#endif
diff --git a/tinyc/win32/include/winapi/poppack.h b/tinyc/win32/include/winapi/poppack.h
deleted file mode 100644
index b08cba222..000000000
--- a/tinyc/win32/include/winapi/poppack.h
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#if !(defined(lint) || defined(RC_INVOKED))
-#pragma pack(pop)
-#endif
diff --git a/tinyc/win32/include/winapi/pshpack1.h b/tinyc/win32/include/winapi/pshpack1.h
deleted file mode 100644
index d18d9e856..000000000
--- a/tinyc/win32/include/winapi/pshpack1.h
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#if !(defined(lint) || defined(RC_INVOKED))
-#pragma pack(push,1)
-#endif
diff --git a/tinyc/win32/include/winapi/pshpack2.h b/tinyc/win32/include/winapi/pshpack2.h
deleted file mode 100644
index 7de16fd39..000000000
--- a/tinyc/win32/include/winapi/pshpack2.h
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#if !(defined(lint) || defined(RC_INVOKED))
-#pragma pack(push,2)
-#endif
diff --git a/tinyc/win32/include/winapi/pshpack4.h b/tinyc/win32/include/winapi/pshpack4.h
deleted file mode 100644
index 1c8e61d7d..000000000
--- a/tinyc/win32/include/winapi/pshpack4.h
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#if !(defined(lint) || defined(RC_INVOKED))
-#pragma pack(push,4)
-#endif
diff --git a/tinyc/win32/include/winapi/pshpack8.h b/tinyc/win32/include/winapi/pshpack8.h
deleted file mode 100644
index 70a3c7f7c..000000000
--- a/tinyc/win32/include/winapi/pshpack8.h
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#if !(defined(lint) || defined(RC_INVOKED))
-#pragma pack(push,8)
-#endif
diff --git a/tinyc/win32/include/winapi/winbase.h b/tinyc/win32/include/winapi/winbase.h
deleted file mode 100644
index 4a38006ef..000000000
--- a/tinyc/win32/include/winapi/winbase.h
+++ /dev/null
@@ -1,2951 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _WINBASE_
-#define _WINBASE_
-
-#define WINADVAPI DECLSPEC_IMPORT
-#define WINBASEAPI DECLSPEC_IMPORT
-#define ZAWPROXYAPI DECLSPEC_IMPORT
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define DefineHandleTable(w) ((w),TRUE)
-#define LimitEmsPages(dw)
-#define SetSwapAreaSize(w) (w)
-#define LockSegment(w) GlobalFix((HANDLE)(w))
-#define UnlockSegment(w) GlobalUnfix((HANDLE)(w))
-#define GetCurrentTime() GetTickCount()
-
-#define Yield()
-
-#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
-#define INVALID_FILE_SIZE ((DWORD)0xffffffff)
-#define INVALID_SET_FILE_POINTER ((DWORD)-1)
-#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
-
-#define FILE_BEGIN 0
-#define FILE_CURRENT 1
-#define FILE_END 2
-
-#define TIME_ZONE_ID_INVALID ((DWORD)0xffffffff)
-
-#define WAIT_FAILED ((DWORD)0xffffffff)
-#define WAIT_OBJECT_0 ((STATUS_WAIT_0) + 0)
-#define WAIT_ABANDONED ((STATUS_ABANDONED_WAIT_0) + 0)
-#define WAIT_ABANDONED_0 ((STATUS_ABANDONED_WAIT_0) + 0)
-#define WAIT_IO_COMPLETION STATUS_USER_APC
-#define STILL_ACTIVE STATUS_PENDING
-#define EXCEPTION_ACCESS_VIOLATION STATUS_ACCESS_VIOLATION
-#define EXCEPTION_DATATYPE_MISALIGNMENT STATUS_DATATYPE_MISALIGNMENT
-#define EXCEPTION_BREAKPOINT STATUS_BREAKPOINT
-#define EXCEPTION_SINGLE_STEP STATUS_SINGLE_STEP
-#define EXCEPTION_ARRAY_BOUNDS_EXCEEDED STATUS_ARRAY_BOUNDS_EXCEEDED
-#define EXCEPTION_FLT_DENORMAL_OPERAND STATUS_FLOAT_DENORMAL_OPERAND
-#define EXCEPTION_FLT_DIVIDE_BY_ZERO STATUS_FLOAT_DIVIDE_BY_ZERO
-#define EXCEPTION_FLT_INEXACT_RESULT STATUS_FLOAT_INEXACT_RESULT
-#define EXCEPTION_FLT_INVALID_OPERATION STATUS_FLOAT_INVALID_OPERATION
-#define EXCEPTION_FLT_OVERFLOW STATUS_FLOAT_OVERFLOW
-#define EXCEPTION_FLT_STACK_CHECK STATUS_FLOAT_STACK_CHECK
-#define EXCEPTION_FLT_UNDERFLOW STATUS_FLOAT_UNDERFLOW
-#define EXCEPTION_INT_DIVIDE_BY_ZERO STATUS_INTEGER_DIVIDE_BY_ZERO
-#define EXCEPTION_INT_OVERFLOW STATUS_INTEGER_OVERFLOW
-#define EXCEPTION_PRIV_INSTRUCTION STATUS_PRIVILEGED_INSTRUCTION
-#define EXCEPTION_IN_PAGE_ERROR STATUS_IN_PAGE_ERROR
-#define EXCEPTION_ILLEGAL_INSTRUCTION STATUS_ILLEGAL_INSTRUCTION
-#define EXCEPTION_NONCONTINUABLE_EXCEPTION STATUS_NONCONTINUABLE_EXCEPTION
-#define EXCEPTION_STACK_OVERFLOW STATUS_STACK_OVERFLOW
-#define EXCEPTION_INVALID_DISPOSITION STATUS_INVALID_DISPOSITION
-#define EXCEPTION_GUARD_PAGE STATUS_GUARD_PAGE_VIOLATION
-#define EXCEPTION_INVALID_HANDLE STATUS_INVALID_HANDLE
-#define EXCEPTION_POSSIBLE_DEADLOCK STATUS_POSSIBLE_DEADLOCK
-#define CONTROL_C_EXIT STATUS_CONTROL_C_EXIT
-#define MoveMemory RtlMoveMemory
-#define CopyMemory RtlCopyMemory
-#define FillMemory RtlFillMemory
-#define ZeroMemory RtlZeroMemory
-#define SecureZeroMemory RtlSecureZeroMemory
-
-#define FILE_FLAG_WRITE_THROUGH 0x80000000
-#define FILE_FLAG_OVERLAPPED 0x40000000
-#define FILE_FLAG_NO_BUFFERING 0x20000000
-#define FILE_FLAG_RANDOM_ACCESS 0x10000000
-#define FILE_FLAG_SEQUENTIAL_SCAN 0x8000000
-#define FILE_FLAG_DELETE_ON_CLOSE 0x4000000
-#define FILE_FLAG_BACKUP_SEMANTICS 0x2000000
-#define FILE_FLAG_POSIX_SEMANTICS 0x1000000
-#define FILE_FLAG_OPEN_REPARSE_POINT 0x200000
-#define FILE_FLAG_OPEN_NO_RECALL 0x100000
-#define FILE_FLAG_FIRST_PIPE_INSTANCE 0x80000
-
-#define CREATE_NEW 1
-#define CREATE_ALWAYS 2
-#define OPEN_EXISTING 3
-#define OPEN_ALWAYS 4
-#define TRUNCATE_EXISTING 5
-
-#define PROGRESS_CONTINUE 0
-#define PROGRESS_CANCEL 1
-#define PROGRESS_STOP 2
-#define PROGRESS_QUIET 3
-
-#define CALLBACK_CHUNK_FINISHED 0x0
-#define CALLBACK_STREAM_SWITCH 0x1
-
-#define COPY_FILE_FAIL_IF_EXISTS 0x1
-#define COPY_FILE_RESTARTABLE 0x2
-#define COPY_FILE_OPEN_SOURCE_FOR_WRITE 0x4
-#define COPY_FILE_ALLOW_DECRYPTED_DESTINATION 0x8
-
-#define REPLACEFILE_WRITE_THROUGH 0x1
-#define REPLACEFILE_IGNORE_MERGE_ERRORS 0x2
-
-#define PIPE_ACCESS_INBOUND 0x1
-#define PIPE_ACCESS_OUTBOUND 0x2
-#define PIPE_ACCESS_DUPLEX 0x3
-
-#define PIPE_CLIENT_END 0x0
-#define PIPE_SERVER_END 0x1
-
-#define PIPE_WAIT 0x0
-#define PIPE_NOWAIT 0x1
-#define PIPE_READMODE_BYTE 0x0
-#define PIPE_READMODE_MESSAGE 0x2
-#define PIPE_TYPE_BYTE 0x0
-#define PIPE_TYPE_MESSAGE 0x4
-
-#define PIPE_UNLIMITED_INSTANCES 255
-
-#define SECURITY_ANONYMOUS (SecurityAnonymous << 16)
-#define SECURITY_IDENTIFICATION (SecurityIdentification << 16)
-#define SECURITY_IMPERSONATION (SecurityImpersonation << 16)
-#define SECURITY_DELEGATION (SecurityDelegation << 16)
-
-#define SECURITY_CONTEXT_TRACKING 0x40000
-#define SECURITY_EFFECTIVE_ONLY 0x80000
-
-#define SECURITY_SQOS_PRESENT 0x100000
-#define SECURITY_VALID_SQOS_FLAGS 0x1f0000
-
-  typedef struct _OVERLAPPED {
-    ULONG_PTR Internal;
-    ULONG_PTR InternalHigh;
-    union {
-      struct {
-	DWORD Offset;
-	DWORD OffsetHigh;
-      };
-      PVOID Pointer;
-    };
-    HANDLE hEvent;
-  } OVERLAPPED,*LPOVERLAPPED;
-
-  typedef struct _SECURITY_ATTRIBUTES {
-    DWORD nLength;
-    LPVOID lpSecurityDescriptor;
-    WINBOOL bInheritHandle;
-  } SECURITY_ATTRIBUTES,*PSECURITY_ATTRIBUTES,*LPSECURITY_ATTRIBUTES;
-
-  typedef struct _PROCESS_INFORMATION {
-    HANDLE hProcess;
-    HANDLE hThread;
-    DWORD dwProcessId;
-    DWORD dwThreadId;
-  } PROCESS_INFORMATION,*PPROCESS_INFORMATION,*LPPROCESS_INFORMATION;
-
-#ifndef _FILETIME_
-#define _FILETIME_
-  typedef struct _FILETIME {
-    DWORD dwLowDateTime;
-    DWORD dwHighDateTime;
-  } FILETIME,*PFILETIME,*LPFILETIME;
-#endif
-
-  typedef struct _SYSTEMTIME {
-    WORD wYear;
-    WORD wMonth;
-    WORD wDayOfWeek;
-    WORD wDay;
-    WORD wHour;
-    WORD wMinute;
-    WORD wSecond;
-    WORD wMilliseconds;
-  } SYSTEMTIME,*PSYSTEMTIME,*LPSYSTEMTIME;
-
-  typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(LPVOID lpThreadParameter);
-  typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;
-  typedef VOID (WINAPI *PFIBER_START_ROUTINE)(LPVOID lpFiberParameter);
-  typedef PFIBER_START_ROUTINE LPFIBER_START_ROUTINE;
-
-  typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;
-  typedef PRTL_CRITICAL_SECTION PCRITICAL_SECTION;
-  typedef PRTL_CRITICAL_SECTION LPCRITICAL_SECTION;
-  typedef RTL_CRITICAL_SECTION_DEBUG CRITICAL_SECTION_DEBUG;
-  typedef PRTL_CRITICAL_SECTION_DEBUG PCRITICAL_SECTION_DEBUG;
-  typedef PRTL_CRITICAL_SECTION_DEBUG LPCRITICAL_SECTION_DEBUG;
-
-  WINBASEAPI PVOID WINAPI EncodePointer(PVOID Ptr);
-  WINBASEAPI PVOID WINAPI DecodePointer(PVOID Ptr);
-  WINBASEAPI PVOID WINAPI EncodeSystemPointer(PVOID Ptr);
-  WINBASEAPI PVOID WINAPI DecodeSystemPointer(PVOID Ptr);
-
-#ifdef I_X86_
-  typedef PLDT_ENTRY LPLDT_ENTRY;
-#else
-  typedef LPVOID LPLDT_ENTRY;
-#endif
-
-#define MUTEX_MODIFY_STATE MUTANT_QUERY_STATE
-#define MUTEX_ALL_ACCESS MUTANT_ALL_ACCESS
-
-#define SP_SERIALCOMM ((DWORD)0x1)
-
-#define PST_UNSPECIFIED ((DWORD)0x0)
-#define PST_RS232 ((DWORD)0x1)
-#define PST_PARALLELPORT ((DWORD)0x2)
-#define PST_RS422 ((DWORD)0x3)
-#define PST_RS423 ((DWORD)0x4)
-#define PST_RS449 ((DWORD)0x5)
-#define PST_MODEM ((DWORD)0x6)
-#define PST_FAX ((DWORD)0x21)
-#define PST_SCANNER ((DWORD)0x22)
-#define PST_NETWORK_BRIDGE ((DWORD)0x100)
-#define PST_LAT ((DWORD)0x101)
-#define PST_TCPIP_TELNET ((DWORD)0x102)
-#define PST_X25 ((DWORD)0x103)
-
-#define PCF_DTRDSR ((DWORD)0x1)
-#define PCF_RTSCTS ((DWORD)0x2)
-#define PCF_RLSD ((DWORD)0x4)
-#define PCF_PARITY_CHECK ((DWORD)0x8)
-#define PCF_XONXOFF ((DWORD)0x10)
-#define PCF_SETXCHAR ((DWORD)0x20)
-#define PCF_TOTALTIMEOUTS ((DWORD)0x40)
-#define PCF_INTTIMEOUTS ((DWORD)0x80)
-#define PCF_SPECIALCHARS ((DWORD)0x100)
-#define PCF_16BITMODE ((DWORD)0x200)
-
-#define SP_PARITY ((DWORD)0x1)
-#define SP_BAUD ((DWORD)0x2)
-#define SP_DATABITS ((DWORD)0x4)
-#define SP_STOPBITS ((DWORD)0x8)
-#define SP_HANDSHAKING ((DWORD)0x10)
-#define SP_PARITY_CHECK ((DWORD)0x20)
-#define SP_RLSD ((DWORD)0x40)
-
-#define BAUD_075 ((DWORD)0x1)
-#define BAUD_110 ((DWORD)0x2)
-#define BAUD_134_5 ((DWORD)0x4)
-#define BAUD_150 ((DWORD)0x8)
-#define BAUD_300 ((DWORD)0x10)
-#define BAUD_600 ((DWORD)0x20)
-#define BAUD_1200 ((DWORD)0x40)
-#define BAUD_1800 ((DWORD)0x80)
-#define BAUD_2400 ((DWORD)0x100)
-#define BAUD_4800 ((DWORD)0x200)
-#define BAUD_7200 ((DWORD)0x400)
-#define BAUD_9600 ((DWORD)0x800)
-#define BAUD_14400 ((DWORD)0x1000)
-#define BAUD_19200 ((DWORD)0x2000)
-#define BAUD_38400 ((DWORD)0x4000)
-#define BAUD_56K ((DWORD)0x8000)
-#define BAUD_128K ((DWORD)0x10000)
-#define BAUD_115200 ((DWORD)0x20000)
-#define BAUD_57600 ((DWORD)0x40000)
-#define BAUD_USER ((DWORD)0x10000000)
-
-#define DATABITS_5 ((WORD)0x1)
-#define DATABITS_6 ((WORD)0x2)
-#define DATABITS_7 ((WORD)0x4)
-#define DATABITS_8 ((WORD)0x8)
-#define DATABITS_16 ((WORD)0x10)
-#define DATABITS_16X ((WORD)0x20)
-
-#define STOPBITS_10 ((WORD)0x1)
-#define STOPBITS_15 ((WORD)0x2)
-#define STOPBITS_20 ((WORD)0x4)
-#define PARITY_NONE ((WORD)0x100)
-#define PARITY_ODD ((WORD)0x200)
-#define PARITY_EVEN ((WORD)0x400)
-#define PARITY_MARK ((WORD)0x800)
-#define PARITY_SPACE ((WORD)0x1000)
-
-  typedef struct _COMMPROP {
-    WORD wPacketLength;
-    WORD wPacketVersion;
-    DWORD dwServiceMask;
-    DWORD dwReserved1;
-    DWORD dwMaxTxQueue;
-    DWORD dwMaxRxQueue;
-    DWORD dwMaxBaud;
-    DWORD dwProvSubType;
-    DWORD dwProvCapabilities;
-    DWORD dwSettableParams;
-    DWORD dwSettableBaud;
-    WORD wSettableData;
-    WORD wSettableStopParity;
-    DWORD dwCurrentTxQueue;
-    DWORD dwCurrentRxQueue;
-    DWORD dwProvSpec1;
-    DWORD dwProvSpec2;
-    WCHAR wcProvChar[1];
-  } COMMPROP,*LPCOMMPROP;
-
-#define COMMPROP_INITIALIZED ((DWORD)0xE73CF52E)
-
-  typedef struct _COMSTAT {
-    DWORD fCtsHold : 1;
-    DWORD fDsrHold : 1;
-    DWORD fRlsdHold : 1;
-    DWORD fXoffHold : 1;
-    DWORD fXoffSent : 1;
-    DWORD fEof : 1;
-    DWORD fTxim : 1;
-    DWORD fReserved : 25;
-    DWORD cbInQue;
-    DWORD cbOutQue;
-  } COMSTAT,*LPCOMSTAT;
-
-#define DTR_CONTROL_DISABLE 0x0
-#define DTR_CONTROL_ENABLE 0x1
-#define DTR_CONTROL_HANDSHAKE 0x2
-
-#define RTS_CONTROL_DISABLE 0x0
-#define RTS_CONTROL_ENABLE 0x1
-#define RTS_CONTROL_HANDSHAKE 0x2
-#define RTS_CONTROL_TOGGLE 0x3
-
-  typedef struct _DCB {
-    DWORD DCBlength;
-    DWORD BaudRate;
-    DWORD fBinary: 1;
-    DWORD fParity: 1;
-    DWORD fOutxCtsFlow:1;
-    DWORD fOutxDsrFlow:1;
-    DWORD fDtrControl:2;
-    DWORD fDsrSensitivity:1;
-    DWORD fTXContinueOnXoff: 1;
-    DWORD fOutX: 1;
-    DWORD fInX: 1;
-    DWORD fErrorChar: 1;
-    DWORD fNull: 1;
-    DWORD fRtsControl:2;
-    DWORD fAbortOnError:1;
-    DWORD fDummy2:17;
-    WORD wReserved;
-    WORD XonLim;
-    WORD XoffLim;
-    BYTE ByteSize;
-    BYTE Parity;
-    BYTE StopBits;
-    char XonChar;
-    char XoffChar;
-    char ErrorChar;
-    char EofChar;
-    char EvtChar;
-    WORD wReserved1;
-  } DCB,*LPDCB;
-
-  typedef struct _COMMTIMEOUTS {
-    DWORD ReadIntervalTimeout;
-    DWORD ReadTotalTimeoutMultiplier;
-    DWORD ReadTotalTimeoutConstant;
-    DWORD WriteTotalTimeoutMultiplier;
-    DWORD WriteTotalTimeoutConstant;
-  } COMMTIMEOUTS,*LPCOMMTIMEOUTS;
-
-  typedef struct _COMMCONFIG {
-    DWORD dwSize;
-    WORD wVersion;
-    WORD wReserved;
-    DCB dcb;
-    DWORD dwProviderSubType;
-    DWORD dwProviderOffset;
-    DWORD dwProviderSize;
-    WCHAR wcProviderData[1];
-  } COMMCONFIG,*LPCOMMCONFIG;
-
-  typedef struct _SYSTEM_INFO {
-    union {
-      DWORD dwOemId;
-      struct {
-	WORD wProcessorArchitecture;
-	WORD wReserved;
-      };
-    };
-    DWORD dwPageSize;
-    LPVOID lpMinimumApplicationAddress;
-    LPVOID lpMaximumApplicationAddress;
-    DWORD_PTR dwActiveProcessorMask;
-    DWORD dwNumberOfProcessors;
-    DWORD dwProcessorType;
-    DWORD dwAllocationGranularity;
-    WORD wProcessorLevel;
-    WORD wProcessorRevision;
-  } SYSTEM_INFO,*LPSYSTEM_INFO;
-
-#define FreeModule(hLibModule) FreeLibrary((hLibModule))
-#define MakeProcInstance(lpProc,hInstance) (lpProc)
-#define FreeProcInstance(lpProc) (lpProc)
-
-#define GMEM_FIXED 0x0
-#define GMEM_MOVEABLE 0x2
-#define GMEM_NOCOMPACT 0x10
-#define GMEM_NODISCARD 0x20
-#define GMEM_ZEROINIT 0x40
-#define GMEM_MODIFY 0x80
-#define GMEM_DISCARDABLE 0x100
-#define GMEM_NOT_BANKED 0x1000
-#define GMEM_SHARE 0x2000
-#define GMEM_DDESHARE 0x2000
-#define GMEM_NOTIFY 0x4000
-#define GMEM_LOWER GMEM_NOT_BANKED
-#define GMEM_VALID_FLAGS 0x7F72
-#define GMEM_INVALID_HANDLE 0x8000
-
-#define GHND (GMEM_MOVEABLE | GMEM_ZEROINIT)
-#define GPTR (GMEM_FIXED | GMEM_ZEROINIT)
-
-#define GlobalLRUNewest(h) ((HANDLE)(h))
-#define GlobalLRUOldest(h) ((HANDLE)(h))
-#define GlobalDiscard(h) GlobalReAlloc((h),0,GMEM_MOVEABLE)
-
-#define GMEM_DISCARDED 0x4000
-#define GMEM_LOCKCOUNT 0xff
-
-  typedef struct _MEMORYSTATUS {
-    DWORD dwLength;
-    DWORD dwMemoryLoad;
-    SIZE_T dwTotalPhys;
-    SIZE_T dwAvailPhys;
-    SIZE_T dwTotalPageFile;
-    SIZE_T dwAvailPageFile;
-    SIZE_T dwTotalVirtual;
-    SIZE_T dwAvailVirtual;
-  } MEMORYSTATUS,*LPMEMORYSTATUS;
-
-#define LMEM_FIXED 0x0
-#define LMEM_MOVEABLE 0x2
-#define LMEM_NOCOMPACT 0x10
-#define LMEM_NODISCARD 0x20
-#define LMEM_ZEROINIT 0x40
-#define LMEM_MODIFY 0x80
-#define LMEM_DISCARDABLE 0xf00
-#define LMEM_VALID_FLAGS 0xf72
-#define LMEM_INVALID_HANDLE 0x8000
-
-#define LHND (LMEM_MOVEABLE | LMEM_ZEROINIT)
-#define LPTR (LMEM_FIXED | LMEM_ZEROINIT)
-
-#define NONZEROLHND (LMEM_MOVEABLE)
-#define NONZEROLPTR (LMEM_FIXED)
-
-#define LocalDiscard(h) LocalReAlloc((h),0,LMEM_MOVEABLE)
-
-#define LMEM_DISCARDED 0x4000
-#define LMEM_LOCKCOUNT 0xff
-
-#define DEBUG_PROCESS 0x1
-#define DEBUG_ONLY_THIS_PROCESS 0x2
-#define CREATE_SUSPENDED 0x4
-#define DETACHED_PROCESS 0x8
-#define CREATE_NEW_CONSOLE 0x10
-#define NORMAL_PRIORITY_CLASS 0x20
-#define IDLE_PRIORITY_CLASS 0x40
-#define HIGH_PRIORITY_CLASS 0x80
-#define REALTIME_PRIORITY_CLASS 0x100
-#define CREATE_NEW_PROCESS_GROUP 0x200
-#define CREATE_UNICODE_ENVIRONMENT 0x400
-#define CREATE_SEPARATE_WOW_VDM 0x800
-#define CREATE_SHARED_WOW_VDM 0x1000
-#define CREATE_FORCEDOS 0x2000
-#define BELOW_NORMAL_PRIORITY_CLASS 0x4000
-#define ABOVE_NORMAL_PRIORITY_CLASS 0x8000
-#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x10000
-
-#define CREATE_BREAKAWAY_FROM_JOB 0x1000000
-#define CREATE_PRESERVE_CODE_AUTHZ_LEVEL 0x2000000
-
-#define CREATE_DEFAULT_ERROR_MODE 0x4000000
-#define CREATE_NO_WINDOW 0x8000000
-
-#define PROFILE_USER 0x10000000
-#define PROFILE_KERNEL 0x20000000
-#define PROFILE_SERVER 0x40000000
-
-#define CREATE_IGNORE_SYSTEM_DEFAULT 0x80000000
-
-#define THREAD_PRIORITY_LOWEST THREAD_BASE_PRIORITY_MIN
-#define THREAD_PRIORITY_BELOW_NORMAL (THREAD_PRIORITY_LOWEST+1)
-#define THREAD_PRIORITY_NORMAL 0
-#define THREAD_PRIORITY_HIGHEST THREAD_BASE_PRIORITY_MAX
-#define THREAD_PRIORITY_ABOVE_NORMAL (THREAD_PRIORITY_HIGHEST-1)
-#define THREAD_PRIORITY_ERROR_RETURN (MAXLONG)
-
-#define THREAD_PRIORITY_TIME_CRITICAL THREAD_BASE_PRIORITY_LOWRT
-#define THREAD_PRIORITY_IDLE THREAD_BASE_PRIORITY_IDLE
-
-#define EXCEPTION_DEBUG_EVENT 1
-#define CREATE_THREAD_DEBUG_EVENT 2
-#define CREATE_PROCESS_DEBUG_EVENT 3
-#define EXIT_THREAD_DEBUG_EVENT 4
-#define EXIT_PROCESS_DEBUG_EVENT 5
-#define LOAD_DLL_DEBUG_EVENT 6
-#define UNLOAD_DLL_DEBUG_EVENT 7
-#define OUTPUT_DEBUG_STRING_EVENT 8
-#define RIP_EVENT 9
-
-  typedef struct _EXCEPTION_DEBUG_INFO {
-    EXCEPTION_RECORD ExceptionRecord;
-    DWORD dwFirstChance;
-  } EXCEPTION_DEBUG_INFO,*LPEXCEPTION_DEBUG_INFO;
-
-  typedef struct _CREATE_THREAD_DEBUG_INFO {
-    HANDLE hThread;
-    LPVOID lpThreadLocalBase;
-    LPTHREAD_START_ROUTINE lpStartAddress;
-  } CREATE_THREAD_DEBUG_INFO,*LPCREATE_THREAD_DEBUG_INFO;
-
-  typedef struct _CREATE_PROCESS_DEBUG_INFO {
-    HANDLE hFile;
-    HANDLE hProcess;
-    HANDLE hThread;
-    LPVOID lpBaseOfImage;
-    DWORD dwDebugInfoFileOffset;
-    DWORD nDebugInfoSize;
-    LPVOID lpThreadLocalBase;
-    LPTHREAD_START_ROUTINE lpStartAddress;
-    LPVOID lpImageName;
-    WORD fUnicode;
-  } CREATE_PROCESS_DEBUG_INFO,*LPCREATE_PROCESS_DEBUG_INFO;
-
-  typedef struct _EXIT_THREAD_DEBUG_INFO {
-    DWORD dwExitCode;
-  } EXIT_THREAD_DEBUG_INFO,*LPEXIT_THREAD_DEBUG_INFO;
-
-  typedef struct _EXIT_PROCESS_DEBUG_INFO {
-    DWORD dwExitCode;
-  } EXIT_PROCESS_DEBUG_INFO,*LPEXIT_PROCESS_DEBUG_INFO;
-
-  typedef struct _LOAD_DLL_DEBUG_INFO {
-    HANDLE hFile;
-    LPVOID lpBaseOfDll;
-    DWORD dwDebugInfoFileOffset;
-    DWORD nDebugInfoSize;
-    LPVOID lpImageName;
-    WORD fUnicode;
-  } LOAD_DLL_DEBUG_INFO,*LPLOAD_DLL_DEBUG_INFO;
-
-  typedef struct _UNLOAD_DLL_DEBUG_INFO {
-    LPVOID lpBaseOfDll;
-  } UNLOAD_DLL_DEBUG_INFO,*LPUNLOAD_DLL_DEBUG_INFO;
-
-  typedef struct _OUTPUT_DEBUG_STRING_INFO {
-    LPSTR lpDebugStringData;
-    WORD fUnicode;
-    WORD nDebugStringLength;
-  } OUTPUT_DEBUG_STRING_INFO,*LPOUTPUT_DEBUG_STRING_INFO;
-
-  typedef struct _RIP_INFO {
-    DWORD dwError;
-    DWORD dwType;
-  } RIP_INFO,*LPRIP_INFO;
-
-  typedef struct _DEBUG_EVENT {
-    DWORD dwDebugEventCode;
-    DWORD dwProcessId;
-    DWORD dwThreadId;
-    union {
-      EXCEPTION_DEBUG_INFO Exception;
-      CREATE_THREAD_DEBUG_INFO CreateThread;
-      CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
-      EXIT_THREAD_DEBUG_INFO ExitThread;
-      EXIT_PROCESS_DEBUG_INFO ExitProcess;
-      LOAD_DLL_DEBUG_INFO LoadDll;
-      UNLOAD_DLL_DEBUG_INFO UnloadDll;
-      OUTPUT_DEBUG_STRING_INFO DebugString;
-      RIP_INFO RipInfo;
-    } u;
-  } DEBUG_EVENT,*LPDEBUG_EVENT;
-
-  typedef PCONTEXT LPCONTEXT;
-  typedef PEXCEPTION_RECORD LPEXCEPTION_RECORD;
-  typedef PEXCEPTION_POINTERS LPEXCEPTION_POINTERS;
-
-#define DRIVE_UNKNOWN 0
-#define DRIVE_NO_ROOT_DIR 1
-#define DRIVE_REMOVABLE 2
-#define DRIVE_FIXED 3
-#define DRIVE_REMOTE 4
-#define DRIVE_CDROM 5
-#define DRIVE_RAMDISK 6
-
-#define GetFreeSpace(w) (0x100000L)
-#define FILE_TYPE_UNKNOWN 0x0
-#define FILE_TYPE_DISK 0x1
-#define FILE_TYPE_CHAR 0x2
-#define FILE_TYPE_PIPE 0x3
-#define FILE_TYPE_REMOTE 0x8000
-
-#define STD_INPUT_HANDLE ((DWORD)-10)
-#define STD_OUTPUT_HANDLE ((DWORD)-11)
-#define STD_ERROR_HANDLE ((DWORD)-12)
-
-#define NOPARITY 0
-#define ODDPARITY 1
-#define EVENPARITY 2
-#define MARKPARITY 3
-#define SPACEPARITY 4
-
-#define ONESTOPBIT 0
-#define ONE5STOPBITS 1
-#define TWOSTOPBITS 2
-
-#define IGNORE 0
-#define INFINITE 0xffffffff
-
-#define CBR_110 110
-#define CBR_300 300
-#define CBR_600 600
-#define CBR_1200 1200
-#define CBR_2400 2400
-#define CBR_4800 4800
-#define CBR_9600 9600
-#define CBR_14400 14400
-#define CBR_19200 19200
-#define CBR_38400 38400
-#define CBR_56000 56000
-#define CBR_57600 57600
-#define CBR_115200 115200
-#define CBR_128000 128000
-#define CBR_256000 256000
-
-#define CE_RXOVER 0x1
-#define CE_OVERRUN 0x2
-#define CE_RXPARITY 0x4
-#define CE_FRAME 0x8
-#define CE_BREAK 0x10
-#define CE_TXFULL 0x100
-#define CE_PTO 0x200
-#define CE_IOE 0x400
-#define CE_DNS 0x800
-#define CE_OOP 0x1000
-#define CE_MODE 0x8000
-
-#define IE_BADID (-1)
-#define IE_OPEN (-2)
-#define IE_NOPEN (-3)
-#define IE_MEMORY (-4)
-#define IE_DEFAULT (-5)
-#define IE_HARDWARE (-10)
-#define IE_BYTESIZE (-11)
-#define IE_BAUDRATE (-12)
-
-#define EV_RXCHAR 0x1
-#define EV_RXFLAG 0x2
-#define EV_TXEMPTY 0x4
-#define EV_CTS 0x8
-#define EV_DSR 0x10
-#define EV_RLSD 0x20
-#define EV_BREAK 0x40
-#define EV_ERR 0x80
-#define EV_RING 0x100
-#define EV_PERR 0x200
-#define EV_RX80FULL 0x400
-#define EV_EVENT1 0x800
-#define EV_EVENT2 0x1000
-
-#define SETXOFF 1
-#define SETXON 2
-#define SETRTS 3
-#define CLRRTS 4
-#define SETDTR 5
-#define CLRDTR 6
-#define RESETDEV 7
-#define SETBREAK 8
-#define CLRBREAK 9
-
-#define PURGE_TXABORT 0x1
-#define PURGE_RXABORT 0x2
-#define PURGE_TXCLEAR 0x4
-#define PURGE_RXCLEAR 0x8
-
-#define LPTx 0x80
-
-#define MS_CTS_ON ((DWORD)0x10)
-#define MS_DSR_ON ((DWORD)0x20)
-#define MS_RING_ON ((DWORD)0x40)
-#define MS_RLSD_ON ((DWORD)0x80)
-
-#define S_QUEUEEMPTY 0
-#define S_THRESHOLD 1
-#define S_ALLTHRESHOLD 2
-
-#define S_NORMAL 0
-#define S_LEGATO 1
-#define S_STACCATO 2
-
-#define S_PERIOD512 0
-#define S_PERIOD1024 1
-#define S_PERIOD2048 2
-#define S_PERIODVOICE 3
-#define S_WHITE512 4
-#define S_WHITE1024 5
-#define S_WHITE2048 6
-#define S_WHITEVOICE 7
-
-#define S_SERDVNA (-1)
-#define S_SEROFM (-2)
-#define S_SERMACT (-3)
-#define S_SERQFUL (-4)
-#define S_SERBDNT (-5)
-#define S_SERDLN (-6)
-#define S_SERDCC (-7)
-#define S_SERDTP (-8)
-#define S_SERDVL (-9)
-#define S_SERDMD (-10)
-#define S_SERDSH (-11)
-#define S_SERDPT (-12)
-#define S_SERDFQ (-13)
-#define S_SERDDR (-14)
-#define S_SERDSR (-15)
-#define S_SERDST (-16)
-
-#define NMPWAIT_WAIT_FOREVER 0xffffffff
-#define NMPWAIT_NOWAIT 0x1
-#define NMPWAIT_USE_DEFAULT_WAIT 0x0
-
-#define FS_CASE_IS_PRESERVED FILE_CASE_PRESERVED_NAMES
-#define FS_CASE_SENSITIVE FILE_CASE_SENSITIVE_SEARCH
-#define FS_UNICODE_STORED_ON_DISK FILE_UNICODE_ON_DISK
-#define FS_PERSISTENT_ACLS FILE_PERSISTENT_ACLS
-#define FS_VOL_IS_COMPRESSED FILE_VOLUME_IS_COMPRESSED
-#define FS_FILE_COMPRESSION FILE_FILE_COMPRESSION
-#define FS_FILE_ENCRYPTION FILE_SUPPORTS_ENCRYPTION
-
-#define FILE_MAP_COPY SECTION_QUERY
-#define FILE_MAP_WRITE SECTION_MAP_WRITE
-#define FILE_MAP_READ SECTION_MAP_READ
-#define FILE_MAP_ALL_ACCESS SECTION_ALL_ACCESS
-#define FILE_MAP_EXECUTE SECTION_MAP_EXECUTE_EXPLICIT
-
-#define OF_READ 0x0
-#define OF_WRITE 0x1
-#define OF_READWRITE 0x2
-#define OF_SHARE_COMPAT 0x0
-#define OF_SHARE_EXCLUSIVE 0x10
-#define OF_SHARE_DENY_WRITE 0x20
-#define OF_SHARE_DENY_READ 0x30
-#define OF_SHARE_DENY_NONE 0x40
-#define OF_PARSE 0x100
-#define OF_DELETE 0x200
-#define OF_VERIFY 0x400
-#define OF_CANCEL 0x800
-#define OF_CREATE 0x1000
-#define OF_PROMPT 0x2000
-#define OF_EXIST 0x4000
-#define OF_REOPEN 0x8000
-
-#define OFS_MAXPATHNAME 128
-  typedef struct _OFSTRUCT {
-    BYTE cBytes;
-    BYTE fFixedDisk;
-    WORD nErrCode;
-    WORD Reserved1;
-    WORD Reserved2;
-    CHAR szPathName[OFS_MAXPATHNAME];
-  } OFSTRUCT,*LPOFSTRUCT,*POFSTRUCT;
-
-#ifndef NOWINBASEINTERLOCK
-
-#ifndef _NTOS_
-
-#if defined(__ia64__) && !defined(RC_INVOKED)
-
-#define InterlockedIncrement _InterlockedIncrement
-#define InterlockedIncrementAcquire _InterlockedIncrement_acq
-#define InterlockedIncrementRelease _InterlockedIncrement_rel
-#define InterlockedDecrement _InterlockedDecrement
-#define InterlockedDecrementAcquire _InterlockedDecrement_acq
-#define InterlockedDecrementRelease _InterlockedDecrement_rel
-#define InterlockedExchange _InterlockedExchange
-#define InterlockedExchangeAdd _InterlockedExchangeAdd
-#define InterlockedCompareExchange _InterlockedCompareExchange
-#define InterlockedCompareExchangeAcquire _InterlockedCompareExchange_acq
-#define InterlockedCompareExchangeRelease _InterlockedCompareExchange_rel
-#define InterlockedExchangePointer _InterlockedExchangePointer
-#define InterlockedCompareExchangePointer _InterlockedCompareExchangePointer
-#define InterlockedCompareExchangePointerRelease _InterlockedCompareExchangePointer_rel
-#define InterlockedCompareExchangePointerAcquire _InterlockedCompareExchangePointer_acq
-
-#define InterlockedIncrement64 _InterlockedIncrement64
-#define InterlockedDecrement64 _InterlockedDecrement64
-#define InterlockedExchange64 _InterlockedExchange64
-#define InterlockedExchangeAcquire64 _InterlockedExchange64_acq
-#define InterlockedExchangeAdd64 _InterlockedExchangeAdd64
-#define InterlockedCompareExchange64 _InterlockedCompareExchange64
-#define InterlockedCompareExchangeAcquire64 _InterlockedCompareExchange64_acq
-#define InterlockedCompareExchangeRelease64 _InterlockedCompareExchange64_rel
-
-  LONGLONG __cdecl InterlockedIncrement64(LONGLONG volatile *Addend);
-  LONGLONG __cdecl InterlockedDecrement64(LONGLONG volatile *Addend);
-  LONG __cdecl InterlockedIncrementAcquire(LONG volatile *Addend);
-  LONG __cdecl InterlockedDecrementAcquire(LONG volatile *Addend);
-  LONG __cdecl InterlockedIncrementRelease(LONG volatile *Addend);
-  LONG __cdecl InterlockedDecrementRelease(LONG volatile *Addend);
-  LONGLONG __cdecl InterlockedExchange64 (LONGLONG volatile *Target,LONGLONG Value);
-  LONGLONG __cdecl InterlockedExchangeAcquire64 (LONGLONG volatile *Target,LONGLONG Value);
-  LONGLONG __cdecl InterlockedExchangeAdd64 (LONGLONG volatile *Addend,LONGLONG Value);
-  LONGLONG __cdecl InterlockedCompareExchange64 (LONGLONG volatile *Destination,LONGLONG ExChange,LONGLONG Comperand);
-  LONGLONG __cdecl InterlockedCompareExchangeAcquire64 (LONGLONG volatile *Destination,LONGLONG ExChange,LONGLONG Comperand);
-  LONGLONG __cdecl InterlockedCompareExchangeRelease64 (LONGLONG volatile *Destination,LONGLONG ExChange,LONGLONG Comperand);
-  LONG __cdecl InterlockedIncrement(LONG volatile *lpAddend);
-  LONG __cdecl InterlockedDecrement(LONG volatile *lpAddend);
-  LONG __cdecl InterlockedExchange(LONG volatile *Target,LONG Value);
-  LONG __cdecl InterlockedExchangeAdd(LONG volatile *Addend,LONG Value);
-  LONG __cdecl InterlockedCompareExchange(LONG volatile *Destination,LONG ExChange,LONG Comperand);
-  LONG __cdecl InterlockedCompareExchangeRelease(LONG volatile *Destination,LONG ExChange,LONG Comperand);
-  LONG __cdecl InterlockedCompareExchangeAcquire(LONG volatile *Destination,LONG ExChange,LONG Comperand);
-  PVOID __cdecl InterlockedExchangePointer(PVOID volatile *Target,PVOID Value);
-  PVOID __cdecl InterlockedCompareExchangePointer(PVOID volatile *Destination,PVOID ExChange,PVOID Comperand);
-  PVOID __cdecl InterlockedCompareExchangePointerAcquire(PVOID volatile *Destination,PVOID Exchange,PVOID Comperand);
-  PVOID __cdecl InterlockedCompareExchangePointerRelease(PVOID volatile *Destination,PVOID Exchange,PVOID Comperand);
-
-#ifndef InterlockedAnd
-#define InterlockedAnd InterlockedAnd_Inline
-  __CRT_INLINE LONG InterlockedAnd_Inline(LONG volatile *Target,LONG Set) {
-    LONG i;
-    LONG j;
-    j = *Target;
-    do {
-      i = j;
-      j = InterlockedCompareExchange(Target,i & Set,i);
-    } while(i!=j);
-    return j;
-  }
-#endif
-
-#ifndef InterlockedOr
-#define InterlockedOr InterlockedOr_Inline
-
-  __CRT_INLINE LONG InterlockedOr_Inline(LONG volatile *Target,LONG Set) {
-    LONG i;
-    LONG j;
-    j = *Target;
-    do {
-      i = j;
-      j = InterlockedCompareExchange(Target,i | Set,i);
-    } while(i!=j);
-    return j;
-  }
-#endif
-
-#ifndef InterlockedXor
-#define InterlockedXor InterlockedXor_Inline
-
-  __CRT_INLINE LONG InterlockedXor_Inline(LONG volatile *Target,LONG Set) {
-    LONG i;
-    LONG j;
-    j = *Target;
-    do {
-      i = j;
-      j = InterlockedCompareExchange(Target,i ^ Set,i);
-    } while(i!=j);
-    return j;
-  }
-#endif
-
-#ifndef !defined (InterlockedAnd64)
-#define InterlockedAnd64 InterlockedAnd64_Inline
-
-  __CRT_INLINE LONGLONG InterlockedAnd64_Inline (LONGLONG volatile *Destination,LONGLONG Value) {
-    LONGLONG Old;
-    do {
-      Old = *Destination;
-    } while(InterlockedCompareExchange64(Destination,Old & Value,Old)!=Old);
-    return Old;
-  }
-#endif
-
-#ifndef InterlockedOr64
-#define InterlockedOr64 InterlockedOr64_Inline
-
-  __CRT_INLINE LONGLONG InterlockedOr64_Inline (LONGLONG volatile *Destination,LONGLONG Value) {
-    LONGLONG Old;
-    do {
-      Old = *Destination;
-    } while(InterlockedCompareExchange64(Destination,Old | Value,Old)!=Old);
-    return Old;
-  }
-#endif
-
-#ifndef InterlockedXor64
-#define InterlockedXor64 InterlockedXor64_Inline
-
-  __CRT_INLINE LONGLONG InterlockedXor64_Inline (LONGLONG volatile *Destination,LONGLONG Value) {
-    LONGLONG Old;
-    do {
-      Old = *Destination;
-    } while(InterlockedCompareExchange64(Destination,Old ^ Value,Old)!=Old);
-    return Old;
-  }
-#endif
-
-#ifndef InterlockedBitTestAndSet
-#define InterlockedBitTestAndSet InterlockedBitTestAndSet_Inline
-
-  __CRT_INLINE BOOLEAN InterlockedBitTestAndSet_Inline(LONG *Base,LONG Bit) {
-    LONG tBit;
-    tBit = 1<<(Bit & (sizeof (*Base)*8-1));
-    return (BOOLEAN)((InterlockedOr(&Base[Bit/(sizeof(*Base)*8)],tBit)&tBit)!=0);
-  }
-#endif
-
-#ifndef InterlockedBitTestAndReset
-#define InterlockedBitTestAndReset InterlockedBitTestAndReset_Inline
-
-  __CRT_INLINE BOOLEAN InterlockedBitTestAndReset_Inline(LONG *Base,LONG Bit) {
-    LONG tBit;
-    tBit = 1<<(Bit & (sizeof (*Base)*8-1));
-    return (BOOLEAN)((InterlockedAnd(&Base[Bit/(sizeof(*Base)*8)],~tBit)&tBit)!=0);
-  }
-#endif
-
-#ifndef InterlockedBitTestAndComplement
-#define InterlockedBitTestAndComplement InterlockedBitTestAndComplement_Inline
-
-  __CRT_INLINE BOOLEAN InterlockedBitTestAndComplement_Inline(LONG *Base,LONG Bit) {
-    LONG tBit;
-    tBit = 1<<(Bit & (sizeof (*Base)*8-1));
-    return (BOOLEAN)((InterlockedXor(&Base[Bit/(sizeof(*Base)*8)],tBit)&tBit)!=0);
-  }
-#endif
-#elif defined(__x86_64) && !defined(RC_INVOKED)
-
-#define InterlockedIncrement _InterlockedIncrement
-#define InterlockedIncrementAcquire InterlockedIncrement
-#define InterlockedIncrementRelease InterlockedIncrement
-#define InterlockedDecrement _InterlockedDecrement
-#define InterlockedDecrementAcquire InterlockedDecrement
-#define InterlockedDecrementRelease InterlockedDecrement
-#define InterlockedExchange _InterlockedExchange
-#define InterlockedExchangeAdd _InterlockedExchangeAdd
-#define InterlockedCompareExchange _InterlockedCompareExchange
-#define InterlockedCompareExchangeAcquire InterlockedCompareExchange
-#define InterlockedCompareExchangeRelease InterlockedCompareExchange
-#define InterlockedExchangePointer _InterlockedExchangePointer
-#define InterlockedCompareExchangePointer _InterlockedCompareExchangePointer
-#define InterlockedCompareExchangePointerAcquire _InterlockedCompareExchangePointer
-#define InterlockedCompareExchangePointerRelease _InterlockedCompareExchangePointer
-#define InterlockedAnd64 _InterlockedAnd64
-#define InterlockedOr64 _InterlockedOr64
-#define InterlockedXor64 _InterlockedXor64
-#define InterlockedIncrement64 _InterlockedIncrement64
-#define InterlockedDecrement64 _InterlockedDecrement64
-#define InterlockedExchange64 _InterlockedExchange64
-#define InterlockedExchangeAdd64 _InterlockedExchangeAdd64
-#define InterlockedCompareExchange64 _InterlockedCompareExchange64
-#define InterlockedCompareExchangeAcquire64 InterlockedCompareExchange64
-#define InterlockedCompareExchangeRelease64 InterlockedCompareExchange64
-
-  LONG InterlockedIncrement(LONG volatile *Addend);
-  LONG InterlockedDecrement(LONG volatile *Addend);
-  LONG InterlockedExchange(LONG volatile *Target,LONG Value);
-  LONG InterlockedExchangeAdd(LONG volatile *Addend,LONG Value);
-  LONG InterlockedCompareExchange(LONG volatile *Destination,LONG ExChange,LONG Comperand);
-  PVOID InterlockedCompareExchangePointer(PVOID volatile *Destination,PVOID Exchange,PVOID Comperand);
-  PVOID InterlockedExchangePointer(PVOID volatile *Target,PVOID Value);
-  LONG64 InterlockedAnd64(LONG64 volatile *Destination,LONG64 Value);
-  LONG64 InterlockedOr64(LONG64 volatile *Destination,LONG64 Value);
-  LONG64 InterlockedXor64(LONG64 volatile *Destination,LONG64 Value);
-  LONG64 InterlockedIncrement64(LONG64 volatile *Addend);
-  LONG64 InterlockedDecrement64(LONG64 volatile *Addend);
-  LONG64 InterlockedExchange64(LONG64 volatile *Target,LONG64 Value);
-  LONG64 InterlockedExchangeAdd64(LONG64 volatile *Addend,LONG64 Value);
-  LONG64 InterlockedCompareExchange64(LONG64 volatile *Destination,LONG64 ExChange,LONG64 Comperand);
-#else
-  LONG WINAPI InterlockedIncrement(LONG volatile *lpAddend);
-  LONG WINAPI InterlockedDecrement(LONG volatile *lpAddend);
-  LONG WINAPI InterlockedExchange(LONG volatile *Target,LONG Value);
-
-#define InterlockedExchangePointer(Target,Value) (PVOID)InterlockedExchange((PLONG)(Target),(LONG)(Value))
-
-  LONG WINAPI InterlockedExchangeAdd(LONG volatile *Addend,LONG Value);
-  LONG WINAPI InterlockedCompareExchange(LONG volatile *Destination,LONG Exchange,LONG Comperand);
-  LONGLONG WINAPI InterlockedCompareExchange64(LONGLONG volatile *Destination,LONGLONG Exchange,LONGLONG Comperand);
-
-  __CRT_INLINE LONGLONG InterlockedAnd64 (LONGLONG volatile *Destination,LONGLONG Value) {
-    LONGLONG Old;
-    do {
-      Old = *Destination;
-    } while(InterlockedCompareExchange64(Destination,Old & Value,Old)!=Old);
-    return Old;
-  }
-
-  __CRT_INLINE LONGLONG InterlockedOr64 (LONGLONG volatile *Destination,LONGLONG Value) {
-    LONGLONG Old;
-    do {
-      Old = *Destination;
-    } while(InterlockedCompareExchange64(Destination,Old | Value,Old)!=Old);
-    return Old;
-  }
-
-  __CRT_INLINE LONGLONG InterlockedXor64 (LONGLONG volatile *Destination,LONGLONG Value) {
-    LONGLONG Old;
-    do {
-      Old = *Destination;
-    } while(InterlockedCompareExchange64(Destination,Old ^ Value,Old)!=Old);
-
-    return Old;
-  }
-
-  __CRT_INLINE LONGLONG InterlockedIncrement64(LONGLONG volatile *Addend) {
-    LONGLONG Old;
-    do {
-      Old = *Addend;
-    } while(InterlockedCompareExchange64(Addend,Old + 1,Old)!=Old);
-    return Old + 1;
-  }
-
-  __CRT_INLINE LONGLONG InterlockedDecrement64(LONGLONG volatile *Addend) {
-    LONGLONG Old;
-    do {
-      Old = *Addend;
-    } while(InterlockedCompareExchange64(Addend,Old - 1,Old)!=Old);
-    return Old - 1;
-  }
-
-  __CRT_INLINE LONGLONG InterlockedExchange64(LONGLONG volatile *Target,LONGLONG Value) {
-    LONGLONG Old;
-    do {
-      Old = *Target;
-    } while(InterlockedCompareExchange64(Target,Value,Old)!=Old);
-    return Old;
-  }
-
-  __CRT_INLINE LONGLONG InterlockedExchangeAdd64(LONGLONG volatile *Addend,LONGLONG Value) {
-    LONGLONG Old;
-    do {
-      Old = *Addend;
-    } while(InterlockedCompareExchange64(Addend,Old + Value,Old)!=Old);
-    return Old;
-  }
-
-#ifdef __cplusplus
-  __CRT_INLINE PVOID __cdecl __InlineInterlockedCompareExchangePointer(PVOID volatile *Destination,PVOID ExChange,PVOID Comperand) {
-    return((PVOID)(LONG_PTR)InterlockedCompareExchange((LONG volatile *)Destination,(LONG)(LONG_PTR)ExChange,(LONG)(LONG_PTR)Comperand));
-  }
-#define InterlockedCompareExchangePointer __InlineInterlockedCompareExchangePointer
-#else
-#define InterlockedCompareExchangePointer(Destination,ExChange,Comperand)(PVOID)(LONG_PTR)InterlockedCompareExchange((LONG volatile *)(Destination),(LONG)(LONG_PTR)(ExChange),(LONG)(LONG_PTR)(Comperand))
-#endif
-
-#define InterlockedIncrementAcquire InterlockedIncrement
-#define InterlockedIncrementRelease InterlockedIncrement
-#define InterlockedDecrementAcquire InterlockedDecrement
-#define InterlockedDecrementRelease InterlockedDecrement
-#define InterlockedIncrementAcquire InterlockedIncrement
-#define InterlockedIncrementRelease InterlockedIncrement
-#define InterlockedCompareExchangeAcquire InterlockedCompareExchange
-#define InterlockedCompareExchangeRelease InterlockedCompareExchange
-#define InterlockedCompareExchangeAcquire64 InterlockedCompareExchange64
-#define InterlockedCompareExchangeRelease64 InterlockedCompareExchange64
-#define InterlockedCompareExchangePointerAcquire InterlockedCompareExchangePointer
-#define InterlockedCompareExchangePointerRelease InterlockedCompareExchangePointer
-#endif
-
-#if defined(_SLIST_HEADER_) && !defined(_NTOSP_)
-  WINBASEAPI VOID WINAPI InitializeSListHead(PSLIST_HEADER ListHead);
-  WINBASEAPI PSLIST_ENTRY WINAPI InterlockedPopEntrySList(PSLIST_HEADER ListHead);
-  WINBASEAPI PSLIST_ENTRY WINAPI InterlockedPushEntrySList(PSLIST_HEADER ListHead,PSLIST_ENTRY ListEntry);
-  WINBASEAPI PSLIST_ENTRY WINAPI InterlockedFlushSList(PSLIST_HEADER ListHead);
-  WINBASEAPI USHORT WINAPI QueryDepthSList(PSLIST_HEADER ListHead);
-#endif
-#endif
-#endif
-
-  WINBASEAPI WINBOOL WINAPI FreeResource(HGLOBAL hResData);
-  WINBASEAPI LPVOID WINAPI LockResource(HGLOBAL hResData);
-
-#define UnlockResource(hResData) ((hResData),0)
-#define MAXINTATOM 0xC000
-#define MAKEINTATOM(i) (LPTSTR)((ULONG_PTR)((WORD)(i)))
-#define INVALID_ATOM ((ATOM)0)
-
-  int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd);
-  WINBASEAPI WINBOOL WINAPI FreeLibrary(HMODULE hLibModule);
-  WINBASEAPI DECLSPEC_NORETURN VOID WINAPI FreeLibraryAndExitThread(HMODULE hLibModule,DWORD dwExitCode);
-  WINBASEAPI WINBOOL WINAPI DisableThreadLibraryCalls(HMODULE hLibModule);
-  WINBASEAPI FARPROC WINAPI GetProcAddress(HMODULE hModule,LPCSTR lpProcName);
-  WINBASEAPI DWORD WINAPI GetVersion(VOID);
-  WINBASEAPI HGLOBAL WINAPI GlobalAlloc(UINT uFlags,SIZE_T dwBytes);
-  WINBASEAPI HGLOBAL WINAPI GlobalReAlloc(HGLOBAL hMem,SIZE_T dwBytes,UINT uFlags);
-  WINBASEAPI SIZE_T WINAPI GlobalSize(HGLOBAL hMem);
-  WINBASEAPI UINT WINAPI GlobalFlags(HGLOBAL hMem);
-  WINBASEAPI LPVOID WINAPI GlobalLock(HGLOBAL hMem);
-  WINBASEAPI HGLOBAL WINAPI GlobalHandle(LPCVOID pMem);
-  WINBASEAPI WINBOOL WINAPI GlobalUnlock(HGLOBAL hMem);
-  WINBASEAPI HGLOBAL WINAPI GlobalFree(HGLOBAL hMem);
-  WINBASEAPI SIZE_T WINAPI GlobalCompact(DWORD dwMinFree);
-  WINBASEAPI VOID WINAPI GlobalFix(HGLOBAL hMem);
-  WINBASEAPI VOID WINAPI GlobalUnfix(HGLOBAL hMem);
-  WINBASEAPI LPVOID WINAPI GlobalWire(HGLOBAL hMem);
-  WINBASEAPI WINBOOL WINAPI GlobalUnWire(HGLOBAL hMem);
-  WINBASEAPI VOID WINAPI GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer);
-
-  typedef struct _MEMORYSTATUSEX {
-    DWORD dwLength;
-    DWORD dwMemoryLoad;
-    DWORDLONG ullTotalPhys;
-    DWORDLONG ullAvailPhys;
-    DWORDLONG ullTotalPageFile;
-    DWORDLONG ullAvailPageFile;
-    DWORDLONG ullTotalVirtual;
-    DWORDLONG ullAvailVirtual;
-    DWORDLONG ullAvailExtendedVirtual;
-  } MEMORYSTATUSEX,*LPMEMORYSTATUSEX;
-
-  WINBASEAPI WINBOOL WINAPI GlobalMemoryStatusEx(LPMEMORYSTATUSEX lpBuffer);
-  WINBASEAPI HLOCAL WINAPI LocalAlloc(UINT uFlags,SIZE_T uBytes);
-  WINBASEAPI HLOCAL WINAPI LocalReAlloc(HLOCAL hMem,SIZE_T uBytes,UINT uFlags);
-  WINBASEAPI LPVOID WINAPI LocalLock(HLOCAL hMem);
-  WINBASEAPI HLOCAL WINAPI LocalHandle(LPCVOID pMem);
-  WINBASEAPI WINBOOL WINAPI LocalUnlock(HLOCAL hMem);
-  WINBASEAPI SIZE_T WINAPI LocalSize(HLOCAL hMem);
-  WINBASEAPI UINT WINAPI LocalFlags(HLOCAL hMem);
-  WINBASEAPI HLOCAL WINAPI LocalFree(HLOCAL hMem);
-  WINBASEAPI SIZE_T WINAPI LocalShrink(HLOCAL hMem,UINT cbNewSize);
-  WINBASEAPI SIZE_T WINAPI LocalCompact(UINT uMinFree);
-  WINBASEAPI WINBOOL WINAPI FlushInstructionCache(HANDLE hProcess,LPCVOID lpBaseAddress,SIZE_T dwSize);
-  WINBASEAPI LPVOID WINAPI VirtualAlloc(LPVOID lpAddress,SIZE_T dwSize,DWORD flAllocationType,DWORD flProtect);
-  WINBASEAPI WINBOOL WINAPI VirtualFree(LPVOID lpAddress,SIZE_T dwSize,DWORD dwFreeType);
-  WINBASEAPI WINBOOL WINAPI VirtualProtect(LPVOID lpAddress,SIZE_T dwSize,DWORD flNewProtect,PDWORD lpflOldProtect);
-  WINBASEAPI SIZE_T WINAPI VirtualQuery(LPCVOID lpAddress,PMEMORY_BASIC_INFORMATION lpBuffer,SIZE_T dwLength);
-  WINBASEAPI LPVOID WINAPI VirtualAllocEx(HANDLE hProcess,LPVOID lpAddress,SIZE_T dwSize,DWORD flAllocationType,DWORD flProtect);
-  WINBASEAPI UINT WINAPI GetWriteWatch(DWORD dwFlags,PVOID lpBaseAddress,SIZE_T dwRegionSize,PVOID *lpAddresses,ULONG_PTR *lpdwCount,PULONG lpdwGranularity);
-  WINBASEAPI UINT WINAPI ResetWriteWatch(LPVOID lpBaseAddress,SIZE_T dwRegionSize);
-  WINBASEAPI SIZE_T WINAPI GetLargePageMinimum(VOID);
-  WINBASEAPI UINT WINAPI EnumSystemFirmwareTables(DWORD FirmwareTableProviderSignature,PVOID pFirmwareTableEnumBuffer,DWORD BufferSize);
-  WINBASEAPI UINT WINAPI GetSystemFirmwareTable(DWORD FirmwareTableProviderSignature,DWORD FirmwareTableID,PVOID pFirmwareTableBuffer,DWORD BufferSize);
-  WINBASEAPI WINBOOL WINAPI VirtualFreeEx(HANDLE hProcess,LPVOID lpAddress,SIZE_T dwSize,DWORD dwFreeType);
-  WINBASEAPI WINBOOL WINAPI VirtualProtectEx(HANDLE hProcess,LPVOID lpAddress,SIZE_T dwSize,DWORD flNewProtect,PDWORD lpflOldProtect);
-  WINBASEAPI SIZE_T WINAPI VirtualQueryEx(HANDLE hProcess,LPCVOID lpAddress,PMEMORY_BASIC_INFORMATION lpBuffer,SIZE_T dwLength);
-  WINBASEAPI HANDLE WINAPI HeapCreate(DWORD flOptions,SIZE_T dwInitialSize,SIZE_T dwMaximumSize);
-  WINBASEAPI WINBOOL WINAPI HeapDestroy(HANDLE hHeap);
-  WINBASEAPI LPVOID WINAPI HeapAlloc(HANDLE hHeap,DWORD dwFlags,SIZE_T dwBytes);
-  WINBASEAPI LPVOID WINAPI HeapReAlloc(HANDLE hHeap,DWORD dwFlags,LPVOID lpMem,SIZE_T dwBytes);
-  WINBASEAPI WINBOOL WINAPI HeapFree(HANDLE hHeap,DWORD dwFlags,LPVOID lpMem);
-  WINBASEAPI SIZE_T WINAPI HeapSize(HANDLE hHeap,DWORD dwFlags,LPCVOID lpMem);
-  WINBASEAPI WINBOOL WINAPI HeapValidate(HANDLE hHeap,DWORD dwFlags,LPCVOID lpMem);
-  WINBASEAPI SIZE_T WINAPI HeapCompact(HANDLE hHeap,DWORD dwFlags);
-  WINBASEAPI HANDLE WINAPI GetProcessHeap(VOID);
-  WINBASEAPI DWORD WINAPI GetProcessHeaps(DWORD NumberOfHeaps,PHANDLE ProcessHeaps);
-
-  typedef struct _PROCESS_HEAP_ENTRY {
-    PVOID lpData;
-    DWORD cbData;
-    BYTE cbOverhead;
-    BYTE iRegionIndex;
-    WORD wFlags;
-    union {
-      struct {
-	HANDLE hMem;
-	DWORD dwReserved[3];
-      } Block;
-      struct {
-	DWORD dwCommittedSize;
-	DWORD dwUnCommittedSize;
-	LPVOID lpFirstBlock;
-	LPVOID lpLastBlock;
-      } Region;
-    };
-  } PROCESS_HEAP_ENTRY,*LPPROCESS_HEAP_ENTRY,*PPROCESS_HEAP_ENTRY;
-
-#define PROCESS_HEAP_REGION 0x1
-#define PROCESS_HEAP_UNCOMMITTED_RANGE 0x2
-#define PROCESS_HEAP_ENTRY_BUSY 0x4
-#define PROCESS_HEAP_ENTRY_MOVEABLE 0x10
-#define PROCESS_HEAP_ENTRY_DDESHARE 0x20
-
-  WINBASEAPI WINBOOL WINAPI HeapLock(HANDLE hHeap);
-  WINBASEAPI WINBOOL WINAPI HeapUnlock(HANDLE hHeap);
-  WINBASEAPI WINBOOL WINAPI HeapWalk(HANDLE hHeap,LPPROCESS_HEAP_ENTRY lpEntry);
-  WINBASEAPI WINBOOL WINAPI HeapSetInformation(HANDLE HeapHandle,HEAP_INFORMATION_CLASS HeapInformationClass,PVOID HeapInformation,SIZE_T HeapInformationLength);
-  WINBASEAPI WINBOOL WINAPI HeapQueryInformation(HANDLE HeapHandle,HEAP_INFORMATION_CLASS HeapInformationClass,PVOID HeapInformation,SIZE_T HeapInformationLength,PSIZE_T ReturnLength);
-
-#define SCS_32BIT_BINARY 0
-#define SCS_DOS_BINARY 1
-#define SCS_WOW_BINARY 2
-#define SCS_PIF_BINARY 3
-#define SCS_POSIX_BINARY 4
-#define SCS_OS216_BINARY 5
-#define SCS_64BIT_BINARY 6
-
-#ifdef UNICODE
-#define GetBinaryType GetBinaryTypeW
-#define GetShortPathName GetShortPathNameW
-#define GetLongPathName GetLongPathNameW
-#define GetEnvironmentStrings GetEnvironmentStringsW
-#define SetEnvironmentStrings SetEnvironmentStringsW
-#define FreeEnvironmentStrings FreeEnvironmentStringsW
-#else
-#define GetBinaryType GetBinaryTypeA
-#define GetShortPathName GetShortPathNameA
-#define GetLongPathName GetLongPathNameA
-#define GetEnvironmentStringsA GetEnvironmentStrings
-#define SetEnvironmentStrings SetEnvironmentStringsA
-#define FreeEnvironmentStrings FreeEnvironmentStringsA
-#endif
-
-#ifdef _WIN64
-#define SCS_THIS_PLATFORM_BINARY SCS_64BIT_BINARY
-#else
-#define SCS_THIS_PLATFORM_BINARY SCS_32BIT_BINARY
-#endif
-
-  WINBASEAPI WINBOOL WINAPI GetBinaryTypeA(LPCSTR lpApplicationName,LPDWORD lpBinaryType);
-  WINBASEAPI WINBOOL WINAPI GetBinaryTypeW(LPCWSTR lpApplicationName,LPDWORD lpBinaryType);
-  WINBASEAPI DWORD WINAPI GetShortPathNameA(LPCSTR lpszLongPath,LPSTR lpszShortPath,DWORD cchBuffer);
-  WINBASEAPI DWORD WINAPI GetShortPathNameW(LPCWSTR lpszLongPath,LPWSTR lpszShortPath,DWORD cchBuffer);
-  WINBASEAPI DWORD WINAPI GetLongPathNameA(LPCSTR lpszShortPath,LPSTR lpszLongPath,DWORD cchBuffer);
-  WINBASEAPI DWORD WINAPI GetLongPathNameW(LPCWSTR lpszShortPath,LPWSTR lpszLongPath,DWORD cchBuffer);
-  WINBASEAPI WINBOOL WINAPI GetProcessAffinityMask(HANDLE hProcess,PDWORD_PTR lpProcessAffinityMask,PDWORD_PTR lpSystemAffinityMask);
-  WINBASEAPI WINBOOL WINAPI SetProcessAffinityMask(HANDLE hProcess,DWORD_PTR dwProcessAffinityMask);
-  WINBASEAPI WINBOOL WINAPI GetProcessHandleCount(HANDLE hProcess,PDWORD pdwHandleCount);
-  WINBASEAPI WINBOOL WINAPI GetProcessTimes(HANDLE hProcess,LPFILETIME lpCreationTime,LPFILETIME lpExitTime,LPFILETIME lpKernelTime,LPFILETIME lpUserTime);
-  WINBASEAPI WINBOOL WINAPI GetProcessIoCounters(HANDLE hProcess,PIO_COUNTERS lpIoCounters);
-  WINBASEAPI WINBOOL WINAPI GetProcessWorkingSetSize(HANDLE hProcess,PSIZE_T lpMinimumWorkingSetSize,PSIZE_T lpMaximumWorkingSetSize);
-  WINBASEAPI WINBOOL WINAPI GetProcessWorkingSetSizeEx(HANDLE hProcess,PSIZE_T lpMinimumWorkingSetSize,PSIZE_T lpMaximumWorkingSetSize,PDWORD Flags);
-  WINBASEAPI WINBOOL WINAPI SetProcessWorkingSetSize(HANDLE hProcess,SIZE_T dwMinimumWorkingSetSize,SIZE_T dwMaximumWorkingSetSize);
-  WINBASEAPI WINBOOL WINAPI SetProcessWorkingSetSizeEx(HANDLE hProcess,SIZE_T dwMinimumWorkingSetSize,SIZE_T dwMaximumWorkingSetSize,DWORD Flags);
-  WINBASEAPI HANDLE WINAPI OpenProcess(DWORD dwDesiredAccess,WINBOOL bInheritHandle,DWORD dwProcessId);
-  WINBASEAPI HANDLE WINAPI GetCurrentProcess(VOID);
-  WINBASEAPI DWORD WINAPI GetCurrentProcessId(VOID);
-  WINBASEAPI DECLSPEC_NORETURN VOID WINAPI ExitProcess(UINT uExitCode);
-  WINBASEAPI WINBOOL WINAPI TerminateProcess(HANDLE hProcess,UINT uExitCode);
-  WINBASEAPI WINBOOL WINAPI GetExitCodeProcess(HANDLE hProcess,LPDWORD lpExitCode);
-  WINBASEAPI VOID WINAPI FatalExit(int ExitCode);
-  /*	WINBASEAPI LPCH WINAPI GetEnvironmentStrings(VOID); */
-  WINBASEAPI LPWCH WINAPI GetEnvironmentStringsW(VOID);
-  WINBASEAPI WINBOOL WINAPI SetEnvironmentStringsA(LPCH NewEnvironment);
-  WINBASEAPI WINBOOL WINAPI SetEnvironmentStringsW(LPWCH NewEnvironment);
-  WINBASEAPI WINBOOL WINAPI FreeEnvironmentStringsA(LPCH);
-  WINBASEAPI WINBOOL WINAPI FreeEnvironmentStringsW(LPWCH);
-  WINBASEAPI VOID WINAPI RaiseException(DWORD dwExceptionCode,DWORD dwExceptionFlags,DWORD nNumberOfArguments,CONST ULONG_PTR *lpArguments);
-  WINBASEAPI LONG WINAPI UnhandledExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo);
-
-  typedef LONG (WINAPI *PTOP_LEVEL_EXCEPTION_FILTER)(struct _EXCEPTION_POINTERS *ExceptionInfo);
-  typedef PTOP_LEVEL_EXCEPTION_FILTER LPTOP_LEVEL_EXCEPTION_FILTER;
-
-  WINBASEAPI LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
-
-#define FIBER_FLAG_FLOAT_SWITCH 0x1
-
-  WINBASEAPI LPVOID WINAPI CreateFiber(SIZE_T dwStackSize,LPFIBER_START_ROUTINE lpStartAddress,LPVOID lpParameter);
-  WINBASEAPI LPVOID WINAPI CreateFiberEx(SIZE_T dwStackCommitSize,SIZE_T dwStackReserveSize,DWORD dwFlags,LPFIBER_START_ROUTINE lpStartAddress,LPVOID lpParameter);
-  WINBASEAPI VOID WINAPI DeleteFiber(LPVOID lpFiber);
-  WINBASEAPI LPVOID WINAPI ConvertThreadToFiber(LPVOID lpParameter);
-  WINBASEAPI LPVOID WINAPI ConvertThreadToFiberEx(LPVOID lpParameter,DWORD dwFlags);
-  WINBASEAPI WINBOOL WINAPI ConvertFiberToThread(VOID);
-  WINBASEAPI VOID WINAPI SwitchToFiber(LPVOID lpFiber);
-  WINBASEAPI WINBOOL WINAPI SwitchToThread(VOID);
-  WINBASEAPI HANDLE WINAPI CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,SIZE_T dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId);
-  WINBASEAPI HANDLE WINAPI CreateRemoteThread(HANDLE hProcess,LPSECURITY_ATTRIBUTES lpThreadAttributes,SIZE_T dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId);
-  WINBASEAPI HANDLE WINAPI GetCurrentThread(VOID);
-  WINBASEAPI DWORD WINAPI GetCurrentThreadId(VOID);
-  WINBASEAPI WINBOOL WINAPI SetThreadStackGuarantee (PULONG StackSizeInBytes);
-  WINBASEAPI DWORD WINAPI GetProcessIdOfThread(HANDLE Thread);
-  WINBASEAPI DWORD WINAPI GetThreadId(HANDLE Thread);
-  WINBASEAPI DWORD WINAPI GetProcessId(HANDLE Process);
-  WINBASEAPI DWORD WINAPI GetCurrentProcessorNumber(VOID);
-  WINBASEAPI DWORD_PTR WINAPI SetThreadAffinityMask(HANDLE hThread,DWORD_PTR dwThreadAffinityMask);
-  WINBASEAPI DWORD WINAPI SetThreadIdealProcessor(HANDLE hThread,DWORD dwIdealProcessor);
-  WINBASEAPI WINBOOL WINAPI SetProcessPriorityBoost(HANDLE hProcess,WINBOOL bDisablePriorityBoost);
-  WINBASEAPI WINBOOL WINAPI GetProcessPriorityBoost(HANDLE hProcess,PBOOL pDisablePriorityBoost);
-  WINBASEAPI WINBOOL WINAPI RequestWakeupLatency(LATENCY_TIME latency);
-  WINBASEAPI WINBOOL WINAPI IsSystemResumeAutomatic(VOID);
-  WINBASEAPI HANDLE WINAPI OpenThread(DWORD dwDesiredAccess,WINBOOL bInheritHandle,DWORD dwThreadId);
-  WINBASEAPI WINBOOL WINAPI SetThreadPriority(HANDLE hThread,int nPriority);
-  WINBASEAPI WINBOOL WINAPI SetThreadPriorityBoost(HANDLE hThread,WINBOOL bDisablePriorityBoost);
-  WINBASEAPI WINBOOL WINAPI GetThreadPriorityBoost(HANDLE hThread,PBOOL pDisablePriorityBoost);
-  WINBASEAPI int WINAPI GetThreadPriority(HANDLE hThread);
-  WINBASEAPI WINBOOL WINAPI GetThreadTimes(HANDLE hThread,LPFILETIME lpCreationTime,LPFILETIME lpExitTime,LPFILETIME lpKernelTime,LPFILETIME lpUserTime);
-  WINBASEAPI WINBOOL WINAPI GetThreadIOPendingFlag(HANDLE hThread,PBOOL lpIOIsPending);
-  WINBASEAPI DECLSPEC_NORETURN VOID WINAPI ExitThread(DWORD dwExitCode);
-  WINBASEAPI WINBOOL WINAPI TerminateThread(HANDLE hThread,DWORD dwExitCode);
-  WINBASEAPI WINBOOL WINAPI GetExitCodeThread(HANDLE hThread,LPDWORD lpExitCode);
-  WINBASEAPI WINBOOL WINAPI GetThreadSelectorEntry(HANDLE hThread,DWORD dwSelector,LPLDT_ENTRY lpSelectorEntry);
-  WINBASEAPI EXECUTION_STATE WINAPI SetThreadExecutionState(EXECUTION_STATE esFlags);
-  WINBASEAPI DWORD WINAPI GetLastError(VOID);
-  WINBASEAPI VOID WINAPI SetLastError(DWORD dwErrCode);
-
-#ifndef RC_INVOKED
-#ifdef WINBASE_DECLARE_RESTORE_LAST_ERROR
-  WINBASEAPI VOID WINAPI RestoreLastError(DWORD dwErrCode);
-
-  typedef VOID (WINAPI *PRESTORE_LAST_ERROR)(DWORD);
-
-#define RESTORE_LAST_ERROR_NAME_A "RestoreLastError"
-#define RESTORE_LAST_ERROR_NAME_W L"RestoreLastError"
-#define RESTORE_LAST_ERROR_NAME TEXT("RestoreLastError")
-#endif
-#endif
-
-#define HasOverlappedIoCompleted(lpOverlapped) (((DWORD)(lpOverlapped)->Internal)!=STATUS_PENDING)
-
-  WINBASEAPI WINBOOL WINAPI GetOverlappedResult(HANDLE hFile,LPOVERLAPPED lpOverlapped,LPDWORD lpNumberOfBytesTransferred,WINBOOL bWait);
-  WINBASEAPI HANDLE WINAPI CreateIoCompletionPort(HANDLE FileHandle,HANDLE ExistingCompletionPort,ULONG_PTR CompletionKey,DWORD NumberOfConcurrentThreads);
-  WINBASEAPI WINBOOL WINAPI GetQueuedCompletionStatus(HANDLE CompletionPort,LPDWORD lpNumberOfBytesTransferred,PULONG_PTR lpCompletionKey,LPOVERLAPPED *lpOverlapped,DWORD dwMilliseconds);
-  WINBASEAPI WINBOOL WINAPI PostQueuedCompletionStatus(HANDLE CompletionPort,DWORD dwNumberOfBytesTransferred,ULONG_PTR dwCompletionKey,LPOVERLAPPED lpOverlapped);
-
-#define SEM_FAILCRITICALERRORS 0x1
-#define SEM_NOGPFAULTERRORBOX 0x2
-#define SEM_NOALIGNMENTFAULTEXCEPT 0x4
-#define SEM_NOOPENFILEERRORBOX 0x8000
-
-  WINBASEAPI UINT WINAPI SetErrorMode(UINT uMode);
-  WINBASEAPI WINBOOL WINAPI ReadProcessMemory(HANDLE hProcess,LPCVOID lpBaseAddress,LPVOID lpBuffer,SIZE_T nSize,SIZE_T *lpNumberOfBytesRead);
-  WINBASEAPI WINBOOL WINAPI WriteProcessMemory(HANDLE hProcess,LPVOID lpBaseAddress,LPCVOID lpBuffer,SIZE_T nSize,SIZE_T *lpNumberOfBytesWritten);
-  WINBASEAPI WINBOOL WINAPI GetThreadContext(HANDLE hThread,LPCONTEXT lpContext);
-  WINBASEAPI WINBOOL WINAPI SetThreadContext(HANDLE hThread,CONST CONTEXT *lpContext);
-  WINBASEAPI DWORD WINAPI SuspendThread(HANDLE hThread);
-  WINBASEAPI DWORD WINAPI ResumeThread(HANDLE hThread);
-
-  typedef VOID (WINAPI *PAPCFUNC)(ULONG_PTR dwParam);
-
-  WINBASEAPI DWORD WINAPI QueueUserAPC(PAPCFUNC pfnAPC,HANDLE hThread,ULONG_PTR dwData);
-  WINBASEAPI WINBOOL WINAPI IsDebuggerPresent(VOID);
-  WINBASEAPI WINBOOL WINAPI CheckRemoteDebuggerPresent(HANDLE hProcess,PBOOL pbDebuggerPresent);
-  WINBASEAPI VOID WINAPI DebugBreak(VOID);
-  WINBASEAPI WINBOOL WINAPI WaitForDebugEvent(LPDEBUG_EVENT lpDebugEvent,DWORD dwMilliseconds);
-  WINBASEAPI WINBOOL WINAPI ContinueDebugEvent(DWORD dwProcessId,DWORD dwThreadId,DWORD dwContinueStatus);
-  WINBASEAPI WINBOOL WINAPI DebugActiveProcess(DWORD dwProcessId);
-  WINBASEAPI WINBOOL WINAPI DebugActiveProcessStop(DWORD dwProcessId);
-  WINBASEAPI WINBOOL WINAPI DebugSetProcessKillOnExit(WINBOOL KillOnExit);
-  WINBASEAPI WINBOOL WINAPI DebugBreakProcess(HANDLE Process);
-  WINBASEAPI VOID WINAPI InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
-  WINBASEAPI VOID WINAPI EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
-  WINBASEAPI VOID WINAPI LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
-  WINBASEAPI WINBOOL WINAPI InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection,DWORD dwSpinCount);
-  WINBASEAPI DWORD WINAPI SetCriticalSectionSpinCount(LPCRITICAL_SECTION lpCriticalSection,DWORD dwSpinCount);
-  WINBASEAPI WINBOOL WINAPI TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
-  WINBASEAPI VOID WINAPI DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
-  WINBASEAPI WINBOOL WINAPI SetEvent(HANDLE hEvent);
-  WINBASEAPI WINBOOL WINAPI ResetEvent(HANDLE hEvent);
-  WINBASEAPI WINBOOL WINAPI PulseEvent(HANDLE hEvent);
-  WINBASEAPI WINBOOL WINAPI ReleaseSemaphore(HANDLE hSemaphore,LONG lReleaseCount,LPLONG lpPreviousCount);
-  WINBASEAPI WINBOOL WINAPI ReleaseMutex(HANDLE hMutex);
-  WINBASEAPI DWORD WINAPI WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);
-  WINBASEAPI DWORD WINAPI WaitForMultipleObjects(DWORD nCount,CONST HANDLE *lpHandles,WINBOOL bWaitAll,DWORD dwMilliseconds);
-  WINBASEAPI VOID WINAPI Sleep(DWORD dwMilliseconds);
-  WINBASEAPI HGLOBAL WINAPI LoadResource(HMODULE hModule,HRSRC hResInfo);
-  WINBASEAPI DWORD WINAPI SizeofResource(HMODULE hModule,HRSRC hResInfo);
-  WINBASEAPI ATOM WINAPI GlobalDeleteAtom(ATOM nAtom);
-  WINBASEAPI WINBOOL WINAPI InitAtomTable(DWORD nSize);
-  WINBASEAPI ATOM WINAPI DeleteAtom(ATOM nAtom);
-  WINBASEAPI UINT WINAPI SetHandleCount(UINT uNumber);
-  WINBASEAPI DWORD WINAPI GetLogicalDrives(VOID);
-  WINBASEAPI WINBOOL WINAPI LockFile(HANDLE hFile,DWORD dwFileOffsetLow,DWORD dwFileOffsetHigh,DWORD nNumberOfBytesToLockLow,DWORD nNumberOfBytesToLockHigh);
-  WINBASEAPI WINBOOL WINAPI UnlockFile(HANDLE hFile,DWORD dwFileOffsetLow,DWORD dwFileOffsetHigh,DWORD nNumberOfBytesToUnlockLow,DWORD nNumberOfBytesToUnlockHigh);
-  WINBASEAPI WINBOOL WINAPI LockFileEx(HANDLE hFile,DWORD dwFlags,DWORD dwReserved,DWORD nNumberOfBytesToLockLow,DWORD nNumberOfBytesToLockHigh,LPOVERLAPPED lpOverlapped);
-
-#define LOCKFILE_FAIL_IMMEDIATELY 0x1
-#define LOCKFILE_EXCLUSIVE_LOCK 0x2
-
-  WINBASEAPI WINBOOL WINAPI UnlockFileEx(HANDLE hFile,DWORD dwReserved,DWORD nNumberOfBytesToUnlockLow,DWORD nNumberOfBytesToUnlockHigh,LPOVERLAPPED lpOverlapped);
-
-  typedef struct _BY_HANDLE_FILE_INFORMATION {
-    DWORD dwFileAttributes;
-    FILETIME ftCreationTime;
-    FILETIME ftLastAccessTime;
-    FILETIME ftLastWriteTime;
-    DWORD dwVolumeSerialNumber;
-    DWORD nFileSizeHigh;
-    DWORD nFileSizeLow;
-    DWORD nNumberOfLinks;
-    DWORD nFileIndexHigh;
-    DWORD nFileIndexLow;
-  } BY_HANDLE_FILE_INFORMATION,*PBY_HANDLE_FILE_INFORMATION,*LPBY_HANDLE_FILE_INFORMATION;
-
-#ifdef UNICODE
-#define SetFileShortName SetFileShortNameW
-#else
-#define SetFileShortName SetFileShortNameA
-#endif
-
-  WINBASEAPI WINBOOL WINAPI GetFileInformationByHandle(HANDLE hFile,LPBY_HANDLE_FILE_INFORMATION lpFileInformation);
-  WINBASEAPI DWORD WINAPI GetFileType(HANDLE hFile);
-  WINBASEAPI DWORD WINAPI GetFileSize(HANDLE hFile,LPDWORD lpFileSizeHigh);
-  WINBASEAPI WINBOOL WINAPI GetFileSizeEx(HANDLE hFile,PLARGE_INTEGER lpFileSize);
-  WINBASEAPI HANDLE WINAPI GetStdHandle(DWORD nStdHandle);
-  WINBASEAPI WINBOOL WINAPI SetStdHandle(DWORD nStdHandle,HANDLE hHandle);
-  WINBASEAPI WINBOOL WINAPI WriteFile(HANDLE hFile,LPCVOID lpBuffer,DWORD nNumberOfBytesToWrite,LPDWORD lpNumberOfBytesWritten,LPOVERLAPPED lpOverlapped);
-  WINBASEAPI WINBOOL WINAPI ReadFile(HANDLE hFile,LPVOID lpBuffer,DWORD nNumberOfBytesToRead,LPDWORD lpNumberOfBytesRead,LPOVERLAPPED lpOverlapped);
-  WINBASEAPI WINBOOL WINAPI FlushFileBuffers(HANDLE hFile);
-  WINBASEAPI WINBOOL WINAPI DeviceIoControl(HANDLE hDevice,DWORD dwIoControlCode,LPVOID lpInBuffer,DWORD nInBufferSize,LPVOID lpOutBuffer,DWORD nOutBufferSize,LPDWORD lpBytesReturned,LPOVERLAPPED lpOverlapped);
-  WINBASEAPI WINBOOL WINAPI RequestDeviceWakeup(HANDLE hDevice);
-  WINBASEAPI WINBOOL WINAPI CancelDeviceWakeupRequest(HANDLE hDevice);
-  WINBASEAPI WINBOOL WINAPI GetDevicePowerState(HANDLE hDevice,WINBOOL *pfOn);
-  WINBASEAPI WINBOOL WINAPI SetMessageWaitingIndicator(HANDLE hMsgIndicator,ULONG ulMsgCount);
-  WINBASEAPI WINBOOL WINAPI SetEndOfFile(HANDLE hFile);
-  WINBASEAPI DWORD WINAPI SetFilePointer(HANDLE hFile,LONG lDistanceToMove,PLONG lpDistanceToMoveHigh,DWORD dwMoveMethod);
-  WINBASEAPI WINBOOL WINAPI SetFilePointerEx(HANDLE hFile,LARGE_INTEGER liDistanceToMove,PLARGE_INTEGER lpNewFilePointer,DWORD dwMoveMethod);
-  WINBASEAPI WINBOOL WINAPI FindClose(HANDLE hFindFile);
-  WINBASEAPI WINBOOL WINAPI GetFileTime(HANDLE hFile,LPFILETIME lpCreationTime,LPFILETIME lpLastAccessTime,LPFILETIME lpLastWriteTime);
-  WINBASEAPI WINBOOL WINAPI SetFileTime(HANDLE hFile,CONST FILETIME *lpCreationTime,CONST FILETIME *lpLastAccessTime,CONST FILETIME *lpLastWriteTime);
-  WINBASEAPI WINBOOL WINAPI SetFileValidData(HANDLE hFile,LONGLONG ValidDataLength);
-  WINBASEAPI WINBOOL WINAPI SetFileShortNameA(HANDLE hFile,LPCSTR lpShortName);
-  WINBASEAPI WINBOOL WINAPI SetFileShortNameW(HANDLE hFile,LPCWSTR lpShortName);
-  WINBASEAPI WINBOOL WINAPI CloseHandle(HANDLE hObject);
-  WINBASEAPI WINBOOL WINAPI DuplicateHandle(HANDLE hSourceProcessHandle,HANDLE hSourceHandle,HANDLE hTargetProcessHandle,LPHANDLE lpTargetHandle,DWORD dwDesiredAccess,WINBOOL bInheritHandle,DWORD dwOptions);
-  WINBASEAPI WINBOOL WINAPI GetHandleInformation(HANDLE hObject,LPDWORD lpdwFlags);
-  WINBASEAPI WINBOOL WINAPI SetHandleInformation(HANDLE hObject,DWORD dwMask,DWORD dwFlags);
-
-#define HANDLE_FLAG_INHERIT 0x1
-#define HANDLE_FLAG_PROTECT_FROM_CLOSE 0x2
-
-#define HINSTANCE_ERROR 32
-
-  WINBASEAPI DWORD WINAPI LoadModule(LPCSTR lpModuleName,LPVOID lpParameterBlock);
-  WINBASEAPI UINT WINAPI WinExec(LPCSTR lpCmdLine,UINT uCmdShow);
-  WINBASEAPI WINBOOL WINAPI ClearCommBreak(HANDLE hFile);
-  WINBASEAPI WINBOOL WINAPI ClearCommError(HANDLE hFile,LPDWORD lpErrors,LPCOMSTAT lpStat);
-  WINBASEAPI WINBOOL WINAPI SetupComm(HANDLE hFile,DWORD dwInQueue,DWORD dwOutQueue);
-  WINBASEAPI WINBOOL WINAPI EscapeCommFunction(HANDLE hFile,DWORD dwFunc);
-  WINBASEAPI WINBOOL WINAPI GetCommConfig(HANDLE hCommDev,LPCOMMCONFIG lpCC,LPDWORD lpdwSize);
-  WINBASEAPI WINBOOL WINAPI GetCommMask(HANDLE hFile,LPDWORD lpEvtMask);
-  WINBASEAPI WINBOOL WINAPI GetCommProperties(HANDLE hFile,LPCOMMPROP lpCommProp);
-  WINBASEAPI WINBOOL WINAPI GetCommModemStatus(HANDLE hFile,LPDWORD lpModemStat);
-  WINBASEAPI WINBOOL WINAPI GetCommState(HANDLE hFile,LPDCB lpDCB);
-  WINBASEAPI WINBOOL WINAPI GetCommTimeouts(HANDLE hFile,LPCOMMTIMEOUTS lpCommTimeouts);
-  WINBASEAPI WINBOOL WINAPI PurgeComm(HANDLE hFile,DWORD dwFlags);
-  WINBASEAPI WINBOOL WINAPI SetCommBreak(HANDLE hFile);
-  WINBASEAPI WINBOOL WINAPI SetCommConfig(HANDLE hCommDev,LPCOMMCONFIG lpCC,DWORD dwSize);
-  WINBASEAPI WINBOOL WINAPI SetCommMask(HANDLE hFile,DWORD dwEvtMask);
-  WINBASEAPI WINBOOL WINAPI SetCommState(HANDLE hFile,LPDCB lpDCB);
-  WINBASEAPI WINBOOL WINAPI SetCommTimeouts(HANDLE hFile,LPCOMMTIMEOUTS lpCommTimeouts);
-  WINBASEAPI WINBOOL WINAPI TransmitCommChar(HANDLE hFile,char cChar);
-  WINBASEAPI WINBOOL WINAPI WaitCommEvent(HANDLE hFile,LPDWORD lpEvtMask,LPOVERLAPPED lpOverlapped);
-  WINBASEAPI DWORD WINAPI SetTapePosition(HANDLE hDevice,DWORD dwPositionMethod,DWORD dwPartition,DWORD dwOffsetLow,DWORD dwOffsetHigh,WINBOOL bImmediate);
-  WINBASEAPI DWORD WINAPI GetTapePosition(HANDLE hDevice,DWORD dwPositionType,LPDWORD lpdwPartition,LPDWORD lpdwOffsetLow,LPDWORD lpdwOffsetHigh);
-  WINBASEAPI DWORD WINAPI PrepareTape(HANDLE hDevice,DWORD dwOperation,WINBOOL bImmediate);
-  WINBASEAPI DWORD WINAPI EraseTape(HANDLE hDevice,DWORD dwEraseType,WINBOOL bImmediate);
-  WINBASEAPI DWORD WINAPI CreateTapePartition(HANDLE hDevice,DWORD dwPartitionMethod,DWORD dwCount,DWORD dwSize);
-  WINBASEAPI DWORD WINAPI WriteTapemark(HANDLE hDevice,DWORD dwTapemarkType,DWORD dwTapemarkCount,WINBOOL bImmediate);
-  WINBASEAPI DWORD WINAPI GetTapeStatus(HANDLE hDevice);
-  WINBASEAPI DWORD WINAPI GetTapeParameters(HANDLE hDevice,DWORD dwOperation,LPDWORD lpdwSize,LPVOID lpTapeInformation);
-
-#define GET_TAPE_MEDIA_INFORMATION 0
-#define GET_TAPE_DRIVE_INFORMATION 1
-
-  WINBASEAPI DWORD WINAPI SetTapeParameters(HANDLE hDevice,DWORD dwOperation,LPVOID lpTapeInformation);
-
-#define SET_TAPE_MEDIA_INFORMATION 0
-#define SET_TAPE_DRIVE_INFORMATION 1
-
-  WINBASEAPI WINBOOL WINAPI Beep(DWORD dwFreq,DWORD dwDuration);
-  WINBASEAPI int WINAPI MulDiv(int nNumber,int nNumerator,int nDenominator);
-  WINBASEAPI VOID WINAPI GetSystemTime(LPSYSTEMTIME lpSystemTime);
-  WINBASEAPI VOID WINAPI GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime);
-  WINBASEAPI WINBOOL WINAPI SetSystemTime(CONST SYSTEMTIME *lpSystemTime);
-  WINBASEAPI VOID WINAPI GetLocalTime(LPSYSTEMTIME lpSystemTime);
-  WINBASEAPI WINBOOL WINAPI SetLocalTime(CONST SYSTEMTIME *lpSystemTime);
-  WINBASEAPI VOID WINAPI GetSystemInfo(LPSYSTEM_INFO lpSystemInfo);
-  WINBASEAPI WINBOOL WINAPI SetSystemFileCacheSize(SIZE_T MinimumFileCacheSize,SIZE_T MaximumFileCacheSize,DWORD Flags);
-  WINBASEAPI WINBOOL WINAPI GetSystemFileCacheSize(PSIZE_T lpMinimumFileCacheSize,PSIZE_T lpMaximumFileCacheSize,PDWORD lpFlags);
-  WINBASEAPI WINBOOL WINAPI GetSystemRegistryQuota(PDWORD pdwQuotaAllowed,PDWORD pdwQuotaUsed);
-  WINBOOL WINAPI GetSystemTimes(LPFILETIME lpIdleTime,LPFILETIME lpKernelTime,LPFILETIME lpUserTime);
-  WINBASEAPI VOID WINAPI GetNativeSystemInfo(LPSYSTEM_INFO lpSystemInfo);
-  WINBASEAPI WINBOOL WINAPI IsProcessorFeaturePresent(DWORD ProcessorFeature);
-
-  typedef struct _TIME_ZONE_INFORMATION {
-    LONG Bias;
-    WCHAR StandardName[32];
-    SYSTEMTIME StandardDate;
-    LONG StandardBias;
-    WCHAR DaylightName[32];
-    SYSTEMTIME DaylightDate;
-    LONG DaylightBias;
-  } TIME_ZONE_INFORMATION,*PTIME_ZONE_INFORMATION,*LPTIME_ZONE_INFORMATION;
-
-#ifdef UNICODE
-#define FormatMessage FormatMessageW
-#else
-#define FormatMessage FormatMessageA
-#endif
-
-  WINBASEAPI WINBOOL WINAPI SystemTimeToTzSpecificLocalTime(LPTIME_ZONE_INFORMATION lpTimeZoneInformation,LPSYSTEMTIME lpUniversalTime,LPSYSTEMTIME lpLocalTime);
-  WINBASEAPI WINBOOL WINAPI TzSpecificLocalTimeToSystemTime(LPTIME_ZONE_INFORMATION lpTimeZoneInformation,LPSYSTEMTIME lpLocalTime,LPSYSTEMTIME lpUniversalTime);
-  WINBASEAPI DWORD WINAPI GetTimeZoneInformation(LPTIME_ZONE_INFORMATION lpTimeZoneInformation);
-  WINBASEAPI WINBOOL WINAPI SetTimeZoneInformation(CONST TIME_ZONE_INFORMATION *lpTimeZoneInformation);
-  WINBASEAPI WINBOOL WINAPI SystemTimeToFileTime(CONST SYSTEMTIME *lpSystemTime,LPFILETIME lpFileTime);
-  WINBASEAPI WINBOOL WINAPI FileTimeToLocalFileTime(CONST FILETIME *lpFileTime,LPFILETIME lpLocalFileTime);
-  WINBASEAPI WINBOOL WINAPI LocalFileTimeToFileTime(CONST FILETIME *lpLocalFileTime,LPFILETIME lpFileTime);
-  WINBASEAPI WINBOOL WINAPI FileTimeToSystemTime(CONST FILETIME *lpFileTime,LPSYSTEMTIME lpSystemTime);
-  WINBASEAPI LONG WINAPI CompareFileTime(CONST FILETIME *lpFileTime1,CONST FILETIME *lpFileTime2);
-  WINBASEAPI WINBOOL WINAPI FileTimeToDosDateTime(CONST FILETIME *lpFileTime,LPWORD lpFatDate,LPWORD lpFatTime);
-  WINBASEAPI WINBOOL WINAPI DosDateTimeToFileTime(WORD wFatDate,WORD wFatTime,LPFILETIME lpFileTime);
-  WINBASEAPI DWORD WINAPI GetTickCount(VOID);
-  WINBASEAPI WINBOOL WINAPI SetSystemTimeAdjustment(DWORD dwTimeAdjustment,WINBOOL bTimeAdjustmentDisabled);
-  WINBASEAPI WINBOOL WINAPI GetSystemTimeAdjustment(PDWORD lpTimeAdjustment,PDWORD lpTimeIncrement,PBOOL lpTimeAdjustmentDisabled);
-  WINBASEAPI DWORD WINAPI FormatMessageA(DWORD dwFlags,LPCVOID lpSource,DWORD dwMessageId,DWORD dwLanguageId,LPSTR lpBuffer,DWORD nSize,va_list *Arguments);
-  WINBASEAPI DWORD WINAPI FormatMessageW(DWORD dwFlags,LPCVOID lpSource,DWORD dwMessageId,DWORD dwLanguageId,LPWSTR lpBuffer,DWORD nSize,va_list *Arguments);
-
-#define FORMAT_MESSAGE_ALLOCATE_BUFFER 0x100
-#define FORMAT_MESSAGE_IGNORE_INSERTS 0x200
-#define FORMAT_MESSAGE_FROM_STRING 0x400
-#define FORMAT_MESSAGE_FROM_HMODULE 0x800
-#define FORMAT_MESSAGE_FROM_SYSTEM 0x1000
-#define FORMAT_MESSAGE_ARGUMENT_ARRAY 0x2000
-#define FORMAT_MESSAGE_MAX_WIDTH_MASK 0xff
-
-#ifdef UNICODE
-#define CreateMailslot CreateMailslotW
-#define EncryptFile EncryptFileW
-#define DecryptFile DecryptFileW
-#define FileEncryptionStatus FileEncryptionStatusW
-#else
-#define CreateMailslot CreateMailslotA
-#define EncryptFile EncryptFileA
-#define DecryptFile DecryptFileA
-#define FileEncryptionStatus FileEncryptionStatusA
-#endif
-
-  WINBASEAPI WINBOOL WINAPI CreatePipe(PHANDLE hReadPipe,PHANDLE hWritePipe,LPSECURITY_ATTRIBUTES lpPipeAttributes,DWORD nSize);
-  WINBASEAPI WINBOOL WINAPI ConnectNamedPipe(HANDLE hNamedPipe,LPOVERLAPPED lpOverlapped);
-  WINBASEAPI WINBOOL WINAPI DisconnectNamedPipe(HANDLE hNamedPipe);
-  WINBASEAPI WINBOOL WINAPI SetNamedPipeHandleState(HANDLE hNamedPipe,LPDWORD lpMode,LPDWORD lpMaxCollectionCount,LPDWORD lpCollectDataTimeout);
-  WINBASEAPI WINBOOL WINAPI GetNamedPipeInfo(HANDLE hNamedPipe,LPDWORD lpFlags,LPDWORD lpOutBufferSize,LPDWORD lpInBufferSize,LPDWORD lpMaxInstances);
-  WINBASEAPI WINBOOL WINAPI PeekNamedPipe(HANDLE hNamedPipe,LPVOID lpBuffer,DWORD nBufferSize,LPDWORD lpBytesRead,LPDWORD lpTotalBytesAvail,LPDWORD lpBytesLeftThisMessage);
-  WINBASEAPI WINBOOL WINAPI TransactNamedPipe(HANDLE hNamedPipe,LPVOID lpInBuffer,DWORD nInBufferSize,LPVOID lpOutBuffer,DWORD nOutBufferSize,LPDWORD lpBytesRead,LPOVERLAPPED lpOverlapped);
-  WINBASEAPI HANDLE WINAPI CreateMailslotA(LPCSTR lpName,DWORD nMaxMessageSize,DWORD lReadTimeout,LPSECURITY_ATTRIBUTES lpSecurityAttributes);
-  WINBASEAPI HANDLE WINAPI CreateMailslotW(LPCWSTR lpName,DWORD nMaxMessageSize,DWORD lReadTimeout,LPSECURITY_ATTRIBUTES lpSecurityAttributes);
-  WINBASEAPI WINBOOL WINAPI GetMailslotInfo(HANDLE hMailslot,LPDWORD lpMaxMessageSize,LPDWORD lpNextSize,LPDWORD lpMessageCount,LPDWORD lpReadTimeout);
-  WINBASEAPI WINBOOL WINAPI SetMailslotInfo(HANDLE hMailslot,DWORD lReadTimeout);
-  WINBASEAPI LPVOID WINAPI MapViewOfFile(HANDLE hFileMappingObject,DWORD dwDesiredAccess,DWORD dwFileOffsetHigh,DWORD dwFileOffsetLow,SIZE_T dwNumberOfBytesToMap);
-  WINBASEAPI WINBOOL WINAPI FlushViewOfFile(LPCVOID lpBaseAddress,SIZE_T dwNumberOfBytesToFlush);
-  WINBASEAPI WINBOOL WINAPI UnmapViewOfFile(LPCVOID lpBaseAddress);
-  WINADVAPI WINBOOL WINAPI EncryptFileA(LPCSTR lpFileName);
-  WINADVAPI WINBOOL WINAPI EncryptFileW(LPCWSTR lpFileName);
-  WINADVAPI WINBOOL WINAPI DecryptFileA(LPCSTR lpFileName,DWORD dwReserved);
-  WINADVAPI WINBOOL WINAPI DecryptFileW(LPCWSTR lpFileName,DWORD dwReserved);
-
-#define FILE_ENCRYPTABLE 0
-#define FILE_IS_ENCRYPTED 1
-#define FILE_SYSTEM_ATTR 2
-#define FILE_ROOT_DIR 3
-#define FILE_SYSTEM_DIR 4
-#define FILE_UNKNOWN 5
-#define FILE_SYSTEM_NOT_SUPPORT 6
-#define FILE_USER_DISALLOWED 7
-#define FILE_READ_ONLY 8
-#define FILE_DIR_DISALLOWED 9
-
-  WINADVAPI WINBOOL WINAPI FileEncryptionStatusA(LPCSTR lpFileName,LPDWORD lpStatus);
-  WINADVAPI WINBOOL WINAPI FileEncryptionStatusW(LPCWSTR lpFileName,LPDWORD lpStatus);
-
-#define EFS_USE_RECOVERY_KEYS (0x1)
-
-  typedef DWORD (WINAPI *PFE_EXPORT_FUNC)(PBYTE pbData,PVOID pvCallbackContext,ULONG ulLength);
-  typedef DWORD (WINAPI *PFE_IMPORT_FUNC)(PBYTE pbData,PVOID pvCallbackContext,PULONG ulLength);
-
-#define CREATE_FOR_IMPORT (1)
-#define CREATE_FOR_DIR (2)
-#define OVERWRITE_HIDDEN (4)
-
-#ifdef UNICODE
-#define OpenEncryptedFileRaw OpenEncryptedFileRawW
-#define lstrcmp lstrcmpW
-#define lstrcmpi lstrcmpiW
-#define lstrcpyn lstrcpynW
-#define lstrcpy lstrcpyW
-#define lstrcat lstrcatW
-#define lstrlen lstrlenW
-#else
-#define OpenEncryptedFileRaw OpenEncryptedFileRawA
-#define lstrcmp lstrcmpA
-#define lstrcmpi lstrcmpiA
-#define lstrcpyn lstrcpynA
-#define lstrcpy lstrcpyA
-#define lstrcat lstrcatA
-#define lstrlen lstrlenA
-#endif
-
-  WINADVAPI DWORD WINAPI OpenEncryptedFileRawA(LPCSTR lpFileName,ULONG ulFlags,PVOID *pvContext);
-  WINADVAPI DWORD WINAPI OpenEncryptedFileRawW(LPCWSTR lpFileName,ULONG ulFlags,PVOID *pvContext);
-  WINADVAPI DWORD WINAPI ReadEncryptedFileRaw(PFE_EXPORT_FUNC pfExportCallback,PVOID pvCallbackContext,PVOID pvContext);
-  WINADVAPI DWORD WINAPI WriteEncryptedFileRaw(PFE_IMPORT_FUNC pfImportCallback,PVOID pvCallbackContext,PVOID pvContext);
-  WINADVAPI VOID WINAPI CloseEncryptedFileRaw(PVOID pvContext);
-  WINBASEAPI int WINAPI lstrcmpA(LPCSTR lpString1,LPCSTR lpString2);
-  WINBASEAPI int WINAPI lstrcmpW(LPCWSTR lpString1,LPCWSTR lpString2);
-  WINBASEAPI int WINAPI lstrcmpiA(LPCSTR lpString1,LPCSTR lpString2);
-  WINBASEAPI int WINAPI lstrcmpiW(LPCWSTR lpString1,LPCWSTR lpString2);
-  WINBASEAPI LPSTR WINAPI lstrcpynA(LPSTR lpString1,LPCSTR lpString2,int iMaxLength);
-  WINBASEAPI LPWSTR WINAPI lstrcpynW(LPWSTR lpString1,LPCWSTR lpString2,int iMaxLength);
-  WINBASEAPI LPSTR WINAPI lstrcpyA(LPSTR lpString1,LPCSTR lpString2);
-  WINBASEAPI LPWSTR WINAPI lstrcpyW(LPWSTR lpString1,LPCWSTR lpString2);
-  WINBASEAPI LPSTR WINAPI lstrcatA(LPSTR lpString1,LPCSTR lpString2);
-  WINBASEAPI LPWSTR WINAPI lstrcatW(LPWSTR lpString1,LPCWSTR lpString2);
-  WINBASEAPI int WINAPI lstrlenA(LPCSTR lpString);
-  WINBASEAPI int WINAPI lstrlenW(LPCWSTR lpString);
-  WINBASEAPI HFILE WINAPI OpenFile(LPCSTR lpFileName,LPOFSTRUCT lpReOpenBuff,UINT uStyle);
-  WINBASEAPI HFILE WINAPI _lopen(LPCSTR lpPathName,int iReadWrite);
-  WINBASEAPI HFILE WINAPI _lcreat(LPCSTR lpPathName,int iAttribute);
-  WINBASEAPI UINT WINAPI _lread(HFILE hFile,LPVOID lpBuffer,UINT uBytes);
-  WINBASEAPI UINT WINAPI _lwrite(HFILE hFile,LPCCH lpBuffer,UINT uBytes);
-  WINBASEAPI long WINAPI _hread(HFILE hFile,LPVOID lpBuffer,long lBytes);
-  WINBASEAPI long WINAPI _hwrite(HFILE hFile,LPCCH lpBuffer,long lBytes);
-  WINBASEAPI HFILE WINAPI _lclose(HFILE hFile);
-  WINBASEAPI LONG WINAPI _llseek(HFILE hFile,LONG lOffset,int iOrigin);
-  WINADVAPI WINBOOL WINAPI IsTextUnicode(CONST VOID *lpv,int iSize,LPINT lpiResult);
-
-#define FLS_OUT_OF_INDEXES ((DWORD)0xffffffff)
-
-  WINBASEAPI DWORD WINAPI FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback);
-  WINBASEAPI PVOID WINAPI FlsGetValue(DWORD dwFlsIndex);
-  WINBASEAPI WINBOOL WINAPI FlsSetValue(DWORD dwFlsIndex,PVOID lpFlsData);
-  WINBASEAPI WINBOOL WINAPI FlsFree(DWORD dwFlsIndex);
-
-#define TLS_OUT_OF_INDEXES ((DWORD)0xffffffff)
-
-  WINBASEAPI DWORD WINAPI TlsAlloc(VOID);
-  WINBASEAPI LPVOID WINAPI TlsGetValue(DWORD dwTlsIndex);
-  WINBASEAPI WINBOOL WINAPI TlsSetValue(DWORD dwTlsIndex,LPVOID lpTlsValue);
-  WINBASEAPI WINBOOL WINAPI TlsFree(DWORD dwTlsIndex);
-
-  typedef VOID (WINAPI *LPOVERLAPPED_COMPLETION_ROUTINE)(DWORD dwErrorCode,DWORD dwNumberOfBytesTransfered,LPOVERLAPPED lpOverlapped);
-
-  WINBASEAPI DWORD WINAPI SleepEx(DWORD dwMilliseconds,WINBOOL bAlertable);
-  WINBASEAPI DWORD WINAPI WaitForSingleObjectEx(HANDLE hHandle,DWORD dwMilliseconds,WINBOOL bAlertable);
-  WINBASEAPI DWORD WINAPI WaitForMultipleObjectsEx(DWORD nCount,CONST HANDLE *lpHandles,WINBOOL bWaitAll,DWORD dwMilliseconds,WINBOOL bAlertable);
-  WINBASEAPI DWORD WINAPI SignalObjectAndWait(HANDLE hObjectToSignal,HANDLE hObjectToWaitOn,DWORD dwMilliseconds,WINBOOL bAlertable);
-  WINBASEAPI WINBOOL WINAPI ReadFileEx(HANDLE hFile,LPVOID lpBuffer,DWORD nNumberOfBytesToRead,LPOVERLAPPED lpOverlapped,LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
-  WINBASEAPI WINBOOL WINAPI WriteFileEx(HANDLE hFile,LPCVOID lpBuffer,DWORD nNumberOfBytesToWrite,LPOVERLAPPED lpOverlapped,LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
-  WINBASEAPI WINBOOL WINAPI BackupRead(HANDLE hFile,LPBYTE lpBuffer,DWORD nNumberOfBytesToRead,LPDWORD lpNumberOfBytesRead,WINBOOL bAbort,WINBOOL bProcessSecurity,LPVOID *lpContext);
-  WINBASEAPI WINBOOL WINAPI BackupSeek(HANDLE hFile,DWORD dwLowBytesToSeek,DWORD dwHighBytesToSeek,LPDWORD lpdwLowByteSeeked,LPDWORD lpdwHighByteSeeked,LPVOID *lpContext);
-  WINBASEAPI WINBOOL WINAPI BackupWrite(HANDLE hFile,LPBYTE lpBuffer,DWORD nNumberOfBytesToWrite,LPDWORD lpNumberOfBytesWritten,WINBOOL bAbort,WINBOOL bProcessSecurity,LPVOID *lpContext);
-
-  typedef struct _WIN32_STREAM_ID {
-    DWORD dwStreamId;
-    DWORD dwStreamAttributes;
-    LARGE_INTEGER Size;
-    DWORD dwStreamNameSize;
-    WCHAR cStreamName[ANYSIZE_ARRAY];
-  } WIN32_STREAM_ID,*LPWIN32_STREAM_ID;
-
-#define BACKUP_INVALID 0x0
-#define BACKUP_DATA 0x1
-#define BACKUP_EA_DATA 0x2
-#define BACKUP_SECURITY_DATA 0x3
-#define BACKUP_ALTERNATE_DATA 0x4
-#define BACKUP_LINK 0x5
-#define BACKUP_PROPERTY_DATA 0x6
-#define BACKUP_OBJECT_ID 0x7
-#define BACKUP_REPARSE_DATA 0x8
-#define BACKUP_SPARSE_BLOCK 0x9
-
-#define STREAM_NORMAL_ATTRIBUTE 0x0
-#define STREAM_MODIFIED_WHEN_READ 0x1
-#define STREAM_CONTAINS_SECURITY 0x2
-#define STREAM_CONTAINS_PROPERTIES 0x4
-#define STREAM_SPARSE_ATTRIBUTE 0x8
-
-  WINBASEAPI WINBOOL WINAPI ReadFileScatter(HANDLE hFile,FILE_SEGMENT_ELEMENT aSegmentArray[],DWORD nNumberOfBytesToRead,LPDWORD lpReserved,LPOVERLAPPED lpOverlapped);
-  WINBASEAPI WINBOOL WINAPI WriteFileGather(HANDLE hFile,FILE_SEGMENT_ELEMENT aSegmentArray[],DWORD nNumberOfBytesToWrite,LPDWORD lpReserved,LPOVERLAPPED lpOverlapped);
-
-#define STARTF_USESHOWWINDOW 0x1
-#define STARTF_USESIZE 0x2
-#define STARTF_USEPOSITION 0x4
-#define STARTF_USECOUNTCHARS 0x8
-#define STARTF_USEFILLATTRIBUTE 0x10
-#define STARTF_RUNFULLSCREEN 0x20
-#define STARTF_FORCEONFEEDBACK 0x40
-#define STARTF_FORCEOFFFEEDBACK 0x80
-#define STARTF_USESTDHANDLES 0x100
-
-#define STARTF_USEHOTKEY 0x200
-
-  typedef struct _STARTUPINFOA {
-    DWORD cb;
-    LPSTR lpReserved;
-    LPSTR lpDesktop;
-    LPSTR lpTitle;
-    DWORD dwX;
-    DWORD dwY;
-    DWORD dwXSize;
-    DWORD dwYSize;
-    DWORD dwXCountChars;
-    DWORD dwYCountChars;
-    DWORD dwFillAttribute;
-    DWORD dwFlags;
-    WORD wShowWindow;
-    WORD cbReserved2;
-    LPBYTE lpReserved2;
-    HANDLE hStdInput;
-    HANDLE hStdOutput;
-    HANDLE hStdError;
-  } STARTUPINFOA,*LPSTARTUPINFOA;
-
-  typedef struct _STARTUPINFOW {
-    DWORD cb;
-    LPWSTR lpReserved;
-    LPWSTR lpDesktop;
-    LPWSTR lpTitle;
-    DWORD dwX;
-    DWORD dwY;
-    DWORD dwXSize;
-    DWORD dwYSize;
-    DWORD dwXCountChars;
-    DWORD dwYCountChars;
-    DWORD dwFillAttribute;
-    DWORD dwFlags;
-    WORD wShowWindow;
-    WORD cbReserved2;
-    LPBYTE lpReserved2;
-    HANDLE hStdInput;
-    HANDLE hStdOutput;
-    HANDLE hStdError;
-  } STARTUPINFOW,*LPSTARTUPINFOW;
-
-#ifdef UNICODE
-  typedef STARTUPINFOW STARTUPINFO;
-  typedef LPSTARTUPINFOW LPSTARTUPINFO;
-#else
-  typedef STARTUPINFOA STARTUPINFO;
-  typedef LPSTARTUPINFOA LPSTARTUPINFO;
-#endif
-
-#define SHUTDOWN_NORETRY 0x1
-
-  typedef struct _WIN32_FIND_DATAA {
-    DWORD dwFileAttributes;
-    FILETIME ftCreationTime;
-    FILETIME ftLastAccessTime;
-    FILETIME ftLastWriteTime;
-    DWORD nFileSizeHigh;
-    DWORD nFileSizeLow;
-    DWORD dwReserved0;
-    DWORD dwReserved1;
-    CHAR cFileName[MAX_PATH];
-    CHAR cAlternateFileName[14];
-  } WIN32_FIND_DATAA,*PWIN32_FIND_DATAA,*LPWIN32_FIND_DATAA;
-
-  typedef struct _WIN32_FIND_DATAW {
-    DWORD dwFileAttributes;
-    FILETIME ftCreationTime;
-    FILETIME ftLastAccessTime;
-    FILETIME ftLastWriteTime;
-    DWORD nFileSizeHigh;
-    DWORD nFileSizeLow;
-    DWORD dwReserved0;
-    DWORD dwReserved1;
-    WCHAR cFileName[MAX_PATH];
-    WCHAR cAlternateFileName[14];
-  } WIN32_FIND_DATAW,*PWIN32_FIND_DATAW,*LPWIN32_FIND_DATAW;
-
-#ifdef UNICODE
-  typedef WIN32_FIND_DATAW WIN32_FIND_DATA;
-  typedef PWIN32_FIND_DATAW PWIN32_FIND_DATA;
-  typedef LPWIN32_FIND_DATAW LPWIN32_FIND_DATA;
-#else
-  typedef WIN32_FIND_DATAA WIN32_FIND_DATA;
-  typedef PWIN32_FIND_DATAA PWIN32_FIND_DATA;
-  typedef LPWIN32_FIND_DATAA LPWIN32_FIND_DATA;
-#endif
-
-  typedef struct _WIN32_FILE_ATTRIBUTE_DATA {
-    DWORD dwFileAttributes;
-    FILETIME ftCreationTime;
-    FILETIME ftLastAccessTime;
-    FILETIME ftLastWriteTime;
-    DWORD nFileSizeHigh;
-    DWORD nFileSizeLow;
-  } WIN32_FILE_ATTRIBUTE_DATA,*LPWIN32_FILE_ATTRIBUTE_DATA;
-
-#ifdef UNICODE
-#define CreateMutex CreateMutexW
-#define OpenMutex OpenMutexW
-#define CreateEvent CreateEventW
-#define OpenEvent OpenEventW
-#define CreateSemaphore CreateSemaphoreW
-#define OpenSemaphore OpenSemaphoreW
-#else
-#define CreateMutex CreateMutexA
-#define OpenMutex OpenMutexA
-#define CreateEvent CreateEventA
-#define OpenEvent OpenEventA
-#define CreateSemaphore CreateSemaphoreA
-#define OpenSemaphore OpenSemaphoreA
-#endif
-
-  WINBASEAPI HANDLE WINAPI CreateMutexA(LPSECURITY_ATTRIBUTES lpMutexAttributes,WINBOOL bInitialOwner,LPCSTR lpName);
-  WINBASEAPI HANDLE WINAPI CreateMutexW(LPSECURITY_ATTRIBUTES lpMutexAttributes,WINBOOL bInitialOwner,LPCWSTR lpName);
-  WINBASEAPI HANDLE WINAPI OpenMutexA(DWORD dwDesiredAccess,WINBOOL bInheritHandle,LPCSTR lpName);
-  WINBASEAPI HANDLE WINAPI OpenMutexW(DWORD dwDesiredAccess,WINBOOL bInheritHandle,LPCWSTR lpName);
-  WINBASEAPI HANDLE WINAPI CreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes,WINBOOL bManualReset,WINBOOL bInitialState,LPCSTR lpName);
-  WINBASEAPI HANDLE WINAPI CreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes,WINBOOL bManualReset,WINBOOL bInitialState,LPCWSTR lpName);
-  WINBASEAPI HANDLE WINAPI OpenEventA(DWORD dwDesiredAccess,WINBOOL bInheritHandle,LPCSTR lpName);
-  WINBASEAPI HANDLE WINAPI OpenEventW(DWORD dwDesiredAccess,WINBOOL bInheritHandle,LPCWSTR lpName);
-  WINBASEAPI HANDLE WINAPI CreateSemaphoreA(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,LONG lInitialCount,LONG lMaximumCount,LPCSTR lpName);
-  WINBASEAPI HANDLE WINAPI CreateSemaphoreW(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,LONG lInitialCount,LONG lMaximumCount,LPCWSTR lpName);
-  WINBASEAPI HANDLE WINAPI OpenSemaphoreA(DWORD dwDesiredAccess,WINBOOL bInheritHandle,LPCSTR lpName);
-  WINBASEAPI HANDLE WINAPI OpenSemaphoreW(DWORD dwDesiredAccess,WINBOOL bInheritHandle,LPCWSTR lpName);
-
-  typedef VOID (WINAPI *PTIMERAPCROUTINE)(LPVOID lpArgToCompletionRoutine,DWORD dwTimerLowValue,DWORD dwTimerHighValue);
-
-#ifdef UNICODE
-#define CreateWaitableTimer CreateWaitableTimerW
-#define OpenWaitableTimer OpenWaitableTimerW
-#define CreateFileMapping CreateFileMappingW
-#define OpenFileMapping OpenFileMappingW
-#define GetLogicalDriveStrings GetLogicalDriveStringsW
-#define LoadLibrary LoadLibraryW
-#define LoadLibraryEx LoadLibraryExW
-#define GetModuleFileName GetModuleFileNameW
-#define GetModuleHandle GetModuleHandleW
-#else
-#define CreateWaitableTimer CreateWaitableTimerA
-#define OpenWaitableTimer OpenWaitableTimerA
-#define CreateFileMapping CreateFileMappingA
-#define OpenFileMapping OpenFileMappingA
-#define GetLogicalDriveStrings GetLogicalDriveStringsA
-#define LoadLibrary LoadLibraryA
-#define LoadLibraryEx LoadLibraryExA
-#define GetModuleFileName GetModuleFileNameA
-#define GetModuleHandle GetModuleHandleA
-#endif
-
-  WINBASEAPI HANDLE WINAPI CreateWaitableTimerA(LPSECURITY_ATTRIBUTES lpTimerAttributes,WINBOOL bManualReset,LPCSTR lpTimerName);
-  WINBASEAPI HANDLE WINAPI CreateWaitableTimerW(LPSECURITY_ATTRIBUTES lpTimerAttributes,WINBOOL bManualReset,LPCWSTR lpTimerName);
-  WINBASEAPI HANDLE WINAPI OpenWaitableTimerA(DWORD dwDesiredAccess,WINBOOL bInheritHandle,LPCSTR lpTimerName);
-  WINBASEAPI HANDLE WINAPI OpenWaitableTimerW(DWORD dwDesiredAccess,WINBOOL bInheritHandle,LPCWSTR lpTimerName);
-  WINBASEAPI WINBOOL WINAPI SetWaitableTimer(HANDLE hTimer,const LARGE_INTEGER *lpDueTime,LONG lPeriod,PTIMERAPCROUTINE pfnCompletionRoutine,LPVOID lpArgToCompletionRoutine,WINBOOL fResume);
-  WINBASEAPI WINBOOL WINAPI CancelWaitableTimer(HANDLE hTimer);
-  WINBASEAPI HANDLE WINAPI CreateFileMappingA(HANDLE hFile,LPSECURITY_ATTRIBUTES lpFileMappingAttributes,DWORD flProtect,DWORD dwMaximumSizeHigh,DWORD dwMaximumSizeLow,LPCSTR lpName);
-  WINBASEAPI HANDLE WINAPI CreateFileMappingW(HANDLE hFile,LPSECURITY_ATTRIBUTES lpFileMappingAttributes,DWORD flProtect,DWORD dwMaximumSizeHigh,DWORD dwMaximumSizeLow,LPCWSTR lpName);
-  WINBASEAPI HANDLE WINAPI OpenFileMappingA(DWORD dwDesiredAccess,WINBOOL bInheritHandle,LPCSTR lpName);
-  WINBASEAPI HANDLE WINAPI OpenFileMappingW(DWORD dwDesiredAccess,WINBOOL bInheritHandle,LPCWSTR lpName);
-  WINBASEAPI DWORD WINAPI GetLogicalDriveStringsA(DWORD nBufferLength,LPSTR lpBuffer);
-  WINBASEAPI DWORD WINAPI GetLogicalDriveStringsW(DWORD nBufferLength,LPWSTR lpBuffer);
-
-  typedef enum _MEMORY_RESOURCE_NOTIFICATION_TYPE {
-    LowMemoryResourceNotification,HighMemoryResourceNotification
-  } MEMORY_RESOURCE_NOTIFICATION_TYPE;
-
-  WINBASEAPI HANDLE WINAPI CreateMemoryResourceNotification(MEMORY_RESOURCE_NOTIFICATION_TYPE NotificationType);
-  WINBASEAPI WINBOOL WINAPI QueryMemoryResourceNotification(HANDLE ResourceNotificationHandle,PBOOL ResourceState);
-  WINBASEAPI HMODULE WINAPI LoadLibraryA(LPCSTR lpLibFileName);
-  WINBASEAPI HMODULE WINAPI LoadLibraryW(LPCWSTR lpLibFileName);
-  WINBASEAPI HMODULE WINAPI LoadLibraryExA(LPCSTR lpLibFileName,HANDLE hFile,DWORD dwFlags);
-  WINBASEAPI HMODULE WINAPI LoadLibraryExW(LPCWSTR lpLibFileName,HANDLE hFile,DWORD dwFlags);
-
-#define DONT_RESOLVE_DLL_REFERENCES 0x1
-#define LOAD_LIBRARY_AS_DATAFILE 0x2
-#define LOAD_WITH_ALTERED_SEARCH_PATH 0x8
-#define LOAD_IGNORE_CODE_AUTHZ_LEVEL 0x10
-#define LOAD_LINRARY_AS_IMAGE_RESOURCE 0x20
-#define LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE 0x40
-
-  WINBASEAPI DWORD WINAPI GetModuleFileNameA(HMODULE hModule,LPCH lpFilename,DWORD nSize);
-  WINBASEAPI DWORD WINAPI GetModuleFileNameW(HMODULE hModule,LPWCH lpFilename,DWORD nSize);
-  WINBASEAPI HMODULE WINAPI GetModuleHandleA(LPCSTR lpModuleName);
-  WINBASEAPI HMODULE WINAPI GetModuleHandleW(LPCWSTR lpModuleName);
-
-#ifndef RC_INVOKED
-#define GET_MODULE_HANDLE_EX_FLAG_PIN (0x1)
-#define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT (0x2)
-#define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS (0x4)
-
-  typedef WINBOOL (WINAPI *PGET_MODULE_HANDLE_EXA)(DWORD dwFlags,LPCSTR lpModuleName,HMODULE *phModule);
-  typedef WINBOOL (WINAPI *PGET_MODULE_HANDLE_EXW)(DWORD dwFlags,LPCWSTR lpModuleName,HMODULE *phModule);
-
-#ifdef UNICODE
-#define PGET_MODULE_HANDLE_EX PGET_MODULE_HANDLE_EXW
-#define GetModuleHandleEx GetModuleHandleExW
-#else
-#define PGET_MODULE_HANDLE_EX PGET_MODULE_HANDLE_EXA
-#define GetModuleHandleEx GetModuleHandleExA
-#endif
-
-  WINBASEAPI WINBOOL WINAPI GetModuleHandleExA(DWORD dwFlags,LPCSTR lpModuleName,HMODULE *phModule);
-  WINBASEAPI WINBOOL WINAPI GetModuleHandleExW(DWORD dwFlags,LPCWSTR lpModuleName,HMODULE *phModule);
-#endif
-
-#ifdef UNICODE
-#define NeedCurrentDirectoryForExePath NeedCurrentDirectoryForExePathW
-#define CreateProcess CreateProcessW
-#define FatalAppExit FatalAppExitW
-#define GetStartupInfo GetStartupInfoW
-#define GetCommandLine GetCommandLineW
-#define GetEnvironmentVariable GetEnvironmentVariableW
-#define SetEnvironmentVariable SetEnvironmentVariableW
-#define ExpandEnvironmentStrings ExpandEnvironmentStringsW
-#define GetFirmwareEnvironmentVariable GetFirmwareEnvironmentVariableW
-#define SetFirmwareEnvironmentVariable SetFirmwareEnvironmentVariableW
-#define OutputDebugString OutputDebugStringW
-#define FindResource FindResourceW
-#define FindResourceEx FindResourceExW
-#else
-#define NeedCurrentDirectoryForExePath NeedCurrentDirectoryForExePathA
-#define CreateProcess CreateProcessA
-#define FatalAppExit FatalAppExitA
-#define GetStartupInfo GetStartupInfoA
-#define GetCommandLine GetCommandLineA
-#define GetEnvironmentVariable GetEnvironmentVariableA
-#define SetEnvironmentVariable SetEnvironmentVariableA
-#define ExpandEnvironmentStrings ExpandEnvironmentStringsA
-#define GetFirmwareEnvironmentVariable GetFirmwareEnvironmentVariableA
-#define SetFirmwareEnvironmentVariable SetFirmwareEnvironmentVariableA
-#define OutputDebugString OutputDebugStringA
-#define FindResource FindResourceA
-#define FindResourceEx FindResourceExA
-#endif
-
-  WINBASEAPI WINBOOL WINAPI NeedCurrentDirectoryForExePathA(LPCSTR ExeName);
-  WINBASEAPI WINBOOL WINAPI NeedCurrentDirectoryForExePathW(LPCWSTR ExeName);
-  WINBASEAPI WINBOOL WINAPI CreateProcessA(LPCSTR lpApplicationName,LPSTR lpCommandLine,LPSECURITY_ATTRIBUTES lpProcessAttributes,LPSECURITY_ATTRIBUTES lpThreadAttributes,WINBOOL bInheritHandles,DWORD dwCreationFlags,LPVOID lpEnvironment,LPCSTR lpCurrentDirectory,LPSTARTUPINFOA lpStartupInfo,LPPROCESS_INFORMATION lpProcessInformation);
-  WINBASEAPI WINBOOL WINAPI CreateProcessW(LPCWSTR lpApplicationName,LPWSTR lpCommandLine,LPSECURITY_ATTRIBUTES lpProcessAttributes,LPSECURITY_ATTRIBUTES lpThreadAttributes,WINBOOL bInheritHandles,DWORD dwCreationFlags,LPVOID lpEnvironment,LPCWSTR lpCurrentDirectory,LPSTARTUPINFOW lpStartupInfo,LPPROCESS_INFORMATION lpProcessInformation);
-  WINBASEAPI DWORD WINAPI AddLocalAlternateComputerNameA(LPCSTR lpDnsFQHostname,ULONG ulFlags);
-  WINBASEAPI DWORD WINAPI AddLocalAlternateComputerNameW(LPCWSTR lpDnsFQHostname,ULONG ulFlags);
-  WINBASEAPI WINBOOL WINAPI SetProcessShutdownParameters(DWORD dwLevel,DWORD dwFlags);
-  WINBASEAPI WINBOOL WINAPI GetProcessShutdownParameters(LPDWORD lpdwLevel,LPDWORD lpdwFlags);
-  WINBASEAPI DWORD WINAPI GetProcessVersion(DWORD ProcessId);
-  WINBASEAPI VOID WINAPI FatalAppExitA(UINT uAction,LPCSTR lpMessageText);
-  WINBASEAPI VOID WINAPI FatalAppExitW(UINT uAction,LPCWSTR lpMessageText);
-  WINBASEAPI VOID WINAPI GetStartupInfoA(LPSTARTUPINFOA lpStartupInfo);
-  WINBASEAPI VOID WINAPI GetStartupInfoW(LPSTARTUPINFOW lpStartupInfo);
-  WINBASEAPI LPSTR WINAPI GetCommandLineA(VOID);
-  WINBASEAPI LPWSTR WINAPI GetCommandLineW(VOID);
-  WINBASEAPI DWORD WINAPI GetEnvironmentVariableA(LPCSTR lpName,LPSTR lpBuffer,DWORD nSize);
-  WINBASEAPI DWORD WINAPI GetEnvironmentVariableW(LPCWSTR lpName,LPWSTR lpBuffer,DWORD nSize);
-  WINBASEAPI WINBOOL WINAPI SetEnvironmentVariableA(LPCSTR lpName,LPCSTR lpValue);
-  WINBASEAPI WINBOOL WINAPI SetEnvironmentVariableW(LPCWSTR lpName,LPCWSTR lpValue);
-  WINBASEAPI DWORD WINAPI ExpandEnvironmentStringsA(LPCSTR lpSrc,LPSTR lpDst,DWORD nSize);
-  WINBASEAPI DWORD WINAPI ExpandEnvironmentStringsW(LPCWSTR lpSrc,LPWSTR lpDst,DWORD nSize);
-  WINBASEAPI DWORD WINAPI GetFirmwareEnvironmentVariableA(LPCSTR lpName,LPCSTR lpGuid,PVOID pBuffer,DWORD nSize);
-  WINBASEAPI DWORD WINAPI GetFirmwareEnvironmentVariableW(LPCWSTR lpName,LPCWSTR lpGuid,PVOID pBuffer,DWORD nSize);
-  WINBASEAPI WINBOOL WINAPI SetFirmwareEnvironmentVariableA(LPCSTR lpName,LPCSTR lpGuid,PVOID pValue,DWORD nSize);
-  WINBASEAPI WINBOOL WINAPI SetFirmwareEnvironmentVariableW(LPCWSTR lpName,LPCWSTR lpGuid,PVOID pValue,DWORD nSize);
-  WINBASEAPI VOID WINAPI OutputDebugStringA(LPCSTR lpOutputString);
-  WINBASEAPI VOID WINAPI OutputDebugStringW(LPCWSTR lpOutputString);
-  WINBASEAPI HRSRC WINAPI FindResourceA(HMODULE hModule,LPCSTR lpName,LPCSTR lpType);
-  WINBASEAPI HRSRC WINAPI FindResourceW(HMODULE hModule,LPCWSTR lpName,LPCWSTR lpType);
-  WINBASEAPI HRSRC WINAPI FindResourceExA(HMODULE hModule,LPCSTR lpType,LPCSTR lpName,WORD wLanguage);
-  WINBASEAPI HRSRC WINAPI FindResourceExW(HMODULE hModule,LPCWSTR lpType,LPCWSTR lpName,WORD wLanguage);
-
-#ifdef UNICODE
-#define ENUMRESTYPEPROC ENUMRESTYPEPROCW
-#define ENUMRESNAMEPROC ENUMRESNAMEPROCW
-#define ENUMRESLANGPROC ENUMRESLANGPROCW
-#define EnumResourceTypes EnumResourceTypesW
-#define EnumResourceNames EnumResourceNamesW
-#define EnumResourceLanguages EnumResourceLanguagesW
-#define BeginUpdateResource BeginUpdateResourceW
-#define UpdateResource UpdateResourceW
-#define EndUpdateResource EndUpdateResourceW
-#define GlobalAddAtom GlobalAddAtomW
-#define GlobalFindAtom GlobalFindAtomW
-#define GlobalGetAtomName GlobalGetAtomNameW
-#define AddAtom AddAtomW
-#define FindAtom FindAtomW
-#define GetAtomName GetAtomNameW
-#define GetProfileInt GetProfileIntW
-#define GetProfileString GetProfileStringW
-#define WriteProfileString WriteProfileStringW
-#define GetProfileSection GetProfileSectionW
-#define WriteProfileSection WriteProfileSectionW
-#define GetPrivateProfileInt GetPrivateProfileIntW
-#define GetPrivateProfileString GetPrivateProfileStringW
-#define WritePrivateProfileString WritePrivateProfileStringW
-#define GetPrivateProfileSection GetPrivateProfileSectionW
-#define WritePrivateProfileSection WritePrivateProfileSectionW
-#define GetPrivateProfileSectionNames GetPrivateProfileSectionNamesW
-#define GetPrivateProfileStruct GetPrivateProfileStructW
-#define WritePrivateProfileStruct WritePrivateProfileStructW
-#define GetDriveType GetDriveTypeW
-#define GetSystemDirectory GetSystemDirectoryW
-#define GetTempPath GetTempPathW
-#define GetTempFileName GetTempFileNameW
-#define GetWindowsDirectory GetWindowsDirectoryW
-#define GetSystemWindowsDirectory GetSystemWindowsDirectoryW
-#define AddLocalAlternateComputerName AddLocalAlternateComputerNameW
-#else
-#define ENUMRESTYPEPROC ENUMRESTYPEPROCA
-#define ENUMRESNAMEPROC ENUMRESNAMEPROCA
-#define ENUMRESLANGPROC ENUMRESLANGPROCA
-#define EnumResourceTypes EnumResourceTypesA
-#define EnumResourceNames EnumResourceNamesA
-#define EnumResourceLanguages EnumResourceLanguagesA
-#define BeginUpdateResource BeginUpdateResourceA
-#define UpdateResource UpdateResourceA
-#define EndUpdateResource EndUpdateResourceA
-#define GlobalAddAtom GlobalAddAtomA
-#define GlobalFindAtom GlobalFindAtomA
-#define GlobalGetAtomName GlobalGetAtomNameA
-#define AddAtom AddAtomA
-#define FindAtom FindAtomA
-#define GetAtomName GetAtomNameA
-#define GetProfileInt GetProfileIntA
-#define GetProfileString GetProfileStringA
-#define WriteProfileString WriteProfileStringA
-#define GetProfileSection GetProfileSectionA
-#define WriteProfileSection WriteProfileSectionA
-#define GetPrivateProfileInt GetPrivateProfileIntA
-#define GetPrivateProfileString GetPrivateProfileStringA
-#define WritePrivateProfileString WritePrivateProfileStringA
-#define GetPrivateProfileSection GetPrivateProfileSectionA
-#define WritePrivateProfileSection WritePrivateProfileSectionA
-#define GetPrivateProfileSectionNames GetPrivateProfileSectionNamesA
-#define GetPrivateProfileStruct GetPrivateProfileStructA
-#define WritePrivateProfileStruct WritePrivateProfileStructA
-#define GetDriveType GetDriveTypeA
-#define GetSystemDirectory GetSystemDirectoryA
-#define GetTempPath GetTempPathA
-#define GetTempFileName GetTempFileNameA
-#define GetWindowsDirectory GetWindowsDirectoryA
-#define GetSystemWindowsDirectory GetSystemWindowsDirectoryA
-#define AddLocalAlternateComputerName AddLocalAlternateComputerNameA
-#endif
-
-  typedef WINBOOL (CALLBACK *ENUMRESTYPEPROCA)(HMODULE hModule,LPSTR lpType,LONG_PTR lParam);
-  typedef WINBOOL (CALLBACK *ENUMRESTYPEPROCW)(HMODULE hModule,LPWSTR lpType,LONG_PTR lParam);
-  typedef WINBOOL (CALLBACK *ENUMRESNAMEPROCA)(HMODULE hModule,LPCSTR lpType,LPSTR lpName,LONG_PTR lParam);
-  typedef WINBOOL (CALLBACK *ENUMRESNAMEPROCW)(HMODULE hModule,LPCWSTR lpType,LPWSTR lpName,LONG_PTR lParam);
-  typedef WINBOOL (CALLBACK *ENUMRESLANGPROCA)(HMODULE hModule,LPCSTR lpType,LPCSTR lpName,WORD wLanguage,LONG_PTR lParam);
-  typedef WINBOOL (CALLBACK *ENUMRESLANGPROCW)(HMODULE hModule,LPCWSTR lpType,LPCWSTR lpName,WORD wLanguage,LONG_PTR lParam);
-
-  WINBASEAPI WINBOOL WINAPI EnumResourceTypesA(HMODULE hModule,ENUMRESTYPEPROCA lpEnumFunc,LONG_PTR lParam);
-  WINBASEAPI WINBOOL WINAPI EnumResourceTypesW(HMODULE hModule,ENUMRESTYPEPROCW lpEnumFunc,LONG_PTR lParam);
-  WINBASEAPI WINBOOL WINAPI EnumResourceNamesA(HMODULE hModule,LPCSTR lpType,ENUMRESNAMEPROCA lpEnumFunc,LONG_PTR lParam);
-  WINBASEAPI WINBOOL WINAPI EnumResourceNamesW(HMODULE hModule,LPCWSTR lpType,ENUMRESNAMEPROCW lpEnumFunc,LONG_PTR lParam);
-  WINBASEAPI WINBOOL WINAPI EnumResourceLanguagesA(HMODULE hModule,LPCSTR lpType,LPCSTR lpName,ENUMRESLANGPROCA lpEnumFunc,LONG_PTR lParam);
-  WINBASEAPI WINBOOL WINAPI EnumResourceLanguagesW(HMODULE hModule,LPCWSTR lpType,LPCWSTR lpName,ENUMRESLANGPROCW lpEnumFunc,LONG_PTR lParam);
-  WINBASEAPI HANDLE WINAPI BeginUpdateResourceA(LPCSTR pFileName,WINBOOL bDeleteExistingResources);
-  WINBASEAPI HANDLE WINAPI BeginUpdateResourceW(LPCWSTR pFileName,WINBOOL bDeleteExistingResources);
-  WINBASEAPI WINBOOL WINAPI UpdateResourceA(HANDLE hUpdate,LPCSTR lpType,LPCSTR lpName,WORD wLanguage,LPVOID lpData,DWORD cb);
-  WINBASEAPI WINBOOL WINAPI UpdateResourceW(HANDLE hUpdate,LPCWSTR lpType,LPCWSTR lpName,WORD wLanguage,LPVOID lpData,DWORD cb);
-  WINBASEAPI WINBOOL WINAPI EndUpdateResourceA(HANDLE hUpdate,WINBOOL fDiscard);
-  WINBASEAPI WINBOOL WINAPI EndUpdateResourceW(HANDLE hUpdate,WINBOOL fDiscard);
-  WINBASEAPI ATOM WINAPI GlobalAddAtomA(LPCSTR lpString);
-  WINBASEAPI ATOM WINAPI GlobalAddAtomW(LPCWSTR lpString);
-  WINBASEAPI ATOM WINAPI GlobalFindAtomA(LPCSTR lpString);
-  WINBASEAPI ATOM WINAPI GlobalFindAtomW(LPCWSTR lpString);
-  WINBASEAPI UINT WINAPI GlobalGetAtomNameA(ATOM nAtom,LPSTR lpBuffer,int nSize);
-  WINBASEAPI UINT WINAPI GlobalGetAtomNameW(ATOM nAtom,LPWSTR lpBuffer,int nSize);
-  WINBASEAPI ATOM WINAPI AddAtomA(LPCSTR lpString);
-  WINBASEAPI ATOM WINAPI AddAtomW(LPCWSTR lpString);
-  WINBASEAPI ATOM WINAPI FindAtomA(LPCSTR lpString);
-  WINBASEAPI ATOM WINAPI FindAtomW(LPCWSTR lpString);
-  WINBASEAPI UINT WINAPI GetAtomNameA(ATOM nAtom,LPSTR lpBuffer,int nSize);
-  WINBASEAPI UINT WINAPI GetAtomNameW(ATOM nAtom,LPWSTR lpBuffer,int nSize);
-  WINBASEAPI UINT WINAPI GetProfileIntA(LPCSTR lpAppName,LPCSTR lpKeyName,INT nDefault);
-  WINBASEAPI UINT WINAPI GetProfileIntW(LPCWSTR lpAppName,LPCWSTR lpKeyName,INT nDefault);
-  WINBASEAPI DWORD WINAPI GetProfileStringA(LPCSTR lpAppName,LPCSTR lpKeyName,LPCSTR lpDefault,LPSTR lpReturnedString,DWORD nSize);
-  WINBASEAPI DWORD WINAPI GetProfileStringW(LPCWSTR lpAppName,LPCWSTR lpKeyName,LPCWSTR lpDefault,LPWSTR lpReturnedString,DWORD nSize);
-  WINBASEAPI WINBOOL WINAPI WriteProfileStringA(LPCSTR lpAppName,LPCSTR lpKeyName,LPCSTR lpString);
-  WINBASEAPI WINBOOL WINAPI WriteProfileStringW(LPCWSTR lpAppName,LPCWSTR lpKeyName,LPCWSTR lpString);
-  WINBASEAPI DWORD WINAPI GetProfileSectionA(LPCSTR lpAppName,LPSTR lpReturnedString,DWORD nSize);
-  WINBASEAPI DWORD WINAPI GetProfileSectionW(LPCWSTR lpAppName,LPWSTR lpReturnedString,DWORD nSize);
-  WINBASEAPI WINBOOL WINAPI WriteProfileSectionA(LPCSTR lpAppName,LPCSTR lpString);
-  WINBASEAPI WINBOOL WINAPI WriteProfileSectionW(LPCWSTR lpAppName,LPCWSTR lpString);
-  WINBASEAPI UINT WINAPI GetPrivateProfileIntA(LPCSTR lpAppName,LPCSTR lpKeyName,INT nDefault,LPCSTR lpFileName);
-  WINBASEAPI UINT WINAPI GetPrivateProfileIntW(LPCWSTR lpAppName,LPCWSTR lpKeyName,INT nDefault,LPCWSTR lpFileName);
-  WINBASEAPI DWORD WINAPI GetPrivateProfileStringA(LPCSTR lpAppName,LPCSTR lpKeyName,LPCSTR lpDefault,LPSTR lpReturnedString,DWORD nSize,LPCSTR lpFileName);
-  WINBASEAPI DWORD WINAPI GetPrivateProfileStringW(LPCWSTR lpAppName,LPCWSTR lpKeyName,LPCWSTR lpDefault,LPWSTR lpReturnedString,DWORD nSize,LPCWSTR lpFileName);
-  WINBASEAPI WINBOOL WINAPI WritePrivateProfileStringA(LPCSTR lpAppName,LPCSTR lpKeyName,LPCSTR lpString,LPCSTR lpFileName);
-  WINBASEAPI WINBOOL WINAPI WritePrivateProfileStringW(LPCWSTR lpAppName,LPCWSTR lpKeyName,LPCWSTR lpString,LPCWSTR lpFileName);
-  WINBASEAPI DWORD WINAPI GetPrivateProfileSectionA(LPCSTR lpAppName,LPSTR lpReturnedString,DWORD nSize,LPCSTR lpFileName);
-  WINBASEAPI DWORD WINAPI GetPrivateProfileSectionW(LPCWSTR lpAppName,LPWSTR lpReturnedString,DWORD nSize,LPCWSTR lpFileName);
-  WINBASEAPI WINBOOL WINAPI WritePrivateProfileSectionA(LPCSTR lpAppName,LPCSTR lpString,LPCSTR lpFileName);
-  WINBASEAPI WINBOOL WINAPI WritePrivateProfileSectionW(LPCWSTR lpAppName,LPCWSTR lpString,LPCWSTR lpFileName);
-  WINBASEAPI DWORD WINAPI GetPrivateProfileSectionNamesA(LPSTR lpszReturnBuffer,DWORD nSize,LPCSTR lpFileName);
-  WINBASEAPI DWORD WINAPI GetPrivateProfileSectionNamesW(LPWSTR lpszReturnBuffer,DWORD nSize,LPCWSTR lpFileName);
-  WINBASEAPI WINBOOL WINAPI GetPrivateProfileStructA(LPCSTR lpszSection,LPCSTR lpszKey,LPVOID lpStruct,UINT uSizeStruct,LPCSTR szFile);
-  WINBASEAPI WINBOOL WINAPI GetPrivateProfileStructW(LPCWSTR lpszSection,LPCWSTR lpszKey,LPVOID lpStruct,UINT uSizeStruct,LPCWSTR szFile);
-  WINBASEAPI WINBOOL WINAPI WritePrivateProfileStructA(LPCSTR lpszSection,LPCSTR lpszKey,LPVOID lpStruct,UINT uSizeStruct,LPCSTR szFile);
-  WINBASEAPI WINBOOL WINAPI WritePrivateProfileStructW(LPCWSTR lpszSection,LPCWSTR lpszKey,LPVOID lpStruct,UINT uSizeStruct,LPCWSTR szFile);
-  WINBASEAPI UINT WINAPI GetDriveTypeA(LPCSTR lpRootPathName);
-  WINBASEAPI UINT WINAPI GetDriveTypeW(LPCWSTR lpRootPathName);
-  WINBASEAPI UINT WINAPI GetSystemDirectoryA(LPSTR lpBuffer,UINT uSize);
-  WINBASEAPI UINT WINAPI GetSystemDirectoryW(LPWSTR lpBuffer,UINT uSize);
-  WINBASEAPI DWORD WINAPI GetTempPathA(DWORD nBufferLength,LPSTR lpBuffer);
-  WINBASEAPI DWORD WINAPI GetTempPathW(DWORD nBufferLength,LPWSTR lpBuffer);
-  WINBASEAPI UINT WINAPI GetTempFileNameA(LPCSTR lpPathName,LPCSTR lpPrefixString,UINT uUnique,LPSTR lpTempFileName);
-  WINBASEAPI UINT WINAPI GetTempFileNameW(LPCWSTR lpPathName,LPCWSTR lpPrefixString,UINT uUnique,LPWSTR lpTempFileName);
-  WINBASEAPI UINT WINAPI GetWindowsDirectoryA(LPSTR lpBuffer,UINT uSize);
-  WINBASEAPI UINT WINAPI GetWindowsDirectoryW(LPWSTR lpBuffer,UINT uSize);
-  WINBASEAPI UINT WINAPI GetSystemWindowsDirectoryA(LPSTR lpBuffer,UINT uSize);
-  WINBASEAPI UINT WINAPI GetSystemWindowsDirectoryW(LPWSTR lpBuffer,UINT uSize);
-
-#ifndef RC_INVOKED
-#ifdef UNICODE
-#define GetSystemWow64Directory GetSystemWow64DirectoryW
-#else
-#define GetSystemWow64Directory GetSystemWow64DirectoryA
-#endif
-
-  WINBASEAPI UINT WINAPI GetSystemWow64DirectoryA(LPSTR lpBuffer,UINT uSize);
-  WINBASEAPI UINT WINAPI GetSystemWow64DirectoryW(LPWSTR lpBuffer,UINT uSize);
-  WINBASEAPI BOOLEAN WINAPI Wow64EnableWow64FsRedirection(BOOLEAN Wow64FsEnableRedirection);
-  WINBASEAPI WINBOOL WINAPI Wow64DisableWow64FsRedirection(PVOID *OldValue);
-  WINBASEAPI WINBOOL WINAPI Wow64RevertWow64FsRedirection(PVOID OlValue);
-
-  typedef UINT (WINAPI *PGET_SYSTEM_WOW64_DIRECTORY_A)(LPSTR lpBuffer,UINT uSize);
-  typedef UINT (WINAPI *PGET_SYSTEM_WOW64_DIRECTORY_W)(LPWSTR lpBuffer,UINT uSize);
-
-#define GET_SYSTEM_WOW64_DIRECTORY_NAME_A_A "GetSystemWow64DirectoryA"
-#define GET_SYSTEM_WOW64_DIRECTORY_NAME_A_W L"GetSystemWow64DirectoryA"
-#define GET_SYSTEM_WOW64_DIRECTORY_NAME_A_T TEXT("GetSystemWow64DirectoryA")
-#define GET_SYSTEM_WOW64_DIRECTORY_NAME_W_A "GetSystemWow64DirectoryW"
-#define GET_SYSTEM_WOW64_DIRECTORY_NAME_W_W L"GetSystemWow64DirectoryW"
-#define GET_SYSTEM_WOW64_DIRECTORY_NAME_W_T TEXT("GetSystemWow64DirectoryW")
-
-#ifdef UNICODE
-#define GET_SYSTEM_WOW64_DIRECTORY_NAME_T_A GET_SYSTEM_WOW64_DIRECTORY_NAME_W_A
-#define GET_SYSTEM_WOW64_DIRECTORY_NAME_T_W GET_SYSTEM_WOW64_DIRECTORY_NAME_W_W
-#define GET_SYSTEM_WOW64_DIRECTORY_NAME_T_T GET_SYSTEM_WOW64_DIRECTORY_NAME_W_T
-#else
-#define GET_SYSTEM_WOW64_DIRECTORY_NAME_T_A GET_SYSTEM_WOW64_DIRECTORY_NAME_A_A
-#define GET_SYSTEM_WOW64_DIRECTORY_NAME_T_W GET_SYSTEM_WOW64_DIRECTORY_NAME_A_W
-#define GET_SYSTEM_WOW64_DIRECTORY_NAME_T_T GET_SYSTEM_WOW64_DIRECTORY_NAME_A_T
-#endif
-#endif
-
-#ifdef UNICODE
-#define SetCurrentDirectory SetCurrentDirectoryW
-#define GetCurrentDirectory GetCurrentDirectoryW
-#define SetDllDirectory SetDllDirectoryW
-#define GetDllDirectory GetDllDirectoryW
-#define GetDiskFreeSpace GetDiskFreeSpaceW
-#define GetDiskFreeSpaceEx GetDiskFreeSpaceExW
-#define CreateDirectory CreateDirectoryW
-#define CreateDirectoryEx CreateDirectoryExW
-#define RemoveDirectory RemoveDirectoryW
-#define GetFullPathName GetFullPathNameW
-#define DefineDosDevice DefineDosDeviceW
-#define QueryDosDevice QueryDosDeviceW
-#define CreateFile CreateFileW
-#define SetFileAttributes SetFileAttributesW
-#define GetFileAttributes GetFileAttributesW
-#else
-#define SetCurrentDirectory SetCurrentDirectoryA
-#define GetCurrentDirectory GetCurrentDirectoryA
-#define SetDllDirectory SetDllDirectoryA
-#define GetDllDirectory GetDllDirectoryA
-#define GetDiskFreeSpace GetDiskFreeSpaceA
-#define GetDiskFreeSpaceEx GetDiskFreeSpaceExA
-#define CreateDirectory CreateDirectoryA
-#define CreateDirectoryEx CreateDirectoryExA
-#define RemoveDirectory RemoveDirectoryA
-#define GetFullPathName GetFullPathNameA
-#define DefineDosDevice DefineDosDeviceA
-#define QueryDosDevice QueryDosDeviceA
-#define CreateFile CreateFileA
-#define SetFileAttributes SetFileAttributesA
-#define GetFileAttributes GetFileAttributesA
-#endif
-
-  WINBASEAPI WINBOOL WINAPI SetCurrentDirectoryA(LPCSTR lpPathName);
-  WINBASEAPI WINBOOL WINAPI SetCurrentDirectoryW(LPCWSTR lpPathName);
-  WINBASEAPI DWORD WINAPI GetCurrentDirectoryA(DWORD nBufferLength,LPSTR lpBuffer);
-  WINBASEAPI DWORD WINAPI GetCurrentDirectoryW(DWORD nBufferLength,LPWSTR lpBuffer);
-  WINBASEAPI WINBOOL WINAPI SetDllDirectoryA(LPCSTR lpPathName);
-  WINBASEAPI WINBOOL WINAPI SetDllDirectoryW(LPCWSTR lpPathName);
-  WINBASEAPI DWORD WINAPI GetDllDirectoryA(DWORD nBufferLength,LPSTR lpBuffer);
-  WINBASEAPI DWORD WINAPI GetDllDirectoryW(DWORD nBufferLength,LPWSTR lpBuffer);
-  WINBASEAPI WINBOOL WINAPI GetDiskFreeSpaceA(LPCSTR lpRootPathName,LPDWORD lpSectorsPerCluster,LPDWORD lpBytesPerSector,LPDWORD lpNumberOfFreeClusters,LPDWORD lpTotalNumberOfClusters);
-  WINBASEAPI WINBOOL WINAPI GetDiskFreeSpaceW(LPCWSTR lpRootPathName,LPDWORD lpSectorsPerCluster,LPDWORD lpBytesPerSector,LPDWORD lpNumberOfFreeClusters,LPDWORD lpTotalNumberOfClusters);
-  WINBASEAPI WINBOOL WINAPI GetDiskFreeSpaceExA(LPCSTR lpDirectoryName,PULARGE_INTEGER lpFreeBytesAvailableToCaller,PULARGE_INTEGER lpTotalNumberOfBytes,PULARGE_INTEGER lpTotalNumberOfFreeBytes);
-  WINBASEAPI WINBOOL WINAPI GetDiskFreeSpaceExW(LPCWSTR lpDirectoryName,PULARGE_INTEGER lpFreeBytesAvailableToCaller,PULARGE_INTEGER lpTotalNumberOfBytes,PULARGE_INTEGER lpTotalNumberOfFreeBytes);
-  WINBASEAPI WINBOOL WINAPI CreateDirectoryA(LPCSTR lpPathName,LPSECURITY_ATTRIBUTES lpSecurityAttributes);
-  WINBASEAPI WINBOOL WINAPI CreateDirectoryW(LPCWSTR lpPathName,LPSECURITY_ATTRIBUTES lpSecurityAttributes);
-  WINBASEAPI WINBOOL WINAPI CreateDirectoryExA(LPCSTR lpTemplateDirectory,LPCSTR lpNewDirectory,LPSECURITY_ATTRIBUTES lpSecurityAttributes);
-  WINBASEAPI WINBOOL WINAPI CreateDirectoryExW(LPCWSTR lpTemplateDirectory,LPCWSTR lpNewDirectory,LPSECURITY_ATTRIBUTES lpSecurityAttributes);
-  WINBASEAPI WINBOOL WINAPI RemoveDirectoryA(LPCSTR lpPathName);
-  WINBASEAPI WINBOOL WINAPI RemoveDirectoryW(LPCWSTR lpPathName);
-  WINBASEAPI DWORD WINAPI GetFullPathNameA(LPCSTR lpFileName,DWORD nBufferLength,LPSTR lpBuffer,LPSTR *lpFilePart);
-  WINBASEAPI DWORD WINAPI GetFullPathNameW(LPCWSTR lpFileName,DWORD nBufferLength,LPWSTR lpBuffer,LPWSTR *lpFilePart);
-
-#define DDD_RAW_TARGET_PATH 0x1
-#define DDD_REMOVE_DEFINITION 0x2
-#define DDD_EXACT_MATCH_ON_REMOVE 0x4
-#define DDD_NO_BROADCAST_SYSTEM 0x8
-#define DDD_LUID_BROADCAST_DRIVE 0x10
-
-  WINBASEAPI WINBOOL WINAPI DefineDosDeviceA(DWORD dwFlags,LPCSTR lpDeviceName,LPCSTR lpTargetPath);
-  WINBASEAPI WINBOOL WINAPI DefineDosDeviceW(DWORD dwFlags,LPCWSTR lpDeviceName,LPCWSTR lpTargetPath);
-  WINBASEAPI DWORD WINAPI QueryDosDeviceA(LPCSTR lpDeviceName,LPSTR lpTargetPath,DWORD ucchMax);
-  WINBASEAPI DWORD WINAPI QueryDosDeviceW(LPCWSTR lpDeviceName,LPWSTR lpTargetPath,DWORD ucchMax);
-
-#define EXPAND_LOCAL_DRIVES
-
-  WINBASEAPI HANDLE WINAPI CreateFileA(LPCSTR lpFileName,DWORD dwDesiredAccess,DWORD dwShareMode,LPSECURITY_ATTRIBUTES lpSecurityAttributes,DWORD dwCreationDisposition,DWORD dwFlagsAndAttributes,HANDLE hTemplateFile);
-  WINBASEAPI HANDLE WINAPI CreateFileW(LPCWSTR lpFileName,DWORD dwDesiredAccess,DWORD dwShareMode,LPSECURITY_ATTRIBUTES lpSecurityAttributes,DWORD dwCreationDisposition,DWORD dwFlagsAndAttributes,HANDLE hTemplateFile);
-  WINBASEAPI HANDLE WINAPI ReOpenFile(HANDLE hOriginalFile,DWORD dwDesiredAccess,DWORD dwShareMode,DWORD dwFlagsAndAttributes);
-  WINBASEAPI WINBOOL WINAPI SetFileAttributesA(LPCSTR lpFileName,DWORD dwFileAttributes);
-  WINBASEAPI WINBOOL WINAPI SetFileAttributesW(LPCWSTR lpFileName,DWORD dwFileAttributes);
-  WINBASEAPI DWORD WINAPI GetFileAttributesA(LPCSTR lpFileName);
-  WINBASEAPI DWORD WINAPI GetFileAttributesW(LPCWSTR lpFileName);
-
-  typedef enum _GET_FILEEX_INFO_LEVELS {
-    GetFileExInfoStandard,GetFileExMaxInfoLevel
-  } GET_FILEEX_INFO_LEVELS;
-
-#ifdef UNICODE
-#define GetFileAttributesEx GetFileAttributesExW
-#define GetCompressedFileSize GetCompressedFileSizeW
-#define DeleteFile DeleteFileW
-#define CheckNameLegalDOS8Dot3 CheckNameLegalDOS8Dot3W
-#else
-#define GetFileAttributesEx GetFileAttributesExA
-#define GetCompressedFileSize GetCompressedFileSizeA
-#define DeleteFile DeleteFileA
-#define CheckNameLegalDOS8Dot3 CheckNameLegalDOS8Dot3A
-#endif
-
-  WINBASEAPI WINBOOL WINAPI GetFileAttributesExA(LPCSTR lpFileName,GET_FILEEX_INFO_LEVELS fInfoLevelId,LPVOID lpFileInformation);
-  WINBASEAPI WINBOOL WINAPI GetFileAttributesExW(LPCWSTR lpFileName,GET_FILEEX_INFO_LEVELS fInfoLevelId,LPVOID lpFileInformation);
-  WINBASEAPI DWORD WINAPI GetCompressedFileSizeA(LPCSTR lpFileName,LPDWORD lpFileSizeHigh);
-  WINBASEAPI DWORD WINAPI GetCompressedFileSizeW(LPCWSTR lpFileName,LPDWORD lpFileSizeHigh);
-  WINBASEAPI WINBOOL WINAPI DeleteFileA(LPCSTR lpFileName);
-  WINBASEAPI WINBOOL WINAPI DeleteFileW(LPCWSTR lpFileName);
-  WINBASEAPI WINBOOL WINAPI CheckNameLegalDOS8Dot3A(LPCSTR lpName,LPSTR lpOemName,DWORD OemNameSize,PBOOL pbNameContainsSpaces,PBOOL pbNameLegal);
-  WINBASEAPI WINBOOL WINAPI CheckNameLegalDOS8Dot3W(LPCWSTR lpName,LPSTR lpOemName,DWORD OemNameSize,PBOOL pbNameContainsSpaces,PBOOL pbNameLegal);
-
-  typedef enum _FINDEX_INFO_LEVELS {
-    FindExInfoStandard,FindExInfoMaxInfoLevel
-  } FINDEX_INFO_LEVELS;
-
-  typedef enum _FINDEX_SEARCH_OPS {
-    FindExSearchNameMatch,FindExSearchLimitToDirectories,FindExSearchLimitToDevices,FindExSearchMaxSearchOp
-  } FINDEX_SEARCH_OPS;
-
-#define FIND_FIRST_EX_CASE_SENSITIVE 0x1
-
-#ifdef UNICODE
-#define FindFirstFileEx FindFirstFileExW
-#define FindFirstFile FindFirstFileW
-#define FindNextFile FindNextFileW
-#define SearchPath SearchPathW
-#define CopyFile CopyFileW
-#define CopyFileEx CopyFileExW
-#define MoveFile MoveFileW
-#define MoveFileEx MoveFileExW
-#define MoveFileWithProgress MoveFileWithProgressW
-#define ReplaceFile ReplaceFileW
-#define CreateHardLink CreateHardLinkW
-#define CreateNamedPipe CreateNamedPipeW
-#define GetNamedPipeHandleState GetNamedPipeHandleStateW
-#define CallNamedPipe CallNamedPipeW
-#define WaitNamedPipe WaitNamedPipeW
-#define SetVolumeLabel SetVolumeLabelW
-#define GetVolumeInformation GetVolumeInformationW
-#define ClearEventLog ClearEventLogW
-#define BackupEventLog BackupEventLogW
-#define OpenEventLog OpenEventLogW
-#define RegisterEventSource RegisterEventSourceW
-#define OpenBackupEventLog OpenBackupEventLogW
-#define ReadEventLog ReadEventLogW
-#define ReportEvent ReportEventW
-#define AccessCheckAndAuditAlarm AccessCheckAndAuditAlarmW
-#define AccessCheckByTypeAndAuditAlarm AccessCheckByTypeAndAuditAlarmW
-#define AccessCheckByTypeResultListAndAuditAlarm AccessCheckByTypeResultListAndAuditAlarmW
-#define AccessCheckByTypeResultListAndAuditAlarmByHandle AccessCheckByTypeResultListAndAuditAlarmByHandleW
-#define ObjectOpenAuditAlarm ObjectOpenAuditAlarmW
-#define ObjectPrivilegeAuditAlarm ObjectPrivilegeAuditAlarmW
-#define ObjectCloseAuditAlarm ObjectCloseAuditAlarmW
-#define ObjectDeleteAuditAlarm ObjectDeleteAuditAlarmW
-#define PrivilegedServiceAuditAlarm PrivilegedServiceAuditAlarmW
-#define SetFileSecurity SetFileSecurityW
-#define GetFileSecurity GetFileSecurityW
-#define FindFirstChangeNotification FindFirstChangeNotificationW
-#define IsBadStringPtr IsBadStringPtrW
-#define LookupAccountSid LookupAccountSidW
-#define LookupAccountName LookupAccountNameW
-#define LookupPrivilegeValue LookupPrivilegeValueW
-#define LookupPrivilegeName LookupPrivilegeNameW
-#define LookupPrivilegeDisplayName LookupPrivilegeDisplayNameW
-#define BuildCommDCB BuildCommDCBW
-#define BuildCommDCBAndTimeouts BuildCommDCBAndTimeoutsW
-#define CommConfigDialog CommConfigDialogW
-#define GetDefaultCommConfig GetDefaultCommConfigW
-#define SetDefaultCommConfig SetDefaultCommConfigW
-#define GetComputerName GetComputerNameW
-#define SetComputerName SetComputerNameW
-#define GetComputerNameEx GetComputerNameExW
-#define SetComputerNameEx SetComputerNameExW
-#define DnsHostnameToComputerName DnsHostnameToComputerNameW
-#define GetUserName GetUserNameW
-#else
-#define FindFirstFileEx FindFirstFileExA
-#define FindFirstFile FindFirstFileA
-#define FindNextFile FindNextFileA
-#define SearchPath SearchPathA
-#define CopyFile CopyFileA
-#define CopyFileEx CopyFileExA
-#define MoveFile MoveFileA
-#define MoveFileEx MoveFileExA
-#define MoveFileWithProgress MoveFileWithProgressA
-#define ReplaceFile ReplaceFileA
-#define CreateHardLink CreateHardLinkA
-#define CreateNamedPipe CreateNamedPipeA
-#define GetNamedPipeHandleState GetNamedPipeHandleStateA
-#define CallNamedPipe CallNamedPipeA
-#define WaitNamedPipe WaitNamedPipeA
-#define SetVolumeLabel SetVolumeLabelA
-#define GetVolumeInformation GetVolumeInformationA
-#define ClearEventLog ClearEventLogA
-#define BackupEventLog BackupEventLogA
-#define OpenEventLog OpenEventLogA
-#define RegisterEventSource RegisterEventSourceA
-#define OpenBackupEventLog OpenBackupEventLogA
-#define ReadEventLog ReadEventLogA
-#define ReportEvent ReportEventA
-#define AccessCheckAndAuditAlarm AccessCheckAndAuditAlarmA
-#define AccessCheckByTypeAndAuditAlarm AccessCheckByTypeAndAuditAlarmA
-#define AccessCheckByTypeResultListAndAuditAlarm AccessCheckByTypeResultListAndAuditAlarmA
-#define AccessCheckByTypeResultListAndAuditAlarmByHandle AccessCheckByTypeResultListAndAuditAlarmByHandleA
-#define ObjectOpenAuditAlarm ObjectOpenAuditAlarmA
-#define ObjectPrivilegeAuditAlarm ObjectPrivilegeAuditAlarmA
-#define ObjectCloseAuditAlarm ObjectCloseAuditAlarmA
-#define ObjectDeleteAuditAlarm ObjectDeleteAuditAlarmA
-#define PrivilegedServiceAuditAlarm PrivilegedServiceAuditAlarmA
-#define SetFileSecurity SetFileSecurityA
-#define GetFileSecurity GetFileSecurityA
-#define FindFirstChangeNotification FindFirstChangeNotificationA
-#define IsBadStringPtr IsBadStringPtrA
-#define LookupAccountSid LookupAccountSidA
-#define LookupAccountName LookupAccountNameA
-#define LookupPrivilegeValue LookupPrivilegeValueA
-#define LookupPrivilegeName LookupPrivilegeNameA
-#define LookupPrivilegeDisplayName LookupPrivilegeDisplayNameA
-#define BuildCommDCB BuildCommDCBA
-#define BuildCommDCBAndTimeouts BuildCommDCBAndTimeoutsA
-#define CommConfigDialog CommConfigDialogA
-#define GetDefaultCommConfig GetDefaultCommConfigA
-#define SetDefaultCommConfig SetDefaultCommConfigA
-#define GetComputerName GetComputerNameA
-#define SetComputerName SetComputerNameA
-#define GetComputerNameEx GetComputerNameExA
-#define SetComputerNameEx SetComputerNameExA
-#define DnsHostnameToComputerName DnsHostnameToComputerNameA
-#define GetUserName GetUserNameA
-#endif
-
-  WINBASEAPI HANDLE WINAPI FindFirstFileExA(LPCSTR lpFileName,FINDEX_INFO_LEVELS fInfoLevelId,LPVOID lpFindFileData,FINDEX_SEARCH_OPS fSearchOp,LPVOID lpSearchFilter,DWORD dwAdditionalFlags);
-  WINBASEAPI HANDLE WINAPI FindFirstFileExW(LPCWSTR lpFileName,FINDEX_INFO_LEVELS fInfoLevelId,LPVOID lpFindFileData,FINDEX_SEARCH_OPS fSearchOp,LPVOID lpSearchFilter,DWORD dwAdditionalFlags);
-  WINBASEAPI HANDLE WINAPI FindFirstFileA(LPCSTR lpFileName,LPWIN32_FIND_DATAA lpFindFileData);
-  WINBASEAPI HANDLE WINAPI FindFirstFileW(LPCWSTR lpFileName,LPWIN32_FIND_DATAW lpFindFileData);
-  WINBASEAPI WINBOOL WINAPI FindNextFileA(HANDLE hFindFile,LPWIN32_FIND_DATAA lpFindFileData);
-  WINBASEAPI WINBOOL WINAPI FindNextFileW(HANDLE hFindFile,LPWIN32_FIND_DATAW lpFindFileData);
-  WINBASEAPI DWORD WINAPI SearchPathA(LPCSTR lpPath,LPCSTR lpFileName,LPCSTR lpExtension,DWORD nBufferLength,LPSTR lpBuffer,LPSTR *lpFilePart);
-  WINBASEAPI DWORD WINAPI SearchPathW(LPCWSTR lpPath,LPCWSTR lpFileName,LPCWSTR lpExtension,DWORD nBufferLength,LPWSTR lpBuffer,LPWSTR *lpFilePart);
-  WINBASEAPI WINBOOL WINAPI CopyFileA(LPCSTR lpExistingFileName,LPCSTR lpNewFileName,WINBOOL bFailIfExists);
-  WINBASEAPI WINBOOL WINAPI CopyFileW(LPCWSTR lpExistingFileName,LPCWSTR lpNewFileName,WINBOOL bFailIfExists);
-
-  typedef DWORD (WINAPI *LPPROGRESS_ROUTINE)(LARGE_INTEGER TotalFileSize,LARGE_INTEGER TotalBytesTransferred,LARGE_INTEGER StreamSize,LARGE_INTEGER StreamBytesTransferred,DWORD dwStreamNumber,DWORD dwCallbackReason,HANDLE hSourceFile,HANDLE hDestinationFile,LPVOID lpData);
-
-  WINBASEAPI WINBOOL WINAPI CopyFileExA(LPCSTR lpExistingFileName,LPCSTR lpNewFileName,LPPROGRESS_ROUTINE lpProgressRoutine,LPVOID lpData,LPBOOL pbCancel,DWORD dwCopyFlags);
-  WINBASEAPI WINBOOL WINAPI CopyFileExW(LPCWSTR lpExistingFileName,LPCWSTR lpNewFileName,LPPROGRESS_ROUTINE lpProgressRoutine,LPVOID lpData,LPBOOL pbCancel,DWORD dwCopyFlags);
-  WINBASEAPI WINBOOL WINAPI MoveFileA(LPCSTR lpExistingFileName,LPCSTR lpNewFileName);
-  WINBASEAPI WINBOOL WINAPI MoveFileW(LPCWSTR lpExistingFileName,LPCWSTR lpNewFileName);
-  WINBASEAPI WINBOOL WINAPI MoveFileExA(LPCSTR lpExistingFileName,LPCSTR lpNewFileName,DWORD dwFlags);
-  WINBASEAPI WINBOOL WINAPI MoveFileExW(LPCWSTR lpExistingFileName,LPCWSTR lpNewFileName,DWORD dwFlags);
-  WINBASEAPI WINBOOL WINAPI MoveFileWithProgressA(LPCSTR lpExistingFileName,LPCSTR lpNewFileName,LPPROGRESS_ROUTINE lpProgressRoutine,LPVOID lpData,DWORD dwFlags);
-  WINBASEAPI WINBOOL WINAPI MoveFileWithProgressW(LPCWSTR lpExistingFileName,LPCWSTR lpNewFileName,LPPROGRESS_ROUTINE lpProgressRoutine,LPVOID lpData,DWORD dwFlags);
-
-#define MOVEFILE_REPLACE_EXISTING 0x1
-#define MOVEFILE_COPY_ALLOWED 0x2
-#define MOVEFILE_DELAY_UNTIL_REBOOT 0x4
-#define MOVEFILE_WRITE_THROUGH 0x8
-#define MOVEFILE_CREATE_HARDLINK 0x10
-#define MOVEFILE_FAIL_IF_NOT_TRACKABLE 0x20
-
-  WINBASEAPI WINBOOL WINAPI ReplaceFileA(LPCSTR lpReplacedFileName,LPCSTR lpReplacementFileName,LPCSTR lpBackupFileName,DWORD dwReplaceFlags,LPVOID lpExclude,LPVOID lpReserved);
-  WINBASEAPI WINBOOL WINAPI ReplaceFileW(LPCWSTR lpReplacedFileName,LPCWSTR lpReplacementFileName,LPCWSTR lpBackupFileName,DWORD dwReplaceFlags,LPVOID lpExclude,LPVOID lpReserved);
-  WINBASEAPI WINBOOL WINAPI CreateHardLinkA(LPCSTR lpFileName,LPCSTR lpExistingFileName,LPSECURITY_ATTRIBUTES lpSecurityAttributes);
-  WINBASEAPI WINBOOL WINAPI CreateHardLinkW(LPCWSTR lpFileName,LPCWSTR lpExistingFileName,LPSECURITY_ATTRIBUTES lpSecurityAttributes);
-
-  typedef enum _STREAM_INFO_LEVELS {
-    FindStreamInfoStandard,FindStreamInfoMaxInfoLevel
-  } STREAM_INFO_LEVELS;
-
-  typedef struct _WIN32_FIND_STREAM_DATA {
-    LARGE_INTEGER StreamSize;
-    WCHAR cStreamName[MAX_PATH + 36];
-  } WIN32_FIND_STREAM_DATA,*PWIN32_FIND_STREAM_DATA;
-
-  HANDLE WINAPI FindFirstStreamW(LPCWSTR lpFileName,STREAM_INFO_LEVELS InfoLevel,LPVOID lpFindStreamData,DWORD dwFlags);
-  WINBOOL WINAPI FindNextStreamW(HANDLE hFindStream,LPVOID lpFindStreamData);
-  WINBASEAPI HANDLE WINAPI CreateNamedPipeA(LPCSTR lpName,DWORD dwOpenMode,DWORD dwPipeMode,DWORD nMaxInstances,DWORD nOutBufferSize,DWORD nInBufferSize,DWORD nDefaultTimeOut,LPSECURITY_ATTRIBUTES lpSecurityAttributes);
-  WINBASEAPI HANDLE WINAPI CreateNamedPipeW(LPCWSTR lpName,DWORD dwOpenMode,DWORD dwPipeMode,DWORD nMaxInstances,DWORD nOutBufferSize,DWORD nInBufferSize,DWORD nDefaultTimeOut,LPSECURITY_ATTRIBUTES lpSecurityAttributes);
-  WINBASEAPI WINBOOL WINAPI GetNamedPipeHandleStateA(HANDLE hNamedPipe,LPDWORD lpState,LPDWORD lpCurInstances,LPDWORD lpMaxCollectionCount,LPDWORD lpCollectDataTimeout,LPSTR lpUserName,DWORD nMaxUserNameSize);
-  WINBASEAPI WINBOOL WINAPI GetNamedPipeHandleStateW(HANDLE hNamedPipe,LPDWORD lpState,LPDWORD lpCurInstances,LPDWORD lpMaxCollectionCount,LPDWORD lpCollectDataTimeout,LPWSTR lpUserName,DWORD nMaxUserNameSize);
-  WINBASEAPI WINBOOL WINAPI CallNamedPipeA(LPCSTR lpNamedPipeName,LPVOID lpInBuffer,DWORD nInBufferSize,LPVOID lpOutBuffer,DWORD nOutBufferSize,LPDWORD lpBytesRead,DWORD nTimeOut);
-  WINBASEAPI WINBOOL WINAPI CallNamedPipeW(LPCWSTR lpNamedPipeName,LPVOID lpInBuffer,DWORD nInBufferSize,LPVOID lpOutBuffer,DWORD nOutBufferSize,LPDWORD lpBytesRead,DWORD nTimeOut);
-  WINBASEAPI WINBOOL WINAPI WaitNamedPipeA(LPCSTR lpNamedPipeName,DWORD nTimeOut);
-  WINBASEAPI WINBOOL WINAPI WaitNamedPipeW(LPCWSTR lpNamedPipeName,DWORD nTimeOut);
-  WINBASEAPI WINBOOL WINAPI SetVolumeLabelA(LPCSTR lpRootPathName,LPCSTR lpVolumeName);
-  WINBASEAPI WINBOOL WINAPI SetVolumeLabelW(LPCWSTR lpRootPathName,LPCWSTR lpVolumeName);
-  WINBASEAPI VOID WINAPI SetFileApisToOEM(VOID);
-  WINBASEAPI VOID WINAPI SetFileApisToANSI(VOID);
-  WINBASEAPI WINBOOL WINAPI AreFileApisANSI(VOID);
-  WINBASEAPI WINBOOL WINAPI GetVolumeInformationA(LPCSTR lpRootPathName,LPSTR lpVolumeNameBuffer,DWORD nVolumeNameSize,LPDWORD lpVolumeSerialNumber,LPDWORD lpMaximumComponentLength,LPDWORD lpFileSystemFlags,LPSTR lpFileSystemNameBuffer,DWORD nFileSystemNameSize);
-  WINBASEAPI WINBOOL WINAPI GetVolumeInformationW(LPCWSTR lpRootPathName,LPWSTR lpVolumeNameBuffer,DWORD nVolumeNameSize,LPDWORD lpVolumeSerialNumber,LPDWORD lpMaximumComponentLength,LPDWORD lpFileSystemFlags,LPWSTR lpFileSystemNameBuffer,DWORD nFileSystemNameSize);
-  WINBASEAPI WINBOOL WINAPI CancelIo(HANDLE hFile);
-  WINADVAPI WINBOOL WINAPI ClearEventLogA(HANDLE hEventLog,LPCSTR lpBackupFileName);
-  WINADVAPI WINBOOL WINAPI ClearEventLogW(HANDLE hEventLog,LPCWSTR lpBackupFileName);
-  WINADVAPI WINBOOL WINAPI BackupEventLogA(HANDLE hEventLog,LPCSTR lpBackupFileName);
-  WINADVAPI WINBOOL WINAPI BackupEventLogW(HANDLE hEventLog,LPCWSTR lpBackupFileName);
-  WINADVAPI WINBOOL WINAPI CloseEventLog(HANDLE hEventLog);
-  WINADVAPI WINBOOL WINAPI DeregisterEventSource(HANDLE hEventLog);
-  WINADVAPI WINBOOL WINAPI NotifyChangeEventLog(HANDLE hEventLog,HANDLE hEvent);
-  WINADVAPI WINBOOL WINAPI GetNumberOfEventLogRecords(HANDLE hEventLog,PDWORD NumberOfRecords);
-  WINADVAPI WINBOOL WINAPI GetOldestEventLogRecord(HANDLE hEventLog,PDWORD OldestRecord);
-  WINADVAPI HANDLE WINAPI OpenEventLogA(LPCSTR lpUNCServerName,LPCSTR lpSourceName);
-  WINADVAPI HANDLE WINAPI OpenEventLogW(LPCWSTR lpUNCServerName,LPCWSTR lpSourceName);
-  WINADVAPI HANDLE WINAPI RegisterEventSourceA(LPCSTR lpUNCServerName,LPCSTR lpSourceName);
-  WINADVAPI HANDLE WINAPI RegisterEventSourceW(LPCWSTR lpUNCServerName,LPCWSTR lpSourceName);
-  WINADVAPI HANDLE WINAPI OpenBackupEventLogA(LPCSTR lpUNCServerName,LPCSTR lpFileName);
-  WINADVAPI HANDLE WINAPI OpenBackupEventLogW(LPCWSTR lpUNCServerName,LPCWSTR lpFileName);
-  WINADVAPI WINBOOL WINAPI ReadEventLogA(HANDLE hEventLog,DWORD dwReadFlags,DWORD dwRecordOffset,LPVOID lpBuffer,DWORD nNumberOfBytesToRead,DWORD *pnBytesRead,DWORD *pnMinNumberOfBytesNeeded);
-  WINADVAPI WINBOOL WINAPI ReadEventLogW(HANDLE hEventLog,DWORD dwReadFlags,DWORD dwRecordOffset,LPVOID lpBuffer,DWORD nNumberOfBytesToRead,DWORD *pnBytesRead,DWORD *pnMinNumberOfBytesNeeded);
-  WINADVAPI WINBOOL WINAPI ReportEventA(HANDLE hEventLog,WORD wType,WORD wCategory,DWORD dwEventID,PSID lpUserSid,WORD wNumStrings,DWORD dwDataSize,LPCSTR *lpStrings,LPVOID lpRawData);
-  WINADVAPI WINBOOL WINAPI ReportEventW(HANDLE hEventLog,WORD wType,WORD wCategory,DWORD dwEventID,PSID lpUserSid,WORD wNumStrings,DWORD dwDataSize,LPCWSTR *lpStrings,LPVOID lpRawData);
-
-#define EVENTLOG_FULL_INFO 0
-
-  typedef struct _EVENTLOG_FULL_INFORMATION {
-    DWORD dwFull;
-  } EVENTLOG_FULL_INFORMATION,*LPEVENTLOG_FULL_INFORMATION;
-
-  WINADVAPI WINBOOL WINAPI GetEventLogInformation(HANDLE hEventLog,DWORD dwInfoLevel,LPVOID lpBuffer,DWORD cbBufSize,LPDWORD pcbBytesNeeded);
-  WINADVAPI WINBOOL WINAPI DuplicateToken(HANDLE ExistingTokenHandle,SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,PHANDLE DuplicateTokenHandle);
-  WINADVAPI WINBOOL WINAPI GetKernelObjectSecurity(HANDLE Handle,SECURITY_INFORMATION RequestedInformation,PSECURITY_DESCRIPTOR pSecurityDescriptor,DWORD nLength,LPDWORD lpnLengthNeeded);
-  WINADVAPI WINBOOL WINAPI ImpersonateNamedPipeClient(HANDLE hNamedPipe);
-  WINADVAPI WINBOOL WINAPI ImpersonateSelf(SECURITY_IMPERSONATION_LEVEL ImpersonationLevel);
-  WINADVAPI WINBOOL WINAPI RevertToSelf(VOID);
-  WINADVAPI WINBOOL WINAPI SetThreadToken (PHANDLE Thread,HANDLE Token);
-  WINADVAPI WINBOOL WINAPI AccessCheck(PSECURITY_DESCRIPTOR pSecurityDescriptor,HANDLE ClientToken,DWORD DesiredAccess,PGENERIC_MAPPING GenericMapping,PPRIVILEGE_SET PrivilegeSet,LPDWORD PrivilegeSetLength,LPDWORD GrantedAccess,LPBOOL AccessStatus);
-  WINADVAPI WINBOOL WINAPI AccessCheckByType(PSECURITY_DESCRIPTOR pSecurityDescriptor,PSID PrincipalSelfSid,HANDLE ClientToken,DWORD DesiredAccess,POBJECT_TYPE_LIST ObjectTypeList,DWORD ObjectTypeListLength,PGENERIC_MAPPING GenericMapping,PPRIVILEGE_SET PrivilegeSet,LPDWORD PrivilegeSetLength,LPDWORD GrantedAccess,LPBOOL AccessStatus);
-  WINADVAPI WINBOOL WINAPI AccessCheckByTypeResultList(PSECURITY_DESCRIPTOR pSecurityDescriptor,PSID PrincipalSelfSid,HANDLE ClientToken,DWORD DesiredAccess,POBJECT_TYPE_LIST ObjectTypeList,DWORD ObjectTypeListLength,PGENERIC_MAPPING GenericMapping,PPRIVILEGE_SET PrivilegeSet,LPDWORD PrivilegeSetLength,LPDWORD GrantedAccessList,LPDWORD AccessStatusList);
-  WINADVAPI WINBOOL WINAPI OpenProcessToken(HANDLE ProcessHandle,DWORD DesiredAccess,PHANDLE TokenHandle);
-  WINADVAPI WINBOOL WINAPI OpenThreadToken(HANDLE ThreadHandle,DWORD DesiredAccess,WINBOOL OpenAsSelf,PHANDLE TokenHandle);
-  WINADVAPI WINBOOL WINAPI GetTokenInformation(HANDLE TokenHandle,TOKEN_INFORMATION_CLASS TokenInformationClass,LPVOID TokenInformation,DWORD TokenInformationLength,PDWORD ReturnLength);
-  WINADVAPI WINBOOL WINAPI SetTokenInformation(HANDLE TokenHandle,TOKEN_INFORMATION_CLASS TokenInformationClass,LPVOID TokenInformation,DWORD TokenInformationLength);
-  WINADVAPI WINBOOL WINAPI AdjustTokenPrivileges(HANDLE TokenHandle,WINBOOL DisableAllPrivileges,PTOKEN_PRIVILEGES NewState,DWORD BufferLength,PTOKEN_PRIVILEGES PreviousState,PDWORD ReturnLength);
-  WINADVAPI WINBOOL WINAPI AdjustTokenGroups(HANDLE TokenHandle,WINBOOL ResetToDefault,PTOKEN_GROUPS NewState,DWORD BufferLength,PTOKEN_GROUPS PreviousState,PDWORD ReturnLength);
-  WINADVAPI WINBOOL WINAPI PrivilegeCheck(HANDLE ClientToken,PPRIVILEGE_SET RequiredPrivileges,LPBOOL pfResult);
-  WINADVAPI WINBOOL WINAPI AccessCheckAndAuditAlarmA(LPCSTR SubsystemName,LPVOID HandleId,LPSTR ObjectTypeName,LPSTR ObjectName,PSECURITY_DESCRIPTOR SecurityDescriptor,DWORD DesiredAccess,PGENERIC_MAPPING GenericMapping,WINBOOL ObjectCreation,LPDWORD GrantedAccess,LPBOOL AccessStatus,LPBOOL pfGenerateOnClose);
-  WINADVAPI WINBOOL WINAPI AccessCheckAndAuditAlarmW(LPCWSTR SubsystemName,LPVOID HandleId,LPWSTR ObjectTypeName,LPWSTR ObjectName,PSECURITY_DESCRIPTOR SecurityDescriptor,DWORD DesiredAccess,PGENERIC_MAPPING GenericMapping,WINBOOL ObjectCreation,LPDWORD GrantedAccess,LPBOOL AccessStatus,LPBOOL pfGenerateOnClose);
-  WINADVAPI WINBOOL WINAPI AccessCheckByTypeAndAuditAlarmA(LPCSTR SubsystemName,LPVOID HandleId,LPCSTR ObjectTypeName,LPCSTR ObjectName,PSECURITY_DESCRIPTOR SecurityDescriptor,PSID PrincipalSelfSid,DWORD DesiredAccess,AUDIT_EVENT_TYPE AuditType,DWORD Flags,POBJECT_TYPE_LIST ObjectTypeList,DWORD ObjectTypeListLength,PGENERIC_MAPPING GenericMapping,WINBOOL ObjectCreation,LPDWORD GrantedAccess,LPBOOL AccessStatus,LPBOOL pfGenerateOnClose);
-  WINADVAPI WINBOOL WINAPI AccessCheckByTypeAndAuditAlarmW(LPCWSTR SubsystemName,LPVOID HandleId,LPCWSTR ObjectTypeName,LPCWSTR ObjectName,PSECURITY_DESCRIPTOR SecurityDescriptor,PSID PrincipalSelfSid,DWORD DesiredAccess,AUDIT_EVENT_TYPE AuditType,DWORD Flags,POBJECT_TYPE_LIST ObjectTypeList,DWORD ObjectTypeListLength,PGENERIC_MAPPING GenericMapping,WINBOOL ObjectCreation,LPDWORD GrantedAccess,LPBOOL AccessStatus,LPBOOL pfGenerateOnClose);
-  WINADVAPI WINBOOL WINAPI AccessCheckByTypeResultListAndAuditAlarmA(LPCSTR SubsystemName,LPVOID HandleId,LPCSTR ObjectTypeName,LPCSTR ObjectName,PSECURITY_DESCRIPTOR SecurityDescriptor,PSID PrincipalSelfSid,DWORD DesiredAccess,AUDIT_EVENT_TYPE AuditType,DWORD Flags,POBJECT_TYPE_LIST ObjectTypeList,DWORD ObjectTypeListLength,PGENERIC_MAPPING GenericMapping,WINBOOL ObjectCreation,LPDWORD GrantedAccess,LPDWORD AccessStatusList,LPBOOL pfGenerateOnClose);
-  WINADVAPI WINBOOL WINAPI AccessCheckByTypeResultListAndAuditAlarmW(LPCWSTR SubsystemName,LPVOID HandleId,LPCWSTR ObjectTypeName,LPCWSTR ObjectName,PSECURITY_DESCRIPTOR SecurityDescriptor,PSID PrincipalSelfSid,DWORD DesiredAccess,AUDIT_EVENT_TYPE AuditType,DWORD Flags,POBJECT_TYPE_LIST ObjectTypeList,DWORD ObjectTypeListLength,PGENERIC_MAPPING GenericMapping,WINBOOL ObjectCreation,LPDWORD GrantedAccess,LPDWORD AccessStatusList,LPBOOL pfGenerateOnClose);
-  WINADVAPI WINBOOL WINAPI AccessCheckByTypeResultListAndAuditAlarmByHandleA(LPCSTR SubsystemName,LPVOID HandleId,HANDLE ClientToken,LPCSTR ObjectTypeName,LPCSTR ObjectName,PSECURITY_DESCRIPTOR SecurityDescriptor,PSID PrincipalSelfSid,DWORD DesiredAccess,AUDIT_EVENT_TYPE AuditType,DWORD Flags,POBJECT_TYPE_LIST ObjectTypeList,DWORD ObjectTypeListLength,PGENERIC_MAPPING GenericMapping,WINBOOL ObjectCreation,LPDWORD GrantedAccess,LPDWORD AccessStatusList,LPBOOL pfGenerateOnClose);
-  WINADVAPI WINBOOL WINAPI AccessCheckByTypeResultListAndAuditAlarmByHandleW(LPCWSTR SubsystemName,LPVOID HandleId,HANDLE ClientToken,LPCWSTR ObjectTypeName,LPCWSTR ObjectName,PSECURITY_DESCRIPTOR SecurityDescriptor,PSID PrincipalSelfSid,DWORD DesiredAccess,AUDIT_EVENT_TYPE AuditType,DWORD Flags,POBJECT_TYPE_LIST ObjectTypeList,DWORD ObjectTypeListLength,PGENERIC_MAPPING GenericMapping,WINBOOL ObjectCreation,LPDWORD GrantedAccess,LPDWORD AccessStatusList,LPBOOL pfGenerateOnClose);
-  WINADVAPI WINBOOL WINAPI ObjectOpenAuditAlarmA(LPCSTR SubsystemName,LPVOID HandleId,LPSTR ObjectTypeName,LPSTR ObjectName,PSECURITY_DESCRIPTOR pSecurityDescriptor,HANDLE ClientToken,DWORD DesiredAccess,DWORD GrantedAccess,PPRIVILEGE_SET Privileges,WINBOOL ObjectCreation,WINBOOL AccessGranted,LPBOOL GenerateOnClose);
-  WINADVAPI WINBOOL WINAPI ObjectOpenAuditAlarmW(LPCWSTR SubsystemName,LPVOID HandleId,LPWSTR ObjectTypeName,LPWSTR ObjectName,PSECURITY_DESCRIPTOR pSecurityDescriptor,HANDLE ClientToken,DWORD DesiredAccess,DWORD GrantedAccess,PPRIVILEGE_SET Privileges,WINBOOL ObjectCreation,WINBOOL AccessGranted,LPBOOL GenerateOnClose);
-  WINADVAPI WINBOOL WINAPI ObjectPrivilegeAuditAlarmA(LPCSTR SubsystemName,LPVOID HandleId,HANDLE ClientToken,DWORD DesiredAccess,PPRIVILEGE_SET Privileges,WINBOOL AccessGranted);
-  WINADVAPI WINBOOL WINAPI ObjectPrivilegeAuditAlarmW(LPCWSTR SubsystemName,LPVOID HandleId,HANDLE ClientToken,DWORD DesiredAccess,PPRIVILEGE_SET Privileges,WINBOOL AccessGranted);
-  WINADVAPI WINBOOL WINAPI ObjectCloseAuditAlarmA(LPCSTR SubsystemName,LPVOID HandleId,WINBOOL GenerateOnClose);
-  WINADVAPI WINBOOL WINAPI ObjectCloseAuditAlarmW(LPCWSTR SubsystemName,LPVOID HandleId,WINBOOL GenerateOnClose);
-  WINADVAPI WINBOOL WINAPI ObjectDeleteAuditAlarmA(LPCSTR SubsystemName,LPVOID HandleId,WINBOOL GenerateOnClose);
-  WINADVAPI WINBOOL WINAPI ObjectDeleteAuditAlarmW(LPCWSTR SubsystemName,LPVOID HandleId,WINBOOL GenerateOnClose);
-  WINADVAPI WINBOOL WINAPI PrivilegedServiceAuditAlarmA(LPCSTR SubsystemName,LPCSTR ServiceName,HANDLE ClientToken,PPRIVILEGE_SET Privileges,WINBOOL AccessGranted);
-  WINADVAPI WINBOOL WINAPI PrivilegedServiceAuditAlarmW(LPCWSTR SubsystemName,LPCWSTR ServiceName,HANDLE ClientToken,PPRIVILEGE_SET Privileges,WINBOOL AccessGranted);
-  WINADVAPI WINBOOL WINAPI IsWellKnownSid(PSID pSid,WELL_KNOWN_SID_TYPE WellKnownSidType);
-  WINADVAPI WINBOOL WINAPI CreateWellKnownSid(WELL_KNOWN_SID_TYPE WellKnownSidType,PSID DomainSid,PSID pSid,DWORD *cbSid);
-  WINADVAPI WINBOOL WINAPI EqualDomainSid(PSID pSid1,PSID pSid2,WINBOOL *pfEqual);
-  WINADVAPI WINBOOL WINAPI GetWindowsAccountDomainSid(PSID pSid,PSID pDomainSid,DWORD *cbDomainSid);
-  WINADVAPI WINBOOL WINAPI IsValidSid(PSID pSid);
-  WINADVAPI WINBOOL WINAPI EqualSid(PSID pSid1,PSID pSid2);
-  WINADVAPI WINBOOL WINAPI EqualPrefixSid(PSID pSid1,PSID pSid2);
-  WINADVAPI DWORD WINAPI GetSidLengthRequired (UCHAR nSubAuthorityCount);
-  WINADVAPI WINBOOL WINAPI AllocateAndInitializeSid(PSID_IDENTIFIER_AUTHORITY pIdentifierAuthority,BYTE nSubAuthorityCount,DWORD nSubAuthority0,DWORD nSubAuthority1,DWORD nSubAuthority2,DWORD nSubAuthority3,DWORD nSubAuthority4,DWORD nSubAuthority5,DWORD nSubAuthority6,DWORD nSubAuthority7,PSID *pSid);
-  WINADVAPI PVOID WINAPI FreeSid(PSID pSid);
-  WINADVAPI WINBOOL WINAPI InitializeSid(PSID Sid,PSID_IDENTIFIER_AUTHORITY pIdentifierAuthority,BYTE nSubAuthorityCount);
-  WINADVAPI PSID_IDENTIFIER_AUTHORITY WINAPI GetSidIdentifierAuthority(PSID pSid);
-  WINADVAPI PDWORD WINAPI GetSidSubAuthority(PSID pSid,DWORD nSubAuthority);
-  WINADVAPI PUCHAR WINAPI GetSidSubAuthorityCount(PSID pSid);
-  WINADVAPI DWORD WINAPI GetLengthSid(PSID pSid);
-  WINADVAPI WINBOOL WINAPI CopySid(DWORD nDestinationSidLength,PSID pDestinationSid,PSID pSourceSid);
-  WINADVAPI WINBOOL WINAPI AreAllAccessesGranted(DWORD GrantedAccess,DWORD DesiredAccess);
-  WINADVAPI WINBOOL WINAPI AreAnyAccessesGranted(DWORD GrantedAccess,DWORD DesiredAccess);
-  WINADVAPI VOID WINAPI MapGenericMask(PDWORD AccessMask,PGENERIC_MAPPING GenericMapping);
-  WINADVAPI WINBOOL WINAPI IsValidAcl(PACL pAcl);
-  WINADVAPI WINBOOL WINAPI InitializeAcl(PACL pAcl,DWORD nAclLength,DWORD dwAclRevision);
-  WINADVAPI WINBOOL WINAPI GetAclInformation(PACL pAcl,LPVOID pAclInformation,DWORD nAclInformationLength,ACL_INFORMATION_CLASS dwAclInformationClass);
-  WINADVAPI WINBOOL WINAPI SetAclInformation(PACL pAcl,LPVOID pAclInformation,DWORD nAclInformationLength,ACL_INFORMATION_CLASS dwAclInformationClass);
-  WINADVAPI WINBOOL WINAPI AddAce(PACL pAcl,DWORD dwAceRevision,DWORD dwStartingAceIndex,LPVOID pAceList,DWORD nAceListLength);
-  WINADVAPI WINBOOL WINAPI DeleteAce(PACL pAcl,DWORD dwAceIndex);
-  WINADVAPI WINBOOL WINAPI GetAce(PACL pAcl,DWORD dwAceIndex,LPVOID *pAce);
-  WINADVAPI WINBOOL WINAPI AddAccessAllowedAce(PACL pAcl,DWORD dwAceRevision,DWORD AccessMask,PSID pSid);
-  WINADVAPI WINBOOL WINAPI AddAccessAllowedAceEx(PACL pAcl,DWORD dwAceRevision,DWORD AceFlags,DWORD AccessMask,PSID pSid);
-  WINADVAPI WINBOOL WINAPI AddAccessDeniedAce(PACL pAcl,DWORD dwAceRevision,DWORD AccessMask,PSID pSid);
-  WINADVAPI WINBOOL WINAPI AddAccessDeniedAceEx(PACL pAcl,DWORD dwAceRevision,DWORD AceFlags,DWORD AccessMask,PSID pSid);
-  WINADVAPI WINBOOL WINAPI AddAuditAccessAce(PACL pAcl,DWORD dwAceRevision,DWORD dwAccessMask,PSID pSid,WINBOOL bAuditSuccess,WINBOOL bAuditFailure);
-  WINADVAPI WINBOOL WINAPI AddAuditAccessAceEx(PACL pAcl,DWORD dwAceRevision,DWORD AceFlags,DWORD dwAccessMask,PSID pSid,WINBOOL bAuditSuccess,WINBOOL bAuditFailure);
-  WINADVAPI WINBOOL WINAPI AddAccessAllowedObjectAce(PACL pAcl,DWORD dwAceRevision,DWORD AceFlags,DWORD AccessMask,GUID *ObjectTypeGuid,GUID *InheritedObjectTypeGuid,PSID pSid);
-  WINADVAPI WINBOOL WINAPI AddAccessDeniedObjectAce(PACL pAcl,DWORD dwAceRevision,DWORD AceFlags,DWORD AccessMask,GUID *ObjectTypeGuid,GUID *InheritedObjectTypeGuid,PSID pSid);
-  WINADVAPI WINBOOL WINAPI AddAuditAccessObjectAce(PACL pAcl,DWORD dwAceRevision,DWORD AceFlags,DWORD AccessMask,GUID *ObjectTypeGuid,GUID *InheritedObjectTypeGuid,PSID pSid,WINBOOL bAuditSuccess,WINBOOL bAuditFailure);
-  WINADVAPI WINBOOL WINAPI FindFirstFreeAce(PACL pAcl,LPVOID *pAce);
-  WINADVAPI WINBOOL WINAPI InitializeSecurityDescriptor(PSECURITY_DESCRIPTOR pSecurityDescriptor,DWORD dwRevision);
-  WINADVAPI WINBOOL WINAPI IsValidSecurityDescriptor(PSECURITY_DESCRIPTOR pSecurityDescriptor);
-  WINADVAPI DWORD WINAPI GetSecurityDescriptorLength(PSECURITY_DESCRIPTOR pSecurityDescriptor);
-  WINADVAPI WINBOOL WINAPI GetSecurityDescriptorControl(PSECURITY_DESCRIPTOR pSecurityDescriptor,PSECURITY_DESCRIPTOR_CONTROL pControl,LPDWORD lpdwRevision);
-  WINADVAPI WINBOOL WINAPI SetSecurityDescriptorControl(PSECURITY_DESCRIPTOR pSecurityDescriptor,SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest,SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet);
-  WINADVAPI WINBOOL WINAPI SetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR pSecurityDescriptor,WINBOOL bDaclPresent,PACL pDacl,WINBOOL bDaclDefaulted);
-  WINADVAPI WINBOOL WINAPI GetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR pSecurityDescriptor,LPBOOL lpbDaclPresent,PACL *pDacl,LPBOOL lpbDaclDefaulted);
-  WINADVAPI WINBOOL WINAPI SetSecurityDescriptorSacl(PSECURITY_DESCRIPTOR pSecurityDescriptor,WINBOOL bSaclPresent,PACL pSacl,WINBOOL bSaclDefaulted);
-  WINADVAPI WINBOOL WINAPI GetSecurityDescriptorSacl(PSECURITY_DESCRIPTOR pSecurityDescriptor,LPBOOL lpbSaclPresent,PACL *pSacl,LPBOOL lpbSaclDefaulted);
-  WINADVAPI WINBOOL WINAPI SetSecurityDescriptorOwner(PSECURITY_DESCRIPTOR pSecurityDescriptor,PSID pOwner,WINBOOL bOwnerDefaulted);
-  WINADVAPI WINBOOL WINAPI GetSecurityDescriptorOwner(PSECURITY_DESCRIPTOR pSecurityDescriptor,PSID *pOwner,LPBOOL lpbOwnerDefaulted);
-  WINADVAPI WINBOOL WINAPI SetSecurityDescriptorGroup(PSECURITY_DESCRIPTOR pSecurityDescriptor,PSID pGroup,WINBOOL bGroupDefaulted);
-  WINADVAPI WINBOOL WINAPI GetSecurityDescriptorGroup(PSECURITY_DESCRIPTOR pSecurityDescriptor,PSID *pGroup,LPBOOL lpbGroupDefaulted);
-  WINADVAPI DWORD WINAPI SetSecurityDescriptorRMControl(PSECURITY_DESCRIPTOR SecurityDescriptor,PUCHAR RMControl);
-  WINADVAPI DWORD WINAPI GetSecurityDescriptorRMControl(PSECURITY_DESCRIPTOR SecurityDescriptor,PUCHAR RMControl);
-  WINADVAPI WINBOOL WINAPI CreatePrivateObjectSecurity(PSECURITY_DESCRIPTOR ParentDescriptor,PSECURITY_DESCRIPTOR CreatorDescriptor,PSECURITY_DESCRIPTOR *NewDescriptor,WINBOOL IsDirectoryObject,HANDLE Token,PGENERIC_MAPPING GenericMapping);
-  WINADVAPI WINBOOL WINAPI ConvertToAutoInheritPrivateObjectSecurity(PSECURITY_DESCRIPTOR ParentDescriptor,PSECURITY_DESCRIPTOR CurrentSecurityDescriptor,PSECURITY_DESCRIPTOR *NewSecurityDescriptor,GUID *ObjectType,BOOLEAN IsDirectoryObject,PGENERIC_MAPPING GenericMapping);
-  WINADVAPI WINBOOL WINAPI CreatePrivateObjectSecurityEx(PSECURITY_DESCRIPTOR ParentDescriptor,PSECURITY_DESCRIPTOR CreatorDescriptor,PSECURITY_DESCRIPTOR *NewDescriptor,GUID *ObjectType,WINBOOL IsContainerObject,ULONG AutoInheritFlags,HANDLE Token,PGENERIC_MAPPING GenericMapping);
-  WINADVAPI WINBOOL WINAPI CreatePrivateObjectSecurityWithMultipleInheritance(PSECURITY_DESCRIPTOR ParentDescriptor,PSECURITY_DESCRIPTOR CreatorDescriptor,PSECURITY_DESCRIPTOR *NewDescriptor,GUID **ObjectTypes,ULONG GuidCount,WINBOOL IsContainerObject,ULONG AutoInheritFlags,HANDLE Token,PGENERIC_MAPPING GenericMapping);
-  WINADVAPI WINBOOL WINAPI SetPrivateObjectSecurity (SECURITY_INFORMATION SecurityInformation,PSECURITY_DESCRIPTOR ModificationDescriptor,PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor,PGENERIC_MAPPING GenericMapping,HANDLE Token);
-  WINADVAPI WINBOOL WINAPI SetPrivateObjectSecurityEx (SECURITY_INFORMATION SecurityInformation,PSECURITY_DESCRIPTOR ModificationDescriptor,PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor,ULONG AutoInheritFlags,PGENERIC_MAPPING GenericMapping,HANDLE Token);
-  WINADVAPI WINBOOL WINAPI GetPrivateObjectSecurity(PSECURITY_DESCRIPTOR ObjectDescriptor,SECURITY_INFORMATION SecurityInformation,PSECURITY_DESCRIPTOR ResultantDescriptor,DWORD DescriptorLength,PDWORD ReturnLength);
-  WINADVAPI WINBOOL WINAPI DestroyPrivateObjectSecurity(PSECURITY_DESCRIPTOR *ObjectDescriptor);
-  WINADVAPI WINBOOL WINAPI MakeSelfRelativeSD(PSECURITY_DESCRIPTOR pAbsoluteSecurityDescriptor,PSECURITY_DESCRIPTOR pSelfRelativeSecurityDescriptor,LPDWORD lpdwBufferLength);
-  WINADVAPI WINBOOL WINAPI MakeAbsoluteSD(PSECURITY_DESCRIPTOR pSelfRelativeSecurityDescriptor,PSECURITY_DESCRIPTOR pAbsoluteSecurityDescriptor,LPDWORD lpdwAbsoluteSecurityDescriptorSize,PACL pDacl,LPDWORD lpdwDaclSize,PACL pSacl,LPDWORD lpdwSaclSize,PSID pOwner,LPDWORD lpdwOwnerSize,PSID pPrimaryGroup,LPDWORD lpdwPrimaryGroupSize);
-  WINADVAPI WINBOOL WINAPI MakeAbsoluteSD2(PSECURITY_DESCRIPTOR pSelfRelativeSecurityDescriptor,LPDWORD lpdwBufferSize);
-  WINADVAPI WINBOOL WINAPI SetFileSecurityA(LPCSTR lpFileName,SECURITY_INFORMATION SecurityInformation,PSECURITY_DESCRIPTOR pSecurityDescriptor);
-  WINADVAPI WINBOOL WINAPI SetFileSecurityW(LPCWSTR lpFileName,SECURITY_INFORMATION SecurityInformation,PSECURITY_DESCRIPTOR pSecurityDescriptor);
-  WINADVAPI WINBOOL WINAPI GetFileSecurityA(LPCSTR lpFileName,SECURITY_INFORMATION RequestedInformation,PSECURITY_DESCRIPTOR pSecurityDescriptor,DWORD nLength,LPDWORD lpnLengthNeeded);
-  WINADVAPI WINBOOL WINAPI GetFileSecurityW(LPCWSTR lpFileName,SECURITY_INFORMATION RequestedInformation,PSECURITY_DESCRIPTOR pSecurityDescriptor,DWORD nLength,LPDWORD lpnLengthNeeded);
-  WINADVAPI WINBOOL WINAPI SetKernelObjectSecurity(HANDLE Handle,SECURITY_INFORMATION SecurityInformation,PSECURITY_DESCRIPTOR SecurityDescriptor);
-  WINBASEAPI HANDLE WINAPI FindFirstChangeNotificationA(LPCSTR lpPathName,WINBOOL bWatchSubtree,DWORD dwNotifyFilter);
-  WINBASEAPI HANDLE WINAPI FindFirstChangeNotificationW(LPCWSTR lpPathName,WINBOOL bWatchSubtree,DWORD dwNotifyFilter);
-  WINBASEAPI WINBOOL WINAPI FindNextChangeNotification(HANDLE hChangeHandle);
-  WINBASEAPI WINBOOL WINAPI FindCloseChangeNotification(HANDLE hChangeHandle);
-  WINBASEAPI WINBOOL WINAPI ReadDirectoryChangesW(HANDLE hDirectory,LPVOID lpBuffer,DWORD nBufferLength,WINBOOL bWatchSubtree,DWORD dwNotifyFilter,LPDWORD lpBytesReturned,LPOVERLAPPED lpOverlapped,LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
-  WINBASEAPI WINBOOL WINAPI VirtualLock(LPVOID lpAddress,SIZE_T dwSize);
-  WINBASEAPI WINBOOL WINAPI VirtualUnlock(LPVOID lpAddress,SIZE_T dwSize);
-  WINBASEAPI LPVOID WINAPI MapViewOfFileEx(HANDLE hFileMappingObject,DWORD dwDesiredAccess,DWORD dwFileOffsetHigh,DWORD dwFileOffsetLow,SIZE_T dwNumberOfBytesToMap,LPVOID lpBaseAddress);
-  WINBASEAPI WINBOOL WINAPI SetPriorityClass(HANDLE hProcess,DWORD dwPriorityClass);
-  WINBASEAPI DWORD WINAPI GetPriorityClass(HANDLE hProcess);
-  WINBASEAPI WINBOOL WINAPI IsBadReadPtr(CONST VOID *lp,UINT_PTR ucb);
-  WINBASEAPI WINBOOL WINAPI IsBadWritePtr(LPVOID lp,UINT_PTR ucb);
-  WINBASEAPI WINBOOL WINAPI IsBadHugeReadPtr(CONST VOID *lp,UINT_PTR ucb);
-  WINBASEAPI WINBOOL WINAPI IsBadHugeWritePtr(LPVOID lp,UINT_PTR ucb);
-  WINBASEAPI WINBOOL WINAPI IsBadCodePtr(FARPROC lpfn);
-  WINBASEAPI WINBOOL WINAPI IsBadStringPtrA(LPCSTR lpsz,UINT_PTR ucchMax);
-  WINBASEAPI WINBOOL WINAPI IsBadStringPtrW(LPCWSTR lpsz,UINT_PTR ucchMax);
-  WINADVAPI WINBOOL WINAPI LookupAccountSidA(LPCSTR lpSystemName,PSID Sid,LPSTR Name,LPDWORD cchName,LPSTR ReferencedDomainName,LPDWORD cchReferencedDomainName,PSID_NAME_USE peUse);
-  WINADVAPI WINBOOL WINAPI LookupAccountSidW(LPCWSTR lpSystemName,PSID Sid,LPWSTR Name,LPDWORD cchName,LPWSTR ReferencedDomainName,LPDWORD cchReferencedDomainName,PSID_NAME_USE peUse);
-  WINADVAPI WINBOOL WINAPI LookupAccountNameA(LPCSTR lpSystemName,LPCSTR lpAccountName,PSID Sid,LPDWORD cbSid,LPSTR ReferencedDomainName,LPDWORD cchReferencedDomainName,PSID_NAME_USE peUse);
-  WINADVAPI WINBOOL WINAPI LookupAccountNameW(LPCWSTR lpSystemName,LPCWSTR lpAccountName,PSID Sid,LPDWORD cbSid,LPWSTR ReferencedDomainName,LPDWORD cchReferencedDomainName,PSID_NAME_USE peUse);
-  WINADVAPI WINBOOL WINAPI LookupPrivilegeValueA(LPCSTR lpSystemName,LPCSTR lpName,PLUID lpLuid);
-  WINADVAPI WINBOOL WINAPI LookupPrivilegeValueW(LPCWSTR lpSystemName,LPCWSTR lpName,PLUID lpLuid);
-  WINADVAPI WINBOOL WINAPI LookupPrivilegeNameA(LPCSTR lpSystemName,PLUID lpLuid,LPSTR lpName,LPDWORD cchName);
-  WINADVAPI WINBOOL WINAPI LookupPrivilegeNameW(LPCWSTR lpSystemName,PLUID lpLuid,LPWSTR lpName,LPDWORD cchName);
-  WINADVAPI WINBOOL WINAPI LookupPrivilegeDisplayNameA(LPCSTR lpSystemName,LPCSTR lpName,LPSTR lpDisplayName,LPDWORD cchDisplayName,LPDWORD lpLanguageId);
-  WINADVAPI WINBOOL WINAPI LookupPrivilegeDisplayNameW(LPCWSTR lpSystemName,LPCWSTR lpName,LPWSTR lpDisplayName,LPDWORD cchDisplayName,LPDWORD lpLanguageId);
-  WINADVAPI WINBOOL WINAPI AllocateLocallyUniqueId(PLUID Luid);
-  WINBASEAPI WINBOOL WINAPI BuildCommDCBA(LPCSTR lpDef,LPDCB lpDCB);
-  WINBASEAPI WINBOOL WINAPI BuildCommDCBW(LPCWSTR lpDef,LPDCB lpDCB);
-  WINBASEAPI WINBOOL WINAPI BuildCommDCBAndTimeoutsA(LPCSTR lpDef,LPDCB lpDCB,LPCOMMTIMEOUTS lpCommTimeouts);
-  WINBASEAPI WINBOOL WINAPI BuildCommDCBAndTimeoutsW(LPCWSTR lpDef,LPDCB lpDCB,LPCOMMTIMEOUTS lpCommTimeouts);
-  WINBASEAPI WINBOOL WINAPI CommConfigDialogA(LPCSTR lpszName,HWND hWnd,LPCOMMCONFIG lpCC);
-  WINBASEAPI WINBOOL WINAPI CommConfigDialogW(LPCWSTR lpszName,HWND hWnd,LPCOMMCONFIG lpCC);
-  WINBASEAPI WINBOOL WINAPI GetDefaultCommConfigA(LPCSTR lpszName,LPCOMMCONFIG lpCC,LPDWORD lpdwSize);
-  WINBASEAPI WINBOOL WINAPI GetDefaultCommConfigW(LPCWSTR lpszName,LPCOMMCONFIG lpCC,LPDWORD lpdwSize);
-  WINBASEAPI WINBOOL WINAPI SetDefaultCommConfigA(LPCSTR lpszName,LPCOMMCONFIG lpCC,DWORD dwSize);
-  WINBASEAPI WINBOOL WINAPI SetDefaultCommConfigW(LPCWSTR lpszName,LPCOMMCONFIG lpCC,DWORD dwSize);
-
-#define MAX_COMPUTERNAME_LENGTH 15
-
-  WINBASEAPI WINBOOL WINAPI GetComputerNameA(LPSTR lpBuffer,LPDWORD nSize);
-  WINBASEAPI WINBOOL WINAPI GetComputerNameW(LPWSTR lpBuffer,LPDWORD nSize);
-  WINBASEAPI WINBOOL WINAPI SetComputerNameA(LPCSTR lpComputerName);
-  WINBASEAPI WINBOOL WINAPI SetComputerNameW(LPCWSTR lpComputerName);
-
-  typedef enum _COMPUTER_NAME_FORMAT {
-    ComputerNameNetBIOS,ComputerNameDnsHostname,ComputerNameDnsDomain,ComputerNameDnsFullyQualified,ComputerNamePhysicalNetBIOS,ComputerNamePhysicalDnsHostname,ComputerNamePhysicalDnsDomain,ComputerNamePhysicalDnsFullyQualified,ComputerNameMax
-  } COMPUTER_NAME_FORMAT;
-
-  WINBASEAPI WINBOOL WINAPI GetComputerNameExA(COMPUTER_NAME_FORMAT NameType,LPSTR lpBuffer,LPDWORD nSize);
-  WINBASEAPI WINBOOL WINAPI GetComputerNameExW(COMPUTER_NAME_FORMAT NameType,LPWSTR lpBuffer,LPDWORD nSize);
-  WINBASEAPI WINBOOL WINAPI SetComputerNameExA(COMPUTER_NAME_FORMAT NameType,LPCSTR lpBuffer);
-  WINBASEAPI WINBOOL WINAPI SetComputerNameExW(COMPUTER_NAME_FORMAT NameType,LPCWSTR lpBuffer);
-  WINBASEAPI WINBOOL WINAPI DnsHostnameToComputerNameA(LPCSTR Hostname,LPSTR ComputerName,LPDWORD nSize);
-  WINBASEAPI WINBOOL WINAPI DnsHostnameToComputerNameW(LPCWSTR Hostname,LPWSTR ComputerName,LPDWORD nSize);
-  WINADVAPI WINBOOL WINAPI GetUserNameA(LPSTR lpBuffer,LPDWORD pcbBuffer);
-  WINADVAPI WINBOOL WINAPI GetUserNameW(LPWSTR lpBuffer,LPDWORD pcbBuffer);
-
-#define LOGON32_LOGON_INTERACTIVE 2
-#define LOGON32_LOGON_NETWORK 3
-#define LOGON32_LOGON_BATCH 4
-#define LOGON32_LOGON_SERVICE 5
-#define LOGON32_LOGON_UNLOCK 7
-#define LOGON32_LOGON_NETWORK_CLEARTEXT 8
-#define LOGON32_LOGON_NEW_CREDENTIALS 9
-
-#define LOGON32_PROVIDER_DEFAULT 0
-#define LOGON32_PROVIDER_WINNT35 1
-#define LOGON32_PROVIDER_WINNT40 2
-#define LOGON32_PROVIDER_WINNT50 3
-
-#ifdef UNICODE
-#define LogonUser LogonUserW
-#define LogonUserEx LogonUserExW
-#define CreateProcessAsUser CreateProcessAsUserW
-#else
-#define LogonUser LogonUserA
-#define LogonUserEx LogonUserExA
-#define CreateProcessAsUser CreateProcessAsUserA
-#endif
-
-  WINADVAPI WINBOOL WINAPI LogonUserA(LPCSTR lpszUsername,LPCSTR lpszDomain,LPCSTR lpszPassword,DWORD dwLogonType,DWORD dwLogonProvider,PHANDLE phToken);
-  WINADVAPI WINBOOL WINAPI LogonUserW(LPCWSTR lpszUsername,LPCWSTR lpszDomain,LPCWSTR lpszPassword,DWORD dwLogonType,DWORD dwLogonProvider,PHANDLE phToken);
-  WINADVAPI WINBOOL WINAPI LogonUserExA(LPCSTR lpszUsername,LPCSTR lpszDomain,LPCSTR lpszPassword,DWORD dwLogonType,DWORD dwLogonProvider,PHANDLE phToken,PSID *ppLogonSid,PVOID *ppProfileBuffer,LPDWORD pdwProfileLength,PQUOTA_LIMITS pQuotaLimits);
-  WINADVAPI WINBOOL WINAPI LogonUserExW(LPCWSTR lpszUsername,LPCWSTR lpszDomain,LPCWSTR lpszPassword,DWORD dwLogonType,DWORD dwLogonProvider,PHANDLE phToken,PSID *ppLogonSid,PVOID *ppProfileBuffer,LPDWORD pdwProfileLength,PQUOTA_LIMITS pQuotaLimits);
-  WINADVAPI WINBOOL WINAPI ImpersonateLoggedOnUser(HANDLE hToken);
-  WINADVAPI WINBOOL WINAPI CreateProcessAsUserA(HANDLE hToken,LPCSTR lpApplicationName,LPSTR lpCommandLine,LPSECURITY_ATTRIBUTES lpProcessAttributes,LPSECURITY_ATTRIBUTES lpThreadAttributes,WINBOOL bInheritHandles,DWORD dwCreationFlags,LPVOID lpEnvironment,LPCSTR lpCurrentDirectory,LPSTARTUPINFOA lpStartupInfo,LPPROCESS_INFORMATION lpProcessInformation);
-  WINADVAPI WINBOOL WINAPI CreateProcessAsUserW(HANDLE hToken,LPCWSTR lpApplicationName,LPWSTR lpCommandLine,LPSECURITY_ATTRIBUTES lpProcessAttributes,LPSECURITY_ATTRIBUTES lpThreadAttributes,WINBOOL bInheritHandles,DWORD dwCreationFlags,LPVOID lpEnvironment,LPCWSTR lpCurrentDirectory,LPSTARTUPINFOW lpStartupInfo,LPPROCESS_INFORMATION lpProcessInformation);
-
-#define LOGON_WITH_PROFILE 0x1
-#define LOGON_NETCREDENTIALS_ONLY 0x2
-#define LOGON_ZERO_PASSWORD_BUFFER 0x80000000
-
-  WINADVAPI WINBOOL WINAPI CreateProcessWithLogonW(LPCWSTR lpUsername,LPCWSTR lpDomain,LPCWSTR lpPassword,DWORD dwLogonFlags,LPCWSTR lpApplicationName,LPWSTR lpCommandLine,DWORD dwCreationFlags,LPVOID lpEnvironment,LPCWSTR lpCurrentDirectory,LPSTARTUPINFOW lpStartupInfo,LPPROCESS_INFORMATION lpProcessInformation);
-  WINADVAPI WINBOOL WINAPI CreateProcessWithTokenW(HANDLE hToken,DWORD dwLogonFlags,LPCWSTR lpApplicationName,LPWSTR lpCommandLine,DWORD dwCreationFlags,LPVOID lpEnvironment,LPCWSTR lpCurrentDirectory,LPSTARTUPINFOW lpStartupInfo,LPPROCESS_INFORMATION lpProcessInformation);
-  WINADVAPI WINBOOL WINAPI ImpersonateAnonymousToken(HANDLE ThreadHandle);
-  WINADVAPI WINBOOL WINAPI DuplicateTokenEx(HANDLE hExistingToken,DWORD dwDesiredAccess,LPSECURITY_ATTRIBUTES lpTokenAttributes,SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,TOKEN_TYPE TokenType,PHANDLE phNewToken);
-  WINADVAPI WINBOOL WINAPI CreateRestrictedToken(HANDLE ExistingTokenHandle,DWORD Flags,DWORD DisableSidCount,PSID_AND_ATTRIBUTES SidsToDisable,DWORD DeletePrivilegeCount,PLUID_AND_ATTRIBUTES PrivilegesToDelete,DWORD RestrictedSidCount,PSID_AND_ATTRIBUTES SidsToRestrict,PHANDLE NewTokenHandle);
-  WINADVAPI WINBOOL WINAPI IsTokenRestricted(HANDLE TokenHandle);
-  WINADVAPI WINBOOL WINAPI IsTokenUntrusted(HANDLE TokenHandle);
-  WINADVAPI WINBOOL WINAPI CheckTokenMembership(HANDLE TokenHandle,PSID SidToCheck,PBOOL IsMember);
-
-  typedef WAITORTIMERCALLBACKFUNC WAITORTIMERCALLBACK;
-
-  WINBASEAPI WINBOOL WINAPI RegisterWaitForSingleObject(PHANDLE phNewWaitObject,HANDLE hObject,WAITORTIMERCALLBACK Callback,PVOID Context,ULONG dwMilliseconds,ULONG dwFlags);
-  WINBASEAPI HANDLE WINAPI RegisterWaitForSingleObjectEx(HANDLE hObject,WAITORTIMERCALLBACK Callback,PVOID Context,ULONG dwMilliseconds,ULONG dwFlags);
-  WINBASEAPI WINBOOL WINAPI UnregisterWait(HANDLE WaitHandle);
-  WINBASEAPI WINBOOL WINAPI UnregisterWaitEx(HANDLE WaitHandle,HANDLE CompletionEvent);
-  WINBASEAPI WINBOOL WINAPI QueueUserWorkItem(LPTHREAD_START_ROUTINE Function,PVOID Context,ULONG Flags);
-  WINBASEAPI WINBOOL WINAPI BindIoCompletionCallback(HANDLE FileHandle,LPOVERLAPPED_COMPLETION_ROUTINE Function,ULONG Flags);
-  WINBASEAPI HANDLE WINAPI CreateTimerQueue(VOID);
-  WINBASEAPI WINBOOL WINAPI CreateTimerQueueTimer(PHANDLE phNewTimer,HANDLE TimerQueue,WAITORTIMERCALLBACK Callback,PVOID Parameter,DWORD DueTime,DWORD Period,ULONG Flags);
-  WINBASEAPI WINBOOL WINAPI ChangeTimerQueueTimer(HANDLE TimerQueue,HANDLE Timer,ULONG DueTime,ULONG Period);
-  WINBASEAPI WINBOOL WINAPI DeleteTimerQueueTimer(HANDLE TimerQueue,HANDLE Timer,HANDLE CompletionEvent);
-  WINBASEAPI WINBOOL WINAPI DeleteTimerQueueEx(HANDLE TimerQueue,HANDLE CompletionEvent);
-  WINBASEAPI HANDLE WINAPI SetTimerQueueTimer(HANDLE TimerQueue,WAITORTIMERCALLBACK Callback,PVOID Parameter,DWORD DueTime,DWORD Period,WINBOOL PreferIo);
-  WINBASEAPI WINBOOL WINAPI CancelTimerQueueTimer(HANDLE TimerQueue,HANDLE Timer);
-  WINBASEAPI WINBOOL WINAPI DeleteTimerQueue(HANDLE TimerQueue);
-
-#define HW_PROFILE_GUIDLEN 39
-#define MAX_PROFILE_LEN 80
-
-#define DOCKINFO_UNDOCKED (0x1)
-#define DOCKINFO_DOCKED (0x2)
-#define DOCKINFO_USER_SUPPLIED (0x4)
-#define DOCKINFO_USER_UNDOCKED (DOCKINFO_USER_SUPPLIED | DOCKINFO_UNDOCKED)
-#define DOCKINFO_USER_DOCKED (DOCKINFO_USER_SUPPLIED | DOCKINFO_DOCKED)
-
-  typedef struct tagHW_PROFILE_INFOA {
-    DWORD dwDockInfo;
-    CHAR szHwProfileGuid[HW_PROFILE_GUIDLEN];
-    CHAR szHwProfileName[MAX_PROFILE_LEN];
-  } HW_PROFILE_INFOA,*LPHW_PROFILE_INFOA;
-
-  typedef struct tagHW_PROFILE_INFOW {
-    DWORD dwDockInfo;
-    WCHAR szHwProfileGuid[HW_PROFILE_GUIDLEN];
-    WCHAR szHwProfileName[MAX_PROFILE_LEN];
-  } HW_PROFILE_INFOW,*LPHW_PROFILE_INFOW;
-
-#ifdef UNICODE
-  typedef HW_PROFILE_INFOW HW_PROFILE_INFO;
-  typedef LPHW_PROFILE_INFOW LPHW_PROFILE_INFO;
-#else
-  typedef HW_PROFILE_INFOA HW_PROFILE_INFO;
-  typedef LPHW_PROFILE_INFOA LPHW_PROFILE_INFO;
-#endif
-
-#ifdef UNICODE
-#define GetCurrentHwProfile GetCurrentHwProfileW
-#define GetVersionEx GetVersionExW
-#define VerifyVersionInfo VerifyVersionInfoW
-#else
-#define GetCurrentHwProfile GetCurrentHwProfileA
-#define GetVersionEx GetVersionExA
-#define VerifyVersionInfo VerifyVersionInfoA
-#endif
-
-  WINADVAPI WINBOOL WINAPI GetCurrentHwProfileA (LPHW_PROFILE_INFOA lpHwProfileInfo);
-  WINADVAPI WINBOOL WINAPI GetCurrentHwProfileW (LPHW_PROFILE_INFOW lpHwProfileInfo);
-  WINBASEAPI WINBOOL WINAPI QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount);
-  WINBASEAPI WINBOOL WINAPI QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
-  WINBASEAPI WINBOOL WINAPI GetVersionExA(LPOSVERSIONINFOA lpVersionInformation);
-  WINBASEAPI WINBOOL WINAPI GetVersionExW(LPOSVERSIONINFOW lpVersionInformation);
-  WINBASEAPI WINBOOL WINAPI VerifyVersionInfoA(LPOSVERSIONINFOEXA lpVersionInformation,DWORD dwTypeMask,DWORDLONG dwlConditionMask);
-  WINBASEAPI WINBOOL WINAPI VerifyVersionInfoW(LPOSVERSIONINFOEXW lpVersionInformation,DWORD dwTypeMask,DWORDLONG dwlConditionMask);
-
-#include <winerror.h>
-
-#define TC_NORMAL 0
-#define TC_HARDERR 1
-#define TC_GP_TRAP 2
-#define TC_SIGNAL 3
-
-#define AC_LINE_OFFLINE 0x0
-#define AC_LINE_ONLINE 0x1
-#define AC_LINE_BACKUP_POWER 0x2
-#define AC_LINE_UNKNOWN 0xff
-
-#define BATTERY_FLAG_HIGH 0x1
-#define BATTERY_FLAG_LOW 0x2
-#define BATTERY_FLAG_CRITICAL 0x4
-#define BATTERY_FLAG_CHARGING 0x8
-#define BATTERY_FLAG_NO_BATTERY 0x80
-#define BATTERY_FLAG_UNKNOWN 0xff
-
-#define BATTERY_PERCENTAGE_UNKNOWN 0xff
-
-#define BATTERY_LIFE_UNKNOWN 0xffffffff
-
-  typedef struct _SYSTEM_POWER_STATUS {
-    BYTE ACLineStatus;
-    BYTE BatteryFlag;
-    BYTE BatteryLifePercent;
-    BYTE Reserved1;
-    DWORD BatteryLifeTime;
-    DWORD BatteryFullLifeTime;
-  } SYSTEM_POWER_STATUS,*LPSYSTEM_POWER_STATUS;
-
-#ifdef UNICODE
-#define CreateJobObject CreateJobObjectW
-#define OpenJobObject OpenJobObjectW
-#define FindFirstVolume FindFirstVolumeW
-#define FindNextVolume FindNextVolumeW
-#define FindFirstVolumeMountPoint FindFirstVolumeMountPointW
-#define FindNextVolumeMountPoint FindNextVolumeMountPointW
-#define SetVolumeMountPoint SetVolumeMountPointW
-#define DeleteVolumeMountPoint DeleteVolumeMountPointW
-#define GetVolumeNameForVolumeMountPoint GetVolumeNameForVolumeMountPointW
-#define GetVolumePathName GetVolumePathNameW
-#define GetVolumePathNamesForVolumeName GetVolumePathNamesForVolumeNameW
-#else
-#define CreateJobObject CreateJobObjectA
-#define OpenJobObject OpenJobObjectA
-#define FindFirstVolume FindFirstVolumeA
-#define FindNextVolume FindNextVolumeA
-#define FindFirstVolumeMountPoint FindFirstVolumeMountPointA
-#define FindNextVolumeMountPoint FindNextVolumeMountPointA
-#define SetVolumeMountPoint SetVolumeMountPointA
-#define DeleteVolumeMountPoint DeleteVolumeMountPointA
-#define GetVolumeNameForVolumeMountPoint GetVolumeNameForVolumeMountPointA
-#define GetVolumePathName GetVolumePathNameA
-#define GetVolumePathNamesForVolumeName GetVolumePathNamesForVolumeNameA
-#endif
-
-  WINBOOL WINAPI GetSystemPowerStatus(LPSYSTEM_POWER_STATUS lpSystemPowerStatus);
-  WINBOOL WINAPI SetSystemPowerState(WINBOOL fSuspend,WINBOOL fForce);
-  WINBASEAPI WINBOOL WINAPI AllocateUserPhysicalPages(HANDLE hProcess,PULONG_PTR NumberOfPages,PULONG_PTR PageArray);
-  WINBASEAPI WINBOOL WINAPI FreeUserPhysicalPages(HANDLE hProcess,PULONG_PTR NumberOfPages,PULONG_PTR PageArray);
-  WINBASEAPI WINBOOL WINAPI MapUserPhysicalPages(PVOID VirtualAddress,ULONG_PTR NumberOfPages,PULONG_PTR PageArray);
-  WINBASEAPI WINBOOL WINAPI MapUserPhysicalPagesScatter(PVOID *VirtualAddresses,ULONG_PTR NumberOfPages,PULONG_PTR PageArray);
-  WINBASEAPI HANDLE WINAPI CreateJobObjectA(LPSECURITY_ATTRIBUTES lpJobAttributes,LPCSTR lpName);
-  WINBASEAPI HANDLE WINAPI CreateJobObjectW(LPSECURITY_ATTRIBUTES lpJobAttributes,LPCWSTR lpName);
-  WINBASEAPI HANDLE WINAPI OpenJobObjectA(DWORD dwDesiredAccess,WINBOOL bInheritHandle,LPCSTR lpName);
-  WINBASEAPI HANDLE WINAPI OpenJobObjectW(DWORD dwDesiredAccess,WINBOOL bInheritHandle,LPCWSTR lpName);
-  WINBASEAPI WINBOOL WINAPI AssignProcessToJobObject(HANDLE hJob,HANDLE hProcess);
-  WINBASEAPI WINBOOL WINAPI TerminateJobObject(HANDLE hJob,UINT uExitCode);
-  WINBASEAPI WINBOOL WINAPI QueryInformationJobObject(HANDLE hJob,JOBOBJECTINFOCLASS JobObjectInformationClass,LPVOID lpJobObjectInformation,DWORD cbJobObjectInformationLength,LPDWORD lpReturnLength);
-  WINBASEAPI WINBOOL WINAPI SetInformationJobObject(HANDLE hJob,JOBOBJECTINFOCLASS JobObjectInformationClass,LPVOID lpJobObjectInformation,DWORD cbJobObjectInformationLength);
-  WINBASEAPI WINBOOL WINAPI IsProcessInJob(HANDLE ProcessHandle,HANDLE JobHandle,PBOOL Result);
-  WINBASEAPI WINBOOL WINAPI CreateJobSet(ULONG NumJob,PJOB_SET_ARRAY UserJobSet,ULONG Flags);
-  WINBASEAPI PVOID WINAPI AddVectoredExceptionHandler (ULONG First,PVECTORED_EXCEPTION_HANDLER Handler);
-  WINBASEAPI ULONG WINAPI RemoveVectoredExceptionHandler(PVOID Handle);
-  WINBASEAPI PVOID WINAPI AddVectoredContinueHandler (ULONG First,PVECTORED_EXCEPTION_HANDLER Handler);
-  WINBASEAPI ULONG WINAPI RemoveVectoredContinueHandler(PVOID Handle);
-  WINBASEAPI HANDLE WINAPI FindFirstVolumeA(LPSTR lpszVolumeName,DWORD cchBufferLength);
-  WINBASEAPI HANDLE WINAPI FindFirstVolumeW(LPWSTR lpszVolumeName,DWORD cchBufferLength);
-  WINBASEAPI WINBOOL WINAPI FindNextVolumeA(HANDLE hFindVolume,LPSTR lpszVolumeName,DWORD cchBufferLength);
-  WINBASEAPI WINBOOL WINAPI FindNextVolumeW(HANDLE hFindVolume,LPWSTR lpszVolumeName,DWORD cchBufferLength);
-  WINBASEAPI WINBOOL WINAPI FindVolumeClose(HANDLE hFindVolume);
-  WINBASEAPI HANDLE WINAPI FindFirstVolumeMountPointA(LPCSTR lpszRootPathName,LPSTR lpszVolumeMountPoint,DWORD cchBufferLength);
-  WINBASEAPI HANDLE WINAPI FindFirstVolumeMountPointW(LPCWSTR lpszRootPathName,LPWSTR lpszVolumeMountPoint,DWORD cchBufferLength);
-  WINBASEAPI WINBOOL WINAPI FindNextVolumeMountPointA(HANDLE hFindVolumeMountPoint,LPSTR lpszVolumeMountPoint,DWORD cchBufferLength);
-  WINBASEAPI WINBOOL WINAPI FindNextVolumeMountPointW(HANDLE hFindVolumeMountPoint,LPWSTR lpszVolumeMountPoint,DWORD cchBufferLength);
-  WINBASEAPI WINBOOL WINAPI FindVolumeMountPointClose(HANDLE hFindVolumeMountPoint);
-  WINBASEAPI WINBOOL WINAPI SetVolumeMountPointA(LPCSTR lpszVolumeMountPoint,LPCSTR lpszVolumeName);
-  WINBASEAPI WINBOOL WINAPI SetVolumeMountPointW(LPCWSTR lpszVolumeMountPoint,LPCWSTR lpszVolumeName);
-  WINBASEAPI WINBOOL WINAPI DeleteVolumeMountPointA(LPCSTR lpszVolumeMountPoint);
-  WINBASEAPI WINBOOL WINAPI DeleteVolumeMountPointW(LPCWSTR lpszVolumeMountPoint);
-  WINBASEAPI WINBOOL WINAPI GetVolumeNameForVolumeMountPointA(LPCSTR lpszVolumeMountPoint,LPSTR lpszVolumeName,DWORD cchBufferLength);
-  WINBASEAPI WINBOOL WINAPI GetVolumeNameForVolumeMountPointW(LPCWSTR lpszVolumeMountPoint,LPWSTR lpszVolumeName,DWORD cchBufferLength);
-  WINBASEAPI WINBOOL WINAPI GetVolumePathNameA(LPCSTR lpszFileName,LPSTR lpszVolumePathName,DWORD cchBufferLength);
-  WINBASEAPI WINBOOL WINAPI GetVolumePathNameW(LPCWSTR lpszFileName,LPWSTR lpszVolumePathName,DWORD cchBufferLength);
-  WINBASEAPI WINBOOL WINAPI GetVolumePathNamesForVolumeNameA(LPCSTR lpszVolumeName,LPCH lpszVolumePathNames,DWORD cchBufferLength,PDWORD lpcchReturnLength);
-  WINBASEAPI WINBOOL WINAPI GetVolumePathNamesForVolumeNameW(LPCWSTR lpszVolumeName,LPWCH lpszVolumePathNames,DWORD cchBufferLength,PDWORD lpcchReturnLength);
-
-#define ACTCTX_FLAG_PROCESSOR_ARCHITECTURE_VALID 0x1
-#define ACTCTX_FLAG_LANGID_VALID 0x2
-#define ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID 0x4
-#define ACTCTX_FLAG_RESOURCE_NAME_VALID 0x8
-#define ACTCTX_FLAG_SET_PROCESS_DEFAULT 0x10
-#define ACTCTX_FLAG_APPLICATION_NAME_VALID 0x20
-#define ACTCTX_FLAG_SOURCE_IS_ASSEMBLYREF 0x40
-#define ACTCTX_FLAG_HMODULE_VALID 0x80
-
-  typedef struct tagACTCTXA {
-    ULONG cbSize;
-    DWORD dwFlags;
-    LPCSTR lpSource;
-    USHORT wProcessorArchitecture;
-    LANGID wLangId;
-    LPCSTR lpAssemblyDirectory;
-    LPCSTR lpResourceName;
-    LPCSTR lpApplicationName;
-    HMODULE hModule;
-  } ACTCTXA,*PACTCTXA;
-
-  typedef struct tagACTCTXW {
-    ULONG cbSize;
-    DWORD dwFlags;
-    LPCWSTR lpSource;
-    USHORT wProcessorArchitecture;
-    LANGID wLangId;
-    LPCWSTR lpAssemblyDirectory;
-    LPCWSTR lpResourceName;
-    LPCWSTR lpApplicationName;
-    HMODULE hModule;
-  } ACTCTXW,*PACTCTXW;
-
-  typedef const ACTCTXA *PCACTCTXA;
-  typedef const ACTCTXW *PCACTCTXW;
-
-#ifdef UNICODE
-  typedef ACTCTXW ACTCTX;
-  typedef PACTCTXW PACTCTX;
-  typedef PCACTCTXW PCACTCTX;
-#else
-  typedef ACTCTXA ACTCTX;
-  typedef PACTCTXA PACTCTX;
-  typedef PCACTCTXA PCACTCTX;
-#endif
-
-#ifdef UNICODE
-#define CreateActCtx CreateActCtxW
-#else
-#define CreateActCtx CreateActCtxA
-#endif
-
-  WINBASEAPI HANDLE WINAPI CreateActCtxA(PCACTCTXA pActCtx);
-  WINBASEAPI HANDLE WINAPI CreateActCtxW(PCACTCTXW pActCtx);
-  WINBASEAPI VOID WINAPI AddRefActCtx(HANDLE hActCtx);
-  WINBASEAPI VOID WINAPI ReleaseActCtx(HANDLE hActCtx);
-  WINBASEAPI WINBOOL WINAPI ZombifyActCtx(HANDLE hActCtx);
-  WINBASEAPI WINBOOL WINAPI ActivateActCtx(HANDLE hActCtx,ULONG_PTR *lpCookie);
-
-#define DEACTIVATE_ACTCTX_FLAG_FORCE_EARLY_DEACTIVATION (0x1)
-
-  WINBASEAPI WINBOOL WINAPI DeactivateActCtx(DWORD dwFlags,ULONG_PTR ulCookie);
-  WINBASEAPI WINBOOL WINAPI GetCurrentActCtx(HANDLE *lphActCtx);
-
-  typedef struct tagACTCTX_SECTION_KEYED_DATA_2600 {
-    ULONG cbSize;
-    ULONG ulDataFormatVersion;
-    PVOID lpData;
-    ULONG ulLength;
-    PVOID lpSectionGlobalData;
-    ULONG ulSectionGlobalDataLength;
-    PVOID lpSectionBase;
-    ULONG ulSectionTotalLength;
-    HANDLE hActCtx;
-    ULONG ulAssemblyRosterIndex;
-  } ACTCTX_SECTION_KEYED_DATA_2600,*PACTCTX_SECTION_KEYED_DATA_2600;
-
-  typedef const ACTCTX_SECTION_KEYED_DATA_2600 *PCACTCTX_SECTION_KEYED_DATA_2600;
-
-  typedef struct tagACTCTX_SECTION_KEYED_DATA_ASSEMBLY_METADATA {
-    PVOID lpInformation;
-    PVOID lpSectionBase;
-    ULONG ulSectionLength;
-    PVOID lpSectionGlobalDataBase;
-    ULONG ulSectionGlobalDataLength;
-  } ACTCTX_SECTION_KEYED_DATA_ASSEMBLY_METADATA,*PACTCTX_SECTION_KEYED_DATA_ASSEMBLY_METADATA;
-
-  typedef const ACTCTX_SECTION_KEYED_DATA_ASSEMBLY_METADATA *PCACTCTX_SECTION_KEYED_DATA_ASSEMBLY_METADATA;
-
-  typedef struct tagACTCTX_SECTION_KEYED_DATA {
-    ULONG cbSize;
-    ULONG ulDataFormatVersion;
-    PVOID lpData;
-    ULONG ulLength;
-    PVOID lpSectionGlobalData;
-    ULONG ulSectionGlobalDataLength;
-    PVOID lpSectionBase;
-    ULONG ulSectionTotalLength;
-    HANDLE hActCtx;
-    ULONG ulAssemblyRosterIndex;
-
-    ULONG ulFlags;
-    ACTCTX_SECTION_KEYED_DATA_ASSEMBLY_METADATA AssemblyMetadata;
-  } ACTCTX_SECTION_KEYED_DATA,*PACTCTX_SECTION_KEYED_DATA;
-
-  typedef const ACTCTX_SECTION_KEYED_DATA *PCACTCTX_SECTION_KEYED_DATA;
-
-#define FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX 0x1
-#define FIND_ACTCTX_SECTION_KEY_RETURN_FLAGS 0x2
-#define FIND_ACTCTX_SECTION_KEY_RETURN_ASSEMBLY_METADATA 0x4
-
-#ifdef UNICODE
-#define FindActCtxSectionString FindActCtxSectionStringW
-#else
-#define FindActCtxSectionString FindActCtxSectionStringA
-#endif
-
-  WINBASEAPI WINBOOL WINAPI FindActCtxSectionStringA(DWORD dwFlags,const GUID *lpExtensionGuid,ULONG ulSectionId,LPCSTR lpStringToFind,PACTCTX_SECTION_KEYED_DATA ReturnedData);
-  WINBASEAPI WINBOOL WINAPI FindActCtxSectionStringW(DWORD dwFlags,const GUID *lpExtensionGuid,ULONG ulSectionId,LPCWSTR lpStringToFind,PACTCTX_SECTION_KEYED_DATA ReturnedData);
-  WINBASEAPI WINBOOL WINAPI FindActCtxSectionGuid(DWORD dwFlags,const GUID *lpExtensionGuid,ULONG ulSectionId,const GUID *lpGuidToFind,PACTCTX_SECTION_KEYED_DATA ReturnedData);
-
-#ifndef RC_INVOKED
-#ifndef ACTIVATION_CONTEXT_BASIC_INFORMATION_DEFINED
-
-  typedef struct _ACTIVATION_CONTEXT_BASIC_INFORMATION {
-    HANDLE hActCtx;
-    DWORD dwFlags;
-  } ACTIVATION_CONTEXT_BASIC_INFORMATION,*PACTIVATION_CONTEXT_BASIC_INFORMATION;
-
-  typedef const struct _ACTIVATION_CONTEXT_BASIC_INFORMATION *PCACTIVATION_CONTEXT_BASIC_INFORMATION;
-
-#define ACTIVATION_CONTEXT_BASIC_INFORMATION_DEFINED 1
-#endif
-#endif
-
-#define QUERY_ACTCTX_FLAG_USE_ACTIVE_ACTCTX 0x4
-#define QUERY_ACTCTX_FLAG_ACTCTX_IS_HMODULE 0x8
-#define QUERY_ACTCTX_FLAG_ACTCTX_IS_ADDRESS 0x10
-#define QUERY_ACTCTX_FLAG_NO_ADDREF 0x80000000
-
-  WINBASEAPI WINBOOL WINAPI QueryActCtxW(DWORD dwFlags,HANDLE hActCtx,PVOID pvSubInstance,ULONG ulInfoClass,PVOID pvBuffer,SIZE_T cbBuffer,SIZE_T *pcbWrittenOrRequired);
-
-  typedef WINBOOL (WINAPI *PQUERYACTCTXW_FUNC)(DWORD dwFlags,HANDLE hActCtx,PVOID pvSubInstance,ULONG ulInfoClass,PVOID pvBuffer,SIZE_T cbBuffer,SIZE_T *pcbWrittenOrRequired);
-
-  WINBASEAPI WINBOOL WINAPI ProcessIdToSessionId(DWORD dwProcessId,DWORD *pSessionId);
-  WINBASEAPI DWORD WINAPI WTSGetActiveConsoleSessionId();
-  WINBASEAPI WINBOOL WINAPI IsWow64Process(HANDLE hProcess,PBOOL Wow64Process);
-  WINBASEAPI WINBOOL WINAPI GetLogicalProcessorInformation(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION Buffer,PDWORD ReturnedLength);
-  WINBASEAPI WINBOOL WINAPI GetNumaHighestNodeNumber(PULONG HighestNodeNumber);
-  WINBASEAPI WINBOOL WINAPI GetNumaProcessorNode(UCHAR Processor,PUCHAR NodeNumber);
-  WINBASEAPI WINBOOL WINAPI GetNumaNodeProcessorMask(UCHAR Node,PULONGLONG ProcessorMask);
-  WINBASEAPI WINBOOL WINAPI GetNumaAvailableMemoryNode(UCHAR Node,PULONGLONG AvailableBytes);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/tinyc/win32/include/winapi/wincon.h b/tinyc/win32/include/winapi/wincon.h
deleted file mode 100644
index a3501ee70..000000000
--- a/tinyc/win32/include/winapi/wincon.h
+++ /dev/null
@@ -1,301 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _WINCON_
-#define _WINCON_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  typedef struct _COORD {
-    SHORT X;
-    SHORT Y;
-  } COORD,*PCOORD;
-
-  typedef struct _SMALL_RECT {
-    SHORT Left;
-    SHORT Top;
-    SHORT Right;
-    SHORT Bottom;
-  } SMALL_RECT,*PSMALL_RECT;
-
-  typedef struct _KEY_EVENT_RECORD {
-    WINBOOL bKeyDown;
-    WORD wRepeatCount;
-    WORD wVirtualKeyCode;
-    WORD wVirtualScanCode;
-    union {
-      WCHAR UnicodeChar;
-      CHAR AsciiChar;
-    } uChar;
-    DWORD dwControlKeyState;
-  } KEY_EVENT_RECORD,*PKEY_EVENT_RECORD;
-
-#define RIGHT_ALT_PRESSED 0x1
-#define LEFT_ALT_PRESSED 0x2
-#define RIGHT_CTRL_PRESSED 0x4
-#define LEFT_CTRL_PRESSED 0x8
-#define SHIFT_PRESSED 0x10
-#define NUMLOCK_ON 0x20
-#define SCROLLLOCK_ON 0x40
-#define CAPSLOCK_ON 0x80
-#define ENHANCED_KEY 0x100
-#define NLS_DBCSCHAR 0x10000
-#define NLS_ALPHANUMERIC 0x0
-#define NLS_KATAKANA 0x20000
-#define NLS_HIRAGANA 0x40000
-#define NLS_ROMAN 0x400000
-#define NLS_IME_CONVERSION 0x800000
-#define NLS_IME_DISABLE 0x20000000
-
-  typedef struct _MOUSE_EVENT_RECORD {
-    COORD dwMousePosition;
-    DWORD dwButtonState;
-    DWORD dwControlKeyState;
-    DWORD dwEventFlags;
-  } MOUSE_EVENT_RECORD,*PMOUSE_EVENT_RECORD;
-
-#define FROM_LEFT_1ST_BUTTON_PRESSED 0x1
-#define RIGHTMOST_BUTTON_PRESSED 0x2
-#define FROM_LEFT_2ND_BUTTON_PRESSED 0x4
-#define FROM_LEFT_3RD_BUTTON_PRESSED 0x8
-#define FROM_LEFT_4TH_BUTTON_PRESSED 0x10
-
-#define MOUSE_MOVED 0x1
-#define DOUBLE_CLICK 0x2
-#define MOUSE_WHEELED 0x4
-
-  typedef struct _WINDOW_BUFFER_SIZE_RECORD {
-    COORD dwSize;
-  } WINDOW_BUFFER_SIZE_RECORD,*PWINDOW_BUFFER_SIZE_RECORD;
-
-  typedef struct _MENU_EVENT_RECORD {
-    UINT dwCommandId;
-  } MENU_EVENT_RECORD,*PMENU_EVENT_RECORD;
-
-  typedef struct _FOCUS_EVENT_RECORD {
-    WINBOOL bSetFocus;
-  } FOCUS_EVENT_RECORD,*PFOCUS_EVENT_RECORD;
-
-  typedef struct _INPUT_RECORD {
-    WORD EventType;
-    union {
-      KEY_EVENT_RECORD KeyEvent;
-      MOUSE_EVENT_RECORD MouseEvent;
-      WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
-      MENU_EVENT_RECORD MenuEvent;
-      FOCUS_EVENT_RECORD FocusEvent;
-    } Event;
-  } INPUT_RECORD,*PINPUT_RECORD;
-
-#define KEY_EVENT 0x1
-#define MOUSE_EVENT 0x2
-#define WINDOW_BUFFER_SIZE_EVENT 0x4
-#define MENU_EVENT 0x8
-#define FOCUS_EVENT 0x10
-
-  typedef struct _CHAR_INFO {
-    union {
-      WCHAR UnicodeChar;
-      CHAR AsciiChar;
-    } Char;
-    WORD Attributes;
-  } CHAR_INFO,*PCHAR_INFO;
-
-#define FOREGROUND_BLUE 0x1
-#define FOREGROUND_GREEN 0x2
-#define FOREGROUND_RED 0x4
-#define FOREGROUND_INTENSITY 0x8
-#define BACKGROUND_BLUE 0x10
-#define BACKGROUND_GREEN 0x20
-#define BACKGROUND_RED 0x40
-#define BACKGROUND_INTENSITY 0x80
-#define COMMON_LVB_LEADING_BYTE 0x100
-#define COMMON_LVB_TRAILING_BYTE 0x200
-#define COMMON_LVB_GRID_HORIZONTAL 0x400
-#define COMMON_LVB_GRID_LVERTICAL 0x800
-#define COMMON_LVB_GRID_RVERTICAL 0x1000
-#define COMMON_LVB_REVERSE_VIDEO 0x4000
-#define COMMON_LVB_UNDERSCORE 0x8000
-
-#define COMMON_LVB_SBCSDBCS 0x300
-
-  typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
-    COORD dwSize;
-    COORD dwCursorPosition;
-    WORD wAttributes;
-    SMALL_RECT srWindow;
-    COORD dwMaximumWindowSize;
-  } CONSOLE_SCREEN_BUFFER_INFO,*PCONSOLE_SCREEN_BUFFER_INFO;
-
-  typedef struct _CONSOLE_CURSOR_INFO {
-    DWORD dwSize;
-    WINBOOL bVisible;
-  } CONSOLE_CURSOR_INFO,*PCONSOLE_CURSOR_INFO;
-
-  typedef struct _CONSOLE_FONT_INFO {
-    DWORD nFont;
-    COORD dwFontSize;
-  } CONSOLE_FONT_INFO,*PCONSOLE_FONT_INFO;
-
-  typedef struct _CONSOLE_SELECTION_INFO {
-    DWORD dwFlags;
-    COORD dwSelectionAnchor;
-    SMALL_RECT srSelection;
-  } CONSOLE_SELECTION_INFO,*PCONSOLE_SELECTION_INFO;
-
-#define CONSOLE_NO_SELECTION 0x0
-#define CONSOLE_SELECTION_IN_PROGRESS 0x1
-#define CONSOLE_SELECTION_NOT_EMPTY 0x2
-#define CONSOLE_MOUSE_SELECTION 0x4
-#define CONSOLE_MOUSE_DOWN 0x8
-
-  typedef WINBOOL (WINAPI *PHANDLER_ROUTINE)(DWORD CtrlType);
-
-#define CTRL_C_EVENT 0
-#define CTRL_BREAK_EVENT 1
-#define CTRL_CLOSE_EVENT 2
-
-#define CTRL_LOGOFF_EVENT 5
-#define CTRL_SHUTDOWN_EVENT 6
-
-#define ENABLE_PROCESSED_INPUT 0x1
-#define ENABLE_LINE_INPUT 0x2
-#define ENABLE_ECHO_INPUT 0x4
-#define ENABLE_WINDOW_INPUT 0x8
-#define ENABLE_MOUSE_INPUT 0x10
-
-#define ENABLE_PROCESSED_OUTPUT 0x1
-#define ENABLE_WRAP_AT_EOL_OUTPUT 0x2
-
-#ifdef UNICODE
-#define PeekConsoleInput PeekConsoleInputW
-#define ReadConsoleInput ReadConsoleInputW
-#define WriteConsoleInput WriteConsoleInputW
-#define ReadConsoleOutput ReadConsoleOutputW
-#define WriteConsoleOutput WriteConsoleOutputW
-#define ReadConsoleOutputCharacter ReadConsoleOutputCharacterW
-#define WriteConsoleOutputCharacter WriteConsoleOutputCharacterW
-#define FillConsoleOutputCharacter FillConsoleOutputCharacterW
-#define ScrollConsoleScreenBuffer ScrollConsoleScreenBufferW
-#define GetConsoleTitle GetConsoleTitleW
-#define SetConsoleTitle SetConsoleTitleW
-#define ReadConsole ReadConsoleW
-#define WriteConsole WriteConsoleW
-#define AddConsoleAlias AddConsoleAliasW
-#define GetConsoleAlias GetConsoleAliasW
-#define GetConsoleAliasesLength GetConsoleAliasesLengthW
-#define GetConsoleAliasExesLength GetConsoleAliasExesLengthW
-#define GetConsoleAliases GetConsoleAliasesW
-#define GetConsoleAliasExes GetConsoleAliasExesW
-#else
-#define PeekConsoleInput PeekConsoleInputA
-#define ReadConsoleInput ReadConsoleInputA
-#define WriteConsoleInput WriteConsoleInputA
-#define ReadConsoleOutput ReadConsoleOutputA
-#define WriteConsoleOutput WriteConsoleOutputA
-#define ReadConsoleOutputCharacter ReadConsoleOutputCharacterA
-#define WriteConsoleOutputCharacter WriteConsoleOutputCharacterA
-#define FillConsoleOutputCharacter FillConsoleOutputCharacterA
-#define ScrollConsoleScreenBuffer ScrollConsoleScreenBufferA
-#define GetConsoleTitle GetConsoleTitleA
-#define SetConsoleTitle SetConsoleTitleA
-#define ReadConsole ReadConsoleA
-#define WriteConsole WriteConsoleA
-#define AddConsoleAlias AddConsoleAliasA
-#define GetConsoleAlias GetConsoleAliasA
-#define GetConsoleAliasesLength GetConsoleAliasesLengthA
-#define GetConsoleAliasExesLength GetConsoleAliasExesLengthA
-#define GetConsoleAliases GetConsoleAliasesA
-#define GetConsoleAliasExes GetConsoleAliasExesA
-#endif
-
-  WINBASEAPI WINBOOL WINAPI PeekConsoleInputA(HANDLE hConsoleInput,PINPUT_RECORD lpBuffer,DWORD nLength,LPDWORD lpNumberOfEventsRead);
-  WINBASEAPI WINBOOL WINAPI PeekConsoleInputW(HANDLE hConsoleInput,PINPUT_RECORD lpBuffer,DWORD nLength,LPDWORD lpNumberOfEventsRead);
-  WINBASEAPI WINBOOL WINAPI ReadConsoleInputA(HANDLE hConsoleInput,PINPUT_RECORD lpBuffer,DWORD nLength,LPDWORD lpNumberOfEventsRead);
-  WINBASEAPI WINBOOL WINAPI ReadConsoleInputW(HANDLE hConsoleInput,PINPUT_RECORD lpBuffer,DWORD nLength,LPDWORD lpNumberOfEventsRead);
-  WINBASEAPI WINBOOL WINAPI WriteConsoleInputA(HANDLE hConsoleInput,CONST INPUT_RECORD *lpBuffer,DWORD nLength,LPDWORD lpNumberOfEventsWritten);
-  WINBASEAPI WINBOOL WINAPI WriteConsoleInputW(HANDLE hConsoleInput,CONST INPUT_RECORD *lpBuffer,DWORD nLength,LPDWORD lpNumberOfEventsWritten);
-  WINBASEAPI WINBOOL WINAPI ReadConsoleOutputA(HANDLE hConsoleOutput,PCHAR_INFO lpBuffer,COORD dwBufferSize,COORD dwBufferCoord,PSMALL_RECT lpReadRegion);
-  WINBASEAPI WINBOOL WINAPI ReadConsoleOutputW(HANDLE hConsoleOutput,PCHAR_INFO lpBuffer,COORD dwBufferSize,COORD dwBufferCoord,PSMALL_RECT lpReadRegion);
-  WINBASEAPI WINBOOL WINAPI WriteConsoleOutputA(HANDLE hConsoleOutput,CONST CHAR_INFO *lpBuffer,COORD dwBufferSize,COORD dwBufferCoord,PSMALL_RECT lpWriteRegion);
-  WINBASEAPI WINBOOL WINAPI WriteConsoleOutputW(HANDLE hConsoleOutput,CONST CHAR_INFO *lpBuffer,COORD dwBufferSize,COORD dwBufferCoord,PSMALL_RECT lpWriteRegion);
-  WINBASEAPI WINBOOL WINAPI ReadConsoleOutputCharacterA(HANDLE hConsoleOutput,LPSTR lpCharacter,DWORD nLength,COORD dwReadCoord,LPDWORD lpNumberOfCharsRead);
-  WINBASEAPI WINBOOL WINAPI ReadConsoleOutputCharacterW(HANDLE hConsoleOutput,LPWSTR lpCharacter,DWORD nLength,COORD dwReadCoord,LPDWORD lpNumberOfCharsRead);
-  WINBASEAPI WINBOOL WINAPI ReadConsoleOutputAttribute(HANDLE hConsoleOutput,LPWORD lpAttribute,DWORD nLength,COORD dwReadCoord,LPDWORD lpNumberOfAttrsRead);
-  WINBASEAPI WINBOOL WINAPI WriteConsoleOutputCharacterA(HANDLE hConsoleOutput,LPCSTR lpCharacter,DWORD nLength,COORD dwWriteCoord,LPDWORD lpNumberOfCharsWritten);
-  WINBASEAPI WINBOOL WINAPI WriteConsoleOutputCharacterW(HANDLE hConsoleOutput,LPCWSTR lpCharacter,DWORD nLength,COORD dwWriteCoord,LPDWORD lpNumberOfCharsWritten);
-  WINBASEAPI WINBOOL WINAPI WriteConsoleOutputAttribute(HANDLE hConsoleOutput,CONST WORD *lpAttribute,DWORD nLength,COORD dwWriteCoord,LPDWORD lpNumberOfAttrsWritten);
-  WINBASEAPI WINBOOL WINAPI FillConsoleOutputCharacterA(HANDLE hConsoleOutput,CHAR cCharacter,DWORD nLength,COORD dwWriteCoord,LPDWORD lpNumberOfCharsWritten);
-  WINBASEAPI WINBOOL WINAPI FillConsoleOutputCharacterW(HANDLE hConsoleOutput,WCHAR cCharacter,DWORD nLength,COORD dwWriteCoord,LPDWORD lpNumberOfCharsWritten);
-  WINBASEAPI WINBOOL WINAPI FillConsoleOutputAttribute(HANDLE hConsoleOutput,WORD wAttribute,DWORD nLength,COORD dwWriteCoord,LPDWORD lpNumberOfAttrsWritten);
-  WINBASEAPI WINBOOL WINAPI GetConsoleMode(HANDLE hConsoleHandle,LPDWORD lpMode);
-  WINBASEAPI WINBOOL WINAPI GetNumberOfConsoleInputEvents(HANDLE hConsoleInput,LPDWORD lpNumberOfEvents);
-  WINBASEAPI WINBOOL WINAPI GetConsoleScreenBufferInfo(HANDLE hConsoleOutput,PCONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
-  WINBASEAPI COORD WINAPI GetLargestConsoleWindowSize(HANDLE hConsoleOutput);
-  WINBASEAPI WINBOOL WINAPI GetConsoleCursorInfo(HANDLE hConsoleOutput,PCONSOLE_CURSOR_INFO lpConsoleCursorInfo);
-  WINBASEAPI WINBOOL WINAPI GetCurrentConsoleFont(HANDLE hConsoleOutput,WINBOOL bMaximumWindow,PCONSOLE_FONT_INFO lpConsoleCurrentFont);
-  WINBASEAPI COORD WINAPI GetConsoleFontSize(HANDLE hConsoleOutput,DWORD nFont);
-  WINBASEAPI WINBOOL WINAPI GetConsoleSelectionInfo(PCONSOLE_SELECTION_INFO lpConsoleSelectionInfo);
-  WINBASEAPI WINBOOL WINAPI GetNumberOfConsoleMouseButtons(LPDWORD lpNumberOfMouseButtons);
-  WINBASEAPI WINBOOL WINAPI SetConsoleMode(HANDLE hConsoleHandle,DWORD dwMode);
-  WINBASEAPI WINBOOL WINAPI SetConsoleActiveScreenBuffer(HANDLE hConsoleOutput);
-  WINBASEAPI WINBOOL WINAPI FlushConsoleInputBuffer(HANDLE hConsoleInput);
-  WINBASEAPI WINBOOL WINAPI SetConsoleScreenBufferSize(HANDLE hConsoleOutput,COORD dwSize);
-  WINBASEAPI WINBOOL WINAPI SetConsoleCursorPosition(HANDLE hConsoleOutput,COORD dwCursorPosition);
-  WINBASEAPI WINBOOL WINAPI SetConsoleCursorInfo(HANDLE hConsoleOutput,CONST CONSOLE_CURSOR_INFO *lpConsoleCursorInfo);
-  WINBASEAPI WINBOOL WINAPI ScrollConsoleScreenBufferA(HANDLE hConsoleOutput,CONST SMALL_RECT *lpScrollRectangle,CONST SMALL_RECT *lpClipRectangle,COORD dwDestinationOrigin,CONST CHAR_INFO *lpFill);
-  WINBASEAPI WINBOOL WINAPI ScrollConsoleScreenBufferW(HANDLE hConsoleOutput,CONST SMALL_RECT *lpScrollRectangle,CONST SMALL_RECT *lpClipRectangle,COORD dwDestinationOrigin,CONST CHAR_INFO *lpFill);
-  WINBASEAPI WINBOOL WINAPI SetConsoleWindowInfo(HANDLE hConsoleOutput,WINBOOL bAbsolute,CONST SMALL_RECT *lpConsoleWindow);
-  WINBASEAPI WINBOOL WINAPI SetConsoleTextAttribute(HANDLE hConsoleOutput,WORD wAttributes);
-  WINBASEAPI WINBOOL WINAPI SetConsoleCtrlHandler(PHANDLER_ROUTINE HandlerRoutine,WINBOOL Add);
-  WINBASEAPI WINBOOL WINAPI GenerateConsoleCtrlEvent(DWORD dwCtrlEvent,DWORD dwProcessGroupId);
-  WINBASEAPI WINBOOL WINAPI AllocConsole(VOID);
-  WINBASEAPI WINBOOL WINAPI FreeConsole(VOID);
-  WINBASEAPI WINBOOL WINAPI AttachConsole(DWORD dwProcessId);
-
-#define ATTACH_PARENT_PROCESS ((DWORD)-1)
-
-  WINBASEAPI DWORD WINAPI GetConsoleTitleA(LPSTR lpConsoleTitle,DWORD nSize);
-  WINBASEAPI DWORD WINAPI GetConsoleTitleW(LPWSTR lpConsoleTitle,DWORD nSize);
-  WINBASEAPI WINBOOL WINAPI SetConsoleTitleA(LPCSTR lpConsoleTitle);
-  WINBASEAPI WINBOOL WINAPI SetConsoleTitleW(LPCWSTR lpConsoleTitle);
-  WINBASEAPI WINBOOL WINAPI ReadConsoleA(HANDLE hConsoleInput,LPVOID lpBuffer,DWORD nNumberOfCharsToRead,LPDWORD lpNumberOfCharsRead,LPVOID lpReserved);
-  WINBASEAPI WINBOOL WINAPI ReadConsoleW(HANDLE hConsoleInput,LPVOID lpBuffer,DWORD nNumberOfCharsToRead,LPDWORD lpNumberOfCharsRead,LPVOID lpReserved);
-  WINBASEAPI WINBOOL WINAPI WriteConsoleA(HANDLE hConsoleOutput,CONST VOID *lpBuffer,DWORD nNumberOfCharsToWrite,LPDWORD lpNumberOfCharsWritten,LPVOID lpReserved);
-  WINBASEAPI WINBOOL WINAPI WriteConsoleW(HANDLE hConsoleOutput,CONST VOID *lpBuffer,DWORD nNumberOfCharsToWrite,LPDWORD lpNumberOfCharsWritten,LPVOID lpReserved);
-
-#define CONSOLE_TEXTMODE_BUFFER 1
-
-  WINBASEAPI HANDLE WINAPI CreateConsoleScreenBuffer(DWORD dwDesiredAccess,DWORD dwShareMode,CONST SECURITY_ATTRIBUTES *lpSecurityAttributes,DWORD dwFlags,LPVOID lpScreenBufferData);
-  WINBASEAPI UINT WINAPI GetConsoleCP(VOID);
-  WINBASEAPI WINBOOL WINAPI SetConsoleCP(UINT wCodePageID);
-  WINBASEAPI UINT WINAPI GetConsoleOutputCP(VOID);
-  WINBASEAPI WINBOOL WINAPI SetConsoleOutputCP(UINT wCodePageID);
-
-#define CONSOLE_FULLSCREEN 1
-#define CONSOLE_FULLSCREEN_HARDWARE 2
-
-  WINBASEAPI WINBOOL WINAPI GetConsoleDisplayMode(LPDWORD lpModeFlags);
-  WINBASEAPI HWND WINAPI GetConsoleWindow(VOID);
-  WINBASEAPI DWORD WINAPI GetConsoleProcessList(LPDWORD lpdwProcessList,DWORD dwProcessCount);
-  WINBASEAPI WINBOOL WINAPI AddConsoleAliasA(LPSTR Source,LPSTR Target,LPSTR ExeName);
-  WINBASEAPI WINBOOL WINAPI AddConsoleAliasW(LPWSTR Source,LPWSTR Target,LPWSTR ExeName);
-  WINBASEAPI DWORD WINAPI GetConsoleAliasA(LPSTR Source,LPSTR TargetBuffer,DWORD TargetBufferLength,LPSTR ExeName);
-  WINBASEAPI DWORD WINAPI GetConsoleAliasW(LPWSTR Source,LPWSTR TargetBuffer,DWORD TargetBufferLength,LPWSTR ExeName);
-  WINBASEAPI DWORD WINAPI GetConsoleAliasesLengthA(LPSTR ExeName);
-  WINBASEAPI DWORD WINAPI GetConsoleAliasesLengthW(LPWSTR ExeName);
-  WINBASEAPI DWORD WINAPI GetConsoleAliasExesLengthA(VOID);
-  WINBASEAPI DWORD WINAPI GetConsoleAliasExesLengthW(VOID);
-  WINBASEAPI DWORD WINAPI GetConsoleAliasesA(LPSTR AliasBuffer,DWORD AliasBufferLength,LPSTR ExeName);
-  WINBASEAPI DWORD WINAPI GetConsoleAliasesW(LPWSTR AliasBuffer,DWORD AliasBufferLength,LPWSTR ExeName);
-  WINBASEAPI DWORD WINAPI GetConsoleAliasExesA(LPSTR ExeNameBuffer,DWORD ExeNameBufferLength);
-  WINBASEAPI DWORD WINAPI GetConsoleAliasExesW(LPWSTR ExeNameBuffer,DWORD ExeNameBufferLength);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/tinyc/win32/include/winapi/windef.h b/tinyc/win32/include/winapi/windef.h
deleted file mode 100644
index d63bdef13..000000000
--- a/tinyc/win32/include/winapi/windef.h
+++ /dev/null
@@ -1,293 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _WINDEF_
-#define _WINDEF_
-
-#ifndef STRICT
-#define STRICT 1
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef WINVER
-#define WINVER 0x0502
-#endif
-
-#ifndef BASETYPES
-#define BASETYPES
-  typedef unsigned long ULONG;
-  typedef ULONG *PULONG;
-  typedef unsigned short USHORT;
-  typedef USHORT *PUSHORT;
-  typedef unsigned char UCHAR;
-  typedef UCHAR *PUCHAR;
-  typedef char *PSZ;
-#endif
-
-#define MAX_PATH 260
-
-#ifndef NULL
-#ifdef __cplusplus
-#define NULL 0
-#else
-#define NULL ((void *)0)
-#endif
-#endif
-
-#ifndef FALSE
-#define FALSE 0
-#endif
-
-#ifndef TRUE
-#define TRUE 1
-#endif
-
-#ifndef IN
-#define IN
-#endif
-
-#ifndef OUT
-#define OUT
-#endif
-
-#ifndef OPTIONAL
-#define OPTIONAL
-#endif
-
-#undef far
-#undef near
-#undef pascal
-
-#define far
-#define near
-#define pascal __stdcall
-
-#define cdecl
-#ifndef CDECL
-#define CDECL
-#endif
-#ifndef CALLBACK
-#define CALLBACK __stdcall
-#endif
-#ifndef WINAPI
-#define WINAPI __stdcall
-#endif
-#define WINAPIV __cdecl
-#define APIENTRY WINAPI
-#define APIPRIVATE WINAPI
-#define PASCAL WINAPI
-#define WINAPI_INLINE WINAPI
-
-#undef FAR
-#undef NEAR
-#define FAR
-#define NEAR
-#ifndef CONST
-#define CONST const
-#endif
-
-  typedef unsigned long DWORD;
-  typedef int WINBOOL;
-#define BOOL WINBOOL
-  typedef unsigned char BYTE;
-  typedef unsigned short WORD;
-  typedef float FLOAT;
-  typedef FLOAT *PFLOAT;
-  typedef WINBOOL *PBOOL;
-  typedef WINBOOL *LPBOOL;
-  typedef BYTE *PBYTE;
-  typedef BYTE *LPBYTE;
-  typedef int *PINT;
-  typedef int *LPINT;
-  typedef WORD *PWORD;
-  typedef WORD *LPWORD;
-  typedef long *LPLONG;
-  typedef DWORD *PDWORD;
-  typedef DWORD *LPDWORD;
-  typedef void *LPVOID;
-# ifndef _LPCVOID_DEFINED
-#define _LPCVOID_DEFINED
-typedef CONST void *LPCVOID;
-#endif
-  typedef int INT;
-  typedef unsigned int UINT;
-  typedef unsigned int *PUINT;
-
-#ifndef NT_INCLUDED
-#include <winnt.h>
-#endif
-
-//gr #include <specstrings.h>
-
-  typedef UINT_PTR WPARAM;
-  typedef LONG_PTR LPARAM;
-  typedef LONG_PTR LRESULT;
-
-#ifndef __cplusplus
-#ifndef NOMINMAX
-#ifndef max
-#define max(a,b) (((a) > (b)) ? (a) : (b))
-#endif
-
-#ifndef min
-#define min(a,b) (((a) < (b)) ? (a) : (b))
-#endif
-#endif
-#endif
-
-#define MAKEWORD(a,b) ((WORD)(((BYTE)((DWORD_PTR)(a) & 0xff)) | ((WORD)((BYTE)((DWORD_PTR)(b) & 0xff))) << 8))
-#define MAKELONG(a,b) ((LONG)(((WORD)((DWORD_PTR)(a) & 0xffff)) | ((DWORD)((WORD)((DWORD_PTR)(b) & 0xffff))) << 16))
-#define LOWORD(l) ((WORD)((DWORD_PTR)(l) & 0xffff))
-#define HIWORD(l) ((WORD)((DWORD_PTR)(l) >> 16))
-#define LOBYTE(w) ((BYTE)((DWORD_PTR)(w) & 0xff))
-#define HIBYTE(w) ((BYTE)((DWORD_PTR)(w) >> 8))
-
-#ifndef WIN_INTERNAL
-  DECLARE_HANDLE (HWND);
-  DECLARE_HANDLE (HHOOK);
-#ifdef WINABLE
-  DECLARE_HANDLE (HEVENT);
-#endif
-#endif
-
-  typedef WORD ATOM;
-
-  typedef HANDLE *SPHANDLE;
-  typedef HANDLE *LPHANDLE;
-  typedef HANDLE HGLOBAL;
-  typedef HANDLE HLOCAL;
-  typedef HANDLE GLOBALHANDLE;
-  typedef HANDLE LOCALHANDLE;
-#ifdef _WIN64
-  typedef INT_PTR (WINAPI *FARPROC)();
-  typedef INT_PTR (WINAPI *NEARPROC)();
-  typedef INT_PTR (WINAPI *PROC)();
-#else
-  typedef int (WINAPI *FARPROC)();
-  typedef int (WINAPI *NEARPROC)();
-  typedef int (WINAPI *PROC)();
-#endif
-
-  typedef void *HGDIOBJ;
-
-  DECLARE_HANDLE(HKEY);
-  typedef HKEY *PHKEY;
-
-  DECLARE_HANDLE(HACCEL);
-  DECLARE_HANDLE(HBITMAP);
-  DECLARE_HANDLE(HBRUSH);
-  DECLARE_HANDLE(HCOLORSPACE);
-  DECLARE_HANDLE(HDC);
-  DECLARE_HANDLE(HGLRC);
-  DECLARE_HANDLE(HDESK);
-  DECLARE_HANDLE(HENHMETAFILE);
-  DECLARE_HANDLE(HFONT);
-  DECLARE_HANDLE(HICON);
-  DECLARE_HANDLE(HMENU);
-  DECLARE_HANDLE(HMETAFILE);
-  DECLARE_HANDLE(HINSTANCE);
-  typedef HINSTANCE HMODULE;
-  DECLARE_HANDLE(HPALETTE);
-  DECLARE_HANDLE(HPEN);
-  DECLARE_HANDLE(HRGN);
-  DECLARE_HANDLE(HRSRC);
-  DECLARE_HANDLE(HSTR);
-  DECLARE_HANDLE(HTASK);
-  DECLARE_HANDLE(HWINSTA);
-  DECLARE_HANDLE(HKL);
-  DECLARE_HANDLE(HMONITOR);
-  DECLARE_HANDLE(HWINEVENTHOOK);
-  DECLARE_HANDLE(HUMPD);
-
-  typedef int HFILE;
-  typedef HICON HCURSOR;
-  typedef DWORD COLORREF;
-  typedef DWORD *LPCOLORREF;
-
-#define HFILE_ERROR ((HFILE)-1)
-
-  typedef struct tagRECT {
-    LONG left;
-    LONG top;
-    LONG right;
-    LONG bottom;
-  } RECT,*PRECT,*NPRECT,*LPRECT;
-
-  typedef const RECT *LPCRECT;
-
-  typedef struct _RECTL {
-    LONG left;
-    LONG top;
-    LONG right;
-    LONG bottom;
-  } RECTL,*PRECTL,*LPRECTL;
-
-  typedef const RECTL *LPCRECTL;
-
-  typedef struct tagPOINT {
-    LONG x;
-    LONG y;
-  } POINT,*PPOINT,*NPPOINT,*LPPOINT;
-
-  typedef struct _POINTL {
-    LONG x;
-    LONG y;
-  } POINTL,*PPOINTL;
-
-  typedef struct tagSIZE {
-    LONG cx;
-    LONG cy;
-  } SIZE,*PSIZE,*LPSIZE;
-
-  typedef SIZE SIZEL;
-  typedef SIZE *PSIZEL,*LPSIZEL;
-
-  typedef struct tagPOINTS {
-    SHORT x;
-    SHORT y;
-  } POINTS,*PPOINTS,*LPPOINTS;
-
-  typedef struct _FILETIME {
-    DWORD dwLowDateTime;
-    DWORD dwHighDateTime;
-  } FILETIME,*PFILETIME,*LPFILETIME;
-#define _FILETIME_
-
-#define DM_UPDATE 1
-#define DM_COPY 2
-#define DM_PROMPT 4
-#define DM_MODIFY 8
-
-#define DM_IN_BUFFER DM_MODIFY
-#define DM_IN_PROMPT DM_PROMPT
-#define DM_OUT_BUFFER DM_COPY
-#define DM_OUT_DEFAULT DM_UPDATE
-
-#define DC_FIELDS 1
-#define DC_PAPERS 2
-#define DC_PAPERSIZE 3
-#define DC_MINEXTENT 4
-#define DC_MAXEXTENT 5
-#define DC_BINS 6
-#define DC_DUPLEX 7
-#define DC_SIZE 8
-#define DC_EXTRA 9
-#define DC_VERSION 10
-#define DC_DRIVER 11
-#define DC_BINNAMES 12
-#define DC_ENUMRESOLUTIONS 13
-#define DC_FILEDEPENDENCIES 14
-#define DC_TRUETYPE 15
-#define DC_PAPERNAMES 16
-#define DC_ORIENTATION 17
-#define DC_COPIES 18
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/tinyc/win32/include/winapi/windows.h b/tinyc/win32/include/winapi/windows.h
deleted file mode 100644
index 2660d7f08..000000000
--- a/tinyc/win32/include/winapi/windows.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _WINDOWS_
-#define _WINDOWS_
-
-#ifndef WIN32_LEAN_AND_MEAN
-#define WIN32_LEAN_AND_MEAN 1
-#endif
-
-#ifndef WINVER
-#define WINVER 0x0502
-#endif
-
-#include <_mingw.h>
-
-#ifndef _INC_WINDOWS
-#define _INC_WINDOWS
-
-#if defined(RC_INVOKED) && !defined(NOWINRES)
-
-#include <winresrc.h>
-#else
-
-#ifdef RC_INVOKED
-#define NOATOM
-#define NOGDI
-#define NOGDICAPMASKS
-#define NOMETAFILE
-#define NOMINMAX
-#define NOMSG
-#define NOOPENFILE
-#define NORASTEROPS
-#define NOSCROLL
-#define NOSOUND
-#define NOSYSMETRICS
-#define NOTEXTMETRIC
-#define NOWH
-#define NOCOMM
-#define NOKANJI
-#define NOCRYPT
-#define NOMCX
-#endif
-
-#if !defined(I_X86_) && !defined(_IA64_) && !defined(_AMD64_) && (defined(_X86_) && !defined(__x86_64))
-#define I_X86_
-#endif
-
-#if !defined(I_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(__x86_64)
-#define _AMD64_
-#endif
-
-#if !defined(I_X86_) && !(defined(_X86_) && !defined(__x86_64)) && !defined(_AMD64_) && defined(__ia64__)
-#if !defined(_IA64_)
-#define _IA64_
-#endif
-#endif
-
-#ifndef RC_INVOKED
-#include <excpt.h>
-#include <stdarg.h>
-#endif
-
-#include <windef.h>
-#include <winbase.h>
-#include <wingdi.h>
-#include <winuser.h>
-//gr #include <winnls.h>
-#include <wincon.h>
-#include <winver.h>
-#include <winreg.h>
-//gr #include <winnetwk.h>
-
-#ifndef WIN32_LEAN_AND_MEAN
-#include <cderr.h>
-#include <dde.h>
-#include <ddeml.h>
-#include <dlgs.h>
-#include <lzexpand.h>
-#include <mmsystem.h>
-#include <nb30.h>
-#include <rpc.h>
-#include <shellapi.h>
-#include <winperf.h>
-#include <winsock.h>
-#ifndef NOCRYPT
-#include <wincrypt.h>
-#include <winefs.h>
-#include <winscard.h>
-#endif
-
-#ifndef NOUSER
-#ifndef NOGDI
-#include <winspool.h>
-#ifdef INC_OLE1
-#include <ole.h>
-#else
-#include <ole2.h>
-#endif
-#include <commdlg.h>
-#endif
-#endif
-#endif
-
-//gr #include <stralign.h>
-
-#ifdef INC_OLE2
-#include <ole2.h>
-#endif
-
-#ifndef NOSERVICE
-#include <winsvc.h>
-#endif
-
-#ifndef NOMCX
-#include <mcx.h>
-#endif
-
-#ifndef NOIME
-#include <imm.h>
-#endif
-
-#endif
-#endif
-#endif
diff --git a/tinyc/win32/include/winapi/winerror.h b/tinyc/win32/include/winapi/winerror.h
deleted file mode 100644
index 77d85edc8..000000000
--- a/tinyc/win32/include/winapi/winerror.h
+++ /dev/null
@@ -1,3166 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _WINERROR_
-#define _WINERROR_
-
-#define FACILITY_WINDOWSUPDATE 36
-#define FACILITY_WINDOWS_CE 24
-#define FACILITY_WINDOWS 8
-#define FACILITY_URT 19
-#define FACILITY_UMI 22
-#define FACILITY_SXS 23
-#define FACILITY_STORAGE 3
-#define FACILITY_STATE_MANAGEMENT 34
-#define FACILITY_SSPI 9
-#define FACILITY_SCARD 16
-#define FACILITY_SETUPAPI 15
-#define FACILITY_SECURITY 9
-#define FACILITY_RPC 1
-#define FACILITY_WIN32 7
-#define FACILITY_CONTROL 10
-#define FACILITY_NULL 0
-#define FACILITY_METADIRECTORY 35
-#define FACILITY_MSMQ 14
-#define FACILITY_MEDIASERVER 13
-#define FACILITY_INTERNET 12
-#define FACILITY_ITF 4
-#define FACILITY_HTTP 25
-#define FACILITY_DPLAY 21
-#define FACILITY_DISPATCH 2
-#define FACILITY_DIRECTORYSERVICE 37
-#define FACILITY_CONFIGURATION 33
-#define FACILITY_COMPLUS 17
-#define FACILITY_CERT 11
-#define FACILITY_BACKGROUNDCOPY 32
-#define FACILITY_ACS 20
-#define FACILITY_AAF 18
-#define ERROR_SUCCESS 0L
-#define NO_ERROR 0L
-#define SEC_E_OK ((HRESULT)0x00000000L)
-#define ERROR_INVALID_FUNCTION 1L
-#define ERROR_FILE_NOT_FOUND 2L
-#define ERROR_PATH_NOT_FOUND 3L
-#define ERROR_TOO_MANY_OPEN_FILES 4L
-#define ERROR_ACCESS_DENIED 5L
-#define ERROR_INVALID_HANDLE 6L
-#define ERROR_ARENA_TRASHED 7L
-#define ERROR_NOT_ENOUGH_MEMORY 8L
-#define ERROR_INVALID_BLOCK 9L
-#define ERROR_BAD_ENVIRONMENT 10L
-#define ERROR_BAD_FORMAT 11L
-#define ERROR_INVALID_ACCESS 12L
-#define ERROR_INVALID_DATA 13L
-#define ERROR_OUTOFMEMORY 14L
-#define ERROR_INVALID_DRIVE 15L
-#define ERROR_CURRENT_DIRECTORY 16L
-#define ERROR_NOT_SAME_DEVICE 17L
-#define ERROR_NO_MORE_FILES 18L
-#define ERROR_WRITE_PROTECT 19L
-#define ERROR_BAD_UNIT 20L
-#define ERROR_NOT_READY 21L
-#define ERROR_BAD_COMMAND 22L
-#define ERROR_CRC 23L
-#define ERROR_BAD_LENGTH 24L
-#define ERROR_SEEK 25L
-#define ERROR_NOT_DOS_DISK 26L
-#define ERROR_SECTOR_NOT_FOUND 27L
-#define ERROR_OUT_OF_PAPER 28L
-#define ERROR_WRITE_FAULT 29L
-#define ERROR_READ_FAULT 30L
-#define ERROR_GEN_FAILURE 31L
-#define ERROR_SHARING_VIOLATION 32L
-#define ERROR_LOCK_VIOLATION 33L
-#define ERROR_WRONG_DISK 34L
-#define ERROR_SHARING_BUFFER_EXCEEDED 36L
-#define ERROR_HANDLE_EOF 38L
-#define ERROR_HANDLE_DISK_FULL 39L
-#define ERROR_NOT_SUPPORTED 50L
-#define ERROR_REM_NOT_LIST 51L
-#define ERROR_DUP_NAME 52L
-#define ERROR_BAD_NETPATH 53L
-#define ERROR_NETWORK_BUSY 54L
-#define ERROR_DEV_NOT_EXIST 55L
-#define ERROR_TOO_MANY_CMDS 56L
-#define ERROR_ADAP_HDW_ERR 57L
-#define ERROR_BAD_NET_RESP 58L
-#define ERROR_UNEXP_NET_ERR 59L
-#define ERROR_BAD_REM_ADAP 60L
-#define ERROR_PRINTQ_FULL 61L
-#define ERROR_NO_SPOOL_SPACE 62L
-#define ERROR_PRINT_CANCELLED 63L
-#define ERROR_NETNAME_DELETED 64L
-#define ERROR_NETWORK_ACCESS_DENIED 65L
-#define ERROR_BAD_DEV_TYPE 66L
-#define ERROR_BAD_NET_NAME 67L
-#define ERROR_TOO_MANY_NAMES 68L
-#define ERROR_TOO_MANY_SESS 69L
-#define ERROR_SHARING_PAUSED 70L
-#define ERROR_REQ_NOT_ACCEP 71L
-#define ERROR_REDIR_PAUSED 72L
-#define ERROR_FILE_EXISTS 80L
-#define ERROR_CANNOT_MAKE 82L
-#define ERROR_FAIL_I24 83L
-#define ERROR_OUT_OF_STRUCTURES 84L
-#define ERROR_ALREADY_ASSIGNED 85L
-#define ERROR_INVALID_PASSWORD 86L
-#define ERROR_INVALID_PARAMETER 87L
-#define ERROR_NET_WRITE_FAULT 88L
-#define ERROR_NO_PROC_SLOTS 89L
-#define ERROR_TOO_MANY_SEMAPHORES 100L
-#define ERROR_EXCL_SEM_ALREADY_OWNED 101L
-#define ERROR_SEM_IS_SET 102L
-#define ERROR_TOO_MANY_SEM_REQUESTS 103L
-#define ERROR_INVALID_AT_INTERRUPT_TIME 104L
-#define ERROR_SEM_OWNER_DIED 105L
-#define ERROR_SEM_USER_LIMIT 106L
-#define ERROR_DISK_CHANGE 107L
-#define ERROR_DRIVE_LOCKED 108L
-#define ERROR_BROKEN_PIPE 109L
-#define ERROR_OPEN_FAILED 110L
-#define ERROR_BUFFER_OVERFLOW 111L
-#define ERROR_DISK_FULL 112L
-#define ERROR_NO_MORE_SEARCH_HANDLES 113L
-#define ERROR_INVALID_TARGET_HANDLE 114L
-#define ERROR_INVALID_CATEGORY 117L
-#define ERROR_INVALID_VERIFY_SWITCH 118L
-#define ERROR_BAD_DRIVER_LEVEL 119L
-#define ERROR_CALL_NOT_IMPLEMENTED 120L
-#define ERROR_SEM_TIMEOUT 121L
-#define ERROR_INSUFFICIENT_BUFFER 122L
-#define ERROR_INVALID_NAME 123L
-#define ERROR_INVALID_LEVEL 124L
-#define ERROR_NO_VOLUME_LABEL 125L
-#define ERROR_MOD_NOT_FOUND 126L
-#define ERROR_PROC_NOT_FOUND 127L
-#define ERROR_WAIT_NO_CHILDREN 128L
-#define ERROR_CHILD_NOT_COMPLETE 129L
-#define ERROR_DIRECT_ACCESS_HANDLE 130L
-#define ERROR_NEGATIVE_SEEK 131L
-#define ERROR_SEEK_ON_DEVICE 132L
-#define ERROR_IS_JOIN_TARGET 133L
-#define ERROR_IS_JOINED 134L
-#define ERROR_IS_SUBSTED 135L
-#define ERROR_NOT_JOINED 136L
-#define ERROR_NOT_SUBSTED 137L
-#define ERROR_JOIN_TO_JOIN 138L
-#define ERROR_SUBST_TO_SUBST 139L
-#define ERROR_JOIN_TO_SUBST 140L
-#define ERROR_SUBST_TO_JOIN 141L
-#define ERROR_BUSY_DRIVE 142L
-#define ERROR_SAME_DRIVE 143L
-#define ERROR_DIR_NOT_ROOT 144L
-#define ERROR_DIR_NOT_EMPTY 145L
-#define ERROR_IS_SUBST_PATH 146L
-#define ERROR_IS_JOIN_PATH 147L
-#define ERROR_PATH_BUSY 148L
-#define ERROR_IS_SUBST_TARGET 149L
-#define ERROR_SYSTEM_TRACE 150L
-#define ERROR_INVALID_EVENT_COUNT 151L
-#define ERROR_TOO_MANY_MUXWAITERS 152L
-#define ERROR_INVALID_LIST_FORMAT 153L
-#define ERROR_LABEL_TOO_LONG 154L
-#define ERROR_TOO_MANY_TCBS 155L
-#define ERROR_SIGNAL_REFUSED 156L
-#define ERROR_DISCARDED 157L
-#define ERROR_NOT_LOCKED 158L
-#define ERROR_BAD_THREADID_ADDR 159L
-#define ERROR_BAD_ARGUMENTS 160L
-#define ERROR_BAD_PATHNAME 161L
-#define ERROR_SIGNAL_PENDING 162L
-#define ERROR_MAX_THRDS_REACHED 164L
-#define ERROR_LOCK_FAILED 167L
-#define ERROR_BUSY 170L
-#define ERROR_CANCEL_VIOLATION 173L
-#define ERROR_ATOMIC_LOCKS_NOT_SUPPORTED 174L
-#define ERROR_INVALID_SEGMENT_NUMBER 180L
-#define ERROR_INVALID_ORDINAL 182L
-#define ERROR_ALREADY_EXISTS 183L
-#define ERROR_INVALID_FLAG_NUMBER 186L
-#define ERROR_SEM_NOT_FOUND 187L
-#define ERROR_INVALID_STARTING_CODESEG 188L
-#define ERROR_INVALID_STACKSEG 189L
-#define ERROR_INVALID_MODULETYPE 190L
-#define ERROR_INVALID_EXE_SIGNATURE 191L
-#define ERROR_EXE_MARKED_INVALID 192L
-#define ERROR_BAD_EXE_FORMAT 193L
-#define ERROR_ITERATED_DATA_EXCEEDS_64k 194L
-#define ERROR_INVALID_MINALLOCSIZE 195L
-#define ERROR_DYNLINK_FROM_INVALID_RING 196L
-#define ERROR_IOPL_NOT_ENABLED 197L
-#define ERROR_INVALID_SEGDPL 198L
-#define ERROR_AUTODATASEG_EXCEEDS_64k 199L
-#define ERROR_RING2SEG_MUST_BE_MOVABLE 200L
-#define ERROR_RELOC_CHAIN_XEEDS_SEGLIM 201L
-#define ERROR_INFLOOP_IN_RELOC_CHAIN 202L
-#define ERROR_ENVVAR_NOT_FOUND 203L
-#define ERROR_NO_SIGNAL_SENT 205L
-#define ERROR_FILENAME_EXCED_RANGE 206L
-#define ERROR_RING2_STACK_IN_USE 207L
-#define ERROR_META_EXPANSION_TOO_LONG 208L
-#define ERROR_INVALID_SIGNAL_NUMBER 209L
-#define ERROR_THREAD_1_INACTIVE 210L
-#define ERROR_LOCKED 212L
-#define ERROR_TOO_MANY_MODULES 214L
-#define ERROR_NESTING_NOT_ALLOWED 215L
-#define ERROR_EXE_MACHINE_TYPE_MISMATCH 216L
-#define ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY 217L
-#define ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY 218L
-#define ERROR_BAD_PIPE 230L
-#define ERROR_PIPE_BUSY 231L
-#define ERROR_NO_DATA 232L
-#define ERROR_PIPE_NOT_CONNECTED 233L
-#define ERROR_MORE_DATA 234L
-#define ERROR_VC_DISCONNECTED 240L
-#define ERROR_INVALID_EA_NAME 254L
-#define ERROR_EA_LIST_INCONSISTENT 255L
-#define WAIT_TIMEOUT 258L
-#define ERROR_NO_MORE_ITEMS 259L
-#define ERROR_CANNOT_COPY 266L
-#define ERROR_DIRECTORY 267L
-#define ERROR_EAS_DIDNT_FIT 275L
-#define ERROR_EA_FILE_CORRUPT 276L
-#define ERROR_EA_TABLE_FULL 277L
-#define ERROR_INVALID_EA_HANDLE 278L
-#define ERROR_EAS_NOT_SUPPORTED 282L
-#define ERROR_NOT_OWNER 288L
-#define ERROR_TOO_MANY_POSTS 298L
-#define ERROR_PARTIAL_COPY 299L
-#define ERROR_OPLOCK_NOT_GRANTED 300L
-#define ERROR_INVALID_OPLOCK_PROTOCOL 301L
-#define ERROR_DISK_TOO_FRAGMENTED 302L
-#define ERROR_DELETE_PENDING 303L
-#define ERROR_MR_MID_NOT_FOUND 317L
-#define ERROR_SCOPE_NOT_FOUND 318L
-#define ERROR_INVALID_ADDRESS 487L
-#define ERROR_ARITHMETIC_OVERFLOW 534L
-#define ERROR_PIPE_CONNECTED 535L
-#define ERROR_PIPE_LISTENING 536L
-#define ERROR_EA_ACCESS_DENIED 994L
-#define ERROR_OPERATION_ABORTED 995L
-#define ERROR_IO_INCOMPLETE 996L
-#define ERROR_IO_PENDING 997L
-#define ERROR_NOACCESS 998L
-#define ERROR_SWAPERROR 999L
-#define ERROR_STACK_OVERFLOW 1001L
-#define ERROR_INVALID_MESSAGE 1002L
-#define ERROR_CAN_NOT_COMPLETE 1003L
-#define ERROR_INVALID_FLAGS 1004L
-#define ERROR_UNRECOGNIZED_VOLUME 1005L
-#define ERROR_FILE_INVALID 1006L
-#define ERROR_FULLSCREEN_MODE 1007L
-#define ERROR_NO_TOKEN 1008L
-#define ERROR_BADDB 1009L
-#define ERROR_BADKEY 1010L
-#define ERROR_CANTOPEN 1011L
-#define ERROR_CANTREAD 1012L
-#define ERROR_CANTWRITE 1013L
-#define ERROR_REGISTRY_RECOVERED 1014L
-#define ERROR_REGISTRY_CORRUPT 1015L
-#define ERROR_REGISTRY_IO_FAILED 1016L
-#define ERROR_NOT_REGISTRY_FILE 1017L
-#define ERROR_KEY_DELETED 1018L
-#define ERROR_NO_LOG_SPACE 1019L
-#define ERROR_KEY_HAS_CHILDREN 1020L
-#define ERROR_CHILD_MUST_BE_VOLATILE 1021L
-#define ERROR_NOTIFY_ENUM_DIR 1022L
-#define ERROR_DEPENDENT_SERVICES_RUNNING 1051L
-#define ERROR_INVALID_SERVICE_CONTROL 1052L
-#define ERROR_SERVICE_REQUEST_TIMEOUT 1053L
-#define ERROR_SERVICE_NO_THREAD 1054L
-#define ERROR_SERVICE_DATABASE_LOCKED 1055L
-#define ERROR_SERVICE_ALREADY_RUNNING 1056L
-#define ERROR_INVALID_SERVICE_ACCOUNT 1057L
-#define ERROR_SERVICE_DISABLED 1058L
-#define ERROR_CIRCULAR_DEPENDENCY 1059L
-#define ERROR_SERVICE_DOES_NOT_EXIST 1060L
-#define ERROR_SERVICE_CANNOT_ACCEPT_CTRL 1061L
-#define ERROR_SERVICE_NOT_ACTIVE 1062L
-#define ERROR_FAILED_SERVICE_CONTROLLER_CONNECT 1063L
-#define ERROR_EXCEPTION_IN_SERVICE 1064L
-#define ERROR_DATABASE_DOES_NOT_EXIST 1065L
-#define ERROR_SERVICE_SPECIFIC_ERROR 1066L
-#define ERROR_PROCESS_ABORTED 1067L
-#define ERROR_SERVICE_DEPENDENCY_FAIL 1068L
-#define ERROR_SERVICE_LOGON_FAILED 1069L
-#define ERROR_SERVICE_START_HANG 1070L
-#define ERROR_INVALID_SERVICE_LOCK 1071L
-#define ERROR_SERVICE_MARKED_FOR_DELETE 1072L
-#define ERROR_SERVICE_EXISTS 1073L
-#define ERROR_ALREADY_RUNNING_LKG 1074L
-#define ERROR_SERVICE_DEPENDENCY_DELETED 1075L
-#define ERROR_BOOT_ALREADY_ACCEPTED 1076L
-#define ERROR_SERVICE_NEVER_STARTED 1077L
-#define ERROR_DUPLICATE_SERVICE_NAME 1078L
-#define ERROR_DIFFERENT_SERVICE_ACCOUNT 1079L
-#define ERROR_CANNOT_DETECT_DRIVER_FAILURE 1080L
-#define ERROR_CANNOT_DETECT_PROCESS_ABORT 1081L
-#define ERROR_NO_RECOVERY_PROGRAM 1082L
-#define ERROR_SERVICE_NOT_IN_EXE 1083L
-#define ERROR_NOT_SAFEBOOT_SERVICE 1084L
-#define ERROR_END_OF_MEDIA 1100L
-#define ERROR_FILEMARK_DETECTED 1101L
-#define ERROR_BEGINNING_OF_MEDIA 1102L
-#define ERROR_SETMARK_DETECTED 1103L
-#define ERROR_NO_DATA_DETECTED 1104L
-#define ERROR_PARTITION_FAILURE 1105L
-#define ERROR_INVALID_BLOCK_LENGTH 1106L
-#define ERROR_DEVICE_NOT_PARTITIONED 1107L
-#define ERROR_UNABLE_TO_LOCK_MEDIA 1108L
-#define ERROR_UNABLE_TO_UNLOAD_MEDIA 1109L
-#define ERROR_MEDIA_CHANGED 1110L
-#define ERROR_BUS_RESET 1111L
-#define ERROR_NO_MEDIA_IN_DRIVE 1112L
-#define ERROR_NO_UNICODE_TRANSLATION 1113L
-#define ERROR_DLL_INIT_FAILED 1114L
-#define ERROR_SHUTDOWN_IN_PROGRESS 1115L
-#define ERROR_NO_SHUTDOWN_IN_PROGRESS 1116L
-#define ERROR_IO_DEVICE 1117L
-#define ERROR_SERIAL_NO_DEVICE 1118L
-#define ERROR_IRQ_BUSY 1119L
-#define ERROR_MORE_WRITES 1120L
-#define ERROR_COUNTER_TIMEOUT 1121L
-#define ERROR_FLOPPY_ID_MARK_NOT_FOUND 1122L
-#define ERROR_FLOPPY_WRONG_CYLINDER 1123L
-#define ERROR_FLOPPY_UNKNOWN_ERROR 1124L
-#define ERROR_FLOPPY_BAD_REGISTERS 1125L
-#define ERROR_DISK_RECALIBRATE_FAILED 1126L
-#define ERROR_DISK_OPERATION_FAILED 1127L
-#define ERROR_DISK_RESET_FAILED 1128L
-#define ERROR_EOM_OVERFLOW 1129L
-#define ERROR_NOT_ENOUGH_SERVER_MEMORY 1130L
-#define ERROR_POSSIBLE_DEADLOCK 1131L
-#define ERROR_MAPPED_ALIGNMENT 1132L
-#define ERROR_SET_POWER_STATE_VETOED 1140L
-#define ERROR_SET_POWER_STATE_FAILED 1141L
-#define ERROR_TOO_MANY_LINKS 1142L
-#define ERROR_OLD_WIN_VERSION 1150L
-#define ERROR_APP_WRONG_OS 1151L
-#define ERROR_SINGLE_INSTANCE_APP 1152L
-#define ERROR_RMODE_APP 1153L
-#define ERROR_INVALID_DLL 1154L
-#define ERROR_NO_ASSOCIATION 1155L
-#define ERROR_DDE_FAIL 1156L
-#define ERROR_DLL_NOT_FOUND 1157L
-#define ERROR_NO_MORE_USER_HANDLES 1158L
-#define ERROR_MESSAGE_SYNC_ONLY 1159L
-#define ERROR_SOURCE_ELEMENT_EMPTY 1160L
-#define ERROR_DESTINATION_ELEMENT_FULL 1161L
-#define ERROR_ILLEGAL_ELEMENT_ADDRESS 1162L
-#define ERROR_MAGAZINE_NOT_PRESENT 1163L
-#define ERROR_DEVICE_REINITIALIZATION_NEEDED 1164L
-#define ERROR_DEVICE_REQUIRES_CLEANING 1165L
-#define ERROR_DEVICE_DOOR_OPEN 1166L
-#define ERROR_DEVICE_NOT_CONNECTED 1167L
-#define ERROR_NOT_FOUND 1168L
-#define ERROR_NO_MATCH 1169L
-#define ERROR_SET_NOT_FOUND 1170L
-#define ERROR_POINT_NOT_FOUND 1171L
-#define ERROR_NO_TRACKING_SERVICE 1172L
-#define ERROR_NO_VOLUME_ID 1173L
-#define ERROR_UNABLE_TO_REMOVE_REPLACED 1175L
-#define ERROR_UNABLE_TO_MOVE_REPLACEMENT 1176L
-#define ERROR_UNABLE_TO_MOVE_REPLACEMENT_2 1177L
-#define ERROR_JOURNAL_DELETE_IN_PROGRESS 1178L
-#define ERROR_JOURNAL_NOT_ACTIVE 1179L
-#define ERROR_POTENTIAL_FILE_FOUND 1180L
-#define ERROR_JOURNAL_ENTRY_DELETED 1181L
-#define ERROR_BAD_DEVICE 1200L
-#define ERROR_CONNECTION_UNAVAIL 1201L
-#define ERROR_DEVICE_ALREADY_REMEMBERED 1202L
-#define ERROR_NO_NET_OR_BAD_PATH 1203L
-#define ERROR_BAD_PROVIDER 1204L
-#define ERROR_CANNOT_OPEN_PROFILE 1205L
-#define ERROR_BAD_PROFILE 1206L
-#define ERROR_NOT_CONTAINER 1207L
-#define ERROR_EXTENDED_ERROR 1208L
-#define ERROR_INVALID_GROUPNAME 1209L
-#define ERROR_INVALID_COMPUTERNAME 1210L
-#define ERROR_INVALID_EVENTNAME 1211L
-#define ERROR_INVALID_DOMAINNAME 1212L
-#define ERROR_INVALID_SERVICENAME 1213L
-#define ERROR_INVALID_NETNAME 1214L
-#define ERROR_INVALID_SHARENAME 1215L
-#define ERROR_INVALID_PASSWORDNAME 1216L
-#define ERROR_INVALID_MESSAGENAME 1217L
-#define ERROR_INVALID_MESSAGEDEST 1218L
-#define ERROR_SESSION_CREDENTIAL_CONFLICT 1219L
-#define ERROR_REMOTE_SESSION_LIMIT_EXCEEDED 1220L
-#define ERROR_DUP_DOMAINNAME 1221L
-#define ERROR_NO_NETWORK 1222L
-#define ERROR_CANCELLED 1223L
-#define ERROR_USER_MAPPED_FILE 1224L
-#define ERROR_CONNECTION_REFUSED 1225L
-#define ERROR_GRACEFUL_DISCONNECT 1226L
-#define ERROR_ADDRESS_ALREADY_ASSOCIATED 1227L
-#define ERROR_ADDRESS_NOT_ASSOCIATED 1228L
-#define ERROR_CONNECTION_INVALID 1229L
-#define ERROR_CONNECTION_ACTIVE 1230L
-#define ERROR_NETWORK_UNREACHABLE 1231L
-#define ERROR_HOST_UNREACHABLE 1232L
-#define ERROR_PROTOCOL_UNREACHABLE 1233L
-#define ERROR_PORT_UNREACHABLE 1234L
-#define ERROR_REQUEST_ABORTED 1235L
-#define ERROR_CONNECTION_ABORTED 1236L
-#define ERROR_RETRY 1237L
-#define ERROR_CONNECTION_COUNT_LIMIT 1238L
-#define ERROR_LOGIN_TIME_RESTRICTION 1239L
-#define ERROR_LOGIN_WKSTA_RESTRICTION 1240L
-#define ERROR_INCORRECT_ADDRESS 1241L
-#define ERROR_ALREADY_REGISTERED 1242L
-#define ERROR_SERVICE_NOT_FOUND 1243L
-#define ERROR_NOT_AUTHENTICATED 1244L
-#define ERROR_NOT_LOGGED_ON 1245L
-#define ERROR_CONTINUE 1246L
-#define ERROR_ALREADY_INITIALIZED 1247L
-#define ERROR_NO_MORE_DEVICES 1248L
-#define ERROR_NO_SUCH_SITE 1249L
-#define ERROR_DOMAIN_CONTROLLER_EXISTS 1250L
-#define ERROR_ONLY_IF_CONNECTED 1251L
-#define ERROR_OVERRIDE_NOCHANGES 1252L
-#define ERROR_BAD_USER_PROFILE 1253L
-#define ERROR_NOT_SUPPORTED_ON_SBS 1254L
-#define ERROR_SERVER_SHUTDOWN_IN_PROGRESS 1255L
-#define ERROR_HOST_DOWN 1256L
-#define ERROR_NON_ACCOUNT_SID 1257L
-#define ERROR_NON_DOMAIN_SID 1258L
-#define ERROR_APPHELP_BLOCK 1259L
-#define ERROR_ACCESS_DISABLED_BY_POLICY 1260L
-#define ERROR_REG_NAT_CONSUMPTION 1261L
-#define ERROR_CSCSHARE_OFFLINE 1262L
-#define ERROR_PKINIT_FAILURE 1263L
-#define ERROR_SMARTCARD_SUBSYSTEM_FAILURE 1264L
-#define ERROR_DOWNGRADE_DETECTED 1265L
-#define ERROR_MACHINE_LOCKED 1271L
-#define ERROR_CALLBACK_SUPPLIED_INVALID_DATA 1273L
-#define ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED 1274L
-#define ERROR_DRIVER_BLOCKED 1275L
-#define ERROR_INVALID_IMPORT_OF_NON_DLL 1276L
-#define ERROR_ACCESS_DISABLED_WEBBLADE 1277L
-#define ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER 1278L
-#define ERROR_RECOVERY_FAILURE 1279L
-#define ERROR_ALREADY_FIBER 1280L
-#define ERROR_ALREADY_THREAD 1281L
-#define ERROR_STACK_BUFFER_OVERRUN 1282L
-#define ERROR_PARAMETER_QUOTA_EXCEEDED 1283L
-#define ERROR_DEBUGGER_INACTIVE 1284L
-#define ERROR_DELAY_LOAD_FAILED 1285L
-#define ERROR_VDM_DISALLOWED 1286L
-#define ERROR_UNIDENTIFIED_ERROR 1287L
-#define ERROR_NOT_ALL_ASSIGNED 1300L
-#define ERROR_SOME_NOT_MAPPED 1301L
-#define ERROR_NO_QUOTAS_FOR_ACCOUNT 1302L
-#define ERROR_LOCAL_USER_SESSION_KEY 1303L
-#define ERROR_NULL_LM_PASSWORD 1304L
-#define ERROR_UNKNOWN_REVISION 1305L
-#define ERROR_REVISION_MISMATCH 1306L
-#define ERROR_INVALID_OWNER 1307L
-#define ERROR_INVALID_PRIMARY_GROUP 1308L
-#define ERROR_NO_IMPERSONATION_TOKEN 1309L
-#define ERROR_CANT_DISABLE_MANDATORY 1310L
-#define ERROR_NO_LOGON_SERVERS 1311L
-#define ERROR_NO_SUCH_LOGON_SESSION 1312L
-#define ERROR_NO_SUCH_PRIVILEGE 1313L
-#define ERROR_PRIVILEGE_NOT_HELD 1314L
-#define ERROR_INVALID_ACCOUNT_NAME 1315L
-#define ERROR_USER_EXISTS 1316L
-#define ERROR_NO_SUCH_USER 1317L
-#define ERROR_GROUP_EXISTS 1318L
-#define ERROR_NO_SUCH_GROUP 1319L
-#define ERROR_MEMBER_IN_GROUP 1320L
-#define ERROR_MEMBER_NOT_IN_GROUP 1321L
-#define ERROR_LAST_ADMIN 1322L
-#define ERROR_WRONG_PASSWORD 1323L
-#define ERROR_ILL_FORMED_PASSWORD 1324L
-#define ERROR_PASSWORD_RESTRICTION 1325L
-#define ERROR_LOGON_FAILURE 1326L
-#define ERROR_ACCOUNT_RESTRICTION 1327L
-#define ERROR_INVALID_LOGON_HOURS 1328L
-#define ERROR_INVALID_WORKSTATION 1329L
-#define ERROR_PASSWORD_EXPIRED 1330L
-#define ERROR_ACCOUNT_DISABLED 1331L
-#define ERROR_NONE_MAPPED 1332L
-#define ERROR_TOO_MANY_LUIDS_REQUESTED 1333L
-#define ERROR_LUIDS_EXHAUSTED 1334L
-#define ERROR_INVALID_SUB_AUTHORITY 1335L
-#define ERROR_INVALID_ACL 1336L
-#define ERROR_INVALID_SID 1337L
-#define ERROR_INVALID_SECURITY_DESCR 1338L
-#define ERROR_BAD_INHERITANCE_ACL 1340L
-#define ERROR_SERVER_DISABLED 1341L
-#define ERROR_SERVER_NOT_DISABLED 1342L
-#define ERROR_INVALID_ID_AUTHORITY 1343L
-#define ERROR_ALLOTTED_SPACE_EXCEEDED 1344L
-#define ERROR_INVALID_GROUP_ATTRIBUTES 1345L
-#define ERROR_BAD_IMPERSONATION_LEVEL 1346L
-#define ERROR_CANT_OPEN_ANONYMOUS 1347L
-#define ERROR_BAD_VALIDATION_CLASS 1348L
-#define ERROR_BAD_TOKEN_TYPE 1349L
-#define ERROR_NO_SECURITY_ON_OBJECT 1350L
-#define ERROR_CANT_ACCESS_DOMAIN_INFO 1351L
-#define ERROR_INVALID_SERVER_STATE 1352L
-#define ERROR_INVALID_DOMAIN_STATE 1353L
-#define ERROR_INVALID_DOMAIN_ROLE 1354L
-#define ERROR_NO_SUCH_DOMAIN 1355L
-#define ERROR_DOMAIN_EXISTS 1356L
-#define ERROR_DOMAIN_LIMIT_EXCEEDED 1357L
-#define ERROR_INTERNAL_DB_CORRUPTION 1358L
-#define ERROR_INTERNAL_ERROR 1359L
-#define ERROR_GENERIC_NOT_MAPPED 1360L
-#define ERROR_BAD_DESCRIPTOR_FORMAT 1361L
-#define ERROR_NOT_LOGON_PROCESS 1362L
-#define ERROR_LOGON_SESSION_EXISTS 1363L
-#define ERROR_NO_SUCH_PACKAGE 1364L
-#define ERROR_BAD_LOGON_SESSION_STATE 1365L
-#define ERROR_LOGON_SESSION_COLLISION 1366L
-#define ERROR_INVALID_LOGON_TYPE 1367L
-#define ERROR_CANNOT_IMPERSONATE 1368L
-#define ERROR_RXACT_INVALID_STATE 1369L
-#define ERROR_RXACT_COMMIT_FAILURE 1370L
-#define ERROR_SPECIAL_ACCOUNT 1371L
-#define ERROR_SPECIAL_GROUP 1372L
-#define ERROR_SPECIAL_USER 1373L
-#define ERROR_MEMBERS_PRIMARY_GROUP 1374L
-#define ERROR_TOKEN_ALREADY_IN_USE 1375L
-#define ERROR_NO_SUCH_ALIAS 1376L
-#define ERROR_MEMBER_NOT_IN_ALIAS 1377L
-#define ERROR_MEMBER_IN_ALIAS 1378L
-#define ERROR_ALIAS_EXISTS 1379L
-#define ERROR_LOGON_NOT_GRANTED 1380L
-#define ERROR_TOO_MANY_SECRETS 1381L
-#define ERROR_SECRET_TOO_LONG 1382L
-#define ERROR_INTERNAL_DB_ERROR 1383L
-#define ERROR_TOO_MANY_CONTEXT_IDS 1384L
-#define ERROR_LOGON_TYPE_NOT_GRANTED 1385L
-#define ERROR_NT_CROSS_ENCRYPTION_REQUIRED 1386L
-#define ERROR_NO_SUCH_MEMBER 1387L
-#define ERROR_INVALID_MEMBER 1388L
-#define ERROR_TOO_MANY_SIDS 1389L
-#define ERROR_LM_CROSS_ENCRYPTION_REQUIRED 1390L
-#define ERROR_NO_INHERITANCE 1391L
-#define ERROR_FILE_CORRUPT 1392L
-#define ERROR_DISK_CORRUPT 1393L
-#define ERROR_NO_USER_SESSION_KEY 1394L
-#define ERROR_LICENSE_QUOTA_EXCEEDED 1395L
-#define ERROR_WRONG_TARGET_NAME 1396L
-#define ERROR_MUTUAL_AUTH_FAILED 1397L
-#define ERROR_TIME_SKEW 1398L
-#define ERROR_CURRENT_DOMAIN_NOT_ALLOWED 1399L
-#define ERROR_INVALID_WINDOW_HANDLE 1400L
-#define ERROR_INVALID_MENU_HANDLE 1401L
-#define ERROR_INVALID_CURSOR_HANDLE 1402L
-#define ERROR_INVALID_ACCEL_HANDLE 1403L
-#define ERROR_INVALID_HOOK_HANDLE 1404L
-#define ERROR_INVALID_DWP_HANDLE 1405L
-#define ERROR_TLW_WITH_WSCHILD 1406L
-#define ERROR_CANNOT_FIND_WND_CLASS 1407L
-#define ERROR_WINDOW_OF_OTHER_THREAD 1408L
-#define ERROR_HOTKEY_ALREADY_REGISTERED 1409L
-#define ERROR_CLASS_ALREADY_EXISTS 1410L
-#define ERROR_CLASS_DOES_NOT_EXIST 1411L
-#define ERROR_CLASS_HAS_WINDOWS 1412L
-#define ERROR_INVALID_INDEX 1413L
-#define ERROR_INVALID_ICON_HANDLE 1414L
-#define ERROR_PRIVATE_DIALOG_INDEX 1415L
-#define ERROR_LISTBOX_ID_NOT_FOUND 1416L
-#define ERROR_NO_WILDCARD_CHARACTERS 1417L
-#define ERROR_CLIPBOARD_NOT_OPEN 1418L
-#define ERROR_HOTKEY_NOT_REGISTERED 1419L
-#define ERROR_WINDOW_NOT_DIALOG 1420L
-#define ERROR_CONTROL_ID_NOT_FOUND 1421L
-#define ERROR_INVALID_COMBOBOX_MESSAGE 1422L
-#define ERROR_WINDOW_NOT_COMBOBOX 1423L
-#define ERROR_INVALID_EDIT_HEIGHT 1424L
-#define ERROR_DC_NOT_FOUND 1425L
-#define ERROR_INVALID_HOOK_FILTER 1426L
-#define ERROR_INVALID_FILTER_PROC 1427L
-#define ERROR_HOOK_NEEDS_HMOD 1428L
-#define ERROR_GLOBAL_ONLY_HOOK 1429L
-#define ERROR_JOURNAL_HOOK_SET 1430L
-#define ERROR_HOOK_NOT_INSTALLED 1431L
-#define ERROR_INVALID_LB_MESSAGE 1432L
-#define ERROR_SETCOUNT_ON_BAD_LB 1433L
-#define ERROR_LB_WITHOUT_TABSTOPS 1434L
-#define ERROR_DESTROY_OBJECT_OF_OTHER_THREAD 1435L
-#define ERROR_CHILD_WINDOW_MENU 1436L
-#define ERROR_NO_SYSTEM_MENU 1437L
-#define ERROR_INVALID_MSGBOX_STYLE 1438L
-#define ERROR_INVALID_SPI_VALUE 1439L
-#define ERROR_SCREEN_ALREADY_LOCKED 1440L
-#define ERROR_HWNDS_HAVE_DIFF_PARENT 1441L
-#define ERROR_NOT_CHILD_WINDOW 1442L
-#define ERROR_INVALID_GW_COMMAND 1443L
-#define ERROR_INVALID_THREAD_ID 1444L
-#define ERROR_NON_MDICHILD_WINDOW 1445L
-#define ERROR_POPUP_ALREADY_ACTIVE 1446L
-#define ERROR_NO_SCROLLBARS 1447L
-#define ERROR_INVALID_SCROLLBAR_RANGE 1448L
-#define ERROR_INVALID_SHOWWIN_COMMAND 1449L
-#define ERROR_NO_SYSTEM_RESOURCES 1450L
-#define ERROR_NONPAGED_SYSTEM_RESOURCES 1451L
-#define ERROR_PAGED_SYSTEM_RESOURCES 1452L
-#define ERROR_WORKING_SET_QUOTA 1453L
-#define ERROR_PAGEFILE_QUOTA 1454L
-#define ERROR_COMMITMENT_LIMIT 1455L
-#define ERROR_MENU_ITEM_NOT_FOUND 1456L
-#define ERROR_INVALID_KEYBOARD_HANDLE 1457L
-#define ERROR_HOOK_TYPE_NOT_ALLOWED 1458L
-#define ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION 1459L
-#define ERROR_TIMEOUT 1460L
-#define ERROR_INVALID_MONITOR_HANDLE 1461L
-#define ERROR_INCORRECT_SIZE 1462L
-#define ERROR_EVENTLOG_FILE_CORRUPT 1500L
-#define ERROR_EVENTLOG_CANT_START 1501L
-#define ERROR_LOG_FILE_FULL 1502L
-#define ERROR_EVENTLOG_FILE_CHANGED 1503L
-#define ERROR_INSTALL_SERVICE_FAILURE 1601L
-#define ERROR_INSTALL_USEREXIT 1602L
-#define ERROR_INSTALL_FAILURE 1603L
-#define ERROR_INSTALL_SUSPEND 1604L
-#define ERROR_UNKNOWN_PRODUCT 1605L
-#define ERROR_UNKNOWN_FEATURE 1606L
-#define ERROR_UNKNOWN_COMPONENT 1607L
-#define ERROR_UNKNOWN_PROPERTY 1608L
-#define ERROR_INVALID_HANDLE_STATE 1609L
-#define ERROR_BAD_CONFIGURATION 1610L
-#define ERROR_INDEX_ABSENT 1611L
-#define ERROR_INSTALL_SOURCE_ABSENT 1612L
-#define ERROR_INSTALL_PACKAGE_VERSION 1613L
-#define ERROR_PRODUCT_UNINSTALLED 1614L
-#define ERROR_BAD_QUERY_SYNTAX 1615L
-#define ERROR_INVALID_FIELD 1616L
-#define ERROR_DEVICE_REMOVED 1617L
-#define ERROR_INSTALL_ALREADY_RUNNING 1618L
-#define ERROR_INSTALL_PACKAGE_OPEN_FAILED 1619L
-#define ERROR_INSTALL_PACKAGE_INVALID 1620L
-#define ERROR_INSTALL_UI_FAILURE 1621L
-#define ERROR_INSTALL_LOG_FAILURE 1622L
-#define ERROR_INSTALL_LANGUAGE_UNSUPPORTED 1623L
-#define ERROR_INSTALL_TRANSFORM_FAILURE 1624L
-#define ERROR_INSTALL_PACKAGE_REJECTED 1625L
-#define ERROR_FUNCTION_NOT_CALLED 1626L
-#define ERROR_FUNCTION_FAILED 1627L
-#define ERROR_INVALID_TABLE 1628L
-#define ERROR_DATATYPE_MISMATCH 1629L
-#define ERROR_UNSUPPORTED_TYPE 1630L
-#define ERROR_CREATE_FAILED 1631L
-#define ERROR_INSTALL_TEMP_UNWRITABLE 1632L
-#define ERROR_INSTALL_PLATFORM_UNSUPPORTED 1633L
-#define ERROR_INSTALL_NOTUSED 1634L
-#define ERROR_PATCH_PACKAGE_OPEN_FAILED 1635L
-#define ERROR_PATCH_PACKAGE_INVALID 1636L
-#define ERROR_PATCH_PACKAGE_UNSUPPORTED 1637L
-#define ERROR_PRODUCT_VERSION 1638L
-#define ERROR_INVALID_COMMAND_LINE 1639L
-#define ERROR_INSTALL_REMOTE_DISALLOWED 1640L
-#define ERROR_SUCCESS_REBOOT_INITIATED 1641L
-#define ERROR_PATCH_TARGET_NOT_FOUND 1642L
-#define ERROR_PATCH_PACKAGE_REJECTED 1643L
-#define ERROR_INSTALL_TRANSFORM_REJECTED 1644L
-#define ERROR_INSTALL_REMOTE_PROHIBITED 1645L
-#define RPC_S_INVALID_STRING_BINDING 1700L
-#define RPC_S_WRONG_KIND_OF_BINDING 1701L
-#define RPC_S_INVALID_BINDING 1702L
-#define RPC_S_PROTSEQ_NOT_SUPPORTED 1703L
-#define RPC_S_INVALID_RPC_PROTSEQ 1704L
-#define RPC_S_INVALID_STRING_UUID 1705L
-#define RPC_S_INVALID_ENDPOINT_FORMAT 1706L
-#define RPC_S_INVALID_NET_ADDR 1707L
-#define RPC_S_NO_ENDPOINT_FOUND 1708L
-#define RPC_S_INVALID_TIMEOUT 1709L
-#define RPC_S_OBJECT_NOT_FOUND 1710L
-#define RPC_S_ALREADY_REGISTERED 1711L
-#define RPC_S_TYPE_ALREADY_REGISTERED 1712L
-#define RPC_S_ALREADY_LISTENING 1713L
-#define RPC_S_NO_PROTSEQS_REGISTERED 1714L
-#define RPC_S_NOT_LISTENING 1715L
-#define RPC_S_UNKNOWN_MGR_TYPE 1716L
-#define RPC_S_UNKNOWN_IF 1717L
-#define RPC_S_NO_BINDINGS 1718L
-#define RPC_S_NO_PROTSEQS 1719L
-#define RPC_S_CANT_CREATE_ENDPOINT 1720L
-#define RPC_S_OUT_OF_RESOURCES 1721L
-#define RPC_S_SERVER_UNAVAILABLE 1722L
-#define RPC_S_SERVER_TOO_BUSY 1723L
-#define RPC_S_INVALID_NETWORK_OPTIONS 1724L
-#define RPC_S_NO_CALL_ACTIVE 1725L
-#define RPC_S_CALL_FAILED 1726L
-#define RPC_S_CALL_FAILED_DNE 1727L
-#define RPC_S_PROTOCOL_ERROR 1728L
-#define RPC_S_UNSUPPORTED_TRANS_SYN 1730L
-#define RPC_S_UNSUPPORTED_TYPE 1732L
-#define RPC_S_INVALID_TAG 1733L
-#define RPC_S_INVALID_BOUND 1734L
-#define RPC_S_NO_ENTRY_NAME 1735L
-#define RPC_S_INVALID_NAME_SYNTAX 1736L
-#define RPC_S_UNSUPPORTED_NAME_SYNTAX 1737L
-#define RPC_S_UUID_NO_ADDRESS 1739L
-#define RPC_S_DUPLICATE_ENDPOINT 1740L
-#define RPC_S_UNKNOWN_AUTHN_TYPE 1741L
-#define RPC_S_MAX_CALLS_TOO_SMALL 1742L
-#define RPC_S_STRING_TOO_LONG 1743L
-#define RPC_S_PROTSEQ_NOT_FOUND 1744L
-#define RPC_S_PROCNUM_OUT_OF_RANGE 1745L
-#define RPC_S_BINDING_HAS_NO_AUTH 1746L
-#define RPC_S_UNKNOWN_AUTHN_SERVICE 1747L
-#define RPC_S_UNKNOWN_AUTHN_LEVEL 1748L
-#define RPC_S_INVALID_AUTH_IDENTITY 1749L
-#define RPC_S_UNKNOWN_AUTHZ_SERVICE 1750L
-#define EPT_S_INVALID_ENTRY 1751L
-#define EPT_S_CANT_PERFORM_OP 1752L
-#define EPT_S_NOT_REGISTERED 1753L
-#define RPC_S_NOTHING_TO_EXPORT 1754L
-#define RPC_S_INCOMPLETE_NAME 1755L
-#define RPC_S_INVALID_VERS_OPTION 1756L
-#define RPC_S_NO_MORE_MEMBERS 1757L
-#define RPC_S_NOT_ALL_OBJS_UNEXPORTED 1758L
-#define RPC_S_INTERFACE_NOT_FOUND 1759L
-#define RPC_S_ENTRY_ALREADY_EXISTS 1760L
-#define RPC_S_ENTRY_NOT_FOUND 1761L
-#define RPC_S_NAME_SERVICE_UNAVAILABLE 1762L
-#define RPC_S_INVALID_NAF_ID 1763L
-#define RPC_S_CANNOT_SUPPORT 1764L
-#define RPC_S_NO_CONTEXT_AVAILABLE 1765L
-#define RPC_S_INTERNAL_ERROR 1766L
-#define RPC_S_ZERO_DIVIDE 1767L
-#define RPC_S_ADDRESS_ERROR 1768L
-#define RPC_S_FP_DIV_ZERO 1769L
-#define RPC_S_FP_UNDERFLOW 1770L
-#define RPC_S_FP_OVERFLOW 1771L
-#define RPC_X_NO_MORE_ENTRIES 1772L
-#define RPC_X_SS_CHAR_TRANS_OPEN_FAIL 1773L
-#define RPC_X_SS_CHAR_TRANS_SHORT_FILE 1774L
-#define RPC_X_SS_IN_NULL_CONTEXT 1775L
-#define RPC_X_SS_CONTEXT_DAMAGED 1777L
-#define RPC_X_SS_HANDLES_MISMATCH 1778L
-#define RPC_X_SS_CANNOT_GET_CALL_HANDLE 1779L
-#define RPC_X_NULL_REF_POINTER 1780L
-#define RPC_X_ENUM_VALUE_OUT_OF_RANGE 1781L
-#define RPC_X_BYTE_COUNT_TOO_SMALL 1782L
-#define RPC_X_BAD_STUB_DATA 1783L
-#define ERROR_INVALID_USER_BUFFER 1784L
-#define ERROR_UNRECOGNIZED_MEDIA 1785L
-#define ERROR_NO_TRUST_LSA_SECRET 1786L
-#define ERROR_NO_TRUST_SAM_ACCOUNT 1787L
-#define ERROR_TRUSTED_DOMAIN_FAILURE 1788L
-#define ERROR_TRUSTED_RELATIONSHIP_FAILURE 1789L
-#define ERROR_TRUST_FAILURE 1790L
-#define RPC_S_CALL_IN_PROGRESS 1791L
-#define ERROR_NETLOGON_NOT_STARTED 1792L
-#define ERROR_ACCOUNT_EXPIRED 1793L
-#define ERROR_REDIRECTOR_HAS_OPEN_HANDLES 1794L
-#define ERROR_PRINTER_DRIVER_ALREADY_INSTALLED 1795L
-#define ERROR_UNKNOWN_PORT 1796L
-#define ERROR_UNKNOWN_PRINTER_DRIVER 1797L
-#define ERROR_UNKNOWN_PRINTPROCESSOR 1798L
-#define ERROR_INVALID_SEPARATOR_FILE 1799L
-#define ERROR_INVALID_PRIORITY 1800L
-#define ERROR_INVALID_PRINTER_NAME 1801L
-#define ERROR_PRINTER_ALREADY_EXISTS 1802L
-#define ERROR_INVALID_PRINTER_COMMAND 1803L
-#define ERROR_INVALID_DATATYPE 1804L
-#define ERROR_INVALID_ENVIRONMENT 1805L
-#define RPC_S_NO_MORE_BINDINGS 1806L
-#define ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT 1807L
-#define ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT 1808L
-#define ERROR_NOLOGON_SERVER_TRUST_ACCOUNT 1809L
-#define ERROR_DOMAIN_TRUST_INCONSISTENT 1810L
-#define ERROR_SERVER_HAS_OPEN_HANDLES 1811L
-#define ERROR_RESOURCE_DATA_NOT_FOUND 1812L
-#define ERROR_RESOURCE_TYPE_NOT_FOUND 1813L
-#define ERROR_RESOURCE_NAME_NOT_FOUND 1814L
-#define ERROR_RESOURCE_LANG_NOT_FOUND 1815L
-#define ERROR_NOT_ENOUGH_QUOTA 1816L
-#define RPC_S_NO_INTERFACES 1817L
-#define RPC_S_CALL_CANCELLED 1818L
-#define RPC_S_BINDING_INCOMPLETE 1819L
-#define RPC_S_COMM_FAILURE 1820L
-#define RPC_S_UNSUPPORTED_AUTHN_LEVEL 1821L
-#define RPC_S_NO_PRINC_NAME 1822L
-#define RPC_S_NOT_RPC_ERROR 1823L
-#define RPC_S_UUID_LOCAL_ONLY 1824L
-#define RPC_S_SEC_PKG_ERROR 1825L
-#define RPC_S_NOT_CANCELLED 1826L
-#define RPC_X_INVALID_ES_ACTION 1827L
-#define RPC_X_WRONG_ES_VERSION 1828L
-#define RPC_X_WRONG_STUB_VERSION 1829L
-#define RPC_X_INVALID_PIPE_OBJECT 1830L
-#define RPC_X_WRONG_PIPE_ORDER 1831L
-#define RPC_X_WRONG_PIPE_VERSION 1832L
-#define RPC_S_GROUP_MEMBER_NOT_FOUND 1898L
-#define EPT_S_CANT_CREATE 1899L
-#define RPC_S_INVALID_OBJECT 1900L
-#define ERROR_INVALID_TIME 1901L
-#define ERROR_INVALID_FORM_NAME 1902L
-#define ERROR_INVALID_FORM_SIZE 1903L
-#define ERROR_ALREADY_WAITING 1904L
-#define ERROR_PRINTER_DELETED 1905L
-#define ERROR_INVALID_PRINTER_STATE 1906L
-#define ERROR_PASSWORD_MUST_CHANGE 1907L
-#define ERROR_DOMAIN_CONTROLLER_NOT_FOUND 1908L
-#define ERROR_ACCOUNT_LOCKED_OUT 1909L
-#define OR_INVALID_OXID 1910L
-#define OR_INVALID_OID 1911L
-#define OR_INVALID_SET 1912L
-#define RPC_S_SEND_INCOMPLETE 1913L
-#define RPC_S_INVALID_ASYNC_HANDLE 1914L
-#define RPC_S_INVALID_ASYNC_CALL 1915L
-#define RPC_X_PIPE_CLOSED 1916L
-#define RPC_X_PIPE_DISCIPLINE_ERROR 1917L
-#define RPC_X_PIPE_EMPTY 1918L
-#define ERROR_NO_SITENAME 1919L
-#define ERROR_CANT_ACCESS_FILE 1920L
-#define ERROR_CANT_RESOLVE_FILENAME 1921L
-#define RPC_S_ENTRY_TYPE_MISMATCH 1922L
-#define RPC_S_NOT_ALL_OBJS_EXPORTED 1923L
-#define RPC_S_INTERFACE_NOT_EXPORTED 1924L
-#define RPC_S_PROFILE_NOT_ADDED 1925L
-#define RPC_S_PRF_ELT_NOT_ADDED 1926L
-#define RPC_S_PRF_ELT_NOT_REMOVED 1927L
-#define RPC_S_GRP_ELT_NOT_ADDED 1928L
-#define RPC_S_GRP_ELT_NOT_REMOVED 1929L
-#define ERROR_KM_DRIVER_BLOCKED 1930L
-#define ERROR_CONTEXT_EXPIRED 1931L
-#define ERROR_PER_USER_TRUST_QUOTA_EXCEEDED 1932L
-#define ERROR_ALL_USER_TRUST_QUOTA_EXCEEDED 1933L
-#define ERROR_USER_DELETE_TRUST_QUOTA_EXCEEDED 1934L
-#define ERROR_AUTHENTICATION_FIREWALL_FAILED 1935L
-#define ERROR_REMOTE_PRINT_CONNECTIONS_BLOCKED 1936L
-#define ERROR_INVALID_PIXEL_FORMAT 2000L
-#define ERROR_BAD_DRIVER 2001L
-#define ERROR_INVALID_WINDOW_STYLE 2002L
-#define ERROR_METAFILE_NOT_SUPPORTED 2003L
-#define ERROR_TRANSFORM_NOT_SUPPORTED 2004L
-#define ERROR_CLIPPING_NOT_SUPPORTED 2005L
-#define ERROR_INVALID_CMM 2010L
-#define ERROR_INVALID_PROFILE 2011L
-#define ERROR_TAG_NOT_FOUND 2012L
-#define ERROR_TAG_NOT_PRESENT 2013L
-#define ERROR_DUPLICATE_TAG 2014L
-#define ERROR_PROFILE_NOT_ASSOCIATED_WITH_DEVICE 2015L
-#define ERROR_PROFILE_NOT_FOUND 2016L
-#define ERROR_INVALID_COLORSPACE 2017L
-#define ERROR_ICM_NOT_ENABLED 2018L
-#define ERROR_DELETING_ICM_XFORM 2019L
-#define ERROR_INVALID_TRANSFORM 2020L
-#define ERROR_COLORSPACE_MISMATCH 2021L
-#define ERROR_INVALID_COLORINDEX 2022L
-#define ERROR_CONNECTED_OTHER_PASSWORD 2108L
-#define ERROR_CONNECTED_OTHER_PASSWORD_DEFAULT 2109L
-#define ERROR_BAD_USERNAME 2202L
-#define ERROR_NOT_CONNECTED 2250L
-#define ERROR_OPEN_FILES 2401L
-#define ERROR_ACTIVE_CONNECTIONS 2402L
-#define ERROR_DEVICE_IN_USE 2404L
-#define ERROR_UNKNOWN_PRINT_MONITOR 3000L
-#define ERROR_PRINTER_DRIVER_IN_USE 3001L
-#define ERROR_SPOOL_FILE_NOT_FOUND 3002L
-#define ERROR_SPL_NO_STARTDOC 3003L
-#define ERROR_SPL_NO_ADDJOB 3004L
-#define ERROR_PRINT_PROCESSOR_ALREADY_INSTALLED 3005L
-#define ERROR_PRINT_MONITOR_ALREADY_INSTALLED 3006L
-#define ERROR_INVALID_PRINT_MONITOR 3007L
-#define ERROR_PRINT_MONITOR_IN_USE 3008L
-#define ERROR_PRINTER_HAS_JOBS_QUEUED 3009L
-#define ERROR_SUCCESS_REBOOT_REQUIRED 3010L
-#define ERROR_SUCCESS_RESTART_REQUIRED 3011L
-#define ERROR_PRINTER_NOT_FOUND 3012L
-#define ERROR_PRINTER_DRIVER_WARNED 3013L
-#define ERROR_PRINTER_DRIVER_BLOCKED 3014L
-#define ERROR_WINS_INTERNAL 4000L
-#define ERROR_CAN_NOT_DEL_LOCAL_WINS 4001L
-#define ERROR_STATIC_INIT 4002L
-#define ERROR_INC_BACKUP 4003L
-#define ERROR_FULL_BACKUP 4004L
-#define ERROR_REC_NON_EXISTENT 4005L
-#define ERROR_RPL_NOT_ALLOWED 4006L
-#define ERROR_DHCP_ADDRESS_CONFLICT 4100L
-#define ERROR_WMI_GUID_NOT_FOUND 4200L
-#define ERROR_WMI_INSTANCE_NOT_FOUND 4201L
-#define ERROR_WMI_ITEMID_NOT_FOUND 4202L
-#define ERROR_WMI_TRY_AGAIN 4203L
-#define ERROR_WMI_DP_NOT_FOUND 4204L
-#define ERROR_WMI_UNRESOLVED_INSTANCE_REF 4205L
-#define ERROR_WMI_ALREADY_ENABLED 4206L
-#define ERROR_WMI_GUID_DISCONNECTED 4207L
-#define ERROR_WMI_SERVER_UNAVAILABLE 4208L
-#define ERROR_WMI_DP_FAILED 4209L
-#define ERROR_WMI_INVALID_MOF 4210L
-#define ERROR_WMI_INVALID_REGINFO 4211L
-#define ERROR_WMI_ALREADY_DISABLED 4212L
-#define ERROR_WMI_READ_ONLY 4213L
-#define ERROR_WMI_SET_FAILURE 4214L
-#define ERROR_INVALID_MEDIA 4300L
-#define ERROR_INVALID_LIBRARY 4301L
-#define ERROR_INVALID_MEDIA_POOL 4302L
-#define ERROR_DRIVE_MEDIA_MISMATCH 4303L
-#define ERROR_MEDIA_OFFLINE 4304L
-#define ERROR_LIBRARY_OFFLINE 4305L
-#define ERROR_EMPTY 4306L
-#define ERROR_NOT_EMPTY 4307L
-#define ERROR_MEDIA_UNAVAILABLE 4308L
-#define ERROR_RESOURCE_DISABLED 4309L
-#define ERROR_INVALID_CLEANER 4310L
-#define ERROR_UNABLE_TO_CLEAN 4311L
-#define ERROR_OBJECT_NOT_FOUND 4312L
-#define ERROR_DATABASE_FAILURE 4313L
-#define ERROR_DATABASE_FULL 4314L
-#define ERROR_MEDIA_INCOMPATIBLE 4315L
-#define ERROR_RESOURCE_NOT_PRESENT 4316L
-#define ERROR_INVALID_OPERATION 4317L
-#define ERROR_MEDIA_NOT_AVAILABLE 4318L
-#define ERROR_DEVICE_NOT_AVAILABLE 4319L
-#define ERROR_REQUEST_REFUSED 4320L
-#define ERROR_INVALID_DRIVE_OBJECT 4321L
-#define ERROR_LIBRARY_FULL 4322L
-#define ERROR_MEDIUM_NOT_ACCESSIBLE 4323L
-#define ERROR_UNABLE_TO_LOAD_MEDIUM 4324L
-#define ERROR_UNABLE_TO_INVENTORY_DRIVE 4325L
-#define ERROR_UNABLE_TO_INVENTORY_SLOT 4326L
-#define ERROR_UNABLE_TO_INVENTORY_TRANSPORT 4327L
-#define ERROR_TRANSPORT_FULL 4328L
-#define ERROR_CONTROLLING_IEPORT 4329L
-#define ERROR_UNABLE_TO_EJECT_MOUNTED_MEDIA 4330L
-#define ERROR_CLEANER_SLOT_SET 4331L
-#define ERROR_CLEANER_SLOT_NOT_SET 4332L
-#define ERROR_CLEANER_CARTRIDGE_SPENT 4333L
-#define ERROR_UNEXPECTED_OMID 4334L
-#define ERROR_CANT_DELETE_LAST_ITEM 4335L
-#define ERROR_MESSAGE_EXCEEDS_MAX_SIZE 4336L
-#define ERROR_VOLUME_CONTAINS_SYS_FILES 4337L
-#define ERROR_INDIGENOUS_TYPE 4338L
-#define ERROR_NO_SUPPORTING_DRIVES 4339L
-#define ERROR_CLEANER_CARTRIDGE_INSTALLED 4340L
-#define ERROR_IEPORT_FULL 4341L
-#define ERROR_FILE_OFFLINE 4350L
-#define ERROR_REMOTE_STORAGE_NOT_ACTIVE 4351L
-#define ERROR_REMOTE_STORAGE_MEDIA_ERROR 4352L
-#define ERROR_NOT_A_REPARSE_POINT 4390L
-#define ERROR_REPARSE_ATTRIBUTE_CONFLICT 4391L
-#define ERROR_INVALID_REPARSE_DATA 4392L
-#define ERROR_REPARSE_TAG_INVALID 4393L
-#define ERROR_REPARSE_TAG_MISMATCH 4394L
-#define ERROR_VOLUME_NOT_SIS_ENABLED 4500L
-#define ERROR_DEPENDENT_RESOURCE_EXISTS 5001L
-#define ERROR_DEPENDENCY_NOT_FOUND 5002L
-#define ERROR_DEPENDENCY_ALREADY_EXISTS 5003L
-#define ERROR_RESOURCE_NOT_ONLINE 5004L
-#define ERROR_HOST_NODE_NOT_AVAILABLE 5005L
-#define ERROR_RESOURCE_NOT_AVAILABLE 5006L
-#define ERROR_RESOURCE_NOT_FOUND 5007L
-#define ERROR_SHUTDOWN_CLUSTER 5008L
-#define ERROR_CANT_EVICT_ACTIVE_NODE 5009L
-#define ERROR_OBJECT_ALREADY_EXISTS 5010L
-#define ERROR_OBJECT_IN_LIST 5011L
-#define ERROR_GROUP_NOT_AVAILABLE 5012L
-#define ERROR_GROUP_NOT_FOUND 5013L
-#define ERROR_GROUP_NOT_ONLINE 5014L
-#define ERROR_HOST_NODE_NOT_RESOURCE_OWNER 5015L
-#define ERROR_HOST_NODE_NOT_GROUP_OWNER 5016L
-#define ERROR_RESMON_CREATE_FAILED 5017L
-#define ERROR_RESMON_ONLINE_FAILED 5018L
-#define ERROR_RESOURCE_ONLINE 5019L
-#define ERROR_QUORUM_RESOURCE 5020L
-#define ERROR_NOT_QUORUM_CAPABLE 5021L
-#define ERROR_CLUSTER_SHUTTING_DOWN 5022L
-#define ERROR_INVALID_STATE 5023L
-#define ERROR_RESOURCE_PROPERTIES_STORED 5024L
-#define ERROR_NOT_QUORUM_CLASS 5025L
-#define ERROR_CORE_RESOURCE 5026L
-#define ERROR_QUORUM_RESOURCE_ONLINE_FAILED 5027L
-#define ERROR_QUORUMLOG_OPEN_FAILED 5028L
-#define ERROR_CLUSTERLOG_CORRUPT 5029L
-#define ERROR_CLUSTERLOG_RECORD_EXCEEDS_MAXSIZE 5030L
-#define ERROR_CLUSTERLOG_EXCEEDS_MAXSIZE 5031L
-#define ERROR_CLUSTERLOG_CHKPOINT_NOT_FOUND 5032L
-#define ERROR_CLUSTERLOG_NOT_ENOUGH_SPACE 5033L
-#define ERROR_QUORUM_OWNER_ALIVE 5034L
-#define ERROR_NETWORK_NOT_AVAILABLE 5035L
-#define ERROR_NODE_NOT_AVAILABLE 5036L
-#define ERROR_ALL_NODES_NOT_AVAILABLE 5037L
-#define ERROR_RESOURCE_FAILED 5038L
-#define ERROR_CLUSTER_INVALID_NODE 5039L
-#define ERROR_CLUSTER_NODE_EXISTS 5040L
-#define ERROR_CLUSTER_JOIN_IN_PROGRESS 5041L
-#define ERROR_CLUSTER_NODE_NOT_FOUND 5042L
-#define ERROR_CLUSTER_LOCAL_NODE_NOT_FOUND 5043L
-#define ERROR_CLUSTER_NETWORK_EXISTS 5044L
-#define ERROR_CLUSTER_NETWORK_NOT_FOUND 5045L
-#define ERROR_CLUSTER_NETINTERFACE_EXISTS 5046L
-#define ERROR_CLUSTER_NETINTERFACE_NOT_FOUND 5047L
-#define ERROR_CLUSTER_INVALID_REQUEST 5048L
-#define ERROR_CLUSTER_INVALID_NETWORK_PROVIDER 5049L
-#define ERROR_CLUSTER_NODE_DOWN 5050L
-#define ERROR_CLUSTER_NODE_UNREACHABLE 5051L
-#define ERROR_CLUSTER_NODE_NOT_MEMBER 5052L
-#define ERROR_CLUSTER_JOIN_NOT_IN_PROGRESS 5053L
-#define ERROR_CLUSTER_INVALID_NETWORK 5054L
-#define ERROR_CLUSTER_NODE_UP 5056L
-#define ERROR_CLUSTER_IPADDR_IN_USE 5057L
-#define ERROR_CLUSTER_NODE_NOT_PAUSED 5058L
-#define ERROR_CLUSTER_NO_SECURITY_CONTEXT 5059L
-#define ERROR_CLUSTER_NETWORK_NOT_INTERNAL 5060L
-#define ERROR_CLUSTER_NODE_ALREADY_UP 5061L
-#define ERROR_CLUSTER_NODE_ALREADY_DOWN 5062L
-#define ERROR_CLUSTER_NETWORK_ALREADY_ONLINE 5063L
-#define ERROR_CLUSTER_NETWORK_ALREADY_OFFLINE 5064L
-#define ERROR_CLUSTER_NODE_ALREADY_MEMBER 5065L
-#define ERROR_CLUSTER_LAST_INTERNAL_NETWORK 5066L
-#define ERROR_CLUSTER_NETWORK_HAS_DEPENDENTS 5067L
-#define ERROR_INVALID_OPERATION_ON_QUORUM 5068L
-#define ERROR_DEPENDENCY_NOT_ALLOWED 5069L
-#define ERROR_CLUSTER_NODE_PAUSED 5070L
-#define ERROR_NODE_CANT_HOST_RESOURCE 5071L
-#define ERROR_CLUSTER_NODE_NOT_READY 5072L
-#define ERROR_CLUSTER_NODE_SHUTTING_DOWN 5073L
-#define ERROR_CLUSTER_JOIN_ABORTED 5074L
-#define ERROR_CLUSTER_INCOMPATIBLE_VERSIONS 5075L
-#define ERROR_CLUSTER_MAXNUM_OF_RESOURCES_EXCEEDED 5076L
-#define ERROR_CLUSTER_SYSTEM_CONFIG_CHANGED 5077L
-#define ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND 5078L
-#define ERROR_CLUSTER_RESTYPE_NOT_SUPPORTED 5079L
-#define ERROR_CLUSTER_RESNAME_NOT_FOUND 5080L
-#define ERROR_CLUSTER_NO_RPC_PACKAGES_REGISTERED 5081L
-#define ERROR_CLUSTER_OWNER_NOT_IN_PREFLIST 5082L
-#define ERROR_CLUSTER_DATABASE_SEQMISMATCH 5083L
-#define ERROR_RESMON_INVALID_STATE 5084L
-#define ERROR_CLUSTER_GUM_NOT_LOCKER 5085L
-#define ERROR_QUORUM_DISK_NOT_FOUND 5086L
-#define ERROR_DATABASE_BACKUP_CORRUPT 5087L
-#define ERROR_CLUSTER_NODE_ALREADY_HAS_DFS_ROOT 5088L
-#define ERROR_RESOURCE_PROPERTY_UNCHANGEABLE 5089L
-#define ERROR_CLUSTER_MEMBERSHIP_INVALID_STATE 5890L
-#define ERROR_CLUSTER_QUORUMLOG_NOT_FOUND 5891L
-#define ERROR_CLUSTER_MEMBERSHIP_HALT 5892L
-#define ERROR_CLUSTER_INSTANCE_ID_MISMATCH 5893L
-#define ERROR_CLUSTER_NETWORK_NOT_FOUND_FOR_IP 5894L
-#define ERROR_CLUSTER_PROPERTY_DATA_TYPE_MISMATCH 5895L
-#define ERROR_CLUSTER_EVICT_WITHOUT_CLEANUP 5896L
-#define ERROR_CLUSTER_PARAMETER_MISMATCH 5897L
-#define ERROR_NODE_CANNOT_BE_CLUSTERED 5898L
-#define ERROR_CLUSTER_WRONG_OS_VERSION 5899L
-#define ERROR_CLUSTER_CANT_CREATE_DUP_CLUSTER_NAME 5900L
-#define ERROR_CLUSCFG_ALREADY_COMMITTED 5901L
-#define ERROR_CLUSCFG_ROLLBACK_FAILED 5902L
-#define ERROR_CLUSCFG_SYSTEM_DISK_DRIVE_LETTER_CONFLICT 5903L
-#define ERROR_CLUSTER_OLD_VERSION 5904L
-#define ERROR_CLUSTER_MISMATCHED_COMPUTER_ACCT_NAME 5905L
-#define ERROR_ENCRYPTION_FAILED 6000L
-#define ERROR_DECRYPTION_FAILED 6001L
-#define ERROR_FILE_ENCRYPTED 6002L
-#define ERROR_NO_RECOVERY_POLICY 6003L
-#define ERROR_NO_EFS 6004L
-#define ERROR_WRONG_EFS 6005L
-#define ERROR_NO_USER_KEYS 6006L
-#define ERROR_FILE_NOT_ENCRYPTED 6007L
-#define ERROR_NOT_EXPORT_FORMAT 6008L
-#define ERROR_FILE_READ_ONLY 6009L
-#define ERROR_DIR_EFS_DISALLOWED 6010L
-#define ERROR_EFS_SERVER_NOT_TRUSTED 6011L
-#define ERROR_BAD_RECOVERY_POLICY 6012L
-#define ERROR_EFS_ALG_BLOB_TOO_BIG 6013L
-#define ERROR_VOLUME_NOT_SUPPORT_EFS 6014L
-#define ERROR_EFS_DISABLED 6015L
-#define ERROR_EFS_VERSION_NOT_SUPPORT 6016L
-#define ERROR_NO_BROWSER_SERVERS_FOUND 6118L
-#define SCHED_E_SERVICE_NOT_LOCALSYSTEM 6200L
-#define ERROR_CTX_WINSTATION_NAME_INVALID 7001L
-#define ERROR_CTX_INVALID_PD 7002L
-#define ERROR_CTX_PD_NOT_FOUND 7003L
-#define ERROR_CTX_WD_NOT_FOUND 7004L
-#define ERROR_CTX_CANNOT_MAKE_EVENTLOG_ENTRY 7005L
-#define ERROR_CTX_SERVICE_NAME_COLLISION 7006L
-#define ERROR_CTX_CLOSE_PENDING 7007L
-#define ERROR_CTX_NO_OUTBUF 7008L
-#define ERROR_CTX_MODEM_INF_NOT_FOUND 7009L
-#define ERROR_CTX_INVALID_MODEMNAME 7010L
-#define ERROR_CTX_MODEM_RESPONSE_ERROR 7011L
-#define ERROR_CTX_MODEM_RESPONSE_TIMEOUT 7012L
-#define ERROR_CTX_MODEM_RESPONSE_NO_CARRIER 7013L
-#define ERROR_CTX_MODEM_RESPONSE_NO_DIALTONE 7014L
-#define ERROR_CTX_MODEM_RESPONSE_BUSY 7015L
-#define ERROR_CTX_MODEM_RESPONSE_VOICE 7016L
-#define ERROR_CTX_TD_ERROR 7017L
-#define ERROR_CTX_WINSTATION_NOT_FOUND 7022L
-#define ERROR_CTX_WINSTATION_ALREADY_EXISTS 7023L
-#define ERROR_CTX_WINSTATION_BUSY 7024L
-#define ERROR_CTX_BAD_VIDEO_MODE 7025L
-#define ERROR_CTX_GRAPHICS_INVALID 7035L
-#define ERROR_CTX_LOGON_DISABLED 7037L
-#define ERROR_CTX_NOT_CONSOLE 7038L
-#define ERROR_CTX_CLIENT_QUERY_TIMEOUT 7040L
-#define ERROR_CTX_CONSOLE_DISCONNECT 7041L
-#define ERROR_CTX_CONSOLE_CONNECT 7042L
-#define ERROR_CTX_SHADOW_DENIED 7044L
-#define ERROR_CTX_WINSTATION_ACCESS_DENIED 7045L
-#define ERROR_CTX_INVALID_WD 7049L
-#define ERROR_CTX_SHADOW_INVALID 7050L
-#define ERROR_CTX_SHADOW_DISABLED 7051L
-#define ERROR_CTX_CLIENT_LICENSE_IN_USE 7052L
-#define ERROR_CTX_CLIENT_LICENSE_NOT_SET 7053L
-#define ERROR_CTX_LICENSE_NOT_AVAILABLE 7054L
-#define ERROR_CTX_LICENSE_CLIENT_INVALID 7055L
-#define ERROR_CTX_LICENSE_EXPIRED 7056L
-#define ERROR_CTX_SHADOW_NOT_RUNNING 7057L
-#define ERROR_CTX_SHADOW_ENDED_BY_MODE_CHANGE 7058L
-#define ERROR_ACTIVATION_COUNT_EXCEEDED 7059L
-#define FRS_ERR_INVALID_API_SEQUENCE 8001L
-#define FRS_ERR_STARTING_SERVICE 8002L
-#define FRS_ERR_STOPPING_SERVICE 8003L
-#define FRS_ERR_INTERNAL_API 8004L
-#define FRS_ERR_INTERNAL 8005L
-#define FRS_ERR_SERVICE_COMM 8006L
-#define FRS_ERR_INSUFFICIENT_PRIV 8007L
-#define FRS_ERR_AUTHENTICATION 8008L
-#define FRS_ERR_PARENT_INSUFFICIENT_PRIV 8009L
-#define FRS_ERR_PARENT_AUTHENTICATION 8010L
-#define FRS_ERR_CHILD_TO_PARENT_COMM 8011L
-#define FRS_ERR_PARENT_TO_CHILD_COMM 8012L
-#define FRS_ERR_SYSVOL_POPULATE 8013L
-#define FRS_ERR_SYSVOL_POPULATE_TIMEOUT 8014L
-#define FRS_ERR_SYSVOL_IS_BUSY 8015L
-#define FRS_ERR_SYSVOL_DEMOTE 8016L
-#define FRS_ERR_INVALID_SERVICE_PARAMETER 8017L
-#define DS_S_SUCCESS NO_ERROR
-#define ERROR_DS_NOT_INSTALLED 8200L
-#define ERROR_DS_MEMBERSHIP_EVALUATED_LOCALLY 8201L
-#define ERROR_DS_NO_ATTRIBUTE_OR_VALUE 8202L
-#define ERROR_DS_INVALID_ATTRIBUTE_SYNTAX 8203L
-#define ERROR_DS_ATTRIBUTE_TYPE_UNDEFINED 8204L
-#define ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS 8205L
-#define ERROR_DS_BUSY 8206L
-#define ERROR_DS_UNAVAILABLE 8207L
-#define ERROR_DS_NO_RIDS_ALLOCATED 8208L
-#define ERROR_DS_NO_MORE_RIDS 8209L
-#define ERROR_DS_INCORRECT_ROLE_OWNER 8210L
-#define ERROR_DS_RIDMGR_INIT_ERROR 8211L
-#define ERROR_DS_OBJ_CLASS_VIOLATION 8212L
-#define ERROR_DS_CANT_ON_NON_LEAF 8213L
-#define ERROR_DS_CANT_ON_RDN 8214L
-#define ERROR_DS_CANT_MOD_OBJ_CLASS 8215L
-#define ERROR_DS_CROSS_DOM_MOVE_ERROR 8216L
-#define ERROR_DS_GC_NOT_AVAILABLE 8217L
-#define ERROR_SHARED_POLICY 8218L
-#define ERROR_POLICY_OBJECT_NOT_FOUND 8219L
-#define ERROR_POLICY_ONLY_IN_DS 8220L
-#define ERROR_PROMOTION_ACTIVE 8221L
-#define ERROR_NO_PROMOTION_ACTIVE 8222L
-#define ERROR_DS_OPERATIONS_ERROR 8224L
-#define ERROR_DS_PROTOCOL_ERROR 8225L
-#define ERROR_DS_TIMELIMIT_EXCEEDED 8226L
-#define ERROR_DS_SIZELIMIT_EXCEEDED 8227L
-#define ERROR_DS_ADMIN_LIMIT_EXCEEDED 8228L
-#define ERROR_DS_COMPARE_FALSE 8229L
-#define ERROR_DS_COMPARE_TRUE 8230L
-#define ERROR_DS_AUTH_METHOD_NOT_SUPPORTED 8231L
-#define ERROR_DS_STRONG_AUTH_REQUIRED 8232L
-#define ERROR_DS_INAPPROPRIATE_AUTH 8233L
-#define ERROR_DS_AUTH_UNKNOWN 8234L
-#define ERROR_DS_REFERRAL 8235L
-#define ERROR_DS_UNAVAILABLE_CRIT_EXTENSION 8236L
-#define ERROR_DS_CONFIDENTIALITY_REQUIRED 8237L
-#define ERROR_DS_INAPPROPRIATE_MATCHING 8238L
-#define ERROR_DS_CONSTRAINT_VIOLATION 8239L
-#define ERROR_DS_NO_SUCH_OBJECT 8240L
-#define ERROR_DS_ALIAS_PROBLEM 8241L
-#define ERROR_DS_INVALID_DN_SYNTAX 8242L
-#define ERROR_DS_IS_LEAF 8243L
-#define ERROR_DS_ALIAS_DEREF_PROBLEM 8244L
-#define ERROR_DS_UNWILLING_TO_PERFORM 8245L
-#define ERROR_DS_LOOP_DETECT 8246L
-#define ERROR_DS_NAMING_VIOLATION 8247L
-#define ERROR_DS_OBJECT_RESULTS_TOO_LARGE 8248L
-#define ERROR_DS_AFFECTS_MULTIPLE_DSAS 8249L
-#define ERROR_DS_SERVER_DOWN 8250L
-#define ERROR_DS_LOCAL_ERROR 8251L
-#define ERROR_DS_ENCODING_ERROR 8252L
-#define ERROR_DS_DECODING_ERROR 8253L
-#define ERROR_DS_FILTER_UNKNOWN 8254L
-#define ERROR_DS_PARAM_ERROR 8255L
-#define ERROR_DS_NOT_SUPPORTED 8256L
-#define ERROR_DS_NO_RESULTS_RETURNED 8257L
-#define ERROR_DS_CONTROL_NOT_FOUND 8258L
-#define ERROR_DS_CLIENT_LOOP 8259L
-#define ERROR_DS_REFERRAL_LIMIT_EXCEEDED 8260L
-#define ERROR_DS_SORT_CONTROL_MISSING 8261L
-#define ERROR_DS_OFFSET_RANGE_ERROR 8262L
-#define ERROR_DS_ROOT_MUST_BE_NC 8301L
-#define ERROR_DS_ADD_REPLICA_INHIBITED 8302L
-#define ERROR_DS_ATT_NOT_DEF_IN_SCHEMA 8303L
-#define ERROR_DS_MAX_OBJ_SIZE_EXCEEDED 8304L
-#define ERROR_DS_OBJ_STRING_NAME_EXISTS 8305L
-#define ERROR_DS_NO_RDN_DEFINED_IN_SCHEMA 8306L
-#define ERROR_DS_RDN_DOESNT_MATCH_SCHEMA 8307L
-#define ERROR_DS_NO_REQUESTED_ATTS_FOUND 8308L
-#define ERROR_DS_USER_BUFFER_TO_SMALL 8309L
-#define ERROR_DS_ATT_IS_NOT_ON_OBJ 8310L
-#define ERROR_DS_ILLEGAL_MOD_OPERATION 8311L
-#define ERROR_DS_OBJ_TOO_LARGE 8312L
-#define ERROR_DS_BAD_INSTANCE_TYPE 8313L
-#define ERROR_DS_MASTERDSA_REQUIRED 8314L
-#define ERROR_DS_OBJECT_CLASS_REQUIRED 8315L
-#define ERROR_DS_MISSING_REQUIRED_ATT 8316L
-#define ERROR_DS_ATT_NOT_DEF_FOR_CLASS 8317L
-#define ERROR_DS_ATT_ALREADY_EXISTS 8318L
-#define ERROR_DS_CANT_ADD_ATT_VALUES 8320L
-#define ERROR_DS_SINGLE_VALUE_CONSTRAINT 8321L
-#define ERROR_DS_RANGE_CONSTRAINT 8322L
-#define ERROR_DS_ATT_VAL_ALREADY_EXISTS 8323L
-#define ERROR_DS_CANT_REM_MISSING_ATT 8324L
-#define ERROR_DS_CANT_REM_MISSING_ATT_VAL 8325L
-#define ERROR_DS_ROOT_CANT_BE_SUBREF 8326L
-#define ERROR_DS_NO_CHAINING 8327L
-#define ERROR_DS_NO_CHAINED_EVAL 8328L
-#define ERROR_DS_NO_PARENT_OBJECT 8329L
-#define ERROR_DS_PARENT_IS_AN_ALIAS 8330L
-#define ERROR_DS_CANT_MIX_MASTER_AND_REPS 8331L
-#define ERROR_DS_CHILDREN_EXIST 8332L
-#define ERROR_DS_OBJ_NOT_FOUND 8333L
-#define ERROR_DS_ALIASED_OBJ_MISSING 8334L
-#define ERROR_DS_BAD_NAME_SYNTAX 8335L
-#define ERROR_DS_ALIAS_POINTS_TO_ALIAS 8336L
-#define ERROR_DS_CANT_DEREF_ALIAS 8337L
-#define ERROR_DS_OUT_OF_SCOPE 8338L
-#define ERROR_DS_OBJECT_BEING_REMOVED 8339L
-#define ERROR_DS_CANT_DELETE_DSA_OBJ 8340L
-#define ERROR_DS_GENERIC_ERROR 8341L
-#define ERROR_DS_DSA_MUST_BE_INT_MASTER 8342L
-#define ERROR_DS_CLASS_NOT_DSA 8343L
-#define ERROR_DS_INSUFF_ACCESS_RIGHTS 8344L
-#define ERROR_DS_ILLEGAL_SUPERIOR 8345L
-#define ERROR_DS_ATTRIBUTE_OWNED_BY_SAM 8346L
-#define ERROR_DS_NAME_TOO_MANY_PARTS 8347L
-#define ERROR_DS_NAME_TOO_LONG 8348L
-#define ERROR_DS_NAME_VALUE_TOO_LONG 8349L
-#define ERROR_DS_NAME_UNPARSEABLE 8350L
-#define ERROR_DS_NAME_TYPE_UNKNOWN 8351L
-#define ERROR_DS_NOT_AN_OBJECT 8352L
-#define ERROR_DS_SEC_DESC_TOO_SHORT 8353L
-#define ERROR_DS_SEC_DESC_INVALID 8354L
-#define ERROR_DS_NO_DELETED_NAME 8355L
-#define ERROR_DS_SUBREF_MUST_HAVE_PARENT 8356L
-#define ERROR_DS_NCNAME_MUST_BE_NC 8357L
-#define ERROR_DS_CANT_ADD_SYSTEM_ONLY 8358L
-#define ERROR_DS_CLASS_MUST_BE_CONCRETE 8359L
-#define ERROR_DS_INVALID_DMD 8360L
-#define ERROR_DS_OBJ_GUID_EXISTS 8361L
-#define ERROR_DS_NOT_ON_BACKLINK 8362L
-#define ERROR_DS_NO_CROSSREF_FOR_NC 8363L
-#define ERROR_DS_SHUTTING_DOWN 8364L
-#define ERROR_DS_UNKNOWN_OPERATION 8365L
-#define ERROR_DS_INVALID_ROLE_OWNER 8366L
-#define ERROR_DS_COULDNT_CONTACT_FSMO 8367L
-#define ERROR_DS_CROSS_NC_DN_RENAME 8368L
-#define ERROR_DS_CANT_MOD_SYSTEM_ONLY 8369L
-#define ERROR_DS_REPLICATOR_ONLY 8370L
-#define ERROR_DS_OBJ_CLASS_NOT_DEFINED 8371L
-#define ERROR_DS_OBJ_CLASS_NOT_SUBCLASS 8372L
-#define ERROR_DS_NAME_REFERENCE_INVALID 8373L
-#define ERROR_DS_CROSS_REF_EXISTS 8374L
-#define ERROR_DS_CANT_DEL_MASTER_CROSSREF 8375L
-#define ERROR_DS_SUBTREE_NOTIFY_NOT_NC_HEAD 8376L
-#define ERROR_DS_NOTIFY_FILTER_TOO_COMPLEX 8377L
-#define ERROR_DS_DUP_RDN 8378L
-#define ERROR_DS_DUP_OID 8379L
-#define ERROR_DS_DUP_MAPI_ID 8380L
-#define ERROR_DS_DUP_SCHEMA_ID_GUID 8381L
-#define ERROR_DS_DUP_LDAP_DISPLAY_NAME 8382L
-#define ERROR_DS_SEMANTIC_ATT_TEST 8383L
-#define ERROR_DS_SYNTAX_MISMATCH 8384L
-#define ERROR_DS_EXISTS_IN_MUST_HAVE 8385L
-#define ERROR_DS_EXISTS_IN_MAY_HAVE 8386L
-#define ERROR_DS_NONEXISTENT_MAY_HAVE 8387L
-#define ERROR_DS_NONEXISTENT_MUST_HAVE 8388L
-#define ERROR_DS_AUX_CLS_TEST_FAIL 8389L
-#define ERROR_DS_NONEXISTENT_POSS_SUP 8390L
-#define ERROR_DS_SUB_CLS_TEST_FAIL 8391L
-#define ERROR_DS_BAD_RDN_ATT_ID_SYNTAX 8392L
-#define ERROR_DS_EXISTS_IN_AUX_CLS 8393L
-#define ERROR_DS_EXISTS_IN_SUB_CLS 8394L
-#define ERROR_DS_EXISTS_IN_POSS_SUP 8395L
-#define ERROR_DS_RECALCSCHEMA_FAILED 8396L
-#define ERROR_DS_TREE_DELETE_NOT_FINISHED 8397L
-#define ERROR_DS_CANT_DELETE 8398L
-#define ERROR_DS_ATT_SCHEMA_REQ_ID 8399L
-#define ERROR_DS_BAD_ATT_SCHEMA_SYNTAX 8400L
-#define ERROR_DS_CANT_CACHE_ATT 8401L
-#define ERROR_DS_CANT_CACHE_CLASS 8402L
-#define ERROR_DS_CANT_REMOVE_ATT_CACHE 8403L
-#define ERROR_DS_CANT_REMOVE_CLASS_CACHE 8404L
-#define ERROR_DS_CANT_RETRIEVE_DN 8405L
-#define ERROR_DS_MISSING_SUPREF 8406L
-#define ERROR_DS_CANT_RETRIEVE_INSTANCE 8407L
-#define ERROR_DS_CODE_INCONSISTENCY 8408L
-#define ERROR_DS_DATABASE_ERROR 8409L
-#define ERROR_DS_GOVERNSID_MISSING 8410L
-#define ERROR_DS_MISSING_EXPECTED_ATT 8411L
-#define ERROR_DS_NCNAME_MISSING_CR_REF 8412L
-#define ERROR_DS_SECURITY_CHECKING_ERROR 8413L
-#define ERROR_DS_SCHEMA_NOT_LOADED 8414L
-#define ERROR_DS_SCHEMA_ALLOC_FAILED 8415L
-#define ERROR_DS_ATT_SCHEMA_REQ_SYNTAX 8416L
-#define ERROR_DS_GCVERIFY_ERROR 8417L
-#define ERROR_DS_DRA_SCHEMA_MISMATCH 8418L
-#define ERROR_DS_CANT_FIND_DSA_OBJ 8419L
-#define ERROR_DS_CANT_FIND_EXPECTED_NC 8420L
-#define ERROR_DS_CANT_FIND_NC_IN_CACHE 8421L
-#define ERROR_DS_CANT_RETRIEVE_CHILD 8422L
-#define ERROR_DS_SECURITY_ILLEGAL_MODIFY 8423L
-#define ERROR_DS_CANT_REPLACE_HIDDEN_REC 8424L
-#define ERROR_DS_BAD_HIERARCHY_FILE 8425L
-#define ERROR_DS_BUILD_HIERARCHY_TABLE_FAILED 8426L
-#define ERROR_DS_CONFIG_PARAM_MISSING 8427L
-#define ERROR_DS_COUNTING_AB_INDICES_FAILED 8428L
-#define ERROR_DS_HIERARCHY_TABLE_MALLOC_FAILED 8429L
-#define ERROR_DS_INTERNAL_FAILURE 8430L
-#define ERROR_DS_UNKNOWN_ERROR 8431L
-#define ERROR_DS_ROOT_REQUIRES_CLASS_TOP 8432L
-#define ERROR_DS_REFUSING_FSMO_ROLES 8433L
-#define ERROR_DS_MISSING_FSMO_SETTINGS 8434L
-#define ERROR_DS_UNABLE_TO_SURRENDER_ROLES 8435L
-#define ERROR_DS_DRA_GENERIC 8436L
-#define ERROR_DS_DRA_INVALID_PARAMETER 8437L
-#define ERROR_DS_DRA_BUSY 8438L
-#define ERROR_DS_DRA_BAD_DN 8439L
-#define ERROR_DS_DRA_BAD_NC 8440L
-#define ERROR_DS_DRA_DN_EXISTS 8441L
-#define ERROR_DS_DRA_INTERNAL_ERROR 8442L
-#define ERROR_DS_DRA_INCONSISTENT_DIT 8443L
-#define ERROR_DS_DRA_CONNECTION_FAILED 8444L
-#define ERROR_DS_DRA_BAD_INSTANCE_TYPE 8445L
-#define ERROR_DS_DRA_OUT_OF_MEM 8446L
-#define ERROR_DS_DRA_MAIL_PROBLEM 8447L
-#define ERROR_DS_DRA_REF_ALREADY_EXISTS 8448L
-#define ERROR_DS_DRA_REF_NOT_FOUND 8449L
-#define ERROR_DS_DRA_OBJ_IS_REP_SOURCE 8450L
-#define ERROR_DS_DRA_DB_ERROR 8451L
-#define ERROR_DS_DRA_NO_REPLICA 8452L
-#define ERROR_DS_DRA_ACCESS_DENIED 8453L
-#define ERROR_DS_DRA_NOT_SUPPORTED 8454L
-#define ERROR_DS_DRA_RPC_CANCELLED 8455L
-#define ERROR_DS_DRA_SOURCE_DISABLED 8456L
-#define ERROR_DS_DRA_SINK_DISABLED 8457L
-#define ERROR_DS_DRA_NAME_COLLISION 8458L
-#define ERROR_DS_DRA_SOURCE_REINSTALLED 8459L
-#define ERROR_DS_DRA_MISSING_PARENT 8460L
-#define ERROR_DS_DRA_PREEMPTED 8461L
-#define ERROR_DS_DRA_ABANDON_SYNC 8462L
-#define ERROR_DS_DRA_SHUTDOWN 8463L
-#define ERROR_DS_DRA_INCOMPATIBLE_PARTIAL_SET 8464L
-#define ERROR_DS_DRA_SOURCE_IS_PARTIAL_REPLICA 8465L
-#define ERROR_DS_DRA_EXTN_CONNECTION_FAILED 8466L
-#define ERROR_DS_INSTALL_SCHEMA_MISMATCH 8467L
-#define ERROR_DS_DUP_LINK_ID 8468L
-#define ERROR_DS_NAME_ERROR_RESOLVING 8469L
-#define ERROR_DS_NAME_ERROR_NOT_FOUND 8470L
-#define ERROR_DS_NAME_ERROR_NOT_UNIQUE 8471L
-#define ERROR_DS_NAME_ERROR_NO_MAPPING 8472L
-#define ERROR_DS_NAME_ERROR_DOMAIN_ONLY 8473L
-#define ERROR_DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING 8474L
-#define ERROR_DS_CONSTRUCTED_ATT_MOD 8475L
-#define ERROR_DS_WRONG_OM_OBJ_CLASS 8476L
-#define ERROR_DS_DRA_REPL_PENDING 8477L
-#define ERROR_DS_DS_REQUIRED 8478L
-#define ERROR_DS_INVALID_LDAP_DISPLAY_NAME 8479L
-#define ERROR_DS_NON_BASE_SEARCH 8480L
-#define ERROR_DS_CANT_RETRIEVE_ATTS 8481L
-#define ERROR_DS_BACKLINK_WITHOUT_LINK 8482L
-#define ERROR_DS_EPOCH_MISMATCH 8483L
-#define ERROR_DS_SRC_NAME_MISMATCH 8484L
-#define ERROR_DS_SRC_AND_DST_NC_IDENTICAL 8485L
-#define ERROR_DS_DST_NC_MISMATCH 8486L
-#define ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC 8487L
-#define ERROR_DS_SRC_GUID_MISMATCH 8488L
-#define ERROR_DS_CANT_MOVE_DELETED_OBJECT 8489L
-#define ERROR_DS_PDC_OPERATION_IN_PROGRESS 8490L
-#define ERROR_DS_CROSS_DOMAIN_CLEANUP_REQD 8491L
-#define ERROR_DS_ILLEGAL_XDOM_MOVE_OPERATION 8492L
-#define ERROR_DS_CANT_WITH_ACCT_GROUP_MEMBERSHPS 8493L
-#define ERROR_DS_NC_MUST_HAVE_NC_PARENT 8494L
-#define ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE 8495L
-#define ERROR_DS_DST_DOMAIN_NOT_NATIVE 8496L
-#define ERROR_DS_MISSING_INFRASTRUCTURE_CONTAINER 8497L
-#define ERROR_DS_CANT_MOVE_ACCOUNT_GROUP 8498L
-#define ERROR_DS_CANT_MOVE_RESOURCE_GROUP 8499L
-#define ERROR_DS_INVALID_SEARCH_FLAG 8500L
-#define ERROR_DS_NO_TREE_DELETE_ABOVE_NC 8501L
-#define ERROR_DS_COULDNT_LOCK_TREE_FOR_DELETE 8502L
-#define ERROR_DS_COULDNT_IDENTIFY_OBJECTS_FOR_TREE_DELETE 8503L
-#define ERROR_DS_SAM_INIT_FAILURE 8504L
-#define ERROR_DS_SENSITIVE_GROUP_VIOLATION 8505L
-#define ERROR_DS_CANT_MOD_PRIMARYGROUPID 8506L
-#define ERROR_DS_ILLEGAL_BASE_SCHEMA_MOD 8507L
-#define ERROR_DS_NONSAFE_SCHEMA_CHANGE 8508L
-#define ERROR_DS_SCHEMA_UPDATE_DISALLOWED 8509L
-#define ERROR_DS_CANT_CREATE_UNDER_SCHEMA 8510L
-#define ERROR_DS_INSTALL_NO_SRC_SCH_VERSION 8511L
-#define ERROR_DS_INSTALL_NO_SCH_VERSION_IN_INIFILE 8512L
-#define ERROR_DS_INVALID_GROUP_TYPE 8513L
-#define ERROR_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN 8514L
-#define ERROR_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN 8515L
-#define ERROR_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER 8516L
-#define ERROR_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER 8517L
-#define ERROR_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER 8518L
-#define ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER 8519L
-#define ERROR_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER 8520L
-#define ERROR_DS_HAVE_PRIMARY_MEMBERS 8521L
-#define ERROR_DS_STRING_SD_CONVERSION_FAILED 8522L
-#define ERROR_DS_NAMING_MASTER_GC 8523L
-#define ERROR_DS_DNS_LOOKUP_FAILURE 8524L
-#define ERROR_DS_COULDNT_UPDATE_SPNS 8525L
-#define ERROR_DS_CANT_RETRIEVE_SD 8526L
-#define ERROR_DS_KEY_NOT_UNIQUE 8527L
-#define ERROR_DS_WRONG_LINKED_ATT_SYNTAX 8528L
-#define ERROR_DS_SAM_NEED_BOOTKEY_PASSWORD 8529L
-#define ERROR_DS_SAM_NEED_BOOTKEY_FLOPPY 8530L
-#define ERROR_DS_CANT_START 8531L
-#define ERROR_DS_INIT_FAILURE 8532L
-#define ERROR_DS_NO_PKT_PRIVACY_ON_CONNECTION 8533L
-#define ERROR_DS_SOURCE_DOMAIN_IN_FOREST 8534L
-#define ERROR_DS_DESTINATION_DOMAIN_NOT_IN_FOREST 8535L
-#define ERROR_DS_DESTINATION_AUDITING_NOT_ENABLED 8536L
-#define ERROR_DS_CANT_FIND_DC_FOR_SRC_DOMAIN 8537L
-#define ERROR_DS_SRC_OBJ_NOT_GROUP_OR_USER 8538L
-#define ERROR_DS_SRC_SID_EXISTS_IN_FOREST 8539L
-#define ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH 8540L
-#define ERROR_SAM_INIT_FAILURE 8541L
-#define ERROR_DS_DRA_SCHEMA_INFO_SHIP 8542L
-#define ERROR_DS_DRA_SCHEMA_CONFLICT 8543L
-#define ERROR_DS_DRA_EARLIER_SCHEMA_CONFLICT 8544L
-#define ERROR_DS_DRA_OBJ_NC_MISMATCH 8545L
-#define ERROR_DS_NC_STILL_HAS_DSAS 8546L
-#define ERROR_DS_GC_REQUIRED 8547L
-#define ERROR_DS_LOCAL_MEMBER_OF_LOCAL_ONLY 8548L
-#define ERROR_DS_NO_FPO_IN_UNIVERSAL_GROUPS 8549L
-#define ERROR_DS_CANT_ADD_TO_GC 8550L
-#define ERROR_DS_NO_CHECKPOINT_WITH_PDC 8551L
-#define ERROR_DS_SOURCE_AUDITING_NOT_ENABLED 8552L
-#define ERROR_DS_CANT_CREATE_IN_NONDOMAIN_NC 8553L
-#define ERROR_DS_INVALID_NAME_FOR_SPN 8554L
-#define ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS 8555L
-#define ERROR_DS_UNICODEPWD_NOT_IN_QUOTES 8556L
-#define ERROR_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED 8557L
-#define ERROR_DS_MUST_BE_RUN_ON_DST_DC 8558L
-#define ERROR_DS_SRC_DC_MUST_BE_SP4_OR_GREATER 8559L
-#define ERROR_DS_CANT_TREE_DELETE_CRITICAL_OBJ 8560L
-#define ERROR_DS_INIT_FAILURE_CONSOLE 8561L
-#define ERROR_DS_SAM_INIT_FAILURE_CONSOLE 8562L
-#define ERROR_DS_FOREST_VERSION_TOO_HIGH 8563L
-#define ERROR_DS_DOMAIN_VERSION_TOO_HIGH 8564L
-#define ERROR_DS_FOREST_VERSION_TOO_LOW 8565L
-#define ERROR_DS_DOMAIN_VERSION_TOO_LOW 8566L
-#define ERROR_DS_INCOMPATIBLE_VERSION 8567L
-#define ERROR_DS_LOW_DSA_VERSION 8568L
-#define ERROR_DS_NO_BEHAVIOR_VERSION_IN_MIXEDDOMAIN 8569L
-#define ERROR_DS_NOT_SUPPORTED_SORT_ORDER 8570L
-#define ERROR_DS_NAME_NOT_UNIQUE 8571L
-#define ERROR_DS_MACHINE_ACCOUNT_CREATED_PRENT4 8572L
-#define ERROR_DS_OUT_OF_VERSION_STORE 8573L
-#define ERROR_DS_INCOMPATIBLE_CONTROLS_USED 8574L
-#define ERROR_DS_NO_REF_DOMAIN 8575L
-#define ERROR_DS_RESERVED_LINK_ID 8576L
-#define ERROR_DS_LINK_ID_NOT_AVAILABLE 8577L
-#define ERROR_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER 8578L
-#define ERROR_DS_MODIFYDN_DISALLOWED_BY_INSTANCE_TYPE 8579L
-#define ERROR_DS_NO_OBJECT_MOVE_IN_SCHEMA_NC 8580L
-#define ERROR_DS_MODIFYDN_DISALLOWED_BY_FLAG 8581L
-#define ERROR_DS_MODIFYDN_WRONG_GRANDPARENT 8582L
-#define ERROR_DS_NAME_ERROR_TRUST_REFERRAL 8583L
-#define ERROR_NOT_SUPPORTED_ON_STANDARD_SERVER 8584L
-#define ERROR_DS_CANT_ACCESS_REMOTE_PART_OF_AD 8585L
-#define ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE_V2 8586L
-#define ERROR_DS_THREAD_LIMIT_EXCEEDED 8587L
-#define ERROR_DS_NOT_CLOSEST 8588L
-#define ERROR_DS_CANT_DERIVE_SPN_WITHOUT_SERVER_REF 8589L
-#define ERROR_DS_SINGLE_USER_MODE_FAILED 8590L
-#define ERROR_DS_NTDSCRIPT_SYNTAX_ERROR 8591L
-#define ERROR_DS_NTDSCRIPT_PROCESS_ERROR 8592L
-#define ERROR_DS_DIFFERENT_REPL_EPOCHS 8593L
-#define ERROR_DS_DRS_EXTENSIONS_CHANGED 8594L
-#define ERROR_DS_REPLICA_SET_CHANGE_NOT_ALLOWED_ON_DISABLED_CR 8595L
-#define ERROR_DS_NO_MSDS_INTID 8596L
-#define ERROR_DS_DUP_MSDS_INTID 8597L
-#define ERROR_DS_EXISTS_IN_RDNATTID 8598L
-#define ERROR_DS_AUTHORIZATION_FAILED 8599L
-#define ERROR_DS_INVALID_SCRIPT 8600L
-#define ERROR_DS_REMOTE_CROSSREF_OP_FAILED 8601L
-#define ERROR_DS_CROSS_REF_BUSY 8602L
-#define ERROR_DS_CANT_DERIVE_SPN_FOR_DELETED_DOMAIN 8603L
-#define ERROR_DS_CANT_DEMOTE_WITH_WRITEABLE_NC 8604L
-#define ERROR_DS_DUPLICATE_ID_FOUND 8605L
-#define ERROR_DS_INSUFFICIENT_ATTR_TO_CREATE_OBJECT 8606L
-#define ERROR_DS_GROUP_CONVERSION_ERROR 8607L
-#define ERROR_DS_CANT_MOVE_APP_BASIC_GROUP 8608L
-#define ERROR_DS_CANT_MOVE_APP_QUERY_GROUP 8609L
-#define ERROR_DS_ROLE_NOT_VERIFIED 8610L
-#define ERROR_DS_WKO_CONTAINER_CANNOT_BE_SPECIAL 8611L
-#define ERROR_DS_DOMAIN_RENAME_IN_PROGRESS 8612L
-#define ERROR_DS_EXISTING_AD_CHILD_NC 8613L
-#define ERROR_DS_REPL_LIFETIME_EXCEEDED 8614L
-#define ERROR_DS_DISALLOWED_IN_SYSTEM_CONTAINER 8615L
-#define ERROR_DS_LDAP_SEND_QUEUE_FULL 8616L
-#define ERROR_DS_DRA_OUT_SCHEDULE_WINDOW 8617L
-#define DNS_ERROR_RESPONSE_CODES_BASE 9000
-#define DNS_ERROR_RCODE_NO_ERROR NO_ERROR
-#define DNS_ERROR_MASK 0x00002328
-#define DNS_ERROR_RCODE_FORMAT_ERROR 9001L
-#define DNS_ERROR_RCODE_SERVER_FAILURE 9002L
-#define DNS_ERROR_RCODE_NAME_ERROR 9003L
-#define DNS_ERROR_RCODE_NOT_IMPLEMENTED 9004L
-#define DNS_ERROR_RCODE_REFUSED 9005L
-#define DNS_ERROR_RCODE_YXDOMAIN 9006L
-#define DNS_ERROR_RCODE_YXRRSET 9007L
-#define DNS_ERROR_RCODE_NXRRSET 9008L
-#define DNS_ERROR_RCODE_NOTAUTH 9009L
-#define DNS_ERROR_RCODE_NOTZONE 9010L
-#define DNS_ERROR_RCODE_BADSIG 9016L
-#define DNS_ERROR_RCODE_BADKEY 9017L
-#define DNS_ERROR_RCODE_BADTIME 9018L
-#define DNS_ERROR_RCODE_LAST DNS_ERROR_RCODE_BADTIME
-#define DNS_ERROR_PACKET_FMT_BASE 9500
-#define DNS_INFO_NO_RECORDS 9501L
-#define DNS_ERROR_BAD_PACKET 9502L
-#define DNS_ERROR_NO_PACKET 9503L
-#define DNS_ERROR_RCODE 9504L
-#define DNS_ERROR_UNSECURE_PACKET 9505L
-#define DNS_STATUS_PACKET_UNSECURE DNS_ERROR_UNSECURE_PACKET
-#define DNS_ERROR_NO_MEMORY ERROR_OUTOFMEMORY
-#define DNS_ERROR_INVALID_NAME ERROR_INVALID_NAME
-#define DNS_ERROR_INVALID_DATA ERROR_INVALID_DATA
-#define DNS_ERROR_GENERAL_API_BASE 9550
-#define DNS_ERROR_INVALID_TYPE 9551L
-#define DNS_ERROR_INVALID_IP_ADDRESS 9552L
-#define DNS_ERROR_INVALID_PROPERTY 9553L
-#define DNS_ERROR_TRY_AGAIN_LATER 9554L
-#define DNS_ERROR_NOT_UNIQUE 9555L
-#define DNS_ERROR_NON_RFC_NAME 9556L
-#define DNS_STATUS_FQDN 9557L
-#define DNS_STATUS_DOTTED_NAME 9558L
-#define DNS_STATUS_SINGLE_PART_NAME 9559L
-#define DNS_ERROR_INVALID_NAME_CHAR 9560L
-#define DNS_ERROR_NUMERIC_NAME 9561L
-#define DNS_ERROR_NOT_ALLOWED_ON_ROOT_SERVER 9562L
-#define DNS_ERROR_NOT_ALLOWED_UNDER_DELEGATION 9563L
-#define DNS_ERROR_CANNOT_FIND_ROOT_HINTS 9564L
-#define DNS_ERROR_INCONSISTENT_ROOT_HINTS 9565L
-#define DNS_ERROR_ZONE_BASE 9600
-#define DNS_ERROR_ZONE_DOES_NOT_EXIST 9601L
-#define DNS_ERROR_NO_ZONE_INFO 9602L
-#define DNS_ERROR_INVALID_ZONE_OPERATION 9603L
-#define DNS_ERROR_ZONE_CONFIGURATION_ERROR 9604L
-#define DNS_ERROR_ZONE_HAS_NO_SOA_RECORD 9605L
-#define DNS_ERROR_ZONE_HAS_NO_NS_RECORDS 9606L
-#define DNS_ERROR_ZONE_LOCKED 9607L
-#define DNS_ERROR_ZONE_CREATION_FAILED 9608L
-#define DNS_ERROR_ZONE_ALREADY_EXISTS 9609L
-#define DNS_ERROR_AUTOZONE_ALREADY_EXISTS 9610L
-#define DNS_ERROR_INVALID_ZONE_TYPE 9611L
-#define DNS_ERROR_SECONDARY_REQUIRES_MASTER_IP 9612L
-#define DNS_ERROR_ZONE_NOT_SECONDARY 9613L
-#define DNS_ERROR_NEED_SECONDARY_ADDRESSES 9614L
-#define DNS_ERROR_WINS_INIT_FAILED 9615L
-#define DNS_ERROR_NEED_WINS_SERVERS 9616L
-#define DNS_ERROR_NBSTAT_INIT_FAILED 9617L
-#define DNS_ERROR_SOA_DELETE_INVALID 9618L
-#define DNS_ERROR_FORWARDER_ALREADY_EXISTS 9619L
-#define DNS_ERROR_ZONE_REQUIRES_MASTER_IP 9620L
-#define DNS_ERROR_ZONE_IS_SHUTDOWN 9621L
-#define DNS_ERROR_DATAFILE_BASE 9650
-#define DNS_ERROR_PRIMARY_REQUIRES_DATAFILE 9651L
-#define DNS_ERROR_INVALID_DATAFILE_NAME 9652L
-#define DNS_ERROR_DATAFILE_OPEN_FAILURE 9653L
-#define DNS_ERROR_FILE_WRITEBACK_FAILED 9654L
-#define DNS_ERROR_DATAFILE_PARSING 9655L
-#define DNS_ERROR_DATABASE_BASE 9700
-#define DNS_ERROR_RECORD_DOES_NOT_EXIST 9701L
-#define DNS_ERROR_RECORD_FORMAT 9702L
-#define DNS_ERROR_NODE_CREATION_FAILED 9703L
-#define DNS_ERROR_UNKNOWN_RECORD_TYPE 9704L
-#define DNS_ERROR_RECORD_TIMED_OUT 9705L
-#define DNS_ERROR_NAME_NOT_IN_ZONE 9706L
-#define DNS_ERROR_CNAME_LOOP 9707L
-#define DNS_ERROR_NODE_IS_CNAME 9708L
-#define DNS_ERROR_CNAME_COLLISION 9709L
-#define DNS_ERROR_RECORD_ONLY_AT_ZONE_ROOT 9710L
-#define DNS_ERROR_RECORD_ALREADY_EXISTS 9711L
-#define DNS_ERROR_SECONDARY_DATA 9712L
-#define DNS_ERROR_NO_CREATE_CACHE_DATA 9713L
-#define DNS_ERROR_NAME_DOES_NOT_EXIST 9714L
-#define DNS_WARNING_PTR_CREATE_FAILED 9715L
-#define DNS_WARNING_DOMAIN_UNDELETED 9716L
-#define DNS_ERROR_DS_UNAVAILABLE 9717L
-#define DNS_ERROR_DS_ZONE_ALREADY_EXISTS 9718L
-#define DNS_ERROR_NO_BOOTFILE_IF_DS_ZONE 9719L
-#define DNS_ERROR_OPERATION_BASE 9750
-#define DNS_INFO_AXFR_COMPLETE 9751L
-#define DNS_ERROR_AXFR 9752L
-#define DNS_INFO_ADDED_LOCAL_WINS 9753L
-#define DNS_ERROR_SECURE_BASE 9800
-#define DNS_STATUS_CONTINUE_NEEDED 9801L
-#define DNS_ERROR_SETUP_BASE 9850
-#define DNS_ERROR_NO_TCPIP 9851L
-#define DNS_ERROR_NO_DNS_SERVERS 9852L
-#define DNS_ERROR_DP_BASE 9900
-#define DNS_ERROR_DP_DOES_NOT_EXIST 9901L
-#define DNS_ERROR_DP_ALREADY_EXISTS 9902L
-#define DNS_ERROR_DP_NOT_ENLISTED 9903L
-#define DNS_ERROR_DP_ALREADY_ENLISTED 9904L
-#define DNS_ERROR_DP_NOT_AVAILABLE 9905L
-#define DNS_ERROR_DP_FSMO_ERROR 9906L
-
-#ifndef WSABASEERR
-#define WSABASEERR 10000
-#define WSAEINTR 10004L
-#define WSAEBADF 10009L
-#define WSAEACCES 10013L
-#define WSAEFAULT 10014L
-#define WSAEINVAL 10022L
-#define WSAEMFILE 10024L
-#define WSAEWOULDBLOCK 10035L
-#define WSAEINPROGRESS 10036L
-#define WSAEALREADY 10037L
-#define WSAENOTSOCK 10038L
-#define WSAEDESTADDRREQ 10039L
-#define WSAEMSGSIZE 10040L
-#define WSAEPROTOTYPE 10041L
-#define WSAENOPROTOOPT 10042L
-#define WSAEPROTONOSUPPORT 10043L
-#define WSAESOCKTNOSUPPORT 10044L
-#define WSAEOPNOTSUPP 10045L
-#define WSAEPFNOSUPPORT 10046L
-#define WSAEAFNOSUPPORT 10047L
-#define WSAEADDRINUSE 10048L
-#define WSAEADDRNOTAVAIL 10049L
-#define WSAENETDOWN 10050L
-#define WSAENETUNREACH 10051L
-#define WSAENETRESET 10052L
-#define WSAECONNABORTED 10053L
-#define WSAECONNRESET 10054L
-#define WSAENOBUFS 10055L
-#define WSAEISCONN 10056L
-#define WSAENOTCONN 10057L
-#define WSAESHUTDOWN 10058L
-#define WSAETOOMANYREFS 10059L
-#define WSAETIMEDOUT 10060L
-#define WSAECONNREFUSED 10061L
-#define WSAELOOP 10062L
-#define WSAENAMETOOLONG 10063L
-#define WSAEHOSTDOWN 10064L
-#define WSAEHOSTUNREACH 10065L
-#define WSAENOTEMPTY 10066L
-#define WSAEPROCLIM 10067L
-#define WSAEUSERS 10068L
-#define WSAEDQUOT 10069L
-#define WSAESTALE 10070L
-#define WSAEREMOTE 10071L
-#define WSASYSNOTREADY 10091L
-#define WSAVERNOTSUPPORTED 10092L
-#define WSANOTINITIALISED 10093L
-#define WSAEDISCON 10101L
-#define WSAENOMORE 10102L
-#define WSAECANCELLED 10103L
-#define WSAEINVALIDPROCTABLE 10104L
-#define WSAEINVALIDPROVIDER 10105L
-#define WSAEPROVIDERFAILEDINIT 10106L
-#define WSASYSCALLFAILURE 10107L
-#define WSASERVICE_NOT_FOUND 10108L
-#define WSATYPE_NOT_FOUND 10109L
-#define WSA_E_NO_MORE 10110L
-#define WSA_E_CANCELLED 10111L
-#define WSAEREFUSED 10112L
-#ifndef WSAHOST_NOT_FOUND
-#define WSAHOST_NOT_FOUND 11001L
-#endif
-#ifndef WSATRY_AGAIN
-#define WSATRY_AGAIN 11002L
-#endif
-#ifndef WSANO_RECOVERY
-#define WSANO_RECOVERY 11003L
-#endif
-#ifndef WSANO_DATA
-#define WSANO_DATA 11004L
-#endif
-#ifndef WSA_QOS_RECEIVERS
-#define WSA_QOS_RECEIVERS 11005L
-#endif
-#ifndef WSA_QOS_SENDERS
-#define WSA_QOS_SENDERS 11006L
-#endif
-#ifndef WSA_QOS_NO_SENDERS
-#define WSA_QOS_NO_SENDERS 11007L
-#endif
-#ifndef WSA_QOS_NO_RECEIVERS
-#define WSA_QOS_NO_RECEIVERS 11008L
-#endif
-#ifndef WSA_QOS_REQUEST_CONFIRMED
-#define WSA_QOS_REQUEST_CONFIRMED 11009L
-#endif
-#ifndef WSA_QOS_ADMISSION_FAILURE
-#define WSA_QOS_ADMISSION_FAILURE 11010L
-#endif
-#ifndef WSA_QOS_POLICY_FAILURE
-#define WSA_QOS_POLICY_FAILURE 11011L
-#endif
-#ifndef WSA_QOS_BAD_STYLE
-#define WSA_QOS_BAD_STYLE 11012L
-#endif
-#ifndef WSA_QOS_BAD_OBJECT
-#define WSA_QOS_BAD_OBJECT 11013L
-#endif
-#ifndef WSA_QOS_TRAFFIC_CTRL_ERROR
-#define WSA_QOS_TRAFFIC_CTRL_ERROR 11014L
-#endif
-#ifndef WSA_QOS_GENERIC_ERROR
-#define WSA_QOS_GENERIC_ERROR 11015L
-#endif
-#ifndef WSA_QOS_ESERVICETYPE
-#define WSA_QOS_ESERVICETYPE 11016L
-#endif
-#ifndef WSA_QOS_EFLOWSPEC
-#define WSA_QOS_EFLOWSPEC 11017L
-#endif
-#ifndef WSA_QOS_EPROVSPECBUF
-#define WSA_QOS_EPROVSPECBUF 11018L
-#endif
-#ifndef WSA_QOS_EFILTERSTYLE
-#define WSA_QOS_EFILTERSTYLE 11019L
-#endif
-#ifndef WSA_QOS_EFILTERTYPE
-#define WSA_QOS_EFILTERTYPE 11020L
-#endif
-#ifndef WSA_QOS_EFILTERCOUNT
-#define WSA_QOS_EFILTERCOUNT 11021L
-#endif
-#ifndef WSA_QOS_EOBJLENGTH
-#define WSA_QOS_EOBJLENGTH 11022L
-#endif
-#ifndef WSA_QOS_EFLOWCOUNT
-#define WSA_QOS_EFLOWCOUNT 11023L
-#endif
-#ifndef WSA_QOS_EUNKNOWNPSOBJ
-#define WSA_QOS_EUNKNOWNPSOBJ 11024L
-#endif
-#ifndef WSA_QOS_EPOLICYOBJ
-#define WSA_QOS_EPOLICYOBJ 11025L
-#endif
-#ifndef WSA_QOS_EFLOWDESC
-#define WSA_QOS_EFLOWDESC 11026L
-#endif
-#ifndef WSA_QOS_EPSFLOWSPEC
-#define WSA_QOS_EPSFLOWSPEC 11027L
-#endif
-#ifndef WSA_QOS_EPSFILTERSPEC
-#define WSA_QOS_EPSFILTERSPEC 11028L
-#endif
-#ifndef WSA_QOS_ESDMODEOBJ
-#define WSA_QOS_ESDMODEOBJ 11029L
-#endif
-#ifndef WSA_QOS_ESHAPERATEOBJ
-#define WSA_QOS_ESHAPERATEOBJ 11030L
-#endif
-#ifndef WSA_QOS_RESERVED_PETYPE
-#define WSA_QOS_RESERVED_PETYPE 11031L
-#endif
-#endif /* WSABASEERR */
-
-#define ERROR_SXS_SECTION_NOT_FOUND 14000L
-#define ERROR_SXS_CANT_GEN_ACTCTX 14001L
-#define ERROR_SXS_INVALID_ACTCTXDATA_FORMAT 14002L
-#define ERROR_SXS_ASSEMBLY_NOT_FOUND 14003L
-#define ERROR_SXS_MANIFEST_FORMAT_ERROR 14004L
-#define ERROR_SXS_MANIFEST_PARSE_ERROR 14005L
-#define ERROR_SXS_ACTIVATION_CONTEXT_DISABLED 14006L
-#define ERROR_SXS_KEY_NOT_FOUND 14007L
-#define ERROR_SXS_VERSION_CONFLICT 14008L
-#define ERROR_SXS_WRONG_SECTION_TYPE 14009L
-#define ERROR_SXS_THREAD_QUERIES_DISABLED 14010L
-#define ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET 14011L
-#define ERROR_SXS_UNKNOWN_ENCODING_GROUP 14012L
-#define ERROR_SXS_UNKNOWN_ENCODING 14013L
-#define ERROR_SXS_INVALID_XML_NAMESPACE_URI 14014L
-#define ERROR_SXS_ROOT_MANIFEST_DEPENDENCY_NOT_INSTALLED 14015L
-#define ERROR_SXS_LEAF_MANIFEST_DEPENDENCY_NOT_INSTALLED 14016L
-#define ERROR_SXS_INVALID_ASSEMBLY_IDENTITY_ATTRIBUTE 14017L
-#define ERROR_SXS_MANIFEST_MISSING_REQUIRED_DEFAULT_NAMESPACE 14018L
-#define ERROR_SXS_MANIFEST_INVALID_REQUIRED_DEFAULT_NAMESPACE 14019L
-#define ERROR_SXS_PRIVATE_MANIFEST_CROSS_PATH_WITH_REPARSE_POINT 14020L
-#define ERROR_SXS_DUPLICATE_DLL_NAME 14021L
-#define ERROR_SXS_DUPLICATE_WINDOWCLASS_NAME 14022L
-#define ERROR_SXS_DUPLICATE_CLSID 14023L
-#define ERROR_SXS_DUPLICATE_IID 14024L
-#define ERROR_SXS_DUPLICATE_TLBID 14025L
-#define ERROR_SXS_DUPLICATE_PROGID 14026L
-#define ERROR_SXS_DUPLICATE_ASSEMBLY_NAME 14027L
-#define ERROR_SXS_FILE_HASH_MISMATCH 14028L
-#define ERROR_SXS_POLICY_PARSE_ERROR 14029L
-#define ERROR_SXS_XML_E_MISSINGQUOTE 14030L
-#define ERROR_SXS_XML_E_COMMENTSYNTAX 14031L
-#define ERROR_SXS_XML_E_BADSTARTNAMECHAR 14032L
-#define ERROR_SXS_XML_E_BADNAMECHAR 14033L
-#define ERROR_SXS_XML_E_BADCHARINSTRING 14034L
-#define ERROR_SXS_XML_E_XMLDECLSYNTAX 14035L
-#define ERROR_SXS_XML_E_BADCHARDATA 14036L
-#define ERROR_SXS_XML_E_MISSINGWHITESPACE 14037L
-#define ERROR_SXS_XML_E_EXPECTINGTAGEND 14038L
-#define ERROR_SXS_XML_E_MISSINGSEMICOLON 14039L
-#define ERROR_SXS_XML_E_UNBALANCEDPAREN 14040L
-#define ERROR_SXS_XML_E_INTERNALERROR 14041L
-#define ERROR_SXS_XML_E_UNEXPECTED_WHITESPACE 14042L
-#define ERROR_SXS_XML_E_INCOMPLETE_ENCODING 14043L
-#define ERROR_SXS_XML_E_MISSING_PAREN 14044L
-#define ERROR_SXS_XML_E_EXPECTINGCLOSEQUOTE 14045L
-#define ERROR_SXS_XML_E_MULTIPLE_COLONS 14046L
-#define ERROR_SXS_XML_E_INVALID_DECIMAL 14047L
-#define ERROR_SXS_XML_E_INVALID_HEXIDECIMAL 14048L
-#define ERROR_SXS_XML_E_INVALID_UNICODE 14049L
-#define ERROR_SXS_XML_E_WHITESPACEORQUESTIONMARK 14050L
-#define ERROR_SXS_XML_E_UNEXPECTEDENDTAG 14051L
-#define ERROR_SXS_XML_E_UNCLOSEDTAG 14052L
-#define ERROR_SXS_XML_E_DUPLICATEATTRIBUTE 14053L
-#define ERROR_SXS_XML_E_MULTIPLEROOTS 14054L
-#define ERROR_SXS_XML_E_INVALIDATROOTLEVEL 14055L
-#define ERROR_SXS_XML_E_BADXMLDECL 14056L
-#define ERROR_SXS_XML_E_MISSINGROOT 14057L
-#define ERROR_SXS_XML_E_UNEXPECTEDEOF 14058L
-#define ERROR_SXS_XML_E_BADPEREFINSUBSET 14059L
-#define ERROR_SXS_XML_E_UNCLOSEDSTARTTAG 14060L
-#define ERROR_SXS_XML_E_UNCLOSEDENDTAG 14061L
-#define ERROR_SXS_XML_E_UNCLOSEDSTRING 14062L
-#define ERROR_SXS_XML_E_UNCLOSEDCOMMENT 14063L
-#define ERROR_SXS_XML_E_UNCLOSEDDECL 14064L
-#define ERROR_SXS_XML_E_UNCLOSEDCDATA 14065L
-#define ERROR_SXS_XML_E_RESERVEDNAMESPACE 14066L
-#define ERROR_SXS_XML_E_INVALIDENCODING 14067L
-#define ERROR_SXS_XML_E_INVALIDSWITCH 14068L
-#define ERROR_SXS_XML_E_BADXMLCASE 14069L
-#define ERROR_SXS_XML_E_INVALID_STANDALONE 14070L
-#define ERROR_SXS_XML_E_UNEXPECTED_STANDALONE 14071L
-#define ERROR_SXS_XML_E_INVALID_VERSION 14072L
-#define ERROR_SXS_XML_E_MISSINGEQUALS 14073L
-#define ERROR_SXS_PROTECTION_RECOVERY_FAILED 14074L
-#define ERROR_SXS_PROTECTION_PUBLIC_KEY_TOO_SHORT 14075L
-#define ERROR_SXS_PROTECTION_CATALOG_NOT_VALID 14076L
-#define ERROR_SXS_UNTRANSLATABLE_HRESULT 14077L
-#define ERROR_SXS_PROTECTION_CATALOG_FILE_MISSING 14078L
-#define ERROR_SXS_MISSING_ASSEMBLY_IDENTITY_ATTRIBUTE 14079L
-#define ERROR_SXS_INVALID_ASSEMBLY_IDENTITY_ATTRIBUTE_NAME 14080L
-#define ERROR_IPSEC_QM_POLICY_EXISTS 13000L
-#define ERROR_IPSEC_QM_POLICY_NOT_FOUND 13001L
-#define ERROR_IPSEC_QM_POLICY_IN_USE 13002L
-#define ERROR_IPSEC_MM_POLICY_EXISTS 13003L
-#define ERROR_IPSEC_MM_POLICY_NOT_FOUND 13004L
-#define ERROR_IPSEC_MM_POLICY_IN_USE 13005L
-#define ERROR_IPSEC_MM_FILTER_EXISTS 13006L
-#define ERROR_IPSEC_MM_FILTER_NOT_FOUND 13007L
-#define ERROR_IPSEC_TRANSPORT_FILTER_EXISTS 13008L
-#define ERROR_IPSEC_TRANSPORT_FILTER_NOT_FOUND 13009L
-#define ERROR_IPSEC_MM_AUTH_EXISTS 13010L
-#define ERROR_IPSEC_MM_AUTH_NOT_FOUND 13011L
-#define ERROR_IPSEC_MM_AUTH_IN_USE 13012L
-#define ERROR_IPSEC_DEFAULT_MM_POLICY_NOT_FOUND 13013L
-#define ERROR_IPSEC_DEFAULT_MM_AUTH_NOT_FOUND 13014L
-#define ERROR_IPSEC_DEFAULT_QM_POLICY_NOT_FOUND 13015L
-#define ERROR_IPSEC_TUNNEL_FILTER_EXISTS 13016L
-#define ERROR_IPSEC_TUNNEL_FILTER_NOT_FOUND 13017L
-#define ERROR_IPSEC_MM_FILTER_PENDING_DELETION 13018L
-#define ERROR_IPSEC_TRANSPORT_FILTER_PENDING_DELETION 13019L
-#define ERROR_IPSEC_TUNNEL_FILTER_PENDING_DELETION 13020L
-#define ERROR_IPSEC_MM_POLICY_PENDING_DELETION 13021L
-#define ERROR_IPSEC_MM_AUTH_PENDING_DELETION 13022L
-#define ERROR_IPSEC_QM_POLICY_PENDING_DELETION 13023L
-#define WARNING_IPSEC_MM_POLICY_PRUNED 13024L
-#define WARNING_IPSEC_QM_POLICY_PRUNED 13025L
-#define ERROR_IPSEC_IKE_NEG_STATUS_BEGIN 13800L
-#define ERROR_IPSEC_IKE_AUTH_FAIL 13801L
-#define ERROR_IPSEC_IKE_ATTRIB_FAIL 13802L
-#define ERROR_IPSEC_IKE_NEGOTIATION_PENDING 13803L
-#define ERROR_IPSEC_IKE_GENERAL_PROCESSING_ERROR 13804L
-#define ERROR_IPSEC_IKE_TIMED_OUT 13805L
-#define ERROR_IPSEC_IKE_NO_CERT 13806L
-#define ERROR_IPSEC_IKE_SA_DELETED 13807L
-#define ERROR_IPSEC_IKE_SA_REAPED 13808L
-#define ERROR_IPSEC_IKE_MM_ACQUIRE_DROP 13809L
-#define ERROR_IPSEC_IKE_QM_ACQUIRE_DROP 13810L
-#define ERROR_IPSEC_IKE_QUEUE_DROP_MM 13811L
-#define ERROR_IPSEC_IKE_QUEUE_DROP_NO_MM 13812L
-#define ERROR_IPSEC_IKE_DROP_NO_RESPONSE 13813L
-#define ERROR_IPSEC_IKE_MM_DELAY_DROP 13814L
-#define ERROR_IPSEC_IKE_QM_DELAY_DROP 13815L
-#define ERROR_IPSEC_IKE_ERROR 13816L
-#define ERROR_IPSEC_IKE_CRL_FAILED 13817L
-#define ERROR_IPSEC_IKE_INVALID_KEY_USAGE 13818L
-#define ERROR_IPSEC_IKE_INVALID_CERT_TYPE 13819L
-#define ERROR_IPSEC_IKE_NO_PRIVATE_KEY 13820L
-#define ERROR_IPSEC_IKE_DH_FAIL 13822L
-#define ERROR_IPSEC_IKE_INVALID_HEADER 13824L
-#define ERROR_IPSEC_IKE_NO_POLICY 13825L
-#define ERROR_IPSEC_IKE_INVALID_SIGNATURE 13826L
-#define ERROR_IPSEC_IKE_KERBEROS_ERROR 13827L
-#define ERROR_IPSEC_IKE_NO_PUBLIC_KEY 13828L
-#define ERROR_IPSEC_IKE_PROCESS_ERR 13829L
-#define ERROR_IPSEC_IKE_PROCESS_ERR_SA 13830L
-#define ERROR_IPSEC_IKE_PROCESS_ERR_PROP 13831L
-#define ERROR_IPSEC_IKE_PROCESS_ERR_TRANS 13832L
-#define ERROR_IPSEC_IKE_PROCESS_ERR_KE 13833L
-#define ERROR_IPSEC_IKE_PROCESS_ERR_ID 13834L
-#define ERROR_IPSEC_IKE_PROCESS_ERR_CERT 13835L
-#define ERROR_IPSEC_IKE_PROCESS_ERR_CERT_REQ 13836L
-#define ERROR_IPSEC_IKE_PROCESS_ERR_HASH 13837L
-#define ERROR_IPSEC_IKE_PROCESS_ERR_SIG 13838L
-#define ERROR_IPSEC_IKE_PROCESS_ERR_NONCE 13839L
-#define ERROR_IPSEC_IKE_PROCESS_ERR_NOTIFY 13840L
-#define ERROR_IPSEC_IKE_PROCESS_ERR_DELETE 13841L
-#define ERROR_IPSEC_IKE_PROCESS_ERR_VENDOR 13842L
-#define ERROR_IPSEC_IKE_INVALID_PAYLOAD 13843L
-#define ERROR_IPSEC_IKE_LOAD_SOFT_SA 13844L
-#define ERROR_IPSEC_IKE_SOFT_SA_TORN_DOWN 13845L
-#define ERROR_IPSEC_IKE_INVALID_COOKIE 13846L
-#define ERROR_IPSEC_IKE_NO_PEER_CERT 13847L
-#define ERROR_IPSEC_IKE_PEER_CRL_FAILED 13848L
-#define ERROR_IPSEC_IKE_POLICY_CHANGE 13849L
-#define ERROR_IPSEC_IKE_NO_MM_POLICY 13850L
-#define ERROR_IPSEC_IKE_NOTCBPRIV 13851L
-#define ERROR_IPSEC_IKE_SECLOADFAIL 13852L
-#define ERROR_IPSEC_IKE_FAILSSPINIT 13853L
-#define ERROR_IPSEC_IKE_FAILQUERYSSP 13854L
-#define ERROR_IPSEC_IKE_SRVACQFAIL 13855L
-#define ERROR_IPSEC_IKE_SRVQUERYCRED 13856L
-#define ERROR_IPSEC_IKE_GETSPIFAIL 13857L
-#define ERROR_IPSEC_IKE_INVALID_FILTER 13858L
-#define ERROR_IPSEC_IKE_OUT_OF_MEMORY 13859L
-#define ERROR_IPSEC_IKE_ADD_UPDATE_KEY_FAILED 13860L
-#define ERROR_IPSEC_IKE_INVALID_POLICY 13861L
-#define ERROR_IPSEC_IKE_UNKNOWN_DOI 13862L
-#define ERROR_IPSEC_IKE_INVALID_SITUATION 13863L
-#define ERROR_IPSEC_IKE_DH_FAILURE 13864L
-#define ERROR_IPSEC_IKE_INVALID_GROUP 13865L
-#define ERROR_IPSEC_IKE_ENCRYPT 13866L
-#define ERROR_IPSEC_IKE_DECRYPT 13867L
-#define ERROR_IPSEC_IKE_POLICY_MATCH 13868L
-#define ERROR_IPSEC_IKE_UNSUPPORTED_ID 13869L
-#define ERROR_IPSEC_IKE_INVALID_HASH 13870L
-#define ERROR_IPSEC_IKE_INVALID_HASH_ALG 13871L
-#define ERROR_IPSEC_IKE_INVALID_HASH_SIZE 13872L
-#define ERROR_IPSEC_IKE_INVALID_ENCRYPT_ALG 13873L
-#define ERROR_IPSEC_IKE_INVALID_AUTH_ALG 13874L
-#define ERROR_IPSEC_IKE_INVALID_SIG 13875L
-#define ERROR_IPSEC_IKE_LOAD_FAILED 13876L
-#define ERROR_IPSEC_IKE_RPC_DELETE 13877L
-#define ERROR_IPSEC_IKE_BENIGN_REINIT 13878L
-#define ERROR_IPSEC_IKE_INVALID_RESPONDER_LIFETIME_NOTIFY 13879L
-#define ERROR_IPSEC_IKE_INVALID_CERT_KEYLEN 13881L
-#define ERROR_IPSEC_IKE_MM_LIMIT 13882L
-#define ERROR_IPSEC_IKE_NEGOTIATION_DISABLED 13883L
-#define ERROR_IPSEC_IKE_NEG_STATUS_END 13884L
-#define SEVERITY_SUCCESS 0
-#define SEVERITY_ERROR 1
-#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)
-#define FAILED(hr) ((HRESULT)(hr) < 0)
-#define IS_ERROR(Status) ((unsigned long)(Status) >> 31==SEVERITY_ERROR)
-#define HRESULT_CODE(hr) ((hr) & 0xFFFF)
-#define SCODE_CODE(sc) ((sc) & 0xFFFF)
-#define HRESULT_FACILITY(hr) (((hr) >> 16) & 0x1fff)
-#define SCODE_FACILITY(sc) (((sc) >> 16) & 0x1fff)
-#define HRESULT_SEVERITY(hr) (((hr) >> 31) & 0x1)
-#define SCODE_SEVERITY(sc) (((sc) >> 31) & 0x1)
-#define MAKE_HRESULT(sev,fac,code) ((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))))
-#define MAKE_SCODE(sev,fac,code) ((SCODE) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))))
-#define FACILITY_NT_BIT 0x10000000
-#define __HRESULT_FROM_WIN32(x) ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : ((HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000)))
-#ifdef INLINE_HRESULT_FROM_WIN32
-#ifndef _HRESULT_DEFINED
-#define _HRESULT_DEFINED
-typedef long HRESULT;
-#endif
-__CRT_INLINE HRESULT HRESULT_FROM_WIN32(long x) { return x <= 0 ? (HRESULT)x : (HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000);}
-#else
-#define HRESULT_FROM_WIN32(x) __HRESULT_FROM_WIN32(x)
-#endif
-#define HRESULT_FROM_NT(x) ((HRESULT) ((x) | FACILITY_NT_BIT))
-#define GetScode(hr) ((SCODE) (hr))
-#define ResultFromScode(sc) ((HRESULT) (sc))
-#define PropagateResult(hrPrevious,scBase) ((HRESULT) scBase)
-#ifdef RC_INVOKED
-#define _HRESULT_TYPEDEF_(_sc) _sc
-#else
-#define _HRESULT_TYPEDEF_(_sc) ((HRESULT)_sc)
-#endif
-#define NOERROR 0
-#define E_UNEXPECTED _HRESULT_TYPEDEF_(0x8000FFFFL)
-#define E_NOTIMPL _HRESULT_TYPEDEF_(0x80004001L)
-#define E_OUTOFMEMORY _HRESULT_TYPEDEF_(0x8007000EL)
-#define E_INVALIDARG _HRESULT_TYPEDEF_(0x80070057L)
-#define E_NOINTERFACE _HRESULT_TYPEDEF_(0x80004002L)
-#define E_POINTER _HRESULT_TYPEDEF_(0x80004003L)
-#define E_HANDLE _HRESULT_TYPEDEF_(0x80070006L)
-#define E_ABORT _HRESULT_TYPEDEF_(0x80004004L)
-#define E_FAIL _HRESULT_TYPEDEF_(0x80004005L)
-#define E_ACCESSDENIED _HRESULT_TYPEDEF_(0x80070005L)
-#define E_PENDING _HRESULT_TYPEDEF_(0x8000000AL)
-#define CO_E_INIT_TLS _HRESULT_TYPEDEF_(0x80004006L)
-#define CO_E_INIT_SHARED_ALLOCATOR _HRESULT_TYPEDEF_(0x80004007L)
-#define CO_E_INIT_MEMORY_ALLOCATOR _HRESULT_TYPEDEF_(0x80004008L)
-#define CO_E_INIT_CLASS_CACHE _HRESULT_TYPEDEF_(0x80004009L)
-#define CO_E_INIT_RPC_CHANNEL _HRESULT_TYPEDEF_(0x8000400AL)
-#define CO_E_INIT_TLS_SET_CHANNEL_CONTROL _HRESULT_TYPEDEF_(0x8000400BL)
-#define CO_E_INIT_TLS_CHANNEL_CONTROL _HRESULT_TYPEDEF_(0x8000400CL)
-#define CO_E_INIT_UNACCEPTED_USER_ALLOCATOR _HRESULT_TYPEDEF_(0x8000400DL)
-#define CO_E_INIT_SCM_MUTEX_EXISTS _HRESULT_TYPEDEF_(0x8000400EL)
-#define CO_E_INIT_SCM_FILE_MAPPING_EXISTS _HRESULT_TYPEDEF_(0x8000400FL)
-#define CO_E_INIT_SCM_MAP_VIEW_OF_FILE _HRESULT_TYPEDEF_(0x80004010L)
-#define CO_E_INIT_SCM_EXEC_FAILURE _HRESULT_TYPEDEF_(0x80004011L)
-#define CO_E_INIT_ONLY_SINGLE_THREADED _HRESULT_TYPEDEF_(0x80004012L)
-#define CO_E_CANT_REMOTE _HRESULT_TYPEDEF_(0x80004013L)
-#define CO_E_BAD_SERVER_NAME _HRESULT_TYPEDEF_(0x80004014L)
-#define CO_E_WRONG_SERVER_IDENTITY _HRESULT_TYPEDEF_(0x80004015L)
-#define CO_E_OLE1DDE_DISABLED _HRESULT_TYPEDEF_(0x80004016L)
-#define CO_E_RUNAS_SYNTAX _HRESULT_TYPEDEF_(0x80004017L)
-#define CO_E_CREATEPROCESS_FAILURE _HRESULT_TYPEDEF_(0x80004018L)
-#define CO_E_RUNAS_CREATEPROCESS_FAILURE _HRESULT_TYPEDEF_(0x80004019L)
-#define CO_E_RUNAS_LOGON_FAILURE _HRESULT_TYPEDEF_(0x8000401AL)
-#define CO_E_LAUNCH_PERMSSION_DENIED _HRESULT_TYPEDEF_(0x8000401BL)
-#define CO_E_START_SERVICE_FAILURE _HRESULT_TYPEDEF_(0x8000401CL)
-#define CO_E_REMOTE_COMMUNICATION_FAILURE _HRESULT_TYPEDEF_(0x8000401DL)
-#define CO_E_SERVER_START_TIMEOUT _HRESULT_TYPEDEF_(0x8000401EL)
-#define CO_E_CLSREG_INCONSISTENT _HRESULT_TYPEDEF_(0x8000401FL)
-#define CO_E_IIDREG_INCONSISTENT _HRESULT_TYPEDEF_(0x80004020L)
-#define CO_E_NOT_SUPPORTED _HRESULT_TYPEDEF_(0x80004021L)
-#define CO_E_RELOAD_DLL _HRESULT_TYPEDEF_(0x80004022L)
-#define CO_E_MSI_ERROR _HRESULT_TYPEDEF_(0x80004023L)
-#define CO_E_ATTEMPT_TO_CREATE_OUTSIDE_CLIENT_CONTEXT _HRESULT_TYPEDEF_(0x80004024L)
-#define CO_E_SERVER_PAUSED _HRESULT_TYPEDEF_(0x80004025L)
-#define CO_E_SERVER_NOT_PAUSED _HRESULT_TYPEDEF_(0x80004026L)
-#define CO_E_CLASS_DISABLED _HRESULT_TYPEDEF_(0x80004027L)
-#define CO_E_CLRNOTAVAILABLE _HRESULT_TYPEDEF_(0x80004028L)
-#define CO_E_ASYNC_WORK_REJECTED _HRESULT_TYPEDEF_(0x80004029L)
-#define CO_E_SERVER_INIT_TIMEOUT _HRESULT_TYPEDEF_(0x8000402AL)
-#define CO_E_NO_SECCTX_IN_ACTIVATE _HRESULT_TYPEDEF_(0x8000402BL)
-#define CO_E_TRACKER_CONFIG _HRESULT_TYPEDEF_(0x80004030L)
-#define CO_E_THREADPOOL_CONFIG _HRESULT_TYPEDEF_(0x80004031L)
-#define CO_E_SXS_CONFIG _HRESULT_TYPEDEF_(0x80004032L)
-#define CO_E_MALFORMED_SPN _HRESULT_TYPEDEF_(0x80004033L)
-#define S_OK ((HRESULT)0x00000000L)
-#define S_FALSE ((HRESULT)0x00000001L)
-#define OLE_E_FIRST ((HRESULT)0x80040000L)
-#define OLE_E_LAST ((HRESULT)0x800400FFL)
-#define OLE_S_FIRST ((HRESULT)0x00040000L)
-#define OLE_S_LAST ((HRESULT)0x000400FFL)
-#define OLE_E_OLEVERB _HRESULT_TYPEDEF_(0x80040000L)
-#define OLE_E_ADVF _HRESULT_TYPEDEF_(0x80040001L)
-#define OLE_E_ENUM_NOMORE _HRESULT_TYPEDEF_(0x80040002L)
-#define OLE_E_ADVISENOTSUPPORTED _HRESULT_TYPEDEF_(0x80040003L)
-#define OLE_E_NOCONNECTION _HRESULT_TYPEDEF_(0x80040004L)
-#define OLE_E_NOTRUNNING _HRESULT_TYPEDEF_(0x80040005L)
-#define OLE_E_NOCACHE _HRESULT_TYPEDEF_(0x80040006L)
-#define OLE_E_BLANK _HRESULT_TYPEDEF_(0x80040007L)
-#define OLE_E_CLASSDIFF _HRESULT_TYPEDEF_(0x80040008L)
-#define OLE_E_CANT_GETMONIKER _HRESULT_TYPEDEF_(0x80040009L)
-#define OLE_E_CANT_BINDTOSOURCE _HRESULT_TYPEDEF_(0x8004000AL)
-#define OLE_E_STATIC _HRESULT_TYPEDEF_(0x8004000BL)
-#define OLE_E_PROMPTSAVECANCELLED _HRESULT_TYPEDEF_(0x8004000CL)
-#define OLE_E_INVALIDRECT _HRESULT_TYPEDEF_(0x8004000DL)
-#define OLE_E_WRONGCOMPOBJ _HRESULT_TYPEDEF_(0x8004000EL)
-#define OLE_E_INVALIDHWND _HRESULT_TYPEDEF_(0x8004000FL)
-#define OLE_E_NOT_INPLACEACTIVE _HRESULT_TYPEDEF_(0x80040010L)
-#define OLE_E_CANTCONVERT _HRESULT_TYPEDEF_(0x80040011L)
-#define OLE_E_NOSTORAGE _HRESULT_TYPEDEF_(0x80040012L)
-#define DV_E_FORMATETC _HRESULT_TYPEDEF_(0x80040064L)
-#define DV_E_DVTARGETDEVICE _HRESULT_TYPEDEF_(0x80040065L)
-#define DV_E_STGMEDIUM _HRESULT_TYPEDEF_(0x80040066L)
-#define DV_E_STATDATA _HRESULT_TYPEDEF_(0x80040067L)
-#define DV_E_LINDEX _HRESULT_TYPEDEF_(0x80040068L)
-#define DV_E_TYMED _HRESULT_TYPEDEF_(0x80040069L)
-#define DV_E_CLIPFORMAT _HRESULT_TYPEDEF_(0x8004006AL)
-#define DV_E_DVASPECT _HRESULT_TYPEDEF_(0x8004006BL)
-#define DV_E_DVTARGETDEVICE_SIZE _HRESULT_TYPEDEF_(0x8004006CL)
-#define DV_E_NOIVIEWOBJECT _HRESULT_TYPEDEF_(0x8004006DL)
-#define DRAGDROP_E_FIRST 0x80040100L
-#define DRAGDROP_E_LAST 0x8004010FL
-#define DRAGDROP_S_FIRST 0x00040100L
-#define DRAGDROP_S_LAST 0x0004010FL
-#define DRAGDROP_E_NOTREGISTERED _HRESULT_TYPEDEF_(0x80040100L)
-#define DRAGDROP_E_ALREADYREGISTERED _HRESULT_TYPEDEF_(0x80040101L)
-#define DRAGDROP_E_INVALIDHWND _HRESULT_TYPEDEF_(0x80040102L)
-#define CLASSFACTORY_E_FIRST 0x80040110L
-#define CLASSFACTORY_E_LAST 0x8004011FL
-#define CLASSFACTORY_S_FIRST 0x00040110L
-#define CLASSFACTORY_S_LAST 0x0004011FL
-#define CLASS_E_NOAGGREGATION _HRESULT_TYPEDEF_(0x80040110L)
-#define CLASS_E_CLASSNOTAVAILABLE _HRESULT_TYPEDEF_(0x80040111L)
-#define CLASS_E_NOTLICENSED _HRESULT_TYPEDEF_(0x80040112L)
-#define MARSHAL_E_FIRST 0x80040120L
-#define MARSHAL_E_LAST 0x8004012FL
-#define MARSHAL_S_FIRST 0x00040120L
-#define MARSHAL_S_LAST 0x0004012FL
-#define DATA_E_FIRST 0x80040130L
-#define DATA_E_LAST 0x8004013FL
-#define DATA_S_FIRST 0x00040130L
-#define DATA_S_LAST 0x0004013FL
-#define VIEW_E_FIRST 0x80040140L
-#define VIEW_E_LAST 0x8004014FL
-#define VIEW_S_FIRST 0x00040140L
-#define VIEW_S_LAST 0x0004014FL
-#define VIEW_E_DRAW _HRESULT_TYPEDEF_(0x80040140L)
-#define REGDB_E_FIRST 0x80040150L
-#define REGDB_E_LAST 0x8004015FL
-#define REGDB_S_FIRST 0x00040150L
-#define REGDB_S_LAST 0x0004015FL
-#define REGDB_E_READREGDB _HRESULT_TYPEDEF_(0x80040150L)
-#define REGDB_E_WRITEREGDB _HRESULT_TYPEDEF_(0x80040151L)
-#define REGDB_E_KEYMISSING _HRESULT_TYPEDEF_(0x80040152L)
-#define REGDB_E_INVALIDVALUE _HRESULT_TYPEDEF_(0x80040153L)
-#define REGDB_E_CLASSNOTREG _HRESULT_TYPEDEF_(0x80040154L)
-#define REGDB_E_IIDNOTREG _HRESULT_TYPEDEF_(0x80040155L)
-#define REGDB_E_BADTHREADINGMODEL _HRESULT_TYPEDEF_(0x80040156L)
-#define CAT_E_FIRST 0x80040160L
-#define CAT_E_LAST 0x80040161L
-#define CAT_E_CATIDNOEXIST _HRESULT_TYPEDEF_(0x80040160L)
-#define CAT_E_NODESCRIPTION _HRESULT_TYPEDEF_(0x80040161L)
-#define CS_E_FIRST 0x80040164L
-#define CS_E_LAST 0x8004016FL
-#define CS_E_PACKAGE_NOTFOUND _HRESULT_TYPEDEF_(0x80040164L)
-#define CS_E_NOT_DELETABLE _HRESULT_TYPEDEF_(0x80040165L)
-#define CS_E_CLASS_NOTFOUND _HRESULT_TYPEDEF_(0x80040166L)
-#define CS_E_INVALID_VERSION _HRESULT_TYPEDEF_(0x80040167L)
-#define CS_E_NO_CLASSSTORE _HRESULT_TYPEDEF_(0x80040168L)
-#define CS_E_OBJECT_NOTFOUND _HRESULT_TYPEDEF_(0x80040169L)
-#define CS_E_OBJECT_ALREADY_EXISTS _HRESULT_TYPEDEF_(0x8004016AL)
-#define CS_E_INVALID_PATH _HRESULT_TYPEDEF_(0x8004016BL)
-#define CS_E_NETWORK_ERROR _HRESULT_TYPEDEF_(0x8004016CL)
-#define CS_E_ADMIN_LIMIT_EXCEEDED _HRESULT_TYPEDEF_(0x8004016DL)
-#define CS_E_SCHEMA_MISMATCH _HRESULT_TYPEDEF_(0x8004016EL)
-#define CS_E_INTERNAL_ERROR _HRESULT_TYPEDEF_(0x8004016FL)
-#define CACHE_E_FIRST 0x80040170L
-#define CACHE_E_LAST 0x8004017FL
-#define CACHE_S_FIRST 0x00040170L
-#define CACHE_S_LAST 0x0004017FL
-#define CACHE_E_NOCACHE_UPDATED _HRESULT_TYPEDEF_(0x80040170L)
-#define OLEOBJ_E_FIRST 0x80040180L
-#define OLEOBJ_E_LAST 0x8004018FL
-#define OLEOBJ_S_FIRST 0x00040180L
-#define OLEOBJ_S_LAST 0x0004018FL
-#define OLEOBJ_E_NOVERBS _HRESULT_TYPEDEF_(0x80040180L)
-#define OLEOBJ_E_INVALIDVERB _HRESULT_TYPEDEF_(0x80040181L)
-#define CLIENTSITE_E_FIRST 0x80040190L
-#define CLIENTSITE_E_LAST 0x8004019FL
-#define CLIENTSITE_S_FIRST 0x00040190L
-#define CLIENTSITE_S_LAST 0x0004019FL
-#define INPLACE_E_NOTUNDOABLE _HRESULT_TYPEDEF_(0x800401A0L)
-#define INPLACE_E_NOTOOLSPACE _HRESULT_TYPEDEF_(0x800401A1L)
-#define INPLACE_E_FIRST 0x800401A0L
-#define INPLACE_E_LAST 0x800401AFL
-#define INPLACE_S_FIRST 0x000401A0L
-#define INPLACE_S_LAST 0x000401AFL
-#define ENUM_E_FIRST 0x800401B0L
-#define ENUM_E_LAST 0x800401BFL
-#define ENUM_S_FIRST 0x000401B0L
-#define ENUM_S_LAST 0x000401BFL
-#define CONVERT10_E_FIRST 0x800401C0L
-#define CONVERT10_E_LAST 0x800401CFL
-#define CONVERT10_S_FIRST 0x000401C0L
-#define CONVERT10_S_LAST 0x000401CFL
-#define CONVERT10_E_OLESTREAM_GET _HRESULT_TYPEDEF_(0x800401C0L)
-#define CONVERT10_E_OLESTREAM_PUT _HRESULT_TYPEDEF_(0x800401C1L)
-#define CONVERT10_E_OLESTREAM_FMT _HRESULT_TYPEDEF_(0x800401C2L)
-#define CONVERT10_E_OLESTREAM_BITMAP_TO_DIB _HRESULT_TYPEDEF_(0x800401C3L)
-#define CONVERT10_E_STG_FMT _HRESULT_TYPEDEF_(0x800401C4L)
-#define CONVERT10_E_STG_NO_STD_STREAM _HRESULT_TYPEDEF_(0x800401C5L)
-#define CONVERT10_E_STG_DIB_TO_BITMAP _HRESULT_TYPEDEF_(0x800401C6L)
-#define CLIPBRD_E_FIRST 0x800401D0L
-#define CLIPBRD_E_LAST 0x800401DFL
-#define CLIPBRD_S_FIRST 0x000401D0L
-#define CLIPBRD_S_LAST 0x000401DFL
-#define CLIPBRD_E_CANT_OPEN _HRESULT_TYPEDEF_(0x800401D0L)
-#define CLIPBRD_E_CANT_EMPTY _HRESULT_TYPEDEF_(0x800401D1L)
-#define CLIPBRD_E_CANT_SET _HRESULT_TYPEDEF_(0x800401D2L)
-#define CLIPBRD_E_BAD_DATA _HRESULT_TYPEDEF_(0x800401D3L)
-#define CLIPBRD_E_CANT_CLOSE _HRESULT_TYPEDEF_(0x800401D4L)
-#define MK_E_FIRST 0x800401E0L
-#define MK_E_LAST 0x800401EFL
-#define MK_S_FIRST 0x000401E0L
-#define MK_S_LAST 0x000401EFL
-#define MK_E_CONNECTMANUALLY _HRESULT_TYPEDEF_(0x800401E0L)
-#define MK_E_EXCEEDEDDEADLINE _HRESULT_TYPEDEF_(0x800401E1L)
-#define MK_E_NEEDGENERIC _HRESULT_TYPEDEF_(0x800401E2L)
-#define MK_E_UNAVAILABLE _HRESULT_TYPEDEF_(0x800401E3L)
-#define MK_E_SYNTAX _HRESULT_TYPEDEF_(0x800401E4L)
-#define MK_E_NOOBJECT _HRESULT_TYPEDEF_(0x800401E5L)
-#define MK_E_INVALIDEXTENSION _HRESULT_TYPEDEF_(0x800401E6L)
-#define MK_E_INTERMEDIATEINTERFACENOTSUPPORTED _HRESULT_TYPEDEF_(0x800401E7L)
-#define MK_E_NOTBINDABLE _HRESULT_TYPEDEF_(0x800401E8L)
-#define MK_E_NOTBOUND _HRESULT_TYPEDEF_(0x800401E9L)
-#define MK_E_CANTOPENFILE _HRESULT_TYPEDEF_(0x800401EAL)
-#define MK_E_MUSTBOTHERUSER _HRESULT_TYPEDEF_(0x800401EBL)
-#define MK_E_NOINVERSE _HRESULT_TYPEDEF_(0x800401ECL)
-#define MK_E_NOSTORAGE _HRESULT_TYPEDEF_(0x800401EDL)
-#define MK_E_NOPREFIX _HRESULT_TYPEDEF_(0x800401EEL)
-#define MK_E_ENUMERATION_FAILED _HRESULT_TYPEDEF_(0x800401EFL)
-#define CO_E_FIRST 0x800401F0L
-#define CO_E_LAST 0x800401FFL
-#define CO_S_FIRST 0x000401F0L
-#define CO_S_LAST 0x000401FFL
-#define CO_E_NOTINITIALIZED _HRESULT_TYPEDEF_(0x800401F0L)
-#define CO_E_ALREADYINITIALIZED _HRESULT_TYPEDEF_(0x800401F1L)
-#define CO_E_CANTDETERMINECLASS _HRESULT_TYPEDEF_(0x800401F2L)
-#define CO_E_CLASSSTRING _HRESULT_TYPEDEF_(0x800401F3L)
-#define CO_E_IIDSTRING _HRESULT_TYPEDEF_(0x800401F4L)
-#define CO_E_APPNOTFOUND _HRESULT_TYPEDEF_(0x800401F5L)
-#define CO_E_APPSINGLEUSE _HRESULT_TYPEDEF_(0x800401F6L)
-#define CO_E_ERRORINAPP _HRESULT_TYPEDEF_(0x800401F7L)
-#define CO_E_DLLNOTFOUND _HRESULT_TYPEDEF_(0x800401F8L)
-#define CO_E_ERRORINDLL _HRESULT_TYPEDEF_(0x800401F9L)
-#define CO_E_WRONGOSFORAPP _HRESULT_TYPEDEF_(0x800401FAL)
-#define CO_E_OBJNOTREG _HRESULT_TYPEDEF_(0x800401FBL)
-#define CO_E_OBJISREG _HRESULT_TYPEDEF_(0x800401FCL)
-#define CO_E_OBJNOTCONNECTED _HRESULT_TYPEDEF_(0x800401FDL)
-#define CO_E_APPDIDNTREG _HRESULT_TYPEDEF_(0x800401FEL)
-#define CO_E_RELEASED _HRESULT_TYPEDEF_(0x800401FFL)
-#define EVENT_E_FIRST 0x80040200L
-#define EVENT_E_LAST 0x8004021FL
-#define EVENT_S_FIRST 0x00040200L
-#define EVENT_S_LAST 0x0004021FL
-#define EVENT_S_SOME_SUBSCRIBERS_FAILED _HRESULT_TYPEDEF_(0x00040200L)
-#define EVENT_E_ALL_SUBSCRIBERS_FAILED _HRESULT_TYPEDEF_(0x80040201L)
-#define EVENT_S_NOSUBSCRIBERS _HRESULT_TYPEDEF_(0x00040202L)
-#define EVENT_E_QUERYSYNTAX _HRESULT_TYPEDEF_(0x80040203L)
-#define EVENT_E_QUERYFIELD _HRESULT_TYPEDEF_(0x80040204L)
-#define EVENT_E_INTERNALEXCEPTION _HRESULT_TYPEDEF_(0x80040205L)
-#define EVENT_E_INTERNALERROR _HRESULT_TYPEDEF_(0x80040206L)
-#define EVENT_E_INVALID_PER_USER_SID _HRESULT_TYPEDEF_(0x80040207L)
-#define EVENT_E_USER_EXCEPTION _HRESULT_TYPEDEF_(0x80040208L)
-#define EVENT_E_TOO_MANY_METHODS _HRESULT_TYPEDEF_(0x80040209L)
-#define EVENT_E_MISSING_EVENTCLASS _HRESULT_TYPEDEF_(0x8004020AL)
-#define EVENT_E_NOT_ALL_REMOVED _HRESULT_TYPEDEF_(0x8004020BL)
-#define EVENT_E_COMPLUS_NOT_INSTALLED _HRESULT_TYPEDEF_(0x8004020CL)
-#define EVENT_E_CANT_MODIFY_OR_DELETE_UNCONFIGURED_OBJECT _HRESULT_TYPEDEF_(0x8004020DL)
-#define EVENT_E_CANT_MODIFY_OR_DELETE_CONFIGURED_OBJECT _HRESULT_TYPEDEF_(0x8004020EL)
-#define EVENT_E_INVALID_EVENT_CLASS_PARTITION _HRESULT_TYPEDEF_(0x8004020FL)
-#define EVENT_E_PER_USER_SID_NOT_LOGGED_ON _HRESULT_TYPEDEF_(0x80040210L)
-#define XACT_E_FIRST 0x8004D000
-#define XACT_E_LAST 0x8004D029
-#define XACT_S_FIRST 0x0004D000
-#define XACT_S_LAST 0x0004D010
-#define XACT_E_ALREADYOTHERSINGLEPHASE _HRESULT_TYPEDEF_(0x8004D000L)
-#define XACT_E_CANTRETAIN _HRESULT_TYPEDEF_(0x8004D001L)
-#define XACT_E_COMMITFAILED _HRESULT_TYPEDEF_(0x8004D002L)
-#define XACT_E_COMMITPREVENTED _HRESULT_TYPEDEF_(0x8004D003L)
-#define XACT_E_HEURISTICABORT _HRESULT_TYPEDEF_(0x8004D004L)
-#define XACT_E_HEURISTICCOMMIT _HRESULT_TYPEDEF_(0x8004D005L)
-#define XACT_E_HEURISTICDAMAGE _HRESULT_TYPEDEF_(0x8004D006L)
-#define XACT_E_HEURISTICDANGER _HRESULT_TYPEDEF_(0x8004D007L)
-#define XACT_E_ISOLATIONLEVEL _HRESULT_TYPEDEF_(0x8004D008L)
-#define XACT_E_NOASYNC _HRESULT_TYPEDEF_(0x8004D009L)
-#define XACT_E_NOENLIST _HRESULT_TYPEDEF_(0x8004D00AL)
-#define XACT_E_NOISORETAIN _HRESULT_TYPEDEF_(0x8004D00BL)
-#define XACT_E_NORESOURCE _HRESULT_TYPEDEF_(0x8004D00CL)
-#define XACT_E_NOTCURRENT _HRESULT_TYPEDEF_(0x8004D00DL)
-#define XACT_E_NOTRANSACTION _HRESULT_TYPEDEF_(0x8004D00EL)
-#define XACT_E_NOTSUPPORTED _HRESULT_TYPEDEF_(0x8004D00FL)
-#define XACT_E_UNKNOWNRMGRID _HRESULT_TYPEDEF_(0x8004D010L)
-#define XACT_E_WRONGSTATE _HRESULT_TYPEDEF_(0x8004D011L)
-#define XACT_E_WRONGUOW _HRESULT_TYPEDEF_(0x8004D012L)
-#define XACT_E_XTIONEXISTS _HRESULT_TYPEDEF_(0x8004D013L)
-#define XACT_E_NOIMPORTOBJECT _HRESULT_TYPEDEF_(0x8004D014L)
-#define XACT_E_INVALIDCOOKIE _HRESULT_TYPEDEF_(0x8004D015L)
-#define XACT_E_INDOUBT _HRESULT_TYPEDEF_(0x8004D016L)
-#define XACT_E_NOTIMEOUT _HRESULT_TYPEDEF_(0x8004D017L)
-#define XACT_E_ALREADYINPROGRESS _HRESULT_TYPEDEF_(0x8004D018L)
-#define XACT_E_ABORTED _HRESULT_TYPEDEF_(0x8004D019L)
-#define XACT_E_LOGFULL _HRESULT_TYPEDEF_(0x8004D01AL)
-#define XACT_E_TMNOTAVAILABLE _HRESULT_TYPEDEF_(0x8004D01BL)
-#define XACT_E_CONNECTION_DOWN _HRESULT_TYPEDEF_(0x8004D01CL)
-#define XACT_E_CONNECTION_DENIED _HRESULT_TYPEDEF_(0x8004D01DL)
-#define XACT_E_REENLISTTIMEOUT _HRESULT_TYPEDEF_(0x8004D01EL)
-#define XACT_E_TIP_CONNECT_FAILED _HRESULT_TYPEDEF_(0x8004D01FL)
-#define XACT_E_TIP_PROTOCOL_ERROR _HRESULT_TYPEDEF_(0x8004D020L)
-#define XACT_E_TIP_PULL_FAILED _HRESULT_TYPEDEF_(0x8004D021L)
-#define XACT_E_DEST_TMNOTAVAILABLE _HRESULT_TYPEDEF_(0x8004D022L)
-#define XACT_E_TIP_DISABLED _HRESULT_TYPEDEF_(0x8004D023L)
-#define XACT_E_NETWORK_TX_DISABLED _HRESULT_TYPEDEF_(0x8004D024L)
-#define XACT_E_PARTNER_NETWORK_TX_DISABLED _HRESULT_TYPEDEF_(0x8004D025L)
-#define XACT_E_XA_TX_DISABLED _HRESULT_TYPEDEF_(0x8004D026L)
-#define XACT_E_UNABLE_TO_READ_DTC_CONFIG _HRESULT_TYPEDEF_(0x8004D027L)
-#define XACT_E_UNABLE_TO_LOAD_DTC_PROXY _HRESULT_TYPEDEF_(0x8004D028L)
-#define XACT_E_ABORTING _HRESULT_TYPEDEF_(0x8004D029L)
-#define XACT_E_CLERKNOTFOUND _HRESULT_TYPEDEF_(0x8004D080L)
-#define XACT_E_CLERKEXISTS _HRESULT_TYPEDEF_(0x8004D081L)
-#define XACT_E_RECOVERYINPROGRESS _HRESULT_TYPEDEF_(0x8004D082L)
-#define XACT_E_TRANSACTIONCLOSED _HRESULT_TYPEDEF_(0x8004D083L)
-#define XACT_E_INVALIDLSN _HRESULT_TYPEDEF_(0x8004D084L)
-#define XACT_E_REPLAYREQUEST _HRESULT_TYPEDEF_(0x8004D085L)
-#define XACT_S_ASYNC _HRESULT_TYPEDEF_(0x0004D000L)
-#define XACT_S_DEFECT _HRESULT_TYPEDEF_(0x0004D001L)
-#define XACT_S_READONLY _HRESULT_TYPEDEF_(0x0004D002L)
-#define XACT_S_SOMENORETAIN _HRESULT_TYPEDEF_(0x0004D003L)
-#define XACT_S_OKINFORM _HRESULT_TYPEDEF_(0x0004D004L)
-#define XACT_S_MADECHANGESCONTENT _HRESULT_TYPEDEF_(0x0004D005L)
-#define XACT_S_MADECHANGESINFORM _HRESULT_TYPEDEF_(0x0004D006L)
-#define XACT_S_ALLNORETAIN _HRESULT_TYPEDEF_(0x0004D007L)
-#define XACT_S_ABORTING _HRESULT_TYPEDEF_(0x0004D008L)
-#define XACT_S_SINGLEPHASE _HRESULT_TYPEDEF_(0x0004D009L)
-#define XACT_S_LOCALLY_OK _HRESULT_TYPEDEF_(0x0004D00AL)
-#define XACT_S_LASTRESOURCEMANAGER _HRESULT_TYPEDEF_(0x0004D010L)
-#define CONTEXT_E_FIRST 0x8004E000L
-#define CONTEXT_E_LAST 0x8004E02FL
-#define CONTEXT_S_FIRST 0x0004E000L
-#define CONTEXT_S_LAST 0x0004E02FL
-#define CONTEXT_E_ABORTED _HRESULT_TYPEDEF_(0x8004E002L)
-#define CONTEXT_E_ABORTING _HRESULT_TYPEDEF_(0x8004E003L)
-#define CONTEXT_E_NOCONTEXT _HRESULT_TYPEDEF_(0x8004E004L)
-#define CONTEXT_E_WOULD_DEADLOCK _HRESULT_TYPEDEF_(0x8004E005L)
-#define CONTEXT_E_SYNCH_TIMEOUT _HRESULT_TYPEDEF_(0x8004E006L)
-#define CONTEXT_E_OLDREF _HRESULT_TYPEDEF_(0x8004E007L)
-#define CONTEXT_E_ROLENOTFOUND _HRESULT_TYPEDEF_(0x8004E00CL)
-#define CONTEXT_E_TMNOTAVAILABLE _HRESULT_TYPEDEF_(0x8004E00FL)
-#define CO_E_ACTIVATIONFAILED _HRESULT_TYPEDEF_(0x8004E021L)
-#define CO_E_ACTIVATIONFAILED_EVENTLOGGED _HRESULT_TYPEDEF_(0x8004E022L)
-#define CO_E_ACTIVATIONFAILED_CATALOGERROR _HRESULT_TYPEDEF_(0x8004E023L)
-#define CO_E_ACTIVATIONFAILED_TIMEOUT _HRESULT_TYPEDEF_(0x8004E024L)
-#define CO_E_INITIALIZATIONFAILED _HRESULT_TYPEDEF_(0x8004E025L)
-#define CONTEXT_E_NOJIT _HRESULT_TYPEDEF_(0x8004E026L)
-#define CONTEXT_E_NOTRANSACTION _HRESULT_TYPEDEF_(0x8004E027L)
-#define CO_E_THREADINGMODEL_CHANGED _HRESULT_TYPEDEF_(0x8004E028L)
-#define CO_E_NOIISINTRINSICS _HRESULT_TYPEDEF_(0x8004E029L)
-#define CO_E_NOCOOKIES _HRESULT_TYPEDEF_(0x8004E02AL)
-#define CO_E_DBERROR _HRESULT_TYPEDEF_(0x8004E02BL)
-#define CO_E_NOTPOOLED _HRESULT_TYPEDEF_(0x8004E02CL)
-#define CO_E_NOTCONSTRUCTED _HRESULT_TYPEDEF_(0x8004E02DL)
-#define CO_E_NOSYNCHRONIZATION _HRESULT_TYPEDEF_(0x8004E02EL)
-#define CO_E_ISOLEVELMISMATCH _HRESULT_TYPEDEF_(0x8004E02FL)
-#define OLE_S_USEREG _HRESULT_TYPEDEF_(0x00040000L)
-#define OLE_S_STATIC _HRESULT_TYPEDEF_(0x00040001L)
-#define OLE_S_MAC_CLIPFORMAT _HRESULT_TYPEDEF_(0x00040002L)
-#define DRAGDROP_S_DROP _HRESULT_TYPEDEF_(0x00040100L)
-#define DRAGDROP_S_CANCEL _HRESULT_TYPEDEF_(0x00040101L)
-#define DRAGDROP_S_USEDEFAULTCURSORS _HRESULT_TYPEDEF_(0x00040102L)
-#define DATA_S_SAMEFORMATETC _HRESULT_TYPEDEF_(0x00040130L)
-#define VIEW_S_ALREADY_FROZEN _HRESULT_TYPEDEF_(0x00040140L)
-#define CACHE_S_FORMATETC_NOTSUPPORTED _HRESULT_TYPEDEF_(0x00040170L)
-#define CACHE_S_SAMECACHE _HRESULT_TYPEDEF_(0x00040171L)
-#define CACHE_S_SOMECACHES_NOTUPDATED _HRESULT_TYPEDEF_(0x00040172L)
-#define OLEOBJ_S_INVALIDVERB _HRESULT_TYPEDEF_(0x00040180L)
-#define OLEOBJ_S_CANNOT_DOVERB_NOW _HRESULT_TYPEDEF_(0x00040181L)
-#define OLEOBJ_S_INVALIDHWND _HRESULT_TYPEDEF_(0x00040182L)
-#define INPLACE_S_TRUNCATED _HRESULT_TYPEDEF_(0x000401A0L)
-#define CONVERT10_S_NO_PRESENTATION _HRESULT_TYPEDEF_(0x000401C0L)
-#define MK_S_REDUCED_TO_SELF _HRESULT_TYPEDEF_(0x000401E2L)
-#define MK_S_ME _HRESULT_TYPEDEF_(0x000401E4L)
-#define MK_S_HIM _HRESULT_TYPEDEF_(0x000401E5L)
-#define MK_S_US _HRESULT_TYPEDEF_(0x000401E6L)
-#define MK_S_MONIKERALREADYREGISTERED _HRESULT_TYPEDEF_(0x000401E7L)
-#define SCHED_S_TASK_READY _HRESULT_TYPEDEF_(0x00041300L)
-#define SCHED_S_TASK_RUNNING _HRESULT_TYPEDEF_(0x00041301L)
-#define SCHED_S_TASK_DISABLED _HRESULT_TYPEDEF_(0x00041302L)
-#define SCHED_S_TASK_HAS_NOT_RUN _HRESULT_TYPEDEF_(0x00041303L)
-#define SCHED_S_TASK_NO_MORE_RUNS _HRESULT_TYPEDEF_(0x00041304L)
-#define SCHED_S_TASK_NOT_SCHEDULED _HRESULT_TYPEDEF_(0x00041305L)
-#define SCHED_S_TASK_TERMINATED _HRESULT_TYPEDEF_(0x00041306L)
-#define SCHED_S_TASK_NO_VALID_TRIGGERS _HRESULT_TYPEDEF_(0x00041307L)
-#define SCHED_S_EVENT_TRIGGER _HRESULT_TYPEDEF_(0x00041308L)
-#define SCHED_E_TRIGGER_NOT_FOUND _HRESULT_TYPEDEF_(0x80041309L)
-#define SCHED_E_TASK_NOT_READY _HRESULT_TYPEDEF_(0x8004130AL)
-#define SCHED_E_TASK_NOT_RUNNING _HRESULT_TYPEDEF_(0x8004130BL)
-#define SCHED_E_SERVICE_NOT_INSTALLED _HRESULT_TYPEDEF_(0x8004130CL)
-#define SCHED_E_CANNOT_OPEN_TASK _HRESULT_TYPEDEF_(0x8004130DL)
-#define SCHED_E_INVALID_TASK _HRESULT_TYPEDEF_(0x8004130EL)
-#define SCHED_E_ACCOUNT_INFORMATION_NOT_SET _HRESULT_TYPEDEF_(0x8004130FL)
-#define SCHED_E_ACCOUNT_NAME_NOT_FOUND _HRESULT_TYPEDEF_(0x80041310L)
-#define SCHED_E_ACCOUNT_DBASE_CORRUPT _HRESULT_TYPEDEF_(0x80041311L)
-#define SCHED_E_NO_SECURITY_SERVICES _HRESULT_TYPEDEF_(0x80041312L)
-#define SCHED_E_UNKNOWN_OBJECT_VERSION _HRESULT_TYPEDEF_(0x80041313L)
-#define SCHED_E_UNSUPPORTED_ACCOUNT_OPTION _HRESULT_TYPEDEF_(0x80041314L)
-#define SCHED_E_SERVICE_NOT_RUNNING _HRESULT_TYPEDEF_(0x80041315L)
-#define CO_E_CLASS_CREATE_FAILED _HRESULT_TYPEDEF_(0x80080001L)
-#define CO_E_SCM_ERROR _HRESULT_TYPEDEF_(0x80080002L)
-#define CO_E_SCM_RPC_FAILURE _HRESULT_TYPEDEF_(0x80080003L)
-#define CO_E_BAD_PATH _HRESULT_TYPEDEF_(0x80080004L)
-#define CO_E_SERVER_EXEC_FAILURE _HRESULT_TYPEDEF_(0x80080005L)
-#define CO_E_OBJSRV_RPC_FAILURE _HRESULT_TYPEDEF_(0x80080006L)
-#define MK_E_NO_NORMALIZED _HRESULT_TYPEDEF_(0x80080007L)
-#define CO_E_SERVER_STOPPING _HRESULT_TYPEDEF_(0x80080008L)
-#define MEM_E_INVALID_ROOT _HRESULT_TYPEDEF_(0x80080009L)
-#define MEM_E_INVALID_LINK _HRESULT_TYPEDEF_(0x80080010L)
-#define MEM_E_INVALID_SIZE _HRESULT_TYPEDEF_(0x80080011L)
-#define CO_S_NOTALLINTERFACES _HRESULT_TYPEDEF_(0x00080012L)
-#define CO_S_MACHINENAMENOTFOUND _HRESULT_TYPEDEF_(0x00080013L)
-#define DISP_E_UNKNOWNINTERFACE _HRESULT_TYPEDEF_(0x80020001L)
-#define DISP_E_MEMBERNOTFOUND _HRESULT_TYPEDEF_(0x80020003L)
-#define DISP_E_PARAMNOTFOUND _HRESULT_TYPEDEF_(0x80020004L)
-#define DISP_E_TYPEMISMATCH _HRESULT_TYPEDEF_(0x80020005L)
-#define DISP_E_UNKNOWNNAME _HRESULT_TYPEDEF_(0x80020006L)
-#define DISP_E_NONAMEDARGS _HRESULT_TYPEDEF_(0x80020007L)
-#define DISP_E_BADVARTYPE _HRESULT_TYPEDEF_(0x80020008L)
-#define DISP_E_EXCEPTION _HRESULT_TYPEDEF_(0x80020009L)
-#define DISP_E_OVERFLOW _HRESULT_TYPEDEF_(0x8002000AL)
-#define DISP_E_BADINDEX _HRESULT_TYPEDEF_(0x8002000BL)
-#define DISP_E_UNKNOWNLCID _HRESULT_TYPEDEF_(0x8002000CL)
-#define DISP_E_ARRAYISLOCKED _HRESULT_TYPEDEF_(0x8002000DL)
-#define DISP_E_BADPARAMCOUNT _HRESULT_TYPEDEF_(0x8002000EL)
-#define DISP_E_PARAMNOTOPTIONAL _HRESULT_TYPEDEF_(0x8002000FL)
-#define DISP_E_BADCALLEE _HRESULT_TYPEDEF_(0x80020010L)
-#define DISP_E_NOTACOLLECTION _HRESULT_TYPEDEF_(0x80020011L)
-#define DISP_E_DIVBYZERO _HRESULT_TYPEDEF_(0x80020012L)
-#define DISP_E_BUFFERTOOSMALL _HRESULT_TYPEDEF_(0x80020013L)
-#define TYPE_E_BUFFERTOOSMALL _HRESULT_TYPEDEF_(0x80028016L)
-#define TYPE_E_FIELDNOTFOUND _HRESULT_TYPEDEF_(0x80028017L)
-#define TYPE_E_INVDATAREAD _HRESULT_TYPEDEF_(0x80028018L)
-#define TYPE_E_UNSUPFORMAT _HRESULT_TYPEDEF_(0x80028019L)
-#define TYPE_E_REGISTRYACCESS _HRESULT_TYPEDEF_(0x8002801CL)
-#define TYPE_E_LIBNOTREGISTERED _HRESULT_TYPEDEF_(0x8002801DL)
-#define TYPE_E_UNDEFINEDTYPE _HRESULT_TYPEDEF_(0x80028027L)
-#define TYPE_E_QUALIFIEDNAMEDISALLOWED _HRESULT_TYPEDEF_(0x80028028L)
-#define TYPE_E_INVALIDSTATE _HRESULT_TYPEDEF_(0x80028029L)
-#define TYPE_E_WRONGTYPEKIND _HRESULT_TYPEDEF_(0x8002802AL)
-#define TYPE_E_ELEMENTNOTFOUND _HRESULT_TYPEDEF_(0x8002802BL)
-#define TYPE_E_AMBIGUOUSNAME _HRESULT_TYPEDEF_(0x8002802CL)
-#define TYPE_E_NAMECONFLICT _HRESULT_TYPEDEF_(0x8002802DL)
-#define TYPE_E_UNKNOWNLCID _HRESULT_TYPEDEF_(0x8002802EL)
-#define TYPE_E_DLLFUNCTIONNOTFOUND _HRESULT_TYPEDEF_(0x8002802FL)
-#define TYPE_E_BADMODULEKIND _HRESULT_TYPEDEF_(0x800288BDL)
-#define TYPE_E_SIZETOOBIG _HRESULT_TYPEDEF_(0x800288C5L)
-#define TYPE_E_DUPLICATEID _HRESULT_TYPEDEF_(0x800288C6L)
-#define TYPE_E_INVALIDID _HRESULT_TYPEDEF_(0x800288CFL)
-#define TYPE_E_TYPEMISMATCH _HRESULT_TYPEDEF_(0x80028CA0L)
-#define TYPE_E_OUTOFBOUNDS _HRESULT_TYPEDEF_(0x80028CA1L)
-#define TYPE_E_IOERROR _HRESULT_TYPEDEF_(0x80028CA2L)
-#define TYPE_E_CANTCREATETMPFILE _HRESULT_TYPEDEF_(0x80028CA3L)
-#define TYPE_E_CANTLOADLIBRARY _HRESULT_TYPEDEF_(0x80029C4AL)
-#define TYPE_E_INCONSISTENTPROPFUNCS _HRESULT_TYPEDEF_(0x80029C83L)
-#define TYPE_E_CIRCULARTYPE _HRESULT_TYPEDEF_(0x80029C84L)
-#define STG_E_INVALIDFUNCTION _HRESULT_TYPEDEF_(0x80030001L)
-#define STG_E_FILENOTFOUND _HRESULT_TYPEDEF_(0x80030002L)
-#define STG_E_PATHNOTFOUND _HRESULT_TYPEDEF_(0x80030003L)
-#define STG_E_TOOMANYOPENFILES _HRESULT_TYPEDEF_(0x80030004L)
-#define STG_E_ACCESSDENIED _HRESULT_TYPEDEF_(0x80030005L)
-#define STG_E_INVALIDHANDLE _HRESULT_TYPEDEF_(0x80030006L)
-#define STG_E_INSUFFICIENTMEMORY _HRESULT_TYPEDEF_(0x80030008L)
-#define STG_E_INVALIDPOINTER _HRESULT_TYPEDEF_(0x80030009L)
-#define STG_E_NOMOREFILES _HRESULT_TYPEDEF_(0x80030012L)
-#define STG_E_DISKISWRITEPROTECTED _HRESULT_TYPEDEF_(0x80030013L)
-#define STG_E_SEEKERROR _HRESULT_TYPEDEF_(0x80030019L)
-#define STG_E_WRITEFAULT _HRESULT_TYPEDEF_(0x8003001DL)
-#define STG_E_READFAULT _HRESULT_TYPEDEF_(0x8003001EL)
-#define STG_E_SHAREVIOLATION _HRESULT_TYPEDEF_(0x80030020L)
-#define STG_E_LOCKVIOLATION _HRESULT_TYPEDEF_(0x80030021L)
-#define STG_E_FILEALREADYEXISTS _HRESULT_TYPEDEF_(0x80030050L)
-#define STG_E_INVALIDPARAMETER _HRESULT_TYPEDEF_(0x80030057L)
-#define STG_E_MEDIUMFULL _HRESULT_TYPEDEF_(0x80030070L)
-#define STG_E_PROPSETMISMATCHED _HRESULT_TYPEDEF_(0x800300F0L)
-#define STG_E_ABNORMALAPIEXIT _HRESULT_TYPEDEF_(0x800300FAL)
-#define STG_E_INVALIDHEADER _HRESULT_TYPEDEF_(0x800300FBL)
-#define STG_E_INVALIDNAME _HRESULT_TYPEDEF_(0x800300FCL)
-#define STG_E_UNKNOWN _HRESULT_TYPEDEF_(0x800300FDL)
-#define STG_E_UNIMPLEMENTEDFUNCTION _HRESULT_TYPEDEF_(0x800300FEL)
-#define STG_E_INVALIDFLAG _HRESULT_TYPEDEF_(0x800300FFL)
-#define STG_E_INUSE _HRESULT_TYPEDEF_(0x80030100L)
-#define STG_E_NOTCURRENT _HRESULT_TYPEDEF_(0x80030101L)
-#define STG_E_REVERTED _HRESULT_TYPEDEF_(0x80030102L)
-#define STG_E_CANTSAVE _HRESULT_TYPEDEF_(0x80030103L)
-#define STG_E_OLDFORMAT _HRESULT_TYPEDEF_(0x80030104L)
-#define STG_E_OLDDLL _HRESULT_TYPEDEF_(0x80030105L)
-#define STG_E_SHAREREQUIRED _HRESULT_TYPEDEF_(0x80030106L)
-#define STG_E_NOTFILEBASEDSTORAGE _HRESULT_TYPEDEF_(0x80030107L)
-#define STG_E_EXTANTMARSHALLINGS _HRESULT_TYPEDEF_(0x80030108L)
-#define STG_E_DOCFILECORRUPT _HRESULT_TYPEDEF_(0x80030109L)
-#define STG_E_BADBASEADDRESS _HRESULT_TYPEDEF_(0x80030110L)
-#define STG_E_DOCFILETOOLARGE _HRESULT_TYPEDEF_(0x80030111L)
-#define STG_E_NOTSIMPLEFORMAT _HRESULT_TYPEDEF_(0x80030112L)
-#define STG_E_INCOMPLETE _HRESULT_TYPEDEF_(0x80030201L)
-#define STG_E_TERMINATED _HRESULT_TYPEDEF_(0x80030202L)
-#define STG_S_CONVERTED _HRESULT_TYPEDEF_(0x00030200L)
-#define STG_S_BLOCK _HRESULT_TYPEDEF_(0x00030201L)
-#define STG_S_RETRYNOW _HRESULT_TYPEDEF_(0x00030202L)
-#define STG_S_MONITORING _HRESULT_TYPEDEF_(0x00030203L)
-#define STG_S_MULTIPLEOPENS _HRESULT_TYPEDEF_(0x00030204L)
-#define STG_S_CONSOLIDATIONFAILED _HRESULT_TYPEDEF_(0x00030205L)
-#define STG_S_CANNOTCONSOLIDATE _HRESULT_TYPEDEF_(0x00030206L)
-#define STG_E_STATUS_COPY_PROTECTION_FAILURE _HRESULT_TYPEDEF_(0x80030305L)
-#define STG_E_CSS_AUTHENTICATION_FAILURE _HRESULT_TYPEDEF_(0x80030306L)
-#define STG_E_CSS_KEY_NOT_PRESENT _HRESULT_TYPEDEF_(0x80030307L)
-#define STG_E_CSS_KEY_NOT_ESTABLISHED _HRESULT_TYPEDEF_(0x80030308L)
-#define STG_E_CSS_SCRAMBLED_SECTOR _HRESULT_TYPEDEF_(0x80030309L)
-#define STG_E_CSS_REGION_MISMATCH _HRESULT_TYPEDEF_(0x8003030AL)
-#define STG_E_RESETS_EXHAUSTED _HRESULT_TYPEDEF_(0x8003030BL)
-#define RPC_E_CALL_REJECTED _HRESULT_TYPEDEF_(0x80010001L)
-#define RPC_E_CALL_CANCELED _HRESULT_TYPEDEF_(0x80010002L)
-#define RPC_E_CANTPOST_INSENDCALL _HRESULT_TYPEDEF_(0x80010003L)
-#define RPC_E_CANTCALLOUT_INASYNCCALL _HRESULT_TYPEDEF_(0x80010004L)
-#define RPC_E_CANTCALLOUT_INEXTERNALCALL _HRESULT_TYPEDEF_(0x80010005L)
-#define RPC_E_CONNECTION_TERMINATED _HRESULT_TYPEDEF_(0x80010006L)
-#define RPC_E_SERVER_DIED _HRESULT_TYPEDEF_(0x80010007L)
-#define RPC_E_CLIENT_DIED _HRESULT_TYPEDEF_(0x80010008L)
-#define RPC_E_INVALID_DATAPACKET _HRESULT_TYPEDEF_(0x80010009L)
-#define RPC_E_CANTTRANSMIT_CALL _HRESULT_TYPEDEF_(0x8001000AL)
-#define RPC_E_CLIENT_CANTMARSHAL_DATA _HRESULT_TYPEDEF_(0x8001000BL)
-#define RPC_E_CLIENT_CANTUNMARSHAL_DATA _HRESULT_TYPEDEF_(0x8001000CL)
-#define RPC_E_SERVER_CANTMARSHAL_DATA _HRESULT_TYPEDEF_(0x8001000DL)
-#define RPC_E_SERVER_CANTUNMARSHAL_DATA _HRESULT_TYPEDEF_(0x8001000EL)
-#define RPC_E_INVALID_DATA _HRESULT_TYPEDEF_(0x8001000FL)
-#define RPC_E_INVALID_PARAMETER _HRESULT_TYPEDEF_(0x80010010L)
-#define RPC_E_CANTCALLOUT_AGAIN _HRESULT_TYPEDEF_(0x80010011L)
-#define RPC_E_SERVER_DIED_DNE _HRESULT_TYPEDEF_(0x80010012L)
-#define RPC_E_SYS_CALL_FAILED _HRESULT_TYPEDEF_(0x80010100L)
-#define RPC_E_OUT_OF_RESOURCES _HRESULT_TYPEDEF_(0x80010101L)
-#define RPC_E_ATTEMPTED_MULTITHREAD _HRESULT_TYPEDEF_(0x80010102L)
-#define RPC_E_NOT_REGISTERED _HRESULT_TYPEDEF_(0x80010103L)
-#define RPC_E_FAULT _HRESULT_TYPEDEF_(0x80010104L)
-#define RPC_E_SERVERFAULT _HRESULT_TYPEDEF_(0x80010105L)
-#define RPC_E_CHANGED_MODE _HRESULT_TYPEDEF_(0x80010106L)
-#define RPC_E_INVALIDMETHOD _HRESULT_TYPEDEF_(0x80010107L)
-#define RPC_E_DISCONNECTED _HRESULT_TYPEDEF_(0x80010108L)
-#define RPC_E_RETRY _HRESULT_TYPEDEF_(0x80010109L)
-#define RPC_E_SERVERCALL_RETRYLATER _HRESULT_TYPEDEF_(0x8001010AL)
-#define RPC_E_SERVERCALL_REJECTED _HRESULT_TYPEDEF_(0x8001010BL)
-#define RPC_E_INVALID_CALLDATA _HRESULT_TYPEDEF_(0x8001010CL)
-#define RPC_E_CANTCALLOUT_ININPUTSYNCCALL _HRESULT_TYPEDEF_(0x8001010DL)
-#define RPC_E_WRONG_THREAD _HRESULT_TYPEDEF_(0x8001010EL)
-#define RPC_E_THREAD_NOT_INIT _HRESULT_TYPEDEF_(0x8001010FL)
-#define RPC_E_VERSION_MISMATCH _HRESULT_TYPEDEF_(0x80010110L)
-#define RPC_E_INVALID_HEADER _HRESULT_TYPEDEF_(0x80010111L)
-#define RPC_E_INVALID_EXTENSION _HRESULT_TYPEDEF_(0x80010112L)
-#define RPC_E_INVALID_IPID _HRESULT_TYPEDEF_(0x80010113L)
-#define RPC_E_INVALID_OBJECT _HRESULT_TYPEDEF_(0x80010114L)
-#define RPC_S_CALLPENDING _HRESULT_TYPEDEF_(0x80010115L)
-#define RPC_S_WAITONTIMER _HRESULT_TYPEDEF_(0x80010116L)
-#define RPC_E_CALL_COMPLETE _HRESULT_TYPEDEF_(0x80010117L)
-#define RPC_E_UNSECURE_CALL _HRESULT_TYPEDEF_(0x80010118L)
-#define RPC_E_TOO_LATE _HRESULT_TYPEDEF_(0x80010119L)
-#define RPC_E_NO_GOOD_SECURITY_PACKAGES _HRESULT_TYPEDEF_(0x8001011AL)
-#define RPC_E_ACCESS_DENIED _HRESULT_TYPEDEF_(0x8001011BL)
-#define RPC_E_REMOTE_DISABLED _HRESULT_TYPEDEF_(0x8001011CL)
-#define RPC_E_INVALID_OBJREF _HRESULT_TYPEDEF_(0x8001011DL)
-#define RPC_E_NO_CONTEXT _HRESULT_TYPEDEF_(0x8001011EL)
-#define RPC_E_TIMEOUT _HRESULT_TYPEDEF_(0x8001011FL)
-#define RPC_E_NO_SYNC _HRESULT_TYPEDEF_(0x80010120L)
-#define RPC_E_FULLSIC_REQUIRED _HRESULT_TYPEDEF_(0x80010121L)
-#define RPC_E_INVALID_STD_NAME _HRESULT_TYPEDEF_(0x80010122L)
-#define CO_E_FAILEDTOIMPERSONATE _HRESULT_TYPEDEF_(0x80010123L)
-#define CO_E_FAILEDTOGETSECCTX _HRESULT_TYPEDEF_(0x80010124L)
-#define CO_E_FAILEDTOOPENTHREADTOKEN _HRESULT_TYPEDEF_(0x80010125L)
-#define CO_E_FAILEDTOGETTOKENINFO _HRESULT_TYPEDEF_(0x80010126L)
-#define CO_E_TRUSTEEDOESNTMATCHCLIENT _HRESULT_TYPEDEF_(0x80010127L)
-#define CO_E_FAILEDTOQUERYCLIENTBLANKET _HRESULT_TYPEDEF_(0x80010128L)
-#define CO_E_FAILEDTOSETDACL _HRESULT_TYPEDEF_(0x80010129L)
-#define CO_E_ACCESSCHECKFAILED _HRESULT_TYPEDEF_(0x8001012AL)
-#define CO_E_NETACCESSAPIFAILED _HRESULT_TYPEDEF_(0x8001012BL)
-#define CO_E_WRONGTRUSTEENAMESYNTAX _HRESULT_TYPEDEF_(0x8001012CL)
-#define CO_E_INVALIDSID _HRESULT_TYPEDEF_(0x8001012DL)
-#define CO_E_CONVERSIONFAILED _HRESULT_TYPEDEF_(0x8001012EL)
-#define CO_E_NOMATCHINGSIDFOUND _HRESULT_TYPEDEF_(0x8001012FL)
-#define CO_E_LOOKUPACCSIDFAILED _HRESULT_TYPEDEF_(0x80010130L)
-#define CO_E_NOMATCHINGNAMEFOUND _HRESULT_TYPEDEF_(0x80010131L)
-#define CO_E_LOOKUPACCNAMEFAILED _HRESULT_TYPEDEF_(0x80010132L)
-#define CO_E_SETSERLHNDLFAILED _HRESULT_TYPEDEF_(0x80010133L)
-#define CO_E_FAILEDTOGETWINDIR _HRESULT_TYPEDEF_(0x80010134L)
-#define CO_E_PATHTOOLONG _HRESULT_TYPEDEF_(0x80010135L)
-#define CO_E_FAILEDTOGENUUID _HRESULT_TYPEDEF_(0x80010136L)
-#define CO_E_FAILEDTOCREATEFILE _HRESULT_TYPEDEF_(0x80010137L)
-#define CO_E_FAILEDTOCLOSEHANDLE _HRESULT_TYPEDEF_(0x80010138L)
-#define CO_E_EXCEEDSYSACLLIMIT _HRESULT_TYPEDEF_(0x80010139L)
-#define CO_E_ACESINWRONGORDER _HRESULT_TYPEDEF_(0x8001013AL)
-#define CO_E_INCOMPATIBLESTREAMVERSION _HRESULT_TYPEDEF_(0x8001013BL)
-#define CO_E_FAILEDTOOPENPROCESSTOKEN _HRESULT_TYPEDEF_(0x8001013CL)
-#define CO_E_DECODEFAILED _HRESULT_TYPEDEF_(0x8001013DL)
-#define CO_E_ACNOTINITIALIZED _HRESULT_TYPEDEF_(0x8001013FL)
-#define CO_E_CANCEL_DISABLED _HRESULT_TYPEDEF_(0x80010140L)
-#define RPC_E_UNEXPECTED _HRESULT_TYPEDEF_(0x8001FFFFL)
-#define ERROR_AUDITING_DISABLED _HRESULT_TYPEDEF_(0xC0090001L)
-#define ERROR_ALL_SIDS_FILTERED _HRESULT_TYPEDEF_(0xC0090002L)
-#define NTE_BAD_UID _HRESULT_TYPEDEF_(0x80090001L)
-#define NTE_BAD_HASH _HRESULT_TYPEDEF_(0x80090002L)
-#define NTE_BAD_KEY _HRESULT_TYPEDEF_(0x80090003L)
-#define NTE_BAD_LEN _HRESULT_TYPEDEF_(0x80090004L)
-#define NTE_BAD_DATA _HRESULT_TYPEDEF_(0x80090005L)
-#define NTE_BAD_SIGNATURE _HRESULT_TYPEDEF_(0x80090006L)
-#define NTE_BAD_VER _HRESULT_TYPEDEF_(0x80090007L)
-#define NTE_BAD_ALGID _HRESULT_TYPEDEF_(0x80090008L)
-#define NTE_BAD_FLAGS _HRESULT_TYPEDEF_(0x80090009L)
-#define NTE_BAD_TYPE _HRESULT_TYPEDEF_(0x8009000AL)
-#define NTE_BAD_KEY_STATE _HRESULT_TYPEDEF_(0x8009000BL)
-#define NTE_BAD_HASH_STATE _HRESULT_TYPEDEF_(0x8009000CL)
-#define NTE_NO_KEY _HRESULT_TYPEDEF_(0x8009000DL)
-#define NTE_NO_MEMORY _HRESULT_TYPEDEF_(0x8009000EL)
-#define NTE_EXISTS _HRESULT_TYPEDEF_(0x8009000FL)
-#define NTE_PERM _HRESULT_TYPEDEF_(0x80090010L)
-#define NTE_NOT_FOUND _HRESULT_TYPEDEF_(0x80090011L)
-#define NTE_DOUBLE_ENCRYPT _HRESULT_TYPEDEF_(0x80090012L)
-#define NTE_BAD_PROVIDER _HRESULT_TYPEDEF_(0x80090013L)
-#define NTE_BAD_PROV_TYPE _HRESULT_TYPEDEF_(0x80090014L)
-#define NTE_BAD_PUBLIC_KEY _HRESULT_TYPEDEF_(0x80090015L)
-#define NTE_BAD_KEYSET _HRESULT_TYPEDEF_(0x80090016L)
-#define NTE_PROV_TYPE_NOT_DEF _HRESULT_TYPEDEF_(0x80090017L)
-#define NTE_PROV_TYPE_ENTRY_BAD _HRESULT_TYPEDEF_(0x80090018L)
-#define NTE_KEYSET_NOT_DEF _HRESULT_TYPEDEF_(0x80090019L)
-#define NTE_KEYSET_ENTRY_BAD _HRESULT_TYPEDEF_(0x8009001AL)
-#define NTE_PROV_TYPE_NO_MATCH _HRESULT_TYPEDEF_(0x8009001BL)
-#define NTE_SIGNATURE_FILE_BAD _HRESULT_TYPEDEF_(0x8009001CL)
-#define NTE_PROVIDER_DLL_FAIL _HRESULT_TYPEDEF_(0x8009001DL)
-#define NTE_PROV_DLL_NOT_FOUND _HRESULT_TYPEDEF_(0x8009001EL)
-#define NTE_BAD_KEYSET_PARAM _HRESULT_TYPEDEF_(0x8009001FL)
-#define NTE_FAIL _HRESULT_TYPEDEF_(0x80090020L)
-#define NTE_SYS_ERR _HRESULT_TYPEDEF_(0x80090021L)
-#define NTE_SILENT_CONTEXT _HRESULT_TYPEDEF_(0x80090022L)
-#define NTE_TOKEN_KEYSET_STORAGE_FULL _HRESULT_TYPEDEF_(0x80090023L)
-#define NTE_TEMPORARY_PROFILE _HRESULT_TYPEDEF_(0x80090024L)
-#define NTE_FIXEDPARAMETER _HRESULT_TYPEDEF_(0x80090025L)
-#define SEC_E_INSUFFICIENT_MEMORY _HRESULT_TYPEDEF_(0x80090300L)
-#define SEC_E_INVALID_HANDLE _HRESULT_TYPEDEF_(0x80090301L)
-#define SEC_E_UNSUPPORTED_FUNCTION _HRESULT_TYPEDEF_(0x80090302L)
-#define SEC_E_TARGET_UNKNOWN _HRESULT_TYPEDEF_(0x80090303L)
-#define SEC_E_INTERNAL_ERROR _HRESULT_TYPEDEF_(0x80090304L)
-#define SEC_E_SECPKG_NOT_FOUND _HRESULT_TYPEDEF_(0x80090305L)
-#define SEC_E_NOT_OWNER _HRESULT_TYPEDEF_(0x80090306L)
-#define SEC_E_CANNOT_INSTALL _HRESULT_TYPEDEF_(0x80090307L)
-#define SEC_E_INVALID_TOKEN _HRESULT_TYPEDEF_(0x80090308L)
-#define SEC_E_CANNOT_PACK _HRESULT_TYPEDEF_(0x80090309L)
-#define SEC_E_QOP_NOT_SUPPORTED _HRESULT_TYPEDEF_(0x8009030AL)
-#define SEC_E_NO_IMPERSONATION _HRESULT_TYPEDEF_(0x8009030BL)
-#define SEC_E_LOGON_DENIED _HRESULT_TYPEDEF_(0x8009030CL)
-#define SEC_E_UNKNOWN_CREDENTIALS _HRESULT_TYPEDEF_(0x8009030DL)
-#define SEC_E_NO_CREDENTIALS _HRESULT_TYPEDEF_(0x8009030EL)
-#define SEC_E_MESSAGE_ALTERED _HRESULT_TYPEDEF_(0x8009030FL)
-#define SEC_E_OUT_OF_SEQUENCE _HRESULT_TYPEDEF_(0x80090310L)
-#define SEC_E_NO_AUTHENTICATING_AUTHORITY _HRESULT_TYPEDEF_(0x80090311L)
-#define SEC_I_CONTINUE_NEEDED _HRESULT_TYPEDEF_(0x00090312L)
-#define SEC_I_COMPLETE_NEEDED _HRESULT_TYPEDEF_(0x00090313L)
-#define SEC_I_COMPLETE_AND_CONTINUE _HRESULT_TYPEDEF_(0x00090314L)
-#define SEC_I_LOCAL_LOGON _HRESULT_TYPEDEF_(0x00090315L)
-#define SEC_E_BAD_PKGID _HRESULT_TYPEDEF_(0x80090316L)
-#define SEC_E_CONTEXT_EXPIRED _HRESULT_TYPEDEF_(0x80090317L)
-#define SEC_I_CONTEXT_EXPIRED _HRESULT_TYPEDEF_(0x00090317L)
-#define SEC_E_INCOMPLETE_MESSAGE _HRESULT_TYPEDEF_(0x80090318L)
-#define SEC_E_INCOMPLETE_CREDENTIALS _HRESULT_TYPEDEF_(0x80090320L)
-#define SEC_E_BUFFER_TOO_SMALL _HRESULT_TYPEDEF_(0x80090321L)
-#define SEC_I_INCOMPLETE_CREDENTIALS _HRESULT_TYPEDEF_(0x00090320L)
-#define SEC_I_RENEGOTIATE _HRESULT_TYPEDEF_(0x00090321L)
-#define SEC_E_WRONG_PRINCIPAL _HRESULT_TYPEDEF_(0x80090322L)
-#define SEC_I_NO_LSA_CONTEXT _HRESULT_TYPEDEF_(0x00090323L)
-#define SEC_E_TIME_SKEW _HRESULT_TYPEDEF_(0x80090324L)
-#define SEC_E_UNTRUSTED_ROOT _HRESULT_TYPEDEF_(0x80090325L)
-#define SEC_E_ILLEGAL_MESSAGE _HRESULT_TYPEDEF_(0x80090326L)
-#define SEC_E_CERT_UNKNOWN _HRESULT_TYPEDEF_(0x80090327L)
-#define SEC_E_CERT_EXPIRED _HRESULT_TYPEDEF_(0x80090328L)
-#define SEC_E_ENCRYPT_FAILURE _HRESULT_TYPEDEF_(0x80090329L)
-#define SEC_E_DECRYPT_FAILURE _HRESULT_TYPEDEF_(0x80090330L)
-#define SEC_E_ALGORITHM_MISMATCH _HRESULT_TYPEDEF_(0x80090331L)
-#define SEC_E_SECURITY_QOS_FAILED _HRESULT_TYPEDEF_(0x80090332L)
-#define SEC_E_UNFINISHED_CONTEXT_DELETED _HRESULT_TYPEDEF_(0x80090333L)
-#define SEC_E_NO_TGT_REPLY _HRESULT_TYPEDEF_(0x80090334L)
-#define SEC_E_NO_IP_ADDRESSES _HRESULT_TYPEDEF_(0x80090335L)
-#define SEC_E_WRONG_CREDENTIAL_HANDLE _HRESULT_TYPEDEF_(0x80090336L)
-#define SEC_E_CRYPTO_SYSTEM_INVALID _HRESULT_TYPEDEF_(0x80090337L)
-#define SEC_E_MAX_REFERRALS_EXCEEDED _HRESULT_TYPEDEF_(0x80090338L)
-#define SEC_E_MUST_BE_KDC _HRESULT_TYPEDEF_(0x80090339L)
-#define SEC_E_STRONG_CRYPTO_NOT_SUPPORTED _HRESULT_TYPEDEF_(0x8009033AL)
-#define SEC_E_TOO_MANY_PRINCIPALS _HRESULT_TYPEDEF_(0x8009033BL)
-#define SEC_E_NO_PA_DATA _HRESULT_TYPEDEF_(0x8009033CL)
-#define SEC_E_PKINIT_NAME_MISMATCH _HRESULT_TYPEDEF_(0x8009033DL)
-#define SEC_E_SMARTCARD_LOGON_REQUIRED _HRESULT_TYPEDEF_(0x8009033EL)
-#define SEC_E_SHUTDOWN_IN_PROGRESS _HRESULT_TYPEDEF_(0x8009033FL)
-#define SEC_E_KDC_INVALID_REQUEST _HRESULT_TYPEDEF_(0x80090340L)
-#define SEC_E_KDC_UNABLE_TO_REFER _HRESULT_TYPEDEF_(0x80090341L)
-#define SEC_E_KDC_UNKNOWN_ETYPE _HRESULT_TYPEDEF_(0x80090342L)
-#define SEC_E_UNSUPPORTED_PREAUTH _HRESULT_TYPEDEF_(0x80090343L)
-#define SEC_E_DELEGATION_REQUIRED _HRESULT_TYPEDEF_(0x80090345L)
-#define SEC_E_BAD_BINDINGS _HRESULT_TYPEDEF_(0x80090346L)
-#define SEC_E_MULTIPLE_ACCOUNTS _HRESULT_TYPEDEF_(0x80090347L)
-#define SEC_E_NO_KERB_KEY _HRESULT_TYPEDEF_(0x80090348L)
-#define SEC_E_CERT_WRONG_USAGE _HRESULT_TYPEDEF_(0x80090349L)
-#define SEC_E_DOWNGRADE_DETECTED _HRESULT_TYPEDEF_(0x80090350L)
-#define SEC_E_SMARTCARD_CERT_REVOKED _HRESULT_TYPEDEF_(0x80090351L)
-#define SEC_E_ISSUING_CA_UNTRUSTED _HRESULT_TYPEDEF_(0x80090352L)
-#define SEC_E_REVOCATION_OFFLINE_C _HRESULT_TYPEDEF_(0x80090353L)
-#define SEC_E_PKINIT_CLIENT_FAILURE _HRESULT_TYPEDEF_(0x80090354L)
-#define SEC_E_SMARTCARD_CERT_EXPIRED _HRESULT_TYPEDEF_(0x80090355L)
-#define SEC_E_NO_S4U_PROT_SUPPORT _HRESULT_TYPEDEF_(0x80090356L)
-#define SEC_E_CROSSREALM_DELEGATION_FAILURE _HRESULT_TYPEDEF_(0x80090357L)
-#define SEC_E_REVOCATION_OFFLINE_KDC _HRESULT_TYPEDEF_(0x80090358L)
-#define SEC_E_ISSUING_CA_UNTRUSTED_KDC _HRESULT_TYPEDEF_(0x80090359L)
-#define SEC_E_KDC_CERT_EXPIRED _HRESULT_TYPEDEF_(0x8009035AL)
-#define SEC_E_KDC_CERT_REVOKED _HRESULT_TYPEDEF_(0x8009035BL)
-#define SEC_E_NO_SPM SEC_E_INTERNAL_ERROR
-#define SEC_E_NOT_SUPPORTED SEC_E_UNSUPPORTED_FUNCTION
-#define CRYPT_E_MSG_ERROR _HRESULT_TYPEDEF_(0x80091001L)
-#define CRYPT_E_UNKNOWN_ALGO _HRESULT_TYPEDEF_(0x80091002L)
-#define CRYPT_E_OID_FORMAT _HRESULT_TYPEDEF_(0x80091003L)
-#define CRYPT_E_INVALID_MSG_TYPE _HRESULT_TYPEDEF_(0x80091004L)
-#define CRYPT_E_UNEXPECTED_ENCODING _HRESULT_TYPEDEF_(0x80091005L)
-#define CRYPT_E_AUTH_ATTR_MISSING _HRESULT_TYPEDEF_(0x80091006L)
-#define CRYPT_E_HASH_VALUE _HRESULT_TYPEDEF_(0x80091007L)
-#define CRYPT_E_INVALID_INDEX _HRESULT_TYPEDEF_(0x80091008L)
-#define CRYPT_E_ALREADY_DECRYPTED _HRESULT_TYPEDEF_(0x80091009L)
-#define CRYPT_E_NOT_DECRYPTED _HRESULT_TYPEDEF_(0x8009100AL)
-#define CRYPT_E_RECIPIENT_NOT_FOUND _HRESULT_TYPEDEF_(0x8009100BL)
-#define CRYPT_E_CONTROL_TYPE _HRESULT_TYPEDEF_(0x8009100CL)
-#define CRYPT_E_ISSUER_SERIALNUMBER _HRESULT_TYPEDEF_(0x8009100DL)
-#define CRYPT_E_SIGNER_NOT_FOUND _HRESULT_TYPEDEF_(0x8009100EL)
-#define CRYPT_E_ATTRIBUTES_MISSING _HRESULT_TYPEDEF_(0x8009100FL)
-#define CRYPT_E_STREAM_MSG_NOT_READY _HRESULT_TYPEDEF_(0x80091010L)
-#define CRYPT_E_STREAM_INSUFFICIENT_DATA _HRESULT_TYPEDEF_(0x80091011L)
-#define CRYPT_I_NEW_PROTECTION_REQUIRED _HRESULT_TYPEDEF_(0x00091012L)
-#define CRYPT_E_BAD_LEN _HRESULT_TYPEDEF_(0x80092001L)
-#define CRYPT_E_BAD_ENCODE _HRESULT_TYPEDEF_(0x80092002L)
-#define CRYPT_E_FILE_ERROR _HRESULT_TYPEDEF_(0x80092003L)
-#define CRYPT_E_NOT_FOUND _HRESULT_TYPEDEF_(0x80092004L)
-#define CRYPT_E_EXISTS _HRESULT_TYPEDEF_(0x80092005L)
-#define CRYPT_E_NO_PROVIDER _HRESULT_TYPEDEF_(0x80092006L)
-#define CRYPT_E_SELF_SIGNED _HRESULT_TYPEDEF_(0x80092007L)
-#define CRYPT_E_DELETED_PREV _HRESULT_TYPEDEF_(0x80092008L)
-#define CRYPT_E_NO_MATCH _HRESULT_TYPEDEF_(0x80092009L)
-#define CRYPT_E_UNEXPECTED_MSG_TYPE _HRESULT_TYPEDEF_(0x8009200AL)
-#define CRYPT_E_NO_KEY_PROPERTY _HRESULT_TYPEDEF_(0x8009200BL)
-#define CRYPT_E_NO_DECRYPT_CERT _HRESULT_TYPEDEF_(0x8009200CL)
-#define CRYPT_E_BAD_MSG _HRESULT_TYPEDEF_(0x8009200DL)
-#define CRYPT_E_NO_SIGNER _HRESULT_TYPEDEF_(0x8009200EL)
-#define CRYPT_E_PENDING_CLOSE _HRESULT_TYPEDEF_(0x8009200FL)
-#define CRYPT_E_REVOKED _HRESULT_TYPEDEF_(0x80092010L)
-#define CRYPT_E_NO_REVOCATION_DLL _HRESULT_TYPEDEF_(0x80092011L)
-#define CRYPT_E_NO_REVOCATION_CHECK _HRESULT_TYPEDEF_(0x80092012L)
-#define CRYPT_E_REVOCATION_OFFLINE _HRESULT_TYPEDEF_(0x80092013L)
-#define CRYPT_E_NOT_IN_REVOCATION_DATABASE _HRESULT_TYPEDEF_(0x80092014L)
-#define CRYPT_E_INVALID_NUMERIC_STRING _HRESULT_TYPEDEF_(0x80092020L)
-#define CRYPT_E_INVALID_PRINTABLE_STRING _HRESULT_TYPEDEF_(0x80092021L)
-#define CRYPT_E_INVALID_IA5_STRING _HRESULT_TYPEDEF_(0x80092022L)
-#define CRYPT_E_INVALID_X500_STRING _HRESULT_TYPEDEF_(0x80092023L)
-#define CRYPT_E_NOT_CHAR_STRING _HRESULT_TYPEDEF_(0x80092024L)
-#define CRYPT_E_FILERESIZED _HRESULT_TYPEDEF_(0x80092025L)
-#define CRYPT_E_SECURITY_SETTINGS _HRESULT_TYPEDEF_(0x80092026L)
-#define CRYPT_E_NO_VERIFY_USAGE_DLL _HRESULT_TYPEDEF_(0x80092027L)
-#define CRYPT_E_NO_VERIFY_USAGE_CHECK _HRESULT_TYPEDEF_(0x80092028L)
-#define CRYPT_E_VERIFY_USAGE_OFFLINE _HRESULT_TYPEDEF_(0x80092029L)
-#define CRYPT_E_NOT_IN_CTL _HRESULT_TYPEDEF_(0x8009202AL)
-#define CRYPT_E_NO_TRUSTED_SIGNER _HRESULT_TYPEDEF_(0x8009202BL)
-#define CRYPT_E_MISSING_PUBKEY_PARA _HRESULT_TYPEDEF_(0x8009202CL)
-#define CRYPT_E_OSS_ERROR _HRESULT_TYPEDEF_(0x80093000L)
-#define OSS_MORE_BUF _HRESULT_TYPEDEF_(0x80093001L)
-#define OSS_NEGATIVE_UINTEGER _HRESULT_TYPEDEF_(0x80093002L)
-#define OSS_PDU_RANGE _HRESULT_TYPEDEF_(0x80093003L)
-#define OSS_MORE_INPUT _HRESULT_TYPEDEF_(0x80093004L)
-#define OSS_DATA_ERROR _HRESULT_TYPEDEF_(0x80093005L)
-#define OSS_BAD_ARG _HRESULT_TYPEDEF_(0x80093006L)
-#define OSS_BAD_VERSION _HRESULT_TYPEDEF_(0x80093007L)
-#define OSS_OUT_MEMORY _HRESULT_TYPEDEF_(0x80093008L)
-#define OSS_PDU_MISMATCH _HRESULT_TYPEDEF_(0x80093009L)
-#define OSS_LIMITED _HRESULT_TYPEDEF_(0x8009300AL)
-#define OSS_BAD_PTR _HRESULT_TYPEDEF_(0x8009300BL)
-#define OSS_BAD_TIME _HRESULT_TYPEDEF_(0x8009300CL)
-#define OSS_INDEFINITE_NOT_SUPPORTED _HRESULT_TYPEDEF_(0x8009300DL)
-#define OSS_MEM_ERROR _HRESULT_TYPEDEF_(0x8009300EL)
-#define OSS_BAD_TABLE _HRESULT_TYPEDEF_(0x8009300FL)
-#define OSS_TOO_LONG _HRESULT_TYPEDEF_(0x80093010L)
-#define OSS_CONSTRAINT_VIOLATED _HRESULT_TYPEDEF_(0x80093011L)
-#define OSS_FATAL_ERROR _HRESULT_TYPEDEF_(0x80093012L)
-#define OSS_ACCESS_SERIALIZATION_ERROR _HRESULT_TYPEDEF_(0x80093013L)
-#define OSS_NULL_TBL _HRESULT_TYPEDEF_(0x80093014L)
-#define OSS_NULL_FCN _HRESULT_TYPEDEF_(0x80093015L)
-#define OSS_BAD_ENCRULES _HRESULT_TYPEDEF_(0x80093016L)
-#define OSS_UNAVAIL_ENCRULES _HRESULT_TYPEDEF_(0x80093017L)
-#define OSS_CANT_OPEN_TRACE_WINDOW _HRESULT_TYPEDEF_(0x80093018L)
-#define OSS_UNIMPLEMENTED _HRESULT_TYPEDEF_(0x80093019L)
-#define OSS_OID_DLL_NOT_LINKED _HRESULT_TYPEDEF_(0x8009301AL)
-#define OSS_CANT_OPEN_TRACE_FILE _HRESULT_TYPEDEF_(0x8009301BL)
-#define OSS_TRACE_FILE_ALREADY_OPEN _HRESULT_TYPEDEF_(0x8009301CL)
-#define OSS_TABLE_MISMATCH _HRESULT_TYPEDEF_(0x8009301DL)
-#define OSS_TYPE_NOT_SUPPORTED _HRESULT_TYPEDEF_(0x8009301EL)
-#define OSS_REAL_DLL_NOT_LINKED _HRESULT_TYPEDEF_(0x8009301FL)
-#define OSS_REAL_CODE_NOT_LINKED _HRESULT_TYPEDEF_(0x80093020L)
-#define OSS_OUT_OF_RANGE _HRESULT_TYPEDEF_(0x80093021L)
-#define OSS_COPIER_DLL_NOT_LINKED _HRESULT_TYPEDEF_(0x80093022L)
-#define OSS_CONSTRAINT_DLL_NOT_LINKED _HRESULT_TYPEDEF_(0x80093023L)
-#define OSS_COMPARATOR_DLL_NOT_LINKED _HRESULT_TYPEDEF_(0x80093024L)
-#define OSS_COMPARATOR_CODE_NOT_LINKED _HRESULT_TYPEDEF_(0x80093025L)
-#define OSS_MEM_MGR_DLL_NOT_LINKED _HRESULT_TYPEDEF_(0x80093026L)
-#define OSS_PDV_DLL_NOT_LINKED _HRESULT_TYPEDEF_(0x80093027L)
-#define OSS_PDV_CODE_NOT_LINKED _HRESULT_TYPEDEF_(0x80093028L)
-#define OSS_API_DLL_NOT_LINKED _HRESULT_TYPEDEF_(0x80093029L)
-#define OSS_BERDER_DLL_NOT_LINKED _HRESULT_TYPEDEF_(0x8009302AL)
-#define OSS_PER_DLL_NOT_LINKED _HRESULT_TYPEDEF_(0x8009302BL)
-#define OSS_OPEN_TYPE_ERROR _HRESULT_TYPEDEF_(0x8009302CL)
-#define OSS_MUTEX_NOT_CREATED _HRESULT_TYPEDEF_(0x8009302DL)
-#define OSS_CANT_CLOSE_TRACE_FILE _HRESULT_TYPEDEF_(0x8009302EL)
-#define CRYPT_E_ASN1_ERROR _HRESULT_TYPEDEF_(0x80093100L)
-#define CRYPT_E_ASN1_INTERNAL _HRESULT_TYPEDEF_(0x80093101L)
-#define CRYPT_E_ASN1_EOD _HRESULT_TYPEDEF_(0x80093102L)
-#define CRYPT_E_ASN1_CORRUPT _HRESULT_TYPEDEF_(0x80093103L)
-#define CRYPT_E_ASN1_LARGE _HRESULT_TYPEDEF_(0x80093104L)
-#define CRYPT_E_ASN1_CONSTRAINT _HRESULT_TYPEDEF_(0x80093105L)
-#define CRYPT_E_ASN1_MEMORY _HRESULT_TYPEDEF_(0x80093106L)
-#define CRYPT_E_ASN1_OVERFLOW _HRESULT_TYPEDEF_(0x80093107L)
-#define CRYPT_E_ASN1_BADPDU _HRESULT_TYPEDEF_(0x80093108L)
-#define CRYPT_E_ASN1_BADARGS _HRESULT_TYPEDEF_(0x80093109L)
-#define CRYPT_E_ASN1_BADREAL _HRESULT_TYPEDEF_(0x8009310AL)
-#define CRYPT_E_ASN1_BADTAG _HRESULT_TYPEDEF_(0x8009310BL)
-#define CRYPT_E_ASN1_CHOICE _HRESULT_TYPEDEF_(0x8009310CL)
-#define CRYPT_E_ASN1_RULE _HRESULT_TYPEDEF_(0x8009310DL)
-#define CRYPT_E_ASN1_UTF8 _HRESULT_TYPEDEF_(0x8009310EL)
-#define CRYPT_E_ASN1_PDU_TYPE _HRESULT_TYPEDEF_(0x80093133L)
-#define CRYPT_E_ASN1_NYI _HRESULT_TYPEDEF_(0x80093134L)
-#define CRYPT_E_ASN1_EXTENDED _HRESULT_TYPEDEF_(0x80093201L)
-#define CRYPT_E_ASN1_NOEOD _HRESULT_TYPEDEF_(0x80093202L)
-#define CERTSRV_E_BAD_REQUESTSUBJECT _HRESULT_TYPEDEF_(0x80094001L)
-#define CERTSRV_E_NO_REQUEST _HRESULT_TYPEDEF_(0x80094002L)
-#define CERTSRV_E_BAD_REQUESTSTATUS _HRESULT_TYPEDEF_(0x80094003L)
-#define CERTSRV_E_PROPERTY_EMPTY _HRESULT_TYPEDEF_(0x80094004L)
-#define CERTSRV_E_INVALID_CA_CERTIFICATE _HRESULT_TYPEDEF_(0x80094005L)
-#define CERTSRV_E_SERVER_SUSPENDED _HRESULT_TYPEDEF_(0x80094006L)
-#define CERTSRV_E_ENCODING_LENGTH _HRESULT_TYPEDEF_(0x80094007L)
-#define CERTSRV_E_ROLECONFLICT _HRESULT_TYPEDEF_(0x80094008L)
-#define CERTSRV_E_RESTRICTEDOFFICER _HRESULT_TYPEDEF_(0x80094009L)
-#define CERTSRV_E_KEY_ARCHIVAL_NOT_CONFIGURED _HRESULT_TYPEDEF_(0x8009400AL)
-#define CERTSRV_E_NO_VALID_KRA _HRESULT_TYPEDEF_(0x8009400BL)
-#define CERTSRV_E_BAD_REQUEST_KEY_ARCHIVAL _HRESULT_TYPEDEF_(0x8009400CL)
-#define CERTSRV_E_NO_CAADMIN_DEFINED _HRESULT_TYPEDEF_(0x8009400DL)
-#define CERTSRV_E_BAD_RENEWAL_CERT_ATTRIBUTE _HRESULT_TYPEDEF_(0x8009400EL)
-#define CERTSRV_E_NO_DB_SESSIONS _HRESULT_TYPEDEF_(0x8009400FL)
-#define CERTSRV_E_ALIGNMENT_FAULT _HRESULT_TYPEDEF_(0x80094010L)
-#define CERTSRV_E_ENROLL_DENIED _HRESULT_TYPEDEF_(0x80094011L)
-#define CERTSRV_E_TEMPLATE_DENIED _HRESULT_TYPEDEF_(0x80094012L)
-#define CERTSRV_E_DOWNLEVEL_DC_SSL_OR_UPGRADE _HRESULT_TYPEDEF_(0x80094013L)
-#define CERTSRV_E_UNSUPPORTED_CERT_TYPE _HRESULT_TYPEDEF_(0x80094800L)
-#define CERTSRV_E_NO_CERT_TYPE _HRESULT_TYPEDEF_(0x80094801L)
-#define CERTSRV_E_TEMPLATE_CONFLICT _HRESULT_TYPEDEF_(0x80094802L)
-#define CERTSRV_E_SUBJECT_ALT_NAME_REQUIRED _HRESULT_TYPEDEF_(0x80094803L)
-#define CERTSRV_E_ARCHIVED_KEY_REQUIRED _HRESULT_TYPEDEF_(0x80094804L)
-#define CERTSRV_E_SMIME_REQUIRED _HRESULT_TYPEDEF_(0x80094805L)
-#define CERTSRV_E_BAD_RENEWAL_SUBJECT _HRESULT_TYPEDEF_(0x80094806L)
-#define CERTSRV_E_BAD_TEMPLATE_VERSION _HRESULT_TYPEDEF_(0x80094807L)
-#define CERTSRV_E_TEMPLATE_POLICY_REQUIRED _HRESULT_TYPEDEF_(0x80094808L)
-#define CERTSRV_E_SIGNATURE_POLICY_REQUIRED _HRESULT_TYPEDEF_(0x80094809L)
-#define CERTSRV_E_SIGNATURE_COUNT _HRESULT_TYPEDEF_(0x8009480AL)
-#define CERTSRV_E_SIGNATURE_REJECTED _HRESULT_TYPEDEF_(0x8009480BL)
-#define CERTSRV_E_ISSUANCE_POLICY_REQUIRED _HRESULT_TYPEDEF_(0x8009480CL)
-#define CERTSRV_E_SUBJECT_UPN_REQUIRED _HRESULT_TYPEDEF_(0x8009480DL)
-#define CERTSRV_E_SUBJECT_DIRECTORY_GUID_REQUIRED _HRESULT_TYPEDEF_(0x8009480EL)
-#define CERTSRV_E_SUBJECT_DNS_REQUIRED _HRESULT_TYPEDEF_(0x8009480FL)
-#define CERTSRV_E_ARCHIVED_KEY_UNEXPECTED _HRESULT_TYPEDEF_(0x80094810L)
-#define CERTSRV_E_KEY_LENGTH _HRESULT_TYPEDEF_(0x80094811L)
-#define CERTSRV_E_SUBJECT_EMAIL_REQUIRED _HRESULT_TYPEDEF_(0x80094812L)
-#define CERTSRV_E_UNKNOWN_CERT_TYPE _HRESULT_TYPEDEF_(0x80094813L)
-#define CERTSRV_E_CERT_TYPE_OVERLAP _HRESULT_TYPEDEF_(0x80094814L)
-#define XENROLL_E_KEY_NOT_EXPORTABLE _HRESULT_TYPEDEF_(0x80095000L)
-#define XENROLL_E_CANNOT_ADD_ROOT_CERT _HRESULT_TYPEDEF_(0x80095001L)
-#define XENROLL_E_RESPONSE_KA_HASH_NOT_FOUND _HRESULT_TYPEDEF_(0x80095002L)
-#define XENROLL_E_RESPONSE_UNEXPECTED_KA_HASH _HRESULT_TYPEDEF_(0x80095003L)
-#define XENROLL_E_RESPONSE_KA_HASH_MISMATCH _HRESULT_TYPEDEF_(0x80095004L)
-#define XENROLL_E_KEYSPEC_SMIME_MISMATCH _HRESULT_TYPEDEF_(0x80095005L)
-#define TRUST_E_SYSTEM_ERROR _HRESULT_TYPEDEF_(0x80096001L)
-#define TRUST_E_NO_SIGNER_CERT _HRESULT_TYPEDEF_(0x80096002L)
-#define TRUST_E_COUNTER_SIGNER _HRESULT_TYPEDEF_(0x80096003L)
-#define TRUST_E_CERT_SIGNATURE _HRESULT_TYPEDEF_(0x80096004L)
-#define TRUST_E_TIME_STAMP _HRESULT_TYPEDEF_(0x80096005L)
-#define TRUST_E_BAD_DIGEST _HRESULT_TYPEDEF_(0x80096010L)
-#define TRUST_E_BASIC_CONSTRAINTS _HRESULT_TYPEDEF_(0x80096019L)
-#define TRUST_E_FINANCIAL_CRITERIA _HRESULT_TYPEDEF_(0x8009601EL)
-#define MSSIPOTF_E_OUTOFMEMRANGE _HRESULT_TYPEDEF_(0x80097001L)
-#define MSSIPOTF_E_CANTGETOBJECT _HRESULT_TYPEDEF_(0x80097002L)
-#define MSSIPOTF_E_NOHEADTABLE _HRESULT_TYPEDEF_(0x80097003L)
-#define MSSIPOTF_E_BAD_MAGICNUMBER _HRESULT_TYPEDEF_(0x80097004L)
-#define MSSIPOTF_E_BAD_OFFSET_TABLE _HRESULT_TYPEDEF_(0x80097005L)
-#define MSSIPOTF_E_TABLE_TAGORDER _HRESULT_TYPEDEF_(0x80097006L)
-#define MSSIPOTF_E_TABLE_LONGWORD _HRESULT_TYPEDEF_(0x80097007L)
-#define MSSIPOTF_E_BAD_FIRST_TABLE_PLACEMENT _HRESULT_TYPEDEF_(0x80097008L)
-#define MSSIPOTF_E_TABLES_OVERLAP _HRESULT_TYPEDEF_(0x80097009L)
-#define MSSIPOTF_E_TABLE_PADBYTES _HRESULT_TYPEDEF_(0x8009700AL)
-#define MSSIPOTF_E_FILETOOSMALL _HRESULT_TYPEDEF_(0x8009700BL)
-#define MSSIPOTF_E_TABLE_CHECKSUM _HRESULT_TYPEDEF_(0x8009700CL)
-#define MSSIPOTF_E_FILE_CHECKSUM _HRESULT_TYPEDEF_(0x8009700DL)
-#define MSSIPOTF_E_FAILED_POLICY _HRESULT_TYPEDEF_(0x80097010L)
-#define MSSIPOTF_E_FAILED_HINTS_CHECK _HRESULT_TYPEDEF_(0x80097011L)
-#define MSSIPOTF_E_NOT_OPENTYPE _HRESULT_TYPEDEF_(0x80097012L)
-#define MSSIPOTF_E_FILE _HRESULT_TYPEDEF_(0x80097013L)
-#define MSSIPOTF_E_CRYPT _HRESULT_TYPEDEF_(0x80097014L)
-#define MSSIPOTF_E_BADVERSION _HRESULT_TYPEDEF_(0x80097015L)
-#define MSSIPOTF_E_DSIG_STRUCTURE _HRESULT_TYPEDEF_(0x80097016L)
-#define MSSIPOTF_E_PCONST_CHECK _HRESULT_TYPEDEF_(0x80097017L)
-#define MSSIPOTF_E_STRUCTURE _HRESULT_TYPEDEF_(0x80097018L)
-#define NTE_OP_OK 0
-#define TRUST_E_PROVIDER_UNKNOWN _HRESULT_TYPEDEF_(0x800B0001L)
-#define TRUST_E_ACTION_UNKNOWN _HRESULT_TYPEDEF_(0x800B0002L)
-#define TRUST_E_SUBJECT_FORM_UNKNOWN _HRESULT_TYPEDEF_(0x800B0003L)
-#define TRUST_E_SUBJECT_NOT_TRUSTED _HRESULT_TYPEDEF_(0x800B0004L)
-#define DIGSIG_E_ENCODE _HRESULT_TYPEDEF_(0x800B0005L)
-#define DIGSIG_E_DECODE _HRESULT_TYPEDEF_(0x800B0006L)
-#define DIGSIG_E_EXTENSIBILITY _HRESULT_TYPEDEF_(0x800B0007L)
-#define DIGSIG_E_CRYPTO _HRESULT_TYPEDEF_(0x800B0008L)
-#define PERSIST_E_SIZEDEFINITE _HRESULT_TYPEDEF_(0x800B0009L)
-#define PERSIST_E_SIZEINDEFINITE _HRESULT_TYPEDEF_(0x800B000AL)
-#define PERSIST_E_NOTSELFSIZING _HRESULT_TYPEDEF_(0x800B000BL)
-#define TRUST_E_NOSIGNATURE _HRESULT_TYPEDEF_(0x800B0100L)
-#define CERT_E_EXPIRED _HRESULT_TYPEDEF_(0x800B0101L)
-#define CERT_E_VALIDITYPERIODNESTING _HRESULT_TYPEDEF_(0x800B0102L)
-#define CERT_E_ROLE _HRESULT_TYPEDEF_(0x800B0103L)
-#define CERT_E_PATHLENCONST _HRESULT_TYPEDEF_(0x800B0104L)
-#define CERT_E_CRITICAL _HRESULT_TYPEDEF_(0x800B0105L)
-#define CERT_E_PURPOSE _HRESULT_TYPEDEF_(0x800B0106L)
-#define CERT_E_ISSUERCHAINING _HRESULT_TYPEDEF_(0x800B0107L)
-#define CERT_E_MALFORMED _HRESULT_TYPEDEF_(0x800B0108L)
-#define CERT_E_UNTRUSTEDROOT _HRESULT_TYPEDEF_(0x800B0109L)
-#define CERT_E_CHAINING _HRESULT_TYPEDEF_(0x800B010AL)
-#define TRUST_E_FAIL _HRESULT_TYPEDEF_(0x800B010BL)
-#define CERT_E_REVOKED _HRESULT_TYPEDEF_(0x800B010CL)
-#define CERT_E_UNTRUSTEDTESTROOT _HRESULT_TYPEDEF_(0x800B010DL)
-#define CERT_E_REVOCATION_FAILURE _HRESULT_TYPEDEF_(0x800B010EL)
-#define CERT_E_CN_NO_MATCH _HRESULT_TYPEDEF_(0x800B010FL)
-#define CERT_E_WRONG_USAGE _HRESULT_TYPEDEF_(0x800B0110L)
-#define TRUST_E_EXPLICIT_DISTRUST _HRESULT_TYPEDEF_(0x800B0111L)
-#define CERT_E_UNTRUSTEDCA _HRESULT_TYPEDEF_(0x800B0112L)
-#define CERT_E_INVALID_POLICY _HRESULT_TYPEDEF_(0x800B0113L)
-#define CERT_E_INVALID_NAME _HRESULT_TYPEDEF_(0x800B0114L)
-#define HRESULT_FROM_SETUPAPI(x) ((((x) & (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR))==(APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR)) ? ((HRESULT) (((x) & 0x0000FFFF) | (FACILITY_SETUPAPI << 16) | 0x80000000)) : HRESULT_FROM_WIN32(x))
-#define SPAPI_E_EXPECTED_SECTION_NAME _HRESULT_TYPEDEF_(0x800F0000L)
-#define SPAPI_E_BAD_SECTION_NAME_LINE _HRESULT_TYPEDEF_(0x800F0001L)
-#define SPAPI_E_SECTION_NAME_TOO_LONG _HRESULT_TYPEDEF_(0x800F0002L)
-#define SPAPI_E_GENERAL_SYNTAX _HRESULT_TYPEDEF_(0x800F0003L)
-#define SPAPI_E_WRONG_INF_STYLE _HRESULT_TYPEDEF_(0x800F0100L)
-#define SPAPI_E_SECTION_NOT_FOUND _HRESULT_TYPEDEF_(0x800F0101L)
-#define SPAPI_E_LINE_NOT_FOUND _HRESULT_TYPEDEF_(0x800F0102L)
-#define SPAPI_E_NO_BACKUP _HRESULT_TYPEDEF_(0x800F0103L)
-#define SPAPI_E_NO_ASSOCIATED_CLASS _HRESULT_TYPEDEF_(0x800F0200L)
-#define SPAPI_E_CLASS_MISMATCH _HRESULT_TYPEDEF_(0x800F0201L)
-#define SPAPI_E_DUPLICATE_FOUND _HRESULT_TYPEDEF_(0x800F0202L)
-#define SPAPI_E_NO_DRIVER_SELECTED _HRESULT_TYPEDEF_(0x800F0203L)
-#define SPAPI_E_KEY_DOES_NOT_EXIST _HRESULT_TYPEDEF_(0x800F0204L)
-#define SPAPI_E_INVALID_DEVINST_NAME _HRESULT_TYPEDEF_(0x800F0205L)
-#define SPAPI_E_INVALID_CLASS _HRESULT_TYPEDEF_(0x800F0206L)
-#define SPAPI_E_DEVINST_ALREADY_EXISTS _HRESULT_TYPEDEF_(0x800F0207L)
-#define SPAPI_E_DEVINFO_NOT_REGISTERED _HRESULT_TYPEDEF_(0x800F0208L)
-#define SPAPI_E_INVALID_REG_PROPERTY _HRESULT_TYPEDEF_(0x800F0209L)
-#define SPAPI_E_NO_INF _HRESULT_TYPEDEF_(0x800F020AL)
-#define SPAPI_E_NO_SUCH_DEVINST _HRESULT_TYPEDEF_(0x800F020BL)
-#define SPAPI_E_CANT_LOAD_CLASS_ICON _HRESULT_TYPEDEF_(0x800F020CL)
-#define SPAPI_E_INVALID_CLASS_INSTALLER _HRESULT_TYPEDEF_(0x800F020DL)
-#define SPAPI_E_DI_DO_DEFAULT _HRESULT_TYPEDEF_(0x800F020EL)
-#define SPAPI_E_DI_NOFILECOPY _HRESULT_TYPEDEF_(0x800F020FL)
-#define SPAPI_E_INVALID_HWPROFILE _HRESULT_TYPEDEF_(0x800F0210L)
-#define SPAPI_E_NO_DEVICE_SELECTED _HRESULT_TYPEDEF_(0x800F0211L)
-#define SPAPI_E_DEVINFO_LIST_LOCKED _HRESULT_TYPEDEF_(0x800F0212L)
-#define SPAPI_E_DEVINFO_DATA_LOCKED _HRESULT_TYPEDEF_(0x800F0213L)
-#define SPAPI_E_DI_BAD_PATH _HRESULT_TYPEDEF_(0x800F0214L)
-#define SPAPI_E_NO_CLASSINSTALL_PARAMS _HRESULT_TYPEDEF_(0x800F0215L)
-#define SPAPI_E_FILEQUEUE_LOCKED _HRESULT_TYPEDEF_(0x800F0216L)
-#define SPAPI_E_BAD_SERVICE_INSTALLSECT _HRESULT_TYPEDEF_(0x800F0217L)
-#define SPAPI_E_NO_CLASS_DRIVER_LIST _HRESULT_TYPEDEF_(0x800F0218L)
-#define SPAPI_E_NO_ASSOCIATED_SERVICE _HRESULT_TYPEDEF_(0x800F0219L)
-#define SPAPI_E_NO_DEFAULT_DEVICE_INTERFACE _HRESULT_TYPEDEF_(0x800F021AL)
-#define SPAPI_E_DEVICE_INTERFACE_ACTIVE _HRESULT_TYPEDEF_(0x800F021BL)
-#define SPAPI_E_DEVICE_INTERFACE_REMOVED _HRESULT_TYPEDEF_(0x800F021CL)
-#define SPAPI_E_BAD_INTERFACE_INSTALLSECT _HRESULT_TYPEDEF_(0x800F021DL)
-#define SPAPI_E_NO_SUCH_INTERFACE_CLASS _HRESULT_TYPEDEF_(0x800F021EL)
-#define SPAPI_E_INVALID_REFERENCE_STRING _HRESULT_TYPEDEF_(0x800F021FL)
-#define SPAPI_E_INVALID_MACHINENAME _HRESULT_TYPEDEF_(0x800F0220L)
-#define SPAPI_E_REMOTE_COMM_FAILURE _HRESULT_TYPEDEF_(0x800F0221L)
-#define SPAPI_E_MACHINE_UNAVAILABLE _HRESULT_TYPEDEF_(0x800F0222L)
-#define SPAPI_E_NO_CONFIGMGR_SERVICES _HRESULT_TYPEDEF_(0x800F0223L)
-#define SPAPI_E_INVALID_PROPPAGE_PROVIDER _HRESULT_TYPEDEF_(0x800F0224L)
-#define SPAPI_E_NO_SUCH_DEVICE_INTERFACE _HRESULT_TYPEDEF_(0x800F0225L)
-#define SPAPI_E_DI_POSTPROCESSING_REQUIRED _HRESULT_TYPEDEF_(0x800F0226L)
-#define SPAPI_E_INVALID_COINSTALLER _HRESULT_TYPEDEF_(0x800F0227L)
-#define SPAPI_E_NO_COMPAT_DRIVERS _HRESULT_TYPEDEF_(0x800F0228L)
-#define SPAPI_E_NO_DEVICE_ICON _HRESULT_TYPEDEF_(0x800F0229L)
-#define SPAPI_E_INVALID_INF_LOGCONFIG _HRESULT_TYPEDEF_(0x800F022AL)
-#define SPAPI_E_DI_DONT_INSTALL _HRESULT_TYPEDEF_(0x800F022BL)
-#define SPAPI_E_INVALID_FILTER_DRIVER _HRESULT_TYPEDEF_(0x800F022CL)
-#define SPAPI_E_NON_WINDOWS_NT_DRIVER _HRESULT_TYPEDEF_(0x800F022DL)
-#define SPAPI_E_NON_WINDOWS_DRIVER _HRESULT_TYPEDEF_(0x800F022EL)
-#define SPAPI_E_NO_CATALOG_FOR_OEM_INF _HRESULT_TYPEDEF_(0x800F022FL)
-#define SPAPI_E_DEVINSTALL_QUEUE_NONNATIVE _HRESULT_TYPEDEF_(0x800F0230L)
-#define SPAPI_E_NOT_DISABLEABLE _HRESULT_TYPEDEF_(0x800F0231L)
-#define SPAPI_E_CANT_REMOVE_DEVINST _HRESULT_TYPEDEF_(0x800F0232L)
-#define SPAPI_E_INVALID_TARGET _HRESULT_TYPEDEF_(0x800F0233L)
-#define SPAPI_E_DRIVER_NONNATIVE _HRESULT_TYPEDEF_(0x800F0234L)
-#define SPAPI_E_IN_WOW64 _HRESULT_TYPEDEF_(0x800F0235L)
-#define SPAPI_E_SET_SYSTEM_RESTORE_POINT _HRESULT_TYPEDEF_(0x800F0236L)
-#define SPAPI_E_INCORRECTLY_COPIED_INF _HRESULT_TYPEDEF_(0x800F0237L)
-#define SPAPI_E_SCE_DISABLED _HRESULT_TYPEDEF_(0x800F0238L)
-#define SPAPI_E_UNKNOWN_EXCEPTION _HRESULT_TYPEDEF_(0x800F0239L)
-#define SPAPI_E_PNP_REGISTRY_ERROR _HRESULT_TYPEDEF_(0x800F023AL)
-#define SPAPI_E_REMOTE_REQUEST_UNSUPPORTED _HRESULT_TYPEDEF_(0x800F023BL)
-#define SPAPI_E_NOT_AN_INSTALLED_OEM_INF _HRESULT_TYPEDEF_(0x800F023CL)
-#define SPAPI_E_INF_IN_USE_BY_DEVICES _HRESULT_TYPEDEF_(0x800F023DL)
-#define SPAPI_E_DI_FUNCTION_OBSOLETE _HRESULT_TYPEDEF_(0x800F023EL)
-#define SPAPI_E_NO_AUTHENTICODE_CATALOG _HRESULT_TYPEDEF_(0x800F023FL)
-#define SPAPI_E_AUTHENTICODE_DISALLOWED _HRESULT_TYPEDEF_(0x800F0240L)
-#define SPAPI_E_AUTHENTICODE_TRUSTED_PUBLISHER _HRESULT_TYPEDEF_(0x800F0241L)
-#define SPAPI_E_AUTHENTICODE_TRUST_NOT_ESTABLISHED _HRESULT_TYPEDEF_(0x800F0242L)
-#define SPAPI_E_AUTHENTICODE_PUBLISHER_NOT_TRUSTED _HRESULT_TYPEDEF_(0x800F0243L)
-#define SPAPI_E_SIGNATURE_OSATTRIBUTE_MISMATCH _HRESULT_TYPEDEF_(0x800F0244L)
-#define SPAPI_E_ONLY_VALIDATE_VIA_AUTHENTICODE _HRESULT_TYPEDEF_(0x800F0245L)
-#define SPAPI_E_UNRECOVERABLE_STACK_OVERFLOW _HRESULT_TYPEDEF_(0x800F0300L)
-#define SPAPI_E_ERROR_NOT_INSTALLED _HRESULT_TYPEDEF_(0x800F1000L)
-#define SCARD_S_SUCCESS NO_ERROR
-#define SCARD_F_INTERNAL_ERROR _HRESULT_TYPEDEF_(0x80100001L)
-#define SCARD_E_CANCELLED _HRESULT_TYPEDEF_(0x80100002L)
-#define SCARD_E_INVALID_HANDLE _HRESULT_TYPEDEF_(0x80100003L)
-#define SCARD_E_INVALID_PARAMETER _HRESULT_TYPEDEF_(0x80100004L)
-#define SCARD_E_INVALID_TARGET _HRESULT_TYPEDEF_(0x80100005L)
-#define SCARD_E_NO_MEMORY _HRESULT_TYPEDEF_(0x80100006L)
-#define SCARD_F_WAITED_TOO_LONG _HRESULT_TYPEDEF_(0x80100007L)
-#define SCARD_E_INSUFFICIENT_BUFFER _HRESULT_TYPEDEF_(0x80100008L)
-#define SCARD_E_UNKNOWN_READER _HRESULT_TYPEDEF_(0x80100009L)
-#define SCARD_E_TIMEOUT _HRESULT_TYPEDEF_(0x8010000AL)
-#define SCARD_E_SHARING_VIOLATION _HRESULT_TYPEDEF_(0x8010000BL)
-#define SCARD_E_NO_SMARTCARD _HRESULT_TYPEDEF_(0x8010000CL)
-#define SCARD_E_UNKNOWN_CARD _HRESULT_TYPEDEF_(0x8010000DL)
-#define SCARD_E_CANT_DISPOSE _HRESULT_TYPEDEF_(0x8010000EL)
-#define SCARD_E_PROTO_MISMATCH _HRESULT_TYPEDEF_(0x8010000FL)
-#define SCARD_E_NOT_READY _HRESULT_TYPEDEF_(0x80100010L)
-#define SCARD_E_INVALID_VALUE _HRESULT_TYPEDEF_(0x80100011L)
-#define SCARD_E_SYSTEM_CANCELLED _HRESULT_TYPEDEF_(0x80100012L)
-#define SCARD_F_COMM_ERROR _HRESULT_TYPEDEF_(0x80100013L)
-#define SCARD_F_UNKNOWN_ERROR _HRESULT_TYPEDEF_(0x80100014L)
-#define SCARD_E_INVALID_ATR _HRESULT_TYPEDEF_(0x80100015L)
-#define SCARD_E_NOT_TRANSACTED _HRESULT_TYPEDEF_(0x80100016L)
-#define SCARD_E_READER_UNAVAILABLE _HRESULT_TYPEDEF_(0x80100017L)
-#define SCARD_P_SHUTDOWN _HRESULT_TYPEDEF_(0x80100018L)
-#define SCARD_E_PCI_TOO_SMALL _HRESULT_TYPEDEF_(0x80100019L)
-#define SCARD_E_READER_UNSUPPORTED _HRESULT_TYPEDEF_(0x8010001AL)
-#define SCARD_E_DUPLICATE_READER _HRESULT_TYPEDEF_(0x8010001BL)
-#define SCARD_E_CARD_UNSUPPORTED _HRESULT_TYPEDEF_(0x8010001CL)
-#define SCARD_E_NO_SERVICE _HRESULT_TYPEDEF_(0x8010001DL)
-#define SCARD_E_SERVICE_STOPPED _HRESULT_TYPEDEF_(0x8010001EL)
-#define SCARD_E_UNEXPECTED _HRESULT_TYPEDEF_(0x8010001FL)
-#define SCARD_E_ICC_INSTALLATION _HRESULT_TYPEDEF_(0x80100020L)
-#define SCARD_E_ICC_CREATEORDER _HRESULT_TYPEDEF_(0x80100021L)
-#define SCARD_E_UNSUPPORTED_FEATURE _HRESULT_TYPEDEF_(0x80100022L)
-#define SCARD_E_DIR_NOT_FOUND _HRESULT_TYPEDEF_(0x80100023L)
-#define SCARD_E_FILE_NOT_FOUND _HRESULT_TYPEDEF_(0x80100024L)
-#define SCARD_E_NO_DIR _HRESULT_TYPEDEF_(0x80100025L)
-#define SCARD_E_NO_FILE _HRESULT_TYPEDEF_(0x80100026L)
-#define SCARD_E_NO_ACCESS _HRESULT_TYPEDEF_(0x80100027L)
-#define SCARD_E_WRITE_TOO_MANY _HRESULT_TYPEDEF_(0x80100028L)
-#define SCARD_E_BAD_SEEK _HRESULT_TYPEDEF_(0x80100029L)
-#define SCARD_E_INVALID_CHV _HRESULT_TYPEDEF_(0x8010002AL)
-#define SCARD_E_UNKNOWN_RES_MNG _HRESULT_TYPEDEF_(0x8010002BL)
-#define SCARD_E_NO_SUCH_CERTIFICATE _HRESULT_TYPEDEF_(0x8010002CL)
-#define SCARD_E_CERTIFICATE_UNAVAILABLE _HRESULT_TYPEDEF_(0x8010002DL)
-#define SCARD_E_NO_READERS_AVAILABLE _HRESULT_TYPEDEF_(0x8010002EL)
-#define SCARD_E_COMM_DATA_LOST _HRESULT_TYPEDEF_(0x8010002FL)
-#define SCARD_E_NO_KEY_CONTAINER _HRESULT_TYPEDEF_(0x80100030L)
-#define SCARD_E_SERVER_TOO_BUSY _HRESULT_TYPEDEF_(0x80100031L)
-#define SCARD_W_UNSUPPORTED_CARD _HRESULT_TYPEDEF_(0x80100065L)
-#define SCARD_W_UNRESPONSIVE_CARD _HRESULT_TYPEDEF_(0x80100066L)
-#define SCARD_W_UNPOWERED_CARD _HRESULT_TYPEDEF_(0x80100067L)
-#define SCARD_W_RESET_CARD _HRESULT_TYPEDEF_(0x80100068L)
-#define SCARD_W_REMOVED_CARD _HRESULT_TYPEDEF_(0x80100069L)
-#define SCARD_W_SECURITY_VIOLATION _HRESULT_TYPEDEF_(0x8010006AL)
-#define SCARD_W_WRONG_CHV _HRESULT_TYPEDEF_(0x8010006BL)
-#define SCARD_W_CHV_BLOCKED _HRESULT_TYPEDEF_(0x8010006CL)
-#define SCARD_W_EOF _HRESULT_TYPEDEF_(0x8010006DL)
-#define SCARD_W_CANCELLED_BY_USER _HRESULT_TYPEDEF_(0x8010006EL)
-#define SCARD_W_CARD_NOT_AUTHENTICATED _HRESULT_TYPEDEF_(0x8010006FL)
-#define COMADMIN_E_OBJECTERRORS _HRESULT_TYPEDEF_(0x80110401L)
-#define COMADMIN_E_OBJECTINVALID _HRESULT_TYPEDEF_(0x80110402L)
-#define COMADMIN_E_KEYMISSING _HRESULT_TYPEDEF_(0x80110403L)
-#define COMADMIN_E_ALREADYINSTALLED _HRESULT_TYPEDEF_(0x80110404L)
-#define COMADMIN_E_APP_FILE_WRITEFAIL _HRESULT_TYPEDEF_(0x80110407L)
-#define COMADMIN_E_APP_FILE_READFAIL _HRESULT_TYPEDEF_(0x80110408L)
-#define COMADMIN_E_APP_FILE_VERSION _HRESULT_TYPEDEF_(0x80110409L)
-#define COMADMIN_E_BADPATH _HRESULT_TYPEDEF_(0x8011040AL)
-#define COMADMIN_E_APPLICATIONEXISTS _HRESULT_TYPEDEF_(0x8011040BL)
-#define COMADMIN_E_ROLEEXISTS _HRESULT_TYPEDEF_(0x8011040CL)
-#define COMADMIN_E_CANTCOPYFILE _HRESULT_TYPEDEF_(0x8011040DL)
-#define COMADMIN_E_NOUSER _HRESULT_TYPEDEF_(0x8011040FL)
-#define COMADMIN_E_INVALIDUSERIDS _HRESULT_TYPEDEF_(0x80110410L)
-#define COMADMIN_E_NOREGISTRYCLSID _HRESULT_TYPEDEF_(0x80110411L)
-#define COMADMIN_E_BADREGISTRYPROGID _HRESULT_TYPEDEF_(0x80110412L)
-#define COMADMIN_E_AUTHENTICATIONLEVEL _HRESULT_TYPEDEF_(0x80110413L)
-#define COMADMIN_E_USERPASSWDNOTVALID _HRESULT_TYPEDEF_(0x80110414L)
-#define COMADMIN_E_CLSIDORIIDMISMATCH _HRESULT_TYPEDEF_(0x80110418L)
-#define COMADMIN_E_REMOTEINTERFACE _HRESULT_TYPEDEF_(0x80110419L)
-#define COMADMIN_E_DLLREGISTERSERVER _HRESULT_TYPEDEF_(0x8011041AL)
-#define COMADMIN_E_NOSERVERSHARE _HRESULT_TYPEDEF_(0x8011041BL)
-#define COMADMIN_E_DLLLOADFAILED _HRESULT_TYPEDEF_(0x8011041DL)
-#define COMADMIN_E_BADREGISTRYLIBID _HRESULT_TYPEDEF_(0x8011041EL)
-#define COMADMIN_E_APPDIRNOTFOUND _HRESULT_TYPEDEF_(0x8011041FL)
-#define COMADMIN_E_REGISTRARFAILED _HRESULT_TYPEDEF_(0x80110423L)
-#define COMADMIN_E_COMPFILE_DOESNOTEXIST _HRESULT_TYPEDEF_(0x80110424L)
-#define COMADMIN_E_COMPFILE_LOADDLLFAIL _HRESULT_TYPEDEF_(0x80110425L)
-#define COMADMIN_E_COMPFILE_GETCLASSOBJ _HRESULT_TYPEDEF_(0x80110426L)
-#define COMADMIN_E_COMPFILE_CLASSNOTAVAIL _HRESULT_TYPEDEF_(0x80110427L)
-#define COMADMIN_E_COMPFILE_BADTLB _HRESULT_TYPEDEF_(0x80110428L)
-#define COMADMIN_E_COMPFILE_NOTINSTALLABLE _HRESULT_TYPEDEF_(0x80110429L)
-#define COMADMIN_E_NOTCHANGEABLE _HRESULT_TYPEDEF_(0x8011042AL)
-#define COMADMIN_E_NOTDELETEABLE _HRESULT_TYPEDEF_(0x8011042BL)
-#define COMADMIN_E_SESSION _HRESULT_TYPEDEF_(0x8011042CL)
-#define COMADMIN_E_COMP_MOVE_LOCKED _HRESULT_TYPEDEF_(0x8011042DL)
-#define COMADMIN_E_COMP_MOVE_BAD_DEST _HRESULT_TYPEDEF_(0x8011042EL)
-#define COMADMIN_E_REGISTERTLB _HRESULT_TYPEDEF_(0x80110430L)
-#define COMADMIN_E_SYSTEMAPP _HRESULT_TYPEDEF_(0x80110433L)
-#define COMADMIN_E_COMPFILE_NOREGISTRAR _HRESULT_TYPEDEF_(0x80110434L)
-#define COMADMIN_E_COREQCOMPINSTALLED _HRESULT_TYPEDEF_(0x80110435L)
-#define COMADMIN_E_SERVICENOTINSTALLED _HRESULT_TYPEDEF_(0x80110436L)
-#define COMADMIN_E_PROPERTYSAVEFAILED _HRESULT_TYPEDEF_(0x80110437L)
-#define COMADMIN_E_OBJECTEXISTS _HRESULT_TYPEDEF_(0x80110438L)
-#define COMADMIN_E_COMPONENTEXISTS _HRESULT_TYPEDEF_(0x80110439L)
-#define COMADMIN_E_REGFILE_CORRUPT _HRESULT_TYPEDEF_(0x8011043BL)
-#define COMADMIN_E_PROPERTY_OVERFLOW _HRESULT_TYPEDEF_(0x8011043CL)
-#define COMADMIN_E_NOTINREGISTRY _HRESULT_TYPEDEF_(0x8011043EL)
-#define COMADMIN_E_OBJECTNOTPOOLABLE _HRESULT_TYPEDEF_(0x8011043FL)
-#define COMADMIN_E_APPLID_MATCHES_CLSID _HRESULT_TYPEDEF_(0x80110446L)
-#define COMADMIN_E_ROLE_DOES_NOT_EXIST _HRESULT_TYPEDEF_(0x80110447L)
-#define COMADMIN_E_START_APP_NEEDS_COMPONENTS _HRESULT_TYPEDEF_(0x80110448L)
-#define COMADMIN_E_REQUIRES_DIFFERENT_PLATFORM _HRESULT_TYPEDEF_(0x80110449L)
-#define COMADMIN_E_CAN_NOT_EXPORT_APP_PROXY _HRESULT_TYPEDEF_(0x8011044AL)
-#define COMADMIN_E_CAN_NOT_START_APP _HRESULT_TYPEDEF_(0x8011044BL)
-#define COMADMIN_E_CAN_NOT_EXPORT_SYS_APP _HRESULT_TYPEDEF_(0x8011044CL)
-#define COMADMIN_E_CANT_SUBSCRIBE_TO_COMPONENT _HRESULT_TYPEDEF_(0x8011044DL)
-#define COMADMIN_E_EVENTCLASS_CANT_BE_SUBSCRIBER _HRESULT_TYPEDEF_(0x8011044EL)
-#define COMADMIN_E_LIB_APP_PROXY_INCOMPATIBLE _HRESULT_TYPEDEF_(0x8011044FL)
-#define COMADMIN_E_BASE_PARTITION_ONLY _HRESULT_TYPEDEF_(0x80110450L)
-#define COMADMIN_E_START_APP_DISABLED _HRESULT_TYPEDEF_(0x80110451L)
-#define COMADMIN_E_CAT_DUPLICATE_PARTITION_NAME _HRESULT_TYPEDEF_(0x80110457L)
-#define COMADMIN_E_CAT_INVALID_PARTITION_NAME _HRESULT_TYPEDEF_(0x80110458L)
-#define COMADMIN_E_CAT_PARTITION_IN_USE _HRESULT_TYPEDEF_(0x80110459L)
-#define COMADMIN_E_FILE_PARTITION_DUPLICATE_FILES _HRESULT_TYPEDEF_(0x8011045AL)
-#define COMADMIN_E_CAT_IMPORTED_COMPONENTS_NOT_ALLOWED _HRESULT_TYPEDEF_(0x8011045BL)
-#define COMADMIN_E_AMBIGUOUS_APPLICATION_NAME _HRESULT_TYPEDEF_(0x8011045CL)
-#define COMADMIN_E_AMBIGUOUS_PARTITION_NAME _HRESULT_TYPEDEF_(0x8011045DL)
-#define COMADMIN_E_REGDB_NOTINITIALIZED _HRESULT_TYPEDEF_(0x80110472L)
-#define COMADMIN_E_REGDB_NOTOPEN _HRESULT_TYPEDEF_(0x80110473L)
-#define COMADMIN_E_REGDB_SYSTEMERR _HRESULT_TYPEDEF_(0x80110474L)
-#define COMADMIN_E_REGDB_ALREADYRUNNING _HRESULT_TYPEDEF_(0x80110475L)
-#define COMADMIN_E_MIG_VERSIONNOTSUPPORTED _HRESULT_TYPEDEF_(0x80110480L)
-#define COMADMIN_E_MIG_SCHEMANOTFOUND _HRESULT_TYPEDEF_(0x80110481L)
-#define COMADMIN_E_CAT_BITNESSMISMATCH _HRESULT_TYPEDEF_(0x80110482L)
-#define COMADMIN_E_CAT_UNACCEPTABLEBITNESS _HRESULT_TYPEDEF_(0x80110483L)
-#define COMADMIN_E_CAT_WRONGAPPBITNESS _HRESULT_TYPEDEF_(0x80110484L)
-#define COMADMIN_E_CAT_PAUSE_RESUME_NOT_SUPPORTED _HRESULT_TYPEDEF_(0x80110485L)
-#define COMADMIN_E_CAT_SERVERFAULT _HRESULT_TYPEDEF_(0x80110486L)
-#define COMQC_E_APPLICATION_NOT_QUEUED _HRESULT_TYPEDEF_(0x80110600L)
-#define COMQC_E_NO_QUEUEABLE_INTERFACES _HRESULT_TYPEDEF_(0x80110601L)
-#define COMQC_E_QUEUING_SERVICE_NOT_AVAILABLE _HRESULT_TYPEDEF_(0x80110602L)
-#define COMQC_E_NO_IPERSISTSTREAM _HRESULT_TYPEDEF_(0x80110603L)
-#define COMQC_E_BAD_MESSAGE _HRESULT_TYPEDEF_(0x80110604L)
-#define COMQC_E_UNAUTHENTICATED _HRESULT_TYPEDEF_(0x80110605L)
-#define COMQC_E_UNTRUSTED_ENQUEUER _HRESULT_TYPEDEF_(0x80110606L)
-#define MSDTC_E_DUPLICATE_RESOURCE _HRESULT_TYPEDEF_(0x80110701L)
-#define COMADMIN_E_OBJECT_PARENT_MISSING _HRESULT_TYPEDEF_(0x80110808L)
-#define COMADMIN_E_OBJECT_DOES_NOT_EXIST _HRESULT_TYPEDEF_(0x80110809L)
-#define COMADMIN_E_APP_NOT_RUNNING _HRESULT_TYPEDEF_(0x8011080AL)
-#define COMADMIN_E_INVALID_PARTITION _HRESULT_TYPEDEF_(0x8011080BL)
-#define COMADMIN_E_SVCAPP_NOT_POOLABLE_OR_RECYCLABLE _HRESULT_TYPEDEF_(0x8011080DL)
-#define COMADMIN_E_USER_IN_SET _HRESULT_TYPEDEF_(0x8011080EL)
-#define COMADMIN_E_CANTRECYCLELIBRARYAPPS _HRESULT_TYPEDEF_(0x8011080FL)
-#define COMADMIN_E_CANTRECYCLESERVICEAPPS _HRESULT_TYPEDEF_(0x80110811L)
-#define COMADMIN_E_PROCESSALREADYRECYCLED _HRESULT_TYPEDEF_(0x80110812L)
-#define COMADMIN_E_PAUSEDPROCESSMAYNOTBERECYCLED _HRESULT_TYPEDEF_(0x80110813L)
-#define COMADMIN_E_CANTMAKEINPROCSERVICE _HRESULT_TYPEDEF_(0x80110814L)
-#define COMADMIN_E_PROGIDINUSEBYCLSID _HRESULT_TYPEDEF_(0x80110815L)
-#define COMADMIN_E_DEFAULT_PARTITION_NOT_IN_SET _HRESULT_TYPEDEF_(0x80110816L)
-#define COMADMIN_E_RECYCLEDPROCESSMAYNOTBEPAUSED _HRESULT_TYPEDEF_(0x80110817L)
-#define COMADMIN_E_PARTITION_ACCESSDENIED _HRESULT_TYPEDEF_(0x80110818L)
-#define COMADMIN_E_PARTITION_MSI_ONLY _HRESULT_TYPEDEF_(0x80110819L)
-#define COMADMIN_E_LEGACYCOMPS_NOT_ALLOWED_IN_1_0_FORMAT _HRESULT_TYPEDEF_(0x8011081AL)
-#define COMADMIN_E_LEGACYCOMPS_NOT_ALLOWED_IN_NONBASE_PARTITIONS _HRESULT_TYPEDEF_(0x8011081BL)
-#define COMADMIN_E_COMP_MOVE_SOURCE _HRESULT_TYPEDEF_(0x8011081CL)
-#define COMADMIN_E_COMP_MOVE_DEST _HRESULT_TYPEDEF_(0x8011081DL)
-#define COMADMIN_E_COMP_MOVE_PRIVATE _HRESULT_TYPEDEF_(0x8011081EL)
-#define COMADMIN_E_BASEPARTITION_REQUIRED_IN_SET _HRESULT_TYPEDEF_(0x8011081FL)
-#define COMADMIN_E_CANNOT_ALIAS_EVENTCLASS _HRESULT_TYPEDEF_(0x80110820L)
-#define COMADMIN_E_PRIVATE_ACCESSDENIED _HRESULT_TYPEDEF_(0x80110821L)
-#define COMADMIN_E_SAFERINVALID _HRESULT_TYPEDEF_(0x80110822L)
-#define COMADMIN_E_REGISTRY_ACCESSDENIED _HRESULT_TYPEDEF_(0x80110823L)
-#define COMADMIN_E_PARTITIONS_DISABLED _HRESULT_TYPEDEF_(0x80110824L)
-#endif /* _WINERROR_ */
diff --git a/tinyc/win32/include/winapi/wingdi.h b/tinyc/win32/include/winapi/wingdi.h
deleted file mode 100644
index 63d389178..000000000
--- a/tinyc/win32/include/winapi/wingdi.h
+++ /dev/null
@@ -1,4080 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _WINGDI_
-#define _WINGDI_
-
-#define WINGDIAPI DECLSPEC_IMPORT
-#define WINSPOOLAPI DECLSPEC_IMPORT
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef WINVER
-#define WINVER 0x0502
-#endif
-
-#ifndef NOGDI
-#ifndef NORASTEROPS
-#define R2_BLACK 1
-#define R2_NOTMERGEPEN 2
-#define R2_MASKNOTPEN 3
-#define R2_NOTCOPYPEN 4
-#define R2_MASKPENNOT 5
-#define R2_NOT 6
-#define R2_XORPEN 7
-#define R2_NOTMASKPEN 8
-#define R2_MASKPEN 9
-#define R2_NOTXORPEN 10
-#define R2_NOP 11
-#define R2_MERGENOTPEN 12
-#define R2_COPYPEN 13
-#define R2_MERGEPENNOT 14
-#define R2_MERGEPEN 15
-#define R2_WHITE 16
-#define R2_LAST 16
-
-#define SRCCOPY (DWORD)0x00CC0020
-#define SRCPAINT (DWORD)0x00EE0086
-#define SRCAND (DWORD)0x008800C6
-#define SRCINVERT (DWORD)0x00660046
-#define SRCERASE (DWORD)0x00440328
-#define NOTSRCCOPY (DWORD)0x00330008
-#define NOTSRCERASE (DWORD)0x001100A6
-#define MERGECOPY (DWORD)0x00C000CA
-#define MERGEPAINT (DWORD)0x00BB0226
-#define PATCOPY (DWORD)0x00F00021
-#define PATPAINT (DWORD)0x00FB0A09
-#define PATINVERT (DWORD)0x005A0049
-#define DSTINVERT (DWORD)0x00550009
-#define BLACKNESS (DWORD)0x00000042
-#define WHITENESS (DWORD)0x00FF0062
-#define NOMIRRORBITMAP (DWORD)0x80000000
-#define CAPTUREBLT (DWORD)0x40000000
-#define MAKEROP4(fore,back) (DWORD)((((back) << 8) & 0xFF000000) | (fore))
-#endif
-
-#define GDI_ERROR (0xFFFFFFFFL)
-#define HGDI_ERROR (LongToHandle(0xFFFFFFFFL))
-
-#define ERROR 0
-#define NULLREGION 1
-#define SIMPLEREGION 2
-#define COMPLEXREGION 3
-#define RGN_ERROR ERROR
-
-#define RGN_AND 1
-#define RGN_OR 2
-#define RGN_XOR 3
-#define RGN_DIFF 4
-#define RGN_COPY 5
-#define RGN_MIN RGN_AND
-#define RGN_MAX RGN_COPY
-
-#define BLACKONWHITE 1
-#define WHITEONBLACK 2
-#define COLORONCOLOR 3
-#define HALFTONE 4
-#define MAXSTRETCHBLTMODE 4
-
-#define STRETCH_ANDSCANS BLACKONWHITE
-#define STRETCH_ORSCANS WHITEONBLACK
-#define STRETCH_DELETESCANS COLORONCOLOR
-#define STRETCH_HALFTONE HALFTONE
-
-#define ALTERNATE 1
-#define WINDING 2
-#define POLYFILL_LAST 2
-
-#define LAYOUT_RTL 0x00000001
-#define LAYOUT_BTT 0x00000002
-#define LAYOUT_VBH 0x00000004
-#define LAYOUT_ORIENTATIONMASK (LAYOUT_RTL | LAYOUT_BTT | LAYOUT_VBH)
-#define LAYOUT_BITMAPORIENTATIONPRESERVED 0x00000008
-
-#define TA_NOUPDATECP 0
-#define TA_UPDATECP 1
-
-#define TA_LEFT 0
-#define TA_RIGHT 2
-#define TA_CENTER 6
-
-#define TA_TOP 0
-#define TA_BOTTOM 8
-#define TA_BASELINE 24
-#define TA_RTLREADING 256
-#define TA_MASK (TA_BASELINE+TA_CENTER+TA_UPDATECP+TA_RTLREADING)
-
-#define VTA_BASELINE TA_BASELINE
-#define VTA_LEFT TA_BOTTOM
-#define VTA_RIGHT TA_TOP
-#define VTA_CENTER TA_CENTER
-#define VTA_BOTTOM TA_RIGHT
-#define VTA_TOP TA_LEFT
-
-#define ETO_OPAQUE 0x0002
-#define ETO_CLIPPED 0x0004
-#define ETO_GLYPH_INDEX 0x0010
-#define ETO_RTLREADING 0x0080
-#define ETO_NUMERICSLOCAL 0x0400
-#define ETO_NUMERICSLATIN 0x0800
-#define ETO_IGNORELANGUAGE 0x1000
-#define ETO_PDY 0x2000
-
-#define ASPECT_FILTERING 0x0001
-
-#define DCB_RESET 0x0001
-#define DCB_ACCUMULATE 0x0002
-#define DCB_DIRTY DCB_ACCUMULATE
-#define DCB_SET (DCB_RESET | DCB_ACCUMULATE)
-#define DCB_ENABLE 0x0004
-#define DCB_DISABLE 0x0008
-
-#ifndef NOMETAFILE
-
-#define META_SETBKCOLOR 0x0201
-#define META_SETBKMODE 0x0102
-#define META_SETMAPMODE 0x0103
-#define META_SETROP2 0x0104
-#define META_SETRELABS 0x0105
-#define META_SETPOLYFILLMODE 0x0106
-#define META_SETSTRETCHBLTMODE 0x0107
-#define META_SETTEXTCHAREXTRA 0x0108
-#define META_SETTEXTCOLOR 0x0209
-#define META_SETTEXTJUSTIFICATION 0x020A
-#define META_SETWINDOWORG 0x020B
-#define META_SETWINDOWEXT 0x020C
-#define META_SETVIEWPORTORG 0x020D
-#define META_SETVIEWPORTEXT 0x020E
-#define META_OFFSETWINDOWORG 0x020F
-#define META_SCALEWINDOWEXT 0x0410
-#define META_OFFSETVIEWPORTORG 0x0211
-#define META_SCALEVIEWPORTEXT 0x0412
-#define META_LINETO 0x0213
-#define META_MOVETO 0x0214
-#define META_EXCLUDECLIPRECT 0x0415
-#define META_INTERSECTCLIPRECT 0x0416
-#define META_ARC 0x0817
-#define META_ELLIPSE 0x0418
-#define META_FLOODFILL 0x0419
-#define META_PIE 0x081A
-#define META_RECTANGLE 0x041B
-#define META_ROUNDRECT 0x061C
-#define META_PATBLT 0x061D
-#define META_SAVEDC 0x001E
-#define META_SETPIXEL 0x041F
-#define META_OFFSETCLIPRGN 0x0220
-#define META_TEXTOUT 0x0521
-#define META_BITBLT 0x0922
-#define META_STRETCHBLT 0x0B23
-#define META_POLYGON 0x0324
-#define META_POLYLINE 0x0325
-#define META_ESCAPE 0x0626
-#define META_RESTOREDC 0x0127
-#define META_FILLREGION 0x0228
-#define META_FRAMEREGION 0x0429
-#define META_INVERTREGION 0x012A
-#define META_PAINTREGION 0x012B
-#define META_SELECTCLIPREGION 0x012C
-#define META_SELECTOBJECT 0x012D
-#define META_SETTEXTALIGN 0x012E
-#define META_CHORD 0x0830
-#define META_SETMAPPERFLAGS 0x0231
-#define META_EXTTEXTOUT 0x0a32
-#define META_SETDIBTODEV 0x0d33
-#define META_SELECTPALETTE 0x0234
-#define META_REALIZEPALETTE 0x0035
-#define META_ANIMATEPALETTE 0x0436
-#define META_SETPALENTRIES 0x0037
-#define META_POLYPOLYGON 0x0538
-#define META_RESIZEPALETTE 0x0139
-#define META_DIBBITBLT 0x0940
-#define META_DIBSTRETCHBLT 0x0b41
-#define META_DIBCREATEPATTERNBRUSH 0x0142
-#define META_STRETCHDIB 0x0f43
-#define META_EXTFLOODFILL 0x0548
-#define META_SETLAYOUT 0x0149
-#define META_DELETEOBJECT 0x01f0
-#define META_CREATEPALETTE 0x00f7
-#define META_CREATEPATTERNBRUSH 0x01F9
-#define META_CREATEPENINDIRECT 0x02FA
-#define META_CREATEFONTINDIRECT 0x02FB
-#define META_CREATEBRUSHINDIRECT 0x02FC
-#define META_CREATEREGION 0x06FF
-
-  typedef struct _DRAWPATRECT {
-    POINT ptPosition;
-    POINT ptSize;
-    WORD wStyle;
-    WORD wPattern;
-  } DRAWPATRECT,*PDRAWPATRECT;
-#endif
-
-#define NEWFRAME 1
-#define ABORTDOC 2
-#define NEXTBAND 3
-#define SETCOLORTABLE 4
-#define GETCOLORTABLE 5
-#define FLUSHOUTPUT 6
-#define DRAFTMODE 7
-#define QUERYESCSUPPORT 8
-#define SETABORTPROC 9
-#define STARTDOC 10
-#define ENDDOC 11
-#define GETPHYSPAGESIZE 12
-#define GETPRINTINGOFFSET 13
-#define GETSCALINGFACTOR 14
-#define MFCOMMENT 15
-#define GETPENWIDTH 16
-#define SETCOPYCOUNT 17
-#define SELECTPAPERSOURCE 18
-#define DEVICEDATA 19
-#define PASSTHROUGH 19
-#define GETTECHNOLGY 20
-#define GETTECHNOLOGY 20
-#define SETLINECAP 21
-#define SETLINEJOIN 22
-#define SETMITERLIMIT 23
-#define BANDINFO 24
-#define DRAWPATTERNRECT 25
-#define GETVECTORPENSIZE 26
-#define GETVECTORBRUSHSIZE 27
-#define ENABLEDUPLEX 28
-#define GETSETPAPERBINS 29
-#define GETSETPRINTORIENT 30
-#define ENUMPAPERBINS 31
-#define SETDIBSCALING 32
-#define EPSPRINTING 33
-#define ENUMPAPERMETRICS 34
-#define GETSETPAPERMETRICS 35
-#define POSTSCRIPT_DATA 37
-#define POSTSCRIPT_IGNORE 38
-#define MOUSETRAILS 39
-#define GETDEVICEUNITS 42
-
-#define GETEXTENDEDTEXTMETRICS 256
-#define GETEXTENTTABLE 257
-#define GETPAIRKERNTABLE 258
-#define GETTRACKKERNTABLE 259
-#define EXTTEXTOUT 512
-#define GETFACENAME 513
-#define DOWNLOADFACE 514
-#define ENABLERELATIVEWIDTHS 768
-#define ENABLEPAIRKERNING 769
-#define SETKERNTRACK 770
-#define SETALLJUSTVALUES 771
-#define SETCHARSET 772
-
-#define STRETCHBLT 2048
-#define METAFILE_DRIVER 2049
-#define GETSETSCREENPARAMS 3072
-#define QUERYDIBSUPPORT 3073
-#define BEGIN_PATH 4096
-#define CLIP_TO_PATH 4097
-#define END_PATH 4098
-#define EXT_DEVICE_CAPS 4099
-#define RESTORE_CTM 4100
-#define SAVE_CTM 4101
-#define SET_ARC_DIRECTION 4102
-#define SET_BACKGROUND_COLOR 4103
-#define SET_POLY_MODE 4104
-#define SET_SCREEN_ANGLE 4105
-#define SET_SPREAD 4106
-#define TRANSFORM_CTM 4107
-#define SET_CLIP_BOX 4108
-#define SET_BOUNDS 4109
-#define SET_MIRROR_MODE 4110
-#define OPENCHANNEL 4110
-#define DOWNLOADHEADER 4111
-#define CLOSECHANNEL 4112
-#define POSTSCRIPT_PASSTHROUGH 4115
-#define ENCAPSULATED_POSTSCRIPT 4116
-
-#define POSTSCRIPT_IDENTIFY 4117
-#define POSTSCRIPT_INJECTION 4118
-
-#define CHECKJPEGFORMAT 4119
-#define CHECKPNGFORMAT 4120
-
-#define GET_PS_FEATURESETTING 4121
-
-#define SPCLPASSTHROUGH2 4568
-
-#define PSIDENT_GDICENTRIC 0
-#define PSIDENT_PSCENTRIC 1
-
-  typedef struct _PSINJECTDATA {
-    DWORD DataBytes;
-    WORD InjectionPoint;
-    WORD PageNumber;
-  } PSINJECTDATA,*PPSINJECTDATA;
-
-#define PSINJECT_BEGINSTREAM 1
-#define PSINJECT_PSADOBE 2
-#define PSINJECT_PAGESATEND 3
-#define PSINJECT_PAGES 4
-
-#define PSINJECT_DOCNEEDEDRES 5
-#define PSINJECT_DOCSUPPLIEDRES 6
-#define PSINJECT_PAGEORDER 7
-#define PSINJECT_ORIENTATION 8
-#define PSINJECT_BOUNDINGBOX 9
-#define PSINJECT_DOCUMENTPROCESSCOLORS 10
-
-#define PSINJECT_COMMENTS 11
-#define PSINJECT_BEGINDEFAULTS 12
-#define PSINJECT_ENDDEFAULTS 13
-#define PSINJECT_BEGINPROLOG 14
-#define PSINJECT_ENDPROLOG 15
-#define PSINJECT_BEGINSETUP 16
-#define PSINJECT_ENDSETUP 17
-#define PSINJECT_TRAILER 18
-#define PSINJECT_EOF 19
-#define PSINJECT_ENDSTREAM 20
-#define PSINJECT_DOCUMENTPROCESSCOLORSATEND 21
-
-#define PSINJECT_PAGENUMBER 100
-#define PSINJECT_BEGINPAGESETUP 101
-#define PSINJECT_ENDPAGESETUP 102
-#define PSINJECT_PAGETRAILER 103
-#define PSINJECT_PLATECOLOR 104
-
-#define PSINJECT_SHOWPAGE 105
-#define PSINJECT_PAGEBBOX 106
-#define PSINJECT_ENDPAGECOMMENTS 107
-
-#define PSINJECT_VMSAVE 200
-#define PSINJECT_VMRESTORE 201
-
-#define FEATURESETTING_NUP 0
-#define FEATURESETTING_OUTPUT 1
-#define FEATURESETTING_PSLEVEL 2
-#define FEATURESETTING_CUSTPAPER 3
-#define FEATURESETTING_MIRROR 4
-#define FEATURESETTING_NEGATIVE 5
-#define FEATURESETTING_PROTOCOL 6
-
-#define FEATURESETTING_PRIVATE_BEGIN 0x1000
-#define FEATURESETTING_PRIVATE_END 0x1FFF
-
-  typedef struct _PSFEATURE_OUTPUT {
-    WINBOOL bPageIndependent;
-    WINBOOL bSetPageDevice;
-  } PSFEATURE_OUTPUT,*PPSFEATURE_OUTPUT;
-
-  typedef struct _PSFEATURE_CUSTPAPER {
-    LONG lOrientation;
-    LONG lWidth;
-    LONG lHeight;
-    LONG lWidthOffset;
-    LONG lHeightOffset;
-  } PSFEATURE_CUSTPAPER,*PPSFEATURE_CUSTPAPER;
-
-#define PSPROTOCOL_ASCII 0
-#define PSPROTOCOL_BCP 1
-#define PSPROTOCOL_TBCP 2
-#define PSPROTOCOL_BINARY 3
-
-#define QDI_SETDIBITS 1
-#define QDI_GETDIBITS 2
-#define QDI_DIBTOSCREEN 4
-#define QDI_STRETCHDIB 8
-
-#define SP_NOTREPORTED 0x4000
-#define SP_ERROR (-1)
-#define SP_APPABORT (-2)
-#define SP_USERABORT (-3)
-#define SP_OUTOFDISK (-4)
-#define SP_OUTOFMEMORY (-5)
-
-#define PR_JOBSTATUS 0x0000
-
-#define OBJ_PEN 1
-#define OBJ_BRUSH 2
-#define OBJ_DC 3
-#define OBJ_METADC 4
-#define OBJ_PAL 5
-#define OBJ_FONT 6
-#define OBJ_BITMAP 7
-#define OBJ_REGION 8
-#define OBJ_METAFILE 9
-#define OBJ_MEMDC 10
-#define OBJ_EXTPEN 11
-#define OBJ_ENHMETADC 12
-#define OBJ_ENHMETAFILE 13
-#define OBJ_COLORSPACE 14
-
-#define MWT_IDENTITY 1
-#define MWT_LEFTMULTIPLY 2
-#define MWT_RIGHTMULTIPLY 3
-
-#define MWT_MIN MWT_IDENTITY
-#define MWT_MAX MWT_RIGHTMULTIPLY
-
-#define _XFORM_
-  typedef struct tagXFORM {
-    FLOAT eM11;
-    FLOAT eM12;
-    FLOAT eM21;
-    FLOAT eM22;
-    FLOAT eDx;
-    FLOAT eDy;
-  } XFORM,*PXFORM,*LPXFORM;
-
-  typedef struct tagBITMAP {
-    LONG bmType;
-    LONG bmWidth;
-    LONG bmHeight;
-    LONG bmWidthBytes;
-    WORD bmPlanes;
-    WORD bmBitsPixel;
-    LPVOID bmBits;
-  } BITMAP,*PBITMAP,*NPBITMAP,*LPBITMAP;
-
-#include <pshpack1.h>
-  typedef struct tagRGBTRIPLE {
-    BYTE rgbtBlue;
-    BYTE rgbtGreen;
-    BYTE rgbtRed;
-  } RGBTRIPLE;
-#include <poppack.h>
-
-  typedef struct tagRGBQUAD {
-    BYTE rgbBlue;
-    BYTE rgbGreen;
-    BYTE rgbRed;
-    BYTE rgbReserved;
-  } RGBQUAD;
-  typedef RGBQUAD *LPRGBQUAD;
-
-#define CS_ENABLE 0x00000001L
-#define CS_DISABLE 0x00000002L
-#define CS_DELETE_TRANSFORM 0x00000003L
-
-//!__TINYC__: #define LCS_SIGNATURE 'PSOC'
-//!__TINYC__: #define LCS_sRGB 'sRGB'
-//!__TINYC__: #define LCS_WINDOWS_COLOR_SPACE 'Win '
-
-  typedef LONG LCSCSTYPE;
-#define LCS_CALIBRATED_RGB 0x00000000L
-
-  typedef LONG LCSGAMUTMATCH;
-#define LCS_GM_BUSINESS 0x00000001L
-#define LCS_GM_GRAPHICS 0x00000002L
-#define LCS_GM_IMAGES 0x00000004L
-#define LCS_GM_ABS_COLORIMETRIC 0x00000008L
-
-#define CM_OUT_OF_GAMUT 255
-#define CM_IN_GAMUT 0
-
-#define ICM_ADDPROFILE 1
-#define ICM_DELETEPROFILE 2
-#define ICM_QUERYPROFILE 3
-#define ICM_SETDEFAULTPROFILE 4
-#define ICM_REGISTERICMATCHER 5
-#define ICM_UNREGISTERICMATCHER 6
-#define ICM_QUERYMATCH 7
-
-#define GetKValue(cmyk) ((BYTE)(cmyk))
-#define GetYValue(cmyk) ((BYTE)((cmyk)>> 8))
-#define GetMValue(cmyk) ((BYTE)((cmyk)>>16))
-#define GetCValue(cmyk) ((BYTE)((cmyk)>>24))
-
-#define CMYK(c,m,y,k) ((COLORREF)((((BYTE)(k)|((WORD)((BYTE)(y))<<8))|(((DWORD)(BYTE)(m))<<16))|(((DWORD)(BYTE)(c))<<24)))
-
-  typedef long FXPT16DOT16,*LPFXPT16DOT16;
-  typedef long FXPT2DOT30,*LPFXPT2DOT30;
-
-  typedef struct tagCIEXYZ {
-    FXPT2DOT30 ciexyzX;
-    FXPT2DOT30 ciexyzY;
-    FXPT2DOT30 ciexyzZ;
-  } CIEXYZ;
-  typedef CIEXYZ *LPCIEXYZ;
-
-  typedef struct tagICEXYZTRIPLE {
-    CIEXYZ ciexyzRed;
-    CIEXYZ ciexyzGreen;
-    CIEXYZ ciexyzBlue;
-  } CIEXYZTRIPLE;
-
-  typedef CIEXYZTRIPLE *LPCIEXYZTRIPLE;
-
-  typedef struct tagLOGCOLORSPACEA {
-    DWORD lcsSignature;
-    DWORD lcsVersion;
-    DWORD lcsSize;
-    LCSCSTYPE lcsCSType;
-    LCSGAMUTMATCH lcsIntent;
-    CIEXYZTRIPLE lcsEndpoints;
-    DWORD lcsGammaRed;
-    DWORD lcsGammaGreen;
-    DWORD lcsGammaBlue;
-    CHAR lcsFilename[MAX_PATH];
-  } LOGCOLORSPACEA,*LPLOGCOLORSPACEA;
-
-  typedef struct tagLOGCOLORSPACEW {
-    DWORD lcsSignature;
-    DWORD lcsVersion;
-    DWORD lcsSize;
-    LCSCSTYPE lcsCSType;
-    LCSGAMUTMATCH lcsIntent;
-    CIEXYZTRIPLE lcsEndpoints;
-    DWORD lcsGammaRed;
-    DWORD lcsGammaGreen;
-    DWORD lcsGammaBlue;
-    WCHAR lcsFilename[MAX_PATH];
-  } LOGCOLORSPACEW,*LPLOGCOLORSPACEW;
-
-#ifdef UNICODE
-  typedef LOGCOLORSPACEW LOGCOLORSPACE;
-  typedef LPLOGCOLORSPACEW LPLOGCOLORSPACE;
-#else
-  typedef LOGCOLORSPACEA LOGCOLORSPACE;
-  typedef LPLOGCOLORSPACEA LPLOGCOLORSPACE;
-#endif
-
-  typedef struct tagBITMAPCOREHEADER {
-    DWORD bcSize;
-    WORD bcWidth;
-    WORD bcHeight;
-    WORD bcPlanes;
-    WORD bcBitCount;
-  } BITMAPCOREHEADER,*LPBITMAPCOREHEADER,*PBITMAPCOREHEADER;
-
-  typedef struct tagBITMAPINFOHEADER {
-    DWORD biSize;
-    LONG biWidth;
-    LONG biHeight;
-    WORD biPlanes;
-    WORD biBitCount;
-    DWORD biCompression;
-    DWORD biSizeImage;
-    LONG biXPelsPerMeter;
-    LONG biYPelsPerMeter;
-    DWORD biClrUsed;
-    DWORD biClrImportant;
-  } BITMAPINFOHEADER,*LPBITMAPINFOHEADER,*PBITMAPINFOHEADER;
-
-  typedef struct {
-    DWORD bV4Size;
-    LONG bV4Width;
-    LONG bV4Height;
-    WORD bV4Planes;
-    WORD bV4BitCount;
-    DWORD bV4V4Compression;
-    DWORD bV4SizeImage;
-    LONG bV4XPelsPerMeter;
-    LONG bV4YPelsPerMeter;
-    DWORD bV4ClrUsed;
-    DWORD bV4ClrImportant;
-    DWORD bV4RedMask;
-    DWORD bV4GreenMask;
-    DWORD bV4BlueMask;
-    DWORD bV4AlphaMask;
-    DWORD bV4CSType;
-    CIEXYZTRIPLE bV4Endpoints;
-    DWORD bV4GammaRed;
-    DWORD bV4GammaGreen;
-    DWORD bV4GammaBlue;
-  } BITMAPV4HEADER,*LPBITMAPV4HEADER,*PBITMAPV4HEADER;
-
-  typedef struct {
-    DWORD bV5Size;
-    LONG bV5Width;
-    LONG bV5Height;
-    WORD bV5Planes;
-    WORD bV5BitCount;
-    DWORD bV5Compression;
-    DWORD bV5SizeImage;
-    LONG bV5XPelsPerMeter;
-    LONG bV5YPelsPerMeter;
-    DWORD bV5ClrUsed;
-    DWORD bV5ClrImportant;
-    DWORD bV5RedMask;
-    DWORD bV5GreenMask;
-    DWORD bV5BlueMask;
-    DWORD bV5AlphaMask;
-    DWORD bV5CSType;
-    CIEXYZTRIPLE bV5Endpoints;
-    DWORD bV5GammaRed;
-    DWORD bV5GammaGreen;
-    DWORD bV5GammaBlue;
-    DWORD bV5Intent;
-    DWORD bV5ProfileData;
-    DWORD bV5ProfileSize;
-    DWORD bV5Reserved;
-  } BITMAPV5HEADER,*LPBITMAPV5HEADER,*PBITMAPV5HEADER;
-
-//!__TINYC__: #define PROFILE_LINKED 'LINK'
-//!__TINYC__: #define PROFILE_EMBEDDED 'MBED'
-
-#define BI_RGB 0L
-#define BI_RLE8 1L
-#define BI_RLE4 2L
-#define BI_BITFIELDS 3L
-#define BI_JPEG 4L
-#define BI_PNG 5L
-
-  typedef struct tagBITMAPINFO {
-    BITMAPINFOHEADER bmiHeader;
-    RGBQUAD bmiColors[1];
-  } BITMAPINFO,*LPBITMAPINFO,*PBITMAPINFO;
-
-  typedef struct tagBITMAPCOREINFO {
-    BITMAPCOREHEADER bmciHeader;
-    RGBTRIPLE bmciColors[1];
-  } BITMAPCOREINFO,*LPBITMAPCOREINFO,*PBITMAPCOREINFO;
-
-#include <pshpack2.h>
-  typedef struct tagBITMAPFILEHEADER {
-    WORD bfType;
-    DWORD bfSize;
-    WORD bfReserved1;
-    WORD bfReserved2;
-    DWORD bfOffBits;
-  } BITMAPFILEHEADER,*LPBITMAPFILEHEADER,*PBITMAPFILEHEADER;
-#include <poppack.h>
-
-#define MAKEPOINTS(l) (*((POINTS *)&(l)))
-
-#ifndef NOFONTSIG
-  typedef struct tagFONTSIGNATURE {
-    DWORD fsUsb[4];
-    DWORD fsCsb[2];
-  } FONTSIGNATURE,*PFONTSIGNATURE,*LPFONTSIGNATURE;
-
-  typedef struct tagCHARSETINFO {
-    UINT ciCharset;
-    UINT ciACP;
-    FONTSIGNATURE fs;
-  } CHARSETINFO,*PCHARSETINFO,*NPCHARSETINFO,*LPCHARSETINFO;
-
-#define TCI_SRCCHARSET 1
-#define TCI_SRCCODEPAGE 2
-#define TCI_SRCFONTSIG 3
-#define TCI_SRCLOCALE 0x1000
-
-  typedef struct tagLOCALESIGNATURE {
-    DWORD lsUsb[4];
-    DWORD lsCsbDefault[2];
-    DWORD lsCsbSupported[2];
-  } LOCALESIGNATURE,*PLOCALESIGNATURE,*LPLOCALESIGNATURE;
-#endif
-
-
-#ifndef NOMETAFILE
-  typedef struct tagHANDLETABLE {
-    HGDIOBJ objectHandle[1];
-  } HANDLETABLE,*PHANDLETABLE,*LPHANDLETABLE;
-
-  typedef struct tagMETARECORD {
-    DWORD rdSize;
-    WORD rdFunction;
-    WORD rdParm[1];
-  } METARECORD;
-  typedef struct tagMETARECORD UNALIGNED *PMETARECORD;
-  typedef struct tagMETARECORD UNALIGNED *LPMETARECORD;
-
-  typedef struct tagMETAFILEPICT {
-    LONG mm;
-    LONG xExt;
-    LONG yExt;
-    HMETAFILE hMF;
-  } METAFILEPICT,*LPMETAFILEPICT;
-
-#include <pshpack2.h>
-  typedef struct tagMETAHEADER {
-    WORD mtType;
-    WORD mtHeaderSize;
-    WORD mtVersion;
-    DWORD mtSize;
-    WORD mtNoObjects;
-    DWORD mtMaxRecord;
-    WORD mtNoParameters;
-  } METAHEADER;
-  typedef struct tagMETAHEADER UNALIGNED *PMETAHEADER;
-  typedef struct tagMETAHEADER UNALIGNED *LPMETAHEADER;
-
-#include <poppack.h>
-
-  typedef struct tagENHMETARECORD {
-    DWORD iType;
-    DWORD nSize;
-    DWORD dParm[1];
-  } ENHMETARECORD,*PENHMETARECORD,*LPENHMETARECORD;
-
-  typedef struct tagENHMETAHEADER {
-    DWORD iType;
-    DWORD nSize;
-    RECTL rclBounds;
-    RECTL rclFrame;
-    DWORD dSignature;
-    DWORD nVersion;
-    DWORD nBytes;
-    DWORD nRecords;
-    WORD nHandles;
-    WORD sReserved;
-    DWORD nDescription;
-    DWORD offDescription;
-    DWORD nPalEntries;
-    SIZEL szlDevice;
-    SIZEL szlMillimeters;
-    DWORD cbPixelFormat;
-    DWORD offPixelFormat;
-    DWORD bOpenGL;
-    SIZEL szlMicrometers;
-  } ENHMETAHEADER,*PENHMETAHEADER,*LPENHMETAHEADER;
-#endif
-
-#ifndef NOTEXTMETRIC
-#define TMPF_FIXED_PITCH 0x01
-#define TMPF_VECTOR 0x02
-#define TMPF_DEVICE 0x08
-#define TMPF_TRUETYPE 0x04
-
-#ifdef UNICODE
-  typedef WCHAR BCHAR;
-#else
-  typedef BYTE BCHAR;
-#endif
-
-#ifndef _TEXTMETRIC_DEFINED
-#define _TEXTMETRIC_DEFINED
-  typedef struct tagTEXTMETRICA {
-    LONG tmHeight;
-    LONG tmAscent;
-    LONG tmDescent;
-    LONG tmInternalLeading;
-    LONG tmExternalLeading;
-    LONG tmAveCharWidth;
-    LONG tmMaxCharWidth;
-    LONG tmWeight;
-    LONG tmOverhang;
-    LONG tmDigitizedAspectX;
-    LONG tmDigitizedAspectY;
-    BYTE tmFirstChar;
-    BYTE tmLastChar;
-    BYTE tmDefaultChar;
-    BYTE tmBreakChar;
-    BYTE tmItalic;
-    BYTE tmUnderlined;
-    BYTE tmStruckOut;
-    BYTE tmPitchAndFamily;
-    BYTE tmCharSet;
-  } TEXTMETRICA,*PTEXTMETRICA,*NPTEXTMETRICA,*LPTEXTMETRICA;
-
-  typedef struct tagTEXTMETRICW {
-    LONG tmHeight;
-    LONG tmAscent;
-    LONG tmDescent;
-    LONG tmInternalLeading;
-    LONG tmExternalLeading;
-    LONG tmAveCharWidth;
-    LONG tmMaxCharWidth;
-    LONG tmWeight;
-    LONG tmOverhang;
-    LONG tmDigitizedAspectX;
-    LONG tmDigitizedAspectY;
-    WCHAR tmFirstChar;
-    WCHAR tmLastChar;
-    WCHAR tmDefaultChar;
-    WCHAR tmBreakChar;
-    BYTE tmItalic;
-    BYTE tmUnderlined;
-    BYTE tmStruckOut;
-    BYTE tmPitchAndFamily;
-    BYTE tmCharSet;
-  } TEXTMETRICW,*PTEXTMETRICW,*NPTEXTMETRICW,*LPTEXTMETRICW;
-#ifdef UNICODE
-  typedef TEXTMETRICW TEXTMETRIC;
-  typedef PTEXTMETRICW PTEXTMETRIC;
-  typedef NPTEXTMETRICW NPTEXTMETRIC;
-  typedef LPTEXTMETRICW LPTEXTMETRIC;
-#else
-  typedef TEXTMETRICA TEXTMETRIC;
-  typedef PTEXTMETRICA PTEXTMETRIC;
-  typedef NPTEXTMETRICA NPTEXTMETRIC;
-  typedef LPTEXTMETRICA LPTEXTMETRIC;
-#endif
-#endif
-
-#define NTM_REGULAR 0x00000040L
-#define NTM_BOLD 0x00000020L
-#define NTM_ITALIC 0x00000001L
-
-#define NTM_NONNEGATIVE_AC 0x00010000
-#define NTM_PS_OPENTYPE 0x00020000
-#define NTM_TT_OPENTYPE 0x00040000
-#define NTM_MULTIPLEMASTER 0x00080000
-#define NTM_TYPE1 0x00100000
-#define NTM_DSIG 0x00200000
-
-#include <pshpack4.h>
-  typedef struct tagNEWTEXTMETRICA {
-    LONG tmHeight;
-    LONG tmAscent;
-    LONG tmDescent;
-    LONG tmInternalLeading;
-    LONG tmExternalLeading;
-    LONG tmAveCharWidth;
-    LONG tmMaxCharWidth;
-    LONG tmWeight;
-    LONG tmOverhang;
-    LONG tmDigitizedAspectX;
-    LONG tmDigitizedAspectY;
-    BYTE tmFirstChar;
-    BYTE tmLastChar;
-    BYTE tmDefaultChar;
-    BYTE tmBreakChar;
-    BYTE tmItalic;
-    BYTE tmUnderlined;
-    BYTE tmStruckOut;
-    BYTE tmPitchAndFamily;
-    BYTE tmCharSet;
-    DWORD ntmFlags;
-    UINT ntmSizeEM;
-    UINT ntmCellHeight;
-    UINT ntmAvgWidth;
-  } NEWTEXTMETRICA,*PNEWTEXTMETRICA,*NPNEWTEXTMETRICA,*LPNEWTEXTMETRICA;
-
-  typedef struct tagNEWTEXTMETRICW {
-    LONG tmHeight;
-    LONG tmAscent;
-    LONG tmDescent;
-    LONG tmInternalLeading;
-    LONG tmExternalLeading;
-    LONG tmAveCharWidth;
-    LONG tmMaxCharWidth;
-    LONG tmWeight;
-    LONG tmOverhang;
-    LONG tmDigitizedAspectX;
-    LONG tmDigitizedAspectY;
-    WCHAR tmFirstChar;
-    WCHAR tmLastChar;
-    WCHAR tmDefaultChar;
-    WCHAR tmBreakChar;
-    BYTE tmItalic;
-    BYTE tmUnderlined;
-    BYTE tmStruckOut;
-    BYTE tmPitchAndFamily;
-    BYTE tmCharSet;
-    DWORD ntmFlags;
-    UINT ntmSizeEM;
-    UINT ntmCellHeight;
-    UINT ntmAvgWidth;
-  } NEWTEXTMETRICW,*PNEWTEXTMETRICW,*NPNEWTEXTMETRICW,*LPNEWTEXTMETRICW;
-#ifdef UNICODE
-  typedef NEWTEXTMETRICW NEWTEXTMETRIC;
-  typedef PNEWTEXTMETRICW PNEWTEXTMETRIC;
-  typedef NPNEWTEXTMETRICW NPNEWTEXTMETRIC;
-  typedef LPNEWTEXTMETRICW LPNEWTEXTMETRIC;
-#else
-  typedef NEWTEXTMETRICA NEWTEXTMETRIC;
-  typedef PNEWTEXTMETRICA PNEWTEXTMETRIC;
-  typedef NPNEWTEXTMETRICA NPNEWTEXTMETRIC;
-  typedef LPNEWTEXTMETRICA LPNEWTEXTMETRIC;
-#endif
-#include <poppack.h>
-
-  typedef struct tagNEWTEXTMETRICEXA {
-    NEWTEXTMETRICA ntmTm;
-    FONTSIGNATURE ntmFontSig;
-  } NEWTEXTMETRICEXA;
-
-  typedef struct tagNEWTEXTMETRICEXW {
-    NEWTEXTMETRICW ntmTm;
-    FONTSIGNATURE ntmFontSig;
-  } NEWTEXTMETRICEXW;
-#ifdef UNICODE
-  typedef NEWTEXTMETRICEXW NEWTEXTMETRICEX;
-#else
-  typedef NEWTEXTMETRICEXA NEWTEXTMETRICEX;
-#endif
-#endif
-
-  typedef struct tagPELARRAY {
-    LONG paXCount;
-    LONG paYCount;
-    LONG paXExt;
-    LONG paYExt;
-    BYTE paRGBs;
-  } PELARRAY,*PPELARRAY,*NPPELARRAY,*LPPELARRAY;
-
-  typedef struct tagLOGBRUSH {
-    UINT lbStyle;
-    COLORREF lbColor;
-    ULONG_PTR lbHatch;
-  } LOGBRUSH,*PLOGBRUSH,*NPLOGBRUSH,*LPLOGBRUSH;
-
-  typedef struct tagLOGBRUSH32 {
-    UINT lbStyle;
-    COLORREF lbColor;
-    ULONG lbHatch;
-  } LOGBRUSH32,*PLOGBRUSH32,*NPLOGBRUSH32,*LPLOGBRUSH32;
-
-  typedef LOGBRUSH PATTERN;
-  typedef PATTERN *PPATTERN;
-  typedef PATTERN *NPPATTERN;
-  typedef PATTERN *LPPATTERN;
-
-  typedef struct tagLOGPEN {
-    UINT lopnStyle;
-    POINT lopnWidth;
-    COLORREF lopnColor;
-  } LOGPEN,*PLOGPEN,*NPLOGPEN,*LPLOGPEN;
-
-  typedef struct tagEXTLOGPEN {
-    DWORD elpPenStyle;
-    DWORD elpWidth;
-    UINT elpBrushStyle;
-    COLORREF elpColor;
-    ULONG_PTR elpHatch;
-    DWORD elpNumEntries;
-    DWORD elpStyleEntry[1];
-  } EXTLOGPEN,*PEXTLOGPEN,*NPEXTLOGPEN,*LPEXTLOGPEN;
-
-#ifndef _PALETTEENTRY_DEFINED
-#define _PALETTEENTRY_DEFINED
-  typedef struct tagPALETTEENTRY {
-    BYTE peRed;
-    BYTE peGreen;
-    BYTE peBlue;
-    BYTE peFlags;
-  } PALETTEENTRY,*PPALETTEENTRY,*LPPALETTEENTRY;
-#endif
-
-#ifndef _LOGPALETTE_DEFINED
-#define _LOGPALETTE_DEFINED
-
-  typedef struct tagLOGPALETTE {
-    WORD palVersion;
-    WORD palNumEntries;
-    PALETTEENTRY palPalEntry[1];
-  } LOGPALETTE,*PLOGPALETTE,*NPLOGPALETTE,*LPLOGPALETTE;
-#endif
-
-#define LF_FACESIZE 32
-
-  typedef struct tagLOGFONTA {
-    LONG lfHeight;
-    LONG lfWidth;
-    LONG lfEscapement;
-    LONG lfOrientation;
-    LONG lfWeight;
-    BYTE lfItalic;
-    BYTE lfUnderline;
-    BYTE lfStrikeOut;
-    BYTE lfCharSet;
-    BYTE lfOutPrecision;
-    BYTE lfClipPrecision;
-    BYTE lfQuality;
-    BYTE lfPitchAndFamily;
-    CHAR lfFaceName[LF_FACESIZE];
-  } LOGFONTA,*PLOGFONTA,*NPLOGFONTA,*LPLOGFONTA;
-
-  typedef struct tagLOGFONTW {
-    LONG lfHeight;
-    LONG lfWidth;
-    LONG lfEscapement;
-    LONG lfOrientation;
-    LONG lfWeight;
-    BYTE lfItalic;
-    BYTE lfUnderline;
-    BYTE lfStrikeOut;
-    BYTE lfCharSet;
-    BYTE lfOutPrecision;
-    BYTE lfClipPrecision;
-    BYTE lfQuality;
-    BYTE lfPitchAndFamily;
-    WCHAR lfFaceName[LF_FACESIZE];
-  } LOGFONTW,*PLOGFONTW,*NPLOGFONTW,*LPLOGFONTW;
-#ifdef UNICODE
-  typedef LOGFONTW LOGFONT;
-  typedef PLOGFONTW PLOGFONT;
-  typedef NPLOGFONTW NPLOGFONT;
-  typedef LPLOGFONTW LPLOGFONT;
-#else
-  typedef LOGFONTA LOGFONT;
-  typedef PLOGFONTA PLOGFONT;
-  typedef NPLOGFONTA NPLOGFONT;
-  typedef LPLOGFONTA LPLOGFONT;
-#endif
-
-#define LF_FULLFACESIZE 64
-
-  typedef struct tagENUMLOGFONTA {
-    LOGFONTA elfLogFont;
-    BYTE elfFullName[LF_FULLFACESIZE];
-    BYTE elfStyle[LF_FACESIZE];
-  } ENUMLOGFONTA,*LPENUMLOGFONTA;
-
-  typedef struct tagENUMLOGFONTW {
-    LOGFONTW elfLogFont;
-    WCHAR elfFullName[LF_FULLFACESIZE];
-    WCHAR elfStyle[LF_FACESIZE];
-  } ENUMLOGFONTW,*LPENUMLOGFONTW;
-#ifdef UNICODE
-  typedef ENUMLOGFONTW ENUMLOGFONT;
-  typedef LPENUMLOGFONTW LPENUMLOGFONT;
-#else
-  typedef ENUMLOGFONTA ENUMLOGFONT;
-  typedef LPENUMLOGFONTA LPENUMLOGFONT;
-#endif
-
-  typedef struct tagENUMLOGFONTEXA {
-    LOGFONTA elfLogFont;
-    BYTE elfFullName[LF_FULLFACESIZE];
-    BYTE elfStyle[LF_FACESIZE];
-    BYTE elfScript[LF_FACESIZE];
-  } ENUMLOGFONTEXA,*LPENUMLOGFONTEXA;
-
-  typedef struct tagENUMLOGFONTEXW {
-    LOGFONTW elfLogFont;
-    WCHAR elfFullName[LF_FULLFACESIZE];
-    WCHAR elfStyle[LF_FACESIZE];
-    WCHAR elfScript[LF_FACESIZE];
-  } ENUMLOGFONTEXW,*LPENUMLOGFONTEXW;
-#ifdef UNICODE
-  typedef ENUMLOGFONTEXW ENUMLOGFONTEX;
-  typedef LPENUMLOGFONTEXW LPENUMLOGFONTEX;
-#else
-  typedef ENUMLOGFONTEXA ENUMLOGFONTEX;
-  typedef LPENUMLOGFONTEXA LPENUMLOGFONTEX;
-#endif
-
-#define OUT_DEFAULT_PRECIS 0
-#define OUT_STRING_PRECIS 1
-#define OUT_CHARACTER_PRECIS 2
-#define OUT_STROKE_PRECIS 3
-#define OUT_TT_PRECIS 4
-#define OUT_DEVICE_PRECIS 5
-#define OUT_RASTER_PRECIS 6
-#define OUT_TT_ONLY_PRECIS 7
-#define OUT_OUTLINE_PRECIS 8
-#define OUT_SCREEN_OUTLINE_PRECIS 9
-#define OUT_PS_ONLY_PRECIS 10
-
-#define CLIP_DEFAULT_PRECIS 0
-#define CLIP_CHARACTER_PRECIS 1
-#define CLIP_STROKE_PRECIS 2
-#define CLIP_MASK 0xf
-#define CLIP_LH_ANGLES (1<<4)
-#define CLIP_TT_ALWAYS (2<<4)
-#define CLIP_DFA_DISABLE (4<<4)
-#define CLIP_EMBEDDED (8<<4)
-
-#define DEFAULT_QUALITY 0
-#define DRAFT_QUALITY 1
-#define PROOF_QUALITY 2
-#define NONANTIALIASED_QUALITY 3
-#define ANTIALIASED_QUALITY 4
-
-#define CLEARTYPE_QUALITY 5
-#define CLEARTYPE_NATURAL_QUALITY 6
-
-#define DEFAULT_PITCH 0
-#define FIXED_PITCH 1
-#define VARIABLE_PITCH 2
-#define MONO_FONT 8
-
-#define ANSI_CHARSET 0
-#define DEFAULT_CHARSET 1
-#define SYMBOL_CHARSET 2
-#define SHIFTJIS_CHARSET 128
-#define HANGEUL_CHARSET 129
-#define HANGUL_CHARSET 129
-#define GB2312_CHARSET 134
-#define CHINESEBIG5_CHARSET 136
-#define OEM_CHARSET 255
-#define JOHAB_CHARSET 130
-#define HEBREW_CHARSET 177
-#define ARABIC_CHARSET 178
-#define GREEK_CHARSET 161
-#define TURKISH_CHARSET 162
-#define VIETNAMESE_CHARSET 163
-#define THAI_CHARSET 222
-#define EASTEUROPE_CHARSET 238
-#define RUSSIAN_CHARSET 204
-
-#define MAC_CHARSET 77
-#define BALTIC_CHARSET 186
-
-#define FS_LATIN1 0x00000001L
-#define FS_LATIN2 0x00000002L
-#define FS_CYRILLIC 0x00000004L
-#define FS_GREEK 0x00000008L
-#define FS_TURKISH 0x00000010L
-#define FS_HEBREW 0x00000020L
-#define FS_ARABIC 0x00000040L
-#define FS_BALTIC 0x00000080L
-#define FS_VIETNAMESE 0x00000100L
-#define FS_THAI 0x00010000L
-#define FS_JISJAPAN 0x00020000L
-#define FS_CHINESESIMP 0x00040000L
-#define FS_WANSUNG 0x00080000L
-#define FS_CHINESETRAD 0x00100000L
-#define FS_JOHAB 0x00200000L
-#define FS_SYMBOL 0x80000000L
-
-#define FF_DONTCARE (0<<4)
-#define FF_ROMAN (1<<4)
-
-#define FF_SWISS (2<<4)
-
-#define FF_MODERN (3<<4)
-
-#define FF_SCRIPT (4<<4)
-#define FF_DECORATIVE (5<<4)
-
-#define FW_DONTCARE 0
-#define FW_THIN 100
-#define FW_EXTRALIGHT 200
-#define FW_LIGHT 300
-#define FW_NORMAL 400
-#define FW_MEDIUM 500
-#define FW_SEMIBOLD 600
-#define FW_BOLD 700
-#define FW_EXTRABOLD 800
-#define FW_HEAVY 900
-
-#define FW_ULTRALIGHT FW_EXTRALIGHT
-#define FW_REGULAR FW_NORMAL
-#define FW_DEMIBOLD FW_SEMIBOLD
-#define FW_ULTRABOLD FW_EXTRABOLD
-#define FW_BLACK FW_HEAVY
-
-#define PANOSE_COUNT 10
-#define PAN_FAMILYTYPE_INDEX 0
-#define PAN_SERIFSTYLE_INDEX 1
-#define PAN_WEIGHT_INDEX 2
-#define PAN_PROPORTION_INDEX 3
-#define PAN_CONTRAST_INDEX 4
-#define PAN_STROKEVARIATION_INDEX 5
-#define PAN_ARMSTYLE_INDEX 6
-#define PAN_LETTERFORM_INDEX 7
-#define PAN_MIDLINE_INDEX 8
-#define PAN_XHEIGHT_INDEX 9
-
-#define PAN_CULTURE_LATIN 0
-
-  typedef struct tagPANOSE {
-    BYTE bFamilyType;
-    BYTE bSerifStyle;
-    BYTE bWeight;
-    BYTE bProportion;
-    BYTE bContrast;
-    BYTE bStrokeVariation;
-    BYTE bArmStyle;
-    BYTE bLetterform;
-    BYTE bMidline;
-    BYTE bXHeight;
-  } PANOSE,*LPPANOSE;
-
-#define PAN_ANY 0
-#define PAN_NO_FIT 1
-
-#define PAN_FAMILY_TEXT_DISPLAY 2
-#define PAN_FAMILY_SCRIPT 3
-#define PAN_FAMILY_DECORATIVE 4
-#define PAN_FAMILY_PICTORIAL 5
-
-#define PAN_SERIF_COVE 2
-#define PAN_SERIF_OBTUSE_COVE 3
-#define PAN_SERIF_SQUARE_COVE 4
-#define PAN_SERIF_OBTUSE_SQUARE_COVE 5
-#define PAN_SERIF_SQUARE 6
-#define PAN_SERIF_THIN 7
-#define PAN_SERIF_BONE 8
-#define PAN_SERIF_EXAGGERATED 9
-#define PAN_SERIF_TRIANGLE 10
-#define PAN_SERIF_NORMAL_SANS 11
-#define PAN_SERIF_OBTUSE_SANS 12
-#define PAN_SERIF_PERP_SANS 13
-#define PAN_SERIF_FLARED 14
-#define PAN_SERIF_ROUNDED 15
-
-#define PAN_WEIGHT_VERY_LIGHT 2
-#define PAN_WEIGHT_LIGHT 3
-#define PAN_WEIGHT_THIN 4
-#define PAN_WEIGHT_BOOK 5
-#define PAN_WEIGHT_MEDIUM 6
-#define PAN_WEIGHT_DEMI 7
-#define PAN_WEIGHT_BOLD 8
-#define PAN_WEIGHT_HEAVY 9
-#define PAN_WEIGHT_BLACK 10
-#define PAN_WEIGHT_NORD 11
-
-#define PAN_PROP_OLD_STYLE 2
-#define PAN_PROP_MODERN 3
-#define PAN_PROP_EVEN_WIDTH 4
-#define PAN_PROP_EXPANDED 5
-#define PAN_PROP_CONDENSED 6
-#define PAN_PROP_VERY_EXPANDED 7
-#define PAN_PROP_VERY_CONDENSED 8
-#define PAN_PROP_MONOSPACED 9
-
-#define PAN_CONTRAST_NONE 2
-#define PAN_CONTRAST_VERY_LOW 3
-#define PAN_CONTRAST_LOW 4
-#define PAN_CONTRAST_MEDIUM_LOW 5
-#define PAN_CONTRAST_MEDIUM 6
-#define PAN_CONTRAST_MEDIUM_HIGH 7
-#define PAN_CONTRAST_HIGH 8
-#define PAN_CONTRAST_VERY_HIGH 9
-
-#define PAN_STROKE_GRADUAL_DIAG 2
-#define PAN_STROKE_GRADUAL_TRAN 3
-#define PAN_STROKE_GRADUAL_VERT 4
-#define PAN_STROKE_GRADUAL_HORZ 5
-#define PAN_STROKE_RAPID_VERT 6
-#define PAN_STROKE_RAPID_HORZ 7
-#define PAN_STROKE_INSTANT_VERT 8
-
-#define PAN_STRAIGHT_ARMS_HORZ 2
-#define PAN_STRAIGHT_ARMS_WEDGE 3
-#define PAN_STRAIGHT_ARMS_VERT 4
-#define PAN_STRAIGHT_ARMS_SINGLE_SERIF 5
-#define PAN_STRAIGHT_ARMS_DOUBLE_SERIF 6
-#define PAN_BENT_ARMS_HORZ 7
-#define PAN_BENT_ARMS_WEDGE 8
-#define PAN_BENT_ARMS_VERT 9
-#define PAN_BENT_ARMS_SINGLE_SERIF 10
-#define PAN_BENT_ARMS_DOUBLE_SERIF 11
-
-#define PAN_LETT_NORMAL_CONTACT 2
-#define PAN_LETT_NORMAL_WEIGHTED 3
-#define PAN_LETT_NORMAL_BOXED 4
-#define PAN_LETT_NORMAL_FLATTENED 5
-#define PAN_LETT_NORMAL_ROUNDED 6
-#define PAN_LETT_NORMAL_OFF_CENTER 7
-#define PAN_LETT_NORMAL_SQUARE 8
-#define PAN_LETT_OBLIQUE_CONTACT 9
-#define PAN_LETT_OBLIQUE_WEIGHTED 10
-#define PAN_LETT_OBLIQUE_BOXED 11
-#define PAN_LETT_OBLIQUE_FLATTENED 12
-#define PAN_LETT_OBLIQUE_ROUNDED 13
-#define PAN_LETT_OBLIQUE_OFF_CENTER 14
-#define PAN_LETT_OBLIQUE_SQUARE 15
-
-#define PAN_MIDLINE_STANDARD_TRIMMED 2
-#define PAN_MIDLINE_STANDARD_POINTED 3
-#define PAN_MIDLINE_STANDARD_SERIFED 4
-#define PAN_MIDLINE_HIGH_TRIMMED 5
-#define PAN_MIDLINE_HIGH_POINTED 6
-#define PAN_MIDLINE_HIGH_SERIFED 7
-#define PAN_MIDLINE_CONSTANT_TRIMMED 8
-#define PAN_MIDLINE_CONSTANT_POINTED 9
-#define PAN_MIDLINE_CONSTANT_SERIFED 10
-#define PAN_MIDLINE_LOW_TRIMMED 11
-#define PAN_MIDLINE_LOW_POINTED 12
-#define PAN_MIDLINE_LOW_SERIFED 13
-
-#define PAN_XHEIGHT_CONSTANT_SMALL 2
-#define PAN_XHEIGHT_CONSTANT_STD 3
-#define PAN_XHEIGHT_CONSTANT_LARGE 4
-#define PAN_XHEIGHT_DUCKING_SMALL 5
-#define PAN_XHEIGHT_DUCKING_STD 6
-#define PAN_XHEIGHT_DUCKING_LARGE 7
-
-#define ELF_VENDOR_SIZE 4
-
-  typedef struct tagEXTLOGFONTA {
-    LOGFONTA elfLogFont;
-    BYTE elfFullName[LF_FULLFACESIZE];
-    BYTE elfStyle[LF_FACESIZE];
-    DWORD elfVersion;
-    DWORD elfStyleSize;
-    DWORD elfMatch;
-    DWORD elfReserved;
-    BYTE elfVendorId[ELF_VENDOR_SIZE];
-    DWORD elfCulture;
-    PANOSE elfPanose;
-  } EXTLOGFONTA,*PEXTLOGFONTA,*NPEXTLOGFONTA,*LPEXTLOGFONTA;
-
-  typedef struct tagEXTLOGFONTW {
-    LOGFONTW elfLogFont;
-    WCHAR elfFullName[LF_FULLFACESIZE];
-    WCHAR elfStyle[LF_FACESIZE];
-    DWORD elfVersion;
-    DWORD elfStyleSize;
-    DWORD elfMatch;
-    DWORD elfReserved;
-    BYTE elfVendorId[ELF_VENDOR_SIZE];
-    DWORD elfCulture;
-    PANOSE elfPanose;
-  } EXTLOGFONTW,*PEXTLOGFONTW,*NPEXTLOGFONTW,*LPEXTLOGFONTW;
-#ifdef UNICODE
-  typedef EXTLOGFONTW EXTLOGFONT;
-  typedef PEXTLOGFONTW PEXTLOGFONT;
-  typedef NPEXTLOGFONTW NPEXTLOGFONT;
-  typedef LPEXTLOGFONTW LPEXTLOGFONT;
-#else
-  typedef EXTLOGFONTA EXTLOGFONT;
-  typedef PEXTLOGFONTA PEXTLOGFONT;
-  typedef NPEXTLOGFONTA NPEXTLOGFONT;
-  typedef LPEXTLOGFONTA LPEXTLOGFONT;
-#endif
-
-#define ELF_VERSION 0
-#define ELF_CULTURE_LATIN 0
-
-#define RASTER_FONTTYPE 0x0001
-#define DEVICE_FONTTYPE 0x002
-#define TRUETYPE_FONTTYPE 0x004
-
-#define RGB(r,g,b) ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))
-#define PALETTERGB(r,g,b) (0x02000000 | RGB(r,g,b))
-#define PALETTEINDEX(i) ((COLORREF)(0x01000000 | (DWORD)(WORD)(i)))
-
-#define PC_RESERVED 0x01
-#define PC_EXPLICIT 0x02
-#define PC_NOCOLLAPSE 0x04
-
-#define GetRValue(rgb) (LOBYTE(rgb))
-#define GetGValue(rgb) (LOBYTE(((WORD)(rgb)) >> 8))
-#define GetBValue(rgb) (LOBYTE((rgb)>>16))
-
-#define TRANSPARENT 1
-#define OPAQUE 2
-#define BKMODE_LAST 2
-
-#define GM_COMPATIBLE 1
-#define GM_ADVANCED 2
-#define GM_LAST 2
-
-#define PT_CLOSEFIGURE 0x01
-#define PT_LINETO 0x02
-#define PT_BEZIERTO 0x04
-#define PT_MOVETO 0x06
-
-#define MM_TEXT 1
-#define MM_LOMETRIC 2
-#define MM_HIMETRIC 3
-#define MM_LOENGLISH 4
-#define MM_HIENGLISH 5
-#define MM_TWIPS 6
-#define MM_ISOTROPIC 7
-#define MM_ANISOTROPIC 8
-
-#define MM_MIN MM_TEXT
-#define MM_MAX MM_ANISOTROPIC
-#define MM_MAX_FIXEDSCALE MM_TWIPS
-
-#define ABSOLUTE 1
-#define RELATIVE 2
-
-#define WHITE_BRUSH 0
-#define LTGRAY_BRUSH 1
-#define GRAY_BRUSH 2
-#define DKGRAY_BRUSH 3
-#define BLACK_BRUSH 4
-#define NULL_BRUSH 5
-#define HOLLOW_BRUSH NULL_BRUSH
-#define WHITE_PEN 6
-#define BLACK_PEN 7
-#define NULL_PEN 8
-#define OEM_FIXED_FONT 10
-#define ANSI_FIXED_FONT 11
-#define ANSI_VAR_FONT 12
-#define SYSTEM_FONT 13
-#define DEVICE_DEFAULT_FONT 14
-#define DEFAULT_PALETTE 15
-#define SYSTEM_FIXED_FONT 16
-
-#define DEFAULT_GUI_FONT 17
-
-#define DC_BRUSH 18
-#define DC_PEN 19
-
-#define STOCK_LAST 19
-
-#define CLR_INVALID 0xFFFFFFFF
-
-#define BS_SOLID 0
-#define BS_NULL 1
-#define BS_HOLLOW BS_NULL
-#define BS_HATCHED 2
-#define BS_PATTERN 3
-#define BS_INDEXED 4
-#define BS_DIBPATTERN 5
-#define BS_DIBPATTERNPT 6
-#define BS_PATTERN8X8 7
-#define BS_DIBPATTERN8X8 8
-#define BS_MONOPATTERN 9
-
-#define HS_HORIZONTAL 0
-#define HS_VERTICAL 1
-#define HS_FDIAGONAL 2
-#define HS_BDIAGONAL 3
-#define HS_CROSS 4
-#define HS_DIAGCROSS 5
-
-#define PS_SOLID 0
-#define PS_DASH 1
-#define PS_DOT 2
-#define PS_DASHDOT 3
-#define PS_DASHDOTDOT 4
-#define PS_NULL 5
-#define PS_INSIDEFRAME 6
-#define PS_USERSTYLE 7
-#define PS_ALTERNATE 8
-#define PS_STYLE_MASK 0x0000000F
-
-#define PS_ENDCAP_ROUND 0x00000000
-#define PS_ENDCAP_SQUARE 0x00000100
-#define PS_ENDCAP_FLAT 0x00000200
-#define PS_ENDCAP_MASK 0x00000F00
-
-#define PS_JOIN_ROUND 0x00000000
-#define PS_JOIN_BEVEL 0x00001000
-#define PS_JOIN_MITER 0x00002000
-#define PS_JOIN_MASK 0x0000F000
-
-#define PS_COSMETIC 0x00000000
-#define PS_GEOMETRIC 0x00010000
-#define PS_TYPE_MASK 0x000F0000
-
-#define AD_COUNTERCLOCKWISE 1
-#define AD_CLOCKWISE 2
-
-#define DRIVERVERSION 0
-#define TECHNOLOGY 2
-#define HORZSIZE 4
-#define VERTSIZE 6
-#define HORZRES 8
-#define VERTRES 10
-#define BITSPIXEL 12
-#define PLANES 14
-#define NUMBRUSHES 16
-#define NUMPENS 18
-#define NUMMARKERS 20
-#define NUMFONTS 22
-#define NUMCOLORS 24
-#define PDEVICESIZE 26
-#define CURVECAPS 28
-#define LINECAPS 30
-#define POLYGONALCAPS 32
-#define TEXTCAPS 34
-#define CLIPCAPS 36
-#define RASTERCAPS 38
-#define ASPECTX 40
-#define ASPECTY 42
-#define ASPECTXY 44
-
-#define LOGPIXELSX 88
-#define LOGPIXELSY 90
-
-#define SIZEPALETTE 104
-#define NUMRESERVED 106
-#define COLORRES 108
-
-#define PHYSICALWIDTH 110
-#define PHYSICALHEIGHT 111
-#define PHYSICALOFFSETX 112
-#define PHYSICALOFFSETY 113
-#define SCALINGFACTORX 114
-#define SCALINGFACTORY 115
-
-#define VREFRESH 116
-
-#define DESKTOPVERTRES 117
-
-#define DESKTOPHORZRES 118
-
-#define BLTALIGNMENT 119
-
-#define SHADEBLENDCAPS 120
-#define COLORMGMTCAPS 121
-
-#ifndef NOGDICAPMASKS
-#define DT_PLOTTER 0
-#define DT_RASDISPLAY 1
-#define DT_RASPRINTER 2
-#define DT_RASCAMERA 3
-#define DT_CHARSTREAM 4
-#define DT_METAFILE 5
-#define DT_DISPFILE 6
-
-#define CC_NONE 0
-#define CC_CIRCLES 1
-#define CC_PIE 2
-#define CC_CHORD 4
-#define CC_ELLIPSES 8
-#define CC_WIDE 16
-#define CC_STYLED 32
-#define CC_WIDESTYLED 64
-#define CC_INTERIORS 128
-#define CC_ROUNDRECT 256
-
-#define LC_NONE 0
-#define LC_POLYLINE 2
-#define LC_MARKER 4
-#define LC_POLYMARKER 8
-#define LC_WIDE 16
-#define LC_STYLED 32
-#define LC_WIDESTYLED 64
-#define LC_INTERIORS 128
-
-#define PC_NONE 0
-#define PC_POLYGON 1
-#define PC_RECTANGLE 2
-#define PC_WINDPOLYGON 4
-#define PC_TRAPEZOID 4
-#define PC_SCANLINE 8
-#define PC_WIDE 16
-#define PC_STYLED 32
-#define PC_WIDESTYLED 64
-#define PC_INTERIORS 128
-#define PC_POLYPOLYGON 256
-#define PC_PATHS 512
-
-#define CP_NONE 0
-#define CP_RECTANGLE 1
-#define CP_REGION 2
-
-#define TC_OP_CHARACTER 0x00000001
-#define TC_OP_STROKE 0x00000002
-#define TC_CP_STROKE 0x00000004
-#define TC_CR_90 0x00000008
-#define TC_CR_ANY 0x00000010
-#define TC_SF_X_YINDEP 0x00000020
-#define TC_SA_DOUBLE 0x00000040
-#define TC_SA_INTEGER 0x00000080
-#define TC_SA_CONTIN 0x00000100
-#define TC_EA_DOUBLE 0x00000200
-#define TC_IA_ABLE 0x00000400
-#define TC_UA_ABLE 0x00000800
-#define TC_SO_ABLE 0x00001000
-#define TC_RA_ABLE 0x00002000
-#define TC_VA_ABLE 0x00004000
-#define TC_RESERVED 0x00008000
-#define TC_SCROLLBLT 0x00010000
-#endif
-
-#define RC_NONE
-#define RC_BITBLT 1
-#define RC_BANDING 2
-#define RC_SCALING 4
-#define RC_BITMAP64 8
-#define RC_GDI20_OUTPUT 0x0010
-#define RC_GDI20_STATE 0x0020
-#define RC_SAVEBITMAP 0x0040
-#define RC_DI_BITMAP 0x0080
-#define RC_PALETTE 0x0100
-#define RC_DIBTODEV 0x0200
-#define RC_BIGFONT 0x0400
-#define RC_STRETCHBLT 0x0800
-#define RC_FLOODFILL 0x1000
-#define RC_STRETCHDIB 0x2000
-#define RC_OP_DX_OUTPUT 0x4000
-#define RC_DEVBITS 0x8000
-
-#define SB_NONE 0x00000000
-#define SB_CONST_ALPHA 0x00000001
-#define SB_PIXEL_ALPHA 0x00000002
-#define SB_PREMULT_ALPHA 0x00000004
-
-#define SB_GRAD_RECT 0x00000010
-#define SB_GRAD_TRI 0x00000020
-
-#define CM_NONE 0x00000000
-#define CM_DEVICE_ICM 0x00000001
-#define CM_GAMMA_RAMP 0x00000002
-#define CM_CMYK_COLOR 0x00000004
-
-#define DIB_RGB_COLORS 0
-#define DIB_PAL_COLORS 1
-
-#define SYSPAL_ERROR 0
-#define SYSPAL_STATIC 1
-#define SYSPAL_NOSTATIC 2
-#define SYSPAL_NOSTATIC256 3
-
-#define CBM_INIT 0x04L
-
-#define FLOODFILLBORDER 0
-#define FLOODFILLSURFACE 1
-
-#define CCHDEVICENAME 32
-
-#define CCHFORMNAME 32
-
-  typedef struct _devicemodeA {
-    BYTE dmDeviceName[CCHDEVICENAME];
-    WORD dmSpecVersion;
-    WORD dmDriverVersion;
-    WORD dmSize;
-    WORD dmDriverExtra;
-    DWORD dmFields;
-    union {
-      struct {
-        short dmOrientation;
-        short dmPaperSize;
-        short dmPaperLength;
-        short dmPaperWidth;
-        short dmScale;
-        short dmCopies;
-        short dmDefaultSource;
-        short dmPrintQuality;
-      };
-      struct {
-        POINTL dmPosition;
-        DWORD dmDisplayOrientation;
-        DWORD dmDisplayFixedOutput;
-      };
-    };
-    short dmColor;
-    short dmDuplex;
-    short dmYResolution;
-    short dmTTOption;
-    short dmCollate;
-    BYTE dmFormName[CCHFORMNAME];
-    WORD dmLogPixels;
-    DWORD dmBitsPerPel;
-    DWORD dmPelsWidth;
-    DWORD dmPelsHeight;
-    union {
-      DWORD dmDisplayFlags;
-      DWORD dmNup;
-    };
-    DWORD dmDisplayFrequency;
-    DWORD dmICMMethod;
-    DWORD dmICMIntent;
-    DWORD dmMediaType;
-    DWORD dmDitherType;
-    DWORD dmReserved1;
-    DWORD dmReserved2;
-    DWORD dmPanningWidth;
-    DWORD dmPanningHeight;
-  } DEVMODEA,*PDEVMODEA,*NPDEVMODEA,*LPDEVMODEA;
-
-  typedef struct _devicemodeW {
-    WCHAR dmDeviceName[CCHDEVICENAME];
-    WORD dmSpecVersion;
-    WORD dmDriverVersion;
-    WORD dmSize;
-    WORD dmDriverExtra;
-    DWORD dmFields;
-    union {
-      struct {
-        short dmOrientation;
-        short dmPaperSize;
-        short dmPaperLength;
-        short dmPaperWidth;
-        short dmScale;
-        short dmCopies;
-        short dmDefaultSource;
-        short dmPrintQuality;
-      };
-      struct {
-        POINTL dmPosition;
-        DWORD dmDisplayOrientation;
-        DWORD dmDisplayFixedOutput;
-      };
-    };
-    short dmColor;
-    short dmDuplex;
-    short dmYResolution;
-    short dmTTOption;
-    short dmCollate;
-    WCHAR dmFormName[CCHFORMNAME];
-    WORD dmLogPixels;
-    DWORD dmBitsPerPel;
-    DWORD dmPelsWidth;
-    DWORD dmPelsHeight;
-    union {
-      DWORD dmDisplayFlags;
-      DWORD dmNup;
-    };
-    DWORD dmDisplayFrequency;
-    DWORD dmICMMethod;
-    DWORD dmICMIntent;
-    DWORD dmMediaType;
-    DWORD dmDitherType;
-    DWORD dmReserved1;
-    DWORD dmReserved2;
-    DWORD dmPanningWidth;
-    DWORD dmPanningHeight;
-  } DEVMODEW,*PDEVMODEW,*NPDEVMODEW,*LPDEVMODEW;
-#ifdef UNICODE
-  typedef DEVMODEW DEVMODE;
-  typedef PDEVMODEW PDEVMODE;
-  typedef NPDEVMODEW NPDEVMODE;
-  typedef LPDEVMODEW LPDEVMODE;
-#else
-  typedef DEVMODEA DEVMODE;
-  typedef PDEVMODEA PDEVMODE;
-  typedef NPDEVMODEA NPDEVMODE;
-  typedef LPDEVMODEA LPDEVMODE;
-#endif
-
-#define DM_SPECVERSION 0x0401
-
-#define DM_ORIENTATION 0x00000001L
-#define DM_PAPERSIZE 0x00000002L
-#define DM_PAPERLENGTH 0x00000004L
-#define DM_PAPERWIDTH 0x00000008L
-#define DM_SCALE 0x00000010L
-#define DM_POSITION 0x00000020L
-#define DM_NUP 0x00000040L
-#define DM_DISPLAYORIENTATION 0x00000080L
-#define DM_COPIES 0x00000100L
-#define DM_DEFAULTSOURCE 0x00000200L
-#define DM_PRINTQUALITY 0x00000400L
-#define DM_COLOR 0x00000800L
-#define DM_DUPLEX 0x00001000L
-#define DM_YRESOLUTION 0x00002000L
-#define DM_TTOPTION 0x00004000L
-#define DM_COLLATE 0x00008000L
-#define DM_FORMNAME 0x00010000L
-#define DM_LOGPIXELS 0x00020000L
-#define DM_BITSPERPEL 0x00040000L
-#define DM_PELSWIDTH 0x00080000L
-#define DM_PELSHEIGHT 0x00100000L
-#define DM_DISPLAYFLAGS 0x00200000L
-#define DM_DISPLAYFREQUENCY 0x00400000L
-#define DM_ICMMETHOD 0x00800000L
-#define DM_ICMINTENT 0x01000000L
-#define DM_MEDIATYPE 0x02000000L
-#define DM_DITHERTYPE 0x04000000L
-#define DM_PANNINGWIDTH 0x08000000L
-#define DM_PANNINGHEIGHT 0x10000000L
-#define DM_DISPLAYFIXEDOUTPUT 0x20000000L
-
-#define DMORIENT_PORTRAIT 1
-#define DMORIENT_LANDSCAPE 2
-
-#define DMPAPER_FIRST DMPAPER_LETTER
-#define DMPAPER_LETTER 1
-#define DMPAPER_LETTERSMALL 2
-#define DMPAPER_TABLOID 3
-#define DMPAPER_LEDGER 4
-#define DMPAPER_LEGAL 5
-#define DMPAPER_STATEMENT 6
-#define DMPAPER_EXECUTIVE 7
-#define DMPAPER_A3 8
-#define DMPAPER_A4 9
-#define DMPAPER_A4SMALL 10
-#define DMPAPER_A5 11
-#define DMPAPER_B4 12
-#define DMPAPER_B5 13
-#define DMPAPER_FOLIO 14
-#define DMPAPER_QUARTO 15
-#define DMPAPER_10X14 16
-#define DMPAPER_11X17 17
-#define DMPAPER_NOTE 18
-#define DMPAPER_ENV_9 19
-#define DMPAPER_ENV_10 20
-#define DMPAPER_ENV_11 21
-#define DMPAPER_ENV_12 22
-#define DMPAPER_ENV_14 23
-#define DMPAPER_CSHEET 24
-#define DMPAPER_DSHEET 25
-#define DMPAPER_ESHEET 26
-#define DMPAPER_ENV_DL 27
-#define DMPAPER_ENV_C5 28
-#define DMPAPER_ENV_C3 29
-#define DMPAPER_ENV_C4 30
-#define DMPAPER_ENV_C6 31
-#define DMPAPER_ENV_C65 32
-#define DMPAPER_ENV_B4 33
-#define DMPAPER_ENV_B5 34
-#define DMPAPER_ENV_B6 35
-#define DMPAPER_ENV_ITALY 36
-#define DMPAPER_ENV_MONARCH 37
-#define DMPAPER_ENV_PERSONAL 38
-#define DMPAPER_FANFOLD_US 39
-#define DMPAPER_FANFOLD_STD_GERMAN 40
-#define DMPAPER_FANFOLD_LGL_GERMAN 41
-#define DMPAPER_ISO_B4 42
-#define DMPAPER_JAPANESE_POSTCARD 43
-#define DMPAPER_9X11 44
-#define DMPAPER_10X11 45
-#define DMPAPER_15X11 46
-#define DMPAPER_ENV_INVITE 47
-#define DMPAPER_RESERVED_48 48
-#define DMPAPER_RESERVED_49 49
-#define DMPAPER_LETTER_EXTRA 50
-#define DMPAPER_LEGAL_EXTRA 51
-#define DMPAPER_TABLOID_EXTRA 52
-#define DMPAPER_A4_EXTRA 53
-#define DMPAPER_LETTER_TRANSVERSE 54
-#define DMPAPER_A4_TRANSVERSE 55
-#define DMPAPER_LETTER_EXTRA_TRANSVERSE 56
-#define DMPAPER_A_PLUS 57
-#define DMPAPER_B_PLUS 58
-#define DMPAPER_LETTER_PLUS 59
-#define DMPAPER_A4_PLUS 60
-#define DMPAPER_A5_TRANSVERSE 61
-#define DMPAPER_B5_TRANSVERSE 62
-#define DMPAPER_A3_EXTRA 63
-#define DMPAPER_A5_EXTRA 64
-#define DMPAPER_B5_EXTRA 65
-#define DMPAPER_A2 66
-#define DMPAPER_A3_TRANSVERSE 67
-#define DMPAPER_A3_EXTRA_TRANSVERSE 68
-#define DMPAPER_DBL_JAPANESE_POSTCARD 69
-#define DMPAPER_A6 70
-#define DMPAPER_JENV_KAKU2 71
-#define DMPAPER_JENV_KAKU3 72
-#define DMPAPER_JENV_CHOU3 73
-#define DMPAPER_JENV_CHOU4 74
-#define DMPAPER_LETTER_ROTATED 75
-#define DMPAPER_A3_ROTATED 76
-#define DMPAPER_A4_ROTATED 77
-#define DMPAPER_A5_ROTATED 78
-#define DMPAPER_B4_JIS_ROTATED 79
-#define DMPAPER_B5_JIS_ROTATED 80
-#define DMPAPER_JAPANESE_POSTCARD_ROTATED 81
-#define DMPAPER_DBL_JAPANESE_POSTCARD_ROTATED 82
-#define DMPAPER_A6_ROTATED 83
-#define DMPAPER_JENV_KAKU2_ROTATED 84
-#define DMPAPER_JENV_KAKU3_ROTATED 85
-#define DMPAPER_JENV_CHOU3_ROTATED 86
-#define DMPAPER_JENV_CHOU4_ROTATED 87
-#define DMPAPER_B6_JIS 88
-#define DMPAPER_B6_JIS_ROTATED 89
-#define DMPAPER_12X11 90
-#define DMPAPER_JENV_YOU4 91
-#define DMPAPER_JENV_YOU4_ROTATED 92
-#define DMPAPER_P16K 93
-#define DMPAPER_P32K 94
-#define DMPAPER_P32KBIG 95
-#define DMPAPER_PENV_1 96
-#define DMPAPER_PENV_2 97
-#define DMPAPER_PENV_3 98
-#define DMPAPER_PENV_4 99
-#define DMPAPER_PENV_5 100
-#define DMPAPER_PENV_6 101
-#define DMPAPER_PENV_7 102
-#define DMPAPER_PENV_8 103
-#define DMPAPER_PENV_9 104
-#define DMPAPER_PENV_10 105
-#define DMPAPER_P16K_ROTATED 106
-#define DMPAPER_P32K_ROTATED 107
-#define DMPAPER_P32KBIG_ROTATED 108
-#define DMPAPER_PENV_1_ROTATED 109
-#define DMPAPER_PENV_2_ROTATED 110
-#define DMPAPER_PENV_3_ROTATED 111
-#define DMPAPER_PENV_4_ROTATED 112
-#define DMPAPER_PENV_5_ROTATED 113
-#define DMPAPER_PENV_6_ROTATED 114
-#define DMPAPER_PENV_7_ROTATED 115
-#define DMPAPER_PENV_8_ROTATED 116
-#define DMPAPER_PENV_9_ROTATED 117
-#define DMPAPER_PENV_10_ROTATED 118
-
-#define DMPAPER_LAST DMPAPER_PENV_10_ROTATED
-
-#define DMPAPER_USER 256
-
-#define DMBIN_FIRST DMBIN_UPPER
-#define DMBIN_UPPER 1
-#define DMBIN_ONLYONE 1
-#define DMBIN_LOWER 2
-#define DMBIN_MIDDLE 3
-#define DMBIN_MANUAL 4
-#define DMBIN_ENVELOPE 5
-#define DMBIN_ENVMANUAL 6
-#define DMBIN_AUTO 7
-#define DMBIN_TRACTOR 8
-#define DMBIN_SMALLFMT 9
-#define DMBIN_LARGEFMT 10
-#define DMBIN_LARGECAPACITY 11
-#define DMBIN_CASSETTE 14
-#define DMBIN_FORMSOURCE 15
-#define DMBIN_LAST DMBIN_FORMSOURCE
-
-#define DMBIN_USER 256
-
-#define DMRES_DRAFT (-1)
-#define DMRES_LOW (-2)
-#define DMRES_MEDIUM (-3)
-#define DMRES_HIGH (-4)
-
-#define DMCOLOR_MONOCHROME 1
-#define DMCOLOR_COLOR 2
-
-#define DMDUP_SIMPLEX 1
-#define DMDUP_VERTICAL 2
-#define DMDUP_HORIZONTAL 3
-
-#define DMTT_BITMAP 1
-#define DMTT_DOWNLOAD 2
-#define DMTT_SUBDEV 3
-#define DMTT_DOWNLOAD_OUTLINE 4
-
-#define DMCOLLATE_FALSE 0
-#define DMCOLLATE_TRUE 1
-
-#define DMDO_DEFAULT 0
-#define DMDO_90 1
-#define DMDO_180 2
-#define DMDO_270 3
-
-#define DMDFO_DEFAULT 0
-#define DMDFO_STRETCH 1
-#define DMDFO_CENTER 2
-
-#define DMDISPLAYFLAGS_TEXTMODE 0x00000004
-
-#define DMNUP_SYSTEM 1
-#define DMNUP_ONEUP 2
-
-#define DMICMMETHOD_NONE 1
-#define DMICMMETHOD_SYSTEM 2
-#define DMICMMETHOD_DRIVER 3
-#define DMICMMETHOD_DEVICE 4
-
-#define DMICMMETHOD_USER 256
-
-#define DMICM_SATURATE 1
-#define DMICM_CONTRAST 2
-#define DMICM_COLORIMETRIC 3
-#define DMICM_ABS_COLORIMETRIC 4
-
-#define DMICM_USER 256
-
-#define DMMEDIA_STANDARD 1
-#define DMMEDIA_TRANSPARENCY 2
-#define DMMEDIA_GLOSSY 3
-
-#define DMMEDIA_USER 256
-
-#define DMDITHER_NONE 1
-#define DMDITHER_COARSE 2
-#define DMDITHER_FINE 3
-#define DMDITHER_LINEART 4
-#define DMDITHER_ERRORDIFFUSION 5
-#define DMDITHER_RESERVED6 6
-#define DMDITHER_RESERVED7 7
-#define DMDITHER_RESERVED8 8
-#define DMDITHER_RESERVED9 9
-#define DMDITHER_GRAYSCALE 10
-
-#define DMDITHER_USER 256
-
-  typedef struct _DISPLAY_DEVICEA {
-    DWORD cb;
-    CHAR DeviceName[32];
-    CHAR DeviceString[128];
-    DWORD StateFlags;
-    CHAR DeviceID[128];
-    CHAR DeviceKey[128];
-  } DISPLAY_DEVICEA,*PDISPLAY_DEVICEA,*LPDISPLAY_DEVICEA;
-  typedef struct _DISPLAY_DEVICEW {
-    DWORD cb;
-    WCHAR DeviceName[32];
-    WCHAR DeviceString[128];
-    DWORD StateFlags;
-    WCHAR DeviceID[128];
-    WCHAR DeviceKey[128];
-  } DISPLAY_DEVICEW,*PDISPLAY_DEVICEW,*LPDISPLAY_DEVICEW;
-#ifdef UNICODE
-  typedef DISPLAY_DEVICEW DISPLAY_DEVICE;
-  typedef PDISPLAY_DEVICEW PDISPLAY_DEVICE;
-  typedef LPDISPLAY_DEVICEW LPDISPLAY_DEVICE;
-#else
-  typedef DISPLAY_DEVICEA DISPLAY_DEVICE;
-  typedef PDISPLAY_DEVICEA PDISPLAY_DEVICE;
-  typedef LPDISPLAY_DEVICEA LPDISPLAY_DEVICE;
-#endif
-
-#define DISPLAY_DEVICE_ATTACHED_TO_DESKTOP 0x00000001
-#define DISPLAY_DEVICE_MULTI_DRIVER 0x00000002
-#define DISPLAY_DEVICE_PRIMARY_DEVICE 0x00000004
-#define DISPLAY_DEVICE_MIRRORING_DRIVER 0x00000008
-#define DISPLAY_DEVICE_VGA_COMPATIBLE 0x00000010
-#define DISPLAY_DEVICE_REMOVABLE 0x00000020
-#define DISPLAY_DEVICE_MODESPRUNED 0x08000000
-#define DISPLAY_DEVICE_REMOTE 0x04000000
-#define DISPLAY_DEVICE_DISCONNECT 0x02000000
-
-#define DISPLAY_DEVICE_ACTIVE 0x00000001
-#define DISPLAY_DEVICE_ATTACHED 0x00000002
-
-#define RDH_RECTANGLES 1
-
-  typedef struct _RGNDATAHEADER {
-    DWORD dwSize;
-    DWORD iType;
-    DWORD nCount;
-    DWORD nRgnSize;
-    RECT rcBound;
-  } RGNDATAHEADER,*PRGNDATAHEADER;
-
-  typedef struct _RGNDATA {
-    RGNDATAHEADER rdh;
-    char Buffer[1];
-  } RGNDATA,*PRGNDATA,*NPRGNDATA,*LPRGNDATA;
-
-#define SYSRGN 4
-
-  typedef struct _ABC {
-    int abcA;
-    UINT abcB;
-    int abcC;
-  } ABC,*PABC,*NPABC,*LPABC;
-
-  typedef struct _ABCFLOAT {
-    FLOAT abcfA;
-    FLOAT abcfB;
-    FLOAT abcfC;
-  } ABCFLOAT,*PABCFLOAT,*NPABCFLOAT,*LPABCFLOAT;
-
-#ifndef NOTEXTMETRIC
-
-  typedef struct _OUTLINETEXTMETRICA {
-    UINT otmSize;
-    TEXTMETRICA otmTextMetrics;
-    BYTE otmFiller;
-    PANOSE otmPanoseNumber;
-    UINT otmfsSelection;
-    UINT otmfsType;
-    int otmsCharSlopeRise;
-    int otmsCharSlopeRun;
-    int otmItalicAngle;
-    UINT otmEMSquare;
-    int otmAscent;
-    int otmDescent;
-    UINT otmLineGap;
-    UINT otmsCapEmHeight;
-    UINT otmsXHeight;
-    RECT otmrcFontBox;
-    int otmMacAscent;
-    int otmMacDescent;
-    UINT otmMacLineGap;
-    UINT otmusMinimumPPEM;
-    POINT otmptSubscriptSize;
-    POINT otmptSubscriptOffset;
-    POINT otmptSuperscriptSize;
-    POINT otmptSuperscriptOffset;
-    UINT otmsStrikeoutSize;
-    int otmsStrikeoutPosition;
-    int otmsUnderscoreSize;
-    int otmsUnderscorePosition;
-    PSTR otmpFamilyName;
-    PSTR otmpFaceName;
-    PSTR otmpStyleName;
-    PSTR otmpFullName;
-  } OUTLINETEXTMETRICA,*POUTLINETEXTMETRICA,*NPOUTLINETEXTMETRICA,*LPOUTLINETEXTMETRICA;
-
-  typedef struct _OUTLINETEXTMETRICW {
-    UINT otmSize;
-    TEXTMETRICW otmTextMetrics;
-    BYTE otmFiller;
-    PANOSE otmPanoseNumber;
-    UINT otmfsSelection;
-    UINT otmfsType;
-    int otmsCharSlopeRise;
-    int otmsCharSlopeRun;
-    int otmItalicAngle;
-    UINT otmEMSquare;
-    int otmAscent;
-    int otmDescent;
-    UINT otmLineGap;
-    UINT otmsCapEmHeight;
-    UINT otmsXHeight;
-    RECT otmrcFontBox;
-    int otmMacAscent;
-    int otmMacDescent;
-    UINT otmMacLineGap;
-    UINT otmusMinimumPPEM;
-    POINT otmptSubscriptSize;
-    POINT otmptSubscriptOffset;
-    POINT otmptSuperscriptSize;
-    POINT otmptSuperscriptOffset;
-    UINT otmsStrikeoutSize;
-    int otmsStrikeoutPosition;
-    int otmsUnderscoreSize;
-    int otmsUnderscorePosition;
-    PSTR otmpFamilyName;
-    PSTR otmpFaceName;
-    PSTR otmpStyleName;
-    PSTR otmpFullName;
-  } OUTLINETEXTMETRICW,*POUTLINETEXTMETRICW,*NPOUTLINETEXTMETRICW,*LPOUTLINETEXTMETRICW;
-#ifdef UNICODE
-  typedef OUTLINETEXTMETRICW OUTLINETEXTMETRIC;
-  typedef POUTLINETEXTMETRICW POUTLINETEXTMETRIC;
-  typedef NPOUTLINETEXTMETRICW NPOUTLINETEXTMETRIC;
-  typedef LPOUTLINETEXTMETRICW LPOUTLINETEXTMETRIC;
-#else
-  typedef OUTLINETEXTMETRICA OUTLINETEXTMETRIC;
-  typedef POUTLINETEXTMETRICA POUTLINETEXTMETRIC;
-  typedef NPOUTLINETEXTMETRICA NPOUTLINETEXTMETRIC;
-  typedef LPOUTLINETEXTMETRICA LPOUTLINETEXTMETRIC;
-#endif
-#endif
-
-  typedef struct tagPOLYTEXTA {
-    int x;
-    int y;
-    UINT n;
-    LPCSTR lpstr;
-    UINT uiFlags;
-    RECT rcl;
-    int *pdx;
-  } POLYTEXTA,*PPOLYTEXTA,*NPPOLYTEXTA,*LPPOLYTEXTA;
-
-  typedef struct tagPOLYTEXTW {
-    int x;
-    int y;
-    UINT n;
-    LPCWSTR lpstr;
-    UINT uiFlags;
-    RECT rcl;
-    int *pdx;
-  } POLYTEXTW,*PPOLYTEXTW,*NPPOLYTEXTW,*LPPOLYTEXTW;
-#ifdef UNICODE
-  typedef POLYTEXTW POLYTEXT;
-  typedef PPOLYTEXTW PPOLYTEXT;
-  typedef NPPOLYTEXTW NPPOLYTEXT;
-  typedef LPPOLYTEXTW LPPOLYTEXT;
-#else
-  typedef POLYTEXTA POLYTEXT;
-  typedef PPOLYTEXTA PPOLYTEXT;
-  typedef NPPOLYTEXTA NPPOLYTEXT;
-  typedef LPPOLYTEXTA LPPOLYTEXT;
-#endif
-
-  typedef struct _FIXED {
-    WORD fract;
-    short value;
-  } FIXED;
-
-  typedef struct _MAT2 {
-    FIXED eM11;
-    FIXED eM12;
-    FIXED eM21;
-    FIXED eM22;
-  } MAT2,*LPMAT2;
-
-  typedef struct _GLYPHMETRICS {
-    UINT gmBlackBoxX;
-    UINT gmBlackBoxY;
-    POINT gmptGlyphOrigin;
-    short gmCellIncX;
-    short gmCellIncY;
-  } GLYPHMETRICS,*LPGLYPHMETRICS;
-
-#define GGO_METRICS 0
-#define GGO_BITMAP 1
-#define GGO_NATIVE 2
-#define GGO_BEZIER 3
-
-#define GGO_GRAY2_BITMAP 4
-#define GGO_GRAY4_BITMAP 5
-#define GGO_GRAY8_BITMAP 6
-#define GGO_GLYPH_INDEX 0x0080
-#define GGO_UNHINTED 0x0100
-
-#define TT_POLYGON_TYPE 24
-
-#define TT_PRIM_LINE 1
-#define TT_PRIM_QSPLINE 2
-#define TT_PRIM_CSPLINE 3
-
-  typedef struct tagPOINTFX {
-    FIXED x;
-    FIXED y;
-  } POINTFX,*LPPOINTFX;
-
-  typedef struct tagTTPOLYCURVE {
-    WORD wType;
-    WORD cpfx;
-    POINTFX apfx[1];
-  } TTPOLYCURVE,*LPTTPOLYCURVE;
-
-  typedef struct tagTTPOLYGONHEADER {
-    DWORD cb;
-    DWORD dwType;
-    POINTFX pfxStart;
-  } TTPOLYGONHEADER,*LPTTPOLYGONHEADER;
-
-#define GCP_DBCS 0x0001
-#define GCP_REORDER 0x0002
-#define GCP_USEKERNING 0x0008
-#define GCP_GLYPHSHAPE 0x0010
-#define GCP_LIGATE 0x0020
-
-#define GCP_DIACRITIC 0x0100
-#define GCP_KASHIDA 0x0400
-#define GCP_ERROR 0x8000
-#define FLI_MASK 0x103B
-
-#define GCP_JUSTIFY 0x00010000L
-
-#define FLI_GLYPHS 0x00040000L
-#define GCP_CLASSIN 0x00080000L
-#define GCP_MAXEXTENT 0x00100000L
-#define GCP_JUSTIFYIN 0x00200000L
-#define GCP_DISPLAYZWG 0x00400000L
-#define GCP_SYMSWAPOFF 0x00800000L
-#define GCP_NUMERICOVERRIDE 0x01000000L
-#define GCP_NEUTRALOVERRIDE 0x02000000L
-#define GCP_NUMERICSLATIN 0x04000000L
-#define GCP_NUMERICSLOCAL 0x08000000L
-
-#define GCPCLASS_LATIN 1
-#define GCPCLASS_HEBREW 2
-#define GCPCLASS_ARABIC 2
-#define GCPCLASS_NEUTRAL 3
-#define GCPCLASS_LOCALNUMBER 4
-#define GCPCLASS_LATINNUMBER 5
-#define GCPCLASS_LATINNUMERICTERMINATOR 6
-#define GCPCLASS_LATINNUMERICSEPARATOR 7
-#define GCPCLASS_NUMERICSEPARATOR 8
-#define GCPCLASS_PREBOUNDLTR 0x80
-#define GCPCLASS_PREBOUNDRTL 0x40
-#define GCPCLASS_POSTBOUNDLTR 0x20
-#define GCPCLASS_POSTBOUNDRTL 0x10
-
-#define GCPGLYPH_LINKBEFORE 0x8000
-#define GCPGLYPH_LINKAFTER 0x4000
-
-  typedef struct tagGCP_RESULTSA {
-    DWORD lStructSize;
-    LPSTR lpOutString;
-    UINT *lpOrder;
-    int *lpDx;
-    int *lpCaretPos;
-    LPSTR lpClass;
-    LPWSTR lpGlyphs;
-    UINT nGlyphs;
-    int nMaxFit;
-  } GCP_RESULTSA,*LPGCP_RESULTSA;
-  typedef struct tagGCP_RESULTSW {
-    DWORD lStructSize;
-    LPWSTR lpOutString;
-    UINT *lpOrder;
-    int *lpDx;
-    int *lpCaretPos;
-    LPSTR lpClass;
-    LPWSTR lpGlyphs;
-    UINT nGlyphs;
-    int nMaxFit;
-  } GCP_RESULTSW,*LPGCP_RESULTSW;
-#ifdef UNICODE
-  typedef GCP_RESULTSW GCP_RESULTS;
-  typedef LPGCP_RESULTSW LPGCP_RESULTS;
-#else
-  typedef GCP_RESULTSA GCP_RESULTS;
-  typedef LPGCP_RESULTSA LPGCP_RESULTS;
-#endif
-
-  typedef struct _RASTERIZER_STATUS {
-    short nSize;
-    short wFlags;
-    short nLanguageID;
-  } RASTERIZER_STATUS,*LPRASTERIZER_STATUS;
-
-#define TT_AVAILABLE 0x0001
-#define TT_ENABLED 0x0002
-
-  typedef struct tagPIXELFORMATDESCRIPTOR {
-    WORD nSize;
-    WORD nVersion;
-    DWORD dwFlags;
-    BYTE iPixelType;
-    BYTE cColorBits;
-    BYTE cRedBits;
-    BYTE cRedShift;
-    BYTE cGreenBits;
-    BYTE cGreenShift;
-    BYTE cBlueBits;
-    BYTE cBlueShift;
-    BYTE cAlphaBits;
-    BYTE cAlphaShift;
-    BYTE cAccumBits;
-    BYTE cAccumRedBits;
-    BYTE cAccumGreenBits;
-    BYTE cAccumBlueBits;
-    BYTE cAccumAlphaBits;
-    BYTE cDepthBits;
-    BYTE cStencilBits;
-    BYTE cAuxBuffers;
-    BYTE iLayerType;
-    BYTE bReserved;
-    DWORD dwLayerMask;
-    DWORD dwVisibleMask;
-    DWORD dwDamageMask;
-  } PIXELFORMATDESCRIPTOR,*PPIXELFORMATDESCRIPTOR,*LPPIXELFORMATDESCRIPTOR;
-
-#define PFD_TYPE_RGBA 0
-#define PFD_TYPE_COLORINDEX 1
-
-#define PFD_MAIN_PLANE 0
-#define PFD_OVERLAY_PLANE 1
-#define PFD_UNDERLAY_PLANE (-1)
-
-#define PFD_DOUBLEBUFFER 0x00000001
-#define PFD_STEREO 0x00000002
-#define PFD_DRAW_TO_WINDOW 0x00000004
-#define PFD_DRAW_TO_BITMAP 0x00000008
-#define PFD_SUPPORT_GDI 0x00000010
-#define PFD_SUPPORT_OPENGL 0x00000020
-#define PFD_GENERIC_FORMAT 0x00000040
-#define PFD_NEED_PALETTE 0x00000080
-#define PFD_NEED_SYSTEM_PALETTE 0x00000100
-#define PFD_SWAP_EXCHANGE 0x00000200
-#define PFD_SWAP_COPY 0x00000400
-#define PFD_SWAP_LAYER_BUFFERS 0x00000800
-#define PFD_GENERIC_ACCELERATED 0x00001000
-#define PFD_SUPPORT_DIRECTDRAW 0x00002000
-
-#define PFD_DEPTH_DONTCARE 0x20000000
-#define PFD_DOUBLEBUFFER_DONTCARE 0x40000000
-#define PFD_STEREO_DONTCARE 0x80000000
-
-#ifndef NOTEXTMETRIC
-  typedef int (CALLBACK *OLDFONTENUMPROCA)(CONST LOGFONTA *,CONST TEXTMETRICA *,DWORD,LPARAM);
-  typedef int (CALLBACK *OLDFONTENUMPROCW)(CONST LOGFONTW *,CONST TEXTMETRICW *,DWORD,LPARAM);
-#ifdef UNICODE
-#define OLDFONTENUMPROC OLDFONTENUMPROCW
-#else
-#define OLDFONTENUMPROC OLDFONTENUMPROCA
-#endif
-#else
-  typedef int (CALLBACK *OLDFONTENUMPROCA)(CONST LOGFONTA *,CONST VOID *,DWORD,LPARAM);
-  typedef int (CALLBACK *OLDFONTENUMPROCW)(CONST LOGFONTW *,CONST VOID *,DWORD,LPARAM);
-#ifdef UNICODE
-#define OLDFONTENUMPROC OLDFONTENUMPROCW
-#else
-#define OLDFONTENUMPROC OLDFONTENUMPROCA
-#endif
-#endif
-
-  typedef OLDFONTENUMPROCA FONTENUMPROCA;
-  typedef OLDFONTENUMPROCW FONTENUMPROCW;
-#ifdef UNICODE
-  typedef FONTENUMPROCW FONTENUMPROC;
-#else
-  typedef FONTENUMPROCA FONTENUMPROC;
-#endif
-
-  typedef int (CALLBACK *GOBJENUMPROC)(LPVOID,LPARAM);
-  typedef VOID (CALLBACK *LINEDDAPROC)(int,int,LPARAM);
-
-#ifdef UNICODE
-#define AddFontResource AddFontResourceW
-#define CopyMetaFile CopyMetaFileW
-#define CreateDC CreateDCW
-#define CreateFontIndirect CreateFontIndirectW
-#define CreateFont CreateFontW
-#define CreateIC CreateICW
-#define CreateMetaFile CreateMetaFileW
-#define CreateScalableFontResource CreateScalableFontResourceW
-#else
-#define AddFontResource AddFontResourceA
-#define CopyMetaFile CopyMetaFileA
-#define CreateDC CreateDCA
-#define CreateFontIndirect CreateFontIndirectA
-#define CreateFont CreateFontA
-#define CreateIC CreateICA
-#define CreateMetaFile CreateMetaFileA
-#define CreateScalableFontResource CreateScalableFontResourceA
-#endif
-
-  WINGDIAPI int WINAPI AddFontResourceA(LPCSTR);
-  WINGDIAPI int WINAPI AddFontResourceW(LPCWSTR);
-  WINGDIAPI WINBOOL WINAPI AnimatePalette(HPALETTE hPal,UINT iStartIndex,UINT cEntries,CONST PALETTEENTRY *ppe);
-  WINGDIAPI WINBOOL WINAPI Arc(HDC hdc,int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4);
-  WINGDIAPI WINBOOL WINAPI BitBlt(HDC hdc,int x,int y,int cx,int cy,HDC hdcSrc,int x1,int y1,DWORD rop);
-  WINGDIAPI WINBOOL WINAPI CancelDC(HDC hdc);
-  WINGDIAPI WINBOOL WINAPI Chord(HDC hdc,int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4);
-  WINGDIAPI int WINAPI ChoosePixelFormat(HDC hdc,CONST PIXELFORMATDESCRIPTOR *ppfd);
-  WINGDIAPI HMETAFILE WINAPI CloseMetaFile(HDC hdc);
-  WINGDIAPI int WINAPI CombineRgn(HRGN hrgnDst,HRGN hrgnSrc1,HRGN hrgnSrc2,int iMode);
-  WINGDIAPI HMETAFILE WINAPI CopyMetaFileA(HMETAFILE,LPCSTR);
-  WINGDIAPI HMETAFILE WINAPI CopyMetaFileW(HMETAFILE,LPCWSTR);
-  WINGDIAPI HBITMAP WINAPI CreateBitmap(int nWidth,int nHeight,UINT nPlanes,UINT nBitCount,CONST VOID *lpBits);
-  WINGDIAPI HBITMAP WINAPI CreateBitmapIndirect(CONST BITMAP *pbm);
-  WINGDIAPI HBRUSH WINAPI CreateBrushIndirect(CONST LOGBRUSH *plbrush);
-  WINGDIAPI HBITMAP WINAPI CreateCompatibleBitmap(HDC hdc,int cx,int cy);
-  WINGDIAPI HBITMAP WINAPI CreateDiscardableBitmap(HDC hdc,int cx,int cy);
-  WINGDIAPI HDC WINAPI CreateCompatibleDC(HDC hdc);
-  WINGDIAPI HDC WINAPI CreateDCA(LPCSTR pwszDriver,LPCSTR pwszDevice,LPCSTR pszPort,CONST DEVMODEA *pdm);
-  WINGDIAPI HDC WINAPI CreateDCW(LPCWSTR pwszDriver,LPCWSTR pwszDevice,LPCWSTR pszPort,CONST DEVMODEW *pdm);
-  WINGDIAPI HBITMAP WINAPI CreateDIBitmap(HDC hdc,CONST BITMAPINFOHEADER *pbmih,DWORD flInit,CONST VOID *pjBits,CONST BITMAPINFO *pbmi,UINT iUsage);
-  WINGDIAPI HBRUSH WINAPI CreateDIBPatternBrush(HGLOBAL h,UINT iUsage);
-  WINGDIAPI HBRUSH WINAPI CreateDIBPatternBrushPt(CONST VOID *lpPackedDIB,UINT iUsage);
-  WINGDIAPI HRGN WINAPI CreateEllipticRgn(int x1,int y1,int x2,int y2);
-  WINGDIAPI HRGN WINAPI CreateEllipticRgnIndirect(CONST RECT *lprect);
-  WINGDIAPI HFONT WINAPI CreateFontIndirectA(CONST LOGFONTA *lplf);
-  WINGDIAPI HFONT WINAPI CreateFontIndirectW(CONST LOGFONTW *lplf);
-  WINGDIAPI HFONT WINAPI CreateFontA(int cHeight,int cWidth,int cEscapement,int cOrientation,int cWeight,DWORD bItalic,DWORD bUnderline,DWORD bStrikeOut,DWORD iCharSet,DWORD iOutPrecision,DWORD iClipPrecision,DWORD iQuality,DWORD iPitchAndFamily,LPCSTR pszFaceName);
-  WINGDIAPI HFONT WINAPI CreateFontW(int cHeight,int cWidth,int cEscapement,int cOrientation,int cWeight,DWORD bItalic,DWORD bUnderline,DWORD bStrikeOut,DWORD iCharSet,DWORD iOutPrecision,DWORD iClipPrecision,DWORD iQuality,DWORD iPitchAndFamily,LPCWSTR pszFaceName);
-  WINGDIAPI HBRUSH WINAPI CreateHatchBrush(int iHatch,COLORREF color);
-  WINGDIAPI HDC WINAPI CreateICA(LPCSTR pszDriver,LPCSTR pszDevice,LPCSTR pszPort,CONST DEVMODEA *pdm);
-  WINGDIAPI HDC WINAPI CreateICW(LPCWSTR pszDriver,LPCWSTR pszDevice,LPCWSTR pszPort,CONST DEVMODEW *pdm);
-  WINGDIAPI HDC WINAPI CreateMetaFileA(LPCSTR pszFile);
-  WINGDIAPI HDC WINAPI CreateMetaFileW(LPCWSTR pszFile);
-  WINGDIAPI HPALETTE WINAPI CreatePalette(CONST LOGPALETTE *plpal);
-  WINGDIAPI HPEN WINAPI CreatePen(int iStyle,int cWidth,COLORREF color);
-  WINGDIAPI HPEN WINAPI CreatePenIndirect(CONST LOGPEN *plpen);
-  WINGDIAPI HRGN WINAPI CreatePolyPolygonRgn(CONST POINT *pptl,CONST INT *pc,int cPoly,int iMode);
-  WINGDIAPI HBRUSH WINAPI CreatePatternBrush(HBITMAP hbm);
-  WINGDIAPI HRGN WINAPI CreateRectRgn(int x1,int y1,int x2,int y2);
-  WINGDIAPI HRGN WINAPI CreateRectRgnIndirect(CONST RECT *lprect);
-  WINGDIAPI HRGN WINAPI CreateRoundRectRgn(int x1,int y1,int x2,int y2,int w,int h);
-  WINGDIAPI WINBOOL WINAPI CreateScalableFontResourceA(DWORD fdwHidden,LPCSTR lpszFont,LPCSTR lpszFile,LPCSTR lpszPath);
-  WINGDIAPI WINBOOL WINAPI CreateScalableFontResourceW(DWORD fdwHidden,LPCWSTR lpszFont,LPCWSTR lpszFile,LPCWSTR lpszPath);
-  WINGDIAPI HBRUSH WINAPI CreateSolidBrush(COLORREF color);
-  WINGDIAPI WINBOOL WINAPI DeleteDC(HDC hdc);
-  WINGDIAPI WINBOOL WINAPI DeleteMetaFile(HMETAFILE hmf);
-  WINGDIAPI WINBOOL WINAPI DeleteObject(HGDIOBJ ho);
-  WINGDIAPI int WINAPI DescribePixelFormat(HDC hdc,int iPixelFormat,UINT nBytes,LPPIXELFORMATDESCRIPTOR ppfd);
-
-  typedef UINT (CALLBACK *LPFNDEVMODE)(HWND,HMODULE,LPDEVMODE,LPSTR,LPSTR,LPDEVMODE,LPSTR,UINT);
-  typedef DWORD (CALLBACK *LPFNDEVCAPS)(LPSTR,LPSTR,UINT,LPSTR,LPDEVMODE);
-
-#define DM_UPDATE 1
-#define DM_COPY 2
-#define DM_PROMPT 4
-#define DM_MODIFY 8
-
-#define DM_IN_BUFFER DM_MODIFY
-#define DM_IN_PROMPT DM_PROMPT
-#define DM_OUT_BUFFER DM_COPY
-#define DM_OUT_DEFAULT DM_UPDATE
-
-#define DC_FIELDS 1
-#define DC_PAPERS 2
-#define DC_PAPERSIZE 3
-#define DC_MINEXTENT 4
-#define DC_MAXEXTENT 5
-#define DC_BINS 6
-#define DC_DUPLEX 7
-#define DC_SIZE 8
-#define DC_EXTRA 9
-#define DC_VERSION 10
-#define DC_DRIVER 11
-#define DC_BINNAMES 12
-#define DC_ENUMRESOLUTIONS 13
-#define DC_FILEDEPENDENCIES 14
-#define DC_TRUETYPE 15
-#define DC_PAPERNAMES 16
-#define DC_ORIENTATION 17
-#define DC_COPIES 18
-#define DC_BINADJUST 19
-#define DC_EMF_COMPLIANT 20
-#define DC_DATATYPE_PRODUCED 21
-#define DC_COLLATE 22
-#define DC_MANUFACTURER 23
-#define DC_MODEL 24
-#define DC_PERSONALITY 25
-#define DC_PRINTRATE 26
-#define DC_PRINTRATEUNIT 27
-#define PRINTRATEUNIT_PPM 1
-#define PRINTRATEUNIT_CPS 2
-#define PRINTRATEUNIT_LPM 3
-#define PRINTRATEUNIT_IPM 4
-#define DC_PRINTERMEM 28
-#define DC_MEDIAREADY 29
-#define DC_STAPLE 30
-#define DC_PRINTRATEPPM 31
-#define DC_COLORDEVICE 32
-#define DC_NUP 33
-#define DC_MEDIATYPENAMES 34
-#define DC_MEDIATYPES 35
-
-#define DCTT_BITMAP 0x0000001L
-#define DCTT_DOWNLOAD 0x0000002L
-#define DCTT_SUBDEV 0x0000004L
-#define DCTT_DOWNLOAD_OUTLINE 0x0000008L
-
-#define DCBA_FACEUPNONE 0x0000
-#define DCBA_FACEUPCENTER 0x0001
-#define DCBA_FACEUPLEFT 0x0002
-#define DCBA_FACEUPRIGHT 0x0003
-#define DCBA_FACEDOWNNONE 0x0100
-#define DCBA_FACEDOWNCENTER 0x0101
-#define DCBA_FACEDOWNLEFT 0x0102
-#define DCBA_FACEDOWNRIGHT 0x0103
-
-#ifdef UNICODE
-#define DeviceCapabilities DeviceCapabilitiesW
-#define EnumFontFamiliesEx EnumFontFamiliesExW
-#define EnumFontFamilies EnumFontFamiliesW
-#define EnumFonts EnumFontsW
-#define GetCharWidth GetCharWidthW
-#define GetCharWidth32 GetCharWidth32W
-#define GetCharWidthFloat GetCharWidthFloatW
-#define GetCharABCWidths GetCharABCWidthsW
-#define GetCharABCWidthsFloat GetCharABCWidthsFloatW
-#define GetGlyphOutline GetGlyphOutlineW
-#define GetMetaFile GetMetaFileW
-#else
-#define DeviceCapabilities DeviceCapabilitiesA
-#define EnumFontFamiliesEx EnumFontFamiliesExA
-#define EnumFontFamilies EnumFontFamiliesA
-#define EnumFonts EnumFontsA
-#define GetCharWidth GetCharWidthA
-#define GetCharWidth32 GetCharWidth32A
-#define GetCharWidthFloat GetCharWidthFloatA
-#define GetCharABCWidths GetCharABCWidthsA
-#define GetCharABCWidthsFloat GetCharABCWidthsFloatA
-#define GetGlyphOutline GetGlyphOutlineA
-#define GetMetaFile GetMetaFileA
-#endif
-
-  WINSPOOLAPI int WINAPI DeviceCapabilitiesA(LPCSTR pDevice,LPCSTR pPort,WORD fwCapability,LPSTR pOutput,CONST DEVMODEA *pDevMode);
-  WINSPOOLAPI int WINAPI DeviceCapabilitiesW(LPCWSTR pDevice,LPCWSTR pPort,WORD fwCapability,LPWSTR pOutput,CONST DEVMODEW *pDevMode);
-  WINGDIAPI int WINAPI DrawEscape(HDC hdc,int iEscape,int cjIn,LPCSTR lpIn);
-  WINGDIAPI WINBOOL WINAPI Ellipse(HDC hdc,int left,int top,int right,int bottom);
-  WINGDIAPI int WINAPI EnumFontFamiliesExA(HDC hdc,LPLOGFONTA lpLogfont,FONTENUMPROCA lpProc,LPARAM lParam,DWORD dwFlags);
-  WINGDIAPI int WINAPI EnumFontFamiliesExW(HDC hdc,LPLOGFONTW lpLogfont,FONTENUMPROCW lpProc,LPARAM lParam,DWORD dwFlags);
-  WINGDIAPI int WINAPI EnumFontFamiliesA(HDC hdc,LPCSTR lpLogfont,FONTENUMPROCA lpProc,LPARAM lParam);
-  WINGDIAPI int WINAPI EnumFontFamiliesW(HDC hdc,LPCWSTR lpLogfont,FONTENUMPROCW lpProc,LPARAM lParam);
-  WINGDIAPI int WINAPI EnumFontsA(HDC hdc,LPCSTR lpLogfont,FONTENUMPROCA lpProc,LPARAM lParam);
-  WINGDIAPI int WINAPI EnumFontsW(HDC hdc,LPCWSTR lpLogfont,FONTENUMPROCW lpProc,LPARAM lParam);
-  WINGDIAPI int WINAPI EnumObjects(HDC hdc,int nType,GOBJENUMPROC lpFunc,LPARAM lParam);
-  WINGDIAPI WINBOOL WINAPI EqualRgn(HRGN hrgn1,HRGN hrgn2);
-  WINGDIAPI int WINAPI Escape(HDC hdc,int iEscape,int cjIn,LPCSTR pvIn,LPVOID pvOut);
-  WINGDIAPI int WINAPI ExtEscape(HDC hdc,int iEscape,int cjInput,LPCSTR lpInData,int cjOutput,LPSTR lpOutData);
-  WINGDIAPI int WINAPI ExcludeClipRect(HDC hdc,int left,int top,int right,int bottom);
-  WINGDIAPI HRGN WINAPI ExtCreateRegion(CONST XFORM *lpx,DWORD nCount,CONST RGNDATA *lpData);
-  WINGDIAPI WINBOOL WINAPI ExtFloodFill(HDC hdc,int x,int y,COLORREF color,UINT type);
-  WINGDIAPI WINBOOL WINAPI FillRgn(HDC hdc,HRGN hrgn,HBRUSH hbr);
-  WINGDIAPI WINBOOL WINAPI FloodFill(HDC hdc,int x,int y,COLORREF color);
-  WINGDIAPI WINBOOL WINAPI FrameRgn(HDC hdc,HRGN hrgn,HBRUSH hbr,int w,int h);
-  WINGDIAPI int WINAPI GetROP2(HDC hdc);
-  WINGDIAPI WINBOOL WINAPI GetAspectRatioFilterEx(HDC hdc,LPSIZE lpsize);
-  WINGDIAPI COLORREF WINAPI GetBkColor(HDC hdc);
-  WINGDIAPI COLORREF WINAPI GetDCBrushColor(HDC hdc);
-  WINGDIAPI COLORREF WINAPI GetDCPenColor(HDC hdc);
-  WINGDIAPI int WINAPI GetBkMode(HDC hdc);
-  WINGDIAPI LONG WINAPI GetBitmapBits(HBITMAP hbit,LONG cb,LPVOID lpvBits);
-  WINGDIAPI WINBOOL WINAPI GetBitmapDimensionEx(HBITMAP hbit,LPSIZE lpsize);
-  WINGDIAPI UINT WINAPI GetBoundsRect(HDC hdc,LPRECT lprect,UINT flags);
-  WINGDIAPI WINBOOL WINAPI GetBrushOrgEx(HDC hdc,LPPOINT lppt);
-  WINGDIAPI WINBOOL WINAPI GetCharWidthA(HDC hdc,UINT iFirst,UINT iLast,LPINT lpBuffer);
-  WINGDIAPI WINBOOL WINAPI GetCharWidthW(HDC hdc,UINT iFirst,UINT iLast,LPINT lpBuffer);
-  WINGDIAPI WINBOOL WINAPI GetCharWidth32A(HDC hdc,UINT iFirst,UINT iLast,LPINT lpBuffer);
-  WINGDIAPI WINBOOL WINAPI GetCharWidth32W(HDC hdc,UINT iFirst,UINT iLast,LPINT lpBuffer);
-  WINGDIAPI WINBOOL WINAPI GetCharWidthFloatA(HDC hdc,UINT iFirst,UINT iLast,PFLOAT lpBuffer);
-  WINGDIAPI WINBOOL WINAPI GetCharWidthFloatW(HDC hdc,UINT iFirst,UINT iLast,PFLOAT lpBuffer);
-  WINGDIAPI WINBOOL WINAPI GetCharABCWidthsA(HDC hdc,UINT wFirst,UINT wLast,LPABC lpABC);
-  WINGDIAPI WINBOOL WINAPI GetCharABCWidthsW(HDC hdc,UINT wFirst,UINT wLast,LPABC lpABC);
-  WINGDIAPI WINBOOL WINAPI GetCharABCWidthsFloatA(HDC hdc,UINT iFirst,UINT iLast,LPABCFLOAT lpABC);
-  WINGDIAPI WINBOOL WINAPI GetCharABCWidthsFloatW(HDC hdc,UINT iFirst,UINT iLast,LPABCFLOAT lpABC);
-  WINGDIAPI int WINAPI GetClipBox(HDC hdc,LPRECT lprect);
-  WINGDIAPI int WINAPI GetClipRgn(HDC hdc,HRGN hrgn);
-  WINGDIAPI int WINAPI GetMetaRgn(HDC hdc,HRGN hrgn);
-  WINGDIAPI HGDIOBJ WINAPI GetCurrentObject(HDC hdc,UINT type);
-  WINGDIAPI WINBOOL WINAPI GetCurrentPositionEx(HDC hdc,LPPOINT lppt);
-  WINGDIAPI int WINAPI GetDeviceCaps(HDC hdc,int index);
-  WINGDIAPI int WINAPI GetDIBits(HDC hdc,HBITMAP hbm,UINT start,UINT cLines,LPVOID lpvBits,LPBITMAPINFO lpbmi,UINT usage);
-  WINGDIAPI DWORD WINAPI GetFontData (HDC hdc,DWORD dwTable,DWORD dwOffset,PVOID pvBuffer,DWORD cjBuffer);
-  WINGDIAPI DWORD WINAPI GetGlyphOutlineA(HDC hdc,UINT uChar,UINT fuFormat,LPGLYPHMETRICS lpgm,DWORD cjBuffer,LPVOID pvBuffer,CONST MAT2 *lpmat2);
-  WINGDIAPI DWORD WINAPI GetGlyphOutlineW(HDC hdc,UINT uChar,UINT fuFormat,LPGLYPHMETRICS lpgm,DWORD cjBuffer,LPVOID pvBuffer,CONST MAT2 *lpmat2);
-  WINGDIAPI int WINAPI GetGraphicsMode(HDC hdc);
-  WINGDIAPI int WINAPI GetMapMode(HDC hdc);
-  WINGDIAPI UINT WINAPI GetMetaFileBitsEx(HMETAFILE hMF,UINT cbBuffer,LPVOID lpData);
-  WINGDIAPI HMETAFILE WINAPI GetMetaFileA(LPCSTR lpName);
-  WINGDIAPI HMETAFILE WINAPI GetMetaFileW(LPCWSTR lpName);
-  WINGDIAPI COLORREF WINAPI GetNearestColor(HDC hdc,COLORREF color);
-  WINGDIAPI UINT WINAPI GetNearestPaletteIndex(HPALETTE h,COLORREF color);
-  WINGDIAPI DWORD WINAPI GetObjectType(HGDIOBJ h);
-
-#ifndef NOTEXTMETRIC
-#ifdef UNICODE
-#define GetOutlineTextMetrics GetOutlineTextMetricsW
-#else
-#define GetOutlineTextMetrics GetOutlineTextMetricsA
-#endif
-
-  WINGDIAPI UINT WINAPI GetOutlineTextMetricsA(HDC hdc,UINT cjCopy,LPOUTLINETEXTMETRICA potm);
-  WINGDIAPI UINT WINAPI GetOutlineTextMetricsW(HDC hdc,UINT cjCopy,LPOUTLINETEXTMETRICW potm);
-#endif
-
-#ifdef UNICODE
-#define GetTextExtentPoint GetTextExtentPointW
-#define GetTextExtentPoint32 GetTextExtentPoint32W
-#define GetTextExtentExPoint GetTextExtentExPointW
-#define GetCharacterPlacement GetCharacterPlacementW
-#else
-#define GetTextExtentPoint GetTextExtentPointA
-#define GetTextExtentPoint32 GetTextExtentPoint32A
-#define GetTextExtentExPoint GetTextExtentExPointA
-#define GetCharacterPlacement GetCharacterPlacementA
-#endif
-
-  WINGDIAPI UINT WINAPI GetPaletteEntries(HPALETTE hpal,UINT iStart,UINT cEntries,LPPALETTEENTRY pPalEntries);
-  WINGDIAPI COLORREF WINAPI GetPixel(HDC hdc,int x,int y);
-  WINGDIAPI int WINAPI GetPixelFormat(HDC hdc);
-  WINGDIAPI int WINAPI GetPolyFillMode(HDC hdc);
-  WINGDIAPI WINBOOL WINAPI GetRasterizerCaps(LPRASTERIZER_STATUS lpraststat,UINT cjBytes);
-  WINGDIAPI int WINAPI GetRandomRgn (HDC hdc,HRGN hrgn,INT i);
-  WINGDIAPI DWORD WINAPI GetRegionData(HRGN hrgn,DWORD nCount,LPRGNDATA lpRgnData);
-  WINGDIAPI int WINAPI GetRgnBox(HRGN hrgn,LPRECT lprc);
-  WINGDIAPI HGDIOBJ WINAPI GetStockObject(int i);
-  WINGDIAPI int WINAPI GetStretchBltMode(HDC hdc);
-  WINGDIAPI UINT WINAPI GetSystemPaletteEntries(HDC hdc,UINT iStart,UINT cEntries,LPPALETTEENTRY pPalEntries);
-  WINGDIAPI UINT WINAPI GetSystemPaletteUse(HDC hdc);
-  WINGDIAPI int WINAPI GetTextCharacterExtra(HDC hdc);
-  WINGDIAPI UINT WINAPI GetTextAlign(HDC hdc);
-  WINGDIAPI COLORREF WINAPI GetTextColor(HDC hdc);
-  WINGDIAPI WINBOOL WINAPI GetTextExtentPointA(HDC hdc,LPCSTR lpString,int c,LPSIZE lpsz);
-  WINGDIAPI WINBOOL WINAPI GetTextExtentPointW(HDC hdc,LPCWSTR lpString,int c,LPSIZE lpsz);
-  WINGDIAPI WINBOOL WINAPI GetTextExtentPoint32A(HDC hdc,LPCSTR lpString,int c,LPSIZE psizl);
-  WINGDIAPI WINBOOL WINAPI GetTextExtentPoint32W(HDC hdc,LPCWSTR lpString,int c,LPSIZE psizl);
-  WINGDIAPI WINBOOL WINAPI GetTextExtentExPointA(HDC hdc,LPCSTR lpszString,int cchString,int nMaxExtent,LPINT lpnFit,LPINT lpnDx,LPSIZE lpSize);
-  WINGDIAPI WINBOOL WINAPI GetTextExtentExPointW(HDC hdc,LPCWSTR lpszString,int cchString,int nMaxExtent,LPINT lpnFit,LPINT lpnDx,LPSIZE lpSize);
-  WINGDIAPI int WINAPI GetTextCharset(HDC hdc);
-  WINGDIAPI int WINAPI GetTextCharsetInfo(HDC hdc,LPFONTSIGNATURE lpSig,DWORD dwFlags);
-  WINGDIAPI WINBOOL WINAPI TranslateCharsetInfo(DWORD *lpSrc,LPCHARSETINFO lpCs,DWORD dwFlags);
-  WINGDIAPI DWORD WINAPI GetFontLanguageInfo(HDC hdc);
-  WINGDIAPI DWORD WINAPI GetCharacterPlacementA(HDC hdc,LPCSTR lpString,int nCount,int nMexExtent,LPGCP_RESULTSA lpResults,DWORD dwFlags);
-  WINGDIAPI DWORD WINAPI GetCharacterPlacementW(HDC hdc,LPCWSTR lpString,int nCount,int nMexExtent,LPGCP_RESULTSW lpResults,DWORD dwFlags);
-
-  typedef struct tagWCRANGE {
-    WCHAR wcLow;
-    USHORT cGlyphs;
-  } WCRANGE,*PWCRANGE,*LPWCRANGE;
-
-  typedef struct tagGLYPHSET {
-    DWORD cbThis;
-    DWORD flAccel;
-    DWORD cGlyphsSupported;
-    DWORD cRanges;
-    WCRANGE ranges[1];
-  } GLYPHSET,*PGLYPHSET,*LPGLYPHSET;
-
-#define GS_8BIT_INDICES 0x00000001
-
-#define GGI_MARK_NONEXISTING_GLYPHS 0X0001
-
-#ifdef UNICODE
-#define GetGlyphIndices GetGlyphIndicesW
-#else
-#define GetGlyphIndices GetGlyphIndicesA
-#endif
-
-  WINGDIAPI DWORD WINAPI GetFontUnicodeRanges(HDC hdc,LPGLYPHSET lpgs);
-  WINGDIAPI DWORD WINAPI GetGlyphIndicesA(HDC hdc,LPCSTR lpstr,int c,LPWORD pgi,DWORD fl);
-  WINGDIAPI DWORD WINAPI GetGlyphIndicesW(HDC hdc,LPCWSTR lpstr,int c,LPWORD pgi,DWORD fl);
-  WINGDIAPI WINBOOL WINAPI GetTextExtentPointI(HDC hdc,LPWORD pgiIn,int cgi,LPSIZE psize);
-  WINGDIAPI WINBOOL WINAPI GetTextExtentExPointI (HDC hdc,LPWORD lpwszString,int cwchString,int nMaxExtent,LPINT lpnFit,LPINT lpnDx,LPSIZE lpSize);
-  WINGDIAPI WINBOOL WINAPI GetCharWidthI(HDC hdc,UINT giFirst,UINT cgi,LPWORD pgi,LPINT piWidths);
-  WINGDIAPI WINBOOL WINAPI GetCharABCWidthsI(HDC hdc,UINT giFirst,UINT cgi,LPWORD pgi,LPABC pabc);
-
-#define STAMP_DESIGNVECTOR (0x8000000 + 'd' + ('v' << 8))
-#define STAMP_AXESLIST (0x8000000 + 'a' + ('l' << 8))
-#define MM_MAX_NUMAXES 16
-
-  typedef struct tagDESIGNVECTOR {
-    DWORD dvReserved;
-    DWORD dvNumAxes;
-    LONG dvValues[MM_MAX_NUMAXES];
-  } DESIGNVECTOR,*PDESIGNVECTOR,*LPDESIGNVECTOR;
-
-#ifdef UNICODE
-#define AddFontResourceEx AddFontResourceExW
-#define RemoveFontResourceEx RemoveFontResourceExW
-#else
-#define AddFontResourceEx AddFontResourceExA
-#define RemoveFontResourceEx RemoveFontResourceExA
-#endif
-
-  WINGDIAPI int WINAPI AddFontResourceExA(LPCSTR name,DWORD fl,PVOID res);
-  WINGDIAPI int WINAPI AddFontResourceExW(LPCWSTR name,DWORD fl,PVOID res);
-  WINGDIAPI WINBOOL WINAPI RemoveFontResourceExA(LPCSTR name,DWORD fl,PVOID pdv);
-  WINGDIAPI WINBOOL WINAPI RemoveFontResourceExW(LPCWSTR name,DWORD fl,PVOID pdv);
-  WINGDIAPI HANDLE WINAPI AddFontMemResourceEx(PVOID pFileView,DWORD cjSize,PVOID pvResrved,DWORD *pNumFonts);
-  WINGDIAPI WINBOOL WINAPI RemoveFontMemResourceEx(HANDLE h);
-
-#define FR_PRIVATE 0x10
-#define FR_NOT_ENUM 0x20
-
-#define MM_MAX_AXES_NAMELEN 16
-
-  typedef struct tagAXISINFOA {
-    LONG axMinValue;
-    LONG axMaxValue;
-    BYTE axAxisName[MM_MAX_AXES_NAMELEN];
-  } AXISINFOA,*PAXISINFOA,*LPAXISINFOA;
-
-  typedef struct tagAXISINFOW {
-    LONG axMinValue;
-    LONG axMaxValue;
-    WCHAR axAxisName[MM_MAX_AXES_NAMELEN];
-  } AXISINFOW,*PAXISINFOW,*LPAXISINFOW;
-#ifdef UNICODE
-  typedef AXISINFOW AXISINFO;
-  typedef PAXISINFOW PAXISINFO;
-  typedef LPAXISINFOW LPAXISINFO;
-#else
-  typedef AXISINFOA AXISINFO;
-  typedef PAXISINFOA PAXISINFO;
-  typedef LPAXISINFOA LPAXISINFO;
-#endif
-
-  typedef struct tagAXESLISTA {
-    DWORD axlReserved;
-    DWORD axlNumAxes;
-    AXISINFOA axlAxisInfo[MM_MAX_NUMAXES];
-  } AXESLISTA,*PAXESLISTA,*LPAXESLISTA;
-
-  typedef struct tagAXESLISTW {
-    DWORD axlReserved;
-    DWORD axlNumAxes;
-    AXISINFOW axlAxisInfo[MM_MAX_NUMAXES];
-  } AXESLISTW,*PAXESLISTW,*LPAXESLISTW;
-#ifdef UNICODE
-  typedef AXESLISTW AXESLIST;
-  typedef PAXESLISTW PAXESLIST;
-  typedef LPAXESLISTW LPAXESLIST;
-#else
-  typedef AXESLISTA AXESLIST;
-  typedef PAXESLISTA PAXESLIST;
-  typedef LPAXESLISTA LPAXESLIST;
-#endif
-
-  typedef struct tagENUMLOGFONTEXDVA {
-    ENUMLOGFONTEXA elfEnumLogfontEx;
-    DESIGNVECTOR elfDesignVector;
-  } ENUMLOGFONTEXDVA,*PENUMLOGFONTEXDVA,*LPENUMLOGFONTEXDVA;
-
-  typedef struct tagENUMLOGFONTEXDVW {
-    ENUMLOGFONTEXW elfEnumLogfontEx;
-    DESIGNVECTOR elfDesignVector;
-  } ENUMLOGFONTEXDVW,*PENUMLOGFONTEXDVW,*LPENUMLOGFONTEXDVW;
-#ifdef UNICODE
-  typedef ENUMLOGFONTEXDVW ENUMLOGFONTEXDV;
-  typedef PENUMLOGFONTEXDVW PENUMLOGFONTEXDV;
-  typedef LPENUMLOGFONTEXDVW LPENUMLOGFONTEXDV;
-#else
-  typedef ENUMLOGFONTEXDVA ENUMLOGFONTEXDV;
-  typedef PENUMLOGFONTEXDVA PENUMLOGFONTEXDV;
-  typedef LPENUMLOGFONTEXDVA LPENUMLOGFONTEXDV;
-#endif
-
-#ifdef UNICODE
-#define CreateFontIndirectEx CreateFontIndirectExW
-#else
-#define CreateFontIndirectEx CreateFontIndirectExA
-#endif
-
-  WINGDIAPI HFONT WINAPI CreateFontIndirectExA(CONST ENUMLOGFONTEXDVA *);
-  WINGDIAPI HFONT WINAPI CreateFontIndirectExW(CONST ENUMLOGFONTEXDVW *);
-
-#ifndef NOTEXTMETRIC
-  typedef struct tagENUMTEXTMETRICA {
-    NEWTEXTMETRICEXA etmNewTextMetricEx;
-    AXESLISTA etmAxesList;
-  } ENUMTEXTMETRICA,*PENUMTEXTMETRICA,*LPENUMTEXTMETRICA;
-  typedef struct tagENUMTEXTMETRICW
-  {
-    NEWTEXTMETRICEXW etmNewTextMetricEx;
-    AXESLISTW etmAxesList;
-  } ENUMTEXTMETRICW,*PENUMTEXTMETRICW,*LPENUMTEXTMETRICW;
-#ifdef UNICODE
-  typedef ENUMTEXTMETRICW ENUMTEXTMETRIC;
-  typedef PENUMTEXTMETRICW PENUMTEXTMETRIC;
-  typedef LPENUMTEXTMETRICW LPENUMTEXTMETRIC;
-#else
-  typedef ENUMTEXTMETRICA ENUMTEXTMETRIC;
-  typedef PENUMTEXTMETRICA PENUMTEXTMETRIC;
-  typedef LPENUMTEXTMETRICA LPENUMTEXTMETRIC;
-#endif
-#endif
-
-#ifdef UNICODE
-#define ResetDC ResetDCW
-#define RemoveFontResource RemoveFontResourceW
-#else
-#define ResetDC ResetDCA
-#define RemoveFontResource RemoveFontResourceA
-#endif
-
-  WINGDIAPI WINBOOL WINAPI GetViewportExtEx(HDC hdc,LPSIZE lpsize);
-  WINGDIAPI WINBOOL WINAPI GetViewportOrgEx(HDC hdc,LPPOINT lppoint);
-  WINGDIAPI WINBOOL WINAPI GetWindowExtEx(HDC hdc,LPSIZE lpsize);
-  WINGDIAPI WINBOOL WINAPI GetWindowOrgEx(HDC hdc,LPPOINT lppoint);
-  WINGDIAPI int WINAPI IntersectClipRect(HDC hdc,int left,int top,int right,int bottom);
-  WINGDIAPI WINBOOL WINAPI InvertRgn(HDC hdc,HRGN hrgn);
-  WINGDIAPI WINBOOL WINAPI LineDDA(int xStart,int yStart,int xEnd,int yEnd,LINEDDAPROC lpProc,LPARAM data);
-  WINGDIAPI WINBOOL WINAPI LineTo(HDC hdc,int x,int y);
-  WINGDIAPI WINBOOL WINAPI MaskBlt(HDC hdcDest,int xDest,int yDest,int width,int height,HDC hdcSrc,int xSrc,int ySrc,HBITMAP hbmMask,int xMask,int yMask,DWORD rop);
-  WINGDIAPI WINBOOL WINAPI PlgBlt(HDC hdcDest,CONST POINT *lpPoint,HDC hdcSrc,int xSrc,int ySrc,int width,int height,HBITMAP hbmMask,int xMask,int yMask);
-  WINGDIAPI int WINAPI OffsetClipRgn(HDC hdc,int x,int y);
-  WINGDIAPI int WINAPI OffsetRgn(HRGN hrgn,int x,int y);
-  WINGDIAPI WINBOOL WINAPI PatBlt(HDC hdc,int x,int y,int w,int h,DWORD rop);
-  WINGDIAPI WINBOOL WINAPI Pie(HDC hdc,int left,int top,int right,int bottom,int xr1,int yr1,int xr2,int yr2);
-  WINGDIAPI WINBOOL WINAPI PlayMetaFile(HDC hdc,HMETAFILE hmf);
-  WINGDIAPI WINBOOL WINAPI PaintRgn(HDC hdc,HRGN hrgn);
-  WINGDIAPI WINBOOL WINAPI PolyPolygon(HDC hdc,CONST POINT *apt,CONST INT *asz,int csz);
-  WINGDIAPI WINBOOL WINAPI PtInRegion(HRGN hrgn,int x,int y);
-  WINGDIAPI WINBOOL WINAPI PtVisible(HDC hdc,int x,int y);
-  WINGDIAPI WINBOOL WINAPI RectInRegion(HRGN hrgn,CONST RECT *lprect);
-  WINGDIAPI WINBOOL WINAPI RectVisible(HDC hdc,CONST RECT *lprect);
-  WINGDIAPI WINBOOL WINAPI Rectangle(HDC hdc,int left,int top,int right,int bottom);
-  WINGDIAPI WINBOOL WINAPI RestoreDC(HDC hdc,int nSavedDC);
-  WINGDIAPI HDC WINAPI ResetDCA(HDC hdc,CONST DEVMODEA *lpdm);
-  WINGDIAPI HDC WINAPI ResetDCW(HDC hdc,CONST DEVMODEW *lpdm);
-  WINGDIAPI UINT WINAPI RealizePalette(HDC hdc);
-  WINGDIAPI WINBOOL WINAPI RemoveFontResourceA(LPCSTR lpFileName);
-  WINGDIAPI WINBOOL WINAPI RemoveFontResourceW(LPCWSTR lpFileName);
-  WINGDIAPI WINBOOL WINAPI RoundRect(HDC hdc,int left,int top,int right,int bottom,int width,int height);
-  WINGDIAPI WINBOOL WINAPI ResizePalette(HPALETTE hpal,UINT n);
-  WINGDIAPI int WINAPI SaveDC(HDC hdc);
-  WINGDIAPI int WINAPI SelectClipRgn(HDC hdc,HRGN hrgn);
-  WINGDIAPI int WINAPI ExtSelectClipRgn(HDC hdc,HRGN hrgn,int mode);
-  WINGDIAPI int WINAPI SetMetaRgn(HDC hdc);
-  WINGDIAPI HGDIOBJ WINAPI SelectObject(HDC hdc,HGDIOBJ h);
-  WINGDIAPI HPALETTE WINAPI SelectPalette(HDC hdc,HPALETTE hPal,WINBOOL bForceBkgd);
-  WINGDIAPI COLORREF WINAPI SetBkColor(HDC hdc,COLORREF color);
-  WINGDIAPI COLORREF WINAPI SetDCBrushColor(HDC hdc,COLORREF color);
-  WINGDIAPI COLORREF WINAPI SetDCPenColor(HDC hdc,COLORREF color);
-  WINGDIAPI int WINAPI SetBkMode(HDC hdc,int mode);
-  WINGDIAPI LONG WINAPI SetBitmapBits(HBITMAP hbm,DWORD cb,CONST VOID *pvBits);
-  WINGDIAPI UINT WINAPI SetBoundsRect(HDC hdc,CONST RECT *lprect,UINT flags);
-  WINGDIAPI int WINAPI SetDIBits(HDC hdc,HBITMAP hbm,UINT start,UINT cLines,CONST VOID *lpBits,CONST BITMAPINFO *lpbmi,UINT ColorUse);
-  WINGDIAPI int WINAPI SetDIBitsToDevice(HDC hdc,int xDest,int yDest,DWORD w,DWORD h,int xSrc,int ySrc,UINT StartScan,UINT cLines,CONST VOID *lpvBits,CONST BITMAPINFO *lpbmi,UINT ColorUse);
-  WINGDIAPI DWORD WINAPI SetMapperFlags(HDC hdc,DWORD flags);
-  WINGDIAPI int WINAPI SetGraphicsMode(HDC hdc,int iMode);
-  WINGDIAPI int WINAPI SetMapMode(HDC hdc,int iMode);
-  WINGDIAPI DWORD WINAPI SetLayout(HDC hdc,DWORD l);
-  WINGDIAPI DWORD WINAPI GetLayout(HDC hdc);
-  WINGDIAPI HMETAFILE WINAPI SetMetaFileBitsEx(UINT cbBuffer,CONST BYTE *lpData);
-  WINGDIAPI UINT WINAPI SetPaletteEntries(HPALETTE hpal,UINT iStart,UINT cEntries,CONST PALETTEENTRY *pPalEntries);
-  WINGDIAPI COLORREF WINAPI SetPixel(HDC hdc,int x,int y,COLORREF color);
-  WINGDIAPI WINBOOL WINAPI SetPixelV(HDC hdc,int x,int y,COLORREF color);
-  WINGDIAPI WINBOOL WINAPI SetPixelFormat(HDC hdc,int format,CONST PIXELFORMATDESCRIPTOR *ppfd);
-  WINGDIAPI int WINAPI SetPolyFillMode(HDC hdc,int mode);
-  WINGDIAPI WINBOOL WINAPI StretchBlt(HDC hdcDest,int xDest,int yDest,int wDest,int hDest,HDC hdcSrc,int xSrc,int ySrc,int wSrc,int hSrc,DWORD rop);
-  WINGDIAPI WINBOOL WINAPI SetRectRgn(HRGN hrgn,int left,int top,int right,int bottom);
-  WINGDIAPI int WINAPI StretchDIBits(HDC hdc,int xDest,int yDest,int DestWidth,int DestHeight,int xSrc,int ySrc,int SrcWidth,int SrcHeight,CONST VOID *lpBits,CONST BITMAPINFO *lpbmi,UINT iUsage,DWORD rop);
-  WINGDIAPI int WINAPI SetROP2(HDC hdc,int rop2);
-  WINGDIAPI int WINAPI SetStretchBltMode(HDC hdc,int mode);
-  WINGDIAPI UINT WINAPI SetSystemPaletteUse(HDC hdc,UINT use);
-  WINGDIAPI int WINAPI SetTextCharacterExtra(HDC hdc,int extra);
-  WINGDIAPI COLORREF WINAPI SetTextColor(HDC hdc,COLORREF color);
-  WINGDIAPI UINT WINAPI SetTextAlign(HDC hdc,UINT align);
-  WINGDIAPI WINBOOL WINAPI SetTextJustification(HDC hdc,int extra,int count);
-  WINGDIAPI WINBOOL WINAPI UpdateColors(HDC hdc);
-
-  typedef USHORT COLOR16;
-
-  typedef struct _TRIVERTEX {
-    LONG x;
-    LONG y;
-    COLOR16 Red;
-    COLOR16 Green;
-    COLOR16 Blue;
-    COLOR16 Alpha;
-  } TRIVERTEX,*PTRIVERTEX,*LPTRIVERTEX;
-
-  typedef struct _GRADIENT_TRIANGLE {
-    ULONG Vertex1;
-    ULONG Vertex2;
-    ULONG Vertex3;
-  } GRADIENT_TRIANGLE,*PGRADIENT_TRIANGLE,*LPGRADIENT_TRIANGLE;
-
-  typedef struct _GRADIENT_RECT {
-    ULONG UpperLeft;
-    ULONG LowerRight;
-  } GRADIENT_RECT,*PGRADIENT_RECT,*LPGRADIENT_RECT;
-
-  typedef struct _BLENDFUNCTION {
-    BYTE BlendOp;
-    BYTE BlendFlags;
-    BYTE SourceConstantAlpha;
-    BYTE AlphaFormat;
-  } BLENDFUNCTION,*PBLENDFUNCTION;
-
-#define AC_SRC_OVER 0x00
-#define AC_SRC_ALPHA 0x01
-
-  WINGDIAPI WINBOOL WINAPI AlphaBlend(HDC hdcDest,int xoriginDest,int yoriginDest,int wDest,int hDest,HDC hdcSrc,int xoriginSrc,int yoriginSrc,int wSrc,int hSrc,BLENDFUNCTION ftn);
-  WINGDIAPI WINBOOL WINAPI TransparentBlt(HDC hdcDest,int xoriginDest,int yoriginDest,int wDest,int hDest,HDC hdcSrc,int xoriginSrc,int yoriginSrc,int wSrc,int hSrc,UINT crTransparent);
-
-#define GRADIENT_FILL_RECT_H 0x00000000
-#define GRADIENT_FILL_RECT_V 0x00000001
-#define GRADIENT_FILL_TRIANGLE 0x00000002
-#define GRADIENT_FILL_OP_FLAG 0x000000ff
-
-  WINGDIAPI WINBOOL WINAPI GradientFill(HDC hdc,PTRIVERTEX pVertex,ULONG nVertex,PVOID pMesh,ULONG nMesh,ULONG ulMode);
-
-#ifndef NOMETAFILE
-
-#ifdef UNICODE
-#define CopyEnhMetaFile CopyEnhMetaFileW
-#define CreateEnhMetaFile CreateEnhMetaFileW
-#define GetEnhMetaFile GetEnhMetaFileW
-#define GetEnhMetaFileDescription GetEnhMetaFileDescriptionW
-#else
-#define CopyEnhMetaFile CopyEnhMetaFileA
-#define CreateEnhMetaFile CreateEnhMetaFileA
-#define GetEnhMetaFile GetEnhMetaFileA
-#define GetEnhMetaFileDescription GetEnhMetaFileDescriptionA
-#endif
-
-  WINGDIAPI WINBOOL WINAPI PlayMetaFileRecord(HDC hdc,LPHANDLETABLE lpHandleTable,LPMETARECORD lpMR,UINT noObjs);
-
-  typedef int (CALLBACK *MFENUMPROC)(HDC hdc,HANDLETABLE *lpht,METARECORD *lpMR,int nObj,LPARAM param);
-
-  WINGDIAPI WINBOOL WINAPI EnumMetaFile(HDC hdc,HMETAFILE hmf,MFENUMPROC proc,LPARAM param);
-
-  typedef int (CALLBACK *ENHMFENUMPROC)(HDC hdc,HANDLETABLE *lpht,CONST ENHMETARECORD *lpmr,int hHandles,LPARAM data);
-
-  WINGDIAPI HENHMETAFILE WINAPI CloseEnhMetaFile(HDC hdc);
-  WINGDIAPI HENHMETAFILE WINAPI CopyEnhMetaFileA(HENHMETAFILE hEnh,LPCSTR lpFileName);
-  WINGDIAPI HENHMETAFILE WINAPI CopyEnhMetaFileW(HENHMETAFILE hEnh,LPCWSTR lpFileName);
-  WINGDIAPI HDC WINAPI CreateEnhMetaFileA(HDC hdc,LPCSTR lpFilename,CONST RECT *lprc,LPCSTR lpDesc);
-  WINGDIAPI HDC WINAPI CreateEnhMetaFileW(HDC hdc,LPCWSTR lpFilename,CONST RECT *lprc,LPCWSTR lpDesc);
-  WINGDIAPI WINBOOL WINAPI DeleteEnhMetaFile(HENHMETAFILE hmf);
-  WINGDIAPI WINBOOL WINAPI EnumEnhMetaFile(HDC hdc,HENHMETAFILE hmf,ENHMFENUMPROC proc,LPVOID param,CONST RECT *lpRect);
-  WINGDIAPI HENHMETAFILE WINAPI GetEnhMetaFileA(LPCSTR lpName);
-  WINGDIAPI HENHMETAFILE WINAPI GetEnhMetaFileW(LPCWSTR lpName);
-  WINGDIAPI UINT WINAPI GetEnhMetaFileBits(HENHMETAFILE hEMF,UINT nSize,LPBYTE lpData);
-  WINGDIAPI UINT WINAPI GetEnhMetaFileDescriptionA(HENHMETAFILE hemf,UINT cchBuffer,LPSTR lpDescription);
-  WINGDIAPI UINT WINAPI GetEnhMetaFileDescriptionW(HENHMETAFILE hemf,UINT cchBuffer,LPWSTR lpDescription);
-  WINGDIAPI UINT WINAPI GetEnhMetaFileHeader(HENHMETAFILE hemf,UINT nSize,LPENHMETAHEADER lpEnhMetaHeader);
-  WINGDIAPI UINT WINAPI GetEnhMetaFilePaletteEntries(HENHMETAFILE hemf,UINT nNumEntries,LPPALETTEENTRY lpPaletteEntries);
-  WINGDIAPI UINT WINAPI GetEnhMetaFilePixelFormat(HENHMETAFILE hemf,UINT cbBuffer,PIXELFORMATDESCRIPTOR *ppfd);
-  WINGDIAPI UINT WINAPI GetWinMetaFileBits(HENHMETAFILE hemf,UINT cbData16,LPBYTE pData16,INT iMapMode,HDC hdcRef);
-  WINGDIAPI WINBOOL WINAPI PlayEnhMetaFile(HDC hdc,HENHMETAFILE hmf,CONST RECT *lprect);
-  WINGDIAPI WINBOOL WINAPI PlayEnhMetaFileRecord(HDC hdc,LPHANDLETABLE pht,CONST ENHMETARECORD *pmr,UINT cht);
-  WINGDIAPI HENHMETAFILE WINAPI SetEnhMetaFileBits(UINT nSize,CONST BYTE *pb);
-  WINGDIAPI HENHMETAFILE WINAPI SetWinMetaFileBits(UINT nSize,CONST BYTE *lpMeta16Data,HDC hdcRef,CONST METAFILEPICT *lpMFP);
-  WINGDIAPI WINBOOL WINAPI GdiComment(HDC hdc,UINT nSize,CONST BYTE *lpData);
-#endif
-
-#ifndef NOTEXTMETRIC
-#ifdef UNICODE
-#define GetTextMetrics GetTextMetricsW
-#else
-#define GetTextMetrics GetTextMetricsA
-#endif
-
-  WINGDIAPI WINBOOL WINAPI GetTextMetricsA(HDC hdc,LPTEXTMETRICA lptm);
-  WINGDIAPI WINBOOL WINAPI GetTextMetricsW(HDC hdc,LPTEXTMETRICW lptm);
-#endif
-
-  typedef struct tagDIBSECTION {
-    BITMAP dsBm;
-    BITMAPINFOHEADER dsBmih;
-    DWORD dsBitfields[3];
-    HANDLE dshSection;
-    DWORD dsOffset;
-  } DIBSECTION,*LPDIBSECTION,*PDIBSECTION;
-
-  WINGDIAPI WINBOOL WINAPI AngleArc(HDC hdc,int x,int y,DWORD r,FLOAT StartAngle,FLOAT SweepAngle);
-  WINGDIAPI WINBOOL WINAPI PolyPolyline(HDC hdc,CONST POINT *apt,CONST DWORD *asz,DWORD csz);
-  WINGDIAPI WINBOOL WINAPI GetWorldTransform(HDC hdc,LPXFORM lpxf);
-  WINGDIAPI WINBOOL WINAPI SetWorldTransform(HDC hdc,CONST XFORM *lpxf);
-  WINGDIAPI WINBOOL WINAPI ModifyWorldTransform(HDC hdc,CONST XFORM *lpxf,DWORD mode);
-  WINGDIAPI WINBOOL WINAPI CombineTransform(LPXFORM lpxfOut,CONST XFORM *lpxf1,CONST XFORM *lpxf2);
-  WINGDIAPI HBITMAP WINAPI CreateDIBSection(HDC hdc,CONST BITMAPINFO *lpbmi,UINT usage,VOID **ppvBits,HANDLE hSection,DWORD offset);
-  WINGDIAPI UINT WINAPI GetDIBColorTable(HDC hdc,UINT iStart,UINT cEntries,RGBQUAD *prgbq);
-  WINGDIAPI UINT WINAPI SetDIBColorTable(HDC hdc,UINT iStart,UINT cEntries,CONST RGBQUAD *prgbq);
-
-#define CA_NEGATIVE 0x0001
-#define CA_LOG_FILTER 0x0002
-
-#define ILLUMINANT_DEVICE_DEFAULT 0
-#define ILLUMINANT_A 1
-#define ILLUMINANT_B 2
-#define ILLUMINANT_C 3
-#define ILLUMINANT_D50 4
-#define ILLUMINANT_D55 5
-#define ILLUMINANT_D65 6
-#define ILLUMINANT_D75 7
-#define ILLUMINANT_F2 8
-#define ILLUMINANT_MAX_INDEX ILLUMINANT_F2
-
-#define ILLUMINANT_TUNGSTEN ILLUMINANT_A
-#define ILLUMINANT_DAYLIGHT ILLUMINANT_C
-#define ILLUMINANT_FLUORESCENT ILLUMINANT_F2
-#define ILLUMINANT_NTSC ILLUMINANT_C
-
-#define RGB_GAMMA_MIN (WORD)02500
-#define RGB_GAMMA_MAX (WORD)65000
-
-#define REFERENCE_WHITE_MIN (WORD)6000
-#define REFERENCE_WHITE_MAX (WORD)10000
-#define REFERENCE_BLACK_MIN (WORD)0
-#define REFERENCE_BLACK_MAX (WORD)4000
-
-#define COLOR_ADJ_MIN (SHORT)-100
-#define COLOR_ADJ_MAX (SHORT)100
-
-  typedef struct tagCOLORADJUSTMENT {
-    WORD caSize;
-    WORD caFlags;
-    WORD caIlluminantIndex;
-    WORD caRedGamma;
-    WORD caGreenGamma;
-    WORD caBlueGamma;
-    WORD caReferenceBlack;
-    WORD caReferenceWhite;
-    SHORT caContrast;
-    SHORT caBrightness;
-    SHORT caColorfulness;
-    SHORT caRedGreenTint;
-  } COLORADJUSTMENT,*PCOLORADJUSTMENT,*LPCOLORADJUSTMENT;
-
-  WINGDIAPI WINBOOL WINAPI SetColorAdjustment(HDC hdc,CONST COLORADJUSTMENT *lpca);
-  WINGDIAPI WINBOOL WINAPI GetColorAdjustment(HDC hdc,LPCOLORADJUSTMENT lpca);
-  WINGDIAPI HPALETTE WINAPI CreateHalftonePalette(HDC hdc);
-
-  typedef WINBOOL (CALLBACK *ABORTPROC)(HDC,int);
-
-  typedef struct _DOCINFOA {
-    int cbSize;
-    LPCSTR lpszDocName;
-    LPCSTR lpszOutput;
-    LPCSTR lpszDatatype;
-    DWORD fwType;
-  } DOCINFOA,*LPDOCINFOA;
-
-  typedef struct _DOCINFOW {
-    int cbSize;
-    LPCWSTR lpszDocName;
-    LPCWSTR lpszOutput;
-    LPCWSTR lpszDatatype;
-    DWORD fwType;
-  } DOCINFOW,*LPDOCINFOW;
-
-#ifdef UNICODE
-  typedef DOCINFOW DOCINFO;
-  typedef LPDOCINFOW LPDOCINFO;
-#else
-  typedef DOCINFOA DOCINFO;
-  typedef LPDOCINFOA LPDOCINFO;
-#endif
-
-#define DI_APPBANDING 0x00000001
-#define DI_ROPS_READ_DESTINATION 0x00000002
-
-#ifdef UNICODE
-#define StartDoc StartDocW
-#define GetObject GetObjectW
-#define TextOut TextOutW
-#define ExtTextOut ExtTextOutW
-#define PolyTextOut PolyTextOutW
-#define GetTextFace GetTextFaceW
-#else
-#define StartDoc StartDocA
-#define GetObject GetObjectA
-#define TextOut TextOutA
-#define ExtTextOut ExtTextOutA
-#define PolyTextOut PolyTextOutA
-#define GetTextFace GetTextFaceA
-#endif
-
-  WINGDIAPI int WINAPI StartDocA(HDC hdc,CONST DOCINFOA *lpdi);
-  WINGDIAPI int WINAPI StartDocW(HDC hdc,CONST DOCINFOW *lpdi);
-  WINGDIAPI int WINAPI EndDoc(HDC hdc);
-  WINGDIAPI int WINAPI StartPage(HDC hdc);
-  WINGDIAPI int WINAPI EndPage(HDC hdc);
-  WINGDIAPI int WINAPI AbortDoc(HDC hdc);
-  WINGDIAPI int WINAPI SetAbortProc(HDC hdc,ABORTPROC proc);
-  WINGDIAPI WINBOOL WINAPI AbortPath(HDC hdc);
-  WINGDIAPI WINBOOL WINAPI ArcTo(HDC hdc,int left,int top,int right,int bottom,int xr1,int yr1,int xr2,int yr2);
-  WINGDIAPI WINBOOL WINAPI BeginPath(HDC hdc);
-  WINGDIAPI WINBOOL WINAPI CloseFigure(HDC hdc);
-  WINGDIAPI WINBOOL WINAPI EndPath(HDC hdc);
-  WINGDIAPI WINBOOL WINAPI FillPath(HDC hdc);
-  WINGDIAPI WINBOOL WINAPI FlattenPath(HDC hdc);
-  WINGDIAPI int WINAPI GetPath(HDC hdc,LPPOINT apt,LPBYTE aj,int cpt);
-  WINGDIAPI HRGN WINAPI PathToRegion(HDC hdc);
-  WINGDIAPI WINBOOL WINAPI PolyDraw(HDC hdc,CONST POINT *apt,CONST BYTE *aj,int cpt);
-  WINGDIAPI WINBOOL WINAPI SelectClipPath(HDC hdc,int mode);
-  WINGDIAPI int WINAPI SetArcDirection(HDC hdc,int dir);
-  WINGDIAPI WINBOOL WINAPI SetMiterLimit(HDC hdc,FLOAT limit,PFLOAT old);
-  WINGDIAPI WINBOOL WINAPI StrokeAndFillPath(HDC hdc);
-  WINGDIAPI WINBOOL WINAPI StrokePath(HDC hdc);
-  WINGDIAPI WINBOOL WINAPI WidenPath(HDC hdc);
-  WINGDIAPI HPEN WINAPI ExtCreatePen(DWORD iPenStyle,DWORD cWidth,CONST LOGBRUSH *plbrush,DWORD cStyle,CONST DWORD *pstyle);
-  WINGDIAPI WINBOOL WINAPI GetMiterLimit(HDC hdc,PFLOAT plimit);
-  WINGDIAPI int WINAPI GetArcDirection(HDC hdc);
-  WINGDIAPI int WINAPI GetObjectA(HANDLE h,int c,LPVOID pv);
-  WINGDIAPI int WINAPI GetObjectW(HANDLE h,int c,LPVOID pv);
-  WINGDIAPI WINBOOL WINAPI MoveToEx(HDC hdc,int x,int y,LPPOINT lppt);
-  WINGDIAPI WINBOOL WINAPI TextOutA(HDC hdc,int x,int y,LPCSTR lpString,int c);
-  WINGDIAPI WINBOOL WINAPI TextOutW(HDC hdc,int x,int y,LPCWSTR lpString,int c);
-  WINGDIAPI WINBOOL WINAPI ExtTextOutA(HDC hdc,int x,int y,UINT options,CONST RECT *lprect,LPCSTR lpString,UINT c,CONST INT *lpDx);
-  WINGDIAPI WINBOOL WINAPI ExtTextOutW(HDC hdc,int x,int y,UINT options,CONST RECT *lprect,LPCWSTR lpString,UINT c,CONST INT *lpDx);
-  WINGDIAPI WINBOOL WINAPI PolyTextOutA(HDC hdc,CONST POLYTEXTA *ppt,int nstrings);
-  WINGDIAPI WINBOOL WINAPI PolyTextOutW(HDC hdc,CONST POLYTEXTW *ppt,int nstrings);
-  WINGDIAPI HRGN WINAPI CreatePolygonRgn(CONST POINT *pptl,int cPoint,int iMode);
-  WINGDIAPI WINBOOL WINAPI DPtoLP(HDC hdc,LPPOINT lppt,int c);
-  WINGDIAPI WINBOOL WINAPI LPtoDP(HDC hdc,LPPOINT lppt,int c);
-  WINGDIAPI WINBOOL WINAPI Polygon(HDC hdc,CONST POINT *apt,int cpt);
-  WINGDIAPI WINBOOL WINAPI Polyline(HDC hdc,CONST POINT *apt,int cpt);
-  WINGDIAPI WINBOOL WINAPI PolyBezier(HDC hdc,CONST POINT *apt,DWORD cpt);
-  WINGDIAPI WINBOOL WINAPI PolyBezierTo(HDC hdc,CONST POINT *apt,DWORD cpt);
-  WINGDIAPI WINBOOL WINAPI PolylineTo(HDC hdc,CONST POINT *apt,DWORD cpt);
-  WINGDIAPI WINBOOL WINAPI SetViewportExtEx(HDC hdc,int x,int y,LPSIZE lpsz);
-  WINGDIAPI WINBOOL WINAPI SetViewportOrgEx(HDC hdc,int x,int y,LPPOINT lppt);
-  WINGDIAPI WINBOOL WINAPI SetWindowExtEx(HDC hdc,int x,int y,LPSIZE lpsz);
-  WINGDIAPI WINBOOL WINAPI SetWindowOrgEx(HDC hdc,int x,int y,LPPOINT lppt);
-  WINGDIAPI WINBOOL WINAPI OffsetViewportOrgEx(HDC hdc,int x,int y,LPPOINT lppt);
-  WINGDIAPI WINBOOL WINAPI OffsetWindowOrgEx(HDC hdc,int x,int y,LPPOINT lppt);
-  WINGDIAPI WINBOOL WINAPI ScaleViewportExtEx(HDC hdc,int xn,int dx,int yn,int yd,LPSIZE lpsz);
-  WINGDIAPI WINBOOL WINAPI ScaleWindowExtEx(HDC hdc,int xn,int xd,int yn,int yd,LPSIZE lpsz);
-  WINGDIAPI WINBOOL WINAPI SetBitmapDimensionEx(HBITMAP hbm,int w,int h,LPSIZE lpsz);
-  WINGDIAPI WINBOOL WINAPI SetBrushOrgEx(HDC hdc,int x,int y,LPPOINT lppt);
-  WINGDIAPI int WINAPI GetTextFaceA(HDC hdc,int c,LPSTR lpName);
-  WINGDIAPI int WINAPI GetTextFaceW(HDC hdc,int c,LPWSTR lpName);
-
-#define FONTMAPPER_MAX 10
-
-  typedef struct tagKERNINGPAIR {
-    WORD wFirst;
-    WORD wSecond;
-    int iKernAmount;
-  } KERNINGPAIR,*LPKERNINGPAIR;
-
-#ifdef UNICODE
-#define GetKerningPairs GetKerningPairsW
-#else
-#define GetKerningPairs GetKerningPairsA
-#endif
-
-  WINGDIAPI DWORD WINAPI GetKerningPairsA(HDC hdc,DWORD nPairs,LPKERNINGPAIR lpKernPair);
-  WINGDIAPI DWORD WINAPI GetKerningPairsW(HDC hdc,DWORD nPairs,LPKERNINGPAIR lpKernPair);
-  WINGDIAPI WINBOOL WINAPI GetDCOrgEx(HDC hdc,LPPOINT lppt);
-  WINGDIAPI WINBOOL WINAPI FixBrushOrgEx(HDC hdc,int x,int y,LPPOINT ptl);
-  WINGDIAPI WINBOOL WINAPI UnrealizeObject(HGDIOBJ h);
-  WINGDIAPI WINBOOL WINAPI GdiFlush();
-  WINGDIAPI DWORD WINAPI GdiSetBatchLimit(DWORD dw);
-  WINGDIAPI DWORD WINAPI GdiGetBatchLimit();
-
-#define ICM_OFF 1
-#define ICM_ON 2
-#define ICM_QUERY 3
-#define ICM_DONE_OUTSIDEDC 4
-
-  typedef int (CALLBACK *ICMENUMPROCA)(LPSTR,LPARAM);
-  typedef int (CALLBACK *ICMENUMPROCW)(LPWSTR,LPARAM);
-
-#ifdef UNICODE
-#define ICMENUMPROC ICMENUMPROCW
-#define EnumICMProfiles EnumICMProfilesW
-#define UpdateICMRegKey UpdateICMRegKeyW
-#define GetLogColorSpace GetLogColorSpaceW
-#define CreateColorSpace CreateColorSpaceW
-#define GetICMProfile GetICMProfileW
-#define SetICMProfile SetICMProfileW
-#else
-#define ICMENUMPROC ICMENUMPROCA
-#define EnumICMProfiles EnumICMProfilesA
-#define UpdateICMRegKey UpdateICMRegKeyA
-#define GetLogColorSpace GetLogColorSpaceA
-#define CreateColorSpace CreateColorSpaceA
-#define GetICMProfile GetICMProfileA
-#define SetICMProfile SetICMProfileA
-#endif
-
-  WINGDIAPI int WINAPI SetICMMode(HDC hdc,int mode);
-  WINGDIAPI WINBOOL WINAPI CheckColorsInGamut(HDC hdc,LPVOID lpRGBTriple,LPVOID dlpBuffer,DWORD nCount);
-  WINGDIAPI HCOLORSPACE WINAPI GetColorSpace(HDC hdc);
-  WINGDIAPI WINBOOL WINAPI GetLogColorSpaceA(HCOLORSPACE hColorSpace,LPLOGCOLORSPACEA lpBuffer,DWORD nSize);
-  WINGDIAPI WINBOOL WINAPI GetLogColorSpaceW(HCOLORSPACE hColorSpace,LPLOGCOLORSPACEW lpBuffer,DWORD nSize);
-  WINGDIAPI HCOLORSPACE WINAPI CreateColorSpaceA(LPLOGCOLORSPACEA lplcs);
-  WINGDIAPI HCOLORSPACE WINAPI CreateColorSpaceW(LPLOGCOLORSPACEW lplcs);
-  WINGDIAPI HCOLORSPACE WINAPI SetColorSpace(HDC hdc,HCOLORSPACE hcs);
-  WINGDIAPI WINBOOL WINAPI DeleteColorSpace(HCOLORSPACE hcs);
-  WINGDIAPI WINBOOL WINAPI GetICMProfileA(HDC hdc,LPDWORD pBufSize,LPSTR pszFilename);
-  WINGDIAPI WINBOOL WINAPI GetICMProfileW(HDC hdc,LPDWORD pBufSize,LPWSTR pszFilename);
-  WINGDIAPI WINBOOL WINAPI SetICMProfileA(HDC hdc,LPSTR lpFileName);
-  WINGDIAPI WINBOOL WINAPI SetICMProfileW(HDC hdc,LPWSTR lpFileName);
-  WINGDIAPI WINBOOL WINAPI GetDeviceGammaRamp(HDC hdc,LPVOID lpRamp);
-  WINGDIAPI WINBOOL WINAPI SetDeviceGammaRamp(HDC hdc,LPVOID lpRamp);
-  WINGDIAPI WINBOOL WINAPI ColorMatchToTarget(HDC hdc,HDC hdcTarget,DWORD action);
-  WINGDIAPI int WINAPI EnumICMProfilesA(HDC hdc,ICMENUMPROCA proc,LPARAM param);
-  WINGDIAPI int WINAPI EnumICMProfilesW(HDC hdc,ICMENUMPROCW proc,LPARAM param);
-  WINGDIAPI WINBOOL WINAPI UpdateICMRegKeyA(DWORD reserved,LPSTR lpszCMID,LPSTR lpszFileName,UINT command);
-  WINGDIAPI WINBOOL WINAPI UpdateICMRegKeyW(DWORD reserved,LPWSTR lpszCMID,LPWSTR lpszFileName,UINT command);
-  WINGDIAPI WINBOOL WINAPI ColorCorrectPalette(HDC hdc,HPALETTE hPal,DWORD deFirst,DWORD num);
-
-#ifndef NOMETAFILE
-
-#define ENHMETA_SIGNATURE 0x464D4520
-#define ENHMETA_STOCK_OBJECT 0x80000000
-
-#define EMR_HEADER 1
-#define EMR_POLYBEZIER 2
-#define EMR_POLYGON 3
-#define EMR_POLYLINE 4
-#define EMR_POLYBEZIERTO 5
-#define EMR_POLYLINETO 6
-#define EMR_POLYPOLYLINE 7
-#define EMR_POLYPOLYGON 8
-#define EMR_SETWINDOWEXTEX 9
-#define EMR_SETWINDOWORGEX 10
-#define EMR_SETVIEWPORTEXTEX 11
-#define EMR_SETVIEWPORTORGEX 12
-#define EMR_SETBRUSHORGEX 13
-#define EMR_EOF 14
-#define EMR_SETPIXELV 15
-#define EMR_SETMAPPERFLAGS 16
-#define EMR_SETMAPMODE 17
-#define EMR_SETBKMODE 18
-#define EMR_SETPOLYFILLMODE 19
-#define EMR_SETROP2 20
-#define EMR_SETSTRETCHBLTMODE 21
-#define EMR_SETTEXTALIGN 22
-#define EMR_SETCOLORADJUSTMENT 23
-#define EMR_SETTEXTCOLOR 24
-#define EMR_SETBKCOLOR 25
-#define EMR_OFFSETCLIPRGN 26
-#define EMR_MOVETOEX 27
-#define EMR_SETMETARGN 28
-#define EMR_EXCLUDECLIPRECT 29
-#define EMR_INTERSECTCLIPRECT 30
-#define EMR_SCALEVIEWPORTEXTEX 31
-#define EMR_SCALEWINDOWEXTEX 32
-#define EMR_SAVEDC 33
-#define EMR_RESTOREDC 34
-#define EMR_SETWORLDTRANSFORM 35
-#define EMR_MODIFYWORLDTRANSFORM 36
-#define EMR_SELECTOBJECT 37
-#define EMR_CREATEPEN 38
-#define EMR_CREATEBRUSHINDIRECT 39
-#define EMR_DELETEOBJECT 40
-#define EMR_ANGLEARC 41
-#define EMR_ELLIPSE 42
-#define EMR_RECTANGLE 43
-#define EMR_ROUNDRECT 44
-#define EMR_ARC 45
-#define EMR_CHORD 46
-#define EMR_PIE 47
-#define EMR_SELECTPALETTE 48
-#define EMR_CREATEPALETTE 49
-#define EMR_SETPALETTEENTRIES 50
-#define EMR_RESIZEPALETTE 51
-#define EMR_REALIZEPALETTE 52
-#define EMR_EXTFLOODFILL 53
-#define EMR_LINETO 54
-#define EMR_ARCTO 55
-#define EMR_POLYDRAW 56
-#define EMR_SETARCDIRECTION 57
-#define EMR_SETMITERLIMIT 58
-#define EMR_BEGINPATH 59
-#define EMR_ENDPATH 60
-#define EMR_CLOSEFIGURE 61
-#define EMR_FILLPATH 62
-#define EMR_STROKEANDFILLPATH 63
-#define EMR_STROKEPATH 64
-#define EMR_FLATTENPATH 65
-#define EMR_WIDENPATH 66
-#define EMR_SELECTCLIPPATH 67
-#define EMR_ABORTPATH 68
-
-#define EMR_GDICOMMENT 70
-#define EMR_FILLRGN 71
-#define EMR_FRAMERGN 72
-#define EMR_INVERTRGN 73
-#define EMR_PAINTRGN 74
-#define EMR_EXTSELECTCLIPRGN 75
-#define EMR_BITBLT 76
-#define EMR_STRETCHBLT 77
-#define EMR_MASKBLT 78
-#define EMR_PLGBLT 79
-#define EMR_SETDIBITSTODEVICE 80
-#define EMR_STRETCHDIBITS 81
-#define EMR_EXTCREATEFONTINDIRECTW 82
-#define EMR_EXTTEXTOUTA 83
-#define EMR_EXTTEXTOUTW 84
-#define EMR_POLYBEZIER16 85
-#define EMR_POLYGON16 86
-#define EMR_POLYLINE16 87
-#define EMR_POLYBEZIERTO16 88
-#define EMR_POLYLINETO16 89
-#define EMR_POLYPOLYLINE16 90
-#define EMR_POLYPOLYGON16 91
-#define EMR_POLYDRAW16 92
-#define EMR_CREATEMONOBRUSH 93
-#define EMR_CREATEDIBPATTERNBRUSHPT 94
-#define EMR_EXTCREATEPEN 95
-#define EMR_POLYTEXTOUTA 96
-#define EMR_POLYTEXTOUTW 97
-
-#define EMR_SETICMMODE 98
-#define EMR_CREATECOLORSPACE 99
-#define EMR_SETCOLORSPACE 100
-#define EMR_DELETECOLORSPACE 101
-#define EMR_GLSRECORD 102
-#define EMR_GLSBOUNDEDRECORD 103
-#define EMR_PIXELFORMAT 104
-#define EMR_RESERVED_105 105
-#define EMR_RESERVED_106 106
-#define EMR_RESERVED_107 107
-#define EMR_RESERVED_108 108
-#define EMR_RESERVED_109 109
-#define EMR_RESERVED_110 110
-#define EMR_COLORCORRECTPALETTE 111
-#define EMR_SETICMPROFILEA 112
-#define EMR_SETICMPROFILEW 113
-#define EMR_ALPHABLEND 114
-#define EMR_SETLAYOUT 115
-#define EMR_TRANSPARENTBLT 116
-#define EMR_RESERVED_117 117
-#define EMR_GRADIENTFILL 118
-#define EMR_RESERVED_119 119
-#define EMR_RESERVED_120 120
-#define EMR_COLORMATCHTOTARGETW 121
-#define EMR_CREATECOLORSPACEW 122
-
-#define EMR_MIN 1
-
-#define EMR_MAX 122
-
-  typedef struct tagEMR {
-    DWORD iType;
-    DWORD nSize;
-  } EMR,*PEMR;
-
-  typedef struct tagEMRTEXT {
-    POINTL ptlReference;
-    DWORD nChars;
-    DWORD offString;
-    DWORD fOptions;
-    RECTL rcl;
-    DWORD offDx;
-  } EMRTEXT,*PEMRTEXT;
-
-  typedef struct tagABORTPATH {
-    EMR emr;
-  } EMRABORTPATH,*PEMRABORTPATH,EMRBEGINPATH,*PEMRBEGINPATH,EMRENDPATH,*PEMRENDPATH,EMRCLOSEFIGURE,*PEMRCLOSEFIGURE,EMRFLATTENPATH,*PEMRFLATTENPATH,EMRWIDENPATH,*PEMRWIDENPATH,EMRSETMETARGN,*PEMRSETMETARGN,EMRSAVEDC,*PEMRSAVEDC,EMRREALIZEPALETTE,*PEMRREALIZEPALETTE;
-
-  typedef struct tagEMRSELECTCLIPPATH {
-    EMR emr;
-    DWORD iMode;
-  } EMRSELECTCLIPPATH,*PEMRSELECTCLIPPATH,EMRSETBKMODE,*PEMRSETBKMODE,EMRSETMAPMODE,*PEMRSETMAPMODE,EMRSETLAYOUT,*PEMRSETLAYOUT,
-    EMRSETPOLYFILLMODE,*PEMRSETPOLYFILLMODE,EMRSETROP2,*PEMRSETROP2,EMRSETSTRETCHBLTMODE,*PEMRSETSTRETCHBLTMODE,EMRSETICMMODE,
-    *PEMRSETICMMODE,EMRSETTEXTALIGN,*PEMRSETTEXTALIGN;
-
-  typedef struct tagEMRSETMITERLIMIT {
-    EMR emr;
-    FLOAT eMiterLimit;
-  } EMRSETMITERLIMIT,*PEMRSETMITERLIMIT;
-
-  typedef struct tagEMRRESTOREDC {
-    EMR emr;
-    LONG iRelative;
-  } EMRRESTOREDC,*PEMRRESTOREDC;
-
-  typedef struct tagEMRSETARCDIRECTION {
-    EMR emr;
-    DWORD iArcDirection;
-
-  } EMRSETARCDIRECTION,*PEMRSETARCDIRECTION;
-
-  typedef struct tagEMRSETMAPPERFLAGS {
-    EMR emr;
-    DWORD dwFlags;
-  } EMRSETMAPPERFLAGS,*PEMRSETMAPPERFLAGS;
-
-  typedef struct tagEMRSETTEXTCOLOR {
-    EMR emr;
-    COLORREF crColor;
-  } EMRSETBKCOLOR,*PEMRSETBKCOLOR,EMRSETTEXTCOLOR,*PEMRSETTEXTCOLOR;
-
-  typedef struct tagEMRSELECTOBJECT {
-    EMR emr;
-    DWORD ihObject;
-  } EMRSELECTOBJECT,*PEMRSELECTOBJECT,EMRDELETEOBJECT,*PEMRDELETEOBJECT;
-
-  typedef struct tagEMRSELECTPALETTE {
-    EMR emr;
-    DWORD ihPal;
-  } EMRSELECTPALETTE,*PEMRSELECTPALETTE;
-
-  typedef struct tagEMRRESIZEPALETTE {
-    EMR emr;
-    DWORD ihPal;
-    DWORD cEntries;
-  } EMRRESIZEPALETTE,*PEMRRESIZEPALETTE;
-
-  typedef struct tagEMRSETPALETTEENTRIES {
-    EMR emr;
-    DWORD ihPal;
-    DWORD iStart;
-    DWORD cEntries;
-    PALETTEENTRY aPalEntries[1];
-  } EMRSETPALETTEENTRIES,*PEMRSETPALETTEENTRIES;
-
-  typedef struct tagEMRSETCOLORADJUSTMENT {
-    EMR emr;
-    COLORADJUSTMENT ColorAdjustment;
-  } EMRSETCOLORADJUSTMENT,*PEMRSETCOLORADJUSTMENT;
-
-  typedef struct tagEMRGDICOMMENT {
-    EMR emr;
-    DWORD cbData;
-    BYTE Data[1];
-  } EMRGDICOMMENT,*PEMRGDICOMMENT;
-
-  typedef struct tagEMREOF {
-    EMR emr;
-    DWORD nPalEntries;
-    DWORD offPalEntries;
-    DWORD nSizeLast;
-  } EMREOF,*PEMREOF;
-
-  typedef struct tagEMRLINETO {
-    EMR emr;
-    POINTL ptl;
-  } EMRLINETO,*PEMRLINETO,EMRMOVETOEX,*PEMRMOVETOEX;
-
-  typedef struct tagEMROFFSETCLIPRGN {
-    EMR emr;
-    POINTL ptlOffset;
-  } EMROFFSETCLIPRGN,*PEMROFFSETCLIPRGN;
-
-  typedef struct tagEMRFILLPATH {
-    EMR emr;
-    RECTL rclBounds;
-  } EMRFILLPATH,*PEMRFILLPATH,EMRSTROKEANDFILLPATH,*PEMRSTROKEANDFILLPATH,EMRSTROKEPATH,*PEMRSTROKEPATH;
-
-  typedef struct tagEMREXCLUDECLIPRECT {
-    EMR emr;
-    RECTL rclClip;
-  } EMREXCLUDECLIPRECT,*PEMREXCLUDECLIPRECT,EMRINTERSECTCLIPRECT,*PEMRINTERSECTCLIPRECT;
-
-  typedef struct tagEMRSETVIEWPORTORGEX {
-    EMR emr;
-    POINTL ptlOrigin;
-  } EMRSETVIEWPORTORGEX,*PEMRSETVIEWPORTORGEX,EMRSETWINDOWORGEX,*PEMRSETWINDOWORGEX,EMRSETBRUSHORGEX,*PEMRSETBRUSHORGEX;
-
-  typedef struct tagEMRSETVIEWPORTEXTEX {
-    EMR emr;
-    SIZEL szlExtent;
-  } EMRSETVIEWPORTEXTEX,*PEMRSETVIEWPORTEXTEX,EMRSETWINDOWEXTEX,*PEMRSETWINDOWEXTEX;
-
-  typedef struct tagEMRSCALEVIEWPORTEXTEX {
-    EMR emr;
-    LONG xNum;
-    LONG xDenom;
-    LONG yNum;
-    LONG yDenom;
-  } EMRSCALEVIEWPORTEXTEX,*PEMRSCALEVIEWPORTEXTEX,EMRSCALEWINDOWEXTEX,*PEMRSCALEWINDOWEXTEX;
-
-  typedef struct tagEMRSETWORLDTRANSFORM {
-    EMR emr;
-    XFORM xform;
-  } EMRSETWORLDTRANSFORM,*PEMRSETWORLDTRANSFORM;
-
-  typedef struct tagEMRMODIFYWORLDTRANSFORM {
-    EMR emr;
-    XFORM xform;
-    DWORD iMode;
-  } EMRMODIFYWORLDTRANSFORM,*PEMRMODIFYWORLDTRANSFORM;
-
-  typedef struct tagEMRSETPIXELV {
-    EMR emr;
-    POINTL ptlPixel;
-    COLORREF crColor;
-  } EMRSETPIXELV,*PEMRSETPIXELV;
-
-  typedef struct tagEMREXTFLOODFILL {
-    EMR emr;
-    POINTL ptlStart;
-    COLORREF crColor;
-    DWORD iMode;
-  } EMREXTFLOODFILL,*PEMREXTFLOODFILL;
-
-  typedef struct tagEMRELLIPSE {
-    EMR emr;
-    RECTL rclBox;
-  } EMRELLIPSE,*PEMRELLIPSE,EMRRECTANGLE,*PEMRRECTANGLE;
-
-  typedef struct tagEMRROUNDRECT {
-    EMR emr;
-    RECTL rclBox;
-    SIZEL szlCorner;
-  } EMRROUNDRECT,*PEMRROUNDRECT;
-
-  typedef struct tagEMRARC {
-    EMR emr;
-    RECTL rclBox;
-    POINTL ptlStart;
-    POINTL ptlEnd;
-  } EMRARC,*PEMRARC,EMRARCTO,*PEMRARCTO,EMRCHORD,*PEMRCHORD,EMRPIE,*PEMRPIE;
-
-  typedef struct tagEMRANGLEARC {
-    EMR emr;
-    POINTL ptlCenter;
-    DWORD nRadius;
-    FLOAT eStartAngle;
-    FLOAT eSweepAngle;
-  } EMRANGLEARC,*PEMRANGLEARC;
-
-  typedef struct tagEMRPOLYLINE {
-    EMR emr;
-    RECTL rclBounds;
-    DWORD cptl;
-    POINTL aptl[1];
-  } EMRPOLYLINE,*PEMRPOLYLINE,EMRPOLYBEZIER,*PEMRPOLYBEZIER,EMRPOLYGON,*PEMRPOLYGON,EMRPOLYBEZIERTO,*PEMRPOLYBEZIERTO,EMRPOLYLINETO,*PEMRPOLYLINETO;
-
-  typedef struct tagEMRPOLYLINE16 {
-    EMR emr;
-    RECTL rclBounds;
-    DWORD cpts;
-    POINTS apts[1];
-  } EMRPOLYLINE16,*PEMRPOLYLINE16,EMRPOLYBEZIER16,*PEMRPOLYBEZIER16,EMRPOLYGON16,*PEMRPOLYGON16,EMRPOLYBEZIERTO16,*PEMRPOLYBEZIERTO16,EMRPOLYLINETO16,*PEMRPOLYLINETO16;
-
-  typedef struct tagEMRPOLYDRAW {
-    EMR emr;
-    RECTL rclBounds;
-    DWORD cptl;
-    POINTL aptl[1];
-    BYTE abTypes[1];
-  } EMRPOLYDRAW,*PEMRPOLYDRAW;
-
-  typedef struct tagEMRPOLYDRAW16 {
-    EMR emr;
-    RECTL rclBounds;
-    DWORD cpts;
-    POINTS apts[1];
-    BYTE abTypes[1];
-  } EMRPOLYDRAW16,*PEMRPOLYDRAW16;
-
-  typedef struct tagEMRPOLYPOLYLINE {
-    EMR emr;
-    RECTL rclBounds;
-    DWORD nPolys;
-    DWORD cptl;
-    DWORD aPolyCounts[1];
-    POINTL aptl[1];
-  } EMRPOLYPOLYLINE,*PEMRPOLYPOLYLINE,EMRPOLYPOLYGON,*PEMRPOLYPOLYGON;
-
-  typedef struct tagEMRPOLYPOLYLINE16 {
-    EMR emr;
-    RECTL rclBounds;
-    DWORD nPolys;
-    DWORD cpts;
-    DWORD aPolyCounts[1];
-    POINTS apts[1];
-  } EMRPOLYPOLYLINE16,*PEMRPOLYPOLYLINE16,EMRPOLYPOLYGON16,*PEMRPOLYPOLYGON16;
-
-  typedef struct tagEMRINVERTRGN {
-    EMR emr;
-    RECTL rclBounds;
-    DWORD cbRgnData;
-    BYTE RgnData[1];
-  } EMRINVERTRGN,*PEMRINVERTRGN,EMRPAINTRGN,*PEMRPAINTRGN;
-
-  typedef struct tagEMRFILLRGN {
-    EMR emr;
-    RECTL rclBounds;
-    DWORD cbRgnData;
-    DWORD ihBrush;
-    BYTE RgnData[1];
-  } EMRFILLRGN,*PEMRFILLRGN;
-
-  typedef struct tagEMRFRAMERGN {
-    EMR emr;
-    RECTL rclBounds;
-    DWORD cbRgnData;
-    DWORD ihBrush;
-    SIZEL szlStroke;
-    BYTE RgnData[1];
-  } EMRFRAMERGN,*PEMRFRAMERGN;
-
-  typedef struct tagEMREXTSELECTCLIPRGN {
-    EMR emr;
-    DWORD cbRgnData;
-    DWORD iMode;
-    BYTE RgnData[1];
-  } EMREXTSELECTCLIPRGN,*PEMREXTSELECTCLIPRGN;
-
-  typedef struct tagEMREXTTEXTOUTA {
-    EMR emr;
-    RECTL rclBounds;
-    DWORD iGraphicsMode;
-    FLOAT exScale;
-    FLOAT eyScale;
-    EMRTEXT emrtext;
-  } EMREXTTEXTOUTA,*PEMREXTTEXTOUTA,EMREXTTEXTOUTW,*PEMREXTTEXTOUTW;
-
-  typedef struct tagEMRPOLYTEXTOUTA {
-    EMR emr;
-    RECTL rclBounds;
-    DWORD iGraphicsMode;
-    FLOAT exScale;
-    FLOAT eyScale;
-    LONG cStrings;
-    EMRTEXT aemrtext[1];
-  } EMRPOLYTEXTOUTA,*PEMRPOLYTEXTOUTA,EMRPOLYTEXTOUTW,*PEMRPOLYTEXTOUTW;
-
-  typedef struct tagEMRBITBLT {
-    EMR emr;
-    RECTL rclBounds;
-    LONG xDest;
-    LONG yDest;
-    LONG cxDest;
-    LONG cyDest;
-    DWORD dwRop;
-    LONG xSrc;
-    LONG ySrc;
-    XFORM xformSrc;
-    COLORREF crBkColorSrc;
-    DWORD iUsageSrc;
-    DWORD offBmiSrc;
-    DWORD cbBmiSrc;
-    DWORD offBitsSrc;
-    DWORD cbBitsSrc;
-  } EMRBITBLT,*PEMRBITBLT;
-
-  typedef struct tagEMRSTRETCHBLT {
-    EMR emr;
-    RECTL rclBounds;
-    LONG xDest;
-    LONG yDest;
-    LONG cxDest;
-    LONG cyDest;
-    DWORD dwRop;
-    LONG xSrc;
-    LONG ySrc;
-    XFORM xformSrc;
-    COLORREF crBkColorSrc;
-    DWORD iUsageSrc;
-    DWORD offBmiSrc;
-    DWORD cbBmiSrc;
-    DWORD offBitsSrc;
-    DWORD cbBitsSrc;
-    LONG cxSrc;
-    LONG cySrc;
-  } EMRSTRETCHBLT,*PEMRSTRETCHBLT;
-
-  typedef struct tagEMRMASKBLT {
-    EMR emr;
-    RECTL rclBounds;
-    LONG xDest;
-    LONG yDest;
-    LONG cxDest;
-    LONG cyDest;
-    DWORD dwRop;
-    LONG xSrc;
-    LONG ySrc;
-    XFORM xformSrc;
-    COLORREF crBkColorSrc;
-    DWORD iUsageSrc;
-    DWORD offBmiSrc;
-    DWORD cbBmiSrc;
-    DWORD offBitsSrc;
-    DWORD cbBitsSrc;
-    LONG xMask;
-    LONG yMask;
-    DWORD iUsageMask;
-    DWORD offBmiMask;
-    DWORD cbBmiMask;
-    DWORD offBitsMask;
-    DWORD cbBitsMask;
-  } EMRMASKBLT,*PEMRMASKBLT;
-
-  typedef struct tagEMRPLGBLT {
-    EMR emr;
-    RECTL rclBounds;
-    POINTL aptlDest[3];
-    LONG xSrc;
-    LONG ySrc;
-    LONG cxSrc;
-    LONG cySrc;
-    XFORM xformSrc;
-    COLORREF crBkColorSrc;
-    DWORD iUsageSrc;
-    DWORD offBmiSrc;
-    DWORD cbBmiSrc;
-    DWORD offBitsSrc;
-    DWORD cbBitsSrc;
-    LONG xMask;
-    LONG yMask;
-    DWORD iUsageMask;
-    DWORD offBmiMask;
-    DWORD cbBmiMask;
-    DWORD offBitsMask;
-    DWORD cbBitsMask;
-  } EMRPLGBLT,*PEMRPLGBLT;
-
-  typedef struct tagEMRSETDIBITSTODEVICE {
-    EMR emr;
-    RECTL rclBounds;
-    LONG xDest;
-    LONG yDest;
-    LONG xSrc;
-    LONG ySrc;
-    LONG cxSrc;
-    LONG cySrc;
-    DWORD offBmiSrc;
-    DWORD cbBmiSrc;
-    DWORD offBitsSrc;
-    DWORD cbBitsSrc;
-    DWORD iUsageSrc;
-    DWORD iStartScan;
-    DWORD cScans;
-  } EMRSETDIBITSTODEVICE,*PEMRSETDIBITSTODEVICE;
-
-  typedef struct tagEMRSTRETCHDIBITS {
-    EMR emr;
-    RECTL rclBounds;
-    LONG xDest;
-    LONG yDest;
-    LONG xSrc;
-    LONG ySrc;
-    LONG cxSrc;
-    LONG cySrc;
-    DWORD offBmiSrc;
-    DWORD cbBmiSrc;
-    DWORD offBitsSrc;
-    DWORD cbBitsSrc;
-    DWORD iUsageSrc;
-    DWORD dwRop;
-    LONG cxDest;
-    LONG cyDest;
-  } EMRSTRETCHDIBITS,*PEMRSTRETCHDIBITS;
-
-  typedef struct tagEMREXTCREATEFONTINDIRECTW {
-    EMR emr;
-    DWORD ihFont;
-    EXTLOGFONTW elfw;
-  } EMREXTCREATEFONTINDIRECTW,*PEMREXTCREATEFONTINDIRECTW;
-
-  typedef struct tagEMRCREATEPALETTE {
-    EMR emr;
-    DWORD ihPal;
-    LOGPALETTE lgpl;
-  } EMRCREATEPALETTE,*PEMRCREATEPALETTE;
-
-  typedef struct tagEMRCREATEPEN {
-    EMR emr;
-    DWORD ihPen;
-    LOGPEN lopn;
-  } EMRCREATEPEN,*PEMRCREATEPEN;
-
-  typedef struct tagEMREXTCREATEPEN {
-    EMR emr;
-    DWORD ihPen;
-    DWORD offBmi;
-    DWORD cbBmi;
-    DWORD offBits;
-    DWORD cbBits;
-    EXTLOGPEN elp;
-  } EMREXTCREATEPEN,*PEMREXTCREATEPEN;
-
-  typedef struct tagEMRCREATEBRUSHINDIRECT {
-    EMR emr;
-    DWORD ihBrush;
-    LOGBRUSH32 lb;
-  } EMRCREATEBRUSHINDIRECT,*PEMRCREATEBRUSHINDIRECT;
-
-  typedef struct tagEMRCREATEMONOBRUSH {
-    EMR emr;
-    DWORD ihBrush;
-    DWORD iUsage;
-    DWORD offBmi;
-    DWORD cbBmi;
-    DWORD offBits;
-    DWORD cbBits;
-  } EMRCREATEMONOBRUSH,*PEMRCREATEMONOBRUSH;
-
-  typedef struct tagEMRCREATEDIBPATTERNBRUSHPT {
-    EMR emr;
-    DWORD ihBrush;
-    DWORD iUsage;
-    DWORD offBmi;
-    DWORD cbBmi;
-    DWORD offBits;
-    DWORD cbBits;
-  } EMRCREATEDIBPATTERNBRUSHPT,*PEMRCREATEDIBPATTERNBRUSHPT;
-
-  typedef struct tagEMRFORMAT {
-    DWORD dSignature;
-    DWORD nVersion;
-    DWORD cbData;
-    DWORD offData;
-  } EMRFORMAT,*PEMRFORMAT;
-
-  typedef struct tagEMRGLSRECORD {
-    EMR emr;
-    DWORD cbData;
-    BYTE Data[1];
-  } EMRGLSRECORD,*PEMRGLSRECORD;
-
-  typedef struct tagEMRGLSBOUNDEDRECORD {
-    EMR emr;
-    RECTL rclBounds;
-    DWORD cbData;
-    BYTE Data[1];
-  } EMRGLSBOUNDEDRECORD,*PEMRGLSBOUNDEDRECORD;
-
-  typedef struct tagEMRPIXELFORMAT {
-    EMR emr;
-    PIXELFORMATDESCRIPTOR pfd;
-  } EMRPIXELFORMAT,*PEMRPIXELFORMAT;
-
-  typedef struct tagEMRCREATECOLORSPACE {
-    EMR emr;
-    DWORD ihCS;
-    LOGCOLORSPACEA lcs;
-  } EMRCREATECOLORSPACE,*PEMRCREATECOLORSPACE;
-
-  typedef struct tagEMRSETCOLORSPACE {
-    EMR emr;
-    DWORD ihCS;
-  } EMRSETCOLORSPACE,*PEMRSETCOLORSPACE,EMRSELECTCOLORSPACE,*PEMRSELECTCOLORSPACE,EMRDELETECOLORSPACE,*PEMRDELETECOLORSPACE;
-
-  typedef struct tagEMREXTESCAPE {
-    EMR emr;
-    INT iEscape;
-    INT cbEscData;
-    BYTE EscData[1];
-  } EMREXTESCAPE,*PEMREXTESCAPE,EMRDRAWESCAPE,*PEMRDRAWESCAPE;
-
-  typedef struct tagEMRNAMEDESCAPE {
-    EMR emr;
-    INT iEscape;
-    INT cbDriver;
-    INT cbEscData;
-    BYTE EscData[1];
-  } EMRNAMEDESCAPE,*PEMRNAMEDESCAPE;
-
-#define SETICMPROFILE_EMBEDED 0x00000001
-
-  typedef struct tagEMRSETICMPROFILE {
-    EMR emr;
-    DWORD dwFlags;
-    DWORD cbName;
-    DWORD cbData;
-    BYTE Data[1];
-  } EMRSETICMPROFILE,*PEMRSETICMPROFILE,EMRSETICMPROFILEA,*PEMRSETICMPROFILEA,EMRSETICMPROFILEW,*PEMRSETICMPROFILEW;
-
-#define CREATECOLORSPACE_EMBEDED 0x00000001
-
-  typedef struct tagEMRCREATECOLORSPACEW {
-    EMR emr;
-    DWORD ihCS;
-    LOGCOLORSPACEW lcs;
-    DWORD dwFlags;
-    DWORD cbData;
-    BYTE Data[1];
-  } EMRCREATECOLORSPACEW,*PEMRCREATECOLORSPACEW;
-
-#define COLORMATCHTOTARGET_EMBEDED 0x00000001
-
-  typedef struct tagCOLORMATCHTOTARGET {
-    EMR emr;
-    DWORD dwAction;
-    DWORD dwFlags;
-    DWORD cbName;
-    DWORD cbData;
-    BYTE Data[1];
-  } EMRCOLORMATCHTOTARGET,*PEMRCOLORMATCHTOTARGET;
-
-  typedef struct tagCOLORCORRECTPALETTE {
-    EMR emr;
-    DWORD ihPalette;
-    DWORD nFirstEntry;
-    DWORD nPalEntries;
-    DWORD nReserved;
-  } EMRCOLORCORRECTPALETTE,*PEMRCOLORCORRECTPALETTE;
-
-  typedef struct tagEMRALPHABLEND {
-    EMR emr;
-    RECTL rclBounds;
-    LONG xDest;
-    LONG yDest;
-    LONG cxDest;
-    LONG cyDest;
-    DWORD dwRop;
-    LONG xSrc;
-    LONG ySrc;
-    XFORM xformSrc;
-    COLORREF crBkColorSrc;
-    DWORD iUsageSrc;
-    DWORD offBmiSrc;
-    DWORD cbBmiSrc;
-    DWORD offBitsSrc;
-    DWORD cbBitsSrc;
-    LONG cxSrc;
-    LONG cySrc;
-  } EMRALPHABLEND,*PEMRALPHABLEND;
-
-  typedef struct tagEMRGRADIENTFILL {
-    EMR emr;
-    RECTL rclBounds;
-    DWORD nVer;
-    DWORD nTri;
-    ULONG ulMode;
-    TRIVERTEX Ver[1];
-  } EMRGRADIENTFILL,*PEMRGRADIENTFILL;
-
-  typedef struct tagEMRTRANSPARENTBLT {
-    EMR emr;
-    RECTL rclBounds;
-    LONG xDest;
-    LONG yDest;
-    LONG cxDest;
-    LONG cyDest;
-    DWORD dwRop;
-    LONG xSrc;
-    LONG ySrc;
-    XFORM xformSrc;
-    COLORREF crBkColorSrc;
-    DWORD iUsageSrc;
-    DWORD offBmiSrc;
-    DWORD cbBmiSrc;
-    DWORD offBitsSrc;
-    DWORD cbBitsSrc;
-    LONG cxSrc;
-    LONG cySrc;
-  } EMRTRANSPARENTBLT,*PEMRTRANSPARENTBLT;
-
-#define GDICOMMENT_IDENTIFIER 0x43494447
-#define GDICOMMENT_WINDOWS_METAFILE 0x80000001
-#define GDICOMMENT_BEGINGROUP 0x00000002
-#define GDICOMMENT_ENDGROUP 0x00000003
-#define GDICOMMENT_MULTIFORMATS 0x40000004
-#define EPS_SIGNATURE 0x46535045
-#define GDICOMMENT_UNICODE_STRING 0x00000040
-#define GDICOMMENT_UNICODE_END 0x00000080
-#endif
-
-#ifdef UNICODE
-#define wglUseFontBitmaps wglUseFontBitmapsW
-#else
-#define wglUseFontBitmaps wglUseFontBitmapsA
-#endif
-
-  WINGDIAPI WINBOOL WINAPI wglCopyContext(HGLRC,HGLRC,UINT);
-  WINGDIAPI HGLRC WINAPI wglCreateContext(HDC);
-  WINGDIAPI HGLRC WINAPI wglCreateLayerContext(HDC,int);
-  WINGDIAPI WINBOOL WINAPI wglDeleteContext(HGLRC);
-  WINGDIAPI HGLRC WINAPI wglGetCurrentContext(VOID);
-  WINGDIAPI HDC WINAPI wglGetCurrentDC(VOID);
-  WINGDIAPI PROC WINAPI wglGetProcAddress(LPCSTR);
-  WINGDIAPI WINBOOL WINAPI wglMakeCurrent(HDC,HGLRC);
-  WINGDIAPI WINBOOL WINAPI wglShareLists(HGLRC,HGLRC);
-  WINGDIAPI WINBOOL WINAPI wglUseFontBitmapsA(HDC,DWORD,DWORD,DWORD);
-  WINGDIAPI WINBOOL WINAPI wglUseFontBitmapsW(HDC,DWORD,DWORD,DWORD);
-  WINGDIAPI WINBOOL WINAPI SwapBuffers(HDC);
-
-  typedef struct _POINTFLOAT {
-    FLOAT x;
-    FLOAT y;
-  } POINTFLOAT,*PPOINTFLOAT;
-
-  typedef struct _GLYPHMETRICSFLOAT {
-    FLOAT gmfBlackBoxX;
-    FLOAT gmfBlackBoxY;
-    POINTFLOAT gmfptGlyphOrigin;
-    FLOAT gmfCellIncX;
-    FLOAT gmfCellIncY;
-  } GLYPHMETRICSFLOAT,*PGLYPHMETRICSFLOAT,*LPGLYPHMETRICSFLOAT;
-
-#define WGL_FONT_LINES 0
-#define WGL_FONT_POLYGONS 1
-
-#ifdef UNICODE
-#define wglUseFontOutlines wglUseFontOutlinesW
-#else
-#define wglUseFontOutlines wglUseFontOutlinesA
-#endif
-
-  WINGDIAPI WINBOOL WINAPI wglUseFontOutlinesA(HDC,DWORD,DWORD,DWORD,FLOAT,FLOAT,int,LPGLYPHMETRICSFLOAT);
-  WINGDIAPI WINBOOL WINAPI wglUseFontOutlinesW(HDC,DWORD,DWORD,DWORD,FLOAT,FLOAT,int,LPGLYPHMETRICSFLOAT);
-
-  typedef struct tagLAYERPLANEDESCRIPTOR {
-    WORD nSize;
-    WORD nVersion;
-    DWORD dwFlags;
-    BYTE iPixelType;
-    BYTE cColorBits;
-    BYTE cRedBits;
-    BYTE cRedShift;
-    BYTE cGreenBits;
-    BYTE cGreenShift;
-    BYTE cBlueBits;
-    BYTE cBlueShift;
-    BYTE cAlphaBits;
-    BYTE cAlphaShift;
-    BYTE cAccumBits;
-    BYTE cAccumRedBits;
-    BYTE cAccumGreenBits;
-    BYTE cAccumBlueBits;
-    BYTE cAccumAlphaBits;
-    BYTE cDepthBits;
-    BYTE cStencilBits;
-    BYTE cAuxBuffers;
-    BYTE iLayerPlane;
-    BYTE bReserved;
-    COLORREF crTransparent;
-  } LAYERPLANEDESCRIPTOR,*PLAYERPLANEDESCRIPTOR,*LPLAYERPLANEDESCRIPTOR;
-
-#define LPD_DOUBLEBUFFER 0x00000001
-#define LPD_STEREO 0x00000002
-#define LPD_SUPPORT_GDI 0x00000010
-#define LPD_SUPPORT_OPENGL 0x00000020
-#define LPD_SHARE_DEPTH 0x00000040
-#define LPD_SHARE_STENCIL 0x00000080
-#define LPD_SHARE_ACCUM 0x00000100
-#define LPD_SWAP_EXCHANGE 0x00000200
-#define LPD_SWAP_COPY 0x00000400
-#define LPD_TRANSPARENT 0x00001000
-
-#define LPD_TYPE_RGBA 0
-#define LPD_TYPE_COLORINDEX 1
-
-#define WGL_SWAP_MAIN_PLANE 0x00000001
-#define WGL_SWAP_OVERLAY1 0x00000002
-#define WGL_SWAP_OVERLAY2 0x00000004
-#define WGL_SWAP_OVERLAY3 0x00000008
-#define WGL_SWAP_OVERLAY4 0x00000010
-#define WGL_SWAP_OVERLAY5 0x00000020
-#define WGL_SWAP_OVERLAY6 0x00000040
-#define WGL_SWAP_OVERLAY7 0x00000080
-#define WGL_SWAP_OVERLAY8 0x00000100
-#define WGL_SWAP_OVERLAY9 0x00000200
-#define WGL_SWAP_OVERLAY10 0x00000400
-#define WGL_SWAP_OVERLAY11 0x00000800
-#define WGL_SWAP_OVERLAY12 0x00001000
-#define WGL_SWAP_OVERLAY13 0x00002000
-#define WGL_SWAP_OVERLAY14 0x00004000
-#define WGL_SWAP_OVERLAY15 0x00008000
-#define WGL_SWAP_UNDERLAY1 0x00010000
-#define WGL_SWAP_UNDERLAY2 0x00020000
-#define WGL_SWAP_UNDERLAY3 0x00040000
-#define WGL_SWAP_UNDERLAY4 0x00080000
-#define WGL_SWAP_UNDERLAY5 0x00100000
-#define WGL_SWAP_UNDERLAY6 0x00200000
-#define WGL_SWAP_UNDERLAY7 0x00400000
-#define WGL_SWAP_UNDERLAY8 0x00800000
-#define WGL_SWAP_UNDERLAY9 0x01000000
-#define WGL_SWAP_UNDERLAY10 0x02000000
-#define WGL_SWAP_UNDERLAY11 0x04000000
-#define WGL_SWAP_UNDERLAY12 0x08000000
-#define WGL_SWAP_UNDERLAY13 0x10000000
-#define WGL_SWAP_UNDERLAY14 0x20000000
-#define WGL_SWAP_UNDERLAY15 0x40000000
-
-  WINGDIAPI WINBOOL WINAPI wglDescribeLayerPlane(HDC,int,int,UINT,LPLAYERPLANEDESCRIPTOR);
-  WINGDIAPI int WINAPI wglSetLayerPaletteEntries(HDC,int,int,int,CONST COLORREF *);
-  WINGDIAPI int WINAPI wglGetLayerPaletteEntries(HDC,int,int,int,COLORREF *);
-  WINGDIAPI WINBOOL WINAPI wglRealizeLayerPalette(HDC,int,WINBOOL);
-  WINGDIAPI WINBOOL WINAPI wglSwapLayerBuffers(HDC,UINT);
-
-  typedef struct _WGLSWAP {
-    HDC hdc;
-    UINT uiFlags;
-  } WGLSWAP,*PWGLSWAP,*LPWGLSWAP;
-
-#define WGL_SWAPMULTIPLE_MAX 16
-
-  WINGDIAPI DWORD WINAPI wglSwapMultipleBuffers(UINT,CONST WGLSWAP *);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/tinyc/win32/include/winapi/winnt.h b/tinyc/win32/include/winapi/winnt.h
deleted file mode 100644
index 4cf685d2a..000000000
--- a/tinyc/win32/include/winapi/winnt.h
+++ /dev/null
@@ -1,5835 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _WINNT_
-#define _WINNT_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <ctype.h>
-#define ANYSIZE_ARRAY 1
-
-//gr #include <specstrings.h>
-
-#define RESTRICTED_POINTER
-
-#ifndef __CRT_UNALIGNED
-#define __CRT_UNALIGNED
-#endif
-
-#if defined(__ia64__) || defined(__x86_64)
-#define UNALIGNED __CRT_UNALIGNED
-#ifdef _WIN64
-#define UNALIGNED64 __CRT_UNALIGNED
-#else
-#define UNALIGNED64
-#endif
-#else
-#define UNALIGNED
-#define UNALIGNED64
-#endif
-
-#if !defined(I_X86_) && !defined(_IA64_) && !defined(_AMD64_) && (defined(_X86_) && !defined(__x86_64))
-#define I_X86_
-#endif
-
-#if !defined(I_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(__x86_64)
-#define _AMD64_
-#endif
-
-#if !defined(I_X86_) && !(defined(_X86_) && !defined(__x86_64)) && !defined(_AMD64_) && defined(__ia64__)
-#if !defined(_IA64_)
-#define _IA64_
-#endif
-#endif
-
-
-#ifdef _WIN64
-#define MAX_NATURAL_ALIGNMENT sizeof(ULONGLONG)
-#define MEMORY_ALLOCATION_ALIGNMENT 16
-#else
-#define MAX_NATURAL_ALIGNMENT sizeof(DWORD)
-#define MEMORY_ALLOCATION_ALIGNMENT 8
-#endif
-
-#ifdef __cplusplus
-#define TYPE_ALIGNMENT(t) __alignof__ (t)
-#else
-#define TYPE_ALIGNMENT(t) FIELD_OFFSET(struct { char x; t test; },test)
-#endif
-
-#ifdef _WIN64
-#ifdef _AMD64_
-#define PROBE_ALIGNMENT(_s) TYPE_ALIGNMENT(DWORD)
-#elif defined(_IA64_)
-#define PROBE_ALIGNMENT(_s) (TYPE_ALIGNMENT(_s) > TYPE_ALIGNMENT(DWORD) ? TYPE_ALIGNMENT(_s) : TYPE_ALIGNMENT(DWORD))
-#else
-#error No Target Architecture
-#endif
-#define PROBE_ALIGNMENT32(_s) TYPE_ALIGNMENT(DWORD)
-#else
-#define PROBE_ALIGNMENT(_s) TYPE_ALIGNMENT(DWORD)
-#endif
-
-#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1]
-
-#include <basetsd.h>
-
-#if defined(_X86_) || defined(__ia64__) || defined(__x86_64)
-#define DECLSPEC_IMPORT __declspec(dllimport)
-#else
-#define DECLSPEC_IMPORT
-#endif
-
-#ifndef DECLSPEC_NORETURN
-#define DECLSPEC_NORETURN __declspec(noreturn)
-#endif
-
-#ifndef DECLSPEC_ALIGN
-#define DECLSPEC_ALIGN(x) __attribute__ ((aligned(x)))
-#endif
-
-#ifndef SYSTEM_CACHE_ALIGNMENT_SIZE
-#if defined(_AMD64_) || defined(I_X86_)
-#define SYSTEM_CACHE_ALIGNMENT_SIZE 64
-#else
-#define SYSTEM_CACHE_ALIGNMENT_SIZE 128
-#endif
-#endif
-
-#ifndef DECLSPEC_CACHEALIGN
-#define DECLSPEC_CACHEALIGN DECLSPEC_ALIGN(SYSTEM_CACHE_ALIGNMENT_SIZE)
-#endif
-
-#ifndef DECLSPEC_UUID
-#define DECLSPEC_UUID(x)
-#endif
-
-#ifndef DECLSPEC_NOVTABLE
-#define DECLSPEC_NOVTABLE
-#endif
-
-#ifndef DECLSPEC_SELECTANY
-#define DECLSPEC_SELECTANY __declspec(selectany)
-#endif
-
-#ifndef NOP_FUNCTION
-#define NOP_FUNCTION (void)0
-#endif
-
-#ifndef DECLSPEC_NOINLINE
-#define DECLSPEC_NOINLINE
-#endif
-
-#ifndef FORCEINLINE
-#define FORCEINLINE static __inline__
-#endif
-
-#ifndef DECLSPEC_DEPRECATED
-#define DECLSPEC_DEPRECATED __declspec(deprecated)
-#define DEPRECATE_SUPPORTED
-#endif
-
-#define DECLSPEC_DEPRECATED_DDK
-#define PRAGMA_DEPRECATED_DDK 0
-
-  typedef void *PVOID;
-  typedef void *PVOID64;
-
-#define NTAPI __stdcall
-#define NTSYSAPI DECLSPEC_IMPORT
-#define NTSYSCALLAPI DECLSPEC_IMPORT
-
-#ifndef VOID
-#define VOID void
-  typedef char CHAR;
-  typedef short SHORT;
-  typedef long LONG;
-#endif
-
-  typedef wchar_t WCHAR;
-  typedef WCHAR *PWCHAR,*LPWCH,*PWCH;
-  typedef CONST WCHAR *LPCWCH,*PCWCH;
-  typedef WCHAR *NWPSTR,*LPWSTR,*PWSTR;
-  typedef PWSTR *PZPWSTR;
-  typedef CONST PWSTR *PCZPWSTR;
-  typedef WCHAR UNALIGNED *LPUWSTR,*PUWSTR;
-  typedef CONST WCHAR *LPCWSTR,*PCWSTR;
-  typedef PCWSTR *PZPCWSTR;
-  typedef CONST WCHAR UNALIGNED *LPCUWSTR,*PCUWSTR;
-  typedef CHAR *PCHAR,*LPCH,*PCH;
-  typedef CONST CHAR *LPCCH,*PCCH;
-  typedef CHAR *NPSTR,*LPSTR,*PSTR;
-  typedef PSTR *PZPSTR;
-  typedef CONST PSTR *PCZPSTR;
-  typedef CONST CHAR *LPCSTR,*PCSTR;
-  typedef PCSTR *PZPCSTR;
-
-#ifdef UNICODE
-#ifndef _TCHAR_DEFINED
-#define _TCHAR_DEFINED
-  typedef WCHAR TCHAR,*PTCHAR;
-  typedef WCHAR TBYTE ,*PTBYTE;
-#endif
-
-  typedef LPWSTR LPTCH,PTCH;
-  typedef LPWSTR PTSTR,LPTSTR;
-  typedef LPCWSTR PCTSTR,LPCTSTR;
-  typedef LPUWSTR PUTSTR,LPUTSTR;
-  typedef LPCUWSTR PCUTSTR,LPCUTSTR;
-  typedef LPWSTR LP;
-#define __TEXT(quote) L##quote
-#else
-#ifndef _TCHAR_DEFINED
-#define _TCHAR_DEFINED
-  typedef char TCHAR,*PTCHAR;
-  typedef unsigned char TBYTE ,*PTBYTE;
-#endif
-
-  typedef LPSTR LPTCH,PTCH;
-  typedef LPSTR PTSTR,LPTSTR,PUTSTR,LPUTSTR;
-  typedef LPCSTR PCTSTR,LPCTSTR,PCUTSTR,LPCUTSTR;
-#define __TEXT(quote) quote
-#endif
-
-#define TEXT(quote) __TEXT(quote)
-
-  typedef SHORT *PSHORT;
-  typedef LONG *PLONG;
-
-  typedef void *HANDLE;
-#define DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name
-  typedef HANDLE *PHANDLE;
-
-  typedef BYTE FCHAR;
-  typedef WORD FSHORT;
-  typedef DWORD FLONG;
-
-#ifndef _HRESULT_DEFINED
-#define _HRESULT_DEFINED
-  typedef LONG HRESULT;
-#endif
-
-#ifdef __cplusplus
-#define EXTERN_C extern "C"
-#else
-#define EXTERN_C extern
-#endif
-
-#define STDMETHODCALLTYPE WINAPI
-#define STDMETHODVCALLTYPE __cdecl
-#define STDAPICALLTYPE WINAPI
-#define STDAPIVCALLTYPE __cdecl
-#define STDAPI EXTERN_C HRESULT WINAPI
-#define STDAPI_(type) EXTERN_C type WINAPI
-#define STDMETHODIMP HRESULT WINAPI
-#define STDMETHODIMP_(type) type WINAPI
-#define STDAPIV EXTERN_C HRESULT STDAPIVCALLTYPE
-#define STDAPIV_(type) EXTERN_C type STDAPIVCALLTYPE
-#define STDMETHODIMPV HRESULT STDMETHODVCALLTYPE
-#define STDMETHODIMPV_(type) type STDMETHODVCALLTYPE
-
-  typedef char CCHAR;
-#ifndef _LCID_DEFINED
-#define _LCID_DEFINED
-typedef DWORD LCID;
-#endif
-  typedef PDWORD PLCID;
-#ifndef _LANGID_DEFINED
-#define _LANGID_DEFINED
-  typedef WORD LANGID;
-#endif
-#define APPLICATION_ERROR_MASK 0x20000000
-#define ERROR_SEVERITY_SUCCESS 0x00000000
-#define ERROR_SEVERITY_INFORMATIONAL 0x40000000
-#define ERROR_SEVERITY_WARNING 0x80000000
-#define ERROR_SEVERITY_ERROR 0xC0000000
-
-#ifdef __ia64__
-  __declspec(align(16))
-#endif
-    typedef struct _FLOAT128 {
-      __int64 LowPart;
-      __int64 HighPart;
-  } FLOAT128;
-
-  typedef FLOAT128 *PFLOAT128;
-
-#define _ULONGLONG_
-#if((!(defined(_X86_) && !defined(__x86_64)) || (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 64)))
-  typedef __int64 LONGLONG;
-  typedef unsigned __int64 ULONGLONG;
-
-#define MAXLONGLONG (0x7fffffffffffffff)
-#else
-
-  typedef double LONGLONG;
-  typedef double ULONGLONG;
-#endif
-
-  typedef LONGLONG *PLONGLONG;
-  typedef ULONGLONG *PULONGLONG;
-
-  typedef LONGLONG USN;
-
-  typedef union _LARGE_INTEGER {
-    struct {
-      DWORD LowPart;
-      LONG HighPart;
-    };
-    struct {
-      DWORD LowPart;
-      LONG HighPart;
-    } u;
-    LONGLONG QuadPart;
-  } LARGE_INTEGER;
-
-  typedef LARGE_INTEGER *PLARGE_INTEGER;
-
-  typedef union _ULARGE_INTEGER {
-    struct {
-      DWORD LowPart;
-      DWORD HighPart;
-    };
-    struct {
-      DWORD LowPart;
-      DWORD HighPart;
-    } u;
-    ULONGLONG QuadPart;
-  } ULARGE_INTEGER;
-
-  typedef ULARGE_INTEGER *PULARGE_INTEGER;
-
-  typedef struct _LUID {
-    DWORD LowPart;
-    LONG HighPart;
-  } LUID,*PLUID;
-
-#define _DWORDLONG_
-  typedef ULONGLONG DWORDLONG;
-  typedef DWORDLONG *PDWORDLONG;
-
-#ifdef RC_INVOKED
-#define Int32x32To64(a,b) ((LONGLONG)((LONG)(a)) *(LONGLONG)((LONG)(b)))
-#define UInt32x32To64(a,b) ((ULONGLONG)((DWORD)(a)) *(ULONGLONG)((DWORD)(b)))
-#define Int64ShrlMod32(a,b) ((ULONGLONG)(a) >> (b))
-#elif (defined(_X86_) && !defined(__x86_64))
-#define Int32x32To64(a,b) (LONGLONG)((LONGLONG)(LONG)(a) *(LONG)(b))
-#define UInt32x32To64(a,b) (ULONGLONG)((ULONGLONG)(DWORD)(a) *(DWORD)(b))
-#define Int64ShrlMod32(a,b) ((DWORDLONG)(a)>>(b))
-#elif defined(__ia64__) || defined(__x86_64)
-#define Int32x32To64(a,b) ((LONGLONG)((LONG)(a)) *(LONGLONG)((LONG)(b)))
-#define UInt32x32To64(a,b) ((ULONGLONG)((DWORD)(a)) *(ULONGLONG)((DWORD)(b)))
-#define Int64ShrlMod32(a,b) ((ULONGLONG)(a) >> (b))
-#else
-#error Must define a target architecture.
-#endif
-
-#define Int64ShraMod32(a,b) ((LONGLONG)(a) >> (b))
-#define Int64ShllMod32(a,b) ((ULONGLONG)(a) << (b))
-
-#ifdef __cplusplus
-  extern "C" {
-#endif
-
-#ifdef __x86_64
-
-#define RotateLeft8 _rotl8
-#define RotateLeft16 _rotl16
-#define RotateRight8 _rotr8
-#define RotateRight16 _rotr16
-
-    unsigned char __cdecl _rotl8(unsigned char Value,unsigned char Shift);
-    unsigned short __cdecl _rotl16(unsigned short Value,unsigned char Shift);
-    unsigned char __cdecl _rotr8(unsigned char Value,unsigned char Shift);
-    unsigned short __cdecl _rotr16(unsigned short Value,unsigned char Shift);
-#endif
-
-#define RotateLeft32 _rotl
-#define RotateLeft64 _rotl64
-#define RotateRight32 _rotr
-#define RotateRight64 _rotr64
-
-    unsigned int __cdecl _rotl(unsigned int Value,int Shift);
-    unsigned __int64 __cdecl _rotl64(unsigned __int64 Value,int Shift);
-    unsigned int __cdecl _rotr(unsigned int Value,int Shift);
-    unsigned __int64 __cdecl _rotr64(unsigned __int64 Value,int Shift);
-#ifdef __cplusplus
-  }
-#endif
-
-#define ANSI_NULL ((CHAR)0)
-#define UNICODE_NULL ((WCHAR)0)
-#define UNICODE_STRING_MAX_BYTES ((WORD) 65534)
-#define UNICODE_STRING_MAX_CHARS (32767)
-
-#ifndef _BOOLEAN_
-#define _BOOLEAN_
-  typedef BYTE BOOLEAN;
-#endif
-  typedef BOOLEAN *PBOOLEAN;
-
-  typedef struct _LIST_ENTRY {
-    struct _LIST_ENTRY *Flink;
-    struct _LIST_ENTRY *Blink;
-  } LIST_ENTRY,*PLIST_ENTRY,*RESTRICTED_POINTER PRLIST_ENTRY;
-
-  typedef struct _SINGLE_LIST_ENTRY {
-    struct _SINGLE_LIST_ENTRY *Next;
-  } SINGLE_LIST_ENTRY,*PSINGLE_LIST_ENTRY;
-
-  typedef struct LIST_ENTRY32 {
-    DWORD Flink;
-    DWORD Blink;
-  } LIST_ENTRY32;
-  typedef LIST_ENTRY32 *PLIST_ENTRY32;
-
-  typedef struct LIST_ENTRY64 {
-    ULONGLONG Flink;
-    ULONGLONG Blink;
-  } LIST_ENTRY64;
-  typedef LIST_ENTRY64 *PLIST_ENTRY64;
-
-#include <guiddef.h>
-
-#ifndef __OBJECTID_DEFINED
-#define __OBJECTID_DEFINED
-  typedef struct _OBJECTID {
-    GUID Lineage;
-    DWORD Uniquifier;
-  } OBJECTID;
-#endif
-
-#define MINCHAR 0x80
-#define MAXCHAR 0x7f
-#define MINSHORT 0x8000
-#define MAXSHORT 0x7fff
-#define MINLONG 0x80000000
-#define MAXLONG 0x7fffffff
-#define MAXBYTE 0xff
-#define MAXWORD 0xffff
-#define MAXDWORD 0xffffffff
-
-#define FIELD_OFFSET(type,field) ((LONG)(LONG_PTR)&(((type *)0)->field))
-#define RTL_FIELD_SIZE(type,field) (sizeof(((type *)0)->field))
-#define RTL_SIZEOF_THROUGH_FIELD(type,field) (FIELD_OFFSET(type,field) + RTL_FIELD_SIZE(type,field))
-#define RTL_CONTAINS_FIELD(Struct,Size,Field) ((((PCHAR)(&(Struct)->Field)) + sizeof((Struct)->Field)) <= (((PCHAR)(Struct))+(Size)))
-#define RTL_NUMBER_OF_V1(A) (sizeof(A)/sizeof((A)[0]))
-#define RTL_NUMBER_OF_V2(A) RTL_NUMBER_OF_V1(A)
-
-#ifdef ENABLE_RTL_NUMBER_OF_V2
-#define RTL_NUMBER_OF(A) RTL_NUMBER_OF_V2(A)
-#else
-#define RTL_NUMBER_OF(A) RTL_NUMBER_OF_V1(A)
-#endif
-
-#define ARRAYSIZE(A) RTL_NUMBER_OF_V2(A)
-#define _ARRAYSIZE(A) RTL_NUMBER_OF_V1(A)
-
-#define RTL_FIELD_TYPE(type,field) (((type*)0)->field)
-#define RTL_NUMBER_OF_FIELD(type,field) (RTL_NUMBER_OF(RTL_FIELD_TYPE(type,field)))
-#define RTL_PADDING_BETWEEN_FIELDS(T,F1,F2) ((FIELD_OFFSET(T,F2) > FIELD_OFFSET(T,F1)) ? (FIELD_OFFSET(T,F2) - FIELD_OFFSET(T,F1) - RTL_FIELD_SIZE(T,F1)) : (FIELD_OFFSET(T,F1) - FIELD_OFFSET(T,F2) - RTL_FIELD_SIZE(T,F2)))
-
-#ifdef __cplusplus
-#define RTL_CONST_CAST(type) const_cast<type>
-#else
-#define RTL_CONST_CAST(type) (type)
-#endif
-
-#define RTL_BITS_OF(sizeOfArg) (sizeof(sizeOfArg) *8)
-#define RTL_BITS_OF_FIELD(type,field) (RTL_BITS_OF(RTL_FIELD_TYPE(type,field)))
-#define CONTAINING_RECORD(address,type,field) ((type *)((PCHAR)(address) - (ULONG_PTR)(&((type *)0)->field)))
-
-#define VER_SERVER_NT 0x80000000
-#define VER_WORKSTATION_NT 0x40000000
-#define VER_SUITE_SMALLBUSINESS 0x00000001
-#define VER_SUITE_ENTERPRISE 0x00000002
-#define VER_SUITE_BACKOFFICE 0x00000004
-#define VER_SUITE_COMMUNICATIONS 0x00000008
-#define VER_SUITE_TERMINAL 0x00000010
-#define VER_SUITE_SMALLBUSINESS_RESTRICTED 0x00000020
-#define VER_SUITE_EMBEDDEDNT 0x00000040
-#define VER_SUITE_DATACENTER 0x00000080
-#define VER_SUITE_SINGLEUSERTS 0x00000100
-#define VER_SUITE_PERSONAL 0x00000200
-#define VER_SUITE_BLADE 0x00000400
-#define VER_SUITE_EMBEDDED_RESTRICTED 0x00000800
-#define VER_SUITE_SECURITY_APPLIANCE 0x00001000
-#define VER_SUITE_STORAGE_SERVER 0x00002000
-#define VER_SUITE_COMPUTE_SERVER 0x00004000
-
-#define PRODUCT_UNDEFINED                       0x0
-
-#define PRODUCT_ULTIMATE                        0x1
-#define PRODUCT_HOME_BASIC                      0x2
-#define PRODUCT_HOME_PREMIUM                    0x3
-#define PRODUCT_ENTERPRISE                      0x4
-#define PRODUCT_HOME_BASIC_N                    0x5
-#define PRODUCT_BUSINESS                        0x6
-#define PRODUCT_STANDARD_SERVER                 0x7
-#define PRODUCT_DATACENTER_SERVER               0x8
-#define PRODUCT_SMALLBUSINESS_SERVER            0x9
-#define PRODUCT_ENTERPRISE_SERVER               0xa
-#define PRODUCT_STARTER                         0xb
-#define PRODUCT_DATACENTER_SERVER_CORE          0xc
-#define PRODUCT_STANDARD_SERVER_CORE            0xd
-#define PRODUCT_ENTERPRISE_SERVER_CORE          0xe
-#define PRODUCT_ENTERPRISE_SERVER_IA64          0xf
-#define PRODUCT_BUSINESS_N                      0x10
-#define PRODUCT_WEB_SERVER                      0x11
-#define PRODUCT_CLUSTER_SERVER                  0x12
-#define PRODUCT_HOME_SERVER                     0x13
-#define PRODUCT_STORAGE_EXPRESS_SERVER          0x14
-#define PRODUCT_STORAGE_STANDARD_SERVER         0x15
-#define PRODUCT_STORAGE_WORKGROUP_SERVER        0x16
-#define PRODUCT_STORAGE_ENTERPRISE_SERVER       0x17
-#define PRODUCT_SERVER_FOR_SMALLBUSINESS        0x18
-#define PRODUCT_SMALLBUSINESS_SERVER_PREMIUM    0x19
-
-#define PRODUCT_UNLICENSED                      0xabcdabcd
-
-#define LANG_NEUTRAL 0x00
-#define LANG_INVARIANT 0x7f
-
-#define LANG_AFRIKAANS 0x36
-#define LANG_ALBANIAN 0x1c
-#define LANG_ALSATIAN 0x84
-#define LANG_AMHARIC 0x5e
-#define LANG_ARABIC 0x01
-#define LANG_ARMENIAN 0x2b
-#define LANG_ASSAMESE 0x4d
-#define LANG_AZERI 0x2c
-#define LANG_BASHKIR 0x6d
-#define LANG_BASQUE 0x2d
-#define LANG_BELARUSIAN 0x23
-#define LANG_BENGALI 0x45
-#define LANG_BRETON 0x7e
-#define LANG_BOSNIAN 0x1a
-#define LANG_BOSNIAN_NEUTRAL 0x781a
-#define LANG_BULGARIAN 0x02
-#define LANG_CATALAN 0x03
-#define LANG_CHINESE 0x04
-#define LANG_CHINESE_SIMPLIFIED 0x04
-#define LANG_CHINESE_TRADITIONAL 0x7c04
-#define LANG_CORSICAN 0x83
-#define LANG_CROATIAN 0x1a
-#define LANG_CZECH 0x05
-#define LANG_DANISH 0x06
-#define LANG_DARI 0x8c
-#define LANG_DIVEHI 0x65
-#define LANG_DUTCH 0x13
-#define LANG_ENGLISH 0x09
-#define LANG_ESTONIAN 0x25
-#define LANG_FAEROESE 0x38
-#define LANG_FARSI 0x29
-#define LANG_FILIPINO 0x64
-#define LANG_FINNISH 0x0b
-#define LANG_FRENCH 0x0c
-#define LANG_FRISIAN 0x62
-#define LANG_GALICIAN 0x56
-#define LANG_GEORGIAN 0x37
-#define LANG_GERMAN 0x07
-#define LANG_GREEK 0x08
-#define LANG_GREENLANDIC 0x6f
-#define LANG_GUJARATI 0x47
-#define LANG_HAUSA 0x68
-#define LANG_HEBREW 0x0d
-#define LANG_HINDI 0x39
-#define LANG_HUNGARIAN 0x0e
-#define LANG_ICELANDIC 0x0f
-#define LANG_IGBO 0x70
-#define LANG_INDONESIAN 0x21
-#define LANG_INUKTITUT 0x5d
-#define LANG_IRISH 0x3c
-#define LANG_ITALIAN 0x10
-#define LANG_JAPANESE 0x11
-#define LANG_KANNADA 0x4b
-#define LANG_KASHMIRI 0x60
-#define LANG_KAZAK 0x3f
-#define LANG_KHMER 0x53
-#define LANG_KICHE 0x86
-#define LANG_KINYARWANDA 0x87
-#define LANG_KONKANI 0x57
-#define LANG_KOREAN 0x12
-#define LANG_KYRGYZ 0x40
-#define LANG_LAO 0x54
-#define LANG_LATVIAN 0x26
-#define LANG_LITHUANIAN 0x27
-#define LANG_LOWER_SORBIAN 0x2e
-#define LANG_LUXEMBOURGISH 0x6e
-#define LANG_MACEDONIAN 0x2f
-#define LANG_MALAY 0x3e
-#define LANG_MALAYALAM 0x4c
-#define LANG_MALTESE 0x3a
-#define LANG_MANIPURI 0x58
-#define LANG_MAORI 0x81
-#define LANG_MAPUDUNGUN 0x7a
-#define LANG_MARATHI 0x4e
-#define LANG_MOHAWK 0x7c
-#define LANG_MONGOLIAN 0x50
-#define LANG_NEPALI 0x61
-#define LANG_NORWEGIAN 0x14
-#define LANG_OCCITAN 0x82
-#define LANG_ORIYA 0x48
-#define LANG_PASHTO 0x63
-#define LANG_PERSIAN 0x29
-#define LANG_POLISH 0x15
-#define LANG_PORTUGUESE 0x16
-#define LANG_PUNJABI 0x46
-#define LANG_QUECHUA 0x6b
-#define LANG_ROMANIAN 0x18
-#define LANG_RUSSIAN 0x19
-#define LANG_SAMI 0x3b
-#define LANG_ROMANSH 0x17
-#define LANG_SANSKRIT 0x4f
-#define LANG_SERBIAN 0x1a
-#define LANG_SERBIAN_NEUTRAL 0x7c1a
-#define LANG_SINDHI 0x59
-#define LANG_SINHALESE 0x5b
-#define LANG_SLOVAK 0x1b
-#define LANG_SLOVENIAN 0x24
-#define LANG_SOTHO 0x6c
-#define LANG_SPANISH 0x0a
-#define LANG_SWAHILI 0x41
-#define LANG_SWEDISH 0x1d
-#define LANG_SYRIAC 0x5a
-#define LANG_TAJIK 0x28
-#define LANG_TAMAZIGHT 0x5f
-#define LANG_TAMIL 0x49
-#define LANG_TATAR 0x44
-#define LANG_TELUGU 0x4a
-#define LANG_THAI 0x1e
-#define LANG_TIBETAN 0x51
-#define LANG_TIGRIGNA 0x73
-#define LANG_TSWANA 0x32
-#define LANG_TURKISH 0x1f
-#define LANG_TURKMEN 0x42
-#define LANG_UIGHUR 0x80
-#define LANG_UKRAINIAN 0x22
-#define LANG_UPPER_SORBIAN 0x2e
-#define LANG_URDU 0x20
-#define LANG_UZBEK 0x43
-#define LANG_VIETNAMESE 0x2a
-#define LANG_WELSH 0x52
-#define LANG_WOLOF 0x88
-#define LANG_XHOSA 0x34
-#define LANG_YAKUT 0x85
-#define LANG_YI 0x78
-#define LANG_YORUBA 0x6a
-#define LANG_ZULU 0x35
-
-#define SUBLANG_NEUTRAL 0x0
-#define SUBLANG_DEFAULT 0x1
-#define SUBLANG_SYS_DEFAULT 0x2
-#define SUBLANG_CUSTOM_DEFAULT 0x3
-#define SUBLANG_CUSTOM_UNSPECIFIED 0x4
-#define SUBLANG_UI_CUSTOM_DEFAULT 0x5
-
-#define SUBLANG_ARABIC_SAUDI_ARABIA 0x01
-#define SUBLANG_ARABIC_IRAQ 0x02
-#define SUBLANG_ARABIC_EGYPT 0x03
-#define SUBLANG_ARABIC_LIBYA 0x04
-#define SUBLANG_ARABIC_ALGERIA 0x05
-#define SUBLANG_ARABIC_MOROCCO 0x06
-#define SUBLANG_ARABIC_TUNISIA 0x07
-#define SUBLANG_ARABIC_OMAN 0x08
-#define SUBLANG_ARABIC_YEMEN 0x09
-#define SUBLANG_ARABIC_SYRIA 0x0a
-#define SUBLANG_ARABIC_JORDAN 0x0b
-#define SUBLANG_ARABIC_LEBANON 0x0c
-#define SUBLANG_ARABIC_KUWAIT 0x0d
-#define SUBLANG_ARABIC_UAE 0x0e
-#define SUBLANG_ARABIC_BAHRAIN 0x0f
-#define SUBLANG_ARABIC_QATAR 0x10
-#define SUBLANG_AZERI_LATIN 0x01
-#define SUBLANG_AZERI_CYRILLIC 0x02
-#define SUBLANG_CHINESE_TRADITIONAL 0x01
-#define SUBLANG_CHINESE_SIMPLIFIED 0x02
-#define SUBLANG_CHINESE_HONGKONG 0x03
-#define SUBLANG_CHINESE_SINGAPORE 0x04
-#define SUBLANG_CHINESE_MACAU 0x05
-#define SUBLANG_DUTCH 0x01
-#define SUBLANG_DUTCH_BELGIAN 0x02
-#define SUBLANG_ENGLISH_US 0x01
-#define SUBLANG_ENGLISH_UK 0x02
-#define SUBLANG_ENGLISH_AUS 0x03
-#define SUBLANG_ENGLISH_CAN 0x04
-#define SUBLANG_ENGLISH_NZ 0x05
-#define SUBLANG_ENGLISH_EIRE 0x06
-#define SUBLANG_ENGLISH_SOUTH_AFRICA 0x07
-#define SUBLANG_ENGLISH_JAMAICA 0x08
-#define SUBLANG_ENGLISH_CARIBBEAN 0x09
-#define SUBLANG_ENGLISH_BELIZE 0x0a
-#define SUBLANG_ENGLISH_TRINIDAD 0x0b
-#define SUBLANG_ENGLISH_ZIMBABWE 0x0c
-#define SUBLANG_ENGLISH_PHILIPPINES 0x0d
-#define SUBLANG_FRENCH 0x01
-#define SUBLANG_FRENCH_BELGIAN 0x02
-#define SUBLANG_FRENCH_CANADIAN 0x03
-#define SUBLANG_FRENCH_SWISS 0x04
-#define SUBLANG_FRENCH_LUXEMBOURG 0x05
-#define SUBLANG_FRENCH_MONACO 0x06
-#define SUBLANG_GERMAN 0x01
-#define SUBLANG_GERMAN_SWISS 0x02
-#define SUBLANG_GERMAN_AUSTRIAN 0x03
-#define SUBLANG_GERMAN_LUXEMBOURG 0x04
-#define SUBLANG_GERMAN_LIECHTENSTEIN 0x05
-#define SUBLANG_ITALIAN 0x01
-#define SUBLANG_ITALIAN_SWISS 0x02
-#define SUBLANG_KASHMIRI_SASIA 0x02
-#define SUBLANG_KASHMIRI_INDIA 0x02
-#define SUBLANG_KOREAN 0x01
-#define SUBLANG_LITHUANIAN 0x01
-#define SUBLANG_MALAY_MALAYSIA 0x01
-#define SUBLANG_MALAY_BRUNEI_DARUSSALAM 0x02
-#define SUBLANG_NEPALI_INDIA 0x02
-#define SUBLANG_NORWEGIAN_BOKMAL 0x01
-#define SUBLANG_NORWEGIAN_NYNORSK 0x02
-#define SUBLANG_PORTUGUESE 0x02
-#define SUBLANG_PORTUGUESE_BRAZILIAN 0x01
-#define SUBLANG_SERBIAN_LATIN 0x02
-#define SUBLANG_SERBIAN_CYRILLIC 0x03
-#define SUBLANG_SPANISH 0x01
-#define SUBLANG_SPANISH_MEXICAN 0x02
-#define SUBLANG_SPANISH_MODERN 0x03
-#define SUBLANG_SPANISH_GUATEMALA 0x04
-#define SUBLANG_SPANISH_COSTA_RICA 0x05
-#define SUBLANG_SPANISH_PANAMA 0x06
-#define SUBLANG_SPANISH_DOMINICAN_REPUBLIC 0x07
-#define SUBLANG_SPANISH_VENEZUELA 0x08
-#define SUBLANG_SPANISH_COLOMBIA 0x09
-#define SUBLANG_SPANISH_PERU 0x0a
-#define SUBLANG_SPANISH_ARGENTINA 0x0b
-#define SUBLANG_SPANISH_ECUADOR 0x0c
-#define SUBLANG_SPANISH_CHILE 0x0d
-#define SUBLANG_SPANISH_URUGUAY 0x0e
-#define SUBLANG_SPANISH_PARAGUAY 0x0f
-#define SUBLANG_SPANISH_BOLIVIA 0x10
-#define SUBLANG_SPANISH_EL_SALVADOR 0x11
-#define SUBLANG_SPANISH_HONDURAS 0x12
-#define SUBLANG_SPANISH_NICARAGUA 0x13
-#define SUBLANG_SPANISH_PUERTO_RICO 0x14
-#define SUBLANG_SWEDISH 0x01
-#define SUBLANG_SWEDISH_FINLAND 0x02
-#define SUBLANG_URDU_PAKISTAN 0x01
-#define SUBLANG_URDU_INDIA 0x02
-#define SUBLANG_UZBEK_LATIN 0x01
-#define SUBLANG_UZBEK_CYRILLIC 0x02
-
-#define SORT_DEFAULT 0x0
-#define SORT_INVARIANT_MATH 0x1
-
-#define SORT_JAPANESE_XJIS 0x0
-#define SORT_JAPANESE_UNICODE 0x1
-#define SORT_JAPANESE_RADICALSTROKE 0x4
-
-#define SORT_CHINESE_BIG5 0x0
-#define SORT_CHINESE_PRCP 0x0
-#define SORT_CHINESE_UNICODE 0x1
-#define SORT_CHINESE_PRC 0x2
-#define SORT_CHINESE_BOPOMOFO 0x3
-
-#define SORT_KOREAN_KSC 0x0
-#define SORT_KOREAN_UNICODE 0x1
-
-#define SORT_GERMAN_PHONE_BOOK 0x1
-
-#define SORT_HUNGARIAN_DEFAULT 0x0
-#define SORT_HUNGARIAN_TECHNICAL 0x1
-
-#define SORT_GEORGIAN_TRADITIONAL 0x0
-#define SORT_GEORGIAN_MODERN 0x1
-
-#define MAKELANGID(p,s) ((((WORD)(s)) << 10) | (WORD)(p))
-#define PRIMARYLANGID(lgid) ((WORD)(lgid) & 0x3ff)
-#define SUBLANGID(lgid) ((WORD)(lgid) >> 10)
-
-#define NLS_VALID_LOCALE_MASK 0x000fffff
-
-#define MAKELCID(lgid,srtid) ((DWORD)((((DWORD)((WORD)(srtid))) << 16) | ((DWORD)((WORD)(lgid)))))
-#define MAKESORTLCID(lgid,srtid,ver) ((DWORD)((MAKELCID(lgid,srtid)) | (((DWORD)((WORD)(ver))) << 20)))
-#define LANGIDFROMLCID(lcid) ((WORD)(lcid))
-#define SORTIDFROMLCID(lcid) ((WORD)((((DWORD)(lcid)) >> 16) & 0xf))
-#define SORTVERSIONFROMLCID(lcid) ((WORD)((((DWORD)(lcid)) >> 20) & 0xf))
-
-#define LOCALE_NAME_MAX_LENGTH 85
-#define LANG_SYSTEM_DEFAULT (MAKELANGID(LANG_NEUTRAL,SUBLANG_SYS_DEFAULT))
-#define LANG_USER_DEFAULT (MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT))
-
-#define LOCALE_SYSTEM_DEFAULT (MAKELCID(LANG_SYSTEM_DEFAULT,SORT_DEFAULT))
-#define LOCALE_USER_DEFAULT (MAKELCID(LANG_USER_DEFAULT,SORT_DEFAULT))
-
-#define LOCALE_NEUTRAL (MAKELCID(MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL),SORT_DEFAULT))
-
-#define LOCALE_CUSTOM_DEFAULT (MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_DEFAULT), SORT_DEFAULT))
-#define LOCALE_CUSTOM_UNSPECIFIED (MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_CUSTOM_UNSPECIFIED), SORT_DEFAULT))
-#define LOCALE_CUSTOM_UI_DEFAULT (MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_UI_CUSTOM_DEFAULT), SORT_DEFAULT))
-
-#define LOCALE_INVARIANT (MAKELCID(MAKELANGID(LANG_INVARIANT,SUBLANG_NEUTRAL),SORT_DEFAULT))
-
-#define UNREFERENCED_PARAMETER(P) (P)
-#define DBG_UNREFERENCED_PARAMETER(P) (P)
-#define DBG_UNREFERENCED_LOCAL_VARIABLE(V) (V)
-
-#define DEFAULT_UNREACHABLE
-
-#ifndef WIN32_NO_STATUS
-#define STATUS_WAIT_0 ((DWORD)0x00000000L)
-#define STATUS_ABANDONED_WAIT_0 ((DWORD)0x00000080L)
-#define STATUS_USER_APC ((DWORD)0x000000C0L)
-#define STATUS_TIMEOUT ((DWORD)0x00000102L)
-#define STATUS_PENDING ((DWORD)0x00000103L)
-#define DBG_EXCEPTION_HANDLED ((DWORD)0x00010001L)
-#define DBG_CONTINUE ((DWORD)0x00010002L)
-#define STATUS_SEGMENT_NOTIFICATION ((DWORD)0x40000005L)
-#define DBG_TERMINATE_THREAD ((DWORD)0x40010003L)
-#define DBG_TERMINATE_PROCESS ((DWORD)0x40010004L)
-#define DBG_CONTROL_C ((DWORD)0x40010005L)
-#define DBG_CONTROL_BREAK ((DWORD)0x40010008L)
-#define DBG_COMMAND_EXCEPTION ((DWORD)0x40010009L)
-#define STATUS_GUARD_PAGE_VIOLATION ((DWORD)0x80000001L)
-#define STATUS_DATATYPE_MISALIGNMENT ((DWORD)0x80000002L)
-#define STATUS_BREAKPOINT ((DWORD)0x80000003L)
-#define STATUS_SINGLE_STEP ((DWORD)0x80000004L)
-#define DBG_EXCEPTION_NOT_HANDLED ((DWORD)0x80010001L)
-#define STATUS_ACCESS_VIOLATION ((DWORD)0xC0000005L)
-#define STATUS_IN_PAGE_ERROR ((DWORD)0xC0000006L)
-#define STATUS_INVALID_HANDLE ((DWORD)0xC0000008L)
-#define STATUS_NO_MEMORY ((DWORD)0xC0000017L)
-#define STATUS_ILLEGAL_INSTRUCTION ((DWORD)0xC000001DL)
-#define STATUS_NONCONTINUABLE_EXCEPTION ((DWORD)0xC0000025L)
-#define STATUS_INVALID_DISPOSITION ((DWORD)0xC0000026L)
-#define STATUS_ARRAY_BOUNDS_EXCEEDED ((DWORD)0xC000008CL)
-#define STATUS_FLOAT_DENORMAL_OPERAND ((DWORD)0xC000008DL)
-#define STATUS_FLOAT_DIVIDE_BY_ZERO ((DWORD)0xC000008EL)
-#define STATUS_FLOAT_INEXACT_RESULT ((DWORD)0xC000008FL)
-#define STATUS_FLOAT_INVALID_OPERATION ((DWORD)0xC0000090L)
-#define STATUS_FLOAT_OVERFLOW ((DWORD)0xC0000091L)
-#define STATUS_FLOAT_STACK_CHECK ((DWORD)0xC0000092L)
-#define STATUS_FLOAT_UNDERFLOW ((DWORD)0xC0000093L)
-#define STATUS_INTEGER_DIVIDE_BY_ZERO ((DWORD)0xC0000094L)
-#define STATUS_INTEGER_OVERFLOW ((DWORD)0xC0000095L)
-#define STATUS_PRIVILEGED_INSTRUCTION ((DWORD)0xC0000096L)
-#define STATUS_STACK_OVERFLOW ((DWORD)0xC00000FDL)
-#define STATUS_CONTROL_C_EXIT ((DWORD)0xC000013AL)
-#define STATUS_FLOAT_MULTIPLE_FAULTS ((DWORD)0xC00002B4L)
-#define STATUS_FLOAT_MULTIPLE_TRAPS ((DWORD)0xC00002B5L)
-#define STATUS_REG_NAT_CONSUMPTION ((DWORD)0xC00002C9L)
-#define STATUS_SXS_EARLY_DEACTIVATION ((DWORD)0xC015000FL)
-#define STATUS_SXS_INVALID_DEACTIVATION ((DWORD)0xC0150010L)
-#endif
-
-#define MAXIMUM_WAIT_OBJECTS 64
-#define MAXIMUM_SUSPEND_COUNT MAXCHAR
-
-  typedef ULONG_PTR KSPIN_LOCK;
-  typedef KSPIN_LOCK *PKSPIN_LOCK;
-
-#ifdef _AMD64_
-
-#if defined(__x86_64) && !defined(RC_INVOKED)
-
-#ifdef __cplusplus
-  extern "C" {
-#endif
-
-#define BitTest _bittest
-#define BitTestAndComplement _bittestandcomplement
-#define BitTestAndSet _bittestandset
-#define BitTestAndReset _bittestandreset
-#define InterlockedBitTestAndSet _interlockedbittestandset
-#define InterlockedBitTestAndReset _interlockedbittestandreset
-#define BitTest64 _bittest64
-#define BitTestAndComplement64 _bittestandcomplement64
-#define BitTestAndSet64 _bittestandset64
-#define BitTestAndReset64 _bittestandreset64
-#define InterlockedBitTestAndSet64 _interlockedbittestandset64
-#define InterlockedBitTestAndReset64 _interlockedbittestandreset64
-
-    __CRT_INLINE BOOLEAN _bittest(LONG const *Base,LONG Offset) {
-      int old = 0;
-      __asm__ __volatile__("btl %2,%1\n\tsbbl %0,%0 "
-	:"=r" (old),"=m" ((*(volatile long *) Base))
-	:"Ir" (Offset));
-      return (BOOLEAN) (old!=0);
-    }
-    __CRT_INLINE BOOLEAN _bittestandcomplement(LONG *Base,LONG Offset) {
-      int old = 0;
-      __asm__ __volatile__("btcl %2,%1\n\tsbbl %0,%0 "
-	:"=r" (old),"=m" ((*(volatile long *) Base))
-	:"Ir" (Offset));
-      return (BOOLEAN) (old!=0);
-    }
-    __CRT_INLINE BOOLEAN InterlockedBitTestAndComplement(LONG *Base,LONG Bit) {
-      int old = 0;
-      __asm__ __volatile__("lock ; btcl %2,%1\n\tsbbl %0,%0 "
-	:"=r" (old),"=m" ((*(volatile long *) Base))
-	:"Ir" (Bit));
-      return (BOOLEAN) (old!=0);
-    }
-    __CRT_INLINE BOOLEAN _bittestandset(LONG *Base,LONG Offset) {
-      int old = 0;
-      __asm__ __volatile__("btsl %2,%1\n\tsbbl %0,%0 "
-	:"=r" (old),"=m" ((*(volatile long *) Base))
-	:"Ir" (Offset));
-      return (BOOLEAN) (old!=0);
-    }
-    __CRT_INLINE BOOLEAN _bittestandreset(LONG *Base,LONG Offset) {
-      int old = 0;
-      __asm__ __volatile__("btrl %2,%1\n\tsbbl %0,%0 "
-	:"=r" (old),"=m" ((*(volatile long *) Base))
-	:"Ir" (Offset));
-      return (BOOLEAN) (old!=0);
-    }
-    __CRT_INLINE BOOLEAN _interlockedbittestandset(LONG *Base,LONG Offset) {
-      int old = 0;
-      __asm__ __volatile__("lock ; btsl %2,%1\n\tsbbl %0,%0 "
-	:"=r" (old),"=m" ((*(volatile long *) Base))
-	:"Ir" (Offset));
-      return (BOOLEAN) (old!=0);
-    }
-    __CRT_INLINE BOOLEAN _interlockedbittestandreset(LONG *Base,LONG Offset) {
-      int old = 0;
-      __asm__ __volatile__("lock ; btrl %2,%1\n\tsbbl %0,%0 "
-	:"=r" (old),"=m" ((*(volatile long *) Base))
-	:"Ir" (Offset));
-      return (BOOLEAN) (old!=0);
-    }
-    __CRT_INLINE BOOLEAN _bittest64(LONG64 const *Base,LONG64 Offset) {
-      int old = 0;
-      __asm__ __volatile__("btq %2,%1\n\tsbbl %0,%0 "
-	:"=r" (old),"=m" ((*(volatile long long *) Base))
-	:"Ir" (Offset));
-      return (BOOLEAN) (old!=0);
-    }
-    __CRT_INLINE BOOLEAN _bittestandcomplement64(LONG64 *Base,LONG64 Offset) {
-      int old = 0;
-      __asm__ __volatile__("btcq %2,%1\n\tsbbl %0,%0 "
-	:"=r" (old),"=m" ((*(volatile long long *) Base))
-	:"Ir" (Offset));
-      return (BOOLEAN) (old!=0);
-    }
-    __CRT_INLINE BOOLEAN _bittestandset64(LONG64 *Base,LONG64 Offset) {
-      int old = 0;
-      __asm__ __volatile__("btsq %2,%1\n\tsbbl %0,%0 "
-	:"=r" (old),"=m" ((*(volatile long long *) Base))
-	:"Ir" (Offset));
-      return (BOOLEAN) (old!=0);
-    }
-    __CRT_INLINE BOOLEAN _bittestandreset64(LONG64 *Base,LONG64 Offset) {
-      int old = 0;
-      __asm__ __volatile__("btrq %2,%1\n\tsbbl %0,%0 "
-	:"=r" (old),"=m" ((*(volatile long long *) Base))
-	:"Ir" (Offset));
-      return (BOOLEAN) (old!=0);
-    }
-    __CRT_INLINE BOOLEAN _interlockedbittestandset64(LONG64 *Base,LONG64 Offset) {
-      int old = 0;
-      __asm__ __volatile__("lock ; btsq %2,%1\n\tsbbl %0,%0 "
-	:"=r" (old),"=m" ((*(volatile long long *) Base))
-	:"Ir" (Offset));
-      return (BOOLEAN) (old!=0);
-    }
-    __CRT_INLINE BOOLEAN _interlockedbittestandreset64(LONG64 *Base,LONG64 Offset) {
-      int old = 0;
-      __asm__ __volatile__("lock ; btrq %2,%1\n\tsbbl %0,%0 "
-	:"=r" (old),"=m" ((*(volatile long long *) Base))
-	:"Ir" (Offset));
-      return (BOOLEAN) (old!=0);
-    }
-#define BitScanForward _BitScanForward
-#define BitScanReverse _BitScanReverse
-#define BitScanForward64 _BitScanForward64
-#define BitScanReverse64 _BitScanReverse64
-
-    __CRT_INLINE BOOLEAN _BitScanForward(DWORD *Index,DWORD Mask) {
-      __asm__ __volatile__("bsfl %1,%0" : "=r" (Mask),"=m" ((*(volatile long *)Index)));
-      return Mask!=0;
-    }
-    __CRT_INLINE BOOLEAN _BitScanReverse(DWORD *Index,DWORD Mask) {
-      __asm__ __volatile__("bsrl %1,%0" : "=r" (Mask),"=m" ((*(volatile long *)Index)));
-      return Mask!=0;
-    }
-    __CRT_INLINE BOOLEAN _BitScanForward64(DWORD *Index,DWORD64 Mask) {
-      __asm__ __volatile__("bsfq %1,%0" : "=r" (Mask),"=m" ((*(volatile long long *)Index)));
-      return Mask!=0;
-    }
-    __CRT_INLINE BOOLEAN _BitScanReverse64(DWORD *Index,DWORD64 Mask) {
-      __asm__ __volatile__("bsrq %1,%0" : "=r" (Mask),"=m" ((*(volatile long long *)Index)));
-      return Mask!=0;
-    }
-
-#define InterlockedIncrement16 _InterlockedIncrement16
-#define InterlockedDecrement16 _InterlockedDecrement16
-#define InterlockedCompareExchange16 _InterlockedCompareExchange16
-
-#define InterlockedAnd _InterlockedAnd
-#define InterlockedOr _InterlockedOr
-#define InterlockedXor _InterlockedXor
-#define InterlockedIncrement _InterlockedIncrement
-#define InterlockedIncrementAcquire InterlockedIncrement
-#define InterlockedIncrementRelease InterlockedIncrement
-#define InterlockedDecrement _InterlockedDecrement
-#define InterlockedDecrementAcquire InterlockedDecrement
-#define InterlockedDecrementRelease InterlockedDecrement
-#define InterlockedAdd _InterlockedAdd
-#define InterlockedExchange _InterlockedExchange
-#define InterlockedExchangeAdd _InterlockedExchangeAdd
-#define InterlockedCompareExchange _InterlockedCompareExchange
-#define InterlockedCompareExchangeAcquire InterlockedCompareExchange
-#define InterlockedCompareExchangeRelease InterlockedCompareExchange
-
-#define InterlockedAnd64 _InterlockedAnd64
-#define InterlockedAndAffinity InterlockedAnd64
-#define InterlockedOr64 _InterlockedOr64
-#define InterlockedOrAffinity InterlockedOr64
-#define InterlockedXor64 _InterlockedXor64
-#define InterlockedIncrement64 _InterlockedIncrement64
-#define InterlockedDecrement64 _InterlockedDecrement64
-#define InterlockedAdd64 _InterlockedAdd64
-#define InterlockedExchange64 _InterlockedExchange64
-#define InterlockedExchangeAcquire64 InterlockedExchange64
-#define InterlockedExchangeAdd64 _InterlockedExchangeAdd64
-#define InterlockedCompareExchange64 _InterlockedCompareExchange64
-#define InterlockedCompareExchangeAcquire64 InterlockedCompareExchange64
-#define InterlockedCompareExchangeRelease64 InterlockedCompareExchange64
-
-#define InterlockedExchangePointer _InterlockedExchangePointer
-#define InterlockedCompareExchangePointer _InterlockedCompareExchangePointer
-#define InterlockedCompareExchangePointerAcquire _InterlockedCompareExchangePointer
-#define InterlockedCompareExchangePointerRelease _InterlockedCompareExchangePointer
-
-#define InterlockedExchangeAddSizeT(a,b) InterlockedExchangeAdd64((LONG64 *)a,b)
-#define InterlockedIncrementSizeT(a) InterlockedIncrement64((LONG64 *)a)
-#define InterlockedDecrementSizeT(a) InterlockedDecrement64((LONG64 *)a)
-
-    __CRT_INLINE SHORT InterlockedIncrement16(SHORT volatile *Addend) {
-      unsigned char c;
-      unsigned char s;
-      __asm__ __volatile__(
-	"lock ; addw $1,%0; sete %1 ; sets %2"
-	:"=m" (*Addend), "=qm" (c), "=qm" (s)
-	:"m" (*Addend) : "memory");
-      return (c != 0 ? 0 : (s != 0 ? -1 : 1));
-    }
-    __CRT_INLINE SHORT InterlockedDecrement16(SHORT volatile *Addend) {
-      unsigned char c;
-      unsigned char s;
-      __asm__ __volatile__(
-	"lock ; subw $1,%0; sete %1 ; sets %2"
-	:"=m" (*Addend), "=qm" (c), "=qm" (s)
-	:"m" (*Addend) : "memory");
-      return (c != 0 ? 0 : (s != 0 ? -1 : 1));
-    }
-    __CRT_INLINE SHORT InterlockedCompareExchange16(SHORT volatile *Destination,SHORT ExChange,SHORT Comperand) {
-      SHORT prev;
-      __asm__ __volatile__("lock ; cmpxchgw %w1,%2"
-	:"=a"(prev)
-	:"q"(ExChange), "m"(*Destination), "0"(Comperand)
-	: "memory");
-      return prev;
-    }
-    __CRT_INLINE LONG InterlockedAnd(LONG volatile *Destination,LONG Value) {
-      __asm__ __volatile__("lock ; andl %0,%1"
-	: :"r"(Value),"m"(*Destination)
-	: "memory");
-      return *Destination;
-    }
-    __CRT_INLINE LONG InterlockedOr(LONG volatile *Destination,LONG Value) {
-      __asm__ __volatile__("lock ; orl %0,%1"
-	: : "r"(Value),"m"(*Destination) : "memory");
-      return *Destination;
-    }
-    __CRT_INLINE LONG InterlockedXor(LONG volatile *Destination,LONG Value) {
-      __asm__ __volatile__("lock ; xorl %0,%1"
-	: : "r"(Value),"m"(*Destination) : "memory");
-      return *Destination;
-    }
-    //		$$$$
-    __CRT_INLINE LONG64 InterlockedAnd64(LONG64 volatile *Destination,LONG64 Value) {
-      __asm__ __volatile__("lock ; andq %0,%1"
-	: : "r"(Value),"m"(*Destination) : "memory");
-      return *Destination;
-    }
-    __CRT_INLINE LONG64 InterlockedOr64(LONG64 volatile *Destination,LONG64 Value) {
-      __asm__ __volatile__("lock ; orq %0,%1"
-	: : "r"(Value),"m"(*Destination) : "memory");
-      return *Destination;
-    }
-    __CRT_INLINE LONG64 InterlockedXor64(LONG64 volatile *Destination,LONG64 Value) {
-      __asm__ __volatile__("lock ; xorq %0,%1"
-	: : "r"(Value),"m"(*Destination) : "memory");
-      return *Destination;
-    }
-    __CRT_INLINE LONG InterlockedIncrement(LONG volatile *Addend) {
-      unsigned char c;
-      unsigned char s;
-      __asm__ __volatile__(
-	"lock ; addl $1,%0; sete %1 ; sets %2"
-	:"=m" (*Addend), "=qm" (c), "=qm" (s)
-	:"m" (*Addend) : "memory");
-      return (c != 0 ? 0 : (s != 0 ? -1 : 1));
-    }
-    __CRT_INLINE LONG InterlockedDecrement(LONG volatile *Addend) {
-      unsigned char c;
-      unsigned char s;
-      __asm__ __volatile__(
-	"lock ; subl $1,%0; sete %1 ; sets %2"
-	:"=m" (*Addend), "=qm" (c), "=qm" (s)
-	:"m" (*Addend) : "memory");
-      return (c != 0 ? 0 : (s != 0 ? -1 : 1));
-    }
-    __CRT_INLINE LONG InterlockedExchange(LONG volatile *Target,LONG Value) {
-      __asm__ __volatile("lock ; xchgl %0,%1"
-	: "=r"(Value)
-	: "m"(*Target),"0"(Value)
-	: "memory");
-      return Value;
-    }
-    LONG InterlockedExchangeAdd(LONG volatile *Addend,LONG Value);
-
-#ifndef _X86AMD64_
-    __CRT_INLINE LONG InterlockedAdd(LONG volatile *Addend,LONG Value) { return InterlockedExchangeAdd(Addend,Value) + Value; }
-#endif
-    __CRT_INLINE LONG InterlockedCompareExchange(LONG volatile *Destination,LONG ExChange,LONG Comperand) {
-      LONG prev;
-      __asm__ __volatile__("lock ; cmpxchgl %1,%2" : "=a" (prev) : "q" (ExChange),"m" (*Destination), "0" (Comperand) : "memory");
-      return prev;
-    }
-    __CRT_INLINE LONG64 InterlockedIncrement64(LONG64 volatile *Addend) {
-      unsigned char c;
-      unsigned char s;
-      __asm__ __volatile__(
-	"lock ; addq $1,%0; sete %1 ; sets %2"
-	:"=m" (*Addend), "=qm" (c), "=qm" (s)
-	:"m" (*Addend) : "memory");
-      return (c != 0 ? 0 : (s != 0 ? -1 : 1));
-    }
-    __CRT_INLINE LONG64 InterlockedDecrement64(LONG64 volatile *Addend) {
-      unsigned char c;
-      unsigned char s;
-      __asm__ __volatile__(
-	"lock ; subq $1,%0; sete %1 ; sets %2"
-	:"=m" (*Addend), "=qm" (c), "=qm" (s)
-	:"m" (*Addend) : "memory");
-      return (c != 0 ? 0 : (s != 0 ? -1 : 1));
-    }
-    __CRT_INLINE LONG64 InterlockedExchange64(LONG64 volatile *Target,LONG64 Value) {
-      __asm__ __volatile("lock ; xchgq %0,%1"
-	: "=r"(Value)
-	: "m"(*Target),"0"(Value)
-	: "memory");
-      return Value;
-    }
-    LONG64 InterlockedExchangeAdd64(LONG64 volatile *Addend,LONG64 Value);
-
-#ifndef _X86AMD64_
-    __CRT_INLINE LONG64 InterlockedAdd64(LONG64 volatile *Addend,LONG64 Value) { return InterlockedExchangeAdd64(Addend,Value) + Value; }
-#endif
-
-    __CRT_INLINE LONG64 InterlockedCompareExchange64(LONG64 volatile *Destination,LONG64 ExChange,LONG64 Comperand) {
-      LONG64 prev;
-      __asm__ __volatile__("lock ; cmpxchgq %1,%2" : "=a" (prev) : "q" (ExChange),"m" (*Destination), "0" (Comperand) : "memory");
-      return prev;
-    }
-    __CRT_INLINE PVOID InterlockedCompareExchangePointer(PVOID volatile *Destination,PVOID ExChange,PVOID Comperand) {
-      PVOID prev;
-      __asm__ __volatile__("lock ; cmpxchgq %1,%2" : "=a" (prev) : "q" (ExChange),"m" (*Destination), "0" (Comperand) : "memory");
-      return prev;
-    }
-    __CRT_INLINE PVOID InterlockedExchangePointer(PVOID volatile *Target,PVOID Value) {
-      __asm__ __volatile("lock ; xchgq %0,%1"
-	: "=r"(Value)
-	: "m"(*Target),"0"(Value)
-	: "memory");
-      return Value;
-    }
-
-#define CacheLineFlush(Address) _mm_clflush(Address)
-
-    VOID _ReadWriteBarrier(VOID);
-
-#define FastFence __faststorefence
-#define LoadFence _mm_lfence
-#define MemoryFence _mm_mfence
-#define StoreFence _mm_sfence
-
-    VOID __faststorefence(VOID);
-    VOID _m_prefetchw(volatile CONST VOID *Source);
-
-//!__TINYC__: #include <intrin.h>
-
-#define YieldProcessor _mm_pause
-#define MemoryBarrier __faststorefence
-#define PreFetchCacheLine(l,a) _mm_prefetch((CHAR CONST *) a,l)
-#define PrefetchForWrite(p) _m_prefetchw(p)
-#define ReadForWriteAccess(p) (_m_prefetchw(p),*(p))
-
-#define PF_TEMPORAL_LEVEL_1 _MM_HINT_T0
-#define PF_TEMPORAL_LEVEL_2 _MM_HINT_T1
-#define PF_TEMPORAL_LEVEL_3 _MM_HINT_T2
-#define PF_NON_TEMPORAL_LEVEL_ALL _MM_HINT_NTA
-
-#define ReadMxCsr _mm_getcsr
-#define WriteMxCsr _mm_setcsr
-
-    VOID __int2c(VOID);
-
-#define DbgRaiseAssertionFailure() __int2c()
-#define GetCallersEflags() __getcallerseflags()
-
-    unsigned __int32 __getcallerseflags(VOID);
-
-#define GetSegmentLimit __segmentlimit
-
-    DWORD __segmentlimit(DWORD Selector);
-
-#define ReadTimeStampCounter() __rdtsc()
-
-    DWORD64 __rdtsc(VOID);
-    VOID __movsb(PBYTE Destination,BYTE const *Source,SIZE_T Count);
-    VOID __movsw(PWORD Destination,WORD const *Source,SIZE_T Count);
-    VOID __movsd(PDWORD Destination,DWORD const *Source,SIZE_T Count);
-    VOID __movsq(PDWORD64 Destination,DWORD64 const *Source,SIZE_T Count);
-    VOID __stosb(PBYTE Destination,BYTE Value,SIZE_T Count);
-    VOID __stosw(PWORD Destination,WORD Value,SIZE_T Count);
-    VOID __stosd(PDWORD Destination,DWORD Value,SIZE_T Count);
-    VOID __stosq(PDWORD64 Destination,DWORD64 Value,SIZE_T Count);
-
-#define MultiplyHigh __mulh
-#define UnsignedMultiplyHigh __umulh
-
-    LONGLONG MultiplyHigh(LONGLONG Multiplier,LONGLONG Multiplicand);
-    ULONGLONG UnsignedMultiplyHigh(ULONGLONG Multiplier,ULONGLONG Multiplicand);
-
-#define ShiftLeft128 __shiftleft128
-#define ShiftRight128 __shiftright128
-
-    DWORD64 ShiftLeft128(DWORD64 LowPart,DWORD64 HighPart,BYTE Shift);
-    DWORD64 ShiftRight128(DWORD64 LowPart,DWORD64 HighPart,BYTE Shift);
-
-#define Multiply128 _mul128
-
-    LONG64 Multiply128(LONG64 Multiplier,LONG64 Multiplicand,LONG64 *HighProduct);
-
-#define UnsignedMultiply128 _umul128
-
-    DWORD64 UnsignedMultiply128(DWORD64 Multiplier,DWORD64 Multiplicand,DWORD64 *HighProduct);
-
-    __CRT_INLINE LONG64 MultiplyExtract128(LONG64 Multiplier,LONG64 Multiplicand,BYTE Shift) {
-      LONG64 extractedProduct;
-      LONG64 highProduct;
-      LONG64 lowProduct;
-      lowProduct = Multiply128(Multiplier,Multiplicand,&highProduct);
-      extractedProduct = (LONG64)ShiftRight128((LONG64)lowProduct,(LONG64)highProduct,Shift);
-      return extractedProduct;
-    }
-
-    __CRT_INLINE DWORD64 UnsignedMultiplyExtract128(DWORD64 Multiplier,DWORD64 Multiplicand,BYTE Shift) {
-      DWORD64 extractedProduct;
-      DWORD64 highProduct;
-      DWORD64 lowProduct;
-      lowProduct = UnsignedMultiply128(Multiplier,Multiplicand,&highProduct);
-      extractedProduct = ShiftRight128(lowProduct,highProduct,Shift);
-      return extractedProduct;
-    }
-
-    __CRT_INLINE BYTE __readgsbyte(DWORD Offset) {
-      BYTE ret;
-      __asm__ volatile ("movb	%%gs:%1,%0"
-	: "=r" (ret) ,"=m" ((*(volatile long *) (DWORD64) Offset)));
-      return ret;
-    }
-    __CRT_INLINE WORD __readgsword(DWORD Offset) {
-      WORD ret;
-      __asm__ volatile ("movw	%%gs:%1,%0"
-	: "=r" (ret) ,"=m" ((*(volatile long *) (DWORD64) Offset)));
-      return ret;
-    }
-    __CRT_INLINE DWORD __readgsdword(DWORD Offset) {
-      DWORD ret;
-      __asm__ volatile ("movl	%%gs:%1,%0"
-	: "=r" (ret) ,"=m" ((*(volatile long *) (DWORD64) Offset)));
-      return ret;
-    }
-    __CRT_INLINE DWORD64 __readgsqword(DWORD Offset) {
-      void *ret;
-      __asm__ volatile ("movq	%%gs:%1,%0"
-	: "=r" (ret) ,"=m" ((*(volatile long *) (DWORD64) Offset)));
-      return (DWORD64) ret;
-    }
-    __CRT_INLINE VOID __writegsbyte(DWORD Offset,BYTE Data) {
-      __asm__ volatile ("movb	%0,%%gs:%1"
-	: "=r" (Data) ,"=m" ((*(volatile long *) (DWORD64) Offset)));
-    }
-    __CRT_INLINE VOID __writegsword(DWORD Offset,WORD Data) {
-      __asm__ volatile ("movw	%0,%%gs:%1"
-	: "=r" (Data) ,"=m" ((*(volatile long *) (DWORD64) Offset)));
-    }
-    __CRT_INLINE VOID __writegsdword(DWORD Offset,DWORD Data) {
-      __asm__ volatile ("movl	%0,%%gs:%1"
-	: "=r" (Data) ,"=m" ((*(volatile long *) (DWORD64) Offset)));
-    }
-    __CRT_INLINE VOID __writegsqword(DWORD Offset,DWORD64 Data) {
-      __asm__ volatile ("movq	%0,%%gs:%1"
-	: "=r" (Data) ,"=m" ((*(volatile long *) (DWORD64) Offset)));
-    }
-
-#ifdef __cplusplus
-  }
-#endif
-#endif
-
-#define EXCEPTION_READ_FAULT 0
-#define EXCEPTION_WRITE_FAULT 1
-#define EXCEPTION_EXECUTE_FAULT 8
-
-#if !defined(RC_INVOKED)
-
-#define CONTEXT_AMD64 0x100000
-
-#define CONTEXT_CONTROL (CONTEXT_AMD64 | 0x1L)
-#define CONTEXT_INTEGER (CONTEXT_AMD64 | 0x2L)
-#define CONTEXT_SEGMENTS (CONTEXT_AMD64 | 0x4L)
-#define CONTEXT_FLOATING_POINT (CONTEXT_AMD64 | 0x8L)
-#define CONTEXT_DEBUG_REGISTERS (CONTEXT_AMD64 | 0x10L)
-
-#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT)
-#define CONTEXT_ALL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS)
-
-#define CONTEXT_EXCEPTION_ACTIVE 0x8000000
-#define CONTEXT_SERVICE_ACTIVE 0x10000000
-#define CONTEXT_EXCEPTION_REQUEST 0x40000000
-#define CONTEXT_EXCEPTION_REPORTING 0x80000000
-#endif
-
-#define INITIAL_MXCSR 0x1f80
-#define INITIAL_FPCSR 0x027f
-
-  typedef DECLSPEC_ALIGN(16) struct _M128A {
-    ULONGLONG Low;
-    LONGLONG High;
-  } M128A,*PM128A;
-
-  typedef struct _XMM_SAVE_AREA32 {
-    WORD ControlWord;
-    WORD StatusWord;
-    BYTE TagWord;
-    BYTE Reserved1;
-    WORD ErrorOpcode;
-    DWORD ErrorOffset;
-    WORD ErrorSelector;
-    WORD Reserved2;
-    DWORD DataOffset;
-    WORD DataSelector;
-    WORD Reserved3;
-    DWORD MxCsr;
-    DWORD MxCsr_Mask;
-    M128A FloatRegisters[8];
-    M128A XmmRegisters[16];
-    BYTE Reserved4[96];
-  } XMM_SAVE_AREA32,*PXMM_SAVE_AREA32;
-
-#define LEGACY_SAVE_AREA_LENGTH sizeof(XMM_SAVE_AREA32)
-
-  typedef DECLSPEC_ALIGN(16) struct _CONTEXT {
-    DWORD64 P1Home;
-    DWORD64 P2Home;
-    DWORD64 P3Home;
-    DWORD64 P4Home;
-    DWORD64 P5Home;
-    DWORD64 P6Home;
-    DWORD ContextFlags;
-    DWORD MxCsr;
-    WORD SegCs;
-    WORD SegDs;
-    WORD SegEs;
-    WORD SegFs;
-    WORD SegGs;
-    WORD SegSs;
-    DWORD EFlags;
-    DWORD64 Dr0;
-    DWORD64 Dr1;
-    DWORD64 Dr2;
-    DWORD64 Dr3;
-    DWORD64 Dr6;
-    DWORD64 Dr7;
-    DWORD64 Rax;
-    DWORD64 Rcx;
-    DWORD64 Rdx;
-    DWORD64 Rbx;
-    DWORD64 Rsp;
-    DWORD64 Rbp;
-    DWORD64 Rsi;
-    DWORD64 Rdi;
-    DWORD64 R8;
-    DWORD64 R9;
-    DWORD64 R10;
-    DWORD64 R11;
-    DWORD64 R12;
-    DWORD64 R13;
-    DWORD64 R14;
-    DWORD64 R15;
-    DWORD64 Rip;
-    union {
-      XMM_SAVE_AREA32 FltSave;
-      XMM_SAVE_AREA32 FloatSave;
-      struct {
-	M128A Header[2];
-	M128A Legacy[8];
-	M128A Xmm0;
-	M128A Xmm1;
-	M128A Xmm2;
-	M128A Xmm3;
-	M128A Xmm4;
-	M128A Xmm5;
-	M128A Xmm6;
-	M128A Xmm7;
-	M128A Xmm8;
-	M128A Xmm9;
-	M128A Xmm10;
-	M128A Xmm11;
-	M128A Xmm12;
-	M128A Xmm13;
-	M128A Xmm14;
-	M128A Xmm15;
-      };
-    };
-    M128A VectorRegister[26];
-    DWORD64 VectorControl;
-    DWORD64 DebugControl;
-    DWORD64 LastBranchToRip;
-    DWORD64 LastBranchFromRip;
-    DWORD64 LastExceptionToRip;
-    DWORD64 LastExceptionFromRip;
-  } CONTEXT,*PCONTEXT;
-
-#define RUNTIME_FUNCTION_INDIRECT 0x1
-
-  typedef struct _RUNTIME_FUNCTION {
-    DWORD BeginAddress;
-    DWORD EndAddress;
-    DWORD UnwindData;
-  } RUNTIME_FUNCTION,*PRUNTIME_FUNCTION;
-
-  typedef PRUNTIME_FUNCTION (*PGET_RUNTIME_FUNCTION_CALLBACK)(DWORD64 ControlPc,PVOID Context);
-  typedef DWORD (*POUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK)(HANDLE Process,PVOID TableAddress,PDWORD Entries,PRUNTIME_FUNCTION *Functions);
-
-#define OUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK_EXPORT_NAME "OutOfProcessFunctionTableCallback"
-
-  NTSYSAPI VOID __cdecl RtlRestoreContext (PCONTEXT ContextRecord,struct _EXCEPTION_RECORD *ExceptionRecord);
-  NTSYSAPI BOOLEAN __cdecl RtlAddFunctionTable(PRUNTIME_FUNCTION FunctionTable,DWORD EntryCount,DWORD64 BaseAddress);
-  NTSYSAPI BOOLEAN __cdecl RtlInstallFunctionTableCallback(DWORD64 TableIdentifier,DWORD64 BaseAddress,DWORD Length,PGET_RUNTIME_FUNCTION_CALLBACK Callback,PVOID Context,PCWSTR OutOfProcessCallbackDll);
-  NTSYSAPI BOOLEAN __cdecl RtlDeleteFunctionTable(PRUNTIME_FUNCTION FunctionTable);
-#endif
-
-#ifdef I_X86_
-#if(defined(_X86_) && !defined(__x86_64)) && !defined(RC_INVOKED)
-#ifdef __cplusplus
-  extern "C" {
-#endif
-
-    __CRT_INLINE BOOLEAN InterlockedBitTestAndSet(LONG *Base,LONG Bit) {
-      int old = 0;
-      __asm__ __volatile__("lock ; btsl %2,%1\n\tsbbl %0,%0 "
-	:"=r" (old),"=m" ((*(volatile long *) Base))
-	:"Ir" (Bit));
-      return (BOOLEAN) (old!=0);
-    }
-
-    __CRT_INLINE BOOLEAN InterlockedBitTestAndReset(LONG *Base,LONG Bit) {
-      int old = 0;
-      __asm__ __volatile__("lock ; btrl %2,%1\n\tsbbl %0,%0 "
-	:"=r" (old),"=m" ((*(volatile long *) Base))
-	:"Ir" (Bit));
-      return (BOOLEAN) (old!=0);
-    }
-
-    __CRT_INLINE BOOLEAN InterlockedBitTestAndComplement(LONG *Base,LONG Bit) {
-      int old = 0;
-      __asm__ __volatile__("lock ; btcl %2,%1\n\tsbbl %0,%0 "
-	:"=r" (old),"=m" ((*(volatile long *) Base))
-	:"Ir" (Bit));
-      return (BOOLEAN) (old!=0);
-    }
-
-#ifdef _PREFIX_
-    BYTE __readfsbyte(DWORD Offset);
-    WORD __readfsword(DWORD Offset);
-    DWORD __readfsdword(DWORD Offset);
-    VOID __writefsbyte(DWORD Offset,BYTE Data);
-    VOID __writefsword(DWORD Offset,WORD Data);
-    VOID __writefsdword(DWORD Offset,DWORD Data);
-#endif
-
-#ifdef __cplusplus
-  }
-#endif
-#endif
-
-#if(defined(_X86_) && !defined(__x86_64))
-  __CRT_INLINE VOID MemoryBarrier(VOID) {
-    LONG Barrier;
-    __asm__ __volatile__("xchgl %eax,%0 "
-      :"=r" (Barrier));
-  }
-#define YieldProcessor() __asm__ __volatile__("rep nop ");
-
-#define PreFetchCacheLine(l,a)
-#define ReadForWriteAccess(p) (*(p))
-
-#define PF_TEMPORAL_LEVEL_1
-#define PF_NON_TEMPORAL_LEVEL_ALL
-
-  __CRT_INLINE VOID DbgRaiseAssertionFailure(void) {
-    __asm__ __volatile__("int 0x2c ");
-  }
-  PVOID GetCurrentFiber(void);
-  __CRT_INLINE PVOID GetCurrentFiber(void)
-  {
-    void *ret;
-    __asm__ volatile ("movl	%%fs:0x10,%0"
-	: "=r" (ret));
-    return ret;
-  }
-  PVOID GetFiberData(void);
-  __CRT_INLINE PVOID GetFiberData(void)
-  {
-    void *ret;
-    __asm__ volatile ("movl	%%fs:0x10,%0\n"
-	"movl	(%0),%0"
-	: "=r" (ret));
-    return ret;
-  }
-#endif
-
-#define EXCEPTION_READ_FAULT 0
-#define EXCEPTION_WRITE_FAULT 1
-#define EXCEPTION_EXECUTE_FAULT 8
-
-#define SIZE_OF_80387_REGISTERS 80
-
-#if !defined(RC_INVOKED)
-
-#define CONTEXT_i386 0x00010000
-#define CONTEXT_i486 0x00010000
-
-#define CONTEXT_CONTROL (CONTEXT_i386 | 0x00000001L)
-#define CONTEXT_INTEGER (CONTEXT_i386 | 0x00000002L)
-#define CONTEXT_SEGMENTS (CONTEXT_i386 | 0x00000004L)
-#define CONTEXT_FLOATING_POINT (CONTEXT_i386 | 0x00000008L)
-#define CONTEXT_DEBUG_REGISTERS (CONTEXT_i386 | 0x00000010L)
-#define CONTEXT_EXTENDED_REGISTERS (CONTEXT_i386 | 0x00000020L)
-
-#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS)
-
-#define CONTEXT_ALL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | CONTEXT_EXTENDED_REGISTERS)
-#endif
-
-#define MAXIMUM_SUPPORTED_EXTENSION 512
-
-    typedef struct _FLOATING_SAVE_AREA {
-      DWORD ControlWord;
-      DWORD StatusWord;
-      DWORD TagWord;
-      DWORD ErrorOffset;
-      DWORD ErrorSelector;
-      DWORD DataOffset;
-      DWORD DataSelector;
-      BYTE RegisterArea[SIZE_OF_80387_REGISTERS];
-      DWORD Cr0NpxState;
-    } FLOATING_SAVE_AREA;
-
-    typedef FLOATING_SAVE_AREA *PFLOATING_SAVE_AREA;
-
-    typedef struct _CONTEXT {
-      DWORD ContextFlags;
-      DWORD Dr0;
-      DWORD Dr1;
-      DWORD Dr2;
-      DWORD Dr3;
-      DWORD Dr6;
-      DWORD Dr7;
-      FLOATING_SAVE_AREA FloatSave;
-      DWORD SegGs;
-      DWORD SegFs;
-      DWORD SegEs;
-      DWORD SegDs;
-
-      DWORD Edi;
-      DWORD Esi;
-      DWORD Ebx;
-      DWORD Edx;
-      DWORD Ecx;
-      DWORD Eax;
-      DWORD Ebp;
-      DWORD Eip;
-      DWORD SegCs;
-      DWORD EFlags;
-      DWORD Esp;
-      DWORD SegSs;
-      BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
-    } CONTEXT;
-
-    typedef CONTEXT *PCONTEXT;
-#endif
-
-#ifndef _LDT_ENTRY_DEFINED
-#define _LDT_ENTRY_DEFINED
-
-    typedef struct _LDT_ENTRY {
-      WORD LimitLow;
-      WORD BaseLow;
-      union {
-	struct {
-	  BYTE BaseMid;
-	  BYTE Flags1;
-	  BYTE Flags2;
-	  BYTE BaseHi;
-	} Bytes;
-	struct {
-	  DWORD BaseMid : 8;
-	  DWORD Type : 5;
-	  DWORD Dpl : 2;
-	  DWORD Pres : 1;
-	  DWORD LimitHi : 4;
-	  DWORD Sys : 1;
-	  DWORD Reserved_0 : 1;
-	  DWORD Default_Big : 1;
-	  DWORD Granularity : 1;
-	  DWORD BaseHi : 8;
-	} Bits;
-      } HighWord;
-    } LDT_ENTRY,*PLDT_ENTRY;
-#endif
-
-#if defined(__ia64__) && !defined(RC_INVOKED)
-
-#ifdef __cplusplus
-    extern "C" {
-#endif
-
-      BOOLEAN BitScanForward64(DWORD *Index,DWORD64 Mask);
-      BOOLEAN BitScanReverse64(DWORD *Index,DWORD64 Mask);
-
-#ifdef __cplusplus
-    }
-#endif
-#endif
-
-#if !defined(GENUTIL) && !defined(_GENIA64_) && defined(_IA64_)
-
-    void *_cdecl _rdteb(void);
-#ifdef __ia64__
-
-#define NtCurrentTeb() ((struct _TEB *)_rdteb())
-#define GetCurrentFiber() (((PNT_TIB)NtCurrentTeb())->FiberData)
-#define GetFiberData() (*(PVOID *)(GetCurrentFiber()))
-
-#ifdef __cplusplus
-    extern "C" {
-#endif
-
-      void __break(int);
-      void __yield(void);
-      void __mf(void);
-      void __lfetch(int Level,VOID CONST *Address);
-      void __lfetchfault(int Level,VOID CONST *Address);
-      void __lfetch_excl(int Level,VOID CONST *Address);
-      void __lfetchfault_excl(int Level,VOID CONST *Address);
-
-#define MD_LFHINT_NONE 0x00
-#define MD_LFHINT_NT1 0x01
-#define MD_LFHINT_NT2 0x02
-#define MD_LFHINT_NTA 0x03
-
-#ifdef __cplusplus
-    }
-#endif
-
-#define YieldProcessor __yield
-#define MemoryBarrier __mf
-#define PreFetchCacheLine __lfetch
-#define ReadForWriteAccess(p) (*(p))
-#define DbgRaiseAssertionFailure() __break(ASSERT_BREAKPOINT)
-
-#define PF_TEMPORAL_LEVEL_1 MD_LFHINT_NONE
-#define PF_NON_TEMPORAL_LEVEL_ALL MD_LFHINT_NTA
-
-#define UnsignedMultiplyHigh __UMULH
-
-    ULONGLONG UnsignedMultiplyHigh(ULONGLONG Multiplier,ULONGLONG Multiplicand);
-#else
-    struct _TEB *NtCurrentTeb(void);
-#endif
-#endif
-
-#ifdef _IA64_
-
-#define EXCEPTION_READ_FAULT 0
-#define EXCEPTION_WRITE_FAULT 1
-#define EXCEPTION_EXECUTE_FAULT 2
-
-#if !defined(RC_INVOKED)
-
-#define CONTEXT_IA64 0x00080000
-
-#define CONTEXT_CONTROL (CONTEXT_IA64 | 0x00000001L)
-#define CONTEXT_LOWER_FLOATING_POINT (CONTEXT_IA64 | 0x00000002L)
-#define CONTEXT_HIGHER_FLOATING_POINT (CONTEXT_IA64 | 0x00000004L)
-#define CONTEXT_INTEGER (CONTEXT_IA64 | 0x00000008L)
-#define CONTEXT_DEBUG (CONTEXT_IA64 | 0x00000010L)
-#define CONTEXT_IA32_CONTROL (CONTEXT_IA64 | 0x00000020L)
-
-#define CONTEXT_FLOATING_POINT (CONTEXT_LOWER_FLOATING_POINT | CONTEXT_HIGHER_FLOATING_POINT)
-#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_FLOATING_POINT | CONTEXT_INTEGER | CONTEXT_IA32_CONTROL)
-#define CONTEXT_ALL (CONTEXT_CONTROL | CONTEXT_FLOATING_POINT | CONTEXT_INTEGER | CONTEXT_DEBUG | CONTEXT_IA32_CONTROL)
-
-#define CONTEXT_EXCEPTION_ACTIVE 0x8000000
-#define CONTEXT_SERVICE_ACTIVE 0x10000000
-#define CONTEXT_EXCEPTION_REQUEST 0x40000000
-#define CONTEXT_EXCEPTION_REPORTING 0x80000000
-#endif
-
-    typedef struct _CONTEXT {
-      DWORD ContextFlags;
-      DWORD Fill1[3];
-      ULONGLONG DbI0;
-      ULONGLONG DbI1;
-      ULONGLONG DbI2;
-      ULONGLONG DbI3;
-      ULONGLONG DbI4;
-      ULONGLONG DbI5;
-      ULONGLONG DbI6;
-      ULONGLONG DbI7;
-      ULONGLONG DbD0;
-      ULONGLONG DbD1;
-      ULONGLONG DbD2;
-      ULONGLONG DbD3;
-      ULONGLONG DbD4;
-      ULONGLONG DbD5;
-      ULONGLONG DbD6;
-      ULONGLONG DbD7;
-      FLOAT128 FltS0;
-      FLOAT128 FltS1;
-      FLOAT128 FltS2;
-      FLOAT128 FltS3;
-      FLOAT128 FltT0;
-      FLOAT128 FltT1;
-      FLOAT128 FltT2;
-      FLOAT128 FltT3;
-      FLOAT128 FltT4;
-      FLOAT128 FltT5;
-      FLOAT128 FltT6;
-      FLOAT128 FltT7;
-      FLOAT128 FltT8;
-      FLOAT128 FltT9;
-      FLOAT128 FltS4;
-      FLOAT128 FltS5;
-      FLOAT128 FltS6;
-      FLOAT128 FltS7;
-      FLOAT128 FltS8;
-      FLOAT128 FltS9;
-      FLOAT128 FltS10;
-      FLOAT128 FltS11;
-      FLOAT128 FltS12;
-      FLOAT128 FltS13;
-      FLOAT128 FltS14;
-      FLOAT128 FltS15;
-      FLOAT128 FltS16;
-      FLOAT128 FltS17;
-      FLOAT128 FltS18;
-      FLOAT128 FltS19;
-      FLOAT128 FltF32;
-      FLOAT128 FltF33;
-      FLOAT128 FltF34;
-      FLOAT128 FltF35;
-      FLOAT128 FltF36;
-      FLOAT128 FltF37;
-      FLOAT128 FltF38;
-      FLOAT128 FltF39;
-      FLOAT128 FltF40;
-      FLOAT128 FltF41;
-      FLOAT128 FltF42;
-      FLOAT128 FltF43;
-      FLOAT128 FltF44;
-      FLOAT128 FltF45;
-      FLOAT128 FltF46;
-      FLOAT128 FltF47;
-      FLOAT128 FltF48;
-      FLOAT128 FltF49;
-      FLOAT128 FltF50;
-      FLOAT128 FltF51;
-      FLOAT128 FltF52;
-      FLOAT128 FltF53;
-      FLOAT128 FltF54;
-      FLOAT128 FltF55;
-      FLOAT128 FltF56;
-      FLOAT128 FltF57;
-      FLOAT128 FltF58;
-      FLOAT128 FltF59;
-      FLOAT128 FltF60;
-      FLOAT128 FltF61;
-      FLOAT128 FltF62;
-      FLOAT128 FltF63;
-      FLOAT128 FltF64;
-      FLOAT128 FltF65;
-      FLOAT128 FltF66;
-      FLOAT128 FltF67;
-      FLOAT128 FltF68;
-      FLOAT128 FltF69;
-      FLOAT128 FltF70;
-      FLOAT128 FltF71;
-      FLOAT128 FltF72;
-      FLOAT128 FltF73;
-      FLOAT128 FltF74;
-      FLOAT128 FltF75;
-      FLOAT128 FltF76;
-      FLOAT128 FltF77;
-      FLOAT128 FltF78;
-      FLOAT128 FltF79;
-      FLOAT128 FltF80;
-      FLOAT128 FltF81;
-      FLOAT128 FltF82;
-      FLOAT128 FltF83;
-      FLOAT128 FltF84;
-      FLOAT128 FltF85;
-      FLOAT128 FltF86;
-      FLOAT128 FltF87;
-      FLOAT128 FltF88;
-      FLOAT128 FltF89;
-      FLOAT128 FltF90;
-      FLOAT128 FltF91;
-      FLOAT128 FltF92;
-      FLOAT128 FltF93;
-      FLOAT128 FltF94;
-      FLOAT128 FltF95;
-      FLOAT128 FltF96;
-      FLOAT128 FltF97;
-      FLOAT128 FltF98;
-      FLOAT128 FltF99;
-      FLOAT128 FltF100;
-      FLOAT128 FltF101;
-      FLOAT128 FltF102;
-      FLOAT128 FltF103;
-      FLOAT128 FltF104;
-      FLOAT128 FltF105;
-      FLOAT128 FltF106;
-      FLOAT128 FltF107;
-      FLOAT128 FltF108;
-      FLOAT128 FltF109;
-      FLOAT128 FltF110;
-      FLOAT128 FltF111;
-      FLOAT128 FltF112;
-      FLOAT128 FltF113;
-      FLOAT128 FltF114;
-      FLOAT128 FltF115;
-      FLOAT128 FltF116;
-      FLOAT128 FltF117;
-      FLOAT128 FltF118;
-      FLOAT128 FltF119;
-      FLOAT128 FltF120;
-      FLOAT128 FltF121;
-      FLOAT128 FltF122;
-      FLOAT128 FltF123;
-      FLOAT128 FltF124;
-      FLOAT128 FltF125;
-      FLOAT128 FltF126;
-      FLOAT128 FltF127;
-      ULONGLONG StFPSR;
-      ULONGLONG IntGp;
-      ULONGLONG IntT0;
-      ULONGLONG IntT1;
-      ULONGLONG IntS0;
-      ULONGLONG IntS1;
-      ULONGLONG IntS2;
-      ULONGLONG IntS3;
-      ULONGLONG IntV0;
-      ULONGLONG IntT2;
-      ULONGLONG IntT3;
-      ULONGLONG IntT4;
-      ULONGLONG IntSp;
-      ULONGLONG IntTeb;
-      ULONGLONG IntT5;
-      ULONGLONG IntT6;
-      ULONGLONG IntT7;
-      ULONGLONG IntT8;
-      ULONGLONG IntT9;
-      ULONGLONG IntT10;
-      ULONGLONG IntT11;
-      ULONGLONG IntT12;
-      ULONGLONG IntT13;
-      ULONGLONG IntT14;
-      ULONGLONG IntT15;
-      ULONGLONG IntT16;
-      ULONGLONG IntT17;
-      ULONGLONG IntT18;
-      ULONGLONG IntT19;
-      ULONGLONG IntT20;
-      ULONGLONG IntT21;
-      ULONGLONG IntT22;
-      ULONGLONG IntNats;
-      ULONGLONG Preds;
-      ULONGLONG BrRp;
-      ULONGLONG BrS0;
-      ULONGLONG BrS1;
-      ULONGLONG BrS2;
-      ULONGLONG BrS3;
-      ULONGLONG BrS4;
-      ULONGLONG BrT0;
-      ULONGLONG BrT1;
-      ULONGLONG ApUNAT;
-      ULONGLONG ApLC;
-      ULONGLONG ApEC;
-      ULONGLONG ApCCV;
-      ULONGLONG ApDCR;
-      ULONGLONG RsPFS;
-      ULONGLONG RsBSP;
-      ULONGLONG RsBSPSTORE;
-      ULONGLONG RsRSC;
-      ULONGLONG RsRNAT;
-      ULONGLONG StIPSR;
-      ULONGLONG StIIP;
-      ULONGLONG StIFS;
-      ULONGLONG StFCR;
-      ULONGLONG Eflag;
-      ULONGLONG SegCSD;
-      ULONGLONG SegSSD;
-      ULONGLONG Cflag;
-      ULONGLONG StFSR;
-      ULONGLONG StFIR;
-      ULONGLONG StFDR;
-      ULONGLONG UNUSEDPACK;
-    } CONTEXT,*PCONTEXT;
-
-    typedef struct _PLABEL_DESCRIPTOR {
-      ULONGLONG EntryPoint;
-      ULONGLONG GlobalPointer;
-    } PLABEL_DESCRIPTOR,*PPLABEL_DESCRIPTOR;
-
-    typedef struct _RUNTIME_FUNCTION {
-      DWORD BeginAddress;
-      DWORD EndAddress;
-      DWORD UnwindInfoAddress;
-    } RUNTIME_FUNCTION,*PRUNTIME_FUNCTION;
-
-    typedef PRUNTIME_FUNCTION (*PGET_RUNTIME_FUNCTION_CALLBACK)(DWORD64 ControlPc,PVOID Context);
-    typedef DWORD (*POUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK)(HANDLE Process,PVOID TableAddress,PDWORD Entries,PRUNTIME_FUNCTION *Functions);
-
-#define OUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK_EXPORT_NAME "OutOfProcessFunctionTableCallback"
-
-    BOOLEAN RtlAddFunctionTable(PRUNTIME_FUNCTION FunctionTable,DWORD EntryCount,ULONGLONG BaseAddress,ULONGLONG TargetGp);
-    BOOLEAN RtlInstallFunctionTableCallback(DWORD64 TableIdentifier,DWORD64 BaseAddress,DWORD Length,DWORD64 TargetGp,PGET_RUNTIME_FUNCTION_CALLBACK Callback,PVOID Context,PCWSTR OutOfProcessCallbackDll);
-    BOOLEAN RtlDeleteFunctionTable(PRUNTIME_FUNCTION FunctionTable);
-    VOID RtlRestoreContext (PCONTEXT ContextRecord,struct _EXCEPTION_RECORD *ExceptionRecord);
-    VOID __jump_unwind(ULONGLONG TargetMsFrame,ULONGLONG TargetBsFrame,ULONGLONG TargetPc);
-#endif
-
-#define EXCEPTION_NONCONTINUABLE 0x1
-#define EXCEPTION_MAXIMUM_PARAMETERS 15
-
-    typedef struct _EXCEPTION_RECORD {
-      DWORD ExceptionCode;
-      DWORD ExceptionFlags;
-      struct _EXCEPTION_RECORD *ExceptionRecord;
-      PVOID ExceptionAddress;
-      DWORD NumberParameters;
-      ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
-    } EXCEPTION_RECORD;
-
-    typedef EXCEPTION_RECORD *PEXCEPTION_RECORD;
-
-    typedef struct _EXCEPTION_RECORD32 {
-      DWORD ExceptionCode;
-      DWORD ExceptionFlags;
-      DWORD ExceptionRecord;
-      DWORD ExceptionAddress;
-      DWORD NumberParameters;
-      DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
-    } EXCEPTION_RECORD32,*PEXCEPTION_RECORD32;
-
-    typedef struct _EXCEPTION_RECORD64 {
-      DWORD ExceptionCode;
-      DWORD ExceptionFlags;
-      DWORD64 ExceptionRecord;
-      DWORD64 ExceptionAddress;
-      DWORD NumberParameters;
-      DWORD __unusedAlignment;
-      DWORD64 ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
-    } EXCEPTION_RECORD64,*PEXCEPTION_RECORD64;
-
-    typedef struct _EXCEPTION_POINTERS {
-      PEXCEPTION_RECORD ExceptionRecord;
-      PCONTEXT ContextRecord;
-    } EXCEPTION_POINTERS,*PEXCEPTION_POINTERS;
-
-#ifdef __x86_64
-
-    typedef EXCEPTION_DISPOSITION NTAPI EXCEPTION_ROUTINE (struct _EXCEPTION_RECORD *ExceptionRecord, PVOID EstablisherFrame, struct _CONTEXT *ContextRecord, PVOID DispatcherContext);
-#ifndef __PEXCEPTION_ROUTINE_DEFINED
-#define __PEXCEPTION_ROUTINE_DEFINED
-    typedef EXCEPTION_ROUTINE *PEXCEPTION_ROUTINE;
-#endif
-
-    /* http://msdn.microsoft.com/en-us/library/ms680597(VS.85).aspx */
-
-#define UNWIND_HISTORY_TABLE_SIZE 12
-
-  typedef struct _UNWIND_HISTORY_TABLE_ENTRY {
-    ULONG64 ImageBase;
-    PRUNTIME_FUNCTION FunctionEntry;
-  } UNWIND_HISTORY_TABLE_ENTRY, *PUNWIND_HISTORY_TABLE_ENTRY;
-
-#define UNWIND_HISTORY_TABLE_NONE    0
-#define UNWIND_HISTORY_TABLE_GLOBAL  1
-#define UNWIND_HISTORY_TABLE_LOCAL   2
-
-  typedef struct _UNWIND_HISTORY_TABLE {
-    ULONG Count;
-    UCHAR Search;
-    ULONG64 LowAddress;
-    ULONG64 HighAddress;
-    UNWIND_HISTORY_TABLE_ENTRY Entry[UNWIND_HISTORY_TABLE_SIZE];
-  } UNWIND_HISTORY_TABLE, *PUNWIND_HISTORY_TABLE;
-
-  /* http://msdn.microsoft.com/en-us/library/b6sf5kbd(VS.80).aspx */
-
-  struct _DISPATCHER_CONTEXT;
-  typedef struct _DISPATCHER_CONTEXT DISPATCHER_CONTEXT;
-  typedef struct _DISPATCHER_CONTEXT *PDISPATCHER_CONTEXT;
-
-  struct _DISPATCHER_CONTEXT {
-    ULONG64 ControlPc;
-    ULONG64 ImageBase;
-    PRUNTIME_FUNCTION FunctionEntry;
-    ULONG64 EstablisherFrame;
-    ULONG64 TargetIp;
-    PCONTEXT ContextRecord;
-    PEXCEPTION_ROUTINE LanguageHandler;
-    PVOID HandlerData;
-    /* http://www.nynaeve.net/?p=99 */
-    PUNWIND_HISTORY_TABLE HistoryTable;
-    ULONG ScopeIndex;
-    ULONG Fill0;
-  };
-
-  /* http://msdn.microsoft.com/en-us/library/ms680617(VS.85).aspx */
-
-  typedef struct _KNONVOLATILE_CONTEXT_POINTERS
-  {
-    PM128A FloatingContext[16];
-    PULONG64 IntegerContext[16];
-  } KNONVOLATILE_CONTEXT_POINTERS, *PKNONVOLATILE_CONTEXT_POINTERS;
-#endif /* defined(__x86_64) */
-
-    typedef PVOID PACCESS_TOKEN;
-    typedef PVOID PSECURITY_DESCRIPTOR;
-    typedef PVOID PSID;
-
-    typedef DWORD ACCESS_MASK;
-    typedef ACCESS_MASK *PACCESS_MASK;
-
-#define DELETE (0x00010000L)
-#define READ_CONTROL (0x00020000L)
-#define WRITE_DAC (0x00040000L)
-#define WRITE_OWNER (0x00080000L)
-#define SYNCHRONIZE (0x00100000L)
-
-#define STANDARD_RIGHTS_REQUIRED (0x000F0000L)
-#define STANDARD_RIGHTS_READ (READ_CONTROL)
-#define STANDARD_RIGHTS_WRITE (READ_CONTROL)
-#define STANDARD_RIGHTS_EXECUTE (READ_CONTROL)
-#define STANDARD_RIGHTS_ALL (0x001F0000L)
-
-#define SPECIFIC_RIGHTS_ALL (0x0000FFFFL)
-
-#define ACCESS_SYSTEM_SECURITY (0x01000000L)
-
-#define MAXIMUM_ALLOWED (0x02000000L)
-
-#define GENERIC_READ (0x80000000L)
-#define GENERIC_WRITE (0x40000000L)
-#define GENERIC_EXECUTE (0x20000000L)
-#define GENERIC_ALL (0x10000000L)
-
-    typedef struct _GENERIC_MAPPING {
-      ACCESS_MASK GenericRead;
-      ACCESS_MASK GenericWrite;
-      ACCESS_MASK GenericExecute;
-      ACCESS_MASK GenericAll;
-    } GENERIC_MAPPING;
-    typedef GENERIC_MAPPING *PGENERIC_MAPPING;
-
-#include <pshpack4.h>
-
-    typedef struct _LUID_AND_ATTRIBUTES {
-      LUID Luid;
-      DWORD Attributes;
-    } LUID_AND_ATTRIBUTES,*PLUID_AND_ATTRIBUTES;
-    typedef LUID_AND_ATTRIBUTES LUID_AND_ATTRIBUTES_ARRAY[ANYSIZE_ARRAY];
-    typedef LUID_AND_ATTRIBUTES_ARRAY *PLUID_AND_ATTRIBUTES_ARRAY;
-
-#include <poppack.h>
-
-#ifndef SID_IDENTIFIER_AUTHORITY_DEFINED
-#define SID_IDENTIFIER_AUTHORITY_DEFINED
-    typedef struct _SID_IDENTIFIER_AUTHORITY {
-      BYTE Value[6];
-    } SID_IDENTIFIER_AUTHORITY,*PSID_IDENTIFIER_AUTHORITY;
-#endif
-
-#ifndef SID_DEFINED
-#define SID_DEFINED
-    typedef struct _SID {
-      BYTE Revision;
-      BYTE SubAuthorityCount;
-      SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
-      DWORD SubAuthority[ANYSIZE_ARRAY];
-    } SID,*PISID;
-#endif
-
-#define SID_REVISION (1)
-#define SID_MAX_SUB_AUTHORITIES (15)
-#define SID_RECOMMENDED_SUB_AUTHORITIES (1)
-
-#define SECURITY_MAX_SID_SIZE (sizeof(SID) - sizeof(DWORD) + (SID_MAX_SUB_AUTHORITIES *sizeof(DWORD)))
-
-    typedef enum _SID_NAME_USE {
-      SidTypeUser = 1,SidTypeGroup,SidTypeDomain,SidTypeAlias,SidTypeWellKnownGroup,SidTypeDeletedAccount,SidTypeInvalid,SidTypeUnknown,SidTypeComputer
-    } SID_NAME_USE,*PSID_NAME_USE;
-
-    typedef struct _SID_AND_ATTRIBUTES {
-      PSID Sid;
-      DWORD Attributes;
-    } SID_AND_ATTRIBUTES,*PSID_AND_ATTRIBUTES;
-
-    typedef SID_AND_ATTRIBUTES SID_AND_ATTRIBUTES_ARRAY[ANYSIZE_ARRAY];
-    typedef SID_AND_ATTRIBUTES_ARRAY *PSID_AND_ATTRIBUTES_ARRAY;
-
-#define SECURITY_NULL_SID_AUTHORITY {0,0,0,0,0,0}
-#define SECURITY_WORLD_SID_AUTHORITY {0,0,0,0,0,1}
-#define SECURITY_LOCAL_SID_AUTHORITY {0,0,0,0,0,2}
-#define SECURITY_CREATOR_SID_AUTHORITY {0,0,0,0,0,3}
-#define SECURITY_NON_UNIQUE_AUTHORITY {0,0,0,0,0,4}
-#define SECURITY_RESOURCE_MANAGER_AUTHORITY {0,0,0,0,0,9}
-
-#define SECURITY_NULL_RID (0x00000000L)
-#define SECURITY_WORLD_RID (0x00000000L)
-#define SECURITY_LOCAL_RID (0x00000000L)
-
-#define SECURITY_CREATOR_OWNER_RID (0x00000000L)
-#define SECURITY_CREATOR_GROUP_RID (0x00000001L)
-
-#define SECURITY_CREATOR_OWNER_SERVER_RID (0x00000002L)
-#define SECURITY_CREATOR_GROUP_SERVER_RID (0x00000003L)
-
-#define SECURITY_NT_AUTHORITY {0,0,0,0,0,5}
-
-#define SECURITY_DIALUP_RID (0x00000001L)
-#define SECURITY_NETWORK_RID (0x00000002L)
-#define SECURITY_BATCH_RID (0x00000003L)
-#define SECURITY_INTERACTIVE_RID (0x00000004L)
-#define SECURITY_LOGON_IDS_RID (0x00000005L)
-#define SECURITY_LOGON_IDS_RID_COUNT (3L)
-#define SECURITY_SERVICE_RID (0x00000006L)
-#define SECURITY_ANONYMOUS_LOGON_RID (0x00000007L)
-#define SECURITY_PROXY_RID (0x00000008L)
-#define SECURITY_ENTERPRISE_CONTROLLERS_RID (0x00000009L)
-#define SECURITY_SERVER_LOGON_RID SECURITY_ENTERPRISE_CONTROLLERS_RID
-#define SECURITY_PRINCIPAL_SELF_RID (0x0000000AL)
-#define SECURITY_AUTHENTICATED_USER_RID (0x0000000BL)
-#define SECURITY_RESTRICTED_CODE_RID (0x0000000CL)
-#define SECURITY_TERMINAL_SERVER_RID (0x0000000DL)
-#define SECURITY_REMOTE_LOGON_RID (0x0000000EL)
-#define SECURITY_THIS_ORGANIZATION_RID (0x0000000FL)
-#define SECURITY_IUSER_RID (0x00000011L)
-
-#define SECURITY_LOCAL_SYSTEM_RID (0x00000012L)
-#define SECURITY_LOCAL_SERVICE_RID (0x00000013L)
-#define SECURITY_NETWORK_SERVICE_RID (0x00000014L)
-
-#define SECURITY_NT_NON_UNIQUE (0x00000015L)
-#define SECURITY_NT_NON_UNIQUE_SUB_AUTH_COUNT (3L)
-
-#define SECURITY_ENTERPRISE_READONLY_CONTROLLERS_RID (0x00000016L)
-
-#define SECURITY_BUILTIN_DOMAIN_RID (0x00000020L)
-#define SECURITY_WRITE_RESTRICTED_CODE_RID (0x00000021L)
-
-#define SECURITY_PACKAGE_BASE_RID (0x00000040L)
-#define SECURITY_PACKAGE_RID_COUNT (2L)
-#define SECURITY_PACKAGE_NTLM_RID (0x0000000AL)
-#define SECURITY_PACKAGE_SCHANNEL_RID (0x0000000EL)
-#define SECURITY_PACKAGE_DIGEST_RID (0x00000015L)
-
-#define SECURITY_SERVICE_ID_BASE_RID (0x00000050L)
-#define SECURITY_SERVICE_ID_RID_COUNT (6L)
-
-#define SECURITY_RESERVED_ID_BASE_RID (0x00000051L)
-
-#define SECURITY_MAX_ALWAYS_FILTERED (0x000003E7L)
-#define SECURITY_MIN_NEVER_FILTERED (0x000003E8L)
-
-#define SECURITY_OTHER_ORGANIZATION_RID (0x000003E8L)
-
-#define FOREST_USER_RID_MAX (0x000001F3L)
-
-#define DOMAIN_USER_RID_ADMIN (0x000001F4L)
-#define DOMAIN_USER_RID_GUEST (0x000001F5L)
-#define DOMAIN_USER_RID_KRBTGT (0x000001F6L)
-
-#define DOMAIN_USER_RID_MAX (0x000003E7L)
-
-#define DOMAIN_GROUP_RID_ADMINS (0x00000200L)
-#define DOMAIN_GROUP_RID_USERS (0x00000201L)
-#define DOMAIN_GROUP_RID_GUESTS (0x00000202L)
-#define DOMAIN_GROUP_RID_COMPUTERS (0x00000203L)
-#define DOMAIN_GROUP_RID_CONTROLLERS (0x00000204L)
-#define DOMAIN_GROUP_RID_CERT_ADMINS (0x00000205L)
-#define DOMAIN_GROUP_RID_SCHEMA_ADMINS (0x00000206L)
-#define DOMAIN_GROUP_RID_ENTERPRISE_ADMINS (0x00000207L)
-#define DOMAIN_GROUP_RID_POLICY_ADMINS (0x00000208L)
-#define DOMAIN_GROUP_RID_READONLY_CONTROLLERS (0x00000209L)
-
-#define DOMAIN_ALIAS_RID_ADMINS (0x00000220L)
-#define DOMAIN_ALIAS_RID_USERS (0x00000221L)
-#define DOMAIN_ALIAS_RID_GUESTS (0x00000222L)
-#define DOMAIN_ALIAS_RID_POWER_USERS (0x00000223L)
-
-#define DOMAIN_ALIAS_RID_ACCOUNT_OPS (0x00000224L)
-#define DOMAIN_ALIAS_RID_SYSTEM_OPS (0x00000225L)
-#define DOMAIN_ALIAS_RID_PRINT_OPS (0x00000226L)
-#define DOMAIN_ALIAS_RID_BACKUP_OPS (0x00000227L)
-
-#define DOMAIN_ALIAS_RID_REPLICATOR (0x00000228L)
-#define DOMAIN_ALIAS_RID_RAS_SERVERS (0x00000229L)
-#define DOMAIN_ALIAS_RID_PREW2KCOMPACCESS (0x0000022AL)
-#define DOMAIN_ALIAS_RID_REMOTE_DESKTOP_USERS (0x0000022BL)
-#define DOMAIN_ALIAS_RID_NETWORK_CONFIGURATION_OPS (0x0000022CL)
-#define DOMAIN_ALIAS_RID_INCOMING_FOREST_TRUST_BUILDERS (0x0000022DL)
-
-#define DOMAIN_ALIAS_RID_MONITORING_USERS (0x0000022EL)
-#define DOMAIN_ALIAS_RID_LOGGING_USERS (0x0000022FL)
-#define DOMAIN_ALIAS_RID_AUTHORIZATIONACCESS (0x00000230L)
-#define DOMAIN_ALIAS_RID_TS_LICENSE_SERVERS (0x00000231L)
-#define DOMAIN_ALIAS_RID_DCOM_USERS (0x00000232L)
-
-#define DOMAIN_ALIAS_RID_IUSERS (0x00000238L)
-#define DOMAIN_ALIAS_RID_CRYPTO_OPERATORS (0x00000239L)
-#define DOMAIN_ALIAS_RID_CACHEABLE_PRINCIPALS_GROUP (0x0000023BL)
-#define DOMAIN_ALIAS_RID_NON_CACHEABLE_PRINCIPALS_GROUP (0x0000023CL)
-#define DOMAIN_ALIAS_RID_EVENT_LOG_READERS_GROUP (0x0000023DL)
-
-#define SECURITY_MANDATORY_LABEL_AUTHORITY {0,0,0,0,0,16}
-#define SECURITY_MANDATORY_UNTRUSTED_RID (0x00000000L)
-#define SECURITY_MANDATORY_LOW_RID (0x00001000L)
-#define SECURITY_MANDATORY_MEDIUM_RID (0x00002000L)
-#define SECURITY_MANDATORY_HIGH_RID (0x00003000L)
-#define SECURITY_MANDATORY_SYSTEM_RID (0x00004000L)
-#define SECURITY_MANDATORY_PROTECTED_PROCESS_RID (0x00005000L)
-
-#define SECURITY_MANDATORY_MAXIMUM_USER_RID SECURITY_MANDATORY_SYSTEM_RID
-
-#define MANDATORY_LEVEL_TO_MANDATORY_RID(IL) (IL * 0x1000)
-
-    typedef enum {
-      WinNullSid = 0,WinWorldSid = 1,WinLocalSid = 2,WinCreatorOwnerSid = 3,WinCreatorGroupSid = 4,WinCreatorOwnerServerSid = 5,WinCreatorGroupServerSid = 6,WinNtAuthoritySid = 7,WinDialupSid = 8,WinNetworkSid = 9,WinBatchSid = 10,WinInteractiveSid = 11,WinServiceSid = 12,WinAnonymousSid = 13,WinProxySid = 14,WinEnterpriseControllersSid = 15,WinSelfSid = 16,WinAuthenticatedUserSid = 17,WinRestrictedCodeSid = 18,WinTerminalServerSid = 19,WinRemoteLogonIdSid = 20,WinLogonIdsSid = 21,WinLocalSystemSid = 22,WinLocalServiceSid = 23,WinNetworkServiceSid = 24,WinBuiltinDomainSid = 25,WinBuiltinAdministratorsSid = 26,WinBuiltinUsersSid = 27,WinBuiltinGuestsSid = 28,WinBuiltinPowerUsersSid = 29,WinBuiltinAccountOperatorsSid = 30,WinBuiltinSystemOperatorsSid = 31,WinBuiltinPrintOperatorsSid = 32,WinBuiltinBackupOperatorsSid = 33,WinBuiltinReplicatorSid = 34,WinBuiltinPreWindows2000CompatibleAccessSid = 35,WinBuiltinRemoteDesktopUsersSid = 36,WinBuiltinNetworkConfigurationOperatorsSid = 37,WinAccountAdministratorSid = 38,WinAccountGuestSid = 39,WinAccountKrbtgtSid = 40,WinAccountDomainAdminsSid = 41,WinAccountDomainUsersSid = 42,WinAccountDomainGuestsSid = 43,WinAccountComputersSid = 44,WinAccountControllersSid = 45,WinAccountCertAdminsSid = 46,WinAccountSchemaAdminsSid = 47,WinAccountEnterpriseAdminsSid = 48,WinAccountPolicyAdminsSid = 49,WinAccountRasAndIasServersSid = 50,WinNTLMAuthenticationSid = 51,WinDigestAuthenticationSid = 52,WinSChannelAuthenticationSid = 53,WinThisOrganizationSid = 54,WinOtherOrganizationSid = 55,WinBuiltinIncomingForestTrustBuildersSid = 56,WinBuiltinPerfMonitoringUsersSid = 57,WinBuiltinPerfLoggingUsersSid = 58,WinBuiltinAuthorizationAccessSid = 59,WinBuiltinTerminalServerLicenseServersSid = 60,WinBuiltinDCOMUsersSid = 61
-    } WELL_KNOWN_SID_TYPE;
-
-#define SYSTEM_LUID { 0x3E7,0x0 }
-#define ANONYMOUS_LOGON_LUID { 0x3e6,0x0 }
-#define LOCALSERVICE_LUID { 0x3e5,0x0 }
-#define NETWORKSERVICE_LUID { 0x3e4,0x0 }
-#define IUSER_LUID { 0x3e3, 0x0 }
-
-#define SE_GROUP_MANDATORY (0x00000001L)
-#define SE_GROUP_ENABLED_BY_DEFAULT (0x00000002L)
-#define SE_GROUP_ENABLED (0x00000004L)
-#define SE_GROUP_OWNER (0x00000008L)
-#define SE_GROUP_USE_FOR_DENY_ONLY (0x00000010L)
-#define SE_GROUP_INTEGRITY (0x00000020L)
-#define SE_GROUP_INTEGRITY_ENABLED (0x00000040L)
-#define SE_GROUP_LOGON_ID (0xC0000000L)
-#define SE_GROUP_RESOURCE (0x20000000L)
-
-#define ACL_REVISION (2)
-#define ACL_REVISION_DS (4)
-
-#define ACL_REVISION1 (1)
-#define MIN_ACL_REVISION ACL_REVISION2
-#define ACL_REVISION2 (2)
-#define ACL_REVISION3 (3)
-#define ACL_REVISION4 (4)
-#define MAX_ACL_REVISION ACL_REVISION4
-
-    typedef struct _ACL {
-      BYTE AclRevision;
-      BYTE Sbz1;
-      WORD AclSize;
-      WORD AceCount;
-      WORD Sbz2;
-    } ACL;
-    typedef ACL *PACL;
-
-    typedef struct _ACE_HEADER {
-      BYTE AceType;
-      BYTE AceFlags;
-      WORD AceSize;
-    } ACE_HEADER;
-    typedef ACE_HEADER *PACE_HEADER;
-
-#define ACCESS_MIN_MS_ACE_TYPE (0x0)
-#define ACCESS_ALLOWED_ACE_TYPE (0x0)
-#define ACCESS_DENIED_ACE_TYPE (0x1)
-#define SYSTEM_AUDIT_ACE_TYPE (0x2)
-#define SYSTEM_ALARM_ACE_TYPE (0x3)
-#define ACCESS_MAX_MS_V2_ACE_TYPE (0x3)
-
-#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE (0x4)
-#define ACCESS_MAX_MS_V3_ACE_TYPE (0x4)
-
-#define ACCESS_MIN_MS_OBJECT_ACE_TYPE (0x5)
-#define ACCESS_ALLOWED_OBJECT_ACE_TYPE (0x5)
-#define ACCESS_DENIED_OBJECT_ACE_TYPE (0x6)
-#define SYSTEM_AUDIT_OBJECT_ACE_TYPE (0x7)
-#define SYSTEM_ALARM_OBJECT_ACE_TYPE (0x8)
-#define ACCESS_MAX_MS_OBJECT_ACE_TYPE (0x8)
-
-#define ACCESS_MAX_MS_V4_ACE_TYPE (0x8)
-#define ACCESS_MAX_MS_ACE_TYPE (0x8)
-
-#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE (0x9)
-#define ACCESS_DENIED_CALLBACK_ACE_TYPE (0xA)
-#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE (0xB)
-#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE (0xC)
-#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE (0xD)
-#define SYSTEM_ALARM_CALLBACK_ACE_TYPE (0xE)
-#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE (0xF)
-#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE (0x10)
-
-#define SYSTEM_MANDATORY_LABEL_ACE_TYPE (0x11)
-#define ACCESS_MAX_MS_V5_ACE_TYPE (0x11)
-
-#define OBJECT_INHERIT_ACE (0x1)
-#define CONTAINER_INHERIT_ACE (0x2)
-#define NO_PROPAGATE_INHERIT_ACE (0x4)
-#define INHERIT_ONLY_ACE (0x8)
-#define INHERITED_ACE (0x10)
-#define VALID_INHERIT_FLAGS (0x1F)
-
-#define SUCCESSFUL_ACCESS_ACE_FLAG (0x40)
-#define FAILED_ACCESS_ACE_FLAG (0x80)
-
-    typedef struct _ACCESS_ALLOWED_ACE {
-      ACE_HEADER Header;
-      ACCESS_MASK Mask;
-      DWORD SidStart;
-    } ACCESS_ALLOWED_ACE;
-
-    typedef ACCESS_ALLOWED_ACE *PACCESS_ALLOWED_ACE;
-
-    typedef struct _ACCESS_DENIED_ACE {
-      ACE_HEADER Header;
-      ACCESS_MASK Mask;
-      DWORD SidStart;
-    } ACCESS_DENIED_ACE;
-    typedef ACCESS_DENIED_ACE *PACCESS_DENIED_ACE;
-
-    typedef struct _SYSTEM_AUDIT_ACE {
-      ACE_HEADER Header;
-      ACCESS_MASK Mask;
-      DWORD SidStart;
-    } SYSTEM_AUDIT_ACE;
-    typedef SYSTEM_AUDIT_ACE *PSYSTEM_AUDIT_ACE;
-
-    typedef struct _SYSTEM_ALARM_ACE {
-      ACE_HEADER Header;
-      ACCESS_MASK Mask;
-      DWORD SidStart;
-    } SYSTEM_ALARM_ACE;
-    typedef SYSTEM_ALARM_ACE *PSYSTEM_ALARM_ACE;
-
-    typedef struct _ACCESS_ALLOWED_OBJECT_ACE {
-      ACE_HEADER Header;
-      ACCESS_MASK Mask;
-      DWORD Flags;
-      GUID ObjectType;
-      GUID InheritedObjectType;
-      DWORD SidStart;
-    } ACCESS_ALLOWED_OBJECT_ACE,*PACCESS_ALLOWED_OBJECT_ACE;
-
-    typedef struct _ACCESS_DENIED_OBJECT_ACE {
-      ACE_HEADER Header;
-      ACCESS_MASK Mask;
-      DWORD Flags;
-      GUID ObjectType;
-      GUID InheritedObjectType;
-      DWORD SidStart;
-    } ACCESS_DENIED_OBJECT_ACE,*PACCESS_DENIED_OBJECT_ACE;
-
-    typedef struct _SYSTEM_AUDIT_OBJECT_ACE {
-      ACE_HEADER Header;
-      ACCESS_MASK Mask;
-      DWORD Flags;
-      GUID ObjectType;
-      GUID InheritedObjectType;
-      DWORD SidStart;
-    } SYSTEM_AUDIT_OBJECT_ACE,*PSYSTEM_AUDIT_OBJECT_ACE;
-
-    typedef struct _SYSTEM_ALARM_OBJECT_ACE {
-      ACE_HEADER Header;
-      ACCESS_MASK Mask;
-      DWORD Flags;
-      GUID ObjectType;
-      GUID InheritedObjectType;
-      DWORD SidStart;
-    } SYSTEM_ALARM_OBJECT_ACE,*PSYSTEM_ALARM_OBJECT_ACE;
-
-    typedef struct _ACCESS_ALLOWED_CALLBACK_ACE {
-      ACE_HEADER Header;
-      ACCESS_MASK Mask;
-      DWORD SidStart;
-
-    } ACCESS_ALLOWED_CALLBACK_ACE,*PACCESS_ALLOWED_CALLBACK_ACE;
-
-    typedef struct _ACCESS_DENIED_CALLBACK_ACE {
-      ACE_HEADER Header;
-      ACCESS_MASK Mask;
-      DWORD SidStart;
-
-    } ACCESS_DENIED_CALLBACK_ACE,*PACCESS_DENIED_CALLBACK_ACE;
-
-    typedef struct _SYSTEM_AUDIT_CALLBACK_ACE {
-      ACE_HEADER Header;
-      ACCESS_MASK Mask;
-      DWORD SidStart;
-
-    } SYSTEM_AUDIT_CALLBACK_ACE,*PSYSTEM_AUDIT_CALLBACK_ACE;
-
-    typedef struct _SYSTEM_ALARM_CALLBACK_ACE {
-      ACE_HEADER Header;
-      ACCESS_MASK Mask;
-      DWORD SidStart;
-
-    } SYSTEM_ALARM_CALLBACK_ACE,*PSYSTEM_ALARM_CALLBACK_ACE;
-
-    typedef struct _ACCESS_ALLOWED_CALLBACK_OBJECT_ACE {
-      ACE_HEADER Header;
-      ACCESS_MASK Mask;
-      DWORD Flags;
-      GUID ObjectType;
-      GUID InheritedObjectType;
-      DWORD SidStart;
-
-    } ACCESS_ALLOWED_CALLBACK_OBJECT_ACE,*PACCESS_ALLOWED_CALLBACK_OBJECT_ACE;
-
-    typedef struct _ACCESS_DENIED_CALLBACK_OBJECT_ACE {
-      ACE_HEADER Header;
-      ACCESS_MASK Mask;
-      DWORD Flags;
-      GUID ObjectType;
-      GUID InheritedObjectType;
-      DWORD SidStart;
-
-    } ACCESS_DENIED_CALLBACK_OBJECT_ACE,*PACCESS_DENIED_CALLBACK_OBJECT_ACE;
-
-    typedef struct _SYSTEM_AUDIT_CALLBACK_OBJECT_ACE {
-      ACE_HEADER Header;
-      ACCESS_MASK Mask;
-      DWORD Flags;
-      GUID ObjectType;
-      GUID InheritedObjectType;
-      DWORD SidStart;
-
-    } SYSTEM_AUDIT_CALLBACK_OBJECT_ACE,*PSYSTEM_AUDIT_CALLBACK_OBJECT_ACE;
-
-    typedef struct _SYSTEM_ALARM_CALLBACK_OBJECT_ACE {
-      ACE_HEADER Header;
-      ACCESS_MASK Mask;
-      DWORD Flags;
-      GUID ObjectType;
-      GUID InheritedObjectType;
-      DWORD SidStart;
-
-    } SYSTEM_ALARM_CALLBACK_OBJECT_ACE,*PSYSTEM_ALARM_CALLBACK_OBJECT_ACE;
-
-#define ACE_OBJECT_TYPE_PRESENT 0x1
-#define ACE_INHERITED_OBJECT_TYPE_PRESENT 0x2
-
-    typedef enum _ACL_INFORMATION_CLASS {
-      AclRevisionInformation = 1,AclSizeInformation
-    } ACL_INFORMATION_CLASS;
-
-    typedef struct _ACL_REVISION_INFORMATION {
-      DWORD AclRevision;
-    } ACL_REVISION_INFORMATION;
-    typedef ACL_REVISION_INFORMATION *PACL_REVISION_INFORMATION;
-
-    typedef struct _ACL_SIZE_INFORMATION {
-      DWORD AceCount;
-      DWORD AclBytesInUse;
-      DWORD AclBytesFree;
-    } ACL_SIZE_INFORMATION;
-    typedef ACL_SIZE_INFORMATION *PACL_SIZE_INFORMATION;
-
-#define SECURITY_DESCRIPTOR_REVISION (1)
-#define SECURITY_DESCRIPTOR_REVISION1 (1)
-
-#define SECURITY_DESCRIPTOR_MIN_LENGTH (sizeof(SECURITY_DESCRIPTOR))
-
-    typedef WORD SECURITY_DESCRIPTOR_CONTROL,*PSECURITY_DESCRIPTOR_CONTROL;
-
-#define SE_OWNER_DEFAULTED (0x0001)
-#define SE_GROUP_DEFAULTED (0x0002)
-#define SE_DACL_PRESENT (0x0004)
-#define SE_DACL_DEFAULTED (0x0008)
-#define SE_SACL_PRESENT (0x0010)
-#define SE_SACL_DEFAULTED (0x0020)
-#define SE_DACL_AUTO_INHERIT_REQ (0x0100)
-#define SE_SACL_AUTO_INHERIT_REQ (0x0200)
-#define SE_DACL_AUTO_INHERITED (0x0400)
-#define SE_SACL_AUTO_INHERITED (0x0800)
-#define SE_DACL_PROTECTED (0x1000)
-#define SE_SACL_PROTECTED (0x2000)
-#define SE_RM_CONTROL_VALID (0x4000)
-#define SE_SELF_RELATIVE (0x8000)
-
-    typedef struct _SECURITY_DESCRIPTOR_RELATIVE {
-      BYTE Revision;
-      BYTE Sbz1;
-      SECURITY_DESCRIPTOR_CONTROL Control;
-      DWORD Owner;
-      DWORD Group;
-      DWORD Sacl;
-      DWORD Dacl;
-    } SECURITY_DESCRIPTOR_RELATIVE,*PISECURITY_DESCRIPTOR_RELATIVE;
-
-    typedef struct _SECURITY_DESCRIPTOR {
-      BYTE Revision;
-      BYTE Sbz1;
-      SECURITY_DESCRIPTOR_CONTROL Control;
-      PSID Owner;
-      PSID Group;
-      PACL Sacl;
-      PACL Dacl;
-
-    } SECURITY_DESCRIPTOR,*PISECURITY_DESCRIPTOR;
-
-    typedef struct _OBJECT_TYPE_LIST {
-      WORD Level;
-      WORD Sbz;
-      GUID *ObjectType;
-    } OBJECT_TYPE_LIST,*POBJECT_TYPE_LIST;
-
-#define ACCESS_OBJECT_GUID 0
-#define ACCESS_PROPERTY_SET_GUID 1
-#define ACCESS_PROPERTY_GUID 2
-
-#define ACCESS_MAX_LEVEL 4
-
-    typedef enum _AUDIT_EVENT_TYPE {
-      AuditEventObjectAccess,AuditEventDirectoryServiceAccess
-    } AUDIT_EVENT_TYPE,*PAUDIT_EVENT_TYPE;
-
-#define AUDIT_ALLOW_NO_PRIVILEGE 0x1
-
-#define ACCESS_DS_SOURCE_A "DS"
-#define ACCESS_DS_SOURCE_W L"DS"
-#define ACCESS_DS_OBJECT_TYPE_NAME_A "Directory Service Object"
-#define ACCESS_DS_OBJECT_TYPE_NAME_W L"Directory Service Object"
-
-#define SE_PRIVILEGE_ENABLED_BY_DEFAULT (0x00000001L)
-#define SE_PRIVILEGE_ENABLED (0x00000002L)
-#define SE_PRIVILEGE_REMOVED (0X00000004L)
-#define SE_PRIVILEGE_USED_FOR_ACCESS (0x80000000L)
-
-#define PRIVILEGE_SET_ALL_NECESSARY (1)
-
-    typedef struct _PRIVILEGE_SET {
-      DWORD PrivilegeCount;
-      DWORD Control;
-      LUID_AND_ATTRIBUTES Privilege[ANYSIZE_ARRAY];
-    } PRIVILEGE_SET,*PPRIVILEGE_SET;
-
-#define SE_CREATE_TOKEN_NAME TEXT("SeCreateTokenPrivilege")
-#define SE_ASSIGNPRIMARYTOKEN_NAME TEXT("SeAssignPrimaryTokenPrivilege")
-#define SE_LOCK_MEMORY_NAME TEXT("SeLockMemoryPrivilege")
-#define SE_INCREASE_QUOTA_NAME TEXT("SeIncreaseQuotaPrivilege")
-#define SE_UNSOLICITED_INPUT_NAME TEXT("SeUnsolicitedInputPrivilege")
-#define SE_MACHINE_ACCOUNT_NAME TEXT("SeMachineAccountPrivilege")
-#define SE_TCB_NAME TEXT("SeTcbPrivilege")
-#define SE_SECURITY_NAME TEXT("SeSecurityPrivilege")
-#define SE_TAKE_OWNERSHIP_NAME TEXT("SeTakeOwnershipPrivilege")
-#define SE_LOAD_DRIVER_NAME TEXT("SeLoadDriverPrivilege")
-#define SE_SYSTEM_PROFILE_NAME TEXT("SeSystemProfilePrivilege")
-#define SE_SYSTEMTIME_NAME TEXT("SeSystemtimePrivilege")
-#define SE_PROF_SINGLE_PROCESS_NAME TEXT("SeProfileSingleProcessPrivilege")
-#define SE_INC_BASE_PRIORITY_NAME TEXT("SeIncreaseBasePriorityPrivilege")
-#define SE_CREATE_PAGEFILE_NAME TEXT("SeCreatePagefilePrivilege")
-#define SE_CREATE_PERMANENT_NAME TEXT("SeCreatePermanentPrivilege")
-#define SE_BACKUP_NAME TEXT("SeBackupPrivilege")
-#define SE_RESTORE_NAME TEXT("SeRestorePrivilege")
-#define SE_SHUTDOWN_NAME TEXT("SeShutdownPrivilege")
-#define SE_DEBUG_NAME TEXT("SeDebugPrivilege")
-#define SE_AUDIT_NAME TEXT("SeAuditPrivilege")
-#define SE_SYSTEM_ENVIRONMENT_NAME TEXT("SeSystemEnvironmentPrivilege")
-#define SE_CHANGE_NOTIFY_NAME TEXT("SeChangeNotifyPrivilege")
-#define SE_REMOTE_SHUTDOWN_NAME TEXT("SeRemoteShutdownPrivilege")
-#define SE_UNDOCK_NAME TEXT("SeUndockPrivilege")
-#define SE_SYNC_AGENT_NAME TEXT("SeSyncAgentPrivilege")
-#define SE_ENABLE_DELEGATION_NAME TEXT("SeEnableDelegationPrivilege")
-#define SE_MANAGE_VOLUME_NAME TEXT("SeManageVolumePrivilege")
-#define SE_IMPERSONATE_NAME TEXT("SeImpersonatePrivilege")
-#define SE_CREATE_GLOBAL_NAME TEXT("SeCreateGlobalPrivilege")
-
-    typedef enum _SECURITY_IMPERSONATION_LEVEL {
-      SecurityAnonymous,SecurityIdentification,SecurityImpersonation,SecurityDelegation
-    } SECURITY_IMPERSONATION_LEVEL,*PSECURITY_IMPERSONATION_LEVEL;
-
-#define SECURITY_MAX_IMPERSONATION_LEVEL SecurityDelegation
-#define SECURITY_MIN_IMPERSONATION_LEVEL SecurityAnonymous
-#define DEFAULT_IMPERSONATION_LEVEL SecurityImpersonation
-#define VALID_IMPERSONATION_LEVEL(L) (((L) >= SECURITY_MIN_IMPERSONATION_LEVEL) && ((L) <= SECURITY_MAX_IMPERSONATION_LEVEL))
-
-#define TOKEN_ASSIGN_PRIMARY (0x0001)
-#define TOKEN_DUPLICATE (0x0002)
-#define TOKEN_IMPERSONATE (0x0004)
-#define TOKEN_QUERY (0x0008)
-#define TOKEN_QUERY_SOURCE (0x0010)
-#define TOKEN_ADJUST_PRIVILEGES (0x0020)
-#define TOKEN_ADJUST_GROUPS (0x0040)
-#define TOKEN_ADJUST_DEFAULT (0x0080)
-#define TOKEN_ADJUST_SESSIONID (0x0100)
-
-#define TOKEN_ALL_ACCESS_P (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT)
-#define TOKEN_ALL_ACCESS (TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID)
-#define TOKEN_READ (STANDARD_RIGHTS_READ | TOKEN_QUERY)
-
-#define TOKEN_WRITE (STANDARD_RIGHTS_WRITE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT)
-
-#define TOKEN_EXECUTE (STANDARD_RIGHTS_EXECUTE)
-
-    typedef enum _TOKEN_TYPE {
-      TokenPrimary = 1,TokenImpersonation
-    } TOKEN_TYPE;
-    typedef TOKEN_TYPE *PTOKEN_TYPE;
-
-    typedef enum _TOKEN_INFORMATION_CLASS {
-      TokenUser = 1,TokenGroups,TokenPrivileges,TokenOwner,TokenPrimaryGroup,TokenDefaultDacl,TokenSource,TokenType,TokenImpersonationLevel,
-      TokenStatistics,TokenRestrictedSids,TokenSessionId,TokenGroupsAndPrivileges,TokenSessionReference,TokenSandBoxInert,TokenAuditPolicy,
-      TokenOrigin,MaxTokenInfoClass
-    } TOKEN_INFORMATION_CLASS,*PTOKEN_INFORMATION_CLASS;
-
-    typedef struct _TOKEN_USER {
-      SID_AND_ATTRIBUTES User;
-    } TOKEN_USER,*PTOKEN_USER;
-
-    typedef struct _TOKEN_GROUPS {
-      DWORD GroupCount;
-      SID_AND_ATTRIBUTES Groups[ANYSIZE_ARRAY];
-    } TOKEN_GROUPS,*PTOKEN_GROUPS;
-
-    typedef struct _TOKEN_PRIVILEGES {
-      DWORD PrivilegeCount;
-      LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY];
-    } TOKEN_PRIVILEGES,*PTOKEN_PRIVILEGES;
-
-    typedef struct _TOKEN_OWNER {
-      PSID Owner;
-    } TOKEN_OWNER,*PTOKEN_OWNER;
-
-    typedef struct _TOKEN_PRIMARY_GROUP {
-      PSID PrimaryGroup;
-    } TOKEN_PRIMARY_GROUP,*PTOKEN_PRIMARY_GROUP;
-
-    typedef struct _TOKEN_DEFAULT_DACL {
-      PACL DefaultDacl;
-    } TOKEN_DEFAULT_DACL,*PTOKEN_DEFAULT_DACL;
-
-    typedef struct _TOKEN_GROUPS_AND_PRIVILEGES {
-      DWORD SidCount;
-      DWORD SidLength;
-      PSID_AND_ATTRIBUTES Sids;
-      DWORD RestrictedSidCount;
-      DWORD RestrictedSidLength;
-      PSID_AND_ATTRIBUTES RestrictedSids;
-      DWORD PrivilegeCount;
-      DWORD PrivilegeLength;
-      PLUID_AND_ATTRIBUTES Privileges;
-      LUID AuthenticationId;
-    } TOKEN_GROUPS_AND_PRIVILEGES,*PTOKEN_GROUPS_AND_PRIVILEGES;
-
-#define TOKEN_AUDIT_SUCCESS_INCLUDE 0x1
-#define TOKEN_AUDIT_SUCCESS_EXCLUDE 0x2
-#define TOKEN_AUDIT_FAILURE_INCLUDE 0x4
-#define TOKEN_AUDIT_FAILURE_EXCLUDE 0x8
-
-#define VALID_AUDIT_POLICY_BITS (TOKEN_AUDIT_SUCCESS_INCLUDE | TOKEN_AUDIT_SUCCESS_EXCLUDE | TOKEN_AUDIT_FAILURE_INCLUDE | TOKEN_AUDIT_FAILURE_EXCLUDE)
-#define VALID_TOKEN_AUDIT_POLICY_ELEMENT(P) ((((P).PolicyMask & ~VALID_AUDIT_POLICY_BITS)==0) && ((P).Category <= AuditEventMaxType))
-
-    typedef struct _TOKEN_AUDIT_POLICY_ELEMENT {
-      DWORD Category;
-      DWORD PolicyMask;
-    } TOKEN_AUDIT_POLICY_ELEMENT,*PTOKEN_AUDIT_POLICY_ELEMENT;
-
-    typedef struct _TOKEN_AUDIT_POLICY {
-      DWORD PolicyCount;
-      TOKEN_AUDIT_POLICY_ELEMENT Policy[ANYSIZE_ARRAY];
-    } TOKEN_AUDIT_POLICY,*PTOKEN_AUDIT_POLICY;
-
-#define PER_USER_AUDITING_POLICY_SIZE(p) (sizeof(TOKEN_AUDIT_POLICY) + (((p)->PolicyCount > ANYSIZE_ARRAY) ? (sizeof(TOKEN_AUDIT_POLICY_ELEMENT) *((p)->PolicyCount - ANYSIZE_ARRAY)) : 0))
-#define PER_USER_AUDITING_POLICY_SIZE_BY_COUNT(C) (sizeof(TOKEN_AUDIT_POLICY) + (((C) > ANYSIZE_ARRAY) ? (sizeof(TOKEN_AUDIT_POLICY_ELEMENT) *((C) - ANYSIZE_ARRAY)) : 0))
-
-#define TOKEN_SOURCE_LENGTH 8
-
-    typedef struct _TOKEN_SOURCE {
-      CHAR SourceName[TOKEN_SOURCE_LENGTH];
-      LUID SourceIdentifier;
-    } TOKEN_SOURCE,*PTOKEN_SOURCE;
-
-    typedef struct _TOKEN_STATISTICS {
-      LUID TokenId;
-      LUID AuthenticationId;
-      LARGE_INTEGER ExpirationTime;
-      TOKEN_TYPE TokenType;
-      SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
-      DWORD DynamicCharged;
-      DWORD DynamicAvailable;
-      DWORD GroupCount;
-      DWORD PrivilegeCount;
-      LUID ModifiedId;
-    } TOKEN_STATISTICS,*PTOKEN_STATISTICS;
-
-    typedef struct _TOKEN_CONTROL {
-      LUID TokenId;
-      LUID AuthenticationId;
-      LUID ModifiedId;
-      TOKEN_SOURCE TokenSource;
-    } TOKEN_CONTROL,*PTOKEN_CONTROL;
-
-    typedef struct _TOKEN_ORIGIN {
-      LUID OriginatingLogonSession;
-    } TOKEN_ORIGIN,*PTOKEN_ORIGIN;
-
-#define SECURITY_DYNAMIC_TRACKING (TRUE)
-#define SECURITY_STATIC_TRACKING (FALSE)
-
-    typedef BOOLEAN SECURITY_CONTEXT_TRACKING_MODE,*PSECURITY_CONTEXT_TRACKING_MODE;
-
-    typedef struct _SECURITY_QUALITY_OF_SERVICE {
-      DWORD Length;
-      SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
-      SECURITY_CONTEXT_TRACKING_MODE ContextTrackingMode;
-      BOOLEAN EffectiveOnly;
-    } SECURITY_QUALITY_OF_SERVICE,*PSECURITY_QUALITY_OF_SERVICE;
-
-    typedef struct _SE_IMPERSONATION_STATE {
-      PACCESS_TOKEN Token;
-      BOOLEAN CopyOnOpen;
-      BOOLEAN EffectiveOnly;
-      SECURITY_IMPERSONATION_LEVEL Level;
-    } SE_IMPERSONATION_STATE,*PSE_IMPERSONATION_STATE;
-
-#define DISABLE_MAX_PRIVILEGE 0x1
-#define SANDBOX_INERT 0x2
-
-    typedef DWORD SECURITY_INFORMATION,*PSECURITY_INFORMATION;
-
-#define OWNER_SECURITY_INFORMATION (0x00000001L)
-#define GROUP_SECURITY_INFORMATION (0x00000002L)
-#define DACL_SECURITY_INFORMATION (0x00000004L)
-#define SACL_SECURITY_INFORMATION (0x00000008L)
-
-#define PROTECTED_DACL_SECURITY_INFORMATION (0x80000000L)
-#define PROTECTED_SACL_SECURITY_INFORMATION (0x40000000L)
-#define UNPROTECTED_DACL_SECURITY_INFORMATION (0x20000000L)
-#define UNPROTECTED_SACL_SECURITY_INFORMATION (0x10000000L)
-
-#define PROCESS_TERMINATE (0x0001)
-#define PROCESS_CREATE_THREAD (0x0002)
-#define PROCESS_SET_SESSIONID (0x0004)
-#define PROCESS_VM_OPERATION (0x0008)
-#define PROCESS_VM_READ (0x0010)
-#define PROCESS_VM_WRITE (0x0020)
-#define PROCESS_DUP_HANDLE (0x0040)
-#define PROCESS_CREATE_PROCESS (0x0080)
-#define PROCESS_SET_QUOTA (0x0100)
-#define PROCESS_SET_INFORMATION (0x0200)
-#define PROCESS_QUERY_INFORMATION (0x0400)
-#define PROCESS_SUSPEND_RESUME (0x0800)
-#define PROCESS_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF)
-
-#ifdef _WIN64
-#define MAXIMUM_PROCESSORS 64
-#else
-#define MAXIMUM_PROCESSORS 32
-#endif
-
-#define THREAD_TERMINATE (0x0001)
-#define THREAD_SUSPEND_RESUME (0x0002)
-#define THREAD_GET_CONTEXT (0x0008)
-#define THREAD_SET_CONTEXT (0x0010)
-#define THREAD_SET_INFORMATION (0x0020)
-#define THREAD_QUERY_INFORMATION (0x0040)
-#define THREAD_SET_THREAD_TOKEN (0x0080)
-#define THREAD_IMPERSONATE (0x0100)
-#define THREAD_DIRECT_IMPERSONATION (0x0200)
-
-#define THREAD_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3FF)
-
-#define JOB_OBJECT_ASSIGN_PROCESS (0x0001)
-#define JOB_OBJECT_SET_ATTRIBUTES (0x0002)
-#define JOB_OBJECT_QUERY (0x0004)
-#define JOB_OBJECT_TERMINATE (0x0008)
-#define JOB_OBJECT_SET_SECURITY_ATTRIBUTES (0x0010)
-#define JOB_OBJECT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1F)
-
-    typedef struct _JOB_SET_ARRAY {
-      HANDLE JobHandle;
-      DWORD MemberLevel;
-      DWORD Flags;
-    } JOB_SET_ARRAY,*PJOB_SET_ARRAY;
-
-#define FLS_MAXIMUM_AVAILABLE 128
-#define TLS_MINIMUM_AVAILABLE 64
-
-#ifndef _NT_TIB_DEFINED
-#define _NT_TIB_DEFINED
-    typedef struct _NT_TIB {
-      struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
-      PVOID StackBase;
-      PVOID StackLimit;
-      PVOID SubSystemTib;
-      union {
-	PVOID FiberData;
-	DWORD Version;
-      };
-      PVOID ArbitraryUserPointer;
-      struct _NT_TIB *Self;
-    } NT_TIB;
-    typedef NT_TIB *PNT_TIB;
-#endif
-
-    typedef struct _NT_TIB32 {
-      DWORD ExceptionList;
-      DWORD StackBase;
-      DWORD StackLimit;
-      DWORD SubSystemTib;
-      union {
-	DWORD FiberData;
-	DWORD Version;
-      };
-      DWORD ArbitraryUserPointer;
-      DWORD Self;
-    } NT_TIB32,*PNT_TIB32;
-
-    typedef struct _NT_TIB64 {
-      DWORD64 ExceptionList;
-      DWORD64 StackBase;
-      DWORD64 StackLimit;
-      DWORD64 SubSystemTib;
-      union {
-	DWORD64 FiberData;
-	DWORD Version;
-      };
-      DWORD64 ArbitraryUserPointer;
-      DWORD64 Self;
-    } NT_TIB64,*PNT_TIB64;
-
-#if !defined(I_X86_) && !defined(_IA64_) && !defined(_AMD64_)
-#define WX86
-#endif
-
-#define THREAD_BASE_PRIORITY_LOWRT 15
-#define THREAD_BASE_PRIORITY_MAX 2
-#define THREAD_BASE_PRIORITY_MIN (-2)
-#define THREAD_BASE_PRIORITY_IDLE (-15)
-
-    typedef struct _QUOTA_LIMITS {
-      SIZE_T PagedPoolLimit;
-      SIZE_T NonPagedPoolLimit;
-      SIZE_T MinimumWorkingSetSize;
-      SIZE_T MaximumWorkingSetSize;
-      SIZE_T PagefileLimit;
-      LARGE_INTEGER TimeLimit;
-    } QUOTA_LIMITS,*PQUOTA_LIMITS;
-
-#define QUOTA_LIMITS_HARDWS_MIN_ENABLE 0x00000001
-#define QUOTA_LIMITS_HARDWS_MIN_DISABLE 0x00000002
-#define QUOTA_LIMITS_HARDWS_MAX_ENABLE 0x00000004
-#define QUOTA_LIMITS_HARDWS_MAX_DISABLE 0x00000008
-
-    typedef struct _QUOTA_LIMITS_EX {
-      SIZE_T PagedPoolLimit;
-      SIZE_T NonPagedPoolLimit;
-      SIZE_T MinimumWorkingSetSize;
-      SIZE_T MaximumWorkingSetSize;
-      SIZE_T PagefileLimit;
-      LARGE_INTEGER TimeLimit;
-      SIZE_T Reserved1;
-      SIZE_T Reserved2;
-      SIZE_T Reserved3;
-      SIZE_T Reserved4;
-      DWORD Flags;
-      DWORD Reserved5;
-    } QUOTA_LIMITS_EX,*PQUOTA_LIMITS_EX;
-
-    typedef struct _IO_COUNTERS {
-      ULONGLONG ReadOperationCount;
-      ULONGLONG WriteOperationCount;
-      ULONGLONG OtherOperationCount;
-      ULONGLONG ReadTransferCount;
-      ULONGLONG WriteTransferCount;
-      ULONGLONG OtherTransferCount;
-    } IO_COUNTERS;
-    typedef IO_COUNTERS *PIO_COUNTERS;
-
-    typedef struct _JOBOBJECT_BASIC_ACCOUNTING_INFORMATION {
-      LARGE_INTEGER TotalUserTime;
-      LARGE_INTEGER TotalKernelTime;
-      LARGE_INTEGER ThisPeriodTotalUserTime;
-      LARGE_INTEGER ThisPeriodTotalKernelTime;
-      DWORD TotalPageFaultCount;
-      DWORD TotalProcesses;
-      DWORD ActiveProcesses;
-      DWORD TotalTerminatedProcesses;
-    } JOBOBJECT_BASIC_ACCOUNTING_INFORMATION,*PJOBOBJECT_BASIC_ACCOUNTING_INFORMATION;
-
-    typedef struct _JOBOBJECT_BASIC_LIMIT_INFORMATION {
-      LARGE_INTEGER PerProcessUserTimeLimit;
-      LARGE_INTEGER PerJobUserTimeLimit;
-      DWORD LimitFlags;
-      SIZE_T MinimumWorkingSetSize;
-      SIZE_T MaximumWorkingSetSize;
-      DWORD ActiveProcessLimit;
-      ULONG_PTR Affinity;
-      DWORD PriorityClass;
-      DWORD SchedulingClass;
-    } JOBOBJECT_BASIC_LIMIT_INFORMATION,*PJOBOBJECT_BASIC_LIMIT_INFORMATION;
-
-    typedef struct _JOBOBJECT_EXTENDED_LIMIT_INFORMATION {
-      JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
-      IO_COUNTERS IoInfo;
-      SIZE_T ProcessMemoryLimit;
-      SIZE_T JobMemoryLimit;
-      SIZE_T PeakProcessMemoryUsed;
-      SIZE_T PeakJobMemoryUsed;
-    } JOBOBJECT_EXTENDED_LIMIT_INFORMATION,*PJOBOBJECT_EXTENDED_LIMIT_INFORMATION;
-
-    typedef struct _JOBOBJECT_BASIC_PROCESS_ID_LIST {
-      DWORD NumberOfAssignedProcesses;
-      DWORD NumberOfProcessIdsInList;
-      ULONG_PTR ProcessIdList[1];
-    } JOBOBJECT_BASIC_PROCESS_ID_LIST,*PJOBOBJECT_BASIC_PROCESS_ID_LIST;
-
-    typedef struct _JOBOBJECT_BASIC_UI_RESTRICTIONS {
-      DWORD UIRestrictionsClass;
-    } JOBOBJECT_BASIC_UI_RESTRICTIONS,*PJOBOBJECT_BASIC_UI_RESTRICTIONS;
-
-    typedef struct _JOBOBJECT_SECURITY_LIMIT_INFORMATION {
-      DWORD SecurityLimitFlags;
-      HANDLE JobToken;
-      PTOKEN_GROUPS SidsToDisable;
-      PTOKEN_PRIVILEGES PrivilegesToDelete;
-      PTOKEN_GROUPS RestrictedSids;
-    } JOBOBJECT_SECURITY_LIMIT_INFORMATION,*PJOBOBJECT_SECURITY_LIMIT_INFORMATION;
-
-    typedef struct _JOBOBJECT_END_OF_JOB_TIME_INFORMATION {
-      DWORD EndOfJobTimeAction;
-    } JOBOBJECT_END_OF_JOB_TIME_INFORMATION,*PJOBOBJECT_END_OF_JOB_TIME_INFORMATION;
-
-    typedef struct _JOBOBJECT_ASSOCIATE_COMPLETION_PORT {
-      PVOID CompletionKey;
-      HANDLE CompletionPort;
-    } JOBOBJECT_ASSOCIATE_COMPLETION_PORT,*PJOBOBJECT_ASSOCIATE_COMPLETION_PORT;
-
-    typedef struct _JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION {
-      JOBOBJECT_BASIC_ACCOUNTING_INFORMATION BasicInfo;
-      IO_COUNTERS IoInfo;
-    } JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION,*PJOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION;
-
-    typedef struct _JOBOBJECT_JOBSET_INFORMATION {
-      DWORD MemberLevel;
-    } JOBOBJECT_JOBSET_INFORMATION,*PJOBOBJECT_JOBSET_INFORMATION;
-
-#define JOB_OBJECT_TERMINATE_AT_END_OF_JOB 0
-#define JOB_OBJECT_POST_AT_END_OF_JOB 1
-
-#define JOB_OBJECT_MSG_END_OF_JOB_TIME 1
-#define JOB_OBJECT_MSG_END_OF_PROCESS_TIME 2
-#define JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT 3
-#define JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO 4
-#define JOB_OBJECT_MSG_NEW_PROCESS 6
-#define JOB_OBJECT_MSG_EXIT_PROCESS 7
-#define JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS 8
-#define JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT 9
-#define JOB_OBJECT_MSG_JOB_MEMORY_LIMIT 10
-
-#define JOB_OBJECT_LIMIT_WORKINGSET 0x00000001
-#define JOB_OBJECT_LIMIT_PROCESS_TIME 0x00000002
-#define JOB_OBJECT_LIMIT_JOB_TIME 0x00000004
-#define JOB_OBJECT_LIMIT_ACTIVE_PROCESS 0x00000008
-#define JOB_OBJECT_LIMIT_AFFINITY 0x00000010
-#define JOB_OBJECT_LIMIT_PRIORITY_CLASS 0x00000020
-#define JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME 0x00000040
-#define JOB_OBJECT_LIMIT_SCHEDULING_CLASS 0x00000080
-
-#define JOB_OBJECT_LIMIT_PROCESS_MEMORY 0x00000100
-#define JOB_OBJECT_LIMIT_JOB_MEMORY 0x00000200
-#define JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION 0x00000400
-#define JOB_OBJECT_LIMIT_BREAKAWAY_OK 0x00000800
-#define JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK 0x00001000
-#define JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE 0x00002000
-
-#define JOB_OBJECT_LIMIT_RESERVED2 0x00004000
-#define JOB_OBJECT_LIMIT_RESERVED3 0x00008000
-#define JOB_OBJECT_LIMIT_RESERVED4 0x00010000
-#define JOB_OBJECT_LIMIT_RESERVED5 0x00020000
-#define JOB_OBJECT_LIMIT_RESERVED6 0x00040000
-
-#define JOB_OBJECT_LIMIT_VALID_FLAGS 0x0007ffff
-
-#define JOB_OBJECT_BASIC_LIMIT_VALID_FLAGS 0x000000ff
-#define JOB_OBJECT_EXTENDED_LIMIT_VALID_FLAGS 0x00003fff
-#define JOB_OBJECT_RESERVED_LIMIT_VALID_FLAGS 0x0007ffff
-
-#define JOB_OBJECT_UILIMIT_NONE 0x00000000
-
-#define JOB_OBJECT_UILIMIT_HANDLES 0x00000001
-#define JOB_OBJECT_UILIMIT_READCLIPBOARD 0x00000002
-#define JOB_OBJECT_UILIMIT_WRITECLIPBOARD 0x00000004
-#define JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS 0x00000008
-#define JOB_OBJECT_UILIMIT_DISPLAYSETTINGS 0x00000010
-#define JOB_OBJECT_UILIMIT_GLOBALATOMS 0x00000020
-#define JOB_OBJECT_UILIMIT_DESKTOP 0x00000040
-#define JOB_OBJECT_UILIMIT_EXITWINDOWS 0x00000080
-
-#define JOB_OBJECT_UILIMIT_ALL 0x000000FF
-
-#define JOB_OBJECT_UI_VALID_FLAGS 0x000000FF
-
-#define JOB_OBJECT_SECURITY_NO_ADMIN 0x00000001
-#define JOB_OBJECT_SECURITY_RESTRICTED_TOKEN 0x00000002
-#define JOB_OBJECT_SECURITY_ONLY_TOKEN 0x00000004
-#define JOB_OBJECT_SECURITY_FILTER_TOKENS 0x00000008
-
-#define JOB_OBJECT_SECURITY_VALID_FLAGS 0x0000000f
-
-    typedef enum _JOBOBJECTINFOCLASS {
-      JobObjectBasicAccountingInformation = 1,JobObjectBasicLimitInformation,JobObjectBasicProcessIdList,JobObjectBasicUIRestrictions,
-      JobObjectSecurityLimitInformation,JobObjectEndOfJobTimeInformation,JobObjectAssociateCompletionPortInformation,
-      JobObjectBasicAndIoAccountingInformation,JobObjectExtendedLimitInformation,JobObjectJobSetInformation,MaxJobObjectInfoClass
-    } JOBOBJECTINFOCLASS;
-
-#define EVENT_MODIFY_STATE 0x0002
-#define EVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3)
-
-#define MUTANT_QUERY_STATE 0x0001
-
-#define MUTANT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE| MUTANT_QUERY_STATE)
-#define SEMAPHORE_MODIFY_STATE 0x0002
-#define SEMAPHORE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3)
-
-#define TIMER_QUERY_STATE 0x0001
-#define TIMER_MODIFY_STATE 0x0002
-
-#define TIMER_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE| TIMER_QUERY_STATE|TIMER_MODIFY_STATE)
-
-#define TIME_ZONE_ID_UNKNOWN 0
-#define TIME_ZONE_ID_STANDARD 1
-#define TIME_ZONE_ID_DAYLIGHT 2
-
-    typedef enum _LOGICAL_PROCESSOR_RELATIONSHIP {
-      RelationProcessorCore,RelationNumaNode,RelationCache
-    } LOGICAL_PROCESSOR_RELATIONSHIP;
-
-#define LTP_PC_SMT 0x1
-
-    typedef enum _PROCESSOR_CACHE_TYPE {
-      CacheUnified,CacheInstruction,CacheData,CacheTrace
-    } PROCESSOR_CACHE_TYPE;
-
-#define CACHE_FULLY_ASSOCIATIVE 0xFF
-
-    typedef struct _CACHE_DESCRIPTOR {
-      BYTE Level;
-      BYTE Associativity;
-      WORD LineSize;
-      DWORD Size;
-      PROCESSOR_CACHE_TYPE Type;
-    } CACHE_DESCRIPTOR,*PCACHE_DESCRIPTOR;
-
-    typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION {
-      ULONG_PTR ProcessorMask;
-      LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
-      union {
-	struct {
-	  BYTE Flags;
-	} ProcessorCore;
-	struct {
-	  DWORD NodeNumber;
-	} NumaNode;
-	CACHE_DESCRIPTOR Cache;
-	ULONGLONG Reserved[2];
-      };
-    } SYSTEM_LOGICAL_PROCESSOR_INFORMATION,*PSYSTEM_LOGICAL_PROCESSOR_INFORMATION;
-
-#define PROCESSOR_INTEL_386 386
-#define PROCESSOR_INTEL_486 486
-#define PROCESSOR_INTEL_PENTIUM 586
-#define PROCESSOR_INTEL_IA64 2200
-#define PROCESSOR_AMD_X8664 8664
-#define PROCESSOR_MIPS_R4000 4000
-#define PROCESSOR_ALPHA_21064 21064
-#define PROCESSOR_PPC_601 601
-#define PROCESSOR_PPC_603 603
-#define PROCESSOR_PPC_604 604
-#define PROCESSOR_PPC_620 620
-#define PROCESSOR_HITACHI_SH3 10003
-#define PROCESSOR_HITACHI_SH3E 10004
-#define PROCESSOR_HITACHI_SH4 10005
-#define PROCESSOR_MOTOROLA_821 821
-#define PROCESSOR_SHx_SH3 103
-#define PROCESSOR_SHx_SH4 104
-#define PROCESSOR_STRONGARM 2577
-#define PROCESSOR_ARM720 1824
-#define PROCESSOR_ARM820 2080
-#define PROCESSOR_ARM920 2336
-#define PROCESSOR_ARM_7TDMI 70001
-#define PROCESSOR_OPTIL 0x494f
-
-#define PROCESSOR_ARCHITECTURE_INTEL 0
-#define PROCESSOR_ARCHITECTURE_MIPS 1
-#define PROCESSOR_ARCHITECTURE_ALPHA 2
-#define PROCESSOR_ARCHITECTURE_PPC 3
-#define PROCESSOR_ARCHITECTURE_SHX 4
-#define PROCESSOR_ARCHITECTURE_ARM 5
-#define PROCESSOR_ARCHITECTURE_IA64 6
-#define PROCESSOR_ARCHITECTURE_ALPHA64 7
-#define PROCESSOR_ARCHITECTURE_MSIL 8
-#define PROCESSOR_ARCHITECTURE_AMD64 9
-#define PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 10
-
-#define PROCESSOR_ARCHITECTURE_UNKNOWN 0xFFFF
-
-#define PF_FLOATING_POINT_PRECISION_ERRATA 0
-#define PF_FLOATING_POINT_EMULATED 1
-#define PF_COMPARE_EXCHANGE_DOUBLE 2
-#define PF_MMX_INSTRUCTIONS_AVAILABLE 3
-#define PF_PPC_MOVEMEM_64BIT_OK 4
-#define PF_ALPHA_BYTE_INSTRUCTIONS 5
-#define PF_XMMI_INSTRUCTIONS_AVAILABLE 6
-#define PF_3DNOW_INSTRUCTIONS_AVAILABLE 7
-#define PF_RDTSC_INSTRUCTION_AVAILABLE 8
-#define PF_PAE_ENABLED 9
-#define PF_XMMI64_INSTRUCTIONS_AVAILABLE 10
-#define PF_SSE_DAZ_MODE_AVAILABLE 11
-#define PF_NX_ENABLED 12
-
-    typedef struct _MEMORY_BASIC_INFORMATION {
-      PVOID BaseAddress;
-      PVOID AllocationBase;
-      DWORD AllocationProtect;
-      SIZE_T RegionSize;
-      DWORD State;
-      DWORD Protect;
-      DWORD Type;
-    } MEMORY_BASIC_INFORMATION,*PMEMORY_BASIC_INFORMATION;
-
-    typedef struct _MEMORY_BASIC_INFORMATION32 {
-      DWORD BaseAddress;
-      DWORD AllocationBase;
-      DWORD AllocationProtect;
-      DWORD RegionSize;
-      DWORD State;
-      DWORD Protect;
-      DWORD Type;
-    } MEMORY_BASIC_INFORMATION32,*PMEMORY_BASIC_INFORMATION32;
-
-    typedef DECLSPEC_ALIGN(16) struct _MEMORY_BASIC_INFORMATION64 {
-      ULONGLONG BaseAddress;
-      ULONGLONG AllocationBase;
-      DWORD AllocationProtect;
-      DWORD __alignment1;
-      ULONGLONG RegionSize;
-      DWORD State;
-      DWORD Protect;
-      DWORD Type;
-      DWORD __alignment2;
-    } MEMORY_BASIC_INFORMATION64,*PMEMORY_BASIC_INFORMATION64;
-
-#define SECTION_QUERY 0x0001
-#define SECTION_MAP_WRITE 0x0002
-#define SECTION_MAP_READ 0x0004
-#define SECTION_MAP_EXECUTE 0x0008
-#define SECTION_EXTEND_SIZE 0x0010
-#define SECTION_MAP_EXECUTE_EXPLICIT 0x0020
-
-#define SECTION_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SECTION_QUERY| SECTION_MAP_WRITE | SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_EXTEND_SIZE)
-#define PAGE_NOACCESS 0x01
-#define PAGE_READONLY 0x02
-#define PAGE_READWRITE 0x04
-#define PAGE_WRITECOPY 0x08
-#define PAGE_EXECUTE 0x10
-#define PAGE_EXECUTE_READ 0x20
-#define PAGE_EXECUTE_READWRITE 0x40
-#define PAGE_EXECUTE_WRITECOPY 0x80
-#define PAGE_GUARD 0x100
-#define PAGE_NOCACHE 0x200
-#define PAGE_WRITECOMBINE 0x400
-#define MEM_COMMIT 0x1000
-#define MEM_RESERVE 0x2000
-#define MEM_DECOMMIT 0x4000
-#define MEM_RELEASE 0x8000
-#define MEM_FREE 0x10000
-#define MEM_PRIVATE 0x20000
-#define MEM_MAPPED 0x40000
-#define MEM_RESET 0x80000
-#define MEM_TOP_DOWN 0x100000
-#define MEM_WRITE_WATCH 0x200000
-#define MEM_PHYSICAL 0x400000
-#define MEM_LARGE_PAGES 0x20000000
-#define MEM_4MB_PAGES 0x80000000
-#define SEC_FILE 0x800000
-#define SEC_IMAGE 0x1000000
-#define SEC_RESERVE 0x4000000
-#define SEC_COMMIT 0x8000000
-#define SEC_NOCACHE 0x10000000
-#define SEC_LARGE_PAGES 0x80000000
-#define MEM_IMAGE SEC_IMAGE
-#define WRITE_WATCH_FLAG_RESET 0x01
-
-#define FILE_READ_DATA (0x0001)
-#define FILE_LIST_DIRECTORY (0x0001)
-
-#define FILE_WRITE_DATA (0x0002)
-#define FILE_ADD_FILE (0x0002)
-
-#define FILE_APPEND_DATA (0x0004)
-#define FILE_ADD_SUBDIRECTORY (0x0004)
-#define FILE_CREATE_PIPE_INSTANCE (0x0004)
-
-#define FILE_READ_EA (0x0008)
-
-#define FILE_WRITE_EA (0x0010)
-
-#define FILE_EXECUTE (0x0020)
-#define FILE_TRAVERSE (0x0020)
-
-#define FILE_DELETE_CHILD (0x0040)
-
-#define FILE_READ_ATTRIBUTES (0x0080)
-
-#define FILE_WRITE_ATTRIBUTES (0x0100)
-
-#define FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF)
-#define FILE_GENERIC_READ (STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE)
-#define FILE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE)
-#define FILE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE)
-
-#define FILE_SHARE_READ 0x00000001
-#define FILE_SHARE_WRITE 0x00000002
-#define FILE_SHARE_DELETE 0x00000004
-#define FILE_ATTRIBUTE_READONLY 0x00000001
-#define FILE_ATTRIBUTE_HIDDEN 0x00000002
-#define FILE_ATTRIBUTE_SYSTEM 0x00000004
-#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
-#define FILE_ATTRIBUTE_ARCHIVE 0x00000020
-#define FILE_ATTRIBUTE_DEVICE 0x00000040
-#define FILE_ATTRIBUTE_NORMAL 0x00000080
-#define FILE_ATTRIBUTE_TEMPORARY 0x00000100
-#define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200
-#define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400
-#define FILE_ATTRIBUTE_COMPRESSED 0x00000800
-#define FILE_ATTRIBUTE_OFFLINE 0x00001000
-#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000
-#define FILE_ATTRIBUTE_ENCRYPTED 0x00004000
-#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001
-#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002
-#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004
-#define FILE_NOTIFY_CHANGE_SIZE 0x00000008
-#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010
-#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020
-#define FILE_NOTIFY_CHANGE_CREATION 0x00000040
-#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100
-#define FILE_ACTION_ADDED 0x00000001
-#define FILE_ACTION_REMOVED 0x00000002
-#define FILE_ACTION_MODIFIED 0x00000003
-#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004
-#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005
-#define MAILSLOT_NO_MESSAGE ((DWORD)-1)
-#define MAILSLOT_WAIT_FOREVER ((DWORD)-1)
-#define FILE_CASE_SENSITIVE_SEARCH 0x00000001
-#define FILE_CASE_PRESERVED_NAMES 0x00000002
-#define FILE_UNICODE_ON_DISK 0x00000004
-#define FILE_PERSISTENT_ACLS 0x00000008
-#define FILE_FILE_COMPRESSION 0x00000010
-#define FILE_VOLUME_QUOTAS 0x00000020
-#define FILE_SUPPORTS_SPARSE_FILES 0x00000040
-#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080
-#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100
-#define FILE_VOLUME_IS_COMPRESSED 0x00008000
-#define FILE_SUPPORTS_OBJECT_IDS 0x00010000
-#define FILE_SUPPORTS_ENCRYPTION 0x00020000
-#define FILE_NAMED_STREAMS 0x00040000
-#define FILE_READ_ONLY_VOLUME 0x00080000
-
-    typedef struct _FILE_NOTIFY_INFORMATION {
-      DWORD NextEntryOffset;
-      DWORD Action;
-      DWORD FileNameLength;
-      WCHAR FileName[1];
-    } FILE_NOTIFY_INFORMATION,*PFILE_NOTIFY_INFORMATION;
-
-    typedef union _FILE_SEGMENT_ELEMENT {
-      PVOID64 Buffer;
-      ULONGLONG Alignment;
-    }FILE_SEGMENT_ELEMENT,*PFILE_SEGMENT_ELEMENT;
-
-    typedef struct _REPARSE_GUID_DATA_BUFFER {
-      DWORD ReparseTag;
-      WORD ReparseDataLength;
-      WORD Reserved;
-      GUID ReparseGuid;
-      struct {
-	BYTE DataBuffer[1];
-      } GenericReparseBuffer;
-    } REPARSE_GUID_DATA_BUFFER,*PREPARSE_GUID_DATA_BUFFER;
-
-#define REPARSE_GUID_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_GUID_DATA_BUFFER,GenericReparseBuffer)
-
-#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16 *1024)
-
-#define IO_REPARSE_TAG_RESERVED_ZERO (0)
-#define IO_REPARSE_TAG_RESERVED_ONE (1)
-
-#define IO_REPARSE_TAG_RESERVED_RANGE IO_REPARSE_TAG_RESERVED_ONE
-
-#define IsReparseTagMicrosoft(_tag) (((_tag) & 0x80000000))
-#define IsReparseTagNameSurrogate(_tag) (((_tag) & 0x20000000))
-
-#define IO_REPARSE_TAG_MOUNT_POINT (0xA0000003L)
-#define IO_REPARSE_TAG_HSM (0xC0000004L)
-#define IO_REPARSE_TAG_SIS (0x80000007L)
-#define IO_REPARSE_TAG_DFS (0x8000000AL)
-#define IO_REPARSE_TAG_FILTER_MANAGER (0x8000000BL)
-#define IO_COMPLETION_MODIFY_STATE 0x0002
-#define IO_COMPLETION_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3)
-#define DUPLICATE_CLOSE_SOURCE 0x00000001
-#define DUPLICATE_SAME_ACCESS 0x00000002
-
-    typedef enum _SYSTEM_POWER_STATE {
-      PowerSystemUnspecified = 0,PowerSystemWorking = 1,PowerSystemSleeping1 = 2,PowerSystemSleeping2 = 3,PowerSystemSleeping3 = 4,PowerSystemHibernate = 5,PowerSystemShutdown = 6,PowerSystemMaximum = 7
-    } SYSTEM_POWER_STATE,*PSYSTEM_POWER_STATE;
-
-#define POWER_SYSTEM_MAXIMUM 7
-
-    typedef enum {
-      PowerActionNone = 0,PowerActionReserved,PowerActionSleep,PowerActionHibernate,PowerActionShutdown,PowerActionShutdownReset,PowerActionShutdownOff,PowerActionWarmEject
-    } POWER_ACTION,*PPOWER_ACTION;
-
-    typedef enum _DEVICE_POWER_STATE {
-      PowerDeviceUnspecified = 0,PowerDeviceD0,PowerDeviceD1,PowerDeviceD2,PowerDeviceD3,PowerDeviceMaximum
-    } DEVICE_POWER_STATE,*PDEVICE_POWER_STATE;
-
-#define ES_SYSTEM_REQUIRED ((DWORD)0x00000001)
-#define ES_DISPLAY_REQUIRED ((DWORD)0x00000002)
-#define ES_USER_PRESENT ((DWORD)0x00000004)
-#define ES_CONTINUOUS ((DWORD)0x80000000)
-
-    typedef DWORD EXECUTION_STATE;
-
-    typedef enum {
-      LT_DONT_CARE,LT_LOWEST_LATENCY
-    } LATENCY_TIME;
-
-#define PDCAP_D0_SUPPORTED 0x00000001
-#define PDCAP_D1_SUPPORTED 0x00000002
-#define PDCAP_D2_SUPPORTED 0x00000004
-#define PDCAP_D3_SUPPORTED 0x00000008
-#define PDCAP_WAKE_FROM_D0_SUPPORTED 0x00000010
-#define PDCAP_WAKE_FROM_D1_SUPPORTED 0x00000020
-#define PDCAP_WAKE_FROM_D2_SUPPORTED 0x00000040
-#define PDCAP_WAKE_FROM_D3_SUPPORTED 0x00000080
-#define PDCAP_WARM_EJECT_SUPPORTED 0x00000100
-
-    typedef struct CM_Power_Data_s {
-      DWORD PD_Size;
-      DEVICE_POWER_STATE PD_MostRecentPowerState;
-      DWORD PD_Capabilities;
-      DWORD PD_D1Latency;
-      DWORD PD_D2Latency;
-      DWORD PD_D3Latency;
-      DEVICE_POWER_STATE PD_PowerStateMapping[POWER_SYSTEM_MAXIMUM];
-      SYSTEM_POWER_STATE PD_DeepestSystemWake;
-    } CM_POWER_DATA,*PCM_POWER_DATA;
-
-    typedef enum {
-      SystemPowerPolicyAc,SystemPowerPolicyDc,VerifySystemPolicyAc,VerifySystemPolicyDc,SystemPowerCapabilities,SystemBatteryState,SystemPowerStateHandler,ProcessorStateHandler,SystemPowerPolicyCurrent,AdministratorPowerPolicy,SystemReserveHiberFile,ProcessorInformation,SystemPowerInformation,ProcessorStateHandler2,LastWakeTime,LastSleepTime,SystemExecutionState,SystemPowerStateNotifyHandler,ProcessorPowerPolicyAc,ProcessorPowerPolicyDc,VerifyProcessorPowerPolicyAc,VerifyProcessorPowerPolicyDc,ProcessorPowerPolicyCurrent,SystemPowerStateLogging,SystemPowerLoggingEntry
-    } POWER_INFORMATION_LEVEL;
-
-    typedef struct {
-      DWORD Granularity;
-      DWORD Capacity;
-    } BATTERY_REPORTING_SCALE,*PBATTERY_REPORTING_SCALE;
-
-    typedef struct {
-      POWER_ACTION Action;
-      DWORD Flags;
-      DWORD EventCode;
-    } POWER_ACTION_POLICY,*PPOWER_ACTION_POLICY;
-
-#define POWER_ACTION_QUERY_ALLOWED 0x00000001
-#define POWER_ACTION_UI_ALLOWED 0x00000002
-#define POWER_ACTION_OVERRIDE_APPS 0x00000004
-#define POWER_ACTION_LIGHTEST_FIRST 0x10000000
-#define POWER_ACTION_LOCK_CONSOLE 0x20000000
-#define POWER_ACTION_DISABLE_WAKES 0x40000000
-#define POWER_ACTION_CRITICAL 0x80000000
-
-#define POWER_LEVEL_USER_NOTIFY_TEXT 0x00000001
-#define POWER_LEVEL_USER_NOTIFY_SOUND 0x00000002
-#define POWER_LEVEL_USER_NOTIFY_EXEC 0x00000004
-#define POWER_USER_NOTIFY_BUTTON 0x00000008
-#define POWER_USER_NOTIFY_SHUTDOWN 0x00000010
-#define POWER_FORCE_TRIGGER_RESET 0x80000000
-
-    typedef struct {
-      BOOLEAN Enable;
-      BYTE Spare[3];
-      DWORD BatteryLevel;
-      POWER_ACTION_POLICY PowerPolicy;
-      SYSTEM_POWER_STATE MinSystemState;
-    } SYSTEM_POWER_LEVEL,*PSYSTEM_POWER_LEVEL;
-
-#define NUM_DISCHARGE_POLICIES 4
-#define DISCHARGE_POLICY_CRITICAL 0
-#define DISCHARGE_POLICY_LOW 1
-
-#define PO_THROTTLE_NONE 0
-#define PO_THROTTLE_CONSTANT 1
-#define PO_THROTTLE_DEGRADE 2
-#define PO_THROTTLE_ADAPTIVE 3
-#define PO_THROTTLE_MAXIMUM 4
-
-    typedef struct _SYSTEM_POWER_POLICY {
-      DWORD Revision;
-      POWER_ACTION_POLICY PowerButton;
-      POWER_ACTION_POLICY SleepButton;
-      POWER_ACTION_POLICY LidClose;
-      SYSTEM_POWER_STATE LidOpenWake;
-      DWORD Reserved;
-      POWER_ACTION_POLICY Idle;
-      DWORD IdleTimeout;
-      BYTE IdleSensitivity;
-      BYTE DynamicThrottle;
-      BYTE Spare2[2];
-      SYSTEM_POWER_STATE MinSleep;
-      SYSTEM_POWER_STATE MaxSleep;
-      SYSTEM_POWER_STATE ReducedLatencySleep;
-      DWORD WinLogonFlags;
-      DWORD Spare3;
-      DWORD DozeS4Timeout;
-      DWORD BroadcastCapacityResolution;
-      SYSTEM_POWER_LEVEL DischargePolicy[NUM_DISCHARGE_POLICIES];
-      DWORD VideoTimeout;
-      BOOLEAN VideoDimDisplay;
-      DWORD VideoReserved[3];
-      DWORD SpindownTimeout;
-      BOOLEAN OptimizeForPower;
-      BYTE FanThrottleTolerance;
-      BYTE ForcedThrottle;
-      BYTE MinThrottle;
-      POWER_ACTION_POLICY OverThrottled;
-    } SYSTEM_POWER_POLICY,*PSYSTEM_POWER_POLICY;
-
-    typedef struct _PROCESSOR_POWER_POLICY_INFO {
-      DWORD TimeCheck;
-      DWORD DemoteLimit;
-      DWORD PromoteLimit;
-      BYTE DemotePercent;
-      BYTE PromotePercent;
-      BYTE Spare[2];
-      DWORD AllowDemotion:1;
-      DWORD AllowPromotion:1;
-      DWORD Reserved:30;
-    } PROCESSOR_POWER_POLICY_INFO,*PPROCESSOR_POWER_POLICY_INFO;
-
-    typedef struct _PROCESSOR_POWER_POLICY {
-      DWORD Revision;
-      BYTE DynamicThrottle;
-      BYTE Spare[3];
-      DWORD DisableCStates:1;
-      DWORD Reserved:31;
-      DWORD PolicyCount;
-      PROCESSOR_POWER_POLICY_INFO Policy[3];
-    } PROCESSOR_POWER_POLICY,*PPROCESSOR_POWER_POLICY;
-
-    typedef struct _ADMINISTRATOR_POWER_POLICY {
-      SYSTEM_POWER_STATE MinSleep;
-      SYSTEM_POWER_STATE MaxSleep;
-      DWORD MinVideoTimeout;
-      DWORD MaxVideoTimeout;
-      DWORD MinSpindownTimeout;
-      DWORD MaxSpindownTimeout;
-    } ADMINISTRATOR_POWER_POLICY,*PADMINISTRATOR_POWER_POLICY;
-
-    typedef struct {
-      BOOLEAN PowerButtonPresent;
-      BOOLEAN SleepButtonPresent;
-      BOOLEAN LidPresent;
-      BOOLEAN SystemS1;
-      BOOLEAN SystemS2;
-      BOOLEAN SystemS3;
-      BOOLEAN SystemS4;
-      BOOLEAN SystemS5;
-      BOOLEAN HiberFilePresent;
-      BOOLEAN FullWake;
-      BOOLEAN VideoDimPresent;
-      BOOLEAN ApmPresent;
-      BOOLEAN UpsPresent;
-      BOOLEAN ThermalControl;
-      BOOLEAN ProcessorThrottle;
-      BYTE ProcessorMinThrottle;
-      BYTE ProcessorMaxThrottle;
-      BYTE spare2[4];
-      BOOLEAN DiskSpinDown;
-      BYTE spare3[8];
-      BOOLEAN SystemBatteriesPresent;
-      BOOLEAN BatteriesAreShortTerm;
-      BATTERY_REPORTING_SCALE BatteryScale[3];
-      SYSTEM_POWER_STATE AcOnLineWake;
-      SYSTEM_POWER_STATE SoftLidWake;
-      SYSTEM_POWER_STATE RtcWake;
-      SYSTEM_POWER_STATE MinDeviceWakeState;
-      SYSTEM_POWER_STATE DefaultLowLatencyWake;
-    } SYSTEM_POWER_CAPABILITIES,*PSYSTEM_POWER_CAPABILITIES;
-
-    typedef struct {
-      BOOLEAN AcOnLine;
-      BOOLEAN BatteryPresent;
-      BOOLEAN Charging;
-      BOOLEAN Discharging;
-      BOOLEAN Spare1[4];
-      DWORD MaxCapacity;
-      DWORD RemainingCapacity;
-      DWORD Rate;
-      DWORD EstimatedTime;
-      DWORD DefaultAlert1;
-      DWORD DefaultAlert2;
-    } SYSTEM_BATTERY_STATE,*PSYSTEM_BATTERY_STATE;
-
-#include "pshpack4.h"
-
-#define IMAGE_DOS_SIGNATURE 0x5A4D
-#define IMAGE_OS2_SIGNATURE 0x454E
-#define IMAGE_OS2_SIGNATURE_LE 0x454C
-#define IMAGE_VXD_SIGNATURE 0x454C
-#define IMAGE_NT_SIGNATURE 0x00004550
-
-#include "pshpack2.h"
-
-    typedef struct _IMAGE_DOS_HEADER {
-      WORD e_magic;
-      WORD e_cblp;
-      WORD e_cp;
-      WORD e_crlc;
-      WORD e_cparhdr;
-      WORD e_minalloc;
-      WORD e_maxalloc;
-      WORD e_ss;
-      WORD e_sp;
-      WORD e_csum;
-      WORD e_ip;
-      WORD e_cs;
-      WORD e_lfarlc;
-      WORD e_ovno;
-      WORD e_res[4];
-      WORD e_oemid;
-      WORD e_oeminfo;
-      WORD e_res2[10];
-      LONG e_lfanew;
-    } IMAGE_DOS_HEADER,*PIMAGE_DOS_HEADER;
-
-    typedef struct _IMAGE_OS2_HEADER {
-      WORD ne_magic;
-      CHAR ne_ver;
-      CHAR ne_rev;
-      WORD ne_enttab;
-      WORD ne_cbenttab;
-      LONG ne_crc;
-      WORD ne_flags;
-      WORD ne_autodata;
-      WORD ne_heap;
-      WORD ne_stack;
-      LONG ne_csip;
-      LONG ne_sssp;
-      WORD ne_cseg;
-      WORD ne_cmod;
-      WORD ne_cbnrestab;
-      WORD ne_segtab;
-      WORD ne_rsrctab;
-      WORD ne_restab;
-      WORD ne_modtab;
-      WORD ne_imptab;
-      LONG ne_nrestab;
-      WORD ne_cmovent;
-      WORD ne_align;
-      WORD ne_cres;
-      BYTE ne_exetyp;
-      BYTE ne_flagsothers;
-      WORD ne_pretthunks;
-      WORD ne_psegrefbytes;
-      WORD ne_swaparea;
-      WORD ne_expver;
-    } IMAGE_OS2_HEADER,*PIMAGE_OS2_HEADER;
-
-    typedef struct _IMAGE_VXD_HEADER {
-      WORD e32_magic;
-      BYTE e32_border;
-      BYTE e32_worder;
-      DWORD e32_level;
-      WORD e32_cpu;
-      WORD e32_os;
-      DWORD e32_ver;
-      DWORD e32_mflags;
-      DWORD e32_mpages;
-      DWORD e32_startobj;
-      DWORD e32_eip;
-      DWORD e32_stackobj;
-      DWORD e32_esp;
-      DWORD e32_pagesize;
-      DWORD e32_lastpagesize;
-      DWORD e32_fixupsize;
-      DWORD e32_fixupsum;
-      DWORD e32_ldrsize;
-      DWORD e32_ldrsum;
-      DWORD e32_objtab;
-      DWORD e32_objcnt;
-      DWORD e32_objmap;
-      DWORD e32_itermap;
-      DWORD e32_rsrctab;
-      DWORD e32_rsrccnt;
-      DWORD e32_restab;
-      DWORD e32_enttab;
-      DWORD e32_dirtab;
-      DWORD e32_dircnt;
-      DWORD e32_fpagetab;
-      DWORD e32_frectab;
-      DWORD e32_impmod;
-      DWORD e32_impmodcnt;
-      DWORD e32_impproc;
-      DWORD e32_pagesum;
-      DWORD e32_datapage;
-      DWORD e32_preload;
-      DWORD e32_nrestab;
-      DWORD e32_cbnrestab;
-      DWORD e32_nressum;
-      DWORD e32_autodata;
-      DWORD e32_debuginfo;
-      DWORD e32_debuglen;
-      DWORD e32_instpreload;
-      DWORD e32_instdemand;
-      DWORD e32_heapsize;
-      BYTE e32_res3[12];
-      DWORD e32_winresoff;
-      DWORD e32_winreslen;
-      WORD e32_devid;
-      WORD e32_ddkver;
-    } IMAGE_VXD_HEADER,*PIMAGE_VXD_HEADER;
-
-#include "poppack.h"
-
-    typedef struct _IMAGE_FILE_HEADER {
-      WORD Machine;
-      WORD NumberOfSections;
-      DWORD TimeDateStamp;
-      DWORD PointerToSymbolTable;
-      DWORD NumberOfSymbols;
-      WORD SizeOfOptionalHeader;
-      WORD Characteristics;
-    } IMAGE_FILE_HEADER,*PIMAGE_FILE_HEADER;
-
-#define IMAGE_SIZEOF_FILE_HEADER 20
-
-#define IMAGE_FILE_RELOCS_STRIPPED 0x0001
-#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002
-#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004
-#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008
-#define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010
-#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020
-#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080
-#define IMAGE_FILE_32BIT_MACHINE 0x0100
-#define IMAGE_FILE_DEBUG_STRIPPED 0x0200
-#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400
-#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800
-#define IMAGE_FILE_SYSTEM 0x1000
-#define IMAGE_FILE_DLL 0x2000
-#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000
-#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000
-
-#define IMAGE_FILE_MACHINE_UNKNOWN 0
-#define IMAGE_FILE_MACHINE_I386 0x014c
-#define IMAGE_FILE_MACHINE_R3000 0x0162
-#define IMAGE_FILE_MACHINE_R4000 0x0166
-#define IMAGE_FILE_MACHINE_R10000 0x0168
-#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169
-#define IMAGE_FILE_MACHINE_ALPHA 0x0184
-#define IMAGE_FILE_MACHINE_SH3 0x01a2
-#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3
-#define IMAGE_FILE_MACHINE_SH3E 0x01a4
-#define IMAGE_FILE_MACHINE_SH4 0x01a6
-#define IMAGE_FILE_MACHINE_SH5 0x01a8
-#define IMAGE_FILE_MACHINE_ARM 0x01c0
-#define IMAGE_FILE_MACHINE_THUMB 0x01c2
-#define IMAGE_FILE_MACHINE_AM33 0x01d3
-#define IMAGE_FILE_MACHINE_POWERPC 0x01F0
-#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1
-#define IMAGE_FILE_MACHINE_IA64 0x0200
-#define IMAGE_FILE_MACHINE_MIPS16 0x0266
-#define IMAGE_FILE_MACHINE_ALPHA64 0x0284
-#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366
-#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466
-#define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64
-#define IMAGE_FILE_MACHINE_TRICORE 0x0520
-#define IMAGE_FILE_MACHINE_CEF 0x0CEF
-#define IMAGE_FILE_MACHINE_EBC 0x0EBC
-#define IMAGE_FILE_MACHINE_AMD64 0x8664
-#define IMAGE_FILE_MACHINE_M32R 0x9041
-#define IMAGE_FILE_MACHINE_CEE 0xC0EE
-
-    typedef struct _IMAGE_DATA_DIRECTORY {
-      DWORD VirtualAddress;
-      DWORD Size;
-    } IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY;
-
-#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
-
-    typedef struct _IMAGE_OPTIONAL_HEADER {
-
-      WORD Magic;
-      BYTE MajorLinkerVersion;
-      BYTE MinorLinkerVersion;
-      DWORD SizeOfCode;
-      DWORD SizeOfInitializedData;
-      DWORD SizeOfUninitializedData;
-      DWORD AddressOfEntryPoint;
-      DWORD BaseOfCode;
-      DWORD BaseOfData;
-      DWORD ImageBase;
-      DWORD SectionAlignment;
-      DWORD FileAlignment;
-      WORD MajorOperatingSystemVersion;
-      WORD MinorOperatingSystemVersion;
-      WORD MajorImageVersion;
-      WORD MinorImageVersion;
-      WORD MajorSubsystemVersion;
-      WORD MinorSubsystemVersion;
-      DWORD Win32VersionValue;
-      DWORD SizeOfImage;
-      DWORD SizeOfHeaders;
-      DWORD CheckSum;
-      WORD Subsystem;
-      WORD DllCharacteristics;
-      DWORD SizeOfStackReserve;
-      DWORD SizeOfStackCommit;
-      DWORD SizeOfHeapReserve;
-      DWORD SizeOfHeapCommit;
-      DWORD LoaderFlags;
-      DWORD NumberOfRvaAndSizes;
-      IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
-    } IMAGE_OPTIONAL_HEADER32,*PIMAGE_OPTIONAL_HEADER32;
-
-    typedef struct _IMAGE_ROM_OPTIONAL_HEADER {
-      WORD Magic;
-      BYTE MajorLinkerVersion;
-      BYTE MinorLinkerVersion;
-      DWORD SizeOfCode;
-      DWORD SizeOfInitializedData;
-      DWORD SizeOfUninitializedData;
-      DWORD AddressOfEntryPoint;
-      DWORD BaseOfCode;
-      DWORD BaseOfData;
-      DWORD BaseOfBss;
-      DWORD GprMask;
-      DWORD CprMask[4];
-      DWORD GpValue;
-    } IMAGE_ROM_OPTIONAL_HEADER,*PIMAGE_ROM_OPTIONAL_HEADER;
-
-    typedef struct _IMAGE_OPTIONAL_HEADER64 {
-      WORD Magic;
-      BYTE MajorLinkerVersion;
-      BYTE MinorLinkerVersion;
-      DWORD SizeOfCode;
-      DWORD SizeOfInitializedData;
-      DWORD SizeOfUninitializedData;
-      DWORD AddressOfEntryPoint;
-      DWORD BaseOfCode;
-      ULONGLONG ImageBase;
-      DWORD SectionAlignment;
-      DWORD FileAlignment;
-      WORD MajorOperatingSystemVersion;
-      WORD MinorOperatingSystemVersion;
-      WORD MajorImageVersion;
-      WORD MinorImageVersion;
-      WORD MajorSubsystemVersion;
-      WORD MinorSubsystemVersion;
-      DWORD Win32VersionValue;
-      DWORD SizeOfImage;
-      DWORD SizeOfHeaders;
-      DWORD CheckSum;
-      WORD Subsystem;
-      WORD DllCharacteristics;
-      ULONGLONG SizeOfStackReserve;
-      ULONGLONG SizeOfStackCommit;
-      ULONGLONG SizeOfHeapReserve;
-      ULONGLONG SizeOfHeapCommit;
-      DWORD LoaderFlags;
-      DWORD NumberOfRvaAndSizes;
-      IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
-    } IMAGE_OPTIONAL_HEADER64,*PIMAGE_OPTIONAL_HEADER64;
-
-#define IMAGE_SIZEOF_ROM_OPTIONAL_HEADER 56
-#define IMAGE_SIZEOF_STD_OPTIONAL_HEADER 28
-#define IMAGE_SIZEOF_NT_OPTIONAL32_HEADER 224
-#define IMAGE_SIZEOF_NT_OPTIONAL64_HEADER 240
-
-#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b
-#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b
-#define IMAGE_ROM_OPTIONAL_HDR_MAGIC 0x107
-
-#ifdef _WIN64
-    typedef IMAGE_OPTIONAL_HEADER64 IMAGE_OPTIONAL_HEADER;
-    typedef PIMAGE_OPTIONAL_HEADER64 PIMAGE_OPTIONAL_HEADER;
-#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER IMAGE_SIZEOF_NT_OPTIONAL64_HEADER
-#define IMAGE_NT_OPTIONAL_HDR_MAGIC IMAGE_NT_OPTIONAL_HDR64_MAGIC
-#else
-    typedef IMAGE_OPTIONAL_HEADER32 IMAGE_OPTIONAL_HEADER;
-    typedef PIMAGE_OPTIONAL_HEADER32 PIMAGE_OPTIONAL_HEADER;
-#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER IMAGE_SIZEOF_NT_OPTIONAL32_HEADER
-#define IMAGE_NT_OPTIONAL_HDR_MAGIC IMAGE_NT_OPTIONAL_HDR32_MAGIC
-#endif
-
-    typedef struct _IMAGE_NT_HEADERS64 {
-      DWORD Signature;
-      IMAGE_FILE_HEADER FileHeader;
-      IMAGE_OPTIONAL_HEADER64 OptionalHeader;
-    } IMAGE_NT_HEADERS64,*PIMAGE_NT_HEADERS64;
-
-    typedef struct _IMAGE_NT_HEADERS {
-      DWORD Signature;
-      IMAGE_FILE_HEADER FileHeader;
-      IMAGE_OPTIONAL_HEADER32 OptionalHeader;
-    } IMAGE_NT_HEADERS32,*PIMAGE_NT_HEADERS32;
-
-    typedef struct _IMAGE_ROM_HEADERS {
-      IMAGE_FILE_HEADER FileHeader;
-      IMAGE_ROM_OPTIONAL_HEADER OptionalHeader;
-    } IMAGE_ROM_HEADERS,*PIMAGE_ROM_HEADERS;
-
-#ifdef _WIN64
-    typedef IMAGE_NT_HEADERS64 IMAGE_NT_HEADERS;
-    typedef PIMAGE_NT_HEADERS64 PIMAGE_NT_HEADERS;
-#else
-    typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS;
-    typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS;
-#endif
-
-#define IMAGE_FIRST_SECTION(ntheader) ((PIMAGE_SECTION_HEADER) ((ULONG_PTR)ntheader + FIELD_OFFSET(IMAGE_NT_HEADERS,OptionalHeader) + ((PIMAGE_NT_HEADERS)(ntheader))->FileHeader.SizeOfOptionalHeader))
-
-#define IMAGE_SUBSYSTEM_UNKNOWN 0
-#define IMAGE_SUBSYSTEM_NATIVE 1
-#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2
-#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3
-#define IMAGE_SUBSYSTEM_OS2_CUI 5
-#define IMAGE_SUBSYSTEM_POSIX_CUI 7
-#define IMAGE_SUBSYSTEM_NATIVE_WINDOWS 8
-#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9
-#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10
-#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11
-#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12
-#define IMAGE_SUBSYSTEM_EFI_ROM 13
-#define IMAGE_SUBSYSTEM_XBOX 14
-
-#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200
-#define IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400
-#define IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800
-#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000
-#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000
-
-#define IMAGE_DIRECTORY_ENTRY_EXPORT 0
-#define IMAGE_DIRECTORY_ENTRY_IMPORT 1
-#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
-#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3
-#define IMAGE_DIRECTORY_ENTRY_SECURITY 4
-#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
-#define IMAGE_DIRECTORY_ENTRY_DEBUG 6
-
-#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7
-#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8
-#define IMAGE_DIRECTORY_ENTRY_TLS 9
-#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10
-#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11
-#define IMAGE_DIRECTORY_ENTRY_IAT 12
-#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13
-#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14
-
-    typedef struct ANON_OBJECT_HEADER {
-      WORD Sig1;
-      WORD Sig2;
-      WORD Version;
-      WORD Machine;
-      DWORD TimeDateStamp;
-      CLSID ClassID;
-      DWORD SizeOfData;
-    } ANON_OBJECT_HEADER;
-
-#define IMAGE_SIZEOF_SHORT_NAME 8
-
-    typedef struct _IMAGE_SECTION_HEADER {
-      BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
-      union {
-	DWORD PhysicalAddress;
-	DWORD VirtualSize;
-      } Misc;
-      DWORD VirtualAddress;
-      DWORD SizeOfRawData;
-      DWORD PointerToRawData;
-      DWORD PointerToRelocations;
-      DWORD PointerToLinenumbers;
-      WORD NumberOfRelocations;
-      WORD NumberOfLinenumbers;
-      DWORD Characteristics;
-    } IMAGE_SECTION_HEADER,*PIMAGE_SECTION_HEADER;
-
-#define IMAGE_SIZEOF_SECTION_HEADER 40
-
-#define IMAGE_SCN_TYPE_NO_PAD 0x00000008
-
-#define IMAGE_SCN_CNT_CODE 0x00000020
-#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040
-#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080
-#define IMAGE_SCN_LNK_OTHER 0x00000100
-#define IMAGE_SCN_LNK_INFO 0x00000200
-#define IMAGE_SCN_LNK_REMOVE 0x00000800
-#define IMAGE_SCN_LNK_COMDAT 0x00001000
-#define IMAGE_SCN_NO_DEFER_SPEC_EXC 0x00004000
-#define IMAGE_SCN_GPREL 0x00008000
-#define IMAGE_SCN_MEM_FARDATA 0x00008000
-#define IMAGE_SCN_MEM_PURGEABLE 0x00020000
-#define IMAGE_SCN_MEM_16BIT 0x00020000
-#define IMAGE_SCN_MEM_LOCKED 0x00040000
-#define IMAGE_SCN_MEM_PRELOAD 0x00080000
-
-#define IMAGE_SCN_ALIGN_1BYTES 0x00100000
-#define IMAGE_SCN_ALIGN_2BYTES 0x00200000
-#define IMAGE_SCN_ALIGN_4BYTES 0x00300000
-#define IMAGE_SCN_ALIGN_8BYTES 0x00400000
-#define IMAGE_SCN_ALIGN_16BYTES 0x00500000
-#define IMAGE_SCN_ALIGN_32BYTES 0x00600000
-#define IMAGE_SCN_ALIGN_64BYTES 0x00700000
-#define IMAGE_SCN_ALIGN_128BYTES 0x00800000
-#define IMAGE_SCN_ALIGN_256BYTES 0x00900000
-#define IMAGE_SCN_ALIGN_512BYTES 0x00A00000
-#define IMAGE_SCN_ALIGN_1024BYTES 0x00B00000
-#define IMAGE_SCN_ALIGN_2048BYTES 0x00C00000
-#define IMAGE_SCN_ALIGN_4096BYTES 0x00D00000
-#define IMAGE_SCN_ALIGN_8192BYTES 0x00E00000
-
-#define IMAGE_SCN_ALIGN_MASK 0x00F00000
-
-#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000
-#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000
-#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000
-#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000
-#define IMAGE_SCN_MEM_SHARED 0x10000000
-#define IMAGE_SCN_MEM_EXECUTE 0x20000000
-#define IMAGE_SCN_MEM_READ 0x40000000
-#define IMAGE_SCN_MEM_WRITE 0x80000000
-
-#define IMAGE_SCN_SCALE_INDEX 0x00000001
-
-#include "pshpack2.h"
-
-    typedef struct _IMAGE_SYMBOL {
-      union {
-	BYTE ShortName[8];
-	struct {
-	  DWORD Short;
-	  DWORD Long;
-	} Name;
-	DWORD LongName[2];
-      } N;
-      DWORD Value;
-      SHORT SectionNumber;
-      WORD Type;
-      BYTE StorageClass;
-      BYTE NumberOfAuxSymbols;
-    } IMAGE_SYMBOL;
-    typedef IMAGE_SYMBOL UNALIGNED *PIMAGE_SYMBOL;
-
-#define IMAGE_SIZEOF_SYMBOL 18
-
-#define IMAGE_SYM_UNDEFINED (SHORT)0
-#define IMAGE_SYM_ABSOLUTE (SHORT)-1
-#define IMAGE_SYM_DEBUG (SHORT)-2
-#define IMAGE_SYM_SECTION_MAX 0xFEFF
-
-#define IMAGE_SYM_TYPE_NULL 0x0000
-#define IMAGE_SYM_TYPE_VOID 0x0001
-#define IMAGE_SYM_TYPE_CHAR 0x0002
-#define IMAGE_SYM_TYPE_SHORT 0x0003
-#define IMAGE_SYM_TYPE_INT 0x0004
-#define IMAGE_SYM_TYPE_LONG 0x0005
-#define IMAGE_SYM_TYPE_FLOAT 0x0006
-#define IMAGE_SYM_TYPE_DOUBLE 0x0007
-#define IMAGE_SYM_TYPE_STRUCT 0x0008
-#define IMAGE_SYM_TYPE_UNION 0x0009
-#define IMAGE_SYM_TYPE_ENUM 0x000A
-#define IMAGE_SYM_TYPE_MOE 0x000B
-#define IMAGE_SYM_TYPE_BYTE 0x000C
-#define IMAGE_SYM_TYPE_WORD 0x000D
-#define IMAGE_SYM_TYPE_UINT 0x000E
-#define IMAGE_SYM_TYPE_DWORD 0x000F
-#define IMAGE_SYM_TYPE_PCODE 0x8000
-
-#define IMAGE_SYM_DTYPE_NULL 0
-#define IMAGE_SYM_DTYPE_POINTER 1
-#define IMAGE_SYM_DTYPE_FUNCTION 2
-#define IMAGE_SYM_DTYPE_ARRAY 3
-
-#define IMAGE_SYM_CLASS_END_OF_FUNCTION (BYTE)-1
-#define IMAGE_SYM_CLASS_NULL 0x0000
-#define IMAGE_SYM_CLASS_AUTOMATIC 0x0001
-#define IMAGE_SYM_CLASS_EXTERNAL 0x0002
-#define IMAGE_SYM_CLASS_STATIC 0x0003
-#define IMAGE_SYM_CLASS_REGISTER 0x0004
-#define IMAGE_SYM_CLASS_EXTERNAL_DEF 0x0005
-#define IMAGE_SYM_CLASS_LABEL 0x0006
-#define IMAGE_SYM_CLASS_UNDEFINED_LABEL 0x0007
-#define IMAGE_SYM_CLASS_MEMBER_OF_STRUCT 0x0008
-#define IMAGE_SYM_CLASS_ARGUMENT 0x0009
-#define IMAGE_SYM_CLASS_STRUCT_TAG 0x000A
-#define IMAGE_SYM_CLASS_MEMBER_OF_UNION 0x000B
-#define IMAGE_SYM_CLASS_UNION_TAG 0x000C
-#define IMAGE_SYM_CLASS_TYPE_DEFINITION 0x000D
-#define IMAGE_SYM_CLASS_UNDEFINED_STATIC 0x000E
-#define IMAGE_SYM_CLASS_ENUM_TAG 0x000F
-#define IMAGE_SYM_CLASS_MEMBER_OF_ENUM 0x0010
-#define IMAGE_SYM_CLASS_REGISTER_PARAM 0x0011
-#define IMAGE_SYM_CLASS_BIT_FIELD 0x0012
-#define IMAGE_SYM_CLASS_FAR_EXTERNAL 0x0044
-#define IMAGE_SYM_CLASS_BLOCK 0x0064
-#define IMAGE_SYM_CLASS_FUNCTION 0x0065
-#define IMAGE_SYM_CLASS_END_OF_STRUCT 0x0066
-#define IMAGE_SYM_CLASS_FILE 0x0067
-#define IMAGE_SYM_CLASS_SECTION 0x0068
-#define IMAGE_SYM_CLASS_WEAK_EXTERNAL 0x0069
-#define IMAGE_SYM_CLASS_CLR_TOKEN 0x006B
-
-#define N_BTMASK 0x000F
-#define N_TMASK 0x0030
-#define N_TMASK1 0x00C0
-#define N_TMASK2 0x00F0
-#define N_BTSHFT 4
-#define N_TSHIFT 2
-
-#define BTYPE(x) ((x) & N_BTMASK)
-
-#ifndef ISPTR
-#define ISPTR(x) (((x) & N_TMASK)==(IMAGE_SYM_DTYPE_POINTER << N_BTSHFT))
-#endif
-
-#ifndef ISFCN
-#define ISFCN(x) (((x) & N_TMASK)==(IMAGE_SYM_DTYPE_FUNCTION << N_BTSHFT))
-#endif
-
-#ifndef ISARY
-#define ISARY(x) (((x) & N_TMASK)==(IMAGE_SYM_DTYPE_ARRAY << N_BTSHFT))
-#endif
-
-#ifndef ISTAG
-#define ISTAG(x) ((x)==IMAGE_SYM_CLASS_STRUCT_TAG || (x)==IMAGE_SYM_CLASS_UNION_TAG || (x)==IMAGE_SYM_CLASS_ENUM_TAG)
-#endif
-
-#ifndef INCREF
-#define INCREF(x) ((((x)&~N_BTMASK)<<N_TSHIFT)|(IMAGE_SYM_DTYPE_POINTER<<N_BTSHFT)|((x)&N_BTMASK))
-#endif
-#ifndef DECREF
-#define DECREF(x) ((((x)>>N_TSHIFT)&~N_BTMASK)|((x)&N_BTMASK))
-#endif
-
-    typedef union _IMAGE_AUX_SYMBOL {
-      struct {
-	DWORD TagIndex;
-	union {
-	  struct {
-	    WORD Linenumber;
-	    WORD Size;
-	  } LnSz;
-	  DWORD TotalSize;
-	} Misc;
-	union {
-	  struct {
-	    DWORD PointerToLinenumber;
-	    DWORD PointerToNextFunction;
-	  } Function;
-	  struct {
-	    WORD Dimension[4];
-	  } Array;
-	} FcnAry;
-	WORD TvIndex;
-      } Sym;
-      struct {
-	BYTE Name[IMAGE_SIZEOF_SYMBOL];
-      } File;
-      struct {
-	DWORD Length;
-	WORD NumberOfRelocations;
-	WORD NumberOfLinenumbers;
-	DWORD CheckSum;
-	SHORT Number;
-	BYTE Selection;
-      } Section;
-    } IMAGE_AUX_SYMBOL;
-    typedef IMAGE_AUX_SYMBOL UNALIGNED *PIMAGE_AUX_SYMBOL;
-
-#define IMAGE_SIZEOF_AUX_SYMBOL 18
-
-    typedef enum IMAGE_AUX_SYMBOL_TYPE {
-      IMAGE_AUX_SYMBOL_TYPE_TOKEN_DEF = 1
-    } IMAGE_AUX_SYMBOL_TYPE;
-
-#include <pshpack2.h>
-
-    typedef struct IMAGE_AUX_SYMBOL_TOKEN_DEF {
-      BYTE bAuxType;
-      BYTE bReserved;
-      DWORD SymbolTableIndex;
-      BYTE rgbReserved[12];
-    } IMAGE_AUX_SYMBOL_TOKEN_DEF;
-
-    typedef IMAGE_AUX_SYMBOL_TOKEN_DEF UNALIGNED *PIMAGE_AUX_SYMBOL_TOKEN_DEF;
-
-#include <poppack.h>
-
-#define IMAGE_COMDAT_SELECT_NODUPLICATES 1
-#define IMAGE_COMDAT_SELECT_ANY 2
-#define IMAGE_COMDAT_SELECT_SAME_SIZE 3
-#define IMAGE_COMDAT_SELECT_EXACT_MATCH 4
-#define IMAGE_COMDAT_SELECT_ASSOCIATIVE 5
-#define IMAGE_COMDAT_SELECT_LARGEST 6
-#define IMAGE_COMDAT_SELECT_NEWEST 7
-
-#define IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY 1
-#define IMAGE_WEAK_EXTERN_SEARCH_LIBRARY 2
-#define IMAGE_WEAK_EXTERN_SEARCH_ALIAS 3
-
-    typedef struct _IMAGE_RELOCATION {
-      union {
-	DWORD VirtualAddress;
-	DWORD RelocCount;
-      };
-      DWORD SymbolTableIndex;
-      WORD Type;
-    } IMAGE_RELOCATION;
-    typedef IMAGE_RELOCATION UNALIGNED *PIMAGE_RELOCATION;
-
-#define IMAGE_SIZEOF_RELOCATION 10
-
-#define IMAGE_REL_I386_ABSOLUTE 0x0000
-#define IMAGE_REL_I386_DIR16 0x0001
-#define IMAGE_REL_I386_REL16 0x0002
-#define IMAGE_REL_I386_DIR32 0x0006
-#define IMAGE_REL_I386_DIR32NB 0x0007
-#define IMAGE_REL_I386_SEG12 0x0009
-#define IMAGE_REL_I386_SECTION 0x000A
-#define IMAGE_REL_I386_SECREL 0x000B
-#define IMAGE_REL_I386_TOKEN 0x000C
-#define IMAGE_REL_I386_SECREL7 0x000D
-#define IMAGE_REL_I386_REL32 0x0014
-
-#define IMAGE_REL_MIPS_ABSOLUTE 0x0000
-#define IMAGE_REL_MIPS_REFHALF 0x0001
-#define IMAGE_REL_MIPS_REFWORD 0x0002
-#define IMAGE_REL_MIPS_JMPADDR 0x0003
-#define IMAGE_REL_MIPS_REFHI 0x0004
-#define IMAGE_REL_MIPS_REFLO 0x0005
-#define IMAGE_REL_MIPS_GPREL 0x0006
-#define IMAGE_REL_MIPS_LITERAL 0x0007
-#define IMAGE_REL_MIPS_SECTION 0x000A
-#define IMAGE_REL_MIPS_SECREL 0x000B
-#define IMAGE_REL_MIPS_SECRELLO 0x000C
-#define IMAGE_REL_MIPS_SECRELHI 0x000D
-#define IMAGE_REL_MIPS_TOKEN 0x000E
-#define IMAGE_REL_MIPS_JMPADDR16 0x0010
-#define IMAGE_REL_MIPS_REFWORDNB 0x0022
-#define IMAGE_REL_MIPS_PAIR 0x0025
-
-#define IMAGE_REL_ALPHA_ABSOLUTE 0x0000
-#define IMAGE_REL_ALPHA_REFLONG 0x0001
-#define IMAGE_REL_ALPHA_REFQUAD 0x0002
-#define IMAGE_REL_ALPHA_GPREL32 0x0003
-#define IMAGE_REL_ALPHA_LITERAL 0x0004
-#define IMAGE_REL_ALPHA_LITUSE 0x0005
-#define IMAGE_REL_ALPHA_GPDISP 0x0006
-#define IMAGE_REL_ALPHA_BRADDR 0x0007
-#define IMAGE_REL_ALPHA_HINT 0x0008
-#define IMAGE_REL_ALPHA_INLINE_REFLONG 0x0009
-#define IMAGE_REL_ALPHA_REFHI 0x000A
-#define IMAGE_REL_ALPHA_REFLO 0x000B
-#define IMAGE_REL_ALPHA_PAIR 0x000C
-#define IMAGE_REL_ALPHA_MATCH 0x000D
-#define IMAGE_REL_ALPHA_SECTION 0x000E
-#define IMAGE_REL_ALPHA_SECREL 0x000F
-#define IMAGE_REL_ALPHA_REFLONGNB 0x0010
-#define IMAGE_REL_ALPHA_SECRELLO 0x0011
-#define IMAGE_REL_ALPHA_SECRELHI 0x0012
-#define IMAGE_REL_ALPHA_REFQ3 0x0013
-#define IMAGE_REL_ALPHA_REFQ2 0x0014
-#define IMAGE_REL_ALPHA_REFQ1 0x0015
-#define IMAGE_REL_ALPHA_GPRELLO 0x0016
-#define IMAGE_REL_ALPHA_GPRELHI 0x0017
-
-#define IMAGE_REL_PPC_ABSOLUTE 0x0000
-#define IMAGE_REL_PPC_ADDR64 0x0001
-#define IMAGE_REL_PPC_ADDR32 0x0002
-#define IMAGE_REL_PPC_ADDR24 0x0003
-#define IMAGE_REL_PPC_ADDR16 0x0004
-#define IMAGE_REL_PPC_ADDR14 0x0005
-#define IMAGE_REL_PPC_REL24 0x0006
-#define IMAGE_REL_PPC_REL14 0x0007
-#define IMAGE_REL_PPC_TOCREL16 0x0008
-#define IMAGE_REL_PPC_TOCREL14 0x0009
-#define IMAGE_REL_PPC_ADDR32NB 0x000A
-#define IMAGE_REL_PPC_SECREL 0x000B
-#define IMAGE_REL_PPC_SECTION 0x000C
-#define IMAGE_REL_PPC_IFGLUE 0x000D
-#define IMAGE_REL_PPC_IMGLUE 0x000E
-#define IMAGE_REL_PPC_SECREL16 0x000F
-#define IMAGE_REL_PPC_REFHI 0x0010
-#define IMAGE_REL_PPC_REFLO 0x0011
-#define IMAGE_REL_PPC_PAIR 0x0012
-#define IMAGE_REL_PPC_SECRELLO 0x0013
-#define IMAGE_REL_PPC_SECRELHI 0x0014
-#define IMAGE_REL_PPC_GPREL 0x0015
-#define IMAGE_REL_PPC_TOKEN 0x0016
-#define IMAGE_REL_PPC_TYPEMASK 0x00FF
-#define IMAGE_REL_PPC_NEG 0x0100
-#define IMAGE_REL_PPC_BRTAKEN 0x0200
-#define IMAGE_REL_PPC_BRNTAKEN 0x0400
-#define IMAGE_REL_PPC_TOCDEFN 0x0800
-
-#define IMAGE_REL_SH3_ABSOLUTE 0x0000
-#define IMAGE_REL_SH3_DIRECT16 0x0001
-#define IMAGE_REL_SH3_DIRECT32 0x0002
-#define IMAGE_REL_SH3_DIRECT8 0x0003
-#define IMAGE_REL_SH3_DIRECT8_WORD 0x0004
-#define IMAGE_REL_SH3_DIRECT8_LONG 0x0005
-#define IMAGE_REL_SH3_DIRECT4 0x0006
-#define IMAGE_REL_SH3_DIRECT4_WORD 0x0007
-#define IMAGE_REL_SH3_DIRECT4_LONG 0x0008
-#define IMAGE_REL_SH3_PCREL8_WORD 0x0009
-#define IMAGE_REL_SH3_PCREL8_LONG 0x000A
-#define IMAGE_REL_SH3_PCREL12_WORD 0x000B
-#define IMAGE_REL_SH3_STARTOF_SECTION 0x000C
-#define IMAGE_REL_SH3_SIZEOF_SECTION 0x000D
-#define IMAGE_REL_SH3_SECTION 0x000E
-#define IMAGE_REL_SH3_SECREL 0x000F
-#define IMAGE_REL_SH3_DIRECT32_NB 0x0010
-#define IMAGE_REL_SH3_GPREL4_LONG 0x0011
-#define IMAGE_REL_SH3_TOKEN 0x0012
-
-#define IMAGE_REL_SHM_PCRELPT 0x0013
-#define IMAGE_REL_SHM_REFLO 0x0014
-#define IMAGE_REL_SHM_REFHALF 0x0015
-#define IMAGE_REL_SHM_RELLO 0x0016
-#define IMAGE_REL_SHM_RELHALF 0x0017
-#define IMAGE_REL_SHM_PAIR 0x0018
-
-#define IMAGE_REL_SH_NOMODE 0x8000
-
-#define IMAGE_REL_ARM_ABSOLUTE 0x0000
-#define IMAGE_REL_ARM_ADDR32 0x0001
-#define IMAGE_REL_ARM_ADDR32NB 0x0002
-#define IMAGE_REL_ARM_BRANCH24 0x0003
-#define IMAGE_REL_ARM_BRANCH11 0x0004
-#define IMAGE_REL_ARM_TOKEN 0x0005
-#define IMAGE_REL_ARM_GPREL12 0x0006
-#define IMAGE_REL_ARM_GPREL7 0x0007
-#define IMAGE_REL_ARM_BLX24 0x0008
-#define IMAGE_REL_ARM_BLX11 0x0009
-#define IMAGE_REL_ARM_SECTION 0x000E
-#define IMAGE_REL_ARM_SECREL 0x000F
-
-#define IMAGE_REL_AM_ABSOLUTE 0x0000
-#define IMAGE_REL_AM_ADDR32 0x0001
-#define IMAGE_REL_AM_ADDR32NB 0x0002
-#define IMAGE_REL_AM_CALL32 0x0003
-#define IMAGE_REL_AM_FUNCINFO 0x0004
-#define IMAGE_REL_AM_REL32_1 0x0005
-#define IMAGE_REL_AM_REL32_2 0x0006
-#define IMAGE_REL_AM_SECREL 0x0007
-#define IMAGE_REL_AM_SECTION 0x0008
-#define IMAGE_REL_AM_TOKEN 0x0009
-
-#define IMAGE_REL_AMD64_ABSOLUTE 0x0000
-#define IMAGE_REL_AMD64_ADDR64 0x0001
-#define IMAGE_REL_AMD64_ADDR32 0x0002
-#define IMAGE_REL_AMD64_ADDR32NB 0x0003
-#define IMAGE_REL_AMD64_REL32 0x0004
-#define IMAGE_REL_AMD64_REL32_1 0x0005
-#define IMAGE_REL_AMD64_REL32_2 0x0006
-#define IMAGE_REL_AMD64_REL32_3 0x0007
-#define IMAGE_REL_AMD64_REL32_4 0x0008
-#define IMAGE_REL_AMD64_REL32_5 0x0009
-#define IMAGE_REL_AMD64_SECTION 0x000A
-#define IMAGE_REL_AMD64_SECREL 0x000B
-#define IMAGE_REL_AMD64_SECREL7 0x000C
-#define IMAGE_REL_AMD64_TOKEN 0x000D
-#define IMAGE_REL_AMD64_SREL32 0x000E
-#define IMAGE_REL_AMD64_PAIR 0x000F
-#define IMAGE_REL_AMD64_SSPAN32 0x0010
-
-#define IMAGE_REL_IA64_ABSOLUTE 0x0000
-#define IMAGE_REL_IA64_IMM14 0x0001
-#define IMAGE_REL_IA64_IMM22 0x0002
-#define IMAGE_REL_IA64_IMM64 0x0003
-#define IMAGE_REL_IA64_DIR32 0x0004
-#define IMAGE_REL_IA64_DIR64 0x0005
-#define IMAGE_REL_IA64_PCREL21B 0x0006
-#define IMAGE_REL_IA64_PCREL21M 0x0007
-#define IMAGE_REL_IA64_PCREL21F 0x0008
-#define IMAGE_REL_IA64_GPREL22 0x0009
-#define IMAGE_REL_IA64_LTOFF22 0x000A
-#define IMAGE_REL_IA64_SECTION 0x000B
-#define IMAGE_REL_IA64_SECREL22 0x000C
-#define IMAGE_REL_IA64_SECREL64I 0x000D
-#define IMAGE_REL_IA64_SECREL32 0x000E
-
-#define IMAGE_REL_IA64_DIR32NB 0x0010
-#define IMAGE_REL_IA64_SREL14 0x0011
-#define IMAGE_REL_IA64_SREL22 0x0012
-#define IMAGE_REL_IA64_SREL32 0x0013
-#define IMAGE_REL_IA64_UREL32 0x0014
-#define IMAGE_REL_IA64_PCREL60X 0x0015
-#define IMAGE_REL_IA64_PCREL60B 0x0016
-#define IMAGE_REL_IA64_PCREL60F 0x0017
-#define IMAGE_REL_IA64_PCREL60I 0x0018
-#define IMAGE_REL_IA64_PCREL60M 0x0019
-#define IMAGE_REL_IA64_IMMGPREL64 0x001A
-#define IMAGE_REL_IA64_TOKEN 0x001B
-#define IMAGE_REL_IA64_GPREL32 0x001C
-#define IMAGE_REL_IA64_ADDEND 0x001F
-
-#define IMAGE_REL_CEF_ABSOLUTE 0x0000
-#define IMAGE_REL_CEF_ADDR32 0x0001
-#define IMAGE_REL_CEF_ADDR64 0x0002
-#define IMAGE_REL_CEF_ADDR32NB 0x0003
-#define IMAGE_REL_CEF_SECTION 0x0004
-#define IMAGE_REL_CEF_SECREL 0x0005
-#define IMAGE_REL_CEF_TOKEN 0x0006
-
-#define IMAGE_REL_CEE_ABSOLUTE 0x0000
-#define IMAGE_REL_CEE_ADDR32 0x0001
-#define IMAGE_REL_CEE_ADDR64 0x0002
-#define IMAGE_REL_CEE_ADDR32NB 0x0003
-#define IMAGE_REL_CEE_SECTION 0x0004
-#define IMAGE_REL_CEE_SECREL 0x0005
-#define IMAGE_REL_CEE_TOKEN 0x0006
-
-#define IMAGE_REL_M32R_ABSOLUTE 0x0000
-#define IMAGE_REL_M32R_ADDR32 0x0001
-#define IMAGE_REL_M32R_ADDR32NB 0x0002
-#define IMAGE_REL_M32R_ADDR24 0x0003
-#define IMAGE_REL_M32R_GPREL16 0x0004
-#define IMAGE_REL_M32R_PCREL24 0x0005
-#define IMAGE_REL_M32R_PCREL16 0x0006
-#define IMAGE_REL_M32R_PCREL8 0x0007
-#define IMAGE_REL_M32R_REFHALF 0x0008
-#define IMAGE_REL_M32R_REFHI 0x0009
-#define IMAGE_REL_M32R_REFLO 0x000A
-#define IMAGE_REL_M32R_PAIR 0x000B
-#define IMAGE_REL_M32R_SECTION 0x000C
-#define IMAGE_REL_M32R_SECREL32 0x000D
-#define IMAGE_REL_M32R_TOKEN 0x000E
-
-#define EXT_IMM64(Value,Address,Size,InstPos,ValPos) Value |= (((ULONGLONG)((*(Address) >> InstPos) & (((ULONGLONG)1 << Size) - 1))) << ValPos)
-#define INS_IMM64(Value,Address,Size,InstPos,ValPos) *(PDWORD)Address = (*(PDWORD)Address & ~(((1 << Size) - 1) << InstPos)) | ((DWORD)((((ULONGLONG)Value >> ValPos) & (((ULONGLONG)1 << Size) - 1))) << InstPos)
-
-#define EMARCH_ENC_I17_IMM7B_INST_WORD_X 3
-#define EMARCH_ENC_I17_IMM7B_SIZE_X 7
-#define EMARCH_ENC_I17_IMM7B_INST_WORD_POS_X 4
-#define EMARCH_ENC_I17_IMM7B_VAL_POS_X 0
-
-#define EMARCH_ENC_I17_IMM9D_INST_WORD_X 3
-#define EMARCH_ENC_I17_IMM9D_SIZE_X 9
-#define EMARCH_ENC_I17_IMM9D_INST_WORD_POS_X 18
-#define EMARCH_ENC_I17_IMM9D_VAL_POS_X 7
-
-#define EMARCH_ENC_I17_IMM5C_INST_WORD_X 3
-#define EMARCH_ENC_I17_IMM5C_SIZE_X 5
-#define EMARCH_ENC_I17_IMM5C_INST_WORD_POS_X 13
-#define EMARCH_ENC_I17_IMM5C_VAL_POS_X 16
-
-#define EMARCH_ENC_I17_IC_INST_WORD_X 3
-#define EMARCH_ENC_I17_IC_SIZE_X 1
-#define EMARCH_ENC_I17_IC_INST_WORD_POS_X 12
-#define EMARCH_ENC_I17_IC_VAL_POS_X 21
-
-#define EMARCH_ENC_I17_IMM41a_INST_WORD_X 1
-#define EMARCH_ENC_I17_IMM41a_SIZE_X 10
-#define EMARCH_ENC_I17_IMM41a_INST_WORD_POS_X 14
-#define EMARCH_ENC_I17_IMM41a_VAL_POS_X 22
-
-#define EMARCH_ENC_I17_IMM41b_INST_WORD_X 1
-#define EMARCH_ENC_I17_IMM41b_SIZE_X 8
-#define EMARCH_ENC_I17_IMM41b_INST_WORD_POS_X 24
-#define EMARCH_ENC_I17_IMM41b_VAL_POS_X 32
-
-#define EMARCH_ENC_I17_IMM41c_INST_WORD_X 2
-#define EMARCH_ENC_I17_IMM41c_SIZE_X 23
-#define EMARCH_ENC_I17_IMM41c_INST_WORD_POS_X 0
-#define EMARCH_ENC_I17_IMM41c_VAL_POS_X 40
-
-#define EMARCH_ENC_I17_SIGN_INST_WORD_X 3
-#define EMARCH_ENC_I17_SIGN_SIZE_X 1
-#define EMARCH_ENC_I17_SIGN_INST_WORD_POS_X 27
-#define EMARCH_ENC_I17_SIGN_VAL_POS_X 63
-
-#define X3_OPCODE_INST_WORD_X 3
-#define X3_OPCODE_SIZE_X 4
-#define X3_OPCODE_INST_WORD_POS_X 28
-#define X3_OPCODE_SIGN_VAL_POS_X 0
-
-#define X3_I_INST_WORD_X 3
-#define X3_I_SIZE_X 1
-#define X3_I_INST_WORD_POS_X 27
-#define X3_I_SIGN_VAL_POS_X 59
-
-#define X3_D_WH_INST_WORD_X 3
-#define X3_D_WH_SIZE_X 3
-#define X3_D_WH_INST_WORD_POS_X 24
-#define X3_D_WH_SIGN_VAL_POS_X 0
-
-#define X3_IMM20_INST_WORD_X 3
-#define X3_IMM20_SIZE_X 20
-#define X3_IMM20_INST_WORD_POS_X 4
-#define X3_IMM20_SIGN_VAL_POS_X 0
-
-#define X3_IMM39_1_INST_WORD_X 2
-#define X3_IMM39_1_SIZE_X 23
-#define X3_IMM39_1_INST_WORD_POS_X 0
-#define X3_IMM39_1_SIGN_VAL_POS_X 36
-
-#define X3_IMM39_2_INST_WORD_X 1
-#define X3_IMM39_2_SIZE_X 16
-#define X3_IMM39_2_INST_WORD_POS_X 16
-#define X3_IMM39_2_SIGN_VAL_POS_X 20
-
-#define X3_P_INST_WORD_X 3
-#define X3_P_SIZE_X 4
-#define X3_P_INST_WORD_POS_X 0
-#define X3_P_SIGN_VAL_POS_X 0
-
-#define X3_TMPLT_INST_WORD_X 0
-#define X3_TMPLT_SIZE_X 4
-#define X3_TMPLT_INST_WORD_POS_X 0
-#define X3_TMPLT_SIGN_VAL_POS_X 0
-
-#define X3_BTYPE_QP_INST_WORD_X 2
-#define X3_BTYPE_QP_SIZE_X 9
-#define X3_BTYPE_QP_INST_WORD_POS_X 23
-#define X3_BTYPE_QP_INST_VAL_POS_X 0
-
-#define X3_EMPTY_INST_WORD_X 1
-#define X3_EMPTY_SIZE_X 2
-#define X3_EMPTY_INST_WORD_POS_X 14
-#define X3_EMPTY_INST_VAL_POS_X 0
-
-    typedef struct _IMAGE_LINENUMBER {
-      union {
-	DWORD SymbolTableIndex;
-	DWORD VirtualAddress;
-      } Type;
-      WORD Linenumber;
-    } IMAGE_LINENUMBER;
-    typedef IMAGE_LINENUMBER UNALIGNED *PIMAGE_LINENUMBER;
-
-#define IMAGE_SIZEOF_LINENUMBER 6
-
-#include "poppack.h"
-
-    typedef struct _IMAGE_BASE_RELOCATION {
-      DWORD VirtualAddress;
-      DWORD SizeOfBlock;
-
-    } IMAGE_BASE_RELOCATION;
-    typedef IMAGE_BASE_RELOCATION UNALIGNED *PIMAGE_BASE_RELOCATION;
-
-#define IMAGE_SIZEOF_BASE_RELOCATION 8
-
-#define IMAGE_REL_BASED_ABSOLUTE 0
-#define IMAGE_REL_BASED_HIGH 1
-#define IMAGE_REL_BASED_LOW 2
-#define IMAGE_REL_BASED_HIGHLOW 3
-#define IMAGE_REL_BASED_HIGHADJ 4
-#define IMAGE_REL_BASED_MIPS_JMPADDR 5
-#define IMAGE_REL_BASED_MIPS_JMPADDR16 9
-#define IMAGE_REL_BASED_IA64_IMM64 9
-#define IMAGE_REL_BASED_DIR64 10
-
-#define IMAGE_ARCHIVE_START_SIZE 8
-#define IMAGE_ARCHIVE_START "!<arch>\n"
-#define IMAGE_ARCHIVE_END "`\n"
-#define IMAGE_ARCHIVE_PAD "\n"
-#define IMAGE_ARCHIVE_LINKER_MEMBER "/               "
-#define IMAGE_ARCHIVE_LONGNAMES_MEMBER "//              "
-
-    typedef struct _IMAGE_ARCHIVE_MEMBER_HEADER {
-      BYTE Name[16];
-      BYTE Date[12];
-      BYTE UserID[6];
-      BYTE GroupID[6];
-      BYTE Mode[8];
-      BYTE Size[10];
-      BYTE EndHeader[2];
-    } IMAGE_ARCHIVE_MEMBER_HEADER,*PIMAGE_ARCHIVE_MEMBER_HEADER;
-
-#define IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR 60
-
-    typedef struct _IMAGE_EXPORT_DIRECTORY {
-      DWORD Characteristics;
-      DWORD TimeDateStamp;
-      WORD MajorVersion;
-      WORD MinorVersion;
-      DWORD Name;
-      DWORD Base;
-      DWORD NumberOfFunctions;
-      DWORD NumberOfNames;
-      DWORD AddressOfFunctions;
-      DWORD AddressOfNames;
-      DWORD AddressOfNameOrdinals;
-    } IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY;
-
-    typedef struct _IMAGE_IMPORT_BY_NAME {
-      WORD Hint;
-      BYTE Name[1];
-    } IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME;
-
-#include "pshpack8.h"
-
-    typedef struct _IMAGE_THUNK_DATA64 {
-      union {
-	ULONGLONG ForwarderString;
-	ULONGLONG Function;
-	ULONGLONG Ordinal;
-	ULONGLONG AddressOfData;
-      } u1;
-    } IMAGE_THUNK_DATA64;
-    typedef IMAGE_THUNK_DATA64 *PIMAGE_THUNK_DATA64;
-
-#include "poppack.h"
-
-    typedef struct _IMAGE_THUNK_DATA32 {
-      union {
-	DWORD ForwarderString;
-	DWORD Function;
-	DWORD Ordinal;
-	DWORD AddressOfData;
-      } u1;
-    } IMAGE_THUNK_DATA32;
-    typedef IMAGE_THUNK_DATA32 *PIMAGE_THUNK_DATA32;
-
-#define IMAGE_ORDINAL_FLAG64 0x8000000000000000ull
-#define IMAGE_ORDINAL_FLAG32 0x80000000
-#define IMAGE_ORDINAL64(Ordinal) (Ordinal & 0xffffull)
-#define IMAGE_ORDINAL32(Ordinal) (Ordinal & 0xffff)
-#define IMAGE_SNAP_BY_ORDINAL64(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG64)!=0)
-#define IMAGE_SNAP_BY_ORDINAL32(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG32)!=0)
-
-    typedef VOID
-      (NTAPI *PIMAGE_TLS_CALLBACK)(PVOID DllHandle,DWORD Reason,PVOID Reserved);
-
-    typedef struct _IMAGE_TLS_DIRECTORY64 {
-      ULONGLONG StartAddressOfRawData;
-      ULONGLONG EndAddressOfRawData;
-      ULONGLONG AddressOfIndex;
-      ULONGLONG AddressOfCallBacks;
-      DWORD SizeOfZeroFill;
-      DWORD Characteristics;
-    } IMAGE_TLS_DIRECTORY64;
-    typedef IMAGE_TLS_DIRECTORY64 *PIMAGE_TLS_DIRECTORY64;
-
-    typedef struct _IMAGE_TLS_DIRECTORY32 {
-      DWORD StartAddressOfRawData;
-      DWORD EndAddressOfRawData;
-      DWORD AddressOfIndex;
-      DWORD AddressOfCallBacks;
-      DWORD SizeOfZeroFill;
-      DWORD Characteristics;
-    } IMAGE_TLS_DIRECTORY32;
-    typedef IMAGE_TLS_DIRECTORY32 *PIMAGE_TLS_DIRECTORY32;
-
-#ifdef _WIN64
-#define IMAGE_ORDINAL_FLAG IMAGE_ORDINAL_FLAG64
-#define IMAGE_ORDINAL(Ordinal) IMAGE_ORDINAL64(Ordinal)
-    typedef IMAGE_THUNK_DATA64 IMAGE_THUNK_DATA;
-    typedef PIMAGE_THUNK_DATA64 PIMAGE_THUNK_DATA;
-#define IMAGE_SNAP_BY_ORDINAL(Ordinal) IMAGE_SNAP_BY_ORDINAL64(Ordinal)
-    typedef IMAGE_TLS_DIRECTORY64 IMAGE_TLS_DIRECTORY;
-    typedef PIMAGE_TLS_DIRECTORY64 PIMAGE_TLS_DIRECTORY;
-#else
-#define IMAGE_ORDINAL_FLAG IMAGE_ORDINAL_FLAG32
-#define IMAGE_ORDINAL(Ordinal) IMAGE_ORDINAL32(Ordinal)
-    typedef IMAGE_THUNK_DATA32 IMAGE_THUNK_DATA;
-    typedef PIMAGE_THUNK_DATA32 PIMAGE_THUNK_DATA;
-#define IMAGE_SNAP_BY_ORDINAL(Ordinal) IMAGE_SNAP_BY_ORDINAL32(Ordinal)
-    typedef IMAGE_TLS_DIRECTORY32 IMAGE_TLS_DIRECTORY;
-    typedef PIMAGE_TLS_DIRECTORY32 PIMAGE_TLS_DIRECTORY;
-#endif
-
-    typedef struct _IMAGE_IMPORT_DESCRIPTOR {
-      union {
-	DWORD Characteristics;
-	DWORD OriginalFirstThunk;
-      };
-      DWORD TimeDateStamp;
-
-      DWORD ForwarderChain;
-      DWORD Name;
-      DWORD FirstThunk;
-    } IMAGE_IMPORT_DESCRIPTOR;
-    typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
-
-    typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR {
-      DWORD TimeDateStamp;
-      WORD OffsetModuleName;
-      WORD NumberOfModuleForwarderRefs;
-    } IMAGE_BOUND_IMPORT_DESCRIPTOR,*PIMAGE_BOUND_IMPORT_DESCRIPTOR;
-
-    typedef struct _IMAGE_BOUND_FORWARDER_REF {
-      DWORD TimeDateStamp;
-      WORD OffsetModuleName;
-      WORD Reserved;
-    } IMAGE_BOUND_FORWARDER_REF,*PIMAGE_BOUND_FORWARDER_REF;
-
-    typedef struct _IMAGE_RESOURCE_DIRECTORY {
-      DWORD Characteristics;
-      DWORD TimeDateStamp;
-      WORD MajorVersion;
-      WORD MinorVersion;
-      WORD NumberOfNamedEntries;
-      WORD NumberOfIdEntries;
-    } IMAGE_RESOURCE_DIRECTORY,*PIMAGE_RESOURCE_DIRECTORY;
-
-#define IMAGE_RESOURCE_NAME_IS_STRING 0x80000000
-#define IMAGE_RESOURCE_DATA_IS_DIRECTORY 0x80000000
-
-    typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
-      union {
-	struct {
-	  DWORD NameOffset:31;
-	  DWORD NameIsString:1;
-	};
-	DWORD Name;
-	WORD Id;
-      };
-      union {
-	DWORD OffsetToData;
-	struct {
-	  DWORD OffsetToDirectory:31;
-	  DWORD DataIsDirectory:1;
-	};
-      };
-    } IMAGE_RESOURCE_DIRECTORY_ENTRY,*PIMAGE_RESOURCE_DIRECTORY_ENTRY;
-
-    typedef struct _IMAGE_RESOURCE_DIRECTORY_STRING {
-      WORD Length;
-      CHAR NameString[1];
-    } IMAGE_RESOURCE_DIRECTORY_STRING,*PIMAGE_RESOURCE_DIRECTORY_STRING;
-
-    typedef struct _IMAGE_RESOURCE_DIR_STRING_U {
-      WORD Length;
-      WCHAR NameString[1];
-    } IMAGE_RESOURCE_DIR_STRING_U,*PIMAGE_RESOURCE_DIR_STRING_U;
-
-    typedef struct _IMAGE_RESOURCE_DATA_ENTRY {
-      DWORD OffsetToData;
-      DWORD Size;
-      DWORD CodePage;
-      DWORD Reserved;
-    } IMAGE_RESOURCE_DATA_ENTRY,*PIMAGE_RESOURCE_DATA_ENTRY;
-
-    typedef struct {
-      DWORD Size;
-      DWORD TimeDateStamp;
-      WORD MajorVersion;
-      WORD MinorVersion;
-      DWORD GlobalFlagsClear;
-      DWORD GlobalFlagsSet;
-      DWORD CriticalSectionDefaultTimeout;
-      DWORD DeCommitFreeBlockThreshold;
-      DWORD DeCommitTotalFreeThreshold;
-      DWORD LockPrefixTable;
-      DWORD MaximumAllocationSize;
-      DWORD VirtualMemoryThreshold;
-      DWORD ProcessHeapFlags;
-      DWORD ProcessAffinityMask;
-      WORD CSDVersion;
-      WORD Reserved1;
-      DWORD EditList;
-      DWORD SecurityCookie;
-      DWORD SEHandlerTable;
-      DWORD SEHandlerCount;
-    } IMAGE_LOAD_CONFIG_DIRECTORY32,*PIMAGE_LOAD_CONFIG_DIRECTORY32;
-
-    typedef struct {
-      DWORD Size;
-      DWORD TimeDateStamp;
-      WORD MajorVersion;
-      WORD MinorVersion;
-      DWORD GlobalFlagsClear;
-      DWORD GlobalFlagsSet;
-      DWORD CriticalSectionDefaultTimeout;
-      ULONGLONG DeCommitFreeBlockThreshold;
-      ULONGLONG DeCommitTotalFreeThreshold;
-      ULONGLONG LockPrefixTable;
-      ULONGLONG MaximumAllocationSize;
-      ULONGLONG VirtualMemoryThreshold;
-      ULONGLONG ProcessAffinityMask;
-      DWORD ProcessHeapFlags;
-      WORD CSDVersion;
-      WORD Reserved1;
-      ULONGLONG EditList;
-      ULONGLONG SecurityCookie;
-      ULONGLONG SEHandlerTable;
-      ULONGLONG SEHandlerCount;
-    } IMAGE_LOAD_CONFIG_DIRECTORY64,*PIMAGE_LOAD_CONFIG_DIRECTORY64;
-
-#ifdef _WIN64
-    typedef IMAGE_LOAD_CONFIG_DIRECTORY64 IMAGE_LOAD_CONFIG_DIRECTORY;
-    typedef PIMAGE_LOAD_CONFIG_DIRECTORY64 PIMAGE_LOAD_CONFIG_DIRECTORY;
-#else
-    typedef IMAGE_LOAD_CONFIG_DIRECTORY32 IMAGE_LOAD_CONFIG_DIRECTORY;
-    typedef PIMAGE_LOAD_CONFIG_DIRECTORY32 PIMAGE_LOAD_CONFIG_DIRECTORY;
-#endif
-
-    typedef struct _IMAGE_CE_RUNTIME_FUNCTION_ENTRY {
-      DWORD FuncStart;
-      DWORD PrologLen : 8;
-      DWORD FuncLen : 22;
-      DWORD ThirtyTwoBit : 1;
-      DWORD ExceptionFlag : 1;
-    } IMAGE_CE_RUNTIME_FUNCTION_ENTRY,*PIMAGE_CE_RUNTIME_FUNCTION_ENTRY;
-
-    typedef struct _IMAGE_ALPHA64_RUNTIME_FUNCTION_ENTRY {
-      ULONGLONG BeginAddress;
-      ULONGLONG EndAddress;
-      ULONGLONG ExceptionHandler;
-      ULONGLONG HandlerData;
-      ULONGLONG PrologEndAddress;
-    } IMAGE_ALPHA64_RUNTIME_FUNCTION_ENTRY,*PIMAGE_ALPHA64_RUNTIME_FUNCTION_ENTRY;
-
-    typedef struct _IMAGE_ALPHA_RUNTIME_FUNCTION_ENTRY {
-      DWORD BeginAddress;
-      DWORD EndAddress;
-      DWORD ExceptionHandler;
-      DWORD HandlerData;
-      DWORD PrologEndAddress;
-    } IMAGE_ALPHA_RUNTIME_FUNCTION_ENTRY,*PIMAGE_ALPHA_RUNTIME_FUNCTION_ENTRY;
-
-    typedef struct _IMAGE_RUNTIME_FUNCTION_ENTRY {
-      DWORD BeginAddress;
-      DWORD EndAddress;
-      DWORD UnwindInfoAddress;
-    } _IMAGE_RUNTIME_FUNCTION_ENTRY,*_PIMAGE_RUNTIME_FUNCTION_ENTRY;
-
-    typedef _IMAGE_RUNTIME_FUNCTION_ENTRY IMAGE_IA64_RUNTIME_FUNCTION_ENTRY;
-    typedef _PIMAGE_RUNTIME_FUNCTION_ENTRY PIMAGE_IA64_RUNTIME_FUNCTION_ENTRY;
-
-    typedef _IMAGE_RUNTIME_FUNCTION_ENTRY IMAGE_RUNTIME_FUNCTION_ENTRY;
-    typedef _PIMAGE_RUNTIME_FUNCTION_ENTRY PIMAGE_RUNTIME_FUNCTION_ENTRY;
-
-    typedef struct _IMAGE_DEBUG_DIRECTORY {
-      DWORD Characteristics;
-      DWORD TimeDateStamp;
-      WORD MajorVersion;
-      WORD MinorVersion;
-      DWORD Type;
-      DWORD SizeOfData;
-      DWORD AddressOfRawData;
-      DWORD PointerToRawData;
-    } IMAGE_DEBUG_DIRECTORY,*PIMAGE_DEBUG_DIRECTORY;
-
-#define IMAGE_DEBUG_TYPE_UNKNOWN 0
-#define IMAGE_DEBUG_TYPE_COFF 1
-#define IMAGE_DEBUG_TYPE_CODEVIEW 2
-#define IMAGE_DEBUG_TYPE_FPO 3
-#define IMAGE_DEBUG_TYPE_MISC 4
-#define IMAGE_DEBUG_TYPE_EXCEPTION 5
-#define IMAGE_DEBUG_TYPE_FIXUP 6
-#define IMAGE_DEBUG_TYPE_OMAP_TO_SRC 7
-#define IMAGE_DEBUG_TYPE_OMAP_FROM_SRC 8
-#define IMAGE_DEBUG_TYPE_BORLAND 9
-#define IMAGE_DEBUG_TYPE_RESERVED10 10
-#define IMAGE_DEBUG_TYPE_CLSID 11
-
-    typedef struct _IMAGE_COFF_SYMBOLS_HEADER {
-      DWORD NumberOfSymbols;
-      DWORD LvaToFirstSymbol;
-      DWORD NumberOfLinenumbers;
-      DWORD LvaToFirstLinenumber;
-      DWORD RvaToFirstByteOfCode;
-      DWORD RvaToLastByteOfCode;
-      DWORD RvaToFirstByteOfData;
-      DWORD RvaToLastByteOfData;
-    } IMAGE_COFF_SYMBOLS_HEADER,*PIMAGE_COFF_SYMBOLS_HEADER;
-
-#define FRAME_FPO 0
-#define FRAME_TRAP 1
-#define FRAME_TSS 2
-#define FRAME_NONFPO 3
-
-    typedef struct _FPO_DATA {
-      DWORD ulOffStart;
-      DWORD cbProcSize;
-      DWORD cdwLocals;
-      WORD cdwParams;
-      WORD cbProlog : 8;
-      WORD cbRegs : 3;
-      WORD fHasSEH : 1;
-      WORD fUseBP : 1;
-      WORD reserved : 1;
-      WORD cbFrame : 2;
-    } FPO_DATA,*PFPO_DATA;
-#define SIZEOF_RFPO_DATA 16
-
-#define IMAGE_DEBUG_MISC_EXENAME 1
-
-    typedef struct _IMAGE_DEBUG_MISC {
-      DWORD DataType;
-      DWORD Length;
-      BOOLEAN Unicode;
-      BYTE Reserved[3];
-      BYTE Data[1];
-    } IMAGE_DEBUG_MISC,*PIMAGE_DEBUG_MISC;
-
-    typedef struct _IMAGE_FUNCTION_ENTRY {
-      DWORD StartingAddress;
-      DWORD EndingAddress;
-      DWORD EndOfPrologue;
-    } IMAGE_FUNCTION_ENTRY,*PIMAGE_FUNCTION_ENTRY;
-
-    typedef struct _IMAGE_FUNCTION_ENTRY64 {
-      ULONGLONG StartingAddress;
-      ULONGLONG EndingAddress;
-      union {
-	ULONGLONG EndOfPrologue;
-	ULONGLONG UnwindInfoAddress;
-      };
-    } IMAGE_FUNCTION_ENTRY64,*PIMAGE_FUNCTION_ENTRY64;
-
-    typedef struct _IMAGE_SEPARATE_DEBUG_HEADER {
-      WORD Signature;
-      WORD Flags;
-      WORD Machine;
-      WORD Characteristics;
-      DWORD TimeDateStamp;
-      DWORD CheckSum;
-      DWORD ImageBase;
-      DWORD SizeOfImage;
-      DWORD NumberOfSections;
-      DWORD ExportedNamesSize;
-      DWORD DebugDirectorySize;
-      DWORD SectionAlignment;
-      DWORD Reserved[2];
-    } IMAGE_SEPARATE_DEBUG_HEADER,*PIMAGE_SEPARATE_DEBUG_HEADER;
-
-    typedef struct _NON_PAGED_DEBUG_INFO {
-      WORD Signature;
-      WORD Flags;
-      DWORD Size;
-      WORD Machine;
-      WORD Characteristics;
-      DWORD TimeDateStamp;
-      DWORD CheckSum;
-      DWORD SizeOfImage;
-      ULONGLONG ImageBase;
-
-    } NON_PAGED_DEBUG_INFO,*PNON_PAGED_DEBUG_INFO;
-
-#define IMAGE_SEPARATE_DEBUG_SIGNATURE 0x4944
-#define NON_PAGED_DEBUG_SIGNATURE 0x494E
-
-#define IMAGE_SEPARATE_DEBUG_FLAGS_MASK 0x8000
-#define IMAGE_SEPARATE_DEBUG_MISMATCH 0x8000
-
-    typedef struct _ImageArchitectureHeader {
-      unsigned int AmaskValue: 1;
-      int Adummy1 :7;
-      unsigned int AmaskShift: 8;
-      int Adummy2 :16;
-      DWORD FirstEntryRVA;
-    } IMAGE_ARCHITECTURE_HEADER,*PIMAGE_ARCHITECTURE_HEADER;
-
-    typedef struct _ImageArchitectureEntry {
-      DWORD FixupInstRVA;
-      DWORD NewInst;
-    } IMAGE_ARCHITECTURE_ENTRY,*PIMAGE_ARCHITECTURE_ENTRY;
-
-#include "poppack.h"
-
-#define IMPORT_OBJECT_HDR_SIG2 0xffff
-
-    typedef struct IMPORT_OBJECT_HEADER {
-      WORD Sig1;
-      WORD Sig2;
-      WORD Version;
-      WORD Machine;
-      DWORD TimeDateStamp;
-      DWORD SizeOfData;
-      union {
-	WORD Ordinal;
-	WORD Hint;
-      };
-      WORD Type : 2;
-      WORD NameType : 3;
-      WORD Reserved : 11;
-    } IMPORT_OBJECT_HEADER;
-
-    typedef enum IMPORT_OBJECT_TYPE {
-      IMPORT_OBJECT_CODE = 0,IMPORT_OBJECT_DATA = 1,IMPORT_OBJECT_CONST = 2
-    } IMPORT_OBJECT_TYPE;
-
-    typedef enum IMPORT_OBJECT_NAME_TYPE {
-      IMPORT_OBJECT_ORDINAL = 0,IMPORT_OBJECT_NAME = 1,IMPORT_OBJECT_NAME_NO_PREFIX = 2,IMPORT_OBJECT_NAME_UNDECORATE = 3
-    } IMPORT_OBJECT_NAME_TYPE;
-
-#ifndef __IMAGE_COR20_HEADER_DEFINED__
-#define __IMAGE_COR20_HEADER_DEFINED__
-    typedef enum ReplacesCorHdrNumericDefines {
-      COMIMAGE_FLAGS_ILONLY =0x00000001,COMIMAGE_FLAGS_32BITREQUIRED =0x00000002,COMIMAGE_FLAGS_IL_LIBRARY =0x00000004,
-      COMIMAGE_FLAGS_STRONGNAMESIGNED =0x00000008,COMIMAGE_FLAGS_TRACKDEBUGDATA =0x00010000,COR_VERSION_MAJOR_V2 =2,
-      COR_VERSION_MAJOR =COR_VERSION_MAJOR_V2,COR_VERSION_MINOR =0,COR_DELETED_NAME_LENGTH =8,COR_VTABLEGAP_NAME_LENGTH =8,
-      NATIVE_TYPE_MAX_CB =1,COR_ILMETHOD_SECT_SMALL_MAX_DATASIZE=0xFF,IMAGE_COR_MIH_METHODRVA =0x01,IMAGE_COR_MIH_EHRVA =0x02,
-      IMAGE_COR_MIH_BASICBLOCK =0x08,COR_VTABLE_32BIT =0x01,COR_VTABLE_64BIT =0x02,COR_VTABLE_FROM_UNMANAGED =0x04,
-      COR_VTABLE_CALL_MOST_DERIVED =0x10,IMAGE_COR_EATJ_THUNK_SIZE =32,MAX_CLASS_NAME =1024,MAX_PACKAGE_NAME =1024
-    } ReplacesCorHdrNumericDefines;
-
-    typedef struct IMAGE_COR20_HEADER {
-      DWORD cb;
-      WORD MajorRuntimeVersion;
-      WORD MinorRuntimeVersion;
-      IMAGE_DATA_DIRECTORY MetaData;
-      DWORD Flags;
-      DWORD EntryPointToken;
-      IMAGE_DATA_DIRECTORY Resources;
-      IMAGE_DATA_DIRECTORY StrongNameSignature;
-      IMAGE_DATA_DIRECTORY CodeManagerTable;
-      IMAGE_DATA_DIRECTORY VTableFixups;
-      IMAGE_DATA_DIRECTORY ExportAddressTableJumps;
-      IMAGE_DATA_DIRECTORY ManagedNativeHeader;
-    } IMAGE_COR20_HEADER,*PIMAGE_COR20_HEADER;
-#endif
-
-#if defined (__x86_64)
-    NTSYSAPI PRUNTIME_FUNCTION NTAPI RtlLookupFunctionEntry (DWORD64 ControlPc, PDWORD64 ImageBase, PUNWIND_HISTORY_TABLE HistoryTable);
-    NTSYSAPI VOID NTAPI RtlUnwindEx (PVOID TargetFrame, PVOID TargetIp, PEXCEPTION_RECORD ExceptionRecord, PVOID ReturnValue, PCONTEXT ContextRecord, PUNWIND_HISTORY_TABLE HistoryTable);
-#endif
-
-#include <string.h>
-
-#ifndef _SLIST_HEADER_
-#define _SLIST_HEADER_
-
-#ifdef _WIN64
-    typedef struct _SLIST_ENTRY *PSLIST_ENTRY;
-    typedef DECLSPEC_ALIGN(16) struct _SLIST_ENTRY {
-      PSLIST_ENTRY Next;
-    } SLIST_ENTRY;
-#else
-
-#define SLIST_ENTRY SINGLE_LIST_ENTRY
-#define _SLIST_ENTRY _SINGLE_LIST_ENTRY
-#define PSLIST_ENTRY PSINGLE_LIST_ENTRY
-#endif
-
-#if defined(_WIN64)
-
-    typedef DECLSPEC_ALIGN(16) struct _SLIST_HEADER {
-      ULONGLONG Alignment;
-      ULONGLONG Region;
-    } SLIST_HEADER;
-
-    typedef struct _SLIST_HEADER *PSLIST_HEADER;
-#else
-
-    typedef union _SLIST_HEADER {
-      ULONGLONG Alignment;
-      struct {
-	SLIST_ENTRY Next;
-	WORD Depth;
-	WORD Sequence;
-      };
-    } SLIST_HEADER,*PSLIST_HEADER;
-#endif
-#endif
-
-    NTSYSAPI VOID NTAPI RtlInitializeSListHead(PSLIST_HEADER ListHead);
-    NTSYSAPI PSLIST_ENTRY NTAPI RtlFirstEntrySList(const SLIST_HEADER *ListHead);
-    NTSYSAPI PSLIST_ENTRY NTAPI RtlInterlockedPopEntrySList(PSLIST_HEADER ListHead);
-    NTSYSAPI PSLIST_ENTRY NTAPI RtlInterlockedPushEntrySList(PSLIST_HEADER ListHead,PSLIST_ENTRY ListEntry);
-    NTSYSAPI PSLIST_ENTRY NTAPI RtlInterlockedFlushSList(PSLIST_HEADER ListHead);
-    NTSYSAPI WORD NTAPI RtlQueryDepthSList(PSLIST_HEADER ListHead);
-
-#define HEAP_NO_SERIALIZE 0x00000001
-#define HEAP_GROWABLE 0x00000002
-#define HEAP_GENERATE_EXCEPTIONS 0x00000004
-#define HEAP_ZERO_MEMORY 0x00000008
-#define HEAP_REALLOC_IN_PLACE_ONLY 0x00000010
-#define HEAP_TAIL_CHECKING_ENABLED 0x00000020
-#define HEAP_FREE_CHECKING_ENABLED 0x00000040
-#define HEAP_DISABLE_COALESCE_ON_FREE 0x00000080
-#define HEAP_CREATE_ALIGN_16 0x00010000
-#define HEAP_CREATE_ENABLE_TRACING 0x00020000
-#define HEAP_CREATE_ENABLE_EXECUTE 0x00040000
-#define HEAP_MAXIMUM_TAG 0x0FFF
-#define HEAP_PSEUDO_TAG_FLAG 0x8000
-#define HEAP_TAG_SHIFT 18
-#define HEAP_MAKE_TAG_FLAGS(b,o) ((DWORD)((b) + ((o) << 18)))
-
-    NTSYSAPI VOID NTAPI RtlCaptureContext(PCONTEXT ContextRecord);
-
-#define IS_TEXT_UNICODE_ASCII16 0x0001
-#define IS_TEXT_UNICODE_REVERSE_ASCII16 0x0010
-
-#define IS_TEXT_UNICODE_STATISTICS 0x0002
-#define IS_TEXT_UNICODE_REVERSE_STATISTICS 0x0020
-
-#define IS_TEXT_UNICODE_CONTROLS 0x0004
-#define IS_TEXT_UNICODE_REVERSE_CONTROLS 0x0040
-
-#define IS_TEXT_UNICODE_SIGNATURE 0x0008
-#define IS_TEXT_UNICODE_REVERSE_SIGNATURE 0x0080
-
-#define IS_TEXT_UNICODE_ILLEGAL_CHARS 0x0100
-#define IS_TEXT_UNICODE_ODD_LENGTH 0x0200
-#define IS_TEXT_UNICODE_DBCS_LEADBYTE 0x0400
-#define IS_TEXT_UNICODE_NULL_BYTES 0x1000
-
-#define IS_TEXT_UNICODE_UNICODE_MASK 0x000F
-#define IS_TEXT_UNICODE_REVERSE_MASK 0x00F0
-#define IS_TEXT_UNICODE_NOT_UNICODE_MASK 0x0F00
-#define IS_TEXT_UNICODE_NOT_ASCII_MASK 0xF000
-
-#define COMPRESSION_FORMAT_NONE (0x0000)
-#define COMPRESSION_FORMAT_DEFAULT (0x0001)
-#define COMPRESSION_FORMAT_LZNT1 (0x0002)
-#define COMPRESSION_ENGINE_STANDARD (0x0000)
-#define COMPRESSION_ENGINE_MAXIMUM (0x0100)
-#define COMPRESSION_ENGINE_HIBER (0x0200)
-
-#if _DBG_MEMCPY_INLINE_ && !defined(_MEMCPY_INLINE_) && !defined(_CRTBLD)
-#define _MEMCPY_INLINE_
-    __CRT_INLINE PVOID __cdecl memcpy_inline(void *dst,const void *src,size_t size) {
-      if(((char *)dst > (char *)src) && ((char *)dst < ((char *)src + size))) {
-	__debugbreak();
-      }
-      return memcpy(dst,src,size);
-    }
-#define memcpy memcpy_inline
-#endif
-
-    NTSYSAPI SIZE_T NTAPI RtlCompareMemory(const VOID *Source1,const VOID *Source2,SIZE_T Length);
-
-#define RtlEqualMemory(Destination,Source,Length) (!memcmp((Destination),(Source),(Length)))
-#define RtlMoveMemory(Destination,Source,Length) memmove((Destination),(Source),(Length))
-#define RtlCopyMemory(Destination,Source,Length) memcpy((Destination),(Source),(Length))
-#define RtlFillMemory(Destination,Length,Fill) memset((Destination),(Fill),(Length))
-#define RtlZeroMemory(Destination,Length) memset((Destination),0,(Length))
-
-    __CRT_INLINE PVOID RtlSecureZeroMemory(PVOID ptr,SIZE_T cnt) {
-      volatile char *vptr =(volatile char *)ptr;
-#ifdef __x86_64
-      __stosb((PBYTE)((DWORD64)vptr),0,cnt);
-#else
-      while(cnt) {
-	*vptr = 0;
-	vptr++;
-	cnt--;
-      }
-#endif
-      return ptr;
-    }
-
-    typedef struct _MESSAGE_RESOURCE_ENTRY {
-      WORD Length;
-      WORD Flags;
-      BYTE Text[1];
-    } MESSAGE_RESOURCE_ENTRY,*PMESSAGE_RESOURCE_ENTRY;
-
-#define MESSAGE_RESOURCE_UNICODE 0x0001
-
-    typedef struct _MESSAGE_RESOURCE_BLOCK {
-      DWORD LowId;
-      DWORD HighId;
-      DWORD OffsetToEntries;
-    } MESSAGE_RESOURCE_BLOCK,*PMESSAGE_RESOURCE_BLOCK;
-
-    typedef struct _MESSAGE_RESOURCE_DATA {
-      DWORD NumberOfBlocks;
-      MESSAGE_RESOURCE_BLOCK Blocks[1];
-    } MESSAGE_RESOURCE_DATA,*PMESSAGE_RESOURCE_DATA;
-
-    typedef struct _OSVERSIONINFOA {
-      DWORD dwOSVersionInfoSize;
-      DWORD dwMajorVersion;
-      DWORD dwMinorVersion;
-      DWORD dwBuildNumber;
-      DWORD dwPlatformId;
-      CHAR szCSDVersion[128];
-    } OSVERSIONINFOA,*POSVERSIONINFOA,*LPOSVERSIONINFOA;
-
-    typedef struct _OSVERSIONINFOW {
-      DWORD dwOSVersionInfoSize;
-      DWORD dwMajorVersion;
-      DWORD dwMinorVersion;
-      DWORD dwBuildNumber;
-      DWORD dwPlatformId;
-      WCHAR szCSDVersion[128];
-    } OSVERSIONINFOW,*POSVERSIONINFOW,*LPOSVERSIONINFOW,RTL_OSVERSIONINFOW,*PRTL_OSVERSIONINFOW;
-
-#ifdef UNICODE
-    typedef OSVERSIONINFOW OSVERSIONINFO;
-    typedef POSVERSIONINFOW POSVERSIONINFO;
-    typedef LPOSVERSIONINFOW LPOSVERSIONINFO;
-#else
-    typedef OSVERSIONINFOA OSVERSIONINFO;
-    typedef POSVERSIONINFOA POSVERSIONINFO;
-    typedef LPOSVERSIONINFOA LPOSVERSIONINFO;
-#endif
-
-    typedef struct _OSVERSIONINFOEXA {
-      DWORD dwOSVersionInfoSize;
-      DWORD dwMajorVersion;
-      DWORD dwMinorVersion;
-      DWORD dwBuildNumber;
-      DWORD dwPlatformId;
-      CHAR szCSDVersion[128];
-      WORD wServicePackMajor;
-      WORD wServicePackMinor;
-      WORD wSuiteMask;
-      BYTE wProductType;
-      BYTE wReserved;
-    } OSVERSIONINFOEXA,*POSVERSIONINFOEXA,*LPOSVERSIONINFOEXA;
-
-    typedef struct _OSVERSIONINFOEXW {
-      DWORD dwOSVersionInfoSize;
-      DWORD dwMajorVersion;
-      DWORD dwMinorVersion;
-      DWORD dwBuildNumber;
-      DWORD dwPlatformId;
-      WCHAR szCSDVersion[128];
-      WORD wServicePackMajor;
-      WORD wServicePackMinor;
-      WORD wSuiteMask;
-      BYTE wProductType;
-      BYTE wReserved;
-    } OSVERSIONINFOEXW,*POSVERSIONINFOEXW,*LPOSVERSIONINFOEXW,RTL_OSVERSIONINFOEXW,*PRTL_OSVERSIONINFOEXW;
-#ifdef UNICODE
-    typedef OSVERSIONINFOEXW OSVERSIONINFOEX;
-    typedef POSVERSIONINFOEXW POSVERSIONINFOEX;
-    typedef LPOSVERSIONINFOEXW LPOSVERSIONINFOEX;
-#else
-    typedef OSVERSIONINFOEXA OSVERSIONINFOEX;
-    typedef POSVERSIONINFOEXA POSVERSIONINFOEX;
-    typedef LPOSVERSIONINFOEXA LPOSVERSIONINFOEX;
-#endif
-
-#define VER_EQUAL 1
-#define VER_GREATER 2
-#define VER_GREATER_EQUAL 3
-#define VER_LESS 4
-#define VER_LESS_EQUAL 5
-#define VER_AND 6
-#define VER_OR 7
-
-#define VER_CONDITION_MASK 7
-#define VER_NUM_BITS_PER_CONDITION_MASK 3
-
-#define VER_MINORVERSION 0x0000001
-#define VER_MAJORVERSION 0x0000002
-#define VER_BUILDNUMBER 0x0000004
-#define VER_PLATFORMID 0x0000008
-#define VER_SERVICEPACKMINOR 0x0000010
-#define VER_SERVICEPACKMAJOR 0x0000020
-#define VER_SUITENAME 0x0000040
-#define VER_PRODUCT_TYPE 0x0000080
-
-#define VER_NT_WORKSTATION 0x0000001
-#define VER_NT_DOMAIN_CONTROLLER 0x0000002
-#define VER_NT_SERVER 0x0000003
-
-#define VER_PLATFORM_WIN32s 0
-#define VER_PLATFORM_WIN32_WINDOWS 1
-#define VER_PLATFORM_WIN32_NT 2
-
-#define VER_SET_CONDITION(_m_,_t_,_c_) ((_m_)=VerSetConditionMask((_m_),(_t_),(_c_)))
-
-    NTSYSAPI ULONGLONG NTAPI VerSetConditionMask(ULONGLONG ConditionMask,DWORD TypeMask,BYTE Condition);
-
-    typedef struct _RTL_CRITICAL_SECTION_DEBUG {
-      WORD Type;
-      WORD CreatorBackTraceIndex;
-      struct _RTL_CRITICAL_SECTION *CriticalSection;
-      LIST_ENTRY ProcessLocksList;
-      DWORD EntryCount;
-      DWORD ContentionCount;
-      DWORD Spare[2];
-    } RTL_CRITICAL_SECTION_DEBUG,*PRTL_CRITICAL_SECTION_DEBUG,RTL_RESOURCE_DEBUG,*PRTL_RESOURCE_DEBUG;
-
-#define RTL_CRITSECT_TYPE 0
-#define RTL_RESOURCE_TYPE 1
-
-    typedef struct _RTL_CRITICAL_SECTION {
-      PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
-      LONG LockCount;
-      LONG RecursionCount;
-      HANDLE OwningThread;
-      HANDLE LockSemaphore;
-      ULONG_PTR SpinCount;
-    } RTL_CRITICAL_SECTION,*PRTL_CRITICAL_SECTION;
-
-    typedef VOID (NTAPI *RTL_VERIFIER_DLL_LOAD_CALLBACK) (PWSTR DllName,PVOID DllBase,SIZE_T DllSize,PVOID Reserved);
-    typedef VOID (NTAPI *RTL_VERIFIER_DLL_UNLOAD_CALLBACK) (PWSTR DllName,PVOID DllBase,SIZE_T DllSize,PVOID Reserved);
-    typedef VOID (NTAPI *RTL_VERIFIER_NTDLLHEAPFREE_CALLBACK)(PVOID AllocationBase,SIZE_T AllocationSize);
-
-    typedef struct _RTL_VERIFIER_THUNK_DESCRIPTOR {
-      PCHAR ThunkName;
-      PVOID ThunkOldAddress;
-      PVOID ThunkNewAddress;
-    } RTL_VERIFIER_THUNK_DESCRIPTOR,*PRTL_VERIFIER_THUNK_DESCRIPTOR;
-
-    typedef struct _RTL_VERIFIER_DLL_DESCRIPTOR {
-      PWCHAR DllName;
-      DWORD DllFlags;
-      PVOID DllAddress;
-      PRTL_VERIFIER_THUNK_DESCRIPTOR DllThunks;
-    } RTL_VERIFIER_DLL_DESCRIPTOR,*PRTL_VERIFIER_DLL_DESCRIPTOR;
-
-    typedef struct _RTL_VERIFIER_PROVIDER_DESCRIPTOR {
-      DWORD Length;
-      PRTL_VERIFIER_DLL_DESCRIPTOR ProviderDlls;
-      RTL_VERIFIER_DLL_LOAD_CALLBACK ProviderDllLoadCallback;
-      RTL_VERIFIER_DLL_UNLOAD_CALLBACK ProviderDllUnloadCallback;
-      PWSTR VerifierImage;
-      DWORD VerifierFlags;
-      DWORD VerifierDebug;
-      PVOID RtlpGetStackTraceAddress;
-      PVOID RtlpDebugPageHeapCreate;
-      PVOID RtlpDebugPageHeapDestroy;
-      RTL_VERIFIER_NTDLLHEAPFREE_CALLBACK ProviderNtdllHeapFreeCallback;
-    } RTL_VERIFIER_PROVIDER_DESCRIPTOR,*PRTL_VERIFIER_PROVIDER_DESCRIPTOR;
-
-#define RTL_VRF_FLG_FULL_PAGE_HEAP 0x00000001
-#define RTL_VRF_FLG_RESERVED_DONOTUSE 0x00000002
-#define RTL_VRF_FLG_HANDLE_CHECKS 0x00000004
-#define RTL_VRF_FLG_STACK_CHECKS 0x00000008
-#define RTL_VRF_FLG_APPCOMPAT_CHECKS 0x00000010
-#define RTL_VRF_FLG_TLS_CHECKS 0x00000020
-#define RTL_VRF_FLG_DIRTY_STACKS 0x00000040
-#define RTL_VRF_FLG_RPC_CHECKS 0x00000080
-#define RTL_VRF_FLG_COM_CHECKS 0x00000100
-#define RTL_VRF_FLG_DANGEROUS_APIS 0x00000200
-#define RTL_VRF_FLG_RACE_CHECKS 0x00000400
-#define RTL_VRF_FLG_DEADLOCK_CHECKS 0x00000800
-#define RTL_VRF_FLG_FIRST_CHANCE_EXCEPTION_CHECKS 0x00001000
-#define RTL_VRF_FLG_VIRTUAL_MEM_CHECKS 0x00002000
-#define RTL_VRF_FLG_ENABLE_LOGGING 0x00004000
-#define RTL_VRF_FLG_FAST_FILL_HEAP 0x00008000
-#define RTL_VRF_FLG_VIRTUAL_SPACE_TRACKING 0x00010000
-#define RTL_VRF_FLG_ENABLED_SYSTEM_WIDE 0x00020000
-#define RTL_VRF_FLG_MISCELLANEOUS_CHECKS 0x00020000
-#define RTL_VRF_FLG_LOCK_CHECKS 0x00040000
-
-#define APPLICATION_VERIFIER_INTERNAL_ERROR 0x80000000
-#define APPLICATION_VERIFIER_INTERNAL_WARNING 0x40000000
-#define APPLICATION_VERIFIER_NO_BREAK 0x20000000
-#define APPLICATION_VERIFIER_CONTINUABLE_BREAK 0x10000000
-
-#define APPLICATION_VERIFIER_UNKNOWN_ERROR 0x0001
-#define APPLICATION_VERIFIER_ACCESS_VIOLATION 0x0002
-#define APPLICATION_VERIFIER_UNSYNCHRONIZED_ACCESS 0x0003
-#define APPLICATION_VERIFIER_EXTREME_SIZE_REQUEST 0x0004
-#define APPLICATION_VERIFIER_BAD_HEAP_HANDLE 0x0005
-#define APPLICATION_VERIFIER_SWITCHED_HEAP_HANDLE 0x0006
-#define APPLICATION_VERIFIER_DOUBLE_FREE 0x0007
-#define APPLICATION_VERIFIER_CORRUPTED_HEAP_BLOCK 0x0008
-#define APPLICATION_VERIFIER_DESTROY_PROCESS_HEAP 0x0009
-#define APPLICATION_VERIFIER_UNEXPECTED_EXCEPTION 0x000A
-#define APPLICATION_VERIFIER_CORRUPTED_HEAP_BLOCK_EXCEPTION_RAISED_FOR_HEADER 0x000B
-#define APPLICATION_VERIFIER_CORRUPTED_HEAP_BLOCK_EXCEPTION_RAISED_FOR_PROBING 0x000C
-#define APPLICATION_VERIFIER_CORRUPTED_HEAP_BLOCK_HEADER 0x000D
-#define APPLICATION_VERIFIER_CORRUPTED_FREED_HEAP_BLOCK 0x000E
-#define APPLICATION_VERIFIER_CORRUPTED_HEAP_BLOCK_SUFFIX 0x000F
-#define APPLICATION_VERIFIER_CORRUPTED_HEAP_BLOCK_START_STAMP 0x0010
-#define APPLICATION_VERIFIER_CORRUPTED_HEAP_BLOCK_END_STAMP 0x0011
-#define APPLICATION_VERIFIER_CORRUPTED_HEAP_BLOCK_PREFIX 0x0012
-#define APPLICATION_VERIFIER_FIRST_CHANCE_ACCESS_VIOLATION 0x0013
-#define APPLICATION_VERIFIER_CORRUPTED_HEAP_LIST 0x0014
-
-#define APPLICATION_VERIFIER_TERMINATE_THREAD_CALL 0x0100
-#define APPLICATION_VERIFIER_STACK_OVERFLOW 0x0101
-#define APPLICATION_VERIFIER_INVALID_EXIT_PROCESS_CALL 0x0102
-
-#define APPLICATION_VERIFIER_EXIT_THREAD_OWNS_LOCK 0x0200
-#define APPLICATION_VERIFIER_LOCK_IN_UNLOADED_DLL 0x0201
-#define APPLICATION_VERIFIER_LOCK_IN_FREED_HEAP 0x0202
-#define APPLICATION_VERIFIER_LOCK_DOUBLE_INITIALIZE 0x0203
-#define APPLICATION_VERIFIER_LOCK_IN_FREED_MEMORY 0x0204
-#define APPLICATION_VERIFIER_LOCK_CORRUPTED 0x0205
-#define APPLICATION_VERIFIER_LOCK_INVALID_OWNER 0x0206
-#define APPLICATION_VERIFIER_LOCK_INVALID_RECURSION_COUNT 0x0207
-#define APPLICATION_VERIFIER_LOCK_INVALID_LOCK_COUNT 0x0208
-#define APPLICATION_VERIFIER_LOCK_OVER_RELEASED 0x0209
-#define APPLICATION_VERIFIER_LOCK_NOT_INITIALIZED 0x0210
-#define APPLICATION_VERIFIER_LOCK_ALREADY_INITIALIZED 0x0211
-#define APPLICATION_VERIFIER_LOCK_IN_FREED_VMEM 0x0212
-#define APPLICATION_VERIFIER_LOCK_IN_UNMAPPED_MEM 0x0213
-#define APPLICATION_VERIFIER_THREAD_NOT_LOCK_OWNER 0x0214
-
-#define APPLICATION_VERIFIER_INVALID_HANDLE 0x0300
-#define APPLICATION_VERIFIER_INVALID_TLS_VALUE 0x0301
-#define APPLICATION_VERIFIER_INCORRECT_WAIT_CALL 0x0302
-#define APPLICATION_VERIFIER_NULL_HANDLE 0x0303
-#define APPLICATION_VERIFIER_WAIT_IN_DLLMAIN 0x0304
-
-#define APPLICATION_VERIFIER_COM_ERROR 0x0400
-#define APPLICATION_VERIFIER_COM_API_IN_DLLMAIN 0x0401
-#define APPLICATION_VERIFIER_COM_UNHANDLED_EXCEPTION 0x0402
-#define APPLICATION_VERIFIER_COM_UNBALANCED_COINIT 0x0403
-#define APPLICATION_VERIFIER_COM_UNBALANCED_OLEINIT 0x0404
-#define APPLICATION_VERIFIER_COM_UNBALANCED_SWC 0x0405
-#define APPLICATION_VERIFIER_COM_NULL_DACL 0x0406
-#define APPLICATION_VERIFIER_COM_UNSAFE_IMPERSONATION 0x0407
-#define APPLICATION_VERIFIER_COM_SMUGGLED_WRAPPER 0x0408
-#define APPLICATION_VERIFIER_COM_SMUGGLED_PROXY 0x0409
-#define APPLICATION_VERIFIER_COM_CF_SUCCESS_WITH_NULL 0x040A
-#define APPLICATION_VERIFIER_COM_GCO_SUCCESS_WITH_NULL 0x040B
-#define APPLICATION_VERIFIER_COM_OBJECT_IN_FREED_MEMORY 0x040C
-#define APPLICATION_VERIFIER_COM_OBJECT_IN_UNLOADED_DLL 0x040D
-#define APPLICATION_VERIFIER_COM_VTBL_IN_FREED_MEMORY 0x040E
-#define APPLICATION_VERIFIER_COM_VTBL_IN_UNLOADED_DLL 0x040F
-#define APPLICATION_VERIFIER_COM_HOLDING_LOCKS_ON_CALL 0x0410
-
-#define APPLICATION_VERIFIER_RPC_ERROR 0x0500
-
-#define APPLICATION_VERIFIER_INVALID_FREEMEM 0x0600
-#define APPLICATION_VERIFIER_INVALID_ALLOCMEM 0x0601
-#define APPLICATION_VERIFIER_INVALID_MAPVIEW 0x0602
-#define APPLICATION_VERIFIER_PROBE_INVALID_ADDRESS 0x0603
-#define APPLICATION_VERIFIER_PROBE_FREE_MEM 0x0604
-#define APPLICATION_VERIFIER_PROBE_GUARD_PAGE 0x0605
-#define APPLICATION_VERIFIER_PROBE_NULL 0x0606
-#define APPLICATION_VERIFIER_PROBE_INVALID_START_OR_SIZE 0x0607
-#define APPLICATION_VERIFIER_SIZE_HEAP_UNEXPECTED_EXCEPTION 0x0618
-
-#define VERIFIER_STOP(Code,Msg,P1,S1,P2,S2,P3,S3,P4,S4) { RtlApplicationVerifierStop ((Code),(Msg),(ULONG_PTR)(P1),(S1),(ULONG_PTR)(P2),(S2),(ULONG_PTR)(P3),(S3),(ULONG_PTR)(P4),(S4)); }
-
-    VOID NTAPI RtlApplicationVerifierStop(ULONG_PTR Code,PSTR Message,ULONG_PTR Param1,PSTR Description1,ULONG_PTR Param2,PSTR Description2,ULONG_PTR Param3,PSTR Description3,ULONG_PTR Param4,PSTR Description4);
-
-    typedef LONG (NTAPI *PVECTORED_EXCEPTION_HANDLER)(struct _EXCEPTION_POINTERS *ExceptionInfo);
-#define SEF_DACL_AUTO_INHERIT 0x01
-#define SEF_SACL_AUTO_INHERIT 0x02
-#define SEF_DEFAULT_DESCRIPTOR_FOR_OBJECT 0x04
-#define SEF_AVOID_PRIVILEGE_CHECK 0x08
-#define SEF_AVOID_OWNER_CHECK 0x10
-#define SEF_DEFAULT_OWNER_FROM_PARENT 0x20
-#define SEF_DEFAULT_GROUP_FROM_PARENT 0x40
-
-    typedef enum _HEAP_INFORMATION_CLASS {
-      HeapCompatibilityInformation
-    } HEAP_INFORMATION_CLASS;
-
-    NTSYSAPI DWORD NTAPI RtlSetHeapInformation(PVOID HeapHandle,HEAP_INFORMATION_CLASS HeapInformationClass,PVOID HeapInformation,SIZE_T HeapInformationLength);
-    NTSYSAPI DWORD NTAPI RtlQueryHeapInformation(PVOID HeapHandle,HEAP_INFORMATION_CLASS HeapInformationClass,PVOID HeapInformation,SIZE_T HeapInformationLength,PSIZE_T ReturnLength);
-    DWORD NTAPI RtlMultipleAllocateHeap(PVOID HeapHandle,DWORD Flags,SIZE_T Size,DWORD Count,PVOID *Array);
-    DWORD NTAPI RtlMultipleFreeHeap(PVOID HeapHandle,DWORD Flags,DWORD Count,PVOID *Array);
-
-#define WT_EXECUTEDEFAULT 0x00000000
-#define WT_EXECUTEINIOTHREAD 0x00000001
-#define WT_EXECUTEINUITHREAD 0x00000002
-#define WT_EXECUTEINWAITTHREAD 0x00000004
-#define WT_EXECUTEONLYONCE 0x00000008
-#define WT_EXECUTEINTIMERTHREAD 0x00000020
-#define WT_EXECUTELONGFUNCTION 0x00000010
-#define WT_EXECUTEINPERSISTENTIOTHREAD 0x00000040
-#define WT_EXECUTEINPERSISTENTTHREAD 0x00000080
-#define WT_TRANSFER_IMPERSONATION 0x00000100
-#define WT_SET_MAX_THREADPOOL_THREADS(Flags,Limit) ((Flags) |= (Limit)<<16)
-    typedef VOID (NTAPI *WAITORTIMERCALLBACKFUNC)(PVOID,BOOLEAN);
-    typedef VOID (NTAPI *WORKERCALLBACKFUNC)(PVOID);
-    typedef VOID (NTAPI *APC_CALLBACK_FUNCTION)(DWORD ,PVOID,PVOID);
-    typedef
-      VOID
-      (NTAPI *PFLS_CALLBACK_FUNCTION)(PVOID lpFlsData);
-#define WT_EXECUTEINLONGTHREAD 0x00000010
-#define WT_EXECUTEDELETEWAIT 0x00000008
-
-    typedef enum _ACTIVATION_CONTEXT_INFO_CLASS {
-      ActivationContextBasicInformation = 1,ActivationContextDetailedInformation = 2,AssemblyDetailedInformationInActivationContext = 3,FileInformationInAssemblyOfAssemblyInActivationContext = 4,MaxActivationContextInfoClass,AssemblyDetailedInformationInActivationContxt = 3,FileInformationInAssemblyOfAssemblyInActivationContxt = 4
-    } ACTIVATION_CONTEXT_INFO_CLASS;
-
-#define ACTIVATIONCONTEXTINFOCLASS ACTIVATION_CONTEXT_INFO_CLASS
-
-    typedef struct _ACTIVATION_CONTEXT_QUERY_INDEX {
-      DWORD ulAssemblyIndex;
-      DWORD ulFileIndexInAssembly;
-    } ACTIVATION_CONTEXT_QUERY_INDEX,*PACTIVATION_CONTEXT_QUERY_INDEX;
-
-    typedef const struct _ACTIVATION_CONTEXT_QUERY_INDEX *PCACTIVATION_CONTEXT_QUERY_INDEX;
-
-#define ACTIVATION_CONTEXT_PATH_TYPE_NONE (1)
-#define ACTIVATION_CONTEXT_PATH_TYPE_WIN32_FILE (2)
-#define ACTIVATION_CONTEXT_PATH_TYPE_URL (3)
-#define ACTIVATION_CONTEXT_PATH_TYPE_ASSEMBLYREF (4)
-
-    typedef struct _ASSEMBLY_FILE_DETAILED_INFORMATION {
-      DWORD ulFlags;
-      DWORD ulFilenameLength;
-      DWORD ulPathLength;
-
-      PCWSTR lpFileName;
-      PCWSTR lpFilePath;
-    } ASSEMBLY_FILE_DETAILED_INFORMATION,*PASSEMBLY_FILE_DETAILED_INFORMATION;
-    typedef const ASSEMBLY_FILE_DETAILED_INFORMATION *PCASSEMBLY_FILE_DETAILED_INFORMATION;
-
-#define _ASSEMBLY_DLL_REDIRECTION_DETAILED_INFORMATION _ASSEMBLY_FILE_DETAILED_INFORMATION
-#define ASSEMBLY_DLL_REDIRECTION_DETAILED_INFORMATION ASSEMBLY_FILE_DETAILED_INFORMATION
-#define PASSEMBLY_DLL_REDIRECTION_DETAILED_INFORMATION PASSEMBLY_FILE_DETAILED_INFORMATION
-#define PCASSEMBLY_DLL_REDIRECTION_DETAILED_INFORMATION PCASSEMBLY_FILE_DETAILED_INFORMATION
-
-    typedef struct _ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION {
-      DWORD ulFlags;
-      DWORD ulEncodedAssemblyIdentityLength;
-      DWORD ulManifestPathType;
-      DWORD ulManifestPathLength;
-      LARGE_INTEGER liManifestLastWriteTime;
-      DWORD ulPolicyPathType;
-      DWORD ulPolicyPathLength;
-      LARGE_INTEGER liPolicyLastWriteTime;
-      DWORD ulMetadataSatelliteRosterIndex;
-      DWORD ulManifestVersionMajor;
-      DWORD ulManifestVersionMinor;
-      DWORD ulPolicyVersionMajor;
-      DWORD ulPolicyVersionMinor;
-      DWORD ulAssemblyDirectoryNameLength;
-      PCWSTR lpAssemblyEncodedAssemblyIdentity;
-      PCWSTR lpAssemblyManifestPath;
-      PCWSTR lpAssemblyPolicyPath;
-      PCWSTR lpAssemblyDirectoryName;
-      DWORD ulFileCount;
-    } ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION,*PACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION;
-
-    typedef const struct _ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION *PCACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION;
-
-    typedef struct _ACTIVATION_CONTEXT_DETAILED_INFORMATION {
-      DWORD dwFlags;
-      DWORD ulFormatVersion;
-      DWORD ulAssemblyCount;
-      DWORD ulRootManifestPathType;
-      DWORD ulRootManifestPathChars;
-      DWORD ulRootConfigurationPathType;
-      DWORD ulRootConfigurationPathChars;
-      DWORD ulAppDirPathType;
-      DWORD ulAppDirPathChars;
-      PCWSTR lpRootManifestPath;
-      PCWSTR lpRootConfigurationPath;
-      PCWSTR lpAppDirPath;
-    } ACTIVATION_CONTEXT_DETAILED_INFORMATION,*PACTIVATION_CONTEXT_DETAILED_INFORMATION;
-
-    typedef const struct _ACTIVATION_CONTEXT_DETAILED_INFORMATION *PCACTIVATION_CONTEXT_DETAILED_INFORMATION;
-
-#define DLL_PROCESS_ATTACH 1
-#define DLL_THREAD_ATTACH 2
-#define DLL_THREAD_DETACH 3
-#define DLL_PROCESS_DETACH 0
-#define DLL_PROCESS_VERIFIER 4
-
-#define EVENTLOG_SEQUENTIAL_READ 0x0001
-#define EVENTLOG_SEEK_READ 0x0002
-#define EVENTLOG_FORWARDS_READ 0x0004
-#define EVENTLOG_BACKWARDS_READ 0x0008
-
-#define EVENTLOG_SUCCESS 0x0000
-#define EVENTLOG_ERROR_TYPE 0x0001
-#define EVENTLOG_WARNING_TYPE 0x0002
-#define EVENTLOG_INFORMATION_TYPE 0x0004
-#define EVENTLOG_AUDIT_SUCCESS 0x0008
-#define EVENTLOG_AUDIT_FAILURE 0x0010
-
-#define EVENTLOG_START_PAIRED_EVENT 0x0001
-#define EVENTLOG_END_PAIRED_EVENT 0x0002
-#define EVENTLOG_END_ALL_PAIRED_EVENTS 0x0004
-#define EVENTLOG_PAIRED_EVENT_ACTIVE 0x0008
-#define EVENTLOG_PAIRED_EVENT_INACTIVE 0x0010
-
-    typedef struct _EVENTLOGRECORD {
-      DWORD Length;
-      DWORD Reserved;
-      DWORD RecordNumber;
-      DWORD TimeGenerated;
-      DWORD TimeWritten;
-      DWORD EventID;
-      WORD EventType;
-      WORD NumStrings;
-      WORD EventCategory;
-      WORD ReservedFlags;
-      DWORD ClosingRecordNumber;
-      DWORD StringOffset;
-      DWORD UserSidLength;
-      DWORD UserSidOffset;
-      DWORD DataLength;
-      DWORD DataOffset;
-    } EVENTLOGRECORD,*PEVENTLOGRECORD;
-
-#define MAXLOGICALLOGNAMESIZE 256
-
-    typedef struct _EVENTSFORLOGFILE{
-      DWORD ulSize;
-      WCHAR szLogicalLogFile[MAXLOGICALLOGNAMESIZE];
-      DWORD ulNumRecords;
-      EVENTLOGRECORD pEventLogRecords[];
-    } EVENTSFORLOGFILE,*PEVENTSFORLOGFILE;
-
-    typedef struct _PACKEDEVENTINFO{
-      DWORD ulSize;
-      DWORD ulNumEventsForLogFile;
-      DWORD ulOffsets[];
-    } PACKEDEVENTINFO,*PPACKEDEVENTINFO;
-
-#define KEY_QUERY_VALUE (0x0001)
-#define KEY_SET_VALUE (0x0002)
-#define KEY_CREATE_SUB_KEY (0x0004)
-#define KEY_ENUMERATE_SUB_KEYS (0x0008)
-#define KEY_NOTIFY (0x0010)
-#define KEY_CREATE_LINK (0x0020)
-#define KEY_WOW64_32KEY (0x0200)
-#define KEY_WOW64_64KEY (0x0100)
-#define KEY_WOW64_RES (0x0300)
-
-#define KEY_READ ((STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY) & (~SYNCHRONIZE))
-#define KEY_WRITE ((STANDARD_RIGHTS_WRITE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY) & (~SYNCHRONIZE))
-#define KEY_EXECUTE ((KEY_READ) & (~SYNCHRONIZE))
-#define KEY_ALL_ACCESS ((STANDARD_RIGHTS_ALL | KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY | KEY_CREATE_LINK) & (~SYNCHRONIZE))
-#define REG_OPTION_RESERVED (0x00000000L)
-
-#define REG_OPTION_NON_VOLATILE (0x00000000L)
-#define REG_OPTION_VOLATILE (0x00000001L)
-#define REG_OPTION_CREATE_LINK (0x00000002L)
-#define REG_OPTION_BACKUP_RESTORE (0x00000004L)
-#define REG_OPTION_OPEN_LINK (0x00000008L)
-#define REG_LEGAL_OPTION (REG_OPTION_RESERVED | REG_OPTION_NON_VOLATILE | REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK | REG_OPTION_BACKUP_RESTORE | REG_OPTION_OPEN_LINK)
-#define REG_CREATED_NEW_KEY (0x00000001L)
-#define REG_OPENED_EXISTING_KEY (0x00000002L)
-#define REG_STANDARD_FORMAT 1
-#define REG_LATEST_FORMAT 2
-#define REG_NO_COMPRESSION 4
-#define REG_WHOLE_HIVE_VOLATILE (0x00000001L)
-#define REG_REFRESH_HIVE (0x00000002L)
-#define REG_NO_LAZY_FLUSH (0x00000004L)
-#define REG_FORCE_RESTORE (0x00000008L)
-#define REG_FORCE_UNLOAD 1
-
-#define REG_NOTIFY_CHANGE_NAME (0x00000001L)
-#define REG_NOTIFY_CHANGE_ATTRIBUTES (0x00000002L)
-#define REG_NOTIFY_CHANGE_LAST_SET (0x00000004L)
-#define REG_NOTIFY_CHANGE_SECURITY (0x00000008L)
-
-#define REG_LEGAL_CHANGE_FILTER (REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES | REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY)
-
-#define REG_NONE (0)
-#define REG_SZ (1)
-#define REG_EXPAND_SZ (2)
-
-#define REG_BINARY (3)
-#define REG_DWORD (4)
-#define REG_DWORD_LITTLE_ENDIAN (4)
-#define REG_DWORD_BIG_ENDIAN (5)
-#define REG_LINK (6)
-#define REG_MULTI_SZ (7)
-#define REG_RESOURCE_LIST (8)
-#define REG_FULL_RESOURCE_DESCRIPTOR (9)
-#define REG_RESOURCE_REQUIREMENTS_LIST (10)
-#define REG_QWORD (11)
-#define REG_QWORD_LITTLE_ENDIAN (11)
-
-#define SERVICE_KERNEL_DRIVER 0x00000001
-#define SERVICE_FILE_SYSTEM_DRIVER 0x00000002
-#define SERVICE_ADAPTER 0x00000004
-#define SERVICE_RECOGNIZER_DRIVER 0x00000008
-
-#define SERVICE_DRIVER (SERVICE_KERNEL_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_RECOGNIZER_DRIVER)
-
-#define SERVICE_WIN32_OWN_PROCESS 0x00000010
-#define SERVICE_WIN32_SHARE_PROCESS 0x00000020
-#define SERVICE_WIN32 (SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS)
-
-#define SERVICE_INTERACTIVE_PROCESS 0x00000100
-
-#define SERVICE_TYPE_ALL (SERVICE_WIN32 | SERVICE_ADAPTER | SERVICE_DRIVER | SERVICE_INTERACTIVE_PROCESS)
-
-#define SERVICE_BOOT_START 0x00000000
-#define SERVICE_SYSTEM_START 0x00000001
-#define SERVICE_AUTO_START 0x00000002
-#define SERVICE_DEMAND_START 0x00000003
-#define SERVICE_DISABLED 0x00000004
-
-#define SERVICE_ERROR_IGNORE 0x00000000
-#define SERVICE_ERROR_NORMAL 0x00000001
-#define SERVICE_ERROR_SEVERE 0x00000002
-#define SERVICE_ERROR_CRITICAL 0x00000003
-
-    typedef enum _CM_SERVICE_NODE_TYPE {
-      DriverType = SERVICE_KERNEL_DRIVER,FileSystemType = SERVICE_FILE_SYSTEM_DRIVER,Win32ServiceOwnProcess = SERVICE_WIN32_OWN_PROCESS,
-      Win32ServiceShareProcess = SERVICE_WIN32_SHARE_PROCESS,AdapterType = SERVICE_ADAPTER,RecognizerType = SERVICE_RECOGNIZER_DRIVER
-    } SERVICE_NODE_TYPE;
-
-    typedef enum _CM_SERVICE_LOAD_TYPE {
-      BootLoad = SERVICE_BOOT_START,SystemLoad = SERVICE_SYSTEM_START,AutoLoad = SERVICE_AUTO_START,DemandLoad = SERVICE_DEMAND_START,
-      DisableLoad = SERVICE_DISABLED
-    } SERVICE_LOAD_TYPE;
-
-    typedef enum _CM_ERROR_CONTROL_TYPE {
-      IgnoreError = SERVICE_ERROR_IGNORE,NormalError = SERVICE_ERROR_NORMAL,SevereError = SERVICE_ERROR_SEVERE,CriticalError = SERVICE_ERROR_CRITICAL
-    } SERVICE_ERROR_TYPE;
-
-#define TAPE_ERASE_SHORT 0L
-#define TAPE_ERASE_LONG 1L
-
-    typedef struct _TAPE_ERASE {
-      DWORD Type;
-      BOOLEAN Immediate;
-    } TAPE_ERASE,*PTAPE_ERASE;
-
-#define TAPE_LOAD 0L
-#define TAPE_UNLOAD 1L
-#define TAPE_TENSION 2L
-#define TAPE_LOCK 3L
-#define TAPE_UNLOCK 4L
-#define TAPE_FORMAT 5L
-
-    typedef struct _TAPE_PREPARE {
-      DWORD Operation;
-      BOOLEAN Immediate;
-    } TAPE_PREPARE,*PTAPE_PREPARE;
-
-#define TAPE_SETMARKS 0L
-#define TAPE_FILEMARKS 1L
-#define TAPE_SHORT_FILEMARKS 2L
-#define TAPE_LONG_FILEMARKS 3L
-
-    typedef struct _TAPE_WRITE_MARKS {
-      DWORD Type;
-      DWORD Count;
-      BOOLEAN Immediate;
-    } TAPE_WRITE_MARKS,*PTAPE_WRITE_MARKS;
-
-#define TAPE_ABSOLUTE_POSITION 0L
-#define TAPE_LOGICAL_POSITION 1L
-#define TAPE_PSEUDO_LOGICAL_POSITION 2L
-
-    typedef struct _TAPE_GET_POSITION {
-      DWORD Type;
-      DWORD Partition;
-      LARGE_INTEGER Offset;
-    } TAPE_GET_POSITION,*PTAPE_GET_POSITION;
-
-#define TAPE_REWIND 0L
-#define TAPE_ABSOLUTE_BLOCK 1L
-#define TAPE_LOGICAL_BLOCK 2L
-#define TAPE_PSEUDO_LOGICAL_BLOCK 3L
-#define TAPE_SPACE_END_OF_DATA 4L
-#define TAPE_SPACE_RELATIVE_BLOCKS 5L
-#define TAPE_SPACE_FILEMARKS 6L
-#define TAPE_SPACE_SEQUENTIAL_FMKS 7L
-#define TAPE_SPACE_SETMARKS 8L
-#define TAPE_SPACE_SEQUENTIAL_SMKS 9L
-
-    typedef struct _TAPE_SET_POSITION {
-      DWORD Method;
-      DWORD Partition;
-      LARGE_INTEGER Offset;
-      BOOLEAN Immediate;
-    } TAPE_SET_POSITION,*PTAPE_SET_POSITION;
-
-#define TAPE_DRIVE_FIXED 0x00000001
-#define TAPE_DRIVE_SELECT 0x00000002
-#define TAPE_DRIVE_INITIATOR 0x00000004
-
-#define TAPE_DRIVE_ERASE_SHORT 0x00000010
-#define TAPE_DRIVE_ERASE_LONG 0x00000020
-#define TAPE_DRIVE_ERASE_BOP_ONLY 0x00000040
-#define TAPE_DRIVE_ERASE_IMMEDIATE 0x00000080
-
-#define TAPE_DRIVE_TAPE_CAPACITY 0x00000100
-#define TAPE_DRIVE_TAPE_REMAINING 0x00000200
-#define TAPE_DRIVE_FIXED_BLOCK 0x00000400
-#define TAPE_DRIVE_VARIABLE_BLOCK 0x00000800
-
-#define TAPE_DRIVE_WRITE_PROTECT 0x00001000
-#define TAPE_DRIVE_EOT_WZ_SIZE 0x00002000
-
-#define TAPE_DRIVE_ECC 0x00010000
-#define TAPE_DRIVE_COMPRESSION 0x00020000
-#define TAPE_DRIVE_PADDING 0x00040000
-#define TAPE_DRIVE_REPORT_SMKS 0x00080000
-
-#define TAPE_DRIVE_GET_ABSOLUTE_BLK 0x00100000
-#define TAPE_DRIVE_GET_LOGICAL_BLK 0x00200000
-#define TAPE_DRIVE_SET_EOT_WZ_SIZE 0x00400000
-
-#define TAPE_DRIVE_EJECT_MEDIA 0x01000000
-#define TAPE_DRIVE_CLEAN_REQUESTS 0x02000000
-#define TAPE_DRIVE_SET_CMP_BOP_ONLY 0x04000000
-
-#define TAPE_DRIVE_RESERVED_BIT 0x80000000
-
-#define TAPE_DRIVE_LOAD_UNLOAD 0x80000001
-#define TAPE_DRIVE_TENSION 0x80000002
-#define TAPE_DRIVE_LOCK_UNLOCK 0x80000004
-#define TAPE_DRIVE_REWIND_IMMEDIATE 0x80000008
-
-#define TAPE_DRIVE_SET_BLOCK_SIZE 0x80000010
-#define TAPE_DRIVE_LOAD_UNLD_IMMED 0x80000020
-#define TAPE_DRIVE_TENSION_IMMED 0x80000040
-#define TAPE_DRIVE_LOCK_UNLK_IMMED 0x80000080
-
-#define TAPE_DRIVE_SET_ECC 0x80000100
-#define TAPE_DRIVE_SET_COMPRESSION 0x80000200
-#define TAPE_DRIVE_SET_PADDING 0x80000400
-#define TAPE_DRIVE_SET_REPORT_SMKS 0x80000800
-
-#define TAPE_DRIVE_ABSOLUTE_BLK 0x80001000
-#define TAPE_DRIVE_ABS_BLK_IMMED 0x80002000
-#define TAPE_DRIVE_LOGICAL_BLK 0x80004000
-#define TAPE_DRIVE_LOG_BLK_IMMED 0x80008000
-
-#define TAPE_DRIVE_END_OF_DATA 0x80010000
-#define TAPE_DRIVE_RELATIVE_BLKS 0x80020000
-#define TAPE_DRIVE_FILEMARKS 0x80040000
-#define TAPE_DRIVE_SEQUENTIAL_FMKS 0x80080000
-
-#define TAPE_DRIVE_SETMARKS 0x80100000
-#define TAPE_DRIVE_SEQUENTIAL_SMKS 0x80200000
-#define TAPE_DRIVE_REVERSE_POSITION 0x80400000
-#define TAPE_DRIVE_SPACE_IMMEDIATE 0x80800000
-
-#define TAPE_DRIVE_WRITE_SETMARKS 0x81000000
-#define TAPE_DRIVE_WRITE_FILEMARKS 0x82000000
-#define TAPE_DRIVE_WRITE_SHORT_FMKS 0x84000000
-#define TAPE_DRIVE_WRITE_LONG_FMKS 0x88000000
-
-#define TAPE_DRIVE_WRITE_MARK_IMMED 0x90000000
-#define TAPE_DRIVE_FORMAT 0xA0000000
-#define TAPE_DRIVE_FORMAT_IMMEDIATE 0xC0000000
-#define TAPE_DRIVE_HIGH_FEATURES 0x80000000
-
-    typedef struct _TAPE_GET_DRIVE_PARAMETERS {
-      BOOLEAN ECC;
-      BOOLEAN Compression;
-      BOOLEAN DataPadding;
-      BOOLEAN ReportSetmarks;
-      DWORD DefaultBlockSize;
-      DWORD MaximumBlockSize;
-      DWORD MinimumBlockSize;
-      DWORD MaximumPartitionCount;
-      DWORD FeaturesLow;
-      DWORD FeaturesHigh;
-      DWORD EOTWarningZoneSize;
-    } TAPE_GET_DRIVE_PARAMETERS,*PTAPE_GET_DRIVE_PARAMETERS;
-
-    typedef struct _TAPE_SET_DRIVE_PARAMETERS {
-      BOOLEAN ECC;
-      BOOLEAN Compression;
-      BOOLEAN DataPadding;
-      BOOLEAN ReportSetmarks;
-      DWORD EOTWarningZoneSize;
-    } TAPE_SET_DRIVE_PARAMETERS,*PTAPE_SET_DRIVE_PARAMETERS;
-
-    typedef struct _TAPE_GET_MEDIA_PARAMETERS {
-      LARGE_INTEGER Capacity;
-      LARGE_INTEGER Remaining;
-      DWORD BlockSize;
-      DWORD PartitionCount;
-      BOOLEAN WriteProtected;
-    } TAPE_GET_MEDIA_PARAMETERS,*PTAPE_GET_MEDIA_PARAMETERS;
-
-    typedef struct _TAPE_SET_MEDIA_PARAMETERS {
-      DWORD BlockSize;
-    } TAPE_SET_MEDIA_PARAMETERS,*PTAPE_SET_MEDIA_PARAMETERS;
-
-#define TAPE_FIXED_PARTITIONS 0L
-#define TAPE_SELECT_PARTITIONS 1L
-#define TAPE_INITIATOR_PARTITIONS 2L
-
-    typedef struct _TAPE_CREATE_PARTITION {
-      DWORD Method;
-      DWORD Count;
-      DWORD Size;
-    } TAPE_CREATE_PARTITION,*PTAPE_CREATE_PARTITION;
-
-#define TAPE_QUERY_DRIVE_PARAMETERS 0L
-#define TAPE_QUERY_MEDIA_CAPACITY 1L
-#define TAPE_CHECK_FOR_DRIVE_PROBLEM 2L
-#define TAPE_QUERY_IO_ERROR_DATA 3L
-#define TAPE_QUERY_DEVICE_ERROR_DATA 4L
-
-    typedef struct _TAPE_WMI_OPERATIONS {
-      DWORD Method;
-      DWORD DataBufferSize;
-      PVOID DataBuffer;
-    } TAPE_WMI_OPERATIONS,*PTAPE_WMI_OPERATIONS;
-
-    typedef enum _TAPE_DRIVE_PROBLEM_TYPE {
-      TapeDriveProblemNone,TapeDriveReadWriteWarning,TapeDriveReadWriteError,TapeDriveReadWarning,TapeDriveWriteWarning,TapeDriveReadError,TapeDriveWriteError,TapeDriveHardwareError,TapeDriveUnsupportedMedia,TapeDriveScsiConnectionError,TapeDriveTimetoClean,TapeDriveCleanDriveNow,TapeDriveMediaLifeExpired,TapeDriveSnappedTape
-    } TAPE_DRIVE_PROBLEM_TYPE;
-
-#if defined(__x86_64)
-    __CRT_INLINE struct _TEB *NtCurrentTeb(VOID) { return (struct _TEB *)__readgsqword(FIELD_OFFSET(NT_TIB,Self)); }
-    __CRT_INLINE PVOID GetCurrentFiber(VOID) { return(PVOID)__readgsqword(FIELD_OFFSET(NT_TIB,FiberData)); }
-    __CRT_INLINE PVOID GetFiberData(VOID) {
-      return *(PVOID *)GetCurrentFiber();
-    }
-#endif
-
-#if(defined(_X86_) && !defined(__x86_64))
-#define PcTeb 0x18
-    __CRT_INLINE struct _TEB *NtCurrentTeb(void) {
-      struct _TEB *ret;
-      __asm__ volatile ("movl	%%fs:0x18,%0"
-	: "=r" (ret));
-      return ret;
-    }
-#endif
-
-#define ACTIVATION_CONTEXT_SECTION_ASSEMBLY_INFORMATION (1)
-#define ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION (2)
-#define ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION (3)
-#define ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION (4)
-#define ACTIVATION_CONTEXT_SECTION_COM_INTERFACE_REDIRECTION (5)
-#define ACTIVATION_CONTEXT_SECTION_COM_TYPE_LIBRARY_REDIRECTION (6)
-#define ACTIVATION_CONTEXT_SECTION_COM_PROGID_REDIRECTION (7)
-#define ACTIVATION_CONTEXT_SECTION_GLOBAL_OBJECT_RENAME_TABLE (8)
-#define ACTIVATION_CONTEXT_SECTION_CLR_SURROGATES (9)
-#define ACTIVATION_CONTEXT_SECTION_APPLICATION_SETTINGS (10)
-
-#ifdef __cplusplus
-  }
-#endif
-#endif
diff --git a/tinyc/win32/include/winapi/winreg.h b/tinyc/win32/include/winapi/winreg.h
deleted file mode 100644
index f158d2820..000000000
--- a/tinyc/win32/include/winapi/winreg.h
+++ /dev/null
@@ -1,272 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _WINREG_
-#define _WINREG_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef WINVER
-#define WINVER 0x0502
-#endif
-
-#define RRF_RT_REG_NONE 0x00000001
-#define RRF_RT_REG_SZ 0x00000002
-#define RRF_RT_REG_EXPAND_SZ 0x00000004
-#define RRF_RT_REG_BINARY 0x00000008
-#define RRF_RT_REG_DWORD 0x00000010
-#define RRF_RT_REG_MULTI_SZ 0x00000020
-#define RRF_RT_REG_QWORD 0x00000040
-
-#define RRF_RT_DWORD (RRF_RT_REG_BINARY | RRF_RT_REG_DWORD)
-#define RRF_RT_QWORD (RRF_RT_REG_BINARY | RRF_RT_REG_QWORD)
-#define RRF_RT_ANY 0x0000ffff
-
-#define RRF_NOEXPAND 0x10000000
-#define RRF_ZEROONFAILURE 0x20000000
-
-  typedef ACCESS_MASK REGSAM;
-
-#define HKEY_CLASSES_ROOT ((HKEY) (ULONG_PTR)((LONG)0x80000000))
-#define HKEY_CURRENT_USER ((HKEY) (ULONG_PTR)((LONG)0x80000001))
-#define HKEY_LOCAL_MACHINE ((HKEY) (ULONG_PTR)((LONG)0x80000002))
-#define HKEY_USERS ((HKEY) (ULONG_PTR)((LONG)0x80000003))
-#define HKEY_PERFORMANCE_DATA ((HKEY) (ULONG_PTR)((LONG)0x80000004))
-#define HKEY_PERFORMANCE_TEXT ((HKEY) (ULONG_PTR)((LONG)0x80000050))
-#define HKEY_PERFORMANCE_NLSTEXT ((HKEY) (ULONG_PTR)((LONG)0x80000060))
-#define HKEY_CURRENT_CONFIG ((HKEY) (ULONG_PTR)((LONG)0x80000005))
-#define HKEY_DYN_DATA ((HKEY) (ULONG_PTR)((LONG)0x80000006))
-
-#define REG_SECURE_CONNECTION 1
-
-#ifndef _PROVIDER_STRUCTS_DEFINED
-#define _PROVIDER_STRUCTS_DEFINED
-
-#define PROVIDER_KEEPS_VALUE_LENGTH 0x1
-  struct val_context {
-    int valuelen;
-    LPVOID value_context;
-    LPVOID val_buff_ptr;
-  };
-
-  typedef struct val_context *PVALCONTEXT;
-
-  typedef struct pvalueA {
-    LPSTR pv_valuename;
-    int pv_valuelen;
-    LPVOID pv_value_context;
-    DWORD pv_type;
-  }PVALUEA,*PPVALUEA;
-
-  typedef struct pvalueW {
-    LPWSTR pv_valuename;
-    int pv_valuelen;
-    LPVOID pv_value_context;
-    DWORD pv_type;
-  }PVALUEW,*PPVALUEW;
-
-#ifdef UNICODE
-  typedef PVALUEW PVALUE;
-  typedef PPVALUEW PPVALUE;
-#else
-  typedef PVALUEA PVALUE;
-  typedef PPVALUEA PPVALUE;
-#endif
-
-  typedef DWORD __cdecl QUERYHANDLER(LPVOID keycontext,PVALCONTEXT val_list,DWORD num_vals,LPVOID outputbuffer,DWORD *total_outlen,DWORD input_blen);
-
-  typedef QUERYHANDLER *PQUERYHANDLER;
-
-  typedef struct provider_info {
-    PQUERYHANDLER pi_R0_1val;
-    PQUERYHANDLER pi_R0_allvals;
-    PQUERYHANDLER pi_R3_1val;
-    PQUERYHANDLER pi_R3_allvals;
-    DWORD pi_flags;
-    LPVOID pi_key_context;
-  } REG_PROVIDER;
-
-  typedef struct provider_info *PPROVIDER;
-
-  typedef struct value_entA {
-    LPSTR ve_valuename;
-    DWORD ve_valuelen;
-    DWORD_PTR ve_valueptr;
-    DWORD ve_type;
-  } VALENTA,*PVALENTA;
-
-  typedef struct value_entW {
-    LPWSTR ve_valuename;
-    DWORD ve_valuelen;
-    DWORD_PTR ve_valueptr;
-    DWORD ve_type;
-  } VALENTW,*PVALENTW;
-
-#ifdef UNICODE
-  typedef VALENTW VALENT;
-  typedef PVALENTW PVALENT;
-#else
-  typedef VALENTA VALENT;
-  typedef PVALENTA PVALENT;
-#endif
-#endif
-
-#define WIN31_CLASS NULL
-
-#ifdef UNICODE
-#define RegConnectRegistry RegConnectRegistryW
-#define RegConnectRegistryEx RegConnectRegistryExW
-#define RegCreateKey RegCreateKeyW
-#define RegCreateKeyEx RegCreateKeyExW
-#define RegDeleteKey RegDeleteKeyW
-#define RegDeleteKeyEx RegDeleteKeyExW
-#define RegDeleteValue RegDeleteValueW
-#define RegEnumKey RegEnumKeyW
-#define RegEnumKeyEx RegEnumKeyExW
-#define RegEnumValue RegEnumValueW
-#define RegLoadKey RegLoadKeyW
-#define RegOpenKey RegOpenKeyW
-#define RegOpenKeyEx RegOpenKeyExW
-#define RegQueryInfoKey RegQueryInfoKeyW
-#define RegQueryValue RegQueryValueW
-#define RegQueryMultipleValues RegQueryMultipleValuesW
-#define RegQueryValueEx RegQueryValueExW
-#define RegReplaceKey RegReplaceKeyW
-#define RegRestoreKey RegRestoreKeyW
-#define RegSaveKey RegSaveKeyW
-#define RegSetValue RegSetValueW
-#define RegSetValueEx RegSetValueExW
-#define RegUnLoadKey RegUnLoadKeyW
-#define RegGetValue RegGetValueW
-#define InitiateSystemShutdown InitiateSystemShutdownW
-#define AbortSystemShutdown AbortSystemShutdownW
-#else
-#define RegConnectRegistry RegConnectRegistryA
-#define RegConnectRegistryEx RegConnectRegistryExA
-#define RegCreateKey RegCreateKeyA
-#define RegCreateKeyEx RegCreateKeyExA
-#define RegDeleteKey RegDeleteKeyA
-#define RegDeleteKeyEx RegDeleteKeyExA
-#define RegDeleteValue RegDeleteValueA
-#define RegEnumKey RegEnumKeyA
-#define RegEnumKeyEx RegEnumKeyExA
-#define RegEnumValue RegEnumValueA
-#define RegLoadKey RegLoadKeyA
-#define RegOpenKey RegOpenKeyA
-#define RegOpenKeyEx RegOpenKeyExA
-#define RegQueryInfoKey RegQueryInfoKeyA
-#define RegQueryValue RegQueryValueA
-#define RegQueryMultipleValues RegQueryMultipleValuesA
-#define RegQueryValueEx RegQueryValueExA
-#define RegReplaceKey RegReplaceKeyA
-#define RegRestoreKey RegRestoreKeyA
-#define RegSaveKey RegSaveKeyA
-#define RegSetValue RegSetValueA
-#define RegSetValueEx RegSetValueExA
-#define RegUnLoadKey RegUnLoadKeyA
-#define RegGetValue RegGetValueA
-#define InitiateSystemShutdown InitiateSystemShutdownA
-#define AbortSystemShutdown AbortSystemShutdownA
-#endif
-
-  WINADVAPI LONG WINAPI RegCloseKey(HKEY hKey);
-  WINADVAPI LONG WINAPI RegOverridePredefKey(HKEY hKey,HKEY hNewHKey);
-  WINADVAPI LONG WINAPI RegOpenUserClassesRoot(HANDLE hToken,DWORD dwOptions,REGSAM samDesired,PHKEY phkResult);
-  WINADVAPI LONG WINAPI RegOpenCurrentUser(REGSAM samDesired,PHKEY phkResult);
-  WINADVAPI LONG WINAPI RegDisablePredefinedCache();
-  WINADVAPI LONG WINAPI RegConnectRegistryA(LPCSTR lpMachineName,HKEY hKey,PHKEY phkResult);
-  WINADVAPI LONG WINAPI RegConnectRegistryW(LPCWSTR lpMachineName,HKEY hKey,PHKEY phkResult);
-  WINADVAPI LONG WINAPI RegConnectRegistryExA(LPCSTR lpMachineName,HKEY hKey,ULONG Flags,PHKEY phkResult);
-  WINADVAPI LONG WINAPI RegConnectRegistryExW(LPCWSTR lpMachineName,HKEY hKey,ULONG Flags,PHKEY phkResult);
-  WINADVAPI LONG WINAPI RegCreateKeyA(HKEY hKey,LPCSTR lpSubKey,PHKEY phkResult);
-  WINADVAPI LONG WINAPI RegCreateKeyW(HKEY hKey,LPCWSTR lpSubKey,PHKEY phkResult);
-  WINADVAPI LONG WINAPI RegCreateKeyExA(HKEY hKey,LPCSTR lpSubKey,DWORD Reserved,LPSTR lpClass,DWORD dwOptions,REGSAM samDesired,LPSECURITY_ATTRIBUTES lpSecurityAttributes,PHKEY phkResult,LPDWORD lpdwDisposition);
-  WINADVAPI LONG WINAPI RegCreateKeyExW(HKEY hKey,LPCWSTR lpSubKey,DWORD Reserved,LPWSTR lpClass,DWORD dwOptions,REGSAM samDesired,LPSECURITY_ATTRIBUTES lpSecurityAttributes,PHKEY phkResult,LPDWORD lpdwDisposition);
-  WINADVAPI LONG WINAPI RegDeleteKeyA(HKEY hKey,LPCSTR lpSubKey);
-  WINADVAPI LONG WINAPI RegDeleteKeyW(HKEY hKey,LPCWSTR lpSubKey);
-  WINADVAPI LONG WINAPI RegDeleteKeyExA(HKEY hKey,LPCSTR lpSubKey,REGSAM samDesired,DWORD Reserved);
-  WINADVAPI LONG WINAPI RegDeleteKeyExW(HKEY hKey,LPCWSTR lpSubKey,REGSAM samDesired,DWORD Reserved);
-  WINADVAPI LONG WINAPI RegDisableReflectionKey(HKEY hBase);
-  WINADVAPI LONG WINAPI RegEnableReflectionKey(HKEY hBase);
-  WINADVAPI LONG WINAPI RegQueryReflectionKey(HKEY hBase,WINBOOL *bIsReflectionDisabled);
-  WINADVAPI LONG WINAPI RegDeleteValueA(HKEY hKey,LPCSTR lpValueName);
-  WINADVAPI LONG WINAPI RegDeleteValueW(HKEY hKey,LPCWSTR lpValueName);
-  WINADVAPI LONG WINAPI RegEnumKeyA(HKEY hKey,DWORD dwIndex,LPSTR lpName,DWORD cchName);
-  WINADVAPI LONG WINAPI RegEnumKeyW(HKEY hKey,DWORD dwIndex,LPWSTR lpName,DWORD cchName);
-  WINADVAPI LONG WINAPI RegEnumKeyExA(HKEY hKey,DWORD dwIndex,LPSTR lpName,LPDWORD lpcchName,LPDWORD lpReserved,LPSTR lpClass,LPDWORD lpcchClass,PFILETIME lpftLastWriteTime);
-  WINADVAPI LONG WINAPI RegEnumKeyExW(HKEY hKey,DWORD dwIndex,LPWSTR lpName,LPDWORD lpcchName,LPDWORD lpReserved,LPWSTR lpClass,LPDWORD lpcchClass,PFILETIME lpftLastWriteTime);
-  WINADVAPI LONG WINAPI RegEnumValueA(HKEY hKey,DWORD dwIndex,LPSTR lpValueName,LPDWORD lpcchValueName,LPDWORD lpReserved,LPDWORD lpType,LPBYTE lpData,LPDWORD lpcbData);
-  WINADVAPI LONG WINAPI RegEnumValueW(HKEY hKey,DWORD dwIndex,LPWSTR lpValueName,LPDWORD lpcchValueName,LPDWORD lpReserved,LPDWORD lpType,LPBYTE lpData,LPDWORD lpcbData);
-  WINADVAPI LONG WINAPI RegFlushKey(HKEY hKey);
-  WINADVAPI LONG WINAPI RegGetKeySecurity(HKEY hKey,SECURITY_INFORMATION SecurityInformation,PSECURITY_DESCRIPTOR pSecurityDescriptor,LPDWORD lpcbSecurityDescriptor);
-  WINADVAPI LONG WINAPI RegLoadKeyA(HKEY hKey,LPCSTR lpSubKey,LPCSTR lpFile);
-  WINADVAPI LONG WINAPI RegLoadKeyW(HKEY hKey,LPCWSTR lpSubKey,LPCWSTR lpFile);
-  WINADVAPI LONG WINAPI RegNotifyChangeKeyValue(HKEY hKey,WINBOOL bWatchSubtree,DWORD dwNotifyFilter,HANDLE hEvent,WINBOOL fAsynchronous);
-  WINADVAPI LONG WINAPI RegOpenKeyA(HKEY hKey,LPCSTR lpSubKey,PHKEY phkResult);
-  WINADVAPI LONG WINAPI RegOpenKeyW(HKEY hKey,LPCWSTR lpSubKey,PHKEY phkResult);
-  WINADVAPI LONG WINAPI RegOpenKeyExA(HKEY hKey,LPCSTR lpSubKey,DWORD ulOptions,REGSAM samDesired,PHKEY phkResult);
-  WINADVAPI LONG WINAPI RegOpenKeyExW(HKEY hKey,LPCWSTR lpSubKey,DWORD ulOptions,REGSAM samDesired,PHKEY phkResult);
-  WINADVAPI LONG WINAPI RegQueryInfoKeyA(HKEY hKey,LPSTR lpClass,LPDWORD lpcchClass,LPDWORD lpReserved,LPDWORD lpcSubKeys,LPDWORD lpcbMaxSubKeyLen,LPDWORD lpcbMaxClassLen,LPDWORD lpcValues,LPDWORD lpcbMaxValueNameLen,LPDWORD lpcbMaxValueLen,LPDWORD lpcbSecurityDescriptor,PFILETIME lpftLastWriteTime);
-  WINADVAPI LONG WINAPI RegQueryInfoKeyW(HKEY hKey,LPWSTR lpClass,LPDWORD lpcchClass,LPDWORD lpReserved,LPDWORD lpcSubKeys,LPDWORD lpcbMaxSubKeyLen,LPDWORD lpcbMaxClassLen,LPDWORD lpcValues,LPDWORD lpcbMaxValueNameLen,LPDWORD lpcbMaxValueLen,LPDWORD lpcbSecurityDescriptor,PFILETIME lpftLastWriteTime);
-  WINADVAPI LONG WINAPI RegQueryValueA(HKEY hKey,LPCSTR lpSubKey,LPSTR lpData,PLONG lpcbData);
-  WINADVAPI LONG WINAPI RegQueryValueW(HKEY hKey,LPCWSTR lpSubKey,LPWSTR lpData,PLONG lpcbData);
-  WINADVAPI LONG WINAPI RegQueryMultipleValuesA(HKEY hKey,PVALENTA val_list,DWORD num_vals,LPSTR lpValueBuf,LPDWORD ldwTotsize);
-  WINADVAPI LONG WINAPI RegQueryMultipleValuesW(HKEY hKey,PVALENTW val_list,DWORD num_vals,LPWSTR lpValueBuf,LPDWORD ldwTotsize);
-  WINADVAPI LONG WINAPI RegQueryValueExA(HKEY hKey,LPCSTR lpValueName,LPDWORD lpReserved,LPDWORD lpType,LPBYTE lpData,LPDWORD lpcbData);
-  WINADVAPI LONG WINAPI RegQueryValueExW(HKEY hKey,LPCWSTR lpValueName,LPDWORD lpReserved,LPDWORD lpType,LPBYTE lpData,LPDWORD lpcbData);
-  WINADVAPI LONG WINAPI RegReplaceKeyA(HKEY hKey,LPCSTR lpSubKey,LPCSTR lpNewFile,LPCSTR lpOldFile);
-  WINADVAPI LONG WINAPI RegReplaceKeyW(HKEY hKey,LPCWSTR lpSubKey,LPCWSTR lpNewFile,LPCWSTR lpOldFile);
-  WINADVAPI LONG WINAPI RegRestoreKeyA(HKEY hKey,LPCSTR lpFile,DWORD dwFlags);
-  WINADVAPI LONG WINAPI RegRestoreKeyW(HKEY hKey,LPCWSTR lpFile,DWORD dwFlags);
-  WINADVAPI LONG WINAPI RegSaveKeyA(HKEY hKey,LPCSTR lpFile,LPSECURITY_ATTRIBUTES lpSecurityAttributes);
-  WINADVAPI LONG WINAPI RegSaveKeyW(HKEY hKey,LPCWSTR lpFile,LPSECURITY_ATTRIBUTES lpSecurityAttributes);
-  WINADVAPI LONG WINAPI RegSetKeySecurity(HKEY hKey,SECURITY_INFORMATION SecurityInformation,PSECURITY_DESCRIPTOR pSecurityDescriptor);
-  WINADVAPI LONG WINAPI RegSetValueA(HKEY hKey,LPCSTR lpSubKey,DWORD dwType,LPCSTR lpData,DWORD cbData);
-  WINADVAPI LONG WINAPI RegSetValueW(HKEY hKey,LPCWSTR lpSubKey,DWORD dwType,LPCWSTR lpData,DWORD cbData);
-  WINADVAPI LONG WINAPI RegSetValueExA(HKEY hKey,LPCSTR lpValueName,DWORD Reserved,DWORD dwType,CONST BYTE *lpData,DWORD cbData);
-  WINADVAPI LONG WINAPI RegSetValueExW(HKEY hKey,LPCWSTR lpValueName,DWORD Reserved,DWORD dwType,CONST BYTE *lpData,DWORD cbData);
-  WINADVAPI LONG WINAPI RegUnLoadKeyA(HKEY hKey,LPCSTR lpSubKey);
-  WINADVAPI LONG WINAPI RegUnLoadKeyW(HKEY hKey,LPCWSTR lpSubKey);
-  WINADVAPI LONG WINAPI RegGetValueA(HKEY hkey,LPCSTR lpSubKey,LPCSTR lpValue,DWORD dwFlags,LPDWORD pdwType,PVOID pvData,LPDWORD pcbData);
-  WINADVAPI LONG WINAPI RegGetValueW(HKEY hkey,LPCWSTR lpSubKey,LPCWSTR lpValue,DWORD dwFlags,LPDWORD pdwType,PVOID pvData,LPDWORD pcbData);
-  WINADVAPI WINBOOL WINAPI InitiateSystemShutdownA(LPSTR lpMachineName,LPSTR lpMessage,DWORD dwTimeout,WINBOOL bForceAppsClosed,WINBOOL bRebootAfterShutdown);
-  WINADVAPI WINBOOL WINAPI InitiateSystemShutdownW(LPWSTR lpMachineName,LPWSTR lpMessage,DWORD dwTimeout,WINBOOL bForceAppsClosed,WINBOOL bRebootAfterShutdown);
-  WINADVAPI WINBOOL WINAPI AbortSystemShutdownA(LPSTR lpMachineName);
-  WINADVAPI WINBOOL WINAPI AbortSystemShutdownW(LPWSTR lpMachineName);
-
-//gr #include <reason.h>
-
-#define REASON_SWINSTALL SHTDN_REASON_MAJOR_SOFTWARE|SHTDN_REASON_MINOR_INSTALLATION
-#define REASON_HWINSTALL SHTDN_REASON_MAJOR_HARDWARE|SHTDN_REASON_MINOR_INSTALLATION
-#define REASON_SERVICEHANG SHTDN_REASON_MAJOR_SOFTWARE|SHTDN_REASON_MINOR_HUNG
-#define REASON_UNSTABLE SHTDN_REASON_MAJOR_SYSTEM|SHTDN_REASON_MINOR_UNSTABLE
-#define REASON_SWHWRECONF SHTDN_REASON_MAJOR_SOFTWARE|SHTDN_REASON_MINOR_RECONFIG
-#define REASON_OTHER SHTDN_REASON_MAJOR_OTHER|SHTDN_REASON_MINOR_OTHER
-#define REASON_UNKNOWN SHTDN_REASON_UNKNOWN
-#define REASON_LEGACY_API SHTDN_REASON_LEGACY_API
-#define REASON_PLANNED_FLAG SHTDN_REASON_FLAG_PLANNED
-
-#define MAX_SHUTDOWN_TIMEOUT (10*365*24*60*60)
-
-#ifdef UNICODE
-#define InitiateSystemShutdownEx InitiateSystemShutdownExW
-#define RegSaveKeyEx RegSaveKeyExW
-#else
-#define InitiateSystemShutdownEx InitiateSystemShutdownExA
-#define RegSaveKeyEx RegSaveKeyExA
-#endif
-
-  WINADVAPI WINBOOL WINAPI InitiateSystemShutdownExA(LPSTR lpMachineName,LPSTR lpMessage,DWORD dwTimeout,WINBOOL bForceAppsClosed,WINBOOL bRebootAfterShutdown,DWORD dwReason);
-  WINADVAPI WINBOOL WINAPI InitiateSystemShutdownExW(LPWSTR lpMachineName,LPWSTR lpMessage,DWORD dwTimeout,WINBOOL bForceAppsClosed,WINBOOL bRebootAfterShutdown,DWORD dwReason);
-  WINADVAPI LONG WINAPI RegSaveKeyExA(HKEY hKey,LPCSTR lpFile,LPSECURITY_ATTRIBUTES lpSecurityAttributes,DWORD Flags);
-  WINADVAPI LONG WINAPI RegSaveKeyExW(HKEY hKey,LPCWSTR lpFile,LPSECURITY_ATTRIBUTES lpSecurityAttributes,DWORD Flags);
-  WINADVAPI LONG WINAPI Wow64Win32ApiEntry (DWORD dwFuncNumber,DWORD dwFlag,DWORD dwRes);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/tinyc/win32/include/winapi/winuser.h b/tinyc/win32/include/winapi/winuser.h
deleted file mode 100644
index 4cd9ffb56..000000000
--- a/tinyc/win32/include/winapi/winuser.h
+++ /dev/null
@@ -1,5651 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef _WINUSER_
-#define _WINUSER_
-
-#define WINUSERAPI DECLSPEC_IMPORT
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef WINVER
-#define WINVER 0x0502
-#endif
-
-#include <stdarg.h>
-
-#ifndef NOUSER
-  typedef HANDLE HDWP;
-  typedef VOID MENUTEMPLATEA;
-  typedef VOID MENUTEMPLATEW;
-  typedef PVOID LPMENUTEMPLATEA;
-  typedef PVOID LPMENUTEMPLATEW;
-
-#ifdef UNICODE
-  typedef MENUTEMPLATEW MENUTEMPLATE;
-  typedef LPMENUTEMPLATEW LPMENUTEMPLATE;
-#else
-  typedef MENUTEMPLATEA MENUTEMPLATE;
-  typedef LPMENUTEMPLATEA LPMENUTEMPLATE;
-#endif
-
-  typedef LRESULT (CALLBACK *WNDPROC)(HWND,UINT,WPARAM,LPARAM);
-  typedef INT_PTR (CALLBACK *DLGPROC)(HWND,UINT,WPARAM,LPARAM);
-  typedef VOID (CALLBACK *TIMERPROC)(HWND,UINT,UINT_PTR,DWORD);
-  typedef WINBOOL (CALLBACK *GRAYSTRINGPROC)(HDC,LPARAM,int);
-  typedef WINBOOL (CALLBACK *WNDENUMPROC)(HWND,LPARAM);
-  typedef LRESULT (CALLBACK *HOOKPROC)(int code,WPARAM wParam,LPARAM lParam);
-  typedef VOID (CALLBACK *SENDASYNCPROC)(HWND,UINT,ULONG_PTR,LRESULT);
-  typedef WINBOOL (CALLBACK *PROPENUMPROCA)(HWND,LPCSTR,HANDLE);
-  typedef WINBOOL (CALLBACK *PROPENUMPROCW)(HWND,LPCWSTR,HANDLE);
-  typedef WINBOOL (CALLBACK *PROPENUMPROCEXA)(HWND,LPSTR,HANDLE,ULONG_PTR);
-  typedef WINBOOL (CALLBACK *PROPENUMPROCEXW)(HWND,LPWSTR,HANDLE,ULONG_PTR);
-  typedef int (CALLBACK *EDITWORDBREAKPROCA)(LPSTR lpch,int ichCurrent,int cch,int code);
-  typedef int (CALLBACK *EDITWORDBREAKPROCW)(LPWSTR lpch,int ichCurrent,int cch,int code);
-  typedef WINBOOL (CALLBACK *DRAWSTATEPROC)(HDC hdc,LPARAM lData,WPARAM wData,int cx,int cy);
-
-#ifdef UNICODE
-  typedef PROPENUMPROCW PROPENUMPROC;
-  typedef PROPENUMPROCEXW PROPENUMPROCEX;
-  typedef EDITWORDBREAKPROCW EDITWORDBREAKPROC;
-#else
-  typedef PROPENUMPROCA PROPENUMPROC;
-  typedef PROPENUMPROCEXA PROPENUMPROCEX;
-  typedef EDITWORDBREAKPROCA EDITWORDBREAKPROC;
-#endif
-
-  typedef WINBOOL (CALLBACK *NAMEENUMPROCA)(LPSTR,LPARAM);
-  typedef WINBOOL (CALLBACK *NAMEENUMPROCW)(LPWSTR,LPARAM);
-  typedef NAMEENUMPROCA WINSTAENUMPROCA;
-  typedef NAMEENUMPROCA DESKTOPENUMPROCA;
-  typedef NAMEENUMPROCW WINSTAENUMPROCW;
-  typedef NAMEENUMPROCW DESKTOPENUMPROCW;
-
-#ifdef UNICODE
-  typedef WINSTAENUMPROCW WINSTAENUMPROC;
-  typedef DESKTOPENUMPROCW DESKTOPENUMPROC;
-#else
-  typedef WINSTAENUMPROCA WINSTAENUMPROC;
-  typedef DESKTOPENUMPROCA DESKTOPENUMPROC;
-#endif
-
-#define IS_INTRESOURCE(_r) ((((ULONG_PTR)(_r)) >> 16)==0)
-#define MAKEINTRESOURCEA(i) ((LPSTR)((ULONG_PTR)((WORD)(i))))
-#define MAKEINTRESOURCEW(i) ((LPWSTR)((ULONG_PTR)((WORD)(i))))
-#ifdef UNICODE
-#define MAKEINTRESOURCE MAKEINTRESOURCEW
-#else
-#define MAKEINTRESOURCE MAKEINTRESOURCEA
-#endif
-
-#ifndef NORESOURCE
-
-#define RT_CURSOR MAKEINTRESOURCE(1)
-#define RT_BITMAP MAKEINTRESOURCE(2)
-#define RT_ICON MAKEINTRESOURCE(3)
-#define RT_MENU MAKEINTRESOURCE(4)
-#define RT_DIALOG MAKEINTRESOURCE(5)
-#define RT_STRING MAKEINTRESOURCE(6)
-#define RT_FONTDIR MAKEINTRESOURCE(7)
-#define RT_FONT MAKEINTRESOURCE(8)
-#define RT_ACCELERATOR MAKEINTRESOURCE(9)
-#define RT_RCDATA MAKEINTRESOURCE(10)
-#define RT_MESSAGETABLE MAKEINTRESOURCE(11)
-
-#define DIFFERENCE 11
-#define RT_GROUP_CURSOR MAKEINTRESOURCE((ULONG_PTR)RT_CURSOR + DIFFERENCE)
-#define RT_GROUP_ICON MAKEINTRESOURCE((ULONG_PTR)RT_ICON + DIFFERENCE)
-#define RT_VERSION MAKEINTRESOURCE(16)
-#define RT_DLGINCLUDE MAKEINTRESOURCE(17)
-#define RT_PLUGPLAY MAKEINTRESOURCE(19)
-#define RT_VXD MAKEINTRESOURCE(20)
-#define RT_ANICURSOR MAKEINTRESOURCE(21)
-#define RT_ANIICON MAKEINTRESOURCE(22)
-#define RT_HTML MAKEINTRESOURCE(23)
-#ifdef RC_INVOKED
-#define RT_MANIFEST 24
-#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
-#define ISOLATIONAWARE_MANIFEST_RESOURCE_ID 2
-#define ISOLATIONAWARE_NOSTATICIMPORT_MANIFEST_RESOURCE_ID 3
-#define MINIMUM_RESERVED_MANIFEST_RESOURCE_ID 1
-#define MAXIMUM_RESERVED_MANIFEST_RESOURCE_ID 16
-#else
-#define RT_MANIFEST MAKEINTRESOURCE(24)
-#define CREATEPROCESS_MANIFEST_RESOURCE_ID MAKEINTRESOURCE(1)
-#define ISOLATIONAWARE_MANIFEST_RESOURCE_ID MAKEINTRESOURCE(2)
-#define ISOLATIONAWARE_NOSTATICIMPORT_MANIFEST_RESOURCE_ID MAKEINTRESOURCE(3)
-#define MINIMUM_RESERVED_MANIFEST_RESOURCE_ID MAKEINTRESOURCE(1)
-#define MAXIMUM_RESERVED_MANIFEST_RESOURCE_ID MAKEINTRESOURCE(16)
-#endif
-#endif
-
-#ifdef UNICODE
-#define wvsprintf wvsprintfW
-#define wsprintf wsprintfW
-#else
-#define wvsprintf wvsprintfA
-#define wsprintf wsprintfA
-#endif
-
-  WINUSERAPI int WINAPI wvsprintfA(LPSTR,LPCSTR,va_list arglist);
-  WINUSERAPI int WINAPI wvsprintfW(LPWSTR,LPCWSTR,va_list arglist);
-  WINUSERAPI int WINAPIV wsprintfA(LPSTR,LPCSTR,...);
-  WINUSERAPI int WINAPIV wsprintfW(LPWSTR,LPCWSTR,...);
-
-#define SETWALLPAPER_DEFAULT ((LPWSTR)-1)
-
-#ifndef NOSCROLL
-#define SB_HORZ 0
-#define SB_VERT 1
-#define SB_CTL 2
-#define SB_BOTH 3
-
-#define SB_LINEUP 0
-#define SB_LINELEFT 0
-#define SB_LINEDOWN 1
-#define SB_LINERIGHT 1
-#define SB_PAGEUP 2
-#define SB_PAGELEFT 2
-#define SB_PAGEDOWN 3
-#define SB_PAGERIGHT 3
-#define SB_THUMBPOSITION 4
-#define SB_THUMBTRACK 5
-#define SB_TOP 6
-#define SB_LEFT 6
-#define SB_BOTTOM 7
-#define SB_RIGHT 7
-#define SB_ENDSCROLL 8
-#endif
-
-#ifndef NOSHOWWINDOW
-#define SW_HIDE 0
-#define SW_SHOWNORMAL 1
-#define SW_NORMAL 1
-#define SW_SHOWMINIMIZED 2
-#define SW_SHOWMAXIMIZED 3
-#define SW_MAXIMIZE 3
-#define SW_SHOWNOACTIVATE 4
-#define SW_SHOW 5
-#define SW_MINIMIZE 6
-#define SW_SHOWMINNOACTIVE 7
-#define SW_SHOWNA 8
-#define SW_RESTORE 9
-#define SW_SHOWDEFAULT 10
-#define SW_FORCEMINIMIZE 11
-#define SW_MAX 11
-
-#define HIDE_WINDOW 0
-#define SHOW_OPENWINDOW 1
-#define SHOW_ICONWINDOW 2
-#define SHOW_FULLSCREEN 3
-#define SHOW_OPENNOACTIVATE 4
-
-#define SW_PARENTCLOSING 1
-#define SW_OTHERZOOM 2
-#define SW_PARENTOPENING 3
-#define SW_OTHERUNZOOM 4
-#endif
-
-#define AW_HOR_POSITIVE 0x00000001
-#define AW_HOR_NEGATIVE 0x00000002
-#define AW_VER_POSITIVE 0x00000004
-#define AW_VER_NEGATIVE 0x00000008
-#define AW_CENTER 0x00000010
-#define AW_HIDE 0x00010000
-#define AW_ACTIVATE 0x00020000
-#define AW_SLIDE 0x00040000
-#define AW_BLEND 0x00080000
-
-#define KF_EXTENDED 0x0100
-#define KF_DLGMODE 0x0800
-#define KF_MENUMODE 0x1000
-#define KF_ALTDOWN 0x2000
-#define KF_REPEAT 0x4000
-#define KF_UP 0x8000
-
-#ifndef NOVIRTUALKEYCODES
-
-#define VK_LBUTTON 0x01
-#define VK_RBUTTON 0x02
-#define VK_CANCEL 0x03
-#define VK_MBUTTON 0x04
-#define VK_XBUTTON1 0x05
-#define VK_XBUTTON2 0x06
-#define VK_BACK 0x08
-#define VK_TAB 0x09
-#define VK_CLEAR 0x0C
-#define VK_RETURN 0x0D
-#define VK_SHIFT 0x10
-#define VK_CONTROL 0x11
-#define VK_MENU 0x12
-#define VK_PAUSE 0x13
-#define VK_CAPITAL 0x14
-#define VK_KANA 0x15
-#define VK_HANGEUL 0x15
-#define VK_HANGUL 0x15
-#define VK_JUNJA 0x17
-#define VK_FINAL 0x18
-#define VK_HANJA 0x19
-#define VK_KANJI 0x19
-#define VK_ESCAPE 0x1B
-#define VK_CONVERT 0x1C
-#define VK_NONCONVERT 0x1D
-#define VK_ACCEPT 0x1E
-#define VK_MODECHANGE 0x1F
-#define VK_SPACE 0x20
-#define VK_PRIOR 0x21
-#define VK_NEXT 0x22
-#define VK_END 0x23
-#define VK_HOME 0x24
-#define VK_LEFT 0x25
-#define VK_UP 0x26
-#define VK_RIGHT 0x27
-#define VK_DOWN 0x28
-#define VK_SELECT 0x29
-#define VK_PRINT 0x2A
-#define VK_EXECUTE 0x2B
-#define VK_SNAPSHOT 0x2C
-#define VK_INSERT 0x2D
-#define VK_DELETE 0x2E
-#define VK_HELP 0x2F
-
-#define VK_LWIN 0x5B
-#define VK_RWIN 0x5C
-#define VK_APPS 0x5D
-#define VK_SLEEP 0x5F
-#define VK_NUMPAD0 0x60
-#define VK_NUMPAD1 0x61
-#define VK_NUMPAD2 0x62
-#define VK_NUMPAD3 0x63
-#define VK_NUMPAD4 0x64
-#define VK_NUMPAD5 0x65
-#define VK_NUMPAD6 0x66
-#define VK_NUMPAD7 0x67
-#define VK_NUMPAD8 0x68
-#define VK_NUMPAD9 0x69
-#define VK_MULTIPLY 0x6A
-#define VK_ADD 0x6B
-#define VK_SEPARATOR 0x6C
-#define VK_SUBTRACT 0x6D
-#define VK_DECIMAL 0x6E
-#define VK_DIVIDE 0x6F
-#define VK_F1 0x70
-#define VK_F2 0x71
-#define VK_F3 0x72
-#define VK_F4 0x73
-#define VK_F5 0x74
-#define VK_F6 0x75
-#define VK_F7 0x76
-#define VK_F8 0x77
-#define VK_F9 0x78
-#define VK_F10 0x79
-#define VK_F11 0x7A
-#define VK_F12 0x7B
-#define VK_F13 0x7C
-#define VK_F14 0x7D
-#define VK_F15 0x7E
-#define VK_F16 0x7F
-#define VK_F17 0x80
-#define VK_F18 0x81
-#define VK_F19 0x82
-#define VK_F20 0x83
-#define VK_F21 0x84
-#define VK_F22 0x85
-#define VK_F23 0x86
-#define VK_F24 0x87
-#define VK_NUMLOCK 0x90
-#define VK_SCROLL 0x91
-#define VK_OEM_NEC_EQUAL 0x92
-#define VK_OEM_FJ_JISHO 0x92
-#define VK_OEM_FJ_MASSHOU 0x93
-#define VK_OEM_FJ_TOUROKU 0x94
-#define VK_OEM_FJ_LOYA 0x95
-#define VK_OEM_FJ_ROYA 0x96
-#define VK_LSHIFT 0xA0
-#define VK_RSHIFT 0xA1
-#define VK_LCONTROL 0xA2
-#define VK_RCONTROL 0xA3
-#define VK_LMENU 0xA4
-#define VK_RMENU 0xA5
-#define VK_BROWSER_BACK 0xA6
-#define VK_BROWSER_FORWARD 0xA7
-#define VK_BROWSER_REFRESH 0xA8
-#define VK_BROWSER_STOP 0xA9
-#define VK_BROWSER_SEARCH 0xAA
-#define VK_BROWSER_FAVORITES 0xAB
-#define VK_BROWSER_HOME 0xAC
-#define VK_VOLUME_MUTE 0xAD
-#define VK_VOLUME_DOWN 0xAE
-#define VK_VOLUME_UP 0xAF
-#define VK_MEDIA_NEXT_TRACK 0xB0
-#define VK_MEDIA_PREV_TRACK 0xB1
-#define VK_MEDIA_STOP 0xB2
-#define VK_MEDIA_PLAY_PAUSE 0xB3
-#define VK_LAUNCH_MAIL 0xB4
-#define VK_LAUNCH_MEDIA_SELECT 0xB5
-#define VK_LAUNCH_APP1 0xB6
-#define VK_LAUNCH_APP2 0xB7
-#define VK_OEM_1 0xBA
-#define VK_OEM_PLUS 0xBB
-#define VK_OEM_COMMA 0xBC
-#define VK_OEM_MINUS 0xBD
-#define VK_OEM_PERIOD 0xBE
-#define VK_OEM_2 0xBF
-#define VK_OEM_3 0xC0
-#define VK_OEM_4 0xDB
-#define VK_OEM_5 0xDC
-#define VK_OEM_6 0xDD
-#define VK_OEM_7 0xDE
-#define VK_OEM_8 0xDF
-#define VK_OEM_AX 0xE1
-#define VK_OEM_102 0xE2
-#define VK_ICO_HELP 0xE3
-#define VK_ICO_00 0xE4
-#define VK_PROCESSKEY 0xE5
-#define VK_ICO_CLEAR 0xE6
-#define VK_PACKET 0xE7
-#define VK_OEM_RESET 0xE9
-#define VK_OEM_JUMP 0xEA
-#define VK_OEM_PA1 0xEB
-#define VK_OEM_PA2 0xEC
-#define VK_OEM_PA3 0xED
-#define VK_OEM_WSCTRL 0xEE
-#define VK_OEM_CUSEL 0xEF
-#define VK_OEM_ATTN 0xF0
-#define VK_OEM_FINISH 0xF1
-#define VK_OEM_COPY 0xF2
-#define VK_OEM_AUTO 0xF3
-#define VK_OEM_ENLW 0xF4
-#define VK_OEM_BACKTAB 0xF5
-#define VK_ATTN 0xF6
-#define VK_CRSEL 0xF7
-#define VK_EXSEL 0xF8
-#define VK_EREOF 0xF9
-#define VK_PLAY 0xFA
-#define VK_ZOOM 0xFB
-#define VK_NONAME 0xFC
-#define VK_PA1 0xFD
-#define VK_OEM_CLEAR 0xFE
-#endif
-
-#ifndef NOWH
-
-#define WH_MIN (-1)
-#define WH_MSGFILTER (-1)
-#define WH_JOURNALRECORD 0
-#define WH_JOURNALPLAYBACK 1
-#define WH_KEYBOARD 2
-#define WH_GETMESSAGE 3
-#define WH_CALLWNDPROC 4
-#define WH_CBT 5
-#define WH_SYSMSGFILTER 6
-#define WH_MOUSE 7
-#define WH_HARDWARE 8
-#define WH_DEBUG 9
-#define WH_SHELL 10
-#define WH_FOREGROUNDIDLE 11
-#define WH_CALLWNDPROCRET 12
-
-#define WH_KEYBOARD_LL 13
-#define WH_MOUSE_LL 14
-
-#define WH_MAX 14
-
-#define WH_MINHOOK WH_MIN
-#define WH_MAXHOOK WH_MAX
-
-#define HC_ACTION 0
-#define HC_GETNEXT 1
-#define HC_SKIP 2
-#define HC_NOREMOVE 3
-#define HC_NOREM HC_NOREMOVE
-#define HC_SYSMODALON 4
-#define HC_SYSMODALOFF 5
-
-#define HCBT_MOVESIZE 0
-#define HCBT_MINMAX 1
-#define HCBT_QS 2
-#define HCBT_CREATEWND 3
-#define HCBT_DESTROYWND 4
-#define HCBT_ACTIVATE 5
-#define HCBT_CLICKSKIPPED 6
-#define HCBT_KEYSKIPPED 7
-#define HCBT_SYSCOMMAND 8
-#define HCBT_SETFOCUS 9
-
-  typedef struct tagCBT_CREATEWNDA {
-    struct tagCREATESTRUCTA *lpcs;
-    HWND hwndInsertAfter;
-  } CBT_CREATEWNDA,*LPCBT_CREATEWNDA;
-
-  typedef struct tagCBT_CREATEWNDW {
-    struct tagCREATESTRUCTW *lpcs;
-    HWND hwndInsertAfter;
-  } CBT_CREATEWNDW,*LPCBT_CREATEWNDW;
-#ifdef UNICODE
-  typedef CBT_CREATEWNDW CBT_CREATEWND;
-  typedef LPCBT_CREATEWNDW LPCBT_CREATEWND;
-#else
-  typedef CBT_CREATEWNDA CBT_CREATEWND;
-  typedef LPCBT_CREATEWNDA LPCBT_CREATEWND;
-#endif
-
-  typedef struct tagCBTACTIVATESTRUCT
-  {
-    WINBOOL fMouse;
-    HWND hWndActive;
-  } CBTACTIVATESTRUCT,*LPCBTACTIVATESTRUCT;
-
-  typedef struct tagWTSSESSION_NOTIFICATION {
-    DWORD cbSize;
-    DWORD dwSessionId;
-
-  } WTSSESSION_NOTIFICATION,*PWTSSESSION_NOTIFICATION;
-
-#define WTS_CONSOLE_CONNECT 0x1
-#define WTS_CONSOLE_DISCONNECT 0x2
-#define WTS_REMOTE_CONNECT 0x3
-#define WTS_REMOTE_DISCONNECT 0x4
-#define WTS_SESSION_LOGON 0x5
-#define WTS_SESSION_LOGOFF 0x6
-#define WTS_SESSION_LOCK 0x7
-#define WTS_SESSION_UNLOCK 0x8
-#define WTS_SESSION_REMOTE_CONTROL 0x9
-
-#define MSGF_DIALOGBOX 0
-#define MSGF_MESSAGEBOX 1
-#define MSGF_MENU 2
-#define MSGF_SCROLLBAR 5
-#define MSGF_NEXTWINDOW 6
-#define MSGF_MAX 8
-#define MSGF_USER 4096
-
-#define HSHELL_WINDOWCREATED 1
-#define HSHELL_WINDOWDESTROYED 2
-#define HSHELL_ACTIVATESHELLWINDOW 3
-
-#define HSHELL_WINDOWACTIVATED 4
-#define HSHELL_GETMINRECT 5
-#define HSHELL_REDRAW 6
-#define HSHELL_TASKMAN 7
-#define HSHELL_LANGUAGE 8
-#define HSHELL_SYSMENU 9
-#define HSHELL_ENDTASK 10
-#define HSHELL_ACCESSIBILITYSTATE 11
-#define HSHELL_APPCOMMAND 12
-#define HSHELL_WINDOWREPLACED 13
-#define HSHELL_WINDOWREPLACING 14
-#define HSHELL_HIGHBIT 0x8000
-#define HSHELL_FLASH (HSHELL_REDRAW|HSHELL_HIGHBIT)
-#define HSHELL_RUDEAPPACTIVATED (HSHELL_WINDOWACTIVATED|HSHELL_HIGHBIT)
-
-#define ACCESS_STICKYKEYS 0x0001
-#define ACCESS_FILTERKEYS 0x0002
-#define ACCESS_MOUSEKEYS 0x0003
-
-#define APPCOMMAND_BROWSER_BACKWARD 1
-#define APPCOMMAND_BROWSER_FORWARD 2
-#define APPCOMMAND_BROWSER_REFRESH 3
-#define APPCOMMAND_BROWSER_STOP 4
-#define APPCOMMAND_BROWSER_SEARCH 5
-#define APPCOMMAND_BROWSER_FAVORITES 6
-#define APPCOMMAND_BROWSER_HOME 7
-#define APPCOMMAND_VOLUME_MUTE 8
-#define APPCOMMAND_VOLUME_DOWN 9
-#define APPCOMMAND_VOLUME_UP 10
-#define APPCOMMAND_MEDIA_NEXTTRACK 11
-#define APPCOMMAND_MEDIA_PREVIOUSTRACK 12
-#define APPCOMMAND_MEDIA_STOP 13
-#define APPCOMMAND_MEDIA_PLAY_PAUSE 14
-#define APPCOMMAND_LAUNCH_MAIL 15
-#define APPCOMMAND_LAUNCH_MEDIA_SELECT 16
-#define APPCOMMAND_LAUNCH_APP1 17
-#define APPCOMMAND_LAUNCH_APP2 18
-#define APPCOMMAND_BASS_DOWN 19
-#define APPCOMMAND_BASS_BOOST 20
-#define APPCOMMAND_BASS_UP 21
-#define APPCOMMAND_TREBLE_DOWN 22
-#define APPCOMMAND_TREBLE_UP 23
-#define APPCOMMAND_MICROPHONE_VOLUME_MUTE 24
-#define APPCOMMAND_MICROPHONE_VOLUME_DOWN 25
-#define APPCOMMAND_MICROPHONE_VOLUME_UP 26
-#define APPCOMMAND_HELP 27
-#define APPCOMMAND_FIND 28
-#define APPCOMMAND_NEW 29
-#define APPCOMMAND_OPEN 30
-#define APPCOMMAND_CLOSE 31
-#define APPCOMMAND_SAVE 32
-#define APPCOMMAND_PRINT 33
-#define APPCOMMAND_UNDO 34
-#define APPCOMMAND_REDO 35
-#define APPCOMMAND_COPY 36
-#define APPCOMMAND_CUT 37
-#define APPCOMMAND_PASTE 38
-#define APPCOMMAND_REPLY_TO_MAIL 39
-#define APPCOMMAND_FORWARD_MAIL 40
-#define APPCOMMAND_SEND_MAIL 41
-#define APPCOMMAND_SPELL_CHECK 42
-#define APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE 43
-#define APPCOMMAND_MIC_ON_OFF_TOGGLE 44
-#define APPCOMMAND_CORRECTION_LIST 45
-#define APPCOMMAND_MEDIA_PLAY 46
-#define APPCOMMAND_MEDIA_PAUSE 47
-#define APPCOMMAND_MEDIA_RECORD 48
-#define APPCOMMAND_MEDIA_FAST_FORWARD 49
-#define APPCOMMAND_MEDIA_REWIND 50
-#define APPCOMMAND_MEDIA_CHANNEL_UP 51
-#define APPCOMMAND_MEDIA_CHANNEL_DOWN 52
-
-#define FAPPCOMMAND_MOUSE 0x8000
-#define FAPPCOMMAND_KEY 0
-#define FAPPCOMMAND_OEM 0x1000
-#define FAPPCOMMAND_MASK 0xF000
-
-#define GET_APPCOMMAND_LPARAM(lParam) ((short)(HIWORD(lParam) & ~FAPPCOMMAND_MASK))
-#define GET_DEVICE_LPARAM(lParam) ((WORD)(HIWORD(lParam) & FAPPCOMMAND_MASK))
-#define GET_MOUSEORKEY_LPARAM GET_DEVICE_LPARAM
-#define GET_FLAGS_LPARAM(lParam) (LOWORD(lParam))
-#define GET_KEYSTATE_LPARAM(lParam) GET_FLAGS_LPARAM(lParam)
-
-  typedef struct {
-    HWND hwnd;
-    RECT rc;
-  } SHELLHOOKINFO,*LPSHELLHOOKINFO;
-
-  typedef struct tagEVENTMSG {
-    UINT message;
-    UINT paramL;
-    UINT paramH;
-    DWORD time;
-    HWND hwnd;
-  } EVENTMSG,*PEVENTMSGMSG,*NPEVENTMSGMSG,*LPEVENTMSGMSG;
-
-  typedef struct tagEVENTMSG *PEVENTMSG,*NPEVENTMSG,*LPEVENTMSG;
-
-  typedef struct tagCWPSTRUCT {
-    LPARAM lParam;
-    WPARAM wParam;
-    UINT message;
-    HWND hwnd;
-  } CWPSTRUCT,*PCWPSTRUCT,*NPCWPSTRUCT,*LPCWPSTRUCT;
-
-  typedef struct tagCWPRETSTRUCT {
-    LRESULT lResult;
-    LPARAM lParam;
-    WPARAM wParam;
-    UINT message;
-    HWND hwnd;
-  } CWPRETSTRUCT,*PCWPRETSTRUCT,*NPCWPRETSTRUCT,*LPCWPRETSTRUCT;
-
-#define LLKHF_EXTENDED (KF_EXTENDED >> 8)
-#define LLKHF_INJECTED 0x00000010
-#define LLKHF_ALTDOWN (KF_ALTDOWN >> 8)
-#define LLKHF_UP (KF_UP >> 8)
-
-#define LLMHF_INJECTED 0x00000001
-
-  typedef struct tagKBDLLHOOKSTRUCT {
-    DWORD vkCode;
-    DWORD scanCode;
-    DWORD flags;
-    DWORD time;
-    ULONG_PTR dwExtraInfo;
-  } KBDLLHOOKSTRUCT,*LPKBDLLHOOKSTRUCT,*PKBDLLHOOKSTRUCT;
-
-  typedef struct tagMSLLHOOKSTRUCT {
-    POINT pt;
-    DWORD mouseData;
-    DWORD flags;
-    DWORD time;
-    ULONG_PTR dwExtraInfo;
-  } MSLLHOOKSTRUCT,*LPMSLLHOOKSTRUCT,*PMSLLHOOKSTRUCT;
-
-  typedef struct tagDEBUGHOOKINFO {
-    DWORD idThread;
-    DWORD idThreadInstaller;
-    LPARAM lParam;
-    WPARAM wParam;
-    int code;
-  } DEBUGHOOKINFO,*PDEBUGHOOKINFO,*NPDEBUGHOOKINFO,*LPDEBUGHOOKINFO;
-
-  typedef struct tagMOUSEHOOKSTRUCT {
-    POINT pt;
-    HWND hwnd;
-    UINT wHitTestCode;
-    ULONG_PTR dwExtraInfo;
-  } MOUSEHOOKSTRUCT,*LPMOUSEHOOKSTRUCT,*PMOUSEHOOKSTRUCT;
-
-#ifdef __cplusplus
-  typedef struct tagMOUSEHOOKSTRUCTEX : public tagMOUSEHOOKSTRUCT {
-    DWORD mouseData;
-  } MOUSEHOOKSTRUCTEX,*LPMOUSEHOOKSTRUCTEX,*PMOUSEHOOKSTRUCTEX;
-#else
-  typedef struct tagMOUSEHOOKSTRUCTEX {
-    MOUSEHOOKSTRUCT _unnamed;
-    DWORD mouseData;
-  } MOUSEHOOKSTRUCTEX,*LPMOUSEHOOKSTRUCTEX,*PMOUSEHOOKSTRUCTEX;
-#endif
-
-  typedef struct tagHARDWAREHOOKSTRUCT {
-    HWND hwnd;
-    UINT message;
-    WPARAM wParam;
-    LPARAM lParam;
-  } HARDWAREHOOKSTRUCT,*LPHARDWAREHOOKSTRUCT,*PHARDWAREHOOKSTRUCT;
-#endif
-
-#define HKL_PREV 0
-#define HKL_NEXT 1
-
-#define KLF_ACTIVATE 0x00000001
-#define KLF_SUBSTITUTE_OK 0x00000002
-#define KLF_REORDER 0x00000008
-#define KLF_REPLACELANG 0x00000010
-#define KLF_NOTELLSHELL 0x00000080
-#define KLF_SETFORPROCESS 0x00000100
-#define KLF_SHIFTLOCK 0x00010000
-#define KLF_RESET 0x40000000
-
-#define INPUTLANGCHANGE_SYSCHARSET 0x0001
-#define INPUTLANGCHANGE_FORWARD 0x0002
-#define INPUTLANGCHANGE_BACKWARD 0x0004
-
-#define KL_NAMELENGTH 9
-
-#ifdef UNICODE
-#define LoadKeyboardLayout LoadKeyboardLayoutW
-#define GetKeyboardLayoutName GetKeyboardLayoutNameW
-#else
-#define LoadKeyboardLayout LoadKeyboardLayoutA
-#define GetKeyboardLayoutName GetKeyboardLayoutNameA
-#endif
-
-  WINUSERAPI HKL WINAPI LoadKeyboardLayoutA(LPCSTR pwszKLID,UINT Flags);
-  WINUSERAPI HKL WINAPI LoadKeyboardLayoutW(LPCWSTR pwszKLID,UINT Flags);
-  WINUSERAPI HKL WINAPI ActivateKeyboardLayout(HKL hkl,UINT Flags);
-  WINUSERAPI int WINAPI ToUnicodeEx(UINT wVirtKey,UINT wScanCode,CONST BYTE *lpKeyState,LPWSTR pwszBuff,int cchBuff,UINT wFlags,HKL dwhkl);
-  WINUSERAPI WINBOOL WINAPI UnloadKeyboardLayout(HKL hkl);
-  WINUSERAPI WINBOOL WINAPI GetKeyboardLayoutNameA(LPSTR pwszKLID);
-  WINUSERAPI WINBOOL WINAPI GetKeyboardLayoutNameW(LPWSTR pwszKLID);
-  WINUSERAPI int WINAPI GetKeyboardLayoutList(int nBuff,HKL *lpList);
-  WINUSERAPI HKL WINAPI GetKeyboardLayout(DWORD idThread);
-
-  typedef struct tagMOUSEMOVEPOINT {
-    int x;
-    int y;
-    DWORD time;
-    ULONG_PTR dwExtraInfo;
-  } MOUSEMOVEPOINT,*PMOUSEMOVEPOINT,*LPMOUSEMOVEPOINT;
-
-#define GMMP_USE_DISPLAY_POINTS 1
-#define GMMP_USE_HIGH_RESOLUTION_POINTS 2
-
-  WINUSERAPI int WINAPI GetMouseMovePointsEx(UINT cbSize,LPMOUSEMOVEPOINT lppt,LPMOUSEMOVEPOINT lpptBuf,int nBufPoints,DWORD resolution);
-
-#ifndef NODESKTOP
-
-#define DESKTOP_READOBJECTS 0x0001L
-#define DESKTOP_CREATEWINDOW 0x0002L
-#define DESKTOP_CREATEMENU 0x0004L
-#define DESKTOP_HOOKCONTROL 0x0008L
-#define DESKTOP_JOURNALRECORD 0x0010L
-#define DESKTOP_JOURNALPLAYBACK 0x0020L
-#define DESKTOP_ENUMERATE 0x0040L
-#define DESKTOP_WRITEOBJECTS 0x0080L
-#define DESKTOP_SWITCHDESKTOP 0x0100L
-
-#define DF_ALLOWOTHERACCOUNTHOOK 0x0001L
-
-#ifdef _WINGDI_
-#ifndef NOGDI
-#ifdef UNICODE
-#define CreateDesktop CreateDesktopW
-#else
-#define CreateDesktop CreateDesktopA
-#endif
-
-  WINUSERAPI HDESK WINAPI CreateDesktopA(LPCSTR lpszDesktop,LPCSTR lpszDevice,LPDEVMODEA pDevmode,DWORD dwFlags,ACCESS_MASK dwDesiredAccess,LPSECURITY_ATTRIBUTES lpsa);
-  WINUSERAPI HDESK WINAPI CreateDesktopW(LPCWSTR lpszDesktop,LPCWSTR lpszDevice,LPDEVMODEW pDevmode,DWORD dwFlags,ACCESS_MASK dwDesiredAccess,LPSECURITY_ATTRIBUTES lpsa);
-#endif
-#endif
-
-#ifdef UNICODE
-#define OpenDesktop OpenDesktopW
-#define EnumDesktops EnumDesktopsW
-#else
-#define OpenDesktop OpenDesktopA
-#define EnumDesktops EnumDesktopsA
-#endif
-
-  WINUSERAPI HDESK WINAPI OpenDesktopA(LPCSTR lpszDesktop,DWORD dwFlags,WINBOOL fInherit,ACCESS_MASK dwDesiredAccess);
-  WINUSERAPI HDESK WINAPI OpenDesktopW(LPCWSTR lpszDesktop,DWORD dwFlags,WINBOOL fInherit,ACCESS_MASK dwDesiredAccess);
-  WINUSERAPI HDESK WINAPI OpenInputDesktop(DWORD dwFlags,WINBOOL fInherit,ACCESS_MASK dwDesiredAccess);
-  WINUSERAPI WINBOOL WINAPI EnumDesktopsA(HWINSTA hwinsta,DESKTOPENUMPROCA lpEnumFunc,LPARAM lParam);
-  WINUSERAPI WINBOOL WINAPI EnumDesktopsW(HWINSTA hwinsta,DESKTOPENUMPROCW lpEnumFunc,LPARAM lParam);
-  WINUSERAPI WINBOOL WINAPI EnumDesktopWindows(HDESK hDesktop,WNDENUMPROC lpfn,LPARAM lParam);
-  WINUSERAPI WINBOOL WINAPI SwitchDesktop(HDESK hDesktop);
-  WINUSERAPI WINBOOL WINAPI SetThreadDesktop(HDESK hDesktop);
-  WINUSERAPI WINBOOL WINAPI CloseDesktop(HDESK hDesktop);
-  WINUSERAPI HDESK WINAPI GetThreadDesktop(DWORD dwThreadId);
-#endif
-
-#ifndef NOWINDOWSTATION
-#define WINSTA_ENUMDESKTOPS 0x0001L
-#define WINSTA_READATTRIBUTES 0x0002L
-#define WINSTA_ACCESSCLIPBOARD 0x0004L
-#define WINSTA_CREATEDESKTOP 0x0008L
-#define WINSTA_WRITEATTRIBUTES 0x0010L
-#define WINSTA_ACCESSGLOBALATOMS 0x0020L
-#define WINSTA_EXITWINDOWS 0x0040L
-#define WINSTA_ENUMERATE 0x0100L
-#define WINSTA_READSCREEN 0x0200L
-#define WINSTA_ALL_ACCESS (WINSTA_ENUMDESKTOPS | WINSTA_READATTRIBUTES | WINSTA_ACCESSCLIPBOARD | WINSTA_CREATEDESKTOP | WINSTA_WRITEATTRIBUTES | WINSTA_ACCESSGLOBALATOMS | WINSTA_EXITWINDOWS | WINSTA_ENUMERATE | WINSTA_READSCREEN)
-
-#define CWF_CREATE_ONLY 0x0001L
-
-#define WSF_VISIBLE 0x0001L
-
-#ifdef UNICODE
-#define CreateWindowStation CreateWindowStationW
-#define OpenWindowStation OpenWindowStationW
-#define EnumWindowStations EnumWindowStationsW
-#else
-#define CreateWindowStation CreateWindowStationA
-#define OpenWindowStation OpenWindowStationA
-#define EnumWindowStations EnumWindowStationsA
-#endif
-
-  WINUSERAPI HWINSTA WINAPI CreateWindowStationA(LPCSTR lpwinsta,DWORD dwFlags,ACCESS_MASK dwDesiredAccess,LPSECURITY_ATTRIBUTES lpsa);
-  WINUSERAPI HWINSTA WINAPI CreateWindowStationW(LPCWSTR lpwinsta,DWORD dwFlags,ACCESS_MASK dwDesiredAccess,LPSECURITY_ATTRIBUTES lpsa);
-  WINUSERAPI HWINSTA WINAPI OpenWindowStationA(LPCSTR lpszWinSta,WINBOOL fInherit,ACCESS_MASK dwDesiredAccess);
-  WINUSERAPI HWINSTA WINAPI OpenWindowStationW(LPCWSTR lpszWinSta,WINBOOL fInherit,ACCESS_MASK dwDesiredAccess);
-  WINUSERAPI WINBOOL WINAPI EnumWindowStationsA(WINSTAENUMPROCA lpEnumFunc,LPARAM lParam);
-  WINUSERAPI WINBOOL WINAPI EnumWindowStationsW(WINSTAENUMPROCW lpEnumFunc,LPARAM lParam);
-  WINUSERAPI WINBOOL WINAPI CloseWindowStation(HWINSTA hWinSta);
-  WINUSERAPI WINBOOL WINAPI SetProcessWindowStation(HWINSTA hWinSta);
-  WINUSERAPI HWINSTA WINAPI GetProcessWindowStation(VOID);
-#endif
-
-#ifndef NOSECURITY
-  WINUSERAPI WINBOOL WINAPI SetUserObjectSecurity(HANDLE hObj,PSECURITY_INFORMATION pSIRequested,PSECURITY_DESCRIPTOR pSID);
-  WINUSERAPI WINBOOL WINAPI GetUserObjectSecurity(HANDLE hObj,PSECURITY_INFORMATION pSIRequested,PSECURITY_DESCRIPTOR pSID,DWORD nLength,LPDWORD lpnLengthNeeded);
-
-#define UOI_FLAGS 1
-#define UOI_NAME 2
-#define UOI_TYPE 3
-#define UOI_USER_SID 4
-
-  typedef struct tagUSEROBJECTFLAGS {
-    WINBOOL fInherit;
-    WINBOOL fReserved;
-    DWORD dwFlags;
-  } USEROBJECTFLAGS,*PUSEROBJECTFLAGS;
-
-#ifdef UNICODE
-#define GetUserObjectInformation GetUserObjectInformationW
-#define SetUserObjectInformation SetUserObjectInformationW
-#else
-#define GetUserObjectInformation GetUserObjectInformationA
-#define SetUserObjectInformation SetUserObjectInformationA
-#endif
-
-  WINUSERAPI WINBOOL WINAPI GetUserObjectInformationA(HANDLE hObj,int nIndex,PVOID pvInfo,DWORD nLength,LPDWORD lpnLengthNeeded);
-  WINUSERAPI WINBOOL WINAPI GetUserObjectInformationW(HANDLE hObj,int nIndex,PVOID pvInfo,DWORD nLength,LPDWORD lpnLengthNeeded);
-  WINUSERAPI WINBOOL WINAPI SetUserObjectInformationA(HANDLE hObj,int nIndex,PVOID pvInfo,DWORD nLength);
-  WINUSERAPI WINBOOL WINAPI SetUserObjectInformationW(HANDLE hObj,int nIndex,PVOID pvInfo,DWORD nLength);
-#endif
-
-  typedef struct tagWNDCLASSEXA {
-    UINT cbSize;
-    UINT style;
-    WNDPROC lpfnWndProc;
-    int cbClsExtra;
-    int cbWndExtra;
-    HINSTANCE hInstance;
-    HICON hIcon;
-    HCURSOR hCursor;
-    HBRUSH hbrBackground;
-    LPCSTR lpszMenuName;
-    LPCSTR lpszClassName;
-    HICON hIconSm;
-  } WNDCLASSEXA,*PWNDCLASSEXA,*NPWNDCLASSEXA,*LPWNDCLASSEXA;
-
-  typedef struct tagWNDCLASSEXW {
-    UINT cbSize;
-    UINT style;
-    WNDPROC lpfnWndProc;
-    int cbClsExtra;
-    int cbWndExtra;
-    HINSTANCE hInstance;
-    HICON hIcon;
-    HCURSOR hCursor;
-    HBRUSH hbrBackground;
-    LPCWSTR lpszMenuName;
-    LPCWSTR lpszClassName;
-
-    HICON hIconSm;
-  } WNDCLASSEXW,*PWNDCLASSEXW,*NPWNDCLASSEXW,*LPWNDCLASSEXW;
-
-#ifdef UNICODE
-  typedef WNDCLASSEXW WNDCLASSEX;
-  typedef PWNDCLASSEXW PWNDCLASSEX;
-  typedef NPWNDCLASSEXW NPWNDCLASSEX;
-  typedef LPWNDCLASSEXW LPWNDCLASSEX;
-#else
-  typedef WNDCLASSEXA WNDCLASSEX;
-  typedef PWNDCLASSEXA PWNDCLASSEX;
-  typedef NPWNDCLASSEXA NPWNDCLASSEX;
-  typedef LPWNDCLASSEXA LPWNDCLASSEX;
-#endif
-
-  typedef struct tagWNDCLASSA {
-    UINT style;
-    WNDPROC lpfnWndProc;
-    int cbClsExtra;
-    int cbWndExtra;
-    HINSTANCE hInstance;
-    HICON hIcon;
-    HCURSOR hCursor;
-    HBRUSH hbrBackground;
-    LPCSTR lpszMenuName;
-    LPCSTR lpszClassName;
-  } WNDCLASSA,*PWNDCLASSA,*NPWNDCLASSA,*LPWNDCLASSA;
-
-  typedef struct tagWNDCLASSW {
-    UINT style;
-    WNDPROC lpfnWndProc;
-    int cbClsExtra;
-    int cbWndExtra;
-    HINSTANCE hInstance;
-    HICON hIcon;
-    HCURSOR hCursor;
-    HBRUSH hbrBackground;
-    LPCWSTR lpszMenuName;
-    LPCWSTR lpszClassName;
-  } WNDCLASSW,*PWNDCLASSW,*NPWNDCLASSW,*LPWNDCLASSW;
-
-#ifdef UNICODE
-  typedef WNDCLASSW WNDCLASS;
-  typedef PWNDCLASSW PWNDCLASS;
-  typedef NPWNDCLASSW NPWNDCLASS;
-  typedef LPWNDCLASSW LPWNDCLASS;
-#else
-  typedef WNDCLASSA WNDCLASS;
-  typedef PWNDCLASSA PWNDCLASS;
-  typedef NPWNDCLASSA NPWNDCLASS;
-  typedef LPWNDCLASSA LPWNDCLASS;
-#endif
-
-  WINUSERAPI WINBOOL WINAPI IsHungAppWindow(HWND hwnd);
-  WINUSERAPI VOID WINAPI DisableProcessWindowsGhosting(VOID);
-
-#ifndef NOMSG
-  typedef struct tagMSG {
-    HWND hwnd;
-    UINT message;
-    WPARAM wParam;
-    LPARAM lParam;
-    DWORD time;
-    POINT pt;
-  } MSG,*PMSG,*NPMSG,*LPMSG;
-
-#define POINTSTOPOINT(pt,pts) { (pt).x = (LONG)(SHORT)LOWORD(*(LONG*)&pts); (pt).y = (LONG)(SHORT)HIWORD(*(LONG*)&pts); }
-
-#define POINTTOPOINTS(pt) (MAKELONG((short)((pt).x),(short)((pt).y)))
-#define MAKEWPARAM(l,h) ((WPARAM)(DWORD)MAKELONG(l,h))
-#define MAKELPARAM(l,h) ((LPARAM)(DWORD)MAKELONG(l,h))
-#define MAKELRESULT(l,h) ((LRESULT)(DWORD)MAKELONG(l,h))
-#endif
-
-#ifndef NOWINOFFSETS
-#define GWL_WNDPROC (-4)
-#define GWL_HINSTANCE (-6)
-#define GWL_HWNDPARENT (-8)
-#define GWL_STYLE (-16)
-#define GWL_EXSTYLE (-20)
-#define GWL_USERDATA (-21)
-#define GWL_ID (-12)
-
-#ifdef _WIN64
-#undef GWL_WNDPROC
-#undef GWL_HINSTANCE
-#undef GWL_HWNDPARENT
-#undef GWL_USERDATA
-#endif
-
-#define GWLP_WNDPROC (-4)
-#define GWLP_HINSTANCE (-6)
-#define GWLP_HWNDPARENT (-8)
-#define GWLP_USERDATA (-21)
-#define GWLP_ID (-12)
-
-#define GCL_MENUNAME (-8)
-#define GCL_HBRBACKGROUND (-10)
-#define GCL_HCURSOR (-12)
-#define GCL_HICON (-14)
-#define GCL_HMODULE (-16)
-#define GCL_CBWNDEXTRA (-18)
-#define GCL_CBCLSEXTRA (-20)
-#define GCL_WNDPROC (-24)
-#define GCL_STYLE (-26)
-#define GCW_ATOM (-32)
-#define GCL_HICONSM (-34)
-
-#ifdef _WIN64
-
-#undef GCL_MENUNAME
-#undef GCL_HBRBACKGROUND
-#undef GCL_HCURSOR
-#undef GCL_HICON
-#undef GCL_HMODULE
-#undef GCL_WNDPROC
-#undef GCL_HICONSM
-#endif
-
-#define GCLP_MENUNAME (-8)
-#define GCLP_HBRBACKGROUND (-10)
-#define GCLP_HCURSOR (-12)
-#define GCLP_HICON (-14)
-#define GCLP_HMODULE (-16)
-#define GCLP_WNDPROC (-24)
-#define GCLP_HICONSM (-34)
-#endif
-
-#ifndef NOWINMESSAGES
-
-#define WM_NULL 0x0000
-#define WM_CREATE 0x0001
-#define WM_DESTROY 0x0002
-#define WM_MOVE 0x0003
-#define WM_SIZE 0x0005
-
-#define WM_ACTIVATE 0x0006
-
-#define WA_INACTIVE 0
-#define WA_ACTIVE 1
-#define WA_CLICKACTIVE 2
-
-#define WM_SETFOCUS 0x0007
-#define WM_KILLFOCUS 0x0008
-#define WM_ENABLE 0x000A
-#define WM_SETREDRAW 0x000B
-#define WM_SETTEXT 0x000C
-#define WM_GETTEXT 0x000D
-#define WM_GETTEXTLENGTH 0x000E
-#define WM_PAINT 0x000F
-#define WM_CLOSE 0x0010
-#ifndef _WIN32_WCE
-#define WM_QUERYENDSESSION 0x0011
-#define WM_QUERYOPEN 0x0013
-#define WM_ENDSESSION 0x0016
-#endif
-#define WM_QUIT 0x0012
-#define WM_ERASEBKGND 0x0014
-#define WM_SYSCOLORCHANGE 0x0015
-#define WM_SHOWWINDOW 0x0018
-#define WM_WININICHANGE 0x001A
-#define WM_SETTINGCHANGE WM_WININICHANGE
-#define WM_DEVMODECHANGE 0x001B
-#define WM_ACTIVATEAPP 0x001C
-#define WM_FONTCHANGE 0x001D
-#define WM_TIMECHANGE 0x001E
-#define WM_CANCELMODE 0x001F
-#define WM_SETCURSOR 0x0020
-#define WM_MOUSEACTIVATE 0x0021
-#define WM_CHILDACTIVATE 0x0022
-#define WM_QUEUESYNC 0x0023
-
-#define WM_GETMINMAXINFO 0x0024
-
-  typedef struct tagMINMAXINFO {
-    POINT ptReserved;
-    POINT ptMaxSize;
-    POINT ptMaxPosition;
-    POINT ptMinTrackSize;
-    POINT ptMaxTrackSize;
-  } MINMAXINFO,*PMINMAXINFO,*LPMINMAXINFO;
-
-#define WM_PAINTICON 0x0026
-#define WM_ICONERASEBKGND 0x0027
-#define WM_NEXTDLGCTL 0x0028
-#define WM_SPOOLERSTATUS 0x002A
-#define WM_DRAWITEM 0x002B
-#define WM_MEASUREITEM 0x002C
-#define WM_DELETEITEM 0x002D
-#define WM_VKEYTOITEM 0x002E
-#define WM_CHARTOITEM 0x002F
-#define WM_SETFONT 0x0030
-#define WM_GETFONT 0x0031
-#define WM_SETHOTKEY 0x0032
-#define WM_GETHOTKEY 0x0033
-#define WM_QUERYDRAGICON 0x0037
-#define WM_COMPAREITEM 0x0039
-#ifndef _WIN32_WCE
-#define WM_GETOBJECT 0x003D
-#endif
-#define WM_COMPACTING 0x0041
-#define WM_COMMNOTIFY 0x0044
-#define WM_WINDOWPOSCHANGING 0x0046
-#define WM_WINDOWPOSCHANGED 0x0047
-
-#define WM_POWER 0x0048
-
-#define PWR_OK 1
-#define PWR_FAIL (-1)
-#define PWR_SUSPENDREQUEST 1
-#define PWR_SUSPENDRESUME 2
-#define PWR_CRITICALRESUME 3
-
-#define WM_COPYDATA 0x004A
-#define WM_CANCELJOURNAL 0x004B
-
-  typedef struct tagCOPYDATASTRUCT {
-    ULONG_PTR dwData;
-    DWORD cbData;
-    PVOID lpData;
-  } COPYDATASTRUCT,*PCOPYDATASTRUCT;
-
-  typedef struct tagMDINEXTMENU {
-    HMENU hmenuIn;
-    HMENU hmenuNext;
-    HWND hwndNext;
-  } MDINEXTMENU,*PMDINEXTMENU,*LPMDINEXTMENU;
-
-#define WM_NOTIFY 0x004E
-#define WM_INPUTLANGCHANGEREQUEST 0x0050
-#define WM_INPUTLANGCHANGE 0x0051
-#define WM_TCARD 0x0052
-#define WM_HELP 0x0053
-#define WM_USERCHANGED 0x0054
-#define WM_NOTIFYFORMAT 0x0055
-
-#define NFR_ANSI 1
-#define NFR_UNICODE 2
-#define NF_QUERY 3
-#define NF_REQUERY 4
-
-#define WM_CONTEXTMENU 0x007B
-#define WM_STYLECHANGING 0x007C
-#define WM_STYLECHANGED 0x007D
-#define WM_DISPLAYCHANGE 0x007E
-#define WM_GETICON 0x007F
-#define WM_SETICON 0x0080
-
-#define WM_NCCREATE 0x0081
-#define WM_NCDESTROY 0x0082
-#define WM_NCCALCSIZE 0x0083
-#define WM_NCHITTEST 0x0084
-#define WM_NCPAINT 0x0085
-#define WM_NCACTIVATE 0x0086
-#define WM_GETDLGCODE 0x0087
-#ifndef _WIN32_WCE
-#define WM_SYNCPAINT 0x0088
-#endif
-#define WM_NCMOUSEMOVE 0x00A0
-#define WM_NCLBUTTONDOWN 0x00A1
-#define WM_NCLBUTTONUP 0x00A2
-#define WM_NCLBUTTONDBLCLK 0x00A3
-#define WM_NCRBUTTONDOWN 0x00A4
-#define WM_NCRBUTTONUP 0x00A5
-#define WM_NCRBUTTONDBLCLK 0x00A6
-#define WM_NCMBUTTONDOWN 0x00A7
-#define WM_NCMBUTTONUP 0x00A8
-#define WM_NCMBUTTONDBLCLK 0x00A9
-
-#define WM_NCXBUTTONDOWN 0x00AB
-#define WM_NCXBUTTONUP 0x00AC
-#define WM_NCXBUTTONDBLCLK 0x00AD
-#define WM_INPUT 0x00FF
-#define WM_KEYFIRST 0x0100
-#define WM_KEYDOWN 0x0100
-#define WM_KEYUP 0x0101
-#define WM_CHAR 0x0102
-#define WM_DEADCHAR 0x0103
-#define WM_SYSKEYDOWN 0x0104
-#define WM_SYSKEYUP 0x0105
-#define WM_SYSCHAR 0x0106
-#define WM_SYSDEADCHAR 0x0107
-#define WM_UNICHAR 0x0109
-#define WM_KEYLAST 0x0109
-#define UNICODE_NOCHAR 0xFFFF
-#define WM_IME_STARTCOMPOSITION 0x010D
-#define WM_IME_ENDCOMPOSITION 0x010E
-#define WM_IME_COMPOSITION 0x010F
-#define WM_IME_KEYLAST 0x010F
-#define WM_INITDIALOG 0x0110
-#define WM_COMMAND 0x0111
-#define WM_SYSCOMMAND 0x0112
-#define WM_TIMER 0x0113
-#define WM_HSCROLL 0x0114
-#define WM_VSCROLL 0x0115
-#define WM_INITMENU 0x0116
-#define WM_INITMENUPOPUP 0x0117
-#define WM_MENUSELECT 0x011F
-#define WM_MENUCHAR 0x0120
-#define WM_ENTERIDLE 0x0121
-#ifndef _WIN32_WCE
-#define WM_MENURBUTTONUP 0x0122
-#define WM_MENUDRAG 0x0123
-#define WM_MENUGETOBJECT 0x0124
-#define WM_UNINITMENUPOPUP 0x0125
-#define WM_MENUCOMMAND 0x0126
-
-#ifndef _WIN32_WCE
-#define WM_CHANGEUISTATE 0x0127
-#define WM_UPDATEUISTATE 0x0128
-#define WM_QUERYUISTATE 0x0129
-
-#define UIS_SET 1
-#define UIS_CLEAR 2
-#define UIS_INITIALIZE 3
-
-#define UISF_HIDEFOCUS 0x1
-#define UISF_HIDEACCEL 0x2
-#define UISF_ACTIVE 0x4
-#endif
-#endif
-
-#define WM_CTLCOLORMSGBOX 0x0132
-#define WM_CTLCOLOREDIT 0x0133
-#define WM_CTLCOLORLISTBOX 0x0134
-#define WM_CTLCOLORBTN 0x0135
-#define WM_CTLCOLORDLG 0x0136
-#define WM_CTLCOLORSCROLLBAR 0x0137
-#define WM_CTLCOLORSTATIC 0x0138
-#define MN_GETHMENU 0x01E1
-
-#define WM_MOUSEFIRST 0x0200
-#define WM_MOUSEMOVE 0x0200
-#define WM_LBUTTONDOWN 0x0201
-#define WM_LBUTTONUP 0x0202
-#define WM_LBUTTONDBLCLK 0x0203
-#define WM_RBUTTONDOWN 0x0204
-#define WM_RBUTTONUP 0x0205
-#define WM_RBUTTONDBLCLK 0x0206
-#define WM_MBUTTONDOWN 0x0207
-#define WM_MBUTTONUP 0x0208
-#define WM_MBUTTONDBLCLK 0x0209
-#define WM_MOUSEWHEEL 0x020A
-#define WM_XBUTTONDOWN 0x020B
-#define WM_XBUTTONUP 0x020C
-#define WM_XBUTTONDBLCLK 0x020D
-#define WM_MOUSELAST 0x020D
-
-#define WHEEL_DELTA 120
-#define GET_WHEEL_DELTA_WPARAM(wParam) ((short)HIWORD(wParam))
-
-#define WHEEL_PAGESCROLL (UINT_MAX)
-
-#define GET_KEYSTATE_WPARAM(wParam) (LOWORD(wParam))
-#define GET_NCHITTEST_WPARAM(wParam) ((short)LOWORD(wParam))
-#define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam))
-
-#define XBUTTON1 0x0001
-#define XBUTTON2 0x0002
-
-#define WM_PARENTNOTIFY 0x0210
-#define WM_ENTERMENULOOP 0x0211
-#define WM_EXITMENULOOP 0x0212
-
-#define WM_NEXTMENU 0x0213
-#define WM_SIZING 0x0214
-#define WM_CAPTURECHANGED 0x0215
-#define WM_MOVING 0x0216
-
-#define WM_POWERBROADCAST 0x0218
-
-#ifndef _WIN32_WCE
-#define PBT_APMQUERYSUSPEND 0x0000
-#define PBT_APMQUERYSTANDBY 0x0001
-
-#define PBT_APMQUERYSUSPENDFAILED 0x0002
-#define PBT_APMQUERYSTANDBYFAILED 0x0003
-
-#define PBT_APMSUSPEND 0x0004
-#define PBT_APMSTANDBY 0x0005
-
-#define PBT_APMRESUMECRITICAL 0x0006
-#define PBT_APMRESUMESUSPEND 0x0007
-#define PBT_APMRESUMESTANDBY 0x0008
-
-#define PBTF_APMRESUMEFROMFAILURE 0x00000001
-
-#define PBT_APMBATTERYLOW 0x0009
-#define PBT_APMPOWERSTATUSCHANGE 0x000A
-
-#define PBT_APMOEMEVENT 0x000B
-#define PBT_APMRESUMEAUTOMATIC 0x0012
-#endif
-
-#define WM_DEVICECHANGE 0x0219
-
-#define WM_MDICREATE 0x0220
-#define WM_MDIDESTROY 0x0221
-#define WM_MDIACTIVATE 0x0222
-#define WM_MDIRESTORE 0x0223
-#define WM_MDINEXT 0x0224
-#define WM_MDIMAXIMIZE 0x0225
-#define WM_MDITILE 0x0226
-#define WM_MDICASCADE 0x0227
-#define WM_MDIICONARRANGE 0x0228
-#define WM_MDIGETACTIVE 0x0229
-
-#define WM_MDISETMENU 0x0230
-#define WM_ENTERSIZEMOVE 0x0231
-#define WM_EXITSIZEMOVE 0x0232
-#define WM_DROPFILES 0x0233
-#define WM_MDIREFRESHMENU 0x0234
-
-#define WM_IME_SETCONTEXT 0x0281
-#define WM_IME_NOTIFY 0x0282
-#define WM_IME_CONTROL 0x0283
-#define WM_IME_COMPOSITIONFULL 0x0284
-#define WM_IME_SELECT 0x0285
-#define WM_IME_CHAR 0x0286
-#define WM_IME_REQUEST 0x0288
-#define WM_IME_KEYDOWN 0x0290
-#define WM_IME_KEYUP 0x0291
-
-#define WM_MOUSEHOVER 0x02A1
-#define WM_MOUSELEAVE 0x02A3
-#define WM_NCMOUSEHOVER 0x02A0
-#define WM_NCMOUSELEAVE 0x02A2
-#define WM_WTSSESSION_CHANGE 0x02B1
-#define WM_TABLET_FIRST 0x02c0
-#define WM_TABLET_LAST 0x02df
-#define WM_CUT 0x0300
-#define WM_COPY 0x0301
-#define WM_PASTE 0x0302
-#define WM_CLEAR 0x0303
-#define WM_UNDO 0x0304
-#define WM_RENDERFORMAT 0x0305
-#define WM_RENDERALLFORMATS 0x0306
-#define WM_DESTROYCLIPBOARD 0x0307
-#define WM_DRAWCLIPBOARD 0x0308
-#define WM_PAINTCLIPBOARD 0x0309
-#define WM_VSCROLLCLIPBOARD 0x030A
-#define WM_SIZECLIPBOARD 0x030B
-#define WM_ASKCBFORMATNAME 0x030C
-#define WM_CHANGECBCHAIN 0x030D
-#define WM_HSCROLLCLIPBOARD 0x030E
-#define WM_QUERYNEWPALETTE 0x030F
-#define WM_PALETTEISCHANGING 0x0310
-#define WM_PALETTECHANGED 0x0311
-#define WM_HOTKEY 0x0312
-#define WM_PRINT 0x0317
-#define WM_PRINTCLIENT 0x0318
-#define WM_APPCOMMAND 0x0319
-#define WM_THEMECHANGED 0x031A
-#define WM_HANDHELDFIRST 0x0358
-#define WM_HANDHELDLAST 0x035F
-#define WM_AFXFIRST 0x0360
-#define WM_AFXLAST 0x037F
-#define WM_PENWINFIRST 0x0380
-#define WM_PENWINLAST 0x038F
-#define WM_APP 0x8000
-#define WM_USER 0x0400
-
-#define WMSZ_LEFT 1
-#define WMSZ_RIGHT 2
-#define WMSZ_TOP 3
-#define WMSZ_TOPLEFT 4
-#define WMSZ_TOPRIGHT 5
-#define WMSZ_BOTTOM 6
-#define WMSZ_BOTTOMLEFT 7
-#define WMSZ_BOTTOMRIGHT 8
-
-#ifndef NONCMESSAGES
-
-#define HTERROR (-2)
-#define HTTRANSPARENT (-1)
-#define HTNOWHERE 0
-#define HTCLIENT 1
-#define HTCAPTION 2
-#define HTSYSMENU 3
-#define HTGROWBOX 4
-#define HTSIZE HTGROWBOX
-#define HTMENU 5
-#define HTHSCROLL 6
-#define HTVSCROLL 7
-#define HTMINBUTTON 8
-#define HTMAXBUTTON 9
-#define HTLEFT 10
-#define HTRIGHT 11
-#define HTTOP 12
-#define HTTOPLEFT 13
-#define HTTOPRIGHT 14
-#define HTBOTTOM 15
-#define HTBOTTOMLEFT 16
-#define HTBOTTOMRIGHT 17
-#define HTBORDER 18
-#define HTREDUCE HTMINBUTTON
-#define HTZOOM HTMAXBUTTON
-#define HTSIZEFIRST HTLEFT
-#define HTSIZELAST HTBOTTOMRIGHT
-#define HTOBJECT 19
-#define HTCLOSE 20
-#define HTHELP 21
-
-#define SMTO_NORMAL 0x0000
-#define SMTO_BLOCK 0x0001
-#define SMTO_ABORTIFHUNG 0x0002
-#define SMTO_NOTIMEOUTIFNOTHUNG 0x0008
-#endif
-
-#define MA_ACTIVATE 1
-#define MA_ACTIVATEANDEAT 2
-#define MA_NOACTIVATE 3
-#define MA_NOACTIVATEANDEAT 4
-
-#define ICON_SMALL 0
-#define ICON_BIG 1
-#define ICON_SMALL2 2
-
-#ifdef UNICODE
-#define RegisterWindowMessage RegisterWindowMessageW
-#else
-#define RegisterWindowMessage RegisterWindowMessageA
-#endif
-
-  WINUSERAPI UINT WINAPI RegisterWindowMessageA(LPCSTR lpString);
-  WINUSERAPI UINT WINAPI RegisterWindowMessageW(LPCWSTR lpString);
-
-#define SIZE_RESTORED 0
-#define SIZE_MINIMIZED 1
-#define SIZE_MAXIMIZED 2
-#define SIZE_MAXSHOW 3
-#define SIZE_MAXHIDE 4
-
-#define SIZENORMAL SIZE_RESTORED
-#define SIZEICONIC SIZE_MINIMIZED
-#define SIZEFULLSCREEN SIZE_MAXIMIZED
-#define SIZEZOOMSHOW SIZE_MAXSHOW
-#define SIZEZOOMHIDE SIZE_MAXHIDE
-
-  typedef struct tagWINDOWPOS {
-    HWND hwnd;
-    HWND hwndInsertAfter;
-    int x;
-    int y;
-    int cx;
-    int cy;
-    UINT flags;
-  } WINDOWPOS,*LPWINDOWPOS,*PWINDOWPOS;
-
-  typedef struct tagNCCALCSIZE_PARAMS {
-    RECT rgrc[3];
-    PWINDOWPOS lppos;
-  } NCCALCSIZE_PARAMS,*LPNCCALCSIZE_PARAMS;
-
-#define WVR_ALIGNTOP 0x0010
-#define WVR_ALIGNLEFT 0x0020
-#define WVR_ALIGNBOTTOM 0x0040
-#define WVR_ALIGNRIGHT 0x0080
-#define WVR_HREDRAW 0x0100
-#define WVR_VREDRAW 0x0200
-#define WVR_REDRAW (WVR_HREDRAW | WVR_VREDRAW)
-#define WVR_VALIDRECTS 0x0400
-
-#ifndef NOKEYSTATES
-
-#define MK_LBUTTON 0x0001
-#define MK_RBUTTON 0x0002
-#define MK_SHIFT 0x0004
-#define MK_CONTROL 0x0008
-#define MK_MBUTTON 0x0010
-#define MK_XBUTTON1 0x0020
-#define MK_XBUTTON2 0x0040
-#endif
-
-#ifndef NOTRACKMOUSEEVENT
-#define TME_HOVER 0x00000001
-#define TME_LEAVE 0x00000002
-#define TME_NONCLIENT 0x00000010
-#define TME_QUERY 0x40000000
-#define TME_CANCEL 0x80000000
-
-#define HOVER_DEFAULT 0xFFFFFFFF
-#endif
-
-  typedef struct tagTRACKMOUSEEVENT {
-    DWORD cbSize;
-    DWORD dwFlags;
-    HWND hwndTrack;
-    DWORD dwHoverTime;
-  } TRACKMOUSEEVENT,*LPTRACKMOUSEEVENT;
-
-  WINUSERAPI WINBOOL WINAPI TrackMouseEvent(LPTRACKMOUSEEVENT lpEventTrack);
-#endif
-
-#ifndef NOWINSTYLES
-
-#define WS_OVERLAPPED 0x00000000L
-#define WS_POPUP 0x80000000L
-#define WS_CHILD 0x40000000L
-#define WS_MINIMIZE 0x20000000L
-#define WS_VISIBLE 0x10000000L
-#define WS_DISABLED 0x08000000L
-#define WS_CLIPSIBLINGS 0x04000000L
-#define WS_CLIPCHILDREN 0x02000000L
-#define WS_MAXIMIZE 0x01000000L
-#define WS_CAPTION 0x00C00000L
-#define WS_BORDER 0x00800000L
-#define WS_DLGFRAME 0x00400000L
-#define WS_VSCROLL 0x00200000L
-#define WS_HSCROLL 0x00100000L
-#define WS_SYSMENU 0x00080000L
-#define WS_THICKFRAME 0x00040000L
-#define WS_GROUP 0x00020000L
-#define WS_TABSTOP 0x00010000L
-#define WS_MINIMIZEBOX 0x00020000L
-#define WS_MAXIMIZEBOX 0x00010000L
-#define WS_TILED WS_OVERLAPPED
-#define WS_ICONIC WS_MINIMIZE
-#define WS_SIZEBOX WS_THICKFRAME
-#define WS_TILEDWINDOW WS_OVERLAPPEDWINDOW
-#define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
-#define WS_POPUPWINDOW (WS_POPUP | WS_BORDER | WS_SYSMENU)
-#define WS_CHILDWINDOW (WS_CHILD)
-
-#define WS_EX_DLGMODALFRAME 0x00000001L
-#define WS_EX_NOPARENTNOTIFY 0x00000004L
-#define WS_EX_TOPMOST 0x00000008L
-#define WS_EX_ACCEPTFILES 0x00000010L
-#define WS_EX_TRANSPARENT 0x00000020L
-#define WS_EX_MDICHILD 0x00000040L
-#define WS_EX_TOOLWINDOW 0x00000080L
-#define WS_EX_WINDOWEDGE 0x00000100L
-#define WS_EX_CLIENTEDGE 0x00000200L
-#define WS_EX_CONTEXTHELP 0x00000400L
-#define WS_EX_RIGHT 0x00001000L
-#define WS_EX_LEFT 0x00000000L
-#define WS_EX_RTLREADING 0x00002000L
-#define WS_EX_LTRREADING 0x00000000L
-#define WS_EX_LEFTSCROLLBAR 0x00004000L
-#define WS_EX_RIGHTSCROLLBAR 0x00000000L
-#define WS_EX_CONTROLPARENT 0x00010000L
-#define WS_EX_STATICEDGE 0x00020000L
-#define WS_EX_APPWINDOW 0x00040000L
-#define WS_EX_OVERLAPPEDWINDOW (WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE)
-#define WS_EX_PALETTEWINDOW (WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST)
-#define WS_EX_LAYERED 0x00080000
-#define WS_EX_NOINHERITLAYOUT 0x00100000L
-#define WS_EX_LAYOUTRTL 0x00400000L
-#define WS_EX_COMPOSITED 0x02000000L
-#define WS_EX_NOACTIVATE 0x08000000L
-
-#define CS_VREDRAW 0x0001
-#define CS_HREDRAW 0x0002
-#define CS_DBLCLKS 0x0008
-#define CS_OWNDC 0x0020
-#define CS_CLASSDC 0x0040
-#define CS_PARENTDC 0x0080
-#define CS_NOCLOSE 0x0200
-#define CS_SAVEBITS 0x0800
-#define CS_BYTEALIGNCLIENT 0x1000
-#define CS_BYTEALIGNWINDOW 0x2000
-#define CS_GLOBALCLASS 0x4000
-#define CS_IME 0x00010000
-#define CS_DROPSHADOW 0x00020000
-#endif
-
-#define PRF_CHECKVISIBLE 0x00000001L
-#define PRF_NONCLIENT 0x00000002L
-#define PRF_CLIENT 0x00000004L
-#define PRF_ERASEBKGND 0x00000008L
-#define PRF_CHILDREN 0x00000010L
-#define PRF_OWNED 0x00000020L
-
-#define BDR_RAISEDOUTER 0x0001
-#define BDR_SUNKENOUTER 0x0002
-#define BDR_RAISEDINNER 0x0004
-#define BDR_SUNKENINNER 0x0008
-
-#define BDR_OUTER (BDR_RAISEDOUTER | BDR_SUNKENOUTER)
-#define BDR_INNER (BDR_RAISEDINNER | BDR_SUNKENINNER)
-#define BDR_RAISED (BDR_RAISEDOUTER | BDR_RAISEDINNER)
-#define BDR_SUNKEN (BDR_SUNKENOUTER | BDR_SUNKENINNER)
-
-#define EDGE_RAISED (BDR_RAISEDOUTER | BDR_RAISEDINNER)
-#define EDGE_SUNKEN (BDR_SUNKENOUTER | BDR_SUNKENINNER)
-#define EDGE_ETCHED (BDR_SUNKENOUTER | BDR_RAISEDINNER)
-#define EDGE_BUMP (BDR_RAISEDOUTER | BDR_SUNKENINNER)
-
-#define BF_LEFT 0x0001
-#define BF_TOP 0x0002
-#define BF_RIGHT 0x0004
-#define BF_BOTTOM 0x0008
-
-#define BF_TOPLEFT (BF_TOP | BF_LEFT)
-#define BF_TOPRIGHT (BF_TOP | BF_RIGHT)
-#define BF_BOTTOMLEFT (BF_BOTTOM | BF_LEFT)
-#define BF_BOTTOMRIGHT (BF_BOTTOM | BF_RIGHT)
-#define BF_RECT (BF_LEFT | BF_TOP | BF_RIGHT | BF_BOTTOM)
-
-#define BF_DIAGONAL 0x0010
-
-#define BF_DIAGONAL_ENDTOPRIGHT (BF_DIAGONAL | BF_TOP | BF_RIGHT)
-#define BF_DIAGONAL_ENDTOPLEFT (BF_DIAGONAL | BF_TOP | BF_LEFT)
-#define BF_DIAGONAL_ENDBOTTOMLEFT (BF_DIAGONAL | BF_BOTTOM | BF_LEFT)
-#define BF_DIAGONAL_ENDBOTTOMRIGHT (BF_DIAGONAL | BF_BOTTOM | BF_RIGHT)
-
-#define BF_MIDDLE 0x0800
-#define BF_SOFT 0x1000
-#define BF_ADJUST 0x2000
-#define BF_FLAT 0x4000
-#define BF_MONO 0x8000
-
-  WINUSERAPI WINBOOL WINAPI DrawEdge(HDC hdc,LPRECT qrc,UINT edge,UINT grfFlags);
-
-#define DFC_CAPTION 1
-#define DFC_MENU 2
-#define DFC_SCROLL 3
-#define DFC_BUTTON 4
-#define DFC_POPUPMENU 5
-
-#define DFCS_CAPTIONCLOSE 0x0000
-#define DFCS_CAPTIONMIN 0x0001
-#define DFCS_CAPTIONMAX 0x0002
-#define DFCS_CAPTIONRESTORE 0x0003
-#define DFCS_CAPTIONHELP 0x0004
-
-#define DFCS_MENUARROW 0x0000
-#define DFCS_MENUCHECK 0x0001
-#define DFCS_MENUBULLET 0x0002
-#define DFCS_MENUARROWRIGHT 0x0004
-#define DFCS_SCROLLUP 0x0000
-#define DFCS_SCROLLDOWN 0x0001
-#define DFCS_SCROLLLEFT 0x0002
-#define DFCS_SCROLLRIGHT 0x0003
-#define DFCS_SCROLLCOMBOBOX 0x0005
-#define DFCS_SCROLLSIZEGRIP 0x0008
-#define DFCS_SCROLLSIZEGRIPRIGHT 0x0010
-
-#define DFCS_BUTTONCHECK 0x0000
-#define DFCS_BUTTONRADIOIMAGE 0x0001
-#define DFCS_BUTTONRADIOMASK 0x0002
-#define DFCS_BUTTONRADIO 0x0004
-#define DFCS_BUTTON3STATE 0x0008
-#define DFCS_BUTTONPUSH 0x0010
-
-#define DFCS_INACTIVE 0x0100
-#define DFCS_PUSHED 0x0200
-#define DFCS_CHECKED 0x0400
-
-#define DFCS_TRANSPARENT 0x0800
-#define DFCS_HOT 0x1000
-
-#define DFCS_ADJUSTRECT 0x2000
-#define DFCS_FLAT 0x4000
-#define DFCS_MONO 0x8000
-
-  WINUSERAPI WINBOOL WINAPI DrawFrameControl(HDC,LPRECT,UINT,UINT);
-
-#define DC_ACTIVE 0x0001
-#define DC_SMALLCAP 0x0002
-#define DC_ICON 0x0004
-#define DC_TEXT 0x0008
-#define DC_INBUTTON 0x0010
-#define DC_GRADIENT 0x0020
-#define DC_BUTTONS 0x1000
-
-  WINUSERAPI WINBOOL WINAPI DrawCaption(HWND hwnd,HDC hdc,CONST RECT *lprect,UINT flags);
-
-#define IDANI_OPEN 1
-#define IDANI_CAPTION 3
-
-  WINUSERAPI WINBOOL WINAPI DrawAnimatedRects(HWND hwnd,int idAni,CONST RECT *lprcFrom,CONST RECT *lprcTo);
-
-#ifndef NOCLIPBOARD
-
-#define CF_TEXT 1
-#define CF_BITMAP 2
-#define CF_METAFILEPICT 3
-#define CF_SYLK 4
-#define CF_DIF 5
-#define CF_TIFF 6
-#define CF_OEMTEXT 7
-#define CF_DIB 8
-#define CF_PALETTE 9
-#define CF_PENDATA 10
-#define CF_RIFF 11
-#define CF_WAVE 12
-#define CF_UNICODETEXT 13
-#define CF_ENHMETAFILE 14
-#define CF_HDROP 15
-#define CF_LOCALE 16
-#define CF_DIBV5 17
-#define CF_MAX 18
-
-#define CF_OWNERDISPLAY 0x0080
-#define CF_DSPTEXT 0x0081
-#define CF_DSPBITMAP 0x0082
-#define CF_DSPMETAFILEPICT 0x0083
-#define CF_DSPENHMETAFILE 0x008E
-
-#define CF_PRIVATEFIRST 0x0200
-#define CF_PRIVATELAST 0x02FF
-
-#define CF_GDIOBJFIRST 0x0300
-#define CF_GDIOBJLAST 0x03FF
-#endif
-
-#define FVIRTKEY TRUE
-#define FNOINVERT 0x02
-#define FSHIFT 0x04
-#define FCONTROL 0x08
-#define FALT 0x10
-
-  typedef struct tagACCEL {
-    BYTE fVirt;
-    WORD key;
-    WORD cmd;
-  } ACCEL,*LPACCEL;
-
-  typedef struct tagPAINTSTRUCT {
-    HDC hdc;
-    WINBOOL fErase;
-    RECT rcPaint;
-    WINBOOL fRestore;
-    WINBOOL fIncUpdate;
-    BYTE rgbReserved[32];
-  } PAINTSTRUCT,*PPAINTSTRUCT,*NPPAINTSTRUCT,*LPPAINTSTRUCT;
-
-  typedef struct tagCREATESTRUCTA {
-    LPVOID lpCreateParams;
-    HINSTANCE hInstance;
-    HMENU hMenu;
-    HWND hwndParent;
-    int cy;
-    int cx;
-    int y;
-    int x;
-    LONG style;
-    LPCSTR lpszName;
-    LPCSTR lpszClass;
-    DWORD dwExStyle;
-  } CREATESTRUCTA,*LPCREATESTRUCTA;
-
-  typedef struct tagCREATESTRUCTW {
-    LPVOID lpCreateParams;
-    HINSTANCE hInstance;
-    HMENU hMenu;
-    HWND hwndParent;
-    int cy;
-    int cx;
-    int y;
-    int x;
-    LONG style;
-    LPCWSTR lpszName;
-    LPCWSTR lpszClass;
-    DWORD dwExStyle;
-  } CREATESTRUCTW,*LPCREATESTRUCTW;
-
-#ifdef UNICODE
-  typedef CREATESTRUCTW CREATESTRUCT;
-  typedef LPCREATESTRUCTW LPCREATESTRUCT;
-#else
-  typedef CREATESTRUCTA CREATESTRUCT;
-  typedef LPCREATESTRUCTA LPCREATESTRUCT;
-#endif
-
-  typedef struct tagWINDOWPLACEMENT {
-    UINT length;
-    UINT flags;
-    UINT showCmd;
-    POINT ptMinPosition;
-    POINT ptMaxPosition;
-    RECT rcNormalPosition;
-  } WINDOWPLACEMENT;
-  typedef WINDOWPLACEMENT *PWINDOWPLACEMENT,*LPWINDOWPLACEMENT;
-
-#define WPF_SETMINPOSITION 0x0001
-#define WPF_RESTORETOMAXIMIZED 0x0002
-#define WPF_ASYNCWINDOWPLACEMENT 0x0004
-
-  typedef struct tagNMHDR {
-    HWND hwndFrom;
-    UINT_PTR idFrom;
-    UINT code;
-  } NMHDR;
-
-  typedef NMHDR *LPNMHDR;
-
-  typedef struct tagSTYLESTRUCT {
-    DWORD styleOld;
-    DWORD styleNew;
-  } STYLESTRUCT,*LPSTYLESTRUCT;
-
-#define ODT_MENU 1
-#define ODT_LISTBOX 2
-#define ODT_COMBOBOX 3
-#define ODT_BUTTON 4
-#define ODT_STATIC 5
-
-#define ODA_DRAWENTIRE 0x0001
-#define ODA_SELECT 0x0002
-#define ODA_FOCUS 0x0004
-
-#define ODS_SELECTED 0x0001
-#define ODS_GRAYED 0x0002
-#define ODS_DISABLED 0x0004
-#define ODS_CHECKED 0x0008
-#define ODS_FOCUS 0x0010
-#define ODS_DEFAULT 0x0020
-#define ODS_COMBOBOXEDIT 0x1000
-#define ODS_HOTLIGHT 0x0040
-#define ODS_INACTIVE 0x0080
-#define ODS_NOACCEL 0x0100
-#define ODS_NOFOCUSRECT 0x0200
-
-  typedef struct tagMEASUREITEMSTRUCT {
-    UINT CtlType;
-    UINT CtlID;
-    UINT itemID;
-    UINT itemWidth;
-    UINT itemHeight;
-    ULONG_PTR itemData;
-  } MEASUREITEMSTRUCT,*PMEASUREITEMSTRUCT,*LPMEASUREITEMSTRUCT;
-
-  typedef struct tagDRAWITEMSTRUCT {
-    UINT CtlType;
-    UINT CtlID;
-    UINT itemID;
-    UINT itemAction;
-    UINT itemState;
-    HWND hwndItem;
-    HDC hDC;
-    RECT rcItem;
-    ULONG_PTR itemData;
-  } DRAWITEMSTRUCT,*PDRAWITEMSTRUCT,*LPDRAWITEMSTRUCT;
-
-  typedef struct tagDELETEITEMSTRUCT {
-    UINT CtlType;
-    UINT CtlID;
-    UINT itemID;
-    HWND hwndItem;
-    ULONG_PTR itemData;
-  } DELETEITEMSTRUCT,*PDELETEITEMSTRUCT,*LPDELETEITEMSTRUCT;
-
-  typedef struct tagCOMPAREITEMSTRUCT {
-    UINT CtlType;
-    UINT CtlID;
-    HWND hwndItem;
-    UINT itemID1;
-    ULONG_PTR itemData1;
-    UINT itemID2;
-    ULONG_PTR itemData2;
-    DWORD dwLocaleId;
-  } COMPAREITEMSTRUCT,*PCOMPAREITEMSTRUCT,*LPCOMPAREITEMSTRUCT;
-
-#ifndef NOMSG
-#ifdef UNICODE
-#define GetMessage GetMessageW
-#define DispatchMessage DispatchMessageW
-#define PeekMessage PeekMessageW
-#else
-#define GetMessage GetMessageA
-#define DispatchMessage DispatchMessageA
-#define PeekMessage PeekMessageA
-#endif
-
-  WINUSERAPI WINBOOL WINAPI GetMessageA(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax);
-  WINUSERAPI WINBOOL WINAPI GetMessageW(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax);
-  WINUSERAPI WINBOOL WINAPI TranslateMessage(CONST MSG *lpMsg);
-  WINUSERAPI LRESULT WINAPI DispatchMessageA(CONST MSG *lpMsg);
-  WINUSERAPI LRESULT WINAPI DispatchMessageW(CONST MSG *lpMsg);
-  WINUSERAPI WINBOOL WINAPI SetMessageQueue(int cMessagesMax);
-  WINUSERAPI WINBOOL WINAPI PeekMessageA(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax,UINT wRemoveMsg);
-  WINUSERAPI WINBOOL WINAPI PeekMessageW(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax,UINT wRemoveMsg);
-
-#define PM_NOREMOVE 0x0000
-#define PM_REMOVE 0x0001
-#define PM_NOYIELD 0x0002
-#define PM_QS_INPUT (QS_INPUT << 16)
-#define PM_QS_POSTMESSAGE ((QS_POSTMESSAGE | QS_HOTKEY | QS_TIMER) << 16)
-#define PM_QS_PAINT (QS_PAINT << 16)
-#define PM_QS_SENDMESSAGE (QS_SENDMESSAGE << 16)
-#endif
-
-  WINUSERAPI WINBOOL WINAPI RegisterHotKey(HWND hWnd,int id,UINT fsModifiers,UINT vk);
-  WINUSERAPI WINBOOL WINAPI UnregisterHotKey(HWND hWnd,int id);
-
-#define MOD_ALT 0x0001
-#define MOD_CONTROL 0x0002
-#define MOD_SHIFT 0x0004
-#define MOD_WIN 0x0008
-
-#define IDHOT_SNAPWINDOW (-1)
-#define IDHOT_SNAPDESKTOP (-2)
-
-#ifdef WIN_INTERNAL
-#ifndef LSTRING
-#define NOLSTRING
-#endif
-#ifndef LFILEIO
-#define NOLFILEIO
-#endif
-#endif
-
-#define ENDSESSION_LOGOFF 0x80000000
-
-#define EWX_LOGOFF 0
-#define EWX_SHUTDOWN 0x00000001
-#define EWX_REBOOT 0x00000002
-#define EWX_FORCE 0x00000004
-#define EWX_POWEROFF 0x00000008
-#define EWX_FORCEIFHUNG 0x00000010
-
-#define ExitWindows(dwReserved,Code) ExitWindowsEx(EWX_LOGOFF,0xFFFFFFFF)
-
-#ifdef UNICODE
-#define SendMessage SendMessageW
-#define SendMessageTimeout SendMessageTimeoutW
-#define SendNotifyMessage SendNotifyMessageW
-#define SendMessageCallback SendMessageCallbackW
-#else
-#define SendMessage SendMessageA
-#define SendMessageTimeout SendMessageTimeoutA
-#define SendNotifyMessage SendNotifyMessageA
-#define SendMessageCallback SendMessageCallbackA
-#endif
-
-  WINUSERAPI WINBOOL WINAPI ExitWindowsEx(UINT uFlags,DWORD dwReason);
-  WINUSERAPI WINBOOL WINAPI SwapMouseButton(WINBOOL fSwap);
-  WINUSERAPI DWORD WINAPI GetMessagePos(VOID);
-  WINUSERAPI LONG WINAPI GetMessageTime(VOID);
-  WINUSERAPI LPARAM WINAPI GetMessageExtraInfo(VOID);
-  WINUSERAPI WINBOOL WINAPI IsWow64Message(VOID);
-  WINUSERAPI LPARAM WINAPI SetMessageExtraInfo(LPARAM lParam);
-  WINUSERAPI LRESULT WINAPI SendMessageA(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);
-  WINUSERAPI LRESULT WINAPI SendMessageW(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);
-  WINUSERAPI LRESULT WINAPI SendMessageTimeoutA(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam,UINT fuFlags,UINT uTimeout,PDWORD_PTR lpdwResult);
-  WINUSERAPI LRESULT WINAPI SendMessageTimeoutW(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam,UINT fuFlags,UINT uTimeout,PDWORD_PTR lpdwResult);
-  WINUSERAPI WINBOOL WINAPI SendNotifyMessageA(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);
-  WINUSERAPI WINBOOL WINAPI SendNotifyMessageW(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);
-  WINUSERAPI WINBOOL WINAPI SendMessageCallbackA(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam,SENDASYNCPROC lpResultCallBack,ULONG_PTR dwData);
-  WINUSERAPI WINBOOL WINAPI SendMessageCallbackW(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam,SENDASYNCPROC lpResultCallBack,ULONG_PTR dwData);
-
-  typedef struct {
-    UINT cbSize;
-    HDESK hdesk;
-    HWND hwnd;
-    LUID luid;
-  } BSMINFO,*PBSMINFO;
-
-#ifdef UNICODE
-#define BroadcastSystemMessageEx BroadcastSystemMessageExW
-#define BroadcastSystemMessage BroadcastSystemMessageW
-#else
-#define BroadcastSystemMessageEx BroadcastSystemMessageExA
-#define BroadcastSystemMessage BroadcastSystemMessageA
-#endif
-
-  WINUSERAPI long WINAPI BroadcastSystemMessageExA(DWORD flags,LPDWORD lpInfo,UINT Msg,WPARAM wParam,LPARAM lParam,PBSMINFO pbsmInfo);
-  WINUSERAPI long WINAPI BroadcastSystemMessageExW(DWORD flags,LPDWORD lpInfo,UINT Msg,WPARAM wParam,LPARAM lParam,PBSMINFO pbsmInfo);
-  WINUSERAPI long WINAPI BroadcastSystemMessageA(DWORD flags,LPDWORD lpInfo,UINT Msg,WPARAM wParam,LPARAM lParam);
-  WINUSERAPI long WINAPI BroadcastSystemMessageW(DWORD flags,LPDWORD lpInfo,UINT Msg,WPARAM wParam,LPARAM lParam);
-
-#define BSM_ALLCOMPONENTS 0x00000000
-#define BSM_VXDS 0x00000001
-#define BSM_NETDRIVER 0x00000002
-#define BSM_INSTALLABLEDRIVERS 0x00000004
-#define BSM_APPLICATIONS 0x00000008
-#define BSM_ALLDESKTOPS 0x00000010
-
-#define BSF_QUERY 0x00000001
-#define BSF_IGNORECURRENTTASK 0x00000002
-#define BSF_FLUSHDISK 0x00000004
-#define BSF_NOHANG 0x00000008
-#define BSF_POSTMESSAGE 0x00000010
-#define BSF_FORCEIFHUNG 0x00000020
-#define BSF_NOTIMEOUTIFNOTHUNG 0x00000040
-#define BSF_ALLOWSFW 0x00000080
-#define BSF_SENDNOTIFYMESSAGE 0x00000100
-#define BSF_RETURNHDESK 0x00000200
-#define BSF_LUID 0x00000400
-
-#define BROADCAST_QUERY_DENY 0x424D5144
-
-  typedef PVOID HDEVNOTIFY;
-  typedef HDEVNOTIFY *PHDEVNOTIFY;
-
-#define DEVICE_NOTIFY_WINDOW_HANDLE 0x00000000
-#define DEVICE_NOTIFY_SERVICE_HANDLE 0x00000001
-#define DEVICE_NOTIFY_ALL_INTERFACE_CLASSES 0x00000004
-
-#ifdef UNICODE
-#define RegisterDeviceNotification RegisterDeviceNotificationW
-#define PostMessage PostMessageW
-#define PostThreadMessage PostThreadMessageW
-#define PostAppMessage PostAppMessageW
-#define DefWindowProc DefWindowProcW
-#define CallWindowProc CallWindowProcW
-#define RegisterClass RegisterClassW
-#define UnregisterClass UnregisterClassW
-#define GetClassInfo GetClassInfoW
-#define RegisterClassEx RegisterClassExW
-#define GetClassInfoEx GetClassInfoExW
-#else
-#define RegisterDeviceNotification RegisterDeviceNotificationA
-#define PostMessage PostMessageA
-#define PostThreadMessage PostThreadMessageA
-#define PostAppMessage PostAppMessageA
-#define DefWindowProc DefWindowProcA
-#define CallWindowProc CallWindowProcA
-#define RegisterClass RegisterClassA
-#define UnregisterClass UnregisterClassA
-#define GetClassInfo GetClassInfoA
-#define RegisterClassEx RegisterClassExA
-#define GetClassInfoEx GetClassInfoExA
-#endif
-
-  WINUSERAPI HDEVNOTIFY WINAPI RegisterDeviceNotificationA(HANDLE hRecipient,LPVOID NotificationFilter,DWORD Flags);
-  WINUSERAPI HDEVNOTIFY WINAPI RegisterDeviceNotificationW(HANDLE hRecipient,LPVOID NotificationFilter,DWORD Flags);
-  WINUSERAPI WINBOOL WINAPI UnregisterDeviceNotification(HDEVNOTIFY Handle);
-  WINUSERAPI WINBOOL WINAPI PostMessageA(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);
-  WINUSERAPI WINBOOL WINAPI PostMessageW(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);
-  WINUSERAPI WINBOOL WINAPI PostThreadMessageA(DWORD idThread,UINT Msg,WPARAM wParam,LPARAM lParam);
-  WINUSERAPI WINBOOL WINAPI PostThreadMessageW(DWORD idThread,UINT Msg,WPARAM wParam,LPARAM lParam);
-#define PostAppMessageA(idThread,wMsg,wParam,lParam) PostThreadMessageA((DWORD)idThread,wMsg,wParam,lParam)
-#define PostAppMessageW(idThread,wMsg,wParam,lParam) PostThreadMessageW((DWORD)idThread,wMsg,wParam,lParam)
-
-#define HWND_BROADCAST ((HWND)0xffff)
-#define HWND_MESSAGE ((HWND)-3)
-
-  WINUSERAPI WINBOOL WINAPI AttachThreadInput(DWORD idAttach,DWORD idAttachTo,WINBOOL fAttach);
-  WINUSERAPI WINBOOL WINAPI ReplyMessage(LRESULT lResult);
-  WINUSERAPI WINBOOL WINAPI WaitMessage(VOID);
-  WINUSERAPI DWORD WINAPI WaitForInputIdle(HANDLE hProcess,DWORD dwMilliseconds);
-  WINUSERAPI LRESULT WINAPI DefWindowProcA(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);
-  WINUSERAPI LRESULT WINAPI DefWindowProcW(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);
-  WINUSERAPI VOID WINAPI PostQuitMessage(int nExitCode);
-  WINUSERAPI LRESULT WINAPI CallWindowProcA(WNDPROC lpPrevWndFunc,HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);
-  WINUSERAPI LRESULT WINAPI CallWindowProcW(WNDPROC lpPrevWndFunc,HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);
-  WINUSERAPI WINBOOL WINAPI InSendMessage(VOID);
-  WINUSERAPI DWORD WINAPI InSendMessageEx(LPVOID lpReserved);
-
-#define ISMEX_NOSEND 0x00000000
-#define ISMEX_SEND 0x00000001
-#define ISMEX_NOTIFY 0x00000002
-#define ISMEX_CALLBACK 0x00000004
-#define ISMEX_REPLIED 0x00000008
-
-  WINUSERAPI UINT WINAPI GetDoubleClickTime(VOID);
-  WINUSERAPI WINBOOL WINAPI SetDoubleClickTime(UINT);
-  WINUSERAPI ATOM WINAPI RegisterClassA(CONST WNDCLASSA *lpWndClass);
-  WINUSERAPI ATOM WINAPI RegisterClassW(CONST WNDCLASSW *lpWndClass);
-  WINUSERAPI WINBOOL WINAPI UnregisterClassA(LPCSTR lpClassName,HINSTANCE hInstance);
-  WINUSERAPI WINBOOL WINAPI UnregisterClassW(LPCWSTR lpClassName,HINSTANCE hInstance);
-  WINUSERAPI WINBOOL WINAPI GetClassInfoA(HINSTANCE hInstance,LPCSTR lpClassName,LPWNDCLASSA lpWndClass);
-  WINUSERAPI WINBOOL WINAPI GetClassInfoW(HINSTANCE hInstance,LPCWSTR lpClassName,LPWNDCLASSW lpWndClass);
-  WINUSERAPI ATOM WINAPI RegisterClassExA(CONST WNDCLASSEXA *);
-  WINUSERAPI ATOM WINAPI RegisterClassExW(CONST WNDCLASSEXW *);
-  WINUSERAPI WINBOOL WINAPI GetClassInfoExA(HINSTANCE hInstance,LPCSTR lpszClass,LPWNDCLASSEXA lpwcx);
-  WINUSERAPI WINBOOL WINAPI GetClassInfoExW(HINSTANCE hInstance,LPCWSTR lpszClass,LPWNDCLASSEXW lpwcx);
-
-#define CW_USEDEFAULT ((int)0x80000000)
-
-#define HWND_DESKTOP ((HWND)0)
-
-  typedef BOOLEAN (WINAPI *PREGISTERCLASSNAMEW)(LPCWSTR);
-
-#ifdef UNICODE
-#define CreateWindowEx CreateWindowExW
-#define CreateWindow CreateWindowW
-#else
-#define CreateWindowEx CreateWindowExA
-#define CreateWindow CreateWindowA
-#endif
-
-  WINUSERAPI HWND WINAPI CreateWindowExA(DWORD dwExStyle,LPCSTR lpClassName,LPCSTR lpWindowName,DWORD dwStyle,int X,int Y,int nWidth,int nHeight,HWND hWndParent,HMENU hMenu,HINSTANCE hInstance,LPVOID lpParam);
-  WINUSERAPI HWND WINAPI CreateWindowExW(DWORD dwExStyle,LPCWSTR lpClassName,LPCWSTR lpWindowName,DWORD dwStyle,int X,int Y,int nWidth,int nHeight,HWND hWndParent,HMENU hMenu,HINSTANCE hInstance,LPVOID lpParam);
-#define CreateWindowA(lpClassName,lpWindowName,dwStyle,x,y,nWidth,nHeight,hWndParent,hMenu,hInstance,lpParam) CreateWindowExA(0L,lpClassName,lpWindowName,dwStyle,x,y,nWidth,nHeight,hWndParent,hMenu,hInstance,lpParam)
-#define CreateWindowW(lpClassName,lpWindowName,dwStyle,x,y,nWidth,nHeight,hWndParent,hMenu,hInstance,lpParam) CreateWindowExW(0L,lpClassName,lpWindowName,dwStyle,x,y,nWidth,nHeight,hWndParent,hMenu,hInstance,lpParam)
-  WINUSERAPI WINBOOL WINAPI IsWindow(HWND hWnd);
-  WINUSERAPI WINBOOL WINAPI IsMenu(HMENU hMenu);
-  WINUSERAPI WINBOOL WINAPI IsChild(HWND hWndParent,HWND hWnd);
-  WINUSERAPI WINBOOL WINAPI DestroyWindow(HWND hWnd);
-  WINUSERAPI WINBOOL WINAPI ShowWindow(HWND hWnd,int nCmdShow);
-  WINUSERAPI WINBOOL WINAPI AnimateWindow(HWND hWnd,DWORD dwTime,DWORD dwFlags);
-
-#if defined(_WINGDI_) && !defined(NOGDI)
-  WINUSERAPI WINBOOL WINAPI UpdateLayeredWindow(HWND hWnd,HDC hdcDst,POINT *pptDst,SIZE *psize,HDC hdcSrc,POINT *pptSrc,COLORREF crKey,BLENDFUNCTION *pblend,DWORD dwFlags);
-
-  typedef struct tagUPDATELAYEREDWINDOWINFO {
-    DWORD cbSize;
-    HDC hdcDst;
-    POINT CONST *pptDst;
-    SIZE CONST *psize;
-    HDC hdcSrc;
-    POINT CONST *pptSrc;
-    COLORREF crKey;
-    BLENDFUNCTION CONST *pblend;
-    DWORD dwFlags;
-    RECT CONST *prcDirty;
-  } UPDATELAYEREDWINDOWINFO,*PUPDATELAYEREDWINDOWINFO;
-
-  WINUSERAPI WINBOOL WINAPI UpdateLayeredWindowIndirect(HWND hWnd,UPDATELAYEREDWINDOWINFO CONST *pULWInfo);
-  WINUSERAPI WINBOOL WINAPI GetLayeredWindowAttributes(HWND hwnd,COLORREF *pcrKey,BYTE *pbAlpha,DWORD *pdwFlags);
-
-#define PW_CLIENTONLY 0x00000001
-
-  WINUSERAPI WINBOOL WINAPI PrintWindow(HWND hwnd,HDC hdcBlt,UINT nFlags);
-  WINUSERAPI WINBOOL WINAPI SetLayeredWindowAttributes(HWND hwnd,COLORREF crKey,BYTE bAlpha,DWORD dwFlags);
-
-#define LWA_COLORKEY 0x00000001
-#define LWA_ALPHA 0x00000002
-
-#define ULW_COLORKEY 0x00000001
-#define ULW_ALPHA 0x00000002
-#define ULW_OPAQUE 0x00000004
-
-#define ULW_EX_NORESIZE 0x00000008
-
-  WINUSERAPI WINBOOL WINAPI ShowWindowAsync(HWND hWnd,int nCmdShow);
-  WINUSERAPI WINBOOL WINAPI FlashWindow(HWND hWnd,WINBOOL bInvert);
-
-  typedef struct {
-    UINT cbSize;
-    HWND hwnd;
-    DWORD dwFlags;
-    UINT uCount;
-    DWORD dwTimeout;
-  } FLASHWINFO,*PFLASHWINFO;
-
-  WINUSERAPI WINBOOL WINAPI FlashWindowEx(PFLASHWINFO pfwi);
-
-#define FLASHW_STOP 0
-#define FLASHW_CAPTION 0x00000001
-#define FLASHW_TRAY 0x00000002
-#define FLASHW_ALL (FLASHW_CAPTION | FLASHW_TRAY)
-#define FLASHW_TIMER 0x00000004
-#define FLASHW_TIMERNOFG 0x0000000C
-
-  WINUSERAPI WINBOOL WINAPI ShowOwnedPopups(HWND hWnd,WINBOOL fShow);
-  WINUSERAPI WINBOOL WINAPI OpenIcon(HWND hWnd);
-  WINUSERAPI WINBOOL WINAPI CloseWindow(HWND hWnd);
-  WINUSERAPI WINBOOL WINAPI MoveWindow(HWND hWnd,int X,int Y,int nWidth,int nHeight,WINBOOL bRepaint);
-  WINUSERAPI WINBOOL WINAPI SetWindowPos(HWND hWnd,HWND hWndInsertAfter,int X,int Y,int cx,int cy,UINT uFlags);
-  WINUSERAPI WINBOOL WINAPI GetWindowPlacement(HWND hWnd,WINDOWPLACEMENT *lpwndpl);
-  WINUSERAPI WINBOOL WINAPI SetWindowPlacement(HWND hWnd,CONST WINDOWPLACEMENT *lpwndpl);
-
-#ifndef NODEFERWINDOWPOS
-  WINUSERAPI HDWP WINAPI BeginDeferWindowPos(int nNumWindows);
-  WINUSERAPI HDWP WINAPI DeferWindowPos(HDWP hWinPosInfo,HWND hWnd,HWND hWndInsertAfter,int x,int y,int cx,int cy,UINT uFlags);
-  WINUSERAPI WINBOOL WINAPI EndDeferWindowPos(HDWP hWinPosInfo);
-#endif
-
-  WINUSERAPI WINBOOL WINAPI IsWindowVisible(HWND hWnd);
-  WINUSERAPI WINBOOL WINAPI IsIconic(HWND hWnd);
-  WINUSERAPI WINBOOL WINAPI AnyPopup(VOID);
-  WINUSERAPI WINBOOL WINAPI BringWindowToTop(HWND hWnd);
-  WINUSERAPI WINBOOL WINAPI IsZoomed(HWND hWnd);
-
-#define SWP_NOSIZE 0x0001
-#define SWP_NOMOVE 0x0002
-#define SWP_NOZORDER 0x0004
-#define SWP_NOREDRAW 0x0008
-#define SWP_NOACTIVATE 0x0010
-#define SWP_FRAMECHANGED 0x0020
-#define SWP_SHOWWINDOW 0x0040
-#define SWP_HIDEWINDOW 0x0080
-#define SWP_NOCOPYBITS 0x0100
-#define SWP_NOOWNERZORDER 0x0200
-#define SWP_NOSENDCHANGING 0x0400
-
-#define SWP_DRAWFRAME SWP_FRAMECHANGED
-#define SWP_NOREPOSITION SWP_NOOWNERZORDER
-#define SWP_DEFERERASE 0x2000
-#define SWP_ASYNCWINDOWPOS 0x4000
-
-#define HWND_TOP ((HWND)0)
-#define HWND_BOTTOM ((HWND)1)
-#define HWND_TOPMOST ((HWND)-1)
-#define HWND_NOTOPMOST ((HWND)-2)
-
-#ifndef NOCTLMGR
-
-#include <pshpack2.h>
-
-  typedef struct {
-    DWORD style;
-    DWORD dwExtendedStyle;
-    WORD cdit;
-    short x;
-    short y;
-    short cx;
-    short cy;
-  } DLGTEMPLATE;
-
-  typedef DLGTEMPLATE *LPDLGTEMPLATEA;
-  typedef DLGTEMPLATE *LPDLGTEMPLATEW;
-
-#ifdef UNICODE
-  typedef LPDLGTEMPLATEW LPDLGTEMPLATE;
-#else
-  typedef LPDLGTEMPLATEA LPDLGTEMPLATE;
-#endif
-
-  typedef CONST DLGTEMPLATE *LPCDLGTEMPLATEA;
-  typedef CONST DLGTEMPLATE *LPCDLGTEMPLATEW;
-
-#ifdef UNICODE
-  typedef LPCDLGTEMPLATEW LPCDLGTEMPLATE;
-#else
-  typedef LPCDLGTEMPLATEA LPCDLGTEMPLATE;
-#endif
-
-  typedef struct {
-    DWORD style;
-    DWORD dwExtendedStyle;
-    short x;
-    short y;
-    short cx;
-    short cy;
-    WORD id;
-  } DLGITEMTEMPLATE;
-
-  typedef DLGITEMTEMPLATE *PDLGITEMTEMPLATEA;
-  typedef DLGITEMTEMPLATE *PDLGITEMTEMPLATEW;
-
-#ifdef UNICODE
-  typedef PDLGITEMTEMPLATEW PDLGITEMTEMPLATE;
-#else
-  typedef PDLGITEMTEMPLATEA PDLGITEMTEMPLATE;
-#endif
-
-  typedef DLGITEMTEMPLATE *LPDLGITEMTEMPLATEA;
-  typedef DLGITEMTEMPLATE *LPDLGITEMTEMPLATEW;
-
-#ifdef UNICODE
-  typedef LPDLGITEMTEMPLATEW LPDLGITEMTEMPLATE;
-#else
-  typedef LPDLGITEMTEMPLATEA LPDLGITEMTEMPLATE;
-#endif
-
-#include <poppack.h>
-
-#ifdef UNICODE
-#define CreateDialogParam CreateDialogParamW
-#define CreateDialogIndirectParam CreateDialogIndirectParamW
-#define CreateDialog CreateDialogW
-#define CreateDialogIndirect CreateDialogIndirectW
-#define DialogBoxParam DialogBoxParamW
-#define DialogBoxIndirectParam DialogBoxIndirectParamW
-#define DialogBox DialogBoxW
-#define DialogBoxIndirect DialogBoxIndirectW
-#define SetDlgItemText SetDlgItemTextW
-#define GetDlgItemText GetDlgItemTextW
-#define SendDlgItemMessage SendDlgItemMessageW
-#define DefDlgProc DefDlgProcW
-#else
-#define CreateDialogParam CreateDialogParamA
-#define CreateDialogIndirectParam CreateDialogIndirectParamA
-#define CreateDialog CreateDialogA
-#define CreateDialogIndirect CreateDialogIndirectA
-#define DialogBoxParam DialogBoxParamA
-#define DialogBoxIndirectParam DialogBoxIndirectParamA
-#define DialogBox DialogBoxA
-#define DialogBoxIndirect DialogBoxIndirectA
-#define SetDlgItemText SetDlgItemTextA
-#define GetDlgItemText GetDlgItemTextA
-#define SendDlgItemMessage SendDlgItemMessageA
-#define DefDlgProc DefDlgProcA
-#endif
-
-  WINUSERAPI HWND WINAPI CreateDialogParamA(HINSTANCE hInstance,LPCSTR lpTemplateName,HWND hWndParent,DLGPROC lpDialogFunc,LPARAM dwInitParam);
-  WINUSERAPI HWND WINAPI CreateDialogParamW(HINSTANCE hInstance,LPCWSTR lpTemplateName,HWND hWndParent,DLGPROC lpDialogFunc,LPARAM dwInitParam);
-  WINUSERAPI HWND WINAPI CreateDialogIndirectParamA(HINSTANCE hInstance,LPCDLGTEMPLATEA lpTemplate,HWND hWndParent,DLGPROC lpDialogFunc,LPARAM dwInitParam);
-  WINUSERAPI HWND WINAPI CreateDialogIndirectParamW(HINSTANCE hInstance,LPCDLGTEMPLATEW lpTemplate,HWND hWndParent,DLGPROC lpDialogFunc,LPARAM dwInitParam);
-#define CreateDialogA(hInstance,lpName,hWndParent,lpDialogFunc) CreateDialogParamA(hInstance,lpName,hWndParent,lpDialogFunc,0L)
-#define CreateDialogW(hInstance,lpName,hWndParent,lpDialogFunc) CreateDialogParamW(hInstance,lpName,hWndParent,lpDialogFunc,0L)
-#define CreateDialogIndirectA(hInstance,lpTemplate,hWndParent,lpDialogFunc) CreateDialogIndirectParamA(hInstance,lpTemplate,hWndParent,lpDialogFunc,0L)
-#define CreateDialogIndirectW(hInstance,lpTemplate,hWndParent,lpDialogFunc) CreateDialogIndirectParamW(hInstance,lpTemplate,hWndParent,lpDialogFunc,0L)
-  WINUSERAPI INT_PTR WINAPI DialogBoxParamA(HINSTANCE hInstance,LPCSTR lpTemplateName,HWND hWndParent,DLGPROC lpDialogFunc,LPARAM dwInitParam);
-  WINUSERAPI INT_PTR WINAPI DialogBoxParamW(HINSTANCE hInstance,LPCWSTR lpTemplateName,HWND hWndParent,DLGPROC lpDialogFunc,LPARAM dwInitParam);
-  WINUSERAPI INT_PTR WINAPI DialogBoxIndirectParamA(HINSTANCE hInstance,LPCDLGTEMPLATEA hDialogTemplate,HWND hWndParent,DLGPROC lpDialogFunc,LPARAM dwInitParam);
-  WINUSERAPI INT_PTR WINAPI DialogBoxIndirectParamW(HINSTANCE hInstance,LPCDLGTEMPLATEW hDialogTemplate,HWND hWndParent,DLGPROC lpDialogFunc,LPARAM dwInitParam);
-#define DialogBoxA(hInstance,lpTemplate,hWndParent,lpDialogFunc) DialogBoxParamA(hInstance,lpTemplate,hWndParent,lpDialogFunc,0L)
-#define DialogBoxW(hInstance,lpTemplate,hWndParent,lpDialogFunc) DialogBoxParamW(hInstance,lpTemplate,hWndParent,lpDialogFunc,0L)
-#define DialogBoxIndirectA(hInstance,lpTemplate,hWndParent,lpDialogFunc) DialogBoxIndirectParamA(hInstance,lpTemplate,hWndParent,lpDialogFunc,0L)
-#define DialogBoxIndirectW(hInstance,lpTemplate,hWndParent,lpDialogFunc) DialogBoxIndirectParamW(hInstance,lpTemplate,hWndParent,lpDialogFunc,0L)
-  WINUSERAPI WINBOOL WINAPI EndDialog(HWND hDlg,INT_PTR nResult);
-  WINUSERAPI HWND WINAPI GetDlgItem(HWND hDlg,int nIDDlgItem);
-  WINUSERAPI WINBOOL WINAPI SetDlgItemInt(HWND hDlg,int nIDDlgItem,UINT uValue,WINBOOL bSigned);
-  WINUSERAPI UINT WINAPI GetDlgItemInt(HWND hDlg,int nIDDlgItem,WINBOOL *lpTranslated,WINBOOL bSigned);
-  WINUSERAPI WINBOOL WINAPI SetDlgItemTextA(HWND hDlg,int nIDDlgItem,LPCSTR lpString);
-  WINUSERAPI WINBOOL WINAPI SetDlgItemTextW(HWND hDlg,int nIDDlgItem,LPCWSTR lpString);
-  WINUSERAPI UINT WINAPI GetDlgItemTextA(HWND hDlg,int nIDDlgItem,LPSTR lpString,int cchMax);
-  WINUSERAPI UINT WINAPI GetDlgItemTextW(HWND hDlg,int nIDDlgItem,LPWSTR lpString,int cchMax);
-  WINUSERAPI WINBOOL WINAPI CheckDlgButton(HWND hDlg,int nIDButton,UINT uCheck);
-  WINUSERAPI WINBOOL WINAPI CheckRadioButton(HWND hDlg,int nIDFirstButton,int nIDLastButton,int nIDCheckButton);
-  WINUSERAPI UINT WINAPI IsDlgButtonChecked(HWND hDlg,int nIDButton);
-  WINUSERAPI LRESULT WINAPI SendDlgItemMessageA(HWND hDlg,int nIDDlgItem,UINT Msg,WPARAM wParam,LPARAM lParam);
-  WINUSERAPI LRESULT WINAPI SendDlgItemMessageW(HWND hDlg,int nIDDlgItem,UINT Msg,WPARAM wParam,LPARAM lParam);
-  WINUSERAPI HWND WINAPI GetNextDlgGroupItem(HWND hDlg,HWND hCtl,WINBOOL bPrevious);
-  WINUSERAPI HWND WINAPI GetNextDlgTabItem(HWND hDlg,HWND hCtl,WINBOOL bPrevious);
-  WINUSERAPI int WINAPI GetDlgCtrlID(HWND hWnd);
-  WINUSERAPI long WINAPI GetDialogBaseUnits(VOID);
-  WINUSERAPI LRESULT WINAPI DefDlgProcA(HWND hDlg,UINT Msg,WPARAM wParam,LPARAM lParam);
-  WINUSERAPI LRESULT WINAPI DefDlgProcW(HWND hDlg,UINT Msg,WPARAM wParam,LPARAM lParam);
-
-#define DLGWINDOWEXTRA 30
-#endif
-
-#ifndef NOMSG
-
-#ifdef UNICODE
-#define CallMsgFilter CallMsgFilterW
-#else
-#define CallMsgFilter CallMsgFilterA
-#endif
-
-  WINUSERAPI WINBOOL WINAPI CallMsgFilterA(LPMSG lpMsg,int nCode);
-  WINUSERAPI WINBOOL WINAPI CallMsgFilterW(LPMSG lpMsg,int nCode);
-#endif
-
-#ifndef NOCLIPBOARD
-
-#ifdef UNICODE
-#define RegisterClipboardFormat RegisterClipboardFormatW
-#define GetClipboardFormatName GetClipboardFormatNameW
-#else
-#define RegisterClipboardFormat RegisterClipboardFormatA
-#define GetClipboardFormatName GetClipboardFormatNameA
-#endif
-
-  WINUSERAPI WINBOOL WINAPI OpenClipboard(HWND hWndNewOwner);
-  WINUSERAPI WINBOOL WINAPI CloseClipboard(VOID);
-  WINUSERAPI DWORD WINAPI GetClipboardSequenceNumber(VOID);
-  WINUSERAPI HWND WINAPI GetClipboardOwner(VOID);
-  WINUSERAPI HWND WINAPI SetClipboardViewer(HWND hWndNewViewer);
-  WINUSERAPI HWND WINAPI GetClipboardViewer(VOID);
-  WINUSERAPI WINBOOL WINAPI ChangeClipboardChain(HWND hWndRemove,HWND hWndNewNext);
-  WINUSERAPI HANDLE WINAPI SetClipboardData(UINT uFormat,HANDLE hMem);
-  WINUSERAPI HANDLE WINAPI GetClipboardData(UINT uFormat);
-  WINUSERAPI UINT WINAPI RegisterClipboardFormatA(LPCSTR lpszFormat);
-  WINUSERAPI UINT WINAPI RegisterClipboardFormatW(LPCWSTR lpszFormat);
-  WINUSERAPI int WINAPI CountClipboardFormats(VOID);
-  WINUSERAPI UINT WINAPI EnumClipboardFormats(UINT format);
-  WINUSERAPI int WINAPI GetClipboardFormatNameA(UINT format,LPSTR lpszFormatName,int cchMaxCount);
-  WINUSERAPI int WINAPI GetClipboardFormatNameW(UINT format,LPWSTR lpszFormatName,int cchMaxCount);
-  WINUSERAPI WINBOOL WINAPI EmptyClipboard(VOID);
-  WINUSERAPI WINBOOL WINAPI IsClipboardFormatAvailable(UINT format);
-  WINUSERAPI int WINAPI GetPriorityClipboardFormat(UINT *paFormatPriorityList,int cFormats);
-  WINUSERAPI HWND WINAPI GetOpenClipboardWindow(VOID);
-#endif
-
-#ifdef UNICODE
-#define CharToOem CharToOemW
-#define OemToChar OemToCharW
-#define CharToOemBuff CharToOemBuffW
-#define OemToCharBuff OemToCharBuffW
-#define CharUpper CharUpperW
-#define CharUpperBuff CharUpperBuffW
-#define CharLower CharLowerW
-#define CharLowerBuff CharLowerBuffW
-#define CharNext CharNextW
-#define CharPrev CharPrevW
-#else
-#define CharToOem CharToOemA
-#define OemToChar OemToCharA
-#define CharToOemBuff CharToOemBuffA
-#define OemToCharBuff OemToCharBuffA
-#define CharUpper CharUpperA
-#define CharUpperBuff CharUpperBuffA
-#define CharLower CharLowerA
-#define CharLowerBuff CharLowerBuffA
-#define CharNext CharNextA
-#define CharPrev CharPrevA
-#endif
-
-  WINUSERAPI WINBOOL WINAPI CharToOemA(LPCSTR lpszSrc,LPSTR lpszDst);
-  WINUSERAPI WINBOOL WINAPI CharToOemW(LPCWSTR lpszSrc,LPSTR lpszDst);
-  WINUSERAPI WINBOOL WINAPI OemToCharA(LPCSTR lpszSrc,LPSTR lpszDst);
-  WINUSERAPI WINBOOL WINAPI OemToCharW(LPCSTR lpszSrc,LPWSTR lpszDst);
-  WINUSERAPI WINBOOL WINAPI CharToOemBuffA(LPCSTR lpszSrc,LPSTR lpszDst,DWORD cchDstLength);
-  WINUSERAPI WINBOOL WINAPI CharToOemBuffW(LPCWSTR lpszSrc,LPSTR lpszDst,DWORD cchDstLength);
-  WINUSERAPI WINBOOL WINAPI OemToCharBuffA(LPCSTR lpszSrc,LPSTR lpszDst,DWORD cchDstLength);
-  WINUSERAPI WINBOOL WINAPI OemToCharBuffW(LPCSTR lpszSrc,LPWSTR lpszDst,DWORD cchDstLength);
-  WINUSERAPI LPSTR WINAPI CharUpperA(LPSTR lpsz);
-  WINUSERAPI LPWSTR WINAPI CharUpperW(LPWSTR lpsz);
-  WINUSERAPI DWORD WINAPI CharUpperBuffA(LPSTR lpsz,DWORD cchLength);
-  WINUSERAPI DWORD WINAPI CharUpperBuffW(LPWSTR lpsz,DWORD cchLength);
-  WINUSERAPI LPSTR WINAPI CharLowerA(LPSTR lpsz);
-  WINUSERAPI LPWSTR WINAPI CharLowerW(LPWSTR lpsz);
-  WINUSERAPI DWORD WINAPI CharLowerBuffA(LPSTR lpsz,DWORD cchLength);
-  WINUSERAPI DWORD WINAPI CharLowerBuffW(LPWSTR lpsz,DWORD cchLength);
-  WINUSERAPI LPSTR WINAPI CharNextA(LPCSTR lpsz);
-  WINUSERAPI LPWSTR WINAPI CharNextW(LPCWSTR lpsz);
-  WINUSERAPI LPSTR WINAPI CharPrevA(LPCSTR lpszStart,LPCSTR lpszCurrent);
-  WINUSERAPI LPWSTR WINAPI CharPrevW(LPCWSTR lpszStart,LPCWSTR lpszCurrent);
-  WINUSERAPI LPSTR WINAPI CharNextExA(WORD CodePage,LPCSTR lpCurrentChar,DWORD dwFlags);
-  WINUSERAPI LPSTR WINAPI CharPrevExA(WORD CodePage,LPCSTR lpStart,LPCSTR lpCurrentChar,DWORD dwFlags);
-
-#define AnsiToOem CharToOemA
-#define OemToAnsi OemToCharA
-#define AnsiToOemBuff CharToOemBuffA
-#define OemToAnsiBuff OemToCharBuffA
-#define AnsiUpper CharUpperA
-#define AnsiUpperBuff CharUpperBuffA
-#define AnsiLower CharLowerA
-#define AnsiLowerBuff CharLowerBuffA
-#define AnsiNext CharNextA
-#define AnsiPrev CharPrevA
-
-#ifndef NOLANGUAGE
-
-#ifdef UNICODE
-#define IsCharAlpha IsCharAlphaW
-#define IsCharAlphaNumeric IsCharAlphaNumericW
-#define IsCharUpper IsCharUpperW
-#define IsCharLower IsCharLowerW
-#else
-#define IsCharAlpha IsCharAlphaA
-#define IsCharAlphaNumeric IsCharAlphaNumericA
-#define IsCharUpper IsCharUpperA
-#define IsCharLower IsCharLowerA
-#endif
-
-  WINUSERAPI WINBOOL WINAPI IsCharAlphaA(CHAR ch);
-  WINUSERAPI WINBOOL WINAPI IsCharAlphaW(WCHAR ch);
-  WINUSERAPI WINBOOL WINAPI IsCharAlphaNumericA(CHAR ch);
-  WINUSERAPI WINBOOL WINAPI IsCharAlphaNumericW(WCHAR ch);
-  WINUSERAPI WINBOOL WINAPI IsCharUpperA(CHAR ch);
-  WINUSERAPI WINBOOL WINAPI IsCharUpperW(WCHAR ch);
-  WINUSERAPI WINBOOL WINAPI IsCharLowerA(CHAR ch);
-  WINUSERAPI WINBOOL WINAPI IsCharLowerW(WCHAR ch);
-#endif
-
-#ifdef UNICODE
-#define GetKeyNameText GetKeyNameTextW
-#define VkKeyScan VkKeyScanW
-#define VkKeyScanEx VkKeyScanExW
-#else
-#define GetKeyNameText GetKeyNameTextA
-#define VkKeyScan VkKeyScanA
-#define VkKeyScanEx VkKeyScanExA
-#endif
-
-  WINUSERAPI HWND WINAPI SetFocus(HWND hWnd);
-  WINUSERAPI HWND WINAPI GetActiveWindow(VOID);
-  WINUSERAPI HWND WINAPI GetFocus(VOID);
-  WINUSERAPI UINT WINAPI GetKBCodePage(VOID);
-  WINUSERAPI SHORT WINAPI GetKeyState(int nVirtKey);
-  WINUSERAPI SHORT WINAPI GetAsyncKeyState(int vKey);
-  WINUSERAPI WINBOOL WINAPI GetKeyboardState(PBYTE lpKeyState);
-  WINUSERAPI WINBOOL WINAPI SetKeyboardState(LPBYTE lpKeyState);
-  WINUSERAPI int WINAPI GetKeyNameTextA(LONG lParam,LPSTR lpString,int cchSize);
-  WINUSERAPI int WINAPI GetKeyNameTextW(LONG lParam,LPWSTR lpString,int cchSize);
-  WINUSERAPI int WINAPI GetKeyboardType(int nTypeFlag);
-  WINUSERAPI int WINAPI ToAscii(UINT uVirtKey,UINT uScanCode,CONST BYTE *lpKeyState,LPWORD lpChar,UINT uFlags);
-  WINUSERAPI int WINAPI ToAsciiEx(UINT uVirtKey,UINT uScanCode,CONST BYTE *lpKeyState,LPWORD lpChar,UINT uFlags,HKL dwhkl);
-  WINUSERAPI int WINAPI ToUnicode(UINT wVirtKey,UINT wScanCode,CONST BYTE *lpKeyState,LPWSTR pwszBuff,int cchBuff,UINT wFlags);
-  WINUSERAPI DWORD WINAPI OemKeyScan(WORD wOemChar);
-  WINUSERAPI SHORT WINAPI VkKeyScanA(CHAR ch);
-  WINUSERAPI SHORT WINAPI VkKeyScanW(WCHAR ch);
-  WINUSERAPI SHORT WINAPI VkKeyScanExA(CHAR ch,HKL dwhkl);
-  WINUSERAPI SHORT WINAPI VkKeyScanExW(WCHAR ch,HKL dwhkl);
-
-#define KEYEVENTF_EXTENDEDKEY 0x0001
-#define KEYEVENTF_KEYUP 0x0002
-#define KEYEVENTF_UNICODE 0x0004
-#define KEYEVENTF_SCANCODE 0x0008
-
-  WINUSERAPI VOID WINAPI keybd_event(BYTE bVk,BYTE bScan,DWORD dwFlags,ULONG_PTR dwExtraInfo);
-
-#define MOUSEEVENTF_MOVE 0x0001
-#define MOUSEEVENTF_LEFTDOWN 0x0002
-#define MOUSEEVENTF_LEFTUP 0x0004
-#define MOUSEEVENTF_RIGHTDOWN 0x0008
-#define MOUSEEVENTF_RIGHTUP 0x0010
-#define MOUSEEVENTF_MIDDLEDOWN 0x0020
-#define MOUSEEVENTF_MIDDLEUP 0x0040
-#define MOUSEEVENTF_XDOWN 0x0080
-#define MOUSEEVENTF_XUP 0x0100
-#define MOUSEEVENTF_WHEEL 0x0800
-#define MOUSEEVENTF_VIRTUALDESK 0x4000
-#define MOUSEEVENTF_ABSOLUTE 0x8000
-
-  WINUSERAPI VOID WINAPI mouse_event(DWORD dwFlags,DWORD dx,DWORD dy,DWORD dwData,ULONG_PTR dwExtraInfo);
-
-  typedef struct tagMOUSEINPUT {
-    LONG dx;
-    LONG dy;
-    DWORD mouseData;
-    DWORD dwFlags;
-    DWORD time;
-    ULONG_PTR dwExtraInfo;
-  } MOUSEINPUT,*PMOUSEINPUT,*LPMOUSEINPUT;
-
-  typedef struct tagKEYBDINPUT {
-    WORD wVk;
-    WORD wScan;
-    DWORD dwFlags;
-    DWORD time;
-    ULONG_PTR dwExtraInfo;
-  } KEYBDINPUT,*PKEYBDINPUT,*LPKEYBDINPUT;
-
-  typedef struct tagHARDWAREINPUT {
-    DWORD uMsg;
-    WORD wParamL;
-    WORD wParamH;
-  } HARDWAREINPUT,*PHARDWAREINPUT,*LPHARDWAREINPUT;
-
-#define INPUT_MOUSE 0
-#define INPUT_KEYBOARD 1
-#define INPUT_HARDWARE 2
-
-  typedef struct tagINPUT {
-    DWORD type;
-    union {
-      MOUSEINPUT mi;
-      KEYBDINPUT ki;
-      HARDWAREINPUT hi;
-    };
-  } INPUT,*PINPUT,*LPINPUT;
-
-  WINUSERAPI UINT WINAPI SendInput(UINT cInputs,LPINPUT pInputs,int cbSize);
-
-  typedef struct tagLASTINPUTINFO {
-    UINT cbSize;
-    DWORD dwTime;
-  } LASTINPUTINFO,*PLASTINPUTINFO;
-
-#ifdef UNICODE
-#define MapVirtualKey MapVirtualKeyW
-#define MapVirtualKeyEx MapVirtualKeyExW
-#else
-#define MapVirtualKey MapVirtualKeyA
-#define MapVirtualKeyEx MapVirtualKeyExA
-#endif
-
-  WINUSERAPI WINBOOL WINAPI GetLastInputInfo(PLASTINPUTINFO plii);
-  WINUSERAPI UINT WINAPI MapVirtualKeyA(UINT uCode,UINT uMapType);
-  WINUSERAPI UINT WINAPI MapVirtualKeyW(UINT uCode,UINT uMapType);
-  WINUSERAPI UINT WINAPI MapVirtualKeyExA(UINT uCode,UINT uMapType,HKL dwhkl);
-  WINUSERAPI UINT WINAPI MapVirtualKeyExW(UINT uCode,UINT uMapType,HKL dwhkl);
-  WINUSERAPI WINBOOL WINAPI GetInputState(VOID);
-  WINUSERAPI DWORD WINAPI GetQueueStatus(UINT flags);
-  WINUSERAPI HWND WINAPI GetCapture(VOID);
-  WINUSERAPI HWND WINAPI SetCapture(HWND hWnd);
-  WINUSERAPI WINBOOL WINAPI ReleaseCapture(VOID);
-  WINUSERAPI DWORD WINAPI MsgWaitForMultipleObjects(DWORD nCount,CONST HANDLE *pHandles,WINBOOL fWaitAll,DWORD dwMilliseconds,DWORD dwWakeMask);
-  WINUSERAPI DWORD WINAPI MsgWaitForMultipleObjectsEx(DWORD nCount,CONST HANDLE *pHandles,DWORD dwMilliseconds,DWORD dwWakeMask,DWORD dwFlags);
-
-#define MWMO_WAITALL 0x0001
-#define MWMO_ALERTABLE 0x0002
-#define MWMO_INPUTAVAILABLE 0x0004
-
-#define QS_KEY 0x0001
-#define QS_MOUSEMOVE 0x0002
-#define QS_MOUSEBUTTON 0x0004
-#define QS_POSTMESSAGE 0x0008
-#define QS_TIMER 0x0010
-#define QS_PAINT 0x0020
-#define QS_SENDMESSAGE 0x0040
-#define QS_HOTKEY 0x0080
-#define QS_ALLPOSTMESSAGE 0x0100
-#define QS_RAWINPUT 0x0400
-#define QS_MOUSE (QS_MOUSEMOVE | QS_MOUSEBUTTON)
-#define QS_INPUT (QS_MOUSE | QS_KEY | QS_RAWINPUT)
-#define QS_ALLEVENTS (QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY)
-#define QS_ALLINPUT (QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY | QS_SENDMESSAGE)
-
-#define USER_TIMER_MAXIMUM 0x7FFFFFFF
-#define USER_TIMER_MINIMUM 0x0000000A
-
-#ifdef UNICODE
-#define LoadAccelerators LoadAcceleratorsW
-#define CreateAcceleratorTable CreateAcceleratorTableW
-#define CopyAcceleratorTable CopyAcceleratorTableW
-#else
-#define LoadAccelerators LoadAcceleratorsA
-#define CreateAcceleratorTable CreateAcceleratorTableA
-#define CopyAcceleratorTable CopyAcceleratorTableA
-#endif
-
-  WINUSERAPI UINT_PTR WINAPI SetTimer(HWND hWnd,UINT_PTR nIDEvent,UINT uElapse,TIMERPROC lpTimerFunc);
-  WINUSERAPI WINBOOL WINAPI KillTimer(HWND hWnd,UINT_PTR uIDEvent);
-  WINUSERAPI WINBOOL WINAPI IsWindowUnicode(HWND hWnd);
-  WINUSERAPI WINBOOL WINAPI EnableWindow(HWND hWnd,WINBOOL bEnable);
-  WINUSERAPI WINBOOL WINAPI IsWindowEnabled(HWND hWnd);
-  WINUSERAPI HACCEL WINAPI LoadAcceleratorsA(HINSTANCE hInstance,LPCSTR lpTableName);
-  WINUSERAPI HACCEL WINAPI LoadAcceleratorsW(HINSTANCE hInstance,LPCWSTR lpTableName);
-  WINUSERAPI HACCEL WINAPI CreateAcceleratorTableA(LPACCEL paccel,int cAccel);
-  WINUSERAPI HACCEL WINAPI CreateAcceleratorTableW(LPACCEL paccel,int cAccel);
-  WINUSERAPI WINBOOL WINAPI DestroyAcceleratorTable(HACCEL hAccel);
-  WINUSERAPI int WINAPI CopyAcceleratorTableA(HACCEL hAccelSrc,LPACCEL lpAccelDst,int cAccelEntries);
-  WINUSERAPI int WINAPI CopyAcceleratorTableW(HACCEL hAccelSrc,LPACCEL lpAccelDst,int cAccelEntries);
-
-#ifndef NOMSG
-
-#ifdef UNICODE
-#define TranslateAccelerator TranslateAcceleratorW
-#else
-#define TranslateAccelerator TranslateAcceleratorA
-#endif
-
-  WINUSERAPI int WINAPI TranslateAcceleratorA(HWND hWnd,HACCEL hAccTable,LPMSG lpMsg);
-  WINUSERAPI int WINAPI TranslateAcceleratorW(HWND hWnd,HACCEL hAccTable,LPMSG lpMsg);
-#endif
-
-#ifndef NOSYSMETRICS
-
-#define SM_CXSCREEN 0
-#define SM_CYSCREEN 1
-#define SM_CXVSCROLL 2
-#define SM_CYHSCROLL 3
-#define SM_CYCAPTION 4
-#define SM_CXBORDER 5
-#define SM_CYBORDER 6
-#define SM_CXDLGFRAME 7
-#define SM_CYDLGFRAME 8
-#define SM_CYVTHUMB 9
-#define SM_CXHTHUMB 10
-#define SM_CXICON 11
-#define SM_CYICON 12
-#define SM_CXCURSOR 13
-#define SM_CYCURSOR 14
-#define SM_CYMENU 15
-#define SM_CXFULLSCREEN 16
-#define SM_CYFULLSCREEN 17
-#define SM_CYKANJIWINDOW 18
-#define SM_MOUSEPRESENT 19
-#define SM_CYVSCROLL 20
-#define SM_CXHSCROLL 21
-#define SM_DEBUG 22
-#define SM_SWAPBUTTON 23
-#define SM_RESERVED1 24
-#define SM_RESERVED2 25
-#define SM_RESERVED3 26
-#define SM_RESERVED4 27
-#define SM_CXMIN 28
-#define SM_CYMIN 29
-#define SM_CXSIZE 30
-#define SM_CYSIZE 31
-#define SM_CXFRAME 32
-#define SM_CYFRAME 33
-#define SM_CXMINTRACK 34
-#define SM_CYMINTRACK 35
-#define SM_CXDOUBLECLK 36
-#define SM_CYDOUBLECLK 37
-#define SM_CXICONSPACING 38
-#define SM_CYICONSPACING 39
-#define SM_MENUDROPALIGNMENT 40
-#define SM_PENWINDOWS 41
-#define SM_DBCSENABLED 42
-#define SM_CMOUSEBUTTONS 43
-
-#define SM_CXFIXEDFRAME SM_CXDLGFRAME
-#define SM_CYFIXEDFRAME SM_CYDLGFRAME
-#define SM_CXSIZEFRAME SM_CXFRAME
-#define SM_CYSIZEFRAME SM_CYFRAME
-
-#define SM_SECURE 44
-#define SM_CXEDGE 45
-#define SM_CYEDGE 46
-#define SM_CXMINSPACING 47
-#define SM_CYMINSPACING 48
-#define SM_CXSMICON 49
-#define SM_CYSMICON 50
-#define SM_CYSMCAPTION 51
-#define SM_CXSMSIZE 52
-#define SM_CYSMSIZE 53
-#define SM_CXMENUSIZE 54
-#define SM_CYMENUSIZE 55
-#define SM_ARRANGE 56
-#define SM_CXMINIMIZED 57
-#define SM_CYMINIMIZED 58
-#define SM_CXMAXTRACK 59
-#define SM_CYMAXTRACK 60
-#define SM_CXMAXIMIZED 61
-#define SM_CYMAXIMIZED 62
-#define SM_NETWORK 63
-#define SM_CLEANBOOT 67
-#define SM_CXDRAG 68
-#define SM_CYDRAG 69
-#define SM_SHOWSOUNDS 70
-#define SM_CXMENUCHECK 71
-#define SM_CYMENUCHECK 72
-#define SM_SLOWMACHINE 73
-#define SM_MIDEASTENABLED 74
-#define SM_MOUSEWHEELPRESENT 75
-#define SM_XVIRTUALSCREEN 76
-#define SM_YVIRTUALSCREEN 77
-#define SM_CXVIRTUALSCREEN 78
-#define SM_CYVIRTUALSCREEN 79
-#define SM_CMONITORS 80
-#define SM_SAMEDISPLAYFORMAT 81
-#define SM_IMMENABLED 82
-#define SM_CXFOCUSBORDER 83
-#define SM_CYFOCUSBORDER 84
-#define SM_TABLETPC 86
-#define SM_MEDIACENTER 87
-#define SM_STARTER 88
-#define SM_SERVERR2 89
-#define SM_CMETRICS 90
-#define SM_REMOTESESSION 0x1000
-#define SM_SHUTTINGDOWN 0x2000
-#define SM_REMOTECONTROL 0x2001
-#define SM_CARETBLINKINGENABLED 0x2002
-
-  WINUSERAPI int WINAPI GetSystemMetrics(int nIndex);
-#endif
-
-#ifndef NOMENUS
-
-#ifdef UNICODE
-#define LoadMenu LoadMenuW
-#define LoadMenuIndirect LoadMenuIndirectW
-#define ChangeMenu ChangeMenuW
-#define GetMenuString GetMenuStringW
-#define InsertMenu InsertMenuW
-#define AppendMenu AppendMenuW
-#define ModifyMenu ModifyMenuW
-#else
-#define LoadMenu LoadMenuA
-#define LoadMenuIndirect LoadMenuIndirectA
-#define ChangeMenu ChangeMenuA
-#define GetMenuString GetMenuStringA
-#define InsertMenu InsertMenuA
-#define AppendMenu AppendMenuA
-#define ModifyMenu ModifyMenuA
-#endif
-
-  WINUSERAPI HMENU WINAPI LoadMenuA(HINSTANCE hInstance,LPCSTR lpMenuName);
-  WINUSERAPI HMENU WINAPI LoadMenuW(HINSTANCE hInstance,LPCWSTR lpMenuName);
-  WINUSERAPI HMENU WINAPI LoadMenuIndirectA(CONST MENUTEMPLATEA *lpMenuTemplate);
-  WINUSERAPI HMENU WINAPI LoadMenuIndirectW(CONST MENUTEMPLATEW *lpMenuTemplate);
-  WINUSERAPI HMENU WINAPI GetMenu(HWND hWnd);
-  WINUSERAPI WINBOOL WINAPI SetMenu(HWND hWnd,HMENU hMenu);
-  WINUSERAPI WINBOOL WINAPI ChangeMenuA(HMENU hMenu,UINT cmd,LPCSTR lpszNewItem,UINT cmdInsert,UINT flags);
-  WINUSERAPI WINBOOL WINAPI ChangeMenuW(HMENU hMenu,UINT cmd,LPCWSTR lpszNewItem,UINT cmdInsert,UINT flags);
-  WINUSERAPI WINBOOL WINAPI HiliteMenuItem(HWND hWnd,HMENU hMenu,UINT uIDHiliteItem,UINT uHilite);
-  WINUSERAPI int WINAPI GetMenuStringA(HMENU hMenu,UINT uIDItem,LPSTR lpString,int cchMax,UINT flags);
-  WINUSERAPI int WINAPI GetMenuStringW(HMENU hMenu,UINT uIDItem,LPWSTR lpString,int cchMax,UINT flags);
-  WINUSERAPI UINT WINAPI GetMenuState(HMENU hMenu,UINT uId,UINT uFlags);
-  WINUSERAPI WINBOOL WINAPI DrawMenuBar(HWND hWnd);
-
-#define PMB_ACTIVE 0x00000001
-
-  WINUSERAPI HMENU WINAPI GetSystemMenu(HWND hWnd,WINBOOL bRevert);
-  WINUSERAPI HMENU WINAPI CreateMenu(VOID);
-  WINUSERAPI HMENU WINAPI CreatePopupMenu(VOID);
-  WINUSERAPI WINBOOL WINAPI DestroyMenu(HMENU hMenu);
-  WINUSERAPI DWORD WINAPI CheckMenuItem(HMENU hMenu,UINT uIDCheckItem,UINT uCheck);
-  WINUSERAPI WINBOOL WINAPI EnableMenuItem(HMENU hMenu,UINT uIDEnableItem,UINT uEnable);
-  WINUSERAPI HMENU WINAPI GetSubMenu(HMENU hMenu,int nPos);
-  WINUSERAPI UINT WINAPI GetMenuItemID(HMENU hMenu,int nPos);
-  WINUSERAPI int WINAPI GetMenuItemCount(HMENU hMenu);
-  WINUSERAPI WINBOOL WINAPI InsertMenuA(HMENU hMenu,UINT uPosition,UINT uFlags,UINT_PTR uIDNewItem,LPCSTR lpNewItem);
-  WINUSERAPI WINBOOL WINAPI InsertMenuW(HMENU hMenu,UINT uPosition,UINT uFlags,UINT_PTR uIDNewItem,LPCWSTR lpNewItem);
-  WINUSERAPI WINBOOL WINAPI AppendMenuA(HMENU hMenu,UINT uFlags,UINT_PTR uIDNewItem,LPCSTR lpNewItem);
-  WINUSERAPI WINBOOL WINAPI AppendMenuW(HMENU hMenu,UINT uFlags,UINT_PTR uIDNewItem,LPCWSTR lpNewItem);
-  WINUSERAPI WINBOOL WINAPI ModifyMenuA(HMENU hMnu,UINT uPosition,UINT uFlags,UINT_PTR uIDNewItem,LPCSTR lpNewItem);
-  WINUSERAPI WINBOOL WINAPI ModifyMenuW(HMENU hMnu,UINT uPosition,UINT uFlags,UINT_PTR uIDNewItem,LPCWSTR lpNewItem);
-  WINUSERAPI WINBOOL WINAPI RemoveMenu(HMENU hMenu,UINT uPosition,UINT uFlags);
-  WINUSERAPI WINBOOL WINAPI DeleteMenu(HMENU hMenu,UINT uPosition,UINT uFlags);
-  WINUSERAPI WINBOOL WINAPI SetMenuItemBitmaps(HMENU hMenu,UINT uPosition,UINT uFlags,HBITMAP hBitmapUnchecked,HBITMAP hBitmapChecked);
-  WINUSERAPI LONG WINAPI GetMenuCheckMarkDimensions(VOID);
-  WINUSERAPI WINBOOL WINAPI TrackPopupMenu(HMENU hMenu,UINT uFlags,int x,int y,int nReserved,HWND hWnd,CONST RECT *prcRect);
-
-#define MNC_IGNORE 0
-#define MNC_CLOSE 1
-#define MNC_EXECUTE 2
-#define MNC_SELECT 3
-
-  typedef struct tagTPMPARAMS {
-    UINT cbSize;
-    RECT rcExclude;
-  } TPMPARAMS;
-
-  typedef TPMPARAMS *LPTPMPARAMS;
-
-  WINUSERAPI WINBOOL WINAPI TrackPopupMenuEx(HMENU,UINT,int,int,HWND,LPTPMPARAMS);
-
-#define MNS_NOCHECK 0x80000000
-#define MNS_MODELESS 0x40000000
-#define MNS_DRAGDROP 0x20000000
-#define MNS_AUTODISMISS 0x10000000
-#define MNS_NOTIFYBYPOS 0x08000000
-#define MNS_CHECKORBMP 0x04000000
-
-#define MIM_MAXHEIGHT 0x00000001
-#define MIM_BACKGROUND 0x00000002
-#define MIM_HELPID 0x00000004
-#define MIM_MENUDATA 0x00000008
-#define MIM_STYLE 0x00000010
-#define MIM_APPLYTOSUBMENUS 0x80000000
-
-  typedef struct tagMENUINFO {
-    DWORD cbSize;
-    DWORD fMask;
-    DWORD dwStyle;
-    UINT cyMax;
-    HBRUSH hbrBack;
-    DWORD dwContextHelpID;
-    ULONG_PTR dwMenuData;
-  } MENUINFO,*LPMENUINFO;
-
-  typedef MENUINFO CONST *LPCMENUINFO;
-
-  WINUSERAPI WINBOOL WINAPI GetMenuInfo(HMENU,LPMENUINFO);
-  WINUSERAPI WINBOOL WINAPI SetMenuInfo(HMENU,LPCMENUINFO);
-  WINUSERAPI WINBOOL WINAPI EndMenu(VOID);
-
-#define MND_CONTINUE 0
-#define MND_ENDMENU 1
-
-  typedef struct tagMENUGETOBJECTINFO {
-    DWORD dwFlags;
-    UINT uPos;
-    HMENU hmenu;
-    PVOID riid;
-    PVOID pvObj;
-  } MENUGETOBJECTINFO,*PMENUGETOBJECTINFO;
-
-#define MNGOF_TOPGAP 0x00000001
-#define MNGOF_BOTTOMGAP 0x00000002
-
-#define MNGO_NOINTERFACE 0x00000000
-#define MNGO_NOERROR 0x00000001
-
-#define MIIM_STATE 0x00000001
-#define MIIM_ID 0x00000002
-#define MIIM_SUBMENU 0x00000004
-#define MIIM_CHECKMARKS 0x00000008
-#define MIIM_TYPE 0x00000010
-#define MIIM_DATA 0x00000020
-
-#define MIIM_STRING 0x00000040
-#define MIIM_BITMAP 0x00000080
-#define MIIM_FTYPE 0x00000100
-
-#define HBMMENU_CALLBACK ((HBITMAP) -1)
-#define HBMMENU_SYSTEM ((HBITMAP) 1)
-#define HBMMENU_MBAR_RESTORE ((HBITMAP) 2)
-#define HBMMENU_MBAR_MINIMIZE ((HBITMAP) 3)
-#define HBMMENU_MBAR_CLOSE ((HBITMAP) 5)
-#define HBMMENU_MBAR_CLOSE_D ((HBITMAP) 6)
-#define HBMMENU_MBAR_MINIMIZE_D ((HBITMAP) 7)
-#define HBMMENU_POPUP_CLOSE ((HBITMAP) 8)
-#define HBMMENU_POPUP_RESTORE ((HBITMAP) 9)
-#define HBMMENU_POPUP_MAXIMIZE ((HBITMAP) 10)
-#define HBMMENU_POPUP_MINIMIZE ((HBITMAP) 11)
-
-  typedef struct tagMENUITEMINFOA {
-    UINT cbSize;
-    UINT fMask;
-    UINT fType;
-    UINT fState;
-    UINT wID;
-    HMENU hSubMenu;
-    HBITMAP hbmpChecked;
-    HBITMAP hbmpUnchecked;
-    ULONG_PTR dwItemData;
-    LPSTR dwTypeData;
-    UINT cch;
-    HBITMAP hbmpItem;
-  } MENUITEMINFOA,*LPMENUITEMINFOA;
-
-  typedef struct tagMENUITEMINFOW {
-    UINT cbSize;
-    UINT fMask;
-    UINT fType;
-    UINT fState;
-    UINT wID;
-    HMENU hSubMenu;
-    HBITMAP hbmpChecked;
-    HBITMAP hbmpUnchecked;
-    ULONG_PTR dwItemData;
-    LPWSTR dwTypeData;
-    UINT cch;
-    HBITMAP hbmpItem;
-  } MENUITEMINFOW,*LPMENUITEMINFOW;
-
-#ifdef UNICODE
-  typedef MENUITEMINFOW MENUITEMINFO;
-  typedef LPMENUITEMINFOW LPMENUITEMINFO;
-#else
-  typedef MENUITEMINFOA MENUITEMINFO;
-  typedef LPMENUITEMINFOA LPMENUITEMINFO;
-#endif
-  typedef MENUITEMINFOA CONST *LPCMENUITEMINFOA;
-  typedef MENUITEMINFOW CONST *LPCMENUITEMINFOW;
-#ifdef UNICODE
-  typedef LPCMENUITEMINFOW LPCMENUITEMINFO;
-#else
-  typedef LPCMENUITEMINFOA LPCMENUITEMINFO;
-#endif
-
-#ifdef UNICODE
-#define InsertMenuItem InsertMenuItemW
-#define GetMenuItemInfo GetMenuItemInfoW
-#define SetMenuItemInfo SetMenuItemInfoW
-#else
-#define InsertMenuItem InsertMenuItemA
-#define GetMenuItemInfo GetMenuItemInfoA
-#define SetMenuItemInfo SetMenuItemInfoA
-#endif
-
-  WINUSERAPI WINBOOL WINAPI InsertMenuItemA(HMENU hmenu,UINT item,WINBOOL fByPosition,LPCMENUITEMINFOA lpmi);
-  WINUSERAPI WINBOOL WINAPI InsertMenuItemW(HMENU hmenu,UINT item,WINBOOL fByPosition,LPCMENUITEMINFOW lpmi);
-  WINUSERAPI WINBOOL WINAPI GetMenuItemInfoA(HMENU hmenu,UINT item,WINBOOL fByPosition,LPMENUITEMINFOA lpmii);
-  WINUSERAPI WINBOOL WINAPI GetMenuItemInfoW(HMENU hmenu,UINT item,WINBOOL fByPosition,LPMENUITEMINFOW lpmii);
-  WINUSERAPI WINBOOL WINAPI SetMenuItemInfoA(HMENU hmenu,UINT item,WINBOOL fByPositon,LPCMENUITEMINFOA lpmii);
-  WINUSERAPI WINBOOL WINAPI SetMenuItemInfoW(HMENU hmenu,UINT item,WINBOOL fByPositon,LPCMENUITEMINFOW lpmii);
-
-#define GMDI_USEDISABLED 0x0001L
-#define GMDI_GOINTOPOPUPS 0x0002L
-
-  WINUSERAPI UINT WINAPI GetMenuDefaultItem(HMENU hMenu,UINT fByPos,UINT gmdiFlags);
-  WINUSERAPI WINBOOL WINAPI SetMenuDefaultItem(HMENU hMenu,UINT uItem,UINT fByPos);
-  WINUSERAPI WINBOOL WINAPI GetMenuItemRect(HWND hWnd,HMENU hMenu,UINT uItem,LPRECT lprcItem);
-  WINUSERAPI int WINAPI MenuItemFromPoint(HWND hWnd,HMENU hMenu,POINT ptScreen);
-
-#define TPM_LEFTBUTTON 0x0000L
-#define TPM_RIGHTBUTTON 0x0002L
-#define TPM_LEFTALIGN 0x0000L
-#define TPM_CENTERALIGN 0x0004L
-#define TPM_RIGHTALIGN 0x0008L
-#define TPM_TOPALIGN 0x0000L
-#define TPM_VCENTERALIGN 0x0010L
-#define TPM_BOTTOMALIGN 0x0020L
-
-#define TPM_HORIZONTAL 0x0000L
-#define TPM_VERTICAL 0x0040L
-#define TPM_NONOTIFY 0x0080L
-#define TPM_RETURNCMD 0x0100L
-#define TPM_RECURSE 0x0001L
-#define TPM_HORPOSANIMATION 0x0400L
-#define TPM_HORNEGANIMATION 0x0800L
-#define TPM_VERPOSANIMATION 0x1000L
-#define TPM_VERNEGANIMATION 0x2000L
-#define TPM_NOANIMATION 0x4000L
-#define TPM_LAYOUTRTL 0x8000L
-#endif
-
-  typedef struct tagDROPSTRUCT {
-    HWND hwndSource;
-    HWND hwndSink;
-    DWORD wFmt;
-    ULONG_PTR dwData;
-    POINT ptDrop;
-    DWORD dwControlData;
-  } DROPSTRUCT,*PDROPSTRUCT,*LPDROPSTRUCT;
-
-#define DOF_EXECUTABLE 0x8001
-#define DOF_DOCUMENT 0x8002
-#define DOF_DIRECTORY 0x8003
-#define DOF_MULTIPLE 0x8004
-#define DOF_PROGMAN 0x0001
-#define DOF_SHELLDATA 0x0002
-
-#define DO_DROPFILE 0x454C4946L
-#define DO_PRINTFILE 0x544E5250L
-
-  WINUSERAPI DWORD WINAPI DragObject(HWND hwndParent,HWND hwndFrom,UINT fmt,ULONG_PTR data,HCURSOR hcur);
-  WINUSERAPI WINBOOL WINAPI DragDetect(HWND hwnd,POINT pt);
-  WINUSERAPI WINBOOL WINAPI DrawIcon(HDC hDC,int X,int Y,HICON hIcon);
-
-#ifndef NODRAWTEXT
-
-#define DT_TOP 0x00000000
-#define DT_LEFT 0x00000000
-#define DT_CENTER 0x00000001
-#define DT_RIGHT 0x00000002
-#define DT_VCENTER 0x00000004
-#define DT_BOTTOM 0x00000008
-#define DT_WORDBREAK 0x00000010
-#define DT_SINGLELINE 0x00000020
-#define DT_EXPANDTABS 0x00000040
-#define DT_TABSTOP 0x00000080
-#define DT_NOCLIP 0x00000100
-#define DT_EXTERNALLEADING 0x00000200
-#define DT_CALCRECT 0x00000400
-#define DT_NOPREFIX 0x00000800
-#define DT_INTERNAL 0x00001000
-
-#define DT_EDITCONTROL 0x00002000
-#define DT_PATH_ELLIPSIS 0x00004000
-#define DT_END_ELLIPSIS 0x00008000
-#define DT_MODIFYSTRING 0x00010000
-#define DT_RTLREADING 0x00020000
-#define DT_WORD_ELLIPSIS 0x00040000
-#define DT_NOFULLWIDTHCHARBREAK 0x00080000
-#define DT_HIDEPREFIX 0x00100000
-#define DT_PREFIXONLY 0x00200000
-
-  typedef struct tagDRAWTEXTPARAMS {
-    UINT cbSize;
-    int iTabLength;
-    int iLeftMargin;
-    int iRightMargin;
-    UINT uiLengthDrawn;
-  } DRAWTEXTPARAMS,*LPDRAWTEXTPARAMS;
-
-#ifdef UNICODE
-#define DrawText DrawTextW
-#define DrawTextEx DrawTextExW
-#else
-#define DrawText DrawTextA
-#define DrawTextEx DrawTextExA
-#endif
-
-  WINUSERAPI int WINAPI DrawTextA(HDC hdc,LPCSTR lpchText,int cchText,LPRECT lprc,UINT format);
-  WINUSERAPI int WINAPI DrawTextW(HDC hdc,LPCWSTR lpchText,int cchText,LPRECT lprc,UINT format);
-  WINUSERAPI int WINAPI DrawTextExA(HDC hdc,LPSTR lpchText,int cchText,LPRECT lprc,UINT format,LPDRAWTEXTPARAMS lpdtp);
-  WINUSERAPI int WINAPI DrawTextExW(HDC hdc,LPWSTR lpchText,int cchText,LPRECT lprc,UINT format,LPDRAWTEXTPARAMS lpdtp);
-#endif
-
-#ifdef UNICODE
-#define GrayString GrayStringW
-#define DrawState DrawStateW
-#define TabbedTextOut TabbedTextOutW
-#define GetTabbedTextExtent GetTabbedTextExtentW
-#else
-#define GrayString GrayStringA
-#define DrawState DrawStateA
-#define TabbedTextOut TabbedTextOutA
-#define GetTabbedTextExtent GetTabbedTextExtentA
-#endif
-
-  WINUSERAPI WINBOOL WINAPI GrayStringA(HDC hDC,HBRUSH hBrush,GRAYSTRINGPROC lpOutputFunc,LPARAM lpData,int nCount,int X,int Y,int nWidth,int nHeight);
-  WINUSERAPI WINBOOL WINAPI GrayStringW(HDC hDC,HBRUSH hBrush,GRAYSTRINGPROC lpOutputFunc,LPARAM lpData,int nCount,int X,int Y,int nWidth,int nHeight);
-
-#define DST_COMPLEX 0x0000
-#define DST_TEXT 0x0001
-#define DST_PREFIXTEXT 0x0002
-#define DST_ICON 0x0003
-#define DST_BITMAP 0x0004
-
-#define DSS_NORMAL 0x0000
-#define DSS_UNION 0x0010
-#define DSS_DISABLED 0x0020
-#define DSS_MONO 0x0080
-#define DSS_HIDEPREFIX 0x0200
-#define DSS_PREFIXONLY 0x0400
-#define DSS_RIGHT 0x8000
-
-  WINUSERAPI WINBOOL WINAPI DrawStateA(HDC hdc,HBRUSH hbrFore,DRAWSTATEPROC qfnCallBack,LPARAM lData,WPARAM wData,int x,int y,int cx,int cy,UINT uFlags);
-  WINUSERAPI WINBOOL WINAPI DrawStateW(HDC hdc,HBRUSH hbrFore,DRAWSTATEPROC qfnCallBack,LPARAM lData,WPARAM wData,int x,int y,int cx,int cy,UINT uFlags);
-  WINUSERAPI LONG WINAPI TabbedTextOutA(HDC hdc,int x,int y,LPCSTR lpString,int chCount,int nTabPositions,CONST INT *lpnTabStopPositions,int nTabOrigin);
-  WINUSERAPI LONG WINAPI TabbedTextOutW(HDC hdc,int x,int y,LPCWSTR lpString,int chCount,int nTabPositions,CONST INT *lpnTabStopPositions,int nTabOrigin);
-  WINUSERAPI DWORD WINAPI GetTabbedTextExtentA(HDC hdc,LPCSTR lpString,int chCount,int nTabPositions,CONST INT *lpnTabStopPositions);
-  WINUSERAPI DWORD WINAPI GetTabbedTextExtentW(HDC hdc,LPCWSTR lpString,int chCount,int nTabPositions,CONST INT *lpnTabStopPositions);
-  WINUSERAPI WINBOOL WINAPI UpdateWindow(HWND hWnd);
-  WINUSERAPI HWND WINAPI SetActiveWindow(HWND hWnd);
-  WINUSERAPI HWND WINAPI GetForegroundWindow(VOID);
-  WINUSERAPI WINBOOL WINAPI PaintDesktop(HDC hdc);
-  WINUSERAPI VOID WINAPI SwitchToThisWindow(HWND hwnd,WINBOOL fUnknown);
-  WINUSERAPI WINBOOL WINAPI SetForegroundWindow(HWND hWnd);
-  WINUSERAPI WINBOOL WINAPI AllowSetForegroundWindow(DWORD dwProcessId);
-
-#define ASFW_ANY ((DWORD)-1)
-
-  WINUSERAPI WINBOOL WINAPI LockSetForegroundWindow(UINT uLockCode);
-
-#define LSFW_LOCK 1
-#define LSFW_UNLOCK 2
-
-  WINUSERAPI HWND WINAPI WindowFromDC(HDC hDC);
-  WINUSERAPI HDC WINAPI GetDC(HWND hWnd);
-  WINUSERAPI HDC WINAPI GetDCEx(HWND hWnd,HRGN hrgnClip,DWORD flags);
-
-#define DCX_WINDOW 0x00000001L
-#define DCX_CACHE 0x00000002L
-#define DCX_NORESETATTRS 0x00000004L
-#define DCX_CLIPCHILDREN 0x00000008L
-#define DCX_CLIPSIBLINGS 0x00000010L
-#define DCX_PARENTCLIP 0x00000020L
-#define DCX_EXCLUDERGN 0x00000040L
-#define DCX_INTERSECTRGN 0x00000080L
-#define DCX_EXCLUDEUPDATE 0x00000100L
-#define DCX_INTERSECTUPDATE 0x00000200L
-#define DCX_LOCKWINDOWUPDATE 0x00000400L
-
-#define DCX_VALIDATE 0x00200000L
-
-  WINUSERAPI HDC WINAPI GetWindowDC(HWND hWnd);
-  WINUSERAPI int WINAPI ReleaseDC(HWND hWnd,HDC hDC);
-  WINUSERAPI HDC WINAPI BeginPaint(HWND hWnd,LPPAINTSTRUCT lpPaint);
-  WINUSERAPI WINBOOL WINAPI EndPaint(HWND hWnd,CONST PAINTSTRUCT *lpPaint);
-  WINUSERAPI WINBOOL WINAPI GetUpdateRect(HWND hWnd,LPRECT lpRect,WINBOOL bErase);
-  WINUSERAPI int WINAPI GetUpdateRgn(HWND hWnd,HRGN hRgn,WINBOOL bErase);
-  WINUSERAPI int WINAPI SetWindowRgn(HWND hWnd,HRGN hRgn,WINBOOL bRedraw);
-  WINUSERAPI int WINAPI GetWindowRgn(HWND hWnd,HRGN hRgn);
-  WINUSERAPI int WINAPI GetWindowRgnBox(HWND hWnd,LPRECT lprc);
-  WINUSERAPI int WINAPI ExcludeUpdateRgn(HDC hDC,HWND hWnd);
-  WINUSERAPI WINBOOL WINAPI InvalidateRect(HWND hWnd,CONST RECT *lpRect,WINBOOL bErase);
-  WINUSERAPI WINBOOL WINAPI ValidateRect(HWND hWnd,CONST RECT *lpRect);
-  WINUSERAPI WINBOOL WINAPI InvalidateRgn(HWND hWnd,HRGN hRgn,WINBOOL bErase);
-  WINUSERAPI WINBOOL WINAPI ValidateRgn(HWND hWnd,HRGN hRgn);
-  WINUSERAPI WINBOOL WINAPI RedrawWindow(HWND hWnd,CONST RECT *lprcUpdate,HRGN hrgnUpdate,UINT flags);
-
-#define RDW_INVALIDATE 0x0001
-#define RDW_INTERNALPAINT 0x0002
-#define RDW_ERASE 0x0004
-
-#define RDW_VALIDATE 0x0008
-#define RDW_NOINTERNALPAINT 0x0010
-#define RDW_NOERASE 0x0020
-
-#define RDW_NOCHILDREN 0x0040
-#define RDW_ALLCHILDREN 0x0080
-
-#define RDW_UPDATENOW 0x0100
-#define RDW_ERASENOW 0x0200
-
-#define RDW_FRAME 0x0400
-#define RDW_NOFRAME 0x0800
-
-  WINUSERAPI WINBOOL WINAPI LockWindowUpdate(HWND hWndLock);
-  WINUSERAPI WINBOOL WINAPI ScrollWindow(HWND hWnd,int XAmount,int YAmount,CONST RECT *lpRect,CONST RECT *lpClipRect);
-  WINUSERAPI WINBOOL WINAPI ScrollDC(HDC hDC,int dx,int dy,CONST RECT *lprcScroll,CONST RECT *lprcClip,HRGN hrgnUpdate,LPRECT lprcUpdate);
-  WINUSERAPI int WINAPI ScrollWindowEx(HWND hWnd,int dx,int dy,CONST RECT *prcScroll,CONST RECT *prcClip,HRGN hrgnUpdate,LPRECT prcUpdate,UINT flags);
-
-#define SW_SCROLLCHILDREN 0x0001
-#define SW_INVALIDATE 0x0002
-#define SW_ERASE 0x0004
-#define SW_SMOOTHSCROLL 0x0010
-
-#ifndef NOSCROLL
-  WINUSERAPI int WINAPI SetScrollPos(HWND hWnd,int nBar,int nPos,WINBOOL bRedraw);
-  WINUSERAPI int WINAPI GetScrollPos(HWND hWnd,int nBar);
-  WINUSERAPI WINBOOL WINAPI SetScrollRange(HWND hWnd,int nBar,int nMinPos,int nMaxPos,WINBOOL bRedraw);
-  WINUSERAPI WINBOOL WINAPI GetScrollRange(HWND hWnd,int nBar,LPINT lpMinPos,LPINT lpMaxPos);
-  WINUSERAPI WINBOOL WINAPI ShowScrollBar(HWND hWnd,int wBar,WINBOOL bShow);
-  WINUSERAPI WINBOOL WINAPI EnableScrollBar(HWND hWnd,UINT wSBflags,UINT wArrows);
-
-#define ESB_ENABLE_BOTH 0x0000
-#define ESB_DISABLE_BOTH 0x0003
-
-#define ESB_DISABLE_LEFT 0x0001
-#define ESB_DISABLE_RIGHT 0x0002
-
-#define ESB_DISABLE_UP 0x0001
-#define ESB_DISABLE_DOWN 0x0002
-
-#define ESB_DISABLE_LTUP ESB_DISABLE_LEFT
-#define ESB_DISABLE_RTDN ESB_DISABLE_RIGHT
-#endif
-
-#ifdef UNICODE
-#define SetProp SetPropW
-#define GetProp GetPropW
-#define RemoveProp RemovePropW
-#define EnumPropsEx EnumPropsExW
-#define EnumProps EnumPropsW
-#define SetWindowText SetWindowTextW
-#define GetWindowText GetWindowTextW
-#define GetWindowTextLength GetWindowTextLengthW
-#else
-#define SetProp SetPropA
-#define GetProp GetPropA
-#define RemoveProp RemovePropA
-#define EnumPropsEx EnumPropsExA
-#define EnumProps EnumPropsA
-#define SetWindowText SetWindowTextA
-#define GetWindowText GetWindowTextA
-#define GetWindowTextLength GetWindowTextLengthA
-#endif
-
-  WINUSERAPI WINBOOL WINAPI SetPropA(HWND hWnd,LPCSTR lpString,HANDLE hData);
-  WINUSERAPI WINBOOL WINAPI SetPropW(HWND hWnd,LPCWSTR lpString,HANDLE hData);
-  WINUSERAPI HANDLE WINAPI GetPropA(HWND hWnd,LPCSTR lpString);
-  WINUSERAPI HANDLE WINAPI GetPropW(HWND hWnd,LPCWSTR lpString);
-  WINUSERAPI HANDLE WINAPI RemovePropA(HWND hWnd,LPCSTR lpString);
-  WINUSERAPI HANDLE WINAPI RemovePropW(HWND hWnd,LPCWSTR lpString);
-  WINUSERAPI int WINAPI EnumPropsExA(HWND hWnd,PROPENUMPROCEXA lpEnumFunc,LPARAM lParam);
-  WINUSERAPI int WINAPI EnumPropsExW(HWND hWnd,PROPENUMPROCEXW lpEnumFunc,LPARAM lParam);
-  WINUSERAPI int WINAPI EnumPropsA(HWND hWnd,PROPENUMPROCA lpEnumFunc);
-  WINUSERAPI int WINAPI EnumPropsW(HWND hWnd,PROPENUMPROCW lpEnumFunc);
-  WINUSERAPI WINBOOL WINAPI SetWindowTextA(HWND hWnd,LPCSTR lpString);
-  WINUSERAPI WINBOOL WINAPI SetWindowTextW(HWND hWnd,LPCWSTR lpString);
-  WINUSERAPI int WINAPI GetWindowTextA(HWND hWnd,LPSTR lpString,int nMaxCount);
-  WINUSERAPI int WINAPI GetWindowTextW(HWND hWnd,LPWSTR lpString,int nMaxCount);
-  WINUSERAPI int WINAPI GetWindowTextLengthA(HWND hWnd);
-  WINUSERAPI int WINAPI GetWindowTextLengthW(HWND hWnd);
-  WINUSERAPI WINBOOL WINAPI GetClientRect(HWND hWnd,LPRECT lpRect);
-  WINUSERAPI WINBOOL WINAPI GetWindowRect(HWND hWnd,LPRECT lpRect);
-  WINUSERAPI WINBOOL WINAPI AdjustWindowRect(LPRECT lpRect,DWORD dwStyle,WINBOOL bMenu);
-  WINUSERAPI WINBOOL WINAPI AdjustWindowRectEx(LPRECT lpRect,DWORD dwStyle,WINBOOL bMenu,DWORD dwExStyle);
-
-#define HELPINFO_WINDOW 0x0001
-#define HELPINFO_MENUITEM 0x0002
-
-  typedef struct tagHELPINFO {
-    UINT cbSize;
-    int iContextType;
-    int iCtrlId;
-    HANDLE hItemHandle;
-    DWORD_PTR dwContextId;
-    POINT MousePos;
-  } HELPINFO,*LPHELPINFO;
-
-  WINUSERAPI WINBOOL WINAPI SetWindowContextHelpId(HWND,DWORD);
-  WINUSERAPI DWORD WINAPI GetWindowContextHelpId(HWND);
-  WINUSERAPI WINBOOL WINAPI SetMenuContextHelpId(HMENU,DWORD);
-  WINUSERAPI DWORD WINAPI GetMenuContextHelpId(HMENU);
-
-#ifndef NOMB
-
-#define MB_OK 0x00000000L
-#define MB_OKCANCEL 0x00000001L
-#define MB_ABORTRETRYIGNORE 0x00000002L
-#define MB_YESNOCANCEL 0x00000003L
-#define MB_YESNO 0x00000004L
-#define MB_RETRYCANCEL 0x00000005L
-#define MB_CANCELTRYCONTINUE 0x00000006L
-#define MB_ICONHAND 0x00000010L
-#define MB_ICONQUESTION 0x00000020L
-#define MB_ICONEXCLAMATION 0x00000030L
-#define MB_ICONASTERISK 0x00000040L
-#define MB_USERICON 0x00000080L
-#define MB_ICONWARNING MB_ICONEXCLAMATION
-#define MB_ICONERROR MB_ICONHAND
-#define MB_ICONINFORMATION MB_ICONASTERISK
-#define MB_ICONSTOP MB_ICONHAND
-#define MB_DEFBUTTON1 0x00000000L
-#define MB_DEFBUTTON2 0x00000100L
-#define MB_DEFBUTTON3 0x00000200L
-#define MB_DEFBUTTON4 0x00000300L
-#define MB_APPLMODAL 0x00000000L
-#define MB_SYSTEMMODAL 0x00001000L
-#define MB_TASKMODAL 0x00002000L
-#define MB_HELP 0x00004000L
-#define MB_NOFOCUS 0x00008000L
-#define MB_SETFOREGROUND 0x00010000L
-#define MB_DEFAULT_DESKTOP_ONLY 0x00020000L
-#define MB_TOPMOST 0x00040000L
-#define MB_RIGHT 0x00080000L
-#define MB_RTLREADING 0x00100000L
-#define MB_SERVICE_NOTIFICATION 0x00200000L
-#define MB_SERVICE_NOTIFICATION_NT3X 0x00040000L
-#define MB_TYPEMASK 0x0000000FL
-#define MB_ICONMASK 0x000000F0L
-#define MB_DEFMASK 0x00000F00L
-#define MB_MODEMASK 0x00003000L
-#define MB_MISCMASK 0x0000C000L
-
-#ifdef UNICODE
-#define MessageBox MessageBoxW
-#define MessageBoxEx MessageBoxExW
-#else
-#define MessageBox MessageBoxA
-#define MessageBoxEx MessageBoxExA
-#endif
-
-  WINUSERAPI int WINAPI MessageBoxA(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType);
-  WINUSERAPI int WINAPI MessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);
-  WINUSERAPI int WINAPI MessageBoxExA(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType,WORD wLanguageId);
-  WINUSERAPI int WINAPI MessageBoxExW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType,WORD wLanguageId);
-
-  typedef VOID (CALLBACK *MSGBOXCALLBACK)(LPHELPINFO lpHelpInfo);
-
-  typedef struct tagMSGBOXPARAMSA {
-    UINT cbSize;
-    HWND hwndOwner;
-    HINSTANCE hInstance;
-    LPCSTR lpszText;
-    LPCSTR lpszCaption;
-    DWORD dwStyle;
-    LPCSTR lpszIcon;
-    DWORD_PTR dwContextHelpId;
-    MSGBOXCALLBACK lpfnMsgBoxCallback;
-    DWORD dwLanguageId;
-  } MSGBOXPARAMSA,*PMSGBOXPARAMSA,*LPMSGBOXPARAMSA;
-
-  typedef struct tagMSGBOXPARAMSW {
-    UINT cbSize;
-    HWND hwndOwner;
-    HINSTANCE hInstance;
-    LPCWSTR lpszText;
-    LPCWSTR lpszCaption;
-    DWORD dwStyle;
-    LPCWSTR lpszIcon;
-    DWORD_PTR dwContextHelpId;
-    MSGBOXCALLBACK lpfnMsgBoxCallback;
-    DWORD dwLanguageId;
-  } MSGBOXPARAMSW,*PMSGBOXPARAMSW,*LPMSGBOXPARAMSW;
-
-#ifdef UNICODE
-  typedef MSGBOXPARAMSW MSGBOXPARAMS;
-  typedef PMSGBOXPARAMSW PMSGBOXPARAMS;
-  typedef LPMSGBOXPARAMSW LPMSGBOXPARAMS;
-#else
-  typedef MSGBOXPARAMSA MSGBOXPARAMS;
-  typedef PMSGBOXPARAMSA PMSGBOXPARAMS;
-  typedef LPMSGBOXPARAMSA LPMSGBOXPARAMS;
-#endif
-
-#ifdef UNICODE
-#define MessageBoxIndirect MessageBoxIndirectW
-#else
-#define MessageBoxIndirect MessageBoxIndirectA
-#endif
-
-  WINUSERAPI int WINAPI MessageBoxIndirectA(CONST MSGBOXPARAMSA *lpmbp);
-  WINUSERAPI int WINAPI MessageBoxIndirectW(CONST MSGBOXPARAMSW *lpmbp);
-  WINUSERAPI WINBOOL WINAPI MessageBeep(UINT uType);
-#endif
-
-  WINUSERAPI int WINAPI ShowCursor(WINBOOL bShow);
-  WINUSERAPI WINBOOL WINAPI SetCursorPos(int X,int Y);
-  WINUSERAPI HCURSOR WINAPI SetCursor(HCURSOR hCursor);
-  WINUSERAPI WINBOOL WINAPI GetCursorPos(LPPOINT lpPoint);
-  WINUSERAPI WINBOOL WINAPI ClipCursor(CONST RECT *lpRect);
-  WINUSERAPI WINBOOL WINAPI GetClipCursor(LPRECT lpRect);
-  WINUSERAPI HCURSOR WINAPI GetCursor(VOID);
-  WINUSERAPI WINBOOL WINAPI CreateCaret(HWND hWnd,HBITMAP hBitmap,int nWidth,int nHeight);
-  WINUSERAPI UINT WINAPI GetCaretBlinkTime(VOID);
-  WINUSERAPI WINBOOL WINAPI SetCaretBlinkTime(UINT uMSeconds);
-  WINUSERAPI WINBOOL WINAPI DestroyCaret(VOID);
-  WINUSERAPI WINBOOL WINAPI HideCaret(HWND hWnd);
-  WINUSERAPI WINBOOL WINAPI ShowCaret(HWND hWnd);
-  WINUSERAPI WINBOOL WINAPI SetCaretPos(int X,int Y);
-  WINUSERAPI WINBOOL WINAPI GetCaretPos(LPPOINT lpPoint);
-  WINUSERAPI WINBOOL WINAPI ClientToScreen(HWND hWnd,LPPOINT lpPoint);
-  WINUSERAPI WINBOOL WINAPI ScreenToClient(HWND hWnd,LPPOINT lpPoint);
-  WINUSERAPI int WINAPI MapWindowPoints(HWND hWndFrom,HWND hWndTo,LPPOINT lpPoints,UINT cPoints);
-  WINUSERAPI HWND WINAPI WindowFromPoint(POINT Point);
-  WINUSERAPI HWND WINAPI ChildWindowFromPoint(HWND hWndParent,POINT Point);
-
-#define CWP_ALL 0x0000
-#define CWP_SKIPINVISIBLE 0x0001
-#define CWP_SKIPDISABLED 0x0002
-#define CWP_SKIPTRANSPARENT 0x0004
-
-  WINUSERAPI HWND WINAPI ChildWindowFromPointEx(HWND hwnd,POINT pt,UINT flags);
-
-#ifndef NOCOLOR
-
-#define CTLCOLOR_MSGBOX 0
-#define CTLCOLOR_EDIT 1
-#define CTLCOLOR_LISTBOX 2
-#define CTLCOLOR_BTN 3
-#define CTLCOLOR_DLG 4
-#define CTLCOLOR_SCROLLBAR 5
-#define CTLCOLOR_STATIC 6
-#define CTLCOLOR_MAX 7
-
-#define COLOR_SCROLLBAR 0
-#define COLOR_BACKGROUND 1
-#define COLOR_ACTIVECAPTION 2
-#define COLOR_INACTIVECAPTION 3
-#define COLOR_MENU 4
-#define COLOR_WINDOW 5
-#define COLOR_WINDOWFRAME 6
-#define COLOR_MENUTEXT 7
-#define COLOR_WINDOWTEXT 8
-#define COLOR_CAPTIONTEXT 9
-#define COLOR_ACTIVEBORDER 10
-#define COLOR_INACTIVEBORDER 11
-#define COLOR_APPWORKSPACE 12
-#define COLOR_HIGHLIGHT 13
-#define COLOR_HIGHLIGHTTEXT 14
-#define COLOR_BTNFACE 15
-#define COLOR_BTNSHADOW 16
-#define COLOR_GRAYTEXT 17
-#define COLOR_BTNTEXT 18
-#define COLOR_INACTIVECAPTIONTEXT 19
-#define COLOR_BTNHIGHLIGHT 20
-
-#define COLOR_3DDKSHADOW 21
-#define COLOR_3DLIGHT 22
-#define COLOR_INFOTEXT 23
-#define COLOR_INFOBK 24
-
-#define COLOR_HOTLIGHT 26
-#define COLOR_GRADIENTACTIVECAPTION 27
-#define COLOR_GRADIENTINACTIVECAPTION 28
-#define COLOR_MENUHILIGHT 29
-#define COLOR_MENUBAR 30
-
-#define COLOR_DESKTOP COLOR_BACKGROUND
-#define COLOR_3DFACE COLOR_BTNFACE
-#define COLOR_3DSHADOW COLOR_BTNSHADOW
-#define COLOR_3DHIGHLIGHT COLOR_BTNHIGHLIGHT
-#define COLOR_3DHILIGHT COLOR_BTNHIGHLIGHT
-#define COLOR_BTNHILIGHT COLOR_BTNHIGHLIGHT
-
-  WINUSERAPI DWORD WINAPI GetSysColor(int nIndex);
-  WINUSERAPI HBRUSH WINAPI GetSysColorBrush(int nIndex);
-  WINUSERAPI WINBOOL WINAPI SetSysColors(int cElements,CONST INT *lpaElements,CONST COLORREF *lpaRgbValues);
-#endif
-
-  WINUSERAPI WINBOOL WINAPI DrawFocusRect(HDC hDC,CONST RECT *lprc);
-  WINUSERAPI int WINAPI FillRect(HDC hDC,CONST RECT *lprc,HBRUSH hbr);
-  WINUSERAPI int WINAPI FrameRect(HDC hDC,CONST RECT *lprc,HBRUSH hbr);
-  WINUSERAPI WINBOOL WINAPI InvertRect(HDC hDC,CONST RECT *lprc);
-  WINUSERAPI WINBOOL WINAPI SetRect(LPRECT lprc,int xLeft,int yTop,int xRight,int yBottom);
-  WINUSERAPI WINBOOL WINAPI SetRectEmpty(LPRECT lprc);
-  WINUSERAPI WINBOOL WINAPI CopyRect(LPRECT lprcDst,CONST RECT *lprcSrc);
-  WINUSERAPI WINBOOL WINAPI InflateRect(LPRECT lprc,int dx,int dy);
-  WINUSERAPI WINBOOL WINAPI IntersectRect(LPRECT lprcDst,CONST RECT *lprcSrc1,CONST RECT *lprcSrc2);
-  WINUSERAPI WINBOOL WINAPI UnionRect(LPRECT lprcDst,CONST RECT *lprcSrc1,CONST RECT *lprcSrc2);
-  WINUSERAPI WINBOOL WINAPI SubtractRect(LPRECT lprcDst,CONST RECT *lprcSrc1,CONST RECT *lprcSrc2);
-  WINUSERAPI WINBOOL WINAPI OffsetRect(LPRECT lprc,int dx,int dy);
-  WINUSERAPI WINBOOL WINAPI IsRectEmpty(CONST RECT *lprc);
-  WINUSERAPI WINBOOL WINAPI EqualRect(CONST RECT *lprc1,CONST RECT *lprc2);
-  WINUSERAPI WINBOOL WINAPI PtInRect(CONST RECT *lprc,POINT pt);
-
-#ifndef NOWINOFFSETS
-
-#ifdef UNICODE
-#define GetWindowLong GetWindowLongW
-#define SetWindowLong SetWindowLongW
-#else
-#define GetWindowLong GetWindowLongA
-#define SetWindowLong SetWindowLongA
-#endif
-
-  WINUSERAPI WORD WINAPI GetWindowWord(HWND hWnd,int nIndex);
-  WINUSERAPI WORD WINAPI SetWindowWord(HWND hWnd,int nIndex,WORD wNewWord);
-  WINUSERAPI LONG WINAPI GetWindowLongA(HWND hWnd,int nIndex);
-  WINUSERAPI LONG WINAPI GetWindowLongW(HWND hWnd,int nIndex);
-  WINUSERAPI LONG WINAPI SetWindowLongA(HWND hWnd,int nIndex,LONG dwNewLong);
-  WINUSERAPI LONG WINAPI SetWindowLongW(HWND hWnd,int nIndex,LONG dwNewLong);
-
-#ifdef _WIN64
-
-#ifdef UNICODE
-#define GetWindowLongPtr GetWindowLongPtrW
-#define SetWindowLongPtr SetWindowLongPtrW
-#else
-#define GetWindowLongPtr GetWindowLongPtrA
-#define SetWindowLongPtr SetWindowLongPtrA
-#endif
-
-  WINUSERAPI LONG_PTR WINAPI GetWindowLongPtrA(HWND hWnd,int nIndex);
-  WINUSERAPI LONG_PTR WINAPI GetWindowLongPtrW(HWND hWnd,int nIndex);
-  WINUSERAPI LONG_PTR WINAPI SetWindowLongPtrA(HWND hWnd,int nIndex,LONG_PTR dwNewLong);
-  WINUSERAPI LONG_PTR WINAPI SetWindowLongPtrW(HWND hWnd,int nIndex,LONG_PTR dwNewLong);
-#else
-
-#ifdef UNICODE
-#define GetWindowLongPtr GetWindowLongPtrW
-#define SetWindowLongPtr SetWindowLongPtrW
-#else
-#define GetWindowLongPtr GetWindowLongPtrA
-#define SetWindowLongPtr SetWindowLongPtrA
-#endif
-
-#define GetWindowLongPtrA GetWindowLongA
-#define GetWindowLongPtrW GetWindowLongW
-#define SetWindowLongPtrA SetWindowLongA
-#define SetWindowLongPtrW SetWindowLongW
-#endif
-
-#ifdef UNICODE
-#define GetClassLong GetClassLongW
-#define SetClassLong SetClassLongW
-#else
-#define GetClassLong GetClassLongA
-#define SetClassLong SetClassLongA
-#endif
-
-  WINUSERAPI WORD WINAPI GetClassWord(HWND hWnd,int nIndex);
-  WINUSERAPI WORD WINAPI SetClassWord(HWND hWnd,int nIndex,WORD wNewWord);
-  WINUSERAPI DWORD WINAPI GetClassLongA(HWND hWnd,int nIndex);
-  WINUSERAPI DWORD WINAPI GetClassLongW(HWND hWnd,int nIndex);
-  WINUSERAPI DWORD WINAPI SetClassLongA(HWND hWnd,int nIndex,LONG dwNewLong);
-  WINUSERAPI DWORD WINAPI SetClassLongW(HWND hWnd,int nIndex,LONG dwNewLong);
-
-#ifdef _WIN64
-
-#ifdef UNICODE
-#define GetClassLongPtr GetClassLongPtrW
-#define SetClassLongPtr SetClassLongPtrW
-#else
-#define GetClassLongPtr GetClassLongPtrA
-#define SetClassLongPtr SetClassLongPtrA
-#endif
-
-  WINUSERAPI ULONG_PTR WINAPI GetClassLongPtrA(HWND hWnd,int nIndex);
-  WINUSERAPI ULONG_PTR WINAPI GetClassLongPtrW(HWND hWnd,int nIndex);
-  WINUSERAPI ULONG_PTR WINAPI SetClassLongPtrA(HWND hWnd,int nIndex,LONG_PTR dwNewLong);
-  WINUSERAPI ULONG_PTR WINAPI SetClassLongPtrW(HWND hWnd,int nIndex,LONG_PTR dwNewLong);
-#else
-#ifdef UNICODE
-#define GetClassLongPtr GetClassLongPtrW
-#define SetClassLongPtr SetClassLongPtrW
-#else
-#define GetClassLongPtr GetClassLongPtrA
-#define SetClassLongPtr SetClassLongPtrA
-#endif
-
-#define GetClassLongPtrA GetClassLongA
-#define GetClassLongPtrW GetClassLongW
-#define SetClassLongPtrA SetClassLongA
-#define SetClassLongPtrW SetClassLongW
-#endif
-#endif
-
-#ifdef UNICODE
-#define FindWindow FindWindowW
-#define FindWindowEx FindWindowExW
-#define GetClassName GetClassNameW
-#else
-#define FindWindow FindWindowA
-#define FindWindowEx FindWindowExA
-#define GetClassName GetClassNameA
-#endif
-
-  WINUSERAPI WINBOOL WINAPI GetProcessDefaultLayout(DWORD *pdwDefaultLayout);
-  WINUSERAPI WINBOOL WINAPI SetProcessDefaultLayout(DWORD dwDefaultLayout);
-  WINUSERAPI HWND WINAPI GetDesktopWindow(VOID);
-  WINUSERAPI HWND WINAPI GetParent(HWND hWnd);
-  WINUSERAPI HWND WINAPI SetParent(HWND hWndChild,HWND hWndNewParent);
-  WINUSERAPI WINBOOL WINAPI EnumChildWindows(HWND hWndParent,WNDENUMPROC lpEnumFunc,LPARAM lParam);
-  WINUSERAPI HWND WINAPI FindWindowA(LPCSTR lpClassName,LPCSTR lpWindowName);
-  WINUSERAPI HWND WINAPI FindWindowW(LPCWSTR lpClassName,LPCWSTR lpWindowName);
-  WINUSERAPI HWND WINAPI FindWindowExA(HWND hWndParent,HWND hWndChildAfter,LPCSTR lpszClass,LPCSTR lpszWindow);
-  WINUSERAPI HWND WINAPI FindWindowExW(HWND hWndParent,HWND hWndChildAfter,LPCWSTR lpszClass,LPCWSTR lpszWindow);
-  WINUSERAPI HWND WINAPI GetShellWindow(VOID);
-  WINUSERAPI WINBOOL WINAPI RegisterShellHookWindow(HWND hwnd);
-  WINUSERAPI WINBOOL WINAPI DeregisterShellHookWindow(HWND hwnd);
-  WINUSERAPI WINBOOL WINAPI EnumWindows(WNDENUMPROC lpEnumFunc,LPARAM lParam);
-  WINUSERAPI WINBOOL WINAPI EnumThreadWindows(DWORD dwThreadId,WNDENUMPROC lpfn,LPARAM lParam);
-
-#define EnumTaskWindows(hTask,lpfn,lParam) EnumThreadWindows(HandleToUlong(hTask),lpfn,lParam)
-
-  WINUSERAPI int WINAPI GetClassNameA(HWND hWnd,LPSTR lpClassName,int nMaxCount);
-  WINUSERAPI int WINAPI GetClassNameW(HWND hWnd,LPWSTR lpClassName,int nMaxCount);
-  WINUSERAPI HWND WINAPI GetTopWindow(HWND hWnd);
-
-#define GetNextWindow(hWnd,wCmd) GetWindow(hWnd,wCmd)
-#define GetSysModalWindow() (NULL)
-#define SetSysModalWindow(hWnd) (NULL)
-
-  WINUSERAPI DWORD WINAPI GetWindowThreadProcessId(HWND hWnd,LPDWORD lpdwProcessId);
-  WINUSERAPI WINBOOL WINAPI IsGUIThread(WINBOOL bConvert);
-
-#define GetWindowTask(hWnd) ((HANDLE)(DWORD_PTR)GetWindowThreadProcessId(hWnd,NULL))
-
-  WINUSERAPI HWND WINAPI GetLastActivePopup(HWND hWnd);
-
-#define GW_HWNDFIRST 0
-#define GW_HWNDLAST 1
-#define GW_HWNDNEXT 2
-#define GW_HWNDPREV 3
-#define GW_OWNER 4
-#define GW_CHILD 5
-#define GW_ENABLEDPOPUP 6
-#define GW_MAX 6
-
-  WINUSERAPI HWND WINAPI GetWindow(HWND hWnd,UINT uCmd);
-
-#ifndef NOWH
-
-#ifdef UNICODE
-#define SetWindowsHook SetWindowsHookW
-#define SetWindowsHookEx SetWindowsHookExW
-#else
-#define SetWindowsHook SetWindowsHookA
-#define SetWindowsHookEx SetWindowsHookExA
-#endif
-
-  WINUSERAPI HHOOK WINAPI SetWindowsHookA(int nFilterType,HOOKPROC pfnFilterProc);
-  WINUSERAPI HHOOK WINAPI SetWindowsHookW(int nFilterType,HOOKPROC pfnFilterProc);
-  WINUSERAPI WINBOOL WINAPI UnhookWindowsHook(int nCode,HOOKPROC pfnFilterProc);
-  WINUSERAPI HHOOK WINAPI SetWindowsHookExA(int idHook,HOOKPROC lpfn,HINSTANCE hmod,DWORD dwThreadId);
-  WINUSERAPI HHOOK WINAPI SetWindowsHookExW(int idHook,HOOKPROC lpfn,HINSTANCE hmod,DWORD dwThreadId);
-  WINUSERAPI WINBOOL WINAPI UnhookWindowsHookEx(HHOOK hhk);
-  WINUSERAPI LRESULT WINAPI CallNextHookEx(HHOOK hhk,int nCode,WPARAM wParam,LPARAM lParam);
-#define DefHookProc(nCode,wParam,lParam,phhk) CallNextHookEx(*phhk,nCode,wParam,lParam)
-#endif
-
-#ifndef NOMENUS
-
-#define MF_INSERT 0x00000000L
-#define MF_CHANGE 0x00000080L
-#define MF_APPEND 0x00000100L
-#define MF_DELETE 0x00000200L
-#define MF_REMOVE 0x00001000L
-#define MF_BYCOMMAND 0x00000000L
-#define MF_BYPOSITION 0x00000400L
-#define MF_SEPARATOR 0x00000800L
-#define MF_ENABLED 0x00000000L
-#define MF_GRAYED 0x00000001L
-#define MF_DISABLED 0x00000002L
-#define MF_UNCHECKED 0x00000000L
-#define MF_CHECKED 0x00000008L
-#define MF_USECHECKBITMAPS 0x00000200L
-#define MF_STRING 0x00000000L
-#define MF_BITMAP 0x00000004L
-#define MF_OWNERDRAW 0x00000100L
-#define MF_POPUP 0x00000010L
-#define MF_MENUBARBREAK 0x00000020L
-#define MF_MENUBREAK 0x00000040L
-#define MF_UNHILITE 0x00000000L
-#define MF_HILITE 0x00000080L
-#define MF_DEFAULT 0x00001000L
-#define MF_SYSMENU 0x00002000L
-#define MF_HELP 0x00004000L
-#define MF_RIGHTJUSTIFY 0x00004000L
-#define MF_MOUSESELECT 0x00008000L
-#define MF_END 0x00000080L
-
-#define MFT_STRING MF_STRING
-#define MFT_BITMAP MF_BITMAP
-#define MFT_MENUBARBREAK MF_MENUBARBREAK
-#define MFT_MENUBREAK MF_MENUBREAK
-#define MFT_OWNERDRAW MF_OWNERDRAW
-#define MFT_RADIOCHECK 0x00000200L
-#define MFT_SEPARATOR MF_SEPARATOR
-#define MFT_RIGHTORDER 0x00002000L
-#define MFT_RIGHTJUSTIFY MF_RIGHTJUSTIFY
-
-#define MFS_GRAYED 0x00000003L
-#define MFS_DISABLED MFS_GRAYED
-#define MFS_CHECKED MF_CHECKED
-#define MFS_HILITE MF_HILITE
-#define MFS_ENABLED MF_ENABLED
-#define MFS_UNCHECKED MF_UNCHECKED
-#define MFS_UNHILITE MF_UNHILITE
-#define MFS_DEFAULT MF_DEFAULT
-
-  WINUSERAPI WINBOOL WINAPI CheckMenuRadioItem(HMENU hmenu,UINT first,UINT last,UINT check,UINT flags);
-
-  typedef struct {
-    WORD versionNumber;
-    WORD offset;
-  } MENUITEMTEMPLATEHEADER,*PMENUITEMTEMPLATEHEADER;
-
-  typedef struct {
-    WORD mtOption;
-    WORD mtID;
-    WCHAR mtString[1];
-  } MENUITEMTEMPLATE,*PMENUITEMTEMPLATE;
-#define MF_END 0x00000080L
-#endif
-
-#ifndef NOSYSCOMMANDS
-
-#define SC_SIZE 0xF000
-#define SC_MOVE 0xF010
-#define SC_MINIMIZE 0xF020
-#define SC_MAXIMIZE 0xF030
-#define SC_NEXTWINDOW 0xF040
-#define SC_PREVWINDOW 0xF050
-#define SC_CLOSE 0xF060
-#define SC_VSCROLL 0xF070
-#define SC_HSCROLL 0xF080
-#define SC_MOUSEMENU 0xF090
-#define SC_KEYMENU 0xF100
-#define SC_ARRANGE 0xF110
-#define SC_RESTORE 0xF120
-#define SC_TASKLIST 0xF130
-#define SC_SCREENSAVE 0xF140
-#define SC_HOTKEY 0xF150
-#define SC_DEFAULT 0xF160
-#define SC_MONITORPOWER 0xF170
-#define SC_CONTEXTHELP 0xF180
-#define SC_SEPARATOR 0xF00F
-#define SC_ICON SC_MINIMIZE
-#define SC_ZOOM SC_MAXIMIZE
-#endif
-
-#ifdef UNICODE
-#define LoadBitmap LoadBitmapW
-#define LoadCursor LoadCursorW
-#define LoadCursorFromFile LoadCursorFromFileW
-#else
-#define LoadBitmap LoadBitmapA
-#define LoadCursor LoadCursorA
-#define LoadCursorFromFile LoadCursorFromFileA
-#endif
-
-  WINUSERAPI HBITMAP WINAPI LoadBitmapA(HINSTANCE hInstance,LPCSTR lpBitmapName);
-  WINUSERAPI HBITMAP WINAPI LoadBitmapW(HINSTANCE hInstance,LPCWSTR lpBitmapName);
-  WINUSERAPI HCURSOR WINAPI LoadCursorA(HINSTANCE hInstance,LPCSTR lpCursorName);
-  WINUSERAPI HCURSOR WINAPI LoadCursorW(HINSTANCE hInstance,LPCWSTR lpCursorName);
-  WINUSERAPI HCURSOR WINAPI LoadCursorFromFileA(LPCSTR lpFileName);
-  WINUSERAPI HCURSOR WINAPI LoadCursorFromFileW(LPCWSTR lpFileName);
-  WINUSERAPI HCURSOR WINAPI CreateCursor(HINSTANCE hInst,int xHotSpot,int yHotSpot,int nWidth,int nHeight,CONST VOID *pvANDPlane,CONST VOID *pvXORPlane);
-  WINUSERAPI WINBOOL WINAPI DestroyCursor(HCURSOR hCursor);
-
-#define CopyCursor(pcur) ((HCURSOR)CopyIcon((HICON)(pcur)))
-
-#define IDC_ARROW MAKEINTRESOURCE(32512)
-#define IDC_IBEAM MAKEINTRESOURCE(32513)
-#define IDC_WAIT MAKEINTRESOURCE(32514)
-#define IDC_CROSS MAKEINTRESOURCE(32515)
-#define IDC_UPARROW MAKEINTRESOURCE(32516)
-#define IDC_SIZE MAKEINTRESOURCE(32640)
-#define IDC_ICON MAKEINTRESOURCE(32641)
-#define IDC_SIZENWSE MAKEINTRESOURCE(32642)
-#define IDC_SIZENESW MAKEINTRESOURCE(32643)
-#define IDC_SIZEWE MAKEINTRESOURCE(32644)
-#define IDC_SIZENS MAKEINTRESOURCE(32645)
-#define IDC_SIZEALL MAKEINTRESOURCE(32646)
-#define IDC_NO MAKEINTRESOURCE(32648)
-#define IDC_HAND MAKEINTRESOURCE(32649)
-#define IDC_APPSTARTING MAKEINTRESOURCE(32650)
-#define IDC_HELP MAKEINTRESOURCE(32651)
-
-  WINUSERAPI WINBOOL WINAPI SetSystemCursor(HCURSOR hcur,DWORD id);
-
-  typedef struct _ICONINFO {
-    WINBOOL fIcon;
-    DWORD xHotspot;
-    DWORD yHotspot;
-    HBITMAP hbmMask;
-    HBITMAP hbmColor;
-  } ICONINFO;
-  typedef ICONINFO *PICONINFO;
-
-#ifdef UNICODE
-#define LoadIcon LoadIconW
-#define PrivateExtractIcons PrivateExtractIconsW
-#else
-#define LoadIcon LoadIconA
-#define PrivateExtractIcons PrivateExtractIconsA
-#endif
-
-  WINUSERAPI HICON WINAPI LoadIconA(HINSTANCE hInstance,LPCSTR lpIconName);
-  WINUSERAPI HICON WINAPI LoadIconW(HINSTANCE hInstance,LPCWSTR lpIconName);
-  WINUSERAPI UINT WINAPI PrivateExtractIconsA(LPCSTR szFileName,int nIconIndex,int cxIcon,int cyIcon,HICON *phicon,UINT *piconid,UINT nIcons,UINT flags);
-  WINUSERAPI UINT WINAPI PrivateExtractIconsW(LPCWSTR szFileName,int nIconIndex,int cxIcon,int cyIcon,HICON *phicon,UINT *piconid,UINT nIcons,UINT flags);
-  WINUSERAPI HICON WINAPI CreateIcon(HINSTANCE hInstance,int nWidth,int nHeight,BYTE cPlanes,BYTE cBitsPixel,CONST BYTE *lpbANDbits,CONST BYTE *lpbXORbits);
-  WINUSERAPI WINBOOL WINAPI DestroyIcon(HICON hIcon);
-  WINUSERAPI int WINAPI LookupIconIdFromDirectory(PBYTE presbits,WINBOOL fIcon);
-  WINUSERAPI int WINAPI LookupIconIdFromDirectoryEx(PBYTE presbits,WINBOOL fIcon,int cxDesired,int cyDesired,UINT Flags);
-  WINUSERAPI HICON WINAPI CreateIconFromResource(PBYTE presbits,DWORD dwResSize,WINBOOL fIcon,DWORD dwVer);
-  WINUSERAPI HICON WINAPI CreateIconFromResourceEx(PBYTE presbits,DWORD dwResSize,WINBOOL fIcon,DWORD dwVer,int cxDesired,int cyDesired,UINT Flags);
-
-  typedef struct tagCURSORSHAPE {
-    int xHotSpot;
-    int yHotSpot;
-    int cx;
-    int cy;
-    int cbWidth;
-    BYTE Planes;
-    BYTE BitsPixel;
-  } CURSORSHAPE,*LPCURSORSHAPE;
-
-#define IMAGE_BITMAP 0
-#define IMAGE_ICON 1
-#define IMAGE_CURSOR 2
-#define IMAGE_ENHMETAFILE 3
-
-#define LR_DEFAULTCOLOR 0x0000
-#define LR_MONOCHROME 0x0001
-#define LR_COLOR 0x0002
-#define LR_COPYRETURNORG 0x0004
-#define LR_COPYDELETEORG 0x0008
-#define LR_LOADFROMFILE 0x0010
-#define LR_LOADTRANSPARENT 0x0020
-#define LR_DEFAULTSIZE 0x0040
-#define LR_VGACOLOR 0x0080
-#define LR_LOADMAP3DCOLORS 0x1000
-#define LR_CREATEDIBSECTION 0x2000
-#define LR_COPYFROMRESOURCE 0x4000
-#define LR_SHARED 0x8000
-
-#ifdef UNICODE
-#define LoadImage LoadImageW
-#else
-#define LoadImage LoadImageA
-#endif
-
-  WINUSERAPI HANDLE WINAPI LoadImageA(HINSTANCE hInst,LPCSTR name,UINT type,int cx,int cy,UINT fuLoad);
-  WINUSERAPI HANDLE WINAPI LoadImageW(HINSTANCE hInst,LPCWSTR name,UINT type,int cx,int cy,UINT fuLoad);
-  WINUSERAPI HANDLE WINAPI CopyImage(HANDLE h,UINT type,int cx,int cy,UINT flags);
-
-#define DI_MASK 0x0001
-#define DI_IMAGE 0x0002
-#define DI_NORMAL 0x0003
-#define DI_COMPAT 0x0004
-#define DI_DEFAULTSIZE 0x0008
-#define DI_NOMIRROR 0x0010
-
-  WINUSERAPI WINBOOL WINAPI DrawIconEx(HDC hdc,int xLeft,int yTop,HICON hIcon,int cxWidth,int cyWidth,UINT istepIfAniCur,HBRUSH hbrFlickerFreeDraw,UINT diFlags);
-  WINUSERAPI HICON WINAPI CreateIconIndirect(PICONINFO piconinfo);
-  WINUSERAPI HICON WINAPI CopyIcon(HICON hIcon);
-  WINUSERAPI WINBOOL WINAPI GetIconInfo(HICON hIcon,PICONINFO piconinfo);
-
-#define RES_ICON 1
-#define RES_CURSOR 2
-
-#ifdef OEMRESOURCE
-
-#define OBM_CLOSE 32754
-#define OBM_UPARROW 32753
-#define OBM_DNARROW 32752
-#define OBM_RGARROW 32751
-#define OBM_LFARROW 32750
-#define OBM_REDUCE 32749
-#define OBM_ZOOM 32748
-#define OBM_RESTORE 32747
-#define OBM_REDUCED 32746
-#define OBM_ZOOMD 32745
-#define OBM_RESTORED 32744
-#define OBM_UPARROWD 32743
-#define OBM_DNARROWD 32742
-#define OBM_RGARROWD 32741
-#define OBM_LFARROWD 32740
-#define OBM_MNARROW 32739
-#define OBM_COMBO 32738
-#define OBM_UPARROWI 32737
-#define OBM_DNARROWI 32736
-#define OBM_RGARROWI 32735
-#define OBM_LFARROWI 32734
-
-#define OBM_OLD_CLOSE 32767
-#define OBM_SIZE 32766
-#define OBM_OLD_UPARROW 32765
-#define OBM_OLD_DNARROW 32764
-#define OBM_OLD_RGARROW 32763
-#define OBM_OLD_LFARROW 32762
-#define OBM_BTSIZE 32761
-#define OBM_CHECK 32760
-#define OBM_CHECKBOXES 32759
-#define OBM_BTNCORNERS 32758
-#define OBM_OLD_REDUCE 32757
-#define OBM_OLD_ZOOM 32756
-#define OBM_OLD_RESTORE 32755
-
-#define OCR_NORMAL 32512
-#define OCR_IBEAM 32513
-#define OCR_WAIT 32514
-#define OCR_CROSS 32515
-#define OCR_UP 32516
-#define OCR_SIZE 32640
-#define OCR_ICON 32641
-#define OCR_SIZENWSE 32642
-#define OCR_SIZENESW 32643
-#define OCR_SIZEWE 32644
-#define OCR_SIZENS 32645
-#define OCR_SIZEALL 32646
-#define OCR_ICOCUR 32647
-#define OCR_NO 32648
-#define OCR_HAND 32649
-#define OCR_APPSTARTING 32650
-
-#define OIC_SAMPLE 32512
-#define OIC_HAND 32513
-#define OIC_QUES 32514
-#define OIC_BANG 32515
-#define OIC_NOTE 32516
-#define OIC_WINLOGO 32517
-#define OIC_WARNING OIC_BANG
-#define OIC_ERROR OIC_HAND
-#define OIC_INFORMATION OIC_NOTE
-#endif
-
-#define ORD_LANGDRIVER 1
-
-#ifndef NOICONS
-
-#ifdef RC_INVOKED
-#define IDI_APPLICATION 32512
-#define IDI_HAND 32513
-#define IDI_QUESTION 32514
-#define IDI_EXCLAMATION 32515
-#define IDI_ASTERISK 32516
-#define IDI_WINLOGO 32517
-#else
-#define IDI_APPLICATION MAKEINTRESOURCE(32512)
-#define IDI_HAND MAKEINTRESOURCE(32513)
-#define IDI_QUESTION MAKEINTRESOURCE(32514)
-#define IDI_EXCLAMATION MAKEINTRESOURCE(32515)
-#define IDI_ASTERISK MAKEINTRESOURCE(32516)
-#define IDI_WINLOGO MAKEINTRESOURCE(32517)
-#endif
-
-#define IDI_WARNING IDI_EXCLAMATION
-#define IDI_ERROR IDI_HAND
-#define IDI_INFORMATION IDI_ASTERISK
-#endif
-
-#ifdef UNICODE
-#define LoadString LoadStringW
-#else
-#define LoadString LoadStringA
-#endif
-
-  WINUSERAPI int WINAPI LoadStringA(HINSTANCE hInstance,UINT uID,LPSTR lpBuffer,int cchBufferMax);
-  WINUSERAPI int WINAPI LoadStringW(HINSTANCE hInstance,UINT uID,LPWSTR lpBuffer,int cchBufferMax);
-
-#define IDOK 1
-#define IDCANCEL 2
-#define IDABORT 3
-#define IDRETRY 4
-#define IDIGNORE 5
-#define IDYES 6
-#define IDNO 7
-#define IDCLOSE 8
-#define IDHELP 9
-#define IDTRYAGAIN 10
-#define IDCONTINUE 11
-
-#ifndef IDTIMEOUT
-#define IDTIMEOUT 32000
-#endif
-
-#ifndef NOCTLMGR
-
-#ifndef NOWINSTYLES
-#define ES_LEFT 0x0000L
-#define ES_CENTER 0x0001L
-#define ES_RIGHT 0x0002L
-#define ES_MULTILINE 0x0004L
-#define ES_UPPERCASE 0x0008L
-#define ES_LOWERCASE 0x0010L
-#define ES_PASSWORD 0x0020L
-#define ES_AUTOVSCROLL 0x0040L
-#define ES_AUTOHSCROLL 0x0080L
-#define ES_NOHIDESEL 0x0100L
-#define ES_OEMCONVERT 0x0400L
-#define ES_READONLY 0x0800L
-#define ES_WANTRETURN 0x1000L
-#define ES_NUMBER 0x2000L
-#endif
-
-#define EN_SETFOCUS 0x0100
-#define EN_KILLFOCUS 0x0200
-#define EN_CHANGE 0x0300
-#define EN_UPDATE 0x0400
-#define EN_ERRSPACE 0x0500
-#define EN_MAXTEXT 0x0501
-#define EN_HSCROLL 0x0601
-#define EN_VSCROLL 0x0602
-#define EN_ALIGN_LTR_EC 0x0700
-#define EN_ALIGN_RTL_EC 0x0701
-
-#define EC_LEFTMARGIN 0x0001
-#define EC_RIGHTMARGIN 0x0002
-#define EC_USEFONTINFO 0xffff
-
-#define EMSIS_COMPOSITIONSTRING 0x0001
-
-#define EIMES_GETCOMPSTRATONCE 0x0001
-#define EIMES_CANCELCOMPSTRINFOCUS 0x0002
-#define EIMES_COMPLETECOMPSTRKILLFOCUS 0x0004
-
-#ifndef NOWINMESSAGES
-
-#define EM_GETSEL 0x00B0
-#define EM_SETSEL 0x00B1
-#define EM_GETRECT 0x00B2
-#define EM_SETRECT 0x00B3
-#define EM_SETRECTNP 0x00B4
-#define EM_SCROLL 0x00B5
-#define EM_LINESCROLL 0x00B6
-#define EM_SCROLLCARET 0x00B7
-#define EM_GETMODIFY 0x00B8
-#define EM_SETMODIFY 0x00B9
-#define EM_GETLINECOUNT 0x00BA
-#define EM_LINEINDEX 0x00BB
-#define EM_SETHANDLE 0x00BC
-#define EM_GETHANDLE 0x00BD
-#define EM_GETTHUMB 0x00BE
-#define EM_LINELENGTH 0x00C1
-#define EM_REPLACESEL 0x00C2
-#define EM_GETLINE 0x00C4
-#define EM_LIMITTEXT 0x00C5
-#define EM_CANUNDO 0x00C6
-#define EM_UNDO 0x00C7
-#define EM_FMTLINES 0x00C8
-#define EM_LINEFROMCHAR 0x00C9
-#define EM_SETTABSTOPS 0x00CB
-#define EM_SETPASSWORDCHAR 0x00CC
-#define EM_EMPTYUNDOBUFFER 0x00CD
-#define EM_GETFIRSTVISIBLELINE 0x00CE
-#define EM_SETREADONLY 0x00CF
-#define EM_SETWORDBREAKPROC 0x00D0
-#define EM_GETWORDBREAKPROC 0x00D1
-#define EM_GETPASSWORDCHAR 0x00D2
-#define EM_SETMARGINS 0x00D3
-#define EM_GETMARGINS 0x00D4
-#define EM_SETLIMITTEXT EM_LIMITTEXT
-#define EM_GETLIMITTEXT 0x00D5
-#define EM_POSFROMCHAR 0x00D6
-#define EM_CHARFROMPOS 0x00D7
-#define EM_SETIMESTATUS 0x00D8
-#define EM_GETIMESTATUS 0x00D9
-#endif
-
-#define WB_LEFT 0
-#define WB_RIGHT 1
-#define WB_ISDELIMITER 2
-
-#define BS_PUSHBUTTON 0x00000000L
-#define BS_DEFPUSHBUTTON 0x00000001L
-#define BS_CHECKBOX 0x00000002L
-#define BS_AUTOCHECKBOX 0x00000003L
-#define BS_RADIOBUTTON 0x00000004L
-#define BS_3STATE 0x00000005L
-#define BS_AUTO3STATE 0x00000006L
-#define BS_GROUPBOX 0x00000007L
-#define BS_USERBUTTON 0x00000008L
-#define BS_AUTORADIOBUTTON 0x00000009L
-#define BS_PUSHBOX 0x0000000AL
-#define BS_OWNERDRAW 0x0000000BL
-#define BS_TYPEMASK 0x0000000FL
-#define BS_LEFTTEXT 0x00000020L
-#define BS_TEXT 0x00000000L
-#define BS_ICON 0x00000040L
-#define BS_BITMAP 0x00000080L
-#define BS_LEFT 0x00000100L
-#define BS_RIGHT 0x00000200L
-#define BS_CENTER 0x00000300L
-#define BS_TOP 0x00000400L
-#define BS_BOTTOM 0x00000800L
-#define BS_VCENTER 0x00000C00L
-#define BS_PUSHLIKE 0x00001000L
-#define BS_MULTILINE 0x00002000L
-#define BS_NOTIFY 0x00004000L
-#define BS_FLAT 0x00008000L
-#define BS_RIGHTBUTTON BS_LEFTTEXT
-
-#define BN_CLICKED 0
-#define BN_PAINT 1
-#define BN_HILITE 2
-#define BN_UNHILITE 3
-#define BN_DISABLE 4
-#define BN_DOUBLECLICKED 5
-#define BN_PUSHED BN_HILITE
-#define BN_UNPUSHED BN_UNHILITE
-#define BN_DBLCLK BN_DOUBLECLICKED
-#define BN_SETFOCUS 6
-#define BN_KILLFOCUS 7
-
-#define BM_GETCHECK 0x00F0
-#define BM_SETCHECK 0x00F1
-#define BM_GETSTATE 0x00F2
-#define BM_SETSTATE 0x00F3
-#define BM_SETSTYLE 0x00F4
-#define BM_CLICK 0x00F5
-#define BM_GETIMAGE 0x00F6
-#define BM_SETIMAGE 0x00F7
-
-#define BST_UNCHECKED 0x0000
-#define BST_CHECKED 0x0001
-#define BST_INDETERMINATE 0x0002
-#define BST_PUSHED 0x0004
-#define BST_FOCUS 0x0008
-
-#define SS_LEFT 0x00000000L
-#define SS_CENTER 0x00000001L
-#define SS_RIGHT 0x00000002L
-#define SS_ICON 0x00000003L
-#define SS_BLACKRECT 0x00000004L
-#define SS_GRAYRECT 0x00000005L
-#define SS_WHITERECT 0x00000006L
-#define SS_BLACKFRAME 0x00000007L
-#define SS_GRAYFRAME 0x00000008L
-#define SS_WHITEFRAME 0x00000009L
-#define SS_USERITEM 0x0000000AL
-#define SS_SIMPLE 0x0000000BL
-#define SS_LEFTNOWORDWRAP 0x0000000CL
-#define SS_OWNERDRAW 0x0000000DL
-#define SS_BITMAP 0x0000000EL
-#define SS_ENHMETAFILE 0x0000000FL
-#define SS_ETCHEDHORZ 0x00000010L
-#define SS_ETCHEDVERT 0x00000011L
-#define SS_ETCHEDFRAME 0x00000012L
-#define SS_TYPEMASK 0x0000001FL
-#define SS_REALSIZECONTROL 0x00000040L
-#define SS_NOPREFIX 0x00000080L
-#define SS_NOTIFY 0x00000100L
-#define SS_CENTERIMAGE 0x00000200L
-#define SS_RIGHTJUST 0x00000400L
-#define SS_REALSIZEIMAGE 0x00000800L
-#define SS_SUNKEN 0x00001000L
-#define SS_EDITCONTROL 0x00002000L
-#define SS_ENDELLIPSIS 0x00004000L
-#define SS_PATHELLIPSIS 0x00008000L
-#define SS_WORDELLIPSIS 0x0000C000L
-#define SS_ELLIPSISMASK 0x0000C000L
-
-#ifndef NOWINMESSAGES
-
-#define STM_SETICON 0x0170
-#define STM_GETICON 0x0171
-#define STM_SETIMAGE 0x0172
-#define STM_GETIMAGE 0x0173
-#define STN_CLICKED 0
-#define STN_DBLCLK 1
-#define STN_ENABLE 2
-#define STN_DISABLE 3
-#define STM_MSGMAX 0x0174
-#endif
-
-#define WC_DIALOG (MAKEINTATOM(0x8002))
-
-#define DWL_MSGRESULT 0
-#define DWL_DLGPROC 4
-#define DWL_USER 8
-
-#ifdef _WIN64
-
-#undef DWL_MSGRESULT
-#undef DWL_DLGPROC
-#undef DWL_USER
-#endif
-
-#define DWLP_MSGRESULT 0
-#define DWLP_DLGPROC DWLP_MSGRESULT + sizeof(LRESULT)
-#define DWLP_USER DWLP_DLGPROC + sizeof(DLGPROC)
-
-#ifndef NOMSG
-
-#ifdef UNICODE
-#define IsDialogMessage IsDialogMessageW
-#else
-#define IsDialogMessage IsDialogMessageA
-#endif
-
-  WINUSERAPI WINBOOL WINAPI IsDialogMessageA(HWND hDlg,LPMSG lpMsg);
-  WINUSERAPI WINBOOL WINAPI IsDialogMessageW(HWND hDlg,LPMSG lpMsg);
-#endif
-
-#ifdef UNICODE
-#define DlgDirList DlgDirListW
-#define DlgDirSelectEx DlgDirSelectExW
-#define DlgDirListComboBox DlgDirListComboBoxW
-#define DlgDirSelectComboBoxEx DlgDirSelectComboBoxExW
-#else
-#define DlgDirList DlgDirListA
-#define DlgDirSelectEx DlgDirSelectExA
-#define DlgDirListComboBox DlgDirListComboBoxA
-#define DlgDirSelectComboBoxEx DlgDirSelectComboBoxExA
-#endif
-
-  WINUSERAPI WINBOOL WINAPI MapDialogRect(HWND hDlg,LPRECT lpRect);
-  WINUSERAPI int WINAPI DlgDirListA(HWND hDlg,LPSTR lpPathSpec,int nIDListBox,int nIDStaticPath,UINT uFileType);
-  WINUSERAPI int WINAPI DlgDirListW(HWND hDlg,LPWSTR lpPathSpec,int nIDListBox,int nIDStaticPath,UINT uFileType);
-
-#define DDL_READWRITE 0x0000
-#define DDL_READONLY 0x0001
-#define DDL_HIDDEN 0x0002
-#define DDL_SYSTEM 0x0004
-#define DDL_DIRECTORY 0x0010
-#define DDL_ARCHIVE 0x0020
-
-#define DDL_POSTMSGS 0x2000
-#define DDL_DRIVES 0x4000
-#define DDL_EXCLUSIVE 0x8000
-
-  WINUSERAPI WINBOOL WINAPI DlgDirSelectExA(HWND hwndDlg,LPSTR lpString,int chCount,int idListBox);
-  WINUSERAPI WINBOOL WINAPI DlgDirSelectExW(HWND hwndDlg,LPWSTR lpString,int chCount,int idListBox);
-  WINUSERAPI int WINAPI DlgDirListComboBoxA(HWND hDlg,LPSTR lpPathSpec,int nIDComboBox,int nIDStaticPath,UINT uFiletype);
-  WINUSERAPI int WINAPI DlgDirListComboBoxW(HWND hDlg,LPWSTR lpPathSpec,int nIDComboBox,int nIDStaticPath,UINT uFiletype);
-  WINUSERAPI WINBOOL WINAPI DlgDirSelectComboBoxExA(HWND hwndDlg,LPSTR lpString,int cchOut,int idComboBox);
-  WINUSERAPI WINBOOL WINAPI DlgDirSelectComboBoxExW(HWND hwndDlg,LPWSTR lpString,int cchOut,int idComboBox);
-
-#define DS_ABSALIGN 0x01L
-#define DS_SYSMODAL 0x02L
-#define DS_LOCALEDIT 0x20L
-#define DS_SETFONT 0x40L
-#define DS_MODALFRAME 0x80L
-#define DS_NOIDLEMSG 0x100L
-#define DS_SETFOREGROUND 0x200L
-
-#define DS_3DLOOK 0x0004L
-#define DS_FIXEDSYS 0x0008L
-#define DS_NOFAILCREATE 0x0010L
-#define DS_CONTROL 0x0400L
-#define DS_CENTER 0x0800L
-#define DS_CENTERMOUSE 0x1000L
-#define DS_CONTEXTHELP 0x2000L
-
-#define DS_SHELLFONT (DS_SETFONT | DS_FIXEDSYS)
-
-#if(_WIN32_WCE >= 0x0500)
-#define DS_USEPIXELS 0x8000L
-#endif
-
-#define DM_GETDEFID (WM_USER+0)
-#define DM_SETDEFID (WM_USER+1)
-#define DM_REPOSITION (WM_USER+2)
-
-#define DC_HASDEFID 0x534B
-
-#define DLGC_WANTARROWS 0x0001
-#define DLGC_WANTTAB 0x0002
-#define DLGC_WANTALLKEYS 0x0004
-#define DLGC_WANTMESSAGE 0x0004
-#define DLGC_HASSETSEL 0x0008
-#define DLGC_DEFPUSHBUTTON 0x0010
-#define DLGC_UNDEFPUSHBUTTON 0x0020
-#define DLGC_RADIOBUTTON 0x0040
-#define DLGC_WANTCHARS 0x0080
-#define DLGC_STATIC 0x0100
-#define DLGC_BUTTON 0x2000
-
-#define LB_CTLCODE 0L
-
-#define LB_OKAY 0
-#define LB_ERR (-1)
-#define LB_ERRSPACE (-2)
-
-#define LBN_ERRSPACE (-2)
-#define LBN_SELCHANGE 1
-#define LBN_DBLCLK 2
-#define LBN_SELCANCEL 3
-#define LBN_SETFOCUS 4
-#define LBN_KILLFOCUS 5
-
-#ifndef NOWINMESSAGES
-
-#define LB_ADDSTRING 0x0180
-#define LB_INSERTSTRING 0x0181
-#define LB_DELETESTRING 0x0182
-#define LB_SELITEMRANGEEX 0x0183
-#define LB_RESETCONTENT 0x0184
-#define LB_SETSEL 0x0185
-#define LB_SETCURSEL 0x0186
-#define LB_GETSEL 0x0187
-#define LB_GETCURSEL 0x0188
-#define LB_GETTEXT 0x0189
-#define LB_GETTEXTLEN 0x018A
-#define LB_GETCOUNT 0x018B
-#define LB_SELECTSTRING 0x018C
-#define LB_DIR 0x018D
-#define LB_GETTOPINDEX 0x018E
-#define LB_FINDSTRING 0x018F
-#define LB_GETSELCOUNT 0x0190
-#define LB_GETSELITEMS 0x0191
-#define LB_SETTABSTOPS 0x0192
-#define LB_GETHORIZONTALEXTENT 0x0193
-#define LB_SETHORIZONTALEXTENT 0x0194
-#define LB_SETCOLUMNWIDTH 0x0195
-#define LB_ADDFILE 0x0196
-#define LB_SETTOPINDEX 0x0197
-#define LB_GETITEMRECT 0x0198
-#define LB_GETITEMDATA 0x0199
-#define LB_SETITEMDATA 0x019A
-#define LB_SELITEMRANGE 0x019B
-#define LB_SETANCHORINDEX 0x019C
-#define LB_GETANCHORINDEX 0x019D
-#define LB_SETCARETINDEX 0x019E
-#define LB_GETCARETINDEX 0x019F
-#define LB_SETITEMHEIGHT 0x01A0
-#define LB_GETITEMHEIGHT 0x01A1
-#define LB_FINDSTRINGEXACT 0x01A2
-#define LB_SETLOCALE 0x01A5
-#define LB_GETLOCALE 0x01A6
-#define LB_SETCOUNT 0x01A7
-#define LB_INITSTORAGE 0x01A8
-#define LB_ITEMFROMPOINT 0x01A9
-#if(_WIN32_WCE >= 0x0400)
-#define LB_MULTIPLEADDSTRING 0x01B1
-#endif
-#define LB_GETLISTBOXINFO 0x01B2
-#define LB_MSGMAX 0x01B3
-#endif
-
-#ifndef NOWINSTYLES
-
-#define LBS_NOTIFY 0x0001L
-#define LBS_SORT 0x0002L
-#define LBS_NOREDRAW 0x0004L
-#define LBS_MULTIPLESEL 0x0008L
-#define LBS_OWNERDRAWFIXED 0x0010L
-#define LBS_OWNERDRAWVARIABLE 0x0020L
-#define LBS_HASSTRINGS 0x0040L
-#define LBS_USETABSTOPS 0x0080L
-#define LBS_NOINTEGRALHEIGHT 0x0100L
-#define LBS_MULTICOLUMN 0x0200L
-#define LBS_WANTKEYBOARDINPUT 0x0400L
-#define LBS_EXTENDEDSEL 0x0800L
-#define LBS_DISABLENOSCROLL 0x1000L
-#define LBS_NODATA 0x2000L
-#define LBS_NOSEL 0x4000L
-#define LBS_COMBOBOX 0x8000L
-
-#define LBS_STANDARD (LBS_NOTIFY | LBS_SORT | WS_VSCROLL | WS_BORDER)
-#endif
-
-#define CB_OKAY 0
-#define CB_ERR (-1)
-#define CB_ERRSPACE (-2)
-
-#define CBN_ERRSPACE (-1)
-#define CBN_SELCHANGE 1
-#define CBN_DBLCLK 2
-#define CBN_SETFOCUS 3
-#define CBN_KILLFOCUS 4
-#define CBN_EDITCHANGE 5
-#define CBN_EDITUPDATE 6
-#define CBN_DROPDOWN 7
-#define CBN_CLOSEUP 8
-#define CBN_SELENDOK 9
-#define CBN_SELENDCANCEL 10
-
-#ifndef NOWINSTYLES
-
-#define CBS_SIMPLE 0x0001L
-#define CBS_DROPDOWN 0x0002L
-#define CBS_DROPDOWNLIST 0x0003L
-#define CBS_OWNERDRAWFIXED 0x0010L
-#define CBS_OWNERDRAWVARIABLE 0x0020L
-#define CBS_AUTOHSCROLL 0x0040L
-#define CBS_OEMCONVERT 0x0080L
-#define CBS_SORT 0x0100L
-#define CBS_HASSTRINGS 0x0200L
-#define CBS_NOINTEGRALHEIGHT 0x0400L
-#define CBS_DISABLENOSCROLL 0x0800L
-#define CBS_UPPERCASE 0x2000L
-#define CBS_LOWERCASE 0x4000L
-#endif
-
-#ifndef NOWINMESSAGES
-#define CB_GETEDITSEL 0x0140
-#define CB_LIMITTEXT 0x0141
-#define CB_SETEDITSEL 0x0142
-#define CB_ADDSTRING 0x0143
-#define CB_DELETESTRING 0x0144
-#define CB_DIR 0x0145
-#define CB_GETCOUNT 0x0146
-#define CB_GETCURSEL 0x0147
-#define CB_GETLBTEXT 0x0148
-#define CB_GETLBTEXTLEN 0x0149
-#define CB_INSERTSTRING 0x014A
-#define CB_RESETCONTENT 0x014B
-#define CB_FINDSTRING 0x014C
-#define CB_SELECTSTRING 0x014D
-#define CB_SETCURSEL 0x014E
-#define CB_SHOWDROPDOWN 0x014F
-#define CB_GETITEMDATA 0x0150
-#define CB_SETITEMDATA 0x0151
-#define CB_GETDROPPEDCONTROLRECT 0x0152
-#define CB_SETITEMHEIGHT 0x0153
-#define CB_GETITEMHEIGHT 0x0154
-#define CB_SETEXTENDEDUI 0x0155
-#define CB_GETEXTENDEDUI 0x0156
-#define CB_GETDROPPEDSTATE 0x0157
-#define CB_FINDSTRINGEXACT 0x0158
-#define CB_SETLOCALE 0x0159
-#define CB_GETLOCALE 0x015A
-#define CB_GETTOPINDEX 0x015b
-#define CB_SETTOPINDEX 0x015c
-#define CB_GETHORIZONTALEXTENT 0x015d
-#define CB_SETHORIZONTALEXTENT 0x015e
-#define CB_GETDROPPEDWIDTH 0x015f
-#define CB_SETDROPPEDWIDTH 0x0160
-#define CB_INITSTORAGE 0x0161
-#if(_WIN32_WCE >= 0x0400)
-#define CB_MULTIPLEADDSTRING 0x0163
-#endif
-#define CB_GETCOMBOBOXINFO 0x0164
-#define CB_MSGMAX 0x0165
-#endif
-
-#ifndef NOWINSTYLES
-
-#define SBS_HORZ 0x0000L
-#define SBS_VERT 0x0001L
-#define SBS_TOPALIGN 0x0002L
-#define SBS_LEFTALIGN 0x0002L
-#define SBS_BOTTOMALIGN 0x0004L
-#define SBS_RIGHTALIGN 0x0004L
-#define SBS_SIZEBOXTOPLEFTALIGN 0x0002L
-#define SBS_SIZEBOXBOTTOMRIGHTALIGN 0x0004L
-#define SBS_SIZEBOX 0x0008L
-#define SBS_SIZEGRIP 0x0010L
-#endif
-
-#ifndef NOWINMESSAGES
-#define SBM_SETPOS 0x00E0
-#define SBM_GETPOS 0x00E1
-#define SBM_SETRANGE 0x00E2
-#define SBM_SETRANGEREDRAW 0x00E6
-#define SBM_GETRANGE 0x00E3
-#define SBM_ENABLE_ARROWS 0x00E4
-#define SBM_SETSCROLLINFO 0x00E9
-#define SBM_GETSCROLLINFO 0x00EA
-#define SBM_GETSCROLLBARINFO 0x00EB
-
-#define SIF_RANGE 0x0001
-#define SIF_PAGE 0x0002
-#define SIF_POS 0x0004
-#define SIF_DISABLENOSCROLL 0x0008
-#define SIF_TRACKPOS 0x0010
-#define SIF_ALL (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS)
-
-  typedef struct tagSCROLLINFO {
-    UINT cbSize;
-    UINT fMask;
-    int nMin;
-    int nMax;
-    UINT nPage;
-    int nPos;
-    int nTrackPos;
-  } SCROLLINFO,*LPSCROLLINFO;
-  typedef SCROLLINFO CONST *LPCSCROLLINFO;
-
-  WINUSERAPI int WINAPI SetScrollInfo(HWND hwnd,int nBar,LPCSCROLLINFO lpsi,WINBOOL redraw);
-  WINUSERAPI WINBOOL WINAPI GetScrollInfo(HWND hwnd,int nBar,LPSCROLLINFO lpsi);
-#endif
-#endif
-
-#ifndef NOMDI
-
-#define MDIS_ALLCHILDSTYLES 0x0001
-
-#define MDITILE_VERTICAL 0x0000
-#define MDITILE_HORIZONTAL 0x0001
-#define MDITILE_SKIPDISABLED 0x0002
-#define MDITILE_ZORDER 0x0004
-
-  typedef struct tagMDICREATESTRUCTA {
-    LPCSTR szClass;
-    LPCSTR szTitle;
-    HANDLE hOwner;
-    int x;
-    int y;
-    int cx;
-    int cy;
-    DWORD style;
-    LPARAM lParam;
-  } MDICREATESTRUCTA,*LPMDICREATESTRUCTA;
-
-  typedef struct tagMDICREATESTRUCTW {
-    LPCWSTR szClass;
-    LPCWSTR szTitle;
-    HANDLE hOwner;
-    int x;
-    int y;
-    int cx;
-    int cy;
-    DWORD style;
-    LPARAM lParam;
-  } MDICREATESTRUCTW,*LPMDICREATESTRUCTW;
-
-#ifdef UNICODE
-  typedef MDICREATESTRUCTW MDICREATESTRUCT;
-  typedef LPMDICREATESTRUCTW LPMDICREATESTRUCT;
-#else
-  typedef MDICREATESTRUCTA MDICREATESTRUCT;
-  typedef LPMDICREATESTRUCTA LPMDICREATESTRUCT;
-#endif
-
-  typedef struct tagCLIENTCREATESTRUCT {
-    HANDLE hWindowMenu;
-    UINT idFirstChild;
-  } CLIENTCREATESTRUCT,*LPCLIENTCREATESTRUCT;
-
-#ifdef UNICODE
-#define DefFrameProc DefFrameProcW
-#define DefMDIChildProc DefMDIChildProcW
-#define CreateMDIWindow CreateMDIWindowW
-#else
-#define DefFrameProc DefFrameProcA
-#define DefMDIChildProc DefMDIChildProcA
-#define CreateMDIWindow CreateMDIWindowA
-#endif
-
-  WINUSERAPI LRESULT WINAPI DefFrameProcA(HWND hWnd,HWND hWndMDIClient,UINT uMsg,WPARAM wParam,LPARAM lParam);
-  WINUSERAPI LRESULT WINAPI DefFrameProcW(HWND hWnd,HWND hWndMDIClient,UINT uMsg,WPARAM wParam,LPARAM lParam);
-  WINUSERAPI LRESULT WINAPI DefMDIChildProcA(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
-  WINUSERAPI LRESULT WINAPI DefMDIChildProcW(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
-
-#ifndef NOMSG
-  WINUSERAPI WINBOOL WINAPI TranslateMDISysAccel(HWND hWndClient,LPMSG lpMsg);
-#endif
-
-  WINUSERAPI UINT WINAPI ArrangeIconicWindows(HWND hWnd);
-  WINUSERAPI HWND WINAPI CreateMDIWindowA(LPCSTR lpClassName,LPCSTR lpWindowName,DWORD dwStyle,int X,int Y,int nWidth,int nHeight,HWND hWndParent,HINSTANCE hInstance,LPARAM lParam);
-  WINUSERAPI HWND WINAPI CreateMDIWindowW(LPCWSTR lpClassName,LPCWSTR lpWindowName,DWORD dwStyle,int X,int Y,int nWidth,int nHeight,HWND hWndParent,HINSTANCE hInstance,LPARAM lParam);
-  WINUSERAPI WORD WINAPI TileWindows(HWND hwndParent,UINT wHow,CONST RECT *lpRect,UINT cKids,const HWND *lpKids);
-  WINUSERAPI WORD WINAPI CascadeWindows(HWND hwndParent,UINT wHow,CONST RECT *lpRect,UINT cKids,const HWND *lpKids);
-#endif
-#endif
-
-#ifndef NOHELP
-
-  typedef DWORD HELPPOLY;
-  typedef struct tagMULTIKEYHELPA {
-    DWORD mkSize;
-    CHAR mkKeylist;
-    CHAR szKeyphrase[1];
-  } MULTIKEYHELPA,*PMULTIKEYHELPA,*LPMULTIKEYHELPA;
-
-  typedef struct tagMULTIKEYHELPW {
-    DWORD mkSize;
-    WCHAR mkKeylist;
-    WCHAR szKeyphrase[1];
-  } MULTIKEYHELPW,*PMULTIKEYHELPW,*LPMULTIKEYHELPW;
-
-#ifdef UNICODE
-  typedef MULTIKEYHELPW MULTIKEYHELP;
-  typedef PMULTIKEYHELPW PMULTIKEYHELP;
-  typedef LPMULTIKEYHELPW LPMULTIKEYHELP;
-#else
-  typedef MULTIKEYHELPA MULTIKEYHELP;
-  typedef PMULTIKEYHELPA PMULTIKEYHELP;
-  typedef LPMULTIKEYHELPA LPMULTIKEYHELP;
-#endif
-
-  typedef struct tagHELPWININFOA {
-    int wStructSize;
-    int x;
-    int y;
-    int dx;
-    int dy;
-    int wMax;
-    CHAR rgchMember[2];
-  } HELPWININFOA,*PHELPWININFOA,*LPHELPWININFOA;
-
-  typedef struct tagHELPWININFOW {
-    int wStructSize;
-    int x;
-    int y;
-    int dx;
-    int dy;
-    int wMax;
-    WCHAR rgchMember[2];
-  } HELPWININFOW,*PHELPWININFOW,*LPHELPWININFOW;
-
-#ifdef UNICODE
-  typedef HELPWININFOW HELPWININFO;
-  typedef PHELPWININFOW PHELPWININFO;
-  typedef LPHELPWININFOW LPHELPWININFO;
-#else
-  typedef HELPWININFOA HELPWININFO;
-  typedef PHELPWININFOA PHELPWININFO;
-  typedef LPHELPWININFOA LPHELPWININFO;
-#endif
-
-#define HELP_CONTEXT 0x0001L
-#define HELP_QUIT 0x0002L
-#define HELP_INDEX 0x0003L
-#define HELP_CONTENTS 0x0003L
-#define HELP_HELPONHELP 0x0004L
-#define HELP_SETINDEX 0x0005L
-#define HELP_SETCONTENTS 0x0005L
-#define HELP_CONTEXTPOPUP 0x0008L
-#define HELP_FORCEFILE 0x0009L
-#define HELP_KEY 0x0101L
-#define HELP_COMMAND 0x0102L
-#define HELP_PARTIALKEY 0x0105L
-#define HELP_MULTIKEY 0x0201L
-#define HELP_SETWINPOS 0x0203L
-#define HELP_CONTEXTMENU 0x000a
-#define HELP_FINDER 0x000b
-#define HELP_WM_HELP 0x000c
-#define HELP_SETPOPUP_POS 0x000d
-
-#define HELP_TCARD 0x8000
-#define HELP_TCARD_DATA 0x0010
-#define HELP_TCARD_OTHER_CALLER 0x0011
-
-#define IDH_NO_HELP 28440
-#define IDH_MISSING_CONTEXT 28441
-#define IDH_GENERIC_HELP_BUTTON 28442
-#define IDH_OK 28443
-#define IDH_CANCEL 28444
-#define IDH_HELP 28445
-
-#ifdef UNICODE
-#define WinHelp WinHelpW
-#else
-#define WinHelp WinHelpA
-#endif
-
-  WINUSERAPI WINBOOL WINAPI WinHelpA(HWND hWndMain,LPCSTR lpszHelp,UINT uCommand,ULONG_PTR dwData);
-  WINUSERAPI WINBOOL WINAPI WinHelpW(HWND hWndMain,LPCWSTR lpszHelp,UINT uCommand,ULONG_PTR dwData);
-#endif
-
-#define GR_GDIOBJECTS 0
-#define GR_USEROBJECTS 1
-
-  WINUSERAPI DWORD WINAPI GetGuiResources(HANDLE hProcess,DWORD uiFlags);
-
-#ifndef NOSYSPARAMSINFO
-
-#define SPI_GETBEEP 0x0001
-#define SPI_SETBEEP 0x0002
-#define SPI_GETMOUSE 0x0003
-#define SPI_SETMOUSE 0x0004
-#define SPI_GETBORDER 0x0005
-#define SPI_SETBORDER 0x0006
-#define SPI_GETKEYBOARDSPEED 0x000A
-#define SPI_SETKEYBOARDSPEED 0x000B
-#define SPI_LANGDRIVER 0x000C
-#define SPI_ICONHORIZONTALSPACING 0x000D
-#define SPI_GETSCREENSAVETIMEOUT 0x000E
-#define SPI_SETSCREENSAVETIMEOUT 0x000F
-#define SPI_GETSCREENSAVEACTIVE 0x0010
-#define SPI_SETSCREENSAVEACTIVE 0x0011
-#define SPI_GETGRIDGRANULARITY 0x0012
-#define SPI_SETGRIDGRANULARITY 0x0013
-#define SPI_SETDESKWALLPAPER 0x0014
-#define SPI_SETDESKPATTERN 0x0015
-#define SPI_GETKEYBOARDDELAY 0x0016
-#define SPI_SETKEYBOARDDELAY 0x0017
-#define SPI_ICONVERTICALSPACING 0x0018
-#define SPI_GETICONTITLEWRAP 0x0019
-#define SPI_SETICONTITLEWRAP 0x001A
-#define SPI_GETMENUDROPALIGNMENT 0x001B
-#define SPI_SETMENUDROPALIGNMENT 0x001C
-#define SPI_SETDOUBLECLKWIDTH 0x001D
-#define SPI_SETDOUBLECLKHEIGHT 0x001E
-#define SPI_GETICONTITLELOGFONT 0x001F
-#define SPI_SETDOUBLECLICKTIME 0x0020
-#define SPI_SETMOUSEBUTTONSWAP 0x0021
-#define SPI_SETICONTITLELOGFONT 0x0022
-#define SPI_GETFASTTASKSWITCH 0x0023
-#define SPI_SETFASTTASKSWITCH 0x0024
-#define SPI_SETDRAGFULLWINDOWS 0x0025
-#define SPI_GETDRAGFULLWINDOWS 0x0026
-#define SPI_GETNONCLIENTMETRICS 0x0029
-#define SPI_SETNONCLIENTMETRICS 0x002A
-#define SPI_GETMINIMIZEDMETRICS 0x002B
-#define SPI_SETMINIMIZEDMETRICS 0x002C
-#define SPI_GETICONMETRICS 0x002D
-#define SPI_SETICONMETRICS 0x002E
-#define SPI_SETWORKAREA 0x002F
-#define SPI_GETWORKAREA 0x0030
-#define SPI_SETPENWINDOWS 0x0031
-
-#define SPI_GETHIGHCONTRAST 0x0042
-#define SPI_SETHIGHCONTRAST 0x0043
-#define SPI_GETKEYBOARDPREF 0x0044
-#define SPI_SETKEYBOARDPREF 0x0045
-#define SPI_GETSCREENREADER 0x0046
-#define SPI_SETSCREENREADER 0x0047
-#define SPI_GETANIMATION 0x0048
-#define SPI_SETANIMATION 0x0049
-#define SPI_GETFONTSMOOTHING 0x004A
-#define SPI_SETFONTSMOOTHING 0x004B
-#define SPI_SETDRAGWIDTH 0x004C
-#define SPI_SETDRAGHEIGHT 0x004D
-#define SPI_SETHANDHELD 0x004E
-#define SPI_GETLOWPOWERTIMEOUT 0x004F
-#define SPI_GETPOWEROFFTIMEOUT 0x0050
-#define SPI_SETLOWPOWERTIMEOUT 0x0051
-#define SPI_SETPOWEROFFTIMEOUT 0x0052
-#define SPI_GETLOWPOWERACTIVE 0x0053
-#define SPI_GETPOWEROFFACTIVE 0x0054
-#define SPI_SETLOWPOWERACTIVE 0x0055
-#define SPI_SETPOWEROFFACTIVE 0x0056
-#define SPI_SETCURSORS 0x0057
-#define SPI_SETICONS 0x0058
-#define SPI_GETDEFAULTINPUTLANG 0x0059
-#define SPI_SETDEFAULTINPUTLANG 0x005A
-#define SPI_SETLANGTOGGLE 0x005B
-#define SPI_GETWINDOWSEXTENSION 0x005C
-#define SPI_SETMOUSETRAILS 0x005D
-#define SPI_GETMOUSETRAILS 0x005E
-#define SPI_SETSCREENSAVERRUNNING 0x0061
-#define SPI_SCREENSAVERRUNNING SPI_SETSCREENSAVERRUNNING
-#define SPI_GETFILTERKEYS 0x0032
-#define SPI_SETFILTERKEYS 0x0033
-#define SPI_GETTOGGLEKEYS 0x0034
-#define SPI_SETTOGGLEKEYS 0x0035
-#define SPI_GETMOUSEKEYS 0x0036
-#define SPI_SETMOUSEKEYS 0x0037
-#define SPI_GETSHOWSOUNDS 0x0038
-#define SPI_SETSHOWSOUNDS 0x0039
-#define SPI_GETSTICKYKEYS 0x003A
-#define SPI_SETSTICKYKEYS 0x003B
-#define SPI_GETACCESSTIMEOUT 0x003C
-#define SPI_SETACCESSTIMEOUT 0x003D
-#define SPI_GETSERIALKEYS 0x003E
-#define SPI_SETSERIALKEYS 0x003F
-#define SPI_GETSOUNDSENTRY 0x0040
-#define SPI_SETSOUNDSENTRY 0x0041
-#define SPI_GETSNAPTODEFBUTTON 0x005F
-#define SPI_SETSNAPTODEFBUTTON 0x0060
-#define SPI_GETMOUSEHOVERWIDTH 0x0062
-#define SPI_SETMOUSEHOVERWIDTH 0x0063
-#define SPI_GETMOUSEHOVERHEIGHT 0x0064
-#define SPI_SETMOUSEHOVERHEIGHT 0x0065
-#define SPI_GETMOUSEHOVERTIME 0x0066
-#define SPI_SETMOUSEHOVERTIME 0x0067
-#define SPI_GETWHEELSCROLLLINES 0x0068
-#define SPI_SETWHEELSCROLLLINES 0x0069
-#define SPI_GETMENUSHOWDELAY 0x006A
-#define SPI_SETMENUSHOWDELAY 0x006B
-#define SPI_GETSHOWIMEUI 0x006E
-#define SPI_SETSHOWIMEUI 0x006F
-#define SPI_GETMOUSESPEED 0x0070
-#define SPI_SETMOUSESPEED 0x0071
-#define SPI_GETSCREENSAVERRUNNING 0x0072
-#define SPI_GETDESKWALLPAPER 0x0073
-
-#define SPI_GETACTIVEWINDOWTRACKING 0x1000
-#define SPI_SETACTIVEWINDOWTRACKING 0x1001
-#define SPI_GETMENUANIMATION 0x1002
-#define SPI_SETMENUANIMATION 0x1003
-#define SPI_GETCOMBOBOXANIMATION 0x1004
-#define SPI_SETCOMBOBOXANIMATION 0x1005
-#define SPI_GETLISTBOXSMOOTHSCROLLING 0x1006
-#define SPI_SETLISTBOXSMOOTHSCROLLING 0x1007
-#define SPI_GETGRADIENTCAPTIONS 0x1008
-#define SPI_SETGRADIENTCAPTIONS 0x1009
-#define SPI_GETKEYBOARDCUES 0x100A
-#define SPI_SETKEYBOARDCUES 0x100B
-#define SPI_GETMENUUNDERLINES SPI_GETKEYBOARDCUES
-#define SPI_SETMENUUNDERLINES SPI_SETKEYBOARDCUES
-#define SPI_GETACTIVEWNDTRKZORDER 0x100C
-#define SPI_SETACTIVEWNDTRKZORDER 0x100D
-#define SPI_GETHOTTRACKING 0x100E
-#define SPI_SETHOTTRACKING 0x100F
-#define SPI_GETMENUFADE 0x1012
-#define SPI_SETMENUFADE 0x1013
-#define SPI_GETSELECTIONFADE 0x1014
-#define SPI_SETSELECTIONFADE 0x1015
-#define SPI_GETTOOLTIPANIMATION 0x1016
-#define SPI_SETTOOLTIPANIMATION 0x1017
-#define SPI_GETTOOLTIPFADE 0x1018
-#define SPI_SETTOOLTIPFADE 0x1019
-#define SPI_GETCURSORSHADOW 0x101A
-#define SPI_SETCURSORSHADOW 0x101B
-#define SPI_GETMOUSESONAR 0x101C
-#define SPI_SETMOUSESONAR 0x101D
-#define SPI_GETMOUSECLICKLOCK 0x101E
-#define SPI_SETMOUSECLICKLOCK 0x101F
-#define SPI_GETMOUSEVANISH 0x1020
-#define SPI_SETMOUSEVANISH 0x1021
-#define SPI_GETFLATMENU 0x1022
-#define SPI_SETFLATMENU 0x1023
-#define SPI_GETDROPSHADOW 0x1024
-#define SPI_SETDROPSHADOW 0x1025
-#define SPI_GETBLOCKSENDINPUTRESETS 0x1026
-#define SPI_SETBLOCKSENDINPUTRESETS 0x1027
-#define SPI_GETUIEFFECTS 0x103E
-#define SPI_SETUIEFFECTS 0x103F
-#define SPI_GETFOREGROUNDLOCKTIMEOUT 0x2000
-#define SPI_SETFOREGROUNDLOCKTIMEOUT 0x2001
-#define SPI_GETACTIVEWNDTRKTIMEOUT 0x2002
-#define SPI_SETACTIVEWNDTRKTIMEOUT 0x2003
-#define SPI_GETFOREGROUNDFLASHCOUNT 0x2004
-#define SPI_SETFOREGROUNDFLASHCOUNT 0x2005
-#define SPI_GETCARETWIDTH 0x2006
-#define SPI_SETCARETWIDTH 0x2007
-#define SPI_GETMOUSECLICKLOCKTIME 0x2008
-#define SPI_SETMOUSECLICKLOCKTIME 0x2009
-#define SPI_GETFONTSMOOTHINGTYPE 0x200A
-#define SPI_SETFONTSMOOTHINGTYPE 0x200B
-
-#define FE_FONTSMOOTHINGSTANDARD 0x0001
-#define FE_FONTSMOOTHINGCLEARTYPE 0x0002
-#define FE_FONTSMOOTHINGDOCKING 0x8000
-
-#define SPI_GETFONTSMOOTHINGCONTRAST 0x200C
-#define SPI_SETFONTSMOOTHINGCONTRAST 0x200D
-#define SPI_GETFOCUSBORDERWIDTH 0x200E
-#define SPI_SETFOCUSBORDERWIDTH 0x200F
-#define SPI_GETFOCUSBORDERHEIGHT 0x2010
-#define SPI_SETFOCUSBORDERHEIGHT 0x2011
-#define SPI_GETFONTSMOOTHINGORIENTATION 0x2012
-#define SPI_SETFONTSMOOTHINGORIENTATION 0x2013
-
-#define FE_FONTSMOOTHINGORIENTATIONBGR 0x0000
-#define FE_FONTSMOOTHINGORIENTATIONRGB 0x0001
-
-#define SPIF_UPDATEINIFILE 0x0001
-#define SPIF_SENDWININICHANGE 0x0002
-#define SPIF_SENDCHANGE SPIF_SENDWININICHANGE
-
-#define METRICS_USEDEFAULT -1
-#ifdef _WINGDI_
-#ifndef NOGDI
-  typedef struct tagNONCLIENTMETRICSA {
-    UINT cbSize;
-    int iBorderWidth;
-    int iScrollWidth;
-    int iScrollHeight;
-    int iCaptionWidth;
-    int iCaptionHeight;
-    LOGFONTA lfCaptionFont;
-    int iSmCaptionWidth;
-    int iSmCaptionHeight;
-    LOGFONTA lfSmCaptionFont;
-    int iMenuWidth;
-    int iMenuHeight;
-    LOGFONTA lfMenuFont;
-    LOGFONTA lfStatusFont;
-    LOGFONTA lfMessageFont;
-  } NONCLIENTMETRICSA,*PNONCLIENTMETRICSA,*LPNONCLIENTMETRICSA;
-
-  typedef struct tagNONCLIENTMETRICSW {
-    UINT cbSize;
-    int iBorderWidth;
-    int iScrollWidth;
-    int iScrollHeight;
-    int iCaptionWidth;
-    int iCaptionHeight;
-    LOGFONTW lfCaptionFont;
-    int iSmCaptionWidth;
-    int iSmCaptionHeight;
-    LOGFONTW lfSmCaptionFont;
-    int iMenuWidth;
-    int iMenuHeight;
-    LOGFONTW lfMenuFont;
-    LOGFONTW lfStatusFont;
-    LOGFONTW lfMessageFont;
-  } NONCLIENTMETRICSW,*PNONCLIENTMETRICSW,*LPNONCLIENTMETRICSW;
-
-#ifdef UNICODE
-  typedef NONCLIENTMETRICSW NONCLIENTMETRICS;
-  typedef PNONCLIENTMETRICSW PNONCLIENTMETRICS;
-  typedef LPNONCLIENTMETRICSW LPNONCLIENTMETRICS;
-#else
-  typedef NONCLIENTMETRICSA NONCLIENTMETRICS;
-  typedef PNONCLIENTMETRICSA PNONCLIENTMETRICS;
-  typedef LPNONCLIENTMETRICSA LPNONCLIENTMETRICS;
-#endif
-#endif
-#endif
-
-#define ARW_BOTTOMLEFT 0x0000L
-#define ARW_BOTTOMRIGHT 0x0001L
-#define ARW_TOPLEFT 0x0002L
-#define ARW_TOPRIGHT 0x0003L
-#define ARW_STARTMASK 0x0003L
-#define ARW_STARTRIGHT 0x0001L
-#define ARW_STARTTOP 0x0002L
-
-#define ARW_LEFT 0x0000L
-#define ARW_RIGHT 0x0000L
-#define ARW_UP 0x0004L
-#define ARW_DOWN 0x0004L
-#define ARW_HIDE 0x0008L
-
-  typedef struct tagMINIMIZEDMETRICS {
-    UINT cbSize;
-    int iWidth;
-    int iHorzGap;
-    int iVertGap;
-    int iArrange;
-  } MINIMIZEDMETRICS,*PMINIMIZEDMETRICS,*LPMINIMIZEDMETRICS;
-
-#ifdef _WINGDI_
-#ifndef NOGDI
-  typedef struct tagICONMETRICSA {
-    UINT cbSize;
-    int iHorzSpacing;
-    int iVertSpacing;
-    int iTitleWrap;
-    LOGFONTA lfFont;
-  } ICONMETRICSA,*PICONMETRICSA,*LPICONMETRICSA;
-
-  typedef struct tagICONMETRICSW {
-    UINT cbSize;
-    int iHorzSpacing;
-    int iVertSpacing;
-    int iTitleWrap;
-    LOGFONTW lfFont;
-  } ICONMETRICSW,*PICONMETRICSW,*LPICONMETRICSW;
-
-#ifdef UNICODE
-  typedef ICONMETRICSW ICONMETRICS;
-  typedef PICONMETRICSW PICONMETRICS;
-  typedef LPICONMETRICSW LPICONMETRICS;
-#else
-  typedef ICONMETRICSA ICONMETRICS;
-  typedef PICONMETRICSA PICONMETRICS;
-  typedef LPICONMETRICSA LPICONMETRICS;
-#endif
-#endif
-#endif
-
-  typedef struct tagANIMATIONINFO {
-    UINT cbSize;
-    int iMinAnimate;
-  } ANIMATIONINFO,*LPANIMATIONINFO;
-
-  typedef struct tagSERIALKEYSA {
-    UINT cbSize;
-    DWORD dwFlags;
-    LPSTR lpszActivePort;
-    LPSTR lpszPort;
-    UINT iBaudRate;
-    UINT iPortState;
-    UINT iActive;
-  } SERIALKEYSA,*LPSERIALKEYSA;
-
-  typedef struct tagSERIALKEYSW {
-    UINT cbSize;
-    DWORD dwFlags;
-    LPWSTR lpszActivePort;
-    LPWSTR lpszPort;
-    UINT iBaudRate;
-    UINT iPortState;
-    UINT iActive;
-  } SERIALKEYSW,*LPSERIALKEYSW;
-
-#ifdef UNICODE
-  typedef SERIALKEYSW SERIALKEYS;
-  typedef LPSERIALKEYSW LPSERIALKEYS;
-#else
-  typedef SERIALKEYSA SERIALKEYS;
-  typedef LPSERIALKEYSA LPSERIALKEYS;
-#endif
-
-#define SERKF_SERIALKEYSON 0x00000001
-#define SERKF_AVAILABLE 0x00000002
-#define SERKF_INDICATOR 0x00000004
-
-  typedef struct tagHIGHCONTRASTA {
-    UINT cbSize;
-    DWORD dwFlags;
-    LPSTR lpszDefaultScheme;
-  } HIGHCONTRASTA,*LPHIGHCONTRASTA;
-
-  typedef struct tagHIGHCONTRASTW {
-    UINT cbSize;
-    DWORD dwFlags;
-    LPWSTR lpszDefaultScheme;
-  } HIGHCONTRASTW,*LPHIGHCONTRASTW;
-
-#ifdef UNICODE
-  typedef HIGHCONTRASTW HIGHCONTRAST;
-  typedef LPHIGHCONTRASTW LPHIGHCONTRAST;
-#else
-  typedef HIGHCONTRASTA HIGHCONTRAST;
-  typedef LPHIGHCONTRASTA LPHIGHCONTRAST;
-#endif
-
-#define HCF_HIGHCONTRASTON 0x00000001
-#define HCF_AVAILABLE 0x00000002
-#define HCF_HOTKEYACTIVE 0x00000004
-#define HCF_CONFIRMHOTKEY 0x00000008
-#define HCF_HOTKEYSOUND 0x00000010
-#define HCF_INDICATOR 0x00000020
-#define HCF_HOTKEYAVAILABLE 0x00000040
-#define HCF_LOGONDESKTOP 0x00000100
-#define HCF_DEFAULTDESKTOP 0x00000200
-
-#define CDS_UPDATEREGISTRY 0x00000001
-#define CDS_TEST 0x00000002
-#define CDS_FULLSCREEN 0x00000004
-#define CDS_GLOBAL 0x00000008
-#define CDS_SET_PRIMARY 0x00000010
-#define CDS_VIDEOPARAMETERS 0x00000020
-#define CDS_RESET 0x40000000
-#define CDS_NORESET 0x10000000
-
-//gr #include <tvout.h>
-
-#define DISP_CHANGE_SUCCESSFUL 0
-#define DISP_CHANGE_RESTART 1
-#define DISP_CHANGE_FAILED -1
-#define DISP_CHANGE_BADMODE -2
-#define DISP_CHANGE_NOTUPDATED -3
-#define DISP_CHANGE_BADFLAGS -4
-#define DISP_CHANGE_BADPARAM -5
-#define DISP_CHANGE_BADDUALVIEW -6
-
-#ifdef _WINGDI_
-#ifndef NOGDI
-
-#ifdef UNICODE
-#define ChangeDisplaySettings ChangeDisplaySettingsW
-#define ChangeDisplaySettingsEx ChangeDisplaySettingsExW
-#define EnumDisplaySettings EnumDisplaySettingsW
-#define EnumDisplaySettingsEx EnumDisplaySettingsExW
-#define EnumDisplayDevices EnumDisplayDevicesW
-#else
-#define ChangeDisplaySettings ChangeDisplaySettingsA
-#define ChangeDisplaySettingsEx ChangeDisplaySettingsExA
-#define EnumDisplaySettings EnumDisplaySettingsA
-#define EnumDisplaySettingsEx EnumDisplaySettingsExA
-#define EnumDisplayDevices EnumDisplayDevicesA
-#endif
-
-  WINUSERAPI LONG WINAPI ChangeDisplaySettingsA(LPDEVMODEA lpDevMode,DWORD dwFlags);
-  WINUSERAPI LONG WINAPI ChangeDisplaySettingsW(LPDEVMODEW lpDevMode,DWORD dwFlags);
-  WINUSERAPI LONG WINAPI ChangeDisplaySettingsExA(LPCSTR lpszDeviceName,LPDEVMODEA lpDevMode,HWND hwnd,DWORD dwflags,LPVOID lParam);
-  WINUSERAPI LONG WINAPI ChangeDisplaySettingsExW(LPCWSTR lpszDeviceName,LPDEVMODEW lpDevMode,HWND hwnd,DWORD dwflags,LPVOID lParam);
-
-#define ENUM_CURRENT_SETTINGS ((DWORD)-1)
-#define ENUM_REGISTRY_SETTINGS ((DWORD)-2)
-
-  WINUSERAPI WINBOOL WINAPI EnumDisplaySettingsA(LPCSTR lpszDeviceName,DWORD iModeNum,LPDEVMODEA lpDevMode);
-  WINUSERAPI WINBOOL WINAPI EnumDisplaySettingsW(LPCWSTR lpszDeviceName,DWORD iModeNum,LPDEVMODEW lpDevMode);
-  WINUSERAPI WINBOOL WINAPI EnumDisplaySettingsExA(LPCSTR lpszDeviceName,DWORD iModeNum,LPDEVMODEA lpDevMode,DWORD dwFlags);
-  WINUSERAPI WINBOOL WINAPI EnumDisplaySettingsExW(LPCWSTR lpszDeviceName,DWORD iModeNum,LPDEVMODEW lpDevMode,DWORD dwFlags);
-
-#define EDS_RAWMODE 0x00000002
-
-  WINUSERAPI WINBOOL WINAPI EnumDisplayDevicesA(LPCSTR lpDevice,DWORD iDevNum,PDISPLAY_DEVICEA lpDisplayDevice,DWORD dwFlags);
-  WINUSERAPI WINBOOL WINAPI EnumDisplayDevicesW(LPCWSTR lpDevice,DWORD iDevNum,PDISPLAY_DEVICEW lpDisplayDevice,DWORD dwFlags);
-#endif
-#endif
-
-#ifdef UNICODE
-#define SystemParametersInfo SystemParametersInfoW
-#else
-#define SystemParametersInfo SystemParametersInfoA
-#endif
-
-  WINUSERAPI WINBOOL WINAPI SystemParametersInfoA(UINT uiAction,UINT uiParam,PVOID pvParam,UINT fWinIni);
-  WINUSERAPI WINBOOL WINAPI SystemParametersInfoW(UINT uiAction,UINT uiParam,PVOID pvParam,UINT fWinIni);
-#endif
-
-  typedef struct tagFILTERKEYS {
-    UINT cbSize;
-    DWORD dwFlags;
-    DWORD iWaitMSec;
-    DWORD iDelayMSec;
-    DWORD iRepeatMSec;
-    DWORD iBounceMSec;
-  } FILTERKEYS,*LPFILTERKEYS;
-
-#define FKF_FILTERKEYSON 0x00000001
-#define FKF_AVAILABLE 0x00000002
-#define FKF_HOTKEYACTIVE 0x00000004
-#define FKF_CONFIRMHOTKEY 0x00000008
-#define FKF_HOTKEYSOUND 0x00000010
-#define FKF_INDICATOR 0x00000020
-#define FKF_CLICKON 0x00000040
-
-  typedef struct tagSTICKYKEYS {
-    UINT cbSize;
-    DWORD dwFlags;
-  } STICKYKEYS,*LPSTICKYKEYS;
-
-#define SKF_STICKYKEYSON 0x00000001
-#define SKF_AVAILABLE 0x00000002
-#define SKF_HOTKEYACTIVE 0x00000004
-#define SKF_CONFIRMHOTKEY 0x00000008
-#define SKF_HOTKEYSOUND 0x00000010
-#define SKF_INDICATOR 0x00000020
-#define SKF_AUDIBLEFEEDBACK 0x00000040
-#define SKF_TRISTATE 0x00000080
-#define SKF_TWOKEYSOFF 0x00000100
-#define SKF_LALTLATCHED 0x10000000
-#define SKF_LCTLLATCHED 0x04000000
-#define SKF_LSHIFTLATCHED 0x01000000
-#define SKF_RALTLATCHED 0x20000000
-#define SKF_RCTLLATCHED 0x08000000
-#define SKF_RSHIFTLATCHED 0x02000000
-#define SKF_LWINLATCHED 0x40000000
-#define SKF_RWINLATCHED 0x80000000
-#define SKF_LALTLOCKED 0x00100000
-#define SKF_LCTLLOCKED 0x00040000
-#define SKF_LSHIFTLOCKED 0x00010000
-#define SKF_RALTLOCKED 0x00200000
-#define SKF_RCTLLOCKED 0x00080000
-#define SKF_RSHIFTLOCKED 0x00020000
-#define SKF_LWINLOCKED 0x00400000
-#define SKF_RWINLOCKED 0x00800000
-
-  typedef struct tagMOUSEKEYS {
-    UINT cbSize;
-    DWORD dwFlags;
-    DWORD iMaxSpeed;
-    DWORD iTimeToMaxSpeed;
-    DWORD iCtrlSpeed;
-    DWORD dwReserved1;
-    DWORD dwReserved2;
-  } MOUSEKEYS,*LPMOUSEKEYS;
-
-#define MKF_MOUSEKEYSON 0x00000001
-#define MKF_AVAILABLE 0x00000002
-#define MKF_HOTKEYACTIVE 0x00000004
-#define MKF_CONFIRMHOTKEY 0x00000008
-#define MKF_HOTKEYSOUND 0x00000010
-#define MKF_INDICATOR 0x00000020
-#define MKF_MODIFIERS 0x00000040
-#define MKF_REPLACENUMBERS 0x00000080
-#define MKF_LEFTBUTTONSEL 0x10000000
-#define MKF_RIGHTBUTTONSEL 0x20000000
-#define MKF_LEFTBUTTONDOWN 0x01000000
-#define MKF_RIGHTBUTTONDOWN 0x02000000
-#define MKF_MOUSEMODE 0x80000000
-
-  typedef struct tagACCESSTIMEOUT {
-    UINT cbSize;
-    DWORD dwFlags;
-    DWORD iTimeOutMSec;
-  } ACCESSTIMEOUT,*LPACCESSTIMEOUT;
-
-#define ATF_TIMEOUTON 0x00000001
-#define ATF_ONOFFFEEDBACK 0x00000002
-
-#define SSGF_NONE 0
-#define SSGF_DISPLAY 3
-
-#define SSTF_NONE 0
-#define SSTF_CHARS 1
-#define SSTF_BORDER 2
-#define SSTF_DISPLAY 3
-
-#define SSWF_NONE 0
-#define SSWF_TITLE 1
-#define SSWF_WINDOW 2
-#define SSWF_DISPLAY 3
-#define SSWF_CUSTOM 4
-
-  typedef struct tagSOUNDSENTRYA {
-    UINT cbSize;
-    DWORD dwFlags;
-    DWORD iFSTextEffect;
-    DWORD iFSTextEffectMSec;
-    DWORD iFSTextEffectColorBits;
-    DWORD iFSGrafEffect;
-    DWORD iFSGrafEffectMSec;
-    DWORD iFSGrafEffectColor;
-    DWORD iWindowsEffect;
-    DWORD iWindowsEffectMSec;
-    LPSTR lpszWindowsEffectDLL;
-    DWORD iWindowsEffectOrdinal;
-  } SOUNDSENTRYA,*LPSOUNDSENTRYA;
-
-  typedef struct tagSOUNDSENTRYW {
-    UINT cbSize;
-    DWORD dwFlags;
-    DWORD iFSTextEffect;
-    DWORD iFSTextEffectMSec;
-    DWORD iFSTextEffectColorBits;
-    DWORD iFSGrafEffect;
-    DWORD iFSGrafEffectMSec;
-    DWORD iFSGrafEffectColor;
-    DWORD iWindowsEffect;
-    DWORD iWindowsEffectMSec;
-    LPWSTR lpszWindowsEffectDLL;
-    DWORD iWindowsEffectOrdinal;
-  } SOUNDSENTRYW,*LPSOUNDSENTRYW;
-
-#ifdef UNICODE
-  typedef SOUNDSENTRYW SOUNDSENTRY;
-  typedef LPSOUNDSENTRYW LPSOUNDSENTRY;
-#else
-  typedef SOUNDSENTRYA SOUNDSENTRY;
-  typedef LPSOUNDSENTRYA LPSOUNDSENTRY;
-#endif
-
-#define SSF_SOUNDSENTRYON 0x00000001
-#define SSF_AVAILABLE 0x00000002
-#define SSF_INDICATOR 0x00000004
-
-  typedef struct tagTOGGLEKEYS {
-    UINT cbSize;
-    DWORD dwFlags;
-  } TOGGLEKEYS,*LPTOGGLEKEYS;
-
-#define TKF_TOGGLEKEYSON 0x00000001
-#define TKF_AVAILABLE 0x00000002
-#define TKF_HOTKEYACTIVE 0x00000004
-#define TKF_CONFIRMHOTKEY 0x00000008
-#define TKF_HOTKEYSOUND 0x00000010
-#define TKF_INDICATOR 0x00000020
-
-  WINUSERAPI VOID WINAPI SetDebugErrorLevel(DWORD dwLevel);
-
-#define SLE_ERROR 0x00000001
-#define SLE_MINORERROR 0x00000002
-#define SLE_WARNING 0x00000003
-
-  WINUSERAPI VOID WINAPI SetLastErrorEx(DWORD dwErrCode,DWORD dwType);
-  WINUSERAPI int WINAPI InternalGetWindowText(HWND hWnd,LPWSTR pString,int cchMaxCount);
-
-#ifdef WINNT
-  WINUSERAPI WINBOOL WINAPI EndTask(HWND hWnd,WINBOOL fShutDown,WINBOOL fForce);
-#endif
-
-#define MONITOR_DEFAULTTONULL 0x00000000
-#define MONITOR_DEFAULTTOPRIMARY 0x00000001
-#define MONITOR_DEFAULTTONEAREST 0x00000002
-
-  WINUSERAPI HMONITOR WINAPI MonitorFromPoint(POINT pt,DWORD dwFlags);
-  WINUSERAPI HMONITOR WINAPI MonitorFromRect(LPCRECT lprc,DWORD dwFlags);
-  WINUSERAPI HMONITOR WINAPI MonitorFromWindow(HWND hwnd,DWORD dwFlags);
-
-#define MONITORINFOF_PRIMARY 0x00000001
-
-#ifndef CCHDEVICENAME
-#define CCHDEVICENAME 32
-#endif
-
-  typedef struct tagMONITORINFO {
-    DWORD cbSize;
-    RECT rcMonitor;
-    RECT rcWork;
-    DWORD dwFlags;
-  } MONITORINFO,*LPMONITORINFO;
-
-#ifdef __cplusplus
-  typedef struct tagMONITORINFOEXA : public tagMONITORINFO {
-    CHAR szDevice[CCHDEVICENAME];
-  } MONITORINFOEXA,*LPMONITORINFOEXA;
-
-  typedef struct tagMONITORINFOEXW : public tagMONITORINFO {
-    WCHAR szDevice[CCHDEVICENAME];
-  } MONITORINFOEXW,*LPMONITORINFOEXW;
-
-#ifdef UNICODE
-  typedef MONITORINFOEXW MONITORINFOEX;
-  typedef LPMONITORINFOEXW LPMONITORINFOEX;
-#else
-  typedef MONITORINFOEXA MONITORINFOEX;
-  typedef LPMONITORINFOEXA LPMONITORINFOEX;
-#endif
-#else
-  typedef struct tagMONITORINFOEXA {
-    MONITORINFO mi;
-    CHAR szDevice[CCHDEVICENAME];
-  } MONITORINFOEXA,*LPMONITORINFOEXA;
-
-  typedef struct tagMONITORINFOEXW {
-    MONITORINFO mi;
-    WCHAR szDevice[CCHDEVICENAME];
-  } MONITORINFOEXW,*LPMONITORINFOEXW;
-#ifdef UNICODE
-  typedef MONITORINFOEXW MONITORINFOEX;
-  typedef LPMONITORINFOEXW LPMONITORINFOEX;
-#else
-  typedef MONITORINFOEXA MONITORINFOEX;
-  typedef LPMONITORINFOEXA LPMONITORINFOEX;
-#endif
-#endif
-
-#ifdef UNICODE 
-#define GetMonitorInfo GetMonitorInfoW
-#else
-#define GetMonitorInfo GetMonitorInfoA
-#endif
-
-  WINUSERAPI WINBOOL WINAPI GetMonitorInfoA(HMONITOR hMonitor,LPMONITORINFO lpmi);
-  WINUSERAPI WINBOOL WINAPI GetMonitorInfoW(HMONITOR hMonitor,LPMONITORINFO lpmi);
-
-  typedef WINBOOL (CALLBACK *MONITORENUMPROC)(HMONITOR,HDC,LPRECT,LPARAM);
-
-  WINUSERAPI WINBOOL WINAPI EnumDisplayMonitors(HDC hdc,LPCRECT lprcClip,MONITORENUMPROC lpfnEnum,LPARAM dwData);
-
-#ifndef NOWINABLE
-  WINUSERAPI VOID WINAPI NotifyWinEvent(DWORD event,HWND hwnd,LONG idObject,LONG idChild);
-
-  typedef VOID (CALLBACK *WINEVENTPROC)(HWINEVENTHOOK hWinEventHook,DWORD event,HWND hwnd,LONG idObject,LONG idChild,DWORD idEventThread,DWORD dwmsEventTime);
-
-  WINUSERAPI HWINEVENTHOOK WINAPI SetWinEventHook(DWORD eventMin,DWORD eventMax,HMODULE hmodWinEventProc,WINEVENTPROC pfnWinEventProc,DWORD idProcess,DWORD idThread,DWORD dwFlags);
-  WINUSERAPI WINBOOL WINAPI IsWinEventHookInstalled(DWORD event);
-
-#define WINEVENT_OUTOFCONTEXT 0x0000
-#define WINEVENT_SKIPOWNTHREAD 0x0001
-#define WINEVENT_SKIPOWNPROCESS 0x0002
-#define WINEVENT_INCONTEXT 0x0004
-
-  WINUSERAPI WINBOOL WINAPI UnhookWinEvent(HWINEVENTHOOK hWinEventHook);
-
-#define CHILDID_SELF 0
-#define INDEXID_OBJECT 0
-#define INDEXID_CONTAINER 0
-
-#define OBJID_WINDOW ((LONG)0x00000000)
-#define OBJID_SYSMENU ((LONG)0xFFFFFFFF)
-#define OBJID_TITLEBAR ((LONG)0xFFFFFFFE)
-#define OBJID_MENU ((LONG)0xFFFFFFFD)
-#define OBJID_CLIENT ((LONG)0xFFFFFFFC)
-#define OBJID_VSCROLL ((LONG)0xFFFFFFFB)
-#define OBJID_HSCROLL ((LONG)0xFFFFFFFA)
-#define OBJID_SIZEGRIP ((LONG)0xFFFFFFF9)
-#define OBJID_CARET ((LONG)0xFFFFFFF8)
-#define OBJID_CURSOR ((LONG)0xFFFFFFF7)
-#define OBJID_ALERT ((LONG)0xFFFFFFF6)
-#define OBJID_SOUND ((LONG)0xFFFFFFF5)
-#define OBJID_QUERYCLASSNAMEIDX ((LONG)0xFFFFFFF4)
-#define OBJID_NATIVEOM ((LONG)0xFFFFFFF0)
-
-#define EVENT_MIN 0x00000001
-#define EVENT_MAX 0x7FFFFFFF
-
-#define EVENT_SYSTEM_SOUND 0x0001
-#define EVENT_SYSTEM_ALERT 0x0002
-#define EVENT_SYSTEM_FOREGROUND 0x0003
-#define EVENT_SYSTEM_MENUSTART 0x0004
-#define EVENT_SYSTEM_MENUEND 0x0005
-#define EVENT_SYSTEM_MENUPOPUPSTART 0x0006
-#define EVENT_SYSTEM_MENUPOPUPEND 0x0007
-#define EVENT_SYSTEM_CAPTURESTART 0x0008
-#define EVENT_SYSTEM_CAPTUREEND 0x0009
-#define EVENT_SYSTEM_MOVESIZESTART 0x000A
-#define EVENT_SYSTEM_MOVESIZEEND 0x000B
-#define EVENT_SYSTEM_CONTEXTHELPSTART 0x000C
-#define EVENT_SYSTEM_CONTEXTHELPEND 0x000D
-#define EVENT_SYSTEM_DRAGDROPSTART 0x000E
-#define EVENT_SYSTEM_DRAGDROPEND 0x000F
-#define EVENT_SYSTEM_DIALOGSTART 0x0010
-#define EVENT_SYSTEM_DIALOGEND 0x0011
-#define EVENT_SYSTEM_SCROLLINGSTART 0x0012
-#define EVENT_SYSTEM_SCROLLINGEND 0x0013
-#define EVENT_SYSTEM_SWITCHSTART 0x0014
-#define EVENT_SYSTEM_SWITCHEND 0x0015
-#define EVENT_SYSTEM_MINIMIZESTART 0x0016
-#define EVENT_SYSTEM_MINIMIZEEND 0x0017
-
-#define EVENT_CONSOLE_CARET 0x4001
-#define EVENT_CONSOLE_UPDATE_REGION 0x4002
-#define EVENT_CONSOLE_UPDATE_SIMPLE 0x4003
-#define EVENT_CONSOLE_UPDATE_SCROLL 0x4004
-#define EVENT_CONSOLE_LAYOUT 0x4005
-#define EVENT_CONSOLE_START_APPLICATION 0x4006
-#define EVENT_CONSOLE_END_APPLICATION 0x4007
-
-#define CONSOLE_APPLICATION_16BIT 0x0001
-
-#define CONSOLE_CARET_SELECTION 0x0001
-#define CONSOLE_CARET_VISIBLE 0x0002
-
-#define EVENT_OBJECT_CREATE 0x8000
-#define EVENT_OBJECT_DESTROY 0x8001
-#define EVENT_OBJECT_SHOW 0x8002
-#define EVENT_OBJECT_HIDE 0x8003
-#define EVENT_OBJECT_REORDER 0x8004
-
-#define EVENT_OBJECT_FOCUS 0x8005
-#define EVENT_OBJECT_SELECTION 0x8006
-#define EVENT_OBJECT_SELECTIONADD 0x8007
-#define EVENT_OBJECT_SELECTIONREMOVE 0x8008
-#define EVENT_OBJECT_SELECTIONWITHIN 0x8009
-
-#define EVENT_OBJECT_STATECHANGE 0x800A
-
-#define EVENT_OBJECT_LOCATIONCHANGE 0x800B
-
-#define EVENT_OBJECT_NAMECHANGE 0x800C
-#define EVENT_OBJECT_DESCRIPTIONCHANGE 0x800D
-#define EVENT_OBJECT_VALUECHANGE 0x800E
-#define EVENT_OBJECT_PARENTCHANGE 0x800F
-#define EVENT_OBJECT_HELPCHANGE 0x8010
-#define EVENT_OBJECT_DEFACTIONCHANGE 0x8011
-#define EVENT_OBJECT_ACCELERATORCHANGE 0x8012
-
-#define SOUND_SYSTEM_STARTUP 1
-#define SOUND_SYSTEM_SHUTDOWN 2
-#define SOUND_SYSTEM_BEEP 3
-#define SOUND_SYSTEM_ERROR 4
-#define SOUND_SYSTEM_QUESTION 5
-#define SOUND_SYSTEM_WARNING 6
-#define SOUND_SYSTEM_INFORMATION 7
-#define SOUND_SYSTEM_MAXIMIZE 8
-#define SOUND_SYSTEM_MINIMIZE 9
-#define SOUND_SYSTEM_RESTOREUP 10
-#define SOUND_SYSTEM_RESTOREDOWN 11
-#define SOUND_SYSTEM_APPSTART 12
-#define SOUND_SYSTEM_FAULT 13
-#define SOUND_SYSTEM_APPEND 14
-#define SOUND_SYSTEM_MENUCOMMAND 15
-#define SOUND_SYSTEM_MENUPOPUP 16
-#define CSOUND_SYSTEM 16
-
-#define ALERT_SYSTEM_INFORMATIONAL 1
-#define ALERT_SYSTEM_WARNING 2
-#define ALERT_SYSTEM_ERROR 3
-#define ALERT_SYSTEM_QUERY 4
-#define ALERT_SYSTEM_CRITICAL 5
-#define CALERT_SYSTEM 6
-
-  typedef struct tagGUITHREADINFO {
-    DWORD cbSize;
-    DWORD flags;
-    HWND hwndActive;
-    HWND hwndFocus;
-    HWND hwndCapture;
-    HWND hwndMenuOwner;
-    HWND hwndMoveSize;
-    HWND hwndCaret;
-    RECT rcCaret;
-  } GUITHREADINFO,*PGUITHREADINFO,*LPGUITHREADINFO;
-
-#define GUI_CARETBLINKING 0x00000001
-#define GUI_INMOVESIZE 0x00000002
-#define GUI_INMENUMODE 0x00000004
-#define GUI_SYSTEMMENUMODE 0x00000008
-#define GUI_POPUPMENUMODE 0x00000010
-#define GUI_16BITTASK 0x00000020
-
-#ifdef UNICODE
-#define GetWindowModuleFileName GetWindowModuleFileNameW
-#else
-#define GetWindowModuleFileName GetWindowModuleFileNameA
-#endif
-
-  WINUSERAPI WINBOOL WINAPI GetGUIThreadInfo(DWORD idThread,PGUITHREADINFO pgui);
-  WINUSERAPI UINT WINAPI GetWindowModuleFileNameA(HWND hwnd,LPSTR pszFileName,UINT cchFileNameMax);
-  WINUSERAPI UINT WINAPI GetWindowModuleFileNameW(HWND hwnd,LPWSTR pszFileName,UINT cchFileNameMax);
-
-#ifndef NO_STATE_FLAGS
-#define STATE_SYSTEM_UNAVAILABLE 0x00000001
-#define STATE_SYSTEM_SELECTED 0x00000002
-#define STATE_SYSTEM_FOCUSED 0x00000004
-#define STATE_SYSTEM_PRESSED 0x00000008
-#define STATE_SYSTEM_CHECKED 0x00000010
-#define STATE_SYSTEM_MIXED 0x00000020
-#define STATE_SYSTEM_INDETERMINATE STATE_SYSTEM_MIXED
-#define STATE_SYSTEM_READONLY 0x00000040
-#define STATE_SYSTEM_HOTTRACKED 0x00000080
-#define STATE_SYSTEM_DEFAULT 0x00000100
-#define STATE_SYSTEM_EXPANDED 0x00000200
-#define STATE_SYSTEM_COLLAPSED 0x00000400
-#define STATE_SYSTEM_BUSY 0x00000800
-#define STATE_SYSTEM_FLOATING 0x00001000
-#define STATE_SYSTEM_MARQUEED 0x00002000
-#define STATE_SYSTEM_ANIMATED 0x00004000
-#define STATE_SYSTEM_INVISIBLE 0x00008000
-#define STATE_SYSTEM_OFFSCREEN 0x00010000
-#define STATE_SYSTEM_SIZEABLE 0x00020000
-#define STATE_SYSTEM_MOVEABLE 0x00040000
-#define STATE_SYSTEM_SELFVOICING 0x00080000
-#define STATE_SYSTEM_FOCUSABLE 0x00100000
-#define STATE_SYSTEM_SELECTABLE 0x00200000
-#define STATE_SYSTEM_LINKED 0x00400000
-#define STATE_SYSTEM_TRAVERSED 0x00800000
-#define STATE_SYSTEM_MULTISELECTABLE 0x01000000
-#define STATE_SYSTEM_EXTSELECTABLE 0x02000000
-#define STATE_SYSTEM_ALERT_LOW 0x04000000
-#define STATE_SYSTEM_ALERT_MEDIUM 0x08000000
-#define STATE_SYSTEM_ALERT_HIGH 0x10000000
-#define STATE_SYSTEM_PROTECTED 0x20000000
-#define STATE_SYSTEM_VALID 0x3FFFFFFF
-#endif
-
-#define CCHILDREN_TITLEBAR 5
-#define CCHILDREN_SCROLLBAR 5
-
-  typedef struct tagCURSORINFO {
-    DWORD cbSize;
-    DWORD flags;
-    HCURSOR hCursor;
-    POINT ptScreenPos;
-  } CURSORINFO,*PCURSORINFO,*LPCURSORINFO;
-
-#define CURSOR_SHOWING 0x00000001
-
-  WINUSERAPI WINBOOL WINAPI GetCursorInfo(PCURSORINFO pci);
-
-  typedef struct tagWINDOWINFO {
-    DWORD cbSize;
-    RECT rcWindow;
-    RECT rcClient;
-    DWORD dwStyle;
-    DWORD dwExStyle;
-    DWORD dwWindowStatus;
-    UINT cxWindowBorders;
-    UINT cyWindowBorders;
-    ATOM atomWindowType;
-    WORD wCreatorVersion;
-  } WINDOWINFO,*PWINDOWINFO,*LPWINDOWINFO;
-
-#define WS_ACTIVECAPTION 0x0001
-
-  WINUSERAPI WINBOOL WINAPI GetWindowInfo(HWND hwnd,PWINDOWINFO pwi);
-
-  typedef struct tagTITLEBARINFO {
-    DWORD cbSize;
-    RECT rcTitleBar;
-    DWORD rgstate[CCHILDREN_TITLEBAR + 1];
-  } TITLEBARINFO,*PTITLEBARINFO,*LPTITLEBARINFO;
-
-  WINUSERAPI WINBOOL WINAPI GetTitleBarInfo(HWND hwnd,PTITLEBARINFO pti);
-
-  typedef struct tagMENUBARINFO {
-    DWORD cbSize;
-    RECT rcBar;
-    HMENU hMenu;
-    HWND hwndMenu;
-    WINBOOL fBarFocused:1;
-    WINBOOL fFocused:1;
-  } MENUBARINFO,*PMENUBARINFO,*LPMENUBARINFO;
-
-  WINUSERAPI WINBOOL WINAPI GetMenuBarInfo(HWND hwnd,LONG idObject,LONG idItem,PMENUBARINFO pmbi);
-
-  typedef struct tagSCROLLBARINFO {
-    DWORD cbSize;
-    RECT rcScrollBar;
-    int dxyLineButton;
-    int xyThumbTop;
-    int xyThumbBottom;
-    int reserved;
-    DWORD rgstate[CCHILDREN_SCROLLBAR + 1];
-  } SCROLLBARINFO,*PSCROLLBARINFO,*LPSCROLLBARINFO;
-
-  WINUSERAPI WINBOOL WINAPI GetScrollBarInfo(HWND hwnd,LONG idObject,PSCROLLBARINFO psbi);
-
-  typedef struct tagCOMBOBOXINFO {
-    DWORD cbSize;
-    RECT rcItem;
-    RECT rcButton;
-    DWORD stateButton;
-    HWND hwndCombo;
-    HWND hwndItem;
-    HWND hwndList;
-  } COMBOBOXINFO,*PCOMBOBOXINFO,*LPCOMBOBOXINFO;
-
-  WINUSERAPI WINBOOL WINAPI GetComboBoxInfo(HWND hwndCombo,PCOMBOBOXINFO pcbi);
-
-#define GA_PARENT 1
-#define GA_ROOT 2
-#define GA_ROOTOWNER 3
-
-  WINUSERAPI HWND WINAPI GetAncestor(HWND hwnd,UINT gaFlags);
-  WINUSERAPI HWND WINAPI RealChildWindowFromPoint(HWND hwndParent,POINT ptParentClientCoords);
-  WINUSERAPI UINT WINAPI RealGetWindowClassA(HWND hwnd,LPSTR ptszClassName,UINT cchClassNameMax);
-  WINUSERAPI UINT WINAPI RealGetWindowClassW(HWND hwnd,LPWSTR ptszClassName,UINT cchClassNameMax);
-#ifdef UNICODE
-#define RealGetWindowClass RealGetWindowClassW
-#else
-#define RealGetWindowClass RealGetWindowClassA
-#endif
-
-  typedef struct tagALTTABINFO {
-    DWORD cbSize;
-    int cItems;
-    int cColumns;
-    int cRows;
-    int iColFocus;
-    int iRowFocus;
-    int cxItem;
-    int cyItem;
-    POINT ptStart;
-  } ALTTABINFO,*PALTTABINFO,*LPALTTABINFO;
-
-#ifdef UNICODE
-#define GetAltTabInfo GetAltTabInfoW
-#else
-#define GetAltTabInfo GetAltTabInfoA
-#endif
-
-  WINUSERAPI WINBOOL WINAPI GetAltTabInfoA(HWND hwnd,int iItem,PALTTABINFO pati,LPSTR pszItemText,UINT cchItemText);
-  WINUSERAPI WINBOOL WINAPI GetAltTabInfoW(HWND hwnd,int iItem,PALTTABINFO pati,LPWSTR pszItemText,UINT cchItemText);
-  WINUSERAPI DWORD WINAPI GetListBoxInfo(HWND hwnd);
-#endif
-
-  WINUSERAPI WINBOOL WINAPI LockWorkStation(VOID);
-  WINUSERAPI WINBOOL WINAPI UserHandleGrantAccess(HANDLE hUserHandle,HANDLE hJob,WINBOOL bGrant);
-
-  DECLARE_HANDLE(HRAWINPUT);
-
-#define GET_RAWINPUT_CODE_WPARAM(wParam) ((wParam) & 0xff)
-
-#define RIM_INPUT 0
-#define RIM_INPUTSINK 1
-
-  typedef struct tagRAWINPUTHEADER {
-    DWORD dwType;
-    DWORD dwSize;
-    HANDLE hDevice;
-    WPARAM wParam;
-  } RAWINPUTHEADER,*PRAWINPUTHEADER,*LPRAWINPUTHEADER;
-
-#define RIM_TYPEMOUSE 0
-#define RIM_TYPEKEYBOARD 1
-#define RIM_TYPEHID 2
-
-  typedef struct tagRAWMOUSE {
-    USHORT usFlags;
-    union {
-      ULONG ulButtons;
-      struct {
-	USHORT usButtonFlags;
-	USHORT usButtonData;
-      };
-    };
-    ULONG ulRawButtons;
-    LONG lLastX;
-    LONG lLastY;
-    ULONG ulExtraInformation;
-  } RAWMOUSE,*PRAWMOUSE,*LPRAWMOUSE;
-
-#define RI_MOUSE_LEFT_BUTTON_DOWN 0x0001
-#define RI_MOUSE_LEFT_BUTTON_UP 0x0002
-#define RI_MOUSE_RIGHT_BUTTON_DOWN 0x0004
-#define RI_MOUSE_RIGHT_BUTTON_UP 0x0008
-#define RI_MOUSE_MIDDLE_BUTTON_DOWN 0x0010
-#define RI_MOUSE_MIDDLE_BUTTON_UP 0x0020
-
-#define RI_MOUSE_BUTTON_1_DOWN RI_MOUSE_LEFT_BUTTON_DOWN
-#define RI_MOUSE_BUTTON_1_UP RI_MOUSE_LEFT_BUTTON_UP
-#define RI_MOUSE_BUTTON_2_DOWN RI_MOUSE_RIGHT_BUTTON_DOWN
-#define RI_MOUSE_BUTTON_2_UP RI_MOUSE_RIGHT_BUTTON_UP
-#define RI_MOUSE_BUTTON_3_DOWN RI_MOUSE_MIDDLE_BUTTON_DOWN
-#define RI_MOUSE_BUTTON_3_UP RI_MOUSE_MIDDLE_BUTTON_UP
-
-#define RI_MOUSE_BUTTON_4_DOWN 0x0040
-#define RI_MOUSE_BUTTON_4_UP 0x0080
-#define RI_MOUSE_BUTTON_5_DOWN 0x0100
-#define RI_MOUSE_BUTTON_5_UP 0x0200
-
-#define RI_MOUSE_WHEEL 0x0400
-
-#define MOUSE_MOVE_RELATIVE 0
-#define MOUSE_MOVE_ABSOLUTE 1
-#define MOUSE_VIRTUAL_DESKTOP 0x02
-#define MOUSE_ATTRIBUTES_CHANGED 0x04
-
-  typedef struct tagRAWKEYBOARD {
-    USHORT MakeCode;
-    USHORT Flags;
-    USHORT Reserved;
-    USHORT VKey;
-    UINT Message;
-    ULONG ExtraInformation;
-  } RAWKEYBOARD,*PRAWKEYBOARD,*LPRAWKEYBOARD;
-
-#define KEYBOARD_OVERRUN_MAKE_CODE 0xFF
-
-#define RI_KEY_MAKE 0
-#define RI_KEY_BREAK 1
-#define RI_KEY_E0 2
-#define RI_KEY_E1 4
-#define RI_KEY_TERMSRV_SET_LED 8
-#define RI_KEY_TERMSRV_SHADOW 0x10
-
-  typedef struct tagRAWHID {
-    DWORD dwSizeHid;
-    DWORD dwCount;
-    BYTE bRawData[1];
-  } RAWHID,*PRAWHID,*LPRAWHID;
-
-  typedef struct tagRAWINPUT {
-    RAWINPUTHEADER header;
-    union {
-      RAWMOUSE mouse;
-      RAWKEYBOARD keyboard;
-      RAWHID hid;
-    } data;
-  } RAWINPUT,*PRAWINPUT,*LPRAWINPUT;
-
-#ifdef _WIN64
-#define RAWINPUT_ALIGN(x) (((x) + sizeof(QWORD) - 1) & ~(sizeof(QWORD) - 1))
-#else
-#define RAWINPUT_ALIGN(x) (((x) + sizeof(DWORD) - 1) & ~(sizeof(DWORD) - 1))
-#endif
-
-#define NEXTRAWINPUTBLOCK(ptr) ((PRAWINPUT)RAWINPUT_ALIGN((ULONG_PTR)((PBYTE)(ptr) + (ptr)->header.dwSize)))
-
-#define RID_INPUT 0x10000003
-#define RID_HEADER 0x10000005
-
-  WINUSERAPI UINT WINAPI GetRawInputData(HRAWINPUT hRawInput,UINT uiCommand,LPVOID pData,PUINT pcbSize,UINT cbSizeHeader);
-
-#define RIDI_PREPARSEDDATA 0x20000005
-#define RIDI_DEVICENAME 0x20000007
-#define RIDI_DEVICEINFO 0x2000000b
-
-  typedef struct tagRID_DEVICE_INFO_MOUSE {
-    DWORD dwId;
-    DWORD dwNumberOfButtons;
-    DWORD dwSampleRate;
-  } RID_DEVICE_INFO_MOUSE,*PRID_DEVICE_INFO_MOUSE;
-
-  typedef struct tagRID_DEVICE_INFO_KEYBOARD {
-    DWORD dwType;
-    DWORD dwSubType;
-    DWORD dwKeyboardMode;
-    DWORD dwNumberOfFunctionKeys;
-    DWORD dwNumberOfIndicators;
-    DWORD dwNumberOfKeysTotal;
-  } RID_DEVICE_INFO_KEYBOARD,*PRID_DEVICE_INFO_KEYBOARD;
-
-  typedef struct tagRID_DEVICE_INFO_HID {
-    DWORD dwVendorId;
-    DWORD dwProductId;
-    DWORD dwVersionNumber;
-    USHORT usUsagePage;
-    USHORT usUsage;
-  } RID_DEVICE_INFO_HID,*PRID_DEVICE_INFO_HID;
-
-  typedef struct tagRID_DEVICE_INFO {
-    DWORD cbSize;
-    DWORD dwType;
-    union {
-      RID_DEVICE_INFO_MOUSE mouse;
-      RID_DEVICE_INFO_KEYBOARD keyboard;
-      RID_DEVICE_INFO_HID hid;
-    };
-  } RID_DEVICE_INFO,*PRID_DEVICE_INFO,*LPRID_DEVICE_INFO;
-
-#ifdef UNICODE
-#define GetRawInputDeviceInfo GetRawInputDeviceInfoW
-#else
-#define GetRawInputDeviceInfo GetRawInputDeviceInfoA
-#endif
-
-  WINUSERAPI UINT WINAPI GetRawInputDeviceInfoA(HANDLE hDevice,UINT uiCommand,LPVOID pData,PUINT pcbSize);
-  WINUSERAPI UINT WINAPI GetRawInputDeviceInfoW(HANDLE hDevice,UINT uiCommand,LPVOID pData,PUINT pcbSize);
-  WINUSERAPI UINT WINAPI GetRawInputBuffer(PRAWINPUT pData,PUINT pcbSize,UINT cbSizeHeader);
-
-  typedef struct tagRAWINPUTDEVICE {
-    USHORT usUsagePage;
-    USHORT usUsage;
-    DWORD dwFlags;
-    HWND hwndTarget;
-  } RAWINPUTDEVICE,*PRAWINPUTDEVICE,*LPRAWINPUTDEVICE;
-
-  typedef CONST RAWINPUTDEVICE *PCRAWINPUTDEVICE;
-
-#define RIDEV_REMOVE 0x00000001
-#define RIDEV_EXCLUDE 0x00000010
-#define RIDEV_PAGEONLY 0x00000020
-#define RIDEV_NOLEGACY 0x00000030
-#define RIDEV_INPUTSINK 0x00000100
-#define RIDEV_CAPTUREMOUSE 0x00000200
-#define RIDEV_NOHOTKEYS 0x00000200
-#define RIDEV_APPKEYS 0x00000400
-#define RIDEV_EXMODEMASK 0x000000F0
-#define RIDEV_EXMODE(mode) ((mode) & RIDEV_EXMODEMASK)
-
-  WINUSERAPI WINBOOL WINAPI RegisterRawInputDevices(PCRAWINPUTDEVICE pRawInputDevices,UINT uiNumDevices,UINT cbSize);
-  WINUSERAPI UINT WINAPI GetRegisteredRawInputDevices(PRAWINPUTDEVICE pRawInputDevices,PUINT puiNumDevices,UINT cbSize);
-
-  typedef struct tagRAWINPUTDEVICELIST {
-    HANDLE hDevice;
-    DWORD dwType;
-  } RAWINPUTDEVICELIST,*PRAWINPUTDEVICELIST;
-
-  WINUSERAPI UINT WINAPI GetRawInputDeviceList(PRAWINPUTDEVICELIST pRawInputDeviceList,PUINT puiNumDevices,UINT cbSize);
-  WINUSERAPI LRESULT WINAPI DefRawInputProc(PRAWINPUT *paRawInput,INT nInput,UINT cbSizeHeader);
-
-#endif /* NOUSER */
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/tinyc/win32/include/winapi/winver.h b/tinyc/win32/include/winapi/winver.h
deleted file mode 100644
index 5c0f036bc..000000000
--- a/tinyc/win32/include/winapi/winver.h
+++ /dev/null
@@ -1,160 +0,0 @@
-/**
- * This file has no copyright assigned and is placed in the Public Domain.
- * This file is part of the w64 mingw-runtime package.
- * No warranty is given; refer to the file DISCLAIMER within this package.
- */
-#ifndef VER_H
-#define VER_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define VS_FILE_INFO RT_VERSION
-#define VS_VERSION_INFO 1
-#define VS_USER_DEFINED 100
-
-#define VS_FFI_SIGNATURE 0xFEEF04BDL
-#define VS_FFI_STRUCVERSION 0x00010000L
-#define VS_FFI_FILEFLAGSMASK 0x0000003FL
-
-#define VS_FF_DEBUG 0x00000001L
-#define VS_FF_PRERELEASE 0x00000002L
-#define VS_FF_PATCHED 0x00000004L
-#define VS_FF_PRIVATEBUILD 0x00000008L
-#define VS_FF_INFOINFERRED 0x00000010L
-#define VS_FF_SPECIALBUILD 0x00000020L
-
-#define VOS_UNKNOWN 0x00000000L
-#define VOS_DOS 0x00010000L
-#define VOS_OS216 0x00020000L
-#define VOS_OS232 0x00030000L
-#define VOS_NT 0x00040000L
-#define VOS_WINCE 0x00050000L
-
-#define VOS__BASE 0x00000000L
-#define VOS__WINDOWS16 0x00000001L
-#define VOS__PM16 0x00000002L
-#define VOS__PM32 0x00000003L
-#define VOS__WINDOWS32 0x00000004L
-
-#define VOS_DOS_WINDOWS16 0x00010001L
-#define VOS_DOS_WINDOWS32 0x00010004L
-#define VOS_OS216_PM16 0x00020002L
-#define VOS_OS232_PM32 0x00030003L
-#define VOS_NT_WINDOWS32 0x00040004L
-
-#define VFT_UNKNOWN 0x00000000L
-#define VFT_APP 0x00000001L
-#define VFT_DLL 0x00000002L
-#define VFT_DRV 0x00000003L
-#define VFT_FONT 0x00000004L
-#define VFT_VXD 0x00000005L
-#define VFT_STATIC_LIB 0x00000007L
-
-#define VFT2_UNKNOWN 0x00000000L
-#define VFT2_DRV_PRINTER 0x00000001L
-#define VFT2_DRV_KEYBOARD 0x00000002L
-#define VFT2_DRV_LANGUAGE 0x00000003L
-#define VFT2_DRV_DISPLAY 0x00000004L
-#define VFT2_DRV_MOUSE 0x00000005L
-#define VFT2_DRV_NETWORK 0x00000006L
-#define VFT2_DRV_SYSTEM 0x00000007L
-#define VFT2_DRV_INSTALLABLE 0x00000008L
-#define VFT2_DRV_SOUND 0x00000009L
-#define VFT2_DRV_COMM 0x0000000AL
-#define VFT2_DRV_INPUTMETHOD 0x0000000BL
-#define VFT2_DRV_VERSIONED_PRINTER 0x0000000CL
-
-#define VFT2_FONT_RASTER 0x00000001L
-#define VFT2_FONT_VECTOR 0x00000002L
-#define VFT2_FONT_TRUETYPE 0x00000003L
-
-#define VFFF_ISSHAREDFILE 0x0001
-
-#define VFF_CURNEDEST 0x0001
-#define VFF_FILEINUSE 0x0002
-#define VFF_BUFFTOOSMALL 0x0004
-
-#define VIFF_FORCEINSTALL 0x0001
-#define VIFF_DONTDELETEOLD 0x0002
-
-#define VIF_TEMPFILE 0x00000001L
-#define VIF_MISMATCH 0x00000002L
-#define VIF_SRCOLD 0x00000004L
-
-#define VIF_DIFFLANG 0x00000008L
-#define VIF_DIFFCODEPG 0x00000010L
-#define VIF_DIFFTYPE 0x00000020L
-
-#define VIF_WRITEPROT 0x00000040L
-#define VIF_FILEINUSE 0x00000080L
-#define VIF_OUTOFSPACE 0x00000100L
-#define VIF_ACCESSVIOLATION 0x00000200L
-#define VIF_SHARINGVIOLATION 0x00000400L
-#define VIF_CANNOTCREATE 0x00000800L
-#define VIF_CANNOTDELETE 0x00001000L
-#define VIF_CANNOTRENAME 0x00002000L
-#define VIF_CANNOTDELETECUR 0x00004000L
-#define VIF_OUTOFMEMORY 0x00008000L
-
-#define VIF_CANNOTREADSRC 0x00010000L
-#define VIF_CANNOTREADDST 0x00020000L
-
-#define VIF_BUFFTOOSMALL 0x00040000L
-#define VIF_CANNOTLOADLZ32 0x00080000L
-#define VIF_CANNOTLOADCABINET 0x00100000L
-
-#ifndef RC_INVOKED
-
-  typedef struct tagVS_FIXEDFILEINFO
-  {
-    DWORD dwSignature;
-    DWORD dwStrucVersion;
-    DWORD dwFileVersionMS;
-    DWORD dwFileVersionLS;
-    DWORD dwProductVersionMS;
-    DWORD dwProductVersionLS;
-    DWORD dwFileFlagsMask;
-    DWORD dwFileFlags;
-    DWORD dwFileOS;
-    DWORD dwFileType;
-    DWORD dwFileSubtype;
-    DWORD dwFileDateMS;
-    DWORD dwFileDateLS;
-  } VS_FIXEDFILEINFO;
-
-#ifdef UNICODE
-#define VerFindFile VerFindFileW
-#define VerInstallFile VerInstallFileW
-#define GetFileVersionInfoSize GetFileVersionInfoSizeW
-#define GetFileVersionInfo GetFileVersionInfoW
-#define VerLanguageName VerLanguageNameW
-#define VerQueryValue VerQueryValueW
-#else
-#define VerFindFile VerFindFileA
-#define VerInstallFile VerInstallFileA
-#define GetFileVersionInfoSize GetFileVersionInfoSizeA
-#define GetFileVersionInfo GetFileVersionInfoA
-#define VerLanguageName VerLanguageNameA
-#define VerQueryValue VerQueryValueA
-#endif
-
-  DWORD WINAPI VerFindFileA(DWORD uFlags,LPSTR szFileName,LPSTR szWinDir,LPSTR szAppDir,LPSTR szCurDir,PUINT lpuCurDirLen,LPSTR szDestDir,PUINT lpuDestDirLen);
-  DWORD WINAPI VerFindFileW(DWORD uFlags,LPWSTR szFileName,LPWSTR szWinDir,LPWSTR szAppDir,LPWSTR szCurDir,PUINT lpuCurDirLen,LPWSTR szDestDir,PUINT lpuDestDirLen);
-  DWORD WINAPI VerInstallFileA(DWORD uFlags,LPSTR szSrcFileName,LPSTR szDestFileName,LPSTR szSrcDir,LPSTR szDestDir,LPSTR szCurDir,LPSTR szTmpFile,PUINT lpuTmpFileLen);
-  DWORD WINAPI VerInstallFileW(DWORD uFlags,LPWSTR szSrcFileName,LPWSTR szDestFileName,LPWSTR szSrcDir,LPWSTR szDestDir,LPWSTR szCurDir,LPWSTR szTmpFile,PUINT lpuTmpFileLen);
-  DWORD WINAPI GetFileVersionInfoSizeA(LPCSTR lptstrFilename,LPDWORD lpdwHandle);
-  DWORD WINAPI GetFileVersionInfoSizeW(LPCWSTR lptstrFilename,LPDWORD lpdwHandle);
-  WINBOOL WINAPI GetFileVersionInfoA(LPCSTR lptstrFilename,DWORD dwHandle,DWORD dwLen,LPVOID lpData);
-  WINBOOL WINAPI GetFileVersionInfoW(LPCWSTR lptstrFilename,DWORD dwHandle,DWORD dwLen,LPVOID lpData);
-  DWORD WINAPI VerLanguageNameA(DWORD wLang,LPSTR szLang,DWORD nSize);
-  DWORD WINAPI VerLanguageNameW(DWORD wLang,LPWSTR szLang,DWORD nSize);
-  WINBOOL WINAPI VerQueryValueA(const LPVOID pBlock,LPSTR lpSubBlock,LPVOID *lplpBuffer,PUINT puLen);
-  WINBOOL WINAPI VerQueryValueW(const LPVOID pBlock,LPWSTR lpSubBlock,LPVOID *lplpBuffer,PUINT puLen);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/tinyc/win32/lib/chkstk.S b/tinyc/win32/lib/chkstk.S
deleted file mode 100644
index ec5c07ff8..000000000
--- a/tinyc/win32/lib/chkstk.S
+++ /dev/null
@@ -1,191 +0,0 @@
-/* ---------------------------------------------- */
-/* chkstk86.s */
-
-/* ---------------------------------------------- */
-#ifndef __x86_64__
-/* ---------------------------------------------- */
-
-.globl __chkstk
-
-__chkstk:
-    xchg    (%esp),%ebp     /* store ebp, get ret.addr */
-    push    %ebp            /* push ret.addr */
-    lea     4(%esp),%ebp    /* setup frame ptr */
-    push    %ecx            /* save ecx */
-    mov     %ebp,%ecx
-P0:
-    sub     $4096,%ecx
-    test    %eax,(%ecx)
-    sub     $4096,%eax
-    cmp     $4096,%eax
-    jge     P0
-    sub     %eax,%ecx
-    test    %eax,(%ecx)
-
-    mov     %esp,%eax
-    mov     %ecx,%esp
-    mov     (%eax),%ecx     /* restore ecx */
-    jmp     *4(%eax)
-
-/* ---------------------------------------------- */
-#else
-/* ---------------------------------------------- */
-
-.globl __chkstk
-
-__chkstk:
-    xchg    (%rsp),%rbp     /* store ebp, get ret.addr */
-    push    %rbp            /* push ret.addr */
-    lea     8(%rsp),%rbp    /* setup frame ptr */
-    push    %rcx            /* save ecx */
-    mov     %rbp,%rcx
-    movslq  %eax,%rax
-P0:
-    sub     $4096,%rcx
-    test    %rax,(%rcx)
-    sub     $4096,%rax
-    cmp     $4096,%rax
-    jge     P0
-    sub     %rax,%rcx
-    test    %rax,(%rcx)
-
-    mov     %rsp,%rax
-    mov     %rcx,%rsp
-    mov     (%rax),%rcx     /* restore ecx */
-    jmp     *8(%rax)
-
-/* ---------------------------------------------- */
-/* setjmp/longjmp support */
-
-.globl tinyc_getbp
-tinyc_getbp:
-    mov %rbp,%rax
-    ret
-
-/* ---------------------------------------------- */
-#endif
-/* ---------------------------------------------- */
-
-
-/* ---------------------------------------------- */
-#ifndef __x86_64__
-/* ---------------------------------------------- */
-
-/*
-    int _except_handler3(
-       PEXCEPTION_RECORD exception_record,
-       PEXCEPTION_REGISTRATION registration,
-       PCONTEXT context,
-       PEXCEPTION_REGISTRATION dispatcher
-    );
-
-    int __cdecl _XcptFilter(
-       unsigned long xcptnum,
-       PEXCEPTION_POINTERS pxcptinfoptrs
-    );
-
-    struct _sehrec {
-       void *esp;                // 0
-       void *exception_pointers; // 1
-       void *prev;               // 2
-       void *handler;            // 3
-       void *scopetable;         // 4
-       int trylevel;             // 5
-       void *ebp                 // 6
-    };
-
-    // this is what the assembler code below means:
-    __try
-    {
-         // ...
-    }
-    __except (_XcptFilter(GetExceptionCode(), GetExceptionInformation()))
-    {
-         exit(GetExceptionCode());
-    }
-*/
-
-.globl _exception_info
-_exception_info:
-    mov 1*4-24(%ebp),%eax
-    ret
-
-.globl _exception_code
-_exception_code:
-    call _exception_info
-    mov (%eax),%eax
-    mov (%eax),%eax
-    ret
-
-seh_filter:
-    call _exception_info
-    push %eax
-    call _exception_code
-    push %eax
-    call _XcptFilter
-    add $ 8,%esp
-    ret
-
-seh_except:
-    mov 0*4-24(%ebp),%esp
-    call _exception_code
-    push %eax
-    call _exit
-
-// msvcrt wants scopetables aligned and in read-only segment (using .text)
-.align 4
-seh_scopetable:
-    .long -1
-    .long seh_filter
-    .long seh_except
-
-seh_handler:
-    jmp _except_handler3
-
-.globl ___try__
-___try__:
-.globl __try__
-__try__:
-    push %ebp
-    mov 8(%esp),%ebp
-
-//    void *esp;
-    lea 12(%esp),%eax
-    mov %eax,0*4(%ebp)
-
-//    void *exception_pointers;
-    xor %eax,%eax
-    mov %eax,1*4(%ebp)
-
-//    void *prev;
-    mov %fs:0,%eax
-    mov %eax,2*4(%ebp)
-
-//    void *handler;
-    mov $ seh_handler,%eax
-    mov %eax,3*4(%ebp)
-
-//    void *scopetable;
-    mov $ seh_scopetable,%eax
-    mov %eax,4*4(%ebp)
-
-//    int trylevel;
-    xor %eax,%eax
-    mov %eax,5*4(%ebp)
-
-//    register new SEH
-    lea 2*4(%ebp),%eax
-    mov %eax,%fs:0
-
-    pop %ebp
-    ret
-
-/* ---------------------------------------------- */
-#else
-/* ---------------------------------------------- */
-
-/* SEH on x86-64 not implemented */
-
-/* ---------------------------------------------- */
-#endif
-/* ---------------------------------------------- */
diff --git a/tinyc/win32/lib/crt1.c b/tinyc/win32/lib/crt1.c
deleted file mode 100644
index 0e04fc012..000000000
--- a/tinyc/win32/lib/crt1.c
+++ /dev/null
@@ -1,79 +0,0 @@
-// =============================================
-// crt1.c
-
-// _UNICODE for tchar.h, UNICODE for API
-#include <tchar.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#define _UNKNOWN_APP    0
-#define _CONSOLE_APP    1
-#define _GUI_APP        2
-
-#define _MCW_PC         0x00030000 // Precision Control
-#define _PC_24          0x00020000 // 24 bits
-#define _PC_53          0x00010000 // 53 bits
-#define _PC_64          0x00000000 // 64 bits
-
-#ifdef _UNICODE
-#define __tgetmainargs __wgetmainargs
-#define _tstart _wstart
-#define _tmain wmain
-#define _runtmain _runwmain
-#else
-#define __tgetmainargs __getmainargs
-#define _tstart _start
-#define _tmain main
-#define _runtmain _runmain
-#endif
-
-typedef struct { int newmode; } _startupinfo;
-int __cdecl __tgetmainargs(int *pargc, _TCHAR ***pargv, _TCHAR ***penv, int globb, _startupinfo*);
-void __cdecl __set_app_type(int apptype);
-unsigned int __cdecl _controlfp(unsigned int new_value, unsigned int mask);
-extern int _tmain(int argc, _TCHAR * argv[], _TCHAR * env[]);
-
-/* Allow command-line globbing with "int _dowildcard = 1;" in the user source */
-int _dowildcard;
-
-void _tstart(void)
-{
-    __TRY__
-    _startupinfo start_info = {0};
-
-    // Sets the current application type
-    __set_app_type(_CONSOLE_APP);
-
-    // Set default FP precision to 53 bits (8-byte double)
-    // _MCW_PC (Precision control) is not supported on ARM
-#if defined __i386__ || defined __x86_64__
-    _controlfp(_PC_53, _MCW_PC);
-#endif
-
-    __tgetmainargs( &__argc, &__targv, &_tenviron, _dowildcard, &start_info);
-    exit(_tmain(__argc, __targv, _tenviron));
-}
-
-int _runtmain(int argc, /* as tcc passed in */ char **argv)
-{
-#ifdef UNICODE
-    _startupinfo start_info = {0};
-
-    __tgetmainargs(&__argc, &__targv, &_tenviron, _dowildcard, &start_info);
-    /* may be wrong when tcc has received wildcards (*.c) */
-    if (argc < __argc) {
-        __targv += __argc - argc;
-        __argc = argc;
-    }
-#else
-    __argc = argc;
-    __targv = argv;
-#endif
-#if defined __i386__ || defined __x86_64__
-    _controlfp(_PC_53, _MCW_PC);
-#endif
-    return _tmain(__argc, __targv, _tenviron);
-}
-
-// =============================================
diff --git a/tinyc/win32/lib/crt1w.c b/tinyc/win32/lib/crt1w.c
deleted file mode 100644
index 2b8bbc8bc..000000000
--- a/tinyc/win32/lib/crt1w.c
+++ /dev/null
@@ -1,3 +0,0 @@
-#define _UNICODE 1
-#define UNICODE 1
-#include "crt1.c"
diff --git a/tinyc/win32/lib/dllcrt1.c b/tinyc/win32/lib/dllcrt1.c
deleted file mode 100644
index ba1dbd048..000000000
--- a/tinyc/win32/lib/dllcrt1.c
+++ /dev/null
@@ -1,13 +0,0 @@
-//+---------------------------------------------------------------------------
-
-#include <windows.h>
-
-BOOL WINAPI DllMain (HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved);
-
-BOOL WINAPI _dllstart(HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved)
-{
-	BOOL bRet;
-	bRet = DllMain (hDll, dwReason, lpReserved);
-	return bRet;
-}
-
diff --git a/tinyc/win32/lib/dllmain.c b/tinyc/win32/lib/dllmain.c
deleted file mode 100644
index 2c25b98b8..000000000
--- a/tinyc/win32/lib/dllmain.c
+++ /dev/null
@@ -1,9 +0,0 @@
-//+---------------------------------------------------------------------------
-
-#include <windows.h>
-
-BOOL WINAPI DllMain (HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved)
-{
-	return TRUE;
-}
-
diff --git a/tinyc/win32/lib/gdi32.def b/tinyc/win32/lib/gdi32.def
deleted file mode 100644
index 02766da43..000000000
--- a/tinyc/win32/lib/gdi32.def
+++ /dev/null
@@ -1,337 +0,0 @@
-LIBRARY gdi32.dll
-
-EXPORTS
-AbortDoc
-AbortPath
-AddFontResourceA
-AddFontResourceW
-AngleArc
-AnimatePalette
-Arc
-ArcTo
-BeginPath
-BitBlt
-ByeByeGDI
-CancelDC
-CheckColorsInGamut
-ChoosePixelFormat
-Chord
-CloseEnhMetaFile
-CloseFigure
-CloseMetaFile
-ColorCorrectPalette
-ColorMatchToTarget
-CombineRgn
-CombineTransform
-CopyEnhMetaFileA
-CopyEnhMetaFileW
-CopyMetaFileA
-CopyMetaFileW
-CreateBitmap
-CreateBitmapIndirect
-CreateBrushIndirect
-CreateColorSpaceA
-CreateColorSpaceW
-CreateCompatibleBitmap
-CreateCompatibleDC
-CreateDCA
-CreateDCW
-CreateDIBPatternBrush
-CreateDIBPatternBrushPt
-CreateDIBSection
-CreateDIBitmap
-CreateDiscardableBitmap
-CreateEllipticRgn
-CreateEllipticRgnIndirect
-CreateEnhMetaFileA
-CreateEnhMetaFileW
-CreateFontA
-CreateFontIndirectA
-CreateFontIndirectW
-CreateFontW
-CreateHalftonePalette
-CreateHatchBrush
-CreateICA
-CreateICW
-CreateMetaFileA
-CreateMetaFileW
-CreatePalette
-CreatePatternBrush
-CreatePen
-CreatePenIndirect
-CreatePolyPolygonRgn
-CreatePolygonRgn
-CreateRectRgn
-CreateRectRgnIndirect
-CreateRoundRectRgn
-CreateScalableFontResourceA
-CreateScalableFontResourceW
-CreateSolidBrush
-DPtoLP
-DeleteColorSpace
-DeleteDC
-DeleteEnhMetaFile
-DeleteMetaFile
-DeleteObject
-DescribePixelFormat
-DeviceCapabilitiesEx
-DeviceCapabilitiesExA
-DeviceCapabilitiesExW
-DrawEscape
-Ellipse
-EnableEUDC
-EndDoc
-EndPage
-EndPath
-EnumEnhMetaFile
-EnumFontFamiliesA
-EnumFontFamiliesExA
-EnumFontFamiliesExW
-EnumFontFamiliesW
-EnumFontsA
-EnumFontsW
-EnumICMProfilesA
-EnumICMProfilesW
-EnumMetaFile
-EnumObjects
-EqualRgn
-Escape
-ExcludeClipRect
-ExtCreatePen
-ExtCreateRegion
-ExtEscape
-ExtFloodFill
-ExtSelectClipRgn
-ExtTextOutA
-ExtTextOutW
-FillPath
-FillRgn
-FixBrushOrgEx
-FlattenPath
-FloodFill
-FrameRgn
-GdiComment
-GdiFlush
-GdiGetBatchLimit
-GdiPlayDCScript
-GdiPlayJournal
-GdiPlayScript
-GdiSetBatchLimit
-GetArcDirection
-GetAspectRatioFilterEx
-GetBitmapBits
-GetBitmapDimensionEx
-GetBkColor
-GetBkMode
-GetBoundsRect
-GetBrushOrgEx
-GetCharABCWidthsA
-GetCharABCWidthsFloatA
-GetCharABCWidthsFloatW
-GetCharABCWidthsW
-GetCharWidth32A
-GetCharWidth32W
-GetCharWidthA
-GetCharWidthFloatA
-GetCharWidthFloatW
-GetCharWidthW
-GetCharacterPlacementA
-GetCharacterPlacementW
-GetClipBox
-GetClipRgn
-GetColorAdjustment
-GetColorSpace
-GetCurrentObject
-GetCurrentPositionEx
-GetDCOrgEx
-GetDIBColorTable
-GetDIBits
-GetDeviceCaps
-GetDeviceGammaRamp
-GetEnhMetaFileA
-GetEnhMetaFileBits
-GetEnhMetaFileDescriptionA
-GetEnhMetaFileDescriptionW
-GetEnhMetaFileHeader
-GetEnhMetaFilePaletteEntries
-GetEnhMetaFileW
-GetFontData
-GetFontLanguageInfo
-GetFontResourceInfo
-GetGlyphOutline
-GetGlyphOutlineA
-GetGlyphOutlineW
-GetGraphicsMode
-GetICMProfileA
-GetICMProfileW
-GetKerningPairs
-GetKerningPairsA
-GetKerningPairsW
-GetLayout
-GetLogColorSpaceA
-GetLogColorSpaceW
-GetMapMode
-GetMetaFileA
-GetMetaFileBitsEx
-GetMetaFileW
-GetMetaRgn
-GetMiterLimit
-GetNearestColor
-GetNearestPaletteIndex
-GetObjectA
-GetObjectType
-GetObjectW
-GetOutlineTextMetricsA
-GetOutlineTextMetricsW
-GetPaletteEntries
-GetPath
-GetPixel
-GetPixelFormat
-GetPolyFillMode
-GetROP2
-GetRandomRgn
-GetRasterizerCaps
-GetRegionData
-GetRgnBox
-GetStockObject
-GetStretchBltMode
-GetSystemPaletteEntries
-GetSystemPaletteUse
-GetTextAlign
-GetTextCharacterExtra
-GetTextCharset
-GetTextCharsetInfo
-GetTextColor
-GetTextExtentExPointA
-GetTextExtentExPointW
-GetTextExtentPoint32A
-GetTextExtentPoint32W
-GetTextExtentPointA
-GetTextExtentPointW
-GetTextFaceA
-GetTextFaceW
-GetTextMetricsA
-GetTextMetricsW
-GetViewportExtEx
-GetViewportOrgEx
-GetWinMetaFileBits
-GetWindowExtEx
-GetWindowOrgEx
-GetWorldTransform
-IntersectClipRect
-InvertRgn
-LPtoDP
-LineDDA
-LineTo
-MaskBlt
-ModifyWorldTransform
-MoveToEx
-OffsetClipRgn
-OffsetRgn
-OffsetViewportOrgEx
-OffsetWindowOrgEx
-PaintRgn
-PatBlt
-PathToRegion
-Pie
-PlayEnhMetaFile
-PlayEnhMetaFileRecord
-PlayMetaFile
-PlayMetaFileRecord
-PlgBlt
-PolyBezier
-PolyBezierTo
-PolyDraw
-PolyPolygon
-PolyPolyline
-PolyTextOutA
-PolyTextOutW
-Polygon
-Polyline
-PolylineTo
-PtInRegion
-PtVisible
-RealizePalette
-RectInRegion
-RectVisible
-Rectangle
-RemoveFontResourceA
-RemoveFontResourceW
-ResetDCA
-ResetDCW
-ResizePalette
-RestoreDC
-RoundRect
-SaveDC
-ScaleViewportExtEx
-ScaleWindowExtEx
-SelectClipPath
-SelectClipRgn
-SelectObject
-SelectPalette
-SetAbortProc
-SetArcDirection
-SetBitmapBits
-SetBitmapDimensionEx
-SetBkColor
-SetBkMode
-SetBoundsRect
-SetBrushOrgEx
-SetColorAdjustment
-SetColorSpace
-SetDIBColorTable
-SetDIBits
-SetDIBitsToDevice
-SetDeviceGammaRamp
-SetEnhMetaFileBits
-SetFontEnumeration
-SetGraphicsMode
-SetICMMode
-SetICMProfileA
-SetICMProfileW
-SetLayout
-SetMagicColors
-SetMapMode
-SetMapperFlags
-SetMetaFileBitsEx
-SetMetaRgn
-SetMiterLimit
-SetObjectOwner
-SetPaletteEntries
-SetPixel
-SetPixelFormat
-SetPixelV
-SetPolyFillMode
-SetROP2
-SetRectRgn
-SetStretchBltMode
-SetSystemPaletteUse
-SetTextAlign
-SetTextCharacterExtra
-SetTextColor
-SetTextJustification
-SetViewportExtEx
-SetViewportOrgEx
-SetWinMetaFileBits
-SetWindowExtEx
-SetWindowOrgEx
-SetWorldTransform
-StartDocA
-StartDocW
-StartPage
-StretchBlt
-StretchDIBits
-StrokeAndFillPath
-StrokePath
-SwapBuffers
-TextOutA
-TextOutW
-TranslateCharsetInfo
-UnrealizeObject
-UpdateColors
-UpdateICMRegKeyA
-UpdateICMRegKeyW
-WidenPath
-gdiPlaySpoolStream
-pfnRealizePalette
-pfnSelectPalette
diff --git a/tinyc/win32/lib/kernel32.def b/tinyc/win32/lib/kernel32.def
deleted file mode 100644
index f03e17ba4..000000000
--- a/tinyc/win32/lib/kernel32.def
+++ /dev/null
@@ -1,770 +0,0 @@
-LIBRARY kernel32.dll
-
-EXPORTS
-AddAtomA
-AddAtomW
-AllocConsole
-AllocLSCallback
-AllocSLCallback
-AreFileApisANSI
-BackupRead
-BackupSeek
-BackupWrite
-Beep
-BeginUpdateResourceA
-BeginUpdateResourceW
-BuildCommDCBA
-BuildCommDCBAndTimeoutsA
-BuildCommDCBAndTimeoutsW
-BuildCommDCBW
-CallNamedPipeA
-CallNamedPipeW
-Callback12
-Callback16
-Callback20
-Callback24
-Callback28
-Callback32
-Callback36
-Callback4
-Callback40
-Callback44
-Callback48
-Callback52
-Callback56
-Callback60
-Callback64
-Callback8
-CancelDeviceWakeupRequest
-CancelIo
-CancelWaitableTimer
-ClearCommBreak
-ClearCommError
-CloseHandle
-CloseProfileUserMapping
-CloseSystemHandle
-CommConfigDialogA
-CommConfigDialogW
-CompareFileTime
-CompareStringA
-CompareStringW
-ConnectNamedPipe
-ContinueDebugEvent
-ConvertDefaultLocale
-ConvertThreadToFiber
-ConvertToGlobalHandle
-CopyFileA
-CopyFileExA
-CopyFileExW
-CopyFileW
-CreateConsoleScreenBuffer
-CreateDirectoryA
-CreateDirectoryExA
-CreateDirectoryExW
-CreateDirectoryW
-CreateEventA
-CreateEventW
-CreateFiber
-CreateFileA
-CreateFileMappingA
-CreateFileMappingW
-CreateFileW
-CreateIoCompletionPort
-CreateKernelThread
-CreateMailslotA
-CreateMailslotW
-CreateMutexA
-CreateMutexW
-CreateNamedPipeA
-CreateNamedPipeW
-CreatePipe
-CreateProcessA
-CreateProcessW
-CreateRemoteThread
-CreateSemaphoreA
-CreateSemaphoreW
-CreateSocketHandle
-CreateTapePartition
-CreateThread
-CreateToolhelp32Snapshot
-CreateWaitableTimerA
-CreateWaitableTimerW
-DebugActiveProcess
-DebugBreak
-DefineDosDeviceA
-DefineDosDeviceW
-DeleteAtom
-DeleteCriticalSection
-DeleteFiber
-DeleteFileA
-DeleteFileW
-DeviceIoControl
-DisableThreadLibraryCalls
-DisconnectNamedPipe
-DosDateTimeToFileTime
-DuplicateHandle
-EndUpdateResourceA
-EndUpdateResourceW
-EnterCriticalSection
-EnumCalendarInfoA
-EnumCalendarInfoExA
-EnumCalendarInfoExW
-EnumCalendarInfoW
-EnumDateFormatsA
-EnumDateFormatsExA
-EnumDateFormatsExW
-EnumDateFormatsW
-EnumLanguageGroupLocalesA
-EnumLanguageGroupLocalesW
-EnumResourceLanguagesA
-EnumResourceLanguagesW
-EnumResourceNamesA
-EnumResourceNamesW
-EnumResourceTypesA
-EnumResourceTypesW
-EnumSystemCodePagesA
-EnumSystemCodePagesW
-EnumSystemGeoID
-EnumSystemLanguageGroupsA
-EnumSystemLanguageGroupsW
-EnumSystemLocalesA
-EnumSystemLocalesW
-EnumTimeFormatsA
-EnumTimeFormatsW
-EnumUILanguagesA
-EnumUILanguagesW
-EraseTape
-EscapeCommFunction
-ExitProcess
-ExitThread
-ExpandEnvironmentStringsA
-ExpandEnvironmentStringsW
-FT_Exit0
-FT_Exit12
-FT_Exit16
-FT_Exit20
-FT_Exit24
-FT_Exit28
-FT_Exit32
-FT_Exit36
-FT_Exit4
-FT_Exit40
-FT_Exit44
-FT_Exit48
-FT_Exit52
-FT_Exit56
-FT_Exit8
-FT_Prolog
-FT_Thunk
-FatalAppExitA
-FatalAppExitW
-FatalExit
-FileTimeToDosDateTime
-FileTimeToLocalFileTime
-FileTimeToSystemTime
-FillConsoleOutputAttribute
-FillConsoleOutputCharacterA
-FillConsoleOutputCharacterW
-FindAtomA
-FindAtomW
-FindClose
-FindCloseChangeNotification
-FindFirstChangeNotificationA
-FindFirstChangeNotificationW
-FindFirstFileA
-FindFirstFileExA
-FindFirstFileExW
-FindFirstFileW
-FindNextChangeNotification
-FindNextFileA
-FindNextFileW
-FindResourceA
-FindResourceExA
-FindResourceExW
-FindResourceW
-FlushConsoleInputBuffer
-FlushFileBuffers
-FlushInstructionCache
-FlushViewOfFile
-FoldStringA
-FoldStringW
-FormatMessageA
-FormatMessageW
-FreeConsole
-FreeEnvironmentStringsA
-FreeEnvironmentStringsW
-FreeLSCallback
-FreeLibrary
-FreeLibraryAndExitThread
-FreeResource
-FreeSLCallback
-GenerateConsoleCtrlEvent
-GetACP
-GetAtomNameA
-GetAtomNameW
-GetBinaryType
-GetBinaryTypeA
-GetBinaryTypeW
-GetCPInfo
-GetCPInfoExA
-GetCPInfoExW
-GetCalendarInfoA
-GetCalendarInfoW
-GetCommConfig
-GetCommMask
-GetCommModemStatus
-GetCommProperties
-GetCommState
-GetCommTimeouts
-GetCommandLineA
-GetCommandLineW
-GetCompressedFileSizeA
-GetCompressedFileSizeW
-GetComputerNameA
-GetComputerNameW
-GetConsoleCP
-GetConsoleCursorInfo
-GetConsoleMode
-GetConsoleOutputCP
-GetConsoleScreenBufferInfo
-GetConsoleTitleA
-GetConsoleTitleW
-GetCurrencyFormatA
-GetCurrencyFormatW
-GetCurrentDirectoryA
-GetCurrentDirectoryW
-GetCurrentProcess
-GetCurrentProcessId
-GetCurrentThread
-GetCurrentThreadId
-GetDateFormatA
-GetDateFormatW
-GetDaylightFlag
-GetDefaultCommConfigA
-GetDefaultCommConfigW
-GetDevicePowerState
-GetDiskFreeSpaceA
-GetDiskFreeSpaceExA
-GetDiskFreeSpaceExW
-GetDiskFreeSpaceW
-GetDriveTypeA
-GetDriveTypeW
-GetEnvironmentStrings
-GetEnvironmentStringsA
-GetEnvironmentStringsW
-GetEnvironmentVariableA
-GetEnvironmentVariableW
-GetErrorMode
-GetExitCodeProcess
-GetExitCodeThread
-GetFileAttributesA
-GetFileAttributesExA
-GetFileAttributesExW
-GetFileAttributesW
-GetFileInformationByHandle
-GetFileSize
-GetFileTime
-GetFileType
-GetFullPathNameA
-GetFullPathNameW
-GetGeoInfoA
-GetGeoInfoW
-GetHandleContext
-GetHandleInformation
-GetLSCallbackTarget
-GetLSCallbackTemplate
-GetLargestConsoleWindowSize
-GetLastError
-GetLocalTime
-GetLocaleInfoA
-GetLocaleInfoW
-GetLogicalDriveStringsA
-GetLogicalDriveStringsW
-GetLogicalDrives
-GetLongPathNameA
-GetLongPathNameW
-GetMailslotInfo
-GetModuleFileNameA
-GetModuleFileNameW
-GetModuleHandleA
-GetModuleHandleW
-GetModuleHandleExA
-GetModuleHandleExW
-GetNamedPipeHandleStateA
-GetNamedPipeHandleStateW
-GetNamedPipeInfo
-GetNumberFormatA
-GetNumberFormatW
-GetNumberOfConsoleInputEvents
-GetNumberOfConsoleMouseButtons
-GetOEMCP
-GetOverlappedResult
-GetPriorityClass
-GetPrivateProfileIntA
-GetPrivateProfileIntW
-GetPrivateProfileSectionA
-GetPrivateProfileSectionNamesA
-GetPrivateProfileSectionNamesW
-GetPrivateProfileSectionW
-GetPrivateProfileStringA
-GetPrivateProfileStringW
-GetPrivateProfileStructA
-GetPrivateProfileStructW
-GetProcAddress
-GetProcessAffinityMask
-GetProcessFlags
-GetProcessHeap
-GetProcessHeaps
-GetProcessPriorityBoost
-GetProcessShutdownParameters
-GetProcessTimes
-GetProcessVersion
-GetProcessWorkingSetSize
-GetProductName
-GetProfileIntA
-GetProfileIntW
-GetProfileSectionA
-GetProfileSectionW
-GetProfileStringA
-GetProfileStringW
-GetQueuedCompletionStatus
-GetSLCallbackTarget
-GetSLCallbackTemplate
-GetShortPathNameA
-GetShortPathNameW
-GetStartupInfoA
-GetStartupInfoW
-GetStdHandle
-GetStringTypeA
-GetStringTypeExA
-GetStringTypeExW
-GetStringTypeW
-GetSystemDefaultLCID
-GetSystemDefaultLangID
-GetSystemDefaultUILanguage
-GetSystemDirectoryA
-GetSystemDirectoryW
-GetSystemInfo
-GetSystemPowerStatus
-GetSystemTime
-GetSystemTimeAdjustment
-GetSystemTimeAsFileTime
-GetTapeParameters
-GetTapePosition
-GetTapeStatus
-GetTempFileNameA
-GetTempFileNameW
-GetTempPathA
-GetTempPathW
-GetThreadContext
-GetThreadLocale
-GetThreadPriority
-GetThreadPriorityBoost
-GetThreadSelectorEntry
-GetThreadTimes
-GetTickCount
-GetTimeFormatA
-GetTimeFormatW
-GetTimeZoneInformation
-GetUserDefaultLCID
-GetUserDefaultLangID
-GetUserDefaultUILanguage
-GetUserGeoID
-GetVersion
-GetVersionExA
-GetVersionExW
-GetVolumeInformationA
-GetVolumeInformationW
-GetWindowsDirectoryA
-GetWindowsDirectoryW
-GetWriteWatch
-GlobalAddAtomA
-GlobalAddAtomW
-GlobalAlloc
-GlobalCompact
-GlobalDeleteAtom
-GlobalFindAtomA
-GlobalFindAtomW
-GlobalFix
-GlobalFlags
-GlobalFree
-GlobalGetAtomNameA
-GlobalGetAtomNameW
-GlobalHandle
-GlobalLock
-GlobalMemoryStatus
-GlobalReAlloc
-GlobalSize
-GlobalUnWire
-GlobalUnfix
-GlobalUnlock
-GlobalWire
-Heap32First
-Heap32ListFirst
-Heap32ListNext
-Heap32Next
-HeapAlloc
-HeapCompact
-HeapCreate
-HeapDestroy
-HeapFree
-HeapLock
-HeapReAlloc
-HeapSetFlags
-HeapSize
-HeapUnlock
-HeapValidate
-HeapWalk
-InitAtomTable
-InitializeCriticalSection
-InitializeCriticalSectionAndSpinCount
-InterlockedCompareExchange
-InterlockedDecrement
-InterlockedExchange
-InterlockedExchangeAdd
-InterlockedIncrement
-InvalidateNLSCache
-IsBadCodePtr
-IsBadHugeReadPtr
-IsBadHugeWritePtr
-IsBadReadPtr
-IsBadStringPtrA
-IsBadStringPtrW
-IsBadWritePtr
-IsDBCSLeadByte
-IsDBCSLeadByteEx
-IsDebuggerPresent
-IsLSCallback
-IsProcessorFeaturePresent
-IsSLCallback
-IsSystemResumeAutomatic
-IsValidCodePage
-IsValidLanguageGroup
-IsValidLocale
-K32Thk1632Epilog
-K32Thk1632Prolog
-K32_NtCreateFile
-K32_RtlNtStatusToDosError
-LCMapStringA
-LCMapStringW
-LeaveCriticalSection
-LoadLibraryA
-LoadLibraryExA
-LoadLibraryExW
-LoadLibraryW
-LoadModule
-LoadResource
-LocalAlloc
-LocalCompact
-LocalFileTimeToFileTime
-LocalFlags
-LocalFree
-LocalHandle
-LocalLock
-LocalReAlloc
-LocalShrink
-LocalSize
-LocalUnlock
-LockFile
-LockFileEx
-LockResource
-MakeCriticalSectionGlobal
-MapHInstLS
-MapHInstLS_PN
-MapHInstSL
-MapHInstSL_PN
-MapHModuleLS
-MapHModuleSL
-MapLS
-MapSL
-MapSLFix
-MapViewOfFile
-MapViewOfFileEx
-Module32First
-Module32Next
-MoveFileA
-MoveFileExA
-MoveFileExW
-MoveFileW
-MulDiv
-MultiByteToWideChar
-NotifyNLSUserCache
-OpenEventA
-OpenEventW
-OpenFile
-OpenFileMappingA
-OpenFileMappingW
-OpenMutexA
-OpenMutexW
-OpenProcess
-OpenProfileUserMapping
-OpenSemaphoreA
-OpenSemaphoreW
-OpenThread
-OpenVxDHandle
-OpenWaitableTimerA
-OpenWaitableTimerW
-OutputDebugStringA
-OutputDebugStringW
-PeekConsoleInputA
-PeekConsoleInputW
-PeekNamedPipe
-PostQueuedCompletionStatus
-PrepareTape
-Process32First
-Process32Next
-PulseEvent
-PurgeComm
-QT_Thunk
-QueryDosDeviceA
-QueryDosDeviceW
-QueryNumberOfEventLogRecords
-QueryOldestEventLogRecord
-QueryPerformanceCounter
-QueryPerformanceFrequency
-QueueUserAPC
-RaiseException
-ReadConsoleA
-ReadConsoleInputA
-ReadConsoleInputW
-ReadConsoleOutputA
-ReadConsoleOutputAttribute
-ReadConsoleOutputCharacterA
-ReadConsoleOutputCharacterW
-ReadConsoleOutputW
-ReadConsoleW
-ReadDirectoryChangesW
-ReadFile
-ReadFileEx
-ReadFileScatter
-ReadProcessMemory
-RegisterServiceProcess
-RegisterSysMsgHandler
-ReinitializeCriticalSection
-ReleaseMutex
-ReleaseSemaphore
-RemoveDirectoryA
-RemoveDirectoryW
-RequestDeviceWakeup
-RequestWakeupLatency
-ResetEvent
-ResetNLSUserInfoCache
-ResetWriteWatch
-ResumeThread
-RtlAddFunctionTable
-RtlDeleteFunctionTable
-RtlFillMemory
-RtlInstallFunctionTableCallback
-RtlMoveMemory
-RtlUnwind
-RtlUnwindEx
-RtlZeroMemory
-SMapLS
-SMapLS_IP_EBP_12
-SMapLS_IP_EBP_16
-SMapLS_IP_EBP_20
-SMapLS_IP_EBP_24
-SMapLS_IP_EBP_28
-SMapLS_IP_EBP_32
-SMapLS_IP_EBP_36
-SMapLS_IP_EBP_40
-SMapLS_IP_EBP_8
-SUnMapLS
-SUnMapLS_IP_EBP_12
-SUnMapLS_IP_EBP_16
-SUnMapLS_IP_EBP_20
-SUnMapLS_IP_EBP_24
-SUnMapLS_IP_EBP_28
-SUnMapLS_IP_EBP_32
-SUnMapLS_IP_EBP_36
-SUnMapLS_IP_EBP_40
-SUnMapLS_IP_EBP_8
-ScrollConsoleScreenBufferA
-ScrollConsoleScreenBufferW
-SearchPathA
-SearchPathW
-SetCalendarInfoA
-SetCalendarInfoW
-SetCommBreak
-SetCommConfig
-SetCommMask
-SetCommState
-SetCommTimeouts
-SetComputerNameA
-SetComputerNameW
-SetConsoleActiveScreenBuffer
-SetConsoleCP
-SetConsoleCtrlHandler
-SetConsoleCursorInfo
-SetConsoleCursorPosition
-SetConsoleMode
-SetConsoleOutputCP
-SetConsoleScreenBufferSize
-SetConsoleTextAttribute
-SetConsoleTitleA
-SetConsoleTitleW
-SetConsoleWindowInfo
-SetCriticalSectionSpinCount
-SetCurrentDirectoryA
-SetCurrentDirectoryW
-SetDaylightFlag
-SetDefaultCommConfigA
-SetDefaultCommConfigW
-SetEndOfFile
-SetEnvironmentVariableA
-SetEnvironmentVariableW
-SetErrorMode
-SetEvent
-SetFileApisToANSI
-SetFileApisToOEM
-SetFileAttributesA
-SetFileAttributesW
-SetFilePointer
-SetFilePointerEx
-SetFileTime
-SetHandleContext
-SetHandleCount
-SetHandleInformation
-SetLastError
-SetLocalTime
-SetLocaleInfoA
-SetLocaleInfoW
-SetMailslotInfo
-SetMessageWaitingIndicator
-SetNamedPipeHandleState
-SetPriorityClass
-SetProcessAffinityMask
-SetProcessPriorityBoost
-SetProcessShutdownParameters
-SetProcessWorkingSetSize
-SetStdHandle
-SetSystemPowerState
-SetSystemTime
-SetSystemTimeAdjustment
-SetTapeParameters
-SetTapePosition
-SetThreadAffinityMask
-SetThreadContext
-SetThreadExecutionState
-SetThreadIdealProcessor
-SetThreadLocale
-SetThreadPriority
-SetThreadPriorityBoost
-SetTimeZoneInformation
-SetUnhandledExceptionFilter
-SetUserGeoID
-SetVolumeLabelA
-SetVolumeLabelW
-SetWaitableTimer
-SetupComm
-SignalObjectAndWait
-SignalSysMsgHandlers
-SizeofResource
-Sleep
-SleepEx
-SuspendThread
-SwitchToFiber
-SwitchToThread
-SystemTimeToFileTime
-SystemTimeToTzSpecificLocalTime
-TerminateProcess
-TerminateThread
-Thread32First
-Thread32Next
-ThunkConnect32
-TlsAlloc
-TlsAllocInternal
-TlsFree
-TlsFreeInternal
-TlsGetValue
-TlsSetValue
-Toolhelp32ReadProcessMemory
-TransactNamedPipe
-TransmitCommChar
-TryEnterCriticalSection
-UTRegister
-UTUnRegister
-UnMapLS
-UnMapSLFixArray
-UnhandledExceptionFilter
-UninitializeCriticalSection
-UnlockFile
-UnlockFileEx
-UnmapViewOfFile
-UpdateResourceA
-UpdateResourceW
-VerLanguageNameA
-VerLanguageNameW
-VirtualAlloc
-VirtualAllocEx
-VirtualFree
-VirtualFreeEx
-VirtualLock
-VirtualProtect
-VirtualProtectEx
-VirtualQuery
-VirtualQueryEx
-VirtualUnlock
-WaitCommEvent
-WaitForDebugEvent
-WaitForMultipleObjects
-WaitForMultipleObjectsEx
-WaitForSingleObject
-WaitForSingleObjectEx
-WaitNamedPipeA
-WaitNamedPipeW
-WideCharToMultiByte
-WinExec
-WriteConsoleA
-WriteConsoleInputA
-WriteConsoleInputW
-WriteConsoleOutputA
-WriteConsoleOutputAttribute
-WriteConsoleOutputCharacterA
-WriteConsoleOutputCharacterW
-WriteConsoleOutputW
-WriteConsoleW
-WriteFile
-WriteFileEx
-WriteFileGather
-WritePrivateProfileSectionA
-WritePrivateProfileSectionW
-WritePrivateProfileStringA
-WritePrivateProfileStringW
-WritePrivateProfileStructA
-WritePrivateProfileStructW
-WriteProcessMemory
-WriteProfileSectionA
-WriteProfileSectionW
-WriteProfileStringA
-WriteProfileStringW
-WriteTapemark
-_DebugOut
-_DebugPrintf
-_hread
-_hwrite
-_lclose
-_lcreat
-_llseek
-_lopen
-_lread
-_lwrite
-dprintf
-lstrcat
-lstrcatA
-lstrcatW
-lstrcmp
-lstrcmpA
-lstrcmpW
-lstrcmpi
-lstrcmpiA
-lstrcmpiW
-lstrcpy
-lstrcpyA
-lstrcpyW
-lstrcpyn
-lstrcpynA
-lstrcpynW
-lstrlen
-lstrlenA
-lstrlenW
diff --git a/tinyc/win32/lib/msvcrt.def b/tinyc/win32/lib/msvcrt.def
deleted file mode 100644
index 742acb8bb..000000000
--- a/tinyc/win32/lib/msvcrt.def
+++ /dev/null
@@ -1,1399 +0,0 @@
-LIBRARY msvcrt.dll
-
-EXPORTS
-$I10_OUTPUT
-??0__non_rtti_object@@QAE@ABV0@@Z
-??0__non_rtti_object@@QAE@PBD@Z
-??0bad_cast@@AAE@PBQBD@Z
-??0bad_cast@@QAE@ABQBD@Z
-??0bad_cast@@QAE@ABV0@@Z
-??0bad_cast@@QAE@PBD@Z
-??0bad_typeid@@QAE@ABV0@@Z
-??0bad_typeid@@QAE@PBD@Z
-??0exception@@QAE@ABQBD@Z
-??0exception@@QAE@ABQBDH@Z
-??0exception@@QAE@ABV0@@Z
-??0exception@@QAE@XZ
-??1__non_rtti_object@@UAE@XZ
-??1bad_cast@@UAE@XZ
-??1bad_typeid@@UAE@XZ
-??1exception@@UAE@XZ
-??1type_info@@UAE@XZ
-??2@YAPAXI@Z
-??2@YAPAXIHPBDH@Z
-??3@YAXPAX@Z
-??4__non_rtti_object@@QAEAAV0@ABV0@@Z
-??4bad_cast@@QAEAAV0@ABV0@@Z
-??4bad_typeid@@QAEAAV0@ABV0@@Z
-??4exception@@QAEAAV0@ABV0@@Z
-??8type_info@@QBEHABV0@@Z
-??9type_info@@QBEHABV0@@Z
-??_7__non_rtti_object@@6B@
-??_7bad_cast@@6B@
-??_7bad_typeid@@6B@
-??_7exception@@6B@
-??_E__non_rtti_object@@UAEPAXI@Z
-??_Ebad_cast@@UAEPAXI@Z
-??_Ebad_typeid@@UAEPAXI@Z
-??_Eexception@@UAEPAXI@Z
-??_Fbad_cast@@QAEXXZ
-??_Fbad_typeid@@QAEXXZ
-??_G__non_rtti_object@@UAEPAXI@Z
-??_Gbad_cast@@UAEPAXI@Z
-??_Gbad_typeid@@UAEPAXI@Z
-??_Gexception@@UAEPAXI@Z
-??_U@YAPAXI@Z
-??_U@YAPAXIHPBDH@Z
-??_V@YAXPAX@Z
-?_query_new_handler@@YAP6AHI@ZXZ
-?_query_new_mode@@YAHXZ
-?_set_new_handler@@YAP6AHI@ZP6AHI@Z@Z
-?_set_new_mode@@YAHH@Z
-?_set_se_translator@@YAP6AXIPAU_EXCEPTION_POINTERS@@@ZP6AXI0@Z@Z
-?before@type_info@@QBEHABV1@@Z
-?name@type_info@@QBEPBDXZ
-?raw_name@type_info@@QBEPBDXZ
-?set_new_handler@@YAP6AXXZP6AXXZ@Z
-?set_terminate@@YAP6AXXZP6AXXZ@Z
-?set_unexpected@@YAP6AXXZP6AXXZ@Z
-?terminate@@YAXXZ
-?unexpected@@YAXXZ
-?what@exception@@UBEPBDXZ
-_CIacos
-_CIasin
-_CIatan
-_CIatan2
-_CIcos
-_CIcosh
-_CIexp
-_CIfmod
-_CIlog
-_CIlog10
-_CIpow
-_CIsin
-_CIsinh
-_CIsqrt
-_CItan
-_CItanh
-_CrtCheckMemory
-_CrtDbgBreak
-_CrtDbgReport
-_CrtDbgReportV
-_CrtDbgReportW
-_CrtDbgReportWV
-_CrtDoForAllClientObjects
-_CrtDumpMemoryLeaks
-_CrtIsMemoryBlock
-_CrtIsValidHeapPointer
-_CrtIsValidPointer
-_CrtMemCheckpoint
-_CrtMemDifference
-_CrtMemDumpAllObjectsSince
-_CrtMemDumpStatistics
-_CrtReportBlockType
-_CrtSetAllocHook
-_CrtSetBreakAlloc
-_CrtSetDbgBlockType
-_CrtSetDbgFlag
-_CrtSetDumpClient
-_CrtSetReportFile
-_CrtSetReportHook
-_CrtSetReportHook2
-_CrtSetReportMode
-_CxxThrowException
-_EH_prolog
-_Getdays
-_Getmonths
-_Gettnames
-_HUGE
-_Strftime
-_XcptFilter
-__CppXcptFilter
-__CxxCallUnwindDelDtor
-__CxxCallUnwindDtor
-__CxxCallUnwindVecDtor
-__CxxDetectRethrow
-__CxxExceptionFilter
-__CxxFrameHandler
-__CxxFrameHandler2
-__CxxFrameHandler3
-__CxxLongjmpUnwind
-__CxxQueryExceptionSize
-__CxxRegisterExceptionObject
-__CxxUnregisterExceptionObject
-__DestructExceptionObject
-__RTCastToVoid
-__RTDynamicCast
-__RTtypeid
-__STRINGTOLD
-___lc_codepage_func
-___lc_collate_cp_func
-___lc_handle_func
-___mb_cur_max_func
-___setlc_active_func
-___unguarded_readlc_active_add_func
-__argc
-__argv
-__badioinfo
-__crtCompareStringA
-__crtCompareStringW
-__crtGetLocaleInfoW
-__crtGetStringTypeW
-__crtLCMapStringA
-__crtLCMapStringW
-__daylight
-__dllonexit
-__doserrno
-__dstbias
-__fpecode
-__getmainargs
-__initenv
-__iob_func
-__isascii
-__iscsym
-__iscsymf
-__lc_codepage
-__lc_collate_cp
-__lc_handle
-__lconv_init
-__libm_sse2_acos
-__libm_sse2_acosf
-__libm_sse2_asin
-__libm_sse2_asinf
-__libm_sse2_atan
-__libm_sse2_atan2
-__libm_sse2_atanf
-__libm_sse2_cos
-__libm_sse2_cosf
-__libm_sse2_exp
-__libm_sse2_expf
-__libm_sse2_log
-__libm_sse2_log10
-__libm_sse2_log10f
-__libm_sse2_logf
-__libm_sse2_pow
-__libm_sse2_powf
-__libm_sse2_sin
-__libm_sse2_sinf
-__libm_sse2_tan
-__libm_sse2_tanf
-__mb_cur_max
-__p___argc
-__p___argv
-__p___initenv
-__p___mb_cur_max
-__p___wargv
-__p___winitenv
-__p__acmdln
-__p__amblksiz
-__p__commode
-__p__daylight
-__p__dstbias
-__p__environ
-__p__fileinfo
-__p__fmode
-__p__iob
-__p__mbcasemap
-__p__mbctype
-__p__osver
-__p__pctype
-__p__pgmptr
-__p__pwctype
-__p__timezone
-__p__tzname
-__p__wcmdln
-__p__wenviron
-__p__winmajor
-__p__winminor
-__p__winver
-__p__wpgmptr
-__pctype_func
-__pioinfo
-__pwctype_func
-__pxcptinfoptrs
-__set_app_type
-__setlc_active
-__setusermatherr
-__strncnt
-__threadhandle
-__threadid
-__toascii
-__unDName
-__unDNameEx
-__uncaught_exception
-__unguarded_readlc_active
-__wargv
-__wcserror
-__wcserror_s
-__wcsncnt
-__wgetmainargs
-__winitenv
-_abnormal_termination
-_abs64
-_access
-_access_s
-_acmdln
-_adj_fdiv_m16i
-_adj_fdiv_m32
-_adj_fdiv_m32i
-_adj_fdiv_m64
-_adj_fdiv_r
-_adj_fdivr_m16i
-_adj_fdivr_m32
-_adj_fdivr_m32i
-_adj_fdivr_m64
-_adj_fpatan
-_adj_fprem
-_adj_fprem1
-_adj_fptan
-_adjust_fdiv
-_aexit_rtn
-_aligned_free
-_aligned_free_dbg
-_aligned_malloc
-_aligned_malloc_dbg
-_aligned_offset_malloc
-_aligned_offset_malloc_dbg
-_aligned_offset_realloc
-_aligned_offset_realloc_dbg
-_aligned_realloc
-_aligned_realloc_dbg
-_amsg_exit
-_assert
-_atodbl
-_atodbl_l
-_atof_l
-_atoflt_l
-_atoi64
-_atoi64_l
-_atoi_l
-_atol_l
-_atoldbl
-_atoldbl_l
-_beep
-_beginthread
-_beginthreadex
-_c_exit
-_cabs
-_callnewh
-_calloc_dbg
-_cexit
-_cgets
-_cgets_s
-_cgetws
-_cgetws_s
-_chdir
-_chdrive
-_chgsign
-_chkesp
-_chmod
-_chsize
-_chsize_s
-_chvalidator
-_chvalidator_l
-_clearfp
-_close
-_commit
-_commode
-_control87
-_controlfp
-_controlfp_s
-_copysign
-_cprintf
-_cprintf_l
-_cprintf_p
-_cprintf_p_l
-_cprintf_s
-_cprintf_s_l
-_cputs
-_cputws
-_creat
-_crtAssertBusy
-_crtBreakAlloc
-_crtDbgFlag
-_cscanf
-_cscanf_l
-_cscanf_s
-_cscanf_s_l
-_ctime32
-_ctime32_s
-_ctime64
-_ctime64_s
-_ctype
-_cwait
-_cwprintf
-_cwprintf_l
-_cwprintf_p
-_cwprintf_p_l
-_cwprintf_s
-_cwprintf_s_l
-_cwscanf
-_cwscanf_l
-_cwscanf_s
-_cwscanf_s_l
-_daylight
-_difftime32
-_difftime64
-_dstbias
-_dup
-_dup2
-_ecvt
-_ecvt_s
-_endthread
-_endthreadex
-_environ
-_eof
-_errno
-_except_handler2
-_except_handler3
-_except_handler4_common
-_execl
-_execle
-_execlp
-_execlpe
-_execv
-_execve
-_execvp
-_execvpe
-_exit
-_expand
-_expand_dbg
-_fcloseall
-_fcvt
-_fcvt_s
-_fdopen
-_fgetchar
-_fgetwchar
-_filbuf
-_fileinfo
-_filelength
-_filelengthi64
-_fileno
-_findclose
-_findfirst
-_findfirst64
-_findfirsti64
-_findnext
-_findnext64
-_findnexti64
-_finite
-_flsbuf
-_flushall
-_fmode
-_fpclass
-_fpieee_flt
-_fpreset
-_fprintf_l
-_fprintf_p
-_fprintf_p_l
-_fprintf_s_l
-_fputchar
-_fputwchar
-_free_dbg
-_freea
-_freea_s
-_fscanf_l
-_fscanf_s_l
-_fseeki64
-_fsopen
-_fstat
-_fstat64
-_fstati64
-_ftime
-_ftime32
-_ftime32_s
-_ftime64
-_ftime64_s
-_ftol
-_ftol2
-_ftol2_sse
-_ftol2_sse_excpt
-_fullpath
-_fullpath_dbg
-_futime
-_futime32
-_futime64
-_fwprintf_l
-_fwprintf_p
-_fwprintf_p_l
-_fwprintf_s_l
-_fwscanf_l
-_fwscanf_s_l
-_gcvt
-_gcvt_s
-_get_doserrno
-_get_environ
-_get_errno
-_get_fileinfo
-_get_fmode
-_get_heap_handle
-_get_osfhandle
-_get_osplatform
-_get_osver
-_get_output_format
-_get_pgmptr
-_get_sbh_threshold
-_get_wenviron
-_get_winmajor
-_get_winminor
-_get_winver
-_get_wpgmptr
-_getch
-_getche
-_getcwd
-_getdcwd
-_getdiskfree
-_getdllprocaddr
-_getdrive
-_getdrives
-_getmaxstdio
-_getmbcp
-_getpid
-_getsystime
-_getw
-_getwch
-_getwche
-_getws
-_global_unwind2
-_gmtime32
-_gmtime32_s
-_gmtime64
-_gmtime64_s
-_heapadd
-_heapchk
-_heapmin
-_heapset
-_heapused
-_heapwalk
-_hypot
-_i64toa
-_i64toa_s
-_i64tow
-_i64tow_s
-_initterm
-_initterm_e
-_inp
-_inpd
-_inpw
-_invalid_parameter
-_iob
-_isalnum_l
-_isalpha_l
-_isatty
-_iscntrl_l
-_isctype
-_isctype_l
-_isdigit_l
-_isgraph_l
-_isleadbyte_l
-_islower_l
-_ismbbalnum
-_ismbbalnum_l
-_ismbbalpha
-_ismbbalpha_l
-_ismbbgraph
-_ismbbgraph_l
-_ismbbkalnum
-_ismbbkalnum_l
-_ismbbkana
-_ismbbkana_l
-_ismbbkprint
-_ismbbkprint_l
-_ismbbkpunct
-_ismbbkpunct_l
-_ismbblead
-_ismbblead_l
-_ismbbprint
-_ismbbprint_l
-_ismbbpunct
-_ismbbpunct_l
-_ismbbtrail
-_ismbbtrail_l
-_ismbcalnum
-_ismbcalnum_l
-_ismbcalpha
-_ismbcalpha_l
-_ismbcdigit
-_ismbcdigit_l
-_ismbcgraph
-_ismbcgraph_l
-_ismbchira
-_ismbchira_l
-_ismbckata
-_ismbckata_l
-_ismbcl0
-_ismbcl0_l
-_ismbcl1
-_ismbcl1_l
-_ismbcl2
-_ismbcl2_l
-_ismbclegal
-_ismbclegal_l
-_ismbclower
-_ismbclower_l
-_ismbcprint
-_ismbcprint_l
-_ismbcpunct
-_ismbcpunct_l
-_ismbcspace
-_ismbcspace_l
-_ismbcsymbol
-_ismbcsymbol_l
-_ismbcupper
-_ismbcupper_l
-_ismbslead
-_ismbslead_l
-_ismbstrail
-_ismbstrail_l
-_isnan
-_isprint_l
-_isspace_l
-_isupper_l
-_iswalnum_l
-_iswalpha_l
-_iswcntrl_l
-_iswctype_l
-_iswdigit_l
-_iswgraph_l
-_iswlower_l
-_iswprint_l
-_iswpunct_l
-_iswspace_l
-_iswupper_l
-_iswxdigit_l
-_isxdigit_l
-_itoa
-_itoa_s
-_itow
-_itow_s
-_j0
-_j1
-_jn
-_kbhit
-_lfind
-_lfind_s
-_loaddll
-_local_unwind2
-_local_unwind4
-_localtime32
-_localtime32_s
-_localtime64
-_localtime64_s
-_lock
-_locking
-_logb
-_longjmpex
-_lrotl
-_lrotr
-_lsearch
-_lsearch_s
-_lseek
-_lseeki64
-_ltoa
-_ltoa_s
-_ltow
-_ltow_s
-_makepath
-_makepath_s
-_malloc_dbg
-_mbbtombc
-_mbbtombc_l
-_mbbtype
-_mbcasemap
-_mbccpy
-_mbccpy_l
-_mbccpy_s
-_mbccpy_s_l
-_mbcjistojms
-_mbcjistojms_l
-_mbcjmstojis
-_mbcjmstojis_l
-_mbclen
-_mbclen_l
-_mbctohira
-_mbctohira_l
-_mbctokata
-_mbctokata_l
-_mbctolower
-_mbctolower_l
-_mbctombb
-_mbctombb_l
-_mbctoupper
-_mbctoupper_l
-_mbctype
-_mblen_l
-_mbsbtype
-_mbsbtype_l
-_mbscat
-_mbscat_s
-_mbscat_s_l
-_mbschr
-_mbschr_l
-_mbscmp
-_mbscmp_l
-_mbscoll
-_mbscoll_l
-_mbscpy
-_mbscpy_s
-_mbscpy_s_l
-_mbscspn
-_mbscspn_l
-_mbsdec
-_mbsdec_l
-_mbsdup
-_mbsicmp
-_mbsicmp_l
-_mbsicoll
-_mbsicoll_l
-_mbsinc
-_mbsinc_l
-_mbslen
-_mbslen_l
-_mbslwr
-_mbslwr_l
-_mbslwr_s
-_mbslwr_s_l
-_mbsnbcat
-_mbsnbcat_l
-_mbsnbcat_s
-_mbsnbcat_s_l
-_mbsnbcmp
-_mbsnbcmp_l
-_mbsnbcnt
-_mbsnbcnt_l
-_mbsnbcoll
-_mbsnbcoll_l
-_mbsnbcpy
-_mbsnbcpy_l
-_mbsnbcpy_s
-_mbsnbcpy_s_l
-_mbsnbicmp
-_mbsnbicmp_l
-_mbsnbicoll
-_mbsnbicoll_l
-_mbsnbset
-_mbsnbset_l
-_mbsnbset_s
-_mbsnbset_s_l
-_mbsncat
-_mbsncat_l
-_mbsncat_s
-_mbsncat_s_l
-_mbsnccnt
-_mbsnccnt_l
-_mbsncmp
-_mbsncmp_l
-_mbsncoll
-_mbsncoll_l
-_mbsncpy
-_mbsncpy_l
-_mbsncpy_s
-_mbsncpy_s_l
-_mbsnextc
-_mbsnextc_l
-_mbsnicmp
-_mbsnicmp_l
-_mbsnicoll
-_mbsnicoll_l
-_mbsninc
-_mbsninc_l
-_mbsnlen
-_mbsnlen_l
-_mbsnset
-_mbsnset_l
-_mbsnset_s
-_mbsnset_s_l
-_mbspbrk
-_mbspbrk_l
-_mbsrchr
-_mbsrchr_l
-_mbsrev
-_mbsrev_l
-_mbsset
-_mbsset_l
-_mbsset_s
-_mbsset_s_l
-_mbsspn
-_mbsspn_l
-_mbsspnp
-_mbsspnp_l
-_mbsstr
-_mbsstr_l
-_mbstok
-_mbstok_l
-_mbstok_s
-_mbstok_s_l
-_mbstowcs_l
-_mbstowcs_s_l
-_mbstrlen
-_mbstrlen_l
-_mbstrnlen
-_mbstrnlen_l
-_mbsupr
-_mbsupr_l
-_mbsupr_s
-_mbsupr_s_l
-_mbtowc_l
-_memccpy
-_memicmp
-_memicmp_l
-_mkdir
-_mkgmtime
-_mkgmtime32
-_mkgmtime64
-_mktemp
-_mktemp_s
-_mktime32
-_mktime64
-_msize
-_msize_debug
-_nextafter
-_onexit
-_open
-_open_osfhandle
-_osplatform
-_osver
-_outp
-_outpd
-_outpw
-_pclose
-_pctype
-_pgmptr
-_pipe
-_popen
-_printf_l
-_printf_p
-_printf_p_l
-_printf_s_l
-_purecall
-_putch
-_putenv
-_putenv_s
-_putw
-_putwch
-_putws
-_pwctype
-_read
-_realloc_dbg
-_resetstkoflw
-_rmdir
-_rmtmp
-_rotl
-_rotl64
-_rotr
-_rotr64
-_safe_fdiv
-_safe_fdivr
-_safe_fprem
-_safe_fprem1
-_scalb
-_scanf_l
-_scanf_s_l
-_scprintf
-_scprintf_l
-_scprintf_p_l
-_scwprintf
-_scwprintf_l
-_scwprintf_p_l
-_searchenv
-_searchenv_s
-_seh_longjmp_unwind
-_seh_longjmp_unwind4
-_set_SSE2_enable
-_set_controlfp
-_set_doserrno
-_set_errno
-_set_error_mode
-_set_fileinfo
-_set_fmode
-_set_output_format
-_set_sbh_threshold
-_seterrormode
-_setjmp
-_setjmp3
-_setmaxstdio
-_setmbcp
-_setmode
-_setsystime
-_sleep
-_snprintf
-_snprintf_c
-_snprintf_c_l
-_snprintf_l
-_snprintf_s
-_snprintf_s_l
-_snscanf
-_snscanf_l
-_snscanf_s
-_snscanf_s_l
-_snwprintf
-_snwprintf_l
-_snwprintf_s
-_snwprintf_s_l
-_snwscanf
-_snwscanf_l
-_snwscanf_s
-_snwscanf_s_l
-_sopen
-_sopen_s
-_spawnl
-_spawnle
-_spawnlp
-_spawnlpe
-_spawnv
-_spawnve
-_spawnvp
-_spawnvpe
-_splitpath
-_splitpath_s
-_sprintf_l
-_sprintf_p_l
-_sprintf_s_l
-_sscanf_l
-_sscanf_s_l
-_stat
-_stat64
-_stati64
-_statusfp
-_strcmpi
-_strcoll_l
-_strdate
-_strdate_s
-_strdup
-_strdup_dbg
-_strerror
-_strerror_s
-_stricmp
-_stricmp_l
-_stricoll
-_stricoll_l
-_strlwr
-_strlwr_l
-_strlwr_s
-_strlwr_s_l
-_strncoll
-_strncoll_l
-_strnicmp
-_strnicmp_l
-_strnicoll
-_strnicoll_l
-_strnset
-_strnset_s
-_strrev
-_strset
-_strset_s
-_strtime
-_strtime_s
-_strtod_l
-_strtoi64
-_strtoi64_l
-_strtol_l
-_strtoui64
-_strtoui64_l
-_strtoul_l
-_strupr
-_strupr_l
-_strupr_s
-_strupr_s_l
-_strxfrm_l
-_swab
-_swprintf
-_swprintf_c
-_swprintf_c_l
-_swprintf_p_l
-_swprintf_s_l
-_swscanf_l
-_swscanf_s_l
-_sys_errlist
-_sys_nerr
-_tell
-_telli64
-_tempnam
-_tempnam_dbg
-_time32
-_time64
-_timezone
-_tolower
-_tolower_l
-_toupper
-_toupper_l
-_towlower_l
-_towupper_l
-_tzname
-_tzset
-_ui64toa
-_ui64toa_s
-_ui64tow
-_ui64tow_s
-_ultoa
-_ultoa_s
-_ultow
-_ultow_s
-_umask
-_umask_s
-_ungetch
-_ungetwch
-_unlink
-_unloaddll
-_unlock
-_utime
-_utime32
-_utime64
-_vcprintf
-_vcprintf_l
-_vcprintf_p
-_vcprintf_p_l
-_vcprintf_s
-_vcprintf_s_l
-_vcwprintf
-_vcwprintf_l
-_vcwprintf_p
-_vcwprintf_p_l
-_vcwprintf_s
-_vcwprintf_s_l
-_vfprintf_l
-_vfprintf_p
-_vfprintf_p_l
-_vfprintf_s_l
-_vfwprintf_l
-_vfwprintf_p
-_vfwprintf_p_l
-_vfwprintf_s_l
-_vprintf_l
-_vprintf_p
-_vprintf_p_l
-_vprintf_s_l
-_vscprintf
-_vscprintf_l
-_vscprintf_p_l
-_vscwprintf
-_vscwprintf_l
-_vscwprintf_p_l
-_vsnprintf
-_vsnprintf_c
-_vsnprintf_c_l
-_vsnprintf_l
-_vsnprintf_s
-_vsnprintf_s_l
-_vsnwprintf
-_vsnwprintf_l
-_vsnwprintf_s
-_vsnwprintf_s_l
-_vsprintf_l
-_vsprintf_p
-_vsprintf_p_l
-_vsprintf_s_l
-_vswprintf
-_vswprintf_c
-_vswprintf_c_l
-_vswprintf_l
-_vswprintf_p_l
-_vswprintf_s_l
-_vwprintf_l
-_vwprintf_p
-_vwprintf_p_l
-_vwprintf_s_l
-_waccess
-_waccess_s
-_wasctime
-_wasctime_s
-_wassert
-_wchdir
-_wchmod
-_wcmdln
-_wcreat
-_wcscoll_l
-_wcsdup
-_wcsdup_dbg
-_wcserror
-_wcserror_s
-_wcsftime_l
-_wcsicmp
-_wcsicmp_l
-_wcsicoll
-_wcsicoll_l
-_wcslwr
-_wcslwr_l
-_wcslwr_s
-_wcslwr_s_l
-_wcsncoll
-_wcsncoll_l
-_wcsnicmp
-_wcsnicmp_l
-_wcsnicoll
-_wcsnicoll_l
-_wcsnset
-_wcsnset_s
-_wcsrev
-_wcsset
-_wcsset_s
-_wcstoi64
-_wcstoi64_l
-_wcstol_l
-_wcstombs_l
-_wcstombs_s_l
-_wcstoui64
-_wcstoui64_l
-_wcstoul_l
-_wcsupr
-_wcsupr_l
-_wcsupr_s
-_wcsupr_s_l
-_wcsxfrm_l
-_wctime
-_wctime32
-_wctime32_s
-_wctime64
-_wctime64_s
-_wctomb_l
-_wctomb_s_l
-_wctype
-_wenviron
-_wexecl
-_wexecle
-_wexeclp
-_wexeclpe
-_wexecv
-_wexecve
-_wexecvp
-_wexecvpe
-_wfdopen
-_wfindfirst
-_wfindfirst64
-_wfindfirsti64
-_wfindnext
-_wfindnext64
-_wfindnexti64
-_wfopen
-_wfopen_s
-_wfreopen
-_wfreopen_s
-_wfsopen
-_wfullpath
-_wfullpath_dbg
-_wgetcwd
-_wgetdcwd
-_wgetenv
-_wgetenv_s
-_winmajor
-_winminor
-_winput_s
-_winver
-_wmakepath
-_wmakepath_s
-_wmkdir
-_wmktemp
-_wmktemp_s
-_wopen
-_woutput_s
-_wperror
-_wpgmptr
-_wpopen
-_wprintf_l
-_wprintf_p
-_wprintf_p_l
-_wprintf_s_l
-_wputenv
-_wputenv_s
-_wremove
-_wrename
-_write
-_wrmdir
-_wscanf_l
-_wscanf_s_l
-_wsearchenv
-_wsearchenv_s
-_wsetlocale
-_wsopen
-_wsopen_s
-_wspawnl
-_wspawnle
-_wspawnlp
-_wspawnlpe
-_wspawnv
-_wspawnve
-_wspawnvp
-_wspawnvpe
-_wsplitpath
-_wsplitpath_s
-_wstat
-_wstat64
-_wstati64
-_wstrdate
-_wstrdate_s
-_wstrtime
-_wstrtime_s
-_wsystem
-_wtempnam
-_wtempnam_dbg
-_wtmpnam
-_wtmpnam_s
-_wtof
-_wtof_l
-_wtoi
-_wtoi64
-_wtoi64_l
-_wtoi_l
-_wtol
-_wtol_l
-_wunlink
-_wutime
-_wutime32
-_wutime64
-_y0
-_y1
-_yn
-abort
-abs
-acos
-asctime
-asctime_s
-asin
-atan
-atan2
-atexit
-atof
-atoi
-atol
-bsearch
-bsearch_s
-btowc
-calloc
-ceil
-clearerr
-clearerr_s
-clock
-cos
-cosh
-ctime
-difftime
-div
-exit
-exp
-fabs
-fclose
-feof
-ferror
-fflush
-fgetc
-fgetpos
-fgets
-fgetwc
-fgetws
-floor
-fmod
-fopen
-fopen_s
-fprintf
-fprintf_s
-fputc
-fputs
-fputwc
-fputws
-fread
-free
-freopen
-freopen_s
-frexp
-fscanf
-fscanf_s
-fseek
-fsetpos
-ftell
-fwprintf
-fwprintf_s
-fwrite
-fwscanf
-fwscanf_s
-getc
-getchar
-getenv
-getenv_s
-gets
-getwc
-getwchar
-gmtime
-is_wctype
-isalnum
-isalpha
-iscntrl
-isdigit
-isgraph
-isleadbyte
-islower
-isprint
-ispunct
-isspace
-isupper
-iswalnum
-iswalpha
-iswascii
-iswcntrl
-iswctype
-iswdigit
-iswgraph
-iswlower
-iswprint
-iswpunct
-iswspace
-iswupper
-iswxdigit
-isxdigit
-labs
-ldexp
-ldiv
-localeconv
-localtime
-log
-log10
-longjmp
-malloc
-mblen
-mbrlen
-mbrtowc
-mbsdup_dbg
-mbsrtowcs
-mbsrtowcs_s
-mbstowcs
-mbstowcs_s
-mbtowc
-memchr
-memcmp
-memcpy
-memcpy_s
-memmove
-memmove_s
-memset
-mktime
-modf
-perror
-pow
-printf
-printf_s
-putc
-putchar
-puts
-putwc
-putwchar
-qsort
-qsort_s
-raise
-rand
-rand_s
-realloc
-remove
-rename
-rewind
-scanf
-scanf_s
-setbuf
-setlocale
-setvbuf
-signal
-sin
-sinh
-sprintf
-sprintf_s
-sqrt
-srand
-sscanf
-sscanf_s
-strcat
-strcat_s
-strchr
-strcmp
-strcoll
-strcpy
-strcpy_s
-strcspn
-strerror
-strerror_s
-strftime
-strlen
-strncat
-strncat_s
-strncmp
-strncpy
-strncpy_s
-strnlen
-strpbrk
-strrchr
-strspn
-strstr
-strtod
-strtok
-strtok_s
-strtol
-strtoul
-strxfrm
-swprintf
-swprintf_s
-swscanf
-swscanf_s
-system
-tan
-tanh
-time
-tmpfile
-tmpfile_s
-tmpnam
-tmpnam_s
-tolower
-toupper
-towlower
-towupper
-ungetc
-ungetwc
-utime
-vfprintf
-vfprintf_s
-vfwprintf
-vfwprintf_s
-vprintf
-vprintf_s
-vsnprintf
-vsprintf
-vsprintf_s
-vswprintf
-vswprintf_s
-vwprintf
-vwprintf_s
-wcrtomb
-wcrtomb_s
-wcscat
-wcscat_s
-wcschr
-wcscmp
-wcscoll
-wcscpy
-wcscpy_s
-wcscspn
-wcsftime
-wcslen
-wcsncat
-wcsncat_s
-wcsncmp
-wcsncpy
-wcsncpy_s
-wcsnlen
-wcspbrk
-wcsrchr
-wcsrtombs
-wcsrtombs_s
-wcsspn
-wcsstr
-wcstod
-wcstok
-wcstok_s
-wcstol
-wcstombs
-wcstombs_s
-wcstoul
-wcsxfrm
-wctob
-wctomb
-wctomb_s
-wprintf
-wprintf_s
-wscanf
-wscanf_s
diff --git a/tinyc/win32/lib/user32.def b/tinyc/win32/lib/user32.def
deleted file mode 100644
index a034dac20..000000000
--- a/tinyc/win32/lib/user32.def
+++ /dev/null
@@ -1,658 +0,0 @@
-LIBRARY user32.dll
-
-EXPORTS
-ActivateKeyboardLayout
-AdjustWindowRect
-AdjustWindowRectEx
-AlignRects
-AllowSetForegroundWindow
-AnimateWindow
-AnyPopup
-AppendMenuA
-AppendMenuW
-ArrangeIconicWindows
-AttachThreadInput
-BeginDeferWindowPos
-BeginPaint
-BlockInput
-BringWindowToTop
-BroadcastSystemMessage
-BroadcastSystemMessageA
-BroadcastSystemMessageW
-CalcChildScroll
-CallMsgFilter
-CallMsgFilterA
-CallMsgFilterW
-CallNextHookEx
-CallWindowProcA
-CallWindowProcW
-CascadeChildWindows
-CascadeWindows
-ChangeClipboardChain
-ChangeDisplaySettingsA
-ChangeDisplaySettingsExA
-ChangeDisplaySettingsExW
-ChangeDisplaySettingsW
-ChangeMenuA
-ChangeMenuW
-CharLowerA
-CharLowerBuffA
-CharLowerBuffW
-CharLowerW
-CharNextA
-CharNextExA
-CharNextExW
-CharNextW
-CharPrevA
-CharPrevExA
-CharPrevExW
-CharPrevW
-CharToOemA
-CharToOemBuffA
-CharToOemBuffW
-CharToOemW
-CharUpperA
-CharUpperBuffA
-CharUpperBuffW
-CharUpperW
-CheckDlgButton
-CheckMenuItem
-CheckMenuRadioItem
-CheckRadioButton
-ChildWindowFromPoint
-ChildWindowFromPointEx
-ClientThreadConnect
-ClientToScreen
-ClipCursor
-CloseClipboard
-CloseDesktop
-CloseWindow
-CloseWindowStation
-CopyAcceleratorTableA
-CopyAcceleratorTableW
-CopyIcon
-CopyImage
-CopyRect
-CountClipboardFormats
-CreateAcceleratorTableA
-CreateAcceleratorTableW
-CreateCaret
-CreateCursor
-CreateDesktopA
-CreateDesktopW
-CreateDialogIndirectParamA
-CreateDialogIndirectParamW
-CreateDialogParamA
-CreateDialogParamW
-CreateIcon
-CreateIconFromResource
-CreateIconFromResourceEx
-CreateIconIndirect
-CreateMDIWindowA
-CreateMDIWindowW
-CreateMenu
-CreatePopupMenu
-CreateWindowExA
-CreateWindowExW
-CreateWindowStationA
-CreateWindowStationW
-DdeAbandonTransaction
-DdeAccessData
-DdeAddData
-DdeClientTransaction
-DdeCmpStringHandles
-DdeConnect
-DdeConnectList
-DdeCreateDataHandle
-DdeCreateStringHandleA
-DdeCreateStringHandleW
-DdeDisconnect
-DdeDisconnectList
-DdeEnableCallback
-DdeFreeDataHandle
-DdeFreeStringHandle
-DdeGetData
-DdeGetLastError
-DdeImpersonateClient
-DdeInitializeA
-DdeInitializeW
-DdeKeepStringHandle
-DdeNameService
-DdePostAdvise
-DdeQueryConvInfo
-DdeQueryNextServer
-DdeQueryStringA
-DdeQueryStringW
-DdeReconnect
-DdeSetQualityOfService
-DdeSetUserHandle
-DdeUnaccessData
-DdeUninitialize
-DefDlgProcA
-DefDlgProcW
-DefFrameProcA
-DefFrameProcW
-DefMDIChildProcA
-DefMDIChildProcW
-DefWindowProcA
-DefWindowProcW
-DeferWindowPos
-DeleteMenu
-DestroyAcceleratorTable
-DestroyCaret
-DestroyCursor
-DestroyIcon
-DestroyMenu
-DestroyWindow
-DialogBoxIndirectParamA
-DialogBoxIndirectParamW
-DialogBoxParamA
-DialogBoxParamW
-DispatchMessageA
-DispatchMessageW
-DlgDirListA
-DlgDirListComboBoxA
-DlgDirListComboBoxW
-DlgDirListW
-DlgDirSelectComboBoxExA
-DlgDirSelectComboBoxExW
-DlgDirSelectExA
-DlgDirSelectExW
-DragDetect
-DragObject
-DrawAnimatedRects
-DrawCaption
-DrawCaptionTempA
-DrawCaptionTempW
-DrawEdge
-DrawFocusRect
-DrawFrame
-DrawFrameControl
-DrawIcon
-DrawIconEx
-DrawMenuBar
-DrawMenuBarTemp
-DrawStateA
-DrawStateW
-DrawTextA
-DrawTextExA
-DrawTextExW
-DrawTextW
-EditWndProc
-EmptyClipboard
-EnableMenuItem
-EnableScrollBar
-EnableWindow
-EndDeferWindowPos
-EndDialog
-EndMenu
-EndPaint
-EndTask
-EnumChildWindows
-EnumClipboardFormats
-EnumDesktopWindows
-EnumDesktopsA
-EnumDesktopsW
-EnumDisplayDevicesA
-EnumDisplayDevicesW
-EnumDisplayMonitors
-EnumDisplaySettingsA
-EnumDisplaySettingsExA
-EnumDisplaySettingsExW
-EnumDisplaySettingsW
-EnumPropsA
-EnumPropsExA
-EnumPropsExW
-EnumPropsW
-EnumThreadWindows
-EnumWindowStationsA
-EnumWindowStationsW
-EnumWindows
-EqualRect
-ExcludeUpdateRgn
-ExitWindowsEx
-FillRect
-FindWindowA
-FindWindowExA
-FindWindowExW
-FindWindowW
-FlashWindow
-FlashWindowEx
-FrameRect
-FreeDDElParam
-GetActiveWindow
-GetAltTabInfo
-GetAncestor
-GetAsyncKeyState
-GetCapture
-GetCaretBlinkTime
-GetCaretPos
-GetClassInfoA
-GetClassInfoExA
-GetClassInfoExW
-GetClassInfoW
-GetClassLongA
-GetClassLongW
-GetClassNameA
-GetClassNameW
-GetClassWord
-GetClientRect
-GetClipCursor
-GetClipboardData
-GetClipboardFormatNameA
-GetClipboardFormatNameW
-GetClipboardOwner
-GetClipboardSequenceNumber
-GetClipboardViewer
-GetComboBoxInfo
-GetCursor
-GetCursorInfo
-GetCursorPos
-GetDC
-GetDCEx
-GetDesktopWindow
-GetDialogBaseUnits
-GetDlgCtrlID
-GetDlgItem
-GetDlgItemInt
-GetDlgItemTextA
-GetDlgItemTextW
-GetDoubleClickTime
-GetFocus
-GetForegroundWindow
-GetGUIThreadInfo
-GetGuiResources
-GetIconInfo
-GetInputDesktop
-GetInputState
-GetInternalWindowPos
-GetKBCodePage
-GetKeyNameTextA
-GetKeyNameTextW
-GetKeyState
-GetKeyboardLayout
-GetKeyboardLayoutList
-GetKeyboardLayoutNameA
-GetKeyboardLayoutNameW
-GetKeyboardState
-GetKeyboardType
-GetLastActivePopup
-GetListBoxInfo
-GetMenu
-GetMenuBarInfo
-GetMenuCheckMarkDimensions
-GetMenuContextHelpId
-GetMenuDefaultItem
-GetMenuInfo
-GetMenuItemCount
-GetMenuItemID
-GetMenuItemInfoA
-GetMenuItemInfoW
-GetMenuItemRect
-GetMenuState
-GetMenuStringA
-GetMenuStringW
-GetMessageA
-GetMessageExtraInfo
-GetMessagePos
-GetMessageTime
-GetMessageW
-GetMonitorInfoA
-GetMonitorInfoW
-GetMouseMovePoints
-GetMouseMovePointsEx
-GetNextDlgGroupItem
-GetNextDlgTabItem
-GetNextQueueWindow
-GetOpenClipboardWindow
-GetParent
-GetPriorityClipboardFormat
-GetProcessDefaultLayout
-GetProcessWindowStation
-GetPropA
-GetPropW
-GetQueueStatus
-GetScrollBarInfo
-GetScrollInfo
-GetScrollPos
-GetScrollRange
-GetShellWindow
-GetSubMenu
-GetSysColor
-GetSysColorBrush
-GetSystemMenu
-GetSystemMetrics
-GetTabbedTextExtentA
-GetTabbedTextExtentW
-GetThreadDesktop
-GetTitleBarInfo
-GetTopWindow
-GetUpdateRect
-GetUpdateRgn
-GetUserObjectInformationA
-GetUserObjectInformationW
-GetUserObjectSecurity
-GetWindow
-GetWindowContextHelpId
-GetWindowDC
-GetWindowInfo
-GetWindowLongPtrA
-GetWindowLongPtrW
-SetWindowLongPtrA
-SetWindowLongPtrW
-GetWindowLongA
-GetWindowLongW
-GetWindowModuleFileNameA
-GetWindowModuleFileNameW
-GetWindowPlacement
-GetWindowRect
-GetWindowRgn
-GetWindowTextA
-GetWindowTextLengthA
-GetWindowTextLengthW
-GetWindowTextW
-GetWindowThreadProcessId
-GetWindowWord
-GrayStringA
-GrayStringW
-HasSystemSleepStarted
-HideCaret
-HiliteMenuItem
-IMPGetIMEA
-IMPGetIMEW
-IMPQueryIMEA
-IMPQueryIMEW
-IMPSetIMEA
-IMPSetIMEW
-ImpersonateDdeClientWindow
-InSendMessage
-InSendMessageEx
-InflateRect
-InitSharedTable
-InitTask
-InsertMenuA
-InsertMenuItemA
-InsertMenuItemW
-InsertMenuW
-InternalGetWindowText
-IntersectRect
-InvalidateRect
-InvalidateRgn
-InvertRect
-IsCharAlphaA
-IsCharAlphaNumericA
-IsCharAlphaNumericW
-IsCharAlphaW
-IsCharLowerA
-IsCharLowerW
-IsCharUpperA
-IsCharUpperW
-IsChild
-IsClipboardFormatAvailable
-IsDialogMessage
-IsDialogMessageA
-IsDialogMessageW
-IsDlgButtonChecked
-IsHungThread
-IsIconic
-IsMenu
-IsRectEmpty
-IsWindow
-IsWindowEnabled
-IsWindowUnicode
-IsWindowVisible
-IsZoomed
-KillTimer
-LoadAcceleratorsA
-LoadAcceleratorsW
-LoadBitmapA
-LoadBitmapW
-LoadCursorA
-LoadCursorFromFileA
-LoadCursorFromFileW
-LoadCursorW
-LoadIconA
-LoadIconW
-LoadImageA
-LoadImageW
-LoadKeyboardLayoutA
-LoadKeyboardLayoutW
-LoadMenuA
-LoadMenuIndirectA
-LoadMenuIndirectW
-LoadMenuW
-LoadStringA
-LoadStringW
-LockSetForegroundWindow
-LockWindowStation
-LockWindowUpdate
-LookupIconIdFromDirectory
-LookupIconIdFromDirectoryEx
-MapDialogRect
-MapVirtualKeyA
-MapVirtualKeyExA
-MapVirtualKeyExW
-MapVirtualKeyW
-MapWindowPoints
-MenuItemFromPoint
-MessageBeep
-MessageBoxA
-MessageBoxExA
-MessageBoxExW
-MessageBoxIndirectA
-MessageBoxIndirectW
-MessageBoxW
-ModifyAccess
-ModifyMenuA
-ModifyMenuW
-MonitorFromPoint
-MonitorFromRect
-MonitorFromWindow
-MoveWindow
-MsgWaitForMultipleObjects
-MsgWaitForMultipleObjectsEx
-NotifyWinEvent
-OemKeyScan
-OemToCharA
-OemToCharBuffA
-OemToCharBuffW
-OemToCharW
-OffsetRect
-OpenClipboard
-OpenDesktopA
-OpenDesktopW
-OpenIcon
-OpenInputDesktop
-OpenWindowStationA
-OpenWindowStationW
-PackDDElParam
-PaintDesktop
-PeekMessageA
-PeekMessageW
-PlaySoundEvent
-PostMessageA
-PostMessageW
-PostQuitMessage
-PostThreadMessageA
-PostThreadMessageW
-PtInRect
-RealChildWindowFromPoint
-RealGetWindowClass
-RedrawWindow
-RegisterClassA
-RegisterClassExA
-RegisterClassExW
-RegisterClassW
-RegisterClipboardFormatA
-RegisterClipboardFormatW
-RegisterDeviceNotificationA
-RegisterDeviceNotificationW
-RegisterHotKey
-RegisterLogonProcess
-RegisterNetworkCapabilities
-RegisterSystemThread
-RegisterTasklist
-RegisterWindowMessageA
-RegisterWindowMessageW
-ReleaseCapture
-ReleaseDC
-RemoveMenu
-RemovePropA
-RemovePropW
-ReplyMessage
-ReuseDDElParam
-ScreenToClient
-ScrollDC
-ScrollWindow
-ScrollWindowEx
-SendDlgItemMessageA
-SendDlgItemMessageW
-SendIMEMessageExA
-SendIMEMessageExW
-SendInput
-SendMessageA
-SendMessageCallbackA
-SendMessageCallbackW
-SendMessageTimeoutA
-SendMessageTimeoutW
-SendMessageW
-SendNotifyMessageA
-SendNotifyMessageW
-SetActiveWindow
-SetCapture
-SetCaretBlinkTime
-SetCaretPos
-SetClassLongA
-SetClassLongW
-SetClassWord
-SetClipboardData
-SetClipboardViewer
-SetCursor
-SetCursorPos
-SetDebugErrorLevel
-SetDeskWallpaper
-SetDesktopBitmap
-SetDlgItemInt
-SetDlgItemTextA
-SetDlgItemTextW
-SetDoubleClickTime
-SetFocus
-SetForegroundWindow
-SetInternalWindowPos
-SetKeyboardState
-SetLastErrorEx
-SetLogonNotifyWindow
-SetMenu
-SetMenuContextHelpId
-SetMenuDefaultItem
-SetMenuInfo
-SetMenuItemBitmaps
-SetMenuItemInfoA
-SetMenuItemInfoW
-SetMessageExtraInfo
-SetMessageQueue
-SetParent
-SetProcessDefaultLayout
-SetProcessWindowStation
-SetPropA
-SetPropW
-SetRect
-SetRectEmpty
-SetScrollInfo
-SetScrollPos
-SetScrollRange
-SetShellWindow
-SetSysColors
-SetSysColorsTemp
-SetSystemCursor
-SetThreadDesktop
-SetTimer
-SetUserObjectInformationA
-SetUserObjectInformationW
-SetUserObjectSecurity
-SetWinEventHook
-SetWindowContextHelpId
-SetWindowFullScreenState
-SetWindowLongA
-SetWindowLongW
-SetWindowPlacement
-SetWindowPos
-SetWindowRgn
-SetWindowTextA
-SetWindowTextW
-SetWindowWord
-SetWindowsHookA
-SetWindowsHookExA
-SetWindowsHookExW
-SetWindowsHookW
-ShowCaret
-ShowCursor
-ShowOwnedPopups
-ShowScrollBar
-ShowWindow
-ShowWindowAsync
-SubtractRect
-SwapMouseButton
-SwitchDesktop
-SwitchToThisWindow
-SysErrorBox
-SystemParametersInfoA
-SystemParametersInfoW
-TabbedTextOutA
-TabbedTextOutW
-TileChildWindows
-TileWindows
-ToAscii
-ToAsciiEx
-ToUnicode
-ToUnicodeEx
-TrackMouseEvent
-TrackPopupMenu
-TrackPopupMenuEx
-TranslateAccelerator
-TranslateAcceleratorA
-TranslateAcceleratorW
-TranslateMDISysAccel
-TranslateMessage
-UnhookWinEvent
-UnhookWindowsHook
-UnhookWindowsHookEx
-UnionRect
-UnloadKeyboardLayout
-UnlockWindowStation
-UnpackDDElParam
-UnregisterClassA
-UnregisterClassW
-UnregisterDeviceNotification
-UnregisterHotKey
-UpdateWindow
-UserClientDllInitialize
-UserIsSystemResumeAutomatic
-UserSetDeviceHoldState
-UserSignalProc
-UserTickleTimer
-ValidateRect
-ValidateRgn
-VkKeyScanA
-VkKeyScanExA
-VkKeyScanExW
-VkKeyScanW
-WINNLSEnableIME
-WINNLSGetEnableStatus
-WINNLSGetIMEHotkey
-WNDPROC_CALLBACK
-WaitForInputIdle
-WaitMessage
-WinHelpA
-WinHelpW
-WinOldAppHackoMatic
-WindowFromDC
-WindowFromPoint
-YieldTask
-_SetProcessDefaultLayout
-keybd_event
-mouse_event
-wsprintfA
-wsprintfW
-wvsprintfA
-wvsprintfW
diff --git a/tinyc/win32/lib/wincrt1.c b/tinyc/win32/lib/wincrt1.c
deleted file mode 100644
index ce3a63f13..000000000
--- a/tinyc/win32/lib/wincrt1.c
+++ /dev/null
@@ -1,75 +0,0 @@
-//+---------------------------------------------------------------------------
-
-// _UNICODE for tchar.h, UNICODE for API
-#include <tchar.h>
-
-#include <windows.h>
-#include <stdlib.h>
-
-#define __UNKNOWN_APP    0
-#define __CONSOLE_APP    1
-#define __GUI_APP        2
-void __set_app_type(int);
-void _controlfp(unsigned a, unsigned b);
-
-#ifdef _UNICODE
-#define __tgetmainargs __wgetmainargs
-#define _twinstart _wwinstart
-#define _runtwinmain _runwwinmain
-int APIENTRY wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int);
-#else
-#define __tgetmainargs __getmainargs
-#define _twinstart _winstart
-#define _runtwinmain _runwinmain
-#endif
-
-typedef struct { int newmode; } _startupinfo;
-int __cdecl __tgetmainargs(int *pargc, _TCHAR ***pargv, _TCHAR ***penv, int globb, _startupinfo*);
-
-static int go_winmain(TCHAR *arg1)
-{
-    STARTUPINFO si;
-    _TCHAR *szCmd, *p;
-    int fShow;
-
-    GetStartupInfo(&si);
-    if (si.dwFlags & STARTF_USESHOWWINDOW)
-        fShow = si.wShowWindow;
-    else
-        fShow = SW_SHOWDEFAULT;
-
-    szCmd = NULL, p = GetCommandLine();
-    if (arg1)
-        szCmd = _tcsstr(p, arg1);
-    if (NULL == szCmd)
-        szCmd = _tcsdup(__T(""));
-    else if (szCmd > p && szCmd[-1] == __T('"'))
-        --szCmd;
-#if defined __i386__ || defined __x86_64__
-    _controlfp(0x10000, 0x30000);
-#endif
-    return _tWinMain(GetModuleHandle(NULL), NULL, szCmd, fShow);
-}
-
-int _twinstart(void)
-{
-    __TRY__
-    _startupinfo start_info_con = {0};
-    __set_app_type(__GUI_APP);
-    __tgetmainargs(&__argc, &__targv, &_tenviron, 0, &start_info_con);
-    exit(go_winmain(__argc > 1 ? __targv[1] : NULL));
-}
-
-int _runtwinmain(int argc, /* as tcc passed in */ char **argv)
-{
-#ifdef UNICODE
-    _startupinfo start_info = {0};
-    __tgetmainargs(&__argc, &__targv, &_tenviron, 0, &start_info);
-    /* may be wrong when tcc has received wildcards (*.c) */
-    if (argc < __argc)
-        __targv += __argc - argc, __argc = argc;
-#else
-    __argc = argc, __targv = argv;
-#endif
-    return go_winmain(__argc > 1 ? __targv[1] : NULL);
-}
diff --git a/tinyc/win32/lib/wincrt1w.c b/tinyc/win32/lib/wincrt1w.c
deleted file mode 100644
index a7d349e10..000000000
--- a/tinyc/win32/lib/wincrt1w.c
+++ /dev/null
@@ -1,3 +0,0 @@
-#define _UNICODE 1
-#define UNICODE 1
-#include "wincrt1.c"
diff --git a/tinyc/win32/tcc-win32.txt b/tinyc/win32/tcc-win32.txt
deleted file mode 100644
index 751a8e074..000000000
--- a/tinyc/win32/tcc-win32.txt
+++ /dev/null
@@ -1,168 +0,0 @@
-
-    TinyCC
-    ======
-
-    This file contains specific information for usage of TinyCC
-    under MS-Windows.  See tcc-doc.html to have all the features.
-
-
-    Installation from the binary ZIP package:
-    -----------------------------------------
-    Unzip the package to a directory of your choice.
-    
-
-    Set the system PATH:
-    --------------------
-    To be able to invoke the compiler from everywhere on your computer by
-    just typing "tcc", please add the directory containing tcc.exe to your
-    system PATH.
-
-
-    Include and library search paths
-    --------------------------------
-    On windows,  the standard "include" and "lib" directories are searched
-    relatively from the location of the executables (tcc.exe, libtcc.dll).
-
-
-    Examples:
-    ---------
-    Open a console window (DOS box) and 'cd' to the examples directory.
-
-    For the 'Fibonacci' example type:
-
-	tcc fib.c
-
-    For the 'Hello Windows' GUI example type:
-
-	tcc hello_win.c
-
-    For the 'Hello DLL' example type
-
-	tcc -shared dll.c
-	tcc -impdef dll.dll (optional)
-        tcc hello_dll.c dll.def
-
-
-    Using libtcc as JIT compiler in your program
-    --------------------------------------------
-    Check out the 'libtcc_test' example:
-
-    - Running it from source:
-        tcc -I libtcc libtcc/libtcc.def -run examples/libtcc_test.c
-
-    - Compiling with TCC:
-        tcc examples/libtcc_test.c -I libtcc libtcc/libtcc.def
-
-    - Compiling with MinGW:
-        gcc examples/libtcc_test.c -I libtcc libtcc.dll -o libtcc_test.exe
-
-    - Compiling with MSVC:
-        lib /def:libtcc\libtcc.def /out:libtcc.lib
-        cl /MD examples/libtcc_test.c -I libtcc libtcc.lib
-
-
-    Import Definition Files:
-    ------------------------
-    To link with Windows system DLLs, TCC uses import definition
-    files (.def) instead of libraries.
-
-    The now built-in 'tiny_impdef' program may be used to make
-    additional .def files for any DLL. For example
-
-        tcc -impdef [-v] opengl32.dll [-o opengl32.def]
-
-    Put opengl32.def into the tcc/lib directory.  Specify -lopengl32 at
-    the TCC commandline to link a program that uses opengl32.dll.
-
-
-    Header Files:
-    -------------
-    The system header files (except _mingw.h) are from the MinGW
-    distribution:
-
-	http://www.mingw.org/
-
-    From the windows headers, only a minimal set is included.  If you need
-    more,  get MinGW's "w32api" package.  Extract the files from "include"
-    into your "tcc/include/winapi" directory.
-
-
-    Resource Files:
-    ---------------
-    TCC can link windows resources in coff format as generated by MinGW's
-    windres.exe.  For example:
-
-        windres -O coff app.rc -o appres.o
-        tcc app.c appres.o -o app.exe
-
-
-    Tiny Libmaker:
-    --------------
-    The now built-in tiny_libmaker tool by Timovj Lahde can be used as
-    'ar' replacement to make a library from several object files:
-
-	tcc -ar [rcsv] library objectfiles ...
-
-
-    Compilation from source:
-    ------------------------
-    * You can use the MinGW and MSYS tools available at
-        http://www.mingw.org
-        http://www.mingw-w64.org
-        http://www.msys2.org
-
-      Untar the TCC archive and type in the MSYS shell:
-        ./configure [--prefix installpath]
-        make
-        make install
-
-      The default install location is c:\Program Files\tcc
-
-      Cygwin can be used too with its mingw cross-compiler installed:
-        ./configure --cross-prefix=i686-w64-mingw32-
-        (the prefix may vary)
-
-    * Alternatively you can compile TCC with just GCC from MinGW using
-        > build-tcc.bat (from the win32 directory)
-
-      Also MSVC can be used with the "VSTools Developer Command Prompt":
-        > build-tcc.bat -c cl
-
-      or with an existing tcc (needs to be in a different directory)
-        > build-tcc.bat -c some-tcc-dir\tcc.exe
-
-      Also you can copy/install everything into another directory:
-        > build-tcc.bat -i <dir>
-
-    Limitations:
-    ------------
-    - On the object file level, currently TCC supports only the ELF format,
-      not COFF as used by MinGW and MSVC.  It is not possible to exchange
-      object files or libraries between TCC and these compilers.
-
-      However libraries for TCC from objects by TCC can be made using
-        tcc -ar lib.a files.o ,,,
-
-    - No leading underscore is generated in the ELF symbols.
-
-    Documentation and License:
-    --------------------------
-    TCC is distributed under the GNU Lesser General Public License. (See
-    COPYING file or http://www.gnu.org/licenses/lgpl-2.1.html)
-
-    TinyCC homepage is at:
-
-	http://fabrice.bellard.free.fr/tcc/
-
-
-    WinAPI Help and 3rd-party tools:
-    --------------------------------
-    The Windows API documentation (Win95) in a single .hlp file is
-    available on the lcc-win32 site as "win32hlp.exe" or from other
-    locations as "win32hlp_big.zip".
-
-    A nice RAD tool to create windows resources (dialog boxes etc.) is
-    "ResEd", available at the RadASM website.
-
-
-    --- grischka
diff --git a/tinyc/x86_64-asm.h b/tinyc/x86_64-asm.h
deleted file mode 100644
index cb9eb16c8..000000000
--- a/tinyc/x86_64-asm.h
+++ /dev/null
@@ -1,525 +0,0 @@
-     DEF_ASM_OP0(clc, 0xf8) /* must be first OP0 */
-     DEF_ASM_OP0(cld, 0xfc)
-     DEF_ASM_OP0(cli, 0xfa)
-     DEF_ASM_OP0(clts, 0x0f06)
-     DEF_ASM_OP0(cmc, 0xf5)
-     DEF_ASM_OP0(lahf, 0x9f)
-     DEF_ASM_OP0(sahf, 0x9e)
-     DEF_ASM_OP0(pushfq, 0x9c)
-     DEF_ASM_OP0(popfq, 0x9d)
-     DEF_ASM_OP0(pushf, 0x9c)
-     DEF_ASM_OP0(popf, 0x9d)
-     DEF_ASM_OP0(stc, 0xf9)
-     DEF_ASM_OP0(std, 0xfd)
-     DEF_ASM_OP0(sti, 0xfb)
-     DEF_ASM_OP0(aaa, 0x37)
-     DEF_ASM_OP0(aas, 0x3f)
-     DEF_ASM_OP0(daa, 0x27)
-     DEF_ASM_OP0(das, 0x2f)
-     DEF_ASM_OP0(aad, 0xd50a)
-     DEF_ASM_OP0(aam, 0xd40a)
-     DEF_ASM_OP0(cbw, 0x6698)
-     DEF_ASM_OP0(cwd, 0x6699)
-     DEF_ASM_OP0(cwde, 0x98)
-     DEF_ASM_OP0(cdq, 0x99)
-     DEF_ASM_OP0(cbtw, 0x6698)
-     DEF_ASM_OP0(cwtl, 0x98)
-     DEF_ASM_OP0(cwtd, 0x6699)
-     DEF_ASM_OP0(cltd, 0x99)
-     DEF_ASM_OP0(cqto, 0x4899)
-     DEF_ASM_OP0(int3, 0xcc)
-     DEF_ASM_OP0(into, 0xce)
-     DEF_ASM_OP0(iret, 0xcf)
-     DEF_ASM_OP0(rsm, 0x0faa)
-     DEF_ASM_OP0(hlt, 0xf4)
-     DEF_ASM_OP0(wait, 0x9b)
-     DEF_ASM_OP0(nop, 0x90)
-     DEF_ASM_OP0(pause, 0xf390)
-     DEF_ASM_OP0(xlat, 0xd7)
-
-     /* strings */
-ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWLX))
-ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWLX))
-
-ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWL))
-ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWL))
-
-ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWLX))
-ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWLX))
-
-ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWLX))
-ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWLX))
-
-ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWLX))
-ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWLX))
-
-ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWLX))
-ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWLX))
-
-     /* bits */
-
-ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WLX, OPT_REGW | OPT_EA, OPT_REGW))
-ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WLX, OPT_REGW | OPT_EA, OPT_REGW))
-
-ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WLX, OPT_REGW, OPT_REGW | OPT_EA))
-ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WLX, OPT_IM8, OPT_REGW | OPT_EA))
-
-ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WLX, OPT_REGW, OPT_REGW | OPT_EA))
-ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WLX, OPT_IM8, OPT_REGW | OPT_EA))
-
-ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WLX, OPT_REGW, OPT_REGW | OPT_EA))
-ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WLX, OPT_IM8, OPT_REGW | OPT_EA))
-
-ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WLX, OPT_REGW, OPT_REGW | OPT_EA))
-ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WLX, OPT_IM8, OPT_REGW | OPT_EA))
-
-     /* prefixes */
-     DEF_ASM_OP0(lock, 0xf0)
-     DEF_ASM_OP0(rep, 0xf3)
-     DEF_ASM_OP0(repe, 0xf3)
-     DEF_ASM_OP0(repz, 0xf3)
-     DEF_ASM_OP0(repne, 0xf2)
-     DEF_ASM_OP0(repnz, 0xf2)
-
-     DEF_ASM_OP0(invd, 0x0f08)
-     DEF_ASM_OP0(wbinvd, 0x0f09)
-     DEF_ASM_OP0(cpuid, 0x0fa2)
-     DEF_ASM_OP0(wrmsr, 0x0f30)
-     DEF_ASM_OP0(rdtsc, 0x0f31)
-     DEF_ASM_OP0(rdmsr, 0x0f32)
-     DEF_ASM_OP0(rdpmc, 0x0f33)
-
-     DEF_ASM_OP0(syscall, 0x0f05)
-     DEF_ASM_OP0(sysret, 0x0f07)
-     DEF_ASM_OP0L(sysretq, 0x480f07, 0, 0)
-     DEF_ASM_OP0(ud2, 0x0f0b)
-
-     /* NOTE: we took the same order as gas opcode definition order */
-/* Right now we can't express the fact that 0xa1/0xa3 can't use $eax and a 
-   32 bit moffset as operands.
-ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWLX, OPT_ADDR, OPT_EAX))
-ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWLX, OPT_EAX, OPT_ADDR)) */
-ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWLX, OPT_REG, OPT_EA | OPT_REG))
-ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWLX, OPT_EA | OPT_REG, OPT_REG))
-/* The moves are special: the 0xb8 form supports IM64 (the only insn that
-   does) with REG64.  It doesn't support IM32 with REG64, it would use
-   the full movabs form (64bit immediate).  For IM32->REG64 we prefer
-   the 0xc7 opcode.  So disallow all 64bit forms and code the rest by hand. */
-ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWLX, OPT_IM, OPT_REG))
-ALT(DEF_ASM_OP2(mov,  0xb8, 0, OPC_REG, OPT_IM64, OPT_REG64))
-ALT(DEF_ASM_OP2(movq, 0xb8, 0, OPC_REG, OPT_IM64, OPT_REG64))
-ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWLX, OPT_IM, OPT_REG | OPT_EA))
-
-ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WLX, OPT_SEG, OPT_EA | OPT_REG))
-ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WLX, OPT_EA | OPT_REG, OPT_SEG))
-
-ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WLX, OPT_CR, OPT_REG64))
-ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WLX, OPT_DB, OPT_REG64))
-ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WLX, OPT_REG64, OPT_CR))
-ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WLX, OPT_REG64, OPT_DB))
-
-ALT(DEF_ASM_OP2(movsbw, 0x660fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG16))
-ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32))
-ALT(DEF_ASM_OP2(movsbq, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REGW))
-ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
-ALT(DEF_ASM_OP2(movswq, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG))
-ALT(DEF_ASM_OP2(movslq, 0x63, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG))
-ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WLX, OPT_REG8 | OPT_EA, OPT_REGW))
-ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
-ALT(DEF_ASM_OP2(movzwq, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG))
-
-ALT(DEF_ASM_OP1(pushq, 0x6a, 0, 0, OPT_IM8S))
-ALT(DEF_ASM_OP1(push, 0x6a, 0, 0, OPT_IM8S))
-ALT(DEF_ASM_OP1(pushw, 0x666a, 0, 0, OPT_IM8S))
-ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WLX, OPT_REG64))
-ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WLX, OPT_REG16))
-ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WLX, OPT_REG64 | OPT_EA))
-ALT(DEF_ASM_OP1(pushw, 0x6668, 0, 0, OPT_IM16))
-ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WLX, OPT_IM32))
-ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WLX, OPT_SEG))
-
-ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WLX, OPT_REG64))
-ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WLX, OPT_REG16))
-ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WLX, OPT_REGW | OPT_EA))
-ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WLX, OPT_SEG))
-
-ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WLX, OPT_REGW, OPT_EAX))
-ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WLX, OPT_EAX, OPT_REGW))
-ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWLX, OPT_REG, OPT_EA | OPT_REG))
-ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWLX, OPT_EA | OPT_REG, OPT_REG))
-
-ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX))
-ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8))
-ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX))
-ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX))
-
-ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8))
-ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8))
-ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX))
-ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX))
-
-ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WLX, OPT_EA, OPT_REG))
-
-ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
-ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
-ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32))
-ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
-ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
-
-     /* arith */
-ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWLX, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */
-ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWLX, OPT_EA | OPT_REG, OPT_REG))
-ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWLX, OPT_IM, OPT_EAX))
-ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WLX, OPT_IM8S, OPT_EA | OPT_REGW))
-ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWLX, OPT_IM, OPT_EA | OPT_REG))
-
-ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWLX, OPT_REG, OPT_EA | OPT_REG))
-ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWLX, OPT_EA | OPT_REG, OPT_REG))
-ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWLX, OPT_IM, OPT_EAX))
-ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWLX, OPT_IM, OPT_EA | OPT_REG))
-
-ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA))
-ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA))
-
-ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA))
-ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA))
-
-ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA))
-ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA))
-
-ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WLX, OPT_REG | OPT_EA, OPT_REG))
-ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WLX, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW))
-ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WLX, OPT_IM8S, OPT_REGW))
-ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WLX, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW))
-ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WLX, OPT_IMW, OPT_REGW))
-
-ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA))
-ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA, OPT_EAX))
-ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA))
-ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWLX, OPT_REG | OPT_EA, OPT_EAX))
-
-     /* shifts */
-ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWLX | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG))
-ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWLX | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG))
-ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWLX | OPC_SHIFT, OPT_EA | OPT_REG))
-
-ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WLX, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
-ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WLX, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
-ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WLX, OPT_REGW, OPT_EA | OPT_REGW))
-ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WLX, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
-ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WLX, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
-ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WLX, OPT_REGW, OPT_EA | OPT_REGW))
-
-ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR))
-ALT(DEF_ASM_OP1(call, 0xe8, 0, 0, OPT_DISP))
-ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR))
-ALT(DEF_ASM_OP1(jmp, 0xeb, 0, 0, OPT_DISP8))
-
-ALT(DEF_ASM_OP1(lcall, 0xff, 3, OPC_MODRM, OPT_EA))
-ALT(DEF_ASM_OP1(ljmp, 0xff, 5, OPC_MODRM, OPT_EA))
-    DEF_ASM_OP1(ljmpw, 0x66ff, 5, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(ljmpl, 0xff, 5, OPC_MODRM, OPT_EA)
-
-ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8))
-ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
-ALT(DEF_ASM_OP1(setob, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
-    DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8)
-    DEF_ASM_OP0(leave, 0xc9)
-    DEF_ASM_OP0(ret, 0xc3)
-    DEF_ASM_OP0(retq, 0xc3)
-ALT(DEF_ASM_OP1(retq, 0xc2, 0, 0, OPT_IM16))
-ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16))
-    DEF_ASM_OP0(lret, 0xcb)
-ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
-
-ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_TEST, OPT_DISP8))
-    DEF_ASM_OP1(loopne, 0xe0, 0, 0, OPT_DISP8)
-    DEF_ASM_OP1(loopnz, 0xe0, 0, 0, OPT_DISP8)
-    DEF_ASM_OP1(loope, 0xe1, 0, 0, OPT_DISP8)
-    DEF_ASM_OP1(loopz, 0xe1, 0, 0, OPT_DISP8)
-    DEF_ASM_OP1(loop, 0xe2, 0, 0, OPT_DISP8)
-    DEF_ASM_OP1(jecxz, 0x67e3, 0, 0, OPT_DISP8)
-
-     /* float */
-     /* specific fcomp handling */
-ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0))
-
-ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST))
-ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
-ALT(DEF_ASM_OP2(fadd, 0xdcc0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
-ALT(DEF_ASM_OP2(fmul, 0xdcc8, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
-ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH))
-ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST))
-ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
-ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
-ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH))
-ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
-ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
-ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
-ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
-
-     DEF_ASM_OP0(fucompp, 0xdae9)
-     DEF_ASM_OP0(ftst, 0xd9e4)
-     DEF_ASM_OP0(fxam, 0xd9e5)
-     DEF_ASM_OP0(fld1, 0xd9e8)
-     DEF_ASM_OP0(fldl2t, 0xd9e9)
-     DEF_ASM_OP0(fldl2e, 0xd9ea)
-     DEF_ASM_OP0(fldpi, 0xd9eb)
-     DEF_ASM_OP0(fldlg2, 0xd9ec)
-     DEF_ASM_OP0(fldln2, 0xd9ed)
-     DEF_ASM_OP0(fldz, 0xd9ee)
-
-     DEF_ASM_OP0(f2xm1, 0xd9f0)
-     DEF_ASM_OP0(fyl2x, 0xd9f1)
-     DEF_ASM_OP0(fptan, 0xd9f2)
-     DEF_ASM_OP0(fpatan, 0xd9f3)
-     DEF_ASM_OP0(fxtract, 0xd9f4)
-     DEF_ASM_OP0(fprem1, 0xd9f5)
-     DEF_ASM_OP0(fdecstp, 0xd9f6)
-     DEF_ASM_OP0(fincstp, 0xd9f7)
-     DEF_ASM_OP0(fprem, 0xd9f8)
-     DEF_ASM_OP0(fyl2xp1, 0xd9f9)
-     DEF_ASM_OP0(fsqrt, 0xd9fa)
-     DEF_ASM_OP0(fsincos, 0xd9fb)
-     DEF_ASM_OP0(frndint, 0xd9fc)
-     DEF_ASM_OP0(fscale, 0xd9fd)
-     DEF_ASM_OP0(fsin, 0xd9fe)
-     DEF_ASM_OP0(fcos, 0xd9ff)
-     DEF_ASM_OP0(fchs, 0xd9e0)
-     DEF_ASM_OP0(fabs, 0xd9e1)
-     DEF_ASM_OP0(fninit, 0xdbe3)
-     DEF_ASM_OP0(fnclex, 0xdbe2)
-     DEF_ASM_OP0(fnop, 0xd9d0)
-     DEF_ASM_OP0(fwait, 0x9b)
-
-    /* fp load */
-    DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST)
-    DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST)
-    DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA)
-ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA))
-    DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA)
-    DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA)
-
-    /* fp store */
-    DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST)
-    DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST)
-    DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA)
-ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA))
-    DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA)
-
-    DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST)
-    DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA)
-
-    /* exchange */
-    DEF_ASM_OP0(fxch, 0xd9c9)
-ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST))
-
-    /* misc FPU */
-    DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST )
-    DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST )
-
-    DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT)
-    DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA )
-    DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA )
-    DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )
-    DEF_ASM_OP0(fnstsw, 0xdfe0)
-ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX ))
-ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA ))
-    DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX )
-ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT))
-ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ))
-    DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT)
-    DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA )
-    DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
-    DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA )
-    DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA )
-    DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
-    DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA )
-    DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST )
-    DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST )
-    DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA )
-    DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA )
-    /* The *q forms of fxrstor/fxsave use a REX prefix.
-       If the operand would use extended registers we would have to modify
-       it instead of generating a second one.  Currently that's no
-       problem with TCC, we don't use extended registers.  */
-    DEF_ASM_OP1(fxsaveq, 0x0fae, 0, OPC_MODRM | OPC_48, OPT_EA )
-    DEF_ASM_OP1(fxrstorq, 0x0fae, 1, OPC_MODRM | OPC_48, OPT_EA )
-
-    /* segments */
-    DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA)
-ALT(DEF_ASM_OP2(larw, 0x0f02, 0, OPC_MODRM | OPC_WLX, OPT_REG | OPT_EA, OPT_REG))
-    DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(lgdtq, 0x0f01, 2, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(lidtq, 0x0f01, 3, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG)
-    DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG)
-ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WLX, OPT_EA | OPT_REG, OPT_REG))
-    DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG16)
-    DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(sgdtq, 0x0f01, 0, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(sidtq, 0x0f01, 1, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA)
-    DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA)
-    DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG32 | OPT_EA)
-ALT(DEF_ASM_OP1(str, 0x660f00, 1, OPC_MODRM, OPT_REG16))
-ALT(DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM | OPC_48, OPT_REG64))
-    DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA)
-    DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA)
-    DEF_ASM_OP0L(swapgs, 0x0f01, 7, OPC_MODRM)
-
-    /* 486 */
-    /* bswap can't be applied to 16bit regs */
-    DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 )
-    DEF_ASM_OP1(bswapl, 0x0fc8, 0, OPC_REG, OPT_REG32 )
-    DEF_ASM_OP1(bswapq, 0x0fc8, 0, OPC_REG | OPC_48, OPT_REG64 )
-
-ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWLX, OPT_REG, OPT_REG | OPT_EA ))
-ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWLX, OPT_REG, OPT_REG | OPT_EA ))
-    DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA )
-
-    /* pentium */
-    DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA )
-
-    /* AMD 64 */
-    DEF_ASM_OP1(cmpxchg16b, 0x0fc7, 1, OPC_MODRM | OPC_48, OPT_EA )
-
-    /* pentium pro */
-ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST | OPC_WLX, OPT_REGW | OPT_EA, OPT_REGW))
-
-    DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 )
-
-    DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
-    DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 )
-
-    /* mmx */
-    DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */
-    DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMXSSE )
-    /* movd shouldn't accept REG64, but AMD64 spec uses it for 32 and 64 bit
-       moves, so let's be compatible. */
-ALT(DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG64, OPT_MMXSSE ))
-ALT(DEF_ASM_OP2(movq, 0x0f6e, 0, OPC_MODRM | OPC_48, OPT_REG64, OPT_MMXSSE ))
-ALT(DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ))
-ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMXSSE, OPT_EA | OPT_REG32 ))
-ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMXSSE, OPT_EA | OPT_REG64 ))
-ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX ))
-ALT(DEF_ASM_OP2(movq, 0x660fd6, 0, OPC_MODRM, OPT_SSE, OPT_EA | OPT_SSE ))
-ALT(DEF_ASM_OP2(movq, 0xf30f7e, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE ))
-ALT(DEF_ASM_OP2(movq, 0x0f7e, 0, OPC_MODRM, OPT_MMXSSE, OPT_EA | OPT_REG64 ))
-
-    DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMXSSE ))
-    DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMXSSE ))
-    DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMXSSE ))
-    DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMXSSE ))
-    DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMXSSE ))
-    DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMXSSE ))
-    DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMXSSE ))
-    DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMXSSE ))
-    DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-
-    /* sse */
-    DEF_ASM_OP2(movups, 0x0f10, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_SSE )
-ALT(DEF_ASM_OP2(movups, 0x0f11, 0, OPC_MODRM, OPT_SSE, OPT_EA | OPT_REG32 ))
-    DEF_ASM_OP2(movaps, 0x0f28, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_SSE )
-ALT(DEF_ASM_OP2(movaps, 0x0f29, 0, OPC_MODRM, OPT_SSE, OPT_EA | OPT_REG32 ))
-    DEF_ASM_OP2(movhps, 0x0f16, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_SSE )
-ALT(DEF_ASM_OP2(movhps, 0x0f17, 0, OPC_MODRM, OPT_SSE, OPT_EA | OPT_REG32 ))
-    DEF_ASM_OP2(addps, 0x0f58, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(cvtpi2ps, 0x0f2a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_SSE )
-    DEF_ASM_OP2(cvtps2pi, 0x0f2d, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_MMX )
-    DEF_ASM_OP2(cvttps2pi, 0x0f2c, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_MMX )
-    DEF_ASM_OP2(divps, 0x0f5e, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(maxps, 0x0f5f, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(minps, 0x0f5d, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(mulps, 0x0f59, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(pavgb, 0x0fe0, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(pavgw, 0x0fe3, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(pmaxsw, 0x0fee, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pmaxub, 0x0fde, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pminsw, 0x0fea, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(pminub, 0x0fda, 0, OPC_MODRM, OPT_EA | OPT_MMXSSE, OPT_MMXSSE )
-    DEF_ASM_OP2(rcpss, 0x0f53, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(rsqrtps, 0x0f52, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(sqrtps, 0x0f51, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-    DEF_ASM_OP2(subps, 0x0f5c, 0, OPC_MODRM, OPT_EA | OPT_SSE, OPT_SSE )
-
-    DEF_ASM_OP1(prefetchnta, 0x0f18, 0, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(prefetcht0, 0x0f18, 1, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(prefetcht1, 0x0f18, 2, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(prefetcht2, 0x0f18, 3, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP1(prefetchw, 0x0f0d, 1, OPC_MODRM, OPT_EA)
-    DEF_ASM_OP0L(lfence, 0x0fae, 5, OPC_MODRM)
-    DEF_ASM_OP0L(mfence, 0x0fae, 6, OPC_MODRM)
-    DEF_ASM_OP0L(sfence, 0x0fae, 7, OPC_MODRM)
-    DEF_ASM_OP1(clflush, 0x0fae, 7, OPC_MODRM, OPT_EA)
-#undef ALT
-#undef DEF_ASM_OP0
-#undef DEF_ASM_OP0L
-#undef DEF_ASM_OP1
-#undef DEF_ASM_OP2
-#undef DEF_ASM_OP3
diff --git a/tinyc/x86_64-gen.c b/tinyc/x86_64-gen.c
deleted file mode 100644
index f4d86e1b7..000000000
--- a/tinyc/x86_64-gen.c
+++ /dev/null
@@ -1,2229 +0,0 @@
-/*
- *  x86-64 code generator for TCC
- *
- *  Copyright (c) 2008 Shinichiro Hamaji
- *
- *  Based on i386-gen.c by Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#ifdef TARGET_DEFS_ONLY
-
-/* number of available registers */
-#define NB_REGS         25
-#define NB_ASM_REGS     16
-#define CONFIG_TCC_ASM
-
-/* a register can belong to several classes. The classes must be
-   sorted from more general to more precise (see gv2() code which does
-   assumptions on it). */
-#define RC_INT     0x0001 /* generic integer register */
-#define RC_FLOAT   0x0002 /* generic float register */
-#define RC_RAX     0x0004
-#define RC_RCX     0x0008
-#define RC_RDX     0x0010
-#define RC_ST0     0x0080 /* only for long double */
-#define RC_R8      0x0100
-#define RC_R9      0x0200
-#define RC_R10     0x0400
-#define RC_R11     0x0800
-#define RC_XMM0    0x1000
-#define RC_XMM1    0x2000
-#define RC_XMM2    0x4000
-#define RC_XMM3    0x8000
-#define RC_XMM4    0x10000
-#define RC_XMM5    0x20000
-#define RC_XMM6    0x40000
-#define RC_XMM7    0x80000
-#define RC_IRET    RC_RAX /* function return: integer register */
-#define RC_LRET    RC_RDX /* function return: second integer register */
-#define RC_FRET    RC_XMM0 /* function return: float register */
-#define RC_QRET    RC_XMM1 /* function return: second float register */
-
-/* pretty names for the registers */
-enum {
-    TREG_RAX = 0,
-    TREG_RCX = 1,
-    TREG_RDX = 2,
-    TREG_RSP = 4,
-    TREG_RSI = 6,
-    TREG_RDI = 7,
-
-    TREG_R8  = 8,
-    TREG_R9  = 9,
-    TREG_R10 = 10,
-    TREG_R11 = 11,
-
-    TREG_XMM0 = 16,
-    TREG_XMM1 = 17,
-    TREG_XMM2 = 18,
-    TREG_XMM3 = 19,
-    TREG_XMM4 = 20,
-    TREG_XMM5 = 21,
-    TREG_XMM6 = 22,
-    TREG_XMM7 = 23,
-
-    TREG_ST0 = 24,
-
-    TREG_MEM = 0x20
-};
-
-#define REX_BASE(reg) (((reg) >> 3) & 1)
-#define REG_VALUE(reg) ((reg) & 7)
-
-/* return registers for function */
-#define REG_IRET TREG_RAX /* single word int return register */
-#define REG_LRET TREG_RDX /* second word return register (for long long) */
-#define REG_FRET TREG_XMM0 /* float return register */
-#define REG_QRET TREG_XMM1 /* second float return register */
-
-/* defined if function parameters must be evaluated in reverse order */
-#define INVERT_FUNC_PARAMS
-
-/* pointer size, in bytes */
-#define PTR_SIZE 8
-
-/* long double size and alignment, in bytes */
-#define LDOUBLE_SIZE  16
-#define LDOUBLE_ALIGN 16
-/* maximum alignment (for aligned attribute support) */
-#define MAX_ALIGN     16
-
-/******************************************************/
-#else /* ! TARGET_DEFS_ONLY */
-/******************************************************/
-#include "tcc.h"
-#include <assert.h>
-
-ST_DATA const int reg_classes[NB_REGS] = {
-    /* eax */ RC_INT | RC_RAX,
-    /* ecx */ RC_INT | RC_RCX,
-    /* edx */ RC_INT | RC_RDX,
-    0,
-    0,
-    0,
-    0,
-    0,
-    RC_R8,
-    RC_R9,
-    RC_R10,
-    RC_R11,
-    0,
-    0,
-    0,
-    0,
-    /* xmm0 */ RC_FLOAT | RC_XMM0,
-    /* xmm1 */ RC_FLOAT | RC_XMM1,
-    /* xmm2 */ RC_FLOAT | RC_XMM2,
-    /* xmm3 */ RC_FLOAT | RC_XMM3,
-    /* xmm4 */ RC_FLOAT | RC_XMM4,
-    /* xmm5 */ RC_FLOAT | RC_XMM5,
-    /* xmm6 an xmm7 are included so gv() can be used on them,
-       but they are not tagged with RC_FLOAT because they are
-       callee saved on Windows */
-    RC_XMM6,
-    RC_XMM7,
-    /* st0 */ RC_ST0
-};
-
-static unsigned long func_sub_sp_offset;
-static int func_ret_sub;
-
-/* XXX: make it faster ? */
-ST_FUNC void g(int c)
-{
-    int ind1;
-    if (nocode_wanted)
-        return;
-    ind1 = ind + 1;
-    if (ind1 > cur_text_section->data_allocated)
-        section_realloc(cur_text_section, ind1);
-    cur_text_section->data[ind] = c;
-    ind = ind1;
-}
-
-ST_FUNC void o(unsigned int c)
-{
-    while (c) {
-        g(c);
-        c = c >> 8;
-    }
-}
-
-ST_FUNC void gen_le16(int v)
-{
-    g(v);
-    g(v >> 8);
-}
-
-ST_FUNC void gen_le32(int c)
-{
-    g(c);
-    g(c >> 8);
-    g(c >> 16);
-    g(c >> 24);
-}
-
-ST_FUNC void gen_le64(int64_t c)
-{
-    g(c);
-    g(c >> 8);
-    g(c >> 16);
-    g(c >> 24);
-    g(c >> 32);
-    g(c >> 40);
-    g(c >> 48);
-    g(c >> 56);
-}
-
-static void orex(int ll, int r, int r2, int b)
-{
-    if ((r & VT_VALMASK) >= VT_CONST)
-        r = 0;
-    if ((r2 & VT_VALMASK) >= VT_CONST)
-        r2 = 0;
-    if (ll || REX_BASE(r) || REX_BASE(r2))
-        o(0x40 | REX_BASE(r) | (REX_BASE(r2) << 2) | (ll << 3));
-    o(b);
-}
-
-/* output a symbol and patch all calls to it */
-ST_FUNC void gsym_addr(int t, int a)
-{
-    while (t) {
-        unsigned char *ptr = cur_text_section->data + t;
-        uint32_t n = read32le(ptr); /* next value */
-        write32le(ptr, a - t - 4);
-        t = n;
-    }
-}
-
-void gsym(int t)
-{
-    gsym_addr(t, ind);
-}
-
-
-static int is64_type(int t)
-{
-    return ((t & VT_BTYPE) == VT_PTR ||
-            (t & VT_BTYPE) == VT_FUNC ||
-            (t & VT_BTYPE) == VT_LLONG);
-}
-
-/* instruction + 4 bytes data. Return the address of the data */
-static int oad(int c, int s)
-{
-    int t;
-    if (nocode_wanted)
-        return s;
-    o(c);
-    t = ind;
-    gen_le32(s);
-    return t;
-}
-
-/* generate jmp to a label */
-#define gjmp2(instr,lbl) oad(instr,lbl)
-
-ST_FUNC void gen_addr32(int r, Sym *sym, int c)
-{
-    if (r & VT_SYM)
-        greloca(cur_text_section, sym, ind, R_X86_64_32S, c), c=0;
-    gen_le32(c);
-}
-
-/* output constant with relocation if 'r & VT_SYM' is true */
-ST_FUNC void gen_addr64(int r, Sym *sym, int64_t c)
-{
-    if (r & VT_SYM)
-        greloca(cur_text_section, sym, ind, R_X86_64_64, c), c=0;
-    gen_le64(c);
-}
-
-/* output constant with relocation if 'r & VT_SYM' is true */
-ST_FUNC void gen_addrpc32(int r, Sym *sym, int c)
-{
-    greloca(cur_text_section, sym, ind, R_X86_64_PC32, 0);
-    gen_le32(c-4);
-}
-
-/* output got address with relocation */
-static void gen_gotpcrel(int r, Sym *sym, int c)
-{
-#ifdef TCC_TARGET_PE
-    tcc_error("internal error: no GOT on PE: %s %x %x | %02x %02x %02x\n",
-        get_tok_str(sym->v, NULL), c, r,
-        cur_text_section->data[ind-3],
-        cur_text_section->data[ind-2],
-        cur_text_section->data[ind-1]
-        );
-#endif
-    greloca(cur_text_section, sym, ind, R_X86_64_GOTPCREL, -4);
-    gen_le32(0);
-    if (c) {
-        /* we use add c, %xxx for displacement */
-        orex(1, r, 0, 0x81);
-        o(0xc0 + REG_VALUE(r));
-        gen_le32(c);
-    }
-}
-
-static void gen_modrm_impl(int op_reg, int r, Sym *sym, int c, int is_got)
-{
-    op_reg = REG_VALUE(op_reg) << 3;
-    if ((r & VT_VALMASK) == VT_CONST) {
-        /* constant memory reference */
-        o(0x05 | op_reg);
-        if (is_got) {
-            gen_gotpcrel(r, sym, c);
-        } else {
-            gen_addrpc32(r, sym, c);
-        }
-    } else if ((r & VT_VALMASK) == VT_LOCAL) {
-        /* currently, we use only ebp as base */
-        if (c == (char)c) {
-            /* short reference */
-            o(0x45 | op_reg);
-            g(c);
-        } else {
-            oad(0x85 | op_reg, c);
-        }
-    } else if ((r & VT_VALMASK) >= TREG_MEM) {
-        if (c) {
-            g(0x80 | op_reg | REG_VALUE(r));
-            gen_le32(c);
-        } else {
-            g(0x00 | op_reg | REG_VALUE(r));
-        }
-    } else {
-        g(0x00 | op_reg | REG_VALUE(r));
-    }
-}
-
-/* generate a modrm reference. 'op_reg' contains the additional 3
-   opcode bits */
-static void gen_modrm(int op_reg, int r, Sym *sym, int c)
-{
-    gen_modrm_impl(op_reg, r, sym, c, 0);
-}
-
-/* generate a modrm reference. 'op_reg' contains the additional 3
-   opcode bits */
-static void gen_modrm64(int opcode, int op_reg, int r, Sym *sym, int c)
-{
-    int is_got;
-    is_got = (op_reg & TREG_MEM) && !(sym->type.t & VT_STATIC);
-    orex(1, r, op_reg, opcode);
-    gen_modrm_impl(op_reg, r, sym, c, is_got);
-}
-
-
-/* load 'r' from value 'sv' */
-void load(int r, SValue *sv)
-{
-    int v, t, ft, fc, fr;
-    SValue v1;
-
-#ifdef TCC_TARGET_PE
-    SValue v2;
-    sv = pe_getimport(sv, &v2);
-#endif
-
-    fr = sv->r;
-    ft = sv->type.t & ~VT_DEFSIGN;
-    fc = sv->c.i;
-    if (fc != sv->c.i && (fr & VT_SYM))
-      tcc_error("64 bit addend in load");
-
-    ft &= ~(VT_VOLATILE | VT_CONSTANT);
-
-#ifndef TCC_TARGET_PE
-    /* we use indirect access via got */
-    if ((fr & VT_VALMASK) == VT_CONST && (fr & VT_SYM) &&
-        (fr & VT_LVAL) && !(sv->sym->type.t & VT_STATIC)) {
-        /* use the result register as a temporal register */
-        int tr = r | TREG_MEM;
-        if (is_float(ft)) {
-            /* we cannot use float registers as a temporal register */
-            tr = get_reg(RC_INT) | TREG_MEM;
-        }
-        gen_modrm64(0x8b, tr, fr, sv->sym, 0);
-
-        /* load from the temporal register */
-        fr = tr | VT_LVAL;
-    }
-#endif
-
-    v = fr & VT_VALMASK;
-    if (fr & VT_LVAL) {
-        int b, ll;
-        if (v == VT_LLOCAL) {
-            v1.type.t = VT_PTR;
-            v1.r = VT_LOCAL | VT_LVAL;
-            v1.c.i = fc;
-            fr = r;
-            if (!(reg_classes[fr] & (RC_INT|RC_R11)))
-                fr = get_reg(RC_INT);
-            load(fr, &v1);
-        }
-        ll = 0;
-	/* Like GCC we can load from small enough properly sized
-	   structs and unions as well.
-	   XXX maybe move to generic operand handling, but should
-	   occur only with asm, so tccasm.c might also be a better place */
-	if ((ft & VT_BTYPE) == VT_STRUCT) {
-	    int align;
-	    switch (type_size(&sv->type, &align)) {
-		case 1: ft = VT_BYTE; break;
-		case 2: ft = VT_SHORT; break;
-		case 4: ft = VT_INT; break;
-		case 8: ft = VT_LLONG; break;
-		default:
-		    tcc_error("invalid aggregate type for register load");
-		    break;
-	    }
-	}
-        if ((ft & VT_BTYPE) == VT_FLOAT) {
-            b = 0x6e0f66;
-            r = REG_VALUE(r); /* movd */
-        } else if ((ft & VT_BTYPE) == VT_DOUBLE) {
-            b = 0x7e0ff3; /* movq */
-            r = REG_VALUE(r);
-        } else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
-            b = 0xdb, r = 5; /* fldt */
-        } else if ((ft & VT_TYPE) == VT_BYTE || (ft & VT_TYPE) == VT_BOOL) {
-            b = 0xbe0f;   /* movsbl */
-        } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) {
-            b = 0xb60f;   /* movzbl */
-        } else if ((ft & VT_TYPE) == VT_SHORT) {
-            b = 0xbf0f;   /* movswl */
-        } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) {
-            b = 0xb70f;   /* movzwl */
-        } else {
-            assert(((ft & VT_BTYPE) == VT_INT)
-                   || ((ft & VT_BTYPE) == VT_LLONG)
-                   || ((ft & VT_BTYPE) == VT_PTR)
-                   || ((ft & VT_BTYPE) == VT_FUNC)
-                );
-            ll = is64_type(ft);
-            b = 0x8b;
-        }
-        if (ll) {
-            gen_modrm64(b, r, fr, sv->sym, fc);
-        } else {
-            orex(ll, fr, r, b);
-            gen_modrm(r, fr, sv->sym, fc);
-        }
-    } else {
-        if (v == VT_CONST) {
-            if (fr & VT_SYM) {
-#ifdef TCC_TARGET_PE
-                orex(1,0,r,0x8d);
-                o(0x05 + REG_VALUE(r) * 8); /* lea xx(%rip), r */
-                gen_addrpc32(fr, sv->sym, fc);
-#else
-                if (sv->sym->type.t & VT_STATIC) {
-                    orex(1,0,r,0x8d);
-                    o(0x05 + REG_VALUE(r) * 8); /* lea xx(%rip), r */
-                    gen_addrpc32(fr, sv->sym, fc);
-                } else {
-                    orex(1,0,r,0x8b);
-                    o(0x05 + REG_VALUE(r) * 8); /* mov xx(%rip), r */
-                    gen_gotpcrel(r, sv->sym, fc);
-                }
-#endif
-            } else if (is64_type(ft)) {
-                orex(1,r,0, 0xb8 + REG_VALUE(r)); /* mov $xx, r */
-                gen_le64(sv->c.i);
-            } else {
-                orex(0,r,0, 0xb8 + REG_VALUE(r)); /* mov $xx, r */
-                gen_le32(fc);
-            }
-        } else if (v == VT_LOCAL) {
-            orex(1,0,r,0x8d); /* lea xxx(%ebp), r */
-            gen_modrm(r, VT_LOCAL, sv->sym, fc);
-        } else if (v == VT_CMP) {
-            orex(0,r,0,0);
-	    if ((fc & ~0x100) != TOK_NE)
-              oad(0xb8 + REG_VALUE(r), 0); /* mov $0, r */
-	    else
-              oad(0xb8 + REG_VALUE(r), 1); /* mov $1, r */
-	    if (fc & 0x100)
-	      {
-	        /* This was a float compare.  If the parity bit is
-		   set the result was unordered, meaning false for everything
-		   except TOK_NE, and true for TOK_NE.  */
-		fc &= ~0x100;
-		o(0x037a + (REX_BASE(r) << 8));
-	      }
-            orex(0,r,0, 0x0f); /* setxx %br */
-            o(fc);
-            o(0xc0 + REG_VALUE(r));
-        } else if (v == VT_JMP || v == VT_JMPI) {
-            t = v & 1;
-            orex(0,r,0,0);
-            oad(0xb8 + REG_VALUE(r), t); /* mov $1, r */
-            o(0x05eb + (REX_BASE(r) << 8)); /* jmp after */
-            gsym(fc);
-            orex(0,r,0,0);
-            oad(0xb8 + REG_VALUE(r), t ^ 1); /* mov $0, r */
-        } else if (v != r) {
-            if ((r >= TREG_XMM0) && (r <= TREG_XMM7)) {
-                if (v == TREG_ST0) {
-                    /* gen_cvt_ftof(VT_DOUBLE); */
-                    o(0xf0245cdd); /* fstpl -0x10(%rsp) */
-                    /* movsd -0x10(%rsp),%xmmN */
-                    o(0x100ff2);
-                    o(0x44 + REG_VALUE(r)*8); /* %xmmN */
-                    o(0xf024);
-                } else {
-                    assert((v >= TREG_XMM0) && (v <= TREG_XMM7));
-                    if ((ft & VT_BTYPE) == VT_FLOAT) {
-                        o(0x100ff3);
-                    } else {
-                        assert((ft & VT_BTYPE) == VT_DOUBLE);
-                        o(0x100ff2);
-                    }
-                    o(0xc0 + REG_VALUE(v) + REG_VALUE(r)*8);
-                }
-            } else if (r == TREG_ST0) {
-                assert((v >= TREG_XMM0) && (v <= TREG_XMM7));
-                /* gen_cvt_ftof(VT_LDOUBLE); */
-                /* movsd %xmmN,-0x10(%rsp) */
-                o(0x110ff2);
-                o(0x44 + REG_VALUE(r)*8); /* %xmmN */
-                o(0xf024);
-                o(0xf02444dd); /* fldl -0x10(%rsp) */
-            } else {
-                orex(1,r,v, 0x89);
-                o(0xc0 + REG_VALUE(r) + REG_VALUE(v) * 8); /* mov v, r */
-            }
-        }
-    }
-}
-
-/* store register 'r' in lvalue 'v' */
-void store(int r, SValue *v)
-{
-    int fr, bt, ft, fc;
-    int op64 = 0;
-    /* store the REX prefix in this variable when PIC is enabled */
-    int pic = 0;
-
-#ifdef TCC_TARGET_PE
-    SValue v2;
-    v = pe_getimport(v, &v2);
-#endif
-
-    fr = v->r & VT_VALMASK;
-    ft = v->type.t;
-    fc = v->c.i;
-    if (fc != v->c.i && (fr & VT_SYM))
-      tcc_error("64 bit addend in store");
-    ft &= ~(VT_VOLATILE | VT_CONSTANT);
-    bt = ft & VT_BTYPE;
-
-#ifndef TCC_TARGET_PE
-    /* we need to access the variable via got */
-    if (fr == VT_CONST && (v->r & VT_SYM)) {
-        /* mov xx(%rip), %r11 */
-        o(0x1d8b4c);
-        gen_gotpcrel(TREG_R11, v->sym, v->c.i);
-        pic = is64_type(bt) ? 0x49 : 0x41;
-    }
-#endif
-
-    /* XXX: incorrect if float reg to reg */
-    if (bt == VT_FLOAT) {
-        o(0x66);
-        o(pic);
-        o(0x7e0f); /* movd */
-        r = REG_VALUE(r);
-    } else if (bt == VT_DOUBLE) {
-        o(0x66);
-        o(pic);
-        o(0xd60f); /* movq */
-        r = REG_VALUE(r);
-    } else if (bt == VT_LDOUBLE) {
-        o(0xc0d9); /* fld %st(0) */
-        o(pic);
-        o(0xdb); /* fstpt */
-        r = 7;
-    } else {
-        if (bt == VT_SHORT)
-            o(0x66);
-        o(pic);
-        if (bt == VT_BYTE || bt == VT_BOOL)
-            orex(0, 0, r, 0x88);
-        else if (is64_type(bt))
-            op64 = 0x89;
-        else
-            orex(0, 0, r, 0x89);
-    }
-    if (pic) {
-        /* xxx r, (%r11) where xxx is mov, movq, fld, or etc */
-        if (op64)
-            o(op64);
-        o(3 + (r << 3));
-    } else if (op64) {
-        if (fr == VT_CONST || fr == VT_LOCAL || (v->r & VT_LVAL)) {
-            gen_modrm64(op64, r, v->r, v->sym, fc);
-        } else if (fr != r) {
-            /* XXX: don't we really come here? */
-            abort();
-            o(0xc0 + fr + r * 8); /* mov r, fr */
-        }
-    } else {
-        if (fr == VT_CONST || fr == VT_LOCAL || (v->r & VT_LVAL)) {
-            gen_modrm(r, v->r, v->sym, fc);
-        } else if (fr != r) {
-            /* XXX: don't we really come here? */
-            abort();
-            o(0xc0 + fr + r * 8); /* mov r, fr */
-        }
-    }
-}
-
-/* 'is_jmp' is '1' if it is a jump */
-static void gcall_or_jmp(int is_jmp)
-{
-    int r;
-    if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST &&
-	((vtop->r & VT_SYM) || (vtop->c.i-4) == (int)(vtop->c.i-4))) {
-        /* constant case */
-        if (vtop->r & VT_SYM) {
-            /* relocation case */
-#ifdef TCC_TARGET_PE
-            greloca(cur_text_section, vtop->sym, ind + 1, R_X86_64_PC32, (int)(vtop->c.i-4));
-#else
-            greloca(cur_text_section, vtop->sym, ind + 1, R_X86_64_PLT32, (int)(vtop->c.i-4));
-#endif
-        } else {
-            /* put an empty PC32 relocation */
-            put_elf_reloca(symtab_section, cur_text_section,
-                          ind + 1, R_X86_64_PC32, 0, (int)(vtop->c.i-4));
-        }
-        oad(0xe8 + is_jmp, 0); /* call/jmp im */
-    } else {
-        /* otherwise, indirect call */
-        r = TREG_R11;
-        load(r, vtop);
-        o(0x41); /* REX */
-        o(0xff); /* call/jmp *r */
-        o(0xd0 + REG_VALUE(r) + (is_jmp << 4));
-    }
-}
-
-#if defined(CONFIG_TCC_BCHECK)
-#ifndef TCC_TARGET_PE
-static addr_t func_bound_offset;
-static unsigned long func_bound_ind;
-#endif
-
-static void gen_static_call(int v)
-{
-    Sym *sym = external_global_sym(v, &func_old_type, 0);
-    oad(0xe8, 0);
-    greloca(cur_text_section, sym, ind-4, R_X86_64_PC32, -4);
-}
-
-/* generate a bounded pointer addition */
-ST_FUNC void gen_bounded_ptr_add(void)
-{
-    /* save all temporary registers */
-    save_regs(0);
-
-    /* prepare fast x86_64 function call */
-    gv(RC_RAX);
-    o(0xc68948); // mov  %rax,%rsi ## second arg in %rsi, this must be size
-    vtop--;
-
-    gv(RC_RAX);
-    o(0xc78948); // mov  %rax,%rdi ## first arg in %rdi, this must be ptr
-    vtop--;
-
-    /* do a fast function call */
-    gen_static_call(TOK___bound_ptr_add);
-
-    /* returned pointer is in rax */
-    vtop++;
-    vtop->r = TREG_RAX | VT_BOUNDED;
-
-
-    /* relocation offset of the bounding function call point */
-    vtop->c.i = (cur_text_section->reloc->data_offset - sizeof(ElfW(Rela)));
-}
-
-/* patch pointer addition in vtop so that pointer dereferencing is
-   also tested */
-ST_FUNC void gen_bounded_ptr_deref(void)
-{
-    addr_t func;
-    int size, align;
-    ElfW(Rela) *rel;
-    Sym *sym;
-
-    size = 0;
-    /* XXX: put that code in generic part of tcc */
-    if (!is_float(vtop->type.t)) {
-        if (vtop->r & VT_LVAL_BYTE)
-            size = 1;
-        else if (vtop->r & VT_LVAL_SHORT)
-            size = 2;
-    }
-    if (!size)
-    size = type_size(&vtop->type, &align);
-    switch(size) {
-    case  1: func = TOK___bound_ptr_indir1; break;
-    case  2: func = TOK___bound_ptr_indir2; break;
-    case  4: func = TOK___bound_ptr_indir4; break;
-    case  8: func = TOK___bound_ptr_indir8; break;
-    case 12: func = TOK___bound_ptr_indir12; break;
-    case 16: func = TOK___bound_ptr_indir16; break;
-    default:
-        tcc_error("unhandled size when dereferencing bounded pointer");
-        func = 0;
-        break;
-    }
-
-    sym = external_global_sym(func, &func_old_type, 0);
-    if (!sym->c)
-        put_extern_sym(sym, NULL, 0, 0);
-
-    /* patch relocation */
-    /* XXX: find a better solution ? */
-
-    rel = (ElfW(Rela) *)(cur_text_section->reloc->data + vtop->c.i);
-    rel->r_info = ELF64_R_INFO(sym->c, ELF64_R_TYPE(rel->r_info));
-}
-#endif
-
-#ifdef TCC_TARGET_PE
-
-#define REGN 4
-static const uint8_t arg_regs[REGN] = {
-    TREG_RCX, TREG_RDX, TREG_R8, TREG_R9
-};
-
-/* Prepare arguments in R10 and R11 rather than RCX and RDX
-   because gv() will not ever use these */
-static int arg_prepare_reg(int idx) {
-  if (idx == 0 || idx == 1)
-      /* idx=0: r10, idx=1: r11 */
-      return idx + 10;
-  else
-      return arg_regs[idx];
-}
-
-static int func_scratch, func_alloca;
-
-/* Generate function call. The function address is pushed first, then
-   all the parameters in call order. This functions pops all the
-   parameters and the function address. */
-
-static void gen_offs_sp(int b, int r, int d)
-{
-    orex(1,0,r & 0x100 ? 0 : r, b);
-    if (d == (char)d) {
-        o(0x2444 | (REG_VALUE(r) << 3));
-        g(d);
-    } else {
-        o(0x2484 | (REG_VALUE(r) << 3));
-        gen_le32(d);
-    }
-}
-
-static int using_regs(int size)
-{
-    return !(size > 8 || (size & (size - 1)));
-}
-
-/* Return the number of registers needed to return the struct, or 0 if
-   returning via struct pointer. */
-ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize)
-{
-    int size, align;
-    *ret_align = 1; // Never have to re-align return values for x86-64
-    *regsize = 8;
-    size = type_size(vt, &align);
-    if (!using_regs(size))
-        return 0;
-    if (size == 8)
-        ret->t = VT_LLONG;
-    else if (size == 4)
-        ret->t = VT_INT;
-    else if (size == 2)
-        ret->t = VT_SHORT;
-    else
-        ret->t = VT_BYTE;
-    ret->ref = NULL;
-    return 1;
-}
-
-static int is_sse_float(int t) {
-    int bt;
-    bt = t & VT_BTYPE;
-    return bt == VT_DOUBLE || bt == VT_FLOAT;
-}
-
-static int gfunc_arg_size(CType *type) {
-    int align;
-    if (type->t & (VT_ARRAY|VT_BITFIELD))
-        return 8;
-    return type_size(type, &align);
-}
-
-void gfunc_call(int nb_args)
-{
-    int size, r, args_size, i, d, bt, struct_size;
-    int arg;
-
-    args_size = (nb_args < REGN ? REGN : nb_args) * PTR_SIZE;
-    arg = nb_args;
-
-    /* for struct arguments, we need to call memcpy and the function
-       call breaks register passing arguments we are preparing.
-       So, we process arguments which will be passed by stack first. */
-    struct_size = args_size;
-    for(i = 0; i < nb_args; i++) {
-        SValue *sv;
-        
-        --arg;
-        sv = &vtop[-i];
-        bt = (sv->type.t & VT_BTYPE);
-        size = gfunc_arg_size(&sv->type);
-
-        if (using_regs(size))
-            continue; /* arguments smaller than 8 bytes passed in registers or on stack */
-
-        if (bt == VT_STRUCT) {
-            /* align to stack align size */
-            size = (size + 15) & ~15;
-            /* generate structure store */
-            r = get_reg(RC_INT);
-            gen_offs_sp(0x8d, r, struct_size);
-            struct_size += size;
-
-            /* generate memcpy call */
-            vset(&sv->type, r | VT_LVAL, 0);
-            vpushv(sv);
-            vstore();
-            --vtop;
-        } else if (bt == VT_LDOUBLE) {
-            gv(RC_ST0);
-            gen_offs_sp(0xdb, 0x107, struct_size);
-            struct_size += 16;
-        }
-    }
-
-    if (func_scratch < struct_size)
-        func_scratch = struct_size;
-
-    arg = nb_args;
-    struct_size = args_size;
-
-    for(i = 0; i < nb_args; i++) {
-        --arg;
-        bt = (vtop->type.t & VT_BTYPE);
-
-        size = gfunc_arg_size(&vtop->type);
-        if (!using_regs(size)) {
-            /* align to stack align size */
-            size = (size + 15) & ~15;
-            if (arg >= REGN) {
-                d = get_reg(RC_INT);
-                gen_offs_sp(0x8d, d, struct_size);
-                gen_offs_sp(0x89, d, arg*8);
-            } else {
-                d = arg_prepare_reg(arg);
-                gen_offs_sp(0x8d, d, struct_size);
-            }
-            struct_size += size;
-        } else {
-            if (is_sse_float(vtop->type.t)) {
-		if (tcc_state->nosse)
-		  tcc_error("SSE disabled");
-                gv(RC_XMM0); /* only use one float register */
-                if (arg >= REGN) {
-                    /* movq %xmm0, j*8(%rsp) */
-                    gen_offs_sp(0xd60f66, 0x100, arg*8);
-                } else {
-                    /* movaps %xmm0, %xmmN */
-                    o(0x280f);
-                    o(0xc0 + (arg << 3));
-                    d = arg_prepare_reg(arg);
-                    /* mov %xmm0, %rxx */
-                    o(0x66);
-                    orex(1,d,0, 0x7e0f);
-                    o(0xc0 + REG_VALUE(d));
-                }
-            } else {
-                if (bt == VT_STRUCT) {
-                    vtop->type.ref = NULL;
-                    vtop->type.t = size > 4 ? VT_LLONG : size > 2 ? VT_INT
-                        : size > 1 ? VT_SHORT : VT_BYTE;
-                }
-                
-                r = gv(RC_INT);
-                if (arg >= REGN) {
-                    gen_offs_sp(0x89, r, arg*8);
-                } else {
-                    d = arg_prepare_reg(arg);
-                    orex(1,d,r,0x89); /* mov */
-                    o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d));
-                }
-            }
-        }
-        vtop--;
-    }
-    save_regs(0);
-    
-    /* Copy R10 and R11 into RCX and RDX, respectively */
-    if (nb_args > 0) {
-        o(0xd1894c); /* mov %r10, %rcx */
-        if (nb_args > 1) {
-            o(0xda894c); /* mov %r11, %rdx */
-        }
-    }
-    
-    gcall_or_jmp(0);
-
-    if ((vtop->r & VT_SYM) && vtop->sym->v == TOK_alloca) {
-        /* need to add the "func_scratch" area after alloca */
-        o(0x0548), gen_le32(func_alloca), func_alloca = ind - 4;
-    }
-
-    /* other compilers don't clear the upper bits when returning char/short */
-    bt = vtop->type.ref->type.t & (VT_BTYPE | VT_UNSIGNED);
-    if (bt == (VT_BYTE | VT_UNSIGNED))
-        o(0xc0b60f);  /* movzbl %al, %eax */
-    else if (bt == VT_BYTE)
-        o(0xc0be0f); /* movsbl %al, %eax */
-    else if (bt == VT_SHORT)
-        o(0x98); /* cwtl */
-    else if (bt == (VT_SHORT | VT_UNSIGNED))
-        o(0xc0b70f);  /* movzbl %al, %eax */
-#if 0 /* handled in gen_cast() */
-    else if (bt == VT_INT)
-        o(0x9848); /* cltq */
-    else if (bt == (VT_INT | VT_UNSIGNED))
-        o(0xc089); /* mov %eax,%eax */
-#endif
-    vtop--;
-}
-
-
-#define FUNC_PROLOG_SIZE 11
-
-/* generate function prolog of type 't' */
-void gfunc_prolog(CType *func_type)
-{
-    int addr, reg_param_index, bt, size;
-    Sym *sym;
-    CType *type;
-
-    func_ret_sub = 0;
-    func_scratch = 0;
-    func_alloca = 0;
-    loc = 0;
-
-    addr = PTR_SIZE * 2;
-    ind += FUNC_PROLOG_SIZE;
-    func_sub_sp_offset = ind;
-    reg_param_index = 0;
-
-    sym = func_type->ref;
-
-    /* if the function returns a structure, then add an
-       implicit pointer parameter */
-    func_vt = sym->type;
-    func_var = (sym->f.func_type == FUNC_ELLIPSIS);
-    size = gfunc_arg_size(&func_vt);
-    if (!using_regs(size)) {
-        gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr);
-        func_vc = addr;
-        reg_param_index++;
-        addr += 8;
-    }
-
-    /* define parameters */
-    while ((sym = sym->next) != NULL) {
-        type = &sym->type;
-        bt = type->t & VT_BTYPE;
-        size = gfunc_arg_size(type);
-        if (!using_regs(size)) {
-            if (reg_param_index < REGN) {
-                gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr);
-            }
-            sym_push(sym->v & ~SYM_FIELD, type, VT_LLOCAL | VT_LVAL, addr);
-        } else {
-            if (reg_param_index < REGN) {
-                /* save arguments passed by register */
-                if ((bt == VT_FLOAT) || (bt == VT_DOUBLE)) {
-		    if (tcc_state->nosse)
-		      tcc_error("SSE disabled");
-                    o(0xd60f66); /* movq */
-                    gen_modrm(reg_param_index, VT_LOCAL, NULL, addr);
-                } else {
-                    gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr);
-                }
-            }
-            sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | VT_LVAL, addr);
-        }
-        addr += 8;
-        reg_param_index++;
-    }
-
-    while (reg_param_index < REGN) {
-        if (func_type->ref->f.func_type == FUNC_ELLIPSIS) {
-            gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr);
-            addr += 8;
-        }
-        reg_param_index++;
-    }
-}
-
-/* generate function epilog */
-void gfunc_epilog(void)
-{
-    int v, saved_ind;
-
-    o(0xc9); /* leave */
-    if (func_ret_sub == 0) {
-        o(0xc3); /* ret */
-    } else {
-        o(0xc2); /* ret n */
-        g(func_ret_sub);
-        g(func_ret_sub >> 8);
-    }
-
-    saved_ind = ind;
-    ind = func_sub_sp_offset - FUNC_PROLOG_SIZE;
-    /* align local size to word & save local variables */
-    v = (func_scratch + -loc + 15) & -16;
-
-    if (v >= 4096) {
-        Sym *sym = external_global_sym(TOK___chkstk, &func_old_type, 0);
-        oad(0xb8, v); /* mov stacksize, %eax */
-        oad(0xe8, 0); /* call __chkstk, (does the stackframe too) */
-        greloca(cur_text_section, sym, ind-4, R_X86_64_PC32, -4);
-        o(0x90); /* fill for FUNC_PROLOG_SIZE = 11 bytes */
-    } else {
-        o(0xe5894855);  /* push %rbp, mov %rsp, %rbp */
-        o(0xec8148);  /* sub rsp, stacksize */
-        gen_le32(v);
-    }
-
-    /* add the "func_scratch" area after each alloca seen */
-    while (func_alloca) {
-        unsigned char *ptr = cur_text_section->data + func_alloca;
-        func_alloca = read32le(ptr);
-        write32le(ptr, func_scratch);
-    }
-
-    cur_text_section->data_offset = saved_ind;
-    pe_add_unwind_data(ind, saved_ind, v);
-    ind = cur_text_section->data_offset;
-}
-
-#else
-
-static void gadd_sp(int val)
-{
-    if (val == (char)val) {
-        o(0xc48348);
-        g(val);
-    } else {
-        oad(0xc48148, val); /* add $xxx, %rsp */
-    }
-}
-
-typedef enum X86_64_Mode {
-  x86_64_mode_none,
-  x86_64_mode_memory,
-  x86_64_mode_integer,
-  x86_64_mode_sse,
-  x86_64_mode_x87
-} X86_64_Mode;
-
-static X86_64_Mode classify_x86_64_merge(X86_64_Mode a, X86_64_Mode b)
-{
-    if (a == b)
-        return a;
-    else if (a == x86_64_mode_none)
-        return b;
-    else if (b == x86_64_mode_none)
-        return a;
-    else if ((a == x86_64_mode_memory) || (b == x86_64_mode_memory))
-        return x86_64_mode_memory;
-    else if ((a == x86_64_mode_integer) || (b == x86_64_mode_integer))
-        return x86_64_mode_integer;
-    else if ((a == x86_64_mode_x87) || (b == x86_64_mode_x87))
-        return x86_64_mode_memory;
-    else
-        return x86_64_mode_sse;
-}
-
-static X86_64_Mode classify_x86_64_inner(CType *ty)
-{
-    X86_64_Mode mode;
-    Sym *f;
-    
-    switch (ty->t & VT_BTYPE) {
-    case VT_VOID: return x86_64_mode_none;
-    
-    case VT_INT:
-    case VT_BYTE:
-    case VT_SHORT:
-    case VT_LLONG:
-    case VT_BOOL:
-    case VT_PTR:
-    case VT_FUNC:
-        return x86_64_mode_integer;
-    
-    case VT_FLOAT:
-    case VT_DOUBLE: return x86_64_mode_sse;
-    
-    case VT_LDOUBLE: return x86_64_mode_x87;
-      
-    case VT_STRUCT:
-        f = ty->ref;
-
-        mode = x86_64_mode_none;
-        for (f = f->next; f; f = f->next)
-            mode = classify_x86_64_merge(mode, classify_x86_64_inner(&f->type));
-        
-        return mode;
-    }
-    assert(0);
-    return 0;
-}
-
-static X86_64_Mode classify_x86_64_arg(CType *ty, CType *ret, int *psize, int *palign, int *reg_count)
-{
-    X86_64_Mode mode;
-    int size, align, ret_t = 0;
-    
-    if (ty->t & (VT_BITFIELD|VT_ARRAY)) {
-        *psize = 8;
-        *palign = 8;
-        *reg_count = 1;
-        ret_t = ty->t;
-        mode = x86_64_mode_integer;
-    } else {
-        size = type_size(ty, &align);
-        *psize = (size + 7) & ~7;
-        *palign = (align + 7) & ~7;
-    
-        if (size > 16) {
-            mode = x86_64_mode_memory;
-        } else {
-            mode = classify_x86_64_inner(ty);
-            switch (mode) {
-            case x86_64_mode_integer:
-                if (size > 8) {
-                    *reg_count = 2;
-                    ret_t = VT_QLONG;
-                } else {
-                    *reg_count = 1;
-                    ret_t = (size > 4) ? VT_LLONG : VT_INT;
-                }
-                break;
-                
-            case x86_64_mode_x87:
-                *reg_count = 1;
-                ret_t = VT_LDOUBLE;
-                break;
-
-            case x86_64_mode_sse:
-                if (size > 8) {
-                    *reg_count = 2;
-                    ret_t = VT_QFLOAT;
-                } else {
-                    *reg_count = 1;
-                    ret_t = (size > 4) ? VT_DOUBLE : VT_FLOAT;
-                }
-                break;
-            default: break; /* nothing to be done for x86_64_mode_memory and x86_64_mode_none*/
-            }
-        }
-    }
-    
-    if (ret) {
-        ret->ref = NULL;
-        ret->t = ret_t;
-    }
-    
-    return mode;
-}
-
-ST_FUNC int classify_x86_64_va_arg(CType *ty)
-{
-    /* This definition must be synced with stdarg.h */
-    enum __va_arg_type {
-        __va_gen_reg, __va_float_reg, __va_stack
-    };
-    int size, align, reg_count;
-    X86_64_Mode mode = classify_x86_64_arg(ty, NULL, &size, &align, &reg_count);
-    switch (mode) {
-    default: return __va_stack;
-    case x86_64_mode_integer: return __va_gen_reg;
-    case x86_64_mode_sse: return __va_float_reg;
-    }
-}
-
-/* Return the number of registers needed to return the struct, or 0 if
-   returning via struct pointer. */
-ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize)
-{
-    int size, align, reg_count;
-    *ret_align = 1; // Never have to re-align return values for x86-64
-    *regsize = 8;
-    return (classify_x86_64_arg(vt, ret, &size, &align, &reg_count) != x86_64_mode_memory);
-}
-
-#define REGN 6
-static const uint8_t arg_regs[REGN] = {
-    TREG_RDI, TREG_RSI, TREG_RDX, TREG_RCX, TREG_R8, TREG_R9
-};
-
-static int arg_prepare_reg(int idx) {
-  if (idx == 2 || idx == 3)
-      /* idx=2: r10, idx=3: r11 */
-      return idx + 8;
-  else
-      return arg_regs[idx];
-}
-
-/* Generate function call. The function address is pushed first, then
-   all the parameters in call order. This functions pops all the
-   parameters and the function address. */
-void gfunc_call(int nb_args)
-{
-    X86_64_Mode mode;
-    CType type;
-    int size, align, r, args_size, stack_adjust, i, reg_count;
-    int nb_reg_args = 0;
-    int nb_sse_args = 0;
-    int sse_reg, gen_reg;
-    char _onstack[nb_args], *onstack = _onstack;
-
-    /* calculate the number of integer/float register arguments, remember
-       arguments to be passed via stack (in onstack[]), and also remember
-       if we have to align the stack pointer to 16 (onstack[i] == 2).  Needs
-       to be done in a left-to-right pass over arguments.  */
-    stack_adjust = 0;
-    for(i = nb_args - 1; i >= 0; i--) {
-        mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, &reg_count);
-        if (mode == x86_64_mode_sse && nb_sse_args + reg_count <= 8) {
-            nb_sse_args += reg_count;
-	    onstack[i] = 0;
-	} else if (mode == x86_64_mode_integer && nb_reg_args + reg_count <= REGN) {
-            nb_reg_args += reg_count;
-	    onstack[i] = 0;
-	} else if (mode == x86_64_mode_none) {
-	    onstack[i] = 0;
-	} else {
-	    if (align == 16 && (stack_adjust &= 15)) {
-		onstack[i] = 2;
-		stack_adjust = 0;
-	    } else
-	      onstack[i] = 1;
-	    stack_adjust += size;
-	}
-    }
-
-    if (nb_sse_args && tcc_state->nosse)
-      tcc_error("SSE disabled but floating point arguments passed");
-
-    /* fetch cpu flag before generating any code */
-    if (vtop >= vstack && (vtop->r & VT_VALMASK) == VT_CMP)
-      gv(RC_INT);
-
-    /* for struct arguments, we need to call memcpy and the function
-       call breaks register passing arguments we are preparing.
-       So, we process arguments which will be passed by stack first. */
-    gen_reg = nb_reg_args;
-    sse_reg = nb_sse_args;
-    args_size = 0;
-    stack_adjust &= 15;
-    for (i = 0; i < nb_args;) {
-	mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, &reg_count);
-	if (!onstack[i]) {
-	    ++i;
-	    continue;
-	}
-        /* Possibly adjust stack to align SSE boundary.  We're processing
-	   args from right to left while allocating happens left to right
-	   (stack grows down), so the adjustment needs to happen _after_
-	   an argument that requires it.  */
-        if (stack_adjust) {
-	    o(0x50); /* push %rax; aka sub $8,%rsp */
-            args_size += 8;
-	    stack_adjust = 0;
-        }
-	if (onstack[i] == 2)
-	  stack_adjust = 1;
-
-	vrotb(i+1);
-
-	switch (vtop->type.t & VT_BTYPE) {
-	    case VT_STRUCT:
-		/* allocate the necessary size on stack */
-		o(0x48);
-		oad(0xec81, size); /* sub $xxx, %rsp */
-		/* generate structure store */
-		r = get_reg(RC_INT);
-		orex(1, r, 0, 0x89); /* mov %rsp, r */
-		o(0xe0 + REG_VALUE(r));
-		vset(&vtop->type, r | VT_LVAL, 0);
-		vswap();
-		vstore();
-		break;
-
-	    case VT_LDOUBLE:
-                gv(RC_ST0);
-                oad(0xec8148, size); /* sub $xxx, %rsp */
-                o(0x7cdb); /* fstpt 0(%rsp) */
-                g(0x24);
-                g(0x00);
-		break;
-
-	    case VT_FLOAT:
-	    case VT_DOUBLE:
-		assert(mode == x86_64_mode_sse);
-		r = gv(RC_FLOAT);
-		o(0x50); /* push $rax */
-		/* movq %xmmN, (%rsp) */
-		o(0xd60f66);
-		o(0x04 + REG_VALUE(r)*8);
-		o(0x24);
-		break;
-
-	    default:
-		assert(mode == x86_64_mode_integer);
-		/* simple type */
-		/* XXX: implicit cast ? */
-		r = gv(RC_INT);
-		orex(0,r,0,0x50 + REG_VALUE(r)); /* push r */
-		break;
-	}
-	args_size += size;
-
-	vpop();
-	--nb_args;
-	onstack++;
-    }
-
-    /* XXX This should be superfluous.  */
-    save_regs(0); /* save used temporary registers */
-
-    /* then, we prepare register passing arguments.
-       Note that we cannot set RDX and RCX in this loop because gv()
-       may break these temporary registers. Let's use R10 and R11
-       instead of them */
-    assert(gen_reg <= REGN);
-    assert(sse_reg <= 8);
-    for(i = 0; i < nb_args; i++) {
-        mode = classify_x86_64_arg(&vtop->type, &type, &size, &align, &reg_count);
-        /* Alter stack entry type so that gv() knows how to treat it */
-        vtop->type = type;
-        if (mode == x86_64_mode_sse) {
-            if (reg_count == 2) {
-                sse_reg -= 2;
-                gv(RC_FRET); /* Use pair load into xmm0 & xmm1 */
-                if (sse_reg) { /* avoid redundant movaps %xmm0, %xmm0 */
-                    /* movaps %xmm0, %xmmN */
-                    o(0x280f);
-                    o(0xc0 + (sse_reg << 3));
-                    /* movaps %xmm1, %xmmN */
-                    o(0x280f);
-                    o(0xc1 + ((sse_reg+1) << 3));
-                }
-            } else {
-                assert(reg_count == 1);
-                --sse_reg;
-                /* Load directly to register */
-                gv(RC_XMM0 << sse_reg);
-            }
-        } else if (mode == x86_64_mode_integer) {
-            /* simple type */
-            /* XXX: implicit cast ? */
-            int d;
-            gen_reg -= reg_count;
-            r = gv(RC_INT);
-            d = arg_prepare_reg(gen_reg);
-            orex(1,d,r,0x89); /* mov */
-            o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d));
-            if (reg_count == 2) {
-                d = arg_prepare_reg(gen_reg+1);
-                orex(1,d,vtop->r2,0x89); /* mov */
-                o(0xc0 + REG_VALUE(vtop->r2) * 8 + REG_VALUE(d));
-            }
-        }
-        vtop--;
-    }
-    assert(gen_reg == 0);
-    assert(sse_reg == 0);
-
-    /* We shouldn't have many operands on the stack anymore, but the
-       call address itself is still there, and it might be in %eax
-       (or edx/ecx) currently, which the below writes would clobber.
-       So evict all remaining operands here.  */
-    save_regs(0);
-
-    /* Copy R10 and R11 into RDX and RCX, respectively */
-    if (nb_reg_args > 2) {
-        o(0xd2894c); /* mov %r10, %rdx */
-        if (nb_reg_args > 3) {
-            o(0xd9894c); /* mov %r11, %rcx */
-        }
-    }
-
-    if (vtop->type.ref->f.func_type != FUNC_NEW) /* implies FUNC_OLD or FUNC_ELLIPSIS */
-        oad(0xb8, nb_sse_args < 8 ? nb_sse_args : 8); /* mov nb_sse_args, %eax */
-    gcall_or_jmp(0);
-    if (args_size)
-        gadd_sp(args_size);
-    vtop--;
-}
-
-
-#define FUNC_PROLOG_SIZE 11
-
-static void push_arg_reg(int i) {
-    loc -= 8;
-    gen_modrm64(0x89, arg_regs[i], VT_LOCAL, NULL, loc);
-}
-
-/* generate function prolog of type 't' */
-void gfunc_prolog(CType *func_type)
-{
-    X86_64_Mode mode;
-    int i, addr, align, size, reg_count;
-    int param_addr = 0, reg_param_index, sse_param_index;
-    Sym *sym;
-    CType *type;
-
-    sym = func_type->ref;
-    addr = PTR_SIZE * 2;
-    loc = 0;
-    ind += FUNC_PROLOG_SIZE;
-    func_sub_sp_offset = ind;
-    func_ret_sub = 0;
-
-    if (sym->f.func_type == FUNC_ELLIPSIS) {
-        int seen_reg_num, seen_sse_num, seen_stack_size;
-        seen_reg_num = seen_sse_num = 0;
-        /* frame pointer and return address */
-        seen_stack_size = PTR_SIZE * 2;
-        /* count the number of seen parameters */
-        sym = func_type->ref;
-        while ((sym = sym->next) != NULL) {
-            type = &sym->type;
-            mode = classify_x86_64_arg(type, NULL, &size, &align, &reg_count);
-            switch (mode) {
-            default:
-            stack_arg:
-                seen_stack_size = ((seen_stack_size + align - 1) & -align) + size;
-                break;
-                
-            case x86_64_mode_integer:
-                if (seen_reg_num + reg_count > REGN)
-		    goto stack_arg;
-		seen_reg_num += reg_count;
-                break;
-                
-            case x86_64_mode_sse:
-                if (seen_sse_num + reg_count > 8)
-		    goto stack_arg;
-		seen_sse_num += reg_count;
-                break;
-            }
-        }
-
-        loc -= 16;
-        /* movl $0x????????, -0x10(%rbp) */
-        o(0xf045c7);
-        gen_le32(seen_reg_num * 8);
-        /* movl $0x????????, -0xc(%rbp) */
-        o(0xf445c7);
-        gen_le32(seen_sse_num * 16 + 48);
-        /* movl $0x????????, -0x8(%rbp) */
-        o(0xf845c7);
-        gen_le32(seen_stack_size);
-
-        /* save all register passing arguments */
-        for (i = 0; i < 8; i++) {
-            loc -= 16;
-	    if (!tcc_state->nosse) {
-		o(0xd60f66); /* movq */
-		gen_modrm(7 - i, VT_LOCAL, NULL, loc);
-	    }
-            /* movq $0, loc+8(%rbp) */
-            o(0x85c748);
-            gen_le32(loc + 8);
-            gen_le32(0);
-        }
-        for (i = 0; i < REGN; i++) {
-            push_arg_reg(REGN-1-i);
-        }
-    }
-
-    sym = func_type->ref;
-    reg_param_index = 0;
-    sse_param_index = 0;
-
-    /* if the function returns a structure, then add an
-       implicit pointer parameter */
-    func_vt = sym->type;
-    mode = classify_x86_64_arg(&func_vt, NULL, &size, &align, &reg_count);
-    if (mode == x86_64_mode_memory) {
-        push_arg_reg(reg_param_index);
-        func_vc = loc;
-        reg_param_index++;
-    }
-    /* define parameters */
-    while ((sym = sym->next) != NULL) {
-        type = &sym->type;
-        mode = classify_x86_64_arg(type, NULL, &size, &align, &reg_count);
-        switch (mode) {
-        case x86_64_mode_sse:
-	    if (tcc_state->nosse)
-	        tcc_error("SSE disabled but floating point arguments used");
-            if (sse_param_index + reg_count <= 8) {
-                /* save arguments passed by register */
-                loc -= reg_count * 8;
-                param_addr = loc;
-                for (i = 0; i < reg_count; ++i) {
-                    o(0xd60f66); /* movq */
-                    gen_modrm(sse_param_index, VT_LOCAL, NULL, param_addr + i*8);
-                    ++sse_param_index;
-                }
-            } else {
-                addr = (addr + align - 1) & -align;
-                param_addr = addr;
-                addr += size;
-            }
-            break;
-            
-        case x86_64_mode_memory:
-        case x86_64_mode_x87:
-            addr = (addr + align - 1) & -align;
-            param_addr = addr;
-            addr += size;
-            break;
-            
-        case x86_64_mode_integer: {
-            if (reg_param_index + reg_count <= REGN) {
-                /* save arguments passed by register */
-                loc -= reg_count * 8;
-                param_addr = loc;
-                for (i = 0; i < reg_count; ++i) {
-                    gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, param_addr + i*8);
-                    ++reg_param_index;
-                }
-            } else {
-                addr = (addr + align - 1) & -align;
-                param_addr = addr;
-                addr += size;
-            }
-            break;
-        }
-	default: break; /* nothing to be done for x86_64_mode_none */
-        }
-        sym_push(sym->v & ~SYM_FIELD, type,
-                 VT_LOCAL | VT_LVAL, param_addr);
-    }
-
-#ifdef CONFIG_TCC_BCHECK
-    /* leave some room for bound checking code */
-    if (tcc_state->do_bounds_check) {
-        func_bound_offset = lbounds_section->data_offset;
-        func_bound_ind = ind;
-        oad(0xb8, 0); /* lbound section pointer */
-	o(0xc78948);  /* mov  %rax,%rdi ## first arg in %rdi, this must be ptr */
-	oad(0xb8, 0); /* call to function */
-    }
-#endif
-}
-
-/* generate function epilog */
-void gfunc_epilog(void)
-{
-    int v, saved_ind;
-
-#ifdef CONFIG_TCC_BCHECK
-    if (tcc_state->do_bounds_check
-	&& func_bound_offset != lbounds_section->data_offset)
-    {
-        addr_t saved_ind;
-        addr_t *bounds_ptr;
-        Sym *sym_data;
-
-        /* add end of table info */
-        bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t));
-        *bounds_ptr = 0;
-
-        /* generate bound local allocation */
-        sym_data = get_sym_ref(&char_pointer_type, lbounds_section, 
-                               func_bound_offset, lbounds_section->data_offset);
-        saved_ind = ind;
-        ind = func_bound_ind;
-        greloca(cur_text_section, sym_data, ind + 1, R_X86_64_64, 0);
-        ind = ind + 5 + 3;
-        gen_static_call(TOK___bound_local_new);
-        ind = saved_ind;
-
-        /* generate bound check local freeing */
-        o(0x5250); /* save returned value, if any */
-        greloca(cur_text_section, sym_data, ind + 1, R_X86_64_64, 0);
-        oad(0xb8, 0); /* mov xxx, %rax */
-        o(0xc78948);  /* mov %rax,%rdi # first arg in %rdi, this must be ptr */
-        gen_static_call(TOK___bound_local_delete);
-        o(0x585a); /* restore returned value, if any */
-    }
-#endif
-    o(0xc9); /* leave */
-    if (func_ret_sub == 0) {
-        o(0xc3); /* ret */
-    } else {
-        o(0xc2); /* ret n */
-        g(func_ret_sub);
-        g(func_ret_sub >> 8);
-    }
-    /* align local size to word & save local variables */
-    v = (-loc + 15) & -16;
-    saved_ind = ind;
-    ind = func_sub_sp_offset - FUNC_PROLOG_SIZE;
-    o(0xe5894855);  /* push %rbp, mov %rsp, %rbp */
-    o(0xec8148);  /* sub rsp, stacksize */
-    gen_le32(v);
-    ind = saved_ind;
-}
-
-#endif /* not PE */
-
-/* generate a jump to a label */
-int gjmp(int t)
-{
-    return gjmp2(0xe9, t);
-}
-
-/* generate a jump to a fixed address */
-void gjmp_addr(int a)
-{
-    int r;
-    r = a - ind - 2;
-    if (r == (char)r) {
-        g(0xeb);
-        g(r);
-    } else {
-        oad(0xe9, a - ind - 5);
-    }
-}
-
-ST_FUNC void gtst_addr(int inv, int a)
-{
-    int v = vtop->r & VT_VALMASK;
-    if (v == VT_CMP) {
-	inv ^= (vtop--)->c.i;
-	a -= ind + 2;
-	if (a == (char)a) {
-	    g(inv - 32);
-	    g(a);
-	} else {
-	    g(0x0f);
-	    oad(inv - 16, a - 4);
-	}
-    } else if ((v & ~1) == VT_JMP) {
-	if ((v & 1) != inv) {
-	    gjmp_addr(a);
-	    gsym(vtop->c.i);
-	} else {
-	    gsym(vtop->c.i);
-	    o(0x05eb);
-	    gjmp_addr(a);
-	}
-	vtop--;
-    }
-}
-
-/* generate a test. set 'inv' to invert test. Stack entry is popped */
-ST_FUNC int gtst(int inv, int t)
-{
-    int v = vtop->r & VT_VALMASK;
-
-    if (nocode_wanted) {
-        ;
-    } else if (v == VT_CMP) {
-        /* fast case : can jump directly since flags are set */
-	if (vtop->c.i & 0x100)
-	  {
-	    /* This was a float compare.  If the parity flag is set
-	       the result was unordered.  For anything except != this
-	       means false and we don't jump (anding both conditions).
-	       For != this means true (oring both).
-	       Take care about inverting the test.  We need to jump
-	       to our target if the result was unordered and test wasn't NE,
-	       otherwise if unordered we don't want to jump.  */
-	    vtop->c.i &= ~0x100;
-            if (inv == (vtop->c.i == TOK_NE))
-	      o(0x067a);  /* jp +6 */
-	    else
-	      {
-	        g(0x0f);
-		t = gjmp2(0x8a, t); /* jp t */
-	      }
-	  }
-        g(0x0f);
-        t = gjmp2((vtop->c.i - 16) ^ inv, t);
-    } else if (v == VT_JMP || v == VT_JMPI) {
-        /* && or || optimization */
-        if ((v & 1) == inv) {
-            /* insert vtop->c jump list in t */
-            uint32_t n1, n = vtop->c.i;
-            if (n) {
-                while ((n1 = read32le(cur_text_section->data + n)))
-                    n = n1;
-                write32le(cur_text_section->data + n, t);
-                t = vtop->c.i;
-            }
-        } else {
-            t = gjmp(t);
-            gsym(vtop->c.i);
-        }
-    }
-    vtop--;
-    return t;
-}
-
-/* generate an integer binary operation */
-void gen_opi(int op)
-{
-    int r, fr, opc, c;
-    int ll, uu, cc;
-
-    ll = is64_type(vtop[-1].type.t);
-    uu = (vtop[-1].type.t & VT_UNSIGNED) != 0;
-    cc = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
-
-    switch(op) {
-    case '+':
-    case TOK_ADDC1: /* add with carry generation */
-        opc = 0;
-    gen_op8:
-        if (cc && (!ll || (int)vtop->c.i == vtop->c.i)) {
-            /* constant case */
-            vswap();
-            r = gv(RC_INT);
-            vswap();
-            c = vtop->c.i;
-            if (c == (char)c) {
-                /* XXX: generate inc and dec for smaller code ? */
-                orex(ll, r, 0, 0x83);
-                o(0xc0 | (opc << 3) | REG_VALUE(r));
-                g(c);
-            } else {
-                orex(ll, r, 0, 0x81);
-                oad(0xc0 | (opc << 3) | REG_VALUE(r), c);
-            }
-        } else {
-            gv2(RC_INT, RC_INT);
-            r = vtop[-1].r;
-            fr = vtop[0].r;
-            orex(ll, r, fr, (opc << 3) | 0x01);
-            o(0xc0 + REG_VALUE(r) + REG_VALUE(fr) * 8);
-        }
-        vtop--;
-        if (op >= TOK_ULT && op <= TOK_GT) {
-            vtop->r = VT_CMP;
-            vtop->c.i = op;
-        }
-        break;
-    case '-':
-    case TOK_SUBC1: /* sub with carry generation */
-        opc = 5;
-        goto gen_op8;
-    case TOK_ADDC2: /* add with carry use */
-        opc = 2;
-        goto gen_op8;
-    case TOK_SUBC2: /* sub with carry use */
-        opc = 3;
-        goto gen_op8;
-    case '&':
-        opc = 4;
-        goto gen_op8;
-    case '^':
-        opc = 6;
-        goto gen_op8;
-    case '|':
-        opc = 1;
-        goto gen_op8;
-    case '*':
-        gv2(RC_INT, RC_INT);
-        r = vtop[-1].r;
-        fr = vtop[0].r;
-        orex(ll, fr, r, 0xaf0f); /* imul fr, r */
-        o(0xc0 + REG_VALUE(fr) + REG_VALUE(r) * 8);
-        vtop--;
-        break;
-    case TOK_SHL:
-        opc = 4;
-        goto gen_shift;
-    case TOK_SHR:
-        opc = 5;
-        goto gen_shift;
-    case TOK_SAR:
-        opc = 7;
-    gen_shift:
-        opc = 0xc0 | (opc << 3);
-        if (cc) {
-            /* constant case */
-            vswap();
-            r = gv(RC_INT);
-            vswap();
-            orex(ll, r, 0, 0xc1); /* shl/shr/sar $xxx, r */
-            o(opc | REG_VALUE(r));
-            g(vtop->c.i & (ll ? 63 : 31));
-        } else {
-            /* we generate the shift in ecx */
-            gv2(RC_INT, RC_RCX);
-            r = vtop[-1].r;
-            orex(ll, r, 0, 0xd3); /* shl/shr/sar %cl, r */
-            o(opc | REG_VALUE(r));
-        }
-        vtop--;
-        break;
-    case TOK_UDIV:
-    case TOK_UMOD:
-        uu = 1;
-        goto divmod;
-    case '/':
-    case '%':
-    case TOK_PDIV:
-        uu = 0;
-    divmod:
-        /* first operand must be in eax */
-        /* XXX: need better constraint for second operand */
-        gv2(RC_RAX, RC_RCX);
-        r = vtop[-1].r;
-        fr = vtop[0].r;
-        vtop--;
-        save_reg(TREG_RDX);
-        orex(ll, 0, 0, uu ? 0xd231 : 0x99); /* xor %edx,%edx : cqto */
-        orex(ll, fr, 0, 0xf7); /* div fr, %eax */
-        o((uu ? 0xf0 : 0xf8) + REG_VALUE(fr));
-        if (op == '%' || op == TOK_UMOD)
-            r = TREG_RDX;
-        else
-            r = TREG_RAX;
-        vtop->r = r;
-        break;
-    default:
-        opc = 7;
-        goto gen_op8;
-    }
-}
-
-void gen_opl(int op)
-{
-    gen_opi(op);
-}
-
-/* generate a floating point operation 'v = t1 op t2' instruction. The
-   two operands are guaranteed to have the same floating point type */
-/* XXX: need to use ST1 too */
-void gen_opf(int op)
-{
-    int a, ft, fc, swapped, r;
-    int float_type =
-        (vtop->type.t & VT_BTYPE) == VT_LDOUBLE ? RC_ST0 : RC_FLOAT;
-
-    /* convert constants to memory references */
-    if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
-        vswap();
-        gv(float_type);
-        vswap();
-    }
-    if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST)
-        gv(float_type);
-
-    /* must put at least one value in the floating point register */
-    if ((vtop[-1].r & VT_LVAL) &&
-        (vtop[0].r & VT_LVAL)) {
-        vswap();
-        gv(float_type);
-        vswap();
-    }
-    swapped = 0;
-    /* swap the stack if needed so that t1 is the register and t2 is
-       the memory reference */
-    if (vtop[-1].r & VT_LVAL) {
-        vswap();
-        swapped = 1;
-    }
-    if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
-        if (op >= TOK_ULT && op <= TOK_GT) {
-            /* load on stack second operand */
-            load(TREG_ST0, vtop);
-            save_reg(TREG_RAX); /* eax is used by FP comparison code */
-            if (op == TOK_GE || op == TOK_GT)
-                swapped = !swapped;
-            else if (op == TOK_EQ || op == TOK_NE)
-                swapped = 0;
-            if (swapped)
-                o(0xc9d9); /* fxch %st(1) */
-            if (op == TOK_EQ || op == TOK_NE)
-                o(0xe9da); /* fucompp */
-            else
-                o(0xd9de); /* fcompp */
-            o(0xe0df); /* fnstsw %ax */
-            if (op == TOK_EQ) {
-                o(0x45e480); /* and $0x45, %ah */
-                o(0x40fC80); /* cmp $0x40, %ah */
-            } else if (op == TOK_NE) {
-                o(0x45e480); /* and $0x45, %ah */
-                o(0x40f480); /* xor $0x40, %ah */
-                op = TOK_NE;
-            } else if (op == TOK_GE || op == TOK_LE) {
-                o(0x05c4f6); /* test $0x05, %ah */
-                op = TOK_EQ;
-            } else {
-                o(0x45c4f6); /* test $0x45, %ah */
-                op = TOK_EQ;
-            }
-            vtop--;
-            vtop->r = VT_CMP;
-            vtop->c.i = op;
-        } else {
-            /* no memory reference possible for long double operations */
-            load(TREG_ST0, vtop);
-            swapped = !swapped;
-
-            switch(op) {
-            default:
-            case '+':
-                a = 0;
-                break;
-            case '-':
-                a = 4;
-                if (swapped)
-                    a++;
-                break;
-            case '*':
-                a = 1;
-                break;
-            case '/':
-                a = 6;
-                if (swapped)
-                    a++;
-                break;
-            }
-            ft = vtop->type.t;
-            fc = vtop->c.i;
-            o(0xde); /* fxxxp %st, %st(1) */
-            o(0xc1 + (a << 3));
-            vtop--;
-        }
-    } else {
-        if (op >= TOK_ULT && op <= TOK_GT) {
-            /* if saved lvalue, then we must reload it */
-            r = vtop->r;
-            fc = vtop->c.i;
-            if ((r & VT_VALMASK) == VT_LLOCAL) {
-                SValue v1;
-                r = get_reg(RC_INT);
-                v1.type.t = VT_PTR;
-                v1.r = VT_LOCAL | VT_LVAL;
-                v1.c.i = fc;
-                load(r, &v1);
-                fc = 0;
-            }
-
-            if (op == TOK_EQ || op == TOK_NE) {
-                swapped = 0;
-            } else {
-                if (op == TOK_LE || op == TOK_LT)
-                    swapped = !swapped;
-                if (op == TOK_LE || op == TOK_GE) {
-                    op = 0x93; /* setae */
-                } else {
-                    op = 0x97; /* seta */
-                }
-            }
-
-            if (swapped) {
-                gv(RC_FLOAT);
-                vswap();
-            }
-            assert(!(vtop[-1].r & VT_LVAL));
-            
-            if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE)
-                o(0x66);
-            if (op == TOK_EQ || op == TOK_NE)
-                o(0x2e0f); /* ucomisd */
-            else
-                o(0x2f0f); /* comisd */
-
-            if (vtop->r & VT_LVAL) {
-                gen_modrm(vtop[-1].r, r, vtop->sym, fc);
-            } else {
-                o(0xc0 + REG_VALUE(vtop[0].r) + REG_VALUE(vtop[-1].r)*8);
-            }
-
-            vtop--;
-            vtop->r = VT_CMP;
-            vtop->c.i = op | 0x100;
-        } else {
-            assert((vtop->type.t & VT_BTYPE) != VT_LDOUBLE);
-            switch(op) {
-            default:
-            case '+':
-                a = 0;
-                break;
-            case '-':
-                a = 4;
-                break;
-            case '*':
-                a = 1;
-                break;
-            case '/':
-                a = 6;
-                break;
-            }
-            ft = vtop->type.t;
-            fc = vtop->c.i;
-            assert((ft & VT_BTYPE) != VT_LDOUBLE);
-            
-            r = vtop->r;
-            /* if saved lvalue, then we must reload it */
-            if ((vtop->r & VT_VALMASK) == VT_LLOCAL) {
-                SValue v1;
-                r = get_reg(RC_INT);
-                v1.type.t = VT_PTR;
-                v1.r = VT_LOCAL | VT_LVAL;
-                v1.c.i = fc;
-                load(r, &v1);
-                fc = 0;
-            }
-            
-            assert(!(vtop[-1].r & VT_LVAL));
-            if (swapped) {
-                assert(vtop->r & VT_LVAL);
-                gv(RC_FLOAT);
-                vswap();
-            }
-            
-            if ((ft & VT_BTYPE) == VT_DOUBLE) {
-                o(0xf2);
-            } else {
-                o(0xf3);
-            }
-            o(0x0f);
-            o(0x58 + a);
-            
-            if (vtop->r & VT_LVAL) {
-                gen_modrm(vtop[-1].r, r, vtop->sym, fc);
-            } else {
-                o(0xc0 + REG_VALUE(vtop[0].r) + REG_VALUE(vtop[-1].r)*8);
-            }
-
-            vtop--;
-        }
-    }
-}
-
-/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
-   and 'long long' cases. */
-void gen_cvt_itof(int t)
-{
-    if ((t & VT_BTYPE) == VT_LDOUBLE) {
-        save_reg(TREG_ST0);
-        gv(RC_INT);
-        if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
-            /* signed long long to float/double/long double (unsigned case
-               is handled generically) */
-            o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
-            o(0x242cdf); /* fildll (%rsp) */
-            o(0x08c48348); /* add $8, %rsp */
-        } else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) ==
-                   (VT_INT | VT_UNSIGNED)) {
-            /* unsigned int to float/double/long double */
-            o(0x6a); /* push $0 */
-            g(0x00);
-            o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
-            o(0x242cdf); /* fildll (%rsp) */
-            o(0x10c48348); /* add $16, %rsp */
-        } else {
-            /* int to float/double/long double */
-            o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
-            o(0x2404db); /* fildl (%rsp) */
-            o(0x08c48348); /* add $8, %rsp */
-        }
-        vtop->r = TREG_ST0;
-    } else {
-        int r = get_reg(RC_FLOAT);
-        gv(RC_INT);
-        o(0xf2 + ((t & VT_BTYPE) == VT_FLOAT?1:0));
-        if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) ==
-            (VT_INT | VT_UNSIGNED) ||
-            (vtop->type.t & VT_BTYPE) == VT_LLONG) {
-            o(0x48); /* REX */
-        }
-        o(0x2a0f);
-        o(0xc0 + (vtop->r & VT_VALMASK) + REG_VALUE(r)*8); /* cvtsi2sd */
-        vtop->r = r;
-    }
-}
-
-/* convert from one floating point type to another */
-void gen_cvt_ftof(int t)
-{
-    int ft, bt, tbt;
-
-    ft = vtop->type.t;
-    bt = ft & VT_BTYPE;
-    tbt = t & VT_BTYPE;
-    
-    if (bt == VT_FLOAT) {
-        gv(RC_FLOAT);
-        if (tbt == VT_DOUBLE) {
-            o(0x140f); /* unpcklps */
-            o(0xc0 + REG_VALUE(vtop->r)*9);
-            o(0x5a0f); /* cvtps2pd */
-            o(0xc0 + REG_VALUE(vtop->r)*9);
-        } else if (tbt == VT_LDOUBLE) {
-            save_reg(RC_ST0);
-            /* movss %xmm0,-0x10(%rsp) */
-            o(0x110ff3);
-            o(0x44 + REG_VALUE(vtop->r)*8);
-            o(0xf024);
-            o(0xf02444d9); /* flds -0x10(%rsp) */
-            vtop->r = TREG_ST0;
-        }
-    } else if (bt == VT_DOUBLE) {
-        gv(RC_FLOAT);
-        if (tbt == VT_FLOAT) {
-            o(0x140f66); /* unpcklpd */
-            o(0xc0 + REG_VALUE(vtop->r)*9);
-            o(0x5a0f66); /* cvtpd2ps */
-            o(0xc0 + REG_VALUE(vtop->r)*9);
-        } else if (tbt == VT_LDOUBLE) {
-            save_reg(RC_ST0);
-            /* movsd %xmm0,-0x10(%rsp) */
-            o(0x110ff2);
-            o(0x44 + REG_VALUE(vtop->r)*8);
-            o(0xf024);
-            o(0xf02444dd); /* fldl -0x10(%rsp) */
-            vtop->r = TREG_ST0;
-        }
-    } else {
-        int r;
-        gv(RC_ST0);
-        r = get_reg(RC_FLOAT);
-        if (tbt == VT_DOUBLE) {
-            o(0xf0245cdd); /* fstpl -0x10(%rsp) */
-            /* movsd -0x10(%rsp),%xmm0 */
-            o(0x100ff2);
-            o(0x44 + REG_VALUE(r)*8);
-            o(0xf024);
-            vtop->r = r;
-        } else if (tbt == VT_FLOAT) {
-            o(0xf0245cd9); /* fstps -0x10(%rsp) */
-            /* movss -0x10(%rsp),%xmm0 */
-            o(0x100ff3);
-            o(0x44 + REG_VALUE(r)*8);
-            o(0xf024);
-            vtop->r = r;
-        }
-    }
-}
-
-/* convert fp to int 't' type */
-void gen_cvt_ftoi(int t)
-{
-    int ft, bt, size, r;
-    ft = vtop->type.t;
-    bt = ft & VT_BTYPE;
-    if (bt == VT_LDOUBLE) {
-        gen_cvt_ftof(VT_DOUBLE);
-        bt = VT_DOUBLE;
-    }
-
-    gv(RC_FLOAT);
-    if (t != VT_INT)
-        size = 8;
-    else
-        size = 4;
-
-    r = get_reg(RC_INT);
-    if (bt == VT_FLOAT) {
-        o(0xf3);
-    } else if (bt == VT_DOUBLE) {
-        o(0xf2);
-    } else {
-        assert(0);
-    }
-    orex(size == 8, r, 0, 0x2c0f); /* cvttss2si or cvttsd2si */
-    o(0xc0 + REG_VALUE(vtop->r) + REG_VALUE(r)*8);
-    vtop->r = r;
-}
-
-/* computed goto support */
-void ggoto(void)
-{
-    gcall_or_jmp(1);
-    vtop--;
-}
-
-/* Save the stack pointer onto the stack and return the location of its address */
-ST_FUNC void gen_vla_sp_save(int addr) {
-    /* mov %rsp,addr(%rbp)*/
-    gen_modrm64(0x89, TREG_RSP, VT_LOCAL, NULL, addr);
-}
-
-/* Restore the SP from a location on the stack */
-ST_FUNC void gen_vla_sp_restore(int addr) {
-    gen_modrm64(0x8b, TREG_RSP, VT_LOCAL, NULL, addr);
-}
-
-/* Subtract from the stack pointer, and push the resulting value onto the stack */
-ST_FUNC void gen_vla_alloc(CType *type, int align) {
-#ifdef TCC_TARGET_PE
-    /* alloca does more than just adjust %rsp on Windows */
-    vpush_global_sym(&func_old_type, TOK_alloca);
-    vswap(); /* Move alloca ref past allocation size */
-    gfunc_call(1);
-#else
-    int r;
-    r = gv(RC_INT); /* allocation size */
-    /* sub r,%rsp */
-    o(0x2b48);
-    o(0xe0 | REG_VALUE(r));
-    /* We align to 16 bytes rather than align */
-    /* and ~15, %rsp */
-    o(0xf0e48348);
-    vpop();
-#endif
-}
-
-
-/* end of x86-64 code generator */
-/*************************************************************/
-#endif /* ! TARGET_DEFS_ONLY */
-/******************************************************/
diff --git a/tinyc/x86_64-link.c b/tinyc/x86_64-link.c
deleted file mode 100644
index c0c5e13bf..000000000
--- a/tinyc/x86_64-link.c
+++ /dev/null
@@ -1,295 +0,0 @@
-#ifdef TARGET_DEFS_ONLY
-
-#define EM_TCC_TARGET EM_X86_64
-
-/* relocation type for 32 bit data relocation */
-#define R_DATA_32   R_X86_64_32S
-#define R_DATA_PTR  R_X86_64_64
-#define R_JMP_SLOT  R_X86_64_JUMP_SLOT
-#define R_GLOB_DAT  R_X86_64_GLOB_DAT
-#define R_COPY      R_X86_64_COPY
-#define R_RELATIVE  R_X86_64_RELATIVE
-
-#define R_NUM       R_X86_64_NUM
-
-#define ELF_START_ADDR 0x400000
-#define ELF_PAGE_SIZE  0x200000
-
-#define PCRELATIVE_DLLPLT 1
-#define RELOCATE_DLLPLT 1
-
-#else /* !TARGET_DEFS_ONLY */
-
-#include "tcc.h"
-
-/* Returns 1 for a code relocation, 0 for a data relocation. For unknown
-   relocations, returns -1. */
-int code_reloc (int reloc_type)
-{
-    switch (reloc_type) {
-        case R_X86_64_32:
-        case R_X86_64_32S:
-        case R_X86_64_64:
-        case R_X86_64_GOTPC32:
-        case R_X86_64_GOTPC64:
-        case R_X86_64_GOTPCREL:
-        case R_X86_64_GOTPCRELX:
-        case R_X86_64_REX_GOTPCRELX:
-        case R_X86_64_GOTTPOFF:
-        case R_X86_64_GOT32:
-        case R_X86_64_GOT64:
-        case R_X86_64_GLOB_DAT:
-        case R_X86_64_COPY:
-        case R_X86_64_RELATIVE:
-        case R_X86_64_GOTOFF64:
-            return 0;
-
-        case R_X86_64_PC32:
-        case R_X86_64_PC64:
-        case R_X86_64_PLT32:
-        case R_X86_64_PLTOFF64:
-        case R_X86_64_JUMP_SLOT:
-            return 1;
-    }
-
-    tcc_error ("Unknown relocation type: %d", reloc_type);
-    return -1;
-}
-
-/* Returns an enumerator to describe whether and when the relocation needs a
-   GOT and/or PLT entry to be created. See tcc.h for a description of the
-   different values. */
-int gotplt_entry_type (int reloc_type)
-{
-    switch (reloc_type) {
-        case R_X86_64_GLOB_DAT:
-        case R_X86_64_JUMP_SLOT:
-        case R_X86_64_COPY:
-        case R_X86_64_RELATIVE:
-            return NO_GOTPLT_ENTRY;
-
-	/* The following relocs wouldn't normally need GOT or PLT
-	   slots, but we need them for simplicity in the link
-	   editor part.  See our caller for comments.  */
-        case R_X86_64_32:
-        case R_X86_64_32S:
-        case R_X86_64_64:
-        case R_X86_64_PC32:
-        case R_X86_64_PC64:
-            return AUTO_GOTPLT_ENTRY;
-
-        case R_X86_64_GOTTPOFF:
-            return BUILD_GOT_ONLY;
-
-        case R_X86_64_GOT32:
-        case R_X86_64_GOT64:
-        case R_X86_64_GOTPC32:
-        case R_X86_64_GOTPC64:
-        case R_X86_64_GOTOFF64:
-        case R_X86_64_GOTPCREL:
-        case R_X86_64_GOTPCRELX:
-        case R_X86_64_REX_GOTPCRELX:
-        case R_X86_64_PLT32:
-        case R_X86_64_PLTOFF64:
-            return ALWAYS_GOTPLT_ENTRY;
-    }
-
-    tcc_error ("Unknown relocation type: %d", reloc_type);
-    return -1;
-}
-
-ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr)
-{
-    Section *plt = s1->plt;
-    uint8_t *p;
-    int modrm;
-    unsigned plt_offset, relofs;
-
-    modrm = 0x25;
-
-    /* empty PLT: create PLT0 entry that pushes the library identifier
-       (GOT + PTR_SIZE) and jumps to ld.so resolution routine
-       (GOT + 2 * PTR_SIZE) */
-    if (plt->data_offset == 0) {
-        p = section_ptr_add(plt, 16);
-        p[0] = 0xff; /* pushl got + PTR_SIZE */
-        p[1] = modrm + 0x10;
-        write32le(p + 2, PTR_SIZE);
-        p[6] = 0xff; /* jmp *(got + PTR_SIZE * 2) */
-        p[7] = modrm;
-        write32le(p + 8, PTR_SIZE * 2);
-    }
-    plt_offset = plt->data_offset;
-
-    /* The PLT slot refers to the relocation entry it needs via offset.
-       The reloc entry is created below, so its offset is the current
-       data_offset */
-    relofs = s1->got->reloc ? s1->got->reloc->data_offset : 0;
-
-    /* Jump to GOT entry where ld.so initially put the address of ip + 4 */
-    p = section_ptr_add(plt, 16);
-    p[0] = 0xff; /* jmp *(got + x) */
-    p[1] = modrm;
-    write32le(p + 2, got_offset);
-    p[6] = 0x68; /* push $xxx */
-    /* On x86-64, the relocation is referred to by _index_ */
-    write32le(p + 7, relofs / sizeof (ElfW_Rel));
-    p[11] = 0xe9; /* jmp plt_start */
-    write32le(p + 12, -(plt->data_offset));
-    return plt_offset;
-}
-
-/* relocate the PLT: compute addresses and offsets in the PLT now that final
-   address for PLT and GOT are known (see fill_program_header) */
-ST_FUNC void relocate_plt(TCCState *s1)
-{
-    uint8_t *p, *p_end;
-
-    if (!s1->plt)
-      return;
-
-    p = s1->plt->data;
-    p_end = p + s1->plt->data_offset;
-
-    if (p < p_end) {
-        int x = s1->got->sh_addr - s1->plt->sh_addr - 6;
-        add32le(p + 2, x);
-        add32le(p + 8, x - 6);
-        p += 16;
-        while (p < p_end) {
-            add32le(p + 2, x + s1->plt->data - p);
-            p += 16;
-        }
-    }
-}
-
-static ElfW_Rel *qrel; /* ptr to next reloc entry reused */
-
-void relocate_init(Section *sr)
-{
-    qrel = (ElfW_Rel *) sr->data;
-}
-
-void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val)
-{
-    int sym_index, esym_index;
-
-    sym_index = ELFW(R_SYM)(rel->r_info);
-
-    switch (type) {
-        case R_X86_64_64:
-            if (s1->output_type == TCC_OUTPUT_DLL) {
-                esym_index = s1->sym_attrs[sym_index].dyn_index;
-                qrel->r_offset = rel->r_offset;
-                if (esym_index) {
-                    qrel->r_info = ELFW(R_INFO)(esym_index, R_X86_64_64);
-                    qrel->r_addend = rel->r_addend;
-                    qrel++;
-                    break;
-                } else {
-                    qrel->r_info = ELFW(R_INFO)(0, R_X86_64_RELATIVE);
-                    qrel->r_addend = read64le(ptr) + val;
-                    qrel++;
-                }
-            }
-            add64le(ptr, val);
-            break;
-        case R_X86_64_32:
-        case R_X86_64_32S:
-            if (s1->output_type == TCC_OUTPUT_DLL) {
-                /* XXX: this logic may depend on TCC's codegen
-                   now TCC uses R_X86_64_32 even for a 64bit pointer */
-                qrel->r_info = ELFW(R_INFO)(0, R_X86_64_RELATIVE);
-                /* Use sign extension! */
-                qrel->r_addend = (int)read32le(ptr) + val;
-                qrel++;
-            }
-            add32le(ptr, val);
-            break;
-
-        case R_X86_64_PC32:
-            if (s1->output_type == TCC_OUTPUT_DLL) {
-                /* DLL relocation */
-                esym_index = s1->sym_attrs[sym_index].dyn_index;
-                if (esym_index) {
-                    qrel->r_offset = rel->r_offset;
-                    qrel->r_info = ELFW(R_INFO)(esym_index, R_X86_64_PC32);
-                    /* Use sign extension! */
-                    qrel->r_addend = (int)read32le(ptr) + rel->r_addend;
-                    qrel++;
-                    break;
-                }
-            }
-            goto plt32pc32;
-
-        case R_X86_64_PLT32:
-            /* fallthrough: val already holds the PLT slot address */
-
-        plt32pc32:
-        {
-            long long diff;
-            diff = (long long)val - addr;
-            if (diff < -2147483648LL || diff > 2147483647LL) {
-                tcc_error("internal error: relocation failed");
-            }
-            add32le(ptr, diff);
-        }
-            break;
-
-        case R_X86_64_PLTOFF64:
-            add64le(ptr, val - s1->got->sh_addr + rel->r_addend);
-            break;
-
-        case R_X86_64_PC64:
-            if (s1->output_type == TCC_OUTPUT_DLL) {
-                /* DLL relocation */
-                esym_index = s1->sym_attrs[sym_index].dyn_index;
-                if (esym_index) {
-                    qrel->r_offset = rel->r_offset;
-                    qrel->r_info = ELFW(R_INFO)(esym_index, R_X86_64_PC64);
-                    qrel->r_addend = read64le(ptr) + rel->r_addend;
-                    qrel++;
-                    break;
-                }
-            }
-            add64le(ptr, val - addr);
-            break;
-
-        case R_X86_64_GLOB_DAT:
-        case R_X86_64_JUMP_SLOT:
-            /* They don't need addend */
-            write64le(ptr, val - rel->r_addend);
-            break;
-        case R_X86_64_GOTPCREL:
-        case R_X86_64_GOTPCRELX:
-        case R_X86_64_REX_GOTPCRELX:
-            add32le(ptr, s1->got->sh_addr - addr +
-                         s1->sym_attrs[sym_index].got_offset - 4);
-            break;
-        case R_X86_64_GOTPC32:
-            add32le(ptr, s1->got->sh_addr - addr + rel->r_addend);
-            break;
-        case R_X86_64_GOTPC64:
-            add64le(ptr, s1->got->sh_addr - addr + rel->r_addend);
-            break;
-        case R_X86_64_GOTTPOFF:
-            add32le(ptr, val - s1->got->sh_addr);
-            break;
-        case R_X86_64_GOT32:
-            /* we load the got offset */
-            add32le(ptr, s1->sym_attrs[sym_index].got_offset);
-            break;
-        case R_X86_64_GOT64:
-            /* we load the got offset */
-            add64le(ptr, s1->sym_attrs[sym_index].got_offset);
-            break;
-        case R_X86_64_GOTOFF64:
-            add64le(ptr, val - s1->got->sh_addr);
-            break;
-        case R_X86_64_RELATIVE:
-            /* do nothing */
-            break;
-    }
-}
-
-#endif /* !TARGET_DEFS_ONLY */
diff --git a/tools/UnicodeData.txt b/tools/UnicodeData.txt
new file mode 100644
index 000000000..d88a60135
--- /dev/null
+++ b/tools/UnicodeData.txt
@@ -0,0 +1,32840 @@
+0000;<control>;Cc;0;BN;;;;;N;NULL;;;;
+0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;;
+0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;;
+0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;;
+0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;;
+0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;;
+0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;;
+0007;<control>;Cc;0;BN;;;;;N;BELL;;;;
+0008;<control>;Cc;0;BN;;;;;N;BACKSPACE;;;;
+0009;<control>;Cc;0;S;;;;;N;CHARACTER TABULATION;;;;
+000A;<control>;Cc;0;B;;;;;N;LINE FEED (LF);;;;
+000B;<control>;Cc;0;S;;;;;N;LINE TABULATION;;;;
+000C;<control>;Cc;0;WS;;;;;N;FORM FEED (FF);;;;
+000D;<control>;Cc;0;B;;;;;N;CARRIAGE RETURN (CR);;;;
+000E;<control>;Cc;0;BN;;;;;N;SHIFT OUT;;;;
+000F;<control>;Cc;0;BN;;;;;N;SHIFT IN;;;;
+0010;<control>;Cc;0;BN;;;;;N;DATA LINK ESCAPE;;;;
+0011;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL ONE;;;;
+0012;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL TWO;;;;
+0013;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL THREE;;;;
+0014;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL FOUR;;;;
+0015;<control>;Cc;0;BN;;;;;N;NEGATIVE ACKNOWLEDGE;;;;
+0016;<control>;Cc;0;BN;;;;;N;SYNCHRONOUS IDLE;;;;
+0017;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION BLOCK;;;;
+0018;<control>;Cc;0;BN;;;;;N;CANCEL;;;;
+0019;<control>;Cc;0;BN;;;;;N;END OF MEDIUM;;;;
+001A;<control>;Cc;0;BN;;;;;N;SUBSTITUTE;;;;
+001B;<control>;Cc;0;BN;;;;;N;ESCAPE;;;;
+001C;<control>;Cc;0;B;;;;;N;INFORMATION SEPARATOR FOUR;;;;
+001D;<control>;Cc;0;B;;;;;N;INFORMATION SEPARATOR THREE;;;;
+001E;<control>;Cc;0;B;;;;;N;INFORMATION SEPARATOR TWO;;;;
+001F;<control>;Cc;0;S;;;;;N;INFORMATION SEPARATOR ONE;;;;
+0020;SPACE;Zs;0;WS;;;;;N;;;;;
+0021;EXCLAMATION MARK;Po;0;ON;;;;;N;;;;;
+0022;QUOTATION MARK;Po;0;ON;;;;;N;;;;;
+0023;NUMBER SIGN;Po;0;ET;;;;;N;;;;;
+0024;DOLLAR SIGN;Sc;0;ET;;;;;N;;;;;
+0025;PERCENT SIGN;Po;0;ET;;;;;N;;;;;
+0026;AMPERSAND;Po;0;ON;;;;;N;;;;;
+0027;APOSTROPHE;Po;0;ON;;;;;N;APOSTROPHE-QUOTE;;;;
+0028;LEFT PARENTHESIS;Ps;0;ON;;;;;Y;OPENING PARENTHESIS;;;;
+0029;RIGHT PARENTHESIS;Pe;0;ON;;;;;Y;CLOSING PARENTHESIS;;;;
+002A;ASTERISK;Po;0;ON;;;;;N;;;;;
+002B;PLUS SIGN;Sm;0;ES;;;;;N;;;;;
+002C;COMMA;Po;0;CS;;;;;N;;;;;
+002D;HYPHEN-MINUS;Pd;0;ES;;;;;N;;;;;
+002E;FULL STOP;Po;0;CS;;;;;N;PERIOD;;;;
+002F;SOLIDUS;Po;0;CS;;;;;N;SLASH;;;;
+0030;DIGIT ZERO;Nd;0;EN;;0;0;0;N;;;;;
+0031;DIGIT ONE;Nd;0;EN;;1;1;1;N;;;;;
+0032;DIGIT TWO;Nd;0;EN;;2;2;2;N;;;;;
+0033;DIGIT THREE;Nd;0;EN;;3;3;3;N;;;;;
+0034;DIGIT FOUR;Nd;0;EN;;4;4;4;N;;;;;
+0035;DIGIT FIVE;Nd;0;EN;;5;5;5;N;;;;;
+0036;DIGIT SIX;Nd;0;EN;;6;6;6;N;;;;;
+0037;DIGIT SEVEN;Nd;0;EN;;7;7;7;N;;;;;
+0038;DIGIT EIGHT;Nd;0;EN;;8;8;8;N;;;;;
+0039;DIGIT NINE;Nd;0;EN;;9;9;9;N;;;;;
+003A;COLON;Po;0;CS;;;;;N;;;;;
+003B;SEMICOLON;Po;0;ON;;;;;N;;;;;
+003C;LESS-THAN SIGN;Sm;0;ON;;;;;Y;;;;;
+003D;EQUALS SIGN;Sm;0;ON;;;;;N;;;;;
+003E;GREATER-THAN SIGN;Sm;0;ON;;;;;Y;;;;;
+003F;QUESTION MARK;Po;0;ON;;;;;N;;;;;
+0040;COMMERCIAL AT;Po;0;ON;;;;;N;;;;;
+0041;LATIN CAPITAL LETTER A;Lu;0;L;;;;;N;;;;0061;
+0042;LATIN CAPITAL LETTER B;Lu;0;L;;;;;N;;;;0062;
+0043;LATIN CAPITAL LETTER C;Lu;0;L;;;;;N;;;;0063;
+0044;LATIN CAPITAL LETTER D;Lu;0;L;;;;;N;;;;0064;
+0045;LATIN CAPITAL LETTER E;Lu;0;L;;;;;N;;;;0065;
+0046;LATIN CAPITAL LETTER F;Lu;0;L;;;;;N;;;;0066;
+0047;LATIN CAPITAL LETTER G;Lu;0;L;;;;;N;;;;0067;
+0048;LATIN CAPITAL LETTER H;Lu;0;L;;;;;N;;;;0068;
+0049;LATIN CAPITAL LETTER I;Lu;0;L;;;;;N;;;;0069;
+004A;LATIN CAPITAL LETTER J;Lu;0;L;;;;;N;;;;006A;
+004B;LATIN CAPITAL LETTER K;Lu;0;L;;;;;N;;;;006B;
+004C;LATIN CAPITAL LETTER L;Lu;0;L;;;;;N;;;;006C;
+004D;LATIN CAPITAL LETTER M;Lu;0;L;;;;;N;;;;006D;
+004E;LATIN CAPITAL LETTER N;Lu;0;L;;;;;N;;;;006E;
+004F;LATIN CAPITAL LETTER O;Lu;0;L;;;;;N;;;;006F;
+0050;LATIN CAPITAL LETTER P;Lu;0;L;;;;;N;;;;0070;
+0051;LATIN CAPITAL LETTER Q;Lu;0;L;;;;;N;;;;0071;
+0052;LATIN CAPITAL LETTER R;Lu;0;L;;;;;N;;;;0072;
+0053;LATIN CAPITAL LETTER S;Lu;0;L;;;;;N;;;;0073;
+0054;LATIN CAPITAL LETTER T;Lu;0;L;;;;;N;;;;0074;
+0055;LATIN CAPITAL LETTER U;Lu;0;L;;;;;N;;;;0075;
+0056;LATIN CAPITAL LETTER V;Lu;0;L;;;;;N;;;;0076;
+0057;LATIN CAPITAL LETTER W;Lu;0;L;;;;;N;;;;0077;
+0058;LATIN CAPITAL LETTER X;Lu;0;L;;;;;N;;;;0078;
+0059;LATIN CAPITAL LETTER Y;Lu;0;L;;;;;N;;;;0079;
+005A;LATIN CAPITAL LETTER Z;Lu;0;L;;;;;N;;;;007A;
+005B;LEFT SQUARE BRACKET;Ps;0;ON;;;;;Y;OPENING SQUARE BRACKET;;;;
+005C;REVERSE SOLIDUS;Po;0;ON;;;;;N;BACKSLASH;;;;
+005D;RIGHT SQUARE BRACKET;Pe;0;ON;;;;;Y;CLOSING SQUARE BRACKET;;;;
+005E;CIRCUMFLEX ACCENT;Sk;0;ON;;;;;N;SPACING CIRCUMFLEX;;;;
+005F;LOW LINE;Pc;0;ON;;;;;N;SPACING UNDERSCORE;;;;
+0060;GRAVE ACCENT;Sk;0;ON;;;;;N;SPACING GRAVE;;;;
+0061;LATIN SMALL LETTER A;Ll;0;L;;;;;N;;;0041;;0041
+0062;LATIN SMALL LETTER B;Ll;0;L;;;;;N;;;0042;;0042
+0063;LATIN SMALL LETTER C;Ll;0;L;;;;;N;;;0043;;0043
+0064;LATIN SMALL LETTER D;Ll;0;L;;;;;N;;;0044;;0044
+0065;LATIN SMALL LETTER E;Ll;0;L;;;;;N;;;0045;;0045
+0066;LATIN SMALL LETTER F;Ll;0;L;;;;;N;;;0046;;0046
+0067;LATIN SMALL LETTER G;Ll;0;L;;;;;N;;;0047;;0047
+0068;LATIN SMALL LETTER H;Ll;0;L;;;;;N;;;0048;;0048
+0069;LATIN SMALL LETTER I;Ll;0;L;;;;;N;;;0049;;0049
+006A;LATIN SMALL LETTER J;Ll;0;L;;;;;N;;;004A;;004A
+006B;LATIN SMALL LETTER K;Ll;0;L;;;;;N;;;004B;;004B
+006C;LATIN SMALL LETTER L;Ll;0;L;;;;;N;;;004C;;004C
+006D;LATIN SMALL LETTER M;Ll;0;L;;;;;N;;;004D;;004D
+006E;LATIN SMALL LETTER N;Ll;0;L;;;;;N;;;004E;;004E
+006F;LATIN SMALL LETTER O;Ll;0;L;;;;;N;;;004F;;004F
+0070;LATIN SMALL LETTER P;Ll;0;L;;;;;N;;;0050;;0050
+0071;LATIN SMALL LETTER Q;Ll;0;L;;;;;N;;;0051;;0051
+0072;LATIN SMALL LETTER R;Ll;0;L;;;;;N;;;0052;;0052
+0073;LATIN SMALL LETTER S;Ll;0;L;;;;;N;;;0053;;0053
+0074;LATIN SMALL LETTER T;Ll;0;L;;;;;N;;;0054;;0054
+0075;LATIN SMALL LETTER U;Ll;0;L;;;;;N;;;0055;;0055
+0076;LATIN SMALL LETTER V;Ll;0;L;;;;;N;;;0056;;0056
+0077;LATIN SMALL LETTER W;Ll;0;L;;;;;N;;;0057;;0057
+0078;LATIN SMALL LETTER X;Ll;0;L;;;;;N;;;0058;;0058
+0079;LATIN SMALL LETTER Y;Ll;0;L;;;;;N;;;0059;;0059
+007A;LATIN SMALL LETTER Z;Ll;0;L;;;;;N;;;005A;;005A
+007B;LEFT CURLY BRACKET;Ps;0;ON;;;;;Y;OPENING CURLY BRACKET;;;;
+007C;VERTICAL LINE;Sm;0;ON;;;;;N;VERTICAL BAR;;;;
+007D;RIGHT CURLY BRACKET;Pe;0;ON;;;;;Y;CLOSING CURLY BRACKET;;;;
+007E;TILDE;Sm;0;ON;;;;;N;;;;;
+007F;<control>;Cc;0;BN;;;;;N;DELETE;;;;
+0080;<control>;Cc;0;BN;;;;;N;;;;;
+0081;<control>;Cc;0;BN;;;;;N;;;;;
+0082;<control>;Cc;0;BN;;;;;N;BREAK PERMITTED HERE;;;;
+0083;<control>;Cc;0;BN;;;;;N;NO BREAK HERE;;;;
+0084;<control>;Cc;0;BN;;;;;N;;;;;
+0085;<control>;Cc;0;B;;;;;N;NEXT LINE (NEL);;;;
+0086;<control>;Cc;0;BN;;;;;N;START OF SELECTED AREA;;;;
+0087;<control>;Cc;0;BN;;;;;N;END OF SELECTED AREA;;;;
+0088;<control>;Cc;0;BN;;;;;N;CHARACTER TABULATION SET;;;;
+0089;<control>;Cc;0;BN;;;;;N;CHARACTER TABULATION WITH JUSTIFICATION;;;;
+008A;<control>;Cc;0;BN;;;;;N;LINE TABULATION SET;;;;
+008B;<control>;Cc;0;BN;;;;;N;PARTIAL LINE FORWARD;;;;
+008C;<control>;Cc;0;BN;;;;;N;PARTIAL LINE BACKWARD;;;;
+008D;<control>;Cc;0;BN;;;;;N;REVERSE LINE FEED;;;;
+008E;<control>;Cc;0;BN;;;;;N;SINGLE SHIFT TWO;;;;
+008F;<control>;Cc;0;BN;;;;;N;SINGLE SHIFT THREE;;;;
+0090;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL STRING;;;;
+0091;<control>;Cc;0;BN;;;;;N;PRIVATE USE ONE;;;;
+0092;<control>;Cc;0;BN;;;;;N;PRIVATE USE TWO;;;;
+0093;<control>;Cc;0;BN;;;;;N;SET TRANSMIT STATE;;;;
+0094;<control>;Cc;0;BN;;;;;N;CANCEL CHARACTER;;;;
+0095;<control>;Cc;0;BN;;;;;N;MESSAGE WAITING;;;;
+0096;<control>;Cc;0;BN;;;;;N;START OF GUARDED AREA;;;;
+0097;<control>;Cc;0;BN;;;;;N;END OF GUARDED AREA;;;;
+0098;<control>;Cc;0;BN;;;;;N;START OF STRING;;;;
+0099;<control>;Cc;0;BN;;;;;N;;;;;
+009A;<control>;Cc;0;BN;;;;;N;SINGLE CHARACTER INTRODUCER;;;;
+009B;<control>;Cc;0;BN;;;;;N;CONTROL SEQUENCE INTRODUCER;;;;
+009C;<control>;Cc;0;BN;;;;;N;STRING TERMINATOR;;;;
+009D;<control>;Cc;0;BN;;;;;N;OPERATING SYSTEM COMMAND;;;;
+009E;<control>;Cc;0;BN;;;;;N;PRIVACY MESSAGE;;;;
+009F;<control>;Cc;0;BN;;;;;N;APPLICATION PROGRAM COMMAND;;;;
+00A0;NO-BREAK SPACE;Zs;0;CS;<noBreak> 0020;;;;N;NON-BREAKING SPACE;;;;
+00A1;INVERTED EXCLAMATION MARK;Po;0;ON;;;;;N;;;;;
+00A2;CENT SIGN;Sc;0;ET;;;;;N;;;;;
+00A3;POUND SIGN;Sc;0;ET;;;;;N;;;;;
+00A4;CURRENCY SIGN;Sc;0;ET;;;;;N;;;;;
+00A5;YEN SIGN;Sc;0;ET;;;;;N;;;;;
+00A6;BROKEN BAR;So;0;ON;;;;;N;BROKEN VERTICAL BAR;;;;
+00A7;SECTION SIGN;Po;0;ON;;;;;N;;;;;
+00A8;DIAERESIS;Sk;0;ON;<compat> 0020 0308;;;;N;SPACING DIAERESIS;;;;
+00A9;COPYRIGHT SIGN;So;0;ON;;;;;N;;;;;
+00AA;FEMININE ORDINAL INDICATOR;Lo;0;L;<super> 0061;;;;N;;;;;
+00AB;LEFT-POINTING DOUBLE ANGLE QUOTATION MARK;Pi;0;ON;;;;;Y;LEFT POINTING GUILLEMET;;;;
+00AC;NOT SIGN;Sm;0;ON;;;;;N;;;;;
+00AD;SOFT HYPHEN;Cf;0;BN;;;;;N;;;;;
+00AE;REGISTERED SIGN;So;0;ON;;;;;N;REGISTERED TRADE MARK SIGN;;;;
+00AF;MACRON;Sk;0;ON;<compat> 0020 0304;;;;N;SPACING MACRON;;;;
+00B0;DEGREE SIGN;So;0;ET;;;;;N;;;;;
+00B1;PLUS-MINUS SIGN;Sm;0;ET;;;;;N;PLUS-OR-MINUS SIGN;;;;
+00B2;SUPERSCRIPT TWO;No;0;EN;<super> 0032;;2;2;N;SUPERSCRIPT DIGIT TWO;;;;
+00B3;SUPERSCRIPT THREE;No;0;EN;<super> 0033;;3;3;N;SUPERSCRIPT DIGIT THREE;;;;
+00B4;ACUTE ACCENT;Sk;0;ON;<compat> 0020 0301;;;;N;SPACING ACUTE;;;;
+00B5;MICRO SIGN;Ll;0;L;<compat> 03BC;;;;N;;;039C;;039C
+00B6;PILCROW SIGN;Po;0;ON;;;;;N;PARAGRAPH SIGN;;;;
+00B7;MIDDLE DOT;Po;0;ON;;;;;N;;;;;
+00B8;CEDILLA;Sk;0;ON;<compat> 0020 0327;;;;N;SPACING CEDILLA;;;;
+00B9;SUPERSCRIPT ONE;No;0;EN;<super> 0031;;1;1;N;SUPERSCRIPT DIGIT ONE;;;;
+00BA;MASCULINE ORDINAL INDICATOR;Lo;0;L;<super> 006F;;;;N;;;;;
+00BB;RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK;Pf;0;ON;;;;;Y;RIGHT POINTING GUILLEMET;;;;
+00BC;VULGAR FRACTION ONE QUARTER;No;0;ON;<fraction> 0031 2044 0034;;;1/4;N;FRACTION ONE QUARTER;;;;
+00BD;VULGAR FRACTION ONE HALF;No;0;ON;<fraction> 0031 2044 0032;;;1/2;N;FRACTION ONE HALF;;;;
+00BE;VULGAR FRACTION THREE QUARTERS;No;0;ON;<fraction> 0033 2044 0034;;;3/4;N;FRACTION THREE QUARTERS;;;;
+00BF;INVERTED QUESTION MARK;Po;0;ON;;;;;N;;;;;
+00C0;LATIN CAPITAL LETTER A WITH GRAVE;Lu;0;L;0041 0300;;;;N;LATIN CAPITAL LETTER A GRAVE;;;00E0;
+00C1;LATIN CAPITAL LETTER A WITH ACUTE;Lu;0;L;0041 0301;;;;N;LATIN CAPITAL LETTER A ACUTE;;;00E1;
+00C2;LATIN CAPITAL LETTER A WITH CIRCUMFLEX;Lu;0;L;0041 0302;;;;N;LATIN CAPITAL LETTER A CIRCUMFLEX;;;00E2;
+00C3;LATIN CAPITAL LETTER A WITH TILDE;Lu;0;L;0041 0303;;;;N;LATIN CAPITAL LETTER A TILDE;;;00E3;
+00C4;LATIN CAPITAL LETTER A WITH DIAERESIS;Lu;0;L;0041 0308;;;;N;LATIN CAPITAL LETTER A DIAERESIS;;;00E4;
+00C5;LATIN CAPITAL LETTER A WITH RING ABOVE;Lu;0;L;0041 030A;;;;N;LATIN CAPITAL LETTER A RING;;;00E5;
+00C6;LATIN CAPITAL LETTER AE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER A E;;;00E6;
+00C7;LATIN CAPITAL LETTER C WITH CEDILLA;Lu;0;L;0043 0327;;;;N;LATIN CAPITAL LETTER C CEDILLA;;;00E7;
+00C8;LATIN CAPITAL LETTER E WITH GRAVE;Lu;0;L;0045 0300;;;;N;LATIN CAPITAL LETTER E GRAVE;;;00E8;
+00C9;LATIN CAPITAL LETTER E WITH ACUTE;Lu;0;L;0045 0301;;;;N;LATIN CAPITAL LETTER E ACUTE;;;00E9;
+00CA;LATIN CAPITAL LETTER E WITH CIRCUMFLEX;Lu;0;L;0045 0302;;;;N;LATIN CAPITAL LETTER E CIRCUMFLEX;;;00EA;
+00CB;LATIN CAPITAL LETTER E WITH DIAERESIS;Lu;0;L;0045 0308;;;;N;LATIN CAPITAL LETTER E DIAERESIS;;;00EB;
+00CC;LATIN CAPITAL LETTER I WITH GRAVE;Lu;0;L;0049 0300;;;;N;LATIN CAPITAL LETTER I GRAVE;;;00EC;
+00CD;LATIN CAPITAL LETTER I WITH ACUTE;Lu;0;L;0049 0301;;;;N;LATIN CAPITAL LETTER I ACUTE;;;00ED;
+00CE;LATIN CAPITAL LETTER I WITH CIRCUMFLEX;Lu;0;L;0049 0302;;;;N;LATIN CAPITAL LETTER I CIRCUMFLEX;;;00EE;
+00CF;LATIN CAPITAL LETTER I WITH DIAERESIS;Lu;0;L;0049 0308;;;;N;LATIN CAPITAL LETTER I DIAERESIS;;;00EF;
+00D0;LATIN CAPITAL LETTER ETH;Lu;0;L;;;;;N;;;;00F0;
+00D1;LATIN CAPITAL LETTER N WITH TILDE;Lu;0;L;004E 0303;;;;N;LATIN CAPITAL LETTER N TILDE;;;00F1;
+00D2;LATIN CAPITAL LETTER O WITH GRAVE;Lu;0;L;004F 0300;;;;N;LATIN CAPITAL LETTER O GRAVE;;;00F2;
+00D3;LATIN CAPITAL LETTER O WITH ACUTE;Lu;0;L;004F 0301;;;;N;LATIN CAPITAL LETTER O ACUTE;;;00F3;
+00D4;LATIN CAPITAL LETTER O WITH CIRCUMFLEX;Lu;0;L;004F 0302;;;;N;LATIN CAPITAL LETTER O CIRCUMFLEX;;;00F4;
+00D5;LATIN CAPITAL LETTER O WITH TILDE;Lu;0;L;004F 0303;;;;N;LATIN CAPITAL LETTER O TILDE;;;00F5;
+00D6;LATIN CAPITAL LETTER O WITH DIAERESIS;Lu;0;L;004F 0308;;;;N;LATIN CAPITAL LETTER O DIAERESIS;;;00F6;
+00D7;MULTIPLICATION SIGN;Sm;0;ON;;;;;N;;;;;
+00D8;LATIN CAPITAL LETTER O WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O SLASH;;;00F8;
+00D9;LATIN CAPITAL LETTER U WITH GRAVE;Lu;0;L;0055 0300;;;;N;LATIN CAPITAL LETTER U GRAVE;;;00F9;
+00DA;LATIN CAPITAL LETTER U WITH ACUTE;Lu;0;L;0055 0301;;;;N;LATIN CAPITAL LETTER U ACUTE;;;00FA;
+00DB;LATIN CAPITAL LETTER U WITH CIRCUMFLEX;Lu;0;L;0055 0302;;;;N;LATIN CAPITAL LETTER U CIRCUMFLEX;;;00FB;
+00DC;LATIN CAPITAL LETTER U WITH DIAERESIS;Lu;0;L;0055 0308;;;;N;LATIN CAPITAL LETTER U DIAERESIS;;;00FC;
+00DD;LATIN CAPITAL LETTER Y WITH ACUTE;Lu;0;L;0059 0301;;;;N;LATIN CAPITAL LETTER Y ACUTE;;;00FD;
+00DE;LATIN CAPITAL LETTER THORN;Lu;0;L;;;;;N;;;;00FE;
+00DF;LATIN SMALL LETTER SHARP S;Ll;0;L;;;;;N;;;;;
+00E0;LATIN SMALL LETTER A WITH GRAVE;Ll;0;L;0061 0300;;;;N;LATIN SMALL LETTER A GRAVE;;00C0;;00C0
+00E1;LATIN SMALL LETTER A WITH ACUTE;Ll;0;L;0061 0301;;;;N;LATIN SMALL LETTER A ACUTE;;00C1;;00C1
+00E2;LATIN SMALL LETTER A WITH CIRCUMFLEX;Ll;0;L;0061 0302;;;;N;LATIN SMALL LETTER A CIRCUMFLEX;;00C2;;00C2
+00E3;LATIN SMALL LETTER A WITH TILDE;Ll;0;L;0061 0303;;;;N;LATIN SMALL LETTER A TILDE;;00C3;;00C3
+00E4;LATIN SMALL LETTER A WITH DIAERESIS;Ll;0;L;0061 0308;;;;N;LATIN SMALL LETTER A DIAERESIS;;00C4;;00C4
+00E5;LATIN SMALL LETTER A WITH RING ABOVE;Ll;0;L;0061 030A;;;;N;LATIN SMALL LETTER A RING;;00C5;;00C5
+00E6;LATIN SMALL LETTER AE;Ll;0;L;;;;;N;LATIN SMALL LETTER A E;;00C6;;00C6
+00E7;LATIN SMALL LETTER C WITH CEDILLA;Ll;0;L;0063 0327;;;;N;LATIN SMALL LETTER C CEDILLA;;00C7;;00C7
+00E8;LATIN SMALL LETTER E WITH GRAVE;Ll;0;L;0065 0300;;;;N;LATIN SMALL LETTER E GRAVE;;00C8;;00C8
+00E9;LATIN SMALL LETTER E WITH ACUTE;Ll;0;L;0065 0301;;;;N;LATIN SMALL LETTER E ACUTE;;00C9;;00C9
+00EA;LATIN SMALL LETTER E WITH CIRCUMFLEX;Ll;0;L;0065 0302;;;;N;LATIN SMALL LETTER E CIRCUMFLEX;;00CA;;00CA
+00EB;LATIN SMALL LETTER E WITH DIAERESIS;Ll;0;L;0065 0308;;;;N;LATIN SMALL LETTER E DIAERESIS;;00CB;;00CB
+00EC;LATIN SMALL LETTER I WITH GRAVE;Ll;0;L;0069 0300;;;;N;LATIN SMALL LETTER I GRAVE;;00CC;;00CC
+00ED;LATIN SMALL LETTER I WITH ACUTE;Ll;0;L;0069 0301;;;;N;LATIN SMALL LETTER I ACUTE;;00CD;;00CD
+00EE;LATIN SMALL LETTER I WITH CIRCUMFLEX;Ll;0;L;0069 0302;;;;N;LATIN SMALL LETTER I CIRCUMFLEX;;00CE;;00CE
+00EF;LATIN SMALL LETTER I WITH DIAERESIS;Ll;0;L;0069 0308;;;;N;LATIN SMALL LETTER I DIAERESIS;;00CF;;00CF
+00F0;LATIN SMALL LETTER ETH;Ll;0;L;;;;;N;;;00D0;;00D0
+00F1;LATIN SMALL LETTER N WITH TILDE;Ll;0;L;006E 0303;;;;N;LATIN SMALL LETTER N TILDE;;00D1;;00D1
+00F2;LATIN SMALL LETTER O WITH GRAVE;Ll;0;L;006F 0300;;;;N;LATIN SMALL LETTER O GRAVE;;00D2;;00D2
+00F3;LATIN SMALL LETTER O WITH ACUTE;Ll;0;L;006F 0301;;;;N;LATIN SMALL LETTER O ACUTE;;00D3;;00D3
+00F4;LATIN SMALL LETTER O WITH CIRCUMFLEX;Ll;0;L;006F 0302;;;;N;LATIN SMALL LETTER O CIRCUMFLEX;;00D4;;00D4
+00F5;LATIN SMALL LETTER O WITH TILDE;Ll;0;L;006F 0303;;;;N;LATIN SMALL LETTER O TILDE;;00D5;;00D5
+00F6;LATIN SMALL LETTER O WITH DIAERESIS;Ll;0;L;006F 0308;;;;N;LATIN SMALL LETTER O DIAERESIS;;00D6;;00D6
+00F7;DIVISION SIGN;Sm;0;ON;;;;;N;;;;;
+00F8;LATIN SMALL LETTER O WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER O SLASH;;00D8;;00D8
+00F9;LATIN SMALL LETTER U WITH GRAVE;Ll;0;L;0075 0300;;;;N;LATIN SMALL LETTER U GRAVE;;00D9;;00D9
+00FA;LATIN SMALL LETTER U WITH ACUTE;Ll;0;L;0075 0301;;;;N;LATIN SMALL LETTER U ACUTE;;00DA;;00DA
+00FB;LATIN SMALL LETTER U WITH CIRCUMFLEX;Ll;0;L;0075 0302;;;;N;LATIN SMALL LETTER U CIRCUMFLEX;;00DB;;00DB
+00FC;LATIN SMALL LETTER U WITH DIAERESIS;Ll;0;L;0075 0308;;;;N;LATIN SMALL LETTER U DIAERESIS;;00DC;;00DC
+00FD;LATIN SMALL LETTER Y WITH ACUTE;Ll;0;L;0079 0301;;;;N;LATIN SMALL LETTER Y ACUTE;;00DD;;00DD
+00FE;LATIN SMALL LETTER THORN;Ll;0;L;;;;;N;;;00DE;;00DE
+00FF;LATIN SMALL LETTER Y WITH DIAERESIS;Ll;0;L;0079 0308;;;;N;LATIN SMALL LETTER Y DIAERESIS;;0178;;0178
+0100;LATIN CAPITAL LETTER A WITH MACRON;Lu;0;L;0041 0304;;;;N;LATIN CAPITAL LETTER A MACRON;;;0101;
+0101;LATIN SMALL LETTER A WITH MACRON;Ll;0;L;0061 0304;;;;N;LATIN SMALL LETTER A MACRON;;0100;;0100
+0102;LATIN CAPITAL LETTER A WITH BREVE;Lu;0;L;0041 0306;;;;N;LATIN CAPITAL LETTER A BREVE;;;0103;
+0103;LATIN SMALL LETTER A WITH BREVE;Ll;0;L;0061 0306;;;;N;LATIN SMALL LETTER A BREVE;;0102;;0102
+0104;LATIN CAPITAL LETTER A WITH OGONEK;Lu;0;L;0041 0328;;;;N;LATIN CAPITAL LETTER A OGONEK;;;0105;
+0105;LATIN SMALL LETTER A WITH OGONEK;Ll;0;L;0061 0328;;;;N;LATIN SMALL LETTER A OGONEK;;0104;;0104
+0106;LATIN CAPITAL LETTER C WITH ACUTE;Lu;0;L;0043 0301;;;;N;LATIN CAPITAL LETTER C ACUTE;;;0107;
+0107;LATIN SMALL LETTER C WITH ACUTE;Ll;0;L;0063 0301;;;;N;LATIN SMALL LETTER C ACUTE;;0106;;0106
+0108;LATIN CAPITAL LETTER C WITH CIRCUMFLEX;Lu;0;L;0043 0302;;;;N;LATIN CAPITAL LETTER C CIRCUMFLEX;;;0109;
+0109;LATIN SMALL LETTER C WITH CIRCUMFLEX;Ll;0;L;0063 0302;;;;N;LATIN SMALL LETTER C CIRCUMFLEX;;0108;;0108
+010A;LATIN CAPITAL LETTER C WITH DOT ABOVE;Lu;0;L;0043 0307;;;;N;LATIN CAPITAL LETTER C DOT;;;010B;
+010B;LATIN SMALL LETTER C WITH DOT ABOVE;Ll;0;L;0063 0307;;;;N;LATIN SMALL LETTER C DOT;;010A;;010A
+010C;LATIN CAPITAL LETTER C WITH CARON;Lu;0;L;0043 030C;;;;N;LATIN CAPITAL LETTER C HACEK;;;010D;
+010D;LATIN SMALL LETTER C WITH CARON;Ll;0;L;0063 030C;;;;N;LATIN SMALL LETTER C HACEK;;010C;;010C
+010E;LATIN CAPITAL LETTER D WITH CARON;Lu;0;L;0044 030C;;;;N;LATIN CAPITAL LETTER D HACEK;;;010F;
+010F;LATIN SMALL LETTER D WITH CARON;Ll;0;L;0064 030C;;;;N;LATIN SMALL LETTER D HACEK;;010E;;010E
+0110;LATIN CAPITAL LETTER D WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D BAR;;;0111;
+0111;LATIN SMALL LETTER D WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER D BAR;;0110;;0110
+0112;LATIN CAPITAL LETTER E WITH MACRON;Lu;0;L;0045 0304;;;;N;LATIN CAPITAL LETTER E MACRON;;;0113;
+0113;LATIN SMALL LETTER E WITH MACRON;Ll;0;L;0065 0304;;;;N;LATIN SMALL LETTER E MACRON;;0112;;0112
+0114;LATIN CAPITAL LETTER E WITH BREVE;Lu;0;L;0045 0306;;;;N;LATIN CAPITAL LETTER E BREVE;;;0115;
+0115;LATIN SMALL LETTER E WITH BREVE;Ll;0;L;0065 0306;;;;N;LATIN SMALL LETTER E BREVE;;0114;;0114
+0116;LATIN CAPITAL LETTER E WITH DOT ABOVE;Lu;0;L;0045 0307;;;;N;LATIN CAPITAL LETTER E DOT;;;0117;
+0117;LATIN SMALL LETTER E WITH DOT ABOVE;Ll;0;L;0065 0307;;;;N;LATIN SMALL LETTER E DOT;;0116;;0116
+0118;LATIN CAPITAL LETTER E WITH OGONEK;Lu;0;L;0045 0328;;;;N;LATIN CAPITAL LETTER E OGONEK;;;0119;
+0119;LATIN SMALL LETTER E WITH OGONEK;Ll;0;L;0065 0328;;;;N;LATIN SMALL LETTER E OGONEK;;0118;;0118
+011A;LATIN CAPITAL LETTER E WITH CARON;Lu;0;L;0045 030C;;;;N;LATIN CAPITAL LETTER E HACEK;;;011B;
+011B;LATIN SMALL LETTER E WITH CARON;Ll;0;L;0065 030C;;;;N;LATIN SMALL LETTER E HACEK;;011A;;011A
+011C;LATIN CAPITAL LETTER G WITH CIRCUMFLEX;Lu;0;L;0047 0302;;;;N;LATIN CAPITAL LETTER G CIRCUMFLEX;;;011D;
+011D;LATIN SMALL LETTER G WITH CIRCUMFLEX;Ll;0;L;0067 0302;;;;N;LATIN SMALL LETTER G CIRCUMFLEX;;011C;;011C
+011E;LATIN CAPITAL LETTER G WITH BREVE;Lu;0;L;0047 0306;;;;N;LATIN CAPITAL LETTER G BREVE;;;011F;
+011F;LATIN SMALL LETTER G WITH BREVE;Ll;0;L;0067 0306;;;;N;LATIN SMALL LETTER G BREVE;;011E;;011E
+0120;LATIN CAPITAL LETTER G WITH DOT ABOVE;Lu;0;L;0047 0307;;;;N;LATIN CAPITAL LETTER G DOT;;;0121;
+0121;LATIN SMALL LETTER G WITH DOT ABOVE;Ll;0;L;0067 0307;;;;N;LATIN SMALL LETTER G DOT;;0120;;0120
+0122;LATIN CAPITAL LETTER G WITH CEDILLA;Lu;0;L;0047 0327;;;;N;LATIN CAPITAL LETTER G CEDILLA;;;0123;
+0123;LATIN SMALL LETTER G WITH CEDILLA;Ll;0;L;0067 0327;;;;N;LATIN SMALL LETTER G CEDILLA;;0122;;0122
+0124;LATIN CAPITAL LETTER H WITH CIRCUMFLEX;Lu;0;L;0048 0302;;;;N;LATIN CAPITAL LETTER H CIRCUMFLEX;;;0125;
+0125;LATIN SMALL LETTER H WITH CIRCUMFLEX;Ll;0;L;0068 0302;;;;N;LATIN SMALL LETTER H CIRCUMFLEX;;0124;;0124
+0126;LATIN CAPITAL LETTER H WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER H BAR;;;0127;
+0127;LATIN SMALL LETTER H WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER H BAR;;0126;;0126
+0128;LATIN CAPITAL LETTER I WITH TILDE;Lu;0;L;0049 0303;;;;N;LATIN CAPITAL LETTER I TILDE;;;0129;
+0129;LATIN SMALL LETTER I WITH TILDE;Ll;0;L;0069 0303;;;;N;LATIN SMALL LETTER I TILDE;;0128;;0128
+012A;LATIN CAPITAL LETTER I WITH MACRON;Lu;0;L;0049 0304;;;;N;LATIN CAPITAL LETTER I MACRON;;;012B;
+012B;LATIN SMALL LETTER I WITH MACRON;Ll;0;L;0069 0304;;;;N;LATIN SMALL LETTER I MACRON;;012A;;012A
+012C;LATIN CAPITAL LETTER I WITH BREVE;Lu;0;L;0049 0306;;;;N;LATIN CAPITAL LETTER I BREVE;;;012D;
+012D;LATIN SMALL LETTER I WITH BREVE;Ll;0;L;0069 0306;;;;N;LATIN SMALL LETTER I BREVE;;012C;;012C
+012E;LATIN CAPITAL LETTER I WITH OGONEK;Lu;0;L;0049 0328;;;;N;LATIN CAPITAL LETTER I OGONEK;;;012F;
+012F;LATIN SMALL LETTER I WITH OGONEK;Ll;0;L;0069 0328;;;;N;LATIN SMALL LETTER I OGONEK;;012E;;012E
+0130;LATIN CAPITAL LETTER I WITH DOT ABOVE;Lu;0;L;0049 0307;;;;N;LATIN CAPITAL LETTER I DOT;;;0069;
+0131;LATIN SMALL LETTER DOTLESS I;Ll;0;L;;;;;N;;;0049;;0049
+0132;LATIN CAPITAL LIGATURE IJ;Lu;0;L;<compat> 0049 004A;;;;N;LATIN CAPITAL LETTER I J;;;0133;
+0133;LATIN SMALL LIGATURE IJ;Ll;0;L;<compat> 0069 006A;;;;N;LATIN SMALL LETTER I J;;0132;;0132
+0134;LATIN CAPITAL LETTER J WITH CIRCUMFLEX;Lu;0;L;004A 0302;;;;N;LATIN CAPITAL LETTER J CIRCUMFLEX;;;0135;
+0135;LATIN SMALL LETTER J WITH CIRCUMFLEX;Ll;0;L;006A 0302;;;;N;LATIN SMALL LETTER J CIRCUMFLEX;;0134;;0134
+0136;LATIN CAPITAL LETTER K WITH CEDILLA;Lu;0;L;004B 0327;;;;N;LATIN CAPITAL LETTER K CEDILLA;;;0137;
+0137;LATIN SMALL LETTER K WITH CEDILLA;Ll;0;L;006B 0327;;;;N;LATIN SMALL LETTER K CEDILLA;;0136;;0136
+0138;LATIN SMALL LETTER KRA;Ll;0;L;;;;;N;;;;;
+0139;LATIN CAPITAL LETTER L WITH ACUTE;Lu;0;L;004C 0301;;;;N;LATIN CAPITAL LETTER L ACUTE;;;013A;
+013A;LATIN SMALL LETTER L WITH ACUTE;Ll;0;L;006C 0301;;;;N;LATIN SMALL LETTER L ACUTE;;0139;;0139
+013B;LATIN CAPITAL LETTER L WITH CEDILLA;Lu;0;L;004C 0327;;;;N;LATIN CAPITAL LETTER L CEDILLA;;;013C;
+013C;LATIN SMALL LETTER L WITH CEDILLA;Ll;0;L;006C 0327;;;;N;LATIN SMALL LETTER L CEDILLA;;013B;;013B
+013D;LATIN CAPITAL LETTER L WITH CARON;Lu;0;L;004C 030C;;;;N;LATIN CAPITAL LETTER L HACEK;;;013E;
+013E;LATIN SMALL LETTER L WITH CARON;Ll;0;L;006C 030C;;;;N;LATIN SMALL LETTER L HACEK;;013D;;013D
+013F;LATIN CAPITAL LETTER L WITH MIDDLE DOT;Lu;0;L;<compat> 004C 00B7;;;;N;;;;0140;
+0140;LATIN SMALL LETTER L WITH MIDDLE DOT;Ll;0;L;<compat> 006C 00B7;;;;N;;;013F;;013F
+0141;LATIN CAPITAL LETTER L WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER L SLASH;;;0142;
+0142;LATIN SMALL LETTER L WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER L SLASH;;0141;;0141
+0143;LATIN CAPITAL LETTER N WITH ACUTE;Lu;0;L;004E 0301;;;;N;LATIN CAPITAL LETTER N ACUTE;;;0144;
+0144;LATIN SMALL LETTER N WITH ACUTE;Ll;0;L;006E 0301;;;;N;LATIN SMALL LETTER N ACUTE;;0143;;0143
+0145;LATIN CAPITAL LETTER N WITH CEDILLA;Lu;0;L;004E 0327;;;;N;LATIN CAPITAL LETTER N CEDILLA;;;0146;
+0146;LATIN SMALL LETTER N WITH CEDILLA;Ll;0;L;006E 0327;;;;N;LATIN SMALL LETTER N CEDILLA;;0145;;0145
+0147;LATIN CAPITAL LETTER N WITH CARON;Lu;0;L;004E 030C;;;;N;LATIN CAPITAL LETTER N HACEK;;;0148;
+0148;LATIN SMALL LETTER N WITH CARON;Ll;0;L;006E 030C;;;;N;LATIN SMALL LETTER N HACEK;;0147;;0147
+0149;LATIN SMALL LETTER N PRECEDED BY APOSTROPHE;Ll;0;L;<compat> 02BC 006E;;;;N;LATIN SMALL LETTER APOSTROPHE N;;;;
+014A;LATIN CAPITAL LETTER ENG;Lu;0;L;;;;;N;;;;014B;
+014B;LATIN SMALL LETTER ENG;Ll;0;L;;;;;N;;;014A;;014A
+014C;LATIN CAPITAL LETTER O WITH MACRON;Lu;0;L;004F 0304;;;;N;LATIN CAPITAL LETTER O MACRON;;;014D;
+014D;LATIN SMALL LETTER O WITH MACRON;Ll;0;L;006F 0304;;;;N;LATIN SMALL LETTER O MACRON;;014C;;014C
+014E;LATIN CAPITAL LETTER O WITH BREVE;Lu;0;L;004F 0306;;;;N;LATIN CAPITAL LETTER O BREVE;;;014F;
+014F;LATIN SMALL LETTER O WITH BREVE;Ll;0;L;006F 0306;;;;N;LATIN SMALL LETTER O BREVE;;014E;;014E
+0150;LATIN CAPITAL LETTER O WITH DOUBLE ACUTE;Lu;0;L;004F 030B;;;;N;LATIN CAPITAL LETTER O DOUBLE ACUTE;;;0151;
+0151;LATIN SMALL LETTER O WITH DOUBLE ACUTE;Ll;0;L;006F 030B;;;;N;LATIN SMALL LETTER O DOUBLE ACUTE;;0150;;0150
+0152;LATIN CAPITAL LIGATURE OE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O E;;;0153;
+0153;LATIN SMALL LIGATURE OE;Ll;0;L;;;;;N;LATIN SMALL LETTER O E;;0152;;0152
+0154;LATIN CAPITAL LETTER R WITH ACUTE;Lu;0;L;0052 0301;;;;N;LATIN CAPITAL LETTER R ACUTE;;;0155;
+0155;LATIN SMALL LETTER R WITH ACUTE;Ll;0;L;0072 0301;;;;N;LATIN SMALL LETTER R ACUTE;;0154;;0154
+0156;LATIN CAPITAL LETTER R WITH CEDILLA;Lu;0;L;0052 0327;;;;N;LATIN CAPITAL LETTER R CEDILLA;;;0157;
+0157;LATIN SMALL LETTER R WITH CEDILLA;Ll;0;L;0072 0327;;;;N;LATIN SMALL LETTER R CEDILLA;;0156;;0156
+0158;LATIN CAPITAL LETTER R WITH CARON;Lu;0;L;0052 030C;;;;N;LATIN CAPITAL LETTER R HACEK;;;0159;
+0159;LATIN SMALL LETTER R WITH CARON;Ll;0;L;0072 030C;;;;N;LATIN SMALL LETTER R HACEK;;0158;;0158
+015A;LATIN CAPITAL LETTER S WITH ACUTE;Lu;0;L;0053 0301;;;;N;LATIN CAPITAL LETTER S ACUTE;;;015B;
+015B;LATIN SMALL LETTER S WITH ACUTE;Ll;0;L;0073 0301;;;;N;LATIN SMALL LETTER S ACUTE;;015A;;015A
+015C;LATIN CAPITAL LETTER S WITH CIRCUMFLEX;Lu;0;L;0053 0302;;;;N;LATIN CAPITAL LETTER S CIRCUMFLEX;;;015D;
+015D;LATIN SMALL LETTER S WITH CIRCUMFLEX;Ll;0;L;0073 0302;;;;N;LATIN SMALL LETTER S CIRCUMFLEX;;015C;;015C
+015E;LATIN CAPITAL LETTER S WITH CEDILLA;Lu;0;L;0053 0327;;;;N;LATIN CAPITAL LETTER S CEDILLA;;;015F;
+015F;LATIN SMALL LETTER S WITH CEDILLA;Ll;0;L;0073 0327;;;;N;LATIN SMALL LETTER S CEDILLA;;015E;;015E
+0160;LATIN CAPITAL LETTER S WITH CARON;Lu;0;L;0053 030C;;;;N;LATIN CAPITAL LETTER S HACEK;;;0161;
+0161;LATIN SMALL LETTER S WITH CARON;Ll;0;L;0073 030C;;;;N;LATIN SMALL LETTER S HACEK;;0160;;0160
+0162;LATIN CAPITAL LETTER T WITH CEDILLA;Lu;0;L;0054 0327;;;;N;LATIN CAPITAL LETTER T CEDILLA;;;0163;
+0163;LATIN SMALL LETTER T WITH CEDILLA;Ll;0;L;0074 0327;;;;N;LATIN SMALL LETTER T CEDILLA;;0162;;0162
+0164;LATIN CAPITAL LETTER T WITH CARON;Lu;0;L;0054 030C;;;;N;LATIN CAPITAL LETTER T HACEK;;;0165;
+0165;LATIN SMALL LETTER T WITH CARON;Ll;0;L;0074 030C;;;;N;LATIN SMALL LETTER T HACEK;;0164;;0164
+0166;LATIN CAPITAL LETTER T WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T BAR;;;0167;
+0167;LATIN SMALL LETTER T WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER T BAR;;0166;;0166
+0168;LATIN CAPITAL LETTER U WITH TILDE;Lu;0;L;0055 0303;;;;N;LATIN CAPITAL LETTER U TILDE;;;0169;
+0169;LATIN SMALL LETTER U WITH TILDE;Ll;0;L;0075 0303;;;;N;LATIN SMALL LETTER U TILDE;;0168;;0168
+016A;LATIN CAPITAL LETTER U WITH MACRON;Lu;0;L;0055 0304;;;;N;LATIN CAPITAL LETTER U MACRON;;;016B;
+016B;LATIN SMALL LETTER U WITH MACRON;Ll;0;L;0075 0304;;;;N;LATIN SMALL LETTER U MACRON;;016A;;016A
+016C;LATIN CAPITAL LETTER U WITH BREVE;Lu;0;L;0055 0306;;;;N;LATIN CAPITAL LETTER U BREVE;;;016D;
+016D;LATIN SMALL LETTER U WITH BREVE;Ll;0;L;0075 0306;;;;N;LATIN SMALL LETTER U BREVE;;016C;;016C
+016E;LATIN CAPITAL LETTER U WITH RING ABOVE;Lu;0;L;0055 030A;;;;N;LATIN CAPITAL LETTER U RING;;;016F;
+016F;LATIN SMALL LETTER U WITH RING ABOVE;Ll;0;L;0075 030A;;;;N;LATIN SMALL LETTER U RING;;016E;;016E
+0170;LATIN CAPITAL LETTER U WITH DOUBLE ACUTE;Lu;0;L;0055 030B;;;;N;LATIN CAPITAL LETTER U DOUBLE ACUTE;;;0171;
+0171;LATIN SMALL LETTER U WITH DOUBLE ACUTE;Ll;0;L;0075 030B;;;;N;LATIN SMALL LETTER U DOUBLE ACUTE;;0170;;0170
+0172;LATIN CAPITAL LETTER U WITH OGONEK;Lu;0;L;0055 0328;;;;N;LATIN CAPITAL LETTER U OGONEK;;;0173;
+0173;LATIN SMALL LETTER U WITH OGONEK;Ll;0;L;0075 0328;;;;N;LATIN SMALL LETTER U OGONEK;;0172;;0172
+0174;LATIN CAPITAL LETTER W WITH CIRCUMFLEX;Lu;0;L;0057 0302;;;;N;LATIN CAPITAL LETTER W CIRCUMFLEX;;;0175;
+0175;LATIN SMALL LETTER W WITH CIRCUMFLEX;Ll;0;L;0077 0302;;;;N;LATIN SMALL LETTER W CIRCUMFLEX;;0174;;0174
+0176;LATIN CAPITAL LETTER Y WITH CIRCUMFLEX;Lu;0;L;0059 0302;;;;N;LATIN CAPITAL LETTER Y CIRCUMFLEX;;;0177;
+0177;LATIN SMALL LETTER Y WITH CIRCUMFLEX;Ll;0;L;0079 0302;;;;N;LATIN SMALL LETTER Y CIRCUMFLEX;;0176;;0176
+0178;LATIN CAPITAL LETTER Y WITH DIAERESIS;Lu;0;L;0059 0308;;;;N;LATIN CAPITAL LETTER Y DIAERESIS;;;00FF;
+0179;LATIN CAPITAL LETTER Z WITH ACUTE;Lu;0;L;005A 0301;;;;N;LATIN CAPITAL LETTER Z ACUTE;;;017A;
+017A;LATIN SMALL LETTER Z WITH ACUTE;Ll;0;L;007A 0301;;;;N;LATIN SMALL LETTER Z ACUTE;;0179;;0179
+017B;LATIN CAPITAL LETTER Z WITH DOT ABOVE;Lu;0;L;005A 0307;;;;N;LATIN CAPITAL LETTER Z DOT;;;017C;
+017C;LATIN SMALL LETTER Z WITH DOT ABOVE;Ll;0;L;007A 0307;;;;N;LATIN SMALL LETTER Z DOT;;017B;;017B
+017D;LATIN CAPITAL LETTER Z WITH CARON;Lu;0;L;005A 030C;;;;N;LATIN CAPITAL LETTER Z HACEK;;;017E;
+017E;LATIN SMALL LETTER Z WITH CARON;Ll;0;L;007A 030C;;;;N;LATIN SMALL LETTER Z HACEK;;017D;;017D
+017F;LATIN SMALL LETTER LONG S;Ll;0;L;<compat> 0073;;;;N;;;0053;;0053
+0180;LATIN SMALL LETTER B WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER B BAR;;0243;;0243
+0181;LATIN CAPITAL LETTER B WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER B HOOK;;;0253;
+0182;LATIN CAPITAL LETTER B WITH TOPBAR;Lu;0;L;;;;;N;LATIN CAPITAL LETTER B TOPBAR;;;0183;
+0183;LATIN SMALL LETTER B WITH TOPBAR;Ll;0;L;;;;;N;LATIN SMALL LETTER B TOPBAR;;0182;;0182
+0184;LATIN CAPITAL LETTER TONE SIX;Lu;0;L;;;;;N;;;;0185;
+0185;LATIN SMALL LETTER TONE SIX;Ll;0;L;;;;;N;;;0184;;0184
+0186;LATIN CAPITAL LETTER OPEN O;Lu;0;L;;;;;N;;;;0254;
+0187;LATIN CAPITAL LETTER C WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER C HOOK;;;0188;
+0188;LATIN SMALL LETTER C WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER C HOOK;;0187;;0187
+0189;LATIN CAPITAL LETTER AFRICAN D;Lu;0;L;;;;;N;;;;0256;
+018A;LATIN CAPITAL LETTER D WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D HOOK;;;0257;
+018B;LATIN CAPITAL LETTER D WITH TOPBAR;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D TOPBAR;;;018C;
+018C;LATIN SMALL LETTER D WITH TOPBAR;Ll;0;L;;;;;N;LATIN SMALL LETTER D TOPBAR;;018B;;018B
+018D;LATIN SMALL LETTER TURNED DELTA;Ll;0;L;;;;;N;;;;;
+018E;LATIN CAPITAL LETTER REVERSED E;Lu;0;L;;;;;N;LATIN CAPITAL LETTER TURNED E;;;01DD;
+018F;LATIN CAPITAL LETTER SCHWA;Lu;0;L;;;;;N;;;;0259;
+0190;LATIN CAPITAL LETTER OPEN E;Lu;0;L;;;;;N;LATIN CAPITAL LETTER EPSILON;;;025B;
+0191;LATIN CAPITAL LETTER F WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER F HOOK;;;0192;
+0192;LATIN SMALL LETTER F WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT F;;0191;;0191
+0193;LATIN CAPITAL LETTER G WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER G HOOK;;;0260;
+0194;LATIN CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;0263;
+0195;LATIN SMALL LETTER HV;Ll;0;L;;;;;N;LATIN SMALL LETTER H V;;01F6;;01F6
+0196;LATIN CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;0269;
+0197;LATIN CAPITAL LETTER I WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER BARRED I;;;0268;
+0198;LATIN CAPITAL LETTER K WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER K HOOK;;;0199;
+0199;LATIN SMALL LETTER K WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER K HOOK;;0198;;0198
+019A;LATIN SMALL LETTER L WITH BAR;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED L;;023D;;023D
+019B;LATIN SMALL LETTER LAMBDA WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED LAMBDA;;;;
+019C;LATIN CAPITAL LETTER TURNED M;Lu;0;L;;;;;N;;;;026F;
+019D;LATIN CAPITAL LETTER N WITH LEFT HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER N HOOK;;;0272;
+019E;LATIN SMALL LETTER N WITH LONG RIGHT LEG;Ll;0;L;;;;;N;;;0220;;0220
+019F;LATIN CAPITAL LETTER O WITH MIDDLE TILDE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER BARRED O;;;0275;
+01A0;LATIN CAPITAL LETTER O WITH HORN;Lu;0;L;004F 031B;;;;N;LATIN CAPITAL LETTER O HORN;;;01A1;
+01A1;LATIN SMALL LETTER O WITH HORN;Ll;0;L;006F 031B;;;;N;LATIN SMALL LETTER O HORN;;01A0;;01A0
+01A2;LATIN CAPITAL LETTER OI;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O I;;;01A3;
+01A3;LATIN SMALL LETTER OI;Ll;0;L;;;;;N;LATIN SMALL LETTER O I;;01A2;;01A2
+01A4;LATIN CAPITAL LETTER P WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER P HOOK;;;01A5;
+01A5;LATIN SMALL LETTER P WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER P HOOK;;01A4;;01A4
+01A6;LATIN LETTER YR;Lu;0;L;;;;;N;LATIN LETTER Y R;;;0280;
+01A7;LATIN CAPITAL LETTER TONE TWO;Lu;0;L;;;;;N;;;;01A8;
+01A8;LATIN SMALL LETTER TONE TWO;Ll;0;L;;;;;N;;;01A7;;01A7
+01A9;LATIN CAPITAL LETTER ESH;Lu;0;L;;;;;N;;;;0283;
+01AA;LATIN LETTER REVERSED ESH LOOP;Ll;0;L;;;;;N;;;;;
+01AB;LATIN SMALL LETTER T WITH PALATAL HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T PALATAL HOOK;;;;
+01AC;LATIN CAPITAL LETTER T WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T HOOK;;;01AD;
+01AD;LATIN SMALL LETTER T WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T HOOK;;01AC;;01AC
+01AE;LATIN CAPITAL LETTER T WITH RETROFLEX HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T RETROFLEX HOOK;;;0288;
+01AF;LATIN CAPITAL LETTER U WITH HORN;Lu;0;L;0055 031B;;;;N;LATIN CAPITAL LETTER U HORN;;;01B0;
+01B0;LATIN SMALL LETTER U WITH HORN;Ll;0;L;0075 031B;;;;N;LATIN SMALL LETTER U HORN;;01AF;;01AF
+01B1;LATIN CAPITAL LETTER UPSILON;Lu;0;L;;;;;N;;;;028A;
+01B2;LATIN CAPITAL LETTER V WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER SCRIPT V;;;028B;
+01B3;LATIN CAPITAL LETTER Y WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER Y HOOK;;;01B4;
+01B4;LATIN SMALL LETTER Y WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Y HOOK;;01B3;;01B3
+01B5;LATIN CAPITAL LETTER Z WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER Z BAR;;;01B6;
+01B6;LATIN SMALL LETTER Z WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER Z BAR;;01B5;;01B5
+01B7;LATIN CAPITAL LETTER EZH;Lu;0;L;;;;;N;LATIN CAPITAL LETTER YOGH;;;0292;
+01B8;LATIN CAPITAL LETTER EZH REVERSED;Lu;0;L;;;;;N;LATIN CAPITAL LETTER REVERSED YOGH;;;01B9;
+01B9;LATIN SMALL LETTER EZH REVERSED;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED YOGH;;01B8;;01B8
+01BA;LATIN SMALL LETTER EZH WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH WITH TAIL;;;;
+01BB;LATIN LETTER TWO WITH STROKE;Lo;0;L;;;;;N;LATIN LETTER TWO BAR;;;;
+01BC;LATIN CAPITAL LETTER TONE FIVE;Lu;0;L;;;;;N;;;;01BD;
+01BD;LATIN SMALL LETTER TONE FIVE;Ll;0;L;;;;;N;;;01BC;;01BC
+01BE;LATIN LETTER INVERTED GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER INVERTED GLOTTAL STOP BAR;;;;
+01BF;LATIN LETTER WYNN;Ll;0;L;;;;;N;;;01F7;;01F7
+01C0;LATIN LETTER DENTAL CLICK;Lo;0;L;;;;;N;LATIN LETTER PIPE;;;;
+01C1;LATIN LETTER LATERAL CLICK;Lo;0;L;;;;;N;LATIN LETTER DOUBLE PIPE;;;;
+01C2;LATIN LETTER ALVEOLAR CLICK;Lo;0;L;;;;;N;LATIN LETTER PIPE DOUBLE BAR;;;;
+01C3;LATIN LETTER RETROFLEX CLICK;Lo;0;L;;;;;N;LATIN LETTER EXCLAMATION MARK;;;;
+01C4;LATIN CAPITAL LETTER DZ WITH CARON;Lu;0;L;<compat> 0044 017D;;;;N;LATIN CAPITAL LETTER D Z HACEK;;;01C6;01C5
+01C5;LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON;Lt;0;L;<compat> 0044 017E;;;;N;LATIN LETTER CAPITAL D SMALL Z HACEK;;01C4;01C6;01C5
+01C6;LATIN SMALL LETTER DZ WITH CARON;Ll;0;L;<compat> 0064 017E;;;;N;LATIN SMALL LETTER D Z HACEK;;01C4;;01C5
+01C7;LATIN CAPITAL LETTER LJ;Lu;0;L;<compat> 004C 004A;;;;N;LATIN CAPITAL LETTER L J;;;01C9;01C8
+01C8;LATIN CAPITAL LETTER L WITH SMALL LETTER J;Lt;0;L;<compat> 004C 006A;;;;N;LATIN LETTER CAPITAL L SMALL J;;01C7;01C9;01C8
+01C9;LATIN SMALL LETTER LJ;Ll;0;L;<compat> 006C 006A;;;;N;LATIN SMALL LETTER L J;;01C7;;01C8
+01CA;LATIN CAPITAL LETTER NJ;Lu;0;L;<compat> 004E 004A;;;;N;LATIN CAPITAL LETTER N J;;;01CC;01CB
+01CB;LATIN CAPITAL LETTER N WITH SMALL LETTER J;Lt;0;L;<compat> 004E 006A;;;;N;LATIN LETTER CAPITAL N SMALL J;;01CA;01CC;01CB
+01CC;LATIN SMALL LETTER NJ;Ll;0;L;<compat> 006E 006A;;;;N;LATIN SMALL LETTER N J;;01CA;;01CB
+01CD;LATIN CAPITAL LETTER A WITH CARON;Lu;0;L;0041 030C;;;;N;LATIN CAPITAL LETTER A HACEK;;;01CE;
+01CE;LATIN SMALL LETTER A WITH CARON;Ll;0;L;0061 030C;;;;N;LATIN SMALL LETTER A HACEK;;01CD;;01CD
+01CF;LATIN CAPITAL LETTER I WITH CARON;Lu;0;L;0049 030C;;;;N;LATIN CAPITAL LETTER I HACEK;;;01D0;
+01D0;LATIN SMALL LETTER I WITH CARON;Ll;0;L;0069 030C;;;;N;LATIN SMALL LETTER I HACEK;;01CF;;01CF
+01D1;LATIN CAPITAL LETTER O WITH CARON;Lu;0;L;004F 030C;;;;N;LATIN CAPITAL LETTER O HACEK;;;01D2;
+01D2;LATIN SMALL LETTER O WITH CARON;Ll;0;L;006F 030C;;;;N;LATIN SMALL LETTER O HACEK;;01D1;;01D1
+01D3;LATIN CAPITAL LETTER U WITH CARON;Lu;0;L;0055 030C;;;;N;LATIN CAPITAL LETTER U HACEK;;;01D4;
+01D4;LATIN SMALL LETTER U WITH CARON;Ll;0;L;0075 030C;;;;N;LATIN SMALL LETTER U HACEK;;01D3;;01D3
+01D5;LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON;Lu;0;L;00DC 0304;;;;N;LATIN CAPITAL LETTER U DIAERESIS MACRON;;;01D6;
+01D6;LATIN SMALL LETTER U WITH DIAERESIS AND MACRON;Ll;0;L;00FC 0304;;;;N;LATIN SMALL LETTER U DIAERESIS MACRON;;01D5;;01D5
+01D7;LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE;Lu;0;L;00DC 0301;;;;N;LATIN CAPITAL LETTER U DIAERESIS ACUTE;;;01D8;
+01D8;LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE;Ll;0;L;00FC 0301;;;;N;LATIN SMALL LETTER U DIAERESIS ACUTE;;01D7;;01D7
+01D9;LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON;Lu;0;L;00DC 030C;;;;N;LATIN CAPITAL LETTER U DIAERESIS HACEK;;;01DA;
+01DA;LATIN SMALL LETTER U WITH DIAERESIS AND CARON;Ll;0;L;00FC 030C;;;;N;LATIN SMALL LETTER U DIAERESIS HACEK;;01D9;;01D9
+01DB;LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE;Lu;0;L;00DC 0300;;;;N;LATIN CAPITAL LETTER U DIAERESIS GRAVE;;;01DC;
+01DC;LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE;Ll;0;L;00FC 0300;;;;N;LATIN SMALL LETTER U DIAERESIS GRAVE;;01DB;;01DB
+01DD;LATIN SMALL LETTER TURNED E;Ll;0;L;;;;;N;;;018E;;018E
+01DE;LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON;Lu;0;L;00C4 0304;;;;N;LATIN CAPITAL LETTER A DIAERESIS MACRON;;;01DF;
+01DF;LATIN SMALL LETTER A WITH DIAERESIS AND MACRON;Ll;0;L;00E4 0304;;;;N;LATIN SMALL LETTER A DIAERESIS MACRON;;01DE;;01DE
+01E0;LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON;Lu;0;L;0226 0304;;;;N;LATIN CAPITAL LETTER A DOT MACRON;;;01E1;
+01E1;LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON;Ll;0;L;0227 0304;;;;N;LATIN SMALL LETTER A DOT MACRON;;01E0;;01E0
+01E2;LATIN CAPITAL LETTER AE WITH MACRON;Lu;0;L;00C6 0304;;;;N;LATIN CAPITAL LETTER A E MACRON;;;01E3;
+01E3;LATIN SMALL LETTER AE WITH MACRON;Ll;0;L;00E6 0304;;;;N;LATIN SMALL LETTER A E MACRON;;01E2;;01E2
+01E4;LATIN CAPITAL LETTER G WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER G BAR;;;01E5;
+01E5;LATIN SMALL LETTER G WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER G BAR;;01E4;;01E4
+01E6;LATIN CAPITAL LETTER G WITH CARON;Lu;0;L;0047 030C;;;;N;LATIN CAPITAL LETTER G HACEK;;;01E7;
+01E7;LATIN SMALL LETTER G WITH CARON;Ll;0;L;0067 030C;;;;N;LATIN SMALL LETTER G HACEK;;01E6;;01E6
+01E8;LATIN CAPITAL LETTER K WITH CARON;Lu;0;L;004B 030C;;;;N;LATIN CAPITAL LETTER K HACEK;;;01E9;
+01E9;LATIN SMALL LETTER K WITH CARON;Ll;0;L;006B 030C;;;;N;LATIN SMALL LETTER K HACEK;;01E8;;01E8
+01EA;LATIN CAPITAL LETTER O WITH OGONEK;Lu;0;L;004F 0328;;;;N;LATIN CAPITAL LETTER O OGONEK;;;01EB;
+01EB;LATIN SMALL LETTER O WITH OGONEK;Ll;0;L;006F 0328;;;;N;LATIN SMALL LETTER O OGONEK;;01EA;;01EA
+01EC;LATIN CAPITAL LETTER O WITH OGONEK AND MACRON;Lu;0;L;01EA 0304;;;;N;LATIN CAPITAL LETTER O OGONEK MACRON;;;01ED;
+01ED;LATIN SMALL LETTER O WITH OGONEK AND MACRON;Ll;0;L;01EB 0304;;;;N;LATIN SMALL LETTER O OGONEK MACRON;;01EC;;01EC
+01EE;LATIN CAPITAL LETTER EZH WITH CARON;Lu;0;L;01B7 030C;;;;N;LATIN CAPITAL LETTER YOGH HACEK;;;01EF;
+01EF;LATIN SMALL LETTER EZH WITH CARON;Ll;0;L;0292 030C;;;;N;LATIN SMALL LETTER YOGH HACEK;;01EE;;01EE
+01F0;LATIN SMALL LETTER J WITH CARON;Ll;0;L;006A 030C;;;;N;LATIN SMALL LETTER J HACEK;;;;
+01F1;LATIN CAPITAL LETTER DZ;Lu;0;L;<compat> 0044 005A;;;;N;;;;01F3;01F2
+01F2;LATIN CAPITAL LETTER D WITH SMALL LETTER Z;Lt;0;L;<compat> 0044 007A;;;;N;;;01F1;01F3;01F2
+01F3;LATIN SMALL LETTER DZ;Ll;0;L;<compat> 0064 007A;;;;N;;;01F1;;01F2
+01F4;LATIN CAPITAL LETTER G WITH ACUTE;Lu;0;L;0047 0301;;;;N;;;;01F5;
+01F5;LATIN SMALL LETTER G WITH ACUTE;Ll;0;L;0067 0301;;;;N;;;01F4;;01F4
+01F6;LATIN CAPITAL LETTER HWAIR;Lu;0;L;;;;;N;;;;0195;
+01F7;LATIN CAPITAL LETTER WYNN;Lu;0;L;;;;;N;;;;01BF;
+01F8;LATIN CAPITAL LETTER N WITH GRAVE;Lu;0;L;004E 0300;;;;N;;;;01F9;
+01F9;LATIN SMALL LETTER N WITH GRAVE;Ll;0;L;006E 0300;;;;N;;;01F8;;01F8
+01FA;LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE;Lu;0;L;00C5 0301;;;;N;;;;01FB;
+01FB;LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE;Ll;0;L;00E5 0301;;;;N;;;01FA;;01FA
+01FC;LATIN CAPITAL LETTER AE WITH ACUTE;Lu;0;L;00C6 0301;;;;N;;;;01FD;
+01FD;LATIN SMALL LETTER AE WITH ACUTE;Ll;0;L;00E6 0301;;;;N;;;01FC;;01FC
+01FE;LATIN CAPITAL LETTER O WITH STROKE AND ACUTE;Lu;0;L;00D8 0301;;;;N;;;;01FF;
+01FF;LATIN SMALL LETTER O WITH STROKE AND ACUTE;Ll;0;L;00F8 0301;;;;N;;;01FE;;01FE
+0200;LATIN CAPITAL LETTER A WITH DOUBLE GRAVE;Lu;0;L;0041 030F;;;;N;;;;0201;
+0201;LATIN SMALL LETTER A WITH DOUBLE GRAVE;Ll;0;L;0061 030F;;;;N;;;0200;;0200
+0202;LATIN CAPITAL LETTER A WITH INVERTED BREVE;Lu;0;L;0041 0311;;;;N;;;;0203;
+0203;LATIN SMALL LETTER A WITH INVERTED BREVE;Ll;0;L;0061 0311;;;;N;;;0202;;0202
+0204;LATIN CAPITAL LETTER E WITH DOUBLE GRAVE;Lu;0;L;0045 030F;;;;N;;;;0205;
+0205;LATIN SMALL LETTER E WITH DOUBLE GRAVE;Ll;0;L;0065 030F;;;;N;;;0204;;0204
+0206;LATIN CAPITAL LETTER E WITH INVERTED BREVE;Lu;0;L;0045 0311;;;;N;;;;0207;
+0207;LATIN SMALL LETTER E WITH INVERTED BREVE;Ll;0;L;0065 0311;;;;N;;;0206;;0206
+0208;LATIN CAPITAL LETTER I WITH DOUBLE GRAVE;Lu;0;L;0049 030F;;;;N;;;;0209;
+0209;LATIN SMALL LETTER I WITH DOUBLE GRAVE;Ll;0;L;0069 030F;;;;N;;;0208;;0208
+020A;LATIN CAPITAL LETTER I WITH INVERTED BREVE;Lu;0;L;0049 0311;;;;N;;;;020B;
+020B;LATIN SMALL LETTER I WITH INVERTED BREVE;Ll;0;L;0069 0311;;;;N;;;020A;;020A
+020C;LATIN CAPITAL LETTER O WITH DOUBLE GRAVE;Lu;0;L;004F 030F;;;;N;;;;020D;
+020D;LATIN SMALL LETTER O WITH DOUBLE GRAVE;Ll;0;L;006F 030F;;;;N;;;020C;;020C
+020E;LATIN CAPITAL LETTER O WITH INVERTED BREVE;Lu;0;L;004F 0311;;;;N;;;;020F;
+020F;LATIN SMALL LETTER O WITH INVERTED BREVE;Ll;0;L;006F 0311;;;;N;;;020E;;020E
+0210;LATIN CAPITAL LETTER R WITH DOUBLE GRAVE;Lu;0;L;0052 030F;;;;N;;;;0211;
+0211;LATIN SMALL LETTER R WITH DOUBLE GRAVE;Ll;0;L;0072 030F;;;;N;;;0210;;0210
+0212;LATIN CAPITAL LETTER R WITH INVERTED BREVE;Lu;0;L;0052 0311;;;;N;;;;0213;
+0213;LATIN SMALL LETTER R WITH INVERTED BREVE;Ll;0;L;0072 0311;;;;N;;;0212;;0212
+0214;LATIN CAPITAL LETTER U WITH DOUBLE GRAVE;Lu;0;L;0055 030F;;;;N;;;;0215;
+0215;LATIN SMALL LETTER U WITH DOUBLE GRAVE;Ll;0;L;0075 030F;;;;N;;;0214;;0214
+0216;LATIN CAPITAL LETTER U WITH INVERTED BREVE;Lu;0;L;0055 0311;;;;N;;;;0217;
+0217;LATIN SMALL LETTER U WITH INVERTED BREVE;Ll;0;L;0075 0311;;;;N;;;0216;;0216
+0218;LATIN CAPITAL LETTER S WITH COMMA BELOW;Lu;0;L;0053 0326;;;;N;;;;0219;
+0219;LATIN SMALL LETTER S WITH COMMA BELOW;Ll;0;L;0073 0326;;;;N;;;0218;;0218
+021A;LATIN CAPITAL LETTER T WITH COMMA BELOW;Lu;0;L;0054 0326;;;;N;;;;021B;
+021B;LATIN SMALL LETTER T WITH COMMA BELOW;Ll;0;L;0074 0326;;;;N;;;021A;;021A
+021C;LATIN CAPITAL LETTER YOGH;Lu;0;L;;;;;N;;;;021D;
+021D;LATIN SMALL LETTER YOGH;Ll;0;L;;;;;N;;;021C;;021C
+021E;LATIN CAPITAL LETTER H WITH CARON;Lu;0;L;0048 030C;;;;N;;;;021F;
+021F;LATIN SMALL LETTER H WITH CARON;Ll;0;L;0068 030C;;;;N;;;021E;;021E
+0220;LATIN CAPITAL LETTER N WITH LONG RIGHT LEG;Lu;0;L;;;;;N;;;;019E;
+0221;LATIN SMALL LETTER D WITH CURL;Ll;0;L;;;;;N;;;;;
+0222;LATIN CAPITAL LETTER OU;Lu;0;L;;;;;N;;;;0223;
+0223;LATIN SMALL LETTER OU;Ll;0;L;;;;;N;;;0222;;0222
+0224;LATIN CAPITAL LETTER Z WITH HOOK;Lu;0;L;;;;;N;;;;0225;
+0225;LATIN SMALL LETTER Z WITH HOOK;Ll;0;L;;;;;N;;;0224;;0224
+0226;LATIN CAPITAL LETTER A WITH DOT ABOVE;Lu;0;L;0041 0307;;;;N;;;;0227;
+0227;LATIN SMALL LETTER A WITH DOT ABOVE;Ll;0;L;0061 0307;;;;N;;;0226;;0226
+0228;LATIN CAPITAL LETTER E WITH CEDILLA;Lu;0;L;0045 0327;;;;N;;;;0229;
+0229;LATIN SMALL LETTER E WITH CEDILLA;Ll;0;L;0065 0327;;;;N;;;0228;;0228
+022A;LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON;Lu;0;L;00D6 0304;;;;N;;;;022B;
+022B;LATIN SMALL LETTER O WITH DIAERESIS AND MACRON;Ll;0;L;00F6 0304;;;;N;;;022A;;022A
+022C;LATIN CAPITAL LETTER O WITH TILDE AND MACRON;Lu;0;L;00D5 0304;;;;N;;;;022D;
+022D;LATIN SMALL LETTER O WITH TILDE AND MACRON;Ll;0;L;00F5 0304;;;;N;;;022C;;022C
+022E;LATIN CAPITAL LETTER O WITH DOT ABOVE;Lu;0;L;004F 0307;;;;N;;;;022F;
+022F;LATIN SMALL LETTER O WITH DOT ABOVE;Ll;0;L;006F 0307;;;;N;;;022E;;022E
+0230;LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON;Lu;0;L;022E 0304;;;;N;;;;0231;
+0231;LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON;Ll;0;L;022F 0304;;;;N;;;0230;;0230
+0232;LATIN CAPITAL LETTER Y WITH MACRON;Lu;0;L;0059 0304;;;;N;;;;0233;
+0233;LATIN SMALL LETTER Y WITH MACRON;Ll;0;L;0079 0304;;;;N;;;0232;;0232
+0234;LATIN SMALL LETTER L WITH CURL;Ll;0;L;;;;;N;;;;;
+0235;LATIN SMALL LETTER N WITH CURL;Ll;0;L;;;;;N;;;;;
+0236;LATIN SMALL LETTER T WITH CURL;Ll;0;L;;;;;N;;;;;
+0237;LATIN SMALL LETTER DOTLESS J;Ll;0;L;;;;;N;;;;;
+0238;LATIN SMALL LETTER DB DIGRAPH;Ll;0;L;;;;;N;;;;;
+0239;LATIN SMALL LETTER QP DIGRAPH;Ll;0;L;;;;;N;;;;;
+023A;LATIN CAPITAL LETTER A WITH STROKE;Lu;0;L;;;;;N;;;;2C65;
+023B;LATIN CAPITAL LETTER C WITH STROKE;Lu;0;L;;;;;N;;;;023C;
+023C;LATIN SMALL LETTER C WITH STROKE;Ll;0;L;;;;;N;;;023B;;023B
+023D;LATIN CAPITAL LETTER L WITH BAR;Lu;0;L;;;;;N;;;;019A;
+023E;LATIN CAPITAL LETTER T WITH DIAGONAL STROKE;Lu;0;L;;;;;N;;;;2C66;
+023F;LATIN SMALL LETTER S WITH SWASH TAIL;Ll;0;L;;;;;N;;;2C7E;;2C7E
+0240;LATIN SMALL LETTER Z WITH SWASH TAIL;Ll;0;L;;;;;N;;;2C7F;;2C7F
+0241;LATIN CAPITAL LETTER GLOTTAL STOP;Lu;0;L;;;;;N;;;;0242;
+0242;LATIN SMALL LETTER GLOTTAL STOP;Ll;0;L;;;;;N;;;0241;;0241
+0243;LATIN CAPITAL LETTER B WITH STROKE;Lu;0;L;;;;;N;;;;0180;
+0244;LATIN CAPITAL LETTER U BAR;Lu;0;L;;;;;N;;;;0289;
+0245;LATIN CAPITAL LETTER TURNED V;Lu;0;L;;;;;N;;;;028C;
+0246;LATIN CAPITAL LETTER E WITH STROKE;Lu;0;L;;;;;N;;;;0247;
+0247;LATIN SMALL LETTER E WITH STROKE;Ll;0;L;;;;;N;;;0246;;0246
+0248;LATIN CAPITAL LETTER J WITH STROKE;Lu;0;L;;;;;N;;;;0249;
+0249;LATIN SMALL LETTER J WITH STROKE;Ll;0;L;;;;;N;;;0248;;0248
+024A;LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL;Lu;0;L;;;;;N;;;;024B;
+024B;LATIN SMALL LETTER Q WITH HOOK TAIL;Ll;0;L;;;;;N;;;024A;;024A
+024C;LATIN CAPITAL LETTER R WITH STROKE;Lu;0;L;;;;;N;;;;024D;
+024D;LATIN SMALL LETTER R WITH STROKE;Ll;0;L;;;;;N;;;024C;;024C
+024E;LATIN CAPITAL LETTER Y WITH STROKE;Lu;0;L;;;;;N;;;;024F;
+024F;LATIN SMALL LETTER Y WITH STROKE;Ll;0;L;;;;;N;;;024E;;024E
+0250;LATIN SMALL LETTER TURNED A;Ll;0;L;;;;;N;;;2C6F;;2C6F
+0251;LATIN SMALL LETTER ALPHA;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT A;;2C6D;;2C6D
+0252;LATIN SMALL LETTER TURNED ALPHA;Ll;0;L;;;;;N;LATIN SMALL LETTER TURNED SCRIPT A;;2C70;;2C70
+0253;LATIN SMALL LETTER B WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER B HOOK;;0181;;0181
+0254;LATIN SMALL LETTER OPEN O;Ll;0;L;;;;;N;;;0186;;0186
+0255;LATIN SMALL LETTER C WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER C CURL;;;;
+0256;LATIN SMALL LETTER D WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER D RETROFLEX HOOK;;0189;;0189
+0257;LATIN SMALL LETTER D WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER D HOOK;;018A;;018A
+0258;LATIN SMALL LETTER REVERSED E;Ll;0;L;;;;;N;;;;;
+0259;LATIN SMALL LETTER SCHWA;Ll;0;L;;;;;N;;;018F;;018F
+025A;LATIN SMALL LETTER SCHWA WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCHWA HOOK;;;;
+025B;LATIN SMALL LETTER OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER EPSILON;;0190;;0190
+025C;LATIN SMALL LETTER REVERSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED EPSILON;;A7AB;;A7AB
+025D;LATIN SMALL LETTER REVERSED OPEN E WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED EPSILON HOOK;;;;
+025E;LATIN SMALL LETTER CLOSED REVERSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER CLOSED REVERSED EPSILON;;;;
+025F;LATIN SMALL LETTER DOTLESS J WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER DOTLESS J BAR;;;;
+0260;LATIN SMALL LETTER G WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER G HOOK;;0193;;0193
+0261;LATIN SMALL LETTER SCRIPT G;Ll;0;L;;;;;N;;;A7AC;;A7AC
+0262;LATIN LETTER SMALL CAPITAL G;Ll;0;L;;;;;N;;;;;
+0263;LATIN SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;0194;;0194
+0264;LATIN SMALL LETTER RAMS HORN;Ll;0;L;;;;;N;LATIN SMALL LETTER BABY GAMMA;;;;
+0265;LATIN SMALL LETTER TURNED H;Ll;0;L;;;;;N;;;A78D;;A78D
+0266;LATIN SMALL LETTER H WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER H HOOK;;A7AA;;A7AA
+0267;LATIN SMALL LETTER HENG WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER HENG HOOK;;;;
+0268;LATIN SMALL LETTER I WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED I;;0197;;0197
+0269;LATIN SMALL LETTER IOTA;Ll;0;L;;;;;N;;;0196;;0196
+026A;LATIN LETTER SMALL CAPITAL I;Ll;0;L;;;;;N;;;A7AE;;A7AE
+026B;LATIN SMALL LETTER L WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;2C62;;2C62
+026C;LATIN SMALL LETTER L WITH BELT;Ll;0;L;;;;;N;LATIN SMALL LETTER L BELT;;A7AD;;A7AD
+026D;LATIN SMALL LETTER L WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER L RETROFLEX HOOK;;;;
+026E;LATIN SMALL LETTER LEZH;Ll;0;L;;;;;N;LATIN SMALL LETTER L YOGH;;;;
+026F;LATIN SMALL LETTER TURNED M;Ll;0;L;;;;;N;;;019C;;019C
+0270;LATIN SMALL LETTER TURNED M WITH LONG LEG;Ll;0;L;;;;;N;;;;;
+0271;LATIN SMALL LETTER M WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER M HOOK;;2C6E;;2C6E
+0272;LATIN SMALL LETTER N WITH LEFT HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER N HOOK;;019D;;019D
+0273;LATIN SMALL LETTER N WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER N RETROFLEX HOOK;;;;
+0274;LATIN LETTER SMALL CAPITAL N;Ll;0;L;;;;;N;;;;;
+0275;LATIN SMALL LETTER BARRED O;Ll;0;L;;;;;N;;;019F;;019F
+0276;LATIN LETTER SMALL CAPITAL OE;Ll;0;L;;;;;N;LATIN LETTER SMALL CAPITAL O E;;;;
+0277;LATIN SMALL LETTER CLOSED OMEGA;Ll;0;L;;;;;N;;;;;
+0278;LATIN SMALL LETTER PHI;Ll;0;L;;;;;N;;;;;
+0279;LATIN SMALL LETTER TURNED R;Ll;0;L;;;;;N;;;;;
+027A;LATIN SMALL LETTER TURNED R WITH LONG LEG;Ll;0;L;;;;;N;;;;;
+027B;LATIN SMALL LETTER TURNED R WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER TURNED R HOOK;;;;
+027C;LATIN SMALL LETTER R WITH LONG LEG;Ll;0;L;;;;;N;;;;;
+027D;LATIN SMALL LETTER R WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER R HOOK;;2C64;;2C64
+027E;LATIN SMALL LETTER R WITH FISHHOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER FISHHOOK R;;;;
+027F;LATIN SMALL LETTER REVERSED R WITH FISHHOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED FISHHOOK R;;;;
+0280;LATIN LETTER SMALL CAPITAL R;Ll;0;L;;;;;N;;;01A6;;01A6
+0281;LATIN LETTER SMALL CAPITAL INVERTED R;Ll;0;L;;;;;N;;;;;
+0282;LATIN SMALL LETTER S WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER S HOOK;;A7C5;;A7C5
+0283;LATIN SMALL LETTER ESH;Ll;0;L;;;;;N;;;01A9;;01A9
+0284;LATIN SMALL LETTER DOTLESS J WITH STROKE AND HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER DOTLESS J BAR HOOK;;;;
+0285;LATIN SMALL LETTER SQUAT REVERSED ESH;Ll;0;L;;;;;N;;;;;
+0286;LATIN SMALL LETTER ESH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER ESH CURL;;;;
+0287;LATIN SMALL LETTER TURNED T;Ll;0;L;;;;;N;;;A7B1;;A7B1
+0288;LATIN SMALL LETTER T WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T RETROFLEX HOOK;;01AE;;01AE
+0289;LATIN SMALL LETTER U BAR;Ll;0;L;;;;;N;;;0244;;0244
+028A;LATIN SMALL LETTER UPSILON;Ll;0;L;;;;;N;;;01B1;;01B1
+028B;LATIN SMALL LETTER V WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT V;;01B2;;01B2
+028C;LATIN SMALL LETTER TURNED V;Ll;0;L;;;;;N;;;0245;;0245
+028D;LATIN SMALL LETTER TURNED W;Ll;0;L;;;;;N;;;;;
+028E;LATIN SMALL LETTER TURNED Y;Ll;0;L;;;;;N;;;;;
+028F;LATIN LETTER SMALL CAPITAL Y;Ll;0;L;;;;;N;;;;;
+0290;LATIN SMALL LETTER Z WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Z RETROFLEX HOOK;;;;
+0291;LATIN SMALL LETTER Z WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER Z CURL;;;;
+0292;LATIN SMALL LETTER EZH;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH;;01B7;;01B7
+0293;LATIN SMALL LETTER EZH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH CURL;;;;
+0294;LATIN LETTER GLOTTAL STOP;Lo;0;L;;;;;N;;;;;
+0295;LATIN LETTER PHARYNGEAL VOICED FRICATIVE;Ll;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP;;;;
+0296;LATIN LETTER INVERTED GLOTTAL STOP;Ll;0;L;;;;;N;;;;;
+0297;LATIN LETTER STRETCHED C;Ll;0;L;;;;;N;;;;;
+0298;LATIN LETTER BILABIAL CLICK;Ll;0;L;;;;;N;LATIN LETTER BULLSEYE;;;;
+0299;LATIN LETTER SMALL CAPITAL B;Ll;0;L;;;;;N;;;;;
+029A;LATIN SMALL LETTER CLOSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER CLOSED EPSILON;;;;
+029B;LATIN LETTER SMALL CAPITAL G WITH HOOK;Ll;0;L;;;;;N;LATIN LETTER SMALL CAPITAL G HOOK;;;;
+029C;LATIN LETTER SMALL CAPITAL H;Ll;0;L;;;;;N;;;;;
+029D;LATIN SMALL LETTER J WITH CROSSED-TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER CROSSED-TAIL J;;A7B2;;A7B2
+029E;LATIN SMALL LETTER TURNED K;Ll;0;L;;;;;N;;;A7B0;;A7B0
+029F;LATIN LETTER SMALL CAPITAL L;Ll;0;L;;;;;N;;;;;
+02A0;LATIN SMALL LETTER Q WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Q HOOK;;;;
+02A1;LATIN LETTER GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER GLOTTAL STOP BAR;;;;
+02A2;LATIN LETTER REVERSED GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP BAR;;;;
+02A3;LATIN SMALL LETTER DZ DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER D Z;;;;
+02A4;LATIN SMALL LETTER DEZH DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER D YOGH;;;;
+02A5;LATIN SMALL LETTER DZ DIGRAPH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER D Z CURL;;;;
+02A6;LATIN SMALL LETTER TS DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER T S;;;;
+02A7;LATIN SMALL LETTER TESH DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER T ESH;;;;
+02A8;LATIN SMALL LETTER TC DIGRAPH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER T C CURL;;;;
+02A9;LATIN SMALL LETTER FENG DIGRAPH;Ll;0;L;;;;;N;;;;;
+02AA;LATIN SMALL LETTER LS DIGRAPH;Ll;0;L;;;;;N;;;;;
+02AB;LATIN SMALL LETTER LZ DIGRAPH;Ll;0;L;;;;;N;;;;;
+02AC;LATIN LETTER BILABIAL PERCUSSIVE;Ll;0;L;;;;;N;;;;;
+02AD;LATIN LETTER BIDENTAL PERCUSSIVE;Ll;0;L;;;;;N;;;;;
+02AE;LATIN SMALL LETTER TURNED H WITH FISHHOOK;Ll;0;L;;;;;N;;;;;
+02AF;LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL;Ll;0;L;;;;;N;;;;;
+02B0;MODIFIER LETTER SMALL H;Lm;0;L;<super> 0068;;;;N;;;;;
+02B1;MODIFIER LETTER SMALL H WITH HOOK;Lm;0;L;<super> 0266;;;;N;MODIFIER LETTER SMALL H HOOK;;;;
+02B2;MODIFIER LETTER SMALL J;Lm;0;L;<super> 006A;;;;N;;;;;
+02B3;MODIFIER LETTER SMALL R;Lm;0;L;<super> 0072;;;;N;;;;;
+02B4;MODIFIER LETTER SMALL TURNED R;Lm;0;L;<super> 0279;;;;N;;;;;
+02B5;MODIFIER LETTER SMALL TURNED R WITH HOOK;Lm;0;L;<super> 027B;;;;N;MODIFIER LETTER SMALL TURNED R HOOK;;;;
+02B6;MODIFIER LETTER SMALL CAPITAL INVERTED R;Lm;0;L;<super> 0281;;;;N;;;;;
+02B7;MODIFIER LETTER SMALL W;Lm;0;L;<super> 0077;;;;N;;;;;
+02B8;MODIFIER LETTER SMALL Y;Lm;0;L;<super> 0079;;;;N;;;;;
+02B9;MODIFIER LETTER PRIME;Lm;0;ON;;;;;N;;;;;
+02BA;MODIFIER LETTER DOUBLE PRIME;Lm;0;ON;;;;;N;;;;;
+02BB;MODIFIER LETTER TURNED COMMA;Lm;0;L;;;;;N;;;;;
+02BC;MODIFIER LETTER APOSTROPHE;Lm;0;L;;;;;N;;;;;
+02BD;MODIFIER LETTER REVERSED COMMA;Lm;0;L;;;;;N;;;;;
+02BE;MODIFIER LETTER RIGHT HALF RING;Lm;0;L;;;;;N;;;;;
+02BF;MODIFIER LETTER LEFT HALF RING;Lm;0;L;;;;;N;;;;;
+02C0;MODIFIER LETTER GLOTTAL STOP;Lm;0;L;;;;;N;;;;;
+02C1;MODIFIER LETTER REVERSED GLOTTAL STOP;Lm;0;L;;;;;N;;;;;
+02C2;MODIFIER LETTER LEFT ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02C3;MODIFIER LETTER RIGHT ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02C4;MODIFIER LETTER UP ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02C5;MODIFIER LETTER DOWN ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02C6;MODIFIER LETTER CIRCUMFLEX ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER CIRCUMFLEX;;;;
+02C7;CARON;Lm;0;ON;;;;;N;MODIFIER LETTER HACEK;;;;
+02C8;MODIFIER LETTER VERTICAL LINE;Lm;0;ON;;;;;N;;;;;
+02C9;MODIFIER LETTER MACRON;Lm;0;ON;;;;;N;;;;;
+02CA;MODIFIER LETTER ACUTE ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER ACUTE;;;;
+02CB;MODIFIER LETTER GRAVE ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER GRAVE;;;;
+02CC;MODIFIER LETTER LOW VERTICAL LINE;Lm;0;ON;;;;;N;;;;;
+02CD;MODIFIER LETTER LOW MACRON;Lm;0;ON;;;;;N;;;;;
+02CE;MODIFIER LETTER LOW GRAVE ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER LOW GRAVE;;;;
+02CF;MODIFIER LETTER LOW ACUTE ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER LOW ACUTE;;;;
+02D0;MODIFIER LETTER TRIANGULAR COLON;Lm;0;L;;;;;N;;;;;
+02D1;MODIFIER LETTER HALF TRIANGULAR COLON;Lm;0;L;;;;;N;;;;;
+02D2;MODIFIER LETTER CENTRED RIGHT HALF RING;Sk;0;ON;;;;;N;MODIFIER LETTER CENTERED RIGHT HALF RING;;;;
+02D3;MODIFIER LETTER CENTRED LEFT HALF RING;Sk;0;ON;;;;;N;MODIFIER LETTER CENTERED LEFT HALF RING;;;;
+02D4;MODIFIER LETTER UP TACK;Sk;0;ON;;;;;N;;;;;
+02D5;MODIFIER LETTER DOWN TACK;Sk;0;ON;;;;;N;;;;;
+02D6;MODIFIER LETTER PLUS SIGN;Sk;0;ON;;;;;N;;;;;
+02D7;MODIFIER LETTER MINUS SIGN;Sk;0;ON;;;;;N;;;;;
+02D8;BREVE;Sk;0;ON;<compat> 0020 0306;;;;N;SPACING BREVE;;;;
+02D9;DOT ABOVE;Sk;0;ON;<compat> 0020 0307;;;;N;SPACING DOT ABOVE;;;;
+02DA;RING ABOVE;Sk;0;ON;<compat> 0020 030A;;;;N;SPACING RING ABOVE;;;;
+02DB;OGONEK;Sk;0;ON;<compat> 0020 0328;;;;N;SPACING OGONEK;;;;
+02DC;SMALL TILDE;Sk;0;ON;<compat> 0020 0303;;;;N;SPACING TILDE;;;;
+02DD;DOUBLE ACUTE ACCENT;Sk;0;ON;<compat> 0020 030B;;;;N;SPACING DOUBLE ACUTE;;;;
+02DE;MODIFIER LETTER RHOTIC HOOK;Sk;0;ON;;;;;N;;;;;
+02DF;MODIFIER LETTER CROSS ACCENT;Sk;0;ON;;;;;N;;;;;
+02E0;MODIFIER LETTER SMALL GAMMA;Lm;0;L;<super> 0263;;;;N;;;;;
+02E1;MODIFIER LETTER SMALL L;Lm;0;L;<super> 006C;;;;N;;;;;
+02E2;MODIFIER LETTER SMALL S;Lm;0;L;<super> 0073;;;;N;;;;;
+02E3;MODIFIER LETTER SMALL X;Lm;0;L;<super> 0078;;;;N;;;;;
+02E4;MODIFIER LETTER SMALL REVERSED GLOTTAL STOP;Lm;0;L;<super> 0295;;;;N;;;;;
+02E5;MODIFIER LETTER EXTRA-HIGH TONE BAR;Sk;0;ON;;;;;N;;;;;
+02E6;MODIFIER LETTER HIGH TONE BAR;Sk;0;ON;;;;;N;;;;;
+02E7;MODIFIER LETTER MID TONE BAR;Sk;0;ON;;;;;N;;;;;
+02E8;MODIFIER LETTER LOW TONE BAR;Sk;0;ON;;;;;N;;;;;
+02E9;MODIFIER LETTER EXTRA-LOW TONE BAR;Sk;0;ON;;;;;N;;;;;
+02EA;MODIFIER LETTER YIN DEPARTING TONE MARK;Sk;0;ON;;;;;N;;;;;
+02EB;MODIFIER LETTER YANG DEPARTING TONE MARK;Sk;0;ON;;;;;N;;;;;
+02EC;MODIFIER LETTER VOICING;Lm;0;ON;;;;;N;;;;;
+02ED;MODIFIER LETTER UNASPIRATED;Sk;0;ON;;;;;N;;;;;
+02EE;MODIFIER LETTER DOUBLE APOSTROPHE;Lm;0;L;;;;;N;;;;;
+02EF;MODIFIER LETTER LOW DOWN ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02F0;MODIFIER LETTER LOW UP ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02F1;MODIFIER LETTER LOW LEFT ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02F2;MODIFIER LETTER LOW RIGHT ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02F3;MODIFIER LETTER LOW RING;Sk;0;ON;;;;;N;;;;;
+02F4;MODIFIER LETTER MIDDLE GRAVE ACCENT;Sk;0;ON;;;;;N;;;;;
+02F5;MODIFIER LETTER MIDDLE DOUBLE GRAVE ACCENT;Sk;0;ON;;;;;N;;;;;
+02F6;MODIFIER LETTER MIDDLE DOUBLE ACUTE ACCENT;Sk;0;ON;;;;;N;;;;;
+02F7;MODIFIER LETTER LOW TILDE;Sk;0;ON;;;;;N;;;;;
+02F8;MODIFIER LETTER RAISED COLON;Sk;0;ON;;;;;N;;;;;
+02F9;MODIFIER LETTER BEGIN HIGH TONE;Sk;0;ON;;;;;N;;;;;
+02FA;MODIFIER LETTER END HIGH TONE;Sk;0;ON;;;;;N;;;;;
+02FB;MODIFIER LETTER BEGIN LOW TONE;Sk;0;ON;;;;;N;;;;;
+02FC;MODIFIER LETTER END LOW TONE;Sk;0;ON;;;;;N;;;;;
+02FD;MODIFIER LETTER SHELF;Sk;0;ON;;;;;N;;;;;
+02FE;MODIFIER LETTER OPEN SHELF;Sk;0;ON;;;;;N;;;;;
+02FF;MODIFIER LETTER LOW LEFT ARROW;Sk;0;ON;;;;;N;;;;;
+0300;COMBINING GRAVE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING GRAVE;;;;
+0301;COMBINING ACUTE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING ACUTE;;;;
+0302;COMBINING CIRCUMFLEX ACCENT;Mn;230;NSM;;;;;N;NON-SPACING CIRCUMFLEX;;;;
+0303;COMBINING TILDE;Mn;230;NSM;;;;;N;NON-SPACING TILDE;;;;
+0304;COMBINING MACRON;Mn;230;NSM;;;;;N;NON-SPACING MACRON;;;;
+0305;COMBINING OVERLINE;Mn;230;NSM;;;;;N;NON-SPACING OVERSCORE;;;;
+0306;COMBINING BREVE;Mn;230;NSM;;;;;N;NON-SPACING BREVE;;;;
+0307;COMBINING DOT ABOVE;Mn;230;NSM;;;;;N;NON-SPACING DOT ABOVE;;;;
+0308;COMBINING DIAERESIS;Mn;230;NSM;;;;;N;NON-SPACING DIAERESIS;;;;
+0309;COMBINING HOOK ABOVE;Mn;230;NSM;;;;;N;NON-SPACING HOOK ABOVE;;;;
+030A;COMBINING RING ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RING ABOVE;;;;
+030B;COMBINING DOUBLE ACUTE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE ACUTE;;;;
+030C;COMBINING CARON;Mn;230;NSM;;;;;N;NON-SPACING HACEK;;;;
+030D;COMBINING VERTICAL LINE ABOVE;Mn;230;NSM;;;;;N;NON-SPACING VERTICAL LINE ABOVE;;;;
+030E;COMBINING DOUBLE VERTICAL LINE ABOVE;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE VERTICAL LINE ABOVE;;;;
+030F;COMBINING DOUBLE GRAVE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE GRAVE;;;;
+0310;COMBINING CANDRABINDU;Mn;230;NSM;;;;;N;NON-SPACING CANDRABINDU;;;;
+0311;COMBINING INVERTED BREVE;Mn;230;NSM;;;;;N;NON-SPACING INVERTED BREVE;;;;
+0312;COMBINING TURNED COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING TURNED COMMA ABOVE;;;;
+0313;COMBINING COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING COMMA ABOVE;;;;
+0314;COMBINING REVERSED COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING REVERSED COMMA ABOVE;;;;
+0315;COMBINING COMMA ABOVE RIGHT;Mn;232;NSM;;;;;N;NON-SPACING COMMA ABOVE RIGHT;;;;
+0316;COMBINING GRAVE ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING GRAVE BELOW;;;;
+0317;COMBINING ACUTE ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING ACUTE BELOW;;;;
+0318;COMBINING LEFT TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING LEFT TACK BELOW;;;;
+0319;COMBINING RIGHT TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING RIGHT TACK BELOW;;;;
+031A;COMBINING LEFT ANGLE ABOVE;Mn;232;NSM;;;;;N;NON-SPACING LEFT ANGLE ABOVE;;;;
+031B;COMBINING HORN;Mn;216;NSM;;;;;N;NON-SPACING HORN;;;;
+031C;COMBINING LEFT HALF RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING LEFT HALF RING BELOW;;;;
+031D;COMBINING UP TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING UP TACK BELOW;;;;
+031E;COMBINING DOWN TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOWN TACK BELOW;;;;
+031F;COMBINING PLUS SIGN BELOW;Mn;220;NSM;;;;;N;NON-SPACING PLUS SIGN BELOW;;;;
+0320;COMBINING MINUS SIGN BELOW;Mn;220;NSM;;;;;N;NON-SPACING MINUS SIGN BELOW;;;;
+0321;COMBINING PALATALIZED HOOK BELOW;Mn;202;NSM;;;;;N;NON-SPACING PALATALIZED HOOK BELOW;;;;
+0322;COMBINING RETROFLEX HOOK BELOW;Mn;202;NSM;;;;;N;NON-SPACING RETROFLEX HOOK BELOW;;;;
+0323;COMBINING DOT BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOT BELOW;;;;
+0324;COMBINING DIAERESIS BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOUBLE DOT BELOW;;;;
+0325;COMBINING RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING RING BELOW;;;;
+0326;COMBINING COMMA BELOW;Mn;220;NSM;;;;;N;NON-SPACING COMMA BELOW;;;;
+0327;COMBINING CEDILLA;Mn;202;NSM;;;;;N;NON-SPACING CEDILLA;;;;
+0328;COMBINING OGONEK;Mn;202;NSM;;;;;N;NON-SPACING OGONEK;;;;
+0329;COMBINING VERTICAL LINE BELOW;Mn;220;NSM;;;;;N;NON-SPACING VERTICAL LINE BELOW;;;;
+032A;COMBINING BRIDGE BELOW;Mn;220;NSM;;;;;N;NON-SPACING BRIDGE BELOW;;;;
+032B;COMBINING INVERTED DOUBLE ARCH BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED DOUBLE ARCH BELOW;;;;
+032C;COMBINING CARON BELOW;Mn;220;NSM;;;;;N;NON-SPACING HACEK BELOW;;;;
+032D;COMBINING CIRCUMFLEX ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING CIRCUMFLEX BELOW;;;;
+032E;COMBINING BREVE BELOW;Mn;220;NSM;;;;;N;NON-SPACING BREVE BELOW;;;;
+032F;COMBINING INVERTED BREVE BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED BREVE BELOW;;;;
+0330;COMBINING TILDE BELOW;Mn;220;NSM;;;;;N;NON-SPACING TILDE BELOW;;;;
+0331;COMBINING MACRON BELOW;Mn;220;NSM;;;;;N;NON-SPACING MACRON BELOW;;;;
+0332;COMBINING LOW LINE;Mn;220;NSM;;;;;N;NON-SPACING UNDERSCORE;;;;
+0333;COMBINING DOUBLE LOW LINE;Mn;220;NSM;;;;;N;NON-SPACING DOUBLE UNDERSCORE;;;;
+0334;COMBINING TILDE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING TILDE OVERLAY;;;;
+0335;COMBINING SHORT STROKE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT BAR OVERLAY;;;;
+0336;COMBINING LONG STROKE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG BAR OVERLAY;;;;
+0337;COMBINING SHORT SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT SLASH OVERLAY;;;;
+0338;COMBINING LONG SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG SLASH OVERLAY;;;;
+0339;COMBINING RIGHT HALF RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING RIGHT HALF RING BELOW;;;;
+033A;COMBINING INVERTED BRIDGE BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED BRIDGE BELOW;;;;
+033B;COMBINING SQUARE BELOW;Mn;220;NSM;;;;;N;NON-SPACING SQUARE BELOW;;;;
+033C;COMBINING SEAGULL BELOW;Mn;220;NSM;;;;;N;NON-SPACING SEAGULL BELOW;;;;
+033D;COMBINING X ABOVE;Mn;230;NSM;;;;;N;NON-SPACING X ABOVE;;;;
+033E;COMBINING VERTICAL TILDE;Mn;230;NSM;;;;;N;NON-SPACING VERTICAL TILDE;;;;
+033F;COMBINING DOUBLE OVERLINE;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE OVERSCORE;;;;
+0340;COMBINING GRAVE TONE MARK;Mn;230;NSM;0300;;;;N;NON-SPACING GRAVE TONE MARK;;;;
+0341;COMBINING ACUTE TONE MARK;Mn;230;NSM;0301;;;;N;NON-SPACING ACUTE TONE MARK;;;;
+0342;COMBINING GREEK PERISPOMENI;Mn;230;NSM;;;;;N;;;;;
+0343;COMBINING GREEK KORONIS;Mn;230;NSM;0313;;;;N;;;;;
+0344;COMBINING GREEK DIALYTIKA TONOS;Mn;230;NSM;0308 0301;;;;N;GREEK NON-SPACING DIAERESIS TONOS;;;;
+0345;COMBINING GREEK YPOGEGRAMMENI;Mn;240;NSM;;;;;N;GREEK NON-SPACING IOTA BELOW;;0399;;0399
+0346;COMBINING BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;;
+0347;COMBINING EQUALS SIGN BELOW;Mn;220;NSM;;;;;N;;;;;
+0348;COMBINING DOUBLE VERTICAL LINE BELOW;Mn;220;NSM;;;;;N;;;;;
+0349;COMBINING LEFT ANGLE BELOW;Mn;220;NSM;;;;;N;;;;;
+034A;COMBINING NOT TILDE ABOVE;Mn;230;NSM;;;;;N;;;;;
+034B;COMBINING HOMOTHETIC ABOVE;Mn;230;NSM;;;;;N;;;;;
+034C;COMBINING ALMOST EQUAL TO ABOVE;Mn;230;NSM;;;;;N;;;;;
+034D;COMBINING LEFT RIGHT ARROW BELOW;Mn;220;NSM;;;;;N;;;;;
+034E;COMBINING UPWARDS ARROW BELOW;Mn;220;NSM;;;;;N;;;;;
+034F;COMBINING GRAPHEME JOINER;Mn;0;NSM;;;;;N;;;;;
+0350;COMBINING RIGHT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;;
+0351;COMBINING LEFT HALF RING ABOVE;Mn;230;NSM;;;;;N;;;;;
+0352;COMBINING FERMATA;Mn;230;NSM;;;;;N;;;;;
+0353;COMBINING X BELOW;Mn;220;NSM;;;;;N;;;;;
+0354;COMBINING LEFT ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;;
+0355;COMBINING RIGHT ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;;
+0356;COMBINING RIGHT ARROWHEAD AND UP ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;;
+0357;COMBINING RIGHT HALF RING ABOVE;Mn;230;NSM;;;;;N;;;;;
+0358;COMBINING DOT ABOVE RIGHT;Mn;232;NSM;;;;;N;;;;;
+0359;COMBINING ASTERISK BELOW;Mn;220;NSM;;;;;N;;;;;
+035A;COMBINING DOUBLE RING BELOW;Mn;220;NSM;;;;;N;;;;;
+035B;COMBINING ZIGZAG ABOVE;Mn;230;NSM;;;;;N;;;;;
+035C;COMBINING DOUBLE BREVE BELOW;Mn;233;NSM;;;;;N;;;;;
+035D;COMBINING DOUBLE BREVE;Mn;234;NSM;;;;;N;;;;;
+035E;COMBINING DOUBLE MACRON;Mn;234;NSM;;;;;N;;;;;
+035F;COMBINING DOUBLE MACRON BELOW;Mn;233;NSM;;;;;N;;;;;
+0360;COMBINING DOUBLE TILDE;Mn;234;NSM;;;;;N;;;;;
+0361;COMBINING DOUBLE INVERTED BREVE;Mn;234;NSM;;;;;N;;;;;
+0362;COMBINING DOUBLE RIGHTWARDS ARROW BELOW;Mn;233;NSM;;;;;N;;;;;
+0363;COMBINING LATIN SMALL LETTER A;Mn;230;NSM;;;;;N;;;;;
+0364;COMBINING LATIN SMALL LETTER E;Mn;230;NSM;;;;;N;;;;;
+0365;COMBINING LATIN SMALL LETTER I;Mn;230;NSM;;;;;N;;;;;
+0366;COMBINING LATIN SMALL LETTER O;Mn;230;NSM;;;;;N;;;;;
+0367;COMBINING LATIN SMALL LETTER U;Mn;230;NSM;;;;;N;;;;;
+0368;COMBINING LATIN SMALL LETTER C;Mn;230;NSM;;;;;N;;;;;
+0369;COMBINING LATIN SMALL LETTER D;Mn;230;NSM;;;;;N;;;;;
+036A;COMBINING LATIN SMALL LETTER H;Mn;230;NSM;;;;;N;;;;;
+036B;COMBINING LATIN SMALL LETTER M;Mn;230;NSM;;;;;N;;;;;
+036C;COMBINING LATIN SMALL LETTER R;Mn;230;NSM;;;;;N;;;;;
+036D;COMBINING LATIN SMALL LETTER T;Mn;230;NSM;;;;;N;;;;;
+036E;COMBINING LATIN SMALL LETTER V;Mn;230;NSM;;;;;N;;;;;
+036F;COMBINING LATIN SMALL LETTER X;Mn;230;NSM;;;;;N;;;;;
+0370;GREEK CAPITAL LETTER HETA;Lu;0;L;;;;;N;;;;0371;
+0371;GREEK SMALL LETTER HETA;Ll;0;L;;;;;N;;;0370;;0370
+0372;GREEK CAPITAL LETTER ARCHAIC SAMPI;Lu;0;L;;;;;N;;;;0373;
+0373;GREEK SMALL LETTER ARCHAIC SAMPI;Ll;0;L;;;;;N;;;0372;;0372
+0374;GREEK NUMERAL SIGN;Lm;0;ON;02B9;;;;N;GREEK UPPER NUMERAL SIGN;;;;
+0375;GREEK LOWER NUMERAL SIGN;Sk;0;ON;;;;;N;;;;;
+0376;GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA;Lu;0;L;;;;;N;;;;0377;
+0377;GREEK SMALL LETTER PAMPHYLIAN DIGAMMA;Ll;0;L;;;;;N;;;0376;;0376
+037A;GREEK YPOGEGRAMMENI;Lm;0;L;<compat> 0020 0345;;;;N;GREEK SPACING IOTA BELOW;;;;
+037B;GREEK SMALL REVERSED LUNATE SIGMA SYMBOL;Ll;0;L;;;;;N;;;03FD;;03FD
+037C;GREEK SMALL DOTTED LUNATE SIGMA SYMBOL;Ll;0;L;;;;;N;;;03FE;;03FE
+037D;GREEK SMALL REVERSED DOTTED LUNATE SIGMA SYMBOL;Ll;0;L;;;;;N;;;03FF;;03FF
+037E;GREEK QUESTION MARK;Po;0;ON;003B;;;;N;;;;;
+037F;GREEK CAPITAL LETTER YOT;Lu;0;L;;;;;N;;;;03F3;
+0384;GREEK TONOS;Sk;0;ON;<compat> 0020 0301;;;;N;GREEK SPACING TONOS;;;;
+0385;GREEK DIALYTIKA TONOS;Sk;0;ON;00A8 0301;;;;N;GREEK SPACING DIAERESIS TONOS;;;;
+0386;GREEK CAPITAL LETTER ALPHA WITH TONOS;Lu;0;L;0391 0301;;;;N;GREEK CAPITAL LETTER ALPHA TONOS;;;03AC;
+0387;GREEK ANO TELEIA;Po;0;ON;00B7;;;;N;;;;;
+0388;GREEK CAPITAL LETTER EPSILON WITH TONOS;Lu;0;L;0395 0301;;;;N;GREEK CAPITAL LETTER EPSILON TONOS;;;03AD;
+0389;GREEK CAPITAL LETTER ETA WITH TONOS;Lu;0;L;0397 0301;;;;N;GREEK CAPITAL LETTER ETA TONOS;;;03AE;
+038A;GREEK CAPITAL LETTER IOTA WITH TONOS;Lu;0;L;0399 0301;;;;N;GREEK CAPITAL LETTER IOTA TONOS;;;03AF;
+038C;GREEK CAPITAL LETTER OMICRON WITH TONOS;Lu;0;L;039F 0301;;;;N;GREEK CAPITAL LETTER OMICRON TONOS;;;03CC;
+038E;GREEK CAPITAL LETTER UPSILON WITH TONOS;Lu;0;L;03A5 0301;;;;N;GREEK CAPITAL LETTER UPSILON TONOS;;;03CD;
+038F;GREEK CAPITAL LETTER OMEGA WITH TONOS;Lu;0;L;03A9 0301;;;;N;GREEK CAPITAL LETTER OMEGA TONOS;;;03CE;
+0390;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS;Ll;0;L;03CA 0301;;;;N;GREEK SMALL LETTER IOTA DIAERESIS TONOS;;;;
+0391;GREEK CAPITAL LETTER ALPHA;Lu;0;L;;;;;N;;;;03B1;
+0392;GREEK CAPITAL LETTER BETA;Lu;0;L;;;;;N;;;;03B2;
+0393;GREEK CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;03B3;
+0394;GREEK CAPITAL LETTER DELTA;Lu;0;L;;;;;N;;;;03B4;
+0395;GREEK CAPITAL LETTER EPSILON;Lu;0;L;;;;;N;;;;03B5;
+0396;GREEK CAPITAL LETTER ZETA;Lu;0;L;;;;;N;;;;03B6;
+0397;GREEK CAPITAL LETTER ETA;Lu;0;L;;;;;N;;;;03B7;
+0398;GREEK CAPITAL LETTER THETA;Lu;0;L;;;;;N;;;;03B8;
+0399;GREEK CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;03B9;
+039A;GREEK CAPITAL LETTER KAPPA;Lu;0;L;;;;;N;;;;03BA;
+039B;GREEK CAPITAL LETTER LAMDA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER LAMBDA;;;03BB;
+039C;GREEK CAPITAL LETTER MU;Lu;0;L;;;;;N;;;;03BC;
+039D;GREEK CAPITAL LETTER NU;Lu;0;L;;;;;N;;;;03BD;
+039E;GREEK CAPITAL LETTER XI;Lu;0;L;;;;;N;;;;03BE;
+039F;GREEK CAPITAL LETTER OMICRON;Lu;0;L;;;;;N;;;;03BF;
+03A0;GREEK CAPITAL LETTER PI;Lu;0;L;;;;;N;;;;03C0;
+03A1;GREEK CAPITAL LETTER RHO;Lu;0;L;;;;;N;;;;03C1;
+03A3;GREEK CAPITAL LETTER SIGMA;Lu;0;L;;;;;N;;;;03C3;
+03A4;GREEK CAPITAL LETTER TAU;Lu;0;L;;;;;N;;;;03C4;
+03A5;GREEK CAPITAL LETTER UPSILON;Lu;0;L;;;;;N;;;;03C5;
+03A6;GREEK CAPITAL LETTER PHI;Lu;0;L;;;;;N;;;;03C6;
+03A7;GREEK CAPITAL LETTER CHI;Lu;0;L;;;;;N;;;;03C7;
+03A8;GREEK CAPITAL LETTER PSI;Lu;0;L;;;;;N;;;;03C8;
+03A9;GREEK CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;03C9;
+03AA;GREEK CAPITAL LETTER IOTA WITH DIALYTIKA;Lu;0;L;0399 0308;;;;N;GREEK CAPITAL LETTER IOTA DIAERESIS;;;03CA;
+03AB;GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA;Lu;0;L;03A5 0308;;;;N;GREEK CAPITAL LETTER UPSILON DIAERESIS;;;03CB;
+03AC;GREEK SMALL LETTER ALPHA WITH TONOS;Ll;0;L;03B1 0301;;;;N;GREEK SMALL LETTER ALPHA TONOS;;0386;;0386
+03AD;GREEK SMALL LETTER EPSILON WITH TONOS;Ll;0;L;03B5 0301;;;;N;GREEK SMALL LETTER EPSILON TONOS;;0388;;0388
+03AE;GREEK SMALL LETTER ETA WITH TONOS;Ll;0;L;03B7 0301;;;;N;GREEK SMALL LETTER ETA TONOS;;0389;;0389
+03AF;GREEK SMALL LETTER IOTA WITH TONOS;Ll;0;L;03B9 0301;;;;N;GREEK SMALL LETTER IOTA TONOS;;038A;;038A
+03B0;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS;Ll;0;L;03CB 0301;;;;N;GREEK SMALL LETTER UPSILON DIAERESIS TONOS;;;;
+03B1;GREEK SMALL LETTER ALPHA;Ll;0;L;;;;;N;;;0391;;0391
+03B2;GREEK SMALL LETTER BETA;Ll;0;L;;;;;N;;;0392;;0392
+03B3;GREEK SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;0393;;0393
+03B4;GREEK SMALL LETTER DELTA;Ll;0;L;;;;;N;;;0394;;0394
+03B5;GREEK SMALL LETTER EPSILON;Ll;0;L;;;;;N;;;0395;;0395
+03B6;GREEK SMALL LETTER ZETA;Ll;0;L;;;;;N;;;0396;;0396
+03B7;GREEK SMALL LETTER ETA;Ll;0;L;;;;;N;;;0397;;0397
+03B8;GREEK SMALL LETTER THETA;Ll;0;L;;;;;N;;;0398;;0398
+03B9;GREEK SMALL LETTER IOTA;Ll;0;L;;;;;N;;;0399;;0399
+03BA;GREEK SMALL LETTER KAPPA;Ll;0;L;;;;;N;;;039A;;039A
+03BB;GREEK SMALL LETTER LAMDA;Ll;0;L;;;;;N;GREEK SMALL LETTER LAMBDA;;039B;;039B
+03BC;GREEK SMALL LETTER MU;Ll;0;L;;;;;N;;;039C;;039C
+03BD;GREEK SMALL LETTER NU;Ll;0;L;;;;;N;;;039D;;039D
+03BE;GREEK SMALL LETTER XI;Ll;0;L;;;;;N;;;039E;;039E
+03BF;GREEK SMALL LETTER OMICRON;Ll;0;L;;;;;N;;;039F;;039F
+03C0;GREEK SMALL LETTER PI;Ll;0;L;;;;;N;;;03A0;;03A0
+03C1;GREEK SMALL LETTER RHO;Ll;0;L;;;;;N;;;03A1;;03A1
+03C2;GREEK SMALL LETTER FINAL SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3
+03C3;GREEK SMALL LETTER SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3
+03C4;GREEK SMALL LETTER TAU;Ll;0;L;;;;;N;;;03A4;;03A4
+03C5;GREEK SMALL LETTER UPSILON;Ll;0;L;;;;;N;;;03A5;;03A5
+03C6;GREEK SMALL LETTER PHI;Ll;0;L;;;;;N;;;03A6;;03A6
+03C7;GREEK SMALL LETTER CHI;Ll;0;L;;;;;N;;;03A7;;03A7
+03C8;GREEK SMALL LETTER PSI;Ll;0;L;;;;;N;;;03A8;;03A8
+03C9;GREEK SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;03A9;;03A9
+03CA;GREEK SMALL LETTER IOTA WITH DIALYTIKA;Ll;0;L;03B9 0308;;;;N;GREEK SMALL LETTER IOTA DIAERESIS;;03AA;;03AA
+03CB;GREEK SMALL LETTER UPSILON WITH DIALYTIKA;Ll;0;L;03C5 0308;;;;N;GREEK SMALL LETTER UPSILON DIAERESIS;;03AB;;03AB
+03CC;GREEK SMALL LETTER OMICRON WITH TONOS;Ll;0;L;03BF 0301;;;;N;GREEK SMALL LETTER OMICRON TONOS;;038C;;038C
+03CD;GREEK SMALL LETTER UPSILON WITH TONOS;Ll;0;L;03C5 0301;;;;N;GREEK SMALL LETTER UPSILON TONOS;;038E;;038E
+03CE;GREEK SMALL LETTER OMEGA WITH TONOS;Ll;0;L;03C9 0301;;;;N;GREEK SMALL LETTER OMEGA TONOS;;038F;;038F
+03CF;GREEK CAPITAL KAI SYMBOL;Lu;0;L;;;;;N;;;;03D7;
+03D0;GREEK BETA SYMBOL;Ll;0;L;<compat> 03B2;;;;N;GREEK SMALL LETTER CURLED BETA;;0392;;0392
+03D1;GREEK THETA SYMBOL;Ll;0;L;<compat> 03B8;;;;N;GREEK SMALL LETTER SCRIPT THETA;;0398;;0398
+03D2;GREEK UPSILON WITH HOOK SYMBOL;Lu;0;L;<compat> 03A5;;;;N;GREEK CAPITAL LETTER UPSILON HOOK;;;;
+03D3;GREEK UPSILON WITH ACUTE AND HOOK SYMBOL;Lu;0;L;03D2 0301;;;;N;GREEK CAPITAL LETTER UPSILON HOOK TONOS;;;;
+03D4;GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL;Lu;0;L;03D2 0308;;;;N;GREEK CAPITAL LETTER UPSILON HOOK DIAERESIS;;;;
+03D5;GREEK PHI SYMBOL;Ll;0;L;<compat> 03C6;;;;N;GREEK SMALL LETTER SCRIPT PHI;;03A6;;03A6
+03D6;GREEK PI SYMBOL;Ll;0;L;<compat> 03C0;;;;N;GREEK SMALL LETTER OMEGA PI;;03A0;;03A0
+03D7;GREEK KAI SYMBOL;Ll;0;L;;;;;N;;;03CF;;03CF
+03D8;GREEK LETTER ARCHAIC KOPPA;Lu;0;L;;;;;N;;;;03D9;
+03D9;GREEK SMALL LETTER ARCHAIC KOPPA;Ll;0;L;;;;;N;;;03D8;;03D8
+03DA;GREEK LETTER STIGMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER STIGMA;;;03DB;
+03DB;GREEK SMALL LETTER STIGMA;Ll;0;L;;;;;N;;;03DA;;03DA
+03DC;GREEK LETTER DIGAMMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER DIGAMMA;;;03DD;
+03DD;GREEK SMALL LETTER DIGAMMA;Ll;0;L;;;;;N;;;03DC;;03DC
+03DE;GREEK LETTER KOPPA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER KOPPA;;;03DF;
+03DF;GREEK SMALL LETTER KOPPA;Ll;0;L;;;;;N;;;03DE;;03DE
+03E0;GREEK LETTER SAMPI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SAMPI;;;03E1;
+03E1;GREEK SMALL LETTER SAMPI;Ll;0;L;;;;;N;;;03E0;;03E0
+03E2;COPTIC CAPITAL LETTER SHEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SHEI;;;03E3;
+03E3;COPTIC SMALL LETTER SHEI;Ll;0;L;;;;;N;GREEK SMALL LETTER SHEI;;03E2;;03E2
+03E4;COPTIC CAPITAL LETTER FEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER FEI;;;03E5;
+03E5;COPTIC SMALL LETTER FEI;Ll;0;L;;;;;N;GREEK SMALL LETTER FEI;;03E4;;03E4
+03E6;COPTIC CAPITAL LETTER KHEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER KHEI;;;03E7;
+03E7;COPTIC SMALL LETTER KHEI;Ll;0;L;;;;;N;GREEK SMALL LETTER KHEI;;03E6;;03E6
+03E8;COPTIC CAPITAL LETTER HORI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER HORI;;;03E9;
+03E9;COPTIC SMALL LETTER HORI;Ll;0;L;;;;;N;GREEK SMALL LETTER HORI;;03E8;;03E8
+03EA;COPTIC CAPITAL LETTER GANGIA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER GANGIA;;;03EB;
+03EB;COPTIC SMALL LETTER GANGIA;Ll;0;L;;;;;N;GREEK SMALL LETTER GANGIA;;03EA;;03EA
+03EC;COPTIC CAPITAL LETTER SHIMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SHIMA;;;03ED;
+03ED;COPTIC SMALL LETTER SHIMA;Ll;0;L;;;;;N;GREEK SMALL LETTER SHIMA;;03EC;;03EC
+03EE;COPTIC CAPITAL LETTER DEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER DEI;;;03EF;
+03EF;COPTIC SMALL LETTER DEI;Ll;0;L;;;;;N;GREEK SMALL LETTER DEI;;03EE;;03EE
+03F0;GREEK KAPPA SYMBOL;Ll;0;L;<compat> 03BA;;;;N;GREEK SMALL LETTER SCRIPT KAPPA;;039A;;039A
+03F1;GREEK RHO SYMBOL;Ll;0;L;<compat> 03C1;;;;N;GREEK SMALL LETTER TAILED RHO;;03A1;;03A1
+03F2;GREEK LUNATE SIGMA SYMBOL;Ll;0;L;<compat> 03C2;;;;N;GREEK SMALL LETTER LUNATE SIGMA;;03F9;;03F9
+03F3;GREEK LETTER YOT;Ll;0;L;;;;;N;;;037F;;037F
+03F4;GREEK CAPITAL THETA SYMBOL;Lu;0;L;<compat> 0398;;;;N;;;;03B8;
+03F5;GREEK LUNATE EPSILON SYMBOL;Ll;0;L;<compat> 03B5;;;;N;;;0395;;0395
+03F6;GREEK REVERSED LUNATE EPSILON SYMBOL;Sm;0;ON;;;;;N;;;;;
+03F7;GREEK CAPITAL LETTER SHO;Lu;0;L;;;;;N;;;;03F8;
+03F8;GREEK SMALL LETTER SHO;Ll;0;L;;;;;N;;;03F7;;03F7
+03F9;GREEK CAPITAL LUNATE SIGMA SYMBOL;Lu;0;L;<compat> 03A3;;;;N;;;;03F2;
+03FA;GREEK CAPITAL LETTER SAN;Lu;0;L;;;;;N;;;;03FB;
+03FB;GREEK SMALL LETTER SAN;Ll;0;L;;;;;N;;;03FA;;03FA
+03FC;GREEK RHO WITH STROKE SYMBOL;Ll;0;L;;;;;N;;;;;
+03FD;GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL;Lu;0;L;;;;;N;;;;037B;
+03FE;GREEK CAPITAL DOTTED LUNATE SIGMA SYMBOL;Lu;0;L;;;;;N;;;;037C;
+03FF;GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL;Lu;0;L;;;;;N;;;;037D;
+0400;CYRILLIC CAPITAL LETTER IE WITH GRAVE;Lu;0;L;0415 0300;;;;N;;;;0450;
+0401;CYRILLIC CAPITAL LETTER IO;Lu;0;L;0415 0308;;;;N;;;;0451;
+0402;CYRILLIC CAPITAL LETTER DJE;Lu;0;L;;;;;N;;;;0452;
+0403;CYRILLIC CAPITAL LETTER GJE;Lu;0;L;0413 0301;;;;N;;;;0453;
+0404;CYRILLIC CAPITAL LETTER UKRAINIAN IE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER E;;;0454;
+0405;CYRILLIC CAPITAL LETTER DZE;Lu;0;L;;;;;N;;;;0455;
+0406;CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER I;;;0456;
+0407;CYRILLIC CAPITAL LETTER YI;Lu;0;L;0406 0308;;;;N;;;;0457;
+0408;CYRILLIC CAPITAL LETTER JE;Lu;0;L;;;;;N;;;;0458;
+0409;CYRILLIC CAPITAL LETTER LJE;Lu;0;L;;;;;N;;;;0459;
+040A;CYRILLIC CAPITAL LETTER NJE;Lu;0;L;;;;;N;;;;045A;
+040B;CYRILLIC CAPITAL LETTER TSHE;Lu;0;L;;;;;N;;;;045B;
+040C;CYRILLIC CAPITAL LETTER KJE;Lu;0;L;041A 0301;;;;N;;;;045C;
+040D;CYRILLIC CAPITAL LETTER I WITH GRAVE;Lu;0;L;0418 0300;;;;N;;;;045D;
+040E;CYRILLIC CAPITAL LETTER SHORT U;Lu;0;L;0423 0306;;;;N;;;;045E;
+040F;CYRILLIC CAPITAL LETTER DZHE;Lu;0;L;;;;;N;;;;045F;
+0410;CYRILLIC CAPITAL LETTER A;Lu;0;L;;;;;N;;;;0430;
+0411;CYRILLIC CAPITAL LETTER BE;Lu;0;L;;;;;N;;;;0431;
+0412;CYRILLIC CAPITAL LETTER VE;Lu;0;L;;;;;N;;;;0432;
+0413;CYRILLIC CAPITAL LETTER GHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE;;;0433;
+0414;CYRILLIC CAPITAL LETTER DE;Lu;0;L;;;;;N;;;;0434;
+0415;CYRILLIC CAPITAL LETTER IE;Lu;0;L;;;;;N;;;;0435;
+0416;CYRILLIC CAPITAL LETTER ZHE;Lu;0;L;;;;;N;;;;0436;
+0417;CYRILLIC CAPITAL LETTER ZE;Lu;0;L;;;;;N;;;;0437;
+0418;CYRILLIC CAPITAL LETTER I;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER II;;;0438;
+0419;CYRILLIC CAPITAL LETTER SHORT I;Lu;0;L;0418 0306;;;;N;CYRILLIC CAPITAL LETTER SHORT II;;;0439;
+041A;CYRILLIC CAPITAL LETTER KA;Lu;0;L;;;;;N;;;;043A;
+041B;CYRILLIC CAPITAL LETTER EL;Lu;0;L;;;;;N;;;;043B;
+041C;CYRILLIC CAPITAL LETTER EM;Lu;0;L;;;;;N;;;;043C;
+041D;CYRILLIC CAPITAL LETTER EN;Lu;0;L;;;;;N;;;;043D;
+041E;CYRILLIC CAPITAL LETTER O;Lu;0;L;;;;;N;;;;043E;
+041F;CYRILLIC CAPITAL LETTER PE;Lu;0;L;;;;;N;;;;043F;
+0420;CYRILLIC CAPITAL LETTER ER;Lu;0;L;;;;;N;;;;0440;
+0421;CYRILLIC CAPITAL LETTER ES;Lu;0;L;;;;;N;;;;0441;
+0422;CYRILLIC CAPITAL LETTER TE;Lu;0;L;;;;;N;;;;0442;
+0423;CYRILLIC CAPITAL LETTER U;Lu;0;L;;;;;N;;;;0443;
+0424;CYRILLIC CAPITAL LETTER EF;Lu;0;L;;;;;N;;;;0444;
+0425;CYRILLIC CAPITAL LETTER HA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KHA;;;0445;
+0426;CYRILLIC CAPITAL LETTER TSE;Lu;0;L;;;;;N;;;;0446;
+0427;CYRILLIC CAPITAL LETTER CHE;Lu;0;L;;;;;N;;;;0447;
+0428;CYRILLIC CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;0448;
+0429;CYRILLIC CAPITAL LETTER SHCHA;Lu;0;L;;;;;N;;;;0449;
+042A;CYRILLIC CAPITAL LETTER HARD SIGN;Lu;0;L;;;;;N;;;;044A;
+042B;CYRILLIC CAPITAL LETTER YERU;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER YERI;;;044B;
+042C;CYRILLIC CAPITAL LETTER SOFT SIGN;Lu;0;L;;;;;N;;;;044C;
+042D;CYRILLIC CAPITAL LETTER E;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER REVERSED E;;;044D;
+042E;CYRILLIC CAPITAL LETTER YU;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IU;;;044E;
+042F;CYRILLIC CAPITAL LETTER YA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IA;;;044F;
+0430;CYRILLIC SMALL LETTER A;Ll;0;L;;;;;N;;;0410;;0410
+0431;CYRILLIC SMALL LETTER BE;Ll;0;L;;;;;N;;;0411;;0411
+0432;CYRILLIC SMALL LETTER VE;Ll;0;L;;;;;N;;;0412;;0412
+0433;CYRILLIC SMALL LETTER GHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE;;0413;;0413
+0434;CYRILLIC SMALL LETTER DE;Ll;0;L;;;;;N;;;0414;;0414
+0435;CYRILLIC SMALL LETTER IE;Ll;0;L;;;;;N;;;0415;;0415
+0436;CYRILLIC SMALL LETTER ZHE;Ll;0;L;;;;;N;;;0416;;0416
+0437;CYRILLIC SMALL LETTER ZE;Ll;0;L;;;;;N;;;0417;;0417
+0438;CYRILLIC SMALL LETTER I;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER II;;0418;;0418
+0439;CYRILLIC SMALL LETTER SHORT I;Ll;0;L;0438 0306;;;;N;CYRILLIC SMALL LETTER SHORT II;;0419;;0419
+043A;CYRILLIC SMALL LETTER KA;Ll;0;L;;;;;N;;;041A;;041A
+043B;CYRILLIC SMALL LETTER EL;Ll;0;L;;;;;N;;;041B;;041B
+043C;CYRILLIC SMALL LETTER EM;Ll;0;L;;;;;N;;;041C;;041C
+043D;CYRILLIC SMALL LETTER EN;Ll;0;L;;;;;N;;;041D;;041D
+043E;CYRILLIC SMALL LETTER O;Ll;0;L;;;;;N;;;041E;;041E
+043F;CYRILLIC SMALL LETTER PE;Ll;0;L;;;;;N;;;041F;;041F
+0440;CYRILLIC SMALL LETTER ER;Ll;0;L;;;;;N;;;0420;;0420
+0441;CYRILLIC SMALL LETTER ES;Ll;0;L;;;;;N;;;0421;;0421
+0442;CYRILLIC SMALL LETTER TE;Ll;0;L;;;;;N;;;0422;;0422
+0443;CYRILLIC SMALL LETTER U;Ll;0;L;;;;;N;;;0423;;0423
+0444;CYRILLIC SMALL LETTER EF;Ll;0;L;;;;;N;;;0424;;0424
+0445;CYRILLIC SMALL LETTER HA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KHA;;0425;;0425
+0446;CYRILLIC SMALL LETTER TSE;Ll;0;L;;;;;N;;;0426;;0426
+0447;CYRILLIC SMALL LETTER CHE;Ll;0;L;;;;;N;;;0427;;0427
+0448;CYRILLIC SMALL LETTER SHA;Ll;0;L;;;;;N;;;0428;;0428
+0449;CYRILLIC SMALL LETTER SHCHA;Ll;0;L;;;;;N;;;0429;;0429
+044A;CYRILLIC SMALL LETTER HARD SIGN;Ll;0;L;;;;;N;;;042A;;042A
+044B;CYRILLIC SMALL LETTER YERU;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER YERI;;042B;;042B
+044C;CYRILLIC SMALL LETTER SOFT SIGN;Ll;0;L;;;;;N;;;042C;;042C
+044D;CYRILLIC SMALL LETTER E;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER REVERSED E;;042D;;042D
+044E;CYRILLIC SMALL LETTER YU;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IU;;042E;;042E
+044F;CYRILLIC SMALL LETTER YA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IA;;042F;;042F
+0450;CYRILLIC SMALL LETTER IE WITH GRAVE;Ll;0;L;0435 0300;;;;N;;;0400;;0400
+0451;CYRILLIC SMALL LETTER IO;Ll;0;L;0435 0308;;;;N;;;0401;;0401
+0452;CYRILLIC SMALL LETTER DJE;Ll;0;L;;;;;N;;;0402;;0402
+0453;CYRILLIC SMALL LETTER GJE;Ll;0;L;0433 0301;;;;N;;;0403;;0403
+0454;CYRILLIC SMALL LETTER UKRAINIAN IE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER E;;0404;;0404
+0455;CYRILLIC SMALL LETTER DZE;Ll;0;L;;;;;N;;;0405;;0405
+0456;CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER I;;0406;;0406
+0457;CYRILLIC SMALL LETTER YI;Ll;0;L;0456 0308;;;;N;;;0407;;0407
+0458;CYRILLIC SMALL LETTER JE;Ll;0;L;;;;;N;;;0408;;0408
+0459;CYRILLIC SMALL LETTER LJE;Ll;0;L;;;;;N;;;0409;;0409
+045A;CYRILLIC SMALL LETTER NJE;Ll;0;L;;;;;N;;;040A;;040A
+045B;CYRILLIC SMALL LETTER TSHE;Ll;0;L;;;;;N;;;040B;;040B
+045C;CYRILLIC SMALL LETTER KJE;Ll;0;L;043A 0301;;;;N;;;040C;;040C
+045D;CYRILLIC SMALL LETTER I WITH GRAVE;Ll;0;L;0438 0300;;;;N;;;040D;;040D
+045E;CYRILLIC SMALL LETTER SHORT U;Ll;0;L;0443 0306;;;;N;;;040E;;040E
+045F;CYRILLIC SMALL LETTER DZHE;Ll;0;L;;;;;N;;;040F;;040F
+0460;CYRILLIC CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;0461;
+0461;CYRILLIC SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;0460;;0460
+0462;CYRILLIC CAPITAL LETTER YAT;Lu;0;L;;;;;N;;;;0463;
+0463;CYRILLIC SMALL LETTER YAT;Ll;0;L;;;;;N;;;0462;;0462
+0464;CYRILLIC CAPITAL LETTER IOTIFIED E;Lu;0;L;;;;;N;;;;0465;
+0465;CYRILLIC SMALL LETTER IOTIFIED E;Ll;0;L;;;;;N;;;0464;;0464
+0466;CYRILLIC CAPITAL LETTER LITTLE YUS;Lu;0;L;;;;;N;;;;0467;
+0467;CYRILLIC SMALL LETTER LITTLE YUS;Ll;0;L;;;;;N;;;0466;;0466
+0468;CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS;Lu;0;L;;;;;N;;;;0469;
+0469;CYRILLIC SMALL LETTER IOTIFIED LITTLE YUS;Ll;0;L;;;;;N;;;0468;;0468
+046A;CYRILLIC CAPITAL LETTER BIG YUS;Lu;0;L;;;;;N;;;;046B;
+046B;CYRILLIC SMALL LETTER BIG YUS;Ll;0;L;;;;;N;;;046A;;046A
+046C;CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS;Lu;0;L;;;;;N;;;;046D;
+046D;CYRILLIC SMALL LETTER IOTIFIED BIG YUS;Ll;0;L;;;;;N;;;046C;;046C
+046E;CYRILLIC CAPITAL LETTER KSI;Lu;0;L;;;;;N;;;;046F;
+046F;CYRILLIC SMALL LETTER KSI;Ll;0;L;;;;;N;;;046E;;046E
+0470;CYRILLIC CAPITAL LETTER PSI;Lu;0;L;;;;;N;;;;0471;
+0471;CYRILLIC SMALL LETTER PSI;Ll;0;L;;;;;N;;;0470;;0470
+0472;CYRILLIC CAPITAL LETTER FITA;Lu;0;L;;;;;N;;;;0473;
+0473;CYRILLIC SMALL LETTER FITA;Ll;0;L;;;;;N;;;0472;;0472
+0474;CYRILLIC CAPITAL LETTER IZHITSA;Lu;0;L;;;;;N;;;;0475;
+0475;CYRILLIC SMALL LETTER IZHITSA;Ll;0;L;;;;;N;;;0474;;0474
+0476;CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT;Lu;0;L;0474 030F;;;;N;CYRILLIC CAPITAL LETTER IZHITSA DOUBLE GRAVE;;;0477;
+0477;CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT;Ll;0;L;0475 030F;;;;N;CYRILLIC SMALL LETTER IZHITSA DOUBLE GRAVE;;0476;;0476
+0478;CYRILLIC CAPITAL LETTER UK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER UK DIGRAPH;;;0479;
+0479;CYRILLIC SMALL LETTER UK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER UK DIGRAPH;;0478;;0478
+047A;CYRILLIC CAPITAL LETTER ROUND OMEGA;Lu;0;L;;;;;N;;;;047B;
+047B;CYRILLIC SMALL LETTER ROUND OMEGA;Ll;0;L;;;;;N;;;047A;;047A
+047C;CYRILLIC CAPITAL LETTER OMEGA WITH TITLO;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER OMEGA TITLO;;;047D;
+047D;CYRILLIC SMALL LETTER OMEGA WITH TITLO;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER OMEGA TITLO;;047C;;047C
+047E;CYRILLIC CAPITAL LETTER OT;Lu;0;L;;;;;N;;;;047F;
+047F;CYRILLIC SMALL LETTER OT;Ll;0;L;;;;;N;;;047E;;047E
+0480;CYRILLIC CAPITAL LETTER KOPPA;Lu;0;L;;;;;N;;;;0481;
+0481;CYRILLIC SMALL LETTER KOPPA;Ll;0;L;;;;;N;;;0480;;0480
+0482;CYRILLIC THOUSANDS SIGN;So;0;L;;;;;N;;;;;
+0483;COMBINING CYRILLIC TITLO;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING TITLO;;;;
+0484;COMBINING CYRILLIC PALATALIZATION;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING PALATALIZATION;;;;
+0485;COMBINING CYRILLIC DASIA PNEUMATA;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING DASIA PNEUMATA;;;;
+0486;COMBINING CYRILLIC PSILI PNEUMATA;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING PSILI PNEUMATA;;;;
+0487;COMBINING CYRILLIC POKRYTIE;Mn;230;NSM;;;;;N;;;;;
+0488;COMBINING CYRILLIC HUNDRED THOUSANDS SIGN;Me;0;NSM;;;;;N;;;;;
+0489;COMBINING CYRILLIC MILLIONS SIGN;Me;0;NSM;;;;;N;;;;;
+048A;CYRILLIC CAPITAL LETTER SHORT I WITH TAIL;Lu;0;L;;;;;N;;;;048B;
+048B;CYRILLIC SMALL LETTER SHORT I WITH TAIL;Ll;0;L;;;;;N;;;048A;;048A
+048C;CYRILLIC CAPITAL LETTER SEMISOFT SIGN;Lu;0;L;;;;;N;;;;048D;
+048D;CYRILLIC SMALL LETTER SEMISOFT SIGN;Ll;0;L;;;;;N;;;048C;;048C
+048E;CYRILLIC CAPITAL LETTER ER WITH TICK;Lu;0;L;;;;;N;;;;048F;
+048F;CYRILLIC SMALL LETTER ER WITH TICK;Ll;0;L;;;;;N;;;048E;;048E
+0490;CYRILLIC CAPITAL LETTER GHE WITH UPTURN;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE WITH UPTURN;;;0491;
+0491;CYRILLIC SMALL LETTER GHE WITH UPTURN;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE WITH UPTURN;;0490;;0490
+0492;CYRILLIC CAPITAL LETTER GHE WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE BAR;;;0493;
+0493;CYRILLIC SMALL LETTER GHE WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE BAR;;0492;;0492
+0494;CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE HOOK;;;0495;
+0495;CYRILLIC SMALL LETTER GHE WITH MIDDLE HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE HOOK;;0494;;0494
+0496;CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ZHE WITH RIGHT DESCENDER;;;0497;
+0497;CYRILLIC SMALL LETTER ZHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ZHE WITH RIGHT DESCENDER;;0496;;0496
+0498;CYRILLIC CAPITAL LETTER ZE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ZE CEDILLA;;;0499;
+0499;CYRILLIC SMALL LETTER ZE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ZE CEDILLA;;0498;;0498
+049A;CYRILLIC CAPITAL LETTER KA WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA WITH RIGHT DESCENDER;;;049B;
+049B;CYRILLIC SMALL LETTER KA WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA WITH RIGHT DESCENDER;;049A;;049A
+049C;CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA VERTICAL BAR;;;049D;
+049D;CYRILLIC SMALL LETTER KA WITH VERTICAL STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA VERTICAL BAR;;049C;;049C
+049E;CYRILLIC CAPITAL LETTER KA WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA BAR;;;049F;
+049F;CYRILLIC SMALL LETTER KA WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA BAR;;049E;;049E
+04A0;CYRILLIC CAPITAL LETTER BASHKIR KA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER REVERSED GE KA;;;04A1;
+04A1;CYRILLIC SMALL LETTER BASHKIR KA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER REVERSED GE KA;;04A0;;04A0
+04A2;CYRILLIC CAPITAL LETTER EN WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN WITH RIGHT DESCENDER;;;04A3;
+04A3;CYRILLIC SMALL LETTER EN WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN WITH RIGHT DESCENDER;;04A2;;04A2
+04A4;CYRILLIC CAPITAL LIGATURE EN GHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN GE;;;04A5;
+04A5;CYRILLIC SMALL LIGATURE EN GHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN GE;;04A4;;04A4
+04A6;CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER PE HOOK;;;04A7;
+04A7;CYRILLIC SMALL LETTER PE WITH MIDDLE HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER PE HOOK;;04A6;;04A6
+04A8;CYRILLIC CAPITAL LETTER ABKHASIAN HA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER O HOOK;;;04A9;
+04A9;CYRILLIC SMALL LETTER ABKHASIAN HA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER O HOOK;;04A8;;04A8
+04AA;CYRILLIC CAPITAL LETTER ES WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ES CEDILLA;;;04AB;
+04AB;CYRILLIC SMALL LETTER ES WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ES CEDILLA;;04AA;;04AA
+04AC;CYRILLIC CAPITAL LETTER TE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER TE WITH RIGHT DESCENDER;;;04AD;
+04AD;CYRILLIC SMALL LETTER TE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER TE WITH RIGHT DESCENDER;;04AC;;04AC
+04AE;CYRILLIC CAPITAL LETTER STRAIGHT U;Lu;0;L;;;;;N;;;;04AF;
+04AF;CYRILLIC SMALL LETTER STRAIGHT U;Ll;0;L;;;;;N;;;04AE;;04AE
+04B0;CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER STRAIGHT U BAR;;;04B1;
+04B1;CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER STRAIGHT U BAR;;04B0;;04B0
+04B2;CYRILLIC CAPITAL LETTER HA WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KHA WITH RIGHT DESCENDER;;;04B3;
+04B3;CYRILLIC SMALL LETTER HA WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KHA WITH RIGHT DESCENDER;;04B2;;04B2
+04B4;CYRILLIC CAPITAL LIGATURE TE TSE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER TE TSE;;;04B5;
+04B5;CYRILLIC SMALL LIGATURE TE TSE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER TE TSE;;04B4;;04B4
+04B6;CYRILLIC CAPITAL LETTER CHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE WITH RIGHT DESCENDER;;;04B7;
+04B7;CYRILLIC SMALL LETTER CHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE WITH RIGHT DESCENDER;;04B6;;04B6
+04B8;CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE VERTICAL BAR;;;04B9;
+04B9;CYRILLIC SMALL LETTER CHE WITH VERTICAL STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE VERTICAL BAR;;04B8;;04B8
+04BA;CYRILLIC CAPITAL LETTER SHHA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER H;;;04BB;
+04BB;CYRILLIC SMALL LETTER SHHA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER H;;04BA;;04BA
+04BC;CYRILLIC CAPITAL LETTER ABKHASIAN CHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IE HOOK;;;04BD;
+04BD;CYRILLIC SMALL LETTER ABKHASIAN CHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IE HOOK;;04BC;;04BC
+04BE;CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IE HOOK OGONEK;;;04BF;
+04BF;CYRILLIC SMALL LETTER ABKHASIAN CHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IE HOOK OGONEK;;04BE;;04BE
+04C0;CYRILLIC LETTER PALOCHKA;Lu;0;L;;;;;N;CYRILLIC LETTER I;;;04CF;
+04C1;CYRILLIC CAPITAL LETTER ZHE WITH BREVE;Lu;0;L;0416 0306;;;;N;CYRILLIC CAPITAL LETTER SHORT ZHE;;;04C2;
+04C2;CYRILLIC SMALL LETTER ZHE WITH BREVE;Ll;0;L;0436 0306;;;;N;CYRILLIC SMALL LETTER SHORT ZHE;;04C1;;04C1
+04C3;CYRILLIC CAPITAL LETTER KA WITH HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA HOOK;;;04C4;
+04C4;CYRILLIC SMALL LETTER KA WITH HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA HOOK;;04C3;;04C3
+04C5;CYRILLIC CAPITAL LETTER EL WITH TAIL;Lu;0;L;;;;;N;;;;04C6;
+04C6;CYRILLIC SMALL LETTER EL WITH TAIL;Ll;0;L;;;;;N;;;04C5;;04C5
+04C7;CYRILLIC CAPITAL LETTER EN WITH HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN HOOK;;;04C8;
+04C8;CYRILLIC SMALL LETTER EN WITH HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN HOOK;;04C7;;04C7
+04C9;CYRILLIC CAPITAL LETTER EN WITH TAIL;Lu;0;L;;;;;N;;;;04CA;
+04CA;CYRILLIC SMALL LETTER EN WITH TAIL;Ll;0;L;;;;;N;;;04C9;;04C9
+04CB;CYRILLIC CAPITAL LETTER KHAKASSIAN CHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE WITH LEFT DESCENDER;;;04CC;
+04CC;CYRILLIC SMALL LETTER KHAKASSIAN CHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE WITH LEFT DESCENDER;;04CB;;04CB
+04CD;CYRILLIC CAPITAL LETTER EM WITH TAIL;Lu;0;L;;;;;N;;;;04CE;
+04CE;CYRILLIC SMALL LETTER EM WITH TAIL;Ll;0;L;;;;;N;;;04CD;;04CD
+04CF;CYRILLIC SMALL LETTER PALOCHKA;Ll;0;L;;;;;N;;;04C0;;04C0
+04D0;CYRILLIC CAPITAL LETTER A WITH BREVE;Lu;0;L;0410 0306;;;;N;;;;04D1;
+04D1;CYRILLIC SMALL LETTER A WITH BREVE;Ll;0;L;0430 0306;;;;N;;;04D0;;04D0
+04D2;CYRILLIC CAPITAL LETTER A WITH DIAERESIS;Lu;0;L;0410 0308;;;;N;;;;04D3;
+04D3;CYRILLIC SMALL LETTER A WITH DIAERESIS;Ll;0;L;0430 0308;;;;N;;;04D2;;04D2
+04D4;CYRILLIC CAPITAL LIGATURE A IE;Lu;0;L;;;;;N;;;;04D5;
+04D5;CYRILLIC SMALL LIGATURE A IE;Ll;0;L;;;;;N;;;04D4;;04D4
+04D6;CYRILLIC CAPITAL LETTER IE WITH BREVE;Lu;0;L;0415 0306;;;;N;;;;04D7;
+04D7;CYRILLIC SMALL LETTER IE WITH BREVE;Ll;0;L;0435 0306;;;;N;;;04D6;;04D6
+04D8;CYRILLIC CAPITAL LETTER SCHWA;Lu;0;L;;;;;N;;;;04D9;
+04D9;CYRILLIC SMALL LETTER SCHWA;Ll;0;L;;;;;N;;;04D8;;04D8
+04DA;CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS;Lu;0;L;04D8 0308;;;;N;;;;04DB;
+04DB;CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS;Ll;0;L;04D9 0308;;;;N;;;04DA;;04DA
+04DC;CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS;Lu;0;L;0416 0308;;;;N;;;;04DD;
+04DD;CYRILLIC SMALL LETTER ZHE WITH DIAERESIS;Ll;0;L;0436 0308;;;;N;;;04DC;;04DC
+04DE;CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS;Lu;0;L;0417 0308;;;;N;;;;04DF;
+04DF;CYRILLIC SMALL LETTER ZE WITH DIAERESIS;Ll;0;L;0437 0308;;;;N;;;04DE;;04DE
+04E0;CYRILLIC CAPITAL LETTER ABKHASIAN DZE;Lu;0;L;;;;;N;;;;04E1;
+04E1;CYRILLIC SMALL LETTER ABKHASIAN DZE;Ll;0;L;;;;;N;;;04E0;;04E0
+04E2;CYRILLIC CAPITAL LETTER I WITH MACRON;Lu;0;L;0418 0304;;;;N;;;;04E3;
+04E3;CYRILLIC SMALL LETTER I WITH MACRON;Ll;0;L;0438 0304;;;;N;;;04E2;;04E2
+04E4;CYRILLIC CAPITAL LETTER I WITH DIAERESIS;Lu;0;L;0418 0308;;;;N;;;;04E5;
+04E5;CYRILLIC SMALL LETTER I WITH DIAERESIS;Ll;0;L;0438 0308;;;;N;;;04E4;;04E4
+04E6;CYRILLIC CAPITAL LETTER O WITH DIAERESIS;Lu;0;L;041E 0308;;;;N;;;;04E7;
+04E7;CYRILLIC SMALL LETTER O WITH DIAERESIS;Ll;0;L;043E 0308;;;;N;;;04E6;;04E6
+04E8;CYRILLIC CAPITAL LETTER BARRED O;Lu;0;L;;;;;N;;;;04E9;
+04E9;CYRILLIC SMALL LETTER BARRED O;Ll;0;L;;;;;N;;;04E8;;04E8
+04EA;CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS;Lu;0;L;04E8 0308;;;;N;;;;04EB;
+04EB;CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS;Ll;0;L;04E9 0308;;;;N;;;04EA;;04EA
+04EC;CYRILLIC CAPITAL LETTER E WITH DIAERESIS;Lu;0;L;042D 0308;;;;N;;;;04ED;
+04ED;CYRILLIC SMALL LETTER E WITH DIAERESIS;Ll;0;L;044D 0308;;;;N;;;04EC;;04EC
+04EE;CYRILLIC CAPITAL LETTER U WITH MACRON;Lu;0;L;0423 0304;;;;N;;;;04EF;
+04EF;CYRILLIC SMALL LETTER U WITH MACRON;Ll;0;L;0443 0304;;;;N;;;04EE;;04EE
+04F0;CYRILLIC CAPITAL LETTER U WITH DIAERESIS;Lu;0;L;0423 0308;;;;N;;;;04F1;
+04F1;CYRILLIC SMALL LETTER U WITH DIAERESIS;Ll;0;L;0443 0308;;;;N;;;04F0;;04F0
+04F2;CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE;Lu;0;L;0423 030B;;;;N;;;;04F3;
+04F3;CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE;Ll;0;L;0443 030B;;;;N;;;04F2;;04F2
+04F4;CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS;Lu;0;L;0427 0308;;;;N;;;;04F5;
+04F5;CYRILLIC SMALL LETTER CHE WITH DIAERESIS;Ll;0;L;0447 0308;;;;N;;;04F4;;04F4
+04F6;CYRILLIC CAPITAL LETTER GHE WITH DESCENDER;Lu;0;L;;;;;N;;;;04F7;
+04F7;CYRILLIC SMALL LETTER GHE WITH DESCENDER;Ll;0;L;;;;;N;;;04F6;;04F6
+04F8;CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS;Lu;0;L;042B 0308;;;;N;;;;04F9;
+04F9;CYRILLIC SMALL LETTER YERU WITH DIAERESIS;Ll;0;L;044B 0308;;;;N;;;04F8;;04F8
+04FA;CYRILLIC CAPITAL LETTER GHE WITH STROKE AND HOOK;Lu;0;L;;;;;N;;;;04FB;
+04FB;CYRILLIC SMALL LETTER GHE WITH STROKE AND HOOK;Ll;0;L;;;;;N;;;04FA;;04FA
+04FC;CYRILLIC CAPITAL LETTER HA WITH HOOK;Lu;0;L;;;;;N;;;;04FD;
+04FD;CYRILLIC SMALL LETTER HA WITH HOOK;Ll;0;L;;;;;N;;;04FC;;04FC
+04FE;CYRILLIC CAPITAL LETTER HA WITH STROKE;Lu;0;L;;;;;N;;;;04FF;
+04FF;CYRILLIC SMALL LETTER HA WITH STROKE;Ll;0;L;;;;;N;;;04FE;;04FE
+0500;CYRILLIC CAPITAL LETTER KOMI DE;Lu;0;L;;;;;N;;;;0501;
+0501;CYRILLIC SMALL LETTER KOMI DE;Ll;0;L;;;;;N;;;0500;;0500
+0502;CYRILLIC CAPITAL LETTER KOMI DJE;Lu;0;L;;;;;N;;;;0503;
+0503;CYRILLIC SMALL LETTER KOMI DJE;Ll;0;L;;;;;N;;;0502;;0502
+0504;CYRILLIC CAPITAL LETTER KOMI ZJE;Lu;0;L;;;;;N;;;;0505;
+0505;CYRILLIC SMALL LETTER KOMI ZJE;Ll;0;L;;;;;N;;;0504;;0504
+0506;CYRILLIC CAPITAL LETTER KOMI DZJE;Lu;0;L;;;;;N;;;;0507;
+0507;CYRILLIC SMALL LETTER KOMI DZJE;Ll;0;L;;;;;N;;;0506;;0506
+0508;CYRILLIC CAPITAL LETTER KOMI LJE;Lu;0;L;;;;;N;;;;0509;
+0509;CYRILLIC SMALL LETTER KOMI LJE;Ll;0;L;;;;;N;;;0508;;0508
+050A;CYRILLIC CAPITAL LETTER KOMI NJE;Lu;0;L;;;;;N;;;;050B;
+050B;CYRILLIC SMALL LETTER KOMI NJE;Ll;0;L;;;;;N;;;050A;;050A
+050C;CYRILLIC CAPITAL LETTER KOMI SJE;Lu;0;L;;;;;N;;;;050D;
+050D;CYRILLIC SMALL LETTER KOMI SJE;Ll;0;L;;;;;N;;;050C;;050C
+050E;CYRILLIC CAPITAL LETTER KOMI TJE;Lu;0;L;;;;;N;;;;050F;
+050F;CYRILLIC SMALL LETTER KOMI TJE;Ll;0;L;;;;;N;;;050E;;050E
+0510;CYRILLIC CAPITAL LETTER REVERSED ZE;Lu;0;L;;;;;N;;;;0511;
+0511;CYRILLIC SMALL LETTER REVERSED ZE;Ll;0;L;;;;;N;;;0510;;0510
+0512;CYRILLIC CAPITAL LETTER EL WITH HOOK;Lu;0;L;;;;;N;;;;0513;
+0513;CYRILLIC SMALL LETTER EL WITH HOOK;Ll;0;L;;;;;N;;;0512;;0512
+0514;CYRILLIC CAPITAL LETTER LHA;Lu;0;L;;;;;N;;;;0515;
+0515;CYRILLIC SMALL LETTER LHA;Ll;0;L;;;;;N;;;0514;;0514
+0516;CYRILLIC CAPITAL LETTER RHA;Lu;0;L;;;;;N;;;;0517;
+0517;CYRILLIC SMALL LETTER RHA;Ll;0;L;;;;;N;;;0516;;0516
+0518;CYRILLIC CAPITAL LETTER YAE;Lu;0;L;;;;;N;;;;0519;
+0519;CYRILLIC SMALL LETTER YAE;Ll;0;L;;;;;N;;;0518;;0518
+051A;CYRILLIC CAPITAL LETTER QA;Lu;0;L;;;;;N;;;;051B;
+051B;CYRILLIC SMALL LETTER QA;Ll;0;L;;;;;N;;;051A;;051A
+051C;CYRILLIC CAPITAL LETTER WE;Lu;0;L;;;;;N;;;;051D;
+051D;CYRILLIC SMALL LETTER WE;Ll;0;L;;;;;N;;;051C;;051C
+051E;CYRILLIC CAPITAL LETTER ALEUT KA;Lu;0;L;;;;;N;;;;051F;
+051F;CYRILLIC SMALL LETTER ALEUT KA;Ll;0;L;;;;;N;;;051E;;051E
+0520;CYRILLIC CAPITAL LETTER EL WITH MIDDLE HOOK;Lu;0;L;;;;;N;;;;0521;
+0521;CYRILLIC SMALL LETTER EL WITH MIDDLE HOOK;Ll;0;L;;;;;N;;;0520;;0520
+0522;CYRILLIC CAPITAL LETTER EN WITH MIDDLE HOOK;Lu;0;L;;;;;N;;;;0523;
+0523;CYRILLIC SMALL LETTER EN WITH MIDDLE HOOK;Ll;0;L;;;;;N;;;0522;;0522
+0524;CYRILLIC CAPITAL LETTER PE WITH DESCENDER;Lu;0;L;;;;;N;;;;0525;
+0525;CYRILLIC SMALL LETTER PE WITH DESCENDER;Ll;0;L;;;;;N;;;0524;;0524
+0526;CYRILLIC CAPITAL LETTER SHHA WITH DESCENDER;Lu;0;L;;;;;N;;;;0527;
+0527;CYRILLIC SMALL LETTER SHHA WITH DESCENDER;Ll;0;L;;;;;N;;;0526;;0526
+0528;CYRILLIC CAPITAL LETTER EN WITH LEFT HOOK;Lu;0;L;;;;;N;;;;0529;
+0529;CYRILLIC SMALL LETTER EN WITH LEFT HOOK;Ll;0;L;;;;;N;;;0528;;0528
+052A;CYRILLIC CAPITAL LETTER DZZHE;Lu;0;L;;;;;N;;;;052B;
+052B;CYRILLIC SMALL LETTER DZZHE;Ll;0;L;;;;;N;;;052A;;052A
+052C;CYRILLIC CAPITAL LETTER DCHE;Lu;0;L;;;;;N;;;;052D;
+052D;CYRILLIC SMALL LETTER DCHE;Ll;0;L;;;;;N;;;052C;;052C
+052E;CYRILLIC CAPITAL LETTER EL WITH DESCENDER;Lu;0;L;;;;;N;;;;052F;
+052F;CYRILLIC SMALL LETTER EL WITH DESCENDER;Ll;0;L;;;;;N;;;052E;;052E
+0531;ARMENIAN CAPITAL LETTER AYB;Lu;0;L;;;;;N;;;;0561;
+0532;ARMENIAN CAPITAL LETTER BEN;Lu;0;L;;;;;N;;;;0562;
+0533;ARMENIAN CAPITAL LETTER GIM;Lu;0;L;;;;;N;;;;0563;
+0534;ARMENIAN CAPITAL LETTER DA;Lu;0;L;;;;;N;;;;0564;
+0535;ARMENIAN CAPITAL LETTER ECH;Lu;0;L;;;;;N;;;;0565;
+0536;ARMENIAN CAPITAL LETTER ZA;Lu;0;L;;;;;N;;;;0566;
+0537;ARMENIAN CAPITAL LETTER EH;Lu;0;L;;;;;N;;;;0567;
+0538;ARMENIAN CAPITAL LETTER ET;Lu;0;L;;;;;N;;;;0568;
+0539;ARMENIAN CAPITAL LETTER TO;Lu;0;L;;;;;N;;;;0569;
+053A;ARMENIAN CAPITAL LETTER ZHE;Lu;0;L;;;;;N;;;;056A;
+053B;ARMENIAN CAPITAL LETTER INI;Lu;0;L;;;;;N;;;;056B;
+053C;ARMENIAN CAPITAL LETTER LIWN;Lu;0;L;;;;;N;;;;056C;
+053D;ARMENIAN CAPITAL LETTER XEH;Lu;0;L;;;;;N;;;;056D;
+053E;ARMENIAN CAPITAL LETTER CA;Lu;0;L;;;;;N;;;;056E;
+053F;ARMENIAN CAPITAL LETTER KEN;Lu;0;L;;;;;N;;;;056F;
+0540;ARMENIAN CAPITAL LETTER HO;Lu;0;L;;;;;N;;;;0570;
+0541;ARMENIAN CAPITAL LETTER JA;Lu;0;L;;;;;N;;;;0571;
+0542;ARMENIAN CAPITAL LETTER GHAD;Lu;0;L;;;;;N;ARMENIAN CAPITAL LETTER LAD;;;0572;
+0543;ARMENIAN CAPITAL LETTER CHEH;Lu;0;L;;;;;N;;;;0573;
+0544;ARMENIAN CAPITAL LETTER MEN;Lu;0;L;;;;;N;;;;0574;
+0545;ARMENIAN CAPITAL LETTER YI;Lu;0;L;;;;;N;;;;0575;
+0546;ARMENIAN CAPITAL LETTER NOW;Lu;0;L;;;;;N;;;;0576;
+0547;ARMENIAN CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;0577;
+0548;ARMENIAN CAPITAL LETTER VO;Lu;0;L;;;;;N;;;;0578;
+0549;ARMENIAN CAPITAL LETTER CHA;Lu;0;L;;;;;N;;;;0579;
+054A;ARMENIAN CAPITAL LETTER PEH;Lu;0;L;;;;;N;;;;057A;
+054B;ARMENIAN CAPITAL LETTER JHEH;Lu;0;L;;;;;N;;;;057B;
+054C;ARMENIAN CAPITAL LETTER RA;Lu;0;L;;;;;N;;;;057C;
+054D;ARMENIAN CAPITAL LETTER SEH;Lu;0;L;;;;;N;;;;057D;
+054E;ARMENIAN CAPITAL LETTER VEW;Lu;0;L;;;;;N;;;;057E;
+054F;ARMENIAN CAPITAL LETTER TIWN;Lu;0;L;;;;;N;;;;057F;
+0550;ARMENIAN CAPITAL LETTER REH;Lu;0;L;;;;;N;;;;0580;
+0551;ARMENIAN CAPITAL LETTER CO;Lu;0;L;;;;;N;;;;0581;
+0552;ARMENIAN CAPITAL LETTER YIWN;Lu;0;L;;;;;N;;;;0582;
+0553;ARMENIAN CAPITAL LETTER PIWR;Lu;0;L;;;;;N;;;;0583;
+0554;ARMENIAN CAPITAL LETTER KEH;Lu;0;L;;;;;N;;;;0584;
+0555;ARMENIAN CAPITAL LETTER OH;Lu;0;L;;;;;N;;;;0585;
+0556;ARMENIAN CAPITAL LETTER FEH;Lu;0;L;;;;;N;;;;0586;
+0559;ARMENIAN MODIFIER LETTER LEFT HALF RING;Lm;0;L;;;;;N;;;;;
+055A;ARMENIAN APOSTROPHE;Po;0;L;;;;;N;ARMENIAN MODIFIER LETTER RIGHT HALF RING;;;;
+055B;ARMENIAN EMPHASIS MARK;Po;0;L;;;;;N;;;;;
+055C;ARMENIAN EXCLAMATION MARK;Po;0;L;;;;;N;;;;;
+055D;ARMENIAN COMMA;Po;0;L;;;;;N;;;;;
+055E;ARMENIAN QUESTION MARK;Po;0;L;;;;;N;;;;;
+055F;ARMENIAN ABBREVIATION MARK;Po;0;L;;;;;N;;;;;
+0560;ARMENIAN SMALL LETTER TURNED AYB;Ll;0;L;;;;;N;;;;;
+0561;ARMENIAN SMALL LETTER AYB;Ll;0;L;;;;;N;;;0531;;0531
+0562;ARMENIAN SMALL LETTER BEN;Ll;0;L;;;;;N;;;0532;;0532
+0563;ARMENIAN SMALL LETTER GIM;Ll;0;L;;;;;N;;;0533;;0533
+0564;ARMENIAN SMALL LETTER DA;Ll;0;L;;;;;N;;;0534;;0534
+0565;ARMENIAN SMALL LETTER ECH;Ll;0;L;;;;;N;;;0535;;0535
+0566;ARMENIAN SMALL LETTER ZA;Ll;0;L;;;;;N;;;0536;;0536
+0567;ARMENIAN SMALL LETTER EH;Ll;0;L;;;;;N;;;0537;;0537
+0568;ARMENIAN SMALL LETTER ET;Ll;0;L;;;;;N;;;0538;;0538
+0569;ARMENIAN SMALL LETTER TO;Ll;0;L;;;;;N;;;0539;;0539
+056A;ARMENIAN SMALL LETTER ZHE;Ll;0;L;;;;;N;;;053A;;053A
+056B;ARMENIAN SMALL LETTER INI;Ll;0;L;;;;;N;;;053B;;053B
+056C;ARMENIAN SMALL LETTER LIWN;Ll;0;L;;;;;N;;;053C;;053C
+056D;ARMENIAN SMALL LETTER XEH;Ll;0;L;;;;;N;;;053D;;053D
+056E;ARMENIAN SMALL LETTER CA;Ll;0;L;;;;;N;;;053E;;053E
+056F;ARMENIAN SMALL LETTER KEN;Ll;0;L;;;;;N;;;053F;;053F
+0570;ARMENIAN SMALL LETTER HO;Ll;0;L;;;;;N;;;0540;;0540
+0571;ARMENIAN SMALL LETTER JA;Ll;0;L;;;;;N;;;0541;;0541
+0572;ARMENIAN SMALL LETTER GHAD;Ll;0;L;;;;;N;ARMENIAN SMALL LETTER LAD;;0542;;0542
+0573;ARMENIAN SMALL LETTER CHEH;Ll;0;L;;;;;N;;;0543;;0543
+0574;ARMENIAN SMALL LETTER MEN;Ll;0;L;;;;;N;;;0544;;0544
+0575;ARMENIAN SMALL LETTER YI;Ll;0;L;;;;;N;;;0545;;0545
+0576;ARMENIAN SMALL LETTER NOW;Ll;0;L;;;;;N;;;0546;;0546
+0577;ARMENIAN SMALL LETTER SHA;Ll;0;L;;;;;N;;;0547;;0547
+0578;ARMENIAN SMALL LETTER VO;Ll;0;L;;;;;N;;;0548;;0548
+0579;ARMENIAN SMALL LETTER CHA;Ll;0;L;;;;;N;;;0549;;0549
+057A;ARMENIAN SMALL LETTER PEH;Ll;0;L;;;;;N;;;054A;;054A
+057B;ARMENIAN SMALL LETTER JHEH;Ll;0;L;;;;;N;;;054B;;054B
+057C;ARMENIAN SMALL LETTER RA;Ll;0;L;;;;;N;;;054C;;054C
+057D;ARMENIAN SMALL LETTER SEH;Ll;0;L;;;;;N;;;054D;;054D
+057E;ARMENIAN SMALL LETTER VEW;Ll;0;L;;;;;N;;;054E;;054E
+057F;ARMENIAN SMALL LETTER TIWN;Ll;0;L;;;;;N;;;054F;;054F
+0580;ARMENIAN SMALL LETTER REH;Ll;0;L;;;;;N;;;0550;;0550
+0581;ARMENIAN SMALL LETTER CO;Ll;0;L;;;;;N;;;0551;;0551
+0582;ARMENIAN SMALL LETTER YIWN;Ll;0;L;;;;;N;;;0552;;0552
+0583;ARMENIAN SMALL LETTER PIWR;Ll;0;L;;;;;N;;;0553;;0553
+0584;ARMENIAN SMALL LETTER KEH;Ll;0;L;;;;;N;;;0554;;0554
+0585;ARMENIAN SMALL LETTER OH;Ll;0;L;;;;;N;;;0555;;0555
+0586;ARMENIAN SMALL LETTER FEH;Ll;0;L;;;;;N;;;0556;;0556
+0587;ARMENIAN SMALL LIGATURE ECH YIWN;Ll;0;L;<compat> 0565 0582;;;;N;;;;;
+0588;ARMENIAN SMALL LETTER YI WITH STROKE;Ll;0;L;;;;;N;;;;;
+0589;ARMENIAN FULL STOP;Po;0;L;;;;;N;ARMENIAN PERIOD;;;;
+058A;ARMENIAN HYPHEN;Pd;0;ON;;;;;N;;;;;
+058D;RIGHT-FACING ARMENIAN ETERNITY SIGN;So;0;ON;;;;;N;;;;;
+058E;LEFT-FACING ARMENIAN ETERNITY SIGN;So;0;ON;;;;;N;;;;;
+058F;ARMENIAN DRAM SIGN;Sc;0;ET;;;;;N;;;;;
+0591;HEBREW ACCENT ETNAHTA;Mn;220;NSM;;;;;N;;;;;
+0592;HEBREW ACCENT SEGOL;Mn;230;NSM;;;;;N;;;;;
+0593;HEBREW ACCENT SHALSHELET;Mn;230;NSM;;;;;N;;;;;
+0594;HEBREW ACCENT ZAQEF QATAN;Mn;230;NSM;;;;;N;;;;;
+0595;HEBREW ACCENT ZAQEF GADOL;Mn;230;NSM;;;;;N;;;;;
+0596;HEBREW ACCENT TIPEHA;Mn;220;NSM;;;;;N;;;;;
+0597;HEBREW ACCENT REVIA;Mn;230;NSM;;;;;N;;;;;
+0598;HEBREW ACCENT ZARQA;Mn;230;NSM;;;;;N;;;;;
+0599;HEBREW ACCENT PASHTA;Mn;230;NSM;;;;;N;;;;;
+059A;HEBREW ACCENT YETIV;Mn;222;NSM;;;;;N;;;;;
+059B;HEBREW ACCENT TEVIR;Mn;220;NSM;;;;;N;;;;;
+059C;HEBREW ACCENT GERESH;Mn;230;NSM;;;;;N;;;;;
+059D;HEBREW ACCENT GERESH MUQDAM;Mn;230;NSM;;;;;N;;;;;
+059E;HEBREW ACCENT GERSHAYIM;Mn;230;NSM;;;;;N;;;;;
+059F;HEBREW ACCENT QARNEY PARA;Mn;230;NSM;;;;;N;;;;;
+05A0;HEBREW ACCENT TELISHA GEDOLA;Mn;230;NSM;;;;;N;;;;;
+05A1;HEBREW ACCENT PAZER;Mn;230;NSM;;;;;N;;;;;
+05A2;HEBREW ACCENT ATNAH HAFUKH;Mn;220;NSM;;;;;N;;;;;
+05A3;HEBREW ACCENT MUNAH;Mn;220;NSM;;;;;N;;;;;
+05A4;HEBREW ACCENT MAHAPAKH;Mn;220;NSM;;;;;N;;;;;
+05A5;HEBREW ACCENT MERKHA;Mn;220;NSM;;;;;N;;;;;
+05A6;HEBREW ACCENT MERKHA KEFULA;Mn;220;NSM;;;;;N;;;;;
+05A7;HEBREW ACCENT DARGA;Mn;220;NSM;;;;;N;;;;;
+05A8;HEBREW ACCENT QADMA;Mn;230;NSM;;;;;N;;;;;
+05A9;HEBREW ACCENT TELISHA QETANA;Mn;230;NSM;;;;;N;;;;;
+05AA;HEBREW ACCENT YERAH BEN YOMO;Mn;220;NSM;;;;;N;;;;;
+05AB;HEBREW ACCENT OLE;Mn;230;NSM;;;;;N;;;;;
+05AC;HEBREW ACCENT ILUY;Mn;230;NSM;;;;;N;;;;;
+05AD;HEBREW ACCENT DEHI;Mn;222;NSM;;;;;N;;;;;
+05AE;HEBREW ACCENT ZINOR;Mn;228;NSM;;;;;N;;;;;
+05AF;HEBREW MARK MASORA CIRCLE;Mn;230;NSM;;;;;N;;;;;
+05B0;HEBREW POINT SHEVA;Mn;10;NSM;;;;;N;;;;;
+05B1;HEBREW POINT HATAF SEGOL;Mn;11;NSM;;;;;N;;;;;
+05B2;HEBREW POINT HATAF PATAH;Mn;12;NSM;;;;;N;;;;;
+05B3;HEBREW POINT HATAF QAMATS;Mn;13;NSM;;;;;N;;;;;
+05B4;HEBREW POINT HIRIQ;Mn;14;NSM;;;;;N;;;;;
+05B5;HEBREW POINT TSERE;Mn;15;NSM;;;;;N;;;;;
+05B6;HEBREW POINT SEGOL;Mn;16;NSM;;;;;N;;;;;
+05B7;HEBREW POINT PATAH;Mn;17;NSM;;;;;N;;;;;
+05B8;HEBREW POINT QAMATS;Mn;18;NSM;;;;;N;;;;;
+05B9;HEBREW POINT HOLAM;Mn;19;NSM;;;;;N;;;;;
+05BA;HEBREW POINT HOLAM HASER FOR VAV;Mn;19;NSM;;;;;N;;;;;
+05BB;HEBREW POINT QUBUTS;Mn;20;NSM;;;;;N;;;;;
+05BC;HEBREW POINT DAGESH OR MAPIQ;Mn;21;NSM;;;;;N;HEBREW POINT DAGESH;;;;
+05BD;HEBREW POINT METEG;Mn;22;NSM;;;;;N;;;;;
+05BE;HEBREW PUNCTUATION MAQAF;Pd;0;R;;;;;N;;;;;
+05BF;HEBREW POINT RAFE;Mn;23;NSM;;;;;N;;;;;
+05C0;HEBREW PUNCTUATION PASEQ;Po;0;R;;;;;N;HEBREW POINT PASEQ;;;;
+05C1;HEBREW POINT SHIN DOT;Mn;24;NSM;;;;;N;;;;;
+05C2;HEBREW POINT SIN DOT;Mn;25;NSM;;;;;N;;;;;
+05C3;HEBREW PUNCTUATION SOF PASUQ;Po;0;R;;;;;N;;;;;
+05C4;HEBREW MARK UPPER DOT;Mn;230;NSM;;;;;N;;;;;
+05C5;HEBREW MARK LOWER DOT;Mn;220;NSM;;;;;N;;;;;
+05C6;HEBREW PUNCTUATION NUN HAFUKHA;Po;0;R;;;;;N;;;;;
+05C7;HEBREW POINT QAMATS QATAN;Mn;18;NSM;;;;;N;;;;;
+05D0;HEBREW LETTER ALEF;Lo;0;R;;;;;N;;;;;
+05D1;HEBREW LETTER BET;Lo;0;R;;;;;N;;;;;
+05D2;HEBREW LETTER GIMEL;Lo;0;R;;;;;N;;;;;
+05D3;HEBREW LETTER DALET;Lo;0;R;;;;;N;;;;;
+05D4;HEBREW LETTER HE;Lo;0;R;;;;;N;;;;;
+05D5;HEBREW LETTER VAV;Lo;0;R;;;;;N;;;;;
+05D6;HEBREW LETTER ZAYIN;Lo;0;R;;;;;N;;;;;
+05D7;HEBREW LETTER HET;Lo;0;R;;;;;N;;;;;
+05D8;HEBREW LETTER TET;Lo;0;R;;;;;N;;;;;
+05D9;HEBREW LETTER YOD;Lo;0;R;;;;;N;;;;;
+05DA;HEBREW LETTER FINAL KAF;Lo;0;R;;;;;N;;;;;
+05DB;HEBREW LETTER KAF;Lo;0;R;;;;;N;;;;;
+05DC;HEBREW LETTER LAMED;Lo;0;R;;;;;N;;;;;
+05DD;HEBREW LETTER FINAL MEM;Lo;0;R;;;;;N;;;;;
+05DE;HEBREW LETTER MEM;Lo;0;R;;;;;N;;;;;
+05DF;HEBREW LETTER FINAL NUN;Lo;0;R;;;;;N;;;;;
+05E0;HEBREW LETTER NUN;Lo;0;R;;;;;N;;;;;
+05E1;HEBREW LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+05E2;HEBREW LETTER AYIN;Lo;0;R;;;;;N;;;;;
+05E3;HEBREW LETTER FINAL PE;Lo;0;R;;;;;N;;;;;
+05E4;HEBREW LETTER PE;Lo;0;R;;;;;N;;;;;
+05E5;HEBREW LETTER FINAL TSADI;Lo;0;R;;;;;N;;;;;
+05E6;HEBREW LETTER TSADI;Lo;0;R;;;;;N;;;;;
+05E7;HEBREW LETTER QOF;Lo;0;R;;;;;N;;;;;
+05E8;HEBREW LETTER RESH;Lo;0;R;;;;;N;;;;;
+05E9;HEBREW LETTER SHIN;Lo;0;R;;;;;N;;;;;
+05EA;HEBREW LETTER TAV;Lo;0;R;;;;;N;;;;;
+05EF;HEBREW YOD TRIANGLE;Lo;0;R;;;;;N;;;;;
+05F0;HEBREW LIGATURE YIDDISH DOUBLE VAV;Lo;0;R;;;;;N;HEBREW LETTER DOUBLE VAV;;;;
+05F1;HEBREW LIGATURE YIDDISH VAV YOD;Lo;0;R;;;;;N;HEBREW LETTER VAV YOD;;;;
+05F2;HEBREW LIGATURE YIDDISH DOUBLE YOD;Lo;0;R;;;;;N;HEBREW LETTER DOUBLE YOD;;;;
+05F3;HEBREW PUNCTUATION GERESH;Po;0;R;;;;;N;;;;;
+05F4;HEBREW PUNCTUATION GERSHAYIM;Po;0;R;;;;;N;;;;;
+0600;ARABIC NUMBER SIGN;Cf;0;AN;;;;;N;;;;;
+0601;ARABIC SIGN SANAH;Cf;0;AN;;;;;N;;;;;
+0602;ARABIC FOOTNOTE MARKER;Cf;0;AN;;;;;N;;;;;
+0603;ARABIC SIGN SAFHA;Cf;0;AN;;;;;N;;;;;
+0604;ARABIC SIGN SAMVAT;Cf;0;AN;;;;;N;;;;;
+0605;ARABIC NUMBER MARK ABOVE;Cf;0;AN;;;;;N;;;;;
+0606;ARABIC-INDIC CUBE ROOT;Sm;0;ON;;;;;N;;;;;
+0607;ARABIC-INDIC FOURTH ROOT;Sm;0;ON;;;;;N;;;;;
+0608;ARABIC RAY;Sm;0;AL;;;;;N;;;;;
+0609;ARABIC-INDIC PER MILLE SIGN;Po;0;ET;;;;;N;;;;;
+060A;ARABIC-INDIC PER TEN THOUSAND SIGN;Po;0;ET;;;;;N;;;;;
+060B;AFGHANI SIGN;Sc;0;AL;;;;;N;;;;;
+060C;ARABIC COMMA;Po;0;CS;;;;;N;;;;;
+060D;ARABIC DATE SEPARATOR;Po;0;AL;;;;;N;;;;;
+060E;ARABIC POETIC VERSE SIGN;So;0;ON;;;;;N;;;;;
+060F;ARABIC SIGN MISRA;So;0;ON;;;;;N;;;;;
+0610;ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM;Mn;230;NSM;;;;;N;;;;;
+0611;ARABIC SIGN ALAYHE ASSALLAM;Mn;230;NSM;;;;;N;;;;;
+0612;ARABIC SIGN RAHMATULLAH ALAYHE;Mn;230;NSM;;;;;N;;;;;
+0613;ARABIC SIGN RADI ALLAHOU ANHU;Mn;230;NSM;;;;;N;;;;;
+0614;ARABIC SIGN TAKHALLUS;Mn;230;NSM;;;;;N;;;;;
+0615;ARABIC SMALL HIGH TAH;Mn;230;NSM;;;;;N;;;;;
+0616;ARABIC SMALL HIGH LIGATURE ALEF WITH LAM WITH YEH;Mn;230;NSM;;;;;N;;;;;
+0617;ARABIC SMALL HIGH ZAIN;Mn;230;NSM;;;;;N;;;;;
+0618;ARABIC SMALL FATHA;Mn;30;NSM;;;;;N;;;;;
+0619;ARABIC SMALL DAMMA;Mn;31;NSM;;;;;N;;;;;
+061A;ARABIC SMALL KASRA;Mn;32;NSM;;;;;N;;;;;
+061B;ARABIC SEMICOLON;Po;0;AL;;;;;N;;;;;
+061C;ARABIC LETTER MARK;Cf;0;AL;;;;;N;;;;;
+061E;ARABIC TRIPLE DOT PUNCTUATION MARK;Po;0;AL;;;;;N;;;;;
+061F;ARABIC QUESTION MARK;Po;0;AL;;;;;N;;;;;
+0620;ARABIC LETTER KASHMIRI YEH;Lo;0;AL;;;;;N;;;;;
+0621;ARABIC LETTER HAMZA;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAH;;;;
+0622;ARABIC LETTER ALEF WITH MADDA ABOVE;Lo;0;AL;0627 0653;;;;N;ARABIC LETTER MADDAH ON ALEF;;;;
+0623;ARABIC LETTER ALEF WITH HAMZA ABOVE;Lo;0;AL;0627 0654;;;;N;ARABIC LETTER HAMZAH ON ALEF;;;;
+0624;ARABIC LETTER WAW WITH HAMZA ABOVE;Lo;0;AL;0648 0654;;;;N;ARABIC LETTER HAMZAH ON WAW;;;;
+0625;ARABIC LETTER ALEF WITH HAMZA BELOW;Lo;0;AL;0627 0655;;;;N;ARABIC LETTER HAMZAH UNDER ALEF;;;;
+0626;ARABIC LETTER YEH WITH HAMZA ABOVE;Lo;0;AL;064A 0654;;;;N;ARABIC LETTER HAMZAH ON YA;;;;
+0627;ARABIC LETTER ALEF;Lo;0;AL;;;;;N;;;;;
+0628;ARABIC LETTER BEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA;;;;
+0629;ARABIC LETTER TEH MARBUTA;Lo;0;AL;;;;;N;ARABIC LETTER TAA MARBUTAH;;;;
+062A;ARABIC LETTER TEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA;;;;
+062B;ARABIC LETTER THEH;Lo;0;AL;;;;;N;ARABIC LETTER THAA;;;;
+062C;ARABIC LETTER JEEM;Lo;0;AL;;;;;N;;;;;
+062D;ARABIC LETTER HAH;Lo;0;AL;;;;;N;ARABIC LETTER HAA;;;;
+062E;ARABIC LETTER KHAH;Lo;0;AL;;;;;N;ARABIC LETTER KHAA;;;;
+062F;ARABIC LETTER DAL;Lo;0;AL;;;;;N;;;;;
+0630;ARABIC LETTER THAL;Lo;0;AL;;;;;N;;;;;
+0631;ARABIC LETTER REH;Lo;0;AL;;;;;N;ARABIC LETTER RA;;;;
+0632;ARABIC LETTER ZAIN;Lo;0;AL;;;;;N;;;;;
+0633;ARABIC LETTER SEEN;Lo;0;AL;;;;;N;;;;;
+0634;ARABIC LETTER SHEEN;Lo;0;AL;;;;;N;;;;;
+0635;ARABIC LETTER SAD;Lo;0;AL;;;;;N;;;;;
+0636;ARABIC LETTER DAD;Lo;0;AL;;;;;N;;;;;
+0637;ARABIC LETTER TAH;Lo;0;AL;;;;;N;;;;;
+0638;ARABIC LETTER ZAH;Lo;0;AL;;;;;N;ARABIC LETTER DHAH;;;;
+0639;ARABIC LETTER AIN;Lo;0;AL;;;;;N;;;;;
+063A;ARABIC LETTER GHAIN;Lo;0;AL;;;;;N;;;;;
+063B;ARABIC LETTER KEHEH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+063C;ARABIC LETTER KEHEH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+063D;ARABIC LETTER FARSI YEH WITH INVERTED V;Lo;0;AL;;;;;N;;;;;
+063E;ARABIC LETTER FARSI YEH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+063F;ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+0640;ARABIC TATWEEL;Lm;0;AL;;;;;N;;;;;
+0641;ARABIC LETTER FEH;Lo;0;AL;;;;;N;ARABIC LETTER FA;;;;
+0642;ARABIC LETTER QAF;Lo;0;AL;;;;;N;;;;;
+0643;ARABIC LETTER KAF;Lo;0;AL;;;;;N;ARABIC LETTER CAF;;;;
+0644;ARABIC LETTER LAM;Lo;0;AL;;;;;N;;;;;
+0645;ARABIC LETTER MEEM;Lo;0;AL;;;;;N;;;;;
+0646;ARABIC LETTER NOON;Lo;0;AL;;;;;N;;;;;
+0647;ARABIC LETTER HEH;Lo;0;AL;;;;;N;ARABIC LETTER HA;;;;
+0648;ARABIC LETTER WAW;Lo;0;AL;;;;;N;;;;;
+0649;ARABIC LETTER ALEF MAKSURA;Lo;0;AL;;;;;N;ARABIC LETTER ALEF MAQSURAH;;;;
+064A;ARABIC LETTER YEH;Lo;0;AL;;;;;N;ARABIC LETTER YA;;;;
+064B;ARABIC FATHATAN;Mn;27;NSM;;;;;N;;;;;
+064C;ARABIC DAMMATAN;Mn;28;NSM;;;;;N;;;;;
+064D;ARABIC KASRATAN;Mn;29;NSM;;;;;N;;;;;
+064E;ARABIC FATHA;Mn;30;NSM;;;;;N;ARABIC FATHAH;;;;
+064F;ARABIC DAMMA;Mn;31;NSM;;;;;N;ARABIC DAMMAH;;;;
+0650;ARABIC KASRA;Mn;32;NSM;;;;;N;ARABIC KASRAH;;;;
+0651;ARABIC SHADDA;Mn;33;NSM;;;;;N;ARABIC SHADDAH;;;;
+0652;ARABIC SUKUN;Mn;34;NSM;;;;;N;;;;;
+0653;ARABIC MADDAH ABOVE;Mn;230;NSM;;;;;N;;;;;
+0654;ARABIC HAMZA ABOVE;Mn;230;NSM;;;;;N;;;;;
+0655;ARABIC HAMZA BELOW;Mn;220;NSM;;;;;N;;;;;
+0656;ARABIC SUBSCRIPT ALEF;Mn;220;NSM;;;;;N;;;;;
+0657;ARABIC INVERTED DAMMA;Mn;230;NSM;;;;;N;;;;;
+0658;ARABIC MARK NOON GHUNNA;Mn;230;NSM;;;;;N;;;;;
+0659;ARABIC ZWARAKAY;Mn;230;NSM;;;;;N;;;;;
+065A;ARABIC VOWEL SIGN SMALL V ABOVE;Mn;230;NSM;;;;;N;;;;;
+065B;ARABIC VOWEL SIGN INVERTED SMALL V ABOVE;Mn;230;NSM;;;;;N;;;;;
+065C;ARABIC VOWEL SIGN DOT BELOW;Mn;220;NSM;;;;;N;;;;;
+065D;ARABIC REVERSED DAMMA;Mn;230;NSM;;;;;N;;;;;
+065E;ARABIC FATHA WITH TWO DOTS;Mn;230;NSM;;;;;N;;;;;
+065F;ARABIC WAVY HAMZA BELOW;Mn;220;NSM;;;;;N;;;;;
+0660;ARABIC-INDIC DIGIT ZERO;Nd;0;AN;;0;0;0;N;;;;;
+0661;ARABIC-INDIC DIGIT ONE;Nd;0;AN;;1;1;1;N;;;;;
+0662;ARABIC-INDIC DIGIT TWO;Nd;0;AN;;2;2;2;N;;;;;
+0663;ARABIC-INDIC DIGIT THREE;Nd;0;AN;;3;3;3;N;;;;;
+0664;ARABIC-INDIC DIGIT FOUR;Nd;0;AN;;4;4;4;N;;;;;
+0665;ARABIC-INDIC DIGIT FIVE;Nd;0;AN;;5;5;5;N;;;;;
+0666;ARABIC-INDIC DIGIT SIX;Nd;0;AN;;6;6;6;N;;;;;
+0667;ARABIC-INDIC DIGIT SEVEN;Nd;0;AN;;7;7;7;N;;;;;
+0668;ARABIC-INDIC DIGIT EIGHT;Nd;0;AN;;8;8;8;N;;;;;
+0669;ARABIC-INDIC DIGIT NINE;Nd;0;AN;;9;9;9;N;;;;;
+066A;ARABIC PERCENT SIGN;Po;0;ET;;;;;N;;;;;
+066B;ARABIC DECIMAL SEPARATOR;Po;0;AN;;;;;N;;;;;
+066C;ARABIC THOUSANDS SEPARATOR;Po;0;AN;;;;;N;;;;;
+066D;ARABIC FIVE POINTED STAR;Po;0;AL;;;;;N;;;;;
+066E;ARABIC LETTER DOTLESS BEH;Lo;0;AL;;;;;N;;;;;
+066F;ARABIC LETTER DOTLESS QAF;Lo;0;AL;;;;;N;;;;;
+0670;ARABIC LETTER SUPERSCRIPT ALEF;Mn;35;NSM;;;;;N;ARABIC ALEF ABOVE;;;;
+0671;ARABIC LETTER ALEF WASLA;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAT WASL ON ALEF;;;;
+0672;ARABIC LETTER ALEF WITH WAVY HAMZA ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER WAVY HAMZAH ON ALEF;;;;
+0673;ARABIC LETTER ALEF WITH WAVY HAMZA BELOW;Lo;0;AL;;;;;N;ARABIC LETTER WAVY HAMZAH UNDER ALEF;;;;
+0674;ARABIC LETTER HIGH HAMZA;Lo;0;AL;;;;;N;ARABIC LETTER HIGH HAMZAH;;;;
+0675;ARABIC LETTER HIGH HAMZA ALEF;Lo;0;AL;<compat> 0627 0674;;;;N;ARABIC LETTER HIGH HAMZAH ALEF;;;;
+0676;ARABIC LETTER HIGH HAMZA WAW;Lo;0;AL;<compat> 0648 0674;;;;N;ARABIC LETTER HIGH HAMZAH WAW;;;;
+0677;ARABIC LETTER U WITH HAMZA ABOVE;Lo;0;AL;<compat> 06C7 0674;;;;N;ARABIC LETTER HIGH HAMZAH WAW WITH DAMMAH;;;;
+0678;ARABIC LETTER HIGH HAMZA YEH;Lo;0;AL;<compat> 064A 0674;;;;N;ARABIC LETTER HIGH HAMZAH YA;;;;
+0679;ARABIC LETTER TTEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH SMALL TAH;;;;
+067A;ARABIC LETTER TTEHEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH TWO DOTS VERTICAL ABOVE;;;;
+067B;ARABIC LETTER BEEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA WITH TWO DOTS VERTICAL BELOW;;;;
+067C;ARABIC LETTER TEH WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH RING;;;;
+067D;ARABIC LETTER TEH WITH THREE DOTS ABOVE DOWNWARDS;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH THREE DOTS ABOVE DOWNWARD;;;;
+067E;ARABIC LETTER PEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH THREE DOTS BELOW;;;;
+067F;ARABIC LETTER TEHEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH FOUR DOTS ABOVE;;;;
+0680;ARABIC LETTER BEHEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA WITH FOUR DOTS BELOW;;;;
+0681;ARABIC LETTER HAH WITH HAMZA ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAH ON HAA;;;;
+0682;ARABIC LETTER HAH WITH TWO DOTS VERTICAL ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH TWO DOTS VERTICAL ABOVE;;;;
+0683;ARABIC LETTER NYEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE TWO DOTS;;;;
+0684;ARABIC LETTER DYEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE TWO DOTS VERTICAL;;;;
+0685;ARABIC LETTER HAH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH THREE DOTS ABOVE;;;;
+0686;ARABIC LETTER TCHEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE THREE DOTS DOWNWARD;;;;
+0687;ARABIC LETTER TCHEHEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE FOUR DOTS;;;;
+0688;ARABIC LETTER DDAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH SMALL TAH;;;;
+0689;ARABIC LETTER DAL WITH RING;Lo;0;AL;;;;;N;;;;;
+068A;ARABIC LETTER DAL WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+068B;ARABIC LETTER DAL WITH DOT BELOW AND SMALL TAH;Lo;0;AL;;;;;N;;;;;
+068C;ARABIC LETTER DAHAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH TWO DOTS ABOVE;;;;
+068D;ARABIC LETTER DDAHAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH TWO DOTS BELOW;;;;
+068E;ARABIC LETTER DUL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH THREE DOTS ABOVE;;;;
+068F;ARABIC LETTER DAL WITH THREE DOTS ABOVE DOWNWARDS;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH THREE DOTS ABOVE DOWNWARD;;;;
+0690;ARABIC LETTER DAL WITH FOUR DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+0691;ARABIC LETTER RREH;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL TAH;;;;
+0692;ARABIC LETTER REH WITH SMALL V;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL V;;;;
+0693;ARABIC LETTER REH WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH RING;;;;
+0694;ARABIC LETTER REH WITH DOT BELOW;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH DOT BELOW;;;;
+0695;ARABIC LETTER REH WITH SMALL V BELOW;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL V BELOW;;;;
+0696;ARABIC LETTER REH WITH DOT BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH DOT BELOW AND DOT ABOVE;;;;
+0697;ARABIC LETTER REH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH TWO DOTS ABOVE;;;;
+0698;ARABIC LETTER JEH;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH THREE DOTS ABOVE;;;;
+0699;ARABIC LETTER REH WITH FOUR DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH FOUR DOTS ABOVE;;;;
+069A;ARABIC LETTER SEEN WITH DOT BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+069B;ARABIC LETTER SEEN WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+069C;ARABIC LETTER SEEN WITH THREE DOTS BELOW AND THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+069D;ARABIC LETTER SAD WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+069E;ARABIC LETTER SAD WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+069F;ARABIC LETTER TAH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06A0;ARABIC LETTER AIN WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06A1;ARABIC LETTER DOTLESS FEH;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS FA;;;;
+06A2;ARABIC LETTER FEH WITH DOT MOVED BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH DOT MOVED BELOW;;;;
+06A3;ARABIC LETTER FEH WITH DOT BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH DOT BELOW;;;;
+06A4;ARABIC LETTER VEH;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH THREE DOTS ABOVE;;;;
+06A5;ARABIC LETTER FEH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH THREE DOTS BELOW;;;;
+06A6;ARABIC LETTER PEHEH;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH FOUR DOTS ABOVE;;;;
+06A7;ARABIC LETTER QAF WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+06A8;ARABIC LETTER QAF WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06A9;ARABIC LETTER KEHEH;Lo;0;AL;;;;;N;ARABIC LETTER OPEN CAF;;;;
+06AA;ARABIC LETTER SWASH KAF;Lo;0;AL;;;;;N;ARABIC LETTER SWASH CAF;;;;
+06AB;ARABIC LETTER KAF WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH RING;;;;
+06AC;ARABIC LETTER KAF WITH DOT ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH DOT ABOVE;;;;
+06AD;ARABIC LETTER NG;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH THREE DOTS ABOVE;;;;
+06AE;ARABIC LETTER KAF WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH THREE DOTS BELOW;;;;
+06AF;ARABIC LETTER GAF;Lo;0;AL;;;;;N;;;;;
+06B0;ARABIC LETTER GAF WITH RING;Lo;0;AL;;;;;N;;;;;
+06B1;ARABIC LETTER NGOEH;Lo;0;AL;;;;;N;ARABIC LETTER GAF WITH TWO DOTS ABOVE;;;;
+06B2;ARABIC LETTER GAF WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+06B3;ARABIC LETTER GUEH;Lo;0;AL;;;;;N;ARABIC LETTER GAF WITH TWO DOTS VERTICAL BELOW;;;;
+06B4;ARABIC LETTER GAF WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06B5;ARABIC LETTER LAM WITH SMALL V;Lo;0;AL;;;;;N;;;;;
+06B6;ARABIC LETTER LAM WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+06B7;ARABIC LETTER LAM WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06B8;ARABIC LETTER LAM WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+06B9;ARABIC LETTER NOON WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+06BA;ARABIC LETTER NOON GHUNNA;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS NOON;;;;
+06BB;ARABIC LETTER RNOON;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS NOON WITH SMALL TAH;;;;
+06BC;ARABIC LETTER NOON WITH RING;Lo;0;AL;;;;;N;;;;;
+06BD;ARABIC LETTER NOON WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06BE;ARABIC LETTER HEH DOACHASHMEE;Lo;0;AL;;;;;N;ARABIC LETTER KNOTTED HA;;;;
+06BF;ARABIC LETTER TCHEH WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+06C0;ARABIC LETTER HEH WITH YEH ABOVE;Lo;0;AL;06D5 0654;;;;N;ARABIC LETTER HAMZAH ON HA;;;;
+06C1;ARABIC LETTER HEH GOAL;Lo;0;AL;;;;;N;ARABIC LETTER HA GOAL;;;;
+06C2;ARABIC LETTER HEH GOAL WITH HAMZA ABOVE;Lo;0;AL;06C1 0654;;;;N;ARABIC LETTER HAMZAH ON HA GOAL;;;;
+06C3;ARABIC LETTER TEH MARBUTA GOAL;Lo;0;AL;;;;;N;ARABIC LETTER TAA MARBUTAH GOAL;;;;
+06C4;ARABIC LETTER WAW WITH RING;Lo;0;AL;;;;;N;;;;;
+06C5;ARABIC LETTER KIRGHIZ OE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH BAR;;;;
+06C6;ARABIC LETTER OE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH SMALL V;;;;
+06C7;ARABIC LETTER U;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH DAMMAH;;;;
+06C8;ARABIC LETTER YU;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH ALEF ABOVE;;;;
+06C9;ARABIC LETTER KIRGHIZ YU;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH INVERTED SMALL V;;;;
+06CA;ARABIC LETTER WAW WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06CB;ARABIC LETTER VE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH THREE DOTS ABOVE;;;;
+06CC;ARABIC LETTER FARSI YEH;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS YA;;;;
+06CD;ARABIC LETTER YEH WITH TAIL;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH TAIL;;;;
+06CE;ARABIC LETTER YEH WITH SMALL V;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH SMALL V;;;;
+06CF;ARABIC LETTER WAW WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+06D0;ARABIC LETTER E;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH TWO DOTS VERTICAL BELOW;;;;
+06D1;ARABIC LETTER YEH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH THREE DOTS BELOW;;;;
+06D2;ARABIC LETTER YEH BARREE;Lo;0;AL;;;;;N;ARABIC LETTER YA BARREE;;;;
+06D3;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE;Lo;0;AL;06D2 0654;;;;N;ARABIC LETTER HAMZAH ON YA BARREE;;;;
+06D4;ARABIC FULL STOP;Po;0;AL;;;;;N;ARABIC PERIOD;;;;
+06D5;ARABIC LETTER AE;Lo;0;AL;;;;;N;;;;;
+06D6;ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA;Mn;230;NSM;;;;;N;;;;;
+06D7;ARABIC SMALL HIGH LIGATURE QAF WITH LAM WITH ALEF MAKSURA;Mn;230;NSM;;;;;N;;;;;
+06D8;ARABIC SMALL HIGH MEEM INITIAL FORM;Mn;230;NSM;;;;;N;;;;;
+06D9;ARABIC SMALL HIGH LAM ALEF;Mn;230;NSM;;;;;N;;;;;
+06DA;ARABIC SMALL HIGH JEEM;Mn;230;NSM;;;;;N;;;;;
+06DB;ARABIC SMALL HIGH THREE DOTS;Mn;230;NSM;;;;;N;;;;;
+06DC;ARABIC SMALL HIGH SEEN;Mn;230;NSM;;;;;N;;;;;
+06DD;ARABIC END OF AYAH;Cf;0;AN;;;;;N;;;;;
+06DE;ARABIC START OF RUB EL HIZB;So;0;ON;;;;;N;;;;;
+06DF;ARABIC SMALL HIGH ROUNDED ZERO;Mn;230;NSM;;;;;N;;;;;
+06E0;ARABIC SMALL HIGH UPRIGHT RECTANGULAR ZERO;Mn;230;NSM;;;;;N;;;;;
+06E1;ARABIC SMALL HIGH DOTLESS HEAD OF KHAH;Mn;230;NSM;;;;;N;;;;;
+06E2;ARABIC SMALL HIGH MEEM ISOLATED FORM;Mn;230;NSM;;;;;N;;;;;
+06E3;ARABIC SMALL LOW SEEN;Mn;220;NSM;;;;;N;;;;;
+06E4;ARABIC SMALL HIGH MADDA;Mn;230;NSM;;;;;N;;;;;
+06E5;ARABIC SMALL WAW;Lm;0;AL;;;;;N;;;;;
+06E6;ARABIC SMALL YEH;Lm;0;AL;;;;;N;;;;;
+06E7;ARABIC SMALL HIGH YEH;Mn;230;NSM;;;;;N;;;;;
+06E8;ARABIC SMALL HIGH NOON;Mn;230;NSM;;;;;N;;;;;
+06E9;ARABIC PLACE OF SAJDAH;So;0;ON;;;;;N;;;;;
+06EA;ARABIC EMPTY CENTRE LOW STOP;Mn;220;NSM;;;;;N;;;;;
+06EB;ARABIC EMPTY CENTRE HIGH STOP;Mn;230;NSM;;;;;N;;;;;
+06EC;ARABIC ROUNDED HIGH STOP WITH FILLED CENTRE;Mn;230;NSM;;;;;N;;;;;
+06ED;ARABIC SMALL LOW MEEM;Mn;220;NSM;;;;;N;;;;;
+06EE;ARABIC LETTER DAL WITH INVERTED V;Lo;0;AL;;;;;N;;;;;
+06EF;ARABIC LETTER REH WITH INVERTED V;Lo;0;AL;;;;;N;;;;;
+06F0;EXTENDED ARABIC-INDIC DIGIT ZERO;Nd;0;EN;;0;0;0;N;EASTERN ARABIC-INDIC DIGIT ZERO;;;;
+06F1;EXTENDED ARABIC-INDIC DIGIT ONE;Nd;0;EN;;1;1;1;N;EASTERN ARABIC-INDIC DIGIT ONE;;;;
+06F2;EXTENDED ARABIC-INDIC DIGIT TWO;Nd;0;EN;;2;2;2;N;EASTERN ARABIC-INDIC DIGIT TWO;;;;
+06F3;EXTENDED ARABIC-INDIC DIGIT THREE;Nd;0;EN;;3;3;3;N;EASTERN ARABIC-INDIC DIGIT THREE;;;;
+06F4;EXTENDED ARABIC-INDIC DIGIT FOUR;Nd;0;EN;;4;4;4;N;EASTERN ARABIC-INDIC DIGIT FOUR;;;;
+06F5;EXTENDED ARABIC-INDIC DIGIT FIVE;Nd;0;EN;;5;5;5;N;EASTERN ARABIC-INDIC DIGIT FIVE;;;;
+06F6;EXTENDED ARABIC-INDIC DIGIT SIX;Nd;0;EN;;6;6;6;N;EASTERN ARABIC-INDIC DIGIT SIX;;;;
+06F7;EXTENDED ARABIC-INDIC DIGIT SEVEN;Nd;0;EN;;7;7;7;N;EASTERN ARABIC-INDIC DIGIT SEVEN;;;;
+06F8;EXTENDED ARABIC-INDIC DIGIT EIGHT;Nd;0;EN;;8;8;8;N;EASTERN ARABIC-INDIC DIGIT EIGHT;;;;
+06F9;EXTENDED ARABIC-INDIC DIGIT NINE;Nd;0;EN;;9;9;9;N;EASTERN ARABIC-INDIC DIGIT NINE;;;;
+06FA;ARABIC LETTER SHEEN WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+06FB;ARABIC LETTER DAD WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+06FC;ARABIC LETTER GHAIN WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+06FD;ARABIC SIGN SINDHI AMPERSAND;So;0;AL;;;;;N;;;;;
+06FE;ARABIC SIGN SINDHI POSTPOSITION MEN;So;0;AL;;;;;N;;;;;
+06FF;ARABIC LETTER HEH WITH INVERTED V;Lo;0;AL;;;;;N;;;;;
+0700;SYRIAC END OF PARAGRAPH;Po;0;AL;;;;;N;;;;;
+0701;SYRIAC SUPRALINEAR FULL STOP;Po;0;AL;;;;;N;;;;;
+0702;SYRIAC SUBLINEAR FULL STOP;Po;0;AL;;;;;N;;;;;
+0703;SYRIAC SUPRALINEAR COLON;Po;0;AL;;;;;N;;;;;
+0704;SYRIAC SUBLINEAR COLON;Po;0;AL;;;;;N;;;;;
+0705;SYRIAC HORIZONTAL COLON;Po;0;AL;;;;;N;;;;;
+0706;SYRIAC COLON SKEWED LEFT;Po;0;AL;;;;;N;;;;;
+0707;SYRIAC COLON SKEWED RIGHT;Po;0;AL;;;;;N;;;;;
+0708;SYRIAC SUPRALINEAR COLON SKEWED LEFT;Po;0;AL;;;;;N;;;;;
+0709;SYRIAC SUBLINEAR COLON SKEWED RIGHT;Po;0;AL;;;;;N;;;;;
+070A;SYRIAC CONTRACTION;Po;0;AL;;;;;N;;;;;
+070B;SYRIAC HARKLEAN OBELUS;Po;0;AL;;;;;N;;;;;
+070C;SYRIAC HARKLEAN METOBELUS;Po;0;AL;;;;;N;;;;;
+070D;SYRIAC HARKLEAN ASTERISCUS;Po;0;AL;;;;;N;;;;;
+070F;SYRIAC ABBREVIATION MARK;Cf;0;AL;;;;;N;;;;;
+0710;SYRIAC LETTER ALAPH;Lo;0;AL;;;;;N;;;;;
+0711;SYRIAC LETTER SUPERSCRIPT ALAPH;Mn;36;NSM;;;;;N;;;;;
+0712;SYRIAC LETTER BETH;Lo;0;AL;;;;;N;;;;;
+0713;SYRIAC LETTER GAMAL;Lo;0;AL;;;;;N;;;;;
+0714;SYRIAC LETTER GAMAL GARSHUNI;Lo;0;AL;;;;;N;;;;;
+0715;SYRIAC LETTER DALATH;Lo;0;AL;;;;;N;;;;;
+0716;SYRIAC LETTER DOTLESS DALATH RISH;Lo;0;AL;;;;;N;;;;;
+0717;SYRIAC LETTER HE;Lo;0;AL;;;;;N;;;;;
+0718;SYRIAC LETTER WAW;Lo;0;AL;;;;;N;;;;;
+0719;SYRIAC LETTER ZAIN;Lo;0;AL;;;;;N;;;;;
+071A;SYRIAC LETTER HETH;Lo;0;AL;;;;;N;;;;;
+071B;SYRIAC LETTER TETH;Lo;0;AL;;;;;N;;;;;
+071C;SYRIAC LETTER TETH GARSHUNI;Lo;0;AL;;;;;N;;;;;
+071D;SYRIAC LETTER YUDH;Lo;0;AL;;;;;N;;;;;
+071E;SYRIAC LETTER YUDH HE;Lo;0;AL;;;;;N;;;;;
+071F;SYRIAC LETTER KAPH;Lo;0;AL;;;;;N;;;;;
+0720;SYRIAC LETTER LAMADH;Lo;0;AL;;;;;N;;;;;
+0721;SYRIAC LETTER MIM;Lo;0;AL;;;;;N;;;;;
+0722;SYRIAC LETTER NUN;Lo;0;AL;;;;;N;;;;;
+0723;SYRIAC LETTER SEMKATH;Lo;0;AL;;;;;N;;;;;
+0724;SYRIAC LETTER FINAL SEMKATH;Lo;0;AL;;;;;N;;;;;
+0725;SYRIAC LETTER E;Lo;0;AL;;;;;N;;;;;
+0726;SYRIAC LETTER PE;Lo;0;AL;;;;;N;;;;;
+0727;SYRIAC LETTER REVERSED PE;Lo;0;AL;;;;;N;;;;;
+0728;SYRIAC LETTER SADHE;Lo;0;AL;;;;;N;;;;;
+0729;SYRIAC LETTER QAPH;Lo;0;AL;;;;;N;;;;;
+072A;SYRIAC LETTER RISH;Lo;0;AL;;;;;N;;;;;
+072B;SYRIAC LETTER SHIN;Lo;0;AL;;;;;N;;;;;
+072C;SYRIAC LETTER TAW;Lo;0;AL;;;;;N;;;;;
+072D;SYRIAC LETTER PERSIAN BHETH;Lo;0;AL;;;;;N;;;;;
+072E;SYRIAC LETTER PERSIAN GHAMAL;Lo;0;AL;;;;;N;;;;;
+072F;SYRIAC LETTER PERSIAN DHALATH;Lo;0;AL;;;;;N;;;;;
+0730;SYRIAC PTHAHA ABOVE;Mn;230;NSM;;;;;N;;;;;
+0731;SYRIAC PTHAHA BELOW;Mn;220;NSM;;;;;N;;;;;
+0732;SYRIAC PTHAHA DOTTED;Mn;230;NSM;;;;;N;;;;;
+0733;SYRIAC ZQAPHA ABOVE;Mn;230;NSM;;;;;N;;;;;
+0734;SYRIAC ZQAPHA BELOW;Mn;220;NSM;;;;;N;;;;;
+0735;SYRIAC ZQAPHA DOTTED;Mn;230;NSM;;;;;N;;;;;
+0736;SYRIAC RBASA ABOVE;Mn;230;NSM;;;;;N;;;;;
+0737;SYRIAC RBASA BELOW;Mn;220;NSM;;;;;N;;;;;
+0738;SYRIAC DOTTED ZLAMA HORIZONTAL;Mn;220;NSM;;;;;N;;;;;
+0739;SYRIAC DOTTED ZLAMA ANGULAR;Mn;220;NSM;;;;;N;;;;;
+073A;SYRIAC HBASA ABOVE;Mn;230;NSM;;;;;N;;;;;
+073B;SYRIAC HBASA BELOW;Mn;220;NSM;;;;;N;;;;;
+073C;SYRIAC HBASA-ESASA DOTTED;Mn;220;NSM;;;;;N;;;;;
+073D;SYRIAC ESASA ABOVE;Mn;230;NSM;;;;;N;;;;;
+073E;SYRIAC ESASA BELOW;Mn;220;NSM;;;;;N;;;;;
+073F;SYRIAC RWAHA;Mn;230;NSM;;;;;N;;;;;
+0740;SYRIAC FEMININE DOT;Mn;230;NSM;;;;;N;;;;;
+0741;SYRIAC QUSHSHAYA;Mn;230;NSM;;;;;N;;;;;
+0742;SYRIAC RUKKAKHA;Mn;220;NSM;;;;;N;;;;;
+0743;SYRIAC TWO VERTICAL DOTS ABOVE;Mn;230;NSM;;;;;N;;;;;
+0744;SYRIAC TWO VERTICAL DOTS BELOW;Mn;220;NSM;;;;;N;;;;;
+0745;SYRIAC THREE DOTS ABOVE;Mn;230;NSM;;;;;N;;;;;
+0746;SYRIAC THREE DOTS BELOW;Mn;220;NSM;;;;;N;;;;;
+0747;SYRIAC OBLIQUE LINE ABOVE;Mn;230;NSM;;;;;N;;;;;
+0748;SYRIAC OBLIQUE LINE BELOW;Mn;220;NSM;;;;;N;;;;;
+0749;SYRIAC MUSIC;Mn;230;NSM;;;;;N;;;;;
+074A;SYRIAC BARREKH;Mn;230;NSM;;;;;N;;;;;
+074D;SYRIAC LETTER SOGDIAN ZHAIN;Lo;0;AL;;;;;N;;;;;
+074E;SYRIAC LETTER SOGDIAN KHAPH;Lo;0;AL;;;;;N;;;;;
+074F;SYRIAC LETTER SOGDIAN FE;Lo;0;AL;;;;;N;;;;;
+0750;ARABIC LETTER BEH WITH THREE DOTS HORIZONTALLY BELOW;Lo;0;AL;;;;;N;;;;;
+0751;ARABIC LETTER BEH WITH DOT BELOW AND THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+0752;ARABIC LETTER BEH WITH THREE DOTS POINTING UPWARDS BELOW;Lo;0;AL;;;;;N;;;;;
+0753;ARABIC LETTER BEH WITH THREE DOTS POINTING UPWARDS BELOW AND TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+0754;ARABIC LETTER BEH WITH TWO DOTS BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+0755;ARABIC LETTER BEH WITH INVERTED SMALL V BELOW;Lo;0;AL;;;;;N;;;;;
+0756;ARABIC LETTER BEH WITH SMALL V;Lo;0;AL;;;;;N;;;;;
+0757;ARABIC LETTER HAH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+0758;ARABIC LETTER HAH WITH THREE DOTS POINTING UPWARDS BELOW;Lo;0;AL;;;;;N;;;;;
+0759;ARABIC LETTER DAL WITH TWO DOTS VERTICALLY BELOW AND SMALL TAH;Lo;0;AL;;;;;N;;;;;
+075A;ARABIC LETTER DAL WITH INVERTED SMALL V BELOW;Lo;0;AL;;;;;N;;;;;
+075B;ARABIC LETTER REH WITH STROKE;Lo;0;AL;;;;;N;;;;;
+075C;ARABIC LETTER SEEN WITH FOUR DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+075D;ARABIC LETTER AIN WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+075E;ARABIC LETTER AIN WITH THREE DOTS POINTING DOWNWARDS ABOVE;Lo;0;AL;;;;;N;;;;;
+075F;ARABIC LETTER AIN WITH TWO DOTS VERTICALLY ABOVE;Lo;0;AL;;;;;N;;;;;
+0760;ARABIC LETTER FEH WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+0761;ARABIC LETTER FEH WITH THREE DOTS POINTING UPWARDS BELOW;Lo;0;AL;;;;;N;;;;;
+0762;ARABIC LETTER KEHEH WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+0763;ARABIC LETTER KEHEH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+0764;ARABIC LETTER KEHEH WITH THREE DOTS POINTING UPWARDS BELOW;Lo;0;AL;;;;;N;;;;;
+0765;ARABIC LETTER MEEM WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+0766;ARABIC LETTER MEEM WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+0767;ARABIC LETTER NOON WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+0768;ARABIC LETTER NOON WITH SMALL TAH;Lo;0;AL;;;;;N;;;;;
+0769;ARABIC LETTER NOON WITH SMALL V;Lo;0;AL;;;;;N;;;;;
+076A;ARABIC LETTER LAM WITH BAR;Lo;0;AL;;;;;N;;;;;
+076B;ARABIC LETTER REH WITH TWO DOTS VERTICALLY ABOVE;Lo;0;AL;;;;;N;;;;;
+076C;ARABIC LETTER REH WITH HAMZA ABOVE;Lo;0;AL;;;;;N;;;;;
+076D;ARABIC LETTER SEEN WITH TWO DOTS VERTICALLY ABOVE;Lo;0;AL;;;;;N;;;;;
+076E;ARABIC LETTER HAH WITH SMALL ARABIC LETTER TAH BELOW;Lo;0;AL;;;;;N;;;;;
+076F;ARABIC LETTER HAH WITH SMALL ARABIC LETTER TAH AND TWO DOTS;Lo;0;AL;;;;;N;;;;;
+0770;ARABIC LETTER SEEN WITH SMALL ARABIC LETTER TAH AND TWO DOTS;Lo;0;AL;;;;;N;;;;;
+0771;ARABIC LETTER REH WITH SMALL ARABIC LETTER TAH AND TWO DOTS;Lo;0;AL;;;;;N;;;;;
+0772;ARABIC LETTER HAH WITH SMALL ARABIC LETTER TAH ABOVE;Lo;0;AL;;;;;N;;;;;
+0773;ARABIC LETTER ALEF WITH EXTENDED ARABIC-INDIC DIGIT TWO ABOVE;Lo;0;AL;;;;;N;;;;;
+0774;ARABIC LETTER ALEF WITH EXTENDED ARABIC-INDIC DIGIT THREE ABOVE;Lo;0;AL;;;;;N;;;;;
+0775;ARABIC LETTER FARSI YEH WITH EXTENDED ARABIC-INDIC DIGIT TWO ABOVE;Lo;0;AL;;;;;N;;;;;
+0776;ARABIC LETTER FARSI YEH WITH EXTENDED ARABIC-INDIC DIGIT THREE ABOVE;Lo;0;AL;;;;;N;;;;;
+0777;ARABIC LETTER FARSI YEH WITH EXTENDED ARABIC-INDIC DIGIT FOUR BELOW;Lo;0;AL;;;;;N;;;;;
+0778;ARABIC LETTER WAW WITH EXTENDED ARABIC-INDIC DIGIT TWO ABOVE;Lo;0;AL;;;;;N;;;;;
+0779;ARABIC LETTER WAW WITH EXTENDED ARABIC-INDIC DIGIT THREE ABOVE;Lo;0;AL;;;;;N;;;;;
+077A;ARABIC LETTER YEH BARREE WITH EXTENDED ARABIC-INDIC DIGIT TWO ABOVE;Lo;0;AL;;;;;N;;;;;
+077B;ARABIC LETTER YEH BARREE WITH EXTENDED ARABIC-INDIC DIGIT THREE ABOVE;Lo;0;AL;;;;;N;;;;;
+077C;ARABIC LETTER HAH WITH EXTENDED ARABIC-INDIC DIGIT FOUR BELOW;Lo;0;AL;;;;;N;;;;;
+077D;ARABIC LETTER SEEN WITH EXTENDED ARABIC-INDIC DIGIT FOUR ABOVE;Lo;0;AL;;;;;N;;;;;
+077E;ARABIC LETTER SEEN WITH INVERTED V;Lo;0;AL;;;;;N;;;;;
+077F;ARABIC LETTER KAF WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+0780;THAANA LETTER HAA;Lo;0;AL;;;;;N;;;;;
+0781;THAANA LETTER SHAVIYANI;Lo;0;AL;;;;;N;;;;;
+0782;THAANA LETTER NOONU;Lo;0;AL;;;;;N;;;;;
+0783;THAANA LETTER RAA;Lo;0;AL;;;;;N;;;;;
+0784;THAANA LETTER BAA;Lo;0;AL;;;;;N;;;;;
+0785;THAANA LETTER LHAVIYANI;Lo;0;AL;;;;;N;;;;;
+0786;THAANA LETTER KAAFU;Lo;0;AL;;;;;N;;;;;
+0787;THAANA LETTER ALIFU;Lo;0;AL;;;;;N;;;;;
+0788;THAANA LETTER VAAVU;Lo;0;AL;;;;;N;;;;;
+0789;THAANA LETTER MEEMU;Lo;0;AL;;;;;N;;;;;
+078A;THAANA LETTER FAAFU;Lo;0;AL;;;;;N;;;;;
+078B;THAANA LETTER DHAALU;Lo;0;AL;;;;;N;;;;;
+078C;THAANA LETTER THAA;Lo;0;AL;;;;;N;;;;;
+078D;THAANA LETTER LAAMU;Lo;0;AL;;;;;N;;;;;
+078E;THAANA LETTER GAAFU;Lo;0;AL;;;;;N;;;;;
+078F;THAANA LETTER GNAVIYANI;Lo;0;AL;;;;;N;;;;;
+0790;THAANA LETTER SEENU;Lo;0;AL;;;;;N;;;;;
+0791;THAANA LETTER DAVIYANI;Lo;0;AL;;;;;N;;;;;
+0792;THAANA LETTER ZAVIYANI;Lo;0;AL;;;;;N;;;;;
+0793;THAANA LETTER TAVIYANI;Lo;0;AL;;;;;N;;;;;
+0794;THAANA LETTER YAA;Lo;0;AL;;;;;N;;;;;
+0795;THAANA LETTER PAVIYANI;Lo;0;AL;;;;;N;;;;;
+0796;THAANA LETTER JAVIYANI;Lo;0;AL;;;;;N;;;;;
+0797;THAANA LETTER CHAVIYANI;Lo;0;AL;;;;;N;;;;;
+0798;THAANA LETTER TTAA;Lo;0;AL;;;;;N;;;;;
+0799;THAANA LETTER HHAA;Lo;0;AL;;;;;N;;;;;
+079A;THAANA LETTER KHAA;Lo;0;AL;;;;;N;;;;;
+079B;THAANA LETTER THAALU;Lo;0;AL;;;;;N;;;;;
+079C;THAANA LETTER ZAA;Lo;0;AL;;;;;N;;;;;
+079D;THAANA LETTER SHEENU;Lo;0;AL;;;;;N;;;;;
+079E;THAANA LETTER SAADHU;Lo;0;AL;;;;;N;;;;;
+079F;THAANA LETTER DAADHU;Lo;0;AL;;;;;N;;;;;
+07A0;THAANA LETTER TO;Lo;0;AL;;;;;N;;;;;
+07A1;THAANA LETTER ZO;Lo;0;AL;;;;;N;;;;;
+07A2;THAANA LETTER AINU;Lo;0;AL;;;;;N;;;;;
+07A3;THAANA LETTER GHAINU;Lo;0;AL;;;;;N;;;;;
+07A4;THAANA LETTER QAAFU;Lo;0;AL;;;;;N;;;;;
+07A5;THAANA LETTER WAAVU;Lo;0;AL;;;;;N;;;;;
+07A6;THAANA ABAFILI;Mn;0;NSM;;;;;N;;;;;
+07A7;THAANA AABAAFILI;Mn;0;NSM;;;;;N;;;;;
+07A8;THAANA IBIFILI;Mn;0;NSM;;;;;N;;;;;
+07A9;THAANA EEBEEFILI;Mn;0;NSM;;;;;N;;;;;
+07AA;THAANA UBUFILI;Mn;0;NSM;;;;;N;;;;;
+07AB;THAANA OOBOOFILI;Mn;0;NSM;;;;;N;;;;;
+07AC;THAANA EBEFILI;Mn;0;NSM;;;;;N;;;;;
+07AD;THAANA EYBEYFILI;Mn;0;NSM;;;;;N;;;;;
+07AE;THAANA OBOFILI;Mn;0;NSM;;;;;N;;;;;
+07AF;THAANA OABOAFILI;Mn;0;NSM;;;;;N;;;;;
+07B0;THAANA SUKUN;Mn;0;NSM;;;;;N;;;;;
+07B1;THAANA LETTER NAA;Lo;0;AL;;;;;N;;;;;
+07C0;NKO DIGIT ZERO;Nd;0;R;;0;0;0;N;;;;;
+07C1;NKO DIGIT ONE;Nd;0;R;;1;1;1;N;;;;;
+07C2;NKO DIGIT TWO;Nd;0;R;;2;2;2;N;;;;;
+07C3;NKO DIGIT THREE;Nd;0;R;;3;3;3;N;;;;;
+07C4;NKO DIGIT FOUR;Nd;0;R;;4;4;4;N;;;;;
+07C5;NKO DIGIT FIVE;Nd;0;R;;5;5;5;N;;;;;
+07C6;NKO DIGIT SIX;Nd;0;R;;6;6;6;N;;;;;
+07C7;NKO DIGIT SEVEN;Nd;0;R;;7;7;7;N;;;;;
+07C8;NKO DIGIT EIGHT;Nd;0;R;;8;8;8;N;;;;;
+07C9;NKO DIGIT NINE;Nd;0;R;;9;9;9;N;;;;;
+07CA;NKO LETTER A;Lo;0;R;;;;;N;;;;;
+07CB;NKO LETTER EE;Lo;0;R;;;;;N;;;;;
+07CC;NKO LETTER I;Lo;0;R;;;;;N;;;;;
+07CD;NKO LETTER E;Lo;0;R;;;;;N;;;;;
+07CE;NKO LETTER U;Lo;0;R;;;;;N;;;;;
+07CF;NKO LETTER OO;Lo;0;R;;;;;N;;;;;
+07D0;NKO LETTER O;Lo;0;R;;;;;N;;;;;
+07D1;NKO LETTER DAGBASINNA;Lo;0;R;;;;;N;;;;;
+07D2;NKO LETTER N;Lo;0;R;;;;;N;;;;;
+07D3;NKO LETTER BA;Lo;0;R;;;;;N;;;;;
+07D4;NKO LETTER PA;Lo;0;R;;;;;N;;;;;
+07D5;NKO LETTER TA;Lo;0;R;;;;;N;;;;;
+07D6;NKO LETTER JA;Lo;0;R;;;;;N;;;;;
+07D7;NKO LETTER CHA;Lo;0;R;;;;;N;;;;;
+07D8;NKO LETTER DA;Lo;0;R;;;;;N;;;;;
+07D9;NKO LETTER RA;Lo;0;R;;;;;N;;;;;
+07DA;NKO LETTER RRA;Lo;0;R;;;;;N;;;;;
+07DB;NKO LETTER SA;Lo;0;R;;;;;N;;;;;
+07DC;NKO LETTER GBA;Lo;0;R;;;;;N;;;;;
+07DD;NKO LETTER FA;Lo;0;R;;;;;N;;;;;
+07DE;NKO LETTER KA;Lo;0;R;;;;;N;;;;;
+07DF;NKO LETTER LA;Lo;0;R;;;;;N;;;;;
+07E0;NKO LETTER NA WOLOSO;Lo;0;R;;;;;N;;;;;
+07E1;NKO LETTER MA;Lo;0;R;;;;;N;;;;;
+07E2;NKO LETTER NYA;Lo;0;R;;;;;N;;;;;
+07E3;NKO LETTER NA;Lo;0;R;;;;;N;;;;;
+07E4;NKO LETTER HA;Lo;0;R;;;;;N;;;;;
+07E5;NKO LETTER WA;Lo;0;R;;;;;N;;;;;
+07E6;NKO LETTER YA;Lo;0;R;;;;;N;;;;;
+07E7;NKO LETTER NYA WOLOSO;Lo;0;R;;;;;N;;;;;
+07E8;NKO LETTER JONA JA;Lo;0;R;;;;;N;;;;;
+07E9;NKO LETTER JONA CHA;Lo;0;R;;;;;N;;;;;
+07EA;NKO LETTER JONA RA;Lo;0;R;;;;;N;;;;;
+07EB;NKO COMBINING SHORT HIGH TONE;Mn;230;NSM;;;;;N;;;;;
+07EC;NKO COMBINING SHORT LOW TONE;Mn;230;NSM;;;;;N;;;;;
+07ED;NKO COMBINING SHORT RISING TONE;Mn;230;NSM;;;;;N;;;;;
+07EE;NKO COMBINING LONG DESCENDING TONE;Mn;230;NSM;;;;;N;;;;;
+07EF;NKO COMBINING LONG HIGH TONE;Mn;230;NSM;;;;;N;;;;;
+07F0;NKO COMBINING LONG LOW TONE;Mn;230;NSM;;;;;N;;;;;
+07F1;NKO COMBINING LONG RISING TONE;Mn;230;NSM;;;;;N;;;;;
+07F2;NKO COMBINING NASALIZATION MARK;Mn;220;NSM;;;;;N;;;;;
+07F3;NKO COMBINING DOUBLE DOT ABOVE;Mn;230;NSM;;;;;N;;;;;
+07F4;NKO HIGH TONE APOSTROPHE;Lm;0;R;;;;;N;;;;;
+07F5;NKO LOW TONE APOSTROPHE;Lm;0;R;;;;;N;;;;;
+07F6;NKO SYMBOL OO DENNEN;So;0;ON;;;;;N;;;;;
+07F7;NKO SYMBOL GBAKURUNEN;Po;0;ON;;;;;N;;;;;
+07F8;NKO COMMA;Po;0;ON;;;;;N;;;;;
+07F9;NKO EXCLAMATION MARK;Po;0;ON;;;;;N;;;;;
+07FA;NKO LAJANYALAN;Lm;0;R;;;;;N;;;;;
+07FD;NKO DANTAYALAN;Mn;220;NSM;;;;;N;;;;;
+07FE;NKO DOROME SIGN;Sc;0;R;;;;;N;;;;;
+07FF;NKO TAMAN SIGN;Sc;0;R;;;;;N;;;;;
+0800;SAMARITAN LETTER ALAF;Lo;0;R;;;;;N;;;;;
+0801;SAMARITAN LETTER BIT;Lo;0;R;;;;;N;;;;;
+0802;SAMARITAN LETTER GAMAN;Lo;0;R;;;;;N;;;;;
+0803;SAMARITAN LETTER DALAT;Lo;0;R;;;;;N;;;;;
+0804;SAMARITAN LETTER IY;Lo;0;R;;;;;N;;;;;
+0805;SAMARITAN LETTER BAA;Lo;0;R;;;;;N;;;;;
+0806;SAMARITAN LETTER ZEN;Lo;0;R;;;;;N;;;;;
+0807;SAMARITAN LETTER IT;Lo;0;R;;;;;N;;;;;
+0808;SAMARITAN LETTER TIT;Lo;0;R;;;;;N;;;;;
+0809;SAMARITAN LETTER YUT;Lo;0;R;;;;;N;;;;;
+080A;SAMARITAN LETTER KAAF;Lo;0;R;;;;;N;;;;;
+080B;SAMARITAN LETTER LABAT;Lo;0;R;;;;;N;;;;;
+080C;SAMARITAN LETTER MIM;Lo;0;R;;;;;N;;;;;
+080D;SAMARITAN LETTER NUN;Lo;0;R;;;;;N;;;;;
+080E;SAMARITAN LETTER SINGAAT;Lo;0;R;;;;;N;;;;;
+080F;SAMARITAN LETTER IN;Lo;0;R;;;;;N;;;;;
+0810;SAMARITAN LETTER FI;Lo;0;R;;;;;N;;;;;
+0811;SAMARITAN LETTER TSAADIY;Lo;0;R;;;;;N;;;;;
+0812;SAMARITAN LETTER QUF;Lo;0;R;;;;;N;;;;;
+0813;SAMARITAN LETTER RISH;Lo;0;R;;;;;N;;;;;
+0814;SAMARITAN LETTER SHAN;Lo;0;R;;;;;N;;;;;
+0815;SAMARITAN LETTER TAAF;Lo;0;R;;;;;N;;;;;
+0816;SAMARITAN MARK IN;Mn;230;NSM;;;;;N;;;;;
+0817;SAMARITAN MARK IN-ALAF;Mn;230;NSM;;;;;N;;;;;
+0818;SAMARITAN MARK OCCLUSION;Mn;230;NSM;;;;;N;;;;;
+0819;SAMARITAN MARK DAGESH;Mn;230;NSM;;;;;N;;;;;
+081A;SAMARITAN MODIFIER LETTER EPENTHETIC YUT;Lm;0;R;;;;;N;;;;;
+081B;SAMARITAN MARK EPENTHETIC YUT;Mn;230;NSM;;;;;N;;;;;
+081C;SAMARITAN VOWEL SIGN LONG E;Mn;230;NSM;;;;;N;;;;;
+081D;SAMARITAN VOWEL SIGN E;Mn;230;NSM;;;;;N;;;;;
+081E;SAMARITAN VOWEL SIGN OVERLONG AA;Mn;230;NSM;;;;;N;;;;;
+081F;SAMARITAN VOWEL SIGN LONG AA;Mn;230;NSM;;;;;N;;;;;
+0820;SAMARITAN VOWEL SIGN AA;Mn;230;NSM;;;;;N;;;;;
+0821;SAMARITAN VOWEL SIGN OVERLONG A;Mn;230;NSM;;;;;N;;;;;
+0822;SAMARITAN VOWEL SIGN LONG A;Mn;230;NSM;;;;;N;;;;;
+0823;SAMARITAN VOWEL SIGN A;Mn;230;NSM;;;;;N;;;;;
+0824;SAMARITAN MODIFIER LETTER SHORT A;Lm;0;R;;;;;N;;;;;
+0825;SAMARITAN VOWEL SIGN SHORT A;Mn;230;NSM;;;;;N;;;;;
+0826;SAMARITAN VOWEL SIGN LONG U;Mn;230;NSM;;;;;N;;;;;
+0827;SAMARITAN VOWEL SIGN U;Mn;230;NSM;;;;;N;;;;;
+0828;SAMARITAN MODIFIER LETTER I;Lm;0;R;;;;;N;;;;;
+0829;SAMARITAN VOWEL SIGN LONG I;Mn;230;NSM;;;;;N;;;;;
+082A;SAMARITAN VOWEL SIGN I;Mn;230;NSM;;;;;N;;;;;
+082B;SAMARITAN VOWEL SIGN O;Mn;230;NSM;;;;;N;;;;;
+082C;SAMARITAN VOWEL SIGN SUKUN;Mn;230;NSM;;;;;N;;;;;
+082D;SAMARITAN MARK NEQUDAA;Mn;230;NSM;;;;;N;;;;;
+0830;SAMARITAN PUNCTUATION NEQUDAA;Po;0;R;;;;;N;;;;;
+0831;SAMARITAN PUNCTUATION AFSAAQ;Po;0;R;;;;;N;;;;;
+0832;SAMARITAN PUNCTUATION ANGED;Po;0;R;;;;;N;;;;;
+0833;SAMARITAN PUNCTUATION BAU;Po;0;R;;;;;N;;;;;
+0834;SAMARITAN PUNCTUATION ATMAAU;Po;0;R;;;;;N;;;;;
+0835;SAMARITAN PUNCTUATION SHIYYAALAA;Po;0;R;;;;;N;;;;;
+0836;SAMARITAN ABBREVIATION MARK;Po;0;R;;;;;N;;;;;
+0837;SAMARITAN PUNCTUATION MELODIC QITSA;Po;0;R;;;;;N;;;;;
+0838;SAMARITAN PUNCTUATION ZIQAA;Po;0;R;;;;;N;;;;;
+0839;SAMARITAN PUNCTUATION QITSA;Po;0;R;;;;;N;;;;;
+083A;SAMARITAN PUNCTUATION ZAEF;Po;0;R;;;;;N;;;;;
+083B;SAMARITAN PUNCTUATION TURU;Po;0;R;;;;;N;;;;;
+083C;SAMARITAN PUNCTUATION ARKAANU;Po;0;R;;;;;N;;;;;
+083D;SAMARITAN PUNCTUATION SOF MASHFAAT;Po;0;R;;;;;N;;;;;
+083E;SAMARITAN PUNCTUATION ANNAAU;Po;0;R;;;;;N;;;;;
+0840;MANDAIC LETTER HALQA;Lo;0;R;;;;;N;;;;;
+0841;MANDAIC LETTER AB;Lo;0;R;;;;;N;;;;;
+0842;MANDAIC LETTER AG;Lo;0;R;;;;;N;;;;;
+0843;MANDAIC LETTER AD;Lo;0;R;;;;;N;;;;;
+0844;MANDAIC LETTER AH;Lo;0;R;;;;;N;;;;;
+0845;MANDAIC LETTER USHENNA;Lo;0;R;;;;;N;;;;;
+0846;MANDAIC LETTER AZ;Lo;0;R;;;;;N;;;;;
+0847;MANDAIC LETTER IT;Lo;0;R;;;;;N;;;;;
+0848;MANDAIC LETTER ATT;Lo;0;R;;;;;N;;;;;
+0849;MANDAIC LETTER AKSA;Lo;0;R;;;;;N;;;;;
+084A;MANDAIC LETTER AK;Lo;0;R;;;;;N;;;;;
+084B;MANDAIC LETTER AL;Lo;0;R;;;;;N;;;;;
+084C;MANDAIC LETTER AM;Lo;0;R;;;;;N;;;;;
+084D;MANDAIC LETTER AN;Lo;0;R;;;;;N;;;;;
+084E;MANDAIC LETTER AS;Lo;0;R;;;;;N;;;;;
+084F;MANDAIC LETTER IN;Lo;0;R;;;;;N;;;;;
+0850;MANDAIC LETTER AP;Lo;0;R;;;;;N;;;;;
+0851;MANDAIC LETTER ASZ;Lo;0;R;;;;;N;;;;;
+0852;MANDAIC LETTER AQ;Lo;0;R;;;;;N;;;;;
+0853;MANDAIC LETTER AR;Lo;0;R;;;;;N;;;;;
+0854;MANDAIC LETTER ASH;Lo;0;R;;;;;N;;;;;
+0855;MANDAIC LETTER AT;Lo;0;R;;;;;N;;;;;
+0856;MANDAIC LETTER DUSHENNA;Lo;0;R;;;;;N;;;;;
+0857;MANDAIC LETTER KAD;Lo;0;R;;;;;N;;;;;
+0858;MANDAIC LETTER AIN;Lo;0;R;;;;;N;;;;;
+0859;MANDAIC AFFRICATION MARK;Mn;220;NSM;;;;;N;;;;;
+085A;MANDAIC VOCALIZATION MARK;Mn;220;NSM;;;;;N;;;;;
+085B;MANDAIC GEMINATION MARK;Mn;220;NSM;;;;;N;;;;;
+085E;MANDAIC PUNCTUATION;Po;0;R;;;;;N;;;;;
+0860;SYRIAC LETTER MALAYALAM NGA;Lo;0;AL;;;;;N;;;;;
+0861;SYRIAC LETTER MALAYALAM JA;Lo;0;AL;;;;;N;;;;;
+0862;SYRIAC LETTER MALAYALAM NYA;Lo;0;AL;;;;;N;;;;;
+0863;SYRIAC LETTER MALAYALAM TTA;Lo;0;AL;;;;;N;;;;;
+0864;SYRIAC LETTER MALAYALAM NNA;Lo;0;AL;;;;;N;;;;;
+0865;SYRIAC LETTER MALAYALAM NNNA;Lo;0;AL;;;;;N;;;;;
+0866;SYRIAC LETTER MALAYALAM BHA;Lo;0;AL;;;;;N;;;;;
+0867;SYRIAC LETTER MALAYALAM RA;Lo;0;AL;;;;;N;;;;;
+0868;SYRIAC LETTER MALAYALAM LLA;Lo;0;AL;;;;;N;;;;;
+0869;SYRIAC LETTER MALAYALAM LLLA;Lo;0;AL;;;;;N;;;;;
+086A;SYRIAC LETTER MALAYALAM SSA;Lo;0;AL;;;;;N;;;;;
+08A0;ARABIC LETTER BEH WITH SMALL V BELOW;Lo;0;AL;;;;;N;;;;;
+08A1;ARABIC LETTER BEH WITH HAMZA ABOVE;Lo;0;AL;;;;;N;;;;;
+08A2;ARABIC LETTER JEEM WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+08A3;ARABIC LETTER TAH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+08A4;ARABIC LETTER FEH WITH DOT BELOW AND THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+08A5;ARABIC LETTER QAF WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+08A6;ARABIC LETTER LAM WITH DOUBLE BAR;Lo;0;AL;;;;;N;;;;;
+08A7;ARABIC LETTER MEEM WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+08A8;ARABIC LETTER YEH WITH TWO DOTS BELOW AND HAMZA ABOVE;Lo;0;AL;;;;;N;;;;;
+08A9;ARABIC LETTER YEH WITH TWO DOTS BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+08AA;ARABIC LETTER REH WITH LOOP;Lo;0;AL;;;;;N;;;;;
+08AB;ARABIC LETTER WAW WITH DOT WITHIN;Lo;0;AL;;;;;N;;;;;
+08AC;ARABIC LETTER ROHINGYA YEH;Lo;0;AL;;;;;N;;;;;
+08AD;ARABIC LETTER LOW ALEF;Lo;0;AL;;;;;N;;;;;
+08AE;ARABIC LETTER DAL WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+08AF;ARABIC LETTER SAD WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+08B0;ARABIC LETTER GAF WITH INVERTED STROKE;Lo;0;AL;;;;;N;;;;;
+08B1;ARABIC LETTER STRAIGHT WAW;Lo;0;AL;;;;;N;;;;;
+08B2;ARABIC LETTER ZAIN WITH INVERTED V ABOVE;Lo;0;AL;;;;;N;;;;;
+08B3;ARABIC LETTER AIN WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+08B4;ARABIC LETTER KAF WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+08B6;ARABIC LETTER BEH WITH SMALL MEEM ABOVE;Lo;0;AL;;;;;N;;;;;
+08B7;ARABIC LETTER PEH WITH SMALL MEEM ABOVE;Lo;0;AL;;;;;N;;;;;
+08B8;ARABIC LETTER TEH WITH SMALL TEH ABOVE;Lo;0;AL;;;;;N;;;;;
+08B9;ARABIC LETTER REH WITH SMALL NOON ABOVE;Lo;0;AL;;;;;N;;;;;
+08BA;ARABIC LETTER YEH WITH TWO DOTS BELOW AND SMALL NOON ABOVE;Lo;0;AL;;;;;N;;;;;
+08BB;ARABIC LETTER AFRICAN FEH;Lo;0;AL;;;;;N;;;;;
+08BC;ARABIC LETTER AFRICAN QAF;Lo;0;AL;;;;;N;;;;;
+08BD;ARABIC LETTER AFRICAN NOON;Lo;0;AL;;;;;N;;;;;
+08D3;ARABIC SMALL LOW WAW;Mn;220;NSM;;;;;N;;;;;
+08D4;ARABIC SMALL HIGH WORD AR-RUB;Mn;230;NSM;;;;;N;;;;;
+08D5;ARABIC SMALL HIGH SAD;Mn;230;NSM;;;;;N;;;;;
+08D6;ARABIC SMALL HIGH AIN;Mn;230;NSM;;;;;N;;;;;
+08D7;ARABIC SMALL HIGH QAF;Mn;230;NSM;;;;;N;;;;;
+08D8;ARABIC SMALL HIGH NOON WITH KASRA;Mn;230;NSM;;;;;N;;;;;
+08D9;ARABIC SMALL LOW NOON WITH KASRA;Mn;230;NSM;;;;;N;;;;;
+08DA;ARABIC SMALL HIGH WORD ATH-THALATHA;Mn;230;NSM;;;;;N;;;;;
+08DB;ARABIC SMALL HIGH WORD AS-SAJDA;Mn;230;NSM;;;;;N;;;;;
+08DC;ARABIC SMALL HIGH WORD AN-NISF;Mn;230;NSM;;;;;N;;;;;
+08DD;ARABIC SMALL HIGH WORD SAKTA;Mn;230;NSM;;;;;N;;;;;
+08DE;ARABIC SMALL HIGH WORD QIF;Mn;230;NSM;;;;;N;;;;;
+08DF;ARABIC SMALL HIGH WORD WAQFA;Mn;230;NSM;;;;;N;;;;;
+08E0;ARABIC SMALL HIGH FOOTNOTE MARKER;Mn;230;NSM;;;;;N;;;;;
+08E1;ARABIC SMALL HIGH SIGN SAFHA;Mn;230;NSM;;;;;N;;;;;
+08E2;ARABIC DISPUTED END OF AYAH;Cf;0;AN;;;;;N;;;;;
+08E3;ARABIC TURNED DAMMA BELOW;Mn;220;NSM;;;;;N;;;;;
+08E4;ARABIC CURLY FATHA;Mn;230;NSM;;;;;N;;;;;
+08E5;ARABIC CURLY DAMMA;Mn;230;NSM;;;;;N;;;;;
+08E6;ARABIC CURLY KASRA;Mn;220;NSM;;;;;N;;;;;
+08E7;ARABIC CURLY FATHATAN;Mn;230;NSM;;;;;N;;;;;
+08E8;ARABIC CURLY DAMMATAN;Mn;230;NSM;;;;;N;;;;;
+08E9;ARABIC CURLY KASRATAN;Mn;220;NSM;;;;;N;;;;;
+08EA;ARABIC TONE ONE DOT ABOVE;Mn;230;NSM;;;;;N;;;;;
+08EB;ARABIC TONE TWO DOTS ABOVE;Mn;230;NSM;;;;;N;;;;;
+08EC;ARABIC TONE LOOP ABOVE;Mn;230;NSM;;;;;N;;;;;
+08ED;ARABIC TONE ONE DOT BELOW;Mn;220;NSM;;;;;N;;;;;
+08EE;ARABIC TONE TWO DOTS BELOW;Mn;220;NSM;;;;;N;;;;;
+08EF;ARABIC TONE LOOP BELOW;Mn;220;NSM;;;;;N;;;;;
+08F0;ARABIC OPEN FATHATAN;Mn;27;NSM;;;;;N;;;;;
+08F1;ARABIC OPEN DAMMATAN;Mn;28;NSM;;;;;N;;;;;
+08F2;ARABIC OPEN KASRATAN;Mn;29;NSM;;;;;N;;;;;
+08F3;ARABIC SMALL HIGH WAW;Mn;230;NSM;;;;;N;;;;;
+08F4;ARABIC FATHA WITH RING;Mn;230;NSM;;;;;N;;;;;
+08F5;ARABIC FATHA WITH DOT ABOVE;Mn;230;NSM;;;;;N;;;;;
+08F6;ARABIC KASRA WITH DOT BELOW;Mn;220;NSM;;;;;N;;;;;
+08F7;ARABIC LEFT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;;
+08F8;ARABIC RIGHT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;;
+08F9;ARABIC LEFT ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;;
+08FA;ARABIC RIGHT ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;;
+08FB;ARABIC DOUBLE RIGHT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;;
+08FC;ARABIC DOUBLE RIGHT ARROWHEAD ABOVE WITH DOT;Mn;230;NSM;;;;;N;;;;;
+08FD;ARABIC RIGHT ARROWHEAD ABOVE WITH DOT;Mn;230;NSM;;;;;N;;;;;
+08FE;ARABIC DAMMA WITH DOT;Mn;230;NSM;;;;;N;;;;;
+08FF;ARABIC MARK SIDEWAYS NOON GHUNNA;Mn;230;NSM;;;;;N;;;;;
+0900;DEVANAGARI SIGN INVERTED CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+0901;DEVANAGARI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+0902;DEVANAGARI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+0903;DEVANAGARI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0904;DEVANAGARI LETTER SHORT A;Lo;0;L;;;;;N;;;;;
+0905;DEVANAGARI LETTER A;Lo;0;L;;;;;N;;;;;
+0906;DEVANAGARI LETTER AA;Lo;0;L;;;;;N;;;;;
+0907;DEVANAGARI LETTER I;Lo;0;L;;;;;N;;;;;
+0908;DEVANAGARI LETTER II;Lo;0;L;;;;;N;;;;;
+0909;DEVANAGARI LETTER U;Lo;0;L;;;;;N;;;;;
+090A;DEVANAGARI LETTER UU;Lo;0;L;;;;;N;;;;;
+090B;DEVANAGARI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+090C;DEVANAGARI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+090D;DEVANAGARI LETTER CANDRA E;Lo;0;L;;;;;N;;;;;
+090E;DEVANAGARI LETTER SHORT E;Lo;0;L;;;;;N;;;;;
+090F;DEVANAGARI LETTER E;Lo;0;L;;;;;N;;;;;
+0910;DEVANAGARI LETTER AI;Lo;0;L;;;;;N;;;;;
+0911;DEVANAGARI LETTER CANDRA O;Lo;0;L;;;;;N;;;;;
+0912;DEVANAGARI LETTER SHORT O;Lo;0;L;;;;;N;;;;;
+0913;DEVANAGARI LETTER O;Lo;0;L;;;;;N;;;;;
+0914;DEVANAGARI LETTER AU;Lo;0;L;;;;;N;;;;;
+0915;DEVANAGARI LETTER KA;Lo;0;L;;;;;N;;;;;
+0916;DEVANAGARI LETTER KHA;Lo;0;L;;;;;N;;;;;
+0917;DEVANAGARI LETTER GA;Lo;0;L;;;;;N;;;;;
+0918;DEVANAGARI LETTER GHA;Lo;0;L;;;;;N;;;;;
+0919;DEVANAGARI LETTER NGA;Lo;0;L;;;;;N;;;;;
+091A;DEVANAGARI LETTER CA;Lo;0;L;;;;;N;;;;;
+091B;DEVANAGARI LETTER CHA;Lo;0;L;;;;;N;;;;;
+091C;DEVANAGARI LETTER JA;Lo;0;L;;;;;N;;;;;
+091D;DEVANAGARI LETTER JHA;Lo;0;L;;;;;N;;;;;
+091E;DEVANAGARI LETTER NYA;Lo;0;L;;;;;N;;;;;
+091F;DEVANAGARI LETTER TTA;Lo;0;L;;;;;N;;;;;
+0920;DEVANAGARI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0921;DEVANAGARI LETTER DDA;Lo;0;L;;;;;N;;;;;
+0922;DEVANAGARI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0923;DEVANAGARI LETTER NNA;Lo;0;L;;;;;N;;;;;
+0924;DEVANAGARI LETTER TA;Lo;0;L;;;;;N;;;;;
+0925;DEVANAGARI LETTER THA;Lo;0;L;;;;;N;;;;;
+0926;DEVANAGARI LETTER DA;Lo;0;L;;;;;N;;;;;
+0927;DEVANAGARI LETTER DHA;Lo;0;L;;;;;N;;;;;
+0928;DEVANAGARI LETTER NA;Lo;0;L;;;;;N;;;;;
+0929;DEVANAGARI LETTER NNNA;Lo;0;L;0928 093C;;;;N;;;;;
+092A;DEVANAGARI LETTER PA;Lo;0;L;;;;;N;;;;;
+092B;DEVANAGARI LETTER PHA;Lo;0;L;;;;;N;;;;;
+092C;DEVANAGARI LETTER BA;Lo;0;L;;;;;N;;;;;
+092D;DEVANAGARI LETTER BHA;Lo;0;L;;;;;N;;;;;
+092E;DEVANAGARI LETTER MA;Lo;0;L;;;;;N;;;;;
+092F;DEVANAGARI LETTER YA;Lo;0;L;;;;;N;;;;;
+0930;DEVANAGARI LETTER RA;Lo;0;L;;;;;N;;;;;
+0931;DEVANAGARI LETTER RRA;Lo;0;L;0930 093C;;;;N;;;;;
+0932;DEVANAGARI LETTER LA;Lo;0;L;;;;;N;;;;;
+0933;DEVANAGARI LETTER LLA;Lo;0;L;;;;;N;;;;;
+0934;DEVANAGARI LETTER LLLA;Lo;0;L;0933 093C;;;;N;;;;;
+0935;DEVANAGARI LETTER VA;Lo;0;L;;;;;N;;;;;
+0936;DEVANAGARI LETTER SHA;Lo;0;L;;;;;N;;;;;
+0937;DEVANAGARI LETTER SSA;Lo;0;L;;;;;N;;;;;
+0938;DEVANAGARI LETTER SA;Lo;0;L;;;;;N;;;;;
+0939;DEVANAGARI LETTER HA;Lo;0;L;;;;;N;;;;;
+093A;DEVANAGARI VOWEL SIGN OE;Mn;0;NSM;;;;;N;;;;;
+093B;DEVANAGARI VOWEL SIGN OOE;Mc;0;L;;;;;N;;;;;
+093C;DEVANAGARI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+093D;DEVANAGARI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+093E;DEVANAGARI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+093F;DEVANAGARI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0940;DEVANAGARI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0941;DEVANAGARI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0942;DEVANAGARI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0943;DEVANAGARI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+0944;DEVANAGARI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+0945;DEVANAGARI VOWEL SIGN CANDRA E;Mn;0;NSM;;;;;N;;;;;
+0946;DEVANAGARI VOWEL SIGN SHORT E;Mn;0;NSM;;;;;N;;;;;
+0947;DEVANAGARI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+0948;DEVANAGARI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+0949;DEVANAGARI VOWEL SIGN CANDRA O;Mc;0;L;;;;;N;;;;;
+094A;DEVANAGARI VOWEL SIGN SHORT O;Mc;0;L;;;;;N;;;;;
+094B;DEVANAGARI VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+094C;DEVANAGARI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+094D;DEVANAGARI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+094E;DEVANAGARI VOWEL SIGN PRISHTHAMATRA E;Mc;0;L;;;;;N;;;;;
+094F;DEVANAGARI VOWEL SIGN AW;Mc;0;L;;;;;N;;;;;
+0950;DEVANAGARI OM;Lo;0;L;;;;;N;;;;;
+0951;DEVANAGARI STRESS SIGN UDATTA;Mn;230;NSM;;;;;N;;;;;
+0952;DEVANAGARI STRESS SIGN ANUDATTA;Mn;220;NSM;;;;;N;;;;;
+0953;DEVANAGARI GRAVE ACCENT;Mn;230;NSM;;;;;N;;;;;
+0954;DEVANAGARI ACUTE ACCENT;Mn;230;NSM;;;;;N;;;;;
+0955;DEVANAGARI VOWEL SIGN CANDRA LONG E;Mn;0;NSM;;;;;N;;;;;
+0956;DEVANAGARI VOWEL SIGN UE;Mn;0;NSM;;;;;N;;;;;
+0957;DEVANAGARI VOWEL SIGN UUE;Mn;0;NSM;;;;;N;;;;;
+0958;DEVANAGARI LETTER QA;Lo;0;L;0915 093C;;;;N;;;;;
+0959;DEVANAGARI LETTER KHHA;Lo;0;L;0916 093C;;;;N;;;;;
+095A;DEVANAGARI LETTER GHHA;Lo;0;L;0917 093C;;;;N;;;;;
+095B;DEVANAGARI LETTER ZA;Lo;0;L;091C 093C;;;;N;;;;;
+095C;DEVANAGARI LETTER DDDHA;Lo;0;L;0921 093C;;;;N;;;;;
+095D;DEVANAGARI LETTER RHA;Lo;0;L;0922 093C;;;;N;;;;;
+095E;DEVANAGARI LETTER FA;Lo;0;L;092B 093C;;;;N;;;;;
+095F;DEVANAGARI LETTER YYA;Lo;0;L;092F 093C;;;;N;;;;;
+0960;DEVANAGARI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0961;DEVANAGARI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0962;DEVANAGARI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+0963;DEVANAGARI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+0964;DEVANAGARI DANDA;Po;0;L;;;;;N;;;;;
+0965;DEVANAGARI DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+0966;DEVANAGARI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0967;DEVANAGARI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0968;DEVANAGARI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0969;DEVANAGARI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+096A;DEVANAGARI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+096B;DEVANAGARI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+096C;DEVANAGARI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+096D;DEVANAGARI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+096E;DEVANAGARI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+096F;DEVANAGARI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0970;DEVANAGARI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
+0971;DEVANAGARI SIGN HIGH SPACING DOT;Lm;0;L;;;;;N;;;;;
+0972;DEVANAGARI LETTER CANDRA A;Lo;0;L;;;;;N;;;;;
+0973;DEVANAGARI LETTER OE;Lo;0;L;;;;;N;;;;;
+0974;DEVANAGARI LETTER OOE;Lo;0;L;;;;;N;;;;;
+0975;DEVANAGARI LETTER AW;Lo;0;L;;;;;N;;;;;
+0976;DEVANAGARI LETTER UE;Lo;0;L;;;;;N;;;;;
+0977;DEVANAGARI LETTER UUE;Lo;0;L;;;;;N;;;;;
+0978;DEVANAGARI LETTER MARWARI DDA;Lo;0;L;;;;;N;;;;;
+0979;DEVANAGARI LETTER ZHA;Lo;0;L;;;;;N;;;;;
+097A;DEVANAGARI LETTER HEAVY YA;Lo;0;L;;;;;N;;;;;
+097B;DEVANAGARI LETTER GGA;Lo;0;L;;;;;N;;;;;
+097C;DEVANAGARI LETTER JJA;Lo;0;L;;;;;N;;;;;
+097D;DEVANAGARI LETTER GLOTTAL STOP;Lo;0;L;;;;;N;;;;;
+097E;DEVANAGARI LETTER DDDA;Lo;0;L;;;;;N;;;;;
+097F;DEVANAGARI LETTER BBA;Lo;0;L;;;;;N;;;;;
+0980;BENGALI ANJI;Lo;0;L;;;;;N;;;;;
+0981;BENGALI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+0982;BENGALI SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0983;BENGALI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0985;BENGALI LETTER A;Lo;0;L;;;;;N;;;;;
+0986;BENGALI LETTER AA;Lo;0;L;;;;;N;;;;;
+0987;BENGALI LETTER I;Lo;0;L;;;;;N;;;;;
+0988;BENGALI LETTER II;Lo;0;L;;;;;N;;;;;
+0989;BENGALI LETTER U;Lo;0;L;;;;;N;;;;;
+098A;BENGALI LETTER UU;Lo;0;L;;;;;N;;;;;
+098B;BENGALI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+098C;BENGALI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+098F;BENGALI LETTER E;Lo;0;L;;;;;N;;;;;
+0990;BENGALI LETTER AI;Lo;0;L;;;;;N;;;;;
+0993;BENGALI LETTER O;Lo;0;L;;;;;N;;;;;
+0994;BENGALI LETTER AU;Lo;0;L;;;;;N;;;;;
+0995;BENGALI LETTER KA;Lo;0;L;;;;;N;;;;;
+0996;BENGALI LETTER KHA;Lo;0;L;;;;;N;;;;;
+0997;BENGALI LETTER GA;Lo;0;L;;;;;N;;;;;
+0998;BENGALI LETTER GHA;Lo;0;L;;;;;N;;;;;
+0999;BENGALI LETTER NGA;Lo;0;L;;;;;N;;;;;
+099A;BENGALI LETTER CA;Lo;0;L;;;;;N;;;;;
+099B;BENGALI LETTER CHA;Lo;0;L;;;;;N;;;;;
+099C;BENGALI LETTER JA;Lo;0;L;;;;;N;;;;;
+099D;BENGALI LETTER JHA;Lo;0;L;;;;;N;;;;;
+099E;BENGALI LETTER NYA;Lo;0;L;;;;;N;;;;;
+099F;BENGALI LETTER TTA;Lo;0;L;;;;;N;;;;;
+09A0;BENGALI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+09A1;BENGALI LETTER DDA;Lo;0;L;;;;;N;;;;;
+09A2;BENGALI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+09A3;BENGALI LETTER NNA;Lo;0;L;;;;;N;;;;;
+09A4;BENGALI LETTER TA;Lo;0;L;;;;;N;;;;;
+09A5;BENGALI LETTER THA;Lo;0;L;;;;;N;;;;;
+09A6;BENGALI LETTER DA;Lo;0;L;;;;;N;;;;;
+09A7;BENGALI LETTER DHA;Lo;0;L;;;;;N;;;;;
+09A8;BENGALI LETTER NA;Lo;0;L;;;;;N;;;;;
+09AA;BENGALI LETTER PA;Lo;0;L;;;;;N;;;;;
+09AB;BENGALI LETTER PHA;Lo;0;L;;;;;N;;;;;
+09AC;BENGALI LETTER BA;Lo;0;L;;;;;N;;;;;
+09AD;BENGALI LETTER BHA;Lo;0;L;;;;;N;;;;;
+09AE;BENGALI LETTER MA;Lo;0;L;;;;;N;;;;;
+09AF;BENGALI LETTER YA;Lo;0;L;;;;;N;;;;;
+09B0;BENGALI LETTER RA;Lo;0;L;;;;;N;;;;;
+09B2;BENGALI LETTER LA;Lo;0;L;;;;;N;;;;;
+09B6;BENGALI LETTER SHA;Lo;0;L;;;;;N;;;;;
+09B7;BENGALI LETTER SSA;Lo;0;L;;;;;N;;;;;
+09B8;BENGALI LETTER SA;Lo;0;L;;;;;N;;;;;
+09B9;BENGALI LETTER HA;Lo;0;L;;;;;N;;;;;
+09BC;BENGALI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+09BD;BENGALI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+09BE;BENGALI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+09BF;BENGALI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+09C0;BENGALI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+09C1;BENGALI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+09C2;BENGALI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+09C3;BENGALI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+09C4;BENGALI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+09C7;BENGALI VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+09C8;BENGALI VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+09CB;BENGALI VOWEL SIGN O;Mc;0;L;09C7 09BE;;;;N;;;;;
+09CC;BENGALI VOWEL SIGN AU;Mc;0;L;09C7 09D7;;;;N;;;;;
+09CD;BENGALI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+09CE;BENGALI LETTER KHANDA TA;Lo;0;L;;;;;N;;;;;
+09D7;BENGALI AU LENGTH MARK;Mc;0;L;;;;;N;;;;;
+09DC;BENGALI LETTER RRA;Lo;0;L;09A1 09BC;;;;N;;;;;
+09DD;BENGALI LETTER RHA;Lo;0;L;09A2 09BC;;;;N;;;;;
+09DF;BENGALI LETTER YYA;Lo;0;L;09AF 09BC;;;;N;;;;;
+09E0;BENGALI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+09E1;BENGALI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+09E2;BENGALI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+09E3;BENGALI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+09E6;BENGALI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+09E7;BENGALI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+09E8;BENGALI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+09E9;BENGALI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+09EA;BENGALI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+09EB;BENGALI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+09EC;BENGALI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+09ED;BENGALI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+09EE;BENGALI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+09EF;BENGALI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+09F0;BENGALI LETTER RA WITH MIDDLE DIAGONAL;Lo;0;L;;;;;N;;;;;
+09F1;BENGALI LETTER RA WITH LOWER DIAGONAL;Lo;0;L;;;;;N;BENGALI LETTER VA WITH LOWER DIAGONAL;;;;
+09F2;BENGALI RUPEE MARK;Sc;0;ET;;;;;N;;;;;
+09F3;BENGALI RUPEE SIGN;Sc;0;ET;;;;;N;;;;;
+09F4;BENGALI CURRENCY NUMERATOR ONE;No;0;L;;;;1/16;N;;;;;
+09F5;BENGALI CURRENCY NUMERATOR TWO;No;0;L;;;;1/8;N;;;;;
+09F6;BENGALI CURRENCY NUMERATOR THREE;No;0;L;;;;3/16;N;;;;;
+09F7;BENGALI CURRENCY NUMERATOR FOUR;No;0;L;;;;1/4;N;;;;;
+09F8;BENGALI CURRENCY NUMERATOR ONE LESS THAN THE DENOMINATOR;No;0;L;;;;3/4;N;;;;;
+09F9;BENGALI CURRENCY DENOMINATOR SIXTEEN;No;0;L;;;;16;N;;;;;
+09FA;BENGALI ISSHAR;So;0;L;;;;;N;;;;;
+09FB;BENGALI GANDA MARK;Sc;0;ET;;;;;N;;;;;
+09FC;BENGALI LETTER VEDIC ANUSVARA;Lo;0;L;;;;;N;;;;;
+09FD;BENGALI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
+09FE;BENGALI SANDHI MARK;Mn;230;NSM;;;;;N;;;;;
+0A01;GURMUKHI SIGN ADAK BINDI;Mn;0;NSM;;;;;N;;;;;
+0A02;GURMUKHI SIGN BINDI;Mn;0;NSM;;;;;N;;;;;
+0A03;GURMUKHI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0A05;GURMUKHI LETTER A;Lo;0;L;;;;;N;;;;;
+0A06;GURMUKHI LETTER AA;Lo;0;L;;;;;N;;;;;
+0A07;GURMUKHI LETTER I;Lo;0;L;;;;;N;;;;;
+0A08;GURMUKHI LETTER II;Lo;0;L;;;;;N;;;;;
+0A09;GURMUKHI LETTER U;Lo;0;L;;;;;N;;;;;
+0A0A;GURMUKHI LETTER UU;Lo;0;L;;;;;N;;;;;
+0A0F;GURMUKHI LETTER EE;Lo;0;L;;;;;N;;;;;
+0A10;GURMUKHI LETTER AI;Lo;0;L;;;;;N;;;;;
+0A13;GURMUKHI LETTER OO;Lo;0;L;;;;;N;;;;;
+0A14;GURMUKHI LETTER AU;Lo;0;L;;;;;N;;;;;
+0A15;GURMUKHI LETTER KA;Lo;0;L;;;;;N;;;;;
+0A16;GURMUKHI LETTER KHA;Lo;0;L;;;;;N;;;;;
+0A17;GURMUKHI LETTER GA;Lo;0;L;;;;;N;;;;;
+0A18;GURMUKHI LETTER GHA;Lo;0;L;;;;;N;;;;;
+0A19;GURMUKHI LETTER NGA;Lo;0;L;;;;;N;;;;;
+0A1A;GURMUKHI LETTER CA;Lo;0;L;;;;;N;;;;;
+0A1B;GURMUKHI LETTER CHA;Lo;0;L;;;;;N;;;;;
+0A1C;GURMUKHI LETTER JA;Lo;0;L;;;;;N;;;;;
+0A1D;GURMUKHI LETTER JHA;Lo;0;L;;;;;N;;;;;
+0A1E;GURMUKHI LETTER NYA;Lo;0;L;;;;;N;;;;;
+0A1F;GURMUKHI LETTER TTA;Lo;0;L;;;;;N;;;;;
+0A20;GURMUKHI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0A21;GURMUKHI LETTER DDA;Lo;0;L;;;;;N;;;;;
+0A22;GURMUKHI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0A23;GURMUKHI LETTER NNA;Lo;0;L;;;;;N;;;;;
+0A24;GURMUKHI LETTER TA;Lo;0;L;;;;;N;;;;;
+0A25;GURMUKHI LETTER THA;Lo;0;L;;;;;N;;;;;
+0A26;GURMUKHI LETTER DA;Lo;0;L;;;;;N;;;;;
+0A27;GURMUKHI LETTER DHA;Lo;0;L;;;;;N;;;;;
+0A28;GURMUKHI LETTER NA;Lo;0;L;;;;;N;;;;;
+0A2A;GURMUKHI LETTER PA;Lo;0;L;;;;;N;;;;;
+0A2B;GURMUKHI LETTER PHA;Lo;0;L;;;;;N;;;;;
+0A2C;GURMUKHI LETTER BA;Lo;0;L;;;;;N;;;;;
+0A2D;GURMUKHI LETTER BHA;Lo;0;L;;;;;N;;;;;
+0A2E;GURMUKHI LETTER MA;Lo;0;L;;;;;N;;;;;
+0A2F;GURMUKHI LETTER YA;Lo;0;L;;;;;N;;;;;
+0A30;GURMUKHI LETTER RA;Lo;0;L;;;;;N;;;;;
+0A32;GURMUKHI LETTER LA;Lo;0;L;;;;;N;;;;;
+0A33;GURMUKHI LETTER LLA;Lo;0;L;0A32 0A3C;;;;N;;;;;
+0A35;GURMUKHI LETTER VA;Lo;0;L;;;;;N;;;;;
+0A36;GURMUKHI LETTER SHA;Lo;0;L;0A38 0A3C;;;;N;;;;;
+0A38;GURMUKHI LETTER SA;Lo;0;L;;;;;N;;;;;
+0A39;GURMUKHI LETTER HA;Lo;0;L;;;;;N;;;;;
+0A3C;GURMUKHI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+0A3E;GURMUKHI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0A3F;GURMUKHI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0A40;GURMUKHI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0A41;GURMUKHI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0A42;GURMUKHI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0A47;GURMUKHI VOWEL SIGN EE;Mn;0;NSM;;;;;N;;;;;
+0A48;GURMUKHI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+0A4B;GURMUKHI VOWEL SIGN OO;Mn;0;NSM;;;;;N;;;;;
+0A4C;GURMUKHI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+0A4D;GURMUKHI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0A51;GURMUKHI SIGN UDAAT;Mn;0;NSM;;;;;N;;;;;
+0A59;GURMUKHI LETTER KHHA;Lo;0;L;0A16 0A3C;;;;N;;;;;
+0A5A;GURMUKHI LETTER GHHA;Lo;0;L;0A17 0A3C;;;;N;;;;;
+0A5B;GURMUKHI LETTER ZA;Lo;0;L;0A1C 0A3C;;;;N;;;;;
+0A5C;GURMUKHI LETTER RRA;Lo;0;L;;;;;N;;;;;
+0A5E;GURMUKHI LETTER FA;Lo;0;L;0A2B 0A3C;;;;N;;;;;
+0A66;GURMUKHI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0A67;GURMUKHI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0A68;GURMUKHI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0A69;GURMUKHI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0A6A;GURMUKHI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0A6B;GURMUKHI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0A6C;GURMUKHI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0A6D;GURMUKHI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0A6E;GURMUKHI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0A6F;GURMUKHI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0A70;GURMUKHI TIPPI;Mn;0;NSM;;;;;N;;;;;
+0A71;GURMUKHI ADDAK;Mn;0;NSM;;;;;N;;;;;
+0A72;GURMUKHI IRI;Lo;0;L;;;;;N;;;;;
+0A73;GURMUKHI URA;Lo;0;L;;;;;N;;;;;
+0A74;GURMUKHI EK ONKAR;Lo;0;L;;;;;N;;;;;
+0A75;GURMUKHI SIGN YAKASH;Mn;0;NSM;;;;;N;;;;;
+0A76;GURMUKHI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
+0A81;GUJARATI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+0A82;GUJARATI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+0A83;GUJARATI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0A85;GUJARATI LETTER A;Lo;0;L;;;;;N;;;;;
+0A86;GUJARATI LETTER AA;Lo;0;L;;;;;N;;;;;
+0A87;GUJARATI LETTER I;Lo;0;L;;;;;N;;;;;
+0A88;GUJARATI LETTER II;Lo;0;L;;;;;N;;;;;
+0A89;GUJARATI LETTER U;Lo;0;L;;;;;N;;;;;
+0A8A;GUJARATI LETTER UU;Lo;0;L;;;;;N;;;;;
+0A8B;GUJARATI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0A8C;GUJARATI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+0A8D;GUJARATI VOWEL CANDRA E;Lo;0;L;;;;;N;;;;;
+0A8F;GUJARATI LETTER E;Lo;0;L;;;;;N;;;;;
+0A90;GUJARATI LETTER AI;Lo;0;L;;;;;N;;;;;
+0A91;GUJARATI VOWEL CANDRA O;Lo;0;L;;;;;N;;;;;
+0A93;GUJARATI LETTER O;Lo;0;L;;;;;N;;;;;
+0A94;GUJARATI LETTER AU;Lo;0;L;;;;;N;;;;;
+0A95;GUJARATI LETTER KA;Lo;0;L;;;;;N;;;;;
+0A96;GUJARATI LETTER KHA;Lo;0;L;;;;;N;;;;;
+0A97;GUJARATI LETTER GA;Lo;0;L;;;;;N;;;;;
+0A98;GUJARATI LETTER GHA;Lo;0;L;;;;;N;;;;;
+0A99;GUJARATI LETTER NGA;Lo;0;L;;;;;N;;;;;
+0A9A;GUJARATI LETTER CA;Lo;0;L;;;;;N;;;;;
+0A9B;GUJARATI LETTER CHA;Lo;0;L;;;;;N;;;;;
+0A9C;GUJARATI LETTER JA;Lo;0;L;;;;;N;;;;;
+0A9D;GUJARATI LETTER JHA;Lo;0;L;;;;;N;;;;;
+0A9E;GUJARATI LETTER NYA;Lo;0;L;;;;;N;;;;;
+0A9F;GUJARATI LETTER TTA;Lo;0;L;;;;;N;;;;;
+0AA0;GUJARATI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0AA1;GUJARATI LETTER DDA;Lo;0;L;;;;;N;;;;;
+0AA2;GUJARATI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0AA3;GUJARATI LETTER NNA;Lo;0;L;;;;;N;;;;;
+0AA4;GUJARATI LETTER TA;Lo;0;L;;;;;N;;;;;
+0AA5;GUJARATI LETTER THA;Lo;0;L;;;;;N;;;;;
+0AA6;GUJARATI LETTER DA;Lo;0;L;;;;;N;;;;;
+0AA7;GUJARATI LETTER DHA;Lo;0;L;;;;;N;;;;;
+0AA8;GUJARATI LETTER NA;Lo;0;L;;;;;N;;;;;
+0AAA;GUJARATI LETTER PA;Lo;0;L;;;;;N;;;;;
+0AAB;GUJARATI LETTER PHA;Lo;0;L;;;;;N;;;;;
+0AAC;GUJARATI LETTER BA;Lo;0;L;;;;;N;;;;;
+0AAD;GUJARATI LETTER BHA;Lo;0;L;;;;;N;;;;;
+0AAE;GUJARATI LETTER MA;Lo;0;L;;;;;N;;;;;
+0AAF;GUJARATI LETTER YA;Lo;0;L;;;;;N;;;;;
+0AB0;GUJARATI LETTER RA;Lo;0;L;;;;;N;;;;;
+0AB2;GUJARATI LETTER LA;Lo;0;L;;;;;N;;;;;
+0AB3;GUJARATI LETTER LLA;Lo;0;L;;;;;N;;;;;
+0AB5;GUJARATI LETTER VA;Lo;0;L;;;;;N;;;;;
+0AB6;GUJARATI LETTER SHA;Lo;0;L;;;;;N;;;;;
+0AB7;GUJARATI LETTER SSA;Lo;0;L;;;;;N;;;;;
+0AB8;GUJARATI LETTER SA;Lo;0;L;;;;;N;;;;;
+0AB9;GUJARATI LETTER HA;Lo;0;L;;;;;N;;;;;
+0ABC;GUJARATI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+0ABD;GUJARATI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+0ABE;GUJARATI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0ABF;GUJARATI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0AC0;GUJARATI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0AC1;GUJARATI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0AC2;GUJARATI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0AC3;GUJARATI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+0AC4;GUJARATI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+0AC5;GUJARATI VOWEL SIGN CANDRA E;Mn;0;NSM;;;;;N;;;;;
+0AC7;GUJARATI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+0AC8;GUJARATI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+0AC9;GUJARATI VOWEL SIGN CANDRA O;Mc;0;L;;;;;N;;;;;
+0ACB;GUJARATI VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+0ACC;GUJARATI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+0ACD;GUJARATI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0AD0;GUJARATI OM;Lo;0;L;;;;;N;;;;;
+0AE0;GUJARATI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0AE1;GUJARATI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0AE2;GUJARATI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+0AE3;GUJARATI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+0AE6;GUJARATI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0AE7;GUJARATI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0AE8;GUJARATI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0AE9;GUJARATI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0AEA;GUJARATI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0AEB;GUJARATI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0AEC;GUJARATI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0AED;GUJARATI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0AEE;GUJARATI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0AEF;GUJARATI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0AF0;GUJARATI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
+0AF1;GUJARATI RUPEE SIGN;Sc;0;ET;;;;;N;;;;;
+0AF9;GUJARATI LETTER ZHA;Lo;0;L;;;;;N;;;;;
+0AFA;GUJARATI SIGN SUKUN;Mn;0;NSM;;;;;N;;;;;
+0AFB;GUJARATI SIGN SHADDA;Mn;0;NSM;;;;;N;;;;;
+0AFC;GUJARATI SIGN MADDAH;Mn;0;NSM;;;;;N;;;;;
+0AFD;GUJARATI SIGN THREE-DOT NUKTA ABOVE;Mn;0;NSM;;;;;N;;;;;
+0AFE;GUJARATI SIGN CIRCLE NUKTA ABOVE;Mn;0;NSM;;;;;N;;;;;
+0AFF;GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE;Mn;0;NSM;;;;;N;;;;;
+0B01;ORIYA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+0B02;ORIYA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0B03;ORIYA SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0B05;ORIYA LETTER A;Lo;0;L;;;;;N;;;;;
+0B06;ORIYA LETTER AA;Lo;0;L;;;;;N;;;;;
+0B07;ORIYA LETTER I;Lo;0;L;;;;;N;;;;;
+0B08;ORIYA LETTER II;Lo;0;L;;;;;N;;;;;
+0B09;ORIYA LETTER U;Lo;0;L;;;;;N;;;;;
+0B0A;ORIYA LETTER UU;Lo;0;L;;;;;N;;;;;
+0B0B;ORIYA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0B0C;ORIYA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+0B0F;ORIYA LETTER E;Lo;0;L;;;;;N;;;;;
+0B10;ORIYA LETTER AI;Lo;0;L;;;;;N;;;;;
+0B13;ORIYA LETTER O;Lo;0;L;;;;;N;;;;;
+0B14;ORIYA LETTER AU;Lo;0;L;;;;;N;;;;;
+0B15;ORIYA LETTER KA;Lo;0;L;;;;;N;;;;;
+0B16;ORIYA LETTER KHA;Lo;0;L;;;;;N;;;;;
+0B17;ORIYA LETTER GA;Lo;0;L;;;;;N;;;;;
+0B18;ORIYA LETTER GHA;Lo;0;L;;;;;N;;;;;
+0B19;ORIYA LETTER NGA;Lo;0;L;;;;;N;;;;;
+0B1A;ORIYA LETTER CA;Lo;0;L;;;;;N;;;;;
+0B1B;ORIYA LETTER CHA;Lo;0;L;;;;;N;;;;;
+0B1C;ORIYA LETTER JA;Lo;0;L;;;;;N;;;;;
+0B1D;ORIYA LETTER JHA;Lo;0;L;;;;;N;;;;;
+0B1E;ORIYA LETTER NYA;Lo;0;L;;;;;N;;;;;
+0B1F;ORIYA LETTER TTA;Lo;0;L;;;;;N;;;;;
+0B20;ORIYA LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0B21;ORIYA LETTER DDA;Lo;0;L;;;;;N;;;;;
+0B22;ORIYA LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0B23;ORIYA LETTER NNA;Lo;0;L;;;;;N;;;;;
+0B24;ORIYA LETTER TA;Lo;0;L;;;;;N;;;;;
+0B25;ORIYA LETTER THA;Lo;0;L;;;;;N;;;;;
+0B26;ORIYA LETTER DA;Lo;0;L;;;;;N;;;;;
+0B27;ORIYA LETTER DHA;Lo;0;L;;;;;N;;;;;
+0B28;ORIYA LETTER NA;Lo;0;L;;;;;N;;;;;
+0B2A;ORIYA LETTER PA;Lo;0;L;;;;;N;;;;;
+0B2B;ORIYA LETTER PHA;Lo;0;L;;;;;N;;;;;
+0B2C;ORIYA LETTER BA;Lo;0;L;;;;;N;;;;;
+0B2D;ORIYA LETTER BHA;Lo;0;L;;;;;N;;;;;
+0B2E;ORIYA LETTER MA;Lo;0;L;;;;;N;;;;;
+0B2F;ORIYA LETTER YA;Lo;0;L;;;;;N;;;;;
+0B30;ORIYA LETTER RA;Lo;0;L;;;;;N;;;;;
+0B32;ORIYA LETTER LA;Lo;0;L;;;;;N;;;;;
+0B33;ORIYA LETTER LLA;Lo;0;L;;;;;N;;;;;
+0B35;ORIYA LETTER VA;Lo;0;L;;;;;N;;;;;
+0B36;ORIYA LETTER SHA;Lo;0;L;;;;;N;;;;;
+0B37;ORIYA LETTER SSA;Lo;0;L;;;;;N;;;;;
+0B38;ORIYA LETTER SA;Lo;0;L;;;;;N;;;;;
+0B39;ORIYA LETTER HA;Lo;0;L;;;;;N;;;;;
+0B3C;ORIYA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+0B3D;ORIYA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+0B3E;ORIYA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0B3F;ORIYA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+0B40;ORIYA VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0B41;ORIYA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0B42;ORIYA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0B43;ORIYA VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+0B44;ORIYA VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+0B47;ORIYA VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+0B48;ORIYA VOWEL SIGN AI;Mc;0;L;0B47 0B56;;;;N;;;;;
+0B4B;ORIYA VOWEL SIGN O;Mc;0;L;0B47 0B3E;;;;N;;;;;
+0B4C;ORIYA VOWEL SIGN AU;Mc;0;L;0B47 0B57;;;;N;;;;;
+0B4D;ORIYA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0B56;ORIYA AI LENGTH MARK;Mn;0;NSM;;;;;N;;;;;
+0B57;ORIYA AU LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0B5C;ORIYA LETTER RRA;Lo;0;L;0B21 0B3C;;;;N;;;;;
+0B5D;ORIYA LETTER RHA;Lo;0;L;0B22 0B3C;;;;N;;;;;
+0B5F;ORIYA LETTER YYA;Lo;0;L;;;;;N;;;;;
+0B60;ORIYA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0B61;ORIYA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0B62;ORIYA VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+0B63;ORIYA VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+0B66;ORIYA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0B67;ORIYA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0B68;ORIYA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0B69;ORIYA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0B6A;ORIYA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0B6B;ORIYA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0B6C;ORIYA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0B6D;ORIYA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0B6E;ORIYA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0B6F;ORIYA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0B70;ORIYA ISSHAR;So;0;L;;;;;N;;;;;
+0B71;ORIYA LETTER WA;Lo;0;L;;;;;N;;;;;
+0B72;ORIYA FRACTION ONE QUARTER;No;0;L;;;;1/4;N;;;;;
+0B73;ORIYA FRACTION ONE HALF;No;0;L;;;;1/2;N;;;;;
+0B74;ORIYA FRACTION THREE QUARTERS;No;0;L;;;;3/4;N;;;;;
+0B75;ORIYA FRACTION ONE SIXTEENTH;No;0;L;;;;1/16;N;;;;;
+0B76;ORIYA FRACTION ONE EIGHTH;No;0;L;;;;1/8;N;;;;;
+0B77;ORIYA FRACTION THREE SIXTEENTHS;No;0;L;;;;3/16;N;;;;;
+0B82;TAMIL SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+0B83;TAMIL SIGN VISARGA;Lo;0;L;;;;;N;;;;;
+0B85;TAMIL LETTER A;Lo;0;L;;;;;N;;;;;
+0B86;TAMIL LETTER AA;Lo;0;L;;;;;N;;;;;
+0B87;TAMIL LETTER I;Lo;0;L;;;;;N;;;;;
+0B88;TAMIL LETTER II;Lo;0;L;;;;;N;;;;;
+0B89;TAMIL LETTER U;Lo;0;L;;;;;N;;;;;
+0B8A;TAMIL LETTER UU;Lo;0;L;;;;;N;;;;;
+0B8E;TAMIL LETTER E;Lo;0;L;;;;;N;;;;;
+0B8F;TAMIL LETTER EE;Lo;0;L;;;;;N;;;;;
+0B90;TAMIL LETTER AI;Lo;0;L;;;;;N;;;;;
+0B92;TAMIL LETTER O;Lo;0;L;;;;;N;;;;;
+0B93;TAMIL LETTER OO;Lo;0;L;;;;;N;;;;;
+0B94;TAMIL LETTER AU;Lo;0;L;0B92 0BD7;;;;N;;;;;
+0B95;TAMIL LETTER KA;Lo;0;L;;;;;N;;;;;
+0B99;TAMIL LETTER NGA;Lo;0;L;;;;;N;;;;;
+0B9A;TAMIL LETTER CA;Lo;0;L;;;;;N;;;;;
+0B9C;TAMIL LETTER JA;Lo;0;L;;;;;N;;;;;
+0B9E;TAMIL LETTER NYA;Lo;0;L;;;;;N;;;;;
+0B9F;TAMIL LETTER TTA;Lo;0;L;;;;;N;;;;;
+0BA3;TAMIL LETTER NNA;Lo;0;L;;;;;N;;;;;
+0BA4;TAMIL LETTER TA;Lo;0;L;;;;;N;;;;;
+0BA8;TAMIL LETTER NA;Lo;0;L;;;;;N;;;;;
+0BA9;TAMIL LETTER NNNA;Lo;0;L;;;;;N;;;;;
+0BAA;TAMIL LETTER PA;Lo;0;L;;;;;N;;;;;
+0BAE;TAMIL LETTER MA;Lo;0;L;;;;;N;;;;;
+0BAF;TAMIL LETTER YA;Lo;0;L;;;;;N;;;;;
+0BB0;TAMIL LETTER RA;Lo;0;L;;;;;N;;;;;
+0BB1;TAMIL LETTER RRA;Lo;0;L;;;;;N;;;;;
+0BB2;TAMIL LETTER LA;Lo;0;L;;;;;N;;;;;
+0BB3;TAMIL LETTER LLA;Lo;0;L;;;;;N;;;;;
+0BB4;TAMIL LETTER LLLA;Lo;0;L;;;;;N;;;;;
+0BB5;TAMIL LETTER VA;Lo;0;L;;;;;N;;;;;
+0BB6;TAMIL LETTER SHA;Lo;0;L;;;;;N;;;;;
+0BB7;TAMIL LETTER SSA;Lo;0;L;;;;;N;;;;;
+0BB8;TAMIL LETTER SA;Lo;0;L;;;;;N;;;;;
+0BB9;TAMIL LETTER HA;Lo;0;L;;;;;N;;;;;
+0BBE;TAMIL VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0BBF;TAMIL VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0BC0;TAMIL VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+0BC1;TAMIL VOWEL SIGN U;Mc;0;L;;;;;N;;;;;
+0BC2;TAMIL VOWEL SIGN UU;Mc;0;L;;;;;N;;;;;
+0BC6;TAMIL VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+0BC7;TAMIL VOWEL SIGN EE;Mc;0;L;;;;;N;;;;;
+0BC8;TAMIL VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+0BCA;TAMIL VOWEL SIGN O;Mc;0;L;0BC6 0BBE;;;;N;;;;;
+0BCB;TAMIL VOWEL SIGN OO;Mc;0;L;0BC7 0BBE;;;;N;;;;;
+0BCC;TAMIL VOWEL SIGN AU;Mc;0;L;0BC6 0BD7;;;;N;;;;;
+0BCD;TAMIL SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0BD0;TAMIL OM;Lo;0;L;;;;;N;;;;;
+0BD7;TAMIL AU LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0BE6;TAMIL DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0BE7;TAMIL DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0BE8;TAMIL DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0BE9;TAMIL DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0BEA;TAMIL DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0BEB;TAMIL DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0BEC;TAMIL DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0BED;TAMIL DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0BEE;TAMIL DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0BEF;TAMIL DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0BF0;TAMIL NUMBER TEN;No;0;L;;;;10;N;;;;;
+0BF1;TAMIL NUMBER ONE HUNDRED;No;0;L;;;;100;N;;;;;
+0BF2;TAMIL NUMBER ONE THOUSAND;No;0;L;;;;1000;N;;;;;
+0BF3;TAMIL DAY SIGN;So;0;ON;;;;;N;;;;;
+0BF4;TAMIL MONTH SIGN;So;0;ON;;;;;N;;;;;
+0BF5;TAMIL YEAR SIGN;So;0;ON;;;;;N;;;;;
+0BF6;TAMIL DEBIT SIGN;So;0;ON;;;;;N;;;;;
+0BF7;TAMIL CREDIT SIGN;So;0;ON;;;;;N;;;;;
+0BF8;TAMIL AS ABOVE SIGN;So;0;ON;;;;;N;;;;;
+0BF9;TAMIL RUPEE SIGN;Sc;0;ET;;;;;N;;;;;
+0BFA;TAMIL NUMBER SIGN;So;0;ON;;;;;N;;;;;
+0C00;TELUGU SIGN COMBINING CANDRABINDU ABOVE;Mn;0;NSM;;;;;N;;;;;
+0C01;TELUGU SIGN CANDRABINDU;Mc;0;L;;;;;N;;;;;
+0C02;TELUGU SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0C03;TELUGU SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0C04;TELUGU SIGN COMBINING ANUSVARA ABOVE;Mn;0;NSM;;;;;N;;;;;
+0C05;TELUGU LETTER A;Lo;0;L;;;;;N;;;;;
+0C06;TELUGU LETTER AA;Lo;0;L;;;;;N;;;;;
+0C07;TELUGU LETTER I;Lo;0;L;;;;;N;;;;;
+0C08;TELUGU LETTER II;Lo;0;L;;;;;N;;;;;
+0C09;TELUGU LETTER U;Lo;0;L;;;;;N;;;;;
+0C0A;TELUGU LETTER UU;Lo;0;L;;;;;N;;;;;
+0C0B;TELUGU LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0C0C;TELUGU LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+0C0E;TELUGU LETTER E;Lo;0;L;;;;;N;;;;;
+0C0F;TELUGU LETTER EE;Lo;0;L;;;;;N;;;;;
+0C10;TELUGU LETTER AI;Lo;0;L;;;;;N;;;;;
+0C12;TELUGU LETTER O;Lo;0;L;;;;;N;;;;;
+0C13;TELUGU LETTER OO;Lo;0;L;;;;;N;;;;;
+0C14;TELUGU LETTER AU;Lo;0;L;;;;;N;;;;;
+0C15;TELUGU LETTER KA;Lo;0;L;;;;;N;;;;;
+0C16;TELUGU LETTER KHA;Lo;0;L;;;;;N;;;;;
+0C17;TELUGU LETTER GA;Lo;0;L;;;;;N;;;;;
+0C18;TELUGU LETTER GHA;Lo;0;L;;;;;N;;;;;
+0C19;TELUGU LETTER NGA;Lo;0;L;;;;;N;;;;;
+0C1A;TELUGU LETTER CA;Lo;0;L;;;;;N;;;;;
+0C1B;TELUGU LETTER CHA;Lo;0;L;;;;;N;;;;;
+0C1C;TELUGU LETTER JA;Lo;0;L;;;;;N;;;;;
+0C1D;TELUGU LETTER JHA;Lo;0;L;;;;;N;;;;;
+0C1E;TELUGU LETTER NYA;Lo;0;L;;;;;N;;;;;
+0C1F;TELUGU LETTER TTA;Lo;0;L;;;;;N;;;;;
+0C20;TELUGU LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0C21;TELUGU LETTER DDA;Lo;0;L;;;;;N;;;;;
+0C22;TELUGU LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0C23;TELUGU LETTER NNA;Lo;0;L;;;;;N;;;;;
+0C24;TELUGU LETTER TA;Lo;0;L;;;;;N;;;;;
+0C25;TELUGU LETTER THA;Lo;0;L;;;;;N;;;;;
+0C26;TELUGU LETTER DA;Lo;0;L;;;;;N;;;;;
+0C27;TELUGU LETTER DHA;Lo;0;L;;;;;N;;;;;
+0C28;TELUGU LETTER NA;Lo;0;L;;;;;N;;;;;
+0C2A;TELUGU LETTER PA;Lo;0;L;;;;;N;;;;;
+0C2B;TELUGU LETTER PHA;Lo;0;L;;;;;N;;;;;
+0C2C;TELUGU LETTER BA;Lo;0;L;;;;;N;;;;;
+0C2D;TELUGU LETTER BHA;Lo;0;L;;;;;N;;;;;
+0C2E;TELUGU LETTER MA;Lo;0;L;;;;;N;;;;;
+0C2F;TELUGU LETTER YA;Lo;0;L;;;;;N;;;;;
+0C30;TELUGU LETTER RA;Lo;0;L;;;;;N;;;;;
+0C31;TELUGU LETTER RRA;Lo;0;L;;;;;N;;;;;
+0C32;TELUGU LETTER LA;Lo;0;L;;;;;N;;;;;
+0C33;TELUGU LETTER LLA;Lo;0;L;;;;;N;;;;;
+0C34;TELUGU LETTER LLLA;Lo;0;L;;;;;N;;;;;
+0C35;TELUGU LETTER VA;Lo;0;L;;;;;N;;;;;
+0C36;TELUGU LETTER SHA;Lo;0;L;;;;;N;;;;;
+0C37;TELUGU LETTER SSA;Lo;0;L;;;;;N;;;;;
+0C38;TELUGU LETTER SA;Lo;0;L;;;;;N;;;;;
+0C39;TELUGU LETTER HA;Lo;0;L;;;;;N;;;;;
+0C3D;TELUGU SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+0C3E;TELUGU VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;;
+0C3F;TELUGU VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+0C40;TELUGU VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+0C41;TELUGU VOWEL SIGN U;Mc;0;L;;;;;N;;;;;
+0C42;TELUGU VOWEL SIGN UU;Mc;0;L;;;;;N;;;;;
+0C43;TELUGU VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;;
+0C44;TELUGU VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;;
+0C46;TELUGU VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+0C47;TELUGU VOWEL SIGN EE;Mn;0;NSM;;;;;N;;;;;
+0C48;TELUGU VOWEL SIGN AI;Mn;0;NSM;0C46 0C56;;;;N;;;;;
+0C4A;TELUGU VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+0C4B;TELUGU VOWEL SIGN OO;Mn;0;NSM;;;;;N;;;;;
+0C4C;TELUGU VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+0C4D;TELUGU SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0C55;TELUGU LENGTH MARK;Mn;84;NSM;;;;;N;;;;;
+0C56;TELUGU AI LENGTH MARK;Mn;91;NSM;;;;;N;;;;;
+0C58;TELUGU LETTER TSA;Lo;0;L;;;;;N;;;;;
+0C59;TELUGU LETTER DZA;Lo;0;L;;;;;N;;;;;
+0C5A;TELUGU LETTER RRRA;Lo;0;L;;;;;N;;;;;
+0C60;TELUGU LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0C61;TELUGU LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0C62;TELUGU VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+0C63;TELUGU VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+0C66;TELUGU DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0C67;TELUGU DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0C68;TELUGU DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0C69;TELUGU DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0C6A;TELUGU DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0C6B;TELUGU DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0C6C;TELUGU DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0C6D;TELUGU DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0C6E;TELUGU DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0C6F;TELUGU DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0C77;TELUGU SIGN SIDDHAM;Po;0;L;;;;;N;;;;;
+0C78;TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF FOUR;No;0;ON;;;;0;N;;;;;
+0C79;TELUGU FRACTION DIGIT ONE FOR ODD POWERS OF FOUR;No;0;ON;;;;1;N;;;;;
+0C7A;TELUGU FRACTION DIGIT TWO FOR ODD POWERS OF FOUR;No;0;ON;;;;2;N;;;;;
+0C7B;TELUGU FRACTION DIGIT THREE FOR ODD POWERS OF FOUR;No;0;ON;;;;3;N;;;;;
+0C7C;TELUGU FRACTION DIGIT ONE FOR EVEN POWERS OF FOUR;No;0;ON;;;;1;N;;;;;
+0C7D;TELUGU FRACTION DIGIT TWO FOR EVEN POWERS OF FOUR;No;0;ON;;;;2;N;;;;;
+0C7E;TELUGU FRACTION DIGIT THREE FOR EVEN POWERS OF FOUR;No;0;ON;;;;3;N;;;;;
+0C7F;TELUGU SIGN TUUMU;So;0;L;;;;;N;;;;;
+0C80;KANNADA SIGN SPACING CANDRABINDU;Lo;0;L;;;;;N;;;;;
+0C81;KANNADA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+0C82;KANNADA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0C83;KANNADA SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0C84;KANNADA SIGN SIDDHAM;Po;0;L;;;;;N;;;;;
+0C85;KANNADA LETTER A;Lo;0;L;;;;;N;;;;;
+0C86;KANNADA LETTER AA;Lo;0;L;;;;;N;;;;;
+0C87;KANNADA LETTER I;Lo;0;L;;;;;N;;;;;
+0C88;KANNADA LETTER II;Lo;0;L;;;;;N;;;;;
+0C89;KANNADA LETTER U;Lo;0;L;;;;;N;;;;;
+0C8A;KANNADA LETTER UU;Lo;0;L;;;;;N;;;;;
+0C8B;KANNADA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0C8C;KANNADA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+0C8E;KANNADA LETTER E;Lo;0;L;;;;;N;;;;;
+0C8F;KANNADA LETTER EE;Lo;0;L;;;;;N;;;;;
+0C90;KANNADA LETTER AI;Lo;0;L;;;;;N;;;;;
+0C92;KANNADA LETTER O;Lo;0;L;;;;;N;;;;;
+0C93;KANNADA LETTER OO;Lo;0;L;;;;;N;;;;;
+0C94;KANNADA LETTER AU;Lo;0;L;;;;;N;;;;;
+0C95;KANNADA LETTER KA;Lo;0;L;;;;;N;;;;;
+0C96;KANNADA LETTER KHA;Lo;0;L;;;;;N;;;;;
+0C97;KANNADA LETTER GA;Lo;0;L;;;;;N;;;;;
+0C98;KANNADA LETTER GHA;Lo;0;L;;;;;N;;;;;
+0C99;KANNADA LETTER NGA;Lo;0;L;;;;;N;;;;;
+0C9A;KANNADA LETTER CA;Lo;0;L;;;;;N;;;;;
+0C9B;KANNADA LETTER CHA;Lo;0;L;;;;;N;;;;;
+0C9C;KANNADA LETTER JA;Lo;0;L;;;;;N;;;;;
+0C9D;KANNADA LETTER JHA;Lo;0;L;;;;;N;;;;;
+0C9E;KANNADA LETTER NYA;Lo;0;L;;;;;N;;;;;
+0C9F;KANNADA LETTER TTA;Lo;0;L;;;;;N;;;;;
+0CA0;KANNADA LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0CA1;KANNADA LETTER DDA;Lo;0;L;;;;;N;;;;;
+0CA2;KANNADA LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0CA3;KANNADA LETTER NNA;Lo;0;L;;;;;N;;;;;
+0CA4;KANNADA LETTER TA;Lo;0;L;;;;;N;;;;;
+0CA5;KANNADA LETTER THA;Lo;0;L;;;;;N;;;;;
+0CA6;KANNADA LETTER DA;Lo;0;L;;;;;N;;;;;
+0CA7;KANNADA LETTER DHA;Lo;0;L;;;;;N;;;;;
+0CA8;KANNADA LETTER NA;Lo;0;L;;;;;N;;;;;
+0CAA;KANNADA LETTER PA;Lo;0;L;;;;;N;;;;;
+0CAB;KANNADA LETTER PHA;Lo;0;L;;;;;N;;;;;
+0CAC;KANNADA LETTER BA;Lo;0;L;;;;;N;;;;;
+0CAD;KANNADA LETTER BHA;Lo;0;L;;;;;N;;;;;
+0CAE;KANNADA LETTER MA;Lo;0;L;;;;;N;;;;;
+0CAF;KANNADA LETTER YA;Lo;0;L;;;;;N;;;;;
+0CB0;KANNADA LETTER RA;Lo;0;L;;;;;N;;;;;
+0CB1;KANNADA LETTER RRA;Lo;0;L;;;;;N;;;;;
+0CB2;KANNADA LETTER LA;Lo;0;L;;;;;N;;;;;
+0CB3;KANNADA LETTER LLA;Lo;0;L;;;;;N;;;;;
+0CB5;KANNADA LETTER VA;Lo;0;L;;;;;N;;;;;
+0CB6;KANNADA LETTER SHA;Lo;0;L;;;;;N;;;;;
+0CB7;KANNADA LETTER SSA;Lo;0;L;;;;;N;;;;;
+0CB8;KANNADA LETTER SA;Lo;0;L;;;;;N;;;;;
+0CB9;KANNADA LETTER HA;Lo;0;L;;;;;N;;;;;
+0CBC;KANNADA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+0CBD;KANNADA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+0CBE;KANNADA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0CBF;KANNADA VOWEL SIGN I;Mn;0;L;;;;;N;;;;;
+0CC0;KANNADA VOWEL SIGN II;Mc;0;L;0CBF 0CD5;;;;N;;;;;
+0CC1;KANNADA VOWEL SIGN U;Mc;0;L;;;;;N;;;;;
+0CC2;KANNADA VOWEL SIGN UU;Mc;0;L;;;;;N;;;;;
+0CC3;KANNADA VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;;
+0CC4;KANNADA VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;;
+0CC6;KANNADA VOWEL SIGN E;Mn;0;L;;;;;N;;;;;
+0CC7;KANNADA VOWEL SIGN EE;Mc;0;L;0CC6 0CD5;;;;N;;;;;
+0CC8;KANNADA VOWEL SIGN AI;Mc;0;L;0CC6 0CD6;;;;N;;;;;
+0CCA;KANNADA VOWEL SIGN O;Mc;0;L;0CC6 0CC2;;;;N;;;;;
+0CCB;KANNADA VOWEL SIGN OO;Mc;0;L;0CCA 0CD5;;;;N;;;;;
+0CCC;KANNADA VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+0CCD;KANNADA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0CD5;KANNADA LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0CD6;KANNADA AI LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0CDE;KANNADA LETTER FA;Lo;0;L;;;;;N;;;;;
+0CE0;KANNADA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0CE1;KANNADA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0CE2;KANNADA VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+0CE3;KANNADA VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+0CE6;KANNADA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0CE7;KANNADA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0CE8;KANNADA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0CE9;KANNADA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0CEA;KANNADA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0CEB;KANNADA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0CEC;KANNADA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0CED;KANNADA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0CEE;KANNADA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0CEF;KANNADA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0CF1;KANNADA SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;;
+0CF2;KANNADA SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;;
+0D00;MALAYALAM SIGN COMBINING ANUSVARA ABOVE;Mn;0;NSM;;;;;N;;;;;
+0D01;MALAYALAM SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+0D02;MALAYALAM SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0D03;MALAYALAM SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0D05;MALAYALAM LETTER A;Lo;0;L;;;;;N;;;;;
+0D06;MALAYALAM LETTER AA;Lo;0;L;;;;;N;;;;;
+0D07;MALAYALAM LETTER I;Lo;0;L;;;;;N;;;;;
+0D08;MALAYALAM LETTER II;Lo;0;L;;;;;N;;;;;
+0D09;MALAYALAM LETTER U;Lo;0;L;;;;;N;;;;;
+0D0A;MALAYALAM LETTER UU;Lo;0;L;;;;;N;;;;;
+0D0B;MALAYALAM LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0D0C;MALAYALAM LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+0D0E;MALAYALAM LETTER E;Lo;0;L;;;;;N;;;;;
+0D0F;MALAYALAM LETTER EE;Lo;0;L;;;;;N;;;;;
+0D10;MALAYALAM LETTER AI;Lo;0;L;;;;;N;;;;;
+0D12;MALAYALAM LETTER O;Lo;0;L;;;;;N;;;;;
+0D13;MALAYALAM LETTER OO;Lo;0;L;;;;;N;;;;;
+0D14;MALAYALAM LETTER AU;Lo;0;L;;;;;N;;;;;
+0D15;MALAYALAM LETTER KA;Lo;0;L;;;;;N;;;;;
+0D16;MALAYALAM LETTER KHA;Lo;0;L;;;;;N;;;;;
+0D17;MALAYALAM LETTER GA;Lo;0;L;;;;;N;;;;;
+0D18;MALAYALAM LETTER GHA;Lo;0;L;;;;;N;;;;;
+0D19;MALAYALAM LETTER NGA;Lo;0;L;;;;;N;;;;;
+0D1A;MALAYALAM LETTER CA;Lo;0;L;;;;;N;;;;;
+0D1B;MALAYALAM LETTER CHA;Lo;0;L;;;;;N;;;;;
+0D1C;MALAYALAM LETTER JA;Lo;0;L;;;;;N;;;;;
+0D1D;MALAYALAM LETTER JHA;Lo;0;L;;;;;N;;;;;
+0D1E;MALAYALAM LETTER NYA;Lo;0;L;;;;;N;;;;;
+0D1F;MALAYALAM LETTER TTA;Lo;0;L;;;;;N;;;;;
+0D20;MALAYALAM LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0D21;MALAYALAM LETTER DDA;Lo;0;L;;;;;N;;;;;
+0D22;MALAYALAM LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0D23;MALAYALAM LETTER NNA;Lo;0;L;;;;;N;;;;;
+0D24;MALAYALAM LETTER TA;Lo;0;L;;;;;N;;;;;
+0D25;MALAYALAM LETTER THA;Lo;0;L;;;;;N;;;;;
+0D26;MALAYALAM LETTER DA;Lo;0;L;;;;;N;;;;;
+0D27;MALAYALAM LETTER DHA;Lo;0;L;;;;;N;;;;;
+0D28;MALAYALAM LETTER NA;Lo;0;L;;;;;N;;;;;
+0D29;MALAYALAM LETTER NNNA;Lo;0;L;;;;;N;;;;;
+0D2A;MALAYALAM LETTER PA;Lo;0;L;;;;;N;;;;;
+0D2B;MALAYALAM LETTER PHA;Lo;0;L;;;;;N;;;;;
+0D2C;MALAYALAM LETTER BA;Lo;0;L;;;;;N;;;;;
+0D2D;MALAYALAM LETTER BHA;Lo;0;L;;;;;N;;;;;
+0D2E;MALAYALAM LETTER MA;Lo;0;L;;;;;N;;;;;
+0D2F;MALAYALAM LETTER YA;Lo;0;L;;;;;N;;;;;
+0D30;MALAYALAM LETTER RA;Lo;0;L;;;;;N;;;;;
+0D31;MALAYALAM LETTER RRA;Lo;0;L;;;;;N;;;;;
+0D32;MALAYALAM LETTER LA;Lo;0;L;;;;;N;;;;;
+0D33;MALAYALAM LETTER LLA;Lo;0;L;;;;;N;;;;;
+0D34;MALAYALAM LETTER LLLA;Lo;0;L;;;;;N;;;;;
+0D35;MALAYALAM LETTER VA;Lo;0;L;;;;;N;;;;;
+0D36;MALAYALAM LETTER SHA;Lo;0;L;;;;;N;;;;;
+0D37;MALAYALAM LETTER SSA;Lo;0;L;;;;;N;;;;;
+0D38;MALAYALAM LETTER SA;Lo;0;L;;;;;N;;;;;
+0D39;MALAYALAM LETTER HA;Lo;0;L;;;;;N;;;;;
+0D3A;MALAYALAM LETTER TTTA;Lo;0;L;;;;;N;;;;;
+0D3B;MALAYALAM SIGN VERTICAL BAR VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0D3C;MALAYALAM SIGN CIRCULAR VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0D3D;MALAYALAM SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+0D3E;MALAYALAM VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0D3F;MALAYALAM VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0D40;MALAYALAM VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0D41;MALAYALAM VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0D42;MALAYALAM VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0D43;MALAYALAM VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+0D44;MALAYALAM VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+0D46;MALAYALAM VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+0D47;MALAYALAM VOWEL SIGN EE;Mc;0;L;;;;;N;;;;;
+0D48;MALAYALAM VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+0D4A;MALAYALAM VOWEL SIGN O;Mc;0;L;0D46 0D3E;;;;N;;;;;
+0D4B;MALAYALAM VOWEL SIGN OO;Mc;0;L;0D47 0D3E;;;;N;;;;;
+0D4C;MALAYALAM VOWEL SIGN AU;Mc;0;L;0D46 0D57;;;;N;;;;;
+0D4D;MALAYALAM SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0D4E;MALAYALAM LETTER DOT REPH;Lo;0;L;;;;;N;;;;;
+0D4F;MALAYALAM SIGN PARA;So;0;L;;;;;N;;;;;
+0D54;MALAYALAM LETTER CHILLU M;Lo;0;L;;;;;N;;;;;
+0D55;MALAYALAM LETTER CHILLU Y;Lo;0;L;;;;;N;;;;;
+0D56;MALAYALAM LETTER CHILLU LLL;Lo;0;L;;;;;N;;;;;
+0D57;MALAYALAM AU LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0D58;MALAYALAM FRACTION ONE ONE-HUNDRED-AND-SIXTIETH;No;0;L;;;;1/160;N;;;;;
+0D59;MALAYALAM FRACTION ONE FORTIETH;No;0;L;;;;1/40;N;;;;;
+0D5A;MALAYALAM FRACTION THREE EIGHTIETHS;No;0;L;;;;3/80;N;;;;;
+0D5B;MALAYALAM FRACTION ONE TWENTIETH;No;0;L;;;;1/20;N;;;;;
+0D5C;MALAYALAM FRACTION ONE TENTH;No;0;L;;;;1/10;N;;;;;
+0D5D;MALAYALAM FRACTION THREE TWENTIETHS;No;0;L;;;;3/20;N;;;;;
+0D5E;MALAYALAM FRACTION ONE FIFTH;No;0;L;;;;1/5;N;;;;;
+0D5F;MALAYALAM LETTER ARCHAIC II;Lo;0;L;;;;;N;;;;;
+0D60;MALAYALAM LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0D61;MALAYALAM LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0D62;MALAYALAM VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+0D63;MALAYALAM VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+0D66;MALAYALAM DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0D67;MALAYALAM DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0D68;MALAYALAM DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0D69;MALAYALAM DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0D6A;MALAYALAM DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0D6B;MALAYALAM DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0D6C;MALAYALAM DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0D6D;MALAYALAM DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0D6E;MALAYALAM DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0D6F;MALAYALAM DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0D70;MALAYALAM NUMBER TEN;No;0;L;;;;10;N;;;;;
+0D71;MALAYALAM NUMBER ONE HUNDRED;No;0;L;;;;100;N;;;;;
+0D72;MALAYALAM NUMBER ONE THOUSAND;No;0;L;;;;1000;N;;;;;
+0D73;MALAYALAM FRACTION ONE QUARTER;No;0;L;;;;1/4;N;;;;;
+0D74;MALAYALAM FRACTION ONE HALF;No;0;L;;;;1/2;N;;;;;
+0D75;MALAYALAM FRACTION THREE QUARTERS;No;0;L;;;;3/4;N;;;;;
+0D76;MALAYALAM FRACTION ONE SIXTEENTH;No;0;L;;;;1/16;N;;;;;
+0D77;MALAYALAM FRACTION ONE EIGHTH;No;0;L;;;;1/8;N;;;;;
+0D78;MALAYALAM FRACTION THREE SIXTEENTHS;No;0;L;;;;3/16;N;;;;;
+0D79;MALAYALAM DATE MARK;So;0;L;;;;;N;;;;;
+0D7A;MALAYALAM LETTER CHILLU NN;Lo;0;L;;;;;N;;;;;
+0D7B;MALAYALAM LETTER CHILLU N;Lo;0;L;;;;;N;;;;;
+0D7C;MALAYALAM LETTER CHILLU RR;Lo;0;L;;;;;N;;;;;
+0D7D;MALAYALAM LETTER CHILLU L;Lo;0;L;;;;;N;;;;;
+0D7E;MALAYALAM LETTER CHILLU LL;Lo;0;L;;;;;N;;;;;
+0D7F;MALAYALAM LETTER CHILLU K;Lo;0;L;;;;;N;;;;;
+0D82;SINHALA SIGN ANUSVARAYA;Mc;0;L;;;;;N;;;;;
+0D83;SINHALA SIGN VISARGAYA;Mc;0;L;;;;;N;;;;;
+0D85;SINHALA LETTER AYANNA;Lo;0;L;;;;;N;;;;;
+0D86;SINHALA LETTER AAYANNA;Lo;0;L;;;;;N;;;;;
+0D87;SINHALA LETTER AEYANNA;Lo;0;L;;;;;N;;;;;
+0D88;SINHALA LETTER AEEYANNA;Lo;0;L;;;;;N;;;;;
+0D89;SINHALA LETTER IYANNA;Lo;0;L;;;;;N;;;;;
+0D8A;SINHALA LETTER IIYANNA;Lo;0;L;;;;;N;;;;;
+0D8B;SINHALA LETTER UYANNA;Lo;0;L;;;;;N;;;;;
+0D8C;SINHALA LETTER UUYANNA;Lo;0;L;;;;;N;;;;;
+0D8D;SINHALA LETTER IRUYANNA;Lo;0;L;;;;;N;;;;;
+0D8E;SINHALA LETTER IRUUYANNA;Lo;0;L;;;;;N;;;;;
+0D8F;SINHALA LETTER ILUYANNA;Lo;0;L;;;;;N;;;;;
+0D90;SINHALA LETTER ILUUYANNA;Lo;0;L;;;;;N;;;;;
+0D91;SINHALA LETTER EYANNA;Lo;0;L;;;;;N;;;;;
+0D92;SINHALA LETTER EEYANNA;Lo;0;L;;;;;N;;;;;
+0D93;SINHALA LETTER AIYANNA;Lo;0;L;;;;;N;;;;;
+0D94;SINHALA LETTER OYANNA;Lo;0;L;;;;;N;;;;;
+0D95;SINHALA LETTER OOYANNA;Lo;0;L;;;;;N;;;;;
+0D96;SINHALA LETTER AUYANNA;Lo;0;L;;;;;N;;;;;
+0D9A;SINHALA LETTER ALPAPRAANA KAYANNA;Lo;0;L;;;;;N;;;;;
+0D9B;SINHALA LETTER MAHAAPRAANA KAYANNA;Lo;0;L;;;;;N;;;;;
+0D9C;SINHALA LETTER ALPAPRAANA GAYANNA;Lo;0;L;;;;;N;;;;;
+0D9D;SINHALA LETTER MAHAAPRAANA GAYANNA;Lo;0;L;;;;;N;;;;;
+0D9E;SINHALA LETTER KANTAJA NAASIKYAYA;Lo;0;L;;;;;N;;;;;
+0D9F;SINHALA LETTER SANYAKA GAYANNA;Lo;0;L;;;;;N;;;;;
+0DA0;SINHALA LETTER ALPAPRAANA CAYANNA;Lo;0;L;;;;;N;;;;;
+0DA1;SINHALA LETTER MAHAAPRAANA CAYANNA;Lo;0;L;;;;;N;;;;;
+0DA2;SINHALA LETTER ALPAPRAANA JAYANNA;Lo;0;L;;;;;N;;;;;
+0DA3;SINHALA LETTER MAHAAPRAANA JAYANNA;Lo;0;L;;;;;N;;;;;
+0DA4;SINHALA LETTER TAALUJA NAASIKYAYA;Lo;0;L;;;;;N;;;;;
+0DA5;SINHALA LETTER TAALUJA SANYOOGA NAAKSIKYAYA;Lo;0;L;;;;;N;;;;;
+0DA6;SINHALA LETTER SANYAKA JAYANNA;Lo;0;L;;;;;N;;;;;
+0DA7;SINHALA LETTER ALPAPRAANA TTAYANNA;Lo;0;L;;;;;N;;;;;
+0DA8;SINHALA LETTER MAHAAPRAANA TTAYANNA;Lo;0;L;;;;;N;;;;;
+0DA9;SINHALA LETTER ALPAPRAANA DDAYANNA;Lo;0;L;;;;;N;;;;;
+0DAA;SINHALA LETTER MAHAAPRAANA DDAYANNA;Lo;0;L;;;;;N;;;;;
+0DAB;SINHALA LETTER MUURDHAJA NAYANNA;Lo;0;L;;;;;N;;;;;
+0DAC;SINHALA LETTER SANYAKA DDAYANNA;Lo;0;L;;;;;N;;;;;
+0DAD;SINHALA LETTER ALPAPRAANA TAYANNA;Lo;0;L;;;;;N;;;;;
+0DAE;SINHALA LETTER MAHAAPRAANA TAYANNA;Lo;0;L;;;;;N;;;;;
+0DAF;SINHALA LETTER ALPAPRAANA DAYANNA;Lo;0;L;;;;;N;;;;;
+0DB0;SINHALA LETTER MAHAAPRAANA DAYANNA;Lo;0;L;;;;;N;;;;;
+0DB1;SINHALA LETTER DANTAJA NAYANNA;Lo;0;L;;;;;N;;;;;
+0DB3;SINHALA LETTER SANYAKA DAYANNA;Lo;0;L;;;;;N;;;;;
+0DB4;SINHALA LETTER ALPAPRAANA PAYANNA;Lo;0;L;;;;;N;;;;;
+0DB5;SINHALA LETTER MAHAAPRAANA PAYANNA;Lo;0;L;;;;;N;;;;;
+0DB6;SINHALA LETTER ALPAPRAANA BAYANNA;Lo;0;L;;;;;N;;;;;
+0DB7;SINHALA LETTER MAHAAPRAANA BAYANNA;Lo;0;L;;;;;N;;;;;
+0DB8;SINHALA LETTER MAYANNA;Lo;0;L;;;;;N;;;;;
+0DB9;SINHALA LETTER AMBA BAYANNA;Lo;0;L;;;;;N;;;;;
+0DBA;SINHALA LETTER YAYANNA;Lo;0;L;;;;;N;;;;;
+0DBB;SINHALA LETTER RAYANNA;Lo;0;L;;;;;N;;;;;
+0DBD;SINHALA LETTER DANTAJA LAYANNA;Lo;0;L;;;;;N;;;;;
+0DC0;SINHALA LETTER VAYANNA;Lo;0;L;;;;;N;;;;;
+0DC1;SINHALA LETTER TAALUJA SAYANNA;Lo;0;L;;;;;N;;;;;
+0DC2;SINHALA LETTER MUURDHAJA SAYANNA;Lo;0;L;;;;;N;;;;;
+0DC3;SINHALA LETTER DANTAJA SAYANNA;Lo;0;L;;;;;N;;;;;
+0DC4;SINHALA LETTER HAYANNA;Lo;0;L;;;;;N;;;;;
+0DC5;SINHALA LETTER MUURDHAJA LAYANNA;Lo;0;L;;;;;N;;;;;
+0DC6;SINHALA LETTER FAYANNA;Lo;0;L;;;;;N;;;;;
+0DCA;SINHALA SIGN AL-LAKUNA;Mn;9;NSM;;;;;N;;;;;
+0DCF;SINHALA VOWEL SIGN AELA-PILLA;Mc;0;L;;;;;N;;;;;
+0DD0;SINHALA VOWEL SIGN KETTI AEDA-PILLA;Mc;0;L;;;;;N;;;;;
+0DD1;SINHALA VOWEL SIGN DIGA AEDA-PILLA;Mc;0;L;;;;;N;;;;;
+0DD2;SINHALA VOWEL SIGN KETTI IS-PILLA;Mn;0;NSM;;;;;N;;;;;
+0DD3;SINHALA VOWEL SIGN DIGA IS-PILLA;Mn;0;NSM;;;;;N;;;;;
+0DD4;SINHALA VOWEL SIGN KETTI PAA-PILLA;Mn;0;NSM;;;;;N;;;;;
+0DD6;SINHALA VOWEL SIGN DIGA PAA-PILLA;Mn;0;NSM;;;;;N;;;;;
+0DD8;SINHALA VOWEL SIGN GAETTA-PILLA;Mc;0;L;;;;;N;;;;;
+0DD9;SINHALA VOWEL SIGN KOMBUVA;Mc;0;L;;;;;N;;;;;
+0DDA;SINHALA VOWEL SIGN DIGA KOMBUVA;Mc;0;L;0DD9 0DCA;;;;N;;;;;
+0DDB;SINHALA VOWEL SIGN KOMBU DEKA;Mc;0;L;;;;;N;;;;;
+0DDC;SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA;Mc;0;L;0DD9 0DCF;;;;N;;;;;
+0DDD;SINHALA VOWEL SIGN KOMBUVA HAA DIGA AELA-PILLA;Mc;0;L;0DDC 0DCA;;;;N;;;;;
+0DDE;SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA;Mc;0;L;0DD9 0DDF;;;;N;;;;;
+0DDF;SINHALA VOWEL SIGN GAYANUKITTA;Mc;0;L;;;;;N;;;;;
+0DE6;SINHALA LITH DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0DE7;SINHALA LITH DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0DE8;SINHALA LITH DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0DE9;SINHALA LITH DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0DEA;SINHALA LITH DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0DEB;SINHALA LITH DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0DEC;SINHALA LITH DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0DED;SINHALA LITH DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0DEE;SINHALA LITH DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0DEF;SINHALA LITH DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0DF2;SINHALA VOWEL SIGN DIGA GAETTA-PILLA;Mc;0;L;;;;;N;;;;;
+0DF3;SINHALA VOWEL SIGN DIGA GAYANUKITTA;Mc;0;L;;;;;N;;;;;
+0DF4;SINHALA PUNCTUATION KUNDDALIYA;Po;0;L;;;;;N;;;;;
+0E01;THAI CHARACTER KO KAI;Lo;0;L;;;;;N;THAI LETTER KO KAI;;;;
+0E02;THAI CHARACTER KHO KHAI;Lo;0;L;;;;;N;THAI LETTER KHO KHAI;;;;
+0E03;THAI CHARACTER KHO KHUAT;Lo;0;L;;;;;N;THAI LETTER KHO KHUAT;;;;
+0E04;THAI CHARACTER KHO KHWAI;Lo;0;L;;;;;N;THAI LETTER KHO KHWAI;;;;
+0E05;THAI CHARACTER KHO KHON;Lo;0;L;;;;;N;THAI LETTER KHO KHON;;;;
+0E06;THAI CHARACTER KHO RAKHANG;Lo;0;L;;;;;N;THAI LETTER KHO RAKHANG;;;;
+0E07;THAI CHARACTER NGO NGU;Lo;0;L;;;;;N;THAI LETTER NGO NGU;;;;
+0E08;THAI CHARACTER CHO CHAN;Lo;0;L;;;;;N;THAI LETTER CHO CHAN;;;;
+0E09;THAI CHARACTER CHO CHING;Lo;0;L;;;;;N;THAI LETTER CHO CHING;;;;
+0E0A;THAI CHARACTER CHO CHANG;Lo;0;L;;;;;N;THAI LETTER CHO CHANG;;;;
+0E0B;THAI CHARACTER SO SO;Lo;0;L;;;;;N;THAI LETTER SO SO;;;;
+0E0C;THAI CHARACTER CHO CHOE;Lo;0;L;;;;;N;THAI LETTER CHO CHOE;;;;
+0E0D;THAI CHARACTER YO YING;Lo;0;L;;;;;N;THAI LETTER YO YING;;;;
+0E0E;THAI CHARACTER DO CHADA;Lo;0;L;;;;;N;THAI LETTER DO CHADA;;;;
+0E0F;THAI CHARACTER TO PATAK;Lo;0;L;;;;;N;THAI LETTER TO PATAK;;;;
+0E10;THAI CHARACTER THO THAN;Lo;0;L;;;;;N;THAI LETTER THO THAN;;;;
+0E11;THAI CHARACTER THO NANGMONTHO;Lo;0;L;;;;;N;THAI LETTER THO NANGMONTHO;;;;
+0E12;THAI CHARACTER THO PHUTHAO;Lo;0;L;;;;;N;THAI LETTER THO PHUTHAO;;;;
+0E13;THAI CHARACTER NO NEN;Lo;0;L;;;;;N;THAI LETTER NO NEN;;;;
+0E14;THAI CHARACTER DO DEK;Lo;0;L;;;;;N;THAI LETTER DO DEK;;;;
+0E15;THAI CHARACTER TO TAO;Lo;0;L;;;;;N;THAI LETTER TO TAO;;;;
+0E16;THAI CHARACTER THO THUNG;Lo;0;L;;;;;N;THAI LETTER THO THUNG;;;;
+0E17;THAI CHARACTER THO THAHAN;Lo;0;L;;;;;N;THAI LETTER THO THAHAN;;;;
+0E18;THAI CHARACTER THO THONG;Lo;0;L;;;;;N;THAI LETTER THO THONG;;;;
+0E19;THAI CHARACTER NO NU;Lo;0;L;;;;;N;THAI LETTER NO NU;;;;
+0E1A;THAI CHARACTER BO BAIMAI;Lo;0;L;;;;;N;THAI LETTER BO BAIMAI;;;;
+0E1B;THAI CHARACTER PO PLA;Lo;0;L;;;;;N;THAI LETTER PO PLA;;;;
+0E1C;THAI CHARACTER PHO PHUNG;Lo;0;L;;;;;N;THAI LETTER PHO PHUNG;;;;
+0E1D;THAI CHARACTER FO FA;Lo;0;L;;;;;N;THAI LETTER FO FA;;;;
+0E1E;THAI CHARACTER PHO PHAN;Lo;0;L;;;;;N;THAI LETTER PHO PHAN;;;;
+0E1F;THAI CHARACTER FO FAN;Lo;0;L;;;;;N;THAI LETTER FO FAN;;;;
+0E20;THAI CHARACTER PHO SAMPHAO;Lo;0;L;;;;;N;THAI LETTER PHO SAMPHAO;;;;
+0E21;THAI CHARACTER MO MA;Lo;0;L;;;;;N;THAI LETTER MO MA;;;;
+0E22;THAI CHARACTER YO YAK;Lo;0;L;;;;;N;THAI LETTER YO YAK;;;;
+0E23;THAI CHARACTER RO RUA;Lo;0;L;;;;;N;THAI LETTER RO RUA;;;;
+0E24;THAI CHARACTER RU;Lo;0;L;;;;;N;THAI LETTER RU;;;;
+0E25;THAI CHARACTER LO LING;Lo;0;L;;;;;N;THAI LETTER LO LING;;;;
+0E26;THAI CHARACTER LU;Lo;0;L;;;;;N;THAI LETTER LU;;;;
+0E27;THAI CHARACTER WO WAEN;Lo;0;L;;;;;N;THAI LETTER WO WAEN;;;;
+0E28;THAI CHARACTER SO SALA;Lo;0;L;;;;;N;THAI LETTER SO SALA;;;;
+0E29;THAI CHARACTER SO RUSI;Lo;0;L;;;;;N;THAI LETTER SO RUSI;;;;
+0E2A;THAI CHARACTER SO SUA;Lo;0;L;;;;;N;THAI LETTER SO SUA;;;;
+0E2B;THAI CHARACTER HO HIP;Lo;0;L;;;;;N;THAI LETTER HO HIP;;;;
+0E2C;THAI CHARACTER LO CHULA;Lo;0;L;;;;;N;THAI LETTER LO CHULA;;;;
+0E2D;THAI CHARACTER O ANG;Lo;0;L;;;;;N;THAI LETTER O ANG;;;;
+0E2E;THAI CHARACTER HO NOKHUK;Lo;0;L;;;;;N;THAI LETTER HO NOK HUK;;;;
+0E2F;THAI CHARACTER PAIYANNOI;Lo;0;L;;;;;N;THAI PAI YAN NOI;;;;
+0E30;THAI CHARACTER SARA A;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA A;;;;
+0E31;THAI CHARACTER MAI HAN-AKAT;Mn;0;NSM;;;;;N;THAI VOWEL SIGN MAI HAN-AKAT;;;;
+0E32;THAI CHARACTER SARA AA;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA AA;;;;
+0E33;THAI CHARACTER SARA AM;Lo;0;L;<compat> 0E4D 0E32;;;;N;THAI VOWEL SIGN SARA AM;;;;
+0E34;THAI CHARACTER SARA I;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA I;;;;
+0E35;THAI CHARACTER SARA II;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA II;;;;
+0E36;THAI CHARACTER SARA UE;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA UE;;;;
+0E37;THAI CHARACTER SARA UEE;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA UEE;;;;
+0E38;THAI CHARACTER SARA U;Mn;103;NSM;;;;;N;THAI VOWEL SIGN SARA U;;;;
+0E39;THAI CHARACTER SARA UU;Mn;103;NSM;;;;;N;THAI VOWEL SIGN SARA UU;;;;
+0E3A;THAI CHARACTER PHINTHU;Mn;9;NSM;;;;;N;THAI VOWEL SIGN PHINTHU;;;;
+0E3F;THAI CURRENCY SYMBOL BAHT;Sc;0;ET;;;;;N;THAI BAHT SIGN;;;;
+0E40;THAI CHARACTER SARA E;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA E;;;;
+0E41;THAI CHARACTER SARA AE;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA AE;;;;
+0E42;THAI CHARACTER SARA O;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA O;;;;
+0E43;THAI CHARACTER SARA AI MAIMUAN;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA MAI MUAN;;;;
+0E44;THAI CHARACTER SARA AI MAIMALAI;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA MAI MALAI;;;;
+0E45;THAI CHARACTER LAKKHANGYAO;Lo;0;L;;;;;N;THAI LAK KHANG YAO;;;;
+0E46;THAI CHARACTER MAIYAMOK;Lm;0;L;;;;;N;THAI MAI YAMOK;;;;
+0E47;THAI CHARACTER MAITAIKHU;Mn;0;NSM;;;;;N;THAI VOWEL SIGN MAI TAI KHU;;;;
+0E48;THAI CHARACTER MAI EK;Mn;107;NSM;;;;;N;THAI TONE MAI EK;;;;
+0E49;THAI CHARACTER MAI THO;Mn;107;NSM;;;;;N;THAI TONE MAI THO;;;;
+0E4A;THAI CHARACTER MAI TRI;Mn;107;NSM;;;;;N;THAI TONE MAI TRI;;;;
+0E4B;THAI CHARACTER MAI CHATTAWA;Mn;107;NSM;;;;;N;THAI TONE MAI CHATTAWA;;;;
+0E4C;THAI CHARACTER THANTHAKHAT;Mn;0;NSM;;;;;N;THAI THANTHAKHAT;;;;
+0E4D;THAI CHARACTER NIKHAHIT;Mn;0;NSM;;;;;N;THAI NIKKHAHIT;;;;
+0E4E;THAI CHARACTER YAMAKKAN;Mn;0;NSM;;;;;N;THAI YAMAKKAN;;;;
+0E4F;THAI CHARACTER FONGMAN;Po;0;L;;;;;N;THAI FONGMAN;;;;
+0E50;THAI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0E51;THAI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0E52;THAI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0E53;THAI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0E54;THAI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0E55;THAI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0E56;THAI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0E57;THAI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0E58;THAI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0E59;THAI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0E5A;THAI CHARACTER ANGKHANKHU;Po;0;L;;;;;N;THAI ANGKHANKHU;;;;
+0E5B;THAI CHARACTER KHOMUT;Po;0;L;;;;;N;THAI KHOMUT;;;;
+0E81;LAO LETTER KO;Lo;0;L;;;;;N;;;;;
+0E82;LAO LETTER KHO SUNG;Lo;0;L;;;;;N;;;;;
+0E84;LAO LETTER KHO TAM;Lo;0;L;;;;;N;;;;;
+0E86;LAO LETTER PALI GHA;Lo;0;L;;;;;N;;;;;
+0E87;LAO LETTER NGO;Lo;0;L;;;;;N;;;;;
+0E88;LAO LETTER CO;Lo;0;L;;;;;N;;;;;
+0E89;LAO LETTER PALI CHA;Lo;0;L;;;;;N;;;;;
+0E8A;LAO LETTER SO TAM;Lo;0;L;;;;;N;;;;;
+0E8C;LAO LETTER PALI JHA;Lo;0;L;;;;;N;;;;;
+0E8D;LAO LETTER NYO;Lo;0;L;;;;;N;;;;;
+0E8E;LAO LETTER PALI NYA;Lo;0;L;;;;;N;;;;;
+0E8F;LAO LETTER PALI TTA;Lo;0;L;;;;;N;;;;;
+0E90;LAO LETTER PALI TTHA;Lo;0;L;;;;;N;;;;;
+0E91;LAO LETTER PALI DDA;Lo;0;L;;;;;N;;;;;
+0E92;LAO LETTER PALI DDHA;Lo;0;L;;;;;N;;;;;
+0E93;LAO LETTER PALI NNA;Lo;0;L;;;;;N;;;;;
+0E94;LAO LETTER DO;Lo;0;L;;;;;N;;;;;
+0E95;LAO LETTER TO;Lo;0;L;;;;;N;;;;;
+0E96;LAO LETTER THO SUNG;Lo;0;L;;;;;N;;;;;
+0E97;LAO LETTER THO TAM;Lo;0;L;;;;;N;;;;;
+0E98;LAO LETTER PALI DHA;Lo;0;L;;;;;N;;;;;
+0E99;LAO LETTER NO;Lo;0;L;;;;;N;;;;;
+0E9A;LAO LETTER BO;Lo;0;L;;;;;N;;;;;
+0E9B;LAO LETTER PO;Lo;0;L;;;;;N;;;;;
+0E9C;LAO LETTER PHO SUNG;Lo;0;L;;;;;N;;;;;
+0E9D;LAO LETTER FO TAM;Lo;0;L;;;;;N;;;;;
+0E9E;LAO LETTER PHO TAM;Lo;0;L;;;;;N;;;;;
+0E9F;LAO LETTER FO SUNG;Lo;0;L;;;;;N;;;;;
+0EA0;LAO LETTER PALI BHA;Lo;0;L;;;;;N;;;;;
+0EA1;LAO LETTER MO;Lo;0;L;;;;;N;;;;;
+0EA2;LAO LETTER YO;Lo;0;L;;;;;N;;;;;
+0EA3;LAO LETTER LO LING;Lo;0;L;;;;;N;;;;;
+0EA5;LAO LETTER LO LOOT;Lo;0;L;;;;;N;;;;;
+0EA7;LAO LETTER WO;Lo;0;L;;;;;N;;;;;
+0EA8;LAO LETTER SANSKRIT SHA;Lo;0;L;;;;;N;;;;;
+0EA9;LAO LETTER SANSKRIT SSA;Lo;0;L;;;;;N;;;;;
+0EAA;LAO LETTER SO SUNG;Lo;0;L;;;;;N;;;;;
+0EAB;LAO LETTER HO SUNG;Lo;0;L;;;;;N;;;;;
+0EAC;LAO LETTER PALI LLA;Lo;0;L;;;;;N;;;;;
+0EAD;LAO LETTER O;Lo;0;L;;;;;N;;;;;
+0EAE;LAO LETTER HO TAM;Lo;0;L;;;;;N;;;;;
+0EAF;LAO ELLIPSIS;Lo;0;L;;;;;N;;;;;
+0EB0;LAO VOWEL SIGN A;Lo;0;L;;;;;N;;;;;
+0EB1;LAO VOWEL SIGN MAI KAN;Mn;0;NSM;;;;;N;;;;;
+0EB2;LAO VOWEL SIGN AA;Lo;0;L;;;;;N;;;;;
+0EB3;LAO VOWEL SIGN AM;Lo;0;L;<compat> 0ECD 0EB2;;;;N;;;;;
+0EB4;LAO VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+0EB5;LAO VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+0EB6;LAO VOWEL SIGN Y;Mn;0;NSM;;;;;N;;;;;
+0EB7;LAO VOWEL SIGN YY;Mn;0;NSM;;;;;N;;;;;
+0EB8;LAO VOWEL SIGN U;Mn;118;NSM;;;;;N;;;;;
+0EB9;LAO VOWEL SIGN UU;Mn;118;NSM;;;;;N;;;;;
+0EBA;LAO SIGN PALI VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0EBB;LAO VOWEL SIGN MAI KON;Mn;0;NSM;;;;;N;;;;;
+0EBC;LAO SEMIVOWEL SIGN LO;Mn;0;NSM;;;;;N;;;;;
+0EBD;LAO SEMIVOWEL SIGN NYO;Lo;0;L;;;;;N;;;;;
+0EC0;LAO VOWEL SIGN E;Lo;0;L;;;;;N;;;;;
+0EC1;LAO VOWEL SIGN EI;Lo;0;L;;;;;N;;;;;
+0EC2;LAO VOWEL SIGN O;Lo;0;L;;;;;N;;;;;
+0EC3;LAO VOWEL SIGN AY;Lo;0;L;;;;;N;;;;;
+0EC4;LAO VOWEL SIGN AI;Lo;0;L;;;;;N;;;;;
+0EC6;LAO KO LA;Lm;0;L;;;;;N;;;;;
+0EC8;LAO TONE MAI EK;Mn;122;NSM;;;;;N;;;;;
+0EC9;LAO TONE MAI THO;Mn;122;NSM;;;;;N;;;;;
+0ECA;LAO TONE MAI TI;Mn;122;NSM;;;;;N;;;;;
+0ECB;LAO TONE MAI CATAWA;Mn;122;NSM;;;;;N;;;;;
+0ECC;LAO CANCELLATION MARK;Mn;0;NSM;;;;;N;;;;;
+0ECD;LAO NIGGAHITA;Mn;0;NSM;;;;;N;;;;;
+0ED0;LAO DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0ED1;LAO DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0ED2;LAO DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0ED3;LAO DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0ED4;LAO DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0ED5;LAO DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0ED6;LAO DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0ED7;LAO DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0ED8;LAO DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0ED9;LAO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0EDC;LAO HO NO;Lo;0;L;<compat> 0EAB 0E99;;;;N;;;;;
+0EDD;LAO HO MO;Lo;0;L;<compat> 0EAB 0EA1;;;;N;;;;;
+0EDE;LAO LETTER KHMU GO;Lo;0;L;;;;;N;;;;;
+0EDF;LAO LETTER KHMU NYO;Lo;0;L;;;;;N;;;;;
+0F00;TIBETAN SYLLABLE OM;Lo;0;L;;;;;N;;;;;
+0F01;TIBETAN MARK GTER YIG MGO TRUNCATED A;So;0;L;;;;;N;;;;;
+0F02;TIBETAN MARK GTER YIG MGO -UM RNAM BCAD MA;So;0;L;;;;;N;;;;;
+0F03;TIBETAN MARK GTER YIG MGO -UM GTER TSHEG MA;So;0;L;;;;;N;;;;;
+0F04;TIBETAN MARK INITIAL YIG MGO MDUN MA;Po;0;L;;;;;N;TIBETAN SINGLE ORNAMENT;;;;
+0F05;TIBETAN MARK CLOSING YIG MGO SGAB MA;Po;0;L;;;;;N;;;;;
+0F06;TIBETAN MARK CARET YIG MGO PHUR SHAD MA;Po;0;L;;;;;N;;;;;
+0F07;TIBETAN MARK YIG MGO TSHEG SHAD MA;Po;0;L;;;;;N;;;;;
+0F08;TIBETAN MARK SBRUL SHAD;Po;0;L;;;;;N;TIBETAN RGYANSHAD;;;;
+0F09;TIBETAN MARK BSKUR YIG MGO;Po;0;L;;;;;N;;;;;
+0F0A;TIBETAN MARK BKA- SHOG YIG MGO;Po;0;L;;;;;N;;;;;
+0F0B;TIBETAN MARK INTERSYLLABIC TSHEG;Po;0;L;;;;;N;TIBETAN TSEG;;;;
+0F0C;TIBETAN MARK DELIMITER TSHEG BSTAR;Po;0;L;<noBreak> 0F0B;;;;N;;;;;
+0F0D;TIBETAN MARK SHAD;Po;0;L;;;;;N;TIBETAN SHAD;;;;
+0F0E;TIBETAN MARK NYIS SHAD;Po;0;L;;;;;N;TIBETAN DOUBLE SHAD;;;;
+0F0F;TIBETAN MARK TSHEG SHAD;Po;0;L;;;;;N;;;;;
+0F10;TIBETAN MARK NYIS TSHEG SHAD;Po;0;L;;;;;N;;;;;
+0F11;TIBETAN MARK RIN CHEN SPUNGS SHAD;Po;0;L;;;;;N;TIBETAN RINCHANPHUNGSHAD;;;;
+0F12;TIBETAN MARK RGYA GRAM SHAD;Po;0;L;;;;;N;;;;;
+0F13;TIBETAN MARK CARET -DZUD RTAGS ME LONG CAN;So;0;L;;;;;N;;;;;
+0F14;TIBETAN MARK GTER TSHEG;Po;0;L;;;;;N;TIBETAN COMMA;;;;
+0F15;TIBETAN LOGOTYPE SIGN CHAD RTAGS;So;0;L;;;;;N;;;;;
+0F16;TIBETAN LOGOTYPE SIGN LHAG RTAGS;So;0;L;;;;;N;;;;;
+0F17;TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CHAR RTAGS;So;0;L;;;;;N;;;;;
+0F18;TIBETAN ASTROLOGICAL SIGN -KHYUD PA;Mn;220;NSM;;;;;N;;;;;
+0F19;TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS;Mn;220;NSM;;;;;N;;;;;
+0F1A;TIBETAN SIGN RDEL DKAR GCIG;So;0;L;;;;;N;;;;;
+0F1B;TIBETAN SIGN RDEL DKAR GNYIS;So;0;L;;;;;N;;;;;
+0F1C;TIBETAN SIGN RDEL DKAR GSUM;So;0;L;;;;;N;;;;;
+0F1D;TIBETAN SIGN RDEL NAG GCIG;So;0;L;;;;;N;;;;;
+0F1E;TIBETAN SIGN RDEL NAG GNYIS;So;0;L;;;;;N;;;;;
+0F1F;TIBETAN SIGN RDEL DKAR RDEL NAG;So;0;L;;;;;N;;;;;
+0F20;TIBETAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0F21;TIBETAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0F22;TIBETAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0F23;TIBETAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0F24;TIBETAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0F25;TIBETAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0F26;TIBETAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0F27;TIBETAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0F28;TIBETAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0F29;TIBETAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0F2A;TIBETAN DIGIT HALF ONE;No;0;L;;;;1/2;N;;;;;
+0F2B;TIBETAN DIGIT HALF TWO;No;0;L;;;;3/2;N;;;;;
+0F2C;TIBETAN DIGIT HALF THREE;No;0;L;;;;5/2;N;;;;;
+0F2D;TIBETAN DIGIT HALF FOUR;No;0;L;;;;7/2;N;;;;;
+0F2E;TIBETAN DIGIT HALF FIVE;No;0;L;;;;9/2;N;;;;;
+0F2F;TIBETAN DIGIT HALF SIX;No;0;L;;;;11/2;N;;;;;
+0F30;TIBETAN DIGIT HALF SEVEN;No;0;L;;;;13/2;N;;;;;
+0F31;TIBETAN DIGIT HALF EIGHT;No;0;L;;;;15/2;N;;;;;
+0F32;TIBETAN DIGIT HALF NINE;No;0;L;;;;17/2;N;;;;;
+0F33;TIBETAN DIGIT HALF ZERO;No;0;L;;;;-1/2;N;;;;;
+0F34;TIBETAN MARK BSDUS RTAGS;So;0;L;;;;;N;;;;;
+0F35;TIBETAN MARK NGAS BZUNG NYI ZLA;Mn;220;NSM;;;;;N;TIBETAN HONORIFIC UNDER RING;;;;
+0F36;TIBETAN MARK CARET -DZUD RTAGS BZHI MIG CAN;So;0;L;;;;;N;;;;;
+0F37;TIBETAN MARK NGAS BZUNG SGOR RTAGS;Mn;220;NSM;;;;;N;TIBETAN UNDER RING;;;;
+0F38;TIBETAN MARK CHE MGO;So;0;L;;;;;N;;;;;
+0F39;TIBETAN MARK TSA -PHRU;Mn;216;NSM;;;;;N;TIBETAN LENITION MARK;;;;
+0F3A;TIBETAN MARK GUG RTAGS GYON;Ps;0;ON;;;;;Y;;;;;
+0F3B;TIBETAN MARK GUG RTAGS GYAS;Pe;0;ON;;;;;Y;;;;;
+0F3C;TIBETAN MARK ANG KHANG GYON;Ps;0;ON;;;;;Y;TIBETAN LEFT BRACE;;;;
+0F3D;TIBETAN MARK ANG KHANG GYAS;Pe;0;ON;;;;;Y;TIBETAN RIGHT BRACE;;;;
+0F3E;TIBETAN SIGN YAR TSHES;Mc;0;L;;;;;N;;;;;
+0F3F;TIBETAN SIGN MAR TSHES;Mc;0;L;;;;;N;;;;;
+0F40;TIBETAN LETTER KA;Lo;0;L;;;;;N;;;;;
+0F41;TIBETAN LETTER KHA;Lo;0;L;;;;;N;;;;;
+0F42;TIBETAN LETTER GA;Lo;0;L;;;;;N;;;;;
+0F43;TIBETAN LETTER GHA;Lo;0;L;0F42 0FB7;;;;N;;;;;
+0F44;TIBETAN LETTER NGA;Lo;0;L;;;;;N;;;;;
+0F45;TIBETAN LETTER CA;Lo;0;L;;;;;N;;;;;
+0F46;TIBETAN LETTER CHA;Lo;0;L;;;;;N;;;;;
+0F47;TIBETAN LETTER JA;Lo;0;L;;;;;N;;;;;
+0F49;TIBETAN LETTER NYA;Lo;0;L;;;;;N;;;;;
+0F4A;TIBETAN LETTER TTA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED TA;;;;
+0F4B;TIBETAN LETTER TTHA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED THA;;;;
+0F4C;TIBETAN LETTER DDA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED DA;;;;
+0F4D;TIBETAN LETTER DDHA;Lo;0;L;0F4C 0FB7;;;;N;;;;;
+0F4E;TIBETAN LETTER NNA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED NA;;;;
+0F4F;TIBETAN LETTER TA;Lo;0;L;;;;;N;;;;;
+0F50;TIBETAN LETTER THA;Lo;0;L;;;;;N;;;;;
+0F51;TIBETAN LETTER DA;Lo;0;L;;;;;N;;;;;
+0F52;TIBETAN LETTER DHA;Lo;0;L;0F51 0FB7;;;;N;;;;;
+0F53;TIBETAN LETTER NA;Lo;0;L;;;;;N;;;;;
+0F54;TIBETAN LETTER PA;Lo;0;L;;;;;N;;;;;
+0F55;TIBETAN LETTER PHA;Lo;0;L;;;;;N;;;;;
+0F56;TIBETAN LETTER BA;Lo;0;L;;;;;N;;;;;
+0F57;TIBETAN LETTER BHA;Lo;0;L;0F56 0FB7;;;;N;;;;;
+0F58;TIBETAN LETTER MA;Lo;0;L;;;;;N;;;;;
+0F59;TIBETAN LETTER TSA;Lo;0;L;;;;;N;;;;;
+0F5A;TIBETAN LETTER TSHA;Lo;0;L;;;;;N;;;;;
+0F5B;TIBETAN LETTER DZA;Lo;0;L;;;;;N;;;;;
+0F5C;TIBETAN LETTER DZHA;Lo;0;L;0F5B 0FB7;;;;N;;;;;
+0F5D;TIBETAN LETTER WA;Lo;0;L;;;;;N;;;;;
+0F5E;TIBETAN LETTER ZHA;Lo;0;L;;;;;N;;;;;
+0F5F;TIBETAN LETTER ZA;Lo;0;L;;;;;N;;;;;
+0F60;TIBETAN LETTER -A;Lo;0;L;;;;;N;TIBETAN LETTER AA;;;;
+0F61;TIBETAN LETTER YA;Lo;0;L;;;;;N;;;;;
+0F62;TIBETAN LETTER RA;Lo;0;L;;;;;N;;;;;
+0F63;TIBETAN LETTER LA;Lo;0;L;;;;;N;;;;;
+0F64;TIBETAN LETTER SHA;Lo;0;L;;;;;N;;;;;
+0F65;TIBETAN LETTER SSA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED SHA;;;;
+0F66;TIBETAN LETTER SA;Lo;0;L;;;;;N;;;;;
+0F67;TIBETAN LETTER HA;Lo;0;L;;;;;N;;;;;
+0F68;TIBETAN LETTER A;Lo;0;L;;;;;N;;;;;
+0F69;TIBETAN LETTER KSSA;Lo;0;L;0F40 0FB5;;;;N;;;;;
+0F6A;TIBETAN LETTER FIXED-FORM RA;Lo;0;L;;;;;N;;;;;
+0F6B;TIBETAN LETTER KKA;Lo;0;L;;;;;N;;;;;
+0F6C;TIBETAN LETTER RRA;Lo;0;L;;;;;N;;;;;
+0F71;TIBETAN VOWEL SIGN AA;Mn;129;NSM;;;;;N;;;;;
+0F72;TIBETAN VOWEL SIGN I;Mn;130;NSM;;;;;N;;;;;
+0F73;TIBETAN VOWEL SIGN II;Mn;0;NSM;0F71 0F72;;;;N;;;;;
+0F74;TIBETAN VOWEL SIGN U;Mn;132;NSM;;;;;N;;;;;
+0F75;TIBETAN VOWEL SIGN UU;Mn;0;NSM;0F71 0F74;;;;N;;;;;
+0F76;TIBETAN VOWEL SIGN VOCALIC R;Mn;0;NSM;0FB2 0F80;;;;N;;;;;
+0F77;TIBETAN VOWEL SIGN VOCALIC RR;Mn;0;NSM;<compat> 0FB2 0F81;;;;N;;;;;
+0F78;TIBETAN VOWEL SIGN VOCALIC L;Mn;0;NSM;0FB3 0F80;;;;N;;;;;
+0F79;TIBETAN VOWEL SIGN VOCALIC LL;Mn;0;NSM;<compat> 0FB3 0F81;;;;N;;;;;
+0F7A;TIBETAN VOWEL SIGN E;Mn;130;NSM;;;;;N;;;;;
+0F7B;TIBETAN VOWEL SIGN EE;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN AI;;;;
+0F7C;TIBETAN VOWEL SIGN O;Mn;130;NSM;;;;;N;;;;;
+0F7D;TIBETAN VOWEL SIGN OO;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN AU;;;;
+0F7E;TIBETAN SIGN RJES SU NGA RO;Mn;0;NSM;;;;;N;TIBETAN ANUSVARA;;;;
+0F7F;TIBETAN SIGN RNAM BCAD;Mc;0;L;;;;;N;TIBETAN VISARGA;;;;
+0F80;TIBETAN VOWEL SIGN REVERSED I;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN SHORT I;;;;
+0F81;TIBETAN VOWEL SIGN REVERSED II;Mn;0;NSM;0F71 0F80;;;;N;;;;;
+0F82;TIBETAN SIGN NYI ZLA NAA DA;Mn;230;NSM;;;;;N;TIBETAN CANDRABINDU WITH ORNAMENT;;;;
+0F83;TIBETAN SIGN SNA LDAN;Mn;230;NSM;;;;;N;TIBETAN CANDRABINDU;;;;
+0F84;TIBETAN MARK HALANTA;Mn;9;NSM;;;;;N;TIBETAN VIRAMA;;;;
+0F85;TIBETAN MARK PALUTA;Po;0;L;;;;;N;TIBETAN CHUCHENYIGE;;;;
+0F86;TIBETAN SIGN LCI RTAGS;Mn;230;NSM;;;;;N;;;;;
+0F87;TIBETAN SIGN YANG RTAGS;Mn;230;NSM;;;;;N;;;;;
+0F88;TIBETAN SIGN LCE TSA CAN;Lo;0;L;;;;;N;;;;;
+0F89;TIBETAN SIGN MCHU CAN;Lo;0;L;;;;;N;;;;;
+0F8A;TIBETAN SIGN GRU CAN RGYINGS;Lo;0;L;;;;;N;;;;;
+0F8B;TIBETAN SIGN GRU MED RGYINGS;Lo;0;L;;;;;N;;;;;
+0F8C;TIBETAN SIGN INVERTED MCHU CAN;Lo;0;L;;;;;N;;;;;
+0F8D;TIBETAN SUBJOINED SIGN LCE TSA CAN;Mn;0;NSM;;;;;N;;;;;
+0F8E;TIBETAN SUBJOINED SIGN MCHU CAN;Mn;0;NSM;;;;;N;;;;;
+0F8F;TIBETAN SUBJOINED SIGN INVERTED MCHU CAN;Mn;0;NSM;;;;;N;;;;;
+0F90;TIBETAN SUBJOINED LETTER KA;Mn;0;NSM;;;;;N;;;;;
+0F91;TIBETAN SUBJOINED LETTER KHA;Mn;0;NSM;;;;;N;;;;;
+0F92;TIBETAN SUBJOINED LETTER GA;Mn;0;NSM;;;;;N;;;;;
+0F93;TIBETAN SUBJOINED LETTER GHA;Mn;0;NSM;0F92 0FB7;;;;N;;;;;
+0F94;TIBETAN SUBJOINED LETTER NGA;Mn;0;NSM;;;;;N;;;;;
+0F95;TIBETAN SUBJOINED LETTER CA;Mn;0;NSM;;;;;N;;;;;
+0F96;TIBETAN SUBJOINED LETTER CHA;Mn;0;NSM;;;;;N;;;;;
+0F97;TIBETAN SUBJOINED LETTER JA;Mn;0;NSM;;;;;N;;;;;
+0F99;TIBETAN SUBJOINED LETTER NYA;Mn;0;NSM;;;;;N;;;;;
+0F9A;TIBETAN SUBJOINED LETTER TTA;Mn;0;NSM;;;;;N;;;;;
+0F9B;TIBETAN SUBJOINED LETTER TTHA;Mn;0;NSM;;;;;N;;;;;
+0F9C;TIBETAN SUBJOINED LETTER DDA;Mn;0;NSM;;;;;N;;;;;
+0F9D;TIBETAN SUBJOINED LETTER DDHA;Mn;0;NSM;0F9C 0FB7;;;;N;;;;;
+0F9E;TIBETAN SUBJOINED LETTER NNA;Mn;0;NSM;;;;;N;;;;;
+0F9F;TIBETAN SUBJOINED LETTER TA;Mn;0;NSM;;;;;N;;;;;
+0FA0;TIBETAN SUBJOINED LETTER THA;Mn;0;NSM;;;;;N;;;;;
+0FA1;TIBETAN SUBJOINED LETTER DA;Mn;0;NSM;;;;;N;;;;;
+0FA2;TIBETAN SUBJOINED LETTER DHA;Mn;0;NSM;0FA1 0FB7;;;;N;;;;;
+0FA3;TIBETAN SUBJOINED LETTER NA;Mn;0;NSM;;;;;N;;;;;
+0FA4;TIBETAN SUBJOINED LETTER PA;Mn;0;NSM;;;;;N;;;;;
+0FA5;TIBETAN SUBJOINED LETTER PHA;Mn;0;NSM;;;;;N;;;;;
+0FA6;TIBETAN SUBJOINED LETTER BA;Mn;0;NSM;;;;;N;;;;;
+0FA7;TIBETAN SUBJOINED LETTER BHA;Mn;0;NSM;0FA6 0FB7;;;;N;;;;;
+0FA8;TIBETAN SUBJOINED LETTER MA;Mn;0;NSM;;;;;N;;;;;
+0FA9;TIBETAN SUBJOINED LETTER TSA;Mn;0;NSM;;;;;N;;;;;
+0FAA;TIBETAN SUBJOINED LETTER TSHA;Mn;0;NSM;;;;;N;;;;;
+0FAB;TIBETAN SUBJOINED LETTER DZA;Mn;0;NSM;;;;;N;;;;;
+0FAC;TIBETAN SUBJOINED LETTER DZHA;Mn;0;NSM;0FAB 0FB7;;;;N;;;;;
+0FAD;TIBETAN SUBJOINED LETTER WA;Mn;0;NSM;;;;;N;;;;;
+0FAE;TIBETAN SUBJOINED LETTER ZHA;Mn;0;NSM;;;;;N;;;;;
+0FAF;TIBETAN SUBJOINED LETTER ZA;Mn;0;NSM;;;;;N;;;;;
+0FB0;TIBETAN SUBJOINED LETTER -A;Mn;0;NSM;;;;;N;;;;;
+0FB1;TIBETAN SUBJOINED LETTER YA;Mn;0;NSM;;;;;N;;;;;
+0FB2;TIBETAN SUBJOINED LETTER RA;Mn;0;NSM;;;;;N;;;;;
+0FB3;TIBETAN SUBJOINED LETTER LA;Mn;0;NSM;;;;;N;;;;;
+0FB4;TIBETAN SUBJOINED LETTER SHA;Mn;0;NSM;;;;;N;;;;;
+0FB5;TIBETAN SUBJOINED LETTER SSA;Mn;0;NSM;;;;;N;;;;;
+0FB6;TIBETAN SUBJOINED LETTER SA;Mn;0;NSM;;;;;N;;;;;
+0FB7;TIBETAN SUBJOINED LETTER HA;Mn;0;NSM;;;;;N;;;;;
+0FB8;TIBETAN SUBJOINED LETTER A;Mn;0;NSM;;;;;N;;;;;
+0FB9;TIBETAN SUBJOINED LETTER KSSA;Mn;0;NSM;0F90 0FB5;;;;N;;;;;
+0FBA;TIBETAN SUBJOINED LETTER FIXED-FORM WA;Mn;0;NSM;;;;;N;;;;;
+0FBB;TIBETAN SUBJOINED LETTER FIXED-FORM YA;Mn;0;NSM;;;;;N;;;;;
+0FBC;TIBETAN SUBJOINED LETTER FIXED-FORM RA;Mn;0;NSM;;;;;N;;;;;
+0FBE;TIBETAN KU RU KHA;So;0;L;;;;;N;;;;;
+0FBF;TIBETAN KU RU KHA BZHI MIG CAN;So;0;L;;;;;N;;;;;
+0FC0;TIBETAN CANTILLATION SIGN HEAVY BEAT;So;0;L;;;;;N;;;;;
+0FC1;TIBETAN CANTILLATION SIGN LIGHT BEAT;So;0;L;;;;;N;;;;;
+0FC2;TIBETAN CANTILLATION SIGN CANG TE-U;So;0;L;;;;;N;;;;;
+0FC3;TIBETAN CANTILLATION SIGN SBUB -CHAL;So;0;L;;;;;N;;;;;
+0FC4;TIBETAN SYMBOL DRIL BU;So;0;L;;;;;N;;;;;
+0FC5;TIBETAN SYMBOL RDO RJE;So;0;L;;;;;N;;;;;
+0FC6;TIBETAN SYMBOL PADMA GDAN;Mn;220;NSM;;;;;N;;;;;
+0FC7;TIBETAN SYMBOL RDO RJE RGYA GRAM;So;0;L;;;;;N;;;;;
+0FC8;TIBETAN SYMBOL PHUR PA;So;0;L;;;;;N;;;;;
+0FC9;TIBETAN SYMBOL NOR BU;So;0;L;;;;;N;;;;;
+0FCA;TIBETAN SYMBOL NOR BU NYIS -KHYIL;So;0;L;;;;;N;;;;;
+0FCB;TIBETAN SYMBOL NOR BU GSUM -KHYIL;So;0;L;;;;;N;;;;;
+0FCC;TIBETAN SYMBOL NOR BU BZHI -KHYIL;So;0;L;;;;;N;;;;;
+0FCE;TIBETAN SIGN RDEL NAG RDEL DKAR;So;0;L;;;;;N;;;;;
+0FCF;TIBETAN SIGN RDEL NAG GSUM;So;0;L;;;;;N;;;;;
+0FD0;TIBETAN MARK BSKA- SHOG GI MGO RGYAN;Po;0;L;;;;;N;;;;;
+0FD1;TIBETAN MARK MNYAM YIG GI MGO RGYAN;Po;0;L;;;;;N;;;;;
+0FD2;TIBETAN MARK NYIS TSHEG;Po;0;L;;;;;N;;;;;
+0FD3;TIBETAN MARK INITIAL BRDA RNYING YIG MGO MDUN MA;Po;0;L;;;;;N;;;;;
+0FD4;TIBETAN MARK CLOSING BRDA RNYING YIG MGO SGAB MA;Po;0;L;;;;;N;;;;;
+0FD5;RIGHT-FACING SVASTI SIGN;So;0;L;;;;;N;;;;;
+0FD6;LEFT-FACING SVASTI SIGN;So;0;L;;;;;N;;;;;
+0FD7;RIGHT-FACING SVASTI SIGN WITH DOTS;So;0;L;;;;;N;;;;;
+0FD8;LEFT-FACING SVASTI SIGN WITH DOTS;So;0;L;;;;;N;;;;;
+0FD9;TIBETAN MARK LEADING MCHAN RTAGS;Po;0;L;;;;;N;;;;;
+0FDA;TIBETAN MARK TRAILING MCHAN RTAGS;Po;0;L;;;;;N;;;;;
+1000;MYANMAR LETTER KA;Lo;0;L;;;;;N;;;;;
+1001;MYANMAR LETTER KHA;Lo;0;L;;;;;N;;;;;
+1002;MYANMAR LETTER GA;Lo;0;L;;;;;N;;;;;
+1003;MYANMAR LETTER GHA;Lo;0;L;;;;;N;;;;;
+1004;MYANMAR LETTER NGA;Lo;0;L;;;;;N;;;;;
+1005;MYANMAR LETTER CA;Lo;0;L;;;;;N;;;;;
+1006;MYANMAR LETTER CHA;Lo;0;L;;;;;N;;;;;
+1007;MYANMAR LETTER JA;Lo;0;L;;;;;N;;;;;
+1008;MYANMAR LETTER JHA;Lo;0;L;;;;;N;;;;;
+1009;MYANMAR LETTER NYA;Lo;0;L;;;;;N;;;;;
+100A;MYANMAR LETTER NNYA;Lo;0;L;;;;;N;;;;;
+100B;MYANMAR LETTER TTA;Lo;0;L;;;;;N;;;;;
+100C;MYANMAR LETTER TTHA;Lo;0;L;;;;;N;;;;;
+100D;MYANMAR LETTER DDA;Lo;0;L;;;;;N;;;;;
+100E;MYANMAR LETTER DDHA;Lo;0;L;;;;;N;;;;;
+100F;MYANMAR LETTER NNA;Lo;0;L;;;;;N;;;;;
+1010;MYANMAR LETTER TA;Lo;0;L;;;;;N;;;;;
+1011;MYANMAR LETTER THA;Lo;0;L;;;;;N;;;;;
+1012;MYANMAR LETTER DA;Lo;0;L;;;;;N;;;;;
+1013;MYANMAR LETTER DHA;Lo;0;L;;;;;N;;;;;
+1014;MYANMAR LETTER NA;Lo;0;L;;;;;N;;;;;
+1015;MYANMAR LETTER PA;Lo;0;L;;;;;N;;;;;
+1016;MYANMAR LETTER PHA;Lo;0;L;;;;;N;;;;;
+1017;MYANMAR LETTER BA;Lo;0;L;;;;;N;;;;;
+1018;MYANMAR LETTER BHA;Lo;0;L;;;;;N;;;;;
+1019;MYANMAR LETTER MA;Lo;0;L;;;;;N;;;;;
+101A;MYANMAR LETTER YA;Lo;0;L;;;;;N;;;;;
+101B;MYANMAR LETTER RA;Lo;0;L;;;;;N;;;;;
+101C;MYANMAR LETTER LA;Lo;0;L;;;;;N;;;;;
+101D;MYANMAR LETTER WA;Lo;0;L;;;;;N;;;;;
+101E;MYANMAR LETTER SA;Lo;0;L;;;;;N;;;;;
+101F;MYANMAR LETTER HA;Lo;0;L;;;;;N;;;;;
+1020;MYANMAR LETTER LLA;Lo;0;L;;;;;N;;;;;
+1021;MYANMAR LETTER A;Lo;0;L;;;;;N;;;;;
+1022;MYANMAR LETTER SHAN A;Lo;0;L;;;;;N;;;;;
+1023;MYANMAR LETTER I;Lo;0;L;;;;;N;;;;;
+1024;MYANMAR LETTER II;Lo;0;L;;;;;N;;;;;
+1025;MYANMAR LETTER U;Lo;0;L;;;;;N;;;;;
+1026;MYANMAR LETTER UU;Lo;0;L;1025 102E;;;;N;;;;;
+1027;MYANMAR LETTER E;Lo;0;L;;;;;N;;;;;
+1028;MYANMAR LETTER MON E;Lo;0;L;;;;;N;;;;;
+1029;MYANMAR LETTER O;Lo;0;L;;;;;N;;;;;
+102A;MYANMAR LETTER AU;Lo;0;L;;;;;N;;;;;
+102B;MYANMAR VOWEL SIGN TALL AA;Mc;0;L;;;;;N;;;;;
+102C;MYANMAR VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+102D;MYANMAR VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+102E;MYANMAR VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+102F;MYANMAR VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1030;MYANMAR VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+1031;MYANMAR VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+1032;MYANMAR VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+1033;MYANMAR VOWEL SIGN MON II;Mn;0;NSM;;;;;N;;;;;
+1034;MYANMAR VOWEL SIGN MON O;Mn;0;NSM;;;;;N;;;;;
+1035;MYANMAR VOWEL SIGN E ABOVE;Mn;0;NSM;;;;;N;;;;;
+1036;MYANMAR SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+1037;MYANMAR SIGN DOT BELOW;Mn;7;NSM;;;;;N;;;;;
+1038;MYANMAR SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+1039;MYANMAR SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+103A;MYANMAR SIGN ASAT;Mn;9;NSM;;;;;N;;;;;
+103B;MYANMAR CONSONANT SIGN MEDIAL YA;Mc;0;L;;;;;N;;;;;
+103C;MYANMAR CONSONANT SIGN MEDIAL RA;Mc;0;L;;;;;N;;;;;
+103D;MYANMAR CONSONANT SIGN MEDIAL WA;Mn;0;NSM;;;;;N;;;;;
+103E;MYANMAR CONSONANT SIGN MEDIAL HA;Mn;0;NSM;;;;;N;;;;;
+103F;MYANMAR LETTER GREAT SA;Lo;0;L;;;;;N;;;;;
+1040;MYANMAR DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1041;MYANMAR DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1042;MYANMAR DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1043;MYANMAR DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1044;MYANMAR DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1045;MYANMAR DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1046;MYANMAR DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1047;MYANMAR DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1048;MYANMAR DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1049;MYANMAR DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+104A;MYANMAR SIGN LITTLE SECTION;Po;0;L;;;;;N;;;;;
+104B;MYANMAR SIGN SECTION;Po;0;L;;;;;N;;;;;
+104C;MYANMAR SYMBOL LOCATIVE;Po;0;L;;;;;N;;;;;
+104D;MYANMAR SYMBOL COMPLETED;Po;0;L;;;;;N;;;;;
+104E;MYANMAR SYMBOL AFOREMENTIONED;Po;0;L;;;;;N;;;;;
+104F;MYANMAR SYMBOL GENITIVE;Po;0;L;;;;;N;;;;;
+1050;MYANMAR LETTER SHA;Lo;0;L;;;;;N;;;;;
+1051;MYANMAR LETTER SSA;Lo;0;L;;;;;N;;;;;
+1052;MYANMAR LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+1053;MYANMAR LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+1054;MYANMAR LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+1055;MYANMAR LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+1056;MYANMAR VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;;
+1057;MYANMAR VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;;
+1058;MYANMAR VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+1059;MYANMAR VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+105A;MYANMAR LETTER MON NGA;Lo;0;L;;;;;N;;;;;
+105B;MYANMAR LETTER MON JHA;Lo;0;L;;;;;N;;;;;
+105C;MYANMAR LETTER MON BBA;Lo;0;L;;;;;N;;;;;
+105D;MYANMAR LETTER MON BBE;Lo;0;L;;;;;N;;;;;
+105E;MYANMAR CONSONANT SIGN MON MEDIAL NA;Mn;0;NSM;;;;;N;;;;;
+105F;MYANMAR CONSONANT SIGN MON MEDIAL MA;Mn;0;NSM;;;;;N;;;;;
+1060;MYANMAR CONSONANT SIGN MON MEDIAL LA;Mn;0;NSM;;;;;N;;;;;
+1061;MYANMAR LETTER SGAW KAREN SHA;Lo;0;L;;;;;N;;;;;
+1062;MYANMAR VOWEL SIGN SGAW KAREN EU;Mc;0;L;;;;;N;;;;;
+1063;MYANMAR TONE MARK SGAW KAREN HATHI;Mc;0;L;;;;;N;;;;;
+1064;MYANMAR TONE MARK SGAW KAREN KE PHO;Mc;0;L;;;;;N;;;;;
+1065;MYANMAR LETTER WESTERN PWO KAREN THA;Lo;0;L;;;;;N;;;;;
+1066;MYANMAR LETTER WESTERN PWO KAREN PWA;Lo;0;L;;;;;N;;;;;
+1067;MYANMAR VOWEL SIGN WESTERN PWO KAREN EU;Mc;0;L;;;;;N;;;;;
+1068;MYANMAR VOWEL SIGN WESTERN PWO KAREN UE;Mc;0;L;;;;;N;;;;;
+1069;MYANMAR SIGN WESTERN PWO KAREN TONE-1;Mc;0;L;;;;;N;;;;;
+106A;MYANMAR SIGN WESTERN PWO KAREN TONE-2;Mc;0;L;;;;;N;;;;;
+106B;MYANMAR SIGN WESTERN PWO KAREN TONE-3;Mc;0;L;;;;;N;;;;;
+106C;MYANMAR SIGN WESTERN PWO KAREN TONE-4;Mc;0;L;;;;;N;;;;;
+106D;MYANMAR SIGN WESTERN PWO KAREN TONE-5;Mc;0;L;;;;;N;;;;;
+106E;MYANMAR LETTER EASTERN PWO KAREN NNA;Lo;0;L;;;;;N;;;;;
+106F;MYANMAR LETTER EASTERN PWO KAREN YWA;Lo;0;L;;;;;N;;;;;
+1070;MYANMAR LETTER EASTERN PWO KAREN GHWA;Lo;0;L;;;;;N;;;;;
+1071;MYANMAR VOWEL SIGN GEBA KAREN I;Mn;0;NSM;;;;;N;;;;;
+1072;MYANMAR VOWEL SIGN KAYAH OE;Mn;0;NSM;;;;;N;;;;;
+1073;MYANMAR VOWEL SIGN KAYAH U;Mn;0;NSM;;;;;N;;;;;
+1074;MYANMAR VOWEL SIGN KAYAH EE;Mn;0;NSM;;;;;N;;;;;
+1075;MYANMAR LETTER SHAN KA;Lo;0;L;;;;;N;;;;;
+1076;MYANMAR LETTER SHAN KHA;Lo;0;L;;;;;N;;;;;
+1077;MYANMAR LETTER SHAN GA;Lo;0;L;;;;;N;;;;;
+1078;MYANMAR LETTER SHAN CA;Lo;0;L;;;;;N;;;;;
+1079;MYANMAR LETTER SHAN ZA;Lo;0;L;;;;;N;;;;;
+107A;MYANMAR LETTER SHAN NYA;Lo;0;L;;;;;N;;;;;
+107B;MYANMAR LETTER SHAN DA;Lo;0;L;;;;;N;;;;;
+107C;MYANMAR LETTER SHAN NA;Lo;0;L;;;;;N;;;;;
+107D;MYANMAR LETTER SHAN PHA;Lo;0;L;;;;;N;;;;;
+107E;MYANMAR LETTER SHAN FA;Lo;0;L;;;;;N;;;;;
+107F;MYANMAR LETTER SHAN BA;Lo;0;L;;;;;N;;;;;
+1080;MYANMAR LETTER SHAN THA;Lo;0;L;;;;;N;;;;;
+1081;MYANMAR LETTER SHAN HA;Lo;0;L;;;;;N;;;;;
+1082;MYANMAR CONSONANT SIGN SHAN MEDIAL WA;Mn;0;NSM;;;;;N;;;;;
+1083;MYANMAR VOWEL SIGN SHAN AA;Mc;0;L;;;;;N;;;;;
+1084;MYANMAR VOWEL SIGN SHAN E;Mc;0;L;;;;;N;;;;;
+1085;MYANMAR VOWEL SIGN SHAN E ABOVE;Mn;0;NSM;;;;;N;;;;;
+1086;MYANMAR VOWEL SIGN SHAN FINAL Y;Mn;0;NSM;;;;;N;;;;;
+1087;MYANMAR SIGN SHAN TONE-2;Mc;0;L;;;;;N;;;;;
+1088;MYANMAR SIGN SHAN TONE-3;Mc;0;L;;;;;N;;;;;
+1089;MYANMAR SIGN SHAN TONE-5;Mc;0;L;;;;;N;;;;;
+108A;MYANMAR SIGN SHAN TONE-6;Mc;0;L;;;;;N;;;;;
+108B;MYANMAR SIGN SHAN COUNCIL TONE-2;Mc;0;L;;;;;N;;;;;
+108C;MYANMAR SIGN SHAN COUNCIL TONE-3;Mc;0;L;;;;;N;;;;;
+108D;MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE;Mn;220;NSM;;;;;N;;;;;
+108E;MYANMAR LETTER RUMAI PALAUNG FA;Lo;0;L;;;;;N;;;;;
+108F;MYANMAR SIGN RUMAI PALAUNG TONE-5;Mc;0;L;;;;;N;;;;;
+1090;MYANMAR SHAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1091;MYANMAR SHAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1092;MYANMAR SHAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1093;MYANMAR SHAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1094;MYANMAR SHAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1095;MYANMAR SHAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1096;MYANMAR SHAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1097;MYANMAR SHAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1098;MYANMAR SHAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1099;MYANMAR SHAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+109A;MYANMAR SIGN KHAMTI TONE-1;Mc;0;L;;;;;N;;;;;
+109B;MYANMAR SIGN KHAMTI TONE-3;Mc;0;L;;;;;N;;;;;
+109C;MYANMAR VOWEL SIGN AITON A;Mc;0;L;;;;;N;;;;;
+109D;MYANMAR VOWEL SIGN AITON AI;Mn;0;NSM;;;;;N;;;;;
+109E;MYANMAR SYMBOL SHAN ONE;So;0;L;;;;;N;;;;;
+109F;MYANMAR SYMBOL SHAN EXCLAMATION;So;0;L;;;;;N;;;;;
+10A0;GEORGIAN CAPITAL LETTER AN;Lu;0;L;;;;;N;;;;2D00;
+10A1;GEORGIAN CAPITAL LETTER BAN;Lu;0;L;;;;;N;;;;2D01;
+10A2;GEORGIAN CAPITAL LETTER GAN;Lu;0;L;;;;;N;;;;2D02;
+10A3;GEORGIAN CAPITAL LETTER DON;Lu;0;L;;;;;N;;;;2D03;
+10A4;GEORGIAN CAPITAL LETTER EN;Lu;0;L;;;;;N;;;;2D04;
+10A5;GEORGIAN CAPITAL LETTER VIN;Lu;0;L;;;;;N;;;;2D05;
+10A6;GEORGIAN CAPITAL LETTER ZEN;Lu;0;L;;;;;N;;;;2D06;
+10A7;GEORGIAN CAPITAL LETTER TAN;Lu;0;L;;;;;N;;;;2D07;
+10A8;GEORGIAN CAPITAL LETTER IN;Lu;0;L;;;;;N;;;;2D08;
+10A9;GEORGIAN CAPITAL LETTER KAN;Lu;0;L;;;;;N;;;;2D09;
+10AA;GEORGIAN CAPITAL LETTER LAS;Lu;0;L;;;;;N;;;;2D0A;
+10AB;GEORGIAN CAPITAL LETTER MAN;Lu;0;L;;;;;N;;;;2D0B;
+10AC;GEORGIAN CAPITAL LETTER NAR;Lu;0;L;;;;;N;;;;2D0C;
+10AD;GEORGIAN CAPITAL LETTER ON;Lu;0;L;;;;;N;;;;2D0D;
+10AE;GEORGIAN CAPITAL LETTER PAR;Lu;0;L;;;;;N;;;;2D0E;
+10AF;GEORGIAN CAPITAL LETTER ZHAR;Lu;0;L;;;;;N;;;;2D0F;
+10B0;GEORGIAN CAPITAL LETTER RAE;Lu;0;L;;;;;N;;;;2D10;
+10B1;GEORGIAN CAPITAL LETTER SAN;Lu;0;L;;;;;N;;;;2D11;
+10B2;GEORGIAN CAPITAL LETTER TAR;Lu;0;L;;;;;N;;;;2D12;
+10B3;GEORGIAN CAPITAL LETTER UN;Lu;0;L;;;;;N;;;;2D13;
+10B4;GEORGIAN CAPITAL LETTER PHAR;Lu;0;L;;;;;N;;;;2D14;
+10B5;GEORGIAN CAPITAL LETTER KHAR;Lu;0;L;;;;;N;;;;2D15;
+10B6;GEORGIAN CAPITAL LETTER GHAN;Lu;0;L;;;;;N;;;;2D16;
+10B7;GEORGIAN CAPITAL LETTER QAR;Lu;0;L;;;;;N;;;;2D17;
+10B8;GEORGIAN CAPITAL LETTER SHIN;Lu;0;L;;;;;N;;;;2D18;
+10B9;GEORGIAN CAPITAL LETTER CHIN;Lu;0;L;;;;;N;;;;2D19;
+10BA;GEORGIAN CAPITAL LETTER CAN;Lu;0;L;;;;;N;;;;2D1A;
+10BB;GEORGIAN CAPITAL LETTER JIL;Lu;0;L;;;;;N;;;;2D1B;
+10BC;GEORGIAN CAPITAL LETTER CIL;Lu;0;L;;;;;N;;;;2D1C;
+10BD;GEORGIAN CAPITAL LETTER CHAR;Lu;0;L;;;;;N;;;;2D1D;
+10BE;GEORGIAN CAPITAL LETTER XAN;Lu;0;L;;;;;N;;;;2D1E;
+10BF;GEORGIAN CAPITAL LETTER JHAN;Lu;0;L;;;;;N;;;;2D1F;
+10C0;GEORGIAN CAPITAL LETTER HAE;Lu;0;L;;;;;N;;;;2D20;
+10C1;GEORGIAN CAPITAL LETTER HE;Lu;0;L;;;;;N;;;;2D21;
+10C2;GEORGIAN CAPITAL LETTER HIE;Lu;0;L;;;;;N;;;;2D22;
+10C3;GEORGIAN CAPITAL LETTER WE;Lu;0;L;;;;;N;;;;2D23;
+10C4;GEORGIAN CAPITAL LETTER HAR;Lu;0;L;;;;;N;;;;2D24;
+10C5;GEORGIAN CAPITAL LETTER HOE;Lu;0;L;;;;;N;;;;2D25;
+10C7;GEORGIAN CAPITAL LETTER YN;Lu;0;L;;;;;N;;;;2D27;
+10CD;GEORGIAN CAPITAL LETTER AEN;Lu;0;L;;;;;N;;;;2D2D;
+10D0;GEORGIAN LETTER AN;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER AN;;1C90;;10D0
+10D1;GEORGIAN LETTER BAN;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER BAN;;1C91;;10D1
+10D2;GEORGIAN LETTER GAN;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER GAN;;1C92;;10D2
+10D3;GEORGIAN LETTER DON;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER DON;;1C93;;10D3
+10D4;GEORGIAN LETTER EN;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER EN;;1C94;;10D4
+10D5;GEORGIAN LETTER VIN;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER VIN;;1C95;;10D5
+10D6;GEORGIAN LETTER ZEN;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER ZEN;;1C96;;10D6
+10D7;GEORGIAN LETTER TAN;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER TAN;;1C97;;10D7
+10D8;GEORGIAN LETTER IN;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER IN;;1C98;;10D8
+10D9;GEORGIAN LETTER KAN;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER KAN;;1C99;;10D9
+10DA;GEORGIAN LETTER LAS;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER LAS;;1C9A;;10DA
+10DB;GEORGIAN LETTER MAN;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER MAN;;1C9B;;10DB
+10DC;GEORGIAN LETTER NAR;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER NAR;;1C9C;;10DC
+10DD;GEORGIAN LETTER ON;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER ON;;1C9D;;10DD
+10DE;GEORGIAN LETTER PAR;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER PAR;;1C9E;;10DE
+10DF;GEORGIAN LETTER ZHAR;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER ZHAR;;1C9F;;10DF
+10E0;GEORGIAN LETTER RAE;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER RAE;;1CA0;;10E0
+10E1;GEORGIAN LETTER SAN;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER SAN;;1CA1;;10E1
+10E2;GEORGIAN LETTER TAR;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER TAR;;1CA2;;10E2
+10E3;GEORGIAN LETTER UN;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER UN;;1CA3;;10E3
+10E4;GEORGIAN LETTER PHAR;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER PHAR;;1CA4;;10E4
+10E5;GEORGIAN LETTER KHAR;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER KHAR;;1CA5;;10E5
+10E6;GEORGIAN LETTER GHAN;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER GHAN;;1CA6;;10E6
+10E7;GEORGIAN LETTER QAR;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER QAR;;1CA7;;10E7
+10E8;GEORGIAN LETTER SHIN;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER SHIN;;1CA8;;10E8
+10E9;GEORGIAN LETTER CHIN;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER CHIN;;1CA9;;10E9
+10EA;GEORGIAN LETTER CAN;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER CAN;;1CAA;;10EA
+10EB;GEORGIAN LETTER JIL;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER JIL;;1CAB;;10EB
+10EC;GEORGIAN LETTER CIL;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER CIL;;1CAC;;10EC
+10ED;GEORGIAN LETTER CHAR;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER CHAR;;1CAD;;10ED
+10EE;GEORGIAN LETTER XAN;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER XAN;;1CAE;;10EE
+10EF;GEORGIAN LETTER JHAN;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER JHAN;;1CAF;;10EF
+10F0;GEORGIAN LETTER HAE;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER HAE;;1CB0;;10F0
+10F1;GEORGIAN LETTER HE;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER HE;;1CB1;;10F1
+10F2;GEORGIAN LETTER HIE;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER HIE;;1CB2;;10F2
+10F3;GEORGIAN LETTER WE;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER WE;;1CB3;;10F3
+10F4;GEORGIAN LETTER HAR;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER HAR;;1CB4;;10F4
+10F5;GEORGIAN LETTER HOE;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER HOE;;1CB5;;10F5
+10F6;GEORGIAN LETTER FI;Ll;0;L;;;;;N;GEORGIAN SMALL LETTER FI;;1CB6;;10F6
+10F7;GEORGIAN LETTER YN;Ll;0;L;;;;;N;;;1CB7;;10F7
+10F8;GEORGIAN LETTER ELIFI;Ll;0;L;;;;;N;;;1CB8;;10F8
+10F9;GEORGIAN LETTER TURNED GAN;Ll;0;L;;;;;N;;;1CB9;;10F9
+10FA;GEORGIAN LETTER AIN;Ll;0;L;;;;;N;;;1CBA;;10FA
+10FB;GEORGIAN PARAGRAPH SEPARATOR;Po;0;L;;;;;N;;;;;
+10FC;MODIFIER LETTER GEORGIAN NAR;Lm;0;L;<super> 10DC;;;;N;;;;;
+10FD;GEORGIAN LETTER AEN;Ll;0;L;;;;;N;;;1CBD;;10FD
+10FE;GEORGIAN LETTER HARD SIGN;Ll;0;L;;;;;N;;;1CBE;;10FE
+10FF;GEORGIAN LETTER LABIAL SIGN;Ll;0;L;;;;;N;;;1CBF;;10FF
+1100;HANGUL CHOSEONG KIYEOK;Lo;0;L;;;;;N;;;;;
+1101;HANGUL CHOSEONG SSANGKIYEOK;Lo;0;L;;;;;N;;;;;
+1102;HANGUL CHOSEONG NIEUN;Lo;0;L;;;;;N;;;;;
+1103;HANGUL CHOSEONG TIKEUT;Lo;0;L;;;;;N;;;;;
+1104;HANGUL CHOSEONG SSANGTIKEUT;Lo;0;L;;;;;N;;;;;
+1105;HANGUL CHOSEONG RIEUL;Lo;0;L;;;;;N;;;;;
+1106;HANGUL CHOSEONG MIEUM;Lo;0;L;;;;;N;;;;;
+1107;HANGUL CHOSEONG PIEUP;Lo;0;L;;;;;N;;;;;
+1108;HANGUL CHOSEONG SSANGPIEUP;Lo;0;L;;;;;N;;;;;
+1109;HANGUL CHOSEONG SIOS;Lo;0;L;;;;;N;;;;;
+110A;HANGUL CHOSEONG SSANGSIOS;Lo;0;L;;;;;N;;;;;
+110B;HANGUL CHOSEONG IEUNG;Lo;0;L;;;;;N;;;;;
+110C;HANGUL CHOSEONG CIEUC;Lo;0;L;;;;;N;;;;;
+110D;HANGUL CHOSEONG SSANGCIEUC;Lo;0;L;;;;;N;;;;;
+110E;HANGUL CHOSEONG CHIEUCH;Lo;0;L;;;;;N;;;;;
+110F;HANGUL CHOSEONG KHIEUKH;Lo;0;L;;;;;N;;;;;
+1110;HANGUL CHOSEONG THIEUTH;Lo;0;L;;;;;N;;;;;
+1111;HANGUL CHOSEONG PHIEUPH;Lo;0;L;;;;;N;;;;;
+1112;HANGUL CHOSEONG HIEUH;Lo;0;L;;;;;N;;;;;
+1113;HANGUL CHOSEONG NIEUN-KIYEOK;Lo;0;L;;;;;N;;;;;
+1114;HANGUL CHOSEONG SSANGNIEUN;Lo;0;L;;;;;N;;;;;
+1115;HANGUL CHOSEONG NIEUN-TIKEUT;Lo;0;L;;;;;N;;;;;
+1116;HANGUL CHOSEONG NIEUN-PIEUP;Lo;0;L;;;;;N;;;;;
+1117;HANGUL CHOSEONG TIKEUT-KIYEOK;Lo;0;L;;;;;N;;;;;
+1118;HANGUL CHOSEONG RIEUL-NIEUN;Lo;0;L;;;;;N;;;;;
+1119;HANGUL CHOSEONG SSANGRIEUL;Lo;0;L;;;;;N;;;;;
+111A;HANGUL CHOSEONG RIEUL-HIEUH;Lo;0;L;;;;;N;;;;;
+111B;HANGUL CHOSEONG KAPYEOUNRIEUL;Lo;0;L;;;;;N;;;;;
+111C;HANGUL CHOSEONG MIEUM-PIEUP;Lo;0;L;;;;;N;;;;;
+111D;HANGUL CHOSEONG KAPYEOUNMIEUM;Lo;0;L;;;;;N;;;;;
+111E;HANGUL CHOSEONG PIEUP-KIYEOK;Lo;0;L;;;;;N;;;;;
+111F;HANGUL CHOSEONG PIEUP-NIEUN;Lo;0;L;;;;;N;;;;;
+1120;HANGUL CHOSEONG PIEUP-TIKEUT;Lo;0;L;;;;;N;;;;;
+1121;HANGUL CHOSEONG PIEUP-SIOS;Lo;0;L;;;;;N;;;;;
+1122;HANGUL CHOSEONG PIEUP-SIOS-KIYEOK;Lo;0;L;;;;;N;;;;;
+1123;HANGUL CHOSEONG PIEUP-SIOS-TIKEUT;Lo;0;L;;;;;N;;;;;
+1124;HANGUL CHOSEONG PIEUP-SIOS-PIEUP;Lo;0;L;;;;;N;;;;;
+1125;HANGUL CHOSEONG PIEUP-SSANGSIOS;Lo;0;L;;;;;N;;;;;
+1126;HANGUL CHOSEONG PIEUP-SIOS-CIEUC;Lo;0;L;;;;;N;;;;;
+1127;HANGUL CHOSEONG PIEUP-CIEUC;Lo;0;L;;;;;N;;;;;
+1128;HANGUL CHOSEONG PIEUP-CHIEUCH;Lo;0;L;;;;;N;;;;;
+1129;HANGUL CHOSEONG PIEUP-THIEUTH;Lo;0;L;;;;;N;;;;;
+112A;HANGUL CHOSEONG PIEUP-PHIEUPH;Lo;0;L;;;;;N;;;;;
+112B;HANGUL CHOSEONG KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;;
+112C;HANGUL CHOSEONG KAPYEOUNSSANGPIEUP;Lo;0;L;;;;;N;;;;;
+112D;HANGUL CHOSEONG SIOS-KIYEOK;Lo;0;L;;;;;N;;;;;
+112E;HANGUL CHOSEONG SIOS-NIEUN;Lo;0;L;;;;;N;;;;;
+112F;HANGUL CHOSEONG SIOS-TIKEUT;Lo;0;L;;;;;N;;;;;
+1130;HANGUL CHOSEONG SIOS-RIEUL;Lo;0;L;;;;;N;;;;;
+1131;HANGUL CHOSEONG SIOS-MIEUM;Lo;0;L;;;;;N;;;;;
+1132;HANGUL CHOSEONG SIOS-PIEUP;Lo;0;L;;;;;N;;;;;
+1133;HANGUL CHOSEONG SIOS-PIEUP-KIYEOK;Lo;0;L;;;;;N;;;;;
+1134;HANGUL CHOSEONG SIOS-SSANGSIOS;Lo;0;L;;;;;N;;;;;
+1135;HANGUL CHOSEONG SIOS-IEUNG;Lo;0;L;;;;;N;;;;;
+1136;HANGUL CHOSEONG SIOS-CIEUC;Lo;0;L;;;;;N;;;;;
+1137;HANGUL CHOSEONG SIOS-CHIEUCH;Lo;0;L;;;;;N;;;;;
+1138;HANGUL CHOSEONG SIOS-KHIEUKH;Lo;0;L;;;;;N;;;;;
+1139;HANGUL CHOSEONG SIOS-THIEUTH;Lo;0;L;;;;;N;;;;;
+113A;HANGUL CHOSEONG SIOS-PHIEUPH;Lo;0;L;;;;;N;;;;;
+113B;HANGUL CHOSEONG SIOS-HIEUH;Lo;0;L;;;;;N;;;;;
+113C;HANGUL CHOSEONG CHITUEUMSIOS;Lo;0;L;;;;;N;;;;;
+113D;HANGUL CHOSEONG CHITUEUMSSANGSIOS;Lo;0;L;;;;;N;;;;;
+113E;HANGUL CHOSEONG CEONGCHIEUMSIOS;Lo;0;L;;;;;N;;;;;
+113F;HANGUL CHOSEONG CEONGCHIEUMSSANGSIOS;Lo;0;L;;;;;N;;;;;
+1140;HANGUL CHOSEONG PANSIOS;Lo;0;L;;;;;N;;;;;
+1141;HANGUL CHOSEONG IEUNG-KIYEOK;Lo;0;L;;;;;N;;;;;
+1142;HANGUL CHOSEONG IEUNG-TIKEUT;Lo;0;L;;;;;N;;;;;
+1143;HANGUL CHOSEONG IEUNG-MIEUM;Lo;0;L;;;;;N;;;;;
+1144;HANGUL CHOSEONG IEUNG-PIEUP;Lo;0;L;;;;;N;;;;;
+1145;HANGUL CHOSEONG IEUNG-SIOS;Lo;0;L;;;;;N;;;;;
+1146;HANGUL CHOSEONG IEUNG-PANSIOS;Lo;0;L;;;;;N;;;;;
+1147;HANGUL CHOSEONG SSANGIEUNG;Lo;0;L;;;;;N;;;;;
+1148;HANGUL CHOSEONG IEUNG-CIEUC;Lo;0;L;;;;;N;;;;;
+1149;HANGUL CHOSEONG IEUNG-CHIEUCH;Lo;0;L;;;;;N;;;;;
+114A;HANGUL CHOSEONG IEUNG-THIEUTH;Lo;0;L;;;;;N;;;;;
+114B;HANGUL CHOSEONG IEUNG-PHIEUPH;Lo;0;L;;;;;N;;;;;
+114C;HANGUL CHOSEONG YESIEUNG;Lo;0;L;;;;;N;;;;;
+114D;HANGUL CHOSEONG CIEUC-IEUNG;Lo;0;L;;;;;N;;;;;
+114E;HANGUL CHOSEONG CHITUEUMCIEUC;Lo;0;L;;;;;N;;;;;
+114F;HANGUL CHOSEONG CHITUEUMSSANGCIEUC;Lo;0;L;;;;;N;;;;;
+1150;HANGUL CHOSEONG CEONGCHIEUMCIEUC;Lo;0;L;;;;;N;;;;;
+1151;HANGUL CHOSEONG CEONGCHIEUMSSANGCIEUC;Lo;0;L;;;;;N;;;;;
+1152;HANGUL CHOSEONG CHIEUCH-KHIEUKH;Lo;0;L;;;;;N;;;;;
+1153;HANGUL CHOSEONG CHIEUCH-HIEUH;Lo;0;L;;;;;N;;;;;
+1154;HANGUL CHOSEONG CHITUEUMCHIEUCH;Lo;0;L;;;;;N;;;;;
+1155;HANGUL CHOSEONG CEONGCHIEUMCHIEUCH;Lo;0;L;;;;;N;;;;;
+1156;HANGUL CHOSEONG PHIEUPH-PIEUP;Lo;0;L;;;;;N;;;;;
+1157;HANGUL CHOSEONG KAPYEOUNPHIEUPH;Lo;0;L;;;;;N;;;;;
+1158;HANGUL CHOSEONG SSANGHIEUH;Lo;0;L;;;;;N;;;;;
+1159;HANGUL CHOSEONG YEORINHIEUH;Lo;0;L;;;;;N;;;;;
+115A;HANGUL CHOSEONG KIYEOK-TIKEUT;Lo;0;L;;;;;N;;;;;
+115B;HANGUL CHOSEONG NIEUN-SIOS;Lo;0;L;;;;;N;;;;;
+115C;HANGUL CHOSEONG NIEUN-CIEUC;Lo;0;L;;;;;N;;;;;
+115D;HANGUL CHOSEONG NIEUN-HIEUH;Lo;0;L;;;;;N;;;;;
+115E;HANGUL CHOSEONG TIKEUT-RIEUL;Lo;0;L;;;;;N;;;;;
+115F;HANGUL CHOSEONG FILLER;Lo;0;L;;;;;N;;;;;
+1160;HANGUL JUNGSEONG FILLER;Lo;0;L;;;;;N;;;;;
+1161;HANGUL JUNGSEONG A;Lo;0;L;;;;;N;;;;;
+1162;HANGUL JUNGSEONG AE;Lo;0;L;;;;;N;;;;;
+1163;HANGUL JUNGSEONG YA;Lo;0;L;;;;;N;;;;;
+1164;HANGUL JUNGSEONG YAE;Lo;0;L;;;;;N;;;;;
+1165;HANGUL JUNGSEONG EO;Lo;0;L;;;;;N;;;;;
+1166;HANGUL JUNGSEONG E;Lo;0;L;;;;;N;;;;;
+1167;HANGUL JUNGSEONG YEO;Lo;0;L;;;;;N;;;;;
+1168;HANGUL JUNGSEONG YE;Lo;0;L;;;;;N;;;;;
+1169;HANGUL JUNGSEONG O;Lo;0;L;;;;;N;;;;;
+116A;HANGUL JUNGSEONG WA;Lo;0;L;;;;;N;;;;;
+116B;HANGUL JUNGSEONG WAE;Lo;0;L;;;;;N;;;;;
+116C;HANGUL JUNGSEONG OE;Lo;0;L;;;;;N;;;;;
+116D;HANGUL JUNGSEONG YO;Lo;0;L;;;;;N;;;;;
+116E;HANGUL JUNGSEONG U;Lo;0;L;;;;;N;;;;;
+116F;HANGUL JUNGSEONG WEO;Lo;0;L;;;;;N;;;;;
+1170;HANGUL JUNGSEONG WE;Lo;0;L;;;;;N;;;;;
+1171;HANGUL JUNGSEONG WI;Lo;0;L;;;;;N;;;;;
+1172;HANGUL JUNGSEONG YU;Lo;0;L;;;;;N;;;;;
+1173;HANGUL JUNGSEONG EU;Lo;0;L;;;;;N;;;;;
+1174;HANGUL JUNGSEONG YI;Lo;0;L;;;;;N;;;;;
+1175;HANGUL JUNGSEONG I;Lo;0;L;;;;;N;;;;;
+1176;HANGUL JUNGSEONG A-O;Lo;0;L;;;;;N;;;;;
+1177;HANGUL JUNGSEONG A-U;Lo;0;L;;;;;N;;;;;
+1178;HANGUL JUNGSEONG YA-O;Lo;0;L;;;;;N;;;;;
+1179;HANGUL JUNGSEONG YA-YO;Lo;0;L;;;;;N;;;;;
+117A;HANGUL JUNGSEONG EO-O;Lo;0;L;;;;;N;;;;;
+117B;HANGUL JUNGSEONG EO-U;Lo;0;L;;;;;N;;;;;
+117C;HANGUL JUNGSEONG EO-EU;Lo;0;L;;;;;N;;;;;
+117D;HANGUL JUNGSEONG YEO-O;Lo;0;L;;;;;N;;;;;
+117E;HANGUL JUNGSEONG YEO-U;Lo;0;L;;;;;N;;;;;
+117F;HANGUL JUNGSEONG O-EO;Lo;0;L;;;;;N;;;;;
+1180;HANGUL JUNGSEONG O-E;Lo;0;L;;;;;N;;;;;
+1181;HANGUL JUNGSEONG O-YE;Lo;0;L;;;;;N;;;;;
+1182;HANGUL JUNGSEONG O-O;Lo;0;L;;;;;N;;;;;
+1183;HANGUL JUNGSEONG O-U;Lo;0;L;;;;;N;;;;;
+1184;HANGUL JUNGSEONG YO-YA;Lo;0;L;;;;;N;;;;;
+1185;HANGUL JUNGSEONG YO-YAE;Lo;0;L;;;;;N;;;;;
+1186;HANGUL JUNGSEONG YO-YEO;Lo;0;L;;;;;N;;;;;
+1187;HANGUL JUNGSEONG YO-O;Lo;0;L;;;;;N;;;;;
+1188;HANGUL JUNGSEONG YO-I;Lo;0;L;;;;;N;;;;;
+1189;HANGUL JUNGSEONG U-A;Lo;0;L;;;;;N;;;;;
+118A;HANGUL JUNGSEONG U-AE;Lo;0;L;;;;;N;;;;;
+118B;HANGUL JUNGSEONG U-EO-EU;Lo;0;L;;;;;N;;;;;
+118C;HANGUL JUNGSEONG U-YE;Lo;0;L;;;;;N;;;;;
+118D;HANGUL JUNGSEONG U-U;Lo;0;L;;;;;N;;;;;
+118E;HANGUL JUNGSEONG YU-A;Lo;0;L;;;;;N;;;;;
+118F;HANGUL JUNGSEONG YU-EO;Lo;0;L;;;;;N;;;;;
+1190;HANGUL JUNGSEONG YU-E;Lo;0;L;;;;;N;;;;;
+1191;HANGUL JUNGSEONG YU-YEO;Lo;0;L;;;;;N;;;;;
+1192;HANGUL JUNGSEONG YU-YE;Lo;0;L;;;;;N;;;;;
+1193;HANGUL JUNGSEONG YU-U;Lo;0;L;;;;;N;;;;;
+1194;HANGUL JUNGSEONG YU-I;Lo;0;L;;;;;N;;;;;
+1195;HANGUL JUNGSEONG EU-U;Lo;0;L;;;;;N;;;;;
+1196;HANGUL JUNGSEONG EU-EU;Lo;0;L;;;;;N;;;;;
+1197;HANGUL JUNGSEONG YI-U;Lo;0;L;;;;;N;;;;;
+1198;HANGUL JUNGSEONG I-A;Lo;0;L;;;;;N;;;;;
+1199;HANGUL JUNGSEONG I-YA;Lo;0;L;;;;;N;;;;;
+119A;HANGUL JUNGSEONG I-O;Lo;0;L;;;;;N;;;;;
+119B;HANGUL JUNGSEONG I-U;Lo;0;L;;;;;N;;;;;
+119C;HANGUL JUNGSEONG I-EU;Lo;0;L;;;;;N;;;;;
+119D;HANGUL JUNGSEONG I-ARAEA;Lo;0;L;;;;;N;;;;;
+119E;HANGUL JUNGSEONG ARAEA;Lo;0;L;;;;;N;;;;;
+119F;HANGUL JUNGSEONG ARAEA-EO;Lo;0;L;;;;;N;;;;;
+11A0;HANGUL JUNGSEONG ARAEA-U;Lo;0;L;;;;;N;;;;;
+11A1;HANGUL JUNGSEONG ARAEA-I;Lo;0;L;;;;;N;;;;;
+11A2;HANGUL JUNGSEONG SSANGARAEA;Lo;0;L;;;;;N;;;;;
+11A3;HANGUL JUNGSEONG A-EU;Lo;0;L;;;;;N;;;;;
+11A4;HANGUL JUNGSEONG YA-U;Lo;0;L;;;;;N;;;;;
+11A5;HANGUL JUNGSEONG YEO-YA;Lo;0;L;;;;;N;;;;;
+11A6;HANGUL JUNGSEONG O-YA;Lo;0;L;;;;;N;;;;;
+11A7;HANGUL JUNGSEONG O-YAE;Lo;0;L;;;;;N;;;;;
+11A8;HANGUL JONGSEONG KIYEOK;Lo;0;L;;;;;N;;;;;
+11A9;HANGUL JONGSEONG SSANGKIYEOK;Lo;0;L;;;;;N;;;;;
+11AA;HANGUL JONGSEONG KIYEOK-SIOS;Lo;0;L;;;;;N;;;;;
+11AB;HANGUL JONGSEONG NIEUN;Lo;0;L;;;;;N;;;;;
+11AC;HANGUL JONGSEONG NIEUN-CIEUC;Lo;0;L;;;;;N;;;;;
+11AD;HANGUL JONGSEONG NIEUN-HIEUH;Lo;0;L;;;;;N;;;;;
+11AE;HANGUL JONGSEONG TIKEUT;Lo;0;L;;;;;N;;;;;
+11AF;HANGUL JONGSEONG RIEUL;Lo;0;L;;;;;N;;;;;
+11B0;HANGUL JONGSEONG RIEUL-KIYEOK;Lo;0;L;;;;;N;;;;;
+11B1;HANGUL JONGSEONG RIEUL-MIEUM;Lo;0;L;;;;;N;;;;;
+11B2;HANGUL JONGSEONG RIEUL-PIEUP;Lo;0;L;;;;;N;;;;;
+11B3;HANGUL JONGSEONG RIEUL-SIOS;Lo;0;L;;;;;N;;;;;
+11B4;HANGUL JONGSEONG RIEUL-THIEUTH;Lo;0;L;;;;;N;;;;;
+11B5;HANGUL JONGSEONG RIEUL-PHIEUPH;Lo;0;L;;;;;N;;;;;
+11B6;HANGUL JONGSEONG RIEUL-HIEUH;Lo;0;L;;;;;N;;;;;
+11B7;HANGUL JONGSEONG MIEUM;Lo;0;L;;;;;N;;;;;
+11B8;HANGUL JONGSEONG PIEUP;Lo;0;L;;;;;N;;;;;
+11B9;HANGUL JONGSEONG PIEUP-SIOS;Lo;0;L;;;;;N;;;;;
+11BA;HANGUL JONGSEONG SIOS;Lo;0;L;;;;;N;;;;;
+11BB;HANGUL JONGSEONG SSANGSIOS;Lo;0;L;;;;;N;;;;;
+11BC;HANGUL JONGSEONG IEUNG;Lo;0;L;;;;;N;;;;;
+11BD;HANGUL JONGSEONG CIEUC;Lo;0;L;;;;;N;;;;;
+11BE;HANGUL JONGSEONG CHIEUCH;Lo;0;L;;;;;N;;;;;
+11BF;HANGUL JONGSEONG KHIEUKH;Lo;0;L;;;;;N;;;;;
+11C0;HANGUL JONGSEONG THIEUTH;Lo;0;L;;;;;N;;;;;
+11C1;HANGUL JONGSEONG PHIEUPH;Lo;0;L;;;;;N;;;;;
+11C2;HANGUL JONGSEONG HIEUH;Lo;0;L;;;;;N;;;;;
+11C3;HANGUL JONGSEONG KIYEOK-RIEUL;Lo;0;L;;;;;N;;;;;
+11C4;HANGUL JONGSEONG KIYEOK-SIOS-KIYEOK;Lo;0;L;;;;;N;;;;;
+11C5;HANGUL JONGSEONG NIEUN-KIYEOK;Lo;0;L;;;;;N;;;;;
+11C6;HANGUL JONGSEONG NIEUN-TIKEUT;Lo;0;L;;;;;N;;;;;
+11C7;HANGUL JONGSEONG NIEUN-SIOS;Lo;0;L;;;;;N;;;;;
+11C8;HANGUL JONGSEONG NIEUN-PANSIOS;Lo;0;L;;;;;N;;;;;
+11C9;HANGUL JONGSEONG NIEUN-THIEUTH;Lo;0;L;;;;;N;;;;;
+11CA;HANGUL JONGSEONG TIKEUT-KIYEOK;Lo;0;L;;;;;N;;;;;
+11CB;HANGUL JONGSEONG TIKEUT-RIEUL;Lo;0;L;;;;;N;;;;;
+11CC;HANGUL JONGSEONG RIEUL-KIYEOK-SIOS;Lo;0;L;;;;;N;;;;;
+11CD;HANGUL JONGSEONG RIEUL-NIEUN;Lo;0;L;;;;;N;;;;;
+11CE;HANGUL JONGSEONG RIEUL-TIKEUT;Lo;0;L;;;;;N;;;;;
+11CF;HANGUL JONGSEONG RIEUL-TIKEUT-HIEUH;Lo;0;L;;;;;N;;;;;
+11D0;HANGUL JONGSEONG SSANGRIEUL;Lo;0;L;;;;;N;;;;;
+11D1;HANGUL JONGSEONG RIEUL-MIEUM-KIYEOK;Lo;0;L;;;;;N;;;;;
+11D2;HANGUL JONGSEONG RIEUL-MIEUM-SIOS;Lo;0;L;;;;;N;;;;;
+11D3;HANGUL JONGSEONG RIEUL-PIEUP-SIOS;Lo;0;L;;;;;N;;;;;
+11D4;HANGUL JONGSEONG RIEUL-PIEUP-HIEUH;Lo;0;L;;;;;N;;;;;
+11D5;HANGUL JONGSEONG RIEUL-KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;;
+11D6;HANGUL JONGSEONG RIEUL-SSANGSIOS;Lo;0;L;;;;;N;;;;;
+11D7;HANGUL JONGSEONG RIEUL-PANSIOS;Lo;0;L;;;;;N;;;;;
+11D8;HANGUL JONGSEONG RIEUL-KHIEUKH;Lo;0;L;;;;;N;;;;;
+11D9;HANGUL JONGSEONG RIEUL-YEORINHIEUH;Lo;0;L;;;;;N;;;;;
+11DA;HANGUL JONGSEONG MIEUM-KIYEOK;Lo;0;L;;;;;N;;;;;
+11DB;HANGUL JONGSEONG MIEUM-RIEUL;Lo;0;L;;;;;N;;;;;
+11DC;HANGUL JONGSEONG MIEUM-PIEUP;Lo;0;L;;;;;N;;;;;
+11DD;HANGUL JONGSEONG MIEUM-SIOS;Lo;0;L;;;;;N;;;;;
+11DE;HANGUL JONGSEONG MIEUM-SSANGSIOS;Lo;0;L;;;;;N;;;;;
+11DF;HANGUL JONGSEONG MIEUM-PANSIOS;Lo;0;L;;;;;N;;;;;
+11E0;HANGUL JONGSEONG MIEUM-CHIEUCH;Lo;0;L;;;;;N;;;;;
+11E1;HANGUL JONGSEONG MIEUM-HIEUH;Lo;0;L;;;;;N;;;;;
+11E2;HANGUL JONGSEONG KAPYEOUNMIEUM;Lo;0;L;;;;;N;;;;;
+11E3;HANGUL JONGSEONG PIEUP-RIEUL;Lo;0;L;;;;;N;;;;;
+11E4;HANGUL JONGSEONG PIEUP-PHIEUPH;Lo;0;L;;;;;N;;;;;
+11E5;HANGUL JONGSEONG PIEUP-HIEUH;Lo;0;L;;;;;N;;;;;
+11E6;HANGUL JONGSEONG KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;;
+11E7;HANGUL JONGSEONG SIOS-KIYEOK;Lo;0;L;;;;;N;;;;;
+11E8;HANGUL JONGSEONG SIOS-TIKEUT;Lo;0;L;;;;;N;;;;;
+11E9;HANGUL JONGSEONG SIOS-RIEUL;Lo;0;L;;;;;N;;;;;
+11EA;HANGUL JONGSEONG SIOS-PIEUP;Lo;0;L;;;;;N;;;;;
+11EB;HANGUL JONGSEONG PANSIOS;Lo;0;L;;;;;N;;;;;
+11EC;HANGUL JONGSEONG IEUNG-KIYEOK;Lo;0;L;;;;;N;;;;;
+11ED;HANGUL JONGSEONG IEUNG-SSANGKIYEOK;Lo;0;L;;;;;N;;;;;
+11EE;HANGUL JONGSEONG SSANGIEUNG;Lo;0;L;;;;;N;;;;;
+11EF;HANGUL JONGSEONG IEUNG-KHIEUKH;Lo;0;L;;;;;N;;;;;
+11F0;HANGUL JONGSEONG YESIEUNG;Lo;0;L;;;;;N;;;;;
+11F1;HANGUL JONGSEONG YESIEUNG-SIOS;Lo;0;L;;;;;N;;;;;
+11F2;HANGUL JONGSEONG YESIEUNG-PANSIOS;Lo;0;L;;;;;N;;;;;
+11F3;HANGUL JONGSEONG PHIEUPH-PIEUP;Lo;0;L;;;;;N;;;;;
+11F4;HANGUL JONGSEONG KAPYEOUNPHIEUPH;Lo;0;L;;;;;N;;;;;
+11F5;HANGUL JONGSEONG HIEUH-NIEUN;Lo;0;L;;;;;N;;;;;
+11F6;HANGUL JONGSEONG HIEUH-RIEUL;Lo;0;L;;;;;N;;;;;
+11F7;HANGUL JONGSEONG HIEUH-MIEUM;Lo;0;L;;;;;N;;;;;
+11F8;HANGUL JONGSEONG HIEUH-PIEUP;Lo;0;L;;;;;N;;;;;
+11F9;HANGUL JONGSEONG YEORINHIEUH;Lo;0;L;;;;;N;;;;;
+11FA;HANGUL JONGSEONG KIYEOK-NIEUN;Lo;0;L;;;;;N;;;;;
+11FB;HANGUL JONGSEONG KIYEOK-PIEUP;Lo;0;L;;;;;N;;;;;
+11FC;HANGUL JONGSEONG KIYEOK-CHIEUCH;Lo;0;L;;;;;N;;;;;
+11FD;HANGUL JONGSEONG KIYEOK-KHIEUKH;Lo;0;L;;;;;N;;;;;
+11FE;HANGUL JONGSEONG KIYEOK-HIEUH;Lo;0;L;;;;;N;;;;;
+11FF;HANGUL JONGSEONG SSANGNIEUN;Lo;0;L;;;;;N;;;;;
+1200;ETHIOPIC SYLLABLE HA;Lo;0;L;;;;;N;;;;;
+1201;ETHIOPIC SYLLABLE HU;Lo;0;L;;;;;N;;;;;
+1202;ETHIOPIC SYLLABLE HI;Lo;0;L;;;;;N;;;;;
+1203;ETHIOPIC SYLLABLE HAA;Lo;0;L;;;;;N;;;;;
+1204;ETHIOPIC SYLLABLE HEE;Lo;0;L;;;;;N;;;;;
+1205;ETHIOPIC SYLLABLE HE;Lo;0;L;;;;;N;;;;;
+1206;ETHIOPIC SYLLABLE HO;Lo;0;L;;;;;N;;;;;
+1207;ETHIOPIC SYLLABLE HOA;Lo;0;L;;;;;N;;;;;
+1208;ETHIOPIC SYLLABLE LA;Lo;0;L;;;;;N;;;;;
+1209;ETHIOPIC SYLLABLE LU;Lo;0;L;;;;;N;;;;;
+120A;ETHIOPIC SYLLABLE LI;Lo;0;L;;;;;N;;;;;
+120B;ETHIOPIC SYLLABLE LAA;Lo;0;L;;;;;N;;;;;
+120C;ETHIOPIC SYLLABLE LEE;Lo;0;L;;;;;N;;;;;
+120D;ETHIOPIC SYLLABLE LE;Lo;0;L;;;;;N;;;;;
+120E;ETHIOPIC SYLLABLE LO;Lo;0;L;;;;;N;;;;;
+120F;ETHIOPIC SYLLABLE LWA;Lo;0;L;;;;;N;;;;;
+1210;ETHIOPIC SYLLABLE HHA;Lo;0;L;;;;;N;;;;;
+1211;ETHIOPIC SYLLABLE HHU;Lo;0;L;;;;;N;;;;;
+1212;ETHIOPIC SYLLABLE HHI;Lo;0;L;;;;;N;;;;;
+1213;ETHIOPIC SYLLABLE HHAA;Lo;0;L;;;;;N;;;;;
+1214;ETHIOPIC SYLLABLE HHEE;Lo;0;L;;;;;N;;;;;
+1215;ETHIOPIC SYLLABLE HHE;Lo;0;L;;;;;N;;;;;
+1216;ETHIOPIC SYLLABLE HHO;Lo;0;L;;;;;N;;;;;
+1217;ETHIOPIC SYLLABLE HHWA;Lo;0;L;;;;;N;;;;;
+1218;ETHIOPIC SYLLABLE MA;Lo;0;L;;;;;N;;;;;
+1219;ETHIOPIC SYLLABLE MU;Lo;0;L;;;;;N;;;;;
+121A;ETHIOPIC SYLLABLE MI;Lo;0;L;;;;;N;;;;;
+121B;ETHIOPIC SYLLABLE MAA;Lo;0;L;;;;;N;;;;;
+121C;ETHIOPIC SYLLABLE MEE;Lo;0;L;;;;;N;;;;;
+121D;ETHIOPIC SYLLABLE ME;Lo;0;L;;;;;N;;;;;
+121E;ETHIOPIC SYLLABLE MO;Lo;0;L;;;;;N;;;;;
+121F;ETHIOPIC SYLLABLE MWA;Lo;0;L;;;;;N;;;;;
+1220;ETHIOPIC SYLLABLE SZA;Lo;0;L;;;;;N;;;;;
+1221;ETHIOPIC SYLLABLE SZU;Lo;0;L;;;;;N;;;;;
+1222;ETHIOPIC SYLLABLE SZI;Lo;0;L;;;;;N;;;;;
+1223;ETHIOPIC SYLLABLE SZAA;Lo;0;L;;;;;N;;;;;
+1224;ETHIOPIC SYLLABLE SZEE;Lo;0;L;;;;;N;;;;;
+1225;ETHIOPIC SYLLABLE SZE;Lo;0;L;;;;;N;;;;;
+1226;ETHIOPIC SYLLABLE SZO;Lo;0;L;;;;;N;;;;;
+1227;ETHIOPIC SYLLABLE SZWA;Lo;0;L;;;;;N;;;;;
+1228;ETHIOPIC SYLLABLE RA;Lo;0;L;;;;;N;;;;;
+1229;ETHIOPIC SYLLABLE RU;Lo;0;L;;;;;N;;;;;
+122A;ETHIOPIC SYLLABLE RI;Lo;0;L;;;;;N;;;;;
+122B;ETHIOPIC SYLLABLE RAA;Lo;0;L;;;;;N;;;;;
+122C;ETHIOPIC SYLLABLE REE;Lo;0;L;;;;;N;;;;;
+122D;ETHIOPIC SYLLABLE RE;Lo;0;L;;;;;N;;;;;
+122E;ETHIOPIC SYLLABLE RO;Lo;0;L;;;;;N;;;;;
+122F;ETHIOPIC SYLLABLE RWA;Lo;0;L;;;;;N;;;;;
+1230;ETHIOPIC SYLLABLE SA;Lo;0;L;;;;;N;;;;;
+1231;ETHIOPIC SYLLABLE SU;Lo;0;L;;;;;N;;;;;
+1232;ETHIOPIC SYLLABLE SI;Lo;0;L;;;;;N;;;;;
+1233;ETHIOPIC SYLLABLE SAA;Lo;0;L;;;;;N;;;;;
+1234;ETHIOPIC SYLLABLE SEE;Lo;0;L;;;;;N;;;;;
+1235;ETHIOPIC SYLLABLE SE;Lo;0;L;;;;;N;;;;;
+1236;ETHIOPIC SYLLABLE SO;Lo;0;L;;;;;N;;;;;
+1237;ETHIOPIC SYLLABLE SWA;Lo;0;L;;;;;N;;;;;
+1238;ETHIOPIC SYLLABLE SHA;Lo;0;L;;;;;N;;;;;
+1239;ETHIOPIC SYLLABLE SHU;Lo;0;L;;;;;N;;;;;
+123A;ETHIOPIC SYLLABLE SHI;Lo;0;L;;;;;N;;;;;
+123B;ETHIOPIC SYLLABLE SHAA;Lo;0;L;;;;;N;;;;;
+123C;ETHIOPIC SYLLABLE SHEE;Lo;0;L;;;;;N;;;;;
+123D;ETHIOPIC SYLLABLE SHE;Lo;0;L;;;;;N;;;;;
+123E;ETHIOPIC SYLLABLE SHO;Lo;0;L;;;;;N;;;;;
+123F;ETHIOPIC SYLLABLE SHWA;Lo;0;L;;;;;N;;;;;
+1240;ETHIOPIC SYLLABLE QA;Lo;0;L;;;;;N;;;;;
+1241;ETHIOPIC SYLLABLE QU;Lo;0;L;;;;;N;;;;;
+1242;ETHIOPIC SYLLABLE QI;Lo;0;L;;;;;N;;;;;
+1243;ETHIOPIC SYLLABLE QAA;Lo;0;L;;;;;N;;;;;
+1244;ETHIOPIC SYLLABLE QEE;Lo;0;L;;;;;N;;;;;
+1245;ETHIOPIC SYLLABLE QE;Lo;0;L;;;;;N;;;;;
+1246;ETHIOPIC SYLLABLE QO;Lo;0;L;;;;;N;;;;;
+1247;ETHIOPIC SYLLABLE QOA;Lo;0;L;;;;;N;;;;;
+1248;ETHIOPIC SYLLABLE QWA;Lo;0;L;;;;;N;;;;;
+124A;ETHIOPIC SYLLABLE QWI;Lo;0;L;;;;;N;;;;;
+124B;ETHIOPIC SYLLABLE QWAA;Lo;0;L;;;;;N;;;;;
+124C;ETHIOPIC SYLLABLE QWEE;Lo;0;L;;;;;N;;;;;
+124D;ETHIOPIC SYLLABLE QWE;Lo;0;L;;;;;N;;;;;
+1250;ETHIOPIC SYLLABLE QHA;Lo;0;L;;;;;N;;;;;
+1251;ETHIOPIC SYLLABLE QHU;Lo;0;L;;;;;N;;;;;
+1252;ETHIOPIC SYLLABLE QHI;Lo;0;L;;;;;N;;;;;
+1253;ETHIOPIC SYLLABLE QHAA;Lo;0;L;;;;;N;;;;;
+1254;ETHIOPIC SYLLABLE QHEE;Lo;0;L;;;;;N;;;;;
+1255;ETHIOPIC SYLLABLE QHE;Lo;0;L;;;;;N;;;;;
+1256;ETHIOPIC SYLLABLE QHO;Lo;0;L;;;;;N;;;;;
+1258;ETHIOPIC SYLLABLE QHWA;Lo;0;L;;;;;N;;;;;
+125A;ETHIOPIC SYLLABLE QHWI;Lo;0;L;;;;;N;;;;;
+125B;ETHIOPIC SYLLABLE QHWAA;Lo;0;L;;;;;N;;;;;
+125C;ETHIOPIC SYLLABLE QHWEE;Lo;0;L;;;;;N;;;;;
+125D;ETHIOPIC SYLLABLE QHWE;Lo;0;L;;;;;N;;;;;
+1260;ETHIOPIC SYLLABLE BA;Lo;0;L;;;;;N;;;;;
+1261;ETHIOPIC SYLLABLE BU;Lo;0;L;;;;;N;;;;;
+1262;ETHIOPIC SYLLABLE BI;Lo;0;L;;;;;N;;;;;
+1263;ETHIOPIC SYLLABLE BAA;Lo;0;L;;;;;N;;;;;
+1264;ETHIOPIC SYLLABLE BEE;Lo;0;L;;;;;N;;;;;
+1265;ETHIOPIC SYLLABLE BE;Lo;0;L;;;;;N;;;;;
+1266;ETHIOPIC SYLLABLE BO;Lo;0;L;;;;;N;;;;;
+1267;ETHIOPIC SYLLABLE BWA;Lo;0;L;;;;;N;;;;;
+1268;ETHIOPIC SYLLABLE VA;Lo;0;L;;;;;N;;;;;
+1269;ETHIOPIC SYLLABLE VU;Lo;0;L;;;;;N;;;;;
+126A;ETHIOPIC SYLLABLE VI;Lo;0;L;;;;;N;;;;;
+126B;ETHIOPIC SYLLABLE VAA;Lo;0;L;;;;;N;;;;;
+126C;ETHIOPIC SYLLABLE VEE;Lo;0;L;;;;;N;;;;;
+126D;ETHIOPIC SYLLABLE VE;Lo;0;L;;;;;N;;;;;
+126E;ETHIOPIC SYLLABLE VO;Lo;0;L;;;;;N;;;;;
+126F;ETHIOPIC SYLLABLE VWA;Lo;0;L;;;;;N;;;;;
+1270;ETHIOPIC SYLLABLE TA;Lo;0;L;;;;;N;;;;;
+1271;ETHIOPIC SYLLABLE TU;Lo;0;L;;;;;N;;;;;
+1272;ETHIOPIC SYLLABLE TI;Lo;0;L;;;;;N;;;;;
+1273;ETHIOPIC SYLLABLE TAA;Lo;0;L;;;;;N;;;;;
+1274;ETHIOPIC SYLLABLE TEE;Lo;0;L;;;;;N;;;;;
+1275;ETHIOPIC SYLLABLE TE;Lo;0;L;;;;;N;;;;;
+1276;ETHIOPIC SYLLABLE TO;Lo;0;L;;;;;N;;;;;
+1277;ETHIOPIC SYLLABLE TWA;Lo;0;L;;;;;N;;;;;
+1278;ETHIOPIC SYLLABLE CA;Lo;0;L;;;;;N;;;;;
+1279;ETHIOPIC SYLLABLE CU;Lo;0;L;;;;;N;;;;;
+127A;ETHIOPIC SYLLABLE CI;Lo;0;L;;;;;N;;;;;
+127B;ETHIOPIC SYLLABLE CAA;Lo;0;L;;;;;N;;;;;
+127C;ETHIOPIC SYLLABLE CEE;Lo;0;L;;;;;N;;;;;
+127D;ETHIOPIC SYLLABLE CE;Lo;0;L;;;;;N;;;;;
+127E;ETHIOPIC SYLLABLE CO;Lo;0;L;;;;;N;;;;;
+127F;ETHIOPIC SYLLABLE CWA;Lo;0;L;;;;;N;;;;;
+1280;ETHIOPIC SYLLABLE XA;Lo;0;L;;;;;N;;;;;
+1281;ETHIOPIC SYLLABLE XU;Lo;0;L;;;;;N;;;;;
+1282;ETHIOPIC SYLLABLE XI;Lo;0;L;;;;;N;;;;;
+1283;ETHIOPIC SYLLABLE XAA;Lo;0;L;;;;;N;;;;;
+1284;ETHIOPIC SYLLABLE XEE;Lo;0;L;;;;;N;;;;;
+1285;ETHIOPIC SYLLABLE XE;Lo;0;L;;;;;N;;;;;
+1286;ETHIOPIC SYLLABLE XO;Lo;0;L;;;;;N;;;;;
+1287;ETHIOPIC SYLLABLE XOA;Lo;0;L;;;;;N;;;;;
+1288;ETHIOPIC SYLLABLE XWA;Lo;0;L;;;;;N;;;;;
+128A;ETHIOPIC SYLLABLE XWI;Lo;0;L;;;;;N;;;;;
+128B;ETHIOPIC SYLLABLE XWAA;Lo;0;L;;;;;N;;;;;
+128C;ETHIOPIC SYLLABLE XWEE;Lo;0;L;;;;;N;;;;;
+128D;ETHIOPIC SYLLABLE XWE;Lo;0;L;;;;;N;;;;;
+1290;ETHIOPIC SYLLABLE NA;Lo;0;L;;;;;N;;;;;
+1291;ETHIOPIC SYLLABLE NU;Lo;0;L;;;;;N;;;;;
+1292;ETHIOPIC SYLLABLE NI;Lo;0;L;;;;;N;;;;;
+1293;ETHIOPIC SYLLABLE NAA;Lo;0;L;;;;;N;;;;;
+1294;ETHIOPIC SYLLABLE NEE;Lo;0;L;;;;;N;;;;;
+1295;ETHIOPIC SYLLABLE NE;Lo;0;L;;;;;N;;;;;
+1296;ETHIOPIC SYLLABLE NO;Lo;0;L;;;;;N;;;;;
+1297;ETHIOPIC SYLLABLE NWA;Lo;0;L;;;;;N;;;;;
+1298;ETHIOPIC SYLLABLE NYA;Lo;0;L;;;;;N;;;;;
+1299;ETHIOPIC SYLLABLE NYU;Lo;0;L;;;;;N;;;;;
+129A;ETHIOPIC SYLLABLE NYI;Lo;0;L;;;;;N;;;;;
+129B;ETHIOPIC SYLLABLE NYAA;Lo;0;L;;;;;N;;;;;
+129C;ETHIOPIC SYLLABLE NYEE;Lo;0;L;;;;;N;;;;;
+129D;ETHIOPIC SYLLABLE NYE;Lo;0;L;;;;;N;;;;;
+129E;ETHIOPIC SYLLABLE NYO;Lo;0;L;;;;;N;;;;;
+129F;ETHIOPIC SYLLABLE NYWA;Lo;0;L;;;;;N;;;;;
+12A0;ETHIOPIC SYLLABLE GLOTTAL A;Lo;0;L;;;;;N;;;;;
+12A1;ETHIOPIC SYLLABLE GLOTTAL U;Lo;0;L;;;;;N;;;;;
+12A2;ETHIOPIC SYLLABLE GLOTTAL I;Lo;0;L;;;;;N;;;;;
+12A3;ETHIOPIC SYLLABLE GLOTTAL AA;Lo;0;L;;;;;N;;;;;
+12A4;ETHIOPIC SYLLABLE GLOTTAL EE;Lo;0;L;;;;;N;;;;;
+12A5;ETHIOPIC SYLLABLE GLOTTAL E;Lo;0;L;;;;;N;;;;;
+12A6;ETHIOPIC SYLLABLE GLOTTAL O;Lo;0;L;;;;;N;;;;;
+12A7;ETHIOPIC SYLLABLE GLOTTAL WA;Lo;0;L;;;;;N;;;;;
+12A8;ETHIOPIC SYLLABLE KA;Lo;0;L;;;;;N;;;;;
+12A9;ETHIOPIC SYLLABLE KU;Lo;0;L;;;;;N;;;;;
+12AA;ETHIOPIC SYLLABLE KI;Lo;0;L;;;;;N;;;;;
+12AB;ETHIOPIC SYLLABLE KAA;Lo;0;L;;;;;N;;;;;
+12AC;ETHIOPIC SYLLABLE KEE;Lo;0;L;;;;;N;;;;;
+12AD;ETHIOPIC SYLLABLE KE;Lo;0;L;;;;;N;;;;;
+12AE;ETHIOPIC SYLLABLE KO;Lo;0;L;;;;;N;;;;;
+12AF;ETHIOPIC SYLLABLE KOA;Lo;0;L;;;;;N;;;;;
+12B0;ETHIOPIC SYLLABLE KWA;Lo;0;L;;;;;N;;;;;
+12B2;ETHIOPIC SYLLABLE KWI;Lo;0;L;;;;;N;;;;;
+12B3;ETHIOPIC SYLLABLE KWAA;Lo;0;L;;;;;N;;;;;
+12B4;ETHIOPIC SYLLABLE KWEE;Lo;0;L;;;;;N;;;;;
+12B5;ETHIOPIC SYLLABLE KWE;Lo;0;L;;;;;N;;;;;
+12B8;ETHIOPIC SYLLABLE KXA;Lo;0;L;;;;;N;;;;;
+12B9;ETHIOPIC SYLLABLE KXU;Lo;0;L;;;;;N;;;;;
+12BA;ETHIOPIC SYLLABLE KXI;Lo;0;L;;;;;N;;;;;
+12BB;ETHIOPIC SYLLABLE KXAA;Lo;0;L;;;;;N;;;;;
+12BC;ETHIOPIC SYLLABLE KXEE;Lo;0;L;;;;;N;;;;;
+12BD;ETHIOPIC SYLLABLE KXE;Lo;0;L;;;;;N;;;;;
+12BE;ETHIOPIC SYLLABLE KXO;Lo;0;L;;;;;N;;;;;
+12C0;ETHIOPIC SYLLABLE KXWA;Lo;0;L;;;;;N;;;;;
+12C2;ETHIOPIC SYLLABLE KXWI;Lo;0;L;;;;;N;;;;;
+12C3;ETHIOPIC SYLLABLE KXWAA;Lo;0;L;;;;;N;;;;;
+12C4;ETHIOPIC SYLLABLE KXWEE;Lo;0;L;;;;;N;;;;;
+12C5;ETHIOPIC SYLLABLE KXWE;Lo;0;L;;;;;N;;;;;
+12C8;ETHIOPIC SYLLABLE WA;Lo;0;L;;;;;N;;;;;
+12C9;ETHIOPIC SYLLABLE WU;Lo;0;L;;;;;N;;;;;
+12CA;ETHIOPIC SYLLABLE WI;Lo;0;L;;;;;N;;;;;
+12CB;ETHIOPIC SYLLABLE WAA;Lo;0;L;;;;;N;;;;;
+12CC;ETHIOPIC SYLLABLE WEE;Lo;0;L;;;;;N;;;;;
+12CD;ETHIOPIC SYLLABLE WE;Lo;0;L;;;;;N;;;;;
+12CE;ETHIOPIC SYLLABLE WO;Lo;0;L;;;;;N;;;;;
+12CF;ETHIOPIC SYLLABLE WOA;Lo;0;L;;;;;N;;;;;
+12D0;ETHIOPIC SYLLABLE PHARYNGEAL A;Lo;0;L;;;;;N;;;;;
+12D1;ETHIOPIC SYLLABLE PHARYNGEAL U;Lo;0;L;;;;;N;;;;;
+12D2;ETHIOPIC SYLLABLE PHARYNGEAL I;Lo;0;L;;;;;N;;;;;
+12D3;ETHIOPIC SYLLABLE PHARYNGEAL AA;Lo;0;L;;;;;N;;;;;
+12D4;ETHIOPIC SYLLABLE PHARYNGEAL EE;Lo;0;L;;;;;N;;;;;
+12D5;ETHIOPIC SYLLABLE PHARYNGEAL E;Lo;0;L;;;;;N;;;;;
+12D6;ETHIOPIC SYLLABLE PHARYNGEAL O;Lo;0;L;;;;;N;;;;;
+12D8;ETHIOPIC SYLLABLE ZA;Lo;0;L;;;;;N;;;;;
+12D9;ETHIOPIC SYLLABLE ZU;Lo;0;L;;;;;N;;;;;
+12DA;ETHIOPIC SYLLABLE ZI;Lo;0;L;;;;;N;;;;;
+12DB;ETHIOPIC SYLLABLE ZAA;Lo;0;L;;;;;N;;;;;
+12DC;ETHIOPIC SYLLABLE ZEE;Lo;0;L;;;;;N;;;;;
+12DD;ETHIOPIC SYLLABLE ZE;Lo;0;L;;;;;N;;;;;
+12DE;ETHIOPIC SYLLABLE ZO;Lo;0;L;;;;;N;;;;;
+12DF;ETHIOPIC SYLLABLE ZWA;Lo;0;L;;;;;N;;;;;
+12E0;ETHIOPIC SYLLABLE ZHA;Lo;0;L;;;;;N;;;;;
+12E1;ETHIOPIC SYLLABLE ZHU;Lo;0;L;;;;;N;;;;;
+12E2;ETHIOPIC SYLLABLE ZHI;Lo;0;L;;;;;N;;;;;
+12E3;ETHIOPIC SYLLABLE ZHAA;Lo;0;L;;;;;N;;;;;
+12E4;ETHIOPIC SYLLABLE ZHEE;Lo;0;L;;;;;N;;;;;
+12E5;ETHIOPIC SYLLABLE ZHE;Lo;0;L;;;;;N;;;;;
+12E6;ETHIOPIC SYLLABLE ZHO;Lo;0;L;;;;;N;;;;;
+12E7;ETHIOPIC SYLLABLE ZHWA;Lo;0;L;;;;;N;;;;;
+12E8;ETHIOPIC SYLLABLE YA;Lo;0;L;;;;;N;;;;;
+12E9;ETHIOPIC SYLLABLE YU;Lo;0;L;;;;;N;;;;;
+12EA;ETHIOPIC SYLLABLE YI;Lo;0;L;;;;;N;;;;;
+12EB;ETHIOPIC SYLLABLE YAA;Lo;0;L;;;;;N;;;;;
+12EC;ETHIOPIC SYLLABLE YEE;Lo;0;L;;;;;N;;;;;
+12ED;ETHIOPIC SYLLABLE YE;Lo;0;L;;;;;N;;;;;
+12EE;ETHIOPIC SYLLABLE YO;Lo;0;L;;;;;N;;;;;
+12EF;ETHIOPIC SYLLABLE YOA;Lo;0;L;;;;;N;;;;;
+12F0;ETHIOPIC SYLLABLE DA;Lo;0;L;;;;;N;;;;;
+12F1;ETHIOPIC SYLLABLE DU;Lo;0;L;;;;;N;;;;;
+12F2;ETHIOPIC SYLLABLE DI;Lo;0;L;;;;;N;;;;;
+12F3;ETHIOPIC SYLLABLE DAA;Lo;0;L;;;;;N;;;;;
+12F4;ETHIOPIC SYLLABLE DEE;Lo;0;L;;;;;N;;;;;
+12F5;ETHIOPIC SYLLABLE DE;Lo;0;L;;;;;N;;;;;
+12F6;ETHIOPIC SYLLABLE DO;Lo;0;L;;;;;N;;;;;
+12F7;ETHIOPIC SYLLABLE DWA;Lo;0;L;;;;;N;;;;;
+12F8;ETHIOPIC SYLLABLE DDA;Lo;0;L;;;;;N;;;;;
+12F9;ETHIOPIC SYLLABLE DDU;Lo;0;L;;;;;N;;;;;
+12FA;ETHIOPIC SYLLABLE DDI;Lo;0;L;;;;;N;;;;;
+12FB;ETHIOPIC SYLLABLE DDAA;Lo;0;L;;;;;N;;;;;
+12FC;ETHIOPIC SYLLABLE DDEE;Lo;0;L;;;;;N;;;;;
+12FD;ETHIOPIC SYLLABLE DDE;Lo;0;L;;;;;N;;;;;
+12FE;ETHIOPIC SYLLABLE DDO;Lo;0;L;;;;;N;;;;;
+12FF;ETHIOPIC SYLLABLE DDWA;Lo;0;L;;;;;N;;;;;
+1300;ETHIOPIC SYLLABLE JA;Lo;0;L;;;;;N;;;;;
+1301;ETHIOPIC SYLLABLE JU;Lo;0;L;;;;;N;;;;;
+1302;ETHIOPIC SYLLABLE JI;Lo;0;L;;;;;N;;;;;
+1303;ETHIOPIC SYLLABLE JAA;Lo;0;L;;;;;N;;;;;
+1304;ETHIOPIC SYLLABLE JEE;Lo;0;L;;;;;N;;;;;
+1305;ETHIOPIC SYLLABLE JE;Lo;0;L;;;;;N;;;;;
+1306;ETHIOPIC SYLLABLE JO;Lo;0;L;;;;;N;;;;;
+1307;ETHIOPIC SYLLABLE JWA;Lo;0;L;;;;;N;;;;;
+1308;ETHIOPIC SYLLABLE GA;Lo;0;L;;;;;N;;;;;
+1309;ETHIOPIC SYLLABLE GU;Lo;0;L;;;;;N;;;;;
+130A;ETHIOPIC SYLLABLE GI;Lo;0;L;;;;;N;;;;;
+130B;ETHIOPIC SYLLABLE GAA;Lo;0;L;;;;;N;;;;;
+130C;ETHIOPIC SYLLABLE GEE;Lo;0;L;;;;;N;;;;;
+130D;ETHIOPIC SYLLABLE GE;Lo;0;L;;;;;N;;;;;
+130E;ETHIOPIC SYLLABLE GO;Lo;0;L;;;;;N;;;;;
+130F;ETHIOPIC SYLLABLE GOA;Lo;0;L;;;;;N;;;;;
+1310;ETHIOPIC SYLLABLE GWA;Lo;0;L;;;;;N;;;;;
+1312;ETHIOPIC SYLLABLE GWI;Lo;0;L;;;;;N;;;;;
+1313;ETHIOPIC SYLLABLE GWAA;Lo;0;L;;;;;N;;;;;
+1314;ETHIOPIC SYLLABLE GWEE;Lo;0;L;;;;;N;;;;;
+1315;ETHIOPIC SYLLABLE GWE;Lo;0;L;;;;;N;;;;;
+1318;ETHIOPIC SYLLABLE GGA;Lo;0;L;;;;;N;;;;;
+1319;ETHIOPIC SYLLABLE GGU;Lo;0;L;;;;;N;;;;;
+131A;ETHIOPIC SYLLABLE GGI;Lo;0;L;;;;;N;;;;;
+131B;ETHIOPIC SYLLABLE GGAA;Lo;0;L;;;;;N;;;;;
+131C;ETHIOPIC SYLLABLE GGEE;Lo;0;L;;;;;N;;;;;
+131D;ETHIOPIC SYLLABLE GGE;Lo;0;L;;;;;N;;;;;
+131E;ETHIOPIC SYLLABLE GGO;Lo;0;L;;;;;N;;;;;
+131F;ETHIOPIC SYLLABLE GGWAA;Lo;0;L;;;;;N;;;;;
+1320;ETHIOPIC SYLLABLE THA;Lo;0;L;;;;;N;;;;;
+1321;ETHIOPIC SYLLABLE THU;Lo;0;L;;;;;N;;;;;
+1322;ETHIOPIC SYLLABLE THI;Lo;0;L;;;;;N;;;;;
+1323;ETHIOPIC SYLLABLE THAA;Lo;0;L;;;;;N;;;;;
+1324;ETHIOPIC SYLLABLE THEE;Lo;0;L;;;;;N;;;;;
+1325;ETHIOPIC SYLLABLE THE;Lo;0;L;;;;;N;;;;;
+1326;ETHIOPIC SYLLABLE THO;Lo;0;L;;;;;N;;;;;
+1327;ETHIOPIC SYLLABLE THWA;Lo;0;L;;;;;N;;;;;
+1328;ETHIOPIC SYLLABLE CHA;Lo;0;L;;;;;N;;;;;
+1329;ETHIOPIC SYLLABLE CHU;Lo;0;L;;;;;N;;;;;
+132A;ETHIOPIC SYLLABLE CHI;Lo;0;L;;;;;N;;;;;
+132B;ETHIOPIC SYLLABLE CHAA;Lo;0;L;;;;;N;;;;;
+132C;ETHIOPIC SYLLABLE CHEE;Lo;0;L;;;;;N;;;;;
+132D;ETHIOPIC SYLLABLE CHE;Lo;0;L;;;;;N;;;;;
+132E;ETHIOPIC SYLLABLE CHO;Lo;0;L;;;;;N;;;;;
+132F;ETHIOPIC SYLLABLE CHWA;Lo;0;L;;;;;N;;;;;
+1330;ETHIOPIC SYLLABLE PHA;Lo;0;L;;;;;N;;;;;
+1331;ETHIOPIC SYLLABLE PHU;Lo;0;L;;;;;N;;;;;
+1332;ETHIOPIC SYLLABLE PHI;Lo;0;L;;;;;N;;;;;
+1333;ETHIOPIC SYLLABLE PHAA;Lo;0;L;;;;;N;;;;;
+1334;ETHIOPIC SYLLABLE PHEE;Lo;0;L;;;;;N;;;;;
+1335;ETHIOPIC SYLLABLE PHE;Lo;0;L;;;;;N;;;;;
+1336;ETHIOPIC SYLLABLE PHO;Lo;0;L;;;;;N;;;;;
+1337;ETHIOPIC SYLLABLE PHWA;Lo;0;L;;;;;N;;;;;
+1338;ETHIOPIC SYLLABLE TSA;Lo;0;L;;;;;N;;;;;
+1339;ETHIOPIC SYLLABLE TSU;Lo;0;L;;;;;N;;;;;
+133A;ETHIOPIC SYLLABLE TSI;Lo;0;L;;;;;N;;;;;
+133B;ETHIOPIC SYLLABLE TSAA;Lo;0;L;;;;;N;;;;;
+133C;ETHIOPIC SYLLABLE TSEE;Lo;0;L;;;;;N;;;;;
+133D;ETHIOPIC SYLLABLE TSE;Lo;0;L;;;;;N;;;;;
+133E;ETHIOPIC SYLLABLE TSO;Lo;0;L;;;;;N;;;;;
+133F;ETHIOPIC SYLLABLE TSWA;Lo;0;L;;;;;N;;;;;
+1340;ETHIOPIC SYLLABLE TZA;Lo;0;L;;;;;N;;;;;
+1341;ETHIOPIC SYLLABLE TZU;Lo;0;L;;;;;N;;;;;
+1342;ETHIOPIC SYLLABLE TZI;Lo;0;L;;;;;N;;;;;
+1343;ETHIOPIC SYLLABLE TZAA;Lo;0;L;;;;;N;;;;;
+1344;ETHIOPIC SYLLABLE TZEE;Lo;0;L;;;;;N;;;;;
+1345;ETHIOPIC SYLLABLE TZE;Lo;0;L;;;;;N;;;;;
+1346;ETHIOPIC SYLLABLE TZO;Lo;0;L;;;;;N;;;;;
+1347;ETHIOPIC SYLLABLE TZOA;Lo;0;L;;;;;N;;;;;
+1348;ETHIOPIC SYLLABLE FA;Lo;0;L;;;;;N;;;;;
+1349;ETHIOPIC SYLLABLE FU;Lo;0;L;;;;;N;;;;;
+134A;ETHIOPIC SYLLABLE FI;Lo;0;L;;;;;N;;;;;
+134B;ETHIOPIC SYLLABLE FAA;Lo;0;L;;;;;N;;;;;
+134C;ETHIOPIC SYLLABLE FEE;Lo;0;L;;;;;N;;;;;
+134D;ETHIOPIC SYLLABLE FE;Lo;0;L;;;;;N;;;;;
+134E;ETHIOPIC SYLLABLE FO;Lo;0;L;;;;;N;;;;;
+134F;ETHIOPIC SYLLABLE FWA;Lo;0;L;;;;;N;;;;;
+1350;ETHIOPIC SYLLABLE PA;Lo;0;L;;;;;N;;;;;
+1351;ETHIOPIC SYLLABLE PU;Lo;0;L;;;;;N;;;;;
+1352;ETHIOPIC SYLLABLE PI;Lo;0;L;;;;;N;;;;;
+1353;ETHIOPIC SYLLABLE PAA;Lo;0;L;;;;;N;;;;;
+1354;ETHIOPIC SYLLABLE PEE;Lo;0;L;;;;;N;;;;;
+1355;ETHIOPIC SYLLABLE PE;Lo;0;L;;;;;N;;;;;
+1356;ETHIOPIC SYLLABLE PO;Lo;0;L;;;;;N;;;;;
+1357;ETHIOPIC SYLLABLE PWA;Lo;0;L;;;;;N;;;;;
+1358;ETHIOPIC SYLLABLE RYA;Lo;0;L;;;;;N;;;;;
+1359;ETHIOPIC SYLLABLE MYA;Lo;0;L;;;;;N;;;;;
+135A;ETHIOPIC SYLLABLE FYA;Lo;0;L;;;;;N;;;;;
+135D;ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK;Mn;230;NSM;;;;;N;;;;;
+135E;ETHIOPIC COMBINING VOWEL LENGTH MARK;Mn;230;NSM;;;;;N;;;;;
+135F;ETHIOPIC COMBINING GEMINATION MARK;Mn;230;NSM;;;;;N;;;;;
+1360;ETHIOPIC SECTION MARK;Po;0;L;;;;;N;;;;;
+1361;ETHIOPIC WORDSPACE;Po;0;L;;;;;N;;;;;
+1362;ETHIOPIC FULL STOP;Po;0;L;;;;;N;;;;;
+1363;ETHIOPIC COMMA;Po;0;L;;;;;N;;;;;
+1364;ETHIOPIC SEMICOLON;Po;0;L;;;;;N;;;;;
+1365;ETHIOPIC COLON;Po;0;L;;;;;N;;;;;
+1366;ETHIOPIC PREFACE COLON;Po;0;L;;;;;N;;;;;
+1367;ETHIOPIC QUESTION MARK;Po;0;L;;;;;N;;;;;
+1368;ETHIOPIC PARAGRAPH SEPARATOR;Po;0;L;;;;;N;;;;;
+1369;ETHIOPIC DIGIT ONE;No;0;L;;;1;1;N;;;;;
+136A;ETHIOPIC DIGIT TWO;No;0;L;;;2;2;N;;;;;
+136B;ETHIOPIC DIGIT THREE;No;0;L;;;3;3;N;;;;;
+136C;ETHIOPIC DIGIT FOUR;No;0;L;;;4;4;N;;;;;
+136D;ETHIOPIC DIGIT FIVE;No;0;L;;;5;5;N;;;;;
+136E;ETHIOPIC DIGIT SIX;No;0;L;;;6;6;N;;;;;
+136F;ETHIOPIC DIGIT SEVEN;No;0;L;;;7;7;N;;;;;
+1370;ETHIOPIC DIGIT EIGHT;No;0;L;;;8;8;N;;;;;
+1371;ETHIOPIC DIGIT NINE;No;0;L;;;9;9;N;;;;;
+1372;ETHIOPIC NUMBER TEN;No;0;L;;;;10;N;;;;;
+1373;ETHIOPIC NUMBER TWENTY;No;0;L;;;;20;N;;;;;
+1374;ETHIOPIC NUMBER THIRTY;No;0;L;;;;30;N;;;;;
+1375;ETHIOPIC NUMBER FORTY;No;0;L;;;;40;N;;;;;
+1376;ETHIOPIC NUMBER FIFTY;No;0;L;;;;50;N;;;;;
+1377;ETHIOPIC NUMBER SIXTY;No;0;L;;;;60;N;;;;;
+1378;ETHIOPIC NUMBER SEVENTY;No;0;L;;;;70;N;;;;;
+1379;ETHIOPIC NUMBER EIGHTY;No;0;L;;;;80;N;;;;;
+137A;ETHIOPIC NUMBER NINETY;No;0;L;;;;90;N;;;;;
+137B;ETHIOPIC NUMBER HUNDRED;No;0;L;;;;100;N;;;;;
+137C;ETHIOPIC NUMBER TEN THOUSAND;No;0;L;;;;10000;N;;;;;
+1380;ETHIOPIC SYLLABLE SEBATBEIT MWA;Lo;0;L;;;;;N;;;;;
+1381;ETHIOPIC SYLLABLE MWI;Lo;0;L;;;;;N;;;;;
+1382;ETHIOPIC SYLLABLE MWEE;Lo;0;L;;;;;N;;;;;
+1383;ETHIOPIC SYLLABLE MWE;Lo;0;L;;;;;N;;;;;
+1384;ETHIOPIC SYLLABLE SEBATBEIT BWA;Lo;0;L;;;;;N;;;;;
+1385;ETHIOPIC SYLLABLE BWI;Lo;0;L;;;;;N;;;;;
+1386;ETHIOPIC SYLLABLE BWEE;Lo;0;L;;;;;N;;;;;
+1387;ETHIOPIC SYLLABLE BWE;Lo;0;L;;;;;N;;;;;
+1388;ETHIOPIC SYLLABLE SEBATBEIT FWA;Lo;0;L;;;;;N;;;;;
+1389;ETHIOPIC SYLLABLE FWI;Lo;0;L;;;;;N;;;;;
+138A;ETHIOPIC SYLLABLE FWEE;Lo;0;L;;;;;N;;;;;
+138B;ETHIOPIC SYLLABLE FWE;Lo;0;L;;;;;N;;;;;
+138C;ETHIOPIC SYLLABLE SEBATBEIT PWA;Lo;0;L;;;;;N;;;;;
+138D;ETHIOPIC SYLLABLE PWI;Lo;0;L;;;;;N;;;;;
+138E;ETHIOPIC SYLLABLE PWEE;Lo;0;L;;;;;N;;;;;
+138F;ETHIOPIC SYLLABLE PWE;Lo;0;L;;;;;N;;;;;
+1390;ETHIOPIC TONAL MARK YIZET;So;0;ON;;;;;N;;;;;
+1391;ETHIOPIC TONAL MARK DERET;So;0;ON;;;;;N;;;;;
+1392;ETHIOPIC TONAL MARK RIKRIK;So;0;ON;;;;;N;;;;;
+1393;ETHIOPIC TONAL MARK SHORT RIKRIK;So;0;ON;;;;;N;;;;;
+1394;ETHIOPIC TONAL MARK DIFAT;So;0;ON;;;;;N;;;;;
+1395;ETHIOPIC TONAL MARK KENAT;So;0;ON;;;;;N;;;;;
+1396;ETHIOPIC TONAL MARK CHIRET;So;0;ON;;;;;N;;;;;
+1397;ETHIOPIC TONAL MARK HIDET;So;0;ON;;;;;N;;;;;
+1398;ETHIOPIC TONAL MARK DERET-HIDET;So;0;ON;;;;;N;;;;;
+1399;ETHIOPIC TONAL MARK KURT;So;0;ON;;;;;N;;;;;
+13A0;CHEROKEE LETTER A;Lu;0;L;;;;;N;;;;AB70;
+13A1;CHEROKEE LETTER E;Lu;0;L;;;;;N;;;;AB71;
+13A2;CHEROKEE LETTER I;Lu;0;L;;;;;N;;;;AB72;
+13A3;CHEROKEE LETTER O;Lu;0;L;;;;;N;;;;AB73;
+13A4;CHEROKEE LETTER U;Lu;0;L;;;;;N;;;;AB74;
+13A5;CHEROKEE LETTER V;Lu;0;L;;;;;N;;;;AB75;
+13A6;CHEROKEE LETTER GA;Lu;0;L;;;;;N;;;;AB76;
+13A7;CHEROKEE LETTER KA;Lu;0;L;;;;;N;;;;AB77;
+13A8;CHEROKEE LETTER GE;Lu;0;L;;;;;N;;;;AB78;
+13A9;CHEROKEE LETTER GI;Lu;0;L;;;;;N;;;;AB79;
+13AA;CHEROKEE LETTER GO;Lu;0;L;;;;;N;;;;AB7A;
+13AB;CHEROKEE LETTER GU;Lu;0;L;;;;;N;;;;AB7B;
+13AC;CHEROKEE LETTER GV;Lu;0;L;;;;;N;;;;AB7C;
+13AD;CHEROKEE LETTER HA;Lu;0;L;;;;;N;;;;AB7D;
+13AE;CHEROKEE LETTER HE;Lu;0;L;;;;;N;;;;AB7E;
+13AF;CHEROKEE LETTER HI;Lu;0;L;;;;;N;;;;AB7F;
+13B0;CHEROKEE LETTER HO;Lu;0;L;;;;;N;;;;AB80;
+13B1;CHEROKEE LETTER HU;Lu;0;L;;;;;N;;;;AB81;
+13B2;CHEROKEE LETTER HV;Lu;0;L;;;;;N;;;;AB82;
+13B3;CHEROKEE LETTER LA;Lu;0;L;;;;;N;;;;AB83;
+13B4;CHEROKEE LETTER LE;Lu;0;L;;;;;N;;;;AB84;
+13B5;CHEROKEE LETTER LI;Lu;0;L;;;;;N;;;;AB85;
+13B6;CHEROKEE LETTER LO;Lu;0;L;;;;;N;;;;AB86;
+13B7;CHEROKEE LETTER LU;Lu;0;L;;;;;N;;;;AB87;
+13B8;CHEROKEE LETTER LV;Lu;0;L;;;;;N;;;;AB88;
+13B9;CHEROKEE LETTER MA;Lu;0;L;;;;;N;;;;AB89;
+13BA;CHEROKEE LETTER ME;Lu;0;L;;;;;N;;;;AB8A;
+13BB;CHEROKEE LETTER MI;Lu;0;L;;;;;N;;;;AB8B;
+13BC;CHEROKEE LETTER MO;Lu;0;L;;;;;N;;;;AB8C;
+13BD;CHEROKEE LETTER MU;Lu;0;L;;;;;N;;;;AB8D;
+13BE;CHEROKEE LETTER NA;Lu;0;L;;;;;N;;;;AB8E;
+13BF;CHEROKEE LETTER HNA;Lu;0;L;;;;;N;;;;AB8F;
+13C0;CHEROKEE LETTER NAH;Lu;0;L;;;;;N;;;;AB90;
+13C1;CHEROKEE LETTER NE;Lu;0;L;;;;;N;;;;AB91;
+13C2;CHEROKEE LETTER NI;Lu;0;L;;;;;N;;;;AB92;
+13C3;CHEROKEE LETTER NO;Lu;0;L;;;;;N;;;;AB93;
+13C4;CHEROKEE LETTER NU;Lu;0;L;;;;;N;;;;AB94;
+13C5;CHEROKEE LETTER NV;Lu;0;L;;;;;N;;;;AB95;
+13C6;CHEROKEE LETTER QUA;Lu;0;L;;;;;N;;;;AB96;
+13C7;CHEROKEE LETTER QUE;Lu;0;L;;;;;N;;;;AB97;
+13C8;CHEROKEE LETTER QUI;Lu;0;L;;;;;N;;;;AB98;
+13C9;CHEROKEE LETTER QUO;Lu;0;L;;;;;N;;;;AB99;
+13CA;CHEROKEE LETTER QUU;Lu;0;L;;;;;N;;;;AB9A;
+13CB;CHEROKEE LETTER QUV;Lu;0;L;;;;;N;;;;AB9B;
+13CC;CHEROKEE LETTER SA;Lu;0;L;;;;;N;;;;AB9C;
+13CD;CHEROKEE LETTER S;Lu;0;L;;;;;N;;;;AB9D;
+13CE;CHEROKEE LETTER SE;Lu;0;L;;;;;N;;;;AB9E;
+13CF;CHEROKEE LETTER SI;Lu;0;L;;;;;N;;;;AB9F;
+13D0;CHEROKEE LETTER SO;Lu;0;L;;;;;N;;;;ABA0;
+13D1;CHEROKEE LETTER SU;Lu;0;L;;;;;N;;;;ABA1;
+13D2;CHEROKEE LETTER SV;Lu;0;L;;;;;N;;;;ABA2;
+13D3;CHEROKEE LETTER DA;Lu;0;L;;;;;N;;;;ABA3;
+13D4;CHEROKEE LETTER TA;Lu;0;L;;;;;N;;;;ABA4;
+13D5;CHEROKEE LETTER DE;Lu;0;L;;;;;N;;;;ABA5;
+13D6;CHEROKEE LETTER TE;Lu;0;L;;;;;N;;;;ABA6;
+13D7;CHEROKEE LETTER DI;Lu;0;L;;;;;N;;;;ABA7;
+13D8;CHEROKEE LETTER TI;Lu;0;L;;;;;N;;;;ABA8;
+13D9;CHEROKEE LETTER DO;Lu;0;L;;;;;N;;;;ABA9;
+13DA;CHEROKEE LETTER DU;Lu;0;L;;;;;N;;;;ABAA;
+13DB;CHEROKEE LETTER DV;Lu;0;L;;;;;N;;;;ABAB;
+13DC;CHEROKEE LETTER DLA;Lu;0;L;;;;;N;;;;ABAC;
+13DD;CHEROKEE LETTER TLA;Lu;0;L;;;;;N;;;;ABAD;
+13DE;CHEROKEE LETTER TLE;Lu;0;L;;;;;N;;;;ABAE;
+13DF;CHEROKEE LETTER TLI;Lu;0;L;;;;;N;;;;ABAF;
+13E0;CHEROKEE LETTER TLO;Lu;0;L;;;;;N;;;;ABB0;
+13E1;CHEROKEE LETTER TLU;Lu;0;L;;;;;N;;;;ABB1;
+13E2;CHEROKEE LETTER TLV;Lu;0;L;;;;;N;;;;ABB2;
+13E3;CHEROKEE LETTER TSA;Lu;0;L;;;;;N;;;;ABB3;
+13E4;CHEROKEE LETTER TSE;Lu;0;L;;;;;N;;;;ABB4;
+13E5;CHEROKEE LETTER TSI;Lu;0;L;;;;;N;;;;ABB5;
+13E6;CHEROKEE LETTER TSO;Lu;0;L;;;;;N;;;;ABB6;
+13E7;CHEROKEE LETTER TSU;Lu;0;L;;;;;N;;;;ABB7;
+13E8;CHEROKEE LETTER TSV;Lu;0;L;;;;;N;;;;ABB8;
+13E9;CHEROKEE LETTER WA;Lu;0;L;;;;;N;;;;ABB9;
+13EA;CHEROKEE LETTER WE;Lu;0;L;;;;;N;;;;ABBA;
+13EB;CHEROKEE LETTER WI;Lu;0;L;;;;;N;;;;ABBB;
+13EC;CHEROKEE LETTER WO;Lu;0;L;;;;;N;;;;ABBC;
+13ED;CHEROKEE LETTER WU;Lu;0;L;;;;;N;;;;ABBD;
+13EE;CHEROKEE LETTER WV;Lu;0;L;;;;;N;;;;ABBE;
+13EF;CHEROKEE LETTER YA;Lu;0;L;;;;;N;;;;ABBF;
+13F0;CHEROKEE LETTER YE;Lu;0;L;;;;;N;;;;13F8;
+13F1;CHEROKEE LETTER YI;Lu;0;L;;;;;N;;;;13F9;
+13F2;CHEROKEE LETTER YO;Lu;0;L;;;;;N;;;;13FA;
+13F3;CHEROKEE LETTER YU;Lu;0;L;;;;;N;;;;13FB;
+13F4;CHEROKEE LETTER YV;Lu;0;L;;;;;N;;;;13FC;
+13F5;CHEROKEE LETTER MV;Lu;0;L;;;;;N;;;;13FD;
+13F8;CHEROKEE SMALL LETTER YE;Ll;0;L;;;;;N;;;13F0;;13F0
+13F9;CHEROKEE SMALL LETTER YI;Ll;0;L;;;;;N;;;13F1;;13F1
+13FA;CHEROKEE SMALL LETTER YO;Ll;0;L;;;;;N;;;13F2;;13F2
+13FB;CHEROKEE SMALL LETTER YU;Ll;0;L;;;;;N;;;13F3;;13F3
+13FC;CHEROKEE SMALL LETTER YV;Ll;0;L;;;;;N;;;13F4;;13F4
+13FD;CHEROKEE SMALL LETTER MV;Ll;0;L;;;;;N;;;13F5;;13F5
+1400;CANADIAN SYLLABICS HYPHEN;Pd;0;ON;;;;;N;;;;;
+1401;CANADIAN SYLLABICS E;Lo;0;L;;;;;N;;;;;
+1402;CANADIAN SYLLABICS AAI;Lo;0;L;;;;;N;;;;;
+1403;CANADIAN SYLLABICS I;Lo;0;L;;;;;N;;;;;
+1404;CANADIAN SYLLABICS II;Lo;0;L;;;;;N;;;;;
+1405;CANADIAN SYLLABICS O;Lo;0;L;;;;;N;;;;;
+1406;CANADIAN SYLLABICS OO;Lo;0;L;;;;;N;;;;;
+1407;CANADIAN SYLLABICS Y-CREE OO;Lo;0;L;;;;;N;;;;;
+1408;CANADIAN SYLLABICS CARRIER EE;Lo;0;L;;;;;N;;;;;
+1409;CANADIAN SYLLABICS CARRIER I;Lo;0;L;;;;;N;;;;;
+140A;CANADIAN SYLLABICS A;Lo;0;L;;;;;N;;;;;
+140B;CANADIAN SYLLABICS AA;Lo;0;L;;;;;N;;;;;
+140C;CANADIAN SYLLABICS WE;Lo;0;L;;;;;N;;;;;
+140D;CANADIAN SYLLABICS WEST-CREE WE;Lo;0;L;;;;;N;;;;;
+140E;CANADIAN SYLLABICS WI;Lo;0;L;;;;;N;;;;;
+140F;CANADIAN SYLLABICS WEST-CREE WI;Lo;0;L;;;;;N;;;;;
+1410;CANADIAN SYLLABICS WII;Lo;0;L;;;;;N;;;;;
+1411;CANADIAN SYLLABICS WEST-CREE WII;Lo;0;L;;;;;N;;;;;
+1412;CANADIAN SYLLABICS WO;Lo;0;L;;;;;N;;;;;
+1413;CANADIAN SYLLABICS WEST-CREE WO;Lo;0;L;;;;;N;;;;;
+1414;CANADIAN SYLLABICS WOO;Lo;0;L;;;;;N;;;;;
+1415;CANADIAN SYLLABICS WEST-CREE WOO;Lo;0;L;;;;;N;;;;;
+1416;CANADIAN SYLLABICS NASKAPI WOO;Lo;0;L;;;;;N;;;;;
+1417;CANADIAN SYLLABICS WA;Lo;0;L;;;;;N;;;;;
+1418;CANADIAN SYLLABICS WEST-CREE WA;Lo;0;L;;;;;N;;;;;
+1419;CANADIAN SYLLABICS WAA;Lo;0;L;;;;;N;;;;;
+141A;CANADIAN SYLLABICS WEST-CREE WAA;Lo;0;L;;;;;N;;;;;
+141B;CANADIAN SYLLABICS NASKAPI WAA;Lo;0;L;;;;;N;;;;;
+141C;CANADIAN SYLLABICS AI;Lo;0;L;;;;;N;;;;;
+141D;CANADIAN SYLLABICS Y-CREE W;Lo;0;L;;;;;N;;;;;
+141E;CANADIAN SYLLABICS GLOTTAL STOP;Lo;0;L;;;;;N;;;;;
+141F;CANADIAN SYLLABICS FINAL ACUTE;Lo;0;L;;;;;N;;;;;
+1420;CANADIAN SYLLABICS FINAL GRAVE;Lo;0;L;;;;;N;;;;;
+1421;CANADIAN SYLLABICS FINAL BOTTOM HALF RING;Lo;0;L;;;;;N;;;;;
+1422;CANADIAN SYLLABICS FINAL TOP HALF RING;Lo;0;L;;;;;N;;;;;
+1423;CANADIAN SYLLABICS FINAL RIGHT HALF RING;Lo;0;L;;;;;N;;;;;
+1424;CANADIAN SYLLABICS FINAL RING;Lo;0;L;;;;;N;;;;;
+1425;CANADIAN SYLLABICS FINAL DOUBLE ACUTE;Lo;0;L;;;;;N;;;;;
+1426;CANADIAN SYLLABICS FINAL DOUBLE SHORT VERTICAL STROKES;Lo;0;L;;;;;N;;;;;
+1427;CANADIAN SYLLABICS FINAL MIDDLE DOT;Lo;0;L;;;;;N;;;;;
+1428;CANADIAN SYLLABICS FINAL SHORT HORIZONTAL STROKE;Lo;0;L;;;;;N;;;;;
+1429;CANADIAN SYLLABICS FINAL PLUS;Lo;0;L;;;;;N;;;;;
+142A;CANADIAN SYLLABICS FINAL DOWN TACK;Lo;0;L;;;;;N;;;;;
+142B;CANADIAN SYLLABICS EN;Lo;0;L;;;;;N;;;;;
+142C;CANADIAN SYLLABICS IN;Lo;0;L;;;;;N;;;;;
+142D;CANADIAN SYLLABICS ON;Lo;0;L;;;;;N;;;;;
+142E;CANADIAN SYLLABICS AN;Lo;0;L;;;;;N;;;;;
+142F;CANADIAN SYLLABICS PE;Lo;0;L;;;;;N;;;;;
+1430;CANADIAN SYLLABICS PAAI;Lo;0;L;;;;;N;;;;;
+1431;CANADIAN SYLLABICS PI;Lo;0;L;;;;;N;;;;;
+1432;CANADIAN SYLLABICS PII;Lo;0;L;;;;;N;;;;;
+1433;CANADIAN SYLLABICS PO;Lo;0;L;;;;;N;;;;;
+1434;CANADIAN SYLLABICS POO;Lo;0;L;;;;;N;;;;;
+1435;CANADIAN SYLLABICS Y-CREE POO;Lo;0;L;;;;;N;;;;;
+1436;CANADIAN SYLLABICS CARRIER HEE;Lo;0;L;;;;;N;;;;;
+1437;CANADIAN SYLLABICS CARRIER HI;Lo;0;L;;;;;N;;;;;
+1438;CANADIAN SYLLABICS PA;Lo;0;L;;;;;N;;;;;
+1439;CANADIAN SYLLABICS PAA;Lo;0;L;;;;;N;;;;;
+143A;CANADIAN SYLLABICS PWE;Lo;0;L;;;;;N;;;;;
+143B;CANADIAN SYLLABICS WEST-CREE PWE;Lo;0;L;;;;;N;;;;;
+143C;CANADIAN SYLLABICS PWI;Lo;0;L;;;;;N;;;;;
+143D;CANADIAN SYLLABICS WEST-CREE PWI;Lo;0;L;;;;;N;;;;;
+143E;CANADIAN SYLLABICS PWII;Lo;0;L;;;;;N;;;;;
+143F;CANADIAN SYLLABICS WEST-CREE PWII;Lo;0;L;;;;;N;;;;;
+1440;CANADIAN SYLLABICS PWO;Lo;0;L;;;;;N;;;;;
+1441;CANADIAN SYLLABICS WEST-CREE PWO;Lo;0;L;;;;;N;;;;;
+1442;CANADIAN SYLLABICS PWOO;Lo;0;L;;;;;N;;;;;
+1443;CANADIAN SYLLABICS WEST-CREE PWOO;Lo;0;L;;;;;N;;;;;
+1444;CANADIAN SYLLABICS PWA;Lo;0;L;;;;;N;;;;;
+1445;CANADIAN SYLLABICS WEST-CREE PWA;Lo;0;L;;;;;N;;;;;
+1446;CANADIAN SYLLABICS PWAA;Lo;0;L;;;;;N;;;;;
+1447;CANADIAN SYLLABICS WEST-CREE PWAA;Lo;0;L;;;;;N;;;;;
+1448;CANADIAN SYLLABICS Y-CREE PWAA;Lo;0;L;;;;;N;;;;;
+1449;CANADIAN SYLLABICS P;Lo;0;L;;;;;N;;;;;
+144A;CANADIAN SYLLABICS WEST-CREE P;Lo;0;L;;;;;N;;;;;
+144B;CANADIAN SYLLABICS CARRIER H;Lo;0;L;;;;;N;;;;;
+144C;CANADIAN SYLLABICS TE;Lo;0;L;;;;;N;;;;;
+144D;CANADIAN SYLLABICS TAAI;Lo;0;L;;;;;N;;;;;
+144E;CANADIAN SYLLABICS TI;Lo;0;L;;;;;N;;;;;
+144F;CANADIAN SYLLABICS TII;Lo;0;L;;;;;N;;;;;
+1450;CANADIAN SYLLABICS TO;Lo;0;L;;;;;N;;;;;
+1451;CANADIAN SYLLABICS TOO;Lo;0;L;;;;;N;;;;;
+1452;CANADIAN SYLLABICS Y-CREE TOO;Lo;0;L;;;;;N;;;;;
+1453;CANADIAN SYLLABICS CARRIER DEE;Lo;0;L;;;;;N;;;;;
+1454;CANADIAN SYLLABICS CARRIER DI;Lo;0;L;;;;;N;;;;;
+1455;CANADIAN SYLLABICS TA;Lo;0;L;;;;;N;;;;;
+1456;CANADIAN SYLLABICS TAA;Lo;0;L;;;;;N;;;;;
+1457;CANADIAN SYLLABICS TWE;Lo;0;L;;;;;N;;;;;
+1458;CANADIAN SYLLABICS WEST-CREE TWE;Lo;0;L;;;;;N;;;;;
+1459;CANADIAN SYLLABICS TWI;Lo;0;L;;;;;N;;;;;
+145A;CANADIAN SYLLABICS WEST-CREE TWI;Lo;0;L;;;;;N;;;;;
+145B;CANADIAN SYLLABICS TWII;Lo;0;L;;;;;N;;;;;
+145C;CANADIAN SYLLABICS WEST-CREE TWII;Lo;0;L;;;;;N;;;;;
+145D;CANADIAN SYLLABICS TWO;Lo;0;L;;;;;N;;;;;
+145E;CANADIAN SYLLABICS WEST-CREE TWO;Lo;0;L;;;;;N;;;;;
+145F;CANADIAN SYLLABICS TWOO;Lo;0;L;;;;;N;;;;;
+1460;CANADIAN SYLLABICS WEST-CREE TWOO;Lo;0;L;;;;;N;;;;;
+1461;CANADIAN SYLLABICS TWA;Lo;0;L;;;;;N;;;;;
+1462;CANADIAN SYLLABICS WEST-CREE TWA;Lo;0;L;;;;;N;;;;;
+1463;CANADIAN SYLLABICS TWAA;Lo;0;L;;;;;N;;;;;
+1464;CANADIAN SYLLABICS WEST-CREE TWAA;Lo;0;L;;;;;N;;;;;
+1465;CANADIAN SYLLABICS NASKAPI TWAA;Lo;0;L;;;;;N;;;;;
+1466;CANADIAN SYLLABICS T;Lo;0;L;;;;;N;;;;;
+1467;CANADIAN SYLLABICS TTE;Lo;0;L;;;;;N;;;;;
+1468;CANADIAN SYLLABICS TTI;Lo;0;L;;;;;N;;;;;
+1469;CANADIAN SYLLABICS TTO;Lo;0;L;;;;;N;;;;;
+146A;CANADIAN SYLLABICS TTA;Lo;0;L;;;;;N;;;;;
+146B;CANADIAN SYLLABICS KE;Lo;0;L;;;;;N;;;;;
+146C;CANADIAN SYLLABICS KAAI;Lo;0;L;;;;;N;;;;;
+146D;CANADIAN SYLLABICS KI;Lo;0;L;;;;;N;;;;;
+146E;CANADIAN SYLLABICS KII;Lo;0;L;;;;;N;;;;;
+146F;CANADIAN SYLLABICS KO;Lo;0;L;;;;;N;;;;;
+1470;CANADIAN SYLLABICS KOO;Lo;0;L;;;;;N;;;;;
+1471;CANADIAN SYLLABICS Y-CREE KOO;Lo;0;L;;;;;N;;;;;
+1472;CANADIAN SYLLABICS KA;Lo;0;L;;;;;N;;;;;
+1473;CANADIAN SYLLABICS KAA;Lo;0;L;;;;;N;;;;;
+1474;CANADIAN SYLLABICS KWE;Lo;0;L;;;;;N;;;;;
+1475;CANADIAN SYLLABICS WEST-CREE KWE;Lo;0;L;;;;;N;;;;;
+1476;CANADIAN SYLLABICS KWI;Lo;0;L;;;;;N;;;;;
+1477;CANADIAN SYLLABICS WEST-CREE KWI;Lo;0;L;;;;;N;;;;;
+1478;CANADIAN SYLLABICS KWII;Lo;0;L;;;;;N;;;;;
+1479;CANADIAN SYLLABICS WEST-CREE KWII;Lo;0;L;;;;;N;;;;;
+147A;CANADIAN SYLLABICS KWO;Lo;0;L;;;;;N;;;;;
+147B;CANADIAN SYLLABICS WEST-CREE KWO;Lo;0;L;;;;;N;;;;;
+147C;CANADIAN SYLLABICS KWOO;Lo;0;L;;;;;N;;;;;
+147D;CANADIAN SYLLABICS WEST-CREE KWOO;Lo;0;L;;;;;N;;;;;
+147E;CANADIAN SYLLABICS KWA;Lo;0;L;;;;;N;;;;;
+147F;CANADIAN SYLLABICS WEST-CREE KWA;Lo;0;L;;;;;N;;;;;
+1480;CANADIAN SYLLABICS KWAA;Lo;0;L;;;;;N;;;;;
+1481;CANADIAN SYLLABICS WEST-CREE KWAA;Lo;0;L;;;;;N;;;;;
+1482;CANADIAN SYLLABICS NASKAPI KWAA;Lo;0;L;;;;;N;;;;;
+1483;CANADIAN SYLLABICS K;Lo;0;L;;;;;N;;;;;
+1484;CANADIAN SYLLABICS KW;Lo;0;L;;;;;N;;;;;
+1485;CANADIAN SYLLABICS SOUTH-SLAVEY KEH;Lo;0;L;;;;;N;;;;;
+1486;CANADIAN SYLLABICS SOUTH-SLAVEY KIH;Lo;0;L;;;;;N;;;;;
+1487;CANADIAN SYLLABICS SOUTH-SLAVEY KOH;Lo;0;L;;;;;N;;;;;
+1488;CANADIAN SYLLABICS SOUTH-SLAVEY KAH;Lo;0;L;;;;;N;;;;;
+1489;CANADIAN SYLLABICS CE;Lo;0;L;;;;;N;;;;;
+148A;CANADIAN SYLLABICS CAAI;Lo;0;L;;;;;N;;;;;
+148B;CANADIAN SYLLABICS CI;Lo;0;L;;;;;N;;;;;
+148C;CANADIAN SYLLABICS CII;Lo;0;L;;;;;N;;;;;
+148D;CANADIAN SYLLABICS CO;Lo;0;L;;;;;N;;;;;
+148E;CANADIAN SYLLABICS COO;Lo;0;L;;;;;N;;;;;
+148F;CANADIAN SYLLABICS Y-CREE COO;Lo;0;L;;;;;N;;;;;
+1490;CANADIAN SYLLABICS CA;Lo;0;L;;;;;N;;;;;
+1491;CANADIAN SYLLABICS CAA;Lo;0;L;;;;;N;;;;;
+1492;CANADIAN SYLLABICS CWE;Lo;0;L;;;;;N;;;;;
+1493;CANADIAN SYLLABICS WEST-CREE CWE;Lo;0;L;;;;;N;;;;;
+1494;CANADIAN SYLLABICS CWI;Lo;0;L;;;;;N;;;;;
+1495;CANADIAN SYLLABICS WEST-CREE CWI;Lo;0;L;;;;;N;;;;;
+1496;CANADIAN SYLLABICS CWII;Lo;0;L;;;;;N;;;;;
+1497;CANADIAN SYLLABICS WEST-CREE CWII;Lo;0;L;;;;;N;;;;;
+1498;CANADIAN SYLLABICS CWO;Lo;0;L;;;;;N;;;;;
+1499;CANADIAN SYLLABICS WEST-CREE CWO;Lo;0;L;;;;;N;;;;;
+149A;CANADIAN SYLLABICS CWOO;Lo;0;L;;;;;N;;;;;
+149B;CANADIAN SYLLABICS WEST-CREE CWOO;Lo;0;L;;;;;N;;;;;
+149C;CANADIAN SYLLABICS CWA;Lo;0;L;;;;;N;;;;;
+149D;CANADIAN SYLLABICS WEST-CREE CWA;Lo;0;L;;;;;N;;;;;
+149E;CANADIAN SYLLABICS CWAA;Lo;0;L;;;;;N;;;;;
+149F;CANADIAN SYLLABICS WEST-CREE CWAA;Lo;0;L;;;;;N;;;;;
+14A0;CANADIAN SYLLABICS NASKAPI CWAA;Lo;0;L;;;;;N;;;;;
+14A1;CANADIAN SYLLABICS C;Lo;0;L;;;;;N;;;;;
+14A2;CANADIAN SYLLABICS SAYISI TH;Lo;0;L;;;;;N;;;;;
+14A3;CANADIAN SYLLABICS ME;Lo;0;L;;;;;N;;;;;
+14A4;CANADIAN SYLLABICS MAAI;Lo;0;L;;;;;N;;;;;
+14A5;CANADIAN SYLLABICS MI;Lo;0;L;;;;;N;;;;;
+14A6;CANADIAN SYLLABICS MII;Lo;0;L;;;;;N;;;;;
+14A7;CANADIAN SYLLABICS MO;Lo;0;L;;;;;N;;;;;
+14A8;CANADIAN SYLLABICS MOO;Lo;0;L;;;;;N;;;;;
+14A9;CANADIAN SYLLABICS Y-CREE MOO;Lo;0;L;;;;;N;;;;;
+14AA;CANADIAN SYLLABICS MA;Lo;0;L;;;;;N;;;;;
+14AB;CANADIAN SYLLABICS MAA;Lo;0;L;;;;;N;;;;;
+14AC;CANADIAN SYLLABICS MWE;Lo;0;L;;;;;N;;;;;
+14AD;CANADIAN SYLLABICS WEST-CREE MWE;Lo;0;L;;;;;N;;;;;
+14AE;CANADIAN SYLLABICS MWI;Lo;0;L;;;;;N;;;;;
+14AF;CANADIAN SYLLABICS WEST-CREE MWI;Lo;0;L;;;;;N;;;;;
+14B0;CANADIAN SYLLABICS MWII;Lo;0;L;;;;;N;;;;;
+14B1;CANADIAN SYLLABICS WEST-CREE MWII;Lo;0;L;;;;;N;;;;;
+14B2;CANADIAN SYLLABICS MWO;Lo;0;L;;;;;N;;;;;
+14B3;CANADIAN SYLLABICS WEST-CREE MWO;Lo;0;L;;;;;N;;;;;
+14B4;CANADIAN SYLLABICS MWOO;Lo;0;L;;;;;N;;;;;
+14B5;CANADIAN SYLLABICS WEST-CREE MWOO;Lo;0;L;;;;;N;;;;;
+14B6;CANADIAN SYLLABICS MWA;Lo;0;L;;;;;N;;;;;
+14B7;CANADIAN SYLLABICS WEST-CREE MWA;Lo;0;L;;;;;N;;;;;
+14B8;CANADIAN SYLLABICS MWAA;Lo;0;L;;;;;N;;;;;
+14B9;CANADIAN SYLLABICS WEST-CREE MWAA;Lo;0;L;;;;;N;;;;;
+14BA;CANADIAN SYLLABICS NASKAPI MWAA;Lo;0;L;;;;;N;;;;;
+14BB;CANADIAN SYLLABICS M;Lo;0;L;;;;;N;;;;;
+14BC;CANADIAN SYLLABICS WEST-CREE M;Lo;0;L;;;;;N;;;;;
+14BD;CANADIAN SYLLABICS MH;Lo;0;L;;;;;N;;;;;
+14BE;CANADIAN SYLLABICS ATHAPASCAN M;Lo;0;L;;;;;N;;;;;
+14BF;CANADIAN SYLLABICS SAYISI M;Lo;0;L;;;;;N;;;;;
+14C0;CANADIAN SYLLABICS NE;Lo;0;L;;;;;N;;;;;
+14C1;CANADIAN SYLLABICS NAAI;Lo;0;L;;;;;N;;;;;
+14C2;CANADIAN SYLLABICS NI;Lo;0;L;;;;;N;;;;;
+14C3;CANADIAN SYLLABICS NII;Lo;0;L;;;;;N;;;;;
+14C4;CANADIAN SYLLABICS NO;Lo;0;L;;;;;N;;;;;
+14C5;CANADIAN SYLLABICS NOO;Lo;0;L;;;;;N;;;;;
+14C6;CANADIAN SYLLABICS Y-CREE NOO;Lo;0;L;;;;;N;;;;;
+14C7;CANADIAN SYLLABICS NA;Lo;0;L;;;;;N;;;;;
+14C8;CANADIAN SYLLABICS NAA;Lo;0;L;;;;;N;;;;;
+14C9;CANADIAN SYLLABICS NWE;Lo;0;L;;;;;N;;;;;
+14CA;CANADIAN SYLLABICS WEST-CREE NWE;Lo;0;L;;;;;N;;;;;
+14CB;CANADIAN SYLLABICS NWA;Lo;0;L;;;;;N;;;;;
+14CC;CANADIAN SYLLABICS WEST-CREE NWA;Lo;0;L;;;;;N;;;;;
+14CD;CANADIAN SYLLABICS NWAA;Lo;0;L;;;;;N;;;;;
+14CE;CANADIAN SYLLABICS WEST-CREE NWAA;Lo;0;L;;;;;N;;;;;
+14CF;CANADIAN SYLLABICS NASKAPI NWAA;Lo;0;L;;;;;N;;;;;
+14D0;CANADIAN SYLLABICS N;Lo;0;L;;;;;N;;;;;
+14D1;CANADIAN SYLLABICS CARRIER NG;Lo;0;L;;;;;N;;;;;
+14D2;CANADIAN SYLLABICS NH;Lo;0;L;;;;;N;;;;;
+14D3;CANADIAN SYLLABICS LE;Lo;0;L;;;;;N;;;;;
+14D4;CANADIAN SYLLABICS LAAI;Lo;0;L;;;;;N;;;;;
+14D5;CANADIAN SYLLABICS LI;Lo;0;L;;;;;N;;;;;
+14D6;CANADIAN SYLLABICS LII;Lo;0;L;;;;;N;;;;;
+14D7;CANADIAN SYLLABICS LO;Lo;0;L;;;;;N;;;;;
+14D8;CANADIAN SYLLABICS LOO;Lo;0;L;;;;;N;;;;;
+14D9;CANADIAN SYLLABICS Y-CREE LOO;Lo;0;L;;;;;N;;;;;
+14DA;CANADIAN SYLLABICS LA;Lo;0;L;;;;;N;;;;;
+14DB;CANADIAN SYLLABICS LAA;Lo;0;L;;;;;N;;;;;
+14DC;CANADIAN SYLLABICS LWE;Lo;0;L;;;;;N;;;;;
+14DD;CANADIAN SYLLABICS WEST-CREE LWE;Lo;0;L;;;;;N;;;;;
+14DE;CANADIAN SYLLABICS LWI;Lo;0;L;;;;;N;;;;;
+14DF;CANADIAN SYLLABICS WEST-CREE LWI;Lo;0;L;;;;;N;;;;;
+14E0;CANADIAN SYLLABICS LWII;Lo;0;L;;;;;N;;;;;
+14E1;CANADIAN SYLLABICS WEST-CREE LWII;Lo;0;L;;;;;N;;;;;
+14E2;CANADIAN SYLLABICS LWO;Lo;0;L;;;;;N;;;;;
+14E3;CANADIAN SYLLABICS WEST-CREE LWO;Lo;0;L;;;;;N;;;;;
+14E4;CANADIAN SYLLABICS LWOO;Lo;0;L;;;;;N;;;;;
+14E5;CANADIAN SYLLABICS WEST-CREE LWOO;Lo;0;L;;;;;N;;;;;
+14E6;CANADIAN SYLLABICS LWA;Lo;0;L;;;;;N;;;;;
+14E7;CANADIAN SYLLABICS WEST-CREE LWA;Lo;0;L;;;;;N;;;;;
+14E8;CANADIAN SYLLABICS LWAA;Lo;0;L;;;;;N;;;;;
+14E9;CANADIAN SYLLABICS WEST-CREE LWAA;Lo;0;L;;;;;N;;;;;
+14EA;CANADIAN SYLLABICS L;Lo;0;L;;;;;N;;;;;
+14EB;CANADIAN SYLLABICS WEST-CREE L;Lo;0;L;;;;;N;;;;;
+14EC;CANADIAN SYLLABICS MEDIAL L;Lo;0;L;;;;;N;;;;;
+14ED;CANADIAN SYLLABICS SE;Lo;0;L;;;;;N;;;;;
+14EE;CANADIAN SYLLABICS SAAI;Lo;0;L;;;;;N;;;;;
+14EF;CANADIAN SYLLABICS SI;Lo;0;L;;;;;N;;;;;
+14F0;CANADIAN SYLLABICS SII;Lo;0;L;;;;;N;;;;;
+14F1;CANADIAN SYLLABICS SO;Lo;0;L;;;;;N;;;;;
+14F2;CANADIAN SYLLABICS SOO;Lo;0;L;;;;;N;;;;;
+14F3;CANADIAN SYLLABICS Y-CREE SOO;Lo;0;L;;;;;N;;;;;
+14F4;CANADIAN SYLLABICS SA;Lo;0;L;;;;;N;;;;;
+14F5;CANADIAN SYLLABICS SAA;Lo;0;L;;;;;N;;;;;
+14F6;CANADIAN SYLLABICS SWE;Lo;0;L;;;;;N;;;;;
+14F7;CANADIAN SYLLABICS WEST-CREE SWE;Lo;0;L;;;;;N;;;;;
+14F8;CANADIAN SYLLABICS SWI;Lo;0;L;;;;;N;;;;;
+14F9;CANADIAN SYLLABICS WEST-CREE SWI;Lo;0;L;;;;;N;;;;;
+14FA;CANADIAN SYLLABICS SWII;Lo;0;L;;;;;N;;;;;
+14FB;CANADIAN SYLLABICS WEST-CREE SWII;Lo;0;L;;;;;N;;;;;
+14FC;CANADIAN SYLLABICS SWO;Lo;0;L;;;;;N;;;;;
+14FD;CANADIAN SYLLABICS WEST-CREE SWO;Lo;0;L;;;;;N;;;;;
+14FE;CANADIAN SYLLABICS SWOO;Lo;0;L;;;;;N;;;;;
+14FF;CANADIAN SYLLABICS WEST-CREE SWOO;Lo;0;L;;;;;N;;;;;
+1500;CANADIAN SYLLABICS SWA;Lo;0;L;;;;;N;;;;;
+1501;CANADIAN SYLLABICS WEST-CREE SWA;Lo;0;L;;;;;N;;;;;
+1502;CANADIAN SYLLABICS SWAA;Lo;0;L;;;;;N;;;;;
+1503;CANADIAN SYLLABICS WEST-CREE SWAA;Lo;0;L;;;;;N;;;;;
+1504;CANADIAN SYLLABICS NASKAPI SWAA;Lo;0;L;;;;;N;;;;;
+1505;CANADIAN SYLLABICS S;Lo;0;L;;;;;N;;;;;
+1506;CANADIAN SYLLABICS ATHAPASCAN S;Lo;0;L;;;;;N;;;;;
+1507;CANADIAN SYLLABICS SW;Lo;0;L;;;;;N;;;;;
+1508;CANADIAN SYLLABICS BLACKFOOT S;Lo;0;L;;;;;N;;;;;
+1509;CANADIAN SYLLABICS MOOSE-CREE SK;Lo;0;L;;;;;N;;;;;
+150A;CANADIAN SYLLABICS NASKAPI SKW;Lo;0;L;;;;;N;;;;;
+150B;CANADIAN SYLLABICS NASKAPI S-W;Lo;0;L;;;;;N;;;;;
+150C;CANADIAN SYLLABICS NASKAPI SPWA;Lo;0;L;;;;;N;;;;;
+150D;CANADIAN SYLLABICS NASKAPI STWA;Lo;0;L;;;;;N;;;;;
+150E;CANADIAN SYLLABICS NASKAPI SKWA;Lo;0;L;;;;;N;;;;;
+150F;CANADIAN SYLLABICS NASKAPI SCWA;Lo;0;L;;;;;N;;;;;
+1510;CANADIAN SYLLABICS SHE;Lo;0;L;;;;;N;;;;;
+1511;CANADIAN SYLLABICS SHI;Lo;0;L;;;;;N;;;;;
+1512;CANADIAN SYLLABICS SHII;Lo;0;L;;;;;N;;;;;
+1513;CANADIAN SYLLABICS SHO;Lo;0;L;;;;;N;;;;;
+1514;CANADIAN SYLLABICS SHOO;Lo;0;L;;;;;N;;;;;
+1515;CANADIAN SYLLABICS SHA;Lo;0;L;;;;;N;;;;;
+1516;CANADIAN SYLLABICS SHAA;Lo;0;L;;;;;N;;;;;
+1517;CANADIAN SYLLABICS SHWE;Lo;0;L;;;;;N;;;;;
+1518;CANADIAN SYLLABICS WEST-CREE SHWE;Lo;0;L;;;;;N;;;;;
+1519;CANADIAN SYLLABICS SHWI;Lo;0;L;;;;;N;;;;;
+151A;CANADIAN SYLLABICS WEST-CREE SHWI;Lo;0;L;;;;;N;;;;;
+151B;CANADIAN SYLLABICS SHWII;Lo;0;L;;;;;N;;;;;
+151C;CANADIAN SYLLABICS WEST-CREE SHWII;Lo;0;L;;;;;N;;;;;
+151D;CANADIAN SYLLABICS SHWO;Lo;0;L;;;;;N;;;;;
+151E;CANADIAN SYLLABICS WEST-CREE SHWO;Lo;0;L;;;;;N;;;;;
+151F;CANADIAN SYLLABICS SHWOO;Lo;0;L;;;;;N;;;;;
+1520;CANADIAN SYLLABICS WEST-CREE SHWOO;Lo;0;L;;;;;N;;;;;
+1521;CANADIAN SYLLABICS SHWA;Lo;0;L;;;;;N;;;;;
+1522;CANADIAN SYLLABICS WEST-CREE SHWA;Lo;0;L;;;;;N;;;;;
+1523;CANADIAN SYLLABICS SHWAA;Lo;0;L;;;;;N;;;;;
+1524;CANADIAN SYLLABICS WEST-CREE SHWAA;Lo;0;L;;;;;N;;;;;
+1525;CANADIAN SYLLABICS SH;Lo;0;L;;;;;N;;;;;
+1526;CANADIAN SYLLABICS YE;Lo;0;L;;;;;N;;;;;
+1527;CANADIAN SYLLABICS YAAI;Lo;0;L;;;;;N;;;;;
+1528;CANADIAN SYLLABICS YI;Lo;0;L;;;;;N;;;;;
+1529;CANADIAN SYLLABICS YII;Lo;0;L;;;;;N;;;;;
+152A;CANADIAN SYLLABICS YO;Lo;0;L;;;;;N;;;;;
+152B;CANADIAN SYLLABICS YOO;Lo;0;L;;;;;N;;;;;
+152C;CANADIAN SYLLABICS Y-CREE YOO;Lo;0;L;;;;;N;;;;;
+152D;CANADIAN SYLLABICS YA;Lo;0;L;;;;;N;;;;;
+152E;CANADIAN SYLLABICS YAA;Lo;0;L;;;;;N;;;;;
+152F;CANADIAN SYLLABICS YWE;Lo;0;L;;;;;N;;;;;
+1530;CANADIAN SYLLABICS WEST-CREE YWE;Lo;0;L;;;;;N;;;;;
+1531;CANADIAN SYLLABICS YWI;Lo;0;L;;;;;N;;;;;
+1532;CANADIAN SYLLABICS WEST-CREE YWI;Lo;0;L;;;;;N;;;;;
+1533;CANADIAN SYLLABICS YWII;Lo;0;L;;;;;N;;;;;
+1534;CANADIAN SYLLABICS WEST-CREE YWII;Lo;0;L;;;;;N;;;;;
+1535;CANADIAN SYLLABICS YWO;Lo;0;L;;;;;N;;;;;
+1536;CANADIAN SYLLABICS WEST-CREE YWO;Lo;0;L;;;;;N;;;;;
+1537;CANADIAN SYLLABICS YWOO;Lo;0;L;;;;;N;;;;;
+1538;CANADIAN SYLLABICS WEST-CREE YWOO;Lo;0;L;;;;;N;;;;;
+1539;CANADIAN SYLLABICS YWA;Lo;0;L;;;;;N;;;;;
+153A;CANADIAN SYLLABICS WEST-CREE YWA;Lo;0;L;;;;;N;;;;;
+153B;CANADIAN SYLLABICS YWAA;Lo;0;L;;;;;N;;;;;
+153C;CANADIAN SYLLABICS WEST-CREE YWAA;Lo;0;L;;;;;N;;;;;
+153D;CANADIAN SYLLABICS NASKAPI YWAA;Lo;0;L;;;;;N;;;;;
+153E;CANADIAN SYLLABICS Y;Lo;0;L;;;;;N;;;;;
+153F;CANADIAN SYLLABICS BIBLE-CREE Y;Lo;0;L;;;;;N;;;;;
+1540;CANADIAN SYLLABICS WEST-CREE Y;Lo;0;L;;;;;N;;;;;
+1541;CANADIAN SYLLABICS SAYISI YI;Lo;0;L;;;;;N;;;;;
+1542;CANADIAN SYLLABICS RE;Lo;0;L;;;;;N;;;;;
+1543;CANADIAN SYLLABICS R-CREE RE;Lo;0;L;;;;;N;;;;;
+1544;CANADIAN SYLLABICS WEST-CREE LE;Lo;0;L;;;;;N;;;;;
+1545;CANADIAN SYLLABICS RAAI;Lo;0;L;;;;;N;;;;;
+1546;CANADIAN SYLLABICS RI;Lo;0;L;;;;;N;;;;;
+1547;CANADIAN SYLLABICS RII;Lo;0;L;;;;;N;;;;;
+1548;CANADIAN SYLLABICS RO;Lo;0;L;;;;;N;;;;;
+1549;CANADIAN SYLLABICS ROO;Lo;0;L;;;;;N;;;;;
+154A;CANADIAN SYLLABICS WEST-CREE LO;Lo;0;L;;;;;N;;;;;
+154B;CANADIAN SYLLABICS RA;Lo;0;L;;;;;N;;;;;
+154C;CANADIAN SYLLABICS RAA;Lo;0;L;;;;;N;;;;;
+154D;CANADIAN SYLLABICS WEST-CREE LA;Lo;0;L;;;;;N;;;;;
+154E;CANADIAN SYLLABICS RWAA;Lo;0;L;;;;;N;;;;;
+154F;CANADIAN SYLLABICS WEST-CREE RWAA;Lo;0;L;;;;;N;;;;;
+1550;CANADIAN SYLLABICS R;Lo;0;L;;;;;N;;;;;
+1551;CANADIAN SYLLABICS WEST-CREE R;Lo;0;L;;;;;N;;;;;
+1552;CANADIAN SYLLABICS MEDIAL R;Lo;0;L;;;;;N;;;;;
+1553;CANADIAN SYLLABICS FE;Lo;0;L;;;;;N;;;;;
+1554;CANADIAN SYLLABICS FAAI;Lo;0;L;;;;;N;;;;;
+1555;CANADIAN SYLLABICS FI;Lo;0;L;;;;;N;;;;;
+1556;CANADIAN SYLLABICS FII;Lo;0;L;;;;;N;;;;;
+1557;CANADIAN SYLLABICS FO;Lo;0;L;;;;;N;;;;;
+1558;CANADIAN SYLLABICS FOO;Lo;0;L;;;;;N;;;;;
+1559;CANADIAN SYLLABICS FA;Lo;0;L;;;;;N;;;;;
+155A;CANADIAN SYLLABICS FAA;Lo;0;L;;;;;N;;;;;
+155B;CANADIAN SYLLABICS FWAA;Lo;0;L;;;;;N;;;;;
+155C;CANADIAN SYLLABICS WEST-CREE FWAA;Lo;0;L;;;;;N;;;;;
+155D;CANADIAN SYLLABICS F;Lo;0;L;;;;;N;;;;;
+155E;CANADIAN SYLLABICS THE;Lo;0;L;;;;;N;;;;;
+155F;CANADIAN SYLLABICS N-CREE THE;Lo;0;L;;;;;N;;;;;
+1560;CANADIAN SYLLABICS THI;Lo;0;L;;;;;N;;;;;
+1561;CANADIAN SYLLABICS N-CREE THI;Lo;0;L;;;;;N;;;;;
+1562;CANADIAN SYLLABICS THII;Lo;0;L;;;;;N;;;;;
+1563;CANADIAN SYLLABICS N-CREE THII;Lo;0;L;;;;;N;;;;;
+1564;CANADIAN SYLLABICS THO;Lo;0;L;;;;;N;;;;;
+1565;CANADIAN SYLLABICS THOO;Lo;0;L;;;;;N;;;;;
+1566;CANADIAN SYLLABICS THA;Lo;0;L;;;;;N;;;;;
+1567;CANADIAN SYLLABICS THAA;Lo;0;L;;;;;N;;;;;
+1568;CANADIAN SYLLABICS THWAA;Lo;0;L;;;;;N;;;;;
+1569;CANADIAN SYLLABICS WEST-CREE THWAA;Lo;0;L;;;;;N;;;;;
+156A;CANADIAN SYLLABICS TH;Lo;0;L;;;;;N;;;;;
+156B;CANADIAN SYLLABICS TTHE;Lo;0;L;;;;;N;;;;;
+156C;CANADIAN SYLLABICS TTHI;Lo;0;L;;;;;N;;;;;
+156D;CANADIAN SYLLABICS TTHO;Lo;0;L;;;;;N;;;;;
+156E;CANADIAN SYLLABICS TTHA;Lo;0;L;;;;;N;;;;;
+156F;CANADIAN SYLLABICS TTH;Lo;0;L;;;;;N;;;;;
+1570;CANADIAN SYLLABICS TYE;Lo;0;L;;;;;N;;;;;
+1571;CANADIAN SYLLABICS TYI;Lo;0;L;;;;;N;;;;;
+1572;CANADIAN SYLLABICS TYO;Lo;0;L;;;;;N;;;;;
+1573;CANADIAN SYLLABICS TYA;Lo;0;L;;;;;N;;;;;
+1574;CANADIAN SYLLABICS NUNAVIK HE;Lo;0;L;;;;;N;;;;;
+1575;CANADIAN SYLLABICS NUNAVIK HI;Lo;0;L;;;;;N;;;;;
+1576;CANADIAN SYLLABICS NUNAVIK HII;Lo;0;L;;;;;N;;;;;
+1577;CANADIAN SYLLABICS NUNAVIK HO;Lo;0;L;;;;;N;;;;;
+1578;CANADIAN SYLLABICS NUNAVIK HOO;Lo;0;L;;;;;N;;;;;
+1579;CANADIAN SYLLABICS NUNAVIK HA;Lo;0;L;;;;;N;;;;;
+157A;CANADIAN SYLLABICS NUNAVIK HAA;Lo;0;L;;;;;N;;;;;
+157B;CANADIAN SYLLABICS NUNAVIK H;Lo;0;L;;;;;N;;;;;
+157C;CANADIAN SYLLABICS NUNAVUT H;Lo;0;L;;;;;N;;;;;
+157D;CANADIAN SYLLABICS HK;Lo;0;L;;;;;N;;;;;
+157E;CANADIAN SYLLABICS QAAI;Lo;0;L;;;;;N;;;;;
+157F;CANADIAN SYLLABICS QI;Lo;0;L;;;;;N;;;;;
+1580;CANADIAN SYLLABICS QII;Lo;0;L;;;;;N;;;;;
+1581;CANADIAN SYLLABICS QO;Lo;0;L;;;;;N;;;;;
+1582;CANADIAN SYLLABICS QOO;Lo;0;L;;;;;N;;;;;
+1583;CANADIAN SYLLABICS QA;Lo;0;L;;;;;N;;;;;
+1584;CANADIAN SYLLABICS QAA;Lo;0;L;;;;;N;;;;;
+1585;CANADIAN SYLLABICS Q;Lo;0;L;;;;;N;;;;;
+1586;CANADIAN SYLLABICS TLHE;Lo;0;L;;;;;N;;;;;
+1587;CANADIAN SYLLABICS TLHI;Lo;0;L;;;;;N;;;;;
+1588;CANADIAN SYLLABICS TLHO;Lo;0;L;;;;;N;;;;;
+1589;CANADIAN SYLLABICS TLHA;Lo;0;L;;;;;N;;;;;
+158A;CANADIAN SYLLABICS WEST-CREE RE;Lo;0;L;;;;;N;;;;;
+158B;CANADIAN SYLLABICS WEST-CREE RI;Lo;0;L;;;;;N;;;;;
+158C;CANADIAN SYLLABICS WEST-CREE RO;Lo;0;L;;;;;N;;;;;
+158D;CANADIAN SYLLABICS WEST-CREE RA;Lo;0;L;;;;;N;;;;;
+158E;CANADIAN SYLLABICS NGAAI;Lo;0;L;;;;;N;;;;;
+158F;CANADIAN SYLLABICS NGI;Lo;0;L;;;;;N;;;;;
+1590;CANADIAN SYLLABICS NGII;Lo;0;L;;;;;N;;;;;
+1591;CANADIAN SYLLABICS NGO;Lo;0;L;;;;;N;;;;;
+1592;CANADIAN SYLLABICS NGOO;Lo;0;L;;;;;N;;;;;
+1593;CANADIAN SYLLABICS NGA;Lo;0;L;;;;;N;;;;;
+1594;CANADIAN SYLLABICS NGAA;Lo;0;L;;;;;N;;;;;
+1595;CANADIAN SYLLABICS NG;Lo;0;L;;;;;N;;;;;
+1596;CANADIAN SYLLABICS NNG;Lo;0;L;;;;;N;;;;;
+1597;CANADIAN SYLLABICS SAYISI SHE;Lo;0;L;;;;;N;;;;;
+1598;CANADIAN SYLLABICS SAYISI SHI;Lo;0;L;;;;;N;;;;;
+1599;CANADIAN SYLLABICS SAYISI SHO;Lo;0;L;;;;;N;;;;;
+159A;CANADIAN SYLLABICS SAYISI SHA;Lo;0;L;;;;;N;;;;;
+159B;CANADIAN SYLLABICS WOODS-CREE THE;Lo;0;L;;;;;N;;;;;
+159C;CANADIAN SYLLABICS WOODS-CREE THI;Lo;0;L;;;;;N;;;;;
+159D;CANADIAN SYLLABICS WOODS-CREE THO;Lo;0;L;;;;;N;;;;;
+159E;CANADIAN SYLLABICS WOODS-CREE THA;Lo;0;L;;;;;N;;;;;
+159F;CANADIAN SYLLABICS WOODS-CREE TH;Lo;0;L;;;;;N;;;;;
+15A0;CANADIAN SYLLABICS LHI;Lo;0;L;;;;;N;;;;;
+15A1;CANADIAN SYLLABICS LHII;Lo;0;L;;;;;N;;;;;
+15A2;CANADIAN SYLLABICS LHO;Lo;0;L;;;;;N;;;;;
+15A3;CANADIAN SYLLABICS LHOO;Lo;0;L;;;;;N;;;;;
+15A4;CANADIAN SYLLABICS LHA;Lo;0;L;;;;;N;;;;;
+15A5;CANADIAN SYLLABICS LHAA;Lo;0;L;;;;;N;;;;;
+15A6;CANADIAN SYLLABICS LH;Lo;0;L;;;;;N;;;;;
+15A7;CANADIAN SYLLABICS TH-CREE THE;Lo;0;L;;;;;N;;;;;
+15A8;CANADIAN SYLLABICS TH-CREE THI;Lo;0;L;;;;;N;;;;;
+15A9;CANADIAN SYLLABICS TH-CREE THII;Lo;0;L;;;;;N;;;;;
+15AA;CANADIAN SYLLABICS TH-CREE THO;Lo;0;L;;;;;N;;;;;
+15AB;CANADIAN SYLLABICS TH-CREE THOO;Lo;0;L;;;;;N;;;;;
+15AC;CANADIAN SYLLABICS TH-CREE THA;Lo;0;L;;;;;N;;;;;
+15AD;CANADIAN SYLLABICS TH-CREE THAA;Lo;0;L;;;;;N;;;;;
+15AE;CANADIAN SYLLABICS TH-CREE TH;Lo;0;L;;;;;N;;;;;
+15AF;CANADIAN SYLLABICS AIVILIK B;Lo;0;L;;;;;N;;;;;
+15B0;CANADIAN SYLLABICS BLACKFOOT E;Lo;0;L;;;;;N;;;;;
+15B1;CANADIAN SYLLABICS BLACKFOOT I;Lo;0;L;;;;;N;;;;;
+15B2;CANADIAN SYLLABICS BLACKFOOT O;Lo;0;L;;;;;N;;;;;
+15B3;CANADIAN SYLLABICS BLACKFOOT A;Lo;0;L;;;;;N;;;;;
+15B4;CANADIAN SYLLABICS BLACKFOOT WE;Lo;0;L;;;;;N;;;;;
+15B5;CANADIAN SYLLABICS BLACKFOOT WI;Lo;0;L;;;;;N;;;;;
+15B6;CANADIAN SYLLABICS BLACKFOOT WO;Lo;0;L;;;;;N;;;;;
+15B7;CANADIAN SYLLABICS BLACKFOOT WA;Lo;0;L;;;;;N;;;;;
+15B8;CANADIAN SYLLABICS BLACKFOOT NE;Lo;0;L;;;;;N;;;;;
+15B9;CANADIAN SYLLABICS BLACKFOOT NI;Lo;0;L;;;;;N;;;;;
+15BA;CANADIAN SYLLABICS BLACKFOOT NO;Lo;0;L;;;;;N;;;;;
+15BB;CANADIAN SYLLABICS BLACKFOOT NA;Lo;0;L;;;;;N;;;;;
+15BC;CANADIAN SYLLABICS BLACKFOOT KE;Lo;0;L;;;;;N;;;;;
+15BD;CANADIAN SYLLABICS BLACKFOOT KI;Lo;0;L;;;;;N;;;;;
+15BE;CANADIAN SYLLABICS BLACKFOOT KO;Lo;0;L;;;;;N;;;;;
+15BF;CANADIAN SYLLABICS BLACKFOOT KA;Lo;0;L;;;;;N;;;;;
+15C0;CANADIAN SYLLABICS SAYISI HE;Lo;0;L;;;;;N;;;;;
+15C1;CANADIAN SYLLABICS SAYISI HI;Lo;0;L;;;;;N;;;;;
+15C2;CANADIAN SYLLABICS SAYISI HO;Lo;0;L;;;;;N;;;;;
+15C3;CANADIAN SYLLABICS SAYISI HA;Lo;0;L;;;;;N;;;;;
+15C4;CANADIAN SYLLABICS CARRIER GHU;Lo;0;L;;;;;N;;;;;
+15C5;CANADIAN SYLLABICS CARRIER GHO;Lo;0;L;;;;;N;;;;;
+15C6;CANADIAN SYLLABICS CARRIER GHE;Lo;0;L;;;;;N;;;;;
+15C7;CANADIAN SYLLABICS CARRIER GHEE;Lo;0;L;;;;;N;;;;;
+15C8;CANADIAN SYLLABICS CARRIER GHI;Lo;0;L;;;;;N;;;;;
+15C9;CANADIAN SYLLABICS CARRIER GHA;Lo;0;L;;;;;N;;;;;
+15CA;CANADIAN SYLLABICS CARRIER RU;Lo;0;L;;;;;N;;;;;
+15CB;CANADIAN SYLLABICS CARRIER RO;Lo;0;L;;;;;N;;;;;
+15CC;CANADIAN SYLLABICS CARRIER RE;Lo;0;L;;;;;N;;;;;
+15CD;CANADIAN SYLLABICS CARRIER REE;Lo;0;L;;;;;N;;;;;
+15CE;CANADIAN SYLLABICS CARRIER RI;Lo;0;L;;;;;N;;;;;
+15CF;CANADIAN SYLLABICS CARRIER RA;Lo;0;L;;;;;N;;;;;
+15D0;CANADIAN SYLLABICS CARRIER WU;Lo;0;L;;;;;N;;;;;
+15D1;CANADIAN SYLLABICS CARRIER WO;Lo;0;L;;;;;N;;;;;
+15D2;CANADIAN SYLLABICS CARRIER WE;Lo;0;L;;;;;N;;;;;
+15D3;CANADIAN SYLLABICS CARRIER WEE;Lo;0;L;;;;;N;;;;;
+15D4;CANADIAN SYLLABICS CARRIER WI;Lo;0;L;;;;;N;;;;;
+15D5;CANADIAN SYLLABICS CARRIER WA;Lo;0;L;;;;;N;;;;;
+15D6;CANADIAN SYLLABICS CARRIER HWU;Lo;0;L;;;;;N;;;;;
+15D7;CANADIAN SYLLABICS CARRIER HWO;Lo;0;L;;;;;N;;;;;
+15D8;CANADIAN SYLLABICS CARRIER HWE;Lo;0;L;;;;;N;;;;;
+15D9;CANADIAN SYLLABICS CARRIER HWEE;Lo;0;L;;;;;N;;;;;
+15DA;CANADIAN SYLLABICS CARRIER HWI;Lo;0;L;;;;;N;;;;;
+15DB;CANADIAN SYLLABICS CARRIER HWA;Lo;0;L;;;;;N;;;;;
+15DC;CANADIAN SYLLABICS CARRIER THU;Lo;0;L;;;;;N;;;;;
+15DD;CANADIAN SYLLABICS CARRIER THO;Lo;0;L;;;;;N;;;;;
+15DE;CANADIAN SYLLABICS CARRIER THE;Lo;0;L;;;;;N;;;;;
+15DF;CANADIAN SYLLABICS CARRIER THEE;Lo;0;L;;;;;N;;;;;
+15E0;CANADIAN SYLLABICS CARRIER THI;Lo;0;L;;;;;N;;;;;
+15E1;CANADIAN SYLLABICS CARRIER THA;Lo;0;L;;;;;N;;;;;
+15E2;CANADIAN SYLLABICS CARRIER TTU;Lo;0;L;;;;;N;;;;;
+15E3;CANADIAN SYLLABICS CARRIER TTO;Lo;0;L;;;;;N;;;;;
+15E4;CANADIAN SYLLABICS CARRIER TTE;Lo;0;L;;;;;N;;;;;
+15E5;CANADIAN SYLLABICS CARRIER TTEE;Lo;0;L;;;;;N;;;;;
+15E6;CANADIAN SYLLABICS CARRIER TTI;Lo;0;L;;;;;N;;;;;
+15E7;CANADIAN SYLLABICS CARRIER TTA;Lo;0;L;;;;;N;;;;;
+15E8;CANADIAN SYLLABICS CARRIER PU;Lo;0;L;;;;;N;;;;;
+15E9;CANADIAN SYLLABICS CARRIER PO;Lo;0;L;;;;;N;;;;;
+15EA;CANADIAN SYLLABICS CARRIER PE;Lo;0;L;;;;;N;;;;;
+15EB;CANADIAN SYLLABICS CARRIER PEE;Lo;0;L;;;;;N;;;;;
+15EC;CANADIAN SYLLABICS CARRIER PI;Lo;0;L;;;;;N;;;;;
+15ED;CANADIAN SYLLABICS CARRIER PA;Lo;0;L;;;;;N;;;;;
+15EE;CANADIAN SYLLABICS CARRIER P;Lo;0;L;;;;;N;;;;;
+15EF;CANADIAN SYLLABICS CARRIER GU;Lo;0;L;;;;;N;;;;;
+15F0;CANADIAN SYLLABICS CARRIER GO;Lo;0;L;;;;;N;;;;;
+15F1;CANADIAN SYLLABICS CARRIER GE;Lo;0;L;;;;;N;;;;;
+15F2;CANADIAN SYLLABICS CARRIER GEE;Lo;0;L;;;;;N;;;;;
+15F3;CANADIAN SYLLABICS CARRIER GI;Lo;0;L;;;;;N;;;;;
+15F4;CANADIAN SYLLABICS CARRIER GA;Lo;0;L;;;;;N;;;;;
+15F5;CANADIAN SYLLABICS CARRIER KHU;Lo;0;L;;;;;N;;;;;
+15F6;CANADIAN SYLLABICS CARRIER KHO;Lo;0;L;;;;;N;;;;;
+15F7;CANADIAN SYLLABICS CARRIER KHE;Lo;0;L;;;;;N;;;;;
+15F8;CANADIAN SYLLABICS CARRIER KHEE;Lo;0;L;;;;;N;;;;;
+15F9;CANADIAN SYLLABICS CARRIER KHI;Lo;0;L;;;;;N;;;;;
+15FA;CANADIAN SYLLABICS CARRIER KHA;Lo;0;L;;;;;N;;;;;
+15FB;CANADIAN SYLLABICS CARRIER KKU;Lo;0;L;;;;;N;;;;;
+15FC;CANADIAN SYLLABICS CARRIER KKO;Lo;0;L;;;;;N;;;;;
+15FD;CANADIAN SYLLABICS CARRIER KKE;Lo;0;L;;;;;N;;;;;
+15FE;CANADIAN SYLLABICS CARRIER KKEE;Lo;0;L;;;;;N;;;;;
+15FF;CANADIAN SYLLABICS CARRIER KKI;Lo;0;L;;;;;N;;;;;
+1600;CANADIAN SYLLABICS CARRIER KKA;Lo;0;L;;;;;N;;;;;
+1601;CANADIAN SYLLABICS CARRIER KK;Lo;0;L;;;;;N;;;;;
+1602;CANADIAN SYLLABICS CARRIER NU;Lo;0;L;;;;;N;;;;;
+1603;CANADIAN SYLLABICS CARRIER NO;Lo;0;L;;;;;N;;;;;
+1604;CANADIAN SYLLABICS CARRIER NE;Lo;0;L;;;;;N;;;;;
+1605;CANADIAN SYLLABICS CARRIER NEE;Lo;0;L;;;;;N;;;;;
+1606;CANADIAN SYLLABICS CARRIER NI;Lo;0;L;;;;;N;;;;;
+1607;CANADIAN SYLLABICS CARRIER NA;Lo;0;L;;;;;N;;;;;
+1608;CANADIAN SYLLABICS CARRIER MU;Lo;0;L;;;;;N;;;;;
+1609;CANADIAN SYLLABICS CARRIER MO;Lo;0;L;;;;;N;;;;;
+160A;CANADIAN SYLLABICS CARRIER ME;Lo;0;L;;;;;N;;;;;
+160B;CANADIAN SYLLABICS CARRIER MEE;Lo;0;L;;;;;N;;;;;
+160C;CANADIAN SYLLABICS CARRIER MI;Lo;0;L;;;;;N;;;;;
+160D;CANADIAN SYLLABICS CARRIER MA;Lo;0;L;;;;;N;;;;;
+160E;CANADIAN SYLLABICS CARRIER YU;Lo;0;L;;;;;N;;;;;
+160F;CANADIAN SYLLABICS CARRIER YO;Lo;0;L;;;;;N;;;;;
+1610;CANADIAN SYLLABICS CARRIER YE;Lo;0;L;;;;;N;;;;;
+1611;CANADIAN SYLLABICS CARRIER YEE;Lo;0;L;;;;;N;;;;;
+1612;CANADIAN SYLLABICS CARRIER YI;Lo;0;L;;;;;N;;;;;
+1613;CANADIAN SYLLABICS CARRIER YA;Lo;0;L;;;;;N;;;;;
+1614;CANADIAN SYLLABICS CARRIER JU;Lo;0;L;;;;;N;;;;;
+1615;CANADIAN SYLLABICS SAYISI JU;Lo;0;L;;;;;N;;;;;
+1616;CANADIAN SYLLABICS CARRIER JO;Lo;0;L;;;;;N;;;;;
+1617;CANADIAN SYLLABICS CARRIER JE;Lo;0;L;;;;;N;;;;;
+1618;CANADIAN SYLLABICS CARRIER JEE;Lo;0;L;;;;;N;;;;;
+1619;CANADIAN SYLLABICS CARRIER JI;Lo;0;L;;;;;N;;;;;
+161A;CANADIAN SYLLABICS SAYISI JI;Lo;0;L;;;;;N;;;;;
+161B;CANADIAN SYLLABICS CARRIER JA;Lo;0;L;;;;;N;;;;;
+161C;CANADIAN SYLLABICS CARRIER JJU;Lo;0;L;;;;;N;;;;;
+161D;CANADIAN SYLLABICS CARRIER JJO;Lo;0;L;;;;;N;;;;;
+161E;CANADIAN SYLLABICS CARRIER JJE;Lo;0;L;;;;;N;;;;;
+161F;CANADIAN SYLLABICS CARRIER JJEE;Lo;0;L;;;;;N;;;;;
+1620;CANADIAN SYLLABICS CARRIER JJI;Lo;0;L;;;;;N;;;;;
+1621;CANADIAN SYLLABICS CARRIER JJA;Lo;0;L;;;;;N;;;;;
+1622;CANADIAN SYLLABICS CARRIER LU;Lo;0;L;;;;;N;;;;;
+1623;CANADIAN SYLLABICS CARRIER LO;Lo;0;L;;;;;N;;;;;
+1624;CANADIAN SYLLABICS CARRIER LE;Lo;0;L;;;;;N;;;;;
+1625;CANADIAN SYLLABICS CARRIER LEE;Lo;0;L;;;;;N;;;;;
+1626;CANADIAN SYLLABICS CARRIER LI;Lo;0;L;;;;;N;;;;;
+1627;CANADIAN SYLLABICS CARRIER LA;Lo;0;L;;;;;N;;;;;
+1628;CANADIAN SYLLABICS CARRIER DLU;Lo;0;L;;;;;N;;;;;
+1629;CANADIAN SYLLABICS CARRIER DLO;Lo;0;L;;;;;N;;;;;
+162A;CANADIAN SYLLABICS CARRIER DLE;Lo;0;L;;;;;N;;;;;
+162B;CANADIAN SYLLABICS CARRIER DLEE;Lo;0;L;;;;;N;;;;;
+162C;CANADIAN SYLLABICS CARRIER DLI;Lo;0;L;;;;;N;;;;;
+162D;CANADIAN SYLLABICS CARRIER DLA;Lo;0;L;;;;;N;;;;;
+162E;CANADIAN SYLLABICS CARRIER LHU;Lo;0;L;;;;;N;;;;;
+162F;CANADIAN SYLLABICS CARRIER LHO;Lo;0;L;;;;;N;;;;;
+1630;CANADIAN SYLLABICS CARRIER LHE;Lo;0;L;;;;;N;;;;;
+1631;CANADIAN SYLLABICS CARRIER LHEE;Lo;0;L;;;;;N;;;;;
+1632;CANADIAN SYLLABICS CARRIER LHI;Lo;0;L;;;;;N;;;;;
+1633;CANADIAN SYLLABICS CARRIER LHA;Lo;0;L;;;;;N;;;;;
+1634;CANADIAN SYLLABICS CARRIER TLHU;Lo;0;L;;;;;N;;;;;
+1635;CANADIAN SYLLABICS CARRIER TLHO;Lo;0;L;;;;;N;;;;;
+1636;CANADIAN SYLLABICS CARRIER TLHE;Lo;0;L;;;;;N;;;;;
+1637;CANADIAN SYLLABICS CARRIER TLHEE;Lo;0;L;;;;;N;;;;;
+1638;CANADIAN SYLLABICS CARRIER TLHI;Lo;0;L;;;;;N;;;;;
+1639;CANADIAN SYLLABICS CARRIER TLHA;Lo;0;L;;;;;N;;;;;
+163A;CANADIAN SYLLABICS CARRIER TLU;Lo;0;L;;;;;N;;;;;
+163B;CANADIAN SYLLABICS CARRIER TLO;Lo;0;L;;;;;N;;;;;
+163C;CANADIAN SYLLABICS CARRIER TLE;Lo;0;L;;;;;N;;;;;
+163D;CANADIAN SYLLABICS CARRIER TLEE;Lo;0;L;;;;;N;;;;;
+163E;CANADIAN SYLLABICS CARRIER TLI;Lo;0;L;;;;;N;;;;;
+163F;CANADIAN SYLLABICS CARRIER TLA;Lo;0;L;;;;;N;;;;;
+1640;CANADIAN SYLLABICS CARRIER ZU;Lo;0;L;;;;;N;;;;;
+1641;CANADIAN SYLLABICS CARRIER ZO;Lo;0;L;;;;;N;;;;;
+1642;CANADIAN SYLLABICS CARRIER ZE;Lo;0;L;;;;;N;;;;;
+1643;CANADIAN SYLLABICS CARRIER ZEE;Lo;0;L;;;;;N;;;;;
+1644;CANADIAN SYLLABICS CARRIER ZI;Lo;0;L;;;;;N;;;;;
+1645;CANADIAN SYLLABICS CARRIER ZA;Lo;0;L;;;;;N;;;;;
+1646;CANADIAN SYLLABICS CARRIER Z;Lo;0;L;;;;;N;;;;;
+1647;CANADIAN SYLLABICS CARRIER INITIAL Z;Lo;0;L;;;;;N;;;;;
+1648;CANADIAN SYLLABICS CARRIER DZU;Lo;0;L;;;;;N;;;;;
+1649;CANADIAN SYLLABICS CARRIER DZO;Lo;0;L;;;;;N;;;;;
+164A;CANADIAN SYLLABICS CARRIER DZE;Lo;0;L;;;;;N;;;;;
+164B;CANADIAN SYLLABICS CARRIER DZEE;Lo;0;L;;;;;N;;;;;
+164C;CANADIAN SYLLABICS CARRIER DZI;Lo;0;L;;;;;N;;;;;
+164D;CANADIAN SYLLABICS CARRIER DZA;Lo;0;L;;;;;N;;;;;
+164E;CANADIAN SYLLABICS CARRIER SU;Lo;0;L;;;;;N;;;;;
+164F;CANADIAN SYLLABICS CARRIER SO;Lo;0;L;;;;;N;;;;;
+1650;CANADIAN SYLLABICS CARRIER SE;Lo;0;L;;;;;N;;;;;
+1651;CANADIAN SYLLABICS CARRIER SEE;Lo;0;L;;;;;N;;;;;
+1652;CANADIAN SYLLABICS CARRIER SI;Lo;0;L;;;;;N;;;;;
+1653;CANADIAN SYLLABICS CARRIER SA;Lo;0;L;;;;;N;;;;;
+1654;CANADIAN SYLLABICS CARRIER SHU;Lo;0;L;;;;;N;;;;;
+1655;CANADIAN SYLLABICS CARRIER SHO;Lo;0;L;;;;;N;;;;;
+1656;CANADIAN SYLLABICS CARRIER SHE;Lo;0;L;;;;;N;;;;;
+1657;CANADIAN SYLLABICS CARRIER SHEE;Lo;0;L;;;;;N;;;;;
+1658;CANADIAN SYLLABICS CARRIER SHI;Lo;0;L;;;;;N;;;;;
+1659;CANADIAN SYLLABICS CARRIER SHA;Lo;0;L;;;;;N;;;;;
+165A;CANADIAN SYLLABICS CARRIER SH;Lo;0;L;;;;;N;;;;;
+165B;CANADIAN SYLLABICS CARRIER TSU;Lo;0;L;;;;;N;;;;;
+165C;CANADIAN SYLLABICS CARRIER TSO;Lo;0;L;;;;;N;;;;;
+165D;CANADIAN SYLLABICS CARRIER TSE;Lo;0;L;;;;;N;;;;;
+165E;CANADIAN SYLLABICS CARRIER TSEE;Lo;0;L;;;;;N;;;;;
+165F;CANADIAN SYLLABICS CARRIER TSI;Lo;0;L;;;;;N;;;;;
+1660;CANADIAN SYLLABICS CARRIER TSA;Lo;0;L;;;;;N;;;;;
+1661;CANADIAN SYLLABICS CARRIER CHU;Lo;0;L;;;;;N;;;;;
+1662;CANADIAN SYLLABICS CARRIER CHO;Lo;0;L;;;;;N;;;;;
+1663;CANADIAN SYLLABICS CARRIER CHE;Lo;0;L;;;;;N;;;;;
+1664;CANADIAN SYLLABICS CARRIER CHEE;Lo;0;L;;;;;N;;;;;
+1665;CANADIAN SYLLABICS CARRIER CHI;Lo;0;L;;;;;N;;;;;
+1666;CANADIAN SYLLABICS CARRIER CHA;Lo;0;L;;;;;N;;;;;
+1667;CANADIAN SYLLABICS CARRIER TTSU;Lo;0;L;;;;;N;;;;;
+1668;CANADIAN SYLLABICS CARRIER TTSO;Lo;0;L;;;;;N;;;;;
+1669;CANADIAN SYLLABICS CARRIER TTSE;Lo;0;L;;;;;N;;;;;
+166A;CANADIAN SYLLABICS CARRIER TTSEE;Lo;0;L;;;;;N;;;;;
+166B;CANADIAN SYLLABICS CARRIER TTSI;Lo;0;L;;;;;N;;;;;
+166C;CANADIAN SYLLABICS CARRIER TTSA;Lo;0;L;;;;;N;;;;;
+166D;CANADIAN SYLLABICS CHI SIGN;So;0;L;;;;;N;;;;;
+166E;CANADIAN SYLLABICS FULL STOP;Po;0;L;;;;;N;;;;;
+166F;CANADIAN SYLLABICS QAI;Lo;0;L;;;;;N;;;;;
+1670;CANADIAN SYLLABICS NGAI;Lo;0;L;;;;;N;;;;;
+1671;CANADIAN SYLLABICS NNGI;Lo;0;L;;;;;N;;;;;
+1672;CANADIAN SYLLABICS NNGII;Lo;0;L;;;;;N;;;;;
+1673;CANADIAN SYLLABICS NNGO;Lo;0;L;;;;;N;;;;;
+1674;CANADIAN SYLLABICS NNGOO;Lo;0;L;;;;;N;;;;;
+1675;CANADIAN SYLLABICS NNGA;Lo;0;L;;;;;N;;;;;
+1676;CANADIAN SYLLABICS NNGAA;Lo;0;L;;;;;N;;;;;
+1677;CANADIAN SYLLABICS WOODS-CREE THWEE;Lo;0;L;;;;;N;;;;;
+1678;CANADIAN SYLLABICS WOODS-CREE THWI;Lo;0;L;;;;;N;;;;;
+1679;CANADIAN SYLLABICS WOODS-CREE THWII;Lo;0;L;;;;;N;;;;;
+167A;CANADIAN SYLLABICS WOODS-CREE THWO;Lo;0;L;;;;;N;;;;;
+167B;CANADIAN SYLLABICS WOODS-CREE THWOO;Lo;0;L;;;;;N;;;;;
+167C;CANADIAN SYLLABICS WOODS-CREE THWA;Lo;0;L;;;;;N;;;;;
+167D;CANADIAN SYLLABICS WOODS-CREE THWAA;Lo;0;L;;;;;N;;;;;
+167E;CANADIAN SYLLABICS WOODS-CREE FINAL TH;Lo;0;L;;;;;N;;;;;
+167F;CANADIAN SYLLABICS BLACKFOOT W;Lo;0;L;;;;;N;;;;;
+1680;OGHAM SPACE MARK;Zs;0;WS;;;;;N;;;;;
+1681;OGHAM LETTER BEITH;Lo;0;L;;;;;N;;;;;
+1682;OGHAM LETTER LUIS;Lo;0;L;;;;;N;;;;;
+1683;OGHAM LETTER FEARN;Lo;0;L;;;;;N;;;;;
+1684;OGHAM LETTER SAIL;Lo;0;L;;;;;N;;;;;
+1685;OGHAM LETTER NION;Lo;0;L;;;;;N;;;;;
+1686;OGHAM LETTER UATH;Lo;0;L;;;;;N;;;;;
+1687;OGHAM LETTER DAIR;Lo;0;L;;;;;N;;;;;
+1688;OGHAM LETTER TINNE;Lo;0;L;;;;;N;;;;;
+1689;OGHAM LETTER COLL;Lo;0;L;;;;;N;;;;;
+168A;OGHAM LETTER CEIRT;Lo;0;L;;;;;N;;;;;
+168B;OGHAM LETTER MUIN;Lo;0;L;;;;;N;;;;;
+168C;OGHAM LETTER GORT;Lo;0;L;;;;;N;;;;;
+168D;OGHAM LETTER NGEADAL;Lo;0;L;;;;;N;;;;;
+168E;OGHAM LETTER STRAIF;Lo;0;L;;;;;N;;;;;
+168F;OGHAM LETTER RUIS;Lo;0;L;;;;;N;;;;;
+1690;OGHAM LETTER AILM;Lo;0;L;;;;;N;;;;;
+1691;OGHAM LETTER ONN;Lo;0;L;;;;;N;;;;;
+1692;OGHAM LETTER UR;Lo;0;L;;;;;N;;;;;
+1693;OGHAM LETTER EADHADH;Lo;0;L;;;;;N;;;;;
+1694;OGHAM LETTER IODHADH;Lo;0;L;;;;;N;;;;;
+1695;OGHAM LETTER EABHADH;Lo;0;L;;;;;N;;;;;
+1696;OGHAM LETTER OR;Lo;0;L;;;;;N;;;;;
+1697;OGHAM LETTER UILLEANN;Lo;0;L;;;;;N;;;;;
+1698;OGHAM LETTER IFIN;Lo;0;L;;;;;N;;;;;
+1699;OGHAM LETTER EAMHANCHOLL;Lo;0;L;;;;;N;;;;;
+169A;OGHAM LETTER PEITH;Lo;0;L;;;;;N;;;;;
+169B;OGHAM FEATHER MARK;Ps;0;ON;;;;;Y;;;;;
+169C;OGHAM REVERSED FEATHER MARK;Pe;0;ON;;;;;Y;;;;;
+16A0;RUNIC LETTER FEHU FEOH FE F;Lo;0;L;;;;;N;;;;;
+16A1;RUNIC LETTER V;Lo;0;L;;;;;N;;;;;
+16A2;RUNIC LETTER URUZ UR U;Lo;0;L;;;;;N;;;;;
+16A3;RUNIC LETTER YR;Lo;0;L;;;;;N;;;;;
+16A4;RUNIC LETTER Y;Lo;0;L;;;;;N;;;;;
+16A5;RUNIC LETTER W;Lo;0;L;;;;;N;;;;;
+16A6;RUNIC LETTER THURISAZ THURS THORN;Lo;0;L;;;;;N;;;;;
+16A7;RUNIC LETTER ETH;Lo;0;L;;;;;N;;;;;
+16A8;RUNIC LETTER ANSUZ A;Lo;0;L;;;;;N;;;;;
+16A9;RUNIC LETTER OS O;Lo;0;L;;;;;N;;;;;
+16AA;RUNIC LETTER AC A;Lo;0;L;;;;;N;;;;;
+16AB;RUNIC LETTER AESC;Lo;0;L;;;;;N;;;;;
+16AC;RUNIC LETTER LONG-BRANCH-OSS O;Lo;0;L;;;;;N;;;;;
+16AD;RUNIC LETTER SHORT-TWIG-OSS O;Lo;0;L;;;;;N;;;;;
+16AE;RUNIC LETTER O;Lo;0;L;;;;;N;;;;;
+16AF;RUNIC LETTER OE;Lo;0;L;;;;;N;;;;;
+16B0;RUNIC LETTER ON;Lo;0;L;;;;;N;;;;;
+16B1;RUNIC LETTER RAIDO RAD REID R;Lo;0;L;;;;;N;;;;;
+16B2;RUNIC LETTER KAUNA;Lo;0;L;;;;;N;;;;;
+16B3;RUNIC LETTER CEN;Lo;0;L;;;;;N;;;;;
+16B4;RUNIC LETTER KAUN K;Lo;0;L;;;;;N;;;;;
+16B5;RUNIC LETTER G;Lo;0;L;;;;;N;;;;;
+16B6;RUNIC LETTER ENG;Lo;0;L;;;;;N;;;;;
+16B7;RUNIC LETTER GEBO GYFU G;Lo;0;L;;;;;N;;;;;
+16B8;RUNIC LETTER GAR;Lo;0;L;;;;;N;;;;;
+16B9;RUNIC LETTER WUNJO WYNN W;Lo;0;L;;;;;N;;;;;
+16BA;RUNIC LETTER HAGLAZ H;Lo;0;L;;;;;N;;;;;
+16BB;RUNIC LETTER HAEGL H;Lo;0;L;;;;;N;;;;;
+16BC;RUNIC LETTER LONG-BRANCH-HAGALL H;Lo;0;L;;;;;N;;;;;
+16BD;RUNIC LETTER SHORT-TWIG-HAGALL H;Lo;0;L;;;;;N;;;;;
+16BE;RUNIC LETTER NAUDIZ NYD NAUD N;Lo;0;L;;;;;N;;;;;
+16BF;RUNIC LETTER SHORT-TWIG-NAUD N;Lo;0;L;;;;;N;;;;;
+16C0;RUNIC LETTER DOTTED-N;Lo;0;L;;;;;N;;;;;
+16C1;RUNIC LETTER ISAZ IS ISS I;Lo;0;L;;;;;N;;;;;
+16C2;RUNIC LETTER E;Lo;0;L;;;;;N;;;;;
+16C3;RUNIC LETTER JERAN J;Lo;0;L;;;;;N;;;;;
+16C4;RUNIC LETTER GER;Lo;0;L;;;;;N;;;;;
+16C5;RUNIC LETTER LONG-BRANCH-AR AE;Lo;0;L;;;;;N;;;;;
+16C6;RUNIC LETTER SHORT-TWIG-AR A;Lo;0;L;;;;;N;;;;;
+16C7;RUNIC LETTER IWAZ EOH;Lo;0;L;;;;;N;;;;;
+16C8;RUNIC LETTER PERTHO PEORTH P;Lo;0;L;;;;;N;;;;;
+16C9;RUNIC LETTER ALGIZ EOLHX;Lo;0;L;;;;;N;;;;;
+16CA;RUNIC LETTER SOWILO S;Lo;0;L;;;;;N;;;;;
+16CB;RUNIC LETTER SIGEL LONG-BRANCH-SOL S;Lo;0;L;;;;;N;;;;;
+16CC;RUNIC LETTER SHORT-TWIG-SOL S;Lo;0;L;;;;;N;;;;;
+16CD;RUNIC LETTER C;Lo;0;L;;;;;N;;;;;
+16CE;RUNIC LETTER Z;Lo;0;L;;;;;N;;;;;
+16CF;RUNIC LETTER TIWAZ TIR TYR T;Lo;0;L;;;;;N;;;;;
+16D0;RUNIC LETTER SHORT-TWIG-TYR T;Lo;0;L;;;;;N;;;;;
+16D1;RUNIC LETTER D;Lo;0;L;;;;;N;;;;;
+16D2;RUNIC LETTER BERKANAN BEORC BJARKAN B;Lo;0;L;;;;;N;;;;;
+16D3;RUNIC LETTER SHORT-TWIG-BJARKAN B;Lo;0;L;;;;;N;;;;;
+16D4;RUNIC LETTER DOTTED-P;Lo;0;L;;;;;N;;;;;
+16D5;RUNIC LETTER OPEN-P;Lo;0;L;;;;;N;;;;;
+16D6;RUNIC LETTER EHWAZ EH E;Lo;0;L;;;;;N;;;;;
+16D7;RUNIC LETTER MANNAZ MAN M;Lo;0;L;;;;;N;;;;;
+16D8;RUNIC LETTER LONG-BRANCH-MADR M;Lo;0;L;;;;;N;;;;;
+16D9;RUNIC LETTER SHORT-TWIG-MADR M;Lo;0;L;;;;;N;;;;;
+16DA;RUNIC LETTER LAUKAZ LAGU LOGR L;Lo;0;L;;;;;N;;;;;
+16DB;RUNIC LETTER DOTTED-L;Lo;0;L;;;;;N;;;;;
+16DC;RUNIC LETTER INGWAZ;Lo;0;L;;;;;N;;;;;
+16DD;RUNIC LETTER ING;Lo;0;L;;;;;N;;;;;
+16DE;RUNIC LETTER DAGAZ DAEG D;Lo;0;L;;;;;N;;;;;
+16DF;RUNIC LETTER OTHALAN ETHEL O;Lo;0;L;;;;;N;;;;;
+16E0;RUNIC LETTER EAR;Lo;0;L;;;;;N;;;;;
+16E1;RUNIC LETTER IOR;Lo;0;L;;;;;N;;;;;
+16E2;RUNIC LETTER CWEORTH;Lo;0;L;;;;;N;;;;;
+16E3;RUNIC LETTER CALC;Lo;0;L;;;;;N;;;;;
+16E4;RUNIC LETTER CEALC;Lo;0;L;;;;;N;;;;;
+16E5;RUNIC LETTER STAN;Lo;0;L;;;;;N;;;;;
+16E6;RUNIC LETTER LONG-BRANCH-YR;Lo;0;L;;;;;N;;;;;
+16E7;RUNIC LETTER SHORT-TWIG-YR;Lo;0;L;;;;;N;;;;;
+16E8;RUNIC LETTER ICELANDIC-YR;Lo;0;L;;;;;N;;;;;
+16E9;RUNIC LETTER Q;Lo;0;L;;;;;N;;;;;
+16EA;RUNIC LETTER X;Lo;0;L;;;;;N;;;;;
+16EB;RUNIC SINGLE PUNCTUATION;Po;0;L;;;;;N;;;;;
+16EC;RUNIC MULTIPLE PUNCTUATION;Po;0;L;;;;;N;;;;;
+16ED;RUNIC CROSS PUNCTUATION;Po;0;L;;;;;N;;;;;
+16EE;RUNIC ARLAUG SYMBOL;Nl;0;L;;;;17;N;;;;;
+16EF;RUNIC TVIMADUR SYMBOL;Nl;0;L;;;;18;N;;;;;
+16F0;RUNIC BELGTHOR SYMBOL;Nl;0;L;;;;19;N;;;;;
+16F1;RUNIC LETTER K;Lo;0;L;;;;;N;;;;;
+16F2;RUNIC LETTER SH;Lo;0;L;;;;;N;;;;;
+16F3;RUNIC LETTER OO;Lo;0;L;;;;;N;;;;;
+16F4;RUNIC LETTER FRANKS CASKET OS;Lo;0;L;;;;;N;;;;;
+16F5;RUNIC LETTER FRANKS CASKET IS;Lo;0;L;;;;;N;;;;;
+16F6;RUNIC LETTER FRANKS CASKET EH;Lo;0;L;;;;;N;;;;;
+16F7;RUNIC LETTER FRANKS CASKET AC;Lo;0;L;;;;;N;;;;;
+16F8;RUNIC LETTER FRANKS CASKET AESC;Lo;0;L;;;;;N;;;;;
+1700;TAGALOG LETTER A;Lo;0;L;;;;;N;;;;;
+1701;TAGALOG LETTER I;Lo;0;L;;;;;N;;;;;
+1702;TAGALOG LETTER U;Lo;0;L;;;;;N;;;;;
+1703;TAGALOG LETTER KA;Lo;0;L;;;;;N;;;;;
+1704;TAGALOG LETTER GA;Lo;0;L;;;;;N;;;;;
+1705;TAGALOG LETTER NGA;Lo;0;L;;;;;N;;;;;
+1706;TAGALOG LETTER TA;Lo;0;L;;;;;N;;;;;
+1707;TAGALOG LETTER DA;Lo;0;L;;;;;N;;;;;
+1708;TAGALOG LETTER NA;Lo;0;L;;;;;N;;;;;
+1709;TAGALOG LETTER PA;Lo;0;L;;;;;N;;;;;
+170A;TAGALOG LETTER BA;Lo;0;L;;;;;N;;;;;
+170B;TAGALOG LETTER MA;Lo;0;L;;;;;N;;;;;
+170C;TAGALOG LETTER YA;Lo;0;L;;;;;N;;;;;
+170E;TAGALOG LETTER LA;Lo;0;L;;;;;N;;;;;
+170F;TAGALOG LETTER WA;Lo;0;L;;;;;N;;;;;
+1710;TAGALOG LETTER SA;Lo;0;L;;;;;N;;;;;
+1711;TAGALOG LETTER HA;Lo;0;L;;;;;N;;;;;
+1712;TAGALOG VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+1713;TAGALOG VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1714;TAGALOG SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+1720;HANUNOO LETTER A;Lo;0;L;;;;;N;;;;;
+1721;HANUNOO LETTER I;Lo;0;L;;;;;N;;;;;
+1722;HANUNOO LETTER U;Lo;0;L;;;;;N;;;;;
+1723;HANUNOO LETTER KA;Lo;0;L;;;;;N;;;;;
+1724;HANUNOO LETTER GA;Lo;0;L;;;;;N;;;;;
+1725;HANUNOO LETTER NGA;Lo;0;L;;;;;N;;;;;
+1726;HANUNOO LETTER TA;Lo;0;L;;;;;N;;;;;
+1727;HANUNOO LETTER DA;Lo;0;L;;;;;N;;;;;
+1728;HANUNOO LETTER NA;Lo;0;L;;;;;N;;;;;
+1729;HANUNOO LETTER PA;Lo;0;L;;;;;N;;;;;
+172A;HANUNOO LETTER BA;Lo;0;L;;;;;N;;;;;
+172B;HANUNOO LETTER MA;Lo;0;L;;;;;N;;;;;
+172C;HANUNOO LETTER YA;Lo;0;L;;;;;N;;;;;
+172D;HANUNOO LETTER RA;Lo;0;L;;;;;N;;;;;
+172E;HANUNOO LETTER LA;Lo;0;L;;;;;N;;;;;
+172F;HANUNOO LETTER WA;Lo;0;L;;;;;N;;;;;
+1730;HANUNOO LETTER SA;Lo;0;L;;;;;N;;;;;
+1731;HANUNOO LETTER HA;Lo;0;L;;;;;N;;;;;
+1732;HANUNOO VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+1733;HANUNOO VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1734;HANUNOO SIGN PAMUDPOD;Mn;9;NSM;;;;;N;;;;;
+1735;PHILIPPINE SINGLE PUNCTUATION;Po;0;L;;;;;N;;;;;
+1736;PHILIPPINE DOUBLE PUNCTUATION;Po;0;L;;;;;N;;;;;
+1740;BUHID LETTER A;Lo;0;L;;;;;N;;;;;
+1741;BUHID LETTER I;Lo;0;L;;;;;N;;;;;
+1742;BUHID LETTER U;Lo;0;L;;;;;N;;;;;
+1743;BUHID LETTER KA;Lo;0;L;;;;;N;;;;;
+1744;BUHID LETTER GA;Lo;0;L;;;;;N;;;;;
+1745;BUHID LETTER NGA;Lo;0;L;;;;;N;;;;;
+1746;BUHID LETTER TA;Lo;0;L;;;;;N;;;;;
+1747;BUHID LETTER DA;Lo;0;L;;;;;N;;;;;
+1748;BUHID LETTER NA;Lo;0;L;;;;;N;;;;;
+1749;BUHID LETTER PA;Lo;0;L;;;;;N;;;;;
+174A;BUHID LETTER BA;Lo;0;L;;;;;N;;;;;
+174B;BUHID LETTER MA;Lo;0;L;;;;;N;;;;;
+174C;BUHID LETTER YA;Lo;0;L;;;;;N;;;;;
+174D;BUHID LETTER RA;Lo;0;L;;;;;N;;;;;
+174E;BUHID LETTER LA;Lo;0;L;;;;;N;;;;;
+174F;BUHID LETTER WA;Lo;0;L;;;;;N;;;;;
+1750;BUHID LETTER SA;Lo;0;L;;;;;N;;;;;
+1751;BUHID LETTER HA;Lo;0;L;;;;;N;;;;;
+1752;BUHID VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+1753;BUHID VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1760;TAGBANWA LETTER A;Lo;0;L;;;;;N;;;;;
+1761;TAGBANWA LETTER I;Lo;0;L;;;;;N;;;;;
+1762;TAGBANWA LETTER U;Lo;0;L;;;;;N;;;;;
+1763;TAGBANWA LETTER KA;Lo;0;L;;;;;N;;;;;
+1764;TAGBANWA LETTER GA;Lo;0;L;;;;;N;;;;;
+1765;TAGBANWA LETTER NGA;Lo;0;L;;;;;N;;;;;
+1766;TAGBANWA LETTER TA;Lo;0;L;;;;;N;;;;;
+1767;TAGBANWA LETTER DA;Lo;0;L;;;;;N;;;;;
+1768;TAGBANWA LETTER NA;Lo;0;L;;;;;N;;;;;
+1769;TAGBANWA LETTER PA;Lo;0;L;;;;;N;;;;;
+176A;TAGBANWA LETTER BA;Lo;0;L;;;;;N;;;;;
+176B;TAGBANWA LETTER MA;Lo;0;L;;;;;N;;;;;
+176C;TAGBANWA LETTER YA;Lo;0;L;;;;;N;;;;;
+176E;TAGBANWA LETTER LA;Lo;0;L;;;;;N;;;;;
+176F;TAGBANWA LETTER WA;Lo;0;L;;;;;N;;;;;
+1770;TAGBANWA LETTER SA;Lo;0;L;;;;;N;;;;;
+1772;TAGBANWA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+1773;TAGBANWA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1780;KHMER LETTER KA;Lo;0;L;;;;;N;;;;;
+1781;KHMER LETTER KHA;Lo;0;L;;;;;N;;;;;
+1782;KHMER LETTER KO;Lo;0;L;;;;;N;;;;;
+1783;KHMER LETTER KHO;Lo;0;L;;;;;N;;;;;
+1784;KHMER LETTER NGO;Lo;0;L;;;;;N;;;;;
+1785;KHMER LETTER CA;Lo;0;L;;;;;N;;;;;
+1786;KHMER LETTER CHA;Lo;0;L;;;;;N;;;;;
+1787;KHMER LETTER CO;Lo;0;L;;;;;N;;;;;
+1788;KHMER LETTER CHO;Lo;0;L;;;;;N;;;;;
+1789;KHMER LETTER NYO;Lo;0;L;;;;;N;;;;;
+178A;KHMER LETTER DA;Lo;0;L;;;;;N;;;;;
+178B;KHMER LETTER TTHA;Lo;0;L;;;;;N;;;;;
+178C;KHMER LETTER DO;Lo;0;L;;;;;N;;;;;
+178D;KHMER LETTER TTHO;Lo;0;L;;;;;N;;;;;
+178E;KHMER LETTER NNO;Lo;0;L;;;;;N;;;;;
+178F;KHMER LETTER TA;Lo;0;L;;;;;N;;;;;
+1790;KHMER LETTER THA;Lo;0;L;;;;;N;;;;;
+1791;KHMER LETTER TO;Lo;0;L;;;;;N;;;;;
+1792;KHMER LETTER THO;Lo;0;L;;;;;N;;;;;
+1793;KHMER LETTER NO;Lo;0;L;;;;;N;;;;;
+1794;KHMER LETTER BA;Lo;0;L;;;;;N;;;;;
+1795;KHMER LETTER PHA;Lo;0;L;;;;;N;;;;;
+1796;KHMER LETTER PO;Lo;0;L;;;;;N;;;;;
+1797;KHMER LETTER PHO;Lo;0;L;;;;;N;;;;;
+1798;KHMER LETTER MO;Lo;0;L;;;;;N;;;;;
+1799;KHMER LETTER YO;Lo;0;L;;;;;N;;;;;
+179A;KHMER LETTER RO;Lo;0;L;;;;;N;;;;;
+179B;KHMER LETTER LO;Lo;0;L;;;;;N;;;;;
+179C;KHMER LETTER VO;Lo;0;L;;;;;N;;;;;
+179D;KHMER LETTER SHA;Lo;0;L;;;;;N;;;;;
+179E;KHMER LETTER SSO;Lo;0;L;;;;;N;;;;;
+179F;KHMER LETTER SA;Lo;0;L;;;;;N;;;;;
+17A0;KHMER LETTER HA;Lo;0;L;;;;;N;;;;;
+17A1;KHMER LETTER LA;Lo;0;L;;;;;N;;;;;
+17A2;KHMER LETTER QA;Lo;0;L;;;;;N;;;;;
+17A3;KHMER INDEPENDENT VOWEL QAQ;Lo;0;L;;;;;N;;;;;
+17A4;KHMER INDEPENDENT VOWEL QAA;Lo;0;L;;;;;N;;;;;
+17A5;KHMER INDEPENDENT VOWEL QI;Lo;0;L;;;;;N;;;;;
+17A6;KHMER INDEPENDENT VOWEL QII;Lo;0;L;;;;;N;;;;;
+17A7;KHMER INDEPENDENT VOWEL QU;Lo;0;L;;;;;N;;;;;
+17A8;KHMER INDEPENDENT VOWEL QUK;Lo;0;L;;;;;N;;;;;
+17A9;KHMER INDEPENDENT VOWEL QUU;Lo;0;L;;;;;N;;;;;
+17AA;KHMER INDEPENDENT VOWEL QUUV;Lo;0;L;;;;;N;;;;;
+17AB;KHMER INDEPENDENT VOWEL RY;Lo;0;L;;;;;N;;;;;
+17AC;KHMER INDEPENDENT VOWEL RYY;Lo;0;L;;;;;N;;;;;
+17AD;KHMER INDEPENDENT VOWEL LY;Lo;0;L;;;;;N;;;;;
+17AE;KHMER INDEPENDENT VOWEL LYY;Lo;0;L;;;;;N;;;;;
+17AF;KHMER INDEPENDENT VOWEL QE;Lo;0;L;;;;;N;;;;;
+17B0;KHMER INDEPENDENT VOWEL QAI;Lo;0;L;;;;;N;;;;;
+17B1;KHMER INDEPENDENT VOWEL QOO TYPE ONE;Lo;0;L;;;;;N;;;;;
+17B2;KHMER INDEPENDENT VOWEL QOO TYPE TWO;Lo;0;L;;;;;N;;;;;
+17B3;KHMER INDEPENDENT VOWEL QAU;Lo;0;L;;;;;N;;;;;
+17B4;KHMER VOWEL INHERENT AQ;Mn;0;NSM;;;;;N;;;;;
+17B5;KHMER VOWEL INHERENT AA;Mn;0;NSM;;;;;N;;;;;
+17B6;KHMER VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+17B7;KHMER VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+17B8;KHMER VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+17B9;KHMER VOWEL SIGN Y;Mn;0;NSM;;;;;N;;;;;
+17BA;KHMER VOWEL SIGN YY;Mn;0;NSM;;;;;N;;;;;
+17BB;KHMER VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+17BC;KHMER VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+17BD;KHMER VOWEL SIGN UA;Mn;0;NSM;;;;;N;;;;;
+17BE;KHMER VOWEL SIGN OE;Mc;0;L;;;;;N;;;;;
+17BF;KHMER VOWEL SIGN YA;Mc;0;L;;;;;N;;;;;
+17C0;KHMER VOWEL SIGN IE;Mc;0;L;;;;;N;;;;;
+17C1;KHMER VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+17C2;KHMER VOWEL SIGN AE;Mc;0;L;;;;;N;;;;;
+17C3;KHMER VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+17C4;KHMER VOWEL SIGN OO;Mc;0;L;;;;;N;;;;;
+17C5;KHMER VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+17C6;KHMER SIGN NIKAHIT;Mn;0;NSM;;;;;N;;;;;
+17C7;KHMER SIGN REAHMUK;Mc;0;L;;;;;N;;;;;
+17C8;KHMER SIGN YUUKALEAPINTU;Mc;0;L;;;;;N;;;;;
+17C9;KHMER SIGN MUUSIKATOAN;Mn;0;NSM;;;;;N;;;;;
+17CA;KHMER SIGN TRIISAP;Mn;0;NSM;;;;;N;;;;;
+17CB;KHMER SIGN BANTOC;Mn;0;NSM;;;;;N;;;;;
+17CC;KHMER SIGN ROBAT;Mn;0;NSM;;;;;N;;;;;
+17CD;KHMER SIGN TOANDAKHIAT;Mn;0;NSM;;;;;N;;;;;
+17CE;KHMER SIGN KAKABAT;Mn;0;NSM;;;;;N;;;;;
+17CF;KHMER SIGN AHSDA;Mn;0;NSM;;;;;N;;;;;
+17D0;KHMER SIGN SAMYOK SANNYA;Mn;0;NSM;;;;;N;;;;;
+17D1;KHMER SIGN VIRIAM;Mn;0;NSM;;;;;N;;;;;
+17D2;KHMER SIGN COENG;Mn;9;NSM;;;;;N;;;;;
+17D3;KHMER SIGN BATHAMASAT;Mn;0;NSM;;;;;N;;;;;
+17D4;KHMER SIGN KHAN;Po;0;L;;;;;N;;;;;
+17D5;KHMER SIGN BARIYOOSAN;Po;0;L;;;;;N;;;;;
+17D6;KHMER SIGN CAMNUC PII KUUH;Po;0;L;;;;;N;;;;;
+17D7;KHMER SIGN LEK TOO;Lm;0;L;;;;;N;;;;;
+17D8;KHMER SIGN BEYYAL;Po;0;L;;;;;N;;;;;
+17D9;KHMER SIGN PHNAEK MUAN;Po;0;L;;;;;N;;;;;
+17DA;KHMER SIGN KOOMUUT;Po;0;L;;;;;N;;;;;
+17DB;KHMER CURRENCY SYMBOL RIEL;Sc;0;ET;;;;;N;;;;;
+17DC;KHMER SIGN AVAKRAHASANYA;Lo;0;L;;;;;N;;;;;
+17DD;KHMER SIGN ATTHACAN;Mn;230;NSM;;;;;N;;;;;
+17E0;KHMER DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+17E1;KHMER DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+17E2;KHMER DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+17E3;KHMER DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+17E4;KHMER DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+17E5;KHMER DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+17E6;KHMER DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+17E7;KHMER DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+17E8;KHMER DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+17E9;KHMER DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+17F0;KHMER SYMBOL LEK ATTAK SON;No;0;ON;;;;0;N;;;;;
+17F1;KHMER SYMBOL LEK ATTAK MUOY;No;0;ON;;;;1;N;;;;;
+17F2;KHMER SYMBOL LEK ATTAK PII;No;0;ON;;;;2;N;;;;;
+17F3;KHMER SYMBOL LEK ATTAK BEI;No;0;ON;;;;3;N;;;;;
+17F4;KHMER SYMBOL LEK ATTAK BUON;No;0;ON;;;;4;N;;;;;
+17F5;KHMER SYMBOL LEK ATTAK PRAM;No;0;ON;;;;5;N;;;;;
+17F6;KHMER SYMBOL LEK ATTAK PRAM-MUOY;No;0;ON;;;;6;N;;;;;
+17F7;KHMER SYMBOL LEK ATTAK PRAM-PII;No;0;ON;;;;7;N;;;;;
+17F8;KHMER SYMBOL LEK ATTAK PRAM-BEI;No;0;ON;;;;8;N;;;;;
+17F9;KHMER SYMBOL LEK ATTAK PRAM-BUON;No;0;ON;;;;9;N;;;;;
+1800;MONGOLIAN BIRGA;Po;0;ON;;;;;N;;;;;
+1801;MONGOLIAN ELLIPSIS;Po;0;ON;;;;;N;;;;;
+1802;MONGOLIAN COMMA;Po;0;ON;;;;;N;;;;;
+1803;MONGOLIAN FULL STOP;Po;0;ON;;;;;N;;;;;
+1804;MONGOLIAN COLON;Po;0;ON;;;;;N;;;;;
+1805;MONGOLIAN FOUR DOTS;Po;0;ON;;;;;N;;;;;
+1806;MONGOLIAN TODO SOFT HYPHEN;Pd;0;ON;;;;;N;;;;;
+1807;MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER;Po;0;ON;;;;;N;;;;;
+1808;MONGOLIAN MANCHU COMMA;Po;0;ON;;;;;N;;;;;
+1809;MONGOLIAN MANCHU FULL STOP;Po;0;ON;;;;;N;;;;;
+180A;MONGOLIAN NIRUGU;Po;0;ON;;;;;N;;;;;
+180B;MONGOLIAN FREE VARIATION SELECTOR ONE;Mn;0;NSM;;;;;N;;;;;
+180C;MONGOLIAN FREE VARIATION SELECTOR TWO;Mn;0;NSM;;;;;N;;;;;
+180D;MONGOLIAN FREE VARIATION SELECTOR THREE;Mn;0;NSM;;;;;N;;;;;
+180E;MONGOLIAN VOWEL SEPARATOR;Cf;0;BN;;;;;N;;;;;
+1810;MONGOLIAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1811;MONGOLIAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1812;MONGOLIAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1813;MONGOLIAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1814;MONGOLIAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1815;MONGOLIAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1816;MONGOLIAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1817;MONGOLIAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1818;MONGOLIAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1819;MONGOLIAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1820;MONGOLIAN LETTER A;Lo;0;L;;;;;N;;;;;
+1821;MONGOLIAN LETTER E;Lo;0;L;;;;;N;;;;;
+1822;MONGOLIAN LETTER I;Lo;0;L;;;;;N;;;;;
+1823;MONGOLIAN LETTER O;Lo;0;L;;;;;N;;;;;
+1824;MONGOLIAN LETTER U;Lo;0;L;;;;;N;;;;;
+1825;MONGOLIAN LETTER OE;Lo;0;L;;;;;N;;;;;
+1826;MONGOLIAN LETTER UE;Lo;0;L;;;;;N;;;;;
+1827;MONGOLIAN LETTER EE;Lo;0;L;;;;;N;;;;;
+1828;MONGOLIAN LETTER NA;Lo;0;L;;;;;N;;;;;
+1829;MONGOLIAN LETTER ANG;Lo;0;L;;;;;N;;;;;
+182A;MONGOLIAN LETTER BA;Lo;0;L;;;;;N;;;;;
+182B;MONGOLIAN LETTER PA;Lo;0;L;;;;;N;;;;;
+182C;MONGOLIAN LETTER QA;Lo;0;L;;;;;N;;;;;
+182D;MONGOLIAN LETTER GA;Lo;0;L;;;;;N;;;;;
+182E;MONGOLIAN LETTER MA;Lo;0;L;;;;;N;;;;;
+182F;MONGOLIAN LETTER LA;Lo;0;L;;;;;N;;;;;
+1830;MONGOLIAN LETTER SA;Lo;0;L;;;;;N;;;;;
+1831;MONGOLIAN LETTER SHA;Lo;0;L;;;;;N;;;;;
+1832;MONGOLIAN LETTER TA;Lo;0;L;;;;;N;;;;;
+1833;MONGOLIAN LETTER DA;Lo;0;L;;;;;N;;;;;
+1834;MONGOLIAN LETTER CHA;Lo;0;L;;;;;N;;;;;
+1835;MONGOLIAN LETTER JA;Lo;0;L;;;;;N;;;;;
+1836;MONGOLIAN LETTER YA;Lo;0;L;;;;;N;;;;;
+1837;MONGOLIAN LETTER RA;Lo;0;L;;;;;N;;;;;
+1838;MONGOLIAN LETTER WA;Lo;0;L;;;;;N;;;;;
+1839;MONGOLIAN LETTER FA;Lo;0;L;;;;;N;;;;;
+183A;MONGOLIAN LETTER KA;Lo;0;L;;;;;N;;;;;
+183B;MONGOLIAN LETTER KHA;Lo;0;L;;;;;N;;;;;
+183C;MONGOLIAN LETTER TSA;Lo;0;L;;;;;N;;;;;
+183D;MONGOLIAN LETTER ZA;Lo;0;L;;;;;N;;;;;
+183E;MONGOLIAN LETTER HAA;Lo;0;L;;;;;N;;;;;
+183F;MONGOLIAN LETTER ZRA;Lo;0;L;;;;;N;;;;;
+1840;MONGOLIAN LETTER LHA;Lo;0;L;;;;;N;;;;;
+1841;MONGOLIAN LETTER ZHI;Lo;0;L;;;;;N;;;;;
+1842;MONGOLIAN LETTER CHI;Lo;0;L;;;;;N;;;;;
+1843;MONGOLIAN LETTER TODO LONG VOWEL SIGN;Lm;0;L;;;;;N;;;;;
+1844;MONGOLIAN LETTER TODO E;Lo;0;L;;;;;N;;;;;
+1845;MONGOLIAN LETTER TODO I;Lo;0;L;;;;;N;;;;;
+1846;MONGOLIAN LETTER TODO O;Lo;0;L;;;;;N;;;;;
+1847;MONGOLIAN LETTER TODO U;Lo;0;L;;;;;N;;;;;
+1848;MONGOLIAN LETTER TODO OE;Lo;0;L;;;;;N;;;;;
+1849;MONGOLIAN LETTER TODO UE;Lo;0;L;;;;;N;;;;;
+184A;MONGOLIAN LETTER TODO ANG;Lo;0;L;;;;;N;;;;;
+184B;MONGOLIAN LETTER TODO BA;Lo;0;L;;;;;N;;;;;
+184C;MONGOLIAN LETTER TODO PA;Lo;0;L;;;;;N;;;;;
+184D;MONGOLIAN LETTER TODO QA;Lo;0;L;;;;;N;;;;;
+184E;MONGOLIAN LETTER TODO GA;Lo;0;L;;;;;N;;;;;
+184F;MONGOLIAN LETTER TODO MA;Lo;0;L;;;;;N;;;;;
+1850;MONGOLIAN LETTER TODO TA;Lo;0;L;;;;;N;;;;;
+1851;MONGOLIAN LETTER TODO DA;Lo;0;L;;;;;N;;;;;
+1852;MONGOLIAN LETTER TODO CHA;Lo;0;L;;;;;N;;;;;
+1853;MONGOLIAN LETTER TODO JA;Lo;0;L;;;;;N;;;;;
+1854;MONGOLIAN LETTER TODO TSA;Lo;0;L;;;;;N;;;;;
+1855;MONGOLIAN LETTER TODO YA;Lo;0;L;;;;;N;;;;;
+1856;MONGOLIAN LETTER TODO WA;Lo;0;L;;;;;N;;;;;
+1857;MONGOLIAN LETTER TODO KA;Lo;0;L;;;;;N;;;;;
+1858;MONGOLIAN LETTER TODO GAA;Lo;0;L;;;;;N;;;;;
+1859;MONGOLIAN LETTER TODO HAA;Lo;0;L;;;;;N;;;;;
+185A;MONGOLIAN LETTER TODO JIA;Lo;0;L;;;;;N;;;;;
+185B;MONGOLIAN LETTER TODO NIA;Lo;0;L;;;;;N;;;;;
+185C;MONGOLIAN LETTER TODO DZA;Lo;0;L;;;;;N;;;;;
+185D;MONGOLIAN LETTER SIBE E;Lo;0;L;;;;;N;;;;;
+185E;MONGOLIAN LETTER SIBE I;Lo;0;L;;;;;N;;;;;
+185F;MONGOLIAN LETTER SIBE IY;Lo;0;L;;;;;N;;;;;
+1860;MONGOLIAN LETTER SIBE UE;Lo;0;L;;;;;N;;;;;
+1861;MONGOLIAN LETTER SIBE U;Lo;0;L;;;;;N;;;;;
+1862;MONGOLIAN LETTER SIBE ANG;Lo;0;L;;;;;N;;;;;
+1863;MONGOLIAN LETTER SIBE KA;Lo;0;L;;;;;N;;;;;
+1864;MONGOLIAN LETTER SIBE GA;Lo;0;L;;;;;N;;;;;
+1865;MONGOLIAN LETTER SIBE HA;Lo;0;L;;;;;N;;;;;
+1866;MONGOLIAN LETTER SIBE PA;Lo;0;L;;;;;N;;;;;
+1867;MONGOLIAN LETTER SIBE SHA;Lo;0;L;;;;;N;;;;;
+1868;MONGOLIAN LETTER SIBE TA;Lo;0;L;;;;;N;;;;;
+1869;MONGOLIAN LETTER SIBE DA;Lo;0;L;;;;;N;;;;;
+186A;MONGOLIAN LETTER SIBE JA;Lo;0;L;;;;;N;;;;;
+186B;MONGOLIAN LETTER SIBE FA;Lo;0;L;;;;;N;;;;;
+186C;MONGOLIAN LETTER SIBE GAA;Lo;0;L;;;;;N;;;;;
+186D;MONGOLIAN LETTER SIBE HAA;Lo;0;L;;;;;N;;;;;
+186E;MONGOLIAN LETTER SIBE TSA;Lo;0;L;;;;;N;;;;;
+186F;MONGOLIAN LETTER SIBE ZA;Lo;0;L;;;;;N;;;;;
+1870;MONGOLIAN LETTER SIBE RAA;Lo;0;L;;;;;N;;;;;
+1871;MONGOLIAN LETTER SIBE CHA;Lo;0;L;;;;;N;;;;;
+1872;MONGOLIAN LETTER SIBE ZHA;Lo;0;L;;;;;N;;;;;
+1873;MONGOLIAN LETTER MANCHU I;Lo;0;L;;;;;N;;;;;
+1874;MONGOLIAN LETTER MANCHU KA;Lo;0;L;;;;;N;;;;;
+1875;MONGOLIAN LETTER MANCHU RA;Lo;0;L;;;;;N;;;;;
+1876;MONGOLIAN LETTER MANCHU FA;Lo;0;L;;;;;N;;;;;
+1877;MONGOLIAN LETTER MANCHU ZHA;Lo;0;L;;;;;N;;;;;
+1878;MONGOLIAN LETTER CHA WITH TWO DOTS;Lo;0;L;;;;;N;;;;;
+1880;MONGOLIAN LETTER ALI GALI ANUSVARA ONE;Lo;0;L;;;;;N;;;;;
+1881;MONGOLIAN LETTER ALI GALI VISARGA ONE;Lo;0;L;;;;;N;;;;;
+1882;MONGOLIAN LETTER ALI GALI DAMARU;Lo;0;L;;;;;N;;;;;
+1883;MONGOLIAN LETTER ALI GALI UBADAMA;Lo;0;L;;;;;N;;;;;
+1884;MONGOLIAN LETTER ALI GALI INVERTED UBADAMA;Lo;0;L;;;;;N;;;;;
+1885;MONGOLIAN LETTER ALI GALI BALUDA;Mn;0;NSM;;;;;N;;;;;
+1886;MONGOLIAN LETTER ALI GALI THREE BALUDA;Mn;0;NSM;;;;;N;;;;;
+1887;MONGOLIAN LETTER ALI GALI A;Lo;0;L;;;;;N;;;;;
+1888;MONGOLIAN LETTER ALI GALI I;Lo;0;L;;;;;N;;;;;
+1889;MONGOLIAN LETTER ALI GALI KA;Lo;0;L;;;;;N;;;;;
+188A;MONGOLIAN LETTER ALI GALI NGA;Lo;0;L;;;;;N;;;;;
+188B;MONGOLIAN LETTER ALI GALI CA;Lo;0;L;;;;;N;;;;;
+188C;MONGOLIAN LETTER ALI GALI TTA;Lo;0;L;;;;;N;;;;;
+188D;MONGOLIAN LETTER ALI GALI TTHA;Lo;0;L;;;;;N;;;;;
+188E;MONGOLIAN LETTER ALI GALI DDA;Lo;0;L;;;;;N;;;;;
+188F;MONGOLIAN LETTER ALI GALI NNA;Lo;0;L;;;;;N;;;;;
+1890;MONGOLIAN LETTER ALI GALI TA;Lo;0;L;;;;;N;;;;;
+1891;MONGOLIAN LETTER ALI GALI DA;Lo;0;L;;;;;N;;;;;
+1892;MONGOLIAN LETTER ALI GALI PA;Lo;0;L;;;;;N;;;;;
+1893;MONGOLIAN LETTER ALI GALI PHA;Lo;0;L;;;;;N;;;;;
+1894;MONGOLIAN LETTER ALI GALI SSA;Lo;0;L;;;;;N;;;;;
+1895;MONGOLIAN LETTER ALI GALI ZHA;Lo;0;L;;;;;N;;;;;
+1896;MONGOLIAN LETTER ALI GALI ZA;Lo;0;L;;;;;N;;;;;
+1897;MONGOLIAN LETTER ALI GALI AH;Lo;0;L;;;;;N;;;;;
+1898;MONGOLIAN LETTER TODO ALI GALI TA;Lo;0;L;;;;;N;;;;;
+1899;MONGOLIAN LETTER TODO ALI GALI ZHA;Lo;0;L;;;;;N;;;;;
+189A;MONGOLIAN LETTER MANCHU ALI GALI GHA;Lo;0;L;;;;;N;;;;;
+189B;MONGOLIAN LETTER MANCHU ALI GALI NGA;Lo;0;L;;;;;N;;;;;
+189C;MONGOLIAN LETTER MANCHU ALI GALI CA;Lo;0;L;;;;;N;;;;;
+189D;MONGOLIAN LETTER MANCHU ALI GALI JHA;Lo;0;L;;;;;N;;;;;
+189E;MONGOLIAN LETTER MANCHU ALI GALI TTA;Lo;0;L;;;;;N;;;;;
+189F;MONGOLIAN LETTER MANCHU ALI GALI DDHA;Lo;0;L;;;;;N;;;;;
+18A0;MONGOLIAN LETTER MANCHU ALI GALI TA;Lo;0;L;;;;;N;;;;;
+18A1;MONGOLIAN LETTER MANCHU ALI GALI DHA;Lo;0;L;;;;;N;;;;;
+18A2;MONGOLIAN LETTER MANCHU ALI GALI SSA;Lo;0;L;;;;;N;;;;;
+18A3;MONGOLIAN LETTER MANCHU ALI GALI CYA;Lo;0;L;;;;;N;;;;;
+18A4;MONGOLIAN LETTER MANCHU ALI GALI ZHA;Lo;0;L;;;;;N;;;;;
+18A5;MONGOLIAN LETTER MANCHU ALI GALI ZA;Lo;0;L;;;;;N;;;;;
+18A6;MONGOLIAN LETTER ALI GALI HALF U;Lo;0;L;;;;;N;;;;;
+18A7;MONGOLIAN LETTER ALI GALI HALF YA;Lo;0;L;;;;;N;;;;;
+18A8;MONGOLIAN LETTER MANCHU ALI GALI BHA;Lo;0;L;;;;;N;;;;;
+18A9;MONGOLIAN LETTER ALI GALI DAGALGA;Mn;228;NSM;;;;;N;;;;;
+18AA;MONGOLIAN LETTER MANCHU ALI GALI LHA;Lo;0;L;;;;;N;;;;;
+18B0;CANADIAN SYLLABICS OY;Lo;0;L;;;;;N;;;;;
+18B1;CANADIAN SYLLABICS AY;Lo;0;L;;;;;N;;;;;
+18B2;CANADIAN SYLLABICS AAY;Lo;0;L;;;;;N;;;;;
+18B3;CANADIAN SYLLABICS WAY;Lo;0;L;;;;;N;;;;;
+18B4;CANADIAN SYLLABICS POY;Lo;0;L;;;;;N;;;;;
+18B5;CANADIAN SYLLABICS PAY;Lo;0;L;;;;;N;;;;;
+18B6;CANADIAN SYLLABICS PWOY;Lo;0;L;;;;;N;;;;;
+18B7;CANADIAN SYLLABICS TAY;Lo;0;L;;;;;N;;;;;
+18B8;CANADIAN SYLLABICS KAY;Lo;0;L;;;;;N;;;;;
+18B9;CANADIAN SYLLABICS KWAY;Lo;0;L;;;;;N;;;;;
+18BA;CANADIAN SYLLABICS MAY;Lo;0;L;;;;;N;;;;;
+18BB;CANADIAN SYLLABICS NOY;Lo;0;L;;;;;N;;;;;
+18BC;CANADIAN SYLLABICS NAY;Lo;0;L;;;;;N;;;;;
+18BD;CANADIAN SYLLABICS LAY;Lo;0;L;;;;;N;;;;;
+18BE;CANADIAN SYLLABICS SOY;Lo;0;L;;;;;N;;;;;
+18BF;CANADIAN SYLLABICS SAY;Lo;0;L;;;;;N;;;;;
+18C0;CANADIAN SYLLABICS SHOY;Lo;0;L;;;;;N;;;;;
+18C1;CANADIAN SYLLABICS SHAY;Lo;0;L;;;;;N;;;;;
+18C2;CANADIAN SYLLABICS SHWOY;Lo;0;L;;;;;N;;;;;
+18C3;CANADIAN SYLLABICS YOY;Lo;0;L;;;;;N;;;;;
+18C4;CANADIAN SYLLABICS YAY;Lo;0;L;;;;;N;;;;;
+18C5;CANADIAN SYLLABICS RAY;Lo;0;L;;;;;N;;;;;
+18C6;CANADIAN SYLLABICS NWI;Lo;0;L;;;;;N;;;;;
+18C7;CANADIAN SYLLABICS OJIBWAY NWI;Lo;0;L;;;;;N;;;;;
+18C8;CANADIAN SYLLABICS NWII;Lo;0;L;;;;;N;;;;;
+18C9;CANADIAN SYLLABICS OJIBWAY NWII;Lo;0;L;;;;;N;;;;;
+18CA;CANADIAN SYLLABICS NWO;Lo;0;L;;;;;N;;;;;
+18CB;CANADIAN SYLLABICS OJIBWAY NWO;Lo;0;L;;;;;N;;;;;
+18CC;CANADIAN SYLLABICS NWOO;Lo;0;L;;;;;N;;;;;
+18CD;CANADIAN SYLLABICS OJIBWAY NWOO;Lo;0;L;;;;;N;;;;;
+18CE;CANADIAN SYLLABICS RWEE;Lo;0;L;;;;;N;;;;;
+18CF;CANADIAN SYLLABICS RWI;Lo;0;L;;;;;N;;;;;
+18D0;CANADIAN SYLLABICS RWII;Lo;0;L;;;;;N;;;;;
+18D1;CANADIAN SYLLABICS RWO;Lo;0;L;;;;;N;;;;;
+18D2;CANADIAN SYLLABICS RWOO;Lo;0;L;;;;;N;;;;;
+18D3;CANADIAN SYLLABICS RWA;Lo;0;L;;;;;N;;;;;
+18D4;CANADIAN SYLLABICS OJIBWAY P;Lo;0;L;;;;;N;;;;;
+18D5;CANADIAN SYLLABICS OJIBWAY T;Lo;0;L;;;;;N;;;;;
+18D6;CANADIAN SYLLABICS OJIBWAY K;Lo;0;L;;;;;N;;;;;
+18D7;CANADIAN SYLLABICS OJIBWAY C;Lo;0;L;;;;;N;;;;;
+18D8;CANADIAN SYLLABICS OJIBWAY M;Lo;0;L;;;;;N;;;;;
+18D9;CANADIAN SYLLABICS OJIBWAY N;Lo;0;L;;;;;N;;;;;
+18DA;CANADIAN SYLLABICS OJIBWAY S;Lo;0;L;;;;;N;;;;;
+18DB;CANADIAN SYLLABICS OJIBWAY SH;Lo;0;L;;;;;N;;;;;
+18DC;CANADIAN SYLLABICS EASTERN W;Lo;0;L;;;;;N;;;;;
+18DD;CANADIAN SYLLABICS WESTERN W;Lo;0;L;;;;;N;;;;;
+18DE;CANADIAN SYLLABICS FINAL SMALL RING;Lo;0;L;;;;;N;;;;;
+18DF;CANADIAN SYLLABICS FINAL RAISED DOT;Lo;0;L;;;;;N;;;;;
+18E0;CANADIAN SYLLABICS R-CREE RWE;Lo;0;L;;;;;N;;;;;
+18E1;CANADIAN SYLLABICS WEST-CREE LOO;Lo;0;L;;;;;N;;;;;
+18E2;CANADIAN SYLLABICS WEST-CREE LAA;Lo;0;L;;;;;N;;;;;
+18E3;CANADIAN SYLLABICS THWE;Lo;0;L;;;;;N;;;;;
+18E4;CANADIAN SYLLABICS THWA;Lo;0;L;;;;;N;;;;;
+18E5;CANADIAN SYLLABICS TTHWE;Lo;0;L;;;;;N;;;;;
+18E6;CANADIAN SYLLABICS TTHOO;Lo;0;L;;;;;N;;;;;
+18E7;CANADIAN SYLLABICS TTHAA;Lo;0;L;;;;;N;;;;;
+18E8;CANADIAN SYLLABICS TLHWE;Lo;0;L;;;;;N;;;;;
+18E9;CANADIAN SYLLABICS TLHOO;Lo;0;L;;;;;N;;;;;
+18EA;CANADIAN SYLLABICS SAYISI SHWE;Lo;0;L;;;;;N;;;;;
+18EB;CANADIAN SYLLABICS SAYISI SHOO;Lo;0;L;;;;;N;;;;;
+18EC;CANADIAN SYLLABICS SAYISI HOO;Lo;0;L;;;;;N;;;;;
+18ED;CANADIAN SYLLABICS CARRIER GWU;Lo;0;L;;;;;N;;;;;
+18EE;CANADIAN SYLLABICS CARRIER DENE GEE;Lo;0;L;;;;;N;;;;;
+18EF;CANADIAN SYLLABICS CARRIER GAA;Lo;0;L;;;;;N;;;;;
+18F0;CANADIAN SYLLABICS CARRIER GWA;Lo;0;L;;;;;N;;;;;
+18F1;CANADIAN SYLLABICS SAYISI JUU;Lo;0;L;;;;;N;;;;;
+18F2;CANADIAN SYLLABICS CARRIER JWA;Lo;0;L;;;;;N;;;;;
+18F3;CANADIAN SYLLABICS BEAVER DENE L;Lo;0;L;;;;;N;;;;;
+18F4;CANADIAN SYLLABICS BEAVER DENE R;Lo;0;L;;;;;N;;;;;
+18F5;CANADIAN SYLLABICS CARRIER DENTAL S;Lo;0;L;;;;;N;;;;;
+1900;LIMBU VOWEL-CARRIER LETTER;Lo;0;L;;;;;N;;;;;
+1901;LIMBU LETTER KA;Lo;0;L;;;;;N;;;;;
+1902;LIMBU LETTER KHA;Lo;0;L;;;;;N;;;;;
+1903;LIMBU LETTER GA;Lo;0;L;;;;;N;;;;;
+1904;LIMBU LETTER GHA;Lo;0;L;;;;;N;;;;;
+1905;LIMBU LETTER NGA;Lo;0;L;;;;;N;;;;;
+1906;LIMBU LETTER CA;Lo;0;L;;;;;N;;;;;
+1907;LIMBU LETTER CHA;Lo;0;L;;;;;N;;;;;
+1908;LIMBU LETTER JA;Lo;0;L;;;;;N;;;;;
+1909;LIMBU LETTER JHA;Lo;0;L;;;;;N;;;;;
+190A;LIMBU LETTER YAN;Lo;0;L;;;;;N;;;;;
+190B;LIMBU LETTER TA;Lo;0;L;;;;;N;;;;;
+190C;LIMBU LETTER THA;Lo;0;L;;;;;N;;;;;
+190D;LIMBU LETTER DA;Lo;0;L;;;;;N;;;;;
+190E;LIMBU LETTER DHA;Lo;0;L;;;;;N;;;;;
+190F;LIMBU LETTER NA;Lo;0;L;;;;;N;;;;;
+1910;LIMBU LETTER PA;Lo;0;L;;;;;N;;;;;
+1911;LIMBU LETTER PHA;Lo;0;L;;;;;N;;;;;
+1912;LIMBU LETTER BA;Lo;0;L;;;;;N;;;;;
+1913;LIMBU LETTER BHA;Lo;0;L;;;;;N;;;;;
+1914;LIMBU LETTER MA;Lo;0;L;;;;;N;;;;;
+1915;LIMBU LETTER YA;Lo;0;L;;;;;N;;;;;
+1916;LIMBU LETTER RA;Lo;0;L;;;;;N;;;;;
+1917;LIMBU LETTER LA;Lo;0;L;;;;;N;;;;;
+1918;LIMBU LETTER WA;Lo;0;L;;;;;N;;;;;
+1919;LIMBU LETTER SHA;Lo;0;L;;;;;N;;;;;
+191A;LIMBU LETTER SSA;Lo;0;L;;;;;N;;;;;
+191B;LIMBU LETTER SA;Lo;0;L;;;;;N;;;;;
+191C;LIMBU LETTER HA;Lo;0;L;;;;;N;;;;;
+191D;LIMBU LETTER GYAN;Lo;0;L;;;;;N;;;;;
+191E;LIMBU LETTER TRA;Lo;0;L;;;;;N;;;;;
+1920;LIMBU VOWEL SIGN A;Mn;0;NSM;;;;;N;;;;;
+1921;LIMBU VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+1922;LIMBU VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1923;LIMBU VOWEL SIGN EE;Mc;0;L;;;;;N;;;;;
+1924;LIMBU VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+1925;LIMBU VOWEL SIGN OO;Mc;0;L;;;;;N;;;;;
+1926;LIMBU VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+1927;LIMBU VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+1928;LIMBU VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+1929;LIMBU SUBJOINED LETTER YA;Mc;0;L;;;;;N;;;;;
+192A;LIMBU SUBJOINED LETTER RA;Mc;0;L;;;;;N;;;;;
+192B;LIMBU SUBJOINED LETTER WA;Mc;0;L;;;;;N;;;;;
+1930;LIMBU SMALL LETTER KA;Mc;0;L;;;;;N;;;;;
+1931;LIMBU SMALL LETTER NGA;Mc;0;L;;;;;N;;;;;
+1932;LIMBU SMALL LETTER ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+1933;LIMBU SMALL LETTER TA;Mc;0;L;;;;;N;;;;;
+1934;LIMBU SMALL LETTER NA;Mc;0;L;;;;;N;;;;;
+1935;LIMBU SMALL LETTER PA;Mc;0;L;;;;;N;;;;;
+1936;LIMBU SMALL LETTER MA;Mc;0;L;;;;;N;;;;;
+1937;LIMBU SMALL LETTER RA;Mc;0;L;;;;;N;;;;;
+1938;LIMBU SMALL LETTER LA;Mc;0;L;;;;;N;;;;;
+1939;LIMBU SIGN MUKPHRENG;Mn;222;NSM;;;;;N;;;;;
+193A;LIMBU SIGN KEMPHRENG;Mn;230;NSM;;;;;N;;;;;
+193B;LIMBU SIGN SA-I;Mn;220;NSM;;;;;N;;;;;
+1940;LIMBU SIGN LOO;So;0;ON;;;;;N;;;;;
+1944;LIMBU EXCLAMATION MARK;Po;0;ON;;;;;N;;;;;
+1945;LIMBU QUESTION MARK;Po;0;ON;;;;;N;;;;;
+1946;LIMBU DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1947;LIMBU DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1948;LIMBU DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1949;LIMBU DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+194A;LIMBU DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+194B;LIMBU DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+194C;LIMBU DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+194D;LIMBU DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+194E;LIMBU DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+194F;LIMBU DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1950;TAI LE LETTER KA;Lo;0;L;;;;;N;;;;;
+1951;TAI LE LETTER XA;Lo;0;L;;;;;N;;;;;
+1952;TAI LE LETTER NGA;Lo;0;L;;;;;N;;;;;
+1953;TAI LE LETTER TSA;Lo;0;L;;;;;N;;;;;
+1954;TAI LE LETTER SA;Lo;0;L;;;;;N;;;;;
+1955;TAI LE LETTER YA;Lo;0;L;;;;;N;;;;;
+1956;TAI LE LETTER TA;Lo;0;L;;;;;N;;;;;
+1957;TAI LE LETTER THA;Lo;0;L;;;;;N;;;;;
+1958;TAI LE LETTER LA;Lo;0;L;;;;;N;;;;;
+1959;TAI LE LETTER PA;Lo;0;L;;;;;N;;;;;
+195A;TAI LE LETTER PHA;Lo;0;L;;;;;N;;;;;
+195B;TAI LE LETTER MA;Lo;0;L;;;;;N;;;;;
+195C;TAI LE LETTER FA;Lo;0;L;;;;;N;;;;;
+195D;TAI LE LETTER VA;Lo;0;L;;;;;N;;;;;
+195E;TAI LE LETTER HA;Lo;0;L;;;;;N;;;;;
+195F;TAI LE LETTER QA;Lo;0;L;;;;;N;;;;;
+1960;TAI LE LETTER KHA;Lo;0;L;;;;;N;;;;;
+1961;TAI LE LETTER TSHA;Lo;0;L;;;;;N;;;;;
+1962;TAI LE LETTER NA;Lo;0;L;;;;;N;;;;;
+1963;TAI LE LETTER A;Lo;0;L;;;;;N;;;;;
+1964;TAI LE LETTER I;Lo;0;L;;;;;N;;;;;
+1965;TAI LE LETTER EE;Lo;0;L;;;;;N;;;;;
+1966;TAI LE LETTER EH;Lo;0;L;;;;;N;;;;;
+1967;TAI LE LETTER U;Lo;0;L;;;;;N;;;;;
+1968;TAI LE LETTER OO;Lo;0;L;;;;;N;;;;;
+1969;TAI LE LETTER O;Lo;0;L;;;;;N;;;;;
+196A;TAI LE LETTER UE;Lo;0;L;;;;;N;;;;;
+196B;TAI LE LETTER E;Lo;0;L;;;;;N;;;;;
+196C;TAI LE LETTER AUE;Lo;0;L;;;;;N;;;;;
+196D;TAI LE LETTER AI;Lo;0;L;;;;;N;;;;;
+1970;TAI LE LETTER TONE-2;Lo;0;L;;;;;N;;;;;
+1971;TAI LE LETTER TONE-3;Lo;0;L;;;;;N;;;;;
+1972;TAI LE LETTER TONE-4;Lo;0;L;;;;;N;;;;;
+1973;TAI LE LETTER TONE-5;Lo;0;L;;;;;N;;;;;
+1974;TAI LE LETTER TONE-6;Lo;0;L;;;;;N;;;;;
+1980;NEW TAI LUE LETTER HIGH QA;Lo;0;L;;;;;N;;;;;
+1981;NEW TAI LUE LETTER LOW QA;Lo;0;L;;;;;N;;;;;
+1982;NEW TAI LUE LETTER HIGH KA;Lo;0;L;;;;;N;;;;;
+1983;NEW TAI LUE LETTER HIGH XA;Lo;0;L;;;;;N;;;;;
+1984;NEW TAI LUE LETTER HIGH NGA;Lo;0;L;;;;;N;;;;;
+1985;NEW TAI LUE LETTER LOW KA;Lo;0;L;;;;;N;;;;;
+1986;NEW TAI LUE LETTER LOW XA;Lo;0;L;;;;;N;;;;;
+1987;NEW TAI LUE LETTER LOW NGA;Lo;0;L;;;;;N;;;;;
+1988;NEW TAI LUE LETTER HIGH TSA;Lo;0;L;;;;;N;;;;;
+1989;NEW TAI LUE LETTER HIGH SA;Lo;0;L;;;;;N;;;;;
+198A;NEW TAI LUE LETTER HIGH YA;Lo;0;L;;;;;N;;;;;
+198B;NEW TAI LUE LETTER LOW TSA;Lo;0;L;;;;;N;;;;;
+198C;NEW TAI LUE LETTER LOW SA;Lo;0;L;;;;;N;;;;;
+198D;NEW TAI LUE LETTER LOW YA;Lo;0;L;;;;;N;;;;;
+198E;NEW TAI LUE LETTER HIGH TA;Lo;0;L;;;;;N;;;;;
+198F;NEW TAI LUE LETTER HIGH THA;Lo;0;L;;;;;N;;;;;
+1990;NEW TAI LUE LETTER HIGH NA;Lo;0;L;;;;;N;;;;;
+1991;NEW TAI LUE LETTER LOW TA;Lo;0;L;;;;;N;;;;;
+1992;NEW TAI LUE LETTER LOW THA;Lo;0;L;;;;;N;;;;;
+1993;NEW TAI LUE LETTER LOW NA;Lo;0;L;;;;;N;;;;;
+1994;NEW TAI LUE LETTER HIGH PA;Lo;0;L;;;;;N;;;;;
+1995;NEW TAI LUE LETTER HIGH PHA;Lo;0;L;;;;;N;;;;;
+1996;NEW TAI LUE LETTER HIGH MA;Lo;0;L;;;;;N;;;;;
+1997;NEW TAI LUE LETTER LOW PA;Lo;0;L;;;;;N;;;;;
+1998;NEW TAI LUE LETTER LOW PHA;Lo;0;L;;;;;N;;;;;
+1999;NEW TAI LUE LETTER LOW MA;Lo;0;L;;;;;N;;;;;
+199A;NEW TAI LUE LETTER HIGH FA;Lo;0;L;;;;;N;;;;;
+199B;NEW TAI LUE LETTER HIGH VA;Lo;0;L;;;;;N;;;;;
+199C;NEW TAI LUE LETTER HIGH LA;Lo;0;L;;;;;N;;;;;
+199D;NEW TAI LUE LETTER LOW FA;Lo;0;L;;;;;N;;;;;
+199E;NEW TAI LUE LETTER LOW VA;Lo;0;L;;;;;N;;;;;
+199F;NEW TAI LUE LETTER LOW LA;Lo;0;L;;;;;N;;;;;
+19A0;NEW TAI LUE LETTER HIGH HA;Lo;0;L;;;;;N;;;;;
+19A1;NEW TAI LUE LETTER HIGH DA;Lo;0;L;;;;;N;;;;;
+19A2;NEW TAI LUE LETTER HIGH BA;Lo;0;L;;;;;N;;;;;
+19A3;NEW TAI LUE LETTER LOW HA;Lo;0;L;;;;;N;;;;;
+19A4;NEW TAI LUE LETTER LOW DA;Lo;0;L;;;;;N;;;;;
+19A5;NEW TAI LUE LETTER LOW BA;Lo;0;L;;;;;N;;;;;
+19A6;NEW TAI LUE LETTER HIGH KVA;Lo;0;L;;;;;N;;;;;
+19A7;NEW TAI LUE LETTER HIGH XVA;Lo;0;L;;;;;N;;;;;
+19A8;NEW TAI LUE LETTER LOW KVA;Lo;0;L;;;;;N;;;;;
+19A9;NEW TAI LUE LETTER LOW XVA;Lo;0;L;;;;;N;;;;;
+19AA;NEW TAI LUE LETTER HIGH SUA;Lo;0;L;;;;;N;;;;;
+19AB;NEW TAI LUE LETTER LOW SUA;Lo;0;L;;;;;N;;;;;
+19B0;NEW TAI LUE VOWEL SIGN VOWEL SHORTENER;Lo;0;L;;;;;N;;;;;
+19B1;NEW TAI LUE VOWEL SIGN AA;Lo;0;L;;;;;N;;;;;
+19B2;NEW TAI LUE VOWEL SIGN II;Lo;0;L;;;;;N;;;;;
+19B3;NEW TAI LUE VOWEL SIGN U;Lo;0;L;;;;;N;;;;;
+19B4;NEW TAI LUE VOWEL SIGN UU;Lo;0;L;;;;;N;;;;;
+19B5;NEW TAI LUE VOWEL SIGN E;Lo;0;L;;;;;N;;;;;
+19B6;NEW TAI LUE VOWEL SIGN AE;Lo;0;L;;;;;N;;;;;
+19B7;NEW TAI LUE VOWEL SIGN O;Lo;0;L;;;;;N;;;;;
+19B8;NEW TAI LUE VOWEL SIGN OA;Lo;0;L;;;;;N;;;;;
+19B9;NEW TAI LUE VOWEL SIGN UE;Lo;0;L;;;;;N;;;;;
+19BA;NEW TAI LUE VOWEL SIGN AY;Lo;0;L;;;;;N;;;;;
+19BB;NEW TAI LUE VOWEL SIGN AAY;Lo;0;L;;;;;N;;;;;
+19BC;NEW TAI LUE VOWEL SIGN UY;Lo;0;L;;;;;N;;;;;
+19BD;NEW TAI LUE VOWEL SIGN OY;Lo;0;L;;;;;N;;;;;
+19BE;NEW TAI LUE VOWEL SIGN OAY;Lo;0;L;;;;;N;;;;;
+19BF;NEW TAI LUE VOWEL SIGN UEY;Lo;0;L;;;;;N;;;;;
+19C0;NEW TAI LUE VOWEL SIGN IY;Lo;0;L;;;;;N;;;;;
+19C1;NEW TAI LUE LETTER FINAL V;Lo;0;L;;;;;N;;;;;
+19C2;NEW TAI LUE LETTER FINAL NG;Lo;0;L;;;;;N;;;;;
+19C3;NEW TAI LUE LETTER FINAL N;Lo;0;L;;;;;N;;;;;
+19C4;NEW TAI LUE LETTER FINAL M;Lo;0;L;;;;;N;;;;;
+19C5;NEW TAI LUE LETTER FINAL K;Lo;0;L;;;;;N;;;;;
+19C6;NEW TAI LUE LETTER FINAL D;Lo;0;L;;;;;N;;;;;
+19C7;NEW TAI LUE LETTER FINAL B;Lo;0;L;;;;;N;;;;;
+19C8;NEW TAI LUE TONE MARK-1;Lo;0;L;;;;;N;;;;;
+19C9;NEW TAI LUE TONE MARK-2;Lo;0;L;;;;;N;;;;;
+19D0;NEW TAI LUE DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+19D1;NEW TAI LUE DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+19D2;NEW TAI LUE DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+19D3;NEW TAI LUE DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+19D4;NEW TAI LUE DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+19D5;NEW TAI LUE DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+19D6;NEW TAI LUE DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+19D7;NEW TAI LUE DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+19D8;NEW TAI LUE DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+19D9;NEW TAI LUE DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+19DA;NEW TAI LUE THAM DIGIT ONE;No;0;L;;;1;1;N;;;;;
+19DE;NEW TAI LUE SIGN LAE;So;0;ON;;;;;N;;;;;
+19DF;NEW TAI LUE SIGN LAEV;So;0;ON;;;;;N;;;;;
+19E0;KHMER SYMBOL PATHAMASAT;So;0;ON;;;;;N;;;;;
+19E1;KHMER SYMBOL MUOY KOET;So;0;ON;;;;;N;;;;;
+19E2;KHMER SYMBOL PII KOET;So;0;ON;;;;;N;;;;;
+19E3;KHMER SYMBOL BEI KOET;So;0;ON;;;;;N;;;;;
+19E4;KHMER SYMBOL BUON KOET;So;0;ON;;;;;N;;;;;
+19E5;KHMER SYMBOL PRAM KOET;So;0;ON;;;;;N;;;;;
+19E6;KHMER SYMBOL PRAM-MUOY KOET;So;0;ON;;;;;N;;;;;
+19E7;KHMER SYMBOL PRAM-PII KOET;So;0;ON;;;;;N;;;;;
+19E8;KHMER SYMBOL PRAM-BEI KOET;So;0;ON;;;;;N;;;;;
+19E9;KHMER SYMBOL PRAM-BUON KOET;So;0;ON;;;;;N;;;;;
+19EA;KHMER SYMBOL DAP KOET;So;0;ON;;;;;N;;;;;
+19EB;KHMER SYMBOL DAP-MUOY KOET;So;0;ON;;;;;N;;;;;
+19EC;KHMER SYMBOL DAP-PII KOET;So;0;ON;;;;;N;;;;;
+19ED;KHMER SYMBOL DAP-BEI KOET;So;0;ON;;;;;N;;;;;
+19EE;KHMER SYMBOL DAP-BUON KOET;So;0;ON;;;;;N;;;;;
+19EF;KHMER SYMBOL DAP-PRAM KOET;So;0;ON;;;;;N;;;;;
+19F0;KHMER SYMBOL TUTEYASAT;So;0;ON;;;;;N;;;;;
+19F1;KHMER SYMBOL MUOY ROC;So;0;ON;;;;;N;;;;;
+19F2;KHMER SYMBOL PII ROC;So;0;ON;;;;;N;;;;;
+19F3;KHMER SYMBOL BEI ROC;So;0;ON;;;;;N;;;;;
+19F4;KHMER SYMBOL BUON ROC;So;0;ON;;;;;N;;;;;
+19F5;KHMER SYMBOL PRAM ROC;So;0;ON;;;;;N;;;;;
+19F6;KHMER SYMBOL PRAM-MUOY ROC;So;0;ON;;;;;N;;;;;
+19F7;KHMER SYMBOL PRAM-PII ROC;So;0;ON;;;;;N;;;;;
+19F8;KHMER SYMBOL PRAM-BEI ROC;So;0;ON;;;;;N;;;;;
+19F9;KHMER SYMBOL PRAM-BUON ROC;So;0;ON;;;;;N;;;;;
+19FA;KHMER SYMBOL DAP ROC;So;0;ON;;;;;N;;;;;
+19FB;KHMER SYMBOL DAP-MUOY ROC;So;0;ON;;;;;N;;;;;
+19FC;KHMER SYMBOL DAP-PII ROC;So;0;ON;;;;;N;;;;;
+19FD;KHMER SYMBOL DAP-BEI ROC;So;0;ON;;;;;N;;;;;
+19FE;KHMER SYMBOL DAP-BUON ROC;So;0;ON;;;;;N;;;;;
+19FF;KHMER SYMBOL DAP-PRAM ROC;So;0;ON;;;;;N;;;;;
+1A00;BUGINESE LETTER KA;Lo;0;L;;;;;N;;;;;
+1A01;BUGINESE LETTER GA;Lo;0;L;;;;;N;;;;;
+1A02;BUGINESE LETTER NGA;Lo;0;L;;;;;N;;;;;
+1A03;BUGINESE LETTER NGKA;Lo;0;L;;;;;N;;;;;
+1A04;BUGINESE LETTER PA;Lo;0;L;;;;;N;;;;;
+1A05;BUGINESE LETTER BA;Lo;0;L;;;;;N;;;;;
+1A06;BUGINESE LETTER MA;Lo;0;L;;;;;N;;;;;
+1A07;BUGINESE LETTER MPA;Lo;0;L;;;;;N;;;;;
+1A08;BUGINESE LETTER TA;Lo;0;L;;;;;N;;;;;
+1A09;BUGINESE LETTER DA;Lo;0;L;;;;;N;;;;;
+1A0A;BUGINESE LETTER NA;Lo;0;L;;;;;N;;;;;
+1A0B;BUGINESE LETTER NRA;Lo;0;L;;;;;N;;;;;
+1A0C;BUGINESE LETTER CA;Lo;0;L;;;;;N;;;;;
+1A0D;BUGINESE LETTER JA;Lo;0;L;;;;;N;;;;;
+1A0E;BUGINESE LETTER NYA;Lo;0;L;;;;;N;;;;;
+1A0F;BUGINESE LETTER NYCA;Lo;0;L;;;;;N;;;;;
+1A10;BUGINESE LETTER YA;Lo;0;L;;;;;N;;;;;
+1A11;BUGINESE LETTER RA;Lo;0;L;;;;;N;;;;;
+1A12;BUGINESE LETTER LA;Lo;0;L;;;;;N;;;;;
+1A13;BUGINESE LETTER VA;Lo;0;L;;;;;N;;;;;
+1A14;BUGINESE LETTER SA;Lo;0;L;;;;;N;;;;;
+1A15;BUGINESE LETTER A;Lo;0;L;;;;;N;;;;;
+1A16;BUGINESE LETTER HA;Lo;0;L;;;;;N;;;;;
+1A17;BUGINESE VOWEL SIGN I;Mn;230;NSM;;;;;N;;;;;
+1A18;BUGINESE VOWEL SIGN U;Mn;220;NSM;;;;;N;;;;;
+1A19;BUGINESE VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+1A1A;BUGINESE VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+1A1B;BUGINESE VOWEL SIGN AE;Mn;0;NSM;;;;;N;;;;;
+1A1E;BUGINESE PALLAWA;Po;0;L;;;;;N;;;;;
+1A1F;BUGINESE END OF SECTION;Po;0;L;;;;;N;;;;;
+1A20;TAI THAM LETTER HIGH KA;Lo;0;L;;;;;N;;;;;
+1A21;TAI THAM LETTER HIGH KHA;Lo;0;L;;;;;N;;;;;
+1A22;TAI THAM LETTER HIGH KXA;Lo;0;L;;;;;N;;;;;
+1A23;TAI THAM LETTER LOW KA;Lo;0;L;;;;;N;;;;;
+1A24;TAI THAM LETTER LOW KXA;Lo;0;L;;;;;N;;;;;
+1A25;TAI THAM LETTER LOW KHA;Lo;0;L;;;;;N;;;;;
+1A26;TAI THAM LETTER NGA;Lo;0;L;;;;;N;;;;;
+1A27;TAI THAM LETTER HIGH CA;Lo;0;L;;;;;N;;;;;
+1A28;TAI THAM LETTER HIGH CHA;Lo;0;L;;;;;N;;;;;
+1A29;TAI THAM LETTER LOW CA;Lo;0;L;;;;;N;;;;;
+1A2A;TAI THAM LETTER LOW SA;Lo;0;L;;;;;N;;;;;
+1A2B;TAI THAM LETTER LOW CHA;Lo;0;L;;;;;N;;;;;
+1A2C;TAI THAM LETTER NYA;Lo;0;L;;;;;N;;;;;
+1A2D;TAI THAM LETTER RATA;Lo;0;L;;;;;N;;;;;
+1A2E;TAI THAM LETTER HIGH RATHA;Lo;0;L;;;;;N;;;;;
+1A2F;TAI THAM LETTER DA;Lo;0;L;;;;;N;;;;;
+1A30;TAI THAM LETTER LOW RATHA;Lo;0;L;;;;;N;;;;;
+1A31;TAI THAM LETTER RANA;Lo;0;L;;;;;N;;;;;
+1A32;TAI THAM LETTER HIGH TA;Lo;0;L;;;;;N;;;;;
+1A33;TAI THAM LETTER HIGH THA;Lo;0;L;;;;;N;;;;;
+1A34;TAI THAM LETTER LOW TA;Lo;0;L;;;;;N;;;;;
+1A35;TAI THAM LETTER LOW THA;Lo;0;L;;;;;N;;;;;
+1A36;TAI THAM LETTER NA;Lo;0;L;;;;;N;;;;;
+1A37;TAI THAM LETTER BA;Lo;0;L;;;;;N;;;;;
+1A38;TAI THAM LETTER HIGH PA;Lo;0;L;;;;;N;;;;;
+1A39;TAI THAM LETTER HIGH PHA;Lo;0;L;;;;;N;;;;;
+1A3A;TAI THAM LETTER HIGH FA;Lo;0;L;;;;;N;;;;;
+1A3B;TAI THAM LETTER LOW PA;Lo;0;L;;;;;N;;;;;
+1A3C;TAI THAM LETTER LOW FA;Lo;0;L;;;;;N;;;;;
+1A3D;TAI THAM LETTER LOW PHA;Lo;0;L;;;;;N;;;;;
+1A3E;TAI THAM LETTER MA;Lo;0;L;;;;;N;;;;;
+1A3F;TAI THAM LETTER LOW YA;Lo;0;L;;;;;N;;;;;
+1A40;TAI THAM LETTER HIGH YA;Lo;0;L;;;;;N;;;;;
+1A41;TAI THAM LETTER RA;Lo;0;L;;;;;N;;;;;
+1A42;TAI THAM LETTER RUE;Lo;0;L;;;;;N;;;;;
+1A43;TAI THAM LETTER LA;Lo;0;L;;;;;N;;;;;
+1A44;TAI THAM LETTER LUE;Lo;0;L;;;;;N;;;;;
+1A45;TAI THAM LETTER WA;Lo;0;L;;;;;N;;;;;
+1A46;TAI THAM LETTER HIGH SHA;Lo;0;L;;;;;N;;;;;
+1A47;TAI THAM LETTER HIGH SSA;Lo;0;L;;;;;N;;;;;
+1A48;TAI THAM LETTER HIGH SA;Lo;0;L;;;;;N;;;;;
+1A49;TAI THAM LETTER HIGH HA;Lo;0;L;;;;;N;;;;;
+1A4A;TAI THAM LETTER LLA;Lo;0;L;;;;;N;;;;;
+1A4B;TAI THAM LETTER A;Lo;0;L;;;;;N;;;;;
+1A4C;TAI THAM LETTER LOW HA;Lo;0;L;;;;;N;;;;;
+1A4D;TAI THAM LETTER I;Lo;0;L;;;;;N;;;;;
+1A4E;TAI THAM LETTER II;Lo;0;L;;;;;N;;;;;
+1A4F;TAI THAM LETTER U;Lo;0;L;;;;;N;;;;;
+1A50;TAI THAM LETTER UU;Lo;0;L;;;;;N;;;;;
+1A51;TAI THAM LETTER EE;Lo;0;L;;;;;N;;;;;
+1A52;TAI THAM LETTER OO;Lo;0;L;;;;;N;;;;;
+1A53;TAI THAM LETTER LAE;Lo;0;L;;;;;N;;;;;
+1A54;TAI THAM LETTER GREAT SA;Lo;0;L;;;;;N;;;;;
+1A55;TAI THAM CONSONANT SIGN MEDIAL RA;Mc;0;L;;;;;N;;;;;
+1A56;TAI THAM CONSONANT SIGN MEDIAL LA;Mn;0;NSM;;;;;N;;;;;
+1A57;TAI THAM CONSONANT SIGN LA TANG LAI;Mc;0;L;;;;;N;;;;;
+1A58;TAI THAM SIGN MAI KANG LAI;Mn;0;NSM;;;;;N;;;;;
+1A59;TAI THAM CONSONANT SIGN FINAL NGA;Mn;0;NSM;;;;;N;;;;;
+1A5A;TAI THAM CONSONANT SIGN LOW PA;Mn;0;NSM;;;;;N;;;;;
+1A5B;TAI THAM CONSONANT SIGN HIGH RATHA OR LOW PA;Mn;0;NSM;;;;;N;;;;;
+1A5C;TAI THAM CONSONANT SIGN MA;Mn;0;NSM;;;;;N;;;;;
+1A5D;TAI THAM CONSONANT SIGN BA;Mn;0;NSM;;;;;N;;;;;
+1A5E;TAI THAM CONSONANT SIGN SA;Mn;0;NSM;;;;;N;;;;;
+1A60;TAI THAM SIGN SAKOT;Mn;9;NSM;;;;;N;;;;;
+1A61;TAI THAM VOWEL SIGN A;Mc;0;L;;;;;N;;;;;
+1A62;TAI THAM VOWEL SIGN MAI SAT;Mn;0;NSM;;;;;N;;;;;
+1A63;TAI THAM VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+1A64;TAI THAM VOWEL SIGN TALL AA;Mc;0;L;;;;;N;;;;;
+1A65;TAI THAM VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+1A66;TAI THAM VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+1A67;TAI THAM VOWEL SIGN UE;Mn;0;NSM;;;;;N;;;;;
+1A68;TAI THAM VOWEL SIGN UUE;Mn;0;NSM;;;;;N;;;;;
+1A69;TAI THAM VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1A6A;TAI THAM VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+1A6B;TAI THAM VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+1A6C;TAI THAM VOWEL SIGN OA BELOW;Mn;0;NSM;;;;;N;;;;;
+1A6D;TAI THAM VOWEL SIGN OY;Mc;0;L;;;;;N;;;;;
+1A6E;TAI THAM VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+1A6F;TAI THAM VOWEL SIGN AE;Mc;0;L;;;;;N;;;;;
+1A70;TAI THAM VOWEL SIGN OO;Mc;0;L;;;;;N;;;;;
+1A71;TAI THAM VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+1A72;TAI THAM VOWEL SIGN THAM AI;Mc;0;L;;;;;N;;;;;
+1A73;TAI THAM VOWEL SIGN OA ABOVE;Mn;0;NSM;;;;;N;;;;;
+1A74;TAI THAM SIGN MAI KANG;Mn;0;NSM;;;;;N;;;;;
+1A75;TAI THAM SIGN TONE-1;Mn;230;NSM;;;;;N;;;;;
+1A76;TAI THAM SIGN TONE-2;Mn;230;NSM;;;;;N;;;;;
+1A77;TAI THAM SIGN KHUEN TONE-3;Mn;230;NSM;;;;;N;;;;;
+1A78;TAI THAM SIGN KHUEN TONE-4;Mn;230;NSM;;;;;N;;;;;
+1A79;TAI THAM SIGN KHUEN TONE-5;Mn;230;NSM;;;;;N;;;;;
+1A7A;TAI THAM SIGN RA HAAM;Mn;230;NSM;;;;;N;;;;;
+1A7B;TAI THAM SIGN MAI SAM;Mn;230;NSM;;;;;N;;;;;
+1A7C;TAI THAM SIGN KHUEN-LUE KARAN;Mn;230;NSM;;;;;N;;;;;
+1A7F;TAI THAM COMBINING CRYPTOGRAMMIC DOT;Mn;220;NSM;;;;;N;;;;;
+1A80;TAI THAM HORA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1A81;TAI THAM HORA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1A82;TAI THAM HORA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1A83;TAI THAM HORA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1A84;TAI THAM HORA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1A85;TAI THAM HORA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1A86;TAI THAM HORA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1A87;TAI THAM HORA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1A88;TAI THAM HORA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1A89;TAI THAM HORA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1A90;TAI THAM THAM DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1A91;TAI THAM THAM DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1A92;TAI THAM THAM DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1A93;TAI THAM THAM DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1A94;TAI THAM THAM DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1A95;TAI THAM THAM DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1A96;TAI THAM THAM DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1A97;TAI THAM THAM DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1A98;TAI THAM THAM DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1A99;TAI THAM THAM DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1AA0;TAI THAM SIGN WIANG;Po;0;L;;;;;N;;;;;
+1AA1;TAI THAM SIGN WIANGWAAK;Po;0;L;;;;;N;;;;;
+1AA2;TAI THAM SIGN SAWAN;Po;0;L;;;;;N;;;;;
+1AA3;TAI THAM SIGN KEOW;Po;0;L;;;;;N;;;;;
+1AA4;TAI THAM SIGN HOY;Po;0;L;;;;;N;;;;;
+1AA5;TAI THAM SIGN DOKMAI;Po;0;L;;;;;N;;;;;
+1AA6;TAI THAM SIGN REVERSED ROTATED RANA;Po;0;L;;;;;N;;;;;
+1AA7;TAI THAM SIGN MAI YAMOK;Lm;0;L;;;;;N;;;;;
+1AA8;TAI THAM SIGN KAAN;Po;0;L;;;;;N;;;;;
+1AA9;TAI THAM SIGN KAANKUU;Po;0;L;;;;;N;;;;;
+1AAA;TAI THAM SIGN SATKAAN;Po;0;L;;;;;N;;;;;
+1AAB;TAI THAM SIGN SATKAANKUU;Po;0;L;;;;;N;;;;;
+1AAC;TAI THAM SIGN HANG;Po;0;L;;;;;N;;;;;
+1AAD;TAI THAM SIGN CAANG;Po;0;L;;;;;N;;;;;
+1AB0;COMBINING DOUBLED CIRCUMFLEX ACCENT;Mn;230;NSM;;;;;N;;;;;
+1AB1;COMBINING DIAERESIS-RING;Mn;230;NSM;;;;;N;;;;;
+1AB2;COMBINING INFINITY;Mn;230;NSM;;;;;N;;;;;
+1AB3;COMBINING DOWNWARDS ARROW;Mn;230;NSM;;;;;N;;;;;
+1AB4;COMBINING TRIPLE DOT;Mn;230;NSM;;;;;N;;;;;
+1AB5;COMBINING X-X BELOW;Mn;220;NSM;;;;;N;;;;;
+1AB6;COMBINING WIGGLY LINE BELOW;Mn;220;NSM;;;;;N;;;;;
+1AB7;COMBINING OPEN MARK BELOW;Mn;220;NSM;;;;;N;;;;;
+1AB8;COMBINING DOUBLE OPEN MARK BELOW;Mn;220;NSM;;;;;N;;;;;
+1AB9;COMBINING LIGHT CENTRALIZATION STROKE BELOW;Mn;220;NSM;;;;;N;;;;;
+1ABA;COMBINING STRONG CENTRALIZATION STROKE BELOW;Mn;220;NSM;;;;;N;;;;;
+1ABB;COMBINING PARENTHESES ABOVE;Mn;230;NSM;;;;;N;;;;;
+1ABC;COMBINING DOUBLE PARENTHESES ABOVE;Mn;230;NSM;;;;;N;;;;;
+1ABD;COMBINING PARENTHESES BELOW;Mn;220;NSM;;;;;N;;;;;
+1ABE;COMBINING PARENTHESES OVERLAY;Me;0;NSM;;;;;N;;;;;
+1B00;BALINESE SIGN ULU RICEM;Mn;0;NSM;;;;;N;;;;;
+1B01;BALINESE SIGN ULU CANDRA;Mn;0;NSM;;;;;N;;;;;
+1B02;BALINESE SIGN CECEK;Mn;0;NSM;;;;;N;;;;;
+1B03;BALINESE SIGN SURANG;Mn;0;NSM;;;;;N;;;;;
+1B04;BALINESE SIGN BISAH;Mc;0;L;;;;;N;;;;;
+1B05;BALINESE LETTER AKARA;Lo;0;L;;;;;N;;;;;
+1B06;BALINESE LETTER AKARA TEDUNG;Lo;0;L;1B05 1B35;;;;N;;;;;
+1B07;BALINESE LETTER IKARA;Lo;0;L;;;;;N;;;;;
+1B08;BALINESE LETTER IKARA TEDUNG;Lo;0;L;1B07 1B35;;;;N;;;;;
+1B09;BALINESE LETTER UKARA;Lo;0;L;;;;;N;;;;;
+1B0A;BALINESE LETTER UKARA TEDUNG;Lo;0;L;1B09 1B35;;;;N;;;;;
+1B0B;BALINESE LETTER RA REPA;Lo;0;L;;;;;N;;;;;
+1B0C;BALINESE LETTER RA REPA TEDUNG;Lo;0;L;1B0B 1B35;;;;N;;;;;
+1B0D;BALINESE LETTER LA LENGA;Lo;0;L;;;;;N;;;;;
+1B0E;BALINESE LETTER LA LENGA TEDUNG;Lo;0;L;1B0D 1B35;;;;N;;;;;
+1B0F;BALINESE LETTER EKARA;Lo;0;L;;;;;N;;;;;
+1B10;BALINESE LETTER AIKARA;Lo;0;L;;;;;N;;;;;
+1B11;BALINESE LETTER OKARA;Lo;0;L;;;;;N;;;;;
+1B12;BALINESE LETTER OKARA TEDUNG;Lo;0;L;1B11 1B35;;;;N;;;;;
+1B13;BALINESE LETTER KA;Lo;0;L;;;;;N;;;;;
+1B14;BALINESE LETTER KA MAHAPRANA;Lo;0;L;;;;;N;;;;;
+1B15;BALINESE LETTER GA;Lo;0;L;;;;;N;;;;;
+1B16;BALINESE LETTER GA GORA;Lo;0;L;;;;;N;;;;;
+1B17;BALINESE LETTER NGA;Lo;0;L;;;;;N;;;;;
+1B18;BALINESE LETTER CA;Lo;0;L;;;;;N;;;;;
+1B19;BALINESE LETTER CA LACA;Lo;0;L;;;;;N;;;;;
+1B1A;BALINESE LETTER JA;Lo;0;L;;;;;N;;;;;
+1B1B;BALINESE LETTER JA JERA;Lo;0;L;;;;;N;;;;;
+1B1C;BALINESE LETTER NYA;Lo;0;L;;;;;N;;;;;
+1B1D;BALINESE LETTER TA LATIK;Lo;0;L;;;;;N;;;;;
+1B1E;BALINESE LETTER TA MURDA MAHAPRANA;Lo;0;L;;;;;N;;;;;
+1B1F;BALINESE LETTER DA MURDA ALPAPRANA;Lo;0;L;;;;;N;;;;;
+1B20;BALINESE LETTER DA MURDA MAHAPRANA;Lo;0;L;;;;;N;;;;;
+1B21;BALINESE LETTER NA RAMBAT;Lo;0;L;;;;;N;;;;;
+1B22;BALINESE LETTER TA;Lo;0;L;;;;;N;;;;;
+1B23;BALINESE LETTER TA TAWA;Lo;0;L;;;;;N;;;;;
+1B24;BALINESE LETTER DA;Lo;0;L;;;;;N;;;;;
+1B25;BALINESE LETTER DA MADU;Lo;0;L;;;;;N;;;;;
+1B26;BALINESE LETTER NA;Lo;0;L;;;;;N;;;;;
+1B27;BALINESE LETTER PA;Lo;0;L;;;;;N;;;;;
+1B28;BALINESE LETTER PA KAPAL;Lo;0;L;;;;;N;;;;;
+1B29;BALINESE LETTER BA;Lo;0;L;;;;;N;;;;;
+1B2A;BALINESE LETTER BA KEMBANG;Lo;0;L;;;;;N;;;;;
+1B2B;BALINESE LETTER MA;Lo;0;L;;;;;N;;;;;
+1B2C;BALINESE LETTER YA;Lo;0;L;;;;;N;;;;;
+1B2D;BALINESE LETTER RA;Lo;0;L;;;;;N;;;;;
+1B2E;BALINESE LETTER LA;Lo;0;L;;;;;N;;;;;
+1B2F;BALINESE LETTER WA;Lo;0;L;;;;;N;;;;;
+1B30;BALINESE LETTER SA SAGA;Lo;0;L;;;;;N;;;;;
+1B31;BALINESE LETTER SA SAPA;Lo;0;L;;;;;N;;;;;
+1B32;BALINESE LETTER SA;Lo;0;L;;;;;N;;;;;
+1B33;BALINESE LETTER HA;Lo;0;L;;;;;N;;;;;
+1B34;BALINESE SIGN REREKAN;Mn;7;NSM;;;;;N;;;;;
+1B35;BALINESE VOWEL SIGN TEDUNG;Mc;0;L;;;;;N;;;;;
+1B36;BALINESE VOWEL SIGN ULU;Mn;0;NSM;;;;;N;;;;;
+1B37;BALINESE VOWEL SIGN ULU SARI;Mn;0;NSM;;;;;N;;;;;
+1B38;BALINESE VOWEL SIGN SUKU;Mn;0;NSM;;;;;N;;;;;
+1B39;BALINESE VOWEL SIGN SUKU ILUT;Mn;0;NSM;;;;;N;;;;;
+1B3A;BALINESE VOWEL SIGN RA REPA;Mn;0;NSM;;;;;N;;;;;
+1B3B;BALINESE VOWEL SIGN RA REPA TEDUNG;Mc;0;L;1B3A 1B35;;;;N;;;;;
+1B3C;BALINESE VOWEL SIGN LA LENGA;Mn;0;NSM;;;;;N;;;;;
+1B3D;BALINESE VOWEL SIGN LA LENGA TEDUNG;Mc;0;L;1B3C 1B35;;;;N;;;;;
+1B3E;BALINESE VOWEL SIGN TALING;Mc;0;L;;;;;N;;;;;
+1B3F;BALINESE VOWEL SIGN TALING REPA;Mc;0;L;;;;;N;;;;;
+1B40;BALINESE VOWEL SIGN TALING TEDUNG;Mc;0;L;1B3E 1B35;;;;N;;;;;
+1B41;BALINESE VOWEL SIGN TALING REPA TEDUNG;Mc;0;L;1B3F 1B35;;;;N;;;;;
+1B42;BALINESE VOWEL SIGN PEPET;Mn;0;NSM;;;;;N;;;;;
+1B43;BALINESE VOWEL SIGN PEPET TEDUNG;Mc;0;L;1B42 1B35;;;;N;;;;;
+1B44;BALINESE ADEG ADEG;Mc;9;L;;;;;N;;;;;
+1B45;BALINESE LETTER KAF SASAK;Lo;0;L;;;;;N;;;;;
+1B46;BALINESE LETTER KHOT SASAK;Lo;0;L;;;;;N;;;;;
+1B47;BALINESE LETTER TZIR SASAK;Lo;0;L;;;;;N;;;;;
+1B48;BALINESE LETTER EF SASAK;Lo;0;L;;;;;N;;;;;
+1B49;BALINESE LETTER VE SASAK;Lo;0;L;;;;;N;;;;;
+1B4A;BALINESE LETTER ZAL SASAK;Lo;0;L;;;;;N;;;;;
+1B4B;BALINESE LETTER ASYURA SASAK;Lo;0;L;;;;;N;;;;;
+1B50;BALINESE DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1B51;BALINESE DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1B52;BALINESE DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1B53;BALINESE DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1B54;BALINESE DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1B55;BALINESE DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1B56;BALINESE DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1B57;BALINESE DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1B58;BALINESE DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1B59;BALINESE DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1B5A;BALINESE PANTI;Po;0;L;;;;;N;;;;;
+1B5B;BALINESE PAMADA;Po;0;L;;;;;N;;;;;
+1B5C;BALINESE WINDU;Po;0;L;;;;;N;;;;;
+1B5D;BALINESE CARIK PAMUNGKAH;Po;0;L;;;;;N;;;;;
+1B5E;BALINESE CARIK SIKI;Po;0;L;;;;;N;;;;;
+1B5F;BALINESE CARIK PAREREN;Po;0;L;;;;;N;;;;;
+1B60;BALINESE PAMENENG;Po;0;L;;;;;N;;;;;
+1B61;BALINESE MUSICAL SYMBOL DONG;So;0;L;;;;;N;;;;;
+1B62;BALINESE MUSICAL SYMBOL DENG;So;0;L;;;;;N;;;;;
+1B63;BALINESE MUSICAL SYMBOL DUNG;So;0;L;;;;;N;;;;;
+1B64;BALINESE MUSICAL SYMBOL DANG;So;0;L;;;;;N;;;;;
+1B65;BALINESE MUSICAL SYMBOL DANG SURANG;So;0;L;;;;;N;;;;;
+1B66;BALINESE MUSICAL SYMBOL DING;So;0;L;;;;;N;;;;;
+1B67;BALINESE MUSICAL SYMBOL DAENG;So;0;L;;;;;N;;;;;
+1B68;BALINESE MUSICAL SYMBOL DEUNG;So;0;L;;;;;N;;;;;
+1B69;BALINESE MUSICAL SYMBOL DAING;So;0;L;;;;;N;;;;;
+1B6A;BALINESE MUSICAL SYMBOL DANG GEDE;So;0;L;;;;;N;;;;;
+1B6B;BALINESE MUSICAL SYMBOL COMBINING TEGEH;Mn;230;NSM;;;;;N;;;;;
+1B6C;BALINESE MUSICAL SYMBOL COMBINING ENDEP;Mn;220;NSM;;;;;N;;;;;
+1B6D;BALINESE MUSICAL SYMBOL COMBINING KEMPUL;Mn;230;NSM;;;;;N;;;;;
+1B6E;BALINESE MUSICAL SYMBOL COMBINING KEMPLI;Mn;230;NSM;;;;;N;;;;;
+1B6F;BALINESE MUSICAL SYMBOL COMBINING JEGOGAN;Mn;230;NSM;;;;;N;;;;;
+1B70;BALINESE MUSICAL SYMBOL COMBINING KEMPUL WITH JEGOGAN;Mn;230;NSM;;;;;N;;;;;
+1B71;BALINESE MUSICAL SYMBOL COMBINING KEMPLI WITH JEGOGAN;Mn;230;NSM;;;;;N;;;;;
+1B72;BALINESE MUSICAL SYMBOL COMBINING BENDE;Mn;230;NSM;;;;;N;;;;;
+1B73;BALINESE MUSICAL SYMBOL COMBINING GONG;Mn;230;NSM;;;;;N;;;;;
+1B74;BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN DUG;So;0;L;;;;;N;;;;;
+1B75;BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN DAG;So;0;L;;;;;N;;;;;
+1B76;BALINESE MUSICAL SYMBOL RIGHT-HAND CLOSED TUK;So;0;L;;;;;N;;;;;
+1B77;BALINESE MUSICAL SYMBOL RIGHT-HAND CLOSED TAK;So;0;L;;;;;N;;;;;
+1B78;BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PANG;So;0;L;;;;;N;;;;;
+1B79;BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PUNG;So;0;L;;;;;N;;;;;
+1B7A;BALINESE MUSICAL SYMBOL LEFT-HAND CLOSED PLAK;So;0;L;;;;;N;;;;;
+1B7B;BALINESE MUSICAL SYMBOL LEFT-HAND CLOSED PLUK;So;0;L;;;;;N;;;;;
+1B7C;BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PING;So;0;L;;;;;N;;;;;
+1B80;SUNDANESE SIGN PANYECEK;Mn;0;NSM;;;;;N;;;;;
+1B81;SUNDANESE SIGN PANGLAYAR;Mn;0;NSM;;;;;N;;;;;
+1B82;SUNDANESE SIGN PANGWISAD;Mc;0;L;;;;;N;;;;;
+1B83;SUNDANESE LETTER A;Lo;0;L;;;;;N;;;;;
+1B84;SUNDANESE LETTER I;Lo;0;L;;;;;N;;;;;
+1B85;SUNDANESE LETTER U;Lo;0;L;;;;;N;;;;;
+1B86;SUNDANESE LETTER AE;Lo;0;L;;;;;N;;;;;
+1B87;SUNDANESE LETTER O;Lo;0;L;;;;;N;;;;;
+1B88;SUNDANESE LETTER E;Lo;0;L;;;;;N;;;;;
+1B89;SUNDANESE LETTER EU;Lo;0;L;;;;;N;;;;;
+1B8A;SUNDANESE LETTER KA;Lo;0;L;;;;;N;;;;;
+1B8B;SUNDANESE LETTER QA;Lo;0;L;;;;;N;;;;;
+1B8C;SUNDANESE LETTER GA;Lo;0;L;;;;;N;;;;;
+1B8D;SUNDANESE LETTER NGA;Lo;0;L;;;;;N;;;;;
+1B8E;SUNDANESE LETTER CA;Lo;0;L;;;;;N;;;;;
+1B8F;SUNDANESE LETTER JA;Lo;0;L;;;;;N;;;;;
+1B90;SUNDANESE LETTER ZA;Lo;0;L;;;;;N;;;;;
+1B91;SUNDANESE LETTER NYA;Lo;0;L;;;;;N;;;;;
+1B92;SUNDANESE LETTER TA;Lo;0;L;;;;;N;;;;;
+1B93;SUNDANESE LETTER DA;Lo;0;L;;;;;N;;;;;
+1B94;SUNDANESE LETTER NA;Lo;0;L;;;;;N;;;;;
+1B95;SUNDANESE LETTER PA;Lo;0;L;;;;;N;;;;;
+1B96;SUNDANESE LETTER FA;Lo;0;L;;;;;N;;;;;
+1B97;SUNDANESE LETTER VA;Lo;0;L;;;;;N;;;;;
+1B98;SUNDANESE LETTER BA;Lo;0;L;;;;;N;;;;;
+1B99;SUNDANESE LETTER MA;Lo;0;L;;;;;N;;;;;
+1B9A;SUNDANESE LETTER YA;Lo;0;L;;;;;N;;;;;
+1B9B;SUNDANESE LETTER RA;Lo;0;L;;;;;N;;;;;
+1B9C;SUNDANESE LETTER LA;Lo;0;L;;;;;N;;;;;
+1B9D;SUNDANESE LETTER WA;Lo;0;L;;;;;N;;;;;
+1B9E;SUNDANESE LETTER SA;Lo;0;L;;;;;N;;;;;
+1B9F;SUNDANESE LETTER XA;Lo;0;L;;;;;N;;;;;
+1BA0;SUNDANESE LETTER HA;Lo;0;L;;;;;N;;;;;
+1BA1;SUNDANESE CONSONANT SIGN PAMINGKAL;Mc;0;L;;;;;N;;;;;
+1BA2;SUNDANESE CONSONANT SIGN PANYAKRA;Mn;0;NSM;;;;;N;;;;;
+1BA3;SUNDANESE CONSONANT SIGN PANYIKU;Mn;0;NSM;;;;;N;;;;;
+1BA4;SUNDANESE VOWEL SIGN PANGHULU;Mn;0;NSM;;;;;N;;;;;
+1BA5;SUNDANESE VOWEL SIGN PANYUKU;Mn;0;NSM;;;;;N;;;;;
+1BA6;SUNDANESE VOWEL SIGN PANAELAENG;Mc;0;L;;;;;N;;;;;
+1BA7;SUNDANESE VOWEL SIGN PANOLONG;Mc;0;L;;;;;N;;;;;
+1BA8;SUNDANESE VOWEL SIGN PAMEPET;Mn;0;NSM;;;;;N;;;;;
+1BA9;SUNDANESE VOWEL SIGN PANEULEUNG;Mn;0;NSM;;;;;N;;;;;
+1BAA;SUNDANESE SIGN PAMAAEH;Mc;9;L;;;;;N;;;;;
+1BAB;SUNDANESE SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+1BAC;SUNDANESE CONSONANT SIGN PASANGAN MA;Mn;0;NSM;;;;;N;;;;;
+1BAD;SUNDANESE CONSONANT SIGN PASANGAN WA;Mn;0;NSM;;;;;N;;;;;
+1BAE;SUNDANESE LETTER KHA;Lo;0;L;;;;;N;;;;;
+1BAF;SUNDANESE LETTER SYA;Lo;0;L;;;;;N;;;;;
+1BB0;SUNDANESE DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1BB1;SUNDANESE DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1BB2;SUNDANESE DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1BB3;SUNDANESE DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1BB4;SUNDANESE DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1BB5;SUNDANESE DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1BB6;SUNDANESE DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1BB7;SUNDANESE DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1BB8;SUNDANESE DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1BB9;SUNDANESE DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1BBA;SUNDANESE AVAGRAHA;Lo;0;L;;;;;N;;;;;
+1BBB;SUNDANESE LETTER REU;Lo;0;L;;;;;N;;;;;
+1BBC;SUNDANESE LETTER LEU;Lo;0;L;;;;;N;;;;;
+1BBD;SUNDANESE LETTER BHA;Lo;0;L;;;;;N;;;;;
+1BBE;SUNDANESE LETTER FINAL K;Lo;0;L;;;;;N;;;;;
+1BBF;SUNDANESE LETTER FINAL M;Lo;0;L;;;;;N;;;;;
+1BC0;BATAK LETTER A;Lo;0;L;;;;;N;;;;;
+1BC1;BATAK LETTER SIMALUNGUN A;Lo;0;L;;;;;N;;;;;
+1BC2;BATAK LETTER HA;Lo;0;L;;;;;N;;;;;
+1BC3;BATAK LETTER SIMALUNGUN HA;Lo;0;L;;;;;N;;;;;
+1BC4;BATAK LETTER MANDAILING HA;Lo;0;L;;;;;N;;;;;
+1BC5;BATAK LETTER BA;Lo;0;L;;;;;N;;;;;
+1BC6;BATAK LETTER KARO BA;Lo;0;L;;;;;N;;;;;
+1BC7;BATAK LETTER PA;Lo;0;L;;;;;N;;;;;
+1BC8;BATAK LETTER SIMALUNGUN PA;Lo;0;L;;;;;N;;;;;
+1BC9;BATAK LETTER NA;Lo;0;L;;;;;N;;;;;
+1BCA;BATAK LETTER MANDAILING NA;Lo;0;L;;;;;N;;;;;
+1BCB;BATAK LETTER WA;Lo;0;L;;;;;N;;;;;
+1BCC;BATAK LETTER SIMALUNGUN WA;Lo;0;L;;;;;N;;;;;
+1BCD;BATAK LETTER PAKPAK WA;Lo;0;L;;;;;N;;;;;
+1BCE;BATAK LETTER GA;Lo;0;L;;;;;N;;;;;
+1BCF;BATAK LETTER SIMALUNGUN GA;Lo;0;L;;;;;N;;;;;
+1BD0;BATAK LETTER JA;Lo;0;L;;;;;N;;;;;
+1BD1;BATAK LETTER DA;Lo;0;L;;;;;N;;;;;
+1BD2;BATAK LETTER RA;Lo;0;L;;;;;N;;;;;
+1BD3;BATAK LETTER SIMALUNGUN RA;Lo;0;L;;;;;N;;;;;
+1BD4;BATAK LETTER MA;Lo;0;L;;;;;N;;;;;
+1BD5;BATAK LETTER SIMALUNGUN MA;Lo;0;L;;;;;N;;;;;
+1BD6;BATAK LETTER SOUTHERN TA;Lo;0;L;;;;;N;;;;;
+1BD7;BATAK LETTER NORTHERN TA;Lo;0;L;;;;;N;;;;;
+1BD8;BATAK LETTER SA;Lo;0;L;;;;;N;;;;;
+1BD9;BATAK LETTER SIMALUNGUN SA;Lo;0;L;;;;;N;;;;;
+1BDA;BATAK LETTER MANDAILING SA;Lo;0;L;;;;;N;;;;;
+1BDB;BATAK LETTER YA;Lo;0;L;;;;;N;;;;;
+1BDC;BATAK LETTER SIMALUNGUN YA;Lo;0;L;;;;;N;;;;;
+1BDD;BATAK LETTER NGA;Lo;0;L;;;;;N;;;;;
+1BDE;BATAK LETTER LA;Lo;0;L;;;;;N;;;;;
+1BDF;BATAK LETTER SIMALUNGUN LA;Lo;0;L;;;;;N;;;;;
+1BE0;BATAK LETTER NYA;Lo;0;L;;;;;N;;;;;
+1BE1;BATAK LETTER CA;Lo;0;L;;;;;N;;;;;
+1BE2;BATAK LETTER NDA;Lo;0;L;;;;;N;;;;;
+1BE3;BATAK LETTER MBA;Lo;0;L;;;;;N;;;;;
+1BE4;BATAK LETTER I;Lo;0;L;;;;;N;;;;;
+1BE5;BATAK LETTER U;Lo;0;L;;;;;N;;;;;
+1BE6;BATAK SIGN TOMPI;Mn;7;NSM;;;;;N;;;;;
+1BE7;BATAK VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+1BE8;BATAK VOWEL SIGN PAKPAK E;Mn;0;NSM;;;;;N;;;;;
+1BE9;BATAK VOWEL SIGN EE;Mn;0;NSM;;;;;N;;;;;
+1BEA;BATAK VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+1BEB;BATAK VOWEL SIGN KARO I;Mc;0;L;;;;;N;;;;;
+1BEC;BATAK VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+1BED;BATAK VOWEL SIGN KARO O;Mn;0;NSM;;;;;N;;;;;
+1BEE;BATAK VOWEL SIGN U;Mc;0;L;;;;;N;;;;;
+1BEF;BATAK VOWEL SIGN U FOR SIMALUNGUN SA;Mn;0;NSM;;;;;N;;;;;
+1BF0;BATAK CONSONANT SIGN NG;Mn;0;NSM;;;;;N;;;;;
+1BF1;BATAK CONSONANT SIGN H;Mn;0;NSM;;;;;N;;;;;
+1BF2;BATAK PANGOLAT;Mc;9;L;;;;;N;;;;;
+1BF3;BATAK PANONGONAN;Mc;9;L;;;;;N;;;;;
+1BFC;BATAK SYMBOL BINDU NA METEK;Po;0;L;;;;;N;;;;;
+1BFD;BATAK SYMBOL BINDU PINARBORAS;Po;0;L;;;;;N;;;;;
+1BFE;BATAK SYMBOL BINDU JUDUL;Po;0;L;;;;;N;;;;;
+1BFF;BATAK SYMBOL BINDU PANGOLAT;Po;0;L;;;;;N;;;;;
+1C00;LEPCHA LETTER KA;Lo;0;L;;;;;N;;;;;
+1C01;LEPCHA LETTER KLA;Lo;0;L;;;;;N;;;;;
+1C02;LEPCHA LETTER KHA;Lo;0;L;;;;;N;;;;;
+1C03;LEPCHA LETTER GA;Lo;0;L;;;;;N;;;;;
+1C04;LEPCHA LETTER GLA;Lo;0;L;;;;;N;;;;;
+1C05;LEPCHA LETTER NGA;Lo;0;L;;;;;N;;;;;
+1C06;LEPCHA LETTER CA;Lo;0;L;;;;;N;;;;;
+1C07;LEPCHA LETTER CHA;Lo;0;L;;;;;N;;;;;
+1C08;LEPCHA LETTER JA;Lo;0;L;;;;;N;;;;;
+1C09;LEPCHA LETTER NYA;Lo;0;L;;;;;N;;;;;
+1C0A;LEPCHA LETTER TA;Lo;0;L;;;;;N;;;;;
+1C0B;LEPCHA LETTER THA;Lo;0;L;;;;;N;;;;;
+1C0C;LEPCHA LETTER DA;Lo;0;L;;;;;N;;;;;
+1C0D;LEPCHA LETTER NA;Lo;0;L;;;;;N;;;;;
+1C0E;LEPCHA LETTER PA;Lo;0;L;;;;;N;;;;;
+1C0F;LEPCHA LETTER PLA;Lo;0;L;;;;;N;;;;;
+1C10;LEPCHA LETTER PHA;Lo;0;L;;;;;N;;;;;
+1C11;LEPCHA LETTER FA;Lo;0;L;;;;;N;;;;;
+1C12;LEPCHA LETTER FLA;Lo;0;L;;;;;N;;;;;
+1C13;LEPCHA LETTER BA;Lo;0;L;;;;;N;;;;;
+1C14;LEPCHA LETTER BLA;Lo;0;L;;;;;N;;;;;
+1C15;LEPCHA LETTER MA;Lo;0;L;;;;;N;;;;;
+1C16;LEPCHA LETTER MLA;Lo;0;L;;;;;N;;;;;
+1C17;LEPCHA LETTER TSA;Lo;0;L;;;;;N;;;;;
+1C18;LEPCHA LETTER TSHA;Lo;0;L;;;;;N;;;;;
+1C19;LEPCHA LETTER DZA;Lo;0;L;;;;;N;;;;;
+1C1A;LEPCHA LETTER YA;Lo;0;L;;;;;N;;;;;
+1C1B;LEPCHA LETTER RA;Lo;0;L;;;;;N;;;;;
+1C1C;LEPCHA LETTER LA;Lo;0;L;;;;;N;;;;;
+1C1D;LEPCHA LETTER HA;Lo;0;L;;;;;N;;;;;
+1C1E;LEPCHA LETTER HLA;Lo;0;L;;;;;N;;;;;
+1C1F;LEPCHA LETTER VA;Lo;0;L;;;;;N;;;;;
+1C20;LEPCHA LETTER SA;Lo;0;L;;;;;N;;;;;
+1C21;LEPCHA LETTER SHA;Lo;0;L;;;;;N;;;;;
+1C22;LEPCHA LETTER WA;Lo;0;L;;;;;N;;;;;
+1C23;LEPCHA LETTER A;Lo;0;L;;;;;N;;;;;
+1C24;LEPCHA SUBJOINED LETTER YA;Mc;0;L;;;;;N;;;;;
+1C25;LEPCHA SUBJOINED LETTER RA;Mc;0;L;;;;;N;;;;;
+1C26;LEPCHA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+1C27;LEPCHA VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+1C28;LEPCHA VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+1C29;LEPCHA VOWEL SIGN OO;Mc;0;L;;;;;N;;;;;
+1C2A;LEPCHA VOWEL SIGN U;Mc;0;L;;;;;N;;;;;
+1C2B;LEPCHA VOWEL SIGN UU;Mc;0;L;;;;;N;;;;;
+1C2C;LEPCHA VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+1C2D;LEPCHA CONSONANT SIGN K;Mn;0;NSM;;;;;N;;;;;
+1C2E;LEPCHA CONSONANT SIGN M;Mn;0;NSM;;;;;N;;;;;
+1C2F;LEPCHA CONSONANT SIGN L;Mn;0;NSM;;;;;N;;;;;
+1C30;LEPCHA CONSONANT SIGN N;Mn;0;NSM;;;;;N;;;;;
+1C31;LEPCHA CONSONANT SIGN P;Mn;0;NSM;;;;;N;;;;;
+1C32;LEPCHA CONSONANT SIGN R;Mn;0;NSM;;;;;N;;;;;
+1C33;LEPCHA CONSONANT SIGN T;Mn;0;NSM;;;;;N;;;;;
+1C34;LEPCHA CONSONANT SIGN NYIN-DO;Mc;0;L;;;;;N;;;;;
+1C35;LEPCHA CONSONANT SIGN KANG;Mc;0;L;;;;;N;;;;;
+1C36;LEPCHA SIGN RAN;Mn;0;NSM;;;;;N;;;;;
+1C37;LEPCHA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+1C3B;LEPCHA PUNCTUATION TA-ROL;Po;0;L;;;;;N;;;;;
+1C3C;LEPCHA PUNCTUATION NYET THYOOM TA-ROL;Po;0;L;;;;;N;;;;;
+1C3D;LEPCHA PUNCTUATION CER-WA;Po;0;L;;;;;N;;;;;
+1C3E;LEPCHA PUNCTUATION TSHOOK CER-WA;Po;0;L;;;;;N;;;;;
+1C3F;LEPCHA PUNCTUATION TSHOOK;Po;0;L;;;;;N;;;;;
+1C40;LEPCHA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1C41;LEPCHA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1C42;LEPCHA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1C43;LEPCHA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1C44;LEPCHA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1C45;LEPCHA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1C46;LEPCHA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1C47;LEPCHA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1C48;LEPCHA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1C49;LEPCHA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1C4D;LEPCHA LETTER TTA;Lo;0;L;;;;;N;;;;;
+1C4E;LEPCHA LETTER TTHA;Lo;0;L;;;;;N;;;;;
+1C4F;LEPCHA LETTER DDA;Lo;0;L;;;;;N;;;;;
+1C50;OL CHIKI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1C51;OL CHIKI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1C52;OL CHIKI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1C53;OL CHIKI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1C54;OL CHIKI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1C55;OL CHIKI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1C56;OL CHIKI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1C57;OL CHIKI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1C58;OL CHIKI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1C59;OL CHIKI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1C5A;OL CHIKI LETTER LA;Lo;0;L;;;;;N;;;;;
+1C5B;OL CHIKI LETTER AT;Lo;0;L;;;;;N;;;;;
+1C5C;OL CHIKI LETTER AG;Lo;0;L;;;;;N;;;;;
+1C5D;OL CHIKI LETTER ANG;Lo;0;L;;;;;N;;;;;
+1C5E;OL CHIKI LETTER AL;Lo;0;L;;;;;N;;;;;
+1C5F;OL CHIKI LETTER LAA;Lo;0;L;;;;;N;;;;;
+1C60;OL CHIKI LETTER AAK;Lo;0;L;;;;;N;;;;;
+1C61;OL CHIKI LETTER AAJ;Lo;0;L;;;;;N;;;;;
+1C62;OL CHIKI LETTER AAM;Lo;0;L;;;;;N;;;;;
+1C63;OL CHIKI LETTER AAW;Lo;0;L;;;;;N;;;;;
+1C64;OL CHIKI LETTER LI;Lo;0;L;;;;;N;;;;;
+1C65;OL CHIKI LETTER IS;Lo;0;L;;;;;N;;;;;
+1C66;OL CHIKI LETTER IH;Lo;0;L;;;;;N;;;;;
+1C67;OL CHIKI LETTER INY;Lo;0;L;;;;;N;;;;;
+1C68;OL CHIKI LETTER IR;Lo;0;L;;;;;N;;;;;
+1C69;OL CHIKI LETTER LU;Lo;0;L;;;;;N;;;;;
+1C6A;OL CHIKI LETTER UC;Lo;0;L;;;;;N;;;;;
+1C6B;OL CHIKI LETTER UD;Lo;0;L;;;;;N;;;;;
+1C6C;OL CHIKI LETTER UNN;Lo;0;L;;;;;N;;;;;
+1C6D;OL CHIKI LETTER UY;Lo;0;L;;;;;N;;;;;
+1C6E;OL CHIKI LETTER LE;Lo;0;L;;;;;N;;;;;
+1C6F;OL CHIKI LETTER EP;Lo;0;L;;;;;N;;;;;
+1C70;OL CHIKI LETTER EDD;Lo;0;L;;;;;N;;;;;
+1C71;OL CHIKI LETTER EN;Lo;0;L;;;;;N;;;;;
+1C72;OL CHIKI LETTER ERR;Lo;0;L;;;;;N;;;;;
+1C73;OL CHIKI LETTER LO;Lo;0;L;;;;;N;;;;;
+1C74;OL CHIKI LETTER OTT;Lo;0;L;;;;;N;;;;;
+1C75;OL CHIKI LETTER OB;Lo;0;L;;;;;N;;;;;
+1C76;OL CHIKI LETTER OV;Lo;0;L;;;;;N;;;;;
+1C77;OL CHIKI LETTER OH;Lo;0;L;;;;;N;;;;;
+1C78;OL CHIKI MU TTUDDAG;Lm;0;L;;;;;N;;;;;
+1C79;OL CHIKI GAAHLAA TTUDDAAG;Lm;0;L;;;;;N;;;;;
+1C7A;OL CHIKI MU-GAAHLAA TTUDDAAG;Lm;0;L;;;;;N;;;;;
+1C7B;OL CHIKI RELAA;Lm;0;L;;;;;N;;;;;
+1C7C;OL CHIKI PHAARKAA;Lm;0;L;;;;;N;;;;;
+1C7D;OL CHIKI AHAD;Lm;0;L;;;;;N;;;;;
+1C7E;OL CHIKI PUNCTUATION MUCAAD;Po;0;L;;;;;N;;;;;
+1C7F;OL CHIKI PUNCTUATION DOUBLE MUCAAD;Po;0;L;;;;;N;;;;;
+1C80;CYRILLIC SMALL LETTER ROUNDED VE;Ll;0;L;;;;;N;;;0412;;0412
+1C81;CYRILLIC SMALL LETTER LONG-LEGGED DE;Ll;0;L;;;;;N;;;0414;;0414
+1C82;CYRILLIC SMALL LETTER NARROW O;Ll;0;L;;;;;N;;;041E;;041E
+1C83;CYRILLIC SMALL LETTER WIDE ES;Ll;0;L;;;;;N;;;0421;;0421
+1C84;CYRILLIC SMALL LETTER TALL TE;Ll;0;L;;;;;N;;;0422;;0422
+1C85;CYRILLIC SMALL LETTER THREE-LEGGED TE;Ll;0;L;;;;;N;;;0422;;0422
+1C86;CYRILLIC SMALL LETTER TALL HARD SIGN;Ll;0;L;;;;;N;;;042A;;042A
+1C87;CYRILLIC SMALL LETTER TALL YAT;Ll;0;L;;;;;N;;;0462;;0462
+1C88;CYRILLIC SMALL LETTER UNBLENDED UK;Ll;0;L;;;;;N;;;A64A;;A64A
+1C90;GEORGIAN MTAVRULI CAPITAL LETTER AN;Lu;0;L;;;;;N;;;;10D0;
+1C91;GEORGIAN MTAVRULI CAPITAL LETTER BAN;Lu;0;L;;;;;N;;;;10D1;
+1C92;GEORGIAN MTAVRULI CAPITAL LETTER GAN;Lu;0;L;;;;;N;;;;10D2;
+1C93;GEORGIAN MTAVRULI CAPITAL LETTER DON;Lu;0;L;;;;;N;;;;10D3;
+1C94;GEORGIAN MTAVRULI CAPITAL LETTER EN;Lu;0;L;;;;;N;;;;10D4;
+1C95;GEORGIAN MTAVRULI CAPITAL LETTER VIN;Lu;0;L;;;;;N;;;;10D5;
+1C96;GEORGIAN MTAVRULI CAPITAL LETTER ZEN;Lu;0;L;;;;;N;;;;10D6;
+1C97;GEORGIAN MTAVRULI CAPITAL LETTER TAN;Lu;0;L;;;;;N;;;;10D7;
+1C98;GEORGIAN MTAVRULI CAPITAL LETTER IN;Lu;0;L;;;;;N;;;;10D8;
+1C99;GEORGIAN MTAVRULI CAPITAL LETTER KAN;Lu;0;L;;;;;N;;;;10D9;
+1C9A;GEORGIAN MTAVRULI CAPITAL LETTER LAS;Lu;0;L;;;;;N;;;;10DA;
+1C9B;GEORGIAN MTAVRULI CAPITAL LETTER MAN;Lu;0;L;;;;;N;;;;10DB;
+1C9C;GEORGIAN MTAVRULI CAPITAL LETTER NAR;Lu;0;L;;;;;N;;;;10DC;
+1C9D;GEORGIAN MTAVRULI CAPITAL LETTER ON;Lu;0;L;;;;;N;;;;10DD;
+1C9E;GEORGIAN MTAVRULI CAPITAL LETTER PAR;Lu;0;L;;;;;N;;;;10DE;
+1C9F;GEORGIAN MTAVRULI CAPITAL LETTER ZHAR;Lu;0;L;;;;;N;;;;10DF;
+1CA0;GEORGIAN MTAVRULI CAPITAL LETTER RAE;Lu;0;L;;;;;N;;;;10E0;
+1CA1;GEORGIAN MTAVRULI CAPITAL LETTER SAN;Lu;0;L;;;;;N;;;;10E1;
+1CA2;GEORGIAN MTAVRULI CAPITAL LETTER TAR;Lu;0;L;;;;;N;;;;10E2;
+1CA3;GEORGIAN MTAVRULI CAPITAL LETTER UN;Lu;0;L;;;;;N;;;;10E3;
+1CA4;GEORGIAN MTAVRULI CAPITAL LETTER PHAR;Lu;0;L;;;;;N;;;;10E4;
+1CA5;GEORGIAN MTAVRULI CAPITAL LETTER KHAR;Lu;0;L;;;;;N;;;;10E5;
+1CA6;GEORGIAN MTAVRULI CAPITAL LETTER GHAN;Lu;0;L;;;;;N;;;;10E6;
+1CA7;GEORGIAN MTAVRULI CAPITAL LETTER QAR;Lu;0;L;;;;;N;;;;10E7;
+1CA8;GEORGIAN MTAVRULI CAPITAL LETTER SHIN;Lu;0;L;;;;;N;;;;10E8;
+1CA9;GEORGIAN MTAVRULI CAPITAL LETTER CHIN;Lu;0;L;;;;;N;;;;10E9;
+1CAA;GEORGIAN MTAVRULI CAPITAL LETTER CAN;Lu;0;L;;;;;N;;;;10EA;
+1CAB;GEORGIAN MTAVRULI CAPITAL LETTER JIL;Lu;0;L;;;;;N;;;;10EB;
+1CAC;GEORGIAN MTAVRULI CAPITAL LETTER CIL;Lu;0;L;;;;;N;;;;10EC;
+1CAD;GEORGIAN MTAVRULI CAPITAL LETTER CHAR;Lu;0;L;;;;;N;;;;10ED;
+1CAE;GEORGIAN MTAVRULI CAPITAL LETTER XAN;Lu;0;L;;;;;N;;;;10EE;
+1CAF;GEORGIAN MTAVRULI CAPITAL LETTER JHAN;Lu;0;L;;;;;N;;;;10EF;
+1CB0;GEORGIAN MTAVRULI CAPITAL LETTER HAE;Lu;0;L;;;;;N;;;;10F0;
+1CB1;GEORGIAN MTAVRULI CAPITAL LETTER HE;Lu;0;L;;;;;N;;;;10F1;
+1CB2;GEORGIAN MTAVRULI CAPITAL LETTER HIE;Lu;0;L;;;;;N;;;;10F2;
+1CB3;GEORGIAN MTAVRULI CAPITAL LETTER WE;Lu;0;L;;;;;N;;;;10F3;
+1CB4;GEORGIAN MTAVRULI CAPITAL LETTER HAR;Lu;0;L;;;;;N;;;;10F4;
+1CB5;GEORGIAN MTAVRULI CAPITAL LETTER HOE;Lu;0;L;;;;;N;;;;10F5;
+1CB6;GEORGIAN MTAVRULI CAPITAL LETTER FI;Lu;0;L;;;;;N;;;;10F6;
+1CB7;GEORGIAN MTAVRULI CAPITAL LETTER YN;Lu;0;L;;;;;N;;;;10F7;
+1CB8;GEORGIAN MTAVRULI CAPITAL LETTER ELIFI;Lu;0;L;;;;;N;;;;10F8;
+1CB9;GEORGIAN MTAVRULI CAPITAL LETTER TURNED GAN;Lu;0;L;;;;;N;;;;10F9;
+1CBA;GEORGIAN MTAVRULI CAPITAL LETTER AIN;Lu;0;L;;;;;N;;;;10FA;
+1CBD;GEORGIAN MTAVRULI CAPITAL LETTER AEN;Lu;0;L;;;;;N;;;;10FD;
+1CBE;GEORGIAN MTAVRULI CAPITAL LETTER HARD SIGN;Lu;0;L;;;;;N;;;;10FE;
+1CBF;GEORGIAN MTAVRULI CAPITAL LETTER LABIAL SIGN;Lu;0;L;;;;;N;;;;10FF;
+1CC0;SUNDANESE PUNCTUATION BINDU SURYA;Po;0;L;;;;;N;;;;;
+1CC1;SUNDANESE PUNCTUATION BINDU PANGLONG;Po;0;L;;;;;N;;;;;
+1CC2;SUNDANESE PUNCTUATION BINDU PURNAMA;Po;0;L;;;;;N;;;;;
+1CC3;SUNDANESE PUNCTUATION BINDU CAKRA;Po;0;L;;;;;N;;;;;
+1CC4;SUNDANESE PUNCTUATION BINDU LEU SATANGA;Po;0;L;;;;;N;;;;;
+1CC5;SUNDANESE PUNCTUATION BINDU KA SATANGA;Po;0;L;;;;;N;;;;;
+1CC6;SUNDANESE PUNCTUATION BINDU DA SATANGA;Po;0;L;;;;;N;;;;;
+1CC7;SUNDANESE PUNCTUATION BINDU BA SATANGA;Po;0;L;;;;;N;;;;;
+1CD0;VEDIC TONE KARSHANA;Mn;230;NSM;;;;;N;;;;;
+1CD1;VEDIC TONE SHARA;Mn;230;NSM;;;;;N;;;;;
+1CD2;VEDIC TONE PRENKHA;Mn;230;NSM;;;;;N;;;;;
+1CD3;VEDIC SIGN NIHSHVASA;Po;0;L;;;;;N;;;;;
+1CD4;VEDIC SIGN YAJURVEDIC MIDLINE SVARITA;Mn;1;NSM;;;;;N;;;;;
+1CD5;VEDIC TONE YAJURVEDIC AGGRAVATED INDEPENDENT SVARITA;Mn;220;NSM;;;;;N;;;;;
+1CD6;VEDIC TONE YAJURVEDIC INDEPENDENT SVARITA;Mn;220;NSM;;;;;N;;;;;
+1CD7;VEDIC TONE YAJURVEDIC KATHAKA INDEPENDENT SVARITA;Mn;220;NSM;;;;;N;;;;;
+1CD8;VEDIC TONE CANDRA BELOW;Mn;220;NSM;;;;;N;;;;;
+1CD9;VEDIC TONE YAJURVEDIC KATHAKA INDEPENDENT SVARITA SCHROEDER;Mn;220;NSM;;;;;N;;;;;
+1CDA;VEDIC TONE DOUBLE SVARITA;Mn;230;NSM;;;;;N;;;;;
+1CDB;VEDIC TONE TRIPLE SVARITA;Mn;230;NSM;;;;;N;;;;;
+1CDC;VEDIC TONE KATHAKA ANUDATTA;Mn;220;NSM;;;;;N;;;;;
+1CDD;VEDIC TONE DOT BELOW;Mn;220;NSM;;;;;N;;;;;
+1CDE;VEDIC TONE TWO DOTS BELOW;Mn;220;NSM;;;;;N;;;;;
+1CDF;VEDIC TONE THREE DOTS BELOW;Mn;220;NSM;;;;;N;;;;;
+1CE0;VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA;Mn;230;NSM;;;;;N;;;;;
+1CE1;VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA;Mc;0;L;;;;;N;;;;;
+1CE2;VEDIC SIGN VISARGA SVARITA;Mn;1;NSM;;;;;N;;;;;
+1CE3;VEDIC SIGN VISARGA UDATTA;Mn;1;NSM;;;;;N;;;;;
+1CE4;VEDIC SIGN REVERSED VISARGA UDATTA;Mn;1;NSM;;;;;N;;;;;
+1CE5;VEDIC SIGN VISARGA ANUDATTA;Mn;1;NSM;;;;;N;;;;;
+1CE6;VEDIC SIGN REVERSED VISARGA ANUDATTA;Mn;1;NSM;;;;;N;;;;;
+1CE7;VEDIC SIGN VISARGA UDATTA WITH TAIL;Mn;1;NSM;;;;;N;;;;;
+1CE8;VEDIC SIGN VISARGA ANUDATTA WITH TAIL;Mn;1;NSM;;;;;N;;;;;
+1CE9;VEDIC SIGN ANUSVARA ANTARGOMUKHA;Lo;0;L;;;;;N;;;;;
+1CEA;VEDIC SIGN ANUSVARA BAHIRGOMUKHA;Lo;0;L;;;;;N;;;;;
+1CEB;VEDIC SIGN ANUSVARA VAMAGOMUKHA;Lo;0;L;;;;;N;;;;;
+1CEC;VEDIC SIGN ANUSVARA VAMAGOMUKHA WITH TAIL;Lo;0;L;;;;;N;;;;;
+1CED;VEDIC SIGN TIRYAK;Mn;220;NSM;;;;;N;;;;;
+1CEE;VEDIC SIGN HEXIFORM LONG ANUSVARA;Lo;0;L;;;;;N;;;;;
+1CEF;VEDIC SIGN LONG ANUSVARA;Lo;0;L;;;;;N;;;;;
+1CF0;VEDIC SIGN RTHANG LONG ANUSVARA;Lo;0;L;;;;;N;;;;;
+1CF1;VEDIC SIGN ANUSVARA UBHAYATO MUKHA;Lo;0;L;;;;;N;;;;;
+1CF2;VEDIC SIGN ARDHAVISARGA;Lo;0;L;;;;;N;;;;;
+1CF3;VEDIC SIGN ROTATED ARDHAVISARGA;Lo;0;L;;;;;N;;;;;
+1CF4;VEDIC TONE CANDRA ABOVE;Mn;230;NSM;;;;;N;;;;;
+1CF5;VEDIC SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;;
+1CF6;VEDIC SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;;
+1CF7;VEDIC SIGN ATIKRAMA;Mc;0;L;;;;;N;;;;;
+1CF8;VEDIC TONE RING ABOVE;Mn;230;NSM;;;;;N;;;;;
+1CF9;VEDIC TONE DOUBLE RING ABOVE;Mn;230;NSM;;;;;N;;;;;
+1CFA;VEDIC SIGN DOUBLE ANUSVARA ANTARGOMUKHA;Lo;0;L;;;;;N;;;;;
+1D00;LATIN LETTER SMALL CAPITAL A;Ll;0;L;;;;;N;;;;;
+1D01;LATIN LETTER SMALL CAPITAL AE;Ll;0;L;;;;;N;;;;;
+1D02;LATIN SMALL LETTER TURNED AE;Ll;0;L;;;;;N;;;;;
+1D03;LATIN LETTER SMALL CAPITAL BARRED B;Ll;0;L;;;;;N;;;;;
+1D04;LATIN LETTER SMALL CAPITAL C;Ll;0;L;;;;;N;;;;;
+1D05;LATIN LETTER SMALL CAPITAL D;Ll;0;L;;;;;N;;;;;
+1D06;LATIN LETTER SMALL CAPITAL ETH;Ll;0;L;;;;;N;;;;;
+1D07;LATIN LETTER SMALL CAPITAL E;Ll;0;L;;;;;N;;;;;
+1D08;LATIN SMALL LETTER TURNED OPEN E;Ll;0;L;;;;;N;;;;;
+1D09;LATIN SMALL LETTER TURNED I;Ll;0;L;;;;;N;;;;;
+1D0A;LATIN LETTER SMALL CAPITAL J;Ll;0;L;;;;;N;;;;;
+1D0B;LATIN LETTER SMALL CAPITAL K;Ll;0;L;;;;;N;;;;;
+1D0C;LATIN LETTER SMALL CAPITAL L WITH STROKE;Ll;0;L;;;;;N;;;;;
+1D0D;LATIN LETTER SMALL CAPITAL M;Ll;0;L;;;;;N;;;;;
+1D0E;LATIN LETTER SMALL CAPITAL REVERSED N;Ll;0;L;;;;;N;;;;;
+1D0F;LATIN LETTER SMALL CAPITAL O;Ll;0;L;;;;;N;;;;;
+1D10;LATIN LETTER SMALL CAPITAL OPEN O;Ll;0;L;;;;;N;;;;;
+1D11;LATIN SMALL LETTER SIDEWAYS O;Ll;0;L;;;;;N;;;;;
+1D12;LATIN SMALL LETTER SIDEWAYS OPEN O;Ll;0;L;;;;;N;;;;;
+1D13;LATIN SMALL LETTER SIDEWAYS O WITH STROKE;Ll;0;L;;;;;N;;;;;
+1D14;LATIN SMALL LETTER TURNED OE;Ll;0;L;;;;;N;;;;;
+1D15;LATIN LETTER SMALL CAPITAL OU;Ll;0;L;;;;;N;;;;;
+1D16;LATIN SMALL LETTER TOP HALF O;Ll;0;L;;;;;N;;;;;
+1D17;LATIN SMALL LETTER BOTTOM HALF O;Ll;0;L;;;;;N;;;;;
+1D18;LATIN LETTER SMALL CAPITAL P;Ll;0;L;;;;;N;;;;;
+1D19;LATIN LETTER SMALL CAPITAL REVERSED R;Ll;0;L;;;;;N;;;;;
+1D1A;LATIN LETTER SMALL CAPITAL TURNED R;Ll;0;L;;;;;N;;;;;
+1D1B;LATIN LETTER SMALL CAPITAL T;Ll;0;L;;;;;N;;;;;
+1D1C;LATIN LETTER SMALL CAPITAL U;Ll;0;L;;;;;N;;;;;
+1D1D;LATIN SMALL LETTER SIDEWAYS U;Ll;0;L;;;;;N;;;;;
+1D1E;LATIN SMALL LETTER SIDEWAYS DIAERESIZED U;Ll;0;L;;;;;N;;;;;
+1D1F;LATIN SMALL LETTER SIDEWAYS TURNED M;Ll;0;L;;;;;N;;;;;
+1D20;LATIN LETTER SMALL CAPITAL V;Ll;0;L;;;;;N;;;;;
+1D21;LATIN LETTER SMALL CAPITAL W;Ll;0;L;;;;;N;;;;;
+1D22;LATIN LETTER SMALL CAPITAL Z;Ll;0;L;;;;;N;;;;;
+1D23;LATIN LETTER SMALL CAPITAL EZH;Ll;0;L;;;;;N;;;;;
+1D24;LATIN LETTER VOICED LARYNGEAL SPIRANT;Ll;0;L;;;;;N;;;;;
+1D25;LATIN LETTER AIN;Ll;0;L;;;;;N;;;;;
+1D26;GREEK LETTER SMALL CAPITAL GAMMA;Ll;0;L;;;;;N;;;;;
+1D27;GREEK LETTER SMALL CAPITAL LAMDA;Ll;0;L;;;;;N;;;;;
+1D28;GREEK LETTER SMALL CAPITAL PI;Ll;0;L;;;;;N;;;;;
+1D29;GREEK LETTER SMALL CAPITAL RHO;Ll;0;L;;;;;N;;;;;
+1D2A;GREEK LETTER SMALL CAPITAL PSI;Ll;0;L;;;;;N;;;;;
+1D2B;CYRILLIC LETTER SMALL CAPITAL EL;Ll;0;L;;;;;N;;;;;
+1D2C;MODIFIER LETTER CAPITAL A;Lm;0;L;<super> 0041;;;;N;;;;;
+1D2D;MODIFIER LETTER CAPITAL AE;Lm;0;L;<super> 00C6;;;;N;;;;;
+1D2E;MODIFIER LETTER CAPITAL B;Lm;0;L;<super> 0042;;;;N;;;;;
+1D2F;MODIFIER LETTER CAPITAL BARRED B;Lm;0;L;;;;;N;;;;;
+1D30;MODIFIER LETTER CAPITAL D;Lm;0;L;<super> 0044;;;;N;;;;;
+1D31;MODIFIER LETTER CAPITAL E;Lm;0;L;<super> 0045;;;;N;;;;;
+1D32;MODIFIER LETTER CAPITAL REVERSED E;Lm;0;L;<super> 018E;;;;N;;;;;
+1D33;MODIFIER LETTER CAPITAL G;Lm;0;L;<super> 0047;;;;N;;;;;
+1D34;MODIFIER LETTER CAPITAL H;Lm;0;L;<super> 0048;;;;N;;;;;
+1D35;MODIFIER LETTER CAPITAL I;Lm;0;L;<super> 0049;;;;N;;;;;
+1D36;MODIFIER LETTER CAPITAL J;Lm;0;L;<super> 004A;;;;N;;;;;
+1D37;MODIFIER LETTER CAPITAL K;Lm;0;L;<super> 004B;;;;N;;;;;
+1D38;MODIFIER LETTER CAPITAL L;Lm;0;L;<super> 004C;;;;N;;;;;
+1D39;MODIFIER LETTER CAPITAL M;Lm;0;L;<super> 004D;;;;N;;;;;
+1D3A;MODIFIER LETTER CAPITAL N;Lm;0;L;<super> 004E;;;;N;;;;;
+1D3B;MODIFIER LETTER CAPITAL REVERSED N;Lm;0;L;;;;;N;;;;;
+1D3C;MODIFIER LETTER CAPITAL O;Lm;0;L;<super> 004F;;;;N;;;;;
+1D3D;MODIFIER LETTER CAPITAL OU;Lm;0;L;<super> 0222;;;;N;;;;;
+1D3E;MODIFIER LETTER CAPITAL P;Lm;0;L;<super> 0050;;;;N;;;;;
+1D3F;MODIFIER LETTER CAPITAL R;Lm;0;L;<super> 0052;;;;N;;;;;
+1D40;MODIFIER LETTER CAPITAL T;Lm;0;L;<super> 0054;;;;N;;;;;
+1D41;MODIFIER LETTER CAPITAL U;Lm;0;L;<super> 0055;;;;N;;;;;
+1D42;MODIFIER LETTER CAPITAL W;Lm;0;L;<super> 0057;;;;N;;;;;
+1D43;MODIFIER LETTER SMALL A;Lm;0;L;<super> 0061;;;;N;;;;;
+1D44;MODIFIER LETTER SMALL TURNED A;Lm;0;L;<super> 0250;;;;N;;;;;
+1D45;MODIFIER LETTER SMALL ALPHA;Lm;0;L;<super> 0251;;;;N;;;;;
+1D46;MODIFIER LETTER SMALL TURNED AE;Lm;0;L;<super> 1D02;;;;N;;;;;
+1D47;MODIFIER LETTER SMALL B;Lm;0;L;<super> 0062;;;;N;;;;;
+1D48;MODIFIER LETTER SMALL D;Lm;0;L;<super> 0064;;;;N;;;;;
+1D49;MODIFIER LETTER SMALL E;Lm;0;L;<super> 0065;;;;N;;;;;
+1D4A;MODIFIER LETTER SMALL SCHWA;Lm;0;L;<super> 0259;;;;N;;;;;
+1D4B;MODIFIER LETTER SMALL OPEN E;Lm;0;L;<super> 025B;;;;N;;;;;
+1D4C;MODIFIER LETTER SMALL TURNED OPEN E;Lm;0;L;<super> 025C;;;;N;;;;;
+1D4D;MODIFIER LETTER SMALL G;Lm;0;L;<super> 0067;;;;N;;;;;
+1D4E;MODIFIER LETTER SMALL TURNED I;Lm;0;L;;;;;N;;;;;
+1D4F;MODIFIER LETTER SMALL K;Lm;0;L;<super> 006B;;;;N;;;;;
+1D50;MODIFIER LETTER SMALL M;Lm;0;L;<super> 006D;;;;N;;;;;
+1D51;MODIFIER LETTER SMALL ENG;Lm;0;L;<super> 014B;;;;N;;;;;
+1D52;MODIFIER LETTER SMALL O;Lm;0;L;<super> 006F;;;;N;;;;;
+1D53;MODIFIER LETTER SMALL OPEN O;Lm;0;L;<super> 0254;;;;N;;;;;
+1D54;MODIFIER LETTER SMALL TOP HALF O;Lm;0;L;<super> 1D16;;;;N;;;;;
+1D55;MODIFIER LETTER SMALL BOTTOM HALF O;Lm;0;L;<super> 1D17;;;;N;;;;;
+1D56;MODIFIER LETTER SMALL P;Lm;0;L;<super> 0070;;;;N;;;;;
+1D57;MODIFIER LETTER SMALL T;Lm;0;L;<super> 0074;;;;N;;;;;
+1D58;MODIFIER LETTER SMALL U;Lm;0;L;<super> 0075;;;;N;;;;;
+1D59;MODIFIER LETTER SMALL SIDEWAYS U;Lm;0;L;<super> 1D1D;;;;N;;;;;
+1D5A;MODIFIER LETTER SMALL TURNED M;Lm;0;L;<super> 026F;;;;N;;;;;
+1D5B;MODIFIER LETTER SMALL V;Lm;0;L;<super> 0076;;;;N;;;;;
+1D5C;MODIFIER LETTER SMALL AIN;Lm;0;L;<super> 1D25;;;;N;;;;;
+1D5D;MODIFIER LETTER SMALL BETA;Lm;0;L;<super> 03B2;;;;N;;;;;
+1D5E;MODIFIER LETTER SMALL GREEK GAMMA;Lm;0;L;<super> 03B3;;;;N;;;;;
+1D5F;MODIFIER LETTER SMALL DELTA;Lm;0;L;<super> 03B4;;;;N;;;;;
+1D60;MODIFIER LETTER SMALL GREEK PHI;Lm;0;L;<super> 03C6;;;;N;;;;;
+1D61;MODIFIER LETTER SMALL CHI;Lm;0;L;<super> 03C7;;;;N;;;;;
+1D62;LATIN SUBSCRIPT SMALL LETTER I;Lm;0;L;<sub> 0069;;;;N;;;;;
+1D63;LATIN SUBSCRIPT SMALL LETTER R;Lm;0;L;<sub> 0072;;;;N;;;;;
+1D64;LATIN SUBSCRIPT SMALL LETTER U;Lm;0;L;<sub> 0075;;;;N;;;;;
+1D65;LATIN SUBSCRIPT SMALL LETTER V;Lm;0;L;<sub> 0076;;;;N;;;;;
+1D66;GREEK SUBSCRIPT SMALL LETTER BETA;Lm;0;L;<sub> 03B2;;;;N;;;;;
+1D67;GREEK SUBSCRIPT SMALL LETTER GAMMA;Lm;0;L;<sub> 03B3;;;;N;;;;;
+1D68;GREEK SUBSCRIPT SMALL LETTER RHO;Lm;0;L;<sub> 03C1;;;;N;;;;;
+1D69;GREEK SUBSCRIPT SMALL LETTER PHI;Lm;0;L;<sub> 03C6;;;;N;;;;;
+1D6A;GREEK SUBSCRIPT SMALL LETTER CHI;Lm;0;L;<sub> 03C7;;;;N;;;;;
+1D6B;LATIN SMALL LETTER UE;Ll;0;L;;;;;N;;;;;
+1D6C;LATIN SMALL LETTER B WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;;
+1D6D;LATIN SMALL LETTER D WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;;
+1D6E;LATIN SMALL LETTER F WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;;
+1D6F;LATIN SMALL LETTER M WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;;
+1D70;LATIN SMALL LETTER N WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;;
+1D71;LATIN SMALL LETTER P WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;;
+1D72;LATIN SMALL LETTER R WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;;
+1D73;LATIN SMALL LETTER R WITH FISHHOOK AND MIDDLE TILDE;Ll;0;L;;;;;N;;;;;
+1D74;LATIN SMALL LETTER S WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;;
+1D75;LATIN SMALL LETTER T WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;;
+1D76;LATIN SMALL LETTER Z WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;;
+1D77;LATIN SMALL LETTER TURNED G;Ll;0;L;;;;;N;;;;;
+1D78;MODIFIER LETTER CYRILLIC EN;Lm;0;L;<super> 043D;;;;N;;;;;
+1D79;LATIN SMALL LETTER INSULAR G;Ll;0;L;;;;;N;;;A77D;;A77D
+1D7A;LATIN SMALL LETTER TH WITH STRIKETHROUGH;Ll;0;L;;;;;N;;;;;
+1D7B;LATIN SMALL CAPITAL LETTER I WITH STROKE;Ll;0;L;;;;;N;;;;;
+1D7C;LATIN SMALL LETTER IOTA WITH STROKE;Ll;0;L;;;;;N;;;;;
+1D7D;LATIN SMALL LETTER P WITH STROKE;Ll;0;L;;;;;N;;;2C63;;2C63
+1D7E;LATIN SMALL CAPITAL LETTER U WITH STROKE;Ll;0;L;;;;;N;;;;;
+1D7F;LATIN SMALL LETTER UPSILON WITH STROKE;Ll;0;L;;;;;N;;;;;
+1D80;LATIN SMALL LETTER B WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1D81;LATIN SMALL LETTER D WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1D82;LATIN SMALL LETTER F WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1D83;LATIN SMALL LETTER G WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1D84;LATIN SMALL LETTER K WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1D85;LATIN SMALL LETTER L WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1D86;LATIN SMALL LETTER M WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1D87;LATIN SMALL LETTER N WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1D88;LATIN SMALL LETTER P WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1D89;LATIN SMALL LETTER R WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1D8A;LATIN SMALL LETTER S WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1D8B;LATIN SMALL LETTER ESH WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1D8C;LATIN SMALL LETTER V WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1D8D;LATIN SMALL LETTER X WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1D8E;LATIN SMALL LETTER Z WITH PALATAL HOOK;Ll;0;L;;;;;N;;;A7C6;;A7C6
+1D8F;LATIN SMALL LETTER A WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+1D90;LATIN SMALL LETTER ALPHA WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+1D91;LATIN SMALL LETTER D WITH HOOK AND TAIL;Ll;0;L;;;;;N;;;;;
+1D92;LATIN SMALL LETTER E WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+1D93;LATIN SMALL LETTER OPEN E WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+1D94;LATIN SMALL LETTER REVERSED OPEN E WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+1D95;LATIN SMALL LETTER SCHWA WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+1D96;LATIN SMALL LETTER I WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+1D97;LATIN SMALL LETTER OPEN O WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+1D98;LATIN SMALL LETTER ESH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+1D99;LATIN SMALL LETTER U WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+1D9A;LATIN SMALL LETTER EZH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+1D9B;MODIFIER LETTER SMALL TURNED ALPHA;Lm;0;L;<super> 0252;;;;N;;;;;
+1D9C;MODIFIER LETTER SMALL C;Lm;0;L;<super> 0063;;;;N;;;;;
+1D9D;MODIFIER LETTER SMALL C WITH CURL;Lm;0;L;<super> 0255;;;;N;;;;;
+1D9E;MODIFIER LETTER SMALL ETH;Lm;0;L;<super> 00F0;;;;N;;;;;
+1D9F;MODIFIER LETTER SMALL REVERSED OPEN E;Lm;0;L;<super> 025C;;;;N;;;;;
+1DA0;MODIFIER LETTER SMALL F;Lm;0;L;<super> 0066;;;;N;;;;;
+1DA1;MODIFIER LETTER SMALL DOTLESS J WITH STROKE;Lm;0;L;<super> 025F;;;;N;;;;;
+1DA2;MODIFIER LETTER SMALL SCRIPT G;Lm;0;L;<super> 0261;;;;N;;;;;
+1DA3;MODIFIER LETTER SMALL TURNED H;Lm;0;L;<super> 0265;;;;N;;;;;
+1DA4;MODIFIER LETTER SMALL I WITH STROKE;Lm;0;L;<super> 0268;;;;N;;;;;
+1DA5;MODIFIER LETTER SMALL IOTA;Lm;0;L;<super> 0269;;;;N;;;;;
+1DA6;MODIFIER LETTER SMALL CAPITAL I;Lm;0;L;<super> 026A;;;;N;;;;;
+1DA7;MODIFIER LETTER SMALL CAPITAL I WITH STROKE;Lm;0;L;<super> 1D7B;;;;N;;;;;
+1DA8;MODIFIER LETTER SMALL J WITH CROSSED-TAIL;Lm;0;L;<super> 029D;;;;N;;;;;
+1DA9;MODIFIER LETTER SMALL L WITH RETROFLEX HOOK;Lm;0;L;<super> 026D;;;;N;;;;;
+1DAA;MODIFIER LETTER SMALL L WITH PALATAL HOOK;Lm;0;L;<super> 1D85;;;;N;;;;;
+1DAB;MODIFIER LETTER SMALL CAPITAL L;Lm;0;L;<super> 029F;;;;N;;;;;
+1DAC;MODIFIER LETTER SMALL M WITH HOOK;Lm;0;L;<super> 0271;;;;N;;;;;
+1DAD;MODIFIER LETTER SMALL TURNED M WITH LONG LEG;Lm;0;L;<super> 0270;;;;N;;;;;
+1DAE;MODIFIER LETTER SMALL N WITH LEFT HOOK;Lm;0;L;<super> 0272;;;;N;;;;;
+1DAF;MODIFIER LETTER SMALL N WITH RETROFLEX HOOK;Lm;0;L;<super> 0273;;;;N;;;;;
+1DB0;MODIFIER LETTER SMALL CAPITAL N;Lm;0;L;<super> 0274;;;;N;;;;;
+1DB1;MODIFIER LETTER SMALL BARRED O;Lm;0;L;<super> 0275;;;;N;;;;;
+1DB2;MODIFIER LETTER SMALL PHI;Lm;0;L;<super> 0278;;;;N;;;;;
+1DB3;MODIFIER LETTER SMALL S WITH HOOK;Lm;0;L;<super> 0282;;;;N;;;;;
+1DB4;MODIFIER LETTER SMALL ESH;Lm;0;L;<super> 0283;;;;N;;;;;
+1DB5;MODIFIER LETTER SMALL T WITH PALATAL HOOK;Lm;0;L;<super> 01AB;;;;N;;;;;
+1DB6;MODIFIER LETTER SMALL U BAR;Lm;0;L;<super> 0289;;;;N;;;;;
+1DB7;MODIFIER LETTER SMALL UPSILON;Lm;0;L;<super> 028A;;;;N;;;;;
+1DB8;MODIFIER LETTER SMALL CAPITAL U;Lm;0;L;<super> 1D1C;;;;N;;;;;
+1DB9;MODIFIER LETTER SMALL V WITH HOOK;Lm;0;L;<super> 028B;;;;N;;;;;
+1DBA;MODIFIER LETTER SMALL TURNED V;Lm;0;L;<super> 028C;;;;N;;;;;
+1DBB;MODIFIER LETTER SMALL Z;Lm;0;L;<super> 007A;;;;N;;;;;
+1DBC;MODIFIER LETTER SMALL Z WITH RETROFLEX HOOK;Lm;0;L;<super> 0290;;;;N;;;;;
+1DBD;MODIFIER LETTER SMALL Z WITH CURL;Lm;0;L;<super> 0291;;;;N;;;;;
+1DBE;MODIFIER LETTER SMALL EZH;Lm;0;L;<super> 0292;;;;N;;;;;
+1DBF;MODIFIER LETTER SMALL THETA;Lm;0;L;<super> 03B8;;;;N;;;;;
+1DC0;COMBINING DOTTED GRAVE ACCENT;Mn;230;NSM;;;;;N;;;;;
+1DC1;COMBINING DOTTED ACUTE ACCENT;Mn;230;NSM;;;;;N;;;;;
+1DC2;COMBINING SNAKE BELOW;Mn;220;NSM;;;;;N;;;;;
+1DC3;COMBINING SUSPENSION MARK;Mn;230;NSM;;;;;N;;;;;
+1DC4;COMBINING MACRON-ACUTE;Mn;230;NSM;;;;;N;;;;;
+1DC5;COMBINING GRAVE-MACRON;Mn;230;NSM;;;;;N;;;;;
+1DC6;COMBINING MACRON-GRAVE;Mn;230;NSM;;;;;N;;;;;
+1DC7;COMBINING ACUTE-MACRON;Mn;230;NSM;;;;;N;;;;;
+1DC8;COMBINING GRAVE-ACUTE-GRAVE;Mn;230;NSM;;;;;N;;;;;
+1DC9;COMBINING ACUTE-GRAVE-ACUTE;Mn;230;NSM;;;;;N;;;;;
+1DCA;COMBINING LATIN SMALL LETTER R BELOW;Mn;220;NSM;;;;;N;;;;;
+1DCB;COMBINING BREVE-MACRON;Mn;230;NSM;;;;;N;;;;;
+1DCC;COMBINING MACRON-BREVE;Mn;230;NSM;;;;;N;;;;;
+1DCD;COMBINING DOUBLE CIRCUMFLEX ABOVE;Mn;234;NSM;;;;;N;;;;;
+1DCE;COMBINING OGONEK ABOVE;Mn;214;NSM;;;;;N;;;;;
+1DCF;COMBINING ZIGZAG BELOW;Mn;220;NSM;;;;;N;;;;;
+1DD0;COMBINING IS BELOW;Mn;202;NSM;;;;;N;;;;;
+1DD1;COMBINING UR ABOVE;Mn;230;NSM;;;;;N;;;;;
+1DD2;COMBINING US ABOVE;Mn;230;NSM;;;;;N;;;;;
+1DD3;COMBINING LATIN SMALL LETTER FLATTENED OPEN A ABOVE;Mn;230;NSM;;;;;N;;;;;
+1DD4;COMBINING LATIN SMALL LETTER AE;Mn;230;NSM;;;;;N;;;;;
+1DD5;COMBINING LATIN SMALL LETTER AO;Mn;230;NSM;;;;;N;;;;;
+1DD6;COMBINING LATIN SMALL LETTER AV;Mn;230;NSM;;;;;N;;;;;
+1DD7;COMBINING LATIN SMALL LETTER C CEDILLA;Mn;230;NSM;;;;;N;;;;;
+1DD8;COMBINING LATIN SMALL LETTER INSULAR D;Mn;230;NSM;;;;;N;;;;;
+1DD9;COMBINING LATIN SMALL LETTER ETH;Mn;230;NSM;;;;;N;;;;;
+1DDA;COMBINING LATIN SMALL LETTER G;Mn;230;NSM;;;;;N;;;;;
+1DDB;COMBINING LATIN LETTER SMALL CAPITAL G;Mn;230;NSM;;;;;N;;;;;
+1DDC;COMBINING LATIN SMALL LETTER K;Mn;230;NSM;;;;;N;;;;;
+1DDD;COMBINING LATIN SMALL LETTER L;Mn;230;NSM;;;;;N;;;;;
+1DDE;COMBINING LATIN LETTER SMALL CAPITAL L;Mn;230;NSM;;;;;N;;;;;
+1DDF;COMBINING LATIN LETTER SMALL CAPITAL M;Mn;230;NSM;;;;;N;;;;;
+1DE0;COMBINING LATIN SMALL LETTER N;Mn;230;NSM;;;;;N;;;;;
+1DE1;COMBINING LATIN LETTER SMALL CAPITAL N;Mn;230;NSM;;;;;N;;;;;
+1DE2;COMBINING LATIN LETTER SMALL CAPITAL R;Mn;230;NSM;;;;;N;;;;;
+1DE3;COMBINING LATIN SMALL LETTER R ROTUNDA;Mn;230;NSM;;;;;N;;;;;
+1DE4;COMBINING LATIN SMALL LETTER S;Mn;230;NSM;;;;;N;;;;;
+1DE5;COMBINING LATIN SMALL LETTER LONG S;Mn;230;NSM;;;;;N;;;;;
+1DE6;COMBINING LATIN SMALL LETTER Z;Mn;230;NSM;;;;;N;;;;;
+1DE7;COMBINING LATIN SMALL LETTER ALPHA;Mn;230;NSM;;;;;N;;;;;
+1DE8;COMBINING LATIN SMALL LETTER B;Mn;230;NSM;;;;;N;;;;;
+1DE9;COMBINING LATIN SMALL LETTER BETA;Mn;230;NSM;;;;;N;;;;;
+1DEA;COMBINING LATIN SMALL LETTER SCHWA;Mn;230;NSM;;;;;N;;;;;
+1DEB;COMBINING LATIN SMALL LETTER F;Mn;230;NSM;;;;;N;;;;;
+1DEC;COMBINING LATIN SMALL LETTER L WITH DOUBLE MIDDLE TILDE;Mn;230;NSM;;;;;N;;;;;
+1DED;COMBINING LATIN SMALL LETTER O WITH LIGHT CENTRALIZATION STROKE;Mn;230;NSM;;;;;N;;;;;
+1DEE;COMBINING LATIN SMALL LETTER P;Mn;230;NSM;;;;;N;;;;;
+1DEF;COMBINING LATIN SMALL LETTER ESH;Mn;230;NSM;;;;;N;;;;;
+1DF0;COMBINING LATIN SMALL LETTER U WITH LIGHT CENTRALIZATION STROKE;Mn;230;NSM;;;;;N;;;;;
+1DF1;COMBINING LATIN SMALL LETTER W;Mn;230;NSM;;;;;N;;;;;
+1DF2;COMBINING LATIN SMALL LETTER A WITH DIAERESIS;Mn;230;NSM;;;;;N;;;;;
+1DF3;COMBINING LATIN SMALL LETTER O WITH DIAERESIS;Mn;230;NSM;;;;;N;;;;;
+1DF4;COMBINING LATIN SMALL LETTER U WITH DIAERESIS;Mn;230;NSM;;;;;N;;;;;
+1DF5;COMBINING UP TACK ABOVE;Mn;230;NSM;;;;;N;;;;;
+1DF6;COMBINING KAVYKA ABOVE RIGHT;Mn;232;NSM;;;;;N;;;;;
+1DF7;COMBINING KAVYKA ABOVE LEFT;Mn;228;NSM;;;;;N;;;;;
+1DF8;COMBINING DOT ABOVE LEFT;Mn;228;NSM;;;;;N;;;;;
+1DF9;COMBINING WIDE INVERTED BRIDGE BELOW;Mn;220;NSM;;;;;N;;;;;
+1DFB;COMBINING DELETION MARK;Mn;230;NSM;;;;;N;;;;;
+1DFC;COMBINING DOUBLE INVERTED BREVE BELOW;Mn;233;NSM;;;;;N;;;;;
+1DFD;COMBINING ALMOST EQUAL TO BELOW;Mn;220;NSM;;;;;N;;;;;
+1DFE;COMBINING LEFT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;;
+1DFF;COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;;
+1E00;LATIN CAPITAL LETTER A WITH RING BELOW;Lu;0;L;0041 0325;;;;N;;;;1E01;
+1E01;LATIN SMALL LETTER A WITH RING BELOW;Ll;0;L;0061 0325;;;;N;;;1E00;;1E00
+1E02;LATIN CAPITAL LETTER B WITH DOT ABOVE;Lu;0;L;0042 0307;;;;N;;;;1E03;
+1E03;LATIN SMALL LETTER B WITH DOT ABOVE;Ll;0;L;0062 0307;;;;N;;;1E02;;1E02
+1E04;LATIN CAPITAL LETTER B WITH DOT BELOW;Lu;0;L;0042 0323;;;;N;;;;1E05;
+1E05;LATIN SMALL LETTER B WITH DOT BELOW;Ll;0;L;0062 0323;;;;N;;;1E04;;1E04
+1E06;LATIN CAPITAL LETTER B WITH LINE BELOW;Lu;0;L;0042 0331;;;;N;;;;1E07;
+1E07;LATIN SMALL LETTER B WITH LINE BELOW;Ll;0;L;0062 0331;;;;N;;;1E06;;1E06
+1E08;LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE;Lu;0;L;00C7 0301;;;;N;;;;1E09;
+1E09;LATIN SMALL LETTER C WITH CEDILLA AND ACUTE;Ll;0;L;00E7 0301;;;;N;;;1E08;;1E08
+1E0A;LATIN CAPITAL LETTER D WITH DOT ABOVE;Lu;0;L;0044 0307;;;;N;;;;1E0B;
+1E0B;LATIN SMALL LETTER D WITH DOT ABOVE;Ll;0;L;0064 0307;;;;N;;;1E0A;;1E0A
+1E0C;LATIN CAPITAL LETTER D WITH DOT BELOW;Lu;0;L;0044 0323;;;;N;;;;1E0D;
+1E0D;LATIN SMALL LETTER D WITH DOT BELOW;Ll;0;L;0064 0323;;;;N;;;1E0C;;1E0C
+1E0E;LATIN CAPITAL LETTER D WITH LINE BELOW;Lu;0;L;0044 0331;;;;N;;;;1E0F;
+1E0F;LATIN SMALL LETTER D WITH LINE BELOW;Ll;0;L;0064 0331;;;;N;;;1E0E;;1E0E
+1E10;LATIN CAPITAL LETTER D WITH CEDILLA;Lu;0;L;0044 0327;;;;N;;;;1E11;
+1E11;LATIN SMALL LETTER D WITH CEDILLA;Ll;0;L;0064 0327;;;;N;;;1E10;;1E10
+1E12;LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW;Lu;0;L;0044 032D;;;;N;;;;1E13;
+1E13;LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW;Ll;0;L;0064 032D;;;;N;;;1E12;;1E12
+1E14;LATIN CAPITAL LETTER E WITH MACRON AND GRAVE;Lu;0;L;0112 0300;;;;N;;;;1E15;
+1E15;LATIN SMALL LETTER E WITH MACRON AND GRAVE;Ll;0;L;0113 0300;;;;N;;;1E14;;1E14
+1E16;LATIN CAPITAL LETTER E WITH MACRON AND ACUTE;Lu;0;L;0112 0301;;;;N;;;;1E17;
+1E17;LATIN SMALL LETTER E WITH MACRON AND ACUTE;Ll;0;L;0113 0301;;;;N;;;1E16;;1E16
+1E18;LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW;Lu;0;L;0045 032D;;;;N;;;;1E19;
+1E19;LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW;Ll;0;L;0065 032D;;;;N;;;1E18;;1E18
+1E1A;LATIN CAPITAL LETTER E WITH TILDE BELOW;Lu;0;L;0045 0330;;;;N;;;;1E1B;
+1E1B;LATIN SMALL LETTER E WITH TILDE BELOW;Ll;0;L;0065 0330;;;;N;;;1E1A;;1E1A
+1E1C;LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE;Lu;0;L;0228 0306;;;;N;;;;1E1D;
+1E1D;LATIN SMALL LETTER E WITH CEDILLA AND BREVE;Ll;0;L;0229 0306;;;;N;;;1E1C;;1E1C
+1E1E;LATIN CAPITAL LETTER F WITH DOT ABOVE;Lu;0;L;0046 0307;;;;N;;;;1E1F;
+1E1F;LATIN SMALL LETTER F WITH DOT ABOVE;Ll;0;L;0066 0307;;;;N;;;1E1E;;1E1E
+1E20;LATIN CAPITAL LETTER G WITH MACRON;Lu;0;L;0047 0304;;;;N;;;;1E21;
+1E21;LATIN SMALL LETTER G WITH MACRON;Ll;0;L;0067 0304;;;;N;;;1E20;;1E20
+1E22;LATIN CAPITAL LETTER H WITH DOT ABOVE;Lu;0;L;0048 0307;;;;N;;;;1E23;
+1E23;LATIN SMALL LETTER H WITH DOT ABOVE;Ll;0;L;0068 0307;;;;N;;;1E22;;1E22
+1E24;LATIN CAPITAL LETTER H WITH DOT BELOW;Lu;0;L;0048 0323;;;;N;;;;1E25;
+1E25;LATIN SMALL LETTER H WITH DOT BELOW;Ll;0;L;0068 0323;;;;N;;;1E24;;1E24
+1E26;LATIN CAPITAL LETTER H WITH DIAERESIS;Lu;0;L;0048 0308;;;;N;;;;1E27;
+1E27;LATIN SMALL LETTER H WITH DIAERESIS;Ll;0;L;0068 0308;;;;N;;;1E26;;1E26
+1E28;LATIN CAPITAL LETTER H WITH CEDILLA;Lu;0;L;0048 0327;;;;N;;;;1E29;
+1E29;LATIN SMALL LETTER H WITH CEDILLA;Ll;0;L;0068 0327;;;;N;;;1E28;;1E28
+1E2A;LATIN CAPITAL LETTER H WITH BREVE BELOW;Lu;0;L;0048 032E;;;;N;;;;1E2B;
+1E2B;LATIN SMALL LETTER H WITH BREVE BELOW;Ll;0;L;0068 032E;;;;N;;;1E2A;;1E2A
+1E2C;LATIN CAPITAL LETTER I WITH TILDE BELOW;Lu;0;L;0049 0330;;;;N;;;;1E2D;
+1E2D;LATIN SMALL LETTER I WITH TILDE BELOW;Ll;0;L;0069 0330;;;;N;;;1E2C;;1E2C
+1E2E;LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE;Lu;0;L;00CF 0301;;;;N;;;;1E2F;
+1E2F;LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE;Ll;0;L;00EF 0301;;;;N;;;1E2E;;1E2E
+1E30;LATIN CAPITAL LETTER K WITH ACUTE;Lu;0;L;004B 0301;;;;N;;;;1E31;
+1E31;LATIN SMALL LETTER K WITH ACUTE;Ll;0;L;006B 0301;;;;N;;;1E30;;1E30
+1E32;LATIN CAPITAL LETTER K WITH DOT BELOW;Lu;0;L;004B 0323;;;;N;;;;1E33;
+1E33;LATIN SMALL LETTER K WITH DOT BELOW;Ll;0;L;006B 0323;;;;N;;;1E32;;1E32
+1E34;LATIN CAPITAL LETTER K WITH LINE BELOW;Lu;0;L;004B 0331;;;;N;;;;1E35;
+1E35;LATIN SMALL LETTER K WITH LINE BELOW;Ll;0;L;006B 0331;;;;N;;;1E34;;1E34
+1E36;LATIN CAPITAL LETTER L WITH DOT BELOW;Lu;0;L;004C 0323;;;;N;;;;1E37;
+1E37;LATIN SMALL LETTER L WITH DOT BELOW;Ll;0;L;006C 0323;;;;N;;;1E36;;1E36
+1E38;LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON;Lu;0;L;1E36 0304;;;;N;;;;1E39;
+1E39;LATIN SMALL LETTER L WITH DOT BELOW AND MACRON;Ll;0;L;1E37 0304;;;;N;;;1E38;;1E38
+1E3A;LATIN CAPITAL LETTER L WITH LINE BELOW;Lu;0;L;004C 0331;;;;N;;;;1E3B;
+1E3B;LATIN SMALL LETTER L WITH LINE BELOW;Ll;0;L;006C 0331;;;;N;;;1E3A;;1E3A
+1E3C;LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW;Lu;0;L;004C 032D;;;;N;;;;1E3D;
+1E3D;LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW;Ll;0;L;006C 032D;;;;N;;;1E3C;;1E3C
+1E3E;LATIN CAPITAL LETTER M WITH ACUTE;Lu;0;L;004D 0301;;;;N;;;;1E3F;
+1E3F;LATIN SMALL LETTER M WITH ACUTE;Ll;0;L;006D 0301;;;;N;;;1E3E;;1E3E
+1E40;LATIN CAPITAL LETTER M WITH DOT ABOVE;Lu;0;L;004D 0307;;;;N;;;;1E41;
+1E41;LATIN SMALL LETTER M WITH DOT ABOVE;Ll;0;L;006D 0307;;;;N;;;1E40;;1E40
+1E42;LATIN CAPITAL LETTER M WITH DOT BELOW;Lu;0;L;004D 0323;;;;N;;;;1E43;
+1E43;LATIN SMALL LETTER M WITH DOT BELOW;Ll;0;L;006D 0323;;;;N;;;1E42;;1E42
+1E44;LATIN CAPITAL LETTER N WITH DOT ABOVE;Lu;0;L;004E 0307;;;;N;;;;1E45;
+1E45;LATIN SMALL LETTER N WITH DOT ABOVE;Ll;0;L;006E 0307;;;;N;;;1E44;;1E44
+1E46;LATIN CAPITAL LETTER N WITH DOT BELOW;Lu;0;L;004E 0323;;;;N;;;;1E47;
+1E47;LATIN SMALL LETTER N WITH DOT BELOW;Ll;0;L;006E 0323;;;;N;;;1E46;;1E46
+1E48;LATIN CAPITAL LETTER N WITH LINE BELOW;Lu;0;L;004E 0331;;;;N;;;;1E49;
+1E49;LATIN SMALL LETTER N WITH LINE BELOW;Ll;0;L;006E 0331;;;;N;;;1E48;;1E48
+1E4A;LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW;Lu;0;L;004E 032D;;;;N;;;;1E4B;
+1E4B;LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW;Ll;0;L;006E 032D;;;;N;;;1E4A;;1E4A
+1E4C;LATIN CAPITAL LETTER O WITH TILDE AND ACUTE;Lu;0;L;00D5 0301;;;;N;;;;1E4D;
+1E4D;LATIN SMALL LETTER O WITH TILDE AND ACUTE;Ll;0;L;00F5 0301;;;;N;;;1E4C;;1E4C
+1E4E;LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS;Lu;0;L;00D5 0308;;;;N;;;;1E4F;
+1E4F;LATIN SMALL LETTER O WITH TILDE AND DIAERESIS;Ll;0;L;00F5 0308;;;;N;;;1E4E;;1E4E
+1E50;LATIN CAPITAL LETTER O WITH MACRON AND GRAVE;Lu;0;L;014C 0300;;;;N;;;;1E51;
+1E51;LATIN SMALL LETTER O WITH MACRON AND GRAVE;Ll;0;L;014D 0300;;;;N;;;1E50;;1E50
+1E52;LATIN CAPITAL LETTER O WITH MACRON AND ACUTE;Lu;0;L;014C 0301;;;;N;;;;1E53;
+1E53;LATIN SMALL LETTER O WITH MACRON AND ACUTE;Ll;0;L;014D 0301;;;;N;;;1E52;;1E52
+1E54;LATIN CAPITAL LETTER P WITH ACUTE;Lu;0;L;0050 0301;;;;N;;;;1E55;
+1E55;LATIN SMALL LETTER P WITH ACUTE;Ll;0;L;0070 0301;;;;N;;;1E54;;1E54
+1E56;LATIN CAPITAL LETTER P WITH DOT ABOVE;Lu;0;L;0050 0307;;;;N;;;;1E57;
+1E57;LATIN SMALL LETTER P WITH DOT ABOVE;Ll;0;L;0070 0307;;;;N;;;1E56;;1E56
+1E58;LATIN CAPITAL LETTER R WITH DOT ABOVE;Lu;0;L;0052 0307;;;;N;;;;1E59;
+1E59;LATIN SMALL LETTER R WITH DOT ABOVE;Ll;0;L;0072 0307;;;;N;;;1E58;;1E58
+1E5A;LATIN CAPITAL LETTER R WITH DOT BELOW;Lu;0;L;0052 0323;;;;N;;;;1E5B;
+1E5B;LATIN SMALL LETTER R WITH DOT BELOW;Ll;0;L;0072 0323;;;;N;;;1E5A;;1E5A
+1E5C;LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON;Lu;0;L;1E5A 0304;;;;N;;;;1E5D;
+1E5D;LATIN SMALL LETTER R WITH DOT BELOW AND MACRON;Ll;0;L;1E5B 0304;;;;N;;;1E5C;;1E5C
+1E5E;LATIN CAPITAL LETTER R WITH LINE BELOW;Lu;0;L;0052 0331;;;;N;;;;1E5F;
+1E5F;LATIN SMALL LETTER R WITH LINE BELOW;Ll;0;L;0072 0331;;;;N;;;1E5E;;1E5E
+1E60;LATIN CAPITAL LETTER S WITH DOT ABOVE;Lu;0;L;0053 0307;;;;N;;;;1E61;
+1E61;LATIN SMALL LETTER S WITH DOT ABOVE;Ll;0;L;0073 0307;;;;N;;;1E60;;1E60
+1E62;LATIN CAPITAL LETTER S WITH DOT BELOW;Lu;0;L;0053 0323;;;;N;;;;1E63;
+1E63;LATIN SMALL LETTER S WITH DOT BELOW;Ll;0;L;0073 0323;;;;N;;;1E62;;1E62
+1E64;LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE;Lu;0;L;015A 0307;;;;N;;;;1E65;
+1E65;LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE;Ll;0;L;015B 0307;;;;N;;;1E64;;1E64
+1E66;LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE;Lu;0;L;0160 0307;;;;N;;;;1E67;
+1E67;LATIN SMALL LETTER S WITH CARON AND DOT ABOVE;Ll;0;L;0161 0307;;;;N;;;1E66;;1E66
+1E68;LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE;Lu;0;L;1E62 0307;;;;N;;;;1E69;
+1E69;LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE;Ll;0;L;1E63 0307;;;;N;;;1E68;;1E68
+1E6A;LATIN CAPITAL LETTER T WITH DOT ABOVE;Lu;0;L;0054 0307;;;;N;;;;1E6B;
+1E6B;LATIN SMALL LETTER T WITH DOT ABOVE;Ll;0;L;0074 0307;;;;N;;;1E6A;;1E6A
+1E6C;LATIN CAPITAL LETTER T WITH DOT BELOW;Lu;0;L;0054 0323;;;;N;;;;1E6D;
+1E6D;LATIN SMALL LETTER T WITH DOT BELOW;Ll;0;L;0074 0323;;;;N;;;1E6C;;1E6C
+1E6E;LATIN CAPITAL LETTER T WITH LINE BELOW;Lu;0;L;0054 0331;;;;N;;;;1E6F;
+1E6F;LATIN SMALL LETTER T WITH LINE BELOW;Ll;0;L;0074 0331;;;;N;;;1E6E;;1E6E
+1E70;LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW;Lu;0;L;0054 032D;;;;N;;;;1E71;
+1E71;LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW;Ll;0;L;0074 032D;;;;N;;;1E70;;1E70
+1E72;LATIN CAPITAL LETTER U WITH DIAERESIS BELOW;Lu;0;L;0055 0324;;;;N;;;;1E73;
+1E73;LATIN SMALL LETTER U WITH DIAERESIS BELOW;Ll;0;L;0075 0324;;;;N;;;1E72;;1E72
+1E74;LATIN CAPITAL LETTER U WITH TILDE BELOW;Lu;0;L;0055 0330;;;;N;;;;1E75;
+1E75;LATIN SMALL LETTER U WITH TILDE BELOW;Ll;0;L;0075 0330;;;;N;;;1E74;;1E74
+1E76;LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW;Lu;0;L;0055 032D;;;;N;;;;1E77;
+1E77;LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW;Ll;0;L;0075 032D;;;;N;;;1E76;;1E76
+1E78;LATIN CAPITAL LETTER U WITH TILDE AND ACUTE;Lu;0;L;0168 0301;;;;N;;;;1E79;
+1E79;LATIN SMALL LETTER U WITH TILDE AND ACUTE;Ll;0;L;0169 0301;;;;N;;;1E78;;1E78
+1E7A;LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS;Lu;0;L;016A 0308;;;;N;;;;1E7B;
+1E7B;LATIN SMALL LETTER U WITH MACRON AND DIAERESIS;Ll;0;L;016B 0308;;;;N;;;1E7A;;1E7A
+1E7C;LATIN CAPITAL LETTER V WITH TILDE;Lu;0;L;0056 0303;;;;N;;;;1E7D;
+1E7D;LATIN SMALL LETTER V WITH TILDE;Ll;0;L;0076 0303;;;;N;;;1E7C;;1E7C
+1E7E;LATIN CAPITAL LETTER V WITH DOT BELOW;Lu;0;L;0056 0323;;;;N;;;;1E7F;
+1E7F;LATIN SMALL LETTER V WITH DOT BELOW;Ll;0;L;0076 0323;;;;N;;;1E7E;;1E7E
+1E80;LATIN CAPITAL LETTER W WITH GRAVE;Lu;0;L;0057 0300;;;;N;;;;1E81;
+1E81;LATIN SMALL LETTER W WITH GRAVE;Ll;0;L;0077 0300;;;;N;;;1E80;;1E80
+1E82;LATIN CAPITAL LETTER W WITH ACUTE;Lu;0;L;0057 0301;;;;N;;;;1E83;
+1E83;LATIN SMALL LETTER W WITH ACUTE;Ll;0;L;0077 0301;;;;N;;;1E82;;1E82
+1E84;LATIN CAPITAL LETTER W WITH DIAERESIS;Lu;0;L;0057 0308;;;;N;;;;1E85;
+1E85;LATIN SMALL LETTER W WITH DIAERESIS;Ll;0;L;0077 0308;;;;N;;;1E84;;1E84
+1E86;LATIN CAPITAL LETTER W WITH DOT ABOVE;Lu;0;L;0057 0307;;;;N;;;;1E87;
+1E87;LATIN SMALL LETTER W WITH DOT ABOVE;Ll;0;L;0077 0307;;;;N;;;1E86;;1E86
+1E88;LATIN CAPITAL LETTER W WITH DOT BELOW;Lu;0;L;0057 0323;;;;N;;;;1E89;
+1E89;LATIN SMALL LETTER W WITH DOT BELOW;Ll;0;L;0077 0323;;;;N;;;1E88;;1E88
+1E8A;LATIN CAPITAL LETTER X WITH DOT ABOVE;Lu;0;L;0058 0307;;;;N;;;;1E8B;
+1E8B;LATIN SMALL LETTER X WITH DOT ABOVE;Ll;0;L;0078 0307;;;;N;;;1E8A;;1E8A
+1E8C;LATIN CAPITAL LETTER X WITH DIAERESIS;Lu;0;L;0058 0308;;;;N;;;;1E8D;
+1E8D;LATIN SMALL LETTER X WITH DIAERESIS;Ll;0;L;0078 0308;;;;N;;;1E8C;;1E8C
+1E8E;LATIN CAPITAL LETTER Y WITH DOT ABOVE;Lu;0;L;0059 0307;;;;N;;;;1E8F;
+1E8F;LATIN SMALL LETTER Y WITH DOT ABOVE;Ll;0;L;0079 0307;;;;N;;;1E8E;;1E8E
+1E90;LATIN CAPITAL LETTER Z WITH CIRCUMFLEX;Lu;0;L;005A 0302;;;;N;;;;1E91;
+1E91;LATIN SMALL LETTER Z WITH CIRCUMFLEX;Ll;0;L;007A 0302;;;;N;;;1E90;;1E90
+1E92;LATIN CAPITAL LETTER Z WITH DOT BELOW;Lu;0;L;005A 0323;;;;N;;;;1E93;
+1E93;LATIN SMALL LETTER Z WITH DOT BELOW;Ll;0;L;007A 0323;;;;N;;;1E92;;1E92
+1E94;LATIN CAPITAL LETTER Z WITH LINE BELOW;Lu;0;L;005A 0331;;;;N;;;;1E95;
+1E95;LATIN SMALL LETTER Z WITH LINE BELOW;Ll;0;L;007A 0331;;;;N;;;1E94;;1E94
+1E96;LATIN SMALL LETTER H WITH LINE BELOW;Ll;0;L;0068 0331;;;;N;;;;;
+1E97;LATIN SMALL LETTER T WITH DIAERESIS;Ll;0;L;0074 0308;;;;N;;;;;
+1E98;LATIN SMALL LETTER W WITH RING ABOVE;Ll;0;L;0077 030A;;;;N;;;;;
+1E99;LATIN SMALL LETTER Y WITH RING ABOVE;Ll;0;L;0079 030A;;;;N;;;;;
+1E9A;LATIN SMALL LETTER A WITH RIGHT HALF RING;Ll;0;L;<compat> 0061 02BE;;;;N;;;;;
+1E9B;LATIN SMALL LETTER LONG S WITH DOT ABOVE;Ll;0;L;017F 0307;;;;N;;;1E60;;1E60
+1E9C;LATIN SMALL LETTER LONG S WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;;;
+1E9D;LATIN SMALL LETTER LONG S WITH HIGH STROKE;Ll;0;L;;;;;N;;;;;
+1E9E;LATIN CAPITAL LETTER SHARP S;Lu;0;L;;;;;N;;;;00DF;
+1E9F;LATIN SMALL LETTER DELTA;Ll;0;L;;;;;N;;;;;
+1EA0;LATIN CAPITAL LETTER A WITH DOT BELOW;Lu;0;L;0041 0323;;;;N;;;;1EA1;
+1EA1;LATIN SMALL LETTER A WITH DOT BELOW;Ll;0;L;0061 0323;;;;N;;;1EA0;;1EA0
+1EA2;LATIN CAPITAL LETTER A WITH HOOK ABOVE;Lu;0;L;0041 0309;;;;N;;;;1EA3;
+1EA3;LATIN SMALL LETTER A WITH HOOK ABOVE;Ll;0;L;0061 0309;;;;N;;;1EA2;;1EA2
+1EA4;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00C2 0301;;;;N;;;;1EA5;
+1EA5;LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00E2 0301;;;;N;;;1EA4;;1EA4
+1EA6;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00C2 0300;;;;N;;;;1EA7;
+1EA7;LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00E2 0300;;;;N;;;1EA6;;1EA6
+1EA8;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00C2 0309;;;;N;;;;1EA9;
+1EA9;LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00E2 0309;;;;N;;;1EA8;;1EA8
+1EAA;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE;Lu;0;L;00C2 0303;;;;N;;;;1EAB;
+1EAB;LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE;Ll;0;L;00E2 0303;;;;N;;;1EAA;;1EAA
+1EAC;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1EA0 0302;;;;N;;;;1EAD;
+1EAD;LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1EA1 0302;;;;N;;;1EAC;;1EAC
+1EAE;LATIN CAPITAL LETTER A WITH BREVE AND ACUTE;Lu;0;L;0102 0301;;;;N;;;;1EAF;
+1EAF;LATIN SMALL LETTER A WITH BREVE AND ACUTE;Ll;0;L;0103 0301;;;;N;;;1EAE;;1EAE
+1EB0;LATIN CAPITAL LETTER A WITH BREVE AND GRAVE;Lu;0;L;0102 0300;;;;N;;;;1EB1;
+1EB1;LATIN SMALL LETTER A WITH BREVE AND GRAVE;Ll;0;L;0103 0300;;;;N;;;1EB0;;1EB0
+1EB2;LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE;Lu;0;L;0102 0309;;;;N;;;;1EB3;
+1EB3;LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE;Ll;0;L;0103 0309;;;;N;;;1EB2;;1EB2
+1EB4;LATIN CAPITAL LETTER A WITH BREVE AND TILDE;Lu;0;L;0102 0303;;;;N;;;;1EB5;
+1EB5;LATIN SMALL LETTER A WITH BREVE AND TILDE;Ll;0;L;0103 0303;;;;N;;;1EB4;;1EB4
+1EB6;LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW;Lu;0;L;1EA0 0306;;;;N;;;;1EB7;
+1EB7;LATIN SMALL LETTER A WITH BREVE AND DOT BELOW;Ll;0;L;1EA1 0306;;;;N;;;1EB6;;1EB6
+1EB8;LATIN CAPITAL LETTER E WITH DOT BELOW;Lu;0;L;0045 0323;;;;N;;;;1EB9;
+1EB9;LATIN SMALL LETTER E WITH DOT BELOW;Ll;0;L;0065 0323;;;;N;;;1EB8;;1EB8
+1EBA;LATIN CAPITAL LETTER E WITH HOOK ABOVE;Lu;0;L;0045 0309;;;;N;;;;1EBB;
+1EBB;LATIN SMALL LETTER E WITH HOOK ABOVE;Ll;0;L;0065 0309;;;;N;;;1EBA;;1EBA
+1EBC;LATIN CAPITAL LETTER E WITH TILDE;Lu;0;L;0045 0303;;;;N;;;;1EBD;
+1EBD;LATIN SMALL LETTER E WITH TILDE;Ll;0;L;0065 0303;;;;N;;;1EBC;;1EBC
+1EBE;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00CA 0301;;;;N;;;;1EBF;
+1EBF;LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00EA 0301;;;;N;;;1EBE;;1EBE
+1EC0;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00CA 0300;;;;N;;;;1EC1;
+1EC1;LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00EA 0300;;;;N;;;1EC0;;1EC0
+1EC2;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00CA 0309;;;;N;;;;1EC3;
+1EC3;LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00EA 0309;;;;N;;;1EC2;;1EC2
+1EC4;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE;Lu;0;L;00CA 0303;;;;N;;;;1EC5;
+1EC5;LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE;Ll;0;L;00EA 0303;;;;N;;;1EC4;;1EC4
+1EC6;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1EB8 0302;;;;N;;;;1EC7;
+1EC7;LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1EB9 0302;;;;N;;;1EC6;;1EC6
+1EC8;LATIN CAPITAL LETTER I WITH HOOK ABOVE;Lu;0;L;0049 0309;;;;N;;;;1EC9;
+1EC9;LATIN SMALL LETTER I WITH HOOK ABOVE;Ll;0;L;0069 0309;;;;N;;;1EC8;;1EC8
+1ECA;LATIN CAPITAL LETTER I WITH DOT BELOW;Lu;0;L;0049 0323;;;;N;;;;1ECB;
+1ECB;LATIN SMALL LETTER I WITH DOT BELOW;Ll;0;L;0069 0323;;;;N;;;1ECA;;1ECA
+1ECC;LATIN CAPITAL LETTER O WITH DOT BELOW;Lu;0;L;004F 0323;;;;N;;;;1ECD;
+1ECD;LATIN SMALL LETTER O WITH DOT BELOW;Ll;0;L;006F 0323;;;;N;;;1ECC;;1ECC
+1ECE;LATIN CAPITAL LETTER O WITH HOOK ABOVE;Lu;0;L;004F 0309;;;;N;;;;1ECF;
+1ECF;LATIN SMALL LETTER O WITH HOOK ABOVE;Ll;0;L;006F 0309;;;;N;;;1ECE;;1ECE
+1ED0;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00D4 0301;;;;N;;;;1ED1;
+1ED1;LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00F4 0301;;;;N;;;1ED0;;1ED0
+1ED2;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00D4 0300;;;;N;;;;1ED3;
+1ED3;LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00F4 0300;;;;N;;;1ED2;;1ED2
+1ED4;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00D4 0309;;;;N;;;;1ED5;
+1ED5;LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00F4 0309;;;;N;;;1ED4;;1ED4
+1ED6;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE;Lu;0;L;00D4 0303;;;;N;;;;1ED7;
+1ED7;LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE;Ll;0;L;00F4 0303;;;;N;;;1ED6;;1ED6
+1ED8;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1ECC 0302;;;;N;;;;1ED9;
+1ED9;LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1ECD 0302;;;;N;;;1ED8;;1ED8
+1EDA;LATIN CAPITAL LETTER O WITH HORN AND ACUTE;Lu;0;L;01A0 0301;;;;N;;;;1EDB;
+1EDB;LATIN SMALL LETTER O WITH HORN AND ACUTE;Ll;0;L;01A1 0301;;;;N;;;1EDA;;1EDA
+1EDC;LATIN CAPITAL LETTER O WITH HORN AND GRAVE;Lu;0;L;01A0 0300;;;;N;;;;1EDD;
+1EDD;LATIN SMALL LETTER O WITH HORN AND GRAVE;Ll;0;L;01A1 0300;;;;N;;;1EDC;;1EDC
+1EDE;LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE;Lu;0;L;01A0 0309;;;;N;;;;1EDF;
+1EDF;LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE;Ll;0;L;01A1 0309;;;;N;;;1EDE;;1EDE
+1EE0;LATIN CAPITAL LETTER O WITH HORN AND TILDE;Lu;0;L;01A0 0303;;;;N;;;;1EE1;
+1EE1;LATIN SMALL LETTER O WITH HORN AND TILDE;Ll;0;L;01A1 0303;;;;N;;;1EE0;;1EE0
+1EE2;LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW;Lu;0;L;01A0 0323;;;;N;;;;1EE3;
+1EE3;LATIN SMALL LETTER O WITH HORN AND DOT BELOW;Ll;0;L;01A1 0323;;;;N;;;1EE2;;1EE2
+1EE4;LATIN CAPITAL LETTER U WITH DOT BELOW;Lu;0;L;0055 0323;;;;N;;;;1EE5;
+1EE5;LATIN SMALL LETTER U WITH DOT BELOW;Ll;0;L;0075 0323;;;;N;;;1EE4;;1EE4
+1EE6;LATIN CAPITAL LETTER U WITH HOOK ABOVE;Lu;0;L;0055 0309;;;;N;;;;1EE7;
+1EE7;LATIN SMALL LETTER U WITH HOOK ABOVE;Ll;0;L;0075 0309;;;;N;;;1EE6;;1EE6
+1EE8;LATIN CAPITAL LETTER U WITH HORN AND ACUTE;Lu;0;L;01AF 0301;;;;N;;;;1EE9;
+1EE9;LATIN SMALL LETTER U WITH HORN AND ACUTE;Ll;0;L;01B0 0301;;;;N;;;1EE8;;1EE8
+1EEA;LATIN CAPITAL LETTER U WITH HORN AND GRAVE;Lu;0;L;01AF 0300;;;;N;;;;1EEB;
+1EEB;LATIN SMALL LETTER U WITH HORN AND GRAVE;Ll;0;L;01B0 0300;;;;N;;;1EEA;;1EEA
+1EEC;LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE;Lu;0;L;01AF 0309;;;;N;;;;1EED;
+1EED;LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE;Ll;0;L;01B0 0309;;;;N;;;1EEC;;1EEC
+1EEE;LATIN CAPITAL LETTER U WITH HORN AND TILDE;Lu;0;L;01AF 0303;;;;N;;;;1EEF;
+1EEF;LATIN SMALL LETTER U WITH HORN AND TILDE;Ll;0;L;01B0 0303;;;;N;;;1EEE;;1EEE
+1EF0;LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW;Lu;0;L;01AF 0323;;;;N;;;;1EF1;
+1EF1;LATIN SMALL LETTER U WITH HORN AND DOT BELOW;Ll;0;L;01B0 0323;;;;N;;;1EF0;;1EF0
+1EF2;LATIN CAPITAL LETTER Y WITH GRAVE;Lu;0;L;0059 0300;;;;N;;;;1EF3;
+1EF3;LATIN SMALL LETTER Y WITH GRAVE;Ll;0;L;0079 0300;;;;N;;;1EF2;;1EF2
+1EF4;LATIN CAPITAL LETTER Y WITH DOT BELOW;Lu;0;L;0059 0323;;;;N;;;;1EF5;
+1EF5;LATIN SMALL LETTER Y WITH DOT BELOW;Ll;0;L;0079 0323;;;;N;;;1EF4;;1EF4
+1EF6;LATIN CAPITAL LETTER Y WITH HOOK ABOVE;Lu;0;L;0059 0309;;;;N;;;;1EF7;
+1EF7;LATIN SMALL LETTER Y WITH HOOK ABOVE;Ll;0;L;0079 0309;;;;N;;;1EF6;;1EF6
+1EF8;LATIN CAPITAL LETTER Y WITH TILDE;Lu;0;L;0059 0303;;;;N;;;;1EF9;
+1EF9;LATIN SMALL LETTER Y WITH TILDE;Ll;0;L;0079 0303;;;;N;;;1EF8;;1EF8
+1EFA;LATIN CAPITAL LETTER MIDDLE-WELSH LL;Lu;0;L;;;;;N;;;;1EFB;
+1EFB;LATIN SMALL LETTER MIDDLE-WELSH LL;Ll;0;L;;;;;N;;;1EFA;;1EFA
+1EFC;LATIN CAPITAL LETTER MIDDLE-WELSH V;Lu;0;L;;;;;N;;;;1EFD;
+1EFD;LATIN SMALL LETTER MIDDLE-WELSH V;Ll;0;L;;;;;N;;;1EFC;;1EFC
+1EFE;LATIN CAPITAL LETTER Y WITH LOOP;Lu;0;L;;;;;N;;;;1EFF;
+1EFF;LATIN SMALL LETTER Y WITH LOOP;Ll;0;L;;;;;N;;;1EFE;;1EFE
+1F00;GREEK SMALL LETTER ALPHA WITH PSILI;Ll;0;L;03B1 0313;;;;N;;;1F08;;1F08
+1F01;GREEK SMALL LETTER ALPHA WITH DASIA;Ll;0;L;03B1 0314;;;;N;;;1F09;;1F09
+1F02;GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA;Ll;0;L;1F00 0300;;;;N;;;1F0A;;1F0A
+1F03;GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA;Ll;0;L;1F01 0300;;;;N;;;1F0B;;1F0B
+1F04;GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA;Ll;0;L;1F00 0301;;;;N;;;1F0C;;1F0C
+1F05;GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA;Ll;0;L;1F01 0301;;;;N;;;1F0D;;1F0D
+1F06;GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI;Ll;0;L;1F00 0342;;;;N;;;1F0E;;1F0E
+1F07;GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI;Ll;0;L;1F01 0342;;;;N;;;1F0F;;1F0F
+1F08;GREEK CAPITAL LETTER ALPHA WITH PSILI;Lu;0;L;0391 0313;;;;N;;;;1F00;
+1F09;GREEK CAPITAL LETTER ALPHA WITH DASIA;Lu;0;L;0391 0314;;;;N;;;;1F01;
+1F0A;GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA;Lu;0;L;1F08 0300;;;;N;;;;1F02;
+1F0B;GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA;Lu;0;L;1F09 0300;;;;N;;;;1F03;
+1F0C;GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA;Lu;0;L;1F08 0301;;;;N;;;;1F04;
+1F0D;GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA;Lu;0;L;1F09 0301;;;;N;;;;1F05;
+1F0E;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI;Lu;0;L;1F08 0342;;;;N;;;;1F06;
+1F0F;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI;Lu;0;L;1F09 0342;;;;N;;;;1F07;
+1F10;GREEK SMALL LETTER EPSILON WITH PSILI;Ll;0;L;03B5 0313;;;;N;;;1F18;;1F18
+1F11;GREEK SMALL LETTER EPSILON WITH DASIA;Ll;0;L;03B5 0314;;;;N;;;1F19;;1F19
+1F12;GREEK SMALL LETTER EPSILON WITH PSILI AND VARIA;Ll;0;L;1F10 0300;;;;N;;;1F1A;;1F1A
+1F13;GREEK SMALL LETTER EPSILON WITH DASIA AND VARIA;Ll;0;L;1F11 0300;;;;N;;;1F1B;;1F1B
+1F14;GREEK SMALL LETTER EPSILON WITH PSILI AND OXIA;Ll;0;L;1F10 0301;;;;N;;;1F1C;;1F1C
+1F15;GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA;Ll;0;L;1F11 0301;;;;N;;;1F1D;;1F1D
+1F18;GREEK CAPITAL LETTER EPSILON WITH PSILI;Lu;0;L;0395 0313;;;;N;;;;1F10;
+1F19;GREEK CAPITAL LETTER EPSILON WITH DASIA;Lu;0;L;0395 0314;;;;N;;;;1F11;
+1F1A;GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA;Lu;0;L;1F18 0300;;;;N;;;;1F12;
+1F1B;GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA;Lu;0;L;1F19 0300;;;;N;;;;1F13;
+1F1C;GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA;Lu;0;L;1F18 0301;;;;N;;;;1F14;
+1F1D;GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA;Lu;0;L;1F19 0301;;;;N;;;;1F15;
+1F20;GREEK SMALL LETTER ETA WITH PSILI;Ll;0;L;03B7 0313;;;;N;;;1F28;;1F28
+1F21;GREEK SMALL LETTER ETA WITH DASIA;Ll;0;L;03B7 0314;;;;N;;;1F29;;1F29
+1F22;GREEK SMALL LETTER ETA WITH PSILI AND VARIA;Ll;0;L;1F20 0300;;;;N;;;1F2A;;1F2A
+1F23;GREEK SMALL LETTER ETA WITH DASIA AND VARIA;Ll;0;L;1F21 0300;;;;N;;;1F2B;;1F2B
+1F24;GREEK SMALL LETTER ETA WITH PSILI AND OXIA;Ll;0;L;1F20 0301;;;;N;;;1F2C;;1F2C
+1F25;GREEK SMALL LETTER ETA WITH DASIA AND OXIA;Ll;0;L;1F21 0301;;;;N;;;1F2D;;1F2D
+1F26;GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI;Ll;0;L;1F20 0342;;;;N;;;1F2E;;1F2E
+1F27;GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI;Ll;0;L;1F21 0342;;;;N;;;1F2F;;1F2F
+1F28;GREEK CAPITAL LETTER ETA WITH PSILI;Lu;0;L;0397 0313;;;;N;;;;1F20;
+1F29;GREEK CAPITAL LETTER ETA WITH DASIA;Lu;0;L;0397 0314;;;;N;;;;1F21;
+1F2A;GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA;Lu;0;L;1F28 0300;;;;N;;;;1F22;
+1F2B;GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA;Lu;0;L;1F29 0300;;;;N;;;;1F23;
+1F2C;GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA;Lu;0;L;1F28 0301;;;;N;;;;1F24;
+1F2D;GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA;Lu;0;L;1F29 0301;;;;N;;;;1F25;
+1F2E;GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI;Lu;0;L;1F28 0342;;;;N;;;;1F26;
+1F2F;GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI;Lu;0;L;1F29 0342;;;;N;;;;1F27;
+1F30;GREEK SMALL LETTER IOTA WITH PSILI;Ll;0;L;03B9 0313;;;;N;;;1F38;;1F38
+1F31;GREEK SMALL LETTER IOTA WITH DASIA;Ll;0;L;03B9 0314;;;;N;;;1F39;;1F39
+1F32;GREEK SMALL LETTER IOTA WITH PSILI AND VARIA;Ll;0;L;1F30 0300;;;;N;;;1F3A;;1F3A
+1F33;GREEK SMALL LETTER IOTA WITH DASIA AND VARIA;Ll;0;L;1F31 0300;;;;N;;;1F3B;;1F3B
+1F34;GREEK SMALL LETTER IOTA WITH PSILI AND OXIA;Ll;0;L;1F30 0301;;;;N;;;1F3C;;1F3C
+1F35;GREEK SMALL LETTER IOTA WITH DASIA AND OXIA;Ll;0;L;1F31 0301;;;;N;;;1F3D;;1F3D
+1F36;GREEK SMALL LETTER IOTA WITH PSILI AND PERISPOMENI;Ll;0;L;1F30 0342;;;;N;;;1F3E;;1F3E
+1F37;GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI;Ll;0;L;1F31 0342;;;;N;;;1F3F;;1F3F
+1F38;GREEK CAPITAL LETTER IOTA WITH PSILI;Lu;0;L;0399 0313;;;;N;;;;1F30;
+1F39;GREEK CAPITAL LETTER IOTA WITH DASIA;Lu;0;L;0399 0314;;;;N;;;;1F31;
+1F3A;GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA;Lu;0;L;1F38 0300;;;;N;;;;1F32;
+1F3B;GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA;Lu;0;L;1F39 0300;;;;N;;;;1F33;
+1F3C;GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA;Lu;0;L;1F38 0301;;;;N;;;;1F34;
+1F3D;GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA;Lu;0;L;1F39 0301;;;;N;;;;1F35;
+1F3E;GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI;Lu;0;L;1F38 0342;;;;N;;;;1F36;
+1F3F;GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI;Lu;0;L;1F39 0342;;;;N;;;;1F37;
+1F40;GREEK SMALL LETTER OMICRON WITH PSILI;Ll;0;L;03BF 0313;;;;N;;;1F48;;1F48
+1F41;GREEK SMALL LETTER OMICRON WITH DASIA;Ll;0;L;03BF 0314;;;;N;;;1F49;;1F49
+1F42;GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA;Ll;0;L;1F40 0300;;;;N;;;1F4A;;1F4A
+1F43;GREEK SMALL LETTER OMICRON WITH DASIA AND VARIA;Ll;0;L;1F41 0300;;;;N;;;1F4B;;1F4B
+1F44;GREEK SMALL LETTER OMICRON WITH PSILI AND OXIA;Ll;0;L;1F40 0301;;;;N;;;1F4C;;1F4C
+1F45;GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA;Ll;0;L;1F41 0301;;;;N;;;1F4D;;1F4D
+1F48;GREEK CAPITAL LETTER OMICRON WITH PSILI;Lu;0;L;039F 0313;;;;N;;;;1F40;
+1F49;GREEK CAPITAL LETTER OMICRON WITH DASIA;Lu;0;L;039F 0314;;;;N;;;;1F41;
+1F4A;GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA;Lu;0;L;1F48 0300;;;;N;;;;1F42;
+1F4B;GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA;Lu;0;L;1F49 0300;;;;N;;;;1F43;
+1F4C;GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA;Lu;0;L;1F48 0301;;;;N;;;;1F44;
+1F4D;GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA;Lu;0;L;1F49 0301;;;;N;;;;1F45;
+1F50;GREEK SMALL LETTER UPSILON WITH PSILI;Ll;0;L;03C5 0313;;;;N;;;;;
+1F51;GREEK SMALL LETTER UPSILON WITH DASIA;Ll;0;L;03C5 0314;;;;N;;;1F59;;1F59
+1F52;GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA;Ll;0;L;1F50 0300;;;;N;;;;;
+1F53;GREEK SMALL LETTER UPSILON WITH DASIA AND VARIA;Ll;0;L;1F51 0300;;;;N;;;1F5B;;1F5B
+1F54;GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA;Ll;0;L;1F50 0301;;;;N;;;;;
+1F55;GREEK SMALL LETTER UPSILON WITH DASIA AND OXIA;Ll;0;L;1F51 0301;;;;N;;;1F5D;;1F5D
+1F56;GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI;Ll;0;L;1F50 0342;;;;N;;;;;
+1F57;GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI;Ll;0;L;1F51 0342;;;;N;;;1F5F;;1F5F
+1F59;GREEK CAPITAL LETTER UPSILON WITH DASIA;Lu;0;L;03A5 0314;;;;N;;;;1F51;
+1F5B;GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA;Lu;0;L;1F59 0300;;;;N;;;;1F53;
+1F5D;GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA;Lu;0;L;1F59 0301;;;;N;;;;1F55;
+1F5F;GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI;Lu;0;L;1F59 0342;;;;N;;;;1F57;
+1F60;GREEK SMALL LETTER OMEGA WITH PSILI;Ll;0;L;03C9 0313;;;;N;;;1F68;;1F68
+1F61;GREEK SMALL LETTER OMEGA WITH DASIA;Ll;0;L;03C9 0314;;;;N;;;1F69;;1F69
+1F62;GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA;Ll;0;L;1F60 0300;;;;N;;;1F6A;;1F6A
+1F63;GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA;Ll;0;L;1F61 0300;;;;N;;;1F6B;;1F6B
+1F64;GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA;Ll;0;L;1F60 0301;;;;N;;;1F6C;;1F6C
+1F65;GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA;Ll;0;L;1F61 0301;;;;N;;;1F6D;;1F6D
+1F66;GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI;Ll;0;L;1F60 0342;;;;N;;;1F6E;;1F6E
+1F67;GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI;Ll;0;L;1F61 0342;;;;N;;;1F6F;;1F6F
+1F68;GREEK CAPITAL LETTER OMEGA WITH PSILI;Lu;0;L;03A9 0313;;;;N;;;;1F60;
+1F69;GREEK CAPITAL LETTER OMEGA WITH DASIA;Lu;0;L;03A9 0314;;;;N;;;;1F61;
+1F6A;GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA;Lu;0;L;1F68 0300;;;;N;;;;1F62;
+1F6B;GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA;Lu;0;L;1F69 0300;;;;N;;;;1F63;
+1F6C;GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA;Lu;0;L;1F68 0301;;;;N;;;;1F64;
+1F6D;GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA;Lu;0;L;1F69 0301;;;;N;;;;1F65;
+1F6E;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI;Lu;0;L;1F68 0342;;;;N;;;;1F66;
+1F6F;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI;Lu;0;L;1F69 0342;;;;N;;;;1F67;
+1F70;GREEK SMALL LETTER ALPHA WITH VARIA;Ll;0;L;03B1 0300;;;;N;;;1FBA;;1FBA
+1F71;GREEK SMALL LETTER ALPHA WITH OXIA;Ll;0;L;03AC;;;;N;;;1FBB;;1FBB
+1F72;GREEK SMALL LETTER EPSILON WITH VARIA;Ll;0;L;03B5 0300;;;;N;;;1FC8;;1FC8
+1F73;GREEK SMALL LETTER EPSILON WITH OXIA;Ll;0;L;03AD;;;;N;;;1FC9;;1FC9
+1F74;GREEK SMALL LETTER ETA WITH VARIA;Ll;0;L;03B7 0300;;;;N;;;1FCA;;1FCA
+1F75;GREEK SMALL LETTER ETA WITH OXIA;Ll;0;L;03AE;;;;N;;;1FCB;;1FCB
+1F76;GREEK SMALL LETTER IOTA WITH VARIA;Ll;0;L;03B9 0300;;;;N;;;1FDA;;1FDA
+1F77;GREEK SMALL LETTER IOTA WITH OXIA;Ll;0;L;03AF;;;;N;;;1FDB;;1FDB
+1F78;GREEK SMALL LETTER OMICRON WITH VARIA;Ll;0;L;03BF 0300;;;;N;;;1FF8;;1FF8
+1F79;GREEK SMALL LETTER OMICRON WITH OXIA;Ll;0;L;03CC;;;;N;;;1FF9;;1FF9
+1F7A;GREEK SMALL LETTER UPSILON WITH VARIA;Ll;0;L;03C5 0300;;;;N;;;1FEA;;1FEA
+1F7B;GREEK SMALL LETTER UPSILON WITH OXIA;Ll;0;L;03CD;;;;N;;;1FEB;;1FEB
+1F7C;GREEK SMALL LETTER OMEGA WITH VARIA;Ll;0;L;03C9 0300;;;;N;;;1FFA;;1FFA
+1F7D;GREEK SMALL LETTER OMEGA WITH OXIA;Ll;0;L;03CE;;;;N;;;1FFB;;1FFB
+1F80;GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F00 0345;;;;N;;;1F88;;1F88
+1F81;GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F01 0345;;;;N;;;1F89;;1F89
+1F82;GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F02 0345;;;;N;;;1F8A;;1F8A
+1F83;GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F03 0345;;;;N;;;1F8B;;1F8B
+1F84;GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F04 0345;;;;N;;;1F8C;;1F8C
+1F85;GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F05 0345;;;;N;;;1F8D;;1F8D
+1F86;GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F06 0345;;;;N;;;1F8E;;1F8E
+1F87;GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F07 0345;;;;N;;;1F8F;;1F8F
+1F88;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F08 0345;;;;N;;;;1F80;
+1F89;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F09 0345;;;;N;;;;1F81;
+1F8A;GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F0A 0345;;;;N;;;;1F82;
+1F8B;GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F0B 0345;;;;N;;;;1F83;
+1F8C;GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F0C 0345;;;;N;;;;1F84;
+1F8D;GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F0D 0345;;;;N;;;;1F85;
+1F8E;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F0E 0345;;;;N;;;;1F86;
+1F8F;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F0F 0345;;;;N;;;;1F87;
+1F90;GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F20 0345;;;;N;;;1F98;;1F98
+1F91;GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F21 0345;;;;N;;;1F99;;1F99
+1F92;GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F22 0345;;;;N;;;1F9A;;1F9A
+1F93;GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F23 0345;;;;N;;;1F9B;;1F9B
+1F94;GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F24 0345;;;;N;;;1F9C;;1F9C
+1F95;GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F25 0345;;;;N;;;1F9D;;1F9D
+1F96;GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F26 0345;;;;N;;;1F9E;;1F9E
+1F97;GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F27 0345;;;;N;;;1F9F;;1F9F
+1F98;GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F28 0345;;;;N;;;;1F90;
+1F99;GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F29 0345;;;;N;;;;1F91;
+1F9A;GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F2A 0345;;;;N;;;;1F92;
+1F9B;GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F2B 0345;;;;N;;;;1F93;
+1F9C;GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F2C 0345;;;;N;;;;1F94;
+1F9D;GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F2D 0345;;;;N;;;;1F95;
+1F9E;GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F2E 0345;;;;N;;;;1F96;
+1F9F;GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F2F 0345;;;;N;;;;1F97;
+1FA0;GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F60 0345;;;;N;;;1FA8;;1FA8
+1FA1;GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F61 0345;;;;N;;;1FA9;;1FA9
+1FA2;GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F62 0345;;;;N;;;1FAA;;1FAA
+1FA3;GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F63 0345;;;;N;;;1FAB;;1FAB
+1FA4;GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F64 0345;;;;N;;;1FAC;;1FAC
+1FA5;GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F65 0345;;;;N;;;1FAD;;1FAD
+1FA6;GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F66 0345;;;;N;;;1FAE;;1FAE
+1FA7;GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F67 0345;;;;N;;;1FAF;;1FAF
+1FA8;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F68 0345;;;;N;;;;1FA0;
+1FA9;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F69 0345;;;;N;;;;1FA1;
+1FAA;GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F6A 0345;;;;N;;;;1FA2;
+1FAB;GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F6B 0345;;;;N;;;;1FA3;
+1FAC;GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F6C 0345;;;;N;;;;1FA4;
+1FAD;GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F6D 0345;;;;N;;;;1FA5;
+1FAE;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F6E 0345;;;;N;;;;1FA6;
+1FAF;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F6F 0345;;;;N;;;;1FA7;
+1FB0;GREEK SMALL LETTER ALPHA WITH VRACHY;Ll;0;L;03B1 0306;;;;N;;;1FB8;;1FB8
+1FB1;GREEK SMALL LETTER ALPHA WITH MACRON;Ll;0;L;03B1 0304;;;;N;;;1FB9;;1FB9
+1FB2;GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F70 0345;;;;N;;;;;
+1FB3;GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI;Ll;0;L;03B1 0345;;;;N;;;1FBC;;1FBC
+1FB4;GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03AC 0345;;;;N;;;;;
+1FB6;GREEK SMALL LETTER ALPHA WITH PERISPOMENI;Ll;0;L;03B1 0342;;;;N;;;;;
+1FB7;GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FB6 0345;;;;N;;;;;
+1FB8;GREEK CAPITAL LETTER ALPHA WITH VRACHY;Lu;0;L;0391 0306;;;;N;;;;1FB0;
+1FB9;GREEK CAPITAL LETTER ALPHA WITH MACRON;Lu;0;L;0391 0304;;;;N;;;;1FB1;
+1FBA;GREEK CAPITAL LETTER ALPHA WITH VARIA;Lu;0;L;0391 0300;;;;N;;;;1F70;
+1FBB;GREEK CAPITAL LETTER ALPHA WITH OXIA;Lu;0;L;0386;;;;N;;;;1F71;
+1FBC;GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI;Lt;0;L;0391 0345;;;;N;;;;1FB3;
+1FBD;GREEK KORONIS;Sk;0;ON;<compat> 0020 0313;;;;N;;;;;
+1FBE;GREEK PROSGEGRAMMENI;Ll;0;L;03B9;;;;N;;;0399;;0399
+1FBF;GREEK PSILI;Sk;0;ON;<compat> 0020 0313;;;;N;;;;;
+1FC0;GREEK PERISPOMENI;Sk;0;ON;<compat> 0020 0342;;;;N;;;;;
+1FC1;GREEK DIALYTIKA AND PERISPOMENI;Sk;0;ON;00A8 0342;;;;N;;;;;
+1FC2;GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F74 0345;;;;N;;;;;
+1FC3;GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI;Ll;0;L;03B7 0345;;;;N;;;1FCC;;1FCC
+1FC4;GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03AE 0345;;;;N;;;;;
+1FC6;GREEK SMALL LETTER ETA WITH PERISPOMENI;Ll;0;L;03B7 0342;;;;N;;;;;
+1FC7;GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FC6 0345;;;;N;;;;;
+1FC8;GREEK CAPITAL LETTER EPSILON WITH VARIA;Lu;0;L;0395 0300;;;;N;;;;1F72;
+1FC9;GREEK CAPITAL LETTER EPSILON WITH OXIA;Lu;0;L;0388;;;;N;;;;1F73;
+1FCA;GREEK CAPITAL LETTER ETA WITH VARIA;Lu;0;L;0397 0300;;;;N;;;;1F74;
+1FCB;GREEK CAPITAL LETTER ETA WITH OXIA;Lu;0;L;0389;;;;N;;;;1F75;
+1FCC;GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI;Lt;0;L;0397 0345;;;;N;;;;1FC3;
+1FCD;GREEK PSILI AND VARIA;Sk;0;ON;1FBF 0300;;;;N;;;;;
+1FCE;GREEK PSILI AND OXIA;Sk;0;ON;1FBF 0301;;;;N;;;;;
+1FCF;GREEK PSILI AND PERISPOMENI;Sk;0;ON;1FBF 0342;;;;N;;;;;
+1FD0;GREEK SMALL LETTER IOTA WITH VRACHY;Ll;0;L;03B9 0306;;;;N;;;1FD8;;1FD8
+1FD1;GREEK SMALL LETTER IOTA WITH MACRON;Ll;0;L;03B9 0304;;;;N;;;1FD9;;1FD9
+1FD2;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA;Ll;0;L;03CA 0300;;;;N;;;;;
+1FD3;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA;Ll;0;L;0390;;;;N;;;;;
+1FD6;GREEK SMALL LETTER IOTA WITH PERISPOMENI;Ll;0;L;03B9 0342;;;;N;;;;;
+1FD7;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI;Ll;0;L;03CA 0342;;;;N;;;;;
+1FD8;GREEK CAPITAL LETTER IOTA WITH VRACHY;Lu;0;L;0399 0306;;;;N;;;;1FD0;
+1FD9;GREEK CAPITAL LETTER IOTA WITH MACRON;Lu;0;L;0399 0304;;;;N;;;;1FD1;
+1FDA;GREEK CAPITAL LETTER IOTA WITH VARIA;Lu;0;L;0399 0300;;;;N;;;;1F76;
+1FDB;GREEK CAPITAL LETTER IOTA WITH OXIA;Lu;0;L;038A;;;;N;;;;1F77;
+1FDD;GREEK DASIA AND VARIA;Sk;0;ON;1FFE 0300;;;;N;;;;;
+1FDE;GREEK DASIA AND OXIA;Sk;0;ON;1FFE 0301;;;;N;;;;;
+1FDF;GREEK DASIA AND PERISPOMENI;Sk;0;ON;1FFE 0342;;;;N;;;;;
+1FE0;GREEK SMALL LETTER UPSILON WITH VRACHY;Ll;0;L;03C5 0306;;;;N;;;1FE8;;1FE8
+1FE1;GREEK SMALL LETTER UPSILON WITH MACRON;Ll;0;L;03C5 0304;;;;N;;;1FE9;;1FE9
+1FE2;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA;Ll;0;L;03CB 0300;;;;N;;;;;
+1FE3;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA;Ll;0;L;03B0;;;;N;;;;;
+1FE4;GREEK SMALL LETTER RHO WITH PSILI;Ll;0;L;03C1 0313;;;;N;;;;;
+1FE5;GREEK SMALL LETTER RHO WITH DASIA;Ll;0;L;03C1 0314;;;;N;;;1FEC;;1FEC
+1FE6;GREEK SMALL LETTER UPSILON WITH PERISPOMENI;Ll;0;L;03C5 0342;;;;N;;;;;
+1FE7;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI;Ll;0;L;03CB 0342;;;;N;;;;;
+1FE8;GREEK CAPITAL LETTER UPSILON WITH VRACHY;Lu;0;L;03A5 0306;;;;N;;;;1FE0;
+1FE9;GREEK CAPITAL LETTER UPSILON WITH MACRON;Lu;0;L;03A5 0304;;;;N;;;;1FE1;
+1FEA;GREEK CAPITAL LETTER UPSILON WITH VARIA;Lu;0;L;03A5 0300;;;;N;;;;1F7A;
+1FEB;GREEK CAPITAL LETTER UPSILON WITH OXIA;Lu;0;L;038E;;;;N;;;;1F7B;
+1FEC;GREEK CAPITAL LETTER RHO WITH DASIA;Lu;0;L;03A1 0314;;;;N;;;;1FE5;
+1FED;GREEK DIALYTIKA AND VARIA;Sk;0;ON;00A8 0300;;;;N;;;;;
+1FEE;GREEK DIALYTIKA AND OXIA;Sk;0;ON;0385;;;;N;;;;;
+1FEF;GREEK VARIA;Sk;0;ON;0060;;;;N;;;;;
+1FF2;GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F7C 0345;;;;N;;;;;
+1FF3;GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI;Ll;0;L;03C9 0345;;;;N;;;1FFC;;1FFC
+1FF4;GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03CE 0345;;;;N;;;;;
+1FF6;GREEK SMALL LETTER OMEGA WITH PERISPOMENI;Ll;0;L;03C9 0342;;;;N;;;;;
+1FF7;GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FF6 0345;;;;N;;;;;
+1FF8;GREEK CAPITAL LETTER OMICRON WITH VARIA;Lu;0;L;039F 0300;;;;N;;;;1F78;
+1FF9;GREEK CAPITAL LETTER OMICRON WITH OXIA;Lu;0;L;038C;;;;N;;;;1F79;
+1FFA;GREEK CAPITAL LETTER OMEGA WITH VARIA;Lu;0;L;03A9 0300;;;;N;;;;1F7C;
+1FFB;GREEK CAPITAL LETTER OMEGA WITH OXIA;Lu;0;L;038F;;;;N;;;;1F7D;
+1FFC;GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI;Lt;0;L;03A9 0345;;;;N;;;;1FF3;
+1FFD;GREEK OXIA;Sk;0;ON;00B4;;;;N;;;;;
+1FFE;GREEK DASIA;Sk;0;ON;<compat> 0020 0314;;;;N;;;;;
+2000;EN QUAD;Zs;0;WS;2002;;;;N;;;;;
+2001;EM QUAD;Zs;0;WS;2003;;;;N;;;;;
+2002;EN SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2003;EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2004;THREE-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2005;FOUR-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2006;SIX-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2007;FIGURE SPACE;Zs;0;WS;<noBreak> 0020;;;;N;;;;;
+2008;PUNCTUATION SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2009;THIN SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+200A;HAIR SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+200B;ZERO WIDTH SPACE;Cf;0;BN;;;;;N;;;;;
+200C;ZERO WIDTH NON-JOINER;Cf;0;BN;;;;;N;;;;;
+200D;ZERO WIDTH JOINER;Cf;0;BN;;;;;N;;;;;
+200E;LEFT-TO-RIGHT MARK;Cf;0;L;;;;;N;;;;;
+200F;RIGHT-TO-LEFT MARK;Cf;0;R;;;;;N;;;;;
+2010;HYPHEN;Pd;0;ON;;;;;N;;;;;
+2011;NON-BREAKING HYPHEN;Pd;0;ON;<noBreak> 2010;;;;N;;;;;
+2012;FIGURE DASH;Pd;0;ON;;;;;N;;;;;
+2013;EN DASH;Pd;0;ON;;;;;N;;;;;
+2014;EM DASH;Pd;0;ON;;;;;N;;;;;
+2015;HORIZONTAL BAR;Pd;0;ON;;;;;N;QUOTATION DASH;;;;
+2016;DOUBLE VERTICAL LINE;Po;0;ON;;;;;N;DOUBLE VERTICAL BAR;;;;
+2017;DOUBLE LOW LINE;Po;0;ON;<compat> 0020 0333;;;;N;SPACING DOUBLE UNDERSCORE;;;;
+2018;LEFT SINGLE QUOTATION MARK;Pi;0;ON;;;;;N;SINGLE TURNED COMMA QUOTATION MARK;;;;
+2019;RIGHT SINGLE QUOTATION MARK;Pf;0;ON;;;;;N;SINGLE COMMA QUOTATION MARK;;;;
+201A;SINGLE LOW-9 QUOTATION MARK;Ps;0;ON;;;;;N;LOW SINGLE COMMA QUOTATION MARK;;;;
+201B;SINGLE HIGH-REVERSED-9 QUOTATION MARK;Pi;0;ON;;;;;N;SINGLE REVERSED COMMA QUOTATION MARK;;;;
+201C;LEFT DOUBLE QUOTATION MARK;Pi;0;ON;;;;;N;DOUBLE TURNED COMMA QUOTATION MARK;;;;
+201D;RIGHT DOUBLE QUOTATION MARK;Pf;0;ON;;;;;N;DOUBLE COMMA QUOTATION MARK;;;;
+201E;DOUBLE LOW-9 QUOTATION MARK;Ps;0;ON;;;;;N;LOW DOUBLE COMMA QUOTATION MARK;;;;
+201F;DOUBLE HIGH-REVERSED-9 QUOTATION MARK;Pi;0;ON;;;;;N;DOUBLE REVERSED COMMA QUOTATION MARK;;;;
+2020;DAGGER;Po;0;ON;;;;;N;;;;;
+2021;DOUBLE DAGGER;Po;0;ON;;;;;N;;;;;
+2022;BULLET;Po;0;ON;;;;;N;;;;;
+2023;TRIANGULAR BULLET;Po;0;ON;;;;;N;;;;;
+2024;ONE DOT LEADER;Po;0;ON;<compat> 002E;;;;N;;;;;
+2025;TWO DOT LEADER;Po;0;ON;<compat> 002E 002E;;;;N;;;;;
+2026;HORIZONTAL ELLIPSIS;Po;0;ON;<compat> 002E 002E 002E;;;;N;;;;;
+2027;HYPHENATION POINT;Po;0;ON;;;;;N;;;;;
+2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;;
+2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;;
+202A;LEFT-TO-RIGHT EMBEDDING;Cf;0;LRE;;;;;N;;;;;
+202B;RIGHT-TO-LEFT EMBEDDING;Cf;0;RLE;;;;;N;;;;;
+202C;POP DIRECTIONAL FORMATTING;Cf;0;PDF;;;;;N;;;;;
+202D;LEFT-TO-RIGHT OVERRIDE;Cf;0;LRO;;;;;N;;;;;
+202E;RIGHT-TO-LEFT OVERRIDE;Cf;0;RLO;;;;;N;;;;;
+202F;NARROW NO-BREAK SPACE;Zs;0;CS;<noBreak> 0020;;;;N;;;;;
+2030;PER MILLE SIGN;Po;0;ET;;;;;N;;;;;
+2031;PER TEN THOUSAND SIGN;Po;0;ET;;;;;N;;;;;
+2032;PRIME;Po;0;ET;;;;;N;;;;;
+2033;DOUBLE PRIME;Po;0;ET;<compat> 2032 2032;;;;N;;;;;
+2034;TRIPLE PRIME;Po;0;ET;<compat> 2032 2032 2032;;;;N;;;;;
+2035;REVERSED PRIME;Po;0;ON;;;;;N;;;;;
+2036;REVERSED DOUBLE PRIME;Po;0;ON;<compat> 2035 2035;;;;N;;;;;
+2037;REVERSED TRIPLE PRIME;Po;0;ON;<compat> 2035 2035 2035;;;;N;;;;;
+2038;CARET;Po;0;ON;;;;;N;;;;;
+2039;SINGLE LEFT-POINTING ANGLE QUOTATION MARK;Pi;0;ON;;;;;Y;LEFT POINTING SINGLE GUILLEMET;;;;
+203A;SINGLE RIGHT-POINTING ANGLE QUOTATION MARK;Pf;0;ON;;;;;Y;RIGHT POINTING SINGLE GUILLEMET;;;;
+203B;REFERENCE MARK;Po;0;ON;;;;;N;;;;;
+203C;DOUBLE EXCLAMATION MARK;Po;0;ON;<compat> 0021 0021;;;;N;;;;;
+203D;INTERROBANG;Po;0;ON;;;;;N;;;;;
+203E;OVERLINE;Po;0;ON;<compat> 0020 0305;;;;N;SPACING OVERSCORE;;;;
+203F;UNDERTIE;Pc;0;ON;;;;;N;;;;;
+2040;CHARACTER TIE;Pc;0;ON;;;;;N;;;;;
+2041;CARET INSERTION POINT;Po;0;ON;;;;;N;;;;;
+2042;ASTERISM;Po;0;ON;;;;;N;;;;;
+2043;HYPHEN BULLET;Po;0;ON;;;;;N;;;;;
+2044;FRACTION SLASH;Sm;0;CS;;;;;N;;;;;
+2045;LEFT SQUARE BRACKET WITH QUILL;Ps;0;ON;;;;;Y;;;;;
+2046;RIGHT SQUARE BRACKET WITH QUILL;Pe;0;ON;;;;;Y;;;;;
+2047;DOUBLE QUESTION MARK;Po;0;ON;<compat> 003F 003F;;;;N;;;;;
+2048;QUESTION EXCLAMATION MARK;Po;0;ON;<compat> 003F 0021;;;;N;;;;;
+2049;EXCLAMATION QUESTION MARK;Po;0;ON;<compat> 0021 003F;;;;N;;;;;
+204A;TIRONIAN SIGN ET;Po;0;ON;;;;;N;;;;;
+204B;REVERSED PILCROW SIGN;Po;0;ON;;;;;N;;;;;
+204C;BLACK LEFTWARDS BULLET;Po;0;ON;;;;;N;;;;;
+204D;BLACK RIGHTWARDS BULLET;Po;0;ON;;;;;N;;;;;
+204E;LOW ASTERISK;Po;0;ON;;;;;N;;;;;
+204F;REVERSED SEMICOLON;Po;0;ON;;;;;N;;;;;
+2050;CLOSE UP;Po;0;ON;;;;;N;;;;;
+2051;TWO ASTERISKS ALIGNED VERTICALLY;Po;0;ON;;;;;N;;;;;
+2052;COMMERCIAL MINUS SIGN;Sm;0;ON;;;;;N;;;;;
+2053;SWUNG DASH;Po;0;ON;;;;;N;;;;;
+2054;INVERTED UNDERTIE;Pc;0;ON;;;;;N;;;;;
+2055;FLOWER PUNCTUATION MARK;Po;0;ON;;;;;N;;;;;
+2056;THREE DOT PUNCTUATION;Po;0;ON;;;;;N;;;;;
+2057;QUADRUPLE PRIME;Po;0;ON;<compat> 2032 2032 2032 2032;;;;N;;;;;
+2058;FOUR DOT PUNCTUATION;Po;0;ON;;;;;N;;;;;
+2059;FIVE DOT PUNCTUATION;Po;0;ON;;;;;N;;;;;
+205A;TWO DOT PUNCTUATION;Po;0;ON;;;;;N;;;;;
+205B;FOUR DOT MARK;Po;0;ON;;;;;N;;;;;
+205C;DOTTED CROSS;Po;0;ON;;;;;N;;;;;
+205D;TRICOLON;Po;0;ON;;;;;N;;;;;
+205E;VERTICAL FOUR DOTS;Po;0;ON;;;;;N;;;;;
+205F;MEDIUM MATHEMATICAL SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2060;WORD JOINER;Cf;0;BN;;;;;N;;;;;
+2061;FUNCTION APPLICATION;Cf;0;BN;;;;;N;;;;;
+2062;INVISIBLE TIMES;Cf;0;BN;;;;;N;;;;;
+2063;INVISIBLE SEPARATOR;Cf;0;BN;;;;;N;;;;;
+2064;INVISIBLE PLUS;Cf;0;BN;;;;;N;;;;;
+2066;LEFT-TO-RIGHT ISOLATE;Cf;0;LRI;;;;;N;;;;;
+2067;RIGHT-TO-LEFT ISOLATE;Cf;0;RLI;;;;;N;;;;;
+2068;FIRST STRONG ISOLATE;Cf;0;FSI;;;;;N;;;;;
+2069;POP DIRECTIONAL ISOLATE;Cf;0;PDI;;;;;N;;;;;
+206A;INHIBIT SYMMETRIC SWAPPING;Cf;0;BN;;;;;N;;;;;
+206B;ACTIVATE SYMMETRIC SWAPPING;Cf;0;BN;;;;;N;;;;;
+206C;INHIBIT ARABIC FORM SHAPING;Cf;0;BN;;;;;N;;;;;
+206D;ACTIVATE ARABIC FORM SHAPING;Cf;0;BN;;;;;N;;;;;
+206E;NATIONAL DIGIT SHAPES;Cf;0;BN;;;;;N;;;;;
+206F;NOMINAL DIGIT SHAPES;Cf;0;BN;;;;;N;;;;;
+2070;SUPERSCRIPT ZERO;No;0;EN;<super> 0030;;0;0;N;SUPERSCRIPT DIGIT ZERO;;;;
+2071;SUPERSCRIPT LATIN SMALL LETTER I;Lm;0;L;<super> 0069;;;;N;;;;;
+2074;SUPERSCRIPT FOUR;No;0;EN;<super> 0034;;4;4;N;SUPERSCRIPT DIGIT FOUR;;;;
+2075;SUPERSCRIPT FIVE;No;0;EN;<super> 0035;;5;5;N;SUPERSCRIPT DIGIT FIVE;;;;
+2076;SUPERSCRIPT SIX;No;0;EN;<super> 0036;;6;6;N;SUPERSCRIPT DIGIT SIX;;;;
+2077;SUPERSCRIPT SEVEN;No;0;EN;<super> 0037;;7;7;N;SUPERSCRIPT DIGIT SEVEN;;;;
+2078;SUPERSCRIPT EIGHT;No;0;EN;<super> 0038;;8;8;N;SUPERSCRIPT DIGIT EIGHT;;;;
+2079;SUPERSCRIPT NINE;No;0;EN;<super> 0039;;9;9;N;SUPERSCRIPT DIGIT NINE;;;;
+207A;SUPERSCRIPT PLUS SIGN;Sm;0;ES;<super> 002B;;;;N;;;;;
+207B;SUPERSCRIPT MINUS;Sm;0;ES;<super> 2212;;;;N;SUPERSCRIPT HYPHEN-MINUS;;;;
+207C;SUPERSCRIPT EQUALS SIGN;Sm;0;ON;<super> 003D;;;;N;;;;;
+207D;SUPERSCRIPT LEFT PARENTHESIS;Ps;0;ON;<super> 0028;;;;Y;SUPERSCRIPT OPENING PARENTHESIS;;;;
+207E;SUPERSCRIPT RIGHT PARENTHESIS;Pe;0;ON;<super> 0029;;;;Y;SUPERSCRIPT CLOSING PARENTHESIS;;;;
+207F;SUPERSCRIPT LATIN SMALL LETTER N;Lm;0;L;<super> 006E;;;;N;;;;;
+2080;SUBSCRIPT ZERO;No;0;EN;<sub> 0030;;0;0;N;SUBSCRIPT DIGIT ZERO;;;;
+2081;SUBSCRIPT ONE;No;0;EN;<sub> 0031;;1;1;N;SUBSCRIPT DIGIT ONE;;;;
+2082;SUBSCRIPT TWO;No;0;EN;<sub> 0032;;2;2;N;SUBSCRIPT DIGIT TWO;;;;
+2083;SUBSCRIPT THREE;No;0;EN;<sub> 0033;;3;3;N;SUBSCRIPT DIGIT THREE;;;;
+2084;SUBSCRIPT FOUR;No;0;EN;<sub> 0034;;4;4;N;SUBSCRIPT DIGIT FOUR;;;;
+2085;SUBSCRIPT FIVE;No;0;EN;<sub> 0035;;5;5;N;SUBSCRIPT DIGIT FIVE;;;;
+2086;SUBSCRIPT SIX;No;0;EN;<sub> 0036;;6;6;N;SUBSCRIPT DIGIT SIX;;;;
+2087;SUBSCRIPT SEVEN;No;0;EN;<sub> 0037;;7;7;N;SUBSCRIPT DIGIT SEVEN;;;;
+2088;SUBSCRIPT EIGHT;No;0;EN;<sub> 0038;;8;8;N;SUBSCRIPT DIGIT EIGHT;;;;
+2089;SUBSCRIPT NINE;No;0;EN;<sub> 0039;;9;9;N;SUBSCRIPT DIGIT NINE;;;;
+208A;SUBSCRIPT PLUS SIGN;Sm;0;ES;<sub> 002B;;;;N;;;;;
+208B;SUBSCRIPT MINUS;Sm;0;ES;<sub> 2212;;;;N;SUBSCRIPT HYPHEN-MINUS;;;;
+208C;SUBSCRIPT EQUALS SIGN;Sm;0;ON;<sub> 003D;;;;N;;;;;
+208D;SUBSCRIPT LEFT PARENTHESIS;Ps;0;ON;<sub> 0028;;;;Y;SUBSCRIPT OPENING PARENTHESIS;;;;
+208E;SUBSCRIPT RIGHT PARENTHESIS;Pe;0;ON;<sub> 0029;;;;Y;SUBSCRIPT CLOSING PARENTHESIS;;;;
+2090;LATIN SUBSCRIPT SMALL LETTER A;Lm;0;L;<sub> 0061;;;;N;;;;;
+2091;LATIN SUBSCRIPT SMALL LETTER E;Lm;0;L;<sub> 0065;;;;N;;;;;
+2092;LATIN SUBSCRIPT SMALL LETTER O;Lm;0;L;<sub> 006F;;;;N;;;;;
+2093;LATIN SUBSCRIPT SMALL LETTER X;Lm;0;L;<sub> 0078;;;;N;;;;;
+2094;LATIN SUBSCRIPT SMALL LETTER SCHWA;Lm;0;L;<sub> 0259;;;;N;;;;;
+2095;LATIN SUBSCRIPT SMALL LETTER H;Lm;0;L;<sub> 0068;;;;N;;;;;
+2096;LATIN SUBSCRIPT SMALL LETTER K;Lm;0;L;<sub> 006B;;;;N;;;;;
+2097;LATIN SUBSCRIPT SMALL LETTER L;Lm;0;L;<sub> 006C;;;;N;;;;;
+2098;LATIN SUBSCRIPT SMALL LETTER M;Lm;0;L;<sub> 006D;;;;N;;;;;
+2099;LATIN SUBSCRIPT SMALL LETTER N;Lm;0;L;<sub> 006E;;;;N;;;;;
+209A;LATIN SUBSCRIPT SMALL LETTER P;Lm;0;L;<sub> 0070;;;;N;;;;;
+209B;LATIN SUBSCRIPT SMALL LETTER S;Lm;0;L;<sub> 0073;;;;N;;;;;
+209C;LATIN SUBSCRIPT SMALL LETTER T;Lm;0;L;<sub> 0074;;;;N;;;;;
+20A0;EURO-CURRENCY SIGN;Sc;0;ET;;;;;N;;;;;
+20A1;COLON SIGN;Sc;0;ET;;;;;N;;;;;
+20A2;CRUZEIRO SIGN;Sc;0;ET;;;;;N;;;;;
+20A3;FRENCH FRANC SIGN;Sc;0;ET;;;;;N;;;;;
+20A4;LIRA SIGN;Sc;0;ET;;;;;N;;;;;
+20A5;MILL SIGN;Sc;0;ET;;;;;N;;;;;
+20A6;NAIRA SIGN;Sc;0;ET;;;;;N;;;;;
+20A7;PESETA SIGN;Sc;0;ET;;;;;N;;;;;
+20A8;RUPEE SIGN;Sc;0;ET;<compat> 0052 0073;;;;N;;;;;
+20A9;WON SIGN;Sc;0;ET;;;;;N;;;;;
+20AA;NEW SHEQEL SIGN;Sc;0;ET;;;;;N;;;;;
+20AB;DONG SIGN;Sc;0;ET;;;;;N;;;;;
+20AC;EURO SIGN;Sc;0;ET;;;;;N;;;;;
+20AD;KIP SIGN;Sc;0;ET;;;;;N;;;;;
+20AE;TUGRIK SIGN;Sc;0;ET;;;;;N;;;;;
+20AF;DRACHMA SIGN;Sc;0;ET;;;;;N;;;;;
+20B0;GERMAN PENNY SIGN;Sc;0;ET;;;;;N;;;;;
+20B1;PESO SIGN;Sc;0;ET;;;;;N;;;;;
+20B2;GUARANI SIGN;Sc;0;ET;;;;;N;;;;;
+20B3;AUSTRAL SIGN;Sc;0;ET;;;;;N;;;;;
+20B4;HRYVNIA SIGN;Sc;0;ET;;;;;N;;;;;
+20B5;CEDI SIGN;Sc;0;ET;;;;;N;;;;;
+20B6;LIVRE TOURNOIS SIGN;Sc;0;ET;;;;;N;;;;;
+20B7;SPESMILO SIGN;Sc;0;ET;;;;;N;;;;;
+20B8;TENGE SIGN;Sc;0;ET;;;;;N;;;;;
+20B9;INDIAN RUPEE SIGN;Sc;0;ET;;;;;N;;;;;
+20BA;TURKISH LIRA SIGN;Sc;0;ET;;;;;N;;;;;
+20BB;NORDIC MARK SIGN;Sc;0;ET;;;;;N;;;;;
+20BC;MANAT SIGN;Sc;0;ET;;;;;N;;;;;
+20BD;RUBLE SIGN;Sc;0;ET;;;;;N;;;;;
+20BE;LARI SIGN;Sc;0;ET;;;;;N;;;;;
+20BF;BITCOIN SIGN;Sc;0;ET;;;;;N;;;;;
+20D0;COMBINING LEFT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT HARPOON ABOVE;;;;
+20D1;COMBINING RIGHT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT HARPOON ABOVE;;;;
+20D2;COMBINING LONG VERTICAL LINE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG VERTICAL BAR OVERLAY;;;;
+20D3;COMBINING SHORT VERTICAL LINE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT VERTICAL BAR OVERLAY;;;;
+20D4;COMBINING ANTICLOCKWISE ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING ANTICLOCKWISE ARROW ABOVE;;;;
+20D5;COMBINING CLOCKWISE ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING CLOCKWISE ARROW ABOVE;;;;
+20D6;COMBINING LEFT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT ARROW ABOVE;;;;
+20D7;COMBINING RIGHT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT ARROW ABOVE;;;;
+20D8;COMBINING RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING RING OVERLAY;;;;
+20D9;COMBINING CLOCKWISE RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING CLOCKWISE RING OVERLAY;;;;
+20DA;COMBINING ANTICLOCKWISE RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING ANTICLOCKWISE RING OVERLAY;;;;
+20DB;COMBINING THREE DOTS ABOVE;Mn;230;NSM;;;;;N;NON-SPACING THREE DOTS ABOVE;;;;
+20DC;COMBINING FOUR DOTS ABOVE;Mn;230;NSM;;;;;N;NON-SPACING FOUR DOTS ABOVE;;;;
+20DD;COMBINING ENCLOSING CIRCLE;Me;0;NSM;;;;;N;ENCLOSING CIRCLE;;;;
+20DE;COMBINING ENCLOSING SQUARE;Me;0;NSM;;;;;N;ENCLOSING SQUARE;;;;
+20DF;COMBINING ENCLOSING DIAMOND;Me;0;NSM;;;;;N;ENCLOSING DIAMOND;;;;
+20E0;COMBINING ENCLOSING CIRCLE BACKSLASH;Me;0;NSM;;;;;N;ENCLOSING CIRCLE SLASH;;;;
+20E1;COMBINING LEFT RIGHT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT RIGHT ARROW ABOVE;;;;
+20E2;COMBINING ENCLOSING SCREEN;Me;0;NSM;;;;;N;;;;;
+20E3;COMBINING ENCLOSING KEYCAP;Me;0;NSM;;;;;N;;;;;
+20E4;COMBINING ENCLOSING UPWARD POINTING TRIANGLE;Me;0;NSM;;;;;N;;;;;
+20E5;COMBINING REVERSE SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;;;;;
+20E6;COMBINING DOUBLE VERTICAL STROKE OVERLAY;Mn;1;NSM;;;;;N;;;;;
+20E7;COMBINING ANNUITY SYMBOL;Mn;230;NSM;;;;;N;;;;;
+20E8;COMBINING TRIPLE UNDERDOT;Mn;220;NSM;;;;;N;;;;;
+20E9;COMBINING WIDE BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;;
+20EA;COMBINING LEFTWARDS ARROW OVERLAY;Mn;1;NSM;;;;;N;;;;;
+20EB;COMBINING LONG DOUBLE SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;;;;;
+20EC;COMBINING RIGHTWARDS HARPOON WITH BARB DOWNWARDS;Mn;220;NSM;;;;;N;;;;;
+20ED;COMBINING LEFTWARDS HARPOON WITH BARB DOWNWARDS;Mn;220;NSM;;;;;N;;;;;
+20EE;COMBINING LEFT ARROW BELOW;Mn;220;NSM;;;;;N;;;;;
+20EF;COMBINING RIGHT ARROW BELOW;Mn;220;NSM;;;;;N;;;;;
+20F0;COMBINING ASTERISK ABOVE;Mn;230;NSM;;;;;N;;;;;
+2100;ACCOUNT OF;So;0;ON;<compat> 0061 002F 0063;;;;N;;;;;
+2101;ADDRESSED TO THE SUBJECT;So;0;ON;<compat> 0061 002F 0073;;;;N;;;;;
+2102;DOUBLE-STRUCK CAPITAL C;Lu;0;L;<font> 0043;;;;N;DOUBLE-STRUCK C;;;;
+2103;DEGREE CELSIUS;So;0;ON;<compat> 00B0 0043;;;;N;DEGREES CENTIGRADE;;;;
+2104;CENTRE LINE SYMBOL;So;0;ON;;;;;N;C L SYMBOL;;;;
+2105;CARE OF;So;0;ON;<compat> 0063 002F 006F;;;;N;;;;;
+2106;CADA UNA;So;0;ON;<compat> 0063 002F 0075;;;;N;;;;;
+2107;EULER CONSTANT;Lu;0;L;<compat> 0190;;;;N;EULERS;;;;
+2108;SCRUPLE;So;0;ON;;;;;N;;;;;
+2109;DEGREE FAHRENHEIT;So;0;ON;<compat> 00B0 0046;;;;N;DEGREES FAHRENHEIT;;;;
+210A;SCRIPT SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+210B;SCRIPT CAPITAL H;Lu;0;L;<font> 0048;;;;N;SCRIPT H;;;;
+210C;BLACK-LETTER CAPITAL H;Lu;0;L;<font> 0048;;;;N;BLACK-LETTER H;;;;
+210D;DOUBLE-STRUCK CAPITAL H;Lu;0;L;<font> 0048;;;;N;DOUBLE-STRUCK H;;;;
+210E;PLANCK CONSTANT;Ll;0;L;<font> 0068;;;;N;;;;;
+210F;PLANCK CONSTANT OVER TWO PI;Ll;0;L;<font> 0127;;;;N;PLANCK CONSTANT OVER 2 PI;;;;
+2110;SCRIPT CAPITAL I;Lu;0;L;<font> 0049;;;;N;SCRIPT I;;;;
+2111;BLACK-LETTER CAPITAL I;Lu;0;L;<font> 0049;;;;N;BLACK-LETTER I;;;;
+2112;SCRIPT CAPITAL L;Lu;0;L;<font> 004C;;;;N;SCRIPT L;;;;
+2113;SCRIPT SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+2114;L B BAR SYMBOL;So;0;ON;;;;;N;;;;;
+2115;DOUBLE-STRUCK CAPITAL N;Lu;0;L;<font> 004E;;;;N;DOUBLE-STRUCK N;;;;
+2116;NUMERO SIGN;So;0;ON;<compat> 004E 006F;;;;N;NUMERO;;;;
+2117;SOUND RECORDING COPYRIGHT;So;0;ON;;;;;N;;;;;
+2118;SCRIPT CAPITAL P;Sm;0;ON;;;;;N;SCRIPT P;;;;
+2119;DOUBLE-STRUCK CAPITAL P;Lu;0;L;<font> 0050;;;;N;DOUBLE-STRUCK P;;;;
+211A;DOUBLE-STRUCK CAPITAL Q;Lu;0;L;<font> 0051;;;;N;DOUBLE-STRUCK Q;;;;
+211B;SCRIPT CAPITAL R;Lu;0;L;<font> 0052;;;;N;SCRIPT R;;;;
+211C;BLACK-LETTER CAPITAL R;Lu;0;L;<font> 0052;;;;N;BLACK-LETTER R;;;;
+211D;DOUBLE-STRUCK CAPITAL R;Lu;0;L;<font> 0052;;;;N;DOUBLE-STRUCK R;;;;
+211E;PRESCRIPTION TAKE;So;0;ON;;;;;N;;;;;
+211F;RESPONSE;So;0;ON;;;;;N;;;;;
+2120;SERVICE MARK;So;0;ON;<super> 0053 004D;;;;N;;;;;
+2121;TELEPHONE SIGN;So;0;ON;<compat> 0054 0045 004C;;;;N;T E L SYMBOL;;;;
+2122;TRADE MARK SIGN;So;0;ON;<super> 0054 004D;;;;N;TRADEMARK;;;;
+2123;VERSICLE;So;0;ON;;;;;N;;;;;
+2124;DOUBLE-STRUCK CAPITAL Z;Lu;0;L;<font> 005A;;;;N;DOUBLE-STRUCK Z;;;;
+2125;OUNCE SIGN;So;0;ON;;;;;N;OUNCE;;;;
+2126;OHM SIGN;Lu;0;L;03A9;;;;N;OHM;;;03C9;
+2127;INVERTED OHM SIGN;So;0;ON;;;;;N;MHO;;;;
+2128;BLACK-LETTER CAPITAL Z;Lu;0;L;<font> 005A;;;;N;BLACK-LETTER Z;;;;
+2129;TURNED GREEK SMALL LETTER IOTA;So;0;ON;;;;;N;;;;;
+212A;KELVIN SIGN;Lu;0;L;004B;;;;N;DEGREES KELVIN;;;006B;
+212B;ANGSTROM SIGN;Lu;0;L;00C5;;;;N;ANGSTROM UNIT;;;00E5;
+212C;SCRIPT CAPITAL B;Lu;0;L;<font> 0042;;;;N;SCRIPT B;;;;
+212D;BLACK-LETTER CAPITAL C;Lu;0;L;<font> 0043;;;;N;BLACK-LETTER C;;;;
+212E;ESTIMATED SYMBOL;So;0;ET;;;;;N;;;;;
+212F;SCRIPT SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+2130;SCRIPT CAPITAL E;Lu;0;L;<font> 0045;;;;N;SCRIPT E;;;;
+2131;SCRIPT CAPITAL F;Lu;0;L;<font> 0046;;;;N;SCRIPT F;;;;
+2132;TURNED CAPITAL F;Lu;0;L;;;;;N;TURNED F;;;214E;
+2133;SCRIPT CAPITAL M;Lu;0;L;<font> 004D;;;;N;SCRIPT M;;;;
+2134;SCRIPT SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+2135;ALEF SYMBOL;Lo;0;L;<compat> 05D0;;;;N;FIRST TRANSFINITE CARDINAL;;;;
+2136;BET SYMBOL;Lo;0;L;<compat> 05D1;;;;N;SECOND TRANSFINITE CARDINAL;;;;
+2137;GIMEL SYMBOL;Lo;0;L;<compat> 05D2;;;;N;THIRD TRANSFINITE CARDINAL;;;;
+2138;DALET SYMBOL;Lo;0;L;<compat> 05D3;;;;N;FOURTH TRANSFINITE CARDINAL;;;;
+2139;INFORMATION SOURCE;Ll;0;L;<font> 0069;;;;N;;;;;
+213A;ROTATED CAPITAL Q;So;0;ON;;;;;N;;;;;
+213B;FACSIMILE SIGN;So;0;ON;<compat> 0046 0041 0058;;;;N;;;;;
+213C;DOUBLE-STRUCK SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+213D;DOUBLE-STRUCK SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+213E;DOUBLE-STRUCK CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+213F;DOUBLE-STRUCK CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+2140;DOUBLE-STRUCK N-ARY SUMMATION;Sm;0;ON;<font> 2211;;;;Y;;;;;
+2141;TURNED SANS-SERIF CAPITAL G;Sm;0;ON;;;;;N;;;;;
+2142;TURNED SANS-SERIF CAPITAL L;Sm;0;ON;;;;;N;;;;;
+2143;REVERSED SANS-SERIF CAPITAL L;Sm;0;ON;;;;;N;;;;;
+2144;TURNED SANS-SERIF CAPITAL Y;Sm;0;ON;;;;;N;;;;;
+2145;DOUBLE-STRUCK ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+2146;DOUBLE-STRUCK ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+2147;DOUBLE-STRUCK ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+2148;DOUBLE-STRUCK ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+2149;DOUBLE-STRUCK ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+214A;PROPERTY LINE;So;0;ON;;;;;N;;;;;
+214B;TURNED AMPERSAND;Sm;0;ON;;;;;N;;;;;
+214C;PER SIGN;So;0;ON;;;;;N;;;;;
+214D;AKTIESELSKAB;So;0;ON;;;;;N;;;;;
+214E;TURNED SMALL F;Ll;0;L;;;;;N;;;2132;;2132
+214F;SYMBOL FOR SAMARITAN SOURCE;So;0;L;;;;;N;;;;;
+2150;VULGAR FRACTION ONE SEVENTH;No;0;ON;<fraction> 0031 2044 0037;;;1/7;N;;;;;
+2151;VULGAR FRACTION ONE NINTH;No;0;ON;<fraction> 0031 2044 0039;;;1/9;N;;;;;
+2152;VULGAR FRACTION ONE TENTH;No;0;ON;<fraction> 0031 2044 0031 0030;;;1/10;N;;;;;
+2153;VULGAR FRACTION ONE THIRD;No;0;ON;<fraction> 0031 2044 0033;;;1/3;N;FRACTION ONE THIRD;;;;
+2154;VULGAR FRACTION TWO THIRDS;No;0;ON;<fraction> 0032 2044 0033;;;2/3;N;FRACTION TWO THIRDS;;;;
+2155;VULGAR FRACTION ONE FIFTH;No;0;ON;<fraction> 0031 2044 0035;;;1/5;N;FRACTION ONE FIFTH;;;;
+2156;VULGAR FRACTION TWO FIFTHS;No;0;ON;<fraction> 0032 2044 0035;;;2/5;N;FRACTION TWO FIFTHS;;;;
+2157;VULGAR FRACTION THREE FIFTHS;No;0;ON;<fraction> 0033 2044 0035;;;3/5;N;FRACTION THREE FIFTHS;;;;
+2158;VULGAR FRACTION FOUR FIFTHS;No;0;ON;<fraction> 0034 2044 0035;;;4/5;N;FRACTION FOUR FIFTHS;;;;
+2159;VULGAR FRACTION ONE SIXTH;No;0;ON;<fraction> 0031 2044 0036;;;1/6;N;FRACTION ONE SIXTH;;;;
+215A;VULGAR FRACTION FIVE SIXTHS;No;0;ON;<fraction> 0035 2044 0036;;;5/6;N;FRACTION FIVE SIXTHS;;;;
+215B;VULGAR FRACTION ONE EIGHTH;No;0;ON;<fraction> 0031 2044 0038;;;1/8;N;FRACTION ONE EIGHTH;;;;
+215C;VULGAR FRACTION THREE EIGHTHS;No;0;ON;<fraction> 0033 2044 0038;;;3/8;N;FRACTION THREE EIGHTHS;;;;
+215D;VULGAR FRACTION FIVE EIGHTHS;No;0;ON;<fraction> 0035 2044 0038;;;5/8;N;FRACTION FIVE EIGHTHS;;;;
+215E;VULGAR FRACTION SEVEN EIGHTHS;No;0;ON;<fraction> 0037 2044 0038;;;7/8;N;FRACTION SEVEN EIGHTHS;;;;
+215F;FRACTION NUMERATOR ONE;No;0;ON;<fraction> 0031 2044;;;1;N;;;;;
+2160;ROMAN NUMERAL ONE;Nl;0;L;<compat> 0049;;;1;N;;;;2170;
+2161;ROMAN NUMERAL TWO;Nl;0;L;<compat> 0049 0049;;;2;N;;;;2171;
+2162;ROMAN NUMERAL THREE;Nl;0;L;<compat> 0049 0049 0049;;;3;N;;;;2172;
+2163;ROMAN NUMERAL FOUR;Nl;0;L;<compat> 0049 0056;;;4;N;;;;2173;
+2164;ROMAN NUMERAL FIVE;Nl;0;L;<compat> 0056;;;5;N;;;;2174;
+2165;ROMAN NUMERAL SIX;Nl;0;L;<compat> 0056 0049;;;6;N;;;;2175;
+2166;ROMAN NUMERAL SEVEN;Nl;0;L;<compat> 0056 0049 0049;;;7;N;;;;2176;
+2167;ROMAN NUMERAL EIGHT;Nl;0;L;<compat> 0056 0049 0049 0049;;;8;N;;;;2177;
+2168;ROMAN NUMERAL NINE;Nl;0;L;<compat> 0049 0058;;;9;N;;;;2178;
+2169;ROMAN NUMERAL TEN;Nl;0;L;<compat> 0058;;;10;N;;;;2179;
+216A;ROMAN NUMERAL ELEVEN;Nl;0;L;<compat> 0058 0049;;;11;N;;;;217A;
+216B;ROMAN NUMERAL TWELVE;Nl;0;L;<compat> 0058 0049 0049;;;12;N;;;;217B;
+216C;ROMAN NUMERAL FIFTY;Nl;0;L;<compat> 004C;;;50;N;;;;217C;
+216D;ROMAN NUMERAL ONE HUNDRED;Nl;0;L;<compat> 0043;;;100;N;;;;217D;
+216E;ROMAN NUMERAL FIVE HUNDRED;Nl;0;L;<compat> 0044;;;500;N;;;;217E;
+216F;ROMAN NUMERAL ONE THOUSAND;Nl;0;L;<compat> 004D;;;1000;N;;;;217F;
+2170;SMALL ROMAN NUMERAL ONE;Nl;0;L;<compat> 0069;;;1;N;;;2160;;2160
+2171;SMALL ROMAN NUMERAL TWO;Nl;0;L;<compat> 0069 0069;;;2;N;;;2161;;2161
+2172;SMALL ROMAN NUMERAL THREE;Nl;0;L;<compat> 0069 0069 0069;;;3;N;;;2162;;2162
+2173;SMALL ROMAN NUMERAL FOUR;Nl;0;L;<compat> 0069 0076;;;4;N;;;2163;;2163
+2174;SMALL ROMAN NUMERAL FIVE;Nl;0;L;<compat> 0076;;;5;N;;;2164;;2164
+2175;SMALL ROMAN NUMERAL SIX;Nl;0;L;<compat> 0076 0069;;;6;N;;;2165;;2165
+2176;SMALL ROMAN NUMERAL SEVEN;Nl;0;L;<compat> 0076 0069 0069;;;7;N;;;2166;;2166
+2177;SMALL ROMAN NUMERAL EIGHT;Nl;0;L;<compat> 0076 0069 0069 0069;;;8;N;;;2167;;2167
+2178;SMALL ROMAN NUMERAL NINE;Nl;0;L;<compat> 0069 0078;;;9;N;;;2168;;2168
+2179;SMALL ROMAN NUMERAL TEN;Nl;0;L;<compat> 0078;;;10;N;;;2169;;2169
+217A;SMALL ROMAN NUMERAL ELEVEN;Nl;0;L;<compat> 0078 0069;;;11;N;;;216A;;216A
+217B;SMALL ROMAN NUMERAL TWELVE;Nl;0;L;<compat> 0078 0069 0069;;;12;N;;;216B;;216B
+217C;SMALL ROMAN NUMERAL FIFTY;Nl;0;L;<compat> 006C;;;50;N;;;216C;;216C
+217D;SMALL ROMAN NUMERAL ONE HUNDRED;Nl;0;L;<compat> 0063;;;100;N;;;216D;;216D
+217E;SMALL ROMAN NUMERAL FIVE HUNDRED;Nl;0;L;<compat> 0064;;;500;N;;;216E;;216E
+217F;SMALL ROMAN NUMERAL ONE THOUSAND;Nl;0;L;<compat> 006D;;;1000;N;;;216F;;216F
+2180;ROMAN NUMERAL ONE THOUSAND C D;Nl;0;L;;;;1000;N;;;;;
+2181;ROMAN NUMERAL FIVE THOUSAND;Nl;0;L;;;;5000;N;;;;;
+2182;ROMAN NUMERAL TEN THOUSAND;Nl;0;L;;;;10000;N;;;;;
+2183;ROMAN NUMERAL REVERSED ONE HUNDRED;Lu;0;L;;;;;N;;;;2184;
+2184;LATIN SMALL LETTER REVERSED C;Ll;0;L;;;;;N;;;2183;;2183
+2185;ROMAN NUMERAL SIX LATE FORM;Nl;0;L;;;;6;N;;;;;
+2186;ROMAN NUMERAL FIFTY EARLY FORM;Nl;0;L;;;;50;N;;;;;
+2187;ROMAN NUMERAL FIFTY THOUSAND;Nl;0;L;;;;50000;N;;;;;
+2188;ROMAN NUMERAL ONE HUNDRED THOUSAND;Nl;0;L;;;;100000;N;;;;;
+2189;VULGAR FRACTION ZERO THIRDS;No;0;ON;<fraction> 0030 2044 0033;;;0;N;;;;;
+218A;TURNED DIGIT TWO;So;0;ON;;;;;N;;;;;
+218B;TURNED DIGIT THREE;So;0;ON;;;;;N;;;;;
+2190;LEFTWARDS ARROW;Sm;0;ON;;;;;N;LEFT ARROW;;;;
+2191;UPWARDS ARROW;Sm;0;ON;;;;;N;UP ARROW;;;;
+2192;RIGHTWARDS ARROW;Sm;0;ON;;;;;N;RIGHT ARROW;;;;
+2193;DOWNWARDS ARROW;Sm;0;ON;;;;;N;DOWN ARROW;;;;
+2194;LEFT RIGHT ARROW;Sm;0;ON;;;;;N;;;;;
+2195;UP DOWN ARROW;So;0;ON;;;;;N;;;;;
+2196;NORTH WEST ARROW;So;0;ON;;;;;N;UPPER LEFT ARROW;;;;
+2197;NORTH EAST ARROW;So;0;ON;;;;;N;UPPER RIGHT ARROW;;;;
+2198;SOUTH EAST ARROW;So;0;ON;;;;;N;LOWER RIGHT ARROW;;;;
+2199;SOUTH WEST ARROW;So;0;ON;;;;;N;LOWER LEFT ARROW;;;;
+219A;LEFTWARDS ARROW WITH STROKE;Sm;0;ON;2190 0338;;;;N;LEFT ARROW WITH STROKE;;;;
+219B;RIGHTWARDS ARROW WITH STROKE;Sm;0;ON;2192 0338;;;;N;RIGHT ARROW WITH STROKE;;;;
+219C;LEFTWARDS WAVE ARROW;So;0;ON;;;;;N;LEFT WAVE ARROW;;;;
+219D;RIGHTWARDS WAVE ARROW;So;0;ON;;;;;N;RIGHT WAVE ARROW;;;;
+219E;LEFTWARDS TWO HEADED ARROW;So;0;ON;;;;;N;LEFT TWO HEADED ARROW;;;;
+219F;UPWARDS TWO HEADED ARROW;So;0;ON;;;;;N;UP TWO HEADED ARROW;;;;
+21A0;RIGHTWARDS TWO HEADED ARROW;Sm;0;ON;;;;;N;RIGHT TWO HEADED ARROW;;;;
+21A1;DOWNWARDS TWO HEADED ARROW;So;0;ON;;;;;N;DOWN TWO HEADED ARROW;;;;
+21A2;LEFTWARDS ARROW WITH TAIL;So;0;ON;;;;;N;LEFT ARROW WITH TAIL;;;;
+21A3;RIGHTWARDS ARROW WITH TAIL;Sm;0;ON;;;;;N;RIGHT ARROW WITH TAIL;;;;
+21A4;LEFTWARDS ARROW FROM BAR;So;0;ON;;;;;N;LEFT ARROW FROM BAR;;;;
+21A5;UPWARDS ARROW FROM BAR;So;0;ON;;;;;N;UP ARROW FROM BAR;;;;
+21A6;RIGHTWARDS ARROW FROM BAR;Sm;0;ON;;;;;N;RIGHT ARROW FROM BAR;;;;
+21A7;DOWNWARDS ARROW FROM BAR;So;0;ON;;;;;N;DOWN ARROW FROM BAR;;;;
+21A8;UP DOWN ARROW WITH BASE;So;0;ON;;;;;N;;;;;
+21A9;LEFTWARDS ARROW WITH HOOK;So;0;ON;;;;;N;LEFT ARROW WITH HOOK;;;;
+21AA;RIGHTWARDS ARROW WITH HOOK;So;0;ON;;;;;N;RIGHT ARROW WITH HOOK;;;;
+21AB;LEFTWARDS ARROW WITH LOOP;So;0;ON;;;;;N;LEFT ARROW WITH LOOP;;;;
+21AC;RIGHTWARDS ARROW WITH LOOP;So;0;ON;;;;;N;RIGHT ARROW WITH LOOP;;;;
+21AD;LEFT RIGHT WAVE ARROW;So;0;ON;;;;;N;;;;;
+21AE;LEFT RIGHT ARROW WITH STROKE;Sm;0;ON;2194 0338;;;;N;;;;;
+21AF;DOWNWARDS ZIGZAG ARROW;So;0;ON;;;;;N;DOWN ZIGZAG ARROW;;;;
+21B0;UPWARDS ARROW WITH TIP LEFTWARDS;So;0;ON;;;;;N;UP ARROW WITH TIP LEFT;;;;
+21B1;UPWARDS ARROW WITH TIP RIGHTWARDS;So;0;ON;;;;;N;UP ARROW WITH TIP RIGHT;;;;
+21B2;DOWNWARDS ARROW WITH TIP LEFTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH TIP LEFT;;;;
+21B3;DOWNWARDS ARROW WITH TIP RIGHTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH TIP RIGHT;;;;
+21B4;RIGHTWARDS ARROW WITH CORNER DOWNWARDS;So;0;ON;;;;;N;RIGHT ARROW WITH CORNER DOWN;;;;
+21B5;DOWNWARDS ARROW WITH CORNER LEFTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH CORNER LEFT;;;;
+21B6;ANTICLOCKWISE TOP SEMICIRCLE ARROW;So;0;ON;;;;;N;;;;;
+21B7;CLOCKWISE TOP SEMICIRCLE ARROW;So;0;ON;;;;;N;;;;;
+21B8;NORTH WEST ARROW TO LONG BAR;So;0;ON;;;;;N;UPPER LEFT ARROW TO LONG BAR;;;;
+21B9;LEFTWARDS ARROW TO BAR OVER RIGHTWARDS ARROW TO BAR;So;0;ON;;;;;N;LEFT ARROW TO BAR OVER RIGHT ARROW TO BAR;;;;
+21BA;ANTICLOCKWISE OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;;
+21BB;CLOCKWISE OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;;
+21BC;LEFTWARDS HARPOON WITH BARB UPWARDS;So;0;ON;;;;;N;LEFT HARPOON WITH BARB UP;;;;
+21BD;LEFTWARDS HARPOON WITH BARB DOWNWARDS;So;0;ON;;;;;N;LEFT HARPOON WITH BARB DOWN;;;;
+21BE;UPWARDS HARPOON WITH BARB RIGHTWARDS;So;0;ON;;;;;N;UP HARPOON WITH BARB RIGHT;;;;
+21BF;UPWARDS HARPOON WITH BARB LEFTWARDS;So;0;ON;;;;;N;UP HARPOON WITH BARB LEFT;;;;
+21C0;RIGHTWARDS HARPOON WITH BARB UPWARDS;So;0;ON;;;;;N;RIGHT HARPOON WITH BARB UP;;;;
+21C1;RIGHTWARDS HARPOON WITH BARB DOWNWARDS;So;0;ON;;;;;N;RIGHT HARPOON WITH BARB DOWN;;;;
+21C2;DOWNWARDS HARPOON WITH BARB RIGHTWARDS;So;0;ON;;;;;N;DOWN HARPOON WITH BARB RIGHT;;;;
+21C3;DOWNWARDS HARPOON WITH BARB LEFTWARDS;So;0;ON;;;;;N;DOWN HARPOON WITH BARB LEFT;;;;
+21C4;RIGHTWARDS ARROW OVER LEFTWARDS ARROW;So;0;ON;;;;;N;RIGHT ARROW OVER LEFT ARROW;;;;
+21C5;UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW;So;0;ON;;;;;N;UP ARROW LEFT OF DOWN ARROW;;;;
+21C6;LEFTWARDS ARROW OVER RIGHTWARDS ARROW;So;0;ON;;;;;N;LEFT ARROW OVER RIGHT ARROW;;;;
+21C7;LEFTWARDS PAIRED ARROWS;So;0;ON;;;;;N;LEFT PAIRED ARROWS;;;;
+21C8;UPWARDS PAIRED ARROWS;So;0;ON;;;;;N;UP PAIRED ARROWS;;;;
+21C9;RIGHTWARDS PAIRED ARROWS;So;0;ON;;;;;N;RIGHT PAIRED ARROWS;;;;
+21CA;DOWNWARDS PAIRED ARROWS;So;0;ON;;;;;N;DOWN PAIRED ARROWS;;;;
+21CB;LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON;So;0;ON;;;;;N;LEFT HARPOON OVER RIGHT HARPOON;;;;
+21CC;RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON;So;0;ON;;;;;N;RIGHT HARPOON OVER LEFT HARPOON;;;;
+21CD;LEFTWARDS DOUBLE ARROW WITH STROKE;So;0;ON;21D0 0338;;;;N;LEFT DOUBLE ARROW WITH STROKE;;;;
+21CE;LEFT RIGHT DOUBLE ARROW WITH STROKE;Sm;0;ON;21D4 0338;;;;N;;;;;
+21CF;RIGHTWARDS DOUBLE ARROW WITH STROKE;Sm;0;ON;21D2 0338;;;;N;RIGHT DOUBLE ARROW WITH STROKE;;;;
+21D0;LEFTWARDS DOUBLE ARROW;So;0;ON;;;;;N;LEFT DOUBLE ARROW;;;;
+21D1;UPWARDS DOUBLE ARROW;So;0;ON;;;;;N;UP DOUBLE ARROW;;;;
+21D2;RIGHTWARDS DOUBLE ARROW;Sm;0;ON;;;;;N;RIGHT DOUBLE ARROW;;;;
+21D3;DOWNWARDS DOUBLE ARROW;So;0;ON;;;;;N;DOWN DOUBLE ARROW;;;;
+21D4;LEFT RIGHT DOUBLE ARROW;Sm;0;ON;;;;;N;;;;;
+21D5;UP DOWN DOUBLE ARROW;So;0;ON;;;;;N;;;;;
+21D6;NORTH WEST DOUBLE ARROW;So;0;ON;;;;;N;UPPER LEFT DOUBLE ARROW;;;;
+21D7;NORTH EAST DOUBLE ARROW;So;0;ON;;;;;N;UPPER RIGHT DOUBLE ARROW;;;;
+21D8;SOUTH EAST DOUBLE ARROW;So;0;ON;;;;;N;LOWER RIGHT DOUBLE ARROW;;;;
+21D9;SOUTH WEST DOUBLE ARROW;So;0;ON;;;;;N;LOWER LEFT DOUBLE ARROW;;;;
+21DA;LEFTWARDS TRIPLE ARROW;So;0;ON;;;;;N;LEFT TRIPLE ARROW;;;;
+21DB;RIGHTWARDS TRIPLE ARROW;So;0;ON;;;;;N;RIGHT TRIPLE ARROW;;;;
+21DC;LEFTWARDS SQUIGGLE ARROW;So;0;ON;;;;;N;LEFT SQUIGGLE ARROW;;;;
+21DD;RIGHTWARDS SQUIGGLE ARROW;So;0;ON;;;;;N;RIGHT SQUIGGLE ARROW;;;;
+21DE;UPWARDS ARROW WITH DOUBLE STROKE;So;0;ON;;;;;N;UP ARROW WITH DOUBLE STROKE;;;;
+21DF;DOWNWARDS ARROW WITH DOUBLE STROKE;So;0;ON;;;;;N;DOWN ARROW WITH DOUBLE STROKE;;;;
+21E0;LEFTWARDS DASHED ARROW;So;0;ON;;;;;N;LEFT DASHED ARROW;;;;
+21E1;UPWARDS DASHED ARROW;So;0;ON;;;;;N;UP DASHED ARROW;;;;
+21E2;RIGHTWARDS DASHED ARROW;So;0;ON;;;;;N;RIGHT DASHED ARROW;;;;
+21E3;DOWNWARDS DASHED ARROW;So;0;ON;;;;;N;DOWN DASHED ARROW;;;;
+21E4;LEFTWARDS ARROW TO BAR;So;0;ON;;;;;N;LEFT ARROW TO BAR;;;;
+21E5;RIGHTWARDS ARROW TO BAR;So;0;ON;;;;;N;RIGHT ARROW TO BAR;;;;
+21E6;LEFTWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE LEFT ARROW;;;;
+21E7;UPWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE UP ARROW;;;;
+21E8;RIGHTWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE RIGHT ARROW;;;;
+21E9;DOWNWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE DOWN ARROW;;;;
+21EA;UPWARDS WHITE ARROW FROM BAR;So;0;ON;;;;;N;WHITE UP ARROW FROM BAR;;;;
+21EB;UPWARDS WHITE ARROW ON PEDESTAL;So;0;ON;;;;;N;;;;;
+21EC;UPWARDS WHITE ARROW ON PEDESTAL WITH HORIZONTAL BAR;So;0;ON;;;;;N;;;;;
+21ED;UPWARDS WHITE ARROW ON PEDESTAL WITH VERTICAL BAR;So;0;ON;;;;;N;;;;;
+21EE;UPWARDS WHITE DOUBLE ARROW;So;0;ON;;;;;N;;;;;
+21EF;UPWARDS WHITE DOUBLE ARROW ON PEDESTAL;So;0;ON;;;;;N;;;;;
+21F0;RIGHTWARDS WHITE ARROW FROM WALL;So;0;ON;;;;;N;;;;;
+21F1;NORTH WEST ARROW TO CORNER;So;0;ON;;;;;N;;;;;
+21F2;SOUTH EAST ARROW TO CORNER;So;0;ON;;;;;N;;;;;
+21F3;UP DOWN WHITE ARROW;So;0;ON;;;;;N;;;;;
+21F4;RIGHT ARROW WITH SMALL CIRCLE;Sm;0;ON;;;;;N;;;;;
+21F5;DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+21F6;THREE RIGHTWARDS ARROWS;Sm;0;ON;;;;;N;;;;;
+21F7;LEFTWARDS ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+21F8;RIGHTWARDS ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+21F9;LEFT RIGHT ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+21FA;LEFTWARDS ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+21FB;RIGHTWARDS ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+21FC;LEFT RIGHT ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+21FD;LEFTWARDS OPEN-HEADED ARROW;Sm;0;ON;;;;;N;;;;;
+21FE;RIGHTWARDS OPEN-HEADED ARROW;Sm;0;ON;;;;;N;;;;;
+21FF;LEFT RIGHT OPEN-HEADED ARROW;Sm;0;ON;;;;;N;;;;;
+2200;FOR ALL;Sm;0;ON;;;;;N;;;;;
+2201;COMPLEMENT;Sm;0;ON;;;;;Y;;;;;
+2202;PARTIAL DIFFERENTIAL;Sm;0;ON;;;;;Y;;;;;
+2203;THERE EXISTS;Sm;0;ON;;;;;Y;;;;;
+2204;THERE DOES NOT EXIST;Sm;0;ON;2203 0338;;;;Y;;;;;
+2205;EMPTY SET;Sm;0;ON;;;;;N;;;;;
+2206;INCREMENT;Sm;0;ON;;;;;N;;;;;
+2207;NABLA;Sm;0;ON;;;;;N;;;;;
+2208;ELEMENT OF;Sm;0;ON;;;;;Y;;;;;
+2209;NOT AN ELEMENT OF;Sm;0;ON;2208 0338;;;;Y;;;;;
+220A;SMALL ELEMENT OF;Sm;0;ON;;;;;Y;;;;;
+220B;CONTAINS AS MEMBER;Sm;0;ON;;;;;Y;;;;;
+220C;DOES NOT CONTAIN AS MEMBER;Sm;0;ON;220B 0338;;;;Y;;;;;
+220D;SMALL CONTAINS AS MEMBER;Sm;0;ON;;;;;Y;;;;;
+220E;END OF PROOF;Sm;0;ON;;;;;N;;;;;
+220F;N-ARY PRODUCT;Sm;0;ON;;;;;N;;;;;
+2210;N-ARY COPRODUCT;Sm;0;ON;;;;;N;;;;;
+2211;N-ARY SUMMATION;Sm;0;ON;;;;;Y;;;;;
+2212;MINUS SIGN;Sm;0;ES;;;;;N;;;;;
+2213;MINUS-OR-PLUS SIGN;Sm;0;ET;;;;;N;;;;;
+2214;DOT PLUS;Sm;0;ON;;;;;N;;;;;
+2215;DIVISION SLASH;Sm;0;ON;;;;;Y;;;;;
+2216;SET MINUS;Sm;0;ON;;;;;Y;;;;;
+2217;ASTERISK OPERATOR;Sm;0;ON;;;;;N;;;;;
+2218;RING OPERATOR;Sm;0;ON;;;;;N;;;;;
+2219;BULLET OPERATOR;Sm;0;ON;;;;;N;;;;;
+221A;SQUARE ROOT;Sm;0;ON;;;;;Y;;;;;
+221B;CUBE ROOT;Sm;0;ON;;;;;Y;;;;;
+221C;FOURTH ROOT;Sm;0;ON;;;;;Y;;;;;
+221D;PROPORTIONAL TO;Sm;0;ON;;;;;Y;;;;;
+221E;INFINITY;Sm;0;ON;;;;;N;;;;;
+221F;RIGHT ANGLE;Sm;0;ON;;;;;Y;;;;;
+2220;ANGLE;Sm;0;ON;;;;;Y;;;;;
+2221;MEASURED ANGLE;Sm;0;ON;;;;;Y;;;;;
+2222;SPHERICAL ANGLE;Sm;0;ON;;;;;Y;;;;;
+2223;DIVIDES;Sm;0;ON;;;;;N;;;;;
+2224;DOES NOT DIVIDE;Sm;0;ON;2223 0338;;;;Y;;;;;
+2225;PARALLEL TO;Sm;0;ON;;;;;N;;;;;
+2226;NOT PARALLEL TO;Sm;0;ON;2225 0338;;;;Y;;;;;
+2227;LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+2228;LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+2229;INTERSECTION;Sm;0;ON;;;;;N;;;;;
+222A;UNION;Sm;0;ON;;;;;N;;;;;
+222B;INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+222C;DOUBLE INTEGRAL;Sm;0;ON;<compat> 222B 222B;;;;Y;;;;;
+222D;TRIPLE INTEGRAL;Sm;0;ON;<compat> 222B 222B 222B;;;;Y;;;;;
+222E;CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+222F;SURFACE INTEGRAL;Sm;0;ON;<compat> 222E 222E;;;;Y;;;;;
+2230;VOLUME INTEGRAL;Sm;0;ON;<compat> 222E 222E 222E;;;;Y;;;;;
+2231;CLOCKWISE INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2232;CLOCKWISE CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2233;ANTICLOCKWISE CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2234;THEREFORE;Sm;0;ON;;;;;N;;;;;
+2235;BECAUSE;Sm;0;ON;;;;;N;;;;;
+2236;RATIO;Sm;0;ON;;;;;N;;;;;
+2237;PROPORTION;Sm;0;ON;;;;;N;;;;;
+2238;DOT MINUS;Sm;0;ON;;;;;N;;;;;
+2239;EXCESS;Sm;0;ON;;;;;Y;;;;;
+223A;GEOMETRIC PROPORTION;Sm;0;ON;;;;;N;;;;;
+223B;HOMOTHETIC;Sm;0;ON;;;;;Y;;;;;
+223C;TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;;
+223D;REVERSED TILDE;Sm;0;ON;;;;;Y;;;;;
+223E;INVERTED LAZY S;Sm;0;ON;;;;;Y;;;;;
+223F;SINE WAVE;Sm;0;ON;;;;;Y;;;;;
+2240;WREATH PRODUCT;Sm;0;ON;;;;;Y;;;;;
+2241;NOT TILDE;Sm;0;ON;223C 0338;;;;Y;;;;;
+2242;MINUS TILDE;Sm;0;ON;;;;;Y;;;;;
+2243;ASYMPTOTICALLY EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2244;NOT ASYMPTOTICALLY EQUAL TO;Sm;0;ON;2243 0338;;;;Y;;;;;
+2245;APPROXIMATELY EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2246;APPROXIMATELY BUT NOT ACTUALLY EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2247;NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO;Sm;0;ON;2245 0338;;;;Y;;;;;
+2248;ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2249;NOT ALMOST EQUAL TO;Sm;0;ON;2248 0338;;;;Y;;;;;
+224A;ALMOST EQUAL OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+224B;TRIPLE TILDE;Sm;0;ON;;;;;Y;;;;;
+224C;ALL EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+224D;EQUIVALENT TO;Sm;0;ON;;;;;N;;;;;
+224E;GEOMETRICALLY EQUIVALENT TO;Sm;0;ON;;;;;N;;;;;
+224F;DIFFERENCE BETWEEN;Sm;0;ON;;;;;N;;;;;
+2250;APPROACHES THE LIMIT;Sm;0;ON;;;;;N;;;;;
+2251;GEOMETRICALLY EQUAL TO;Sm;0;ON;;;;;N;;;;;
+2252;APPROXIMATELY EQUAL TO OR THE IMAGE OF;Sm;0;ON;;;;;Y;;;;;
+2253;IMAGE OF OR APPROXIMATELY EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2254;COLON EQUALS;Sm;0;ON;;;;;Y;COLON EQUAL;;;;
+2255;EQUALS COLON;Sm;0;ON;;;;;Y;EQUAL COLON;;;;
+2256;RING IN EQUAL TO;Sm;0;ON;;;;;N;;;;;
+2257;RING EQUAL TO;Sm;0;ON;;;;;N;;;;;
+2258;CORRESPONDS TO;Sm;0;ON;;;;;N;;;;;
+2259;ESTIMATES;Sm;0;ON;;;;;N;;;;;
+225A;EQUIANGULAR TO;Sm;0;ON;;;;;N;;;;;
+225B;STAR EQUALS;Sm;0;ON;;;;;N;;;;;
+225C;DELTA EQUAL TO;Sm;0;ON;;;;;N;;;;;
+225D;EQUAL TO BY DEFINITION;Sm;0;ON;;;;;N;;;;;
+225E;MEASURED BY;Sm;0;ON;;;;;N;;;;;
+225F;QUESTIONED EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2260;NOT EQUAL TO;Sm;0;ON;003D 0338;;;;Y;;;;;
+2261;IDENTICAL TO;Sm;0;ON;;;;;N;;;;;
+2262;NOT IDENTICAL TO;Sm;0;ON;2261 0338;;;;Y;;;;;
+2263;STRICTLY EQUIVALENT TO;Sm;0;ON;;;;;N;;;;;
+2264;LESS-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN OR EQUAL TO;;;;
+2265;GREATER-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN OR EQUAL TO;;;;
+2266;LESS-THAN OVER EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN OVER EQUAL TO;;;;
+2267;GREATER-THAN OVER EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN OVER EQUAL TO;;;;
+2268;LESS-THAN BUT NOT EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN BUT NOT EQUAL TO;;;;
+2269;GREATER-THAN BUT NOT EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN BUT NOT EQUAL TO;;;;
+226A;MUCH LESS-THAN;Sm;0;ON;;;;;Y;MUCH LESS THAN;;;;
+226B;MUCH GREATER-THAN;Sm;0;ON;;;;;Y;MUCH GREATER THAN;;;;
+226C;BETWEEN;Sm;0;ON;;;;;N;;;;;
+226D;NOT EQUIVALENT TO;Sm;0;ON;224D 0338;;;;N;;;;;
+226E;NOT LESS-THAN;Sm;0;ON;003C 0338;;;;Y;NOT LESS THAN;;;;
+226F;NOT GREATER-THAN;Sm;0;ON;003E 0338;;;;Y;NOT GREATER THAN;;;;
+2270;NEITHER LESS-THAN NOR EQUAL TO;Sm;0;ON;2264 0338;;;;Y;NEITHER LESS THAN NOR EQUAL TO;;;;
+2271;NEITHER GREATER-THAN NOR EQUAL TO;Sm;0;ON;2265 0338;;;;Y;NEITHER GREATER THAN NOR EQUAL TO;;;;
+2272;LESS-THAN OR EQUIVALENT TO;Sm;0;ON;;;;;Y;LESS THAN OR EQUIVALENT TO;;;;
+2273;GREATER-THAN OR EQUIVALENT TO;Sm;0;ON;;;;;Y;GREATER THAN OR EQUIVALENT TO;;;;
+2274;NEITHER LESS-THAN NOR EQUIVALENT TO;Sm;0;ON;2272 0338;;;;Y;NEITHER LESS THAN NOR EQUIVALENT TO;;;;
+2275;NEITHER GREATER-THAN NOR EQUIVALENT TO;Sm;0;ON;2273 0338;;;;Y;NEITHER GREATER THAN NOR EQUIVALENT TO;;;;
+2276;LESS-THAN OR GREATER-THAN;Sm;0;ON;;;;;Y;LESS THAN OR GREATER THAN;;;;
+2277;GREATER-THAN OR LESS-THAN;Sm;0;ON;;;;;Y;GREATER THAN OR LESS THAN;;;;
+2278;NEITHER LESS-THAN NOR GREATER-THAN;Sm;0;ON;2276 0338;;;;Y;NEITHER LESS THAN NOR GREATER THAN;;;;
+2279;NEITHER GREATER-THAN NOR LESS-THAN;Sm;0;ON;2277 0338;;;;Y;NEITHER GREATER THAN NOR LESS THAN;;;;
+227A;PRECEDES;Sm;0;ON;;;;;Y;;;;;
+227B;SUCCEEDS;Sm;0;ON;;;;;Y;;;;;
+227C;PRECEDES OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+227D;SUCCEEDS OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+227E;PRECEDES OR EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;;
+227F;SUCCEEDS OR EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;;
+2280;DOES NOT PRECEDE;Sm;0;ON;227A 0338;;;;Y;;;;;
+2281;DOES NOT SUCCEED;Sm;0;ON;227B 0338;;;;Y;;;;;
+2282;SUBSET OF;Sm;0;ON;;;;;Y;;;;;
+2283;SUPERSET OF;Sm;0;ON;;;;;Y;;;;;
+2284;NOT A SUBSET OF;Sm;0;ON;2282 0338;;;;Y;;;;;
+2285;NOT A SUPERSET OF;Sm;0;ON;2283 0338;;;;Y;;;;;
+2286;SUBSET OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2287;SUPERSET OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2288;NEITHER A SUBSET OF NOR EQUAL TO;Sm;0;ON;2286 0338;;;;Y;;;;;
+2289;NEITHER A SUPERSET OF NOR EQUAL TO;Sm;0;ON;2287 0338;;;;Y;;;;;
+228A;SUBSET OF WITH NOT EQUAL TO;Sm;0;ON;;;;;Y;SUBSET OF OR NOT EQUAL TO;;;;
+228B;SUPERSET OF WITH NOT EQUAL TO;Sm;0;ON;;;;;Y;SUPERSET OF OR NOT EQUAL TO;;;;
+228C;MULTISET;Sm;0;ON;;;;;Y;;;;;
+228D;MULTISET MULTIPLICATION;Sm;0;ON;;;;;N;;;;;
+228E;MULTISET UNION;Sm;0;ON;;;;;N;;;;;
+228F;SQUARE IMAGE OF;Sm;0;ON;;;;;Y;;;;;
+2290;SQUARE ORIGINAL OF;Sm;0;ON;;;;;Y;;;;;
+2291;SQUARE IMAGE OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2292;SQUARE ORIGINAL OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2293;SQUARE CAP;Sm;0;ON;;;;;N;;;;;
+2294;SQUARE CUP;Sm;0;ON;;;;;N;;;;;
+2295;CIRCLED PLUS;Sm;0;ON;;;;;N;;;;;
+2296;CIRCLED MINUS;Sm;0;ON;;;;;N;;;;;
+2297;CIRCLED TIMES;Sm;0;ON;;;;;N;;;;;
+2298;CIRCLED DIVISION SLASH;Sm;0;ON;;;;;Y;;;;;
+2299;CIRCLED DOT OPERATOR;Sm;0;ON;;;;;N;;;;;
+229A;CIRCLED RING OPERATOR;Sm;0;ON;;;;;N;;;;;
+229B;CIRCLED ASTERISK OPERATOR;Sm;0;ON;;;;;N;;;;;
+229C;CIRCLED EQUALS;Sm;0;ON;;;;;N;;;;;
+229D;CIRCLED DASH;Sm;0;ON;;;;;N;;;;;
+229E;SQUARED PLUS;Sm;0;ON;;;;;N;;;;;
+229F;SQUARED MINUS;Sm;0;ON;;;;;N;;;;;
+22A0;SQUARED TIMES;Sm;0;ON;;;;;N;;;;;
+22A1;SQUARED DOT OPERATOR;Sm;0;ON;;;;;N;;;;;
+22A2;RIGHT TACK;Sm;0;ON;;;;;Y;;;;;
+22A3;LEFT TACK;Sm;0;ON;;;;;Y;;;;;
+22A4;DOWN TACK;Sm;0;ON;;;;;N;;;;;
+22A5;UP TACK;Sm;0;ON;;;;;N;;;;;
+22A6;ASSERTION;Sm;0;ON;;;;;Y;;;;;
+22A7;MODELS;Sm;0;ON;;;;;Y;;;;;
+22A8;TRUE;Sm;0;ON;;;;;Y;;;;;
+22A9;FORCES;Sm;0;ON;;;;;Y;;;;;
+22AA;TRIPLE VERTICAL BAR RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+22AB;DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+22AC;DOES NOT PROVE;Sm;0;ON;22A2 0338;;;;Y;;;;;
+22AD;NOT TRUE;Sm;0;ON;22A8 0338;;;;Y;;;;;
+22AE;DOES NOT FORCE;Sm;0;ON;22A9 0338;;;;Y;;;;;
+22AF;NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE;Sm;0;ON;22AB 0338;;;;Y;;;;;
+22B0;PRECEDES UNDER RELATION;Sm;0;ON;;;;;Y;;;;;
+22B1;SUCCEEDS UNDER RELATION;Sm;0;ON;;;;;Y;;;;;
+22B2;NORMAL SUBGROUP OF;Sm;0;ON;;;;;Y;;;;;
+22B3;CONTAINS AS NORMAL SUBGROUP;Sm;0;ON;;;;;Y;;;;;
+22B4;NORMAL SUBGROUP OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+22B5;CONTAINS AS NORMAL SUBGROUP OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+22B6;ORIGINAL OF;Sm;0;ON;;;;;Y;;;;;
+22B7;IMAGE OF;Sm;0;ON;;;;;Y;;;;;
+22B8;MULTIMAP;Sm;0;ON;;;;;Y;;;;;
+22B9;HERMITIAN CONJUGATE MATRIX;Sm;0;ON;;;;;N;;;;;
+22BA;INTERCALATE;Sm;0;ON;;;;;N;;;;;
+22BB;XOR;Sm;0;ON;;;;;N;;;;;
+22BC;NAND;Sm;0;ON;;;;;N;;;;;
+22BD;NOR;Sm;0;ON;;;;;N;;;;;
+22BE;RIGHT ANGLE WITH ARC;Sm;0;ON;;;;;Y;;;;;
+22BF;RIGHT TRIANGLE;Sm;0;ON;;;;;Y;;;;;
+22C0;N-ARY LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+22C1;N-ARY LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+22C2;N-ARY INTERSECTION;Sm;0;ON;;;;;N;;;;;
+22C3;N-ARY UNION;Sm;0;ON;;;;;N;;;;;
+22C4;DIAMOND OPERATOR;Sm;0;ON;;;;;N;;;;;
+22C5;DOT OPERATOR;Sm;0;ON;;;;;N;;;;;
+22C6;STAR OPERATOR;Sm;0;ON;;;;;N;;;;;
+22C7;DIVISION TIMES;Sm;0;ON;;;;;N;;;;;
+22C8;BOWTIE;Sm;0;ON;;;;;N;;;;;
+22C9;LEFT NORMAL FACTOR SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;;
+22CA;RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;;
+22CB;LEFT SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;;
+22CC;RIGHT SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;;
+22CD;REVERSED TILDE EQUALS;Sm;0;ON;;;;;Y;;;;;
+22CE;CURLY LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+22CF;CURLY LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+22D0;DOUBLE SUBSET;Sm;0;ON;;;;;Y;;;;;
+22D1;DOUBLE SUPERSET;Sm;0;ON;;;;;Y;;;;;
+22D2;DOUBLE INTERSECTION;Sm;0;ON;;;;;N;;;;;
+22D3;DOUBLE UNION;Sm;0;ON;;;;;N;;;;;
+22D4;PITCHFORK;Sm;0;ON;;;;;N;;;;;
+22D5;EQUAL AND PARALLEL TO;Sm;0;ON;;;;;N;;;;;
+22D6;LESS-THAN WITH DOT;Sm;0;ON;;;;;Y;LESS THAN WITH DOT;;;;
+22D7;GREATER-THAN WITH DOT;Sm;0;ON;;;;;Y;GREATER THAN WITH DOT;;;;
+22D8;VERY MUCH LESS-THAN;Sm;0;ON;;;;;Y;VERY MUCH LESS THAN;;;;
+22D9;VERY MUCH GREATER-THAN;Sm;0;ON;;;;;Y;VERY MUCH GREATER THAN;;;;
+22DA;LESS-THAN EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;LESS THAN EQUAL TO OR GREATER THAN;;;;
+22DB;GREATER-THAN EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;GREATER THAN EQUAL TO OR LESS THAN;;;;
+22DC;EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;EQUAL TO OR LESS THAN;;;;
+22DD;EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;EQUAL TO OR GREATER THAN;;;;
+22DE;EQUAL TO OR PRECEDES;Sm;0;ON;;;;;Y;;;;;
+22DF;EQUAL TO OR SUCCEEDS;Sm;0;ON;;;;;Y;;;;;
+22E0;DOES NOT PRECEDE OR EQUAL;Sm;0;ON;227C 0338;;;;Y;;;;;
+22E1;DOES NOT SUCCEED OR EQUAL;Sm;0;ON;227D 0338;;;;Y;;;;;
+22E2;NOT SQUARE IMAGE OF OR EQUAL TO;Sm;0;ON;2291 0338;;;;Y;;;;;
+22E3;NOT SQUARE ORIGINAL OF OR EQUAL TO;Sm;0;ON;2292 0338;;;;Y;;;;;
+22E4;SQUARE IMAGE OF OR NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+22E5;SQUARE ORIGINAL OF OR NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+22E6;LESS-THAN BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;LESS THAN BUT NOT EQUIVALENT TO;;;;
+22E7;GREATER-THAN BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;GREATER THAN BUT NOT EQUIVALENT TO;;;;
+22E8;PRECEDES BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;;
+22E9;SUCCEEDS BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;;
+22EA;NOT NORMAL SUBGROUP OF;Sm;0;ON;22B2 0338;;;;Y;;;;;
+22EB;DOES NOT CONTAIN AS NORMAL SUBGROUP;Sm;0;ON;22B3 0338;;;;Y;;;;;
+22EC;NOT NORMAL SUBGROUP OF OR EQUAL TO;Sm;0;ON;22B4 0338;;;;Y;;;;;
+22ED;DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL;Sm;0;ON;22B5 0338;;;;Y;;;;;
+22EE;VERTICAL ELLIPSIS;Sm;0;ON;;;;;N;;;;;
+22EF;MIDLINE HORIZONTAL ELLIPSIS;Sm;0;ON;;;;;N;;;;;
+22F0;UP RIGHT DIAGONAL ELLIPSIS;Sm;0;ON;;;;;Y;;;;;
+22F1;DOWN RIGHT DIAGONAL ELLIPSIS;Sm;0;ON;;;;;Y;;;;;
+22F2;ELEMENT OF WITH LONG HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+22F3;ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+22F4;SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+22F5;ELEMENT OF WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+22F6;ELEMENT OF WITH OVERBAR;Sm;0;ON;;;;;Y;;;;;
+22F7;SMALL ELEMENT OF WITH OVERBAR;Sm;0;ON;;;;;Y;;;;;
+22F8;ELEMENT OF WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;;
+22F9;ELEMENT OF WITH TWO HORIZONTAL STROKES;Sm;0;ON;;;;;Y;;;;;
+22FA;CONTAINS WITH LONG HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+22FB;CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+22FC;SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+22FD;CONTAINS WITH OVERBAR;Sm;0;ON;;;;;Y;;;;;
+22FE;SMALL CONTAINS WITH OVERBAR;Sm;0;ON;;;;;Y;;;;;
+22FF;Z NOTATION BAG MEMBERSHIP;Sm;0;ON;;;;;Y;;;;;
+2300;DIAMETER SIGN;So;0;ON;;;;;N;;;;;
+2301;ELECTRIC ARROW;So;0;ON;;;;;N;;;;;
+2302;HOUSE;So;0;ON;;;;;N;;;;;
+2303;UP ARROWHEAD;So;0;ON;;;;;N;;;;;
+2304;DOWN ARROWHEAD;So;0;ON;;;;;N;;;;;
+2305;PROJECTIVE;So;0;ON;;;;;N;;;;;
+2306;PERSPECTIVE;So;0;ON;;;;;N;;;;;
+2307;WAVY LINE;So;0;ON;;;;;N;;;;;
+2308;LEFT CEILING;Ps;0;ON;;;;;Y;;;;;
+2309;RIGHT CEILING;Pe;0;ON;;;;;Y;;;;;
+230A;LEFT FLOOR;Ps;0;ON;;;;;Y;;;;;
+230B;RIGHT FLOOR;Pe;0;ON;;;;;Y;;;;;
+230C;BOTTOM RIGHT CROP;So;0;ON;;;;;N;;;;;
+230D;BOTTOM LEFT CROP;So;0;ON;;;;;N;;;;;
+230E;TOP RIGHT CROP;So;0;ON;;;;;N;;;;;
+230F;TOP LEFT CROP;So;0;ON;;;;;N;;;;;
+2310;REVERSED NOT SIGN;So;0;ON;;;;;N;;;;;
+2311;SQUARE LOZENGE;So;0;ON;;;;;N;;;;;
+2312;ARC;So;0;ON;;;;;N;;;;;
+2313;SEGMENT;So;0;ON;;;;;N;;;;;
+2314;SECTOR;So;0;ON;;;;;N;;;;;
+2315;TELEPHONE RECORDER;So;0;ON;;;;;N;;;;;
+2316;POSITION INDICATOR;So;0;ON;;;;;N;;;;;
+2317;VIEWDATA SQUARE;So;0;ON;;;;;N;;;;;
+2318;PLACE OF INTEREST SIGN;So;0;ON;;;;;N;COMMAND KEY;;;;
+2319;TURNED NOT SIGN;So;0;ON;;;;;N;;;;;
+231A;WATCH;So;0;ON;;;;;N;;;;;
+231B;HOURGLASS;So;0;ON;;;;;N;;;;;
+231C;TOP LEFT CORNER;So;0;ON;;;;;N;;;;;
+231D;TOP RIGHT CORNER;So;0;ON;;;;;N;;;;;
+231E;BOTTOM LEFT CORNER;So;0;ON;;;;;N;;;;;
+231F;BOTTOM RIGHT CORNER;So;0;ON;;;;;N;;;;;
+2320;TOP HALF INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2321;BOTTOM HALF INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2322;FROWN;So;0;ON;;;;;N;;;;;
+2323;SMILE;So;0;ON;;;;;N;;;;;
+2324;UP ARROWHEAD BETWEEN TWO HORIZONTAL BARS;So;0;ON;;;;;N;ENTER KEY;;;;
+2325;OPTION KEY;So;0;ON;;;;;N;;;;;
+2326;ERASE TO THE RIGHT;So;0;ON;;;;;N;DELETE TO THE RIGHT KEY;;;;
+2327;X IN A RECTANGLE BOX;So;0;ON;;;;;N;CLEAR KEY;;;;
+2328;KEYBOARD;So;0;ON;;;;;N;;;;;
+2329;LEFT-POINTING ANGLE BRACKET;Ps;0;ON;3008;;;;Y;BRA;;;;
+232A;RIGHT-POINTING ANGLE BRACKET;Pe;0;ON;3009;;;;Y;KET;;;;
+232B;ERASE TO THE LEFT;So;0;ON;;;;;N;DELETE TO THE LEFT KEY;;;;
+232C;BENZENE RING;So;0;ON;;;;;N;;;;;
+232D;CYLINDRICITY;So;0;ON;;;;;N;;;;;
+232E;ALL AROUND-PROFILE;So;0;ON;;;;;N;;;;;
+232F;SYMMETRY;So;0;ON;;;;;N;;;;;
+2330;TOTAL RUNOUT;So;0;ON;;;;;N;;;;;
+2331;DIMENSION ORIGIN;So;0;ON;;;;;N;;;;;
+2332;CONICAL TAPER;So;0;ON;;;;;N;;;;;
+2333;SLOPE;So;0;ON;;;;;N;;;;;
+2334;COUNTERBORE;So;0;ON;;;;;N;;;;;
+2335;COUNTERSINK;So;0;ON;;;;;N;;;;;
+2336;APL FUNCTIONAL SYMBOL I-BEAM;So;0;L;;;;;N;;;;;
+2337;APL FUNCTIONAL SYMBOL SQUISH QUAD;So;0;L;;;;;N;;;;;
+2338;APL FUNCTIONAL SYMBOL QUAD EQUAL;So;0;L;;;;;N;;;;;
+2339;APL FUNCTIONAL SYMBOL QUAD DIVIDE;So;0;L;;;;;N;;;;;
+233A;APL FUNCTIONAL SYMBOL QUAD DIAMOND;So;0;L;;;;;N;;;;;
+233B;APL FUNCTIONAL SYMBOL QUAD JOT;So;0;L;;;;;N;;;;;
+233C;APL FUNCTIONAL SYMBOL QUAD CIRCLE;So;0;L;;;;;N;;;;;
+233D;APL FUNCTIONAL SYMBOL CIRCLE STILE;So;0;L;;;;;N;;;;;
+233E;APL FUNCTIONAL SYMBOL CIRCLE JOT;So;0;L;;;;;N;;;;;
+233F;APL FUNCTIONAL SYMBOL SLASH BAR;So;0;L;;;;;N;;;;;
+2340;APL FUNCTIONAL SYMBOL BACKSLASH BAR;So;0;L;;;;;N;;;;;
+2341;APL FUNCTIONAL SYMBOL QUAD SLASH;So;0;L;;;;;N;;;;;
+2342;APL FUNCTIONAL SYMBOL QUAD BACKSLASH;So;0;L;;;;;N;;;;;
+2343;APL FUNCTIONAL SYMBOL QUAD LESS-THAN;So;0;L;;;;;N;;;;;
+2344;APL FUNCTIONAL SYMBOL QUAD GREATER-THAN;So;0;L;;;;;N;;;;;
+2345;APL FUNCTIONAL SYMBOL LEFTWARDS VANE;So;0;L;;;;;N;;;;;
+2346;APL FUNCTIONAL SYMBOL RIGHTWARDS VANE;So;0;L;;;;;N;;;;;
+2347;APL FUNCTIONAL SYMBOL QUAD LEFTWARDS ARROW;So;0;L;;;;;N;;;;;
+2348;APL FUNCTIONAL SYMBOL QUAD RIGHTWARDS ARROW;So;0;L;;;;;N;;;;;
+2349;APL FUNCTIONAL SYMBOL CIRCLE BACKSLASH;So;0;L;;;;;N;;;;;
+234A;APL FUNCTIONAL SYMBOL DOWN TACK UNDERBAR;So;0;L;;;;;N;;;;;
+234B;APL FUNCTIONAL SYMBOL DELTA STILE;So;0;L;;;;;N;;;;;
+234C;APL FUNCTIONAL SYMBOL QUAD DOWN CARET;So;0;L;;;;;N;;;;;
+234D;APL FUNCTIONAL SYMBOL QUAD DELTA;So;0;L;;;;;N;;;;;
+234E;APL FUNCTIONAL SYMBOL DOWN TACK JOT;So;0;L;;;;;N;;;;;
+234F;APL FUNCTIONAL SYMBOL UPWARDS VANE;So;0;L;;;;;N;;;;;
+2350;APL FUNCTIONAL SYMBOL QUAD UPWARDS ARROW;So;0;L;;;;;N;;;;;
+2351;APL FUNCTIONAL SYMBOL UP TACK OVERBAR;So;0;L;;;;;N;;;;;
+2352;APL FUNCTIONAL SYMBOL DEL STILE;So;0;L;;;;;N;;;;;
+2353;APL FUNCTIONAL SYMBOL QUAD UP CARET;So;0;L;;;;;N;;;;;
+2354;APL FUNCTIONAL SYMBOL QUAD DEL;So;0;L;;;;;N;;;;;
+2355;APL FUNCTIONAL SYMBOL UP TACK JOT;So;0;L;;;;;N;;;;;
+2356;APL FUNCTIONAL SYMBOL DOWNWARDS VANE;So;0;L;;;;;N;;;;;
+2357;APL FUNCTIONAL SYMBOL QUAD DOWNWARDS ARROW;So;0;L;;;;;N;;;;;
+2358;APL FUNCTIONAL SYMBOL QUOTE UNDERBAR;So;0;L;;;;;N;;;;;
+2359;APL FUNCTIONAL SYMBOL DELTA UNDERBAR;So;0;L;;;;;N;;;;;
+235A;APL FUNCTIONAL SYMBOL DIAMOND UNDERBAR;So;0;L;;;;;N;;;;;
+235B;APL FUNCTIONAL SYMBOL JOT UNDERBAR;So;0;L;;;;;N;;;;;
+235C;APL FUNCTIONAL SYMBOL CIRCLE UNDERBAR;So;0;L;;;;;N;;;;;
+235D;APL FUNCTIONAL SYMBOL UP SHOE JOT;So;0;L;;;;;N;;;;;
+235E;APL FUNCTIONAL SYMBOL QUOTE QUAD;So;0;L;;;;;N;;;;;
+235F;APL FUNCTIONAL SYMBOL CIRCLE STAR;So;0;L;;;;;N;;;;;
+2360;APL FUNCTIONAL SYMBOL QUAD COLON;So;0;L;;;;;N;;;;;
+2361;APL FUNCTIONAL SYMBOL UP TACK DIAERESIS;So;0;L;;;;;N;;;;;
+2362;APL FUNCTIONAL SYMBOL DEL DIAERESIS;So;0;L;;;;;N;;;;;
+2363;APL FUNCTIONAL SYMBOL STAR DIAERESIS;So;0;L;;;;;N;;;;;
+2364;APL FUNCTIONAL SYMBOL JOT DIAERESIS;So;0;L;;;;;N;;;;;
+2365;APL FUNCTIONAL SYMBOL CIRCLE DIAERESIS;So;0;L;;;;;N;;;;;
+2366;APL FUNCTIONAL SYMBOL DOWN SHOE STILE;So;0;L;;;;;N;;;;;
+2367;APL FUNCTIONAL SYMBOL LEFT SHOE STILE;So;0;L;;;;;N;;;;;
+2368;APL FUNCTIONAL SYMBOL TILDE DIAERESIS;So;0;L;;;;;N;;;;;
+2369;APL FUNCTIONAL SYMBOL GREATER-THAN DIAERESIS;So;0;L;;;;;N;;;;;
+236A;APL FUNCTIONAL SYMBOL COMMA BAR;So;0;L;;;;;N;;;;;
+236B;APL FUNCTIONAL SYMBOL DEL TILDE;So;0;L;;;;;N;;;;;
+236C;APL FUNCTIONAL SYMBOL ZILDE;So;0;L;;;;;N;;;;;
+236D;APL FUNCTIONAL SYMBOL STILE TILDE;So;0;L;;;;;N;;;;;
+236E;APL FUNCTIONAL SYMBOL SEMICOLON UNDERBAR;So;0;L;;;;;N;;;;;
+236F;APL FUNCTIONAL SYMBOL QUAD NOT EQUAL;So;0;L;;;;;N;;;;;
+2370;APL FUNCTIONAL SYMBOL QUAD QUESTION;So;0;L;;;;;N;;;;;
+2371;APL FUNCTIONAL SYMBOL DOWN CARET TILDE;So;0;L;;;;;N;;;;;
+2372;APL FUNCTIONAL SYMBOL UP CARET TILDE;So;0;L;;;;;N;;;;;
+2373;APL FUNCTIONAL SYMBOL IOTA;So;0;L;;;;;N;;;;;
+2374;APL FUNCTIONAL SYMBOL RHO;So;0;L;;;;;N;;;;;
+2375;APL FUNCTIONAL SYMBOL OMEGA;So;0;L;;;;;N;;;;;
+2376;APL FUNCTIONAL SYMBOL ALPHA UNDERBAR;So;0;L;;;;;N;;;;;
+2377;APL FUNCTIONAL SYMBOL EPSILON UNDERBAR;So;0;L;;;;;N;;;;;
+2378;APL FUNCTIONAL SYMBOL IOTA UNDERBAR;So;0;L;;;;;N;;;;;
+2379;APL FUNCTIONAL SYMBOL OMEGA UNDERBAR;So;0;L;;;;;N;;;;;
+237A;APL FUNCTIONAL SYMBOL ALPHA;So;0;L;;;;;N;;;;;
+237B;NOT CHECK MARK;So;0;ON;;;;;N;;;;;
+237C;RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW;Sm;0;ON;;;;;N;;;;;
+237D;SHOULDERED OPEN BOX;So;0;ON;;;;;N;;;;;
+237E;BELL SYMBOL;So;0;ON;;;;;N;;;;;
+237F;VERTICAL LINE WITH MIDDLE DOT;So;0;ON;;;;;N;;;;;
+2380;INSERTION SYMBOL;So;0;ON;;;;;N;;;;;
+2381;CONTINUOUS UNDERLINE SYMBOL;So;0;ON;;;;;N;;;;;
+2382;DISCONTINUOUS UNDERLINE SYMBOL;So;0;ON;;;;;N;;;;;
+2383;EMPHASIS SYMBOL;So;0;ON;;;;;N;;;;;
+2384;COMPOSITION SYMBOL;So;0;ON;;;;;N;;;;;
+2385;WHITE SQUARE WITH CENTRE VERTICAL LINE;So;0;ON;;;;;N;;;;;
+2386;ENTER SYMBOL;So;0;ON;;;;;N;;;;;
+2387;ALTERNATIVE KEY SYMBOL;So;0;ON;;;;;N;;;;;
+2388;HELM SYMBOL;So;0;ON;;;;;N;;;;;
+2389;CIRCLED HORIZONTAL BAR WITH NOTCH;So;0;ON;;;;;N;;;;;
+238A;CIRCLED TRIANGLE DOWN;So;0;ON;;;;;N;;;;;
+238B;BROKEN CIRCLE WITH NORTHWEST ARROW;So;0;ON;;;;;N;;;;;
+238C;UNDO SYMBOL;So;0;ON;;;;;N;;;;;
+238D;MONOSTABLE SYMBOL;So;0;ON;;;;;N;;;;;
+238E;HYSTERESIS SYMBOL;So;0;ON;;;;;N;;;;;
+238F;OPEN-CIRCUIT-OUTPUT H-TYPE SYMBOL;So;0;ON;;;;;N;;;;;
+2390;OPEN-CIRCUIT-OUTPUT L-TYPE SYMBOL;So;0;ON;;;;;N;;;;;
+2391;PASSIVE-PULL-DOWN-OUTPUT SYMBOL;So;0;ON;;;;;N;;;;;
+2392;PASSIVE-PULL-UP-OUTPUT SYMBOL;So;0;ON;;;;;N;;;;;
+2393;DIRECT CURRENT SYMBOL FORM TWO;So;0;ON;;;;;N;;;;;
+2394;SOFTWARE-FUNCTION SYMBOL;So;0;ON;;;;;N;;;;;
+2395;APL FUNCTIONAL SYMBOL QUAD;So;0;L;;;;;N;;;;;
+2396;DECIMAL SEPARATOR KEY SYMBOL;So;0;ON;;;;;N;;;;;
+2397;PREVIOUS PAGE;So;0;ON;;;;;N;;;;;
+2398;NEXT PAGE;So;0;ON;;;;;N;;;;;
+2399;PRINT SCREEN SYMBOL;So;0;ON;;;;;N;;;;;
+239A;CLEAR SCREEN SYMBOL;So;0;ON;;;;;N;;;;;
+239B;LEFT PARENTHESIS UPPER HOOK;Sm;0;ON;;;;;N;;;;;
+239C;LEFT PARENTHESIS EXTENSION;Sm;0;ON;;;;;N;;;;;
+239D;LEFT PARENTHESIS LOWER HOOK;Sm;0;ON;;;;;N;;;;;
+239E;RIGHT PARENTHESIS UPPER HOOK;Sm;0;ON;;;;;N;;;;;
+239F;RIGHT PARENTHESIS EXTENSION;Sm;0;ON;;;;;N;;;;;
+23A0;RIGHT PARENTHESIS LOWER HOOK;Sm;0;ON;;;;;N;;;;;
+23A1;LEFT SQUARE BRACKET UPPER CORNER;Sm;0;ON;;;;;N;;;;;
+23A2;LEFT SQUARE BRACKET EXTENSION;Sm;0;ON;;;;;N;;;;;
+23A3;LEFT SQUARE BRACKET LOWER CORNER;Sm;0;ON;;;;;N;;;;;
+23A4;RIGHT SQUARE BRACKET UPPER CORNER;Sm;0;ON;;;;;N;;;;;
+23A5;RIGHT SQUARE BRACKET EXTENSION;Sm;0;ON;;;;;N;;;;;
+23A6;RIGHT SQUARE BRACKET LOWER CORNER;Sm;0;ON;;;;;N;;;;;
+23A7;LEFT CURLY BRACKET UPPER HOOK;Sm;0;ON;;;;;N;;;;;
+23A8;LEFT CURLY BRACKET MIDDLE PIECE;Sm;0;ON;;;;;N;;;;;
+23A9;LEFT CURLY BRACKET LOWER HOOK;Sm;0;ON;;;;;N;;;;;
+23AA;CURLY BRACKET EXTENSION;Sm;0;ON;;;;;N;;;;;
+23AB;RIGHT CURLY BRACKET UPPER HOOK;Sm;0;ON;;;;;N;;;;;
+23AC;RIGHT CURLY BRACKET MIDDLE PIECE;Sm;0;ON;;;;;N;;;;;
+23AD;RIGHT CURLY BRACKET LOWER HOOK;Sm;0;ON;;;;;N;;;;;
+23AE;INTEGRAL EXTENSION;Sm;0;ON;;;;;N;;;;;
+23AF;HORIZONTAL LINE EXTENSION;Sm;0;ON;;;;;N;;;;;
+23B0;UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION;Sm;0;ON;;;;;N;;;;;
+23B1;UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION;Sm;0;ON;;;;;N;;;;;
+23B2;SUMMATION TOP;Sm;0;ON;;;;;N;;;;;
+23B3;SUMMATION BOTTOM;Sm;0;ON;;;;;N;;;;;
+23B4;TOP SQUARE BRACKET;So;0;ON;;;;;N;;;;;
+23B5;BOTTOM SQUARE BRACKET;So;0;ON;;;;;N;;;;;
+23B6;BOTTOM SQUARE BRACKET OVER TOP SQUARE BRACKET;So;0;ON;;;;;N;;;;;
+23B7;RADICAL SYMBOL BOTTOM;So;0;ON;;;;;N;;;;;
+23B8;LEFT VERTICAL BOX LINE;So;0;ON;;;;;N;;;;;
+23B9;RIGHT VERTICAL BOX LINE;So;0;ON;;;;;N;;;;;
+23BA;HORIZONTAL SCAN LINE-1;So;0;ON;;;;;N;;;;;
+23BB;HORIZONTAL SCAN LINE-3;So;0;ON;;;;;N;;;;;
+23BC;HORIZONTAL SCAN LINE-7;So;0;ON;;;;;N;;;;;
+23BD;HORIZONTAL SCAN LINE-9;So;0;ON;;;;;N;;;;;
+23BE;DENTISTRY SYMBOL LIGHT VERTICAL AND TOP RIGHT;So;0;ON;;;;;N;;;;;
+23BF;DENTISTRY SYMBOL LIGHT VERTICAL AND BOTTOM RIGHT;So;0;ON;;;;;N;;;;;
+23C0;DENTISTRY SYMBOL LIGHT VERTICAL WITH CIRCLE;So;0;ON;;;;;N;;;;;
+23C1;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH CIRCLE;So;0;ON;;;;;N;;;;;
+23C2;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH CIRCLE;So;0;ON;;;;;N;;;;;
+23C3;DENTISTRY SYMBOL LIGHT VERTICAL WITH TRIANGLE;So;0;ON;;;;;N;;;;;
+23C4;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH TRIANGLE;So;0;ON;;;;;N;;;;;
+23C5;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH TRIANGLE;So;0;ON;;;;;N;;;;;
+23C6;DENTISTRY SYMBOL LIGHT VERTICAL AND WAVE;So;0;ON;;;;;N;;;;;
+23C7;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH WAVE;So;0;ON;;;;;N;;;;;
+23C8;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH WAVE;So;0;ON;;;;;N;;;;;
+23C9;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL;So;0;ON;;;;;N;;;;;
+23CA;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL;So;0;ON;;;;;N;;;;;
+23CB;DENTISTRY SYMBOL LIGHT VERTICAL AND TOP LEFT;So;0;ON;;;;;N;;;;;
+23CC;DENTISTRY SYMBOL LIGHT VERTICAL AND BOTTOM LEFT;So;0;ON;;;;;N;;;;;
+23CD;SQUARE FOOT;So;0;ON;;;;;N;;;;;
+23CE;RETURN SYMBOL;So;0;ON;;;;;N;;;;;
+23CF;EJECT SYMBOL;So;0;ON;;;;;N;;;;;
+23D0;VERTICAL LINE EXTENSION;So;0;ON;;;;;N;;;;;
+23D1;METRICAL BREVE;So;0;ON;;;;;N;;;;;
+23D2;METRICAL LONG OVER SHORT;So;0;ON;;;;;N;;;;;
+23D3;METRICAL SHORT OVER LONG;So;0;ON;;;;;N;;;;;
+23D4;METRICAL LONG OVER TWO SHORTS;So;0;ON;;;;;N;;;;;
+23D5;METRICAL TWO SHORTS OVER LONG;So;0;ON;;;;;N;;;;;
+23D6;METRICAL TWO SHORTS JOINED;So;0;ON;;;;;N;;;;;
+23D7;METRICAL TRISEME;So;0;ON;;;;;N;;;;;
+23D8;METRICAL TETRASEME;So;0;ON;;;;;N;;;;;
+23D9;METRICAL PENTASEME;So;0;ON;;;;;N;;;;;
+23DA;EARTH GROUND;So;0;ON;;;;;N;;;;;
+23DB;FUSE;So;0;ON;;;;;N;;;;;
+23DC;TOP PARENTHESIS;Sm;0;ON;;;;;N;;;;;
+23DD;BOTTOM PARENTHESIS;Sm;0;ON;;;;;N;;;;;
+23DE;TOP CURLY BRACKET;Sm;0;ON;;;;;N;;;;;
+23DF;BOTTOM CURLY BRACKET;Sm;0;ON;;;;;N;;;;;
+23E0;TOP TORTOISE SHELL BRACKET;Sm;0;ON;;;;;N;;;;;
+23E1;BOTTOM TORTOISE SHELL BRACKET;Sm;0;ON;;;;;N;;;;;
+23E2;WHITE TRAPEZIUM;So;0;ON;;;;;N;;;;;
+23E3;BENZENE RING WITH CIRCLE;So;0;ON;;;;;N;;;;;
+23E4;STRAIGHTNESS;So;0;ON;;;;;N;;;;;
+23E5;FLATNESS;So;0;ON;;;;;N;;;;;
+23E6;AC CURRENT;So;0;ON;;;;;N;;;;;
+23E7;ELECTRICAL INTERSECTION;So;0;ON;;;;;N;;;;;
+23E8;DECIMAL EXPONENT SYMBOL;So;0;ON;;;;;N;;;;;
+23E9;BLACK RIGHT-POINTING DOUBLE TRIANGLE;So;0;ON;;;;;N;;;;;
+23EA;BLACK LEFT-POINTING DOUBLE TRIANGLE;So;0;ON;;;;;N;;;;;
+23EB;BLACK UP-POINTING DOUBLE TRIANGLE;So;0;ON;;;;;N;;;;;
+23EC;BLACK DOWN-POINTING DOUBLE TRIANGLE;So;0;ON;;;;;N;;;;;
+23ED;BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR;So;0;ON;;;;;N;;;;;
+23EE;BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR;So;0;ON;;;;;N;;;;;
+23EF;BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR;So;0;ON;;;;;N;;;;;
+23F0;ALARM CLOCK;So;0;ON;;;;;N;;;;;
+23F1;STOPWATCH;So;0;ON;;;;;N;;;;;
+23F2;TIMER CLOCK;So;0;ON;;;;;N;;;;;
+23F3;HOURGLASS WITH FLOWING SAND;So;0;ON;;;;;N;;;;;
+23F4;BLACK MEDIUM LEFT-POINTING TRIANGLE;So;0;ON;;;;;N;;;;;
+23F5;BLACK MEDIUM RIGHT-POINTING TRIANGLE;So;0;ON;;;;;N;;;;;
+23F6;BLACK MEDIUM UP-POINTING TRIANGLE;So;0;ON;;;;;N;;;;;
+23F7;BLACK MEDIUM DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;;;;;
+23F8;DOUBLE VERTICAL BAR;So;0;ON;;;;;N;;;;;
+23F9;BLACK SQUARE FOR STOP;So;0;ON;;;;;N;;;;;
+23FA;BLACK CIRCLE FOR RECORD;So;0;ON;;;;;N;;;;;
+23FB;POWER SYMBOL;So;0;ON;;;;;N;;;;;
+23FC;POWER ON-OFF SYMBOL;So;0;ON;;;;;N;;;;;
+23FD;POWER ON SYMBOL;So;0;ON;;;;;N;;;;;
+23FE;POWER SLEEP SYMBOL;So;0;ON;;;;;N;;;;;
+23FF;OBSERVER EYE SYMBOL;So;0;ON;;;;;N;;;;;
+2400;SYMBOL FOR NULL;So;0;ON;;;;;N;GRAPHIC FOR NULL;;;;
+2401;SYMBOL FOR START OF HEADING;So;0;ON;;;;;N;GRAPHIC FOR START OF HEADING;;;;
+2402;SYMBOL FOR START OF TEXT;So;0;ON;;;;;N;GRAPHIC FOR START OF TEXT;;;;
+2403;SYMBOL FOR END OF TEXT;So;0;ON;;;;;N;GRAPHIC FOR END OF TEXT;;;;
+2404;SYMBOL FOR END OF TRANSMISSION;So;0;ON;;;;;N;GRAPHIC FOR END OF TRANSMISSION;;;;
+2405;SYMBOL FOR ENQUIRY;So;0;ON;;;;;N;GRAPHIC FOR ENQUIRY;;;;
+2406;SYMBOL FOR ACKNOWLEDGE;So;0;ON;;;;;N;GRAPHIC FOR ACKNOWLEDGE;;;;
+2407;SYMBOL FOR BELL;So;0;ON;;;;;N;GRAPHIC FOR BELL;;;;
+2408;SYMBOL FOR BACKSPACE;So;0;ON;;;;;N;GRAPHIC FOR BACKSPACE;;;;
+2409;SYMBOL FOR HORIZONTAL TABULATION;So;0;ON;;;;;N;GRAPHIC FOR HORIZONTAL TABULATION;;;;
+240A;SYMBOL FOR LINE FEED;So;0;ON;;;;;N;GRAPHIC FOR LINE FEED;;;;
+240B;SYMBOL FOR VERTICAL TABULATION;So;0;ON;;;;;N;GRAPHIC FOR VERTICAL TABULATION;;;;
+240C;SYMBOL FOR FORM FEED;So;0;ON;;;;;N;GRAPHIC FOR FORM FEED;;;;
+240D;SYMBOL FOR CARRIAGE RETURN;So;0;ON;;;;;N;GRAPHIC FOR CARRIAGE RETURN;;;;
+240E;SYMBOL FOR SHIFT OUT;So;0;ON;;;;;N;GRAPHIC FOR SHIFT OUT;;;;
+240F;SYMBOL FOR SHIFT IN;So;0;ON;;;;;N;GRAPHIC FOR SHIFT IN;;;;
+2410;SYMBOL FOR DATA LINK ESCAPE;So;0;ON;;;;;N;GRAPHIC FOR DATA LINK ESCAPE;;;;
+2411;SYMBOL FOR DEVICE CONTROL ONE;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL ONE;;;;
+2412;SYMBOL FOR DEVICE CONTROL TWO;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL TWO;;;;
+2413;SYMBOL FOR DEVICE CONTROL THREE;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL THREE;;;;
+2414;SYMBOL FOR DEVICE CONTROL FOUR;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL FOUR;;;;
+2415;SYMBOL FOR NEGATIVE ACKNOWLEDGE;So;0;ON;;;;;N;GRAPHIC FOR NEGATIVE ACKNOWLEDGE;;;;
+2416;SYMBOL FOR SYNCHRONOUS IDLE;So;0;ON;;;;;N;GRAPHIC FOR SYNCHRONOUS IDLE;;;;
+2417;SYMBOL FOR END OF TRANSMISSION BLOCK;So;0;ON;;;;;N;GRAPHIC FOR END OF TRANSMISSION BLOCK;;;;
+2418;SYMBOL FOR CANCEL;So;0;ON;;;;;N;GRAPHIC FOR CANCEL;;;;
+2419;SYMBOL FOR END OF MEDIUM;So;0;ON;;;;;N;GRAPHIC FOR END OF MEDIUM;;;;
+241A;SYMBOL FOR SUBSTITUTE;So;0;ON;;;;;N;GRAPHIC FOR SUBSTITUTE;;;;
+241B;SYMBOL FOR ESCAPE;So;0;ON;;;;;N;GRAPHIC FOR ESCAPE;;;;
+241C;SYMBOL FOR FILE SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR FILE SEPARATOR;;;;
+241D;SYMBOL FOR GROUP SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR GROUP SEPARATOR;;;;
+241E;SYMBOL FOR RECORD SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR RECORD SEPARATOR;;;;
+241F;SYMBOL FOR UNIT SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR UNIT SEPARATOR;;;;
+2420;SYMBOL FOR SPACE;So;0;ON;;;;;N;GRAPHIC FOR SPACE;;;;
+2421;SYMBOL FOR DELETE;So;0;ON;;;;;N;GRAPHIC FOR DELETE;;;;
+2422;BLANK SYMBOL;So;0;ON;;;;;N;BLANK;;;;
+2423;OPEN BOX;So;0;ON;;;;;N;;;;;
+2424;SYMBOL FOR NEWLINE;So;0;ON;;;;;N;GRAPHIC FOR NEWLINE;;;;
+2425;SYMBOL FOR DELETE FORM TWO;So;0;ON;;;;;N;;;;;
+2426;SYMBOL FOR SUBSTITUTE FORM TWO;So;0;ON;;;;;N;;;;;
+2440;OCR HOOK;So;0;ON;;;;;N;;;;;
+2441;OCR CHAIR;So;0;ON;;;;;N;;;;;
+2442;OCR FORK;So;0;ON;;;;;N;;;;;
+2443;OCR INVERTED FORK;So;0;ON;;;;;N;;;;;
+2444;OCR BELT BUCKLE;So;0;ON;;;;;N;;;;;
+2445;OCR BOW TIE;So;0;ON;;;;;N;;;;;
+2446;OCR BRANCH BANK IDENTIFICATION;So;0;ON;;;;;N;;;;;
+2447;OCR AMOUNT OF CHECK;So;0;ON;;;;;N;;;;;
+2448;OCR DASH;So;0;ON;;;;;N;;;;;
+2449;OCR CUSTOMER ACCOUNT NUMBER;So;0;ON;;;;;N;;;;;
+244A;OCR DOUBLE BACKSLASH;So;0;ON;;;;;N;;;;;
+2460;CIRCLED DIGIT ONE;No;0;ON;<circle> 0031;;1;1;N;;;;;
+2461;CIRCLED DIGIT TWO;No;0;ON;<circle> 0032;;2;2;N;;;;;
+2462;CIRCLED DIGIT THREE;No;0;ON;<circle> 0033;;3;3;N;;;;;
+2463;CIRCLED DIGIT FOUR;No;0;ON;<circle> 0034;;4;4;N;;;;;
+2464;CIRCLED DIGIT FIVE;No;0;ON;<circle> 0035;;5;5;N;;;;;
+2465;CIRCLED DIGIT SIX;No;0;ON;<circle> 0036;;6;6;N;;;;;
+2466;CIRCLED DIGIT SEVEN;No;0;ON;<circle> 0037;;7;7;N;;;;;
+2467;CIRCLED DIGIT EIGHT;No;0;ON;<circle> 0038;;8;8;N;;;;;
+2468;CIRCLED DIGIT NINE;No;0;ON;<circle> 0039;;9;9;N;;;;;
+2469;CIRCLED NUMBER TEN;No;0;ON;<circle> 0031 0030;;;10;N;;;;;
+246A;CIRCLED NUMBER ELEVEN;No;0;ON;<circle> 0031 0031;;;11;N;;;;;
+246B;CIRCLED NUMBER TWELVE;No;0;ON;<circle> 0031 0032;;;12;N;;;;;
+246C;CIRCLED NUMBER THIRTEEN;No;0;ON;<circle> 0031 0033;;;13;N;;;;;
+246D;CIRCLED NUMBER FOURTEEN;No;0;ON;<circle> 0031 0034;;;14;N;;;;;
+246E;CIRCLED NUMBER FIFTEEN;No;0;ON;<circle> 0031 0035;;;15;N;;;;;
+246F;CIRCLED NUMBER SIXTEEN;No;0;ON;<circle> 0031 0036;;;16;N;;;;;
+2470;CIRCLED NUMBER SEVENTEEN;No;0;ON;<circle> 0031 0037;;;17;N;;;;;
+2471;CIRCLED NUMBER EIGHTEEN;No;0;ON;<circle> 0031 0038;;;18;N;;;;;
+2472;CIRCLED NUMBER NINETEEN;No;0;ON;<circle> 0031 0039;;;19;N;;;;;
+2473;CIRCLED NUMBER TWENTY;No;0;ON;<circle> 0032 0030;;;20;N;;;;;
+2474;PARENTHESIZED DIGIT ONE;No;0;ON;<compat> 0028 0031 0029;;1;1;N;;;;;
+2475;PARENTHESIZED DIGIT TWO;No;0;ON;<compat> 0028 0032 0029;;2;2;N;;;;;
+2476;PARENTHESIZED DIGIT THREE;No;0;ON;<compat> 0028 0033 0029;;3;3;N;;;;;
+2477;PARENTHESIZED DIGIT FOUR;No;0;ON;<compat> 0028 0034 0029;;4;4;N;;;;;
+2478;PARENTHESIZED DIGIT FIVE;No;0;ON;<compat> 0028 0035 0029;;5;5;N;;;;;
+2479;PARENTHESIZED DIGIT SIX;No;0;ON;<compat> 0028 0036 0029;;6;6;N;;;;;
+247A;PARENTHESIZED DIGIT SEVEN;No;0;ON;<compat> 0028 0037 0029;;7;7;N;;;;;
+247B;PARENTHESIZED DIGIT EIGHT;No;0;ON;<compat> 0028 0038 0029;;8;8;N;;;;;
+247C;PARENTHESIZED DIGIT NINE;No;0;ON;<compat> 0028 0039 0029;;9;9;N;;;;;
+247D;PARENTHESIZED NUMBER TEN;No;0;ON;<compat> 0028 0031 0030 0029;;;10;N;;;;;
+247E;PARENTHESIZED NUMBER ELEVEN;No;0;ON;<compat> 0028 0031 0031 0029;;;11;N;;;;;
+247F;PARENTHESIZED NUMBER TWELVE;No;0;ON;<compat> 0028 0031 0032 0029;;;12;N;;;;;
+2480;PARENTHESIZED NUMBER THIRTEEN;No;0;ON;<compat> 0028 0031 0033 0029;;;13;N;;;;;
+2481;PARENTHESIZED NUMBER FOURTEEN;No;0;ON;<compat> 0028 0031 0034 0029;;;14;N;;;;;
+2482;PARENTHESIZED NUMBER FIFTEEN;No;0;ON;<compat> 0028 0031 0035 0029;;;15;N;;;;;
+2483;PARENTHESIZED NUMBER SIXTEEN;No;0;ON;<compat> 0028 0031 0036 0029;;;16;N;;;;;
+2484;PARENTHESIZED NUMBER SEVENTEEN;No;0;ON;<compat> 0028 0031 0037 0029;;;17;N;;;;;
+2485;PARENTHESIZED NUMBER EIGHTEEN;No;0;ON;<compat> 0028 0031 0038 0029;;;18;N;;;;;
+2486;PARENTHESIZED NUMBER NINETEEN;No;0;ON;<compat> 0028 0031 0039 0029;;;19;N;;;;;
+2487;PARENTHESIZED NUMBER TWENTY;No;0;ON;<compat> 0028 0032 0030 0029;;;20;N;;;;;
+2488;DIGIT ONE FULL STOP;No;0;EN;<compat> 0031 002E;;1;1;N;DIGIT ONE PERIOD;;;;
+2489;DIGIT TWO FULL STOP;No;0;EN;<compat> 0032 002E;;2;2;N;DIGIT TWO PERIOD;;;;
+248A;DIGIT THREE FULL STOP;No;0;EN;<compat> 0033 002E;;3;3;N;DIGIT THREE PERIOD;;;;
+248B;DIGIT FOUR FULL STOP;No;0;EN;<compat> 0034 002E;;4;4;N;DIGIT FOUR PERIOD;;;;
+248C;DIGIT FIVE FULL STOP;No;0;EN;<compat> 0035 002E;;5;5;N;DIGIT FIVE PERIOD;;;;
+248D;DIGIT SIX FULL STOP;No;0;EN;<compat> 0036 002E;;6;6;N;DIGIT SIX PERIOD;;;;
+248E;DIGIT SEVEN FULL STOP;No;0;EN;<compat> 0037 002E;;7;7;N;DIGIT SEVEN PERIOD;;;;
+248F;DIGIT EIGHT FULL STOP;No;0;EN;<compat> 0038 002E;;8;8;N;DIGIT EIGHT PERIOD;;;;
+2490;DIGIT NINE FULL STOP;No;0;EN;<compat> 0039 002E;;9;9;N;DIGIT NINE PERIOD;;;;
+2491;NUMBER TEN FULL STOP;No;0;EN;<compat> 0031 0030 002E;;;10;N;NUMBER TEN PERIOD;;;;
+2492;NUMBER ELEVEN FULL STOP;No;0;EN;<compat> 0031 0031 002E;;;11;N;NUMBER ELEVEN PERIOD;;;;
+2493;NUMBER TWELVE FULL STOP;No;0;EN;<compat> 0031 0032 002E;;;12;N;NUMBER TWELVE PERIOD;;;;
+2494;NUMBER THIRTEEN FULL STOP;No;0;EN;<compat> 0031 0033 002E;;;13;N;NUMBER THIRTEEN PERIOD;;;;
+2495;NUMBER FOURTEEN FULL STOP;No;0;EN;<compat> 0031 0034 002E;;;14;N;NUMBER FOURTEEN PERIOD;;;;
+2496;NUMBER FIFTEEN FULL STOP;No;0;EN;<compat> 0031 0035 002E;;;15;N;NUMBER FIFTEEN PERIOD;;;;
+2497;NUMBER SIXTEEN FULL STOP;No;0;EN;<compat> 0031 0036 002E;;;16;N;NUMBER SIXTEEN PERIOD;;;;
+2498;NUMBER SEVENTEEN FULL STOP;No;0;EN;<compat> 0031 0037 002E;;;17;N;NUMBER SEVENTEEN PERIOD;;;;
+2499;NUMBER EIGHTEEN FULL STOP;No;0;EN;<compat> 0031 0038 002E;;;18;N;NUMBER EIGHTEEN PERIOD;;;;
+249A;NUMBER NINETEEN FULL STOP;No;0;EN;<compat> 0031 0039 002E;;;19;N;NUMBER NINETEEN PERIOD;;;;
+249B;NUMBER TWENTY FULL STOP;No;0;EN;<compat> 0032 0030 002E;;;20;N;NUMBER TWENTY PERIOD;;;;
+249C;PARENTHESIZED LATIN SMALL LETTER A;So;0;L;<compat> 0028 0061 0029;;;;N;;;;;
+249D;PARENTHESIZED LATIN SMALL LETTER B;So;0;L;<compat> 0028 0062 0029;;;;N;;;;;
+249E;PARENTHESIZED LATIN SMALL LETTER C;So;0;L;<compat> 0028 0063 0029;;;;N;;;;;
+249F;PARENTHESIZED LATIN SMALL LETTER D;So;0;L;<compat> 0028 0064 0029;;;;N;;;;;
+24A0;PARENTHESIZED LATIN SMALL LETTER E;So;0;L;<compat> 0028 0065 0029;;;;N;;;;;
+24A1;PARENTHESIZED LATIN SMALL LETTER F;So;0;L;<compat> 0028 0066 0029;;;;N;;;;;
+24A2;PARENTHESIZED LATIN SMALL LETTER G;So;0;L;<compat> 0028 0067 0029;;;;N;;;;;
+24A3;PARENTHESIZED LATIN SMALL LETTER H;So;0;L;<compat> 0028 0068 0029;;;;N;;;;;
+24A4;PARENTHESIZED LATIN SMALL LETTER I;So;0;L;<compat> 0028 0069 0029;;;;N;;;;;
+24A5;PARENTHESIZED LATIN SMALL LETTER J;So;0;L;<compat> 0028 006A 0029;;;;N;;;;;
+24A6;PARENTHESIZED LATIN SMALL LETTER K;So;0;L;<compat> 0028 006B 0029;;;;N;;;;;
+24A7;PARENTHESIZED LATIN SMALL LETTER L;So;0;L;<compat> 0028 006C 0029;;;;N;;;;;
+24A8;PARENTHESIZED LATIN SMALL LETTER M;So;0;L;<compat> 0028 006D 0029;;;;N;;;;;
+24A9;PARENTHESIZED LATIN SMALL LETTER N;So;0;L;<compat> 0028 006E 0029;;;;N;;;;;
+24AA;PARENTHESIZED LATIN SMALL LETTER O;So;0;L;<compat> 0028 006F 0029;;;;N;;;;;
+24AB;PARENTHESIZED LATIN SMALL LETTER P;So;0;L;<compat> 0028 0070 0029;;;;N;;;;;
+24AC;PARENTHESIZED LATIN SMALL LETTER Q;So;0;L;<compat> 0028 0071 0029;;;;N;;;;;
+24AD;PARENTHESIZED LATIN SMALL LETTER R;So;0;L;<compat> 0028 0072 0029;;;;N;;;;;
+24AE;PARENTHESIZED LATIN SMALL LETTER S;So;0;L;<compat> 0028 0073 0029;;;;N;;;;;
+24AF;PARENTHESIZED LATIN SMALL LETTER T;So;0;L;<compat> 0028 0074 0029;;;;N;;;;;
+24B0;PARENTHESIZED LATIN SMALL LETTER U;So;0;L;<compat> 0028 0075 0029;;;;N;;;;;
+24B1;PARENTHESIZED LATIN SMALL LETTER V;So;0;L;<compat> 0028 0076 0029;;;;N;;;;;
+24B2;PARENTHESIZED LATIN SMALL LETTER W;So;0;L;<compat> 0028 0077 0029;;;;N;;;;;
+24B3;PARENTHESIZED LATIN SMALL LETTER X;So;0;L;<compat> 0028 0078 0029;;;;N;;;;;
+24B4;PARENTHESIZED LATIN SMALL LETTER Y;So;0;L;<compat> 0028 0079 0029;;;;N;;;;;
+24B5;PARENTHESIZED LATIN SMALL LETTER Z;So;0;L;<compat> 0028 007A 0029;;;;N;;;;;
+24B6;CIRCLED LATIN CAPITAL LETTER A;So;0;L;<circle> 0041;;;;N;;;;24D0;
+24B7;CIRCLED LATIN CAPITAL LETTER B;So;0;L;<circle> 0042;;;;N;;;;24D1;
+24B8;CIRCLED LATIN CAPITAL LETTER C;So;0;L;<circle> 0043;;;;N;;;;24D2;
+24B9;CIRCLED LATIN CAPITAL LETTER D;So;0;L;<circle> 0044;;;;N;;;;24D3;
+24BA;CIRCLED LATIN CAPITAL LETTER E;So;0;L;<circle> 0045;;;;N;;;;24D4;
+24BB;CIRCLED LATIN CAPITAL LETTER F;So;0;L;<circle> 0046;;;;N;;;;24D5;
+24BC;CIRCLED LATIN CAPITAL LETTER G;So;0;L;<circle> 0047;;;;N;;;;24D6;
+24BD;CIRCLED LATIN CAPITAL LETTER H;So;0;L;<circle> 0048;;;;N;;;;24D7;
+24BE;CIRCLED LATIN CAPITAL LETTER I;So;0;L;<circle> 0049;;;;N;;;;24D8;
+24BF;CIRCLED LATIN CAPITAL LETTER J;So;0;L;<circle> 004A;;;;N;;;;24D9;
+24C0;CIRCLED LATIN CAPITAL LETTER K;So;0;L;<circle> 004B;;;;N;;;;24DA;
+24C1;CIRCLED LATIN CAPITAL LETTER L;So;0;L;<circle> 004C;;;;N;;;;24DB;
+24C2;CIRCLED LATIN CAPITAL LETTER M;So;0;L;<circle> 004D;;;;N;;;;24DC;
+24C3;CIRCLED LATIN CAPITAL LETTER N;So;0;L;<circle> 004E;;;;N;;;;24DD;
+24C4;CIRCLED LATIN CAPITAL LETTER O;So;0;L;<circle> 004F;;;;N;;;;24DE;
+24C5;CIRCLED LATIN CAPITAL LETTER P;So;0;L;<circle> 0050;;;;N;;;;24DF;
+24C6;CIRCLED LATIN CAPITAL LETTER Q;So;0;L;<circle> 0051;;;;N;;;;24E0;
+24C7;CIRCLED LATIN CAPITAL LETTER R;So;0;L;<circle> 0052;;;;N;;;;24E1;
+24C8;CIRCLED LATIN CAPITAL LETTER S;So;0;L;<circle> 0053;;;;N;;;;24E2;
+24C9;CIRCLED LATIN CAPITAL LETTER T;So;0;L;<circle> 0054;;;;N;;;;24E3;
+24CA;CIRCLED LATIN CAPITAL LETTER U;So;0;L;<circle> 0055;;;;N;;;;24E4;
+24CB;CIRCLED LATIN CAPITAL LETTER V;So;0;L;<circle> 0056;;;;N;;;;24E5;
+24CC;CIRCLED LATIN CAPITAL LETTER W;So;0;L;<circle> 0057;;;;N;;;;24E6;
+24CD;CIRCLED LATIN CAPITAL LETTER X;So;0;L;<circle> 0058;;;;N;;;;24E7;
+24CE;CIRCLED LATIN CAPITAL LETTER Y;So;0;L;<circle> 0059;;;;N;;;;24E8;
+24CF;CIRCLED LATIN CAPITAL LETTER Z;So;0;L;<circle> 005A;;;;N;;;;24E9;
+24D0;CIRCLED LATIN SMALL LETTER A;So;0;L;<circle> 0061;;;;N;;;24B6;;24B6
+24D1;CIRCLED LATIN SMALL LETTER B;So;0;L;<circle> 0062;;;;N;;;24B7;;24B7
+24D2;CIRCLED LATIN SMALL LETTER C;So;0;L;<circle> 0063;;;;N;;;24B8;;24B8
+24D3;CIRCLED LATIN SMALL LETTER D;So;0;L;<circle> 0064;;;;N;;;24B9;;24B9
+24D4;CIRCLED LATIN SMALL LETTER E;So;0;L;<circle> 0065;;;;N;;;24BA;;24BA
+24D5;CIRCLED LATIN SMALL LETTER F;So;0;L;<circle> 0066;;;;N;;;24BB;;24BB
+24D6;CIRCLED LATIN SMALL LETTER G;So;0;L;<circle> 0067;;;;N;;;24BC;;24BC
+24D7;CIRCLED LATIN SMALL LETTER H;So;0;L;<circle> 0068;;;;N;;;24BD;;24BD
+24D8;CIRCLED LATIN SMALL LETTER I;So;0;L;<circle> 0069;;;;N;;;24BE;;24BE
+24D9;CIRCLED LATIN SMALL LETTER J;So;0;L;<circle> 006A;;;;N;;;24BF;;24BF
+24DA;CIRCLED LATIN SMALL LETTER K;So;0;L;<circle> 006B;;;;N;;;24C0;;24C0
+24DB;CIRCLED LATIN SMALL LETTER L;So;0;L;<circle> 006C;;;;N;;;24C1;;24C1
+24DC;CIRCLED LATIN SMALL LETTER M;So;0;L;<circle> 006D;;;;N;;;24C2;;24C2
+24DD;CIRCLED LATIN SMALL LETTER N;So;0;L;<circle> 006E;;;;N;;;24C3;;24C3
+24DE;CIRCLED LATIN SMALL LETTER O;So;0;L;<circle> 006F;;;;N;;;24C4;;24C4
+24DF;CIRCLED LATIN SMALL LETTER P;So;0;L;<circle> 0070;;;;N;;;24C5;;24C5
+24E0;CIRCLED LATIN SMALL LETTER Q;So;0;L;<circle> 0071;;;;N;;;24C6;;24C6
+24E1;CIRCLED LATIN SMALL LETTER R;So;0;L;<circle> 0072;;;;N;;;24C7;;24C7
+24E2;CIRCLED LATIN SMALL LETTER S;So;0;L;<circle> 0073;;;;N;;;24C8;;24C8
+24E3;CIRCLED LATIN SMALL LETTER T;So;0;L;<circle> 0074;;;;N;;;24C9;;24C9
+24E4;CIRCLED LATIN SMALL LETTER U;So;0;L;<circle> 0075;;;;N;;;24CA;;24CA
+24E5;CIRCLED LATIN SMALL LETTER V;So;0;L;<circle> 0076;;;;N;;;24CB;;24CB
+24E6;CIRCLED LATIN SMALL LETTER W;So;0;L;<circle> 0077;;;;N;;;24CC;;24CC
+24E7;CIRCLED LATIN SMALL LETTER X;So;0;L;<circle> 0078;;;;N;;;24CD;;24CD
+24E8;CIRCLED LATIN SMALL LETTER Y;So;0;L;<circle> 0079;;;;N;;;24CE;;24CE
+24E9;CIRCLED LATIN SMALL LETTER Z;So;0;L;<circle> 007A;;;;N;;;24CF;;24CF
+24EA;CIRCLED DIGIT ZERO;No;0;ON;<circle> 0030;;0;0;N;;;;;
+24EB;NEGATIVE CIRCLED NUMBER ELEVEN;No;0;ON;;;;11;N;;;;;
+24EC;NEGATIVE CIRCLED NUMBER TWELVE;No;0;ON;;;;12;N;;;;;
+24ED;NEGATIVE CIRCLED NUMBER THIRTEEN;No;0;ON;;;;13;N;;;;;
+24EE;NEGATIVE CIRCLED NUMBER FOURTEEN;No;0;ON;;;;14;N;;;;;
+24EF;NEGATIVE CIRCLED NUMBER FIFTEEN;No;0;ON;;;;15;N;;;;;
+24F0;NEGATIVE CIRCLED NUMBER SIXTEEN;No;0;ON;;;;16;N;;;;;
+24F1;NEGATIVE CIRCLED NUMBER SEVENTEEN;No;0;ON;;;;17;N;;;;;
+24F2;NEGATIVE CIRCLED NUMBER EIGHTEEN;No;0;ON;;;;18;N;;;;;
+24F3;NEGATIVE CIRCLED NUMBER NINETEEN;No;0;ON;;;;19;N;;;;;
+24F4;NEGATIVE CIRCLED NUMBER TWENTY;No;0;ON;;;;20;N;;;;;
+24F5;DOUBLE CIRCLED DIGIT ONE;No;0;ON;;;1;1;N;;;;;
+24F6;DOUBLE CIRCLED DIGIT TWO;No;0;ON;;;2;2;N;;;;;
+24F7;DOUBLE CIRCLED DIGIT THREE;No;0;ON;;;3;3;N;;;;;
+24F8;DOUBLE CIRCLED DIGIT FOUR;No;0;ON;;;4;4;N;;;;;
+24F9;DOUBLE CIRCLED DIGIT FIVE;No;0;ON;;;5;5;N;;;;;
+24FA;DOUBLE CIRCLED DIGIT SIX;No;0;ON;;;6;6;N;;;;;
+24FB;DOUBLE CIRCLED DIGIT SEVEN;No;0;ON;;;7;7;N;;;;;
+24FC;DOUBLE CIRCLED DIGIT EIGHT;No;0;ON;;;8;8;N;;;;;
+24FD;DOUBLE CIRCLED DIGIT NINE;No;0;ON;;;9;9;N;;;;;
+24FE;DOUBLE CIRCLED NUMBER TEN;No;0;ON;;;;10;N;;;;;
+24FF;NEGATIVE CIRCLED DIGIT ZERO;No;0;ON;;;0;0;N;;;;;
+2500;BOX DRAWINGS LIGHT HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT HORIZONTAL;;;;
+2501;BOX DRAWINGS HEAVY HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY HORIZONTAL;;;;
+2502;BOX DRAWINGS LIGHT VERTICAL;So;0;ON;;;;;N;FORMS LIGHT VERTICAL;;;;
+2503;BOX DRAWINGS HEAVY VERTICAL;So;0;ON;;;;;N;FORMS HEAVY VERTICAL;;;;
+2504;BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT TRIPLE DASH HORIZONTAL;;;;
+2505;BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY TRIPLE DASH HORIZONTAL;;;;
+2506;BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT TRIPLE DASH VERTICAL;;;;
+2507;BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY TRIPLE DASH VERTICAL;;;;
+2508;BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT QUADRUPLE DASH HORIZONTAL;;;;
+2509;BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY QUADRUPLE DASH HORIZONTAL;;;;
+250A;BOX DRAWINGS LIGHT QUADRUPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT QUADRUPLE DASH VERTICAL;;;;
+250B;BOX DRAWINGS HEAVY QUADRUPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY QUADRUPLE DASH VERTICAL;;;;
+250C;BOX DRAWINGS LIGHT DOWN AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT DOWN AND RIGHT;;;;
+250D;BOX DRAWINGS DOWN LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND RIGHT HEAVY;;;;
+250E;BOX DRAWINGS DOWN HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND RIGHT LIGHT;;;;
+250F;BOX DRAWINGS HEAVY DOWN AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY DOWN AND RIGHT;;;;
+2510;BOX DRAWINGS LIGHT DOWN AND LEFT;So;0;ON;;;;;N;FORMS LIGHT DOWN AND LEFT;;;;
+2511;BOX DRAWINGS DOWN LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND LEFT HEAVY;;;;
+2512;BOX DRAWINGS DOWN HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND LEFT LIGHT;;;;
+2513;BOX DRAWINGS HEAVY DOWN AND LEFT;So;0;ON;;;;;N;FORMS HEAVY DOWN AND LEFT;;;;
+2514;BOX DRAWINGS LIGHT UP AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT UP AND RIGHT;;;;
+2515;BOX DRAWINGS UP LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND RIGHT HEAVY;;;;
+2516;BOX DRAWINGS UP HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND RIGHT LIGHT;;;;
+2517;BOX DRAWINGS HEAVY UP AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY UP AND RIGHT;;;;
+2518;BOX DRAWINGS LIGHT UP AND LEFT;So;0;ON;;;;;N;FORMS LIGHT UP AND LEFT;;;;
+2519;BOX DRAWINGS UP LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND LEFT HEAVY;;;;
+251A;BOX DRAWINGS UP HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND LEFT LIGHT;;;;
+251B;BOX DRAWINGS HEAVY UP AND LEFT;So;0;ON;;;;;N;FORMS HEAVY UP AND LEFT;;;;
+251C;BOX DRAWINGS LIGHT VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND RIGHT;;;;
+251D;BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND RIGHT HEAVY;;;;
+251E;BOX DRAWINGS UP HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND RIGHT DOWN LIGHT;;;;
+251F;BOX DRAWINGS DOWN HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND RIGHT UP LIGHT;;;;
+2520;BOX DRAWINGS VERTICAL HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND RIGHT LIGHT;;;;
+2521;BOX DRAWINGS DOWN LIGHT AND RIGHT UP HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND RIGHT UP HEAVY;;;;
+2522;BOX DRAWINGS UP LIGHT AND RIGHT DOWN HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND RIGHT DOWN HEAVY;;;;
+2523;BOX DRAWINGS HEAVY VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND RIGHT;;;;
+2524;BOX DRAWINGS LIGHT VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND LEFT;;;;
+2525;BOX DRAWINGS VERTICAL LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND LEFT HEAVY;;;;
+2526;BOX DRAWINGS UP HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND LEFT DOWN LIGHT;;;;
+2527;BOX DRAWINGS DOWN HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND LEFT UP LIGHT;;;;
+2528;BOX DRAWINGS VERTICAL HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND LEFT LIGHT;;;;
+2529;BOX DRAWINGS DOWN LIGHT AND LEFT UP HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND LEFT UP HEAVY;;;;
+252A;BOX DRAWINGS UP LIGHT AND LEFT DOWN HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND LEFT DOWN HEAVY;;;;
+252B;BOX DRAWINGS HEAVY VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND LEFT;;;;
+252C;BOX DRAWINGS LIGHT DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT DOWN AND HORIZONTAL;;;;
+252D;BOX DRAWINGS LEFT HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT DOWN LIGHT;;;;
+252E;BOX DRAWINGS RIGHT HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT DOWN LIGHT;;;;
+252F;BOX DRAWINGS DOWN LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND HORIZONTAL HEAVY;;;;
+2530;BOX DRAWINGS DOWN HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND HORIZONTAL LIGHT;;;;
+2531;BOX DRAWINGS RIGHT LIGHT AND LEFT DOWN HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT DOWN HEAVY;;;;
+2532;BOX DRAWINGS LEFT LIGHT AND RIGHT DOWN HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT DOWN HEAVY;;;;
+2533;BOX DRAWINGS HEAVY DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY DOWN AND HORIZONTAL;;;;
+2534;BOX DRAWINGS LIGHT UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT UP AND HORIZONTAL;;;;
+2535;BOX DRAWINGS LEFT HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT UP LIGHT;;;;
+2536;BOX DRAWINGS RIGHT HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT UP LIGHT;;;;
+2537;BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND HORIZONTAL HEAVY;;;;
+2538;BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND HORIZONTAL LIGHT;;;;
+2539;BOX DRAWINGS RIGHT LIGHT AND LEFT UP HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT UP HEAVY;;;;
+253A;BOX DRAWINGS LEFT LIGHT AND RIGHT UP HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT UP HEAVY;;;;
+253B;BOX DRAWINGS HEAVY UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY UP AND HORIZONTAL;;;;
+253C;BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND HORIZONTAL;;;;
+253D;BOX DRAWINGS LEFT HEAVY AND RIGHT VERTICAL LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT VERTICAL LIGHT;;;;
+253E;BOX DRAWINGS RIGHT HEAVY AND LEFT VERTICAL LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT VERTICAL LIGHT;;;;
+253F;BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND HORIZONTAL HEAVY;;;;
+2540;BOX DRAWINGS UP HEAVY AND DOWN HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND DOWN HORIZONTAL LIGHT;;;;
+2541;BOX DRAWINGS DOWN HEAVY AND UP HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND UP HORIZONTAL LIGHT;;;;
+2542;BOX DRAWINGS VERTICAL HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND HORIZONTAL LIGHT;;;;
+2543;BOX DRAWINGS LEFT UP HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS LEFT UP HEAVY AND RIGHT DOWN LIGHT;;;;
+2544;BOX DRAWINGS RIGHT UP HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS RIGHT UP HEAVY AND LEFT DOWN LIGHT;;;;
+2545;BOX DRAWINGS LEFT DOWN HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS LEFT DOWN HEAVY AND RIGHT UP LIGHT;;;;
+2546;BOX DRAWINGS RIGHT DOWN HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS RIGHT DOWN HEAVY AND LEFT UP LIGHT;;;;
+2547;BOX DRAWINGS DOWN LIGHT AND UP HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND UP HORIZONTAL HEAVY;;;;
+2548;BOX DRAWINGS UP LIGHT AND DOWN HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND DOWN HORIZONTAL HEAVY;;;;
+2549;BOX DRAWINGS RIGHT LIGHT AND LEFT VERTICAL HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT VERTICAL HEAVY;;;;
+254A;BOX DRAWINGS LEFT LIGHT AND RIGHT VERTICAL HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT VERTICAL HEAVY;;;;
+254B;BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND HORIZONTAL;;;;
+254C;BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT DOUBLE DASH HORIZONTAL;;;;
+254D;BOX DRAWINGS HEAVY DOUBLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY DOUBLE DASH HORIZONTAL;;;;
+254E;BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT DOUBLE DASH VERTICAL;;;;
+254F;BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY DOUBLE DASH VERTICAL;;;;
+2550;BOX DRAWINGS DOUBLE HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE HORIZONTAL;;;;
+2551;BOX DRAWINGS DOUBLE VERTICAL;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL;;;;
+2552;BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND RIGHT DOUBLE;;;;
+2553;BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND RIGHT SINGLE;;;;
+2554;BOX DRAWINGS DOUBLE DOWN AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND RIGHT;;;;
+2555;BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND LEFT DOUBLE;;;;
+2556;BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND LEFT SINGLE;;;;
+2557;BOX DRAWINGS DOUBLE DOWN AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND LEFT;;;;
+2558;BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND RIGHT DOUBLE;;;;
+2559;BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND RIGHT SINGLE;;;;
+255A;BOX DRAWINGS DOUBLE UP AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE UP AND RIGHT;;;;
+255B;BOX DRAWINGS UP SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND LEFT DOUBLE;;;;
+255C;BOX DRAWINGS UP DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND LEFT SINGLE;;;;
+255D;BOX DRAWINGS DOUBLE UP AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE UP AND LEFT;;;;
+255E;BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND RIGHT DOUBLE;;;;
+255F;BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND RIGHT SINGLE;;;;
+2560;BOX DRAWINGS DOUBLE VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND RIGHT;;;;
+2561;BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND LEFT DOUBLE;;;;
+2562;BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND LEFT SINGLE;;;;
+2563;BOX DRAWINGS DOUBLE VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND LEFT;;;;
+2564;BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND HORIZONTAL DOUBLE;;;;
+2565;BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND HORIZONTAL SINGLE;;;;
+2566;BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND HORIZONTAL;;;;
+2567;BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND HORIZONTAL DOUBLE;;;;
+2568;BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND HORIZONTAL SINGLE;;;;
+2569;BOX DRAWINGS DOUBLE UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE UP AND HORIZONTAL;;;;
+256A;BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND HORIZONTAL DOUBLE;;;;
+256B;BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND HORIZONTAL SINGLE;;;;
+256C;BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND HORIZONTAL;;;;
+256D;BOX DRAWINGS LIGHT ARC DOWN AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT ARC DOWN AND RIGHT;;;;
+256E;BOX DRAWINGS LIGHT ARC DOWN AND LEFT;So;0;ON;;;;;N;FORMS LIGHT ARC DOWN AND LEFT;;;;
+256F;BOX DRAWINGS LIGHT ARC UP AND LEFT;So;0;ON;;;;;N;FORMS LIGHT ARC UP AND LEFT;;;;
+2570;BOX DRAWINGS LIGHT ARC UP AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT ARC UP AND RIGHT;;;;
+2571;BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT;;;;
+2572;BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT;;;;
+2573;BOX DRAWINGS LIGHT DIAGONAL CROSS;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL CROSS;;;;
+2574;BOX DRAWINGS LIGHT LEFT;So;0;ON;;;;;N;FORMS LIGHT LEFT;;;;
+2575;BOX DRAWINGS LIGHT UP;So;0;ON;;;;;N;FORMS LIGHT UP;;;;
+2576;BOX DRAWINGS LIGHT RIGHT;So;0;ON;;;;;N;FORMS LIGHT RIGHT;;;;
+2577;BOX DRAWINGS LIGHT DOWN;So;0;ON;;;;;N;FORMS LIGHT DOWN;;;;
+2578;BOX DRAWINGS HEAVY LEFT;So;0;ON;;;;;N;FORMS HEAVY LEFT;;;;
+2579;BOX DRAWINGS HEAVY UP;So;0;ON;;;;;N;FORMS HEAVY UP;;;;
+257A;BOX DRAWINGS HEAVY RIGHT;So;0;ON;;;;;N;FORMS HEAVY RIGHT;;;;
+257B;BOX DRAWINGS HEAVY DOWN;So;0;ON;;;;;N;FORMS HEAVY DOWN;;;;
+257C;BOX DRAWINGS LIGHT LEFT AND HEAVY RIGHT;So;0;ON;;;;;N;FORMS LIGHT LEFT AND HEAVY RIGHT;;;;
+257D;BOX DRAWINGS LIGHT UP AND HEAVY DOWN;So;0;ON;;;;;N;FORMS LIGHT UP AND HEAVY DOWN;;;;
+257E;BOX DRAWINGS HEAVY LEFT AND LIGHT RIGHT;So;0;ON;;;;;N;FORMS HEAVY LEFT AND LIGHT RIGHT;;;;
+257F;BOX DRAWINGS HEAVY UP AND LIGHT DOWN;So;0;ON;;;;;N;FORMS HEAVY UP AND LIGHT DOWN;;;;
+2580;UPPER HALF BLOCK;So;0;ON;;;;;N;;;;;
+2581;LOWER ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;;
+2582;LOWER ONE QUARTER BLOCK;So;0;ON;;;;;N;;;;;
+2583;LOWER THREE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+2584;LOWER HALF BLOCK;So;0;ON;;;;;N;;;;;
+2585;LOWER FIVE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+2586;LOWER THREE QUARTERS BLOCK;So;0;ON;;;;;N;LOWER THREE QUARTER BLOCK;;;;
+2587;LOWER SEVEN EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+2588;FULL BLOCK;So;0;ON;;;;;N;;;;;
+2589;LEFT SEVEN EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+258A;LEFT THREE QUARTERS BLOCK;So;0;ON;;;;;N;LEFT THREE QUARTER BLOCK;;;;
+258B;LEFT FIVE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+258C;LEFT HALF BLOCK;So;0;ON;;;;;N;;;;;
+258D;LEFT THREE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+258E;LEFT ONE QUARTER BLOCK;So;0;ON;;;;;N;;;;;
+258F;LEFT ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;;
+2590;RIGHT HALF BLOCK;So;0;ON;;;;;N;;;;;
+2591;LIGHT SHADE;So;0;ON;;;;;N;;;;;
+2592;MEDIUM SHADE;So;0;ON;;;;;N;;;;;
+2593;DARK SHADE;So;0;ON;;;;;N;;;;;
+2594;UPPER ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;;
+2595;RIGHT ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;;
+2596;QUADRANT LOWER LEFT;So;0;ON;;;;;N;;;;;
+2597;QUADRANT LOWER RIGHT;So;0;ON;;;;;N;;;;;
+2598;QUADRANT UPPER LEFT;So;0;ON;;;;;N;;;;;
+2599;QUADRANT UPPER LEFT AND LOWER LEFT AND LOWER RIGHT;So;0;ON;;;;;N;;;;;
+259A;QUADRANT UPPER LEFT AND LOWER RIGHT;So;0;ON;;;;;N;;;;;
+259B;QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER LEFT;So;0;ON;;;;;N;;;;;
+259C;QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER RIGHT;So;0;ON;;;;;N;;;;;
+259D;QUADRANT UPPER RIGHT;So;0;ON;;;;;N;;;;;
+259E;QUADRANT UPPER RIGHT AND LOWER LEFT;So;0;ON;;;;;N;;;;;
+259F;QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT;So;0;ON;;;;;N;;;;;
+25A0;BLACK SQUARE;So;0;ON;;;;;N;;;;;
+25A1;WHITE SQUARE;So;0;ON;;;;;N;;;;;
+25A2;WHITE SQUARE WITH ROUNDED CORNERS;So;0;ON;;;;;N;;;;;
+25A3;WHITE SQUARE CONTAINING BLACK SMALL SQUARE;So;0;ON;;;;;N;;;;;
+25A4;SQUARE WITH HORIZONTAL FILL;So;0;ON;;;;;N;;;;;
+25A5;SQUARE WITH VERTICAL FILL;So;0;ON;;;;;N;;;;;
+25A6;SQUARE WITH ORTHOGONAL CROSSHATCH FILL;So;0;ON;;;;;N;;;;;
+25A7;SQUARE WITH UPPER LEFT TO LOWER RIGHT FILL;So;0;ON;;;;;N;;;;;
+25A8;SQUARE WITH UPPER RIGHT TO LOWER LEFT FILL;So;0;ON;;;;;N;;;;;
+25A9;SQUARE WITH DIAGONAL CROSSHATCH FILL;So;0;ON;;;;;N;;;;;
+25AA;BLACK SMALL SQUARE;So;0;ON;;;;;N;;;;;
+25AB;WHITE SMALL SQUARE;So;0;ON;;;;;N;;;;;
+25AC;BLACK RECTANGLE;So;0;ON;;;;;N;;;;;
+25AD;WHITE RECTANGLE;So;0;ON;;;;;N;;;;;
+25AE;BLACK VERTICAL RECTANGLE;So;0;ON;;;;;N;;;;;
+25AF;WHITE VERTICAL RECTANGLE;So;0;ON;;;;;N;;;;;
+25B0;BLACK PARALLELOGRAM;So;0;ON;;;;;N;;;;;
+25B1;WHITE PARALLELOGRAM;So;0;ON;;;;;N;;;;;
+25B2;BLACK UP-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK UP POINTING TRIANGLE;;;;
+25B3;WHITE UP-POINTING TRIANGLE;So;0;ON;;;;;N;WHITE UP POINTING TRIANGLE;;;;
+25B4;BLACK UP-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK UP POINTING SMALL TRIANGLE;;;;
+25B5;WHITE UP-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE UP POINTING SMALL TRIANGLE;;;;
+25B6;BLACK RIGHT-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK RIGHT POINTING TRIANGLE;;;;
+25B7;WHITE RIGHT-POINTING TRIANGLE;Sm;0;ON;;;;;N;WHITE RIGHT POINTING TRIANGLE;;;;
+25B8;BLACK RIGHT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK RIGHT POINTING SMALL TRIANGLE;;;;
+25B9;WHITE RIGHT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE RIGHT POINTING SMALL TRIANGLE;;;;
+25BA;BLACK RIGHT-POINTING POINTER;So;0;ON;;;;;N;BLACK RIGHT POINTING POINTER;;;;
+25BB;WHITE RIGHT-POINTING POINTER;So;0;ON;;;;;N;WHITE RIGHT POINTING POINTER;;;;
+25BC;BLACK DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK DOWN POINTING TRIANGLE;;;;
+25BD;WHITE DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;WHITE DOWN POINTING TRIANGLE;;;;
+25BE;BLACK DOWN-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK DOWN POINTING SMALL TRIANGLE;;;;
+25BF;WHITE DOWN-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE DOWN POINTING SMALL TRIANGLE;;;;
+25C0;BLACK LEFT-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK LEFT POINTING TRIANGLE;;;;
+25C1;WHITE LEFT-POINTING TRIANGLE;Sm;0;ON;;;;;N;WHITE LEFT POINTING TRIANGLE;;;;
+25C2;BLACK LEFT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK LEFT POINTING SMALL TRIANGLE;;;;
+25C3;WHITE LEFT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE LEFT POINTING SMALL TRIANGLE;;;;
+25C4;BLACK LEFT-POINTING POINTER;So;0;ON;;;;;N;BLACK LEFT POINTING POINTER;;;;
+25C5;WHITE LEFT-POINTING POINTER;So;0;ON;;;;;N;WHITE LEFT POINTING POINTER;;;;
+25C6;BLACK DIAMOND;So;0;ON;;;;;N;;;;;
+25C7;WHITE DIAMOND;So;0;ON;;;;;N;;;;;
+25C8;WHITE DIAMOND CONTAINING BLACK SMALL DIAMOND;So;0;ON;;;;;N;;;;;
+25C9;FISHEYE;So;0;ON;;;;;N;;;;;
+25CA;LOZENGE;So;0;ON;;;;;N;;;;;
+25CB;WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+25CC;DOTTED CIRCLE;So;0;ON;;;;;N;;;;;
+25CD;CIRCLE WITH VERTICAL FILL;So;0;ON;;;;;N;;;;;
+25CE;BULLSEYE;So;0;ON;;;;;N;;;;;
+25CF;BLACK CIRCLE;So;0;ON;;;;;N;;;;;
+25D0;CIRCLE WITH LEFT HALF BLACK;So;0;ON;;;;;N;;;;;
+25D1;CIRCLE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;;;;;
+25D2;CIRCLE WITH LOWER HALF BLACK;So;0;ON;;;;;N;;;;;
+25D3;CIRCLE WITH UPPER HALF BLACK;So;0;ON;;;;;N;;;;;
+25D4;CIRCLE WITH UPPER RIGHT QUADRANT BLACK;So;0;ON;;;;;N;;;;;
+25D5;CIRCLE WITH ALL BUT UPPER LEFT QUADRANT BLACK;So;0;ON;;;;;N;;;;;
+25D6;LEFT HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;;
+25D7;RIGHT HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;;
+25D8;INVERSE BULLET;So;0;ON;;;;;N;;;;;
+25D9;INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+25DA;UPPER HALF INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+25DB;LOWER HALF INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+25DC;UPPER LEFT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;;
+25DD;UPPER RIGHT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;;
+25DE;LOWER RIGHT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;;
+25DF;LOWER LEFT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;;
+25E0;UPPER HALF CIRCLE;So;0;ON;;;;;N;;;;;
+25E1;LOWER HALF CIRCLE;So;0;ON;;;;;N;;;;;
+25E2;BLACK LOWER RIGHT TRIANGLE;So;0;ON;;;;;N;;;;;
+25E3;BLACK LOWER LEFT TRIANGLE;So;0;ON;;;;;N;;;;;
+25E4;BLACK UPPER LEFT TRIANGLE;So;0;ON;;;;;N;;;;;
+25E5;BLACK UPPER RIGHT TRIANGLE;So;0;ON;;;;;N;;;;;
+25E6;WHITE BULLET;So;0;ON;;;;;N;;;;;
+25E7;SQUARE WITH LEFT HALF BLACK;So;0;ON;;;;;N;;;;;
+25E8;SQUARE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;;;;;
+25E9;SQUARE WITH UPPER LEFT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;;
+25EA;SQUARE WITH LOWER RIGHT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;;
+25EB;WHITE SQUARE WITH VERTICAL BISECTING LINE;So;0;ON;;;;;N;;;;;
+25EC;WHITE UP-POINTING TRIANGLE WITH DOT;So;0;ON;;;;;N;WHITE UP POINTING TRIANGLE WITH DOT;;;;
+25ED;UP-POINTING TRIANGLE WITH LEFT HALF BLACK;So;0;ON;;;;;N;UP POINTING TRIANGLE WITH LEFT HALF BLACK;;;;
+25EE;UP-POINTING TRIANGLE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;UP POINTING TRIANGLE WITH RIGHT HALF BLACK;;;;
+25EF;LARGE CIRCLE;So;0;ON;;;;;N;;;;;
+25F0;WHITE SQUARE WITH UPPER LEFT QUADRANT;So;0;ON;;;;;N;;;;;
+25F1;WHITE SQUARE WITH LOWER LEFT QUADRANT;So;0;ON;;;;;N;;;;;
+25F2;WHITE SQUARE WITH LOWER RIGHT QUADRANT;So;0;ON;;;;;N;;;;;
+25F3;WHITE SQUARE WITH UPPER RIGHT QUADRANT;So;0;ON;;;;;N;;;;;
+25F4;WHITE CIRCLE WITH UPPER LEFT QUADRANT;So;0;ON;;;;;N;;;;;
+25F5;WHITE CIRCLE WITH LOWER LEFT QUADRANT;So;0;ON;;;;;N;;;;;
+25F6;WHITE CIRCLE WITH LOWER RIGHT QUADRANT;So;0;ON;;;;;N;;;;;
+25F7;WHITE CIRCLE WITH UPPER RIGHT QUADRANT;So;0;ON;;;;;N;;;;;
+25F8;UPPER LEFT TRIANGLE;Sm;0;ON;;;;;N;;;;;
+25F9;UPPER RIGHT TRIANGLE;Sm;0;ON;;;;;N;;;;;
+25FA;LOWER LEFT TRIANGLE;Sm;0;ON;;;;;N;;;;;
+25FB;WHITE MEDIUM SQUARE;Sm;0;ON;;;;;N;;;;;
+25FC;BLACK MEDIUM SQUARE;Sm;0;ON;;;;;N;;;;;
+25FD;WHITE MEDIUM SMALL SQUARE;Sm;0;ON;;;;;N;;;;;
+25FE;BLACK MEDIUM SMALL SQUARE;Sm;0;ON;;;;;N;;;;;
+25FF;LOWER RIGHT TRIANGLE;Sm;0;ON;;;;;N;;;;;
+2600;BLACK SUN WITH RAYS;So;0;ON;;;;;N;;;;;
+2601;CLOUD;So;0;ON;;;;;N;;;;;
+2602;UMBRELLA;So;0;ON;;;;;N;;;;;
+2603;SNOWMAN;So;0;ON;;;;;N;;;;;
+2604;COMET;So;0;ON;;;;;N;;;;;
+2605;BLACK STAR;So;0;ON;;;;;N;;;;;
+2606;WHITE STAR;So;0;ON;;;;;N;;;;;
+2607;LIGHTNING;So;0;ON;;;;;N;;;;;
+2608;THUNDERSTORM;So;0;ON;;;;;N;;;;;
+2609;SUN;So;0;ON;;;;;N;;;;;
+260A;ASCENDING NODE;So;0;ON;;;;;N;;;;;
+260B;DESCENDING NODE;So;0;ON;;;;;N;;;;;
+260C;CONJUNCTION;So;0;ON;;;;;N;;;;;
+260D;OPPOSITION;So;0;ON;;;;;N;;;;;
+260E;BLACK TELEPHONE;So;0;ON;;;;;N;;;;;
+260F;WHITE TELEPHONE;So;0;ON;;;;;N;;;;;
+2610;BALLOT BOX;So;0;ON;;;;;N;;;;;
+2611;BALLOT BOX WITH CHECK;So;0;ON;;;;;N;;;;;
+2612;BALLOT BOX WITH X;So;0;ON;;;;;N;;;;;
+2613;SALTIRE;So;0;ON;;;;;N;;;;;
+2614;UMBRELLA WITH RAIN DROPS;So;0;ON;;;;;N;;;;;
+2615;HOT BEVERAGE;So;0;ON;;;;;N;;;;;
+2616;WHITE SHOGI PIECE;So;0;ON;;;;;N;;;;;
+2617;BLACK SHOGI PIECE;So;0;ON;;;;;N;;;;;
+2618;SHAMROCK;So;0;ON;;;;;N;;;;;
+2619;REVERSED ROTATED FLORAL HEART BULLET;So;0;ON;;;;;N;;;;;
+261A;BLACK LEFT POINTING INDEX;So;0;ON;;;;;N;;;;;
+261B;BLACK RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;;
+261C;WHITE LEFT POINTING INDEX;So;0;ON;;;;;N;;;;;
+261D;WHITE UP POINTING INDEX;So;0;ON;;;;;N;;;;;
+261E;WHITE RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;;
+261F;WHITE DOWN POINTING INDEX;So;0;ON;;;;;N;;;;;
+2620;SKULL AND CROSSBONES;So;0;ON;;;;;N;;;;;
+2621;CAUTION SIGN;So;0;ON;;;;;N;;;;;
+2622;RADIOACTIVE SIGN;So;0;ON;;;;;N;;;;;
+2623;BIOHAZARD SIGN;So;0;ON;;;;;N;;;;;
+2624;CADUCEUS;So;0;ON;;;;;N;;;;;
+2625;ANKH;So;0;ON;;;;;N;;;;;
+2626;ORTHODOX CROSS;So;0;ON;;;;;N;;;;;
+2627;CHI RHO;So;0;ON;;;;;N;;;;;
+2628;CROSS OF LORRAINE;So;0;ON;;;;;N;;;;;
+2629;CROSS OF JERUSALEM;So;0;ON;;;;;N;;;;;
+262A;STAR AND CRESCENT;So;0;ON;;;;;N;;;;;
+262B;FARSI SYMBOL;So;0;ON;;;;;N;SYMBOL OF IRAN;;;;
+262C;ADI SHAKTI;So;0;ON;;;;;N;;;;;
+262D;HAMMER AND SICKLE;So;0;ON;;;;;N;;;;;
+262E;PEACE SYMBOL;So;0;ON;;;;;N;;;;;
+262F;YIN YANG;So;0;ON;;;;;N;;;;;
+2630;TRIGRAM FOR HEAVEN;So;0;ON;;;;;N;;;;;
+2631;TRIGRAM FOR LAKE;So;0;ON;;;;;N;;;;;
+2632;TRIGRAM FOR FIRE;So;0;ON;;;;;N;;;;;
+2633;TRIGRAM FOR THUNDER;So;0;ON;;;;;N;;;;;
+2634;TRIGRAM FOR WIND;So;0;ON;;;;;N;;;;;
+2635;TRIGRAM FOR WATER;So;0;ON;;;;;N;;;;;
+2636;TRIGRAM FOR MOUNTAIN;So;0;ON;;;;;N;;;;;
+2637;TRIGRAM FOR EARTH;So;0;ON;;;;;N;;;;;
+2638;WHEEL OF DHARMA;So;0;ON;;;;;N;;;;;
+2639;WHITE FROWNING FACE;So;0;ON;;;;;N;;;;;
+263A;WHITE SMILING FACE;So;0;ON;;;;;N;;;;;
+263B;BLACK SMILING FACE;So;0;ON;;;;;N;;;;;
+263C;WHITE SUN WITH RAYS;So;0;ON;;;;;N;;;;;
+263D;FIRST QUARTER MOON;So;0;ON;;;;;N;;;;;
+263E;LAST QUARTER MOON;So;0;ON;;;;;N;;;;;
+263F;MERCURY;So;0;ON;;;;;N;;;;;
+2640;FEMALE SIGN;So;0;ON;;;;;N;;;;;
+2641;EARTH;So;0;ON;;;;;N;;;;;
+2642;MALE SIGN;So;0;ON;;;;;N;;;;;
+2643;JUPITER;So;0;ON;;;;;N;;;;;
+2644;SATURN;So;0;ON;;;;;N;;;;;
+2645;URANUS;So;0;ON;;;;;N;;;;;
+2646;NEPTUNE;So;0;ON;;;;;N;;;;;
+2647;PLUTO;So;0;ON;;;;;N;;;;;
+2648;ARIES;So;0;ON;;;;;N;;;;;
+2649;TAURUS;So;0;ON;;;;;N;;;;;
+264A;GEMINI;So;0;ON;;;;;N;;;;;
+264B;CANCER;So;0;ON;;;;;N;;;;;
+264C;LEO;So;0;ON;;;;;N;;;;;
+264D;VIRGO;So;0;ON;;;;;N;;;;;
+264E;LIBRA;So;0;ON;;;;;N;;;;;
+264F;SCORPIUS;So;0;ON;;;;;N;;;;;
+2650;SAGITTARIUS;So;0;ON;;;;;N;;;;;
+2651;CAPRICORN;So;0;ON;;;;;N;;;;;
+2652;AQUARIUS;So;0;ON;;;;;N;;;;;
+2653;PISCES;So;0;ON;;;;;N;;;;;
+2654;WHITE CHESS KING;So;0;ON;;;;;N;;;;;
+2655;WHITE CHESS QUEEN;So;0;ON;;;;;N;;;;;
+2656;WHITE CHESS ROOK;So;0;ON;;;;;N;;;;;
+2657;WHITE CHESS BISHOP;So;0;ON;;;;;N;;;;;
+2658;WHITE CHESS KNIGHT;So;0;ON;;;;;N;;;;;
+2659;WHITE CHESS PAWN;So;0;ON;;;;;N;;;;;
+265A;BLACK CHESS KING;So;0;ON;;;;;N;;;;;
+265B;BLACK CHESS QUEEN;So;0;ON;;;;;N;;;;;
+265C;BLACK CHESS ROOK;So;0;ON;;;;;N;;;;;
+265D;BLACK CHESS BISHOP;So;0;ON;;;;;N;;;;;
+265E;BLACK CHESS KNIGHT;So;0;ON;;;;;N;;;;;
+265F;BLACK CHESS PAWN;So;0;ON;;;;;N;;;;;
+2660;BLACK SPADE SUIT;So;0;ON;;;;;N;;;;;
+2661;WHITE HEART SUIT;So;0;ON;;;;;N;;;;;
+2662;WHITE DIAMOND SUIT;So;0;ON;;;;;N;;;;;
+2663;BLACK CLUB SUIT;So;0;ON;;;;;N;;;;;
+2664;WHITE SPADE SUIT;So;0;ON;;;;;N;;;;;
+2665;BLACK HEART SUIT;So;0;ON;;;;;N;;;;;
+2666;BLACK DIAMOND SUIT;So;0;ON;;;;;N;;;;;
+2667;WHITE CLUB SUIT;So;0;ON;;;;;N;;;;;
+2668;HOT SPRINGS;So;0;ON;;;;;N;;;;;
+2669;QUARTER NOTE;So;0;ON;;;;;N;;;;;
+266A;EIGHTH NOTE;So;0;ON;;;;;N;;;;;
+266B;BEAMED EIGHTH NOTES;So;0;ON;;;;;N;BARRED EIGHTH NOTES;;;;
+266C;BEAMED SIXTEENTH NOTES;So;0;ON;;;;;N;BARRED SIXTEENTH NOTES;;;;
+266D;MUSIC FLAT SIGN;So;0;ON;;;;;N;FLAT;;;;
+266E;MUSIC NATURAL SIGN;So;0;ON;;;;;N;NATURAL;;;;
+266F;MUSIC SHARP SIGN;Sm;0;ON;;;;;N;SHARP;;;;
+2670;WEST SYRIAC CROSS;So;0;ON;;;;;N;;;;;
+2671;EAST SYRIAC CROSS;So;0;ON;;;;;N;;;;;
+2672;UNIVERSAL RECYCLING SYMBOL;So;0;ON;;;;;N;;;;;
+2673;RECYCLING SYMBOL FOR TYPE-1 PLASTICS;So;0;ON;;;;;N;;;;;
+2674;RECYCLING SYMBOL FOR TYPE-2 PLASTICS;So;0;ON;;;;;N;;;;;
+2675;RECYCLING SYMBOL FOR TYPE-3 PLASTICS;So;0;ON;;;;;N;;;;;
+2676;RECYCLING SYMBOL FOR TYPE-4 PLASTICS;So;0;ON;;;;;N;;;;;
+2677;RECYCLING SYMBOL FOR TYPE-5 PLASTICS;So;0;ON;;;;;N;;;;;
+2678;RECYCLING SYMBOL FOR TYPE-6 PLASTICS;So;0;ON;;;;;N;;;;;
+2679;RECYCLING SYMBOL FOR TYPE-7 PLASTICS;So;0;ON;;;;;N;;;;;
+267A;RECYCLING SYMBOL FOR GENERIC MATERIALS;So;0;ON;;;;;N;;;;;
+267B;BLACK UNIVERSAL RECYCLING SYMBOL;So;0;ON;;;;;N;;;;;
+267C;RECYCLED PAPER SYMBOL;So;0;ON;;;;;N;;;;;
+267D;PARTIALLY-RECYCLED PAPER SYMBOL;So;0;ON;;;;;N;;;;;
+267E;PERMANENT PAPER SIGN;So;0;ON;;;;;N;;;;;
+267F;WHEELCHAIR SYMBOL;So;0;ON;;;;;N;;;;;
+2680;DIE FACE-1;So;0;ON;;;;;N;;;;;
+2681;DIE FACE-2;So;0;ON;;;;;N;;;;;
+2682;DIE FACE-3;So;0;ON;;;;;N;;;;;
+2683;DIE FACE-4;So;0;ON;;;;;N;;;;;
+2684;DIE FACE-5;So;0;ON;;;;;N;;;;;
+2685;DIE FACE-6;So;0;ON;;;;;N;;;;;
+2686;WHITE CIRCLE WITH DOT RIGHT;So;0;ON;;;;;N;;;;;
+2687;WHITE CIRCLE WITH TWO DOTS;So;0;ON;;;;;N;;;;;
+2688;BLACK CIRCLE WITH WHITE DOT RIGHT;So;0;ON;;;;;N;;;;;
+2689;BLACK CIRCLE WITH TWO WHITE DOTS;So;0;ON;;;;;N;;;;;
+268A;MONOGRAM FOR YANG;So;0;ON;;;;;N;;;;;
+268B;MONOGRAM FOR YIN;So;0;ON;;;;;N;;;;;
+268C;DIGRAM FOR GREATER YANG;So;0;ON;;;;;N;;;;;
+268D;DIGRAM FOR LESSER YIN;So;0;ON;;;;;N;;;;;
+268E;DIGRAM FOR LESSER YANG;So;0;ON;;;;;N;;;;;
+268F;DIGRAM FOR GREATER YIN;So;0;ON;;;;;N;;;;;
+2690;WHITE FLAG;So;0;ON;;;;;N;;;;;
+2691;BLACK FLAG;So;0;ON;;;;;N;;;;;
+2692;HAMMER AND PICK;So;0;ON;;;;;N;;;;;
+2693;ANCHOR;So;0;ON;;;;;N;;;;;
+2694;CROSSED SWORDS;So;0;ON;;;;;N;;;;;
+2695;STAFF OF AESCULAPIUS;So;0;ON;;;;;N;;;;;
+2696;SCALES;So;0;ON;;;;;N;;;;;
+2697;ALEMBIC;So;0;ON;;;;;N;;;;;
+2698;FLOWER;So;0;ON;;;;;N;;;;;
+2699;GEAR;So;0;ON;;;;;N;;;;;
+269A;STAFF OF HERMES;So;0;ON;;;;;N;;;;;
+269B;ATOM SYMBOL;So;0;ON;;;;;N;;;;;
+269C;FLEUR-DE-LIS;So;0;ON;;;;;N;;;;;
+269D;OUTLINED WHITE STAR;So;0;ON;;;;;N;;;;;
+269E;THREE LINES CONVERGING RIGHT;So;0;ON;;;;;N;;;;;
+269F;THREE LINES CONVERGING LEFT;So;0;ON;;;;;N;;;;;
+26A0;WARNING SIGN;So;0;ON;;;;;N;;;;;
+26A1;HIGH VOLTAGE SIGN;So;0;ON;;;;;N;;;;;
+26A2;DOUBLED FEMALE SIGN;So;0;ON;;;;;N;;;;;
+26A3;DOUBLED MALE SIGN;So;0;ON;;;;;N;;;;;
+26A4;INTERLOCKED FEMALE AND MALE SIGN;So;0;ON;;;;;N;;;;;
+26A5;MALE AND FEMALE SIGN;So;0;ON;;;;;N;;;;;
+26A6;MALE WITH STROKE SIGN;So;0;ON;;;;;N;;;;;
+26A7;MALE WITH STROKE AND MALE AND FEMALE SIGN;So;0;ON;;;;;N;;;;;
+26A8;VERTICAL MALE WITH STROKE SIGN;So;0;ON;;;;;N;;;;;
+26A9;HORIZONTAL MALE WITH STROKE SIGN;So;0;ON;;;;;N;;;;;
+26AA;MEDIUM WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+26AB;MEDIUM BLACK CIRCLE;So;0;ON;;;;;N;;;;;
+26AC;MEDIUM SMALL WHITE CIRCLE;So;0;L;;;;;N;;;;;
+26AD;MARRIAGE SYMBOL;So;0;ON;;;;;N;;;;;
+26AE;DIVORCE SYMBOL;So;0;ON;;;;;N;;;;;
+26AF;UNMARRIED PARTNERSHIP SYMBOL;So;0;ON;;;;;N;;;;;
+26B0;COFFIN;So;0;ON;;;;;N;;;;;
+26B1;FUNERAL URN;So;0;ON;;;;;N;;;;;
+26B2;NEUTER;So;0;ON;;;;;N;;;;;
+26B3;CERES;So;0;ON;;;;;N;;;;;
+26B4;PALLAS;So;0;ON;;;;;N;;;;;
+26B5;JUNO;So;0;ON;;;;;N;;;;;
+26B6;VESTA;So;0;ON;;;;;N;;;;;
+26B7;CHIRON;So;0;ON;;;;;N;;;;;
+26B8;BLACK MOON LILITH;So;0;ON;;;;;N;;;;;
+26B9;SEXTILE;So;0;ON;;;;;N;;;;;
+26BA;SEMISEXTILE;So;0;ON;;;;;N;;;;;
+26BB;QUINCUNX;So;0;ON;;;;;N;;;;;
+26BC;SESQUIQUADRATE;So;0;ON;;;;;N;;;;;
+26BD;SOCCER BALL;So;0;ON;;;;;N;;;;;
+26BE;BASEBALL;So;0;ON;;;;;N;;;;;
+26BF;SQUARED KEY;So;0;ON;;;;;N;;;;;
+26C0;WHITE DRAUGHTS MAN;So;0;ON;;;;;N;;;;;
+26C1;WHITE DRAUGHTS KING;So;0;ON;;;;;N;;;;;
+26C2;BLACK DRAUGHTS MAN;So;0;ON;;;;;N;;;;;
+26C3;BLACK DRAUGHTS KING;So;0;ON;;;;;N;;;;;
+26C4;SNOWMAN WITHOUT SNOW;So;0;ON;;;;;N;;;;;
+26C5;SUN BEHIND CLOUD;So;0;ON;;;;;N;;;;;
+26C6;RAIN;So;0;ON;;;;;N;;;;;
+26C7;BLACK SNOWMAN;So;0;ON;;;;;N;;;;;
+26C8;THUNDER CLOUD AND RAIN;So;0;ON;;;;;N;;;;;
+26C9;TURNED WHITE SHOGI PIECE;So;0;ON;;;;;N;;;;;
+26CA;TURNED BLACK SHOGI PIECE;So;0;ON;;;;;N;;;;;
+26CB;WHITE DIAMOND IN SQUARE;So;0;ON;;;;;N;;;;;
+26CC;CROSSING LANES;So;0;ON;;;;;N;;;;;
+26CD;DISABLED CAR;So;0;ON;;;;;N;;;;;
+26CE;OPHIUCHUS;So;0;ON;;;;;N;;;;;
+26CF;PICK;So;0;ON;;;;;N;;;;;
+26D0;CAR SLIDING;So;0;ON;;;;;N;;;;;
+26D1;HELMET WITH WHITE CROSS;So;0;ON;;;;;N;;;;;
+26D2;CIRCLED CROSSING LANES;So;0;ON;;;;;N;;;;;
+26D3;CHAINS;So;0;ON;;;;;N;;;;;
+26D4;NO ENTRY;So;0;ON;;;;;N;;;;;
+26D5;ALTERNATE ONE-WAY LEFT WAY TRAFFIC;So;0;ON;;;;;N;;;;;
+26D6;BLACK TWO-WAY LEFT WAY TRAFFIC;So;0;ON;;;;;N;;;;;
+26D7;WHITE TWO-WAY LEFT WAY TRAFFIC;So;0;ON;;;;;N;;;;;
+26D8;BLACK LEFT LANE MERGE;So;0;ON;;;;;N;;;;;
+26D9;WHITE LEFT LANE MERGE;So;0;ON;;;;;N;;;;;
+26DA;DRIVE SLOW SIGN;So;0;ON;;;;;N;;;;;
+26DB;HEAVY WHITE DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;;;;;
+26DC;LEFT CLOSED ENTRY;So;0;ON;;;;;N;;;;;
+26DD;SQUARED SALTIRE;So;0;ON;;;;;N;;;;;
+26DE;FALLING DIAGONAL IN WHITE CIRCLE IN BLACK SQUARE;So;0;ON;;;;;N;;;;;
+26DF;BLACK TRUCK;So;0;ON;;;;;N;;;;;
+26E0;RESTRICTED LEFT ENTRY-1;So;0;ON;;;;;N;;;;;
+26E1;RESTRICTED LEFT ENTRY-2;So;0;ON;;;;;N;;;;;
+26E2;ASTRONOMICAL SYMBOL FOR URANUS;So;0;ON;;;;;N;;;;;
+26E3;HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE;So;0;ON;;;;;N;;;;;
+26E4;PENTAGRAM;So;0;ON;;;;;N;;;;;
+26E5;RIGHT-HANDED INTERLACED PENTAGRAM;So;0;ON;;;;;N;;;;;
+26E6;LEFT-HANDED INTERLACED PENTAGRAM;So;0;ON;;;;;N;;;;;
+26E7;INVERTED PENTAGRAM;So;0;ON;;;;;N;;;;;
+26E8;BLACK CROSS ON SHIELD;So;0;ON;;;;;N;;;;;
+26E9;SHINTO SHRINE;So;0;ON;;;;;N;;;;;
+26EA;CHURCH;So;0;ON;;;;;N;;;;;
+26EB;CASTLE;So;0;ON;;;;;N;;;;;
+26EC;HISTORIC SITE;So;0;ON;;;;;N;;;;;
+26ED;GEAR WITHOUT HUB;So;0;ON;;;;;N;;;;;
+26EE;GEAR WITH HANDLES;So;0;ON;;;;;N;;;;;
+26EF;MAP SYMBOL FOR LIGHTHOUSE;So;0;ON;;;;;N;;;;;
+26F0;MOUNTAIN;So;0;ON;;;;;N;;;;;
+26F1;UMBRELLA ON GROUND;So;0;ON;;;;;N;;;;;
+26F2;FOUNTAIN;So;0;ON;;;;;N;;;;;
+26F3;FLAG IN HOLE;So;0;ON;;;;;N;;;;;
+26F4;FERRY;So;0;ON;;;;;N;;;;;
+26F5;SAILBOAT;So;0;ON;;;;;N;;;;;
+26F6;SQUARE FOUR CORNERS;So;0;ON;;;;;N;;;;;
+26F7;SKIER;So;0;ON;;;;;N;;;;;
+26F8;ICE SKATE;So;0;ON;;;;;N;;;;;
+26F9;PERSON WITH BALL;So;0;ON;;;;;N;;;;;
+26FA;TENT;So;0;ON;;;;;N;;;;;
+26FB;JAPANESE BANK SYMBOL;So;0;ON;;;;;N;;;;;
+26FC;HEADSTONE GRAVEYARD SYMBOL;So;0;ON;;;;;N;;;;;
+26FD;FUEL PUMP;So;0;ON;;;;;N;;;;;
+26FE;CUP ON BLACK SQUARE;So;0;ON;;;;;N;;;;;
+26FF;WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE;So;0;ON;;;;;N;;;;;
+2700;BLACK SAFETY SCISSORS;So;0;ON;;;;;N;;;;;
+2701;UPPER BLADE SCISSORS;So;0;ON;;;;;N;;;;;
+2702;BLACK SCISSORS;So;0;ON;;;;;N;;;;;
+2703;LOWER BLADE SCISSORS;So;0;ON;;;;;N;;;;;
+2704;WHITE SCISSORS;So;0;ON;;;;;N;;;;;
+2705;WHITE HEAVY CHECK MARK;So;0;ON;;;;;N;;;;;
+2706;TELEPHONE LOCATION SIGN;So;0;ON;;;;;N;;;;;
+2707;TAPE DRIVE;So;0;ON;;;;;N;;;;;
+2708;AIRPLANE;So;0;ON;;;;;N;;;;;
+2709;ENVELOPE;So;0;ON;;;;;N;;;;;
+270A;RAISED FIST;So;0;ON;;;;;N;;;;;
+270B;RAISED HAND;So;0;ON;;;;;N;;;;;
+270C;VICTORY HAND;So;0;ON;;;;;N;;;;;
+270D;WRITING HAND;So;0;ON;;;;;N;;;;;
+270E;LOWER RIGHT PENCIL;So;0;ON;;;;;N;;;;;
+270F;PENCIL;So;0;ON;;;;;N;;;;;
+2710;UPPER RIGHT PENCIL;So;0;ON;;;;;N;;;;;
+2711;WHITE NIB;So;0;ON;;;;;N;;;;;
+2712;BLACK NIB;So;0;ON;;;;;N;;;;;
+2713;CHECK MARK;So;0;ON;;;;;N;;;;;
+2714;HEAVY CHECK MARK;So;0;ON;;;;;N;;;;;
+2715;MULTIPLICATION X;So;0;ON;;;;;N;;;;;
+2716;HEAVY MULTIPLICATION X;So;0;ON;;;;;N;;;;;
+2717;BALLOT X;So;0;ON;;;;;N;;;;;
+2718;HEAVY BALLOT X;So;0;ON;;;;;N;;;;;
+2719;OUTLINED GREEK CROSS;So;0;ON;;;;;N;;;;;
+271A;HEAVY GREEK CROSS;So;0;ON;;;;;N;;;;;
+271B;OPEN CENTRE CROSS;So;0;ON;;;;;N;OPEN CENTER CROSS;;;;
+271C;HEAVY OPEN CENTRE CROSS;So;0;ON;;;;;N;HEAVY OPEN CENTER CROSS;;;;
+271D;LATIN CROSS;So;0;ON;;;;;N;;;;;
+271E;SHADOWED WHITE LATIN CROSS;So;0;ON;;;;;N;;;;;
+271F;OUTLINED LATIN CROSS;So;0;ON;;;;;N;;;;;
+2720;MALTESE CROSS;So;0;ON;;;;;N;;;;;
+2721;STAR OF DAVID;So;0;ON;;;;;N;;;;;
+2722;FOUR TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2723;FOUR BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2724;HEAVY FOUR BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2725;FOUR CLUB-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2726;BLACK FOUR POINTED STAR;So;0;ON;;;;;N;;;;;
+2727;WHITE FOUR POINTED STAR;So;0;ON;;;;;N;;;;;
+2728;SPARKLES;So;0;ON;;;;;N;;;;;
+2729;STRESS OUTLINED WHITE STAR;So;0;ON;;;;;N;;;;;
+272A;CIRCLED WHITE STAR;So;0;ON;;;;;N;;;;;
+272B;OPEN CENTRE BLACK STAR;So;0;ON;;;;;N;OPEN CENTER BLACK STAR;;;;
+272C;BLACK CENTRE WHITE STAR;So;0;ON;;;;;N;BLACK CENTER WHITE STAR;;;;
+272D;OUTLINED BLACK STAR;So;0;ON;;;;;N;;;;;
+272E;HEAVY OUTLINED BLACK STAR;So;0;ON;;;;;N;;;;;
+272F;PINWHEEL STAR;So;0;ON;;;;;N;;;;;
+2730;SHADOWED WHITE STAR;So;0;ON;;;;;N;;;;;
+2731;HEAVY ASTERISK;So;0;ON;;;;;N;;;;;
+2732;OPEN CENTRE ASTERISK;So;0;ON;;;;;N;OPEN CENTER ASTERISK;;;;
+2733;EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2734;EIGHT POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+2735;EIGHT POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;;
+2736;SIX POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+2737;EIGHT POINTED RECTILINEAR BLACK STAR;So;0;ON;;;;;N;;;;;
+2738;HEAVY EIGHT POINTED RECTILINEAR BLACK STAR;So;0;ON;;;;;N;;;;;
+2739;TWELVE POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+273A;SIXTEEN POINTED ASTERISK;So;0;ON;;;;;N;;;;;
+273B;TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+273C;OPEN CENTRE TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;OPEN CENTER TEARDROP-SPOKED ASTERISK;;;;
+273D;HEAVY TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+273E;SIX PETALLED BLACK AND WHITE FLORETTE;So;0;ON;;;;;N;;;;;
+273F;BLACK FLORETTE;So;0;ON;;;;;N;;;;;
+2740;WHITE FLORETTE;So;0;ON;;;;;N;;;;;
+2741;EIGHT PETALLED OUTLINED BLACK FLORETTE;So;0;ON;;;;;N;;;;;
+2742;CIRCLED OPEN CENTRE EIGHT POINTED STAR;So;0;ON;;;;;N;CIRCLED OPEN CENTER EIGHT POINTED STAR;;;;
+2743;HEAVY TEARDROP-SPOKED PINWHEEL ASTERISK;So;0;ON;;;;;N;;;;;
+2744;SNOWFLAKE;So;0;ON;;;;;N;;;;;
+2745;TIGHT TRIFOLIATE SNOWFLAKE;So;0;ON;;;;;N;;;;;
+2746;HEAVY CHEVRON SNOWFLAKE;So;0;ON;;;;;N;;;;;
+2747;SPARKLE;So;0;ON;;;;;N;;;;;
+2748;HEAVY SPARKLE;So;0;ON;;;;;N;;;;;
+2749;BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+274A;EIGHT TEARDROP-SPOKED PROPELLER ASTERISK;So;0;ON;;;;;N;;;;;
+274B;HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK;So;0;ON;;;;;N;;;;;
+274C;CROSS MARK;So;0;ON;;;;;N;;;;;
+274D;SHADOWED WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+274E;NEGATIVE SQUARED CROSS MARK;So;0;ON;;;;;N;;;;;
+274F;LOWER RIGHT DROP-SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;;
+2750;UPPER RIGHT DROP-SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;;
+2751;LOWER RIGHT SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;;
+2752;UPPER RIGHT SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;;
+2753;BLACK QUESTION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+2754;WHITE QUESTION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+2755;WHITE EXCLAMATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+2756;BLACK DIAMOND MINUS WHITE X;So;0;ON;;;;;N;;;;;
+2757;HEAVY EXCLAMATION MARK SYMBOL;So;0;ON;;;;;N;;;;;
+2758;LIGHT VERTICAL BAR;So;0;ON;;;;;N;;;;;
+2759;MEDIUM VERTICAL BAR;So;0;ON;;;;;N;;;;;
+275A;HEAVY VERTICAL BAR;So;0;ON;;;;;N;;;;;
+275B;HEAVY SINGLE TURNED COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+275C;HEAVY SINGLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+275D;HEAVY DOUBLE TURNED COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+275E;HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+275F;HEAVY LOW SINGLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+2760;HEAVY LOW DOUBLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+2761;CURVED STEM PARAGRAPH SIGN ORNAMENT;So;0;ON;;;;;N;;;;;
+2762;HEAVY EXCLAMATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+2763;HEAVY HEART EXCLAMATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+2764;HEAVY BLACK HEART;So;0;ON;;;;;N;;;;;
+2765;ROTATED HEAVY BLACK HEART BULLET;So;0;ON;;;;;N;;;;;
+2766;FLORAL HEART;So;0;ON;;;;;N;;;;;
+2767;ROTATED FLORAL HEART BULLET;So;0;ON;;;;;N;;;;;
+2768;MEDIUM LEFT PARENTHESIS ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+2769;MEDIUM RIGHT PARENTHESIS ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+276A;MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+276B;MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+276C;MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+276D;MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+276E;HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+276F;HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+2770;HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+2771;HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+2772;LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+2773;LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+2774;MEDIUM LEFT CURLY BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+2775;MEDIUM RIGHT CURLY BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+2776;DINGBAT NEGATIVE CIRCLED DIGIT ONE;No;0;ON;;;1;1;N;INVERSE CIRCLED DIGIT ONE;;;;
+2777;DINGBAT NEGATIVE CIRCLED DIGIT TWO;No;0;ON;;;2;2;N;INVERSE CIRCLED DIGIT TWO;;;;
+2778;DINGBAT NEGATIVE CIRCLED DIGIT THREE;No;0;ON;;;3;3;N;INVERSE CIRCLED DIGIT THREE;;;;
+2779;DINGBAT NEGATIVE CIRCLED DIGIT FOUR;No;0;ON;;;4;4;N;INVERSE CIRCLED DIGIT FOUR;;;;
+277A;DINGBAT NEGATIVE CIRCLED DIGIT FIVE;No;0;ON;;;5;5;N;INVERSE CIRCLED DIGIT FIVE;;;;
+277B;DINGBAT NEGATIVE CIRCLED DIGIT SIX;No;0;ON;;;6;6;N;INVERSE CIRCLED DIGIT SIX;;;;
+277C;DINGBAT NEGATIVE CIRCLED DIGIT SEVEN;No;0;ON;;;7;7;N;INVERSE CIRCLED DIGIT SEVEN;;;;
+277D;DINGBAT NEGATIVE CIRCLED DIGIT EIGHT;No;0;ON;;;8;8;N;INVERSE CIRCLED DIGIT EIGHT;;;;
+277E;DINGBAT NEGATIVE CIRCLED DIGIT NINE;No;0;ON;;;9;9;N;INVERSE CIRCLED DIGIT NINE;;;;
+277F;DINGBAT NEGATIVE CIRCLED NUMBER TEN;No;0;ON;;;;10;N;INVERSE CIRCLED NUMBER TEN;;;;
+2780;DINGBAT CIRCLED SANS-SERIF DIGIT ONE;No;0;ON;;;1;1;N;CIRCLED SANS-SERIF DIGIT ONE;;;;
+2781;DINGBAT CIRCLED SANS-SERIF DIGIT TWO;No;0;ON;;;2;2;N;CIRCLED SANS-SERIF DIGIT TWO;;;;
+2782;DINGBAT CIRCLED SANS-SERIF DIGIT THREE;No;0;ON;;;3;3;N;CIRCLED SANS-SERIF DIGIT THREE;;;;
+2783;DINGBAT CIRCLED SANS-SERIF DIGIT FOUR;No;0;ON;;;4;4;N;CIRCLED SANS-SERIF DIGIT FOUR;;;;
+2784;DINGBAT CIRCLED SANS-SERIF DIGIT FIVE;No;0;ON;;;5;5;N;CIRCLED SANS-SERIF DIGIT FIVE;;;;
+2785;DINGBAT CIRCLED SANS-SERIF DIGIT SIX;No;0;ON;;;6;6;N;CIRCLED SANS-SERIF DIGIT SIX;;;;
+2786;DINGBAT CIRCLED SANS-SERIF DIGIT SEVEN;No;0;ON;;;7;7;N;CIRCLED SANS-SERIF DIGIT SEVEN;;;;
+2787;DINGBAT CIRCLED SANS-SERIF DIGIT EIGHT;No;0;ON;;;8;8;N;CIRCLED SANS-SERIF DIGIT EIGHT;;;;
+2788;DINGBAT CIRCLED SANS-SERIF DIGIT NINE;No;0;ON;;;9;9;N;CIRCLED SANS-SERIF DIGIT NINE;;;;
+2789;DINGBAT CIRCLED SANS-SERIF NUMBER TEN;No;0;ON;;;;10;N;CIRCLED SANS-SERIF NUMBER TEN;;;;
+278A;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ONE;No;0;ON;;;1;1;N;INVERSE CIRCLED SANS-SERIF DIGIT ONE;;;;
+278B;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT TWO;No;0;ON;;;2;2;N;INVERSE CIRCLED SANS-SERIF DIGIT TWO;;;;
+278C;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT THREE;No;0;ON;;;3;3;N;INVERSE CIRCLED SANS-SERIF DIGIT THREE;;;;
+278D;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FOUR;No;0;ON;;;4;4;N;INVERSE CIRCLED SANS-SERIF DIGIT FOUR;;;;
+278E;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FIVE;No;0;ON;;;5;5;N;INVERSE CIRCLED SANS-SERIF DIGIT FIVE;;;;
+278F;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SIX;No;0;ON;;;6;6;N;INVERSE CIRCLED SANS-SERIF DIGIT SIX;;;;
+2790;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SEVEN;No;0;ON;;;7;7;N;INVERSE CIRCLED SANS-SERIF DIGIT SEVEN;;;;
+2791;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT EIGHT;No;0;ON;;;8;8;N;INVERSE CIRCLED SANS-SERIF DIGIT EIGHT;;;;
+2792;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT NINE;No;0;ON;;;9;9;N;INVERSE CIRCLED SANS-SERIF DIGIT NINE;;;;
+2793;DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN;No;0;ON;;;;10;N;INVERSE CIRCLED SANS-SERIF NUMBER TEN;;;;
+2794;HEAVY WIDE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY WIDE-HEADED RIGHT ARROW;;;;
+2795;HEAVY PLUS SIGN;So;0;ON;;;;;N;;;;;
+2796;HEAVY MINUS SIGN;So;0;ON;;;;;N;;;;;
+2797;HEAVY DIVISION SIGN;So;0;ON;;;;;N;;;;;
+2798;HEAVY SOUTH EAST ARROW;So;0;ON;;;;;N;HEAVY LOWER RIGHT ARROW;;;;
+2799;HEAVY RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY RIGHT ARROW;;;;
+279A;HEAVY NORTH EAST ARROW;So;0;ON;;;;;N;HEAVY UPPER RIGHT ARROW;;;;
+279B;DRAFTING POINT RIGHTWARDS ARROW;So;0;ON;;;;;N;DRAFTING POINT RIGHT ARROW;;;;
+279C;HEAVY ROUND-TIPPED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY ROUND-TIPPED RIGHT ARROW;;;;
+279D;TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;TRIANGLE-HEADED RIGHT ARROW;;;;
+279E;HEAVY TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY TRIANGLE-HEADED RIGHT ARROW;;;;
+279F;DASHED TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;DASHED TRIANGLE-HEADED RIGHT ARROW;;;;
+27A0;HEAVY DASHED TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY DASHED TRIANGLE-HEADED RIGHT ARROW;;;;
+27A1;BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;BLACK RIGHT ARROW;;;;
+27A2;THREE-D TOP-LIGHTED RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;THREE-D TOP-LIGHTED RIGHT ARROWHEAD;;;;
+27A3;THREE-D BOTTOM-LIGHTED RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;THREE-D BOTTOM-LIGHTED RIGHT ARROWHEAD;;;;
+27A4;BLACK RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;BLACK RIGHT ARROWHEAD;;;;
+27A5;HEAVY BLACK CURVED DOWNWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK CURVED DOWN AND RIGHT ARROW;;;;
+27A6;HEAVY BLACK CURVED UPWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK CURVED UP AND RIGHT ARROW;;;;
+27A7;SQUAT BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;SQUAT BLACK RIGHT ARROW;;;;
+27A8;HEAVY CONCAVE-POINTED BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY CONCAVE-POINTED BLACK RIGHT ARROW;;;;
+27A9;RIGHT-SHADED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;RIGHT-SHADED WHITE RIGHT ARROW;;;;
+27AA;LEFT-SHADED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;LEFT-SHADED WHITE RIGHT ARROW;;;;
+27AB;BACK-TILTED SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;BACK-TILTED SHADOWED WHITE RIGHT ARROW;;;;
+27AC;FRONT-TILTED SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;FRONT-TILTED SHADOWED WHITE RIGHT ARROW;;;;
+27AD;HEAVY LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY LOWER RIGHT-SHADOWED WHITE RIGHT ARROW;;;;
+27AE;HEAVY UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY UPPER RIGHT-SHADOWED WHITE RIGHT ARROW;;;;
+27AF;NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHT ARROW;;;;
+27B0;CURLY LOOP;So;0;ON;;;;;N;;;;;
+27B1;NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHT ARROW;;;;
+27B2;CIRCLED HEAVY WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;CIRCLED HEAVY WHITE RIGHT ARROW;;;;
+27B3;WHITE-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;WHITE-FEATHERED RIGHT ARROW;;;;
+27B4;BLACK-FEATHERED SOUTH EAST ARROW;So;0;ON;;;;;N;BLACK-FEATHERED LOWER RIGHT ARROW;;;;
+27B5;BLACK-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;BLACK-FEATHERED RIGHT ARROW;;;;
+27B6;BLACK-FEATHERED NORTH EAST ARROW;So;0;ON;;;;;N;BLACK-FEATHERED UPPER RIGHT ARROW;;;;
+27B7;HEAVY BLACK-FEATHERED SOUTH EAST ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED LOWER RIGHT ARROW;;;;
+27B8;HEAVY BLACK-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED RIGHT ARROW;;;;
+27B9;HEAVY BLACK-FEATHERED NORTH EAST ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED UPPER RIGHT ARROW;;;;
+27BA;TEARDROP-BARBED RIGHTWARDS ARROW;So;0;ON;;;;;N;TEARDROP-BARBED RIGHT ARROW;;;;
+27BB;HEAVY TEARDROP-SHANKED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY TEARDROP-SHANKED RIGHT ARROW;;;;
+27BC;WEDGE-TAILED RIGHTWARDS ARROW;So;0;ON;;;;;N;WEDGE-TAILED RIGHT ARROW;;;;
+27BD;HEAVY WEDGE-TAILED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY WEDGE-TAILED RIGHT ARROW;;;;
+27BE;OPEN-OUTLINED RIGHTWARDS ARROW;So;0;ON;;;;;N;OPEN-OUTLINED RIGHT ARROW;;;;
+27BF;DOUBLE CURLY LOOP;So;0;ON;;;;;N;;;;;
+27C0;THREE DIMENSIONAL ANGLE;Sm;0;ON;;;;;Y;;;;;
+27C1;WHITE TRIANGLE CONTAINING SMALL WHITE TRIANGLE;Sm;0;ON;;;;;N;;;;;
+27C2;PERPENDICULAR;Sm;0;ON;;;;;N;;;;;
+27C3;OPEN SUBSET;Sm;0;ON;;;;;Y;;;;;
+27C4;OPEN SUPERSET;Sm;0;ON;;;;;Y;;;;;
+27C5;LEFT S-SHAPED BAG DELIMITER;Ps;0;ON;;;;;Y;;;;;
+27C6;RIGHT S-SHAPED BAG DELIMITER;Pe;0;ON;;;;;Y;;;;;
+27C7;OR WITH DOT INSIDE;Sm;0;ON;;;;;N;;;;;
+27C8;REVERSE SOLIDUS PRECEDING SUBSET;Sm;0;ON;;;;;Y;;;;;
+27C9;SUPERSET PRECEDING SOLIDUS;Sm;0;ON;;;;;Y;;;;;
+27CA;VERTICAL BAR WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;;
+27CB;MATHEMATICAL RISING DIAGONAL;Sm;0;ON;;;;;Y;;;;;
+27CC;LONG DIVISION;Sm;0;ON;;;;;Y;;;;;
+27CD;MATHEMATICAL FALLING DIAGONAL;Sm;0;ON;;;;;Y;;;;;
+27CE;SQUARED LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+27CF;SQUARED LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+27D0;WHITE DIAMOND WITH CENTRED DOT;Sm;0;ON;;;;;N;;;;;
+27D1;AND WITH DOT;Sm;0;ON;;;;;N;;;;;
+27D2;ELEMENT OF OPENING UPWARDS;Sm;0;ON;;;;;N;;;;;
+27D3;LOWER RIGHT CORNER WITH DOT;Sm;0;ON;;;;;Y;;;;;
+27D4;UPPER LEFT CORNER WITH DOT;Sm;0;ON;;;;;Y;;;;;
+27D5;LEFT OUTER JOIN;Sm;0;ON;;;;;Y;;;;;
+27D6;RIGHT OUTER JOIN;Sm;0;ON;;;;;Y;;;;;
+27D7;FULL OUTER JOIN;Sm;0;ON;;;;;N;;;;;
+27D8;LARGE UP TACK;Sm;0;ON;;;;;N;;;;;
+27D9;LARGE DOWN TACK;Sm;0;ON;;;;;N;;;;;
+27DA;LEFT AND RIGHT DOUBLE TURNSTILE;Sm;0;ON;;;;;N;;;;;
+27DB;LEFT AND RIGHT TACK;Sm;0;ON;;;;;N;;;;;
+27DC;LEFT MULTIMAP;Sm;0;ON;;;;;Y;;;;;
+27DD;LONG RIGHT TACK;Sm;0;ON;;;;;Y;;;;;
+27DE;LONG LEFT TACK;Sm;0;ON;;;;;Y;;;;;
+27DF;UP TACK WITH CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;;
+27E0;LOZENGE DIVIDED BY HORIZONTAL RULE;Sm;0;ON;;;;;N;;;;;
+27E1;WHITE CONCAVE-SIDED DIAMOND;Sm;0;ON;;;;;N;;;;;
+27E2;WHITE CONCAVE-SIDED DIAMOND WITH LEFTWARDS TICK;Sm;0;ON;;;;;Y;;;;;
+27E3;WHITE CONCAVE-SIDED DIAMOND WITH RIGHTWARDS TICK;Sm;0;ON;;;;;Y;;;;;
+27E4;WHITE SQUARE WITH LEFTWARDS TICK;Sm;0;ON;;;;;Y;;;;;
+27E5;WHITE SQUARE WITH RIGHTWARDS TICK;Sm;0;ON;;;;;Y;;;;;
+27E6;MATHEMATICAL LEFT WHITE SQUARE BRACKET;Ps;0;ON;;;;;Y;;;;;
+27E7;MATHEMATICAL RIGHT WHITE SQUARE BRACKET;Pe;0;ON;;;;;Y;;;;;
+27E8;MATHEMATICAL LEFT ANGLE BRACKET;Ps;0;ON;;;;;Y;;;;;
+27E9;MATHEMATICAL RIGHT ANGLE BRACKET;Pe;0;ON;;;;;Y;;;;;
+27EA;MATHEMATICAL LEFT DOUBLE ANGLE BRACKET;Ps;0;ON;;;;;Y;;;;;
+27EB;MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON;;;;;Y;;;;;
+27EC;MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;;;;;
+27ED;MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;;;;;
+27EE;MATHEMATICAL LEFT FLATTENED PARENTHESIS;Ps;0;ON;;;;;Y;;;;;
+27EF;MATHEMATICAL RIGHT FLATTENED PARENTHESIS;Pe;0;ON;;;;;Y;;;;;
+27F0;UPWARDS QUADRUPLE ARROW;Sm;0;ON;;;;;N;;;;;
+27F1;DOWNWARDS QUADRUPLE ARROW;Sm;0;ON;;;;;N;;;;;
+27F2;ANTICLOCKWISE GAPPED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;;
+27F3;CLOCKWISE GAPPED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;;
+27F4;RIGHT ARROW WITH CIRCLED PLUS;Sm;0;ON;;;;;N;;;;;
+27F5;LONG LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+27F6;LONG RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+27F7;LONG LEFT RIGHT ARROW;Sm;0;ON;;;;;N;;;;;
+27F8;LONG LEFTWARDS DOUBLE ARROW;Sm;0;ON;;;;;N;;;;;
+27F9;LONG RIGHTWARDS DOUBLE ARROW;Sm;0;ON;;;;;N;;;;;
+27FA;LONG LEFT RIGHT DOUBLE ARROW;Sm;0;ON;;;;;N;;;;;
+27FB;LONG LEFTWARDS ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+27FC;LONG RIGHTWARDS ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+27FD;LONG LEFTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+27FE;LONG RIGHTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+27FF;LONG RIGHTWARDS SQUIGGLE ARROW;Sm;0;ON;;;;;N;;;;;
+2800;BRAILLE PATTERN BLANK;So;0;L;;;;;N;;;;;
+2801;BRAILLE PATTERN DOTS-1;So;0;L;;;;;N;;;;;
+2802;BRAILLE PATTERN DOTS-2;So;0;L;;;;;N;;;;;
+2803;BRAILLE PATTERN DOTS-12;So;0;L;;;;;N;;;;;
+2804;BRAILLE PATTERN DOTS-3;So;0;L;;;;;N;;;;;
+2805;BRAILLE PATTERN DOTS-13;So;0;L;;;;;N;;;;;
+2806;BRAILLE PATTERN DOTS-23;So;0;L;;;;;N;;;;;
+2807;BRAILLE PATTERN DOTS-123;So;0;L;;;;;N;;;;;
+2808;BRAILLE PATTERN DOTS-4;So;0;L;;;;;N;;;;;
+2809;BRAILLE PATTERN DOTS-14;So;0;L;;;;;N;;;;;
+280A;BRAILLE PATTERN DOTS-24;So;0;L;;;;;N;;;;;
+280B;BRAILLE PATTERN DOTS-124;So;0;L;;;;;N;;;;;
+280C;BRAILLE PATTERN DOTS-34;So;0;L;;;;;N;;;;;
+280D;BRAILLE PATTERN DOTS-134;So;0;L;;;;;N;;;;;
+280E;BRAILLE PATTERN DOTS-234;So;0;L;;;;;N;;;;;
+280F;BRAILLE PATTERN DOTS-1234;So;0;L;;;;;N;;;;;
+2810;BRAILLE PATTERN DOTS-5;So;0;L;;;;;N;;;;;
+2811;BRAILLE PATTERN DOTS-15;So;0;L;;;;;N;;;;;
+2812;BRAILLE PATTERN DOTS-25;So;0;L;;;;;N;;;;;
+2813;BRAILLE PATTERN DOTS-125;So;0;L;;;;;N;;;;;
+2814;BRAILLE PATTERN DOTS-35;So;0;L;;;;;N;;;;;
+2815;BRAILLE PATTERN DOTS-135;So;0;L;;;;;N;;;;;
+2816;BRAILLE PATTERN DOTS-235;So;0;L;;;;;N;;;;;
+2817;BRAILLE PATTERN DOTS-1235;So;0;L;;;;;N;;;;;
+2818;BRAILLE PATTERN DOTS-45;So;0;L;;;;;N;;;;;
+2819;BRAILLE PATTERN DOTS-145;So;0;L;;;;;N;;;;;
+281A;BRAILLE PATTERN DOTS-245;So;0;L;;;;;N;;;;;
+281B;BRAILLE PATTERN DOTS-1245;So;0;L;;;;;N;;;;;
+281C;BRAILLE PATTERN DOTS-345;So;0;L;;;;;N;;;;;
+281D;BRAILLE PATTERN DOTS-1345;So;0;L;;;;;N;;;;;
+281E;BRAILLE PATTERN DOTS-2345;So;0;L;;;;;N;;;;;
+281F;BRAILLE PATTERN DOTS-12345;So;0;L;;;;;N;;;;;
+2820;BRAILLE PATTERN DOTS-6;So;0;L;;;;;N;;;;;
+2821;BRAILLE PATTERN DOTS-16;So;0;L;;;;;N;;;;;
+2822;BRAILLE PATTERN DOTS-26;So;0;L;;;;;N;;;;;
+2823;BRAILLE PATTERN DOTS-126;So;0;L;;;;;N;;;;;
+2824;BRAILLE PATTERN DOTS-36;So;0;L;;;;;N;;;;;
+2825;BRAILLE PATTERN DOTS-136;So;0;L;;;;;N;;;;;
+2826;BRAILLE PATTERN DOTS-236;So;0;L;;;;;N;;;;;
+2827;BRAILLE PATTERN DOTS-1236;So;0;L;;;;;N;;;;;
+2828;BRAILLE PATTERN DOTS-46;So;0;L;;;;;N;;;;;
+2829;BRAILLE PATTERN DOTS-146;So;0;L;;;;;N;;;;;
+282A;BRAILLE PATTERN DOTS-246;So;0;L;;;;;N;;;;;
+282B;BRAILLE PATTERN DOTS-1246;So;0;L;;;;;N;;;;;
+282C;BRAILLE PATTERN DOTS-346;So;0;L;;;;;N;;;;;
+282D;BRAILLE PATTERN DOTS-1346;So;0;L;;;;;N;;;;;
+282E;BRAILLE PATTERN DOTS-2346;So;0;L;;;;;N;;;;;
+282F;BRAILLE PATTERN DOTS-12346;So;0;L;;;;;N;;;;;
+2830;BRAILLE PATTERN DOTS-56;So;0;L;;;;;N;;;;;
+2831;BRAILLE PATTERN DOTS-156;So;0;L;;;;;N;;;;;
+2832;BRAILLE PATTERN DOTS-256;So;0;L;;;;;N;;;;;
+2833;BRAILLE PATTERN DOTS-1256;So;0;L;;;;;N;;;;;
+2834;BRAILLE PATTERN DOTS-356;So;0;L;;;;;N;;;;;
+2835;BRAILLE PATTERN DOTS-1356;So;0;L;;;;;N;;;;;
+2836;BRAILLE PATTERN DOTS-2356;So;0;L;;;;;N;;;;;
+2837;BRAILLE PATTERN DOTS-12356;So;0;L;;;;;N;;;;;
+2838;BRAILLE PATTERN DOTS-456;So;0;L;;;;;N;;;;;
+2839;BRAILLE PATTERN DOTS-1456;So;0;L;;;;;N;;;;;
+283A;BRAILLE PATTERN DOTS-2456;So;0;L;;;;;N;;;;;
+283B;BRAILLE PATTERN DOTS-12456;So;0;L;;;;;N;;;;;
+283C;BRAILLE PATTERN DOTS-3456;So;0;L;;;;;N;;;;;
+283D;BRAILLE PATTERN DOTS-13456;So;0;L;;;;;N;;;;;
+283E;BRAILLE PATTERN DOTS-23456;So;0;L;;;;;N;;;;;
+283F;BRAILLE PATTERN DOTS-123456;So;0;L;;;;;N;;;;;
+2840;BRAILLE PATTERN DOTS-7;So;0;L;;;;;N;;;;;
+2841;BRAILLE PATTERN DOTS-17;So;0;L;;;;;N;;;;;
+2842;BRAILLE PATTERN DOTS-27;So;0;L;;;;;N;;;;;
+2843;BRAILLE PATTERN DOTS-127;So;0;L;;;;;N;;;;;
+2844;BRAILLE PATTERN DOTS-37;So;0;L;;;;;N;;;;;
+2845;BRAILLE PATTERN DOTS-137;So;0;L;;;;;N;;;;;
+2846;BRAILLE PATTERN DOTS-237;So;0;L;;;;;N;;;;;
+2847;BRAILLE PATTERN DOTS-1237;So;0;L;;;;;N;;;;;
+2848;BRAILLE PATTERN DOTS-47;So;0;L;;;;;N;;;;;
+2849;BRAILLE PATTERN DOTS-147;So;0;L;;;;;N;;;;;
+284A;BRAILLE PATTERN DOTS-247;So;0;L;;;;;N;;;;;
+284B;BRAILLE PATTERN DOTS-1247;So;0;L;;;;;N;;;;;
+284C;BRAILLE PATTERN DOTS-347;So;0;L;;;;;N;;;;;
+284D;BRAILLE PATTERN DOTS-1347;So;0;L;;;;;N;;;;;
+284E;BRAILLE PATTERN DOTS-2347;So;0;L;;;;;N;;;;;
+284F;BRAILLE PATTERN DOTS-12347;So;0;L;;;;;N;;;;;
+2850;BRAILLE PATTERN DOTS-57;So;0;L;;;;;N;;;;;
+2851;BRAILLE PATTERN DOTS-157;So;0;L;;;;;N;;;;;
+2852;BRAILLE PATTERN DOTS-257;So;0;L;;;;;N;;;;;
+2853;BRAILLE PATTERN DOTS-1257;So;0;L;;;;;N;;;;;
+2854;BRAILLE PATTERN DOTS-357;So;0;L;;;;;N;;;;;
+2855;BRAILLE PATTERN DOTS-1357;So;0;L;;;;;N;;;;;
+2856;BRAILLE PATTERN DOTS-2357;So;0;L;;;;;N;;;;;
+2857;BRAILLE PATTERN DOTS-12357;So;0;L;;;;;N;;;;;
+2858;BRAILLE PATTERN DOTS-457;So;0;L;;;;;N;;;;;
+2859;BRAILLE PATTERN DOTS-1457;So;0;L;;;;;N;;;;;
+285A;BRAILLE PATTERN DOTS-2457;So;0;L;;;;;N;;;;;
+285B;BRAILLE PATTERN DOTS-12457;So;0;L;;;;;N;;;;;
+285C;BRAILLE PATTERN DOTS-3457;So;0;L;;;;;N;;;;;
+285D;BRAILLE PATTERN DOTS-13457;So;0;L;;;;;N;;;;;
+285E;BRAILLE PATTERN DOTS-23457;So;0;L;;;;;N;;;;;
+285F;BRAILLE PATTERN DOTS-123457;So;0;L;;;;;N;;;;;
+2860;BRAILLE PATTERN DOTS-67;So;0;L;;;;;N;;;;;
+2861;BRAILLE PATTERN DOTS-167;So;0;L;;;;;N;;;;;
+2862;BRAILLE PATTERN DOTS-267;So;0;L;;;;;N;;;;;
+2863;BRAILLE PATTERN DOTS-1267;So;0;L;;;;;N;;;;;
+2864;BRAILLE PATTERN DOTS-367;So;0;L;;;;;N;;;;;
+2865;BRAILLE PATTERN DOTS-1367;So;0;L;;;;;N;;;;;
+2866;BRAILLE PATTERN DOTS-2367;So;0;L;;;;;N;;;;;
+2867;BRAILLE PATTERN DOTS-12367;So;0;L;;;;;N;;;;;
+2868;BRAILLE PATTERN DOTS-467;So;0;L;;;;;N;;;;;
+2869;BRAILLE PATTERN DOTS-1467;So;0;L;;;;;N;;;;;
+286A;BRAILLE PATTERN DOTS-2467;So;0;L;;;;;N;;;;;
+286B;BRAILLE PATTERN DOTS-12467;So;0;L;;;;;N;;;;;
+286C;BRAILLE PATTERN DOTS-3467;So;0;L;;;;;N;;;;;
+286D;BRAILLE PATTERN DOTS-13467;So;0;L;;;;;N;;;;;
+286E;BRAILLE PATTERN DOTS-23467;So;0;L;;;;;N;;;;;
+286F;BRAILLE PATTERN DOTS-123467;So;0;L;;;;;N;;;;;
+2870;BRAILLE PATTERN DOTS-567;So;0;L;;;;;N;;;;;
+2871;BRAILLE PATTERN DOTS-1567;So;0;L;;;;;N;;;;;
+2872;BRAILLE PATTERN DOTS-2567;So;0;L;;;;;N;;;;;
+2873;BRAILLE PATTERN DOTS-12567;So;0;L;;;;;N;;;;;
+2874;BRAILLE PATTERN DOTS-3567;So;0;L;;;;;N;;;;;
+2875;BRAILLE PATTERN DOTS-13567;So;0;L;;;;;N;;;;;
+2876;BRAILLE PATTERN DOTS-23567;So;0;L;;;;;N;;;;;
+2877;BRAILLE PATTERN DOTS-123567;So;0;L;;;;;N;;;;;
+2878;BRAILLE PATTERN DOTS-4567;So;0;L;;;;;N;;;;;
+2879;BRAILLE PATTERN DOTS-14567;So;0;L;;;;;N;;;;;
+287A;BRAILLE PATTERN DOTS-24567;So;0;L;;;;;N;;;;;
+287B;BRAILLE PATTERN DOTS-124567;So;0;L;;;;;N;;;;;
+287C;BRAILLE PATTERN DOTS-34567;So;0;L;;;;;N;;;;;
+287D;BRAILLE PATTERN DOTS-134567;So;0;L;;;;;N;;;;;
+287E;BRAILLE PATTERN DOTS-234567;So;0;L;;;;;N;;;;;
+287F;BRAILLE PATTERN DOTS-1234567;So;0;L;;;;;N;;;;;
+2880;BRAILLE PATTERN DOTS-8;So;0;L;;;;;N;;;;;
+2881;BRAILLE PATTERN DOTS-18;So;0;L;;;;;N;;;;;
+2882;BRAILLE PATTERN DOTS-28;So;0;L;;;;;N;;;;;
+2883;BRAILLE PATTERN DOTS-128;So;0;L;;;;;N;;;;;
+2884;BRAILLE PATTERN DOTS-38;So;0;L;;;;;N;;;;;
+2885;BRAILLE PATTERN DOTS-138;So;0;L;;;;;N;;;;;
+2886;BRAILLE PATTERN DOTS-238;So;0;L;;;;;N;;;;;
+2887;BRAILLE PATTERN DOTS-1238;So;0;L;;;;;N;;;;;
+2888;BRAILLE PATTERN DOTS-48;So;0;L;;;;;N;;;;;
+2889;BRAILLE PATTERN DOTS-148;So;0;L;;;;;N;;;;;
+288A;BRAILLE PATTERN DOTS-248;So;0;L;;;;;N;;;;;
+288B;BRAILLE PATTERN DOTS-1248;So;0;L;;;;;N;;;;;
+288C;BRAILLE PATTERN DOTS-348;So;0;L;;;;;N;;;;;
+288D;BRAILLE PATTERN DOTS-1348;So;0;L;;;;;N;;;;;
+288E;BRAILLE PATTERN DOTS-2348;So;0;L;;;;;N;;;;;
+288F;BRAILLE PATTERN DOTS-12348;So;0;L;;;;;N;;;;;
+2890;BRAILLE PATTERN DOTS-58;So;0;L;;;;;N;;;;;
+2891;BRAILLE PATTERN DOTS-158;So;0;L;;;;;N;;;;;
+2892;BRAILLE PATTERN DOTS-258;So;0;L;;;;;N;;;;;
+2893;BRAILLE PATTERN DOTS-1258;So;0;L;;;;;N;;;;;
+2894;BRAILLE PATTERN DOTS-358;So;0;L;;;;;N;;;;;
+2895;BRAILLE PATTERN DOTS-1358;So;0;L;;;;;N;;;;;
+2896;BRAILLE PATTERN DOTS-2358;So;0;L;;;;;N;;;;;
+2897;BRAILLE PATTERN DOTS-12358;So;0;L;;;;;N;;;;;
+2898;BRAILLE PATTERN DOTS-458;So;0;L;;;;;N;;;;;
+2899;BRAILLE PATTERN DOTS-1458;So;0;L;;;;;N;;;;;
+289A;BRAILLE PATTERN DOTS-2458;So;0;L;;;;;N;;;;;
+289B;BRAILLE PATTERN DOTS-12458;So;0;L;;;;;N;;;;;
+289C;BRAILLE PATTERN DOTS-3458;So;0;L;;;;;N;;;;;
+289D;BRAILLE PATTERN DOTS-13458;So;0;L;;;;;N;;;;;
+289E;BRAILLE PATTERN DOTS-23458;So;0;L;;;;;N;;;;;
+289F;BRAILLE PATTERN DOTS-123458;So;0;L;;;;;N;;;;;
+28A0;BRAILLE PATTERN DOTS-68;So;0;L;;;;;N;;;;;
+28A1;BRAILLE PATTERN DOTS-168;So;0;L;;;;;N;;;;;
+28A2;BRAILLE PATTERN DOTS-268;So;0;L;;;;;N;;;;;
+28A3;BRAILLE PATTERN DOTS-1268;So;0;L;;;;;N;;;;;
+28A4;BRAILLE PATTERN DOTS-368;So;0;L;;;;;N;;;;;
+28A5;BRAILLE PATTERN DOTS-1368;So;0;L;;;;;N;;;;;
+28A6;BRAILLE PATTERN DOTS-2368;So;0;L;;;;;N;;;;;
+28A7;BRAILLE PATTERN DOTS-12368;So;0;L;;;;;N;;;;;
+28A8;BRAILLE PATTERN DOTS-468;So;0;L;;;;;N;;;;;
+28A9;BRAILLE PATTERN DOTS-1468;So;0;L;;;;;N;;;;;
+28AA;BRAILLE PATTERN DOTS-2468;So;0;L;;;;;N;;;;;
+28AB;BRAILLE PATTERN DOTS-12468;So;0;L;;;;;N;;;;;
+28AC;BRAILLE PATTERN DOTS-3468;So;0;L;;;;;N;;;;;
+28AD;BRAILLE PATTERN DOTS-13468;So;0;L;;;;;N;;;;;
+28AE;BRAILLE PATTERN DOTS-23468;So;0;L;;;;;N;;;;;
+28AF;BRAILLE PATTERN DOTS-123468;So;0;L;;;;;N;;;;;
+28B0;BRAILLE PATTERN DOTS-568;So;0;L;;;;;N;;;;;
+28B1;BRAILLE PATTERN DOTS-1568;So;0;L;;;;;N;;;;;
+28B2;BRAILLE PATTERN DOTS-2568;So;0;L;;;;;N;;;;;
+28B3;BRAILLE PATTERN DOTS-12568;So;0;L;;;;;N;;;;;
+28B4;BRAILLE PATTERN DOTS-3568;So;0;L;;;;;N;;;;;
+28B5;BRAILLE PATTERN DOTS-13568;So;0;L;;;;;N;;;;;
+28B6;BRAILLE PATTERN DOTS-23568;So;0;L;;;;;N;;;;;
+28B7;BRAILLE PATTERN DOTS-123568;So;0;L;;;;;N;;;;;
+28B8;BRAILLE PATTERN DOTS-4568;So;0;L;;;;;N;;;;;
+28B9;BRAILLE PATTERN DOTS-14568;So;0;L;;;;;N;;;;;
+28BA;BRAILLE PATTERN DOTS-24568;So;0;L;;;;;N;;;;;
+28BB;BRAILLE PATTERN DOTS-124568;So;0;L;;;;;N;;;;;
+28BC;BRAILLE PATTERN DOTS-34568;So;0;L;;;;;N;;;;;
+28BD;BRAILLE PATTERN DOTS-134568;So;0;L;;;;;N;;;;;
+28BE;BRAILLE PATTERN DOTS-234568;So;0;L;;;;;N;;;;;
+28BF;BRAILLE PATTERN DOTS-1234568;So;0;L;;;;;N;;;;;
+28C0;BRAILLE PATTERN DOTS-78;So;0;L;;;;;N;;;;;
+28C1;BRAILLE PATTERN DOTS-178;So;0;L;;;;;N;;;;;
+28C2;BRAILLE PATTERN DOTS-278;So;0;L;;;;;N;;;;;
+28C3;BRAILLE PATTERN DOTS-1278;So;0;L;;;;;N;;;;;
+28C4;BRAILLE PATTERN DOTS-378;So;0;L;;;;;N;;;;;
+28C5;BRAILLE PATTERN DOTS-1378;So;0;L;;;;;N;;;;;
+28C6;BRAILLE PATTERN DOTS-2378;So;0;L;;;;;N;;;;;
+28C7;BRAILLE PATTERN DOTS-12378;So;0;L;;;;;N;;;;;
+28C8;BRAILLE PATTERN DOTS-478;So;0;L;;;;;N;;;;;
+28C9;BRAILLE PATTERN DOTS-1478;So;0;L;;;;;N;;;;;
+28CA;BRAILLE PATTERN DOTS-2478;So;0;L;;;;;N;;;;;
+28CB;BRAILLE PATTERN DOTS-12478;So;0;L;;;;;N;;;;;
+28CC;BRAILLE PATTERN DOTS-3478;So;0;L;;;;;N;;;;;
+28CD;BRAILLE PATTERN DOTS-13478;So;0;L;;;;;N;;;;;
+28CE;BRAILLE PATTERN DOTS-23478;So;0;L;;;;;N;;;;;
+28CF;BRAILLE PATTERN DOTS-123478;So;0;L;;;;;N;;;;;
+28D0;BRAILLE PATTERN DOTS-578;So;0;L;;;;;N;;;;;
+28D1;BRAILLE PATTERN DOTS-1578;So;0;L;;;;;N;;;;;
+28D2;BRAILLE PATTERN DOTS-2578;So;0;L;;;;;N;;;;;
+28D3;BRAILLE PATTERN DOTS-12578;So;0;L;;;;;N;;;;;
+28D4;BRAILLE PATTERN DOTS-3578;So;0;L;;;;;N;;;;;
+28D5;BRAILLE PATTERN DOTS-13578;So;0;L;;;;;N;;;;;
+28D6;BRAILLE PATTERN DOTS-23578;So;0;L;;;;;N;;;;;
+28D7;BRAILLE PATTERN DOTS-123578;So;0;L;;;;;N;;;;;
+28D8;BRAILLE PATTERN DOTS-4578;So;0;L;;;;;N;;;;;
+28D9;BRAILLE PATTERN DOTS-14578;So;0;L;;;;;N;;;;;
+28DA;BRAILLE PATTERN DOTS-24578;So;0;L;;;;;N;;;;;
+28DB;BRAILLE PATTERN DOTS-124578;So;0;L;;;;;N;;;;;
+28DC;BRAILLE PATTERN DOTS-34578;So;0;L;;;;;N;;;;;
+28DD;BRAILLE PATTERN DOTS-134578;So;0;L;;;;;N;;;;;
+28DE;BRAILLE PATTERN DOTS-234578;So;0;L;;;;;N;;;;;
+28DF;BRAILLE PATTERN DOTS-1234578;So;0;L;;;;;N;;;;;
+28E0;BRAILLE PATTERN DOTS-678;So;0;L;;;;;N;;;;;
+28E1;BRAILLE PATTERN DOTS-1678;So;0;L;;;;;N;;;;;
+28E2;BRAILLE PATTERN DOTS-2678;So;0;L;;;;;N;;;;;
+28E3;BRAILLE PATTERN DOTS-12678;So;0;L;;;;;N;;;;;
+28E4;BRAILLE PATTERN DOTS-3678;So;0;L;;;;;N;;;;;
+28E5;BRAILLE PATTERN DOTS-13678;So;0;L;;;;;N;;;;;
+28E6;BRAILLE PATTERN DOTS-23678;So;0;L;;;;;N;;;;;
+28E7;BRAILLE PATTERN DOTS-123678;So;0;L;;;;;N;;;;;
+28E8;BRAILLE PATTERN DOTS-4678;So;0;L;;;;;N;;;;;
+28E9;BRAILLE PATTERN DOTS-14678;So;0;L;;;;;N;;;;;
+28EA;BRAILLE PATTERN DOTS-24678;So;0;L;;;;;N;;;;;
+28EB;BRAILLE PATTERN DOTS-124678;So;0;L;;;;;N;;;;;
+28EC;BRAILLE PATTERN DOTS-34678;So;0;L;;;;;N;;;;;
+28ED;BRAILLE PATTERN DOTS-134678;So;0;L;;;;;N;;;;;
+28EE;BRAILLE PATTERN DOTS-234678;So;0;L;;;;;N;;;;;
+28EF;BRAILLE PATTERN DOTS-1234678;So;0;L;;;;;N;;;;;
+28F0;BRAILLE PATTERN DOTS-5678;So;0;L;;;;;N;;;;;
+28F1;BRAILLE PATTERN DOTS-15678;So;0;L;;;;;N;;;;;
+28F2;BRAILLE PATTERN DOTS-25678;So;0;L;;;;;N;;;;;
+28F3;BRAILLE PATTERN DOTS-125678;So;0;L;;;;;N;;;;;
+28F4;BRAILLE PATTERN DOTS-35678;So;0;L;;;;;N;;;;;
+28F5;BRAILLE PATTERN DOTS-135678;So;0;L;;;;;N;;;;;
+28F6;BRAILLE PATTERN DOTS-235678;So;0;L;;;;;N;;;;;
+28F7;BRAILLE PATTERN DOTS-1235678;So;0;L;;;;;N;;;;;
+28F8;BRAILLE PATTERN DOTS-45678;So;0;L;;;;;N;;;;;
+28F9;BRAILLE PATTERN DOTS-145678;So;0;L;;;;;N;;;;;
+28FA;BRAILLE PATTERN DOTS-245678;So;0;L;;;;;N;;;;;
+28FB;BRAILLE PATTERN DOTS-1245678;So;0;L;;;;;N;;;;;
+28FC;BRAILLE PATTERN DOTS-345678;So;0;L;;;;;N;;;;;
+28FD;BRAILLE PATTERN DOTS-1345678;So;0;L;;;;;N;;;;;
+28FE;BRAILLE PATTERN DOTS-2345678;So;0;L;;;;;N;;;;;
+28FF;BRAILLE PATTERN DOTS-12345678;So;0;L;;;;;N;;;;;
+2900;RIGHTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2901;RIGHTWARDS TWO-HEADED ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2902;LEFTWARDS DOUBLE ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2903;RIGHTWARDS DOUBLE ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2904;LEFT RIGHT DOUBLE ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2905;RIGHTWARDS TWO-HEADED ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+2906;LEFTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+2907;RIGHTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+2908;DOWNWARDS ARROW WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;;
+2909;UPWARDS ARROW WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;;
+290A;UPWARDS TRIPLE ARROW;Sm;0;ON;;;;;N;;;;;
+290B;DOWNWARDS TRIPLE ARROW;Sm;0;ON;;;;;N;;;;;
+290C;LEFTWARDS DOUBLE DASH ARROW;Sm;0;ON;;;;;N;;;;;
+290D;RIGHTWARDS DOUBLE DASH ARROW;Sm;0;ON;;;;;N;;;;;
+290E;LEFTWARDS TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;;
+290F;RIGHTWARDS TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;;
+2910;RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;;
+2911;RIGHTWARDS ARROW WITH DOTTED STEM;Sm;0;ON;;;;;N;;;;;
+2912;UPWARDS ARROW TO BAR;Sm;0;ON;;;;;N;;;;;
+2913;DOWNWARDS ARROW TO BAR;Sm;0;ON;;;;;N;;;;;
+2914;RIGHTWARDS ARROW WITH TAIL WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2915;RIGHTWARDS ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2916;RIGHTWARDS TWO-HEADED ARROW WITH TAIL;Sm;0;ON;;;;;N;;;;;
+2917;RIGHTWARDS TWO-HEADED ARROW WITH TAIL WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2918;RIGHTWARDS TWO-HEADED ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2919;LEFTWARDS ARROW-TAIL;Sm;0;ON;;;;;N;;;;;
+291A;RIGHTWARDS ARROW-TAIL;Sm;0;ON;;;;;N;;;;;
+291B;LEFTWARDS DOUBLE ARROW-TAIL;Sm;0;ON;;;;;N;;;;;
+291C;RIGHTWARDS DOUBLE ARROW-TAIL;Sm;0;ON;;;;;N;;;;;
+291D;LEFTWARDS ARROW TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;;
+291E;RIGHTWARDS ARROW TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;;
+291F;LEFTWARDS ARROW FROM BAR TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;;
+2920;RIGHTWARDS ARROW FROM BAR TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;;
+2921;NORTH WEST AND SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+2922;NORTH EAST AND SOUTH WEST ARROW;Sm;0;ON;;;;;N;;;;;
+2923;NORTH WEST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;;
+2924;NORTH EAST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;;
+2925;SOUTH EAST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;;
+2926;SOUTH WEST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;;
+2927;NORTH WEST ARROW AND NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+2928;NORTH EAST ARROW AND SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+2929;SOUTH EAST ARROW AND SOUTH WEST ARROW;Sm;0;ON;;;;;N;;;;;
+292A;SOUTH WEST ARROW AND NORTH WEST ARROW;Sm;0;ON;;;;;N;;;;;
+292B;RISING DIAGONAL CROSSING FALLING DIAGONAL;Sm;0;ON;;;;;N;;;;;
+292C;FALLING DIAGONAL CROSSING RISING DIAGONAL;Sm;0;ON;;;;;N;;;;;
+292D;SOUTH EAST ARROW CROSSING NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+292E;NORTH EAST ARROW CROSSING SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+292F;FALLING DIAGONAL CROSSING NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+2930;RISING DIAGONAL CROSSING SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+2931;NORTH EAST ARROW CROSSING NORTH WEST ARROW;Sm;0;ON;;;;;N;;;;;
+2932;NORTH WEST ARROW CROSSING NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+2933;WAVE ARROW POINTING DIRECTLY RIGHT;Sm;0;ON;;;;;N;;;;;
+2934;ARROW POINTING RIGHTWARDS THEN CURVING UPWARDS;Sm;0;ON;;;;;N;;;;;
+2935;ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS;Sm;0;ON;;;;;N;;;;;
+2936;ARROW POINTING DOWNWARDS THEN CURVING LEFTWARDS;Sm;0;ON;;;;;N;;;;;
+2937;ARROW POINTING DOWNWARDS THEN CURVING RIGHTWARDS;Sm;0;ON;;;;;N;;;;;
+2938;RIGHT-SIDE ARC CLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;;
+2939;LEFT-SIDE ARC ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;;
+293A;TOP ARC ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;;
+293B;BOTTOM ARC ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;;
+293C;TOP ARC CLOCKWISE ARROW WITH MINUS;Sm;0;ON;;;;;N;;;;;
+293D;TOP ARC ANTICLOCKWISE ARROW WITH PLUS;Sm;0;ON;;;;;N;;;;;
+293E;LOWER RIGHT SEMICIRCULAR CLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;;
+293F;LOWER LEFT SEMICIRCULAR ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;;
+2940;ANTICLOCKWISE CLOSED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;;
+2941;CLOCKWISE CLOSED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;;
+2942;RIGHTWARDS ARROW ABOVE SHORT LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2943;LEFTWARDS ARROW ABOVE SHORT RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2944;SHORT RIGHTWARDS ARROW ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2945;RIGHTWARDS ARROW WITH PLUS BELOW;Sm;0;ON;;;;;N;;;;;
+2946;LEFTWARDS ARROW WITH PLUS BELOW;Sm;0;ON;;;;;N;;;;;
+2947;RIGHTWARDS ARROW THROUGH X;Sm;0;ON;;;;;N;;;;;
+2948;LEFT RIGHT ARROW THROUGH SMALL CIRCLE;Sm;0;ON;;;;;N;;;;;
+2949;UPWARDS TWO-HEADED ARROW FROM SMALL CIRCLE;Sm;0;ON;;;;;N;;;;;
+294A;LEFT BARB UP RIGHT BARB DOWN HARPOON;Sm;0;ON;;;;;N;;;;;
+294B;LEFT BARB DOWN RIGHT BARB UP HARPOON;Sm;0;ON;;;;;N;;;;;
+294C;UP BARB RIGHT DOWN BARB LEFT HARPOON;Sm;0;ON;;;;;N;;;;;
+294D;UP BARB LEFT DOWN BARB RIGHT HARPOON;Sm;0;ON;;;;;N;;;;;
+294E;LEFT BARB UP RIGHT BARB UP HARPOON;Sm;0;ON;;;;;N;;;;;
+294F;UP BARB RIGHT DOWN BARB RIGHT HARPOON;Sm;0;ON;;;;;N;;;;;
+2950;LEFT BARB DOWN RIGHT BARB DOWN HARPOON;Sm;0;ON;;;;;N;;;;;
+2951;UP BARB LEFT DOWN BARB LEFT HARPOON;Sm;0;ON;;;;;N;;;;;
+2952;LEFTWARDS HARPOON WITH BARB UP TO BAR;Sm;0;ON;;;;;N;;;;;
+2953;RIGHTWARDS HARPOON WITH BARB UP TO BAR;Sm;0;ON;;;;;N;;;;;
+2954;UPWARDS HARPOON WITH BARB RIGHT TO BAR;Sm;0;ON;;;;;N;;;;;
+2955;DOWNWARDS HARPOON WITH BARB RIGHT TO BAR;Sm;0;ON;;;;;N;;;;;
+2956;LEFTWARDS HARPOON WITH BARB DOWN TO BAR;Sm;0;ON;;;;;N;;;;;
+2957;RIGHTWARDS HARPOON WITH BARB DOWN TO BAR;Sm;0;ON;;;;;N;;;;;
+2958;UPWARDS HARPOON WITH BARB LEFT TO BAR;Sm;0;ON;;;;;N;;;;;
+2959;DOWNWARDS HARPOON WITH BARB LEFT TO BAR;Sm;0;ON;;;;;N;;;;;
+295A;LEFTWARDS HARPOON WITH BARB UP FROM BAR;Sm;0;ON;;;;;N;;;;;
+295B;RIGHTWARDS HARPOON WITH BARB UP FROM BAR;Sm;0;ON;;;;;N;;;;;
+295C;UPWARDS HARPOON WITH BARB RIGHT FROM BAR;Sm;0;ON;;;;;N;;;;;
+295D;DOWNWARDS HARPOON WITH BARB RIGHT FROM BAR;Sm;0;ON;;;;;N;;;;;
+295E;LEFTWARDS HARPOON WITH BARB DOWN FROM BAR;Sm;0;ON;;;;;N;;;;;
+295F;RIGHTWARDS HARPOON WITH BARB DOWN FROM BAR;Sm;0;ON;;;;;N;;;;;
+2960;UPWARDS HARPOON WITH BARB LEFT FROM BAR;Sm;0;ON;;;;;N;;;;;
+2961;DOWNWARDS HARPOON WITH BARB LEFT FROM BAR;Sm;0;ON;;;;;N;;;;;
+2962;LEFTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;;
+2963;UPWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;;
+2964;RIGHTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;;
+2965;DOWNWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;;
+2966;LEFTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB UP;Sm;0;ON;;;;;N;;;;;
+2967;LEFTWARDS HARPOON WITH BARB DOWN ABOVE RIGHTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;;
+2968;RIGHTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB UP;Sm;0;ON;;;;;N;;;;;
+2969;RIGHTWARDS HARPOON WITH BARB DOWN ABOVE LEFTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;;
+296A;LEFTWARDS HARPOON WITH BARB UP ABOVE LONG DASH;Sm;0;ON;;;;;N;;;;;
+296B;LEFTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH;Sm;0;ON;;;;;N;;;;;
+296C;RIGHTWARDS HARPOON WITH BARB UP ABOVE LONG DASH;Sm;0;ON;;;;;N;;;;;
+296D;RIGHTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH;Sm;0;ON;;;;;N;;;;;
+296E;UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;;
+296F;DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;;
+2970;RIGHT DOUBLE ARROW WITH ROUNDED HEAD;Sm;0;ON;;;;;N;;;;;
+2971;EQUALS SIGN ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2972;TILDE OPERATOR ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2973;LEFTWARDS ARROW ABOVE TILDE OPERATOR;Sm;0;ON;;;;;N;;;;;
+2974;RIGHTWARDS ARROW ABOVE TILDE OPERATOR;Sm;0;ON;;;;;N;;;;;
+2975;RIGHTWARDS ARROW ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;N;;;;;
+2976;LESS-THAN ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2977;LEFTWARDS ARROW THROUGH LESS-THAN;Sm;0;ON;;;;;N;;;;;
+2978;GREATER-THAN ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2979;SUBSET ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+297A;LEFTWARDS ARROW THROUGH SUBSET;Sm;0;ON;;;;;N;;;;;
+297B;SUPERSET ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+297C;LEFT FISH TAIL;Sm;0;ON;;;;;N;;;;;
+297D;RIGHT FISH TAIL;Sm;0;ON;;;;;N;;;;;
+297E;UP FISH TAIL;Sm;0;ON;;;;;N;;;;;
+297F;DOWN FISH TAIL;Sm;0;ON;;;;;N;;;;;
+2980;TRIPLE VERTICAL BAR DELIMITER;Sm;0;ON;;;;;N;;;;;
+2981;Z NOTATION SPOT;Sm;0;ON;;;;;N;;;;;
+2982;Z NOTATION TYPE COLON;Sm;0;ON;;;;;N;;;;;
+2983;LEFT WHITE CURLY BRACKET;Ps;0;ON;;;;;Y;;;;;
+2984;RIGHT WHITE CURLY BRACKET;Pe;0;ON;;;;;Y;;;;;
+2985;LEFT WHITE PARENTHESIS;Ps;0;ON;;;;;Y;;;;;
+2986;RIGHT WHITE PARENTHESIS;Pe;0;ON;;;;;Y;;;;;
+2987;Z NOTATION LEFT IMAGE BRACKET;Ps;0;ON;;;;;Y;;;;;
+2988;Z NOTATION RIGHT IMAGE BRACKET;Pe;0;ON;;;;;Y;;;;;
+2989;Z NOTATION LEFT BINDING BRACKET;Ps;0;ON;;;;;Y;;;;;
+298A;Z NOTATION RIGHT BINDING BRACKET;Pe;0;ON;;;;;Y;;;;;
+298B;LEFT SQUARE BRACKET WITH UNDERBAR;Ps;0;ON;;;;;Y;;;;;
+298C;RIGHT SQUARE BRACKET WITH UNDERBAR;Pe;0;ON;;;;;Y;;;;;
+298D;LEFT SQUARE BRACKET WITH TICK IN TOP CORNER;Ps;0;ON;;;;;Y;;;;;
+298E;RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER;Pe;0;ON;;;;;Y;;;;;
+298F;LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER;Ps;0;ON;;;;;Y;;;;;
+2990;RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER;Pe;0;ON;;;;;Y;;;;;
+2991;LEFT ANGLE BRACKET WITH DOT;Ps;0;ON;;;;;Y;;;;;
+2992;RIGHT ANGLE BRACKET WITH DOT;Pe;0;ON;;;;;Y;;;;;
+2993;LEFT ARC LESS-THAN BRACKET;Ps;0;ON;;;;;Y;;;;;
+2994;RIGHT ARC GREATER-THAN BRACKET;Pe;0;ON;;;;;Y;;;;;
+2995;DOUBLE LEFT ARC GREATER-THAN BRACKET;Ps;0;ON;;;;;Y;;;;;
+2996;DOUBLE RIGHT ARC LESS-THAN BRACKET;Pe;0;ON;;;;;Y;;;;;
+2997;LEFT BLACK TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;;;;;
+2998;RIGHT BLACK TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;;;;;
+2999;DOTTED FENCE;Sm;0;ON;;;;;N;;;;;
+299A;VERTICAL ZIGZAG LINE;Sm;0;ON;;;;;N;;;;;
+299B;MEASURED ANGLE OPENING LEFT;Sm;0;ON;;;;;Y;;;;;
+299C;RIGHT ANGLE VARIANT WITH SQUARE;Sm;0;ON;;;;;Y;;;;;
+299D;MEASURED RIGHT ANGLE WITH DOT;Sm;0;ON;;;;;Y;;;;;
+299E;ANGLE WITH S INSIDE;Sm;0;ON;;;;;Y;;;;;
+299F;ACUTE ANGLE;Sm;0;ON;;;;;Y;;;;;
+29A0;SPHERICAL ANGLE OPENING LEFT;Sm;0;ON;;;;;Y;;;;;
+29A1;SPHERICAL ANGLE OPENING UP;Sm;0;ON;;;;;N;;;;;
+29A2;TURNED ANGLE;Sm;0;ON;;;;;Y;;;;;
+29A3;REVERSED ANGLE;Sm;0;ON;;;;;Y;;;;;
+29A4;ANGLE WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;;
+29A5;REVERSED ANGLE WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;;
+29A6;OBLIQUE ANGLE OPENING UP;Sm;0;ON;;;;;Y;;;;;
+29A7;OBLIQUE ANGLE OPENING DOWN;Sm;0;ON;;;;;Y;;;;;
+29A8;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND RIGHT;Sm;0;ON;;;;;Y;;;;;
+29A9;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND LEFT;Sm;0;ON;;;;;Y;;;;;
+29AA;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND RIGHT;Sm;0;ON;;;;;Y;;;;;
+29AB;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND LEFT;Sm;0;ON;;;;;Y;;;;;
+29AC;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND UP;Sm;0;ON;;;;;Y;;;;;
+29AD;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND UP;Sm;0;ON;;;;;Y;;;;;
+29AE;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND DOWN;Sm;0;ON;;;;;Y;;;;;
+29AF;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND DOWN;Sm;0;ON;;;;;Y;;;;;
+29B0;REVERSED EMPTY SET;Sm;0;ON;;;;;N;;;;;
+29B1;EMPTY SET WITH OVERBAR;Sm;0;ON;;;;;N;;;;;
+29B2;EMPTY SET WITH SMALL CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;;
+29B3;EMPTY SET WITH RIGHT ARROW ABOVE;Sm;0;ON;;;;;N;;;;;
+29B4;EMPTY SET WITH LEFT ARROW ABOVE;Sm;0;ON;;;;;N;;;;;
+29B5;CIRCLE WITH HORIZONTAL BAR;Sm;0;ON;;;;;N;;;;;
+29B6;CIRCLED VERTICAL BAR;Sm;0;ON;;;;;N;;;;;
+29B7;CIRCLED PARALLEL;Sm;0;ON;;;;;N;;;;;
+29B8;CIRCLED REVERSE SOLIDUS;Sm;0;ON;;;;;Y;;;;;
+29B9;CIRCLED PERPENDICULAR;Sm;0;ON;;;;;N;;;;;
+29BA;CIRCLE DIVIDED BY HORIZONTAL BAR AND TOP HALF DIVIDED BY VERTICAL BAR;Sm;0;ON;;;;;N;;;;;
+29BB;CIRCLE WITH SUPERIMPOSED X;Sm;0;ON;;;;;N;;;;;
+29BC;CIRCLED ANTICLOCKWISE-ROTATED DIVISION SIGN;Sm;0;ON;;;;;N;;;;;
+29BD;UP ARROW THROUGH CIRCLE;Sm;0;ON;;;;;N;;;;;
+29BE;CIRCLED WHITE BULLET;Sm;0;ON;;;;;N;;;;;
+29BF;CIRCLED BULLET;Sm;0;ON;;;;;N;;;;;
+29C0;CIRCLED LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+29C1;CIRCLED GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+29C2;CIRCLE WITH SMALL CIRCLE TO THE RIGHT;Sm;0;ON;;;;;Y;;;;;
+29C3;CIRCLE WITH TWO HORIZONTAL STROKES TO THE RIGHT;Sm;0;ON;;;;;Y;;;;;
+29C4;SQUARED RISING DIAGONAL SLASH;Sm;0;ON;;;;;Y;;;;;
+29C5;SQUARED FALLING DIAGONAL SLASH;Sm;0;ON;;;;;Y;;;;;
+29C6;SQUARED ASTERISK;Sm;0;ON;;;;;N;;;;;
+29C7;SQUARED SMALL CIRCLE;Sm;0;ON;;;;;N;;;;;
+29C8;SQUARED SQUARE;Sm;0;ON;;;;;N;;;;;
+29C9;TWO JOINED SQUARES;Sm;0;ON;;;;;Y;;;;;
+29CA;TRIANGLE WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;;
+29CB;TRIANGLE WITH UNDERBAR;Sm;0;ON;;;;;N;;;;;
+29CC;S IN TRIANGLE;Sm;0;ON;;;;;N;;;;;
+29CD;TRIANGLE WITH SERIFS AT BOTTOM;Sm;0;ON;;;;;N;;;;;
+29CE;RIGHT TRIANGLE ABOVE LEFT TRIANGLE;Sm;0;ON;;;;;Y;;;;;
+29CF;LEFT TRIANGLE BESIDE VERTICAL BAR;Sm;0;ON;;;;;Y;;;;;
+29D0;VERTICAL BAR BESIDE RIGHT TRIANGLE;Sm;0;ON;;;;;Y;;;;;
+29D1;BOWTIE WITH LEFT HALF BLACK;Sm;0;ON;;;;;Y;;;;;
+29D2;BOWTIE WITH RIGHT HALF BLACK;Sm;0;ON;;;;;Y;;;;;
+29D3;BLACK BOWTIE;Sm;0;ON;;;;;N;;;;;
+29D4;TIMES WITH LEFT HALF BLACK;Sm;0;ON;;;;;Y;;;;;
+29D5;TIMES WITH RIGHT HALF BLACK;Sm;0;ON;;;;;Y;;;;;
+29D6;WHITE HOURGLASS;Sm;0;ON;;;;;N;;;;;
+29D7;BLACK HOURGLASS;Sm;0;ON;;;;;N;;;;;
+29D8;LEFT WIGGLY FENCE;Ps;0;ON;;;;;Y;;;;;
+29D9;RIGHT WIGGLY FENCE;Pe;0;ON;;;;;Y;;;;;
+29DA;LEFT DOUBLE WIGGLY FENCE;Ps;0;ON;;;;;Y;;;;;
+29DB;RIGHT DOUBLE WIGGLY FENCE;Pe;0;ON;;;;;Y;;;;;
+29DC;INCOMPLETE INFINITY;Sm;0;ON;;;;;Y;;;;;
+29DD;TIE OVER INFINITY;Sm;0;ON;;;;;N;;;;;
+29DE;INFINITY NEGATED WITH VERTICAL BAR;Sm;0;ON;;;;;N;;;;;
+29DF;DOUBLE-ENDED MULTIMAP;Sm;0;ON;;;;;N;;;;;
+29E0;SQUARE WITH CONTOURED OUTLINE;Sm;0;ON;;;;;N;;;;;
+29E1;INCREASES AS;Sm;0;ON;;;;;Y;;;;;
+29E2;SHUFFLE PRODUCT;Sm;0;ON;;;;;N;;;;;
+29E3;EQUALS SIGN AND SLANTED PARALLEL;Sm;0;ON;;;;;Y;;;;;
+29E4;EQUALS SIGN AND SLANTED PARALLEL WITH TILDE ABOVE;Sm;0;ON;;;;;Y;;;;;
+29E5;IDENTICAL TO AND SLANTED PARALLEL;Sm;0;ON;;;;;Y;;;;;
+29E6;GLEICH STARK;Sm;0;ON;;;;;N;;;;;
+29E7;THERMODYNAMIC;Sm;0;ON;;;;;N;;;;;
+29E8;DOWN-POINTING TRIANGLE WITH LEFT HALF BLACK;Sm;0;ON;;;;;Y;;;;;
+29E9;DOWN-POINTING TRIANGLE WITH RIGHT HALF BLACK;Sm;0;ON;;;;;Y;;;;;
+29EA;BLACK DIAMOND WITH DOWN ARROW;Sm;0;ON;;;;;N;;;;;
+29EB;BLACK LOZENGE;Sm;0;ON;;;;;N;;;;;
+29EC;WHITE CIRCLE WITH DOWN ARROW;Sm;0;ON;;;;;N;;;;;
+29ED;BLACK CIRCLE WITH DOWN ARROW;Sm;0;ON;;;;;N;;;;;
+29EE;ERROR-BARRED WHITE SQUARE;Sm;0;ON;;;;;N;;;;;
+29EF;ERROR-BARRED BLACK SQUARE;Sm;0;ON;;;;;N;;;;;
+29F0;ERROR-BARRED WHITE DIAMOND;Sm;0;ON;;;;;N;;;;;
+29F1;ERROR-BARRED BLACK DIAMOND;Sm;0;ON;;;;;N;;;;;
+29F2;ERROR-BARRED WHITE CIRCLE;Sm;0;ON;;;;;N;;;;;
+29F3;ERROR-BARRED BLACK CIRCLE;Sm;0;ON;;;;;N;;;;;
+29F4;RULE-DELAYED;Sm;0;ON;;;;;Y;;;;;
+29F5;REVERSE SOLIDUS OPERATOR;Sm;0;ON;;;;;Y;;;;;
+29F6;SOLIDUS WITH OVERBAR;Sm;0;ON;;;;;Y;;;;;
+29F7;REVERSE SOLIDUS WITH HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+29F8;BIG SOLIDUS;Sm;0;ON;;;;;Y;;;;;
+29F9;BIG REVERSE SOLIDUS;Sm;0;ON;;;;;Y;;;;;
+29FA;DOUBLE PLUS;Sm;0;ON;;;;;N;;;;;
+29FB;TRIPLE PLUS;Sm;0;ON;;;;;N;;;;;
+29FC;LEFT-POINTING CURVED ANGLE BRACKET;Ps;0;ON;;;;;Y;;;;;
+29FD;RIGHT-POINTING CURVED ANGLE BRACKET;Pe;0;ON;;;;;Y;;;;;
+29FE;TINY;Sm;0;ON;;;;;N;;;;;
+29FF;MINY;Sm;0;ON;;;;;N;;;;;
+2A00;N-ARY CIRCLED DOT OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A01;N-ARY CIRCLED PLUS OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A02;N-ARY CIRCLED TIMES OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A03;N-ARY UNION OPERATOR WITH DOT;Sm;0;ON;;;;;N;;;;;
+2A04;N-ARY UNION OPERATOR WITH PLUS;Sm;0;ON;;;;;N;;;;;
+2A05;N-ARY SQUARE INTERSECTION OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A06;N-ARY SQUARE UNION OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A07;TWO LOGICAL AND OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A08;TWO LOGICAL OR OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A09;N-ARY TIMES OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A0A;MODULO TWO SUM;Sm;0;ON;;;;;Y;;;;;
+2A0B;SUMMATION WITH INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2A0C;QUADRUPLE INTEGRAL OPERATOR;Sm;0;ON;<compat> 222B 222B 222B 222B;;;;Y;;;;;
+2A0D;FINITE PART INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2A0E;INTEGRAL WITH DOUBLE STROKE;Sm;0;ON;;;;;Y;;;;;
+2A0F;INTEGRAL AVERAGE WITH SLASH;Sm;0;ON;;;;;Y;;;;;
+2A10;CIRCULATION FUNCTION;Sm;0;ON;;;;;Y;;;;;
+2A11;ANTICLOCKWISE INTEGRATION;Sm;0;ON;;;;;Y;;;;;
+2A12;LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE;Sm;0;ON;;;;;Y;;;;;
+2A13;LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE;Sm;0;ON;;;;;Y;;;;;
+2A14;LINE INTEGRATION NOT INCLUDING THE POLE;Sm;0;ON;;;;;Y;;;;;
+2A15;INTEGRAL AROUND A POINT OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2A16;QUATERNION INTEGRAL OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2A17;INTEGRAL WITH LEFTWARDS ARROW WITH HOOK;Sm;0;ON;;;;;Y;;;;;
+2A18;INTEGRAL WITH TIMES SIGN;Sm;0;ON;;;;;Y;;;;;
+2A19;INTEGRAL WITH INTERSECTION;Sm;0;ON;;;;;Y;;;;;
+2A1A;INTEGRAL WITH UNION;Sm;0;ON;;;;;Y;;;;;
+2A1B;INTEGRAL WITH OVERBAR;Sm;0;ON;;;;;Y;;;;;
+2A1C;INTEGRAL WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;;
+2A1D;JOIN;Sm;0;ON;;;;;N;;;;;
+2A1E;LARGE LEFT TRIANGLE OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2A1F;Z NOTATION SCHEMA COMPOSITION;Sm;0;ON;;;;;Y;;;;;
+2A20;Z NOTATION SCHEMA PIPING;Sm;0;ON;;;;;Y;;;;;
+2A21;Z NOTATION SCHEMA PROJECTION;Sm;0;ON;;;;;Y;;;;;
+2A22;PLUS SIGN WITH SMALL CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;;
+2A23;PLUS SIGN WITH CIRCUMFLEX ACCENT ABOVE;Sm;0;ON;;;;;N;;;;;
+2A24;PLUS SIGN WITH TILDE ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A25;PLUS SIGN WITH DOT BELOW;Sm;0;ON;;;;;N;;;;;
+2A26;PLUS SIGN WITH TILDE BELOW;Sm;0;ON;;;;;Y;;;;;
+2A27;PLUS SIGN WITH SUBSCRIPT TWO;Sm;0;ON;;;;;N;;;;;
+2A28;PLUS SIGN WITH BLACK TRIANGLE;Sm;0;ON;;;;;N;;;;;
+2A29;MINUS SIGN WITH COMMA ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A2A;MINUS SIGN WITH DOT BELOW;Sm;0;ON;;;;;N;;;;;
+2A2B;MINUS SIGN WITH FALLING DOTS;Sm;0;ON;;;;;Y;;;;;
+2A2C;MINUS SIGN WITH RISING DOTS;Sm;0;ON;;;;;Y;;;;;
+2A2D;PLUS SIGN IN LEFT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;;
+2A2E;PLUS SIGN IN RIGHT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;;
+2A2F;VECTOR OR CROSS PRODUCT;Sm;0;ON;;;;;N;;;;;
+2A30;MULTIPLICATION SIGN WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;;
+2A31;MULTIPLICATION SIGN WITH UNDERBAR;Sm;0;ON;;;;;N;;;;;
+2A32;SEMIDIRECT PRODUCT WITH BOTTOM CLOSED;Sm;0;ON;;;;;N;;;;;
+2A33;SMASH PRODUCT;Sm;0;ON;;;;;N;;;;;
+2A34;MULTIPLICATION SIGN IN LEFT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;;
+2A35;MULTIPLICATION SIGN IN RIGHT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;;
+2A36;CIRCLED MULTIPLICATION SIGN WITH CIRCUMFLEX ACCENT;Sm;0;ON;;;;;N;;;;;
+2A37;MULTIPLICATION SIGN IN DOUBLE CIRCLE;Sm;0;ON;;;;;N;;;;;
+2A38;CIRCLED DIVISION SIGN;Sm;0;ON;;;;;N;;;;;
+2A39;PLUS SIGN IN TRIANGLE;Sm;0;ON;;;;;N;;;;;
+2A3A;MINUS SIGN IN TRIANGLE;Sm;0;ON;;;;;N;;;;;
+2A3B;MULTIPLICATION SIGN IN TRIANGLE;Sm;0;ON;;;;;N;;;;;
+2A3C;INTERIOR PRODUCT;Sm;0;ON;;;;;Y;;;;;
+2A3D;RIGHTHAND INTERIOR PRODUCT;Sm;0;ON;;;;;Y;;;;;
+2A3E;Z NOTATION RELATIONAL COMPOSITION;Sm;0;ON;;;;;Y;;;;;
+2A3F;AMALGAMATION OR COPRODUCT;Sm;0;ON;;;;;N;;;;;
+2A40;INTERSECTION WITH DOT;Sm;0;ON;;;;;N;;;;;
+2A41;UNION WITH MINUS SIGN;Sm;0;ON;;;;;N;;;;;
+2A42;UNION WITH OVERBAR;Sm;0;ON;;;;;N;;;;;
+2A43;INTERSECTION WITH OVERBAR;Sm;0;ON;;;;;N;;;;;
+2A44;INTERSECTION WITH LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+2A45;UNION WITH LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+2A46;UNION ABOVE INTERSECTION;Sm;0;ON;;;;;N;;;;;
+2A47;INTERSECTION ABOVE UNION;Sm;0;ON;;;;;N;;;;;
+2A48;UNION ABOVE BAR ABOVE INTERSECTION;Sm;0;ON;;;;;N;;;;;
+2A49;INTERSECTION ABOVE BAR ABOVE UNION;Sm;0;ON;;;;;N;;;;;
+2A4A;UNION BESIDE AND JOINED WITH UNION;Sm;0;ON;;;;;N;;;;;
+2A4B;INTERSECTION BESIDE AND JOINED WITH INTERSECTION;Sm;0;ON;;;;;N;;;;;
+2A4C;CLOSED UNION WITH SERIFS;Sm;0;ON;;;;;N;;;;;
+2A4D;CLOSED INTERSECTION WITH SERIFS;Sm;0;ON;;;;;N;;;;;
+2A4E;DOUBLE SQUARE INTERSECTION;Sm;0;ON;;;;;N;;;;;
+2A4F;DOUBLE SQUARE UNION;Sm;0;ON;;;;;N;;;;;
+2A50;CLOSED UNION WITH SERIFS AND SMASH PRODUCT;Sm;0;ON;;;;;N;;;;;
+2A51;LOGICAL AND WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;;
+2A52;LOGICAL OR WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;;
+2A53;DOUBLE LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+2A54;DOUBLE LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+2A55;TWO INTERSECTING LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+2A56;TWO INTERSECTING LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+2A57;SLOPING LARGE OR;Sm;0;ON;;;;;Y;;;;;
+2A58;SLOPING LARGE AND;Sm;0;ON;;;;;Y;;;;;
+2A59;LOGICAL OR OVERLAPPING LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+2A5A;LOGICAL AND WITH MIDDLE STEM;Sm;0;ON;;;;;N;;;;;
+2A5B;LOGICAL OR WITH MIDDLE STEM;Sm;0;ON;;;;;N;;;;;
+2A5C;LOGICAL AND WITH HORIZONTAL DASH;Sm;0;ON;;;;;N;;;;;
+2A5D;LOGICAL OR WITH HORIZONTAL DASH;Sm;0;ON;;;;;N;;;;;
+2A5E;LOGICAL AND WITH DOUBLE OVERBAR;Sm;0;ON;;;;;N;;;;;
+2A5F;LOGICAL AND WITH UNDERBAR;Sm;0;ON;;;;;N;;;;;
+2A60;LOGICAL AND WITH DOUBLE UNDERBAR;Sm;0;ON;;;;;N;;;;;
+2A61;SMALL VEE WITH UNDERBAR;Sm;0;ON;;;;;N;;;;;
+2A62;LOGICAL OR WITH DOUBLE OVERBAR;Sm;0;ON;;;;;N;;;;;
+2A63;LOGICAL OR WITH DOUBLE UNDERBAR;Sm;0;ON;;;;;N;;;;;
+2A64;Z NOTATION DOMAIN ANTIRESTRICTION;Sm;0;ON;;;;;Y;;;;;
+2A65;Z NOTATION RANGE ANTIRESTRICTION;Sm;0;ON;;;;;Y;;;;;
+2A66;EQUALS SIGN WITH DOT BELOW;Sm;0;ON;;;;;N;;;;;
+2A67;IDENTICAL WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;;
+2A68;TRIPLE HORIZONTAL BAR WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2A69;TRIPLE HORIZONTAL BAR WITH TRIPLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2A6A;TILDE OPERATOR WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A6B;TILDE OPERATOR WITH RISING DOTS;Sm;0;ON;;;;;Y;;;;;
+2A6C;SIMILAR MINUS SIMILAR;Sm;0;ON;;;;;Y;;;;;
+2A6D;CONGRUENT WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A6E;EQUALS WITH ASTERISK;Sm;0;ON;;;;;N;;;;;
+2A6F;ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT;Sm;0;ON;;;;;Y;;;;;
+2A70;APPROXIMATELY EQUAL OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2A71;EQUALS SIGN ABOVE PLUS SIGN;Sm;0;ON;;;;;N;;;;;
+2A72;PLUS SIGN ABOVE EQUALS SIGN;Sm;0;ON;;;;;N;;;;;
+2A73;EQUALS SIGN ABOVE TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2A74;DOUBLE COLON EQUAL;Sm;0;ON;<compat> 003A 003A 003D;;;;Y;;;;;
+2A75;TWO CONSECUTIVE EQUALS SIGNS;Sm;0;ON;<compat> 003D 003D;;;;N;;;;;
+2A76;THREE CONSECUTIVE EQUALS SIGNS;Sm;0;ON;<compat> 003D 003D 003D;;;;N;;;;;
+2A77;EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW;Sm;0;ON;;;;;N;;;;;
+2A78;EQUIVALENT WITH FOUR DOTS ABOVE;Sm;0;ON;;;;;N;;;;;
+2A79;LESS-THAN WITH CIRCLE INSIDE;Sm;0;ON;;;;;Y;;;;;
+2A7A;GREATER-THAN WITH CIRCLE INSIDE;Sm;0;ON;;;;;Y;;;;;
+2A7B;LESS-THAN WITH QUESTION MARK ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A7C;GREATER-THAN WITH QUESTION MARK ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A7D;LESS-THAN OR SLANTED EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2A7E;GREATER-THAN OR SLANTED EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2A7F;LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;;
+2A80;GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;;
+2A81;LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A82;GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A83;LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT;Sm;0;ON;;;;;Y;;;;;
+2A84;GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT;Sm;0;ON;;;;;Y;;;;;
+2A85;LESS-THAN OR APPROXIMATE;Sm;0;ON;;;;;Y;;;;;
+2A86;GREATER-THAN OR APPROXIMATE;Sm;0;ON;;;;;Y;;;;;
+2A87;LESS-THAN AND SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2A88;GREATER-THAN AND SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2A89;LESS-THAN AND NOT APPROXIMATE;Sm;0;ON;;;;;Y;;;;;
+2A8A;GREATER-THAN AND NOT APPROXIMATE;Sm;0;ON;;;;;Y;;;;;
+2A8B;LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2A8C;GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2A8D;LESS-THAN ABOVE SIMILAR OR EQUAL;Sm;0;ON;;;;;Y;;;;;
+2A8E;GREATER-THAN ABOVE SIMILAR OR EQUAL;Sm;0;ON;;;;;Y;;;;;
+2A8F;LESS-THAN ABOVE SIMILAR ABOVE GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2A90;GREATER-THAN ABOVE SIMILAR ABOVE LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2A91;LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL;Sm;0;ON;;;;;Y;;;;;
+2A92;GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL;Sm;0;ON;;;;;Y;;;;;
+2A93;LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;;
+2A94;GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;;
+2A95;SLANTED EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2A96;SLANTED EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2A97;SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;;
+2A98;SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;;
+2A99;DOUBLE-LINE EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2A9A;DOUBLE-LINE EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2A9B;DOUBLE-LINE SLANTED EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2A9C;DOUBLE-LINE SLANTED EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2A9D;SIMILAR OR LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2A9E;SIMILAR OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2A9F;SIMILAR ABOVE LESS-THAN ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AA0;SIMILAR ABOVE GREATER-THAN ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AA1;DOUBLE NESTED LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2AA2;DOUBLE NESTED GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2AA3;DOUBLE NESTED LESS-THAN WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;;
+2AA4;GREATER-THAN OVERLAPPING LESS-THAN;Sm;0;ON;;;;;N;;;;;
+2AA5;GREATER-THAN BESIDE LESS-THAN;Sm;0;ON;;;;;N;;;;;
+2AA6;LESS-THAN CLOSED BY CURVE;Sm;0;ON;;;;;Y;;;;;
+2AA7;GREATER-THAN CLOSED BY CURVE;Sm;0;ON;;;;;Y;;;;;
+2AA8;LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;;
+2AA9;GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;;
+2AAA;SMALLER THAN;Sm;0;ON;;;;;Y;;;;;
+2AAB;LARGER THAN;Sm;0;ON;;;;;Y;;;;;
+2AAC;SMALLER THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AAD;LARGER THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AAE;EQUALS SIGN WITH BUMPY ABOVE;Sm;0;ON;;;;;N;;;;;
+2AAF;PRECEDES ABOVE SINGLE-LINE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AB0;SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AB1;PRECEDES ABOVE SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AB2;SUCCEEDS ABOVE SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AB3;PRECEDES ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AB4;SUCCEEDS ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AB5;PRECEDES ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AB6;SUCCEEDS ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AB7;PRECEDES ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AB8;SUCCEEDS ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AB9;PRECEDES ABOVE NOT ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2ABA;SUCCEEDS ABOVE NOT ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2ABB;DOUBLE PRECEDES;Sm;0;ON;;;;;Y;;;;;
+2ABC;DOUBLE SUCCEEDS;Sm;0;ON;;;;;Y;;;;;
+2ABD;SUBSET WITH DOT;Sm;0;ON;;;;;Y;;;;;
+2ABE;SUPERSET WITH DOT;Sm;0;ON;;;;;Y;;;;;
+2ABF;SUBSET WITH PLUS SIGN BELOW;Sm;0;ON;;;;;Y;;;;;
+2AC0;SUPERSET WITH PLUS SIGN BELOW;Sm;0;ON;;;;;Y;;;;;
+2AC1;SUBSET WITH MULTIPLICATION SIGN BELOW;Sm;0;ON;;;;;Y;;;;;
+2AC2;SUPERSET WITH MULTIPLICATION SIGN BELOW;Sm;0;ON;;;;;Y;;;;;
+2AC3;SUBSET OF OR EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+2AC4;SUPERSET OF OR EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+2AC5;SUBSET OF ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AC6;SUPERSET OF ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AC7;SUBSET OF ABOVE TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2AC8;SUPERSET OF ABOVE TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2AC9;SUBSET OF ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2ACA;SUPERSET OF ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2ACB;SUBSET OF ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2ACC;SUPERSET OF ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2ACD;SQUARE LEFT OPEN BOX OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2ACE;SQUARE RIGHT OPEN BOX OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2ACF;CLOSED SUBSET;Sm;0;ON;;;;;Y;;;;;
+2AD0;CLOSED SUPERSET;Sm;0;ON;;;;;Y;;;;;
+2AD1;CLOSED SUBSET OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AD2;CLOSED SUPERSET OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AD3;SUBSET ABOVE SUPERSET;Sm;0;ON;;;;;Y;;;;;
+2AD4;SUPERSET ABOVE SUBSET;Sm;0;ON;;;;;Y;;;;;
+2AD5;SUBSET ABOVE SUBSET;Sm;0;ON;;;;;Y;;;;;
+2AD6;SUPERSET ABOVE SUPERSET;Sm;0;ON;;;;;Y;;;;;
+2AD7;SUPERSET BESIDE SUBSET;Sm;0;ON;;;;;N;;;;;
+2AD8;SUPERSET BESIDE AND JOINED BY DASH WITH SUBSET;Sm;0;ON;;;;;N;;;;;
+2AD9;ELEMENT OF OPENING DOWNWARDS;Sm;0;ON;;;;;N;;;;;
+2ADA;PITCHFORK WITH TEE TOP;Sm;0;ON;;;;;N;;;;;
+2ADB;TRANSVERSAL INTERSECTION;Sm;0;ON;;;;;N;;;;;
+2ADC;FORKING;Sm;0;ON;2ADD 0338;;;;Y;;;;;
+2ADD;NONFORKING;Sm;0;ON;;;;;N;;;;;
+2ADE;SHORT LEFT TACK;Sm;0;ON;;;;;Y;;;;;
+2ADF;SHORT DOWN TACK;Sm;0;ON;;;;;N;;;;;
+2AE0;SHORT UP TACK;Sm;0;ON;;;;;N;;;;;
+2AE1;PERPENDICULAR WITH S;Sm;0;ON;;;;;N;;;;;
+2AE2;VERTICAL BAR TRIPLE RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+2AE3;DOUBLE VERTICAL BAR LEFT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+2AE4;VERTICAL BAR DOUBLE LEFT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+2AE5;DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+2AE6;LONG DASH FROM LEFT MEMBER OF DOUBLE VERTICAL;Sm;0;ON;;;;;Y;;;;;
+2AE7;SHORT DOWN TACK WITH OVERBAR;Sm;0;ON;;;;;N;;;;;
+2AE8;SHORT UP TACK WITH UNDERBAR;Sm;0;ON;;;;;N;;;;;
+2AE9;SHORT UP TACK ABOVE SHORT DOWN TACK;Sm;0;ON;;;;;N;;;;;
+2AEA;DOUBLE DOWN TACK;Sm;0;ON;;;;;N;;;;;
+2AEB;DOUBLE UP TACK;Sm;0;ON;;;;;N;;;;;
+2AEC;DOUBLE STROKE NOT SIGN;Sm;0;ON;;;;;Y;;;;;
+2AED;REVERSED DOUBLE STROKE NOT SIGN;Sm;0;ON;;;;;Y;;;;;
+2AEE;DOES NOT DIVIDE WITH REVERSED NEGATION SLASH;Sm;0;ON;;;;;Y;;;;;
+2AEF;VERTICAL LINE WITH CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;;
+2AF0;VERTICAL LINE WITH CIRCLE BELOW;Sm;0;ON;;;;;N;;;;;
+2AF1;DOWN TACK WITH CIRCLE BELOW;Sm;0;ON;;;;;N;;;;;
+2AF2;PARALLEL WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;;
+2AF3;PARALLEL WITH TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2AF4;TRIPLE VERTICAL BAR BINARY RELATION;Sm;0;ON;;;;;N;;;;;
+2AF5;TRIPLE VERTICAL BAR WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;;
+2AF6;TRIPLE COLON OPERATOR;Sm;0;ON;;;;;N;;;;;
+2AF7;TRIPLE NESTED LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2AF8;TRIPLE NESTED GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2AF9;DOUBLE-LINE SLANTED LESS-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AFA;DOUBLE-LINE SLANTED GREATER-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AFB;TRIPLE SOLIDUS BINARY RELATION;Sm;0;ON;;;;;Y;;;;;
+2AFC;LARGE TRIPLE VERTICAL BAR OPERATOR;Sm;0;ON;;;;;N;;;;;
+2AFD;DOUBLE SOLIDUS OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2AFE;WHITE VERTICAL BAR;Sm;0;ON;;;;;N;;;;;
+2AFF;N-ARY WHITE VERTICAL BAR;Sm;0;ON;;;;;N;;;;;
+2B00;NORTH EAST WHITE ARROW;So;0;ON;;;;;N;;;;;
+2B01;NORTH WEST WHITE ARROW;So;0;ON;;;;;N;;;;;
+2B02;SOUTH EAST WHITE ARROW;So;0;ON;;;;;N;;;;;
+2B03;SOUTH WEST WHITE ARROW;So;0;ON;;;;;N;;;;;
+2B04;LEFT RIGHT WHITE ARROW;So;0;ON;;;;;N;;;;;
+2B05;LEFTWARDS BLACK ARROW;So;0;ON;;;;;N;;;;;
+2B06;UPWARDS BLACK ARROW;So;0;ON;;;;;N;;;;;
+2B07;DOWNWARDS BLACK ARROW;So;0;ON;;;;;N;;;;;
+2B08;NORTH EAST BLACK ARROW;So;0;ON;;;;;N;;;;;
+2B09;NORTH WEST BLACK ARROW;So;0;ON;;;;;N;;;;;
+2B0A;SOUTH EAST BLACK ARROW;So;0;ON;;;;;N;;;;;
+2B0B;SOUTH WEST BLACK ARROW;So;0;ON;;;;;N;;;;;
+2B0C;LEFT RIGHT BLACK ARROW;So;0;ON;;;;;N;;;;;
+2B0D;UP DOWN BLACK ARROW;So;0;ON;;;;;N;;;;;
+2B0E;RIGHTWARDS ARROW WITH TIP DOWNWARDS;So;0;ON;;;;;N;;;;;
+2B0F;RIGHTWARDS ARROW WITH TIP UPWARDS;So;0;ON;;;;;N;;;;;
+2B10;LEFTWARDS ARROW WITH TIP DOWNWARDS;So;0;ON;;;;;N;;;;;
+2B11;LEFTWARDS ARROW WITH TIP UPWARDS;So;0;ON;;;;;N;;;;;
+2B12;SQUARE WITH TOP HALF BLACK;So;0;ON;;;;;N;;;;;
+2B13;SQUARE WITH BOTTOM HALF BLACK;So;0;ON;;;;;N;;;;;
+2B14;SQUARE WITH UPPER RIGHT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;;
+2B15;SQUARE WITH LOWER LEFT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;;
+2B16;DIAMOND WITH LEFT HALF BLACK;So;0;ON;;;;;N;;;;;
+2B17;DIAMOND WITH RIGHT HALF BLACK;So;0;ON;;;;;N;;;;;
+2B18;DIAMOND WITH TOP HALF BLACK;So;0;ON;;;;;N;;;;;
+2B19;DIAMOND WITH BOTTOM HALF BLACK;So;0;ON;;;;;N;;;;;
+2B1A;DOTTED SQUARE;So;0;ON;;;;;N;;;;;
+2B1B;BLACK LARGE SQUARE;So;0;ON;;;;;N;;;;;
+2B1C;WHITE LARGE SQUARE;So;0;ON;;;;;N;;;;;
+2B1D;BLACK VERY SMALL SQUARE;So;0;ON;;;;;N;;;;;
+2B1E;WHITE VERY SMALL SQUARE;So;0;ON;;;;;N;;;;;
+2B1F;BLACK PENTAGON;So;0;ON;;;;;N;;;;;
+2B20;WHITE PENTAGON;So;0;ON;;;;;N;;;;;
+2B21;WHITE HEXAGON;So;0;ON;;;;;N;;;;;
+2B22;BLACK HEXAGON;So;0;ON;;;;;N;;;;;
+2B23;HORIZONTAL BLACK HEXAGON;So;0;ON;;;;;N;;;;;
+2B24;BLACK LARGE CIRCLE;So;0;ON;;;;;N;;;;;
+2B25;BLACK MEDIUM DIAMOND;So;0;ON;;;;;N;;;;;
+2B26;WHITE MEDIUM DIAMOND;So;0;ON;;;;;N;;;;;
+2B27;BLACK MEDIUM LOZENGE;So;0;ON;;;;;N;;;;;
+2B28;WHITE MEDIUM LOZENGE;So;0;ON;;;;;N;;;;;
+2B29;BLACK SMALL DIAMOND;So;0;ON;;;;;N;;;;;
+2B2A;BLACK SMALL LOZENGE;So;0;ON;;;;;N;;;;;
+2B2B;WHITE SMALL LOZENGE;So;0;ON;;;;;N;;;;;
+2B2C;BLACK HORIZONTAL ELLIPSE;So;0;ON;;;;;N;;;;;
+2B2D;WHITE HORIZONTAL ELLIPSE;So;0;ON;;;;;N;;;;;
+2B2E;BLACK VERTICAL ELLIPSE;So;0;ON;;;;;N;;;;;
+2B2F;WHITE VERTICAL ELLIPSE;So;0;ON;;;;;N;;;;;
+2B30;LEFT ARROW WITH SMALL CIRCLE;Sm;0;ON;;;;;N;;;;;
+2B31;THREE LEFTWARDS ARROWS;Sm;0;ON;;;;;N;;;;;
+2B32;LEFT ARROW WITH CIRCLED PLUS;Sm;0;ON;;;;;N;;;;;
+2B33;LONG LEFTWARDS SQUIGGLE ARROW;Sm;0;ON;;;;;N;;;;;
+2B34;LEFTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2B35;LEFTWARDS TWO-HEADED ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2B36;LEFTWARDS TWO-HEADED ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+2B37;LEFTWARDS TWO-HEADED TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;;
+2B38;LEFTWARDS ARROW WITH DOTTED STEM;Sm;0;ON;;;;;N;;;;;
+2B39;LEFTWARDS ARROW WITH TAIL WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2B3A;LEFTWARDS ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2B3B;LEFTWARDS TWO-HEADED ARROW WITH TAIL;Sm;0;ON;;;;;N;;;;;
+2B3C;LEFTWARDS TWO-HEADED ARROW WITH TAIL WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2B3D;LEFTWARDS TWO-HEADED ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2B3E;LEFTWARDS ARROW THROUGH X;Sm;0;ON;;;;;N;;;;;
+2B3F;WAVE ARROW POINTING DIRECTLY LEFT;Sm;0;ON;;;;;N;;;;;
+2B40;EQUALS SIGN ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2B41;REVERSE TILDE OPERATOR ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2B42;LEFTWARDS ARROW ABOVE REVERSE ALMOST EQUAL TO;Sm;0;ON;;;;;N;;;;;
+2B43;RIGHTWARDS ARROW THROUGH GREATER-THAN;Sm;0;ON;;;;;N;;;;;
+2B44;RIGHTWARDS ARROW THROUGH SUPERSET;Sm;0;ON;;;;;N;;;;;
+2B45;LEFTWARDS QUADRUPLE ARROW;So;0;ON;;;;;N;;;;;
+2B46;RIGHTWARDS QUADRUPLE ARROW;So;0;ON;;;;;N;;;;;
+2B47;REVERSE TILDE OPERATOR ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2B48;RIGHTWARDS ARROW ABOVE REVERSE ALMOST EQUAL TO;Sm;0;ON;;;;;N;;;;;
+2B49;TILDE OPERATOR ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2B4A;LEFTWARDS ARROW ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;N;;;;;
+2B4B;LEFTWARDS ARROW ABOVE REVERSE TILDE OPERATOR;Sm;0;ON;;;;;N;;;;;
+2B4C;RIGHTWARDS ARROW ABOVE REVERSE TILDE OPERATOR;Sm;0;ON;;;;;N;;;;;
+2B4D;DOWNWARDS TRIANGLE-HEADED ZIGZAG ARROW;So;0;ON;;;;;N;;;;;
+2B4E;SHORT SLANTED NORTH ARROW;So;0;ON;;;;;N;;;;;
+2B4F;SHORT BACKSLANTED SOUTH ARROW;So;0;ON;;;;;N;;;;;
+2B50;WHITE MEDIUM STAR;So;0;ON;;;;;N;;;;;
+2B51;BLACK SMALL STAR;So;0;ON;;;;;N;;;;;
+2B52;WHITE SMALL STAR;So;0;ON;;;;;N;;;;;
+2B53;BLACK RIGHT-POINTING PENTAGON;So;0;ON;;;;;N;;;;;
+2B54;WHITE RIGHT-POINTING PENTAGON;So;0;ON;;;;;N;;;;;
+2B55;HEAVY LARGE CIRCLE;So;0;ON;;;;;N;;;;;
+2B56;HEAVY OVAL WITH OVAL INSIDE;So;0;ON;;;;;N;;;;;
+2B57;HEAVY CIRCLE WITH CIRCLE INSIDE;So;0;ON;;;;;N;;;;;
+2B58;HEAVY CIRCLE;So;0;ON;;;;;N;;;;;
+2B59;HEAVY CIRCLED SALTIRE;So;0;ON;;;;;N;;;;;
+2B5A;SLANTED NORTH ARROW WITH HOOKED HEAD;So;0;ON;;;;;N;;;;;
+2B5B;BACKSLANTED SOUTH ARROW WITH HOOKED TAIL;So;0;ON;;;;;N;;;;;
+2B5C;SLANTED NORTH ARROW WITH HORIZONTAL TAIL;So;0;ON;;;;;N;;;;;
+2B5D;BACKSLANTED SOUTH ARROW WITH HORIZONTAL TAIL;So;0;ON;;;;;N;;;;;
+2B5E;BENT ARROW POINTING DOWNWARDS THEN NORTH EAST;So;0;ON;;;;;N;;;;;
+2B5F;SHORT BENT ARROW POINTING DOWNWARDS THEN NORTH EAST;So;0;ON;;;;;N;;;;;
+2B60;LEFTWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;;
+2B61;UPWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;;
+2B62;RIGHTWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;;
+2B63;DOWNWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;;
+2B64;LEFT RIGHT TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;;
+2B65;UP DOWN TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;;
+2B66;NORTH WEST TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;;
+2B67;NORTH EAST TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;;
+2B68;SOUTH EAST TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;;
+2B69;SOUTH WEST TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;;
+2B6A;LEFTWARDS TRIANGLE-HEADED DASHED ARROW;So;0;ON;;;;;N;;;;;
+2B6B;UPWARDS TRIANGLE-HEADED DASHED ARROW;So;0;ON;;;;;N;;;;;
+2B6C;RIGHTWARDS TRIANGLE-HEADED DASHED ARROW;So;0;ON;;;;;N;;;;;
+2B6D;DOWNWARDS TRIANGLE-HEADED DASHED ARROW;So;0;ON;;;;;N;;;;;
+2B6E;CLOCKWISE TRIANGLE-HEADED OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;;
+2B6F;ANTICLOCKWISE TRIANGLE-HEADED OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;;
+2B70;LEFTWARDS TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;;
+2B71;UPWARDS TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;;
+2B72;RIGHTWARDS TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;;
+2B73;DOWNWARDS TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;;
+2B76;NORTH WEST TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;;
+2B77;NORTH EAST TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;;
+2B78;SOUTH EAST TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;;
+2B79;SOUTH WEST TRIANGLE-HEADED ARROW TO BAR;So;0;ON;;;;;N;;;;;
+2B7A;LEFTWARDS TRIANGLE-HEADED ARROW WITH DOUBLE HORIZONTAL STROKE;So;0;ON;;;;;N;;;;;
+2B7B;UPWARDS TRIANGLE-HEADED ARROW WITH DOUBLE HORIZONTAL STROKE;So;0;ON;;;;;N;;;;;
+2B7C;RIGHTWARDS TRIANGLE-HEADED ARROW WITH DOUBLE HORIZONTAL STROKE;So;0;ON;;;;;N;;;;;
+2B7D;DOWNWARDS TRIANGLE-HEADED ARROW WITH DOUBLE HORIZONTAL STROKE;So;0;ON;;;;;N;;;;;
+2B7E;HORIZONTAL TAB KEY;So;0;ON;;;;;N;;;;;
+2B7F;VERTICAL TAB KEY;So;0;ON;;;;;N;;;;;
+2B80;LEFTWARDS TRIANGLE-HEADED ARROW OVER RIGHTWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;;
+2B81;UPWARDS TRIANGLE-HEADED ARROW LEFTWARDS OF DOWNWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;;
+2B82;RIGHTWARDS TRIANGLE-HEADED ARROW OVER LEFTWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;;
+2B83;DOWNWARDS TRIANGLE-HEADED ARROW LEFTWARDS OF UPWARDS TRIANGLE-HEADED ARROW;So;0;ON;;;;;N;;;;;
+2B84;LEFTWARDS TRIANGLE-HEADED PAIRED ARROWS;So;0;ON;;;;;N;;;;;
+2B85;UPWARDS TRIANGLE-HEADED PAIRED ARROWS;So;0;ON;;;;;N;;;;;
+2B86;RIGHTWARDS TRIANGLE-HEADED PAIRED ARROWS;So;0;ON;;;;;N;;;;;
+2B87;DOWNWARDS TRIANGLE-HEADED PAIRED ARROWS;So;0;ON;;;;;N;;;;;
+2B88;LEFTWARDS BLACK CIRCLED WHITE ARROW;So;0;ON;;;;;N;;;;;
+2B89;UPWARDS BLACK CIRCLED WHITE ARROW;So;0;ON;;;;;N;;;;;
+2B8A;RIGHTWARDS BLACK CIRCLED WHITE ARROW;So;0;ON;;;;;N;;;;;
+2B8B;DOWNWARDS BLACK CIRCLED WHITE ARROW;So;0;ON;;;;;N;;;;;
+2B8C;ANTICLOCKWISE TRIANGLE-HEADED RIGHT U-SHAPED ARROW;So;0;ON;;;;;N;;;;;
+2B8D;ANTICLOCKWISE TRIANGLE-HEADED BOTTOM U-SHAPED ARROW;So;0;ON;;;;;N;;;;;
+2B8E;ANTICLOCKWISE TRIANGLE-HEADED LEFT U-SHAPED ARROW;So;0;ON;;;;;N;;;;;
+2B8F;ANTICLOCKWISE TRIANGLE-HEADED TOP U-SHAPED ARROW;So;0;ON;;;;;N;;;;;
+2B90;RETURN LEFT;So;0;ON;;;;;N;;;;;
+2B91;RETURN RIGHT;So;0;ON;;;;;N;;;;;
+2B92;NEWLINE LEFT;So;0;ON;;;;;N;;;;;
+2B93;NEWLINE RIGHT;So;0;ON;;;;;N;;;;;
+2B94;FOUR CORNER ARROWS CIRCLING ANTICLOCKWISE;So;0;ON;;;;;N;;;;;
+2B95;RIGHTWARDS BLACK ARROW;So;0;ON;;;;;N;;;;;
+2B98;THREE-D TOP-LIGHTED LEFTWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+2B99;THREE-D RIGHT-LIGHTED UPWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+2B9A;THREE-D TOP-LIGHTED RIGHTWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+2B9B;THREE-D LEFT-LIGHTED DOWNWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+2B9C;BLACK LEFTWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+2B9D;BLACK UPWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+2B9E;BLACK RIGHTWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+2B9F;BLACK DOWNWARDS EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+2BA0;DOWNWARDS TRIANGLE-HEADED ARROW WITH LONG TIP LEFTWARDS;So;0;ON;;;;;N;;;;;
+2BA1;DOWNWARDS TRIANGLE-HEADED ARROW WITH LONG TIP RIGHTWARDS;So;0;ON;;;;;N;;;;;
+2BA2;UPWARDS TRIANGLE-HEADED ARROW WITH LONG TIP LEFTWARDS;So;0;ON;;;;;N;;;;;
+2BA3;UPWARDS TRIANGLE-HEADED ARROW WITH LONG TIP RIGHTWARDS;So;0;ON;;;;;N;;;;;
+2BA4;LEFTWARDS TRIANGLE-HEADED ARROW WITH LONG TIP UPWARDS;So;0;ON;;;;;N;;;;;
+2BA5;RIGHTWARDS TRIANGLE-HEADED ARROW WITH LONG TIP UPWARDS;So;0;ON;;;;;N;;;;;
+2BA6;LEFTWARDS TRIANGLE-HEADED ARROW WITH LONG TIP DOWNWARDS;So;0;ON;;;;;N;;;;;
+2BA7;RIGHTWARDS TRIANGLE-HEADED ARROW WITH LONG TIP DOWNWARDS;So;0;ON;;;;;N;;;;;
+2BA8;BLACK CURVED DOWNWARDS AND LEFTWARDS ARROW;So;0;ON;;;;;N;;;;;
+2BA9;BLACK CURVED DOWNWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;;;;;
+2BAA;BLACK CURVED UPWARDS AND LEFTWARDS ARROW;So;0;ON;;;;;N;;;;;
+2BAB;BLACK CURVED UPWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;;;;;
+2BAC;BLACK CURVED LEFTWARDS AND UPWARDS ARROW;So;0;ON;;;;;N;;;;;
+2BAD;BLACK CURVED RIGHTWARDS AND UPWARDS ARROW;So;0;ON;;;;;N;;;;;
+2BAE;BLACK CURVED LEFTWARDS AND DOWNWARDS ARROW;So;0;ON;;;;;N;;;;;
+2BAF;BLACK CURVED RIGHTWARDS AND DOWNWARDS ARROW;So;0;ON;;;;;N;;;;;
+2BB0;RIBBON ARROW DOWN LEFT;So;0;ON;;;;;N;;;;;
+2BB1;RIBBON ARROW DOWN RIGHT;So;0;ON;;;;;N;;;;;
+2BB2;RIBBON ARROW UP LEFT;So;0;ON;;;;;N;;;;;
+2BB3;RIBBON ARROW UP RIGHT;So;0;ON;;;;;N;;;;;
+2BB4;RIBBON ARROW LEFT UP;So;0;ON;;;;;N;;;;;
+2BB5;RIBBON ARROW RIGHT UP;So;0;ON;;;;;N;;;;;
+2BB6;RIBBON ARROW LEFT DOWN;So;0;ON;;;;;N;;;;;
+2BB7;RIBBON ARROW RIGHT DOWN;So;0;ON;;;;;N;;;;;
+2BB8;UPWARDS WHITE ARROW FROM BAR WITH HORIZONTAL BAR;So;0;ON;;;;;N;;;;;
+2BB9;UP ARROWHEAD IN A RECTANGLE BOX;So;0;ON;;;;;N;;;;;
+2BBA;OVERLAPPING WHITE SQUARES;So;0;ON;;;;;N;;;;;
+2BBB;OVERLAPPING WHITE AND BLACK SQUARES;So;0;ON;;;;;N;;;;;
+2BBC;OVERLAPPING BLACK SQUARES;So;0;ON;;;;;N;;;;;
+2BBD;BALLOT BOX WITH LIGHT X;So;0;ON;;;;;N;;;;;
+2BBE;CIRCLED X;So;0;ON;;;;;N;;;;;
+2BBF;CIRCLED BOLD X;So;0;ON;;;;;N;;;;;
+2BC0;BLACK SQUARE CENTRED;So;0;ON;;;;;N;;;;;
+2BC1;BLACK DIAMOND CENTRED;So;0;ON;;;;;N;;;;;
+2BC2;TURNED BLACK PENTAGON;So;0;ON;;;;;N;;;;;
+2BC3;HORIZONTAL BLACK OCTAGON;So;0;ON;;;;;N;;;;;
+2BC4;BLACK OCTAGON;So;0;ON;;;;;N;;;;;
+2BC5;BLACK MEDIUM UP-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;;
+2BC6;BLACK MEDIUM DOWN-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;;
+2BC7;BLACK MEDIUM LEFT-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;;
+2BC8;BLACK MEDIUM RIGHT-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;;
+2BC9;NEPTUNE FORM TWO;So;0;ON;;;;;N;;;;;
+2BCA;TOP HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;;
+2BCB;BOTTOM HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;;
+2BCC;LIGHT FOUR POINTED BLACK CUSP;So;0;ON;;;;;N;;;;;
+2BCD;ROTATED LIGHT FOUR POINTED BLACK CUSP;So;0;ON;;;;;N;;;;;
+2BCE;WHITE FOUR POINTED CUSP;So;0;ON;;;;;N;;;;;
+2BCF;ROTATED WHITE FOUR POINTED CUSP;So;0;ON;;;;;N;;;;;
+2BD0;SQUARE POSITION INDICATOR;So;0;ON;;;;;N;;;;;
+2BD1;UNCERTAINTY SIGN;So;0;ON;;;;;N;;;;;
+2BD2;GROUP MARK;So;0;ON;;;;;N;;;;;
+2BD3;PLUTO FORM TWO;So;0;ON;;;;;N;;;;;
+2BD4;PLUTO FORM THREE;So;0;ON;;;;;N;;;;;
+2BD5;PLUTO FORM FOUR;So;0;ON;;;;;N;;;;;
+2BD6;PLUTO FORM FIVE;So;0;ON;;;;;N;;;;;
+2BD7;TRANSPLUTO;So;0;ON;;;;;N;;;;;
+2BD8;PROSERPINA;So;0;ON;;;;;N;;;;;
+2BD9;ASTRAEA;So;0;ON;;;;;N;;;;;
+2BDA;HYGIEA;So;0;ON;;;;;N;;;;;
+2BDB;PHOLUS;So;0;ON;;;;;N;;;;;
+2BDC;NESSUS;So;0;ON;;;;;N;;;;;
+2BDD;WHITE MOON SELENA;So;0;ON;;;;;N;;;;;
+2BDE;BLACK DIAMOND ON CROSS;So;0;ON;;;;;N;;;;;
+2BDF;TRUE LIGHT MOON ARTA;So;0;ON;;;;;N;;;;;
+2BE0;CUPIDO;So;0;ON;;;;;N;;;;;
+2BE1;HADES;So;0;ON;;;;;N;;;;;
+2BE2;ZEUS;So;0;ON;;;;;N;;;;;
+2BE3;KRONOS;So;0;ON;;;;;N;;;;;
+2BE4;APOLLON;So;0;ON;;;;;N;;;;;
+2BE5;ADMETOS;So;0;ON;;;;;N;;;;;
+2BE6;VULCANUS;So;0;ON;;;;;N;;;;;
+2BE7;POSEIDON;So;0;ON;;;;;N;;;;;
+2BE8;LEFT HALF BLACK STAR;So;0;ON;;;;;N;;;;;
+2BE9;RIGHT HALF BLACK STAR;So;0;ON;;;;;N;;;;;
+2BEA;STAR WITH LEFT HALF BLACK;So;0;ON;;;;;N;;;;;
+2BEB;STAR WITH RIGHT HALF BLACK;So;0;ON;;;;;N;;;;;
+2BEC;LEFTWARDS TWO-HEADED ARROW WITH TRIANGLE ARROWHEADS;So;0;ON;;;;;N;;;;;
+2BED;UPWARDS TWO-HEADED ARROW WITH TRIANGLE ARROWHEADS;So;0;ON;;;;;N;;;;;
+2BEE;RIGHTWARDS TWO-HEADED ARROW WITH TRIANGLE ARROWHEADS;So;0;ON;;;;;N;;;;;
+2BEF;DOWNWARDS TWO-HEADED ARROW WITH TRIANGLE ARROWHEADS;So;0;ON;;;;;N;;;;;
+2BF0;ERIS FORM ONE;So;0;ON;;;;;N;;;;;
+2BF1;ERIS FORM TWO;So;0;ON;;;;;N;;;;;
+2BF2;SEDNA;So;0;ON;;;;;N;;;;;
+2BF3;RUSSIAN ASTROLOGICAL SYMBOL VIGINTILE;So;0;ON;;;;;N;;;;;
+2BF4;RUSSIAN ASTROLOGICAL SYMBOL NOVILE;So;0;ON;;;;;N;;;;;
+2BF5;RUSSIAN ASTROLOGICAL SYMBOL QUINTILE;So;0;ON;;;;;N;;;;;
+2BF6;RUSSIAN ASTROLOGICAL SYMBOL BINOVILE;So;0;ON;;;;;N;;;;;
+2BF7;RUSSIAN ASTROLOGICAL SYMBOL SENTAGON;So;0;ON;;;;;N;;;;;
+2BF8;RUSSIAN ASTROLOGICAL SYMBOL TREDECILE;So;0;ON;;;;;N;;;;;
+2BF9;EQUALS SIGN WITH INFINITY BELOW;So;0;ON;;;;;N;;;;;
+2BFA;UNITED SYMBOL;So;0;ON;;;;;N;;;;;
+2BFB;SEPARATED SYMBOL;So;0;ON;;;;;N;;;;;
+2BFC;DOUBLED SYMBOL;So;0;ON;;;;;N;;;;;
+2BFD;PASSED SYMBOL;So;0;ON;;;;;N;;;;;
+2BFE;REVERSED RIGHT ANGLE;So;0;ON;;;;;Y;;;;;
+2BFF;HELLSCHREIBER PAUSE SYMBOL;So;0;ON;;;;;N;;;;;
+2C00;GLAGOLITIC CAPITAL LETTER AZU;Lu;0;L;;;;;N;;;;2C30;
+2C01;GLAGOLITIC CAPITAL LETTER BUKY;Lu;0;L;;;;;N;;;;2C31;
+2C02;GLAGOLITIC CAPITAL LETTER VEDE;Lu;0;L;;;;;N;;;;2C32;
+2C03;GLAGOLITIC CAPITAL LETTER GLAGOLI;Lu;0;L;;;;;N;;;;2C33;
+2C04;GLAGOLITIC CAPITAL LETTER DOBRO;Lu;0;L;;;;;N;;;;2C34;
+2C05;GLAGOLITIC CAPITAL LETTER YESTU;Lu;0;L;;;;;N;;;;2C35;
+2C06;GLAGOLITIC CAPITAL LETTER ZHIVETE;Lu;0;L;;;;;N;;;;2C36;
+2C07;GLAGOLITIC CAPITAL LETTER DZELO;Lu;0;L;;;;;N;;;;2C37;
+2C08;GLAGOLITIC CAPITAL LETTER ZEMLJA;Lu;0;L;;;;;N;;;;2C38;
+2C09;GLAGOLITIC CAPITAL LETTER IZHE;Lu;0;L;;;;;N;;;;2C39;
+2C0A;GLAGOLITIC CAPITAL LETTER INITIAL IZHE;Lu;0;L;;;;;N;;;;2C3A;
+2C0B;GLAGOLITIC CAPITAL LETTER I;Lu;0;L;;;;;N;;;;2C3B;
+2C0C;GLAGOLITIC CAPITAL LETTER DJERVI;Lu;0;L;;;;;N;;;;2C3C;
+2C0D;GLAGOLITIC CAPITAL LETTER KAKO;Lu;0;L;;;;;N;;;;2C3D;
+2C0E;GLAGOLITIC CAPITAL LETTER LJUDIJE;Lu;0;L;;;;;N;;;;2C3E;
+2C0F;GLAGOLITIC CAPITAL LETTER MYSLITE;Lu;0;L;;;;;N;;;;2C3F;
+2C10;GLAGOLITIC CAPITAL LETTER NASHI;Lu;0;L;;;;;N;;;;2C40;
+2C11;GLAGOLITIC CAPITAL LETTER ONU;Lu;0;L;;;;;N;;;;2C41;
+2C12;GLAGOLITIC CAPITAL LETTER POKOJI;Lu;0;L;;;;;N;;;;2C42;
+2C13;GLAGOLITIC CAPITAL LETTER RITSI;Lu;0;L;;;;;N;;;;2C43;
+2C14;GLAGOLITIC CAPITAL LETTER SLOVO;Lu;0;L;;;;;N;;;;2C44;
+2C15;GLAGOLITIC CAPITAL LETTER TVRIDO;Lu;0;L;;;;;N;;;;2C45;
+2C16;GLAGOLITIC CAPITAL LETTER UKU;Lu;0;L;;;;;N;;;;2C46;
+2C17;GLAGOLITIC CAPITAL LETTER FRITU;Lu;0;L;;;;;N;;;;2C47;
+2C18;GLAGOLITIC CAPITAL LETTER HERU;Lu;0;L;;;;;N;;;;2C48;
+2C19;GLAGOLITIC CAPITAL LETTER OTU;Lu;0;L;;;;;N;;;;2C49;
+2C1A;GLAGOLITIC CAPITAL LETTER PE;Lu;0;L;;;;;N;;;;2C4A;
+2C1B;GLAGOLITIC CAPITAL LETTER SHTA;Lu;0;L;;;;;N;;;;2C4B;
+2C1C;GLAGOLITIC CAPITAL LETTER TSI;Lu;0;L;;;;;N;;;;2C4C;
+2C1D;GLAGOLITIC CAPITAL LETTER CHRIVI;Lu;0;L;;;;;N;;;;2C4D;
+2C1E;GLAGOLITIC CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;2C4E;
+2C1F;GLAGOLITIC CAPITAL LETTER YERU;Lu;0;L;;;;;N;;;;2C4F;
+2C20;GLAGOLITIC CAPITAL LETTER YERI;Lu;0;L;;;;;N;;;;2C50;
+2C21;GLAGOLITIC CAPITAL LETTER YATI;Lu;0;L;;;;;N;;;;2C51;
+2C22;GLAGOLITIC CAPITAL LETTER SPIDERY HA;Lu;0;L;;;;;N;;;;2C52;
+2C23;GLAGOLITIC CAPITAL LETTER YU;Lu;0;L;;;;;N;;;;2C53;
+2C24;GLAGOLITIC CAPITAL LETTER SMALL YUS;Lu;0;L;;;;;N;;;;2C54;
+2C25;GLAGOLITIC CAPITAL LETTER SMALL YUS WITH TAIL;Lu;0;L;;;;;N;;;;2C55;
+2C26;GLAGOLITIC CAPITAL LETTER YO;Lu;0;L;;;;;N;;;;2C56;
+2C27;GLAGOLITIC CAPITAL LETTER IOTATED SMALL YUS;Lu;0;L;;;;;N;;;;2C57;
+2C28;GLAGOLITIC CAPITAL LETTER BIG YUS;Lu;0;L;;;;;N;;;;2C58;
+2C29;GLAGOLITIC CAPITAL LETTER IOTATED BIG YUS;Lu;0;L;;;;;N;;;;2C59;
+2C2A;GLAGOLITIC CAPITAL LETTER FITA;Lu;0;L;;;;;N;;;;2C5A;
+2C2B;GLAGOLITIC CAPITAL LETTER IZHITSA;Lu;0;L;;;;;N;;;;2C5B;
+2C2C;GLAGOLITIC CAPITAL LETTER SHTAPIC;Lu;0;L;;;;;N;;;;2C5C;
+2C2D;GLAGOLITIC CAPITAL LETTER TROKUTASTI A;Lu;0;L;;;;;N;;;;2C5D;
+2C2E;GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE;Lu;0;L;;;;;N;;;;2C5E;
+2C30;GLAGOLITIC SMALL LETTER AZU;Ll;0;L;;;;;N;;;2C00;;2C00
+2C31;GLAGOLITIC SMALL LETTER BUKY;Ll;0;L;;;;;N;;;2C01;;2C01
+2C32;GLAGOLITIC SMALL LETTER VEDE;Ll;0;L;;;;;N;;;2C02;;2C02
+2C33;GLAGOLITIC SMALL LETTER GLAGOLI;Ll;0;L;;;;;N;;;2C03;;2C03
+2C34;GLAGOLITIC SMALL LETTER DOBRO;Ll;0;L;;;;;N;;;2C04;;2C04
+2C35;GLAGOLITIC SMALL LETTER YESTU;Ll;0;L;;;;;N;;;2C05;;2C05
+2C36;GLAGOLITIC SMALL LETTER ZHIVETE;Ll;0;L;;;;;N;;;2C06;;2C06
+2C37;GLAGOLITIC SMALL LETTER DZELO;Ll;0;L;;;;;N;;;2C07;;2C07
+2C38;GLAGOLITIC SMALL LETTER ZEMLJA;Ll;0;L;;;;;N;;;2C08;;2C08
+2C39;GLAGOLITIC SMALL LETTER IZHE;Ll;0;L;;;;;N;;;2C09;;2C09
+2C3A;GLAGOLITIC SMALL LETTER INITIAL IZHE;Ll;0;L;;;;;N;;;2C0A;;2C0A
+2C3B;GLAGOLITIC SMALL LETTER I;Ll;0;L;;;;;N;;;2C0B;;2C0B
+2C3C;GLAGOLITIC SMALL LETTER DJERVI;Ll;0;L;;;;;N;;;2C0C;;2C0C
+2C3D;GLAGOLITIC SMALL LETTER KAKO;Ll;0;L;;;;;N;;;2C0D;;2C0D
+2C3E;GLAGOLITIC SMALL LETTER LJUDIJE;Ll;0;L;;;;;N;;;2C0E;;2C0E
+2C3F;GLAGOLITIC SMALL LETTER MYSLITE;Ll;0;L;;;;;N;;;2C0F;;2C0F
+2C40;GLAGOLITIC SMALL LETTER NASHI;Ll;0;L;;;;;N;;;2C10;;2C10
+2C41;GLAGOLITIC SMALL LETTER ONU;Ll;0;L;;;;;N;;;2C11;;2C11
+2C42;GLAGOLITIC SMALL LETTER POKOJI;Ll;0;L;;;;;N;;;2C12;;2C12
+2C43;GLAGOLITIC SMALL LETTER RITSI;Ll;0;L;;;;;N;;;2C13;;2C13
+2C44;GLAGOLITIC SMALL LETTER SLOVO;Ll;0;L;;;;;N;;;2C14;;2C14
+2C45;GLAGOLITIC SMALL LETTER TVRIDO;Ll;0;L;;;;;N;;;2C15;;2C15
+2C46;GLAGOLITIC SMALL LETTER UKU;Ll;0;L;;;;;N;;;2C16;;2C16
+2C47;GLAGOLITIC SMALL LETTER FRITU;Ll;0;L;;;;;N;;;2C17;;2C17
+2C48;GLAGOLITIC SMALL LETTER HERU;Ll;0;L;;;;;N;;;2C18;;2C18
+2C49;GLAGOLITIC SMALL LETTER OTU;Ll;0;L;;;;;N;;;2C19;;2C19
+2C4A;GLAGOLITIC SMALL LETTER PE;Ll;0;L;;;;;N;;;2C1A;;2C1A
+2C4B;GLAGOLITIC SMALL LETTER SHTA;Ll;0;L;;;;;N;;;2C1B;;2C1B
+2C4C;GLAGOLITIC SMALL LETTER TSI;Ll;0;L;;;;;N;;;2C1C;;2C1C
+2C4D;GLAGOLITIC SMALL LETTER CHRIVI;Ll;0;L;;;;;N;;;2C1D;;2C1D
+2C4E;GLAGOLITIC SMALL LETTER SHA;Ll;0;L;;;;;N;;;2C1E;;2C1E
+2C4F;GLAGOLITIC SMALL LETTER YERU;Ll;0;L;;;;;N;;;2C1F;;2C1F
+2C50;GLAGOLITIC SMALL LETTER YERI;Ll;0;L;;;;;N;;;2C20;;2C20
+2C51;GLAGOLITIC SMALL LETTER YATI;Ll;0;L;;;;;N;;;2C21;;2C21
+2C52;GLAGOLITIC SMALL LETTER SPIDERY HA;Ll;0;L;;;;;N;;;2C22;;2C22
+2C53;GLAGOLITIC SMALL LETTER YU;Ll;0;L;;;;;N;;;2C23;;2C23
+2C54;GLAGOLITIC SMALL LETTER SMALL YUS;Ll;0;L;;;;;N;;;2C24;;2C24
+2C55;GLAGOLITIC SMALL LETTER SMALL YUS WITH TAIL;Ll;0;L;;;;;N;;;2C25;;2C25
+2C56;GLAGOLITIC SMALL LETTER YO;Ll;0;L;;;;;N;;;2C26;;2C26
+2C57;GLAGOLITIC SMALL LETTER IOTATED SMALL YUS;Ll;0;L;;;;;N;;;2C27;;2C27
+2C58;GLAGOLITIC SMALL LETTER BIG YUS;Ll;0;L;;;;;N;;;2C28;;2C28
+2C59;GLAGOLITIC SMALL LETTER IOTATED BIG YUS;Ll;0;L;;;;;N;;;2C29;;2C29
+2C5A;GLAGOLITIC SMALL LETTER FITA;Ll;0;L;;;;;N;;;2C2A;;2C2A
+2C5B;GLAGOLITIC SMALL LETTER IZHITSA;Ll;0;L;;;;;N;;;2C2B;;2C2B
+2C5C;GLAGOLITIC SMALL LETTER SHTAPIC;Ll;0;L;;;;;N;;;2C2C;;2C2C
+2C5D;GLAGOLITIC SMALL LETTER TROKUTASTI A;Ll;0;L;;;;;N;;;2C2D;;2C2D
+2C5E;GLAGOLITIC SMALL LETTER LATINATE MYSLITE;Ll;0;L;;;;;N;;;2C2E;;2C2E
+2C60;LATIN CAPITAL LETTER L WITH DOUBLE BAR;Lu;0;L;;;;;N;;;;2C61;
+2C61;LATIN SMALL LETTER L WITH DOUBLE BAR;Ll;0;L;;;;;N;;;2C60;;2C60
+2C62;LATIN CAPITAL LETTER L WITH MIDDLE TILDE;Lu;0;L;;;;;N;;;;026B;
+2C63;LATIN CAPITAL LETTER P WITH STROKE;Lu;0;L;;;;;N;;;;1D7D;
+2C64;LATIN CAPITAL LETTER R WITH TAIL;Lu;0;L;;;;;N;;;;027D;
+2C65;LATIN SMALL LETTER A WITH STROKE;Ll;0;L;;;;;N;;;023A;;023A
+2C66;LATIN SMALL LETTER T WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;023E;;023E
+2C67;LATIN CAPITAL LETTER H WITH DESCENDER;Lu;0;L;;;;;N;;;;2C68;
+2C68;LATIN SMALL LETTER H WITH DESCENDER;Ll;0;L;;;;;N;;;2C67;;2C67
+2C69;LATIN CAPITAL LETTER K WITH DESCENDER;Lu;0;L;;;;;N;;;;2C6A;
+2C6A;LATIN SMALL LETTER K WITH DESCENDER;Ll;0;L;;;;;N;;;2C69;;2C69
+2C6B;LATIN CAPITAL LETTER Z WITH DESCENDER;Lu;0;L;;;;;N;;;;2C6C;
+2C6C;LATIN SMALL LETTER Z WITH DESCENDER;Ll;0;L;;;;;N;;;2C6B;;2C6B
+2C6D;LATIN CAPITAL LETTER ALPHA;Lu;0;L;;;;;N;;;;0251;
+2C6E;LATIN CAPITAL LETTER M WITH HOOK;Lu;0;L;;;;;N;;;;0271;
+2C6F;LATIN CAPITAL LETTER TURNED A;Lu;0;L;;;;;N;;;;0250;
+2C70;LATIN CAPITAL LETTER TURNED ALPHA;Lu;0;L;;;;;N;;;;0252;
+2C71;LATIN SMALL LETTER V WITH RIGHT HOOK;Ll;0;L;;;;;N;;;;;
+2C72;LATIN CAPITAL LETTER W WITH HOOK;Lu;0;L;;;;;N;;;;2C73;
+2C73;LATIN SMALL LETTER W WITH HOOK;Ll;0;L;;;;;N;;;2C72;;2C72
+2C74;LATIN SMALL LETTER V WITH CURL;Ll;0;L;;;;;N;;;;;
+2C75;LATIN CAPITAL LETTER HALF H;Lu;0;L;;;;;N;;;;2C76;
+2C76;LATIN SMALL LETTER HALF H;Ll;0;L;;;;;N;;;2C75;;2C75
+2C77;LATIN SMALL LETTER TAILLESS PHI;Ll;0;L;;;;;N;;;;;
+2C78;LATIN SMALL LETTER E WITH NOTCH;Ll;0;L;;;;;N;;;;;
+2C79;LATIN SMALL LETTER TURNED R WITH TAIL;Ll;0;L;;;;;N;;;;;
+2C7A;LATIN SMALL LETTER O WITH LOW RING INSIDE;Ll;0;L;;;;;N;;;;;
+2C7B;LATIN LETTER SMALL CAPITAL TURNED E;Ll;0;L;;;;;N;;;;;
+2C7C;LATIN SUBSCRIPT SMALL LETTER J;Lm;0;L;<sub> 006A;;;;N;;;;;
+2C7D;MODIFIER LETTER CAPITAL V;Lm;0;L;<super> 0056;;;;N;;;;;
+2C7E;LATIN CAPITAL LETTER S WITH SWASH TAIL;Lu;0;L;;;;;N;;;;023F;
+2C7F;LATIN CAPITAL LETTER Z WITH SWASH TAIL;Lu;0;L;;;;;N;;;;0240;
+2C80;COPTIC CAPITAL LETTER ALFA;Lu;0;L;;;;;N;;;;2C81;
+2C81;COPTIC SMALL LETTER ALFA;Ll;0;L;;;;;N;;;2C80;;2C80
+2C82;COPTIC CAPITAL LETTER VIDA;Lu;0;L;;;;;N;;;;2C83;
+2C83;COPTIC SMALL LETTER VIDA;Ll;0;L;;;;;N;;;2C82;;2C82
+2C84;COPTIC CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;2C85;
+2C85;COPTIC SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;2C84;;2C84
+2C86;COPTIC CAPITAL LETTER DALDA;Lu;0;L;;;;;N;;;;2C87;
+2C87;COPTIC SMALL LETTER DALDA;Ll;0;L;;;;;N;;;2C86;;2C86
+2C88;COPTIC CAPITAL LETTER EIE;Lu;0;L;;;;;N;;;;2C89;
+2C89;COPTIC SMALL LETTER EIE;Ll;0;L;;;;;N;;;2C88;;2C88
+2C8A;COPTIC CAPITAL LETTER SOU;Lu;0;L;;;;;N;;;;2C8B;
+2C8B;COPTIC SMALL LETTER SOU;Ll;0;L;;;;;N;;;2C8A;;2C8A
+2C8C;COPTIC CAPITAL LETTER ZATA;Lu;0;L;;;;;N;;;;2C8D;
+2C8D;COPTIC SMALL LETTER ZATA;Ll;0;L;;;;;N;;;2C8C;;2C8C
+2C8E;COPTIC CAPITAL LETTER HATE;Lu;0;L;;;;;N;;;;2C8F;
+2C8F;COPTIC SMALL LETTER HATE;Ll;0;L;;;;;N;;;2C8E;;2C8E
+2C90;COPTIC CAPITAL LETTER THETHE;Lu;0;L;;;;;N;;;;2C91;
+2C91;COPTIC SMALL LETTER THETHE;Ll;0;L;;;;;N;;;2C90;;2C90
+2C92;COPTIC CAPITAL LETTER IAUDA;Lu;0;L;;;;;N;;;;2C93;
+2C93;COPTIC SMALL LETTER IAUDA;Ll;0;L;;;;;N;;;2C92;;2C92
+2C94;COPTIC CAPITAL LETTER KAPA;Lu;0;L;;;;;N;;;;2C95;
+2C95;COPTIC SMALL LETTER KAPA;Ll;0;L;;;;;N;;;2C94;;2C94
+2C96;COPTIC CAPITAL LETTER LAULA;Lu;0;L;;;;;N;;;;2C97;
+2C97;COPTIC SMALL LETTER LAULA;Ll;0;L;;;;;N;;;2C96;;2C96
+2C98;COPTIC CAPITAL LETTER MI;Lu;0;L;;;;;N;;;;2C99;
+2C99;COPTIC SMALL LETTER MI;Ll;0;L;;;;;N;;;2C98;;2C98
+2C9A;COPTIC CAPITAL LETTER NI;Lu;0;L;;;;;N;;;;2C9B;
+2C9B;COPTIC SMALL LETTER NI;Ll;0;L;;;;;N;;;2C9A;;2C9A
+2C9C;COPTIC CAPITAL LETTER KSI;Lu;0;L;;;;;N;;;;2C9D;
+2C9D;COPTIC SMALL LETTER KSI;Ll;0;L;;;;;N;;;2C9C;;2C9C
+2C9E;COPTIC CAPITAL LETTER O;Lu;0;L;;;;;N;;;;2C9F;
+2C9F;COPTIC SMALL LETTER O;Ll;0;L;;;;;N;;;2C9E;;2C9E
+2CA0;COPTIC CAPITAL LETTER PI;Lu;0;L;;;;;N;;;;2CA1;
+2CA1;COPTIC SMALL LETTER PI;Ll;0;L;;;;;N;;;2CA0;;2CA0
+2CA2;COPTIC CAPITAL LETTER RO;Lu;0;L;;;;;N;;;;2CA3;
+2CA3;COPTIC SMALL LETTER RO;Ll;0;L;;;;;N;;;2CA2;;2CA2
+2CA4;COPTIC CAPITAL LETTER SIMA;Lu;0;L;;;;;N;;;;2CA5;
+2CA5;COPTIC SMALL LETTER SIMA;Ll;0;L;;;;;N;;;2CA4;;2CA4
+2CA6;COPTIC CAPITAL LETTER TAU;Lu;0;L;;;;;N;;;;2CA7;
+2CA7;COPTIC SMALL LETTER TAU;Ll;0;L;;;;;N;;;2CA6;;2CA6
+2CA8;COPTIC CAPITAL LETTER UA;Lu;0;L;;;;;N;;;;2CA9;
+2CA9;COPTIC SMALL LETTER UA;Ll;0;L;;;;;N;;;2CA8;;2CA8
+2CAA;COPTIC CAPITAL LETTER FI;Lu;0;L;;;;;N;;;;2CAB;
+2CAB;COPTIC SMALL LETTER FI;Ll;0;L;;;;;N;;;2CAA;;2CAA
+2CAC;COPTIC CAPITAL LETTER KHI;Lu;0;L;;;;;N;;;;2CAD;
+2CAD;COPTIC SMALL LETTER KHI;Ll;0;L;;;;;N;;;2CAC;;2CAC
+2CAE;COPTIC CAPITAL LETTER PSI;Lu;0;L;;;;;N;;;;2CAF;
+2CAF;COPTIC SMALL LETTER PSI;Ll;0;L;;;;;N;;;2CAE;;2CAE
+2CB0;COPTIC CAPITAL LETTER OOU;Lu;0;L;;;;;N;;;;2CB1;
+2CB1;COPTIC SMALL LETTER OOU;Ll;0;L;;;;;N;;;2CB0;;2CB0
+2CB2;COPTIC CAPITAL LETTER DIALECT-P ALEF;Lu;0;L;;;;;N;;;;2CB3;
+2CB3;COPTIC SMALL LETTER DIALECT-P ALEF;Ll;0;L;;;;;N;;;2CB2;;2CB2
+2CB4;COPTIC CAPITAL LETTER OLD COPTIC AIN;Lu;0;L;;;;;N;;;;2CB5;
+2CB5;COPTIC SMALL LETTER OLD COPTIC AIN;Ll;0;L;;;;;N;;;2CB4;;2CB4
+2CB6;COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE;Lu;0;L;;;;;N;;;;2CB7;
+2CB7;COPTIC SMALL LETTER CRYPTOGRAMMIC EIE;Ll;0;L;;;;;N;;;2CB6;;2CB6
+2CB8;COPTIC CAPITAL LETTER DIALECT-P KAPA;Lu;0;L;;;;;N;;;;2CB9;
+2CB9;COPTIC SMALL LETTER DIALECT-P KAPA;Ll;0;L;;;;;N;;;2CB8;;2CB8
+2CBA;COPTIC CAPITAL LETTER DIALECT-P NI;Lu;0;L;;;;;N;;;;2CBB;
+2CBB;COPTIC SMALL LETTER DIALECT-P NI;Ll;0;L;;;;;N;;;2CBA;;2CBA
+2CBC;COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI;Lu;0;L;;;;;N;;;;2CBD;
+2CBD;COPTIC SMALL LETTER CRYPTOGRAMMIC NI;Ll;0;L;;;;;N;;;2CBC;;2CBC
+2CBE;COPTIC CAPITAL LETTER OLD COPTIC OOU;Lu;0;L;;;;;N;;;;2CBF;
+2CBF;COPTIC SMALL LETTER OLD COPTIC OOU;Ll;0;L;;;;;N;;;2CBE;;2CBE
+2CC0;COPTIC CAPITAL LETTER SAMPI;Lu;0;L;;;;;N;;;;2CC1;
+2CC1;COPTIC SMALL LETTER SAMPI;Ll;0;L;;;;;N;;;2CC0;;2CC0
+2CC2;COPTIC CAPITAL LETTER CROSSED SHEI;Lu;0;L;;;;;N;;;;2CC3;
+2CC3;COPTIC SMALL LETTER CROSSED SHEI;Ll;0;L;;;;;N;;;2CC2;;2CC2
+2CC4;COPTIC CAPITAL LETTER OLD COPTIC SHEI;Lu;0;L;;;;;N;;;;2CC5;
+2CC5;COPTIC SMALL LETTER OLD COPTIC SHEI;Ll;0;L;;;;;N;;;2CC4;;2CC4
+2CC6;COPTIC CAPITAL LETTER OLD COPTIC ESH;Lu;0;L;;;;;N;;;;2CC7;
+2CC7;COPTIC SMALL LETTER OLD COPTIC ESH;Ll;0;L;;;;;N;;;2CC6;;2CC6
+2CC8;COPTIC CAPITAL LETTER AKHMIMIC KHEI;Lu;0;L;;;;;N;;;;2CC9;
+2CC9;COPTIC SMALL LETTER AKHMIMIC KHEI;Ll;0;L;;;;;N;;;2CC8;;2CC8
+2CCA;COPTIC CAPITAL LETTER DIALECT-P HORI;Lu;0;L;;;;;N;;;;2CCB;
+2CCB;COPTIC SMALL LETTER DIALECT-P HORI;Ll;0;L;;;;;N;;;2CCA;;2CCA
+2CCC;COPTIC CAPITAL LETTER OLD COPTIC HORI;Lu;0;L;;;;;N;;;;2CCD;
+2CCD;COPTIC SMALL LETTER OLD COPTIC HORI;Ll;0;L;;;;;N;;;2CCC;;2CCC
+2CCE;COPTIC CAPITAL LETTER OLD COPTIC HA;Lu;0;L;;;;;N;;;;2CCF;
+2CCF;COPTIC SMALL LETTER OLD COPTIC HA;Ll;0;L;;;;;N;;;2CCE;;2CCE
+2CD0;COPTIC CAPITAL LETTER L-SHAPED HA;Lu;0;L;;;;;N;;;;2CD1;
+2CD1;COPTIC SMALL LETTER L-SHAPED HA;Ll;0;L;;;;;N;;;2CD0;;2CD0
+2CD2;COPTIC CAPITAL LETTER OLD COPTIC HEI;Lu;0;L;;;;;N;;;;2CD3;
+2CD3;COPTIC SMALL LETTER OLD COPTIC HEI;Ll;0;L;;;;;N;;;2CD2;;2CD2
+2CD4;COPTIC CAPITAL LETTER OLD COPTIC HAT;Lu;0;L;;;;;N;;;;2CD5;
+2CD5;COPTIC SMALL LETTER OLD COPTIC HAT;Ll;0;L;;;;;N;;;2CD4;;2CD4
+2CD6;COPTIC CAPITAL LETTER OLD COPTIC GANGIA;Lu;0;L;;;;;N;;;;2CD7;
+2CD7;COPTIC SMALL LETTER OLD COPTIC GANGIA;Ll;0;L;;;;;N;;;2CD6;;2CD6
+2CD8;COPTIC CAPITAL LETTER OLD COPTIC DJA;Lu;0;L;;;;;N;;;;2CD9;
+2CD9;COPTIC SMALL LETTER OLD COPTIC DJA;Ll;0;L;;;;;N;;;2CD8;;2CD8
+2CDA;COPTIC CAPITAL LETTER OLD COPTIC SHIMA;Lu;0;L;;;;;N;;;;2CDB;
+2CDB;COPTIC SMALL LETTER OLD COPTIC SHIMA;Ll;0;L;;;;;N;;;2CDA;;2CDA
+2CDC;COPTIC CAPITAL LETTER OLD NUBIAN SHIMA;Lu;0;L;;;;;N;;;;2CDD;
+2CDD;COPTIC SMALL LETTER OLD NUBIAN SHIMA;Ll;0;L;;;;;N;;;2CDC;;2CDC
+2CDE;COPTIC CAPITAL LETTER OLD NUBIAN NGI;Lu;0;L;;;;;N;;;;2CDF;
+2CDF;COPTIC SMALL LETTER OLD NUBIAN NGI;Ll;0;L;;;;;N;;;2CDE;;2CDE
+2CE0;COPTIC CAPITAL LETTER OLD NUBIAN NYI;Lu;0;L;;;;;N;;;;2CE1;
+2CE1;COPTIC SMALL LETTER OLD NUBIAN NYI;Ll;0;L;;;;;N;;;2CE0;;2CE0
+2CE2;COPTIC CAPITAL LETTER OLD NUBIAN WAU;Lu;0;L;;;;;N;;;;2CE3;
+2CE3;COPTIC SMALL LETTER OLD NUBIAN WAU;Ll;0;L;;;;;N;;;2CE2;;2CE2
+2CE4;COPTIC SYMBOL KAI;Ll;0;L;;;;;N;;;;;
+2CE5;COPTIC SYMBOL MI RO;So;0;ON;;;;;N;;;;;
+2CE6;COPTIC SYMBOL PI RO;So;0;ON;;;;;N;;;;;
+2CE7;COPTIC SYMBOL STAUROS;So;0;ON;;;;;N;;;;;
+2CE8;COPTIC SYMBOL TAU RO;So;0;ON;;;;;N;;;;;
+2CE9;COPTIC SYMBOL KHI RO;So;0;ON;;;;;N;;;;;
+2CEA;COPTIC SYMBOL SHIMA SIMA;So;0;ON;;;;;N;;;;;
+2CEB;COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHEI;Lu;0;L;;;;;N;;;;2CEC;
+2CEC;COPTIC SMALL LETTER CRYPTOGRAMMIC SHEI;Ll;0;L;;;;;N;;;2CEB;;2CEB
+2CED;COPTIC CAPITAL LETTER CRYPTOGRAMMIC GANGIA;Lu;0;L;;;;;N;;;;2CEE;
+2CEE;COPTIC SMALL LETTER CRYPTOGRAMMIC GANGIA;Ll;0;L;;;;;N;;;2CED;;2CED
+2CEF;COPTIC COMBINING NI ABOVE;Mn;230;NSM;;;;;N;;;;;
+2CF0;COPTIC COMBINING SPIRITUS ASPER;Mn;230;NSM;;;;;N;;;;;
+2CF1;COPTIC COMBINING SPIRITUS LENIS;Mn;230;NSM;;;;;N;;;;;
+2CF2;COPTIC CAPITAL LETTER BOHAIRIC KHEI;Lu;0;L;;;;;N;;;;2CF3;
+2CF3;COPTIC SMALL LETTER BOHAIRIC KHEI;Ll;0;L;;;;;N;;;2CF2;;2CF2
+2CF9;COPTIC OLD NUBIAN FULL STOP;Po;0;ON;;;;;N;;;;;
+2CFA;COPTIC OLD NUBIAN DIRECT QUESTION MARK;Po;0;ON;;;;;N;;;;;
+2CFB;COPTIC OLD NUBIAN INDIRECT QUESTION MARK;Po;0;ON;;;;;N;;;;;
+2CFC;COPTIC OLD NUBIAN VERSE DIVIDER;Po;0;ON;;;;;N;;;;;
+2CFD;COPTIC FRACTION ONE HALF;No;0;ON;;;;1/2;N;;;;;
+2CFE;COPTIC FULL STOP;Po;0;ON;;;;;N;;;;;
+2CFF;COPTIC MORPHOLOGICAL DIVIDER;Po;0;ON;;;;;N;;;;;
+2D00;GEORGIAN SMALL LETTER AN;Ll;0;L;;;;;N;;;10A0;;10A0
+2D01;GEORGIAN SMALL LETTER BAN;Ll;0;L;;;;;N;;;10A1;;10A1
+2D02;GEORGIAN SMALL LETTER GAN;Ll;0;L;;;;;N;;;10A2;;10A2
+2D03;GEORGIAN SMALL LETTER DON;Ll;0;L;;;;;N;;;10A3;;10A3
+2D04;GEORGIAN SMALL LETTER EN;Ll;0;L;;;;;N;;;10A4;;10A4
+2D05;GEORGIAN SMALL LETTER VIN;Ll;0;L;;;;;N;;;10A5;;10A5
+2D06;GEORGIAN SMALL LETTER ZEN;Ll;0;L;;;;;N;;;10A6;;10A6
+2D07;GEORGIAN SMALL LETTER TAN;Ll;0;L;;;;;N;;;10A7;;10A7
+2D08;GEORGIAN SMALL LETTER IN;Ll;0;L;;;;;N;;;10A8;;10A8
+2D09;GEORGIAN SMALL LETTER KAN;Ll;0;L;;;;;N;;;10A9;;10A9
+2D0A;GEORGIAN SMALL LETTER LAS;Ll;0;L;;;;;N;;;10AA;;10AA
+2D0B;GEORGIAN SMALL LETTER MAN;Ll;0;L;;;;;N;;;10AB;;10AB
+2D0C;GEORGIAN SMALL LETTER NAR;Ll;0;L;;;;;N;;;10AC;;10AC
+2D0D;GEORGIAN SMALL LETTER ON;Ll;0;L;;;;;N;;;10AD;;10AD
+2D0E;GEORGIAN SMALL LETTER PAR;Ll;0;L;;;;;N;;;10AE;;10AE
+2D0F;GEORGIAN SMALL LETTER ZHAR;Ll;0;L;;;;;N;;;10AF;;10AF
+2D10;GEORGIAN SMALL LETTER RAE;Ll;0;L;;;;;N;;;10B0;;10B0
+2D11;GEORGIAN SMALL LETTER SAN;Ll;0;L;;;;;N;;;10B1;;10B1
+2D12;GEORGIAN SMALL LETTER TAR;Ll;0;L;;;;;N;;;10B2;;10B2
+2D13;GEORGIAN SMALL LETTER UN;Ll;0;L;;;;;N;;;10B3;;10B3
+2D14;GEORGIAN SMALL LETTER PHAR;Ll;0;L;;;;;N;;;10B4;;10B4
+2D15;GEORGIAN SMALL LETTER KHAR;Ll;0;L;;;;;N;;;10B5;;10B5
+2D16;GEORGIAN SMALL LETTER GHAN;Ll;0;L;;;;;N;;;10B6;;10B6
+2D17;GEORGIAN SMALL LETTER QAR;Ll;0;L;;;;;N;;;10B7;;10B7
+2D18;GEORGIAN SMALL LETTER SHIN;Ll;0;L;;;;;N;;;10B8;;10B8
+2D19;GEORGIAN SMALL LETTER CHIN;Ll;0;L;;;;;N;;;10B9;;10B9
+2D1A;GEORGIAN SMALL LETTER CAN;Ll;0;L;;;;;N;;;10BA;;10BA
+2D1B;GEORGIAN SMALL LETTER JIL;Ll;0;L;;;;;N;;;10BB;;10BB
+2D1C;GEORGIAN SMALL LETTER CIL;Ll;0;L;;;;;N;;;10BC;;10BC
+2D1D;GEORGIAN SMALL LETTER CHAR;Ll;0;L;;;;;N;;;10BD;;10BD
+2D1E;GEORGIAN SMALL LETTER XAN;Ll;0;L;;;;;N;;;10BE;;10BE
+2D1F;GEORGIAN SMALL LETTER JHAN;Ll;0;L;;;;;N;;;10BF;;10BF
+2D20;GEORGIAN SMALL LETTER HAE;Ll;0;L;;;;;N;;;10C0;;10C0
+2D21;GEORGIAN SMALL LETTER HE;Ll;0;L;;;;;N;;;10C1;;10C1
+2D22;GEORGIAN SMALL LETTER HIE;Ll;0;L;;;;;N;;;10C2;;10C2
+2D23;GEORGIAN SMALL LETTER WE;Ll;0;L;;;;;N;;;10C3;;10C3
+2D24;GEORGIAN SMALL LETTER HAR;Ll;0;L;;;;;N;;;10C4;;10C4
+2D25;GEORGIAN SMALL LETTER HOE;Ll;0;L;;;;;N;;;10C5;;10C5
+2D27;GEORGIAN SMALL LETTER YN;Ll;0;L;;;;;N;;;10C7;;10C7
+2D2D;GEORGIAN SMALL LETTER AEN;Ll;0;L;;;;;N;;;10CD;;10CD
+2D30;TIFINAGH LETTER YA;Lo;0;L;;;;;N;;;;;
+2D31;TIFINAGH LETTER YAB;Lo;0;L;;;;;N;;;;;
+2D32;TIFINAGH LETTER YABH;Lo;0;L;;;;;N;;;;;
+2D33;TIFINAGH LETTER YAG;Lo;0;L;;;;;N;;;;;
+2D34;TIFINAGH LETTER YAGHH;Lo;0;L;;;;;N;;;;;
+2D35;TIFINAGH LETTER BERBER ACADEMY YAJ;Lo;0;L;;;;;N;;;;;
+2D36;TIFINAGH LETTER YAJ;Lo;0;L;;;;;N;;;;;
+2D37;TIFINAGH LETTER YAD;Lo;0;L;;;;;N;;;;;
+2D38;TIFINAGH LETTER YADH;Lo;0;L;;;;;N;;;;;
+2D39;TIFINAGH LETTER YADD;Lo;0;L;;;;;N;;;;;
+2D3A;TIFINAGH LETTER YADDH;Lo;0;L;;;;;N;;;;;
+2D3B;TIFINAGH LETTER YEY;Lo;0;L;;;;;N;;;;;
+2D3C;TIFINAGH LETTER YAF;Lo;0;L;;;;;N;;;;;
+2D3D;TIFINAGH LETTER YAK;Lo;0;L;;;;;N;;;;;
+2D3E;TIFINAGH LETTER TUAREG YAK;Lo;0;L;;;;;N;;;;;
+2D3F;TIFINAGH LETTER YAKHH;Lo;0;L;;;;;N;;;;;
+2D40;TIFINAGH LETTER YAH;Lo;0;L;;;;;N;;;;;
+2D41;TIFINAGH LETTER BERBER ACADEMY YAH;Lo;0;L;;;;;N;;;;;
+2D42;TIFINAGH LETTER TUAREG YAH;Lo;0;L;;;;;N;;;;;
+2D43;TIFINAGH LETTER YAHH;Lo;0;L;;;;;N;;;;;
+2D44;TIFINAGH LETTER YAA;Lo;0;L;;;;;N;;;;;
+2D45;TIFINAGH LETTER YAKH;Lo;0;L;;;;;N;;;;;
+2D46;TIFINAGH LETTER TUAREG YAKH;Lo;0;L;;;;;N;;;;;
+2D47;TIFINAGH LETTER YAQ;Lo;0;L;;;;;N;;;;;
+2D48;TIFINAGH LETTER TUAREG YAQ;Lo;0;L;;;;;N;;;;;
+2D49;TIFINAGH LETTER YI;Lo;0;L;;;;;N;;;;;
+2D4A;TIFINAGH LETTER YAZH;Lo;0;L;;;;;N;;;;;
+2D4B;TIFINAGH LETTER AHAGGAR YAZH;Lo;0;L;;;;;N;;;;;
+2D4C;TIFINAGH LETTER TUAREG YAZH;Lo;0;L;;;;;N;;;;;
+2D4D;TIFINAGH LETTER YAL;Lo;0;L;;;;;N;;;;;
+2D4E;TIFINAGH LETTER YAM;Lo;0;L;;;;;N;;;;;
+2D4F;TIFINAGH LETTER YAN;Lo;0;L;;;;;N;;;;;
+2D50;TIFINAGH LETTER TUAREG YAGN;Lo;0;L;;;;;N;;;;;
+2D51;TIFINAGH LETTER TUAREG YANG;Lo;0;L;;;;;N;;;;;
+2D52;TIFINAGH LETTER YAP;Lo;0;L;;;;;N;;;;;
+2D53;TIFINAGH LETTER YU;Lo;0;L;;;;;N;;;;;
+2D54;TIFINAGH LETTER YAR;Lo;0;L;;;;;N;;;;;
+2D55;TIFINAGH LETTER YARR;Lo;0;L;;;;;N;;;;;
+2D56;TIFINAGH LETTER YAGH;Lo;0;L;;;;;N;;;;;
+2D57;TIFINAGH LETTER TUAREG YAGH;Lo;0;L;;;;;N;;;;;
+2D58;TIFINAGH LETTER AYER YAGH;Lo;0;L;;;;;N;;;;;
+2D59;TIFINAGH LETTER YAS;Lo;0;L;;;;;N;;;;;
+2D5A;TIFINAGH LETTER YASS;Lo;0;L;;;;;N;;;;;
+2D5B;TIFINAGH LETTER YASH;Lo;0;L;;;;;N;;;;;
+2D5C;TIFINAGH LETTER YAT;Lo;0;L;;;;;N;;;;;
+2D5D;TIFINAGH LETTER YATH;Lo;0;L;;;;;N;;;;;
+2D5E;TIFINAGH LETTER YACH;Lo;0;L;;;;;N;;;;;
+2D5F;TIFINAGH LETTER YATT;Lo;0;L;;;;;N;;;;;
+2D60;TIFINAGH LETTER YAV;Lo;0;L;;;;;N;;;;;
+2D61;TIFINAGH LETTER YAW;Lo;0;L;;;;;N;;;;;
+2D62;TIFINAGH LETTER YAY;Lo;0;L;;;;;N;;;;;
+2D63;TIFINAGH LETTER YAZ;Lo;0;L;;;;;N;;;;;
+2D64;TIFINAGH LETTER TAWELLEMET YAZ;Lo;0;L;;;;;N;;;;;
+2D65;TIFINAGH LETTER YAZZ;Lo;0;L;;;;;N;;;;;
+2D66;TIFINAGH LETTER YE;Lo;0;L;;;;;N;;;;;
+2D67;TIFINAGH LETTER YO;Lo;0;L;;;;;N;;;;;
+2D6F;TIFINAGH MODIFIER LETTER LABIALIZATION MARK;Lm;0;L;<super> 2D61;;;;N;;;;;
+2D70;TIFINAGH SEPARATOR MARK;Po;0;L;;;;;N;;;;;
+2D7F;TIFINAGH CONSONANT JOINER;Mn;9;NSM;;;;;N;;;;;
+2D80;ETHIOPIC SYLLABLE LOA;Lo;0;L;;;;;N;;;;;
+2D81;ETHIOPIC SYLLABLE MOA;Lo;0;L;;;;;N;;;;;
+2D82;ETHIOPIC SYLLABLE ROA;Lo;0;L;;;;;N;;;;;
+2D83;ETHIOPIC SYLLABLE SOA;Lo;0;L;;;;;N;;;;;
+2D84;ETHIOPIC SYLLABLE SHOA;Lo;0;L;;;;;N;;;;;
+2D85;ETHIOPIC SYLLABLE BOA;Lo;0;L;;;;;N;;;;;
+2D86;ETHIOPIC SYLLABLE TOA;Lo;0;L;;;;;N;;;;;
+2D87;ETHIOPIC SYLLABLE COA;Lo;0;L;;;;;N;;;;;
+2D88;ETHIOPIC SYLLABLE NOA;Lo;0;L;;;;;N;;;;;
+2D89;ETHIOPIC SYLLABLE NYOA;Lo;0;L;;;;;N;;;;;
+2D8A;ETHIOPIC SYLLABLE GLOTTAL OA;Lo;0;L;;;;;N;;;;;
+2D8B;ETHIOPIC SYLLABLE ZOA;Lo;0;L;;;;;N;;;;;
+2D8C;ETHIOPIC SYLLABLE DOA;Lo;0;L;;;;;N;;;;;
+2D8D;ETHIOPIC SYLLABLE DDOA;Lo;0;L;;;;;N;;;;;
+2D8E;ETHIOPIC SYLLABLE JOA;Lo;0;L;;;;;N;;;;;
+2D8F;ETHIOPIC SYLLABLE THOA;Lo;0;L;;;;;N;;;;;
+2D90;ETHIOPIC SYLLABLE CHOA;Lo;0;L;;;;;N;;;;;
+2D91;ETHIOPIC SYLLABLE PHOA;Lo;0;L;;;;;N;;;;;
+2D92;ETHIOPIC SYLLABLE POA;Lo;0;L;;;;;N;;;;;
+2D93;ETHIOPIC SYLLABLE GGWA;Lo;0;L;;;;;N;;;;;
+2D94;ETHIOPIC SYLLABLE GGWI;Lo;0;L;;;;;N;;;;;
+2D95;ETHIOPIC SYLLABLE GGWEE;Lo;0;L;;;;;N;;;;;
+2D96;ETHIOPIC SYLLABLE GGWE;Lo;0;L;;;;;N;;;;;
+2DA0;ETHIOPIC SYLLABLE SSA;Lo;0;L;;;;;N;;;;;
+2DA1;ETHIOPIC SYLLABLE SSU;Lo;0;L;;;;;N;;;;;
+2DA2;ETHIOPIC SYLLABLE SSI;Lo;0;L;;;;;N;;;;;
+2DA3;ETHIOPIC SYLLABLE SSAA;Lo;0;L;;;;;N;;;;;
+2DA4;ETHIOPIC SYLLABLE SSEE;Lo;0;L;;;;;N;;;;;
+2DA5;ETHIOPIC SYLLABLE SSE;Lo;0;L;;;;;N;;;;;
+2DA6;ETHIOPIC SYLLABLE SSO;Lo;0;L;;;;;N;;;;;
+2DA8;ETHIOPIC SYLLABLE CCA;Lo;0;L;;;;;N;;;;;
+2DA9;ETHIOPIC SYLLABLE CCU;Lo;0;L;;;;;N;;;;;
+2DAA;ETHIOPIC SYLLABLE CCI;Lo;0;L;;;;;N;;;;;
+2DAB;ETHIOPIC SYLLABLE CCAA;Lo;0;L;;;;;N;;;;;
+2DAC;ETHIOPIC SYLLABLE CCEE;Lo;0;L;;;;;N;;;;;
+2DAD;ETHIOPIC SYLLABLE CCE;Lo;0;L;;;;;N;;;;;
+2DAE;ETHIOPIC SYLLABLE CCO;Lo;0;L;;;;;N;;;;;
+2DB0;ETHIOPIC SYLLABLE ZZA;Lo;0;L;;;;;N;;;;;
+2DB1;ETHIOPIC SYLLABLE ZZU;Lo;0;L;;;;;N;;;;;
+2DB2;ETHIOPIC SYLLABLE ZZI;Lo;0;L;;;;;N;;;;;
+2DB3;ETHIOPIC SYLLABLE ZZAA;Lo;0;L;;;;;N;;;;;
+2DB4;ETHIOPIC SYLLABLE ZZEE;Lo;0;L;;;;;N;;;;;
+2DB5;ETHIOPIC SYLLABLE ZZE;Lo;0;L;;;;;N;;;;;
+2DB6;ETHIOPIC SYLLABLE ZZO;Lo;0;L;;;;;N;;;;;
+2DB8;ETHIOPIC SYLLABLE CCHA;Lo;0;L;;;;;N;;;;;
+2DB9;ETHIOPIC SYLLABLE CCHU;Lo;0;L;;;;;N;;;;;
+2DBA;ETHIOPIC SYLLABLE CCHI;Lo;0;L;;;;;N;;;;;
+2DBB;ETHIOPIC SYLLABLE CCHAA;Lo;0;L;;;;;N;;;;;
+2DBC;ETHIOPIC SYLLABLE CCHEE;Lo;0;L;;;;;N;;;;;
+2DBD;ETHIOPIC SYLLABLE CCHE;Lo;0;L;;;;;N;;;;;
+2DBE;ETHIOPIC SYLLABLE CCHO;Lo;0;L;;;;;N;;;;;
+2DC0;ETHIOPIC SYLLABLE QYA;Lo;0;L;;;;;N;;;;;
+2DC1;ETHIOPIC SYLLABLE QYU;Lo;0;L;;;;;N;;;;;
+2DC2;ETHIOPIC SYLLABLE QYI;Lo;0;L;;;;;N;;;;;
+2DC3;ETHIOPIC SYLLABLE QYAA;Lo;0;L;;;;;N;;;;;
+2DC4;ETHIOPIC SYLLABLE QYEE;Lo;0;L;;;;;N;;;;;
+2DC5;ETHIOPIC SYLLABLE QYE;Lo;0;L;;;;;N;;;;;
+2DC6;ETHIOPIC SYLLABLE QYO;Lo;0;L;;;;;N;;;;;
+2DC8;ETHIOPIC SYLLABLE KYA;Lo;0;L;;;;;N;;;;;
+2DC9;ETHIOPIC SYLLABLE KYU;Lo;0;L;;;;;N;;;;;
+2DCA;ETHIOPIC SYLLABLE KYI;Lo;0;L;;;;;N;;;;;
+2DCB;ETHIOPIC SYLLABLE KYAA;Lo;0;L;;;;;N;;;;;
+2DCC;ETHIOPIC SYLLABLE KYEE;Lo;0;L;;;;;N;;;;;
+2DCD;ETHIOPIC SYLLABLE KYE;Lo;0;L;;;;;N;;;;;
+2DCE;ETHIOPIC SYLLABLE KYO;Lo;0;L;;;;;N;;;;;
+2DD0;ETHIOPIC SYLLABLE XYA;Lo;0;L;;;;;N;;;;;
+2DD1;ETHIOPIC SYLLABLE XYU;Lo;0;L;;;;;N;;;;;
+2DD2;ETHIOPIC SYLLABLE XYI;Lo;0;L;;;;;N;;;;;
+2DD3;ETHIOPIC SYLLABLE XYAA;Lo;0;L;;;;;N;;;;;
+2DD4;ETHIOPIC SYLLABLE XYEE;Lo;0;L;;;;;N;;;;;
+2DD5;ETHIOPIC SYLLABLE XYE;Lo;0;L;;;;;N;;;;;
+2DD6;ETHIOPIC SYLLABLE XYO;Lo;0;L;;;;;N;;;;;
+2DD8;ETHIOPIC SYLLABLE GYA;Lo;0;L;;;;;N;;;;;
+2DD9;ETHIOPIC SYLLABLE GYU;Lo;0;L;;;;;N;;;;;
+2DDA;ETHIOPIC SYLLABLE GYI;Lo;0;L;;;;;N;;;;;
+2DDB;ETHIOPIC SYLLABLE GYAA;Lo;0;L;;;;;N;;;;;
+2DDC;ETHIOPIC SYLLABLE GYEE;Lo;0;L;;;;;N;;;;;
+2DDD;ETHIOPIC SYLLABLE GYE;Lo;0;L;;;;;N;;;;;
+2DDE;ETHIOPIC SYLLABLE GYO;Lo;0;L;;;;;N;;;;;
+2DE0;COMBINING CYRILLIC LETTER BE;Mn;230;NSM;;;;;N;;;;;
+2DE1;COMBINING CYRILLIC LETTER VE;Mn;230;NSM;;;;;N;;;;;
+2DE2;COMBINING CYRILLIC LETTER GHE;Mn;230;NSM;;;;;N;;;;;
+2DE3;COMBINING CYRILLIC LETTER DE;Mn;230;NSM;;;;;N;;;;;
+2DE4;COMBINING CYRILLIC LETTER ZHE;Mn;230;NSM;;;;;N;;;;;
+2DE5;COMBINING CYRILLIC LETTER ZE;Mn;230;NSM;;;;;N;;;;;
+2DE6;COMBINING CYRILLIC LETTER KA;Mn;230;NSM;;;;;N;;;;;
+2DE7;COMBINING CYRILLIC LETTER EL;Mn;230;NSM;;;;;N;;;;;
+2DE8;COMBINING CYRILLIC LETTER EM;Mn;230;NSM;;;;;N;;;;;
+2DE9;COMBINING CYRILLIC LETTER EN;Mn;230;NSM;;;;;N;;;;;
+2DEA;COMBINING CYRILLIC LETTER O;Mn;230;NSM;;;;;N;;;;;
+2DEB;COMBINING CYRILLIC LETTER PE;Mn;230;NSM;;;;;N;;;;;
+2DEC;COMBINING CYRILLIC LETTER ER;Mn;230;NSM;;;;;N;;;;;
+2DED;COMBINING CYRILLIC LETTER ES;Mn;230;NSM;;;;;N;;;;;
+2DEE;COMBINING CYRILLIC LETTER TE;Mn;230;NSM;;;;;N;;;;;
+2DEF;COMBINING CYRILLIC LETTER HA;Mn;230;NSM;;;;;N;;;;;
+2DF0;COMBINING CYRILLIC LETTER TSE;Mn;230;NSM;;;;;N;;;;;
+2DF1;COMBINING CYRILLIC LETTER CHE;Mn;230;NSM;;;;;N;;;;;
+2DF2;COMBINING CYRILLIC LETTER SHA;Mn;230;NSM;;;;;N;;;;;
+2DF3;COMBINING CYRILLIC LETTER SHCHA;Mn;230;NSM;;;;;N;;;;;
+2DF4;COMBINING CYRILLIC LETTER FITA;Mn;230;NSM;;;;;N;;;;;
+2DF5;COMBINING CYRILLIC LETTER ES-TE;Mn;230;NSM;;;;;N;;;;;
+2DF6;COMBINING CYRILLIC LETTER A;Mn;230;NSM;;;;;N;;;;;
+2DF7;COMBINING CYRILLIC LETTER IE;Mn;230;NSM;;;;;N;;;;;
+2DF8;COMBINING CYRILLIC LETTER DJERV;Mn;230;NSM;;;;;N;;;;;
+2DF9;COMBINING CYRILLIC LETTER MONOGRAPH UK;Mn;230;NSM;;;;;N;;;;;
+2DFA;COMBINING CYRILLIC LETTER YAT;Mn;230;NSM;;;;;N;;;;;
+2DFB;COMBINING CYRILLIC LETTER YU;Mn;230;NSM;;;;;N;;;;;
+2DFC;COMBINING CYRILLIC LETTER IOTIFIED A;Mn;230;NSM;;;;;N;;;;;
+2DFD;COMBINING CYRILLIC LETTER LITTLE YUS;Mn;230;NSM;;;;;N;;;;;
+2DFE;COMBINING CYRILLIC LETTER BIG YUS;Mn;230;NSM;;;;;N;;;;;
+2DFF;COMBINING CYRILLIC LETTER IOTIFIED BIG YUS;Mn;230;NSM;;;;;N;;;;;
+2E00;RIGHT ANGLE SUBSTITUTION MARKER;Po;0;ON;;;;;N;;;;;
+2E01;RIGHT ANGLE DOTTED SUBSTITUTION MARKER;Po;0;ON;;;;;N;;;;;
+2E02;LEFT SUBSTITUTION BRACKET;Pi;0;ON;;;;;Y;;;;;
+2E03;RIGHT SUBSTITUTION BRACKET;Pf;0;ON;;;;;Y;;;;;
+2E04;LEFT DOTTED SUBSTITUTION BRACKET;Pi;0;ON;;;;;Y;;;;;
+2E05;RIGHT DOTTED SUBSTITUTION BRACKET;Pf;0;ON;;;;;Y;;;;;
+2E06;RAISED INTERPOLATION MARKER;Po;0;ON;;;;;N;;;;;
+2E07;RAISED DOTTED INTERPOLATION MARKER;Po;0;ON;;;;;N;;;;;
+2E08;DOTTED TRANSPOSITION MARKER;Po;0;ON;;;;;N;;;;;
+2E09;LEFT TRANSPOSITION BRACKET;Pi;0;ON;;;;;Y;;;;;
+2E0A;RIGHT TRANSPOSITION BRACKET;Pf;0;ON;;;;;Y;;;;;
+2E0B;RAISED SQUARE;Po;0;ON;;;;;N;;;;;
+2E0C;LEFT RAISED OMISSION BRACKET;Pi;0;ON;;;;;Y;;;;;
+2E0D;RIGHT RAISED OMISSION BRACKET;Pf;0;ON;;;;;Y;;;;;
+2E0E;EDITORIAL CORONIS;Po;0;ON;;;;;N;;;;;
+2E0F;PARAGRAPHOS;Po;0;ON;;;;;N;;;;;
+2E10;FORKED PARAGRAPHOS;Po;0;ON;;;;;N;;;;;
+2E11;REVERSED FORKED PARAGRAPHOS;Po;0;ON;;;;;N;;;;;
+2E12;HYPODIASTOLE;Po;0;ON;;;;;N;;;;;
+2E13;DOTTED OBELOS;Po;0;ON;;;;;N;;;;;
+2E14;DOWNWARDS ANCORA;Po;0;ON;;;;;N;;;;;
+2E15;UPWARDS ANCORA;Po;0;ON;;;;;N;;;;;
+2E16;DOTTED RIGHT-POINTING ANGLE;Po;0;ON;;;;;N;;;;;
+2E17;DOUBLE OBLIQUE HYPHEN;Pd;0;ON;;;;;N;;;;;
+2E18;INVERTED INTERROBANG;Po;0;ON;;;;;N;;;;;
+2E19;PALM BRANCH;Po;0;ON;;;;;N;;;;;
+2E1A;HYPHEN WITH DIAERESIS;Pd;0;ON;;;;;N;;;;;
+2E1B;TILDE WITH RING ABOVE;Po;0;ON;;;;;N;;;;;
+2E1C;LEFT LOW PARAPHRASE BRACKET;Pi;0;ON;;;;;Y;;;;;
+2E1D;RIGHT LOW PARAPHRASE BRACKET;Pf;0;ON;;;;;Y;;;;;
+2E1E;TILDE WITH DOT ABOVE;Po;0;ON;;;;;N;;;;;
+2E1F;TILDE WITH DOT BELOW;Po;0;ON;;;;;N;;;;;
+2E20;LEFT VERTICAL BAR WITH QUILL;Pi;0;ON;;;;;Y;;;;;
+2E21;RIGHT VERTICAL BAR WITH QUILL;Pf;0;ON;;;;;Y;;;;;
+2E22;TOP LEFT HALF BRACKET;Ps;0;ON;;;;;Y;;;;;
+2E23;TOP RIGHT HALF BRACKET;Pe;0;ON;;;;;Y;;;;;
+2E24;BOTTOM LEFT HALF BRACKET;Ps;0;ON;;;;;Y;;;;;
+2E25;BOTTOM RIGHT HALF BRACKET;Pe;0;ON;;;;;Y;;;;;
+2E26;LEFT SIDEWAYS U BRACKET;Ps;0;ON;;;;;Y;;;;;
+2E27;RIGHT SIDEWAYS U BRACKET;Pe;0;ON;;;;;Y;;;;;
+2E28;LEFT DOUBLE PARENTHESIS;Ps;0;ON;;;;;Y;;;;;
+2E29;RIGHT DOUBLE PARENTHESIS;Pe;0;ON;;;;;Y;;;;;
+2E2A;TWO DOTS OVER ONE DOT PUNCTUATION;Po;0;ON;;;;;N;;;;;
+2E2B;ONE DOT OVER TWO DOTS PUNCTUATION;Po;0;ON;;;;;N;;;;;
+2E2C;SQUARED FOUR DOT PUNCTUATION;Po;0;ON;;;;;N;;;;;
+2E2D;FIVE DOT MARK;Po;0;ON;;;;;N;;;;;
+2E2E;REVERSED QUESTION MARK;Po;0;ON;;;;;N;;;;;
+2E2F;VERTICAL TILDE;Lm;0;ON;;;;;N;;;;;
+2E30;RING POINT;Po;0;ON;;;;;N;;;;;
+2E31;WORD SEPARATOR MIDDLE DOT;Po;0;ON;;;;;N;;;;;
+2E32;TURNED COMMA;Po;0;ON;;;;;N;;;;;
+2E33;RAISED DOT;Po;0;ON;;;;;N;;;;;
+2E34;RAISED COMMA;Po;0;ON;;;;;N;;;;;
+2E35;TURNED SEMICOLON;Po;0;ON;;;;;N;;;;;
+2E36;DAGGER WITH LEFT GUARD;Po;0;ON;;;;;N;;;;;
+2E37;DAGGER WITH RIGHT GUARD;Po;0;ON;;;;;N;;;;;
+2E38;TURNED DAGGER;Po;0;ON;;;;;N;;;;;
+2E39;TOP HALF SECTION SIGN;Po;0;ON;;;;;N;;;;;
+2E3A;TWO-EM DASH;Pd;0;ON;;;;;N;;;;;
+2E3B;THREE-EM DASH;Pd;0;ON;;;;;N;;;;;
+2E3C;STENOGRAPHIC FULL STOP;Po;0;ON;;;;;N;;;;;
+2E3D;VERTICAL SIX DOTS;Po;0;ON;;;;;N;;;;;
+2E3E;WIGGLY VERTICAL LINE;Po;0;ON;;;;;N;;;;;
+2E3F;CAPITULUM;Po;0;ON;;;;;N;;;;;
+2E40;DOUBLE HYPHEN;Pd;0;ON;;;;;N;;;;;
+2E41;REVERSED COMMA;Po;0;ON;;;;;N;;;;;
+2E42;DOUBLE LOW-REVERSED-9 QUOTATION MARK;Ps;0;ON;;;;;N;;;;;
+2E43;DASH WITH LEFT UPTURN;Po;0;ON;;;;;N;;;;;
+2E44;DOUBLE SUSPENSION MARK;Po;0;ON;;;;;N;;;;;
+2E45;INVERTED LOW KAVYKA;Po;0;ON;;;;;N;;;;;
+2E46;INVERTED LOW KAVYKA WITH KAVYKA ABOVE;Po;0;ON;;;;;N;;;;;
+2E47;LOW KAVYKA;Po;0;ON;;;;;N;;;;;
+2E48;LOW KAVYKA WITH DOT;Po;0;ON;;;;;N;;;;;
+2E49;DOUBLE STACKED COMMA;Po;0;ON;;;;;N;;;;;
+2E4A;DOTTED SOLIDUS;Po;0;ON;;;;;N;;;;;
+2E4B;TRIPLE DAGGER;Po;0;ON;;;;;N;;;;;
+2E4C;MEDIEVAL COMMA;Po;0;ON;;;;;N;;;;;
+2E4D;PARAGRAPHUS MARK;Po;0;ON;;;;;N;;;;;
+2E4E;PUNCTUS ELEVATUS MARK;Po;0;ON;;;;;N;;;;;
+2E4F;CORNISH VERSE DIVIDER;Po;0;ON;;;;;N;;;;;
+2E80;CJK RADICAL REPEAT;So;0;ON;;;;;N;;;;;
+2E81;CJK RADICAL CLIFF;So;0;ON;;;;;N;;;;;
+2E82;CJK RADICAL SECOND ONE;So;0;ON;;;;;N;;;;;
+2E83;CJK RADICAL SECOND TWO;So;0;ON;;;;;N;;;;;
+2E84;CJK RADICAL SECOND THREE;So;0;ON;;;;;N;;;;;
+2E85;CJK RADICAL PERSON;So;0;ON;;;;;N;;;;;
+2E86;CJK RADICAL BOX;So;0;ON;;;;;N;;;;;
+2E87;CJK RADICAL TABLE;So;0;ON;;;;;N;;;;;
+2E88;CJK RADICAL KNIFE ONE;So;0;ON;;;;;N;;;;;
+2E89;CJK RADICAL KNIFE TWO;So;0;ON;;;;;N;;;;;
+2E8A;CJK RADICAL DIVINATION;So;0;ON;;;;;N;;;;;
+2E8B;CJK RADICAL SEAL;So;0;ON;;;;;N;;;;;
+2E8C;CJK RADICAL SMALL ONE;So;0;ON;;;;;N;;;;;
+2E8D;CJK RADICAL SMALL TWO;So;0;ON;;;;;N;;;;;
+2E8E;CJK RADICAL LAME ONE;So;0;ON;;;;;N;;;;;
+2E8F;CJK RADICAL LAME TWO;So;0;ON;;;;;N;;;;;
+2E90;CJK RADICAL LAME THREE;So;0;ON;;;;;N;;;;;
+2E91;CJK RADICAL LAME FOUR;So;0;ON;;;;;N;;;;;
+2E92;CJK RADICAL SNAKE;So;0;ON;;;;;N;;;;;
+2E93;CJK RADICAL THREAD;So;0;ON;;;;;N;;;;;
+2E94;CJK RADICAL SNOUT ONE;So;0;ON;;;;;N;;;;;
+2E95;CJK RADICAL SNOUT TWO;So;0;ON;;;;;N;;;;;
+2E96;CJK RADICAL HEART ONE;So;0;ON;;;;;N;;;;;
+2E97;CJK RADICAL HEART TWO;So;0;ON;;;;;N;;;;;
+2E98;CJK RADICAL HAND;So;0;ON;;;;;N;;;;;
+2E99;CJK RADICAL RAP;So;0;ON;;;;;N;;;;;
+2E9B;CJK RADICAL CHOKE;So;0;ON;;;;;N;;;;;
+2E9C;CJK RADICAL SUN;So;0;ON;;;;;N;;;;;
+2E9D;CJK RADICAL MOON;So;0;ON;;;;;N;;;;;
+2E9E;CJK RADICAL DEATH;So;0;ON;;;;;N;;;;;
+2E9F;CJK RADICAL MOTHER;So;0;ON;<compat> 6BCD;;;;N;;;;;
+2EA0;CJK RADICAL CIVILIAN;So;0;ON;;;;;N;;;;;
+2EA1;CJK RADICAL WATER ONE;So;0;ON;;;;;N;;;;;
+2EA2;CJK RADICAL WATER TWO;So;0;ON;;;;;N;;;;;
+2EA3;CJK RADICAL FIRE;So;0;ON;;;;;N;;;;;
+2EA4;CJK RADICAL PAW ONE;So;0;ON;;;;;N;;;;;
+2EA5;CJK RADICAL PAW TWO;So;0;ON;;;;;N;;;;;
+2EA6;CJK RADICAL SIMPLIFIED HALF TREE TRUNK;So;0;ON;;;;;N;;;;;
+2EA7;CJK RADICAL COW;So;0;ON;;;;;N;;;;;
+2EA8;CJK RADICAL DOG;So;0;ON;;;;;N;;;;;
+2EA9;CJK RADICAL JADE;So;0;ON;;;;;N;;;;;
+2EAA;CJK RADICAL BOLT OF CLOTH;So;0;ON;;;;;N;;;;;
+2EAB;CJK RADICAL EYE;So;0;ON;;;;;N;;;;;
+2EAC;CJK RADICAL SPIRIT ONE;So;0;ON;;;;;N;;;;;
+2EAD;CJK RADICAL SPIRIT TWO;So;0;ON;;;;;N;;;;;
+2EAE;CJK RADICAL BAMBOO;So;0;ON;;;;;N;;;;;
+2EAF;CJK RADICAL SILK;So;0;ON;;;;;N;;;;;
+2EB0;CJK RADICAL C-SIMPLIFIED SILK;So;0;ON;;;;;N;;;;;
+2EB1;CJK RADICAL NET ONE;So;0;ON;;;;;N;;;;;
+2EB2;CJK RADICAL NET TWO;So;0;ON;;;;;N;;;;;
+2EB3;CJK RADICAL NET THREE;So;0;ON;;;;;N;;;;;
+2EB4;CJK RADICAL NET FOUR;So;0;ON;;;;;N;;;;;
+2EB5;CJK RADICAL MESH;So;0;ON;;;;;N;;;;;
+2EB6;CJK RADICAL SHEEP;So;0;ON;;;;;N;;;;;
+2EB7;CJK RADICAL RAM;So;0;ON;;;;;N;;;;;
+2EB8;CJK RADICAL EWE;So;0;ON;;;;;N;;;;;
+2EB9;CJK RADICAL OLD;So;0;ON;;;;;N;;;;;
+2EBA;CJK RADICAL BRUSH ONE;So;0;ON;;;;;N;;;;;
+2EBB;CJK RADICAL BRUSH TWO;So;0;ON;;;;;N;;;;;
+2EBC;CJK RADICAL MEAT;So;0;ON;;;;;N;;;;;
+2EBD;CJK RADICAL MORTAR;So;0;ON;;;;;N;;;;;
+2EBE;CJK RADICAL GRASS ONE;So;0;ON;;;;;N;;;;;
+2EBF;CJK RADICAL GRASS TWO;So;0;ON;;;;;N;;;;;
+2EC0;CJK RADICAL GRASS THREE;So;0;ON;;;;;N;;;;;
+2EC1;CJK RADICAL TIGER;So;0;ON;;;;;N;;;;;
+2EC2;CJK RADICAL CLOTHES;So;0;ON;;;;;N;;;;;
+2EC3;CJK RADICAL WEST ONE;So;0;ON;;;;;N;;;;;
+2EC4;CJK RADICAL WEST TWO;So;0;ON;;;;;N;;;;;
+2EC5;CJK RADICAL C-SIMPLIFIED SEE;So;0;ON;;;;;N;;;;;
+2EC6;CJK RADICAL SIMPLIFIED HORN;So;0;ON;;;;;N;;;;;
+2EC7;CJK RADICAL HORN;So;0;ON;;;;;N;;;;;
+2EC8;CJK RADICAL C-SIMPLIFIED SPEECH;So;0;ON;;;;;N;;;;;
+2EC9;CJK RADICAL C-SIMPLIFIED SHELL;So;0;ON;;;;;N;;;;;
+2ECA;CJK RADICAL FOOT;So;0;ON;;;;;N;;;;;
+2ECB;CJK RADICAL C-SIMPLIFIED CART;So;0;ON;;;;;N;;;;;
+2ECC;CJK RADICAL SIMPLIFIED WALK;So;0;ON;;;;;N;;;;;
+2ECD;CJK RADICAL WALK ONE;So;0;ON;;;;;N;;;;;
+2ECE;CJK RADICAL WALK TWO;So;0;ON;;;;;N;;;;;
+2ECF;CJK RADICAL CITY;So;0;ON;;;;;N;;;;;
+2ED0;CJK RADICAL C-SIMPLIFIED GOLD;So;0;ON;;;;;N;;;;;
+2ED1;CJK RADICAL LONG ONE;So;0;ON;;;;;N;;;;;
+2ED2;CJK RADICAL LONG TWO;So;0;ON;;;;;N;;;;;
+2ED3;CJK RADICAL C-SIMPLIFIED LONG;So;0;ON;;;;;N;;;;;
+2ED4;CJK RADICAL C-SIMPLIFIED GATE;So;0;ON;;;;;N;;;;;
+2ED5;CJK RADICAL MOUND ONE;So;0;ON;;;;;N;;;;;
+2ED6;CJK RADICAL MOUND TWO;So;0;ON;;;;;N;;;;;
+2ED7;CJK RADICAL RAIN;So;0;ON;;;;;N;;;;;
+2ED8;CJK RADICAL BLUE;So;0;ON;;;;;N;;;;;
+2ED9;CJK RADICAL C-SIMPLIFIED TANNED LEATHER;So;0;ON;;;;;N;;;;;
+2EDA;CJK RADICAL C-SIMPLIFIED LEAF;So;0;ON;;;;;N;;;;;
+2EDB;CJK RADICAL C-SIMPLIFIED WIND;So;0;ON;;;;;N;;;;;
+2EDC;CJK RADICAL C-SIMPLIFIED FLY;So;0;ON;;;;;N;;;;;
+2EDD;CJK RADICAL EAT ONE;So;0;ON;;;;;N;;;;;
+2EDE;CJK RADICAL EAT TWO;So;0;ON;;;;;N;;;;;
+2EDF;CJK RADICAL EAT THREE;So;0;ON;;;;;N;;;;;
+2EE0;CJK RADICAL C-SIMPLIFIED EAT;So;0;ON;;;;;N;;;;;
+2EE1;CJK RADICAL HEAD;So;0;ON;;;;;N;;;;;
+2EE2;CJK RADICAL C-SIMPLIFIED HORSE;So;0;ON;;;;;N;;;;;
+2EE3;CJK RADICAL BONE;So;0;ON;;;;;N;;;;;
+2EE4;CJK RADICAL GHOST;So;0;ON;;;;;N;;;;;
+2EE5;CJK RADICAL C-SIMPLIFIED FISH;So;0;ON;;;;;N;;;;;
+2EE6;CJK RADICAL C-SIMPLIFIED BIRD;So;0;ON;;;;;N;;;;;
+2EE7;CJK RADICAL C-SIMPLIFIED SALT;So;0;ON;;;;;N;;;;;
+2EE8;CJK RADICAL SIMPLIFIED WHEAT;So;0;ON;;;;;N;;;;;
+2EE9;CJK RADICAL SIMPLIFIED YELLOW;So;0;ON;;;;;N;;;;;
+2EEA;CJK RADICAL C-SIMPLIFIED FROG;So;0;ON;;;;;N;;;;;
+2EEB;CJK RADICAL J-SIMPLIFIED EVEN;So;0;ON;;;;;N;;;;;
+2EEC;CJK RADICAL C-SIMPLIFIED EVEN;So;0;ON;;;;;N;;;;;
+2EED;CJK RADICAL J-SIMPLIFIED TOOTH;So;0;ON;;;;;N;;;;;
+2EEE;CJK RADICAL C-SIMPLIFIED TOOTH;So;0;ON;;;;;N;;;;;
+2EEF;CJK RADICAL J-SIMPLIFIED DRAGON;So;0;ON;;;;;N;;;;;
+2EF0;CJK RADICAL C-SIMPLIFIED DRAGON;So;0;ON;;;;;N;;;;;
+2EF1;CJK RADICAL TURTLE;So;0;ON;;;;;N;;;;;
+2EF2;CJK RADICAL J-SIMPLIFIED TURTLE;So;0;ON;;;;;N;;;;;
+2EF3;CJK RADICAL C-SIMPLIFIED TURTLE;So;0;ON;<compat> 9F9F;;;;N;;;;;
+2F00;KANGXI RADICAL ONE;So;0;ON;<compat> 4E00;;;;N;;;;;
+2F01;KANGXI RADICAL LINE;So;0;ON;<compat> 4E28;;;;N;;;;;
+2F02;KANGXI RADICAL DOT;So;0;ON;<compat> 4E36;;;;N;;;;;
+2F03;KANGXI RADICAL SLASH;So;0;ON;<compat> 4E3F;;;;N;;;;;
+2F04;KANGXI RADICAL SECOND;So;0;ON;<compat> 4E59;;;;N;;;;;
+2F05;KANGXI RADICAL HOOK;So;0;ON;<compat> 4E85;;;;N;;;;;
+2F06;KANGXI RADICAL TWO;So;0;ON;<compat> 4E8C;;;;N;;;;;
+2F07;KANGXI RADICAL LID;So;0;ON;<compat> 4EA0;;;;N;;;;;
+2F08;KANGXI RADICAL MAN;So;0;ON;<compat> 4EBA;;;;N;;;;;
+2F09;KANGXI RADICAL LEGS;So;0;ON;<compat> 513F;;;;N;;;;;
+2F0A;KANGXI RADICAL ENTER;So;0;ON;<compat> 5165;;;;N;;;;;
+2F0B;KANGXI RADICAL EIGHT;So;0;ON;<compat> 516B;;;;N;;;;;
+2F0C;KANGXI RADICAL DOWN BOX;So;0;ON;<compat> 5182;;;;N;;;;;
+2F0D;KANGXI RADICAL COVER;So;0;ON;<compat> 5196;;;;N;;;;;
+2F0E;KANGXI RADICAL ICE;So;0;ON;<compat> 51AB;;;;N;;;;;
+2F0F;KANGXI RADICAL TABLE;So;0;ON;<compat> 51E0;;;;N;;;;;
+2F10;KANGXI RADICAL OPEN BOX;So;0;ON;<compat> 51F5;;;;N;;;;;
+2F11;KANGXI RADICAL KNIFE;So;0;ON;<compat> 5200;;;;N;;;;;
+2F12;KANGXI RADICAL POWER;So;0;ON;<compat> 529B;;;;N;;;;;
+2F13;KANGXI RADICAL WRAP;So;0;ON;<compat> 52F9;;;;N;;;;;
+2F14;KANGXI RADICAL SPOON;So;0;ON;<compat> 5315;;;;N;;;;;
+2F15;KANGXI RADICAL RIGHT OPEN BOX;So;0;ON;<compat> 531A;;;;N;;;;;
+2F16;KANGXI RADICAL HIDING ENCLOSURE;So;0;ON;<compat> 5338;;;;N;;;;;
+2F17;KANGXI RADICAL TEN;So;0;ON;<compat> 5341;;;;N;;;;;
+2F18;KANGXI RADICAL DIVINATION;So;0;ON;<compat> 535C;;;;N;;;;;
+2F19;KANGXI RADICAL SEAL;So;0;ON;<compat> 5369;;;;N;;;;;
+2F1A;KANGXI RADICAL CLIFF;So;0;ON;<compat> 5382;;;;N;;;;;
+2F1B;KANGXI RADICAL PRIVATE;So;0;ON;<compat> 53B6;;;;N;;;;;
+2F1C;KANGXI RADICAL AGAIN;So;0;ON;<compat> 53C8;;;;N;;;;;
+2F1D;KANGXI RADICAL MOUTH;So;0;ON;<compat> 53E3;;;;N;;;;;
+2F1E;KANGXI RADICAL ENCLOSURE;So;0;ON;<compat> 56D7;;;;N;;;;;
+2F1F;KANGXI RADICAL EARTH;So;0;ON;<compat> 571F;;;;N;;;;;
+2F20;KANGXI RADICAL SCHOLAR;So;0;ON;<compat> 58EB;;;;N;;;;;
+2F21;KANGXI RADICAL GO;So;0;ON;<compat> 5902;;;;N;;;;;
+2F22;KANGXI RADICAL GO SLOWLY;So;0;ON;<compat> 590A;;;;N;;;;;
+2F23;KANGXI RADICAL EVENING;So;0;ON;<compat> 5915;;;;N;;;;;
+2F24;KANGXI RADICAL BIG;So;0;ON;<compat> 5927;;;;N;;;;;
+2F25;KANGXI RADICAL WOMAN;So;0;ON;<compat> 5973;;;;N;;;;;
+2F26;KANGXI RADICAL CHILD;So;0;ON;<compat> 5B50;;;;N;;;;;
+2F27;KANGXI RADICAL ROOF;So;0;ON;<compat> 5B80;;;;N;;;;;
+2F28;KANGXI RADICAL INCH;So;0;ON;<compat> 5BF8;;;;N;;;;;
+2F29;KANGXI RADICAL SMALL;So;0;ON;<compat> 5C0F;;;;N;;;;;
+2F2A;KANGXI RADICAL LAME;So;0;ON;<compat> 5C22;;;;N;;;;;
+2F2B;KANGXI RADICAL CORPSE;So;0;ON;<compat> 5C38;;;;N;;;;;
+2F2C;KANGXI RADICAL SPROUT;So;0;ON;<compat> 5C6E;;;;N;;;;;
+2F2D;KANGXI RADICAL MOUNTAIN;So;0;ON;<compat> 5C71;;;;N;;;;;
+2F2E;KANGXI RADICAL RIVER;So;0;ON;<compat> 5DDB;;;;N;;;;;
+2F2F;KANGXI RADICAL WORK;So;0;ON;<compat> 5DE5;;;;N;;;;;
+2F30;KANGXI RADICAL ONESELF;So;0;ON;<compat> 5DF1;;;;N;;;;;
+2F31;KANGXI RADICAL TURBAN;So;0;ON;<compat> 5DFE;;;;N;;;;;
+2F32;KANGXI RADICAL DRY;So;0;ON;<compat> 5E72;;;;N;;;;;
+2F33;KANGXI RADICAL SHORT THREAD;So;0;ON;<compat> 5E7A;;;;N;;;;;
+2F34;KANGXI RADICAL DOTTED CLIFF;So;0;ON;<compat> 5E7F;;;;N;;;;;
+2F35;KANGXI RADICAL LONG STRIDE;So;0;ON;<compat> 5EF4;;;;N;;;;;
+2F36;KANGXI RADICAL TWO HANDS;So;0;ON;<compat> 5EFE;;;;N;;;;;
+2F37;KANGXI RADICAL SHOOT;So;0;ON;<compat> 5F0B;;;;N;;;;;
+2F38;KANGXI RADICAL BOW;So;0;ON;<compat> 5F13;;;;N;;;;;
+2F39;KANGXI RADICAL SNOUT;So;0;ON;<compat> 5F50;;;;N;;;;;
+2F3A;KANGXI RADICAL BRISTLE;So;0;ON;<compat> 5F61;;;;N;;;;;
+2F3B;KANGXI RADICAL STEP;So;0;ON;<compat> 5F73;;;;N;;;;;
+2F3C;KANGXI RADICAL HEART;So;0;ON;<compat> 5FC3;;;;N;;;;;
+2F3D;KANGXI RADICAL HALBERD;So;0;ON;<compat> 6208;;;;N;;;;;
+2F3E;KANGXI RADICAL DOOR;So;0;ON;<compat> 6236;;;;N;;;;;
+2F3F;KANGXI RADICAL HAND;So;0;ON;<compat> 624B;;;;N;;;;;
+2F40;KANGXI RADICAL BRANCH;So;0;ON;<compat> 652F;;;;N;;;;;
+2F41;KANGXI RADICAL RAP;So;0;ON;<compat> 6534;;;;N;;;;;
+2F42;KANGXI RADICAL SCRIPT;So;0;ON;<compat> 6587;;;;N;;;;;
+2F43;KANGXI RADICAL DIPPER;So;0;ON;<compat> 6597;;;;N;;;;;
+2F44;KANGXI RADICAL AXE;So;0;ON;<compat> 65A4;;;;N;;;;;
+2F45;KANGXI RADICAL SQUARE;So;0;ON;<compat> 65B9;;;;N;;;;;
+2F46;KANGXI RADICAL NOT;So;0;ON;<compat> 65E0;;;;N;;;;;
+2F47;KANGXI RADICAL SUN;So;0;ON;<compat> 65E5;;;;N;;;;;
+2F48;KANGXI RADICAL SAY;So;0;ON;<compat> 66F0;;;;N;;;;;
+2F49;KANGXI RADICAL MOON;So;0;ON;<compat> 6708;;;;N;;;;;
+2F4A;KANGXI RADICAL TREE;So;0;ON;<compat> 6728;;;;N;;;;;
+2F4B;KANGXI RADICAL LACK;So;0;ON;<compat> 6B20;;;;N;;;;;
+2F4C;KANGXI RADICAL STOP;So;0;ON;<compat> 6B62;;;;N;;;;;
+2F4D;KANGXI RADICAL DEATH;So;0;ON;<compat> 6B79;;;;N;;;;;
+2F4E;KANGXI RADICAL WEAPON;So;0;ON;<compat> 6BB3;;;;N;;;;;
+2F4F;KANGXI RADICAL DO NOT;So;0;ON;<compat> 6BCB;;;;N;;;;;
+2F50;KANGXI RADICAL COMPARE;So;0;ON;<compat> 6BD4;;;;N;;;;;
+2F51;KANGXI RADICAL FUR;So;0;ON;<compat> 6BDB;;;;N;;;;;
+2F52;KANGXI RADICAL CLAN;So;0;ON;<compat> 6C0F;;;;N;;;;;
+2F53;KANGXI RADICAL STEAM;So;0;ON;<compat> 6C14;;;;N;;;;;
+2F54;KANGXI RADICAL WATER;So;0;ON;<compat> 6C34;;;;N;;;;;
+2F55;KANGXI RADICAL FIRE;So;0;ON;<compat> 706B;;;;N;;;;;
+2F56;KANGXI RADICAL CLAW;So;0;ON;<compat> 722A;;;;N;;;;;
+2F57;KANGXI RADICAL FATHER;So;0;ON;<compat> 7236;;;;N;;;;;
+2F58;KANGXI RADICAL DOUBLE X;So;0;ON;<compat> 723B;;;;N;;;;;
+2F59;KANGXI RADICAL HALF TREE TRUNK;So;0;ON;<compat> 723F;;;;N;;;;;
+2F5A;KANGXI RADICAL SLICE;So;0;ON;<compat> 7247;;;;N;;;;;
+2F5B;KANGXI RADICAL FANG;So;0;ON;<compat> 7259;;;;N;;;;;
+2F5C;KANGXI RADICAL COW;So;0;ON;<compat> 725B;;;;N;;;;;
+2F5D;KANGXI RADICAL DOG;So;0;ON;<compat> 72AC;;;;N;;;;;
+2F5E;KANGXI RADICAL PROFOUND;So;0;ON;<compat> 7384;;;;N;;;;;
+2F5F;KANGXI RADICAL JADE;So;0;ON;<compat> 7389;;;;N;;;;;
+2F60;KANGXI RADICAL MELON;So;0;ON;<compat> 74DC;;;;N;;;;;
+2F61;KANGXI RADICAL TILE;So;0;ON;<compat> 74E6;;;;N;;;;;
+2F62;KANGXI RADICAL SWEET;So;0;ON;<compat> 7518;;;;N;;;;;
+2F63;KANGXI RADICAL LIFE;So;0;ON;<compat> 751F;;;;N;;;;;
+2F64;KANGXI RADICAL USE;So;0;ON;<compat> 7528;;;;N;;;;;
+2F65;KANGXI RADICAL FIELD;So;0;ON;<compat> 7530;;;;N;;;;;
+2F66;KANGXI RADICAL BOLT OF CLOTH;So;0;ON;<compat> 758B;;;;N;;;;;
+2F67;KANGXI RADICAL SICKNESS;So;0;ON;<compat> 7592;;;;N;;;;;
+2F68;KANGXI RADICAL DOTTED TENT;So;0;ON;<compat> 7676;;;;N;;;;;
+2F69;KANGXI RADICAL WHITE;So;0;ON;<compat> 767D;;;;N;;;;;
+2F6A;KANGXI RADICAL SKIN;So;0;ON;<compat> 76AE;;;;N;;;;;
+2F6B;KANGXI RADICAL DISH;So;0;ON;<compat> 76BF;;;;N;;;;;
+2F6C;KANGXI RADICAL EYE;So;0;ON;<compat> 76EE;;;;N;;;;;
+2F6D;KANGXI RADICAL SPEAR;So;0;ON;<compat> 77DB;;;;N;;;;;
+2F6E;KANGXI RADICAL ARROW;So;0;ON;<compat> 77E2;;;;N;;;;;
+2F6F;KANGXI RADICAL STONE;So;0;ON;<compat> 77F3;;;;N;;;;;
+2F70;KANGXI RADICAL SPIRIT;So;0;ON;<compat> 793A;;;;N;;;;;
+2F71;KANGXI RADICAL TRACK;So;0;ON;<compat> 79B8;;;;N;;;;;
+2F72;KANGXI RADICAL GRAIN;So;0;ON;<compat> 79BE;;;;N;;;;;
+2F73;KANGXI RADICAL CAVE;So;0;ON;<compat> 7A74;;;;N;;;;;
+2F74;KANGXI RADICAL STAND;So;0;ON;<compat> 7ACB;;;;N;;;;;
+2F75;KANGXI RADICAL BAMBOO;So;0;ON;<compat> 7AF9;;;;N;;;;;
+2F76;KANGXI RADICAL RICE;So;0;ON;<compat> 7C73;;;;N;;;;;
+2F77;KANGXI RADICAL SILK;So;0;ON;<compat> 7CF8;;;;N;;;;;
+2F78;KANGXI RADICAL JAR;So;0;ON;<compat> 7F36;;;;N;;;;;
+2F79;KANGXI RADICAL NET;So;0;ON;<compat> 7F51;;;;N;;;;;
+2F7A;KANGXI RADICAL SHEEP;So;0;ON;<compat> 7F8A;;;;N;;;;;
+2F7B;KANGXI RADICAL FEATHER;So;0;ON;<compat> 7FBD;;;;N;;;;;
+2F7C;KANGXI RADICAL OLD;So;0;ON;<compat> 8001;;;;N;;;;;
+2F7D;KANGXI RADICAL AND;So;0;ON;<compat> 800C;;;;N;;;;;
+2F7E;KANGXI RADICAL PLOW;So;0;ON;<compat> 8012;;;;N;;;;;
+2F7F;KANGXI RADICAL EAR;So;0;ON;<compat> 8033;;;;N;;;;;
+2F80;KANGXI RADICAL BRUSH;So;0;ON;<compat> 807F;;;;N;;;;;
+2F81;KANGXI RADICAL MEAT;So;0;ON;<compat> 8089;;;;N;;;;;
+2F82;KANGXI RADICAL MINISTER;So;0;ON;<compat> 81E3;;;;N;;;;;
+2F83;KANGXI RADICAL SELF;So;0;ON;<compat> 81EA;;;;N;;;;;
+2F84;KANGXI RADICAL ARRIVE;So;0;ON;<compat> 81F3;;;;N;;;;;
+2F85;KANGXI RADICAL MORTAR;So;0;ON;<compat> 81FC;;;;N;;;;;
+2F86;KANGXI RADICAL TONGUE;So;0;ON;<compat> 820C;;;;N;;;;;
+2F87;KANGXI RADICAL OPPOSE;So;0;ON;<compat> 821B;;;;N;;;;;
+2F88;KANGXI RADICAL BOAT;So;0;ON;<compat> 821F;;;;N;;;;;
+2F89;KANGXI RADICAL STOPPING;So;0;ON;<compat> 826E;;;;N;;;;;
+2F8A;KANGXI RADICAL COLOR;So;0;ON;<compat> 8272;;;;N;;;;;
+2F8B;KANGXI RADICAL GRASS;So;0;ON;<compat> 8278;;;;N;;;;;
+2F8C;KANGXI RADICAL TIGER;So;0;ON;<compat> 864D;;;;N;;;;;
+2F8D;KANGXI RADICAL INSECT;So;0;ON;<compat> 866B;;;;N;;;;;
+2F8E;KANGXI RADICAL BLOOD;So;0;ON;<compat> 8840;;;;N;;;;;
+2F8F;KANGXI RADICAL WALK ENCLOSURE;So;0;ON;<compat> 884C;;;;N;;;;;
+2F90;KANGXI RADICAL CLOTHES;So;0;ON;<compat> 8863;;;;N;;;;;
+2F91;KANGXI RADICAL WEST;So;0;ON;<compat> 897E;;;;N;;;;;
+2F92;KANGXI RADICAL SEE;So;0;ON;<compat> 898B;;;;N;;;;;
+2F93;KANGXI RADICAL HORN;So;0;ON;<compat> 89D2;;;;N;;;;;
+2F94;KANGXI RADICAL SPEECH;So;0;ON;<compat> 8A00;;;;N;;;;;
+2F95;KANGXI RADICAL VALLEY;So;0;ON;<compat> 8C37;;;;N;;;;;
+2F96;KANGXI RADICAL BEAN;So;0;ON;<compat> 8C46;;;;N;;;;;
+2F97;KANGXI RADICAL PIG;So;0;ON;<compat> 8C55;;;;N;;;;;
+2F98;KANGXI RADICAL BADGER;So;0;ON;<compat> 8C78;;;;N;;;;;
+2F99;KANGXI RADICAL SHELL;So;0;ON;<compat> 8C9D;;;;N;;;;;
+2F9A;KANGXI RADICAL RED;So;0;ON;<compat> 8D64;;;;N;;;;;
+2F9B;KANGXI RADICAL RUN;So;0;ON;<compat> 8D70;;;;N;;;;;
+2F9C;KANGXI RADICAL FOOT;So;0;ON;<compat> 8DB3;;;;N;;;;;
+2F9D;KANGXI RADICAL BODY;So;0;ON;<compat> 8EAB;;;;N;;;;;
+2F9E;KANGXI RADICAL CART;So;0;ON;<compat> 8ECA;;;;N;;;;;
+2F9F;KANGXI RADICAL BITTER;So;0;ON;<compat> 8F9B;;;;N;;;;;
+2FA0;KANGXI RADICAL MORNING;So;0;ON;<compat> 8FB0;;;;N;;;;;
+2FA1;KANGXI RADICAL WALK;So;0;ON;<compat> 8FB5;;;;N;;;;;
+2FA2;KANGXI RADICAL CITY;So;0;ON;<compat> 9091;;;;N;;;;;
+2FA3;KANGXI RADICAL WINE;So;0;ON;<compat> 9149;;;;N;;;;;
+2FA4;KANGXI RADICAL DISTINGUISH;So;0;ON;<compat> 91C6;;;;N;;;;;
+2FA5;KANGXI RADICAL VILLAGE;So;0;ON;<compat> 91CC;;;;N;;;;;
+2FA6;KANGXI RADICAL GOLD;So;0;ON;<compat> 91D1;;;;N;;;;;
+2FA7;KANGXI RADICAL LONG;So;0;ON;<compat> 9577;;;;N;;;;;
+2FA8;KANGXI RADICAL GATE;So;0;ON;<compat> 9580;;;;N;;;;;
+2FA9;KANGXI RADICAL MOUND;So;0;ON;<compat> 961C;;;;N;;;;;
+2FAA;KANGXI RADICAL SLAVE;So;0;ON;<compat> 96B6;;;;N;;;;;
+2FAB;KANGXI RADICAL SHORT TAILED BIRD;So;0;ON;<compat> 96B9;;;;N;;;;;
+2FAC;KANGXI RADICAL RAIN;So;0;ON;<compat> 96E8;;;;N;;;;;
+2FAD;KANGXI RADICAL BLUE;So;0;ON;<compat> 9751;;;;N;;;;;
+2FAE;KANGXI RADICAL WRONG;So;0;ON;<compat> 975E;;;;N;;;;;
+2FAF;KANGXI RADICAL FACE;So;0;ON;<compat> 9762;;;;N;;;;;
+2FB0;KANGXI RADICAL LEATHER;So;0;ON;<compat> 9769;;;;N;;;;;
+2FB1;KANGXI RADICAL TANNED LEATHER;So;0;ON;<compat> 97CB;;;;N;;;;;
+2FB2;KANGXI RADICAL LEEK;So;0;ON;<compat> 97ED;;;;N;;;;;
+2FB3;KANGXI RADICAL SOUND;So;0;ON;<compat> 97F3;;;;N;;;;;
+2FB4;KANGXI RADICAL LEAF;So;0;ON;<compat> 9801;;;;N;;;;;
+2FB5;KANGXI RADICAL WIND;So;0;ON;<compat> 98A8;;;;N;;;;;
+2FB6;KANGXI RADICAL FLY;So;0;ON;<compat> 98DB;;;;N;;;;;
+2FB7;KANGXI RADICAL EAT;So;0;ON;<compat> 98DF;;;;N;;;;;
+2FB8;KANGXI RADICAL HEAD;So;0;ON;<compat> 9996;;;;N;;;;;
+2FB9;KANGXI RADICAL FRAGRANT;So;0;ON;<compat> 9999;;;;N;;;;;
+2FBA;KANGXI RADICAL HORSE;So;0;ON;<compat> 99AC;;;;N;;;;;
+2FBB;KANGXI RADICAL BONE;So;0;ON;<compat> 9AA8;;;;N;;;;;
+2FBC;KANGXI RADICAL TALL;So;0;ON;<compat> 9AD8;;;;N;;;;;
+2FBD;KANGXI RADICAL HAIR;So;0;ON;<compat> 9ADF;;;;N;;;;;
+2FBE;KANGXI RADICAL FIGHT;So;0;ON;<compat> 9B25;;;;N;;;;;
+2FBF;KANGXI RADICAL SACRIFICIAL WINE;So;0;ON;<compat> 9B2F;;;;N;;;;;
+2FC0;KANGXI RADICAL CAULDRON;So;0;ON;<compat> 9B32;;;;N;;;;;
+2FC1;KANGXI RADICAL GHOST;So;0;ON;<compat> 9B3C;;;;N;;;;;
+2FC2;KANGXI RADICAL FISH;So;0;ON;<compat> 9B5A;;;;N;;;;;
+2FC3;KANGXI RADICAL BIRD;So;0;ON;<compat> 9CE5;;;;N;;;;;
+2FC4;KANGXI RADICAL SALT;So;0;ON;<compat> 9E75;;;;N;;;;;
+2FC5;KANGXI RADICAL DEER;So;0;ON;<compat> 9E7F;;;;N;;;;;
+2FC6;KANGXI RADICAL WHEAT;So;0;ON;<compat> 9EA5;;;;N;;;;;
+2FC7;KANGXI RADICAL HEMP;So;0;ON;<compat> 9EBB;;;;N;;;;;
+2FC8;KANGXI RADICAL YELLOW;So;0;ON;<compat> 9EC3;;;;N;;;;;
+2FC9;KANGXI RADICAL MILLET;So;0;ON;<compat> 9ECD;;;;N;;;;;
+2FCA;KANGXI RADICAL BLACK;So;0;ON;<compat> 9ED1;;;;N;;;;;
+2FCB;KANGXI RADICAL EMBROIDERY;So;0;ON;<compat> 9EF9;;;;N;;;;;
+2FCC;KANGXI RADICAL FROG;So;0;ON;<compat> 9EFD;;;;N;;;;;
+2FCD;KANGXI RADICAL TRIPOD;So;0;ON;<compat> 9F0E;;;;N;;;;;
+2FCE;KANGXI RADICAL DRUM;So;0;ON;<compat> 9F13;;;;N;;;;;
+2FCF;KANGXI RADICAL RAT;So;0;ON;<compat> 9F20;;;;N;;;;;
+2FD0;KANGXI RADICAL NOSE;So;0;ON;<compat> 9F3B;;;;N;;;;;
+2FD1;KANGXI RADICAL EVEN;So;0;ON;<compat> 9F4A;;;;N;;;;;
+2FD2;KANGXI RADICAL TOOTH;So;0;ON;<compat> 9F52;;;;N;;;;;
+2FD3;KANGXI RADICAL DRAGON;So;0;ON;<compat> 9F8D;;;;N;;;;;
+2FD4;KANGXI RADICAL TURTLE;So;0;ON;<compat> 9F9C;;;;N;;;;;
+2FD5;KANGXI RADICAL FLUTE;So;0;ON;<compat> 9FA0;;;;N;;;;;
+2FF0;IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT;So;0;ON;;;;;N;;;;;
+2FF1;IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE TO BELOW;So;0;ON;;;;;N;;;;;
+2FF2;IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO MIDDLE AND RIGHT;So;0;ON;;;;;N;;;;;
+2FF3;IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE TO MIDDLE AND BELOW;So;0;ON;;;;;N;;;;;
+2FF4;IDEOGRAPHIC DESCRIPTION CHARACTER FULL SURROUND;So;0;ON;;;;;N;;;;;
+2FF5;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM ABOVE;So;0;ON;;;;;N;;;;;
+2FF6;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM BELOW;So;0;ON;;;;;N;;;;;
+2FF7;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM LEFT;So;0;ON;;;;;N;;;;;
+2FF8;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM UPPER LEFT;So;0;ON;;;;;N;;;;;
+2FF9;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM UPPER RIGHT;So;0;ON;;;;;N;;;;;
+2FFA;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM LOWER LEFT;So;0;ON;;;;;N;;;;;
+2FFB;IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID;So;0;ON;;;;;N;;;;;
+3000;IDEOGRAPHIC SPACE;Zs;0;WS;<wide> 0020;;;;N;;;;;
+3001;IDEOGRAPHIC COMMA;Po;0;ON;;;;;N;;;;;
+3002;IDEOGRAPHIC FULL STOP;Po;0;ON;;;;;N;IDEOGRAPHIC PERIOD;;;;
+3003;DITTO MARK;Po;0;ON;;;;;N;;;;;
+3004;JAPANESE INDUSTRIAL STANDARD SYMBOL;So;0;ON;;;;;N;;;;;
+3005;IDEOGRAPHIC ITERATION MARK;Lm;0;L;;;;;N;;;;;
+3006;IDEOGRAPHIC CLOSING MARK;Lo;0;L;;;;;N;;;;;
+3007;IDEOGRAPHIC NUMBER ZERO;Nl;0;L;;;;0;N;;;;;
+3008;LEFT ANGLE BRACKET;Ps;0;ON;;;;;Y;OPENING ANGLE BRACKET;;;;
+3009;RIGHT ANGLE BRACKET;Pe;0;ON;;;;;Y;CLOSING ANGLE BRACKET;;;;
+300A;LEFT DOUBLE ANGLE BRACKET;Ps;0;ON;;;;;Y;OPENING DOUBLE ANGLE BRACKET;;;;
+300B;RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON;;;;;Y;CLOSING DOUBLE ANGLE BRACKET;;;;
+300C;LEFT CORNER BRACKET;Ps;0;ON;;;;;Y;OPENING CORNER BRACKET;;;;
+300D;RIGHT CORNER BRACKET;Pe;0;ON;;;;;Y;CLOSING CORNER BRACKET;;;;
+300E;LEFT WHITE CORNER BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE CORNER BRACKET;;;;
+300F;RIGHT WHITE CORNER BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE CORNER BRACKET;;;;
+3010;LEFT BLACK LENTICULAR BRACKET;Ps;0;ON;;;;;Y;OPENING BLACK LENTICULAR BRACKET;;;;
+3011;RIGHT BLACK LENTICULAR BRACKET;Pe;0;ON;;;;;Y;CLOSING BLACK LENTICULAR BRACKET;;;;
+3012;POSTAL MARK;So;0;ON;;;;;N;;;;;
+3013;GETA MARK;So;0;ON;;;;;N;;;;;
+3014;LEFT TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;OPENING TORTOISE SHELL BRACKET;;;;
+3015;RIGHT TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;CLOSING TORTOISE SHELL BRACKET;;;;
+3016;LEFT WHITE LENTICULAR BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE LENTICULAR BRACKET;;;;
+3017;RIGHT WHITE LENTICULAR BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE LENTICULAR BRACKET;;;;
+3018;LEFT WHITE TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE TORTOISE SHELL BRACKET;;;;
+3019;RIGHT WHITE TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE TORTOISE SHELL BRACKET;;;;
+301A;LEFT WHITE SQUARE BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE SQUARE BRACKET;;;;
+301B;RIGHT WHITE SQUARE BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE SQUARE BRACKET;;;;
+301C;WAVE DASH;Pd;0;ON;;;;;N;;;;;
+301D;REVERSED DOUBLE PRIME QUOTATION MARK;Ps;0;ON;;;;;N;;;;;
+301E;DOUBLE PRIME QUOTATION MARK;Pe;0;ON;;;;;N;;;;;
+301F;LOW DOUBLE PRIME QUOTATION MARK;Pe;0;ON;;;;;N;;;;;
+3020;POSTAL MARK FACE;So;0;ON;;;;;N;;;;;
+3021;HANGZHOU NUMERAL ONE;Nl;0;L;;;;1;N;;;;;
+3022;HANGZHOU NUMERAL TWO;Nl;0;L;;;;2;N;;;;;
+3023;HANGZHOU NUMERAL THREE;Nl;0;L;;;;3;N;;;;;
+3024;HANGZHOU NUMERAL FOUR;Nl;0;L;;;;4;N;;;;;
+3025;HANGZHOU NUMERAL FIVE;Nl;0;L;;;;5;N;;;;;
+3026;HANGZHOU NUMERAL SIX;Nl;0;L;;;;6;N;;;;;
+3027;HANGZHOU NUMERAL SEVEN;Nl;0;L;;;;7;N;;;;;
+3028;HANGZHOU NUMERAL EIGHT;Nl;0;L;;;;8;N;;;;;
+3029;HANGZHOU NUMERAL NINE;Nl;0;L;;;;9;N;;;;;
+302A;IDEOGRAPHIC LEVEL TONE MARK;Mn;218;NSM;;;;;N;;;;;
+302B;IDEOGRAPHIC RISING TONE MARK;Mn;228;NSM;;;;;N;;;;;
+302C;IDEOGRAPHIC DEPARTING TONE MARK;Mn;232;NSM;;;;;N;;;;;
+302D;IDEOGRAPHIC ENTERING TONE MARK;Mn;222;NSM;;;;;N;;;;;
+302E;HANGUL SINGLE DOT TONE MARK;Mc;224;L;;;;;N;;;;;
+302F;HANGUL DOUBLE DOT TONE MARK;Mc;224;L;;;;;N;;;;;
+3030;WAVY DASH;Pd;0;ON;;;;;N;;;;;
+3031;VERTICAL KANA REPEAT MARK;Lm;0;L;;;;;N;;;;;
+3032;VERTICAL KANA REPEAT WITH VOICED SOUND MARK;Lm;0;L;;;;;N;;;;;
+3033;VERTICAL KANA REPEAT MARK UPPER HALF;Lm;0;L;;;;;N;;;;;
+3034;VERTICAL KANA REPEAT WITH VOICED SOUND MARK UPPER HALF;Lm;0;L;;;;;N;;;;;
+3035;VERTICAL KANA REPEAT MARK LOWER HALF;Lm;0;L;;;;;N;;;;;
+3036;CIRCLED POSTAL MARK;So;0;ON;<compat> 3012;;;;N;;;;;
+3037;IDEOGRAPHIC TELEGRAPH LINE FEED SEPARATOR SYMBOL;So;0;ON;;;;;N;;;;;
+3038;HANGZHOU NUMERAL TEN;Nl;0;L;<compat> 5341;;;10;N;;;;;
+3039;HANGZHOU NUMERAL TWENTY;Nl;0;L;<compat> 5344;;;20;N;;;;;
+303A;HANGZHOU NUMERAL THIRTY;Nl;0;L;<compat> 5345;;;30;N;;;;;
+303B;VERTICAL IDEOGRAPHIC ITERATION MARK;Lm;0;L;;;;;N;;;;;
+303C;MASU MARK;Lo;0;L;;;;;N;;;;;
+303D;PART ALTERNATION MARK;Po;0;ON;;;;;N;;;;;
+303E;IDEOGRAPHIC VARIATION INDICATOR;So;0;ON;;;;;N;;;;;
+303F;IDEOGRAPHIC HALF FILL SPACE;So;0;ON;;;;;N;;;;;
+3041;HIRAGANA LETTER SMALL A;Lo;0;L;;;;;N;;;;;
+3042;HIRAGANA LETTER A;Lo;0;L;;;;;N;;;;;
+3043;HIRAGANA LETTER SMALL I;Lo;0;L;;;;;N;;;;;
+3044;HIRAGANA LETTER I;Lo;0;L;;;;;N;;;;;
+3045;HIRAGANA LETTER SMALL U;Lo;0;L;;;;;N;;;;;
+3046;HIRAGANA LETTER U;Lo;0;L;;;;;N;;;;;
+3047;HIRAGANA LETTER SMALL E;Lo;0;L;;;;;N;;;;;
+3048;HIRAGANA LETTER E;Lo;0;L;;;;;N;;;;;
+3049;HIRAGANA LETTER SMALL O;Lo;0;L;;;;;N;;;;;
+304A;HIRAGANA LETTER O;Lo;0;L;;;;;N;;;;;
+304B;HIRAGANA LETTER KA;Lo;0;L;;;;;N;;;;;
+304C;HIRAGANA LETTER GA;Lo;0;L;304B 3099;;;;N;;;;;
+304D;HIRAGANA LETTER KI;Lo;0;L;;;;;N;;;;;
+304E;HIRAGANA LETTER GI;Lo;0;L;304D 3099;;;;N;;;;;
+304F;HIRAGANA LETTER KU;Lo;0;L;;;;;N;;;;;
+3050;HIRAGANA LETTER GU;Lo;0;L;304F 3099;;;;N;;;;;
+3051;HIRAGANA LETTER KE;Lo;0;L;;;;;N;;;;;
+3052;HIRAGANA LETTER GE;Lo;0;L;3051 3099;;;;N;;;;;
+3053;HIRAGANA LETTER KO;Lo;0;L;;;;;N;;;;;
+3054;HIRAGANA LETTER GO;Lo;0;L;3053 3099;;;;N;;;;;
+3055;HIRAGANA LETTER SA;Lo;0;L;;;;;N;;;;;
+3056;HIRAGANA LETTER ZA;Lo;0;L;3055 3099;;;;N;;;;;
+3057;HIRAGANA LETTER SI;Lo;0;L;;;;;N;;;;;
+3058;HIRAGANA LETTER ZI;Lo;0;L;3057 3099;;;;N;;;;;
+3059;HIRAGANA LETTER SU;Lo;0;L;;;;;N;;;;;
+305A;HIRAGANA LETTER ZU;Lo;0;L;3059 3099;;;;N;;;;;
+305B;HIRAGANA LETTER SE;Lo;0;L;;;;;N;;;;;
+305C;HIRAGANA LETTER ZE;Lo;0;L;305B 3099;;;;N;;;;;
+305D;HIRAGANA LETTER SO;Lo;0;L;;;;;N;;;;;
+305E;HIRAGANA LETTER ZO;Lo;0;L;305D 3099;;;;N;;;;;
+305F;HIRAGANA LETTER TA;Lo;0;L;;;;;N;;;;;
+3060;HIRAGANA LETTER DA;Lo;0;L;305F 3099;;;;N;;;;;
+3061;HIRAGANA LETTER TI;Lo;0;L;;;;;N;;;;;
+3062;HIRAGANA LETTER DI;Lo;0;L;3061 3099;;;;N;;;;;
+3063;HIRAGANA LETTER SMALL TU;Lo;0;L;;;;;N;;;;;
+3064;HIRAGANA LETTER TU;Lo;0;L;;;;;N;;;;;
+3065;HIRAGANA LETTER DU;Lo;0;L;3064 3099;;;;N;;;;;
+3066;HIRAGANA LETTER TE;Lo;0;L;;;;;N;;;;;
+3067;HIRAGANA LETTER DE;Lo;0;L;3066 3099;;;;N;;;;;
+3068;HIRAGANA LETTER TO;Lo;0;L;;;;;N;;;;;
+3069;HIRAGANA LETTER DO;Lo;0;L;3068 3099;;;;N;;;;;
+306A;HIRAGANA LETTER NA;Lo;0;L;;;;;N;;;;;
+306B;HIRAGANA LETTER NI;Lo;0;L;;;;;N;;;;;
+306C;HIRAGANA LETTER NU;Lo;0;L;;;;;N;;;;;
+306D;HIRAGANA LETTER NE;Lo;0;L;;;;;N;;;;;
+306E;HIRAGANA LETTER NO;Lo;0;L;;;;;N;;;;;
+306F;HIRAGANA LETTER HA;Lo;0;L;;;;;N;;;;;
+3070;HIRAGANA LETTER BA;Lo;0;L;306F 3099;;;;N;;;;;
+3071;HIRAGANA LETTER PA;Lo;0;L;306F 309A;;;;N;;;;;
+3072;HIRAGANA LETTER HI;Lo;0;L;;;;;N;;;;;
+3073;HIRAGANA LETTER BI;Lo;0;L;3072 3099;;;;N;;;;;
+3074;HIRAGANA LETTER PI;Lo;0;L;3072 309A;;;;N;;;;;
+3075;HIRAGANA LETTER HU;Lo;0;L;;;;;N;;;;;
+3076;HIRAGANA LETTER BU;Lo;0;L;3075 3099;;;;N;;;;;
+3077;HIRAGANA LETTER PU;Lo;0;L;3075 309A;;;;N;;;;;
+3078;HIRAGANA LETTER HE;Lo;0;L;;;;;N;;;;;
+3079;HIRAGANA LETTER BE;Lo;0;L;3078 3099;;;;N;;;;;
+307A;HIRAGANA LETTER PE;Lo;0;L;3078 309A;;;;N;;;;;
+307B;HIRAGANA LETTER HO;Lo;0;L;;;;;N;;;;;
+307C;HIRAGANA LETTER BO;Lo;0;L;307B 3099;;;;N;;;;;
+307D;HIRAGANA LETTER PO;Lo;0;L;307B 309A;;;;N;;;;;
+307E;HIRAGANA LETTER MA;Lo;0;L;;;;;N;;;;;
+307F;HIRAGANA LETTER MI;Lo;0;L;;;;;N;;;;;
+3080;HIRAGANA LETTER MU;Lo;0;L;;;;;N;;;;;
+3081;HIRAGANA LETTER ME;Lo;0;L;;;;;N;;;;;
+3082;HIRAGANA LETTER MO;Lo;0;L;;;;;N;;;;;
+3083;HIRAGANA LETTER SMALL YA;Lo;0;L;;;;;N;;;;;
+3084;HIRAGANA LETTER YA;Lo;0;L;;;;;N;;;;;
+3085;HIRAGANA LETTER SMALL YU;Lo;0;L;;;;;N;;;;;
+3086;HIRAGANA LETTER YU;Lo;0;L;;;;;N;;;;;
+3087;HIRAGANA LETTER SMALL YO;Lo;0;L;;;;;N;;;;;
+3088;HIRAGANA LETTER YO;Lo;0;L;;;;;N;;;;;
+3089;HIRAGANA LETTER RA;Lo;0;L;;;;;N;;;;;
+308A;HIRAGANA LETTER RI;Lo;0;L;;;;;N;;;;;
+308B;HIRAGANA LETTER RU;Lo;0;L;;;;;N;;;;;
+308C;HIRAGANA LETTER RE;Lo;0;L;;;;;N;;;;;
+308D;HIRAGANA LETTER RO;Lo;0;L;;;;;N;;;;;
+308E;HIRAGANA LETTER SMALL WA;Lo;0;L;;;;;N;;;;;
+308F;HIRAGANA LETTER WA;Lo;0;L;;;;;N;;;;;
+3090;HIRAGANA LETTER WI;Lo;0;L;;;;;N;;;;;
+3091;HIRAGANA LETTER WE;Lo;0;L;;;;;N;;;;;
+3092;HIRAGANA LETTER WO;Lo;0;L;;;;;N;;;;;
+3093;HIRAGANA LETTER N;Lo;0;L;;;;;N;;;;;
+3094;HIRAGANA LETTER VU;Lo;0;L;3046 3099;;;;N;;;;;
+3095;HIRAGANA LETTER SMALL KA;Lo;0;L;;;;;N;;;;;
+3096;HIRAGANA LETTER SMALL KE;Lo;0;L;;;;;N;;;;;
+3099;COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK;Mn;8;NSM;;;;;N;NON-SPACING KATAKANA-HIRAGANA VOICED SOUND MARK;;;;
+309A;COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;Mn;8;NSM;;;;;N;NON-SPACING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;;;;
+309B;KATAKANA-HIRAGANA VOICED SOUND MARK;Sk;0;ON;<compat> 0020 3099;;;;N;;;;;
+309C;KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;Sk;0;ON;<compat> 0020 309A;;;;N;;;;;
+309D;HIRAGANA ITERATION MARK;Lm;0;L;;;;;N;;;;;
+309E;HIRAGANA VOICED ITERATION MARK;Lm;0;L;309D 3099;;;;N;;;;;
+309F;HIRAGANA DIGRAPH YORI;Lo;0;L;<vertical> 3088 308A;;;;N;;;;;
+30A0;KATAKANA-HIRAGANA DOUBLE HYPHEN;Pd;0;ON;;;;;N;;;;;
+30A1;KATAKANA LETTER SMALL A;Lo;0;L;;;;;N;;;;;
+30A2;KATAKANA LETTER A;Lo;0;L;;;;;N;;;;;
+30A3;KATAKANA LETTER SMALL I;Lo;0;L;;;;;N;;;;;
+30A4;KATAKANA LETTER I;Lo;0;L;;;;;N;;;;;
+30A5;KATAKANA LETTER SMALL U;Lo;0;L;;;;;N;;;;;
+30A6;KATAKANA LETTER U;Lo;0;L;;;;;N;;;;;
+30A7;KATAKANA LETTER SMALL E;Lo;0;L;;;;;N;;;;;
+30A8;KATAKANA LETTER E;Lo;0;L;;;;;N;;;;;
+30A9;KATAKANA LETTER SMALL O;Lo;0;L;;;;;N;;;;;
+30AA;KATAKANA LETTER O;Lo;0;L;;;;;N;;;;;
+30AB;KATAKANA LETTER KA;Lo;0;L;;;;;N;;;;;
+30AC;KATAKANA LETTER GA;Lo;0;L;30AB 3099;;;;N;;;;;
+30AD;KATAKANA LETTER KI;Lo;0;L;;;;;N;;;;;
+30AE;KATAKANA LETTER GI;Lo;0;L;30AD 3099;;;;N;;;;;
+30AF;KATAKANA LETTER KU;Lo;0;L;;;;;N;;;;;
+30B0;KATAKANA LETTER GU;Lo;0;L;30AF 3099;;;;N;;;;;
+30B1;KATAKANA LETTER KE;Lo;0;L;;;;;N;;;;;
+30B2;KATAKANA LETTER GE;Lo;0;L;30B1 3099;;;;N;;;;;
+30B3;KATAKANA LETTER KO;Lo;0;L;;;;;N;;;;;
+30B4;KATAKANA LETTER GO;Lo;0;L;30B3 3099;;;;N;;;;;
+30B5;KATAKANA LETTER SA;Lo;0;L;;;;;N;;;;;
+30B6;KATAKANA LETTER ZA;Lo;0;L;30B5 3099;;;;N;;;;;
+30B7;KATAKANA LETTER SI;Lo;0;L;;;;;N;;;;;
+30B8;KATAKANA LETTER ZI;Lo;0;L;30B7 3099;;;;N;;;;;
+30B9;KATAKANA LETTER SU;Lo;0;L;;;;;N;;;;;
+30BA;KATAKANA LETTER ZU;Lo;0;L;30B9 3099;;;;N;;;;;
+30BB;KATAKANA LETTER SE;Lo;0;L;;;;;N;;;;;
+30BC;KATAKANA LETTER ZE;Lo;0;L;30BB 3099;;;;N;;;;;
+30BD;KATAKANA LETTER SO;Lo;0;L;;;;;N;;;;;
+30BE;KATAKANA LETTER ZO;Lo;0;L;30BD 3099;;;;N;;;;;
+30BF;KATAKANA LETTER TA;Lo;0;L;;;;;N;;;;;
+30C0;KATAKANA LETTER DA;Lo;0;L;30BF 3099;;;;N;;;;;
+30C1;KATAKANA LETTER TI;Lo;0;L;;;;;N;;;;;
+30C2;KATAKANA LETTER DI;Lo;0;L;30C1 3099;;;;N;;;;;
+30C3;KATAKANA LETTER SMALL TU;Lo;0;L;;;;;N;;;;;
+30C4;KATAKANA LETTER TU;Lo;0;L;;;;;N;;;;;
+30C5;KATAKANA LETTER DU;Lo;0;L;30C4 3099;;;;N;;;;;
+30C6;KATAKANA LETTER TE;Lo;0;L;;;;;N;;;;;
+30C7;KATAKANA LETTER DE;Lo;0;L;30C6 3099;;;;N;;;;;
+30C8;KATAKANA LETTER TO;Lo;0;L;;;;;N;;;;;
+30C9;KATAKANA LETTER DO;Lo;0;L;30C8 3099;;;;N;;;;;
+30CA;KATAKANA LETTER NA;Lo;0;L;;;;;N;;;;;
+30CB;KATAKANA LETTER NI;Lo;0;L;;;;;N;;;;;
+30CC;KATAKANA LETTER NU;Lo;0;L;;;;;N;;;;;
+30CD;KATAKANA LETTER NE;Lo;0;L;;;;;N;;;;;
+30CE;KATAKANA LETTER NO;Lo;0;L;;;;;N;;;;;
+30CF;KATAKANA LETTER HA;Lo;0;L;;;;;N;;;;;
+30D0;KATAKANA LETTER BA;Lo;0;L;30CF 3099;;;;N;;;;;
+30D1;KATAKANA LETTER PA;Lo;0;L;30CF 309A;;;;N;;;;;
+30D2;KATAKANA LETTER HI;Lo;0;L;;;;;N;;;;;
+30D3;KATAKANA LETTER BI;Lo;0;L;30D2 3099;;;;N;;;;;
+30D4;KATAKANA LETTER PI;Lo;0;L;30D2 309A;;;;N;;;;;
+30D5;KATAKANA LETTER HU;Lo;0;L;;;;;N;;;;;
+30D6;KATAKANA LETTER BU;Lo;0;L;30D5 3099;;;;N;;;;;
+30D7;KATAKANA LETTER PU;Lo;0;L;30D5 309A;;;;N;;;;;
+30D8;KATAKANA LETTER HE;Lo;0;L;;;;;N;;;;;
+30D9;KATAKANA LETTER BE;Lo;0;L;30D8 3099;;;;N;;;;;
+30DA;KATAKANA LETTER PE;Lo;0;L;30D8 309A;;;;N;;;;;
+30DB;KATAKANA LETTER HO;Lo;0;L;;;;;N;;;;;
+30DC;KATAKANA LETTER BO;Lo;0;L;30DB 3099;;;;N;;;;;
+30DD;KATAKANA LETTER PO;Lo;0;L;30DB 309A;;;;N;;;;;
+30DE;KATAKANA LETTER MA;Lo;0;L;;;;;N;;;;;
+30DF;KATAKANA LETTER MI;Lo;0;L;;;;;N;;;;;
+30E0;KATAKANA LETTER MU;Lo;0;L;;;;;N;;;;;
+30E1;KATAKANA LETTER ME;Lo;0;L;;;;;N;;;;;
+30E2;KATAKANA LETTER MO;Lo;0;L;;;;;N;;;;;
+30E3;KATAKANA LETTER SMALL YA;Lo;0;L;;;;;N;;;;;
+30E4;KATAKANA LETTER YA;Lo;0;L;;;;;N;;;;;
+30E5;KATAKANA LETTER SMALL YU;Lo;0;L;;;;;N;;;;;
+30E6;KATAKANA LETTER YU;Lo;0;L;;;;;N;;;;;
+30E7;KATAKANA LETTER SMALL YO;Lo;0;L;;;;;N;;;;;
+30E8;KATAKANA LETTER YO;Lo;0;L;;;;;N;;;;;
+30E9;KATAKANA LETTER RA;Lo;0;L;;;;;N;;;;;
+30EA;KATAKANA LETTER RI;Lo;0;L;;;;;N;;;;;
+30EB;KATAKANA LETTER RU;Lo;0;L;;;;;N;;;;;
+30EC;KATAKANA LETTER RE;Lo;0;L;;;;;N;;;;;
+30ED;KATAKANA LETTER RO;Lo;0;L;;;;;N;;;;;
+30EE;KATAKANA LETTER SMALL WA;Lo;0;L;;;;;N;;;;;
+30EF;KATAKANA LETTER WA;Lo;0;L;;;;;N;;;;;
+30F0;KATAKANA LETTER WI;Lo;0;L;;;;;N;;;;;
+30F1;KATAKANA LETTER WE;Lo;0;L;;;;;N;;;;;
+30F2;KATAKANA LETTER WO;Lo;0;L;;;;;N;;;;;
+30F3;KATAKANA LETTER N;Lo;0;L;;;;;N;;;;;
+30F4;KATAKANA LETTER VU;Lo;0;L;30A6 3099;;;;N;;;;;
+30F5;KATAKANA LETTER SMALL KA;Lo;0;L;;;;;N;;;;;
+30F6;KATAKANA LETTER SMALL KE;Lo;0;L;;;;;N;;;;;
+30F7;KATAKANA LETTER VA;Lo;0;L;30EF 3099;;;;N;;;;;
+30F8;KATAKANA LETTER VI;Lo;0;L;30F0 3099;;;;N;;;;;
+30F9;KATAKANA LETTER VE;Lo;0;L;30F1 3099;;;;N;;;;;
+30FA;KATAKANA LETTER VO;Lo;0;L;30F2 3099;;;;N;;;;;
+30FB;KATAKANA MIDDLE DOT;Po;0;ON;;;;;N;;;;;
+30FC;KATAKANA-HIRAGANA PROLONGED SOUND MARK;Lm;0;L;;;;;N;;;;;
+30FD;KATAKANA ITERATION MARK;Lm;0;L;;;;;N;;;;;
+30FE;KATAKANA VOICED ITERATION MARK;Lm;0;L;30FD 3099;;;;N;;;;;
+30FF;KATAKANA DIGRAPH KOTO;Lo;0;L;<vertical> 30B3 30C8;;;;N;;;;;
+3105;BOPOMOFO LETTER B;Lo;0;L;;;;;N;;;;;
+3106;BOPOMOFO LETTER P;Lo;0;L;;;;;N;;;;;
+3107;BOPOMOFO LETTER M;Lo;0;L;;;;;N;;;;;
+3108;BOPOMOFO LETTER F;Lo;0;L;;;;;N;;;;;
+3109;BOPOMOFO LETTER D;Lo;0;L;;;;;N;;;;;
+310A;BOPOMOFO LETTER T;Lo;0;L;;;;;N;;;;;
+310B;BOPOMOFO LETTER N;Lo;0;L;;;;;N;;;;;
+310C;BOPOMOFO LETTER L;Lo;0;L;;;;;N;;;;;
+310D;BOPOMOFO LETTER G;Lo;0;L;;;;;N;;;;;
+310E;BOPOMOFO LETTER K;Lo;0;L;;;;;N;;;;;
+310F;BOPOMOFO LETTER H;Lo;0;L;;;;;N;;;;;
+3110;BOPOMOFO LETTER J;Lo;0;L;;;;;N;;;;;
+3111;BOPOMOFO LETTER Q;Lo;0;L;;;;;N;;;;;
+3112;BOPOMOFO LETTER X;Lo;0;L;;;;;N;;;;;
+3113;BOPOMOFO LETTER ZH;Lo;0;L;;;;;N;;;;;
+3114;BOPOMOFO LETTER CH;Lo;0;L;;;;;N;;;;;
+3115;BOPOMOFO LETTER SH;Lo;0;L;;;;;N;;;;;
+3116;BOPOMOFO LETTER R;Lo;0;L;;;;;N;;;;;
+3117;BOPOMOFO LETTER Z;Lo;0;L;;;;;N;;;;;
+3118;BOPOMOFO LETTER C;Lo;0;L;;;;;N;;;;;
+3119;BOPOMOFO LETTER S;Lo;0;L;;;;;N;;;;;
+311A;BOPOMOFO LETTER A;Lo;0;L;;;;;N;;;;;
+311B;BOPOMOFO LETTER O;Lo;0;L;;;;;N;;;;;
+311C;BOPOMOFO LETTER E;Lo;0;L;;;;;N;;;;;
+311D;BOPOMOFO LETTER EH;Lo;0;L;;;;;N;;;;;
+311E;BOPOMOFO LETTER AI;Lo;0;L;;;;;N;;;;;
+311F;BOPOMOFO LETTER EI;Lo;0;L;;;;;N;;;;;
+3120;BOPOMOFO LETTER AU;Lo;0;L;;;;;N;;;;;
+3121;BOPOMOFO LETTER OU;Lo;0;L;;;;;N;;;;;
+3122;BOPOMOFO LETTER AN;Lo;0;L;;;;;N;;;;;
+3123;BOPOMOFO LETTER EN;Lo;0;L;;;;;N;;;;;
+3124;BOPOMOFO LETTER ANG;Lo;0;L;;;;;N;;;;;
+3125;BOPOMOFO LETTER ENG;Lo;0;L;;;;;N;;;;;
+3126;BOPOMOFO LETTER ER;Lo;0;L;;;;;N;;;;;
+3127;BOPOMOFO LETTER I;Lo;0;L;;;;;N;;;;;
+3128;BOPOMOFO LETTER U;Lo;0;L;;;;;N;;;;;
+3129;BOPOMOFO LETTER IU;Lo;0;L;;;;;N;;;;;
+312A;BOPOMOFO LETTER V;Lo;0;L;;;;;N;;;;;
+312B;BOPOMOFO LETTER NG;Lo;0;L;;;;;N;;;;;
+312C;BOPOMOFO LETTER GN;Lo;0;L;;;;;N;;;;;
+312D;BOPOMOFO LETTER IH;Lo;0;L;;;;;N;;;;;
+312E;BOPOMOFO LETTER O WITH DOT ABOVE;Lo;0;L;;;;;N;;;;;
+312F;BOPOMOFO LETTER NN;Lo;0;L;;;;;N;;;;;
+3131;HANGUL LETTER KIYEOK;Lo;0;L;<compat> 1100;;;;N;HANGUL LETTER GIYEOG;;;;
+3132;HANGUL LETTER SSANGKIYEOK;Lo;0;L;<compat> 1101;;;;N;HANGUL LETTER SSANG GIYEOG;;;;
+3133;HANGUL LETTER KIYEOK-SIOS;Lo;0;L;<compat> 11AA;;;;N;HANGUL LETTER GIYEOG SIOS;;;;
+3134;HANGUL LETTER NIEUN;Lo;0;L;<compat> 1102;;;;N;;;;;
+3135;HANGUL LETTER NIEUN-CIEUC;Lo;0;L;<compat> 11AC;;;;N;HANGUL LETTER NIEUN JIEUJ;;;;
+3136;HANGUL LETTER NIEUN-HIEUH;Lo;0;L;<compat> 11AD;;;;N;HANGUL LETTER NIEUN HIEUH;;;;
+3137;HANGUL LETTER TIKEUT;Lo;0;L;<compat> 1103;;;;N;HANGUL LETTER DIGEUD;;;;
+3138;HANGUL LETTER SSANGTIKEUT;Lo;0;L;<compat> 1104;;;;N;HANGUL LETTER SSANG DIGEUD;;;;
+3139;HANGUL LETTER RIEUL;Lo;0;L;<compat> 1105;;;;N;HANGUL LETTER LIEUL;;;;
+313A;HANGUL LETTER RIEUL-KIYEOK;Lo;0;L;<compat> 11B0;;;;N;HANGUL LETTER LIEUL GIYEOG;;;;
+313B;HANGUL LETTER RIEUL-MIEUM;Lo;0;L;<compat> 11B1;;;;N;HANGUL LETTER LIEUL MIEUM;;;;
+313C;HANGUL LETTER RIEUL-PIEUP;Lo;0;L;<compat> 11B2;;;;N;HANGUL LETTER LIEUL BIEUB;;;;
+313D;HANGUL LETTER RIEUL-SIOS;Lo;0;L;<compat> 11B3;;;;N;HANGUL LETTER LIEUL SIOS;;;;
+313E;HANGUL LETTER RIEUL-THIEUTH;Lo;0;L;<compat> 11B4;;;;N;HANGUL LETTER LIEUL TIEUT;;;;
+313F;HANGUL LETTER RIEUL-PHIEUPH;Lo;0;L;<compat> 11B5;;;;N;HANGUL LETTER LIEUL PIEUP;;;;
+3140;HANGUL LETTER RIEUL-HIEUH;Lo;0;L;<compat> 111A;;;;N;HANGUL LETTER LIEUL HIEUH;;;;
+3141;HANGUL LETTER MIEUM;Lo;0;L;<compat> 1106;;;;N;;;;;
+3142;HANGUL LETTER PIEUP;Lo;0;L;<compat> 1107;;;;N;HANGUL LETTER BIEUB;;;;
+3143;HANGUL LETTER SSANGPIEUP;Lo;0;L;<compat> 1108;;;;N;HANGUL LETTER SSANG BIEUB;;;;
+3144;HANGUL LETTER PIEUP-SIOS;Lo;0;L;<compat> 1121;;;;N;HANGUL LETTER BIEUB SIOS;;;;
+3145;HANGUL LETTER SIOS;Lo;0;L;<compat> 1109;;;;N;;;;;
+3146;HANGUL LETTER SSANGSIOS;Lo;0;L;<compat> 110A;;;;N;HANGUL LETTER SSANG SIOS;;;;
+3147;HANGUL LETTER IEUNG;Lo;0;L;<compat> 110B;;;;N;;;;;
+3148;HANGUL LETTER CIEUC;Lo;0;L;<compat> 110C;;;;N;HANGUL LETTER JIEUJ;;;;
+3149;HANGUL LETTER SSANGCIEUC;Lo;0;L;<compat> 110D;;;;N;HANGUL LETTER SSANG JIEUJ;;;;
+314A;HANGUL LETTER CHIEUCH;Lo;0;L;<compat> 110E;;;;N;HANGUL LETTER CIEUC;;;;
+314B;HANGUL LETTER KHIEUKH;Lo;0;L;<compat> 110F;;;;N;HANGUL LETTER KIYEOK;;;;
+314C;HANGUL LETTER THIEUTH;Lo;0;L;<compat> 1110;;;;N;HANGUL LETTER TIEUT;;;;
+314D;HANGUL LETTER PHIEUPH;Lo;0;L;<compat> 1111;;;;N;HANGUL LETTER PIEUP;;;;
+314E;HANGUL LETTER HIEUH;Lo;0;L;<compat> 1112;;;;N;;;;;
+314F;HANGUL LETTER A;Lo;0;L;<compat> 1161;;;;N;;;;;
+3150;HANGUL LETTER AE;Lo;0;L;<compat> 1162;;;;N;;;;;
+3151;HANGUL LETTER YA;Lo;0;L;<compat> 1163;;;;N;;;;;
+3152;HANGUL LETTER YAE;Lo;0;L;<compat> 1164;;;;N;;;;;
+3153;HANGUL LETTER EO;Lo;0;L;<compat> 1165;;;;N;;;;;
+3154;HANGUL LETTER E;Lo;0;L;<compat> 1166;;;;N;;;;;
+3155;HANGUL LETTER YEO;Lo;0;L;<compat> 1167;;;;N;;;;;
+3156;HANGUL LETTER YE;Lo;0;L;<compat> 1168;;;;N;;;;;
+3157;HANGUL LETTER O;Lo;0;L;<compat> 1169;;;;N;;;;;
+3158;HANGUL LETTER WA;Lo;0;L;<compat> 116A;;;;N;;;;;
+3159;HANGUL LETTER WAE;Lo;0;L;<compat> 116B;;;;N;;;;;
+315A;HANGUL LETTER OE;Lo;0;L;<compat> 116C;;;;N;;;;;
+315B;HANGUL LETTER YO;Lo;0;L;<compat> 116D;;;;N;;;;;
+315C;HANGUL LETTER U;Lo;0;L;<compat> 116E;;;;N;;;;;
+315D;HANGUL LETTER WEO;Lo;0;L;<compat> 116F;;;;N;;;;;
+315E;HANGUL LETTER WE;Lo;0;L;<compat> 1170;;;;N;;;;;
+315F;HANGUL LETTER WI;Lo;0;L;<compat> 1171;;;;N;;;;;
+3160;HANGUL LETTER YU;Lo;0;L;<compat> 1172;;;;N;;;;;
+3161;HANGUL LETTER EU;Lo;0;L;<compat> 1173;;;;N;;;;;
+3162;HANGUL LETTER YI;Lo;0;L;<compat> 1174;;;;N;;;;;
+3163;HANGUL LETTER I;Lo;0;L;<compat> 1175;;;;N;;;;;
+3164;HANGUL FILLER;Lo;0;L;<compat> 1160;;;;N;HANGUL CAE OM;;;;
+3165;HANGUL LETTER SSANGNIEUN;Lo;0;L;<compat> 1114;;;;N;HANGUL LETTER SSANG NIEUN;;;;
+3166;HANGUL LETTER NIEUN-TIKEUT;Lo;0;L;<compat> 1115;;;;N;HANGUL LETTER NIEUN DIGEUD;;;;
+3167;HANGUL LETTER NIEUN-SIOS;Lo;0;L;<compat> 11C7;;;;N;HANGUL LETTER NIEUN SIOS;;;;
+3168;HANGUL LETTER NIEUN-PANSIOS;Lo;0;L;<compat> 11C8;;;;N;HANGUL LETTER NIEUN BAN CHI EUM;;;;
+3169;HANGUL LETTER RIEUL-KIYEOK-SIOS;Lo;0;L;<compat> 11CC;;;;N;HANGUL LETTER LIEUL GIYEOG SIOS;;;;
+316A;HANGUL LETTER RIEUL-TIKEUT;Lo;0;L;<compat> 11CE;;;;N;HANGUL LETTER LIEUL DIGEUD;;;;
+316B;HANGUL LETTER RIEUL-PIEUP-SIOS;Lo;0;L;<compat> 11D3;;;;N;HANGUL LETTER LIEUL BIEUB SIOS;;;;
+316C;HANGUL LETTER RIEUL-PANSIOS;Lo;0;L;<compat> 11D7;;;;N;HANGUL LETTER LIEUL BAN CHI EUM;;;;
+316D;HANGUL LETTER RIEUL-YEORINHIEUH;Lo;0;L;<compat> 11D9;;;;N;HANGUL LETTER LIEUL YEOLIN HIEUH;;;;
+316E;HANGUL LETTER MIEUM-PIEUP;Lo;0;L;<compat> 111C;;;;N;HANGUL LETTER MIEUM BIEUB;;;;
+316F;HANGUL LETTER MIEUM-SIOS;Lo;0;L;<compat> 11DD;;;;N;HANGUL LETTER MIEUM SIOS;;;;
+3170;HANGUL LETTER MIEUM-PANSIOS;Lo;0;L;<compat> 11DF;;;;N;HANGUL LETTER BIEUB BAN CHI EUM;;;;
+3171;HANGUL LETTER KAPYEOUNMIEUM;Lo;0;L;<compat> 111D;;;;N;HANGUL LETTER MIEUM SUN GYEONG EUM;;;;
+3172;HANGUL LETTER PIEUP-KIYEOK;Lo;0;L;<compat> 111E;;;;N;HANGUL LETTER BIEUB GIYEOG;;;;
+3173;HANGUL LETTER PIEUP-TIKEUT;Lo;0;L;<compat> 1120;;;;N;HANGUL LETTER BIEUB DIGEUD;;;;
+3174;HANGUL LETTER PIEUP-SIOS-KIYEOK;Lo;0;L;<compat> 1122;;;;N;HANGUL LETTER BIEUB SIOS GIYEOG;;;;
+3175;HANGUL LETTER PIEUP-SIOS-TIKEUT;Lo;0;L;<compat> 1123;;;;N;HANGUL LETTER BIEUB SIOS DIGEUD;;;;
+3176;HANGUL LETTER PIEUP-CIEUC;Lo;0;L;<compat> 1127;;;;N;HANGUL LETTER BIEUB JIEUJ;;;;
+3177;HANGUL LETTER PIEUP-THIEUTH;Lo;0;L;<compat> 1129;;;;N;HANGUL LETTER BIEUB TIEUT;;;;
+3178;HANGUL LETTER KAPYEOUNPIEUP;Lo;0;L;<compat> 112B;;;;N;HANGUL LETTER BIEUB SUN GYEONG EUM;;;;
+3179;HANGUL LETTER KAPYEOUNSSANGPIEUP;Lo;0;L;<compat> 112C;;;;N;HANGUL LETTER SSANG BIEUB SUN GYEONG EUM;;;;
+317A;HANGUL LETTER SIOS-KIYEOK;Lo;0;L;<compat> 112D;;;;N;HANGUL LETTER SIOS GIYEOG;;;;
+317B;HANGUL LETTER SIOS-NIEUN;Lo;0;L;<compat> 112E;;;;N;HANGUL LETTER SIOS NIEUN;;;;
+317C;HANGUL LETTER SIOS-TIKEUT;Lo;0;L;<compat> 112F;;;;N;HANGUL LETTER SIOS DIGEUD;;;;
+317D;HANGUL LETTER SIOS-PIEUP;Lo;0;L;<compat> 1132;;;;N;HANGUL LETTER SIOS BIEUB;;;;
+317E;HANGUL LETTER SIOS-CIEUC;Lo;0;L;<compat> 1136;;;;N;HANGUL LETTER SIOS JIEUJ;;;;
+317F;HANGUL LETTER PANSIOS;Lo;0;L;<compat> 1140;;;;N;HANGUL LETTER BAN CHI EUM;;;;
+3180;HANGUL LETTER SSANGIEUNG;Lo;0;L;<compat> 1147;;;;N;HANGUL LETTER SSANG IEUNG;;;;
+3181;HANGUL LETTER YESIEUNG;Lo;0;L;<compat> 114C;;;;N;HANGUL LETTER NGIEUNG;;;;
+3182;HANGUL LETTER YESIEUNG-SIOS;Lo;0;L;<compat> 11F1;;;;N;HANGUL LETTER NGIEUNG SIOS;;;;
+3183;HANGUL LETTER YESIEUNG-PANSIOS;Lo;0;L;<compat> 11F2;;;;N;HANGUL LETTER NGIEUNG BAN CHI EUM;;;;
+3184;HANGUL LETTER KAPYEOUNPHIEUPH;Lo;0;L;<compat> 1157;;;;N;HANGUL LETTER PIEUP SUN GYEONG EUM;;;;
+3185;HANGUL LETTER SSANGHIEUH;Lo;0;L;<compat> 1158;;;;N;HANGUL LETTER SSANG HIEUH;;;;
+3186;HANGUL LETTER YEORINHIEUH;Lo;0;L;<compat> 1159;;;;N;HANGUL LETTER YEOLIN HIEUH;;;;
+3187;HANGUL LETTER YO-YA;Lo;0;L;<compat> 1184;;;;N;HANGUL LETTER YOYA;;;;
+3188;HANGUL LETTER YO-YAE;Lo;0;L;<compat> 1185;;;;N;HANGUL LETTER YOYAE;;;;
+3189;HANGUL LETTER YO-I;Lo;0;L;<compat> 1188;;;;N;HANGUL LETTER YOI;;;;
+318A;HANGUL LETTER YU-YEO;Lo;0;L;<compat> 1191;;;;N;HANGUL LETTER YUYEO;;;;
+318B;HANGUL LETTER YU-YE;Lo;0;L;<compat> 1192;;;;N;HANGUL LETTER YUYE;;;;
+318C;HANGUL LETTER YU-I;Lo;0;L;<compat> 1194;;;;N;HANGUL LETTER YUI;;;;
+318D;HANGUL LETTER ARAEA;Lo;0;L;<compat> 119E;;;;N;HANGUL LETTER ALAE A;;;;
+318E;HANGUL LETTER ARAEAE;Lo;0;L;<compat> 11A1;;;;N;HANGUL LETTER ALAE AE;;;;
+3190;IDEOGRAPHIC ANNOTATION LINKING MARK;So;0;L;;;;;N;KANBUN TATETEN;;;;
+3191;IDEOGRAPHIC ANNOTATION REVERSE MARK;So;0;L;;;;;N;KAERITEN RE;;;;
+3192;IDEOGRAPHIC ANNOTATION ONE MARK;No;0;L;<super> 4E00;;;1;N;KAERITEN ITI;;;;
+3193;IDEOGRAPHIC ANNOTATION TWO MARK;No;0;L;<super> 4E8C;;;2;N;KAERITEN NI;;;;
+3194;IDEOGRAPHIC ANNOTATION THREE MARK;No;0;L;<super> 4E09;;;3;N;KAERITEN SAN;;;;
+3195;IDEOGRAPHIC ANNOTATION FOUR MARK;No;0;L;<super> 56DB;;;4;N;KAERITEN SI;;;;
+3196;IDEOGRAPHIC ANNOTATION TOP MARK;So;0;L;<super> 4E0A;;;;N;KAERITEN ZYOU;;;;
+3197;IDEOGRAPHIC ANNOTATION MIDDLE MARK;So;0;L;<super> 4E2D;;;;N;KAERITEN TYUU;;;;
+3198;IDEOGRAPHIC ANNOTATION BOTTOM MARK;So;0;L;<super> 4E0B;;;;N;KAERITEN GE;;;;
+3199;IDEOGRAPHIC ANNOTATION FIRST MARK;So;0;L;<super> 7532;;;;N;KAERITEN KOU;;;;
+319A;IDEOGRAPHIC ANNOTATION SECOND MARK;So;0;L;<super> 4E59;;;;N;KAERITEN OTU;;;;
+319B;IDEOGRAPHIC ANNOTATION THIRD MARK;So;0;L;<super> 4E19;;;;N;KAERITEN HEI;;;;
+319C;IDEOGRAPHIC ANNOTATION FOURTH MARK;So;0;L;<super> 4E01;;;;N;KAERITEN TEI;;;;
+319D;IDEOGRAPHIC ANNOTATION HEAVEN MARK;So;0;L;<super> 5929;;;;N;KAERITEN TEN;;;;
+319E;IDEOGRAPHIC ANNOTATION EARTH MARK;So;0;L;<super> 5730;;;;N;KAERITEN TI;;;;
+319F;IDEOGRAPHIC ANNOTATION MAN MARK;So;0;L;<super> 4EBA;;;;N;KAERITEN ZIN;;;;
+31A0;BOPOMOFO LETTER BU;Lo;0;L;;;;;N;;;;;
+31A1;BOPOMOFO LETTER ZI;Lo;0;L;;;;;N;;;;;
+31A2;BOPOMOFO LETTER JI;Lo;0;L;;;;;N;;;;;
+31A3;BOPOMOFO LETTER GU;Lo;0;L;;;;;N;;;;;
+31A4;BOPOMOFO LETTER EE;Lo;0;L;;;;;N;;;;;
+31A5;BOPOMOFO LETTER ENN;Lo;0;L;;;;;N;;;;;
+31A6;BOPOMOFO LETTER OO;Lo;0;L;;;;;N;;;;;
+31A7;BOPOMOFO LETTER ONN;Lo;0;L;;;;;N;;;;;
+31A8;BOPOMOFO LETTER IR;Lo;0;L;;;;;N;;;;;
+31A9;BOPOMOFO LETTER ANN;Lo;0;L;;;;;N;;;;;
+31AA;BOPOMOFO LETTER INN;Lo;0;L;;;;;N;;;;;
+31AB;BOPOMOFO LETTER UNN;Lo;0;L;;;;;N;;;;;
+31AC;BOPOMOFO LETTER IM;Lo;0;L;;;;;N;;;;;
+31AD;BOPOMOFO LETTER NGG;Lo;0;L;;;;;N;;;;;
+31AE;BOPOMOFO LETTER AINN;Lo;0;L;;;;;N;;;;;
+31AF;BOPOMOFO LETTER AUNN;Lo;0;L;;;;;N;;;;;
+31B0;BOPOMOFO LETTER AM;Lo;0;L;;;;;N;;;;;
+31B1;BOPOMOFO LETTER OM;Lo;0;L;;;;;N;;;;;
+31B2;BOPOMOFO LETTER ONG;Lo;0;L;;;;;N;;;;;
+31B3;BOPOMOFO LETTER INNN;Lo;0;L;;;;;N;;;;;
+31B4;BOPOMOFO FINAL LETTER P;Lo;0;L;;;;;N;;;;;
+31B5;BOPOMOFO FINAL LETTER T;Lo;0;L;;;;;N;;;;;
+31B6;BOPOMOFO FINAL LETTER K;Lo;0;L;;;;;N;;;;;
+31B7;BOPOMOFO FINAL LETTER H;Lo;0;L;;;;;N;;;;;
+31B8;BOPOMOFO LETTER GH;Lo;0;L;;;;;N;;;;;
+31B9;BOPOMOFO LETTER LH;Lo;0;L;;;;;N;;;;;
+31BA;BOPOMOFO LETTER ZY;Lo;0;L;;;;;N;;;;;
+31C0;CJK STROKE T;So;0;ON;;;;;N;;;;;
+31C1;CJK STROKE WG;So;0;ON;;;;;N;;;;;
+31C2;CJK STROKE XG;So;0;ON;;;;;N;;;;;
+31C3;CJK STROKE BXG;So;0;ON;;;;;N;;;;;
+31C4;CJK STROKE SW;So;0;ON;;;;;N;;;;;
+31C5;CJK STROKE HZZ;So;0;ON;;;;;N;;;;;
+31C6;CJK STROKE HZG;So;0;ON;;;;;N;;;;;
+31C7;CJK STROKE HP;So;0;ON;;;;;N;;;;;
+31C8;CJK STROKE HZWG;So;0;ON;;;;;N;;;;;
+31C9;CJK STROKE SZWG;So;0;ON;;;;;N;;;;;
+31CA;CJK STROKE HZT;So;0;ON;;;;;N;;;;;
+31CB;CJK STROKE HZZP;So;0;ON;;;;;N;;;;;
+31CC;CJK STROKE HPWG;So;0;ON;;;;;N;;;;;
+31CD;CJK STROKE HZW;So;0;ON;;;;;N;;;;;
+31CE;CJK STROKE HZZZ;So;0;ON;;;;;N;;;;;
+31CF;CJK STROKE N;So;0;ON;;;;;N;;;;;
+31D0;CJK STROKE H;So;0;ON;;;;;N;;;;;
+31D1;CJK STROKE S;So;0;ON;;;;;N;;;;;
+31D2;CJK STROKE P;So;0;ON;;;;;N;;;;;
+31D3;CJK STROKE SP;So;0;ON;;;;;N;;;;;
+31D4;CJK STROKE D;So;0;ON;;;;;N;;;;;
+31D5;CJK STROKE HZ;So;0;ON;;;;;N;;;;;
+31D6;CJK STROKE HG;So;0;ON;;;;;N;;;;;
+31D7;CJK STROKE SZ;So;0;ON;;;;;N;;;;;
+31D8;CJK STROKE SWZ;So;0;ON;;;;;N;;;;;
+31D9;CJK STROKE ST;So;0;ON;;;;;N;;;;;
+31DA;CJK STROKE SG;So;0;ON;;;;;N;;;;;
+31DB;CJK STROKE PD;So;0;ON;;;;;N;;;;;
+31DC;CJK STROKE PZ;So;0;ON;;;;;N;;;;;
+31DD;CJK STROKE TN;So;0;ON;;;;;N;;;;;
+31DE;CJK STROKE SZZ;So;0;ON;;;;;N;;;;;
+31DF;CJK STROKE SWG;So;0;ON;;;;;N;;;;;
+31E0;CJK STROKE HXWG;So;0;ON;;;;;N;;;;;
+31E1;CJK STROKE HZZZG;So;0;ON;;;;;N;;;;;
+31E2;CJK STROKE PG;So;0;ON;;;;;N;;;;;
+31E3;CJK STROKE Q;So;0;ON;;;;;N;;;;;
+31F0;KATAKANA LETTER SMALL KU;Lo;0;L;;;;;N;;;;;
+31F1;KATAKANA LETTER SMALL SI;Lo;0;L;;;;;N;;;;;
+31F2;KATAKANA LETTER SMALL SU;Lo;0;L;;;;;N;;;;;
+31F3;KATAKANA LETTER SMALL TO;Lo;0;L;;;;;N;;;;;
+31F4;KATAKANA LETTER SMALL NU;Lo;0;L;;;;;N;;;;;
+31F5;KATAKANA LETTER SMALL HA;Lo;0;L;;;;;N;;;;;
+31F6;KATAKANA LETTER SMALL HI;Lo;0;L;;;;;N;;;;;
+31F7;KATAKANA LETTER SMALL HU;Lo;0;L;;;;;N;;;;;
+31F8;KATAKANA LETTER SMALL HE;Lo;0;L;;;;;N;;;;;
+31F9;KATAKANA LETTER SMALL HO;Lo;0;L;;;;;N;;;;;
+31FA;KATAKANA LETTER SMALL MU;Lo;0;L;;;;;N;;;;;
+31FB;KATAKANA LETTER SMALL RA;Lo;0;L;;;;;N;;;;;
+31FC;KATAKANA LETTER SMALL RI;Lo;0;L;;;;;N;;;;;
+31FD;KATAKANA LETTER SMALL RU;Lo;0;L;;;;;N;;;;;
+31FE;KATAKANA LETTER SMALL RE;Lo;0;L;;;;;N;;;;;
+31FF;KATAKANA LETTER SMALL RO;Lo;0;L;;;;;N;;;;;
+3200;PARENTHESIZED HANGUL KIYEOK;So;0;L;<compat> 0028 1100 0029;;;;N;PARENTHESIZED HANGUL GIYEOG;;;;
+3201;PARENTHESIZED HANGUL NIEUN;So;0;L;<compat> 0028 1102 0029;;;;N;;;;;
+3202;PARENTHESIZED HANGUL TIKEUT;So;0;L;<compat> 0028 1103 0029;;;;N;PARENTHESIZED HANGUL DIGEUD;;;;
+3203;PARENTHESIZED HANGUL RIEUL;So;0;L;<compat> 0028 1105 0029;;;;N;PARENTHESIZED HANGUL LIEUL;;;;
+3204;PARENTHESIZED HANGUL MIEUM;So;0;L;<compat> 0028 1106 0029;;;;N;;;;;
+3205;PARENTHESIZED HANGUL PIEUP;So;0;L;<compat> 0028 1107 0029;;;;N;PARENTHESIZED HANGUL BIEUB;;;;
+3206;PARENTHESIZED HANGUL SIOS;So;0;L;<compat> 0028 1109 0029;;;;N;;;;;
+3207;PARENTHESIZED HANGUL IEUNG;So;0;L;<compat> 0028 110B 0029;;;;N;;;;;
+3208;PARENTHESIZED HANGUL CIEUC;So;0;L;<compat> 0028 110C 0029;;;;N;PARENTHESIZED HANGUL JIEUJ;;;;
+3209;PARENTHESIZED HANGUL CHIEUCH;So;0;L;<compat> 0028 110E 0029;;;;N;PARENTHESIZED HANGUL CIEUC;;;;
+320A;PARENTHESIZED HANGUL KHIEUKH;So;0;L;<compat> 0028 110F 0029;;;;N;PARENTHESIZED HANGUL KIYEOK;;;;
+320B;PARENTHESIZED HANGUL THIEUTH;So;0;L;<compat> 0028 1110 0029;;;;N;PARENTHESIZED HANGUL TIEUT;;;;
+320C;PARENTHESIZED HANGUL PHIEUPH;So;0;L;<compat> 0028 1111 0029;;;;N;PARENTHESIZED HANGUL PIEUP;;;;
+320D;PARENTHESIZED HANGUL HIEUH;So;0;L;<compat> 0028 1112 0029;;;;N;;;;;
+320E;PARENTHESIZED HANGUL KIYEOK A;So;0;L;<compat> 0028 1100 1161 0029;;;;N;PARENTHESIZED HANGUL GA;;;;
+320F;PARENTHESIZED HANGUL NIEUN A;So;0;L;<compat> 0028 1102 1161 0029;;;;N;PARENTHESIZED HANGUL NA;;;;
+3210;PARENTHESIZED HANGUL TIKEUT A;So;0;L;<compat> 0028 1103 1161 0029;;;;N;PARENTHESIZED HANGUL DA;;;;
+3211;PARENTHESIZED HANGUL RIEUL A;So;0;L;<compat> 0028 1105 1161 0029;;;;N;PARENTHESIZED HANGUL LA;;;;
+3212;PARENTHESIZED HANGUL MIEUM A;So;0;L;<compat> 0028 1106 1161 0029;;;;N;PARENTHESIZED HANGUL MA;;;;
+3213;PARENTHESIZED HANGUL PIEUP A;So;0;L;<compat> 0028 1107 1161 0029;;;;N;PARENTHESIZED HANGUL BA;;;;
+3214;PARENTHESIZED HANGUL SIOS A;So;0;L;<compat> 0028 1109 1161 0029;;;;N;PARENTHESIZED HANGUL SA;;;;
+3215;PARENTHESIZED HANGUL IEUNG A;So;0;L;<compat> 0028 110B 1161 0029;;;;N;PARENTHESIZED HANGUL A;;;;
+3216;PARENTHESIZED HANGUL CIEUC A;So;0;L;<compat> 0028 110C 1161 0029;;;;N;PARENTHESIZED HANGUL JA;;;;
+3217;PARENTHESIZED HANGUL CHIEUCH A;So;0;L;<compat> 0028 110E 1161 0029;;;;N;PARENTHESIZED HANGUL CA;;;;
+3218;PARENTHESIZED HANGUL KHIEUKH A;So;0;L;<compat> 0028 110F 1161 0029;;;;N;PARENTHESIZED HANGUL KA;;;;
+3219;PARENTHESIZED HANGUL THIEUTH A;So;0;L;<compat> 0028 1110 1161 0029;;;;N;PARENTHESIZED HANGUL TA;;;;
+321A;PARENTHESIZED HANGUL PHIEUPH A;So;0;L;<compat> 0028 1111 1161 0029;;;;N;PARENTHESIZED HANGUL PA;;;;
+321B;PARENTHESIZED HANGUL HIEUH A;So;0;L;<compat> 0028 1112 1161 0029;;;;N;PARENTHESIZED HANGUL HA;;;;
+321C;PARENTHESIZED HANGUL CIEUC U;So;0;L;<compat> 0028 110C 116E 0029;;;;N;PARENTHESIZED HANGUL JU;;;;
+321D;PARENTHESIZED KOREAN CHARACTER OJEON;So;0;ON;<compat> 0028 110B 1169 110C 1165 11AB 0029;;;;N;;;;;
+321E;PARENTHESIZED KOREAN CHARACTER O HU;So;0;ON;<compat> 0028 110B 1169 1112 116E 0029;;;;N;;;;;
+3220;PARENTHESIZED IDEOGRAPH ONE;No;0;L;<compat> 0028 4E00 0029;;;1;N;;;;;
+3221;PARENTHESIZED IDEOGRAPH TWO;No;0;L;<compat> 0028 4E8C 0029;;;2;N;;;;;
+3222;PARENTHESIZED IDEOGRAPH THREE;No;0;L;<compat> 0028 4E09 0029;;;3;N;;;;;
+3223;PARENTHESIZED IDEOGRAPH FOUR;No;0;L;<compat> 0028 56DB 0029;;;4;N;;;;;
+3224;PARENTHESIZED IDEOGRAPH FIVE;No;0;L;<compat> 0028 4E94 0029;;;5;N;;;;;
+3225;PARENTHESIZED IDEOGRAPH SIX;No;0;L;<compat> 0028 516D 0029;;;6;N;;;;;
+3226;PARENTHESIZED IDEOGRAPH SEVEN;No;0;L;<compat> 0028 4E03 0029;;;7;N;;;;;
+3227;PARENTHESIZED IDEOGRAPH EIGHT;No;0;L;<compat> 0028 516B 0029;;;8;N;;;;;
+3228;PARENTHESIZED IDEOGRAPH NINE;No;0;L;<compat> 0028 4E5D 0029;;;9;N;;;;;
+3229;PARENTHESIZED IDEOGRAPH TEN;No;0;L;<compat> 0028 5341 0029;;;10;N;;;;;
+322A;PARENTHESIZED IDEOGRAPH MOON;So;0;L;<compat> 0028 6708 0029;;;;N;;;;;
+322B;PARENTHESIZED IDEOGRAPH FIRE;So;0;L;<compat> 0028 706B 0029;;;;N;;;;;
+322C;PARENTHESIZED IDEOGRAPH WATER;So;0;L;<compat> 0028 6C34 0029;;;;N;;;;;
+322D;PARENTHESIZED IDEOGRAPH WOOD;So;0;L;<compat> 0028 6728 0029;;;;N;;;;;
+322E;PARENTHESIZED IDEOGRAPH METAL;So;0;L;<compat> 0028 91D1 0029;;;;N;;;;;
+322F;PARENTHESIZED IDEOGRAPH EARTH;So;0;L;<compat> 0028 571F 0029;;;;N;;;;;
+3230;PARENTHESIZED IDEOGRAPH SUN;So;0;L;<compat> 0028 65E5 0029;;;;N;;;;;
+3231;PARENTHESIZED IDEOGRAPH STOCK;So;0;L;<compat> 0028 682A 0029;;;;N;;;;;
+3232;PARENTHESIZED IDEOGRAPH HAVE;So;0;L;<compat> 0028 6709 0029;;;;N;;;;;
+3233;PARENTHESIZED IDEOGRAPH SOCIETY;So;0;L;<compat> 0028 793E 0029;;;;N;;;;;
+3234;PARENTHESIZED IDEOGRAPH NAME;So;0;L;<compat> 0028 540D 0029;;;;N;;;;;
+3235;PARENTHESIZED IDEOGRAPH SPECIAL;So;0;L;<compat> 0028 7279 0029;;;;N;;;;;
+3236;PARENTHESIZED IDEOGRAPH FINANCIAL;So;0;L;<compat> 0028 8CA1 0029;;;;N;;;;;
+3237;PARENTHESIZED IDEOGRAPH CONGRATULATION;So;0;L;<compat> 0028 795D 0029;;;;N;;;;;
+3238;PARENTHESIZED IDEOGRAPH LABOR;So;0;L;<compat> 0028 52B4 0029;;;;N;;;;;
+3239;PARENTHESIZED IDEOGRAPH REPRESENT;So;0;L;<compat> 0028 4EE3 0029;;;;N;;;;;
+323A;PARENTHESIZED IDEOGRAPH CALL;So;0;L;<compat> 0028 547C 0029;;;;N;;;;;
+323B;PARENTHESIZED IDEOGRAPH STUDY;So;0;L;<compat> 0028 5B66 0029;;;;N;;;;;
+323C;PARENTHESIZED IDEOGRAPH SUPERVISE;So;0;L;<compat> 0028 76E3 0029;;;;N;;;;;
+323D;PARENTHESIZED IDEOGRAPH ENTERPRISE;So;0;L;<compat> 0028 4F01 0029;;;;N;;;;;
+323E;PARENTHESIZED IDEOGRAPH RESOURCE;So;0;L;<compat> 0028 8CC7 0029;;;;N;;;;;
+323F;PARENTHESIZED IDEOGRAPH ALLIANCE;So;0;L;<compat> 0028 5354 0029;;;;N;;;;;
+3240;PARENTHESIZED IDEOGRAPH FESTIVAL;So;0;L;<compat> 0028 796D 0029;;;;N;;;;;
+3241;PARENTHESIZED IDEOGRAPH REST;So;0;L;<compat> 0028 4F11 0029;;;;N;;;;;
+3242;PARENTHESIZED IDEOGRAPH SELF;So;0;L;<compat> 0028 81EA 0029;;;;N;;;;;
+3243;PARENTHESIZED IDEOGRAPH REACH;So;0;L;<compat> 0028 81F3 0029;;;;N;;;;;
+3244;CIRCLED IDEOGRAPH QUESTION;So;0;L;<circle> 554F;;;;N;;;;;
+3245;CIRCLED IDEOGRAPH KINDERGARTEN;So;0;L;<circle> 5E7C;;;;N;;;;;
+3246;CIRCLED IDEOGRAPH SCHOOL;So;0;L;<circle> 6587;;;;N;;;;;
+3247;CIRCLED IDEOGRAPH KOTO;So;0;L;<circle> 7B8F;;;;N;;;;;
+3248;CIRCLED NUMBER TEN ON BLACK SQUARE;No;0;L;;;;10;N;;;;;
+3249;CIRCLED NUMBER TWENTY ON BLACK SQUARE;No;0;L;;;;20;N;;;;;
+324A;CIRCLED NUMBER THIRTY ON BLACK SQUARE;No;0;L;;;;30;N;;;;;
+324B;CIRCLED NUMBER FORTY ON BLACK SQUARE;No;0;L;;;;40;N;;;;;
+324C;CIRCLED NUMBER FIFTY ON BLACK SQUARE;No;0;L;;;;50;N;;;;;
+324D;CIRCLED NUMBER SIXTY ON BLACK SQUARE;No;0;L;;;;60;N;;;;;
+324E;CIRCLED NUMBER SEVENTY ON BLACK SQUARE;No;0;L;;;;70;N;;;;;
+324F;CIRCLED NUMBER EIGHTY ON BLACK SQUARE;No;0;L;;;;80;N;;;;;
+3250;PARTNERSHIP SIGN;So;0;ON;<square> 0050 0054 0045;;;;N;;;;;
+3251;CIRCLED NUMBER TWENTY ONE;No;0;ON;<circle> 0032 0031;;;21;N;;;;;
+3252;CIRCLED NUMBER TWENTY TWO;No;0;ON;<circle> 0032 0032;;;22;N;;;;;
+3253;CIRCLED NUMBER TWENTY THREE;No;0;ON;<circle> 0032 0033;;;23;N;;;;;
+3254;CIRCLED NUMBER TWENTY FOUR;No;0;ON;<circle> 0032 0034;;;24;N;;;;;
+3255;CIRCLED NUMBER TWENTY FIVE;No;0;ON;<circle> 0032 0035;;;25;N;;;;;
+3256;CIRCLED NUMBER TWENTY SIX;No;0;ON;<circle> 0032 0036;;;26;N;;;;;
+3257;CIRCLED NUMBER TWENTY SEVEN;No;0;ON;<circle> 0032 0037;;;27;N;;;;;
+3258;CIRCLED NUMBER TWENTY EIGHT;No;0;ON;<circle> 0032 0038;;;28;N;;;;;
+3259;CIRCLED NUMBER TWENTY NINE;No;0;ON;<circle> 0032 0039;;;29;N;;;;;
+325A;CIRCLED NUMBER THIRTY;No;0;ON;<circle> 0033 0030;;;30;N;;;;;
+325B;CIRCLED NUMBER THIRTY ONE;No;0;ON;<circle> 0033 0031;;;31;N;;;;;
+325C;CIRCLED NUMBER THIRTY TWO;No;0;ON;<circle> 0033 0032;;;32;N;;;;;
+325D;CIRCLED NUMBER THIRTY THREE;No;0;ON;<circle> 0033 0033;;;33;N;;;;;
+325E;CIRCLED NUMBER THIRTY FOUR;No;0;ON;<circle> 0033 0034;;;34;N;;;;;
+325F;CIRCLED NUMBER THIRTY FIVE;No;0;ON;<circle> 0033 0035;;;35;N;;;;;
+3260;CIRCLED HANGUL KIYEOK;So;0;L;<circle> 1100;;;;N;CIRCLED HANGUL GIYEOG;;;;
+3261;CIRCLED HANGUL NIEUN;So;0;L;<circle> 1102;;;;N;;;;;
+3262;CIRCLED HANGUL TIKEUT;So;0;L;<circle> 1103;;;;N;CIRCLED HANGUL DIGEUD;;;;
+3263;CIRCLED HANGUL RIEUL;So;0;L;<circle> 1105;;;;N;CIRCLED HANGUL LIEUL;;;;
+3264;CIRCLED HANGUL MIEUM;So;0;L;<circle> 1106;;;;N;;;;;
+3265;CIRCLED HANGUL PIEUP;So;0;L;<circle> 1107;;;;N;CIRCLED HANGUL BIEUB;;;;
+3266;CIRCLED HANGUL SIOS;So;0;L;<circle> 1109;;;;N;;;;;
+3267;CIRCLED HANGUL IEUNG;So;0;L;<circle> 110B;;;;N;;;;;
+3268;CIRCLED HANGUL CIEUC;So;0;L;<circle> 110C;;;;N;CIRCLED HANGUL JIEUJ;;;;
+3269;CIRCLED HANGUL CHIEUCH;So;0;L;<circle> 110E;;;;N;CIRCLED HANGUL CIEUC;;;;
+326A;CIRCLED HANGUL KHIEUKH;So;0;L;<circle> 110F;;;;N;CIRCLED HANGUL KIYEOK;;;;
+326B;CIRCLED HANGUL THIEUTH;So;0;L;<circle> 1110;;;;N;CIRCLED HANGUL TIEUT;;;;
+326C;CIRCLED HANGUL PHIEUPH;So;0;L;<circle> 1111;;;;N;CIRCLED HANGUL PIEUP;;;;
+326D;CIRCLED HANGUL HIEUH;So;0;L;<circle> 1112;;;;N;;;;;
+326E;CIRCLED HANGUL KIYEOK A;So;0;L;<circle> 1100 1161;;;;N;CIRCLED HANGUL GA;;;;
+326F;CIRCLED HANGUL NIEUN A;So;0;L;<circle> 1102 1161;;;;N;CIRCLED HANGUL NA;;;;
+3270;CIRCLED HANGUL TIKEUT A;So;0;L;<circle> 1103 1161;;;;N;CIRCLED HANGUL DA;;;;
+3271;CIRCLED HANGUL RIEUL A;So;0;L;<circle> 1105 1161;;;;N;CIRCLED HANGUL LA;;;;
+3272;CIRCLED HANGUL MIEUM A;So;0;L;<circle> 1106 1161;;;;N;CIRCLED HANGUL MA;;;;
+3273;CIRCLED HANGUL PIEUP A;So;0;L;<circle> 1107 1161;;;;N;CIRCLED HANGUL BA;;;;
+3274;CIRCLED HANGUL SIOS A;So;0;L;<circle> 1109 1161;;;;N;CIRCLED HANGUL SA;;;;
+3275;CIRCLED HANGUL IEUNG A;So;0;L;<circle> 110B 1161;;;;N;CIRCLED HANGUL A;;;;
+3276;CIRCLED HANGUL CIEUC A;So;0;L;<circle> 110C 1161;;;;N;CIRCLED HANGUL JA;;;;
+3277;CIRCLED HANGUL CHIEUCH A;So;0;L;<circle> 110E 1161;;;;N;CIRCLED HANGUL CA;;;;
+3278;CIRCLED HANGUL KHIEUKH A;So;0;L;<circle> 110F 1161;;;;N;CIRCLED HANGUL KA;;;;
+3279;CIRCLED HANGUL THIEUTH A;So;0;L;<circle> 1110 1161;;;;N;CIRCLED HANGUL TA;;;;
+327A;CIRCLED HANGUL PHIEUPH A;So;0;L;<circle> 1111 1161;;;;N;CIRCLED HANGUL PA;;;;
+327B;CIRCLED HANGUL HIEUH A;So;0;L;<circle> 1112 1161;;;;N;CIRCLED HANGUL HA;;;;
+327C;CIRCLED KOREAN CHARACTER CHAMKO;So;0;ON;<circle> 110E 1161 11B7 1100 1169;;;;N;;;;;
+327D;CIRCLED KOREAN CHARACTER JUEUI;So;0;ON;<circle> 110C 116E 110B 1174;;;;N;;;;;
+327E;CIRCLED HANGUL IEUNG U;So;0;ON;<circle> 110B 116E;;;;N;;;;;
+327F;KOREAN STANDARD SYMBOL;So;0;L;;;;;N;;;;;
+3280;CIRCLED IDEOGRAPH ONE;No;0;L;<circle> 4E00;;;1;N;;;;;
+3281;CIRCLED IDEOGRAPH TWO;No;0;L;<circle> 4E8C;;;2;N;;;;;
+3282;CIRCLED IDEOGRAPH THREE;No;0;L;<circle> 4E09;;;3;N;;;;;
+3283;CIRCLED IDEOGRAPH FOUR;No;0;L;<circle> 56DB;;;4;N;;;;;
+3284;CIRCLED IDEOGRAPH FIVE;No;0;L;<circle> 4E94;;;5;N;;;;;
+3285;CIRCLED IDEOGRAPH SIX;No;0;L;<circle> 516D;;;6;N;;;;;
+3286;CIRCLED IDEOGRAPH SEVEN;No;0;L;<circle> 4E03;;;7;N;;;;;
+3287;CIRCLED IDEOGRAPH EIGHT;No;0;L;<circle> 516B;;;8;N;;;;;
+3288;CIRCLED IDEOGRAPH NINE;No;0;L;<circle> 4E5D;;;9;N;;;;;
+3289;CIRCLED IDEOGRAPH TEN;No;0;L;<circle> 5341;;;10;N;;;;;
+328A;CIRCLED IDEOGRAPH MOON;So;0;L;<circle> 6708;;;;N;;;;;
+328B;CIRCLED IDEOGRAPH FIRE;So;0;L;<circle> 706B;;;;N;;;;;
+328C;CIRCLED IDEOGRAPH WATER;So;0;L;<circle> 6C34;;;;N;;;;;
+328D;CIRCLED IDEOGRAPH WOOD;So;0;L;<circle> 6728;;;;N;;;;;
+328E;CIRCLED IDEOGRAPH METAL;So;0;L;<circle> 91D1;;;;N;;;;;
+328F;CIRCLED IDEOGRAPH EARTH;So;0;L;<circle> 571F;;;;N;;;;;
+3290;CIRCLED IDEOGRAPH SUN;So;0;L;<circle> 65E5;;;;N;;;;;
+3291;CIRCLED IDEOGRAPH STOCK;So;0;L;<circle> 682A;;;;N;;;;;
+3292;CIRCLED IDEOGRAPH HAVE;So;0;L;<circle> 6709;;;;N;;;;;
+3293;CIRCLED IDEOGRAPH SOCIETY;So;0;L;<circle> 793E;;;;N;;;;;
+3294;CIRCLED IDEOGRAPH NAME;So;0;L;<circle> 540D;;;;N;;;;;
+3295;CIRCLED IDEOGRAPH SPECIAL;So;0;L;<circle> 7279;;;;N;;;;;
+3296;CIRCLED IDEOGRAPH FINANCIAL;So;0;L;<circle> 8CA1;;;;N;;;;;
+3297;CIRCLED IDEOGRAPH CONGRATULATION;So;0;L;<circle> 795D;;;;N;;;;;
+3298;CIRCLED IDEOGRAPH LABOR;So;0;L;<circle> 52B4;;;;N;;;;;
+3299;CIRCLED IDEOGRAPH SECRET;So;0;L;<circle> 79D8;;;;N;;;;;
+329A;CIRCLED IDEOGRAPH MALE;So;0;L;<circle> 7537;;;;N;;;;;
+329B;CIRCLED IDEOGRAPH FEMALE;So;0;L;<circle> 5973;;;;N;;;;;
+329C;CIRCLED IDEOGRAPH SUITABLE;So;0;L;<circle> 9069;;;;N;;;;;
+329D;CIRCLED IDEOGRAPH EXCELLENT;So;0;L;<circle> 512A;;;;N;;;;;
+329E;CIRCLED IDEOGRAPH PRINT;So;0;L;<circle> 5370;;;;N;;;;;
+329F;CIRCLED IDEOGRAPH ATTENTION;So;0;L;<circle> 6CE8;;;;N;;;;;
+32A0;CIRCLED IDEOGRAPH ITEM;So;0;L;<circle> 9805;;;;N;;;;;
+32A1;CIRCLED IDEOGRAPH REST;So;0;L;<circle> 4F11;;;;N;;;;;
+32A2;CIRCLED IDEOGRAPH COPY;So;0;L;<circle> 5199;;;;N;;;;;
+32A3;CIRCLED IDEOGRAPH CORRECT;So;0;L;<circle> 6B63;;;;N;;;;;
+32A4;CIRCLED IDEOGRAPH HIGH;So;0;L;<circle> 4E0A;;;;N;;;;;
+32A5;CIRCLED IDEOGRAPH CENTRE;So;0;L;<circle> 4E2D;;;;N;CIRCLED IDEOGRAPH CENTER;;;;
+32A6;CIRCLED IDEOGRAPH LOW;So;0;L;<circle> 4E0B;;;;N;;;;;
+32A7;CIRCLED IDEOGRAPH LEFT;So;0;L;<circle> 5DE6;;;;N;;;;;
+32A8;CIRCLED IDEOGRAPH RIGHT;So;0;L;<circle> 53F3;;;;N;;;;;
+32A9;CIRCLED IDEOGRAPH MEDICINE;So;0;L;<circle> 533B;;;;N;;;;;
+32AA;CIRCLED IDEOGRAPH RELIGION;So;0;L;<circle> 5B97;;;;N;;;;;
+32AB;CIRCLED IDEOGRAPH STUDY;So;0;L;<circle> 5B66;;;;N;;;;;
+32AC;CIRCLED IDEOGRAPH SUPERVISE;So;0;L;<circle> 76E3;;;;N;;;;;
+32AD;CIRCLED IDEOGRAPH ENTERPRISE;So;0;L;<circle> 4F01;;;;N;;;;;
+32AE;CIRCLED IDEOGRAPH RESOURCE;So;0;L;<circle> 8CC7;;;;N;;;;;
+32AF;CIRCLED IDEOGRAPH ALLIANCE;So;0;L;<circle> 5354;;;;N;;;;;
+32B0;CIRCLED IDEOGRAPH NIGHT;So;0;L;<circle> 591C;;;;N;;;;;
+32B1;CIRCLED NUMBER THIRTY SIX;No;0;ON;<circle> 0033 0036;;;36;N;;;;;
+32B2;CIRCLED NUMBER THIRTY SEVEN;No;0;ON;<circle> 0033 0037;;;37;N;;;;;
+32B3;CIRCLED NUMBER THIRTY EIGHT;No;0;ON;<circle> 0033 0038;;;38;N;;;;;
+32B4;CIRCLED NUMBER THIRTY NINE;No;0;ON;<circle> 0033 0039;;;39;N;;;;;
+32B5;CIRCLED NUMBER FORTY;No;0;ON;<circle> 0034 0030;;;40;N;;;;;
+32B6;CIRCLED NUMBER FORTY ONE;No;0;ON;<circle> 0034 0031;;;41;N;;;;;
+32B7;CIRCLED NUMBER FORTY TWO;No;0;ON;<circle> 0034 0032;;;42;N;;;;;
+32B8;CIRCLED NUMBER FORTY THREE;No;0;ON;<circle> 0034 0033;;;43;N;;;;;
+32B9;CIRCLED NUMBER FORTY FOUR;No;0;ON;<circle> 0034 0034;;;44;N;;;;;
+32BA;CIRCLED NUMBER FORTY FIVE;No;0;ON;<circle> 0034 0035;;;45;N;;;;;
+32BB;CIRCLED NUMBER FORTY SIX;No;0;ON;<circle> 0034 0036;;;46;N;;;;;
+32BC;CIRCLED NUMBER FORTY SEVEN;No;0;ON;<circle> 0034 0037;;;47;N;;;;;
+32BD;CIRCLED NUMBER FORTY EIGHT;No;0;ON;<circle> 0034 0038;;;48;N;;;;;
+32BE;CIRCLED NUMBER FORTY NINE;No;0;ON;<circle> 0034 0039;;;49;N;;;;;
+32BF;CIRCLED NUMBER FIFTY;No;0;ON;<circle> 0035 0030;;;50;N;;;;;
+32C0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY;So;0;L;<compat> 0031 6708;;;;N;;;;;
+32C1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR FEBRUARY;So;0;L;<compat> 0032 6708;;;;N;;;;;
+32C2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR MARCH;So;0;L;<compat> 0033 6708;;;;N;;;;;
+32C3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR APRIL;So;0;L;<compat> 0034 6708;;;;N;;;;;
+32C4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR MAY;So;0;L;<compat> 0035 6708;;;;N;;;;;
+32C5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JUNE;So;0;L;<compat> 0036 6708;;;;N;;;;;
+32C6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JULY;So;0;L;<compat> 0037 6708;;;;N;;;;;
+32C7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR AUGUST;So;0;L;<compat> 0038 6708;;;;N;;;;;
+32C8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR SEPTEMBER;So;0;L;<compat> 0039 6708;;;;N;;;;;
+32C9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR OCTOBER;So;0;L;<compat> 0031 0030 6708;;;;N;;;;;
+32CA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR NOVEMBER;So;0;L;<compat> 0031 0031 6708;;;;N;;;;;
+32CB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DECEMBER;So;0;L;<compat> 0031 0032 6708;;;;N;;;;;
+32CC;SQUARE HG;So;0;ON;<square> 0048 0067;;;;N;;;;;
+32CD;SQUARE ERG;So;0;ON;<square> 0065 0072 0067;;;;N;;;;;
+32CE;SQUARE EV;So;0;ON;<square> 0065 0056;;;;N;;;;;
+32CF;LIMITED LIABILITY SIGN;So;0;ON;<square> 004C 0054 0044;;;;N;;;;;
+32D0;CIRCLED KATAKANA A;So;0;L;<circle> 30A2;;;;N;;;;;
+32D1;CIRCLED KATAKANA I;So;0;L;<circle> 30A4;;;;N;;;;;
+32D2;CIRCLED KATAKANA U;So;0;L;<circle> 30A6;;;;N;;;;;
+32D3;CIRCLED KATAKANA E;So;0;L;<circle> 30A8;;;;N;;;;;
+32D4;CIRCLED KATAKANA O;So;0;L;<circle> 30AA;;;;N;;;;;
+32D5;CIRCLED KATAKANA KA;So;0;L;<circle> 30AB;;;;N;;;;;
+32D6;CIRCLED KATAKANA KI;So;0;L;<circle> 30AD;;;;N;;;;;
+32D7;CIRCLED KATAKANA KU;So;0;L;<circle> 30AF;;;;N;;;;;
+32D8;CIRCLED KATAKANA KE;So;0;L;<circle> 30B1;;;;N;;;;;
+32D9;CIRCLED KATAKANA KO;So;0;L;<circle> 30B3;;;;N;;;;;
+32DA;CIRCLED KATAKANA SA;So;0;L;<circle> 30B5;;;;N;;;;;
+32DB;CIRCLED KATAKANA SI;So;0;L;<circle> 30B7;;;;N;;;;;
+32DC;CIRCLED KATAKANA SU;So;0;L;<circle> 30B9;;;;N;;;;;
+32DD;CIRCLED KATAKANA SE;So;0;L;<circle> 30BB;;;;N;;;;;
+32DE;CIRCLED KATAKANA SO;So;0;L;<circle> 30BD;;;;N;;;;;
+32DF;CIRCLED KATAKANA TA;So;0;L;<circle> 30BF;;;;N;;;;;
+32E0;CIRCLED KATAKANA TI;So;0;L;<circle> 30C1;;;;N;;;;;
+32E1;CIRCLED KATAKANA TU;So;0;L;<circle> 30C4;;;;N;;;;;
+32E2;CIRCLED KATAKANA TE;So;0;L;<circle> 30C6;;;;N;;;;;
+32E3;CIRCLED KATAKANA TO;So;0;L;<circle> 30C8;;;;N;;;;;
+32E4;CIRCLED KATAKANA NA;So;0;L;<circle> 30CA;;;;N;;;;;
+32E5;CIRCLED KATAKANA NI;So;0;L;<circle> 30CB;;;;N;;;;;
+32E6;CIRCLED KATAKANA NU;So;0;L;<circle> 30CC;;;;N;;;;;
+32E7;CIRCLED KATAKANA NE;So;0;L;<circle> 30CD;;;;N;;;;;
+32E8;CIRCLED KATAKANA NO;So;0;L;<circle> 30CE;;;;N;;;;;
+32E9;CIRCLED KATAKANA HA;So;0;L;<circle> 30CF;;;;N;;;;;
+32EA;CIRCLED KATAKANA HI;So;0;L;<circle> 30D2;;;;N;;;;;
+32EB;CIRCLED KATAKANA HU;So;0;L;<circle> 30D5;;;;N;;;;;
+32EC;CIRCLED KATAKANA HE;So;0;L;<circle> 30D8;;;;N;;;;;
+32ED;CIRCLED KATAKANA HO;So;0;L;<circle> 30DB;;;;N;;;;;
+32EE;CIRCLED KATAKANA MA;So;0;L;<circle> 30DE;;;;N;;;;;
+32EF;CIRCLED KATAKANA MI;So;0;L;<circle> 30DF;;;;N;;;;;
+32F0;CIRCLED KATAKANA MU;So;0;L;<circle> 30E0;;;;N;;;;;
+32F1;CIRCLED KATAKANA ME;So;0;L;<circle> 30E1;;;;N;;;;;
+32F2;CIRCLED KATAKANA MO;So;0;L;<circle> 30E2;;;;N;;;;;
+32F3;CIRCLED KATAKANA YA;So;0;L;<circle> 30E4;;;;N;;;;;
+32F4;CIRCLED KATAKANA YU;So;0;L;<circle> 30E6;;;;N;;;;;
+32F5;CIRCLED KATAKANA YO;So;0;L;<circle> 30E8;;;;N;;;;;
+32F6;CIRCLED KATAKANA RA;So;0;L;<circle> 30E9;;;;N;;;;;
+32F7;CIRCLED KATAKANA RI;So;0;L;<circle> 30EA;;;;N;;;;;
+32F8;CIRCLED KATAKANA RU;So;0;L;<circle> 30EB;;;;N;;;;;
+32F9;CIRCLED KATAKANA RE;So;0;L;<circle> 30EC;;;;N;;;;;
+32FA;CIRCLED KATAKANA RO;So;0;L;<circle> 30ED;;;;N;;;;;
+32FB;CIRCLED KATAKANA WA;So;0;L;<circle> 30EF;;;;N;;;;;
+32FC;CIRCLED KATAKANA WI;So;0;L;<circle> 30F0;;;;N;;;;;
+32FD;CIRCLED KATAKANA WE;So;0;L;<circle> 30F1;;;;N;;;;;
+32FE;CIRCLED KATAKANA WO;So;0;L;<circle> 30F2;;;;N;;;;;
+3300;SQUARE APAATO;So;0;L;<square> 30A2 30D1 30FC 30C8;;;;N;SQUARED APAATO;;;;
+3301;SQUARE ARUHUA;So;0;L;<square> 30A2 30EB 30D5 30A1;;;;N;SQUARED ARUHUA;;;;
+3302;SQUARE ANPEA;So;0;L;<square> 30A2 30F3 30DA 30A2;;;;N;SQUARED ANPEA;;;;
+3303;SQUARE AARU;So;0;L;<square> 30A2 30FC 30EB;;;;N;SQUARED AARU;;;;
+3304;SQUARE ININGU;So;0;L;<square> 30A4 30CB 30F3 30B0;;;;N;SQUARED ININGU;;;;
+3305;SQUARE INTI;So;0;L;<square> 30A4 30F3 30C1;;;;N;SQUARED INTI;;;;
+3306;SQUARE UON;So;0;L;<square> 30A6 30A9 30F3;;;;N;SQUARED UON;;;;
+3307;SQUARE ESUKUUDO;So;0;L;<square> 30A8 30B9 30AF 30FC 30C9;;;;N;SQUARED ESUKUUDO;;;;
+3308;SQUARE EEKAA;So;0;L;<square> 30A8 30FC 30AB 30FC;;;;N;SQUARED EEKAA;;;;
+3309;SQUARE ONSU;So;0;L;<square> 30AA 30F3 30B9;;;;N;SQUARED ONSU;;;;
+330A;SQUARE OOMU;So;0;L;<square> 30AA 30FC 30E0;;;;N;SQUARED OOMU;;;;
+330B;SQUARE KAIRI;So;0;L;<square> 30AB 30A4 30EA;;;;N;SQUARED KAIRI;;;;
+330C;SQUARE KARATTO;So;0;L;<square> 30AB 30E9 30C3 30C8;;;;N;SQUARED KARATTO;;;;
+330D;SQUARE KARORII;So;0;L;<square> 30AB 30ED 30EA 30FC;;;;N;SQUARED KARORII;;;;
+330E;SQUARE GARON;So;0;L;<square> 30AC 30ED 30F3;;;;N;SQUARED GARON;;;;
+330F;SQUARE GANMA;So;0;L;<square> 30AC 30F3 30DE;;;;N;SQUARED GANMA;;;;
+3310;SQUARE GIGA;So;0;L;<square> 30AE 30AC;;;;N;SQUARED GIGA;;;;
+3311;SQUARE GINII;So;0;L;<square> 30AE 30CB 30FC;;;;N;SQUARED GINII;;;;
+3312;SQUARE KYURII;So;0;L;<square> 30AD 30E5 30EA 30FC;;;;N;SQUARED KYURII;;;;
+3313;SQUARE GIRUDAA;So;0;L;<square> 30AE 30EB 30C0 30FC;;;;N;SQUARED GIRUDAA;;;;
+3314;SQUARE KIRO;So;0;L;<square> 30AD 30ED;;;;N;SQUARED KIRO;;;;
+3315;SQUARE KIROGURAMU;So;0;L;<square> 30AD 30ED 30B0 30E9 30E0;;;;N;SQUARED KIROGURAMU;;;;
+3316;SQUARE KIROMEETORU;So;0;L;<square> 30AD 30ED 30E1 30FC 30C8 30EB;;;;N;SQUARED KIROMEETORU;;;;
+3317;SQUARE KIROWATTO;So;0;L;<square> 30AD 30ED 30EF 30C3 30C8;;;;N;SQUARED KIROWATTO;;;;
+3318;SQUARE GURAMU;So;0;L;<square> 30B0 30E9 30E0;;;;N;SQUARED GURAMU;;;;
+3319;SQUARE GURAMUTON;So;0;L;<square> 30B0 30E9 30E0 30C8 30F3;;;;N;SQUARED GURAMUTON;;;;
+331A;SQUARE KURUZEIRO;So;0;L;<square> 30AF 30EB 30BC 30A4 30ED;;;;N;SQUARED KURUZEIRO;;;;
+331B;SQUARE KUROONE;So;0;L;<square> 30AF 30ED 30FC 30CD;;;;N;SQUARED KUROONE;;;;
+331C;SQUARE KEESU;So;0;L;<square> 30B1 30FC 30B9;;;;N;SQUARED KEESU;;;;
+331D;SQUARE KORUNA;So;0;L;<square> 30B3 30EB 30CA;;;;N;SQUARED KORUNA;;;;
+331E;SQUARE KOOPO;So;0;L;<square> 30B3 30FC 30DD;;;;N;SQUARED KOOPO;;;;
+331F;SQUARE SAIKURU;So;0;L;<square> 30B5 30A4 30AF 30EB;;;;N;SQUARED SAIKURU;;;;
+3320;SQUARE SANTIIMU;So;0;L;<square> 30B5 30F3 30C1 30FC 30E0;;;;N;SQUARED SANTIIMU;;;;
+3321;SQUARE SIRINGU;So;0;L;<square> 30B7 30EA 30F3 30B0;;;;N;SQUARED SIRINGU;;;;
+3322;SQUARE SENTI;So;0;L;<square> 30BB 30F3 30C1;;;;N;SQUARED SENTI;;;;
+3323;SQUARE SENTO;So;0;L;<square> 30BB 30F3 30C8;;;;N;SQUARED SENTO;;;;
+3324;SQUARE DAASU;So;0;L;<square> 30C0 30FC 30B9;;;;N;SQUARED DAASU;;;;
+3325;SQUARE DESI;So;0;L;<square> 30C7 30B7;;;;N;SQUARED DESI;;;;
+3326;SQUARE DORU;So;0;L;<square> 30C9 30EB;;;;N;SQUARED DORU;;;;
+3327;SQUARE TON;So;0;L;<square> 30C8 30F3;;;;N;SQUARED TON;;;;
+3328;SQUARE NANO;So;0;L;<square> 30CA 30CE;;;;N;SQUARED NANO;;;;
+3329;SQUARE NOTTO;So;0;L;<square> 30CE 30C3 30C8;;;;N;SQUARED NOTTO;;;;
+332A;SQUARE HAITU;So;0;L;<square> 30CF 30A4 30C4;;;;N;SQUARED HAITU;;;;
+332B;SQUARE PAASENTO;So;0;L;<square> 30D1 30FC 30BB 30F3 30C8;;;;N;SQUARED PAASENTO;;;;
+332C;SQUARE PAATU;So;0;L;<square> 30D1 30FC 30C4;;;;N;SQUARED PAATU;;;;
+332D;SQUARE BAARERU;So;0;L;<square> 30D0 30FC 30EC 30EB;;;;N;SQUARED BAARERU;;;;
+332E;SQUARE PIASUTORU;So;0;L;<square> 30D4 30A2 30B9 30C8 30EB;;;;N;SQUARED PIASUTORU;;;;
+332F;SQUARE PIKURU;So;0;L;<square> 30D4 30AF 30EB;;;;N;SQUARED PIKURU;;;;
+3330;SQUARE PIKO;So;0;L;<square> 30D4 30B3;;;;N;SQUARED PIKO;;;;
+3331;SQUARE BIRU;So;0;L;<square> 30D3 30EB;;;;N;SQUARED BIRU;;;;
+3332;SQUARE HUARADDO;So;0;L;<square> 30D5 30A1 30E9 30C3 30C9;;;;N;SQUARED HUARADDO;;;;
+3333;SQUARE HUIITO;So;0;L;<square> 30D5 30A3 30FC 30C8;;;;N;SQUARED HUIITO;;;;
+3334;SQUARE BUSSYERU;So;0;L;<square> 30D6 30C3 30B7 30A7 30EB;;;;N;SQUARED BUSSYERU;;;;
+3335;SQUARE HURAN;So;0;L;<square> 30D5 30E9 30F3;;;;N;SQUARED HURAN;;;;
+3336;SQUARE HEKUTAARU;So;0;L;<square> 30D8 30AF 30BF 30FC 30EB;;;;N;SQUARED HEKUTAARU;;;;
+3337;SQUARE PESO;So;0;L;<square> 30DA 30BD;;;;N;SQUARED PESO;;;;
+3338;SQUARE PENIHI;So;0;L;<square> 30DA 30CB 30D2;;;;N;SQUARED PENIHI;;;;
+3339;SQUARE HERUTU;So;0;L;<square> 30D8 30EB 30C4;;;;N;SQUARED HERUTU;;;;
+333A;SQUARE PENSU;So;0;L;<square> 30DA 30F3 30B9;;;;N;SQUARED PENSU;;;;
+333B;SQUARE PEEZI;So;0;L;<square> 30DA 30FC 30B8;;;;N;SQUARED PEEZI;;;;
+333C;SQUARE BEETA;So;0;L;<square> 30D9 30FC 30BF;;;;N;SQUARED BEETA;;;;
+333D;SQUARE POINTO;So;0;L;<square> 30DD 30A4 30F3 30C8;;;;N;SQUARED POINTO;;;;
+333E;SQUARE BORUTO;So;0;L;<square> 30DC 30EB 30C8;;;;N;SQUARED BORUTO;;;;
+333F;SQUARE HON;So;0;L;<square> 30DB 30F3;;;;N;SQUARED HON;;;;
+3340;SQUARE PONDO;So;0;L;<square> 30DD 30F3 30C9;;;;N;SQUARED PONDO;;;;
+3341;SQUARE HOORU;So;0;L;<square> 30DB 30FC 30EB;;;;N;SQUARED HOORU;;;;
+3342;SQUARE HOON;So;0;L;<square> 30DB 30FC 30F3;;;;N;SQUARED HOON;;;;
+3343;SQUARE MAIKURO;So;0;L;<square> 30DE 30A4 30AF 30ED;;;;N;SQUARED MAIKURO;;;;
+3344;SQUARE MAIRU;So;0;L;<square> 30DE 30A4 30EB;;;;N;SQUARED MAIRU;;;;
+3345;SQUARE MAHHA;So;0;L;<square> 30DE 30C3 30CF;;;;N;SQUARED MAHHA;;;;
+3346;SQUARE MARUKU;So;0;L;<square> 30DE 30EB 30AF;;;;N;SQUARED MARUKU;;;;
+3347;SQUARE MANSYON;So;0;L;<square> 30DE 30F3 30B7 30E7 30F3;;;;N;SQUARED MANSYON;;;;
+3348;SQUARE MIKURON;So;0;L;<square> 30DF 30AF 30ED 30F3;;;;N;SQUARED MIKURON;;;;
+3349;SQUARE MIRI;So;0;L;<square> 30DF 30EA;;;;N;SQUARED MIRI;;;;
+334A;SQUARE MIRIBAARU;So;0;L;<square> 30DF 30EA 30D0 30FC 30EB;;;;N;SQUARED MIRIBAARU;;;;
+334B;SQUARE MEGA;So;0;L;<square> 30E1 30AC;;;;N;SQUARED MEGA;;;;
+334C;SQUARE MEGATON;So;0;L;<square> 30E1 30AC 30C8 30F3;;;;N;SQUARED MEGATON;;;;
+334D;SQUARE MEETORU;So;0;L;<square> 30E1 30FC 30C8 30EB;;;;N;SQUARED MEETORU;;;;
+334E;SQUARE YAADO;So;0;L;<square> 30E4 30FC 30C9;;;;N;SQUARED YAADO;;;;
+334F;SQUARE YAARU;So;0;L;<square> 30E4 30FC 30EB;;;;N;SQUARED YAARU;;;;
+3350;SQUARE YUAN;So;0;L;<square> 30E6 30A2 30F3;;;;N;SQUARED YUAN;;;;
+3351;SQUARE RITTORU;So;0;L;<square> 30EA 30C3 30C8 30EB;;;;N;SQUARED RITTORU;;;;
+3352;SQUARE RIRA;So;0;L;<square> 30EA 30E9;;;;N;SQUARED RIRA;;;;
+3353;SQUARE RUPII;So;0;L;<square> 30EB 30D4 30FC;;;;N;SQUARED RUPII;;;;
+3354;SQUARE RUUBURU;So;0;L;<square> 30EB 30FC 30D6 30EB;;;;N;SQUARED RUUBURU;;;;
+3355;SQUARE REMU;So;0;L;<square> 30EC 30E0;;;;N;SQUARED REMU;;;;
+3356;SQUARE RENTOGEN;So;0;L;<square> 30EC 30F3 30C8 30B2 30F3;;;;N;SQUARED RENTOGEN;;;;
+3357;SQUARE WATTO;So;0;L;<square> 30EF 30C3 30C8;;;;N;SQUARED WATTO;;;;
+3358;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ZERO;So;0;L;<compat> 0030 70B9;;;;N;;;;;
+3359;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ONE;So;0;L;<compat> 0031 70B9;;;;N;;;;;
+335A;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWO;So;0;L;<compat> 0032 70B9;;;;N;;;;;
+335B;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THREE;So;0;L;<compat> 0033 70B9;;;;N;;;;;
+335C;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOUR;So;0;L;<compat> 0034 70B9;;;;N;;;;;
+335D;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIVE;So;0;L;<compat> 0035 70B9;;;;N;;;;;
+335E;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIX;So;0;L;<compat> 0036 70B9;;;;N;;;;;
+335F;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVEN;So;0;L;<compat> 0037 70B9;;;;N;;;;;
+3360;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHT;So;0;L;<compat> 0038 70B9;;;;N;;;;;
+3361;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINE;So;0;L;<compat> 0039 70B9;;;;N;;;;;
+3362;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TEN;So;0;L;<compat> 0031 0030 70B9;;;;N;;;;;
+3363;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ELEVEN;So;0;L;<compat> 0031 0031 70B9;;;;N;;;;;
+3364;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWELVE;So;0;L;<compat> 0031 0032 70B9;;;;N;;;;;
+3365;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THIRTEEN;So;0;L;<compat> 0031 0033 70B9;;;;N;;;;;
+3366;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOURTEEN;So;0;L;<compat> 0031 0034 70B9;;;;N;;;;;
+3367;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIFTEEN;So;0;L;<compat> 0031 0035 70B9;;;;N;;;;;
+3368;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIXTEEN;So;0;L;<compat> 0031 0036 70B9;;;;N;;;;;
+3369;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVENTEEN;So;0;L;<compat> 0031 0037 70B9;;;;N;;;;;
+336A;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHTEEN;So;0;L;<compat> 0031 0038 70B9;;;;N;;;;;
+336B;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINETEEN;So;0;L;<compat> 0031 0039 70B9;;;;N;;;;;
+336C;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY;So;0;L;<compat> 0032 0030 70B9;;;;N;;;;;
+336D;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-ONE;So;0;L;<compat> 0032 0031 70B9;;;;N;;;;;
+336E;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-TWO;So;0;L;<compat> 0032 0032 70B9;;;;N;;;;;
+336F;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-THREE;So;0;L;<compat> 0032 0033 70B9;;;;N;;;;;
+3370;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-FOUR;So;0;L;<compat> 0032 0034 70B9;;;;N;;;;;
+3371;SQUARE HPA;So;0;L;<square> 0068 0050 0061;;;;N;;;;;
+3372;SQUARE DA;So;0;L;<square> 0064 0061;;;;N;;;;;
+3373;SQUARE AU;So;0;L;<square> 0041 0055;;;;N;;;;;
+3374;SQUARE BAR;So;0;L;<square> 0062 0061 0072;;;;N;;;;;
+3375;SQUARE OV;So;0;L;<square> 006F 0056;;;;N;;;;;
+3376;SQUARE PC;So;0;L;<square> 0070 0063;;;;N;;;;;
+3377;SQUARE DM;So;0;ON;<square> 0064 006D;;;;N;;;;;
+3378;SQUARE DM SQUARED;So;0;ON;<square> 0064 006D 00B2;;;;N;;;;;
+3379;SQUARE DM CUBED;So;0;ON;<square> 0064 006D 00B3;;;;N;;;;;
+337A;SQUARE IU;So;0;ON;<square> 0049 0055;;;;N;;;;;
+337B;SQUARE ERA NAME HEISEI;So;0;L;<square> 5E73 6210;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME HEISEI;;;;
+337C;SQUARE ERA NAME SYOUWA;So;0;L;<square> 662D 548C;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME SYOUWA;;;;
+337D;SQUARE ERA NAME TAISYOU;So;0;L;<square> 5927 6B63;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME TAISYOU;;;;
+337E;SQUARE ERA NAME MEIZI;So;0;L;<square> 660E 6CBB;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME MEIZI;;;;
+337F;SQUARE CORPORATION;So;0;L;<square> 682A 5F0F 4F1A 793E;;;;N;SQUARED FOUR IDEOGRAPHS CORPORATION;;;;
+3380;SQUARE PA AMPS;So;0;L;<square> 0070 0041;;;;N;SQUARED PA AMPS;;;;
+3381;SQUARE NA;So;0;L;<square> 006E 0041;;;;N;SQUARED NA;;;;
+3382;SQUARE MU A;So;0;L;<square> 03BC 0041;;;;N;SQUARED MU A;;;;
+3383;SQUARE MA;So;0;L;<square> 006D 0041;;;;N;SQUARED MA;;;;
+3384;SQUARE KA;So;0;L;<square> 006B 0041;;;;N;SQUARED KA;;;;
+3385;SQUARE KB;So;0;L;<square> 004B 0042;;;;N;SQUARED KB;;;;
+3386;SQUARE MB;So;0;L;<square> 004D 0042;;;;N;SQUARED MB;;;;
+3387;SQUARE GB;So;0;L;<square> 0047 0042;;;;N;SQUARED GB;;;;
+3388;SQUARE CAL;So;0;L;<square> 0063 0061 006C;;;;N;SQUARED CAL;;;;
+3389;SQUARE KCAL;So;0;L;<square> 006B 0063 0061 006C;;;;N;SQUARED KCAL;;;;
+338A;SQUARE PF;So;0;L;<square> 0070 0046;;;;N;SQUARED PF;;;;
+338B;SQUARE NF;So;0;L;<square> 006E 0046;;;;N;SQUARED NF;;;;
+338C;SQUARE MU F;So;0;L;<square> 03BC 0046;;;;N;SQUARED MU F;;;;
+338D;SQUARE MU G;So;0;L;<square> 03BC 0067;;;;N;SQUARED MU G;;;;
+338E;SQUARE MG;So;0;L;<square> 006D 0067;;;;N;SQUARED MG;;;;
+338F;SQUARE KG;So;0;L;<square> 006B 0067;;;;N;SQUARED KG;;;;
+3390;SQUARE HZ;So;0;L;<square> 0048 007A;;;;N;SQUARED HZ;;;;
+3391;SQUARE KHZ;So;0;L;<square> 006B 0048 007A;;;;N;SQUARED KHZ;;;;
+3392;SQUARE MHZ;So;0;L;<square> 004D 0048 007A;;;;N;SQUARED MHZ;;;;
+3393;SQUARE GHZ;So;0;L;<square> 0047 0048 007A;;;;N;SQUARED GHZ;;;;
+3394;SQUARE THZ;So;0;L;<square> 0054 0048 007A;;;;N;SQUARED THZ;;;;
+3395;SQUARE MU L;So;0;L;<square> 03BC 2113;;;;N;SQUARED MU L;;;;
+3396;SQUARE ML;So;0;L;<square> 006D 2113;;;;N;SQUARED ML;;;;
+3397;SQUARE DL;So;0;L;<square> 0064 2113;;;;N;SQUARED DL;;;;
+3398;SQUARE KL;So;0;L;<square> 006B 2113;;;;N;SQUARED KL;;;;
+3399;SQUARE FM;So;0;L;<square> 0066 006D;;;;N;SQUARED FM;;;;
+339A;SQUARE NM;So;0;L;<square> 006E 006D;;;;N;SQUARED NM;;;;
+339B;SQUARE MU M;So;0;L;<square> 03BC 006D;;;;N;SQUARED MU M;;;;
+339C;SQUARE MM;So;0;L;<square> 006D 006D;;;;N;SQUARED MM;;;;
+339D;SQUARE CM;So;0;L;<square> 0063 006D;;;;N;SQUARED CM;;;;
+339E;SQUARE KM;So;0;L;<square> 006B 006D;;;;N;SQUARED KM;;;;
+339F;SQUARE MM SQUARED;So;0;L;<square> 006D 006D 00B2;;;;N;SQUARED MM SQUARED;;;;
+33A0;SQUARE CM SQUARED;So;0;L;<square> 0063 006D 00B2;;;;N;SQUARED CM SQUARED;;;;
+33A1;SQUARE M SQUARED;So;0;L;<square> 006D 00B2;;;;N;SQUARED M SQUARED;;;;
+33A2;SQUARE KM SQUARED;So;0;L;<square> 006B 006D 00B2;;;;N;SQUARED KM SQUARED;;;;
+33A3;SQUARE MM CUBED;So;0;L;<square> 006D 006D 00B3;;;;N;SQUARED MM CUBED;;;;
+33A4;SQUARE CM CUBED;So;0;L;<square> 0063 006D 00B3;;;;N;SQUARED CM CUBED;;;;
+33A5;SQUARE M CUBED;So;0;L;<square> 006D 00B3;;;;N;SQUARED M CUBED;;;;
+33A6;SQUARE KM CUBED;So;0;L;<square> 006B 006D 00B3;;;;N;SQUARED KM CUBED;;;;
+33A7;SQUARE M OVER S;So;0;L;<square> 006D 2215 0073;;;;N;SQUARED M OVER S;;;;
+33A8;SQUARE M OVER S SQUARED;So;0;L;<square> 006D 2215 0073 00B2;;;;N;SQUARED M OVER S SQUARED;;;;
+33A9;SQUARE PA;So;0;L;<square> 0050 0061;;;;N;SQUARED PA;;;;
+33AA;SQUARE KPA;So;0;L;<square> 006B 0050 0061;;;;N;SQUARED KPA;;;;
+33AB;SQUARE MPA;So;0;L;<square> 004D 0050 0061;;;;N;SQUARED MPA;;;;
+33AC;SQUARE GPA;So;0;L;<square> 0047 0050 0061;;;;N;SQUARED GPA;;;;
+33AD;SQUARE RAD;So;0;L;<square> 0072 0061 0064;;;;N;SQUARED RAD;;;;
+33AE;SQUARE RAD OVER S;So;0;L;<square> 0072 0061 0064 2215 0073;;;;N;SQUARED RAD OVER S;;;;
+33AF;SQUARE RAD OVER S SQUARED;So;0;L;<square> 0072 0061 0064 2215 0073 00B2;;;;N;SQUARED RAD OVER S SQUARED;;;;
+33B0;SQUARE PS;So;0;L;<square> 0070 0073;;;;N;SQUARED PS;;;;
+33B1;SQUARE NS;So;0;L;<square> 006E 0073;;;;N;SQUARED NS;;;;
+33B2;SQUARE MU S;So;0;L;<square> 03BC 0073;;;;N;SQUARED MU S;;;;
+33B3;SQUARE MS;So;0;L;<square> 006D 0073;;;;N;SQUARED MS;;;;
+33B4;SQUARE PV;So;0;L;<square> 0070 0056;;;;N;SQUARED PV;;;;
+33B5;SQUARE NV;So;0;L;<square> 006E 0056;;;;N;SQUARED NV;;;;
+33B6;SQUARE MU V;So;0;L;<square> 03BC 0056;;;;N;SQUARED MU V;;;;
+33B7;SQUARE MV;So;0;L;<square> 006D 0056;;;;N;SQUARED MV;;;;
+33B8;SQUARE KV;So;0;L;<square> 006B 0056;;;;N;SQUARED KV;;;;
+33B9;SQUARE MV MEGA;So;0;L;<square> 004D 0056;;;;N;SQUARED MV MEGA;;;;
+33BA;SQUARE PW;So;0;L;<square> 0070 0057;;;;N;SQUARED PW;;;;
+33BB;SQUARE NW;So;0;L;<square> 006E 0057;;;;N;SQUARED NW;;;;
+33BC;SQUARE MU W;So;0;L;<square> 03BC 0057;;;;N;SQUARED MU W;;;;
+33BD;SQUARE MW;So;0;L;<square> 006D 0057;;;;N;SQUARED MW;;;;
+33BE;SQUARE KW;So;0;L;<square> 006B 0057;;;;N;SQUARED KW;;;;
+33BF;SQUARE MW MEGA;So;0;L;<square> 004D 0057;;;;N;SQUARED MW MEGA;;;;
+33C0;SQUARE K OHM;So;0;L;<square> 006B 03A9;;;;N;SQUARED K OHM;;;;
+33C1;SQUARE M OHM;So;0;L;<square> 004D 03A9;;;;N;SQUARED M OHM;;;;
+33C2;SQUARE AM;So;0;L;<square> 0061 002E 006D 002E;;;;N;SQUARED AM;;;;
+33C3;SQUARE BQ;So;0;L;<square> 0042 0071;;;;N;SQUARED BQ;;;;
+33C4;SQUARE CC;So;0;L;<square> 0063 0063;;;;N;SQUARED CC;;;;
+33C5;SQUARE CD;So;0;L;<square> 0063 0064;;;;N;SQUARED CD;;;;
+33C6;SQUARE C OVER KG;So;0;L;<square> 0043 2215 006B 0067;;;;N;SQUARED C OVER KG;;;;
+33C7;SQUARE CO;So;0;L;<square> 0043 006F 002E;;;;N;SQUARED CO;;;;
+33C8;SQUARE DB;So;0;L;<square> 0064 0042;;;;N;SQUARED DB;;;;
+33C9;SQUARE GY;So;0;L;<square> 0047 0079;;;;N;SQUARED GY;;;;
+33CA;SQUARE HA;So;0;L;<square> 0068 0061;;;;N;SQUARED HA;;;;
+33CB;SQUARE HP;So;0;L;<square> 0048 0050;;;;N;SQUARED HP;;;;
+33CC;SQUARE IN;So;0;L;<square> 0069 006E;;;;N;SQUARED IN;;;;
+33CD;SQUARE KK;So;0;L;<square> 004B 004B;;;;N;SQUARED KK;;;;
+33CE;SQUARE KM CAPITAL;So;0;L;<square> 004B 004D;;;;N;SQUARED KM CAPITAL;;;;
+33CF;SQUARE KT;So;0;L;<square> 006B 0074;;;;N;SQUARED KT;;;;
+33D0;SQUARE LM;So;0;L;<square> 006C 006D;;;;N;SQUARED LM;;;;
+33D1;SQUARE LN;So;0;L;<square> 006C 006E;;;;N;SQUARED LN;;;;
+33D2;SQUARE LOG;So;0;L;<square> 006C 006F 0067;;;;N;SQUARED LOG;;;;
+33D3;SQUARE LX;So;0;L;<square> 006C 0078;;;;N;SQUARED LX;;;;
+33D4;SQUARE MB SMALL;So;0;L;<square> 006D 0062;;;;N;SQUARED MB SMALL;;;;
+33D5;SQUARE MIL;So;0;L;<square> 006D 0069 006C;;;;N;SQUARED MIL;;;;
+33D6;SQUARE MOL;So;0;L;<square> 006D 006F 006C;;;;N;SQUARED MOL;;;;
+33D7;SQUARE PH;So;0;L;<square> 0050 0048;;;;N;SQUARED PH;;;;
+33D8;SQUARE PM;So;0;L;<square> 0070 002E 006D 002E;;;;N;SQUARED PM;;;;
+33D9;SQUARE PPM;So;0;L;<square> 0050 0050 004D;;;;N;SQUARED PPM;;;;
+33DA;SQUARE PR;So;0;L;<square> 0050 0052;;;;N;SQUARED PR;;;;
+33DB;SQUARE SR;So;0;L;<square> 0073 0072;;;;N;SQUARED SR;;;;
+33DC;SQUARE SV;So;0;L;<square> 0053 0076;;;;N;SQUARED SV;;;;
+33DD;SQUARE WB;So;0;L;<square> 0057 0062;;;;N;SQUARED WB;;;;
+33DE;SQUARE V OVER M;So;0;ON;<square> 0056 2215 006D;;;;N;;;;;
+33DF;SQUARE A OVER M;So;0;ON;<square> 0041 2215 006D;;;;N;;;;;
+33E0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ONE;So;0;L;<compat> 0031 65E5;;;;N;;;;;
+33E1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWO;So;0;L;<compat> 0032 65E5;;;;N;;;;;
+33E2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THREE;So;0;L;<compat> 0033 65E5;;;;N;;;;;
+33E3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOUR;So;0;L;<compat> 0034 65E5;;;;N;;;;;
+33E4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIVE;So;0;L;<compat> 0035 65E5;;;;N;;;;;
+33E5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIX;So;0;L;<compat> 0036 65E5;;;;N;;;;;
+33E6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVEN;So;0;L;<compat> 0037 65E5;;;;N;;;;;
+33E7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHT;So;0;L;<compat> 0038 65E5;;;;N;;;;;
+33E8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINE;So;0;L;<compat> 0039 65E5;;;;N;;;;;
+33E9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TEN;So;0;L;<compat> 0031 0030 65E5;;;;N;;;;;
+33EA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ELEVEN;So;0;L;<compat> 0031 0031 65E5;;;;N;;;;;
+33EB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWELVE;So;0;L;<compat> 0031 0032 65E5;;;;N;;;;;
+33EC;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTEEN;So;0;L;<compat> 0031 0033 65E5;;;;N;;;;;
+33ED;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOURTEEN;So;0;L;<compat> 0031 0034 65E5;;;;N;;;;;
+33EE;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIFTEEN;So;0;L;<compat> 0031 0035 65E5;;;;N;;;;;
+33EF;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIXTEEN;So;0;L;<compat> 0031 0036 65E5;;;;N;;;;;
+33F0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVENTEEN;So;0;L;<compat> 0031 0037 65E5;;;;N;;;;;
+33F1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHTEEN;So;0;L;<compat> 0031 0038 65E5;;;;N;;;;;
+33F2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINETEEN;So;0;L;<compat> 0031 0039 65E5;;;;N;;;;;
+33F3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY;So;0;L;<compat> 0032 0030 65E5;;;;N;;;;;
+33F4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-ONE;So;0;L;<compat> 0032 0031 65E5;;;;N;;;;;
+33F5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-TWO;So;0;L;<compat> 0032 0032 65E5;;;;N;;;;;
+33F6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-THREE;So;0;L;<compat> 0032 0033 65E5;;;;N;;;;;
+33F7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FOUR;So;0;L;<compat> 0032 0034 65E5;;;;N;;;;;
+33F8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FIVE;So;0;L;<compat> 0032 0035 65E5;;;;N;;;;;
+33F9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SIX;So;0;L;<compat> 0032 0036 65E5;;;;N;;;;;
+33FA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SEVEN;So;0;L;<compat> 0032 0037 65E5;;;;N;;;;;
+33FB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-EIGHT;So;0;L;<compat> 0032 0038 65E5;;;;N;;;;;
+33FC;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-NINE;So;0;L;<compat> 0032 0039 65E5;;;;N;;;;;
+33FD;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY;So;0;L;<compat> 0033 0030 65E5;;;;N;;;;;
+33FE;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY-ONE;So;0;L;<compat> 0033 0031 65E5;;;;N;;;;;
+33FF;SQUARE GAL;So;0;ON;<square> 0067 0061 006C;;;;N;;;;;
+3400;<CJK Ideograph Extension A, First>;Lo;0;L;;;;;N;;;;;
+4DB5;<CJK Ideograph Extension A, Last>;Lo;0;L;;;;;N;;;;;
+4DC0;HEXAGRAM FOR THE CREATIVE HEAVEN;So;0;ON;;;;;N;;;;;
+4DC1;HEXAGRAM FOR THE RECEPTIVE EARTH;So;0;ON;;;;;N;;;;;
+4DC2;HEXAGRAM FOR DIFFICULTY AT THE BEGINNING;So;0;ON;;;;;N;;;;;
+4DC3;HEXAGRAM FOR YOUTHFUL FOLLY;So;0;ON;;;;;N;;;;;
+4DC4;HEXAGRAM FOR WAITING;So;0;ON;;;;;N;;;;;
+4DC5;HEXAGRAM FOR CONFLICT;So;0;ON;;;;;N;;;;;
+4DC6;HEXAGRAM FOR THE ARMY;So;0;ON;;;;;N;;;;;
+4DC7;HEXAGRAM FOR HOLDING TOGETHER;So;0;ON;;;;;N;;;;;
+4DC8;HEXAGRAM FOR SMALL TAMING;So;0;ON;;;;;N;;;;;
+4DC9;HEXAGRAM FOR TREADING;So;0;ON;;;;;N;;;;;
+4DCA;HEXAGRAM FOR PEACE;So;0;ON;;;;;N;;;;;
+4DCB;HEXAGRAM FOR STANDSTILL;So;0;ON;;;;;N;;;;;
+4DCC;HEXAGRAM FOR FELLOWSHIP;So;0;ON;;;;;N;;;;;
+4DCD;HEXAGRAM FOR GREAT POSSESSION;So;0;ON;;;;;N;;;;;
+4DCE;HEXAGRAM FOR MODESTY;So;0;ON;;;;;N;;;;;
+4DCF;HEXAGRAM FOR ENTHUSIASM;So;0;ON;;;;;N;;;;;
+4DD0;HEXAGRAM FOR FOLLOWING;So;0;ON;;;;;N;;;;;
+4DD1;HEXAGRAM FOR WORK ON THE DECAYED;So;0;ON;;;;;N;;;;;
+4DD2;HEXAGRAM FOR APPROACH;So;0;ON;;;;;N;;;;;
+4DD3;HEXAGRAM FOR CONTEMPLATION;So;0;ON;;;;;N;;;;;
+4DD4;HEXAGRAM FOR BITING THROUGH;So;0;ON;;;;;N;;;;;
+4DD5;HEXAGRAM FOR GRACE;So;0;ON;;;;;N;;;;;
+4DD6;HEXAGRAM FOR SPLITTING APART;So;0;ON;;;;;N;;;;;
+4DD7;HEXAGRAM FOR RETURN;So;0;ON;;;;;N;;;;;
+4DD8;HEXAGRAM FOR INNOCENCE;So;0;ON;;;;;N;;;;;
+4DD9;HEXAGRAM FOR GREAT TAMING;So;0;ON;;;;;N;;;;;
+4DDA;HEXAGRAM FOR MOUTH CORNERS;So;0;ON;;;;;N;;;;;
+4DDB;HEXAGRAM FOR GREAT PREPONDERANCE;So;0;ON;;;;;N;;;;;
+4DDC;HEXAGRAM FOR THE ABYSMAL WATER;So;0;ON;;;;;N;;;;;
+4DDD;HEXAGRAM FOR THE CLINGING FIRE;So;0;ON;;;;;N;;;;;
+4DDE;HEXAGRAM FOR INFLUENCE;So;0;ON;;;;;N;;;;;
+4DDF;HEXAGRAM FOR DURATION;So;0;ON;;;;;N;;;;;
+4DE0;HEXAGRAM FOR RETREAT;So;0;ON;;;;;N;;;;;
+4DE1;HEXAGRAM FOR GREAT POWER;So;0;ON;;;;;N;;;;;
+4DE2;HEXAGRAM FOR PROGRESS;So;0;ON;;;;;N;;;;;
+4DE3;HEXAGRAM FOR DARKENING OF THE LIGHT;So;0;ON;;;;;N;;;;;
+4DE4;HEXAGRAM FOR THE FAMILY;So;0;ON;;;;;N;;;;;
+4DE5;HEXAGRAM FOR OPPOSITION;So;0;ON;;;;;N;;;;;
+4DE6;HEXAGRAM FOR OBSTRUCTION;So;0;ON;;;;;N;;;;;
+4DE7;HEXAGRAM FOR DELIVERANCE;So;0;ON;;;;;N;;;;;
+4DE8;HEXAGRAM FOR DECREASE;So;0;ON;;;;;N;;;;;
+4DE9;HEXAGRAM FOR INCREASE;So;0;ON;;;;;N;;;;;
+4DEA;HEXAGRAM FOR BREAKTHROUGH;So;0;ON;;;;;N;;;;;
+4DEB;HEXAGRAM FOR COMING TO MEET;So;0;ON;;;;;N;;;;;
+4DEC;HEXAGRAM FOR GATHERING TOGETHER;So;0;ON;;;;;N;;;;;
+4DED;HEXAGRAM FOR PUSHING UPWARD;So;0;ON;;;;;N;;;;;
+4DEE;HEXAGRAM FOR OPPRESSION;So;0;ON;;;;;N;;;;;
+4DEF;HEXAGRAM FOR THE WELL;So;0;ON;;;;;N;;;;;
+4DF0;HEXAGRAM FOR REVOLUTION;So;0;ON;;;;;N;;;;;
+4DF1;HEXAGRAM FOR THE CAULDRON;So;0;ON;;;;;N;;;;;
+4DF2;HEXAGRAM FOR THE AROUSING THUNDER;So;0;ON;;;;;N;;;;;
+4DF3;HEXAGRAM FOR THE KEEPING STILL MOUNTAIN;So;0;ON;;;;;N;;;;;
+4DF4;HEXAGRAM FOR DEVELOPMENT;So;0;ON;;;;;N;;;;;
+4DF5;HEXAGRAM FOR THE MARRYING MAIDEN;So;0;ON;;;;;N;;;;;
+4DF6;HEXAGRAM FOR ABUNDANCE;So;0;ON;;;;;N;;;;;
+4DF7;HEXAGRAM FOR THE WANDERER;So;0;ON;;;;;N;;;;;
+4DF8;HEXAGRAM FOR THE GENTLE WIND;So;0;ON;;;;;N;;;;;
+4DF9;HEXAGRAM FOR THE JOYOUS LAKE;So;0;ON;;;;;N;;;;;
+4DFA;HEXAGRAM FOR DISPERSION;So;0;ON;;;;;N;;;;;
+4DFB;HEXAGRAM FOR LIMITATION;So;0;ON;;;;;N;;;;;
+4DFC;HEXAGRAM FOR INNER TRUTH;So;0;ON;;;;;N;;;;;
+4DFD;HEXAGRAM FOR SMALL PREPONDERANCE;So;0;ON;;;;;N;;;;;
+4DFE;HEXAGRAM FOR AFTER COMPLETION;So;0;ON;;;;;N;;;;;
+4DFF;HEXAGRAM FOR BEFORE COMPLETION;So;0;ON;;;;;N;;;;;
+4E00;<CJK Ideograph, First>;Lo;0;L;;;;;N;;;;;
+9FEF;<CJK Ideograph, Last>;Lo;0;L;;;;;N;;;;;
+A000;YI SYLLABLE IT;Lo;0;L;;;;;N;;;;;
+A001;YI SYLLABLE IX;Lo;0;L;;;;;N;;;;;
+A002;YI SYLLABLE I;Lo;0;L;;;;;N;;;;;
+A003;YI SYLLABLE IP;Lo;0;L;;;;;N;;;;;
+A004;YI SYLLABLE IET;Lo;0;L;;;;;N;;;;;
+A005;YI SYLLABLE IEX;Lo;0;L;;;;;N;;;;;
+A006;YI SYLLABLE IE;Lo;0;L;;;;;N;;;;;
+A007;YI SYLLABLE IEP;Lo;0;L;;;;;N;;;;;
+A008;YI SYLLABLE AT;Lo;0;L;;;;;N;;;;;
+A009;YI SYLLABLE AX;Lo;0;L;;;;;N;;;;;
+A00A;YI SYLLABLE A;Lo;0;L;;;;;N;;;;;
+A00B;YI SYLLABLE AP;Lo;0;L;;;;;N;;;;;
+A00C;YI SYLLABLE UOX;Lo;0;L;;;;;N;;;;;
+A00D;YI SYLLABLE UO;Lo;0;L;;;;;N;;;;;
+A00E;YI SYLLABLE UOP;Lo;0;L;;;;;N;;;;;
+A00F;YI SYLLABLE OT;Lo;0;L;;;;;N;;;;;
+A010;YI SYLLABLE OX;Lo;0;L;;;;;N;;;;;
+A011;YI SYLLABLE O;Lo;0;L;;;;;N;;;;;
+A012;YI SYLLABLE OP;Lo;0;L;;;;;N;;;;;
+A013;YI SYLLABLE EX;Lo;0;L;;;;;N;;;;;
+A014;YI SYLLABLE E;Lo;0;L;;;;;N;;;;;
+A015;YI SYLLABLE WU;Lm;0;L;;;;;N;;;;;
+A016;YI SYLLABLE BIT;Lo;0;L;;;;;N;;;;;
+A017;YI SYLLABLE BIX;Lo;0;L;;;;;N;;;;;
+A018;YI SYLLABLE BI;Lo;0;L;;;;;N;;;;;
+A019;YI SYLLABLE BIP;Lo;0;L;;;;;N;;;;;
+A01A;YI SYLLABLE BIET;Lo;0;L;;;;;N;;;;;
+A01B;YI SYLLABLE BIEX;Lo;0;L;;;;;N;;;;;
+A01C;YI SYLLABLE BIE;Lo;0;L;;;;;N;;;;;
+A01D;YI SYLLABLE BIEP;Lo;0;L;;;;;N;;;;;
+A01E;YI SYLLABLE BAT;Lo;0;L;;;;;N;;;;;
+A01F;YI SYLLABLE BAX;Lo;0;L;;;;;N;;;;;
+A020;YI SYLLABLE BA;Lo;0;L;;;;;N;;;;;
+A021;YI SYLLABLE BAP;Lo;0;L;;;;;N;;;;;
+A022;YI SYLLABLE BUOX;Lo;0;L;;;;;N;;;;;
+A023;YI SYLLABLE BUO;Lo;0;L;;;;;N;;;;;
+A024;YI SYLLABLE BUOP;Lo;0;L;;;;;N;;;;;
+A025;YI SYLLABLE BOT;Lo;0;L;;;;;N;;;;;
+A026;YI SYLLABLE BOX;Lo;0;L;;;;;N;;;;;
+A027;YI SYLLABLE BO;Lo;0;L;;;;;N;;;;;
+A028;YI SYLLABLE BOP;Lo;0;L;;;;;N;;;;;
+A029;YI SYLLABLE BEX;Lo;0;L;;;;;N;;;;;
+A02A;YI SYLLABLE BE;Lo;0;L;;;;;N;;;;;
+A02B;YI SYLLABLE BEP;Lo;0;L;;;;;N;;;;;
+A02C;YI SYLLABLE BUT;Lo;0;L;;;;;N;;;;;
+A02D;YI SYLLABLE BUX;Lo;0;L;;;;;N;;;;;
+A02E;YI SYLLABLE BU;Lo;0;L;;;;;N;;;;;
+A02F;YI SYLLABLE BUP;Lo;0;L;;;;;N;;;;;
+A030;YI SYLLABLE BURX;Lo;0;L;;;;;N;;;;;
+A031;YI SYLLABLE BUR;Lo;0;L;;;;;N;;;;;
+A032;YI SYLLABLE BYT;Lo;0;L;;;;;N;;;;;
+A033;YI SYLLABLE BYX;Lo;0;L;;;;;N;;;;;
+A034;YI SYLLABLE BY;Lo;0;L;;;;;N;;;;;
+A035;YI SYLLABLE BYP;Lo;0;L;;;;;N;;;;;
+A036;YI SYLLABLE BYRX;Lo;0;L;;;;;N;;;;;
+A037;YI SYLLABLE BYR;Lo;0;L;;;;;N;;;;;
+A038;YI SYLLABLE PIT;Lo;0;L;;;;;N;;;;;
+A039;YI SYLLABLE PIX;Lo;0;L;;;;;N;;;;;
+A03A;YI SYLLABLE PI;Lo;0;L;;;;;N;;;;;
+A03B;YI SYLLABLE PIP;Lo;0;L;;;;;N;;;;;
+A03C;YI SYLLABLE PIEX;Lo;0;L;;;;;N;;;;;
+A03D;YI SYLLABLE PIE;Lo;0;L;;;;;N;;;;;
+A03E;YI SYLLABLE PIEP;Lo;0;L;;;;;N;;;;;
+A03F;YI SYLLABLE PAT;Lo;0;L;;;;;N;;;;;
+A040;YI SYLLABLE PAX;Lo;0;L;;;;;N;;;;;
+A041;YI SYLLABLE PA;Lo;0;L;;;;;N;;;;;
+A042;YI SYLLABLE PAP;Lo;0;L;;;;;N;;;;;
+A043;YI SYLLABLE PUOX;Lo;0;L;;;;;N;;;;;
+A044;YI SYLLABLE PUO;Lo;0;L;;;;;N;;;;;
+A045;YI SYLLABLE PUOP;Lo;0;L;;;;;N;;;;;
+A046;YI SYLLABLE POT;Lo;0;L;;;;;N;;;;;
+A047;YI SYLLABLE POX;Lo;0;L;;;;;N;;;;;
+A048;YI SYLLABLE PO;Lo;0;L;;;;;N;;;;;
+A049;YI SYLLABLE POP;Lo;0;L;;;;;N;;;;;
+A04A;YI SYLLABLE PUT;Lo;0;L;;;;;N;;;;;
+A04B;YI SYLLABLE PUX;Lo;0;L;;;;;N;;;;;
+A04C;YI SYLLABLE PU;Lo;0;L;;;;;N;;;;;
+A04D;YI SYLLABLE PUP;Lo;0;L;;;;;N;;;;;
+A04E;YI SYLLABLE PURX;Lo;0;L;;;;;N;;;;;
+A04F;YI SYLLABLE PUR;Lo;0;L;;;;;N;;;;;
+A050;YI SYLLABLE PYT;Lo;0;L;;;;;N;;;;;
+A051;YI SYLLABLE PYX;Lo;0;L;;;;;N;;;;;
+A052;YI SYLLABLE PY;Lo;0;L;;;;;N;;;;;
+A053;YI SYLLABLE PYP;Lo;0;L;;;;;N;;;;;
+A054;YI SYLLABLE PYRX;Lo;0;L;;;;;N;;;;;
+A055;YI SYLLABLE PYR;Lo;0;L;;;;;N;;;;;
+A056;YI SYLLABLE BBIT;Lo;0;L;;;;;N;;;;;
+A057;YI SYLLABLE BBIX;Lo;0;L;;;;;N;;;;;
+A058;YI SYLLABLE BBI;Lo;0;L;;;;;N;;;;;
+A059;YI SYLLABLE BBIP;Lo;0;L;;;;;N;;;;;
+A05A;YI SYLLABLE BBIET;Lo;0;L;;;;;N;;;;;
+A05B;YI SYLLABLE BBIEX;Lo;0;L;;;;;N;;;;;
+A05C;YI SYLLABLE BBIE;Lo;0;L;;;;;N;;;;;
+A05D;YI SYLLABLE BBIEP;Lo;0;L;;;;;N;;;;;
+A05E;YI SYLLABLE BBAT;Lo;0;L;;;;;N;;;;;
+A05F;YI SYLLABLE BBAX;Lo;0;L;;;;;N;;;;;
+A060;YI SYLLABLE BBA;Lo;0;L;;;;;N;;;;;
+A061;YI SYLLABLE BBAP;Lo;0;L;;;;;N;;;;;
+A062;YI SYLLABLE BBUOX;Lo;0;L;;;;;N;;;;;
+A063;YI SYLLABLE BBUO;Lo;0;L;;;;;N;;;;;
+A064;YI SYLLABLE BBUOP;Lo;0;L;;;;;N;;;;;
+A065;YI SYLLABLE BBOT;Lo;0;L;;;;;N;;;;;
+A066;YI SYLLABLE BBOX;Lo;0;L;;;;;N;;;;;
+A067;YI SYLLABLE BBO;Lo;0;L;;;;;N;;;;;
+A068;YI SYLLABLE BBOP;Lo;0;L;;;;;N;;;;;
+A069;YI SYLLABLE BBEX;Lo;0;L;;;;;N;;;;;
+A06A;YI SYLLABLE BBE;Lo;0;L;;;;;N;;;;;
+A06B;YI SYLLABLE BBEP;Lo;0;L;;;;;N;;;;;
+A06C;YI SYLLABLE BBUT;Lo;0;L;;;;;N;;;;;
+A06D;YI SYLLABLE BBUX;Lo;0;L;;;;;N;;;;;
+A06E;YI SYLLABLE BBU;Lo;0;L;;;;;N;;;;;
+A06F;YI SYLLABLE BBUP;Lo;0;L;;;;;N;;;;;
+A070;YI SYLLABLE BBURX;Lo;0;L;;;;;N;;;;;
+A071;YI SYLLABLE BBUR;Lo;0;L;;;;;N;;;;;
+A072;YI SYLLABLE BBYT;Lo;0;L;;;;;N;;;;;
+A073;YI SYLLABLE BBYX;Lo;0;L;;;;;N;;;;;
+A074;YI SYLLABLE BBY;Lo;0;L;;;;;N;;;;;
+A075;YI SYLLABLE BBYP;Lo;0;L;;;;;N;;;;;
+A076;YI SYLLABLE NBIT;Lo;0;L;;;;;N;;;;;
+A077;YI SYLLABLE NBIX;Lo;0;L;;;;;N;;;;;
+A078;YI SYLLABLE NBI;Lo;0;L;;;;;N;;;;;
+A079;YI SYLLABLE NBIP;Lo;0;L;;;;;N;;;;;
+A07A;YI SYLLABLE NBIEX;Lo;0;L;;;;;N;;;;;
+A07B;YI SYLLABLE NBIE;Lo;0;L;;;;;N;;;;;
+A07C;YI SYLLABLE NBIEP;Lo;0;L;;;;;N;;;;;
+A07D;YI SYLLABLE NBAT;Lo;0;L;;;;;N;;;;;
+A07E;YI SYLLABLE NBAX;Lo;0;L;;;;;N;;;;;
+A07F;YI SYLLABLE NBA;Lo;0;L;;;;;N;;;;;
+A080;YI SYLLABLE NBAP;Lo;0;L;;;;;N;;;;;
+A081;YI SYLLABLE NBOT;Lo;0;L;;;;;N;;;;;
+A082;YI SYLLABLE NBOX;Lo;0;L;;;;;N;;;;;
+A083;YI SYLLABLE NBO;Lo;0;L;;;;;N;;;;;
+A084;YI SYLLABLE NBOP;Lo;0;L;;;;;N;;;;;
+A085;YI SYLLABLE NBUT;Lo;0;L;;;;;N;;;;;
+A086;YI SYLLABLE NBUX;Lo;0;L;;;;;N;;;;;
+A087;YI SYLLABLE NBU;Lo;0;L;;;;;N;;;;;
+A088;YI SYLLABLE NBUP;Lo;0;L;;;;;N;;;;;
+A089;YI SYLLABLE NBURX;Lo;0;L;;;;;N;;;;;
+A08A;YI SYLLABLE NBUR;Lo;0;L;;;;;N;;;;;
+A08B;YI SYLLABLE NBYT;Lo;0;L;;;;;N;;;;;
+A08C;YI SYLLABLE NBYX;Lo;0;L;;;;;N;;;;;
+A08D;YI SYLLABLE NBY;Lo;0;L;;;;;N;;;;;
+A08E;YI SYLLABLE NBYP;Lo;0;L;;;;;N;;;;;
+A08F;YI SYLLABLE NBYRX;Lo;0;L;;;;;N;;;;;
+A090;YI SYLLABLE NBYR;Lo;0;L;;;;;N;;;;;
+A091;YI SYLLABLE HMIT;Lo;0;L;;;;;N;;;;;
+A092;YI SYLLABLE HMIX;Lo;0;L;;;;;N;;;;;
+A093;YI SYLLABLE HMI;Lo;0;L;;;;;N;;;;;
+A094;YI SYLLABLE HMIP;Lo;0;L;;;;;N;;;;;
+A095;YI SYLLABLE HMIEX;Lo;0;L;;;;;N;;;;;
+A096;YI SYLLABLE HMIE;Lo;0;L;;;;;N;;;;;
+A097;YI SYLLABLE HMIEP;Lo;0;L;;;;;N;;;;;
+A098;YI SYLLABLE HMAT;Lo;0;L;;;;;N;;;;;
+A099;YI SYLLABLE HMAX;Lo;0;L;;;;;N;;;;;
+A09A;YI SYLLABLE HMA;Lo;0;L;;;;;N;;;;;
+A09B;YI SYLLABLE HMAP;Lo;0;L;;;;;N;;;;;
+A09C;YI SYLLABLE HMUOX;Lo;0;L;;;;;N;;;;;
+A09D;YI SYLLABLE HMUO;Lo;0;L;;;;;N;;;;;
+A09E;YI SYLLABLE HMUOP;Lo;0;L;;;;;N;;;;;
+A09F;YI SYLLABLE HMOT;Lo;0;L;;;;;N;;;;;
+A0A0;YI SYLLABLE HMOX;Lo;0;L;;;;;N;;;;;
+A0A1;YI SYLLABLE HMO;Lo;0;L;;;;;N;;;;;
+A0A2;YI SYLLABLE HMOP;Lo;0;L;;;;;N;;;;;
+A0A3;YI SYLLABLE HMUT;Lo;0;L;;;;;N;;;;;
+A0A4;YI SYLLABLE HMUX;Lo;0;L;;;;;N;;;;;
+A0A5;YI SYLLABLE HMU;Lo;0;L;;;;;N;;;;;
+A0A6;YI SYLLABLE HMUP;Lo;0;L;;;;;N;;;;;
+A0A7;YI SYLLABLE HMURX;Lo;0;L;;;;;N;;;;;
+A0A8;YI SYLLABLE HMUR;Lo;0;L;;;;;N;;;;;
+A0A9;YI SYLLABLE HMYX;Lo;0;L;;;;;N;;;;;
+A0AA;YI SYLLABLE HMY;Lo;0;L;;;;;N;;;;;
+A0AB;YI SYLLABLE HMYP;Lo;0;L;;;;;N;;;;;
+A0AC;YI SYLLABLE HMYRX;Lo;0;L;;;;;N;;;;;
+A0AD;YI SYLLABLE HMYR;Lo;0;L;;;;;N;;;;;
+A0AE;YI SYLLABLE MIT;Lo;0;L;;;;;N;;;;;
+A0AF;YI SYLLABLE MIX;Lo;0;L;;;;;N;;;;;
+A0B0;YI SYLLABLE MI;Lo;0;L;;;;;N;;;;;
+A0B1;YI SYLLABLE MIP;Lo;0;L;;;;;N;;;;;
+A0B2;YI SYLLABLE MIEX;Lo;0;L;;;;;N;;;;;
+A0B3;YI SYLLABLE MIE;Lo;0;L;;;;;N;;;;;
+A0B4;YI SYLLABLE MIEP;Lo;0;L;;;;;N;;;;;
+A0B5;YI SYLLABLE MAT;Lo;0;L;;;;;N;;;;;
+A0B6;YI SYLLABLE MAX;Lo;0;L;;;;;N;;;;;
+A0B7;YI SYLLABLE MA;Lo;0;L;;;;;N;;;;;
+A0B8;YI SYLLABLE MAP;Lo;0;L;;;;;N;;;;;
+A0B9;YI SYLLABLE MUOT;Lo;0;L;;;;;N;;;;;
+A0BA;YI SYLLABLE MUOX;Lo;0;L;;;;;N;;;;;
+A0BB;YI SYLLABLE MUO;Lo;0;L;;;;;N;;;;;
+A0BC;YI SYLLABLE MUOP;Lo;0;L;;;;;N;;;;;
+A0BD;YI SYLLABLE MOT;Lo;0;L;;;;;N;;;;;
+A0BE;YI SYLLABLE MOX;Lo;0;L;;;;;N;;;;;
+A0BF;YI SYLLABLE MO;Lo;0;L;;;;;N;;;;;
+A0C0;YI SYLLABLE MOP;Lo;0;L;;;;;N;;;;;
+A0C1;YI SYLLABLE MEX;Lo;0;L;;;;;N;;;;;
+A0C2;YI SYLLABLE ME;Lo;0;L;;;;;N;;;;;
+A0C3;YI SYLLABLE MUT;Lo;0;L;;;;;N;;;;;
+A0C4;YI SYLLABLE MUX;Lo;0;L;;;;;N;;;;;
+A0C5;YI SYLLABLE MU;Lo;0;L;;;;;N;;;;;
+A0C6;YI SYLLABLE MUP;Lo;0;L;;;;;N;;;;;
+A0C7;YI SYLLABLE MURX;Lo;0;L;;;;;N;;;;;
+A0C8;YI SYLLABLE MUR;Lo;0;L;;;;;N;;;;;
+A0C9;YI SYLLABLE MYT;Lo;0;L;;;;;N;;;;;
+A0CA;YI SYLLABLE MYX;Lo;0;L;;;;;N;;;;;
+A0CB;YI SYLLABLE MY;Lo;0;L;;;;;N;;;;;
+A0CC;YI SYLLABLE MYP;Lo;0;L;;;;;N;;;;;
+A0CD;YI SYLLABLE FIT;Lo;0;L;;;;;N;;;;;
+A0CE;YI SYLLABLE FIX;Lo;0;L;;;;;N;;;;;
+A0CF;YI SYLLABLE FI;Lo;0;L;;;;;N;;;;;
+A0D0;YI SYLLABLE FIP;Lo;0;L;;;;;N;;;;;
+A0D1;YI SYLLABLE FAT;Lo;0;L;;;;;N;;;;;
+A0D2;YI SYLLABLE FAX;Lo;0;L;;;;;N;;;;;
+A0D3;YI SYLLABLE FA;Lo;0;L;;;;;N;;;;;
+A0D4;YI SYLLABLE FAP;Lo;0;L;;;;;N;;;;;
+A0D5;YI SYLLABLE FOX;Lo;0;L;;;;;N;;;;;
+A0D6;YI SYLLABLE FO;Lo;0;L;;;;;N;;;;;
+A0D7;YI SYLLABLE FOP;Lo;0;L;;;;;N;;;;;
+A0D8;YI SYLLABLE FUT;Lo;0;L;;;;;N;;;;;
+A0D9;YI SYLLABLE FUX;Lo;0;L;;;;;N;;;;;
+A0DA;YI SYLLABLE FU;Lo;0;L;;;;;N;;;;;
+A0DB;YI SYLLABLE FUP;Lo;0;L;;;;;N;;;;;
+A0DC;YI SYLLABLE FURX;Lo;0;L;;;;;N;;;;;
+A0DD;YI SYLLABLE FUR;Lo;0;L;;;;;N;;;;;
+A0DE;YI SYLLABLE FYT;Lo;0;L;;;;;N;;;;;
+A0DF;YI SYLLABLE FYX;Lo;0;L;;;;;N;;;;;
+A0E0;YI SYLLABLE FY;Lo;0;L;;;;;N;;;;;
+A0E1;YI SYLLABLE FYP;Lo;0;L;;;;;N;;;;;
+A0E2;YI SYLLABLE VIT;Lo;0;L;;;;;N;;;;;
+A0E3;YI SYLLABLE VIX;Lo;0;L;;;;;N;;;;;
+A0E4;YI SYLLABLE VI;Lo;0;L;;;;;N;;;;;
+A0E5;YI SYLLABLE VIP;Lo;0;L;;;;;N;;;;;
+A0E6;YI SYLLABLE VIET;Lo;0;L;;;;;N;;;;;
+A0E7;YI SYLLABLE VIEX;Lo;0;L;;;;;N;;;;;
+A0E8;YI SYLLABLE VIE;Lo;0;L;;;;;N;;;;;
+A0E9;YI SYLLABLE VIEP;Lo;0;L;;;;;N;;;;;
+A0EA;YI SYLLABLE VAT;Lo;0;L;;;;;N;;;;;
+A0EB;YI SYLLABLE VAX;Lo;0;L;;;;;N;;;;;
+A0EC;YI SYLLABLE VA;Lo;0;L;;;;;N;;;;;
+A0ED;YI SYLLABLE VAP;Lo;0;L;;;;;N;;;;;
+A0EE;YI SYLLABLE VOT;Lo;0;L;;;;;N;;;;;
+A0EF;YI SYLLABLE VOX;Lo;0;L;;;;;N;;;;;
+A0F0;YI SYLLABLE VO;Lo;0;L;;;;;N;;;;;
+A0F1;YI SYLLABLE VOP;Lo;0;L;;;;;N;;;;;
+A0F2;YI SYLLABLE VEX;Lo;0;L;;;;;N;;;;;
+A0F3;YI SYLLABLE VEP;Lo;0;L;;;;;N;;;;;
+A0F4;YI SYLLABLE VUT;Lo;0;L;;;;;N;;;;;
+A0F5;YI SYLLABLE VUX;Lo;0;L;;;;;N;;;;;
+A0F6;YI SYLLABLE VU;Lo;0;L;;;;;N;;;;;
+A0F7;YI SYLLABLE VUP;Lo;0;L;;;;;N;;;;;
+A0F8;YI SYLLABLE VURX;Lo;0;L;;;;;N;;;;;
+A0F9;YI SYLLABLE VUR;Lo;0;L;;;;;N;;;;;
+A0FA;YI SYLLABLE VYT;Lo;0;L;;;;;N;;;;;
+A0FB;YI SYLLABLE VYX;Lo;0;L;;;;;N;;;;;
+A0FC;YI SYLLABLE VY;Lo;0;L;;;;;N;;;;;
+A0FD;YI SYLLABLE VYP;Lo;0;L;;;;;N;;;;;
+A0FE;YI SYLLABLE VYRX;Lo;0;L;;;;;N;;;;;
+A0FF;YI SYLLABLE VYR;Lo;0;L;;;;;N;;;;;
+A100;YI SYLLABLE DIT;Lo;0;L;;;;;N;;;;;
+A101;YI SYLLABLE DIX;Lo;0;L;;;;;N;;;;;
+A102;YI SYLLABLE DI;Lo;0;L;;;;;N;;;;;
+A103;YI SYLLABLE DIP;Lo;0;L;;;;;N;;;;;
+A104;YI SYLLABLE DIEX;Lo;0;L;;;;;N;;;;;
+A105;YI SYLLABLE DIE;Lo;0;L;;;;;N;;;;;
+A106;YI SYLLABLE DIEP;Lo;0;L;;;;;N;;;;;
+A107;YI SYLLABLE DAT;Lo;0;L;;;;;N;;;;;
+A108;YI SYLLABLE DAX;Lo;0;L;;;;;N;;;;;
+A109;YI SYLLABLE DA;Lo;0;L;;;;;N;;;;;
+A10A;YI SYLLABLE DAP;Lo;0;L;;;;;N;;;;;
+A10B;YI SYLLABLE DUOX;Lo;0;L;;;;;N;;;;;
+A10C;YI SYLLABLE DUO;Lo;0;L;;;;;N;;;;;
+A10D;YI SYLLABLE DOT;Lo;0;L;;;;;N;;;;;
+A10E;YI SYLLABLE DOX;Lo;0;L;;;;;N;;;;;
+A10F;YI SYLLABLE DO;Lo;0;L;;;;;N;;;;;
+A110;YI SYLLABLE DOP;Lo;0;L;;;;;N;;;;;
+A111;YI SYLLABLE DEX;Lo;0;L;;;;;N;;;;;
+A112;YI SYLLABLE DE;Lo;0;L;;;;;N;;;;;
+A113;YI SYLLABLE DEP;Lo;0;L;;;;;N;;;;;
+A114;YI SYLLABLE DUT;Lo;0;L;;;;;N;;;;;
+A115;YI SYLLABLE DUX;Lo;0;L;;;;;N;;;;;
+A116;YI SYLLABLE DU;Lo;0;L;;;;;N;;;;;
+A117;YI SYLLABLE DUP;Lo;0;L;;;;;N;;;;;
+A118;YI SYLLABLE DURX;Lo;0;L;;;;;N;;;;;
+A119;YI SYLLABLE DUR;Lo;0;L;;;;;N;;;;;
+A11A;YI SYLLABLE TIT;Lo;0;L;;;;;N;;;;;
+A11B;YI SYLLABLE TIX;Lo;0;L;;;;;N;;;;;
+A11C;YI SYLLABLE TI;Lo;0;L;;;;;N;;;;;
+A11D;YI SYLLABLE TIP;Lo;0;L;;;;;N;;;;;
+A11E;YI SYLLABLE TIEX;Lo;0;L;;;;;N;;;;;
+A11F;YI SYLLABLE TIE;Lo;0;L;;;;;N;;;;;
+A120;YI SYLLABLE TIEP;Lo;0;L;;;;;N;;;;;
+A121;YI SYLLABLE TAT;Lo;0;L;;;;;N;;;;;
+A122;YI SYLLABLE TAX;Lo;0;L;;;;;N;;;;;
+A123;YI SYLLABLE TA;Lo;0;L;;;;;N;;;;;
+A124;YI SYLLABLE TAP;Lo;0;L;;;;;N;;;;;
+A125;YI SYLLABLE TUOT;Lo;0;L;;;;;N;;;;;
+A126;YI SYLLABLE TUOX;Lo;0;L;;;;;N;;;;;
+A127;YI SYLLABLE TUO;Lo;0;L;;;;;N;;;;;
+A128;YI SYLLABLE TUOP;Lo;0;L;;;;;N;;;;;
+A129;YI SYLLABLE TOT;Lo;0;L;;;;;N;;;;;
+A12A;YI SYLLABLE TOX;Lo;0;L;;;;;N;;;;;
+A12B;YI SYLLABLE TO;Lo;0;L;;;;;N;;;;;
+A12C;YI SYLLABLE TOP;Lo;0;L;;;;;N;;;;;
+A12D;YI SYLLABLE TEX;Lo;0;L;;;;;N;;;;;
+A12E;YI SYLLABLE TE;Lo;0;L;;;;;N;;;;;
+A12F;YI SYLLABLE TEP;Lo;0;L;;;;;N;;;;;
+A130;YI SYLLABLE TUT;Lo;0;L;;;;;N;;;;;
+A131;YI SYLLABLE TUX;Lo;0;L;;;;;N;;;;;
+A132;YI SYLLABLE TU;Lo;0;L;;;;;N;;;;;
+A133;YI SYLLABLE TUP;Lo;0;L;;;;;N;;;;;
+A134;YI SYLLABLE TURX;Lo;0;L;;;;;N;;;;;
+A135;YI SYLLABLE TUR;Lo;0;L;;;;;N;;;;;
+A136;YI SYLLABLE DDIT;Lo;0;L;;;;;N;;;;;
+A137;YI SYLLABLE DDIX;Lo;0;L;;;;;N;;;;;
+A138;YI SYLLABLE DDI;Lo;0;L;;;;;N;;;;;
+A139;YI SYLLABLE DDIP;Lo;0;L;;;;;N;;;;;
+A13A;YI SYLLABLE DDIEX;Lo;0;L;;;;;N;;;;;
+A13B;YI SYLLABLE DDIE;Lo;0;L;;;;;N;;;;;
+A13C;YI SYLLABLE DDIEP;Lo;0;L;;;;;N;;;;;
+A13D;YI SYLLABLE DDAT;Lo;0;L;;;;;N;;;;;
+A13E;YI SYLLABLE DDAX;Lo;0;L;;;;;N;;;;;
+A13F;YI SYLLABLE DDA;Lo;0;L;;;;;N;;;;;
+A140;YI SYLLABLE DDAP;Lo;0;L;;;;;N;;;;;
+A141;YI SYLLABLE DDUOX;Lo;0;L;;;;;N;;;;;
+A142;YI SYLLABLE DDUO;Lo;0;L;;;;;N;;;;;
+A143;YI SYLLABLE DDUOP;Lo;0;L;;;;;N;;;;;
+A144;YI SYLLABLE DDOT;Lo;0;L;;;;;N;;;;;
+A145;YI SYLLABLE DDOX;Lo;0;L;;;;;N;;;;;
+A146;YI SYLLABLE DDO;Lo;0;L;;;;;N;;;;;
+A147;YI SYLLABLE DDOP;Lo;0;L;;;;;N;;;;;
+A148;YI SYLLABLE DDEX;Lo;0;L;;;;;N;;;;;
+A149;YI SYLLABLE DDE;Lo;0;L;;;;;N;;;;;
+A14A;YI SYLLABLE DDEP;Lo;0;L;;;;;N;;;;;
+A14B;YI SYLLABLE DDUT;Lo;0;L;;;;;N;;;;;
+A14C;YI SYLLABLE DDUX;Lo;0;L;;;;;N;;;;;
+A14D;YI SYLLABLE DDU;Lo;0;L;;;;;N;;;;;
+A14E;YI SYLLABLE DDUP;Lo;0;L;;;;;N;;;;;
+A14F;YI SYLLABLE DDURX;Lo;0;L;;;;;N;;;;;
+A150;YI SYLLABLE DDUR;Lo;0;L;;;;;N;;;;;
+A151;YI SYLLABLE NDIT;Lo;0;L;;;;;N;;;;;
+A152;YI SYLLABLE NDIX;Lo;0;L;;;;;N;;;;;
+A153;YI SYLLABLE NDI;Lo;0;L;;;;;N;;;;;
+A154;YI SYLLABLE NDIP;Lo;0;L;;;;;N;;;;;
+A155;YI SYLLABLE NDIEX;Lo;0;L;;;;;N;;;;;
+A156;YI SYLLABLE NDIE;Lo;0;L;;;;;N;;;;;
+A157;YI SYLLABLE NDAT;Lo;0;L;;;;;N;;;;;
+A158;YI SYLLABLE NDAX;Lo;0;L;;;;;N;;;;;
+A159;YI SYLLABLE NDA;Lo;0;L;;;;;N;;;;;
+A15A;YI SYLLABLE NDAP;Lo;0;L;;;;;N;;;;;
+A15B;YI SYLLABLE NDOT;Lo;0;L;;;;;N;;;;;
+A15C;YI SYLLABLE NDOX;Lo;0;L;;;;;N;;;;;
+A15D;YI SYLLABLE NDO;Lo;0;L;;;;;N;;;;;
+A15E;YI SYLLABLE NDOP;Lo;0;L;;;;;N;;;;;
+A15F;YI SYLLABLE NDEX;Lo;0;L;;;;;N;;;;;
+A160;YI SYLLABLE NDE;Lo;0;L;;;;;N;;;;;
+A161;YI SYLLABLE NDEP;Lo;0;L;;;;;N;;;;;
+A162;YI SYLLABLE NDUT;Lo;0;L;;;;;N;;;;;
+A163;YI SYLLABLE NDUX;Lo;0;L;;;;;N;;;;;
+A164;YI SYLLABLE NDU;Lo;0;L;;;;;N;;;;;
+A165;YI SYLLABLE NDUP;Lo;0;L;;;;;N;;;;;
+A166;YI SYLLABLE NDURX;Lo;0;L;;;;;N;;;;;
+A167;YI SYLLABLE NDUR;Lo;0;L;;;;;N;;;;;
+A168;YI SYLLABLE HNIT;Lo;0;L;;;;;N;;;;;
+A169;YI SYLLABLE HNIX;Lo;0;L;;;;;N;;;;;
+A16A;YI SYLLABLE HNI;Lo;0;L;;;;;N;;;;;
+A16B;YI SYLLABLE HNIP;Lo;0;L;;;;;N;;;;;
+A16C;YI SYLLABLE HNIET;Lo;0;L;;;;;N;;;;;
+A16D;YI SYLLABLE HNIEX;Lo;0;L;;;;;N;;;;;
+A16E;YI SYLLABLE HNIE;Lo;0;L;;;;;N;;;;;
+A16F;YI SYLLABLE HNIEP;Lo;0;L;;;;;N;;;;;
+A170;YI SYLLABLE HNAT;Lo;0;L;;;;;N;;;;;
+A171;YI SYLLABLE HNAX;Lo;0;L;;;;;N;;;;;
+A172;YI SYLLABLE HNA;Lo;0;L;;;;;N;;;;;
+A173;YI SYLLABLE HNAP;Lo;0;L;;;;;N;;;;;
+A174;YI SYLLABLE HNUOX;Lo;0;L;;;;;N;;;;;
+A175;YI SYLLABLE HNUO;Lo;0;L;;;;;N;;;;;
+A176;YI SYLLABLE HNOT;Lo;0;L;;;;;N;;;;;
+A177;YI SYLLABLE HNOX;Lo;0;L;;;;;N;;;;;
+A178;YI SYLLABLE HNOP;Lo;0;L;;;;;N;;;;;
+A179;YI SYLLABLE HNEX;Lo;0;L;;;;;N;;;;;
+A17A;YI SYLLABLE HNE;Lo;0;L;;;;;N;;;;;
+A17B;YI SYLLABLE HNEP;Lo;0;L;;;;;N;;;;;
+A17C;YI SYLLABLE HNUT;Lo;0;L;;;;;N;;;;;
+A17D;YI SYLLABLE NIT;Lo;0;L;;;;;N;;;;;
+A17E;YI SYLLABLE NIX;Lo;0;L;;;;;N;;;;;
+A17F;YI SYLLABLE NI;Lo;0;L;;;;;N;;;;;
+A180;YI SYLLABLE NIP;Lo;0;L;;;;;N;;;;;
+A181;YI SYLLABLE NIEX;Lo;0;L;;;;;N;;;;;
+A182;YI SYLLABLE NIE;Lo;0;L;;;;;N;;;;;
+A183;YI SYLLABLE NIEP;Lo;0;L;;;;;N;;;;;
+A184;YI SYLLABLE NAX;Lo;0;L;;;;;N;;;;;
+A185;YI SYLLABLE NA;Lo;0;L;;;;;N;;;;;
+A186;YI SYLLABLE NAP;Lo;0;L;;;;;N;;;;;
+A187;YI SYLLABLE NUOX;Lo;0;L;;;;;N;;;;;
+A188;YI SYLLABLE NUO;Lo;0;L;;;;;N;;;;;
+A189;YI SYLLABLE NUOP;Lo;0;L;;;;;N;;;;;
+A18A;YI SYLLABLE NOT;Lo;0;L;;;;;N;;;;;
+A18B;YI SYLLABLE NOX;Lo;0;L;;;;;N;;;;;
+A18C;YI SYLLABLE NO;Lo;0;L;;;;;N;;;;;
+A18D;YI SYLLABLE NOP;Lo;0;L;;;;;N;;;;;
+A18E;YI SYLLABLE NEX;Lo;0;L;;;;;N;;;;;
+A18F;YI SYLLABLE NE;Lo;0;L;;;;;N;;;;;
+A190;YI SYLLABLE NEP;Lo;0;L;;;;;N;;;;;
+A191;YI SYLLABLE NUT;Lo;0;L;;;;;N;;;;;
+A192;YI SYLLABLE NUX;Lo;0;L;;;;;N;;;;;
+A193;YI SYLLABLE NU;Lo;0;L;;;;;N;;;;;
+A194;YI SYLLABLE NUP;Lo;0;L;;;;;N;;;;;
+A195;YI SYLLABLE NURX;Lo;0;L;;;;;N;;;;;
+A196;YI SYLLABLE NUR;Lo;0;L;;;;;N;;;;;
+A197;YI SYLLABLE HLIT;Lo;0;L;;;;;N;;;;;
+A198;YI SYLLABLE HLIX;Lo;0;L;;;;;N;;;;;
+A199;YI SYLLABLE HLI;Lo;0;L;;;;;N;;;;;
+A19A;YI SYLLABLE HLIP;Lo;0;L;;;;;N;;;;;
+A19B;YI SYLLABLE HLIEX;Lo;0;L;;;;;N;;;;;
+A19C;YI SYLLABLE HLIE;Lo;0;L;;;;;N;;;;;
+A19D;YI SYLLABLE HLIEP;Lo;0;L;;;;;N;;;;;
+A19E;YI SYLLABLE HLAT;Lo;0;L;;;;;N;;;;;
+A19F;YI SYLLABLE HLAX;Lo;0;L;;;;;N;;;;;
+A1A0;YI SYLLABLE HLA;Lo;0;L;;;;;N;;;;;
+A1A1;YI SYLLABLE HLAP;Lo;0;L;;;;;N;;;;;
+A1A2;YI SYLLABLE HLUOX;Lo;0;L;;;;;N;;;;;
+A1A3;YI SYLLABLE HLUO;Lo;0;L;;;;;N;;;;;
+A1A4;YI SYLLABLE HLUOP;Lo;0;L;;;;;N;;;;;
+A1A5;YI SYLLABLE HLOX;Lo;0;L;;;;;N;;;;;
+A1A6;YI SYLLABLE HLO;Lo;0;L;;;;;N;;;;;
+A1A7;YI SYLLABLE HLOP;Lo;0;L;;;;;N;;;;;
+A1A8;YI SYLLABLE HLEX;Lo;0;L;;;;;N;;;;;
+A1A9;YI SYLLABLE HLE;Lo;0;L;;;;;N;;;;;
+A1AA;YI SYLLABLE HLEP;Lo;0;L;;;;;N;;;;;
+A1AB;YI SYLLABLE HLUT;Lo;0;L;;;;;N;;;;;
+A1AC;YI SYLLABLE HLUX;Lo;0;L;;;;;N;;;;;
+A1AD;YI SYLLABLE HLU;Lo;0;L;;;;;N;;;;;
+A1AE;YI SYLLABLE HLUP;Lo;0;L;;;;;N;;;;;
+A1AF;YI SYLLABLE HLURX;Lo;0;L;;;;;N;;;;;
+A1B0;YI SYLLABLE HLUR;Lo;0;L;;;;;N;;;;;
+A1B1;YI SYLLABLE HLYT;Lo;0;L;;;;;N;;;;;
+A1B2;YI SYLLABLE HLYX;Lo;0;L;;;;;N;;;;;
+A1B3;YI SYLLABLE HLY;Lo;0;L;;;;;N;;;;;
+A1B4;YI SYLLABLE HLYP;Lo;0;L;;;;;N;;;;;
+A1B5;YI SYLLABLE HLYRX;Lo;0;L;;;;;N;;;;;
+A1B6;YI SYLLABLE HLYR;Lo;0;L;;;;;N;;;;;
+A1B7;YI SYLLABLE LIT;Lo;0;L;;;;;N;;;;;
+A1B8;YI SYLLABLE LIX;Lo;0;L;;;;;N;;;;;
+A1B9;YI SYLLABLE LI;Lo;0;L;;;;;N;;;;;
+A1BA;YI SYLLABLE LIP;Lo;0;L;;;;;N;;;;;
+A1BB;YI SYLLABLE LIET;Lo;0;L;;;;;N;;;;;
+A1BC;YI SYLLABLE LIEX;Lo;0;L;;;;;N;;;;;
+A1BD;YI SYLLABLE LIE;Lo;0;L;;;;;N;;;;;
+A1BE;YI SYLLABLE LIEP;Lo;0;L;;;;;N;;;;;
+A1BF;YI SYLLABLE LAT;Lo;0;L;;;;;N;;;;;
+A1C0;YI SYLLABLE LAX;Lo;0;L;;;;;N;;;;;
+A1C1;YI SYLLABLE LA;Lo;0;L;;;;;N;;;;;
+A1C2;YI SYLLABLE LAP;Lo;0;L;;;;;N;;;;;
+A1C3;YI SYLLABLE LUOT;Lo;0;L;;;;;N;;;;;
+A1C4;YI SYLLABLE LUOX;Lo;0;L;;;;;N;;;;;
+A1C5;YI SYLLABLE LUO;Lo;0;L;;;;;N;;;;;
+A1C6;YI SYLLABLE LUOP;Lo;0;L;;;;;N;;;;;
+A1C7;YI SYLLABLE LOT;Lo;0;L;;;;;N;;;;;
+A1C8;YI SYLLABLE LOX;Lo;0;L;;;;;N;;;;;
+A1C9;YI SYLLABLE LO;Lo;0;L;;;;;N;;;;;
+A1CA;YI SYLLABLE LOP;Lo;0;L;;;;;N;;;;;
+A1CB;YI SYLLABLE LEX;Lo;0;L;;;;;N;;;;;
+A1CC;YI SYLLABLE LE;Lo;0;L;;;;;N;;;;;
+A1CD;YI SYLLABLE LEP;Lo;0;L;;;;;N;;;;;
+A1CE;YI SYLLABLE LUT;Lo;0;L;;;;;N;;;;;
+A1CF;YI SYLLABLE LUX;Lo;0;L;;;;;N;;;;;
+A1D0;YI SYLLABLE LU;Lo;0;L;;;;;N;;;;;
+A1D1;YI SYLLABLE LUP;Lo;0;L;;;;;N;;;;;
+A1D2;YI SYLLABLE LURX;Lo;0;L;;;;;N;;;;;
+A1D3;YI SYLLABLE LUR;Lo;0;L;;;;;N;;;;;
+A1D4;YI SYLLABLE LYT;Lo;0;L;;;;;N;;;;;
+A1D5;YI SYLLABLE LYX;Lo;0;L;;;;;N;;;;;
+A1D6;YI SYLLABLE LY;Lo;0;L;;;;;N;;;;;
+A1D7;YI SYLLABLE LYP;Lo;0;L;;;;;N;;;;;
+A1D8;YI SYLLABLE LYRX;Lo;0;L;;;;;N;;;;;
+A1D9;YI SYLLABLE LYR;Lo;0;L;;;;;N;;;;;
+A1DA;YI SYLLABLE GIT;Lo;0;L;;;;;N;;;;;
+A1DB;YI SYLLABLE GIX;Lo;0;L;;;;;N;;;;;
+A1DC;YI SYLLABLE GI;Lo;0;L;;;;;N;;;;;
+A1DD;YI SYLLABLE GIP;Lo;0;L;;;;;N;;;;;
+A1DE;YI SYLLABLE GIET;Lo;0;L;;;;;N;;;;;
+A1DF;YI SYLLABLE GIEX;Lo;0;L;;;;;N;;;;;
+A1E0;YI SYLLABLE GIE;Lo;0;L;;;;;N;;;;;
+A1E1;YI SYLLABLE GIEP;Lo;0;L;;;;;N;;;;;
+A1E2;YI SYLLABLE GAT;Lo;0;L;;;;;N;;;;;
+A1E3;YI SYLLABLE GAX;Lo;0;L;;;;;N;;;;;
+A1E4;YI SYLLABLE GA;Lo;0;L;;;;;N;;;;;
+A1E5;YI SYLLABLE GAP;Lo;0;L;;;;;N;;;;;
+A1E6;YI SYLLABLE GUOT;Lo;0;L;;;;;N;;;;;
+A1E7;YI SYLLABLE GUOX;Lo;0;L;;;;;N;;;;;
+A1E8;YI SYLLABLE GUO;Lo;0;L;;;;;N;;;;;
+A1E9;YI SYLLABLE GUOP;Lo;0;L;;;;;N;;;;;
+A1EA;YI SYLLABLE GOT;Lo;0;L;;;;;N;;;;;
+A1EB;YI SYLLABLE GOX;Lo;0;L;;;;;N;;;;;
+A1EC;YI SYLLABLE GO;Lo;0;L;;;;;N;;;;;
+A1ED;YI SYLLABLE GOP;Lo;0;L;;;;;N;;;;;
+A1EE;YI SYLLABLE GET;Lo;0;L;;;;;N;;;;;
+A1EF;YI SYLLABLE GEX;Lo;0;L;;;;;N;;;;;
+A1F0;YI SYLLABLE GE;Lo;0;L;;;;;N;;;;;
+A1F1;YI SYLLABLE GEP;Lo;0;L;;;;;N;;;;;
+A1F2;YI SYLLABLE GUT;Lo;0;L;;;;;N;;;;;
+A1F3;YI SYLLABLE GUX;Lo;0;L;;;;;N;;;;;
+A1F4;YI SYLLABLE GU;Lo;0;L;;;;;N;;;;;
+A1F5;YI SYLLABLE GUP;Lo;0;L;;;;;N;;;;;
+A1F6;YI SYLLABLE GURX;Lo;0;L;;;;;N;;;;;
+A1F7;YI SYLLABLE GUR;Lo;0;L;;;;;N;;;;;
+A1F8;YI SYLLABLE KIT;Lo;0;L;;;;;N;;;;;
+A1F9;YI SYLLABLE KIX;Lo;0;L;;;;;N;;;;;
+A1FA;YI SYLLABLE KI;Lo;0;L;;;;;N;;;;;
+A1FB;YI SYLLABLE KIP;Lo;0;L;;;;;N;;;;;
+A1FC;YI SYLLABLE KIEX;Lo;0;L;;;;;N;;;;;
+A1FD;YI SYLLABLE KIE;Lo;0;L;;;;;N;;;;;
+A1FE;YI SYLLABLE KIEP;Lo;0;L;;;;;N;;;;;
+A1FF;YI SYLLABLE KAT;Lo;0;L;;;;;N;;;;;
+A200;YI SYLLABLE KAX;Lo;0;L;;;;;N;;;;;
+A201;YI SYLLABLE KA;Lo;0;L;;;;;N;;;;;
+A202;YI SYLLABLE KAP;Lo;0;L;;;;;N;;;;;
+A203;YI SYLLABLE KUOX;Lo;0;L;;;;;N;;;;;
+A204;YI SYLLABLE KUO;Lo;0;L;;;;;N;;;;;
+A205;YI SYLLABLE KUOP;Lo;0;L;;;;;N;;;;;
+A206;YI SYLLABLE KOT;Lo;0;L;;;;;N;;;;;
+A207;YI SYLLABLE KOX;Lo;0;L;;;;;N;;;;;
+A208;YI SYLLABLE KO;Lo;0;L;;;;;N;;;;;
+A209;YI SYLLABLE KOP;Lo;0;L;;;;;N;;;;;
+A20A;YI SYLLABLE KET;Lo;0;L;;;;;N;;;;;
+A20B;YI SYLLABLE KEX;Lo;0;L;;;;;N;;;;;
+A20C;YI SYLLABLE KE;Lo;0;L;;;;;N;;;;;
+A20D;YI SYLLABLE KEP;Lo;0;L;;;;;N;;;;;
+A20E;YI SYLLABLE KUT;Lo;0;L;;;;;N;;;;;
+A20F;YI SYLLABLE KUX;Lo;0;L;;;;;N;;;;;
+A210;YI SYLLABLE KU;Lo;0;L;;;;;N;;;;;
+A211;YI SYLLABLE KUP;Lo;0;L;;;;;N;;;;;
+A212;YI SYLLABLE KURX;Lo;0;L;;;;;N;;;;;
+A213;YI SYLLABLE KUR;Lo;0;L;;;;;N;;;;;
+A214;YI SYLLABLE GGIT;Lo;0;L;;;;;N;;;;;
+A215;YI SYLLABLE GGIX;Lo;0;L;;;;;N;;;;;
+A216;YI SYLLABLE GGI;Lo;0;L;;;;;N;;;;;
+A217;YI SYLLABLE GGIEX;Lo;0;L;;;;;N;;;;;
+A218;YI SYLLABLE GGIE;Lo;0;L;;;;;N;;;;;
+A219;YI SYLLABLE GGIEP;Lo;0;L;;;;;N;;;;;
+A21A;YI SYLLABLE GGAT;Lo;0;L;;;;;N;;;;;
+A21B;YI SYLLABLE GGAX;Lo;0;L;;;;;N;;;;;
+A21C;YI SYLLABLE GGA;Lo;0;L;;;;;N;;;;;
+A21D;YI SYLLABLE GGAP;Lo;0;L;;;;;N;;;;;
+A21E;YI SYLLABLE GGUOT;Lo;0;L;;;;;N;;;;;
+A21F;YI SYLLABLE GGUOX;Lo;0;L;;;;;N;;;;;
+A220;YI SYLLABLE GGUO;Lo;0;L;;;;;N;;;;;
+A221;YI SYLLABLE GGUOP;Lo;0;L;;;;;N;;;;;
+A222;YI SYLLABLE GGOT;Lo;0;L;;;;;N;;;;;
+A223;YI SYLLABLE GGOX;Lo;0;L;;;;;N;;;;;
+A224;YI SYLLABLE GGO;Lo;0;L;;;;;N;;;;;
+A225;YI SYLLABLE GGOP;Lo;0;L;;;;;N;;;;;
+A226;YI SYLLABLE GGET;Lo;0;L;;;;;N;;;;;
+A227;YI SYLLABLE GGEX;Lo;0;L;;;;;N;;;;;
+A228;YI SYLLABLE GGE;Lo;0;L;;;;;N;;;;;
+A229;YI SYLLABLE GGEP;Lo;0;L;;;;;N;;;;;
+A22A;YI SYLLABLE GGUT;Lo;0;L;;;;;N;;;;;
+A22B;YI SYLLABLE GGUX;Lo;0;L;;;;;N;;;;;
+A22C;YI SYLLABLE GGU;Lo;0;L;;;;;N;;;;;
+A22D;YI SYLLABLE GGUP;Lo;0;L;;;;;N;;;;;
+A22E;YI SYLLABLE GGURX;Lo;0;L;;;;;N;;;;;
+A22F;YI SYLLABLE GGUR;Lo;0;L;;;;;N;;;;;
+A230;YI SYLLABLE MGIEX;Lo;0;L;;;;;N;;;;;
+A231;YI SYLLABLE MGIE;Lo;0;L;;;;;N;;;;;
+A232;YI SYLLABLE MGAT;Lo;0;L;;;;;N;;;;;
+A233;YI SYLLABLE MGAX;Lo;0;L;;;;;N;;;;;
+A234;YI SYLLABLE MGA;Lo;0;L;;;;;N;;;;;
+A235;YI SYLLABLE MGAP;Lo;0;L;;;;;N;;;;;
+A236;YI SYLLABLE MGUOX;Lo;0;L;;;;;N;;;;;
+A237;YI SYLLABLE MGUO;Lo;0;L;;;;;N;;;;;
+A238;YI SYLLABLE MGUOP;Lo;0;L;;;;;N;;;;;
+A239;YI SYLLABLE MGOT;Lo;0;L;;;;;N;;;;;
+A23A;YI SYLLABLE MGOX;Lo;0;L;;;;;N;;;;;
+A23B;YI SYLLABLE MGO;Lo;0;L;;;;;N;;;;;
+A23C;YI SYLLABLE MGOP;Lo;0;L;;;;;N;;;;;
+A23D;YI SYLLABLE MGEX;Lo;0;L;;;;;N;;;;;
+A23E;YI SYLLABLE MGE;Lo;0;L;;;;;N;;;;;
+A23F;YI SYLLABLE MGEP;Lo;0;L;;;;;N;;;;;
+A240;YI SYLLABLE MGUT;Lo;0;L;;;;;N;;;;;
+A241;YI SYLLABLE MGUX;Lo;0;L;;;;;N;;;;;
+A242;YI SYLLABLE MGU;Lo;0;L;;;;;N;;;;;
+A243;YI SYLLABLE MGUP;Lo;0;L;;;;;N;;;;;
+A244;YI SYLLABLE MGURX;Lo;0;L;;;;;N;;;;;
+A245;YI SYLLABLE MGUR;Lo;0;L;;;;;N;;;;;
+A246;YI SYLLABLE HXIT;Lo;0;L;;;;;N;;;;;
+A247;YI SYLLABLE HXIX;Lo;0;L;;;;;N;;;;;
+A248;YI SYLLABLE HXI;Lo;0;L;;;;;N;;;;;
+A249;YI SYLLABLE HXIP;Lo;0;L;;;;;N;;;;;
+A24A;YI SYLLABLE HXIET;Lo;0;L;;;;;N;;;;;
+A24B;YI SYLLABLE HXIEX;Lo;0;L;;;;;N;;;;;
+A24C;YI SYLLABLE HXIE;Lo;0;L;;;;;N;;;;;
+A24D;YI SYLLABLE HXIEP;Lo;0;L;;;;;N;;;;;
+A24E;YI SYLLABLE HXAT;Lo;0;L;;;;;N;;;;;
+A24F;YI SYLLABLE HXAX;Lo;0;L;;;;;N;;;;;
+A250;YI SYLLABLE HXA;Lo;0;L;;;;;N;;;;;
+A251;YI SYLLABLE HXAP;Lo;0;L;;;;;N;;;;;
+A252;YI SYLLABLE HXUOT;Lo;0;L;;;;;N;;;;;
+A253;YI SYLLABLE HXUOX;Lo;0;L;;;;;N;;;;;
+A254;YI SYLLABLE HXUO;Lo;0;L;;;;;N;;;;;
+A255;YI SYLLABLE HXUOP;Lo;0;L;;;;;N;;;;;
+A256;YI SYLLABLE HXOT;Lo;0;L;;;;;N;;;;;
+A257;YI SYLLABLE HXOX;Lo;0;L;;;;;N;;;;;
+A258;YI SYLLABLE HXO;Lo;0;L;;;;;N;;;;;
+A259;YI SYLLABLE HXOP;Lo;0;L;;;;;N;;;;;
+A25A;YI SYLLABLE HXEX;Lo;0;L;;;;;N;;;;;
+A25B;YI SYLLABLE HXE;Lo;0;L;;;;;N;;;;;
+A25C;YI SYLLABLE HXEP;Lo;0;L;;;;;N;;;;;
+A25D;YI SYLLABLE NGIEX;Lo;0;L;;;;;N;;;;;
+A25E;YI SYLLABLE NGIE;Lo;0;L;;;;;N;;;;;
+A25F;YI SYLLABLE NGIEP;Lo;0;L;;;;;N;;;;;
+A260;YI SYLLABLE NGAT;Lo;0;L;;;;;N;;;;;
+A261;YI SYLLABLE NGAX;Lo;0;L;;;;;N;;;;;
+A262;YI SYLLABLE NGA;Lo;0;L;;;;;N;;;;;
+A263;YI SYLLABLE NGAP;Lo;0;L;;;;;N;;;;;
+A264;YI SYLLABLE NGUOT;Lo;0;L;;;;;N;;;;;
+A265;YI SYLLABLE NGUOX;Lo;0;L;;;;;N;;;;;
+A266;YI SYLLABLE NGUO;Lo;0;L;;;;;N;;;;;
+A267;YI SYLLABLE NGOT;Lo;0;L;;;;;N;;;;;
+A268;YI SYLLABLE NGOX;Lo;0;L;;;;;N;;;;;
+A269;YI SYLLABLE NGO;Lo;0;L;;;;;N;;;;;
+A26A;YI SYLLABLE NGOP;Lo;0;L;;;;;N;;;;;
+A26B;YI SYLLABLE NGEX;Lo;0;L;;;;;N;;;;;
+A26C;YI SYLLABLE NGE;Lo;0;L;;;;;N;;;;;
+A26D;YI SYLLABLE NGEP;Lo;0;L;;;;;N;;;;;
+A26E;YI SYLLABLE HIT;Lo;0;L;;;;;N;;;;;
+A26F;YI SYLLABLE HIEX;Lo;0;L;;;;;N;;;;;
+A270;YI SYLLABLE HIE;Lo;0;L;;;;;N;;;;;
+A271;YI SYLLABLE HAT;Lo;0;L;;;;;N;;;;;
+A272;YI SYLLABLE HAX;Lo;0;L;;;;;N;;;;;
+A273;YI SYLLABLE HA;Lo;0;L;;;;;N;;;;;
+A274;YI SYLLABLE HAP;Lo;0;L;;;;;N;;;;;
+A275;YI SYLLABLE HUOT;Lo;0;L;;;;;N;;;;;
+A276;YI SYLLABLE HUOX;Lo;0;L;;;;;N;;;;;
+A277;YI SYLLABLE HUO;Lo;0;L;;;;;N;;;;;
+A278;YI SYLLABLE HUOP;Lo;0;L;;;;;N;;;;;
+A279;YI SYLLABLE HOT;Lo;0;L;;;;;N;;;;;
+A27A;YI SYLLABLE HOX;Lo;0;L;;;;;N;;;;;
+A27B;YI SYLLABLE HO;Lo;0;L;;;;;N;;;;;
+A27C;YI SYLLABLE HOP;Lo;0;L;;;;;N;;;;;
+A27D;YI SYLLABLE HEX;Lo;0;L;;;;;N;;;;;
+A27E;YI SYLLABLE HE;Lo;0;L;;;;;N;;;;;
+A27F;YI SYLLABLE HEP;Lo;0;L;;;;;N;;;;;
+A280;YI SYLLABLE WAT;Lo;0;L;;;;;N;;;;;
+A281;YI SYLLABLE WAX;Lo;0;L;;;;;N;;;;;
+A282;YI SYLLABLE WA;Lo;0;L;;;;;N;;;;;
+A283;YI SYLLABLE WAP;Lo;0;L;;;;;N;;;;;
+A284;YI SYLLABLE WUOX;Lo;0;L;;;;;N;;;;;
+A285;YI SYLLABLE WUO;Lo;0;L;;;;;N;;;;;
+A286;YI SYLLABLE WUOP;Lo;0;L;;;;;N;;;;;
+A287;YI SYLLABLE WOX;Lo;0;L;;;;;N;;;;;
+A288;YI SYLLABLE WO;Lo;0;L;;;;;N;;;;;
+A289;YI SYLLABLE WOP;Lo;0;L;;;;;N;;;;;
+A28A;YI SYLLABLE WEX;Lo;0;L;;;;;N;;;;;
+A28B;YI SYLLABLE WE;Lo;0;L;;;;;N;;;;;
+A28C;YI SYLLABLE WEP;Lo;0;L;;;;;N;;;;;
+A28D;YI SYLLABLE ZIT;Lo;0;L;;;;;N;;;;;
+A28E;YI SYLLABLE ZIX;Lo;0;L;;;;;N;;;;;
+A28F;YI SYLLABLE ZI;Lo;0;L;;;;;N;;;;;
+A290;YI SYLLABLE ZIP;Lo;0;L;;;;;N;;;;;
+A291;YI SYLLABLE ZIEX;Lo;0;L;;;;;N;;;;;
+A292;YI SYLLABLE ZIE;Lo;0;L;;;;;N;;;;;
+A293;YI SYLLABLE ZIEP;Lo;0;L;;;;;N;;;;;
+A294;YI SYLLABLE ZAT;Lo;0;L;;;;;N;;;;;
+A295;YI SYLLABLE ZAX;Lo;0;L;;;;;N;;;;;
+A296;YI SYLLABLE ZA;Lo;0;L;;;;;N;;;;;
+A297;YI SYLLABLE ZAP;Lo;0;L;;;;;N;;;;;
+A298;YI SYLLABLE ZUOX;Lo;0;L;;;;;N;;;;;
+A299;YI SYLLABLE ZUO;Lo;0;L;;;;;N;;;;;
+A29A;YI SYLLABLE ZUOP;Lo;0;L;;;;;N;;;;;
+A29B;YI SYLLABLE ZOT;Lo;0;L;;;;;N;;;;;
+A29C;YI SYLLABLE ZOX;Lo;0;L;;;;;N;;;;;
+A29D;YI SYLLABLE ZO;Lo;0;L;;;;;N;;;;;
+A29E;YI SYLLABLE ZOP;Lo;0;L;;;;;N;;;;;
+A29F;YI SYLLABLE ZEX;Lo;0;L;;;;;N;;;;;
+A2A0;YI SYLLABLE ZE;Lo;0;L;;;;;N;;;;;
+A2A1;YI SYLLABLE ZEP;Lo;0;L;;;;;N;;;;;
+A2A2;YI SYLLABLE ZUT;Lo;0;L;;;;;N;;;;;
+A2A3;YI SYLLABLE ZUX;Lo;0;L;;;;;N;;;;;
+A2A4;YI SYLLABLE ZU;Lo;0;L;;;;;N;;;;;
+A2A5;YI SYLLABLE ZUP;Lo;0;L;;;;;N;;;;;
+A2A6;YI SYLLABLE ZURX;Lo;0;L;;;;;N;;;;;
+A2A7;YI SYLLABLE ZUR;Lo;0;L;;;;;N;;;;;
+A2A8;YI SYLLABLE ZYT;Lo;0;L;;;;;N;;;;;
+A2A9;YI SYLLABLE ZYX;Lo;0;L;;;;;N;;;;;
+A2AA;YI SYLLABLE ZY;Lo;0;L;;;;;N;;;;;
+A2AB;YI SYLLABLE ZYP;Lo;0;L;;;;;N;;;;;
+A2AC;YI SYLLABLE ZYRX;Lo;0;L;;;;;N;;;;;
+A2AD;YI SYLLABLE ZYR;Lo;0;L;;;;;N;;;;;
+A2AE;YI SYLLABLE CIT;Lo;0;L;;;;;N;;;;;
+A2AF;YI SYLLABLE CIX;Lo;0;L;;;;;N;;;;;
+A2B0;YI SYLLABLE CI;Lo;0;L;;;;;N;;;;;
+A2B1;YI SYLLABLE CIP;Lo;0;L;;;;;N;;;;;
+A2B2;YI SYLLABLE CIET;Lo;0;L;;;;;N;;;;;
+A2B3;YI SYLLABLE CIEX;Lo;0;L;;;;;N;;;;;
+A2B4;YI SYLLABLE CIE;Lo;0;L;;;;;N;;;;;
+A2B5;YI SYLLABLE CIEP;Lo;0;L;;;;;N;;;;;
+A2B6;YI SYLLABLE CAT;Lo;0;L;;;;;N;;;;;
+A2B7;YI SYLLABLE CAX;Lo;0;L;;;;;N;;;;;
+A2B8;YI SYLLABLE CA;Lo;0;L;;;;;N;;;;;
+A2B9;YI SYLLABLE CAP;Lo;0;L;;;;;N;;;;;
+A2BA;YI SYLLABLE CUOX;Lo;0;L;;;;;N;;;;;
+A2BB;YI SYLLABLE CUO;Lo;0;L;;;;;N;;;;;
+A2BC;YI SYLLABLE CUOP;Lo;0;L;;;;;N;;;;;
+A2BD;YI SYLLABLE COT;Lo;0;L;;;;;N;;;;;
+A2BE;YI SYLLABLE COX;Lo;0;L;;;;;N;;;;;
+A2BF;YI SYLLABLE CO;Lo;0;L;;;;;N;;;;;
+A2C0;YI SYLLABLE COP;Lo;0;L;;;;;N;;;;;
+A2C1;YI SYLLABLE CEX;Lo;0;L;;;;;N;;;;;
+A2C2;YI SYLLABLE CE;Lo;0;L;;;;;N;;;;;
+A2C3;YI SYLLABLE CEP;Lo;0;L;;;;;N;;;;;
+A2C4;YI SYLLABLE CUT;Lo;0;L;;;;;N;;;;;
+A2C5;YI SYLLABLE CUX;Lo;0;L;;;;;N;;;;;
+A2C6;YI SYLLABLE CU;Lo;0;L;;;;;N;;;;;
+A2C7;YI SYLLABLE CUP;Lo;0;L;;;;;N;;;;;
+A2C8;YI SYLLABLE CURX;Lo;0;L;;;;;N;;;;;
+A2C9;YI SYLLABLE CUR;Lo;0;L;;;;;N;;;;;
+A2CA;YI SYLLABLE CYT;Lo;0;L;;;;;N;;;;;
+A2CB;YI SYLLABLE CYX;Lo;0;L;;;;;N;;;;;
+A2CC;YI SYLLABLE CY;Lo;0;L;;;;;N;;;;;
+A2CD;YI SYLLABLE CYP;Lo;0;L;;;;;N;;;;;
+A2CE;YI SYLLABLE CYRX;Lo;0;L;;;;;N;;;;;
+A2CF;YI SYLLABLE CYR;Lo;0;L;;;;;N;;;;;
+A2D0;YI SYLLABLE ZZIT;Lo;0;L;;;;;N;;;;;
+A2D1;YI SYLLABLE ZZIX;Lo;0;L;;;;;N;;;;;
+A2D2;YI SYLLABLE ZZI;Lo;0;L;;;;;N;;;;;
+A2D3;YI SYLLABLE ZZIP;Lo;0;L;;;;;N;;;;;
+A2D4;YI SYLLABLE ZZIET;Lo;0;L;;;;;N;;;;;
+A2D5;YI SYLLABLE ZZIEX;Lo;0;L;;;;;N;;;;;
+A2D6;YI SYLLABLE ZZIE;Lo;0;L;;;;;N;;;;;
+A2D7;YI SYLLABLE ZZIEP;Lo;0;L;;;;;N;;;;;
+A2D8;YI SYLLABLE ZZAT;Lo;0;L;;;;;N;;;;;
+A2D9;YI SYLLABLE ZZAX;Lo;0;L;;;;;N;;;;;
+A2DA;YI SYLLABLE ZZA;Lo;0;L;;;;;N;;;;;
+A2DB;YI SYLLABLE ZZAP;Lo;0;L;;;;;N;;;;;
+A2DC;YI SYLLABLE ZZOX;Lo;0;L;;;;;N;;;;;
+A2DD;YI SYLLABLE ZZO;Lo;0;L;;;;;N;;;;;
+A2DE;YI SYLLABLE ZZOP;Lo;0;L;;;;;N;;;;;
+A2DF;YI SYLLABLE ZZEX;Lo;0;L;;;;;N;;;;;
+A2E0;YI SYLLABLE ZZE;Lo;0;L;;;;;N;;;;;
+A2E1;YI SYLLABLE ZZEP;Lo;0;L;;;;;N;;;;;
+A2E2;YI SYLLABLE ZZUX;Lo;0;L;;;;;N;;;;;
+A2E3;YI SYLLABLE ZZU;Lo;0;L;;;;;N;;;;;
+A2E4;YI SYLLABLE ZZUP;Lo;0;L;;;;;N;;;;;
+A2E5;YI SYLLABLE ZZURX;Lo;0;L;;;;;N;;;;;
+A2E6;YI SYLLABLE ZZUR;Lo;0;L;;;;;N;;;;;
+A2E7;YI SYLLABLE ZZYT;Lo;0;L;;;;;N;;;;;
+A2E8;YI SYLLABLE ZZYX;Lo;0;L;;;;;N;;;;;
+A2E9;YI SYLLABLE ZZY;Lo;0;L;;;;;N;;;;;
+A2EA;YI SYLLABLE ZZYP;Lo;0;L;;;;;N;;;;;
+A2EB;YI SYLLABLE ZZYRX;Lo;0;L;;;;;N;;;;;
+A2EC;YI SYLLABLE ZZYR;Lo;0;L;;;;;N;;;;;
+A2ED;YI SYLLABLE NZIT;Lo;0;L;;;;;N;;;;;
+A2EE;YI SYLLABLE NZIX;Lo;0;L;;;;;N;;;;;
+A2EF;YI SYLLABLE NZI;Lo;0;L;;;;;N;;;;;
+A2F0;YI SYLLABLE NZIP;Lo;0;L;;;;;N;;;;;
+A2F1;YI SYLLABLE NZIEX;Lo;0;L;;;;;N;;;;;
+A2F2;YI SYLLABLE NZIE;Lo;0;L;;;;;N;;;;;
+A2F3;YI SYLLABLE NZIEP;Lo;0;L;;;;;N;;;;;
+A2F4;YI SYLLABLE NZAT;Lo;0;L;;;;;N;;;;;
+A2F5;YI SYLLABLE NZAX;Lo;0;L;;;;;N;;;;;
+A2F6;YI SYLLABLE NZA;Lo;0;L;;;;;N;;;;;
+A2F7;YI SYLLABLE NZAP;Lo;0;L;;;;;N;;;;;
+A2F8;YI SYLLABLE NZUOX;Lo;0;L;;;;;N;;;;;
+A2F9;YI SYLLABLE NZUO;Lo;0;L;;;;;N;;;;;
+A2FA;YI SYLLABLE NZOX;Lo;0;L;;;;;N;;;;;
+A2FB;YI SYLLABLE NZOP;Lo;0;L;;;;;N;;;;;
+A2FC;YI SYLLABLE NZEX;Lo;0;L;;;;;N;;;;;
+A2FD;YI SYLLABLE NZE;Lo;0;L;;;;;N;;;;;
+A2FE;YI SYLLABLE NZUX;Lo;0;L;;;;;N;;;;;
+A2FF;YI SYLLABLE NZU;Lo;0;L;;;;;N;;;;;
+A300;YI SYLLABLE NZUP;Lo;0;L;;;;;N;;;;;
+A301;YI SYLLABLE NZURX;Lo;0;L;;;;;N;;;;;
+A302;YI SYLLABLE NZUR;Lo;0;L;;;;;N;;;;;
+A303;YI SYLLABLE NZYT;Lo;0;L;;;;;N;;;;;
+A304;YI SYLLABLE NZYX;Lo;0;L;;;;;N;;;;;
+A305;YI SYLLABLE NZY;Lo;0;L;;;;;N;;;;;
+A306;YI SYLLABLE NZYP;Lo;0;L;;;;;N;;;;;
+A307;YI SYLLABLE NZYRX;Lo;0;L;;;;;N;;;;;
+A308;YI SYLLABLE NZYR;Lo;0;L;;;;;N;;;;;
+A309;YI SYLLABLE SIT;Lo;0;L;;;;;N;;;;;
+A30A;YI SYLLABLE SIX;Lo;0;L;;;;;N;;;;;
+A30B;YI SYLLABLE SI;Lo;0;L;;;;;N;;;;;
+A30C;YI SYLLABLE SIP;Lo;0;L;;;;;N;;;;;
+A30D;YI SYLLABLE SIEX;Lo;0;L;;;;;N;;;;;
+A30E;YI SYLLABLE SIE;Lo;0;L;;;;;N;;;;;
+A30F;YI SYLLABLE SIEP;Lo;0;L;;;;;N;;;;;
+A310;YI SYLLABLE SAT;Lo;0;L;;;;;N;;;;;
+A311;YI SYLLABLE SAX;Lo;0;L;;;;;N;;;;;
+A312;YI SYLLABLE SA;Lo;0;L;;;;;N;;;;;
+A313;YI SYLLABLE SAP;Lo;0;L;;;;;N;;;;;
+A314;YI SYLLABLE SUOX;Lo;0;L;;;;;N;;;;;
+A315;YI SYLLABLE SUO;Lo;0;L;;;;;N;;;;;
+A316;YI SYLLABLE SUOP;Lo;0;L;;;;;N;;;;;
+A317;YI SYLLABLE SOT;Lo;0;L;;;;;N;;;;;
+A318;YI SYLLABLE SOX;Lo;0;L;;;;;N;;;;;
+A319;YI SYLLABLE SO;Lo;0;L;;;;;N;;;;;
+A31A;YI SYLLABLE SOP;Lo;0;L;;;;;N;;;;;
+A31B;YI SYLLABLE SEX;Lo;0;L;;;;;N;;;;;
+A31C;YI SYLLABLE SE;Lo;0;L;;;;;N;;;;;
+A31D;YI SYLLABLE SEP;Lo;0;L;;;;;N;;;;;
+A31E;YI SYLLABLE SUT;Lo;0;L;;;;;N;;;;;
+A31F;YI SYLLABLE SUX;Lo;0;L;;;;;N;;;;;
+A320;YI SYLLABLE SU;Lo;0;L;;;;;N;;;;;
+A321;YI SYLLABLE SUP;Lo;0;L;;;;;N;;;;;
+A322;YI SYLLABLE SURX;Lo;0;L;;;;;N;;;;;
+A323;YI SYLLABLE SUR;Lo;0;L;;;;;N;;;;;
+A324;YI SYLLABLE SYT;Lo;0;L;;;;;N;;;;;
+A325;YI SYLLABLE SYX;Lo;0;L;;;;;N;;;;;
+A326;YI SYLLABLE SY;Lo;0;L;;;;;N;;;;;
+A327;YI SYLLABLE SYP;Lo;0;L;;;;;N;;;;;
+A328;YI SYLLABLE SYRX;Lo;0;L;;;;;N;;;;;
+A329;YI SYLLABLE SYR;Lo;0;L;;;;;N;;;;;
+A32A;YI SYLLABLE SSIT;Lo;0;L;;;;;N;;;;;
+A32B;YI SYLLABLE SSIX;Lo;0;L;;;;;N;;;;;
+A32C;YI SYLLABLE SSI;Lo;0;L;;;;;N;;;;;
+A32D;YI SYLLABLE SSIP;Lo;0;L;;;;;N;;;;;
+A32E;YI SYLLABLE SSIEX;Lo;0;L;;;;;N;;;;;
+A32F;YI SYLLABLE SSIE;Lo;0;L;;;;;N;;;;;
+A330;YI SYLLABLE SSIEP;Lo;0;L;;;;;N;;;;;
+A331;YI SYLLABLE SSAT;Lo;0;L;;;;;N;;;;;
+A332;YI SYLLABLE SSAX;Lo;0;L;;;;;N;;;;;
+A333;YI SYLLABLE SSA;Lo;0;L;;;;;N;;;;;
+A334;YI SYLLABLE SSAP;Lo;0;L;;;;;N;;;;;
+A335;YI SYLLABLE SSOT;Lo;0;L;;;;;N;;;;;
+A336;YI SYLLABLE SSOX;Lo;0;L;;;;;N;;;;;
+A337;YI SYLLABLE SSO;Lo;0;L;;;;;N;;;;;
+A338;YI SYLLABLE SSOP;Lo;0;L;;;;;N;;;;;
+A339;YI SYLLABLE SSEX;Lo;0;L;;;;;N;;;;;
+A33A;YI SYLLABLE SSE;Lo;0;L;;;;;N;;;;;
+A33B;YI SYLLABLE SSEP;Lo;0;L;;;;;N;;;;;
+A33C;YI SYLLABLE SSUT;Lo;0;L;;;;;N;;;;;
+A33D;YI SYLLABLE SSUX;Lo;0;L;;;;;N;;;;;
+A33E;YI SYLLABLE SSU;Lo;0;L;;;;;N;;;;;
+A33F;YI SYLLABLE SSUP;Lo;0;L;;;;;N;;;;;
+A340;YI SYLLABLE SSYT;Lo;0;L;;;;;N;;;;;
+A341;YI SYLLABLE SSYX;Lo;0;L;;;;;N;;;;;
+A342;YI SYLLABLE SSY;Lo;0;L;;;;;N;;;;;
+A343;YI SYLLABLE SSYP;Lo;0;L;;;;;N;;;;;
+A344;YI SYLLABLE SSYRX;Lo;0;L;;;;;N;;;;;
+A345;YI SYLLABLE SSYR;Lo;0;L;;;;;N;;;;;
+A346;YI SYLLABLE ZHAT;Lo;0;L;;;;;N;;;;;
+A347;YI SYLLABLE ZHAX;Lo;0;L;;;;;N;;;;;
+A348;YI SYLLABLE ZHA;Lo;0;L;;;;;N;;;;;
+A349;YI SYLLABLE ZHAP;Lo;0;L;;;;;N;;;;;
+A34A;YI SYLLABLE ZHUOX;Lo;0;L;;;;;N;;;;;
+A34B;YI SYLLABLE ZHUO;Lo;0;L;;;;;N;;;;;
+A34C;YI SYLLABLE ZHUOP;Lo;0;L;;;;;N;;;;;
+A34D;YI SYLLABLE ZHOT;Lo;0;L;;;;;N;;;;;
+A34E;YI SYLLABLE ZHOX;Lo;0;L;;;;;N;;;;;
+A34F;YI SYLLABLE ZHO;Lo;0;L;;;;;N;;;;;
+A350;YI SYLLABLE ZHOP;Lo;0;L;;;;;N;;;;;
+A351;YI SYLLABLE ZHET;Lo;0;L;;;;;N;;;;;
+A352;YI SYLLABLE ZHEX;Lo;0;L;;;;;N;;;;;
+A353;YI SYLLABLE ZHE;Lo;0;L;;;;;N;;;;;
+A354;YI SYLLABLE ZHEP;Lo;0;L;;;;;N;;;;;
+A355;YI SYLLABLE ZHUT;Lo;0;L;;;;;N;;;;;
+A356;YI SYLLABLE ZHUX;Lo;0;L;;;;;N;;;;;
+A357;YI SYLLABLE ZHU;Lo;0;L;;;;;N;;;;;
+A358;YI SYLLABLE ZHUP;Lo;0;L;;;;;N;;;;;
+A359;YI SYLLABLE ZHURX;Lo;0;L;;;;;N;;;;;
+A35A;YI SYLLABLE ZHUR;Lo;0;L;;;;;N;;;;;
+A35B;YI SYLLABLE ZHYT;Lo;0;L;;;;;N;;;;;
+A35C;YI SYLLABLE ZHYX;Lo;0;L;;;;;N;;;;;
+A35D;YI SYLLABLE ZHY;Lo;0;L;;;;;N;;;;;
+A35E;YI SYLLABLE ZHYP;Lo;0;L;;;;;N;;;;;
+A35F;YI SYLLABLE ZHYRX;Lo;0;L;;;;;N;;;;;
+A360;YI SYLLABLE ZHYR;Lo;0;L;;;;;N;;;;;
+A361;YI SYLLABLE CHAT;Lo;0;L;;;;;N;;;;;
+A362;YI SYLLABLE CHAX;Lo;0;L;;;;;N;;;;;
+A363;YI SYLLABLE CHA;Lo;0;L;;;;;N;;;;;
+A364;YI SYLLABLE CHAP;Lo;0;L;;;;;N;;;;;
+A365;YI SYLLABLE CHUOT;Lo;0;L;;;;;N;;;;;
+A366;YI SYLLABLE CHUOX;Lo;0;L;;;;;N;;;;;
+A367;YI SYLLABLE CHUO;Lo;0;L;;;;;N;;;;;
+A368;YI SYLLABLE CHUOP;Lo;0;L;;;;;N;;;;;
+A369;YI SYLLABLE CHOT;Lo;0;L;;;;;N;;;;;
+A36A;YI SYLLABLE CHOX;Lo;0;L;;;;;N;;;;;
+A36B;YI SYLLABLE CHO;Lo;0;L;;;;;N;;;;;
+A36C;YI SYLLABLE CHOP;Lo;0;L;;;;;N;;;;;
+A36D;YI SYLLABLE CHET;Lo;0;L;;;;;N;;;;;
+A36E;YI SYLLABLE CHEX;Lo;0;L;;;;;N;;;;;
+A36F;YI SYLLABLE CHE;Lo;0;L;;;;;N;;;;;
+A370;YI SYLLABLE CHEP;Lo;0;L;;;;;N;;;;;
+A371;YI SYLLABLE CHUX;Lo;0;L;;;;;N;;;;;
+A372;YI SYLLABLE CHU;Lo;0;L;;;;;N;;;;;
+A373;YI SYLLABLE CHUP;Lo;0;L;;;;;N;;;;;
+A374;YI SYLLABLE CHURX;Lo;0;L;;;;;N;;;;;
+A375;YI SYLLABLE CHUR;Lo;0;L;;;;;N;;;;;
+A376;YI SYLLABLE CHYT;Lo;0;L;;;;;N;;;;;
+A377;YI SYLLABLE CHYX;Lo;0;L;;;;;N;;;;;
+A378;YI SYLLABLE CHY;Lo;0;L;;;;;N;;;;;
+A379;YI SYLLABLE CHYP;Lo;0;L;;;;;N;;;;;
+A37A;YI SYLLABLE CHYRX;Lo;0;L;;;;;N;;;;;
+A37B;YI SYLLABLE CHYR;Lo;0;L;;;;;N;;;;;
+A37C;YI SYLLABLE RRAX;Lo;0;L;;;;;N;;;;;
+A37D;YI SYLLABLE RRA;Lo;0;L;;;;;N;;;;;
+A37E;YI SYLLABLE RRUOX;Lo;0;L;;;;;N;;;;;
+A37F;YI SYLLABLE RRUO;Lo;0;L;;;;;N;;;;;
+A380;YI SYLLABLE RROT;Lo;0;L;;;;;N;;;;;
+A381;YI SYLLABLE RROX;Lo;0;L;;;;;N;;;;;
+A382;YI SYLLABLE RRO;Lo;0;L;;;;;N;;;;;
+A383;YI SYLLABLE RROP;Lo;0;L;;;;;N;;;;;
+A384;YI SYLLABLE RRET;Lo;0;L;;;;;N;;;;;
+A385;YI SYLLABLE RREX;Lo;0;L;;;;;N;;;;;
+A386;YI SYLLABLE RRE;Lo;0;L;;;;;N;;;;;
+A387;YI SYLLABLE RREP;Lo;0;L;;;;;N;;;;;
+A388;YI SYLLABLE RRUT;Lo;0;L;;;;;N;;;;;
+A389;YI SYLLABLE RRUX;Lo;0;L;;;;;N;;;;;
+A38A;YI SYLLABLE RRU;Lo;0;L;;;;;N;;;;;
+A38B;YI SYLLABLE RRUP;Lo;0;L;;;;;N;;;;;
+A38C;YI SYLLABLE RRURX;Lo;0;L;;;;;N;;;;;
+A38D;YI SYLLABLE RRUR;Lo;0;L;;;;;N;;;;;
+A38E;YI SYLLABLE RRYT;Lo;0;L;;;;;N;;;;;
+A38F;YI SYLLABLE RRYX;Lo;0;L;;;;;N;;;;;
+A390;YI SYLLABLE RRY;Lo;0;L;;;;;N;;;;;
+A391;YI SYLLABLE RRYP;Lo;0;L;;;;;N;;;;;
+A392;YI SYLLABLE RRYRX;Lo;0;L;;;;;N;;;;;
+A393;YI SYLLABLE RRYR;Lo;0;L;;;;;N;;;;;
+A394;YI SYLLABLE NRAT;Lo;0;L;;;;;N;;;;;
+A395;YI SYLLABLE NRAX;Lo;0;L;;;;;N;;;;;
+A396;YI SYLLABLE NRA;Lo;0;L;;;;;N;;;;;
+A397;YI SYLLABLE NRAP;Lo;0;L;;;;;N;;;;;
+A398;YI SYLLABLE NROX;Lo;0;L;;;;;N;;;;;
+A399;YI SYLLABLE NRO;Lo;0;L;;;;;N;;;;;
+A39A;YI SYLLABLE NROP;Lo;0;L;;;;;N;;;;;
+A39B;YI SYLLABLE NRET;Lo;0;L;;;;;N;;;;;
+A39C;YI SYLLABLE NREX;Lo;0;L;;;;;N;;;;;
+A39D;YI SYLLABLE NRE;Lo;0;L;;;;;N;;;;;
+A39E;YI SYLLABLE NREP;Lo;0;L;;;;;N;;;;;
+A39F;YI SYLLABLE NRUT;Lo;0;L;;;;;N;;;;;
+A3A0;YI SYLLABLE NRUX;Lo;0;L;;;;;N;;;;;
+A3A1;YI SYLLABLE NRU;Lo;0;L;;;;;N;;;;;
+A3A2;YI SYLLABLE NRUP;Lo;0;L;;;;;N;;;;;
+A3A3;YI SYLLABLE NRURX;Lo;0;L;;;;;N;;;;;
+A3A4;YI SYLLABLE NRUR;Lo;0;L;;;;;N;;;;;
+A3A5;YI SYLLABLE NRYT;Lo;0;L;;;;;N;;;;;
+A3A6;YI SYLLABLE NRYX;Lo;0;L;;;;;N;;;;;
+A3A7;YI SYLLABLE NRY;Lo;0;L;;;;;N;;;;;
+A3A8;YI SYLLABLE NRYP;Lo;0;L;;;;;N;;;;;
+A3A9;YI SYLLABLE NRYRX;Lo;0;L;;;;;N;;;;;
+A3AA;YI SYLLABLE NRYR;Lo;0;L;;;;;N;;;;;
+A3AB;YI SYLLABLE SHAT;Lo;0;L;;;;;N;;;;;
+A3AC;YI SYLLABLE SHAX;Lo;0;L;;;;;N;;;;;
+A3AD;YI SYLLABLE SHA;Lo;0;L;;;;;N;;;;;
+A3AE;YI SYLLABLE SHAP;Lo;0;L;;;;;N;;;;;
+A3AF;YI SYLLABLE SHUOX;Lo;0;L;;;;;N;;;;;
+A3B0;YI SYLLABLE SHUO;Lo;0;L;;;;;N;;;;;
+A3B1;YI SYLLABLE SHUOP;Lo;0;L;;;;;N;;;;;
+A3B2;YI SYLLABLE SHOT;Lo;0;L;;;;;N;;;;;
+A3B3;YI SYLLABLE SHOX;Lo;0;L;;;;;N;;;;;
+A3B4;YI SYLLABLE SHO;Lo;0;L;;;;;N;;;;;
+A3B5;YI SYLLABLE SHOP;Lo;0;L;;;;;N;;;;;
+A3B6;YI SYLLABLE SHET;Lo;0;L;;;;;N;;;;;
+A3B7;YI SYLLABLE SHEX;Lo;0;L;;;;;N;;;;;
+A3B8;YI SYLLABLE SHE;Lo;0;L;;;;;N;;;;;
+A3B9;YI SYLLABLE SHEP;Lo;0;L;;;;;N;;;;;
+A3BA;YI SYLLABLE SHUT;Lo;0;L;;;;;N;;;;;
+A3BB;YI SYLLABLE SHUX;Lo;0;L;;;;;N;;;;;
+A3BC;YI SYLLABLE SHU;Lo;0;L;;;;;N;;;;;
+A3BD;YI SYLLABLE SHUP;Lo;0;L;;;;;N;;;;;
+A3BE;YI SYLLABLE SHURX;Lo;0;L;;;;;N;;;;;
+A3BF;YI SYLLABLE SHUR;Lo;0;L;;;;;N;;;;;
+A3C0;YI SYLLABLE SHYT;Lo;0;L;;;;;N;;;;;
+A3C1;YI SYLLABLE SHYX;Lo;0;L;;;;;N;;;;;
+A3C2;YI SYLLABLE SHY;Lo;0;L;;;;;N;;;;;
+A3C3;YI SYLLABLE SHYP;Lo;0;L;;;;;N;;;;;
+A3C4;YI SYLLABLE SHYRX;Lo;0;L;;;;;N;;;;;
+A3C5;YI SYLLABLE SHYR;Lo;0;L;;;;;N;;;;;
+A3C6;YI SYLLABLE RAT;Lo;0;L;;;;;N;;;;;
+A3C7;YI SYLLABLE RAX;Lo;0;L;;;;;N;;;;;
+A3C8;YI SYLLABLE RA;Lo;0;L;;;;;N;;;;;
+A3C9;YI SYLLABLE RAP;Lo;0;L;;;;;N;;;;;
+A3CA;YI SYLLABLE RUOX;Lo;0;L;;;;;N;;;;;
+A3CB;YI SYLLABLE RUO;Lo;0;L;;;;;N;;;;;
+A3CC;YI SYLLABLE RUOP;Lo;0;L;;;;;N;;;;;
+A3CD;YI SYLLABLE ROT;Lo;0;L;;;;;N;;;;;
+A3CE;YI SYLLABLE ROX;Lo;0;L;;;;;N;;;;;
+A3CF;YI SYLLABLE RO;Lo;0;L;;;;;N;;;;;
+A3D0;YI SYLLABLE ROP;Lo;0;L;;;;;N;;;;;
+A3D1;YI SYLLABLE REX;Lo;0;L;;;;;N;;;;;
+A3D2;YI SYLLABLE RE;Lo;0;L;;;;;N;;;;;
+A3D3;YI SYLLABLE REP;Lo;0;L;;;;;N;;;;;
+A3D4;YI SYLLABLE RUT;Lo;0;L;;;;;N;;;;;
+A3D5;YI SYLLABLE RUX;Lo;0;L;;;;;N;;;;;
+A3D6;YI SYLLABLE RU;Lo;0;L;;;;;N;;;;;
+A3D7;YI SYLLABLE RUP;Lo;0;L;;;;;N;;;;;
+A3D8;YI SYLLABLE RURX;Lo;0;L;;;;;N;;;;;
+A3D9;YI SYLLABLE RUR;Lo;0;L;;;;;N;;;;;
+A3DA;YI SYLLABLE RYT;Lo;0;L;;;;;N;;;;;
+A3DB;YI SYLLABLE RYX;Lo;0;L;;;;;N;;;;;
+A3DC;YI SYLLABLE RY;Lo;0;L;;;;;N;;;;;
+A3DD;YI SYLLABLE RYP;Lo;0;L;;;;;N;;;;;
+A3DE;YI SYLLABLE RYRX;Lo;0;L;;;;;N;;;;;
+A3DF;YI SYLLABLE RYR;Lo;0;L;;;;;N;;;;;
+A3E0;YI SYLLABLE JIT;Lo;0;L;;;;;N;;;;;
+A3E1;YI SYLLABLE JIX;Lo;0;L;;;;;N;;;;;
+A3E2;YI SYLLABLE JI;Lo;0;L;;;;;N;;;;;
+A3E3;YI SYLLABLE JIP;Lo;0;L;;;;;N;;;;;
+A3E4;YI SYLLABLE JIET;Lo;0;L;;;;;N;;;;;
+A3E5;YI SYLLABLE JIEX;Lo;0;L;;;;;N;;;;;
+A3E6;YI SYLLABLE JIE;Lo;0;L;;;;;N;;;;;
+A3E7;YI SYLLABLE JIEP;Lo;0;L;;;;;N;;;;;
+A3E8;YI SYLLABLE JUOT;Lo;0;L;;;;;N;;;;;
+A3E9;YI SYLLABLE JUOX;Lo;0;L;;;;;N;;;;;
+A3EA;YI SYLLABLE JUO;Lo;0;L;;;;;N;;;;;
+A3EB;YI SYLLABLE JUOP;Lo;0;L;;;;;N;;;;;
+A3EC;YI SYLLABLE JOT;Lo;0;L;;;;;N;;;;;
+A3ED;YI SYLLABLE JOX;Lo;0;L;;;;;N;;;;;
+A3EE;YI SYLLABLE JO;Lo;0;L;;;;;N;;;;;
+A3EF;YI SYLLABLE JOP;Lo;0;L;;;;;N;;;;;
+A3F0;YI SYLLABLE JUT;Lo;0;L;;;;;N;;;;;
+A3F1;YI SYLLABLE JUX;Lo;0;L;;;;;N;;;;;
+A3F2;YI SYLLABLE JU;Lo;0;L;;;;;N;;;;;
+A3F3;YI SYLLABLE JUP;Lo;0;L;;;;;N;;;;;
+A3F4;YI SYLLABLE JURX;Lo;0;L;;;;;N;;;;;
+A3F5;YI SYLLABLE JUR;Lo;0;L;;;;;N;;;;;
+A3F6;YI SYLLABLE JYT;Lo;0;L;;;;;N;;;;;
+A3F7;YI SYLLABLE JYX;Lo;0;L;;;;;N;;;;;
+A3F8;YI SYLLABLE JY;Lo;0;L;;;;;N;;;;;
+A3F9;YI SYLLABLE JYP;Lo;0;L;;;;;N;;;;;
+A3FA;YI SYLLABLE JYRX;Lo;0;L;;;;;N;;;;;
+A3FB;YI SYLLABLE JYR;Lo;0;L;;;;;N;;;;;
+A3FC;YI SYLLABLE QIT;Lo;0;L;;;;;N;;;;;
+A3FD;YI SYLLABLE QIX;Lo;0;L;;;;;N;;;;;
+A3FE;YI SYLLABLE QI;Lo;0;L;;;;;N;;;;;
+A3FF;YI SYLLABLE QIP;Lo;0;L;;;;;N;;;;;
+A400;YI SYLLABLE QIET;Lo;0;L;;;;;N;;;;;
+A401;YI SYLLABLE QIEX;Lo;0;L;;;;;N;;;;;
+A402;YI SYLLABLE QIE;Lo;0;L;;;;;N;;;;;
+A403;YI SYLLABLE QIEP;Lo;0;L;;;;;N;;;;;
+A404;YI SYLLABLE QUOT;Lo;0;L;;;;;N;;;;;
+A405;YI SYLLABLE QUOX;Lo;0;L;;;;;N;;;;;
+A406;YI SYLLABLE QUO;Lo;0;L;;;;;N;;;;;
+A407;YI SYLLABLE QUOP;Lo;0;L;;;;;N;;;;;
+A408;YI SYLLABLE QOT;Lo;0;L;;;;;N;;;;;
+A409;YI SYLLABLE QOX;Lo;0;L;;;;;N;;;;;
+A40A;YI SYLLABLE QO;Lo;0;L;;;;;N;;;;;
+A40B;YI SYLLABLE QOP;Lo;0;L;;;;;N;;;;;
+A40C;YI SYLLABLE QUT;Lo;0;L;;;;;N;;;;;
+A40D;YI SYLLABLE QUX;Lo;0;L;;;;;N;;;;;
+A40E;YI SYLLABLE QU;Lo;0;L;;;;;N;;;;;
+A40F;YI SYLLABLE QUP;Lo;0;L;;;;;N;;;;;
+A410;YI SYLLABLE QURX;Lo;0;L;;;;;N;;;;;
+A411;YI SYLLABLE QUR;Lo;0;L;;;;;N;;;;;
+A412;YI SYLLABLE QYT;Lo;0;L;;;;;N;;;;;
+A413;YI SYLLABLE QYX;Lo;0;L;;;;;N;;;;;
+A414;YI SYLLABLE QY;Lo;0;L;;;;;N;;;;;
+A415;YI SYLLABLE QYP;Lo;0;L;;;;;N;;;;;
+A416;YI SYLLABLE QYRX;Lo;0;L;;;;;N;;;;;
+A417;YI SYLLABLE QYR;Lo;0;L;;;;;N;;;;;
+A418;YI SYLLABLE JJIT;Lo;0;L;;;;;N;;;;;
+A419;YI SYLLABLE JJIX;Lo;0;L;;;;;N;;;;;
+A41A;YI SYLLABLE JJI;Lo;0;L;;;;;N;;;;;
+A41B;YI SYLLABLE JJIP;Lo;0;L;;;;;N;;;;;
+A41C;YI SYLLABLE JJIET;Lo;0;L;;;;;N;;;;;
+A41D;YI SYLLABLE JJIEX;Lo;0;L;;;;;N;;;;;
+A41E;YI SYLLABLE JJIE;Lo;0;L;;;;;N;;;;;
+A41F;YI SYLLABLE JJIEP;Lo;0;L;;;;;N;;;;;
+A420;YI SYLLABLE JJUOX;Lo;0;L;;;;;N;;;;;
+A421;YI SYLLABLE JJUO;Lo;0;L;;;;;N;;;;;
+A422;YI SYLLABLE JJUOP;Lo;0;L;;;;;N;;;;;
+A423;YI SYLLABLE JJOT;Lo;0;L;;;;;N;;;;;
+A424;YI SYLLABLE JJOX;Lo;0;L;;;;;N;;;;;
+A425;YI SYLLABLE JJO;Lo;0;L;;;;;N;;;;;
+A426;YI SYLLABLE JJOP;Lo;0;L;;;;;N;;;;;
+A427;YI SYLLABLE JJUT;Lo;0;L;;;;;N;;;;;
+A428;YI SYLLABLE JJUX;Lo;0;L;;;;;N;;;;;
+A429;YI SYLLABLE JJU;Lo;0;L;;;;;N;;;;;
+A42A;YI SYLLABLE JJUP;Lo;0;L;;;;;N;;;;;
+A42B;YI SYLLABLE JJURX;Lo;0;L;;;;;N;;;;;
+A42C;YI SYLLABLE JJUR;Lo;0;L;;;;;N;;;;;
+A42D;YI SYLLABLE JJYT;Lo;0;L;;;;;N;;;;;
+A42E;YI SYLLABLE JJYX;Lo;0;L;;;;;N;;;;;
+A42F;YI SYLLABLE JJY;Lo;0;L;;;;;N;;;;;
+A430;YI SYLLABLE JJYP;Lo;0;L;;;;;N;;;;;
+A431;YI SYLLABLE NJIT;Lo;0;L;;;;;N;;;;;
+A432;YI SYLLABLE NJIX;Lo;0;L;;;;;N;;;;;
+A433;YI SYLLABLE NJI;Lo;0;L;;;;;N;;;;;
+A434;YI SYLLABLE NJIP;Lo;0;L;;;;;N;;;;;
+A435;YI SYLLABLE NJIET;Lo;0;L;;;;;N;;;;;
+A436;YI SYLLABLE NJIEX;Lo;0;L;;;;;N;;;;;
+A437;YI SYLLABLE NJIE;Lo;0;L;;;;;N;;;;;
+A438;YI SYLLABLE NJIEP;Lo;0;L;;;;;N;;;;;
+A439;YI SYLLABLE NJUOX;Lo;0;L;;;;;N;;;;;
+A43A;YI SYLLABLE NJUO;Lo;0;L;;;;;N;;;;;
+A43B;YI SYLLABLE NJOT;Lo;0;L;;;;;N;;;;;
+A43C;YI SYLLABLE NJOX;Lo;0;L;;;;;N;;;;;
+A43D;YI SYLLABLE NJO;Lo;0;L;;;;;N;;;;;
+A43E;YI SYLLABLE NJOP;Lo;0;L;;;;;N;;;;;
+A43F;YI SYLLABLE NJUX;Lo;0;L;;;;;N;;;;;
+A440;YI SYLLABLE NJU;Lo;0;L;;;;;N;;;;;
+A441;YI SYLLABLE NJUP;Lo;0;L;;;;;N;;;;;
+A442;YI SYLLABLE NJURX;Lo;0;L;;;;;N;;;;;
+A443;YI SYLLABLE NJUR;Lo;0;L;;;;;N;;;;;
+A444;YI SYLLABLE NJYT;Lo;0;L;;;;;N;;;;;
+A445;YI SYLLABLE NJYX;Lo;0;L;;;;;N;;;;;
+A446;YI SYLLABLE NJY;Lo;0;L;;;;;N;;;;;
+A447;YI SYLLABLE NJYP;Lo;0;L;;;;;N;;;;;
+A448;YI SYLLABLE NJYRX;Lo;0;L;;;;;N;;;;;
+A449;YI SYLLABLE NJYR;Lo;0;L;;;;;N;;;;;
+A44A;YI SYLLABLE NYIT;Lo;0;L;;;;;N;;;;;
+A44B;YI SYLLABLE NYIX;Lo;0;L;;;;;N;;;;;
+A44C;YI SYLLABLE NYI;Lo;0;L;;;;;N;;;;;
+A44D;YI SYLLABLE NYIP;Lo;0;L;;;;;N;;;;;
+A44E;YI SYLLABLE NYIET;Lo;0;L;;;;;N;;;;;
+A44F;YI SYLLABLE NYIEX;Lo;0;L;;;;;N;;;;;
+A450;YI SYLLABLE NYIE;Lo;0;L;;;;;N;;;;;
+A451;YI SYLLABLE NYIEP;Lo;0;L;;;;;N;;;;;
+A452;YI SYLLABLE NYUOX;Lo;0;L;;;;;N;;;;;
+A453;YI SYLLABLE NYUO;Lo;0;L;;;;;N;;;;;
+A454;YI SYLLABLE NYUOP;Lo;0;L;;;;;N;;;;;
+A455;YI SYLLABLE NYOT;Lo;0;L;;;;;N;;;;;
+A456;YI SYLLABLE NYOX;Lo;0;L;;;;;N;;;;;
+A457;YI SYLLABLE NYO;Lo;0;L;;;;;N;;;;;
+A458;YI SYLLABLE NYOP;Lo;0;L;;;;;N;;;;;
+A459;YI SYLLABLE NYUT;Lo;0;L;;;;;N;;;;;
+A45A;YI SYLLABLE NYUX;Lo;0;L;;;;;N;;;;;
+A45B;YI SYLLABLE NYU;Lo;0;L;;;;;N;;;;;
+A45C;YI SYLLABLE NYUP;Lo;0;L;;;;;N;;;;;
+A45D;YI SYLLABLE XIT;Lo;0;L;;;;;N;;;;;
+A45E;YI SYLLABLE XIX;Lo;0;L;;;;;N;;;;;
+A45F;YI SYLLABLE XI;Lo;0;L;;;;;N;;;;;
+A460;YI SYLLABLE XIP;Lo;0;L;;;;;N;;;;;
+A461;YI SYLLABLE XIET;Lo;0;L;;;;;N;;;;;
+A462;YI SYLLABLE XIEX;Lo;0;L;;;;;N;;;;;
+A463;YI SYLLABLE XIE;Lo;0;L;;;;;N;;;;;
+A464;YI SYLLABLE XIEP;Lo;0;L;;;;;N;;;;;
+A465;YI SYLLABLE XUOX;Lo;0;L;;;;;N;;;;;
+A466;YI SYLLABLE XUO;Lo;0;L;;;;;N;;;;;
+A467;YI SYLLABLE XOT;Lo;0;L;;;;;N;;;;;
+A468;YI SYLLABLE XOX;Lo;0;L;;;;;N;;;;;
+A469;YI SYLLABLE XO;Lo;0;L;;;;;N;;;;;
+A46A;YI SYLLABLE XOP;Lo;0;L;;;;;N;;;;;
+A46B;YI SYLLABLE XYT;Lo;0;L;;;;;N;;;;;
+A46C;YI SYLLABLE XYX;Lo;0;L;;;;;N;;;;;
+A46D;YI SYLLABLE XY;Lo;0;L;;;;;N;;;;;
+A46E;YI SYLLABLE XYP;Lo;0;L;;;;;N;;;;;
+A46F;YI SYLLABLE XYRX;Lo;0;L;;;;;N;;;;;
+A470;YI SYLLABLE XYR;Lo;0;L;;;;;N;;;;;
+A471;YI SYLLABLE YIT;Lo;0;L;;;;;N;;;;;
+A472;YI SYLLABLE YIX;Lo;0;L;;;;;N;;;;;
+A473;YI SYLLABLE YI;Lo;0;L;;;;;N;;;;;
+A474;YI SYLLABLE YIP;Lo;0;L;;;;;N;;;;;
+A475;YI SYLLABLE YIET;Lo;0;L;;;;;N;;;;;
+A476;YI SYLLABLE YIEX;Lo;0;L;;;;;N;;;;;
+A477;YI SYLLABLE YIE;Lo;0;L;;;;;N;;;;;
+A478;YI SYLLABLE YIEP;Lo;0;L;;;;;N;;;;;
+A479;YI SYLLABLE YUOT;Lo;0;L;;;;;N;;;;;
+A47A;YI SYLLABLE YUOX;Lo;0;L;;;;;N;;;;;
+A47B;YI SYLLABLE YUO;Lo;0;L;;;;;N;;;;;
+A47C;YI SYLLABLE YUOP;Lo;0;L;;;;;N;;;;;
+A47D;YI SYLLABLE YOT;Lo;0;L;;;;;N;;;;;
+A47E;YI SYLLABLE YOX;Lo;0;L;;;;;N;;;;;
+A47F;YI SYLLABLE YO;Lo;0;L;;;;;N;;;;;
+A480;YI SYLLABLE YOP;Lo;0;L;;;;;N;;;;;
+A481;YI SYLLABLE YUT;Lo;0;L;;;;;N;;;;;
+A482;YI SYLLABLE YUX;Lo;0;L;;;;;N;;;;;
+A483;YI SYLLABLE YU;Lo;0;L;;;;;N;;;;;
+A484;YI SYLLABLE YUP;Lo;0;L;;;;;N;;;;;
+A485;YI SYLLABLE YURX;Lo;0;L;;;;;N;;;;;
+A486;YI SYLLABLE YUR;Lo;0;L;;;;;N;;;;;
+A487;YI SYLLABLE YYT;Lo;0;L;;;;;N;;;;;
+A488;YI SYLLABLE YYX;Lo;0;L;;;;;N;;;;;
+A489;YI SYLLABLE YY;Lo;0;L;;;;;N;;;;;
+A48A;YI SYLLABLE YYP;Lo;0;L;;;;;N;;;;;
+A48B;YI SYLLABLE YYRX;Lo;0;L;;;;;N;;;;;
+A48C;YI SYLLABLE YYR;Lo;0;L;;;;;N;;;;;
+A490;YI RADICAL QOT;So;0;ON;;;;;N;;;;;
+A491;YI RADICAL LI;So;0;ON;;;;;N;;;;;
+A492;YI RADICAL KIT;So;0;ON;;;;;N;;;;;
+A493;YI RADICAL NYIP;So;0;ON;;;;;N;;;;;
+A494;YI RADICAL CYP;So;0;ON;;;;;N;;;;;
+A495;YI RADICAL SSI;So;0;ON;;;;;N;;;;;
+A496;YI RADICAL GGOP;So;0;ON;;;;;N;;;;;
+A497;YI RADICAL GEP;So;0;ON;;;;;N;;;;;
+A498;YI RADICAL MI;So;0;ON;;;;;N;;;;;
+A499;YI RADICAL HXIT;So;0;ON;;;;;N;;;;;
+A49A;YI RADICAL LYR;So;0;ON;;;;;N;;;;;
+A49B;YI RADICAL BBUT;So;0;ON;;;;;N;;;;;
+A49C;YI RADICAL MOP;So;0;ON;;;;;N;;;;;
+A49D;YI RADICAL YO;So;0;ON;;;;;N;;;;;
+A49E;YI RADICAL PUT;So;0;ON;;;;;N;;;;;
+A49F;YI RADICAL HXUO;So;0;ON;;;;;N;;;;;
+A4A0;YI RADICAL TAT;So;0;ON;;;;;N;;;;;
+A4A1;YI RADICAL GA;So;0;ON;;;;;N;;;;;
+A4A2;YI RADICAL ZUP;So;0;ON;;;;;N;;;;;
+A4A3;YI RADICAL CYT;So;0;ON;;;;;N;;;;;
+A4A4;YI RADICAL DDUR;So;0;ON;;;;;N;;;;;
+A4A5;YI RADICAL BUR;So;0;ON;;;;;N;;;;;
+A4A6;YI RADICAL GGUO;So;0;ON;;;;;N;;;;;
+A4A7;YI RADICAL NYOP;So;0;ON;;;;;N;;;;;
+A4A8;YI RADICAL TU;So;0;ON;;;;;N;;;;;
+A4A9;YI RADICAL OP;So;0;ON;;;;;N;;;;;
+A4AA;YI RADICAL JJUT;So;0;ON;;;;;N;;;;;
+A4AB;YI RADICAL ZOT;So;0;ON;;;;;N;;;;;
+A4AC;YI RADICAL PYT;So;0;ON;;;;;N;;;;;
+A4AD;YI RADICAL HMO;So;0;ON;;;;;N;;;;;
+A4AE;YI RADICAL YIT;So;0;ON;;;;;N;;;;;
+A4AF;YI RADICAL VUR;So;0;ON;;;;;N;;;;;
+A4B0;YI RADICAL SHY;So;0;ON;;;;;N;;;;;
+A4B1;YI RADICAL VEP;So;0;ON;;;;;N;;;;;
+A4B2;YI RADICAL ZA;So;0;ON;;;;;N;;;;;
+A4B3;YI RADICAL JO;So;0;ON;;;;;N;;;;;
+A4B4;YI RADICAL NZUP;So;0;ON;;;;;N;;;;;
+A4B5;YI RADICAL JJY;So;0;ON;;;;;N;;;;;
+A4B6;YI RADICAL GOT;So;0;ON;;;;;N;;;;;
+A4B7;YI RADICAL JJIE;So;0;ON;;;;;N;;;;;
+A4B8;YI RADICAL WO;So;0;ON;;;;;N;;;;;
+A4B9;YI RADICAL DU;So;0;ON;;;;;N;;;;;
+A4BA;YI RADICAL SHUR;So;0;ON;;;;;N;;;;;
+A4BB;YI RADICAL LIE;So;0;ON;;;;;N;;;;;
+A4BC;YI RADICAL CY;So;0;ON;;;;;N;;;;;
+A4BD;YI RADICAL CUOP;So;0;ON;;;;;N;;;;;
+A4BE;YI RADICAL CIP;So;0;ON;;;;;N;;;;;
+A4BF;YI RADICAL HXOP;So;0;ON;;;;;N;;;;;
+A4C0;YI RADICAL SHAT;So;0;ON;;;;;N;;;;;
+A4C1;YI RADICAL ZUR;So;0;ON;;;;;N;;;;;
+A4C2;YI RADICAL SHOP;So;0;ON;;;;;N;;;;;
+A4C3;YI RADICAL CHE;So;0;ON;;;;;N;;;;;
+A4C4;YI RADICAL ZZIET;So;0;ON;;;;;N;;;;;
+A4C5;YI RADICAL NBIE;So;0;ON;;;;;N;;;;;
+A4C6;YI RADICAL KE;So;0;ON;;;;;N;;;;;
+A4D0;LISU LETTER BA;Lo;0;L;;;;;N;;;;;
+A4D1;LISU LETTER PA;Lo;0;L;;;;;N;;;;;
+A4D2;LISU LETTER PHA;Lo;0;L;;;;;N;;;;;
+A4D3;LISU LETTER DA;Lo;0;L;;;;;N;;;;;
+A4D4;LISU LETTER TA;Lo;0;L;;;;;N;;;;;
+A4D5;LISU LETTER THA;Lo;0;L;;;;;N;;;;;
+A4D6;LISU LETTER GA;Lo;0;L;;;;;N;;;;;
+A4D7;LISU LETTER KA;Lo;0;L;;;;;N;;;;;
+A4D8;LISU LETTER KHA;Lo;0;L;;;;;N;;;;;
+A4D9;LISU LETTER JA;Lo;0;L;;;;;N;;;;;
+A4DA;LISU LETTER CA;Lo;0;L;;;;;N;;;;;
+A4DB;LISU LETTER CHA;Lo;0;L;;;;;N;;;;;
+A4DC;LISU LETTER DZA;Lo;0;L;;;;;N;;;;;
+A4DD;LISU LETTER TSA;Lo;0;L;;;;;N;;;;;
+A4DE;LISU LETTER TSHA;Lo;0;L;;;;;N;;;;;
+A4DF;LISU LETTER MA;Lo;0;L;;;;;N;;;;;
+A4E0;LISU LETTER NA;Lo;0;L;;;;;N;;;;;
+A4E1;LISU LETTER LA;Lo;0;L;;;;;N;;;;;
+A4E2;LISU LETTER SA;Lo;0;L;;;;;N;;;;;
+A4E3;LISU LETTER ZHA;Lo;0;L;;;;;N;;;;;
+A4E4;LISU LETTER ZA;Lo;0;L;;;;;N;;;;;
+A4E5;LISU LETTER NGA;Lo;0;L;;;;;N;;;;;
+A4E6;LISU LETTER HA;Lo;0;L;;;;;N;;;;;
+A4E7;LISU LETTER XA;Lo;0;L;;;;;N;;;;;
+A4E8;LISU LETTER HHA;Lo;0;L;;;;;N;;;;;
+A4E9;LISU LETTER FA;Lo;0;L;;;;;N;;;;;
+A4EA;LISU LETTER WA;Lo;0;L;;;;;N;;;;;
+A4EB;LISU LETTER SHA;Lo;0;L;;;;;N;;;;;
+A4EC;LISU LETTER YA;Lo;0;L;;;;;N;;;;;
+A4ED;LISU LETTER GHA;Lo;0;L;;;;;N;;;;;
+A4EE;LISU LETTER A;Lo;0;L;;;;;N;;;;;
+A4EF;LISU LETTER AE;Lo;0;L;;;;;N;;;;;
+A4F0;LISU LETTER E;Lo;0;L;;;;;N;;;;;
+A4F1;LISU LETTER EU;Lo;0;L;;;;;N;;;;;
+A4F2;LISU LETTER I;Lo;0;L;;;;;N;;;;;
+A4F3;LISU LETTER O;Lo;0;L;;;;;N;;;;;
+A4F4;LISU LETTER U;Lo;0;L;;;;;N;;;;;
+A4F5;LISU LETTER UE;Lo;0;L;;;;;N;;;;;
+A4F6;LISU LETTER UH;Lo;0;L;;;;;N;;;;;
+A4F7;LISU LETTER OE;Lo;0;L;;;;;N;;;;;
+A4F8;LISU LETTER TONE MYA TI;Lm;0;L;;;;;N;;;;;
+A4F9;LISU LETTER TONE NA PO;Lm;0;L;;;;;N;;;;;
+A4FA;LISU LETTER TONE MYA CYA;Lm;0;L;;;;;N;;;;;
+A4FB;LISU LETTER TONE MYA BO;Lm;0;L;;;;;N;;;;;
+A4FC;LISU LETTER TONE MYA NA;Lm;0;L;;;;;N;;;;;
+A4FD;LISU LETTER TONE MYA JEU;Lm;0;L;;;;;N;;;;;
+A4FE;LISU PUNCTUATION COMMA;Po;0;L;;;;;N;;;;;
+A4FF;LISU PUNCTUATION FULL STOP;Po;0;L;;;;;N;;;;;
+A500;VAI SYLLABLE EE;Lo;0;L;;;;;N;;;;;
+A501;VAI SYLLABLE EEN;Lo;0;L;;;;;N;;;;;
+A502;VAI SYLLABLE HEE;Lo;0;L;;;;;N;;;;;
+A503;VAI SYLLABLE WEE;Lo;0;L;;;;;N;;;;;
+A504;VAI SYLLABLE WEEN;Lo;0;L;;;;;N;;;;;
+A505;VAI SYLLABLE PEE;Lo;0;L;;;;;N;;;;;
+A506;VAI SYLLABLE BHEE;Lo;0;L;;;;;N;;;;;
+A507;VAI SYLLABLE BEE;Lo;0;L;;;;;N;;;;;
+A508;VAI SYLLABLE MBEE;Lo;0;L;;;;;N;;;;;
+A509;VAI SYLLABLE KPEE;Lo;0;L;;;;;N;;;;;
+A50A;VAI SYLLABLE MGBEE;Lo;0;L;;;;;N;;;;;
+A50B;VAI SYLLABLE GBEE;Lo;0;L;;;;;N;;;;;
+A50C;VAI SYLLABLE FEE;Lo;0;L;;;;;N;;;;;
+A50D;VAI SYLLABLE VEE;Lo;0;L;;;;;N;;;;;
+A50E;VAI SYLLABLE TEE;Lo;0;L;;;;;N;;;;;
+A50F;VAI SYLLABLE THEE;Lo;0;L;;;;;N;;;;;
+A510;VAI SYLLABLE DHEE;Lo;0;L;;;;;N;;;;;
+A511;VAI SYLLABLE DHHEE;Lo;0;L;;;;;N;;;;;
+A512;VAI SYLLABLE LEE;Lo;0;L;;;;;N;;;;;
+A513;VAI SYLLABLE REE;Lo;0;L;;;;;N;;;;;
+A514;VAI SYLLABLE DEE;Lo;0;L;;;;;N;;;;;
+A515;VAI SYLLABLE NDEE;Lo;0;L;;;;;N;;;;;
+A516;VAI SYLLABLE SEE;Lo;0;L;;;;;N;;;;;
+A517;VAI SYLLABLE SHEE;Lo;0;L;;;;;N;;;;;
+A518;VAI SYLLABLE ZEE;Lo;0;L;;;;;N;;;;;
+A519;VAI SYLLABLE ZHEE;Lo;0;L;;;;;N;;;;;
+A51A;VAI SYLLABLE CEE;Lo;0;L;;;;;N;;;;;
+A51B;VAI SYLLABLE JEE;Lo;0;L;;;;;N;;;;;
+A51C;VAI SYLLABLE NJEE;Lo;0;L;;;;;N;;;;;
+A51D;VAI SYLLABLE YEE;Lo;0;L;;;;;N;;;;;
+A51E;VAI SYLLABLE KEE;Lo;0;L;;;;;N;;;;;
+A51F;VAI SYLLABLE NGGEE;Lo;0;L;;;;;N;;;;;
+A520;VAI SYLLABLE GEE;Lo;0;L;;;;;N;;;;;
+A521;VAI SYLLABLE MEE;Lo;0;L;;;;;N;;;;;
+A522;VAI SYLLABLE NEE;Lo;0;L;;;;;N;;;;;
+A523;VAI SYLLABLE NYEE;Lo;0;L;;;;;N;;;;;
+A524;VAI SYLLABLE I;Lo;0;L;;;;;N;;;;;
+A525;VAI SYLLABLE IN;Lo;0;L;;;;;N;;;;;
+A526;VAI SYLLABLE HI;Lo;0;L;;;;;N;;;;;
+A527;VAI SYLLABLE HIN;Lo;0;L;;;;;N;;;;;
+A528;VAI SYLLABLE WI;Lo;0;L;;;;;N;;;;;
+A529;VAI SYLLABLE WIN;Lo;0;L;;;;;N;;;;;
+A52A;VAI SYLLABLE PI;Lo;0;L;;;;;N;;;;;
+A52B;VAI SYLLABLE BHI;Lo;0;L;;;;;N;;;;;
+A52C;VAI SYLLABLE BI;Lo;0;L;;;;;N;;;;;
+A52D;VAI SYLLABLE MBI;Lo;0;L;;;;;N;;;;;
+A52E;VAI SYLLABLE KPI;Lo;0;L;;;;;N;;;;;
+A52F;VAI SYLLABLE MGBI;Lo;0;L;;;;;N;;;;;
+A530;VAI SYLLABLE GBI;Lo;0;L;;;;;N;;;;;
+A531;VAI SYLLABLE FI;Lo;0;L;;;;;N;;;;;
+A532;VAI SYLLABLE VI;Lo;0;L;;;;;N;;;;;
+A533;VAI SYLLABLE TI;Lo;0;L;;;;;N;;;;;
+A534;VAI SYLLABLE THI;Lo;0;L;;;;;N;;;;;
+A535;VAI SYLLABLE DHI;Lo;0;L;;;;;N;;;;;
+A536;VAI SYLLABLE DHHI;Lo;0;L;;;;;N;;;;;
+A537;VAI SYLLABLE LI;Lo;0;L;;;;;N;;;;;
+A538;VAI SYLLABLE RI;Lo;0;L;;;;;N;;;;;
+A539;VAI SYLLABLE DI;Lo;0;L;;;;;N;;;;;
+A53A;VAI SYLLABLE NDI;Lo;0;L;;;;;N;;;;;
+A53B;VAI SYLLABLE SI;Lo;0;L;;;;;N;;;;;
+A53C;VAI SYLLABLE SHI;Lo;0;L;;;;;N;;;;;
+A53D;VAI SYLLABLE ZI;Lo;0;L;;;;;N;;;;;
+A53E;VAI SYLLABLE ZHI;Lo;0;L;;;;;N;;;;;
+A53F;VAI SYLLABLE CI;Lo;0;L;;;;;N;;;;;
+A540;VAI SYLLABLE JI;Lo;0;L;;;;;N;;;;;
+A541;VAI SYLLABLE NJI;Lo;0;L;;;;;N;;;;;
+A542;VAI SYLLABLE YI;Lo;0;L;;;;;N;;;;;
+A543;VAI SYLLABLE KI;Lo;0;L;;;;;N;;;;;
+A544;VAI SYLLABLE NGGI;Lo;0;L;;;;;N;;;;;
+A545;VAI SYLLABLE GI;Lo;0;L;;;;;N;;;;;
+A546;VAI SYLLABLE MI;Lo;0;L;;;;;N;;;;;
+A547;VAI SYLLABLE NI;Lo;0;L;;;;;N;;;;;
+A548;VAI SYLLABLE NYI;Lo;0;L;;;;;N;;;;;
+A549;VAI SYLLABLE A;Lo;0;L;;;;;N;;;;;
+A54A;VAI SYLLABLE AN;Lo;0;L;;;;;N;;;;;
+A54B;VAI SYLLABLE NGAN;Lo;0;L;;;;;N;;;;;
+A54C;VAI SYLLABLE HA;Lo;0;L;;;;;N;;;;;
+A54D;VAI SYLLABLE HAN;Lo;0;L;;;;;N;;;;;
+A54E;VAI SYLLABLE WA;Lo;0;L;;;;;N;;;;;
+A54F;VAI SYLLABLE WAN;Lo;0;L;;;;;N;;;;;
+A550;VAI SYLLABLE PA;Lo;0;L;;;;;N;;;;;
+A551;VAI SYLLABLE BHA;Lo;0;L;;;;;N;;;;;
+A552;VAI SYLLABLE BA;Lo;0;L;;;;;N;;;;;
+A553;VAI SYLLABLE MBA;Lo;0;L;;;;;N;;;;;
+A554;VAI SYLLABLE KPA;Lo;0;L;;;;;N;;;;;
+A555;VAI SYLLABLE KPAN;Lo;0;L;;;;;N;;;;;
+A556;VAI SYLLABLE MGBA;Lo;0;L;;;;;N;;;;;
+A557;VAI SYLLABLE GBA;Lo;0;L;;;;;N;;;;;
+A558;VAI SYLLABLE FA;Lo;0;L;;;;;N;;;;;
+A559;VAI SYLLABLE VA;Lo;0;L;;;;;N;;;;;
+A55A;VAI SYLLABLE TA;Lo;0;L;;;;;N;;;;;
+A55B;VAI SYLLABLE THA;Lo;0;L;;;;;N;;;;;
+A55C;VAI SYLLABLE DHA;Lo;0;L;;;;;N;;;;;
+A55D;VAI SYLLABLE DHHA;Lo;0;L;;;;;N;;;;;
+A55E;VAI SYLLABLE LA;Lo;0;L;;;;;N;;;;;
+A55F;VAI SYLLABLE RA;Lo;0;L;;;;;N;;;;;
+A560;VAI SYLLABLE DA;Lo;0;L;;;;;N;;;;;
+A561;VAI SYLLABLE NDA;Lo;0;L;;;;;N;;;;;
+A562;VAI SYLLABLE SA;Lo;0;L;;;;;N;;;;;
+A563;VAI SYLLABLE SHA;Lo;0;L;;;;;N;;;;;
+A564;VAI SYLLABLE ZA;Lo;0;L;;;;;N;;;;;
+A565;VAI SYLLABLE ZHA;Lo;0;L;;;;;N;;;;;
+A566;VAI SYLLABLE CA;Lo;0;L;;;;;N;;;;;
+A567;VAI SYLLABLE JA;Lo;0;L;;;;;N;;;;;
+A568;VAI SYLLABLE NJA;Lo;0;L;;;;;N;;;;;
+A569;VAI SYLLABLE YA;Lo;0;L;;;;;N;;;;;
+A56A;VAI SYLLABLE KA;Lo;0;L;;;;;N;;;;;
+A56B;VAI SYLLABLE KAN;Lo;0;L;;;;;N;;;;;
+A56C;VAI SYLLABLE NGGA;Lo;0;L;;;;;N;;;;;
+A56D;VAI SYLLABLE GA;Lo;0;L;;;;;N;;;;;
+A56E;VAI SYLLABLE MA;Lo;0;L;;;;;N;;;;;
+A56F;VAI SYLLABLE NA;Lo;0;L;;;;;N;;;;;
+A570;VAI SYLLABLE NYA;Lo;0;L;;;;;N;;;;;
+A571;VAI SYLLABLE OO;Lo;0;L;;;;;N;;;;;
+A572;VAI SYLLABLE OON;Lo;0;L;;;;;N;;;;;
+A573;VAI SYLLABLE HOO;Lo;0;L;;;;;N;;;;;
+A574;VAI SYLLABLE WOO;Lo;0;L;;;;;N;;;;;
+A575;VAI SYLLABLE WOON;Lo;0;L;;;;;N;;;;;
+A576;VAI SYLLABLE POO;Lo;0;L;;;;;N;;;;;
+A577;VAI SYLLABLE BHOO;Lo;0;L;;;;;N;;;;;
+A578;VAI SYLLABLE BOO;Lo;0;L;;;;;N;;;;;
+A579;VAI SYLLABLE MBOO;Lo;0;L;;;;;N;;;;;
+A57A;VAI SYLLABLE KPOO;Lo;0;L;;;;;N;;;;;
+A57B;VAI SYLLABLE MGBOO;Lo;0;L;;;;;N;;;;;
+A57C;VAI SYLLABLE GBOO;Lo;0;L;;;;;N;;;;;
+A57D;VAI SYLLABLE FOO;Lo;0;L;;;;;N;;;;;
+A57E;VAI SYLLABLE VOO;Lo;0;L;;;;;N;;;;;
+A57F;VAI SYLLABLE TOO;Lo;0;L;;;;;N;;;;;
+A580;VAI SYLLABLE THOO;Lo;0;L;;;;;N;;;;;
+A581;VAI SYLLABLE DHOO;Lo;0;L;;;;;N;;;;;
+A582;VAI SYLLABLE DHHOO;Lo;0;L;;;;;N;;;;;
+A583;VAI SYLLABLE LOO;Lo;0;L;;;;;N;;;;;
+A584;VAI SYLLABLE ROO;Lo;0;L;;;;;N;;;;;
+A585;VAI SYLLABLE DOO;Lo;0;L;;;;;N;;;;;
+A586;VAI SYLLABLE NDOO;Lo;0;L;;;;;N;;;;;
+A587;VAI SYLLABLE SOO;Lo;0;L;;;;;N;;;;;
+A588;VAI SYLLABLE SHOO;Lo;0;L;;;;;N;;;;;
+A589;VAI SYLLABLE ZOO;Lo;0;L;;;;;N;;;;;
+A58A;VAI SYLLABLE ZHOO;Lo;0;L;;;;;N;;;;;
+A58B;VAI SYLLABLE COO;Lo;0;L;;;;;N;;;;;
+A58C;VAI SYLLABLE JOO;Lo;0;L;;;;;N;;;;;
+A58D;VAI SYLLABLE NJOO;Lo;0;L;;;;;N;;;;;
+A58E;VAI SYLLABLE YOO;Lo;0;L;;;;;N;;;;;
+A58F;VAI SYLLABLE KOO;Lo;0;L;;;;;N;;;;;
+A590;VAI SYLLABLE NGGOO;Lo;0;L;;;;;N;;;;;
+A591;VAI SYLLABLE GOO;Lo;0;L;;;;;N;;;;;
+A592;VAI SYLLABLE MOO;Lo;0;L;;;;;N;;;;;
+A593;VAI SYLLABLE NOO;Lo;0;L;;;;;N;;;;;
+A594;VAI SYLLABLE NYOO;Lo;0;L;;;;;N;;;;;
+A595;VAI SYLLABLE U;Lo;0;L;;;;;N;;;;;
+A596;VAI SYLLABLE UN;Lo;0;L;;;;;N;;;;;
+A597;VAI SYLLABLE HU;Lo;0;L;;;;;N;;;;;
+A598;VAI SYLLABLE HUN;Lo;0;L;;;;;N;;;;;
+A599;VAI SYLLABLE WU;Lo;0;L;;;;;N;;;;;
+A59A;VAI SYLLABLE WUN;Lo;0;L;;;;;N;;;;;
+A59B;VAI SYLLABLE PU;Lo;0;L;;;;;N;;;;;
+A59C;VAI SYLLABLE BHU;Lo;0;L;;;;;N;;;;;
+A59D;VAI SYLLABLE BU;Lo;0;L;;;;;N;;;;;
+A59E;VAI SYLLABLE MBU;Lo;0;L;;;;;N;;;;;
+A59F;VAI SYLLABLE KPU;Lo;0;L;;;;;N;;;;;
+A5A0;VAI SYLLABLE MGBU;Lo;0;L;;;;;N;;;;;
+A5A1;VAI SYLLABLE GBU;Lo;0;L;;;;;N;;;;;
+A5A2;VAI SYLLABLE FU;Lo;0;L;;;;;N;;;;;
+A5A3;VAI SYLLABLE VU;Lo;0;L;;;;;N;;;;;
+A5A4;VAI SYLLABLE TU;Lo;0;L;;;;;N;;;;;
+A5A5;VAI SYLLABLE THU;Lo;0;L;;;;;N;;;;;
+A5A6;VAI SYLLABLE DHU;Lo;0;L;;;;;N;;;;;
+A5A7;VAI SYLLABLE DHHU;Lo;0;L;;;;;N;;;;;
+A5A8;VAI SYLLABLE LU;Lo;0;L;;;;;N;;;;;
+A5A9;VAI SYLLABLE RU;Lo;0;L;;;;;N;;;;;
+A5AA;VAI SYLLABLE DU;Lo;0;L;;;;;N;;;;;
+A5AB;VAI SYLLABLE NDU;Lo;0;L;;;;;N;;;;;
+A5AC;VAI SYLLABLE SU;Lo;0;L;;;;;N;;;;;
+A5AD;VAI SYLLABLE SHU;Lo;0;L;;;;;N;;;;;
+A5AE;VAI SYLLABLE ZU;Lo;0;L;;;;;N;;;;;
+A5AF;VAI SYLLABLE ZHU;Lo;0;L;;;;;N;;;;;
+A5B0;VAI SYLLABLE CU;Lo;0;L;;;;;N;;;;;
+A5B1;VAI SYLLABLE JU;Lo;0;L;;;;;N;;;;;
+A5B2;VAI SYLLABLE NJU;Lo;0;L;;;;;N;;;;;
+A5B3;VAI SYLLABLE YU;Lo;0;L;;;;;N;;;;;
+A5B4;VAI SYLLABLE KU;Lo;0;L;;;;;N;;;;;
+A5B5;VAI SYLLABLE NGGU;Lo;0;L;;;;;N;;;;;
+A5B6;VAI SYLLABLE GU;Lo;0;L;;;;;N;;;;;
+A5B7;VAI SYLLABLE MU;Lo;0;L;;;;;N;;;;;
+A5B8;VAI SYLLABLE NU;Lo;0;L;;;;;N;;;;;
+A5B9;VAI SYLLABLE NYU;Lo;0;L;;;;;N;;;;;
+A5BA;VAI SYLLABLE O;Lo;0;L;;;;;N;;;;;
+A5BB;VAI SYLLABLE ON;Lo;0;L;;;;;N;;;;;
+A5BC;VAI SYLLABLE NGON;Lo;0;L;;;;;N;;;;;
+A5BD;VAI SYLLABLE HO;Lo;0;L;;;;;N;;;;;
+A5BE;VAI SYLLABLE HON;Lo;0;L;;;;;N;;;;;
+A5BF;VAI SYLLABLE WO;Lo;0;L;;;;;N;;;;;
+A5C0;VAI SYLLABLE WON;Lo;0;L;;;;;N;;;;;
+A5C1;VAI SYLLABLE PO;Lo;0;L;;;;;N;;;;;
+A5C2;VAI SYLLABLE BHO;Lo;0;L;;;;;N;;;;;
+A5C3;VAI SYLLABLE BO;Lo;0;L;;;;;N;;;;;
+A5C4;VAI SYLLABLE MBO;Lo;0;L;;;;;N;;;;;
+A5C5;VAI SYLLABLE KPO;Lo;0;L;;;;;N;;;;;
+A5C6;VAI SYLLABLE MGBO;Lo;0;L;;;;;N;;;;;
+A5C7;VAI SYLLABLE GBO;Lo;0;L;;;;;N;;;;;
+A5C8;VAI SYLLABLE GBON;Lo;0;L;;;;;N;;;;;
+A5C9;VAI SYLLABLE FO;Lo;0;L;;;;;N;;;;;
+A5CA;VAI SYLLABLE VO;Lo;0;L;;;;;N;;;;;
+A5CB;VAI SYLLABLE TO;Lo;0;L;;;;;N;;;;;
+A5CC;VAI SYLLABLE THO;Lo;0;L;;;;;N;;;;;
+A5CD;VAI SYLLABLE DHO;Lo;0;L;;;;;N;;;;;
+A5CE;VAI SYLLABLE DHHO;Lo;0;L;;;;;N;;;;;
+A5CF;VAI SYLLABLE LO;Lo;0;L;;;;;N;;;;;
+A5D0;VAI SYLLABLE RO;Lo;0;L;;;;;N;;;;;
+A5D1;VAI SYLLABLE DO;Lo;0;L;;;;;N;;;;;
+A5D2;VAI SYLLABLE NDO;Lo;0;L;;;;;N;;;;;
+A5D3;VAI SYLLABLE SO;Lo;0;L;;;;;N;;;;;
+A5D4;VAI SYLLABLE SHO;Lo;0;L;;;;;N;;;;;
+A5D5;VAI SYLLABLE ZO;Lo;0;L;;;;;N;;;;;
+A5D6;VAI SYLLABLE ZHO;Lo;0;L;;;;;N;;;;;
+A5D7;VAI SYLLABLE CO;Lo;0;L;;;;;N;;;;;
+A5D8;VAI SYLLABLE JO;Lo;0;L;;;;;N;;;;;
+A5D9;VAI SYLLABLE NJO;Lo;0;L;;;;;N;;;;;
+A5DA;VAI SYLLABLE YO;Lo;0;L;;;;;N;;;;;
+A5DB;VAI SYLLABLE KO;Lo;0;L;;;;;N;;;;;
+A5DC;VAI SYLLABLE NGGO;Lo;0;L;;;;;N;;;;;
+A5DD;VAI SYLLABLE GO;Lo;0;L;;;;;N;;;;;
+A5DE;VAI SYLLABLE MO;Lo;0;L;;;;;N;;;;;
+A5DF;VAI SYLLABLE NO;Lo;0;L;;;;;N;;;;;
+A5E0;VAI SYLLABLE NYO;Lo;0;L;;;;;N;;;;;
+A5E1;VAI SYLLABLE E;Lo;0;L;;;;;N;;;;;
+A5E2;VAI SYLLABLE EN;Lo;0;L;;;;;N;;;;;
+A5E3;VAI SYLLABLE NGEN;Lo;0;L;;;;;N;;;;;
+A5E4;VAI SYLLABLE HE;Lo;0;L;;;;;N;;;;;
+A5E5;VAI SYLLABLE HEN;Lo;0;L;;;;;N;;;;;
+A5E6;VAI SYLLABLE WE;Lo;0;L;;;;;N;;;;;
+A5E7;VAI SYLLABLE WEN;Lo;0;L;;;;;N;;;;;
+A5E8;VAI SYLLABLE PE;Lo;0;L;;;;;N;;;;;
+A5E9;VAI SYLLABLE BHE;Lo;0;L;;;;;N;;;;;
+A5EA;VAI SYLLABLE BE;Lo;0;L;;;;;N;;;;;
+A5EB;VAI SYLLABLE MBE;Lo;0;L;;;;;N;;;;;
+A5EC;VAI SYLLABLE KPE;Lo;0;L;;;;;N;;;;;
+A5ED;VAI SYLLABLE KPEN;Lo;0;L;;;;;N;;;;;
+A5EE;VAI SYLLABLE MGBE;Lo;0;L;;;;;N;;;;;
+A5EF;VAI SYLLABLE GBE;Lo;0;L;;;;;N;;;;;
+A5F0;VAI SYLLABLE GBEN;Lo;0;L;;;;;N;;;;;
+A5F1;VAI SYLLABLE FE;Lo;0;L;;;;;N;;;;;
+A5F2;VAI SYLLABLE VE;Lo;0;L;;;;;N;;;;;
+A5F3;VAI SYLLABLE TE;Lo;0;L;;;;;N;;;;;
+A5F4;VAI SYLLABLE THE;Lo;0;L;;;;;N;;;;;
+A5F5;VAI SYLLABLE DHE;Lo;0;L;;;;;N;;;;;
+A5F6;VAI SYLLABLE DHHE;Lo;0;L;;;;;N;;;;;
+A5F7;VAI SYLLABLE LE;Lo;0;L;;;;;N;;;;;
+A5F8;VAI SYLLABLE RE;Lo;0;L;;;;;N;;;;;
+A5F9;VAI SYLLABLE DE;Lo;0;L;;;;;N;;;;;
+A5FA;VAI SYLLABLE NDE;Lo;0;L;;;;;N;;;;;
+A5FB;VAI SYLLABLE SE;Lo;0;L;;;;;N;;;;;
+A5FC;VAI SYLLABLE SHE;Lo;0;L;;;;;N;;;;;
+A5FD;VAI SYLLABLE ZE;Lo;0;L;;;;;N;;;;;
+A5FE;VAI SYLLABLE ZHE;Lo;0;L;;;;;N;;;;;
+A5FF;VAI SYLLABLE CE;Lo;0;L;;;;;N;;;;;
+A600;VAI SYLLABLE JE;Lo;0;L;;;;;N;;;;;
+A601;VAI SYLLABLE NJE;Lo;0;L;;;;;N;;;;;
+A602;VAI SYLLABLE YE;Lo;0;L;;;;;N;;;;;
+A603;VAI SYLLABLE KE;Lo;0;L;;;;;N;;;;;
+A604;VAI SYLLABLE NGGE;Lo;0;L;;;;;N;;;;;
+A605;VAI SYLLABLE NGGEN;Lo;0;L;;;;;N;;;;;
+A606;VAI SYLLABLE GE;Lo;0;L;;;;;N;;;;;
+A607;VAI SYLLABLE GEN;Lo;0;L;;;;;N;;;;;
+A608;VAI SYLLABLE ME;Lo;0;L;;;;;N;;;;;
+A609;VAI SYLLABLE NE;Lo;0;L;;;;;N;;;;;
+A60A;VAI SYLLABLE NYE;Lo;0;L;;;;;N;;;;;
+A60B;VAI SYLLABLE NG;Lo;0;L;;;;;N;;;;;
+A60C;VAI SYLLABLE LENGTHENER;Lm;0;L;;;;;N;;;;;
+A60D;VAI COMMA;Po;0;ON;;;;;N;;;;;
+A60E;VAI FULL STOP;Po;0;ON;;;;;N;;;;;
+A60F;VAI QUESTION MARK;Po;0;ON;;;;;N;;;;;
+A610;VAI SYLLABLE NDOLE FA;Lo;0;L;;;;;N;;;;;
+A611;VAI SYLLABLE NDOLE KA;Lo;0;L;;;;;N;;;;;
+A612;VAI SYLLABLE NDOLE SOO;Lo;0;L;;;;;N;;;;;
+A613;VAI SYMBOL FEENG;Lo;0;L;;;;;N;;;;;
+A614;VAI SYMBOL KEENG;Lo;0;L;;;;;N;;;;;
+A615;VAI SYMBOL TING;Lo;0;L;;;;;N;;;;;
+A616;VAI SYMBOL NII;Lo;0;L;;;;;N;;;;;
+A617;VAI SYMBOL BANG;Lo;0;L;;;;;N;;;;;
+A618;VAI SYMBOL FAA;Lo;0;L;;;;;N;;;;;
+A619;VAI SYMBOL TAA;Lo;0;L;;;;;N;;;;;
+A61A;VAI SYMBOL DANG;Lo;0;L;;;;;N;;;;;
+A61B;VAI SYMBOL DOONG;Lo;0;L;;;;;N;;;;;
+A61C;VAI SYMBOL KUNG;Lo;0;L;;;;;N;;;;;
+A61D;VAI SYMBOL TONG;Lo;0;L;;;;;N;;;;;
+A61E;VAI SYMBOL DO-O;Lo;0;L;;;;;N;;;;;
+A61F;VAI SYMBOL JONG;Lo;0;L;;;;;N;;;;;
+A620;VAI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+A621;VAI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+A622;VAI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+A623;VAI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+A624;VAI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+A625;VAI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+A626;VAI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+A627;VAI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+A628;VAI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+A629;VAI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+A62A;VAI SYLLABLE NDOLE MA;Lo;0;L;;;;;N;;;;;
+A62B;VAI SYLLABLE NDOLE DO;Lo;0;L;;;;;N;;;;;
+A640;CYRILLIC CAPITAL LETTER ZEMLYA;Lu;0;L;;;;;N;;;;A641;
+A641;CYRILLIC SMALL LETTER ZEMLYA;Ll;0;L;;;;;N;;;A640;;A640
+A642;CYRILLIC CAPITAL LETTER DZELO;Lu;0;L;;;;;N;;;;A643;
+A643;CYRILLIC SMALL LETTER DZELO;Ll;0;L;;;;;N;;;A642;;A642
+A644;CYRILLIC CAPITAL LETTER REVERSED DZE;Lu;0;L;;;;;N;;;;A645;
+A645;CYRILLIC SMALL LETTER REVERSED DZE;Ll;0;L;;;;;N;;;A644;;A644
+A646;CYRILLIC CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;A647;
+A647;CYRILLIC SMALL LETTER IOTA;Ll;0;L;;;;;N;;;A646;;A646
+A648;CYRILLIC CAPITAL LETTER DJERV;Lu;0;L;;;;;N;;;;A649;
+A649;CYRILLIC SMALL LETTER DJERV;Ll;0;L;;;;;N;;;A648;;A648
+A64A;CYRILLIC CAPITAL LETTER MONOGRAPH UK;Lu;0;L;;;;;N;;;;A64B;
+A64B;CYRILLIC SMALL LETTER MONOGRAPH UK;Ll;0;L;;;;;N;;;A64A;;A64A
+A64C;CYRILLIC CAPITAL LETTER BROAD OMEGA;Lu;0;L;;;;;N;;;;A64D;
+A64D;CYRILLIC SMALL LETTER BROAD OMEGA;Ll;0;L;;;;;N;;;A64C;;A64C
+A64E;CYRILLIC CAPITAL LETTER NEUTRAL YER;Lu;0;L;;;;;N;;;;A64F;
+A64F;CYRILLIC SMALL LETTER NEUTRAL YER;Ll;0;L;;;;;N;;;A64E;;A64E
+A650;CYRILLIC CAPITAL LETTER YERU WITH BACK YER;Lu;0;L;;;;;N;;;;A651;
+A651;CYRILLIC SMALL LETTER YERU WITH BACK YER;Ll;0;L;;;;;N;;;A650;;A650
+A652;CYRILLIC CAPITAL LETTER IOTIFIED YAT;Lu;0;L;;;;;N;;;;A653;
+A653;CYRILLIC SMALL LETTER IOTIFIED YAT;Ll;0;L;;;;;N;;;A652;;A652
+A654;CYRILLIC CAPITAL LETTER REVERSED YU;Lu;0;L;;;;;N;;;;A655;
+A655;CYRILLIC SMALL LETTER REVERSED YU;Ll;0;L;;;;;N;;;A654;;A654
+A656;CYRILLIC CAPITAL LETTER IOTIFIED A;Lu;0;L;;;;;N;;;;A657;
+A657;CYRILLIC SMALL LETTER IOTIFIED A;Ll;0;L;;;;;N;;;A656;;A656
+A658;CYRILLIC CAPITAL LETTER CLOSED LITTLE YUS;Lu;0;L;;;;;N;;;;A659;
+A659;CYRILLIC SMALL LETTER CLOSED LITTLE YUS;Ll;0;L;;;;;N;;;A658;;A658
+A65A;CYRILLIC CAPITAL LETTER BLENDED YUS;Lu;0;L;;;;;N;;;;A65B;
+A65B;CYRILLIC SMALL LETTER BLENDED YUS;Ll;0;L;;;;;N;;;A65A;;A65A
+A65C;CYRILLIC CAPITAL LETTER IOTIFIED CLOSED LITTLE YUS;Lu;0;L;;;;;N;;;;A65D;
+A65D;CYRILLIC SMALL LETTER IOTIFIED CLOSED LITTLE YUS;Ll;0;L;;;;;N;;;A65C;;A65C
+A65E;CYRILLIC CAPITAL LETTER YN;Lu;0;L;;;;;N;;;;A65F;
+A65F;CYRILLIC SMALL LETTER YN;Ll;0;L;;;;;N;;;A65E;;A65E
+A660;CYRILLIC CAPITAL LETTER REVERSED TSE;Lu;0;L;;;;;N;;;;A661;
+A661;CYRILLIC SMALL LETTER REVERSED TSE;Ll;0;L;;;;;N;;;A660;;A660
+A662;CYRILLIC CAPITAL LETTER SOFT DE;Lu;0;L;;;;;N;;;;A663;
+A663;CYRILLIC SMALL LETTER SOFT DE;Ll;0;L;;;;;N;;;A662;;A662
+A664;CYRILLIC CAPITAL LETTER SOFT EL;Lu;0;L;;;;;N;;;;A665;
+A665;CYRILLIC SMALL LETTER SOFT EL;Ll;0;L;;;;;N;;;A664;;A664
+A666;CYRILLIC CAPITAL LETTER SOFT EM;Lu;0;L;;;;;N;;;;A667;
+A667;CYRILLIC SMALL LETTER SOFT EM;Ll;0;L;;;;;N;;;A666;;A666
+A668;CYRILLIC CAPITAL LETTER MONOCULAR O;Lu;0;L;;;;;N;;;;A669;
+A669;CYRILLIC SMALL LETTER MONOCULAR O;Ll;0;L;;;;;N;;;A668;;A668
+A66A;CYRILLIC CAPITAL LETTER BINOCULAR O;Lu;0;L;;;;;N;;;;A66B;
+A66B;CYRILLIC SMALL LETTER BINOCULAR O;Ll;0;L;;;;;N;;;A66A;;A66A
+A66C;CYRILLIC CAPITAL LETTER DOUBLE MONOCULAR O;Lu;0;L;;;;;N;;;;A66D;
+A66D;CYRILLIC SMALL LETTER DOUBLE MONOCULAR O;Ll;0;L;;;;;N;;;A66C;;A66C
+A66E;CYRILLIC LETTER MULTIOCULAR O;Lo;0;L;;;;;N;;;;;
+A66F;COMBINING CYRILLIC VZMET;Mn;230;NSM;;;;;N;;;;;
+A670;COMBINING CYRILLIC TEN MILLIONS SIGN;Me;0;NSM;;;;;N;;;;;
+A671;COMBINING CYRILLIC HUNDRED MILLIONS SIGN;Me;0;NSM;;;;;N;;;;;
+A672;COMBINING CYRILLIC THOUSAND MILLIONS SIGN;Me;0;NSM;;;;;N;;;;;
+A673;SLAVONIC ASTERISK;Po;0;ON;;;;;N;;;;;
+A674;COMBINING CYRILLIC LETTER UKRAINIAN IE;Mn;230;NSM;;;;;N;;;;;
+A675;COMBINING CYRILLIC LETTER I;Mn;230;NSM;;;;;N;;;;;
+A676;COMBINING CYRILLIC LETTER YI;Mn;230;NSM;;;;;N;;;;;
+A677;COMBINING CYRILLIC LETTER U;Mn;230;NSM;;;;;N;;;;;
+A678;COMBINING CYRILLIC LETTER HARD SIGN;Mn;230;NSM;;;;;N;;;;;
+A679;COMBINING CYRILLIC LETTER YERU;Mn;230;NSM;;;;;N;;;;;
+A67A;COMBINING CYRILLIC LETTER SOFT SIGN;Mn;230;NSM;;;;;N;;;;;
+A67B;COMBINING CYRILLIC LETTER OMEGA;Mn;230;NSM;;;;;N;;;;;
+A67C;COMBINING CYRILLIC KAVYKA;Mn;230;NSM;;;;;N;;;;;
+A67D;COMBINING CYRILLIC PAYEROK;Mn;230;NSM;;;;;N;;;;;
+A67E;CYRILLIC KAVYKA;Po;0;ON;;;;;N;;;;;
+A67F;CYRILLIC PAYEROK;Lm;0;ON;;;;;N;;;;;
+A680;CYRILLIC CAPITAL LETTER DWE;Lu;0;L;;;;;N;;;;A681;
+A681;CYRILLIC SMALL LETTER DWE;Ll;0;L;;;;;N;;;A680;;A680
+A682;CYRILLIC CAPITAL LETTER DZWE;Lu;0;L;;;;;N;;;;A683;
+A683;CYRILLIC SMALL LETTER DZWE;Ll;0;L;;;;;N;;;A682;;A682
+A684;CYRILLIC CAPITAL LETTER ZHWE;Lu;0;L;;;;;N;;;;A685;
+A685;CYRILLIC SMALL LETTER ZHWE;Ll;0;L;;;;;N;;;A684;;A684
+A686;CYRILLIC CAPITAL LETTER CCHE;Lu;0;L;;;;;N;;;;A687;
+A687;CYRILLIC SMALL LETTER CCHE;Ll;0;L;;;;;N;;;A686;;A686
+A688;CYRILLIC CAPITAL LETTER DZZE;Lu;0;L;;;;;N;;;;A689;
+A689;CYRILLIC SMALL LETTER DZZE;Ll;0;L;;;;;N;;;A688;;A688
+A68A;CYRILLIC CAPITAL LETTER TE WITH MIDDLE HOOK;Lu;0;L;;;;;N;;;;A68B;
+A68B;CYRILLIC SMALL LETTER TE WITH MIDDLE HOOK;Ll;0;L;;;;;N;;;A68A;;A68A
+A68C;CYRILLIC CAPITAL LETTER TWE;Lu;0;L;;;;;N;;;;A68D;
+A68D;CYRILLIC SMALL LETTER TWE;Ll;0;L;;;;;N;;;A68C;;A68C
+A68E;CYRILLIC CAPITAL LETTER TSWE;Lu;0;L;;;;;N;;;;A68F;
+A68F;CYRILLIC SMALL LETTER TSWE;Ll;0;L;;;;;N;;;A68E;;A68E
+A690;CYRILLIC CAPITAL LETTER TSSE;Lu;0;L;;;;;N;;;;A691;
+A691;CYRILLIC SMALL LETTER TSSE;Ll;0;L;;;;;N;;;A690;;A690
+A692;CYRILLIC CAPITAL LETTER TCHE;Lu;0;L;;;;;N;;;;A693;
+A693;CYRILLIC SMALL LETTER TCHE;Ll;0;L;;;;;N;;;A692;;A692
+A694;CYRILLIC CAPITAL LETTER HWE;Lu;0;L;;;;;N;;;;A695;
+A695;CYRILLIC SMALL LETTER HWE;Ll;0;L;;;;;N;;;A694;;A694
+A696;CYRILLIC CAPITAL LETTER SHWE;Lu;0;L;;;;;N;;;;A697;
+A697;CYRILLIC SMALL LETTER SHWE;Ll;0;L;;;;;N;;;A696;;A696
+A698;CYRILLIC CAPITAL LETTER DOUBLE O;Lu;0;L;;;;;N;;;;A699;
+A699;CYRILLIC SMALL LETTER DOUBLE O;Ll;0;L;;;;;N;;;A698;;A698
+A69A;CYRILLIC CAPITAL LETTER CROSSED O;Lu;0;L;;;;;N;;;;A69B;
+A69B;CYRILLIC SMALL LETTER CROSSED O;Ll;0;L;;;;;N;;;A69A;;A69A
+A69C;MODIFIER LETTER CYRILLIC HARD SIGN;Lm;0;L;<super> 044A;;;;N;;;;;
+A69D;MODIFIER LETTER CYRILLIC SOFT SIGN;Lm;0;L;<super> 044C;;;;N;;;;;
+A69E;COMBINING CYRILLIC LETTER EF;Mn;230;NSM;;;;;N;;;;;
+A69F;COMBINING CYRILLIC LETTER IOTIFIED E;Mn;230;NSM;;;;;N;;;;;
+A6A0;BAMUM LETTER A;Lo;0;L;;;;;N;;;;;
+A6A1;BAMUM LETTER KA;Lo;0;L;;;;;N;;;;;
+A6A2;BAMUM LETTER U;Lo;0;L;;;;;N;;;;;
+A6A3;BAMUM LETTER KU;Lo;0;L;;;;;N;;;;;
+A6A4;BAMUM LETTER EE;Lo;0;L;;;;;N;;;;;
+A6A5;BAMUM LETTER REE;Lo;0;L;;;;;N;;;;;
+A6A6;BAMUM LETTER TAE;Lo;0;L;;;;;N;;;;;
+A6A7;BAMUM LETTER O;Lo;0;L;;;;;N;;;;;
+A6A8;BAMUM LETTER NYI;Lo;0;L;;;;;N;;;;;
+A6A9;BAMUM LETTER I;Lo;0;L;;;;;N;;;;;
+A6AA;BAMUM LETTER LA;Lo;0;L;;;;;N;;;;;
+A6AB;BAMUM LETTER PA;Lo;0;L;;;;;N;;;;;
+A6AC;BAMUM LETTER RII;Lo;0;L;;;;;N;;;;;
+A6AD;BAMUM LETTER RIEE;Lo;0;L;;;;;N;;;;;
+A6AE;BAMUM LETTER LEEEE;Lo;0;L;;;;;N;;;;;
+A6AF;BAMUM LETTER MEEEE;Lo;0;L;;;;;N;;;;;
+A6B0;BAMUM LETTER TAA;Lo;0;L;;;;;N;;;;;
+A6B1;BAMUM LETTER NDAA;Lo;0;L;;;;;N;;;;;
+A6B2;BAMUM LETTER NJAEM;Lo;0;L;;;;;N;;;;;
+A6B3;BAMUM LETTER M;Lo;0;L;;;;;N;;;;;
+A6B4;BAMUM LETTER SUU;Lo;0;L;;;;;N;;;;;
+A6B5;BAMUM LETTER MU;Lo;0;L;;;;;N;;;;;
+A6B6;BAMUM LETTER SHII;Lo;0;L;;;;;N;;;;;
+A6B7;BAMUM LETTER SI;Lo;0;L;;;;;N;;;;;
+A6B8;BAMUM LETTER SHEUX;Lo;0;L;;;;;N;;;;;
+A6B9;BAMUM LETTER SEUX;Lo;0;L;;;;;N;;;;;
+A6BA;BAMUM LETTER KYEE;Lo;0;L;;;;;N;;;;;
+A6BB;BAMUM LETTER KET;Lo;0;L;;;;;N;;;;;
+A6BC;BAMUM LETTER NUAE;Lo;0;L;;;;;N;;;;;
+A6BD;BAMUM LETTER NU;Lo;0;L;;;;;N;;;;;
+A6BE;BAMUM LETTER NJUAE;Lo;0;L;;;;;N;;;;;
+A6BF;BAMUM LETTER YOQ;Lo;0;L;;;;;N;;;;;
+A6C0;BAMUM LETTER SHU;Lo;0;L;;;;;N;;;;;
+A6C1;BAMUM LETTER YUQ;Lo;0;L;;;;;N;;;;;
+A6C2;BAMUM LETTER YA;Lo;0;L;;;;;N;;;;;
+A6C3;BAMUM LETTER NSHA;Lo;0;L;;;;;N;;;;;
+A6C4;BAMUM LETTER KEUX;Lo;0;L;;;;;N;;;;;
+A6C5;BAMUM LETTER PEUX;Lo;0;L;;;;;N;;;;;
+A6C6;BAMUM LETTER NJEE;Lo;0;L;;;;;N;;;;;
+A6C7;BAMUM LETTER NTEE;Lo;0;L;;;;;N;;;;;
+A6C8;BAMUM LETTER PUE;Lo;0;L;;;;;N;;;;;
+A6C9;BAMUM LETTER WUE;Lo;0;L;;;;;N;;;;;
+A6CA;BAMUM LETTER PEE;Lo;0;L;;;;;N;;;;;
+A6CB;BAMUM LETTER FEE;Lo;0;L;;;;;N;;;;;
+A6CC;BAMUM LETTER RU;Lo;0;L;;;;;N;;;;;
+A6CD;BAMUM LETTER LU;Lo;0;L;;;;;N;;;;;
+A6CE;BAMUM LETTER MI;Lo;0;L;;;;;N;;;;;
+A6CF;BAMUM LETTER NI;Lo;0;L;;;;;N;;;;;
+A6D0;BAMUM LETTER REUX;Lo;0;L;;;;;N;;;;;
+A6D1;BAMUM LETTER RAE;Lo;0;L;;;;;N;;;;;
+A6D2;BAMUM LETTER KEN;Lo;0;L;;;;;N;;;;;
+A6D3;BAMUM LETTER NGKWAEN;Lo;0;L;;;;;N;;;;;
+A6D4;BAMUM LETTER NGGA;Lo;0;L;;;;;N;;;;;
+A6D5;BAMUM LETTER NGA;Lo;0;L;;;;;N;;;;;
+A6D6;BAMUM LETTER SHO;Lo;0;L;;;;;N;;;;;
+A6D7;BAMUM LETTER PUAE;Lo;0;L;;;;;N;;;;;
+A6D8;BAMUM LETTER FU;Lo;0;L;;;;;N;;;;;
+A6D9;BAMUM LETTER FOM;Lo;0;L;;;;;N;;;;;
+A6DA;BAMUM LETTER WA;Lo;0;L;;;;;N;;;;;
+A6DB;BAMUM LETTER NA;Lo;0;L;;;;;N;;;;;
+A6DC;BAMUM LETTER LI;Lo;0;L;;;;;N;;;;;
+A6DD;BAMUM LETTER PI;Lo;0;L;;;;;N;;;;;
+A6DE;BAMUM LETTER LOQ;Lo;0;L;;;;;N;;;;;
+A6DF;BAMUM LETTER KO;Lo;0;L;;;;;N;;;;;
+A6E0;BAMUM LETTER MBEN;Lo;0;L;;;;;N;;;;;
+A6E1;BAMUM LETTER REN;Lo;0;L;;;;;N;;;;;
+A6E2;BAMUM LETTER MEN;Lo;0;L;;;;;N;;;;;
+A6E3;BAMUM LETTER MA;Lo;0;L;;;;;N;;;;;
+A6E4;BAMUM LETTER TI;Lo;0;L;;;;;N;;;;;
+A6E5;BAMUM LETTER KI;Lo;0;L;;;;;N;;;;;
+A6E6;BAMUM LETTER MO;Nl;0;L;;;;1;N;;;;;
+A6E7;BAMUM LETTER MBAA;Nl;0;L;;;;2;N;;;;;
+A6E8;BAMUM LETTER TET;Nl;0;L;;;;3;N;;;;;
+A6E9;BAMUM LETTER KPA;Nl;0;L;;;;4;N;;;;;
+A6EA;BAMUM LETTER TEN;Nl;0;L;;;;5;N;;;;;
+A6EB;BAMUM LETTER NTUU;Nl;0;L;;;;6;N;;;;;
+A6EC;BAMUM LETTER SAMBA;Nl;0;L;;;;7;N;;;;;
+A6ED;BAMUM LETTER FAAMAE;Nl;0;L;;;;8;N;;;;;
+A6EE;BAMUM LETTER KOVUU;Nl;0;L;;;;9;N;;;;;
+A6EF;BAMUM LETTER KOGHOM;Nl;0;L;;;;0;N;;;;;
+A6F0;BAMUM COMBINING MARK KOQNDON;Mn;230;NSM;;;;;N;;;;;
+A6F1;BAMUM COMBINING MARK TUKWENTIS;Mn;230;NSM;;;;;N;;;;;
+A6F2;BAMUM NJAEMLI;Po;0;L;;;;;N;;;;;
+A6F3;BAMUM FULL STOP;Po;0;L;;;;;N;;;;;
+A6F4;BAMUM COLON;Po;0;L;;;;;N;;;;;
+A6F5;BAMUM COMMA;Po;0;L;;;;;N;;;;;
+A6F6;BAMUM SEMICOLON;Po;0;L;;;;;N;;;;;
+A6F7;BAMUM QUESTION MARK;Po;0;L;;;;;N;;;;;
+A700;MODIFIER LETTER CHINESE TONE YIN PING;Sk;0;ON;;;;;N;;;;;
+A701;MODIFIER LETTER CHINESE TONE YANG PING;Sk;0;ON;;;;;N;;;;;
+A702;MODIFIER LETTER CHINESE TONE YIN SHANG;Sk;0;ON;;;;;N;;;;;
+A703;MODIFIER LETTER CHINESE TONE YANG SHANG;Sk;0;ON;;;;;N;;;;;
+A704;MODIFIER LETTER CHINESE TONE YIN QU;Sk;0;ON;;;;;N;;;;;
+A705;MODIFIER LETTER CHINESE TONE YANG QU;Sk;0;ON;;;;;N;;;;;
+A706;MODIFIER LETTER CHINESE TONE YIN RU;Sk;0;ON;;;;;N;;;;;
+A707;MODIFIER LETTER CHINESE TONE YANG RU;Sk;0;ON;;;;;N;;;;;
+A708;MODIFIER LETTER EXTRA-HIGH DOTTED TONE BAR;Sk;0;ON;;;;;N;;;;;
+A709;MODIFIER LETTER HIGH DOTTED TONE BAR;Sk;0;ON;;;;;N;;;;;
+A70A;MODIFIER LETTER MID DOTTED TONE BAR;Sk;0;ON;;;;;N;;;;;
+A70B;MODIFIER LETTER LOW DOTTED TONE BAR;Sk;0;ON;;;;;N;;;;;
+A70C;MODIFIER LETTER EXTRA-LOW DOTTED TONE BAR;Sk;0;ON;;;;;N;;;;;
+A70D;MODIFIER LETTER EXTRA-HIGH DOTTED LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;;
+A70E;MODIFIER LETTER HIGH DOTTED LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;;
+A70F;MODIFIER LETTER MID DOTTED LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;;
+A710;MODIFIER LETTER LOW DOTTED LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;;
+A711;MODIFIER LETTER EXTRA-LOW DOTTED LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;;
+A712;MODIFIER LETTER EXTRA-HIGH LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;;
+A713;MODIFIER LETTER HIGH LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;;
+A714;MODIFIER LETTER MID LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;;
+A715;MODIFIER LETTER LOW LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;;
+A716;MODIFIER LETTER EXTRA-LOW LEFT-STEM TONE BAR;Sk;0;ON;;;;;N;;;;;
+A717;MODIFIER LETTER DOT VERTICAL BAR;Lm;0;ON;;;;;N;;;;;
+A718;MODIFIER LETTER DOT SLASH;Lm;0;ON;;;;;N;;;;;
+A719;MODIFIER LETTER DOT HORIZONTAL BAR;Lm;0;ON;;;;;N;;;;;
+A71A;MODIFIER LETTER LOWER RIGHT CORNER ANGLE;Lm;0;ON;;;;;N;;;;;
+A71B;MODIFIER LETTER RAISED UP ARROW;Lm;0;ON;;;;;N;;;;;
+A71C;MODIFIER LETTER RAISED DOWN ARROW;Lm;0;ON;;;;;N;;;;;
+A71D;MODIFIER LETTER RAISED EXCLAMATION MARK;Lm;0;ON;;;;;N;;;;;
+A71E;MODIFIER LETTER RAISED INVERTED EXCLAMATION MARK;Lm;0;ON;;;;;N;;;;;
+A71F;MODIFIER LETTER LOW INVERTED EXCLAMATION MARK;Lm;0;ON;;;;;N;;;;;
+A720;MODIFIER LETTER STRESS AND HIGH TONE;Sk;0;ON;;;;;N;;;;;
+A721;MODIFIER LETTER STRESS AND LOW TONE;Sk;0;ON;;;;;N;;;;;
+A722;LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF;Lu;0;L;;;;;N;;;;A723;
+A723;LATIN SMALL LETTER EGYPTOLOGICAL ALEF;Ll;0;L;;;;;N;;;A722;;A722
+A724;LATIN CAPITAL LETTER EGYPTOLOGICAL AIN;Lu;0;L;;;;;N;;;;A725;
+A725;LATIN SMALL LETTER EGYPTOLOGICAL AIN;Ll;0;L;;;;;N;;;A724;;A724
+A726;LATIN CAPITAL LETTER HENG;Lu;0;L;;;;;N;;;;A727;
+A727;LATIN SMALL LETTER HENG;Ll;0;L;;;;;N;;;A726;;A726
+A728;LATIN CAPITAL LETTER TZ;Lu;0;L;;;;;N;;;;A729;
+A729;LATIN SMALL LETTER TZ;Ll;0;L;;;;;N;;;A728;;A728
+A72A;LATIN CAPITAL LETTER TRESILLO;Lu;0;L;;;;;N;;;;A72B;
+A72B;LATIN SMALL LETTER TRESILLO;Ll;0;L;;;;;N;;;A72A;;A72A
+A72C;LATIN CAPITAL LETTER CUATRILLO;Lu;0;L;;;;;N;;;;A72D;
+A72D;LATIN SMALL LETTER CUATRILLO;Ll;0;L;;;;;N;;;A72C;;A72C
+A72E;LATIN CAPITAL LETTER CUATRILLO WITH COMMA;Lu;0;L;;;;;N;;;;A72F;
+A72F;LATIN SMALL LETTER CUATRILLO WITH COMMA;Ll;0;L;;;;;N;;;A72E;;A72E
+A730;LATIN LETTER SMALL CAPITAL F;Ll;0;L;;;;;N;;;;;
+A731;LATIN LETTER SMALL CAPITAL S;Ll;0;L;;;;;N;;;;;
+A732;LATIN CAPITAL LETTER AA;Lu;0;L;;;;;N;;;;A733;
+A733;LATIN SMALL LETTER AA;Ll;0;L;;;;;N;;;A732;;A732
+A734;LATIN CAPITAL LETTER AO;Lu;0;L;;;;;N;;;;A735;
+A735;LATIN SMALL LETTER AO;Ll;0;L;;;;;N;;;A734;;A734
+A736;LATIN CAPITAL LETTER AU;Lu;0;L;;;;;N;;;;A737;
+A737;LATIN SMALL LETTER AU;Ll;0;L;;;;;N;;;A736;;A736
+A738;LATIN CAPITAL LETTER AV;Lu;0;L;;;;;N;;;;A739;
+A739;LATIN SMALL LETTER AV;Ll;0;L;;;;;N;;;A738;;A738
+A73A;LATIN CAPITAL LETTER AV WITH HORIZONTAL BAR;Lu;0;L;;;;;N;;;;A73B;
+A73B;LATIN SMALL LETTER AV WITH HORIZONTAL BAR;Ll;0;L;;;;;N;;;A73A;;A73A
+A73C;LATIN CAPITAL LETTER AY;Lu;0;L;;;;;N;;;;A73D;
+A73D;LATIN SMALL LETTER AY;Ll;0;L;;;;;N;;;A73C;;A73C
+A73E;LATIN CAPITAL LETTER REVERSED C WITH DOT;Lu;0;L;;;;;N;;;;A73F;
+A73F;LATIN SMALL LETTER REVERSED C WITH DOT;Ll;0;L;;;;;N;;;A73E;;A73E
+A740;LATIN CAPITAL LETTER K WITH STROKE;Lu;0;L;;;;;N;;;;A741;
+A741;LATIN SMALL LETTER K WITH STROKE;Ll;0;L;;;;;N;;;A740;;A740
+A742;LATIN CAPITAL LETTER K WITH DIAGONAL STROKE;Lu;0;L;;;;;N;;;;A743;
+A743;LATIN SMALL LETTER K WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;A742;;A742
+A744;LATIN CAPITAL LETTER K WITH STROKE AND DIAGONAL STROKE;Lu;0;L;;;;;N;;;;A745;
+A745;LATIN SMALL LETTER K WITH STROKE AND DIAGONAL STROKE;Ll;0;L;;;;;N;;;A744;;A744
+A746;LATIN CAPITAL LETTER BROKEN L;Lu;0;L;;;;;N;;;;A747;
+A747;LATIN SMALL LETTER BROKEN L;Ll;0;L;;;;;N;;;A746;;A746
+A748;LATIN CAPITAL LETTER L WITH HIGH STROKE;Lu;0;L;;;;;N;;;;A749;
+A749;LATIN SMALL LETTER L WITH HIGH STROKE;Ll;0;L;;;;;N;;;A748;;A748
+A74A;LATIN CAPITAL LETTER O WITH LONG STROKE OVERLAY;Lu;0;L;;;;;N;;;;A74B;
+A74B;LATIN SMALL LETTER O WITH LONG STROKE OVERLAY;Ll;0;L;;;;;N;;;A74A;;A74A
+A74C;LATIN CAPITAL LETTER O WITH LOOP;Lu;0;L;;;;;N;;;;A74D;
+A74D;LATIN SMALL LETTER O WITH LOOP;Ll;0;L;;;;;N;;;A74C;;A74C
+A74E;LATIN CAPITAL LETTER OO;Lu;0;L;;;;;N;;;;A74F;
+A74F;LATIN SMALL LETTER OO;Ll;0;L;;;;;N;;;A74E;;A74E
+A750;LATIN CAPITAL LETTER P WITH STROKE THROUGH DESCENDER;Lu;0;L;;;;;N;;;;A751;
+A751;LATIN SMALL LETTER P WITH STROKE THROUGH DESCENDER;Ll;0;L;;;;;N;;;A750;;A750
+A752;LATIN CAPITAL LETTER P WITH FLOURISH;Lu;0;L;;;;;N;;;;A753;
+A753;LATIN SMALL LETTER P WITH FLOURISH;Ll;0;L;;;;;N;;;A752;;A752
+A754;LATIN CAPITAL LETTER P WITH SQUIRREL TAIL;Lu;0;L;;;;;N;;;;A755;
+A755;LATIN SMALL LETTER P WITH SQUIRREL TAIL;Ll;0;L;;;;;N;;;A754;;A754
+A756;LATIN CAPITAL LETTER Q WITH STROKE THROUGH DESCENDER;Lu;0;L;;;;;N;;;;A757;
+A757;LATIN SMALL LETTER Q WITH STROKE THROUGH DESCENDER;Ll;0;L;;;;;N;;;A756;;A756
+A758;LATIN CAPITAL LETTER Q WITH DIAGONAL STROKE;Lu;0;L;;;;;N;;;;A759;
+A759;LATIN SMALL LETTER Q WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;A758;;A758
+A75A;LATIN CAPITAL LETTER R ROTUNDA;Lu;0;L;;;;;N;;;;A75B;
+A75B;LATIN SMALL LETTER R ROTUNDA;Ll;0;L;;;;;N;;;A75A;;A75A
+A75C;LATIN CAPITAL LETTER RUM ROTUNDA;Lu;0;L;;;;;N;;;;A75D;
+A75D;LATIN SMALL LETTER RUM ROTUNDA;Ll;0;L;;;;;N;;;A75C;;A75C
+A75E;LATIN CAPITAL LETTER V WITH DIAGONAL STROKE;Lu;0;L;;;;;N;;;;A75F;
+A75F;LATIN SMALL LETTER V WITH DIAGONAL STROKE;Ll;0;L;;;;;N;;;A75E;;A75E
+A760;LATIN CAPITAL LETTER VY;Lu;0;L;;;;;N;;;;A761;
+A761;LATIN SMALL LETTER VY;Ll;0;L;;;;;N;;;A760;;A760
+A762;LATIN CAPITAL LETTER VISIGOTHIC Z;Lu;0;L;;;;;N;;;;A763;
+A763;LATIN SMALL LETTER VISIGOTHIC Z;Ll;0;L;;;;;N;;;A762;;A762
+A764;LATIN CAPITAL LETTER THORN WITH STROKE;Lu;0;L;;;;;N;;;;A765;
+A765;LATIN SMALL LETTER THORN WITH STROKE;Ll;0;L;;;;;N;;;A764;;A764
+A766;LATIN CAPITAL LETTER THORN WITH STROKE THROUGH DESCENDER;Lu;0;L;;;;;N;;;;A767;
+A767;LATIN SMALL LETTER THORN WITH STROKE THROUGH DESCENDER;Ll;0;L;;;;;N;;;A766;;A766
+A768;LATIN CAPITAL LETTER VEND;Lu;0;L;;;;;N;;;;A769;
+A769;LATIN SMALL LETTER VEND;Ll;0;L;;;;;N;;;A768;;A768
+A76A;LATIN CAPITAL LETTER ET;Lu;0;L;;;;;N;;;;A76B;
+A76B;LATIN SMALL LETTER ET;Ll;0;L;;;;;N;;;A76A;;A76A
+A76C;LATIN CAPITAL LETTER IS;Lu;0;L;;;;;N;;;;A76D;
+A76D;LATIN SMALL LETTER IS;Ll;0;L;;;;;N;;;A76C;;A76C
+A76E;LATIN CAPITAL LETTER CON;Lu;0;L;;;;;N;;;;A76F;
+A76F;LATIN SMALL LETTER CON;Ll;0;L;;;;;N;;;A76E;;A76E
+A770;MODIFIER LETTER US;Lm;0;L;<super> A76F;;;;N;;;;;
+A771;LATIN SMALL LETTER DUM;Ll;0;L;;;;;N;;;;;
+A772;LATIN SMALL LETTER LUM;Ll;0;L;;;;;N;;;;;
+A773;LATIN SMALL LETTER MUM;Ll;0;L;;;;;N;;;;;
+A774;LATIN SMALL LETTER NUM;Ll;0;L;;;;;N;;;;;
+A775;LATIN SMALL LETTER RUM;Ll;0;L;;;;;N;;;;;
+A776;LATIN LETTER SMALL CAPITAL RUM;Ll;0;L;;;;;N;;;;;
+A777;LATIN SMALL LETTER TUM;Ll;0;L;;;;;N;;;;;
+A778;LATIN SMALL LETTER UM;Ll;0;L;;;;;N;;;;;
+A779;LATIN CAPITAL LETTER INSULAR D;Lu;0;L;;;;;N;;;;A77A;
+A77A;LATIN SMALL LETTER INSULAR D;Ll;0;L;;;;;N;;;A779;;A779
+A77B;LATIN CAPITAL LETTER INSULAR F;Lu;0;L;;;;;N;;;;A77C;
+A77C;LATIN SMALL LETTER INSULAR F;Ll;0;L;;;;;N;;;A77B;;A77B
+A77D;LATIN CAPITAL LETTER INSULAR G;Lu;0;L;;;;;N;;;;1D79;
+A77E;LATIN CAPITAL LETTER TURNED INSULAR G;Lu;0;L;;;;;N;;;;A77F;
+A77F;LATIN SMALL LETTER TURNED INSULAR G;Ll;0;L;;;;;N;;;A77E;;A77E
+A780;LATIN CAPITAL LETTER TURNED L;Lu;0;L;;;;;N;;;;A781;
+A781;LATIN SMALL LETTER TURNED L;Ll;0;L;;;;;N;;;A780;;A780
+A782;LATIN CAPITAL LETTER INSULAR R;Lu;0;L;;;;;N;;;;A783;
+A783;LATIN SMALL LETTER INSULAR R;Ll;0;L;;;;;N;;;A782;;A782
+A784;LATIN CAPITAL LETTER INSULAR S;Lu;0;L;;;;;N;;;;A785;
+A785;LATIN SMALL LETTER INSULAR S;Ll;0;L;;;;;N;;;A784;;A784
+A786;LATIN CAPITAL LETTER INSULAR T;Lu;0;L;;;;;N;;;;A787;
+A787;LATIN SMALL LETTER INSULAR T;Ll;0;L;;;;;N;;;A786;;A786
+A788;MODIFIER LETTER LOW CIRCUMFLEX ACCENT;Lm;0;ON;;;;;N;;;;;
+A789;MODIFIER LETTER COLON;Sk;0;L;;;;;N;;;;;
+A78A;MODIFIER LETTER SHORT EQUALS SIGN;Sk;0;L;;;;;N;;;;;
+A78B;LATIN CAPITAL LETTER SALTILLO;Lu;0;L;;;;;N;;;;A78C;
+A78C;LATIN SMALL LETTER SALTILLO;Ll;0;L;;;;;N;;;A78B;;A78B
+A78D;LATIN CAPITAL LETTER TURNED H;Lu;0;L;;;;;N;;;;0265;
+A78E;LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT;Ll;0;L;;;;;N;;;;;
+A78F;LATIN LETTER SINOLOGICAL DOT;Lo;0;L;;;;;N;;;;;
+A790;LATIN CAPITAL LETTER N WITH DESCENDER;Lu;0;L;;;;;N;;;;A791;
+A791;LATIN SMALL LETTER N WITH DESCENDER;Ll;0;L;;;;;N;;;A790;;A790
+A792;LATIN CAPITAL LETTER C WITH BAR;Lu;0;L;;;;;N;;;;A793;
+A793;LATIN SMALL LETTER C WITH BAR;Ll;0;L;;;;;N;;;A792;;A792
+A794;LATIN SMALL LETTER C WITH PALATAL HOOK;Ll;0;L;;;;;N;;;A7C4;;A7C4
+A795;LATIN SMALL LETTER H WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+A796;LATIN CAPITAL LETTER B WITH FLOURISH;Lu;0;L;;;;;N;;;;A797;
+A797;LATIN SMALL LETTER B WITH FLOURISH;Ll;0;L;;;;;N;;;A796;;A796
+A798;LATIN CAPITAL LETTER F WITH STROKE;Lu;0;L;;;;;N;;;;A799;
+A799;LATIN SMALL LETTER F WITH STROKE;Ll;0;L;;;;;N;;;A798;;A798
+A79A;LATIN CAPITAL LETTER VOLAPUK AE;Lu;0;L;;;;;N;;;;A79B;
+A79B;LATIN SMALL LETTER VOLAPUK AE;Ll;0;L;;;;;N;;;A79A;;A79A
+A79C;LATIN CAPITAL LETTER VOLAPUK OE;Lu;0;L;;;;;N;;;;A79D;
+A79D;LATIN SMALL LETTER VOLAPUK OE;Ll;0;L;;;;;N;;;A79C;;A79C
+A79E;LATIN CAPITAL LETTER VOLAPUK UE;Lu;0;L;;;;;N;;;;A79F;
+A79F;LATIN SMALL LETTER VOLAPUK UE;Ll;0;L;;;;;N;;;A79E;;A79E
+A7A0;LATIN CAPITAL LETTER G WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A1;
+A7A1;LATIN SMALL LETTER G WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A0;;A7A0
+A7A2;LATIN CAPITAL LETTER K WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A3;
+A7A3;LATIN SMALL LETTER K WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A2;;A7A2
+A7A4;LATIN CAPITAL LETTER N WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A5;
+A7A5;LATIN SMALL LETTER N WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A4;;A7A4
+A7A6;LATIN CAPITAL LETTER R WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A7;
+A7A7;LATIN SMALL LETTER R WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A6;;A7A6
+A7A8;LATIN CAPITAL LETTER S WITH OBLIQUE STROKE;Lu;0;L;;;;;N;;;;A7A9;
+A7A9;LATIN SMALL LETTER S WITH OBLIQUE STROKE;Ll;0;L;;;;;N;;;A7A8;;A7A8
+A7AA;LATIN CAPITAL LETTER H WITH HOOK;Lu;0;L;;;;;N;;;;0266;
+A7AB;LATIN CAPITAL LETTER REVERSED OPEN E;Lu;0;L;;;;;N;;;;025C;
+A7AC;LATIN CAPITAL LETTER SCRIPT G;Lu;0;L;;;;;N;;;;0261;
+A7AD;LATIN CAPITAL LETTER L WITH BELT;Lu;0;L;;;;;N;;;;026C;
+A7AE;LATIN CAPITAL LETTER SMALL CAPITAL I;Lu;0;L;;;;;N;;;;026A;
+A7AF;LATIN LETTER SMALL CAPITAL Q;Ll;0;L;;;;;N;;;;;
+A7B0;LATIN CAPITAL LETTER TURNED K;Lu;0;L;;;;;N;;;;029E;
+A7B1;LATIN CAPITAL LETTER TURNED T;Lu;0;L;;;;;N;;;;0287;
+A7B2;LATIN CAPITAL LETTER J WITH CROSSED-TAIL;Lu;0;L;;;;;N;;;;029D;
+A7B3;LATIN CAPITAL LETTER CHI;Lu;0;L;;;;;N;;;;AB53;
+A7B4;LATIN CAPITAL LETTER BETA;Lu;0;L;;;;;N;;;;A7B5;
+A7B5;LATIN SMALL LETTER BETA;Ll;0;L;;;;;N;;;A7B4;;A7B4
+A7B6;LATIN CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;A7B7;
+A7B7;LATIN SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;A7B6;;A7B6
+A7B8;LATIN CAPITAL LETTER U WITH STROKE;Lu;0;L;;;;;N;;;;A7B9;
+A7B9;LATIN SMALL LETTER U WITH STROKE;Ll;0;L;;;;;N;;;A7B8;;A7B8
+A7BA;LATIN CAPITAL LETTER GLOTTAL A;Lu;0;L;;;;;N;;;;A7BB;
+A7BB;LATIN SMALL LETTER GLOTTAL A;Ll;0;L;;;;;N;;;A7BA;;A7BA
+A7BC;LATIN CAPITAL LETTER GLOTTAL I;Lu;0;L;;;;;N;;;;A7BD;
+A7BD;LATIN SMALL LETTER GLOTTAL I;Ll;0;L;;;;;N;;;A7BC;;A7BC
+A7BE;LATIN CAPITAL LETTER GLOTTAL U;Lu;0;L;;;;;N;;;;A7BF;
+A7BF;LATIN SMALL LETTER GLOTTAL U;Ll;0;L;;;;;N;;;A7BE;;A7BE
+A7C2;LATIN CAPITAL LETTER ANGLICANA W;Lu;0;L;;;;;N;;;;A7C3;
+A7C3;LATIN SMALL LETTER ANGLICANA W;Ll;0;L;;;;;N;;;A7C2;;A7C2
+A7C4;LATIN CAPITAL LETTER C WITH PALATAL HOOK;Lu;0;L;;;;;N;;;;A794;
+A7C5;LATIN CAPITAL LETTER S WITH HOOK;Lu;0;L;;;;;N;;;;0282;
+A7C6;LATIN CAPITAL LETTER Z WITH PALATAL HOOK;Lu;0;L;;;;;N;;;;1D8E;
+A7F7;LATIN EPIGRAPHIC LETTER SIDEWAYS I;Lo;0;L;;;;;N;;;;;
+A7F8;MODIFIER LETTER CAPITAL H WITH STROKE;Lm;0;L;<super> 0126;;;;N;;;;;
+A7F9;MODIFIER LETTER SMALL LIGATURE OE;Lm;0;L;<super> 0153;;;;N;;;;;
+A7FA;LATIN LETTER SMALL CAPITAL TURNED M;Ll;0;L;;;;;N;;;;;
+A7FB;LATIN EPIGRAPHIC LETTER REVERSED F;Lo;0;L;;;;;N;;;;;
+A7FC;LATIN EPIGRAPHIC LETTER REVERSED P;Lo;0;L;;;;;N;;;;;
+A7FD;LATIN EPIGRAPHIC LETTER INVERTED M;Lo;0;L;;;;;N;;;;;
+A7FE;LATIN EPIGRAPHIC LETTER I LONGA;Lo;0;L;;;;;N;;;;;
+A7FF;LATIN EPIGRAPHIC LETTER ARCHAIC M;Lo;0;L;;;;;N;;;;;
+A800;SYLOTI NAGRI LETTER A;Lo;0;L;;;;;N;;;;;
+A801;SYLOTI NAGRI LETTER I;Lo;0;L;;;;;N;;;;;
+A802;SYLOTI NAGRI SIGN DVISVARA;Mn;0;NSM;;;;;N;;;;;
+A803;SYLOTI NAGRI LETTER U;Lo;0;L;;;;;N;;;;;
+A804;SYLOTI NAGRI LETTER E;Lo;0;L;;;;;N;;;;;
+A805;SYLOTI NAGRI LETTER O;Lo;0;L;;;;;N;;;;;
+A806;SYLOTI NAGRI SIGN HASANTA;Mn;9;NSM;;;;;N;;;;;
+A807;SYLOTI NAGRI LETTER KO;Lo;0;L;;;;;N;;;;;
+A808;SYLOTI NAGRI LETTER KHO;Lo;0;L;;;;;N;;;;;
+A809;SYLOTI NAGRI LETTER GO;Lo;0;L;;;;;N;;;;;
+A80A;SYLOTI NAGRI LETTER GHO;Lo;0;L;;;;;N;;;;;
+A80B;SYLOTI NAGRI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+A80C;SYLOTI NAGRI LETTER CO;Lo;0;L;;;;;N;;;;;
+A80D;SYLOTI NAGRI LETTER CHO;Lo;0;L;;;;;N;;;;;
+A80E;SYLOTI NAGRI LETTER JO;Lo;0;L;;;;;N;;;;;
+A80F;SYLOTI NAGRI LETTER JHO;Lo;0;L;;;;;N;;;;;
+A810;SYLOTI NAGRI LETTER TTO;Lo;0;L;;;;;N;;;;;
+A811;SYLOTI NAGRI LETTER TTHO;Lo;0;L;;;;;N;;;;;
+A812;SYLOTI NAGRI LETTER DDO;Lo;0;L;;;;;N;;;;;
+A813;SYLOTI NAGRI LETTER DDHO;Lo;0;L;;;;;N;;;;;
+A814;SYLOTI NAGRI LETTER TO;Lo;0;L;;;;;N;;;;;
+A815;SYLOTI NAGRI LETTER THO;Lo;0;L;;;;;N;;;;;
+A816;SYLOTI NAGRI LETTER DO;Lo;0;L;;;;;N;;;;;
+A817;SYLOTI NAGRI LETTER DHO;Lo;0;L;;;;;N;;;;;
+A818;SYLOTI NAGRI LETTER NO;Lo;0;L;;;;;N;;;;;
+A819;SYLOTI NAGRI LETTER PO;Lo;0;L;;;;;N;;;;;
+A81A;SYLOTI NAGRI LETTER PHO;Lo;0;L;;;;;N;;;;;
+A81B;SYLOTI NAGRI LETTER BO;Lo;0;L;;;;;N;;;;;
+A81C;SYLOTI NAGRI LETTER BHO;Lo;0;L;;;;;N;;;;;
+A81D;SYLOTI NAGRI LETTER MO;Lo;0;L;;;;;N;;;;;
+A81E;SYLOTI NAGRI LETTER RO;Lo;0;L;;;;;N;;;;;
+A81F;SYLOTI NAGRI LETTER LO;Lo;0;L;;;;;N;;;;;
+A820;SYLOTI NAGRI LETTER RRO;Lo;0;L;;;;;N;;;;;
+A821;SYLOTI NAGRI LETTER SO;Lo;0;L;;;;;N;;;;;
+A822;SYLOTI NAGRI LETTER HO;Lo;0;L;;;;;N;;;;;
+A823;SYLOTI NAGRI VOWEL SIGN A;Mc;0;L;;;;;N;;;;;
+A824;SYLOTI NAGRI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+A825;SYLOTI NAGRI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+A826;SYLOTI NAGRI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+A827;SYLOTI NAGRI VOWEL SIGN OO;Mc;0;L;;;;;N;;;;;
+A828;SYLOTI NAGRI POETRY MARK-1;So;0;ON;;;;;N;;;;;
+A829;SYLOTI NAGRI POETRY MARK-2;So;0;ON;;;;;N;;;;;
+A82A;SYLOTI NAGRI POETRY MARK-3;So;0;ON;;;;;N;;;;;
+A82B;SYLOTI NAGRI POETRY MARK-4;So;0;ON;;;;;N;;;;;
+A830;NORTH INDIC FRACTION ONE QUARTER;No;0;L;;;;1/4;N;;;;;
+A831;NORTH INDIC FRACTION ONE HALF;No;0;L;;;;1/2;N;;;;;
+A832;NORTH INDIC FRACTION THREE QUARTERS;No;0;L;;;;3/4;N;;;;;
+A833;NORTH INDIC FRACTION ONE SIXTEENTH;No;0;L;;;;1/16;N;;;;;
+A834;NORTH INDIC FRACTION ONE EIGHTH;No;0;L;;;;1/8;N;;;;;
+A835;NORTH INDIC FRACTION THREE SIXTEENTHS;No;0;L;;;;3/16;N;;;;;
+A836;NORTH INDIC QUARTER MARK;So;0;L;;;;;N;;;;;
+A837;NORTH INDIC PLACEHOLDER MARK;So;0;L;;;;;N;;;;;
+A838;NORTH INDIC RUPEE MARK;Sc;0;ET;;;;;N;;;;;
+A839;NORTH INDIC QUANTITY MARK;So;0;ET;;;;;N;;;;;
+A840;PHAGS-PA LETTER KA;Lo;0;L;;;;;N;;;;;
+A841;PHAGS-PA LETTER KHA;Lo;0;L;;;;;N;;;;;
+A842;PHAGS-PA LETTER GA;Lo;0;L;;;;;N;;;;;
+A843;PHAGS-PA LETTER NGA;Lo;0;L;;;;;N;;;;;
+A844;PHAGS-PA LETTER CA;Lo;0;L;;;;;N;;;;;
+A845;PHAGS-PA LETTER CHA;Lo;0;L;;;;;N;;;;;
+A846;PHAGS-PA LETTER JA;Lo;0;L;;;;;N;;;;;
+A847;PHAGS-PA LETTER NYA;Lo;0;L;;;;;N;;;;;
+A848;PHAGS-PA LETTER TA;Lo;0;L;;;;;N;;;;;
+A849;PHAGS-PA LETTER THA;Lo;0;L;;;;;N;;;;;
+A84A;PHAGS-PA LETTER DA;Lo;0;L;;;;;N;;;;;
+A84B;PHAGS-PA LETTER NA;Lo;0;L;;;;;N;;;;;
+A84C;PHAGS-PA LETTER PA;Lo;0;L;;;;;N;;;;;
+A84D;PHAGS-PA LETTER PHA;Lo;0;L;;;;;N;;;;;
+A84E;PHAGS-PA LETTER BA;Lo;0;L;;;;;N;;;;;
+A84F;PHAGS-PA LETTER MA;Lo;0;L;;;;;N;;;;;
+A850;PHAGS-PA LETTER TSA;Lo;0;L;;;;;N;;;;;
+A851;PHAGS-PA LETTER TSHA;Lo;0;L;;;;;N;;;;;
+A852;PHAGS-PA LETTER DZA;Lo;0;L;;;;;N;;;;;
+A853;PHAGS-PA LETTER WA;Lo;0;L;;;;;N;;;;;
+A854;PHAGS-PA LETTER ZHA;Lo;0;L;;;;;N;;;;;
+A855;PHAGS-PA LETTER ZA;Lo;0;L;;;;;N;;;;;
+A856;PHAGS-PA LETTER SMALL A;Lo;0;L;;;;;N;;;;;
+A857;PHAGS-PA LETTER YA;Lo;0;L;;;;;N;;;;;
+A858;PHAGS-PA LETTER RA;Lo;0;L;;;;;N;;;;;
+A859;PHAGS-PA LETTER LA;Lo;0;L;;;;;N;;;;;
+A85A;PHAGS-PA LETTER SHA;Lo;0;L;;;;;N;;;;;
+A85B;PHAGS-PA LETTER SA;Lo;0;L;;;;;N;;;;;
+A85C;PHAGS-PA LETTER HA;Lo;0;L;;;;;N;;;;;
+A85D;PHAGS-PA LETTER A;Lo;0;L;;;;;N;;;;;
+A85E;PHAGS-PA LETTER I;Lo;0;L;;;;;N;;;;;
+A85F;PHAGS-PA LETTER U;Lo;0;L;;;;;N;;;;;
+A860;PHAGS-PA LETTER E;Lo;0;L;;;;;N;;;;;
+A861;PHAGS-PA LETTER O;Lo;0;L;;;;;N;;;;;
+A862;PHAGS-PA LETTER QA;Lo;0;L;;;;;N;;;;;
+A863;PHAGS-PA LETTER XA;Lo;0;L;;;;;N;;;;;
+A864;PHAGS-PA LETTER FA;Lo;0;L;;;;;N;;;;;
+A865;PHAGS-PA LETTER GGA;Lo;0;L;;;;;N;;;;;
+A866;PHAGS-PA LETTER EE;Lo;0;L;;;;;N;;;;;
+A867;PHAGS-PA SUBJOINED LETTER WA;Lo;0;L;;;;;N;;;;;
+A868;PHAGS-PA SUBJOINED LETTER YA;Lo;0;L;;;;;N;;;;;
+A869;PHAGS-PA LETTER TTA;Lo;0;L;;;;;N;;;;;
+A86A;PHAGS-PA LETTER TTHA;Lo;0;L;;;;;N;;;;;
+A86B;PHAGS-PA LETTER DDA;Lo;0;L;;;;;N;;;;;
+A86C;PHAGS-PA LETTER NNA;Lo;0;L;;;;;N;;;;;
+A86D;PHAGS-PA LETTER ALTERNATE YA;Lo;0;L;;;;;N;;;;;
+A86E;PHAGS-PA LETTER VOICELESS SHA;Lo;0;L;;;;;N;;;;;
+A86F;PHAGS-PA LETTER VOICED HA;Lo;0;L;;;;;N;;;;;
+A870;PHAGS-PA LETTER ASPIRATED FA;Lo;0;L;;;;;N;;;;;
+A871;PHAGS-PA SUBJOINED LETTER RA;Lo;0;L;;;;;N;;;;;
+A872;PHAGS-PA SUPERFIXED LETTER RA;Lo;0;L;;;;;N;;;;;
+A873;PHAGS-PA LETTER CANDRABINDU;Lo;0;L;;;;;N;;;;;
+A874;PHAGS-PA SINGLE HEAD MARK;Po;0;ON;;;;;N;;;;;
+A875;PHAGS-PA DOUBLE HEAD MARK;Po;0;ON;;;;;N;;;;;
+A876;PHAGS-PA MARK SHAD;Po;0;ON;;;;;N;;;;;
+A877;PHAGS-PA MARK DOUBLE SHAD;Po;0;ON;;;;;N;;;;;
+A880;SAURASHTRA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+A881;SAURASHTRA SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+A882;SAURASHTRA LETTER A;Lo;0;L;;;;;N;;;;;
+A883;SAURASHTRA LETTER AA;Lo;0;L;;;;;N;;;;;
+A884;SAURASHTRA LETTER I;Lo;0;L;;;;;N;;;;;
+A885;SAURASHTRA LETTER II;Lo;0;L;;;;;N;;;;;
+A886;SAURASHTRA LETTER U;Lo;0;L;;;;;N;;;;;
+A887;SAURASHTRA LETTER UU;Lo;0;L;;;;;N;;;;;
+A888;SAURASHTRA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+A889;SAURASHTRA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+A88A;SAURASHTRA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+A88B;SAURASHTRA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+A88C;SAURASHTRA LETTER E;Lo;0;L;;;;;N;;;;;
+A88D;SAURASHTRA LETTER EE;Lo;0;L;;;;;N;;;;;
+A88E;SAURASHTRA LETTER AI;Lo;0;L;;;;;N;;;;;
+A88F;SAURASHTRA LETTER O;Lo;0;L;;;;;N;;;;;
+A890;SAURASHTRA LETTER OO;Lo;0;L;;;;;N;;;;;
+A891;SAURASHTRA LETTER AU;Lo;0;L;;;;;N;;;;;
+A892;SAURASHTRA LETTER KA;Lo;0;L;;;;;N;;;;;
+A893;SAURASHTRA LETTER KHA;Lo;0;L;;;;;N;;;;;
+A894;SAURASHTRA LETTER GA;Lo;0;L;;;;;N;;;;;
+A895;SAURASHTRA LETTER GHA;Lo;0;L;;;;;N;;;;;
+A896;SAURASHTRA LETTER NGA;Lo;0;L;;;;;N;;;;;
+A897;SAURASHTRA LETTER CA;Lo;0;L;;;;;N;;;;;
+A898;SAURASHTRA LETTER CHA;Lo;0;L;;;;;N;;;;;
+A899;SAURASHTRA LETTER JA;Lo;0;L;;;;;N;;;;;
+A89A;SAURASHTRA LETTER JHA;Lo;0;L;;;;;N;;;;;
+A89B;SAURASHTRA LETTER NYA;Lo;0;L;;;;;N;;;;;
+A89C;SAURASHTRA LETTER TTA;Lo;0;L;;;;;N;;;;;
+A89D;SAURASHTRA LETTER TTHA;Lo;0;L;;;;;N;;;;;
+A89E;SAURASHTRA LETTER DDA;Lo;0;L;;;;;N;;;;;
+A89F;SAURASHTRA LETTER DDHA;Lo;0;L;;;;;N;;;;;
+A8A0;SAURASHTRA LETTER NNA;Lo;0;L;;;;;N;;;;;
+A8A1;SAURASHTRA LETTER TA;Lo;0;L;;;;;N;;;;;
+A8A2;SAURASHTRA LETTER THA;Lo;0;L;;;;;N;;;;;
+A8A3;SAURASHTRA LETTER DA;Lo;0;L;;;;;N;;;;;
+A8A4;SAURASHTRA LETTER DHA;Lo;0;L;;;;;N;;;;;
+A8A5;SAURASHTRA LETTER NA;Lo;0;L;;;;;N;;;;;
+A8A6;SAURASHTRA LETTER PA;Lo;0;L;;;;;N;;;;;
+A8A7;SAURASHTRA LETTER PHA;Lo;0;L;;;;;N;;;;;
+A8A8;SAURASHTRA LETTER BA;Lo;0;L;;;;;N;;;;;
+A8A9;SAURASHTRA LETTER BHA;Lo;0;L;;;;;N;;;;;
+A8AA;SAURASHTRA LETTER MA;Lo;0;L;;;;;N;;;;;
+A8AB;SAURASHTRA LETTER YA;Lo;0;L;;;;;N;;;;;
+A8AC;SAURASHTRA LETTER RA;Lo;0;L;;;;;N;;;;;
+A8AD;SAURASHTRA LETTER LA;Lo;0;L;;;;;N;;;;;
+A8AE;SAURASHTRA LETTER VA;Lo;0;L;;;;;N;;;;;
+A8AF;SAURASHTRA LETTER SHA;Lo;0;L;;;;;N;;;;;
+A8B0;SAURASHTRA LETTER SSA;Lo;0;L;;;;;N;;;;;
+A8B1;SAURASHTRA LETTER SA;Lo;0;L;;;;;N;;;;;
+A8B2;SAURASHTRA LETTER HA;Lo;0;L;;;;;N;;;;;
+A8B3;SAURASHTRA LETTER LLA;Lo;0;L;;;;;N;;;;;
+A8B4;SAURASHTRA CONSONANT SIGN HAARU;Mc;0;L;;;;;N;;;;;
+A8B5;SAURASHTRA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+A8B6;SAURASHTRA VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+A8B7;SAURASHTRA VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+A8B8;SAURASHTRA VOWEL SIGN U;Mc;0;L;;;;;N;;;;;
+A8B9;SAURASHTRA VOWEL SIGN UU;Mc;0;L;;;;;N;;;;;
+A8BA;SAURASHTRA VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;;
+A8BB;SAURASHTRA VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;;
+A8BC;SAURASHTRA VOWEL SIGN VOCALIC L;Mc;0;L;;;;;N;;;;;
+A8BD;SAURASHTRA VOWEL SIGN VOCALIC LL;Mc;0;L;;;;;N;;;;;
+A8BE;SAURASHTRA VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+A8BF;SAURASHTRA VOWEL SIGN EE;Mc;0;L;;;;;N;;;;;
+A8C0;SAURASHTRA VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+A8C1;SAURASHTRA VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+A8C2;SAURASHTRA VOWEL SIGN OO;Mc;0;L;;;;;N;;;;;
+A8C3;SAURASHTRA VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+A8C4;SAURASHTRA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+A8C5;SAURASHTRA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+A8CE;SAURASHTRA DANDA;Po;0;L;;;;;N;;;;;
+A8CF;SAURASHTRA DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+A8D0;SAURASHTRA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+A8D1;SAURASHTRA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+A8D2;SAURASHTRA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+A8D3;SAURASHTRA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+A8D4;SAURASHTRA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+A8D5;SAURASHTRA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+A8D6;SAURASHTRA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+A8D7;SAURASHTRA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+A8D8;SAURASHTRA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+A8D9;SAURASHTRA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+A8E0;COMBINING DEVANAGARI DIGIT ZERO;Mn;230;NSM;;;;;N;;;;;
+A8E1;COMBINING DEVANAGARI DIGIT ONE;Mn;230;NSM;;;;;N;;;;;
+A8E2;COMBINING DEVANAGARI DIGIT TWO;Mn;230;NSM;;;;;N;;;;;
+A8E3;COMBINING DEVANAGARI DIGIT THREE;Mn;230;NSM;;;;;N;;;;;
+A8E4;COMBINING DEVANAGARI DIGIT FOUR;Mn;230;NSM;;;;;N;;;;;
+A8E5;COMBINING DEVANAGARI DIGIT FIVE;Mn;230;NSM;;;;;N;;;;;
+A8E6;COMBINING DEVANAGARI DIGIT SIX;Mn;230;NSM;;;;;N;;;;;
+A8E7;COMBINING DEVANAGARI DIGIT SEVEN;Mn;230;NSM;;;;;N;;;;;
+A8E8;COMBINING DEVANAGARI DIGIT EIGHT;Mn;230;NSM;;;;;N;;;;;
+A8E9;COMBINING DEVANAGARI DIGIT NINE;Mn;230;NSM;;;;;N;;;;;
+A8EA;COMBINING DEVANAGARI LETTER A;Mn;230;NSM;;;;;N;;;;;
+A8EB;COMBINING DEVANAGARI LETTER U;Mn;230;NSM;;;;;N;;;;;
+A8EC;COMBINING DEVANAGARI LETTER KA;Mn;230;NSM;;;;;N;;;;;
+A8ED;COMBINING DEVANAGARI LETTER NA;Mn;230;NSM;;;;;N;;;;;
+A8EE;COMBINING DEVANAGARI LETTER PA;Mn;230;NSM;;;;;N;;;;;
+A8EF;COMBINING DEVANAGARI LETTER RA;Mn;230;NSM;;;;;N;;;;;
+A8F0;COMBINING DEVANAGARI LETTER VI;Mn;230;NSM;;;;;N;;;;;
+A8F1;COMBINING DEVANAGARI SIGN AVAGRAHA;Mn;230;NSM;;;;;N;;;;;
+A8F2;DEVANAGARI SIGN SPACING CANDRABINDU;Lo;0;L;;;;;N;;;;;
+A8F3;DEVANAGARI SIGN CANDRABINDU VIRAMA;Lo;0;L;;;;;N;;;;;
+A8F4;DEVANAGARI SIGN DOUBLE CANDRABINDU VIRAMA;Lo;0;L;;;;;N;;;;;
+A8F5;DEVANAGARI SIGN CANDRABINDU TWO;Lo;0;L;;;;;N;;;;;
+A8F6;DEVANAGARI SIGN CANDRABINDU THREE;Lo;0;L;;;;;N;;;;;
+A8F7;DEVANAGARI SIGN CANDRABINDU AVAGRAHA;Lo;0;L;;;;;N;;;;;
+A8F8;DEVANAGARI SIGN PUSHPIKA;Po;0;L;;;;;N;;;;;
+A8F9;DEVANAGARI GAP FILLER;Po;0;L;;;;;N;;;;;
+A8FA;DEVANAGARI CARET;Po;0;L;;;;;N;;;;;
+A8FB;DEVANAGARI HEADSTROKE;Lo;0;L;;;;;N;;;;;
+A8FC;DEVANAGARI SIGN SIDDHAM;Po;0;L;;;;;N;;;;;
+A8FD;DEVANAGARI JAIN OM;Lo;0;L;;;;;N;;;;;
+A8FE;DEVANAGARI LETTER AY;Lo;0;L;;;;;N;;;;;
+A8FF;DEVANAGARI VOWEL SIGN AY;Mn;0;NSM;;;;;N;;;;;
+A900;KAYAH LI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+A901;KAYAH LI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+A902;KAYAH LI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+A903;KAYAH LI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+A904;KAYAH LI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+A905;KAYAH LI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+A906;KAYAH LI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+A907;KAYAH LI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+A908;KAYAH LI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+A909;KAYAH LI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+A90A;KAYAH LI LETTER KA;Lo;0;L;;;;;N;;;;;
+A90B;KAYAH LI LETTER KHA;Lo;0;L;;;;;N;;;;;
+A90C;KAYAH LI LETTER GA;Lo;0;L;;;;;N;;;;;
+A90D;KAYAH LI LETTER NGA;Lo;0;L;;;;;N;;;;;
+A90E;KAYAH LI LETTER SA;Lo;0;L;;;;;N;;;;;
+A90F;KAYAH LI LETTER SHA;Lo;0;L;;;;;N;;;;;
+A910;KAYAH LI LETTER ZA;Lo;0;L;;;;;N;;;;;
+A911;KAYAH LI LETTER NYA;Lo;0;L;;;;;N;;;;;
+A912;KAYAH LI LETTER TA;Lo;0;L;;;;;N;;;;;
+A913;KAYAH LI LETTER HTA;Lo;0;L;;;;;N;;;;;
+A914;KAYAH LI LETTER NA;Lo;0;L;;;;;N;;;;;
+A915;KAYAH LI LETTER PA;Lo;0;L;;;;;N;;;;;
+A916;KAYAH LI LETTER PHA;Lo;0;L;;;;;N;;;;;
+A917;KAYAH LI LETTER MA;Lo;0;L;;;;;N;;;;;
+A918;KAYAH LI LETTER DA;Lo;0;L;;;;;N;;;;;
+A919;KAYAH LI LETTER BA;Lo;0;L;;;;;N;;;;;
+A91A;KAYAH LI LETTER RA;Lo;0;L;;;;;N;;;;;
+A91B;KAYAH LI LETTER YA;Lo;0;L;;;;;N;;;;;
+A91C;KAYAH LI LETTER LA;Lo;0;L;;;;;N;;;;;
+A91D;KAYAH LI LETTER WA;Lo;0;L;;;;;N;;;;;
+A91E;KAYAH LI LETTER THA;Lo;0;L;;;;;N;;;;;
+A91F;KAYAH LI LETTER HA;Lo;0;L;;;;;N;;;;;
+A920;KAYAH LI LETTER VA;Lo;0;L;;;;;N;;;;;
+A921;KAYAH LI LETTER CA;Lo;0;L;;;;;N;;;;;
+A922;KAYAH LI LETTER A;Lo;0;L;;;;;N;;;;;
+A923;KAYAH LI LETTER OE;Lo;0;L;;;;;N;;;;;
+A924;KAYAH LI LETTER I;Lo;0;L;;;;;N;;;;;
+A925;KAYAH LI LETTER OO;Lo;0;L;;;;;N;;;;;
+A926;KAYAH LI VOWEL UE;Mn;0;NSM;;;;;N;;;;;
+A927;KAYAH LI VOWEL E;Mn;0;NSM;;;;;N;;;;;
+A928;KAYAH LI VOWEL U;Mn;0;NSM;;;;;N;;;;;
+A929;KAYAH LI VOWEL EE;Mn;0;NSM;;;;;N;;;;;
+A92A;KAYAH LI VOWEL O;Mn;0;NSM;;;;;N;;;;;
+A92B;KAYAH LI TONE PLOPHU;Mn;220;NSM;;;;;N;;;;;
+A92C;KAYAH LI TONE CALYA;Mn;220;NSM;;;;;N;;;;;
+A92D;KAYAH LI TONE CALYA PLOPHU;Mn;220;NSM;;;;;N;;;;;
+A92E;KAYAH LI SIGN CWI;Po;0;L;;;;;N;;;;;
+A92F;KAYAH LI SIGN SHYA;Po;0;L;;;;;N;;;;;
+A930;REJANG LETTER KA;Lo;0;L;;;;;N;;;;;
+A931;REJANG LETTER GA;Lo;0;L;;;;;N;;;;;
+A932;REJANG LETTER NGA;Lo;0;L;;;;;N;;;;;
+A933;REJANG LETTER TA;Lo;0;L;;;;;N;;;;;
+A934;REJANG LETTER DA;Lo;0;L;;;;;N;;;;;
+A935;REJANG LETTER NA;Lo;0;L;;;;;N;;;;;
+A936;REJANG LETTER PA;Lo;0;L;;;;;N;;;;;
+A937;REJANG LETTER BA;Lo;0;L;;;;;N;;;;;
+A938;REJANG LETTER MA;Lo;0;L;;;;;N;;;;;
+A939;REJANG LETTER CA;Lo;0;L;;;;;N;;;;;
+A93A;REJANG LETTER JA;Lo;0;L;;;;;N;;;;;
+A93B;REJANG LETTER NYA;Lo;0;L;;;;;N;;;;;
+A93C;REJANG LETTER SA;Lo;0;L;;;;;N;;;;;
+A93D;REJANG LETTER RA;Lo;0;L;;;;;N;;;;;
+A93E;REJANG LETTER LA;Lo;0;L;;;;;N;;;;;
+A93F;REJANG LETTER YA;Lo;0;L;;;;;N;;;;;
+A940;REJANG LETTER WA;Lo;0;L;;;;;N;;;;;
+A941;REJANG LETTER HA;Lo;0;L;;;;;N;;;;;
+A942;REJANG LETTER MBA;Lo;0;L;;;;;N;;;;;
+A943;REJANG LETTER NGGA;Lo;0;L;;;;;N;;;;;
+A944;REJANG LETTER NDA;Lo;0;L;;;;;N;;;;;
+A945;REJANG LETTER NYJA;Lo;0;L;;;;;N;;;;;
+A946;REJANG LETTER A;Lo;0;L;;;;;N;;;;;
+A947;REJANG VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+A948;REJANG VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+A949;REJANG VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+A94A;REJANG VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+A94B;REJANG VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+A94C;REJANG VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+A94D;REJANG VOWEL SIGN EU;Mn;0;NSM;;;;;N;;;;;
+A94E;REJANG VOWEL SIGN EA;Mn;0;NSM;;;;;N;;;;;
+A94F;REJANG CONSONANT SIGN NG;Mn;0;NSM;;;;;N;;;;;
+A950;REJANG CONSONANT SIGN N;Mn;0;NSM;;;;;N;;;;;
+A951;REJANG CONSONANT SIGN R;Mn;0;NSM;;;;;N;;;;;
+A952;REJANG CONSONANT SIGN H;Mc;0;L;;;;;N;;;;;
+A953;REJANG VIRAMA;Mc;9;L;;;;;N;;;;;
+A95F;REJANG SECTION MARK;Po;0;L;;;;;N;;;;;
+A960;HANGUL CHOSEONG TIKEUT-MIEUM;Lo;0;L;;;;;N;;;;;
+A961;HANGUL CHOSEONG TIKEUT-PIEUP;Lo;0;L;;;;;N;;;;;
+A962;HANGUL CHOSEONG TIKEUT-SIOS;Lo;0;L;;;;;N;;;;;
+A963;HANGUL CHOSEONG TIKEUT-CIEUC;Lo;0;L;;;;;N;;;;;
+A964;HANGUL CHOSEONG RIEUL-KIYEOK;Lo;0;L;;;;;N;;;;;
+A965;HANGUL CHOSEONG RIEUL-SSANGKIYEOK;Lo;0;L;;;;;N;;;;;
+A966;HANGUL CHOSEONG RIEUL-TIKEUT;Lo;0;L;;;;;N;;;;;
+A967;HANGUL CHOSEONG RIEUL-SSANGTIKEUT;Lo;0;L;;;;;N;;;;;
+A968;HANGUL CHOSEONG RIEUL-MIEUM;Lo;0;L;;;;;N;;;;;
+A969;HANGUL CHOSEONG RIEUL-PIEUP;Lo;0;L;;;;;N;;;;;
+A96A;HANGUL CHOSEONG RIEUL-SSANGPIEUP;Lo;0;L;;;;;N;;;;;
+A96B;HANGUL CHOSEONG RIEUL-KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;;
+A96C;HANGUL CHOSEONG RIEUL-SIOS;Lo;0;L;;;;;N;;;;;
+A96D;HANGUL CHOSEONG RIEUL-CIEUC;Lo;0;L;;;;;N;;;;;
+A96E;HANGUL CHOSEONG RIEUL-KHIEUKH;Lo;0;L;;;;;N;;;;;
+A96F;HANGUL CHOSEONG MIEUM-KIYEOK;Lo;0;L;;;;;N;;;;;
+A970;HANGUL CHOSEONG MIEUM-TIKEUT;Lo;0;L;;;;;N;;;;;
+A971;HANGUL CHOSEONG MIEUM-SIOS;Lo;0;L;;;;;N;;;;;
+A972;HANGUL CHOSEONG PIEUP-SIOS-THIEUTH;Lo;0;L;;;;;N;;;;;
+A973;HANGUL CHOSEONG PIEUP-KHIEUKH;Lo;0;L;;;;;N;;;;;
+A974;HANGUL CHOSEONG PIEUP-HIEUH;Lo;0;L;;;;;N;;;;;
+A975;HANGUL CHOSEONG SSANGSIOS-PIEUP;Lo;0;L;;;;;N;;;;;
+A976;HANGUL CHOSEONG IEUNG-RIEUL;Lo;0;L;;;;;N;;;;;
+A977;HANGUL CHOSEONG IEUNG-HIEUH;Lo;0;L;;;;;N;;;;;
+A978;HANGUL CHOSEONG SSANGCIEUC-HIEUH;Lo;0;L;;;;;N;;;;;
+A979;HANGUL CHOSEONG SSANGTHIEUTH;Lo;0;L;;;;;N;;;;;
+A97A;HANGUL CHOSEONG PHIEUPH-HIEUH;Lo;0;L;;;;;N;;;;;
+A97B;HANGUL CHOSEONG HIEUH-SIOS;Lo;0;L;;;;;N;;;;;
+A97C;HANGUL CHOSEONG SSANGYEORINHIEUH;Lo;0;L;;;;;N;;;;;
+A980;JAVANESE SIGN PANYANGGA;Mn;0;NSM;;;;;N;;;;;
+A981;JAVANESE SIGN CECAK;Mn;0;NSM;;;;;N;;;;;
+A982;JAVANESE SIGN LAYAR;Mn;0;NSM;;;;;N;;;;;
+A983;JAVANESE SIGN WIGNYAN;Mc;0;L;;;;;N;;;;;
+A984;JAVANESE LETTER A;Lo;0;L;;;;;N;;;;;
+A985;JAVANESE LETTER I KAWI;Lo;0;L;;;;;N;;;;;
+A986;JAVANESE LETTER I;Lo;0;L;;;;;N;;;;;
+A987;JAVANESE LETTER II;Lo;0;L;;;;;N;;;;;
+A988;JAVANESE LETTER U;Lo;0;L;;;;;N;;;;;
+A989;JAVANESE LETTER PA CEREK;Lo;0;L;;;;;N;;;;;
+A98A;JAVANESE LETTER NGA LELET;Lo;0;L;;;;;N;;;;;
+A98B;JAVANESE LETTER NGA LELET RASWADI;Lo;0;L;;;;;N;;;;;
+A98C;JAVANESE LETTER E;Lo;0;L;;;;;N;;;;;
+A98D;JAVANESE LETTER AI;Lo;0;L;;;;;N;;;;;
+A98E;JAVANESE LETTER O;Lo;0;L;;;;;N;;;;;
+A98F;JAVANESE LETTER KA;Lo;0;L;;;;;N;;;;;
+A990;JAVANESE LETTER KA SASAK;Lo;0;L;;;;;N;;;;;
+A991;JAVANESE LETTER KA MURDA;Lo;0;L;;;;;N;;;;;
+A992;JAVANESE LETTER GA;Lo;0;L;;;;;N;;;;;
+A993;JAVANESE LETTER GA MURDA;Lo;0;L;;;;;N;;;;;
+A994;JAVANESE LETTER NGA;Lo;0;L;;;;;N;;;;;
+A995;JAVANESE LETTER CA;Lo;0;L;;;;;N;;;;;
+A996;JAVANESE LETTER CA MURDA;Lo;0;L;;;;;N;;;;;
+A997;JAVANESE LETTER JA;Lo;0;L;;;;;N;;;;;
+A998;JAVANESE LETTER NYA MURDA;Lo;0;L;;;;;N;;;;;
+A999;JAVANESE LETTER JA MAHAPRANA;Lo;0;L;;;;;N;;;;;
+A99A;JAVANESE LETTER NYA;Lo;0;L;;;;;N;;;;;
+A99B;JAVANESE LETTER TTA;Lo;0;L;;;;;N;;;;;
+A99C;JAVANESE LETTER TTA MAHAPRANA;Lo;0;L;;;;;N;;;;;
+A99D;JAVANESE LETTER DDA;Lo;0;L;;;;;N;;;;;
+A99E;JAVANESE LETTER DDA MAHAPRANA;Lo;0;L;;;;;N;;;;;
+A99F;JAVANESE LETTER NA MURDA;Lo;0;L;;;;;N;;;;;
+A9A0;JAVANESE LETTER TA;Lo;0;L;;;;;N;;;;;
+A9A1;JAVANESE LETTER TA MURDA;Lo;0;L;;;;;N;;;;;
+A9A2;JAVANESE LETTER DA;Lo;0;L;;;;;N;;;;;
+A9A3;JAVANESE LETTER DA MAHAPRANA;Lo;0;L;;;;;N;;;;;
+A9A4;JAVANESE LETTER NA;Lo;0;L;;;;;N;;;;;
+A9A5;JAVANESE LETTER PA;Lo;0;L;;;;;N;;;;;
+A9A6;JAVANESE LETTER PA MURDA;Lo;0;L;;;;;N;;;;;
+A9A7;JAVANESE LETTER BA;Lo;0;L;;;;;N;;;;;
+A9A8;JAVANESE LETTER BA MURDA;Lo;0;L;;;;;N;;;;;
+A9A9;JAVANESE LETTER MA;Lo;0;L;;;;;N;;;;;
+A9AA;JAVANESE LETTER YA;Lo;0;L;;;;;N;;;;;
+A9AB;JAVANESE LETTER RA;Lo;0;L;;;;;N;;;;;
+A9AC;JAVANESE LETTER RA AGUNG;Lo;0;L;;;;;N;;;;;
+A9AD;JAVANESE LETTER LA;Lo;0;L;;;;;N;;;;;
+A9AE;JAVANESE LETTER WA;Lo;0;L;;;;;N;;;;;
+A9AF;JAVANESE LETTER SA MURDA;Lo;0;L;;;;;N;;;;;
+A9B0;JAVANESE LETTER SA MAHAPRANA;Lo;0;L;;;;;N;;;;;
+A9B1;JAVANESE LETTER SA;Lo;0;L;;;;;N;;;;;
+A9B2;JAVANESE LETTER HA;Lo;0;L;;;;;N;;;;;
+A9B3;JAVANESE SIGN CECAK TELU;Mn;7;NSM;;;;;N;;;;;
+A9B4;JAVANESE VOWEL SIGN TARUNG;Mc;0;L;;;;;N;;;;;
+A9B5;JAVANESE VOWEL SIGN TOLONG;Mc;0;L;;;;;N;;;;;
+A9B6;JAVANESE VOWEL SIGN WULU;Mn;0;NSM;;;;;N;;;;;
+A9B7;JAVANESE VOWEL SIGN WULU MELIK;Mn;0;NSM;;;;;N;;;;;
+A9B8;JAVANESE VOWEL SIGN SUKU;Mn;0;NSM;;;;;N;;;;;
+A9B9;JAVANESE VOWEL SIGN SUKU MENDUT;Mn;0;NSM;;;;;N;;;;;
+A9BA;JAVANESE VOWEL SIGN TALING;Mc;0;L;;;;;N;;;;;
+A9BB;JAVANESE VOWEL SIGN DIRGA MURE;Mc;0;L;;;;;N;;;;;
+A9BC;JAVANESE VOWEL SIGN PEPET;Mn;0;NSM;;;;;N;;;;;
+A9BD;JAVANESE CONSONANT SIGN KERET;Mn;0;NSM;;;;;N;;;;;
+A9BE;JAVANESE CONSONANT SIGN PENGKAL;Mc;0;L;;;;;N;;;;;
+A9BF;JAVANESE CONSONANT SIGN CAKRA;Mc;0;L;;;;;N;;;;;
+A9C0;JAVANESE PANGKON;Mc;9;L;;;;;N;;;;;
+A9C1;JAVANESE LEFT RERENGGAN;Po;0;L;;;;;N;;;;;
+A9C2;JAVANESE RIGHT RERENGGAN;Po;0;L;;;;;N;;;;;
+A9C3;JAVANESE PADA ANDAP;Po;0;L;;;;;N;;;;;
+A9C4;JAVANESE PADA MADYA;Po;0;L;;;;;N;;;;;
+A9C5;JAVANESE PADA LUHUR;Po;0;L;;;;;N;;;;;
+A9C6;JAVANESE PADA WINDU;Po;0;L;;;;;N;;;;;
+A9C7;JAVANESE PADA PANGKAT;Po;0;L;;;;;N;;;;;
+A9C8;JAVANESE PADA LINGSA;Po;0;L;;;;;N;;;;;
+A9C9;JAVANESE PADA LUNGSI;Po;0;L;;;;;N;;;;;
+A9CA;JAVANESE PADA ADEG;Po;0;L;;;;;N;;;;;
+A9CB;JAVANESE PADA ADEG ADEG;Po;0;L;;;;;N;;;;;
+A9CC;JAVANESE PADA PISELEH;Po;0;L;;;;;N;;;;;
+A9CD;JAVANESE TURNED PADA PISELEH;Po;0;L;;;;;N;;;;;
+A9CF;JAVANESE PANGRANGKEP;Lm;0;L;;;;;N;;;;;
+A9D0;JAVANESE DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+A9D1;JAVANESE DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+A9D2;JAVANESE DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+A9D3;JAVANESE DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+A9D4;JAVANESE DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+A9D5;JAVANESE DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+A9D6;JAVANESE DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+A9D7;JAVANESE DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+A9D8;JAVANESE DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+A9D9;JAVANESE DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+A9DE;JAVANESE PADA TIRTA TUMETES;Po;0;L;;;;;N;;;;;
+A9DF;JAVANESE PADA ISEN-ISEN;Po;0;L;;;;;N;;;;;
+A9E0;MYANMAR LETTER SHAN GHA;Lo;0;L;;;;;N;;;;;
+A9E1;MYANMAR LETTER SHAN CHA;Lo;0;L;;;;;N;;;;;
+A9E2;MYANMAR LETTER SHAN JHA;Lo;0;L;;;;;N;;;;;
+A9E3;MYANMAR LETTER SHAN NNA;Lo;0;L;;;;;N;;;;;
+A9E4;MYANMAR LETTER SHAN BHA;Lo;0;L;;;;;N;;;;;
+A9E5;MYANMAR SIGN SHAN SAW;Mn;0;NSM;;;;;N;;;;;
+A9E6;MYANMAR MODIFIER LETTER SHAN REDUPLICATION;Lm;0;L;;;;;N;;;;;
+A9E7;MYANMAR LETTER TAI LAING NYA;Lo;0;L;;;;;N;;;;;
+A9E8;MYANMAR LETTER TAI LAING FA;Lo;0;L;;;;;N;;;;;
+A9E9;MYANMAR LETTER TAI LAING GA;Lo;0;L;;;;;N;;;;;
+A9EA;MYANMAR LETTER TAI LAING GHA;Lo;0;L;;;;;N;;;;;
+A9EB;MYANMAR LETTER TAI LAING JA;Lo;0;L;;;;;N;;;;;
+A9EC;MYANMAR LETTER TAI LAING JHA;Lo;0;L;;;;;N;;;;;
+A9ED;MYANMAR LETTER TAI LAING DDA;Lo;0;L;;;;;N;;;;;
+A9EE;MYANMAR LETTER TAI LAING DDHA;Lo;0;L;;;;;N;;;;;
+A9EF;MYANMAR LETTER TAI LAING NNA;Lo;0;L;;;;;N;;;;;
+A9F0;MYANMAR TAI LAING DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+A9F1;MYANMAR TAI LAING DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+A9F2;MYANMAR TAI LAING DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+A9F3;MYANMAR TAI LAING DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+A9F4;MYANMAR TAI LAING DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+A9F5;MYANMAR TAI LAING DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+A9F6;MYANMAR TAI LAING DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+A9F7;MYANMAR TAI LAING DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+A9F8;MYANMAR TAI LAING DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+A9F9;MYANMAR TAI LAING DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+A9FA;MYANMAR LETTER TAI LAING LLA;Lo;0;L;;;;;N;;;;;
+A9FB;MYANMAR LETTER TAI LAING DA;Lo;0;L;;;;;N;;;;;
+A9FC;MYANMAR LETTER TAI LAING DHA;Lo;0;L;;;;;N;;;;;
+A9FD;MYANMAR LETTER TAI LAING BA;Lo;0;L;;;;;N;;;;;
+A9FE;MYANMAR LETTER TAI LAING BHA;Lo;0;L;;;;;N;;;;;
+AA00;CHAM LETTER A;Lo;0;L;;;;;N;;;;;
+AA01;CHAM LETTER I;Lo;0;L;;;;;N;;;;;
+AA02;CHAM LETTER U;Lo;0;L;;;;;N;;;;;
+AA03;CHAM LETTER E;Lo;0;L;;;;;N;;;;;
+AA04;CHAM LETTER AI;Lo;0;L;;;;;N;;;;;
+AA05;CHAM LETTER O;Lo;0;L;;;;;N;;;;;
+AA06;CHAM LETTER KA;Lo;0;L;;;;;N;;;;;
+AA07;CHAM LETTER KHA;Lo;0;L;;;;;N;;;;;
+AA08;CHAM LETTER GA;Lo;0;L;;;;;N;;;;;
+AA09;CHAM LETTER GHA;Lo;0;L;;;;;N;;;;;
+AA0A;CHAM LETTER NGUE;Lo;0;L;;;;;N;;;;;
+AA0B;CHAM LETTER NGA;Lo;0;L;;;;;N;;;;;
+AA0C;CHAM LETTER CHA;Lo;0;L;;;;;N;;;;;
+AA0D;CHAM LETTER CHHA;Lo;0;L;;;;;N;;;;;
+AA0E;CHAM LETTER JA;Lo;0;L;;;;;N;;;;;
+AA0F;CHAM LETTER JHA;Lo;0;L;;;;;N;;;;;
+AA10;CHAM LETTER NHUE;Lo;0;L;;;;;N;;;;;
+AA11;CHAM LETTER NHA;Lo;0;L;;;;;N;;;;;
+AA12;CHAM LETTER NHJA;Lo;0;L;;;;;N;;;;;
+AA13;CHAM LETTER TA;Lo;0;L;;;;;N;;;;;
+AA14;CHAM LETTER THA;Lo;0;L;;;;;N;;;;;
+AA15;CHAM LETTER DA;Lo;0;L;;;;;N;;;;;
+AA16;CHAM LETTER DHA;Lo;0;L;;;;;N;;;;;
+AA17;CHAM LETTER NUE;Lo;0;L;;;;;N;;;;;
+AA18;CHAM LETTER NA;Lo;0;L;;;;;N;;;;;
+AA19;CHAM LETTER DDA;Lo;0;L;;;;;N;;;;;
+AA1A;CHAM LETTER PA;Lo;0;L;;;;;N;;;;;
+AA1B;CHAM LETTER PPA;Lo;0;L;;;;;N;;;;;
+AA1C;CHAM LETTER PHA;Lo;0;L;;;;;N;;;;;
+AA1D;CHAM LETTER BA;Lo;0;L;;;;;N;;;;;
+AA1E;CHAM LETTER BHA;Lo;0;L;;;;;N;;;;;
+AA1F;CHAM LETTER MUE;Lo;0;L;;;;;N;;;;;
+AA20;CHAM LETTER MA;Lo;0;L;;;;;N;;;;;
+AA21;CHAM LETTER BBA;Lo;0;L;;;;;N;;;;;
+AA22;CHAM LETTER YA;Lo;0;L;;;;;N;;;;;
+AA23;CHAM LETTER RA;Lo;0;L;;;;;N;;;;;
+AA24;CHAM LETTER LA;Lo;0;L;;;;;N;;;;;
+AA25;CHAM LETTER VA;Lo;0;L;;;;;N;;;;;
+AA26;CHAM LETTER SSA;Lo;0;L;;;;;N;;;;;
+AA27;CHAM LETTER SA;Lo;0;L;;;;;N;;;;;
+AA28;CHAM LETTER HA;Lo;0;L;;;;;N;;;;;
+AA29;CHAM VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;;
+AA2A;CHAM VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+AA2B;CHAM VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+AA2C;CHAM VOWEL SIGN EI;Mn;0;NSM;;;;;N;;;;;
+AA2D;CHAM VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+AA2E;CHAM VOWEL SIGN OE;Mn;0;NSM;;;;;N;;;;;
+AA2F;CHAM VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+AA30;CHAM VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+AA31;CHAM VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+AA32;CHAM VOWEL SIGN UE;Mn;0;NSM;;;;;N;;;;;
+AA33;CHAM CONSONANT SIGN YA;Mc;0;L;;;;;N;;;;;
+AA34;CHAM CONSONANT SIGN RA;Mc;0;L;;;;;N;;;;;
+AA35;CHAM CONSONANT SIGN LA;Mn;0;NSM;;;;;N;;;;;
+AA36;CHAM CONSONANT SIGN WA;Mn;0;NSM;;;;;N;;;;;
+AA40;CHAM LETTER FINAL K;Lo;0;L;;;;;N;;;;;
+AA41;CHAM LETTER FINAL G;Lo;0;L;;;;;N;;;;;
+AA42;CHAM LETTER FINAL NG;Lo;0;L;;;;;N;;;;;
+AA43;CHAM CONSONANT SIGN FINAL NG;Mn;0;NSM;;;;;N;;;;;
+AA44;CHAM LETTER FINAL CH;Lo;0;L;;;;;N;;;;;
+AA45;CHAM LETTER FINAL T;Lo;0;L;;;;;N;;;;;
+AA46;CHAM LETTER FINAL N;Lo;0;L;;;;;N;;;;;
+AA47;CHAM LETTER FINAL P;Lo;0;L;;;;;N;;;;;
+AA48;CHAM LETTER FINAL Y;Lo;0;L;;;;;N;;;;;
+AA49;CHAM LETTER FINAL R;Lo;0;L;;;;;N;;;;;
+AA4A;CHAM LETTER FINAL L;Lo;0;L;;;;;N;;;;;
+AA4B;CHAM LETTER FINAL SS;Lo;0;L;;;;;N;;;;;
+AA4C;CHAM CONSONANT SIGN FINAL M;Mn;0;NSM;;;;;N;;;;;
+AA4D;CHAM CONSONANT SIGN FINAL H;Mc;0;L;;;;;N;;;;;
+AA50;CHAM DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+AA51;CHAM DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+AA52;CHAM DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+AA53;CHAM DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+AA54;CHAM DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+AA55;CHAM DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+AA56;CHAM DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+AA57;CHAM DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+AA58;CHAM DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+AA59;CHAM DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+AA5C;CHAM PUNCTUATION SPIRAL;Po;0;L;;;;;N;;;;;
+AA5D;CHAM PUNCTUATION DANDA;Po;0;L;;;;;N;;;;;
+AA5E;CHAM PUNCTUATION DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+AA5F;CHAM PUNCTUATION TRIPLE DANDA;Po;0;L;;;;;N;;;;;
+AA60;MYANMAR LETTER KHAMTI GA;Lo;0;L;;;;;N;;;;;
+AA61;MYANMAR LETTER KHAMTI CA;Lo;0;L;;;;;N;;;;;
+AA62;MYANMAR LETTER KHAMTI CHA;Lo;0;L;;;;;N;;;;;
+AA63;MYANMAR LETTER KHAMTI JA;Lo;0;L;;;;;N;;;;;
+AA64;MYANMAR LETTER KHAMTI JHA;Lo;0;L;;;;;N;;;;;
+AA65;MYANMAR LETTER KHAMTI NYA;Lo;0;L;;;;;N;;;;;
+AA66;MYANMAR LETTER KHAMTI TTA;Lo;0;L;;;;;N;;;;;
+AA67;MYANMAR LETTER KHAMTI TTHA;Lo;0;L;;;;;N;;;;;
+AA68;MYANMAR LETTER KHAMTI DDA;Lo;0;L;;;;;N;;;;;
+AA69;MYANMAR LETTER KHAMTI DDHA;Lo;0;L;;;;;N;;;;;
+AA6A;MYANMAR LETTER KHAMTI DHA;Lo;0;L;;;;;N;;;;;
+AA6B;MYANMAR LETTER KHAMTI NA;Lo;0;L;;;;;N;;;;;
+AA6C;MYANMAR LETTER KHAMTI SA;Lo;0;L;;;;;N;;;;;
+AA6D;MYANMAR LETTER KHAMTI HA;Lo;0;L;;;;;N;;;;;
+AA6E;MYANMAR LETTER KHAMTI HHA;Lo;0;L;;;;;N;;;;;
+AA6F;MYANMAR LETTER KHAMTI FA;Lo;0;L;;;;;N;;;;;
+AA70;MYANMAR MODIFIER LETTER KHAMTI REDUPLICATION;Lm;0;L;;;;;N;;;;;
+AA71;MYANMAR LETTER KHAMTI XA;Lo;0;L;;;;;N;;;;;
+AA72;MYANMAR LETTER KHAMTI ZA;Lo;0;L;;;;;N;;;;;
+AA73;MYANMAR LETTER KHAMTI RA;Lo;0;L;;;;;N;;;;;
+AA74;MYANMAR LOGOGRAM KHAMTI OAY;Lo;0;L;;;;;N;;;;;
+AA75;MYANMAR LOGOGRAM KHAMTI QN;Lo;0;L;;;;;N;;;;;
+AA76;MYANMAR LOGOGRAM KHAMTI HM;Lo;0;L;;;;;N;;;;;
+AA77;MYANMAR SYMBOL AITON EXCLAMATION;So;0;L;;;;;N;;;;;
+AA78;MYANMAR SYMBOL AITON ONE;So;0;L;;;;;N;;;;;
+AA79;MYANMAR SYMBOL AITON TWO;So;0;L;;;;;N;;;;;
+AA7A;MYANMAR LETTER AITON RA;Lo;0;L;;;;;N;;;;;
+AA7B;MYANMAR SIGN PAO KAREN TONE;Mc;0;L;;;;;N;;;;;
+AA7C;MYANMAR SIGN TAI LAING TONE-2;Mn;0;NSM;;;;;N;;;;;
+AA7D;MYANMAR SIGN TAI LAING TONE-5;Mc;0;L;;;;;N;;;;;
+AA7E;MYANMAR LETTER SHWE PALAUNG CHA;Lo;0;L;;;;;N;;;;;
+AA7F;MYANMAR LETTER SHWE PALAUNG SHA;Lo;0;L;;;;;N;;;;;
+AA80;TAI VIET LETTER LOW KO;Lo;0;L;;;;;N;;;;;
+AA81;TAI VIET LETTER HIGH KO;Lo;0;L;;;;;N;;;;;
+AA82;TAI VIET LETTER LOW KHO;Lo;0;L;;;;;N;;;;;
+AA83;TAI VIET LETTER HIGH KHO;Lo;0;L;;;;;N;;;;;
+AA84;TAI VIET LETTER LOW KHHO;Lo;0;L;;;;;N;;;;;
+AA85;TAI VIET LETTER HIGH KHHO;Lo;0;L;;;;;N;;;;;
+AA86;TAI VIET LETTER LOW GO;Lo;0;L;;;;;N;;;;;
+AA87;TAI VIET LETTER HIGH GO;Lo;0;L;;;;;N;;;;;
+AA88;TAI VIET LETTER LOW NGO;Lo;0;L;;;;;N;;;;;
+AA89;TAI VIET LETTER HIGH NGO;Lo;0;L;;;;;N;;;;;
+AA8A;TAI VIET LETTER LOW CO;Lo;0;L;;;;;N;;;;;
+AA8B;TAI VIET LETTER HIGH CO;Lo;0;L;;;;;N;;;;;
+AA8C;TAI VIET LETTER LOW CHO;Lo;0;L;;;;;N;;;;;
+AA8D;TAI VIET LETTER HIGH CHO;Lo;0;L;;;;;N;;;;;
+AA8E;TAI VIET LETTER LOW SO;Lo;0;L;;;;;N;;;;;
+AA8F;TAI VIET LETTER HIGH SO;Lo;0;L;;;;;N;;;;;
+AA90;TAI VIET LETTER LOW NYO;Lo;0;L;;;;;N;;;;;
+AA91;TAI VIET LETTER HIGH NYO;Lo;0;L;;;;;N;;;;;
+AA92;TAI VIET LETTER LOW DO;Lo;0;L;;;;;N;;;;;
+AA93;TAI VIET LETTER HIGH DO;Lo;0;L;;;;;N;;;;;
+AA94;TAI VIET LETTER LOW TO;Lo;0;L;;;;;N;;;;;
+AA95;TAI VIET LETTER HIGH TO;Lo;0;L;;;;;N;;;;;
+AA96;TAI VIET LETTER LOW THO;Lo;0;L;;;;;N;;;;;
+AA97;TAI VIET LETTER HIGH THO;Lo;0;L;;;;;N;;;;;
+AA98;TAI VIET LETTER LOW NO;Lo;0;L;;;;;N;;;;;
+AA99;TAI VIET LETTER HIGH NO;Lo;0;L;;;;;N;;;;;
+AA9A;TAI VIET LETTER LOW BO;Lo;0;L;;;;;N;;;;;
+AA9B;TAI VIET LETTER HIGH BO;Lo;0;L;;;;;N;;;;;
+AA9C;TAI VIET LETTER LOW PO;Lo;0;L;;;;;N;;;;;
+AA9D;TAI VIET LETTER HIGH PO;Lo;0;L;;;;;N;;;;;
+AA9E;TAI VIET LETTER LOW PHO;Lo;0;L;;;;;N;;;;;
+AA9F;TAI VIET LETTER HIGH PHO;Lo;0;L;;;;;N;;;;;
+AAA0;TAI VIET LETTER LOW FO;Lo;0;L;;;;;N;;;;;
+AAA1;TAI VIET LETTER HIGH FO;Lo;0;L;;;;;N;;;;;
+AAA2;TAI VIET LETTER LOW MO;Lo;0;L;;;;;N;;;;;
+AAA3;TAI VIET LETTER HIGH MO;Lo;0;L;;;;;N;;;;;
+AAA4;TAI VIET LETTER LOW YO;Lo;0;L;;;;;N;;;;;
+AAA5;TAI VIET LETTER HIGH YO;Lo;0;L;;;;;N;;;;;
+AAA6;TAI VIET LETTER LOW RO;Lo;0;L;;;;;N;;;;;
+AAA7;TAI VIET LETTER HIGH RO;Lo;0;L;;;;;N;;;;;
+AAA8;TAI VIET LETTER LOW LO;Lo;0;L;;;;;N;;;;;
+AAA9;TAI VIET LETTER HIGH LO;Lo;0;L;;;;;N;;;;;
+AAAA;TAI VIET LETTER LOW VO;Lo;0;L;;;;;N;;;;;
+AAAB;TAI VIET LETTER HIGH VO;Lo;0;L;;;;;N;;;;;
+AAAC;TAI VIET LETTER LOW HO;Lo;0;L;;;;;N;;;;;
+AAAD;TAI VIET LETTER HIGH HO;Lo;0;L;;;;;N;;;;;
+AAAE;TAI VIET LETTER LOW O;Lo;0;L;;;;;N;;;;;
+AAAF;TAI VIET LETTER HIGH O;Lo;0;L;;;;;N;;;;;
+AAB0;TAI VIET MAI KANG;Mn;230;NSM;;;;;N;;;;;
+AAB1;TAI VIET VOWEL AA;Lo;0;L;;;;;N;;;;;
+AAB2;TAI VIET VOWEL I;Mn;230;NSM;;;;;N;;;;;
+AAB3;TAI VIET VOWEL UE;Mn;230;NSM;;;;;N;;;;;
+AAB4;TAI VIET VOWEL U;Mn;220;NSM;;;;;N;;;;;
+AAB5;TAI VIET VOWEL E;Lo;0;L;;;;;N;;;;;
+AAB6;TAI VIET VOWEL O;Lo;0;L;;;;;N;;;;;
+AAB7;TAI VIET MAI KHIT;Mn;230;NSM;;;;;N;;;;;
+AAB8;TAI VIET VOWEL IA;Mn;230;NSM;;;;;N;;;;;
+AAB9;TAI VIET VOWEL UEA;Lo;0;L;;;;;N;;;;;
+AABA;TAI VIET VOWEL UA;Lo;0;L;;;;;N;;;;;
+AABB;TAI VIET VOWEL AUE;Lo;0;L;;;;;N;;;;;
+AABC;TAI VIET VOWEL AY;Lo;0;L;;;;;N;;;;;
+AABD;TAI VIET VOWEL AN;Lo;0;L;;;;;N;;;;;
+AABE;TAI VIET VOWEL AM;Mn;230;NSM;;;;;N;;;;;
+AABF;TAI VIET TONE MAI EK;Mn;230;NSM;;;;;N;;;;;
+AAC0;TAI VIET TONE MAI NUENG;Lo;0;L;;;;;N;;;;;
+AAC1;TAI VIET TONE MAI THO;Mn;230;NSM;;;;;N;;;;;
+AAC2;TAI VIET TONE MAI SONG;Lo;0;L;;;;;N;;;;;
+AADB;TAI VIET SYMBOL KON;Lo;0;L;;;;;N;;;;;
+AADC;TAI VIET SYMBOL NUENG;Lo;0;L;;;;;N;;;;;
+AADD;TAI VIET SYMBOL SAM;Lm;0;L;;;;;N;;;;;
+AADE;TAI VIET SYMBOL HO HOI;Po;0;L;;;;;N;;;;;
+AADF;TAI VIET SYMBOL KOI KOI;Po;0;L;;;;;N;;;;;
+AAE0;MEETEI MAYEK LETTER E;Lo;0;L;;;;;N;;;;;
+AAE1;MEETEI MAYEK LETTER O;Lo;0;L;;;;;N;;;;;
+AAE2;MEETEI MAYEK LETTER CHA;Lo;0;L;;;;;N;;;;;
+AAE3;MEETEI MAYEK LETTER NYA;Lo;0;L;;;;;N;;;;;
+AAE4;MEETEI MAYEK LETTER TTA;Lo;0;L;;;;;N;;;;;
+AAE5;MEETEI MAYEK LETTER TTHA;Lo;0;L;;;;;N;;;;;
+AAE6;MEETEI MAYEK LETTER DDA;Lo;0;L;;;;;N;;;;;
+AAE7;MEETEI MAYEK LETTER DDHA;Lo;0;L;;;;;N;;;;;
+AAE8;MEETEI MAYEK LETTER NNA;Lo;0;L;;;;;N;;;;;
+AAE9;MEETEI MAYEK LETTER SHA;Lo;0;L;;;;;N;;;;;
+AAEA;MEETEI MAYEK LETTER SSA;Lo;0;L;;;;;N;;;;;
+AAEB;MEETEI MAYEK VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+AAEC;MEETEI MAYEK VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+AAED;MEETEI MAYEK VOWEL SIGN AAI;Mn;0;NSM;;;;;N;;;;;
+AAEE;MEETEI MAYEK VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+AAEF;MEETEI MAYEK VOWEL SIGN AAU;Mc;0;L;;;;;N;;;;;
+AAF0;MEETEI MAYEK CHEIKHAN;Po;0;L;;;;;N;;;;;
+AAF1;MEETEI MAYEK AHANG KHUDAM;Po;0;L;;;;;N;;;;;
+AAF2;MEETEI MAYEK ANJI;Lo;0;L;;;;;N;;;;;
+AAF3;MEETEI MAYEK SYLLABLE REPETITION MARK;Lm;0;L;;;;;N;;;;;
+AAF4;MEETEI MAYEK WORD REPETITION MARK;Lm;0;L;;;;;N;;;;;
+AAF5;MEETEI MAYEK VOWEL SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+AAF6;MEETEI MAYEK VIRAMA;Mn;9;NSM;;;;;N;;;;;
+AB01;ETHIOPIC SYLLABLE TTHU;Lo;0;L;;;;;N;;;;;
+AB02;ETHIOPIC SYLLABLE TTHI;Lo;0;L;;;;;N;;;;;
+AB03;ETHIOPIC SYLLABLE TTHAA;Lo;0;L;;;;;N;;;;;
+AB04;ETHIOPIC SYLLABLE TTHEE;Lo;0;L;;;;;N;;;;;
+AB05;ETHIOPIC SYLLABLE TTHE;Lo;0;L;;;;;N;;;;;
+AB06;ETHIOPIC SYLLABLE TTHO;Lo;0;L;;;;;N;;;;;
+AB09;ETHIOPIC SYLLABLE DDHU;Lo;0;L;;;;;N;;;;;
+AB0A;ETHIOPIC SYLLABLE DDHI;Lo;0;L;;;;;N;;;;;
+AB0B;ETHIOPIC SYLLABLE DDHAA;Lo;0;L;;;;;N;;;;;
+AB0C;ETHIOPIC SYLLABLE DDHEE;Lo;0;L;;;;;N;;;;;
+AB0D;ETHIOPIC SYLLABLE DDHE;Lo;0;L;;;;;N;;;;;
+AB0E;ETHIOPIC SYLLABLE DDHO;Lo;0;L;;;;;N;;;;;
+AB11;ETHIOPIC SYLLABLE DZU;Lo;0;L;;;;;N;;;;;
+AB12;ETHIOPIC SYLLABLE DZI;Lo;0;L;;;;;N;;;;;
+AB13;ETHIOPIC SYLLABLE DZAA;Lo;0;L;;;;;N;;;;;
+AB14;ETHIOPIC SYLLABLE DZEE;Lo;0;L;;;;;N;;;;;
+AB15;ETHIOPIC SYLLABLE DZE;Lo;0;L;;;;;N;;;;;
+AB16;ETHIOPIC SYLLABLE DZO;Lo;0;L;;;;;N;;;;;
+AB20;ETHIOPIC SYLLABLE CCHHA;Lo;0;L;;;;;N;;;;;
+AB21;ETHIOPIC SYLLABLE CCHHU;Lo;0;L;;;;;N;;;;;
+AB22;ETHIOPIC SYLLABLE CCHHI;Lo;0;L;;;;;N;;;;;
+AB23;ETHIOPIC SYLLABLE CCHHAA;Lo;0;L;;;;;N;;;;;
+AB24;ETHIOPIC SYLLABLE CCHHEE;Lo;0;L;;;;;N;;;;;
+AB25;ETHIOPIC SYLLABLE CCHHE;Lo;0;L;;;;;N;;;;;
+AB26;ETHIOPIC SYLLABLE CCHHO;Lo;0;L;;;;;N;;;;;
+AB28;ETHIOPIC SYLLABLE BBA;Lo;0;L;;;;;N;;;;;
+AB29;ETHIOPIC SYLLABLE BBU;Lo;0;L;;;;;N;;;;;
+AB2A;ETHIOPIC SYLLABLE BBI;Lo;0;L;;;;;N;;;;;
+AB2B;ETHIOPIC SYLLABLE BBAA;Lo;0;L;;;;;N;;;;;
+AB2C;ETHIOPIC SYLLABLE BBEE;Lo;0;L;;;;;N;;;;;
+AB2D;ETHIOPIC SYLLABLE BBE;Lo;0;L;;;;;N;;;;;
+AB2E;ETHIOPIC SYLLABLE BBO;Lo;0;L;;;;;N;;;;;
+AB30;LATIN SMALL LETTER BARRED ALPHA;Ll;0;L;;;;;N;;;;;
+AB31;LATIN SMALL LETTER A REVERSED-SCHWA;Ll;0;L;;;;;N;;;;;
+AB32;LATIN SMALL LETTER BLACKLETTER E;Ll;0;L;;;;;N;;;;;
+AB33;LATIN SMALL LETTER BARRED E;Ll;0;L;;;;;N;;;;;
+AB34;LATIN SMALL LETTER E WITH FLOURISH;Ll;0;L;;;;;N;;;;;
+AB35;LATIN SMALL LETTER LENIS F;Ll;0;L;;;;;N;;;;;
+AB36;LATIN SMALL LETTER SCRIPT G WITH CROSSED-TAIL;Ll;0;L;;;;;N;;;;;
+AB37;LATIN SMALL LETTER L WITH INVERTED LAZY S;Ll;0;L;;;;;N;;;;;
+AB38;LATIN SMALL LETTER L WITH DOUBLE MIDDLE TILDE;Ll;0;L;;;;;N;;;;;
+AB39;LATIN SMALL LETTER L WITH MIDDLE RING;Ll;0;L;;;;;N;;;;;
+AB3A;LATIN SMALL LETTER M WITH CROSSED-TAIL;Ll;0;L;;;;;N;;;;;
+AB3B;LATIN SMALL LETTER N WITH CROSSED-TAIL;Ll;0;L;;;;;N;;;;;
+AB3C;LATIN SMALL LETTER ENG WITH CROSSED-TAIL;Ll;0;L;;;;;N;;;;;
+AB3D;LATIN SMALL LETTER BLACKLETTER O;Ll;0;L;;;;;N;;;;;
+AB3E;LATIN SMALL LETTER BLACKLETTER O WITH STROKE;Ll;0;L;;;;;N;;;;;
+AB3F;LATIN SMALL LETTER OPEN O WITH STROKE;Ll;0;L;;;;;N;;;;;
+AB40;LATIN SMALL LETTER INVERTED OE;Ll;0;L;;;;;N;;;;;
+AB41;LATIN SMALL LETTER TURNED OE WITH STROKE;Ll;0;L;;;;;N;;;;;
+AB42;LATIN SMALL LETTER TURNED OE WITH HORIZONTAL STROKE;Ll;0;L;;;;;N;;;;;
+AB43;LATIN SMALL LETTER TURNED O OPEN-O;Ll;0;L;;;;;N;;;;;
+AB44;LATIN SMALL LETTER TURNED O OPEN-O WITH STROKE;Ll;0;L;;;;;N;;;;;
+AB45;LATIN SMALL LETTER STIRRUP R;Ll;0;L;;;;;N;;;;;
+AB46;LATIN LETTER SMALL CAPITAL R WITH RIGHT LEG;Ll;0;L;;;;;N;;;;;
+AB47;LATIN SMALL LETTER R WITHOUT HANDLE;Ll;0;L;;;;;N;;;;;
+AB48;LATIN SMALL LETTER DOUBLE R;Ll;0;L;;;;;N;;;;;
+AB49;LATIN SMALL LETTER R WITH CROSSED-TAIL;Ll;0;L;;;;;N;;;;;
+AB4A;LATIN SMALL LETTER DOUBLE R WITH CROSSED-TAIL;Ll;0;L;;;;;N;;;;;
+AB4B;LATIN SMALL LETTER SCRIPT R;Ll;0;L;;;;;N;;;;;
+AB4C;LATIN SMALL LETTER SCRIPT R WITH RING;Ll;0;L;;;;;N;;;;;
+AB4D;LATIN SMALL LETTER BASELINE ESH;Ll;0;L;;;;;N;;;;;
+AB4E;LATIN SMALL LETTER U WITH SHORT RIGHT LEG;Ll;0;L;;;;;N;;;;;
+AB4F;LATIN SMALL LETTER U BAR WITH SHORT RIGHT LEG;Ll;0;L;;;;;N;;;;;
+AB50;LATIN SMALL LETTER UI;Ll;0;L;;;;;N;;;;;
+AB51;LATIN SMALL LETTER TURNED UI;Ll;0;L;;;;;N;;;;;
+AB52;LATIN SMALL LETTER U WITH LEFT HOOK;Ll;0;L;;;;;N;;;;;
+AB53;LATIN SMALL LETTER CHI;Ll;0;L;;;;;N;;;A7B3;;A7B3
+AB54;LATIN SMALL LETTER CHI WITH LOW RIGHT RING;Ll;0;L;;;;;N;;;;;
+AB55;LATIN SMALL LETTER CHI WITH LOW LEFT SERIF;Ll;0;L;;;;;N;;;;;
+AB56;LATIN SMALL LETTER X WITH LOW RIGHT RING;Ll;0;L;;;;;N;;;;;
+AB57;LATIN SMALL LETTER X WITH LONG LEFT LEG;Ll;0;L;;;;;N;;;;;
+AB58;LATIN SMALL LETTER X WITH LONG LEFT LEG AND LOW RIGHT RING;Ll;0;L;;;;;N;;;;;
+AB59;LATIN SMALL LETTER X WITH LONG LEFT LEG WITH SERIF;Ll;0;L;;;;;N;;;;;
+AB5A;LATIN SMALL LETTER Y WITH SHORT RIGHT LEG;Ll;0;L;;;;;N;;;;;
+AB5B;MODIFIER BREVE WITH INVERTED BREVE;Sk;0;L;;;;;N;;;;;
+AB5C;MODIFIER LETTER SMALL HENG;Lm;0;L;<super> A727;;;;N;;;;;
+AB5D;MODIFIER LETTER SMALL L WITH INVERTED LAZY S;Lm;0;L;<super> AB37;;;;N;;;;;
+AB5E;MODIFIER LETTER SMALL L WITH MIDDLE TILDE;Lm;0;L;<super> 026B;;;;N;;;;;
+AB5F;MODIFIER LETTER SMALL U WITH LEFT HOOK;Lm;0;L;<super> AB52;;;;N;;;;;
+AB60;LATIN SMALL LETTER SAKHA YAT;Ll;0;L;;;;;N;;;;;
+AB61;LATIN SMALL LETTER IOTIFIED E;Ll;0;L;;;;;N;;;;;
+AB62;LATIN SMALL LETTER OPEN OE;Ll;0;L;;;;;N;;;;;
+AB63;LATIN SMALL LETTER UO;Ll;0;L;;;;;N;;;;;
+AB64;LATIN SMALL LETTER INVERTED ALPHA;Ll;0;L;;;;;N;;;;;
+AB65;GREEK LETTER SMALL CAPITAL OMEGA;Ll;0;L;;;;;N;;;;;
+AB66;LATIN SMALL LETTER DZ DIGRAPH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+AB67;LATIN SMALL LETTER TS DIGRAPH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+AB70;CHEROKEE SMALL LETTER A;Ll;0;L;;;;;N;;;13A0;;13A0
+AB71;CHEROKEE SMALL LETTER E;Ll;0;L;;;;;N;;;13A1;;13A1
+AB72;CHEROKEE SMALL LETTER I;Ll;0;L;;;;;N;;;13A2;;13A2
+AB73;CHEROKEE SMALL LETTER O;Ll;0;L;;;;;N;;;13A3;;13A3
+AB74;CHEROKEE SMALL LETTER U;Ll;0;L;;;;;N;;;13A4;;13A4
+AB75;CHEROKEE SMALL LETTER V;Ll;0;L;;;;;N;;;13A5;;13A5
+AB76;CHEROKEE SMALL LETTER GA;Ll;0;L;;;;;N;;;13A6;;13A6
+AB77;CHEROKEE SMALL LETTER KA;Ll;0;L;;;;;N;;;13A7;;13A7
+AB78;CHEROKEE SMALL LETTER GE;Ll;0;L;;;;;N;;;13A8;;13A8
+AB79;CHEROKEE SMALL LETTER GI;Ll;0;L;;;;;N;;;13A9;;13A9
+AB7A;CHEROKEE SMALL LETTER GO;Ll;0;L;;;;;N;;;13AA;;13AA
+AB7B;CHEROKEE SMALL LETTER GU;Ll;0;L;;;;;N;;;13AB;;13AB
+AB7C;CHEROKEE SMALL LETTER GV;Ll;0;L;;;;;N;;;13AC;;13AC
+AB7D;CHEROKEE SMALL LETTER HA;Ll;0;L;;;;;N;;;13AD;;13AD
+AB7E;CHEROKEE SMALL LETTER HE;Ll;0;L;;;;;N;;;13AE;;13AE
+AB7F;CHEROKEE SMALL LETTER HI;Ll;0;L;;;;;N;;;13AF;;13AF
+AB80;CHEROKEE SMALL LETTER HO;Ll;0;L;;;;;N;;;13B0;;13B0
+AB81;CHEROKEE SMALL LETTER HU;Ll;0;L;;;;;N;;;13B1;;13B1
+AB82;CHEROKEE SMALL LETTER HV;Ll;0;L;;;;;N;;;13B2;;13B2
+AB83;CHEROKEE SMALL LETTER LA;Ll;0;L;;;;;N;;;13B3;;13B3
+AB84;CHEROKEE SMALL LETTER LE;Ll;0;L;;;;;N;;;13B4;;13B4
+AB85;CHEROKEE SMALL LETTER LI;Ll;0;L;;;;;N;;;13B5;;13B5
+AB86;CHEROKEE SMALL LETTER LO;Ll;0;L;;;;;N;;;13B6;;13B6
+AB87;CHEROKEE SMALL LETTER LU;Ll;0;L;;;;;N;;;13B7;;13B7
+AB88;CHEROKEE SMALL LETTER LV;Ll;0;L;;;;;N;;;13B8;;13B8
+AB89;CHEROKEE SMALL LETTER MA;Ll;0;L;;;;;N;;;13B9;;13B9
+AB8A;CHEROKEE SMALL LETTER ME;Ll;0;L;;;;;N;;;13BA;;13BA
+AB8B;CHEROKEE SMALL LETTER MI;Ll;0;L;;;;;N;;;13BB;;13BB
+AB8C;CHEROKEE SMALL LETTER MO;Ll;0;L;;;;;N;;;13BC;;13BC
+AB8D;CHEROKEE SMALL LETTER MU;Ll;0;L;;;;;N;;;13BD;;13BD
+AB8E;CHEROKEE SMALL LETTER NA;Ll;0;L;;;;;N;;;13BE;;13BE
+AB8F;CHEROKEE SMALL LETTER HNA;Ll;0;L;;;;;N;;;13BF;;13BF
+AB90;CHEROKEE SMALL LETTER NAH;Ll;0;L;;;;;N;;;13C0;;13C0
+AB91;CHEROKEE SMALL LETTER NE;Ll;0;L;;;;;N;;;13C1;;13C1
+AB92;CHEROKEE SMALL LETTER NI;Ll;0;L;;;;;N;;;13C2;;13C2
+AB93;CHEROKEE SMALL LETTER NO;Ll;0;L;;;;;N;;;13C3;;13C3
+AB94;CHEROKEE SMALL LETTER NU;Ll;0;L;;;;;N;;;13C4;;13C4
+AB95;CHEROKEE SMALL LETTER NV;Ll;0;L;;;;;N;;;13C5;;13C5
+AB96;CHEROKEE SMALL LETTER QUA;Ll;0;L;;;;;N;;;13C6;;13C6
+AB97;CHEROKEE SMALL LETTER QUE;Ll;0;L;;;;;N;;;13C7;;13C7
+AB98;CHEROKEE SMALL LETTER QUI;Ll;0;L;;;;;N;;;13C8;;13C8
+AB99;CHEROKEE SMALL LETTER QUO;Ll;0;L;;;;;N;;;13C9;;13C9
+AB9A;CHEROKEE SMALL LETTER QUU;Ll;0;L;;;;;N;;;13CA;;13CA
+AB9B;CHEROKEE SMALL LETTER QUV;Ll;0;L;;;;;N;;;13CB;;13CB
+AB9C;CHEROKEE SMALL LETTER SA;Ll;0;L;;;;;N;;;13CC;;13CC
+AB9D;CHEROKEE SMALL LETTER S;Ll;0;L;;;;;N;;;13CD;;13CD
+AB9E;CHEROKEE SMALL LETTER SE;Ll;0;L;;;;;N;;;13CE;;13CE
+AB9F;CHEROKEE SMALL LETTER SI;Ll;0;L;;;;;N;;;13CF;;13CF
+ABA0;CHEROKEE SMALL LETTER SO;Ll;0;L;;;;;N;;;13D0;;13D0
+ABA1;CHEROKEE SMALL LETTER SU;Ll;0;L;;;;;N;;;13D1;;13D1
+ABA2;CHEROKEE SMALL LETTER SV;Ll;0;L;;;;;N;;;13D2;;13D2
+ABA3;CHEROKEE SMALL LETTER DA;Ll;0;L;;;;;N;;;13D3;;13D3
+ABA4;CHEROKEE SMALL LETTER TA;Ll;0;L;;;;;N;;;13D4;;13D4
+ABA5;CHEROKEE SMALL LETTER DE;Ll;0;L;;;;;N;;;13D5;;13D5
+ABA6;CHEROKEE SMALL LETTER TE;Ll;0;L;;;;;N;;;13D6;;13D6
+ABA7;CHEROKEE SMALL LETTER DI;Ll;0;L;;;;;N;;;13D7;;13D7
+ABA8;CHEROKEE SMALL LETTER TI;Ll;0;L;;;;;N;;;13D8;;13D8
+ABA9;CHEROKEE SMALL LETTER DO;Ll;0;L;;;;;N;;;13D9;;13D9
+ABAA;CHEROKEE SMALL LETTER DU;Ll;0;L;;;;;N;;;13DA;;13DA
+ABAB;CHEROKEE SMALL LETTER DV;Ll;0;L;;;;;N;;;13DB;;13DB
+ABAC;CHEROKEE SMALL LETTER DLA;Ll;0;L;;;;;N;;;13DC;;13DC
+ABAD;CHEROKEE SMALL LETTER TLA;Ll;0;L;;;;;N;;;13DD;;13DD
+ABAE;CHEROKEE SMALL LETTER TLE;Ll;0;L;;;;;N;;;13DE;;13DE
+ABAF;CHEROKEE SMALL LETTER TLI;Ll;0;L;;;;;N;;;13DF;;13DF
+ABB0;CHEROKEE SMALL LETTER TLO;Ll;0;L;;;;;N;;;13E0;;13E0
+ABB1;CHEROKEE SMALL LETTER TLU;Ll;0;L;;;;;N;;;13E1;;13E1
+ABB2;CHEROKEE SMALL LETTER TLV;Ll;0;L;;;;;N;;;13E2;;13E2
+ABB3;CHEROKEE SMALL LETTER TSA;Ll;0;L;;;;;N;;;13E3;;13E3
+ABB4;CHEROKEE SMALL LETTER TSE;Ll;0;L;;;;;N;;;13E4;;13E4
+ABB5;CHEROKEE SMALL LETTER TSI;Ll;0;L;;;;;N;;;13E5;;13E5
+ABB6;CHEROKEE SMALL LETTER TSO;Ll;0;L;;;;;N;;;13E6;;13E6
+ABB7;CHEROKEE SMALL LETTER TSU;Ll;0;L;;;;;N;;;13E7;;13E7
+ABB8;CHEROKEE SMALL LETTER TSV;Ll;0;L;;;;;N;;;13E8;;13E8
+ABB9;CHEROKEE SMALL LETTER WA;Ll;0;L;;;;;N;;;13E9;;13E9
+ABBA;CHEROKEE SMALL LETTER WE;Ll;0;L;;;;;N;;;13EA;;13EA
+ABBB;CHEROKEE SMALL LETTER WI;Ll;0;L;;;;;N;;;13EB;;13EB
+ABBC;CHEROKEE SMALL LETTER WO;Ll;0;L;;;;;N;;;13EC;;13EC
+ABBD;CHEROKEE SMALL LETTER WU;Ll;0;L;;;;;N;;;13ED;;13ED
+ABBE;CHEROKEE SMALL LETTER WV;Ll;0;L;;;;;N;;;13EE;;13EE
+ABBF;CHEROKEE SMALL LETTER YA;Ll;0;L;;;;;N;;;13EF;;13EF
+ABC0;MEETEI MAYEK LETTER KOK;Lo;0;L;;;;;N;;;;;
+ABC1;MEETEI MAYEK LETTER SAM;Lo;0;L;;;;;N;;;;;
+ABC2;MEETEI MAYEK LETTER LAI;Lo;0;L;;;;;N;;;;;
+ABC3;MEETEI MAYEK LETTER MIT;Lo;0;L;;;;;N;;;;;
+ABC4;MEETEI MAYEK LETTER PA;Lo;0;L;;;;;N;;;;;
+ABC5;MEETEI MAYEK LETTER NA;Lo;0;L;;;;;N;;;;;
+ABC6;MEETEI MAYEK LETTER CHIL;Lo;0;L;;;;;N;;;;;
+ABC7;MEETEI MAYEK LETTER TIL;Lo;0;L;;;;;N;;;;;
+ABC8;MEETEI MAYEK LETTER KHOU;Lo;0;L;;;;;N;;;;;
+ABC9;MEETEI MAYEK LETTER NGOU;Lo;0;L;;;;;N;;;;;
+ABCA;MEETEI MAYEK LETTER THOU;Lo;0;L;;;;;N;;;;;
+ABCB;MEETEI MAYEK LETTER WAI;Lo;0;L;;;;;N;;;;;
+ABCC;MEETEI MAYEK LETTER YANG;Lo;0;L;;;;;N;;;;;
+ABCD;MEETEI MAYEK LETTER HUK;Lo;0;L;;;;;N;;;;;
+ABCE;MEETEI MAYEK LETTER UN;Lo;0;L;;;;;N;;;;;
+ABCF;MEETEI MAYEK LETTER I;Lo;0;L;;;;;N;;;;;
+ABD0;MEETEI MAYEK LETTER PHAM;Lo;0;L;;;;;N;;;;;
+ABD1;MEETEI MAYEK LETTER ATIYA;Lo;0;L;;;;;N;;;;;
+ABD2;MEETEI MAYEK LETTER GOK;Lo;0;L;;;;;N;;;;;
+ABD3;MEETEI MAYEK LETTER JHAM;Lo;0;L;;;;;N;;;;;
+ABD4;MEETEI MAYEK LETTER RAI;Lo;0;L;;;;;N;;;;;
+ABD5;MEETEI MAYEK LETTER BA;Lo;0;L;;;;;N;;;;;
+ABD6;MEETEI MAYEK LETTER JIL;Lo;0;L;;;;;N;;;;;
+ABD7;MEETEI MAYEK LETTER DIL;Lo;0;L;;;;;N;;;;;
+ABD8;MEETEI MAYEK LETTER GHOU;Lo;0;L;;;;;N;;;;;
+ABD9;MEETEI MAYEK LETTER DHOU;Lo;0;L;;;;;N;;;;;
+ABDA;MEETEI MAYEK LETTER BHAM;Lo;0;L;;;;;N;;;;;
+ABDB;MEETEI MAYEK LETTER KOK LONSUM;Lo;0;L;;;;;N;;;;;
+ABDC;MEETEI MAYEK LETTER LAI LONSUM;Lo;0;L;;;;;N;;;;;
+ABDD;MEETEI MAYEK LETTER MIT LONSUM;Lo;0;L;;;;;N;;;;;
+ABDE;MEETEI MAYEK LETTER PA LONSUM;Lo;0;L;;;;;N;;;;;
+ABDF;MEETEI MAYEK LETTER NA LONSUM;Lo;0;L;;;;;N;;;;;
+ABE0;MEETEI MAYEK LETTER TIL LONSUM;Lo;0;L;;;;;N;;;;;
+ABE1;MEETEI MAYEK LETTER NGOU LONSUM;Lo;0;L;;;;;N;;;;;
+ABE2;MEETEI MAYEK LETTER I LONSUM;Lo;0;L;;;;;N;;;;;
+ABE3;MEETEI MAYEK VOWEL SIGN ONAP;Mc;0;L;;;;;N;;;;;
+ABE4;MEETEI MAYEK VOWEL SIGN INAP;Mc;0;L;;;;;N;;;;;
+ABE5;MEETEI MAYEK VOWEL SIGN ANAP;Mn;0;NSM;;;;;N;;;;;
+ABE6;MEETEI MAYEK VOWEL SIGN YENAP;Mc;0;L;;;;;N;;;;;
+ABE7;MEETEI MAYEK VOWEL SIGN SOUNAP;Mc;0;L;;;;;N;;;;;
+ABE8;MEETEI MAYEK VOWEL SIGN UNAP;Mn;0;NSM;;;;;N;;;;;
+ABE9;MEETEI MAYEK VOWEL SIGN CHEINAP;Mc;0;L;;;;;N;;;;;
+ABEA;MEETEI MAYEK VOWEL SIGN NUNG;Mc;0;L;;;;;N;;;;;
+ABEB;MEETEI MAYEK CHEIKHEI;Po;0;L;;;;;N;;;;;
+ABEC;MEETEI MAYEK LUM IYEK;Mc;0;L;;;;;N;;;;;
+ABED;MEETEI MAYEK APUN IYEK;Mn;9;NSM;;;;;N;;;;;
+ABF0;MEETEI MAYEK DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+ABF1;MEETEI MAYEK DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+ABF2;MEETEI MAYEK DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+ABF3;MEETEI MAYEK DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+ABF4;MEETEI MAYEK DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+ABF5;MEETEI MAYEK DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+ABF6;MEETEI MAYEK DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+ABF7;MEETEI MAYEK DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+ABF8;MEETEI MAYEK DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+ABF9;MEETEI MAYEK DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+AC00;<Hangul Syllable, First>;Lo;0;L;;;;;N;;;;;
+D7A3;<Hangul Syllable, Last>;Lo;0;L;;;;;N;;;;;
+D7B0;HANGUL JUNGSEONG O-YEO;Lo;0;L;;;;;N;;;;;
+D7B1;HANGUL JUNGSEONG O-O-I;Lo;0;L;;;;;N;;;;;
+D7B2;HANGUL JUNGSEONG YO-A;Lo;0;L;;;;;N;;;;;
+D7B3;HANGUL JUNGSEONG YO-AE;Lo;0;L;;;;;N;;;;;
+D7B4;HANGUL JUNGSEONG YO-EO;Lo;0;L;;;;;N;;;;;
+D7B5;HANGUL JUNGSEONG U-YEO;Lo;0;L;;;;;N;;;;;
+D7B6;HANGUL JUNGSEONG U-I-I;Lo;0;L;;;;;N;;;;;
+D7B7;HANGUL JUNGSEONG YU-AE;Lo;0;L;;;;;N;;;;;
+D7B8;HANGUL JUNGSEONG YU-O;Lo;0;L;;;;;N;;;;;
+D7B9;HANGUL JUNGSEONG EU-A;Lo;0;L;;;;;N;;;;;
+D7BA;HANGUL JUNGSEONG EU-EO;Lo;0;L;;;;;N;;;;;
+D7BB;HANGUL JUNGSEONG EU-E;Lo;0;L;;;;;N;;;;;
+D7BC;HANGUL JUNGSEONG EU-O;Lo;0;L;;;;;N;;;;;
+D7BD;HANGUL JUNGSEONG I-YA-O;Lo;0;L;;;;;N;;;;;
+D7BE;HANGUL JUNGSEONG I-YAE;Lo;0;L;;;;;N;;;;;
+D7BF;HANGUL JUNGSEONG I-YEO;Lo;0;L;;;;;N;;;;;
+D7C0;HANGUL JUNGSEONG I-YE;Lo;0;L;;;;;N;;;;;
+D7C1;HANGUL JUNGSEONG I-O-I;Lo;0;L;;;;;N;;;;;
+D7C2;HANGUL JUNGSEONG I-YO;Lo;0;L;;;;;N;;;;;
+D7C3;HANGUL JUNGSEONG I-YU;Lo;0;L;;;;;N;;;;;
+D7C4;HANGUL JUNGSEONG I-I;Lo;0;L;;;;;N;;;;;
+D7C5;HANGUL JUNGSEONG ARAEA-A;Lo;0;L;;;;;N;;;;;
+D7C6;HANGUL JUNGSEONG ARAEA-E;Lo;0;L;;;;;N;;;;;
+D7CB;HANGUL JONGSEONG NIEUN-RIEUL;Lo;0;L;;;;;N;;;;;
+D7CC;HANGUL JONGSEONG NIEUN-CHIEUCH;Lo;0;L;;;;;N;;;;;
+D7CD;HANGUL JONGSEONG SSANGTIKEUT;Lo;0;L;;;;;N;;;;;
+D7CE;HANGUL JONGSEONG SSANGTIKEUT-PIEUP;Lo;0;L;;;;;N;;;;;
+D7CF;HANGUL JONGSEONG TIKEUT-PIEUP;Lo;0;L;;;;;N;;;;;
+D7D0;HANGUL JONGSEONG TIKEUT-SIOS;Lo;0;L;;;;;N;;;;;
+D7D1;HANGUL JONGSEONG TIKEUT-SIOS-KIYEOK;Lo;0;L;;;;;N;;;;;
+D7D2;HANGUL JONGSEONG TIKEUT-CIEUC;Lo;0;L;;;;;N;;;;;
+D7D3;HANGUL JONGSEONG TIKEUT-CHIEUCH;Lo;0;L;;;;;N;;;;;
+D7D4;HANGUL JONGSEONG TIKEUT-THIEUTH;Lo;0;L;;;;;N;;;;;
+D7D5;HANGUL JONGSEONG RIEUL-SSANGKIYEOK;Lo;0;L;;;;;N;;;;;
+D7D6;HANGUL JONGSEONG RIEUL-KIYEOK-HIEUH;Lo;0;L;;;;;N;;;;;
+D7D7;HANGUL JONGSEONG SSANGRIEUL-KHIEUKH;Lo;0;L;;;;;N;;;;;
+D7D8;HANGUL JONGSEONG RIEUL-MIEUM-HIEUH;Lo;0;L;;;;;N;;;;;
+D7D9;HANGUL JONGSEONG RIEUL-PIEUP-TIKEUT;Lo;0;L;;;;;N;;;;;
+D7DA;HANGUL JONGSEONG RIEUL-PIEUP-PHIEUPH;Lo;0;L;;;;;N;;;;;
+D7DB;HANGUL JONGSEONG RIEUL-YESIEUNG;Lo;0;L;;;;;N;;;;;
+D7DC;HANGUL JONGSEONG RIEUL-YEORINHIEUH-HIEUH;Lo;0;L;;;;;N;;;;;
+D7DD;HANGUL JONGSEONG KAPYEOUNRIEUL;Lo;0;L;;;;;N;;;;;
+D7DE;HANGUL JONGSEONG MIEUM-NIEUN;Lo;0;L;;;;;N;;;;;
+D7DF;HANGUL JONGSEONG MIEUM-SSANGNIEUN;Lo;0;L;;;;;N;;;;;
+D7E0;HANGUL JONGSEONG SSANGMIEUM;Lo;0;L;;;;;N;;;;;
+D7E1;HANGUL JONGSEONG MIEUM-PIEUP-SIOS;Lo;0;L;;;;;N;;;;;
+D7E2;HANGUL JONGSEONG MIEUM-CIEUC;Lo;0;L;;;;;N;;;;;
+D7E3;HANGUL JONGSEONG PIEUP-TIKEUT;Lo;0;L;;;;;N;;;;;
+D7E4;HANGUL JONGSEONG PIEUP-RIEUL-PHIEUPH;Lo;0;L;;;;;N;;;;;
+D7E5;HANGUL JONGSEONG PIEUP-MIEUM;Lo;0;L;;;;;N;;;;;
+D7E6;HANGUL JONGSEONG SSANGPIEUP;Lo;0;L;;;;;N;;;;;
+D7E7;HANGUL JONGSEONG PIEUP-SIOS-TIKEUT;Lo;0;L;;;;;N;;;;;
+D7E8;HANGUL JONGSEONG PIEUP-CIEUC;Lo;0;L;;;;;N;;;;;
+D7E9;HANGUL JONGSEONG PIEUP-CHIEUCH;Lo;0;L;;;;;N;;;;;
+D7EA;HANGUL JONGSEONG SIOS-MIEUM;Lo;0;L;;;;;N;;;;;
+D7EB;HANGUL JONGSEONG SIOS-KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;;
+D7EC;HANGUL JONGSEONG SSANGSIOS-KIYEOK;Lo;0;L;;;;;N;;;;;
+D7ED;HANGUL JONGSEONG SSANGSIOS-TIKEUT;Lo;0;L;;;;;N;;;;;
+D7EE;HANGUL JONGSEONG SIOS-PANSIOS;Lo;0;L;;;;;N;;;;;
+D7EF;HANGUL JONGSEONG SIOS-CIEUC;Lo;0;L;;;;;N;;;;;
+D7F0;HANGUL JONGSEONG SIOS-CHIEUCH;Lo;0;L;;;;;N;;;;;
+D7F1;HANGUL JONGSEONG SIOS-THIEUTH;Lo;0;L;;;;;N;;;;;
+D7F2;HANGUL JONGSEONG SIOS-HIEUH;Lo;0;L;;;;;N;;;;;
+D7F3;HANGUL JONGSEONG PANSIOS-PIEUP;Lo;0;L;;;;;N;;;;;
+D7F4;HANGUL JONGSEONG PANSIOS-KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;;
+D7F5;HANGUL JONGSEONG YESIEUNG-MIEUM;Lo;0;L;;;;;N;;;;;
+D7F6;HANGUL JONGSEONG YESIEUNG-HIEUH;Lo;0;L;;;;;N;;;;;
+D7F7;HANGUL JONGSEONG CIEUC-PIEUP;Lo;0;L;;;;;N;;;;;
+D7F8;HANGUL JONGSEONG CIEUC-SSANGPIEUP;Lo;0;L;;;;;N;;;;;
+D7F9;HANGUL JONGSEONG SSANGCIEUC;Lo;0;L;;;;;N;;;;;
+D7FA;HANGUL JONGSEONG PHIEUPH-SIOS;Lo;0;L;;;;;N;;;;;
+D7FB;HANGUL JONGSEONG PHIEUPH-THIEUTH;Lo;0;L;;;;;N;;;;;
+D800;<Non Private Use High Surrogate, First>;Cs;0;L;;;;;N;;;;;
+DB7F;<Non Private Use High Surrogate, Last>;Cs;0;L;;;;;N;;;;;
+DB80;<Private Use High Surrogate, First>;Cs;0;L;;;;;N;;;;;
+DBFF;<Private Use High Surrogate, Last>;Cs;0;L;;;;;N;;;;;
+DC00;<Low Surrogate, First>;Cs;0;L;;;;;N;;;;;
+DFFF;<Low Surrogate, Last>;Cs;0;L;;;;;N;;;;;
+E000;<Private Use, First>;Co;0;L;;;;;N;;;;;
+F8FF;<Private Use, Last>;Co;0;L;;;;;N;;;;;
+F900;CJK COMPATIBILITY IDEOGRAPH-F900;Lo;0;L;8C48;;;;N;;;;;
+F901;CJK COMPATIBILITY IDEOGRAPH-F901;Lo;0;L;66F4;;;;N;;;;;
+F902;CJK COMPATIBILITY IDEOGRAPH-F902;Lo;0;L;8ECA;;;;N;;;;;
+F903;CJK COMPATIBILITY IDEOGRAPH-F903;Lo;0;L;8CC8;;;;N;;;;;
+F904;CJK COMPATIBILITY IDEOGRAPH-F904;Lo;0;L;6ED1;;;;N;;;;;
+F905;CJK COMPATIBILITY IDEOGRAPH-F905;Lo;0;L;4E32;;;;N;;;;;
+F906;CJK COMPATIBILITY IDEOGRAPH-F906;Lo;0;L;53E5;;;;N;;;;;
+F907;CJK COMPATIBILITY IDEOGRAPH-F907;Lo;0;L;9F9C;;;;N;;;;;
+F908;CJK COMPATIBILITY IDEOGRAPH-F908;Lo;0;L;9F9C;;;;N;;;;;
+F909;CJK COMPATIBILITY IDEOGRAPH-F909;Lo;0;L;5951;;;;N;;;;;
+F90A;CJK COMPATIBILITY IDEOGRAPH-F90A;Lo;0;L;91D1;;;;N;;;;;
+F90B;CJK COMPATIBILITY IDEOGRAPH-F90B;Lo;0;L;5587;;;;N;;;;;
+F90C;CJK COMPATIBILITY IDEOGRAPH-F90C;Lo;0;L;5948;;;;N;;;;;
+F90D;CJK COMPATIBILITY IDEOGRAPH-F90D;Lo;0;L;61F6;;;;N;;;;;
+F90E;CJK COMPATIBILITY IDEOGRAPH-F90E;Lo;0;L;7669;;;;N;;;;;
+F90F;CJK COMPATIBILITY IDEOGRAPH-F90F;Lo;0;L;7F85;;;;N;;;;;
+F910;CJK COMPATIBILITY IDEOGRAPH-F910;Lo;0;L;863F;;;;N;;;;;
+F911;CJK COMPATIBILITY IDEOGRAPH-F911;Lo;0;L;87BA;;;;N;;;;;
+F912;CJK COMPATIBILITY IDEOGRAPH-F912;Lo;0;L;88F8;;;;N;;;;;
+F913;CJK COMPATIBILITY IDEOGRAPH-F913;Lo;0;L;908F;;;;N;;;;;
+F914;CJK COMPATIBILITY IDEOGRAPH-F914;Lo;0;L;6A02;;;;N;;;;;
+F915;CJK COMPATIBILITY IDEOGRAPH-F915;Lo;0;L;6D1B;;;;N;;;;;
+F916;CJK COMPATIBILITY IDEOGRAPH-F916;Lo;0;L;70D9;;;;N;;;;;
+F917;CJK COMPATIBILITY IDEOGRAPH-F917;Lo;0;L;73DE;;;;N;;;;;
+F918;CJK COMPATIBILITY IDEOGRAPH-F918;Lo;0;L;843D;;;;N;;;;;
+F919;CJK COMPATIBILITY IDEOGRAPH-F919;Lo;0;L;916A;;;;N;;;;;
+F91A;CJK COMPATIBILITY IDEOGRAPH-F91A;Lo;0;L;99F1;;;;N;;;;;
+F91B;CJK COMPATIBILITY IDEOGRAPH-F91B;Lo;0;L;4E82;;;;N;;;;;
+F91C;CJK COMPATIBILITY IDEOGRAPH-F91C;Lo;0;L;5375;;;;N;;;;;
+F91D;CJK COMPATIBILITY IDEOGRAPH-F91D;Lo;0;L;6B04;;;;N;;;;;
+F91E;CJK COMPATIBILITY IDEOGRAPH-F91E;Lo;0;L;721B;;;;N;;;;;
+F91F;CJK COMPATIBILITY IDEOGRAPH-F91F;Lo;0;L;862D;;;;N;;;;;
+F920;CJK COMPATIBILITY IDEOGRAPH-F920;Lo;0;L;9E1E;;;;N;;;;;
+F921;CJK COMPATIBILITY IDEOGRAPH-F921;Lo;0;L;5D50;;;;N;;;;;
+F922;CJK COMPATIBILITY IDEOGRAPH-F922;Lo;0;L;6FEB;;;;N;;;;;
+F923;CJK COMPATIBILITY IDEOGRAPH-F923;Lo;0;L;85CD;;;;N;;;;;
+F924;CJK COMPATIBILITY IDEOGRAPH-F924;Lo;0;L;8964;;;;N;;;;;
+F925;CJK COMPATIBILITY IDEOGRAPH-F925;Lo;0;L;62C9;;;;N;;;;;
+F926;CJK COMPATIBILITY IDEOGRAPH-F926;Lo;0;L;81D8;;;;N;;;;;
+F927;CJK COMPATIBILITY IDEOGRAPH-F927;Lo;0;L;881F;;;;N;;;;;
+F928;CJK COMPATIBILITY IDEOGRAPH-F928;Lo;0;L;5ECA;;;;N;;;;;
+F929;CJK COMPATIBILITY IDEOGRAPH-F929;Lo;0;L;6717;;;;N;;;;;
+F92A;CJK COMPATIBILITY IDEOGRAPH-F92A;Lo;0;L;6D6A;;;;N;;;;;
+F92B;CJK COMPATIBILITY IDEOGRAPH-F92B;Lo;0;L;72FC;;;;N;;;;;
+F92C;CJK COMPATIBILITY IDEOGRAPH-F92C;Lo;0;L;90CE;;;;N;;;;;
+F92D;CJK COMPATIBILITY IDEOGRAPH-F92D;Lo;0;L;4F86;;;;N;;;;;
+F92E;CJK COMPATIBILITY IDEOGRAPH-F92E;Lo;0;L;51B7;;;;N;;;;;
+F92F;CJK COMPATIBILITY IDEOGRAPH-F92F;Lo;0;L;52DE;;;;N;;;;;
+F930;CJK COMPATIBILITY IDEOGRAPH-F930;Lo;0;L;64C4;;;;N;;;;;
+F931;CJK COMPATIBILITY IDEOGRAPH-F931;Lo;0;L;6AD3;;;;N;;;;;
+F932;CJK COMPATIBILITY IDEOGRAPH-F932;Lo;0;L;7210;;;;N;;;;;
+F933;CJK COMPATIBILITY IDEOGRAPH-F933;Lo;0;L;76E7;;;;N;;;;;
+F934;CJK COMPATIBILITY IDEOGRAPH-F934;Lo;0;L;8001;;;;N;;;;;
+F935;CJK COMPATIBILITY IDEOGRAPH-F935;Lo;0;L;8606;;;;N;;;;;
+F936;CJK COMPATIBILITY IDEOGRAPH-F936;Lo;0;L;865C;;;;N;;;;;
+F937;CJK COMPATIBILITY IDEOGRAPH-F937;Lo;0;L;8DEF;;;;N;;;;;
+F938;CJK COMPATIBILITY IDEOGRAPH-F938;Lo;0;L;9732;;;;N;;;;;
+F939;CJK COMPATIBILITY IDEOGRAPH-F939;Lo;0;L;9B6F;;;;N;;;;;
+F93A;CJK COMPATIBILITY IDEOGRAPH-F93A;Lo;0;L;9DFA;;;;N;;;;;
+F93B;CJK COMPATIBILITY IDEOGRAPH-F93B;Lo;0;L;788C;;;;N;;;;;
+F93C;CJK COMPATIBILITY IDEOGRAPH-F93C;Lo;0;L;797F;;;;N;;;;;
+F93D;CJK COMPATIBILITY IDEOGRAPH-F93D;Lo;0;L;7DA0;;;;N;;;;;
+F93E;CJK COMPATIBILITY IDEOGRAPH-F93E;Lo;0;L;83C9;;;;N;;;;;
+F93F;CJK COMPATIBILITY IDEOGRAPH-F93F;Lo;0;L;9304;;;;N;;;;;
+F940;CJK COMPATIBILITY IDEOGRAPH-F940;Lo;0;L;9E7F;;;;N;;;;;
+F941;CJK COMPATIBILITY IDEOGRAPH-F941;Lo;0;L;8AD6;;;;N;;;;;
+F942;CJK COMPATIBILITY IDEOGRAPH-F942;Lo;0;L;58DF;;;;N;;;;;
+F943;CJK COMPATIBILITY IDEOGRAPH-F943;Lo;0;L;5F04;;;;N;;;;;
+F944;CJK COMPATIBILITY IDEOGRAPH-F944;Lo;0;L;7C60;;;;N;;;;;
+F945;CJK COMPATIBILITY IDEOGRAPH-F945;Lo;0;L;807E;;;;N;;;;;
+F946;CJK COMPATIBILITY IDEOGRAPH-F946;Lo;0;L;7262;;;;N;;;;;
+F947;CJK COMPATIBILITY IDEOGRAPH-F947;Lo;0;L;78CA;;;;N;;;;;
+F948;CJK COMPATIBILITY IDEOGRAPH-F948;Lo;0;L;8CC2;;;;N;;;;;
+F949;CJK COMPATIBILITY IDEOGRAPH-F949;Lo;0;L;96F7;;;;N;;;;;
+F94A;CJK COMPATIBILITY IDEOGRAPH-F94A;Lo;0;L;58D8;;;;N;;;;;
+F94B;CJK COMPATIBILITY IDEOGRAPH-F94B;Lo;0;L;5C62;;;;N;;;;;
+F94C;CJK COMPATIBILITY IDEOGRAPH-F94C;Lo;0;L;6A13;;;;N;;;;;
+F94D;CJK COMPATIBILITY IDEOGRAPH-F94D;Lo;0;L;6DDA;;;;N;;;;;
+F94E;CJK COMPATIBILITY IDEOGRAPH-F94E;Lo;0;L;6F0F;;;;N;;;;;
+F94F;CJK COMPATIBILITY IDEOGRAPH-F94F;Lo;0;L;7D2F;;;;N;;;;;
+F950;CJK COMPATIBILITY IDEOGRAPH-F950;Lo;0;L;7E37;;;;N;;;;;
+F951;CJK COMPATIBILITY IDEOGRAPH-F951;Lo;0;L;964B;;;;N;;;;;
+F952;CJK COMPATIBILITY IDEOGRAPH-F952;Lo;0;L;52D2;;;;N;;;;;
+F953;CJK COMPATIBILITY IDEOGRAPH-F953;Lo;0;L;808B;;;;N;;;;;
+F954;CJK COMPATIBILITY IDEOGRAPH-F954;Lo;0;L;51DC;;;;N;;;;;
+F955;CJK COMPATIBILITY IDEOGRAPH-F955;Lo;0;L;51CC;;;;N;;;;;
+F956;CJK COMPATIBILITY IDEOGRAPH-F956;Lo;0;L;7A1C;;;;N;;;;;
+F957;CJK COMPATIBILITY IDEOGRAPH-F957;Lo;0;L;7DBE;;;;N;;;;;
+F958;CJK COMPATIBILITY IDEOGRAPH-F958;Lo;0;L;83F1;;;;N;;;;;
+F959;CJK COMPATIBILITY IDEOGRAPH-F959;Lo;0;L;9675;;;;N;;;;;
+F95A;CJK COMPATIBILITY IDEOGRAPH-F95A;Lo;0;L;8B80;;;;N;;;;;
+F95B;CJK COMPATIBILITY IDEOGRAPH-F95B;Lo;0;L;62CF;;;;N;;;;;
+F95C;CJK COMPATIBILITY IDEOGRAPH-F95C;Lo;0;L;6A02;;;;N;;;;;
+F95D;CJK COMPATIBILITY IDEOGRAPH-F95D;Lo;0;L;8AFE;;;;N;;;;;
+F95E;CJK COMPATIBILITY IDEOGRAPH-F95E;Lo;0;L;4E39;;;;N;;;;;
+F95F;CJK COMPATIBILITY IDEOGRAPH-F95F;Lo;0;L;5BE7;;;;N;;;;;
+F960;CJK COMPATIBILITY IDEOGRAPH-F960;Lo;0;L;6012;;;;N;;;;;
+F961;CJK COMPATIBILITY IDEOGRAPH-F961;Lo;0;L;7387;;;;N;;;;;
+F962;CJK COMPATIBILITY IDEOGRAPH-F962;Lo;0;L;7570;;;;N;;;;;
+F963;CJK COMPATIBILITY IDEOGRAPH-F963;Lo;0;L;5317;;;;N;;;;;
+F964;CJK COMPATIBILITY IDEOGRAPH-F964;Lo;0;L;78FB;;;;N;;;;;
+F965;CJK COMPATIBILITY IDEOGRAPH-F965;Lo;0;L;4FBF;;;;N;;;;;
+F966;CJK COMPATIBILITY IDEOGRAPH-F966;Lo;0;L;5FA9;;;;N;;;;;
+F967;CJK COMPATIBILITY IDEOGRAPH-F967;Lo;0;L;4E0D;;;;N;;;;;
+F968;CJK COMPATIBILITY IDEOGRAPH-F968;Lo;0;L;6CCC;;;;N;;;;;
+F969;CJK COMPATIBILITY IDEOGRAPH-F969;Lo;0;L;6578;;;;N;;;;;
+F96A;CJK COMPATIBILITY IDEOGRAPH-F96A;Lo;0;L;7D22;;;;N;;;;;
+F96B;CJK COMPATIBILITY IDEOGRAPH-F96B;Lo;0;L;53C3;;;3;N;;;;;
+F96C;CJK COMPATIBILITY IDEOGRAPH-F96C;Lo;0;L;585E;;;;N;;;;;
+F96D;CJK COMPATIBILITY IDEOGRAPH-F96D;Lo;0;L;7701;;;;N;;;;;
+F96E;CJK COMPATIBILITY IDEOGRAPH-F96E;Lo;0;L;8449;;;;N;;;;;
+F96F;CJK COMPATIBILITY IDEOGRAPH-F96F;Lo;0;L;8AAA;;;;N;;;;;
+F970;CJK COMPATIBILITY IDEOGRAPH-F970;Lo;0;L;6BBA;;;;N;;;;;
+F971;CJK COMPATIBILITY IDEOGRAPH-F971;Lo;0;L;8FB0;;;;N;;;;;
+F972;CJK COMPATIBILITY IDEOGRAPH-F972;Lo;0;L;6C88;;;;N;;;;;
+F973;CJK COMPATIBILITY IDEOGRAPH-F973;Lo;0;L;62FE;;;10;N;;;;;
+F974;CJK COMPATIBILITY IDEOGRAPH-F974;Lo;0;L;82E5;;;;N;;;;;
+F975;CJK COMPATIBILITY IDEOGRAPH-F975;Lo;0;L;63A0;;;;N;;;;;
+F976;CJK COMPATIBILITY IDEOGRAPH-F976;Lo;0;L;7565;;;;N;;;;;
+F977;CJK COMPATIBILITY IDEOGRAPH-F977;Lo;0;L;4EAE;;;;N;;;;;
+F978;CJK COMPATIBILITY IDEOGRAPH-F978;Lo;0;L;5169;;;2;N;;;;;
+F979;CJK COMPATIBILITY IDEOGRAPH-F979;Lo;0;L;51C9;;;;N;;;;;
+F97A;CJK COMPATIBILITY IDEOGRAPH-F97A;Lo;0;L;6881;;;;N;;;;;
+F97B;CJK COMPATIBILITY IDEOGRAPH-F97B;Lo;0;L;7CE7;;;;N;;;;;
+F97C;CJK COMPATIBILITY IDEOGRAPH-F97C;Lo;0;L;826F;;;;N;;;;;
+F97D;CJK COMPATIBILITY IDEOGRAPH-F97D;Lo;0;L;8AD2;;;;N;;;;;
+F97E;CJK COMPATIBILITY IDEOGRAPH-F97E;Lo;0;L;91CF;;;;N;;;;;
+F97F;CJK COMPATIBILITY IDEOGRAPH-F97F;Lo;0;L;52F5;;;;N;;;;;
+F980;CJK COMPATIBILITY IDEOGRAPH-F980;Lo;0;L;5442;;;;N;;;;;
+F981;CJK COMPATIBILITY IDEOGRAPH-F981;Lo;0;L;5973;;;;N;;;;;
+F982;CJK COMPATIBILITY IDEOGRAPH-F982;Lo;0;L;5EEC;;;;N;;;;;
+F983;CJK COMPATIBILITY IDEOGRAPH-F983;Lo;0;L;65C5;;;;N;;;;;
+F984;CJK COMPATIBILITY IDEOGRAPH-F984;Lo;0;L;6FFE;;;;N;;;;;
+F985;CJK COMPATIBILITY IDEOGRAPH-F985;Lo;0;L;792A;;;;N;;;;;
+F986;CJK COMPATIBILITY IDEOGRAPH-F986;Lo;0;L;95AD;;;;N;;;;;
+F987;CJK COMPATIBILITY IDEOGRAPH-F987;Lo;0;L;9A6A;;;;N;;;;;
+F988;CJK COMPATIBILITY IDEOGRAPH-F988;Lo;0;L;9E97;;;;N;;;;;
+F989;CJK COMPATIBILITY IDEOGRAPH-F989;Lo;0;L;9ECE;;;;N;;;;;
+F98A;CJK COMPATIBILITY IDEOGRAPH-F98A;Lo;0;L;529B;;;;N;;;;;
+F98B;CJK COMPATIBILITY IDEOGRAPH-F98B;Lo;0;L;66C6;;;;N;;;;;
+F98C;CJK COMPATIBILITY IDEOGRAPH-F98C;Lo;0;L;6B77;;;;N;;;;;
+F98D;CJK COMPATIBILITY IDEOGRAPH-F98D;Lo;0;L;8F62;;;;N;;;;;
+F98E;CJK COMPATIBILITY IDEOGRAPH-F98E;Lo;0;L;5E74;;;;N;;;;;
+F98F;CJK COMPATIBILITY IDEOGRAPH-F98F;Lo;0;L;6190;;;;N;;;;;
+F990;CJK COMPATIBILITY IDEOGRAPH-F990;Lo;0;L;6200;;;;N;;;;;
+F991;CJK COMPATIBILITY IDEOGRAPH-F991;Lo;0;L;649A;;;;N;;;;;
+F992;CJK COMPATIBILITY IDEOGRAPH-F992;Lo;0;L;6F23;;;;N;;;;;
+F993;CJK COMPATIBILITY IDEOGRAPH-F993;Lo;0;L;7149;;;;N;;;;;
+F994;CJK COMPATIBILITY IDEOGRAPH-F994;Lo;0;L;7489;;;;N;;;;;
+F995;CJK COMPATIBILITY IDEOGRAPH-F995;Lo;0;L;79CA;;;;N;;;;;
+F996;CJK COMPATIBILITY IDEOGRAPH-F996;Lo;0;L;7DF4;;;;N;;;;;
+F997;CJK COMPATIBILITY IDEOGRAPH-F997;Lo;0;L;806F;;;;N;;;;;
+F998;CJK COMPATIBILITY IDEOGRAPH-F998;Lo;0;L;8F26;;;;N;;;;;
+F999;CJK COMPATIBILITY IDEOGRAPH-F999;Lo;0;L;84EE;;;;N;;;;;
+F99A;CJK COMPATIBILITY IDEOGRAPH-F99A;Lo;0;L;9023;;;;N;;;;;
+F99B;CJK COMPATIBILITY IDEOGRAPH-F99B;Lo;0;L;934A;;;;N;;;;;
+F99C;CJK COMPATIBILITY IDEOGRAPH-F99C;Lo;0;L;5217;;;;N;;;;;
+F99D;CJK COMPATIBILITY IDEOGRAPH-F99D;Lo;0;L;52A3;;;;N;;;;;
+F99E;CJK COMPATIBILITY IDEOGRAPH-F99E;Lo;0;L;54BD;;;;N;;;;;
+F99F;CJK COMPATIBILITY IDEOGRAPH-F99F;Lo;0;L;70C8;;;;N;;;;;
+F9A0;CJK COMPATIBILITY IDEOGRAPH-F9A0;Lo;0;L;88C2;;;;N;;;;;
+F9A1;CJK COMPATIBILITY IDEOGRAPH-F9A1;Lo;0;L;8AAA;;;;N;;;;;
+F9A2;CJK COMPATIBILITY IDEOGRAPH-F9A2;Lo;0;L;5EC9;;;;N;;;;;
+F9A3;CJK COMPATIBILITY IDEOGRAPH-F9A3;Lo;0;L;5FF5;;;;N;;;;;
+F9A4;CJK COMPATIBILITY IDEOGRAPH-F9A4;Lo;0;L;637B;;;;N;;;;;
+F9A5;CJK COMPATIBILITY IDEOGRAPH-F9A5;Lo;0;L;6BAE;;;;N;;;;;
+F9A6;CJK COMPATIBILITY IDEOGRAPH-F9A6;Lo;0;L;7C3E;;;;N;;;;;
+F9A7;CJK COMPATIBILITY IDEOGRAPH-F9A7;Lo;0;L;7375;;;;N;;;;;
+F9A8;CJK COMPATIBILITY IDEOGRAPH-F9A8;Lo;0;L;4EE4;;;;N;;;;;
+F9A9;CJK COMPATIBILITY IDEOGRAPH-F9A9;Lo;0;L;56F9;;;;N;;;;;
+F9AA;CJK COMPATIBILITY IDEOGRAPH-F9AA;Lo;0;L;5BE7;;;;N;;;;;
+F9AB;CJK COMPATIBILITY IDEOGRAPH-F9AB;Lo;0;L;5DBA;;;;N;;;;;
+F9AC;CJK COMPATIBILITY IDEOGRAPH-F9AC;Lo;0;L;601C;;;;N;;;;;
+F9AD;CJK COMPATIBILITY IDEOGRAPH-F9AD;Lo;0;L;73B2;;;;N;;;;;
+F9AE;CJK COMPATIBILITY IDEOGRAPH-F9AE;Lo;0;L;7469;;;;N;;;;;
+F9AF;CJK COMPATIBILITY IDEOGRAPH-F9AF;Lo;0;L;7F9A;;;;N;;;;;
+F9B0;CJK COMPATIBILITY IDEOGRAPH-F9B0;Lo;0;L;8046;;;;N;;;;;
+F9B1;CJK COMPATIBILITY IDEOGRAPH-F9B1;Lo;0;L;9234;;;;N;;;;;
+F9B2;CJK COMPATIBILITY IDEOGRAPH-F9B2;Lo;0;L;96F6;;;0;N;;;;;
+F9B3;CJK COMPATIBILITY IDEOGRAPH-F9B3;Lo;0;L;9748;;;;N;;;;;
+F9B4;CJK COMPATIBILITY IDEOGRAPH-F9B4;Lo;0;L;9818;;;;N;;;;;
+F9B5;CJK COMPATIBILITY IDEOGRAPH-F9B5;Lo;0;L;4F8B;;;;N;;;;;
+F9B6;CJK COMPATIBILITY IDEOGRAPH-F9B6;Lo;0;L;79AE;;;;N;;;;;
+F9B7;CJK COMPATIBILITY IDEOGRAPH-F9B7;Lo;0;L;91B4;;;;N;;;;;
+F9B8;CJK COMPATIBILITY IDEOGRAPH-F9B8;Lo;0;L;96B8;;;;N;;;;;
+F9B9;CJK COMPATIBILITY IDEOGRAPH-F9B9;Lo;0;L;60E1;;;;N;;;;;
+F9BA;CJK COMPATIBILITY IDEOGRAPH-F9BA;Lo;0;L;4E86;;;;N;;;;;
+F9BB;CJK COMPATIBILITY IDEOGRAPH-F9BB;Lo;0;L;50DA;;;;N;;;;;
+F9BC;CJK COMPATIBILITY IDEOGRAPH-F9BC;Lo;0;L;5BEE;;;;N;;;;;
+F9BD;CJK COMPATIBILITY IDEOGRAPH-F9BD;Lo;0;L;5C3F;;;;N;;;;;
+F9BE;CJK COMPATIBILITY IDEOGRAPH-F9BE;Lo;0;L;6599;;;;N;;;;;
+F9BF;CJK COMPATIBILITY IDEOGRAPH-F9BF;Lo;0;L;6A02;;;;N;;;;;
+F9C0;CJK COMPATIBILITY IDEOGRAPH-F9C0;Lo;0;L;71CE;;;;N;;;;;
+F9C1;CJK COMPATIBILITY IDEOGRAPH-F9C1;Lo;0;L;7642;;;;N;;;;;
+F9C2;CJK COMPATIBILITY IDEOGRAPH-F9C2;Lo;0;L;84FC;;;;N;;;;;
+F9C3;CJK COMPATIBILITY IDEOGRAPH-F9C3;Lo;0;L;907C;;;;N;;;;;
+F9C4;CJK COMPATIBILITY IDEOGRAPH-F9C4;Lo;0;L;9F8D;;;;N;;;;;
+F9C5;CJK COMPATIBILITY IDEOGRAPH-F9C5;Lo;0;L;6688;;;;N;;;;;
+F9C6;CJK COMPATIBILITY IDEOGRAPH-F9C6;Lo;0;L;962E;;;;N;;;;;
+F9C7;CJK COMPATIBILITY IDEOGRAPH-F9C7;Lo;0;L;5289;;;;N;;;;;
+F9C8;CJK COMPATIBILITY IDEOGRAPH-F9C8;Lo;0;L;677B;;;;N;;;;;
+F9C9;CJK COMPATIBILITY IDEOGRAPH-F9C9;Lo;0;L;67F3;;;;N;;;;;
+F9CA;CJK COMPATIBILITY IDEOGRAPH-F9CA;Lo;0;L;6D41;;;;N;;;;;
+F9CB;CJK COMPATIBILITY IDEOGRAPH-F9CB;Lo;0;L;6E9C;;;;N;;;;;
+F9CC;CJK COMPATIBILITY IDEOGRAPH-F9CC;Lo;0;L;7409;;;;N;;;;;
+F9CD;CJK COMPATIBILITY IDEOGRAPH-F9CD;Lo;0;L;7559;;;;N;;;;;
+F9CE;CJK COMPATIBILITY IDEOGRAPH-F9CE;Lo;0;L;786B;;;;N;;;;;
+F9CF;CJK COMPATIBILITY IDEOGRAPH-F9CF;Lo;0;L;7D10;;;;N;;;;;
+F9D0;CJK COMPATIBILITY IDEOGRAPH-F9D0;Lo;0;L;985E;;;;N;;;;;
+F9D1;CJK COMPATIBILITY IDEOGRAPH-F9D1;Lo;0;L;516D;;;6;N;;;;;
+F9D2;CJK COMPATIBILITY IDEOGRAPH-F9D2;Lo;0;L;622E;;;;N;;;;;
+F9D3;CJK COMPATIBILITY IDEOGRAPH-F9D3;Lo;0;L;9678;;;6;N;;;;;
+F9D4;CJK COMPATIBILITY IDEOGRAPH-F9D4;Lo;0;L;502B;;;;N;;;;;
+F9D5;CJK COMPATIBILITY IDEOGRAPH-F9D5;Lo;0;L;5D19;;;;N;;;;;
+F9D6;CJK COMPATIBILITY IDEOGRAPH-F9D6;Lo;0;L;6DEA;;;;N;;;;;
+F9D7;CJK COMPATIBILITY IDEOGRAPH-F9D7;Lo;0;L;8F2A;;;;N;;;;;
+F9D8;CJK COMPATIBILITY IDEOGRAPH-F9D8;Lo;0;L;5F8B;;;;N;;;;;
+F9D9;CJK COMPATIBILITY IDEOGRAPH-F9D9;Lo;0;L;6144;;;;N;;;;;
+F9DA;CJK COMPATIBILITY IDEOGRAPH-F9DA;Lo;0;L;6817;;;;N;;;;;
+F9DB;CJK COMPATIBILITY IDEOGRAPH-F9DB;Lo;0;L;7387;;;;N;;;;;
+F9DC;CJK COMPATIBILITY IDEOGRAPH-F9DC;Lo;0;L;9686;;;;N;;;;;
+F9DD;CJK COMPATIBILITY IDEOGRAPH-F9DD;Lo;0;L;5229;;;;N;;;;;
+F9DE;CJK COMPATIBILITY IDEOGRAPH-F9DE;Lo;0;L;540F;;;;N;;;;;
+F9DF;CJK COMPATIBILITY IDEOGRAPH-F9DF;Lo;0;L;5C65;;;;N;;;;;
+F9E0;CJK COMPATIBILITY IDEOGRAPH-F9E0;Lo;0;L;6613;;;;N;;;;;
+F9E1;CJK COMPATIBILITY IDEOGRAPH-F9E1;Lo;0;L;674E;;;;N;;;;;
+F9E2;CJK COMPATIBILITY IDEOGRAPH-F9E2;Lo;0;L;68A8;;;;N;;;;;
+F9E3;CJK COMPATIBILITY IDEOGRAPH-F9E3;Lo;0;L;6CE5;;;;N;;;;;
+F9E4;CJK COMPATIBILITY IDEOGRAPH-F9E4;Lo;0;L;7406;;;;N;;;;;
+F9E5;CJK COMPATIBILITY IDEOGRAPH-F9E5;Lo;0;L;75E2;;;;N;;;;;
+F9E6;CJK COMPATIBILITY IDEOGRAPH-F9E6;Lo;0;L;7F79;;;;N;;;;;
+F9E7;CJK COMPATIBILITY IDEOGRAPH-F9E7;Lo;0;L;88CF;;;;N;;;;;
+F9E8;CJK COMPATIBILITY IDEOGRAPH-F9E8;Lo;0;L;88E1;;;;N;;;;;
+F9E9;CJK COMPATIBILITY IDEOGRAPH-F9E9;Lo;0;L;91CC;;;;N;;;;;
+F9EA;CJK COMPATIBILITY IDEOGRAPH-F9EA;Lo;0;L;96E2;;;;N;;;;;
+F9EB;CJK COMPATIBILITY IDEOGRAPH-F9EB;Lo;0;L;533F;;;;N;;;;;
+F9EC;CJK COMPATIBILITY IDEOGRAPH-F9EC;Lo;0;L;6EBA;;;;N;;;;;
+F9ED;CJK COMPATIBILITY IDEOGRAPH-F9ED;Lo;0;L;541D;;;;N;;;;;
+F9EE;CJK COMPATIBILITY IDEOGRAPH-F9EE;Lo;0;L;71D0;;;;N;;;;;
+F9EF;CJK COMPATIBILITY IDEOGRAPH-F9EF;Lo;0;L;7498;;;;N;;;;;
+F9F0;CJK COMPATIBILITY IDEOGRAPH-F9F0;Lo;0;L;85FA;;;;N;;;;;
+F9F1;CJK COMPATIBILITY IDEOGRAPH-F9F1;Lo;0;L;96A3;;;;N;;;;;
+F9F2;CJK COMPATIBILITY IDEOGRAPH-F9F2;Lo;0;L;9C57;;;;N;;;;;
+F9F3;CJK COMPATIBILITY IDEOGRAPH-F9F3;Lo;0;L;9E9F;;;;N;;;;;
+F9F4;CJK COMPATIBILITY IDEOGRAPH-F9F4;Lo;0;L;6797;;;;N;;;;;
+F9F5;CJK COMPATIBILITY IDEOGRAPH-F9F5;Lo;0;L;6DCB;;;;N;;;;;
+F9F6;CJK COMPATIBILITY IDEOGRAPH-F9F6;Lo;0;L;81E8;;;;N;;;;;
+F9F7;CJK COMPATIBILITY IDEOGRAPH-F9F7;Lo;0;L;7ACB;;;;N;;;;;
+F9F8;CJK COMPATIBILITY IDEOGRAPH-F9F8;Lo;0;L;7B20;;;;N;;;;;
+F9F9;CJK COMPATIBILITY IDEOGRAPH-F9F9;Lo;0;L;7C92;;;;N;;;;;
+F9FA;CJK COMPATIBILITY IDEOGRAPH-F9FA;Lo;0;L;72C0;;;;N;;;;;
+F9FB;CJK COMPATIBILITY IDEOGRAPH-F9FB;Lo;0;L;7099;;;;N;;;;;
+F9FC;CJK COMPATIBILITY IDEOGRAPH-F9FC;Lo;0;L;8B58;;;;N;;;;;
+F9FD;CJK COMPATIBILITY IDEOGRAPH-F9FD;Lo;0;L;4EC0;;;10;N;;;;;
+F9FE;CJK COMPATIBILITY IDEOGRAPH-F9FE;Lo;0;L;8336;;;;N;;;;;
+F9FF;CJK COMPATIBILITY IDEOGRAPH-F9FF;Lo;0;L;523A;;;;N;;;;;
+FA00;CJK COMPATIBILITY IDEOGRAPH-FA00;Lo;0;L;5207;;;;N;;;;;
+FA01;CJK COMPATIBILITY IDEOGRAPH-FA01;Lo;0;L;5EA6;;;;N;;;;;
+FA02;CJK COMPATIBILITY IDEOGRAPH-FA02;Lo;0;L;62D3;;;;N;;;;;
+FA03;CJK COMPATIBILITY IDEOGRAPH-FA03;Lo;0;L;7CD6;;;;N;;;;;
+FA04;CJK COMPATIBILITY IDEOGRAPH-FA04;Lo;0;L;5B85;;;;N;;;;;
+FA05;CJK COMPATIBILITY IDEOGRAPH-FA05;Lo;0;L;6D1E;;;;N;;;;;
+FA06;CJK COMPATIBILITY IDEOGRAPH-FA06;Lo;0;L;66B4;;;;N;;;;;
+FA07;CJK COMPATIBILITY IDEOGRAPH-FA07;Lo;0;L;8F3B;;;;N;;;;;
+FA08;CJK COMPATIBILITY IDEOGRAPH-FA08;Lo;0;L;884C;;;;N;;;;;
+FA09;CJK COMPATIBILITY IDEOGRAPH-FA09;Lo;0;L;964D;;;;N;;;;;
+FA0A;CJK COMPATIBILITY IDEOGRAPH-FA0A;Lo;0;L;898B;;;;N;;;;;
+FA0B;CJK COMPATIBILITY IDEOGRAPH-FA0B;Lo;0;L;5ED3;;;;N;;;;;
+FA0C;CJK COMPATIBILITY IDEOGRAPH-FA0C;Lo;0;L;5140;;;;N;;;;;
+FA0D;CJK COMPATIBILITY IDEOGRAPH-FA0D;Lo;0;L;55C0;;;;N;;;;;
+FA0E;CJK COMPATIBILITY IDEOGRAPH-FA0E;Lo;0;L;;;;;N;;;;;
+FA0F;CJK COMPATIBILITY IDEOGRAPH-FA0F;Lo;0;L;;;;;N;;;;;
+FA10;CJK COMPATIBILITY IDEOGRAPH-FA10;Lo;0;L;585A;;;;N;;;;;
+FA11;CJK COMPATIBILITY IDEOGRAPH-FA11;Lo;0;L;;;;;N;;;;;
+FA12;CJK COMPATIBILITY IDEOGRAPH-FA12;Lo;0;L;6674;;;;N;;;;;
+FA13;CJK COMPATIBILITY IDEOGRAPH-FA13;Lo;0;L;;;;;N;;;;;
+FA14;CJK COMPATIBILITY IDEOGRAPH-FA14;Lo;0;L;;;;;N;;;;;
+FA15;CJK COMPATIBILITY IDEOGRAPH-FA15;Lo;0;L;51DE;;;;N;;;;;
+FA16;CJK COMPATIBILITY IDEOGRAPH-FA16;Lo;0;L;732A;;;;N;;;;;
+FA17;CJK COMPATIBILITY IDEOGRAPH-FA17;Lo;0;L;76CA;;;;N;;;;;
+FA18;CJK COMPATIBILITY IDEOGRAPH-FA18;Lo;0;L;793C;;;;N;;;;;
+FA19;CJK COMPATIBILITY IDEOGRAPH-FA19;Lo;0;L;795E;;;;N;;;;;
+FA1A;CJK COMPATIBILITY IDEOGRAPH-FA1A;Lo;0;L;7965;;;;N;;;;;
+FA1B;CJK COMPATIBILITY IDEOGRAPH-FA1B;Lo;0;L;798F;;;;N;;;;;
+FA1C;CJK COMPATIBILITY IDEOGRAPH-FA1C;Lo;0;L;9756;;;;N;;;;;
+FA1D;CJK COMPATIBILITY IDEOGRAPH-FA1D;Lo;0;L;7CBE;;;;N;;;;;
+FA1E;CJK COMPATIBILITY IDEOGRAPH-FA1E;Lo;0;L;7FBD;;;;N;;;;;
+FA1F;CJK COMPATIBILITY IDEOGRAPH-FA1F;Lo;0;L;;;;;N;;;;;
+FA20;CJK COMPATIBILITY IDEOGRAPH-FA20;Lo;0;L;8612;;;;N;;;;;
+FA21;CJK COMPATIBILITY IDEOGRAPH-FA21;Lo;0;L;;;;;N;;;;;
+FA22;CJK COMPATIBILITY IDEOGRAPH-FA22;Lo;0;L;8AF8;;;;N;;;;;
+FA23;CJK COMPATIBILITY IDEOGRAPH-FA23;Lo;0;L;;;;;N;;;;;
+FA24;CJK COMPATIBILITY IDEOGRAPH-FA24;Lo;0;L;;;;;N;;;;;
+FA25;CJK COMPATIBILITY IDEOGRAPH-FA25;Lo;0;L;9038;;;;N;;;;;
+FA26;CJK COMPATIBILITY IDEOGRAPH-FA26;Lo;0;L;90FD;;;;N;;;;;
+FA27;CJK COMPATIBILITY IDEOGRAPH-FA27;Lo;0;L;;;;;N;;;;;
+FA28;CJK COMPATIBILITY IDEOGRAPH-FA28;Lo;0;L;;;;;N;;;;;
+FA29;CJK COMPATIBILITY IDEOGRAPH-FA29;Lo;0;L;;;;;N;;;;;
+FA2A;CJK COMPATIBILITY IDEOGRAPH-FA2A;Lo;0;L;98EF;;;;N;;;;;
+FA2B;CJK COMPATIBILITY IDEOGRAPH-FA2B;Lo;0;L;98FC;;;;N;;;;;
+FA2C;CJK COMPATIBILITY IDEOGRAPH-FA2C;Lo;0;L;9928;;;;N;;;;;
+FA2D;CJK COMPATIBILITY IDEOGRAPH-FA2D;Lo;0;L;9DB4;;;;N;;;;;
+FA2E;CJK COMPATIBILITY IDEOGRAPH-FA2E;Lo;0;L;90DE;;;;N;;;;;
+FA2F;CJK COMPATIBILITY IDEOGRAPH-FA2F;Lo;0;L;96B7;;;;N;;;;;
+FA30;CJK COMPATIBILITY IDEOGRAPH-FA30;Lo;0;L;4FAE;;;;N;;;;;
+FA31;CJK COMPATIBILITY IDEOGRAPH-FA31;Lo;0;L;50E7;;;;N;;;;;
+FA32;CJK COMPATIBILITY IDEOGRAPH-FA32;Lo;0;L;514D;;;;N;;;;;
+FA33;CJK COMPATIBILITY IDEOGRAPH-FA33;Lo;0;L;52C9;;;;N;;;;;
+FA34;CJK COMPATIBILITY IDEOGRAPH-FA34;Lo;0;L;52E4;;;;N;;;;;
+FA35;CJK COMPATIBILITY IDEOGRAPH-FA35;Lo;0;L;5351;;;;N;;;;;
+FA36;CJK COMPATIBILITY IDEOGRAPH-FA36;Lo;0;L;559D;;;;N;;;;;
+FA37;CJK COMPATIBILITY IDEOGRAPH-FA37;Lo;0;L;5606;;;;N;;;;;
+FA38;CJK COMPATIBILITY IDEOGRAPH-FA38;Lo;0;L;5668;;;;N;;;;;
+FA39;CJK COMPATIBILITY IDEOGRAPH-FA39;Lo;0;L;5840;;;;N;;;;;
+FA3A;CJK COMPATIBILITY IDEOGRAPH-FA3A;Lo;0;L;58A8;;;;N;;;;;
+FA3B;CJK COMPATIBILITY IDEOGRAPH-FA3B;Lo;0;L;5C64;;;;N;;;;;
+FA3C;CJK COMPATIBILITY IDEOGRAPH-FA3C;Lo;0;L;5C6E;;;;N;;;;;
+FA3D;CJK COMPATIBILITY IDEOGRAPH-FA3D;Lo;0;L;6094;;;;N;;;;;
+FA3E;CJK COMPATIBILITY IDEOGRAPH-FA3E;Lo;0;L;6168;;;;N;;;;;
+FA3F;CJK COMPATIBILITY IDEOGRAPH-FA3F;Lo;0;L;618E;;;;N;;;;;
+FA40;CJK COMPATIBILITY IDEOGRAPH-FA40;Lo;0;L;61F2;;;;N;;;;;
+FA41;CJK COMPATIBILITY IDEOGRAPH-FA41;Lo;0;L;654F;;;;N;;;;;
+FA42;CJK COMPATIBILITY IDEOGRAPH-FA42;Lo;0;L;65E2;;;;N;;;;;
+FA43;CJK COMPATIBILITY IDEOGRAPH-FA43;Lo;0;L;6691;;;;N;;;;;
+FA44;CJK COMPATIBILITY IDEOGRAPH-FA44;Lo;0;L;6885;;;;N;;;;;
+FA45;CJK COMPATIBILITY IDEOGRAPH-FA45;Lo;0;L;6D77;;;;N;;;;;
+FA46;CJK COMPATIBILITY IDEOGRAPH-FA46;Lo;0;L;6E1A;;;;N;;;;;
+FA47;CJK COMPATIBILITY IDEOGRAPH-FA47;Lo;0;L;6F22;;;;N;;;;;
+FA48;CJK COMPATIBILITY IDEOGRAPH-FA48;Lo;0;L;716E;;;;N;;;;;
+FA49;CJK COMPATIBILITY IDEOGRAPH-FA49;Lo;0;L;722B;;;;N;;;;;
+FA4A;CJK COMPATIBILITY IDEOGRAPH-FA4A;Lo;0;L;7422;;;;N;;;;;
+FA4B;CJK COMPATIBILITY IDEOGRAPH-FA4B;Lo;0;L;7891;;;;N;;;;;
+FA4C;CJK COMPATIBILITY IDEOGRAPH-FA4C;Lo;0;L;793E;;;;N;;;;;
+FA4D;CJK COMPATIBILITY IDEOGRAPH-FA4D;Lo;0;L;7949;;;;N;;;;;
+FA4E;CJK COMPATIBILITY IDEOGRAPH-FA4E;Lo;0;L;7948;;;;N;;;;;
+FA4F;CJK COMPATIBILITY IDEOGRAPH-FA4F;Lo;0;L;7950;;;;N;;;;;
+FA50;CJK COMPATIBILITY IDEOGRAPH-FA50;Lo;0;L;7956;;;;N;;;;;
+FA51;CJK COMPATIBILITY IDEOGRAPH-FA51;Lo;0;L;795D;;;;N;;;;;
+FA52;CJK COMPATIBILITY IDEOGRAPH-FA52;Lo;0;L;798D;;;;N;;;;;
+FA53;CJK COMPATIBILITY IDEOGRAPH-FA53;Lo;0;L;798E;;;;N;;;;;
+FA54;CJK COMPATIBILITY IDEOGRAPH-FA54;Lo;0;L;7A40;;;;N;;;;;
+FA55;CJK COMPATIBILITY IDEOGRAPH-FA55;Lo;0;L;7A81;;;;N;;;;;
+FA56;CJK COMPATIBILITY IDEOGRAPH-FA56;Lo;0;L;7BC0;;;;N;;;;;
+FA57;CJK COMPATIBILITY IDEOGRAPH-FA57;Lo;0;L;7DF4;;;;N;;;;;
+FA58;CJK COMPATIBILITY IDEOGRAPH-FA58;Lo;0;L;7E09;;;;N;;;;;
+FA59;CJK COMPATIBILITY IDEOGRAPH-FA59;Lo;0;L;7E41;;;;N;;;;;
+FA5A;CJK COMPATIBILITY IDEOGRAPH-FA5A;Lo;0;L;7F72;;;;N;;;;;
+FA5B;CJK COMPATIBILITY IDEOGRAPH-FA5B;Lo;0;L;8005;;;;N;;;;;
+FA5C;CJK COMPATIBILITY IDEOGRAPH-FA5C;Lo;0;L;81ED;;;;N;;;;;
+FA5D;CJK COMPATIBILITY IDEOGRAPH-FA5D;Lo;0;L;8279;;;;N;;;;;
+FA5E;CJK COMPATIBILITY IDEOGRAPH-FA5E;Lo;0;L;8279;;;;N;;;;;
+FA5F;CJK COMPATIBILITY IDEOGRAPH-FA5F;Lo;0;L;8457;;;;N;;;;;
+FA60;CJK COMPATIBILITY IDEOGRAPH-FA60;Lo;0;L;8910;;;;N;;;;;
+FA61;CJK COMPATIBILITY IDEOGRAPH-FA61;Lo;0;L;8996;;;;N;;;;;
+FA62;CJK COMPATIBILITY IDEOGRAPH-FA62;Lo;0;L;8B01;;;;N;;;;;
+FA63;CJK COMPATIBILITY IDEOGRAPH-FA63;Lo;0;L;8B39;;;;N;;;;;
+FA64;CJK COMPATIBILITY IDEOGRAPH-FA64;Lo;0;L;8CD3;;;;N;;;;;
+FA65;CJK COMPATIBILITY IDEOGRAPH-FA65;Lo;0;L;8D08;;;;N;;;;;
+FA66;CJK COMPATIBILITY IDEOGRAPH-FA66;Lo;0;L;8FB6;;;;N;;;;;
+FA67;CJK COMPATIBILITY IDEOGRAPH-FA67;Lo;0;L;9038;;;;N;;;;;
+FA68;CJK COMPATIBILITY IDEOGRAPH-FA68;Lo;0;L;96E3;;;;N;;;;;
+FA69;CJK COMPATIBILITY IDEOGRAPH-FA69;Lo;0;L;97FF;;;;N;;;;;
+FA6A;CJK COMPATIBILITY IDEOGRAPH-FA6A;Lo;0;L;983B;;;;N;;;;;
+FA6B;CJK COMPATIBILITY IDEOGRAPH-FA6B;Lo;0;L;6075;;;;N;;;;;
+FA6C;CJK COMPATIBILITY IDEOGRAPH-FA6C;Lo;0;L;242EE;;;;N;;;;;
+FA6D;CJK COMPATIBILITY IDEOGRAPH-FA6D;Lo;0;L;8218;;;;N;;;;;
+FA70;CJK COMPATIBILITY IDEOGRAPH-FA70;Lo;0;L;4E26;;;;N;;;;;
+FA71;CJK COMPATIBILITY IDEOGRAPH-FA71;Lo;0;L;51B5;;;;N;;;;;
+FA72;CJK COMPATIBILITY IDEOGRAPH-FA72;Lo;0;L;5168;;;;N;;;;;
+FA73;CJK COMPATIBILITY IDEOGRAPH-FA73;Lo;0;L;4F80;;;;N;;;;;
+FA74;CJK COMPATIBILITY IDEOGRAPH-FA74;Lo;0;L;5145;;;;N;;;;;
+FA75;CJK COMPATIBILITY IDEOGRAPH-FA75;Lo;0;L;5180;;;;N;;;;;
+FA76;CJK COMPATIBILITY IDEOGRAPH-FA76;Lo;0;L;52C7;;;;N;;;;;
+FA77;CJK COMPATIBILITY IDEOGRAPH-FA77;Lo;0;L;52FA;;;;N;;;;;
+FA78;CJK COMPATIBILITY IDEOGRAPH-FA78;Lo;0;L;559D;;;;N;;;;;
+FA79;CJK COMPATIBILITY IDEOGRAPH-FA79;Lo;0;L;5555;;;;N;;;;;
+FA7A;CJK COMPATIBILITY IDEOGRAPH-FA7A;Lo;0;L;5599;;;;N;;;;;
+FA7B;CJK COMPATIBILITY IDEOGRAPH-FA7B;Lo;0;L;55E2;;;;N;;;;;
+FA7C;CJK COMPATIBILITY IDEOGRAPH-FA7C;Lo;0;L;585A;;;;N;;;;;
+FA7D;CJK COMPATIBILITY IDEOGRAPH-FA7D;Lo;0;L;58B3;;;;N;;;;;
+FA7E;CJK COMPATIBILITY IDEOGRAPH-FA7E;Lo;0;L;5944;;;;N;;;;;
+FA7F;CJK COMPATIBILITY IDEOGRAPH-FA7F;Lo;0;L;5954;;;;N;;;;;
+FA80;CJK COMPATIBILITY IDEOGRAPH-FA80;Lo;0;L;5A62;;;;N;;;;;
+FA81;CJK COMPATIBILITY IDEOGRAPH-FA81;Lo;0;L;5B28;;;;N;;;;;
+FA82;CJK COMPATIBILITY IDEOGRAPH-FA82;Lo;0;L;5ED2;;;;N;;;;;
+FA83;CJK COMPATIBILITY IDEOGRAPH-FA83;Lo;0;L;5ED9;;;;N;;;;;
+FA84;CJK COMPATIBILITY IDEOGRAPH-FA84;Lo;0;L;5F69;;;;N;;;;;
+FA85;CJK COMPATIBILITY IDEOGRAPH-FA85;Lo;0;L;5FAD;;;;N;;;;;
+FA86;CJK COMPATIBILITY IDEOGRAPH-FA86;Lo;0;L;60D8;;;;N;;;;;
+FA87;CJK COMPATIBILITY IDEOGRAPH-FA87;Lo;0;L;614E;;;;N;;;;;
+FA88;CJK COMPATIBILITY IDEOGRAPH-FA88;Lo;0;L;6108;;;;N;;;;;
+FA89;CJK COMPATIBILITY IDEOGRAPH-FA89;Lo;0;L;618E;;;;N;;;;;
+FA8A;CJK COMPATIBILITY IDEOGRAPH-FA8A;Lo;0;L;6160;;;;N;;;;;
+FA8B;CJK COMPATIBILITY IDEOGRAPH-FA8B;Lo;0;L;61F2;;;;N;;;;;
+FA8C;CJK COMPATIBILITY IDEOGRAPH-FA8C;Lo;0;L;6234;;;;N;;;;;
+FA8D;CJK COMPATIBILITY IDEOGRAPH-FA8D;Lo;0;L;63C4;;;;N;;;;;
+FA8E;CJK COMPATIBILITY IDEOGRAPH-FA8E;Lo;0;L;641C;;;;N;;;;;
+FA8F;CJK COMPATIBILITY IDEOGRAPH-FA8F;Lo;0;L;6452;;;;N;;;;;
+FA90;CJK COMPATIBILITY IDEOGRAPH-FA90;Lo;0;L;6556;;;;N;;;;;
+FA91;CJK COMPATIBILITY IDEOGRAPH-FA91;Lo;0;L;6674;;;;N;;;;;
+FA92;CJK COMPATIBILITY IDEOGRAPH-FA92;Lo;0;L;6717;;;;N;;;;;
+FA93;CJK COMPATIBILITY IDEOGRAPH-FA93;Lo;0;L;671B;;;;N;;;;;
+FA94;CJK COMPATIBILITY IDEOGRAPH-FA94;Lo;0;L;6756;;;;N;;;;;
+FA95;CJK COMPATIBILITY IDEOGRAPH-FA95;Lo;0;L;6B79;;;;N;;;;;
+FA96;CJK COMPATIBILITY IDEOGRAPH-FA96;Lo;0;L;6BBA;;;;N;;;;;
+FA97;CJK COMPATIBILITY IDEOGRAPH-FA97;Lo;0;L;6D41;;;;N;;;;;
+FA98;CJK COMPATIBILITY IDEOGRAPH-FA98;Lo;0;L;6EDB;;;;N;;;;;
+FA99;CJK COMPATIBILITY IDEOGRAPH-FA99;Lo;0;L;6ECB;;;;N;;;;;
+FA9A;CJK COMPATIBILITY IDEOGRAPH-FA9A;Lo;0;L;6F22;;;;N;;;;;
+FA9B;CJK COMPATIBILITY IDEOGRAPH-FA9B;Lo;0;L;701E;;;;N;;;;;
+FA9C;CJK COMPATIBILITY IDEOGRAPH-FA9C;Lo;0;L;716E;;;;N;;;;;
+FA9D;CJK COMPATIBILITY IDEOGRAPH-FA9D;Lo;0;L;77A7;;;;N;;;;;
+FA9E;CJK COMPATIBILITY IDEOGRAPH-FA9E;Lo;0;L;7235;;;;N;;;;;
+FA9F;CJK COMPATIBILITY IDEOGRAPH-FA9F;Lo;0;L;72AF;;;;N;;;;;
+FAA0;CJK COMPATIBILITY IDEOGRAPH-FAA0;Lo;0;L;732A;;;;N;;;;;
+FAA1;CJK COMPATIBILITY IDEOGRAPH-FAA1;Lo;0;L;7471;;;;N;;;;;
+FAA2;CJK COMPATIBILITY IDEOGRAPH-FAA2;Lo;0;L;7506;;;;N;;;;;
+FAA3;CJK COMPATIBILITY IDEOGRAPH-FAA3;Lo;0;L;753B;;;;N;;;;;
+FAA4;CJK COMPATIBILITY IDEOGRAPH-FAA4;Lo;0;L;761D;;;;N;;;;;
+FAA5;CJK COMPATIBILITY IDEOGRAPH-FAA5;Lo;0;L;761F;;;;N;;;;;
+FAA6;CJK COMPATIBILITY IDEOGRAPH-FAA6;Lo;0;L;76CA;;;;N;;;;;
+FAA7;CJK COMPATIBILITY IDEOGRAPH-FAA7;Lo;0;L;76DB;;;;N;;;;;
+FAA8;CJK COMPATIBILITY IDEOGRAPH-FAA8;Lo;0;L;76F4;;;;N;;;;;
+FAA9;CJK COMPATIBILITY IDEOGRAPH-FAA9;Lo;0;L;774A;;;;N;;;;;
+FAAA;CJK COMPATIBILITY IDEOGRAPH-FAAA;Lo;0;L;7740;;;;N;;;;;
+FAAB;CJK COMPATIBILITY IDEOGRAPH-FAAB;Lo;0;L;78CC;;;;N;;;;;
+FAAC;CJK COMPATIBILITY IDEOGRAPH-FAAC;Lo;0;L;7AB1;;;;N;;;;;
+FAAD;CJK COMPATIBILITY IDEOGRAPH-FAAD;Lo;0;L;7BC0;;;;N;;;;;
+FAAE;CJK COMPATIBILITY IDEOGRAPH-FAAE;Lo;0;L;7C7B;;;;N;;;;;
+FAAF;CJK COMPATIBILITY IDEOGRAPH-FAAF;Lo;0;L;7D5B;;;;N;;;;;
+FAB0;CJK COMPATIBILITY IDEOGRAPH-FAB0;Lo;0;L;7DF4;;;;N;;;;;
+FAB1;CJK COMPATIBILITY IDEOGRAPH-FAB1;Lo;0;L;7F3E;;;;N;;;;;
+FAB2;CJK COMPATIBILITY IDEOGRAPH-FAB2;Lo;0;L;8005;;;;N;;;;;
+FAB3;CJK COMPATIBILITY IDEOGRAPH-FAB3;Lo;0;L;8352;;;;N;;;;;
+FAB4;CJK COMPATIBILITY IDEOGRAPH-FAB4;Lo;0;L;83EF;;;;N;;;;;
+FAB5;CJK COMPATIBILITY IDEOGRAPH-FAB5;Lo;0;L;8779;;;;N;;;;;
+FAB6;CJK COMPATIBILITY IDEOGRAPH-FAB6;Lo;0;L;8941;;;;N;;;;;
+FAB7;CJK COMPATIBILITY IDEOGRAPH-FAB7;Lo;0;L;8986;;;;N;;;;;
+FAB8;CJK COMPATIBILITY IDEOGRAPH-FAB8;Lo;0;L;8996;;;;N;;;;;
+FAB9;CJK COMPATIBILITY IDEOGRAPH-FAB9;Lo;0;L;8ABF;;;;N;;;;;
+FABA;CJK COMPATIBILITY IDEOGRAPH-FABA;Lo;0;L;8AF8;;;;N;;;;;
+FABB;CJK COMPATIBILITY IDEOGRAPH-FABB;Lo;0;L;8ACB;;;;N;;;;;
+FABC;CJK COMPATIBILITY IDEOGRAPH-FABC;Lo;0;L;8B01;;;;N;;;;;
+FABD;CJK COMPATIBILITY IDEOGRAPH-FABD;Lo;0;L;8AFE;;;;N;;;;;
+FABE;CJK COMPATIBILITY IDEOGRAPH-FABE;Lo;0;L;8AED;;;;N;;;;;
+FABF;CJK COMPATIBILITY IDEOGRAPH-FABF;Lo;0;L;8B39;;;;N;;;;;
+FAC0;CJK COMPATIBILITY IDEOGRAPH-FAC0;Lo;0;L;8B8A;;;;N;;;;;
+FAC1;CJK COMPATIBILITY IDEOGRAPH-FAC1;Lo;0;L;8D08;;;;N;;;;;
+FAC2;CJK COMPATIBILITY IDEOGRAPH-FAC2;Lo;0;L;8F38;;;;N;;;;;
+FAC3;CJK COMPATIBILITY IDEOGRAPH-FAC3;Lo;0;L;9072;;;;N;;;;;
+FAC4;CJK COMPATIBILITY IDEOGRAPH-FAC4;Lo;0;L;9199;;;;N;;;;;
+FAC5;CJK COMPATIBILITY IDEOGRAPH-FAC5;Lo;0;L;9276;;;;N;;;;;
+FAC6;CJK COMPATIBILITY IDEOGRAPH-FAC6;Lo;0;L;967C;;;;N;;;;;
+FAC7;CJK COMPATIBILITY IDEOGRAPH-FAC7;Lo;0;L;96E3;;;;N;;;;;
+FAC8;CJK COMPATIBILITY IDEOGRAPH-FAC8;Lo;0;L;9756;;;;N;;;;;
+FAC9;CJK COMPATIBILITY IDEOGRAPH-FAC9;Lo;0;L;97DB;;;;N;;;;;
+FACA;CJK COMPATIBILITY IDEOGRAPH-FACA;Lo;0;L;97FF;;;;N;;;;;
+FACB;CJK COMPATIBILITY IDEOGRAPH-FACB;Lo;0;L;980B;;;;N;;;;;
+FACC;CJK COMPATIBILITY IDEOGRAPH-FACC;Lo;0;L;983B;;;;N;;;;;
+FACD;CJK COMPATIBILITY IDEOGRAPH-FACD;Lo;0;L;9B12;;;;N;;;;;
+FACE;CJK COMPATIBILITY IDEOGRAPH-FACE;Lo;0;L;9F9C;;;;N;;;;;
+FACF;CJK COMPATIBILITY IDEOGRAPH-FACF;Lo;0;L;2284A;;;;N;;;;;
+FAD0;CJK COMPATIBILITY IDEOGRAPH-FAD0;Lo;0;L;22844;;;;N;;;;;
+FAD1;CJK COMPATIBILITY IDEOGRAPH-FAD1;Lo;0;L;233D5;;;;N;;;;;
+FAD2;CJK COMPATIBILITY IDEOGRAPH-FAD2;Lo;0;L;3B9D;;;;N;;;;;
+FAD3;CJK COMPATIBILITY IDEOGRAPH-FAD3;Lo;0;L;4018;;;;N;;;;;
+FAD4;CJK COMPATIBILITY IDEOGRAPH-FAD4;Lo;0;L;4039;;;;N;;;;;
+FAD5;CJK COMPATIBILITY IDEOGRAPH-FAD5;Lo;0;L;25249;;;;N;;;;;
+FAD6;CJK COMPATIBILITY IDEOGRAPH-FAD6;Lo;0;L;25CD0;;;;N;;;;;
+FAD7;CJK COMPATIBILITY IDEOGRAPH-FAD7;Lo;0;L;27ED3;;;;N;;;;;
+FAD8;CJK COMPATIBILITY IDEOGRAPH-FAD8;Lo;0;L;9F43;;;;N;;;;;
+FAD9;CJK COMPATIBILITY IDEOGRAPH-FAD9;Lo;0;L;9F8E;;;;N;;;;;
+FB00;LATIN SMALL LIGATURE FF;Ll;0;L;<compat> 0066 0066;;;;N;;;;;
+FB01;LATIN SMALL LIGATURE FI;Ll;0;L;<compat> 0066 0069;;;;N;;;;;
+FB02;LATIN SMALL LIGATURE FL;Ll;0;L;<compat> 0066 006C;;;;N;;;;;
+FB03;LATIN SMALL LIGATURE FFI;Ll;0;L;<compat> 0066 0066 0069;;;;N;;;;;
+FB04;LATIN SMALL LIGATURE FFL;Ll;0;L;<compat> 0066 0066 006C;;;;N;;;;;
+FB05;LATIN SMALL LIGATURE LONG S T;Ll;0;L;<compat> 017F 0074;;;;N;;;;;
+FB06;LATIN SMALL LIGATURE ST;Ll;0;L;<compat> 0073 0074;;;;N;;;;;
+FB13;ARMENIAN SMALL LIGATURE MEN NOW;Ll;0;L;<compat> 0574 0576;;;;N;;;;;
+FB14;ARMENIAN SMALL LIGATURE MEN ECH;Ll;0;L;<compat> 0574 0565;;;;N;;;;;
+FB15;ARMENIAN SMALL LIGATURE MEN INI;Ll;0;L;<compat> 0574 056B;;;;N;;;;;
+FB16;ARMENIAN SMALL LIGATURE VEW NOW;Ll;0;L;<compat> 057E 0576;;;;N;;;;;
+FB17;ARMENIAN SMALL LIGATURE MEN XEH;Ll;0;L;<compat> 0574 056D;;;;N;;;;;
+FB1D;HEBREW LETTER YOD WITH HIRIQ;Lo;0;R;05D9 05B4;;;;N;;;;;
+FB1E;HEBREW POINT JUDEO-SPANISH VARIKA;Mn;26;NSM;;;;;N;HEBREW POINT VARIKA;;;;
+FB1F;HEBREW LIGATURE YIDDISH YOD YOD PATAH;Lo;0;R;05F2 05B7;;;;N;;;;;
+FB20;HEBREW LETTER ALTERNATIVE AYIN;Lo;0;R;<font> 05E2;;;;N;;;;;
+FB21;HEBREW LETTER WIDE ALEF;Lo;0;R;<font> 05D0;;;;N;;;;;
+FB22;HEBREW LETTER WIDE DALET;Lo;0;R;<font> 05D3;;;;N;;;;;
+FB23;HEBREW LETTER WIDE HE;Lo;0;R;<font> 05D4;;;;N;;;;;
+FB24;HEBREW LETTER WIDE KAF;Lo;0;R;<font> 05DB;;;;N;;;;;
+FB25;HEBREW LETTER WIDE LAMED;Lo;0;R;<font> 05DC;;;;N;;;;;
+FB26;HEBREW LETTER WIDE FINAL MEM;Lo;0;R;<font> 05DD;;;;N;;;;;
+FB27;HEBREW LETTER WIDE RESH;Lo;0;R;<font> 05E8;;;;N;;;;;
+FB28;HEBREW LETTER WIDE TAV;Lo;0;R;<font> 05EA;;;;N;;;;;
+FB29;HEBREW LETTER ALTERNATIVE PLUS SIGN;Sm;0;ES;<font> 002B;;;;N;;;;;
+FB2A;HEBREW LETTER SHIN WITH SHIN DOT;Lo;0;R;05E9 05C1;;;;N;;;;;
+FB2B;HEBREW LETTER SHIN WITH SIN DOT;Lo;0;R;05E9 05C2;;;;N;;;;;
+FB2C;HEBREW LETTER SHIN WITH DAGESH AND SHIN DOT;Lo;0;R;FB49 05C1;;;;N;;;;;
+FB2D;HEBREW LETTER SHIN WITH DAGESH AND SIN DOT;Lo;0;R;FB49 05C2;;;;N;;;;;
+FB2E;HEBREW LETTER ALEF WITH PATAH;Lo;0;R;05D0 05B7;;;;N;;;;;
+FB2F;HEBREW LETTER ALEF WITH QAMATS;Lo;0;R;05D0 05B8;;;;N;;;;;
+FB30;HEBREW LETTER ALEF WITH MAPIQ;Lo;0;R;05D0 05BC;;;;N;;;;;
+FB31;HEBREW LETTER BET WITH DAGESH;Lo;0;R;05D1 05BC;;;;N;;;;;
+FB32;HEBREW LETTER GIMEL WITH DAGESH;Lo;0;R;05D2 05BC;;;;N;;;;;
+FB33;HEBREW LETTER DALET WITH DAGESH;Lo;0;R;05D3 05BC;;;;N;;;;;
+FB34;HEBREW LETTER HE WITH MAPIQ;Lo;0;R;05D4 05BC;;;;N;;;;;
+FB35;HEBREW LETTER VAV WITH DAGESH;Lo;0;R;05D5 05BC;;;;N;;;;;
+FB36;HEBREW LETTER ZAYIN WITH DAGESH;Lo;0;R;05D6 05BC;;;;N;;;;;
+FB38;HEBREW LETTER TET WITH DAGESH;Lo;0;R;05D8 05BC;;;;N;;;;;
+FB39;HEBREW LETTER YOD WITH DAGESH;Lo;0;R;05D9 05BC;;;;N;;;;;
+FB3A;HEBREW LETTER FINAL KAF WITH DAGESH;Lo;0;R;05DA 05BC;;;;N;;;;;
+FB3B;HEBREW LETTER KAF WITH DAGESH;Lo;0;R;05DB 05BC;;;;N;;;;;
+FB3C;HEBREW LETTER LAMED WITH DAGESH;Lo;0;R;05DC 05BC;;;;N;;;;;
+FB3E;HEBREW LETTER MEM WITH DAGESH;Lo;0;R;05DE 05BC;;;;N;;;;;
+FB40;HEBREW LETTER NUN WITH DAGESH;Lo;0;R;05E0 05BC;;;;N;;;;;
+FB41;HEBREW LETTER SAMEKH WITH DAGESH;Lo;0;R;05E1 05BC;;;;N;;;;;
+FB43;HEBREW LETTER FINAL PE WITH DAGESH;Lo;0;R;05E3 05BC;;;;N;;;;;
+FB44;HEBREW LETTER PE WITH DAGESH;Lo;0;R;05E4 05BC;;;;N;;;;;
+FB46;HEBREW LETTER TSADI WITH DAGESH;Lo;0;R;05E6 05BC;;;;N;;;;;
+FB47;HEBREW LETTER QOF WITH DAGESH;Lo;0;R;05E7 05BC;;;;N;;;;;
+FB48;HEBREW LETTER RESH WITH DAGESH;Lo;0;R;05E8 05BC;;;;N;;;;;
+FB49;HEBREW LETTER SHIN WITH DAGESH;Lo;0;R;05E9 05BC;;;;N;;;;;
+FB4A;HEBREW LETTER TAV WITH DAGESH;Lo;0;R;05EA 05BC;;;;N;;;;;
+FB4B;HEBREW LETTER VAV WITH HOLAM;Lo;0;R;05D5 05B9;;;;N;;;;;
+FB4C;HEBREW LETTER BET WITH RAFE;Lo;0;R;05D1 05BF;;;;N;;;;;
+FB4D;HEBREW LETTER KAF WITH RAFE;Lo;0;R;05DB 05BF;;;;N;;;;;
+FB4E;HEBREW LETTER PE WITH RAFE;Lo;0;R;05E4 05BF;;;;N;;;;;
+FB4F;HEBREW LIGATURE ALEF LAMED;Lo;0;R;<compat> 05D0 05DC;;;;N;;;;;
+FB50;ARABIC LETTER ALEF WASLA ISOLATED FORM;Lo;0;AL;<isolated> 0671;;;;N;;;;;
+FB51;ARABIC LETTER ALEF WASLA FINAL FORM;Lo;0;AL;<final> 0671;;;;N;;;;;
+FB52;ARABIC LETTER BEEH ISOLATED FORM;Lo;0;AL;<isolated> 067B;;;;N;;;;;
+FB53;ARABIC LETTER BEEH FINAL FORM;Lo;0;AL;<final> 067B;;;;N;;;;;
+FB54;ARABIC LETTER BEEH INITIAL FORM;Lo;0;AL;<initial> 067B;;;;N;;;;;
+FB55;ARABIC LETTER BEEH MEDIAL FORM;Lo;0;AL;<medial> 067B;;;;N;;;;;
+FB56;ARABIC LETTER PEH ISOLATED FORM;Lo;0;AL;<isolated> 067E;;;;N;;;;;
+FB57;ARABIC LETTER PEH FINAL FORM;Lo;0;AL;<final> 067E;;;;N;;;;;
+FB58;ARABIC LETTER PEH INITIAL FORM;Lo;0;AL;<initial> 067E;;;;N;;;;;
+FB59;ARABIC LETTER PEH MEDIAL FORM;Lo;0;AL;<medial> 067E;;;;N;;;;;
+FB5A;ARABIC LETTER BEHEH ISOLATED FORM;Lo;0;AL;<isolated> 0680;;;;N;;;;;
+FB5B;ARABIC LETTER BEHEH FINAL FORM;Lo;0;AL;<final> 0680;;;;N;;;;;
+FB5C;ARABIC LETTER BEHEH INITIAL FORM;Lo;0;AL;<initial> 0680;;;;N;;;;;
+FB5D;ARABIC LETTER BEHEH MEDIAL FORM;Lo;0;AL;<medial> 0680;;;;N;;;;;
+FB5E;ARABIC LETTER TTEHEH ISOLATED FORM;Lo;0;AL;<isolated> 067A;;;;N;;;;;
+FB5F;ARABIC LETTER TTEHEH FINAL FORM;Lo;0;AL;<final> 067A;;;;N;;;;;
+FB60;ARABIC LETTER TTEHEH INITIAL FORM;Lo;0;AL;<initial> 067A;;;;N;;;;;
+FB61;ARABIC LETTER TTEHEH MEDIAL FORM;Lo;0;AL;<medial> 067A;;;;N;;;;;
+FB62;ARABIC LETTER TEHEH ISOLATED FORM;Lo;0;AL;<isolated> 067F;;;;N;;;;;
+FB63;ARABIC LETTER TEHEH FINAL FORM;Lo;0;AL;<final> 067F;;;;N;;;;;
+FB64;ARABIC LETTER TEHEH INITIAL FORM;Lo;0;AL;<initial> 067F;;;;N;;;;;
+FB65;ARABIC LETTER TEHEH MEDIAL FORM;Lo;0;AL;<medial> 067F;;;;N;;;;;
+FB66;ARABIC LETTER TTEH ISOLATED FORM;Lo;0;AL;<isolated> 0679;;;;N;;;;;
+FB67;ARABIC LETTER TTEH FINAL FORM;Lo;0;AL;<final> 0679;;;;N;;;;;
+FB68;ARABIC LETTER TTEH INITIAL FORM;Lo;0;AL;<initial> 0679;;;;N;;;;;
+FB69;ARABIC LETTER TTEH MEDIAL FORM;Lo;0;AL;<medial> 0679;;;;N;;;;;
+FB6A;ARABIC LETTER VEH ISOLATED FORM;Lo;0;AL;<isolated> 06A4;;;;N;;;;;
+FB6B;ARABIC LETTER VEH FINAL FORM;Lo;0;AL;<final> 06A4;;;;N;;;;;
+FB6C;ARABIC LETTER VEH INITIAL FORM;Lo;0;AL;<initial> 06A4;;;;N;;;;;
+FB6D;ARABIC LETTER VEH MEDIAL FORM;Lo;0;AL;<medial> 06A4;;;;N;;;;;
+FB6E;ARABIC LETTER PEHEH ISOLATED FORM;Lo;0;AL;<isolated> 06A6;;;;N;;;;;
+FB6F;ARABIC LETTER PEHEH FINAL FORM;Lo;0;AL;<final> 06A6;;;;N;;;;;
+FB70;ARABIC LETTER PEHEH INITIAL FORM;Lo;0;AL;<initial> 06A6;;;;N;;;;;
+FB71;ARABIC LETTER PEHEH MEDIAL FORM;Lo;0;AL;<medial> 06A6;;;;N;;;;;
+FB72;ARABIC LETTER DYEH ISOLATED FORM;Lo;0;AL;<isolated> 0684;;;;N;;;;;
+FB73;ARABIC LETTER DYEH FINAL FORM;Lo;0;AL;<final> 0684;;;;N;;;;;
+FB74;ARABIC LETTER DYEH INITIAL FORM;Lo;0;AL;<initial> 0684;;;;N;;;;;
+FB75;ARABIC LETTER DYEH MEDIAL FORM;Lo;0;AL;<medial> 0684;;;;N;;;;;
+FB76;ARABIC LETTER NYEH ISOLATED FORM;Lo;0;AL;<isolated> 0683;;;;N;;;;;
+FB77;ARABIC LETTER NYEH FINAL FORM;Lo;0;AL;<final> 0683;;;;N;;;;;
+FB78;ARABIC LETTER NYEH INITIAL FORM;Lo;0;AL;<initial> 0683;;;;N;;;;;
+FB79;ARABIC LETTER NYEH MEDIAL FORM;Lo;0;AL;<medial> 0683;;;;N;;;;;
+FB7A;ARABIC LETTER TCHEH ISOLATED FORM;Lo;0;AL;<isolated> 0686;;;;N;;;;;
+FB7B;ARABIC LETTER TCHEH FINAL FORM;Lo;0;AL;<final> 0686;;;;N;;;;;
+FB7C;ARABIC LETTER TCHEH INITIAL FORM;Lo;0;AL;<initial> 0686;;;;N;;;;;
+FB7D;ARABIC LETTER TCHEH MEDIAL FORM;Lo;0;AL;<medial> 0686;;;;N;;;;;
+FB7E;ARABIC LETTER TCHEHEH ISOLATED FORM;Lo;0;AL;<isolated> 0687;;;;N;;;;;
+FB7F;ARABIC LETTER TCHEHEH FINAL FORM;Lo;0;AL;<final> 0687;;;;N;;;;;
+FB80;ARABIC LETTER TCHEHEH INITIAL FORM;Lo;0;AL;<initial> 0687;;;;N;;;;;
+FB81;ARABIC LETTER TCHEHEH MEDIAL FORM;Lo;0;AL;<medial> 0687;;;;N;;;;;
+FB82;ARABIC LETTER DDAHAL ISOLATED FORM;Lo;0;AL;<isolated> 068D;;;;N;;;;;
+FB83;ARABIC LETTER DDAHAL FINAL FORM;Lo;0;AL;<final> 068D;;;;N;;;;;
+FB84;ARABIC LETTER DAHAL ISOLATED FORM;Lo;0;AL;<isolated> 068C;;;;N;;;;;
+FB85;ARABIC LETTER DAHAL FINAL FORM;Lo;0;AL;<final> 068C;;;;N;;;;;
+FB86;ARABIC LETTER DUL ISOLATED FORM;Lo;0;AL;<isolated> 068E;;;;N;;;;;
+FB87;ARABIC LETTER DUL FINAL FORM;Lo;0;AL;<final> 068E;;;;N;;;;;
+FB88;ARABIC LETTER DDAL ISOLATED FORM;Lo;0;AL;<isolated> 0688;;;;N;;;;;
+FB89;ARABIC LETTER DDAL FINAL FORM;Lo;0;AL;<final> 0688;;;;N;;;;;
+FB8A;ARABIC LETTER JEH ISOLATED FORM;Lo;0;AL;<isolated> 0698;;;;N;;;;;
+FB8B;ARABIC LETTER JEH FINAL FORM;Lo;0;AL;<final> 0698;;;;N;;;;;
+FB8C;ARABIC LETTER RREH ISOLATED FORM;Lo;0;AL;<isolated> 0691;;;;N;;;;;
+FB8D;ARABIC LETTER RREH FINAL FORM;Lo;0;AL;<final> 0691;;;;N;;;;;
+FB8E;ARABIC LETTER KEHEH ISOLATED FORM;Lo;0;AL;<isolated> 06A9;;;;N;;;;;
+FB8F;ARABIC LETTER KEHEH FINAL FORM;Lo;0;AL;<final> 06A9;;;;N;;;;;
+FB90;ARABIC LETTER KEHEH INITIAL FORM;Lo;0;AL;<initial> 06A9;;;;N;;;;;
+FB91;ARABIC LETTER KEHEH MEDIAL FORM;Lo;0;AL;<medial> 06A9;;;;N;;;;;
+FB92;ARABIC LETTER GAF ISOLATED FORM;Lo;0;AL;<isolated> 06AF;;;;N;;;;;
+FB93;ARABIC LETTER GAF FINAL FORM;Lo;0;AL;<final> 06AF;;;;N;;;;;
+FB94;ARABIC LETTER GAF INITIAL FORM;Lo;0;AL;<initial> 06AF;;;;N;;;;;
+FB95;ARABIC LETTER GAF MEDIAL FORM;Lo;0;AL;<medial> 06AF;;;;N;;;;;
+FB96;ARABIC LETTER GUEH ISOLATED FORM;Lo;0;AL;<isolated> 06B3;;;;N;;;;;
+FB97;ARABIC LETTER GUEH FINAL FORM;Lo;0;AL;<final> 06B3;;;;N;;;;;
+FB98;ARABIC LETTER GUEH INITIAL FORM;Lo;0;AL;<initial> 06B3;;;;N;;;;;
+FB99;ARABIC LETTER GUEH MEDIAL FORM;Lo;0;AL;<medial> 06B3;;;;N;;;;;
+FB9A;ARABIC LETTER NGOEH ISOLATED FORM;Lo;0;AL;<isolated> 06B1;;;;N;;;;;
+FB9B;ARABIC LETTER NGOEH FINAL FORM;Lo;0;AL;<final> 06B1;;;;N;;;;;
+FB9C;ARABIC LETTER NGOEH INITIAL FORM;Lo;0;AL;<initial> 06B1;;;;N;;;;;
+FB9D;ARABIC LETTER NGOEH MEDIAL FORM;Lo;0;AL;<medial> 06B1;;;;N;;;;;
+FB9E;ARABIC LETTER NOON GHUNNA ISOLATED FORM;Lo;0;AL;<isolated> 06BA;;;;N;;;;;
+FB9F;ARABIC LETTER NOON GHUNNA FINAL FORM;Lo;0;AL;<final> 06BA;;;;N;;;;;
+FBA0;ARABIC LETTER RNOON ISOLATED FORM;Lo;0;AL;<isolated> 06BB;;;;N;;;;;
+FBA1;ARABIC LETTER RNOON FINAL FORM;Lo;0;AL;<final> 06BB;;;;N;;;;;
+FBA2;ARABIC LETTER RNOON INITIAL FORM;Lo;0;AL;<initial> 06BB;;;;N;;;;;
+FBA3;ARABIC LETTER RNOON MEDIAL FORM;Lo;0;AL;<medial> 06BB;;;;N;;;;;
+FBA4;ARABIC LETTER HEH WITH YEH ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 06C0;;;;N;;;;;
+FBA5;ARABIC LETTER HEH WITH YEH ABOVE FINAL FORM;Lo;0;AL;<final> 06C0;;;;N;;;;;
+FBA6;ARABIC LETTER HEH GOAL ISOLATED FORM;Lo;0;AL;<isolated> 06C1;;;;N;;;;;
+FBA7;ARABIC LETTER HEH GOAL FINAL FORM;Lo;0;AL;<final> 06C1;;;;N;;;;;
+FBA8;ARABIC LETTER HEH GOAL INITIAL FORM;Lo;0;AL;<initial> 06C1;;;;N;;;;;
+FBA9;ARABIC LETTER HEH GOAL MEDIAL FORM;Lo;0;AL;<medial> 06C1;;;;N;;;;;
+FBAA;ARABIC LETTER HEH DOACHASHMEE ISOLATED FORM;Lo;0;AL;<isolated> 06BE;;;;N;;;;;
+FBAB;ARABIC LETTER HEH DOACHASHMEE FINAL FORM;Lo;0;AL;<final> 06BE;;;;N;;;;;
+FBAC;ARABIC LETTER HEH DOACHASHMEE INITIAL FORM;Lo;0;AL;<initial> 06BE;;;;N;;;;;
+FBAD;ARABIC LETTER HEH DOACHASHMEE MEDIAL FORM;Lo;0;AL;<medial> 06BE;;;;N;;;;;
+FBAE;ARABIC LETTER YEH BARREE ISOLATED FORM;Lo;0;AL;<isolated> 06D2;;;;N;;;;;
+FBAF;ARABIC LETTER YEH BARREE FINAL FORM;Lo;0;AL;<final> 06D2;;;;N;;;;;
+FBB0;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 06D3;;;;N;;;;;
+FBB1;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 06D3;;;;N;;;;;
+FBB2;ARABIC SYMBOL DOT ABOVE;Sk;0;AL;;;;;N;;;;;
+FBB3;ARABIC SYMBOL DOT BELOW;Sk;0;AL;;;;;N;;;;;
+FBB4;ARABIC SYMBOL TWO DOTS ABOVE;Sk;0;AL;;;;;N;;;;;
+FBB5;ARABIC SYMBOL TWO DOTS BELOW;Sk;0;AL;;;;;N;;;;;
+FBB6;ARABIC SYMBOL THREE DOTS ABOVE;Sk;0;AL;;;;;N;;;;;
+FBB7;ARABIC SYMBOL THREE DOTS BELOW;Sk;0;AL;;;;;N;;;;;
+FBB8;ARABIC SYMBOL THREE DOTS POINTING DOWNWARDS ABOVE;Sk;0;AL;;;;;N;;;;;
+FBB9;ARABIC SYMBOL THREE DOTS POINTING DOWNWARDS BELOW;Sk;0;AL;;;;;N;;;;;
+FBBA;ARABIC SYMBOL FOUR DOTS ABOVE;Sk;0;AL;;;;;N;;;;;
+FBBB;ARABIC SYMBOL FOUR DOTS BELOW;Sk;0;AL;;;;;N;;;;;
+FBBC;ARABIC SYMBOL DOUBLE VERTICAL BAR BELOW;Sk;0;AL;;;;;N;;;;;
+FBBD;ARABIC SYMBOL TWO DOTS VERTICALLY ABOVE;Sk;0;AL;;;;;N;;;;;
+FBBE;ARABIC SYMBOL TWO DOTS VERTICALLY BELOW;Sk;0;AL;;;;;N;;;;;
+FBBF;ARABIC SYMBOL RING;Sk;0;AL;;;;;N;;;;;
+FBC0;ARABIC SYMBOL SMALL TAH ABOVE;Sk;0;AL;;;;;N;;;;;
+FBC1;ARABIC SYMBOL SMALL TAH BELOW;Sk;0;AL;;;;;N;;;;;
+FBD3;ARABIC LETTER NG ISOLATED FORM;Lo;0;AL;<isolated> 06AD;;;;N;;;;;
+FBD4;ARABIC LETTER NG FINAL FORM;Lo;0;AL;<final> 06AD;;;;N;;;;;
+FBD5;ARABIC LETTER NG INITIAL FORM;Lo;0;AL;<initial> 06AD;;;;N;;;;;
+FBD6;ARABIC LETTER NG MEDIAL FORM;Lo;0;AL;<medial> 06AD;;;;N;;;;;
+FBD7;ARABIC LETTER U ISOLATED FORM;Lo;0;AL;<isolated> 06C7;;;;N;;;;;
+FBD8;ARABIC LETTER U FINAL FORM;Lo;0;AL;<final> 06C7;;;;N;;;;;
+FBD9;ARABIC LETTER OE ISOLATED FORM;Lo;0;AL;<isolated> 06C6;;;;N;;;;;
+FBDA;ARABIC LETTER OE FINAL FORM;Lo;0;AL;<final> 06C6;;;;N;;;;;
+FBDB;ARABIC LETTER YU ISOLATED FORM;Lo;0;AL;<isolated> 06C8;;;;N;;;;;
+FBDC;ARABIC LETTER YU FINAL FORM;Lo;0;AL;<final> 06C8;;;;N;;;;;
+FBDD;ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0677;;;;N;;;;;
+FBDE;ARABIC LETTER VE ISOLATED FORM;Lo;0;AL;<isolated> 06CB;;;;N;;;;;
+FBDF;ARABIC LETTER VE FINAL FORM;Lo;0;AL;<final> 06CB;;;;N;;;;;
+FBE0;ARABIC LETTER KIRGHIZ OE ISOLATED FORM;Lo;0;AL;<isolated> 06C5;;;;N;;;;;
+FBE1;ARABIC LETTER KIRGHIZ OE FINAL FORM;Lo;0;AL;<final> 06C5;;;;N;;;;;
+FBE2;ARABIC LETTER KIRGHIZ YU ISOLATED FORM;Lo;0;AL;<isolated> 06C9;;;;N;;;;;
+FBE3;ARABIC LETTER KIRGHIZ YU FINAL FORM;Lo;0;AL;<final> 06C9;;;;N;;;;;
+FBE4;ARABIC LETTER E ISOLATED FORM;Lo;0;AL;<isolated> 06D0;;;;N;;;;;
+FBE5;ARABIC LETTER E FINAL FORM;Lo;0;AL;<final> 06D0;;;;N;;;;;
+FBE6;ARABIC LETTER E INITIAL FORM;Lo;0;AL;<initial> 06D0;;;;N;;;;;
+FBE7;ARABIC LETTER E MEDIAL FORM;Lo;0;AL;<medial> 06D0;;;;N;;;;;
+FBE8;ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA INITIAL FORM;Lo;0;AL;<initial> 0649;;;;N;;;;;
+FBE9;ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA MEDIAL FORM;Lo;0;AL;<medial> 0649;;;;N;;;;;
+FBEA;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0626 0627;;;;N;;;;;
+FBEB;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF FINAL FORM;Lo;0;AL;<final> 0626 0627;;;;N;;;;;
+FBEC;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE ISOLATED FORM;Lo;0;AL;<isolated> 0626 06D5;;;;N;;;;;
+FBED;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE FINAL FORM;Lo;0;AL;<final> 0626 06D5;;;;N;;;;;
+FBEE;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW ISOLATED FORM;Lo;0;AL;<isolated> 0626 0648;;;;N;;;;;
+FBEF;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW FINAL FORM;Lo;0;AL;<final> 0626 0648;;;;N;;;;;
+FBF0;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U ISOLATED FORM;Lo;0;AL;<isolated> 0626 06C7;;;;N;;;;;
+FBF1;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U FINAL FORM;Lo;0;AL;<final> 0626 06C7;;;;N;;;;;
+FBF2;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE ISOLATED FORM;Lo;0;AL;<isolated> 0626 06C6;;;;N;;;;;
+FBF3;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE FINAL FORM;Lo;0;AL;<final> 0626 06C6;;;;N;;;;;
+FBF4;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU ISOLATED FORM;Lo;0;AL;<isolated> 0626 06C8;;;;N;;;;;
+FBF5;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU FINAL FORM;Lo;0;AL;<final> 0626 06C8;;;;N;;;;;
+FBF6;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E ISOLATED FORM;Lo;0;AL;<isolated> 0626 06D0;;;;N;;;;;
+FBF7;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E FINAL FORM;Lo;0;AL;<final> 0626 06D0;;;;N;;;;;
+FBF8;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E INITIAL FORM;Lo;0;AL;<initial> 0626 06D0;;;;N;;;;;
+FBF9;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0626 0649;;;;N;;;;;
+FBFA;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0626 0649;;;;N;;;;;
+FBFB;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA INITIAL FORM;Lo;0;AL;<initial> 0626 0649;;;;N;;;;;
+FBFC;ARABIC LETTER FARSI YEH ISOLATED FORM;Lo;0;AL;<isolated> 06CC;;;;N;;;;;
+FBFD;ARABIC LETTER FARSI YEH FINAL FORM;Lo;0;AL;<final> 06CC;;;;N;;;;;
+FBFE;ARABIC LETTER FARSI YEH INITIAL FORM;Lo;0;AL;<initial> 06CC;;;;N;;;;;
+FBFF;ARABIC LETTER FARSI YEH MEDIAL FORM;Lo;0;AL;<medial> 06CC;;;;N;;;;;
+FC00;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0626 062C;;;;N;;;;;
+FC01;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0626 062D;;;;N;;;;;
+FC02;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0626 0645;;;;N;;;;;
+FC03;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0626 0649;;;;N;;;;;
+FC04;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0626 064A;;;;N;;;;;
+FC05;ARABIC LIGATURE BEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0628 062C;;;;N;;;;;
+FC06;ARABIC LIGATURE BEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0628 062D;;;;N;;;;;
+FC07;ARABIC LIGATURE BEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0628 062E;;;;N;;;;;
+FC08;ARABIC LIGATURE BEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0628 0645;;;;N;;;;;
+FC09;ARABIC LIGATURE BEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0628 0649;;;;N;;;;;
+FC0A;ARABIC LIGATURE BEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0628 064A;;;;N;;;;;
+FC0B;ARABIC LIGATURE TEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062A 062C;;;;N;;;;;
+FC0C;ARABIC LIGATURE TEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 062A 062D;;;;N;;;;;
+FC0D;ARABIC LIGATURE TEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 062A 062E;;;;N;;;;;
+FC0E;ARABIC LIGATURE TEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062A 0645;;;;N;;;;;
+FC0F;ARABIC LIGATURE TEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062A 0649;;;;N;;;;;
+FC10;ARABIC LIGATURE TEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062A 064A;;;;N;;;;;
+FC11;ARABIC LIGATURE THEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062B 062C;;;;N;;;;;
+FC12;ARABIC LIGATURE THEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062B 0645;;;;N;;;;;
+FC13;ARABIC LIGATURE THEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062B 0649;;;;N;;;;;
+FC14;ARABIC LIGATURE THEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062B 064A;;;;N;;;;;
+FC15;ARABIC LIGATURE JEEM WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 062C 062D;;;;N;;;;;
+FC16;ARABIC LIGATURE JEEM WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062C 0645;;;;N;;;;;
+FC17;ARABIC LIGATURE HAH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062D 062C;;;;N;;;;;
+FC18;ARABIC LIGATURE HAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062D 0645;;;;N;;;;;
+FC19;ARABIC LIGATURE KHAH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062E 062C;;;;N;;;;;
+FC1A;ARABIC LIGATURE KHAH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 062E 062D;;;;N;;;;;
+FC1B;ARABIC LIGATURE KHAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062E 0645;;;;N;;;;;
+FC1C;ARABIC LIGATURE SEEN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0633 062C;;;;N;;;;;
+FC1D;ARABIC LIGATURE SEEN WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0633 062D;;;;N;;;;;
+FC1E;ARABIC LIGATURE SEEN WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0633 062E;;;;N;;;;;
+FC1F;ARABIC LIGATURE SEEN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0633 0645;;;;N;;;;;
+FC20;ARABIC LIGATURE SAD WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0635 062D;;;;N;;;;;
+FC21;ARABIC LIGATURE SAD WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0635 0645;;;;N;;;;;
+FC22;ARABIC LIGATURE DAD WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0636 062C;;;;N;;;;;
+FC23;ARABIC LIGATURE DAD WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0636 062D;;;;N;;;;;
+FC24;ARABIC LIGATURE DAD WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0636 062E;;;;N;;;;;
+FC25;ARABIC LIGATURE DAD WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0636 0645;;;;N;;;;;
+FC26;ARABIC LIGATURE TAH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0637 062D;;;;N;;;;;
+FC27;ARABIC LIGATURE TAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0637 0645;;;;N;;;;;
+FC28;ARABIC LIGATURE ZAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0638 0645;;;;N;;;;;
+FC29;ARABIC LIGATURE AIN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0639 062C;;;;N;;;;;
+FC2A;ARABIC LIGATURE AIN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0639 0645;;;;N;;;;;
+FC2B;ARABIC LIGATURE GHAIN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 063A 062C;;;;N;;;;;
+FC2C;ARABIC LIGATURE GHAIN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 063A 0645;;;;N;;;;;
+FC2D;ARABIC LIGATURE FEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0641 062C;;;;N;;;;;
+FC2E;ARABIC LIGATURE FEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0641 062D;;;;N;;;;;
+FC2F;ARABIC LIGATURE FEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0641 062E;;;;N;;;;;
+FC30;ARABIC LIGATURE FEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0641 0645;;;;N;;;;;
+FC31;ARABIC LIGATURE FEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0641 0649;;;;N;;;;;
+FC32;ARABIC LIGATURE FEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0641 064A;;;;N;;;;;
+FC33;ARABIC LIGATURE QAF WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0642 062D;;;;N;;;;;
+FC34;ARABIC LIGATURE QAF WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0642 0645;;;;N;;;;;
+FC35;ARABIC LIGATURE QAF WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0642 0649;;;;N;;;;;
+FC36;ARABIC LIGATURE QAF WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0642 064A;;;;N;;;;;
+FC37;ARABIC LIGATURE KAF WITH ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0643 0627;;;;N;;;;;
+FC38;ARABIC LIGATURE KAF WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0643 062C;;;;N;;;;;
+FC39;ARABIC LIGATURE KAF WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0643 062D;;;;N;;;;;
+FC3A;ARABIC LIGATURE KAF WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0643 062E;;;;N;;;;;
+FC3B;ARABIC LIGATURE KAF WITH LAM ISOLATED FORM;Lo;0;AL;<isolated> 0643 0644;;;;N;;;;;
+FC3C;ARABIC LIGATURE KAF WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0643 0645;;;;N;;;;;
+FC3D;ARABIC LIGATURE KAF WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0643 0649;;;;N;;;;;
+FC3E;ARABIC LIGATURE KAF WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0643 064A;;;;N;;;;;
+FC3F;ARABIC LIGATURE LAM WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0644 062C;;;;N;;;;;
+FC40;ARABIC LIGATURE LAM WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0644 062D;;;;N;;;;;
+FC41;ARABIC LIGATURE LAM WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0644 062E;;;;N;;;;;
+FC42;ARABIC LIGATURE LAM WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0644 0645;;;;N;;;;;
+FC43;ARABIC LIGATURE LAM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0644 0649;;;;N;;;;;
+FC44;ARABIC LIGATURE LAM WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0644 064A;;;;N;;;;;
+FC45;ARABIC LIGATURE MEEM WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0645 062C;;;;N;;;;;
+FC46;ARABIC LIGATURE MEEM WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0645 062D;;;;N;;;;;
+FC47;ARABIC LIGATURE MEEM WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0645 062E;;;;N;;;;;
+FC48;ARABIC LIGATURE MEEM WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0645 0645;;;;N;;;;;
+FC49;ARABIC LIGATURE MEEM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0645 0649;;;;N;;;;;
+FC4A;ARABIC LIGATURE MEEM WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0645 064A;;;;N;;;;;
+FC4B;ARABIC LIGATURE NOON WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0646 062C;;;;N;;;;;
+FC4C;ARABIC LIGATURE NOON WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0646 062D;;;;N;;;;;
+FC4D;ARABIC LIGATURE NOON WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0646 062E;;;;N;;;;;
+FC4E;ARABIC LIGATURE NOON WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0646 0645;;;;N;;;;;
+FC4F;ARABIC LIGATURE NOON WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0646 0649;;;;N;;;;;
+FC50;ARABIC LIGATURE NOON WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0646 064A;;;;N;;;;;
+FC51;ARABIC LIGATURE HEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0647 062C;;;;N;;;;;
+FC52;ARABIC LIGATURE HEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0647 0645;;;;N;;;;;
+FC53;ARABIC LIGATURE HEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0647 0649;;;;N;;;;;
+FC54;ARABIC LIGATURE HEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0647 064A;;;;N;;;;;
+FC55;ARABIC LIGATURE YEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 064A 062C;;;;N;;;;;
+FC56;ARABIC LIGATURE YEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 064A 062D;;;;N;;;;;
+FC57;ARABIC LIGATURE YEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 064A 062E;;;;N;;;;;
+FC58;ARABIC LIGATURE YEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 064A 0645;;;;N;;;;;
+FC59;ARABIC LIGATURE YEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 064A 0649;;;;N;;;;;
+FC5A;ARABIC LIGATURE YEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 064A 064A;;;;N;;;;;
+FC5B;ARABIC LIGATURE THAL WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0630 0670;;;;N;;;;;
+FC5C;ARABIC LIGATURE REH WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0631 0670;;;;N;;;;;
+FC5D;ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0649 0670;;;;N;;;;;
+FC5E;ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064C 0651;;;;N;;;;;
+FC5F;ARABIC LIGATURE SHADDA WITH KASRATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064D 0651;;;;N;;;;;
+FC60;ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064E 0651;;;;N;;;;;
+FC61;ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064F 0651;;;;N;;;;;
+FC62;ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM;Lo;0;AL;<isolated> 0020 0650 0651;;;;N;;;;;
+FC63;ARABIC LIGATURE SHADDA WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0020 0651 0670;;;;N;;;;;
+FC64;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH REH FINAL FORM;Lo;0;AL;<final> 0626 0631;;;;N;;;;;
+FC65;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ZAIN FINAL FORM;Lo;0;AL;<final> 0626 0632;;;;N;;;;;
+FC66;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM FINAL FORM;Lo;0;AL;<final> 0626 0645;;;;N;;;;;
+FC67;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH NOON FINAL FORM;Lo;0;AL;<final> 0626 0646;;;;N;;;;;
+FC68;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0626 0649;;;;N;;;;;
+FC69;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH FINAL FORM;Lo;0;AL;<final> 0626 064A;;;;N;;;;;
+FC6A;ARABIC LIGATURE BEH WITH REH FINAL FORM;Lo;0;AL;<final> 0628 0631;;;;N;;;;;
+FC6B;ARABIC LIGATURE BEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 0628 0632;;;;N;;;;;
+FC6C;ARABIC LIGATURE BEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0628 0645;;;;N;;;;;
+FC6D;ARABIC LIGATURE BEH WITH NOON FINAL FORM;Lo;0;AL;<final> 0628 0646;;;;N;;;;;
+FC6E;ARABIC LIGATURE BEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0628 0649;;;;N;;;;;
+FC6F;ARABIC LIGATURE BEH WITH YEH FINAL FORM;Lo;0;AL;<final> 0628 064A;;;;N;;;;;
+FC70;ARABIC LIGATURE TEH WITH REH FINAL FORM;Lo;0;AL;<final> 062A 0631;;;;N;;;;;
+FC71;ARABIC LIGATURE TEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 062A 0632;;;;N;;;;;
+FC72;ARABIC LIGATURE TEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 062A 0645;;;;N;;;;;
+FC73;ARABIC LIGATURE TEH WITH NOON FINAL FORM;Lo;0;AL;<final> 062A 0646;;;;N;;;;;
+FC74;ARABIC LIGATURE TEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 0649;;;;N;;;;;
+FC75;ARABIC LIGATURE TEH WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 064A;;;;N;;;;;
+FC76;ARABIC LIGATURE THEH WITH REH FINAL FORM;Lo;0;AL;<final> 062B 0631;;;;N;;;;;
+FC77;ARABIC LIGATURE THEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 062B 0632;;;;N;;;;;
+FC78;ARABIC LIGATURE THEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 062B 0645;;;;N;;;;;
+FC79;ARABIC LIGATURE THEH WITH NOON FINAL FORM;Lo;0;AL;<final> 062B 0646;;;;N;;;;;
+FC7A;ARABIC LIGATURE THEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062B 0649;;;;N;;;;;
+FC7B;ARABIC LIGATURE THEH WITH YEH FINAL FORM;Lo;0;AL;<final> 062B 064A;;;;N;;;;;
+FC7C;ARABIC LIGATURE FEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0641 0649;;;;N;;;;;
+FC7D;ARABIC LIGATURE FEH WITH YEH FINAL FORM;Lo;0;AL;<final> 0641 064A;;;;N;;;;;
+FC7E;ARABIC LIGATURE QAF WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0642 0649;;;;N;;;;;
+FC7F;ARABIC LIGATURE QAF WITH YEH FINAL FORM;Lo;0;AL;<final> 0642 064A;;;;N;;;;;
+FC80;ARABIC LIGATURE KAF WITH ALEF FINAL FORM;Lo;0;AL;<final> 0643 0627;;;;N;;;;;
+FC81;ARABIC LIGATURE KAF WITH LAM FINAL FORM;Lo;0;AL;<final> 0643 0644;;;;N;;;;;
+FC82;ARABIC LIGATURE KAF WITH MEEM FINAL FORM;Lo;0;AL;<final> 0643 0645;;;;N;;;;;
+FC83;ARABIC LIGATURE KAF WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0643 0649;;;;N;;;;;
+FC84;ARABIC LIGATURE KAF WITH YEH FINAL FORM;Lo;0;AL;<final> 0643 064A;;;;N;;;;;
+FC85;ARABIC LIGATURE LAM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 0645;;;;N;;;;;
+FC86;ARABIC LIGATURE LAM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0644 0649;;;;N;;;;;
+FC87;ARABIC LIGATURE LAM WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 064A;;;;N;;;;;
+FC88;ARABIC LIGATURE MEEM WITH ALEF FINAL FORM;Lo;0;AL;<final> 0645 0627;;;;N;;;;;
+FC89;ARABIC LIGATURE MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0645 0645;;;;N;;;;;
+FC8A;ARABIC LIGATURE NOON WITH REH FINAL FORM;Lo;0;AL;<final> 0646 0631;;;;N;;;;;
+FC8B;ARABIC LIGATURE NOON WITH ZAIN FINAL FORM;Lo;0;AL;<final> 0646 0632;;;;N;;;;;
+FC8C;ARABIC LIGATURE NOON WITH MEEM FINAL FORM;Lo;0;AL;<final> 0646 0645;;;;N;;;;;
+FC8D;ARABIC LIGATURE NOON WITH NOON FINAL FORM;Lo;0;AL;<final> 0646 0646;;;;N;;;;;
+FC8E;ARABIC LIGATURE NOON WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 0649;;;;N;;;;;
+FC8F;ARABIC LIGATURE NOON WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 064A;;;;N;;;;;
+FC90;ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF FINAL FORM;Lo;0;AL;<final> 0649 0670;;;;N;;;;;
+FC91;ARABIC LIGATURE YEH WITH REH FINAL FORM;Lo;0;AL;<final> 064A 0631;;;;N;;;;;
+FC92;ARABIC LIGATURE YEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 064A 0632;;;;N;;;;;
+FC93;ARABIC LIGATURE YEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 064A 0645;;;;N;;;;;
+FC94;ARABIC LIGATURE YEH WITH NOON FINAL FORM;Lo;0;AL;<final> 064A 0646;;;;N;;;;;
+FC95;ARABIC LIGATURE YEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 064A 0649;;;;N;;;;;
+FC96;ARABIC LIGATURE YEH WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 064A;;;;N;;;;;
+FC97;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0626 062C;;;;N;;;;;
+FC98;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0626 062D;;;;N;;;;;
+FC99;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0626 062E;;;;N;;;;;
+FC9A;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0626 0645;;;;N;;;;;
+FC9B;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0626 0647;;;;N;;;;;
+FC9C;ARABIC LIGATURE BEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0628 062C;;;;N;;;;;
+FC9D;ARABIC LIGATURE BEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0628 062D;;;;N;;;;;
+FC9E;ARABIC LIGATURE BEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0628 062E;;;;N;;;;;
+FC9F;ARABIC LIGATURE BEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0628 0645;;;;N;;;;;
+FCA0;ARABIC LIGATURE BEH WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0628 0647;;;;N;;;;;
+FCA1;ARABIC LIGATURE TEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062C;;;;N;;;;;
+FCA2;ARABIC LIGATURE TEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062A 062D;;;;N;;;;;
+FCA3;ARABIC LIGATURE TEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 062A 062E;;;;N;;;;;
+FCA4;ARABIC LIGATURE TEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 0645;;;;N;;;;;
+FCA5;ARABIC LIGATURE TEH WITH HEH INITIAL FORM;Lo;0;AL;<initial> 062A 0647;;;;N;;;;;
+FCA6;ARABIC LIGATURE THEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062B 0645;;;;N;;;;;
+FCA7;ARABIC LIGATURE JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062C 062D;;;;N;;;;;
+FCA8;ARABIC LIGATURE JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062C 0645;;;;N;;;;;
+FCA9;ARABIC LIGATURE HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062D 062C;;;;N;;;;;
+FCAA;ARABIC LIGATURE HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062D 0645;;;;N;;;;;
+FCAB;ARABIC LIGATURE KHAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062E 062C;;;;N;;;;;
+FCAC;ARABIC LIGATURE KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062E 0645;;;;N;;;;;
+FCAD;ARABIC LIGATURE SEEN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0633 062C;;;;N;;;;;
+FCAE;ARABIC LIGATURE SEEN WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0633 062D;;;;N;;;;;
+FCAF;ARABIC LIGATURE SEEN WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0633 062E;;;;N;;;;;
+FCB0;ARABIC LIGATURE SEEN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0633 0645;;;;N;;;;;
+FCB1;ARABIC LIGATURE SAD WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0635 062D;;;;N;;;;;
+FCB2;ARABIC LIGATURE SAD WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0635 062E;;;;N;;;;;
+FCB3;ARABIC LIGATURE SAD WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0635 0645;;;;N;;;;;
+FCB4;ARABIC LIGATURE DAD WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0636 062C;;;;N;;;;;
+FCB5;ARABIC LIGATURE DAD WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0636 062D;;;;N;;;;;
+FCB6;ARABIC LIGATURE DAD WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0636 062E;;;;N;;;;;
+FCB7;ARABIC LIGATURE DAD WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0636 0645;;;;N;;;;;
+FCB8;ARABIC LIGATURE TAH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0637 062D;;;;N;;;;;
+FCB9;ARABIC LIGATURE ZAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0638 0645;;;;N;;;;;
+FCBA;ARABIC LIGATURE AIN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0639 062C;;;;N;;;;;
+FCBB;ARABIC LIGATURE AIN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639 0645;;;;N;;;;;
+FCBC;ARABIC LIGATURE GHAIN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 063A 062C;;;;N;;;;;
+FCBD;ARABIC LIGATURE GHAIN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 063A 0645;;;;N;;;;;
+FCBE;ARABIC LIGATURE FEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0641 062C;;;;N;;;;;
+FCBF;ARABIC LIGATURE FEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0641 062D;;;;N;;;;;
+FCC0;ARABIC LIGATURE FEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0641 062E;;;;N;;;;;
+FCC1;ARABIC LIGATURE FEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0641 0645;;;;N;;;;;
+FCC2;ARABIC LIGATURE QAF WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0642 062D;;;;N;;;;;
+FCC3;ARABIC LIGATURE QAF WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0642 0645;;;;N;;;;;
+FCC4;ARABIC LIGATURE KAF WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0643 062C;;;;N;;;;;
+FCC5;ARABIC LIGATURE KAF WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0643 062D;;;;N;;;;;
+FCC6;ARABIC LIGATURE KAF WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0643 062E;;;;N;;;;;
+FCC7;ARABIC LIGATURE KAF WITH LAM INITIAL FORM;Lo;0;AL;<initial> 0643 0644;;;;N;;;;;
+FCC8;ARABIC LIGATURE KAF WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0643 0645;;;;N;;;;;
+FCC9;ARABIC LIGATURE LAM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062C;;;;N;;;;;
+FCCA;ARABIC LIGATURE LAM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0644 062D;;;;N;;;;;
+FCCB;ARABIC LIGATURE LAM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0644 062E;;;;N;;;;;
+FCCC;ARABIC LIGATURE LAM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 0645;;;;N;;;;;
+FCCD;ARABIC LIGATURE LAM WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0644 0647;;;;N;;;;;
+FCCE;ARABIC LIGATURE MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062C;;;;N;;;;;
+FCCF;ARABIC LIGATURE MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0645 062D;;;;N;;;;;
+FCD0;ARABIC LIGATURE MEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0645 062E;;;;N;;;;;
+FCD1;ARABIC LIGATURE MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 0645;;;;N;;;;;
+FCD2;ARABIC LIGATURE NOON WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0646 062C;;;;N;;;;;
+FCD3;ARABIC LIGATURE NOON WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0646 062D;;;;N;;;;;
+FCD4;ARABIC LIGATURE NOON WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0646 062E;;;;N;;;;;
+FCD5;ARABIC LIGATURE NOON WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0646 0645;;;;N;;;;;
+FCD6;ARABIC LIGATURE NOON WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0646 0647;;;;N;;;;;
+FCD7;ARABIC LIGATURE HEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0647 062C;;;;N;;;;;
+FCD8;ARABIC LIGATURE HEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645;;;;N;;;;;
+FCD9;ARABIC LIGATURE HEH WITH SUPERSCRIPT ALEF INITIAL FORM;Lo;0;AL;<initial> 0647 0670;;;;N;;;;;
+FCDA;ARABIC LIGATURE YEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 064A 062C;;;;N;;;;;
+FCDB;ARABIC LIGATURE YEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 064A 062D;;;;N;;;;;
+FCDC;ARABIC LIGATURE YEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 064A 062E;;;;N;;;;;
+FCDD;ARABIC LIGATURE YEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 064A 0645;;;;N;;;;;
+FCDE;ARABIC LIGATURE YEH WITH HEH INITIAL FORM;Lo;0;AL;<initial> 064A 0647;;;;N;;;;;
+FCDF;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0626 0645;;;;N;;;;;
+FCE0;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0626 0647;;;;N;;;;;
+FCE1;ARABIC LIGATURE BEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0628 0645;;;;N;;;;;
+FCE2;ARABIC LIGATURE BEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0628 0647;;;;N;;;;;
+FCE3;ARABIC LIGATURE TEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 062A 0645;;;;N;;;;;
+FCE4;ARABIC LIGATURE TEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 062A 0647;;;;N;;;;;
+FCE5;ARABIC LIGATURE THEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 062B 0645;;;;N;;;;;
+FCE6;ARABIC LIGATURE THEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 062B 0647;;;;N;;;;;
+FCE7;ARABIC LIGATURE SEEN WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0633 0645;;;;N;;;;;
+FCE8;ARABIC LIGATURE SEEN WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0633 0647;;;;N;;;;;
+FCE9;ARABIC LIGATURE SHEEN WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0634 0645;;;;N;;;;;
+FCEA;ARABIC LIGATURE SHEEN WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0634 0647;;;;N;;;;;
+FCEB;ARABIC LIGATURE KAF WITH LAM MEDIAL FORM;Lo;0;AL;<medial> 0643 0644;;;;N;;;;;
+FCEC;ARABIC LIGATURE KAF WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0643 0645;;;;N;;;;;
+FCED;ARABIC LIGATURE LAM WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0644 0645;;;;N;;;;;
+FCEE;ARABIC LIGATURE NOON WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0646 0645;;;;N;;;;;
+FCEF;ARABIC LIGATURE NOON WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0646 0647;;;;N;;;;;
+FCF0;ARABIC LIGATURE YEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 064A 0645;;;;N;;;;;
+FCF1;ARABIC LIGATURE YEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 064A 0647;;;;N;;;;;
+FCF2;ARABIC LIGATURE SHADDA WITH FATHA MEDIAL FORM;Lo;0;AL;<medial> 0640 064E 0651;;;;N;;;;;
+FCF3;ARABIC LIGATURE SHADDA WITH DAMMA MEDIAL FORM;Lo;0;AL;<medial> 0640 064F 0651;;;;N;;;;;
+FCF4;ARABIC LIGATURE SHADDA WITH KASRA MEDIAL FORM;Lo;0;AL;<medial> 0640 0650 0651;;;;N;;;;;
+FCF5;ARABIC LIGATURE TAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0637 0649;;;;N;;;;;
+FCF6;ARABIC LIGATURE TAH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0637 064A;;;;N;;;;;
+FCF7;ARABIC LIGATURE AIN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0639 0649;;;;N;;;;;
+FCF8;ARABIC LIGATURE AIN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0639 064A;;;;N;;;;;
+FCF9;ARABIC LIGATURE GHAIN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 063A 0649;;;;N;;;;;
+FCFA;ARABIC LIGATURE GHAIN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 063A 064A;;;;N;;;;;
+FCFB;ARABIC LIGATURE SEEN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0633 0649;;;;N;;;;;
+FCFC;ARABIC LIGATURE SEEN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0633 064A;;;;N;;;;;
+FCFD;ARABIC LIGATURE SHEEN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0634 0649;;;;N;;;;;
+FCFE;ARABIC LIGATURE SHEEN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0634 064A;;;;N;;;;;
+FCFF;ARABIC LIGATURE HAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062D 0649;;;;N;;;;;
+FD00;ARABIC LIGATURE HAH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062D 064A;;;;N;;;;;
+FD01;ARABIC LIGATURE JEEM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062C 0649;;;;N;;;;;
+FD02;ARABIC LIGATURE JEEM WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062C 064A;;;;N;;;;;
+FD03;ARABIC LIGATURE KHAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062E 0649;;;;N;;;;;
+FD04;ARABIC LIGATURE KHAH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062E 064A;;;;N;;;;;
+FD05;ARABIC LIGATURE SAD WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0635 0649;;;;N;;;;;
+FD06;ARABIC LIGATURE SAD WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0635 064A;;;;N;;;;;
+FD07;ARABIC LIGATURE DAD WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0636 0649;;;;N;;;;;
+FD08;ARABIC LIGATURE DAD WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0636 064A;;;;N;;;;;
+FD09;ARABIC LIGATURE SHEEN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0634 062C;;;;N;;;;;
+FD0A;ARABIC LIGATURE SHEEN WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0634 062D;;;;N;;;;;
+FD0B;ARABIC LIGATURE SHEEN WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0634 062E;;;;N;;;;;
+FD0C;ARABIC LIGATURE SHEEN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0634 0645;;;;N;;;;;
+FD0D;ARABIC LIGATURE SHEEN WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0634 0631;;;;N;;;;;
+FD0E;ARABIC LIGATURE SEEN WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0633 0631;;;;N;;;;;
+FD0F;ARABIC LIGATURE SAD WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0635 0631;;;;N;;;;;
+FD10;ARABIC LIGATURE DAD WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0636 0631;;;;N;;;;;
+FD11;ARABIC LIGATURE TAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0637 0649;;;;N;;;;;
+FD12;ARABIC LIGATURE TAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0637 064A;;;;N;;;;;
+FD13;ARABIC LIGATURE AIN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0639 0649;;;;N;;;;;
+FD14;ARABIC LIGATURE AIN WITH YEH FINAL FORM;Lo;0;AL;<final> 0639 064A;;;;N;;;;;
+FD15;ARABIC LIGATURE GHAIN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 063A 0649;;;;N;;;;;
+FD16;ARABIC LIGATURE GHAIN WITH YEH FINAL FORM;Lo;0;AL;<final> 063A 064A;;;;N;;;;;
+FD17;ARABIC LIGATURE SEEN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0633 0649;;;;N;;;;;
+FD18;ARABIC LIGATURE SEEN WITH YEH FINAL FORM;Lo;0;AL;<final> 0633 064A;;;;N;;;;;
+FD19;ARABIC LIGATURE SHEEN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0634 0649;;;;N;;;;;
+FD1A;ARABIC LIGATURE SHEEN WITH YEH FINAL FORM;Lo;0;AL;<final> 0634 064A;;;;N;;;;;
+FD1B;ARABIC LIGATURE HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062D 0649;;;;N;;;;;
+FD1C;ARABIC LIGATURE HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062D 064A;;;;N;;;;;
+FD1D;ARABIC LIGATURE JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062C 0649;;;;N;;;;;
+FD1E;ARABIC LIGATURE JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062C 064A;;;;N;;;;;
+FD1F;ARABIC LIGATURE KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062E 0649;;;;N;;;;;
+FD20;ARABIC LIGATURE KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062E 064A;;;;N;;;;;
+FD21;ARABIC LIGATURE SAD WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0635 0649;;;;N;;;;;
+FD22;ARABIC LIGATURE SAD WITH YEH FINAL FORM;Lo;0;AL;<final> 0635 064A;;;;N;;;;;
+FD23;ARABIC LIGATURE DAD WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0636 0649;;;;N;;;;;
+FD24;ARABIC LIGATURE DAD WITH YEH FINAL FORM;Lo;0;AL;<final> 0636 064A;;;;N;;;;;
+FD25;ARABIC LIGATURE SHEEN WITH JEEM FINAL FORM;Lo;0;AL;<final> 0634 062C;;;;N;;;;;
+FD26;ARABIC LIGATURE SHEEN WITH HAH FINAL FORM;Lo;0;AL;<final> 0634 062D;;;;N;;;;;
+FD27;ARABIC LIGATURE SHEEN WITH KHAH FINAL FORM;Lo;0;AL;<final> 0634 062E;;;;N;;;;;
+FD28;ARABIC LIGATURE SHEEN WITH MEEM FINAL FORM;Lo;0;AL;<final> 0634 0645;;;;N;;;;;
+FD29;ARABIC LIGATURE SHEEN WITH REH FINAL FORM;Lo;0;AL;<final> 0634 0631;;;;N;;;;;
+FD2A;ARABIC LIGATURE SEEN WITH REH FINAL FORM;Lo;0;AL;<final> 0633 0631;;;;N;;;;;
+FD2B;ARABIC LIGATURE SAD WITH REH FINAL FORM;Lo;0;AL;<final> 0635 0631;;;;N;;;;;
+FD2C;ARABIC LIGATURE DAD WITH REH FINAL FORM;Lo;0;AL;<final> 0636 0631;;;;N;;;;;
+FD2D;ARABIC LIGATURE SHEEN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0634 062C;;;;N;;;;;
+FD2E;ARABIC LIGATURE SHEEN WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0634 062D;;;;N;;;;;
+FD2F;ARABIC LIGATURE SHEEN WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0634 062E;;;;N;;;;;
+FD30;ARABIC LIGATURE SHEEN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0634 0645;;;;N;;;;;
+FD31;ARABIC LIGATURE SEEN WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0633 0647;;;;N;;;;;
+FD32;ARABIC LIGATURE SHEEN WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0634 0647;;;;N;;;;;
+FD33;ARABIC LIGATURE TAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0637 0645;;;;N;;;;;
+FD34;ARABIC LIGATURE SEEN WITH JEEM MEDIAL FORM;Lo;0;AL;<medial> 0633 062C;;;;N;;;;;
+FD35;ARABIC LIGATURE SEEN WITH HAH MEDIAL FORM;Lo;0;AL;<medial> 0633 062D;;;;N;;;;;
+FD36;ARABIC LIGATURE SEEN WITH KHAH MEDIAL FORM;Lo;0;AL;<medial> 0633 062E;;;;N;;;;;
+FD37;ARABIC LIGATURE SHEEN WITH JEEM MEDIAL FORM;Lo;0;AL;<medial> 0634 062C;;;;N;;;;;
+FD38;ARABIC LIGATURE SHEEN WITH HAH MEDIAL FORM;Lo;0;AL;<medial> 0634 062D;;;;N;;;;;
+FD39;ARABIC LIGATURE SHEEN WITH KHAH MEDIAL FORM;Lo;0;AL;<medial> 0634 062E;;;;N;;;;;
+FD3A;ARABIC LIGATURE TAH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0637 0645;;;;N;;;;;
+FD3B;ARABIC LIGATURE ZAH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0638 0645;;;;N;;;;;
+FD3C;ARABIC LIGATURE ALEF WITH FATHATAN FINAL FORM;Lo;0;AL;<final> 0627 064B;;;;N;;;;;
+FD3D;ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM;Lo;0;AL;<isolated> 0627 064B;;;;N;;;;;
+FD3E;ORNATE LEFT PARENTHESIS;Pe;0;ON;;;;;N;;;;;
+FD3F;ORNATE RIGHT PARENTHESIS;Ps;0;ON;;;;;N;;;;;
+FD50;ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062C 0645;;;;N;;;;;
+FD51;ARABIC LIGATURE TEH WITH HAH WITH JEEM FINAL FORM;Lo;0;AL;<final> 062A 062D 062C;;;;N;;;;;
+FD52;ARABIC LIGATURE TEH WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062D 062C;;;;N;;;;;
+FD53;ARABIC LIGATURE TEH WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062D 0645;;;;N;;;;;
+FD54;ARABIC LIGATURE TEH WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062E 0645;;;;N;;;;;
+FD55;ARABIC LIGATURE TEH WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062A 0645 062C;;;;N;;;;;
+FD56;ARABIC LIGATURE TEH WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062A 0645 062D;;;;N;;;;;
+FD57;ARABIC LIGATURE TEH WITH MEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 062A 0645 062E;;;;N;;;;;
+FD58;ARABIC LIGATURE JEEM WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 062C 0645 062D;;;;N;;;;;
+FD59;ARABIC LIGATURE JEEM WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062C 0645 062D;;;;N;;;;;
+FD5A;ARABIC LIGATURE HAH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062D 0645 064A;;;;N;;;;;
+FD5B;ARABIC LIGATURE HAH WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062D 0645 0649;;;;N;;;;;
+FD5C;ARABIC LIGATURE SEEN WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0633 062D 062C;;;;N;;;;;
+FD5D;ARABIC LIGATURE SEEN WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0633 062C 062D;;;;N;;;;;
+FD5E;ARABIC LIGATURE SEEN WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0633 062C 0649;;;;N;;;;;
+FD5F;ARABIC LIGATURE SEEN WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0633 0645 062D;;;;N;;;;;
+FD60;ARABIC LIGATURE SEEN WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0633 0645 062D;;;;N;;;;;
+FD61;ARABIC LIGATURE SEEN WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0633 0645 062C;;;;N;;;;;
+FD62;ARABIC LIGATURE SEEN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0633 0645 0645;;;;N;;;;;
+FD63;ARABIC LIGATURE SEEN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0633 0645 0645;;;;N;;;;;
+FD64;ARABIC LIGATURE SAD WITH HAH WITH HAH FINAL FORM;Lo;0;AL;<final> 0635 062D 062D;;;;N;;;;;
+FD65;ARABIC LIGATURE SAD WITH HAH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0635 062D 062D;;;;N;;;;;
+FD66;ARABIC LIGATURE SAD WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0635 0645 0645;;;;N;;;;;
+FD67;ARABIC LIGATURE SHEEN WITH HAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0634 062D 0645;;;;N;;;;;
+FD68;ARABIC LIGATURE SHEEN WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0634 062D 0645;;;;N;;;;;
+FD69;ARABIC LIGATURE SHEEN WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0634 062C 064A;;;;N;;;;;
+FD6A;ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH FINAL FORM;Lo;0;AL;<final> 0634 0645 062E;;;;N;;;;;
+FD6B;ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0634 0645 062E;;;;N;;;;;
+FD6C;ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0634 0645 0645;;;;N;;;;;
+FD6D;ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0634 0645 0645;;;;N;;;;;
+FD6E;ARABIC LIGATURE DAD WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0636 062D 0649;;;;N;;;;;
+FD6F;ARABIC LIGATURE DAD WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0636 062E 0645;;;;N;;;;;
+FD70;ARABIC LIGATURE DAD WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0636 062E 0645;;;;N;;;;;
+FD71;ARABIC LIGATURE TAH WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0637 0645 062D;;;;N;;;;;
+FD72;ARABIC LIGATURE TAH WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0637 0645 062D;;;;N;;;;;
+FD73;ARABIC LIGATURE TAH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0637 0645 0645;;;;N;;;;;
+FD74;ARABIC LIGATURE TAH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0637 0645 064A;;;;N;;;;;
+FD75;ARABIC LIGATURE AIN WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0639 062C 0645;;;;N;;;;;
+FD76;ARABIC LIGATURE AIN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0639 0645 0645;;;;N;;;;;
+FD77;ARABIC LIGATURE AIN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639 0645 0645;;;;N;;;;;
+FD78;ARABIC LIGATURE AIN WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0639 0645 0649;;;;N;;;;;
+FD79;ARABIC LIGATURE GHAIN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 063A 0645 0645;;;;N;;;;;
+FD7A;ARABIC LIGATURE GHAIN WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 063A 0645 064A;;;;N;;;;;
+FD7B;ARABIC LIGATURE GHAIN WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 063A 0645 0649;;;;N;;;;;
+FD7C;ARABIC LIGATURE FEH WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0641 062E 0645;;;;N;;;;;
+FD7D;ARABIC LIGATURE FEH WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0641 062E 0645;;;;N;;;;;
+FD7E;ARABIC LIGATURE QAF WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0642 0645 062D;;;;N;;;;;
+FD7F;ARABIC LIGATURE QAF WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0642 0645 0645;;;;N;;;;;
+FD80;ARABIC LIGATURE LAM WITH HAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 062D 0645;;;;N;;;;;
+FD81;ARABIC LIGATURE LAM WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 062D 064A;;;;N;;;;;
+FD82;ARABIC LIGATURE LAM WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0644 062D 0649;;;;N;;;;;
+FD83;ARABIC LIGATURE LAM WITH JEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062C 062C;;;;N;;;;;
+FD84;ARABIC LIGATURE LAM WITH JEEM WITH JEEM FINAL FORM;Lo;0;AL;<final> 0644 062C 062C;;;;N;;;;;
+FD85;ARABIC LIGATURE LAM WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 062E 0645;;;;N;;;;;
+FD86;ARABIC LIGATURE LAM WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062E 0645;;;;N;;;;;
+FD87;ARABIC LIGATURE LAM WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0644 0645 062D;;;;N;;;;;
+FD88;ARABIC LIGATURE LAM WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0644 0645 062D;;;;N;;;;;
+FD89;ARABIC LIGATURE MEEM WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062D 062C;;;;N;;;;;
+FD8A;ARABIC LIGATURE MEEM WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062D 0645;;;;N;;;;;
+FD8B;ARABIC LIGATURE MEEM WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 062D 064A;;;;N;;;;;
+FD8C;ARABIC LIGATURE MEEM WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0645 062C 062D;;;;N;;;;;
+FD8D;ARABIC LIGATURE MEEM WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062C 0645;;;;N;;;;;
+FD8E;ARABIC LIGATURE MEEM WITH KHAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062E 062C;;;;N;;;;;
+FD8F;ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062E 0645;;;;N;;;;;
+FD92;ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0645 062C 062E;;;;N;;;;;
+FD93;ARABIC LIGATURE HEH WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645 062C;;;;N;;;;;
+FD94;ARABIC LIGATURE HEH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645 0645;;;;N;;;;;
+FD95;ARABIC LIGATURE NOON WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0646 062D 0645;;;;N;;;;;
+FD96;ARABIC LIGATURE NOON WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 062D 0649;;;;N;;;;;
+FD97;ARABIC LIGATURE NOON WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0646 062C 0645;;;;N;;;;;
+FD98;ARABIC LIGATURE NOON WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0646 062C 0645;;;;N;;;;;
+FD99;ARABIC LIGATURE NOON WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 062C 0649;;;;N;;;;;
+FD9A;ARABIC LIGATURE NOON WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 0645 064A;;;;N;;;;;
+FD9B;ARABIC LIGATURE NOON WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 0645 0649;;;;N;;;;;
+FD9C;ARABIC LIGATURE YEH WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 064A 0645 0645;;;;N;;;;;
+FD9D;ARABIC LIGATURE YEH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 064A 0645 0645;;;;N;;;;;
+FD9E;ARABIC LIGATURE BEH WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0628 062E 064A;;;;N;;;;;
+FD9F;ARABIC LIGATURE TEH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 062C 064A;;;;N;;;;;
+FDA0;ARABIC LIGATURE TEH WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 062C 0649;;;;N;;;;;
+FDA1;ARABIC LIGATURE TEH WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 062E 064A;;;;N;;;;;
+FDA2;ARABIC LIGATURE TEH WITH KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 062E 0649;;;;N;;;;;
+FDA3;ARABIC LIGATURE TEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 0645 064A;;;;N;;;;;
+FDA4;ARABIC LIGATURE TEH WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 0645 0649;;;;N;;;;;
+FDA5;ARABIC LIGATURE JEEM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062C 0645 064A;;;;N;;;;;
+FDA6;ARABIC LIGATURE JEEM WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062C 062D 0649;;;;N;;;;;
+FDA7;ARABIC LIGATURE JEEM WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062C 0645 0649;;;;N;;;;;
+FDA8;ARABIC LIGATURE SEEN WITH KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0633 062E 0649;;;;N;;;;;
+FDA9;ARABIC LIGATURE SAD WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0635 062D 064A;;;;N;;;;;
+FDAA;ARABIC LIGATURE SHEEN WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0634 062D 064A;;;;N;;;;;
+FDAB;ARABIC LIGATURE DAD WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0636 062D 064A;;;;N;;;;;
+FDAC;ARABIC LIGATURE LAM WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 062C 064A;;;;N;;;;;
+FDAD;ARABIC LIGATURE LAM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 0645 064A;;;;N;;;;;
+FDAE;ARABIC LIGATURE YEH WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 062D 064A;;;;N;;;;;
+FDAF;ARABIC LIGATURE YEH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 062C 064A;;;;N;;;;;
+FDB0;ARABIC LIGATURE YEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 0645 064A;;;;N;;;;;
+FDB1;ARABIC LIGATURE MEEM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 0645 064A;;;;N;;;;;
+FDB2;ARABIC LIGATURE QAF WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0642 0645 064A;;;;N;;;;;
+FDB3;ARABIC LIGATURE NOON WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 062D 064A;;;;N;;;;;
+FDB4;ARABIC LIGATURE QAF WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0642 0645 062D;;;;N;;;;;
+FDB5;ARABIC LIGATURE LAM WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062D 0645;;;;N;;;;;
+FDB6;ARABIC LIGATURE AIN WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0639 0645 064A;;;;N;;;;;
+FDB7;ARABIC LIGATURE KAF WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0643 0645 064A;;;;N;;;;;
+FDB8;ARABIC LIGATURE NOON WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0646 062C 062D;;;;N;;;;;
+FDB9;ARABIC LIGATURE MEEM WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 062E 064A;;;;N;;;;;
+FDBA;ARABIC LIGATURE LAM WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062C 0645;;;;N;;;;;
+FDBB;ARABIC LIGATURE KAF WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0643 0645 0645;;;;N;;;;;
+FDBC;ARABIC LIGATURE LAM WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 062C 0645;;;;N;;;;;
+FDBD;ARABIC LIGATURE NOON WITH JEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0646 062C 062D;;;;N;;;;;
+FDBE;ARABIC LIGATURE JEEM WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062C 062D 064A;;;;N;;;;;
+FDBF;ARABIC LIGATURE HAH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062D 062C 064A;;;;N;;;;;
+FDC0;ARABIC LIGATURE MEEM WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 062C 064A;;;;N;;;;;
+FDC1;ARABIC LIGATURE FEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0641 0645 064A;;;;N;;;;;
+FDC2;ARABIC LIGATURE BEH WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0628 062D 064A;;;;N;;;;;
+FDC3;ARABIC LIGATURE KAF WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0643 0645 0645;;;;N;;;;;
+FDC4;ARABIC LIGATURE AIN WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639 062C 0645;;;;N;;;;;
+FDC5;ARABIC LIGATURE SAD WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0635 0645 0645;;;;N;;;;;
+FDC6;ARABIC LIGATURE SEEN WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0633 062E 064A;;;;N;;;;;
+FDC7;ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 062C 064A;;;;N;;;;;
+FDF0;ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 06D2;;;;N;;;;;
+FDF1;ARABIC LIGATURE QALA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL;<isolated> 0642 0644 06D2;;;;N;;;;;
+FDF2;ARABIC LIGATURE ALLAH ISOLATED FORM;Lo;0;AL;<isolated> 0627 0644 0644 0647;;;;N;;;;;
+FDF3;ARABIC LIGATURE AKBAR ISOLATED FORM;Lo;0;AL;<isolated> 0627 0643 0628 0631;;;;N;;;;;
+FDF4;ARABIC LIGATURE MOHAMMAD ISOLATED FORM;Lo;0;AL;<isolated> 0645 062D 0645 062F;;;;N;;;;;
+FDF5;ARABIC LIGATURE SALAM ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 0639 0645;;;;N;;;;;
+FDF6;ARABIC LIGATURE RASOUL ISOLATED FORM;Lo;0;AL;<isolated> 0631 0633 0648 0644;;;;N;;;;;
+FDF7;ARABIC LIGATURE ALAYHE ISOLATED FORM;Lo;0;AL;<isolated> 0639 0644 064A 0647;;;;N;;;;;
+FDF8;ARABIC LIGATURE WASALLAM ISOLATED FORM;Lo;0;AL;<isolated> 0648 0633 0644 0645;;;;N;;;;;
+FDF9;ARABIC LIGATURE SALLA ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 0649;;;;N;;;;;
+FDFA;ARABIC LIGATURE SALLALLAHOU ALAYHE WASALLAM;Lo;0;AL;<isolated> 0635 0644 0649 0020 0627 0644 0644 0647 0020 0639 0644 064A 0647 0020 0648 0633 0644 0645;;;;N;ARABIC LETTER SALLALLAHOU ALAYHE WASALLAM;;;;
+FDFB;ARABIC LIGATURE JALLAJALALOUHOU;Lo;0;AL;<isolated> 062C 0644 0020 062C 0644 0627 0644 0647;;;;N;ARABIC LETTER JALLAJALALOUHOU;;;;
+FDFC;RIAL SIGN;Sc;0;AL;<isolated> 0631 06CC 0627 0644;;;;N;;;;;
+FDFD;ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM;So;0;ON;;;;;N;;;;;
+FE00;VARIATION SELECTOR-1;Mn;0;NSM;;;;;N;;;;;
+FE01;VARIATION SELECTOR-2;Mn;0;NSM;;;;;N;;;;;
+FE02;VARIATION SELECTOR-3;Mn;0;NSM;;;;;N;;;;;
+FE03;VARIATION SELECTOR-4;Mn;0;NSM;;;;;N;;;;;
+FE04;VARIATION SELECTOR-5;Mn;0;NSM;;;;;N;;;;;
+FE05;VARIATION SELECTOR-6;Mn;0;NSM;;;;;N;;;;;
+FE06;VARIATION SELECTOR-7;Mn;0;NSM;;;;;N;;;;;
+FE07;VARIATION SELECTOR-8;Mn;0;NSM;;;;;N;;;;;
+FE08;VARIATION SELECTOR-9;Mn;0;NSM;;;;;N;;;;;
+FE09;VARIATION SELECTOR-10;Mn;0;NSM;;;;;N;;;;;
+FE0A;VARIATION SELECTOR-11;Mn;0;NSM;;;;;N;;;;;
+FE0B;VARIATION SELECTOR-12;Mn;0;NSM;;;;;N;;;;;
+FE0C;VARIATION SELECTOR-13;Mn;0;NSM;;;;;N;;;;;
+FE0D;VARIATION SELECTOR-14;Mn;0;NSM;;;;;N;;;;;
+FE0E;VARIATION SELECTOR-15;Mn;0;NSM;;;;;N;;;;;
+FE0F;VARIATION SELECTOR-16;Mn;0;NSM;;;;;N;;;;;
+FE10;PRESENTATION FORM FOR VERTICAL COMMA;Po;0;ON;<vertical> 002C;;;;N;;;;;
+FE11;PRESENTATION FORM FOR VERTICAL IDEOGRAPHIC COMMA;Po;0;ON;<vertical> 3001;;;;N;;;;;
+FE12;PRESENTATION FORM FOR VERTICAL IDEOGRAPHIC FULL STOP;Po;0;ON;<vertical> 3002;;;;N;;;;;
+FE13;PRESENTATION FORM FOR VERTICAL COLON;Po;0;ON;<vertical> 003A;;;;N;;;;;
+FE14;PRESENTATION FORM FOR VERTICAL SEMICOLON;Po;0;ON;<vertical> 003B;;;;N;;;;;
+FE15;PRESENTATION FORM FOR VERTICAL EXCLAMATION MARK;Po;0;ON;<vertical> 0021;;;;N;;;;;
+FE16;PRESENTATION FORM FOR VERTICAL QUESTION MARK;Po;0;ON;<vertical> 003F;;;;N;;;;;
+FE17;PRESENTATION FORM FOR VERTICAL LEFT WHITE LENTICULAR BRACKET;Ps;0;ON;<vertical> 3016;;;;N;;;;;
+FE18;PRESENTATION FORM FOR VERTICAL RIGHT WHITE LENTICULAR BRAKCET;Pe;0;ON;<vertical> 3017;;;;N;;;;;
+FE19;PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS;Po;0;ON;<vertical> 2026;;;;N;;;;;
+FE20;COMBINING LIGATURE LEFT HALF;Mn;230;NSM;;;;;N;;;;;
+FE21;COMBINING LIGATURE RIGHT HALF;Mn;230;NSM;;;;;N;;;;;
+FE22;COMBINING DOUBLE TILDE LEFT HALF;Mn;230;NSM;;;;;N;;;;;
+FE23;COMBINING DOUBLE TILDE RIGHT HALF;Mn;230;NSM;;;;;N;;;;;
+FE24;COMBINING MACRON LEFT HALF;Mn;230;NSM;;;;;N;;;;;
+FE25;COMBINING MACRON RIGHT HALF;Mn;230;NSM;;;;;N;;;;;
+FE26;COMBINING CONJOINING MACRON;Mn;230;NSM;;;;;N;;;;;
+FE27;COMBINING LIGATURE LEFT HALF BELOW;Mn;220;NSM;;;;;N;;;;;
+FE28;COMBINING LIGATURE RIGHT HALF BELOW;Mn;220;NSM;;;;;N;;;;;
+FE29;COMBINING TILDE LEFT HALF BELOW;Mn;220;NSM;;;;;N;;;;;
+FE2A;COMBINING TILDE RIGHT HALF BELOW;Mn;220;NSM;;;;;N;;;;;
+FE2B;COMBINING MACRON LEFT HALF BELOW;Mn;220;NSM;;;;;N;;;;;
+FE2C;COMBINING MACRON RIGHT HALF BELOW;Mn;220;NSM;;;;;N;;;;;
+FE2D;COMBINING CONJOINING MACRON BELOW;Mn;220;NSM;;;;;N;;;;;
+FE2E;COMBINING CYRILLIC TITLO LEFT HALF;Mn;230;NSM;;;;;N;;;;;
+FE2F;COMBINING CYRILLIC TITLO RIGHT HALF;Mn;230;NSM;;;;;N;;;;;
+FE30;PRESENTATION FORM FOR VERTICAL TWO DOT LEADER;Po;0;ON;<vertical> 2025;;;;N;GLYPH FOR VERTICAL TWO DOT LEADER;;;;
+FE31;PRESENTATION FORM FOR VERTICAL EM DASH;Pd;0;ON;<vertical> 2014;;;;N;GLYPH FOR VERTICAL EM DASH;;;;
+FE32;PRESENTATION FORM FOR VERTICAL EN DASH;Pd;0;ON;<vertical> 2013;;;;N;GLYPH FOR VERTICAL EN DASH;;;;
+FE33;PRESENTATION FORM FOR VERTICAL LOW LINE;Pc;0;ON;<vertical> 005F;;;;N;GLYPH FOR VERTICAL SPACING UNDERSCORE;;;;
+FE34;PRESENTATION FORM FOR VERTICAL WAVY LOW LINE;Pc;0;ON;<vertical> 005F;;;;N;GLYPH FOR VERTICAL SPACING WAVY UNDERSCORE;;;;
+FE35;PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS;Ps;0;ON;<vertical> 0028;;;;N;GLYPH FOR VERTICAL OPENING PARENTHESIS;;;;
+FE36;PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS;Pe;0;ON;<vertical> 0029;;;;N;GLYPH FOR VERTICAL CLOSING PARENTHESIS;;;;
+FE37;PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET;Ps;0;ON;<vertical> 007B;;;;N;GLYPH FOR VERTICAL OPENING CURLY BRACKET;;;;
+FE38;PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET;Pe;0;ON;<vertical> 007D;;;;N;GLYPH FOR VERTICAL CLOSING CURLY BRACKET;;;;
+FE39;PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET;Ps;0;ON;<vertical> 3014;;;;N;GLYPH FOR VERTICAL OPENING TORTOISE SHELL BRACKET;;;;
+FE3A;PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET;Pe;0;ON;<vertical> 3015;;;;N;GLYPH FOR VERTICAL CLOSING TORTOISE SHELL BRACKET;;;;
+FE3B;PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET;Ps;0;ON;<vertical> 3010;;;;N;GLYPH FOR VERTICAL OPENING BLACK LENTICULAR BRACKET;;;;
+FE3C;PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET;Pe;0;ON;<vertical> 3011;;;;N;GLYPH FOR VERTICAL CLOSING BLACK LENTICULAR BRACKET;;;;
+FE3D;PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET;Ps;0;ON;<vertical> 300A;;;;N;GLYPH FOR VERTICAL OPENING DOUBLE ANGLE BRACKET;;;;
+FE3E;PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON;<vertical> 300B;;;;N;GLYPH FOR VERTICAL CLOSING DOUBLE ANGLE BRACKET;;;;
+FE3F;PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET;Ps;0;ON;<vertical> 3008;;;;N;GLYPH FOR VERTICAL OPENING ANGLE BRACKET;;;;
+FE40;PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET;Pe;0;ON;<vertical> 3009;;;;N;GLYPH FOR VERTICAL CLOSING ANGLE BRACKET;;;;
+FE41;PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET;Ps;0;ON;<vertical> 300C;;;;N;GLYPH FOR VERTICAL OPENING CORNER BRACKET;;;;
+FE42;PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET;Pe;0;ON;<vertical> 300D;;;;N;GLYPH FOR VERTICAL CLOSING CORNER BRACKET;;;;
+FE43;PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET;Ps;0;ON;<vertical> 300E;;;;N;GLYPH FOR VERTICAL OPENING WHITE CORNER BRACKET;;;;
+FE44;PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET;Pe;0;ON;<vertical> 300F;;;;N;GLYPH FOR VERTICAL CLOSING WHITE CORNER BRACKET;;;;
+FE45;SESAME DOT;Po;0;ON;;;;;N;;;;;
+FE46;WHITE SESAME DOT;Po;0;ON;;;;;N;;;;;
+FE47;PRESENTATION FORM FOR VERTICAL LEFT SQUARE BRACKET;Ps;0;ON;<vertical> 005B;;;;N;;;;;
+FE48;PRESENTATION FORM FOR VERTICAL RIGHT SQUARE BRACKET;Pe;0;ON;<vertical> 005D;;;;N;;;;;
+FE49;DASHED OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING DASHED OVERSCORE;;;;
+FE4A;CENTRELINE OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING CENTERLINE OVERSCORE;;;;
+FE4B;WAVY OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING WAVY OVERSCORE;;;;
+FE4C;DOUBLE WAVY OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING DOUBLE WAVY OVERSCORE;;;;
+FE4D;DASHED LOW LINE;Pc;0;ON;<compat> 005F;;;;N;SPACING DASHED UNDERSCORE;;;;
+FE4E;CENTRELINE LOW LINE;Pc;0;ON;<compat> 005F;;;;N;SPACING CENTERLINE UNDERSCORE;;;;
+FE4F;WAVY LOW LINE;Pc;0;ON;<compat> 005F;;;;N;SPACING WAVY UNDERSCORE;;;;
+FE50;SMALL COMMA;Po;0;CS;<small> 002C;;;;N;;;;;
+FE51;SMALL IDEOGRAPHIC COMMA;Po;0;ON;<small> 3001;;;;N;;;;;
+FE52;SMALL FULL STOP;Po;0;CS;<small> 002E;;;;N;SMALL PERIOD;;;;
+FE54;SMALL SEMICOLON;Po;0;ON;<small> 003B;;;;N;;;;;
+FE55;SMALL COLON;Po;0;CS;<small> 003A;;;;N;;;;;
+FE56;SMALL QUESTION MARK;Po;0;ON;<small> 003F;;;;N;;;;;
+FE57;SMALL EXCLAMATION MARK;Po;0;ON;<small> 0021;;;;N;;;;;
+FE58;SMALL EM DASH;Pd;0;ON;<small> 2014;;;;N;;;;;
+FE59;SMALL LEFT PARENTHESIS;Ps;0;ON;<small> 0028;;;;Y;SMALL OPENING PARENTHESIS;;;;
+FE5A;SMALL RIGHT PARENTHESIS;Pe;0;ON;<small> 0029;;;;Y;SMALL CLOSING PARENTHESIS;;;;
+FE5B;SMALL LEFT CURLY BRACKET;Ps;0;ON;<small> 007B;;;;Y;SMALL OPENING CURLY BRACKET;;;;
+FE5C;SMALL RIGHT CURLY BRACKET;Pe;0;ON;<small> 007D;;;;Y;SMALL CLOSING CURLY BRACKET;;;;
+FE5D;SMALL LEFT TORTOISE SHELL BRACKET;Ps;0;ON;<small> 3014;;;;Y;SMALL OPENING TORTOISE SHELL BRACKET;;;;
+FE5E;SMALL RIGHT TORTOISE SHELL BRACKET;Pe;0;ON;<small> 3015;;;;Y;SMALL CLOSING TORTOISE SHELL BRACKET;;;;
+FE5F;SMALL NUMBER SIGN;Po;0;ET;<small> 0023;;;;N;;;;;
+FE60;SMALL AMPERSAND;Po;0;ON;<small> 0026;;;;N;;;;;
+FE61;SMALL ASTERISK;Po;0;ON;<small> 002A;;;;N;;;;;
+FE62;SMALL PLUS SIGN;Sm;0;ES;<small> 002B;;;;N;;;;;
+FE63;SMALL HYPHEN-MINUS;Pd;0;ES;<small> 002D;;;;N;;;;;
+FE64;SMALL LESS-THAN SIGN;Sm;0;ON;<small> 003C;;;;Y;;;;;
+FE65;SMALL GREATER-THAN SIGN;Sm;0;ON;<small> 003E;;;;Y;;;;;
+FE66;SMALL EQUALS SIGN;Sm;0;ON;<small> 003D;;;;N;;;;;
+FE68;SMALL REVERSE SOLIDUS;Po;0;ON;<small> 005C;;;;N;SMALL BACKSLASH;;;;
+FE69;SMALL DOLLAR SIGN;Sc;0;ET;<small> 0024;;;;N;;;;;
+FE6A;SMALL PERCENT SIGN;Po;0;ET;<small> 0025;;;;N;;;;;
+FE6B;SMALL COMMERCIAL AT;Po;0;ON;<small> 0040;;;;N;;;;;
+FE70;ARABIC FATHATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064B;;;;N;ARABIC SPACING FATHATAN;;;;
+FE71;ARABIC TATWEEL WITH FATHATAN ABOVE;Lo;0;AL;<medial> 0640 064B;;;;N;ARABIC FATHATAN ON TATWEEL;;;;
+FE72;ARABIC DAMMATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064C;;;;N;ARABIC SPACING DAMMATAN;;;;
+FE73;ARABIC TAIL FRAGMENT;Lo;0;AL;;;;;N;;;;;
+FE74;ARABIC KASRATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064D;;;;N;ARABIC SPACING KASRATAN;;;;
+FE76;ARABIC FATHA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064E;;;;N;ARABIC SPACING FATHAH;;;;
+FE77;ARABIC FATHA MEDIAL FORM;Lo;0;AL;<medial> 0640 064E;;;;N;ARABIC FATHAH ON TATWEEL;;;;
+FE78;ARABIC DAMMA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064F;;;;N;ARABIC SPACING DAMMAH;;;;
+FE79;ARABIC DAMMA MEDIAL FORM;Lo;0;AL;<medial> 0640 064F;;;;N;ARABIC DAMMAH ON TATWEEL;;;;
+FE7A;ARABIC KASRA ISOLATED FORM;Lo;0;AL;<isolated> 0020 0650;;;;N;ARABIC SPACING KASRAH;;;;
+FE7B;ARABIC KASRA MEDIAL FORM;Lo;0;AL;<medial> 0640 0650;;;;N;ARABIC KASRAH ON TATWEEL;;;;
+FE7C;ARABIC SHADDA ISOLATED FORM;Lo;0;AL;<isolated> 0020 0651;;;;N;ARABIC SPACING SHADDAH;;;;
+FE7D;ARABIC SHADDA MEDIAL FORM;Lo;0;AL;<medial> 0640 0651;;;;N;ARABIC SHADDAH ON TATWEEL;;;;
+FE7E;ARABIC SUKUN ISOLATED FORM;Lo;0;AL;<isolated> 0020 0652;;;;N;ARABIC SPACING SUKUN;;;;
+FE7F;ARABIC SUKUN MEDIAL FORM;Lo;0;AL;<medial> 0640 0652;;;;N;ARABIC SUKUN ON TATWEEL;;;;
+FE80;ARABIC LETTER HAMZA ISOLATED FORM;Lo;0;AL;<isolated> 0621;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH;;;;
+FE81;ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0622;;;;N;GLYPH FOR ISOLATE ARABIC MADDAH ON ALEF;;;;
+FE82;ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM;Lo;0;AL;<final> 0622;;;;N;GLYPH FOR FINAL ARABIC MADDAH ON ALEF;;;;
+FE83;ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0623;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON ALEF;;;;
+FE84;ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0623;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON ALEF;;;;
+FE85;ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0624;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON WAW;;;;
+FE86;ARABIC LETTER WAW WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0624;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON WAW;;;;
+FE87;ARABIC LETTER ALEF WITH HAMZA BELOW ISOLATED FORM;Lo;0;AL;<isolated> 0625;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH UNDER ALEF;;;;
+FE88;ARABIC LETTER ALEF WITH HAMZA BELOW FINAL FORM;Lo;0;AL;<final> 0625;;;;N;GLYPH FOR FINAL ARABIC HAMZAH UNDER ALEF;;;;
+FE89;ARABIC LETTER YEH WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0626;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON YA;;;;
+FE8A;ARABIC LETTER YEH WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0626;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON YA;;;;
+FE8B;ARABIC LETTER YEH WITH HAMZA ABOVE INITIAL FORM;Lo;0;AL;<initial> 0626;;;;N;GLYPH FOR INITIAL ARABIC HAMZAH ON YA;;;;
+FE8C;ARABIC LETTER YEH WITH HAMZA ABOVE MEDIAL FORM;Lo;0;AL;<medial> 0626;;;;N;GLYPH FOR MEDIAL ARABIC HAMZAH ON YA;;;;
+FE8D;ARABIC LETTER ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0627;;;;N;GLYPH FOR ISOLATE ARABIC ALEF;;;;
+FE8E;ARABIC LETTER ALEF FINAL FORM;Lo;0;AL;<final> 0627;;;;N;GLYPH FOR FINAL ARABIC ALEF;;;;
+FE8F;ARABIC LETTER BEH ISOLATED FORM;Lo;0;AL;<isolated> 0628;;;;N;GLYPH FOR ISOLATE ARABIC BAA;;;;
+FE90;ARABIC LETTER BEH FINAL FORM;Lo;0;AL;<final> 0628;;;;N;GLYPH FOR FINAL ARABIC BAA;;;;
+FE91;ARABIC LETTER BEH INITIAL FORM;Lo;0;AL;<initial> 0628;;;;N;GLYPH FOR INITIAL ARABIC BAA;;;;
+FE92;ARABIC LETTER BEH MEDIAL FORM;Lo;0;AL;<medial> 0628;;;;N;GLYPH FOR MEDIAL ARABIC BAA;;;;
+FE93;ARABIC LETTER TEH MARBUTA ISOLATED FORM;Lo;0;AL;<isolated> 0629;;;;N;GLYPH FOR ISOLATE ARABIC TAA MARBUTAH;;;;
+FE94;ARABIC LETTER TEH MARBUTA FINAL FORM;Lo;0;AL;<final> 0629;;;;N;GLYPH FOR FINAL ARABIC TAA MARBUTAH;;;;
+FE95;ARABIC LETTER TEH ISOLATED FORM;Lo;0;AL;<isolated> 062A;;;;N;GLYPH FOR ISOLATE ARABIC TAA;;;;
+FE96;ARABIC LETTER TEH FINAL FORM;Lo;0;AL;<final> 062A;;;;N;GLYPH FOR FINAL ARABIC TAA;;;;
+FE97;ARABIC LETTER TEH INITIAL FORM;Lo;0;AL;<initial> 062A;;;;N;GLYPH FOR INITIAL ARABIC TAA;;;;
+FE98;ARABIC LETTER TEH MEDIAL FORM;Lo;0;AL;<medial> 062A;;;;N;GLYPH FOR MEDIAL ARABIC TAA;;;;
+FE99;ARABIC LETTER THEH ISOLATED FORM;Lo;0;AL;<isolated> 062B;;;;N;GLYPH FOR ISOLATE ARABIC THAA;;;;
+FE9A;ARABIC LETTER THEH FINAL FORM;Lo;0;AL;<final> 062B;;;;N;GLYPH FOR FINAL ARABIC THAA;;;;
+FE9B;ARABIC LETTER THEH INITIAL FORM;Lo;0;AL;<initial> 062B;;;;N;GLYPH FOR INITIAL ARABIC THAA;;;;
+FE9C;ARABIC LETTER THEH MEDIAL FORM;Lo;0;AL;<medial> 062B;;;;N;GLYPH FOR MEDIAL ARABIC THAA;;;;
+FE9D;ARABIC LETTER JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062C;;;;N;GLYPH FOR ISOLATE ARABIC JEEM;;;;
+FE9E;ARABIC LETTER JEEM FINAL FORM;Lo;0;AL;<final> 062C;;;;N;GLYPH FOR FINAL ARABIC JEEM;;;;
+FE9F;ARABIC LETTER JEEM INITIAL FORM;Lo;0;AL;<initial> 062C;;;;N;GLYPH FOR INITIAL ARABIC JEEM;;;;
+FEA0;ARABIC LETTER JEEM MEDIAL FORM;Lo;0;AL;<medial> 062C;;;;N;GLYPH FOR MEDIAL ARABIC JEEM;;;;
+FEA1;ARABIC LETTER HAH ISOLATED FORM;Lo;0;AL;<isolated> 062D;;;;N;GLYPH FOR ISOLATE ARABIC HAA;;;;
+FEA2;ARABIC LETTER HAH FINAL FORM;Lo;0;AL;<final> 062D;;;;N;GLYPH FOR FINAL ARABIC HAA;;;;
+FEA3;ARABIC LETTER HAH INITIAL FORM;Lo;0;AL;<initial> 062D;;;;N;GLYPH FOR INITIAL ARABIC HAA;;;;
+FEA4;ARABIC LETTER HAH MEDIAL FORM;Lo;0;AL;<medial> 062D;;;;N;GLYPH FOR MEDIAL ARABIC HAA;;;;
+FEA5;ARABIC LETTER KHAH ISOLATED FORM;Lo;0;AL;<isolated> 062E;;;;N;GLYPH FOR ISOLATE ARABIC KHAA;;;;
+FEA6;ARABIC LETTER KHAH FINAL FORM;Lo;0;AL;<final> 062E;;;;N;GLYPH FOR FINAL ARABIC KHAA;;;;
+FEA7;ARABIC LETTER KHAH INITIAL FORM;Lo;0;AL;<initial> 062E;;;;N;GLYPH FOR INITIAL ARABIC KHAA;;;;
+FEA8;ARABIC LETTER KHAH MEDIAL FORM;Lo;0;AL;<medial> 062E;;;;N;GLYPH FOR MEDIAL ARABIC KHAA;;;;
+FEA9;ARABIC LETTER DAL ISOLATED FORM;Lo;0;AL;<isolated> 062F;;;;N;GLYPH FOR ISOLATE ARABIC DAL;;;;
+FEAA;ARABIC LETTER DAL FINAL FORM;Lo;0;AL;<final> 062F;;;;N;GLYPH FOR FINAL ARABIC DAL;;;;
+FEAB;ARABIC LETTER THAL ISOLATED FORM;Lo;0;AL;<isolated> 0630;;;;N;GLYPH FOR ISOLATE ARABIC THAL;;;;
+FEAC;ARABIC LETTER THAL FINAL FORM;Lo;0;AL;<final> 0630;;;;N;GLYPH FOR FINAL ARABIC THAL;;;;
+FEAD;ARABIC LETTER REH ISOLATED FORM;Lo;0;AL;<isolated> 0631;;;;N;GLYPH FOR ISOLATE ARABIC RA;;;;
+FEAE;ARABIC LETTER REH FINAL FORM;Lo;0;AL;<final> 0631;;;;N;GLYPH FOR FINAL ARABIC RA;;;;
+FEAF;ARABIC LETTER ZAIN ISOLATED FORM;Lo;0;AL;<isolated> 0632;;;;N;GLYPH FOR ISOLATE ARABIC ZAIN;;;;
+FEB0;ARABIC LETTER ZAIN FINAL FORM;Lo;0;AL;<final> 0632;;;;N;GLYPH FOR FINAL ARABIC ZAIN;;;;
+FEB1;ARABIC LETTER SEEN ISOLATED FORM;Lo;0;AL;<isolated> 0633;;;;N;GLYPH FOR ISOLATE ARABIC SEEN;;;;
+FEB2;ARABIC LETTER SEEN FINAL FORM;Lo;0;AL;<final> 0633;;;;N;GLYPH FOR FINAL ARABIC SEEN;;;;
+FEB3;ARABIC LETTER SEEN INITIAL FORM;Lo;0;AL;<initial> 0633;;;;N;GLYPH FOR INITIAL ARABIC SEEN;;;;
+FEB4;ARABIC LETTER SEEN MEDIAL FORM;Lo;0;AL;<medial> 0633;;;;N;GLYPH FOR MEDIAL ARABIC SEEN;;;;
+FEB5;ARABIC LETTER SHEEN ISOLATED FORM;Lo;0;AL;<isolated> 0634;;;;N;GLYPH FOR ISOLATE ARABIC SHEEN;;;;
+FEB6;ARABIC LETTER SHEEN FINAL FORM;Lo;0;AL;<final> 0634;;;;N;GLYPH FOR FINAL ARABIC SHEEN;;;;
+FEB7;ARABIC LETTER SHEEN INITIAL FORM;Lo;0;AL;<initial> 0634;;;;N;GLYPH FOR INITIAL ARABIC SHEEN;;;;
+FEB8;ARABIC LETTER SHEEN MEDIAL FORM;Lo;0;AL;<medial> 0634;;;;N;GLYPH FOR MEDIAL ARABIC SHEEN;;;;
+FEB9;ARABIC LETTER SAD ISOLATED FORM;Lo;0;AL;<isolated> 0635;;;;N;GLYPH FOR ISOLATE ARABIC SAD;;;;
+FEBA;ARABIC LETTER SAD FINAL FORM;Lo;0;AL;<final> 0635;;;;N;GLYPH FOR FINAL ARABIC SAD;;;;
+FEBB;ARABIC LETTER SAD INITIAL FORM;Lo;0;AL;<initial> 0635;;;;N;GLYPH FOR INITIAL ARABIC SAD;;;;
+FEBC;ARABIC LETTER SAD MEDIAL FORM;Lo;0;AL;<medial> 0635;;;;N;GLYPH FOR MEDIAL ARABIC SAD;;;;
+FEBD;ARABIC LETTER DAD ISOLATED FORM;Lo;0;AL;<isolated> 0636;;;;N;GLYPH FOR ISOLATE ARABIC DAD;;;;
+FEBE;ARABIC LETTER DAD FINAL FORM;Lo;0;AL;<final> 0636;;;;N;GLYPH FOR FINAL ARABIC DAD;;;;
+FEBF;ARABIC LETTER DAD INITIAL FORM;Lo;0;AL;<initial> 0636;;;;N;GLYPH FOR INITIAL ARABIC DAD;;;;
+FEC0;ARABIC LETTER DAD MEDIAL FORM;Lo;0;AL;<medial> 0636;;;;N;GLYPH FOR MEDIAL ARABIC DAD;;;;
+FEC1;ARABIC LETTER TAH ISOLATED FORM;Lo;0;AL;<isolated> 0637;;;;N;GLYPH FOR ISOLATE ARABIC TAH;;;;
+FEC2;ARABIC LETTER TAH FINAL FORM;Lo;0;AL;<final> 0637;;;;N;GLYPH FOR FINAL ARABIC TAH;;;;
+FEC3;ARABIC LETTER TAH INITIAL FORM;Lo;0;AL;<initial> 0637;;;;N;GLYPH FOR INITIAL ARABIC TAH;;;;
+FEC4;ARABIC LETTER TAH MEDIAL FORM;Lo;0;AL;<medial> 0637;;;;N;GLYPH FOR MEDIAL ARABIC TAH;;;;
+FEC5;ARABIC LETTER ZAH ISOLATED FORM;Lo;0;AL;<isolated> 0638;;;;N;GLYPH FOR ISOLATE ARABIC DHAH;;;;
+FEC6;ARABIC LETTER ZAH FINAL FORM;Lo;0;AL;<final> 0638;;;;N;GLYPH FOR FINAL ARABIC DHAH;;;;
+FEC7;ARABIC LETTER ZAH INITIAL FORM;Lo;0;AL;<initial> 0638;;;;N;GLYPH FOR INITIAL ARABIC DHAH;;;;
+FEC8;ARABIC LETTER ZAH MEDIAL FORM;Lo;0;AL;<medial> 0638;;;;N;GLYPH FOR MEDIAL ARABIC DHAH;;;;
+FEC9;ARABIC LETTER AIN ISOLATED FORM;Lo;0;AL;<isolated> 0639;;;;N;GLYPH FOR ISOLATE ARABIC AIN;;;;
+FECA;ARABIC LETTER AIN FINAL FORM;Lo;0;AL;<final> 0639;;;;N;GLYPH FOR FINAL ARABIC AIN;;;;
+FECB;ARABIC LETTER AIN INITIAL FORM;Lo;0;AL;<initial> 0639;;;;N;GLYPH FOR INITIAL ARABIC AIN;;;;
+FECC;ARABIC LETTER AIN MEDIAL FORM;Lo;0;AL;<medial> 0639;;;;N;GLYPH FOR MEDIAL ARABIC AIN;;;;
+FECD;ARABIC LETTER GHAIN ISOLATED FORM;Lo;0;AL;<isolated> 063A;;;;N;GLYPH FOR ISOLATE ARABIC GHAIN;;;;
+FECE;ARABIC LETTER GHAIN FINAL FORM;Lo;0;AL;<final> 063A;;;;N;GLYPH FOR FINAL ARABIC GHAIN;;;;
+FECF;ARABIC LETTER GHAIN INITIAL FORM;Lo;0;AL;<initial> 063A;;;;N;GLYPH FOR INITIAL ARABIC GHAIN;;;;
+FED0;ARABIC LETTER GHAIN MEDIAL FORM;Lo;0;AL;<medial> 063A;;;;N;GLYPH FOR MEDIAL ARABIC GHAIN;;;;
+FED1;ARABIC LETTER FEH ISOLATED FORM;Lo;0;AL;<isolated> 0641;;;;N;GLYPH FOR ISOLATE ARABIC FA;;;;
+FED2;ARABIC LETTER FEH FINAL FORM;Lo;0;AL;<final> 0641;;;;N;GLYPH FOR FINAL ARABIC FA;;;;
+FED3;ARABIC LETTER FEH INITIAL FORM;Lo;0;AL;<initial> 0641;;;;N;GLYPH FOR INITIAL ARABIC FA;;;;
+FED4;ARABIC LETTER FEH MEDIAL FORM;Lo;0;AL;<medial> 0641;;;;N;GLYPH FOR MEDIAL ARABIC FA;;;;
+FED5;ARABIC LETTER QAF ISOLATED FORM;Lo;0;AL;<isolated> 0642;;;;N;GLYPH FOR ISOLATE ARABIC QAF;;;;
+FED6;ARABIC LETTER QAF FINAL FORM;Lo;0;AL;<final> 0642;;;;N;GLYPH FOR FINAL ARABIC QAF;;;;
+FED7;ARABIC LETTER QAF INITIAL FORM;Lo;0;AL;<initial> 0642;;;;N;GLYPH FOR INITIAL ARABIC QAF;;;;
+FED8;ARABIC LETTER QAF MEDIAL FORM;Lo;0;AL;<medial> 0642;;;;N;GLYPH FOR MEDIAL ARABIC QAF;;;;
+FED9;ARABIC LETTER KAF ISOLATED FORM;Lo;0;AL;<isolated> 0643;;;;N;GLYPH FOR ISOLATE ARABIC CAF;;;;
+FEDA;ARABIC LETTER KAF FINAL FORM;Lo;0;AL;<final> 0643;;;;N;GLYPH FOR FINAL ARABIC CAF;;;;
+FEDB;ARABIC LETTER KAF INITIAL FORM;Lo;0;AL;<initial> 0643;;;;N;GLYPH FOR INITIAL ARABIC CAF;;;;
+FEDC;ARABIC LETTER KAF MEDIAL FORM;Lo;0;AL;<medial> 0643;;;;N;GLYPH FOR MEDIAL ARABIC CAF;;;;
+FEDD;ARABIC LETTER LAM ISOLATED FORM;Lo;0;AL;<isolated> 0644;;;;N;GLYPH FOR ISOLATE ARABIC LAM;;;;
+FEDE;ARABIC LETTER LAM FINAL FORM;Lo;0;AL;<final> 0644;;;;N;GLYPH FOR FINAL ARABIC LAM;;;;
+FEDF;ARABIC LETTER LAM INITIAL FORM;Lo;0;AL;<initial> 0644;;;;N;GLYPH FOR INITIAL ARABIC LAM;;;;
+FEE0;ARABIC LETTER LAM MEDIAL FORM;Lo;0;AL;<medial> 0644;;;;N;GLYPH FOR MEDIAL ARABIC LAM;;;;
+FEE1;ARABIC LETTER MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0645;;;;N;GLYPH FOR ISOLATE ARABIC MEEM;;;;
+FEE2;ARABIC LETTER MEEM FINAL FORM;Lo;0;AL;<final> 0645;;;;N;GLYPH FOR FINAL ARABIC MEEM;;;;
+FEE3;ARABIC LETTER MEEM INITIAL FORM;Lo;0;AL;<initial> 0645;;;;N;GLYPH FOR INITIAL ARABIC MEEM;;;;
+FEE4;ARABIC LETTER MEEM MEDIAL FORM;Lo;0;AL;<medial> 0645;;;;N;GLYPH FOR MEDIAL ARABIC MEEM;;;;
+FEE5;ARABIC LETTER NOON ISOLATED FORM;Lo;0;AL;<isolated> 0646;;;;N;GLYPH FOR ISOLATE ARABIC NOON;;;;
+FEE6;ARABIC LETTER NOON FINAL FORM;Lo;0;AL;<final> 0646;;;;N;GLYPH FOR FINAL ARABIC NOON;;;;
+FEE7;ARABIC LETTER NOON INITIAL FORM;Lo;0;AL;<initial> 0646;;;;N;GLYPH FOR INITIAL ARABIC NOON;;;;
+FEE8;ARABIC LETTER NOON MEDIAL FORM;Lo;0;AL;<medial> 0646;;;;N;GLYPH FOR MEDIAL ARABIC NOON;;;;
+FEE9;ARABIC LETTER HEH ISOLATED FORM;Lo;0;AL;<isolated> 0647;;;;N;GLYPH FOR ISOLATE ARABIC HA;;;;
+FEEA;ARABIC LETTER HEH FINAL FORM;Lo;0;AL;<final> 0647;;;;N;GLYPH FOR FINAL ARABIC HA;;;;
+FEEB;ARABIC LETTER HEH INITIAL FORM;Lo;0;AL;<initial> 0647;;;;N;GLYPH FOR INITIAL ARABIC HA;;;;
+FEEC;ARABIC LETTER HEH MEDIAL FORM;Lo;0;AL;<medial> 0647;;;;N;GLYPH FOR MEDIAL ARABIC HA;;;;
+FEED;ARABIC LETTER WAW ISOLATED FORM;Lo;0;AL;<isolated> 0648;;;;N;GLYPH FOR ISOLATE ARABIC WAW;;;;
+FEEE;ARABIC LETTER WAW FINAL FORM;Lo;0;AL;<final> 0648;;;;N;GLYPH FOR FINAL ARABIC WAW;;;;
+FEEF;ARABIC LETTER ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0649;;;;N;GLYPH FOR ISOLATE ARABIC ALEF MAQSURAH;;;;
+FEF0;ARABIC LETTER ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0649;;;;N;GLYPH FOR FINAL ARABIC ALEF MAQSURAH;;;;
+FEF1;ARABIC LETTER YEH ISOLATED FORM;Lo;0;AL;<isolated> 064A;;;;N;GLYPH FOR ISOLATE ARABIC YA;;;;
+FEF2;ARABIC LETTER YEH FINAL FORM;Lo;0;AL;<final> 064A;;;;N;GLYPH FOR FINAL ARABIC YA;;;;
+FEF3;ARABIC LETTER YEH INITIAL FORM;Lo;0;AL;<initial> 064A;;;;N;GLYPH FOR INITIAL ARABIC YA;;;;
+FEF4;ARABIC LETTER YEH MEDIAL FORM;Lo;0;AL;<medial> 064A;;;;N;GLYPH FOR MEDIAL ARABIC YA;;;;
+FEF5;ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0644 0622;;;;N;GLYPH FOR ISOLATE ARABIC MADDAH ON LIGATURE LAM ALEF;;;;
+FEF6;ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM;Lo;0;AL;<final> 0644 0622;;;;N;GLYPH FOR FINAL ARABIC MADDAH ON LIGATURE LAM ALEF;;;;
+FEF7;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0644 0623;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON LIGATURE LAM ALEF;;;;
+FEF8;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0644 0623;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON LIGATURE LAM ALEF;;;;
+FEF9;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM;Lo;0;AL;<isolated> 0644 0625;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH UNDER LIGATURE LAM ALEF;;;;
+FEFA;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM;Lo;0;AL;<final> 0644 0625;;;;N;GLYPH FOR FINAL ARABIC HAMZAH UNDER LIGATURE LAM ALEF;;;;
+FEFB;ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0644 0627;;;;N;GLYPH FOR ISOLATE ARABIC LIGATURE LAM ALEF;;;;
+FEFC;ARABIC LIGATURE LAM WITH ALEF FINAL FORM;Lo;0;AL;<final> 0644 0627;;;;N;GLYPH FOR FINAL ARABIC LIGATURE LAM ALEF;;;;
+FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;;
+FF01;FULLWIDTH EXCLAMATION MARK;Po;0;ON;<wide> 0021;;;;N;;;;;
+FF02;FULLWIDTH QUOTATION MARK;Po;0;ON;<wide> 0022;;;;N;;;;;
+FF03;FULLWIDTH NUMBER SIGN;Po;0;ET;<wide> 0023;;;;N;;;;;
+FF04;FULLWIDTH DOLLAR SIGN;Sc;0;ET;<wide> 0024;;;;N;;;;;
+FF05;FULLWIDTH PERCENT SIGN;Po;0;ET;<wide> 0025;;;;N;;;;;
+FF06;FULLWIDTH AMPERSAND;Po;0;ON;<wide> 0026;;;;N;;;;;
+FF07;FULLWIDTH APOSTROPHE;Po;0;ON;<wide> 0027;;;;N;;;;;
+FF08;FULLWIDTH LEFT PARENTHESIS;Ps;0;ON;<wide> 0028;;;;Y;FULLWIDTH OPENING PARENTHESIS;;;;
+FF09;FULLWIDTH RIGHT PARENTHESIS;Pe;0;ON;<wide> 0029;;;;Y;FULLWIDTH CLOSING PARENTHESIS;;;;
+FF0A;FULLWIDTH ASTERISK;Po;0;ON;<wide> 002A;;;;N;;;;;
+FF0B;FULLWIDTH PLUS SIGN;Sm;0;ES;<wide> 002B;;;;N;;;;;
+FF0C;FULLWIDTH COMMA;Po;0;CS;<wide> 002C;;;;N;;;;;
+FF0D;FULLWIDTH HYPHEN-MINUS;Pd;0;ES;<wide> 002D;;;;N;;;;;
+FF0E;FULLWIDTH FULL STOP;Po;0;CS;<wide> 002E;;;;N;FULLWIDTH PERIOD;;;;
+FF0F;FULLWIDTH SOLIDUS;Po;0;CS;<wide> 002F;;;;N;FULLWIDTH SLASH;;;;
+FF10;FULLWIDTH DIGIT ZERO;Nd;0;EN;<wide> 0030;0;0;0;N;;;;;
+FF11;FULLWIDTH DIGIT ONE;Nd;0;EN;<wide> 0031;1;1;1;N;;;;;
+FF12;FULLWIDTH DIGIT TWO;Nd;0;EN;<wide> 0032;2;2;2;N;;;;;
+FF13;FULLWIDTH DIGIT THREE;Nd;0;EN;<wide> 0033;3;3;3;N;;;;;
+FF14;FULLWIDTH DIGIT FOUR;Nd;0;EN;<wide> 0034;4;4;4;N;;;;;
+FF15;FULLWIDTH DIGIT FIVE;Nd;0;EN;<wide> 0035;5;5;5;N;;;;;
+FF16;FULLWIDTH DIGIT SIX;Nd;0;EN;<wide> 0036;6;6;6;N;;;;;
+FF17;FULLWIDTH DIGIT SEVEN;Nd;0;EN;<wide> 0037;7;7;7;N;;;;;
+FF18;FULLWIDTH DIGIT EIGHT;Nd;0;EN;<wide> 0038;8;8;8;N;;;;;
+FF19;FULLWIDTH DIGIT NINE;Nd;0;EN;<wide> 0039;9;9;9;N;;;;;
+FF1A;FULLWIDTH COLON;Po;0;CS;<wide> 003A;;;;N;;;;;
+FF1B;FULLWIDTH SEMICOLON;Po;0;ON;<wide> 003B;;;;N;;;;;
+FF1C;FULLWIDTH LESS-THAN SIGN;Sm;0;ON;<wide> 003C;;;;Y;;;;;
+FF1D;FULLWIDTH EQUALS SIGN;Sm;0;ON;<wide> 003D;;;;N;;;;;
+FF1E;FULLWIDTH GREATER-THAN SIGN;Sm;0;ON;<wide> 003E;;;;Y;;;;;
+FF1F;FULLWIDTH QUESTION MARK;Po;0;ON;<wide> 003F;;;;N;;;;;
+FF20;FULLWIDTH COMMERCIAL AT;Po;0;ON;<wide> 0040;;;;N;;;;;
+FF21;FULLWIDTH LATIN CAPITAL LETTER A;Lu;0;L;<wide> 0041;;;;N;;;;FF41;
+FF22;FULLWIDTH LATIN CAPITAL LETTER B;Lu;0;L;<wide> 0042;;;;N;;;;FF42;
+FF23;FULLWIDTH LATIN CAPITAL LETTER C;Lu;0;L;<wide> 0043;;;;N;;;;FF43;
+FF24;FULLWIDTH LATIN CAPITAL LETTER D;Lu;0;L;<wide> 0044;;;;N;;;;FF44;
+FF25;FULLWIDTH LATIN CAPITAL LETTER E;Lu;0;L;<wide> 0045;;;;N;;;;FF45;
+FF26;FULLWIDTH LATIN CAPITAL LETTER F;Lu;0;L;<wide> 0046;;;;N;;;;FF46;
+FF27;FULLWIDTH LATIN CAPITAL LETTER G;Lu;0;L;<wide> 0047;;;;N;;;;FF47;
+FF28;FULLWIDTH LATIN CAPITAL LETTER H;Lu;0;L;<wide> 0048;;;;N;;;;FF48;
+FF29;FULLWIDTH LATIN CAPITAL LETTER I;Lu;0;L;<wide> 0049;;;;N;;;;FF49;
+FF2A;FULLWIDTH LATIN CAPITAL LETTER J;Lu;0;L;<wide> 004A;;;;N;;;;FF4A;
+FF2B;FULLWIDTH LATIN CAPITAL LETTER K;Lu;0;L;<wide> 004B;;;;N;;;;FF4B;
+FF2C;FULLWIDTH LATIN CAPITAL LETTER L;Lu;0;L;<wide> 004C;;;;N;;;;FF4C;
+FF2D;FULLWIDTH LATIN CAPITAL LETTER M;Lu;0;L;<wide> 004D;;;;N;;;;FF4D;
+FF2E;FULLWIDTH LATIN CAPITAL LETTER N;Lu;0;L;<wide> 004E;;;;N;;;;FF4E;
+FF2F;FULLWIDTH LATIN CAPITAL LETTER O;Lu;0;L;<wide> 004F;;;;N;;;;FF4F;
+FF30;FULLWIDTH LATIN CAPITAL LETTER P;Lu;0;L;<wide> 0050;;;;N;;;;FF50;
+FF31;FULLWIDTH LATIN CAPITAL LETTER Q;Lu;0;L;<wide> 0051;;;;N;;;;FF51;
+FF32;FULLWIDTH LATIN CAPITAL LETTER R;Lu;0;L;<wide> 0052;;;;N;;;;FF52;
+FF33;FULLWIDTH LATIN CAPITAL LETTER S;Lu;0;L;<wide> 0053;;;;N;;;;FF53;
+FF34;FULLWIDTH LATIN CAPITAL LETTER T;Lu;0;L;<wide> 0054;;;;N;;;;FF54;
+FF35;FULLWIDTH LATIN CAPITAL LETTER U;Lu;0;L;<wide> 0055;;;;N;;;;FF55;
+FF36;FULLWIDTH LATIN CAPITAL LETTER V;Lu;0;L;<wide> 0056;;;;N;;;;FF56;
+FF37;FULLWIDTH LATIN CAPITAL LETTER W;Lu;0;L;<wide> 0057;;;;N;;;;FF57;
+FF38;FULLWIDTH LATIN CAPITAL LETTER X;Lu;0;L;<wide> 0058;;;;N;;;;FF58;
+FF39;FULLWIDTH LATIN CAPITAL LETTER Y;Lu;0;L;<wide> 0059;;;;N;;;;FF59;
+FF3A;FULLWIDTH LATIN CAPITAL LETTER Z;Lu;0;L;<wide> 005A;;;;N;;;;FF5A;
+FF3B;FULLWIDTH LEFT SQUARE BRACKET;Ps;0;ON;<wide> 005B;;;;Y;FULLWIDTH OPENING SQUARE BRACKET;;;;
+FF3C;FULLWIDTH REVERSE SOLIDUS;Po;0;ON;<wide> 005C;;;;N;FULLWIDTH BACKSLASH;;;;
+FF3D;FULLWIDTH RIGHT SQUARE BRACKET;Pe;0;ON;<wide> 005D;;;;Y;FULLWIDTH CLOSING SQUARE BRACKET;;;;
+FF3E;FULLWIDTH CIRCUMFLEX ACCENT;Sk;0;ON;<wide> 005E;;;;N;FULLWIDTH SPACING CIRCUMFLEX;;;;
+FF3F;FULLWIDTH LOW LINE;Pc;0;ON;<wide> 005F;;;;N;FULLWIDTH SPACING UNDERSCORE;;;;
+FF40;FULLWIDTH GRAVE ACCENT;Sk;0;ON;<wide> 0060;;;;N;FULLWIDTH SPACING GRAVE;;;;
+FF41;FULLWIDTH LATIN SMALL LETTER A;Ll;0;L;<wide> 0061;;;;N;;;FF21;;FF21
+FF42;FULLWIDTH LATIN SMALL LETTER B;Ll;0;L;<wide> 0062;;;;N;;;FF22;;FF22
+FF43;FULLWIDTH LATIN SMALL LETTER C;Ll;0;L;<wide> 0063;;;;N;;;FF23;;FF23
+FF44;FULLWIDTH LATIN SMALL LETTER D;Ll;0;L;<wide> 0064;;;;N;;;FF24;;FF24
+FF45;FULLWIDTH LATIN SMALL LETTER E;Ll;0;L;<wide> 0065;;;;N;;;FF25;;FF25
+FF46;FULLWIDTH LATIN SMALL LETTER F;Ll;0;L;<wide> 0066;;;;N;;;FF26;;FF26
+FF47;FULLWIDTH LATIN SMALL LETTER G;Ll;0;L;<wide> 0067;;;;N;;;FF27;;FF27
+FF48;FULLWIDTH LATIN SMALL LETTER H;Ll;0;L;<wide> 0068;;;;N;;;FF28;;FF28
+FF49;FULLWIDTH LATIN SMALL LETTER I;Ll;0;L;<wide> 0069;;;;N;;;FF29;;FF29
+FF4A;FULLWIDTH LATIN SMALL LETTER J;Ll;0;L;<wide> 006A;;;;N;;;FF2A;;FF2A
+FF4B;FULLWIDTH LATIN SMALL LETTER K;Ll;0;L;<wide> 006B;;;;N;;;FF2B;;FF2B
+FF4C;FULLWIDTH LATIN SMALL LETTER L;Ll;0;L;<wide> 006C;;;;N;;;FF2C;;FF2C
+FF4D;FULLWIDTH LATIN SMALL LETTER M;Ll;0;L;<wide> 006D;;;;N;;;FF2D;;FF2D
+FF4E;FULLWIDTH LATIN SMALL LETTER N;Ll;0;L;<wide> 006E;;;;N;;;FF2E;;FF2E
+FF4F;FULLWIDTH LATIN SMALL LETTER O;Ll;0;L;<wide> 006F;;;;N;;;FF2F;;FF2F
+FF50;FULLWIDTH LATIN SMALL LETTER P;Ll;0;L;<wide> 0070;;;;N;;;FF30;;FF30
+FF51;FULLWIDTH LATIN SMALL LETTER Q;Ll;0;L;<wide> 0071;;;;N;;;FF31;;FF31
+FF52;FULLWIDTH LATIN SMALL LETTER R;Ll;0;L;<wide> 0072;;;;N;;;FF32;;FF32
+FF53;FULLWIDTH LATIN SMALL LETTER S;Ll;0;L;<wide> 0073;;;;N;;;FF33;;FF33
+FF54;FULLWIDTH LATIN SMALL LETTER T;Ll;0;L;<wide> 0074;;;;N;;;FF34;;FF34
+FF55;FULLWIDTH LATIN SMALL LETTER U;Ll;0;L;<wide> 0075;;;;N;;;FF35;;FF35
+FF56;FULLWIDTH LATIN SMALL LETTER V;Ll;0;L;<wide> 0076;;;;N;;;FF36;;FF36
+FF57;FULLWIDTH LATIN SMALL LETTER W;Ll;0;L;<wide> 0077;;;;N;;;FF37;;FF37
+FF58;FULLWIDTH LATIN SMALL LETTER X;Ll;0;L;<wide> 0078;;;;N;;;FF38;;FF38
+FF59;FULLWIDTH LATIN SMALL LETTER Y;Ll;0;L;<wide> 0079;;;;N;;;FF39;;FF39
+FF5A;FULLWIDTH LATIN SMALL LETTER Z;Ll;0;L;<wide> 007A;;;;N;;;FF3A;;FF3A
+FF5B;FULLWIDTH LEFT CURLY BRACKET;Ps;0;ON;<wide> 007B;;;;Y;FULLWIDTH OPENING CURLY BRACKET;;;;
+FF5C;FULLWIDTH VERTICAL LINE;Sm;0;ON;<wide> 007C;;;;N;FULLWIDTH VERTICAL BAR;;;;
+FF5D;FULLWIDTH RIGHT CURLY BRACKET;Pe;0;ON;<wide> 007D;;;;Y;FULLWIDTH CLOSING CURLY BRACKET;;;;
+FF5E;FULLWIDTH TILDE;Sm;0;ON;<wide> 007E;;;;N;FULLWIDTH SPACING TILDE;;;;
+FF5F;FULLWIDTH LEFT WHITE PARENTHESIS;Ps;0;ON;<wide> 2985;;;;Y;;;;;
+FF60;FULLWIDTH RIGHT WHITE PARENTHESIS;Pe;0;ON;<wide> 2986;;;;Y;;;;;
+FF61;HALFWIDTH IDEOGRAPHIC FULL STOP;Po;0;ON;<narrow> 3002;;;;N;HALFWIDTH IDEOGRAPHIC PERIOD;;;;
+FF62;HALFWIDTH LEFT CORNER BRACKET;Ps;0;ON;<narrow> 300C;;;;Y;HALFWIDTH OPENING CORNER BRACKET;;;;
+FF63;HALFWIDTH RIGHT CORNER BRACKET;Pe;0;ON;<narrow> 300D;;;;Y;HALFWIDTH CLOSING CORNER BRACKET;;;;
+FF64;HALFWIDTH IDEOGRAPHIC COMMA;Po;0;ON;<narrow> 3001;;;;N;;;;;
+FF65;HALFWIDTH KATAKANA MIDDLE DOT;Po;0;ON;<narrow> 30FB;;;;N;;;;;
+FF66;HALFWIDTH KATAKANA LETTER WO;Lo;0;L;<narrow> 30F2;;;;N;;;;;
+FF67;HALFWIDTH KATAKANA LETTER SMALL A;Lo;0;L;<narrow> 30A1;;;;N;;;;;
+FF68;HALFWIDTH KATAKANA LETTER SMALL I;Lo;0;L;<narrow> 30A3;;;;N;;;;;
+FF69;HALFWIDTH KATAKANA LETTER SMALL U;Lo;0;L;<narrow> 30A5;;;;N;;;;;
+FF6A;HALFWIDTH KATAKANA LETTER SMALL E;Lo;0;L;<narrow> 30A7;;;;N;;;;;
+FF6B;HALFWIDTH KATAKANA LETTER SMALL O;Lo;0;L;<narrow> 30A9;;;;N;;;;;
+FF6C;HALFWIDTH KATAKANA LETTER SMALL YA;Lo;0;L;<narrow> 30E3;;;;N;;;;;
+FF6D;HALFWIDTH KATAKANA LETTER SMALL YU;Lo;0;L;<narrow> 30E5;;;;N;;;;;
+FF6E;HALFWIDTH KATAKANA LETTER SMALL YO;Lo;0;L;<narrow> 30E7;;;;N;;;;;
+FF6F;HALFWIDTH KATAKANA LETTER SMALL TU;Lo;0;L;<narrow> 30C3;;;;N;;;;;
+FF70;HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK;Lm;0;L;<narrow> 30FC;;;;N;;;;;
+FF71;HALFWIDTH KATAKANA LETTER A;Lo;0;L;<narrow> 30A2;;;;N;;;;;
+FF72;HALFWIDTH KATAKANA LETTER I;Lo;0;L;<narrow> 30A4;;;;N;;;;;
+FF73;HALFWIDTH KATAKANA LETTER U;Lo;0;L;<narrow> 30A6;;;;N;;;;;
+FF74;HALFWIDTH KATAKANA LETTER E;Lo;0;L;<narrow> 30A8;;;;N;;;;;
+FF75;HALFWIDTH KATAKANA LETTER O;Lo;0;L;<narrow> 30AA;;;;N;;;;;
+FF76;HALFWIDTH KATAKANA LETTER KA;Lo;0;L;<narrow> 30AB;;;;N;;;;;
+FF77;HALFWIDTH KATAKANA LETTER KI;Lo;0;L;<narrow> 30AD;;;;N;;;;;
+FF78;HALFWIDTH KATAKANA LETTER KU;Lo;0;L;<narrow> 30AF;;;;N;;;;;
+FF79;HALFWIDTH KATAKANA LETTER KE;Lo;0;L;<narrow> 30B1;;;;N;;;;;
+FF7A;HALFWIDTH KATAKANA LETTER KO;Lo;0;L;<narrow> 30B3;;;;N;;;;;
+FF7B;HALFWIDTH KATAKANA LETTER SA;Lo;0;L;<narrow> 30B5;;;;N;;;;;
+FF7C;HALFWIDTH KATAKANA LETTER SI;Lo;0;L;<narrow> 30B7;;;;N;;;;;
+FF7D;HALFWIDTH KATAKANA LETTER SU;Lo;0;L;<narrow> 30B9;;;;N;;;;;
+FF7E;HALFWIDTH KATAKANA LETTER SE;Lo;0;L;<narrow> 30BB;;;;N;;;;;
+FF7F;HALFWIDTH KATAKANA LETTER SO;Lo;0;L;<narrow> 30BD;;;;N;;;;;
+FF80;HALFWIDTH KATAKANA LETTER TA;Lo;0;L;<narrow> 30BF;;;;N;;;;;
+FF81;HALFWIDTH KATAKANA LETTER TI;Lo;0;L;<narrow> 30C1;;;;N;;;;;
+FF82;HALFWIDTH KATAKANA LETTER TU;Lo;0;L;<narrow> 30C4;;;;N;;;;;
+FF83;HALFWIDTH KATAKANA LETTER TE;Lo;0;L;<narrow> 30C6;;;;N;;;;;
+FF84;HALFWIDTH KATAKANA LETTER TO;Lo;0;L;<narrow> 30C8;;;;N;;;;;
+FF85;HALFWIDTH KATAKANA LETTER NA;Lo;0;L;<narrow> 30CA;;;;N;;;;;
+FF86;HALFWIDTH KATAKANA LETTER NI;Lo;0;L;<narrow> 30CB;;;;N;;;;;
+FF87;HALFWIDTH KATAKANA LETTER NU;Lo;0;L;<narrow> 30CC;;;;N;;;;;
+FF88;HALFWIDTH KATAKANA LETTER NE;Lo;0;L;<narrow> 30CD;;;;N;;;;;
+FF89;HALFWIDTH KATAKANA LETTER NO;Lo;0;L;<narrow> 30CE;;;;N;;;;;
+FF8A;HALFWIDTH KATAKANA LETTER HA;Lo;0;L;<narrow> 30CF;;;;N;;;;;
+FF8B;HALFWIDTH KATAKANA LETTER HI;Lo;0;L;<narrow> 30D2;;;;N;;;;;
+FF8C;HALFWIDTH KATAKANA LETTER HU;Lo;0;L;<narrow> 30D5;;;;N;;;;;
+FF8D;HALFWIDTH KATAKANA LETTER HE;Lo;0;L;<narrow> 30D8;;;;N;;;;;
+FF8E;HALFWIDTH KATAKANA LETTER HO;Lo;0;L;<narrow> 30DB;;;;N;;;;;
+FF8F;HALFWIDTH KATAKANA LETTER MA;Lo;0;L;<narrow> 30DE;;;;N;;;;;
+FF90;HALFWIDTH KATAKANA LETTER MI;Lo;0;L;<narrow> 30DF;;;;N;;;;;
+FF91;HALFWIDTH KATAKANA LETTER MU;Lo;0;L;<narrow> 30E0;;;;N;;;;;
+FF92;HALFWIDTH KATAKANA LETTER ME;Lo;0;L;<narrow> 30E1;;;;N;;;;;
+FF93;HALFWIDTH KATAKANA LETTER MO;Lo;0;L;<narrow> 30E2;;;;N;;;;;
+FF94;HALFWIDTH KATAKANA LETTER YA;Lo;0;L;<narrow> 30E4;;;;N;;;;;
+FF95;HALFWIDTH KATAKANA LETTER YU;Lo;0;L;<narrow> 30E6;;;;N;;;;;
+FF96;HALFWIDTH KATAKANA LETTER YO;Lo;0;L;<narrow> 30E8;;;;N;;;;;
+FF97;HALFWIDTH KATAKANA LETTER RA;Lo;0;L;<narrow> 30E9;;;;N;;;;;
+FF98;HALFWIDTH KATAKANA LETTER RI;Lo;0;L;<narrow> 30EA;;;;N;;;;;
+FF99;HALFWIDTH KATAKANA LETTER RU;Lo;0;L;<narrow> 30EB;;;;N;;;;;
+FF9A;HALFWIDTH KATAKANA LETTER RE;Lo;0;L;<narrow> 30EC;;;;N;;;;;
+FF9B;HALFWIDTH KATAKANA LETTER RO;Lo;0;L;<narrow> 30ED;;;;N;;;;;
+FF9C;HALFWIDTH KATAKANA LETTER WA;Lo;0;L;<narrow> 30EF;;;;N;;;;;
+FF9D;HALFWIDTH KATAKANA LETTER N;Lo;0;L;<narrow> 30F3;;;;N;;;;;
+FF9E;HALFWIDTH KATAKANA VOICED SOUND MARK;Lm;0;L;<narrow> 3099;;;;N;;;;;
+FF9F;HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK;Lm;0;L;<narrow> 309A;;;;N;;;;;
+FFA0;HALFWIDTH HANGUL FILLER;Lo;0;L;<narrow> 3164;;;;N;HALFWIDTH HANGUL CAE OM;;;;
+FFA1;HALFWIDTH HANGUL LETTER KIYEOK;Lo;0;L;<narrow> 3131;;;;N;HALFWIDTH HANGUL LETTER GIYEOG;;;;
+FFA2;HALFWIDTH HANGUL LETTER SSANGKIYEOK;Lo;0;L;<narrow> 3132;;;;N;HALFWIDTH HANGUL LETTER SSANG GIYEOG;;;;
+FFA3;HALFWIDTH HANGUL LETTER KIYEOK-SIOS;Lo;0;L;<narrow> 3133;;;;N;HALFWIDTH HANGUL LETTER GIYEOG SIOS;;;;
+FFA4;HALFWIDTH HANGUL LETTER NIEUN;Lo;0;L;<narrow> 3134;;;;N;;;;;
+FFA5;HALFWIDTH HANGUL LETTER NIEUN-CIEUC;Lo;0;L;<narrow> 3135;;;;N;HALFWIDTH HANGUL LETTER NIEUN JIEUJ;;;;
+FFA6;HALFWIDTH HANGUL LETTER NIEUN-HIEUH;Lo;0;L;<narrow> 3136;;;;N;HALFWIDTH HANGUL LETTER NIEUN HIEUH;;;;
+FFA7;HALFWIDTH HANGUL LETTER TIKEUT;Lo;0;L;<narrow> 3137;;;;N;HALFWIDTH HANGUL LETTER DIGEUD;;;;
+FFA8;HALFWIDTH HANGUL LETTER SSANGTIKEUT;Lo;0;L;<narrow> 3138;;;;N;HALFWIDTH HANGUL LETTER SSANG DIGEUD;;;;
+FFA9;HALFWIDTH HANGUL LETTER RIEUL;Lo;0;L;<narrow> 3139;;;;N;HALFWIDTH HANGUL LETTER LIEUL;;;;
+FFAA;HALFWIDTH HANGUL LETTER RIEUL-KIYEOK;Lo;0;L;<narrow> 313A;;;;N;HALFWIDTH HANGUL LETTER LIEUL GIYEOG;;;;
+FFAB;HALFWIDTH HANGUL LETTER RIEUL-MIEUM;Lo;0;L;<narrow> 313B;;;;N;HALFWIDTH HANGUL LETTER LIEUL MIEUM;;;;
+FFAC;HALFWIDTH HANGUL LETTER RIEUL-PIEUP;Lo;0;L;<narrow> 313C;;;;N;HALFWIDTH HANGUL LETTER LIEUL BIEUB;;;;
+FFAD;HALFWIDTH HANGUL LETTER RIEUL-SIOS;Lo;0;L;<narrow> 313D;;;;N;HALFWIDTH HANGUL LETTER LIEUL SIOS;;;;
+FFAE;HALFWIDTH HANGUL LETTER RIEUL-THIEUTH;Lo;0;L;<narrow> 313E;;;;N;HALFWIDTH HANGUL LETTER LIEUL TIEUT;;;;
+FFAF;HALFWIDTH HANGUL LETTER RIEUL-PHIEUPH;Lo;0;L;<narrow> 313F;;;;N;HALFWIDTH HANGUL LETTER LIEUL PIEUP;;;;
+FFB0;HALFWIDTH HANGUL LETTER RIEUL-HIEUH;Lo;0;L;<narrow> 3140;;;;N;HALFWIDTH HANGUL LETTER LIEUL HIEUH;;;;
+FFB1;HALFWIDTH HANGUL LETTER MIEUM;Lo;0;L;<narrow> 3141;;;;N;;;;;
+FFB2;HALFWIDTH HANGUL LETTER PIEUP;Lo;0;L;<narrow> 3142;;;;N;HALFWIDTH HANGUL LETTER BIEUB;;;;
+FFB3;HALFWIDTH HANGUL LETTER SSANGPIEUP;Lo;0;L;<narrow> 3143;;;;N;HALFWIDTH HANGUL LETTER SSANG BIEUB;;;;
+FFB4;HALFWIDTH HANGUL LETTER PIEUP-SIOS;Lo;0;L;<narrow> 3144;;;;N;HALFWIDTH HANGUL LETTER BIEUB SIOS;;;;
+FFB5;HALFWIDTH HANGUL LETTER SIOS;Lo;0;L;<narrow> 3145;;;;N;;;;;
+FFB6;HALFWIDTH HANGUL LETTER SSANGSIOS;Lo;0;L;<narrow> 3146;;;;N;HALFWIDTH HANGUL LETTER SSANG SIOS;;;;
+FFB7;HALFWIDTH HANGUL LETTER IEUNG;Lo;0;L;<narrow> 3147;;;;N;;;;;
+FFB8;HALFWIDTH HANGUL LETTER CIEUC;Lo;0;L;<narrow> 3148;;;;N;HALFWIDTH HANGUL LETTER JIEUJ;;;;
+FFB9;HALFWIDTH HANGUL LETTER SSANGCIEUC;Lo;0;L;<narrow> 3149;;;;N;HALFWIDTH HANGUL LETTER SSANG JIEUJ;;;;
+FFBA;HALFWIDTH HANGUL LETTER CHIEUCH;Lo;0;L;<narrow> 314A;;;;N;HALFWIDTH HANGUL LETTER CIEUC;;;;
+FFBB;HALFWIDTH HANGUL LETTER KHIEUKH;Lo;0;L;<narrow> 314B;;;;N;HALFWIDTH HANGUL LETTER KIYEOK;;;;
+FFBC;HALFWIDTH HANGUL LETTER THIEUTH;Lo;0;L;<narrow> 314C;;;;N;HALFWIDTH HANGUL LETTER TIEUT;;;;
+FFBD;HALFWIDTH HANGUL LETTER PHIEUPH;Lo;0;L;<narrow> 314D;;;;N;HALFWIDTH HANGUL LETTER PIEUP;;;;
+FFBE;HALFWIDTH HANGUL LETTER HIEUH;Lo;0;L;<narrow> 314E;;;;N;;;;;
+FFC2;HALFWIDTH HANGUL LETTER A;Lo;0;L;<narrow> 314F;;;;N;;;;;
+FFC3;HALFWIDTH HANGUL LETTER AE;Lo;0;L;<narrow> 3150;;;;N;;;;;
+FFC4;HALFWIDTH HANGUL LETTER YA;Lo;0;L;<narrow> 3151;;;;N;;;;;
+FFC5;HALFWIDTH HANGUL LETTER YAE;Lo;0;L;<narrow> 3152;;;;N;;;;;
+FFC6;HALFWIDTH HANGUL LETTER EO;Lo;0;L;<narrow> 3153;;;;N;;;;;
+FFC7;HALFWIDTH HANGUL LETTER E;Lo;0;L;<narrow> 3154;;;;N;;;;;
+FFCA;HALFWIDTH HANGUL LETTER YEO;Lo;0;L;<narrow> 3155;;;;N;;;;;
+FFCB;HALFWIDTH HANGUL LETTER YE;Lo;0;L;<narrow> 3156;;;;N;;;;;
+FFCC;HALFWIDTH HANGUL LETTER O;Lo;0;L;<narrow> 3157;;;;N;;;;;
+FFCD;HALFWIDTH HANGUL LETTER WA;Lo;0;L;<narrow> 3158;;;;N;;;;;
+FFCE;HALFWIDTH HANGUL LETTER WAE;Lo;0;L;<narrow> 3159;;;;N;;;;;
+FFCF;HALFWIDTH HANGUL LETTER OE;Lo;0;L;<narrow> 315A;;;;N;;;;;
+FFD2;HALFWIDTH HANGUL LETTER YO;Lo;0;L;<narrow> 315B;;;;N;;;;;
+FFD3;HALFWIDTH HANGUL LETTER U;Lo;0;L;<narrow> 315C;;;;N;;;;;
+FFD4;HALFWIDTH HANGUL LETTER WEO;Lo;0;L;<narrow> 315D;;;;N;;;;;
+FFD5;HALFWIDTH HANGUL LETTER WE;Lo;0;L;<narrow> 315E;;;;N;;;;;
+FFD6;HALFWIDTH HANGUL LETTER WI;Lo;0;L;<narrow> 315F;;;;N;;;;;
+FFD7;HALFWIDTH HANGUL LETTER YU;Lo;0;L;<narrow> 3160;;;;N;;;;;
+FFDA;HALFWIDTH HANGUL LETTER EU;Lo;0;L;<narrow> 3161;;;;N;;;;;
+FFDB;HALFWIDTH HANGUL LETTER YI;Lo;0;L;<narrow> 3162;;;;N;;;;;
+FFDC;HALFWIDTH HANGUL LETTER I;Lo;0;L;<narrow> 3163;;;;N;;;;;
+FFE0;FULLWIDTH CENT SIGN;Sc;0;ET;<wide> 00A2;;;;N;;;;;
+FFE1;FULLWIDTH POUND SIGN;Sc;0;ET;<wide> 00A3;;;;N;;;;;
+FFE2;FULLWIDTH NOT SIGN;Sm;0;ON;<wide> 00AC;;;;N;;;;;
+FFE3;FULLWIDTH MACRON;Sk;0;ON;<wide> 00AF;;;;N;FULLWIDTH SPACING MACRON;;;;
+FFE4;FULLWIDTH BROKEN BAR;So;0;ON;<wide> 00A6;;;;N;FULLWIDTH BROKEN VERTICAL BAR;;;;
+FFE5;FULLWIDTH YEN SIGN;Sc;0;ET;<wide> 00A5;;;;N;;;;;
+FFE6;FULLWIDTH WON SIGN;Sc;0;ET;<wide> 20A9;;;;N;;;;;
+FFE8;HALFWIDTH FORMS LIGHT VERTICAL;So;0;ON;<narrow> 2502;;;;N;;;;;
+FFE9;HALFWIDTH LEFTWARDS ARROW;Sm;0;ON;<narrow> 2190;;;;N;;;;;
+FFEA;HALFWIDTH UPWARDS ARROW;Sm;0;ON;<narrow> 2191;;;;N;;;;;
+FFEB;HALFWIDTH RIGHTWARDS ARROW;Sm;0;ON;<narrow> 2192;;;;N;;;;;
+FFEC;HALFWIDTH DOWNWARDS ARROW;Sm;0;ON;<narrow> 2193;;;;N;;;;;
+FFED;HALFWIDTH BLACK SQUARE;So;0;ON;<narrow> 25A0;;;;N;;;;;
+FFEE;HALFWIDTH WHITE CIRCLE;So;0;ON;<narrow> 25CB;;;;N;;;;;
+FFF9;INTERLINEAR ANNOTATION ANCHOR;Cf;0;ON;;;;;N;;;;;
+FFFA;INTERLINEAR ANNOTATION SEPARATOR;Cf;0;ON;;;;;N;;;;;
+FFFB;INTERLINEAR ANNOTATION TERMINATOR;Cf;0;ON;;;;;N;;;;;
+FFFC;OBJECT REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
+FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
+10000;LINEAR B SYLLABLE B008 A;Lo;0;L;;;;;N;;;;;
+10001;LINEAR B SYLLABLE B038 E;Lo;0;L;;;;;N;;;;;
+10002;LINEAR B SYLLABLE B028 I;Lo;0;L;;;;;N;;;;;
+10003;LINEAR B SYLLABLE B061 O;Lo;0;L;;;;;N;;;;;
+10004;LINEAR B SYLLABLE B010 U;Lo;0;L;;;;;N;;;;;
+10005;LINEAR B SYLLABLE B001 DA;Lo;0;L;;;;;N;;;;;
+10006;LINEAR B SYLLABLE B045 DE;Lo;0;L;;;;;N;;;;;
+10007;LINEAR B SYLLABLE B007 DI;Lo;0;L;;;;;N;;;;;
+10008;LINEAR B SYLLABLE B014 DO;Lo;0;L;;;;;N;;;;;
+10009;LINEAR B SYLLABLE B051 DU;Lo;0;L;;;;;N;;;;;
+1000A;LINEAR B SYLLABLE B057 JA;Lo;0;L;;;;;N;;;;;
+1000B;LINEAR B SYLLABLE B046 JE;Lo;0;L;;;;;N;;;;;
+1000D;LINEAR B SYLLABLE B036 JO;Lo;0;L;;;;;N;;;;;
+1000E;LINEAR B SYLLABLE B065 JU;Lo;0;L;;;;;N;;;;;
+1000F;LINEAR B SYLLABLE B077 KA;Lo;0;L;;;;;N;;;;;
+10010;LINEAR B SYLLABLE B044 KE;Lo;0;L;;;;;N;;;;;
+10011;LINEAR B SYLLABLE B067 KI;Lo;0;L;;;;;N;;;;;
+10012;LINEAR B SYLLABLE B070 KO;Lo;0;L;;;;;N;;;;;
+10013;LINEAR B SYLLABLE B081 KU;Lo;0;L;;;;;N;;;;;
+10014;LINEAR B SYLLABLE B080 MA;Lo;0;L;;;;;N;;;;;
+10015;LINEAR B SYLLABLE B013 ME;Lo;0;L;;;;;N;;;;;
+10016;LINEAR B SYLLABLE B073 MI;Lo;0;L;;;;;N;;;;;
+10017;LINEAR B SYLLABLE B015 MO;Lo;0;L;;;;;N;;;;;
+10018;LINEAR B SYLLABLE B023 MU;Lo;0;L;;;;;N;;;;;
+10019;LINEAR B SYLLABLE B006 NA;Lo;0;L;;;;;N;;;;;
+1001A;LINEAR B SYLLABLE B024 NE;Lo;0;L;;;;;N;;;;;
+1001B;LINEAR B SYLLABLE B030 NI;Lo;0;L;;;;;N;;;;;
+1001C;LINEAR B SYLLABLE B052 NO;Lo;0;L;;;;;N;;;;;
+1001D;LINEAR B SYLLABLE B055 NU;Lo;0;L;;;;;N;;;;;
+1001E;LINEAR B SYLLABLE B003 PA;Lo;0;L;;;;;N;;;;;
+1001F;LINEAR B SYLLABLE B072 PE;Lo;0;L;;;;;N;;;;;
+10020;LINEAR B SYLLABLE B039 PI;Lo;0;L;;;;;N;;;;;
+10021;LINEAR B SYLLABLE B011 PO;Lo;0;L;;;;;N;;;;;
+10022;LINEAR B SYLLABLE B050 PU;Lo;0;L;;;;;N;;;;;
+10023;LINEAR B SYLLABLE B016 QA;Lo;0;L;;;;;N;;;;;
+10024;LINEAR B SYLLABLE B078 QE;Lo;0;L;;;;;N;;;;;
+10025;LINEAR B SYLLABLE B021 QI;Lo;0;L;;;;;N;;;;;
+10026;LINEAR B SYLLABLE B032 QO;Lo;0;L;;;;;N;;;;;
+10028;LINEAR B SYLLABLE B060 RA;Lo;0;L;;;;;N;;;;;
+10029;LINEAR B SYLLABLE B027 RE;Lo;0;L;;;;;N;;;;;
+1002A;LINEAR B SYLLABLE B053 RI;Lo;0;L;;;;;N;;;;;
+1002B;LINEAR B SYLLABLE B002 RO;Lo;0;L;;;;;N;;;;;
+1002C;LINEAR B SYLLABLE B026 RU;Lo;0;L;;;;;N;;;;;
+1002D;LINEAR B SYLLABLE B031 SA;Lo;0;L;;;;;N;;;;;
+1002E;LINEAR B SYLLABLE B009 SE;Lo;0;L;;;;;N;;;;;
+1002F;LINEAR B SYLLABLE B041 SI;Lo;0;L;;;;;N;;;;;
+10030;LINEAR B SYLLABLE B012 SO;Lo;0;L;;;;;N;;;;;
+10031;LINEAR B SYLLABLE B058 SU;Lo;0;L;;;;;N;;;;;
+10032;LINEAR B SYLLABLE B059 TA;Lo;0;L;;;;;N;;;;;
+10033;LINEAR B SYLLABLE B004 TE;Lo;0;L;;;;;N;;;;;
+10034;LINEAR B SYLLABLE B037 TI;Lo;0;L;;;;;N;;;;;
+10035;LINEAR B SYLLABLE B005 TO;Lo;0;L;;;;;N;;;;;
+10036;LINEAR B SYLLABLE B069 TU;Lo;0;L;;;;;N;;;;;
+10037;LINEAR B SYLLABLE B054 WA;Lo;0;L;;;;;N;;;;;
+10038;LINEAR B SYLLABLE B075 WE;Lo;0;L;;;;;N;;;;;
+10039;LINEAR B SYLLABLE B040 WI;Lo;0;L;;;;;N;;;;;
+1003A;LINEAR B SYLLABLE B042 WO;Lo;0;L;;;;;N;;;;;
+1003C;LINEAR B SYLLABLE B017 ZA;Lo;0;L;;;;;N;;;;;
+1003D;LINEAR B SYLLABLE B074 ZE;Lo;0;L;;;;;N;;;;;
+1003F;LINEAR B SYLLABLE B020 ZO;Lo;0;L;;;;;N;;;;;
+10040;LINEAR B SYLLABLE B025 A2;Lo;0;L;;;;;N;;;;;
+10041;LINEAR B SYLLABLE B043 A3;Lo;0;L;;;;;N;;;;;
+10042;LINEAR B SYLLABLE B085 AU;Lo;0;L;;;;;N;;;;;
+10043;LINEAR B SYLLABLE B071 DWE;Lo;0;L;;;;;N;;;;;
+10044;LINEAR B SYLLABLE B090 DWO;Lo;0;L;;;;;N;;;;;
+10045;LINEAR B SYLLABLE B048 NWA;Lo;0;L;;;;;N;;;;;
+10046;LINEAR B SYLLABLE B029 PU2;Lo;0;L;;;;;N;;;;;
+10047;LINEAR B SYLLABLE B062 PTE;Lo;0;L;;;;;N;;;;;
+10048;LINEAR B SYLLABLE B076 RA2;Lo;0;L;;;;;N;;;;;
+10049;LINEAR B SYLLABLE B033 RA3;Lo;0;L;;;;;N;;;;;
+1004A;LINEAR B SYLLABLE B068 RO2;Lo;0;L;;;;;N;;;;;
+1004B;LINEAR B SYLLABLE B066 TA2;Lo;0;L;;;;;N;;;;;
+1004C;LINEAR B SYLLABLE B087 TWE;Lo;0;L;;;;;N;;;;;
+1004D;LINEAR B SYLLABLE B091 TWO;Lo;0;L;;;;;N;;;;;
+10050;LINEAR B SYMBOL B018;Lo;0;L;;;;;N;;;;;
+10051;LINEAR B SYMBOL B019;Lo;0;L;;;;;N;;;;;
+10052;LINEAR B SYMBOL B022;Lo;0;L;;;;;N;;;;;
+10053;LINEAR B SYMBOL B034;Lo;0;L;;;;;N;;;;;
+10054;LINEAR B SYMBOL B047;Lo;0;L;;;;;N;;;;;
+10055;LINEAR B SYMBOL B049;Lo;0;L;;;;;N;;;;;
+10056;LINEAR B SYMBOL B056;Lo;0;L;;;;;N;;;;;
+10057;LINEAR B SYMBOL B063;Lo;0;L;;;;;N;;;;;
+10058;LINEAR B SYMBOL B064;Lo;0;L;;;;;N;;;;;
+10059;LINEAR B SYMBOL B079;Lo;0;L;;;;;N;;;;;
+1005A;LINEAR B SYMBOL B082;Lo;0;L;;;;;N;;;;;
+1005B;LINEAR B SYMBOL B083;Lo;0;L;;;;;N;;;;;
+1005C;LINEAR B SYMBOL B086;Lo;0;L;;;;;N;;;;;
+1005D;LINEAR B SYMBOL B089;Lo;0;L;;;;;N;;;;;
+10080;LINEAR B IDEOGRAM B100 MAN;Lo;0;L;;;;;N;;;;;
+10081;LINEAR B IDEOGRAM B102 WOMAN;Lo;0;L;;;;;N;;;;;
+10082;LINEAR B IDEOGRAM B104 DEER;Lo;0;L;;;;;N;;;;;
+10083;LINEAR B IDEOGRAM B105 EQUID;Lo;0;L;;;;;N;;;;;
+10084;LINEAR B IDEOGRAM B105F MARE;Lo;0;L;;;;;N;;;;;
+10085;LINEAR B IDEOGRAM B105M STALLION;Lo;0;L;;;;;N;;;;;
+10086;LINEAR B IDEOGRAM B106F EWE;Lo;0;L;;;;;N;;;;;
+10087;LINEAR B IDEOGRAM B106M RAM;Lo;0;L;;;;;N;;;;;
+10088;LINEAR B IDEOGRAM B107F SHE-GOAT;Lo;0;L;;;;;N;;;;;
+10089;LINEAR B IDEOGRAM B107M HE-GOAT;Lo;0;L;;;;;N;;;;;
+1008A;LINEAR B IDEOGRAM B108F SOW;Lo;0;L;;;;;N;;;;;
+1008B;LINEAR B IDEOGRAM B108M BOAR;Lo;0;L;;;;;N;;;;;
+1008C;LINEAR B IDEOGRAM B109F COW;Lo;0;L;;;;;N;;;;;
+1008D;LINEAR B IDEOGRAM B109M BULL;Lo;0;L;;;;;N;;;;;
+1008E;LINEAR B IDEOGRAM B120 WHEAT;Lo;0;L;;;;;N;;;;;
+1008F;LINEAR B IDEOGRAM B121 BARLEY;Lo;0;L;;;;;N;;;;;
+10090;LINEAR B IDEOGRAM B122 OLIVE;Lo;0;L;;;;;N;;;;;
+10091;LINEAR B IDEOGRAM B123 SPICE;Lo;0;L;;;;;N;;;;;
+10092;LINEAR B IDEOGRAM B125 CYPERUS;Lo;0;L;;;;;N;;;;;
+10093;LINEAR B MONOGRAM B127 KAPO;Lo;0;L;;;;;N;;;;;
+10094;LINEAR B MONOGRAM B128 KANAKO;Lo;0;L;;;;;N;;;;;
+10095;LINEAR B IDEOGRAM B130 OIL;Lo;0;L;;;;;N;;;;;
+10096;LINEAR B IDEOGRAM B131 WINE;Lo;0;L;;;;;N;;;;;
+10097;LINEAR B IDEOGRAM B132;Lo;0;L;;;;;N;;;;;
+10098;LINEAR B MONOGRAM B133 AREPA;Lo;0;L;;;;;N;;;;;
+10099;LINEAR B MONOGRAM B135 MERI;Lo;0;L;;;;;N;;;;;
+1009A;LINEAR B IDEOGRAM B140 BRONZE;Lo;0;L;;;;;N;;;;;
+1009B;LINEAR B IDEOGRAM B141 GOLD;Lo;0;L;;;;;N;;;;;
+1009C;LINEAR B IDEOGRAM B142;Lo;0;L;;;;;N;;;;;
+1009D;LINEAR B IDEOGRAM B145 WOOL;Lo;0;L;;;;;N;;;;;
+1009E;LINEAR B IDEOGRAM B146;Lo;0;L;;;;;N;;;;;
+1009F;LINEAR B IDEOGRAM B150;Lo;0;L;;;;;N;;;;;
+100A0;LINEAR B IDEOGRAM B151 HORN;Lo;0;L;;;;;N;;;;;
+100A1;LINEAR B IDEOGRAM B152;Lo;0;L;;;;;N;;;;;
+100A2;LINEAR B IDEOGRAM B153;Lo;0;L;;;;;N;;;;;
+100A3;LINEAR B IDEOGRAM B154;Lo;0;L;;;;;N;;;;;
+100A4;LINEAR B MONOGRAM B156 TURO2;Lo;0;L;;;;;N;;;;;
+100A5;LINEAR B IDEOGRAM B157;Lo;0;L;;;;;N;;;;;
+100A6;LINEAR B IDEOGRAM B158;Lo;0;L;;;;;N;;;;;
+100A7;LINEAR B IDEOGRAM B159 CLOTH;Lo;0;L;;;;;N;;;;;
+100A8;LINEAR B IDEOGRAM B160;Lo;0;L;;;;;N;;;;;
+100A9;LINEAR B IDEOGRAM B161;Lo;0;L;;;;;N;;;;;
+100AA;LINEAR B IDEOGRAM B162 GARMENT;Lo;0;L;;;;;N;;;;;
+100AB;LINEAR B IDEOGRAM B163 ARMOUR;Lo;0;L;;;;;N;;;;;
+100AC;LINEAR B IDEOGRAM B164;Lo;0;L;;;;;N;;;;;
+100AD;LINEAR B IDEOGRAM B165;Lo;0;L;;;;;N;;;;;
+100AE;LINEAR B IDEOGRAM B166;Lo;0;L;;;;;N;;;;;
+100AF;LINEAR B IDEOGRAM B167;Lo;0;L;;;;;N;;;;;
+100B0;LINEAR B IDEOGRAM B168;Lo;0;L;;;;;N;;;;;
+100B1;LINEAR B IDEOGRAM B169;Lo;0;L;;;;;N;;;;;
+100B2;LINEAR B IDEOGRAM B170;Lo;0;L;;;;;N;;;;;
+100B3;LINEAR B IDEOGRAM B171;Lo;0;L;;;;;N;;;;;
+100B4;LINEAR B IDEOGRAM B172;Lo;0;L;;;;;N;;;;;
+100B5;LINEAR B IDEOGRAM B173 MONTH;Lo;0;L;;;;;N;;;;;
+100B6;LINEAR B IDEOGRAM B174;Lo;0;L;;;;;N;;;;;
+100B7;LINEAR B IDEOGRAM B176 TREE;Lo;0;L;;;;;N;;;;;
+100B8;LINEAR B IDEOGRAM B177;Lo;0;L;;;;;N;;;;;
+100B9;LINEAR B IDEOGRAM B178;Lo;0;L;;;;;N;;;;;
+100BA;LINEAR B IDEOGRAM B179;Lo;0;L;;;;;N;;;;;
+100BB;LINEAR B IDEOGRAM B180;Lo;0;L;;;;;N;;;;;
+100BC;LINEAR B IDEOGRAM B181;Lo;0;L;;;;;N;;;;;
+100BD;LINEAR B IDEOGRAM B182;Lo;0;L;;;;;N;;;;;
+100BE;LINEAR B IDEOGRAM B183;Lo;0;L;;;;;N;;;;;
+100BF;LINEAR B IDEOGRAM B184;Lo;0;L;;;;;N;;;;;
+100C0;LINEAR B IDEOGRAM B185;Lo;0;L;;;;;N;;;;;
+100C1;LINEAR B IDEOGRAM B189;Lo;0;L;;;;;N;;;;;
+100C2;LINEAR B IDEOGRAM B190;Lo;0;L;;;;;N;;;;;
+100C3;LINEAR B IDEOGRAM B191 HELMET;Lo;0;L;;;;;N;;;;;
+100C4;LINEAR B IDEOGRAM B220 FOOTSTOOL;Lo;0;L;;;;;N;;;;;
+100C5;LINEAR B IDEOGRAM B225 BATHTUB;Lo;0;L;;;;;N;;;;;
+100C6;LINEAR B IDEOGRAM B230 SPEAR;Lo;0;L;;;;;N;;;;;
+100C7;LINEAR B IDEOGRAM B231 ARROW;Lo;0;L;;;;;N;;;;;
+100C8;LINEAR B IDEOGRAM B232;Lo;0;L;;;;;N;;;;;
+100C9;LINEAR B IDEOGRAM B233 SWORD;Lo;0;L;;;;;N;;;;;
+100CA;LINEAR B IDEOGRAM B234;Lo;0;L;;;;;N;;;;;
+100CB;LINEAR B IDEOGRAM B236;Lo;0;L;;;;;N;;;;;
+100CC;LINEAR B IDEOGRAM B240 WHEELED CHARIOT;Lo;0;L;;;;;N;;;;;
+100CD;LINEAR B IDEOGRAM B241 CHARIOT;Lo;0;L;;;;;N;;;;;
+100CE;LINEAR B IDEOGRAM B242 CHARIOT FRAME;Lo;0;L;;;;;N;;;;;
+100CF;LINEAR B IDEOGRAM B243 WHEEL;Lo;0;L;;;;;N;;;;;
+100D0;LINEAR B IDEOGRAM B245;Lo;0;L;;;;;N;;;;;
+100D1;LINEAR B IDEOGRAM B246;Lo;0;L;;;;;N;;;;;
+100D2;LINEAR B MONOGRAM B247 DIPTE;Lo;0;L;;;;;N;;;;;
+100D3;LINEAR B IDEOGRAM B248;Lo;0;L;;;;;N;;;;;
+100D4;LINEAR B IDEOGRAM B249;Lo;0;L;;;;;N;;;;;
+100D5;LINEAR B IDEOGRAM B251;Lo;0;L;;;;;N;;;;;
+100D6;LINEAR B IDEOGRAM B252;Lo;0;L;;;;;N;;;;;
+100D7;LINEAR B IDEOGRAM B253;Lo;0;L;;;;;N;;;;;
+100D8;LINEAR B IDEOGRAM B254 DART;Lo;0;L;;;;;N;;;;;
+100D9;LINEAR B IDEOGRAM B255;Lo;0;L;;;;;N;;;;;
+100DA;LINEAR B IDEOGRAM B256;Lo;0;L;;;;;N;;;;;
+100DB;LINEAR B IDEOGRAM B257;Lo;0;L;;;;;N;;;;;
+100DC;LINEAR B IDEOGRAM B258;Lo;0;L;;;;;N;;;;;
+100DD;LINEAR B IDEOGRAM B259;Lo;0;L;;;;;N;;;;;
+100DE;LINEAR B IDEOGRAM VESSEL B155;Lo;0;L;;;;;N;;;;;
+100DF;LINEAR B IDEOGRAM VESSEL B200;Lo;0;L;;;;;N;;;;;
+100E0;LINEAR B IDEOGRAM VESSEL B201;Lo;0;L;;;;;N;;;;;
+100E1;LINEAR B IDEOGRAM VESSEL B202;Lo;0;L;;;;;N;;;;;
+100E2;LINEAR B IDEOGRAM VESSEL B203;Lo;0;L;;;;;N;;;;;
+100E3;LINEAR B IDEOGRAM VESSEL B204;Lo;0;L;;;;;N;;;;;
+100E4;LINEAR B IDEOGRAM VESSEL B205;Lo;0;L;;;;;N;;;;;
+100E5;LINEAR B IDEOGRAM VESSEL B206;Lo;0;L;;;;;N;;;;;
+100E6;LINEAR B IDEOGRAM VESSEL B207;Lo;0;L;;;;;N;;;;;
+100E7;LINEAR B IDEOGRAM VESSEL B208;Lo;0;L;;;;;N;;;;;
+100E8;LINEAR B IDEOGRAM VESSEL B209;Lo;0;L;;;;;N;;;;;
+100E9;LINEAR B IDEOGRAM VESSEL B210;Lo;0;L;;;;;N;;;;;
+100EA;LINEAR B IDEOGRAM VESSEL B211;Lo;0;L;;;;;N;;;;;
+100EB;LINEAR B IDEOGRAM VESSEL B212;Lo;0;L;;;;;N;;;;;
+100EC;LINEAR B IDEOGRAM VESSEL B213;Lo;0;L;;;;;N;;;;;
+100ED;LINEAR B IDEOGRAM VESSEL B214;Lo;0;L;;;;;N;;;;;
+100EE;LINEAR B IDEOGRAM VESSEL B215;Lo;0;L;;;;;N;;;;;
+100EF;LINEAR B IDEOGRAM VESSEL B216;Lo;0;L;;;;;N;;;;;
+100F0;LINEAR B IDEOGRAM VESSEL B217;Lo;0;L;;;;;N;;;;;
+100F1;LINEAR B IDEOGRAM VESSEL B218;Lo;0;L;;;;;N;;;;;
+100F2;LINEAR B IDEOGRAM VESSEL B219;Lo;0;L;;;;;N;;;;;
+100F3;LINEAR B IDEOGRAM VESSEL B221;Lo;0;L;;;;;N;;;;;
+100F4;LINEAR B IDEOGRAM VESSEL B222;Lo;0;L;;;;;N;;;;;
+100F5;LINEAR B IDEOGRAM VESSEL B226;Lo;0;L;;;;;N;;;;;
+100F6;LINEAR B IDEOGRAM VESSEL B227;Lo;0;L;;;;;N;;;;;
+100F7;LINEAR B IDEOGRAM VESSEL B228;Lo;0;L;;;;;N;;;;;
+100F8;LINEAR B IDEOGRAM VESSEL B229;Lo;0;L;;;;;N;;;;;
+100F9;LINEAR B IDEOGRAM VESSEL B250;Lo;0;L;;;;;N;;;;;
+100FA;LINEAR B IDEOGRAM VESSEL B305;Lo;0;L;;;;;N;;;;;
+10100;AEGEAN WORD SEPARATOR LINE;Po;0;L;;;;;N;;;;;
+10101;AEGEAN WORD SEPARATOR DOT;Po;0;ON;;;;;N;;;;;
+10102;AEGEAN CHECK MARK;Po;0;L;;;;;N;;;;;
+10107;AEGEAN NUMBER ONE;No;0;L;;;;1;N;;;;;
+10108;AEGEAN NUMBER TWO;No;0;L;;;;2;N;;;;;
+10109;AEGEAN NUMBER THREE;No;0;L;;;;3;N;;;;;
+1010A;AEGEAN NUMBER FOUR;No;0;L;;;;4;N;;;;;
+1010B;AEGEAN NUMBER FIVE;No;0;L;;;;5;N;;;;;
+1010C;AEGEAN NUMBER SIX;No;0;L;;;;6;N;;;;;
+1010D;AEGEAN NUMBER SEVEN;No;0;L;;;;7;N;;;;;
+1010E;AEGEAN NUMBER EIGHT;No;0;L;;;;8;N;;;;;
+1010F;AEGEAN NUMBER NINE;No;0;L;;;;9;N;;;;;
+10110;AEGEAN NUMBER TEN;No;0;L;;;;10;N;;;;;
+10111;AEGEAN NUMBER TWENTY;No;0;L;;;;20;N;;;;;
+10112;AEGEAN NUMBER THIRTY;No;0;L;;;;30;N;;;;;
+10113;AEGEAN NUMBER FORTY;No;0;L;;;;40;N;;;;;
+10114;AEGEAN NUMBER FIFTY;No;0;L;;;;50;N;;;;;
+10115;AEGEAN NUMBER SIXTY;No;0;L;;;;60;N;;;;;
+10116;AEGEAN NUMBER SEVENTY;No;0;L;;;;70;N;;;;;
+10117;AEGEAN NUMBER EIGHTY;No;0;L;;;;80;N;;;;;
+10118;AEGEAN NUMBER NINETY;No;0;L;;;;90;N;;;;;
+10119;AEGEAN NUMBER ONE HUNDRED;No;0;L;;;;100;N;;;;;
+1011A;AEGEAN NUMBER TWO HUNDRED;No;0;L;;;;200;N;;;;;
+1011B;AEGEAN NUMBER THREE HUNDRED;No;0;L;;;;300;N;;;;;
+1011C;AEGEAN NUMBER FOUR HUNDRED;No;0;L;;;;400;N;;;;;
+1011D;AEGEAN NUMBER FIVE HUNDRED;No;0;L;;;;500;N;;;;;
+1011E;AEGEAN NUMBER SIX HUNDRED;No;0;L;;;;600;N;;;;;
+1011F;AEGEAN NUMBER SEVEN HUNDRED;No;0;L;;;;700;N;;;;;
+10120;AEGEAN NUMBER EIGHT HUNDRED;No;0;L;;;;800;N;;;;;
+10121;AEGEAN NUMBER NINE HUNDRED;No;0;L;;;;900;N;;;;;
+10122;AEGEAN NUMBER ONE THOUSAND;No;0;L;;;;1000;N;;;;;
+10123;AEGEAN NUMBER TWO THOUSAND;No;0;L;;;;2000;N;;;;;
+10124;AEGEAN NUMBER THREE THOUSAND;No;0;L;;;;3000;N;;;;;
+10125;AEGEAN NUMBER FOUR THOUSAND;No;0;L;;;;4000;N;;;;;
+10126;AEGEAN NUMBER FIVE THOUSAND;No;0;L;;;;5000;N;;;;;
+10127;AEGEAN NUMBER SIX THOUSAND;No;0;L;;;;6000;N;;;;;
+10128;AEGEAN NUMBER SEVEN THOUSAND;No;0;L;;;;7000;N;;;;;
+10129;AEGEAN NUMBER EIGHT THOUSAND;No;0;L;;;;8000;N;;;;;
+1012A;AEGEAN NUMBER NINE THOUSAND;No;0;L;;;;9000;N;;;;;
+1012B;AEGEAN NUMBER TEN THOUSAND;No;0;L;;;;10000;N;;;;;
+1012C;AEGEAN NUMBER TWENTY THOUSAND;No;0;L;;;;20000;N;;;;;
+1012D;AEGEAN NUMBER THIRTY THOUSAND;No;0;L;;;;30000;N;;;;;
+1012E;AEGEAN NUMBER FORTY THOUSAND;No;0;L;;;;40000;N;;;;;
+1012F;AEGEAN NUMBER FIFTY THOUSAND;No;0;L;;;;50000;N;;;;;
+10130;AEGEAN NUMBER SIXTY THOUSAND;No;0;L;;;;60000;N;;;;;
+10131;AEGEAN NUMBER SEVENTY THOUSAND;No;0;L;;;;70000;N;;;;;
+10132;AEGEAN NUMBER EIGHTY THOUSAND;No;0;L;;;;80000;N;;;;;
+10133;AEGEAN NUMBER NINETY THOUSAND;No;0;L;;;;90000;N;;;;;
+10137;AEGEAN WEIGHT BASE UNIT;So;0;L;;;;;N;;;;;
+10138;AEGEAN WEIGHT FIRST SUBUNIT;So;0;L;;;;;N;;;;;
+10139;AEGEAN WEIGHT SECOND SUBUNIT;So;0;L;;;;;N;;;;;
+1013A;AEGEAN WEIGHT THIRD SUBUNIT;So;0;L;;;;;N;;;;;
+1013B;AEGEAN WEIGHT FOURTH SUBUNIT;So;0;L;;;;;N;;;;;
+1013C;AEGEAN DRY MEASURE FIRST SUBUNIT;So;0;L;;;;;N;;;;;
+1013D;AEGEAN LIQUID MEASURE FIRST SUBUNIT;So;0;L;;;;;N;;;;;
+1013E;AEGEAN MEASURE SECOND SUBUNIT;So;0;L;;;;;N;;;;;
+1013F;AEGEAN MEASURE THIRD SUBUNIT;So;0;L;;;;;N;;;;;
+10140;GREEK ACROPHONIC ATTIC ONE QUARTER;Nl;0;ON;;;;1/4;N;;;;;
+10141;GREEK ACROPHONIC ATTIC ONE HALF;Nl;0;ON;;;;1/2;N;;;;;
+10142;GREEK ACROPHONIC ATTIC ONE DRACHMA;Nl;0;ON;;;;1;N;;;;;
+10143;GREEK ACROPHONIC ATTIC FIVE;Nl;0;ON;;;;5;N;;;;;
+10144;GREEK ACROPHONIC ATTIC FIFTY;Nl;0;ON;;;;50;N;;;;;
+10145;GREEK ACROPHONIC ATTIC FIVE HUNDRED;Nl;0;ON;;;;500;N;;;;;
+10146;GREEK ACROPHONIC ATTIC FIVE THOUSAND;Nl;0;ON;;;;5000;N;;;;;
+10147;GREEK ACROPHONIC ATTIC FIFTY THOUSAND;Nl;0;ON;;;;50000;N;;;;;
+10148;GREEK ACROPHONIC ATTIC FIVE TALENTS;Nl;0;ON;;;;5;N;;;;;
+10149;GREEK ACROPHONIC ATTIC TEN TALENTS;Nl;0;ON;;;;10;N;;;;;
+1014A;GREEK ACROPHONIC ATTIC FIFTY TALENTS;Nl;0;ON;;;;50;N;;;;;
+1014B;GREEK ACROPHONIC ATTIC ONE HUNDRED TALENTS;Nl;0;ON;;;;100;N;;;;;
+1014C;GREEK ACROPHONIC ATTIC FIVE HUNDRED TALENTS;Nl;0;ON;;;;500;N;;;;;
+1014D;GREEK ACROPHONIC ATTIC ONE THOUSAND TALENTS;Nl;0;ON;;;;1000;N;;;;;
+1014E;GREEK ACROPHONIC ATTIC FIVE THOUSAND TALENTS;Nl;0;ON;;;;5000;N;;;;;
+1014F;GREEK ACROPHONIC ATTIC FIVE STATERS;Nl;0;ON;;;;5;N;;;;;
+10150;GREEK ACROPHONIC ATTIC TEN STATERS;Nl;0;ON;;;;10;N;;;;;
+10151;GREEK ACROPHONIC ATTIC FIFTY STATERS;Nl;0;ON;;;;50;N;;;;;
+10152;GREEK ACROPHONIC ATTIC ONE HUNDRED STATERS;Nl;0;ON;;;;100;N;;;;;
+10153;GREEK ACROPHONIC ATTIC FIVE HUNDRED STATERS;Nl;0;ON;;;;500;N;;;;;
+10154;GREEK ACROPHONIC ATTIC ONE THOUSAND STATERS;Nl;0;ON;;;;1000;N;;;;;
+10155;GREEK ACROPHONIC ATTIC TEN THOUSAND STATERS;Nl;0;ON;;;;10000;N;;;;;
+10156;GREEK ACROPHONIC ATTIC FIFTY THOUSAND STATERS;Nl;0;ON;;;;50000;N;;;;;
+10157;GREEK ACROPHONIC ATTIC TEN MNAS;Nl;0;ON;;;;10;N;;;;;
+10158;GREEK ACROPHONIC HERAEUM ONE PLETHRON;Nl;0;ON;;;;1;N;;;;;
+10159;GREEK ACROPHONIC THESPIAN ONE;Nl;0;ON;;;;1;N;;;;;
+1015A;GREEK ACROPHONIC HERMIONIAN ONE;Nl;0;ON;;;;1;N;;;;;
+1015B;GREEK ACROPHONIC EPIDAUREAN TWO;Nl;0;ON;;;;2;N;;;;;
+1015C;GREEK ACROPHONIC THESPIAN TWO;Nl;0;ON;;;;2;N;;;;;
+1015D;GREEK ACROPHONIC CYRENAIC TWO DRACHMAS;Nl;0;ON;;;;2;N;;;;;
+1015E;GREEK ACROPHONIC EPIDAUREAN TWO DRACHMAS;Nl;0;ON;;;;2;N;;;;;
+1015F;GREEK ACROPHONIC TROEZENIAN FIVE;Nl;0;ON;;;;5;N;;;;;
+10160;GREEK ACROPHONIC TROEZENIAN TEN;Nl;0;ON;;;;10;N;;;;;
+10161;GREEK ACROPHONIC TROEZENIAN TEN ALTERNATE FORM;Nl;0;ON;;;;10;N;;;;;
+10162;GREEK ACROPHONIC HERMIONIAN TEN;Nl;0;ON;;;;10;N;;;;;
+10163;GREEK ACROPHONIC MESSENIAN TEN;Nl;0;ON;;;;10;N;;;;;
+10164;GREEK ACROPHONIC THESPIAN TEN;Nl;0;ON;;;;10;N;;;;;
+10165;GREEK ACROPHONIC THESPIAN THIRTY;Nl;0;ON;;;;30;N;;;;;
+10166;GREEK ACROPHONIC TROEZENIAN FIFTY;Nl;0;ON;;;;50;N;;;;;
+10167;GREEK ACROPHONIC TROEZENIAN FIFTY ALTERNATE FORM;Nl;0;ON;;;;50;N;;;;;
+10168;GREEK ACROPHONIC HERMIONIAN FIFTY;Nl;0;ON;;;;50;N;;;;;
+10169;GREEK ACROPHONIC THESPIAN FIFTY;Nl;0;ON;;;;50;N;;;;;
+1016A;GREEK ACROPHONIC THESPIAN ONE HUNDRED;Nl;0;ON;;;;100;N;;;;;
+1016B;GREEK ACROPHONIC THESPIAN THREE HUNDRED;Nl;0;ON;;;;300;N;;;;;
+1016C;GREEK ACROPHONIC EPIDAUREAN FIVE HUNDRED;Nl;0;ON;;;;500;N;;;;;
+1016D;GREEK ACROPHONIC TROEZENIAN FIVE HUNDRED;Nl;0;ON;;;;500;N;;;;;
+1016E;GREEK ACROPHONIC THESPIAN FIVE HUNDRED;Nl;0;ON;;;;500;N;;;;;
+1016F;GREEK ACROPHONIC CARYSTIAN FIVE HUNDRED;Nl;0;ON;;;;500;N;;;;;
+10170;GREEK ACROPHONIC NAXIAN FIVE HUNDRED;Nl;0;ON;;;;500;N;;;;;
+10171;GREEK ACROPHONIC THESPIAN ONE THOUSAND;Nl;0;ON;;;;1000;N;;;;;
+10172;GREEK ACROPHONIC THESPIAN FIVE THOUSAND;Nl;0;ON;;;;5000;N;;;;;
+10173;GREEK ACROPHONIC DELPHIC FIVE MNAS;Nl;0;ON;;;;5;N;;;;;
+10174;GREEK ACROPHONIC STRATIAN FIFTY MNAS;Nl;0;ON;;;;50;N;;;;;
+10175;GREEK ONE HALF SIGN;No;0;ON;;;;1/2;N;;;;;
+10176;GREEK ONE HALF SIGN ALTERNATE FORM;No;0;ON;;;;1/2;N;;;;;
+10177;GREEK TWO THIRDS SIGN;No;0;ON;;;;2/3;N;;;;;
+10178;GREEK THREE QUARTERS SIGN;No;0;ON;;;;3/4;N;;;;;
+10179;GREEK YEAR SIGN;So;0;ON;;;;;N;;;;;
+1017A;GREEK TALENT SIGN;So;0;ON;;;;;N;;;;;
+1017B;GREEK DRACHMA SIGN;So;0;ON;;;;;N;;;;;
+1017C;GREEK OBOL SIGN;So;0;ON;;;;;N;;;;;
+1017D;GREEK TWO OBOLS SIGN;So;0;ON;;;;;N;;;;;
+1017E;GREEK THREE OBOLS SIGN;So;0;ON;;;;;N;;;;;
+1017F;GREEK FOUR OBOLS SIGN;So;0;ON;;;;;N;;;;;
+10180;GREEK FIVE OBOLS SIGN;So;0;ON;;;;;N;;;;;
+10181;GREEK METRETES SIGN;So;0;ON;;;;;N;;;;;
+10182;GREEK KYATHOS BASE SIGN;So;0;ON;;;;;N;;;;;
+10183;GREEK LITRA SIGN;So;0;ON;;;;;N;;;;;
+10184;GREEK OUNKIA SIGN;So;0;ON;;;;;N;;;;;
+10185;GREEK XESTES SIGN;So;0;ON;;;;;N;;;;;
+10186;GREEK ARTABE SIGN;So;0;ON;;;;;N;;;;;
+10187;GREEK AROURA SIGN;So;0;ON;;;;;N;;;;;
+10188;GREEK GRAMMA SIGN;So;0;ON;;;;;N;;;;;
+10189;GREEK TRYBLION BASE SIGN;So;0;ON;;;;;N;;;;;
+1018A;GREEK ZERO SIGN;No;0;ON;;;;0;N;;;;;
+1018B;GREEK ONE QUARTER SIGN;No;0;ON;;;;1/4;N;;;;;
+1018C;GREEK SINUSOID SIGN;So;0;ON;;;;;N;;;;;
+1018D;GREEK INDICTION SIGN;So;0;L;;;;;N;;;;;
+1018E;NOMISMA SIGN;So;0;L;;;;;N;;;;;
+10190;ROMAN SEXTANS SIGN;So;0;ON;;;;;N;;;;;
+10191;ROMAN UNCIA SIGN;So;0;ON;;;;;N;;;;;
+10192;ROMAN SEMUNCIA SIGN;So;0;ON;;;;;N;;;;;
+10193;ROMAN SEXTULA SIGN;So;0;ON;;;;;N;;;;;
+10194;ROMAN DIMIDIA SEXTULA SIGN;So;0;ON;;;;;N;;;;;
+10195;ROMAN SILIQUA SIGN;So;0;ON;;;;;N;;;;;
+10196;ROMAN DENARIUS SIGN;So;0;ON;;;;;N;;;;;
+10197;ROMAN QUINARIUS SIGN;So;0;ON;;;;;N;;;;;
+10198;ROMAN SESTERTIUS SIGN;So;0;ON;;;;;N;;;;;
+10199;ROMAN DUPONDIUS SIGN;So;0;ON;;;;;N;;;;;
+1019A;ROMAN AS SIGN;So;0;ON;;;;;N;;;;;
+1019B;ROMAN CENTURIAL SIGN;So;0;ON;;;;;N;;;;;
+101A0;GREEK SYMBOL TAU RHO;So;0;ON;;;;;N;;;;;
+101D0;PHAISTOS DISC SIGN PEDESTRIAN;So;0;L;;;;;N;;;;;
+101D1;PHAISTOS DISC SIGN PLUMED HEAD;So;0;L;;;;;N;;;;;
+101D2;PHAISTOS DISC SIGN TATTOOED HEAD;So;0;L;;;;;N;;;;;
+101D3;PHAISTOS DISC SIGN CAPTIVE;So;0;L;;;;;N;;;;;
+101D4;PHAISTOS DISC SIGN CHILD;So;0;L;;;;;N;;;;;
+101D5;PHAISTOS DISC SIGN WOMAN;So;0;L;;;;;N;;;;;
+101D6;PHAISTOS DISC SIGN HELMET;So;0;L;;;;;N;;;;;
+101D7;PHAISTOS DISC SIGN GAUNTLET;So;0;L;;;;;N;;;;;
+101D8;PHAISTOS DISC SIGN TIARA;So;0;L;;;;;N;;;;;
+101D9;PHAISTOS DISC SIGN ARROW;So;0;L;;;;;N;;;;;
+101DA;PHAISTOS DISC SIGN BOW;So;0;L;;;;;N;;;;;
+101DB;PHAISTOS DISC SIGN SHIELD;So;0;L;;;;;N;;;;;
+101DC;PHAISTOS DISC SIGN CLUB;So;0;L;;;;;N;;;;;
+101DD;PHAISTOS DISC SIGN MANACLES;So;0;L;;;;;N;;;;;
+101DE;PHAISTOS DISC SIGN MATTOCK;So;0;L;;;;;N;;;;;
+101DF;PHAISTOS DISC SIGN SAW;So;0;L;;;;;N;;;;;
+101E0;PHAISTOS DISC SIGN LID;So;0;L;;;;;N;;;;;
+101E1;PHAISTOS DISC SIGN BOOMERANG;So;0;L;;;;;N;;;;;
+101E2;PHAISTOS DISC SIGN CARPENTRY PLANE;So;0;L;;;;;N;;;;;
+101E3;PHAISTOS DISC SIGN DOLIUM;So;0;L;;;;;N;;;;;
+101E4;PHAISTOS DISC SIGN COMB;So;0;L;;;;;N;;;;;
+101E5;PHAISTOS DISC SIGN SLING;So;0;L;;;;;N;;;;;
+101E6;PHAISTOS DISC SIGN COLUMN;So;0;L;;;;;N;;;;;
+101E7;PHAISTOS DISC SIGN BEEHIVE;So;0;L;;;;;N;;;;;
+101E8;PHAISTOS DISC SIGN SHIP;So;0;L;;;;;N;;;;;
+101E9;PHAISTOS DISC SIGN HORN;So;0;L;;;;;N;;;;;
+101EA;PHAISTOS DISC SIGN HIDE;So;0;L;;;;;N;;;;;
+101EB;PHAISTOS DISC SIGN BULLS LEG;So;0;L;;;;;N;;;;;
+101EC;PHAISTOS DISC SIGN CAT;So;0;L;;;;;N;;;;;
+101ED;PHAISTOS DISC SIGN RAM;So;0;L;;;;;N;;;;;
+101EE;PHAISTOS DISC SIGN EAGLE;So;0;L;;;;;N;;;;;
+101EF;PHAISTOS DISC SIGN DOVE;So;0;L;;;;;N;;;;;
+101F0;PHAISTOS DISC SIGN TUNNY;So;0;L;;;;;N;;;;;
+101F1;PHAISTOS DISC SIGN BEE;So;0;L;;;;;N;;;;;
+101F2;PHAISTOS DISC SIGN PLANE TREE;So;0;L;;;;;N;;;;;
+101F3;PHAISTOS DISC SIGN VINE;So;0;L;;;;;N;;;;;
+101F4;PHAISTOS DISC SIGN PAPYRUS;So;0;L;;;;;N;;;;;
+101F5;PHAISTOS DISC SIGN ROSETTE;So;0;L;;;;;N;;;;;
+101F6;PHAISTOS DISC SIGN LILY;So;0;L;;;;;N;;;;;
+101F7;PHAISTOS DISC SIGN OX BACK;So;0;L;;;;;N;;;;;
+101F8;PHAISTOS DISC SIGN FLUTE;So;0;L;;;;;N;;;;;
+101F9;PHAISTOS DISC SIGN GRATER;So;0;L;;;;;N;;;;;
+101FA;PHAISTOS DISC SIGN STRAINER;So;0;L;;;;;N;;;;;
+101FB;PHAISTOS DISC SIGN SMALL AXE;So;0;L;;;;;N;;;;;
+101FC;PHAISTOS DISC SIGN WAVY BAND;So;0;L;;;;;N;;;;;
+101FD;PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE;Mn;220;NSM;;;;;N;;;;;
+10280;LYCIAN LETTER A;Lo;0;L;;;;;N;;;;;
+10281;LYCIAN LETTER E;Lo;0;L;;;;;N;;;;;
+10282;LYCIAN LETTER B;Lo;0;L;;;;;N;;;;;
+10283;LYCIAN LETTER BH;Lo;0;L;;;;;N;;;;;
+10284;LYCIAN LETTER G;Lo;0;L;;;;;N;;;;;
+10285;LYCIAN LETTER D;Lo;0;L;;;;;N;;;;;
+10286;LYCIAN LETTER I;Lo;0;L;;;;;N;;;;;
+10287;LYCIAN LETTER W;Lo;0;L;;;;;N;;;;;
+10288;LYCIAN LETTER Z;Lo;0;L;;;;;N;;;;;
+10289;LYCIAN LETTER TH;Lo;0;L;;;;;N;;;;;
+1028A;LYCIAN LETTER J;Lo;0;L;;;;;N;;;;;
+1028B;LYCIAN LETTER K;Lo;0;L;;;;;N;;;;;
+1028C;LYCIAN LETTER Q;Lo;0;L;;;;;N;;;;;
+1028D;LYCIAN LETTER L;Lo;0;L;;;;;N;;;;;
+1028E;LYCIAN LETTER M;Lo;0;L;;;;;N;;;;;
+1028F;LYCIAN LETTER N;Lo;0;L;;;;;N;;;;;
+10290;LYCIAN LETTER MM;Lo;0;L;;;;;N;;;;;
+10291;LYCIAN LETTER NN;Lo;0;L;;;;;N;;;;;
+10292;LYCIAN LETTER U;Lo;0;L;;;;;N;;;;;
+10293;LYCIAN LETTER P;Lo;0;L;;;;;N;;;;;
+10294;LYCIAN LETTER KK;Lo;0;L;;;;;N;;;;;
+10295;LYCIAN LETTER R;Lo;0;L;;;;;N;;;;;
+10296;LYCIAN LETTER S;Lo;0;L;;;;;N;;;;;
+10297;LYCIAN LETTER T;Lo;0;L;;;;;N;;;;;
+10298;LYCIAN LETTER TT;Lo;0;L;;;;;N;;;;;
+10299;LYCIAN LETTER AN;Lo;0;L;;;;;N;;;;;
+1029A;LYCIAN LETTER EN;Lo;0;L;;;;;N;;;;;
+1029B;LYCIAN LETTER H;Lo;0;L;;;;;N;;;;;
+1029C;LYCIAN LETTER X;Lo;0;L;;;;;N;;;;;
+102A0;CARIAN LETTER A;Lo;0;L;;;;;N;;;;;
+102A1;CARIAN LETTER P2;Lo;0;L;;;;;N;;;;;
+102A2;CARIAN LETTER D;Lo;0;L;;;;;N;;;;;
+102A3;CARIAN LETTER L;Lo;0;L;;;;;N;;;;;
+102A4;CARIAN LETTER UUU;Lo;0;L;;;;;N;;;;;
+102A5;CARIAN LETTER R;Lo;0;L;;;;;N;;;;;
+102A6;CARIAN LETTER LD;Lo;0;L;;;;;N;;;;;
+102A7;CARIAN LETTER A2;Lo;0;L;;;;;N;;;;;
+102A8;CARIAN LETTER Q;Lo;0;L;;;;;N;;;;;
+102A9;CARIAN LETTER B;Lo;0;L;;;;;N;;;;;
+102AA;CARIAN LETTER M;Lo;0;L;;;;;N;;;;;
+102AB;CARIAN LETTER O;Lo;0;L;;;;;N;;;;;
+102AC;CARIAN LETTER D2;Lo;0;L;;;;;N;;;;;
+102AD;CARIAN LETTER T;Lo;0;L;;;;;N;;;;;
+102AE;CARIAN LETTER SH;Lo;0;L;;;;;N;;;;;
+102AF;CARIAN LETTER SH2;Lo;0;L;;;;;N;;;;;
+102B0;CARIAN LETTER S;Lo;0;L;;;;;N;;;;;
+102B1;CARIAN LETTER C-18;Lo;0;L;;;;;N;;;;;
+102B2;CARIAN LETTER U;Lo;0;L;;;;;N;;;;;
+102B3;CARIAN LETTER NN;Lo;0;L;;;;;N;;;;;
+102B4;CARIAN LETTER X;Lo;0;L;;;;;N;;;;;
+102B5;CARIAN LETTER N;Lo;0;L;;;;;N;;;;;
+102B6;CARIAN LETTER TT2;Lo;0;L;;;;;N;;;;;
+102B7;CARIAN LETTER P;Lo;0;L;;;;;N;;;;;
+102B8;CARIAN LETTER SS;Lo;0;L;;;;;N;;;;;
+102B9;CARIAN LETTER I;Lo;0;L;;;;;N;;;;;
+102BA;CARIAN LETTER E;Lo;0;L;;;;;N;;;;;
+102BB;CARIAN LETTER UUUU;Lo;0;L;;;;;N;;;;;
+102BC;CARIAN LETTER K;Lo;0;L;;;;;N;;;;;
+102BD;CARIAN LETTER K2;Lo;0;L;;;;;N;;;;;
+102BE;CARIAN LETTER ND;Lo;0;L;;;;;N;;;;;
+102BF;CARIAN LETTER UU;Lo;0;L;;;;;N;;;;;
+102C0;CARIAN LETTER G;Lo;0;L;;;;;N;;;;;
+102C1;CARIAN LETTER G2;Lo;0;L;;;;;N;;;;;
+102C2;CARIAN LETTER ST;Lo;0;L;;;;;N;;;;;
+102C3;CARIAN LETTER ST2;Lo;0;L;;;;;N;;;;;
+102C4;CARIAN LETTER NG;Lo;0;L;;;;;N;;;;;
+102C5;CARIAN LETTER II;Lo;0;L;;;;;N;;;;;
+102C6;CARIAN LETTER C-39;Lo;0;L;;;;;N;;;;;
+102C7;CARIAN LETTER TT;Lo;0;L;;;;;N;;;;;
+102C8;CARIAN LETTER UUU2;Lo;0;L;;;;;N;;;;;
+102C9;CARIAN LETTER RR;Lo;0;L;;;;;N;;;;;
+102CA;CARIAN LETTER MB;Lo;0;L;;;;;N;;;;;
+102CB;CARIAN LETTER MB2;Lo;0;L;;;;;N;;;;;
+102CC;CARIAN LETTER MB3;Lo;0;L;;;;;N;;;;;
+102CD;CARIAN LETTER MB4;Lo;0;L;;;;;N;;;;;
+102CE;CARIAN LETTER LD2;Lo;0;L;;;;;N;;;;;
+102CF;CARIAN LETTER E2;Lo;0;L;;;;;N;;;;;
+102D0;CARIAN LETTER UUU3;Lo;0;L;;;;;N;;;;;
+102E0;COPTIC EPACT THOUSANDS MARK;Mn;220;NSM;;;;;N;;;;;
+102E1;COPTIC EPACT DIGIT ONE;No;0;EN;;;;1;N;;;;;
+102E2;COPTIC EPACT DIGIT TWO;No;0;EN;;;;2;N;;;;;
+102E3;COPTIC EPACT DIGIT THREE;No;0;EN;;;;3;N;;;;;
+102E4;COPTIC EPACT DIGIT FOUR;No;0;EN;;;;4;N;;;;;
+102E5;COPTIC EPACT DIGIT FIVE;No;0;EN;;;;5;N;;;;;
+102E6;COPTIC EPACT DIGIT SIX;No;0;EN;;;;6;N;;;;;
+102E7;COPTIC EPACT DIGIT SEVEN;No;0;EN;;;;7;N;;;;;
+102E8;COPTIC EPACT DIGIT EIGHT;No;0;EN;;;;8;N;;;;;
+102E9;COPTIC EPACT DIGIT NINE;No;0;EN;;;;9;N;;;;;
+102EA;COPTIC EPACT NUMBER TEN;No;0;EN;;;;10;N;;;;;
+102EB;COPTIC EPACT NUMBER TWENTY;No;0;EN;;;;20;N;;;;;
+102EC;COPTIC EPACT NUMBER THIRTY;No;0;EN;;;;30;N;;;;;
+102ED;COPTIC EPACT NUMBER FORTY;No;0;EN;;;;40;N;;;;;
+102EE;COPTIC EPACT NUMBER FIFTY;No;0;EN;;;;50;N;;;;;
+102EF;COPTIC EPACT NUMBER SIXTY;No;0;EN;;;;60;N;;;;;
+102F0;COPTIC EPACT NUMBER SEVENTY;No;0;EN;;;;70;N;;;;;
+102F1;COPTIC EPACT NUMBER EIGHTY;No;0;EN;;;;80;N;;;;;
+102F2;COPTIC EPACT NUMBER NINETY;No;0;EN;;;;90;N;;;;;
+102F3;COPTIC EPACT NUMBER ONE HUNDRED;No;0;EN;;;;100;N;;;;;
+102F4;COPTIC EPACT NUMBER TWO HUNDRED;No;0;EN;;;;200;N;;;;;
+102F5;COPTIC EPACT NUMBER THREE HUNDRED;No;0;EN;;;;300;N;;;;;
+102F6;COPTIC EPACT NUMBER FOUR HUNDRED;No;0;EN;;;;400;N;;;;;
+102F7;COPTIC EPACT NUMBER FIVE HUNDRED;No;0;EN;;;;500;N;;;;;
+102F8;COPTIC EPACT NUMBER SIX HUNDRED;No;0;EN;;;;600;N;;;;;
+102F9;COPTIC EPACT NUMBER SEVEN HUNDRED;No;0;EN;;;;700;N;;;;;
+102FA;COPTIC EPACT NUMBER EIGHT HUNDRED;No;0;EN;;;;800;N;;;;;
+102FB;COPTIC EPACT NUMBER NINE HUNDRED;No;0;EN;;;;900;N;;;;;
+10300;OLD ITALIC LETTER A;Lo;0;L;;;;;N;;;;;
+10301;OLD ITALIC LETTER BE;Lo;0;L;;;;;N;;;;;
+10302;OLD ITALIC LETTER KE;Lo;0;L;;;;;N;;;;;
+10303;OLD ITALIC LETTER DE;Lo;0;L;;;;;N;;;;;
+10304;OLD ITALIC LETTER E;Lo;0;L;;;;;N;;;;;
+10305;OLD ITALIC LETTER VE;Lo;0;L;;;;;N;;;;;
+10306;OLD ITALIC LETTER ZE;Lo;0;L;;;;;N;;;;;
+10307;OLD ITALIC LETTER HE;Lo;0;L;;;;;N;;;;;
+10308;OLD ITALIC LETTER THE;Lo;0;L;;;;;N;;;;;
+10309;OLD ITALIC LETTER I;Lo;0;L;;;;;N;;;;;
+1030A;OLD ITALIC LETTER KA;Lo;0;L;;;;;N;;;;;
+1030B;OLD ITALIC LETTER EL;Lo;0;L;;;;;N;;;;;
+1030C;OLD ITALIC LETTER EM;Lo;0;L;;;;;N;;;;;
+1030D;OLD ITALIC LETTER EN;Lo;0;L;;;;;N;;;;;
+1030E;OLD ITALIC LETTER ESH;Lo;0;L;;;;;N;;;;;
+1030F;OLD ITALIC LETTER O;Lo;0;L;;;;;N;;;;;
+10310;OLD ITALIC LETTER PE;Lo;0;L;;;;;N;;;;;
+10311;OLD ITALIC LETTER SHE;Lo;0;L;;;;;N;;;;;
+10312;OLD ITALIC LETTER KU;Lo;0;L;;;;;N;;;;;
+10313;OLD ITALIC LETTER ER;Lo;0;L;;;;;N;;;;;
+10314;OLD ITALIC LETTER ES;Lo;0;L;;;;;N;;;;;
+10315;OLD ITALIC LETTER TE;Lo;0;L;;;;;N;;;;;
+10316;OLD ITALIC LETTER U;Lo;0;L;;;;;N;;;;;
+10317;OLD ITALIC LETTER EKS;Lo;0;L;;;;;N;;;;;
+10318;OLD ITALIC LETTER PHE;Lo;0;L;;;;;N;;;;;
+10319;OLD ITALIC LETTER KHE;Lo;0;L;;;;;N;;;;;
+1031A;OLD ITALIC LETTER EF;Lo;0;L;;;;;N;;;;;
+1031B;OLD ITALIC LETTER ERS;Lo;0;L;;;;;N;;;;;
+1031C;OLD ITALIC LETTER CHE;Lo;0;L;;;;;N;;;;;
+1031D;OLD ITALIC LETTER II;Lo;0;L;;;;;N;;;;;
+1031E;OLD ITALIC LETTER UU;Lo;0;L;;;;;N;;;;;
+1031F;OLD ITALIC LETTER ESS;Lo;0;L;;;;;N;;;;;
+10320;OLD ITALIC NUMERAL ONE;No;0;L;;;;1;N;;;;;
+10321;OLD ITALIC NUMERAL FIVE;No;0;L;;;;5;N;;;;;
+10322;OLD ITALIC NUMERAL TEN;No;0;L;;;;10;N;;;;;
+10323;OLD ITALIC NUMERAL FIFTY;No;0;L;;;;50;N;;;;;
+1032D;OLD ITALIC LETTER YE;Lo;0;L;;;;;N;;;;;
+1032E;OLD ITALIC LETTER NORTHERN TSE;Lo;0;L;;;;;N;;;;;
+1032F;OLD ITALIC LETTER SOUTHERN TSE;Lo;0;L;;;;;N;;;;;
+10330;GOTHIC LETTER AHSA;Lo;0;L;;;;;N;;;;;
+10331;GOTHIC LETTER BAIRKAN;Lo;0;L;;;;;N;;;;;
+10332;GOTHIC LETTER GIBA;Lo;0;L;;;;;N;;;;;
+10333;GOTHIC LETTER DAGS;Lo;0;L;;;;;N;;;;;
+10334;GOTHIC LETTER AIHVUS;Lo;0;L;;;;;N;;;;;
+10335;GOTHIC LETTER QAIRTHRA;Lo;0;L;;;;;N;;;;;
+10336;GOTHIC LETTER IUJA;Lo;0;L;;;;;N;;;;;
+10337;GOTHIC LETTER HAGL;Lo;0;L;;;;;N;;;;;
+10338;GOTHIC LETTER THIUTH;Lo;0;L;;;;;N;;;;;
+10339;GOTHIC LETTER EIS;Lo;0;L;;;;;N;;;;;
+1033A;GOTHIC LETTER KUSMA;Lo;0;L;;;;;N;;;;;
+1033B;GOTHIC LETTER LAGUS;Lo;0;L;;;;;N;;;;;
+1033C;GOTHIC LETTER MANNA;Lo;0;L;;;;;N;;;;;
+1033D;GOTHIC LETTER NAUTHS;Lo;0;L;;;;;N;;;;;
+1033E;GOTHIC LETTER JER;Lo;0;L;;;;;N;;;;;
+1033F;GOTHIC LETTER URUS;Lo;0;L;;;;;N;;;;;
+10340;GOTHIC LETTER PAIRTHRA;Lo;0;L;;;;;N;;;;;
+10341;GOTHIC LETTER NINETY;Nl;0;L;;;;90;N;;;;;
+10342;GOTHIC LETTER RAIDA;Lo;0;L;;;;;N;;;;;
+10343;GOTHIC LETTER SAUIL;Lo;0;L;;;;;N;;;;;
+10344;GOTHIC LETTER TEIWS;Lo;0;L;;;;;N;;;;;
+10345;GOTHIC LETTER WINJA;Lo;0;L;;;;;N;;;;;
+10346;GOTHIC LETTER FAIHU;Lo;0;L;;;;;N;;;;;
+10347;GOTHIC LETTER IGGWS;Lo;0;L;;;;;N;;;;;
+10348;GOTHIC LETTER HWAIR;Lo;0;L;;;;;N;;;;;
+10349;GOTHIC LETTER OTHAL;Lo;0;L;;;;;N;;;;;
+1034A;GOTHIC LETTER NINE HUNDRED;Nl;0;L;;;;900;N;;;;;
+10350;OLD PERMIC LETTER AN;Lo;0;L;;;;;N;;;;;
+10351;OLD PERMIC LETTER BUR;Lo;0;L;;;;;N;;;;;
+10352;OLD PERMIC LETTER GAI;Lo;0;L;;;;;N;;;;;
+10353;OLD PERMIC LETTER DOI;Lo;0;L;;;;;N;;;;;
+10354;OLD PERMIC LETTER E;Lo;0;L;;;;;N;;;;;
+10355;OLD PERMIC LETTER ZHOI;Lo;0;L;;;;;N;;;;;
+10356;OLD PERMIC LETTER DZHOI;Lo;0;L;;;;;N;;;;;
+10357;OLD PERMIC LETTER ZATA;Lo;0;L;;;;;N;;;;;
+10358;OLD PERMIC LETTER DZITA;Lo;0;L;;;;;N;;;;;
+10359;OLD PERMIC LETTER I;Lo;0;L;;;;;N;;;;;
+1035A;OLD PERMIC LETTER KOKE;Lo;0;L;;;;;N;;;;;
+1035B;OLD PERMIC LETTER LEI;Lo;0;L;;;;;N;;;;;
+1035C;OLD PERMIC LETTER MENOE;Lo;0;L;;;;;N;;;;;
+1035D;OLD PERMIC LETTER NENOE;Lo;0;L;;;;;N;;;;;
+1035E;OLD PERMIC LETTER VOOI;Lo;0;L;;;;;N;;;;;
+1035F;OLD PERMIC LETTER PEEI;Lo;0;L;;;;;N;;;;;
+10360;OLD PERMIC LETTER REI;Lo;0;L;;;;;N;;;;;
+10361;OLD PERMIC LETTER SII;Lo;0;L;;;;;N;;;;;
+10362;OLD PERMIC LETTER TAI;Lo;0;L;;;;;N;;;;;
+10363;OLD PERMIC LETTER U;Lo;0;L;;;;;N;;;;;
+10364;OLD PERMIC LETTER CHERY;Lo;0;L;;;;;N;;;;;
+10365;OLD PERMIC LETTER SHOOI;Lo;0;L;;;;;N;;;;;
+10366;OLD PERMIC LETTER SHCHOOI;Lo;0;L;;;;;N;;;;;
+10367;OLD PERMIC LETTER YRY;Lo;0;L;;;;;N;;;;;
+10368;OLD PERMIC LETTER YERU;Lo;0;L;;;;;N;;;;;
+10369;OLD PERMIC LETTER O;Lo;0;L;;;;;N;;;;;
+1036A;OLD PERMIC LETTER OO;Lo;0;L;;;;;N;;;;;
+1036B;OLD PERMIC LETTER EF;Lo;0;L;;;;;N;;;;;
+1036C;OLD PERMIC LETTER HA;Lo;0;L;;;;;N;;;;;
+1036D;OLD PERMIC LETTER TSIU;Lo;0;L;;;;;N;;;;;
+1036E;OLD PERMIC LETTER VER;Lo;0;L;;;;;N;;;;;
+1036F;OLD PERMIC LETTER YER;Lo;0;L;;;;;N;;;;;
+10370;OLD PERMIC LETTER YERI;Lo;0;L;;;;;N;;;;;
+10371;OLD PERMIC LETTER YAT;Lo;0;L;;;;;N;;;;;
+10372;OLD PERMIC LETTER IE;Lo;0;L;;;;;N;;;;;
+10373;OLD PERMIC LETTER YU;Lo;0;L;;;;;N;;;;;
+10374;OLD PERMIC LETTER YA;Lo;0;L;;;;;N;;;;;
+10375;OLD PERMIC LETTER IA;Lo;0;L;;;;;N;;;;;
+10376;COMBINING OLD PERMIC LETTER AN;Mn;230;NSM;;;;;N;;;;;
+10377;COMBINING OLD PERMIC LETTER DOI;Mn;230;NSM;;;;;N;;;;;
+10378;COMBINING OLD PERMIC LETTER ZATA;Mn;230;NSM;;;;;N;;;;;
+10379;COMBINING OLD PERMIC LETTER NENOE;Mn;230;NSM;;;;;N;;;;;
+1037A;COMBINING OLD PERMIC LETTER SII;Mn;230;NSM;;;;;N;;;;;
+10380;UGARITIC LETTER ALPA;Lo;0;L;;;;;N;;;;;
+10381;UGARITIC LETTER BETA;Lo;0;L;;;;;N;;;;;
+10382;UGARITIC LETTER GAMLA;Lo;0;L;;;;;N;;;;;
+10383;UGARITIC LETTER KHA;Lo;0;L;;;;;N;;;;;
+10384;UGARITIC LETTER DELTA;Lo;0;L;;;;;N;;;;;
+10385;UGARITIC LETTER HO;Lo;0;L;;;;;N;;;;;
+10386;UGARITIC LETTER WO;Lo;0;L;;;;;N;;;;;
+10387;UGARITIC LETTER ZETA;Lo;0;L;;;;;N;;;;;
+10388;UGARITIC LETTER HOTA;Lo;0;L;;;;;N;;;;;
+10389;UGARITIC LETTER TET;Lo;0;L;;;;;N;;;;;
+1038A;UGARITIC LETTER YOD;Lo;0;L;;;;;N;;;;;
+1038B;UGARITIC LETTER KAF;Lo;0;L;;;;;N;;;;;
+1038C;UGARITIC LETTER SHIN;Lo;0;L;;;;;N;;;;;
+1038D;UGARITIC LETTER LAMDA;Lo;0;L;;;;;N;;;;;
+1038E;UGARITIC LETTER MEM;Lo;0;L;;;;;N;;;;;
+1038F;UGARITIC LETTER DHAL;Lo;0;L;;;;;N;;;;;
+10390;UGARITIC LETTER NUN;Lo;0;L;;;;;N;;;;;
+10391;UGARITIC LETTER ZU;Lo;0;L;;;;;N;;;;;
+10392;UGARITIC LETTER SAMKA;Lo;0;L;;;;;N;;;;;
+10393;UGARITIC LETTER AIN;Lo;0;L;;;;;N;;;;;
+10394;UGARITIC LETTER PU;Lo;0;L;;;;;N;;;;;
+10395;UGARITIC LETTER SADE;Lo;0;L;;;;;N;;;;;
+10396;UGARITIC LETTER QOPA;Lo;0;L;;;;;N;;;;;
+10397;UGARITIC LETTER RASHA;Lo;0;L;;;;;N;;;;;
+10398;UGARITIC LETTER THANNA;Lo;0;L;;;;;N;;;;;
+10399;UGARITIC LETTER GHAIN;Lo;0;L;;;;;N;;;;;
+1039A;UGARITIC LETTER TO;Lo;0;L;;;;;N;;;;;
+1039B;UGARITIC LETTER I;Lo;0;L;;;;;N;;;;;
+1039C;UGARITIC LETTER U;Lo;0;L;;;;;N;;;;;
+1039D;UGARITIC LETTER SSU;Lo;0;L;;;;;N;;;;;
+1039F;UGARITIC WORD DIVIDER;Po;0;L;;;;;N;;;;;
+103A0;OLD PERSIAN SIGN A;Lo;0;L;;;;;N;;;;;
+103A1;OLD PERSIAN SIGN I;Lo;0;L;;;;;N;;;;;
+103A2;OLD PERSIAN SIGN U;Lo;0;L;;;;;N;;;;;
+103A3;OLD PERSIAN SIGN KA;Lo;0;L;;;;;N;;;;;
+103A4;OLD PERSIAN SIGN KU;Lo;0;L;;;;;N;;;;;
+103A5;OLD PERSIAN SIGN GA;Lo;0;L;;;;;N;;;;;
+103A6;OLD PERSIAN SIGN GU;Lo;0;L;;;;;N;;;;;
+103A7;OLD PERSIAN SIGN XA;Lo;0;L;;;;;N;;;;;
+103A8;OLD PERSIAN SIGN CA;Lo;0;L;;;;;N;;;;;
+103A9;OLD PERSIAN SIGN JA;Lo;0;L;;;;;N;;;;;
+103AA;OLD PERSIAN SIGN JI;Lo;0;L;;;;;N;;;;;
+103AB;OLD PERSIAN SIGN TA;Lo;0;L;;;;;N;;;;;
+103AC;OLD PERSIAN SIGN TU;Lo;0;L;;;;;N;;;;;
+103AD;OLD PERSIAN SIGN DA;Lo;0;L;;;;;N;;;;;
+103AE;OLD PERSIAN SIGN DI;Lo;0;L;;;;;N;;;;;
+103AF;OLD PERSIAN SIGN DU;Lo;0;L;;;;;N;;;;;
+103B0;OLD PERSIAN SIGN THA;Lo;0;L;;;;;N;;;;;
+103B1;OLD PERSIAN SIGN PA;Lo;0;L;;;;;N;;;;;
+103B2;OLD PERSIAN SIGN BA;Lo;0;L;;;;;N;;;;;
+103B3;OLD PERSIAN SIGN FA;Lo;0;L;;;;;N;;;;;
+103B4;OLD PERSIAN SIGN NA;Lo;0;L;;;;;N;;;;;
+103B5;OLD PERSIAN SIGN NU;Lo;0;L;;;;;N;;;;;
+103B6;OLD PERSIAN SIGN MA;Lo;0;L;;;;;N;;;;;
+103B7;OLD PERSIAN SIGN MI;Lo;0;L;;;;;N;;;;;
+103B8;OLD PERSIAN SIGN MU;Lo;0;L;;;;;N;;;;;
+103B9;OLD PERSIAN SIGN YA;Lo;0;L;;;;;N;;;;;
+103BA;OLD PERSIAN SIGN VA;Lo;0;L;;;;;N;;;;;
+103BB;OLD PERSIAN SIGN VI;Lo;0;L;;;;;N;;;;;
+103BC;OLD PERSIAN SIGN RA;Lo;0;L;;;;;N;;;;;
+103BD;OLD PERSIAN SIGN RU;Lo;0;L;;;;;N;;;;;
+103BE;OLD PERSIAN SIGN LA;Lo;0;L;;;;;N;;;;;
+103BF;OLD PERSIAN SIGN SA;Lo;0;L;;;;;N;;;;;
+103C0;OLD PERSIAN SIGN ZA;Lo;0;L;;;;;N;;;;;
+103C1;OLD PERSIAN SIGN SHA;Lo;0;L;;;;;N;;;;;
+103C2;OLD PERSIAN SIGN SSA;Lo;0;L;;;;;N;;;;;
+103C3;OLD PERSIAN SIGN HA;Lo;0;L;;;;;N;;;;;
+103C8;OLD PERSIAN SIGN AURAMAZDAA;Lo;0;L;;;;;N;;;;;
+103C9;OLD PERSIAN SIGN AURAMAZDAA-2;Lo;0;L;;;;;N;;;;;
+103CA;OLD PERSIAN SIGN AURAMAZDAAHA;Lo;0;L;;;;;N;;;;;
+103CB;OLD PERSIAN SIGN XSHAAYATHIYA;Lo;0;L;;;;;N;;;;;
+103CC;OLD PERSIAN SIGN DAHYAAUSH;Lo;0;L;;;;;N;;;;;
+103CD;OLD PERSIAN SIGN DAHYAAUSH-2;Lo;0;L;;;;;N;;;;;
+103CE;OLD PERSIAN SIGN BAGA;Lo;0;L;;;;;N;;;;;
+103CF;OLD PERSIAN SIGN BUUMISH;Lo;0;L;;;;;N;;;;;
+103D0;OLD PERSIAN WORD DIVIDER;Po;0;L;;;;;N;;;;;
+103D1;OLD PERSIAN NUMBER ONE;Nl;0;L;;;;1;N;;;;;
+103D2;OLD PERSIAN NUMBER TWO;Nl;0;L;;;;2;N;;;;;
+103D3;OLD PERSIAN NUMBER TEN;Nl;0;L;;;;10;N;;;;;
+103D4;OLD PERSIAN NUMBER TWENTY;Nl;0;L;;;;20;N;;;;;
+103D5;OLD PERSIAN NUMBER HUNDRED;Nl;0;L;;;;100;N;;;;;
+10400;DESERET CAPITAL LETTER LONG I;Lu;0;L;;;;;N;;;;10428;
+10401;DESERET CAPITAL LETTER LONG E;Lu;0;L;;;;;N;;;;10429;
+10402;DESERET CAPITAL LETTER LONG A;Lu;0;L;;;;;N;;;;1042A;
+10403;DESERET CAPITAL LETTER LONG AH;Lu;0;L;;;;;N;;;;1042B;
+10404;DESERET CAPITAL LETTER LONG O;Lu;0;L;;;;;N;;;;1042C;
+10405;DESERET CAPITAL LETTER LONG OO;Lu;0;L;;;;;N;;;;1042D;
+10406;DESERET CAPITAL LETTER SHORT I;Lu;0;L;;;;;N;;;;1042E;
+10407;DESERET CAPITAL LETTER SHORT E;Lu;0;L;;;;;N;;;;1042F;
+10408;DESERET CAPITAL LETTER SHORT A;Lu;0;L;;;;;N;;;;10430;
+10409;DESERET CAPITAL LETTER SHORT AH;Lu;0;L;;;;;N;;;;10431;
+1040A;DESERET CAPITAL LETTER SHORT O;Lu;0;L;;;;;N;;;;10432;
+1040B;DESERET CAPITAL LETTER SHORT OO;Lu;0;L;;;;;N;;;;10433;
+1040C;DESERET CAPITAL LETTER AY;Lu;0;L;;;;;N;;;;10434;
+1040D;DESERET CAPITAL LETTER OW;Lu;0;L;;;;;N;;;;10435;
+1040E;DESERET CAPITAL LETTER WU;Lu;0;L;;;;;N;;;;10436;
+1040F;DESERET CAPITAL LETTER YEE;Lu;0;L;;;;;N;;;;10437;
+10410;DESERET CAPITAL LETTER H;Lu;0;L;;;;;N;;;;10438;
+10411;DESERET CAPITAL LETTER PEE;Lu;0;L;;;;;N;;;;10439;
+10412;DESERET CAPITAL LETTER BEE;Lu;0;L;;;;;N;;;;1043A;
+10413;DESERET CAPITAL LETTER TEE;Lu;0;L;;;;;N;;;;1043B;
+10414;DESERET CAPITAL LETTER DEE;Lu;0;L;;;;;N;;;;1043C;
+10415;DESERET CAPITAL LETTER CHEE;Lu;0;L;;;;;N;;;;1043D;
+10416;DESERET CAPITAL LETTER JEE;Lu;0;L;;;;;N;;;;1043E;
+10417;DESERET CAPITAL LETTER KAY;Lu;0;L;;;;;N;;;;1043F;
+10418;DESERET CAPITAL LETTER GAY;Lu;0;L;;;;;N;;;;10440;
+10419;DESERET CAPITAL LETTER EF;Lu;0;L;;;;;N;;;;10441;
+1041A;DESERET CAPITAL LETTER VEE;Lu;0;L;;;;;N;;;;10442;
+1041B;DESERET CAPITAL LETTER ETH;Lu;0;L;;;;;N;;;;10443;
+1041C;DESERET CAPITAL LETTER THEE;Lu;0;L;;;;;N;;;;10444;
+1041D;DESERET CAPITAL LETTER ES;Lu;0;L;;;;;N;;;;10445;
+1041E;DESERET CAPITAL LETTER ZEE;Lu;0;L;;;;;N;;;;10446;
+1041F;DESERET CAPITAL LETTER ESH;Lu;0;L;;;;;N;;;;10447;
+10420;DESERET CAPITAL LETTER ZHEE;Lu;0;L;;;;;N;;;;10448;
+10421;DESERET CAPITAL LETTER ER;Lu;0;L;;;;;N;;;;10449;
+10422;DESERET CAPITAL LETTER EL;Lu;0;L;;;;;N;;;;1044A;
+10423;DESERET CAPITAL LETTER EM;Lu;0;L;;;;;N;;;;1044B;
+10424;DESERET CAPITAL LETTER EN;Lu;0;L;;;;;N;;;;1044C;
+10425;DESERET CAPITAL LETTER ENG;Lu;0;L;;;;;N;;;;1044D;
+10426;DESERET CAPITAL LETTER OI;Lu;0;L;;;;;N;;;;1044E;
+10427;DESERET CAPITAL LETTER EW;Lu;0;L;;;;;N;;;;1044F;
+10428;DESERET SMALL LETTER LONG I;Ll;0;L;;;;;N;;;10400;;10400
+10429;DESERET SMALL LETTER LONG E;Ll;0;L;;;;;N;;;10401;;10401
+1042A;DESERET SMALL LETTER LONG A;Ll;0;L;;;;;N;;;10402;;10402
+1042B;DESERET SMALL LETTER LONG AH;Ll;0;L;;;;;N;;;10403;;10403
+1042C;DESERET SMALL LETTER LONG O;Ll;0;L;;;;;N;;;10404;;10404
+1042D;DESERET SMALL LETTER LONG OO;Ll;0;L;;;;;N;;;10405;;10405
+1042E;DESERET SMALL LETTER SHORT I;Ll;0;L;;;;;N;;;10406;;10406
+1042F;DESERET SMALL LETTER SHORT E;Ll;0;L;;;;;N;;;10407;;10407
+10430;DESERET SMALL LETTER SHORT A;Ll;0;L;;;;;N;;;10408;;10408
+10431;DESERET SMALL LETTER SHORT AH;Ll;0;L;;;;;N;;;10409;;10409
+10432;DESERET SMALL LETTER SHORT O;Ll;0;L;;;;;N;;;1040A;;1040A
+10433;DESERET SMALL LETTER SHORT OO;Ll;0;L;;;;;N;;;1040B;;1040B
+10434;DESERET SMALL LETTER AY;Ll;0;L;;;;;N;;;1040C;;1040C
+10435;DESERET SMALL LETTER OW;Ll;0;L;;;;;N;;;1040D;;1040D
+10436;DESERET SMALL LETTER WU;Ll;0;L;;;;;N;;;1040E;;1040E
+10437;DESERET SMALL LETTER YEE;Ll;0;L;;;;;N;;;1040F;;1040F
+10438;DESERET SMALL LETTER H;Ll;0;L;;;;;N;;;10410;;10410
+10439;DESERET SMALL LETTER PEE;Ll;0;L;;;;;N;;;10411;;10411
+1043A;DESERET SMALL LETTER BEE;Ll;0;L;;;;;N;;;10412;;10412
+1043B;DESERET SMALL LETTER TEE;Ll;0;L;;;;;N;;;10413;;10413
+1043C;DESERET SMALL LETTER DEE;Ll;0;L;;;;;N;;;10414;;10414
+1043D;DESERET SMALL LETTER CHEE;Ll;0;L;;;;;N;;;10415;;10415
+1043E;DESERET SMALL LETTER JEE;Ll;0;L;;;;;N;;;10416;;10416
+1043F;DESERET SMALL LETTER KAY;Ll;0;L;;;;;N;;;10417;;10417
+10440;DESERET SMALL LETTER GAY;Ll;0;L;;;;;N;;;10418;;10418
+10441;DESERET SMALL LETTER EF;Ll;0;L;;;;;N;;;10419;;10419
+10442;DESERET SMALL LETTER VEE;Ll;0;L;;;;;N;;;1041A;;1041A
+10443;DESERET SMALL LETTER ETH;Ll;0;L;;;;;N;;;1041B;;1041B
+10444;DESERET SMALL LETTER THEE;Ll;0;L;;;;;N;;;1041C;;1041C
+10445;DESERET SMALL LETTER ES;Ll;0;L;;;;;N;;;1041D;;1041D
+10446;DESERET SMALL LETTER ZEE;Ll;0;L;;;;;N;;;1041E;;1041E
+10447;DESERET SMALL LETTER ESH;Ll;0;L;;;;;N;;;1041F;;1041F
+10448;DESERET SMALL LETTER ZHEE;Ll;0;L;;;;;N;;;10420;;10420
+10449;DESERET SMALL LETTER ER;Ll;0;L;;;;;N;;;10421;;10421
+1044A;DESERET SMALL LETTER EL;Ll;0;L;;;;;N;;;10422;;10422
+1044B;DESERET SMALL LETTER EM;Ll;0;L;;;;;N;;;10423;;10423
+1044C;DESERET SMALL LETTER EN;Ll;0;L;;;;;N;;;10424;;10424
+1044D;DESERET SMALL LETTER ENG;Ll;0;L;;;;;N;;;10425;;10425
+1044E;DESERET SMALL LETTER OI;Ll;0;L;;;;;N;;;10426;;10426
+1044F;DESERET SMALL LETTER EW;Ll;0;L;;;;;N;;;10427;;10427
+10450;SHAVIAN LETTER PEEP;Lo;0;L;;;;;N;;;;;
+10451;SHAVIAN LETTER TOT;Lo;0;L;;;;;N;;;;;
+10452;SHAVIAN LETTER KICK;Lo;0;L;;;;;N;;;;;
+10453;SHAVIAN LETTER FEE;Lo;0;L;;;;;N;;;;;
+10454;SHAVIAN LETTER THIGH;Lo;0;L;;;;;N;;;;;
+10455;SHAVIAN LETTER SO;Lo;0;L;;;;;N;;;;;
+10456;SHAVIAN LETTER SURE;Lo;0;L;;;;;N;;;;;
+10457;SHAVIAN LETTER CHURCH;Lo;0;L;;;;;N;;;;;
+10458;SHAVIAN LETTER YEA;Lo;0;L;;;;;N;;;;;
+10459;SHAVIAN LETTER HUNG;Lo;0;L;;;;;N;;;;;
+1045A;SHAVIAN LETTER BIB;Lo;0;L;;;;;N;;;;;
+1045B;SHAVIAN LETTER DEAD;Lo;0;L;;;;;N;;;;;
+1045C;SHAVIAN LETTER GAG;Lo;0;L;;;;;N;;;;;
+1045D;SHAVIAN LETTER VOW;Lo;0;L;;;;;N;;;;;
+1045E;SHAVIAN LETTER THEY;Lo;0;L;;;;;N;;;;;
+1045F;SHAVIAN LETTER ZOO;Lo;0;L;;;;;N;;;;;
+10460;SHAVIAN LETTER MEASURE;Lo;0;L;;;;;N;;;;;
+10461;SHAVIAN LETTER JUDGE;Lo;0;L;;;;;N;;;;;
+10462;SHAVIAN LETTER WOE;Lo;0;L;;;;;N;;;;;
+10463;SHAVIAN LETTER HA-HA;Lo;0;L;;;;;N;;;;;
+10464;SHAVIAN LETTER LOLL;Lo;0;L;;;;;N;;;;;
+10465;SHAVIAN LETTER MIME;Lo;0;L;;;;;N;;;;;
+10466;SHAVIAN LETTER IF;Lo;0;L;;;;;N;;;;;
+10467;SHAVIAN LETTER EGG;Lo;0;L;;;;;N;;;;;
+10468;SHAVIAN LETTER ASH;Lo;0;L;;;;;N;;;;;
+10469;SHAVIAN LETTER ADO;Lo;0;L;;;;;N;;;;;
+1046A;SHAVIAN LETTER ON;Lo;0;L;;;;;N;;;;;
+1046B;SHAVIAN LETTER WOOL;Lo;0;L;;;;;N;;;;;
+1046C;SHAVIAN LETTER OUT;Lo;0;L;;;;;N;;;;;
+1046D;SHAVIAN LETTER AH;Lo;0;L;;;;;N;;;;;
+1046E;SHAVIAN LETTER ROAR;Lo;0;L;;;;;N;;;;;
+1046F;SHAVIAN LETTER NUN;Lo;0;L;;;;;N;;;;;
+10470;SHAVIAN LETTER EAT;Lo;0;L;;;;;N;;;;;
+10471;SHAVIAN LETTER AGE;Lo;0;L;;;;;N;;;;;
+10472;SHAVIAN LETTER ICE;Lo;0;L;;;;;N;;;;;
+10473;SHAVIAN LETTER UP;Lo;0;L;;;;;N;;;;;
+10474;SHAVIAN LETTER OAK;Lo;0;L;;;;;N;;;;;
+10475;SHAVIAN LETTER OOZE;Lo;0;L;;;;;N;;;;;
+10476;SHAVIAN LETTER OIL;Lo;0;L;;;;;N;;;;;
+10477;SHAVIAN LETTER AWE;Lo;0;L;;;;;N;;;;;
+10478;SHAVIAN LETTER ARE;Lo;0;L;;;;;N;;;;;
+10479;SHAVIAN LETTER OR;Lo;0;L;;;;;N;;;;;
+1047A;SHAVIAN LETTER AIR;Lo;0;L;;;;;N;;;;;
+1047B;SHAVIAN LETTER ERR;Lo;0;L;;;;;N;;;;;
+1047C;SHAVIAN LETTER ARRAY;Lo;0;L;;;;;N;;;;;
+1047D;SHAVIAN LETTER EAR;Lo;0;L;;;;;N;;;;;
+1047E;SHAVIAN LETTER IAN;Lo;0;L;;;;;N;;;;;
+1047F;SHAVIAN LETTER YEW;Lo;0;L;;;;;N;;;;;
+10480;OSMANYA LETTER ALEF;Lo;0;L;;;;;N;;;;;
+10481;OSMANYA LETTER BA;Lo;0;L;;;;;N;;;;;
+10482;OSMANYA LETTER TA;Lo;0;L;;;;;N;;;;;
+10483;OSMANYA LETTER JA;Lo;0;L;;;;;N;;;;;
+10484;OSMANYA LETTER XA;Lo;0;L;;;;;N;;;;;
+10485;OSMANYA LETTER KHA;Lo;0;L;;;;;N;;;;;
+10486;OSMANYA LETTER DEEL;Lo;0;L;;;;;N;;;;;
+10487;OSMANYA LETTER RA;Lo;0;L;;;;;N;;;;;
+10488;OSMANYA LETTER SA;Lo;0;L;;;;;N;;;;;
+10489;OSMANYA LETTER SHIIN;Lo;0;L;;;;;N;;;;;
+1048A;OSMANYA LETTER DHA;Lo;0;L;;;;;N;;;;;
+1048B;OSMANYA LETTER CAYN;Lo;0;L;;;;;N;;;;;
+1048C;OSMANYA LETTER GA;Lo;0;L;;;;;N;;;;;
+1048D;OSMANYA LETTER FA;Lo;0;L;;;;;N;;;;;
+1048E;OSMANYA LETTER QAAF;Lo;0;L;;;;;N;;;;;
+1048F;OSMANYA LETTER KAAF;Lo;0;L;;;;;N;;;;;
+10490;OSMANYA LETTER LAAN;Lo;0;L;;;;;N;;;;;
+10491;OSMANYA LETTER MIIN;Lo;0;L;;;;;N;;;;;
+10492;OSMANYA LETTER NUUN;Lo;0;L;;;;;N;;;;;
+10493;OSMANYA LETTER WAW;Lo;0;L;;;;;N;;;;;
+10494;OSMANYA LETTER HA;Lo;0;L;;;;;N;;;;;
+10495;OSMANYA LETTER YA;Lo;0;L;;;;;N;;;;;
+10496;OSMANYA LETTER A;Lo;0;L;;;;;N;;;;;
+10497;OSMANYA LETTER E;Lo;0;L;;;;;N;;;;;
+10498;OSMANYA LETTER I;Lo;0;L;;;;;N;;;;;
+10499;OSMANYA LETTER O;Lo;0;L;;;;;N;;;;;
+1049A;OSMANYA LETTER U;Lo;0;L;;;;;N;;;;;
+1049B;OSMANYA LETTER AA;Lo;0;L;;;;;N;;;;;
+1049C;OSMANYA LETTER EE;Lo;0;L;;;;;N;;;;;
+1049D;OSMANYA LETTER OO;Lo;0;L;;;;;N;;;;;
+104A0;OSMANYA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+104A1;OSMANYA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+104A2;OSMANYA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+104A3;OSMANYA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+104A4;OSMANYA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+104A5;OSMANYA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+104A6;OSMANYA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+104A7;OSMANYA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+104A8;OSMANYA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+104A9;OSMANYA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+104B0;OSAGE CAPITAL LETTER A;Lu;0;L;;;;;N;;;;104D8;
+104B1;OSAGE CAPITAL LETTER AI;Lu;0;L;;;;;N;;;;104D9;
+104B2;OSAGE CAPITAL LETTER AIN;Lu;0;L;;;;;N;;;;104DA;
+104B3;OSAGE CAPITAL LETTER AH;Lu;0;L;;;;;N;;;;104DB;
+104B4;OSAGE CAPITAL LETTER BRA;Lu;0;L;;;;;N;;;;104DC;
+104B5;OSAGE CAPITAL LETTER CHA;Lu;0;L;;;;;N;;;;104DD;
+104B6;OSAGE CAPITAL LETTER EHCHA;Lu;0;L;;;;;N;;;;104DE;
+104B7;OSAGE CAPITAL LETTER E;Lu;0;L;;;;;N;;;;104DF;
+104B8;OSAGE CAPITAL LETTER EIN;Lu;0;L;;;;;N;;;;104E0;
+104B9;OSAGE CAPITAL LETTER HA;Lu;0;L;;;;;N;;;;104E1;
+104BA;OSAGE CAPITAL LETTER HYA;Lu;0;L;;;;;N;;;;104E2;
+104BB;OSAGE CAPITAL LETTER I;Lu;0;L;;;;;N;;;;104E3;
+104BC;OSAGE CAPITAL LETTER KA;Lu;0;L;;;;;N;;;;104E4;
+104BD;OSAGE CAPITAL LETTER EHKA;Lu;0;L;;;;;N;;;;104E5;
+104BE;OSAGE CAPITAL LETTER KYA;Lu;0;L;;;;;N;;;;104E6;
+104BF;OSAGE CAPITAL LETTER LA;Lu;0;L;;;;;N;;;;104E7;
+104C0;OSAGE CAPITAL LETTER MA;Lu;0;L;;;;;N;;;;104E8;
+104C1;OSAGE CAPITAL LETTER NA;Lu;0;L;;;;;N;;;;104E9;
+104C2;OSAGE CAPITAL LETTER O;Lu;0;L;;;;;N;;;;104EA;
+104C3;OSAGE CAPITAL LETTER OIN;Lu;0;L;;;;;N;;;;104EB;
+104C4;OSAGE CAPITAL LETTER PA;Lu;0;L;;;;;N;;;;104EC;
+104C5;OSAGE CAPITAL LETTER EHPA;Lu;0;L;;;;;N;;;;104ED;
+104C6;OSAGE CAPITAL LETTER SA;Lu;0;L;;;;;N;;;;104EE;
+104C7;OSAGE CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;104EF;
+104C8;OSAGE CAPITAL LETTER TA;Lu;0;L;;;;;N;;;;104F0;
+104C9;OSAGE CAPITAL LETTER EHTA;Lu;0;L;;;;;N;;;;104F1;
+104CA;OSAGE CAPITAL LETTER TSA;Lu;0;L;;;;;N;;;;104F2;
+104CB;OSAGE CAPITAL LETTER EHTSA;Lu;0;L;;;;;N;;;;104F3;
+104CC;OSAGE CAPITAL LETTER TSHA;Lu;0;L;;;;;N;;;;104F4;
+104CD;OSAGE CAPITAL LETTER DHA;Lu;0;L;;;;;N;;;;104F5;
+104CE;OSAGE CAPITAL LETTER U;Lu;0;L;;;;;N;;;;104F6;
+104CF;OSAGE CAPITAL LETTER WA;Lu;0;L;;;;;N;;;;104F7;
+104D0;OSAGE CAPITAL LETTER KHA;Lu;0;L;;;;;N;;;;104F8;
+104D1;OSAGE CAPITAL LETTER GHA;Lu;0;L;;;;;N;;;;104F9;
+104D2;OSAGE CAPITAL LETTER ZA;Lu;0;L;;;;;N;;;;104FA;
+104D3;OSAGE CAPITAL LETTER ZHA;Lu;0;L;;;;;N;;;;104FB;
+104D8;OSAGE SMALL LETTER A;Ll;0;L;;;;;N;;;104B0;;104B0
+104D9;OSAGE SMALL LETTER AI;Ll;0;L;;;;;N;;;104B1;;104B1
+104DA;OSAGE SMALL LETTER AIN;Ll;0;L;;;;;N;;;104B2;;104B2
+104DB;OSAGE SMALL LETTER AH;Ll;0;L;;;;;N;;;104B3;;104B3
+104DC;OSAGE SMALL LETTER BRA;Ll;0;L;;;;;N;;;104B4;;104B4
+104DD;OSAGE SMALL LETTER CHA;Ll;0;L;;;;;N;;;104B5;;104B5
+104DE;OSAGE SMALL LETTER EHCHA;Ll;0;L;;;;;N;;;104B6;;104B6
+104DF;OSAGE SMALL LETTER E;Ll;0;L;;;;;N;;;104B7;;104B7
+104E0;OSAGE SMALL LETTER EIN;Ll;0;L;;;;;N;;;104B8;;104B8
+104E1;OSAGE SMALL LETTER HA;Ll;0;L;;;;;N;;;104B9;;104B9
+104E2;OSAGE SMALL LETTER HYA;Ll;0;L;;;;;N;;;104BA;;104BA
+104E3;OSAGE SMALL LETTER I;Ll;0;L;;;;;N;;;104BB;;104BB
+104E4;OSAGE SMALL LETTER KA;Ll;0;L;;;;;N;;;104BC;;104BC
+104E5;OSAGE SMALL LETTER EHKA;Ll;0;L;;;;;N;;;104BD;;104BD
+104E6;OSAGE SMALL LETTER KYA;Ll;0;L;;;;;N;;;104BE;;104BE
+104E7;OSAGE SMALL LETTER LA;Ll;0;L;;;;;N;;;104BF;;104BF
+104E8;OSAGE SMALL LETTER MA;Ll;0;L;;;;;N;;;104C0;;104C0
+104E9;OSAGE SMALL LETTER NA;Ll;0;L;;;;;N;;;104C1;;104C1
+104EA;OSAGE SMALL LETTER O;Ll;0;L;;;;;N;;;104C2;;104C2
+104EB;OSAGE SMALL LETTER OIN;Ll;0;L;;;;;N;;;104C3;;104C3
+104EC;OSAGE SMALL LETTER PA;Ll;0;L;;;;;N;;;104C4;;104C4
+104ED;OSAGE SMALL LETTER EHPA;Ll;0;L;;;;;N;;;104C5;;104C5
+104EE;OSAGE SMALL LETTER SA;Ll;0;L;;;;;N;;;104C6;;104C6
+104EF;OSAGE SMALL LETTER SHA;Ll;0;L;;;;;N;;;104C7;;104C7
+104F0;OSAGE SMALL LETTER TA;Ll;0;L;;;;;N;;;104C8;;104C8
+104F1;OSAGE SMALL LETTER EHTA;Ll;0;L;;;;;N;;;104C9;;104C9
+104F2;OSAGE SMALL LETTER TSA;Ll;0;L;;;;;N;;;104CA;;104CA
+104F3;OSAGE SMALL LETTER EHTSA;Ll;0;L;;;;;N;;;104CB;;104CB
+104F4;OSAGE SMALL LETTER TSHA;Ll;0;L;;;;;N;;;104CC;;104CC
+104F5;OSAGE SMALL LETTER DHA;Ll;0;L;;;;;N;;;104CD;;104CD
+104F6;OSAGE SMALL LETTER U;Ll;0;L;;;;;N;;;104CE;;104CE
+104F7;OSAGE SMALL LETTER WA;Ll;0;L;;;;;N;;;104CF;;104CF
+104F8;OSAGE SMALL LETTER KHA;Ll;0;L;;;;;N;;;104D0;;104D0
+104F9;OSAGE SMALL LETTER GHA;Ll;0;L;;;;;N;;;104D1;;104D1
+104FA;OSAGE SMALL LETTER ZA;Ll;0;L;;;;;N;;;104D2;;104D2
+104FB;OSAGE SMALL LETTER ZHA;Ll;0;L;;;;;N;;;104D3;;104D3
+10500;ELBASAN LETTER A;Lo;0;L;;;;;N;;;;;
+10501;ELBASAN LETTER BE;Lo;0;L;;;;;N;;;;;
+10502;ELBASAN LETTER CE;Lo;0;L;;;;;N;;;;;
+10503;ELBASAN LETTER CHE;Lo;0;L;;;;;N;;;;;
+10504;ELBASAN LETTER DE;Lo;0;L;;;;;N;;;;;
+10505;ELBASAN LETTER NDE;Lo;0;L;;;;;N;;;;;
+10506;ELBASAN LETTER DHE;Lo;0;L;;;;;N;;;;;
+10507;ELBASAN LETTER EI;Lo;0;L;;;;;N;;;;;
+10508;ELBASAN LETTER E;Lo;0;L;;;;;N;;;;;
+10509;ELBASAN LETTER FE;Lo;0;L;;;;;N;;;;;
+1050A;ELBASAN LETTER GE;Lo;0;L;;;;;N;;;;;
+1050B;ELBASAN LETTER GJE;Lo;0;L;;;;;N;;;;;
+1050C;ELBASAN LETTER HE;Lo;0;L;;;;;N;;;;;
+1050D;ELBASAN LETTER I;Lo;0;L;;;;;N;;;;;
+1050E;ELBASAN LETTER JE;Lo;0;L;;;;;N;;;;;
+1050F;ELBASAN LETTER KE;Lo;0;L;;;;;N;;;;;
+10510;ELBASAN LETTER LE;Lo;0;L;;;;;N;;;;;
+10511;ELBASAN LETTER LLE;Lo;0;L;;;;;N;;;;;
+10512;ELBASAN LETTER ME;Lo;0;L;;;;;N;;;;;
+10513;ELBASAN LETTER NE;Lo;0;L;;;;;N;;;;;
+10514;ELBASAN LETTER NA;Lo;0;L;;;;;N;;;;;
+10515;ELBASAN LETTER NJE;Lo;0;L;;;;;N;;;;;
+10516;ELBASAN LETTER O;Lo;0;L;;;;;N;;;;;
+10517;ELBASAN LETTER PE;Lo;0;L;;;;;N;;;;;
+10518;ELBASAN LETTER QE;Lo;0;L;;;;;N;;;;;
+10519;ELBASAN LETTER RE;Lo;0;L;;;;;N;;;;;
+1051A;ELBASAN LETTER RRE;Lo;0;L;;;;;N;;;;;
+1051B;ELBASAN LETTER SE;Lo;0;L;;;;;N;;;;;
+1051C;ELBASAN LETTER SHE;Lo;0;L;;;;;N;;;;;
+1051D;ELBASAN LETTER TE;Lo;0;L;;;;;N;;;;;
+1051E;ELBASAN LETTER THE;Lo;0;L;;;;;N;;;;;
+1051F;ELBASAN LETTER U;Lo;0;L;;;;;N;;;;;
+10520;ELBASAN LETTER VE;Lo;0;L;;;;;N;;;;;
+10521;ELBASAN LETTER XE;Lo;0;L;;;;;N;;;;;
+10522;ELBASAN LETTER Y;Lo;0;L;;;;;N;;;;;
+10523;ELBASAN LETTER ZE;Lo;0;L;;;;;N;;;;;
+10524;ELBASAN LETTER ZHE;Lo;0;L;;;;;N;;;;;
+10525;ELBASAN LETTER GHE;Lo;0;L;;;;;N;;;;;
+10526;ELBASAN LETTER GHAMMA;Lo;0;L;;;;;N;;;;;
+10527;ELBASAN LETTER KHE;Lo;0;L;;;;;N;;;;;
+10530;CAUCASIAN ALBANIAN LETTER ALT;Lo;0;L;;;;;N;;;;;
+10531;CAUCASIAN ALBANIAN LETTER BET;Lo;0;L;;;;;N;;;;;
+10532;CAUCASIAN ALBANIAN LETTER GIM;Lo;0;L;;;;;N;;;;;
+10533;CAUCASIAN ALBANIAN LETTER DAT;Lo;0;L;;;;;N;;;;;
+10534;CAUCASIAN ALBANIAN LETTER EB;Lo;0;L;;;;;N;;;;;
+10535;CAUCASIAN ALBANIAN LETTER ZARL;Lo;0;L;;;;;N;;;;;
+10536;CAUCASIAN ALBANIAN LETTER EYN;Lo;0;L;;;;;N;;;;;
+10537;CAUCASIAN ALBANIAN LETTER ZHIL;Lo;0;L;;;;;N;;;;;
+10538;CAUCASIAN ALBANIAN LETTER TAS;Lo;0;L;;;;;N;;;;;
+10539;CAUCASIAN ALBANIAN LETTER CHA;Lo;0;L;;;;;N;;;;;
+1053A;CAUCASIAN ALBANIAN LETTER YOWD;Lo;0;L;;;;;N;;;;;
+1053B;CAUCASIAN ALBANIAN LETTER ZHA;Lo;0;L;;;;;N;;;;;
+1053C;CAUCASIAN ALBANIAN LETTER IRB;Lo;0;L;;;;;N;;;;;
+1053D;CAUCASIAN ALBANIAN LETTER SHA;Lo;0;L;;;;;N;;;;;
+1053E;CAUCASIAN ALBANIAN LETTER LAN;Lo;0;L;;;;;N;;;;;
+1053F;CAUCASIAN ALBANIAN LETTER INYA;Lo;0;L;;;;;N;;;;;
+10540;CAUCASIAN ALBANIAN LETTER XEYN;Lo;0;L;;;;;N;;;;;
+10541;CAUCASIAN ALBANIAN LETTER DYAN;Lo;0;L;;;;;N;;;;;
+10542;CAUCASIAN ALBANIAN LETTER CAR;Lo;0;L;;;;;N;;;;;
+10543;CAUCASIAN ALBANIAN LETTER JHOX;Lo;0;L;;;;;N;;;;;
+10544;CAUCASIAN ALBANIAN LETTER KAR;Lo;0;L;;;;;N;;;;;
+10545;CAUCASIAN ALBANIAN LETTER LYIT;Lo;0;L;;;;;N;;;;;
+10546;CAUCASIAN ALBANIAN LETTER HEYT;Lo;0;L;;;;;N;;;;;
+10547;CAUCASIAN ALBANIAN LETTER QAY;Lo;0;L;;;;;N;;;;;
+10548;CAUCASIAN ALBANIAN LETTER AOR;Lo;0;L;;;;;N;;;;;
+10549;CAUCASIAN ALBANIAN LETTER CHOY;Lo;0;L;;;;;N;;;;;
+1054A;CAUCASIAN ALBANIAN LETTER CHI;Lo;0;L;;;;;N;;;;;
+1054B;CAUCASIAN ALBANIAN LETTER CYAY;Lo;0;L;;;;;N;;;;;
+1054C;CAUCASIAN ALBANIAN LETTER MAQ;Lo;0;L;;;;;N;;;;;
+1054D;CAUCASIAN ALBANIAN LETTER QAR;Lo;0;L;;;;;N;;;;;
+1054E;CAUCASIAN ALBANIAN LETTER NOWC;Lo;0;L;;;;;N;;;;;
+1054F;CAUCASIAN ALBANIAN LETTER DZYAY;Lo;0;L;;;;;N;;;;;
+10550;CAUCASIAN ALBANIAN LETTER SHAK;Lo;0;L;;;;;N;;;;;
+10551;CAUCASIAN ALBANIAN LETTER JAYN;Lo;0;L;;;;;N;;;;;
+10552;CAUCASIAN ALBANIAN LETTER ON;Lo;0;L;;;;;N;;;;;
+10553;CAUCASIAN ALBANIAN LETTER TYAY;Lo;0;L;;;;;N;;;;;
+10554;CAUCASIAN ALBANIAN LETTER FAM;Lo;0;L;;;;;N;;;;;
+10555;CAUCASIAN ALBANIAN LETTER DZAY;Lo;0;L;;;;;N;;;;;
+10556;CAUCASIAN ALBANIAN LETTER CHAT;Lo;0;L;;;;;N;;;;;
+10557;CAUCASIAN ALBANIAN LETTER PEN;Lo;0;L;;;;;N;;;;;
+10558;CAUCASIAN ALBANIAN LETTER GHEYS;Lo;0;L;;;;;N;;;;;
+10559;CAUCASIAN ALBANIAN LETTER RAT;Lo;0;L;;;;;N;;;;;
+1055A;CAUCASIAN ALBANIAN LETTER SEYK;Lo;0;L;;;;;N;;;;;
+1055B;CAUCASIAN ALBANIAN LETTER VEYZ;Lo;0;L;;;;;N;;;;;
+1055C;CAUCASIAN ALBANIAN LETTER TIWR;Lo;0;L;;;;;N;;;;;
+1055D;CAUCASIAN ALBANIAN LETTER SHOY;Lo;0;L;;;;;N;;;;;
+1055E;CAUCASIAN ALBANIAN LETTER IWN;Lo;0;L;;;;;N;;;;;
+1055F;CAUCASIAN ALBANIAN LETTER CYAW;Lo;0;L;;;;;N;;;;;
+10560;CAUCASIAN ALBANIAN LETTER CAYN;Lo;0;L;;;;;N;;;;;
+10561;CAUCASIAN ALBANIAN LETTER YAYD;Lo;0;L;;;;;N;;;;;
+10562;CAUCASIAN ALBANIAN LETTER PIWR;Lo;0;L;;;;;N;;;;;
+10563;CAUCASIAN ALBANIAN LETTER KIW;Lo;0;L;;;;;N;;;;;
+1056F;CAUCASIAN ALBANIAN CITATION MARK;Po;0;L;;;;;N;;;;;
+10600;LINEAR A SIGN AB001;Lo;0;L;;;;;N;;;;;
+10601;LINEAR A SIGN AB002;Lo;0;L;;;;;N;;;;;
+10602;LINEAR A SIGN AB003;Lo;0;L;;;;;N;;;;;
+10603;LINEAR A SIGN AB004;Lo;0;L;;;;;N;;;;;
+10604;LINEAR A SIGN AB005;Lo;0;L;;;;;N;;;;;
+10605;LINEAR A SIGN AB006;Lo;0;L;;;;;N;;;;;
+10606;LINEAR A SIGN AB007;Lo;0;L;;;;;N;;;;;
+10607;LINEAR A SIGN AB008;Lo;0;L;;;;;N;;;;;
+10608;LINEAR A SIGN AB009;Lo;0;L;;;;;N;;;;;
+10609;LINEAR A SIGN AB010;Lo;0;L;;;;;N;;;;;
+1060A;LINEAR A SIGN AB011;Lo;0;L;;;;;N;;;;;
+1060B;LINEAR A SIGN AB013;Lo;0;L;;;;;N;;;;;
+1060C;LINEAR A SIGN AB016;Lo;0;L;;;;;N;;;;;
+1060D;LINEAR A SIGN AB017;Lo;0;L;;;;;N;;;;;
+1060E;LINEAR A SIGN AB020;Lo;0;L;;;;;N;;;;;
+1060F;LINEAR A SIGN AB021;Lo;0;L;;;;;N;;;;;
+10610;LINEAR A SIGN AB021F;Lo;0;L;;;;;N;;;;;
+10611;LINEAR A SIGN AB021M;Lo;0;L;;;;;N;;;;;
+10612;LINEAR A SIGN AB022;Lo;0;L;;;;;N;;;;;
+10613;LINEAR A SIGN AB022F;Lo;0;L;;;;;N;;;;;
+10614;LINEAR A SIGN AB022M;Lo;0;L;;;;;N;;;;;
+10615;LINEAR A SIGN AB023;Lo;0;L;;;;;N;;;;;
+10616;LINEAR A SIGN AB023M;Lo;0;L;;;;;N;;;;;
+10617;LINEAR A SIGN AB024;Lo;0;L;;;;;N;;;;;
+10618;LINEAR A SIGN AB026;Lo;0;L;;;;;N;;;;;
+10619;LINEAR A SIGN AB027;Lo;0;L;;;;;N;;;;;
+1061A;LINEAR A SIGN AB028;Lo;0;L;;;;;N;;;;;
+1061B;LINEAR A SIGN A028B;Lo;0;L;;;;;N;;;;;
+1061C;LINEAR A SIGN AB029;Lo;0;L;;;;;N;;;;;
+1061D;LINEAR A SIGN AB030;Lo;0;L;;;;;N;;;;;
+1061E;LINEAR A SIGN AB031;Lo;0;L;;;;;N;;;;;
+1061F;LINEAR A SIGN AB034;Lo;0;L;;;;;N;;;;;
+10620;LINEAR A SIGN AB037;Lo;0;L;;;;;N;;;;;
+10621;LINEAR A SIGN AB038;Lo;0;L;;;;;N;;;;;
+10622;LINEAR A SIGN AB039;Lo;0;L;;;;;N;;;;;
+10623;LINEAR A SIGN AB040;Lo;0;L;;;;;N;;;;;
+10624;LINEAR A SIGN AB041;Lo;0;L;;;;;N;;;;;
+10625;LINEAR A SIGN AB044;Lo;0;L;;;;;N;;;;;
+10626;LINEAR A SIGN AB045;Lo;0;L;;;;;N;;;;;
+10627;LINEAR A SIGN AB046;Lo;0;L;;;;;N;;;;;
+10628;LINEAR A SIGN AB047;Lo;0;L;;;;;N;;;;;
+10629;LINEAR A SIGN AB048;Lo;0;L;;;;;N;;;;;
+1062A;LINEAR A SIGN AB049;Lo;0;L;;;;;N;;;;;
+1062B;LINEAR A SIGN AB050;Lo;0;L;;;;;N;;;;;
+1062C;LINEAR A SIGN AB051;Lo;0;L;;;;;N;;;;;
+1062D;LINEAR A SIGN AB053;Lo;0;L;;;;;N;;;;;
+1062E;LINEAR A SIGN AB054;Lo;0;L;;;;;N;;;;;
+1062F;LINEAR A SIGN AB055;Lo;0;L;;;;;N;;;;;
+10630;LINEAR A SIGN AB056;Lo;0;L;;;;;N;;;;;
+10631;LINEAR A SIGN AB057;Lo;0;L;;;;;N;;;;;
+10632;LINEAR A SIGN AB058;Lo;0;L;;;;;N;;;;;
+10633;LINEAR A SIGN AB059;Lo;0;L;;;;;N;;;;;
+10634;LINEAR A SIGN AB060;Lo;0;L;;;;;N;;;;;
+10635;LINEAR A SIGN AB061;Lo;0;L;;;;;N;;;;;
+10636;LINEAR A SIGN AB065;Lo;0;L;;;;;N;;;;;
+10637;LINEAR A SIGN AB066;Lo;0;L;;;;;N;;;;;
+10638;LINEAR A SIGN AB067;Lo;0;L;;;;;N;;;;;
+10639;LINEAR A SIGN AB069;Lo;0;L;;;;;N;;;;;
+1063A;LINEAR A SIGN AB070;Lo;0;L;;;;;N;;;;;
+1063B;LINEAR A SIGN AB073;Lo;0;L;;;;;N;;;;;
+1063C;LINEAR A SIGN AB074;Lo;0;L;;;;;N;;;;;
+1063D;LINEAR A SIGN AB076;Lo;0;L;;;;;N;;;;;
+1063E;LINEAR A SIGN AB077;Lo;0;L;;;;;N;;;;;
+1063F;LINEAR A SIGN AB078;Lo;0;L;;;;;N;;;;;
+10640;LINEAR A SIGN AB079;Lo;0;L;;;;;N;;;;;
+10641;LINEAR A SIGN AB080;Lo;0;L;;;;;N;;;;;
+10642;LINEAR A SIGN AB081;Lo;0;L;;;;;N;;;;;
+10643;LINEAR A SIGN AB082;Lo;0;L;;;;;N;;;;;
+10644;LINEAR A SIGN AB085;Lo;0;L;;;;;N;;;;;
+10645;LINEAR A SIGN AB086;Lo;0;L;;;;;N;;;;;
+10646;LINEAR A SIGN AB087;Lo;0;L;;;;;N;;;;;
+10647;LINEAR A SIGN A100-102;Lo;0;L;;;;;N;;;;;
+10648;LINEAR A SIGN AB118;Lo;0;L;;;;;N;;;;;
+10649;LINEAR A SIGN AB120;Lo;0;L;;;;;N;;;;;
+1064A;LINEAR A SIGN A120B;Lo;0;L;;;;;N;;;;;
+1064B;LINEAR A SIGN AB122;Lo;0;L;;;;;N;;;;;
+1064C;LINEAR A SIGN AB123;Lo;0;L;;;;;N;;;;;
+1064D;LINEAR A SIGN AB131A;Lo;0;L;;;;;N;;;;;
+1064E;LINEAR A SIGN AB131B;Lo;0;L;;;;;N;;;;;
+1064F;LINEAR A SIGN A131C;Lo;0;L;;;;;N;;;;;
+10650;LINEAR A SIGN AB164;Lo;0;L;;;;;N;;;;;
+10651;LINEAR A SIGN AB171;Lo;0;L;;;;;N;;;;;
+10652;LINEAR A SIGN AB180;Lo;0;L;;;;;N;;;;;
+10653;LINEAR A SIGN AB188;Lo;0;L;;;;;N;;;;;
+10654;LINEAR A SIGN AB191;Lo;0;L;;;;;N;;;;;
+10655;LINEAR A SIGN A301;Lo;0;L;;;;;N;;;;;
+10656;LINEAR A SIGN A302;Lo;0;L;;;;;N;;;;;
+10657;LINEAR A SIGN A303;Lo;0;L;;;;;N;;;;;
+10658;LINEAR A SIGN A304;Lo;0;L;;;;;N;;;;;
+10659;LINEAR A SIGN A305;Lo;0;L;;;;;N;;;;;
+1065A;LINEAR A SIGN A306;Lo;0;L;;;;;N;;;;;
+1065B;LINEAR A SIGN A307;Lo;0;L;;;;;N;;;;;
+1065C;LINEAR A SIGN A308;Lo;0;L;;;;;N;;;;;
+1065D;LINEAR A SIGN A309A;Lo;0;L;;;;;N;;;;;
+1065E;LINEAR A SIGN A309B;Lo;0;L;;;;;N;;;;;
+1065F;LINEAR A SIGN A309C;Lo;0;L;;;;;N;;;;;
+10660;LINEAR A SIGN A310;Lo;0;L;;;;;N;;;;;
+10661;LINEAR A SIGN A311;Lo;0;L;;;;;N;;;;;
+10662;LINEAR A SIGN A312;Lo;0;L;;;;;N;;;;;
+10663;LINEAR A SIGN A313A;Lo;0;L;;;;;N;;;;;
+10664;LINEAR A SIGN A313B;Lo;0;L;;;;;N;;;;;
+10665;LINEAR A SIGN A313C;Lo;0;L;;;;;N;;;;;
+10666;LINEAR A SIGN A314;Lo;0;L;;;;;N;;;;;
+10667;LINEAR A SIGN A315;Lo;0;L;;;;;N;;;;;
+10668;LINEAR A SIGN A316;Lo;0;L;;;;;N;;;;;
+10669;LINEAR A SIGN A317;Lo;0;L;;;;;N;;;;;
+1066A;LINEAR A SIGN A318;Lo;0;L;;;;;N;;;;;
+1066B;LINEAR A SIGN A319;Lo;0;L;;;;;N;;;;;
+1066C;LINEAR A SIGN A320;Lo;0;L;;;;;N;;;;;
+1066D;LINEAR A SIGN A321;Lo;0;L;;;;;N;;;;;
+1066E;LINEAR A SIGN A322;Lo;0;L;;;;;N;;;;;
+1066F;LINEAR A SIGN A323;Lo;0;L;;;;;N;;;;;
+10670;LINEAR A SIGN A324;Lo;0;L;;;;;N;;;;;
+10671;LINEAR A SIGN A325;Lo;0;L;;;;;N;;;;;
+10672;LINEAR A SIGN A326;Lo;0;L;;;;;N;;;;;
+10673;LINEAR A SIGN A327;Lo;0;L;;;;;N;;;;;
+10674;LINEAR A SIGN A328;Lo;0;L;;;;;N;;;;;
+10675;LINEAR A SIGN A329;Lo;0;L;;;;;N;;;;;
+10676;LINEAR A SIGN A330;Lo;0;L;;;;;N;;;;;
+10677;LINEAR A SIGN A331;Lo;0;L;;;;;N;;;;;
+10678;LINEAR A SIGN A332;Lo;0;L;;;;;N;;;;;
+10679;LINEAR A SIGN A333;Lo;0;L;;;;;N;;;;;
+1067A;LINEAR A SIGN A334;Lo;0;L;;;;;N;;;;;
+1067B;LINEAR A SIGN A335;Lo;0;L;;;;;N;;;;;
+1067C;LINEAR A SIGN A336;Lo;0;L;;;;;N;;;;;
+1067D;LINEAR A SIGN A337;Lo;0;L;;;;;N;;;;;
+1067E;LINEAR A SIGN A338;Lo;0;L;;;;;N;;;;;
+1067F;LINEAR A SIGN A339;Lo;0;L;;;;;N;;;;;
+10680;LINEAR A SIGN A340;Lo;0;L;;;;;N;;;;;
+10681;LINEAR A SIGN A341;Lo;0;L;;;;;N;;;;;
+10682;LINEAR A SIGN A342;Lo;0;L;;;;;N;;;;;
+10683;LINEAR A SIGN A343;Lo;0;L;;;;;N;;;;;
+10684;LINEAR A SIGN A344;Lo;0;L;;;;;N;;;;;
+10685;LINEAR A SIGN A345;Lo;0;L;;;;;N;;;;;
+10686;LINEAR A SIGN A346;Lo;0;L;;;;;N;;;;;
+10687;LINEAR A SIGN A347;Lo;0;L;;;;;N;;;;;
+10688;LINEAR A SIGN A348;Lo;0;L;;;;;N;;;;;
+10689;LINEAR A SIGN A349;Lo;0;L;;;;;N;;;;;
+1068A;LINEAR A SIGN A350;Lo;0;L;;;;;N;;;;;
+1068B;LINEAR A SIGN A351;Lo;0;L;;;;;N;;;;;
+1068C;LINEAR A SIGN A352;Lo;0;L;;;;;N;;;;;
+1068D;LINEAR A SIGN A353;Lo;0;L;;;;;N;;;;;
+1068E;LINEAR A SIGN A354;Lo;0;L;;;;;N;;;;;
+1068F;LINEAR A SIGN A355;Lo;0;L;;;;;N;;;;;
+10690;LINEAR A SIGN A356;Lo;0;L;;;;;N;;;;;
+10691;LINEAR A SIGN A357;Lo;0;L;;;;;N;;;;;
+10692;LINEAR A SIGN A358;Lo;0;L;;;;;N;;;;;
+10693;LINEAR A SIGN A359;Lo;0;L;;;;;N;;;;;
+10694;LINEAR A SIGN A360;Lo;0;L;;;;;N;;;;;
+10695;LINEAR A SIGN A361;Lo;0;L;;;;;N;;;;;
+10696;LINEAR A SIGN A362;Lo;0;L;;;;;N;;;;;
+10697;LINEAR A SIGN A363;Lo;0;L;;;;;N;;;;;
+10698;LINEAR A SIGN A364;Lo;0;L;;;;;N;;;;;
+10699;LINEAR A SIGN A365;Lo;0;L;;;;;N;;;;;
+1069A;LINEAR A SIGN A366;Lo;0;L;;;;;N;;;;;
+1069B;LINEAR A SIGN A367;Lo;0;L;;;;;N;;;;;
+1069C;LINEAR A SIGN A368;Lo;0;L;;;;;N;;;;;
+1069D;LINEAR A SIGN A369;Lo;0;L;;;;;N;;;;;
+1069E;LINEAR A SIGN A370;Lo;0;L;;;;;N;;;;;
+1069F;LINEAR A SIGN A371;Lo;0;L;;;;;N;;;;;
+106A0;LINEAR A SIGN A400-VAS;Lo;0;L;;;;;N;;;;;
+106A1;LINEAR A SIGN A401-VAS;Lo;0;L;;;;;N;;;;;
+106A2;LINEAR A SIGN A402-VAS;Lo;0;L;;;;;N;;;;;
+106A3;LINEAR A SIGN A403-VAS;Lo;0;L;;;;;N;;;;;
+106A4;LINEAR A SIGN A404-VAS;Lo;0;L;;;;;N;;;;;
+106A5;LINEAR A SIGN A405-VAS;Lo;0;L;;;;;N;;;;;
+106A6;LINEAR A SIGN A406-VAS;Lo;0;L;;;;;N;;;;;
+106A7;LINEAR A SIGN A407-VAS;Lo;0;L;;;;;N;;;;;
+106A8;LINEAR A SIGN A408-VAS;Lo;0;L;;;;;N;;;;;
+106A9;LINEAR A SIGN A409-VAS;Lo;0;L;;;;;N;;;;;
+106AA;LINEAR A SIGN A410-VAS;Lo;0;L;;;;;N;;;;;
+106AB;LINEAR A SIGN A411-VAS;Lo;0;L;;;;;N;;;;;
+106AC;LINEAR A SIGN A412-VAS;Lo;0;L;;;;;N;;;;;
+106AD;LINEAR A SIGN A413-VAS;Lo;0;L;;;;;N;;;;;
+106AE;LINEAR A SIGN A414-VAS;Lo;0;L;;;;;N;;;;;
+106AF;LINEAR A SIGN A415-VAS;Lo;0;L;;;;;N;;;;;
+106B0;LINEAR A SIGN A416-VAS;Lo;0;L;;;;;N;;;;;
+106B1;LINEAR A SIGN A417-VAS;Lo;0;L;;;;;N;;;;;
+106B2;LINEAR A SIGN A418-VAS;Lo;0;L;;;;;N;;;;;
+106B3;LINEAR A SIGN A501;Lo;0;L;;;;;N;;;;;
+106B4;LINEAR A SIGN A502;Lo;0;L;;;;;N;;;;;
+106B5;LINEAR A SIGN A503;Lo;0;L;;;;;N;;;;;
+106B6;LINEAR A SIGN A504;Lo;0;L;;;;;N;;;;;
+106B7;LINEAR A SIGN A505;Lo;0;L;;;;;N;;;;;
+106B8;LINEAR A SIGN A506;Lo;0;L;;;;;N;;;;;
+106B9;LINEAR A SIGN A508;Lo;0;L;;;;;N;;;;;
+106BA;LINEAR A SIGN A509;Lo;0;L;;;;;N;;;;;
+106BB;LINEAR A SIGN A510;Lo;0;L;;;;;N;;;;;
+106BC;LINEAR A SIGN A511;Lo;0;L;;;;;N;;;;;
+106BD;LINEAR A SIGN A512;Lo;0;L;;;;;N;;;;;
+106BE;LINEAR A SIGN A513;Lo;0;L;;;;;N;;;;;
+106BF;LINEAR A SIGN A515;Lo;0;L;;;;;N;;;;;
+106C0;LINEAR A SIGN A516;Lo;0;L;;;;;N;;;;;
+106C1;LINEAR A SIGN A520;Lo;0;L;;;;;N;;;;;
+106C2;LINEAR A SIGN A521;Lo;0;L;;;;;N;;;;;
+106C3;LINEAR A SIGN A523;Lo;0;L;;;;;N;;;;;
+106C4;LINEAR A SIGN A524;Lo;0;L;;;;;N;;;;;
+106C5;LINEAR A SIGN A525;Lo;0;L;;;;;N;;;;;
+106C6;LINEAR A SIGN A526;Lo;0;L;;;;;N;;;;;
+106C7;LINEAR A SIGN A527;Lo;0;L;;;;;N;;;;;
+106C8;LINEAR A SIGN A528;Lo;0;L;;;;;N;;;;;
+106C9;LINEAR A SIGN A529;Lo;0;L;;;;;N;;;;;
+106CA;LINEAR A SIGN A530;Lo;0;L;;;;;N;;;;;
+106CB;LINEAR A SIGN A531;Lo;0;L;;;;;N;;;;;
+106CC;LINEAR A SIGN A532;Lo;0;L;;;;;N;;;;;
+106CD;LINEAR A SIGN A534;Lo;0;L;;;;;N;;;;;
+106CE;LINEAR A SIGN A535;Lo;0;L;;;;;N;;;;;
+106CF;LINEAR A SIGN A536;Lo;0;L;;;;;N;;;;;
+106D0;LINEAR A SIGN A537;Lo;0;L;;;;;N;;;;;
+106D1;LINEAR A SIGN A538;Lo;0;L;;;;;N;;;;;
+106D2;LINEAR A SIGN A539;Lo;0;L;;;;;N;;;;;
+106D3;LINEAR A SIGN A540;Lo;0;L;;;;;N;;;;;
+106D4;LINEAR A SIGN A541;Lo;0;L;;;;;N;;;;;
+106D5;LINEAR A SIGN A542;Lo;0;L;;;;;N;;;;;
+106D6;LINEAR A SIGN A545;Lo;0;L;;;;;N;;;;;
+106D7;LINEAR A SIGN A547;Lo;0;L;;;;;N;;;;;
+106D8;LINEAR A SIGN A548;Lo;0;L;;;;;N;;;;;
+106D9;LINEAR A SIGN A549;Lo;0;L;;;;;N;;;;;
+106DA;LINEAR A SIGN A550;Lo;0;L;;;;;N;;;;;
+106DB;LINEAR A SIGN A551;Lo;0;L;;;;;N;;;;;
+106DC;LINEAR A SIGN A552;Lo;0;L;;;;;N;;;;;
+106DD;LINEAR A SIGN A553;Lo;0;L;;;;;N;;;;;
+106DE;LINEAR A SIGN A554;Lo;0;L;;;;;N;;;;;
+106DF;LINEAR A SIGN A555;Lo;0;L;;;;;N;;;;;
+106E0;LINEAR A SIGN A556;Lo;0;L;;;;;N;;;;;
+106E1;LINEAR A SIGN A557;Lo;0;L;;;;;N;;;;;
+106E2;LINEAR A SIGN A559;Lo;0;L;;;;;N;;;;;
+106E3;LINEAR A SIGN A563;Lo;0;L;;;;;N;;;;;
+106E4;LINEAR A SIGN A564;Lo;0;L;;;;;N;;;;;
+106E5;LINEAR A SIGN A565;Lo;0;L;;;;;N;;;;;
+106E6;LINEAR A SIGN A566;Lo;0;L;;;;;N;;;;;
+106E7;LINEAR A SIGN A568;Lo;0;L;;;;;N;;;;;
+106E8;LINEAR A SIGN A569;Lo;0;L;;;;;N;;;;;
+106E9;LINEAR A SIGN A570;Lo;0;L;;;;;N;;;;;
+106EA;LINEAR A SIGN A571;Lo;0;L;;;;;N;;;;;
+106EB;LINEAR A SIGN A572;Lo;0;L;;;;;N;;;;;
+106EC;LINEAR A SIGN A573;Lo;0;L;;;;;N;;;;;
+106ED;LINEAR A SIGN A574;Lo;0;L;;;;;N;;;;;
+106EE;LINEAR A SIGN A575;Lo;0;L;;;;;N;;;;;
+106EF;LINEAR A SIGN A576;Lo;0;L;;;;;N;;;;;
+106F0;LINEAR A SIGN A577;Lo;0;L;;;;;N;;;;;
+106F1;LINEAR A SIGN A578;Lo;0;L;;;;;N;;;;;
+106F2;LINEAR A SIGN A579;Lo;0;L;;;;;N;;;;;
+106F3;LINEAR A SIGN A580;Lo;0;L;;;;;N;;;;;
+106F4;LINEAR A SIGN A581;Lo;0;L;;;;;N;;;;;
+106F5;LINEAR A SIGN A582;Lo;0;L;;;;;N;;;;;
+106F6;LINEAR A SIGN A583;Lo;0;L;;;;;N;;;;;
+106F7;LINEAR A SIGN A584;Lo;0;L;;;;;N;;;;;
+106F8;LINEAR A SIGN A585;Lo;0;L;;;;;N;;;;;
+106F9;LINEAR A SIGN A586;Lo;0;L;;;;;N;;;;;
+106FA;LINEAR A SIGN A587;Lo;0;L;;;;;N;;;;;
+106FB;LINEAR A SIGN A588;Lo;0;L;;;;;N;;;;;
+106FC;LINEAR A SIGN A589;Lo;0;L;;;;;N;;;;;
+106FD;LINEAR A SIGN A591;Lo;0;L;;;;;N;;;;;
+106FE;LINEAR A SIGN A592;Lo;0;L;;;;;N;;;;;
+106FF;LINEAR A SIGN A594;Lo;0;L;;;;;N;;;;;
+10700;LINEAR A SIGN A595;Lo;0;L;;;;;N;;;;;
+10701;LINEAR A SIGN A596;Lo;0;L;;;;;N;;;;;
+10702;LINEAR A SIGN A598;Lo;0;L;;;;;N;;;;;
+10703;LINEAR A SIGN A600;Lo;0;L;;;;;N;;;;;
+10704;LINEAR A SIGN A601;Lo;0;L;;;;;N;;;;;
+10705;LINEAR A SIGN A602;Lo;0;L;;;;;N;;;;;
+10706;LINEAR A SIGN A603;Lo;0;L;;;;;N;;;;;
+10707;LINEAR A SIGN A604;Lo;0;L;;;;;N;;;;;
+10708;LINEAR A SIGN A606;Lo;0;L;;;;;N;;;;;
+10709;LINEAR A SIGN A608;Lo;0;L;;;;;N;;;;;
+1070A;LINEAR A SIGN A609;Lo;0;L;;;;;N;;;;;
+1070B;LINEAR A SIGN A610;Lo;0;L;;;;;N;;;;;
+1070C;LINEAR A SIGN A611;Lo;0;L;;;;;N;;;;;
+1070D;LINEAR A SIGN A612;Lo;0;L;;;;;N;;;;;
+1070E;LINEAR A SIGN A613;Lo;0;L;;;;;N;;;;;
+1070F;LINEAR A SIGN A614;Lo;0;L;;;;;N;;;;;
+10710;LINEAR A SIGN A615;Lo;0;L;;;;;N;;;;;
+10711;LINEAR A SIGN A616;Lo;0;L;;;;;N;;;;;
+10712;LINEAR A SIGN A617;Lo;0;L;;;;;N;;;;;
+10713;LINEAR A SIGN A618;Lo;0;L;;;;;N;;;;;
+10714;LINEAR A SIGN A619;Lo;0;L;;;;;N;;;;;
+10715;LINEAR A SIGN A620;Lo;0;L;;;;;N;;;;;
+10716;LINEAR A SIGN A621;Lo;0;L;;;;;N;;;;;
+10717;LINEAR A SIGN A622;Lo;0;L;;;;;N;;;;;
+10718;LINEAR A SIGN A623;Lo;0;L;;;;;N;;;;;
+10719;LINEAR A SIGN A624;Lo;0;L;;;;;N;;;;;
+1071A;LINEAR A SIGN A626;Lo;0;L;;;;;N;;;;;
+1071B;LINEAR A SIGN A627;Lo;0;L;;;;;N;;;;;
+1071C;LINEAR A SIGN A628;Lo;0;L;;;;;N;;;;;
+1071D;LINEAR A SIGN A629;Lo;0;L;;;;;N;;;;;
+1071E;LINEAR A SIGN A634;Lo;0;L;;;;;N;;;;;
+1071F;LINEAR A SIGN A637;Lo;0;L;;;;;N;;;;;
+10720;LINEAR A SIGN A638;Lo;0;L;;;;;N;;;;;
+10721;LINEAR A SIGN A640;Lo;0;L;;;;;N;;;;;
+10722;LINEAR A SIGN A642;Lo;0;L;;;;;N;;;;;
+10723;LINEAR A SIGN A643;Lo;0;L;;;;;N;;;;;
+10724;LINEAR A SIGN A644;Lo;0;L;;;;;N;;;;;
+10725;LINEAR A SIGN A645;Lo;0;L;;;;;N;;;;;
+10726;LINEAR A SIGN A646;Lo;0;L;;;;;N;;;;;
+10727;LINEAR A SIGN A648;Lo;0;L;;;;;N;;;;;
+10728;LINEAR A SIGN A649;Lo;0;L;;;;;N;;;;;
+10729;LINEAR A SIGN A651;Lo;0;L;;;;;N;;;;;
+1072A;LINEAR A SIGN A652;Lo;0;L;;;;;N;;;;;
+1072B;LINEAR A SIGN A653;Lo;0;L;;;;;N;;;;;
+1072C;LINEAR A SIGN A654;Lo;0;L;;;;;N;;;;;
+1072D;LINEAR A SIGN A655;Lo;0;L;;;;;N;;;;;
+1072E;LINEAR A SIGN A656;Lo;0;L;;;;;N;;;;;
+1072F;LINEAR A SIGN A657;Lo;0;L;;;;;N;;;;;
+10730;LINEAR A SIGN A658;Lo;0;L;;;;;N;;;;;
+10731;LINEAR A SIGN A659;Lo;0;L;;;;;N;;;;;
+10732;LINEAR A SIGN A660;Lo;0;L;;;;;N;;;;;
+10733;LINEAR A SIGN A661;Lo;0;L;;;;;N;;;;;
+10734;LINEAR A SIGN A662;Lo;0;L;;;;;N;;;;;
+10735;LINEAR A SIGN A663;Lo;0;L;;;;;N;;;;;
+10736;LINEAR A SIGN A664;Lo;0;L;;;;;N;;;;;
+10740;LINEAR A SIGN A701 A;Lo;0;L;;;;;N;;;;;
+10741;LINEAR A SIGN A702 B;Lo;0;L;;;;;N;;;;;
+10742;LINEAR A SIGN A703 D;Lo;0;L;;;;;N;;;;;
+10743;LINEAR A SIGN A704 E;Lo;0;L;;;;;N;;;;;
+10744;LINEAR A SIGN A705 F;Lo;0;L;;;;;N;;;;;
+10745;LINEAR A SIGN A706 H;Lo;0;L;;;;;N;;;;;
+10746;LINEAR A SIGN A707 J;Lo;0;L;;;;;N;;;;;
+10747;LINEAR A SIGN A708 K;Lo;0;L;;;;;N;;;;;
+10748;LINEAR A SIGN A709 L;Lo;0;L;;;;;N;;;;;
+10749;LINEAR A SIGN A709-2 L2;Lo;0;L;;;;;N;;;;;
+1074A;LINEAR A SIGN A709-3 L3;Lo;0;L;;;;;N;;;;;
+1074B;LINEAR A SIGN A709-4 L4;Lo;0;L;;;;;N;;;;;
+1074C;LINEAR A SIGN A709-6 L6;Lo;0;L;;;;;N;;;;;
+1074D;LINEAR A SIGN A710 W;Lo;0;L;;;;;N;;;;;
+1074E;LINEAR A SIGN A711 X;Lo;0;L;;;;;N;;;;;
+1074F;LINEAR A SIGN A712 Y;Lo;0;L;;;;;N;;;;;
+10750;LINEAR A SIGN A713 OMEGA;Lo;0;L;;;;;N;;;;;
+10751;LINEAR A SIGN A714 ABB;Lo;0;L;;;;;N;;;;;
+10752;LINEAR A SIGN A715 BB;Lo;0;L;;;;;N;;;;;
+10753;LINEAR A SIGN A717 DD;Lo;0;L;;;;;N;;;;;
+10754;LINEAR A SIGN A726 EYYY;Lo;0;L;;;;;N;;;;;
+10755;LINEAR A SIGN A732 JE;Lo;0;L;;;;;N;;;;;
+10760;LINEAR A SIGN A800;Lo;0;L;;;;;N;;;;;
+10761;LINEAR A SIGN A801;Lo;0;L;;;;;N;;;;;
+10762;LINEAR A SIGN A802;Lo;0;L;;;;;N;;;;;
+10763;LINEAR A SIGN A803;Lo;0;L;;;;;N;;;;;
+10764;LINEAR A SIGN A804;Lo;0;L;;;;;N;;;;;
+10765;LINEAR A SIGN A805;Lo;0;L;;;;;N;;;;;
+10766;LINEAR A SIGN A806;Lo;0;L;;;;;N;;;;;
+10767;LINEAR A SIGN A807;Lo;0;L;;;;;N;;;;;
+10800;CYPRIOT SYLLABLE A;Lo;0;R;;;;;N;;;;;
+10801;CYPRIOT SYLLABLE E;Lo;0;R;;;;;N;;;;;
+10802;CYPRIOT SYLLABLE I;Lo;0;R;;;;;N;;;;;
+10803;CYPRIOT SYLLABLE O;Lo;0;R;;;;;N;;;;;
+10804;CYPRIOT SYLLABLE U;Lo;0;R;;;;;N;;;;;
+10805;CYPRIOT SYLLABLE JA;Lo;0;R;;;;;N;;;;;
+10808;CYPRIOT SYLLABLE JO;Lo;0;R;;;;;N;;;;;
+1080A;CYPRIOT SYLLABLE KA;Lo;0;R;;;;;N;;;;;
+1080B;CYPRIOT SYLLABLE KE;Lo;0;R;;;;;N;;;;;
+1080C;CYPRIOT SYLLABLE KI;Lo;0;R;;;;;N;;;;;
+1080D;CYPRIOT SYLLABLE KO;Lo;0;R;;;;;N;;;;;
+1080E;CYPRIOT SYLLABLE KU;Lo;0;R;;;;;N;;;;;
+1080F;CYPRIOT SYLLABLE LA;Lo;0;R;;;;;N;;;;;
+10810;CYPRIOT SYLLABLE LE;Lo;0;R;;;;;N;;;;;
+10811;CYPRIOT SYLLABLE LI;Lo;0;R;;;;;N;;;;;
+10812;CYPRIOT SYLLABLE LO;Lo;0;R;;;;;N;;;;;
+10813;CYPRIOT SYLLABLE LU;Lo;0;R;;;;;N;;;;;
+10814;CYPRIOT SYLLABLE MA;Lo;0;R;;;;;N;;;;;
+10815;CYPRIOT SYLLABLE ME;Lo;0;R;;;;;N;;;;;
+10816;CYPRIOT SYLLABLE MI;Lo;0;R;;;;;N;;;;;
+10817;CYPRIOT SYLLABLE MO;Lo;0;R;;;;;N;;;;;
+10818;CYPRIOT SYLLABLE MU;Lo;0;R;;;;;N;;;;;
+10819;CYPRIOT SYLLABLE NA;Lo;0;R;;;;;N;;;;;
+1081A;CYPRIOT SYLLABLE NE;Lo;0;R;;;;;N;;;;;
+1081B;CYPRIOT SYLLABLE NI;Lo;0;R;;;;;N;;;;;
+1081C;CYPRIOT SYLLABLE NO;Lo;0;R;;;;;N;;;;;
+1081D;CYPRIOT SYLLABLE NU;Lo;0;R;;;;;N;;;;;
+1081E;CYPRIOT SYLLABLE PA;Lo;0;R;;;;;N;;;;;
+1081F;CYPRIOT SYLLABLE PE;Lo;0;R;;;;;N;;;;;
+10820;CYPRIOT SYLLABLE PI;Lo;0;R;;;;;N;;;;;
+10821;CYPRIOT SYLLABLE PO;Lo;0;R;;;;;N;;;;;
+10822;CYPRIOT SYLLABLE PU;Lo;0;R;;;;;N;;;;;
+10823;CYPRIOT SYLLABLE RA;Lo;0;R;;;;;N;;;;;
+10824;CYPRIOT SYLLABLE RE;Lo;0;R;;;;;N;;;;;
+10825;CYPRIOT SYLLABLE RI;Lo;0;R;;;;;N;;;;;
+10826;CYPRIOT SYLLABLE RO;Lo;0;R;;;;;N;;;;;
+10827;CYPRIOT SYLLABLE RU;Lo;0;R;;;;;N;;;;;
+10828;CYPRIOT SYLLABLE SA;Lo;0;R;;;;;N;;;;;
+10829;CYPRIOT SYLLABLE SE;Lo;0;R;;;;;N;;;;;
+1082A;CYPRIOT SYLLABLE SI;Lo;0;R;;;;;N;;;;;
+1082B;CYPRIOT SYLLABLE SO;Lo;0;R;;;;;N;;;;;
+1082C;CYPRIOT SYLLABLE SU;Lo;0;R;;;;;N;;;;;
+1082D;CYPRIOT SYLLABLE TA;Lo;0;R;;;;;N;;;;;
+1082E;CYPRIOT SYLLABLE TE;Lo;0;R;;;;;N;;;;;
+1082F;CYPRIOT SYLLABLE TI;Lo;0;R;;;;;N;;;;;
+10830;CYPRIOT SYLLABLE TO;Lo;0;R;;;;;N;;;;;
+10831;CYPRIOT SYLLABLE TU;Lo;0;R;;;;;N;;;;;
+10832;CYPRIOT SYLLABLE WA;Lo;0;R;;;;;N;;;;;
+10833;CYPRIOT SYLLABLE WE;Lo;0;R;;;;;N;;;;;
+10834;CYPRIOT SYLLABLE WI;Lo;0;R;;;;;N;;;;;
+10835;CYPRIOT SYLLABLE WO;Lo;0;R;;;;;N;;;;;
+10837;CYPRIOT SYLLABLE XA;Lo;0;R;;;;;N;;;;;
+10838;CYPRIOT SYLLABLE XE;Lo;0;R;;;;;N;;;;;
+1083C;CYPRIOT SYLLABLE ZA;Lo;0;R;;;;;N;;;;;
+1083F;CYPRIOT SYLLABLE ZO;Lo;0;R;;;;;N;;;;;
+10840;IMPERIAL ARAMAIC LETTER ALEPH;Lo;0;R;;;;;N;;;;;
+10841;IMPERIAL ARAMAIC LETTER BETH;Lo;0;R;;;;;N;;;;;
+10842;IMPERIAL ARAMAIC LETTER GIMEL;Lo;0;R;;;;;N;;;;;
+10843;IMPERIAL ARAMAIC LETTER DALETH;Lo;0;R;;;;;N;;;;;
+10844;IMPERIAL ARAMAIC LETTER HE;Lo;0;R;;;;;N;;;;;
+10845;IMPERIAL ARAMAIC LETTER WAW;Lo;0;R;;;;;N;;;;;
+10846;IMPERIAL ARAMAIC LETTER ZAYIN;Lo;0;R;;;;;N;;;;;
+10847;IMPERIAL ARAMAIC LETTER HETH;Lo;0;R;;;;;N;;;;;
+10848;IMPERIAL ARAMAIC LETTER TETH;Lo;0;R;;;;;N;;;;;
+10849;IMPERIAL ARAMAIC LETTER YODH;Lo;0;R;;;;;N;;;;;
+1084A;IMPERIAL ARAMAIC LETTER KAPH;Lo;0;R;;;;;N;;;;;
+1084B;IMPERIAL ARAMAIC LETTER LAMEDH;Lo;0;R;;;;;N;;;;;
+1084C;IMPERIAL ARAMAIC LETTER MEM;Lo;0;R;;;;;N;;;;;
+1084D;IMPERIAL ARAMAIC LETTER NUN;Lo;0;R;;;;;N;;;;;
+1084E;IMPERIAL ARAMAIC LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+1084F;IMPERIAL ARAMAIC LETTER AYIN;Lo;0;R;;;;;N;;;;;
+10850;IMPERIAL ARAMAIC LETTER PE;Lo;0;R;;;;;N;;;;;
+10851;IMPERIAL ARAMAIC LETTER SADHE;Lo;0;R;;;;;N;;;;;
+10852;IMPERIAL ARAMAIC LETTER QOPH;Lo;0;R;;;;;N;;;;;
+10853;IMPERIAL ARAMAIC LETTER RESH;Lo;0;R;;;;;N;;;;;
+10854;IMPERIAL ARAMAIC LETTER SHIN;Lo;0;R;;;;;N;;;;;
+10855;IMPERIAL ARAMAIC LETTER TAW;Lo;0;R;;;;;N;;;;;
+10857;IMPERIAL ARAMAIC SECTION SIGN;Po;0;R;;;;;N;;;;;
+10858;IMPERIAL ARAMAIC NUMBER ONE;No;0;R;;;;1;N;;;;;
+10859;IMPERIAL ARAMAIC NUMBER TWO;No;0;R;;;;2;N;;;;;
+1085A;IMPERIAL ARAMAIC NUMBER THREE;No;0;R;;;;3;N;;;;;
+1085B;IMPERIAL ARAMAIC NUMBER TEN;No;0;R;;;;10;N;;;;;
+1085C;IMPERIAL ARAMAIC NUMBER TWENTY;No;0;R;;;;20;N;;;;;
+1085D;IMPERIAL ARAMAIC NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;;
+1085E;IMPERIAL ARAMAIC NUMBER ONE THOUSAND;No;0;R;;;;1000;N;;;;;
+1085F;IMPERIAL ARAMAIC NUMBER TEN THOUSAND;No;0;R;;;;10000;N;;;;;
+10860;PALMYRENE LETTER ALEPH;Lo;0;R;;;;;N;;;;;
+10861;PALMYRENE LETTER BETH;Lo;0;R;;;;;N;;;;;
+10862;PALMYRENE LETTER GIMEL;Lo;0;R;;;;;N;;;;;
+10863;PALMYRENE LETTER DALETH;Lo;0;R;;;;;N;;;;;
+10864;PALMYRENE LETTER HE;Lo;0;R;;;;;N;;;;;
+10865;PALMYRENE LETTER WAW;Lo;0;R;;;;;N;;;;;
+10866;PALMYRENE LETTER ZAYIN;Lo;0;R;;;;;N;;;;;
+10867;PALMYRENE LETTER HETH;Lo;0;R;;;;;N;;;;;
+10868;PALMYRENE LETTER TETH;Lo;0;R;;;;;N;;;;;
+10869;PALMYRENE LETTER YODH;Lo;0;R;;;;;N;;;;;
+1086A;PALMYRENE LETTER KAPH;Lo;0;R;;;;;N;;;;;
+1086B;PALMYRENE LETTER LAMEDH;Lo;0;R;;;;;N;;;;;
+1086C;PALMYRENE LETTER MEM;Lo;0;R;;;;;N;;;;;
+1086D;PALMYRENE LETTER FINAL NUN;Lo;0;R;;;;;N;;;;;
+1086E;PALMYRENE LETTER NUN;Lo;0;R;;;;;N;;;;;
+1086F;PALMYRENE LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+10870;PALMYRENE LETTER AYIN;Lo;0;R;;;;;N;;;;;
+10871;PALMYRENE LETTER PE;Lo;0;R;;;;;N;;;;;
+10872;PALMYRENE LETTER SADHE;Lo;0;R;;;;;N;;;;;
+10873;PALMYRENE LETTER QOPH;Lo;0;R;;;;;N;;;;;
+10874;PALMYRENE LETTER RESH;Lo;0;R;;;;;N;;;;;
+10875;PALMYRENE LETTER SHIN;Lo;0;R;;;;;N;;;;;
+10876;PALMYRENE LETTER TAW;Lo;0;R;;;;;N;;;;;
+10877;PALMYRENE LEFT-POINTING FLEURON;So;0;R;;;;;N;;;;;
+10878;PALMYRENE RIGHT-POINTING FLEURON;So;0;R;;;;;N;;;;;
+10879;PALMYRENE NUMBER ONE;No;0;R;;;;1;N;;;;;
+1087A;PALMYRENE NUMBER TWO;No;0;R;;;;2;N;;;;;
+1087B;PALMYRENE NUMBER THREE;No;0;R;;;;3;N;;;;;
+1087C;PALMYRENE NUMBER FOUR;No;0;R;;;;4;N;;;;;
+1087D;PALMYRENE NUMBER FIVE;No;0;R;;;;5;N;;;;;
+1087E;PALMYRENE NUMBER TEN;No;0;R;;;;10;N;;;;;
+1087F;PALMYRENE NUMBER TWENTY;No;0;R;;;;20;N;;;;;
+10880;NABATAEAN LETTER FINAL ALEPH;Lo;0;R;;;;;N;;;;;
+10881;NABATAEAN LETTER ALEPH;Lo;0;R;;;;;N;;;;;
+10882;NABATAEAN LETTER FINAL BETH;Lo;0;R;;;;;N;;;;;
+10883;NABATAEAN LETTER BETH;Lo;0;R;;;;;N;;;;;
+10884;NABATAEAN LETTER GIMEL;Lo;0;R;;;;;N;;;;;
+10885;NABATAEAN LETTER DALETH;Lo;0;R;;;;;N;;;;;
+10886;NABATAEAN LETTER FINAL HE;Lo;0;R;;;;;N;;;;;
+10887;NABATAEAN LETTER HE;Lo;0;R;;;;;N;;;;;
+10888;NABATAEAN LETTER WAW;Lo;0;R;;;;;N;;;;;
+10889;NABATAEAN LETTER ZAYIN;Lo;0;R;;;;;N;;;;;
+1088A;NABATAEAN LETTER HETH;Lo;0;R;;;;;N;;;;;
+1088B;NABATAEAN LETTER TETH;Lo;0;R;;;;;N;;;;;
+1088C;NABATAEAN LETTER FINAL YODH;Lo;0;R;;;;;N;;;;;
+1088D;NABATAEAN LETTER YODH;Lo;0;R;;;;;N;;;;;
+1088E;NABATAEAN LETTER FINAL KAPH;Lo;0;R;;;;;N;;;;;
+1088F;NABATAEAN LETTER KAPH;Lo;0;R;;;;;N;;;;;
+10890;NABATAEAN LETTER FINAL LAMEDH;Lo;0;R;;;;;N;;;;;
+10891;NABATAEAN LETTER LAMEDH;Lo;0;R;;;;;N;;;;;
+10892;NABATAEAN LETTER FINAL MEM;Lo;0;R;;;;;N;;;;;
+10893;NABATAEAN LETTER MEM;Lo;0;R;;;;;N;;;;;
+10894;NABATAEAN LETTER FINAL NUN;Lo;0;R;;;;;N;;;;;
+10895;NABATAEAN LETTER NUN;Lo;0;R;;;;;N;;;;;
+10896;NABATAEAN LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+10897;NABATAEAN LETTER AYIN;Lo;0;R;;;;;N;;;;;
+10898;NABATAEAN LETTER PE;Lo;0;R;;;;;N;;;;;
+10899;NABATAEAN LETTER SADHE;Lo;0;R;;;;;N;;;;;
+1089A;NABATAEAN LETTER QOPH;Lo;0;R;;;;;N;;;;;
+1089B;NABATAEAN LETTER RESH;Lo;0;R;;;;;N;;;;;
+1089C;NABATAEAN LETTER FINAL SHIN;Lo;0;R;;;;;N;;;;;
+1089D;NABATAEAN LETTER SHIN;Lo;0;R;;;;;N;;;;;
+1089E;NABATAEAN LETTER TAW;Lo;0;R;;;;;N;;;;;
+108A7;NABATAEAN NUMBER ONE;No;0;R;;;;1;N;;;;;
+108A8;NABATAEAN NUMBER TWO;No;0;R;;;;2;N;;;;;
+108A9;NABATAEAN NUMBER THREE;No;0;R;;;;3;N;;;;;
+108AA;NABATAEAN NUMBER FOUR;No;0;R;;;;4;N;;;;;
+108AB;NABATAEAN CRUCIFORM NUMBER FOUR;No;0;R;;;;4;N;;;;;
+108AC;NABATAEAN NUMBER FIVE;No;0;R;;;;5;N;;;;;
+108AD;NABATAEAN NUMBER TEN;No;0;R;;;;10;N;;;;;
+108AE;NABATAEAN NUMBER TWENTY;No;0;R;;;;20;N;;;;;
+108AF;NABATAEAN NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;;
+108E0;HATRAN LETTER ALEPH;Lo;0;R;;;;;N;;;;;
+108E1;HATRAN LETTER BETH;Lo;0;R;;;;;N;;;;;
+108E2;HATRAN LETTER GIMEL;Lo;0;R;;;;;N;;;;;
+108E3;HATRAN LETTER DALETH-RESH;Lo;0;R;;;;;N;;;;;
+108E4;HATRAN LETTER HE;Lo;0;R;;;;;N;;;;;
+108E5;HATRAN LETTER WAW;Lo;0;R;;;;;N;;;;;
+108E6;HATRAN LETTER ZAYN;Lo;0;R;;;;;N;;;;;
+108E7;HATRAN LETTER HETH;Lo;0;R;;;;;N;;;;;
+108E8;HATRAN LETTER TETH;Lo;0;R;;;;;N;;;;;
+108E9;HATRAN LETTER YODH;Lo;0;R;;;;;N;;;;;
+108EA;HATRAN LETTER KAPH;Lo;0;R;;;;;N;;;;;
+108EB;HATRAN LETTER LAMEDH;Lo;0;R;;;;;N;;;;;
+108EC;HATRAN LETTER MEM;Lo;0;R;;;;;N;;;;;
+108ED;HATRAN LETTER NUN;Lo;0;R;;;;;N;;;;;
+108EE;HATRAN LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+108EF;HATRAN LETTER AYN;Lo;0;R;;;;;N;;;;;
+108F0;HATRAN LETTER PE;Lo;0;R;;;;;N;;;;;
+108F1;HATRAN LETTER SADHE;Lo;0;R;;;;;N;;;;;
+108F2;HATRAN LETTER QOPH;Lo;0;R;;;;;N;;;;;
+108F4;HATRAN LETTER SHIN;Lo;0;R;;;;;N;;;;;
+108F5;HATRAN LETTER TAW;Lo;0;R;;;;;N;;;;;
+108FB;HATRAN NUMBER ONE;No;0;R;;;;1;N;;;;;
+108FC;HATRAN NUMBER FIVE;No;0;R;;;;5;N;;;;;
+108FD;HATRAN NUMBER TEN;No;0;R;;;;10;N;;;;;
+108FE;HATRAN NUMBER TWENTY;No;0;R;;;;20;N;;;;;
+108FF;HATRAN NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;;
+10900;PHOENICIAN LETTER ALF;Lo;0;R;;;;;N;;;;;
+10901;PHOENICIAN LETTER BET;Lo;0;R;;;;;N;;;;;
+10902;PHOENICIAN LETTER GAML;Lo;0;R;;;;;N;;;;;
+10903;PHOENICIAN LETTER DELT;Lo;0;R;;;;;N;;;;;
+10904;PHOENICIAN LETTER HE;Lo;0;R;;;;;N;;;;;
+10905;PHOENICIAN LETTER WAU;Lo;0;R;;;;;N;;;;;
+10906;PHOENICIAN LETTER ZAI;Lo;0;R;;;;;N;;;;;
+10907;PHOENICIAN LETTER HET;Lo;0;R;;;;;N;;;;;
+10908;PHOENICIAN LETTER TET;Lo;0;R;;;;;N;;;;;
+10909;PHOENICIAN LETTER YOD;Lo;0;R;;;;;N;;;;;
+1090A;PHOENICIAN LETTER KAF;Lo;0;R;;;;;N;;;;;
+1090B;PHOENICIAN LETTER LAMD;Lo;0;R;;;;;N;;;;;
+1090C;PHOENICIAN LETTER MEM;Lo;0;R;;;;;N;;;;;
+1090D;PHOENICIAN LETTER NUN;Lo;0;R;;;;;N;;;;;
+1090E;PHOENICIAN LETTER SEMK;Lo;0;R;;;;;N;;;;;
+1090F;PHOENICIAN LETTER AIN;Lo;0;R;;;;;N;;;;;
+10910;PHOENICIAN LETTER PE;Lo;0;R;;;;;N;;;;;
+10911;PHOENICIAN LETTER SADE;Lo;0;R;;;;;N;;;;;
+10912;PHOENICIAN LETTER QOF;Lo;0;R;;;;;N;;;;;
+10913;PHOENICIAN LETTER ROSH;Lo;0;R;;;;;N;;;;;
+10914;PHOENICIAN LETTER SHIN;Lo;0;R;;;;;N;;;;;
+10915;PHOENICIAN LETTER TAU;Lo;0;R;;;;;N;;;;;
+10916;PHOENICIAN NUMBER ONE;No;0;R;;;;1;N;;;;;
+10917;PHOENICIAN NUMBER TEN;No;0;R;;;;10;N;;;;;
+10918;PHOENICIAN NUMBER TWENTY;No;0;R;;;;20;N;;;;;
+10919;PHOENICIAN NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;;
+1091A;PHOENICIAN NUMBER TWO;No;0;R;;;;2;N;;;;;
+1091B;PHOENICIAN NUMBER THREE;No;0;R;;;;3;N;;;;;
+1091F;PHOENICIAN WORD SEPARATOR;Po;0;ON;;;;;N;;;;;
+10920;LYDIAN LETTER A;Lo;0;R;;;;;N;;;;;
+10921;LYDIAN LETTER B;Lo;0;R;;;;;N;;;;;
+10922;LYDIAN LETTER G;Lo;0;R;;;;;N;;;;;
+10923;LYDIAN LETTER D;Lo;0;R;;;;;N;;;;;
+10924;LYDIAN LETTER E;Lo;0;R;;;;;N;;;;;
+10925;LYDIAN LETTER V;Lo;0;R;;;;;N;;;;;
+10926;LYDIAN LETTER I;Lo;0;R;;;;;N;;;;;
+10927;LYDIAN LETTER Y;Lo;0;R;;;;;N;;;;;
+10928;LYDIAN LETTER K;Lo;0;R;;;;;N;;;;;
+10929;LYDIAN LETTER L;Lo;0;R;;;;;N;;;;;
+1092A;LYDIAN LETTER M;Lo;0;R;;;;;N;;;;;
+1092B;LYDIAN LETTER N;Lo;0;R;;;;;N;;;;;
+1092C;LYDIAN LETTER O;Lo;0;R;;;;;N;;;;;
+1092D;LYDIAN LETTER R;Lo;0;R;;;;;N;;;;;
+1092E;LYDIAN LETTER SS;Lo;0;R;;;;;N;;;;;
+1092F;LYDIAN LETTER T;Lo;0;R;;;;;N;;;;;
+10930;LYDIAN LETTER U;Lo;0;R;;;;;N;;;;;
+10931;LYDIAN LETTER F;Lo;0;R;;;;;N;;;;;
+10932;LYDIAN LETTER Q;Lo;0;R;;;;;N;;;;;
+10933;LYDIAN LETTER S;Lo;0;R;;;;;N;;;;;
+10934;LYDIAN LETTER TT;Lo;0;R;;;;;N;;;;;
+10935;LYDIAN LETTER AN;Lo;0;R;;;;;N;;;;;
+10936;LYDIAN LETTER EN;Lo;0;R;;;;;N;;;;;
+10937;LYDIAN LETTER LY;Lo;0;R;;;;;N;;;;;
+10938;LYDIAN LETTER NN;Lo;0;R;;;;;N;;;;;
+10939;LYDIAN LETTER C;Lo;0;R;;;;;N;;;;;
+1093F;LYDIAN TRIANGULAR MARK;Po;0;R;;;;;N;;;;;
+10980;MEROITIC HIEROGLYPHIC LETTER A;Lo;0;R;;;;;N;;;;;
+10981;MEROITIC HIEROGLYPHIC LETTER E;Lo;0;R;;;;;N;;;;;
+10982;MEROITIC HIEROGLYPHIC LETTER I;Lo;0;R;;;;;N;;;;;
+10983;MEROITIC HIEROGLYPHIC LETTER O;Lo;0;R;;;;;N;;;;;
+10984;MEROITIC HIEROGLYPHIC LETTER YA;Lo;0;R;;;;;N;;;;;
+10985;MEROITIC HIEROGLYPHIC LETTER WA;Lo;0;R;;;;;N;;;;;
+10986;MEROITIC HIEROGLYPHIC LETTER BA;Lo;0;R;;;;;N;;;;;
+10987;MEROITIC HIEROGLYPHIC LETTER BA-2;Lo;0;R;;;;;N;;;;;
+10988;MEROITIC HIEROGLYPHIC LETTER PA;Lo;0;R;;;;;N;;;;;
+10989;MEROITIC HIEROGLYPHIC LETTER MA;Lo;0;R;;;;;N;;;;;
+1098A;MEROITIC HIEROGLYPHIC LETTER NA;Lo;0;R;;;;;N;;;;;
+1098B;MEROITIC HIEROGLYPHIC LETTER NA-2;Lo;0;R;;;;;N;;;;;
+1098C;MEROITIC HIEROGLYPHIC LETTER NE;Lo;0;R;;;;;N;;;;;
+1098D;MEROITIC HIEROGLYPHIC LETTER NE-2;Lo;0;R;;;;;N;;;;;
+1098E;MEROITIC HIEROGLYPHIC LETTER RA;Lo;0;R;;;;;N;;;;;
+1098F;MEROITIC HIEROGLYPHIC LETTER RA-2;Lo;0;R;;;;;N;;;;;
+10990;MEROITIC HIEROGLYPHIC LETTER LA;Lo;0;R;;;;;N;;;;;
+10991;MEROITIC HIEROGLYPHIC LETTER KHA;Lo;0;R;;;;;N;;;;;
+10992;MEROITIC HIEROGLYPHIC LETTER HHA;Lo;0;R;;;;;N;;;;;
+10993;MEROITIC HIEROGLYPHIC LETTER SA;Lo;0;R;;;;;N;;;;;
+10994;MEROITIC HIEROGLYPHIC LETTER SA-2;Lo;0;R;;;;;N;;;;;
+10995;MEROITIC HIEROGLYPHIC LETTER SE;Lo;0;R;;;;;N;;;;;
+10996;MEROITIC HIEROGLYPHIC LETTER KA;Lo;0;R;;;;;N;;;;;
+10997;MEROITIC HIEROGLYPHIC LETTER QA;Lo;0;R;;;;;N;;;;;
+10998;MEROITIC HIEROGLYPHIC LETTER TA;Lo;0;R;;;;;N;;;;;
+10999;MEROITIC HIEROGLYPHIC LETTER TA-2;Lo;0;R;;;;;N;;;;;
+1099A;MEROITIC HIEROGLYPHIC LETTER TE;Lo;0;R;;;;;N;;;;;
+1099B;MEROITIC HIEROGLYPHIC LETTER TE-2;Lo;0;R;;;;;N;;;;;
+1099C;MEROITIC HIEROGLYPHIC LETTER TO;Lo;0;R;;;;;N;;;;;
+1099D;MEROITIC HIEROGLYPHIC LETTER DA;Lo;0;R;;;;;N;;;;;
+1099E;MEROITIC HIEROGLYPHIC SYMBOL VIDJ;Lo;0;R;;;;;N;;;;;
+1099F;MEROITIC HIEROGLYPHIC SYMBOL VIDJ-2;Lo;0;R;;;;;N;;;;;
+109A0;MEROITIC CURSIVE LETTER A;Lo;0;R;;;;;N;;;;;
+109A1;MEROITIC CURSIVE LETTER E;Lo;0;R;;;;;N;;;;;
+109A2;MEROITIC CURSIVE LETTER I;Lo;0;R;;;;;N;;;;;
+109A3;MEROITIC CURSIVE LETTER O;Lo;0;R;;;;;N;;;;;
+109A4;MEROITIC CURSIVE LETTER YA;Lo;0;R;;;;;N;;;;;
+109A5;MEROITIC CURSIVE LETTER WA;Lo;0;R;;;;;N;;;;;
+109A6;MEROITIC CURSIVE LETTER BA;Lo;0;R;;;;;N;;;;;
+109A7;MEROITIC CURSIVE LETTER PA;Lo;0;R;;;;;N;;;;;
+109A8;MEROITIC CURSIVE LETTER MA;Lo;0;R;;;;;N;;;;;
+109A9;MEROITIC CURSIVE LETTER NA;Lo;0;R;;;;;N;;;;;
+109AA;MEROITIC CURSIVE LETTER NE;Lo;0;R;;;;;N;;;;;
+109AB;MEROITIC CURSIVE LETTER RA;Lo;0;R;;;;;N;;;;;
+109AC;MEROITIC CURSIVE LETTER LA;Lo;0;R;;;;;N;;;;;
+109AD;MEROITIC CURSIVE LETTER KHA;Lo;0;R;;;;;N;;;;;
+109AE;MEROITIC CURSIVE LETTER HHA;Lo;0;R;;;;;N;;;;;
+109AF;MEROITIC CURSIVE LETTER SA;Lo;0;R;;;;;N;;;;;
+109B0;MEROITIC CURSIVE LETTER ARCHAIC SA;Lo;0;R;;;;;N;;;;;
+109B1;MEROITIC CURSIVE LETTER SE;Lo;0;R;;;;;N;;;;;
+109B2;MEROITIC CURSIVE LETTER KA;Lo;0;R;;;;;N;;;;;
+109B3;MEROITIC CURSIVE LETTER QA;Lo;0;R;;;;;N;;;;;
+109B4;MEROITIC CURSIVE LETTER TA;Lo;0;R;;;;;N;;;;;
+109B5;MEROITIC CURSIVE LETTER TE;Lo;0;R;;;;;N;;;;;
+109B6;MEROITIC CURSIVE LETTER TO;Lo;0;R;;;;;N;;;;;
+109B7;MEROITIC CURSIVE LETTER DA;Lo;0;R;;;;;N;;;;;
+109BC;MEROITIC CURSIVE FRACTION ELEVEN TWELFTHS;No;0;R;;;;11/12;N;;;;;
+109BD;MEROITIC CURSIVE FRACTION ONE HALF;No;0;R;;;;1/2;N;;;;;
+109BE;MEROITIC CURSIVE LOGOGRAM RMT;Lo;0;R;;;;;N;;;;;
+109BF;MEROITIC CURSIVE LOGOGRAM IMN;Lo;0;R;;;;;N;;;;;
+109C0;MEROITIC CURSIVE NUMBER ONE;No;0;R;;;;1;N;;;;;
+109C1;MEROITIC CURSIVE NUMBER TWO;No;0;R;;;;2;N;;;;;
+109C2;MEROITIC CURSIVE NUMBER THREE;No;0;R;;;;3;N;;;;;
+109C3;MEROITIC CURSIVE NUMBER FOUR;No;0;R;;;;4;N;;;;;
+109C4;MEROITIC CURSIVE NUMBER FIVE;No;0;R;;;;5;N;;;;;
+109C5;MEROITIC CURSIVE NUMBER SIX;No;0;R;;;;6;N;;;;;
+109C6;MEROITIC CURSIVE NUMBER SEVEN;No;0;R;;;;7;N;;;;;
+109C7;MEROITIC CURSIVE NUMBER EIGHT;No;0;R;;;;8;N;;;;;
+109C8;MEROITIC CURSIVE NUMBER NINE;No;0;R;;;;9;N;;;;;
+109C9;MEROITIC CURSIVE NUMBER TEN;No;0;R;;;;10;N;;;;;
+109CA;MEROITIC CURSIVE NUMBER TWENTY;No;0;R;;;;20;N;;;;;
+109CB;MEROITIC CURSIVE NUMBER THIRTY;No;0;R;;;;30;N;;;;;
+109CC;MEROITIC CURSIVE NUMBER FORTY;No;0;R;;;;40;N;;;;;
+109CD;MEROITIC CURSIVE NUMBER FIFTY;No;0;R;;;;50;N;;;;;
+109CE;MEROITIC CURSIVE NUMBER SIXTY;No;0;R;;;;60;N;;;;;
+109CF;MEROITIC CURSIVE NUMBER SEVENTY;No;0;R;;;;70;N;;;;;
+109D2;MEROITIC CURSIVE NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;;
+109D3;MEROITIC CURSIVE NUMBER TWO HUNDRED;No;0;R;;;;200;N;;;;;
+109D4;MEROITIC CURSIVE NUMBER THREE HUNDRED;No;0;R;;;;300;N;;;;;
+109D5;MEROITIC CURSIVE NUMBER FOUR HUNDRED;No;0;R;;;;400;N;;;;;
+109D6;MEROITIC CURSIVE NUMBER FIVE HUNDRED;No;0;R;;;;500;N;;;;;
+109D7;MEROITIC CURSIVE NUMBER SIX HUNDRED;No;0;R;;;;600;N;;;;;
+109D8;MEROITIC CURSIVE NUMBER SEVEN HUNDRED;No;0;R;;;;700;N;;;;;
+109D9;MEROITIC CURSIVE NUMBER EIGHT HUNDRED;No;0;R;;;;800;N;;;;;
+109DA;MEROITIC CURSIVE NUMBER NINE HUNDRED;No;0;R;;;;900;N;;;;;
+109DB;MEROITIC CURSIVE NUMBER ONE THOUSAND;No;0;R;;;;1000;N;;;;;
+109DC;MEROITIC CURSIVE NUMBER TWO THOUSAND;No;0;R;;;;2000;N;;;;;
+109DD;MEROITIC CURSIVE NUMBER THREE THOUSAND;No;0;R;;;;3000;N;;;;;
+109DE;MEROITIC CURSIVE NUMBER FOUR THOUSAND;No;0;R;;;;4000;N;;;;;
+109DF;MEROITIC CURSIVE NUMBER FIVE THOUSAND;No;0;R;;;;5000;N;;;;;
+109E0;MEROITIC CURSIVE NUMBER SIX THOUSAND;No;0;R;;;;6000;N;;;;;
+109E1;MEROITIC CURSIVE NUMBER SEVEN THOUSAND;No;0;R;;;;7000;N;;;;;
+109E2;MEROITIC CURSIVE NUMBER EIGHT THOUSAND;No;0;R;;;;8000;N;;;;;
+109E3;MEROITIC CURSIVE NUMBER NINE THOUSAND;No;0;R;;;;9000;N;;;;;
+109E4;MEROITIC CURSIVE NUMBER TEN THOUSAND;No;0;R;;;;10000;N;;;;;
+109E5;MEROITIC CURSIVE NUMBER TWENTY THOUSAND;No;0;R;;;;20000;N;;;;;
+109E6;MEROITIC CURSIVE NUMBER THIRTY THOUSAND;No;0;R;;;;30000;N;;;;;
+109E7;MEROITIC CURSIVE NUMBER FORTY THOUSAND;No;0;R;;;;40000;N;;;;;
+109E8;MEROITIC CURSIVE NUMBER FIFTY THOUSAND;No;0;R;;;;50000;N;;;;;
+109E9;MEROITIC CURSIVE NUMBER SIXTY THOUSAND;No;0;R;;;;60000;N;;;;;
+109EA;MEROITIC CURSIVE NUMBER SEVENTY THOUSAND;No;0;R;;;;70000;N;;;;;
+109EB;MEROITIC CURSIVE NUMBER EIGHTY THOUSAND;No;0;R;;;;80000;N;;;;;
+109EC;MEROITIC CURSIVE NUMBER NINETY THOUSAND;No;0;R;;;;90000;N;;;;;
+109ED;MEROITIC CURSIVE NUMBER ONE HUNDRED THOUSAND;No;0;R;;;;100000;N;;;;;
+109EE;MEROITIC CURSIVE NUMBER TWO HUNDRED THOUSAND;No;0;R;;;;200000;N;;;;;
+109EF;MEROITIC CURSIVE NUMBER THREE HUNDRED THOUSAND;No;0;R;;;;300000;N;;;;;
+109F0;MEROITIC CURSIVE NUMBER FOUR HUNDRED THOUSAND;No;0;R;;;;400000;N;;;;;
+109F1;MEROITIC CURSIVE NUMBER FIVE HUNDRED THOUSAND;No;0;R;;;;500000;N;;;;;
+109F2;MEROITIC CURSIVE NUMBER SIX HUNDRED THOUSAND;No;0;R;;;;600000;N;;;;;
+109F3;MEROITIC CURSIVE NUMBER SEVEN HUNDRED THOUSAND;No;0;R;;;;700000;N;;;;;
+109F4;MEROITIC CURSIVE NUMBER EIGHT HUNDRED THOUSAND;No;0;R;;;;800000;N;;;;;
+109F5;MEROITIC CURSIVE NUMBER NINE HUNDRED THOUSAND;No;0;R;;;;900000;N;;;;;
+109F6;MEROITIC CURSIVE FRACTION ONE TWELFTH;No;0;R;;;;1/12;N;;;;;
+109F7;MEROITIC CURSIVE FRACTION TWO TWELFTHS;No;0;R;;;;2/12;N;;;;;
+109F8;MEROITIC CURSIVE FRACTION THREE TWELFTHS;No;0;R;;;;3/12;N;;;;;
+109F9;MEROITIC CURSIVE FRACTION FOUR TWELFTHS;No;0;R;;;;4/12;N;;;;;
+109FA;MEROITIC CURSIVE FRACTION FIVE TWELFTHS;No;0;R;;;;5/12;N;;;;;
+109FB;MEROITIC CURSIVE FRACTION SIX TWELFTHS;No;0;R;;;;6/12;N;;;;;
+109FC;MEROITIC CURSIVE FRACTION SEVEN TWELFTHS;No;0;R;;;;7/12;N;;;;;
+109FD;MEROITIC CURSIVE FRACTION EIGHT TWELFTHS;No;0;R;;;;8/12;N;;;;;
+109FE;MEROITIC CURSIVE FRACTION NINE TWELFTHS;No;0;R;;;;9/12;N;;;;;
+109FF;MEROITIC CURSIVE FRACTION TEN TWELFTHS;No;0;R;;;;10/12;N;;;;;
+10A00;KHAROSHTHI LETTER A;Lo;0;R;;;;;N;;;;;
+10A01;KHAROSHTHI VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+10A02;KHAROSHTHI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+10A03;KHAROSHTHI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+10A05;KHAROSHTHI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+10A06;KHAROSHTHI VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+10A0C;KHAROSHTHI VOWEL LENGTH MARK;Mn;0;NSM;;;;;N;;;;;
+10A0D;KHAROSHTHI SIGN DOUBLE RING BELOW;Mn;220;NSM;;;;;N;;;;;
+10A0E;KHAROSHTHI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+10A0F;KHAROSHTHI SIGN VISARGA;Mn;230;NSM;;;;;N;;;;;
+10A10;KHAROSHTHI LETTER KA;Lo;0;R;;;;;N;;;;;
+10A11;KHAROSHTHI LETTER KHA;Lo;0;R;;;;;N;;;;;
+10A12;KHAROSHTHI LETTER GA;Lo;0;R;;;;;N;;;;;
+10A13;KHAROSHTHI LETTER GHA;Lo;0;R;;;;;N;;;;;
+10A15;KHAROSHTHI LETTER CA;Lo;0;R;;;;;N;;;;;
+10A16;KHAROSHTHI LETTER CHA;Lo;0;R;;;;;N;;;;;
+10A17;KHAROSHTHI LETTER JA;Lo;0;R;;;;;N;;;;;
+10A19;KHAROSHTHI LETTER NYA;Lo;0;R;;;;;N;;;;;
+10A1A;KHAROSHTHI LETTER TTA;Lo;0;R;;;;;N;;;;;
+10A1B;KHAROSHTHI LETTER TTHA;Lo;0;R;;;;;N;;;;;
+10A1C;KHAROSHTHI LETTER DDA;Lo;0;R;;;;;N;;;;;
+10A1D;KHAROSHTHI LETTER DDHA;Lo;0;R;;;;;N;;;;;
+10A1E;KHAROSHTHI LETTER NNA;Lo;0;R;;;;;N;;;;;
+10A1F;KHAROSHTHI LETTER TA;Lo;0;R;;;;;N;;;;;
+10A20;KHAROSHTHI LETTER THA;Lo;0;R;;;;;N;;;;;
+10A21;KHAROSHTHI LETTER DA;Lo;0;R;;;;;N;;;;;
+10A22;KHAROSHTHI LETTER DHA;Lo;0;R;;;;;N;;;;;
+10A23;KHAROSHTHI LETTER NA;Lo;0;R;;;;;N;;;;;
+10A24;KHAROSHTHI LETTER PA;Lo;0;R;;;;;N;;;;;
+10A25;KHAROSHTHI LETTER PHA;Lo;0;R;;;;;N;;;;;
+10A26;KHAROSHTHI LETTER BA;Lo;0;R;;;;;N;;;;;
+10A27;KHAROSHTHI LETTER BHA;Lo;0;R;;;;;N;;;;;
+10A28;KHAROSHTHI LETTER MA;Lo;0;R;;;;;N;;;;;
+10A29;KHAROSHTHI LETTER YA;Lo;0;R;;;;;N;;;;;
+10A2A;KHAROSHTHI LETTER RA;Lo;0;R;;;;;N;;;;;
+10A2B;KHAROSHTHI LETTER LA;Lo;0;R;;;;;N;;;;;
+10A2C;KHAROSHTHI LETTER VA;Lo;0;R;;;;;N;;;;;
+10A2D;KHAROSHTHI LETTER SHA;Lo;0;R;;;;;N;;;;;
+10A2E;KHAROSHTHI LETTER SSA;Lo;0;R;;;;;N;;;;;
+10A2F;KHAROSHTHI LETTER SA;Lo;0;R;;;;;N;;;;;
+10A30;KHAROSHTHI LETTER ZA;Lo;0;R;;;;;N;;;;;
+10A31;KHAROSHTHI LETTER HA;Lo;0;R;;;;;N;;;;;
+10A32;KHAROSHTHI LETTER KKA;Lo;0;R;;;;;N;;;;;
+10A33;KHAROSHTHI LETTER TTTHA;Lo;0;R;;;;;N;;;;;
+10A34;KHAROSHTHI LETTER TTTA;Lo;0;R;;;;;N;;;;;
+10A35;KHAROSHTHI LETTER VHA;Lo;0;R;;;;;N;;;;;
+10A38;KHAROSHTHI SIGN BAR ABOVE;Mn;230;NSM;;;;;N;;;;;
+10A39;KHAROSHTHI SIGN CAUDA;Mn;1;NSM;;;;;N;;;;;
+10A3A;KHAROSHTHI SIGN DOT BELOW;Mn;220;NSM;;;;;N;;;;;
+10A3F;KHAROSHTHI VIRAMA;Mn;9;NSM;;;;;N;;;;;
+10A40;KHAROSHTHI DIGIT ONE;No;0;R;;;1;1;N;;;;;
+10A41;KHAROSHTHI DIGIT TWO;No;0;R;;;2;2;N;;;;;
+10A42;KHAROSHTHI DIGIT THREE;No;0;R;;;3;3;N;;;;;
+10A43;KHAROSHTHI DIGIT FOUR;No;0;R;;;4;4;N;;;;;
+10A44;KHAROSHTHI NUMBER TEN;No;0;R;;;;10;N;;;;;
+10A45;KHAROSHTHI NUMBER TWENTY;No;0;R;;;;20;N;;;;;
+10A46;KHAROSHTHI NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;;
+10A47;KHAROSHTHI NUMBER ONE THOUSAND;No;0;R;;;;1000;N;;;;;
+10A48;KHAROSHTHI FRACTION ONE HALF;No;0;R;;;;1/2;N;;;;;
+10A50;KHAROSHTHI PUNCTUATION DOT;Po;0;R;;;;;N;;;;;
+10A51;KHAROSHTHI PUNCTUATION SMALL CIRCLE;Po;0;R;;;;;N;;;;;
+10A52;KHAROSHTHI PUNCTUATION CIRCLE;Po;0;R;;;;;N;;;;;
+10A53;KHAROSHTHI PUNCTUATION CRESCENT BAR;Po;0;R;;;;;N;;;;;
+10A54;KHAROSHTHI PUNCTUATION MANGALAM;Po;0;R;;;;;N;;;;;
+10A55;KHAROSHTHI PUNCTUATION LOTUS;Po;0;R;;;;;N;;;;;
+10A56;KHAROSHTHI PUNCTUATION DANDA;Po;0;R;;;;;N;;;;;
+10A57;KHAROSHTHI PUNCTUATION DOUBLE DANDA;Po;0;R;;;;;N;;;;;
+10A58;KHAROSHTHI PUNCTUATION LINES;Po;0;R;;;;;N;;;;;
+10A60;OLD SOUTH ARABIAN LETTER HE;Lo;0;R;;;;;N;;;;;
+10A61;OLD SOUTH ARABIAN LETTER LAMEDH;Lo;0;R;;;;;N;;;;;
+10A62;OLD SOUTH ARABIAN LETTER HETH;Lo;0;R;;;;;N;;;;;
+10A63;OLD SOUTH ARABIAN LETTER MEM;Lo;0;R;;;;;N;;;;;
+10A64;OLD SOUTH ARABIAN LETTER QOPH;Lo;0;R;;;;;N;;;;;
+10A65;OLD SOUTH ARABIAN LETTER WAW;Lo;0;R;;;;;N;;;;;
+10A66;OLD SOUTH ARABIAN LETTER SHIN;Lo;0;R;;;;;N;;;;;
+10A67;OLD SOUTH ARABIAN LETTER RESH;Lo;0;R;;;;;N;;;;;
+10A68;OLD SOUTH ARABIAN LETTER BETH;Lo;0;R;;;;;N;;;;;
+10A69;OLD SOUTH ARABIAN LETTER TAW;Lo;0;R;;;;;N;;;;;
+10A6A;OLD SOUTH ARABIAN LETTER SAT;Lo;0;R;;;;;N;;;;;
+10A6B;OLD SOUTH ARABIAN LETTER KAPH;Lo;0;R;;;;;N;;;;;
+10A6C;OLD SOUTH ARABIAN LETTER NUN;Lo;0;R;;;;;N;;;;;
+10A6D;OLD SOUTH ARABIAN LETTER KHETH;Lo;0;R;;;;;N;;;;;
+10A6E;OLD SOUTH ARABIAN LETTER SADHE;Lo;0;R;;;;;N;;;;;
+10A6F;OLD SOUTH ARABIAN LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+10A70;OLD SOUTH ARABIAN LETTER FE;Lo;0;R;;;;;N;;;;;
+10A71;OLD SOUTH ARABIAN LETTER ALEF;Lo;0;R;;;;;N;;;;;
+10A72;OLD SOUTH ARABIAN LETTER AYN;Lo;0;R;;;;;N;;;;;
+10A73;OLD SOUTH ARABIAN LETTER DHADHE;Lo;0;R;;;;;N;;;;;
+10A74;OLD SOUTH ARABIAN LETTER GIMEL;Lo;0;R;;;;;N;;;;;
+10A75;OLD SOUTH ARABIAN LETTER DALETH;Lo;0;R;;;;;N;;;;;
+10A76;OLD SOUTH ARABIAN LETTER GHAYN;Lo;0;R;;;;;N;;;;;
+10A77;OLD SOUTH ARABIAN LETTER TETH;Lo;0;R;;;;;N;;;;;
+10A78;OLD SOUTH ARABIAN LETTER ZAYN;Lo;0;R;;;;;N;;;;;
+10A79;OLD SOUTH ARABIAN LETTER DHALETH;Lo;0;R;;;;;N;;;;;
+10A7A;OLD SOUTH ARABIAN LETTER YODH;Lo;0;R;;;;;N;;;;;
+10A7B;OLD SOUTH ARABIAN LETTER THAW;Lo;0;R;;;;;N;;;;;
+10A7C;OLD SOUTH ARABIAN LETTER THETH;Lo;0;R;;;;;N;;;;;
+10A7D;OLD SOUTH ARABIAN NUMBER ONE;No;0;R;;;;1;N;;;;;
+10A7E;OLD SOUTH ARABIAN NUMBER FIFTY;No;0;R;;;;50;N;;;;;
+10A7F;OLD SOUTH ARABIAN NUMERIC INDICATOR;Po;0;R;;;;;N;;;;;
+10A80;OLD NORTH ARABIAN LETTER HEH;Lo;0;R;;;;;N;;;;;
+10A81;OLD NORTH ARABIAN LETTER LAM;Lo;0;R;;;;;N;;;;;
+10A82;OLD NORTH ARABIAN LETTER HAH;Lo;0;R;;;;;N;;;;;
+10A83;OLD NORTH ARABIAN LETTER MEEM;Lo;0;R;;;;;N;;;;;
+10A84;OLD NORTH ARABIAN LETTER QAF;Lo;0;R;;;;;N;;;;;
+10A85;OLD NORTH ARABIAN LETTER WAW;Lo;0;R;;;;;N;;;;;
+10A86;OLD NORTH ARABIAN LETTER ES-2;Lo;0;R;;;;;N;;;;;
+10A87;OLD NORTH ARABIAN LETTER REH;Lo;0;R;;;;;N;;;;;
+10A88;OLD NORTH ARABIAN LETTER BEH;Lo;0;R;;;;;N;;;;;
+10A89;OLD NORTH ARABIAN LETTER TEH;Lo;0;R;;;;;N;;;;;
+10A8A;OLD NORTH ARABIAN LETTER ES-1;Lo;0;R;;;;;N;;;;;
+10A8B;OLD NORTH ARABIAN LETTER KAF;Lo;0;R;;;;;N;;;;;
+10A8C;OLD NORTH ARABIAN LETTER NOON;Lo;0;R;;;;;N;;;;;
+10A8D;OLD NORTH ARABIAN LETTER KHAH;Lo;0;R;;;;;N;;;;;
+10A8E;OLD NORTH ARABIAN LETTER SAD;Lo;0;R;;;;;N;;;;;
+10A8F;OLD NORTH ARABIAN LETTER ES-3;Lo;0;R;;;;;N;;;;;
+10A90;OLD NORTH ARABIAN LETTER FEH;Lo;0;R;;;;;N;;;;;
+10A91;OLD NORTH ARABIAN LETTER ALEF;Lo;0;R;;;;;N;;;;;
+10A92;OLD NORTH ARABIAN LETTER AIN;Lo;0;R;;;;;N;;;;;
+10A93;OLD NORTH ARABIAN LETTER DAD;Lo;0;R;;;;;N;;;;;
+10A94;OLD NORTH ARABIAN LETTER GEEM;Lo;0;R;;;;;N;;;;;
+10A95;OLD NORTH ARABIAN LETTER DAL;Lo;0;R;;;;;N;;;;;
+10A96;OLD NORTH ARABIAN LETTER GHAIN;Lo;0;R;;;;;N;;;;;
+10A97;OLD NORTH ARABIAN LETTER TAH;Lo;0;R;;;;;N;;;;;
+10A98;OLD NORTH ARABIAN LETTER ZAIN;Lo;0;R;;;;;N;;;;;
+10A99;OLD NORTH ARABIAN LETTER THAL;Lo;0;R;;;;;N;;;;;
+10A9A;OLD NORTH ARABIAN LETTER YEH;Lo;0;R;;;;;N;;;;;
+10A9B;OLD NORTH ARABIAN LETTER THEH;Lo;0;R;;;;;N;;;;;
+10A9C;OLD NORTH ARABIAN LETTER ZAH;Lo;0;R;;;;;N;;;;;
+10A9D;OLD NORTH ARABIAN NUMBER ONE;No;0;R;;;;1;N;;;;;
+10A9E;OLD NORTH ARABIAN NUMBER TEN;No;0;R;;;;10;N;;;;;
+10A9F;OLD NORTH ARABIAN NUMBER TWENTY;No;0;R;;;;20;N;;;;;
+10AC0;MANICHAEAN LETTER ALEPH;Lo;0;R;;;;;N;;;;;
+10AC1;MANICHAEAN LETTER BETH;Lo;0;R;;;;;N;;;;;
+10AC2;MANICHAEAN LETTER BHETH;Lo;0;R;;;;;N;;;;;
+10AC3;MANICHAEAN LETTER GIMEL;Lo;0;R;;;;;N;;;;;
+10AC4;MANICHAEAN LETTER GHIMEL;Lo;0;R;;;;;N;;;;;
+10AC5;MANICHAEAN LETTER DALETH;Lo;0;R;;;;;N;;;;;
+10AC6;MANICHAEAN LETTER HE;Lo;0;R;;;;;N;;;;;
+10AC7;MANICHAEAN LETTER WAW;Lo;0;R;;;;;N;;;;;
+10AC8;MANICHAEAN SIGN UD;So;0;R;;;;;N;;;;;
+10AC9;MANICHAEAN LETTER ZAYIN;Lo;0;R;;;;;N;;;;;
+10ACA;MANICHAEAN LETTER ZHAYIN;Lo;0;R;;;;;N;;;;;
+10ACB;MANICHAEAN LETTER JAYIN;Lo;0;R;;;;;N;;;;;
+10ACC;MANICHAEAN LETTER JHAYIN;Lo;0;R;;;;;N;;;;;
+10ACD;MANICHAEAN LETTER HETH;Lo;0;R;;;;;N;;;;;
+10ACE;MANICHAEAN LETTER TETH;Lo;0;R;;;;;N;;;;;
+10ACF;MANICHAEAN LETTER YODH;Lo;0;R;;;;;N;;;;;
+10AD0;MANICHAEAN LETTER KAPH;Lo;0;R;;;;;N;;;;;
+10AD1;MANICHAEAN LETTER XAPH;Lo;0;R;;;;;N;;;;;
+10AD2;MANICHAEAN LETTER KHAPH;Lo;0;R;;;;;N;;;;;
+10AD3;MANICHAEAN LETTER LAMEDH;Lo;0;R;;;;;N;;;;;
+10AD4;MANICHAEAN LETTER DHAMEDH;Lo;0;R;;;;;N;;;;;
+10AD5;MANICHAEAN LETTER THAMEDH;Lo;0;R;;;;;N;;;;;
+10AD6;MANICHAEAN LETTER MEM;Lo;0;R;;;;;N;;;;;
+10AD7;MANICHAEAN LETTER NUN;Lo;0;R;;;;;N;;;;;
+10AD8;MANICHAEAN LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+10AD9;MANICHAEAN LETTER AYIN;Lo;0;R;;;;;N;;;;;
+10ADA;MANICHAEAN LETTER AAYIN;Lo;0;R;;;;;N;;;;;
+10ADB;MANICHAEAN LETTER PE;Lo;0;R;;;;;N;;;;;
+10ADC;MANICHAEAN LETTER FE;Lo;0;R;;;;;N;;;;;
+10ADD;MANICHAEAN LETTER SADHE;Lo;0;R;;;;;N;;;;;
+10ADE;MANICHAEAN LETTER QOPH;Lo;0;R;;;;;N;;;;;
+10ADF;MANICHAEAN LETTER XOPH;Lo;0;R;;;;;N;;;;;
+10AE0;MANICHAEAN LETTER QHOPH;Lo;0;R;;;;;N;;;;;
+10AE1;MANICHAEAN LETTER RESH;Lo;0;R;;;;;N;;;;;
+10AE2;MANICHAEAN LETTER SHIN;Lo;0;R;;;;;N;;;;;
+10AE3;MANICHAEAN LETTER SSHIN;Lo;0;R;;;;;N;;;;;
+10AE4;MANICHAEAN LETTER TAW;Lo;0;R;;;;;N;;;;;
+10AE5;MANICHAEAN ABBREVIATION MARK ABOVE;Mn;230;NSM;;;;;N;;;;;
+10AE6;MANICHAEAN ABBREVIATION MARK BELOW;Mn;220;NSM;;;;;N;;;;;
+10AEB;MANICHAEAN NUMBER ONE;No;0;R;;;;1;N;;;;;
+10AEC;MANICHAEAN NUMBER FIVE;No;0;R;;;;5;N;;;;;
+10AED;MANICHAEAN NUMBER TEN;No;0;R;;;;10;N;;;;;
+10AEE;MANICHAEAN NUMBER TWENTY;No;0;R;;;;20;N;;;;;
+10AEF;MANICHAEAN NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;;
+10AF0;MANICHAEAN PUNCTUATION STAR;Po;0;R;;;;;N;;;;;
+10AF1;MANICHAEAN PUNCTUATION FLEURON;Po;0;R;;;;;N;;;;;
+10AF2;MANICHAEAN PUNCTUATION DOUBLE DOT WITHIN DOT;Po;0;R;;;;;N;;;;;
+10AF3;MANICHAEAN PUNCTUATION DOT WITHIN DOT;Po;0;R;;;;;N;;;;;
+10AF4;MANICHAEAN PUNCTUATION DOT;Po;0;R;;;;;N;;;;;
+10AF5;MANICHAEAN PUNCTUATION TWO DOTS;Po;0;R;;;;;N;;;;;
+10AF6;MANICHAEAN PUNCTUATION LINE FILLER;Po;0;R;;;;;N;;;;;
+10B00;AVESTAN LETTER A;Lo;0;R;;;;;N;;;;;
+10B01;AVESTAN LETTER AA;Lo;0;R;;;;;N;;;;;
+10B02;AVESTAN LETTER AO;Lo;0;R;;;;;N;;;;;
+10B03;AVESTAN LETTER AAO;Lo;0;R;;;;;N;;;;;
+10B04;AVESTAN LETTER AN;Lo;0;R;;;;;N;;;;;
+10B05;AVESTAN LETTER AAN;Lo;0;R;;;;;N;;;;;
+10B06;AVESTAN LETTER AE;Lo;0;R;;;;;N;;;;;
+10B07;AVESTAN LETTER AEE;Lo;0;R;;;;;N;;;;;
+10B08;AVESTAN LETTER E;Lo;0;R;;;;;N;;;;;
+10B09;AVESTAN LETTER EE;Lo;0;R;;;;;N;;;;;
+10B0A;AVESTAN LETTER O;Lo;0;R;;;;;N;;;;;
+10B0B;AVESTAN LETTER OO;Lo;0;R;;;;;N;;;;;
+10B0C;AVESTAN LETTER I;Lo;0;R;;;;;N;;;;;
+10B0D;AVESTAN LETTER II;Lo;0;R;;;;;N;;;;;
+10B0E;AVESTAN LETTER U;Lo;0;R;;;;;N;;;;;
+10B0F;AVESTAN LETTER UU;Lo;0;R;;;;;N;;;;;
+10B10;AVESTAN LETTER KE;Lo;0;R;;;;;N;;;;;
+10B11;AVESTAN LETTER XE;Lo;0;R;;;;;N;;;;;
+10B12;AVESTAN LETTER XYE;Lo;0;R;;;;;N;;;;;
+10B13;AVESTAN LETTER XVE;Lo;0;R;;;;;N;;;;;
+10B14;AVESTAN LETTER GE;Lo;0;R;;;;;N;;;;;
+10B15;AVESTAN LETTER GGE;Lo;0;R;;;;;N;;;;;
+10B16;AVESTAN LETTER GHE;Lo;0;R;;;;;N;;;;;
+10B17;AVESTAN LETTER CE;Lo;0;R;;;;;N;;;;;
+10B18;AVESTAN LETTER JE;Lo;0;R;;;;;N;;;;;
+10B19;AVESTAN LETTER TE;Lo;0;R;;;;;N;;;;;
+10B1A;AVESTAN LETTER THE;Lo;0;R;;;;;N;;;;;
+10B1B;AVESTAN LETTER DE;Lo;0;R;;;;;N;;;;;
+10B1C;AVESTAN LETTER DHE;Lo;0;R;;;;;N;;;;;
+10B1D;AVESTAN LETTER TTE;Lo;0;R;;;;;N;;;;;
+10B1E;AVESTAN LETTER PE;Lo;0;R;;;;;N;;;;;
+10B1F;AVESTAN LETTER FE;Lo;0;R;;;;;N;;;;;
+10B20;AVESTAN LETTER BE;Lo;0;R;;;;;N;;;;;
+10B21;AVESTAN LETTER BHE;Lo;0;R;;;;;N;;;;;
+10B22;AVESTAN LETTER NGE;Lo;0;R;;;;;N;;;;;
+10B23;AVESTAN LETTER NGYE;Lo;0;R;;;;;N;;;;;
+10B24;AVESTAN LETTER NGVE;Lo;0;R;;;;;N;;;;;
+10B25;AVESTAN LETTER NE;Lo;0;R;;;;;N;;;;;
+10B26;AVESTAN LETTER NYE;Lo;0;R;;;;;N;;;;;
+10B27;AVESTAN LETTER NNE;Lo;0;R;;;;;N;;;;;
+10B28;AVESTAN LETTER ME;Lo;0;R;;;;;N;;;;;
+10B29;AVESTAN LETTER HME;Lo;0;R;;;;;N;;;;;
+10B2A;AVESTAN LETTER YYE;Lo;0;R;;;;;N;;;;;
+10B2B;AVESTAN LETTER YE;Lo;0;R;;;;;N;;;;;
+10B2C;AVESTAN LETTER VE;Lo;0;R;;;;;N;;;;;
+10B2D;AVESTAN LETTER RE;Lo;0;R;;;;;N;;;;;
+10B2E;AVESTAN LETTER LE;Lo;0;R;;;;;N;;;;;
+10B2F;AVESTAN LETTER SE;Lo;0;R;;;;;N;;;;;
+10B30;AVESTAN LETTER ZE;Lo;0;R;;;;;N;;;;;
+10B31;AVESTAN LETTER SHE;Lo;0;R;;;;;N;;;;;
+10B32;AVESTAN LETTER ZHE;Lo;0;R;;;;;N;;;;;
+10B33;AVESTAN LETTER SHYE;Lo;0;R;;;;;N;;;;;
+10B34;AVESTAN LETTER SSHE;Lo;0;R;;;;;N;;;;;
+10B35;AVESTAN LETTER HE;Lo;0;R;;;;;N;;;;;
+10B39;AVESTAN ABBREVIATION MARK;Po;0;ON;;;;;N;;;;;
+10B3A;TINY TWO DOTS OVER ONE DOT PUNCTUATION;Po;0;ON;;;;;N;;;;;
+10B3B;SMALL TWO DOTS OVER ONE DOT PUNCTUATION;Po;0;ON;;;;;N;;;;;
+10B3C;LARGE TWO DOTS OVER ONE DOT PUNCTUATION;Po;0;ON;;;;;N;;;;;
+10B3D;LARGE ONE DOT OVER TWO DOTS PUNCTUATION;Po;0;ON;;;;;N;;;;;
+10B3E;LARGE TWO RINGS OVER ONE RING PUNCTUATION;Po;0;ON;;;;;N;;;;;
+10B3F;LARGE ONE RING OVER TWO RINGS PUNCTUATION;Po;0;ON;;;;;N;;;;;
+10B40;INSCRIPTIONAL PARTHIAN LETTER ALEPH;Lo;0;R;;;;;N;;;;;
+10B41;INSCRIPTIONAL PARTHIAN LETTER BETH;Lo;0;R;;;;;N;;;;;
+10B42;INSCRIPTIONAL PARTHIAN LETTER GIMEL;Lo;0;R;;;;;N;;;;;
+10B43;INSCRIPTIONAL PARTHIAN LETTER DALETH;Lo;0;R;;;;;N;;;;;
+10B44;INSCRIPTIONAL PARTHIAN LETTER HE;Lo;0;R;;;;;N;;;;;
+10B45;INSCRIPTIONAL PARTHIAN LETTER WAW;Lo;0;R;;;;;N;;;;;
+10B46;INSCRIPTIONAL PARTHIAN LETTER ZAYIN;Lo;0;R;;;;;N;;;;;
+10B47;INSCRIPTIONAL PARTHIAN LETTER HETH;Lo;0;R;;;;;N;;;;;
+10B48;INSCRIPTIONAL PARTHIAN LETTER TETH;Lo;0;R;;;;;N;;;;;
+10B49;INSCRIPTIONAL PARTHIAN LETTER YODH;Lo;0;R;;;;;N;;;;;
+10B4A;INSCRIPTIONAL PARTHIAN LETTER KAPH;Lo;0;R;;;;;N;;;;;
+10B4B;INSCRIPTIONAL PARTHIAN LETTER LAMEDH;Lo;0;R;;;;;N;;;;;
+10B4C;INSCRIPTIONAL PARTHIAN LETTER MEM;Lo;0;R;;;;;N;;;;;
+10B4D;INSCRIPTIONAL PARTHIAN LETTER NUN;Lo;0;R;;;;;N;;;;;
+10B4E;INSCRIPTIONAL PARTHIAN LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+10B4F;INSCRIPTIONAL PARTHIAN LETTER AYIN;Lo;0;R;;;;;N;;;;;
+10B50;INSCRIPTIONAL PARTHIAN LETTER PE;Lo;0;R;;;;;N;;;;;
+10B51;INSCRIPTIONAL PARTHIAN LETTER SADHE;Lo;0;R;;;;;N;;;;;
+10B52;INSCRIPTIONAL PARTHIAN LETTER QOPH;Lo;0;R;;;;;N;;;;;
+10B53;INSCRIPTIONAL PARTHIAN LETTER RESH;Lo;0;R;;;;;N;;;;;
+10B54;INSCRIPTIONAL PARTHIAN LETTER SHIN;Lo;0;R;;;;;N;;;;;
+10B55;INSCRIPTIONAL PARTHIAN LETTER TAW;Lo;0;R;;;;;N;;;;;
+10B58;INSCRIPTIONAL PARTHIAN NUMBER ONE;No;0;R;;;;1;N;;;;;
+10B59;INSCRIPTIONAL PARTHIAN NUMBER TWO;No;0;R;;;;2;N;;;;;
+10B5A;INSCRIPTIONAL PARTHIAN NUMBER THREE;No;0;R;;;;3;N;;;;;
+10B5B;INSCRIPTIONAL PARTHIAN NUMBER FOUR;No;0;R;;;;4;N;;;;;
+10B5C;INSCRIPTIONAL PARTHIAN NUMBER TEN;No;0;R;;;;10;N;;;;;
+10B5D;INSCRIPTIONAL PARTHIAN NUMBER TWENTY;No;0;R;;;;20;N;;;;;
+10B5E;INSCRIPTIONAL PARTHIAN NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;;
+10B5F;INSCRIPTIONAL PARTHIAN NUMBER ONE THOUSAND;No;0;R;;;;1000;N;;;;;
+10B60;INSCRIPTIONAL PAHLAVI LETTER ALEPH;Lo;0;R;;;;;N;;;;;
+10B61;INSCRIPTIONAL PAHLAVI LETTER BETH;Lo;0;R;;;;;N;;;;;
+10B62;INSCRIPTIONAL PAHLAVI LETTER GIMEL;Lo;0;R;;;;;N;;;;;
+10B63;INSCRIPTIONAL PAHLAVI LETTER DALETH;Lo;0;R;;;;;N;;;;;
+10B64;INSCRIPTIONAL PAHLAVI LETTER HE;Lo;0;R;;;;;N;;;;;
+10B65;INSCRIPTIONAL PAHLAVI LETTER WAW-AYIN-RESH;Lo;0;R;;;;;N;;;;;
+10B66;INSCRIPTIONAL PAHLAVI LETTER ZAYIN;Lo;0;R;;;;;N;;;;;
+10B67;INSCRIPTIONAL PAHLAVI LETTER HETH;Lo;0;R;;;;;N;;;;;
+10B68;INSCRIPTIONAL PAHLAVI LETTER TETH;Lo;0;R;;;;;N;;;;;
+10B69;INSCRIPTIONAL PAHLAVI LETTER YODH;Lo;0;R;;;;;N;;;;;
+10B6A;INSCRIPTIONAL PAHLAVI LETTER KAPH;Lo;0;R;;;;;N;;;;;
+10B6B;INSCRIPTIONAL PAHLAVI LETTER LAMEDH;Lo;0;R;;;;;N;;;;;
+10B6C;INSCRIPTIONAL PAHLAVI LETTER MEM-QOPH;Lo;0;R;;;;;N;;;;;
+10B6D;INSCRIPTIONAL PAHLAVI LETTER NUN;Lo;0;R;;;;;N;;;;;
+10B6E;INSCRIPTIONAL PAHLAVI LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+10B6F;INSCRIPTIONAL PAHLAVI LETTER PE;Lo;0;R;;;;;N;;;;;
+10B70;INSCRIPTIONAL PAHLAVI LETTER SADHE;Lo;0;R;;;;;N;;;;;
+10B71;INSCRIPTIONAL PAHLAVI LETTER SHIN;Lo;0;R;;;;;N;;;;;
+10B72;INSCRIPTIONAL PAHLAVI LETTER TAW;Lo;0;R;;;;;N;;;;;
+10B78;INSCRIPTIONAL PAHLAVI NUMBER ONE;No;0;R;;;;1;N;;;;;
+10B79;INSCRIPTIONAL PAHLAVI NUMBER TWO;No;0;R;;;;2;N;;;;;
+10B7A;INSCRIPTIONAL PAHLAVI NUMBER THREE;No;0;R;;;;3;N;;;;;
+10B7B;INSCRIPTIONAL PAHLAVI NUMBER FOUR;No;0;R;;;;4;N;;;;;
+10B7C;INSCRIPTIONAL PAHLAVI NUMBER TEN;No;0;R;;;;10;N;;;;;
+10B7D;INSCRIPTIONAL PAHLAVI NUMBER TWENTY;No;0;R;;;;20;N;;;;;
+10B7E;INSCRIPTIONAL PAHLAVI NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;;
+10B7F;INSCRIPTIONAL PAHLAVI NUMBER ONE THOUSAND;No;0;R;;;;1000;N;;;;;
+10B80;PSALTER PAHLAVI LETTER ALEPH;Lo;0;R;;;;;N;;;;;
+10B81;PSALTER PAHLAVI LETTER BETH;Lo;0;R;;;;;N;;;;;
+10B82;PSALTER PAHLAVI LETTER GIMEL;Lo;0;R;;;;;N;;;;;
+10B83;PSALTER PAHLAVI LETTER DALETH;Lo;0;R;;;;;N;;;;;
+10B84;PSALTER PAHLAVI LETTER HE;Lo;0;R;;;;;N;;;;;
+10B85;PSALTER PAHLAVI LETTER WAW-AYIN-RESH;Lo;0;R;;;;;N;;;;;
+10B86;PSALTER PAHLAVI LETTER ZAYIN;Lo;0;R;;;;;N;;;;;
+10B87;PSALTER PAHLAVI LETTER HETH;Lo;0;R;;;;;N;;;;;
+10B88;PSALTER PAHLAVI LETTER YODH;Lo;0;R;;;;;N;;;;;
+10B89;PSALTER PAHLAVI LETTER KAPH;Lo;0;R;;;;;N;;;;;
+10B8A;PSALTER PAHLAVI LETTER LAMEDH;Lo;0;R;;;;;N;;;;;
+10B8B;PSALTER PAHLAVI LETTER MEM-QOPH;Lo;0;R;;;;;N;;;;;
+10B8C;PSALTER PAHLAVI LETTER NUN;Lo;0;R;;;;;N;;;;;
+10B8D;PSALTER PAHLAVI LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+10B8E;PSALTER PAHLAVI LETTER PE;Lo;0;R;;;;;N;;;;;
+10B8F;PSALTER PAHLAVI LETTER SADHE;Lo;0;R;;;;;N;;;;;
+10B90;PSALTER PAHLAVI LETTER SHIN;Lo;0;R;;;;;N;;;;;
+10B91;PSALTER PAHLAVI LETTER TAW;Lo;0;R;;;;;N;;;;;
+10B99;PSALTER PAHLAVI SECTION MARK;Po;0;R;;;;;N;;;;;
+10B9A;PSALTER PAHLAVI TURNED SECTION MARK;Po;0;R;;;;;N;;;;;
+10B9B;PSALTER PAHLAVI FOUR DOTS WITH CROSS;Po;0;R;;;;;N;;;;;
+10B9C;PSALTER PAHLAVI FOUR DOTS WITH DOT;Po;0;R;;;;;N;;;;;
+10BA9;PSALTER PAHLAVI NUMBER ONE;No;0;R;;;;1;N;;;;;
+10BAA;PSALTER PAHLAVI NUMBER TWO;No;0;R;;;;2;N;;;;;
+10BAB;PSALTER PAHLAVI NUMBER THREE;No;0;R;;;;3;N;;;;;
+10BAC;PSALTER PAHLAVI NUMBER FOUR;No;0;R;;;;4;N;;;;;
+10BAD;PSALTER PAHLAVI NUMBER TEN;No;0;R;;;;10;N;;;;;
+10BAE;PSALTER PAHLAVI NUMBER TWENTY;No;0;R;;;;20;N;;;;;
+10BAF;PSALTER PAHLAVI NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;;
+10C00;OLD TURKIC LETTER ORKHON A;Lo;0;R;;;;;N;;;;;
+10C01;OLD TURKIC LETTER YENISEI A;Lo;0;R;;;;;N;;;;;
+10C02;OLD TURKIC LETTER YENISEI AE;Lo;0;R;;;;;N;;;;;
+10C03;OLD TURKIC LETTER ORKHON I;Lo;0;R;;;;;N;;;;;
+10C04;OLD TURKIC LETTER YENISEI I;Lo;0;R;;;;;N;;;;;
+10C05;OLD TURKIC LETTER YENISEI E;Lo;0;R;;;;;N;;;;;
+10C06;OLD TURKIC LETTER ORKHON O;Lo;0;R;;;;;N;;;;;
+10C07;OLD TURKIC LETTER ORKHON OE;Lo;0;R;;;;;N;;;;;
+10C08;OLD TURKIC LETTER YENISEI OE;Lo;0;R;;;;;N;;;;;
+10C09;OLD TURKIC LETTER ORKHON AB;Lo;0;R;;;;;N;;;;;
+10C0A;OLD TURKIC LETTER YENISEI AB;Lo;0;R;;;;;N;;;;;
+10C0B;OLD TURKIC LETTER ORKHON AEB;Lo;0;R;;;;;N;;;;;
+10C0C;OLD TURKIC LETTER YENISEI AEB;Lo;0;R;;;;;N;;;;;
+10C0D;OLD TURKIC LETTER ORKHON AG;Lo;0;R;;;;;N;;;;;
+10C0E;OLD TURKIC LETTER YENISEI AG;Lo;0;R;;;;;N;;;;;
+10C0F;OLD TURKIC LETTER ORKHON AEG;Lo;0;R;;;;;N;;;;;
+10C10;OLD TURKIC LETTER YENISEI AEG;Lo;0;R;;;;;N;;;;;
+10C11;OLD TURKIC LETTER ORKHON AD;Lo;0;R;;;;;N;;;;;
+10C12;OLD TURKIC LETTER YENISEI AD;Lo;0;R;;;;;N;;;;;
+10C13;OLD TURKIC LETTER ORKHON AED;Lo;0;R;;;;;N;;;;;
+10C14;OLD TURKIC LETTER ORKHON EZ;Lo;0;R;;;;;N;;;;;
+10C15;OLD TURKIC LETTER YENISEI EZ;Lo;0;R;;;;;N;;;;;
+10C16;OLD TURKIC LETTER ORKHON AY;Lo;0;R;;;;;N;;;;;
+10C17;OLD TURKIC LETTER YENISEI AY;Lo;0;R;;;;;N;;;;;
+10C18;OLD TURKIC LETTER ORKHON AEY;Lo;0;R;;;;;N;;;;;
+10C19;OLD TURKIC LETTER YENISEI AEY;Lo;0;R;;;;;N;;;;;
+10C1A;OLD TURKIC LETTER ORKHON AEK;Lo;0;R;;;;;N;;;;;
+10C1B;OLD TURKIC LETTER YENISEI AEK;Lo;0;R;;;;;N;;;;;
+10C1C;OLD TURKIC LETTER ORKHON OEK;Lo;0;R;;;;;N;;;;;
+10C1D;OLD TURKIC LETTER YENISEI OEK;Lo;0;R;;;;;N;;;;;
+10C1E;OLD TURKIC LETTER ORKHON AL;Lo;0;R;;;;;N;;;;;
+10C1F;OLD TURKIC LETTER YENISEI AL;Lo;0;R;;;;;N;;;;;
+10C20;OLD TURKIC LETTER ORKHON AEL;Lo;0;R;;;;;N;;;;;
+10C21;OLD TURKIC LETTER ORKHON ELT;Lo;0;R;;;;;N;;;;;
+10C22;OLD TURKIC LETTER ORKHON EM;Lo;0;R;;;;;N;;;;;
+10C23;OLD TURKIC LETTER ORKHON AN;Lo;0;R;;;;;N;;;;;
+10C24;OLD TURKIC LETTER ORKHON AEN;Lo;0;R;;;;;N;;;;;
+10C25;OLD TURKIC LETTER YENISEI AEN;Lo;0;R;;;;;N;;;;;
+10C26;OLD TURKIC LETTER ORKHON ENT;Lo;0;R;;;;;N;;;;;
+10C27;OLD TURKIC LETTER YENISEI ENT;Lo;0;R;;;;;N;;;;;
+10C28;OLD TURKIC LETTER ORKHON ENC;Lo;0;R;;;;;N;;;;;
+10C29;OLD TURKIC LETTER YENISEI ENC;Lo;0;R;;;;;N;;;;;
+10C2A;OLD TURKIC LETTER ORKHON ENY;Lo;0;R;;;;;N;;;;;
+10C2B;OLD TURKIC LETTER YENISEI ENY;Lo;0;R;;;;;N;;;;;
+10C2C;OLD TURKIC LETTER YENISEI ANG;Lo;0;R;;;;;N;;;;;
+10C2D;OLD TURKIC LETTER ORKHON ENG;Lo;0;R;;;;;N;;;;;
+10C2E;OLD TURKIC LETTER YENISEI AENG;Lo;0;R;;;;;N;;;;;
+10C2F;OLD TURKIC LETTER ORKHON EP;Lo;0;R;;;;;N;;;;;
+10C30;OLD TURKIC LETTER ORKHON OP;Lo;0;R;;;;;N;;;;;
+10C31;OLD TURKIC LETTER ORKHON IC;Lo;0;R;;;;;N;;;;;
+10C32;OLD TURKIC LETTER ORKHON EC;Lo;0;R;;;;;N;;;;;
+10C33;OLD TURKIC LETTER YENISEI EC;Lo;0;R;;;;;N;;;;;
+10C34;OLD TURKIC LETTER ORKHON AQ;Lo;0;R;;;;;N;;;;;
+10C35;OLD TURKIC LETTER YENISEI AQ;Lo;0;R;;;;;N;;;;;
+10C36;OLD TURKIC LETTER ORKHON IQ;Lo;0;R;;;;;N;;;;;
+10C37;OLD TURKIC LETTER YENISEI IQ;Lo;0;R;;;;;N;;;;;
+10C38;OLD TURKIC LETTER ORKHON OQ;Lo;0;R;;;;;N;;;;;
+10C39;OLD TURKIC LETTER YENISEI OQ;Lo;0;R;;;;;N;;;;;
+10C3A;OLD TURKIC LETTER ORKHON AR;Lo;0;R;;;;;N;;;;;
+10C3B;OLD TURKIC LETTER YENISEI AR;Lo;0;R;;;;;N;;;;;
+10C3C;OLD TURKIC LETTER ORKHON AER;Lo;0;R;;;;;N;;;;;
+10C3D;OLD TURKIC LETTER ORKHON AS;Lo;0;R;;;;;N;;;;;
+10C3E;OLD TURKIC LETTER ORKHON AES;Lo;0;R;;;;;N;;;;;
+10C3F;OLD TURKIC LETTER ORKHON ASH;Lo;0;R;;;;;N;;;;;
+10C40;OLD TURKIC LETTER YENISEI ASH;Lo;0;R;;;;;N;;;;;
+10C41;OLD TURKIC LETTER ORKHON ESH;Lo;0;R;;;;;N;;;;;
+10C42;OLD TURKIC LETTER YENISEI ESH;Lo;0;R;;;;;N;;;;;
+10C43;OLD TURKIC LETTER ORKHON AT;Lo;0;R;;;;;N;;;;;
+10C44;OLD TURKIC LETTER YENISEI AT;Lo;0;R;;;;;N;;;;;
+10C45;OLD TURKIC LETTER ORKHON AET;Lo;0;R;;;;;N;;;;;
+10C46;OLD TURKIC LETTER YENISEI AET;Lo;0;R;;;;;N;;;;;
+10C47;OLD TURKIC LETTER ORKHON OT;Lo;0;R;;;;;N;;;;;
+10C48;OLD TURKIC LETTER ORKHON BASH;Lo;0;R;;;;;N;;;;;
+10C80;OLD HUNGARIAN CAPITAL LETTER A;Lu;0;R;;;;;N;;;;10CC0;
+10C81;OLD HUNGARIAN CAPITAL LETTER AA;Lu;0;R;;;;;N;;;;10CC1;
+10C82;OLD HUNGARIAN CAPITAL LETTER EB;Lu;0;R;;;;;N;;;;10CC2;
+10C83;OLD HUNGARIAN CAPITAL LETTER AMB;Lu;0;R;;;;;N;;;;10CC3;
+10C84;OLD HUNGARIAN CAPITAL LETTER EC;Lu;0;R;;;;;N;;;;10CC4;
+10C85;OLD HUNGARIAN CAPITAL LETTER ENC;Lu;0;R;;;;;N;;;;10CC5;
+10C86;OLD HUNGARIAN CAPITAL LETTER ECS;Lu;0;R;;;;;N;;;;10CC6;
+10C87;OLD HUNGARIAN CAPITAL LETTER ED;Lu;0;R;;;;;N;;;;10CC7;
+10C88;OLD HUNGARIAN CAPITAL LETTER AND;Lu;0;R;;;;;N;;;;10CC8;
+10C89;OLD HUNGARIAN CAPITAL LETTER E;Lu;0;R;;;;;N;;;;10CC9;
+10C8A;OLD HUNGARIAN CAPITAL LETTER CLOSE E;Lu;0;R;;;;;N;;;;10CCA;
+10C8B;OLD HUNGARIAN CAPITAL LETTER EE;Lu;0;R;;;;;N;;;;10CCB;
+10C8C;OLD HUNGARIAN CAPITAL LETTER EF;Lu;0;R;;;;;N;;;;10CCC;
+10C8D;OLD HUNGARIAN CAPITAL LETTER EG;Lu;0;R;;;;;N;;;;10CCD;
+10C8E;OLD HUNGARIAN CAPITAL LETTER EGY;Lu;0;R;;;;;N;;;;10CCE;
+10C8F;OLD HUNGARIAN CAPITAL LETTER EH;Lu;0;R;;;;;N;;;;10CCF;
+10C90;OLD HUNGARIAN CAPITAL LETTER I;Lu;0;R;;;;;N;;;;10CD0;
+10C91;OLD HUNGARIAN CAPITAL LETTER II;Lu;0;R;;;;;N;;;;10CD1;
+10C92;OLD HUNGARIAN CAPITAL LETTER EJ;Lu;0;R;;;;;N;;;;10CD2;
+10C93;OLD HUNGARIAN CAPITAL LETTER EK;Lu;0;R;;;;;N;;;;10CD3;
+10C94;OLD HUNGARIAN CAPITAL LETTER AK;Lu;0;R;;;;;N;;;;10CD4;
+10C95;OLD HUNGARIAN CAPITAL LETTER UNK;Lu;0;R;;;;;N;;;;10CD5;
+10C96;OLD HUNGARIAN CAPITAL LETTER EL;Lu;0;R;;;;;N;;;;10CD6;
+10C97;OLD HUNGARIAN CAPITAL LETTER ELY;Lu;0;R;;;;;N;;;;10CD7;
+10C98;OLD HUNGARIAN CAPITAL LETTER EM;Lu;0;R;;;;;N;;;;10CD8;
+10C99;OLD HUNGARIAN CAPITAL LETTER EN;Lu;0;R;;;;;N;;;;10CD9;
+10C9A;OLD HUNGARIAN CAPITAL LETTER ENY;Lu;0;R;;;;;N;;;;10CDA;
+10C9B;OLD HUNGARIAN CAPITAL LETTER O;Lu;0;R;;;;;N;;;;10CDB;
+10C9C;OLD HUNGARIAN CAPITAL LETTER OO;Lu;0;R;;;;;N;;;;10CDC;
+10C9D;OLD HUNGARIAN CAPITAL LETTER NIKOLSBURG OE;Lu;0;R;;;;;N;;;;10CDD;
+10C9E;OLD HUNGARIAN CAPITAL LETTER RUDIMENTA OE;Lu;0;R;;;;;N;;;;10CDE;
+10C9F;OLD HUNGARIAN CAPITAL LETTER OEE;Lu;0;R;;;;;N;;;;10CDF;
+10CA0;OLD HUNGARIAN CAPITAL LETTER EP;Lu;0;R;;;;;N;;;;10CE0;
+10CA1;OLD HUNGARIAN CAPITAL LETTER EMP;Lu;0;R;;;;;N;;;;10CE1;
+10CA2;OLD HUNGARIAN CAPITAL LETTER ER;Lu;0;R;;;;;N;;;;10CE2;
+10CA3;OLD HUNGARIAN CAPITAL LETTER SHORT ER;Lu;0;R;;;;;N;;;;10CE3;
+10CA4;OLD HUNGARIAN CAPITAL LETTER ES;Lu;0;R;;;;;N;;;;10CE4;
+10CA5;OLD HUNGARIAN CAPITAL LETTER ESZ;Lu;0;R;;;;;N;;;;10CE5;
+10CA6;OLD HUNGARIAN CAPITAL LETTER ET;Lu;0;R;;;;;N;;;;10CE6;
+10CA7;OLD HUNGARIAN CAPITAL LETTER ENT;Lu;0;R;;;;;N;;;;10CE7;
+10CA8;OLD HUNGARIAN CAPITAL LETTER ETY;Lu;0;R;;;;;N;;;;10CE8;
+10CA9;OLD HUNGARIAN CAPITAL LETTER ECH;Lu;0;R;;;;;N;;;;10CE9;
+10CAA;OLD HUNGARIAN CAPITAL LETTER U;Lu;0;R;;;;;N;;;;10CEA;
+10CAB;OLD HUNGARIAN CAPITAL LETTER UU;Lu;0;R;;;;;N;;;;10CEB;
+10CAC;OLD HUNGARIAN CAPITAL LETTER NIKOLSBURG UE;Lu;0;R;;;;;N;;;;10CEC;
+10CAD;OLD HUNGARIAN CAPITAL LETTER RUDIMENTA UE;Lu;0;R;;;;;N;;;;10CED;
+10CAE;OLD HUNGARIAN CAPITAL LETTER EV;Lu;0;R;;;;;N;;;;10CEE;
+10CAF;OLD HUNGARIAN CAPITAL LETTER EZ;Lu;0;R;;;;;N;;;;10CEF;
+10CB0;OLD HUNGARIAN CAPITAL LETTER EZS;Lu;0;R;;;;;N;;;;10CF0;
+10CB1;OLD HUNGARIAN CAPITAL LETTER ENT-SHAPED SIGN;Lu;0;R;;;;;N;;;;10CF1;
+10CB2;OLD HUNGARIAN CAPITAL LETTER US;Lu;0;R;;;;;N;;;;10CF2;
+10CC0;OLD HUNGARIAN SMALL LETTER A;Ll;0;R;;;;;N;;;10C80;;10C80
+10CC1;OLD HUNGARIAN SMALL LETTER AA;Ll;0;R;;;;;N;;;10C81;;10C81
+10CC2;OLD HUNGARIAN SMALL LETTER EB;Ll;0;R;;;;;N;;;10C82;;10C82
+10CC3;OLD HUNGARIAN SMALL LETTER AMB;Ll;0;R;;;;;N;;;10C83;;10C83
+10CC4;OLD HUNGARIAN SMALL LETTER EC;Ll;0;R;;;;;N;;;10C84;;10C84
+10CC5;OLD HUNGARIAN SMALL LETTER ENC;Ll;0;R;;;;;N;;;10C85;;10C85
+10CC6;OLD HUNGARIAN SMALL LETTER ECS;Ll;0;R;;;;;N;;;10C86;;10C86
+10CC7;OLD HUNGARIAN SMALL LETTER ED;Ll;0;R;;;;;N;;;10C87;;10C87
+10CC8;OLD HUNGARIAN SMALL LETTER AND;Ll;0;R;;;;;N;;;10C88;;10C88
+10CC9;OLD HUNGARIAN SMALL LETTER E;Ll;0;R;;;;;N;;;10C89;;10C89
+10CCA;OLD HUNGARIAN SMALL LETTER CLOSE E;Ll;0;R;;;;;N;;;10C8A;;10C8A
+10CCB;OLD HUNGARIAN SMALL LETTER EE;Ll;0;R;;;;;N;;;10C8B;;10C8B
+10CCC;OLD HUNGARIAN SMALL LETTER EF;Ll;0;R;;;;;N;;;10C8C;;10C8C
+10CCD;OLD HUNGARIAN SMALL LETTER EG;Ll;0;R;;;;;N;;;10C8D;;10C8D
+10CCE;OLD HUNGARIAN SMALL LETTER EGY;Ll;0;R;;;;;N;;;10C8E;;10C8E
+10CCF;OLD HUNGARIAN SMALL LETTER EH;Ll;0;R;;;;;N;;;10C8F;;10C8F
+10CD0;OLD HUNGARIAN SMALL LETTER I;Ll;0;R;;;;;N;;;10C90;;10C90
+10CD1;OLD HUNGARIAN SMALL LETTER II;Ll;0;R;;;;;N;;;10C91;;10C91
+10CD2;OLD HUNGARIAN SMALL LETTER EJ;Ll;0;R;;;;;N;;;10C92;;10C92
+10CD3;OLD HUNGARIAN SMALL LETTER EK;Ll;0;R;;;;;N;;;10C93;;10C93
+10CD4;OLD HUNGARIAN SMALL LETTER AK;Ll;0;R;;;;;N;;;10C94;;10C94
+10CD5;OLD HUNGARIAN SMALL LETTER UNK;Ll;0;R;;;;;N;;;10C95;;10C95
+10CD6;OLD HUNGARIAN SMALL LETTER EL;Ll;0;R;;;;;N;;;10C96;;10C96
+10CD7;OLD HUNGARIAN SMALL LETTER ELY;Ll;0;R;;;;;N;;;10C97;;10C97
+10CD8;OLD HUNGARIAN SMALL LETTER EM;Ll;0;R;;;;;N;;;10C98;;10C98
+10CD9;OLD HUNGARIAN SMALL LETTER EN;Ll;0;R;;;;;N;;;10C99;;10C99
+10CDA;OLD HUNGARIAN SMALL LETTER ENY;Ll;0;R;;;;;N;;;10C9A;;10C9A
+10CDB;OLD HUNGARIAN SMALL LETTER O;Ll;0;R;;;;;N;;;10C9B;;10C9B
+10CDC;OLD HUNGARIAN SMALL LETTER OO;Ll;0;R;;;;;N;;;10C9C;;10C9C
+10CDD;OLD HUNGARIAN SMALL LETTER NIKOLSBURG OE;Ll;0;R;;;;;N;;;10C9D;;10C9D
+10CDE;OLD HUNGARIAN SMALL LETTER RUDIMENTA OE;Ll;0;R;;;;;N;;;10C9E;;10C9E
+10CDF;OLD HUNGARIAN SMALL LETTER OEE;Ll;0;R;;;;;N;;;10C9F;;10C9F
+10CE0;OLD HUNGARIAN SMALL LETTER EP;Ll;0;R;;;;;N;;;10CA0;;10CA0
+10CE1;OLD HUNGARIAN SMALL LETTER EMP;Ll;0;R;;;;;N;;;10CA1;;10CA1
+10CE2;OLD HUNGARIAN SMALL LETTER ER;Ll;0;R;;;;;N;;;10CA2;;10CA2
+10CE3;OLD HUNGARIAN SMALL LETTER SHORT ER;Ll;0;R;;;;;N;;;10CA3;;10CA3
+10CE4;OLD HUNGARIAN SMALL LETTER ES;Ll;0;R;;;;;N;;;10CA4;;10CA4
+10CE5;OLD HUNGARIAN SMALL LETTER ESZ;Ll;0;R;;;;;N;;;10CA5;;10CA5
+10CE6;OLD HUNGARIAN SMALL LETTER ET;Ll;0;R;;;;;N;;;10CA6;;10CA6
+10CE7;OLD HUNGARIAN SMALL LETTER ENT;Ll;0;R;;;;;N;;;10CA7;;10CA7
+10CE8;OLD HUNGARIAN SMALL LETTER ETY;Ll;0;R;;;;;N;;;10CA8;;10CA8
+10CE9;OLD HUNGARIAN SMALL LETTER ECH;Ll;0;R;;;;;N;;;10CA9;;10CA9
+10CEA;OLD HUNGARIAN SMALL LETTER U;Ll;0;R;;;;;N;;;10CAA;;10CAA
+10CEB;OLD HUNGARIAN SMALL LETTER UU;Ll;0;R;;;;;N;;;10CAB;;10CAB
+10CEC;OLD HUNGARIAN SMALL LETTER NIKOLSBURG UE;Ll;0;R;;;;;N;;;10CAC;;10CAC
+10CED;OLD HUNGARIAN SMALL LETTER RUDIMENTA UE;Ll;0;R;;;;;N;;;10CAD;;10CAD
+10CEE;OLD HUNGARIAN SMALL LETTER EV;Ll;0;R;;;;;N;;;10CAE;;10CAE
+10CEF;OLD HUNGARIAN SMALL LETTER EZ;Ll;0;R;;;;;N;;;10CAF;;10CAF
+10CF0;OLD HUNGARIAN SMALL LETTER EZS;Ll;0;R;;;;;N;;;10CB0;;10CB0
+10CF1;OLD HUNGARIAN SMALL LETTER ENT-SHAPED SIGN;Ll;0;R;;;;;N;;;10CB1;;10CB1
+10CF2;OLD HUNGARIAN SMALL LETTER US;Ll;0;R;;;;;N;;;10CB2;;10CB2
+10CFA;OLD HUNGARIAN NUMBER ONE;No;0;R;;;;1;N;;;;;
+10CFB;OLD HUNGARIAN NUMBER FIVE;No;0;R;;;;5;N;;;;;
+10CFC;OLD HUNGARIAN NUMBER TEN;No;0;R;;;;10;N;;;;;
+10CFD;OLD HUNGARIAN NUMBER FIFTY;No;0;R;;;;50;N;;;;;
+10CFE;OLD HUNGARIAN NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;;
+10CFF;OLD HUNGARIAN NUMBER ONE THOUSAND;No;0;R;;;;1000;N;;;;;
+10D00;HANIFI ROHINGYA LETTER A;Lo;0;AL;;;;;N;;;;;
+10D01;HANIFI ROHINGYA LETTER BA;Lo;0;AL;;;;;N;;;;;
+10D02;HANIFI ROHINGYA LETTER PA;Lo;0;AL;;;;;N;;;;;
+10D03;HANIFI ROHINGYA LETTER TA;Lo;0;AL;;;;;N;;;;;
+10D04;HANIFI ROHINGYA LETTER TTA;Lo;0;AL;;;;;N;;;;;
+10D05;HANIFI ROHINGYA LETTER JA;Lo;0;AL;;;;;N;;;;;
+10D06;HANIFI ROHINGYA LETTER CA;Lo;0;AL;;;;;N;;;;;
+10D07;HANIFI ROHINGYA LETTER HA;Lo;0;AL;;;;;N;;;;;
+10D08;HANIFI ROHINGYA LETTER KHA;Lo;0;AL;;;;;N;;;;;
+10D09;HANIFI ROHINGYA LETTER FA;Lo;0;AL;;;;;N;;;;;
+10D0A;HANIFI ROHINGYA LETTER DA;Lo;0;AL;;;;;N;;;;;
+10D0B;HANIFI ROHINGYA LETTER DDA;Lo;0;AL;;;;;N;;;;;
+10D0C;HANIFI ROHINGYA LETTER RA;Lo;0;AL;;;;;N;;;;;
+10D0D;HANIFI ROHINGYA LETTER RRA;Lo;0;AL;;;;;N;;;;;
+10D0E;HANIFI ROHINGYA LETTER ZA;Lo;0;AL;;;;;N;;;;;
+10D0F;HANIFI ROHINGYA LETTER SA;Lo;0;AL;;;;;N;;;;;
+10D10;HANIFI ROHINGYA LETTER SHA;Lo;0;AL;;;;;N;;;;;
+10D11;HANIFI ROHINGYA LETTER KA;Lo;0;AL;;;;;N;;;;;
+10D12;HANIFI ROHINGYA LETTER GA;Lo;0;AL;;;;;N;;;;;
+10D13;HANIFI ROHINGYA LETTER LA;Lo;0;AL;;;;;N;;;;;
+10D14;HANIFI ROHINGYA LETTER MA;Lo;0;AL;;;;;N;;;;;
+10D15;HANIFI ROHINGYA LETTER NA;Lo;0;AL;;;;;N;;;;;
+10D16;HANIFI ROHINGYA LETTER WA;Lo;0;AL;;;;;N;;;;;
+10D17;HANIFI ROHINGYA LETTER KINNA WA;Lo;0;AL;;;;;N;;;;;
+10D18;HANIFI ROHINGYA LETTER YA;Lo;0;AL;;;;;N;;;;;
+10D19;HANIFI ROHINGYA LETTER KINNA YA;Lo;0;AL;;;;;N;;;;;
+10D1A;HANIFI ROHINGYA LETTER NGA;Lo;0;AL;;;;;N;;;;;
+10D1B;HANIFI ROHINGYA LETTER NYA;Lo;0;AL;;;;;N;;;;;
+10D1C;HANIFI ROHINGYA LETTER VA;Lo;0;AL;;;;;N;;;;;
+10D1D;HANIFI ROHINGYA VOWEL A;Lo;0;AL;;;;;N;;;;;
+10D1E;HANIFI ROHINGYA VOWEL I;Lo;0;AL;;;;;N;;;;;
+10D1F;HANIFI ROHINGYA VOWEL U;Lo;0;AL;;;;;N;;;;;
+10D20;HANIFI ROHINGYA VOWEL E;Lo;0;AL;;;;;N;;;;;
+10D21;HANIFI ROHINGYA VOWEL O;Lo;0;AL;;;;;N;;;;;
+10D22;HANIFI ROHINGYA MARK SAKIN;Lo;0;AL;;;;;N;;;;;
+10D23;HANIFI ROHINGYA MARK NA KHONNA;Lo;0;AL;;;;;N;;;;;
+10D24;HANIFI ROHINGYA SIGN HARBAHAY;Mn;230;NSM;;;;;N;;;;;
+10D25;HANIFI ROHINGYA SIGN TAHALA;Mn;230;NSM;;;;;N;;;;;
+10D26;HANIFI ROHINGYA SIGN TANA;Mn;230;NSM;;;;;N;;;;;
+10D27;HANIFI ROHINGYA SIGN TASSI;Mn;230;NSM;;;;;N;;;;;
+10D30;HANIFI ROHINGYA DIGIT ZERO;Nd;0;AN;;0;0;0;N;;;;;
+10D31;HANIFI ROHINGYA DIGIT ONE;Nd;0;AN;;1;1;1;N;;;;;
+10D32;HANIFI ROHINGYA DIGIT TWO;Nd;0;AN;;2;2;2;N;;;;;
+10D33;HANIFI ROHINGYA DIGIT THREE;Nd;0;AN;;3;3;3;N;;;;;
+10D34;HANIFI ROHINGYA DIGIT FOUR;Nd;0;AN;;4;4;4;N;;;;;
+10D35;HANIFI ROHINGYA DIGIT FIVE;Nd;0;AN;;5;5;5;N;;;;;
+10D36;HANIFI ROHINGYA DIGIT SIX;Nd;0;AN;;6;6;6;N;;;;;
+10D37;HANIFI ROHINGYA DIGIT SEVEN;Nd;0;AN;;7;7;7;N;;;;;
+10D38;HANIFI ROHINGYA DIGIT EIGHT;Nd;0;AN;;8;8;8;N;;;;;
+10D39;HANIFI ROHINGYA DIGIT NINE;Nd;0;AN;;9;9;9;N;;;;;
+10E60;RUMI DIGIT ONE;No;0;AN;;;1;1;N;;;;;
+10E61;RUMI DIGIT TWO;No;0;AN;;;2;2;N;;;;;
+10E62;RUMI DIGIT THREE;No;0;AN;;;3;3;N;;;;;
+10E63;RUMI DIGIT FOUR;No;0;AN;;;4;4;N;;;;;
+10E64;RUMI DIGIT FIVE;No;0;AN;;;5;5;N;;;;;
+10E65;RUMI DIGIT SIX;No;0;AN;;;6;6;N;;;;;
+10E66;RUMI DIGIT SEVEN;No;0;AN;;;7;7;N;;;;;
+10E67;RUMI DIGIT EIGHT;No;0;AN;;;8;8;N;;;;;
+10E68;RUMI DIGIT NINE;No;0;AN;;;9;9;N;;;;;
+10E69;RUMI NUMBER TEN;No;0;AN;;;;10;N;;;;;
+10E6A;RUMI NUMBER TWENTY;No;0;AN;;;;20;N;;;;;
+10E6B;RUMI NUMBER THIRTY;No;0;AN;;;;30;N;;;;;
+10E6C;RUMI NUMBER FORTY;No;0;AN;;;;40;N;;;;;
+10E6D;RUMI NUMBER FIFTY;No;0;AN;;;;50;N;;;;;
+10E6E;RUMI NUMBER SIXTY;No;0;AN;;;;60;N;;;;;
+10E6F;RUMI NUMBER SEVENTY;No;0;AN;;;;70;N;;;;;
+10E70;RUMI NUMBER EIGHTY;No;0;AN;;;;80;N;;;;;
+10E71;RUMI NUMBER NINETY;No;0;AN;;;;90;N;;;;;
+10E72;RUMI NUMBER ONE HUNDRED;No;0;AN;;;;100;N;;;;;
+10E73;RUMI NUMBER TWO HUNDRED;No;0;AN;;;;200;N;;;;;
+10E74;RUMI NUMBER THREE HUNDRED;No;0;AN;;;;300;N;;;;;
+10E75;RUMI NUMBER FOUR HUNDRED;No;0;AN;;;;400;N;;;;;
+10E76;RUMI NUMBER FIVE HUNDRED;No;0;AN;;;;500;N;;;;;
+10E77;RUMI NUMBER SIX HUNDRED;No;0;AN;;;;600;N;;;;;
+10E78;RUMI NUMBER SEVEN HUNDRED;No;0;AN;;;;700;N;;;;;
+10E79;RUMI NUMBER EIGHT HUNDRED;No;0;AN;;;;800;N;;;;;
+10E7A;RUMI NUMBER NINE HUNDRED;No;0;AN;;;;900;N;;;;;
+10E7B;RUMI FRACTION ONE HALF;No;0;AN;;;;1/2;N;;;;;
+10E7C;RUMI FRACTION ONE QUARTER;No;0;AN;;;;1/4;N;;;;;
+10E7D;RUMI FRACTION ONE THIRD;No;0;AN;;;;1/3;N;;;;;
+10E7E;RUMI FRACTION TWO THIRDS;No;0;AN;;;;2/3;N;;;;;
+10F00;OLD SOGDIAN LETTER ALEPH;Lo;0;R;;;;;N;;;;;
+10F01;OLD SOGDIAN LETTER FINAL ALEPH;Lo;0;R;;;;;N;;;;;
+10F02;OLD SOGDIAN LETTER BETH;Lo;0;R;;;;;N;;;;;
+10F03;OLD SOGDIAN LETTER FINAL BETH;Lo;0;R;;;;;N;;;;;
+10F04;OLD SOGDIAN LETTER GIMEL;Lo;0;R;;;;;N;;;;;
+10F05;OLD SOGDIAN LETTER HE;Lo;0;R;;;;;N;;;;;
+10F06;OLD SOGDIAN LETTER FINAL HE;Lo;0;R;;;;;N;;;;;
+10F07;OLD SOGDIAN LETTER WAW;Lo;0;R;;;;;N;;;;;
+10F08;OLD SOGDIAN LETTER ZAYIN;Lo;0;R;;;;;N;;;;;
+10F09;OLD SOGDIAN LETTER HETH;Lo;0;R;;;;;N;;;;;
+10F0A;OLD SOGDIAN LETTER YODH;Lo;0;R;;;;;N;;;;;
+10F0B;OLD SOGDIAN LETTER KAPH;Lo;0;R;;;;;N;;;;;
+10F0C;OLD SOGDIAN LETTER LAMEDH;Lo;0;R;;;;;N;;;;;
+10F0D;OLD SOGDIAN LETTER MEM;Lo;0;R;;;;;N;;;;;
+10F0E;OLD SOGDIAN LETTER NUN;Lo;0;R;;;;;N;;;;;
+10F0F;OLD SOGDIAN LETTER FINAL NUN;Lo;0;R;;;;;N;;;;;
+10F10;OLD SOGDIAN LETTER FINAL NUN WITH VERTICAL TAIL;Lo;0;R;;;;;N;;;;;
+10F11;OLD SOGDIAN LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+10F12;OLD SOGDIAN LETTER AYIN;Lo;0;R;;;;;N;;;;;
+10F13;OLD SOGDIAN LETTER ALTERNATE AYIN;Lo;0;R;;;;;N;;;;;
+10F14;OLD SOGDIAN LETTER PE;Lo;0;R;;;;;N;;;;;
+10F15;OLD SOGDIAN LETTER SADHE;Lo;0;R;;;;;N;;;;;
+10F16;OLD SOGDIAN LETTER FINAL SADHE;Lo;0;R;;;;;N;;;;;
+10F17;OLD SOGDIAN LETTER FINAL SADHE WITH VERTICAL TAIL;Lo;0;R;;;;;N;;;;;
+10F18;OLD SOGDIAN LETTER RESH-AYIN-DALETH;Lo;0;R;;;;;N;;;;;
+10F19;OLD SOGDIAN LETTER SHIN;Lo;0;R;;;;;N;;;;;
+10F1A;OLD SOGDIAN LETTER TAW;Lo;0;R;;;;;N;;;;;
+10F1B;OLD SOGDIAN LETTER FINAL TAW;Lo;0;R;;;;;N;;;;;
+10F1C;OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL;Lo;0;R;;;;;N;;;;;
+10F1D;OLD SOGDIAN NUMBER ONE;No;0;R;;;;1;N;;;;;
+10F1E;OLD SOGDIAN NUMBER TWO;No;0;R;;;;2;N;;;;;
+10F1F;OLD SOGDIAN NUMBER THREE;No;0;R;;;;3;N;;;;;
+10F20;OLD SOGDIAN NUMBER FOUR;No;0;R;;;;4;N;;;;;
+10F21;OLD SOGDIAN NUMBER FIVE;No;0;R;;;;5;N;;;;;
+10F22;OLD SOGDIAN NUMBER TEN;No;0;R;;;;10;N;;;;;
+10F23;OLD SOGDIAN NUMBER TWENTY;No;0;R;;;;20;N;;;;;
+10F24;OLD SOGDIAN NUMBER THIRTY;No;0;R;;;;30;N;;;;;
+10F25;OLD SOGDIAN NUMBER ONE HUNDRED;No;0;R;;;;100;N;;;;;
+10F26;OLD SOGDIAN FRACTION ONE HALF;No;0;R;;;;1/2;N;;;;;
+10F27;OLD SOGDIAN LIGATURE AYIN-DALETH;Lo;0;R;;;;;N;;;;;
+10F30;SOGDIAN LETTER ALEPH;Lo;0;AL;;;;;N;;;;;
+10F31;SOGDIAN LETTER BETH;Lo;0;AL;;;;;N;;;;;
+10F32;SOGDIAN LETTER GIMEL;Lo;0;AL;;;;;N;;;;;
+10F33;SOGDIAN LETTER HE;Lo;0;AL;;;;;N;;;;;
+10F34;SOGDIAN LETTER WAW;Lo;0;AL;;;;;N;;;;;
+10F35;SOGDIAN LETTER ZAYIN;Lo;0;AL;;;;;N;;;;;
+10F36;SOGDIAN LETTER HETH;Lo;0;AL;;;;;N;;;;;
+10F37;SOGDIAN LETTER YODH;Lo;0;AL;;;;;N;;;;;
+10F38;SOGDIAN LETTER KAPH;Lo;0;AL;;;;;N;;;;;
+10F39;SOGDIAN LETTER LAMEDH;Lo;0;AL;;;;;N;;;;;
+10F3A;SOGDIAN LETTER MEM;Lo;0;AL;;;;;N;;;;;
+10F3B;SOGDIAN LETTER NUN;Lo;0;AL;;;;;N;;;;;
+10F3C;SOGDIAN LETTER SAMEKH;Lo;0;AL;;;;;N;;;;;
+10F3D;SOGDIAN LETTER AYIN;Lo;0;AL;;;;;N;;;;;
+10F3E;SOGDIAN LETTER PE;Lo;0;AL;;;;;N;;;;;
+10F3F;SOGDIAN LETTER SADHE;Lo;0;AL;;;;;N;;;;;
+10F40;SOGDIAN LETTER RESH-AYIN;Lo;0;AL;;;;;N;;;;;
+10F41;SOGDIAN LETTER SHIN;Lo;0;AL;;;;;N;;;;;
+10F42;SOGDIAN LETTER TAW;Lo;0;AL;;;;;N;;;;;
+10F43;SOGDIAN LETTER FETH;Lo;0;AL;;;;;N;;;;;
+10F44;SOGDIAN LETTER LESH;Lo;0;AL;;;;;N;;;;;
+10F45;SOGDIAN INDEPENDENT SHIN;Lo;0;AL;;;;;N;;;;;
+10F46;SOGDIAN COMBINING DOT BELOW;Mn;220;NSM;;;;;N;;;;;
+10F47;SOGDIAN COMBINING TWO DOTS BELOW;Mn;220;NSM;;;;;N;;;;;
+10F48;SOGDIAN COMBINING DOT ABOVE;Mn;230;NSM;;;;;N;;;;;
+10F49;SOGDIAN COMBINING TWO DOTS ABOVE;Mn;230;NSM;;;;;N;;;;;
+10F4A;SOGDIAN COMBINING CURVE ABOVE;Mn;230;NSM;;;;;N;;;;;
+10F4B;SOGDIAN COMBINING CURVE BELOW;Mn;220;NSM;;;;;N;;;;;
+10F4C;SOGDIAN COMBINING HOOK ABOVE;Mn;230;NSM;;;;;N;;;;;
+10F4D;SOGDIAN COMBINING HOOK BELOW;Mn;220;NSM;;;;;N;;;;;
+10F4E;SOGDIAN COMBINING LONG HOOK BELOW;Mn;220;NSM;;;;;N;;;;;
+10F4F;SOGDIAN COMBINING RESH BELOW;Mn;220;NSM;;;;;N;;;;;
+10F50;SOGDIAN COMBINING STROKE BELOW;Mn;220;NSM;;;;;N;;;;;
+10F51;SOGDIAN NUMBER ONE;No;0;AL;;;;1;N;;;;;
+10F52;SOGDIAN NUMBER TEN;No;0;AL;;;;10;N;;;;;
+10F53;SOGDIAN NUMBER TWENTY;No;0;AL;;;;20;N;;;;;
+10F54;SOGDIAN NUMBER ONE HUNDRED;No;0;AL;;;;100;N;;;;;
+10F55;SOGDIAN PUNCTUATION TWO VERTICAL BARS;Po;0;AL;;;;;N;;;;;
+10F56;SOGDIAN PUNCTUATION TWO VERTICAL BARS WITH DOTS;Po;0;AL;;;;;N;;;;;
+10F57;SOGDIAN PUNCTUATION CIRCLE WITH DOT;Po;0;AL;;;;;N;;;;;
+10F58;SOGDIAN PUNCTUATION TWO CIRCLES WITH DOTS;Po;0;AL;;;;;N;;;;;
+10F59;SOGDIAN PUNCTUATION HALF CIRCLE WITH DOT;Po;0;AL;;;;;N;;;;;
+10FE0;ELYMAIC LETTER ALEPH;Lo;0;R;;;;;N;;;;;
+10FE1;ELYMAIC LETTER BETH;Lo;0;R;;;;;N;;;;;
+10FE2;ELYMAIC LETTER GIMEL;Lo;0;R;;;;;N;;;;;
+10FE3;ELYMAIC LETTER DALETH;Lo;0;R;;;;;N;;;;;
+10FE4;ELYMAIC LETTER HE;Lo;0;R;;;;;N;;;;;
+10FE5;ELYMAIC LETTER WAW;Lo;0;R;;;;;N;;;;;
+10FE6;ELYMAIC LETTER ZAYIN;Lo;0;R;;;;;N;;;;;
+10FE7;ELYMAIC LETTER HETH;Lo;0;R;;;;;N;;;;;
+10FE8;ELYMAIC LETTER TETH;Lo;0;R;;;;;N;;;;;
+10FE9;ELYMAIC LETTER YODH;Lo;0;R;;;;;N;;;;;
+10FEA;ELYMAIC LETTER KAPH;Lo;0;R;;;;;N;;;;;
+10FEB;ELYMAIC LETTER LAMEDH;Lo;0;R;;;;;N;;;;;
+10FEC;ELYMAIC LETTER MEM;Lo;0;R;;;;;N;;;;;
+10FED;ELYMAIC LETTER NUN;Lo;0;R;;;;;N;;;;;
+10FEE;ELYMAIC LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+10FEF;ELYMAIC LETTER AYIN;Lo;0;R;;;;;N;;;;;
+10FF0;ELYMAIC LETTER PE;Lo;0;R;;;;;N;;;;;
+10FF1;ELYMAIC LETTER SADHE;Lo;0;R;;;;;N;;;;;
+10FF2;ELYMAIC LETTER QOPH;Lo;0;R;;;;;N;;;;;
+10FF3;ELYMAIC LETTER RESH;Lo;0;R;;;;;N;;;;;
+10FF4;ELYMAIC LETTER SHIN;Lo;0;R;;;;;N;;;;;
+10FF5;ELYMAIC LETTER TAW;Lo;0;R;;;;;N;;;;;
+10FF6;ELYMAIC LIGATURE ZAYIN-YODH;Lo;0;R;;;;;N;;;;;
+11000;BRAHMI SIGN CANDRABINDU;Mc;0;L;;;;;N;;;;;
+11001;BRAHMI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+11002;BRAHMI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+11003;BRAHMI SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;;
+11004;BRAHMI SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;;
+11005;BRAHMI LETTER A;Lo;0;L;;;;;N;;;;;
+11006;BRAHMI LETTER AA;Lo;0;L;;;;;N;;;;;
+11007;BRAHMI LETTER I;Lo;0;L;;;;;N;;;;;
+11008;BRAHMI LETTER II;Lo;0;L;;;;;N;;;;;
+11009;BRAHMI LETTER U;Lo;0;L;;;;;N;;;;;
+1100A;BRAHMI LETTER UU;Lo;0;L;;;;;N;;;;;
+1100B;BRAHMI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+1100C;BRAHMI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+1100D;BRAHMI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+1100E;BRAHMI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+1100F;BRAHMI LETTER E;Lo;0;L;;;;;N;;;;;
+11010;BRAHMI LETTER AI;Lo;0;L;;;;;N;;;;;
+11011;BRAHMI LETTER O;Lo;0;L;;;;;N;;;;;
+11012;BRAHMI LETTER AU;Lo;0;L;;;;;N;;;;;
+11013;BRAHMI LETTER KA;Lo;0;L;;;;;N;;;;;
+11014;BRAHMI LETTER KHA;Lo;0;L;;;;;N;;;;;
+11015;BRAHMI LETTER GA;Lo;0;L;;;;;N;;;;;
+11016;BRAHMI LETTER GHA;Lo;0;L;;;;;N;;;;;
+11017;BRAHMI LETTER NGA;Lo;0;L;;;;;N;;;;;
+11018;BRAHMI LETTER CA;Lo;0;L;;;;;N;;;;;
+11019;BRAHMI LETTER CHA;Lo;0;L;;;;;N;;;;;
+1101A;BRAHMI LETTER JA;Lo;0;L;;;;;N;;;;;
+1101B;BRAHMI LETTER JHA;Lo;0;L;;;;;N;;;;;
+1101C;BRAHMI LETTER NYA;Lo;0;L;;;;;N;;;;;
+1101D;BRAHMI LETTER TTA;Lo;0;L;;;;;N;;;;;
+1101E;BRAHMI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+1101F;BRAHMI LETTER DDA;Lo;0;L;;;;;N;;;;;
+11020;BRAHMI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+11021;BRAHMI LETTER NNA;Lo;0;L;;;;;N;;;;;
+11022;BRAHMI LETTER TA;Lo;0;L;;;;;N;;;;;
+11023;BRAHMI LETTER THA;Lo;0;L;;;;;N;;;;;
+11024;BRAHMI LETTER DA;Lo;0;L;;;;;N;;;;;
+11025;BRAHMI LETTER DHA;Lo;0;L;;;;;N;;;;;
+11026;BRAHMI LETTER NA;Lo;0;L;;;;;N;;;;;
+11027;BRAHMI LETTER PA;Lo;0;L;;;;;N;;;;;
+11028;BRAHMI LETTER PHA;Lo;0;L;;;;;N;;;;;
+11029;BRAHMI LETTER BA;Lo;0;L;;;;;N;;;;;
+1102A;BRAHMI LETTER BHA;Lo;0;L;;;;;N;;;;;
+1102B;BRAHMI LETTER MA;Lo;0;L;;;;;N;;;;;
+1102C;BRAHMI LETTER YA;Lo;0;L;;;;;N;;;;;
+1102D;BRAHMI LETTER RA;Lo;0;L;;;;;N;;;;;
+1102E;BRAHMI LETTER LA;Lo;0;L;;;;;N;;;;;
+1102F;BRAHMI LETTER VA;Lo;0;L;;;;;N;;;;;
+11030;BRAHMI LETTER SHA;Lo;0;L;;;;;N;;;;;
+11031;BRAHMI LETTER SSA;Lo;0;L;;;;;N;;;;;
+11032;BRAHMI LETTER SA;Lo;0;L;;;;;N;;;;;
+11033;BRAHMI LETTER HA;Lo;0;L;;;;;N;;;;;
+11034;BRAHMI LETTER LLA;Lo;0;L;;;;;N;;;;;
+11035;BRAHMI LETTER OLD TAMIL LLLA;Lo;0;L;;;;;N;;;;;
+11036;BRAHMI LETTER OLD TAMIL RRA;Lo;0;L;;;;;N;;;;;
+11037;BRAHMI LETTER OLD TAMIL NNNA;Lo;0;L;;;;;N;;;;;
+11038;BRAHMI VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;;
+11039;BRAHMI VOWEL SIGN BHATTIPROLU AA;Mn;0;NSM;;;;;N;;;;;
+1103A;BRAHMI VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+1103B;BRAHMI VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+1103C;BRAHMI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1103D;BRAHMI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+1103E;BRAHMI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+1103F;BRAHMI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+11040;BRAHMI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+11041;BRAHMI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+11042;BRAHMI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+11043;BRAHMI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+11044;BRAHMI VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+11045;BRAHMI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+11046;BRAHMI VIRAMA;Mn;9;NSM;;;;;N;;;;;
+11047;BRAHMI DANDA;Po;0;L;;;;;N;;;;;
+11048;BRAHMI DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+11049;BRAHMI PUNCTUATION DOT;Po;0;L;;;;;N;;;;;
+1104A;BRAHMI PUNCTUATION DOUBLE DOT;Po;0;L;;;;;N;;;;;
+1104B;BRAHMI PUNCTUATION LINE;Po;0;L;;;;;N;;;;;
+1104C;BRAHMI PUNCTUATION CRESCENT BAR;Po;0;L;;;;;N;;;;;
+1104D;BRAHMI PUNCTUATION LOTUS;Po;0;L;;;;;N;;;;;
+11052;BRAHMI NUMBER ONE;No;0;ON;;;1;1;N;;;;;
+11053;BRAHMI NUMBER TWO;No;0;ON;;;2;2;N;;;;;
+11054;BRAHMI NUMBER THREE;No;0;ON;;;3;3;N;;;;;
+11055;BRAHMI NUMBER FOUR;No;0;ON;;;4;4;N;;;;;
+11056;BRAHMI NUMBER FIVE;No;0;ON;;;5;5;N;;;;;
+11057;BRAHMI NUMBER SIX;No;0;ON;;;6;6;N;;;;;
+11058;BRAHMI NUMBER SEVEN;No;0;ON;;;7;7;N;;;;;
+11059;BRAHMI NUMBER EIGHT;No;0;ON;;;8;8;N;;;;;
+1105A;BRAHMI NUMBER NINE;No;0;ON;;;9;9;N;;;;;
+1105B;BRAHMI NUMBER TEN;No;0;ON;;;;10;N;;;;;
+1105C;BRAHMI NUMBER TWENTY;No;0;ON;;;;20;N;;;;;
+1105D;BRAHMI NUMBER THIRTY;No;0;ON;;;;30;N;;;;;
+1105E;BRAHMI NUMBER FORTY;No;0;ON;;;;40;N;;;;;
+1105F;BRAHMI NUMBER FIFTY;No;0;ON;;;;50;N;;;;;
+11060;BRAHMI NUMBER SIXTY;No;0;ON;;;;60;N;;;;;
+11061;BRAHMI NUMBER SEVENTY;No;0;ON;;;;70;N;;;;;
+11062;BRAHMI NUMBER EIGHTY;No;0;ON;;;;80;N;;;;;
+11063;BRAHMI NUMBER NINETY;No;0;ON;;;;90;N;;;;;
+11064;BRAHMI NUMBER ONE HUNDRED;No;0;ON;;;;100;N;;;;;
+11065;BRAHMI NUMBER ONE THOUSAND;No;0;ON;;;;1000;N;;;;;
+11066;BRAHMI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+11067;BRAHMI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+11068;BRAHMI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+11069;BRAHMI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1106A;BRAHMI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1106B;BRAHMI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1106C;BRAHMI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1106D;BRAHMI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1106E;BRAHMI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1106F;BRAHMI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1107F;BRAHMI NUMBER JOINER;Mn;9;NSM;;;;;N;;;;;
+11080;KAITHI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+11081;KAITHI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+11082;KAITHI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+11083;KAITHI LETTER A;Lo;0;L;;;;;N;;;;;
+11084;KAITHI LETTER AA;Lo;0;L;;;;;N;;;;;
+11085;KAITHI LETTER I;Lo;0;L;;;;;N;;;;;
+11086;KAITHI LETTER II;Lo;0;L;;;;;N;;;;;
+11087;KAITHI LETTER U;Lo;0;L;;;;;N;;;;;
+11088;KAITHI LETTER UU;Lo;0;L;;;;;N;;;;;
+11089;KAITHI LETTER E;Lo;0;L;;;;;N;;;;;
+1108A;KAITHI LETTER AI;Lo;0;L;;;;;N;;;;;
+1108B;KAITHI LETTER O;Lo;0;L;;;;;N;;;;;
+1108C;KAITHI LETTER AU;Lo;0;L;;;;;N;;;;;
+1108D;KAITHI LETTER KA;Lo;0;L;;;;;N;;;;;
+1108E;KAITHI LETTER KHA;Lo;0;L;;;;;N;;;;;
+1108F;KAITHI LETTER GA;Lo;0;L;;;;;N;;;;;
+11090;KAITHI LETTER GHA;Lo;0;L;;;;;N;;;;;
+11091;KAITHI LETTER NGA;Lo;0;L;;;;;N;;;;;
+11092;KAITHI LETTER CA;Lo;0;L;;;;;N;;;;;
+11093;KAITHI LETTER CHA;Lo;0;L;;;;;N;;;;;
+11094;KAITHI LETTER JA;Lo;0;L;;;;;N;;;;;
+11095;KAITHI LETTER JHA;Lo;0;L;;;;;N;;;;;
+11096;KAITHI LETTER NYA;Lo;0;L;;;;;N;;;;;
+11097;KAITHI LETTER TTA;Lo;0;L;;;;;N;;;;;
+11098;KAITHI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+11099;KAITHI LETTER DDA;Lo;0;L;;;;;N;;;;;
+1109A;KAITHI LETTER DDDHA;Lo;0;L;11099 110BA;;;;N;;;;;
+1109B;KAITHI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+1109C;KAITHI LETTER RHA;Lo;0;L;1109B 110BA;;;;N;;;;;
+1109D;KAITHI LETTER NNA;Lo;0;L;;;;;N;;;;;
+1109E;KAITHI LETTER TA;Lo;0;L;;;;;N;;;;;
+1109F;KAITHI LETTER THA;Lo;0;L;;;;;N;;;;;
+110A0;KAITHI LETTER DA;Lo;0;L;;;;;N;;;;;
+110A1;KAITHI LETTER DHA;Lo;0;L;;;;;N;;;;;
+110A2;KAITHI LETTER NA;Lo;0;L;;;;;N;;;;;
+110A3;KAITHI LETTER PA;Lo;0;L;;;;;N;;;;;
+110A4;KAITHI LETTER PHA;Lo;0;L;;;;;N;;;;;
+110A5;KAITHI LETTER BA;Lo;0;L;;;;;N;;;;;
+110A6;KAITHI LETTER BHA;Lo;0;L;;;;;N;;;;;
+110A7;KAITHI LETTER MA;Lo;0;L;;;;;N;;;;;
+110A8;KAITHI LETTER YA;Lo;0;L;;;;;N;;;;;
+110A9;KAITHI LETTER RA;Lo;0;L;;;;;N;;;;;
+110AA;KAITHI LETTER LA;Lo;0;L;;;;;N;;;;;
+110AB;KAITHI LETTER VA;Lo;0;L;110A5 110BA;;;;N;;;;;
+110AC;KAITHI LETTER SHA;Lo;0;L;;;;;N;;;;;
+110AD;KAITHI LETTER SSA;Lo;0;L;;;;;N;;;;;
+110AE;KAITHI LETTER SA;Lo;0;L;;;;;N;;;;;
+110AF;KAITHI LETTER HA;Lo;0;L;;;;;N;;;;;
+110B0;KAITHI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+110B1;KAITHI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+110B2;KAITHI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+110B3;KAITHI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+110B4;KAITHI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+110B5;KAITHI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+110B6;KAITHI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+110B7;KAITHI VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+110B8;KAITHI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+110B9;KAITHI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+110BA;KAITHI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+110BB;KAITHI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
+110BC;KAITHI ENUMERATION SIGN;Po;0;L;;;;;N;;;;;
+110BD;KAITHI NUMBER SIGN;Cf;0;L;;;;;N;;;;;
+110BE;KAITHI SECTION MARK;Po;0;L;;;;;N;;;;;
+110BF;KAITHI DOUBLE SECTION MARK;Po;0;L;;;;;N;;;;;
+110C0;KAITHI DANDA;Po;0;L;;;;;N;;;;;
+110C1;KAITHI DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+110CD;KAITHI NUMBER SIGN ABOVE;Cf;0;L;;;;;N;;;;;
+110D0;SORA SOMPENG LETTER SAH;Lo;0;L;;;;;N;;;;;
+110D1;SORA SOMPENG LETTER TAH;Lo;0;L;;;;;N;;;;;
+110D2;SORA SOMPENG LETTER BAH;Lo;0;L;;;;;N;;;;;
+110D3;SORA SOMPENG LETTER CAH;Lo;0;L;;;;;N;;;;;
+110D4;SORA SOMPENG LETTER DAH;Lo;0;L;;;;;N;;;;;
+110D5;SORA SOMPENG LETTER GAH;Lo;0;L;;;;;N;;;;;
+110D6;SORA SOMPENG LETTER MAH;Lo;0;L;;;;;N;;;;;
+110D7;SORA SOMPENG LETTER NGAH;Lo;0;L;;;;;N;;;;;
+110D8;SORA SOMPENG LETTER LAH;Lo;0;L;;;;;N;;;;;
+110D9;SORA SOMPENG LETTER NAH;Lo;0;L;;;;;N;;;;;
+110DA;SORA SOMPENG LETTER VAH;Lo;0;L;;;;;N;;;;;
+110DB;SORA SOMPENG LETTER PAH;Lo;0;L;;;;;N;;;;;
+110DC;SORA SOMPENG LETTER YAH;Lo;0;L;;;;;N;;;;;
+110DD;SORA SOMPENG LETTER RAH;Lo;0;L;;;;;N;;;;;
+110DE;SORA SOMPENG LETTER HAH;Lo;0;L;;;;;N;;;;;
+110DF;SORA SOMPENG LETTER KAH;Lo;0;L;;;;;N;;;;;
+110E0;SORA SOMPENG LETTER JAH;Lo;0;L;;;;;N;;;;;
+110E1;SORA SOMPENG LETTER NYAH;Lo;0;L;;;;;N;;;;;
+110E2;SORA SOMPENG LETTER AH;Lo;0;L;;;;;N;;;;;
+110E3;SORA SOMPENG LETTER EEH;Lo;0;L;;;;;N;;;;;
+110E4;SORA SOMPENG LETTER IH;Lo;0;L;;;;;N;;;;;
+110E5;SORA SOMPENG LETTER UH;Lo;0;L;;;;;N;;;;;
+110E6;SORA SOMPENG LETTER OH;Lo;0;L;;;;;N;;;;;
+110E7;SORA SOMPENG LETTER EH;Lo;0;L;;;;;N;;;;;
+110E8;SORA SOMPENG LETTER MAE;Lo;0;L;;;;;N;;;;;
+110F0;SORA SOMPENG DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+110F1;SORA SOMPENG DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+110F2;SORA SOMPENG DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+110F3;SORA SOMPENG DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+110F4;SORA SOMPENG DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+110F5;SORA SOMPENG DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+110F6;SORA SOMPENG DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+110F7;SORA SOMPENG DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+110F8;SORA SOMPENG DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+110F9;SORA SOMPENG DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+11100;CHAKMA SIGN CANDRABINDU;Mn;230;NSM;;;;;N;;;;;
+11101;CHAKMA SIGN ANUSVARA;Mn;230;NSM;;;;;N;;;;;
+11102;CHAKMA SIGN VISARGA;Mn;230;NSM;;;;;N;;;;;
+11103;CHAKMA LETTER AA;Lo;0;L;;;;;N;;;;;
+11104;CHAKMA LETTER I;Lo;0;L;;;;;N;;;;;
+11105;CHAKMA LETTER U;Lo;0;L;;;;;N;;;;;
+11106;CHAKMA LETTER E;Lo;0;L;;;;;N;;;;;
+11107;CHAKMA LETTER KAA;Lo;0;L;;;;;N;;;;;
+11108;CHAKMA LETTER KHAA;Lo;0;L;;;;;N;;;;;
+11109;CHAKMA LETTER GAA;Lo;0;L;;;;;N;;;;;
+1110A;CHAKMA LETTER GHAA;Lo;0;L;;;;;N;;;;;
+1110B;CHAKMA LETTER NGAA;Lo;0;L;;;;;N;;;;;
+1110C;CHAKMA LETTER CAA;Lo;0;L;;;;;N;;;;;
+1110D;CHAKMA LETTER CHAA;Lo;0;L;;;;;N;;;;;
+1110E;CHAKMA LETTER JAA;Lo;0;L;;;;;N;;;;;
+1110F;CHAKMA LETTER JHAA;Lo;0;L;;;;;N;;;;;
+11110;CHAKMA LETTER NYAA;Lo;0;L;;;;;N;;;;;
+11111;CHAKMA LETTER TTAA;Lo;0;L;;;;;N;;;;;
+11112;CHAKMA LETTER TTHAA;Lo;0;L;;;;;N;;;;;
+11113;CHAKMA LETTER DDAA;Lo;0;L;;;;;N;;;;;
+11114;CHAKMA LETTER DDHAA;Lo;0;L;;;;;N;;;;;
+11115;CHAKMA LETTER NNAA;Lo;0;L;;;;;N;;;;;
+11116;CHAKMA LETTER TAA;Lo;0;L;;;;;N;;;;;
+11117;CHAKMA LETTER THAA;Lo;0;L;;;;;N;;;;;
+11118;CHAKMA LETTER DAA;Lo;0;L;;;;;N;;;;;
+11119;CHAKMA LETTER DHAA;Lo;0;L;;;;;N;;;;;
+1111A;CHAKMA LETTER NAA;Lo;0;L;;;;;N;;;;;
+1111B;CHAKMA LETTER PAA;Lo;0;L;;;;;N;;;;;
+1111C;CHAKMA LETTER PHAA;Lo;0;L;;;;;N;;;;;
+1111D;CHAKMA LETTER BAA;Lo;0;L;;;;;N;;;;;
+1111E;CHAKMA LETTER BHAA;Lo;0;L;;;;;N;;;;;
+1111F;CHAKMA LETTER MAA;Lo;0;L;;;;;N;;;;;
+11120;CHAKMA LETTER YYAA;Lo;0;L;;;;;N;;;;;
+11121;CHAKMA LETTER YAA;Lo;0;L;;;;;N;;;;;
+11122;CHAKMA LETTER RAA;Lo;0;L;;;;;N;;;;;
+11123;CHAKMA LETTER LAA;Lo;0;L;;;;;N;;;;;
+11124;CHAKMA LETTER WAA;Lo;0;L;;;;;N;;;;;
+11125;CHAKMA LETTER SAA;Lo;0;L;;;;;N;;;;;
+11126;CHAKMA LETTER HAA;Lo;0;L;;;;;N;;;;;
+11127;CHAKMA VOWEL SIGN A;Mn;0;NSM;;;;;N;;;;;
+11128;CHAKMA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+11129;CHAKMA VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+1112A;CHAKMA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1112B;CHAKMA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+1112C;CHAKMA VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+1112D;CHAKMA VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+1112E;CHAKMA VOWEL SIGN O;Mn;0;NSM;11131 11127;;;;N;;;;;
+1112F;CHAKMA VOWEL SIGN AU;Mn;0;NSM;11132 11127;;;;N;;;;;
+11130;CHAKMA VOWEL SIGN OI;Mn;0;NSM;;;;;N;;;;;
+11131;CHAKMA O MARK;Mn;0;NSM;;;;;N;;;;;
+11132;CHAKMA AU MARK;Mn;0;NSM;;;;;N;;;;;
+11133;CHAKMA VIRAMA;Mn;9;NSM;;;;;N;;;;;
+11134;CHAKMA MAAYYAA;Mn;9;NSM;;;;;N;;;;;
+11136;CHAKMA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+11137;CHAKMA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+11138;CHAKMA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+11139;CHAKMA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1113A;CHAKMA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1113B;CHAKMA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1113C;CHAKMA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1113D;CHAKMA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1113E;CHAKMA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1113F;CHAKMA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+11140;CHAKMA SECTION MARK;Po;0;L;;;;;N;;;;;
+11141;CHAKMA DANDA;Po;0;L;;;;;N;;;;;
+11142;CHAKMA DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+11143;CHAKMA QUESTION MARK;Po;0;L;;;;;N;;;;;
+11144;CHAKMA LETTER LHAA;Lo;0;L;;;;;N;;;;;
+11145;CHAKMA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+11146;CHAKMA VOWEL SIGN EI;Mc;0;L;;;;;N;;;;;
+11150;MAHAJANI LETTER A;Lo;0;L;;;;;N;;;;;
+11151;MAHAJANI LETTER I;Lo;0;L;;;;;N;;;;;
+11152;MAHAJANI LETTER U;Lo;0;L;;;;;N;;;;;
+11153;MAHAJANI LETTER E;Lo;0;L;;;;;N;;;;;
+11154;MAHAJANI LETTER O;Lo;0;L;;;;;N;;;;;
+11155;MAHAJANI LETTER KA;Lo;0;L;;;;;N;;;;;
+11156;MAHAJANI LETTER KHA;Lo;0;L;;;;;N;;;;;
+11157;MAHAJANI LETTER GA;Lo;0;L;;;;;N;;;;;
+11158;MAHAJANI LETTER GHA;Lo;0;L;;;;;N;;;;;
+11159;MAHAJANI LETTER CA;Lo;0;L;;;;;N;;;;;
+1115A;MAHAJANI LETTER CHA;Lo;0;L;;;;;N;;;;;
+1115B;MAHAJANI LETTER JA;Lo;0;L;;;;;N;;;;;
+1115C;MAHAJANI LETTER JHA;Lo;0;L;;;;;N;;;;;
+1115D;MAHAJANI LETTER NYA;Lo;0;L;;;;;N;;;;;
+1115E;MAHAJANI LETTER TTA;Lo;0;L;;;;;N;;;;;
+1115F;MAHAJANI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+11160;MAHAJANI LETTER DDA;Lo;0;L;;;;;N;;;;;
+11161;MAHAJANI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+11162;MAHAJANI LETTER NNA;Lo;0;L;;;;;N;;;;;
+11163;MAHAJANI LETTER TA;Lo;0;L;;;;;N;;;;;
+11164;MAHAJANI LETTER THA;Lo;0;L;;;;;N;;;;;
+11165;MAHAJANI LETTER DA;Lo;0;L;;;;;N;;;;;
+11166;MAHAJANI LETTER DHA;Lo;0;L;;;;;N;;;;;
+11167;MAHAJANI LETTER NA;Lo;0;L;;;;;N;;;;;
+11168;MAHAJANI LETTER PA;Lo;0;L;;;;;N;;;;;
+11169;MAHAJANI LETTER PHA;Lo;0;L;;;;;N;;;;;
+1116A;MAHAJANI LETTER BA;Lo;0;L;;;;;N;;;;;
+1116B;MAHAJANI LETTER BHA;Lo;0;L;;;;;N;;;;;
+1116C;MAHAJANI LETTER MA;Lo;0;L;;;;;N;;;;;
+1116D;MAHAJANI LETTER RA;Lo;0;L;;;;;N;;;;;
+1116E;MAHAJANI LETTER LA;Lo;0;L;;;;;N;;;;;
+1116F;MAHAJANI LETTER VA;Lo;0;L;;;;;N;;;;;
+11170;MAHAJANI LETTER SA;Lo;0;L;;;;;N;;;;;
+11171;MAHAJANI LETTER HA;Lo;0;L;;;;;N;;;;;
+11172;MAHAJANI LETTER RRA;Lo;0;L;;;;;N;;;;;
+11173;MAHAJANI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+11174;MAHAJANI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
+11175;MAHAJANI SECTION MARK;Po;0;L;;;;;N;;;;;
+11176;MAHAJANI LIGATURE SHRI;Lo;0;L;;;;;N;;;;;
+11180;SHARADA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+11181;SHARADA SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+11182;SHARADA SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+11183;SHARADA LETTER A;Lo;0;L;;;;;N;;;;;
+11184;SHARADA LETTER AA;Lo;0;L;;;;;N;;;;;
+11185;SHARADA LETTER I;Lo;0;L;;;;;N;;;;;
+11186;SHARADA LETTER II;Lo;0;L;;;;;N;;;;;
+11187;SHARADA LETTER U;Lo;0;L;;;;;N;;;;;
+11188;SHARADA LETTER UU;Lo;0;L;;;;;N;;;;;
+11189;SHARADA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+1118A;SHARADA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+1118B;SHARADA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+1118C;SHARADA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+1118D;SHARADA LETTER E;Lo;0;L;;;;;N;;;;;
+1118E;SHARADA LETTER AI;Lo;0;L;;;;;N;;;;;
+1118F;SHARADA LETTER O;Lo;0;L;;;;;N;;;;;
+11190;SHARADA LETTER AU;Lo;0;L;;;;;N;;;;;
+11191;SHARADA LETTER KA;Lo;0;L;;;;;N;;;;;
+11192;SHARADA LETTER KHA;Lo;0;L;;;;;N;;;;;
+11193;SHARADA LETTER GA;Lo;0;L;;;;;N;;;;;
+11194;SHARADA LETTER GHA;Lo;0;L;;;;;N;;;;;
+11195;SHARADA LETTER NGA;Lo;0;L;;;;;N;;;;;
+11196;SHARADA LETTER CA;Lo;0;L;;;;;N;;;;;
+11197;SHARADA LETTER CHA;Lo;0;L;;;;;N;;;;;
+11198;SHARADA LETTER JA;Lo;0;L;;;;;N;;;;;
+11199;SHARADA LETTER JHA;Lo;0;L;;;;;N;;;;;
+1119A;SHARADA LETTER NYA;Lo;0;L;;;;;N;;;;;
+1119B;SHARADA LETTER TTA;Lo;0;L;;;;;N;;;;;
+1119C;SHARADA LETTER TTHA;Lo;0;L;;;;;N;;;;;
+1119D;SHARADA LETTER DDA;Lo;0;L;;;;;N;;;;;
+1119E;SHARADA LETTER DDHA;Lo;0;L;;;;;N;;;;;
+1119F;SHARADA LETTER NNA;Lo;0;L;;;;;N;;;;;
+111A0;SHARADA LETTER TA;Lo;0;L;;;;;N;;;;;
+111A1;SHARADA LETTER THA;Lo;0;L;;;;;N;;;;;
+111A2;SHARADA LETTER DA;Lo;0;L;;;;;N;;;;;
+111A3;SHARADA LETTER DHA;Lo;0;L;;;;;N;;;;;
+111A4;SHARADA LETTER NA;Lo;0;L;;;;;N;;;;;
+111A5;SHARADA LETTER PA;Lo;0;L;;;;;N;;;;;
+111A6;SHARADA LETTER PHA;Lo;0;L;;;;;N;;;;;
+111A7;SHARADA LETTER BA;Lo;0;L;;;;;N;;;;;
+111A8;SHARADA LETTER BHA;Lo;0;L;;;;;N;;;;;
+111A9;SHARADA LETTER MA;Lo;0;L;;;;;N;;;;;
+111AA;SHARADA LETTER YA;Lo;0;L;;;;;N;;;;;
+111AB;SHARADA LETTER RA;Lo;0;L;;;;;N;;;;;
+111AC;SHARADA LETTER LA;Lo;0;L;;;;;N;;;;;
+111AD;SHARADA LETTER LLA;Lo;0;L;;;;;N;;;;;
+111AE;SHARADA LETTER VA;Lo;0;L;;;;;N;;;;;
+111AF;SHARADA LETTER SHA;Lo;0;L;;;;;N;;;;;
+111B0;SHARADA LETTER SSA;Lo;0;L;;;;;N;;;;;
+111B1;SHARADA LETTER SA;Lo;0;L;;;;;N;;;;;
+111B2;SHARADA LETTER HA;Lo;0;L;;;;;N;;;;;
+111B3;SHARADA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+111B4;SHARADA VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+111B5;SHARADA VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+111B6;SHARADA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+111B7;SHARADA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+111B8;SHARADA VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+111B9;SHARADA VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+111BA;SHARADA VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+111BB;SHARADA VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+111BC;SHARADA VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+111BD;SHARADA VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+111BE;SHARADA VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+111BF;SHARADA VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+111C0;SHARADA SIGN VIRAMA;Mc;9;L;;;;;N;;;;;
+111C1;SHARADA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+111C2;SHARADA SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;;
+111C3;SHARADA SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;;
+111C4;SHARADA OM;Lo;0;L;;;;;N;;;;;
+111C5;SHARADA DANDA;Po;0;L;;;;;N;;;;;
+111C6;SHARADA DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+111C7;SHARADA ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
+111C8;SHARADA SEPARATOR;Po;0;L;;;;;N;;;;;
+111C9;SHARADA SANDHI MARK;Mn;0;NSM;;;;;N;;;;;
+111CA;SHARADA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+111CB;SHARADA VOWEL MODIFIER MARK;Mn;0;NSM;;;;;N;;;;;
+111CC;SHARADA EXTRA SHORT VOWEL MARK;Mn;0;NSM;;;;;N;;;;;
+111CD;SHARADA SUTRA MARK;Po;0;L;;;;;N;;;;;
+111D0;SHARADA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+111D1;SHARADA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+111D2;SHARADA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+111D3;SHARADA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+111D4;SHARADA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+111D5;SHARADA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+111D6;SHARADA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+111D7;SHARADA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+111D8;SHARADA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+111D9;SHARADA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+111DA;SHARADA EKAM;Lo;0;L;;;;;N;;;;;
+111DB;SHARADA SIGN SIDDHAM;Po;0;L;;;;;N;;;;;
+111DC;SHARADA HEADSTROKE;Lo;0;L;;;;;N;;;;;
+111DD;SHARADA CONTINUATION SIGN;Po;0;L;;;;;N;;;;;
+111DE;SHARADA SECTION MARK-1;Po;0;L;;;;;N;;;;;
+111DF;SHARADA SECTION MARK-2;Po;0;L;;;;;N;;;;;
+111E1;SINHALA ARCHAIC DIGIT ONE;No;0;L;;;;1;N;;;;;
+111E2;SINHALA ARCHAIC DIGIT TWO;No;0;L;;;;2;N;;;;;
+111E3;SINHALA ARCHAIC DIGIT THREE;No;0;L;;;;3;N;;;;;
+111E4;SINHALA ARCHAIC DIGIT FOUR;No;0;L;;;;4;N;;;;;
+111E5;SINHALA ARCHAIC DIGIT FIVE;No;0;L;;;;5;N;;;;;
+111E6;SINHALA ARCHAIC DIGIT SIX;No;0;L;;;;6;N;;;;;
+111E7;SINHALA ARCHAIC DIGIT SEVEN;No;0;L;;;;7;N;;;;;
+111E8;SINHALA ARCHAIC DIGIT EIGHT;No;0;L;;;;8;N;;;;;
+111E9;SINHALA ARCHAIC DIGIT NINE;No;0;L;;;;9;N;;;;;
+111EA;SINHALA ARCHAIC NUMBER TEN;No;0;L;;;;10;N;;;;;
+111EB;SINHALA ARCHAIC NUMBER TWENTY;No;0;L;;;;20;N;;;;;
+111EC;SINHALA ARCHAIC NUMBER THIRTY;No;0;L;;;;30;N;;;;;
+111ED;SINHALA ARCHAIC NUMBER FORTY;No;0;L;;;;40;N;;;;;
+111EE;SINHALA ARCHAIC NUMBER FIFTY;No;0;L;;;;50;N;;;;;
+111EF;SINHALA ARCHAIC NUMBER SIXTY;No;0;L;;;;60;N;;;;;
+111F0;SINHALA ARCHAIC NUMBER SEVENTY;No;0;L;;;;70;N;;;;;
+111F1;SINHALA ARCHAIC NUMBER EIGHTY;No;0;L;;;;80;N;;;;;
+111F2;SINHALA ARCHAIC NUMBER NINETY;No;0;L;;;;90;N;;;;;
+111F3;SINHALA ARCHAIC NUMBER ONE HUNDRED;No;0;L;;;;100;N;;;;;
+111F4;SINHALA ARCHAIC NUMBER ONE THOUSAND;No;0;L;;;;1000;N;;;;;
+11200;KHOJKI LETTER A;Lo;0;L;;;;;N;;;;;
+11201;KHOJKI LETTER AA;Lo;0;L;;;;;N;;;;;
+11202;KHOJKI LETTER I;Lo;0;L;;;;;N;;;;;
+11203;KHOJKI LETTER U;Lo;0;L;;;;;N;;;;;
+11204;KHOJKI LETTER E;Lo;0;L;;;;;N;;;;;
+11205;KHOJKI LETTER AI;Lo;0;L;;;;;N;;;;;
+11206;KHOJKI LETTER O;Lo;0;L;;;;;N;;;;;
+11207;KHOJKI LETTER AU;Lo;0;L;;;;;N;;;;;
+11208;KHOJKI LETTER KA;Lo;0;L;;;;;N;;;;;
+11209;KHOJKI LETTER KHA;Lo;0;L;;;;;N;;;;;
+1120A;KHOJKI LETTER GA;Lo;0;L;;;;;N;;;;;
+1120B;KHOJKI LETTER GGA;Lo;0;L;;;;;N;;;;;
+1120C;KHOJKI LETTER GHA;Lo;0;L;;;;;N;;;;;
+1120D;KHOJKI LETTER NGA;Lo;0;L;;;;;N;;;;;
+1120E;KHOJKI LETTER CA;Lo;0;L;;;;;N;;;;;
+1120F;KHOJKI LETTER CHA;Lo;0;L;;;;;N;;;;;
+11210;KHOJKI LETTER JA;Lo;0;L;;;;;N;;;;;
+11211;KHOJKI LETTER JJA;Lo;0;L;;;;;N;;;;;
+11213;KHOJKI LETTER NYA;Lo;0;L;;;;;N;;;;;
+11214;KHOJKI LETTER TTA;Lo;0;L;;;;;N;;;;;
+11215;KHOJKI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+11216;KHOJKI LETTER DDA;Lo;0;L;;;;;N;;;;;
+11217;KHOJKI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+11218;KHOJKI LETTER NNA;Lo;0;L;;;;;N;;;;;
+11219;KHOJKI LETTER TA;Lo;0;L;;;;;N;;;;;
+1121A;KHOJKI LETTER THA;Lo;0;L;;;;;N;;;;;
+1121B;KHOJKI LETTER DA;Lo;0;L;;;;;N;;;;;
+1121C;KHOJKI LETTER DDDA;Lo;0;L;;;;;N;;;;;
+1121D;KHOJKI LETTER DHA;Lo;0;L;;;;;N;;;;;
+1121E;KHOJKI LETTER NA;Lo;0;L;;;;;N;;;;;
+1121F;KHOJKI LETTER PA;Lo;0;L;;;;;N;;;;;
+11220;KHOJKI LETTER PHA;Lo;0;L;;;;;N;;;;;
+11221;KHOJKI LETTER BA;Lo;0;L;;;;;N;;;;;
+11222;KHOJKI LETTER BBA;Lo;0;L;;;;;N;;;;;
+11223;KHOJKI LETTER BHA;Lo;0;L;;;;;N;;;;;
+11224;KHOJKI LETTER MA;Lo;0;L;;;;;N;;;;;
+11225;KHOJKI LETTER YA;Lo;0;L;;;;;N;;;;;
+11226;KHOJKI LETTER RA;Lo;0;L;;;;;N;;;;;
+11227;KHOJKI LETTER LA;Lo;0;L;;;;;N;;;;;
+11228;KHOJKI LETTER VA;Lo;0;L;;;;;N;;;;;
+11229;KHOJKI LETTER SA;Lo;0;L;;;;;N;;;;;
+1122A;KHOJKI LETTER HA;Lo;0;L;;;;;N;;;;;
+1122B;KHOJKI LETTER LLA;Lo;0;L;;;;;N;;;;;
+1122C;KHOJKI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+1122D;KHOJKI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+1122E;KHOJKI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+1122F;KHOJKI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+11230;KHOJKI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+11231;KHOJKI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+11232;KHOJKI VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+11233;KHOJKI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+11234;KHOJKI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+11235;KHOJKI SIGN VIRAMA;Mc;9;L;;;;;N;;;;;
+11236;KHOJKI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+11237;KHOJKI SIGN SHADDA;Mn;0;NSM;;;;;N;;;;;
+11238;KHOJKI DANDA;Po;0;L;;;;;N;;;;;
+11239;KHOJKI DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+1123A;KHOJKI WORD SEPARATOR;Po;0;L;;;;;N;;;;;
+1123B;KHOJKI SECTION MARK;Po;0;L;;;;;N;;;;;
+1123C;KHOJKI DOUBLE SECTION MARK;Po;0;L;;;;;N;;;;;
+1123D;KHOJKI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
+1123E;KHOJKI SIGN SUKUN;Mn;0;NSM;;;;;N;;;;;
+11280;MULTANI LETTER A;Lo;0;L;;;;;N;;;;;
+11281;MULTANI LETTER I;Lo;0;L;;;;;N;;;;;
+11282;MULTANI LETTER U;Lo;0;L;;;;;N;;;;;
+11283;MULTANI LETTER E;Lo;0;L;;;;;N;;;;;
+11284;MULTANI LETTER KA;Lo;0;L;;;;;N;;;;;
+11285;MULTANI LETTER KHA;Lo;0;L;;;;;N;;;;;
+11286;MULTANI LETTER GA;Lo;0;L;;;;;N;;;;;
+11288;MULTANI LETTER GHA;Lo;0;L;;;;;N;;;;;
+1128A;MULTANI LETTER CA;Lo;0;L;;;;;N;;;;;
+1128B;MULTANI LETTER CHA;Lo;0;L;;;;;N;;;;;
+1128C;MULTANI LETTER JA;Lo;0;L;;;;;N;;;;;
+1128D;MULTANI LETTER JJA;Lo;0;L;;;;;N;;;;;
+1128F;MULTANI LETTER NYA;Lo;0;L;;;;;N;;;;;
+11290;MULTANI LETTER TTA;Lo;0;L;;;;;N;;;;;
+11291;MULTANI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+11292;MULTANI LETTER DDA;Lo;0;L;;;;;N;;;;;
+11293;MULTANI LETTER DDDA;Lo;0;L;;;;;N;;;;;
+11294;MULTANI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+11295;MULTANI LETTER NNA;Lo;0;L;;;;;N;;;;;
+11296;MULTANI LETTER TA;Lo;0;L;;;;;N;;;;;
+11297;MULTANI LETTER THA;Lo;0;L;;;;;N;;;;;
+11298;MULTANI LETTER DA;Lo;0;L;;;;;N;;;;;
+11299;MULTANI LETTER DHA;Lo;0;L;;;;;N;;;;;
+1129A;MULTANI LETTER NA;Lo;0;L;;;;;N;;;;;
+1129B;MULTANI LETTER PA;Lo;0;L;;;;;N;;;;;
+1129C;MULTANI LETTER PHA;Lo;0;L;;;;;N;;;;;
+1129D;MULTANI LETTER BA;Lo;0;L;;;;;N;;;;;
+1129F;MULTANI LETTER BHA;Lo;0;L;;;;;N;;;;;
+112A0;MULTANI LETTER MA;Lo;0;L;;;;;N;;;;;
+112A1;MULTANI LETTER YA;Lo;0;L;;;;;N;;;;;
+112A2;MULTANI LETTER RA;Lo;0;L;;;;;N;;;;;
+112A3;MULTANI LETTER LA;Lo;0;L;;;;;N;;;;;
+112A4;MULTANI LETTER VA;Lo;0;L;;;;;N;;;;;
+112A5;MULTANI LETTER SA;Lo;0;L;;;;;N;;;;;
+112A6;MULTANI LETTER HA;Lo;0;L;;;;;N;;;;;
+112A7;MULTANI LETTER RRA;Lo;0;L;;;;;N;;;;;
+112A8;MULTANI LETTER RHA;Lo;0;L;;;;;N;;;;;
+112A9;MULTANI SECTION MARK;Po;0;L;;;;;N;;;;;
+112B0;KHUDAWADI LETTER A;Lo;0;L;;;;;N;;;;;
+112B1;KHUDAWADI LETTER AA;Lo;0;L;;;;;N;;;;;
+112B2;KHUDAWADI LETTER I;Lo;0;L;;;;;N;;;;;
+112B3;KHUDAWADI LETTER II;Lo;0;L;;;;;N;;;;;
+112B4;KHUDAWADI LETTER U;Lo;0;L;;;;;N;;;;;
+112B5;KHUDAWADI LETTER UU;Lo;0;L;;;;;N;;;;;
+112B6;KHUDAWADI LETTER E;Lo;0;L;;;;;N;;;;;
+112B7;KHUDAWADI LETTER AI;Lo;0;L;;;;;N;;;;;
+112B8;KHUDAWADI LETTER O;Lo;0;L;;;;;N;;;;;
+112B9;KHUDAWADI LETTER AU;Lo;0;L;;;;;N;;;;;
+112BA;KHUDAWADI LETTER KA;Lo;0;L;;;;;N;;;;;
+112BB;KHUDAWADI LETTER KHA;Lo;0;L;;;;;N;;;;;
+112BC;KHUDAWADI LETTER GA;Lo;0;L;;;;;N;;;;;
+112BD;KHUDAWADI LETTER GGA;Lo;0;L;;;;;N;;;;;
+112BE;KHUDAWADI LETTER GHA;Lo;0;L;;;;;N;;;;;
+112BF;KHUDAWADI LETTER NGA;Lo;0;L;;;;;N;;;;;
+112C0;KHUDAWADI LETTER CA;Lo;0;L;;;;;N;;;;;
+112C1;KHUDAWADI LETTER CHA;Lo;0;L;;;;;N;;;;;
+112C2;KHUDAWADI LETTER JA;Lo;0;L;;;;;N;;;;;
+112C3;KHUDAWADI LETTER JJA;Lo;0;L;;;;;N;;;;;
+112C4;KHUDAWADI LETTER JHA;Lo;0;L;;;;;N;;;;;
+112C5;KHUDAWADI LETTER NYA;Lo;0;L;;;;;N;;;;;
+112C6;KHUDAWADI LETTER TTA;Lo;0;L;;;;;N;;;;;
+112C7;KHUDAWADI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+112C8;KHUDAWADI LETTER DDA;Lo;0;L;;;;;N;;;;;
+112C9;KHUDAWADI LETTER DDDA;Lo;0;L;;;;;N;;;;;
+112CA;KHUDAWADI LETTER RRA;Lo;0;L;;;;;N;;;;;
+112CB;KHUDAWADI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+112CC;KHUDAWADI LETTER NNA;Lo;0;L;;;;;N;;;;;
+112CD;KHUDAWADI LETTER TA;Lo;0;L;;;;;N;;;;;
+112CE;KHUDAWADI LETTER THA;Lo;0;L;;;;;N;;;;;
+112CF;KHUDAWADI LETTER DA;Lo;0;L;;;;;N;;;;;
+112D0;KHUDAWADI LETTER DHA;Lo;0;L;;;;;N;;;;;
+112D1;KHUDAWADI LETTER NA;Lo;0;L;;;;;N;;;;;
+112D2;KHUDAWADI LETTER PA;Lo;0;L;;;;;N;;;;;
+112D3;KHUDAWADI LETTER PHA;Lo;0;L;;;;;N;;;;;
+112D4;KHUDAWADI LETTER BA;Lo;0;L;;;;;N;;;;;
+112D5;KHUDAWADI LETTER BBA;Lo;0;L;;;;;N;;;;;
+112D6;KHUDAWADI LETTER BHA;Lo;0;L;;;;;N;;;;;
+112D7;KHUDAWADI LETTER MA;Lo;0;L;;;;;N;;;;;
+112D8;KHUDAWADI LETTER YA;Lo;0;L;;;;;N;;;;;
+112D9;KHUDAWADI LETTER RA;Lo;0;L;;;;;N;;;;;
+112DA;KHUDAWADI LETTER LA;Lo;0;L;;;;;N;;;;;
+112DB;KHUDAWADI LETTER VA;Lo;0;L;;;;;N;;;;;
+112DC;KHUDAWADI LETTER SHA;Lo;0;L;;;;;N;;;;;
+112DD;KHUDAWADI LETTER SA;Lo;0;L;;;;;N;;;;;
+112DE;KHUDAWADI LETTER HA;Lo;0;L;;;;;N;;;;;
+112DF;KHUDAWADI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+112E0;KHUDAWADI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+112E1;KHUDAWADI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+112E2;KHUDAWADI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+112E3;KHUDAWADI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+112E4;KHUDAWADI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+112E5;KHUDAWADI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+112E6;KHUDAWADI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+112E7;KHUDAWADI VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+112E8;KHUDAWADI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+112E9;KHUDAWADI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+112EA;KHUDAWADI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+112F0;KHUDAWADI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+112F1;KHUDAWADI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+112F2;KHUDAWADI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+112F3;KHUDAWADI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+112F4;KHUDAWADI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+112F5;KHUDAWADI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+112F6;KHUDAWADI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+112F7;KHUDAWADI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+112F8;KHUDAWADI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+112F9;KHUDAWADI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+11300;GRANTHA SIGN COMBINING ANUSVARA ABOVE;Mn;0;NSM;;;;;N;;;;;
+11301;GRANTHA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+11302;GRANTHA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+11303;GRANTHA SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+11305;GRANTHA LETTER A;Lo;0;L;;;;;N;;;;;
+11306;GRANTHA LETTER AA;Lo;0;L;;;;;N;;;;;
+11307;GRANTHA LETTER I;Lo;0;L;;;;;N;;;;;
+11308;GRANTHA LETTER II;Lo;0;L;;;;;N;;;;;
+11309;GRANTHA LETTER U;Lo;0;L;;;;;N;;;;;
+1130A;GRANTHA LETTER UU;Lo;0;L;;;;;N;;;;;
+1130B;GRANTHA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+1130C;GRANTHA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+1130F;GRANTHA LETTER EE;Lo;0;L;;;;;N;;;;;
+11310;GRANTHA LETTER AI;Lo;0;L;;;;;N;;;;;
+11313;GRANTHA LETTER OO;Lo;0;L;;;;;N;;;;;
+11314;GRANTHA LETTER AU;Lo;0;L;;;;;N;;;;;
+11315;GRANTHA LETTER KA;Lo;0;L;;;;;N;;;;;
+11316;GRANTHA LETTER KHA;Lo;0;L;;;;;N;;;;;
+11317;GRANTHA LETTER GA;Lo;0;L;;;;;N;;;;;
+11318;GRANTHA LETTER GHA;Lo;0;L;;;;;N;;;;;
+11319;GRANTHA LETTER NGA;Lo;0;L;;;;;N;;;;;
+1131A;GRANTHA LETTER CA;Lo;0;L;;;;;N;;;;;
+1131B;GRANTHA LETTER CHA;Lo;0;L;;;;;N;;;;;
+1131C;GRANTHA LETTER JA;Lo;0;L;;;;;N;;;;;
+1131D;GRANTHA LETTER JHA;Lo;0;L;;;;;N;;;;;
+1131E;GRANTHA LETTER NYA;Lo;0;L;;;;;N;;;;;
+1131F;GRANTHA LETTER TTA;Lo;0;L;;;;;N;;;;;
+11320;GRANTHA LETTER TTHA;Lo;0;L;;;;;N;;;;;
+11321;GRANTHA LETTER DDA;Lo;0;L;;;;;N;;;;;
+11322;GRANTHA LETTER DDHA;Lo;0;L;;;;;N;;;;;
+11323;GRANTHA LETTER NNA;Lo;0;L;;;;;N;;;;;
+11324;GRANTHA LETTER TA;Lo;0;L;;;;;N;;;;;
+11325;GRANTHA LETTER THA;Lo;0;L;;;;;N;;;;;
+11326;GRANTHA LETTER DA;Lo;0;L;;;;;N;;;;;
+11327;GRANTHA LETTER DHA;Lo;0;L;;;;;N;;;;;
+11328;GRANTHA LETTER NA;Lo;0;L;;;;;N;;;;;
+1132A;GRANTHA LETTER PA;Lo;0;L;;;;;N;;;;;
+1132B;GRANTHA LETTER PHA;Lo;0;L;;;;;N;;;;;
+1132C;GRANTHA LETTER BA;Lo;0;L;;;;;N;;;;;
+1132D;GRANTHA LETTER BHA;Lo;0;L;;;;;N;;;;;
+1132E;GRANTHA LETTER MA;Lo;0;L;;;;;N;;;;;
+1132F;GRANTHA LETTER YA;Lo;0;L;;;;;N;;;;;
+11330;GRANTHA LETTER RA;Lo;0;L;;;;;N;;;;;
+11332;GRANTHA LETTER LA;Lo;0;L;;;;;N;;;;;
+11333;GRANTHA LETTER LLA;Lo;0;L;;;;;N;;;;;
+11335;GRANTHA LETTER VA;Lo;0;L;;;;;N;;;;;
+11336;GRANTHA LETTER SHA;Lo;0;L;;;;;N;;;;;
+11337;GRANTHA LETTER SSA;Lo;0;L;;;;;N;;;;;
+11338;GRANTHA LETTER SA;Lo;0;L;;;;;N;;;;;
+11339;GRANTHA LETTER HA;Lo;0;L;;;;;N;;;;;
+1133B;COMBINING BINDU BELOW;Mn;7;NSM;;;;;N;;;;;
+1133C;GRANTHA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+1133D;GRANTHA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+1133E;GRANTHA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+1133F;GRANTHA VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+11340;GRANTHA VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+11341;GRANTHA VOWEL SIGN U;Mc;0;L;;;;;N;;;;;
+11342;GRANTHA VOWEL SIGN UU;Mc;0;L;;;;;N;;;;;
+11343;GRANTHA VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;;
+11344;GRANTHA VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;;
+11347;GRANTHA VOWEL SIGN EE;Mc;0;L;;;;;N;;;;;
+11348;GRANTHA VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+1134B;GRANTHA VOWEL SIGN OO;Mc;0;L;11347 1133E;;;;N;;;;;
+1134C;GRANTHA VOWEL SIGN AU;Mc;0;L;11347 11357;;;;N;;;;;
+1134D;GRANTHA SIGN VIRAMA;Mc;9;L;;;;;N;;;;;
+11350;GRANTHA OM;Lo;0;L;;;;;N;;;;;
+11357;GRANTHA AU LENGTH MARK;Mc;0;L;;;;;N;;;;;
+1135D;GRANTHA SIGN PLUTA;Lo;0;L;;;;;N;;;;;
+1135E;GRANTHA LETTER VEDIC ANUSVARA;Lo;0;L;;;;;N;;;;;
+1135F;GRANTHA LETTER VEDIC DOUBLE ANUSVARA;Lo;0;L;;;;;N;;;;;
+11360;GRANTHA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+11361;GRANTHA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+11362;GRANTHA VOWEL SIGN VOCALIC L;Mc;0;L;;;;;N;;;;;
+11363;GRANTHA VOWEL SIGN VOCALIC LL;Mc;0;L;;;;;N;;;;;
+11366;COMBINING GRANTHA DIGIT ZERO;Mn;230;NSM;;;;;N;;;;;
+11367;COMBINING GRANTHA DIGIT ONE;Mn;230;NSM;;;;;N;;;;;
+11368;COMBINING GRANTHA DIGIT TWO;Mn;230;NSM;;;;;N;;;;;
+11369;COMBINING GRANTHA DIGIT THREE;Mn;230;NSM;;;;;N;;;;;
+1136A;COMBINING GRANTHA DIGIT FOUR;Mn;230;NSM;;;;;N;;;;;
+1136B;COMBINING GRANTHA DIGIT FIVE;Mn;230;NSM;;;;;N;;;;;
+1136C;COMBINING GRANTHA DIGIT SIX;Mn;230;NSM;;;;;N;;;;;
+11370;COMBINING GRANTHA LETTER A;Mn;230;NSM;;;;;N;;;;;
+11371;COMBINING GRANTHA LETTER KA;Mn;230;NSM;;;;;N;;;;;
+11372;COMBINING GRANTHA LETTER NA;Mn;230;NSM;;;;;N;;;;;
+11373;COMBINING GRANTHA LETTER VI;Mn;230;NSM;;;;;N;;;;;
+11374;COMBINING GRANTHA LETTER PA;Mn;230;NSM;;;;;N;;;;;
+11400;NEWA LETTER A;Lo;0;L;;;;;N;;;;;
+11401;NEWA LETTER AA;Lo;0;L;;;;;N;;;;;
+11402;NEWA LETTER I;Lo;0;L;;;;;N;;;;;
+11403;NEWA LETTER II;Lo;0;L;;;;;N;;;;;
+11404;NEWA LETTER U;Lo;0;L;;;;;N;;;;;
+11405;NEWA LETTER UU;Lo;0;L;;;;;N;;;;;
+11406;NEWA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+11407;NEWA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+11408;NEWA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+11409;NEWA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+1140A;NEWA LETTER E;Lo;0;L;;;;;N;;;;;
+1140B;NEWA LETTER AI;Lo;0;L;;;;;N;;;;;
+1140C;NEWA LETTER O;Lo;0;L;;;;;N;;;;;
+1140D;NEWA LETTER AU;Lo;0;L;;;;;N;;;;;
+1140E;NEWA LETTER KA;Lo;0;L;;;;;N;;;;;
+1140F;NEWA LETTER KHA;Lo;0;L;;;;;N;;;;;
+11410;NEWA LETTER GA;Lo;0;L;;;;;N;;;;;
+11411;NEWA LETTER GHA;Lo;0;L;;;;;N;;;;;
+11412;NEWA LETTER NGA;Lo;0;L;;;;;N;;;;;
+11413;NEWA LETTER NGHA;Lo;0;L;;;;;N;;;;;
+11414;NEWA LETTER CA;Lo;0;L;;;;;N;;;;;
+11415;NEWA LETTER CHA;Lo;0;L;;;;;N;;;;;
+11416;NEWA LETTER JA;Lo;0;L;;;;;N;;;;;
+11417;NEWA LETTER JHA;Lo;0;L;;;;;N;;;;;
+11418;NEWA LETTER NYA;Lo;0;L;;;;;N;;;;;
+11419;NEWA LETTER NYHA;Lo;0;L;;;;;N;;;;;
+1141A;NEWA LETTER TTA;Lo;0;L;;;;;N;;;;;
+1141B;NEWA LETTER TTHA;Lo;0;L;;;;;N;;;;;
+1141C;NEWA LETTER DDA;Lo;0;L;;;;;N;;;;;
+1141D;NEWA LETTER DDHA;Lo;0;L;;;;;N;;;;;
+1141E;NEWA LETTER NNA;Lo;0;L;;;;;N;;;;;
+1141F;NEWA LETTER TA;Lo;0;L;;;;;N;;;;;
+11420;NEWA LETTER THA;Lo;0;L;;;;;N;;;;;
+11421;NEWA LETTER DA;Lo;0;L;;;;;N;;;;;
+11422;NEWA LETTER DHA;Lo;0;L;;;;;N;;;;;
+11423;NEWA LETTER NA;Lo;0;L;;;;;N;;;;;
+11424;NEWA LETTER NHA;Lo;0;L;;;;;N;;;;;
+11425;NEWA LETTER PA;Lo;0;L;;;;;N;;;;;
+11426;NEWA LETTER PHA;Lo;0;L;;;;;N;;;;;
+11427;NEWA LETTER BA;Lo;0;L;;;;;N;;;;;
+11428;NEWA LETTER BHA;Lo;0;L;;;;;N;;;;;
+11429;NEWA LETTER MA;Lo;0;L;;;;;N;;;;;
+1142A;NEWA LETTER MHA;Lo;0;L;;;;;N;;;;;
+1142B;NEWA LETTER YA;Lo;0;L;;;;;N;;;;;
+1142C;NEWA LETTER RA;Lo;0;L;;;;;N;;;;;
+1142D;NEWA LETTER RHA;Lo;0;L;;;;;N;;;;;
+1142E;NEWA LETTER LA;Lo;0;L;;;;;N;;;;;
+1142F;NEWA LETTER LHA;Lo;0;L;;;;;N;;;;;
+11430;NEWA LETTER WA;Lo;0;L;;;;;N;;;;;
+11431;NEWA LETTER SHA;Lo;0;L;;;;;N;;;;;
+11432;NEWA LETTER SSA;Lo;0;L;;;;;N;;;;;
+11433;NEWA LETTER SA;Lo;0;L;;;;;N;;;;;
+11434;NEWA LETTER HA;Lo;0;L;;;;;N;;;;;
+11435;NEWA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+11436;NEWA VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+11437;NEWA VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+11438;NEWA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+11439;NEWA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+1143A;NEWA VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+1143B;NEWA VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+1143C;NEWA VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+1143D;NEWA VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+1143E;NEWA VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+1143F;NEWA VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+11440;NEWA VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+11441;NEWA VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+11442;NEWA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+11443;NEWA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+11444;NEWA SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+11445;NEWA SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+11446;NEWA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+11447;NEWA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+11448;NEWA SIGN FINAL ANUSVARA;Lo;0;L;;;;;N;;;;;
+11449;NEWA OM;Lo;0;L;;;;;N;;;;;
+1144A;NEWA SIDDHI;Lo;0;L;;;;;N;;;;;
+1144B;NEWA DANDA;Po;0;L;;;;;N;;;;;
+1144C;NEWA DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+1144D;NEWA COMMA;Po;0;L;;;;;N;;;;;
+1144E;NEWA GAP FILLER;Po;0;L;;;;;N;;;;;
+1144F;NEWA ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
+11450;NEWA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+11451;NEWA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+11452;NEWA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+11453;NEWA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+11454;NEWA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+11455;NEWA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+11456;NEWA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+11457;NEWA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+11458;NEWA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+11459;NEWA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1145B;NEWA PLACEHOLDER MARK;Po;0;L;;;;;N;;;;;
+1145D;NEWA INSERTION SIGN;Po;0;L;;;;;N;;;;;
+1145E;NEWA SANDHI MARK;Mn;230;NSM;;;;;N;;;;;
+1145F;NEWA LETTER VEDIC ANUSVARA;Lo;0;L;;;;;N;;;;;
+11480;TIRHUTA ANJI;Lo;0;L;;;;;N;;;;;
+11481;TIRHUTA LETTER A;Lo;0;L;;;;;N;;;;;
+11482;TIRHUTA LETTER AA;Lo;0;L;;;;;N;;;;;
+11483;TIRHUTA LETTER I;Lo;0;L;;;;;N;;;;;
+11484;TIRHUTA LETTER II;Lo;0;L;;;;;N;;;;;
+11485;TIRHUTA LETTER U;Lo;0;L;;;;;N;;;;;
+11486;TIRHUTA LETTER UU;Lo;0;L;;;;;N;;;;;
+11487;TIRHUTA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+11488;TIRHUTA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+11489;TIRHUTA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+1148A;TIRHUTA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+1148B;TIRHUTA LETTER E;Lo;0;L;;;;;N;;;;;
+1148C;TIRHUTA LETTER AI;Lo;0;L;;;;;N;;;;;
+1148D;TIRHUTA LETTER O;Lo;0;L;;;;;N;;;;;
+1148E;TIRHUTA LETTER AU;Lo;0;L;;;;;N;;;;;
+1148F;TIRHUTA LETTER KA;Lo;0;L;;;;;N;;;;;
+11490;TIRHUTA LETTER KHA;Lo;0;L;;;;;N;;;;;
+11491;TIRHUTA LETTER GA;Lo;0;L;;;;;N;;;;;
+11492;TIRHUTA LETTER GHA;Lo;0;L;;;;;N;;;;;
+11493;TIRHUTA LETTER NGA;Lo;0;L;;;;;N;;;;;
+11494;TIRHUTA LETTER CA;Lo;0;L;;;;;N;;;;;
+11495;TIRHUTA LETTER CHA;Lo;0;L;;;;;N;;;;;
+11496;TIRHUTA LETTER JA;Lo;0;L;;;;;N;;;;;
+11497;TIRHUTA LETTER JHA;Lo;0;L;;;;;N;;;;;
+11498;TIRHUTA LETTER NYA;Lo;0;L;;;;;N;;;;;
+11499;TIRHUTA LETTER TTA;Lo;0;L;;;;;N;;;;;
+1149A;TIRHUTA LETTER TTHA;Lo;0;L;;;;;N;;;;;
+1149B;TIRHUTA LETTER DDA;Lo;0;L;;;;;N;;;;;
+1149C;TIRHUTA LETTER DDHA;Lo;0;L;;;;;N;;;;;
+1149D;TIRHUTA LETTER NNA;Lo;0;L;;;;;N;;;;;
+1149E;TIRHUTA LETTER TA;Lo;0;L;;;;;N;;;;;
+1149F;TIRHUTA LETTER THA;Lo;0;L;;;;;N;;;;;
+114A0;TIRHUTA LETTER DA;Lo;0;L;;;;;N;;;;;
+114A1;TIRHUTA LETTER DHA;Lo;0;L;;;;;N;;;;;
+114A2;TIRHUTA LETTER NA;Lo;0;L;;;;;N;;;;;
+114A3;TIRHUTA LETTER PA;Lo;0;L;;;;;N;;;;;
+114A4;TIRHUTA LETTER PHA;Lo;0;L;;;;;N;;;;;
+114A5;TIRHUTA LETTER BA;Lo;0;L;;;;;N;;;;;
+114A6;TIRHUTA LETTER BHA;Lo;0;L;;;;;N;;;;;
+114A7;TIRHUTA LETTER MA;Lo;0;L;;;;;N;;;;;
+114A8;TIRHUTA LETTER YA;Lo;0;L;;;;;N;;;;;
+114A9;TIRHUTA LETTER RA;Lo;0;L;;;;;N;;;;;
+114AA;TIRHUTA LETTER LA;Lo;0;L;;;;;N;;;;;
+114AB;TIRHUTA LETTER VA;Lo;0;L;;;;;N;;;;;
+114AC;TIRHUTA LETTER SHA;Lo;0;L;;;;;N;;;;;
+114AD;TIRHUTA LETTER SSA;Lo;0;L;;;;;N;;;;;
+114AE;TIRHUTA LETTER SA;Lo;0;L;;;;;N;;;;;
+114AF;TIRHUTA LETTER HA;Lo;0;L;;;;;N;;;;;
+114B0;TIRHUTA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+114B1;TIRHUTA VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+114B2;TIRHUTA VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+114B3;TIRHUTA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+114B4;TIRHUTA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+114B5;TIRHUTA VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+114B6;TIRHUTA VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+114B7;TIRHUTA VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+114B8;TIRHUTA VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+114B9;TIRHUTA VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+114BA;TIRHUTA VOWEL SIGN SHORT E;Mn;0;NSM;;;;;N;;;;;
+114BB;TIRHUTA VOWEL SIGN AI;Mc;0;L;114B9 114BA;;;;N;;;;;
+114BC;TIRHUTA VOWEL SIGN O;Mc;0;L;114B9 114B0;;;;N;;;;;
+114BD;TIRHUTA VOWEL SIGN SHORT O;Mc;0;L;;;;;N;;;;;
+114BE;TIRHUTA VOWEL SIGN AU;Mc;0;L;114B9 114BD;;;;N;;;;;
+114BF;TIRHUTA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+114C0;TIRHUTA SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+114C1;TIRHUTA SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+114C2;TIRHUTA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+114C3;TIRHUTA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+114C4;TIRHUTA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+114C5;TIRHUTA GVANG;Lo;0;L;;;;;N;;;;;
+114C6;TIRHUTA ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
+114C7;TIRHUTA OM;Lo;0;L;;;;;N;;;;;
+114D0;TIRHUTA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+114D1;TIRHUTA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+114D2;TIRHUTA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+114D3;TIRHUTA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+114D4;TIRHUTA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+114D5;TIRHUTA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+114D6;TIRHUTA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+114D7;TIRHUTA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+114D8;TIRHUTA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+114D9;TIRHUTA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+11580;SIDDHAM LETTER A;Lo;0;L;;;;;N;;;;;
+11581;SIDDHAM LETTER AA;Lo;0;L;;;;;N;;;;;
+11582;SIDDHAM LETTER I;Lo;0;L;;;;;N;;;;;
+11583;SIDDHAM LETTER II;Lo;0;L;;;;;N;;;;;
+11584;SIDDHAM LETTER U;Lo;0;L;;;;;N;;;;;
+11585;SIDDHAM LETTER UU;Lo;0;L;;;;;N;;;;;
+11586;SIDDHAM LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+11587;SIDDHAM LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+11588;SIDDHAM LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+11589;SIDDHAM LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+1158A;SIDDHAM LETTER E;Lo;0;L;;;;;N;;;;;
+1158B;SIDDHAM LETTER AI;Lo;0;L;;;;;N;;;;;
+1158C;SIDDHAM LETTER O;Lo;0;L;;;;;N;;;;;
+1158D;SIDDHAM LETTER AU;Lo;0;L;;;;;N;;;;;
+1158E;SIDDHAM LETTER KA;Lo;0;L;;;;;N;;;;;
+1158F;SIDDHAM LETTER KHA;Lo;0;L;;;;;N;;;;;
+11590;SIDDHAM LETTER GA;Lo;0;L;;;;;N;;;;;
+11591;SIDDHAM LETTER GHA;Lo;0;L;;;;;N;;;;;
+11592;SIDDHAM LETTER NGA;Lo;0;L;;;;;N;;;;;
+11593;SIDDHAM LETTER CA;Lo;0;L;;;;;N;;;;;
+11594;SIDDHAM LETTER CHA;Lo;0;L;;;;;N;;;;;
+11595;SIDDHAM LETTER JA;Lo;0;L;;;;;N;;;;;
+11596;SIDDHAM LETTER JHA;Lo;0;L;;;;;N;;;;;
+11597;SIDDHAM LETTER NYA;Lo;0;L;;;;;N;;;;;
+11598;SIDDHAM LETTER TTA;Lo;0;L;;;;;N;;;;;
+11599;SIDDHAM LETTER TTHA;Lo;0;L;;;;;N;;;;;
+1159A;SIDDHAM LETTER DDA;Lo;0;L;;;;;N;;;;;
+1159B;SIDDHAM LETTER DDHA;Lo;0;L;;;;;N;;;;;
+1159C;SIDDHAM LETTER NNA;Lo;0;L;;;;;N;;;;;
+1159D;SIDDHAM LETTER TA;Lo;0;L;;;;;N;;;;;
+1159E;SIDDHAM LETTER THA;Lo;0;L;;;;;N;;;;;
+1159F;SIDDHAM LETTER DA;Lo;0;L;;;;;N;;;;;
+115A0;SIDDHAM LETTER DHA;Lo;0;L;;;;;N;;;;;
+115A1;SIDDHAM LETTER NA;Lo;0;L;;;;;N;;;;;
+115A2;SIDDHAM LETTER PA;Lo;0;L;;;;;N;;;;;
+115A3;SIDDHAM LETTER PHA;Lo;0;L;;;;;N;;;;;
+115A4;SIDDHAM LETTER BA;Lo;0;L;;;;;N;;;;;
+115A5;SIDDHAM LETTER BHA;Lo;0;L;;;;;N;;;;;
+115A6;SIDDHAM LETTER MA;Lo;0;L;;;;;N;;;;;
+115A7;SIDDHAM LETTER YA;Lo;0;L;;;;;N;;;;;
+115A8;SIDDHAM LETTER RA;Lo;0;L;;;;;N;;;;;
+115A9;SIDDHAM LETTER LA;Lo;0;L;;;;;N;;;;;
+115AA;SIDDHAM LETTER VA;Lo;0;L;;;;;N;;;;;
+115AB;SIDDHAM LETTER SHA;Lo;0;L;;;;;N;;;;;
+115AC;SIDDHAM LETTER SSA;Lo;0;L;;;;;N;;;;;
+115AD;SIDDHAM LETTER SA;Lo;0;L;;;;;N;;;;;
+115AE;SIDDHAM LETTER HA;Lo;0;L;;;;;N;;;;;
+115AF;SIDDHAM VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+115B0;SIDDHAM VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+115B1;SIDDHAM VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+115B2;SIDDHAM VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+115B3;SIDDHAM VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+115B4;SIDDHAM VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+115B5;SIDDHAM VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+115B8;SIDDHAM VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+115B9;SIDDHAM VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+115BA;SIDDHAM VOWEL SIGN O;Mc;0;L;115B8 115AF;;;;N;;;;;
+115BB;SIDDHAM VOWEL SIGN AU;Mc;0;L;115B9 115AF;;;;N;;;;;
+115BC;SIDDHAM SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+115BD;SIDDHAM SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+115BE;SIDDHAM SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+115BF;SIDDHAM SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+115C0;SIDDHAM SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+115C1;SIDDHAM SIGN SIDDHAM;Po;0;L;;;;;N;;;;;
+115C2;SIDDHAM DANDA;Po;0;L;;;;;N;;;;;
+115C3;SIDDHAM DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+115C4;SIDDHAM SEPARATOR DOT;Po;0;L;;;;;N;;;;;
+115C5;SIDDHAM SEPARATOR BAR;Po;0;L;;;;;N;;;;;
+115C6;SIDDHAM REPETITION MARK-1;Po;0;L;;;;;N;;;;;
+115C7;SIDDHAM REPETITION MARK-2;Po;0;L;;;;;N;;;;;
+115C8;SIDDHAM REPETITION MARK-3;Po;0;L;;;;;N;;;;;
+115C9;SIDDHAM END OF TEXT MARK;Po;0;L;;;;;N;;;;;
+115CA;SIDDHAM SECTION MARK WITH TRIDENT AND U-SHAPED ORNAMENTS;Po;0;L;;;;;N;;;;;
+115CB;SIDDHAM SECTION MARK WITH TRIDENT AND DOTTED CRESCENTS;Po;0;L;;;;;N;;;;;
+115CC;SIDDHAM SECTION MARK WITH RAYS AND DOTTED CRESCENTS;Po;0;L;;;;;N;;;;;
+115CD;SIDDHAM SECTION MARK WITH RAYS AND DOTTED DOUBLE CRESCENTS;Po;0;L;;;;;N;;;;;
+115CE;SIDDHAM SECTION MARK WITH RAYS AND DOTTED TRIPLE CRESCENTS;Po;0;L;;;;;N;;;;;
+115CF;SIDDHAM SECTION MARK DOUBLE RING;Po;0;L;;;;;N;;;;;
+115D0;SIDDHAM SECTION MARK DOUBLE RING WITH RAYS;Po;0;L;;;;;N;;;;;
+115D1;SIDDHAM SECTION MARK WITH DOUBLE CRESCENTS;Po;0;L;;;;;N;;;;;
+115D2;SIDDHAM SECTION MARK WITH TRIPLE CRESCENTS;Po;0;L;;;;;N;;;;;
+115D3;SIDDHAM SECTION MARK WITH QUADRUPLE CRESCENTS;Po;0;L;;;;;N;;;;;
+115D4;SIDDHAM SECTION MARK WITH SEPTUPLE CRESCENTS;Po;0;L;;;;;N;;;;;
+115D5;SIDDHAM SECTION MARK WITH CIRCLES AND RAYS;Po;0;L;;;;;N;;;;;
+115D6;SIDDHAM SECTION MARK WITH CIRCLES AND TWO ENCLOSURES;Po;0;L;;;;;N;;;;;
+115D7;SIDDHAM SECTION MARK WITH CIRCLES AND FOUR ENCLOSURES;Po;0;L;;;;;N;;;;;
+115D8;SIDDHAM LETTER THREE-CIRCLE ALTERNATE I;Lo;0;L;;;;;N;;;;;
+115D9;SIDDHAM LETTER TWO-CIRCLE ALTERNATE I;Lo;0;L;;;;;N;;;;;
+115DA;SIDDHAM LETTER TWO-CIRCLE ALTERNATE II;Lo;0;L;;;;;N;;;;;
+115DB;SIDDHAM LETTER ALTERNATE U;Lo;0;L;;;;;N;;;;;
+115DC;SIDDHAM VOWEL SIGN ALTERNATE U;Mn;0;NSM;;;;;N;;;;;
+115DD;SIDDHAM VOWEL SIGN ALTERNATE UU;Mn;0;NSM;;;;;N;;;;;
+11600;MODI LETTER A;Lo;0;L;;;;;N;;;;;
+11601;MODI LETTER AA;Lo;0;L;;;;;N;;;;;
+11602;MODI LETTER I;Lo;0;L;;;;;N;;;;;
+11603;MODI LETTER II;Lo;0;L;;;;;N;;;;;
+11604;MODI LETTER U;Lo;0;L;;;;;N;;;;;
+11605;MODI LETTER UU;Lo;0;L;;;;;N;;;;;
+11606;MODI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+11607;MODI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+11608;MODI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+11609;MODI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+1160A;MODI LETTER E;Lo;0;L;;;;;N;;;;;
+1160B;MODI LETTER AI;Lo;0;L;;;;;N;;;;;
+1160C;MODI LETTER O;Lo;0;L;;;;;N;;;;;
+1160D;MODI LETTER AU;Lo;0;L;;;;;N;;;;;
+1160E;MODI LETTER KA;Lo;0;L;;;;;N;;;;;
+1160F;MODI LETTER KHA;Lo;0;L;;;;;N;;;;;
+11610;MODI LETTER GA;Lo;0;L;;;;;N;;;;;
+11611;MODI LETTER GHA;Lo;0;L;;;;;N;;;;;
+11612;MODI LETTER NGA;Lo;0;L;;;;;N;;;;;
+11613;MODI LETTER CA;Lo;0;L;;;;;N;;;;;
+11614;MODI LETTER CHA;Lo;0;L;;;;;N;;;;;
+11615;MODI LETTER JA;Lo;0;L;;;;;N;;;;;
+11616;MODI LETTER JHA;Lo;0;L;;;;;N;;;;;
+11617;MODI LETTER NYA;Lo;0;L;;;;;N;;;;;
+11618;MODI LETTER TTA;Lo;0;L;;;;;N;;;;;
+11619;MODI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+1161A;MODI LETTER DDA;Lo;0;L;;;;;N;;;;;
+1161B;MODI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+1161C;MODI LETTER NNA;Lo;0;L;;;;;N;;;;;
+1161D;MODI LETTER TA;Lo;0;L;;;;;N;;;;;
+1161E;MODI LETTER THA;Lo;0;L;;;;;N;;;;;
+1161F;MODI LETTER DA;Lo;0;L;;;;;N;;;;;
+11620;MODI LETTER DHA;Lo;0;L;;;;;N;;;;;
+11621;MODI LETTER NA;Lo;0;L;;;;;N;;;;;
+11622;MODI LETTER PA;Lo;0;L;;;;;N;;;;;
+11623;MODI LETTER PHA;Lo;0;L;;;;;N;;;;;
+11624;MODI LETTER BA;Lo;0;L;;;;;N;;;;;
+11625;MODI LETTER BHA;Lo;0;L;;;;;N;;;;;
+11626;MODI LETTER MA;Lo;0;L;;;;;N;;;;;
+11627;MODI LETTER YA;Lo;0;L;;;;;N;;;;;
+11628;MODI LETTER RA;Lo;0;L;;;;;N;;;;;
+11629;MODI LETTER LA;Lo;0;L;;;;;N;;;;;
+1162A;MODI LETTER VA;Lo;0;L;;;;;N;;;;;
+1162B;MODI LETTER SHA;Lo;0;L;;;;;N;;;;;
+1162C;MODI LETTER SSA;Lo;0;L;;;;;N;;;;;
+1162D;MODI LETTER SA;Lo;0;L;;;;;N;;;;;
+1162E;MODI LETTER HA;Lo;0;L;;;;;N;;;;;
+1162F;MODI LETTER LLA;Lo;0;L;;;;;N;;;;;
+11630;MODI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+11631;MODI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+11632;MODI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+11633;MODI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+11634;MODI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+11635;MODI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+11636;MODI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+11637;MODI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+11638;MODI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+11639;MODI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+1163A;MODI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+1163B;MODI VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+1163C;MODI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+1163D;MODI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+1163E;MODI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+1163F;MODI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+11640;MODI SIGN ARDHACANDRA;Mn;0;NSM;;;;;N;;;;;
+11641;MODI DANDA;Po;0;L;;;;;N;;;;;
+11642;MODI DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+11643;MODI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
+11644;MODI SIGN HUVA;Lo;0;L;;;;;N;;;;;
+11650;MODI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+11651;MODI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+11652;MODI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+11653;MODI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+11654;MODI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+11655;MODI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+11656;MODI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+11657;MODI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+11658;MODI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+11659;MODI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+11660;MONGOLIAN BIRGA WITH ORNAMENT;Po;0;ON;;;;;N;;;;;
+11661;MONGOLIAN ROTATED BIRGA;Po;0;ON;;;;;N;;;;;
+11662;MONGOLIAN DOUBLE BIRGA WITH ORNAMENT;Po;0;ON;;;;;N;;;;;
+11663;MONGOLIAN TRIPLE BIRGA WITH ORNAMENT;Po;0;ON;;;;;N;;;;;
+11664;MONGOLIAN BIRGA WITH DOUBLE ORNAMENT;Po;0;ON;;;;;N;;;;;
+11665;MONGOLIAN ROTATED BIRGA WITH ORNAMENT;Po;0;ON;;;;;N;;;;;
+11666;MONGOLIAN ROTATED BIRGA WITH DOUBLE ORNAMENT;Po;0;ON;;;;;N;;;;;
+11667;MONGOLIAN INVERTED BIRGA;Po;0;ON;;;;;N;;;;;
+11668;MONGOLIAN INVERTED BIRGA WITH DOUBLE ORNAMENT;Po;0;ON;;;;;N;;;;;
+11669;MONGOLIAN SWIRL BIRGA;Po;0;ON;;;;;N;;;;;
+1166A;MONGOLIAN SWIRL BIRGA WITH ORNAMENT;Po;0;ON;;;;;N;;;;;
+1166B;MONGOLIAN SWIRL BIRGA WITH DOUBLE ORNAMENT;Po;0;ON;;;;;N;;;;;
+1166C;MONGOLIAN TURNED SWIRL BIRGA WITH DOUBLE ORNAMENT;Po;0;ON;;;;;N;;;;;
+11680;TAKRI LETTER A;Lo;0;L;;;;;N;;;;;
+11681;TAKRI LETTER AA;Lo;0;L;;;;;N;;;;;
+11682;TAKRI LETTER I;Lo;0;L;;;;;N;;;;;
+11683;TAKRI LETTER II;Lo;0;L;;;;;N;;;;;
+11684;TAKRI LETTER U;Lo;0;L;;;;;N;;;;;
+11685;TAKRI LETTER UU;Lo;0;L;;;;;N;;;;;
+11686;TAKRI LETTER E;Lo;0;L;;;;;N;;;;;
+11687;TAKRI LETTER AI;Lo;0;L;;;;;N;;;;;
+11688;TAKRI LETTER O;Lo;0;L;;;;;N;;;;;
+11689;TAKRI LETTER AU;Lo;0;L;;;;;N;;;;;
+1168A;TAKRI LETTER KA;Lo;0;L;;;;;N;;;;;
+1168B;TAKRI LETTER KHA;Lo;0;L;;;;;N;;;;;
+1168C;TAKRI LETTER GA;Lo;0;L;;;;;N;;;;;
+1168D;TAKRI LETTER GHA;Lo;0;L;;;;;N;;;;;
+1168E;TAKRI LETTER NGA;Lo;0;L;;;;;N;;;;;
+1168F;TAKRI LETTER CA;Lo;0;L;;;;;N;;;;;
+11690;TAKRI LETTER CHA;Lo;0;L;;;;;N;;;;;
+11691;TAKRI LETTER JA;Lo;0;L;;;;;N;;;;;
+11692;TAKRI LETTER JHA;Lo;0;L;;;;;N;;;;;
+11693;TAKRI LETTER NYA;Lo;0;L;;;;;N;;;;;
+11694;TAKRI LETTER TTA;Lo;0;L;;;;;N;;;;;
+11695;TAKRI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+11696;TAKRI LETTER DDA;Lo;0;L;;;;;N;;;;;
+11697;TAKRI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+11698;TAKRI LETTER NNA;Lo;0;L;;;;;N;;;;;
+11699;TAKRI LETTER TA;Lo;0;L;;;;;N;;;;;
+1169A;TAKRI LETTER THA;Lo;0;L;;;;;N;;;;;
+1169B;TAKRI LETTER DA;Lo;0;L;;;;;N;;;;;
+1169C;TAKRI LETTER DHA;Lo;0;L;;;;;N;;;;;
+1169D;TAKRI LETTER NA;Lo;0;L;;;;;N;;;;;
+1169E;TAKRI LETTER PA;Lo;0;L;;;;;N;;;;;
+1169F;TAKRI LETTER PHA;Lo;0;L;;;;;N;;;;;
+116A0;TAKRI LETTER BA;Lo;0;L;;;;;N;;;;;
+116A1;TAKRI LETTER BHA;Lo;0;L;;;;;N;;;;;
+116A2;TAKRI LETTER MA;Lo;0;L;;;;;N;;;;;
+116A3;TAKRI LETTER YA;Lo;0;L;;;;;N;;;;;
+116A4;TAKRI LETTER RA;Lo;0;L;;;;;N;;;;;
+116A5;TAKRI LETTER LA;Lo;0;L;;;;;N;;;;;
+116A6;TAKRI LETTER VA;Lo;0;L;;;;;N;;;;;
+116A7;TAKRI LETTER SHA;Lo;0;L;;;;;N;;;;;
+116A8;TAKRI LETTER SA;Lo;0;L;;;;;N;;;;;
+116A9;TAKRI LETTER HA;Lo;0;L;;;;;N;;;;;
+116AA;TAKRI LETTER RRA;Lo;0;L;;;;;N;;;;;
+116AB;TAKRI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+116AC;TAKRI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+116AD;TAKRI VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;;
+116AE;TAKRI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+116AF;TAKRI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+116B0;TAKRI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+116B1;TAKRI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+116B2;TAKRI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+116B3;TAKRI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+116B4;TAKRI VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+116B5;TAKRI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+116B6;TAKRI SIGN VIRAMA;Mc;9;L;;;;;N;;;;;
+116B7;TAKRI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+116B8;TAKRI LETTER ARCHAIC KHA;Lo;0;L;;;;;N;;;;;
+116C0;TAKRI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+116C1;TAKRI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+116C2;TAKRI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+116C3;TAKRI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+116C4;TAKRI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+116C5;TAKRI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+116C6;TAKRI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+116C7;TAKRI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+116C8;TAKRI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+116C9;TAKRI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+11700;AHOM LETTER KA;Lo;0;L;;;;;N;;;;;
+11701;AHOM LETTER KHA;Lo;0;L;;;;;N;;;;;
+11702;AHOM LETTER NGA;Lo;0;L;;;;;N;;;;;
+11703;AHOM LETTER NA;Lo;0;L;;;;;N;;;;;
+11704;AHOM LETTER TA;Lo;0;L;;;;;N;;;;;
+11705;AHOM LETTER ALTERNATE TA;Lo;0;L;;;;;N;;;;;
+11706;AHOM LETTER PA;Lo;0;L;;;;;N;;;;;
+11707;AHOM LETTER PHA;Lo;0;L;;;;;N;;;;;
+11708;AHOM LETTER BA;Lo;0;L;;;;;N;;;;;
+11709;AHOM LETTER MA;Lo;0;L;;;;;N;;;;;
+1170A;AHOM LETTER JA;Lo;0;L;;;;;N;;;;;
+1170B;AHOM LETTER CHA;Lo;0;L;;;;;N;;;;;
+1170C;AHOM LETTER THA;Lo;0;L;;;;;N;;;;;
+1170D;AHOM LETTER RA;Lo;0;L;;;;;N;;;;;
+1170E;AHOM LETTER LA;Lo;0;L;;;;;N;;;;;
+1170F;AHOM LETTER SA;Lo;0;L;;;;;N;;;;;
+11710;AHOM LETTER NYA;Lo;0;L;;;;;N;;;;;
+11711;AHOM LETTER HA;Lo;0;L;;;;;N;;;;;
+11712;AHOM LETTER A;Lo;0;L;;;;;N;;;;;
+11713;AHOM LETTER DA;Lo;0;L;;;;;N;;;;;
+11714;AHOM LETTER DHA;Lo;0;L;;;;;N;;;;;
+11715;AHOM LETTER GA;Lo;0;L;;;;;N;;;;;
+11716;AHOM LETTER ALTERNATE GA;Lo;0;L;;;;;N;;;;;
+11717;AHOM LETTER GHA;Lo;0;L;;;;;N;;;;;
+11718;AHOM LETTER BHA;Lo;0;L;;;;;N;;;;;
+11719;AHOM LETTER JHA;Lo;0;L;;;;;N;;;;;
+1171A;AHOM LETTER ALTERNATE BA;Lo;0;L;;;;;N;;;;;
+1171D;AHOM CONSONANT SIGN MEDIAL LA;Mn;0;NSM;;;;;N;;;;;
+1171E;AHOM CONSONANT SIGN MEDIAL RA;Mn;0;NSM;;;;;N;;;;;
+1171F;AHOM CONSONANT SIGN MEDIAL LIGATING RA;Mn;0;NSM;;;;;N;;;;;
+11720;AHOM VOWEL SIGN A;Mc;0;L;;;;;N;;;;;
+11721;AHOM VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+11722;AHOM VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+11723;AHOM VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+11724;AHOM VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+11725;AHOM VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+11726;AHOM VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+11727;AHOM VOWEL SIGN AW;Mn;0;NSM;;;;;N;;;;;
+11728;AHOM VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+11729;AHOM VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+1172A;AHOM VOWEL SIGN AM;Mn;0;NSM;;;;;N;;;;;
+1172B;AHOM SIGN KILLER;Mn;9;NSM;;;;;N;;;;;
+11730;AHOM DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+11731;AHOM DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+11732;AHOM DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+11733;AHOM DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+11734;AHOM DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+11735;AHOM DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+11736;AHOM DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+11737;AHOM DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+11738;AHOM DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+11739;AHOM DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1173A;AHOM NUMBER TEN;No;0;L;;;;10;N;;;;;
+1173B;AHOM NUMBER TWENTY;No;0;L;;;;20;N;;;;;
+1173C;AHOM SIGN SMALL SECTION;Po;0;L;;;;;N;;;;;
+1173D;AHOM SIGN SECTION;Po;0;L;;;;;N;;;;;
+1173E;AHOM SIGN RULAI;Po;0;L;;;;;N;;;;;
+1173F;AHOM SYMBOL VI;So;0;L;;;;;N;;;;;
+11800;DOGRA LETTER A;Lo;0;L;;;;;N;;;;;
+11801;DOGRA LETTER AA;Lo;0;L;;;;;N;;;;;
+11802;DOGRA LETTER I;Lo;0;L;;;;;N;;;;;
+11803;DOGRA LETTER II;Lo;0;L;;;;;N;;;;;
+11804;DOGRA LETTER U;Lo;0;L;;;;;N;;;;;
+11805;DOGRA LETTER UU;Lo;0;L;;;;;N;;;;;
+11806;DOGRA LETTER E;Lo;0;L;;;;;N;;;;;
+11807;DOGRA LETTER AI;Lo;0;L;;;;;N;;;;;
+11808;DOGRA LETTER O;Lo;0;L;;;;;N;;;;;
+11809;DOGRA LETTER AU;Lo;0;L;;;;;N;;;;;
+1180A;DOGRA LETTER KA;Lo;0;L;;;;;N;;;;;
+1180B;DOGRA LETTER KHA;Lo;0;L;;;;;N;;;;;
+1180C;DOGRA LETTER GA;Lo;0;L;;;;;N;;;;;
+1180D;DOGRA LETTER GHA;Lo;0;L;;;;;N;;;;;
+1180E;DOGRA LETTER NGA;Lo;0;L;;;;;N;;;;;
+1180F;DOGRA LETTER CA;Lo;0;L;;;;;N;;;;;
+11810;DOGRA LETTER CHA;Lo;0;L;;;;;N;;;;;
+11811;DOGRA LETTER JA;Lo;0;L;;;;;N;;;;;
+11812;DOGRA LETTER JHA;Lo;0;L;;;;;N;;;;;
+11813;DOGRA LETTER NYA;Lo;0;L;;;;;N;;;;;
+11814;DOGRA LETTER TTA;Lo;0;L;;;;;N;;;;;
+11815;DOGRA LETTER TTHA;Lo;0;L;;;;;N;;;;;
+11816;DOGRA LETTER DDA;Lo;0;L;;;;;N;;;;;
+11817;DOGRA LETTER DDHA;Lo;0;L;;;;;N;;;;;
+11818;DOGRA LETTER NNA;Lo;0;L;;;;;N;;;;;
+11819;DOGRA LETTER TA;Lo;0;L;;;;;N;;;;;
+1181A;DOGRA LETTER THA;Lo;0;L;;;;;N;;;;;
+1181B;DOGRA LETTER DA;Lo;0;L;;;;;N;;;;;
+1181C;DOGRA LETTER DHA;Lo;0;L;;;;;N;;;;;
+1181D;DOGRA LETTER NA;Lo;0;L;;;;;N;;;;;
+1181E;DOGRA LETTER PA;Lo;0;L;;;;;N;;;;;
+1181F;DOGRA LETTER PHA;Lo;0;L;;;;;N;;;;;
+11820;DOGRA LETTER BA;Lo;0;L;;;;;N;;;;;
+11821;DOGRA LETTER BHA;Lo;0;L;;;;;N;;;;;
+11822;DOGRA LETTER MA;Lo;0;L;;;;;N;;;;;
+11823;DOGRA LETTER YA;Lo;0;L;;;;;N;;;;;
+11824;DOGRA LETTER RA;Lo;0;L;;;;;N;;;;;
+11825;DOGRA LETTER LA;Lo;0;L;;;;;N;;;;;
+11826;DOGRA LETTER VA;Lo;0;L;;;;;N;;;;;
+11827;DOGRA LETTER SHA;Lo;0;L;;;;;N;;;;;
+11828;DOGRA LETTER SSA;Lo;0;L;;;;;N;;;;;
+11829;DOGRA LETTER SA;Lo;0;L;;;;;N;;;;;
+1182A;DOGRA LETTER HA;Lo;0;L;;;;;N;;;;;
+1182B;DOGRA LETTER RRA;Lo;0;L;;;;;N;;;;;
+1182C;DOGRA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+1182D;DOGRA VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+1182E;DOGRA VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+1182F;DOGRA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+11830;DOGRA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+11831;DOGRA VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+11832;DOGRA VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+11833;DOGRA VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+11834;DOGRA VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+11835;DOGRA VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+11836;DOGRA VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+11837;DOGRA SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+11838;DOGRA SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+11839;DOGRA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+1183A;DOGRA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+1183B;DOGRA ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
+118A0;WARANG CITI CAPITAL LETTER NGAA;Lu;0;L;;;;;N;;;;118C0;
+118A1;WARANG CITI CAPITAL LETTER A;Lu;0;L;;;;;N;;;;118C1;
+118A2;WARANG CITI CAPITAL LETTER WI;Lu;0;L;;;;;N;;;;118C2;
+118A3;WARANG CITI CAPITAL LETTER YU;Lu;0;L;;;;;N;;;;118C3;
+118A4;WARANG CITI CAPITAL LETTER YA;Lu;0;L;;;;;N;;;;118C4;
+118A5;WARANG CITI CAPITAL LETTER YO;Lu;0;L;;;;;N;;;;118C5;
+118A6;WARANG CITI CAPITAL LETTER II;Lu;0;L;;;;;N;;;;118C6;
+118A7;WARANG CITI CAPITAL LETTER UU;Lu;0;L;;;;;N;;;;118C7;
+118A8;WARANG CITI CAPITAL LETTER E;Lu;0;L;;;;;N;;;;118C8;
+118A9;WARANG CITI CAPITAL LETTER O;Lu;0;L;;;;;N;;;;118C9;
+118AA;WARANG CITI CAPITAL LETTER ANG;Lu;0;L;;;;;N;;;;118CA;
+118AB;WARANG CITI CAPITAL LETTER GA;Lu;0;L;;;;;N;;;;118CB;
+118AC;WARANG CITI CAPITAL LETTER KO;Lu;0;L;;;;;N;;;;118CC;
+118AD;WARANG CITI CAPITAL LETTER ENY;Lu;0;L;;;;;N;;;;118CD;
+118AE;WARANG CITI CAPITAL LETTER YUJ;Lu;0;L;;;;;N;;;;118CE;
+118AF;WARANG CITI CAPITAL LETTER UC;Lu;0;L;;;;;N;;;;118CF;
+118B0;WARANG CITI CAPITAL LETTER ENN;Lu;0;L;;;;;N;;;;118D0;
+118B1;WARANG CITI CAPITAL LETTER ODD;Lu;0;L;;;;;N;;;;118D1;
+118B2;WARANG CITI CAPITAL LETTER TTE;Lu;0;L;;;;;N;;;;118D2;
+118B3;WARANG CITI CAPITAL LETTER NUNG;Lu;0;L;;;;;N;;;;118D3;
+118B4;WARANG CITI CAPITAL LETTER DA;Lu;0;L;;;;;N;;;;118D4;
+118B5;WARANG CITI CAPITAL LETTER AT;Lu;0;L;;;;;N;;;;118D5;
+118B6;WARANG CITI CAPITAL LETTER AM;Lu;0;L;;;;;N;;;;118D6;
+118B7;WARANG CITI CAPITAL LETTER BU;Lu;0;L;;;;;N;;;;118D7;
+118B8;WARANG CITI CAPITAL LETTER PU;Lu;0;L;;;;;N;;;;118D8;
+118B9;WARANG CITI CAPITAL LETTER HIYO;Lu;0;L;;;;;N;;;;118D9;
+118BA;WARANG CITI CAPITAL LETTER HOLO;Lu;0;L;;;;;N;;;;118DA;
+118BB;WARANG CITI CAPITAL LETTER HORR;Lu;0;L;;;;;N;;;;118DB;
+118BC;WARANG CITI CAPITAL LETTER HAR;Lu;0;L;;;;;N;;;;118DC;
+118BD;WARANG CITI CAPITAL LETTER SSUU;Lu;0;L;;;;;N;;;;118DD;
+118BE;WARANG CITI CAPITAL LETTER SII;Lu;0;L;;;;;N;;;;118DE;
+118BF;WARANG CITI CAPITAL LETTER VIYO;Lu;0;L;;;;;N;;;;118DF;
+118C0;WARANG CITI SMALL LETTER NGAA;Ll;0;L;;;;;N;;;118A0;;118A0
+118C1;WARANG CITI SMALL LETTER A;Ll;0;L;;;;;N;;;118A1;;118A1
+118C2;WARANG CITI SMALL LETTER WI;Ll;0;L;;;;;N;;;118A2;;118A2
+118C3;WARANG CITI SMALL LETTER YU;Ll;0;L;;;;;N;;;118A3;;118A3
+118C4;WARANG CITI SMALL LETTER YA;Ll;0;L;;;;;N;;;118A4;;118A4
+118C5;WARANG CITI SMALL LETTER YO;Ll;0;L;;;;;N;;;118A5;;118A5
+118C6;WARANG CITI SMALL LETTER II;Ll;0;L;;;;;N;;;118A6;;118A6
+118C7;WARANG CITI SMALL LETTER UU;Ll;0;L;;;;;N;;;118A7;;118A7
+118C8;WARANG CITI SMALL LETTER E;Ll;0;L;;;;;N;;;118A8;;118A8
+118C9;WARANG CITI SMALL LETTER O;Ll;0;L;;;;;N;;;118A9;;118A9
+118CA;WARANG CITI SMALL LETTER ANG;Ll;0;L;;;;;N;;;118AA;;118AA
+118CB;WARANG CITI SMALL LETTER GA;Ll;0;L;;;;;N;;;118AB;;118AB
+118CC;WARANG CITI SMALL LETTER KO;Ll;0;L;;;;;N;;;118AC;;118AC
+118CD;WARANG CITI SMALL LETTER ENY;Ll;0;L;;;;;N;;;118AD;;118AD
+118CE;WARANG CITI SMALL LETTER YUJ;Ll;0;L;;;;;N;;;118AE;;118AE
+118CF;WARANG CITI SMALL LETTER UC;Ll;0;L;;;;;N;;;118AF;;118AF
+118D0;WARANG CITI SMALL LETTER ENN;Ll;0;L;;;;;N;;;118B0;;118B0
+118D1;WARANG CITI SMALL LETTER ODD;Ll;0;L;;;;;N;;;118B1;;118B1
+118D2;WARANG CITI SMALL LETTER TTE;Ll;0;L;;;;;N;;;118B2;;118B2
+118D3;WARANG CITI SMALL LETTER NUNG;Ll;0;L;;;;;N;;;118B3;;118B3
+118D4;WARANG CITI SMALL LETTER DA;Ll;0;L;;;;;N;;;118B4;;118B4
+118D5;WARANG CITI SMALL LETTER AT;Ll;0;L;;;;;N;;;118B5;;118B5
+118D6;WARANG CITI SMALL LETTER AM;Ll;0;L;;;;;N;;;118B6;;118B6
+118D7;WARANG CITI SMALL LETTER BU;Ll;0;L;;;;;N;;;118B7;;118B7
+118D8;WARANG CITI SMALL LETTER PU;Ll;0;L;;;;;N;;;118B8;;118B8
+118D9;WARANG CITI SMALL LETTER HIYO;Ll;0;L;;;;;N;;;118B9;;118B9
+118DA;WARANG CITI SMALL LETTER HOLO;Ll;0;L;;;;;N;;;118BA;;118BA
+118DB;WARANG CITI SMALL LETTER HORR;Ll;0;L;;;;;N;;;118BB;;118BB
+118DC;WARANG CITI SMALL LETTER HAR;Ll;0;L;;;;;N;;;118BC;;118BC
+118DD;WARANG CITI SMALL LETTER SSUU;Ll;0;L;;;;;N;;;118BD;;118BD
+118DE;WARANG CITI SMALL LETTER SII;Ll;0;L;;;;;N;;;118BE;;118BE
+118DF;WARANG CITI SMALL LETTER VIYO;Ll;0;L;;;;;N;;;118BF;;118BF
+118E0;WARANG CITI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+118E1;WARANG CITI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+118E2;WARANG CITI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+118E3;WARANG CITI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+118E4;WARANG CITI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+118E5;WARANG CITI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+118E6;WARANG CITI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+118E7;WARANG CITI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+118E8;WARANG CITI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+118E9;WARANG CITI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+118EA;WARANG CITI NUMBER TEN;No;0;L;;;;10;N;;;;;
+118EB;WARANG CITI NUMBER TWENTY;No;0;L;;;;20;N;;;;;
+118EC;WARANG CITI NUMBER THIRTY;No;0;L;;;;30;N;;;;;
+118ED;WARANG CITI NUMBER FORTY;No;0;L;;;;40;N;;;;;
+118EE;WARANG CITI NUMBER FIFTY;No;0;L;;;;50;N;;;;;
+118EF;WARANG CITI NUMBER SIXTY;No;0;L;;;;60;N;;;;;
+118F0;WARANG CITI NUMBER SEVENTY;No;0;L;;;;70;N;;;;;
+118F1;WARANG CITI NUMBER EIGHTY;No;0;L;;;;80;N;;;;;
+118F2;WARANG CITI NUMBER NINETY;No;0;L;;;;90;N;;;;;
+118FF;WARANG CITI OM;Lo;0;L;;;;;N;;;;;
+119A0;NANDINAGARI LETTER A;Lo;0;L;;;;;N;;;;;
+119A1;NANDINAGARI LETTER AA;Lo;0;L;;;;;N;;;;;
+119A2;NANDINAGARI LETTER I;Lo;0;L;;;;;N;;;;;
+119A3;NANDINAGARI LETTER II;Lo;0;L;;;;;N;;;;;
+119A4;NANDINAGARI LETTER U;Lo;0;L;;;;;N;;;;;
+119A5;NANDINAGARI LETTER UU;Lo;0;L;;;;;N;;;;;
+119A6;NANDINAGARI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+119A7;NANDINAGARI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+119AA;NANDINAGARI LETTER E;Lo;0;L;;;;;N;;;;;
+119AB;NANDINAGARI LETTER AI;Lo;0;L;;;;;N;;;;;
+119AC;NANDINAGARI LETTER O;Lo;0;L;;;;;N;;;;;
+119AD;NANDINAGARI LETTER AU;Lo;0;L;;;;;N;;;;;
+119AE;NANDINAGARI LETTER KA;Lo;0;L;;;;;N;;;;;
+119AF;NANDINAGARI LETTER KHA;Lo;0;L;;;;;N;;;;;
+119B0;NANDINAGARI LETTER GA;Lo;0;L;;;;;N;;;;;
+119B1;NANDINAGARI LETTER GHA;Lo;0;L;;;;;N;;;;;
+119B2;NANDINAGARI LETTER NGA;Lo;0;L;;;;;N;;;;;
+119B3;NANDINAGARI LETTER CA;Lo;0;L;;;;;N;;;;;
+119B4;NANDINAGARI LETTER CHA;Lo;0;L;;;;;N;;;;;
+119B5;NANDINAGARI LETTER JA;Lo;0;L;;;;;N;;;;;
+119B6;NANDINAGARI LETTER JHA;Lo;0;L;;;;;N;;;;;
+119B7;NANDINAGARI LETTER NYA;Lo;0;L;;;;;N;;;;;
+119B8;NANDINAGARI LETTER TTA;Lo;0;L;;;;;N;;;;;
+119B9;NANDINAGARI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+119BA;NANDINAGARI LETTER DDA;Lo;0;L;;;;;N;;;;;
+119BB;NANDINAGARI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+119BC;NANDINAGARI LETTER NNA;Lo;0;L;;;;;N;;;;;
+119BD;NANDINAGARI LETTER TA;Lo;0;L;;;;;N;;;;;
+119BE;NANDINAGARI LETTER THA;Lo;0;L;;;;;N;;;;;
+119BF;NANDINAGARI LETTER DA;Lo;0;L;;;;;N;;;;;
+119C0;NANDINAGARI LETTER DHA;Lo;0;L;;;;;N;;;;;
+119C1;NANDINAGARI LETTER NA;Lo;0;L;;;;;N;;;;;
+119C2;NANDINAGARI LETTER PA;Lo;0;L;;;;;N;;;;;
+119C3;NANDINAGARI LETTER PHA;Lo;0;L;;;;;N;;;;;
+119C4;NANDINAGARI LETTER BA;Lo;0;L;;;;;N;;;;;
+119C5;NANDINAGARI LETTER BHA;Lo;0;L;;;;;N;;;;;
+119C6;NANDINAGARI LETTER MA;Lo;0;L;;;;;N;;;;;
+119C7;NANDINAGARI LETTER YA;Lo;0;L;;;;;N;;;;;
+119C8;NANDINAGARI LETTER RA;Lo;0;L;;;;;N;;;;;
+119C9;NANDINAGARI LETTER LA;Lo;0;L;;;;;N;;;;;
+119CA;NANDINAGARI LETTER VA;Lo;0;L;;;;;N;;;;;
+119CB;NANDINAGARI LETTER SHA;Lo;0;L;;;;;N;;;;;
+119CC;NANDINAGARI LETTER SSA;Lo;0;L;;;;;N;;;;;
+119CD;NANDINAGARI LETTER SA;Lo;0;L;;;;;N;;;;;
+119CE;NANDINAGARI LETTER HA;Lo;0;L;;;;;N;;;;;
+119CF;NANDINAGARI LETTER LLA;Lo;0;L;;;;;N;;;;;
+119D0;NANDINAGARI LETTER RRA;Lo;0;L;;;;;N;;;;;
+119D1;NANDINAGARI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+119D2;NANDINAGARI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+119D3;NANDINAGARI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+119D4;NANDINAGARI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+119D5;NANDINAGARI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+119D6;NANDINAGARI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+119D7;NANDINAGARI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+119DA;NANDINAGARI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+119DB;NANDINAGARI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+119DC;NANDINAGARI VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+119DD;NANDINAGARI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+119DE;NANDINAGARI SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+119DF;NANDINAGARI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+119E0;NANDINAGARI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+119E1;NANDINAGARI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+119E2;NANDINAGARI SIGN SIDDHAM;Po;0;L;;;;;N;;;;;
+119E3;NANDINAGARI HEADSTROKE;Lo;0;L;;;;;N;;;;;
+119E4;NANDINAGARI VOWEL SIGN PRISHTHAMATRA E;Mc;0;L;;;;;N;;;;;
+11A00;ZANABAZAR SQUARE LETTER A;Lo;0;L;;;;;N;;;;;
+11A01;ZANABAZAR SQUARE VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+11A02;ZANABAZAR SQUARE VOWEL SIGN UE;Mn;0;NSM;;;;;N;;;;;
+11A03;ZANABAZAR SQUARE VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+11A04;ZANABAZAR SQUARE VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+11A05;ZANABAZAR SQUARE VOWEL SIGN OE;Mn;0;NSM;;;;;N;;;;;
+11A06;ZANABAZAR SQUARE VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+11A07;ZANABAZAR SQUARE VOWEL SIGN AI;Mn;0;L;;;;;N;;;;;
+11A08;ZANABAZAR SQUARE VOWEL SIGN AU;Mn;0;L;;;;;N;;;;;
+11A09;ZANABAZAR SQUARE VOWEL SIGN REVERSED I;Mn;0;NSM;;;;;N;;;;;
+11A0A;ZANABAZAR SQUARE VOWEL LENGTH MARK;Mn;0;NSM;;;;;N;;;;;
+11A0B;ZANABAZAR SQUARE LETTER KA;Lo;0;L;;;;;N;;;;;
+11A0C;ZANABAZAR SQUARE LETTER KHA;Lo;0;L;;;;;N;;;;;
+11A0D;ZANABAZAR SQUARE LETTER GA;Lo;0;L;;;;;N;;;;;
+11A0E;ZANABAZAR SQUARE LETTER GHA;Lo;0;L;;;;;N;;;;;
+11A0F;ZANABAZAR SQUARE LETTER NGA;Lo;0;L;;;;;N;;;;;
+11A10;ZANABAZAR SQUARE LETTER CA;Lo;0;L;;;;;N;;;;;
+11A11;ZANABAZAR SQUARE LETTER CHA;Lo;0;L;;;;;N;;;;;
+11A12;ZANABAZAR SQUARE LETTER JA;Lo;0;L;;;;;N;;;;;
+11A13;ZANABAZAR SQUARE LETTER NYA;Lo;0;L;;;;;N;;;;;
+11A14;ZANABAZAR SQUARE LETTER TTA;Lo;0;L;;;;;N;;;;;
+11A15;ZANABAZAR SQUARE LETTER TTHA;Lo;0;L;;;;;N;;;;;
+11A16;ZANABAZAR SQUARE LETTER DDA;Lo;0;L;;;;;N;;;;;
+11A17;ZANABAZAR SQUARE LETTER DDHA;Lo;0;L;;;;;N;;;;;
+11A18;ZANABAZAR SQUARE LETTER NNA;Lo;0;L;;;;;N;;;;;
+11A19;ZANABAZAR SQUARE LETTER TA;Lo;0;L;;;;;N;;;;;
+11A1A;ZANABAZAR SQUARE LETTER THA;Lo;0;L;;;;;N;;;;;
+11A1B;ZANABAZAR SQUARE LETTER DA;Lo;0;L;;;;;N;;;;;
+11A1C;ZANABAZAR SQUARE LETTER DHA;Lo;0;L;;;;;N;;;;;
+11A1D;ZANABAZAR SQUARE LETTER NA;Lo;0;L;;;;;N;;;;;
+11A1E;ZANABAZAR SQUARE LETTER PA;Lo;0;L;;;;;N;;;;;
+11A1F;ZANABAZAR SQUARE LETTER PHA;Lo;0;L;;;;;N;;;;;
+11A20;ZANABAZAR SQUARE LETTER BA;Lo;0;L;;;;;N;;;;;
+11A21;ZANABAZAR SQUARE LETTER BHA;Lo;0;L;;;;;N;;;;;
+11A22;ZANABAZAR SQUARE LETTER MA;Lo;0;L;;;;;N;;;;;
+11A23;ZANABAZAR SQUARE LETTER TSA;Lo;0;L;;;;;N;;;;;
+11A24;ZANABAZAR SQUARE LETTER TSHA;Lo;0;L;;;;;N;;;;;
+11A25;ZANABAZAR SQUARE LETTER DZA;Lo;0;L;;;;;N;;;;;
+11A26;ZANABAZAR SQUARE LETTER DZHA;Lo;0;L;;;;;N;;;;;
+11A27;ZANABAZAR SQUARE LETTER ZHA;Lo;0;L;;;;;N;;;;;
+11A28;ZANABAZAR SQUARE LETTER ZA;Lo;0;L;;;;;N;;;;;
+11A29;ZANABAZAR SQUARE LETTER -A;Lo;0;L;;;;;N;;;;;
+11A2A;ZANABAZAR SQUARE LETTER YA;Lo;0;L;;;;;N;;;;;
+11A2B;ZANABAZAR SQUARE LETTER RA;Lo;0;L;;;;;N;;;;;
+11A2C;ZANABAZAR SQUARE LETTER LA;Lo;0;L;;;;;N;;;;;
+11A2D;ZANABAZAR SQUARE LETTER VA;Lo;0;L;;;;;N;;;;;
+11A2E;ZANABAZAR SQUARE LETTER SHA;Lo;0;L;;;;;N;;;;;
+11A2F;ZANABAZAR SQUARE LETTER SSA;Lo;0;L;;;;;N;;;;;
+11A30;ZANABAZAR SQUARE LETTER SA;Lo;0;L;;;;;N;;;;;
+11A31;ZANABAZAR SQUARE LETTER HA;Lo;0;L;;;;;N;;;;;
+11A32;ZANABAZAR SQUARE LETTER KSSA;Lo;0;L;;;;;N;;;;;
+11A33;ZANABAZAR SQUARE FINAL CONSONANT MARK;Mn;0;NSM;;;;;N;;;;;
+11A34;ZANABAZAR SQUARE SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+11A35;ZANABAZAR SQUARE SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+11A36;ZANABAZAR SQUARE SIGN CANDRABINDU WITH ORNAMENT;Mn;0;NSM;;;;;N;;;;;
+11A37;ZANABAZAR SQUARE SIGN CANDRA WITH ORNAMENT;Mn;0;NSM;;;;;N;;;;;
+11A38;ZANABAZAR SQUARE SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+11A39;ZANABAZAR SQUARE SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+11A3A;ZANABAZAR SQUARE CLUSTER-INITIAL LETTER RA;Lo;0;L;;;;;N;;;;;
+11A3B;ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA;Mn;0;NSM;;;;;N;;;;;
+11A3C;ZANABAZAR SQUARE CLUSTER-FINAL LETTER RA;Mn;0;NSM;;;;;N;;;;;
+11A3D;ZANABAZAR SQUARE CLUSTER-FINAL LETTER LA;Mn;0;NSM;;;;;N;;;;;
+11A3E;ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA;Mn;0;NSM;;;;;N;;;;;
+11A3F;ZANABAZAR SQUARE INITIAL HEAD MARK;Po;0;L;;;;;N;;;;;
+11A40;ZANABAZAR SQUARE CLOSING HEAD MARK;Po;0;L;;;;;N;;;;;
+11A41;ZANABAZAR SQUARE MARK TSHEG;Po;0;L;;;;;N;;;;;
+11A42;ZANABAZAR SQUARE MARK SHAD;Po;0;L;;;;;N;;;;;
+11A43;ZANABAZAR SQUARE MARK DOUBLE SHAD;Po;0;L;;;;;N;;;;;
+11A44;ZANABAZAR SQUARE MARK LONG TSHEG;Po;0;L;;;;;N;;;;;
+11A45;ZANABAZAR SQUARE INITIAL DOUBLE-LINED HEAD MARK;Po;0;L;;;;;N;;;;;
+11A46;ZANABAZAR SQUARE CLOSING DOUBLE-LINED HEAD MARK;Po;0;L;;;;;N;;;;;
+11A47;ZANABAZAR SQUARE SUBJOINER;Mn;9;NSM;;;;;N;;;;;
+11A50;SOYOMBO LETTER A;Lo;0;L;;;;;N;;;;;
+11A51;SOYOMBO VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+11A52;SOYOMBO VOWEL SIGN UE;Mn;0;NSM;;;;;N;;;;;
+11A53;SOYOMBO VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+11A54;SOYOMBO VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+11A55;SOYOMBO VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+11A56;SOYOMBO VOWEL SIGN OE;Mn;0;NSM;;;;;N;;;;;
+11A57;SOYOMBO VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+11A58;SOYOMBO VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+11A59;SOYOMBO VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+11A5A;SOYOMBO VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+11A5B;SOYOMBO VOWEL LENGTH MARK;Mn;0;NSM;;;;;N;;;;;
+11A5C;SOYOMBO LETTER KA;Lo;0;L;;;;;N;;;;;
+11A5D;SOYOMBO LETTER KHA;Lo;0;L;;;;;N;;;;;
+11A5E;SOYOMBO LETTER GA;Lo;0;L;;;;;N;;;;;
+11A5F;SOYOMBO LETTER GHA;Lo;0;L;;;;;N;;;;;
+11A60;SOYOMBO LETTER NGA;Lo;0;L;;;;;N;;;;;
+11A61;SOYOMBO LETTER CA;Lo;0;L;;;;;N;;;;;
+11A62;SOYOMBO LETTER CHA;Lo;0;L;;;;;N;;;;;
+11A63;SOYOMBO LETTER JA;Lo;0;L;;;;;N;;;;;
+11A64;SOYOMBO LETTER JHA;Lo;0;L;;;;;N;;;;;
+11A65;SOYOMBO LETTER NYA;Lo;0;L;;;;;N;;;;;
+11A66;SOYOMBO LETTER TTA;Lo;0;L;;;;;N;;;;;
+11A67;SOYOMBO LETTER TTHA;Lo;0;L;;;;;N;;;;;
+11A68;SOYOMBO LETTER DDA;Lo;0;L;;;;;N;;;;;
+11A69;SOYOMBO LETTER DDHA;Lo;0;L;;;;;N;;;;;
+11A6A;SOYOMBO LETTER NNA;Lo;0;L;;;;;N;;;;;
+11A6B;SOYOMBO LETTER TA;Lo;0;L;;;;;N;;;;;
+11A6C;SOYOMBO LETTER THA;Lo;0;L;;;;;N;;;;;
+11A6D;SOYOMBO LETTER DA;Lo;0;L;;;;;N;;;;;
+11A6E;SOYOMBO LETTER DHA;Lo;0;L;;;;;N;;;;;
+11A6F;SOYOMBO LETTER NA;Lo;0;L;;;;;N;;;;;
+11A70;SOYOMBO LETTER PA;Lo;0;L;;;;;N;;;;;
+11A71;SOYOMBO LETTER PHA;Lo;0;L;;;;;N;;;;;
+11A72;SOYOMBO LETTER BA;Lo;0;L;;;;;N;;;;;
+11A73;SOYOMBO LETTER BHA;Lo;0;L;;;;;N;;;;;
+11A74;SOYOMBO LETTER MA;Lo;0;L;;;;;N;;;;;
+11A75;SOYOMBO LETTER TSA;Lo;0;L;;;;;N;;;;;
+11A76;SOYOMBO LETTER TSHA;Lo;0;L;;;;;N;;;;;
+11A77;SOYOMBO LETTER DZA;Lo;0;L;;;;;N;;;;;
+11A78;SOYOMBO LETTER ZHA;Lo;0;L;;;;;N;;;;;
+11A79;SOYOMBO LETTER ZA;Lo;0;L;;;;;N;;;;;
+11A7A;SOYOMBO LETTER -A;Lo;0;L;;;;;N;;;;;
+11A7B;SOYOMBO LETTER YA;Lo;0;L;;;;;N;;;;;
+11A7C;SOYOMBO LETTER RA;Lo;0;L;;;;;N;;;;;
+11A7D;SOYOMBO LETTER LA;Lo;0;L;;;;;N;;;;;
+11A7E;SOYOMBO LETTER VA;Lo;0;L;;;;;N;;;;;
+11A7F;SOYOMBO LETTER SHA;Lo;0;L;;;;;N;;;;;
+11A80;SOYOMBO LETTER SSA;Lo;0;L;;;;;N;;;;;
+11A81;SOYOMBO LETTER SA;Lo;0;L;;;;;N;;;;;
+11A82;SOYOMBO LETTER HA;Lo;0;L;;;;;N;;;;;
+11A83;SOYOMBO LETTER KSSA;Lo;0;L;;;;;N;;;;;
+11A84;SOYOMBO SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;;
+11A85;SOYOMBO SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;;
+11A86;SOYOMBO CLUSTER-INITIAL LETTER RA;Lo;0;L;;;;;N;;;;;
+11A87;SOYOMBO CLUSTER-INITIAL LETTER LA;Lo;0;L;;;;;N;;;;;
+11A88;SOYOMBO CLUSTER-INITIAL LETTER SHA;Lo;0;L;;;;;N;;;;;
+11A89;SOYOMBO CLUSTER-INITIAL LETTER SA;Lo;0;L;;;;;N;;;;;
+11A8A;SOYOMBO FINAL CONSONANT SIGN G;Mn;0;NSM;;;;;N;;;;;
+11A8B;SOYOMBO FINAL CONSONANT SIGN K;Mn;0;NSM;;;;;N;;;;;
+11A8C;SOYOMBO FINAL CONSONANT SIGN NG;Mn;0;NSM;;;;;N;;;;;
+11A8D;SOYOMBO FINAL CONSONANT SIGN D;Mn;0;NSM;;;;;N;;;;;
+11A8E;SOYOMBO FINAL CONSONANT SIGN N;Mn;0;NSM;;;;;N;;;;;
+11A8F;SOYOMBO FINAL CONSONANT SIGN B;Mn;0;NSM;;;;;N;;;;;
+11A90;SOYOMBO FINAL CONSONANT SIGN M;Mn;0;NSM;;;;;N;;;;;
+11A91;SOYOMBO FINAL CONSONANT SIGN R;Mn;0;NSM;;;;;N;;;;;
+11A92;SOYOMBO FINAL CONSONANT SIGN L;Mn;0;NSM;;;;;N;;;;;
+11A93;SOYOMBO FINAL CONSONANT SIGN SH;Mn;0;NSM;;;;;N;;;;;
+11A94;SOYOMBO FINAL CONSONANT SIGN S;Mn;0;NSM;;;;;N;;;;;
+11A95;SOYOMBO FINAL CONSONANT SIGN -A;Mn;0;NSM;;;;;N;;;;;
+11A96;SOYOMBO SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+11A97;SOYOMBO SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+11A98;SOYOMBO GEMINATION MARK;Mn;0;NSM;;;;;N;;;;;
+11A99;SOYOMBO SUBJOINER;Mn;9;NSM;;;;;N;;;;;
+11A9A;SOYOMBO MARK TSHEG;Po;0;L;;;;;N;;;;;
+11A9B;SOYOMBO MARK SHAD;Po;0;L;;;;;N;;;;;
+11A9C;SOYOMBO MARK DOUBLE SHAD;Po;0;L;;;;;N;;;;;
+11A9D;SOYOMBO MARK PLUTA;Lo;0;L;;;;;N;;;;;
+11A9E;SOYOMBO HEAD MARK WITH MOON AND SUN AND TRIPLE FLAME;Po;0;L;;;;;N;;;;;
+11A9F;SOYOMBO HEAD MARK WITH MOON AND SUN AND FLAME;Po;0;L;;;;;N;;;;;
+11AA0;SOYOMBO HEAD MARK WITH MOON AND SUN;Po;0;L;;;;;N;;;;;
+11AA1;SOYOMBO TERMINAL MARK-1;Po;0;L;;;;;N;;;;;
+11AA2;SOYOMBO TERMINAL MARK-2;Po;0;L;;;;;N;;;;;
+11AC0;PAU CIN HAU LETTER PA;Lo;0;L;;;;;N;;;;;
+11AC1;PAU CIN HAU LETTER KA;Lo;0;L;;;;;N;;;;;
+11AC2;PAU CIN HAU LETTER LA;Lo;0;L;;;;;N;;;;;
+11AC3;PAU CIN HAU LETTER MA;Lo;0;L;;;;;N;;;;;
+11AC4;PAU CIN HAU LETTER DA;Lo;0;L;;;;;N;;;;;
+11AC5;PAU CIN HAU LETTER ZA;Lo;0;L;;;;;N;;;;;
+11AC6;PAU CIN HAU LETTER VA;Lo;0;L;;;;;N;;;;;
+11AC7;PAU CIN HAU LETTER NGA;Lo;0;L;;;;;N;;;;;
+11AC8;PAU CIN HAU LETTER HA;Lo;0;L;;;;;N;;;;;
+11AC9;PAU CIN HAU LETTER GA;Lo;0;L;;;;;N;;;;;
+11ACA;PAU CIN HAU LETTER KHA;Lo;0;L;;;;;N;;;;;
+11ACB;PAU CIN HAU LETTER SA;Lo;0;L;;;;;N;;;;;
+11ACC;PAU CIN HAU LETTER BA;Lo;0;L;;;;;N;;;;;
+11ACD;PAU CIN HAU LETTER CA;Lo;0;L;;;;;N;;;;;
+11ACE;PAU CIN HAU LETTER TA;Lo;0;L;;;;;N;;;;;
+11ACF;PAU CIN HAU LETTER THA;Lo;0;L;;;;;N;;;;;
+11AD0;PAU CIN HAU LETTER NA;Lo;0;L;;;;;N;;;;;
+11AD1;PAU CIN HAU LETTER PHA;Lo;0;L;;;;;N;;;;;
+11AD2;PAU CIN HAU LETTER RA;Lo;0;L;;;;;N;;;;;
+11AD3;PAU CIN HAU LETTER FA;Lo;0;L;;;;;N;;;;;
+11AD4;PAU CIN HAU LETTER CHA;Lo;0;L;;;;;N;;;;;
+11AD5;PAU CIN HAU LETTER A;Lo;0;L;;;;;N;;;;;
+11AD6;PAU CIN HAU LETTER E;Lo;0;L;;;;;N;;;;;
+11AD7;PAU CIN HAU LETTER I;Lo;0;L;;;;;N;;;;;
+11AD8;PAU CIN HAU LETTER O;Lo;0;L;;;;;N;;;;;
+11AD9;PAU CIN HAU LETTER U;Lo;0;L;;;;;N;;;;;
+11ADA;PAU CIN HAU LETTER UA;Lo;0;L;;;;;N;;;;;
+11ADB;PAU CIN HAU LETTER IA;Lo;0;L;;;;;N;;;;;
+11ADC;PAU CIN HAU LETTER FINAL P;Lo;0;L;;;;;N;;;;;
+11ADD;PAU CIN HAU LETTER FINAL K;Lo;0;L;;;;;N;;;;;
+11ADE;PAU CIN HAU LETTER FINAL T;Lo;0;L;;;;;N;;;;;
+11ADF;PAU CIN HAU LETTER FINAL M;Lo;0;L;;;;;N;;;;;
+11AE0;PAU CIN HAU LETTER FINAL N;Lo;0;L;;;;;N;;;;;
+11AE1;PAU CIN HAU LETTER FINAL L;Lo;0;L;;;;;N;;;;;
+11AE2;PAU CIN HAU LETTER FINAL W;Lo;0;L;;;;;N;;;;;
+11AE3;PAU CIN HAU LETTER FINAL NG;Lo;0;L;;;;;N;;;;;
+11AE4;PAU CIN HAU LETTER FINAL Y;Lo;0;L;;;;;N;;;;;
+11AE5;PAU CIN HAU RISING TONE LONG;Lo;0;L;;;;;N;;;;;
+11AE6;PAU CIN HAU RISING TONE;Lo;0;L;;;;;N;;;;;
+11AE7;PAU CIN HAU SANDHI GLOTTAL STOP;Lo;0;L;;;;;N;;;;;
+11AE8;PAU CIN HAU RISING TONE LONG FINAL;Lo;0;L;;;;;N;;;;;
+11AE9;PAU CIN HAU RISING TONE FINAL;Lo;0;L;;;;;N;;;;;
+11AEA;PAU CIN HAU SANDHI GLOTTAL STOP FINAL;Lo;0;L;;;;;N;;;;;
+11AEB;PAU CIN HAU SANDHI TONE LONG;Lo;0;L;;;;;N;;;;;
+11AEC;PAU CIN HAU SANDHI TONE;Lo;0;L;;;;;N;;;;;
+11AED;PAU CIN HAU SANDHI TONE LONG FINAL;Lo;0;L;;;;;N;;;;;
+11AEE;PAU CIN HAU SANDHI TONE FINAL;Lo;0;L;;;;;N;;;;;
+11AEF;PAU CIN HAU MID-LEVEL TONE;Lo;0;L;;;;;N;;;;;
+11AF0;PAU CIN HAU GLOTTAL STOP VARIANT;Lo;0;L;;;;;N;;;;;
+11AF1;PAU CIN HAU MID-LEVEL TONE LONG FINAL;Lo;0;L;;;;;N;;;;;
+11AF2;PAU CIN HAU MID-LEVEL TONE FINAL;Lo;0;L;;;;;N;;;;;
+11AF3;PAU CIN HAU LOW-FALLING TONE LONG;Lo;0;L;;;;;N;;;;;
+11AF4;PAU CIN HAU LOW-FALLING TONE;Lo;0;L;;;;;N;;;;;
+11AF5;PAU CIN HAU GLOTTAL STOP;Lo;0;L;;;;;N;;;;;
+11AF6;PAU CIN HAU LOW-FALLING TONE LONG FINAL;Lo;0;L;;;;;N;;;;;
+11AF7;PAU CIN HAU LOW-FALLING TONE FINAL;Lo;0;L;;;;;N;;;;;
+11AF8;PAU CIN HAU GLOTTAL STOP FINAL;Lo;0;L;;;;;N;;;;;
+11C00;BHAIKSUKI LETTER A;Lo;0;L;;;;;N;;;;;
+11C01;BHAIKSUKI LETTER AA;Lo;0;L;;;;;N;;;;;
+11C02;BHAIKSUKI LETTER I;Lo;0;L;;;;;N;;;;;
+11C03;BHAIKSUKI LETTER II;Lo;0;L;;;;;N;;;;;
+11C04;BHAIKSUKI LETTER U;Lo;0;L;;;;;N;;;;;
+11C05;BHAIKSUKI LETTER UU;Lo;0;L;;;;;N;;;;;
+11C06;BHAIKSUKI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+11C07;BHAIKSUKI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+11C08;BHAIKSUKI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+11C0A;BHAIKSUKI LETTER E;Lo;0;L;;;;;N;;;;;
+11C0B;BHAIKSUKI LETTER AI;Lo;0;L;;;;;N;;;;;
+11C0C;BHAIKSUKI LETTER O;Lo;0;L;;;;;N;;;;;
+11C0D;BHAIKSUKI LETTER AU;Lo;0;L;;;;;N;;;;;
+11C0E;BHAIKSUKI LETTER KA;Lo;0;L;;;;;N;;;;;
+11C0F;BHAIKSUKI LETTER KHA;Lo;0;L;;;;;N;;;;;
+11C10;BHAIKSUKI LETTER GA;Lo;0;L;;;;;N;;;;;
+11C11;BHAIKSUKI LETTER GHA;Lo;0;L;;;;;N;;;;;
+11C12;BHAIKSUKI LETTER NGA;Lo;0;L;;;;;N;;;;;
+11C13;BHAIKSUKI LETTER CA;Lo;0;L;;;;;N;;;;;
+11C14;BHAIKSUKI LETTER CHA;Lo;0;L;;;;;N;;;;;
+11C15;BHAIKSUKI LETTER JA;Lo;0;L;;;;;N;;;;;
+11C16;BHAIKSUKI LETTER JHA;Lo;0;L;;;;;N;;;;;
+11C17;BHAIKSUKI LETTER NYA;Lo;0;L;;;;;N;;;;;
+11C18;BHAIKSUKI LETTER TTA;Lo;0;L;;;;;N;;;;;
+11C19;BHAIKSUKI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+11C1A;BHAIKSUKI LETTER DDA;Lo;0;L;;;;;N;;;;;
+11C1B;BHAIKSUKI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+11C1C;BHAIKSUKI LETTER NNA;Lo;0;L;;;;;N;;;;;
+11C1D;BHAIKSUKI LETTER TA;Lo;0;L;;;;;N;;;;;
+11C1E;BHAIKSUKI LETTER THA;Lo;0;L;;;;;N;;;;;
+11C1F;BHAIKSUKI LETTER DA;Lo;0;L;;;;;N;;;;;
+11C20;BHAIKSUKI LETTER DHA;Lo;0;L;;;;;N;;;;;
+11C21;BHAIKSUKI LETTER NA;Lo;0;L;;;;;N;;;;;
+11C22;BHAIKSUKI LETTER PA;Lo;0;L;;;;;N;;;;;
+11C23;BHAIKSUKI LETTER PHA;Lo;0;L;;;;;N;;;;;
+11C24;BHAIKSUKI LETTER BA;Lo;0;L;;;;;N;;;;;
+11C25;BHAIKSUKI LETTER BHA;Lo;0;L;;;;;N;;;;;
+11C26;BHAIKSUKI LETTER MA;Lo;0;L;;;;;N;;;;;
+11C27;BHAIKSUKI LETTER YA;Lo;0;L;;;;;N;;;;;
+11C28;BHAIKSUKI LETTER RA;Lo;0;L;;;;;N;;;;;
+11C29;BHAIKSUKI LETTER LA;Lo;0;L;;;;;N;;;;;
+11C2A;BHAIKSUKI LETTER VA;Lo;0;L;;;;;N;;;;;
+11C2B;BHAIKSUKI LETTER SHA;Lo;0;L;;;;;N;;;;;
+11C2C;BHAIKSUKI LETTER SSA;Lo;0;L;;;;;N;;;;;
+11C2D;BHAIKSUKI LETTER SA;Lo;0;L;;;;;N;;;;;
+11C2E;BHAIKSUKI LETTER HA;Lo;0;L;;;;;N;;;;;
+11C2F;BHAIKSUKI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+11C30;BHAIKSUKI VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+11C31;BHAIKSUKI VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+11C32;BHAIKSUKI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+11C33;BHAIKSUKI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+11C34;BHAIKSUKI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+11C35;BHAIKSUKI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+11C36;BHAIKSUKI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+11C38;BHAIKSUKI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+11C39;BHAIKSUKI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+11C3A;BHAIKSUKI VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+11C3B;BHAIKSUKI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+11C3C;BHAIKSUKI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+11C3D;BHAIKSUKI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+11C3E;BHAIKSUKI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+11C3F;BHAIKSUKI SIGN VIRAMA;Mn;9;L;;;;;N;;;;;
+11C40;BHAIKSUKI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+11C41;BHAIKSUKI DANDA;Po;0;L;;;;;N;;;;;
+11C42;BHAIKSUKI DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+11C43;BHAIKSUKI WORD SEPARATOR;Po;0;L;;;;;N;;;;;
+11C44;BHAIKSUKI GAP FILLER-1;Po;0;L;;;;;N;;;;;
+11C45;BHAIKSUKI GAP FILLER-2;Po;0;L;;;;;N;;;;;
+11C50;BHAIKSUKI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+11C51;BHAIKSUKI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+11C52;BHAIKSUKI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+11C53;BHAIKSUKI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+11C54;BHAIKSUKI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+11C55;BHAIKSUKI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+11C56;BHAIKSUKI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+11C57;BHAIKSUKI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+11C58;BHAIKSUKI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+11C59;BHAIKSUKI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+11C5A;BHAIKSUKI NUMBER ONE;No;0;L;;;;1;N;;;;;
+11C5B;BHAIKSUKI NUMBER TWO;No;0;L;;;;2;N;;;;;
+11C5C;BHAIKSUKI NUMBER THREE;No;0;L;;;;3;N;;;;;
+11C5D;BHAIKSUKI NUMBER FOUR;No;0;L;;;;4;N;;;;;
+11C5E;BHAIKSUKI NUMBER FIVE;No;0;L;;;;5;N;;;;;
+11C5F;BHAIKSUKI NUMBER SIX;No;0;L;;;;6;N;;;;;
+11C60;BHAIKSUKI NUMBER SEVEN;No;0;L;;;;7;N;;;;;
+11C61;BHAIKSUKI NUMBER EIGHT;No;0;L;;;;8;N;;;;;
+11C62;BHAIKSUKI NUMBER NINE;No;0;L;;;;9;N;;;;;
+11C63;BHAIKSUKI NUMBER TEN;No;0;L;;;;10;N;;;;;
+11C64;BHAIKSUKI NUMBER TWENTY;No;0;L;;;;20;N;;;;;
+11C65;BHAIKSUKI NUMBER THIRTY;No;0;L;;;;30;N;;;;;
+11C66;BHAIKSUKI NUMBER FORTY;No;0;L;;;;40;N;;;;;
+11C67;BHAIKSUKI NUMBER FIFTY;No;0;L;;;;50;N;;;;;
+11C68;BHAIKSUKI NUMBER SIXTY;No;0;L;;;;60;N;;;;;
+11C69;BHAIKSUKI NUMBER SEVENTY;No;0;L;;;;70;N;;;;;
+11C6A;BHAIKSUKI NUMBER EIGHTY;No;0;L;;;;80;N;;;;;
+11C6B;BHAIKSUKI NUMBER NINETY;No;0;L;;;;90;N;;;;;
+11C6C;BHAIKSUKI HUNDREDS UNIT MARK;No;0;L;;;;100;N;;;;;
+11C70;MARCHEN HEAD MARK;Po;0;L;;;;;N;;;;;
+11C71;MARCHEN MARK SHAD;Po;0;L;;;;;N;;;;;
+11C72;MARCHEN LETTER KA;Lo;0;L;;;;;N;;;;;
+11C73;MARCHEN LETTER KHA;Lo;0;L;;;;;N;;;;;
+11C74;MARCHEN LETTER GA;Lo;0;L;;;;;N;;;;;
+11C75;MARCHEN LETTER NGA;Lo;0;L;;;;;N;;;;;
+11C76;MARCHEN LETTER CA;Lo;0;L;;;;;N;;;;;
+11C77;MARCHEN LETTER CHA;Lo;0;L;;;;;N;;;;;
+11C78;MARCHEN LETTER JA;Lo;0;L;;;;;N;;;;;
+11C79;MARCHEN LETTER NYA;Lo;0;L;;;;;N;;;;;
+11C7A;MARCHEN LETTER TA;Lo;0;L;;;;;N;;;;;
+11C7B;MARCHEN LETTER THA;Lo;0;L;;;;;N;;;;;
+11C7C;MARCHEN LETTER DA;Lo;0;L;;;;;N;;;;;
+11C7D;MARCHEN LETTER NA;Lo;0;L;;;;;N;;;;;
+11C7E;MARCHEN LETTER PA;Lo;0;L;;;;;N;;;;;
+11C7F;MARCHEN LETTER PHA;Lo;0;L;;;;;N;;;;;
+11C80;MARCHEN LETTER BA;Lo;0;L;;;;;N;;;;;
+11C81;MARCHEN LETTER MA;Lo;0;L;;;;;N;;;;;
+11C82;MARCHEN LETTER TSA;Lo;0;L;;;;;N;;;;;
+11C83;MARCHEN LETTER TSHA;Lo;0;L;;;;;N;;;;;
+11C84;MARCHEN LETTER DZA;Lo;0;L;;;;;N;;;;;
+11C85;MARCHEN LETTER WA;Lo;0;L;;;;;N;;;;;
+11C86;MARCHEN LETTER ZHA;Lo;0;L;;;;;N;;;;;
+11C87;MARCHEN LETTER ZA;Lo;0;L;;;;;N;;;;;
+11C88;MARCHEN LETTER -A;Lo;0;L;;;;;N;;;;;
+11C89;MARCHEN LETTER YA;Lo;0;L;;;;;N;;;;;
+11C8A;MARCHEN LETTER RA;Lo;0;L;;;;;N;;;;;
+11C8B;MARCHEN LETTER LA;Lo;0;L;;;;;N;;;;;
+11C8C;MARCHEN LETTER SHA;Lo;0;L;;;;;N;;;;;
+11C8D;MARCHEN LETTER SA;Lo;0;L;;;;;N;;;;;
+11C8E;MARCHEN LETTER HA;Lo;0;L;;;;;N;;;;;
+11C8F;MARCHEN LETTER A;Lo;0;L;;;;;N;;;;;
+11C92;MARCHEN SUBJOINED LETTER KA;Mn;0;NSM;;;;;N;;;;;
+11C93;MARCHEN SUBJOINED LETTER KHA;Mn;0;NSM;;;;;N;;;;;
+11C94;MARCHEN SUBJOINED LETTER GA;Mn;0;NSM;;;;;N;;;;;
+11C95;MARCHEN SUBJOINED LETTER NGA;Mn;0;NSM;;;;;N;;;;;
+11C96;MARCHEN SUBJOINED LETTER CA;Mn;0;NSM;;;;;N;;;;;
+11C97;MARCHEN SUBJOINED LETTER CHA;Mn;0;NSM;;;;;N;;;;;
+11C98;MARCHEN SUBJOINED LETTER JA;Mn;0;NSM;;;;;N;;;;;
+11C99;MARCHEN SUBJOINED LETTER NYA;Mn;0;NSM;;;;;N;;;;;
+11C9A;MARCHEN SUBJOINED LETTER TA;Mn;0;NSM;;;;;N;;;;;
+11C9B;MARCHEN SUBJOINED LETTER THA;Mn;0;NSM;;;;;N;;;;;
+11C9C;MARCHEN SUBJOINED LETTER DA;Mn;0;NSM;;;;;N;;;;;
+11C9D;MARCHEN SUBJOINED LETTER NA;Mn;0;NSM;;;;;N;;;;;
+11C9E;MARCHEN SUBJOINED LETTER PA;Mn;0;NSM;;;;;N;;;;;
+11C9F;MARCHEN SUBJOINED LETTER PHA;Mn;0;NSM;;;;;N;;;;;
+11CA0;MARCHEN SUBJOINED LETTER BA;Mn;0;NSM;;;;;N;;;;;
+11CA1;MARCHEN SUBJOINED LETTER MA;Mn;0;NSM;;;;;N;;;;;
+11CA2;MARCHEN SUBJOINED LETTER TSA;Mn;0;NSM;;;;;N;;;;;
+11CA3;MARCHEN SUBJOINED LETTER TSHA;Mn;0;NSM;;;;;N;;;;;
+11CA4;MARCHEN SUBJOINED LETTER DZA;Mn;0;NSM;;;;;N;;;;;
+11CA5;MARCHEN SUBJOINED LETTER WA;Mn;0;NSM;;;;;N;;;;;
+11CA6;MARCHEN SUBJOINED LETTER ZHA;Mn;0;NSM;;;;;N;;;;;
+11CA7;MARCHEN SUBJOINED LETTER ZA;Mn;0;NSM;;;;;N;;;;;
+11CA9;MARCHEN SUBJOINED LETTER YA;Mc;0;L;;;;;N;;;;;
+11CAA;MARCHEN SUBJOINED LETTER RA;Mn;0;NSM;;;;;N;;;;;
+11CAB;MARCHEN SUBJOINED LETTER LA;Mn;0;NSM;;;;;N;;;;;
+11CAC;MARCHEN SUBJOINED LETTER SHA;Mn;0;NSM;;;;;N;;;;;
+11CAD;MARCHEN SUBJOINED LETTER SA;Mn;0;NSM;;;;;N;;;;;
+11CAE;MARCHEN SUBJOINED LETTER HA;Mn;0;NSM;;;;;N;;;;;
+11CAF;MARCHEN SUBJOINED LETTER A;Mn;0;NSM;;;;;N;;;;;
+11CB0;MARCHEN VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;;
+11CB1;MARCHEN VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+11CB2;MARCHEN VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+11CB3;MARCHEN VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+11CB4;MARCHEN VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+11CB5;MARCHEN SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+11CB6;MARCHEN SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+11D00;MASARAM GONDI LETTER A;Lo;0;L;;;;;N;;;;;
+11D01;MASARAM GONDI LETTER AA;Lo;0;L;;;;;N;;;;;
+11D02;MASARAM GONDI LETTER I;Lo;0;L;;;;;N;;;;;
+11D03;MASARAM GONDI LETTER II;Lo;0;L;;;;;N;;;;;
+11D04;MASARAM GONDI LETTER U;Lo;0;L;;;;;N;;;;;
+11D05;MASARAM GONDI LETTER UU;Lo;0;L;;;;;N;;;;;
+11D06;MASARAM GONDI LETTER E;Lo;0;L;;;;;N;;;;;
+11D08;MASARAM GONDI LETTER AI;Lo;0;L;;;;;N;;;;;
+11D09;MASARAM GONDI LETTER O;Lo;0;L;;;;;N;;;;;
+11D0B;MASARAM GONDI LETTER AU;Lo;0;L;;;;;N;;;;;
+11D0C;MASARAM GONDI LETTER KA;Lo;0;L;;;;;N;;;;;
+11D0D;MASARAM GONDI LETTER KHA;Lo;0;L;;;;;N;;;;;
+11D0E;MASARAM GONDI LETTER GA;Lo;0;L;;;;;N;;;;;
+11D0F;MASARAM GONDI LETTER GHA;Lo;0;L;;;;;N;;;;;
+11D10;MASARAM GONDI LETTER NGA;Lo;0;L;;;;;N;;;;;
+11D11;MASARAM GONDI LETTER CA;Lo;0;L;;;;;N;;;;;
+11D12;MASARAM GONDI LETTER CHA;Lo;0;L;;;;;N;;;;;
+11D13;MASARAM GONDI LETTER JA;Lo;0;L;;;;;N;;;;;
+11D14;MASARAM GONDI LETTER JHA;Lo;0;L;;;;;N;;;;;
+11D15;MASARAM GONDI LETTER NYA;Lo;0;L;;;;;N;;;;;
+11D16;MASARAM GONDI LETTER TTA;Lo;0;L;;;;;N;;;;;
+11D17;MASARAM GONDI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+11D18;MASARAM GONDI LETTER DDA;Lo;0;L;;;;;N;;;;;
+11D19;MASARAM GONDI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+11D1A;MASARAM GONDI LETTER NNA;Lo;0;L;;;;;N;;;;;
+11D1B;MASARAM GONDI LETTER TA;Lo;0;L;;;;;N;;;;;
+11D1C;MASARAM GONDI LETTER THA;Lo;0;L;;;;;N;;;;;
+11D1D;MASARAM GONDI LETTER DA;Lo;0;L;;;;;N;;;;;
+11D1E;MASARAM GONDI LETTER DHA;Lo;0;L;;;;;N;;;;;
+11D1F;MASARAM GONDI LETTER NA;Lo;0;L;;;;;N;;;;;
+11D20;MASARAM GONDI LETTER PA;Lo;0;L;;;;;N;;;;;
+11D21;MASARAM GONDI LETTER PHA;Lo;0;L;;;;;N;;;;;
+11D22;MASARAM GONDI LETTER BA;Lo;0;L;;;;;N;;;;;
+11D23;MASARAM GONDI LETTER BHA;Lo;0;L;;;;;N;;;;;
+11D24;MASARAM GONDI LETTER MA;Lo;0;L;;;;;N;;;;;
+11D25;MASARAM GONDI LETTER YA;Lo;0;L;;;;;N;;;;;
+11D26;MASARAM GONDI LETTER RA;Lo;0;L;;;;;N;;;;;
+11D27;MASARAM GONDI LETTER LA;Lo;0;L;;;;;N;;;;;
+11D28;MASARAM GONDI LETTER VA;Lo;0;L;;;;;N;;;;;
+11D29;MASARAM GONDI LETTER SHA;Lo;0;L;;;;;N;;;;;
+11D2A;MASARAM GONDI LETTER SSA;Lo;0;L;;;;;N;;;;;
+11D2B;MASARAM GONDI LETTER SA;Lo;0;L;;;;;N;;;;;
+11D2C;MASARAM GONDI LETTER HA;Lo;0;L;;;;;N;;;;;
+11D2D;MASARAM GONDI LETTER LLA;Lo;0;L;;;;;N;;;;;
+11D2E;MASARAM GONDI LETTER KSSA;Lo;0;L;;;;;N;;;;;
+11D2F;MASARAM GONDI LETTER JNYA;Lo;0;L;;;;;N;;;;;
+11D30;MASARAM GONDI LETTER TRA;Lo;0;L;;;;;N;;;;;
+11D31;MASARAM GONDI VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;;
+11D32;MASARAM GONDI VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+11D33;MASARAM GONDI VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+11D34;MASARAM GONDI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+11D35;MASARAM GONDI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+11D36;MASARAM GONDI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+11D3A;MASARAM GONDI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+11D3C;MASARAM GONDI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+11D3D;MASARAM GONDI VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+11D3F;MASARAM GONDI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+11D40;MASARAM GONDI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+11D41;MASARAM GONDI SIGN VISARGA;Mn;0;NSM;;;;;N;;;;;
+11D42;MASARAM GONDI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+11D43;MASARAM GONDI SIGN CANDRA;Mn;0;NSM;;;;;N;;;;;
+11D44;MASARAM GONDI SIGN HALANTA;Mn;9;NSM;;;;;N;;;;;
+11D45;MASARAM GONDI VIRAMA;Mn;9;NSM;;;;;N;;;;;
+11D46;MASARAM GONDI REPHA;Lo;0;L;;;;;N;;;;;
+11D47;MASARAM GONDI RA-KARA;Mn;0;NSM;;;;;N;;;;;
+11D50;MASARAM GONDI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+11D51;MASARAM GONDI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+11D52;MASARAM GONDI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+11D53;MASARAM GONDI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+11D54;MASARAM GONDI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+11D55;MASARAM GONDI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+11D56;MASARAM GONDI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+11D57;MASARAM GONDI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+11D58;MASARAM GONDI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+11D59;MASARAM GONDI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+11D60;GUNJALA GONDI LETTER A;Lo;0;L;;;;;N;;;;;
+11D61;GUNJALA GONDI LETTER AA;Lo;0;L;;;;;N;;;;;
+11D62;GUNJALA GONDI LETTER I;Lo;0;L;;;;;N;;;;;
+11D63;GUNJALA GONDI LETTER II;Lo;0;L;;;;;N;;;;;
+11D64;GUNJALA GONDI LETTER U;Lo;0;L;;;;;N;;;;;
+11D65;GUNJALA GONDI LETTER UU;Lo;0;L;;;;;N;;;;;
+11D67;GUNJALA GONDI LETTER EE;Lo;0;L;;;;;N;;;;;
+11D68;GUNJALA GONDI LETTER AI;Lo;0;L;;;;;N;;;;;
+11D6A;GUNJALA GONDI LETTER OO;Lo;0;L;;;;;N;;;;;
+11D6B;GUNJALA GONDI LETTER AU;Lo;0;L;;;;;N;;;;;
+11D6C;GUNJALA GONDI LETTER YA;Lo;0;L;;;;;N;;;;;
+11D6D;GUNJALA GONDI LETTER VA;Lo;0;L;;;;;N;;;;;
+11D6E;GUNJALA GONDI LETTER BA;Lo;0;L;;;;;N;;;;;
+11D6F;GUNJALA GONDI LETTER BHA;Lo;0;L;;;;;N;;;;;
+11D70;GUNJALA GONDI LETTER MA;Lo;0;L;;;;;N;;;;;
+11D71;GUNJALA GONDI LETTER KA;Lo;0;L;;;;;N;;;;;
+11D72;GUNJALA GONDI LETTER KHA;Lo;0;L;;;;;N;;;;;
+11D73;GUNJALA GONDI LETTER TA;Lo;0;L;;;;;N;;;;;
+11D74;GUNJALA GONDI LETTER THA;Lo;0;L;;;;;N;;;;;
+11D75;GUNJALA GONDI LETTER LA;Lo;0;L;;;;;N;;;;;
+11D76;GUNJALA GONDI LETTER GA;Lo;0;L;;;;;N;;;;;
+11D77;GUNJALA GONDI LETTER GHA;Lo;0;L;;;;;N;;;;;
+11D78;GUNJALA GONDI LETTER DA;Lo;0;L;;;;;N;;;;;
+11D79;GUNJALA GONDI LETTER DHA;Lo;0;L;;;;;N;;;;;
+11D7A;GUNJALA GONDI LETTER NA;Lo;0;L;;;;;N;;;;;
+11D7B;GUNJALA GONDI LETTER CA;Lo;0;L;;;;;N;;;;;
+11D7C;GUNJALA GONDI LETTER CHA;Lo;0;L;;;;;N;;;;;
+11D7D;GUNJALA GONDI LETTER TTA;Lo;0;L;;;;;N;;;;;
+11D7E;GUNJALA GONDI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+11D7F;GUNJALA GONDI LETTER LLA;Lo;0;L;;;;;N;;;;;
+11D80;GUNJALA GONDI LETTER JA;Lo;0;L;;;;;N;;;;;
+11D81;GUNJALA GONDI LETTER JHA;Lo;0;L;;;;;N;;;;;
+11D82;GUNJALA GONDI LETTER DDA;Lo;0;L;;;;;N;;;;;
+11D83;GUNJALA GONDI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+11D84;GUNJALA GONDI LETTER NGA;Lo;0;L;;;;;N;;;;;
+11D85;GUNJALA GONDI LETTER PA;Lo;0;L;;;;;N;;;;;
+11D86;GUNJALA GONDI LETTER PHA;Lo;0;L;;;;;N;;;;;
+11D87;GUNJALA GONDI LETTER HA;Lo;0;L;;;;;N;;;;;
+11D88;GUNJALA GONDI LETTER RA;Lo;0;L;;;;;N;;;;;
+11D89;GUNJALA GONDI LETTER SA;Lo;0;L;;;;;N;;;;;
+11D8A;GUNJALA GONDI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+11D8B;GUNJALA GONDI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+11D8C;GUNJALA GONDI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+11D8D;GUNJALA GONDI VOWEL SIGN U;Mc;0;L;;;;;N;;;;;
+11D8E;GUNJALA GONDI VOWEL SIGN UU;Mc;0;L;;;;;N;;;;;
+11D90;GUNJALA GONDI VOWEL SIGN EE;Mn;0;NSM;;;;;N;;;;;
+11D91;GUNJALA GONDI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+11D93;GUNJALA GONDI VOWEL SIGN OO;Mc;0;L;;;;;N;;;;;
+11D94;GUNJALA GONDI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+11D95;GUNJALA GONDI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+11D96;GUNJALA GONDI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+11D97;GUNJALA GONDI VIRAMA;Mn;9;NSM;;;;;N;;;;;
+11D98;GUNJALA GONDI OM;Lo;0;L;;;;;N;;;;;
+11DA0;GUNJALA GONDI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+11DA1;GUNJALA GONDI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+11DA2;GUNJALA GONDI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+11DA3;GUNJALA GONDI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+11DA4;GUNJALA GONDI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+11DA5;GUNJALA GONDI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+11DA6;GUNJALA GONDI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+11DA7;GUNJALA GONDI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+11DA8;GUNJALA GONDI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+11DA9;GUNJALA GONDI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+11EE0;MAKASAR LETTER KA;Lo;0;L;;;;;N;;;;;
+11EE1;MAKASAR LETTER GA;Lo;0;L;;;;;N;;;;;
+11EE2;MAKASAR LETTER NGA;Lo;0;L;;;;;N;;;;;
+11EE3;MAKASAR LETTER PA;Lo;0;L;;;;;N;;;;;
+11EE4;MAKASAR LETTER BA;Lo;0;L;;;;;N;;;;;
+11EE5;MAKASAR LETTER MA;Lo;0;L;;;;;N;;;;;
+11EE6;MAKASAR LETTER TA;Lo;0;L;;;;;N;;;;;
+11EE7;MAKASAR LETTER DA;Lo;0;L;;;;;N;;;;;
+11EE8;MAKASAR LETTER NA;Lo;0;L;;;;;N;;;;;
+11EE9;MAKASAR LETTER CA;Lo;0;L;;;;;N;;;;;
+11EEA;MAKASAR LETTER JA;Lo;0;L;;;;;N;;;;;
+11EEB;MAKASAR LETTER NYA;Lo;0;L;;;;;N;;;;;
+11EEC;MAKASAR LETTER YA;Lo;0;L;;;;;N;;;;;
+11EED;MAKASAR LETTER RA;Lo;0;L;;;;;N;;;;;
+11EEE;MAKASAR LETTER LA;Lo;0;L;;;;;N;;;;;
+11EEF;MAKASAR LETTER VA;Lo;0;L;;;;;N;;;;;
+11EF0;MAKASAR LETTER SA;Lo;0;L;;;;;N;;;;;
+11EF1;MAKASAR LETTER A;Lo;0;L;;;;;N;;;;;
+11EF2;MAKASAR ANGKA;Lo;0;L;;;;;N;;;;;
+11EF3;MAKASAR VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+11EF4;MAKASAR VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+11EF5;MAKASAR VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+11EF6;MAKASAR VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+11EF7;MAKASAR PASSIMBANG;Po;0;L;;;;;N;;;;;
+11EF8;MAKASAR END OF SECTION;Po;0;L;;;;;N;;;;;
+11FC0;TAMIL FRACTION ONE THREE-HUNDRED-AND-TWENTIETH;No;0;L;;;;1/320;N;;;;;
+11FC1;TAMIL FRACTION ONE ONE-HUNDRED-AND-SIXTIETH;No;0;L;;;;1/160;N;;;;;
+11FC2;TAMIL FRACTION ONE EIGHTIETH;No;0;L;;;;1/80;N;;;;;
+11FC3;TAMIL FRACTION ONE SIXTY-FOURTH;No;0;L;;;;1/64;N;;;;;
+11FC4;TAMIL FRACTION ONE FORTIETH;No;0;L;;;;1/40;N;;;;;
+11FC5;TAMIL FRACTION ONE THIRTY-SECOND;No;0;L;;;;1/32;N;;;;;
+11FC6;TAMIL FRACTION THREE EIGHTIETHS;No;0;L;;;;3/80;N;;;;;
+11FC7;TAMIL FRACTION THREE SIXTY-FOURTHS;No;0;L;;;;3/64;N;;;;;
+11FC8;TAMIL FRACTION ONE TWENTIETH;No;0;L;;;;1/20;N;;;;;
+11FC9;TAMIL FRACTION ONE SIXTEENTH-1;No;0;L;;;;1/16;N;;;;;
+11FCA;TAMIL FRACTION ONE SIXTEENTH-2;No;0;L;;;;1/16;N;;;;;
+11FCB;TAMIL FRACTION ONE TENTH;No;0;L;;;;1/10;N;;;;;
+11FCC;TAMIL FRACTION ONE EIGHTH;No;0;L;;;;1/8;N;;;;;
+11FCD;TAMIL FRACTION THREE TWENTIETHS;No;0;L;;;;3/20;N;;;;;
+11FCE;TAMIL FRACTION THREE SIXTEENTHS;No;0;L;;;;3/16;N;;;;;
+11FCF;TAMIL FRACTION ONE FIFTH;No;0;L;;;;1/5;N;;;;;
+11FD0;TAMIL FRACTION ONE QUARTER;No;0;L;;;;1/4;N;;;;;
+11FD1;TAMIL FRACTION ONE HALF-1;No;0;L;;;;1/2;N;;;;;
+11FD2;TAMIL FRACTION ONE HALF-2;No;0;L;;;;1/2;N;;;;;
+11FD3;TAMIL FRACTION THREE QUARTERS;No;0;L;;;;3/4;N;;;;;
+11FD4;TAMIL FRACTION DOWNSCALING FACTOR KIIZH;No;0;L;;;;1/320;N;;;;;
+11FD5;TAMIL SIGN NEL;So;0;ON;;;;;N;;;;;
+11FD6;TAMIL SIGN CEVITU;So;0;ON;;;;;N;;;;;
+11FD7;TAMIL SIGN AAZHAAKKU;So;0;ON;;;;;N;;;;;
+11FD8;TAMIL SIGN UZHAKKU;So;0;ON;;;;;N;;;;;
+11FD9;TAMIL SIGN MUUVUZHAKKU;So;0;ON;;;;;N;;;;;
+11FDA;TAMIL SIGN KURUNI;So;0;ON;;;;;N;;;;;
+11FDB;TAMIL SIGN PATHAKKU;So;0;ON;;;;;N;;;;;
+11FDC;TAMIL SIGN MUKKURUNI;So;0;ON;;;;;N;;;;;
+11FDD;TAMIL SIGN KAACU;Sc;0;ET;;;;;N;;;;;
+11FDE;TAMIL SIGN PANAM;Sc;0;ET;;;;;N;;;;;
+11FDF;TAMIL SIGN PON;Sc;0;ET;;;;;N;;;;;
+11FE0;TAMIL SIGN VARAAKAN;Sc;0;ET;;;;;N;;;;;
+11FE1;TAMIL SIGN PAARAM;So;0;ON;;;;;N;;;;;
+11FE2;TAMIL SIGN KUZHI;So;0;ON;;;;;N;;;;;
+11FE3;TAMIL SIGN VELI;So;0;ON;;;;;N;;;;;
+11FE4;TAMIL WET CULTIVATION SIGN;So;0;ON;;;;;N;;;;;
+11FE5;TAMIL DRY CULTIVATION SIGN;So;0;ON;;;;;N;;;;;
+11FE6;TAMIL LAND SIGN;So;0;ON;;;;;N;;;;;
+11FE7;TAMIL SALT PAN SIGN;So;0;ON;;;;;N;;;;;
+11FE8;TAMIL TRADITIONAL CREDIT SIGN;So;0;ON;;;;;N;;;;;
+11FE9;TAMIL TRADITIONAL NUMBER SIGN;So;0;ON;;;;;N;;;;;
+11FEA;TAMIL CURRENT SIGN;So;0;ON;;;;;N;;;;;
+11FEB;TAMIL AND ODD SIGN;So;0;ON;;;;;N;;;;;
+11FEC;TAMIL SPENT SIGN;So;0;ON;;;;;N;;;;;
+11FED;TAMIL TOTAL SIGN;So;0;ON;;;;;N;;;;;
+11FEE;TAMIL IN POSSESSION SIGN;So;0;ON;;;;;N;;;;;
+11FEF;TAMIL STARTING FROM SIGN;So;0;ON;;;;;N;;;;;
+11FF0;TAMIL SIGN MUTHALIYA;So;0;ON;;;;;N;;;;;
+11FF1;TAMIL SIGN VAKAIYARAA;So;0;ON;;;;;N;;;;;
+11FFF;TAMIL PUNCTUATION END OF TEXT;Po;0;L;;;;;N;;;;;
+12000;CUNEIFORM SIGN A;Lo;0;L;;;;;N;;;;;
+12001;CUNEIFORM SIGN A TIMES A;Lo;0;L;;;;;N;;;;;
+12002;CUNEIFORM SIGN A TIMES BAD;Lo;0;L;;;;;N;;;;;
+12003;CUNEIFORM SIGN A TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;;
+12004;CUNEIFORM SIGN A TIMES HA;Lo;0;L;;;;;N;;;;;
+12005;CUNEIFORM SIGN A TIMES IGI;Lo;0;L;;;;;N;;;;;
+12006;CUNEIFORM SIGN A TIMES LAGAR GUNU;Lo;0;L;;;;;N;;;;;
+12007;CUNEIFORM SIGN A TIMES MUSH;Lo;0;L;;;;;N;;;;;
+12008;CUNEIFORM SIGN A TIMES SAG;Lo;0;L;;;;;N;;;;;
+12009;CUNEIFORM SIGN A2;Lo;0;L;;;;;N;;;;;
+1200A;CUNEIFORM SIGN AB;Lo;0;L;;;;;N;;;;;
+1200B;CUNEIFORM SIGN AB TIMES ASH2;Lo;0;L;;;;;N;;;;;
+1200C;CUNEIFORM SIGN AB TIMES DUN3 GUNU;Lo;0;L;;;;;N;;;;;
+1200D;CUNEIFORM SIGN AB TIMES GAL;Lo;0;L;;;;;N;;;;;
+1200E;CUNEIFORM SIGN AB TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;;
+1200F;CUNEIFORM SIGN AB TIMES HA;Lo;0;L;;;;;N;;;;;
+12010;CUNEIFORM SIGN AB TIMES IGI GUNU;Lo;0;L;;;;;N;;;;;
+12011;CUNEIFORM SIGN AB TIMES IMIN;Lo;0;L;;;;;N;;;;;
+12012;CUNEIFORM SIGN AB TIMES LAGAB;Lo;0;L;;;;;N;;;;;
+12013;CUNEIFORM SIGN AB TIMES SHESH;Lo;0;L;;;;;N;;;;;
+12014;CUNEIFORM SIGN AB TIMES U PLUS U PLUS U;Lo;0;L;;;;;N;;;;;
+12015;CUNEIFORM SIGN AB GUNU;Lo;0;L;;;;;N;;;;;
+12016;CUNEIFORM SIGN AB2;Lo;0;L;;;;;N;;;;;
+12017;CUNEIFORM SIGN AB2 TIMES BALAG;Lo;0;L;;;;;N;;;;;
+12018;CUNEIFORM SIGN AB2 TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;;
+12019;CUNEIFORM SIGN AB2 TIMES ME PLUS EN;Lo;0;L;;;;;N;;;;;
+1201A;CUNEIFORM SIGN AB2 TIMES SHA3;Lo;0;L;;;;;N;;;;;
+1201B;CUNEIFORM SIGN AB2 TIMES TAK4;Lo;0;L;;;;;N;;;;;
+1201C;CUNEIFORM SIGN AD;Lo;0;L;;;;;N;;;;;
+1201D;CUNEIFORM SIGN AK;Lo;0;L;;;;;N;;;;;
+1201E;CUNEIFORM SIGN AK TIMES ERIN2;Lo;0;L;;;;;N;;;;;
+1201F;CUNEIFORM SIGN AK TIMES SHITA PLUS GISH;Lo;0;L;;;;;N;;;;;
+12020;CUNEIFORM SIGN AL;Lo;0;L;;;;;N;;;;;
+12021;CUNEIFORM SIGN AL TIMES AL;Lo;0;L;;;;;N;;;;;
+12022;CUNEIFORM SIGN AL TIMES DIM2;Lo;0;L;;;;;N;;;;;
+12023;CUNEIFORM SIGN AL TIMES GISH;Lo;0;L;;;;;N;;;;;
+12024;CUNEIFORM SIGN AL TIMES HA;Lo;0;L;;;;;N;;;;;
+12025;CUNEIFORM SIGN AL TIMES KAD3;Lo;0;L;;;;;N;;;;;
+12026;CUNEIFORM SIGN AL TIMES KI;Lo;0;L;;;;;N;;;;;
+12027;CUNEIFORM SIGN AL TIMES SHE;Lo;0;L;;;;;N;;;;;
+12028;CUNEIFORM SIGN AL TIMES USH;Lo;0;L;;;;;N;;;;;
+12029;CUNEIFORM SIGN ALAN;Lo;0;L;;;;;N;;;;;
+1202A;CUNEIFORM SIGN ALEPH;Lo;0;L;;;;;N;;;;;
+1202B;CUNEIFORM SIGN AMAR;Lo;0;L;;;;;N;;;;;
+1202C;CUNEIFORM SIGN AMAR TIMES SHE;Lo;0;L;;;;;N;;;;;
+1202D;CUNEIFORM SIGN AN;Lo;0;L;;;;;N;;;;;
+1202E;CUNEIFORM SIGN AN OVER AN;Lo;0;L;;;;;N;;;;;
+1202F;CUNEIFORM SIGN AN THREE TIMES;Lo;0;L;;;;;N;;;;;
+12030;CUNEIFORM SIGN AN PLUS NAGA OPPOSING AN PLUS NAGA;Lo;0;L;;;;;N;;;;;
+12031;CUNEIFORM SIGN AN PLUS NAGA SQUARED;Lo;0;L;;;;;N;;;;;
+12032;CUNEIFORM SIGN ANSHE;Lo;0;L;;;;;N;;;;;
+12033;CUNEIFORM SIGN APIN;Lo;0;L;;;;;N;;;;;
+12034;CUNEIFORM SIGN ARAD;Lo;0;L;;;;;N;;;;;
+12035;CUNEIFORM SIGN ARAD TIMES KUR;Lo;0;L;;;;;N;;;;;
+12036;CUNEIFORM SIGN ARKAB;Lo;0;L;;;;;N;;;;;
+12037;CUNEIFORM SIGN ASAL2;Lo;0;L;;;;;N;;;;;
+12038;CUNEIFORM SIGN ASH;Lo;0;L;;;;;N;;;;;
+12039;CUNEIFORM SIGN ASH ZIDA TENU;Lo;0;L;;;;;N;;;;;
+1203A;CUNEIFORM SIGN ASH KABA TENU;Lo;0;L;;;;;N;;;;;
+1203B;CUNEIFORM SIGN ASH OVER ASH TUG2 OVER TUG2 TUG2 OVER TUG2 PAP;Lo;0;L;;;;;N;;;;;
+1203C;CUNEIFORM SIGN ASH OVER ASH OVER ASH;Lo;0;L;;;;;N;;;;;
+1203D;CUNEIFORM SIGN ASH OVER ASH OVER ASH CROSSING ASH OVER ASH OVER ASH;Lo;0;L;;;;;N;;;;;
+1203E;CUNEIFORM SIGN ASH2;Lo;0;L;;;;;N;;;;;
+1203F;CUNEIFORM SIGN ASHGAB;Lo;0;L;;;;;N;;;;;
+12040;CUNEIFORM SIGN BA;Lo;0;L;;;;;N;;;;;
+12041;CUNEIFORM SIGN BAD;Lo;0;L;;;;;N;;;;;
+12042;CUNEIFORM SIGN BAG3;Lo;0;L;;;;;N;;;;;
+12043;CUNEIFORM SIGN BAHAR2;Lo;0;L;;;;;N;;;;;
+12044;CUNEIFORM SIGN BAL;Lo;0;L;;;;;N;;;;;
+12045;CUNEIFORM SIGN BAL OVER BAL;Lo;0;L;;;;;N;;;;;
+12046;CUNEIFORM SIGN BALAG;Lo;0;L;;;;;N;;;;;
+12047;CUNEIFORM SIGN BAR;Lo;0;L;;;;;N;;;;;
+12048;CUNEIFORM SIGN BARA2;Lo;0;L;;;;;N;;;;;
+12049;CUNEIFORM SIGN BI;Lo;0;L;;;;;N;;;;;
+1204A;CUNEIFORM SIGN BI TIMES A;Lo;0;L;;;;;N;;;;;
+1204B;CUNEIFORM SIGN BI TIMES GAR;Lo;0;L;;;;;N;;;;;
+1204C;CUNEIFORM SIGN BI TIMES IGI GUNU;Lo;0;L;;;;;N;;;;;
+1204D;CUNEIFORM SIGN BU;Lo;0;L;;;;;N;;;;;
+1204E;CUNEIFORM SIGN BU OVER BU AB;Lo;0;L;;;;;N;;;;;
+1204F;CUNEIFORM SIGN BU OVER BU UN;Lo;0;L;;;;;N;;;;;
+12050;CUNEIFORM SIGN BU CROSSING BU;Lo;0;L;;;;;N;;;;;
+12051;CUNEIFORM SIGN BULUG;Lo;0;L;;;;;N;;;;;
+12052;CUNEIFORM SIGN BULUG OVER BULUG;Lo;0;L;;;;;N;;;;;
+12053;CUNEIFORM SIGN BUR;Lo;0;L;;;;;N;;;;;
+12054;CUNEIFORM SIGN BUR2;Lo;0;L;;;;;N;;;;;
+12055;CUNEIFORM SIGN DA;Lo;0;L;;;;;N;;;;;
+12056;CUNEIFORM SIGN DAG;Lo;0;L;;;;;N;;;;;
+12057;CUNEIFORM SIGN DAG KISIM5 TIMES A PLUS MASH;Lo;0;L;;;;;N;;;;;
+12058;CUNEIFORM SIGN DAG KISIM5 TIMES AMAR;Lo;0;L;;;;;N;;;;;
+12059;CUNEIFORM SIGN DAG KISIM5 TIMES BALAG;Lo;0;L;;;;;N;;;;;
+1205A;CUNEIFORM SIGN DAG KISIM5 TIMES BI;Lo;0;L;;;;;N;;;;;
+1205B;CUNEIFORM SIGN DAG KISIM5 TIMES GA;Lo;0;L;;;;;N;;;;;
+1205C;CUNEIFORM SIGN DAG KISIM5 TIMES GA PLUS MASH;Lo;0;L;;;;;N;;;;;
+1205D;CUNEIFORM SIGN DAG KISIM5 TIMES GI;Lo;0;L;;;;;N;;;;;
+1205E;CUNEIFORM SIGN DAG KISIM5 TIMES GIR2;Lo;0;L;;;;;N;;;;;
+1205F;CUNEIFORM SIGN DAG KISIM5 TIMES GUD;Lo;0;L;;;;;N;;;;;
+12060;CUNEIFORM SIGN DAG KISIM5 TIMES HA;Lo;0;L;;;;;N;;;;;
+12061;CUNEIFORM SIGN DAG KISIM5 TIMES IR;Lo;0;L;;;;;N;;;;;
+12062;CUNEIFORM SIGN DAG KISIM5 TIMES IR PLUS LU;Lo;0;L;;;;;N;;;;;
+12063;CUNEIFORM SIGN DAG KISIM5 TIMES KAK;Lo;0;L;;;;;N;;;;;
+12064;CUNEIFORM SIGN DAG KISIM5 TIMES LA;Lo;0;L;;;;;N;;;;;
+12065;CUNEIFORM SIGN DAG KISIM5 TIMES LU;Lo;0;L;;;;;N;;;;;
+12066;CUNEIFORM SIGN DAG KISIM5 TIMES LU PLUS MASH2;Lo;0;L;;;;;N;;;;;
+12067;CUNEIFORM SIGN DAG KISIM5 TIMES LUM;Lo;0;L;;;;;N;;;;;
+12068;CUNEIFORM SIGN DAG KISIM5 TIMES NE;Lo;0;L;;;;;N;;;;;
+12069;CUNEIFORM SIGN DAG KISIM5 TIMES PAP PLUS PAP;Lo;0;L;;;;;N;;;;;
+1206A;CUNEIFORM SIGN DAG KISIM5 TIMES SI;Lo;0;L;;;;;N;;;;;
+1206B;CUNEIFORM SIGN DAG KISIM5 TIMES TAK4;Lo;0;L;;;;;N;;;;;
+1206C;CUNEIFORM SIGN DAG KISIM5 TIMES U2 PLUS GIR2;Lo;0;L;;;;;N;;;;;
+1206D;CUNEIFORM SIGN DAG KISIM5 TIMES USH;Lo;0;L;;;;;N;;;;;
+1206E;CUNEIFORM SIGN DAM;Lo;0;L;;;;;N;;;;;
+1206F;CUNEIFORM SIGN DAR;Lo;0;L;;;;;N;;;;;
+12070;CUNEIFORM SIGN DARA3;Lo;0;L;;;;;N;;;;;
+12071;CUNEIFORM SIGN DARA4;Lo;0;L;;;;;N;;;;;
+12072;CUNEIFORM SIGN DI;Lo;0;L;;;;;N;;;;;
+12073;CUNEIFORM SIGN DIB;Lo;0;L;;;;;N;;;;;
+12074;CUNEIFORM SIGN DIM;Lo;0;L;;;;;N;;;;;
+12075;CUNEIFORM SIGN DIM TIMES SHE;Lo;0;L;;;;;N;;;;;
+12076;CUNEIFORM SIGN DIM2;Lo;0;L;;;;;N;;;;;
+12077;CUNEIFORM SIGN DIN;Lo;0;L;;;;;N;;;;;
+12078;CUNEIFORM SIGN DIN KASKAL U GUNU DISH;Lo;0;L;;;;;N;;;;;
+12079;CUNEIFORM SIGN DISH;Lo;0;L;;;;;N;;;;;
+1207A;CUNEIFORM SIGN DU;Lo;0;L;;;;;N;;;;;
+1207B;CUNEIFORM SIGN DU OVER DU;Lo;0;L;;;;;N;;;;;
+1207C;CUNEIFORM SIGN DU GUNU;Lo;0;L;;;;;N;;;;;
+1207D;CUNEIFORM SIGN DU SHESHIG;Lo;0;L;;;;;N;;;;;
+1207E;CUNEIFORM SIGN DUB;Lo;0;L;;;;;N;;;;;
+1207F;CUNEIFORM SIGN DUB TIMES ESH2;Lo;0;L;;;;;N;;;;;
+12080;CUNEIFORM SIGN DUB2;Lo;0;L;;;;;N;;;;;
+12081;CUNEIFORM SIGN DUG;Lo;0;L;;;;;N;;;;;
+12082;CUNEIFORM SIGN DUGUD;Lo;0;L;;;;;N;;;;;
+12083;CUNEIFORM SIGN DUH;Lo;0;L;;;;;N;;;;;
+12084;CUNEIFORM SIGN DUN;Lo;0;L;;;;;N;;;;;
+12085;CUNEIFORM SIGN DUN3;Lo;0;L;;;;;N;;;;;
+12086;CUNEIFORM SIGN DUN3 GUNU;Lo;0;L;;;;;N;;;;;
+12087;CUNEIFORM SIGN DUN3 GUNU GUNU;Lo;0;L;;;;;N;;;;;
+12088;CUNEIFORM SIGN DUN4;Lo;0;L;;;;;N;;;;;
+12089;CUNEIFORM SIGN DUR2;Lo;0;L;;;;;N;;;;;
+1208A;CUNEIFORM SIGN E;Lo;0;L;;;;;N;;;;;
+1208B;CUNEIFORM SIGN E TIMES PAP;Lo;0;L;;;;;N;;;;;
+1208C;CUNEIFORM SIGN E OVER E NUN OVER NUN;Lo;0;L;;;;;N;;;;;
+1208D;CUNEIFORM SIGN E2;Lo;0;L;;;;;N;;;;;
+1208E;CUNEIFORM SIGN E2 TIMES A PLUS HA PLUS DA;Lo;0;L;;;;;N;;;;;
+1208F;CUNEIFORM SIGN E2 TIMES GAR;Lo;0;L;;;;;N;;;;;
+12090;CUNEIFORM SIGN E2 TIMES MI;Lo;0;L;;;;;N;;;;;
+12091;CUNEIFORM SIGN E2 TIMES SAL;Lo;0;L;;;;;N;;;;;
+12092;CUNEIFORM SIGN E2 TIMES SHE;Lo;0;L;;;;;N;;;;;
+12093;CUNEIFORM SIGN E2 TIMES U;Lo;0;L;;;;;N;;;;;
+12094;CUNEIFORM SIGN EDIN;Lo;0;L;;;;;N;;;;;
+12095;CUNEIFORM SIGN EGIR;Lo;0;L;;;;;N;;;;;
+12096;CUNEIFORM SIGN EL;Lo;0;L;;;;;N;;;;;
+12097;CUNEIFORM SIGN EN;Lo;0;L;;;;;N;;;;;
+12098;CUNEIFORM SIGN EN TIMES GAN2;Lo;0;L;;;;;N;;;;;
+12099;CUNEIFORM SIGN EN TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;;
+1209A;CUNEIFORM SIGN EN TIMES ME;Lo;0;L;;;;;N;;;;;
+1209B;CUNEIFORM SIGN EN CROSSING EN;Lo;0;L;;;;;N;;;;;
+1209C;CUNEIFORM SIGN EN OPPOSING EN;Lo;0;L;;;;;N;;;;;
+1209D;CUNEIFORM SIGN EN SQUARED;Lo;0;L;;;;;N;;;;;
+1209E;CUNEIFORM SIGN EREN;Lo;0;L;;;;;N;;;;;
+1209F;CUNEIFORM SIGN ERIN2;Lo;0;L;;;;;N;;;;;
+120A0;CUNEIFORM SIGN ESH2;Lo;0;L;;;;;N;;;;;
+120A1;CUNEIFORM SIGN EZEN;Lo;0;L;;;;;N;;;;;
+120A2;CUNEIFORM SIGN EZEN TIMES A;Lo;0;L;;;;;N;;;;;
+120A3;CUNEIFORM SIGN EZEN TIMES A PLUS LAL;Lo;0;L;;;;;N;;;;;
+120A4;CUNEIFORM SIGN EZEN TIMES A PLUS LAL TIMES LAL;Lo;0;L;;;;;N;;;;;
+120A5;CUNEIFORM SIGN EZEN TIMES AN;Lo;0;L;;;;;N;;;;;
+120A6;CUNEIFORM SIGN EZEN TIMES BAD;Lo;0;L;;;;;N;;;;;
+120A7;CUNEIFORM SIGN EZEN TIMES DUN3 GUNU;Lo;0;L;;;;;N;;;;;
+120A8;CUNEIFORM SIGN EZEN TIMES DUN3 GUNU GUNU;Lo;0;L;;;;;N;;;;;
+120A9;CUNEIFORM SIGN EZEN TIMES HA;Lo;0;L;;;;;N;;;;;
+120AA;CUNEIFORM SIGN EZEN TIMES HA GUNU;Lo;0;L;;;;;N;;;;;
+120AB;CUNEIFORM SIGN EZEN TIMES IGI GUNU;Lo;0;L;;;;;N;;;;;
+120AC;CUNEIFORM SIGN EZEN TIMES KASKAL;Lo;0;L;;;;;N;;;;;
+120AD;CUNEIFORM SIGN EZEN TIMES KASKAL SQUARED;Lo;0;L;;;;;N;;;;;
+120AE;CUNEIFORM SIGN EZEN TIMES KU3;Lo;0;L;;;;;N;;;;;
+120AF;CUNEIFORM SIGN EZEN TIMES LA;Lo;0;L;;;;;N;;;;;
+120B0;CUNEIFORM SIGN EZEN TIMES LAL TIMES LAL;Lo;0;L;;;;;N;;;;;
+120B1;CUNEIFORM SIGN EZEN TIMES LI;Lo;0;L;;;;;N;;;;;
+120B2;CUNEIFORM SIGN EZEN TIMES LU;Lo;0;L;;;;;N;;;;;
+120B3;CUNEIFORM SIGN EZEN TIMES U2;Lo;0;L;;;;;N;;;;;
+120B4;CUNEIFORM SIGN EZEN TIMES UD;Lo;0;L;;;;;N;;;;;
+120B5;CUNEIFORM SIGN GA;Lo;0;L;;;;;N;;;;;
+120B6;CUNEIFORM SIGN GA GUNU;Lo;0;L;;;;;N;;;;;
+120B7;CUNEIFORM SIGN GA2;Lo;0;L;;;;;N;;;;;
+120B8;CUNEIFORM SIGN GA2 TIMES A PLUS DA PLUS HA;Lo;0;L;;;;;N;;;;;
+120B9;CUNEIFORM SIGN GA2 TIMES A PLUS HA;Lo;0;L;;;;;N;;;;;
+120BA;CUNEIFORM SIGN GA2 TIMES A PLUS IGI;Lo;0;L;;;;;N;;;;;
+120BB;CUNEIFORM SIGN GA2 TIMES AB2 TENU PLUS TAB;Lo;0;L;;;;;N;;;;;
+120BC;CUNEIFORM SIGN GA2 TIMES AN;Lo;0;L;;;;;N;;;;;
+120BD;CUNEIFORM SIGN GA2 TIMES ASH;Lo;0;L;;;;;N;;;;;
+120BE;CUNEIFORM SIGN GA2 TIMES ASH2 PLUS GAL;Lo;0;L;;;;;N;;;;;
+120BF;CUNEIFORM SIGN GA2 TIMES BAD;Lo;0;L;;;;;N;;;;;
+120C0;CUNEIFORM SIGN GA2 TIMES BAR PLUS RA;Lo;0;L;;;;;N;;;;;
+120C1;CUNEIFORM SIGN GA2 TIMES BUR;Lo;0;L;;;;;N;;;;;
+120C2;CUNEIFORM SIGN GA2 TIMES BUR PLUS RA;Lo;0;L;;;;;N;;;;;
+120C3;CUNEIFORM SIGN GA2 TIMES DA;Lo;0;L;;;;;N;;;;;
+120C4;CUNEIFORM SIGN GA2 TIMES DI;Lo;0;L;;;;;N;;;;;
+120C5;CUNEIFORM SIGN GA2 TIMES DIM TIMES SHE;Lo;0;L;;;;;N;;;;;
+120C6;CUNEIFORM SIGN GA2 TIMES DUB;Lo;0;L;;;;;N;;;;;
+120C7;CUNEIFORM SIGN GA2 TIMES EL;Lo;0;L;;;;;N;;;;;
+120C8;CUNEIFORM SIGN GA2 TIMES EL PLUS LA;Lo;0;L;;;;;N;;;;;
+120C9;CUNEIFORM SIGN GA2 TIMES EN;Lo;0;L;;;;;N;;;;;
+120CA;CUNEIFORM SIGN GA2 TIMES EN TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;;
+120CB;CUNEIFORM SIGN GA2 TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;;
+120CC;CUNEIFORM SIGN GA2 TIMES GAR;Lo;0;L;;;;;N;;;;;
+120CD;CUNEIFORM SIGN GA2 TIMES GI;Lo;0;L;;;;;N;;;;;
+120CE;CUNEIFORM SIGN GA2 TIMES GI4;Lo;0;L;;;;;N;;;;;
+120CF;CUNEIFORM SIGN GA2 TIMES GI4 PLUS A;Lo;0;L;;;;;N;;;;;
+120D0;CUNEIFORM SIGN GA2 TIMES GIR2 PLUS SU;Lo;0;L;;;;;N;;;;;
+120D1;CUNEIFORM SIGN GA2 TIMES HA PLUS LU PLUS ESH2;Lo;0;L;;;;;N;;;;;
+120D2;CUNEIFORM SIGN GA2 TIMES HAL;Lo;0;L;;;;;N;;;;;
+120D3;CUNEIFORM SIGN GA2 TIMES HAL PLUS LA;Lo;0;L;;;;;N;;;;;
+120D4;CUNEIFORM SIGN GA2 TIMES HI PLUS LI;Lo;0;L;;;;;N;;;;;
+120D5;CUNEIFORM SIGN GA2 TIMES HUB2;Lo;0;L;;;;;N;;;;;
+120D6;CUNEIFORM SIGN GA2 TIMES IGI GUNU;Lo;0;L;;;;;N;;;;;
+120D7;CUNEIFORM SIGN GA2 TIMES ISH PLUS HU PLUS ASH;Lo;0;L;;;;;N;;;;;
+120D8;CUNEIFORM SIGN GA2 TIMES KAK;Lo;0;L;;;;;N;;;;;
+120D9;CUNEIFORM SIGN GA2 TIMES KASKAL;Lo;0;L;;;;;N;;;;;
+120DA;CUNEIFORM SIGN GA2 TIMES KID;Lo;0;L;;;;;N;;;;;
+120DB;CUNEIFORM SIGN GA2 TIMES KID PLUS LAL;Lo;0;L;;;;;N;;;;;
+120DC;CUNEIFORM SIGN GA2 TIMES KU3 PLUS AN;Lo;0;L;;;;;N;;;;;
+120DD;CUNEIFORM SIGN GA2 TIMES LA;Lo;0;L;;;;;N;;;;;
+120DE;CUNEIFORM SIGN GA2 TIMES ME PLUS EN;Lo;0;L;;;;;N;;;;;
+120DF;CUNEIFORM SIGN GA2 TIMES MI;Lo;0;L;;;;;N;;;;;
+120E0;CUNEIFORM SIGN GA2 TIMES NUN;Lo;0;L;;;;;N;;;;;
+120E1;CUNEIFORM SIGN GA2 TIMES NUN OVER NUN;Lo;0;L;;;;;N;;;;;
+120E2;CUNEIFORM SIGN GA2 TIMES PA;Lo;0;L;;;;;N;;;;;
+120E3;CUNEIFORM SIGN GA2 TIMES SAL;Lo;0;L;;;;;N;;;;;
+120E4;CUNEIFORM SIGN GA2 TIMES SAR;Lo;0;L;;;;;N;;;;;
+120E5;CUNEIFORM SIGN GA2 TIMES SHE;Lo;0;L;;;;;N;;;;;
+120E6;CUNEIFORM SIGN GA2 TIMES SHE PLUS TUR;Lo;0;L;;;;;N;;;;;
+120E7;CUNEIFORM SIGN GA2 TIMES SHID;Lo;0;L;;;;;N;;;;;
+120E8;CUNEIFORM SIGN GA2 TIMES SUM;Lo;0;L;;;;;N;;;;;
+120E9;CUNEIFORM SIGN GA2 TIMES TAK4;Lo;0;L;;;;;N;;;;;
+120EA;CUNEIFORM SIGN GA2 TIMES U;Lo;0;L;;;;;N;;;;;
+120EB;CUNEIFORM SIGN GA2 TIMES UD;Lo;0;L;;;;;N;;;;;
+120EC;CUNEIFORM SIGN GA2 TIMES UD PLUS DU;Lo;0;L;;;;;N;;;;;
+120ED;CUNEIFORM SIGN GA2 OVER GA2;Lo;0;L;;;;;N;;;;;
+120EE;CUNEIFORM SIGN GABA;Lo;0;L;;;;;N;;;;;
+120EF;CUNEIFORM SIGN GABA CROSSING GABA;Lo;0;L;;;;;N;;;;;
+120F0;CUNEIFORM SIGN GAD;Lo;0;L;;;;;N;;;;;
+120F1;CUNEIFORM SIGN GAD OVER GAD GAR OVER GAR;Lo;0;L;;;;;N;;;;;
+120F2;CUNEIFORM SIGN GAL;Lo;0;L;;;;;N;;;;;
+120F3;CUNEIFORM SIGN GAL GAD OVER GAD GAR OVER GAR;Lo;0;L;;;;;N;;;;;
+120F4;CUNEIFORM SIGN GALAM;Lo;0;L;;;;;N;;;;;
+120F5;CUNEIFORM SIGN GAM;Lo;0;L;;;;;N;;;;;
+120F6;CUNEIFORM SIGN GAN;Lo;0;L;;;;;N;;;;;
+120F7;CUNEIFORM SIGN GAN2;Lo;0;L;;;;;N;;;;;
+120F8;CUNEIFORM SIGN GAN2 TENU;Lo;0;L;;;;;N;;;;;
+120F9;CUNEIFORM SIGN GAN2 OVER GAN2;Lo;0;L;;;;;N;;;;;
+120FA;CUNEIFORM SIGN GAN2 CROSSING GAN2;Lo;0;L;;;;;N;;;;;
+120FB;CUNEIFORM SIGN GAR;Lo;0;L;;;;;N;;;;;
+120FC;CUNEIFORM SIGN GAR3;Lo;0;L;;;;;N;;;;;
+120FD;CUNEIFORM SIGN GASHAN;Lo;0;L;;;;;N;;;;;
+120FE;CUNEIFORM SIGN GESHTIN;Lo;0;L;;;;;N;;;;;
+120FF;CUNEIFORM SIGN GESHTIN TIMES KUR;Lo;0;L;;;;;N;;;;;
+12100;CUNEIFORM SIGN GI;Lo;0;L;;;;;N;;;;;
+12101;CUNEIFORM SIGN GI TIMES E;Lo;0;L;;;;;N;;;;;
+12102;CUNEIFORM SIGN GI TIMES U;Lo;0;L;;;;;N;;;;;
+12103;CUNEIFORM SIGN GI CROSSING GI;Lo;0;L;;;;;N;;;;;
+12104;CUNEIFORM SIGN GI4;Lo;0;L;;;;;N;;;;;
+12105;CUNEIFORM SIGN GI4 OVER GI4;Lo;0;L;;;;;N;;;;;
+12106;CUNEIFORM SIGN GI4 CROSSING GI4;Lo;0;L;;;;;N;;;;;
+12107;CUNEIFORM SIGN GIDIM;Lo;0;L;;;;;N;;;;;
+12108;CUNEIFORM SIGN GIR2;Lo;0;L;;;;;N;;;;;
+12109;CUNEIFORM SIGN GIR2 GUNU;Lo;0;L;;;;;N;;;;;
+1210A;CUNEIFORM SIGN GIR3;Lo;0;L;;;;;N;;;;;
+1210B;CUNEIFORM SIGN GIR3 TIMES A PLUS IGI;Lo;0;L;;;;;N;;;;;
+1210C;CUNEIFORM SIGN GIR3 TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;;
+1210D;CUNEIFORM SIGN GIR3 TIMES IGI;Lo;0;L;;;;;N;;;;;
+1210E;CUNEIFORM SIGN GIR3 TIMES LU PLUS IGI;Lo;0;L;;;;;N;;;;;
+1210F;CUNEIFORM SIGN GIR3 TIMES PA;Lo;0;L;;;;;N;;;;;
+12110;CUNEIFORM SIGN GISAL;Lo;0;L;;;;;N;;;;;
+12111;CUNEIFORM SIGN GISH;Lo;0;L;;;;;N;;;;;
+12112;CUNEIFORM SIGN GISH CROSSING GISH;Lo;0;L;;;;;N;;;;;
+12113;CUNEIFORM SIGN GISH TIMES BAD;Lo;0;L;;;;;N;;;;;
+12114;CUNEIFORM SIGN GISH TIMES TAK4;Lo;0;L;;;;;N;;;;;
+12115;CUNEIFORM SIGN GISH TENU;Lo;0;L;;;;;N;;;;;
+12116;CUNEIFORM SIGN GU;Lo;0;L;;;;;N;;;;;
+12117;CUNEIFORM SIGN GU CROSSING GU;Lo;0;L;;;;;N;;;;;
+12118;CUNEIFORM SIGN GU2;Lo;0;L;;;;;N;;;;;
+12119;CUNEIFORM SIGN GU2 TIMES KAK;Lo;0;L;;;;;N;;;;;
+1211A;CUNEIFORM SIGN GU2 TIMES KAK TIMES IGI GUNU;Lo;0;L;;;;;N;;;;;
+1211B;CUNEIFORM SIGN GU2 TIMES NUN;Lo;0;L;;;;;N;;;;;
+1211C;CUNEIFORM SIGN GU2 TIMES SAL PLUS TUG2;Lo;0;L;;;;;N;;;;;
+1211D;CUNEIFORM SIGN GU2 GUNU;Lo;0;L;;;;;N;;;;;
+1211E;CUNEIFORM SIGN GUD;Lo;0;L;;;;;N;;;;;
+1211F;CUNEIFORM SIGN GUD TIMES A PLUS KUR;Lo;0;L;;;;;N;;;;;
+12120;CUNEIFORM SIGN GUD TIMES KUR;Lo;0;L;;;;;N;;;;;
+12121;CUNEIFORM SIGN GUD OVER GUD LUGAL;Lo;0;L;;;;;N;;;;;
+12122;CUNEIFORM SIGN GUL;Lo;0;L;;;;;N;;;;;
+12123;CUNEIFORM SIGN GUM;Lo;0;L;;;;;N;;;;;
+12124;CUNEIFORM SIGN GUM TIMES SHE;Lo;0;L;;;;;N;;;;;
+12125;CUNEIFORM SIGN GUR;Lo;0;L;;;;;N;;;;;
+12126;CUNEIFORM SIGN GUR7;Lo;0;L;;;;;N;;;;;
+12127;CUNEIFORM SIGN GURUN;Lo;0;L;;;;;N;;;;;
+12128;CUNEIFORM SIGN GURUSH;Lo;0;L;;;;;N;;;;;
+12129;CUNEIFORM SIGN HA;Lo;0;L;;;;;N;;;;;
+1212A;CUNEIFORM SIGN HA TENU;Lo;0;L;;;;;N;;;;;
+1212B;CUNEIFORM SIGN HA GUNU;Lo;0;L;;;;;N;;;;;
+1212C;CUNEIFORM SIGN HAL;Lo;0;L;;;;;N;;;;;
+1212D;CUNEIFORM SIGN HI;Lo;0;L;;;;;N;;;;;
+1212E;CUNEIFORM SIGN HI TIMES ASH;Lo;0;L;;;;;N;;;;;
+1212F;CUNEIFORM SIGN HI TIMES ASH2;Lo;0;L;;;;;N;;;;;
+12130;CUNEIFORM SIGN HI TIMES BAD;Lo;0;L;;;;;N;;;;;
+12131;CUNEIFORM SIGN HI TIMES DISH;Lo;0;L;;;;;N;;;;;
+12132;CUNEIFORM SIGN HI TIMES GAD;Lo;0;L;;;;;N;;;;;
+12133;CUNEIFORM SIGN HI TIMES KIN;Lo;0;L;;;;;N;;;;;
+12134;CUNEIFORM SIGN HI TIMES NUN;Lo;0;L;;;;;N;;;;;
+12135;CUNEIFORM SIGN HI TIMES SHE;Lo;0;L;;;;;N;;;;;
+12136;CUNEIFORM SIGN HI TIMES U;Lo;0;L;;;;;N;;;;;
+12137;CUNEIFORM SIGN HU;Lo;0;L;;;;;N;;;;;
+12138;CUNEIFORM SIGN HUB2;Lo;0;L;;;;;N;;;;;
+12139;CUNEIFORM SIGN HUB2 TIMES AN;Lo;0;L;;;;;N;;;;;
+1213A;CUNEIFORM SIGN HUB2 TIMES HAL;Lo;0;L;;;;;N;;;;;
+1213B;CUNEIFORM SIGN HUB2 TIMES KASKAL;Lo;0;L;;;;;N;;;;;
+1213C;CUNEIFORM SIGN HUB2 TIMES LISH;Lo;0;L;;;;;N;;;;;
+1213D;CUNEIFORM SIGN HUB2 TIMES UD;Lo;0;L;;;;;N;;;;;
+1213E;CUNEIFORM SIGN HUL2;Lo;0;L;;;;;N;;;;;
+1213F;CUNEIFORM SIGN I;Lo;0;L;;;;;N;;;;;
+12140;CUNEIFORM SIGN I A;Lo;0;L;;;;;N;;;;;
+12141;CUNEIFORM SIGN IB;Lo;0;L;;;;;N;;;;;
+12142;CUNEIFORM SIGN IDIM;Lo;0;L;;;;;N;;;;;
+12143;CUNEIFORM SIGN IDIM OVER IDIM BUR;Lo;0;L;;;;;N;;;;;
+12144;CUNEIFORM SIGN IDIM OVER IDIM SQUARED;Lo;0;L;;;;;N;;;;;
+12145;CUNEIFORM SIGN IG;Lo;0;L;;;;;N;;;;;
+12146;CUNEIFORM SIGN IGI;Lo;0;L;;;;;N;;;;;
+12147;CUNEIFORM SIGN IGI DIB;Lo;0;L;;;;;N;;;;;
+12148;CUNEIFORM SIGN IGI RI;Lo;0;L;;;;;N;;;;;
+12149;CUNEIFORM SIGN IGI OVER IGI SHIR OVER SHIR UD OVER UD;Lo;0;L;;;;;N;;;;;
+1214A;CUNEIFORM SIGN IGI GUNU;Lo;0;L;;;;;N;;;;;
+1214B;CUNEIFORM SIGN IL;Lo;0;L;;;;;N;;;;;
+1214C;CUNEIFORM SIGN IL TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;;
+1214D;CUNEIFORM SIGN IL2;Lo;0;L;;;;;N;;;;;
+1214E;CUNEIFORM SIGN IM;Lo;0;L;;;;;N;;;;;
+1214F;CUNEIFORM SIGN IM TIMES TAK4;Lo;0;L;;;;;N;;;;;
+12150;CUNEIFORM SIGN IM CROSSING IM;Lo;0;L;;;;;N;;;;;
+12151;CUNEIFORM SIGN IM OPPOSING IM;Lo;0;L;;;;;N;;;;;
+12152;CUNEIFORM SIGN IM SQUARED;Lo;0;L;;;;;N;;;;;
+12153;CUNEIFORM SIGN IMIN;Lo;0;L;;;;;N;;;;;
+12154;CUNEIFORM SIGN IN;Lo;0;L;;;;;N;;;;;
+12155;CUNEIFORM SIGN IR;Lo;0;L;;;;;N;;;;;
+12156;CUNEIFORM SIGN ISH;Lo;0;L;;;;;N;;;;;
+12157;CUNEIFORM SIGN KA;Lo;0;L;;;;;N;;;;;
+12158;CUNEIFORM SIGN KA TIMES A;Lo;0;L;;;;;N;;;;;
+12159;CUNEIFORM SIGN KA TIMES AD;Lo;0;L;;;;;N;;;;;
+1215A;CUNEIFORM SIGN KA TIMES AD PLUS KU3;Lo;0;L;;;;;N;;;;;
+1215B;CUNEIFORM SIGN KA TIMES ASH2;Lo;0;L;;;;;N;;;;;
+1215C;CUNEIFORM SIGN KA TIMES BAD;Lo;0;L;;;;;N;;;;;
+1215D;CUNEIFORM SIGN KA TIMES BALAG;Lo;0;L;;;;;N;;;;;
+1215E;CUNEIFORM SIGN KA TIMES BAR;Lo;0;L;;;;;N;;;;;
+1215F;CUNEIFORM SIGN KA TIMES BI;Lo;0;L;;;;;N;;;;;
+12160;CUNEIFORM SIGN KA TIMES ERIN2;Lo;0;L;;;;;N;;;;;
+12161;CUNEIFORM SIGN KA TIMES ESH2;Lo;0;L;;;;;N;;;;;
+12162;CUNEIFORM SIGN KA TIMES GA;Lo;0;L;;;;;N;;;;;
+12163;CUNEIFORM SIGN KA TIMES GAL;Lo;0;L;;;;;N;;;;;
+12164;CUNEIFORM SIGN KA TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;;
+12165;CUNEIFORM SIGN KA TIMES GAR;Lo;0;L;;;;;N;;;;;
+12166;CUNEIFORM SIGN KA TIMES GAR PLUS SHA3 PLUS A;Lo;0;L;;;;;N;;;;;
+12167;CUNEIFORM SIGN KA TIMES GI;Lo;0;L;;;;;N;;;;;
+12168;CUNEIFORM SIGN KA TIMES GIR2;Lo;0;L;;;;;N;;;;;
+12169;CUNEIFORM SIGN KA TIMES GISH PLUS SAR;Lo;0;L;;;;;N;;;;;
+1216A;CUNEIFORM SIGN KA TIMES GISH CROSSING GISH;Lo;0;L;;;;;N;;;;;
+1216B;CUNEIFORM SIGN KA TIMES GU;Lo;0;L;;;;;N;;;;;
+1216C;CUNEIFORM SIGN KA TIMES GUR7;Lo;0;L;;;;;N;;;;;
+1216D;CUNEIFORM SIGN KA TIMES IGI;Lo;0;L;;;;;N;;;;;
+1216E;CUNEIFORM SIGN KA TIMES IM;Lo;0;L;;;;;N;;;;;
+1216F;CUNEIFORM SIGN KA TIMES KAK;Lo;0;L;;;;;N;;;;;
+12170;CUNEIFORM SIGN KA TIMES KI;Lo;0;L;;;;;N;;;;;
+12171;CUNEIFORM SIGN KA TIMES KID;Lo;0;L;;;;;N;;;;;
+12172;CUNEIFORM SIGN KA TIMES LI;Lo;0;L;;;;;N;;;;;
+12173;CUNEIFORM SIGN KA TIMES LU;Lo;0;L;;;;;N;;;;;
+12174;CUNEIFORM SIGN KA TIMES ME;Lo;0;L;;;;;N;;;;;
+12175;CUNEIFORM SIGN KA TIMES ME PLUS DU;Lo;0;L;;;;;N;;;;;
+12176;CUNEIFORM SIGN KA TIMES ME PLUS GI;Lo;0;L;;;;;N;;;;;
+12177;CUNEIFORM SIGN KA TIMES ME PLUS TE;Lo;0;L;;;;;N;;;;;
+12178;CUNEIFORM SIGN KA TIMES MI;Lo;0;L;;;;;N;;;;;
+12179;CUNEIFORM SIGN KA TIMES MI PLUS NUNUZ;Lo;0;L;;;;;N;;;;;
+1217A;CUNEIFORM SIGN KA TIMES NE;Lo;0;L;;;;;N;;;;;
+1217B;CUNEIFORM SIGN KA TIMES NUN;Lo;0;L;;;;;N;;;;;
+1217C;CUNEIFORM SIGN KA TIMES PI;Lo;0;L;;;;;N;;;;;
+1217D;CUNEIFORM SIGN KA TIMES RU;Lo;0;L;;;;;N;;;;;
+1217E;CUNEIFORM SIGN KA TIMES SA;Lo;0;L;;;;;N;;;;;
+1217F;CUNEIFORM SIGN KA TIMES SAR;Lo;0;L;;;;;N;;;;;
+12180;CUNEIFORM SIGN KA TIMES SHA;Lo;0;L;;;;;N;;;;;
+12181;CUNEIFORM SIGN KA TIMES SHE;Lo;0;L;;;;;N;;;;;
+12182;CUNEIFORM SIGN KA TIMES SHID;Lo;0;L;;;;;N;;;;;
+12183;CUNEIFORM SIGN KA TIMES SHU;Lo;0;L;;;;;N;;;;;
+12184;CUNEIFORM SIGN KA TIMES SIG;Lo;0;L;;;;;N;;;;;
+12185;CUNEIFORM SIGN KA TIMES SUHUR;Lo;0;L;;;;;N;;;;;
+12186;CUNEIFORM SIGN KA TIMES TAR;Lo;0;L;;;;;N;;;;;
+12187;CUNEIFORM SIGN KA TIMES U;Lo;0;L;;;;;N;;;;;
+12188;CUNEIFORM SIGN KA TIMES U2;Lo;0;L;;;;;N;;;;;
+12189;CUNEIFORM SIGN KA TIMES UD;Lo;0;L;;;;;N;;;;;
+1218A;CUNEIFORM SIGN KA TIMES UMUM TIMES PA;Lo;0;L;;;;;N;;;;;
+1218B;CUNEIFORM SIGN KA TIMES USH;Lo;0;L;;;;;N;;;;;
+1218C;CUNEIFORM SIGN KA TIMES ZI;Lo;0;L;;;;;N;;;;;
+1218D;CUNEIFORM SIGN KA2;Lo;0;L;;;;;N;;;;;
+1218E;CUNEIFORM SIGN KA2 CROSSING KA2;Lo;0;L;;;;;N;;;;;
+1218F;CUNEIFORM SIGN KAB;Lo;0;L;;;;;N;;;;;
+12190;CUNEIFORM SIGN KAD2;Lo;0;L;;;;;N;;;;;
+12191;CUNEIFORM SIGN KAD3;Lo;0;L;;;;;N;;;;;
+12192;CUNEIFORM SIGN KAD4;Lo;0;L;;;;;N;;;;;
+12193;CUNEIFORM SIGN KAD5;Lo;0;L;;;;;N;;;;;
+12194;CUNEIFORM SIGN KAD5 OVER KAD5;Lo;0;L;;;;;N;;;;;
+12195;CUNEIFORM SIGN KAK;Lo;0;L;;;;;N;;;;;
+12196;CUNEIFORM SIGN KAK TIMES IGI GUNU;Lo;0;L;;;;;N;;;;;
+12197;CUNEIFORM SIGN KAL;Lo;0;L;;;;;N;;;;;
+12198;CUNEIFORM SIGN KAL TIMES BAD;Lo;0;L;;;;;N;;;;;
+12199;CUNEIFORM SIGN KAL CROSSING KAL;Lo;0;L;;;;;N;;;;;
+1219A;CUNEIFORM SIGN KAM2;Lo;0;L;;;;;N;;;;;
+1219B;CUNEIFORM SIGN KAM4;Lo;0;L;;;;;N;;;;;
+1219C;CUNEIFORM SIGN KASKAL;Lo;0;L;;;;;N;;;;;
+1219D;CUNEIFORM SIGN KASKAL LAGAB TIMES U OVER LAGAB TIMES U;Lo;0;L;;;;;N;;;;;
+1219E;CUNEIFORM SIGN KASKAL OVER KASKAL LAGAB TIMES U OVER LAGAB TIMES U;Lo;0;L;;;;;N;;;;;
+1219F;CUNEIFORM SIGN KESH2;Lo;0;L;;;;;N;;;;;
+121A0;CUNEIFORM SIGN KI;Lo;0;L;;;;;N;;;;;
+121A1;CUNEIFORM SIGN KI TIMES BAD;Lo;0;L;;;;;N;;;;;
+121A2;CUNEIFORM SIGN KI TIMES U;Lo;0;L;;;;;N;;;;;
+121A3;CUNEIFORM SIGN KI TIMES UD;Lo;0;L;;;;;N;;;;;
+121A4;CUNEIFORM SIGN KID;Lo;0;L;;;;;N;;;;;
+121A5;CUNEIFORM SIGN KIN;Lo;0;L;;;;;N;;;;;
+121A6;CUNEIFORM SIGN KISAL;Lo;0;L;;;;;N;;;;;
+121A7;CUNEIFORM SIGN KISH;Lo;0;L;;;;;N;;;;;
+121A8;CUNEIFORM SIGN KISIM5;Lo;0;L;;;;;N;;;;;
+121A9;CUNEIFORM SIGN KISIM5 OVER KISIM5;Lo;0;L;;;;;N;;;;;
+121AA;CUNEIFORM SIGN KU;Lo;0;L;;;;;N;;;;;
+121AB;CUNEIFORM SIGN KU OVER HI TIMES ASH2 KU OVER HI TIMES ASH2;Lo;0;L;;;;;N;;;;;
+121AC;CUNEIFORM SIGN KU3;Lo;0;L;;;;;N;;;;;
+121AD;CUNEIFORM SIGN KU4;Lo;0;L;;;;;N;;;;;
+121AE;CUNEIFORM SIGN KU4 VARIANT FORM;Lo;0;L;;;;;N;;;;;
+121AF;CUNEIFORM SIGN KU7;Lo;0;L;;;;;N;;;;;
+121B0;CUNEIFORM SIGN KUL;Lo;0;L;;;;;N;;;;;
+121B1;CUNEIFORM SIGN KUL GUNU;Lo;0;L;;;;;N;;;;;
+121B2;CUNEIFORM SIGN KUN;Lo;0;L;;;;;N;;;;;
+121B3;CUNEIFORM SIGN KUR;Lo;0;L;;;;;N;;;;;
+121B4;CUNEIFORM SIGN KUR OPPOSING KUR;Lo;0;L;;;;;N;;;;;
+121B5;CUNEIFORM SIGN KUSHU2;Lo;0;L;;;;;N;;;;;
+121B6;CUNEIFORM SIGN KWU318;Lo;0;L;;;;;N;;;;;
+121B7;CUNEIFORM SIGN LA;Lo;0;L;;;;;N;;;;;
+121B8;CUNEIFORM SIGN LAGAB;Lo;0;L;;;;;N;;;;;
+121B9;CUNEIFORM SIGN LAGAB TIMES A;Lo;0;L;;;;;N;;;;;
+121BA;CUNEIFORM SIGN LAGAB TIMES A PLUS DA PLUS HA;Lo;0;L;;;;;N;;;;;
+121BB;CUNEIFORM SIGN LAGAB TIMES A PLUS GAR;Lo;0;L;;;;;N;;;;;
+121BC;CUNEIFORM SIGN LAGAB TIMES A PLUS LAL;Lo;0;L;;;;;N;;;;;
+121BD;CUNEIFORM SIGN LAGAB TIMES AL;Lo;0;L;;;;;N;;;;;
+121BE;CUNEIFORM SIGN LAGAB TIMES AN;Lo;0;L;;;;;N;;;;;
+121BF;CUNEIFORM SIGN LAGAB TIMES ASH ZIDA TENU;Lo;0;L;;;;;N;;;;;
+121C0;CUNEIFORM SIGN LAGAB TIMES BAD;Lo;0;L;;;;;N;;;;;
+121C1;CUNEIFORM SIGN LAGAB TIMES BI;Lo;0;L;;;;;N;;;;;
+121C2;CUNEIFORM SIGN LAGAB TIMES DAR;Lo;0;L;;;;;N;;;;;
+121C3;CUNEIFORM SIGN LAGAB TIMES EN;Lo;0;L;;;;;N;;;;;
+121C4;CUNEIFORM SIGN LAGAB TIMES GA;Lo;0;L;;;;;N;;;;;
+121C5;CUNEIFORM SIGN LAGAB TIMES GAR;Lo;0;L;;;;;N;;;;;
+121C6;CUNEIFORM SIGN LAGAB TIMES GUD;Lo;0;L;;;;;N;;;;;
+121C7;CUNEIFORM SIGN LAGAB TIMES GUD PLUS GUD;Lo;0;L;;;;;N;;;;;
+121C8;CUNEIFORM SIGN LAGAB TIMES HA;Lo;0;L;;;;;N;;;;;
+121C9;CUNEIFORM SIGN LAGAB TIMES HAL;Lo;0;L;;;;;N;;;;;
+121CA;CUNEIFORM SIGN LAGAB TIMES HI TIMES NUN;Lo;0;L;;;;;N;;;;;
+121CB;CUNEIFORM SIGN LAGAB TIMES IGI GUNU;Lo;0;L;;;;;N;;;;;
+121CC;CUNEIFORM SIGN LAGAB TIMES IM;Lo;0;L;;;;;N;;;;;
+121CD;CUNEIFORM SIGN LAGAB TIMES IM PLUS HA;Lo;0;L;;;;;N;;;;;
+121CE;CUNEIFORM SIGN LAGAB TIMES IM PLUS LU;Lo;0;L;;;;;N;;;;;
+121CF;CUNEIFORM SIGN LAGAB TIMES KI;Lo;0;L;;;;;N;;;;;
+121D0;CUNEIFORM SIGN LAGAB TIMES KIN;Lo;0;L;;;;;N;;;;;
+121D1;CUNEIFORM SIGN LAGAB TIMES KU3;Lo;0;L;;;;;N;;;;;
+121D2;CUNEIFORM SIGN LAGAB TIMES KUL;Lo;0;L;;;;;N;;;;;
+121D3;CUNEIFORM SIGN LAGAB TIMES KUL PLUS HI PLUS A;Lo;0;L;;;;;N;;;;;
+121D4;CUNEIFORM SIGN LAGAB TIMES LAGAB;Lo;0;L;;;;;N;;;;;
+121D5;CUNEIFORM SIGN LAGAB TIMES LISH;Lo;0;L;;;;;N;;;;;
+121D6;CUNEIFORM SIGN LAGAB TIMES LU;Lo;0;L;;;;;N;;;;;
+121D7;CUNEIFORM SIGN LAGAB TIMES LUL;Lo;0;L;;;;;N;;;;;
+121D8;CUNEIFORM SIGN LAGAB TIMES ME;Lo;0;L;;;;;N;;;;;
+121D9;CUNEIFORM SIGN LAGAB TIMES ME PLUS EN;Lo;0;L;;;;;N;;;;;
+121DA;CUNEIFORM SIGN LAGAB TIMES MUSH;Lo;0;L;;;;;N;;;;;
+121DB;CUNEIFORM SIGN LAGAB TIMES NE;Lo;0;L;;;;;N;;;;;
+121DC;CUNEIFORM SIGN LAGAB TIMES SHE PLUS SUM;Lo;0;L;;;;;N;;;;;
+121DD;CUNEIFORM SIGN LAGAB TIMES SHITA PLUS GISH PLUS ERIN2;Lo;0;L;;;;;N;;;;;
+121DE;CUNEIFORM SIGN LAGAB TIMES SHITA PLUS GISH TENU;Lo;0;L;;;;;N;;;;;
+121DF;CUNEIFORM SIGN LAGAB TIMES SHU2;Lo;0;L;;;;;N;;;;;
+121E0;CUNEIFORM SIGN LAGAB TIMES SHU2 PLUS SHU2;Lo;0;L;;;;;N;;;;;
+121E1;CUNEIFORM SIGN LAGAB TIMES SUM;Lo;0;L;;;;;N;;;;;
+121E2;CUNEIFORM SIGN LAGAB TIMES TAG;Lo;0;L;;;;;N;;;;;
+121E3;CUNEIFORM SIGN LAGAB TIMES TAK4;Lo;0;L;;;;;N;;;;;
+121E4;CUNEIFORM SIGN LAGAB TIMES TE PLUS A PLUS SU PLUS NA;Lo;0;L;;;;;N;;;;;
+121E5;CUNEIFORM SIGN LAGAB TIMES U;Lo;0;L;;;;;N;;;;;
+121E6;CUNEIFORM SIGN LAGAB TIMES U PLUS A;Lo;0;L;;;;;N;;;;;
+121E7;CUNEIFORM SIGN LAGAB TIMES U PLUS U PLUS U;Lo;0;L;;;;;N;;;;;
+121E8;CUNEIFORM SIGN LAGAB TIMES U2 PLUS ASH;Lo;0;L;;;;;N;;;;;
+121E9;CUNEIFORM SIGN LAGAB TIMES UD;Lo;0;L;;;;;N;;;;;
+121EA;CUNEIFORM SIGN LAGAB TIMES USH;Lo;0;L;;;;;N;;;;;
+121EB;CUNEIFORM SIGN LAGAB SQUARED;Lo;0;L;;;;;N;;;;;
+121EC;CUNEIFORM SIGN LAGAR;Lo;0;L;;;;;N;;;;;
+121ED;CUNEIFORM SIGN LAGAR TIMES SHE;Lo;0;L;;;;;N;;;;;
+121EE;CUNEIFORM SIGN LAGAR TIMES SHE PLUS SUM;Lo;0;L;;;;;N;;;;;
+121EF;CUNEIFORM SIGN LAGAR GUNU;Lo;0;L;;;;;N;;;;;
+121F0;CUNEIFORM SIGN LAGAR GUNU OVER LAGAR GUNU SHE;Lo;0;L;;;;;N;;;;;
+121F1;CUNEIFORM SIGN LAHSHU;Lo;0;L;;;;;N;;;;;
+121F2;CUNEIFORM SIGN LAL;Lo;0;L;;;;;N;;;;;
+121F3;CUNEIFORM SIGN LAL TIMES LAL;Lo;0;L;;;;;N;;;;;
+121F4;CUNEIFORM SIGN LAM;Lo;0;L;;;;;N;;;;;
+121F5;CUNEIFORM SIGN LAM TIMES KUR;Lo;0;L;;;;;N;;;;;
+121F6;CUNEIFORM SIGN LAM TIMES KUR PLUS RU;Lo;0;L;;;;;N;;;;;
+121F7;CUNEIFORM SIGN LI;Lo;0;L;;;;;N;;;;;
+121F8;CUNEIFORM SIGN LIL;Lo;0;L;;;;;N;;;;;
+121F9;CUNEIFORM SIGN LIMMU2;Lo;0;L;;;;;N;;;;;
+121FA;CUNEIFORM SIGN LISH;Lo;0;L;;;;;N;;;;;
+121FB;CUNEIFORM SIGN LU;Lo;0;L;;;;;N;;;;;
+121FC;CUNEIFORM SIGN LU TIMES BAD;Lo;0;L;;;;;N;;;;;
+121FD;CUNEIFORM SIGN LU2;Lo;0;L;;;;;N;;;;;
+121FE;CUNEIFORM SIGN LU2 TIMES AL;Lo;0;L;;;;;N;;;;;
+121FF;CUNEIFORM SIGN LU2 TIMES BAD;Lo;0;L;;;;;N;;;;;
+12200;CUNEIFORM SIGN LU2 TIMES ESH2;Lo;0;L;;;;;N;;;;;
+12201;CUNEIFORM SIGN LU2 TIMES ESH2 TENU;Lo;0;L;;;;;N;;;;;
+12202;CUNEIFORM SIGN LU2 TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;;
+12203;CUNEIFORM SIGN LU2 TIMES HI TIMES BAD;Lo;0;L;;;;;N;;;;;
+12204;CUNEIFORM SIGN LU2 TIMES IM;Lo;0;L;;;;;N;;;;;
+12205;CUNEIFORM SIGN LU2 TIMES KAD2;Lo;0;L;;;;;N;;;;;
+12206;CUNEIFORM SIGN LU2 TIMES KAD3;Lo;0;L;;;;;N;;;;;
+12207;CUNEIFORM SIGN LU2 TIMES KAD3 PLUS ASH;Lo;0;L;;;;;N;;;;;
+12208;CUNEIFORM SIGN LU2 TIMES KI;Lo;0;L;;;;;N;;;;;
+12209;CUNEIFORM SIGN LU2 TIMES LA PLUS ASH;Lo;0;L;;;;;N;;;;;
+1220A;CUNEIFORM SIGN LU2 TIMES LAGAB;Lo;0;L;;;;;N;;;;;
+1220B;CUNEIFORM SIGN LU2 TIMES ME PLUS EN;Lo;0;L;;;;;N;;;;;
+1220C;CUNEIFORM SIGN LU2 TIMES NE;Lo;0;L;;;;;N;;;;;
+1220D;CUNEIFORM SIGN LU2 TIMES NU;Lo;0;L;;;;;N;;;;;
+1220E;CUNEIFORM SIGN LU2 TIMES SI PLUS ASH;Lo;0;L;;;;;N;;;;;
+1220F;CUNEIFORM SIGN LU2 TIMES SIK2 PLUS BU;Lo;0;L;;;;;N;;;;;
+12210;CUNEIFORM SIGN LU2 TIMES TUG2;Lo;0;L;;;;;N;;;;;
+12211;CUNEIFORM SIGN LU2 TENU;Lo;0;L;;;;;N;;;;;
+12212;CUNEIFORM SIGN LU2 CROSSING LU2;Lo;0;L;;;;;N;;;;;
+12213;CUNEIFORM SIGN LU2 OPPOSING LU2;Lo;0;L;;;;;N;;;;;
+12214;CUNEIFORM SIGN LU2 SQUARED;Lo;0;L;;;;;N;;;;;
+12215;CUNEIFORM SIGN LU2 SHESHIG;Lo;0;L;;;;;N;;;;;
+12216;CUNEIFORM SIGN LU3;Lo;0;L;;;;;N;;;;;
+12217;CUNEIFORM SIGN LUGAL;Lo;0;L;;;;;N;;;;;
+12218;CUNEIFORM SIGN LUGAL OVER LUGAL;Lo;0;L;;;;;N;;;;;
+12219;CUNEIFORM SIGN LUGAL OPPOSING LUGAL;Lo;0;L;;;;;N;;;;;
+1221A;CUNEIFORM SIGN LUGAL SHESHIG;Lo;0;L;;;;;N;;;;;
+1221B;CUNEIFORM SIGN LUH;Lo;0;L;;;;;N;;;;;
+1221C;CUNEIFORM SIGN LUL;Lo;0;L;;;;;N;;;;;
+1221D;CUNEIFORM SIGN LUM;Lo;0;L;;;;;N;;;;;
+1221E;CUNEIFORM SIGN LUM OVER LUM;Lo;0;L;;;;;N;;;;;
+1221F;CUNEIFORM SIGN LUM OVER LUM GAR OVER GAR;Lo;0;L;;;;;N;;;;;
+12220;CUNEIFORM SIGN MA;Lo;0;L;;;;;N;;;;;
+12221;CUNEIFORM SIGN MA TIMES TAK4;Lo;0;L;;;;;N;;;;;
+12222;CUNEIFORM SIGN MA GUNU;Lo;0;L;;;;;N;;;;;
+12223;CUNEIFORM SIGN MA2;Lo;0;L;;;;;N;;;;;
+12224;CUNEIFORM SIGN MAH;Lo;0;L;;;;;N;;;;;
+12225;CUNEIFORM SIGN MAR;Lo;0;L;;;;;N;;;;;
+12226;CUNEIFORM SIGN MASH;Lo;0;L;;;;;N;;;;;
+12227;CUNEIFORM SIGN MASH2;Lo;0;L;;;;;N;;;;;
+12228;CUNEIFORM SIGN ME;Lo;0;L;;;;;N;;;;;
+12229;CUNEIFORM SIGN MES;Lo;0;L;;;;;N;;;;;
+1222A;CUNEIFORM SIGN MI;Lo;0;L;;;;;N;;;;;
+1222B;CUNEIFORM SIGN MIN;Lo;0;L;;;;;N;;;;;
+1222C;CUNEIFORM SIGN MU;Lo;0;L;;;;;N;;;;;
+1222D;CUNEIFORM SIGN MU OVER MU;Lo;0;L;;;;;N;;;;;
+1222E;CUNEIFORM SIGN MUG;Lo;0;L;;;;;N;;;;;
+1222F;CUNEIFORM SIGN MUG GUNU;Lo;0;L;;;;;N;;;;;
+12230;CUNEIFORM SIGN MUNSUB;Lo;0;L;;;;;N;;;;;
+12231;CUNEIFORM SIGN MURGU2;Lo;0;L;;;;;N;;;;;
+12232;CUNEIFORM SIGN MUSH;Lo;0;L;;;;;N;;;;;
+12233;CUNEIFORM SIGN MUSH TIMES A;Lo;0;L;;;;;N;;;;;
+12234;CUNEIFORM SIGN MUSH TIMES KUR;Lo;0;L;;;;;N;;;;;
+12235;CUNEIFORM SIGN MUSH TIMES ZA;Lo;0;L;;;;;N;;;;;
+12236;CUNEIFORM SIGN MUSH OVER MUSH;Lo;0;L;;;;;N;;;;;
+12237;CUNEIFORM SIGN MUSH OVER MUSH TIMES A PLUS NA;Lo;0;L;;;;;N;;;;;
+12238;CUNEIFORM SIGN MUSH CROSSING MUSH;Lo;0;L;;;;;N;;;;;
+12239;CUNEIFORM SIGN MUSH3;Lo;0;L;;;;;N;;;;;
+1223A;CUNEIFORM SIGN MUSH3 TIMES A;Lo;0;L;;;;;N;;;;;
+1223B;CUNEIFORM SIGN MUSH3 TIMES A PLUS DI;Lo;0;L;;;;;N;;;;;
+1223C;CUNEIFORM SIGN MUSH3 TIMES DI;Lo;0;L;;;;;N;;;;;
+1223D;CUNEIFORM SIGN MUSH3 GUNU;Lo;0;L;;;;;N;;;;;
+1223E;CUNEIFORM SIGN NA;Lo;0;L;;;;;N;;;;;
+1223F;CUNEIFORM SIGN NA2;Lo;0;L;;;;;N;;;;;
+12240;CUNEIFORM SIGN NAGA;Lo;0;L;;;;;N;;;;;
+12241;CUNEIFORM SIGN NAGA INVERTED;Lo;0;L;;;;;N;;;;;
+12242;CUNEIFORM SIGN NAGA TIMES SHU TENU;Lo;0;L;;;;;N;;;;;
+12243;CUNEIFORM SIGN NAGA OPPOSING NAGA;Lo;0;L;;;;;N;;;;;
+12244;CUNEIFORM SIGN NAGAR;Lo;0;L;;;;;N;;;;;
+12245;CUNEIFORM SIGN NAM NUTILLU;Lo;0;L;;;;;N;;;;;
+12246;CUNEIFORM SIGN NAM;Lo;0;L;;;;;N;;;;;
+12247;CUNEIFORM SIGN NAM2;Lo;0;L;;;;;N;;;;;
+12248;CUNEIFORM SIGN NE;Lo;0;L;;;;;N;;;;;
+12249;CUNEIFORM SIGN NE TIMES A;Lo;0;L;;;;;N;;;;;
+1224A;CUNEIFORM SIGN NE TIMES UD;Lo;0;L;;;;;N;;;;;
+1224B;CUNEIFORM SIGN NE SHESHIG;Lo;0;L;;;;;N;;;;;
+1224C;CUNEIFORM SIGN NI;Lo;0;L;;;;;N;;;;;
+1224D;CUNEIFORM SIGN NI TIMES E;Lo;0;L;;;;;N;;;;;
+1224E;CUNEIFORM SIGN NI2;Lo;0;L;;;;;N;;;;;
+1224F;CUNEIFORM SIGN NIM;Lo;0;L;;;;;N;;;;;
+12250;CUNEIFORM SIGN NIM TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;;
+12251;CUNEIFORM SIGN NIM TIMES GAR PLUS GAN2 TENU;Lo;0;L;;;;;N;;;;;
+12252;CUNEIFORM SIGN NINDA2;Lo;0;L;;;;;N;;;;;
+12253;CUNEIFORM SIGN NINDA2 TIMES AN;Lo;0;L;;;;;N;;;;;
+12254;CUNEIFORM SIGN NINDA2 TIMES ASH;Lo;0;L;;;;;N;;;;;
+12255;CUNEIFORM SIGN NINDA2 TIMES ASH PLUS ASH;Lo;0;L;;;;;N;;;;;
+12256;CUNEIFORM SIGN NINDA2 TIMES GUD;Lo;0;L;;;;;N;;;;;
+12257;CUNEIFORM SIGN NINDA2 TIMES ME PLUS GAN2 TENU;Lo;0;L;;;;;N;;;;;
+12258;CUNEIFORM SIGN NINDA2 TIMES NE;Lo;0;L;;;;;N;;;;;
+12259;CUNEIFORM SIGN NINDA2 TIMES NUN;Lo;0;L;;;;;N;;;;;
+1225A;CUNEIFORM SIGN NINDA2 TIMES SHE;Lo;0;L;;;;;N;;;;;
+1225B;CUNEIFORM SIGN NINDA2 TIMES SHE PLUS A AN;Lo;0;L;;;;;N;;;;;
+1225C;CUNEIFORM SIGN NINDA2 TIMES SHE PLUS ASH;Lo;0;L;;;;;N;;;;;
+1225D;CUNEIFORM SIGN NINDA2 TIMES SHE PLUS ASH PLUS ASH;Lo;0;L;;;;;N;;;;;
+1225E;CUNEIFORM SIGN NINDA2 TIMES U2 PLUS ASH;Lo;0;L;;;;;N;;;;;
+1225F;CUNEIFORM SIGN NINDA2 TIMES USH;Lo;0;L;;;;;N;;;;;
+12260;CUNEIFORM SIGN NISAG;Lo;0;L;;;;;N;;;;;
+12261;CUNEIFORM SIGN NU;Lo;0;L;;;;;N;;;;;
+12262;CUNEIFORM SIGN NU11;Lo;0;L;;;;;N;;;;;
+12263;CUNEIFORM SIGN NUN;Lo;0;L;;;;;N;;;;;
+12264;CUNEIFORM SIGN NUN LAGAR TIMES GAR;Lo;0;L;;;;;N;;;;;
+12265;CUNEIFORM SIGN NUN LAGAR TIMES MASH;Lo;0;L;;;;;N;;;;;
+12266;CUNEIFORM SIGN NUN LAGAR TIMES SAL;Lo;0;L;;;;;N;;;;;
+12267;CUNEIFORM SIGN NUN LAGAR TIMES SAL OVER NUN LAGAR TIMES SAL;Lo;0;L;;;;;N;;;;;
+12268;CUNEIFORM SIGN NUN LAGAR TIMES USH;Lo;0;L;;;;;N;;;;;
+12269;CUNEIFORM SIGN NUN TENU;Lo;0;L;;;;;N;;;;;
+1226A;CUNEIFORM SIGN NUN OVER NUN;Lo;0;L;;;;;N;;;;;
+1226B;CUNEIFORM SIGN NUN CROSSING NUN;Lo;0;L;;;;;N;;;;;
+1226C;CUNEIFORM SIGN NUN CROSSING NUN LAGAR OVER LAGAR;Lo;0;L;;;;;N;;;;;
+1226D;CUNEIFORM SIGN NUNUZ;Lo;0;L;;;;;N;;;;;
+1226E;CUNEIFORM SIGN NUNUZ AB2 TIMES ASHGAB;Lo;0;L;;;;;N;;;;;
+1226F;CUNEIFORM SIGN NUNUZ AB2 TIMES BI;Lo;0;L;;;;;N;;;;;
+12270;CUNEIFORM SIGN NUNUZ AB2 TIMES DUG;Lo;0;L;;;;;N;;;;;
+12271;CUNEIFORM SIGN NUNUZ AB2 TIMES GUD;Lo;0;L;;;;;N;;;;;
+12272;CUNEIFORM SIGN NUNUZ AB2 TIMES IGI GUNU;Lo;0;L;;;;;N;;;;;
+12273;CUNEIFORM SIGN NUNUZ AB2 TIMES KAD3;Lo;0;L;;;;;N;;;;;
+12274;CUNEIFORM SIGN NUNUZ AB2 TIMES LA;Lo;0;L;;;;;N;;;;;
+12275;CUNEIFORM SIGN NUNUZ AB2 TIMES NE;Lo;0;L;;;;;N;;;;;
+12276;CUNEIFORM SIGN NUNUZ AB2 TIMES SILA3;Lo;0;L;;;;;N;;;;;
+12277;CUNEIFORM SIGN NUNUZ AB2 TIMES U2;Lo;0;L;;;;;N;;;;;
+12278;CUNEIFORM SIGN NUNUZ KISIM5 TIMES BI;Lo;0;L;;;;;N;;;;;
+12279;CUNEIFORM SIGN NUNUZ KISIM5 TIMES BI U;Lo;0;L;;;;;N;;;;;
+1227A;CUNEIFORM SIGN PA;Lo;0;L;;;;;N;;;;;
+1227B;CUNEIFORM SIGN PAD;Lo;0;L;;;;;N;;;;;
+1227C;CUNEIFORM SIGN PAN;Lo;0;L;;;;;N;;;;;
+1227D;CUNEIFORM SIGN PAP;Lo;0;L;;;;;N;;;;;
+1227E;CUNEIFORM SIGN PESH2;Lo;0;L;;;;;N;;;;;
+1227F;CUNEIFORM SIGN PI;Lo;0;L;;;;;N;;;;;
+12280;CUNEIFORM SIGN PI TIMES A;Lo;0;L;;;;;N;;;;;
+12281;CUNEIFORM SIGN PI TIMES AB;Lo;0;L;;;;;N;;;;;
+12282;CUNEIFORM SIGN PI TIMES BI;Lo;0;L;;;;;N;;;;;
+12283;CUNEIFORM SIGN PI TIMES BU;Lo;0;L;;;;;N;;;;;
+12284;CUNEIFORM SIGN PI TIMES E;Lo;0;L;;;;;N;;;;;
+12285;CUNEIFORM SIGN PI TIMES I;Lo;0;L;;;;;N;;;;;
+12286;CUNEIFORM SIGN PI TIMES IB;Lo;0;L;;;;;N;;;;;
+12287;CUNEIFORM SIGN PI TIMES U;Lo;0;L;;;;;N;;;;;
+12288;CUNEIFORM SIGN PI TIMES U2;Lo;0;L;;;;;N;;;;;
+12289;CUNEIFORM SIGN PI CROSSING PI;Lo;0;L;;;;;N;;;;;
+1228A;CUNEIFORM SIGN PIRIG;Lo;0;L;;;;;N;;;;;
+1228B;CUNEIFORM SIGN PIRIG TIMES KAL;Lo;0;L;;;;;N;;;;;
+1228C;CUNEIFORM SIGN PIRIG TIMES UD;Lo;0;L;;;;;N;;;;;
+1228D;CUNEIFORM SIGN PIRIG TIMES ZA;Lo;0;L;;;;;N;;;;;
+1228E;CUNEIFORM SIGN PIRIG OPPOSING PIRIG;Lo;0;L;;;;;N;;;;;
+1228F;CUNEIFORM SIGN RA;Lo;0;L;;;;;N;;;;;
+12290;CUNEIFORM SIGN RAB;Lo;0;L;;;;;N;;;;;
+12291;CUNEIFORM SIGN RI;Lo;0;L;;;;;N;;;;;
+12292;CUNEIFORM SIGN RU;Lo;0;L;;;;;N;;;;;
+12293;CUNEIFORM SIGN SA;Lo;0;L;;;;;N;;;;;
+12294;CUNEIFORM SIGN SAG NUTILLU;Lo;0;L;;;;;N;;;;;
+12295;CUNEIFORM SIGN SAG;Lo;0;L;;;;;N;;;;;
+12296;CUNEIFORM SIGN SAG TIMES A;Lo;0;L;;;;;N;;;;;
+12297;CUNEIFORM SIGN SAG TIMES DU;Lo;0;L;;;;;N;;;;;
+12298;CUNEIFORM SIGN SAG TIMES DUB;Lo;0;L;;;;;N;;;;;
+12299;CUNEIFORM SIGN SAG TIMES HA;Lo;0;L;;;;;N;;;;;
+1229A;CUNEIFORM SIGN SAG TIMES KAK;Lo;0;L;;;;;N;;;;;
+1229B;CUNEIFORM SIGN SAG TIMES KUR;Lo;0;L;;;;;N;;;;;
+1229C;CUNEIFORM SIGN SAG TIMES LUM;Lo;0;L;;;;;N;;;;;
+1229D;CUNEIFORM SIGN SAG TIMES MI;Lo;0;L;;;;;N;;;;;
+1229E;CUNEIFORM SIGN SAG TIMES NUN;Lo;0;L;;;;;N;;;;;
+1229F;CUNEIFORM SIGN SAG TIMES SAL;Lo;0;L;;;;;N;;;;;
+122A0;CUNEIFORM SIGN SAG TIMES SHID;Lo;0;L;;;;;N;;;;;
+122A1;CUNEIFORM SIGN SAG TIMES TAB;Lo;0;L;;;;;N;;;;;
+122A2;CUNEIFORM SIGN SAG TIMES U2;Lo;0;L;;;;;N;;;;;
+122A3;CUNEIFORM SIGN SAG TIMES UB;Lo;0;L;;;;;N;;;;;
+122A4;CUNEIFORM SIGN SAG TIMES UM;Lo;0;L;;;;;N;;;;;
+122A5;CUNEIFORM SIGN SAG TIMES UR;Lo;0;L;;;;;N;;;;;
+122A6;CUNEIFORM SIGN SAG TIMES USH;Lo;0;L;;;;;N;;;;;
+122A7;CUNEIFORM SIGN SAG OVER SAG;Lo;0;L;;;;;N;;;;;
+122A8;CUNEIFORM SIGN SAG GUNU;Lo;0;L;;;;;N;;;;;
+122A9;CUNEIFORM SIGN SAL;Lo;0;L;;;;;N;;;;;
+122AA;CUNEIFORM SIGN SAL LAGAB TIMES ASH2;Lo;0;L;;;;;N;;;;;
+122AB;CUNEIFORM SIGN SANGA2;Lo;0;L;;;;;N;;;;;
+122AC;CUNEIFORM SIGN SAR;Lo;0;L;;;;;N;;;;;
+122AD;CUNEIFORM SIGN SHA;Lo;0;L;;;;;N;;;;;
+122AE;CUNEIFORM SIGN SHA3;Lo;0;L;;;;;N;;;;;
+122AF;CUNEIFORM SIGN SHA3 TIMES A;Lo;0;L;;;;;N;;;;;
+122B0;CUNEIFORM SIGN SHA3 TIMES BAD;Lo;0;L;;;;;N;;;;;
+122B1;CUNEIFORM SIGN SHA3 TIMES GISH;Lo;0;L;;;;;N;;;;;
+122B2;CUNEIFORM SIGN SHA3 TIMES NE;Lo;0;L;;;;;N;;;;;
+122B3;CUNEIFORM SIGN SHA3 TIMES SHU2;Lo;0;L;;;;;N;;;;;
+122B4;CUNEIFORM SIGN SHA3 TIMES TUR;Lo;0;L;;;;;N;;;;;
+122B5;CUNEIFORM SIGN SHA3 TIMES U;Lo;0;L;;;;;N;;;;;
+122B6;CUNEIFORM SIGN SHA3 TIMES U PLUS A;Lo;0;L;;;;;N;;;;;
+122B7;CUNEIFORM SIGN SHA6;Lo;0;L;;;;;N;;;;;
+122B8;CUNEIFORM SIGN SHAB6;Lo;0;L;;;;;N;;;;;
+122B9;CUNEIFORM SIGN SHAR2;Lo;0;L;;;;;N;;;;;
+122BA;CUNEIFORM SIGN SHE;Lo;0;L;;;;;N;;;;;
+122BB;CUNEIFORM SIGN SHE HU;Lo;0;L;;;;;N;;;;;
+122BC;CUNEIFORM SIGN SHE OVER SHE GAD OVER GAD GAR OVER GAR;Lo;0;L;;;;;N;;;;;
+122BD;CUNEIFORM SIGN SHE OVER SHE TAB OVER TAB GAR OVER GAR;Lo;0;L;;;;;N;;;;;
+122BE;CUNEIFORM SIGN SHEG9;Lo;0;L;;;;;N;;;;;
+122BF;CUNEIFORM SIGN SHEN;Lo;0;L;;;;;N;;;;;
+122C0;CUNEIFORM SIGN SHESH;Lo;0;L;;;;;N;;;;;
+122C1;CUNEIFORM SIGN SHESH2;Lo;0;L;;;;;N;;;;;
+122C2;CUNEIFORM SIGN SHESHLAM;Lo;0;L;;;;;N;;;;;
+122C3;CUNEIFORM SIGN SHID;Lo;0;L;;;;;N;;;;;
+122C4;CUNEIFORM SIGN SHID TIMES A;Lo;0;L;;;;;N;;;;;
+122C5;CUNEIFORM SIGN SHID TIMES IM;Lo;0;L;;;;;N;;;;;
+122C6;CUNEIFORM SIGN SHIM;Lo;0;L;;;;;N;;;;;
+122C7;CUNEIFORM SIGN SHIM TIMES A;Lo;0;L;;;;;N;;;;;
+122C8;CUNEIFORM SIGN SHIM TIMES BAL;Lo;0;L;;;;;N;;;;;
+122C9;CUNEIFORM SIGN SHIM TIMES BULUG;Lo;0;L;;;;;N;;;;;
+122CA;CUNEIFORM SIGN SHIM TIMES DIN;Lo;0;L;;;;;N;;;;;
+122CB;CUNEIFORM SIGN SHIM TIMES GAR;Lo;0;L;;;;;N;;;;;
+122CC;CUNEIFORM SIGN SHIM TIMES IGI;Lo;0;L;;;;;N;;;;;
+122CD;CUNEIFORM SIGN SHIM TIMES IGI GUNU;Lo;0;L;;;;;N;;;;;
+122CE;CUNEIFORM SIGN SHIM TIMES KUSHU2;Lo;0;L;;;;;N;;;;;
+122CF;CUNEIFORM SIGN SHIM TIMES LUL;Lo;0;L;;;;;N;;;;;
+122D0;CUNEIFORM SIGN SHIM TIMES MUG;Lo;0;L;;;;;N;;;;;
+122D1;CUNEIFORM SIGN SHIM TIMES SAL;Lo;0;L;;;;;N;;;;;
+122D2;CUNEIFORM SIGN SHINIG;Lo;0;L;;;;;N;;;;;
+122D3;CUNEIFORM SIGN SHIR;Lo;0;L;;;;;N;;;;;
+122D4;CUNEIFORM SIGN SHIR TENU;Lo;0;L;;;;;N;;;;;
+122D5;CUNEIFORM SIGN SHIR OVER SHIR BUR OVER BUR;Lo;0;L;;;;;N;;;;;
+122D6;CUNEIFORM SIGN SHITA;Lo;0;L;;;;;N;;;;;
+122D7;CUNEIFORM SIGN SHU;Lo;0;L;;;;;N;;;;;
+122D8;CUNEIFORM SIGN SHU OVER INVERTED SHU;Lo;0;L;;;;;N;;;;;
+122D9;CUNEIFORM SIGN SHU2;Lo;0;L;;;;;N;;;;;
+122DA;CUNEIFORM SIGN SHUBUR;Lo;0;L;;;;;N;;;;;
+122DB;CUNEIFORM SIGN SI;Lo;0;L;;;;;N;;;;;
+122DC;CUNEIFORM SIGN SI GUNU;Lo;0;L;;;;;N;;;;;
+122DD;CUNEIFORM SIGN SIG;Lo;0;L;;;;;N;;;;;
+122DE;CUNEIFORM SIGN SIG4;Lo;0;L;;;;;N;;;;;
+122DF;CUNEIFORM SIGN SIG4 OVER SIG4 SHU2;Lo;0;L;;;;;N;;;;;
+122E0;CUNEIFORM SIGN SIK2;Lo;0;L;;;;;N;;;;;
+122E1;CUNEIFORM SIGN SILA3;Lo;0;L;;;;;N;;;;;
+122E2;CUNEIFORM SIGN SU;Lo;0;L;;;;;N;;;;;
+122E3;CUNEIFORM SIGN SU OVER SU;Lo;0;L;;;;;N;;;;;
+122E4;CUNEIFORM SIGN SUD;Lo;0;L;;;;;N;;;;;
+122E5;CUNEIFORM SIGN SUD2;Lo;0;L;;;;;N;;;;;
+122E6;CUNEIFORM SIGN SUHUR;Lo;0;L;;;;;N;;;;;
+122E7;CUNEIFORM SIGN SUM;Lo;0;L;;;;;N;;;;;
+122E8;CUNEIFORM SIGN SUMASH;Lo;0;L;;;;;N;;;;;
+122E9;CUNEIFORM SIGN SUR;Lo;0;L;;;;;N;;;;;
+122EA;CUNEIFORM SIGN SUR9;Lo;0;L;;;;;N;;;;;
+122EB;CUNEIFORM SIGN TA;Lo;0;L;;;;;N;;;;;
+122EC;CUNEIFORM SIGN TA ASTERISK;Lo;0;L;;;;;N;;;;;
+122ED;CUNEIFORM SIGN TA TIMES HI;Lo;0;L;;;;;N;;;;;
+122EE;CUNEIFORM SIGN TA TIMES MI;Lo;0;L;;;;;N;;;;;
+122EF;CUNEIFORM SIGN TA GUNU;Lo;0;L;;;;;N;;;;;
+122F0;CUNEIFORM SIGN TAB;Lo;0;L;;;;;N;;;;;
+122F1;CUNEIFORM SIGN TAB OVER TAB NI OVER NI DISH OVER DISH;Lo;0;L;;;;;N;;;;;
+122F2;CUNEIFORM SIGN TAB SQUARED;Lo;0;L;;;;;N;;;;;
+122F3;CUNEIFORM SIGN TAG;Lo;0;L;;;;;N;;;;;
+122F4;CUNEIFORM SIGN TAG TIMES BI;Lo;0;L;;;;;N;;;;;
+122F5;CUNEIFORM SIGN TAG TIMES GUD;Lo;0;L;;;;;N;;;;;
+122F6;CUNEIFORM SIGN TAG TIMES SHE;Lo;0;L;;;;;N;;;;;
+122F7;CUNEIFORM SIGN TAG TIMES SHU;Lo;0;L;;;;;N;;;;;
+122F8;CUNEIFORM SIGN TAG TIMES TUG2;Lo;0;L;;;;;N;;;;;
+122F9;CUNEIFORM SIGN TAG TIMES UD;Lo;0;L;;;;;N;;;;;
+122FA;CUNEIFORM SIGN TAK4;Lo;0;L;;;;;N;;;;;
+122FB;CUNEIFORM SIGN TAR;Lo;0;L;;;;;N;;;;;
+122FC;CUNEIFORM SIGN TE;Lo;0;L;;;;;N;;;;;
+122FD;CUNEIFORM SIGN TE GUNU;Lo;0;L;;;;;N;;;;;
+122FE;CUNEIFORM SIGN TI;Lo;0;L;;;;;N;;;;;
+122FF;CUNEIFORM SIGN TI TENU;Lo;0;L;;;;;N;;;;;
+12300;CUNEIFORM SIGN TIL;Lo;0;L;;;;;N;;;;;
+12301;CUNEIFORM SIGN TIR;Lo;0;L;;;;;N;;;;;
+12302;CUNEIFORM SIGN TIR TIMES TAK4;Lo;0;L;;;;;N;;;;;
+12303;CUNEIFORM SIGN TIR OVER TIR;Lo;0;L;;;;;N;;;;;
+12304;CUNEIFORM SIGN TIR OVER TIR GAD OVER GAD GAR OVER GAR;Lo;0;L;;;;;N;;;;;
+12305;CUNEIFORM SIGN TU;Lo;0;L;;;;;N;;;;;
+12306;CUNEIFORM SIGN TUG2;Lo;0;L;;;;;N;;;;;
+12307;CUNEIFORM SIGN TUK;Lo;0;L;;;;;N;;;;;
+12308;CUNEIFORM SIGN TUM;Lo;0;L;;;;;N;;;;;
+12309;CUNEIFORM SIGN TUR;Lo;0;L;;;;;N;;;;;
+1230A;CUNEIFORM SIGN TUR OVER TUR ZA OVER ZA;Lo;0;L;;;;;N;;;;;
+1230B;CUNEIFORM SIGN U;Lo;0;L;;;;;N;;;;;
+1230C;CUNEIFORM SIGN U GUD;Lo;0;L;;;;;N;;;;;
+1230D;CUNEIFORM SIGN U U U;Lo;0;L;;;;;N;;;;;
+1230E;CUNEIFORM SIGN U OVER U PA OVER PA GAR OVER GAR;Lo;0;L;;;;;N;;;;;
+1230F;CUNEIFORM SIGN U OVER U SUR OVER SUR;Lo;0;L;;;;;N;;;;;
+12310;CUNEIFORM SIGN U OVER U U REVERSED OVER U REVERSED;Lo;0;L;;;;;N;;;;;
+12311;CUNEIFORM SIGN U2;Lo;0;L;;;;;N;;;;;
+12312;CUNEIFORM SIGN UB;Lo;0;L;;;;;N;;;;;
+12313;CUNEIFORM SIGN UD;Lo;0;L;;;;;N;;;;;
+12314;CUNEIFORM SIGN UD KUSHU2;Lo;0;L;;;;;N;;;;;
+12315;CUNEIFORM SIGN UD TIMES BAD;Lo;0;L;;;;;N;;;;;
+12316;CUNEIFORM SIGN UD TIMES MI;Lo;0;L;;;;;N;;;;;
+12317;CUNEIFORM SIGN UD TIMES U PLUS U PLUS U;Lo;0;L;;;;;N;;;;;
+12318;CUNEIFORM SIGN UD TIMES U PLUS U PLUS U GUNU;Lo;0;L;;;;;N;;;;;
+12319;CUNEIFORM SIGN UD GUNU;Lo;0;L;;;;;N;;;;;
+1231A;CUNEIFORM SIGN UD SHESHIG;Lo;0;L;;;;;N;;;;;
+1231B;CUNEIFORM SIGN UD SHESHIG TIMES BAD;Lo;0;L;;;;;N;;;;;
+1231C;CUNEIFORM SIGN UDUG;Lo;0;L;;;;;N;;;;;
+1231D;CUNEIFORM SIGN UM;Lo;0;L;;;;;N;;;;;
+1231E;CUNEIFORM SIGN UM TIMES LAGAB;Lo;0;L;;;;;N;;;;;
+1231F;CUNEIFORM SIGN UM TIMES ME PLUS DA;Lo;0;L;;;;;N;;;;;
+12320;CUNEIFORM SIGN UM TIMES SHA3;Lo;0;L;;;;;N;;;;;
+12321;CUNEIFORM SIGN UM TIMES U;Lo;0;L;;;;;N;;;;;
+12322;CUNEIFORM SIGN UMBIN;Lo;0;L;;;;;N;;;;;
+12323;CUNEIFORM SIGN UMUM;Lo;0;L;;;;;N;;;;;
+12324;CUNEIFORM SIGN UMUM TIMES KASKAL;Lo;0;L;;;;;N;;;;;
+12325;CUNEIFORM SIGN UMUM TIMES PA;Lo;0;L;;;;;N;;;;;
+12326;CUNEIFORM SIGN UN;Lo;0;L;;;;;N;;;;;
+12327;CUNEIFORM SIGN UN GUNU;Lo;0;L;;;;;N;;;;;
+12328;CUNEIFORM SIGN UR;Lo;0;L;;;;;N;;;;;
+12329;CUNEIFORM SIGN UR CROSSING UR;Lo;0;L;;;;;N;;;;;
+1232A;CUNEIFORM SIGN UR SHESHIG;Lo;0;L;;;;;N;;;;;
+1232B;CUNEIFORM SIGN UR2;Lo;0;L;;;;;N;;;;;
+1232C;CUNEIFORM SIGN UR2 TIMES A PLUS HA;Lo;0;L;;;;;N;;;;;
+1232D;CUNEIFORM SIGN UR2 TIMES A PLUS NA;Lo;0;L;;;;;N;;;;;
+1232E;CUNEIFORM SIGN UR2 TIMES AL;Lo;0;L;;;;;N;;;;;
+1232F;CUNEIFORM SIGN UR2 TIMES HA;Lo;0;L;;;;;N;;;;;
+12330;CUNEIFORM SIGN UR2 TIMES NUN;Lo;0;L;;;;;N;;;;;
+12331;CUNEIFORM SIGN UR2 TIMES U2;Lo;0;L;;;;;N;;;;;
+12332;CUNEIFORM SIGN UR2 TIMES U2 PLUS ASH;Lo;0;L;;;;;N;;;;;
+12333;CUNEIFORM SIGN UR2 TIMES U2 PLUS BI;Lo;0;L;;;;;N;;;;;
+12334;CUNEIFORM SIGN UR4;Lo;0;L;;;;;N;;;;;
+12335;CUNEIFORM SIGN URI;Lo;0;L;;;;;N;;;;;
+12336;CUNEIFORM SIGN URI3;Lo;0;L;;;;;N;;;;;
+12337;CUNEIFORM SIGN URU;Lo;0;L;;;;;N;;;;;
+12338;CUNEIFORM SIGN URU TIMES A;Lo;0;L;;;;;N;;;;;
+12339;CUNEIFORM SIGN URU TIMES ASHGAB;Lo;0;L;;;;;N;;;;;
+1233A;CUNEIFORM SIGN URU TIMES BAR;Lo;0;L;;;;;N;;;;;
+1233B;CUNEIFORM SIGN URU TIMES DUN;Lo;0;L;;;;;N;;;;;
+1233C;CUNEIFORM SIGN URU TIMES GA;Lo;0;L;;;;;N;;;;;
+1233D;CUNEIFORM SIGN URU TIMES GAL;Lo;0;L;;;;;N;;;;;
+1233E;CUNEIFORM SIGN URU TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;;
+1233F;CUNEIFORM SIGN URU TIMES GAR;Lo;0;L;;;;;N;;;;;
+12340;CUNEIFORM SIGN URU TIMES GU;Lo;0;L;;;;;N;;;;;
+12341;CUNEIFORM SIGN URU TIMES HA;Lo;0;L;;;;;N;;;;;
+12342;CUNEIFORM SIGN URU TIMES IGI;Lo;0;L;;;;;N;;;;;
+12343;CUNEIFORM SIGN URU TIMES IM;Lo;0;L;;;;;N;;;;;
+12344;CUNEIFORM SIGN URU TIMES ISH;Lo;0;L;;;;;N;;;;;
+12345;CUNEIFORM SIGN URU TIMES KI;Lo;0;L;;;;;N;;;;;
+12346;CUNEIFORM SIGN URU TIMES LUM;Lo;0;L;;;;;N;;;;;
+12347;CUNEIFORM SIGN URU TIMES MIN;Lo;0;L;;;;;N;;;;;
+12348;CUNEIFORM SIGN URU TIMES PA;Lo;0;L;;;;;N;;;;;
+12349;CUNEIFORM SIGN URU TIMES SHE;Lo;0;L;;;;;N;;;;;
+1234A;CUNEIFORM SIGN URU TIMES SIG4;Lo;0;L;;;;;N;;;;;
+1234B;CUNEIFORM SIGN URU TIMES TU;Lo;0;L;;;;;N;;;;;
+1234C;CUNEIFORM SIGN URU TIMES U PLUS GUD;Lo;0;L;;;;;N;;;;;
+1234D;CUNEIFORM SIGN URU TIMES UD;Lo;0;L;;;;;N;;;;;
+1234E;CUNEIFORM SIGN URU TIMES URUDA;Lo;0;L;;;;;N;;;;;
+1234F;CUNEIFORM SIGN URUDA;Lo;0;L;;;;;N;;;;;
+12350;CUNEIFORM SIGN URUDA TIMES U;Lo;0;L;;;;;N;;;;;
+12351;CUNEIFORM SIGN USH;Lo;0;L;;;;;N;;;;;
+12352;CUNEIFORM SIGN USH TIMES A;Lo;0;L;;;;;N;;;;;
+12353;CUNEIFORM SIGN USH TIMES KU;Lo;0;L;;;;;N;;;;;
+12354;CUNEIFORM SIGN USH TIMES KUR;Lo;0;L;;;;;N;;;;;
+12355;CUNEIFORM SIGN USH TIMES TAK4;Lo;0;L;;;;;N;;;;;
+12356;CUNEIFORM SIGN USHX;Lo;0;L;;;;;N;;;;;
+12357;CUNEIFORM SIGN USH2;Lo;0;L;;;;;N;;;;;
+12358;CUNEIFORM SIGN USHUMX;Lo;0;L;;;;;N;;;;;
+12359;CUNEIFORM SIGN UTUKI;Lo;0;L;;;;;N;;;;;
+1235A;CUNEIFORM SIGN UZ3;Lo;0;L;;;;;N;;;;;
+1235B;CUNEIFORM SIGN UZ3 TIMES KASKAL;Lo;0;L;;;;;N;;;;;
+1235C;CUNEIFORM SIGN UZU;Lo;0;L;;;;;N;;;;;
+1235D;CUNEIFORM SIGN ZA;Lo;0;L;;;;;N;;;;;
+1235E;CUNEIFORM SIGN ZA TENU;Lo;0;L;;;;;N;;;;;
+1235F;CUNEIFORM SIGN ZA SQUARED TIMES KUR;Lo;0;L;;;;;N;;;;;
+12360;CUNEIFORM SIGN ZAG;Lo;0;L;;;;;N;;;;;
+12361;CUNEIFORM SIGN ZAMX;Lo;0;L;;;;;N;;;;;
+12362;CUNEIFORM SIGN ZE2;Lo;0;L;;;;;N;;;;;
+12363;CUNEIFORM SIGN ZI;Lo;0;L;;;;;N;;;;;
+12364;CUNEIFORM SIGN ZI OVER ZI;Lo;0;L;;;;;N;;;;;
+12365;CUNEIFORM SIGN ZI3;Lo;0;L;;;;;N;;;;;
+12366;CUNEIFORM SIGN ZIB;Lo;0;L;;;;;N;;;;;
+12367;CUNEIFORM SIGN ZIB KABA TENU;Lo;0;L;;;;;N;;;;;
+12368;CUNEIFORM SIGN ZIG;Lo;0;L;;;;;N;;;;;
+12369;CUNEIFORM SIGN ZIZ2;Lo;0;L;;;;;N;;;;;
+1236A;CUNEIFORM SIGN ZU;Lo;0;L;;;;;N;;;;;
+1236B;CUNEIFORM SIGN ZU5;Lo;0;L;;;;;N;;;;;
+1236C;CUNEIFORM SIGN ZU5 TIMES A;Lo;0;L;;;;;N;;;;;
+1236D;CUNEIFORM SIGN ZUBUR;Lo;0;L;;;;;N;;;;;
+1236E;CUNEIFORM SIGN ZUM;Lo;0;L;;;;;N;;;;;
+1236F;CUNEIFORM SIGN KAP ELAMITE;Lo;0;L;;;;;N;;;;;
+12370;CUNEIFORM SIGN AB TIMES NUN;Lo;0;L;;;;;N;;;;;
+12371;CUNEIFORM SIGN AB2 TIMES A;Lo;0;L;;;;;N;;;;;
+12372;CUNEIFORM SIGN AMAR TIMES KUG;Lo;0;L;;;;;N;;;;;
+12373;CUNEIFORM SIGN DAG KISIM5 TIMES U2 PLUS MASH;Lo;0;L;;;;;N;;;;;
+12374;CUNEIFORM SIGN DAG3;Lo;0;L;;;;;N;;;;;
+12375;CUNEIFORM SIGN DISH PLUS SHU;Lo;0;L;;;;;N;;;;;
+12376;CUNEIFORM SIGN DUB TIMES SHE;Lo;0;L;;;;;N;;;;;
+12377;CUNEIFORM SIGN EZEN TIMES GUD;Lo;0;L;;;;;N;;;;;
+12378;CUNEIFORM SIGN EZEN TIMES SHE;Lo;0;L;;;;;N;;;;;
+12379;CUNEIFORM SIGN GA2 TIMES AN PLUS KAK PLUS A;Lo;0;L;;;;;N;;;;;
+1237A;CUNEIFORM SIGN GA2 TIMES ASH2;Lo;0;L;;;;;N;;;;;
+1237B;CUNEIFORM SIGN GE22;Lo;0;L;;;;;N;;;;;
+1237C;CUNEIFORM SIGN GIG;Lo;0;L;;;;;N;;;;;
+1237D;CUNEIFORM SIGN HUSH;Lo;0;L;;;;;N;;;;;
+1237E;CUNEIFORM SIGN KA TIMES ANSHE;Lo;0;L;;;;;N;;;;;
+1237F;CUNEIFORM SIGN KA TIMES ASH3;Lo;0;L;;;;;N;;;;;
+12380;CUNEIFORM SIGN KA TIMES GISH;Lo;0;L;;;;;N;;;;;
+12381;CUNEIFORM SIGN KA TIMES GUD;Lo;0;L;;;;;N;;;;;
+12382;CUNEIFORM SIGN KA TIMES HI TIMES ASH2;Lo;0;L;;;;;N;;;;;
+12383;CUNEIFORM SIGN KA TIMES LUM;Lo;0;L;;;;;N;;;;;
+12384;CUNEIFORM SIGN KA TIMES PA;Lo;0;L;;;;;N;;;;;
+12385;CUNEIFORM SIGN KA TIMES SHUL;Lo;0;L;;;;;N;;;;;
+12386;CUNEIFORM SIGN KA TIMES TU;Lo;0;L;;;;;N;;;;;
+12387;CUNEIFORM SIGN KA TIMES UR2;Lo;0;L;;;;;N;;;;;
+12388;CUNEIFORM SIGN LAGAB TIMES GI;Lo;0;L;;;;;N;;;;;
+12389;CUNEIFORM SIGN LU2 SHESHIG TIMES BAD;Lo;0;L;;;;;N;;;;;
+1238A;CUNEIFORM SIGN LU2 TIMES ESH2 PLUS LAL;Lo;0;L;;;;;N;;;;;
+1238B;CUNEIFORM SIGN LU2 TIMES SHU;Lo;0;L;;;;;N;;;;;
+1238C;CUNEIFORM SIGN MESH;Lo;0;L;;;;;N;;;;;
+1238D;CUNEIFORM SIGN MUSH3 TIMES ZA;Lo;0;L;;;;;N;;;;;
+1238E;CUNEIFORM SIGN NA4;Lo;0;L;;;;;N;;;;;
+1238F;CUNEIFORM SIGN NIN;Lo;0;L;;;;;N;;;;;
+12390;CUNEIFORM SIGN NIN9;Lo;0;L;;;;;N;;;;;
+12391;CUNEIFORM SIGN NINDA2 TIMES BAL;Lo;0;L;;;;;N;;;;;
+12392;CUNEIFORM SIGN NINDA2 TIMES GI;Lo;0;L;;;;;N;;;;;
+12393;CUNEIFORM SIGN NU11 ROTATED NINETY DEGREES;Lo;0;L;;;;;N;;;;;
+12394;CUNEIFORM SIGN PESH2 ASTERISK;Lo;0;L;;;;;N;;;;;
+12395;CUNEIFORM SIGN PIR2;Lo;0;L;;;;;N;;;;;
+12396;CUNEIFORM SIGN SAG TIMES IGI GUNU;Lo;0;L;;;;;N;;;;;
+12397;CUNEIFORM SIGN TI2;Lo;0;L;;;;;N;;;;;
+12398;CUNEIFORM SIGN UM TIMES ME;Lo;0;L;;;;;N;;;;;
+12399;CUNEIFORM SIGN U U;Lo;0;L;;;;;N;;;;;
+12400;CUNEIFORM NUMERIC SIGN TWO ASH;Nl;0;L;;;;2;N;;;;;
+12401;CUNEIFORM NUMERIC SIGN THREE ASH;Nl;0;L;;;;3;N;;;;;
+12402;CUNEIFORM NUMERIC SIGN FOUR ASH;Nl;0;L;;;;4;N;;;;;
+12403;CUNEIFORM NUMERIC SIGN FIVE ASH;Nl;0;L;;;;5;N;;;;;
+12404;CUNEIFORM NUMERIC SIGN SIX ASH;Nl;0;L;;;;6;N;;;;;
+12405;CUNEIFORM NUMERIC SIGN SEVEN ASH;Nl;0;L;;;;7;N;;;;;
+12406;CUNEIFORM NUMERIC SIGN EIGHT ASH;Nl;0;L;;;;8;N;;;;;
+12407;CUNEIFORM NUMERIC SIGN NINE ASH;Nl;0;L;;;;9;N;;;;;
+12408;CUNEIFORM NUMERIC SIGN THREE DISH;Nl;0;L;;;;3;N;;;;;
+12409;CUNEIFORM NUMERIC SIGN FOUR DISH;Nl;0;L;;;;4;N;;;;;
+1240A;CUNEIFORM NUMERIC SIGN FIVE DISH;Nl;0;L;;;;5;N;;;;;
+1240B;CUNEIFORM NUMERIC SIGN SIX DISH;Nl;0;L;;;;6;N;;;;;
+1240C;CUNEIFORM NUMERIC SIGN SEVEN DISH;Nl;0;L;;;;7;N;;;;;
+1240D;CUNEIFORM NUMERIC SIGN EIGHT DISH;Nl;0;L;;;;8;N;;;;;
+1240E;CUNEIFORM NUMERIC SIGN NINE DISH;Nl;0;L;;;;9;N;;;;;
+1240F;CUNEIFORM NUMERIC SIGN FOUR U;Nl;0;L;;;;4;N;;;;;
+12410;CUNEIFORM NUMERIC SIGN FIVE U;Nl;0;L;;;;5;N;;;;;
+12411;CUNEIFORM NUMERIC SIGN SIX U;Nl;0;L;;;;6;N;;;;;
+12412;CUNEIFORM NUMERIC SIGN SEVEN U;Nl;0;L;;;;7;N;;;;;
+12413;CUNEIFORM NUMERIC SIGN EIGHT U;Nl;0;L;;;;8;N;;;;;
+12414;CUNEIFORM NUMERIC SIGN NINE U;Nl;0;L;;;;9;N;;;;;
+12415;CUNEIFORM NUMERIC SIGN ONE GESH2;Nl;0;L;;;;1;N;;;;;
+12416;CUNEIFORM NUMERIC SIGN TWO GESH2;Nl;0;L;;;;2;N;;;;;
+12417;CUNEIFORM NUMERIC SIGN THREE GESH2;Nl;0;L;;;;3;N;;;;;
+12418;CUNEIFORM NUMERIC SIGN FOUR GESH2;Nl;0;L;;;;4;N;;;;;
+12419;CUNEIFORM NUMERIC SIGN FIVE GESH2;Nl;0;L;;;;5;N;;;;;
+1241A;CUNEIFORM NUMERIC SIGN SIX GESH2;Nl;0;L;;;;6;N;;;;;
+1241B;CUNEIFORM NUMERIC SIGN SEVEN GESH2;Nl;0;L;;;;7;N;;;;;
+1241C;CUNEIFORM NUMERIC SIGN EIGHT GESH2;Nl;0;L;;;;8;N;;;;;
+1241D;CUNEIFORM NUMERIC SIGN NINE GESH2;Nl;0;L;;;;9;N;;;;;
+1241E;CUNEIFORM NUMERIC SIGN ONE GESHU;Nl;0;L;;;;1;N;;;;;
+1241F;CUNEIFORM NUMERIC SIGN TWO GESHU;Nl;0;L;;;;2;N;;;;;
+12420;CUNEIFORM NUMERIC SIGN THREE GESHU;Nl;0;L;;;;3;N;;;;;
+12421;CUNEIFORM NUMERIC SIGN FOUR GESHU;Nl;0;L;;;;4;N;;;;;
+12422;CUNEIFORM NUMERIC SIGN FIVE GESHU;Nl;0;L;;;;5;N;;;;;
+12423;CUNEIFORM NUMERIC SIGN TWO SHAR2;Nl;0;L;;;;2;N;;;;;
+12424;CUNEIFORM NUMERIC SIGN THREE SHAR2;Nl;0;L;;;;3;N;;;;;
+12425;CUNEIFORM NUMERIC SIGN THREE SHAR2 VARIANT FORM;Nl;0;L;;;;3;N;;;;;
+12426;CUNEIFORM NUMERIC SIGN FOUR SHAR2;Nl;0;L;;;;4;N;;;;;
+12427;CUNEIFORM NUMERIC SIGN FIVE SHAR2;Nl;0;L;;;;5;N;;;;;
+12428;CUNEIFORM NUMERIC SIGN SIX SHAR2;Nl;0;L;;;;6;N;;;;;
+12429;CUNEIFORM NUMERIC SIGN SEVEN SHAR2;Nl;0;L;;;;7;N;;;;;
+1242A;CUNEIFORM NUMERIC SIGN EIGHT SHAR2;Nl;0;L;;;;8;N;;;;;
+1242B;CUNEIFORM NUMERIC SIGN NINE SHAR2;Nl;0;L;;;;9;N;;;;;
+1242C;CUNEIFORM NUMERIC SIGN ONE SHARU;Nl;0;L;;;;1;N;;;;;
+1242D;CUNEIFORM NUMERIC SIGN TWO SHARU;Nl;0;L;;;;2;N;;;;;
+1242E;CUNEIFORM NUMERIC SIGN THREE SHARU;Nl;0;L;;;;3;N;;;;;
+1242F;CUNEIFORM NUMERIC SIGN THREE SHARU VARIANT FORM;Nl;0;L;;;;3;N;;;;;
+12430;CUNEIFORM NUMERIC SIGN FOUR SHARU;Nl;0;L;;;;4;N;;;;;
+12431;CUNEIFORM NUMERIC SIGN FIVE SHARU;Nl;0;L;;;;5;N;;;;;
+12432;CUNEIFORM NUMERIC SIGN SHAR2 TIMES GAL PLUS DISH;Nl;0;L;;;;216000;N;;;;;
+12433;CUNEIFORM NUMERIC SIGN SHAR2 TIMES GAL PLUS MIN;Nl;0;L;;;;432000;N;;;;;
+12434;CUNEIFORM NUMERIC SIGN ONE BURU;Nl;0;L;;;;1;N;;;;;
+12435;CUNEIFORM NUMERIC SIGN TWO BURU;Nl;0;L;;;;2;N;;;;;
+12436;CUNEIFORM NUMERIC SIGN THREE BURU;Nl;0;L;;;;3;N;;;;;
+12437;CUNEIFORM NUMERIC SIGN THREE BURU VARIANT FORM;Nl;0;L;;;;3;N;;;;;
+12438;CUNEIFORM NUMERIC SIGN FOUR BURU;Nl;0;L;;;;4;N;;;;;
+12439;CUNEIFORM NUMERIC SIGN FIVE BURU;Nl;0;L;;;;5;N;;;;;
+1243A;CUNEIFORM NUMERIC SIGN THREE VARIANT FORM ESH16;Nl;0;L;;;;3;N;;;;;
+1243B;CUNEIFORM NUMERIC SIGN THREE VARIANT FORM ESH21;Nl;0;L;;;;3;N;;;;;
+1243C;CUNEIFORM NUMERIC SIGN FOUR VARIANT FORM LIMMU;Nl;0;L;;;;4;N;;;;;
+1243D;CUNEIFORM NUMERIC SIGN FOUR VARIANT FORM LIMMU4;Nl;0;L;;;;4;N;;;;;
+1243E;CUNEIFORM NUMERIC SIGN FOUR VARIANT FORM LIMMU A;Nl;0;L;;;;4;N;;;;;
+1243F;CUNEIFORM NUMERIC SIGN FOUR VARIANT FORM LIMMU B;Nl;0;L;;;;4;N;;;;;
+12440;CUNEIFORM NUMERIC SIGN SIX VARIANT FORM ASH9;Nl;0;L;;;;6;N;;;;;
+12441;CUNEIFORM NUMERIC SIGN SEVEN VARIANT FORM IMIN3;Nl;0;L;;;;7;N;;;;;
+12442;CUNEIFORM NUMERIC SIGN SEVEN VARIANT FORM IMIN A;Nl;0;L;;;;7;N;;;;;
+12443;CUNEIFORM NUMERIC SIGN SEVEN VARIANT FORM IMIN B;Nl;0;L;;;;7;N;;;;;
+12444;CUNEIFORM NUMERIC SIGN EIGHT VARIANT FORM USSU;Nl;0;L;;;;8;N;;;;;
+12445;CUNEIFORM NUMERIC SIGN EIGHT VARIANT FORM USSU3;Nl;0;L;;;;8;N;;;;;
+12446;CUNEIFORM NUMERIC SIGN NINE VARIANT FORM ILIMMU;Nl;0;L;;;;9;N;;;;;
+12447;CUNEIFORM NUMERIC SIGN NINE VARIANT FORM ILIMMU3;Nl;0;L;;;;9;N;;;;;
+12448;CUNEIFORM NUMERIC SIGN NINE VARIANT FORM ILIMMU4;Nl;0;L;;;;9;N;;;;;
+12449;CUNEIFORM NUMERIC SIGN NINE VARIANT FORM ILIMMU A;Nl;0;L;;;;9;N;;;;;
+1244A;CUNEIFORM NUMERIC SIGN TWO ASH TENU;Nl;0;L;;;;2;N;;;;;
+1244B;CUNEIFORM NUMERIC SIGN THREE ASH TENU;Nl;0;L;;;;3;N;;;;;
+1244C;CUNEIFORM NUMERIC SIGN FOUR ASH TENU;Nl;0;L;;;;4;N;;;;;
+1244D;CUNEIFORM NUMERIC SIGN FIVE ASH TENU;Nl;0;L;;;;5;N;;;;;
+1244E;CUNEIFORM NUMERIC SIGN SIX ASH TENU;Nl;0;L;;;;6;N;;;;;
+1244F;CUNEIFORM NUMERIC SIGN ONE BAN2;Nl;0;L;;;;1;N;;;;;
+12450;CUNEIFORM NUMERIC SIGN TWO BAN2;Nl;0;L;;;;2;N;;;;;
+12451;CUNEIFORM NUMERIC SIGN THREE BAN2;Nl;0;L;;;;3;N;;;;;
+12452;CUNEIFORM NUMERIC SIGN FOUR BAN2;Nl;0;L;;;;4;N;;;;;
+12453;CUNEIFORM NUMERIC SIGN FOUR BAN2 VARIANT FORM;Nl;0;L;;;;4;N;;;;;
+12454;CUNEIFORM NUMERIC SIGN FIVE BAN2;Nl;0;L;;;;5;N;;;;;
+12455;CUNEIFORM NUMERIC SIGN FIVE BAN2 VARIANT FORM;Nl;0;L;;;;5;N;;;;;
+12456;CUNEIFORM NUMERIC SIGN NIGIDAMIN;Nl;0;L;;;;2;N;;;;;
+12457;CUNEIFORM NUMERIC SIGN NIGIDAESH;Nl;0;L;;;;3;N;;;;;
+12458;CUNEIFORM NUMERIC SIGN ONE ESHE3;Nl;0;L;;;;1;N;;;;;
+12459;CUNEIFORM NUMERIC SIGN TWO ESHE3;Nl;0;L;;;;2;N;;;;;
+1245A;CUNEIFORM NUMERIC SIGN ONE THIRD DISH;Nl;0;L;;;;1/3;N;;;;;
+1245B;CUNEIFORM NUMERIC SIGN TWO THIRDS DISH;Nl;0;L;;;;2/3;N;;;;;
+1245C;CUNEIFORM NUMERIC SIGN FIVE SIXTHS DISH;Nl;0;L;;;;5/6;N;;;;;
+1245D;CUNEIFORM NUMERIC SIGN ONE THIRD VARIANT FORM A;Nl;0;L;;;;1/3;N;;;;;
+1245E;CUNEIFORM NUMERIC SIGN TWO THIRDS VARIANT FORM A;Nl;0;L;;;;2/3;N;;;;;
+1245F;CUNEIFORM NUMERIC SIGN ONE EIGHTH ASH;Nl;0;L;;;;1/8;N;;;;;
+12460;CUNEIFORM NUMERIC SIGN ONE QUARTER ASH;Nl;0;L;;;;1/4;N;;;;;
+12461;CUNEIFORM NUMERIC SIGN OLD ASSYRIAN ONE SIXTH;Nl;0;L;;;;1/6;N;;;;;
+12462;CUNEIFORM NUMERIC SIGN OLD ASSYRIAN ONE QUARTER;Nl;0;L;;;;1/4;N;;;;;
+12463;CUNEIFORM NUMERIC SIGN ONE QUARTER GUR;Nl;0;L;;;;1/4;N;;;;;
+12464;CUNEIFORM NUMERIC SIGN ONE HALF GUR;Nl;0;L;;;;1/2;N;;;;;
+12465;CUNEIFORM NUMERIC SIGN ELAMITE ONE THIRD;Nl;0;L;;;;1/3;N;;;;;
+12466;CUNEIFORM NUMERIC SIGN ELAMITE TWO THIRDS;Nl;0;L;;;;2/3;N;;;;;
+12467;CUNEIFORM NUMERIC SIGN ELAMITE FORTY;Nl;0;L;;;;40;N;;;;;
+12468;CUNEIFORM NUMERIC SIGN ELAMITE FIFTY;Nl;0;L;;;;50;N;;;;;
+12469;CUNEIFORM NUMERIC SIGN FOUR U VARIANT FORM;Nl;0;L;;;;4;N;;;;;
+1246A;CUNEIFORM NUMERIC SIGN FIVE U VARIANT FORM;Nl;0;L;;;;5;N;;;;;
+1246B;CUNEIFORM NUMERIC SIGN SIX U VARIANT FORM;Nl;0;L;;;;6;N;;;;;
+1246C;CUNEIFORM NUMERIC SIGN SEVEN U VARIANT FORM;Nl;0;L;;;;7;N;;;;;
+1246D;CUNEIFORM NUMERIC SIGN EIGHT U VARIANT FORM;Nl;0;L;;;;8;N;;;;;
+1246E;CUNEIFORM NUMERIC SIGN NINE U VARIANT FORM;Nl;0;L;;;;9;N;;;;;
+12470;CUNEIFORM PUNCTUATION SIGN OLD ASSYRIAN WORD DIVIDER;Po;0;L;;;;;N;;;;;
+12471;CUNEIFORM PUNCTUATION SIGN VERTICAL COLON;Po;0;L;;;;;N;;;;;
+12472;CUNEIFORM PUNCTUATION SIGN DIAGONAL COLON;Po;0;L;;;;;N;;;;;
+12473;CUNEIFORM PUNCTUATION SIGN DIAGONAL TRICOLON;Po;0;L;;;;;N;;;;;
+12474;CUNEIFORM PUNCTUATION SIGN DIAGONAL QUADCOLON;Po;0;L;;;;;N;;;;;
+12480;CUNEIFORM SIGN AB TIMES NUN TENU;Lo;0;L;;;;;N;;;;;
+12481;CUNEIFORM SIGN AB TIMES SHU2;Lo;0;L;;;;;N;;;;;
+12482;CUNEIFORM SIGN AD TIMES ESH2;Lo;0;L;;;;;N;;;;;
+12483;CUNEIFORM SIGN BAD TIMES DISH TENU;Lo;0;L;;;;;N;;;;;
+12484;CUNEIFORM SIGN BAHAR2 TIMES AB2;Lo;0;L;;;;;N;;;;;
+12485;CUNEIFORM SIGN BAHAR2 TIMES NI;Lo;0;L;;;;;N;;;;;
+12486;CUNEIFORM SIGN BAHAR2 TIMES ZA;Lo;0;L;;;;;N;;;;;
+12487;CUNEIFORM SIGN BU OVER BU TIMES NA2;Lo;0;L;;;;;N;;;;;
+12488;CUNEIFORM SIGN DA TIMES TAK4;Lo;0;L;;;;;N;;;;;
+12489;CUNEIFORM SIGN DAG TIMES KUR;Lo;0;L;;;;;N;;;;;
+1248A;CUNEIFORM SIGN DIM TIMES IGI;Lo;0;L;;;;;N;;;;;
+1248B;CUNEIFORM SIGN DIM TIMES U U U;Lo;0;L;;;;;N;;;;;
+1248C;CUNEIFORM SIGN DIM2 TIMES UD;Lo;0;L;;;;;N;;;;;
+1248D;CUNEIFORM SIGN DUG TIMES ANSHE;Lo;0;L;;;;;N;;;;;
+1248E;CUNEIFORM SIGN DUG TIMES ASH;Lo;0;L;;;;;N;;;;;
+1248F;CUNEIFORM SIGN DUG TIMES ASH AT LEFT;Lo;0;L;;;;;N;;;;;
+12490;CUNEIFORM SIGN DUG TIMES DIN;Lo;0;L;;;;;N;;;;;
+12491;CUNEIFORM SIGN DUG TIMES DUN;Lo;0;L;;;;;N;;;;;
+12492;CUNEIFORM SIGN DUG TIMES ERIN2;Lo;0;L;;;;;N;;;;;
+12493;CUNEIFORM SIGN DUG TIMES GA;Lo;0;L;;;;;N;;;;;
+12494;CUNEIFORM SIGN DUG TIMES GI;Lo;0;L;;;;;N;;;;;
+12495;CUNEIFORM SIGN DUG TIMES GIR2 GUNU;Lo;0;L;;;;;N;;;;;
+12496;CUNEIFORM SIGN DUG TIMES GISH;Lo;0;L;;;;;N;;;;;
+12497;CUNEIFORM SIGN DUG TIMES HA;Lo;0;L;;;;;N;;;;;
+12498;CUNEIFORM SIGN DUG TIMES HI;Lo;0;L;;;;;N;;;;;
+12499;CUNEIFORM SIGN DUG TIMES IGI GUNU;Lo;0;L;;;;;N;;;;;
+1249A;CUNEIFORM SIGN DUG TIMES KASKAL;Lo;0;L;;;;;N;;;;;
+1249B;CUNEIFORM SIGN DUG TIMES KUR;Lo;0;L;;;;;N;;;;;
+1249C;CUNEIFORM SIGN DUG TIMES KUSHU2;Lo;0;L;;;;;N;;;;;
+1249D;CUNEIFORM SIGN DUG TIMES KUSHU2 PLUS KASKAL;Lo;0;L;;;;;N;;;;;
+1249E;CUNEIFORM SIGN DUG TIMES LAK-020;Lo;0;L;;;;;N;;;;;
+1249F;CUNEIFORM SIGN DUG TIMES LAM;Lo;0;L;;;;;N;;;;;
+124A0;CUNEIFORM SIGN DUG TIMES LAM TIMES KUR;Lo;0;L;;;;;N;;;;;
+124A1;CUNEIFORM SIGN DUG TIMES LUH PLUS GISH;Lo;0;L;;;;;N;;;;;
+124A2;CUNEIFORM SIGN DUG TIMES MASH;Lo;0;L;;;;;N;;;;;
+124A3;CUNEIFORM SIGN DUG TIMES MES;Lo;0;L;;;;;N;;;;;
+124A4;CUNEIFORM SIGN DUG TIMES MI;Lo;0;L;;;;;N;;;;;
+124A5;CUNEIFORM SIGN DUG TIMES NI;Lo;0;L;;;;;N;;;;;
+124A6;CUNEIFORM SIGN DUG TIMES PI;Lo;0;L;;;;;N;;;;;
+124A7;CUNEIFORM SIGN DUG TIMES SHE;Lo;0;L;;;;;N;;;;;
+124A8;CUNEIFORM SIGN DUG TIMES SI GUNU;Lo;0;L;;;;;N;;;;;
+124A9;CUNEIFORM SIGN E2 TIMES KUR;Lo;0;L;;;;;N;;;;;
+124AA;CUNEIFORM SIGN E2 TIMES PAP;Lo;0;L;;;;;N;;;;;
+124AB;CUNEIFORM SIGN ERIN2 X;Lo;0;L;;;;;N;;;;;
+124AC;CUNEIFORM SIGN ESH2 CROSSING ESH2;Lo;0;L;;;;;N;;;;;
+124AD;CUNEIFORM SIGN EZEN SHESHIG TIMES ASH;Lo;0;L;;;;;N;;;;;
+124AE;CUNEIFORM SIGN EZEN SHESHIG TIMES HI;Lo;0;L;;;;;N;;;;;
+124AF;CUNEIFORM SIGN EZEN SHESHIG TIMES IGI GUNU;Lo;0;L;;;;;N;;;;;
+124B0;CUNEIFORM SIGN EZEN SHESHIG TIMES LA;Lo;0;L;;;;;N;;;;;
+124B1;CUNEIFORM SIGN EZEN SHESHIG TIMES LAL;Lo;0;L;;;;;N;;;;;
+124B2;CUNEIFORM SIGN EZEN SHESHIG TIMES ME;Lo;0;L;;;;;N;;;;;
+124B3;CUNEIFORM SIGN EZEN SHESHIG TIMES MES;Lo;0;L;;;;;N;;;;;
+124B4;CUNEIFORM SIGN EZEN SHESHIG TIMES SU;Lo;0;L;;;;;N;;;;;
+124B5;CUNEIFORM SIGN EZEN TIMES SU;Lo;0;L;;;;;N;;;;;
+124B6;CUNEIFORM SIGN GA2 TIMES BAHAR2;Lo;0;L;;;;;N;;;;;
+124B7;CUNEIFORM SIGN GA2 TIMES DIM GUNU;Lo;0;L;;;;;N;;;;;
+124B8;CUNEIFORM SIGN GA2 TIMES DUG TIMES IGI GUNU;Lo;0;L;;;;;N;;;;;
+124B9;CUNEIFORM SIGN GA2 TIMES DUG TIMES KASKAL;Lo;0;L;;;;;N;;;;;
+124BA;CUNEIFORM SIGN GA2 TIMES EREN;Lo;0;L;;;;;N;;;;;
+124BB;CUNEIFORM SIGN GA2 TIMES GA;Lo;0;L;;;;;N;;;;;
+124BC;CUNEIFORM SIGN GA2 TIMES GAR PLUS DI;Lo;0;L;;;;;N;;;;;
+124BD;CUNEIFORM SIGN GA2 TIMES GAR PLUS NE;Lo;0;L;;;;;N;;;;;
+124BE;CUNEIFORM SIGN GA2 TIMES HA PLUS A;Lo;0;L;;;;;N;;;;;
+124BF;CUNEIFORM SIGN GA2 TIMES KUSHU2 PLUS KASKAL;Lo;0;L;;;;;N;;;;;
+124C0;CUNEIFORM SIGN GA2 TIMES LAM;Lo;0;L;;;;;N;;;;;
+124C1;CUNEIFORM SIGN GA2 TIMES LAM TIMES KUR;Lo;0;L;;;;;N;;;;;
+124C2;CUNEIFORM SIGN GA2 TIMES LUH;Lo;0;L;;;;;N;;;;;
+124C3;CUNEIFORM SIGN GA2 TIMES MUSH;Lo;0;L;;;;;N;;;;;
+124C4;CUNEIFORM SIGN GA2 TIMES NE;Lo;0;L;;;;;N;;;;;
+124C5;CUNEIFORM SIGN GA2 TIMES NE PLUS E2;Lo;0;L;;;;;N;;;;;
+124C6;CUNEIFORM SIGN GA2 TIMES NE PLUS GI;Lo;0;L;;;;;N;;;;;
+124C7;CUNEIFORM SIGN GA2 TIMES SHIM;Lo;0;L;;;;;N;;;;;
+124C8;CUNEIFORM SIGN GA2 TIMES ZIZ2;Lo;0;L;;;;;N;;;;;
+124C9;CUNEIFORM SIGN GABA ROTATED NINETY DEGREES;Lo;0;L;;;;;N;;;;;
+124CA;CUNEIFORM SIGN GESHTIN TIMES U;Lo;0;L;;;;;N;;;;;
+124CB;CUNEIFORM SIGN GISH TIMES GISH CROSSING GISH;Lo;0;L;;;;;N;;;;;
+124CC;CUNEIFORM SIGN GU2 TIMES IGI GUNU;Lo;0;L;;;;;N;;;;;
+124CD;CUNEIFORM SIGN GUD PLUS GISH TIMES TAK4;Lo;0;L;;;;;N;;;;;
+124CE;CUNEIFORM SIGN HA TENU GUNU;Lo;0;L;;;;;N;;;;;
+124CF;CUNEIFORM SIGN HI TIMES ASH OVER HI TIMES ASH;Lo;0;L;;;;;N;;;;;
+124D0;CUNEIFORM SIGN KA TIMES BU;Lo;0;L;;;;;N;;;;;
+124D1;CUNEIFORM SIGN KA TIMES KA;Lo;0;L;;;;;N;;;;;
+124D2;CUNEIFORM SIGN KA TIMES U U U;Lo;0;L;;;;;N;;;;;
+124D3;CUNEIFORM SIGN KA TIMES UR;Lo;0;L;;;;;N;;;;;
+124D4;CUNEIFORM SIGN LAGAB TIMES ZU OVER ZU;Lo;0;L;;;;;N;;;;;
+124D5;CUNEIFORM SIGN LAK-003;Lo;0;L;;;;;N;;;;;
+124D6;CUNEIFORM SIGN LAK-021;Lo;0;L;;;;;N;;;;;
+124D7;CUNEIFORM SIGN LAK-025;Lo;0;L;;;;;N;;;;;
+124D8;CUNEIFORM SIGN LAK-030;Lo;0;L;;;;;N;;;;;
+124D9;CUNEIFORM SIGN LAK-050;Lo;0;L;;;;;N;;;;;
+124DA;CUNEIFORM SIGN LAK-051;Lo;0;L;;;;;N;;;;;
+124DB;CUNEIFORM SIGN LAK-062;Lo;0;L;;;;;N;;;;;
+124DC;CUNEIFORM SIGN LAK-079 OVER LAK-079 GUNU;Lo;0;L;;;;;N;;;;;
+124DD;CUNEIFORM SIGN LAK-080;Lo;0;L;;;;;N;;;;;
+124DE;CUNEIFORM SIGN LAK-081 OVER LAK-081;Lo;0;L;;;;;N;;;;;
+124DF;CUNEIFORM SIGN LAK-092;Lo;0;L;;;;;N;;;;;
+124E0;CUNEIFORM SIGN LAK-130;Lo;0;L;;;;;N;;;;;
+124E1;CUNEIFORM SIGN LAK-142;Lo;0;L;;;;;N;;;;;
+124E2;CUNEIFORM SIGN LAK-210;Lo;0;L;;;;;N;;;;;
+124E3;CUNEIFORM SIGN LAK-219;Lo;0;L;;;;;N;;;;;
+124E4;CUNEIFORM SIGN LAK-220;Lo;0;L;;;;;N;;;;;
+124E5;CUNEIFORM SIGN LAK-225;Lo;0;L;;;;;N;;;;;
+124E6;CUNEIFORM SIGN LAK-228;Lo;0;L;;;;;N;;;;;
+124E7;CUNEIFORM SIGN LAK-238;Lo;0;L;;;;;N;;;;;
+124E8;CUNEIFORM SIGN LAK-265;Lo;0;L;;;;;N;;;;;
+124E9;CUNEIFORM SIGN LAK-266;Lo;0;L;;;;;N;;;;;
+124EA;CUNEIFORM SIGN LAK-343;Lo;0;L;;;;;N;;;;;
+124EB;CUNEIFORM SIGN LAK-347;Lo;0;L;;;;;N;;;;;
+124EC;CUNEIFORM SIGN LAK-348;Lo;0;L;;;;;N;;;;;
+124ED;CUNEIFORM SIGN LAK-383;Lo;0;L;;;;;N;;;;;
+124EE;CUNEIFORM SIGN LAK-384;Lo;0;L;;;;;N;;;;;
+124EF;CUNEIFORM SIGN LAK-390;Lo;0;L;;;;;N;;;;;
+124F0;CUNEIFORM SIGN LAK-441;Lo;0;L;;;;;N;;;;;
+124F1;CUNEIFORM SIGN LAK-449;Lo;0;L;;;;;N;;;;;
+124F2;CUNEIFORM SIGN LAK-449 TIMES GU;Lo;0;L;;;;;N;;;;;
+124F3;CUNEIFORM SIGN LAK-449 TIMES IGI;Lo;0;L;;;;;N;;;;;
+124F4;CUNEIFORM SIGN LAK-449 TIMES PAP PLUS LU3;Lo;0;L;;;;;N;;;;;
+124F5;CUNEIFORM SIGN LAK-449 TIMES PAP PLUS PAP PLUS LU3;Lo;0;L;;;;;N;;;;;
+124F6;CUNEIFORM SIGN LAK-449 TIMES U2 PLUS BA;Lo;0;L;;;;;N;;;;;
+124F7;CUNEIFORM SIGN LAK-450;Lo;0;L;;;;;N;;;;;
+124F8;CUNEIFORM SIGN LAK-457;Lo;0;L;;;;;N;;;;;
+124F9;CUNEIFORM SIGN LAK-470;Lo;0;L;;;;;N;;;;;
+124FA;CUNEIFORM SIGN LAK-483;Lo;0;L;;;;;N;;;;;
+124FB;CUNEIFORM SIGN LAK-490;Lo;0;L;;;;;N;;;;;
+124FC;CUNEIFORM SIGN LAK-492;Lo;0;L;;;;;N;;;;;
+124FD;CUNEIFORM SIGN LAK-493;Lo;0;L;;;;;N;;;;;
+124FE;CUNEIFORM SIGN LAK-495;Lo;0;L;;;;;N;;;;;
+124FF;CUNEIFORM SIGN LAK-550;Lo;0;L;;;;;N;;;;;
+12500;CUNEIFORM SIGN LAK-608;Lo;0;L;;;;;N;;;;;
+12501;CUNEIFORM SIGN LAK-617;Lo;0;L;;;;;N;;;;;
+12502;CUNEIFORM SIGN LAK-617 TIMES ASH;Lo;0;L;;;;;N;;;;;
+12503;CUNEIFORM SIGN LAK-617 TIMES BAD;Lo;0;L;;;;;N;;;;;
+12504;CUNEIFORM SIGN LAK-617 TIMES DUN3 GUNU GUNU;Lo;0;L;;;;;N;;;;;
+12505;CUNEIFORM SIGN LAK-617 TIMES KU3;Lo;0;L;;;;;N;;;;;
+12506;CUNEIFORM SIGN LAK-617 TIMES LA;Lo;0;L;;;;;N;;;;;
+12507;CUNEIFORM SIGN LAK-617 TIMES TAR;Lo;0;L;;;;;N;;;;;
+12508;CUNEIFORM SIGN LAK-617 TIMES TE;Lo;0;L;;;;;N;;;;;
+12509;CUNEIFORM SIGN LAK-617 TIMES U2;Lo;0;L;;;;;N;;;;;
+1250A;CUNEIFORM SIGN LAK-617 TIMES UD;Lo;0;L;;;;;N;;;;;
+1250B;CUNEIFORM SIGN LAK-617 TIMES URUDA;Lo;0;L;;;;;N;;;;;
+1250C;CUNEIFORM SIGN LAK-636;Lo;0;L;;;;;N;;;;;
+1250D;CUNEIFORM SIGN LAK-648;Lo;0;L;;;;;N;;;;;
+1250E;CUNEIFORM SIGN LAK-648 TIMES DUB;Lo;0;L;;;;;N;;;;;
+1250F;CUNEIFORM SIGN LAK-648 TIMES GA;Lo;0;L;;;;;N;;;;;
+12510;CUNEIFORM SIGN LAK-648 TIMES IGI;Lo;0;L;;;;;N;;;;;
+12511;CUNEIFORM SIGN LAK-648 TIMES IGI GUNU;Lo;0;L;;;;;N;;;;;
+12512;CUNEIFORM SIGN LAK-648 TIMES NI;Lo;0;L;;;;;N;;;;;
+12513;CUNEIFORM SIGN LAK-648 TIMES PAP PLUS PAP PLUS LU3;Lo;0;L;;;;;N;;;;;
+12514;CUNEIFORM SIGN LAK-648 TIMES SHESH PLUS KI;Lo;0;L;;;;;N;;;;;
+12515;CUNEIFORM SIGN LAK-648 TIMES UD;Lo;0;L;;;;;N;;;;;
+12516;CUNEIFORM SIGN LAK-648 TIMES URUDA;Lo;0;L;;;;;N;;;;;
+12517;CUNEIFORM SIGN LAK-724;Lo;0;L;;;;;N;;;;;
+12518;CUNEIFORM SIGN LAK-749;Lo;0;L;;;;;N;;;;;
+12519;CUNEIFORM SIGN LU2 GUNU TIMES ASH;Lo;0;L;;;;;N;;;;;
+1251A;CUNEIFORM SIGN LU2 TIMES DISH;Lo;0;L;;;;;N;;;;;
+1251B;CUNEIFORM SIGN LU2 TIMES HAL;Lo;0;L;;;;;N;;;;;
+1251C;CUNEIFORM SIGN LU2 TIMES PAP;Lo;0;L;;;;;N;;;;;
+1251D;CUNEIFORM SIGN LU2 TIMES PAP PLUS PAP PLUS LU3;Lo;0;L;;;;;N;;;;;
+1251E;CUNEIFORM SIGN LU2 TIMES TAK4;Lo;0;L;;;;;N;;;;;
+1251F;CUNEIFORM SIGN MI PLUS ZA7;Lo;0;L;;;;;N;;;;;
+12520;CUNEIFORM SIGN MUSH OVER MUSH TIMES GA;Lo;0;L;;;;;N;;;;;
+12521;CUNEIFORM SIGN MUSH OVER MUSH TIMES KAK;Lo;0;L;;;;;N;;;;;
+12522;CUNEIFORM SIGN NINDA2 TIMES DIM GUNU;Lo;0;L;;;;;N;;;;;
+12523;CUNEIFORM SIGN NINDA2 TIMES GISH;Lo;0;L;;;;;N;;;;;
+12524;CUNEIFORM SIGN NINDA2 TIMES GUL;Lo;0;L;;;;;N;;;;;
+12525;CUNEIFORM SIGN NINDA2 TIMES HI;Lo;0;L;;;;;N;;;;;
+12526;CUNEIFORM SIGN NINDA2 TIMES KESH2;Lo;0;L;;;;;N;;;;;
+12527;CUNEIFORM SIGN NINDA2 TIMES LAK-050;Lo;0;L;;;;;N;;;;;
+12528;CUNEIFORM SIGN NINDA2 TIMES MASH;Lo;0;L;;;;;N;;;;;
+12529;CUNEIFORM SIGN NINDA2 TIMES PAP PLUS PAP;Lo;0;L;;;;;N;;;;;
+1252A;CUNEIFORM SIGN NINDA2 TIMES U;Lo;0;L;;;;;N;;;;;
+1252B;CUNEIFORM SIGN NINDA2 TIMES U PLUS U;Lo;0;L;;;;;N;;;;;
+1252C;CUNEIFORM SIGN NINDA2 TIMES URUDA;Lo;0;L;;;;;N;;;;;
+1252D;CUNEIFORM SIGN SAG GUNU TIMES HA;Lo;0;L;;;;;N;;;;;
+1252E;CUNEIFORM SIGN SAG TIMES EN;Lo;0;L;;;;;N;;;;;
+1252F;CUNEIFORM SIGN SAG TIMES SHE AT LEFT;Lo;0;L;;;;;N;;;;;
+12530;CUNEIFORM SIGN SAG TIMES TAK4;Lo;0;L;;;;;N;;;;;
+12531;CUNEIFORM SIGN SHA6 TENU;Lo;0;L;;;;;N;;;;;
+12532;CUNEIFORM SIGN SHE OVER SHE;Lo;0;L;;;;;N;;;;;
+12533;CUNEIFORM SIGN SHE PLUS HUB2;Lo;0;L;;;;;N;;;;;
+12534;CUNEIFORM SIGN SHE PLUS NAM2;Lo;0;L;;;;;N;;;;;
+12535;CUNEIFORM SIGN SHE PLUS SAR;Lo;0;L;;;;;N;;;;;
+12536;CUNEIFORM SIGN SHU2 PLUS DUG TIMES NI;Lo;0;L;;;;;N;;;;;
+12537;CUNEIFORM SIGN SHU2 PLUS E2 TIMES AN;Lo;0;L;;;;;N;;;;;
+12538;CUNEIFORM SIGN SI TIMES TAK4;Lo;0;L;;;;;N;;;;;
+12539;CUNEIFORM SIGN TAK4 PLUS SAG;Lo;0;L;;;;;N;;;;;
+1253A;CUNEIFORM SIGN TUM TIMES GAN2 TENU;Lo;0;L;;;;;N;;;;;
+1253B;CUNEIFORM SIGN TUM TIMES THREE DISH;Lo;0;L;;;;;N;;;;;
+1253C;CUNEIFORM SIGN UR2 INVERTED;Lo;0;L;;;;;N;;;;;
+1253D;CUNEIFORM SIGN UR2 TIMES UD;Lo;0;L;;;;;N;;;;;
+1253E;CUNEIFORM SIGN URU TIMES DARA3;Lo;0;L;;;;;N;;;;;
+1253F;CUNEIFORM SIGN URU TIMES LAK-668;Lo;0;L;;;;;N;;;;;
+12540;CUNEIFORM SIGN URU TIMES LU3;Lo;0;L;;;;;N;;;;;
+12541;CUNEIFORM SIGN ZA7;Lo;0;L;;;;;N;;;;;
+12542;CUNEIFORM SIGN ZU OVER ZU PLUS SAR;Lo;0;L;;;;;N;;;;;
+12543;CUNEIFORM SIGN ZU5 TIMES THREE DISH TENU;Lo;0;L;;;;;N;;;;;
+13000;EGYPTIAN HIEROGLYPH A001;Lo;0;L;;;;;N;;;;;
+13001;EGYPTIAN HIEROGLYPH A002;Lo;0;L;;;;;N;;;;;
+13002;EGYPTIAN HIEROGLYPH A003;Lo;0;L;;;;;N;;;;;
+13003;EGYPTIAN HIEROGLYPH A004;Lo;0;L;;;;;N;;;;;
+13004;EGYPTIAN HIEROGLYPH A005;Lo;0;L;;;;;N;;;;;
+13005;EGYPTIAN HIEROGLYPH A005A;Lo;0;L;;;;;N;;;;;
+13006;EGYPTIAN HIEROGLYPH A006;Lo;0;L;;;;;N;;;;;
+13007;EGYPTIAN HIEROGLYPH A006A;Lo;0;L;;;;;N;;;;;
+13008;EGYPTIAN HIEROGLYPH A006B;Lo;0;L;;;;;N;;;;;
+13009;EGYPTIAN HIEROGLYPH A007;Lo;0;L;;;;;N;;;;;
+1300A;EGYPTIAN HIEROGLYPH A008;Lo;0;L;;;;;N;;;;;
+1300B;EGYPTIAN HIEROGLYPH A009;Lo;0;L;;;;;N;;;;;
+1300C;EGYPTIAN HIEROGLYPH A010;Lo;0;L;;;;;N;;;;;
+1300D;EGYPTIAN HIEROGLYPH A011;Lo;0;L;;;;;N;;;;;
+1300E;EGYPTIAN HIEROGLYPH A012;Lo;0;L;;;;;N;;;;;
+1300F;EGYPTIAN HIEROGLYPH A013;Lo;0;L;;;;;N;;;;;
+13010;EGYPTIAN HIEROGLYPH A014;Lo;0;L;;;;;N;;;;;
+13011;EGYPTIAN HIEROGLYPH A014A;Lo;0;L;;;;;N;;;;;
+13012;EGYPTIAN HIEROGLYPH A015;Lo;0;L;;;;;N;;;;;
+13013;EGYPTIAN HIEROGLYPH A016;Lo;0;L;;;;;N;;;;;
+13014;EGYPTIAN HIEROGLYPH A017;Lo;0;L;;;;;N;;;;;
+13015;EGYPTIAN HIEROGLYPH A017A;Lo;0;L;;;;;N;;;;;
+13016;EGYPTIAN HIEROGLYPH A018;Lo;0;L;;;;;N;;;;;
+13017;EGYPTIAN HIEROGLYPH A019;Lo;0;L;;;;;N;;;;;
+13018;EGYPTIAN HIEROGLYPH A020;Lo;0;L;;;;;N;;;;;
+13019;EGYPTIAN HIEROGLYPH A021;Lo;0;L;;;;;N;;;;;
+1301A;EGYPTIAN HIEROGLYPH A022;Lo;0;L;;;;;N;;;;;
+1301B;EGYPTIAN HIEROGLYPH A023;Lo;0;L;;;;;N;;;;;
+1301C;EGYPTIAN HIEROGLYPH A024;Lo;0;L;;;;;N;;;;;
+1301D;EGYPTIAN HIEROGLYPH A025;Lo;0;L;;;;;N;;;;;
+1301E;EGYPTIAN HIEROGLYPH A026;Lo;0;L;;;;;N;;;;;
+1301F;EGYPTIAN HIEROGLYPH A027;Lo;0;L;;;;;N;;;;;
+13020;EGYPTIAN HIEROGLYPH A028;Lo;0;L;;;;;N;;;;;
+13021;EGYPTIAN HIEROGLYPH A029;Lo;0;L;;;;;N;;;;;
+13022;EGYPTIAN HIEROGLYPH A030;Lo;0;L;;;;;N;;;;;
+13023;EGYPTIAN HIEROGLYPH A031;Lo;0;L;;;;;N;;;;;
+13024;EGYPTIAN HIEROGLYPH A032;Lo;0;L;;;;;N;;;;;
+13025;EGYPTIAN HIEROGLYPH A032A;Lo;0;L;;;;;N;;;;;
+13026;EGYPTIAN HIEROGLYPH A033;Lo;0;L;;;;;N;;;;;
+13027;EGYPTIAN HIEROGLYPH A034;Lo;0;L;;;;;N;;;;;
+13028;EGYPTIAN HIEROGLYPH A035;Lo;0;L;;;;;N;;;;;
+13029;EGYPTIAN HIEROGLYPH A036;Lo;0;L;;;;;N;;;;;
+1302A;EGYPTIAN HIEROGLYPH A037;Lo;0;L;;;;;N;;;;;
+1302B;EGYPTIAN HIEROGLYPH A038;Lo;0;L;;;;;N;;;;;
+1302C;EGYPTIAN HIEROGLYPH A039;Lo;0;L;;;;;N;;;;;
+1302D;EGYPTIAN HIEROGLYPH A040;Lo;0;L;;;;;N;;;;;
+1302E;EGYPTIAN HIEROGLYPH A040A;Lo;0;L;;;;;N;;;;;
+1302F;EGYPTIAN HIEROGLYPH A041;Lo;0;L;;;;;N;;;;;
+13030;EGYPTIAN HIEROGLYPH A042;Lo;0;L;;;;;N;;;;;
+13031;EGYPTIAN HIEROGLYPH A042A;Lo;0;L;;;;;N;;;;;
+13032;EGYPTIAN HIEROGLYPH A043;Lo;0;L;;;;;N;;;;;
+13033;EGYPTIAN HIEROGLYPH A043A;Lo;0;L;;;;;N;;;;;
+13034;EGYPTIAN HIEROGLYPH A044;Lo;0;L;;;;;N;;;;;
+13035;EGYPTIAN HIEROGLYPH A045;Lo;0;L;;;;;N;;;;;
+13036;EGYPTIAN HIEROGLYPH A045A;Lo;0;L;;;;;N;;;;;
+13037;EGYPTIAN HIEROGLYPH A046;Lo;0;L;;;;;N;;;;;
+13038;EGYPTIAN HIEROGLYPH A047;Lo;0;L;;;;;N;;;;;
+13039;EGYPTIAN HIEROGLYPH A048;Lo;0;L;;;;;N;;;;;
+1303A;EGYPTIAN HIEROGLYPH A049;Lo;0;L;;;;;N;;;;;
+1303B;EGYPTIAN HIEROGLYPH A050;Lo;0;L;;;;;N;;;;;
+1303C;EGYPTIAN HIEROGLYPH A051;Lo;0;L;;;;;N;;;;;
+1303D;EGYPTIAN HIEROGLYPH A052;Lo;0;L;;;;;N;;;;;
+1303E;EGYPTIAN HIEROGLYPH A053;Lo;0;L;;;;;N;;;;;
+1303F;EGYPTIAN HIEROGLYPH A054;Lo;0;L;;;;;N;;;;;
+13040;EGYPTIAN HIEROGLYPH A055;Lo;0;L;;;;;N;;;;;
+13041;EGYPTIAN HIEROGLYPH A056;Lo;0;L;;;;;N;;;;;
+13042;EGYPTIAN HIEROGLYPH A057;Lo;0;L;;;;;N;;;;;
+13043;EGYPTIAN HIEROGLYPH A058;Lo;0;L;;;;;N;;;;;
+13044;EGYPTIAN HIEROGLYPH A059;Lo;0;L;;;;;N;;;;;
+13045;EGYPTIAN HIEROGLYPH A060;Lo;0;L;;;;;N;;;;;
+13046;EGYPTIAN HIEROGLYPH A061;Lo;0;L;;;;;N;;;;;
+13047;EGYPTIAN HIEROGLYPH A062;Lo;0;L;;;;;N;;;;;
+13048;EGYPTIAN HIEROGLYPH A063;Lo;0;L;;;;;N;;;;;
+13049;EGYPTIAN HIEROGLYPH A064;Lo;0;L;;;;;N;;;;;
+1304A;EGYPTIAN HIEROGLYPH A065;Lo;0;L;;;;;N;;;;;
+1304B;EGYPTIAN HIEROGLYPH A066;Lo;0;L;;;;;N;;;;;
+1304C;EGYPTIAN HIEROGLYPH A067;Lo;0;L;;;;;N;;;;;
+1304D;EGYPTIAN HIEROGLYPH A068;Lo;0;L;;;;;N;;;;;
+1304E;EGYPTIAN HIEROGLYPH A069;Lo;0;L;;;;;N;;;;;
+1304F;EGYPTIAN HIEROGLYPH A070;Lo;0;L;;;;;N;;;;;
+13050;EGYPTIAN HIEROGLYPH B001;Lo;0;L;;;;;N;;;;;
+13051;EGYPTIAN HIEROGLYPH B002;Lo;0;L;;;;;N;;;;;
+13052;EGYPTIAN HIEROGLYPH B003;Lo;0;L;;;;;N;;;;;
+13053;EGYPTIAN HIEROGLYPH B004;Lo;0;L;;;;;N;;;;;
+13054;EGYPTIAN HIEROGLYPH B005;Lo;0;L;;;;;N;;;;;
+13055;EGYPTIAN HIEROGLYPH B005A;Lo;0;L;;;;;N;;;;;
+13056;EGYPTIAN HIEROGLYPH B006;Lo;0;L;;;;;N;;;;;
+13057;EGYPTIAN HIEROGLYPH B007;Lo;0;L;;;;;N;;;;;
+13058;EGYPTIAN HIEROGLYPH B008;Lo;0;L;;;;;N;;;;;
+13059;EGYPTIAN HIEROGLYPH B009;Lo;0;L;;;;;N;;;;;
+1305A;EGYPTIAN HIEROGLYPH C001;Lo;0;L;;;;;N;;;;;
+1305B;EGYPTIAN HIEROGLYPH C002;Lo;0;L;;;;;N;;;;;
+1305C;EGYPTIAN HIEROGLYPH C002A;Lo;0;L;;;;;N;;;;;
+1305D;EGYPTIAN HIEROGLYPH C002B;Lo;0;L;;;;;N;;;;;
+1305E;EGYPTIAN HIEROGLYPH C002C;Lo;0;L;;;;;N;;;;;
+1305F;EGYPTIAN HIEROGLYPH C003;Lo;0;L;;;;;N;;;;;
+13060;EGYPTIAN HIEROGLYPH C004;Lo;0;L;;;;;N;;;;;
+13061;EGYPTIAN HIEROGLYPH C005;Lo;0;L;;;;;N;;;;;
+13062;EGYPTIAN HIEROGLYPH C006;Lo;0;L;;;;;N;;;;;
+13063;EGYPTIAN HIEROGLYPH C007;Lo;0;L;;;;;N;;;;;
+13064;EGYPTIAN HIEROGLYPH C008;Lo;0;L;;;;;N;;;;;
+13065;EGYPTIAN HIEROGLYPH C009;Lo;0;L;;;;;N;;;;;
+13066;EGYPTIAN HIEROGLYPH C010;Lo;0;L;;;;;N;;;;;
+13067;EGYPTIAN HIEROGLYPH C010A;Lo;0;L;;;;;N;;;;;
+13068;EGYPTIAN HIEROGLYPH C011;Lo;0;L;;;;;N;;;;;
+13069;EGYPTIAN HIEROGLYPH C012;Lo;0;L;;;;;N;;;;;
+1306A;EGYPTIAN HIEROGLYPH C013;Lo;0;L;;;;;N;;;;;
+1306B;EGYPTIAN HIEROGLYPH C014;Lo;0;L;;;;;N;;;;;
+1306C;EGYPTIAN HIEROGLYPH C015;Lo;0;L;;;;;N;;;;;
+1306D;EGYPTIAN HIEROGLYPH C016;Lo;0;L;;;;;N;;;;;
+1306E;EGYPTIAN HIEROGLYPH C017;Lo;0;L;;;;;N;;;;;
+1306F;EGYPTIAN HIEROGLYPH C018;Lo;0;L;;;;;N;;;;;
+13070;EGYPTIAN HIEROGLYPH C019;Lo;0;L;;;;;N;;;;;
+13071;EGYPTIAN HIEROGLYPH C020;Lo;0;L;;;;;N;;;;;
+13072;EGYPTIAN HIEROGLYPH C021;Lo;0;L;;;;;N;;;;;
+13073;EGYPTIAN HIEROGLYPH C022;Lo;0;L;;;;;N;;;;;
+13074;EGYPTIAN HIEROGLYPH C023;Lo;0;L;;;;;N;;;;;
+13075;EGYPTIAN HIEROGLYPH C024;Lo;0;L;;;;;N;;;;;
+13076;EGYPTIAN HIEROGLYPH D001;Lo;0;L;;;;;N;;;;;
+13077;EGYPTIAN HIEROGLYPH D002;Lo;0;L;;;;;N;;;;;
+13078;EGYPTIAN HIEROGLYPH D003;Lo;0;L;;;;;N;;;;;
+13079;EGYPTIAN HIEROGLYPH D004;Lo;0;L;;;;;N;;;;;
+1307A;EGYPTIAN HIEROGLYPH D005;Lo;0;L;;;;;N;;;;;
+1307B;EGYPTIAN HIEROGLYPH D006;Lo;0;L;;;;;N;;;;;
+1307C;EGYPTIAN HIEROGLYPH D007;Lo;0;L;;;;;N;;;;;
+1307D;EGYPTIAN HIEROGLYPH D008;Lo;0;L;;;;;N;;;;;
+1307E;EGYPTIAN HIEROGLYPH D008A;Lo;0;L;;;;;N;;;;;
+1307F;EGYPTIAN HIEROGLYPH D009;Lo;0;L;;;;;N;;;;;
+13080;EGYPTIAN HIEROGLYPH D010;Lo;0;L;;;;;N;;;;;
+13081;EGYPTIAN HIEROGLYPH D011;Lo;0;L;;;;;N;;;;;
+13082;EGYPTIAN HIEROGLYPH D012;Lo;0;L;;;;;N;;;;;
+13083;EGYPTIAN HIEROGLYPH D013;Lo;0;L;;;;;N;;;;;
+13084;EGYPTIAN HIEROGLYPH D014;Lo;0;L;;;;;N;;;;;
+13085;EGYPTIAN HIEROGLYPH D015;Lo;0;L;;;;;N;;;;;
+13086;EGYPTIAN HIEROGLYPH D016;Lo;0;L;;;;;N;;;;;
+13087;EGYPTIAN HIEROGLYPH D017;Lo;0;L;;;;;N;;;;;
+13088;EGYPTIAN HIEROGLYPH D018;Lo;0;L;;;;;N;;;;;
+13089;EGYPTIAN HIEROGLYPH D019;Lo;0;L;;;;;N;;;;;
+1308A;EGYPTIAN HIEROGLYPH D020;Lo;0;L;;;;;N;;;;;
+1308B;EGYPTIAN HIEROGLYPH D021;Lo;0;L;;;;;N;;;;;
+1308C;EGYPTIAN HIEROGLYPH D022;Lo;0;L;;;;;N;;;;;
+1308D;EGYPTIAN HIEROGLYPH D023;Lo;0;L;;;;;N;;;;;
+1308E;EGYPTIAN HIEROGLYPH D024;Lo;0;L;;;;;N;;;;;
+1308F;EGYPTIAN HIEROGLYPH D025;Lo;0;L;;;;;N;;;;;
+13090;EGYPTIAN HIEROGLYPH D026;Lo;0;L;;;;;N;;;;;
+13091;EGYPTIAN HIEROGLYPH D027;Lo;0;L;;;;;N;;;;;
+13092;EGYPTIAN HIEROGLYPH D027A;Lo;0;L;;;;;N;;;;;
+13093;EGYPTIAN HIEROGLYPH D028;Lo;0;L;;;;;N;;;;;
+13094;EGYPTIAN HIEROGLYPH D029;Lo;0;L;;;;;N;;;;;
+13095;EGYPTIAN HIEROGLYPH D030;Lo;0;L;;;;;N;;;;;
+13096;EGYPTIAN HIEROGLYPH D031;Lo;0;L;;;;;N;;;;;
+13097;EGYPTIAN HIEROGLYPH D031A;Lo;0;L;;;;;N;;;;;
+13098;EGYPTIAN HIEROGLYPH D032;Lo;0;L;;;;;N;;;;;
+13099;EGYPTIAN HIEROGLYPH D033;Lo;0;L;;;;;N;;;;;
+1309A;EGYPTIAN HIEROGLYPH D034;Lo;0;L;;;;;N;;;;;
+1309B;EGYPTIAN HIEROGLYPH D034A;Lo;0;L;;;;;N;;;;;
+1309C;EGYPTIAN HIEROGLYPH D035;Lo;0;L;;;;;N;;;;;
+1309D;EGYPTIAN HIEROGLYPH D036;Lo;0;L;;;;;N;;;;;
+1309E;EGYPTIAN HIEROGLYPH D037;Lo;0;L;;;;;N;;;;;
+1309F;EGYPTIAN HIEROGLYPH D038;Lo;0;L;;;;;N;;;;;
+130A0;EGYPTIAN HIEROGLYPH D039;Lo;0;L;;;;;N;;;;;
+130A1;EGYPTIAN HIEROGLYPH D040;Lo;0;L;;;;;N;;;;;
+130A2;EGYPTIAN HIEROGLYPH D041;Lo;0;L;;;;;N;;;;;
+130A3;EGYPTIAN HIEROGLYPH D042;Lo;0;L;;;;;N;;;;;
+130A4;EGYPTIAN HIEROGLYPH D043;Lo;0;L;;;;;N;;;;;
+130A5;EGYPTIAN HIEROGLYPH D044;Lo;0;L;;;;;N;;;;;
+130A6;EGYPTIAN HIEROGLYPH D045;Lo;0;L;;;;;N;;;;;
+130A7;EGYPTIAN HIEROGLYPH D046;Lo;0;L;;;;;N;;;;;
+130A8;EGYPTIAN HIEROGLYPH D046A;Lo;0;L;;;;;N;;;;;
+130A9;EGYPTIAN HIEROGLYPH D047;Lo;0;L;;;;;N;;;;;
+130AA;EGYPTIAN HIEROGLYPH D048;Lo;0;L;;;;;N;;;;;
+130AB;EGYPTIAN HIEROGLYPH D048A;Lo;0;L;;;;;N;;;;;
+130AC;EGYPTIAN HIEROGLYPH D049;Lo;0;L;;;;;N;;;;;
+130AD;EGYPTIAN HIEROGLYPH D050;Lo;0;L;;;;;N;;;;;
+130AE;EGYPTIAN HIEROGLYPH D050A;Lo;0;L;;;;;N;;;;;
+130AF;EGYPTIAN HIEROGLYPH D050B;Lo;0;L;;;;;N;;;;;
+130B0;EGYPTIAN HIEROGLYPH D050C;Lo;0;L;;;;;N;;;;;
+130B1;EGYPTIAN HIEROGLYPH D050D;Lo;0;L;;;;;N;;;;;
+130B2;EGYPTIAN HIEROGLYPH D050E;Lo;0;L;;;;;N;;;;;
+130B3;EGYPTIAN HIEROGLYPH D050F;Lo;0;L;;;;;N;;;;;
+130B4;EGYPTIAN HIEROGLYPH D050G;Lo;0;L;;;;;N;;;;;
+130B5;EGYPTIAN HIEROGLYPH D050H;Lo;0;L;;;;;N;;;;;
+130B6;EGYPTIAN HIEROGLYPH D050I;Lo;0;L;;;;;N;;;;;
+130B7;EGYPTIAN HIEROGLYPH D051;Lo;0;L;;;;;N;;;;;
+130B8;EGYPTIAN HIEROGLYPH D052;Lo;0;L;;;;;N;;;;;
+130B9;EGYPTIAN HIEROGLYPH D052A;Lo;0;L;;;;;N;;;;;
+130BA;EGYPTIAN HIEROGLYPH D053;Lo;0;L;;;;;N;;;;;
+130BB;EGYPTIAN HIEROGLYPH D054;Lo;0;L;;;;;N;;;;;
+130BC;EGYPTIAN HIEROGLYPH D054A;Lo;0;L;;;;;N;;;;;
+130BD;EGYPTIAN HIEROGLYPH D055;Lo;0;L;;;;;N;;;;;
+130BE;EGYPTIAN HIEROGLYPH D056;Lo;0;L;;;;;N;;;;;
+130BF;EGYPTIAN HIEROGLYPH D057;Lo;0;L;;;;;N;;;;;
+130C0;EGYPTIAN HIEROGLYPH D058;Lo;0;L;;;;;N;;;;;
+130C1;EGYPTIAN HIEROGLYPH D059;Lo;0;L;;;;;N;;;;;
+130C2;EGYPTIAN HIEROGLYPH D060;Lo;0;L;;;;;N;;;;;
+130C3;EGYPTIAN HIEROGLYPH D061;Lo;0;L;;;;;N;;;;;
+130C4;EGYPTIAN HIEROGLYPH D062;Lo;0;L;;;;;N;;;;;
+130C5;EGYPTIAN HIEROGLYPH D063;Lo;0;L;;;;;N;;;;;
+130C6;EGYPTIAN HIEROGLYPH D064;Lo;0;L;;;;;N;;;;;
+130C7;EGYPTIAN HIEROGLYPH D065;Lo;0;L;;;;;N;;;;;
+130C8;EGYPTIAN HIEROGLYPH D066;Lo;0;L;;;;;N;;;;;
+130C9;EGYPTIAN HIEROGLYPH D067;Lo;0;L;;;;;N;;;;;
+130CA;EGYPTIAN HIEROGLYPH D067A;Lo;0;L;;;;;N;;;;;
+130CB;EGYPTIAN HIEROGLYPH D067B;Lo;0;L;;;;;N;;;;;
+130CC;EGYPTIAN HIEROGLYPH D067C;Lo;0;L;;;;;N;;;;;
+130CD;EGYPTIAN HIEROGLYPH D067D;Lo;0;L;;;;;N;;;;;
+130CE;EGYPTIAN HIEROGLYPH D067E;Lo;0;L;;;;;N;;;;;
+130CF;EGYPTIAN HIEROGLYPH D067F;Lo;0;L;;;;;N;;;;;
+130D0;EGYPTIAN HIEROGLYPH D067G;Lo;0;L;;;;;N;;;;;
+130D1;EGYPTIAN HIEROGLYPH D067H;Lo;0;L;;;;;N;;;;;
+130D2;EGYPTIAN HIEROGLYPH E001;Lo;0;L;;;;;N;;;;;
+130D3;EGYPTIAN HIEROGLYPH E002;Lo;0;L;;;;;N;;;;;
+130D4;EGYPTIAN HIEROGLYPH E003;Lo;0;L;;;;;N;;;;;
+130D5;EGYPTIAN HIEROGLYPH E004;Lo;0;L;;;;;N;;;;;
+130D6;EGYPTIAN HIEROGLYPH E005;Lo;0;L;;;;;N;;;;;
+130D7;EGYPTIAN HIEROGLYPH E006;Lo;0;L;;;;;N;;;;;
+130D8;EGYPTIAN HIEROGLYPH E007;Lo;0;L;;;;;N;;;;;
+130D9;EGYPTIAN HIEROGLYPH E008;Lo;0;L;;;;;N;;;;;
+130DA;EGYPTIAN HIEROGLYPH E008A;Lo;0;L;;;;;N;;;;;
+130DB;EGYPTIAN HIEROGLYPH E009;Lo;0;L;;;;;N;;;;;
+130DC;EGYPTIAN HIEROGLYPH E009A;Lo;0;L;;;;;N;;;;;
+130DD;EGYPTIAN HIEROGLYPH E010;Lo;0;L;;;;;N;;;;;
+130DE;EGYPTIAN HIEROGLYPH E011;Lo;0;L;;;;;N;;;;;
+130DF;EGYPTIAN HIEROGLYPH E012;Lo;0;L;;;;;N;;;;;
+130E0;EGYPTIAN HIEROGLYPH E013;Lo;0;L;;;;;N;;;;;
+130E1;EGYPTIAN HIEROGLYPH E014;Lo;0;L;;;;;N;;;;;
+130E2;EGYPTIAN HIEROGLYPH E015;Lo;0;L;;;;;N;;;;;
+130E3;EGYPTIAN HIEROGLYPH E016;Lo;0;L;;;;;N;;;;;
+130E4;EGYPTIAN HIEROGLYPH E016A;Lo;0;L;;;;;N;;;;;
+130E5;EGYPTIAN HIEROGLYPH E017;Lo;0;L;;;;;N;;;;;
+130E6;EGYPTIAN HIEROGLYPH E017A;Lo;0;L;;;;;N;;;;;
+130E7;EGYPTIAN HIEROGLYPH E018;Lo;0;L;;;;;N;;;;;
+130E8;EGYPTIAN HIEROGLYPH E019;Lo;0;L;;;;;N;;;;;
+130E9;EGYPTIAN HIEROGLYPH E020;Lo;0;L;;;;;N;;;;;
+130EA;EGYPTIAN HIEROGLYPH E020A;Lo;0;L;;;;;N;;;;;
+130EB;EGYPTIAN HIEROGLYPH E021;Lo;0;L;;;;;N;;;;;
+130EC;EGYPTIAN HIEROGLYPH E022;Lo;0;L;;;;;N;;;;;
+130ED;EGYPTIAN HIEROGLYPH E023;Lo;0;L;;;;;N;;;;;
+130EE;EGYPTIAN HIEROGLYPH E024;Lo;0;L;;;;;N;;;;;
+130EF;EGYPTIAN HIEROGLYPH E025;Lo;0;L;;;;;N;;;;;
+130F0;EGYPTIAN HIEROGLYPH E026;Lo;0;L;;;;;N;;;;;
+130F1;EGYPTIAN HIEROGLYPH E027;Lo;0;L;;;;;N;;;;;
+130F2;EGYPTIAN HIEROGLYPH E028;Lo;0;L;;;;;N;;;;;
+130F3;EGYPTIAN HIEROGLYPH E028A;Lo;0;L;;;;;N;;;;;
+130F4;EGYPTIAN HIEROGLYPH E029;Lo;0;L;;;;;N;;;;;
+130F5;EGYPTIAN HIEROGLYPH E030;Lo;0;L;;;;;N;;;;;
+130F6;EGYPTIAN HIEROGLYPH E031;Lo;0;L;;;;;N;;;;;
+130F7;EGYPTIAN HIEROGLYPH E032;Lo;0;L;;;;;N;;;;;
+130F8;EGYPTIAN HIEROGLYPH E033;Lo;0;L;;;;;N;;;;;
+130F9;EGYPTIAN HIEROGLYPH E034;Lo;0;L;;;;;N;;;;;
+130FA;EGYPTIAN HIEROGLYPH E034A;Lo;0;L;;;;;N;;;;;
+130FB;EGYPTIAN HIEROGLYPH E036;Lo;0;L;;;;;N;;;;;
+130FC;EGYPTIAN HIEROGLYPH E037;Lo;0;L;;;;;N;;;;;
+130FD;EGYPTIAN HIEROGLYPH E038;Lo;0;L;;;;;N;;;;;
+130FE;EGYPTIAN HIEROGLYPH F001;Lo;0;L;;;;;N;;;;;
+130FF;EGYPTIAN HIEROGLYPH F001A;Lo;0;L;;;;;N;;;;;
+13100;EGYPTIAN HIEROGLYPH F002;Lo;0;L;;;;;N;;;;;
+13101;EGYPTIAN HIEROGLYPH F003;Lo;0;L;;;;;N;;;;;
+13102;EGYPTIAN HIEROGLYPH F004;Lo;0;L;;;;;N;;;;;
+13103;EGYPTIAN HIEROGLYPH F005;Lo;0;L;;;;;N;;;;;
+13104;EGYPTIAN HIEROGLYPH F006;Lo;0;L;;;;;N;;;;;
+13105;EGYPTIAN HIEROGLYPH F007;Lo;0;L;;;;;N;;;;;
+13106;EGYPTIAN HIEROGLYPH F008;Lo;0;L;;;;;N;;;;;
+13107;EGYPTIAN HIEROGLYPH F009;Lo;0;L;;;;;N;;;;;
+13108;EGYPTIAN HIEROGLYPH F010;Lo;0;L;;;;;N;;;;;
+13109;EGYPTIAN HIEROGLYPH F011;Lo;0;L;;;;;N;;;;;
+1310A;EGYPTIAN HIEROGLYPH F012;Lo;0;L;;;;;N;;;;;
+1310B;EGYPTIAN HIEROGLYPH F013;Lo;0;L;;;;;N;;;;;
+1310C;EGYPTIAN HIEROGLYPH F013A;Lo;0;L;;;;;N;;;;;
+1310D;EGYPTIAN HIEROGLYPH F014;Lo;0;L;;;;;N;;;;;
+1310E;EGYPTIAN HIEROGLYPH F015;Lo;0;L;;;;;N;;;;;
+1310F;EGYPTIAN HIEROGLYPH F016;Lo;0;L;;;;;N;;;;;
+13110;EGYPTIAN HIEROGLYPH F017;Lo;0;L;;;;;N;;;;;
+13111;EGYPTIAN HIEROGLYPH F018;Lo;0;L;;;;;N;;;;;
+13112;EGYPTIAN HIEROGLYPH F019;Lo;0;L;;;;;N;;;;;
+13113;EGYPTIAN HIEROGLYPH F020;Lo;0;L;;;;;N;;;;;
+13114;EGYPTIAN HIEROGLYPH F021;Lo;0;L;;;;;N;;;;;
+13115;EGYPTIAN HIEROGLYPH F021A;Lo;0;L;;;;;N;;;;;
+13116;EGYPTIAN HIEROGLYPH F022;Lo;0;L;;;;;N;;;;;
+13117;EGYPTIAN HIEROGLYPH F023;Lo;0;L;;;;;N;;;;;
+13118;EGYPTIAN HIEROGLYPH F024;Lo;0;L;;;;;N;;;;;
+13119;EGYPTIAN HIEROGLYPH F025;Lo;0;L;;;;;N;;;;;
+1311A;EGYPTIAN HIEROGLYPH F026;Lo;0;L;;;;;N;;;;;
+1311B;EGYPTIAN HIEROGLYPH F027;Lo;0;L;;;;;N;;;;;
+1311C;EGYPTIAN HIEROGLYPH F028;Lo;0;L;;;;;N;;;;;
+1311D;EGYPTIAN HIEROGLYPH F029;Lo;0;L;;;;;N;;;;;
+1311E;EGYPTIAN HIEROGLYPH F030;Lo;0;L;;;;;N;;;;;
+1311F;EGYPTIAN HIEROGLYPH F031;Lo;0;L;;;;;N;;;;;
+13120;EGYPTIAN HIEROGLYPH F031A;Lo;0;L;;;;;N;;;;;
+13121;EGYPTIAN HIEROGLYPH F032;Lo;0;L;;;;;N;;;;;
+13122;EGYPTIAN HIEROGLYPH F033;Lo;0;L;;;;;N;;;;;
+13123;EGYPTIAN HIEROGLYPH F034;Lo;0;L;;;;;N;;;;;
+13124;EGYPTIAN HIEROGLYPH F035;Lo;0;L;;;;;N;;;;;
+13125;EGYPTIAN HIEROGLYPH F036;Lo;0;L;;;;;N;;;;;
+13126;EGYPTIAN HIEROGLYPH F037;Lo;0;L;;;;;N;;;;;
+13127;EGYPTIAN HIEROGLYPH F037A;Lo;0;L;;;;;N;;;;;
+13128;EGYPTIAN HIEROGLYPH F038;Lo;0;L;;;;;N;;;;;
+13129;EGYPTIAN HIEROGLYPH F038A;Lo;0;L;;;;;N;;;;;
+1312A;EGYPTIAN HIEROGLYPH F039;Lo;0;L;;;;;N;;;;;
+1312B;EGYPTIAN HIEROGLYPH F040;Lo;0;L;;;;;N;;;;;
+1312C;EGYPTIAN HIEROGLYPH F041;Lo;0;L;;;;;N;;;;;
+1312D;EGYPTIAN HIEROGLYPH F042;Lo;0;L;;;;;N;;;;;
+1312E;EGYPTIAN HIEROGLYPH F043;Lo;0;L;;;;;N;;;;;
+1312F;EGYPTIAN HIEROGLYPH F044;Lo;0;L;;;;;N;;;;;
+13130;EGYPTIAN HIEROGLYPH F045;Lo;0;L;;;;;N;;;;;
+13131;EGYPTIAN HIEROGLYPH F045A;Lo;0;L;;;;;N;;;;;
+13132;EGYPTIAN HIEROGLYPH F046;Lo;0;L;;;;;N;;;;;
+13133;EGYPTIAN HIEROGLYPH F046A;Lo;0;L;;;;;N;;;;;
+13134;EGYPTIAN HIEROGLYPH F047;Lo;0;L;;;;;N;;;;;
+13135;EGYPTIAN HIEROGLYPH F047A;Lo;0;L;;;;;N;;;;;
+13136;EGYPTIAN HIEROGLYPH F048;Lo;0;L;;;;;N;;;;;
+13137;EGYPTIAN HIEROGLYPH F049;Lo;0;L;;;;;N;;;;;
+13138;EGYPTIAN HIEROGLYPH F050;Lo;0;L;;;;;N;;;;;
+13139;EGYPTIAN HIEROGLYPH F051;Lo;0;L;;;;;N;;;;;
+1313A;EGYPTIAN HIEROGLYPH F051A;Lo;0;L;;;;;N;;;;;
+1313B;EGYPTIAN HIEROGLYPH F051B;Lo;0;L;;;;;N;;;;;
+1313C;EGYPTIAN HIEROGLYPH F051C;Lo;0;L;;;;;N;;;;;
+1313D;EGYPTIAN HIEROGLYPH F052;Lo;0;L;;;;;N;;;;;
+1313E;EGYPTIAN HIEROGLYPH F053;Lo;0;L;;;;;N;;;;;
+1313F;EGYPTIAN HIEROGLYPH G001;Lo;0;L;;;;;N;;;;;
+13140;EGYPTIAN HIEROGLYPH G002;Lo;0;L;;;;;N;;;;;
+13141;EGYPTIAN HIEROGLYPH G003;Lo;0;L;;;;;N;;;;;
+13142;EGYPTIAN HIEROGLYPH G004;Lo;0;L;;;;;N;;;;;
+13143;EGYPTIAN HIEROGLYPH G005;Lo;0;L;;;;;N;;;;;
+13144;EGYPTIAN HIEROGLYPH G006;Lo;0;L;;;;;N;;;;;
+13145;EGYPTIAN HIEROGLYPH G006A;Lo;0;L;;;;;N;;;;;
+13146;EGYPTIAN HIEROGLYPH G007;Lo;0;L;;;;;N;;;;;
+13147;EGYPTIAN HIEROGLYPH G007A;Lo;0;L;;;;;N;;;;;
+13148;EGYPTIAN HIEROGLYPH G007B;Lo;0;L;;;;;N;;;;;
+13149;EGYPTIAN HIEROGLYPH G008;Lo;0;L;;;;;N;;;;;
+1314A;EGYPTIAN HIEROGLYPH G009;Lo;0;L;;;;;N;;;;;
+1314B;EGYPTIAN HIEROGLYPH G010;Lo;0;L;;;;;N;;;;;
+1314C;EGYPTIAN HIEROGLYPH G011;Lo;0;L;;;;;N;;;;;
+1314D;EGYPTIAN HIEROGLYPH G011A;Lo;0;L;;;;;N;;;;;
+1314E;EGYPTIAN HIEROGLYPH G012;Lo;0;L;;;;;N;;;;;
+1314F;EGYPTIAN HIEROGLYPH G013;Lo;0;L;;;;;N;;;;;
+13150;EGYPTIAN HIEROGLYPH G014;Lo;0;L;;;;;N;;;;;
+13151;EGYPTIAN HIEROGLYPH G015;Lo;0;L;;;;;N;;;;;
+13152;EGYPTIAN HIEROGLYPH G016;Lo;0;L;;;;;N;;;;;
+13153;EGYPTIAN HIEROGLYPH G017;Lo;0;L;;;;;N;;;;;
+13154;EGYPTIAN HIEROGLYPH G018;Lo;0;L;;;;;N;;;;;
+13155;EGYPTIAN HIEROGLYPH G019;Lo;0;L;;;;;N;;;;;
+13156;EGYPTIAN HIEROGLYPH G020;Lo;0;L;;;;;N;;;;;
+13157;EGYPTIAN HIEROGLYPH G020A;Lo;0;L;;;;;N;;;;;
+13158;EGYPTIAN HIEROGLYPH G021;Lo;0;L;;;;;N;;;;;
+13159;EGYPTIAN HIEROGLYPH G022;Lo;0;L;;;;;N;;;;;
+1315A;EGYPTIAN HIEROGLYPH G023;Lo;0;L;;;;;N;;;;;
+1315B;EGYPTIAN HIEROGLYPH G024;Lo;0;L;;;;;N;;;;;
+1315C;EGYPTIAN HIEROGLYPH G025;Lo;0;L;;;;;N;;;;;
+1315D;EGYPTIAN HIEROGLYPH G026;Lo;0;L;;;;;N;;;;;
+1315E;EGYPTIAN HIEROGLYPH G026A;Lo;0;L;;;;;N;;;;;
+1315F;EGYPTIAN HIEROGLYPH G027;Lo;0;L;;;;;N;;;;;
+13160;EGYPTIAN HIEROGLYPH G028;Lo;0;L;;;;;N;;;;;
+13161;EGYPTIAN HIEROGLYPH G029;Lo;0;L;;;;;N;;;;;
+13162;EGYPTIAN HIEROGLYPH G030;Lo;0;L;;;;;N;;;;;
+13163;EGYPTIAN HIEROGLYPH G031;Lo;0;L;;;;;N;;;;;
+13164;EGYPTIAN HIEROGLYPH G032;Lo;0;L;;;;;N;;;;;
+13165;EGYPTIAN HIEROGLYPH G033;Lo;0;L;;;;;N;;;;;
+13166;EGYPTIAN HIEROGLYPH G034;Lo;0;L;;;;;N;;;;;
+13167;EGYPTIAN HIEROGLYPH G035;Lo;0;L;;;;;N;;;;;
+13168;EGYPTIAN HIEROGLYPH G036;Lo;0;L;;;;;N;;;;;
+13169;EGYPTIAN HIEROGLYPH G036A;Lo;0;L;;;;;N;;;;;
+1316A;EGYPTIAN HIEROGLYPH G037;Lo;0;L;;;;;N;;;;;
+1316B;EGYPTIAN HIEROGLYPH G037A;Lo;0;L;;;;;N;;;;;
+1316C;EGYPTIAN HIEROGLYPH G038;Lo;0;L;;;;;N;;;;;
+1316D;EGYPTIAN HIEROGLYPH G039;Lo;0;L;;;;;N;;;;;
+1316E;EGYPTIAN HIEROGLYPH G040;Lo;0;L;;;;;N;;;;;
+1316F;EGYPTIAN HIEROGLYPH G041;Lo;0;L;;;;;N;;;;;
+13170;EGYPTIAN HIEROGLYPH G042;Lo;0;L;;;;;N;;;;;
+13171;EGYPTIAN HIEROGLYPH G043;Lo;0;L;;;;;N;;;;;
+13172;EGYPTIAN HIEROGLYPH G043A;Lo;0;L;;;;;N;;;;;
+13173;EGYPTIAN HIEROGLYPH G044;Lo;0;L;;;;;N;;;;;
+13174;EGYPTIAN HIEROGLYPH G045;Lo;0;L;;;;;N;;;;;
+13175;EGYPTIAN HIEROGLYPH G045A;Lo;0;L;;;;;N;;;;;
+13176;EGYPTIAN HIEROGLYPH G046;Lo;0;L;;;;;N;;;;;
+13177;EGYPTIAN HIEROGLYPH G047;Lo;0;L;;;;;N;;;;;
+13178;EGYPTIAN HIEROGLYPH G048;Lo;0;L;;;;;N;;;;;
+13179;EGYPTIAN HIEROGLYPH G049;Lo;0;L;;;;;N;;;;;
+1317A;EGYPTIAN HIEROGLYPH G050;Lo;0;L;;;;;N;;;;;
+1317B;EGYPTIAN HIEROGLYPH G051;Lo;0;L;;;;;N;;;;;
+1317C;EGYPTIAN HIEROGLYPH G052;Lo;0;L;;;;;N;;;;;
+1317D;EGYPTIAN HIEROGLYPH G053;Lo;0;L;;;;;N;;;;;
+1317E;EGYPTIAN HIEROGLYPH G054;Lo;0;L;;;;;N;;;;;
+1317F;EGYPTIAN HIEROGLYPH H001;Lo;0;L;;;;;N;;;;;
+13180;EGYPTIAN HIEROGLYPH H002;Lo;0;L;;;;;N;;;;;
+13181;EGYPTIAN HIEROGLYPH H003;Lo;0;L;;;;;N;;;;;
+13182;EGYPTIAN HIEROGLYPH H004;Lo;0;L;;;;;N;;;;;
+13183;EGYPTIAN HIEROGLYPH H005;Lo;0;L;;;;;N;;;;;
+13184;EGYPTIAN HIEROGLYPH H006;Lo;0;L;;;;;N;;;;;
+13185;EGYPTIAN HIEROGLYPH H006A;Lo;0;L;;;;;N;;;;;
+13186;EGYPTIAN HIEROGLYPH H007;Lo;0;L;;;;;N;;;;;
+13187;EGYPTIAN HIEROGLYPH H008;Lo;0;L;;;;;N;;;;;
+13188;EGYPTIAN HIEROGLYPH I001;Lo;0;L;;;;;N;;;;;
+13189;EGYPTIAN HIEROGLYPH I002;Lo;0;L;;;;;N;;;;;
+1318A;EGYPTIAN HIEROGLYPH I003;Lo;0;L;;;;;N;;;;;
+1318B;EGYPTIAN HIEROGLYPH I004;Lo;0;L;;;;;N;;;;;
+1318C;EGYPTIAN HIEROGLYPH I005;Lo;0;L;;;;;N;;;;;
+1318D;EGYPTIAN HIEROGLYPH I005A;Lo;0;L;;;;;N;;;;;
+1318E;EGYPTIAN HIEROGLYPH I006;Lo;0;L;;;;;N;;;;;
+1318F;EGYPTIAN HIEROGLYPH I007;Lo;0;L;;;;;N;;;;;
+13190;EGYPTIAN HIEROGLYPH I008;Lo;0;L;;;;;N;;;;;
+13191;EGYPTIAN HIEROGLYPH I009;Lo;0;L;;;;;N;;;;;
+13192;EGYPTIAN HIEROGLYPH I009A;Lo;0;L;;;;;N;;;;;
+13193;EGYPTIAN HIEROGLYPH I010;Lo;0;L;;;;;N;;;;;
+13194;EGYPTIAN HIEROGLYPH I010A;Lo;0;L;;;;;N;;;;;
+13195;EGYPTIAN HIEROGLYPH I011;Lo;0;L;;;;;N;;;;;
+13196;EGYPTIAN HIEROGLYPH I011A;Lo;0;L;;;;;N;;;;;
+13197;EGYPTIAN HIEROGLYPH I012;Lo;0;L;;;;;N;;;;;
+13198;EGYPTIAN HIEROGLYPH I013;Lo;0;L;;;;;N;;;;;
+13199;EGYPTIAN HIEROGLYPH I014;Lo;0;L;;;;;N;;;;;
+1319A;EGYPTIAN HIEROGLYPH I015;Lo;0;L;;;;;N;;;;;
+1319B;EGYPTIAN HIEROGLYPH K001;Lo;0;L;;;;;N;;;;;
+1319C;EGYPTIAN HIEROGLYPH K002;Lo;0;L;;;;;N;;;;;
+1319D;EGYPTIAN HIEROGLYPH K003;Lo;0;L;;;;;N;;;;;
+1319E;EGYPTIAN HIEROGLYPH K004;Lo;0;L;;;;;N;;;;;
+1319F;EGYPTIAN HIEROGLYPH K005;Lo;0;L;;;;;N;;;;;
+131A0;EGYPTIAN HIEROGLYPH K006;Lo;0;L;;;;;N;;;;;
+131A1;EGYPTIAN HIEROGLYPH K007;Lo;0;L;;;;;N;;;;;
+131A2;EGYPTIAN HIEROGLYPH K008;Lo;0;L;;;;;N;;;;;
+131A3;EGYPTIAN HIEROGLYPH L001;Lo;0;L;;;;;N;;;;;
+131A4;EGYPTIAN HIEROGLYPH L002;Lo;0;L;;;;;N;;;;;
+131A5;EGYPTIAN HIEROGLYPH L002A;Lo;0;L;;;;;N;;;;;
+131A6;EGYPTIAN HIEROGLYPH L003;Lo;0;L;;;;;N;;;;;
+131A7;EGYPTIAN HIEROGLYPH L004;Lo;0;L;;;;;N;;;;;
+131A8;EGYPTIAN HIEROGLYPH L005;Lo;0;L;;;;;N;;;;;
+131A9;EGYPTIAN HIEROGLYPH L006;Lo;0;L;;;;;N;;;;;
+131AA;EGYPTIAN HIEROGLYPH L006A;Lo;0;L;;;;;N;;;;;
+131AB;EGYPTIAN HIEROGLYPH L007;Lo;0;L;;;;;N;;;;;
+131AC;EGYPTIAN HIEROGLYPH L008;Lo;0;L;;;;;N;;;;;
+131AD;EGYPTIAN HIEROGLYPH M001;Lo;0;L;;;;;N;;;;;
+131AE;EGYPTIAN HIEROGLYPH M001A;Lo;0;L;;;;;N;;;;;
+131AF;EGYPTIAN HIEROGLYPH M001B;Lo;0;L;;;;;N;;;;;
+131B0;EGYPTIAN HIEROGLYPH M002;Lo;0;L;;;;;N;;;;;
+131B1;EGYPTIAN HIEROGLYPH M003;Lo;0;L;;;;;N;;;;;
+131B2;EGYPTIAN HIEROGLYPH M003A;Lo;0;L;;;;;N;;;;;
+131B3;EGYPTIAN HIEROGLYPH M004;Lo;0;L;;;;;N;;;;;
+131B4;EGYPTIAN HIEROGLYPH M005;Lo;0;L;;;;;N;;;;;
+131B5;EGYPTIAN HIEROGLYPH M006;Lo;0;L;;;;;N;;;;;
+131B6;EGYPTIAN HIEROGLYPH M007;Lo;0;L;;;;;N;;;;;
+131B7;EGYPTIAN HIEROGLYPH M008;Lo;0;L;;;;;N;;;;;
+131B8;EGYPTIAN HIEROGLYPH M009;Lo;0;L;;;;;N;;;;;
+131B9;EGYPTIAN HIEROGLYPH M010;Lo;0;L;;;;;N;;;;;
+131BA;EGYPTIAN HIEROGLYPH M010A;Lo;0;L;;;;;N;;;;;
+131BB;EGYPTIAN HIEROGLYPH M011;Lo;0;L;;;;;N;;;;;
+131BC;EGYPTIAN HIEROGLYPH M012;Lo;0;L;;;;;N;;;;;
+131BD;EGYPTIAN HIEROGLYPH M012A;Lo;0;L;;;;;N;;;;;
+131BE;EGYPTIAN HIEROGLYPH M012B;Lo;0;L;;;;;N;;;;;
+131BF;EGYPTIAN HIEROGLYPH M012C;Lo;0;L;;;;;N;;;;;
+131C0;EGYPTIAN HIEROGLYPH M012D;Lo;0;L;;;;;N;;;;;
+131C1;EGYPTIAN HIEROGLYPH M012E;Lo;0;L;;;;;N;;;;;
+131C2;EGYPTIAN HIEROGLYPH M012F;Lo;0;L;;;;;N;;;;;
+131C3;EGYPTIAN HIEROGLYPH M012G;Lo;0;L;;;;;N;;;;;
+131C4;EGYPTIAN HIEROGLYPH M012H;Lo;0;L;;;;;N;;;;;
+131C5;EGYPTIAN HIEROGLYPH M013;Lo;0;L;;;;;N;;;;;
+131C6;EGYPTIAN HIEROGLYPH M014;Lo;0;L;;;;;N;;;;;
+131C7;EGYPTIAN HIEROGLYPH M015;Lo;0;L;;;;;N;;;;;
+131C8;EGYPTIAN HIEROGLYPH M015A;Lo;0;L;;;;;N;;;;;
+131C9;EGYPTIAN HIEROGLYPH M016;Lo;0;L;;;;;N;;;;;
+131CA;EGYPTIAN HIEROGLYPH M016A;Lo;0;L;;;;;N;;;;;
+131CB;EGYPTIAN HIEROGLYPH M017;Lo;0;L;;;;;N;;;;;
+131CC;EGYPTIAN HIEROGLYPH M017A;Lo;0;L;;;;;N;;;;;
+131CD;EGYPTIAN HIEROGLYPH M018;Lo;0;L;;;;;N;;;;;
+131CE;EGYPTIAN HIEROGLYPH M019;Lo;0;L;;;;;N;;;;;
+131CF;EGYPTIAN HIEROGLYPH M020;Lo;0;L;;;;;N;;;;;
+131D0;EGYPTIAN HIEROGLYPH M021;Lo;0;L;;;;;N;;;;;
+131D1;EGYPTIAN HIEROGLYPH M022;Lo;0;L;;;;;N;;;;;
+131D2;EGYPTIAN HIEROGLYPH M022A;Lo;0;L;;;;;N;;;;;
+131D3;EGYPTIAN HIEROGLYPH M023;Lo;0;L;;;;;N;;;;;
+131D4;EGYPTIAN HIEROGLYPH M024;Lo;0;L;;;;;N;;;;;
+131D5;EGYPTIAN HIEROGLYPH M024A;Lo;0;L;;;;;N;;;;;
+131D6;EGYPTIAN HIEROGLYPH M025;Lo;0;L;;;;;N;;;;;
+131D7;EGYPTIAN HIEROGLYPH M026;Lo;0;L;;;;;N;;;;;
+131D8;EGYPTIAN HIEROGLYPH M027;Lo;0;L;;;;;N;;;;;
+131D9;EGYPTIAN HIEROGLYPH M028;Lo;0;L;;;;;N;;;;;
+131DA;EGYPTIAN HIEROGLYPH M028A;Lo;0;L;;;;;N;;;;;
+131DB;EGYPTIAN HIEROGLYPH M029;Lo;0;L;;;;;N;;;;;
+131DC;EGYPTIAN HIEROGLYPH M030;Lo;0;L;;;;;N;;;;;
+131DD;EGYPTIAN HIEROGLYPH M031;Lo;0;L;;;;;N;;;;;
+131DE;EGYPTIAN HIEROGLYPH M031A;Lo;0;L;;;;;N;;;;;
+131DF;EGYPTIAN HIEROGLYPH M032;Lo;0;L;;;;;N;;;;;
+131E0;EGYPTIAN HIEROGLYPH M033;Lo;0;L;;;;;N;;;;;
+131E1;EGYPTIAN HIEROGLYPH M033A;Lo;0;L;;;;;N;;;;;
+131E2;EGYPTIAN HIEROGLYPH M033B;Lo;0;L;;;;;N;;;;;
+131E3;EGYPTIAN HIEROGLYPH M034;Lo;0;L;;;;;N;;;;;
+131E4;EGYPTIAN HIEROGLYPH M035;Lo;0;L;;;;;N;;;;;
+131E5;EGYPTIAN HIEROGLYPH M036;Lo;0;L;;;;;N;;;;;
+131E6;EGYPTIAN HIEROGLYPH M037;Lo;0;L;;;;;N;;;;;
+131E7;EGYPTIAN HIEROGLYPH M038;Lo;0;L;;;;;N;;;;;
+131E8;EGYPTIAN HIEROGLYPH M039;Lo;0;L;;;;;N;;;;;
+131E9;EGYPTIAN HIEROGLYPH M040;Lo;0;L;;;;;N;;;;;
+131EA;EGYPTIAN HIEROGLYPH M040A;Lo;0;L;;;;;N;;;;;
+131EB;EGYPTIAN HIEROGLYPH M041;Lo;0;L;;;;;N;;;;;
+131EC;EGYPTIAN HIEROGLYPH M042;Lo;0;L;;;;;N;;;;;
+131ED;EGYPTIAN HIEROGLYPH M043;Lo;0;L;;;;;N;;;;;
+131EE;EGYPTIAN HIEROGLYPH M044;Lo;0;L;;;;;N;;;;;
+131EF;EGYPTIAN HIEROGLYPH N001;Lo;0;L;;;;;N;;;;;
+131F0;EGYPTIAN HIEROGLYPH N002;Lo;0;L;;;;;N;;;;;
+131F1;EGYPTIAN HIEROGLYPH N003;Lo;0;L;;;;;N;;;;;
+131F2;EGYPTIAN HIEROGLYPH N004;Lo;0;L;;;;;N;;;;;
+131F3;EGYPTIAN HIEROGLYPH N005;Lo;0;L;;;;;N;;;;;
+131F4;EGYPTIAN HIEROGLYPH N006;Lo;0;L;;;;;N;;;;;
+131F5;EGYPTIAN HIEROGLYPH N007;Lo;0;L;;;;;N;;;;;
+131F6;EGYPTIAN HIEROGLYPH N008;Lo;0;L;;;;;N;;;;;
+131F7;EGYPTIAN HIEROGLYPH N009;Lo;0;L;;;;;N;;;;;
+131F8;EGYPTIAN HIEROGLYPH N010;Lo;0;L;;;;;N;;;;;
+131F9;EGYPTIAN HIEROGLYPH N011;Lo;0;L;;;;;N;;;;;
+131FA;EGYPTIAN HIEROGLYPH N012;Lo;0;L;;;;;N;;;;;
+131FB;EGYPTIAN HIEROGLYPH N013;Lo;0;L;;;;;N;;;;;
+131FC;EGYPTIAN HIEROGLYPH N014;Lo;0;L;;;;;N;;;;;
+131FD;EGYPTIAN HIEROGLYPH N015;Lo;0;L;;;;;N;;;;;
+131FE;EGYPTIAN HIEROGLYPH N016;Lo;0;L;;;;;N;;;;;
+131FF;EGYPTIAN HIEROGLYPH N017;Lo;0;L;;;;;N;;;;;
+13200;EGYPTIAN HIEROGLYPH N018;Lo;0;L;;;;;N;;;;;
+13201;EGYPTIAN HIEROGLYPH N018A;Lo;0;L;;;;;N;;;;;
+13202;EGYPTIAN HIEROGLYPH N018B;Lo;0;L;;;;;N;;;;;
+13203;EGYPTIAN HIEROGLYPH N019;Lo;0;L;;;;;N;;;;;
+13204;EGYPTIAN HIEROGLYPH N020;Lo;0;L;;;;;N;;;;;
+13205;EGYPTIAN HIEROGLYPH N021;Lo;0;L;;;;;N;;;;;
+13206;EGYPTIAN HIEROGLYPH N022;Lo;0;L;;;;;N;;;;;
+13207;EGYPTIAN HIEROGLYPH N023;Lo;0;L;;;;;N;;;;;
+13208;EGYPTIAN HIEROGLYPH N024;Lo;0;L;;;;;N;;;;;
+13209;EGYPTIAN HIEROGLYPH N025;Lo;0;L;;;;;N;;;;;
+1320A;EGYPTIAN HIEROGLYPH N025A;Lo;0;L;;;;;N;;;;;
+1320B;EGYPTIAN HIEROGLYPH N026;Lo;0;L;;;;;N;;;;;
+1320C;EGYPTIAN HIEROGLYPH N027;Lo;0;L;;;;;N;;;;;
+1320D;EGYPTIAN HIEROGLYPH N028;Lo;0;L;;;;;N;;;;;
+1320E;EGYPTIAN HIEROGLYPH N029;Lo;0;L;;;;;N;;;;;
+1320F;EGYPTIAN HIEROGLYPH N030;Lo;0;L;;;;;N;;;;;
+13210;EGYPTIAN HIEROGLYPH N031;Lo;0;L;;;;;N;;;;;
+13211;EGYPTIAN HIEROGLYPH N032;Lo;0;L;;;;;N;;;;;
+13212;EGYPTIAN HIEROGLYPH N033;Lo;0;L;;;;;N;;;;;
+13213;EGYPTIAN HIEROGLYPH N033A;Lo;0;L;;;;;N;;;;;
+13214;EGYPTIAN HIEROGLYPH N034;Lo;0;L;;;;;N;;;;;
+13215;EGYPTIAN HIEROGLYPH N034A;Lo;0;L;;;;;N;;;;;
+13216;EGYPTIAN HIEROGLYPH N035;Lo;0;L;;;;;N;;;;;
+13217;EGYPTIAN HIEROGLYPH N035A;Lo;0;L;;;;;N;;;;;
+13218;EGYPTIAN HIEROGLYPH N036;Lo;0;L;;;;;N;;;;;
+13219;EGYPTIAN HIEROGLYPH N037;Lo;0;L;;;;;N;;;;;
+1321A;EGYPTIAN HIEROGLYPH N037A;Lo;0;L;;;;;N;;;;;
+1321B;EGYPTIAN HIEROGLYPH N038;Lo;0;L;;;;;N;;;;;
+1321C;EGYPTIAN HIEROGLYPH N039;Lo;0;L;;;;;N;;;;;
+1321D;EGYPTIAN HIEROGLYPH N040;Lo;0;L;;;;;N;;;;;
+1321E;EGYPTIAN HIEROGLYPH N041;Lo;0;L;;;;;N;;;;;
+1321F;EGYPTIAN HIEROGLYPH N042;Lo;0;L;;;;;N;;;;;
+13220;EGYPTIAN HIEROGLYPH NL001;Lo;0;L;;;;;N;;;;;
+13221;EGYPTIAN HIEROGLYPH NL002;Lo;0;L;;;;;N;;;;;
+13222;EGYPTIAN HIEROGLYPH NL003;Lo;0;L;;;;;N;;;;;
+13223;EGYPTIAN HIEROGLYPH NL004;Lo;0;L;;;;;N;;;;;
+13224;EGYPTIAN HIEROGLYPH NL005;Lo;0;L;;;;;N;;;;;
+13225;EGYPTIAN HIEROGLYPH NL005A;Lo;0;L;;;;;N;;;;;
+13226;EGYPTIAN HIEROGLYPH NL006;Lo;0;L;;;;;N;;;;;
+13227;EGYPTIAN HIEROGLYPH NL007;Lo;0;L;;;;;N;;;;;
+13228;EGYPTIAN HIEROGLYPH NL008;Lo;0;L;;;;;N;;;;;
+13229;EGYPTIAN HIEROGLYPH NL009;Lo;0;L;;;;;N;;;;;
+1322A;EGYPTIAN HIEROGLYPH NL010;Lo;0;L;;;;;N;;;;;
+1322B;EGYPTIAN HIEROGLYPH NL011;Lo;0;L;;;;;N;;;;;
+1322C;EGYPTIAN HIEROGLYPH NL012;Lo;0;L;;;;;N;;;;;
+1322D;EGYPTIAN HIEROGLYPH NL013;Lo;0;L;;;;;N;;;;;
+1322E;EGYPTIAN HIEROGLYPH NL014;Lo;0;L;;;;;N;;;;;
+1322F;EGYPTIAN HIEROGLYPH NL015;Lo;0;L;;;;;N;;;;;
+13230;EGYPTIAN HIEROGLYPH NL016;Lo;0;L;;;;;N;;;;;
+13231;EGYPTIAN HIEROGLYPH NL017;Lo;0;L;;;;;N;;;;;
+13232;EGYPTIAN HIEROGLYPH NL017A;Lo;0;L;;;;;N;;;;;
+13233;EGYPTIAN HIEROGLYPH NL018;Lo;0;L;;;;;N;;;;;
+13234;EGYPTIAN HIEROGLYPH NL019;Lo;0;L;;;;;N;;;;;
+13235;EGYPTIAN HIEROGLYPH NL020;Lo;0;L;;;;;N;;;;;
+13236;EGYPTIAN HIEROGLYPH NU001;Lo;0;L;;;;;N;;;;;
+13237;EGYPTIAN HIEROGLYPH NU002;Lo;0;L;;;;;N;;;;;
+13238;EGYPTIAN HIEROGLYPH NU003;Lo;0;L;;;;;N;;;;;
+13239;EGYPTIAN HIEROGLYPH NU004;Lo;0;L;;;;;N;;;;;
+1323A;EGYPTIAN HIEROGLYPH NU005;Lo;0;L;;;;;N;;;;;
+1323B;EGYPTIAN HIEROGLYPH NU006;Lo;0;L;;;;;N;;;;;
+1323C;EGYPTIAN HIEROGLYPH NU007;Lo;0;L;;;;;N;;;;;
+1323D;EGYPTIAN HIEROGLYPH NU008;Lo;0;L;;;;;N;;;;;
+1323E;EGYPTIAN HIEROGLYPH NU009;Lo;0;L;;;;;N;;;;;
+1323F;EGYPTIAN HIEROGLYPH NU010;Lo;0;L;;;;;N;;;;;
+13240;EGYPTIAN HIEROGLYPH NU010A;Lo;0;L;;;;;N;;;;;
+13241;EGYPTIAN HIEROGLYPH NU011;Lo;0;L;;;;;N;;;;;
+13242;EGYPTIAN HIEROGLYPH NU011A;Lo;0;L;;;;;N;;;;;
+13243;EGYPTIAN HIEROGLYPH NU012;Lo;0;L;;;;;N;;;;;
+13244;EGYPTIAN HIEROGLYPH NU013;Lo;0;L;;;;;N;;;;;
+13245;EGYPTIAN HIEROGLYPH NU014;Lo;0;L;;;;;N;;;;;
+13246;EGYPTIAN HIEROGLYPH NU015;Lo;0;L;;;;;N;;;;;
+13247;EGYPTIAN HIEROGLYPH NU016;Lo;0;L;;;;;N;;;;;
+13248;EGYPTIAN HIEROGLYPH NU017;Lo;0;L;;;;;N;;;;;
+13249;EGYPTIAN HIEROGLYPH NU018;Lo;0;L;;;;;N;;;;;
+1324A;EGYPTIAN HIEROGLYPH NU018A;Lo;0;L;;;;;N;;;;;
+1324B;EGYPTIAN HIEROGLYPH NU019;Lo;0;L;;;;;N;;;;;
+1324C;EGYPTIAN HIEROGLYPH NU020;Lo;0;L;;;;;N;;;;;
+1324D;EGYPTIAN HIEROGLYPH NU021;Lo;0;L;;;;;N;;;;;
+1324E;EGYPTIAN HIEROGLYPH NU022;Lo;0;L;;;;;N;;;;;
+1324F;EGYPTIAN HIEROGLYPH NU022A;Lo;0;L;;;;;N;;;;;
+13250;EGYPTIAN HIEROGLYPH O001;Lo;0;L;;;;;N;;;;;
+13251;EGYPTIAN HIEROGLYPH O001A;Lo;0;L;;;;;N;;;;;
+13252;EGYPTIAN HIEROGLYPH O002;Lo;0;L;;;;;N;;;;;
+13253;EGYPTIAN HIEROGLYPH O003;Lo;0;L;;;;;N;;;;;
+13254;EGYPTIAN HIEROGLYPH O004;Lo;0;L;;;;;N;;;;;
+13255;EGYPTIAN HIEROGLYPH O005;Lo;0;L;;;;;N;;;;;
+13256;EGYPTIAN HIEROGLYPH O005A;Lo;0;L;;;;;N;;;;;
+13257;EGYPTIAN HIEROGLYPH O006;Lo;0;L;;;;;N;;;;;
+13258;EGYPTIAN HIEROGLYPH O006A;Lo;0;L;;;;;N;;;;;
+13259;EGYPTIAN HIEROGLYPH O006B;Lo;0;L;;;;;N;;;;;
+1325A;EGYPTIAN HIEROGLYPH O006C;Lo;0;L;;;;;N;;;;;
+1325B;EGYPTIAN HIEROGLYPH O006D;Lo;0;L;;;;;N;;;;;
+1325C;EGYPTIAN HIEROGLYPH O006E;Lo;0;L;;;;;N;;;;;
+1325D;EGYPTIAN HIEROGLYPH O006F;Lo;0;L;;;;;N;;;;;
+1325E;EGYPTIAN HIEROGLYPH O007;Lo;0;L;;;;;N;;;;;
+1325F;EGYPTIAN HIEROGLYPH O008;Lo;0;L;;;;;N;;;;;
+13260;EGYPTIAN HIEROGLYPH O009;Lo;0;L;;;;;N;;;;;
+13261;EGYPTIAN HIEROGLYPH O010;Lo;0;L;;;;;N;;;;;
+13262;EGYPTIAN HIEROGLYPH O010A;Lo;0;L;;;;;N;;;;;
+13263;EGYPTIAN HIEROGLYPH O010B;Lo;0;L;;;;;N;;;;;
+13264;EGYPTIAN HIEROGLYPH O010C;Lo;0;L;;;;;N;;;;;
+13265;EGYPTIAN HIEROGLYPH O011;Lo;0;L;;;;;N;;;;;
+13266;EGYPTIAN HIEROGLYPH O012;Lo;0;L;;;;;N;;;;;
+13267;EGYPTIAN HIEROGLYPH O013;Lo;0;L;;;;;N;;;;;
+13268;EGYPTIAN HIEROGLYPH O014;Lo;0;L;;;;;N;;;;;
+13269;EGYPTIAN HIEROGLYPH O015;Lo;0;L;;;;;N;;;;;
+1326A;EGYPTIAN HIEROGLYPH O016;Lo;0;L;;;;;N;;;;;
+1326B;EGYPTIAN HIEROGLYPH O017;Lo;0;L;;;;;N;;;;;
+1326C;EGYPTIAN HIEROGLYPH O018;Lo;0;L;;;;;N;;;;;
+1326D;EGYPTIAN HIEROGLYPH O019;Lo;0;L;;;;;N;;;;;
+1326E;EGYPTIAN HIEROGLYPH O019A;Lo;0;L;;;;;N;;;;;
+1326F;EGYPTIAN HIEROGLYPH O020;Lo;0;L;;;;;N;;;;;
+13270;EGYPTIAN HIEROGLYPH O020A;Lo;0;L;;;;;N;;;;;
+13271;EGYPTIAN HIEROGLYPH O021;Lo;0;L;;;;;N;;;;;
+13272;EGYPTIAN HIEROGLYPH O022;Lo;0;L;;;;;N;;;;;
+13273;EGYPTIAN HIEROGLYPH O023;Lo;0;L;;;;;N;;;;;
+13274;EGYPTIAN HIEROGLYPH O024;Lo;0;L;;;;;N;;;;;
+13275;EGYPTIAN HIEROGLYPH O024A;Lo;0;L;;;;;N;;;;;
+13276;EGYPTIAN HIEROGLYPH O025;Lo;0;L;;;;;N;;;;;
+13277;EGYPTIAN HIEROGLYPH O025A;Lo;0;L;;;;;N;;;;;
+13278;EGYPTIAN HIEROGLYPH O026;Lo;0;L;;;;;N;;;;;
+13279;EGYPTIAN HIEROGLYPH O027;Lo;0;L;;;;;N;;;;;
+1327A;EGYPTIAN HIEROGLYPH O028;Lo;0;L;;;;;N;;;;;
+1327B;EGYPTIAN HIEROGLYPH O029;Lo;0;L;;;;;N;;;;;
+1327C;EGYPTIAN HIEROGLYPH O029A;Lo;0;L;;;;;N;;;;;
+1327D;EGYPTIAN HIEROGLYPH O030;Lo;0;L;;;;;N;;;;;
+1327E;EGYPTIAN HIEROGLYPH O030A;Lo;0;L;;;;;N;;;;;
+1327F;EGYPTIAN HIEROGLYPH O031;Lo;0;L;;;;;N;;;;;
+13280;EGYPTIAN HIEROGLYPH O032;Lo;0;L;;;;;N;;;;;
+13281;EGYPTIAN HIEROGLYPH O033;Lo;0;L;;;;;N;;;;;
+13282;EGYPTIAN HIEROGLYPH O033A;Lo;0;L;;;;;N;;;;;
+13283;EGYPTIAN HIEROGLYPH O034;Lo;0;L;;;;;N;;;;;
+13284;EGYPTIAN HIEROGLYPH O035;Lo;0;L;;;;;N;;;;;
+13285;EGYPTIAN HIEROGLYPH O036;Lo;0;L;;;;;N;;;;;
+13286;EGYPTIAN HIEROGLYPH O036A;Lo;0;L;;;;;N;;;;;
+13287;EGYPTIAN HIEROGLYPH O036B;Lo;0;L;;;;;N;;;;;
+13288;EGYPTIAN HIEROGLYPH O036C;Lo;0;L;;;;;N;;;;;
+13289;EGYPTIAN HIEROGLYPH O036D;Lo;0;L;;;;;N;;;;;
+1328A;EGYPTIAN HIEROGLYPH O037;Lo;0;L;;;;;N;;;;;
+1328B;EGYPTIAN HIEROGLYPH O038;Lo;0;L;;;;;N;;;;;
+1328C;EGYPTIAN HIEROGLYPH O039;Lo;0;L;;;;;N;;;;;
+1328D;EGYPTIAN HIEROGLYPH O040;Lo;0;L;;;;;N;;;;;
+1328E;EGYPTIAN HIEROGLYPH O041;Lo;0;L;;;;;N;;;;;
+1328F;EGYPTIAN HIEROGLYPH O042;Lo;0;L;;;;;N;;;;;
+13290;EGYPTIAN HIEROGLYPH O043;Lo;0;L;;;;;N;;;;;
+13291;EGYPTIAN HIEROGLYPH O044;Lo;0;L;;;;;N;;;;;
+13292;EGYPTIAN HIEROGLYPH O045;Lo;0;L;;;;;N;;;;;
+13293;EGYPTIAN HIEROGLYPH O046;Lo;0;L;;;;;N;;;;;
+13294;EGYPTIAN HIEROGLYPH O047;Lo;0;L;;;;;N;;;;;
+13295;EGYPTIAN HIEROGLYPH O048;Lo;0;L;;;;;N;;;;;
+13296;EGYPTIAN HIEROGLYPH O049;Lo;0;L;;;;;N;;;;;
+13297;EGYPTIAN HIEROGLYPH O050;Lo;0;L;;;;;N;;;;;
+13298;EGYPTIAN HIEROGLYPH O050A;Lo;0;L;;;;;N;;;;;
+13299;EGYPTIAN HIEROGLYPH O050B;Lo;0;L;;;;;N;;;;;
+1329A;EGYPTIAN HIEROGLYPH O051;Lo;0;L;;;;;N;;;;;
+1329B;EGYPTIAN HIEROGLYPH P001;Lo;0;L;;;;;N;;;;;
+1329C;EGYPTIAN HIEROGLYPH P001A;Lo;0;L;;;;;N;;;;;
+1329D;EGYPTIAN HIEROGLYPH P002;Lo;0;L;;;;;N;;;;;
+1329E;EGYPTIAN HIEROGLYPH P003;Lo;0;L;;;;;N;;;;;
+1329F;EGYPTIAN HIEROGLYPH P003A;Lo;0;L;;;;;N;;;;;
+132A0;EGYPTIAN HIEROGLYPH P004;Lo;0;L;;;;;N;;;;;
+132A1;EGYPTIAN HIEROGLYPH P005;Lo;0;L;;;;;N;;;;;
+132A2;EGYPTIAN HIEROGLYPH P006;Lo;0;L;;;;;N;;;;;
+132A3;EGYPTIAN HIEROGLYPH P007;Lo;0;L;;;;;N;;;;;
+132A4;EGYPTIAN HIEROGLYPH P008;Lo;0;L;;;;;N;;;;;
+132A5;EGYPTIAN HIEROGLYPH P009;Lo;0;L;;;;;N;;;;;
+132A6;EGYPTIAN HIEROGLYPH P010;Lo;0;L;;;;;N;;;;;
+132A7;EGYPTIAN HIEROGLYPH P011;Lo;0;L;;;;;N;;;;;
+132A8;EGYPTIAN HIEROGLYPH Q001;Lo;0;L;;;;;N;;;;;
+132A9;EGYPTIAN HIEROGLYPH Q002;Lo;0;L;;;;;N;;;;;
+132AA;EGYPTIAN HIEROGLYPH Q003;Lo;0;L;;;;;N;;;;;
+132AB;EGYPTIAN HIEROGLYPH Q004;Lo;0;L;;;;;N;;;;;
+132AC;EGYPTIAN HIEROGLYPH Q005;Lo;0;L;;;;;N;;;;;
+132AD;EGYPTIAN HIEROGLYPH Q006;Lo;0;L;;;;;N;;;;;
+132AE;EGYPTIAN HIEROGLYPH Q007;Lo;0;L;;;;;N;;;;;
+132AF;EGYPTIAN HIEROGLYPH R001;Lo;0;L;;;;;N;;;;;
+132B0;EGYPTIAN HIEROGLYPH R002;Lo;0;L;;;;;N;;;;;
+132B1;EGYPTIAN HIEROGLYPH R002A;Lo;0;L;;;;;N;;;;;
+132B2;EGYPTIAN HIEROGLYPH R003;Lo;0;L;;;;;N;;;;;
+132B3;EGYPTIAN HIEROGLYPH R003A;Lo;0;L;;;;;N;;;;;
+132B4;EGYPTIAN HIEROGLYPH R003B;Lo;0;L;;;;;N;;;;;
+132B5;EGYPTIAN HIEROGLYPH R004;Lo;0;L;;;;;N;;;;;
+132B6;EGYPTIAN HIEROGLYPH R005;Lo;0;L;;;;;N;;;;;
+132B7;EGYPTIAN HIEROGLYPH R006;Lo;0;L;;;;;N;;;;;
+132B8;EGYPTIAN HIEROGLYPH R007;Lo;0;L;;;;;N;;;;;
+132B9;EGYPTIAN HIEROGLYPH R008;Lo;0;L;;;;;N;;;;;
+132BA;EGYPTIAN HIEROGLYPH R009;Lo;0;L;;;;;N;;;;;
+132BB;EGYPTIAN HIEROGLYPH R010;Lo;0;L;;;;;N;;;;;
+132BC;EGYPTIAN HIEROGLYPH R010A;Lo;0;L;;;;;N;;;;;
+132BD;EGYPTIAN HIEROGLYPH R011;Lo;0;L;;;;;N;;;;;
+132BE;EGYPTIAN HIEROGLYPH R012;Lo;0;L;;;;;N;;;;;
+132BF;EGYPTIAN HIEROGLYPH R013;Lo;0;L;;;;;N;;;;;
+132C0;EGYPTIAN HIEROGLYPH R014;Lo;0;L;;;;;N;;;;;
+132C1;EGYPTIAN HIEROGLYPH R015;Lo;0;L;;;;;N;;;;;
+132C2;EGYPTIAN HIEROGLYPH R016;Lo;0;L;;;;;N;;;;;
+132C3;EGYPTIAN HIEROGLYPH R016A;Lo;0;L;;;;;N;;;;;
+132C4;EGYPTIAN HIEROGLYPH R017;Lo;0;L;;;;;N;;;;;
+132C5;EGYPTIAN HIEROGLYPH R018;Lo;0;L;;;;;N;;;;;
+132C6;EGYPTIAN HIEROGLYPH R019;Lo;0;L;;;;;N;;;;;
+132C7;EGYPTIAN HIEROGLYPH R020;Lo;0;L;;;;;N;;;;;
+132C8;EGYPTIAN HIEROGLYPH R021;Lo;0;L;;;;;N;;;;;
+132C9;EGYPTIAN HIEROGLYPH R022;Lo;0;L;;;;;N;;;;;
+132CA;EGYPTIAN HIEROGLYPH R023;Lo;0;L;;;;;N;;;;;
+132CB;EGYPTIAN HIEROGLYPH R024;Lo;0;L;;;;;N;;;;;
+132CC;EGYPTIAN HIEROGLYPH R025;Lo;0;L;;;;;N;;;;;
+132CD;EGYPTIAN HIEROGLYPH R026;Lo;0;L;;;;;N;;;;;
+132CE;EGYPTIAN HIEROGLYPH R027;Lo;0;L;;;;;N;;;;;
+132CF;EGYPTIAN HIEROGLYPH R028;Lo;0;L;;;;;N;;;;;
+132D0;EGYPTIAN HIEROGLYPH R029;Lo;0;L;;;;;N;;;;;
+132D1;EGYPTIAN HIEROGLYPH S001;Lo;0;L;;;;;N;;;;;
+132D2;EGYPTIAN HIEROGLYPH S002;Lo;0;L;;;;;N;;;;;
+132D3;EGYPTIAN HIEROGLYPH S002A;Lo;0;L;;;;;N;;;;;
+132D4;EGYPTIAN HIEROGLYPH S003;Lo;0;L;;;;;N;;;;;
+132D5;EGYPTIAN HIEROGLYPH S004;Lo;0;L;;;;;N;;;;;
+132D6;EGYPTIAN HIEROGLYPH S005;Lo;0;L;;;;;N;;;;;
+132D7;EGYPTIAN HIEROGLYPH S006;Lo;0;L;;;;;N;;;;;
+132D8;EGYPTIAN HIEROGLYPH S006A;Lo;0;L;;;;;N;;;;;
+132D9;EGYPTIAN HIEROGLYPH S007;Lo;0;L;;;;;N;;;;;
+132DA;EGYPTIAN HIEROGLYPH S008;Lo;0;L;;;;;N;;;;;
+132DB;EGYPTIAN HIEROGLYPH S009;Lo;0;L;;;;;N;;;;;
+132DC;EGYPTIAN HIEROGLYPH S010;Lo;0;L;;;;;N;;;;;
+132DD;EGYPTIAN HIEROGLYPH S011;Lo;0;L;;;;;N;;;;;
+132DE;EGYPTIAN HIEROGLYPH S012;Lo;0;L;;;;;N;;;;;
+132DF;EGYPTIAN HIEROGLYPH S013;Lo;0;L;;;;;N;;;;;
+132E0;EGYPTIAN HIEROGLYPH S014;Lo;0;L;;;;;N;;;;;
+132E1;EGYPTIAN HIEROGLYPH S014A;Lo;0;L;;;;;N;;;;;
+132E2;EGYPTIAN HIEROGLYPH S014B;Lo;0;L;;;;;N;;;;;
+132E3;EGYPTIAN HIEROGLYPH S015;Lo;0;L;;;;;N;;;;;
+132E4;EGYPTIAN HIEROGLYPH S016;Lo;0;L;;;;;N;;;;;
+132E5;EGYPTIAN HIEROGLYPH S017;Lo;0;L;;;;;N;;;;;
+132E6;EGYPTIAN HIEROGLYPH S017A;Lo;0;L;;;;;N;;;;;
+132E7;EGYPTIAN HIEROGLYPH S018;Lo;0;L;;;;;N;;;;;
+132E8;EGYPTIAN HIEROGLYPH S019;Lo;0;L;;;;;N;;;;;
+132E9;EGYPTIAN HIEROGLYPH S020;Lo;0;L;;;;;N;;;;;
+132EA;EGYPTIAN HIEROGLYPH S021;Lo;0;L;;;;;N;;;;;
+132EB;EGYPTIAN HIEROGLYPH S022;Lo;0;L;;;;;N;;;;;
+132EC;EGYPTIAN HIEROGLYPH S023;Lo;0;L;;;;;N;;;;;
+132ED;EGYPTIAN HIEROGLYPH S024;Lo;0;L;;;;;N;;;;;
+132EE;EGYPTIAN HIEROGLYPH S025;Lo;0;L;;;;;N;;;;;
+132EF;EGYPTIAN HIEROGLYPH S026;Lo;0;L;;;;;N;;;;;
+132F0;EGYPTIAN HIEROGLYPH S026A;Lo;0;L;;;;;N;;;;;
+132F1;EGYPTIAN HIEROGLYPH S026B;Lo;0;L;;;;;N;;;;;
+132F2;EGYPTIAN HIEROGLYPH S027;Lo;0;L;;;;;N;;;;;
+132F3;EGYPTIAN HIEROGLYPH S028;Lo;0;L;;;;;N;;;;;
+132F4;EGYPTIAN HIEROGLYPH S029;Lo;0;L;;;;;N;;;;;
+132F5;EGYPTIAN HIEROGLYPH S030;Lo;0;L;;;;;N;;;;;
+132F6;EGYPTIAN HIEROGLYPH S031;Lo;0;L;;;;;N;;;;;
+132F7;EGYPTIAN HIEROGLYPH S032;Lo;0;L;;;;;N;;;;;
+132F8;EGYPTIAN HIEROGLYPH S033;Lo;0;L;;;;;N;;;;;
+132F9;EGYPTIAN HIEROGLYPH S034;Lo;0;L;;;;;N;;;;;
+132FA;EGYPTIAN HIEROGLYPH S035;Lo;0;L;;;;;N;;;;;
+132FB;EGYPTIAN HIEROGLYPH S035A;Lo;0;L;;;;;N;;;;;
+132FC;EGYPTIAN HIEROGLYPH S036;Lo;0;L;;;;;N;;;;;
+132FD;EGYPTIAN HIEROGLYPH S037;Lo;0;L;;;;;N;;;;;
+132FE;EGYPTIAN HIEROGLYPH S038;Lo;0;L;;;;;N;;;;;
+132FF;EGYPTIAN HIEROGLYPH S039;Lo;0;L;;;;;N;;;;;
+13300;EGYPTIAN HIEROGLYPH S040;Lo;0;L;;;;;N;;;;;
+13301;EGYPTIAN HIEROGLYPH S041;Lo;0;L;;;;;N;;;;;
+13302;EGYPTIAN HIEROGLYPH S042;Lo;0;L;;;;;N;;;;;
+13303;EGYPTIAN HIEROGLYPH S043;Lo;0;L;;;;;N;;;;;
+13304;EGYPTIAN HIEROGLYPH S044;Lo;0;L;;;;;N;;;;;
+13305;EGYPTIAN HIEROGLYPH S045;Lo;0;L;;;;;N;;;;;
+13306;EGYPTIAN HIEROGLYPH S046;Lo;0;L;;;;;N;;;;;
+13307;EGYPTIAN HIEROGLYPH T001;Lo;0;L;;;;;N;;;;;
+13308;EGYPTIAN HIEROGLYPH T002;Lo;0;L;;;;;N;;;;;
+13309;EGYPTIAN HIEROGLYPH T003;Lo;0;L;;;;;N;;;;;
+1330A;EGYPTIAN HIEROGLYPH T003A;Lo;0;L;;;;;N;;;;;
+1330B;EGYPTIAN HIEROGLYPH T004;Lo;0;L;;;;;N;;;;;
+1330C;EGYPTIAN HIEROGLYPH T005;Lo;0;L;;;;;N;;;;;
+1330D;EGYPTIAN HIEROGLYPH T006;Lo;0;L;;;;;N;;;;;
+1330E;EGYPTIAN HIEROGLYPH T007;Lo;0;L;;;;;N;;;;;
+1330F;EGYPTIAN HIEROGLYPH T007A;Lo;0;L;;;;;N;;;;;
+13310;EGYPTIAN HIEROGLYPH T008;Lo;0;L;;;;;N;;;;;
+13311;EGYPTIAN HIEROGLYPH T008A;Lo;0;L;;;;;N;;;;;
+13312;EGYPTIAN HIEROGLYPH T009;Lo;0;L;;;;;N;;;;;
+13313;EGYPTIAN HIEROGLYPH T009A;Lo;0;L;;;;;N;;;;;
+13314;EGYPTIAN HIEROGLYPH T010;Lo;0;L;;;;;N;;;;;
+13315;EGYPTIAN HIEROGLYPH T011;Lo;0;L;;;;;N;;;;;
+13316;EGYPTIAN HIEROGLYPH T011A;Lo;0;L;;;;;N;;;;;
+13317;EGYPTIAN HIEROGLYPH T012;Lo;0;L;;;;;N;;;;;
+13318;EGYPTIAN HIEROGLYPH T013;Lo;0;L;;;;;N;;;;;
+13319;EGYPTIAN HIEROGLYPH T014;Lo;0;L;;;;;N;;;;;
+1331A;EGYPTIAN HIEROGLYPH T015;Lo;0;L;;;;;N;;;;;
+1331B;EGYPTIAN HIEROGLYPH T016;Lo;0;L;;;;;N;;;;;
+1331C;EGYPTIAN HIEROGLYPH T016A;Lo;0;L;;;;;N;;;;;
+1331D;EGYPTIAN HIEROGLYPH T017;Lo;0;L;;;;;N;;;;;
+1331E;EGYPTIAN HIEROGLYPH T018;Lo;0;L;;;;;N;;;;;
+1331F;EGYPTIAN HIEROGLYPH T019;Lo;0;L;;;;;N;;;;;
+13320;EGYPTIAN HIEROGLYPH T020;Lo;0;L;;;;;N;;;;;
+13321;EGYPTIAN HIEROGLYPH T021;Lo;0;L;;;;;N;;;;;
+13322;EGYPTIAN HIEROGLYPH T022;Lo;0;L;;;;;N;;;;;
+13323;EGYPTIAN HIEROGLYPH T023;Lo;0;L;;;;;N;;;;;
+13324;EGYPTIAN HIEROGLYPH T024;Lo;0;L;;;;;N;;;;;
+13325;EGYPTIAN HIEROGLYPH T025;Lo;0;L;;;;;N;;;;;
+13326;EGYPTIAN HIEROGLYPH T026;Lo;0;L;;;;;N;;;;;
+13327;EGYPTIAN HIEROGLYPH T027;Lo;0;L;;;;;N;;;;;
+13328;EGYPTIAN HIEROGLYPH T028;Lo;0;L;;;;;N;;;;;
+13329;EGYPTIAN HIEROGLYPH T029;Lo;0;L;;;;;N;;;;;
+1332A;EGYPTIAN HIEROGLYPH T030;Lo;0;L;;;;;N;;;;;
+1332B;EGYPTIAN HIEROGLYPH T031;Lo;0;L;;;;;N;;;;;
+1332C;EGYPTIAN HIEROGLYPH T032;Lo;0;L;;;;;N;;;;;
+1332D;EGYPTIAN HIEROGLYPH T032A;Lo;0;L;;;;;N;;;;;
+1332E;EGYPTIAN HIEROGLYPH T033;Lo;0;L;;;;;N;;;;;
+1332F;EGYPTIAN HIEROGLYPH T033A;Lo;0;L;;;;;N;;;;;
+13330;EGYPTIAN HIEROGLYPH T034;Lo;0;L;;;;;N;;;;;
+13331;EGYPTIAN HIEROGLYPH T035;Lo;0;L;;;;;N;;;;;
+13332;EGYPTIAN HIEROGLYPH T036;Lo;0;L;;;;;N;;;;;
+13333;EGYPTIAN HIEROGLYPH U001;Lo;0;L;;;;;N;;;;;
+13334;EGYPTIAN HIEROGLYPH U002;Lo;0;L;;;;;N;;;;;
+13335;EGYPTIAN HIEROGLYPH U003;Lo;0;L;;;;;N;;;;;
+13336;EGYPTIAN HIEROGLYPH U004;Lo;0;L;;;;;N;;;;;
+13337;EGYPTIAN HIEROGLYPH U005;Lo;0;L;;;;;N;;;;;
+13338;EGYPTIAN HIEROGLYPH U006;Lo;0;L;;;;;N;;;;;
+13339;EGYPTIAN HIEROGLYPH U006A;Lo;0;L;;;;;N;;;;;
+1333A;EGYPTIAN HIEROGLYPH U006B;Lo;0;L;;;;;N;;;;;
+1333B;EGYPTIAN HIEROGLYPH U007;Lo;0;L;;;;;N;;;;;
+1333C;EGYPTIAN HIEROGLYPH U008;Lo;0;L;;;;;N;;;;;
+1333D;EGYPTIAN HIEROGLYPH U009;Lo;0;L;;;;;N;;;;;
+1333E;EGYPTIAN HIEROGLYPH U010;Lo;0;L;;;;;N;;;;;
+1333F;EGYPTIAN HIEROGLYPH U011;Lo;0;L;;;;;N;;;;;
+13340;EGYPTIAN HIEROGLYPH U012;Lo;0;L;;;;;N;;;;;
+13341;EGYPTIAN HIEROGLYPH U013;Lo;0;L;;;;;N;;;;;
+13342;EGYPTIAN HIEROGLYPH U014;Lo;0;L;;;;;N;;;;;
+13343;EGYPTIAN HIEROGLYPH U015;Lo;0;L;;;;;N;;;;;
+13344;EGYPTIAN HIEROGLYPH U016;Lo;0;L;;;;;N;;;;;
+13345;EGYPTIAN HIEROGLYPH U017;Lo;0;L;;;;;N;;;;;
+13346;EGYPTIAN HIEROGLYPH U018;Lo;0;L;;;;;N;;;;;
+13347;EGYPTIAN HIEROGLYPH U019;Lo;0;L;;;;;N;;;;;
+13348;EGYPTIAN HIEROGLYPH U020;Lo;0;L;;;;;N;;;;;
+13349;EGYPTIAN HIEROGLYPH U021;Lo;0;L;;;;;N;;;;;
+1334A;EGYPTIAN HIEROGLYPH U022;Lo;0;L;;;;;N;;;;;
+1334B;EGYPTIAN HIEROGLYPH U023;Lo;0;L;;;;;N;;;;;
+1334C;EGYPTIAN HIEROGLYPH U023A;Lo;0;L;;;;;N;;;;;
+1334D;EGYPTIAN HIEROGLYPH U024;Lo;0;L;;;;;N;;;;;
+1334E;EGYPTIAN HIEROGLYPH U025;Lo;0;L;;;;;N;;;;;
+1334F;EGYPTIAN HIEROGLYPH U026;Lo;0;L;;;;;N;;;;;
+13350;EGYPTIAN HIEROGLYPH U027;Lo;0;L;;;;;N;;;;;
+13351;EGYPTIAN HIEROGLYPH U028;Lo;0;L;;;;;N;;;;;
+13352;EGYPTIAN HIEROGLYPH U029;Lo;0;L;;;;;N;;;;;
+13353;EGYPTIAN HIEROGLYPH U029A;Lo;0;L;;;;;N;;;;;
+13354;EGYPTIAN HIEROGLYPH U030;Lo;0;L;;;;;N;;;;;
+13355;EGYPTIAN HIEROGLYPH U031;Lo;0;L;;;;;N;;;;;
+13356;EGYPTIAN HIEROGLYPH U032;Lo;0;L;;;;;N;;;;;
+13357;EGYPTIAN HIEROGLYPH U032A;Lo;0;L;;;;;N;;;;;
+13358;EGYPTIAN HIEROGLYPH U033;Lo;0;L;;;;;N;;;;;
+13359;EGYPTIAN HIEROGLYPH U034;Lo;0;L;;;;;N;;;;;
+1335A;EGYPTIAN HIEROGLYPH U035;Lo;0;L;;;;;N;;;;;
+1335B;EGYPTIAN HIEROGLYPH U036;Lo;0;L;;;;;N;;;;;
+1335C;EGYPTIAN HIEROGLYPH U037;Lo;0;L;;;;;N;;;;;
+1335D;EGYPTIAN HIEROGLYPH U038;Lo;0;L;;;;;N;;;;;
+1335E;EGYPTIAN HIEROGLYPH U039;Lo;0;L;;;;;N;;;;;
+1335F;EGYPTIAN HIEROGLYPH U040;Lo;0;L;;;;;N;;;;;
+13360;EGYPTIAN HIEROGLYPH U041;Lo;0;L;;;;;N;;;;;
+13361;EGYPTIAN HIEROGLYPH U042;Lo;0;L;;;;;N;;;;;
+13362;EGYPTIAN HIEROGLYPH V001;Lo;0;L;;;;;N;;;;;
+13363;EGYPTIAN HIEROGLYPH V001A;Lo;0;L;;;;;N;;;;;
+13364;EGYPTIAN HIEROGLYPH V001B;Lo;0;L;;;;;N;;;;;
+13365;EGYPTIAN HIEROGLYPH V001C;Lo;0;L;;;;;N;;;;;
+13366;EGYPTIAN HIEROGLYPH V001D;Lo;0;L;;;;;N;;;;;
+13367;EGYPTIAN HIEROGLYPH V001E;Lo;0;L;;;;;N;;;;;
+13368;EGYPTIAN HIEROGLYPH V001F;Lo;0;L;;;;;N;;;;;
+13369;EGYPTIAN HIEROGLYPH V001G;Lo;0;L;;;;;N;;;;;
+1336A;EGYPTIAN HIEROGLYPH V001H;Lo;0;L;;;;;N;;;;;
+1336B;EGYPTIAN HIEROGLYPH V001I;Lo;0;L;;;;;N;;;;;
+1336C;EGYPTIAN HIEROGLYPH V002;Lo;0;L;;;;;N;;;;;
+1336D;EGYPTIAN HIEROGLYPH V002A;Lo;0;L;;;;;N;;;;;
+1336E;EGYPTIAN HIEROGLYPH V003;Lo;0;L;;;;;N;;;;;
+1336F;EGYPTIAN HIEROGLYPH V004;Lo;0;L;;;;;N;;;;;
+13370;EGYPTIAN HIEROGLYPH V005;Lo;0;L;;;;;N;;;;;
+13371;EGYPTIAN HIEROGLYPH V006;Lo;0;L;;;;;N;;;;;
+13372;EGYPTIAN HIEROGLYPH V007;Lo;0;L;;;;;N;;;;;
+13373;EGYPTIAN HIEROGLYPH V007A;Lo;0;L;;;;;N;;;;;
+13374;EGYPTIAN HIEROGLYPH V007B;Lo;0;L;;;;;N;;;;;
+13375;EGYPTIAN HIEROGLYPH V008;Lo;0;L;;;;;N;;;;;
+13376;EGYPTIAN HIEROGLYPH V009;Lo;0;L;;;;;N;;;;;
+13377;EGYPTIAN HIEROGLYPH V010;Lo;0;L;;;;;N;;;;;
+13378;EGYPTIAN HIEROGLYPH V011;Lo;0;L;;;;;N;;;;;
+13379;EGYPTIAN HIEROGLYPH V011A;Lo;0;L;;;;;N;;;;;
+1337A;EGYPTIAN HIEROGLYPH V011B;Lo;0;L;;;;;N;;;;;
+1337B;EGYPTIAN HIEROGLYPH V011C;Lo;0;L;;;;;N;;;;;
+1337C;EGYPTIAN HIEROGLYPH V012;Lo;0;L;;;;;N;;;;;
+1337D;EGYPTIAN HIEROGLYPH V012A;Lo;0;L;;;;;N;;;;;
+1337E;EGYPTIAN HIEROGLYPH V012B;Lo;0;L;;;;;N;;;;;
+1337F;EGYPTIAN HIEROGLYPH V013;Lo;0;L;;;;;N;;;;;
+13380;EGYPTIAN HIEROGLYPH V014;Lo;0;L;;;;;N;;;;;
+13381;EGYPTIAN HIEROGLYPH V015;Lo;0;L;;;;;N;;;;;
+13382;EGYPTIAN HIEROGLYPH V016;Lo;0;L;;;;;N;;;;;
+13383;EGYPTIAN HIEROGLYPH V017;Lo;0;L;;;;;N;;;;;
+13384;EGYPTIAN HIEROGLYPH V018;Lo;0;L;;;;;N;;;;;
+13385;EGYPTIAN HIEROGLYPH V019;Lo;0;L;;;;;N;;;;;
+13386;EGYPTIAN HIEROGLYPH V020;Lo;0;L;;;;;N;;;;;
+13387;EGYPTIAN HIEROGLYPH V020A;Lo;0;L;;;;;N;;;;;
+13388;EGYPTIAN HIEROGLYPH V020B;Lo;0;L;;;;;N;;;;;
+13389;EGYPTIAN HIEROGLYPH V020C;Lo;0;L;;;;;N;;;;;
+1338A;EGYPTIAN HIEROGLYPH V020D;Lo;0;L;;;;;N;;;;;
+1338B;EGYPTIAN HIEROGLYPH V020E;Lo;0;L;;;;;N;;;;;
+1338C;EGYPTIAN HIEROGLYPH V020F;Lo;0;L;;;;;N;;;;;
+1338D;EGYPTIAN HIEROGLYPH V020G;Lo;0;L;;;;;N;;;;;
+1338E;EGYPTIAN HIEROGLYPH V020H;Lo;0;L;;;;;N;;;;;
+1338F;EGYPTIAN HIEROGLYPH V020I;Lo;0;L;;;;;N;;;;;
+13390;EGYPTIAN HIEROGLYPH V020J;Lo;0;L;;;;;N;;;;;
+13391;EGYPTIAN HIEROGLYPH V020K;Lo;0;L;;;;;N;;;;;
+13392;EGYPTIAN HIEROGLYPH V020L;Lo;0;L;;;;;N;;;;;
+13393;EGYPTIAN HIEROGLYPH V021;Lo;0;L;;;;;N;;;;;
+13394;EGYPTIAN HIEROGLYPH V022;Lo;0;L;;;;;N;;;;;
+13395;EGYPTIAN HIEROGLYPH V023;Lo;0;L;;;;;N;;;;;
+13396;EGYPTIAN HIEROGLYPH V023A;Lo;0;L;;;;;N;;;;;
+13397;EGYPTIAN HIEROGLYPH V024;Lo;0;L;;;;;N;;;;;
+13398;EGYPTIAN HIEROGLYPH V025;Lo;0;L;;;;;N;;;;;
+13399;EGYPTIAN HIEROGLYPH V026;Lo;0;L;;;;;N;;;;;
+1339A;EGYPTIAN HIEROGLYPH V027;Lo;0;L;;;;;N;;;;;
+1339B;EGYPTIAN HIEROGLYPH V028;Lo;0;L;;;;;N;;;;;
+1339C;EGYPTIAN HIEROGLYPH V028A;Lo;0;L;;;;;N;;;;;
+1339D;EGYPTIAN HIEROGLYPH V029;Lo;0;L;;;;;N;;;;;
+1339E;EGYPTIAN HIEROGLYPH V029A;Lo;0;L;;;;;N;;;;;
+1339F;EGYPTIAN HIEROGLYPH V030;Lo;0;L;;;;;N;;;;;
+133A0;EGYPTIAN HIEROGLYPH V030A;Lo;0;L;;;;;N;;;;;
+133A1;EGYPTIAN HIEROGLYPH V031;Lo;0;L;;;;;N;;;;;
+133A2;EGYPTIAN HIEROGLYPH V031A;Lo;0;L;;;;;N;;;;;
+133A3;EGYPTIAN HIEROGLYPH V032;Lo;0;L;;;;;N;;;;;
+133A4;EGYPTIAN HIEROGLYPH V033;Lo;0;L;;;;;N;;;;;
+133A5;EGYPTIAN HIEROGLYPH V033A;Lo;0;L;;;;;N;;;;;
+133A6;EGYPTIAN HIEROGLYPH V034;Lo;0;L;;;;;N;;;;;
+133A7;EGYPTIAN HIEROGLYPH V035;Lo;0;L;;;;;N;;;;;
+133A8;EGYPTIAN HIEROGLYPH V036;Lo;0;L;;;;;N;;;;;
+133A9;EGYPTIAN HIEROGLYPH V037;Lo;0;L;;;;;N;;;;;
+133AA;EGYPTIAN HIEROGLYPH V037A;Lo;0;L;;;;;N;;;;;
+133AB;EGYPTIAN HIEROGLYPH V038;Lo;0;L;;;;;N;;;;;
+133AC;EGYPTIAN HIEROGLYPH V039;Lo;0;L;;;;;N;;;;;
+133AD;EGYPTIAN HIEROGLYPH V040;Lo;0;L;;;;;N;;;;;
+133AE;EGYPTIAN HIEROGLYPH V040A;Lo;0;L;;;;;N;;;;;
+133AF;EGYPTIAN HIEROGLYPH W001;Lo;0;L;;;;;N;;;;;
+133B0;EGYPTIAN HIEROGLYPH W002;Lo;0;L;;;;;N;;;;;
+133B1;EGYPTIAN HIEROGLYPH W003;Lo;0;L;;;;;N;;;;;
+133B2;EGYPTIAN HIEROGLYPH W003A;Lo;0;L;;;;;N;;;;;
+133B3;EGYPTIAN HIEROGLYPH W004;Lo;0;L;;;;;N;;;;;
+133B4;EGYPTIAN HIEROGLYPH W005;Lo;0;L;;;;;N;;;;;
+133B5;EGYPTIAN HIEROGLYPH W006;Lo;0;L;;;;;N;;;;;
+133B6;EGYPTIAN HIEROGLYPH W007;Lo;0;L;;;;;N;;;;;
+133B7;EGYPTIAN HIEROGLYPH W008;Lo;0;L;;;;;N;;;;;
+133B8;EGYPTIAN HIEROGLYPH W009;Lo;0;L;;;;;N;;;;;
+133B9;EGYPTIAN HIEROGLYPH W009A;Lo;0;L;;;;;N;;;;;
+133BA;EGYPTIAN HIEROGLYPH W010;Lo;0;L;;;;;N;;;;;
+133BB;EGYPTIAN HIEROGLYPH W010A;Lo;0;L;;;;;N;;;;;
+133BC;EGYPTIAN HIEROGLYPH W011;Lo;0;L;;;;;N;;;;;
+133BD;EGYPTIAN HIEROGLYPH W012;Lo;0;L;;;;;N;;;;;
+133BE;EGYPTIAN HIEROGLYPH W013;Lo;0;L;;;;;N;;;;;
+133BF;EGYPTIAN HIEROGLYPH W014;Lo;0;L;;;;;N;;;;;
+133C0;EGYPTIAN HIEROGLYPH W014A;Lo;0;L;;;;;N;;;;;
+133C1;EGYPTIAN HIEROGLYPH W015;Lo;0;L;;;;;N;;;;;
+133C2;EGYPTIAN HIEROGLYPH W016;Lo;0;L;;;;;N;;;;;
+133C3;EGYPTIAN HIEROGLYPH W017;Lo;0;L;;;;;N;;;;;
+133C4;EGYPTIAN HIEROGLYPH W017A;Lo;0;L;;;;;N;;;;;
+133C5;EGYPTIAN HIEROGLYPH W018;Lo;0;L;;;;;N;;;;;
+133C6;EGYPTIAN HIEROGLYPH W018A;Lo;0;L;;;;;N;;;;;
+133C7;EGYPTIAN HIEROGLYPH W019;Lo;0;L;;;;;N;;;;;
+133C8;EGYPTIAN HIEROGLYPH W020;Lo;0;L;;;;;N;;;;;
+133C9;EGYPTIAN HIEROGLYPH W021;Lo;0;L;;;;;N;;;;;
+133CA;EGYPTIAN HIEROGLYPH W022;Lo;0;L;;;;;N;;;;;
+133CB;EGYPTIAN HIEROGLYPH W023;Lo;0;L;;;;;N;;;;;
+133CC;EGYPTIAN HIEROGLYPH W024;Lo;0;L;;;;;N;;;;;
+133CD;EGYPTIAN HIEROGLYPH W024A;Lo;0;L;;;;;N;;;;;
+133CE;EGYPTIAN HIEROGLYPH W025;Lo;0;L;;;;;N;;;;;
+133CF;EGYPTIAN HIEROGLYPH X001;Lo;0;L;;;;;N;;;;;
+133D0;EGYPTIAN HIEROGLYPH X002;Lo;0;L;;;;;N;;;;;
+133D1;EGYPTIAN HIEROGLYPH X003;Lo;0;L;;;;;N;;;;;
+133D2;EGYPTIAN HIEROGLYPH X004;Lo;0;L;;;;;N;;;;;
+133D3;EGYPTIAN HIEROGLYPH X004A;Lo;0;L;;;;;N;;;;;
+133D4;EGYPTIAN HIEROGLYPH X004B;Lo;0;L;;;;;N;;;;;
+133D5;EGYPTIAN HIEROGLYPH X005;Lo;0;L;;;;;N;;;;;
+133D6;EGYPTIAN HIEROGLYPH X006;Lo;0;L;;;;;N;;;;;
+133D7;EGYPTIAN HIEROGLYPH X006A;Lo;0;L;;;;;N;;;;;
+133D8;EGYPTIAN HIEROGLYPH X007;Lo;0;L;;;;;N;;;;;
+133D9;EGYPTIAN HIEROGLYPH X008;Lo;0;L;;;;;N;;;;;
+133DA;EGYPTIAN HIEROGLYPH X008A;Lo;0;L;;;;;N;;;;;
+133DB;EGYPTIAN HIEROGLYPH Y001;Lo;0;L;;;;;N;;;;;
+133DC;EGYPTIAN HIEROGLYPH Y001A;Lo;0;L;;;;;N;;;;;
+133DD;EGYPTIAN HIEROGLYPH Y002;Lo;0;L;;;;;N;;;;;
+133DE;EGYPTIAN HIEROGLYPH Y003;Lo;0;L;;;;;N;;;;;
+133DF;EGYPTIAN HIEROGLYPH Y004;Lo;0;L;;;;;N;;;;;
+133E0;EGYPTIAN HIEROGLYPH Y005;Lo;0;L;;;;;N;;;;;
+133E1;EGYPTIAN HIEROGLYPH Y006;Lo;0;L;;;;;N;;;;;
+133E2;EGYPTIAN HIEROGLYPH Y007;Lo;0;L;;;;;N;;;;;
+133E3;EGYPTIAN HIEROGLYPH Y008;Lo;0;L;;;;;N;;;;;
+133E4;EGYPTIAN HIEROGLYPH Z001;Lo;0;L;;;;;N;;;;;
+133E5;EGYPTIAN HIEROGLYPH Z002;Lo;0;L;;;;;N;;;;;
+133E6;EGYPTIAN HIEROGLYPH Z002A;Lo;0;L;;;;;N;;;;;
+133E7;EGYPTIAN HIEROGLYPH Z002B;Lo;0;L;;;;;N;;;;;
+133E8;EGYPTIAN HIEROGLYPH Z002C;Lo;0;L;;;;;N;;;;;
+133E9;EGYPTIAN HIEROGLYPH Z002D;Lo;0;L;;;;;N;;;;;
+133EA;EGYPTIAN HIEROGLYPH Z003;Lo;0;L;;;;;N;;;;;
+133EB;EGYPTIAN HIEROGLYPH Z003A;Lo;0;L;;;;;N;;;;;
+133EC;EGYPTIAN HIEROGLYPH Z003B;Lo;0;L;;;;;N;;;;;
+133ED;EGYPTIAN HIEROGLYPH Z004;Lo;0;L;;;;;N;;;;;
+133EE;EGYPTIAN HIEROGLYPH Z004A;Lo;0;L;;;;;N;;;;;
+133EF;EGYPTIAN HIEROGLYPH Z005;Lo;0;L;;;;;N;;;;;
+133F0;EGYPTIAN HIEROGLYPH Z005A;Lo;0;L;;;;;N;;;;;
+133F1;EGYPTIAN HIEROGLYPH Z006;Lo;0;L;;;;;N;;;;;
+133F2;EGYPTIAN HIEROGLYPH Z007;Lo;0;L;;;;;N;;;;;
+133F3;EGYPTIAN HIEROGLYPH Z008;Lo;0;L;;;;;N;;;;;
+133F4;EGYPTIAN HIEROGLYPH Z009;Lo;0;L;;;;;N;;;;;
+133F5;EGYPTIAN HIEROGLYPH Z010;Lo;0;L;;;;;N;;;;;
+133F6;EGYPTIAN HIEROGLYPH Z011;Lo;0;L;;;;;N;;;;;
+133F7;EGYPTIAN HIEROGLYPH Z012;Lo;0;L;;;;;N;;;;;
+133F8;EGYPTIAN HIEROGLYPH Z013;Lo;0;L;;;;;N;;;;;
+133F9;EGYPTIAN HIEROGLYPH Z014;Lo;0;L;;;;;N;;;;;
+133FA;EGYPTIAN HIEROGLYPH Z015;Lo;0;L;;;;;N;;;;;
+133FB;EGYPTIAN HIEROGLYPH Z015A;Lo;0;L;;;;;N;;;;;
+133FC;EGYPTIAN HIEROGLYPH Z015B;Lo;0;L;;;;;N;;;;;
+133FD;EGYPTIAN HIEROGLYPH Z015C;Lo;0;L;;;;;N;;;;;
+133FE;EGYPTIAN HIEROGLYPH Z015D;Lo;0;L;;;;;N;;;;;
+133FF;EGYPTIAN HIEROGLYPH Z015E;Lo;0;L;;;;;N;;;;;
+13400;EGYPTIAN HIEROGLYPH Z015F;Lo;0;L;;;;;N;;;;;
+13401;EGYPTIAN HIEROGLYPH Z015G;Lo;0;L;;;;;N;;;;;
+13402;EGYPTIAN HIEROGLYPH Z015H;Lo;0;L;;;;;N;;;;;
+13403;EGYPTIAN HIEROGLYPH Z015I;Lo;0;L;;;;;N;;;;;
+13404;EGYPTIAN HIEROGLYPH Z016;Lo;0;L;;;;;N;;;;;
+13405;EGYPTIAN HIEROGLYPH Z016A;Lo;0;L;;;;;N;;;;;
+13406;EGYPTIAN HIEROGLYPH Z016B;Lo;0;L;;;;;N;;;;;
+13407;EGYPTIAN HIEROGLYPH Z016C;Lo;0;L;;;;;N;;;;;
+13408;EGYPTIAN HIEROGLYPH Z016D;Lo;0;L;;;;;N;;;;;
+13409;EGYPTIAN HIEROGLYPH Z016E;Lo;0;L;;;;;N;;;;;
+1340A;EGYPTIAN HIEROGLYPH Z016F;Lo;0;L;;;;;N;;;;;
+1340B;EGYPTIAN HIEROGLYPH Z016G;Lo;0;L;;;;;N;;;;;
+1340C;EGYPTIAN HIEROGLYPH Z016H;Lo;0;L;;;;;N;;;;;
+1340D;EGYPTIAN HIEROGLYPH AA001;Lo;0;L;;;;;N;;;;;
+1340E;EGYPTIAN HIEROGLYPH AA002;Lo;0;L;;;;;N;;;;;
+1340F;EGYPTIAN HIEROGLYPH AA003;Lo;0;L;;;;;N;;;;;
+13410;EGYPTIAN HIEROGLYPH AA004;Lo;0;L;;;;;N;;;;;
+13411;EGYPTIAN HIEROGLYPH AA005;Lo;0;L;;;;;N;;;;;
+13412;EGYPTIAN HIEROGLYPH AA006;Lo;0;L;;;;;N;;;;;
+13413;EGYPTIAN HIEROGLYPH AA007;Lo;0;L;;;;;N;;;;;
+13414;EGYPTIAN HIEROGLYPH AA007A;Lo;0;L;;;;;N;;;;;
+13415;EGYPTIAN HIEROGLYPH AA007B;Lo;0;L;;;;;N;;;;;
+13416;EGYPTIAN HIEROGLYPH AA008;Lo;0;L;;;;;N;;;;;
+13417;EGYPTIAN HIEROGLYPH AA009;Lo;0;L;;;;;N;;;;;
+13418;EGYPTIAN HIEROGLYPH AA010;Lo;0;L;;;;;N;;;;;
+13419;EGYPTIAN HIEROGLYPH AA011;Lo;0;L;;;;;N;;;;;
+1341A;EGYPTIAN HIEROGLYPH AA012;Lo;0;L;;;;;N;;;;;
+1341B;EGYPTIAN HIEROGLYPH AA013;Lo;0;L;;;;;N;;;;;
+1341C;EGYPTIAN HIEROGLYPH AA014;Lo;0;L;;;;;N;;;;;
+1341D;EGYPTIAN HIEROGLYPH AA015;Lo;0;L;;;;;N;;;;;
+1341E;EGYPTIAN HIEROGLYPH AA016;Lo;0;L;;;;;N;;;;;
+1341F;EGYPTIAN HIEROGLYPH AA017;Lo;0;L;;;;;N;;;;;
+13420;EGYPTIAN HIEROGLYPH AA018;Lo;0;L;;;;;N;;;;;
+13421;EGYPTIAN HIEROGLYPH AA019;Lo;0;L;;;;;N;;;;;
+13422;EGYPTIAN HIEROGLYPH AA020;Lo;0;L;;;;;N;;;;;
+13423;EGYPTIAN HIEROGLYPH AA021;Lo;0;L;;;;;N;;;;;
+13424;EGYPTIAN HIEROGLYPH AA022;Lo;0;L;;;;;N;;;;;
+13425;EGYPTIAN HIEROGLYPH AA023;Lo;0;L;;;;;N;;;;;
+13426;EGYPTIAN HIEROGLYPH AA024;Lo;0;L;;;;;N;;;;;
+13427;EGYPTIAN HIEROGLYPH AA025;Lo;0;L;;;;;N;;;;;
+13428;EGYPTIAN HIEROGLYPH AA026;Lo;0;L;;;;;N;;;;;
+13429;EGYPTIAN HIEROGLYPH AA027;Lo;0;L;;;;;N;;;;;
+1342A;EGYPTIAN HIEROGLYPH AA028;Lo;0;L;;;;;N;;;;;
+1342B;EGYPTIAN HIEROGLYPH AA029;Lo;0;L;;;;;N;;;;;
+1342C;EGYPTIAN HIEROGLYPH AA030;Lo;0;L;;;;;N;;;;;
+1342D;EGYPTIAN HIEROGLYPH AA031;Lo;0;L;;;;;N;;;;;
+1342E;EGYPTIAN HIEROGLYPH AA032;Lo;0;L;;;;;N;;;;;
+13430;EGYPTIAN HIEROGLYPH VERTICAL JOINER;Cf;0;L;;;;;N;;;;;
+13431;EGYPTIAN HIEROGLYPH HORIZONTAL JOINER;Cf;0;L;;;;;N;;;;;
+13432;EGYPTIAN HIEROGLYPH INSERT AT TOP START;Cf;0;L;;;;;N;;;;;
+13433;EGYPTIAN HIEROGLYPH INSERT AT BOTTOM START;Cf;0;L;;;;;N;;;;;
+13434;EGYPTIAN HIEROGLYPH INSERT AT TOP END;Cf;0;L;;;;;N;;;;;
+13435;EGYPTIAN HIEROGLYPH INSERT AT BOTTOM END;Cf;0;L;;;;;N;;;;;
+13436;EGYPTIAN HIEROGLYPH OVERLAY MIDDLE;Cf;0;L;;;;;N;;;;;
+13437;EGYPTIAN HIEROGLYPH BEGIN SEGMENT;Cf;0;L;;;;;N;;;;;
+13438;EGYPTIAN HIEROGLYPH END SEGMENT;Cf;0;L;;;;;N;;;;;
+14400;ANATOLIAN HIEROGLYPH A001;Lo;0;L;;;;;N;;;;;
+14401;ANATOLIAN HIEROGLYPH A002;Lo;0;L;;;;;N;;;;;
+14402;ANATOLIAN HIEROGLYPH A003;Lo;0;L;;;;;N;;;;;
+14403;ANATOLIAN HIEROGLYPH A004;Lo;0;L;;;;;N;;;;;
+14404;ANATOLIAN HIEROGLYPH A005;Lo;0;L;;;;;N;;;;;
+14405;ANATOLIAN HIEROGLYPH A006;Lo;0;L;;;;;N;;;;;
+14406;ANATOLIAN HIEROGLYPH A007;Lo;0;L;;;;;N;;;;;
+14407;ANATOLIAN HIEROGLYPH A008;Lo;0;L;;;;;N;;;;;
+14408;ANATOLIAN HIEROGLYPH A009;Lo;0;L;;;;;N;;;;;
+14409;ANATOLIAN HIEROGLYPH A010;Lo;0;L;;;;;N;;;;;
+1440A;ANATOLIAN HIEROGLYPH A010A;Lo;0;L;;;;;N;;;;;
+1440B;ANATOLIAN HIEROGLYPH A011;Lo;0;L;;;;;N;;;;;
+1440C;ANATOLIAN HIEROGLYPH A012;Lo;0;L;;;;;N;;;;;
+1440D;ANATOLIAN HIEROGLYPH A013;Lo;0;L;;;;;N;;;;;
+1440E;ANATOLIAN HIEROGLYPH A014;Lo;0;L;;;;;N;;;;;
+1440F;ANATOLIAN HIEROGLYPH A015;Lo;0;L;;;;;N;;;;;
+14410;ANATOLIAN HIEROGLYPH A016;Lo;0;L;;;;;N;;;;;
+14411;ANATOLIAN HIEROGLYPH A017;Lo;0;L;;;;;N;;;;;
+14412;ANATOLIAN HIEROGLYPH A018;Lo;0;L;;;;;N;;;;;
+14413;ANATOLIAN HIEROGLYPH A019;Lo;0;L;;;;;N;;;;;
+14414;ANATOLIAN HIEROGLYPH A020;Lo;0;L;;;;;N;;;;;
+14415;ANATOLIAN HIEROGLYPH A021;Lo;0;L;;;;;N;;;;;
+14416;ANATOLIAN HIEROGLYPH A022;Lo;0;L;;;;;N;;;;;
+14417;ANATOLIAN HIEROGLYPH A023;Lo;0;L;;;;;N;;;;;
+14418;ANATOLIAN HIEROGLYPH A024;Lo;0;L;;;;;N;;;;;
+14419;ANATOLIAN HIEROGLYPH A025;Lo;0;L;;;;;N;;;;;
+1441A;ANATOLIAN HIEROGLYPH A026;Lo;0;L;;;;;N;;;;;
+1441B;ANATOLIAN HIEROGLYPH A026A;Lo;0;L;;;;;N;;;;;
+1441C;ANATOLIAN HIEROGLYPH A027;Lo;0;L;;;;;N;;;;;
+1441D;ANATOLIAN HIEROGLYPH A028;Lo;0;L;;;;;N;;;;;
+1441E;ANATOLIAN HIEROGLYPH A029;Lo;0;L;;;;;N;;;;;
+1441F;ANATOLIAN HIEROGLYPH A030;Lo;0;L;;;;;N;;;;;
+14420;ANATOLIAN HIEROGLYPH A031;Lo;0;L;;;;;N;;;;;
+14421;ANATOLIAN HIEROGLYPH A032;Lo;0;L;;;;;N;;;;;
+14422;ANATOLIAN HIEROGLYPH A033;Lo;0;L;;;;;N;;;;;
+14423;ANATOLIAN HIEROGLYPH A034;Lo;0;L;;;;;N;;;;;
+14424;ANATOLIAN HIEROGLYPH A035;Lo;0;L;;;;;N;;;;;
+14425;ANATOLIAN HIEROGLYPH A036;Lo;0;L;;;;;N;;;;;
+14426;ANATOLIAN HIEROGLYPH A037;Lo;0;L;;;;;N;;;;;
+14427;ANATOLIAN HIEROGLYPH A038;Lo;0;L;;;;;N;;;;;
+14428;ANATOLIAN HIEROGLYPH A039;Lo;0;L;;;;;N;;;;;
+14429;ANATOLIAN HIEROGLYPH A039A;Lo;0;L;;;;;N;;;;;
+1442A;ANATOLIAN HIEROGLYPH A040;Lo;0;L;;;;;N;;;;;
+1442B;ANATOLIAN HIEROGLYPH A041;Lo;0;L;;;;;N;;;;;
+1442C;ANATOLIAN HIEROGLYPH A041A;Lo;0;L;;;;;N;;;;;
+1442D;ANATOLIAN HIEROGLYPH A042;Lo;0;L;;;;;N;;;;;
+1442E;ANATOLIAN HIEROGLYPH A043;Lo;0;L;;;;;N;;;;;
+1442F;ANATOLIAN HIEROGLYPH A044;Lo;0;L;;;;;N;;;;;
+14430;ANATOLIAN HIEROGLYPH A045;Lo;0;L;;;;;N;;;;;
+14431;ANATOLIAN HIEROGLYPH A045A;Lo;0;L;;;;;N;;;;;
+14432;ANATOLIAN HIEROGLYPH A046;Lo;0;L;;;;;N;;;;;
+14433;ANATOLIAN HIEROGLYPH A046A;Lo;0;L;;;;;N;;;;;
+14434;ANATOLIAN HIEROGLYPH A046B;Lo;0;L;;;;;N;;;;;
+14435;ANATOLIAN HIEROGLYPH A047;Lo;0;L;;;;;N;;;;;
+14436;ANATOLIAN HIEROGLYPH A048;Lo;0;L;;;;;N;;;;;
+14437;ANATOLIAN HIEROGLYPH A049;Lo;0;L;;;;;N;;;;;
+14438;ANATOLIAN HIEROGLYPH A050;Lo;0;L;;;;;N;;;;;
+14439;ANATOLIAN HIEROGLYPH A051;Lo;0;L;;;;;N;;;;;
+1443A;ANATOLIAN HIEROGLYPH A052;Lo;0;L;;;;;N;;;;;
+1443B;ANATOLIAN HIEROGLYPH A053;Lo;0;L;;;;;N;;;;;
+1443C;ANATOLIAN HIEROGLYPH A054;Lo;0;L;;;;;N;;;;;
+1443D;ANATOLIAN HIEROGLYPH A055;Lo;0;L;;;;;N;;;;;
+1443E;ANATOLIAN HIEROGLYPH A056;Lo;0;L;;;;;N;;;;;
+1443F;ANATOLIAN HIEROGLYPH A057;Lo;0;L;;;;;N;;;;;
+14440;ANATOLIAN HIEROGLYPH A058;Lo;0;L;;;;;N;;;;;
+14441;ANATOLIAN HIEROGLYPH A059;Lo;0;L;;;;;N;;;;;
+14442;ANATOLIAN HIEROGLYPH A060;Lo;0;L;;;;;N;;;;;
+14443;ANATOLIAN HIEROGLYPH A061;Lo;0;L;;;;;N;;;;;
+14444;ANATOLIAN HIEROGLYPH A062;Lo;0;L;;;;;N;;;;;
+14445;ANATOLIAN HIEROGLYPH A063;Lo;0;L;;;;;N;;;;;
+14446;ANATOLIAN HIEROGLYPH A064;Lo;0;L;;;;;N;;;;;
+14447;ANATOLIAN HIEROGLYPH A065;Lo;0;L;;;;;N;;;;;
+14448;ANATOLIAN HIEROGLYPH A066;Lo;0;L;;;;;N;;;;;
+14449;ANATOLIAN HIEROGLYPH A066A;Lo;0;L;;;;;N;;;;;
+1444A;ANATOLIAN HIEROGLYPH A066B;Lo;0;L;;;;;N;;;;;
+1444B;ANATOLIAN HIEROGLYPH A066C;Lo;0;L;;;;;N;;;;;
+1444C;ANATOLIAN HIEROGLYPH A067;Lo;0;L;;;;;N;;;;;
+1444D;ANATOLIAN HIEROGLYPH A068;Lo;0;L;;;;;N;;;;;
+1444E;ANATOLIAN HIEROGLYPH A069;Lo;0;L;;;;;N;;;;;
+1444F;ANATOLIAN HIEROGLYPH A070;Lo;0;L;;;;;N;;;;;
+14450;ANATOLIAN HIEROGLYPH A071;Lo;0;L;;;;;N;;;;;
+14451;ANATOLIAN HIEROGLYPH A072;Lo;0;L;;;;;N;;;;;
+14452;ANATOLIAN HIEROGLYPH A073;Lo;0;L;;;;;N;;;;;
+14453;ANATOLIAN HIEROGLYPH A074;Lo;0;L;;;;;N;;;;;
+14454;ANATOLIAN HIEROGLYPH A075;Lo;0;L;;;;;N;;;;;
+14455;ANATOLIAN HIEROGLYPH A076;Lo;0;L;;;;;N;;;;;
+14456;ANATOLIAN HIEROGLYPH A077;Lo;0;L;;;;;N;;;;;
+14457;ANATOLIAN HIEROGLYPH A078;Lo;0;L;;;;;N;;;;;
+14458;ANATOLIAN HIEROGLYPH A079;Lo;0;L;;;;;N;;;;;
+14459;ANATOLIAN HIEROGLYPH A080;Lo;0;L;;;;;N;;;;;
+1445A;ANATOLIAN HIEROGLYPH A081;Lo;0;L;;;;;N;;;;;
+1445B;ANATOLIAN HIEROGLYPH A082;Lo;0;L;;;;;N;;;;;
+1445C;ANATOLIAN HIEROGLYPH A083;Lo;0;L;;;;;N;;;;;
+1445D;ANATOLIAN HIEROGLYPH A084;Lo;0;L;;;;;N;;;;;
+1445E;ANATOLIAN HIEROGLYPH A085;Lo;0;L;;;;;N;;;;;
+1445F;ANATOLIAN HIEROGLYPH A086;Lo;0;L;;;;;N;;;;;
+14460;ANATOLIAN HIEROGLYPH A087;Lo;0;L;;;;;N;;;;;
+14461;ANATOLIAN HIEROGLYPH A088;Lo;0;L;;;;;N;;;;;
+14462;ANATOLIAN HIEROGLYPH A089;Lo;0;L;;;;;N;;;;;
+14463;ANATOLIAN HIEROGLYPH A090;Lo;0;L;;;;;N;;;;;
+14464;ANATOLIAN HIEROGLYPH A091;Lo;0;L;;;;;N;;;;;
+14465;ANATOLIAN HIEROGLYPH A092;Lo;0;L;;;;;N;;;;;
+14466;ANATOLIAN HIEROGLYPH A093;Lo;0;L;;;;;N;;;;;
+14467;ANATOLIAN HIEROGLYPH A094;Lo;0;L;;;;;N;;;;;
+14468;ANATOLIAN HIEROGLYPH A095;Lo;0;L;;;;;N;;;;;
+14469;ANATOLIAN HIEROGLYPH A096;Lo;0;L;;;;;N;;;;;
+1446A;ANATOLIAN HIEROGLYPH A097;Lo;0;L;;;;;N;;;;;
+1446B;ANATOLIAN HIEROGLYPH A097A;Lo;0;L;;;;;N;;;;;
+1446C;ANATOLIAN HIEROGLYPH A098;Lo;0;L;;;;;N;;;;;
+1446D;ANATOLIAN HIEROGLYPH A098A;Lo;0;L;;;;;N;;;;;
+1446E;ANATOLIAN HIEROGLYPH A099;Lo;0;L;;;;;N;;;;;
+1446F;ANATOLIAN HIEROGLYPH A100;Lo;0;L;;;;;N;;;;;
+14470;ANATOLIAN HIEROGLYPH A100A;Lo;0;L;;;;;N;;;;;
+14471;ANATOLIAN HIEROGLYPH A101;Lo;0;L;;;;;N;;;;;
+14472;ANATOLIAN HIEROGLYPH A101A;Lo;0;L;;;;;N;;;;;
+14473;ANATOLIAN HIEROGLYPH A102;Lo;0;L;;;;;N;;;;;
+14474;ANATOLIAN HIEROGLYPH A102A;Lo;0;L;;;;;N;;;;;
+14475;ANATOLIAN HIEROGLYPH A103;Lo;0;L;;;;;N;;;;;
+14476;ANATOLIAN HIEROGLYPH A104;Lo;0;L;;;;;N;;;;;
+14477;ANATOLIAN HIEROGLYPH A104A;Lo;0;L;;;;;N;;;;;
+14478;ANATOLIAN HIEROGLYPH A104B;Lo;0;L;;;;;N;;;;;
+14479;ANATOLIAN HIEROGLYPH A104C;Lo;0;L;;;;;N;;;;;
+1447A;ANATOLIAN HIEROGLYPH A105;Lo;0;L;;;;;N;;;;;
+1447B;ANATOLIAN HIEROGLYPH A105A;Lo;0;L;;;;;N;;;;;
+1447C;ANATOLIAN HIEROGLYPH A105B;Lo;0;L;;;;;N;;;;;
+1447D;ANATOLIAN HIEROGLYPH A106;Lo;0;L;;;;;N;;;;;
+1447E;ANATOLIAN HIEROGLYPH A107;Lo;0;L;;;;;N;;;;;
+1447F;ANATOLIAN HIEROGLYPH A107A;Lo;0;L;;;;;N;;;;;
+14480;ANATOLIAN HIEROGLYPH A107B;Lo;0;L;;;;;N;;;;;
+14481;ANATOLIAN HIEROGLYPH A107C;Lo;0;L;;;;;N;;;;;
+14482;ANATOLIAN HIEROGLYPH A108;Lo;0;L;;;;;N;;;;;
+14483;ANATOLIAN HIEROGLYPH A109;Lo;0;L;;;;;N;;;;;
+14484;ANATOLIAN HIEROGLYPH A110;Lo;0;L;;;;;N;;;;;
+14485;ANATOLIAN HIEROGLYPH A110A;Lo;0;L;;;;;N;;;;;
+14486;ANATOLIAN HIEROGLYPH A110B;Lo;0;L;;;;;N;;;;;
+14487;ANATOLIAN HIEROGLYPH A111;Lo;0;L;;;;;N;;;;;
+14488;ANATOLIAN HIEROGLYPH A112;Lo;0;L;;;;;N;;;;;
+14489;ANATOLIAN HIEROGLYPH A113;Lo;0;L;;;;;N;;;;;
+1448A;ANATOLIAN HIEROGLYPH A114;Lo;0;L;;;;;N;;;;;
+1448B;ANATOLIAN HIEROGLYPH A115;Lo;0;L;;;;;N;;;;;
+1448C;ANATOLIAN HIEROGLYPH A115A;Lo;0;L;;;;;N;;;;;
+1448D;ANATOLIAN HIEROGLYPH A116;Lo;0;L;;;;;N;;;;;
+1448E;ANATOLIAN HIEROGLYPH A117;Lo;0;L;;;;;N;;;;;
+1448F;ANATOLIAN HIEROGLYPH A118;Lo;0;L;;;;;N;;;;;
+14490;ANATOLIAN HIEROGLYPH A119;Lo;0;L;;;;;N;;;;;
+14491;ANATOLIAN HIEROGLYPH A120;Lo;0;L;;;;;N;;;;;
+14492;ANATOLIAN HIEROGLYPH A121;Lo;0;L;;;;;N;;;;;
+14493;ANATOLIAN HIEROGLYPH A122;Lo;0;L;;;;;N;;;;;
+14494;ANATOLIAN HIEROGLYPH A123;Lo;0;L;;;;;N;;;;;
+14495;ANATOLIAN HIEROGLYPH A124;Lo;0;L;;;;;N;;;;;
+14496;ANATOLIAN HIEROGLYPH A125;Lo;0;L;;;;;N;;;;;
+14497;ANATOLIAN HIEROGLYPH A125A;Lo;0;L;;;;;N;;;;;
+14498;ANATOLIAN HIEROGLYPH A126;Lo;0;L;;;;;N;;;;;
+14499;ANATOLIAN HIEROGLYPH A127;Lo;0;L;;;;;N;;;;;
+1449A;ANATOLIAN HIEROGLYPH A128;Lo;0;L;;;;;N;;;;;
+1449B;ANATOLIAN HIEROGLYPH A129;Lo;0;L;;;;;N;;;;;
+1449C;ANATOLIAN HIEROGLYPH A130;Lo;0;L;;;;;N;;;;;
+1449D;ANATOLIAN HIEROGLYPH A131;Lo;0;L;;;;;N;;;;;
+1449E;ANATOLIAN HIEROGLYPH A132;Lo;0;L;;;;;N;;;;;
+1449F;ANATOLIAN HIEROGLYPH A133;Lo;0;L;;;;;N;;;;;
+144A0;ANATOLIAN HIEROGLYPH A134;Lo;0;L;;;;;N;;;;;
+144A1;ANATOLIAN HIEROGLYPH A135;Lo;0;L;;;;;N;;;;;
+144A2;ANATOLIAN HIEROGLYPH A135A;Lo;0;L;;;;;N;;;;;
+144A3;ANATOLIAN HIEROGLYPH A136;Lo;0;L;;;;;N;;;;;
+144A4;ANATOLIAN HIEROGLYPH A137;Lo;0;L;;;;;N;;;;;
+144A5;ANATOLIAN HIEROGLYPH A138;Lo;0;L;;;;;N;;;;;
+144A6;ANATOLIAN HIEROGLYPH A139;Lo;0;L;;;;;N;;;;;
+144A7;ANATOLIAN HIEROGLYPH A140;Lo;0;L;;;;;N;;;;;
+144A8;ANATOLIAN HIEROGLYPH A141;Lo;0;L;;;;;N;;;;;
+144A9;ANATOLIAN HIEROGLYPH A142;Lo;0;L;;;;;N;;;;;
+144AA;ANATOLIAN HIEROGLYPH A143;Lo;0;L;;;;;N;;;;;
+144AB;ANATOLIAN HIEROGLYPH A144;Lo;0;L;;;;;N;;;;;
+144AC;ANATOLIAN HIEROGLYPH A145;Lo;0;L;;;;;N;;;;;
+144AD;ANATOLIAN HIEROGLYPH A146;Lo;0;L;;;;;N;;;;;
+144AE;ANATOLIAN HIEROGLYPH A147;Lo;0;L;;;;;N;;;;;
+144AF;ANATOLIAN HIEROGLYPH A148;Lo;0;L;;;;;N;;;;;
+144B0;ANATOLIAN HIEROGLYPH A149;Lo;0;L;;;;;N;;;;;
+144B1;ANATOLIAN HIEROGLYPH A150;Lo;0;L;;;;;N;;;;;
+144B2;ANATOLIAN HIEROGLYPH A151;Lo;0;L;;;;;N;;;;;
+144B3;ANATOLIAN HIEROGLYPH A152;Lo;0;L;;;;;N;;;;;
+144B4;ANATOLIAN HIEROGLYPH A153;Lo;0;L;;;;;N;;;;;
+144B5;ANATOLIAN HIEROGLYPH A154;Lo;0;L;;;;;N;;;;;
+144B6;ANATOLIAN HIEROGLYPH A155;Lo;0;L;;;;;N;;;;;
+144B7;ANATOLIAN HIEROGLYPH A156;Lo;0;L;;;;;N;;;;;
+144B8;ANATOLIAN HIEROGLYPH A157;Lo;0;L;;;;;N;;;;;
+144B9;ANATOLIAN HIEROGLYPH A158;Lo;0;L;;;;;N;;;;;
+144BA;ANATOLIAN HIEROGLYPH A159;Lo;0;L;;;;;N;;;;;
+144BB;ANATOLIAN HIEROGLYPH A160;Lo;0;L;;;;;N;;;;;
+144BC;ANATOLIAN HIEROGLYPH A161;Lo;0;L;;;;;N;;;;;
+144BD;ANATOLIAN HIEROGLYPH A162;Lo;0;L;;;;;N;;;;;
+144BE;ANATOLIAN HIEROGLYPH A163;Lo;0;L;;;;;N;;;;;
+144BF;ANATOLIAN HIEROGLYPH A164;Lo;0;L;;;;;N;;;;;
+144C0;ANATOLIAN HIEROGLYPH A165;Lo;0;L;;;;;N;;;;;
+144C1;ANATOLIAN HIEROGLYPH A166;Lo;0;L;;;;;N;;;;;
+144C2;ANATOLIAN HIEROGLYPH A167;Lo;0;L;;;;;N;;;;;
+144C3;ANATOLIAN HIEROGLYPH A168;Lo;0;L;;;;;N;;;;;
+144C4;ANATOLIAN HIEROGLYPH A169;Lo;0;L;;;;;N;;;;;
+144C5;ANATOLIAN HIEROGLYPH A170;Lo;0;L;;;;;N;;;;;
+144C6;ANATOLIAN HIEROGLYPH A171;Lo;0;L;;;;;N;;;;;
+144C7;ANATOLIAN HIEROGLYPH A172;Lo;0;L;;;;;N;;;;;
+144C8;ANATOLIAN HIEROGLYPH A173;Lo;0;L;;;;;N;;;;;
+144C9;ANATOLIAN HIEROGLYPH A174;Lo;0;L;;;;;N;;;;;
+144CA;ANATOLIAN HIEROGLYPH A175;Lo;0;L;;;;;N;;;;;
+144CB;ANATOLIAN HIEROGLYPH A176;Lo;0;L;;;;;N;;;;;
+144CC;ANATOLIAN HIEROGLYPH A177;Lo;0;L;;;;;N;;;;;
+144CD;ANATOLIAN HIEROGLYPH A178;Lo;0;L;;;;;N;;;;;
+144CE;ANATOLIAN HIEROGLYPH A179;Lo;0;L;;;;;N;;;;;
+144CF;ANATOLIAN HIEROGLYPH A180;Lo;0;L;;;;;N;;;;;
+144D0;ANATOLIAN HIEROGLYPH A181;Lo;0;L;;;;;N;;;;;
+144D1;ANATOLIAN HIEROGLYPH A182;Lo;0;L;;;;;N;;;;;
+144D2;ANATOLIAN HIEROGLYPH A183;Lo;0;L;;;;;N;;;;;
+144D3;ANATOLIAN HIEROGLYPH A184;Lo;0;L;;;;;N;;;;;
+144D4;ANATOLIAN HIEROGLYPH A185;Lo;0;L;;;;;N;;;;;
+144D5;ANATOLIAN HIEROGLYPH A186;Lo;0;L;;;;;N;;;;;
+144D6;ANATOLIAN HIEROGLYPH A187;Lo;0;L;;;;;N;;;;;
+144D7;ANATOLIAN HIEROGLYPH A188;Lo;0;L;;;;;N;;;;;
+144D8;ANATOLIAN HIEROGLYPH A189;Lo;0;L;;;;;N;;;;;
+144D9;ANATOLIAN HIEROGLYPH A190;Lo;0;L;;;;;N;;;;;
+144DA;ANATOLIAN HIEROGLYPH A191;Lo;0;L;;;;;N;;;;;
+144DB;ANATOLIAN HIEROGLYPH A192;Lo;0;L;;;;;N;;;;;
+144DC;ANATOLIAN HIEROGLYPH A193;Lo;0;L;;;;;N;;;;;
+144DD;ANATOLIAN HIEROGLYPH A194;Lo;0;L;;;;;N;;;;;
+144DE;ANATOLIAN HIEROGLYPH A195;Lo;0;L;;;;;N;;;;;
+144DF;ANATOLIAN HIEROGLYPH A196;Lo;0;L;;;;;N;;;;;
+144E0;ANATOLIAN HIEROGLYPH A197;Lo;0;L;;;;;N;;;;;
+144E1;ANATOLIAN HIEROGLYPH A198;Lo;0;L;;;;;N;;;;;
+144E2;ANATOLIAN HIEROGLYPH A199;Lo;0;L;;;;;N;;;;;
+144E3;ANATOLIAN HIEROGLYPH A200;Lo;0;L;;;;;N;;;;;
+144E4;ANATOLIAN HIEROGLYPH A201;Lo;0;L;;;;;N;;;;;
+144E5;ANATOLIAN HIEROGLYPH A202;Lo;0;L;;;;;N;;;;;
+144E6;ANATOLIAN HIEROGLYPH A202A;Lo;0;L;;;;;N;;;;;
+144E7;ANATOLIAN HIEROGLYPH A202B;Lo;0;L;;;;;N;;;;;
+144E8;ANATOLIAN HIEROGLYPH A203;Lo;0;L;;;;;N;;;;;
+144E9;ANATOLIAN HIEROGLYPH A204;Lo;0;L;;;;;N;;;;;
+144EA;ANATOLIAN HIEROGLYPH A205;Lo;0;L;;;;;N;;;;;
+144EB;ANATOLIAN HIEROGLYPH A206;Lo;0;L;;;;;N;;;;;
+144EC;ANATOLIAN HIEROGLYPH A207;Lo;0;L;;;;;N;;;;;
+144ED;ANATOLIAN HIEROGLYPH A207A;Lo;0;L;;;;;N;;;;;
+144EE;ANATOLIAN HIEROGLYPH A208;Lo;0;L;;;;;N;;;;;
+144EF;ANATOLIAN HIEROGLYPH A209;Lo;0;L;;;;;N;;;;;
+144F0;ANATOLIAN HIEROGLYPH A209A;Lo;0;L;;;;;N;;;;;
+144F1;ANATOLIAN HIEROGLYPH A210;Lo;0;L;;;;;N;;;;;
+144F2;ANATOLIAN HIEROGLYPH A211;Lo;0;L;;;;;N;;;;;
+144F3;ANATOLIAN HIEROGLYPH A212;Lo;0;L;;;;;N;;;;;
+144F4;ANATOLIAN HIEROGLYPH A213;Lo;0;L;;;;;N;;;;;
+144F5;ANATOLIAN HIEROGLYPH A214;Lo;0;L;;;;;N;;;;;
+144F6;ANATOLIAN HIEROGLYPH A215;Lo;0;L;;;;;N;;;;;
+144F7;ANATOLIAN HIEROGLYPH A215A;Lo;0;L;;;;;N;;;;;
+144F8;ANATOLIAN HIEROGLYPH A216;Lo;0;L;;;;;N;;;;;
+144F9;ANATOLIAN HIEROGLYPH A216A;Lo;0;L;;;;;N;;;;;
+144FA;ANATOLIAN HIEROGLYPH A217;Lo;0;L;;;;;N;;;;;
+144FB;ANATOLIAN HIEROGLYPH A218;Lo;0;L;;;;;N;;;;;
+144FC;ANATOLIAN HIEROGLYPH A219;Lo;0;L;;;;;N;;;;;
+144FD;ANATOLIAN HIEROGLYPH A220;Lo;0;L;;;;;N;;;;;
+144FE;ANATOLIAN HIEROGLYPH A221;Lo;0;L;;;;;N;;;;;
+144FF;ANATOLIAN HIEROGLYPH A222;Lo;0;L;;;;;N;;;;;
+14500;ANATOLIAN HIEROGLYPH A223;Lo;0;L;;;;;N;;;;;
+14501;ANATOLIAN HIEROGLYPH A224;Lo;0;L;;;;;N;;;;;
+14502;ANATOLIAN HIEROGLYPH A225;Lo;0;L;;;;;N;;;;;
+14503;ANATOLIAN HIEROGLYPH A226;Lo;0;L;;;;;N;;;;;
+14504;ANATOLIAN HIEROGLYPH A227;Lo;0;L;;;;;N;;;;;
+14505;ANATOLIAN HIEROGLYPH A227A;Lo;0;L;;;;;N;;;;;
+14506;ANATOLIAN HIEROGLYPH A228;Lo;0;L;;;;;N;;;;;
+14507;ANATOLIAN HIEROGLYPH A229;Lo;0;L;;;;;N;;;;;
+14508;ANATOLIAN HIEROGLYPH A230;Lo;0;L;;;;;N;;;;;
+14509;ANATOLIAN HIEROGLYPH A231;Lo;0;L;;;;;N;;;;;
+1450A;ANATOLIAN HIEROGLYPH A232;Lo;0;L;;;;;N;;;;;
+1450B;ANATOLIAN HIEROGLYPH A233;Lo;0;L;;;;;N;;;;;
+1450C;ANATOLIAN HIEROGLYPH A234;Lo;0;L;;;;;N;;;;;
+1450D;ANATOLIAN HIEROGLYPH A235;Lo;0;L;;;;;N;;;;;
+1450E;ANATOLIAN HIEROGLYPH A236;Lo;0;L;;;;;N;;;;;
+1450F;ANATOLIAN HIEROGLYPH A237;Lo;0;L;;;;;N;;;;;
+14510;ANATOLIAN HIEROGLYPH A238;Lo;0;L;;;;;N;;;;;
+14511;ANATOLIAN HIEROGLYPH A239;Lo;0;L;;;;;N;;;;;
+14512;ANATOLIAN HIEROGLYPH A240;Lo;0;L;;;;;N;;;;;
+14513;ANATOLIAN HIEROGLYPH A241;Lo;0;L;;;;;N;;;;;
+14514;ANATOLIAN HIEROGLYPH A242;Lo;0;L;;;;;N;;;;;
+14515;ANATOLIAN HIEROGLYPH A243;Lo;0;L;;;;;N;;;;;
+14516;ANATOLIAN HIEROGLYPH A244;Lo;0;L;;;;;N;;;;;
+14517;ANATOLIAN HIEROGLYPH A245;Lo;0;L;;;;;N;;;;;
+14518;ANATOLIAN HIEROGLYPH A246;Lo;0;L;;;;;N;;;;;
+14519;ANATOLIAN HIEROGLYPH A247;Lo;0;L;;;;;N;;;;;
+1451A;ANATOLIAN HIEROGLYPH A248;Lo;0;L;;;;;N;;;;;
+1451B;ANATOLIAN HIEROGLYPH A249;Lo;0;L;;;;;N;;;;;
+1451C;ANATOLIAN HIEROGLYPH A250;Lo;0;L;;;;;N;;;;;
+1451D;ANATOLIAN HIEROGLYPH A251;Lo;0;L;;;;;N;;;;;
+1451E;ANATOLIAN HIEROGLYPH A252;Lo;0;L;;;;;N;;;;;
+1451F;ANATOLIAN HIEROGLYPH A253;Lo;0;L;;;;;N;;;;;
+14520;ANATOLIAN HIEROGLYPH A254;Lo;0;L;;;;;N;;;;;
+14521;ANATOLIAN HIEROGLYPH A255;Lo;0;L;;;;;N;;;;;
+14522;ANATOLIAN HIEROGLYPH A256;Lo;0;L;;;;;N;;;;;
+14523;ANATOLIAN HIEROGLYPH A257;Lo;0;L;;;;;N;;;;;
+14524;ANATOLIAN HIEROGLYPH A258;Lo;0;L;;;;;N;;;;;
+14525;ANATOLIAN HIEROGLYPH A259;Lo;0;L;;;;;N;;;;;
+14526;ANATOLIAN HIEROGLYPH A260;Lo;0;L;;;;;N;;;;;
+14527;ANATOLIAN HIEROGLYPH A261;Lo;0;L;;;;;N;;;;;
+14528;ANATOLIAN HIEROGLYPH A262;Lo;0;L;;;;;N;;;;;
+14529;ANATOLIAN HIEROGLYPH A263;Lo;0;L;;;;;N;;;;;
+1452A;ANATOLIAN HIEROGLYPH A264;Lo;0;L;;;;;N;;;;;
+1452B;ANATOLIAN HIEROGLYPH A265;Lo;0;L;;;;;N;;;;;
+1452C;ANATOLIAN HIEROGLYPH A266;Lo;0;L;;;;;N;;;;;
+1452D;ANATOLIAN HIEROGLYPH A267;Lo;0;L;;;;;N;;;;;
+1452E;ANATOLIAN HIEROGLYPH A267A;Lo;0;L;;;;;N;;;;;
+1452F;ANATOLIAN HIEROGLYPH A268;Lo;0;L;;;;;N;;;;;
+14530;ANATOLIAN HIEROGLYPH A269;Lo;0;L;;;;;N;;;;;
+14531;ANATOLIAN HIEROGLYPH A270;Lo;0;L;;;;;N;;;;;
+14532;ANATOLIAN HIEROGLYPH A271;Lo;0;L;;;;;N;;;;;
+14533;ANATOLIAN HIEROGLYPH A272;Lo;0;L;;;;;N;;;;;
+14534;ANATOLIAN HIEROGLYPH A273;Lo;0;L;;;;;N;;;;;
+14535;ANATOLIAN HIEROGLYPH A274;Lo;0;L;;;;;N;;;;;
+14536;ANATOLIAN HIEROGLYPH A275;Lo;0;L;;;;;N;;;;;
+14537;ANATOLIAN HIEROGLYPH A276;Lo;0;L;;;;;N;;;;;
+14538;ANATOLIAN HIEROGLYPH A277;Lo;0;L;;;;;N;;;;;
+14539;ANATOLIAN HIEROGLYPH A278;Lo;0;L;;;;;N;;;;;
+1453A;ANATOLIAN HIEROGLYPH A279;Lo;0;L;;;;;N;;;;;
+1453B;ANATOLIAN HIEROGLYPH A280;Lo;0;L;;;;;N;;;;;
+1453C;ANATOLIAN HIEROGLYPH A281;Lo;0;L;;;;;N;;;;;
+1453D;ANATOLIAN HIEROGLYPH A282;Lo;0;L;;;;;N;;;;;
+1453E;ANATOLIAN HIEROGLYPH A283;Lo;0;L;;;;;N;;;;;
+1453F;ANATOLIAN HIEROGLYPH A284;Lo;0;L;;;;;N;;;;;
+14540;ANATOLIAN HIEROGLYPH A285;Lo;0;L;;;;;N;;;;;
+14541;ANATOLIAN HIEROGLYPH A286;Lo;0;L;;;;;N;;;;;
+14542;ANATOLIAN HIEROGLYPH A287;Lo;0;L;;;;;N;;;;;
+14543;ANATOLIAN HIEROGLYPH A288;Lo;0;L;;;;;N;;;;;
+14544;ANATOLIAN HIEROGLYPH A289;Lo;0;L;;;;;N;;;;;
+14545;ANATOLIAN HIEROGLYPH A289A;Lo;0;L;;;;;N;;;;;
+14546;ANATOLIAN HIEROGLYPH A290;Lo;0;L;;;;;N;;;;;
+14547;ANATOLIAN HIEROGLYPH A291;Lo;0;L;;;;;N;;;;;
+14548;ANATOLIAN HIEROGLYPH A292;Lo;0;L;;;;;N;;;;;
+14549;ANATOLIAN HIEROGLYPH A293;Lo;0;L;;;;;N;;;;;
+1454A;ANATOLIAN HIEROGLYPH A294;Lo;0;L;;;;;N;;;;;
+1454B;ANATOLIAN HIEROGLYPH A294A;Lo;0;L;;;;;N;;;;;
+1454C;ANATOLIAN HIEROGLYPH A295;Lo;0;L;;;;;N;;;;;
+1454D;ANATOLIAN HIEROGLYPH A296;Lo;0;L;;;;;N;;;;;
+1454E;ANATOLIAN HIEROGLYPH A297;Lo;0;L;;;;;N;;;;;
+1454F;ANATOLIAN HIEROGLYPH A298;Lo;0;L;;;;;N;;;;;
+14550;ANATOLIAN HIEROGLYPH A299;Lo;0;L;;;;;N;;;;;
+14551;ANATOLIAN HIEROGLYPH A299A;Lo;0;L;;;;;N;;;;;
+14552;ANATOLIAN HIEROGLYPH A300;Lo;0;L;;;;;N;;;;;
+14553;ANATOLIAN HIEROGLYPH A301;Lo;0;L;;;;;N;;;;;
+14554;ANATOLIAN HIEROGLYPH A302;Lo;0;L;;;;;N;;;;;
+14555;ANATOLIAN HIEROGLYPH A303;Lo;0;L;;;;;N;;;;;
+14556;ANATOLIAN HIEROGLYPH A304;Lo;0;L;;;;;N;;;;;
+14557;ANATOLIAN HIEROGLYPH A305;Lo;0;L;;;;;N;;;;;
+14558;ANATOLIAN HIEROGLYPH A306;Lo;0;L;;;;;N;;;;;
+14559;ANATOLIAN HIEROGLYPH A307;Lo;0;L;;;;;N;;;;;
+1455A;ANATOLIAN HIEROGLYPH A308;Lo;0;L;;;;;N;;;;;
+1455B;ANATOLIAN HIEROGLYPH A309;Lo;0;L;;;;;N;;;;;
+1455C;ANATOLIAN HIEROGLYPH A309A;Lo;0;L;;;;;N;;;;;
+1455D;ANATOLIAN HIEROGLYPH A310;Lo;0;L;;;;;N;;;;;
+1455E;ANATOLIAN HIEROGLYPH A311;Lo;0;L;;;;;N;;;;;
+1455F;ANATOLIAN HIEROGLYPH A312;Lo;0;L;;;;;N;;;;;
+14560;ANATOLIAN HIEROGLYPH A313;Lo;0;L;;;;;N;;;;;
+14561;ANATOLIAN HIEROGLYPH A314;Lo;0;L;;;;;N;;;;;
+14562;ANATOLIAN HIEROGLYPH A315;Lo;0;L;;;;;N;;;;;
+14563;ANATOLIAN HIEROGLYPH A316;Lo;0;L;;;;;N;;;;;
+14564;ANATOLIAN HIEROGLYPH A317;Lo;0;L;;;;;N;;;;;
+14565;ANATOLIAN HIEROGLYPH A318;Lo;0;L;;;;;N;;;;;
+14566;ANATOLIAN HIEROGLYPH A319;Lo;0;L;;;;;N;;;;;
+14567;ANATOLIAN HIEROGLYPH A320;Lo;0;L;;;;;N;;;;;
+14568;ANATOLIAN HIEROGLYPH A321;Lo;0;L;;;;;N;;;;;
+14569;ANATOLIAN HIEROGLYPH A322;Lo;0;L;;;;;N;;;;;
+1456A;ANATOLIAN HIEROGLYPH A323;Lo;0;L;;;;;N;;;;;
+1456B;ANATOLIAN HIEROGLYPH A324;Lo;0;L;;;;;N;;;;;
+1456C;ANATOLIAN HIEROGLYPH A325;Lo;0;L;;;;;N;;;;;
+1456D;ANATOLIAN HIEROGLYPH A326;Lo;0;L;;;;;N;;;;;
+1456E;ANATOLIAN HIEROGLYPH A327;Lo;0;L;;;;;N;;;;;
+1456F;ANATOLIAN HIEROGLYPH A328;Lo;0;L;;;;;N;;;;;
+14570;ANATOLIAN HIEROGLYPH A329;Lo;0;L;;;;;N;;;;;
+14571;ANATOLIAN HIEROGLYPH A329A;Lo;0;L;;;;;N;;;;;
+14572;ANATOLIAN HIEROGLYPH A330;Lo;0;L;;;;;N;;;;;
+14573;ANATOLIAN HIEROGLYPH A331;Lo;0;L;;;;;N;;;;;
+14574;ANATOLIAN HIEROGLYPH A332A;Lo;0;L;;;;;N;;;;;
+14575;ANATOLIAN HIEROGLYPH A332B;Lo;0;L;;;;;N;;;;;
+14576;ANATOLIAN HIEROGLYPH A332C;Lo;0;L;;;;;N;;;;;
+14577;ANATOLIAN HIEROGLYPH A333;Lo;0;L;;;;;N;;;;;
+14578;ANATOLIAN HIEROGLYPH A334;Lo;0;L;;;;;N;;;;;
+14579;ANATOLIAN HIEROGLYPH A335;Lo;0;L;;;;;N;;;;;
+1457A;ANATOLIAN HIEROGLYPH A336;Lo;0;L;;;;;N;;;;;
+1457B;ANATOLIAN HIEROGLYPH A336A;Lo;0;L;;;;;N;;;;;
+1457C;ANATOLIAN HIEROGLYPH A336B;Lo;0;L;;;;;N;;;;;
+1457D;ANATOLIAN HIEROGLYPH A336C;Lo;0;L;;;;;N;;;;;
+1457E;ANATOLIAN HIEROGLYPH A337;Lo;0;L;;;;;N;;;;;
+1457F;ANATOLIAN HIEROGLYPH A338;Lo;0;L;;;;;N;;;;;
+14580;ANATOLIAN HIEROGLYPH A339;Lo;0;L;;;;;N;;;;;
+14581;ANATOLIAN HIEROGLYPH A340;Lo;0;L;;;;;N;;;;;
+14582;ANATOLIAN HIEROGLYPH A341;Lo;0;L;;;;;N;;;;;
+14583;ANATOLIAN HIEROGLYPH A342;Lo;0;L;;;;;N;;;;;
+14584;ANATOLIAN HIEROGLYPH A343;Lo;0;L;;;;;N;;;;;
+14585;ANATOLIAN HIEROGLYPH A344;Lo;0;L;;;;;N;;;;;
+14586;ANATOLIAN HIEROGLYPH A345;Lo;0;L;;;;;N;;;;;
+14587;ANATOLIAN HIEROGLYPH A346;Lo;0;L;;;;;N;;;;;
+14588;ANATOLIAN HIEROGLYPH A347;Lo;0;L;;;;;N;;;;;
+14589;ANATOLIAN HIEROGLYPH A348;Lo;0;L;;;;;N;;;;;
+1458A;ANATOLIAN HIEROGLYPH A349;Lo;0;L;;;;;N;;;;;
+1458B;ANATOLIAN HIEROGLYPH A350;Lo;0;L;;;;;N;;;;;
+1458C;ANATOLIAN HIEROGLYPH A351;Lo;0;L;;;;;N;;;;;
+1458D;ANATOLIAN HIEROGLYPH A352;Lo;0;L;;;;;N;;;;;
+1458E;ANATOLIAN HIEROGLYPH A353;Lo;0;L;;;;;N;;;;;
+1458F;ANATOLIAN HIEROGLYPH A354;Lo;0;L;;;;;N;;;;;
+14590;ANATOLIAN HIEROGLYPH A355;Lo;0;L;;;;;N;;;;;
+14591;ANATOLIAN HIEROGLYPH A356;Lo;0;L;;;;;N;;;;;
+14592;ANATOLIAN HIEROGLYPH A357;Lo;0;L;;;;;N;;;;;
+14593;ANATOLIAN HIEROGLYPH A358;Lo;0;L;;;;;N;;;;;
+14594;ANATOLIAN HIEROGLYPH A359;Lo;0;L;;;;;N;;;;;
+14595;ANATOLIAN HIEROGLYPH A359A;Lo;0;L;;;;;N;;;;;
+14596;ANATOLIAN HIEROGLYPH A360;Lo;0;L;;;;;N;;;;;
+14597;ANATOLIAN HIEROGLYPH A361;Lo;0;L;;;;;N;;;;;
+14598;ANATOLIAN HIEROGLYPH A362;Lo;0;L;;;;;N;;;;;
+14599;ANATOLIAN HIEROGLYPH A363;Lo;0;L;;;;;N;;;;;
+1459A;ANATOLIAN HIEROGLYPH A364;Lo;0;L;;;;;N;;;;;
+1459B;ANATOLIAN HIEROGLYPH A364A;Lo;0;L;;;;;N;;;;;
+1459C;ANATOLIAN HIEROGLYPH A365;Lo;0;L;;;;;N;;;;;
+1459D;ANATOLIAN HIEROGLYPH A366;Lo;0;L;;;;;N;;;;;
+1459E;ANATOLIAN HIEROGLYPH A367;Lo;0;L;;;;;N;;;;;
+1459F;ANATOLIAN HIEROGLYPH A368;Lo;0;L;;;;;N;;;;;
+145A0;ANATOLIAN HIEROGLYPH A368A;Lo;0;L;;;;;N;;;;;
+145A1;ANATOLIAN HIEROGLYPH A369;Lo;0;L;;;;;N;;;;;
+145A2;ANATOLIAN HIEROGLYPH A370;Lo;0;L;;;;;N;;;;;
+145A3;ANATOLIAN HIEROGLYPH A371;Lo;0;L;;;;;N;;;;;
+145A4;ANATOLIAN HIEROGLYPH A371A;Lo;0;L;;;;;N;;;;;
+145A5;ANATOLIAN HIEROGLYPH A372;Lo;0;L;;;;;N;;;;;
+145A6;ANATOLIAN HIEROGLYPH A373;Lo;0;L;;;;;N;;;;;
+145A7;ANATOLIAN HIEROGLYPH A374;Lo;0;L;;;;;N;;;;;
+145A8;ANATOLIAN HIEROGLYPH A375;Lo;0;L;;;;;N;;;;;
+145A9;ANATOLIAN HIEROGLYPH A376;Lo;0;L;;;;;N;;;;;
+145AA;ANATOLIAN HIEROGLYPH A377;Lo;0;L;;;;;N;;;;;
+145AB;ANATOLIAN HIEROGLYPH A378;Lo;0;L;;;;;N;;;;;
+145AC;ANATOLIAN HIEROGLYPH A379;Lo;0;L;;;;;N;;;;;
+145AD;ANATOLIAN HIEROGLYPH A380;Lo;0;L;;;;;N;;;;;
+145AE;ANATOLIAN HIEROGLYPH A381;Lo;0;L;;;;;N;;;;;
+145AF;ANATOLIAN HIEROGLYPH A381A;Lo;0;L;;;;;N;;;;;
+145B0;ANATOLIAN HIEROGLYPH A382;Lo;0;L;;;;;N;;;;;
+145B1;ANATOLIAN HIEROGLYPH A383 RA OR RI;Lo;0;L;;;;;N;;;;;
+145B2;ANATOLIAN HIEROGLYPH A383A;Lo;0;L;;;;;N;;;;;
+145B3;ANATOLIAN HIEROGLYPH A384;Lo;0;L;;;;;N;;;;;
+145B4;ANATOLIAN HIEROGLYPH A385;Lo;0;L;;;;;N;;;;;
+145B5;ANATOLIAN HIEROGLYPH A386;Lo;0;L;;;;;N;;;;;
+145B6;ANATOLIAN HIEROGLYPH A386A;Lo;0;L;;;;;N;;;;;
+145B7;ANATOLIAN HIEROGLYPH A387;Lo;0;L;;;;;N;;;;;
+145B8;ANATOLIAN HIEROGLYPH A388;Lo;0;L;;;;;N;;;;;
+145B9;ANATOLIAN HIEROGLYPH A389;Lo;0;L;;;;;N;;;;;
+145BA;ANATOLIAN HIEROGLYPH A390;Lo;0;L;;;;;N;;;;;
+145BB;ANATOLIAN HIEROGLYPH A391;Lo;0;L;;;;;N;;;;;
+145BC;ANATOLIAN HIEROGLYPH A392;Lo;0;L;;;;;N;;;;;
+145BD;ANATOLIAN HIEROGLYPH A393 EIGHT;Lo;0;L;;;;;N;;;;;
+145BE;ANATOLIAN HIEROGLYPH A394;Lo;0;L;;;;;N;;;;;
+145BF;ANATOLIAN HIEROGLYPH A395;Lo;0;L;;;;;N;;;;;
+145C0;ANATOLIAN HIEROGLYPH A396;Lo;0;L;;;;;N;;;;;
+145C1;ANATOLIAN HIEROGLYPH A397;Lo;0;L;;;;;N;;;;;
+145C2;ANATOLIAN HIEROGLYPH A398;Lo;0;L;;;;;N;;;;;
+145C3;ANATOLIAN HIEROGLYPH A399;Lo;0;L;;;;;N;;;;;
+145C4;ANATOLIAN HIEROGLYPH A400;Lo;0;L;;;;;N;;;;;
+145C5;ANATOLIAN HIEROGLYPH A401;Lo;0;L;;;;;N;;;;;
+145C6;ANATOLIAN HIEROGLYPH A402;Lo;0;L;;;;;N;;;;;
+145C7;ANATOLIAN HIEROGLYPH A403;Lo;0;L;;;;;N;;;;;
+145C8;ANATOLIAN HIEROGLYPH A404;Lo;0;L;;;;;N;;;;;
+145C9;ANATOLIAN HIEROGLYPH A405;Lo;0;L;;;;;N;;;;;
+145CA;ANATOLIAN HIEROGLYPH A406;Lo;0;L;;;;;N;;;;;
+145CB;ANATOLIAN HIEROGLYPH A407;Lo;0;L;;;;;N;;;;;
+145CC;ANATOLIAN HIEROGLYPH A408;Lo;0;L;;;;;N;;;;;
+145CD;ANATOLIAN HIEROGLYPH A409;Lo;0;L;;;;;N;;;;;
+145CE;ANATOLIAN HIEROGLYPH A410 BEGIN LOGOGRAM MARK;Lo;0;L;;;;;N;;;;;
+145CF;ANATOLIAN HIEROGLYPH A410A END LOGOGRAM MARK;Lo;0;L;;;;;N;;;;;
+145D0;ANATOLIAN HIEROGLYPH A411;Lo;0;L;;;;;N;;;;;
+145D1;ANATOLIAN HIEROGLYPH A412;Lo;0;L;;;;;N;;;;;
+145D2;ANATOLIAN HIEROGLYPH A413;Lo;0;L;;;;;N;;;;;
+145D3;ANATOLIAN HIEROGLYPH A414;Lo;0;L;;;;;N;;;;;
+145D4;ANATOLIAN HIEROGLYPH A415;Lo;0;L;;;;;N;;;;;
+145D5;ANATOLIAN HIEROGLYPH A416;Lo;0;L;;;;;N;;;;;
+145D6;ANATOLIAN HIEROGLYPH A417;Lo;0;L;;;;;N;;;;;
+145D7;ANATOLIAN HIEROGLYPH A418;Lo;0;L;;;;;N;;;;;
+145D8;ANATOLIAN HIEROGLYPH A419;Lo;0;L;;;;;N;;;;;
+145D9;ANATOLIAN HIEROGLYPH A420;Lo;0;L;;;;;N;;;;;
+145DA;ANATOLIAN HIEROGLYPH A421;Lo;0;L;;;;;N;;;;;
+145DB;ANATOLIAN HIEROGLYPH A422;Lo;0;L;;;;;N;;;;;
+145DC;ANATOLIAN HIEROGLYPH A423;Lo;0;L;;;;;N;;;;;
+145DD;ANATOLIAN HIEROGLYPH A424;Lo;0;L;;;;;N;;;;;
+145DE;ANATOLIAN HIEROGLYPH A425;Lo;0;L;;;;;N;;;;;
+145DF;ANATOLIAN HIEROGLYPH A426;Lo;0;L;;;;;N;;;;;
+145E0;ANATOLIAN HIEROGLYPH A427;Lo;0;L;;;;;N;;;;;
+145E1;ANATOLIAN HIEROGLYPH A428;Lo;0;L;;;;;N;;;;;
+145E2;ANATOLIAN HIEROGLYPH A429;Lo;0;L;;;;;N;;;;;
+145E3;ANATOLIAN HIEROGLYPH A430;Lo;0;L;;;;;N;;;;;
+145E4;ANATOLIAN HIEROGLYPH A431;Lo;0;L;;;;;N;;;;;
+145E5;ANATOLIAN HIEROGLYPH A432;Lo;0;L;;;;;N;;;;;
+145E6;ANATOLIAN HIEROGLYPH A433;Lo;0;L;;;;;N;;;;;
+145E7;ANATOLIAN HIEROGLYPH A434;Lo;0;L;;;;;N;;;;;
+145E8;ANATOLIAN HIEROGLYPH A435;Lo;0;L;;;;;N;;;;;
+145E9;ANATOLIAN HIEROGLYPH A436;Lo;0;L;;;;;N;;;;;
+145EA;ANATOLIAN HIEROGLYPH A437;Lo;0;L;;;;;N;;;;;
+145EB;ANATOLIAN HIEROGLYPH A438;Lo;0;L;;;;;N;;;;;
+145EC;ANATOLIAN HIEROGLYPH A439;Lo;0;L;;;;;N;;;;;
+145ED;ANATOLIAN HIEROGLYPH A440;Lo;0;L;;;;;N;;;;;
+145EE;ANATOLIAN HIEROGLYPH A441;Lo;0;L;;;;;N;;;;;
+145EF;ANATOLIAN HIEROGLYPH A442;Lo;0;L;;;;;N;;;;;
+145F0;ANATOLIAN HIEROGLYPH A443;Lo;0;L;;;;;N;;;;;
+145F1;ANATOLIAN HIEROGLYPH A444;Lo;0;L;;;;;N;;;;;
+145F2;ANATOLIAN HIEROGLYPH A445;Lo;0;L;;;;;N;;;;;
+145F3;ANATOLIAN HIEROGLYPH A446;Lo;0;L;;;;;N;;;;;
+145F4;ANATOLIAN HIEROGLYPH A447;Lo;0;L;;;;;N;;;;;
+145F5;ANATOLIAN HIEROGLYPH A448;Lo;0;L;;;;;N;;;;;
+145F6;ANATOLIAN HIEROGLYPH A449;Lo;0;L;;;;;N;;;;;
+145F7;ANATOLIAN HIEROGLYPH A450;Lo;0;L;;;;;N;;;;;
+145F8;ANATOLIAN HIEROGLYPH A450A;Lo;0;L;;;;;N;;;;;
+145F9;ANATOLIAN HIEROGLYPH A451;Lo;0;L;;;;;N;;;;;
+145FA;ANATOLIAN HIEROGLYPH A452;Lo;0;L;;;;;N;;;;;
+145FB;ANATOLIAN HIEROGLYPH A453;Lo;0;L;;;;;N;;;;;
+145FC;ANATOLIAN HIEROGLYPH A454;Lo;0;L;;;;;N;;;;;
+145FD;ANATOLIAN HIEROGLYPH A455;Lo;0;L;;;;;N;;;;;
+145FE;ANATOLIAN HIEROGLYPH A456;Lo;0;L;;;;;N;;;;;
+145FF;ANATOLIAN HIEROGLYPH A457;Lo;0;L;;;;;N;;;;;
+14600;ANATOLIAN HIEROGLYPH A457A;Lo;0;L;;;;;N;;;;;
+14601;ANATOLIAN HIEROGLYPH A458;Lo;0;L;;;;;N;;;;;
+14602;ANATOLIAN HIEROGLYPH A459;Lo;0;L;;;;;N;;;;;
+14603;ANATOLIAN HIEROGLYPH A460;Lo;0;L;;;;;N;;;;;
+14604;ANATOLIAN HIEROGLYPH A461;Lo;0;L;;;;;N;;;;;
+14605;ANATOLIAN HIEROGLYPH A462;Lo;0;L;;;;;N;;;;;
+14606;ANATOLIAN HIEROGLYPH A463;Lo;0;L;;;;;N;;;;;
+14607;ANATOLIAN HIEROGLYPH A464;Lo;0;L;;;;;N;;;;;
+14608;ANATOLIAN HIEROGLYPH A465;Lo;0;L;;;;;N;;;;;
+14609;ANATOLIAN HIEROGLYPH A466;Lo;0;L;;;;;N;;;;;
+1460A;ANATOLIAN HIEROGLYPH A467;Lo;0;L;;;;;N;;;;;
+1460B;ANATOLIAN HIEROGLYPH A468;Lo;0;L;;;;;N;;;;;
+1460C;ANATOLIAN HIEROGLYPH A469;Lo;0;L;;;;;N;;;;;
+1460D;ANATOLIAN HIEROGLYPH A470;Lo;0;L;;;;;N;;;;;
+1460E;ANATOLIAN HIEROGLYPH A471;Lo;0;L;;;;;N;;;;;
+1460F;ANATOLIAN HIEROGLYPH A472;Lo;0;L;;;;;N;;;;;
+14610;ANATOLIAN HIEROGLYPH A473;Lo;0;L;;;;;N;;;;;
+14611;ANATOLIAN HIEROGLYPH A474;Lo;0;L;;;;;N;;;;;
+14612;ANATOLIAN HIEROGLYPH A475;Lo;0;L;;;;;N;;;;;
+14613;ANATOLIAN HIEROGLYPH A476;Lo;0;L;;;;;N;;;;;
+14614;ANATOLIAN HIEROGLYPH A477;Lo;0;L;;;;;N;;;;;
+14615;ANATOLIAN HIEROGLYPH A478;Lo;0;L;;;;;N;;;;;
+14616;ANATOLIAN HIEROGLYPH A479;Lo;0;L;;;;;N;;;;;
+14617;ANATOLIAN HIEROGLYPH A480;Lo;0;L;;;;;N;;;;;
+14618;ANATOLIAN HIEROGLYPH A481;Lo;0;L;;;;;N;;;;;
+14619;ANATOLIAN HIEROGLYPH A482;Lo;0;L;;;;;N;;;;;
+1461A;ANATOLIAN HIEROGLYPH A483;Lo;0;L;;;;;N;;;;;
+1461B;ANATOLIAN HIEROGLYPH A484;Lo;0;L;;;;;N;;;;;
+1461C;ANATOLIAN HIEROGLYPH A485;Lo;0;L;;;;;N;;;;;
+1461D;ANATOLIAN HIEROGLYPH A486;Lo;0;L;;;;;N;;;;;
+1461E;ANATOLIAN HIEROGLYPH A487;Lo;0;L;;;;;N;;;;;
+1461F;ANATOLIAN HIEROGLYPH A488;Lo;0;L;;;;;N;;;;;
+14620;ANATOLIAN HIEROGLYPH A489;Lo;0;L;;;;;N;;;;;
+14621;ANATOLIAN HIEROGLYPH A490;Lo;0;L;;;;;N;;;;;
+14622;ANATOLIAN HIEROGLYPH A491;Lo;0;L;;;;;N;;;;;
+14623;ANATOLIAN HIEROGLYPH A492;Lo;0;L;;;;;N;;;;;
+14624;ANATOLIAN HIEROGLYPH A493;Lo;0;L;;;;;N;;;;;
+14625;ANATOLIAN HIEROGLYPH A494;Lo;0;L;;;;;N;;;;;
+14626;ANATOLIAN HIEROGLYPH A495;Lo;0;L;;;;;N;;;;;
+14627;ANATOLIAN HIEROGLYPH A496;Lo;0;L;;;;;N;;;;;
+14628;ANATOLIAN HIEROGLYPH A497;Lo;0;L;;;;;N;;;;;
+14629;ANATOLIAN HIEROGLYPH A501;Lo;0;L;;;;;N;;;;;
+1462A;ANATOLIAN HIEROGLYPH A502;Lo;0;L;;;;;N;;;;;
+1462B;ANATOLIAN HIEROGLYPH A503;Lo;0;L;;;;;N;;;;;
+1462C;ANATOLIAN HIEROGLYPH A504;Lo;0;L;;;;;N;;;;;
+1462D;ANATOLIAN HIEROGLYPH A505;Lo;0;L;;;;;N;;;;;
+1462E;ANATOLIAN HIEROGLYPH A506;Lo;0;L;;;;;N;;;;;
+1462F;ANATOLIAN HIEROGLYPH A507;Lo;0;L;;;;;N;;;;;
+14630;ANATOLIAN HIEROGLYPH A508;Lo;0;L;;;;;N;;;;;
+14631;ANATOLIAN HIEROGLYPH A509;Lo;0;L;;;;;N;;;;;
+14632;ANATOLIAN HIEROGLYPH A510;Lo;0;L;;;;;N;;;;;
+14633;ANATOLIAN HIEROGLYPH A511;Lo;0;L;;;;;N;;;;;
+14634;ANATOLIAN HIEROGLYPH A512;Lo;0;L;;;;;N;;;;;
+14635;ANATOLIAN HIEROGLYPH A513;Lo;0;L;;;;;N;;;;;
+14636;ANATOLIAN HIEROGLYPH A514;Lo;0;L;;;;;N;;;;;
+14637;ANATOLIAN HIEROGLYPH A515;Lo;0;L;;;;;N;;;;;
+14638;ANATOLIAN HIEROGLYPH A516;Lo;0;L;;;;;N;;;;;
+14639;ANATOLIAN HIEROGLYPH A517;Lo;0;L;;;;;N;;;;;
+1463A;ANATOLIAN HIEROGLYPH A518;Lo;0;L;;;;;N;;;;;
+1463B;ANATOLIAN HIEROGLYPH A519;Lo;0;L;;;;;N;;;;;
+1463C;ANATOLIAN HIEROGLYPH A520;Lo;0;L;;;;;N;;;;;
+1463D;ANATOLIAN HIEROGLYPH A521;Lo;0;L;;;;;N;;;;;
+1463E;ANATOLIAN HIEROGLYPH A522;Lo;0;L;;;;;N;;;;;
+1463F;ANATOLIAN HIEROGLYPH A523;Lo;0;L;;;;;N;;;;;
+14640;ANATOLIAN HIEROGLYPH A524;Lo;0;L;;;;;N;;;;;
+14641;ANATOLIAN HIEROGLYPH A525;Lo;0;L;;;;;N;;;;;
+14642;ANATOLIAN HIEROGLYPH A526;Lo;0;L;;;;;N;;;;;
+14643;ANATOLIAN HIEROGLYPH A527;Lo;0;L;;;;;N;;;;;
+14644;ANATOLIAN HIEROGLYPH A528;Lo;0;L;;;;;N;;;;;
+14645;ANATOLIAN HIEROGLYPH A529;Lo;0;L;;;;;N;;;;;
+14646;ANATOLIAN HIEROGLYPH A530;Lo;0;L;;;;;N;;;;;
+16800;BAMUM LETTER PHASE-A NGKUE MFON;Lo;0;L;;;;;N;;;;;
+16801;BAMUM LETTER PHASE-A GBIEE FON;Lo;0;L;;;;;N;;;;;
+16802;BAMUM LETTER PHASE-A PON MFON PIPAEMGBIEE;Lo;0;L;;;;;N;;;;;
+16803;BAMUM LETTER PHASE-A PON MFON PIPAEMBA;Lo;0;L;;;;;N;;;;;
+16804;BAMUM LETTER PHASE-A NAA MFON;Lo;0;L;;;;;N;;;;;
+16805;BAMUM LETTER PHASE-A SHUENSHUET;Lo;0;L;;;;;N;;;;;
+16806;BAMUM LETTER PHASE-A TITA MFON;Lo;0;L;;;;;N;;;;;
+16807;BAMUM LETTER PHASE-A NZA MFON;Lo;0;L;;;;;N;;;;;
+16808;BAMUM LETTER PHASE-A SHINDA PA NJI;Lo;0;L;;;;;N;;;;;
+16809;BAMUM LETTER PHASE-A PON PA NJI PIPAEMGBIEE;Lo;0;L;;;;;N;;;;;
+1680A;BAMUM LETTER PHASE-A PON PA NJI PIPAEMBA;Lo;0;L;;;;;N;;;;;
+1680B;BAMUM LETTER PHASE-A MAEMBGBIEE;Lo;0;L;;;;;N;;;;;
+1680C;BAMUM LETTER PHASE-A TU MAEMBA;Lo;0;L;;;;;N;;;;;
+1680D;BAMUM LETTER PHASE-A NGANGU;Lo;0;L;;;;;N;;;;;
+1680E;BAMUM LETTER PHASE-A MAEMVEUX;Lo;0;L;;;;;N;;;;;
+1680F;BAMUM LETTER PHASE-A MANSUAE;Lo;0;L;;;;;N;;;;;
+16810;BAMUM LETTER PHASE-A MVEUAENGAM;Lo;0;L;;;;;N;;;;;
+16811;BAMUM LETTER PHASE-A SEUNYAM;Lo;0;L;;;;;N;;;;;
+16812;BAMUM LETTER PHASE-A NTOQPEN;Lo;0;L;;;;;N;;;;;
+16813;BAMUM LETTER PHASE-A KEUKEUTNDA;Lo;0;L;;;;;N;;;;;
+16814;BAMUM LETTER PHASE-A NKINDI;Lo;0;L;;;;;N;;;;;
+16815;BAMUM LETTER PHASE-A SUU;Lo;0;L;;;;;N;;;;;
+16816;BAMUM LETTER PHASE-A NGKUENZEUM;Lo;0;L;;;;;N;;;;;
+16817;BAMUM LETTER PHASE-A LAPAQ;Lo;0;L;;;;;N;;;;;
+16818;BAMUM LETTER PHASE-A LET KUT;Lo;0;L;;;;;N;;;;;
+16819;BAMUM LETTER PHASE-A NTAP MFAA;Lo;0;L;;;;;N;;;;;
+1681A;BAMUM LETTER PHASE-A MAEKEUP;Lo;0;L;;;;;N;;;;;
+1681B;BAMUM LETTER PHASE-A PASHAE;Lo;0;L;;;;;N;;;;;
+1681C;BAMUM LETTER PHASE-A GHEUAERAE;Lo;0;L;;;;;N;;;;;
+1681D;BAMUM LETTER PHASE-A PAMSHAE;Lo;0;L;;;;;N;;;;;
+1681E;BAMUM LETTER PHASE-A MON NGGEUAET;Lo;0;L;;;;;N;;;;;
+1681F;BAMUM LETTER PHASE-A NZUN MEUT;Lo;0;L;;;;;N;;;;;
+16820;BAMUM LETTER PHASE-A U YUQ NAE;Lo;0;L;;;;;N;;;;;
+16821;BAMUM LETTER PHASE-A GHEUAEGHEUAE;Lo;0;L;;;;;N;;;;;
+16822;BAMUM LETTER PHASE-A NTAP NTAA;Lo;0;L;;;;;N;;;;;
+16823;BAMUM LETTER PHASE-A SISA;Lo;0;L;;;;;N;;;;;
+16824;BAMUM LETTER PHASE-A MGBASA;Lo;0;L;;;;;N;;;;;
+16825;BAMUM LETTER PHASE-A MEUNJOMNDEUQ;Lo;0;L;;;;;N;;;;;
+16826;BAMUM LETTER PHASE-A MOOMPUQ;Lo;0;L;;;;;N;;;;;
+16827;BAMUM LETTER PHASE-A KAFA;Lo;0;L;;;;;N;;;;;
+16828;BAMUM LETTER PHASE-A PA LEERAEWA;Lo;0;L;;;;;N;;;;;
+16829;BAMUM LETTER PHASE-A NDA LEERAEWA;Lo;0;L;;;;;N;;;;;
+1682A;BAMUM LETTER PHASE-A PET;Lo;0;L;;;;;N;;;;;
+1682B;BAMUM LETTER PHASE-A MAEMKPEN;Lo;0;L;;;;;N;;;;;
+1682C;BAMUM LETTER PHASE-A NIKA;Lo;0;L;;;;;N;;;;;
+1682D;BAMUM LETTER PHASE-A PUP;Lo;0;L;;;;;N;;;;;
+1682E;BAMUM LETTER PHASE-A TUAEP;Lo;0;L;;;;;N;;;;;
+1682F;BAMUM LETTER PHASE-A LUAEP;Lo;0;L;;;;;N;;;;;
+16830;BAMUM LETTER PHASE-A SONJAM;Lo;0;L;;;;;N;;;;;
+16831;BAMUM LETTER PHASE-A TEUTEUWEN;Lo;0;L;;;;;N;;;;;
+16832;BAMUM LETTER PHASE-A MAENYI;Lo;0;L;;;;;N;;;;;
+16833;BAMUM LETTER PHASE-A KET;Lo;0;L;;;;;N;;;;;
+16834;BAMUM LETTER PHASE-A NDAANGGEUAET;Lo;0;L;;;;;N;;;;;
+16835;BAMUM LETTER PHASE-A KUOQ;Lo;0;L;;;;;N;;;;;
+16836;BAMUM LETTER PHASE-A MOOMEUT;Lo;0;L;;;;;N;;;;;
+16837;BAMUM LETTER PHASE-A SHUM;Lo;0;L;;;;;N;;;;;
+16838;BAMUM LETTER PHASE-A LOMMAE;Lo;0;L;;;;;N;;;;;
+16839;BAMUM LETTER PHASE-A FIRI;Lo;0;L;;;;;N;;;;;
+1683A;BAMUM LETTER PHASE-A ROM;Lo;0;L;;;;;N;;;;;
+1683B;BAMUM LETTER PHASE-A KPOQ;Lo;0;L;;;;;N;;;;;
+1683C;BAMUM LETTER PHASE-A SOQ;Lo;0;L;;;;;N;;;;;
+1683D;BAMUM LETTER PHASE-A MAP PIEET;Lo;0;L;;;;;N;;;;;
+1683E;BAMUM LETTER PHASE-A SHIRAE;Lo;0;L;;;;;N;;;;;
+1683F;BAMUM LETTER PHASE-A NTAP;Lo;0;L;;;;;N;;;;;
+16840;BAMUM LETTER PHASE-A SHOQ NSHUT YUM;Lo;0;L;;;;;N;;;;;
+16841;BAMUM LETTER PHASE-A NYIT MONGKEUAEQ;Lo;0;L;;;;;N;;;;;
+16842;BAMUM LETTER PHASE-A PAARAE;Lo;0;L;;;;;N;;;;;
+16843;BAMUM LETTER PHASE-A NKAARAE;Lo;0;L;;;;;N;;;;;
+16844;BAMUM LETTER PHASE-A UNKNOWN;Lo;0;L;;;;;N;;;;;
+16845;BAMUM LETTER PHASE-A NGGEN;Lo;0;L;;;;;N;;;;;
+16846;BAMUM LETTER PHASE-A MAESI;Lo;0;L;;;;;N;;;;;
+16847;BAMUM LETTER PHASE-A NJAM;Lo;0;L;;;;;N;;;;;
+16848;BAMUM LETTER PHASE-A MBANYI;Lo;0;L;;;;;N;;;;;
+16849;BAMUM LETTER PHASE-A NYET;Lo;0;L;;;;;N;;;;;
+1684A;BAMUM LETTER PHASE-A TEUAEN;Lo;0;L;;;;;N;;;;;
+1684B;BAMUM LETTER PHASE-A SOT;Lo;0;L;;;;;N;;;;;
+1684C;BAMUM LETTER PHASE-A PAAM;Lo;0;L;;;;;N;;;;;
+1684D;BAMUM LETTER PHASE-A NSHIEE;Lo;0;L;;;;;N;;;;;
+1684E;BAMUM LETTER PHASE-A MAEM;Lo;0;L;;;;;N;;;;;
+1684F;BAMUM LETTER PHASE-A NYI;Lo;0;L;;;;;N;;;;;
+16850;BAMUM LETTER PHASE-A KAQ;Lo;0;L;;;;;N;;;;;
+16851;BAMUM LETTER PHASE-A NSHA;Lo;0;L;;;;;N;;;;;
+16852;BAMUM LETTER PHASE-A VEE;Lo;0;L;;;;;N;;;;;
+16853;BAMUM LETTER PHASE-A LU;Lo;0;L;;;;;N;;;;;
+16854;BAMUM LETTER PHASE-A NEN;Lo;0;L;;;;;N;;;;;
+16855;BAMUM LETTER PHASE-A NAQ;Lo;0;L;;;;;N;;;;;
+16856;BAMUM LETTER PHASE-A MBAQ;Lo;0;L;;;;;N;;;;;
+16857;BAMUM LETTER PHASE-B NSHUET;Lo;0;L;;;;;N;;;;;
+16858;BAMUM LETTER PHASE-B TU MAEMGBIEE;Lo;0;L;;;;;N;;;;;
+16859;BAMUM LETTER PHASE-B SIEE;Lo;0;L;;;;;N;;;;;
+1685A;BAMUM LETTER PHASE-B SET TU;Lo;0;L;;;;;N;;;;;
+1685B;BAMUM LETTER PHASE-B LOM NTEUM;Lo;0;L;;;;;N;;;;;
+1685C;BAMUM LETTER PHASE-B MBA MAELEE;Lo;0;L;;;;;N;;;;;
+1685D;BAMUM LETTER PHASE-B KIEEM;Lo;0;L;;;;;N;;;;;
+1685E;BAMUM LETTER PHASE-B YEURAE;Lo;0;L;;;;;N;;;;;
+1685F;BAMUM LETTER PHASE-B MBAARAE;Lo;0;L;;;;;N;;;;;
+16860;BAMUM LETTER PHASE-B KAM;Lo;0;L;;;;;N;;;;;
+16861;BAMUM LETTER PHASE-B PEESHI;Lo;0;L;;;;;N;;;;;
+16862;BAMUM LETTER PHASE-B YAFU LEERAEWA;Lo;0;L;;;;;N;;;;;
+16863;BAMUM LETTER PHASE-B LAM NSHUT NYAM;Lo;0;L;;;;;N;;;;;
+16864;BAMUM LETTER PHASE-B NTIEE SHEUOQ;Lo;0;L;;;;;N;;;;;
+16865;BAMUM LETTER PHASE-B NDU NJAA;Lo;0;L;;;;;N;;;;;
+16866;BAMUM LETTER PHASE-B GHEUGHEUAEM;Lo;0;L;;;;;N;;;;;
+16867;BAMUM LETTER PHASE-B PIT;Lo;0;L;;;;;N;;;;;
+16868;BAMUM LETTER PHASE-B TU NSIEE;Lo;0;L;;;;;N;;;;;
+16869;BAMUM LETTER PHASE-B SHET NJAQ;Lo;0;L;;;;;N;;;;;
+1686A;BAMUM LETTER PHASE-B SHEUAEQTU;Lo;0;L;;;;;N;;;;;
+1686B;BAMUM LETTER PHASE-B MFON TEUAEQ;Lo;0;L;;;;;N;;;;;
+1686C;BAMUM LETTER PHASE-B MBIT MBAAKET;Lo;0;L;;;;;N;;;;;
+1686D;BAMUM LETTER PHASE-B NYI NTEUM;Lo;0;L;;;;;N;;;;;
+1686E;BAMUM LETTER PHASE-B KEUPUQ;Lo;0;L;;;;;N;;;;;
+1686F;BAMUM LETTER PHASE-B GHEUGHEN;Lo;0;L;;;;;N;;;;;
+16870;BAMUM LETTER PHASE-B KEUYEUX;Lo;0;L;;;;;N;;;;;
+16871;BAMUM LETTER PHASE-B LAANAE;Lo;0;L;;;;;N;;;;;
+16872;BAMUM LETTER PHASE-B PARUM;Lo;0;L;;;;;N;;;;;
+16873;BAMUM LETTER PHASE-B VEUM;Lo;0;L;;;;;N;;;;;
+16874;BAMUM LETTER PHASE-B NGKINDI MVOP;Lo;0;L;;;;;N;;;;;
+16875;BAMUM LETTER PHASE-B NGGEU MBU;Lo;0;L;;;;;N;;;;;
+16876;BAMUM LETTER PHASE-B WUAET;Lo;0;L;;;;;N;;;;;
+16877;BAMUM LETTER PHASE-B SAKEUAE;Lo;0;L;;;;;N;;;;;
+16878;BAMUM LETTER PHASE-B TAAM;Lo;0;L;;;;;N;;;;;
+16879;BAMUM LETTER PHASE-B MEUQ;Lo;0;L;;;;;N;;;;;
+1687A;BAMUM LETTER PHASE-B NGGUOQ;Lo;0;L;;;;;N;;;;;
+1687B;BAMUM LETTER PHASE-B NGGUOQ LARGE;Lo;0;L;;;;;N;;;;;
+1687C;BAMUM LETTER PHASE-B MFIYAQ;Lo;0;L;;;;;N;;;;;
+1687D;BAMUM LETTER PHASE-B SUE;Lo;0;L;;;;;N;;;;;
+1687E;BAMUM LETTER PHASE-B MBEURI;Lo;0;L;;;;;N;;;;;
+1687F;BAMUM LETTER PHASE-B MONTIEEN;Lo;0;L;;;;;N;;;;;
+16880;BAMUM LETTER PHASE-B NYAEMAE;Lo;0;L;;;;;N;;;;;
+16881;BAMUM LETTER PHASE-B PUNGAAM;Lo;0;L;;;;;N;;;;;
+16882;BAMUM LETTER PHASE-B MEUT NGGEET;Lo;0;L;;;;;N;;;;;
+16883;BAMUM LETTER PHASE-B FEUX;Lo;0;L;;;;;N;;;;;
+16884;BAMUM LETTER PHASE-B MBUOQ;Lo;0;L;;;;;N;;;;;
+16885;BAMUM LETTER PHASE-B FEE;Lo;0;L;;;;;N;;;;;
+16886;BAMUM LETTER PHASE-B KEUAEM;Lo;0;L;;;;;N;;;;;
+16887;BAMUM LETTER PHASE-B MA NJEUAENA;Lo;0;L;;;;;N;;;;;
+16888;BAMUM LETTER PHASE-B MA NJUQA;Lo;0;L;;;;;N;;;;;
+16889;BAMUM LETTER PHASE-B LET;Lo;0;L;;;;;N;;;;;
+1688A;BAMUM LETTER PHASE-B NGGAAM;Lo;0;L;;;;;N;;;;;
+1688B;BAMUM LETTER PHASE-B NSEN;Lo;0;L;;;;;N;;;;;
+1688C;BAMUM LETTER PHASE-B MA;Lo;0;L;;;;;N;;;;;
+1688D;BAMUM LETTER PHASE-B KIQ;Lo;0;L;;;;;N;;;;;
+1688E;BAMUM LETTER PHASE-B NGOM;Lo;0;L;;;;;N;;;;;
+1688F;BAMUM LETTER PHASE-C NGKUE MAEMBA;Lo;0;L;;;;;N;;;;;
+16890;BAMUM LETTER PHASE-C NZA;Lo;0;L;;;;;N;;;;;
+16891;BAMUM LETTER PHASE-C YUM;Lo;0;L;;;;;N;;;;;
+16892;BAMUM LETTER PHASE-C WANGKUOQ;Lo;0;L;;;;;N;;;;;
+16893;BAMUM LETTER PHASE-C NGGEN;Lo;0;L;;;;;N;;;;;
+16894;BAMUM LETTER PHASE-C NDEUAEREE;Lo;0;L;;;;;N;;;;;
+16895;BAMUM LETTER PHASE-C NGKAQ;Lo;0;L;;;;;N;;;;;
+16896;BAMUM LETTER PHASE-C GHARAE;Lo;0;L;;;;;N;;;;;
+16897;BAMUM LETTER PHASE-C MBEEKEET;Lo;0;L;;;;;N;;;;;
+16898;BAMUM LETTER PHASE-C GBAYI;Lo;0;L;;;;;N;;;;;
+16899;BAMUM LETTER PHASE-C NYIR MKPARAQ MEUN;Lo;0;L;;;;;N;;;;;
+1689A;BAMUM LETTER PHASE-C NTU MBIT;Lo;0;L;;;;;N;;;;;
+1689B;BAMUM LETTER PHASE-C MBEUM;Lo;0;L;;;;;N;;;;;
+1689C;BAMUM LETTER PHASE-C PIRIEEN;Lo;0;L;;;;;N;;;;;
+1689D;BAMUM LETTER PHASE-C NDOMBU;Lo;0;L;;;;;N;;;;;
+1689E;BAMUM LETTER PHASE-C MBAA CABBAGE-TREE;Lo;0;L;;;;;N;;;;;
+1689F;BAMUM LETTER PHASE-C KEUSHEUAEP;Lo;0;L;;;;;N;;;;;
+168A0;BAMUM LETTER PHASE-C GHAP;Lo;0;L;;;;;N;;;;;
+168A1;BAMUM LETTER PHASE-C KEUKAQ;Lo;0;L;;;;;N;;;;;
+168A2;BAMUM LETTER PHASE-C YU MUOMAE;Lo;0;L;;;;;N;;;;;
+168A3;BAMUM LETTER PHASE-C NZEUM;Lo;0;L;;;;;N;;;;;
+168A4;BAMUM LETTER PHASE-C MBUE;Lo;0;L;;;;;N;;;;;
+168A5;BAMUM LETTER PHASE-C NSEUAEN;Lo;0;L;;;;;N;;;;;
+168A6;BAMUM LETTER PHASE-C MBIT;Lo;0;L;;;;;N;;;;;
+168A7;BAMUM LETTER PHASE-C YEUQ;Lo;0;L;;;;;N;;;;;
+168A8;BAMUM LETTER PHASE-C KPARAQ;Lo;0;L;;;;;N;;;;;
+168A9;BAMUM LETTER PHASE-C KAA;Lo;0;L;;;;;N;;;;;
+168AA;BAMUM LETTER PHASE-C SEUX;Lo;0;L;;;;;N;;;;;
+168AB;BAMUM LETTER PHASE-C NDIDA;Lo;0;L;;;;;N;;;;;
+168AC;BAMUM LETTER PHASE-C TAASHAE;Lo;0;L;;;;;N;;;;;
+168AD;BAMUM LETTER PHASE-C NJUEQ;Lo;0;L;;;;;N;;;;;
+168AE;BAMUM LETTER PHASE-C TITA YUE;Lo;0;L;;;;;N;;;;;
+168AF;BAMUM LETTER PHASE-C SUAET;Lo;0;L;;;;;N;;;;;
+168B0;BAMUM LETTER PHASE-C NGGUAEN NYAM;Lo;0;L;;;;;N;;;;;
+168B1;BAMUM LETTER PHASE-C VEUX;Lo;0;L;;;;;N;;;;;
+168B2;BAMUM LETTER PHASE-C NANSANAQ;Lo;0;L;;;;;N;;;;;
+168B3;BAMUM LETTER PHASE-C MA KEUAERI;Lo;0;L;;;;;N;;;;;
+168B4;BAMUM LETTER PHASE-C NTAA;Lo;0;L;;;;;N;;;;;
+168B5;BAMUM LETTER PHASE-C NGGUON;Lo;0;L;;;;;N;;;;;
+168B6;BAMUM LETTER PHASE-C LAP;Lo;0;L;;;;;N;;;;;
+168B7;BAMUM LETTER PHASE-C MBIRIEEN;Lo;0;L;;;;;N;;;;;
+168B8;BAMUM LETTER PHASE-C MGBASAQ;Lo;0;L;;;;;N;;;;;
+168B9;BAMUM LETTER PHASE-C NTEUNGBA;Lo;0;L;;;;;N;;;;;
+168BA;BAMUM LETTER PHASE-C TEUTEUX;Lo;0;L;;;;;N;;;;;
+168BB;BAMUM LETTER PHASE-C NGGUM;Lo;0;L;;;;;N;;;;;
+168BC;BAMUM LETTER PHASE-C FUE;Lo;0;L;;;;;N;;;;;
+168BD;BAMUM LETTER PHASE-C NDEUT;Lo;0;L;;;;;N;;;;;
+168BE;BAMUM LETTER PHASE-C NSA;Lo;0;L;;;;;N;;;;;
+168BF;BAMUM LETTER PHASE-C NSHAQ;Lo;0;L;;;;;N;;;;;
+168C0;BAMUM LETTER PHASE-C BUNG;Lo;0;L;;;;;N;;;;;
+168C1;BAMUM LETTER PHASE-C VEUAEPEN;Lo;0;L;;;;;N;;;;;
+168C2;BAMUM LETTER PHASE-C MBERAE;Lo;0;L;;;;;N;;;;;
+168C3;BAMUM LETTER PHASE-C RU;Lo;0;L;;;;;N;;;;;
+168C4;BAMUM LETTER PHASE-C NJAEM;Lo;0;L;;;;;N;;;;;
+168C5;BAMUM LETTER PHASE-C LAM;Lo;0;L;;;;;N;;;;;
+168C6;BAMUM LETTER PHASE-C TITUAEP;Lo;0;L;;;;;N;;;;;
+168C7;BAMUM LETTER PHASE-C NSUOT NGOM;Lo;0;L;;;;;N;;;;;
+168C8;BAMUM LETTER PHASE-C NJEEEE;Lo;0;L;;;;;N;;;;;
+168C9;BAMUM LETTER PHASE-C KET;Lo;0;L;;;;;N;;;;;
+168CA;BAMUM LETTER PHASE-C NGGU;Lo;0;L;;;;;N;;;;;
+168CB;BAMUM LETTER PHASE-C MAESI;Lo;0;L;;;;;N;;;;;
+168CC;BAMUM LETTER PHASE-C MBUAEM;Lo;0;L;;;;;N;;;;;
+168CD;BAMUM LETTER PHASE-C LU;Lo;0;L;;;;;N;;;;;
+168CE;BAMUM LETTER PHASE-C KUT;Lo;0;L;;;;;N;;;;;
+168CF;BAMUM LETTER PHASE-C NJAM;Lo;0;L;;;;;N;;;;;
+168D0;BAMUM LETTER PHASE-C NGOM;Lo;0;L;;;;;N;;;;;
+168D1;BAMUM LETTER PHASE-C WUP;Lo;0;L;;;;;N;;;;;
+168D2;BAMUM LETTER PHASE-C NGGUEET;Lo;0;L;;;;;N;;;;;
+168D3;BAMUM LETTER PHASE-C NSOM;Lo;0;L;;;;;N;;;;;
+168D4;BAMUM LETTER PHASE-C NTEN;Lo;0;L;;;;;N;;;;;
+168D5;BAMUM LETTER PHASE-C KUOP NKAARAE;Lo;0;L;;;;;N;;;;;
+168D6;BAMUM LETTER PHASE-C NSUN;Lo;0;L;;;;;N;;;;;
+168D7;BAMUM LETTER PHASE-C NDAM;Lo;0;L;;;;;N;;;;;
+168D8;BAMUM LETTER PHASE-C MA NSIEE;Lo;0;L;;;;;N;;;;;
+168D9;BAMUM LETTER PHASE-C YAA;Lo;0;L;;;;;N;;;;;
+168DA;BAMUM LETTER PHASE-C NDAP;Lo;0;L;;;;;N;;;;;
+168DB;BAMUM LETTER PHASE-C SHUEQ;Lo;0;L;;;;;N;;;;;
+168DC;BAMUM LETTER PHASE-C SETFON;Lo;0;L;;;;;N;;;;;
+168DD;BAMUM LETTER PHASE-C MBI;Lo;0;L;;;;;N;;;;;
+168DE;BAMUM LETTER PHASE-C MAEMBA;Lo;0;L;;;;;N;;;;;
+168DF;BAMUM LETTER PHASE-C MBANYI;Lo;0;L;;;;;N;;;;;
+168E0;BAMUM LETTER PHASE-C KEUSEUX;Lo;0;L;;;;;N;;;;;
+168E1;BAMUM LETTER PHASE-C MBEUX;Lo;0;L;;;;;N;;;;;
+168E2;BAMUM LETTER PHASE-C KEUM;Lo;0;L;;;;;N;;;;;
+168E3;BAMUM LETTER PHASE-C MBAA PICKET;Lo;0;L;;;;;N;;;;;
+168E4;BAMUM LETTER PHASE-C YUWOQ;Lo;0;L;;;;;N;;;;;
+168E5;BAMUM LETTER PHASE-C NJEUX;Lo;0;L;;;;;N;;;;;
+168E6;BAMUM LETTER PHASE-C MIEE;Lo;0;L;;;;;N;;;;;
+168E7;BAMUM LETTER PHASE-C MUAE;Lo;0;L;;;;;N;;;;;
+168E8;BAMUM LETTER PHASE-C SHIQ;Lo;0;L;;;;;N;;;;;
+168E9;BAMUM LETTER PHASE-C KEN LAW;Lo;0;L;;;;;N;;;;;
+168EA;BAMUM LETTER PHASE-C KEN FATIGUE;Lo;0;L;;;;;N;;;;;
+168EB;BAMUM LETTER PHASE-C NGAQ;Lo;0;L;;;;;N;;;;;
+168EC;BAMUM LETTER PHASE-C NAQ;Lo;0;L;;;;;N;;;;;
+168ED;BAMUM LETTER PHASE-C LIQ;Lo;0;L;;;;;N;;;;;
+168EE;BAMUM LETTER PHASE-C PIN;Lo;0;L;;;;;N;;;;;
+168EF;BAMUM LETTER PHASE-C PEN;Lo;0;L;;;;;N;;;;;
+168F0;BAMUM LETTER PHASE-C TET;Lo;0;L;;;;;N;;;;;
+168F1;BAMUM LETTER PHASE-D MBUO;Lo;0;L;;;;;N;;;;;
+168F2;BAMUM LETTER PHASE-D WAP;Lo;0;L;;;;;N;;;;;
+168F3;BAMUM LETTER PHASE-D NJI;Lo;0;L;;;;;N;;;;;
+168F4;BAMUM LETTER PHASE-D MFON;Lo;0;L;;;;;N;;;;;
+168F5;BAMUM LETTER PHASE-D NJIEE;Lo;0;L;;;;;N;;;;;
+168F6;BAMUM LETTER PHASE-D LIEE;Lo;0;L;;;;;N;;;;;
+168F7;BAMUM LETTER PHASE-D NJEUT;Lo;0;L;;;;;N;;;;;
+168F8;BAMUM LETTER PHASE-D NSHEE;Lo;0;L;;;;;N;;;;;
+168F9;BAMUM LETTER PHASE-D NGGAAMAE;Lo;0;L;;;;;N;;;;;
+168FA;BAMUM LETTER PHASE-D NYAM;Lo;0;L;;;;;N;;;;;
+168FB;BAMUM LETTER PHASE-D WUAEN;Lo;0;L;;;;;N;;;;;
+168FC;BAMUM LETTER PHASE-D NGKUN;Lo;0;L;;;;;N;;;;;
+168FD;BAMUM LETTER PHASE-D SHEE;Lo;0;L;;;;;N;;;;;
+168FE;BAMUM LETTER PHASE-D NGKAP;Lo;0;L;;;;;N;;;;;
+168FF;BAMUM LETTER PHASE-D KEUAETMEUN;Lo;0;L;;;;;N;;;;;
+16900;BAMUM LETTER PHASE-D TEUT;Lo;0;L;;;;;N;;;;;
+16901;BAMUM LETTER PHASE-D SHEUAE;Lo;0;L;;;;;N;;;;;
+16902;BAMUM LETTER PHASE-D NJAP;Lo;0;L;;;;;N;;;;;
+16903;BAMUM LETTER PHASE-D SUE;Lo;0;L;;;;;N;;;;;
+16904;BAMUM LETTER PHASE-D KET;Lo;0;L;;;;;N;;;;;
+16905;BAMUM LETTER PHASE-D YAEMMAE;Lo;0;L;;;;;N;;;;;
+16906;BAMUM LETTER PHASE-D KUOM;Lo;0;L;;;;;N;;;;;
+16907;BAMUM LETTER PHASE-D SAP;Lo;0;L;;;;;N;;;;;
+16908;BAMUM LETTER PHASE-D MFEUT;Lo;0;L;;;;;N;;;;;
+16909;BAMUM LETTER PHASE-D NDEUX;Lo;0;L;;;;;N;;;;;
+1690A;BAMUM LETTER PHASE-D MALEERI;Lo;0;L;;;;;N;;;;;
+1690B;BAMUM LETTER PHASE-D MEUT;Lo;0;L;;;;;N;;;;;
+1690C;BAMUM LETTER PHASE-D SEUAEQ;Lo;0;L;;;;;N;;;;;
+1690D;BAMUM LETTER PHASE-D YEN;Lo;0;L;;;;;N;;;;;
+1690E;BAMUM LETTER PHASE-D NJEUAEM;Lo;0;L;;;;;N;;;;;
+1690F;BAMUM LETTER PHASE-D KEUOT MBUAE;Lo;0;L;;;;;N;;;;;
+16910;BAMUM LETTER PHASE-D NGKEURI;Lo;0;L;;;;;N;;;;;
+16911;BAMUM LETTER PHASE-D TU;Lo;0;L;;;;;N;;;;;
+16912;BAMUM LETTER PHASE-D GHAA;Lo;0;L;;;;;N;;;;;
+16913;BAMUM LETTER PHASE-D NGKYEE;Lo;0;L;;;;;N;;;;;
+16914;BAMUM LETTER PHASE-D FEUFEUAET;Lo;0;L;;;;;N;;;;;
+16915;BAMUM LETTER PHASE-D NDEE;Lo;0;L;;;;;N;;;;;
+16916;BAMUM LETTER PHASE-D MGBOFUM;Lo;0;L;;;;;N;;;;;
+16917;BAMUM LETTER PHASE-D LEUAEP;Lo;0;L;;;;;N;;;;;
+16918;BAMUM LETTER PHASE-D NDON;Lo;0;L;;;;;N;;;;;
+16919;BAMUM LETTER PHASE-D MONI;Lo;0;L;;;;;N;;;;;
+1691A;BAMUM LETTER PHASE-D MGBEUN;Lo;0;L;;;;;N;;;;;
+1691B;BAMUM LETTER PHASE-D PUUT;Lo;0;L;;;;;N;;;;;
+1691C;BAMUM LETTER PHASE-D MGBIEE;Lo;0;L;;;;;N;;;;;
+1691D;BAMUM LETTER PHASE-D MFO;Lo;0;L;;;;;N;;;;;
+1691E;BAMUM LETTER PHASE-D LUM;Lo;0;L;;;;;N;;;;;
+1691F;BAMUM LETTER PHASE-D NSIEEP;Lo;0;L;;;;;N;;;;;
+16920;BAMUM LETTER PHASE-D MBAA;Lo;0;L;;;;;N;;;;;
+16921;BAMUM LETTER PHASE-D KWAET;Lo;0;L;;;;;N;;;;;
+16922;BAMUM LETTER PHASE-D NYET;Lo;0;L;;;;;N;;;;;
+16923;BAMUM LETTER PHASE-D TEUAEN;Lo;0;L;;;;;N;;;;;
+16924;BAMUM LETTER PHASE-D SOT;Lo;0;L;;;;;N;;;;;
+16925;BAMUM LETTER PHASE-D YUWOQ;Lo;0;L;;;;;N;;;;;
+16926;BAMUM LETTER PHASE-D KEUM;Lo;0;L;;;;;N;;;;;
+16927;BAMUM LETTER PHASE-D RAEM;Lo;0;L;;;;;N;;;;;
+16928;BAMUM LETTER PHASE-D TEEEE;Lo;0;L;;;;;N;;;;;
+16929;BAMUM LETTER PHASE-D NGKEUAEQ;Lo;0;L;;;;;N;;;;;
+1692A;BAMUM LETTER PHASE-D MFEUAE;Lo;0;L;;;;;N;;;;;
+1692B;BAMUM LETTER PHASE-D NSIEET;Lo;0;L;;;;;N;;;;;
+1692C;BAMUM LETTER PHASE-D KEUP;Lo;0;L;;;;;N;;;;;
+1692D;BAMUM LETTER PHASE-D PIP;Lo;0;L;;;;;N;;;;;
+1692E;BAMUM LETTER PHASE-D PEUTAE;Lo;0;L;;;;;N;;;;;
+1692F;BAMUM LETTER PHASE-D NYUE;Lo;0;L;;;;;N;;;;;
+16930;BAMUM LETTER PHASE-D LET;Lo;0;L;;;;;N;;;;;
+16931;BAMUM LETTER PHASE-D NGGAAM;Lo;0;L;;;;;N;;;;;
+16932;BAMUM LETTER PHASE-D MFIEE;Lo;0;L;;;;;N;;;;;
+16933;BAMUM LETTER PHASE-D NGGWAEN;Lo;0;L;;;;;N;;;;;
+16934;BAMUM LETTER PHASE-D YUOM;Lo;0;L;;;;;N;;;;;
+16935;BAMUM LETTER PHASE-D PAP;Lo;0;L;;;;;N;;;;;
+16936;BAMUM LETTER PHASE-D YUOP;Lo;0;L;;;;;N;;;;;
+16937;BAMUM LETTER PHASE-D NDAM;Lo;0;L;;;;;N;;;;;
+16938;BAMUM LETTER PHASE-D NTEUM;Lo;0;L;;;;;N;;;;;
+16939;BAMUM LETTER PHASE-D SUAE;Lo;0;L;;;;;N;;;;;
+1693A;BAMUM LETTER PHASE-D KUN;Lo;0;L;;;;;N;;;;;
+1693B;BAMUM LETTER PHASE-D NGGEUX;Lo;0;L;;;;;N;;;;;
+1693C;BAMUM LETTER PHASE-D NGKIEE;Lo;0;L;;;;;N;;;;;
+1693D;BAMUM LETTER PHASE-D TUOT;Lo;0;L;;;;;N;;;;;
+1693E;BAMUM LETTER PHASE-D MEUN;Lo;0;L;;;;;N;;;;;
+1693F;BAMUM LETTER PHASE-D KUQ;Lo;0;L;;;;;N;;;;;
+16940;BAMUM LETTER PHASE-D NSUM;Lo;0;L;;;;;N;;;;;
+16941;BAMUM LETTER PHASE-D TEUN;Lo;0;L;;;;;N;;;;;
+16942;BAMUM LETTER PHASE-D MAENJET;Lo;0;L;;;;;N;;;;;
+16943;BAMUM LETTER PHASE-D NGGAP;Lo;0;L;;;;;N;;;;;
+16944;BAMUM LETTER PHASE-D LEUM;Lo;0;L;;;;;N;;;;;
+16945;BAMUM LETTER PHASE-D NGGUOM;Lo;0;L;;;;;N;;;;;
+16946;BAMUM LETTER PHASE-D NSHUT;Lo;0;L;;;;;N;;;;;
+16947;BAMUM LETTER PHASE-D NJUEQ;Lo;0;L;;;;;N;;;;;
+16948;BAMUM LETTER PHASE-D GHEUAE;Lo;0;L;;;;;N;;;;;
+16949;BAMUM LETTER PHASE-D KU;Lo;0;L;;;;;N;;;;;
+1694A;BAMUM LETTER PHASE-D REN OLD;Lo;0;L;;;;;N;;;;;
+1694B;BAMUM LETTER PHASE-D TAE;Lo;0;L;;;;;N;;;;;
+1694C;BAMUM LETTER PHASE-D TOQ;Lo;0;L;;;;;N;;;;;
+1694D;BAMUM LETTER PHASE-D NYI;Lo;0;L;;;;;N;;;;;
+1694E;BAMUM LETTER PHASE-D RII;Lo;0;L;;;;;N;;;;;
+1694F;BAMUM LETTER PHASE-D LEEEE;Lo;0;L;;;;;N;;;;;
+16950;BAMUM LETTER PHASE-D MEEEE;Lo;0;L;;;;;N;;;;;
+16951;BAMUM LETTER PHASE-D M;Lo;0;L;;;;;N;;;;;
+16952;BAMUM LETTER PHASE-D SUU;Lo;0;L;;;;;N;;;;;
+16953;BAMUM LETTER PHASE-D MU;Lo;0;L;;;;;N;;;;;
+16954;BAMUM LETTER PHASE-D SHII;Lo;0;L;;;;;N;;;;;
+16955;BAMUM LETTER PHASE-D SHEUX;Lo;0;L;;;;;N;;;;;
+16956;BAMUM LETTER PHASE-D KYEE;Lo;0;L;;;;;N;;;;;
+16957;BAMUM LETTER PHASE-D NU;Lo;0;L;;;;;N;;;;;
+16958;BAMUM LETTER PHASE-D SHU;Lo;0;L;;;;;N;;;;;
+16959;BAMUM LETTER PHASE-D NTEE;Lo;0;L;;;;;N;;;;;
+1695A;BAMUM LETTER PHASE-D PEE;Lo;0;L;;;;;N;;;;;
+1695B;BAMUM LETTER PHASE-D NI;Lo;0;L;;;;;N;;;;;
+1695C;BAMUM LETTER PHASE-D SHOQ;Lo;0;L;;;;;N;;;;;
+1695D;BAMUM LETTER PHASE-D PUQ;Lo;0;L;;;;;N;;;;;
+1695E;BAMUM LETTER PHASE-D MVOP;Lo;0;L;;;;;N;;;;;
+1695F;BAMUM LETTER PHASE-D LOQ;Lo;0;L;;;;;N;;;;;
+16960;BAMUM LETTER PHASE-D REN MUCH;Lo;0;L;;;;;N;;;;;
+16961;BAMUM LETTER PHASE-D TI;Lo;0;L;;;;;N;;;;;
+16962;BAMUM LETTER PHASE-D NTUU;Lo;0;L;;;;;N;;;;;
+16963;BAMUM LETTER PHASE-D MBAA SEVEN;Lo;0;L;;;;;N;;;;;
+16964;BAMUM LETTER PHASE-D SAQ;Lo;0;L;;;;;N;;;;;
+16965;BAMUM LETTER PHASE-D FAA;Lo;0;L;;;;;N;;;;;
+16966;BAMUM LETTER PHASE-E NDAP;Lo;0;L;;;;;N;;;;;
+16967;BAMUM LETTER PHASE-E TOON;Lo;0;L;;;;;N;;;;;
+16968;BAMUM LETTER PHASE-E MBEUM;Lo;0;L;;;;;N;;;;;
+16969;BAMUM LETTER PHASE-E LAP;Lo;0;L;;;;;N;;;;;
+1696A;BAMUM LETTER PHASE-E VOM;Lo;0;L;;;;;N;;;;;
+1696B;BAMUM LETTER PHASE-E LOON;Lo;0;L;;;;;N;;;;;
+1696C;BAMUM LETTER PHASE-E PAA;Lo;0;L;;;;;N;;;;;
+1696D;BAMUM LETTER PHASE-E SOM;Lo;0;L;;;;;N;;;;;
+1696E;BAMUM LETTER PHASE-E RAQ;Lo;0;L;;;;;N;;;;;
+1696F;BAMUM LETTER PHASE-E NSHUOP;Lo;0;L;;;;;N;;;;;
+16970;BAMUM LETTER PHASE-E NDUN;Lo;0;L;;;;;N;;;;;
+16971;BAMUM LETTER PHASE-E PUAE;Lo;0;L;;;;;N;;;;;
+16972;BAMUM LETTER PHASE-E TAM;Lo;0;L;;;;;N;;;;;
+16973;BAMUM LETTER PHASE-E NGKA;Lo;0;L;;;;;N;;;;;
+16974;BAMUM LETTER PHASE-E KPEUX;Lo;0;L;;;;;N;;;;;
+16975;BAMUM LETTER PHASE-E WUO;Lo;0;L;;;;;N;;;;;
+16976;BAMUM LETTER PHASE-E SEE;Lo;0;L;;;;;N;;;;;
+16977;BAMUM LETTER PHASE-E NGGEUAET;Lo;0;L;;;;;N;;;;;
+16978;BAMUM LETTER PHASE-E PAAM;Lo;0;L;;;;;N;;;;;
+16979;BAMUM LETTER PHASE-E TOO;Lo;0;L;;;;;N;;;;;
+1697A;BAMUM LETTER PHASE-E KUOP;Lo;0;L;;;;;N;;;;;
+1697B;BAMUM LETTER PHASE-E LOM;Lo;0;L;;;;;N;;;;;
+1697C;BAMUM LETTER PHASE-E NSHIEE;Lo;0;L;;;;;N;;;;;
+1697D;BAMUM LETTER PHASE-E NGOP;Lo;0;L;;;;;N;;;;;
+1697E;BAMUM LETTER PHASE-E MAEM;Lo;0;L;;;;;N;;;;;
+1697F;BAMUM LETTER PHASE-E NGKEUX;Lo;0;L;;;;;N;;;;;
+16980;BAMUM LETTER PHASE-E NGOQ;Lo;0;L;;;;;N;;;;;
+16981;BAMUM LETTER PHASE-E NSHUE;Lo;0;L;;;;;N;;;;;
+16982;BAMUM LETTER PHASE-E RIMGBA;Lo;0;L;;;;;N;;;;;
+16983;BAMUM LETTER PHASE-E NJEUX;Lo;0;L;;;;;N;;;;;
+16984;BAMUM LETTER PHASE-E PEEM;Lo;0;L;;;;;N;;;;;
+16985;BAMUM LETTER PHASE-E SAA;Lo;0;L;;;;;N;;;;;
+16986;BAMUM LETTER PHASE-E NGGURAE;Lo;0;L;;;;;N;;;;;
+16987;BAMUM LETTER PHASE-E MGBA;Lo;0;L;;;;;N;;;;;
+16988;BAMUM LETTER PHASE-E GHEUX;Lo;0;L;;;;;N;;;;;
+16989;BAMUM LETTER PHASE-E NGKEUAEM;Lo;0;L;;;;;N;;;;;
+1698A;BAMUM LETTER PHASE-E NJAEMLI;Lo;0;L;;;;;N;;;;;
+1698B;BAMUM LETTER PHASE-E MAP;Lo;0;L;;;;;N;;;;;
+1698C;BAMUM LETTER PHASE-E LOOT;Lo;0;L;;;;;N;;;;;
+1698D;BAMUM LETTER PHASE-E NGGEEEE;Lo;0;L;;;;;N;;;;;
+1698E;BAMUM LETTER PHASE-E NDIQ;Lo;0;L;;;;;N;;;;;
+1698F;BAMUM LETTER PHASE-E TAEN NTEUM;Lo;0;L;;;;;N;;;;;
+16990;BAMUM LETTER PHASE-E SET;Lo;0;L;;;;;N;;;;;
+16991;BAMUM LETTER PHASE-E PUM;Lo;0;L;;;;;N;;;;;
+16992;BAMUM LETTER PHASE-E NDAA SOFTNESS;Lo;0;L;;;;;N;;;;;
+16993;BAMUM LETTER PHASE-E NGGUAESHAE NYAM;Lo;0;L;;;;;N;;;;;
+16994;BAMUM LETTER PHASE-E YIEE;Lo;0;L;;;;;N;;;;;
+16995;BAMUM LETTER PHASE-E GHEUN;Lo;0;L;;;;;N;;;;;
+16996;BAMUM LETTER PHASE-E TUAE;Lo;0;L;;;;;N;;;;;
+16997;BAMUM LETTER PHASE-E YEUAE;Lo;0;L;;;;;N;;;;;
+16998;BAMUM LETTER PHASE-E PO;Lo;0;L;;;;;N;;;;;
+16999;BAMUM LETTER PHASE-E TUMAE;Lo;0;L;;;;;N;;;;;
+1699A;BAMUM LETTER PHASE-E KEUAE;Lo;0;L;;;;;N;;;;;
+1699B;BAMUM LETTER PHASE-E SUAEN;Lo;0;L;;;;;N;;;;;
+1699C;BAMUM LETTER PHASE-E TEUAEQ;Lo;0;L;;;;;N;;;;;
+1699D;BAMUM LETTER PHASE-E VEUAE;Lo;0;L;;;;;N;;;;;
+1699E;BAMUM LETTER PHASE-E WEUX;Lo;0;L;;;;;N;;;;;
+1699F;BAMUM LETTER PHASE-E LAAM;Lo;0;L;;;;;N;;;;;
+169A0;BAMUM LETTER PHASE-E PU;Lo;0;L;;;;;N;;;;;
+169A1;BAMUM LETTER PHASE-E TAAQ;Lo;0;L;;;;;N;;;;;
+169A2;BAMUM LETTER PHASE-E GHAAMAE;Lo;0;L;;;;;N;;;;;
+169A3;BAMUM LETTER PHASE-E NGEUREUT;Lo;0;L;;;;;N;;;;;
+169A4;BAMUM LETTER PHASE-E SHEUAEQ;Lo;0;L;;;;;N;;;;;
+169A5;BAMUM LETTER PHASE-E MGBEN;Lo;0;L;;;;;N;;;;;
+169A6;BAMUM LETTER PHASE-E MBEE;Lo;0;L;;;;;N;;;;;
+169A7;BAMUM LETTER PHASE-E NZAQ;Lo;0;L;;;;;N;;;;;
+169A8;BAMUM LETTER PHASE-E NKOM;Lo;0;L;;;;;N;;;;;
+169A9;BAMUM LETTER PHASE-E GBET;Lo;0;L;;;;;N;;;;;
+169AA;BAMUM LETTER PHASE-E TUM;Lo;0;L;;;;;N;;;;;
+169AB;BAMUM LETTER PHASE-E KUET;Lo;0;L;;;;;N;;;;;
+169AC;BAMUM LETTER PHASE-E YAP;Lo;0;L;;;;;N;;;;;
+169AD;BAMUM LETTER PHASE-E NYI CLEAVER;Lo;0;L;;;;;N;;;;;
+169AE;BAMUM LETTER PHASE-E YIT;Lo;0;L;;;;;N;;;;;
+169AF;BAMUM LETTER PHASE-E MFEUQ;Lo;0;L;;;;;N;;;;;
+169B0;BAMUM LETTER PHASE-E NDIAQ;Lo;0;L;;;;;N;;;;;
+169B1;BAMUM LETTER PHASE-E PIEEQ;Lo;0;L;;;;;N;;;;;
+169B2;BAMUM LETTER PHASE-E YUEQ;Lo;0;L;;;;;N;;;;;
+169B3;BAMUM LETTER PHASE-E LEUAEM;Lo;0;L;;;;;N;;;;;
+169B4;BAMUM LETTER PHASE-E FUE;Lo;0;L;;;;;N;;;;;
+169B5;BAMUM LETTER PHASE-E GBEUX;Lo;0;L;;;;;N;;;;;
+169B6;BAMUM LETTER PHASE-E NGKUP;Lo;0;L;;;;;N;;;;;
+169B7;BAMUM LETTER PHASE-E KET;Lo;0;L;;;;;N;;;;;
+169B8;BAMUM LETTER PHASE-E MAE;Lo;0;L;;;;;N;;;;;
+169B9;BAMUM LETTER PHASE-E NGKAAMI;Lo;0;L;;;;;N;;;;;
+169BA;BAMUM LETTER PHASE-E GHET;Lo;0;L;;;;;N;;;;;
+169BB;BAMUM LETTER PHASE-E FA;Lo;0;L;;;;;N;;;;;
+169BC;BAMUM LETTER PHASE-E NTUM;Lo;0;L;;;;;N;;;;;
+169BD;BAMUM LETTER PHASE-E PEUT;Lo;0;L;;;;;N;;;;;
+169BE;BAMUM LETTER PHASE-E YEUM;Lo;0;L;;;;;N;;;;;
+169BF;BAMUM LETTER PHASE-E NGGEUAE;Lo;0;L;;;;;N;;;;;
+169C0;BAMUM LETTER PHASE-E NYI BETWEEN;Lo;0;L;;;;;N;;;;;
+169C1;BAMUM LETTER PHASE-E NZUQ;Lo;0;L;;;;;N;;;;;
+169C2;BAMUM LETTER PHASE-E POON;Lo;0;L;;;;;N;;;;;
+169C3;BAMUM LETTER PHASE-E MIEE;Lo;0;L;;;;;N;;;;;
+169C4;BAMUM LETTER PHASE-E FUET;Lo;0;L;;;;;N;;;;;
+169C5;BAMUM LETTER PHASE-E NAE;Lo;0;L;;;;;N;;;;;
+169C6;BAMUM LETTER PHASE-E MUAE;Lo;0;L;;;;;N;;;;;
+169C7;BAMUM LETTER PHASE-E GHEUAE;Lo;0;L;;;;;N;;;;;
+169C8;BAMUM LETTER PHASE-E FU I;Lo;0;L;;;;;N;;;;;
+169C9;BAMUM LETTER PHASE-E MVI;Lo;0;L;;;;;N;;;;;
+169CA;BAMUM LETTER PHASE-E PUAQ;Lo;0;L;;;;;N;;;;;
+169CB;BAMUM LETTER PHASE-E NGKUM;Lo;0;L;;;;;N;;;;;
+169CC;BAMUM LETTER PHASE-E KUT;Lo;0;L;;;;;N;;;;;
+169CD;BAMUM LETTER PHASE-E PIET;Lo;0;L;;;;;N;;;;;
+169CE;BAMUM LETTER PHASE-E NTAP;Lo;0;L;;;;;N;;;;;
+169CF;BAMUM LETTER PHASE-E YEUAET;Lo;0;L;;;;;N;;;;;
+169D0;BAMUM LETTER PHASE-E NGGUP;Lo;0;L;;;;;N;;;;;
+169D1;BAMUM LETTER PHASE-E PA PEOPLE;Lo;0;L;;;;;N;;;;;
+169D2;BAMUM LETTER PHASE-E FU CALL;Lo;0;L;;;;;N;;;;;
+169D3;BAMUM LETTER PHASE-E FOM;Lo;0;L;;;;;N;;;;;
+169D4;BAMUM LETTER PHASE-E NJEE;Lo;0;L;;;;;N;;;;;
+169D5;BAMUM LETTER PHASE-E A;Lo;0;L;;;;;N;;;;;
+169D6;BAMUM LETTER PHASE-E TOQ;Lo;0;L;;;;;N;;;;;
+169D7;BAMUM LETTER PHASE-E O;Lo;0;L;;;;;N;;;;;
+169D8;BAMUM LETTER PHASE-E I;Lo;0;L;;;;;N;;;;;
+169D9;BAMUM LETTER PHASE-E LAQ;Lo;0;L;;;;;N;;;;;
+169DA;BAMUM LETTER PHASE-E PA PLURAL;Lo;0;L;;;;;N;;;;;
+169DB;BAMUM LETTER PHASE-E TAA;Lo;0;L;;;;;N;;;;;
+169DC;BAMUM LETTER PHASE-E TAQ;Lo;0;L;;;;;N;;;;;
+169DD;BAMUM LETTER PHASE-E NDAA MY HOUSE;Lo;0;L;;;;;N;;;;;
+169DE;BAMUM LETTER PHASE-E SHIQ;Lo;0;L;;;;;N;;;;;
+169DF;BAMUM LETTER PHASE-E YEUX;Lo;0;L;;;;;N;;;;;
+169E0;BAMUM LETTER PHASE-E NGUAE;Lo;0;L;;;;;N;;;;;
+169E1;BAMUM LETTER PHASE-E YUAEN;Lo;0;L;;;;;N;;;;;
+169E2;BAMUM LETTER PHASE-E YOQ SWIMMING;Lo;0;L;;;;;N;;;;;
+169E3;BAMUM LETTER PHASE-E YOQ COVER;Lo;0;L;;;;;N;;;;;
+169E4;BAMUM LETTER PHASE-E YUQ;Lo;0;L;;;;;N;;;;;
+169E5;BAMUM LETTER PHASE-E YUN;Lo;0;L;;;;;N;;;;;
+169E6;BAMUM LETTER PHASE-E KEUX;Lo;0;L;;;;;N;;;;;
+169E7;BAMUM LETTER PHASE-E PEUX;Lo;0;L;;;;;N;;;;;
+169E8;BAMUM LETTER PHASE-E NJEE EPOCH;Lo;0;L;;;;;N;;;;;
+169E9;BAMUM LETTER PHASE-E PUE;Lo;0;L;;;;;N;;;;;
+169EA;BAMUM LETTER PHASE-E WUE;Lo;0;L;;;;;N;;;;;
+169EB;BAMUM LETTER PHASE-E FEE;Lo;0;L;;;;;N;;;;;
+169EC;BAMUM LETTER PHASE-E VEE;Lo;0;L;;;;;N;;;;;
+169ED;BAMUM LETTER PHASE-E LU;Lo;0;L;;;;;N;;;;;
+169EE;BAMUM LETTER PHASE-E MI;Lo;0;L;;;;;N;;;;;
+169EF;BAMUM LETTER PHASE-E REUX;Lo;0;L;;;;;N;;;;;
+169F0;BAMUM LETTER PHASE-E RAE;Lo;0;L;;;;;N;;;;;
+169F1;BAMUM LETTER PHASE-E NGUAET;Lo;0;L;;;;;N;;;;;
+169F2;BAMUM LETTER PHASE-E NGA;Lo;0;L;;;;;N;;;;;
+169F3;BAMUM LETTER PHASE-E SHO;Lo;0;L;;;;;N;;;;;
+169F4;BAMUM LETTER PHASE-E SHOQ;Lo;0;L;;;;;N;;;;;
+169F5;BAMUM LETTER PHASE-E FU REMEDY;Lo;0;L;;;;;N;;;;;
+169F6;BAMUM LETTER PHASE-E NA;Lo;0;L;;;;;N;;;;;
+169F7;BAMUM LETTER PHASE-E PI;Lo;0;L;;;;;N;;;;;
+169F8;BAMUM LETTER PHASE-E LOQ;Lo;0;L;;;;;N;;;;;
+169F9;BAMUM LETTER PHASE-E KO;Lo;0;L;;;;;N;;;;;
+169FA;BAMUM LETTER PHASE-E MEN;Lo;0;L;;;;;N;;;;;
+169FB;BAMUM LETTER PHASE-E MA;Lo;0;L;;;;;N;;;;;
+169FC;BAMUM LETTER PHASE-E MAQ;Lo;0;L;;;;;N;;;;;
+169FD;BAMUM LETTER PHASE-E TEU;Lo;0;L;;;;;N;;;;;
+169FE;BAMUM LETTER PHASE-E KI;Lo;0;L;;;;;N;;;;;
+169FF;BAMUM LETTER PHASE-E MON;Lo;0;L;;;;;N;;;;;
+16A00;BAMUM LETTER PHASE-E TEN;Lo;0;L;;;;;N;;;;;
+16A01;BAMUM LETTER PHASE-E FAQ;Lo;0;L;;;;;N;;;;;
+16A02;BAMUM LETTER PHASE-E GHOM;Lo;0;L;;;;;N;;;;;
+16A03;BAMUM LETTER PHASE-F KA;Lo;0;L;;;;;N;;;;;
+16A04;BAMUM LETTER PHASE-F U;Lo;0;L;;;;;N;;;;;
+16A05;BAMUM LETTER PHASE-F KU;Lo;0;L;;;;;N;;;;;
+16A06;BAMUM LETTER PHASE-F EE;Lo;0;L;;;;;N;;;;;
+16A07;BAMUM LETTER PHASE-F REE;Lo;0;L;;;;;N;;;;;
+16A08;BAMUM LETTER PHASE-F TAE;Lo;0;L;;;;;N;;;;;
+16A09;BAMUM LETTER PHASE-F NYI;Lo;0;L;;;;;N;;;;;
+16A0A;BAMUM LETTER PHASE-F LA;Lo;0;L;;;;;N;;;;;
+16A0B;BAMUM LETTER PHASE-F RII;Lo;0;L;;;;;N;;;;;
+16A0C;BAMUM LETTER PHASE-F RIEE;Lo;0;L;;;;;N;;;;;
+16A0D;BAMUM LETTER PHASE-F MEEEE;Lo;0;L;;;;;N;;;;;
+16A0E;BAMUM LETTER PHASE-F TAA;Lo;0;L;;;;;N;;;;;
+16A0F;BAMUM LETTER PHASE-F NDAA;Lo;0;L;;;;;N;;;;;
+16A10;BAMUM LETTER PHASE-F NJAEM;Lo;0;L;;;;;N;;;;;
+16A11;BAMUM LETTER PHASE-F M;Lo;0;L;;;;;N;;;;;
+16A12;BAMUM LETTER PHASE-F SUU;Lo;0;L;;;;;N;;;;;
+16A13;BAMUM LETTER PHASE-F SHII;Lo;0;L;;;;;N;;;;;
+16A14;BAMUM LETTER PHASE-F SI;Lo;0;L;;;;;N;;;;;
+16A15;BAMUM LETTER PHASE-F SEUX;Lo;0;L;;;;;N;;;;;
+16A16;BAMUM LETTER PHASE-F KYEE;Lo;0;L;;;;;N;;;;;
+16A17;BAMUM LETTER PHASE-F KET;Lo;0;L;;;;;N;;;;;
+16A18;BAMUM LETTER PHASE-F NUAE;Lo;0;L;;;;;N;;;;;
+16A19;BAMUM LETTER PHASE-F NU;Lo;0;L;;;;;N;;;;;
+16A1A;BAMUM LETTER PHASE-F NJUAE;Lo;0;L;;;;;N;;;;;
+16A1B;BAMUM LETTER PHASE-F YOQ;Lo;0;L;;;;;N;;;;;
+16A1C;BAMUM LETTER PHASE-F SHU;Lo;0;L;;;;;N;;;;;
+16A1D;BAMUM LETTER PHASE-F YA;Lo;0;L;;;;;N;;;;;
+16A1E;BAMUM LETTER PHASE-F NSHA;Lo;0;L;;;;;N;;;;;
+16A1F;BAMUM LETTER PHASE-F PEUX;Lo;0;L;;;;;N;;;;;
+16A20;BAMUM LETTER PHASE-F NTEE;Lo;0;L;;;;;N;;;;;
+16A21;BAMUM LETTER PHASE-F WUE;Lo;0;L;;;;;N;;;;;
+16A22;BAMUM LETTER PHASE-F PEE;Lo;0;L;;;;;N;;;;;
+16A23;BAMUM LETTER PHASE-F RU;Lo;0;L;;;;;N;;;;;
+16A24;BAMUM LETTER PHASE-F NI;Lo;0;L;;;;;N;;;;;
+16A25;BAMUM LETTER PHASE-F REUX;Lo;0;L;;;;;N;;;;;
+16A26;BAMUM LETTER PHASE-F KEN;Lo;0;L;;;;;N;;;;;
+16A27;BAMUM LETTER PHASE-F NGKWAEN;Lo;0;L;;;;;N;;;;;
+16A28;BAMUM LETTER PHASE-F NGGA;Lo;0;L;;;;;N;;;;;
+16A29;BAMUM LETTER PHASE-F SHO;Lo;0;L;;;;;N;;;;;
+16A2A;BAMUM LETTER PHASE-F PUAE;Lo;0;L;;;;;N;;;;;
+16A2B;BAMUM LETTER PHASE-F FOM;Lo;0;L;;;;;N;;;;;
+16A2C;BAMUM LETTER PHASE-F WA;Lo;0;L;;;;;N;;;;;
+16A2D;BAMUM LETTER PHASE-F LI;Lo;0;L;;;;;N;;;;;
+16A2E;BAMUM LETTER PHASE-F LOQ;Lo;0;L;;;;;N;;;;;
+16A2F;BAMUM LETTER PHASE-F KO;Lo;0;L;;;;;N;;;;;
+16A30;BAMUM LETTER PHASE-F MBEN;Lo;0;L;;;;;N;;;;;
+16A31;BAMUM LETTER PHASE-F REN;Lo;0;L;;;;;N;;;;;
+16A32;BAMUM LETTER PHASE-F MA;Lo;0;L;;;;;N;;;;;
+16A33;BAMUM LETTER PHASE-F MO;Lo;0;L;;;;;N;;;;;
+16A34;BAMUM LETTER PHASE-F MBAA;Lo;0;L;;;;;N;;;;;
+16A35;BAMUM LETTER PHASE-F TET;Lo;0;L;;;;;N;;;;;
+16A36;BAMUM LETTER PHASE-F KPA;Lo;0;L;;;;;N;;;;;
+16A37;BAMUM LETTER PHASE-F SAMBA;Lo;0;L;;;;;N;;;;;
+16A38;BAMUM LETTER PHASE-F VUEQ;Lo;0;L;;;;;N;;;;;
+16A40;MRO LETTER TA;Lo;0;L;;;;;N;;;;;
+16A41;MRO LETTER NGI;Lo;0;L;;;;;N;;;;;
+16A42;MRO LETTER YO;Lo;0;L;;;;;N;;;;;
+16A43;MRO LETTER MIM;Lo;0;L;;;;;N;;;;;
+16A44;MRO LETTER BA;Lo;0;L;;;;;N;;;;;
+16A45;MRO LETTER DA;Lo;0;L;;;;;N;;;;;
+16A46;MRO LETTER A;Lo;0;L;;;;;N;;;;;
+16A47;MRO LETTER PHI;Lo;0;L;;;;;N;;;;;
+16A48;MRO LETTER KHAI;Lo;0;L;;;;;N;;;;;
+16A49;MRO LETTER HAO;Lo;0;L;;;;;N;;;;;
+16A4A;MRO LETTER DAI;Lo;0;L;;;;;N;;;;;
+16A4B;MRO LETTER CHU;Lo;0;L;;;;;N;;;;;
+16A4C;MRO LETTER KEAAE;Lo;0;L;;;;;N;;;;;
+16A4D;MRO LETTER OL;Lo;0;L;;;;;N;;;;;
+16A4E;MRO LETTER MAEM;Lo;0;L;;;;;N;;;;;
+16A4F;MRO LETTER NIN;Lo;0;L;;;;;N;;;;;
+16A50;MRO LETTER PA;Lo;0;L;;;;;N;;;;;
+16A51;MRO LETTER OO;Lo;0;L;;;;;N;;;;;
+16A52;MRO LETTER O;Lo;0;L;;;;;N;;;;;
+16A53;MRO LETTER RO;Lo;0;L;;;;;N;;;;;
+16A54;MRO LETTER SHI;Lo;0;L;;;;;N;;;;;
+16A55;MRO LETTER THEA;Lo;0;L;;;;;N;;;;;
+16A56;MRO LETTER EA;Lo;0;L;;;;;N;;;;;
+16A57;MRO LETTER WA;Lo;0;L;;;;;N;;;;;
+16A58;MRO LETTER E;Lo;0;L;;;;;N;;;;;
+16A59;MRO LETTER KO;Lo;0;L;;;;;N;;;;;
+16A5A;MRO LETTER LAN;Lo;0;L;;;;;N;;;;;
+16A5B;MRO LETTER LA;Lo;0;L;;;;;N;;;;;
+16A5C;MRO LETTER HAI;Lo;0;L;;;;;N;;;;;
+16A5D;MRO LETTER RI;Lo;0;L;;;;;N;;;;;
+16A5E;MRO LETTER TEK;Lo;0;L;;;;;N;;;;;
+16A60;MRO DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+16A61;MRO DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+16A62;MRO DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+16A63;MRO DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+16A64;MRO DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+16A65;MRO DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+16A66;MRO DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+16A67;MRO DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+16A68;MRO DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+16A69;MRO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+16A6E;MRO DANDA;Po;0;L;;;;;N;;;;;
+16A6F;MRO DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+16AD0;BASSA VAH LETTER ENNI;Lo;0;L;;;;;N;;;;;
+16AD1;BASSA VAH LETTER KA;Lo;0;L;;;;;N;;;;;
+16AD2;BASSA VAH LETTER SE;Lo;0;L;;;;;N;;;;;
+16AD3;BASSA VAH LETTER FA;Lo;0;L;;;;;N;;;;;
+16AD4;BASSA VAH LETTER MBE;Lo;0;L;;;;;N;;;;;
+16AD5;BASSA VAH LETTER YIE;Lo;0;L;;;;;N;;;;;
+16AD6;BASSA VAH LETTER GAH;Lo;0;L;;;;;N;;;;;
+16AD7;BASSA VAH LETTER DHII;Lo;0;L;;;;;N;;;;;
+16AD8;BASSA VAH LETTER KPAH;Lo;0;L;;;;;N;;;;;
+16AD9;BASSA VAH LETTER JO;Lo;0;L;;;;;N;;;;;
+16ADA;BASSA VAH LETTER HWAH;Lo;0;L;;;;;N;;;;;
+16ADB;BASSA VAH LETTER WA;Lo;0;L;;;;;N;;;;;
+16ADC;BASSA VAH LETTER ZO;Lo;0;L;;;;;N;;;;;
+16ADD;BASSA VAH LETTER GBU;Lo;0;L;;;;;N;;;;;
+16ADE;BASSA VAH LETTER DO;Lo;0;L;;;;;N;;;;;
+16ADF;BASSA VAH LETTER CE;Lo;0;L;;;;;N;;;;;
+16AE0;BASSA VAH LETTER UWU;Lo;0;L;;;;;N;;;;;
+16AE1;BASSA VAH LETTER TO;Lo;0;L;;;;;N;;;;;
+16AE2;BASSA VAH LETTER BA;Lo;0;L;;;;;N;;;;;
+16AE3;BASSA VAH LETTER VU;Lo;0;L;;;;;N;;;;;
+16AE4;BASSA VAH LETTER YEIN;Lo;0;L;;;;;N;;;;;
+16AE5;BASSA VAH LETTER PA;Lo;0;L;;;;;N;;;;;
+16AE6;BASSA VAH LETTER WADDA;Lo;0;L;;;;;N;;;;;
+16AE7;BASSA VAH LETTER A;Lo;0;L;;;;;N;;;;;
+16AE8;BASSA VAH LETTER O;Lo;0;L;;;;;N;;;;;
+16AE9;BASSA VAH LETTER OO;Lo;0;L;;;;;N;;;;;
+16AEA;BASSA VAH LETTER U;Lo;0;L;;;;;N;;;;;
+16AEB;BASSA VAH LETTER EE;Lo;0;L;;;;;N;;;;;
+16AEC;BASSA VAH LETTER E;Lo;0;L;;;;;N;;;;;
+16AED;BASSA VAH LETTER I;Lo;0;L;;;;;N;;;;;
+16AF0;BASSA VAH COMBINING HIGH TONE;Mn;1;NSM;;;;;N;;;;;
+16AF1;BASSA VAH COMBINING LOW TONE;Mn;1;NSM;;;;;N;;;;;
+16AF2;BASSA VAH COMBINING MID TONE;Mn;1;NSM;;;;;N;;;;;
+16AF3;BASSA VAH COMBINING LOW-MID TONE;Mn;1;NSM;;;;;N;;;;;
+16AF4;BASSA VAH COMBINING HIGH-LOW TONE;Mn;1;NSM;;;;;N;;;;;
+16AF5;BASSA VAH FULL STOP;Po;0;L;;;;;N;;;;;
+16B00;PAHAWH HMONG VOWEL KEEB;Lo;0;L;;;;;N;;;;;
+16B01;PAHAWH HMONG VOWEL KEEV;Lo;0;L;;;;;N;;;;;
+16B02;PAHAWH HMONG VOWEL KIB;Lo;0;L;;;;;N;;;;;
+16B03;PAHAWH HMONG VOWEL KIV;Lo;0;L;;;;;N;;;;;
+16B04;PAHAWH HMONG VOWEL KAUB;Lo;0;L;;;;;N;;;;;
+16B05;PAHAWH HMONG VOWEL KAUV;Lo;0;L;;;;;N;;;;;
+16B06;PAHAWH HMONG VOWEL KUB;Lo;0;L;;;;;N;;;;;
+16B07;PAHAWH HMONG VOWEL KUV;Lo;0;L;;;;;N;;;;;
+16B08;PAHAWH HMONG VOWEL KEB;Lo;0;L;;;;;N;;;;;
+16B09;PAHAWH HMONG VOWEL KEV;Lo;0;L;;;;;N;;;;;
+16B0A;PAHAWH HMONG VOWEL KAIB;Lo;0;L;;;;;N;;;;;
+16B0B;PAHAWH HMONG VOWEL KAIV;Lo;0;L;;;;;N;;;;;
+16B0C;PAHAWH HMONG VOWEL KOOB;Lo;0;L;;;;;N;;;;;
+16B0D;PAHAWH HMONG VOWEL KOOV;Lo;0;L;;;;;N;;;;;
+16B0E;PAHAWH HMONG VOWEL KAWB;Lo;0;L;;;;;N;;;;;
+16B0F;PAHAWH HMONG VOWEL KAWV;Lo;0;L;;;;;N;;;;;
+16B10;PAHAWH HMONG VOWEL KUAB;Lo;0;L;;;;;N;;;;;
+16B11;PAHAWH HMONG VOWEL KUAV;Lo;0;L;;;;;N;;;;;
+16B12;PAHAWH HMONG VOWEL KOB;Lo;0;L;;;;;N;;;;;
+16B13;PAHAWH HMONG VOWEL KOV;Lo;0;L;;;;;N;;;;;
+16B14;PAHAWH HMONG VOWEL KIAB;Lo;0;L;;;;;N;;;;;
+16B15;PAHAWH HMONG VOWEL KIAV;Lo;0;L;;;;;N;;;;;
+16B16;PAHAWH HMONG VOWEL KAB;Lo;0;L;;;;;N;;;;;
+16B17;PAHAWH HMONG VOWEL KAV;Lo;0;L;;;;;N;;;;;
+16B18;PAHAWH HMONG VOWEL KWB;Lo;0;L;;;;;N;;;;;
+16B19;PAHAWH HMONG VOWEL KWV;Lo;0;L;;;;;N;;;;;
+16B1A;PAHAWH HMONG VOWEL KAAB;Lo;0;L;;;;;N;;;;;
+16B1B;PAHAWH HMONG VOWEL KAAV;Lo;0;L;;;;;N;;;;;
+16B1C;PAHAWH HMONG CONSONANT VAU;Lo;0;L;;;;;N;;;;;
+16B1D;PAHAWH HMONG CONSONANT NTSAU;Lo;0;L;;;;;N;;;;;
+16B1E;PAHAWH HMONG CONSONANT LAU;Lo;0;L;;;;;N;;;;;
+16B1F;PAHAWH HMONG CONSONANT HAU;Lo;0;L;;;;;N;;;;;
+16B20;PAHAWH HMONG CONSONANT NLAU;Lo;0;L;;;;;N;;;;;
+16B21;PAHAWH HMONG CONSONANT RAU;Lo;0;L;;;;;N;;;;;
+16B22;PAHAWH HMONG CONSONANT NKAU;Lo;0;L;;;;;N;;;;;
+16B23;PAHAWH HMONG CONSONANT QHAU;Lo;0;L;;;;;N;;;;;
+16B24;PAHAWH HMONG CONSONANT YAU;Lo;0;L;;;;;N;;;;;
+16B25;PAHAWH HMONG CONSONANT HLAU;Lo;0;L;;;;;N;;;;;
+16B26;PAHAWH HMONG CONSONANT MAU;Lo;0;L;;;;;N;;;;;
+16B27;PAHAWH HMONG CONSONANT CHAU;Lo;0;L;;;;;N;;;;;
+16B28;PAHAWH HMONG CONSONANT NCHAU;Lo;0;L;;;;;N;;;;;
+16B29;PAHAWH HMONG CONSONANT HNAU;Lo;0;L;;;;;N;;;;;
+16B2A;PAHAWH HMONG CONSONANT PLHAU;Lo;0;L;;;;;N;;;;;
+16B2B;PAHAWH HMONG CONSONANT NTHAU;Lo;0;L;;;;;N;;;;;
+16B2C;PAHAWH HMONG CONSONANT NAU;Lo;0;L;;;;;N;;;;;
+16B2D;PAHAWH HMONG CONSONANT AU;Lo;0;L;;;;;N;;;;;
+16B2E;PAHAWH HMONG CONSONANT XAU;Lo;0;L;;;;;N;;;;;
+16B2F;PAHAWH HMONG CONSONANT CAU;Lo;0;L;;;;;N;;;;;
+16B30;PAHAWH HMONG MARK CIM TUB;Mn;230;NSM;;;;;N;;;;;
+16B31;PAHAWH HMONG MARK CIM SO;Mn;230;NSM;;;;;N;;;;;
+16B32;PAHAWH HMONG MARK CIM KES;Mn;230;NSM;;;;;N;;;;;
+16B33;PAHAWH HMONG MARK CIM KHAV;Mn;230;NSM;;;;;N;;;;;
+16B34;PAHAWH HMONG MARK CIM SUAM;Mn;230;NSM;;;;;N;;;;;
+16B35;PAHAWH HMONG MARK CIM HOM;Mn;230;NSM;;;;;N;;;;;
+16B36;PAHAWH HMONG MARK CIM TAUM;Mn;230;NSM;;;;;N;;;;;
+16B37;PAHAWH HMONG SIGN VOS THOM;Po;0;L;;;;;N;;;;;
+16B38;PAHAWH HMONG SIGN VOS TSHAB CEEB;Po;0;L;;;;;N;;;;;
+16B39;PAHAWH HMONG SIGN CIM CHEEM;Po;0;L;;;;;N;;;;;
+16B3A;PAHAWH HMONG SIGN VOS THIAB;Po;0;L;;;;;N;;;;;
+16B3B;PAHAWH HMONG SIGN VOS FEEM;Po;0;L;;;;;N;;;;;
+16B3C;PAHAWH HMONG SIGN XYEEM NTXIV;So;0;L;;;;;N;;;;;
+16B3D;PAHAWH HMONG SIGN XYEEM RHO;So;0;L;;;;;N;;;;;
+16B3E;PAHAWH HMONG SIGN XYEEM TOV;So;0;L;;;;;N;;;;;
+16B3F;PAHAWH HMONG SIGN XYEEM FAIB;So;0;L;;;;;N;;;;;
+16B40;PAHAWH HMONG SIGN VOS SEEV;Lm;0;L;;;;;N;;;;;
+16B41;PAHAWH HMONG SIGN MEEJ SUAB;Lm;0;L;;;;;N;;;;;
+16B42;PAHAWH HMONG SIGN VOS NRUA;Lm;0;L;;;;;N;;;;;
+16B43;PAHAWH HMONG SIGN IB YAM;Lm;0;L;;;;;N;;;;;
+16B44;PAHAWH HMONG SIGN XAUS;Po;0;L;;;;;N;;;;;
+16B45;PAHAWH HMONG SIGN CIM TSOV ROG;So;0;L;;;;;N;;;;;
+16B50;PAHAWH HMONG DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+16B51;PAHAWH HMONG DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+16B52;PAHAWH HMONG DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+16B53;PAHAWH HMONG DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+16B54;PAHAWH HMONG DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+16B55;PAHAWH HMONG DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+16B56;PAHAWH HMONG DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+16B57;PAHAWH HMONG DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+16B58;PAHAWH HMONG DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+16B59;PAHAWH HMONG DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+16B5B;PAHAWH HMONG NUMBER TENS;No;0;L;;;;10;N;;;;;
+16B5C;PAHAWH HMONG NUMBER HUNDREDS;No;0;L;;;;100;N;;;;;
+16B5D;PAHAWH HMONG NUMBER TEN THOUSANDS;No;0;L;;;;10000;N;;;;;
+16B5E;PAHAWH HMONG NUMBER MILLIONS;No;0;L;;;;1000000;N;;;;;
+16B5F;PAHAWH HMONG NUMBER HUNDRED MILLIONS;No;0;L;;;;100000000;N;;;;;
+16B60;PAHAWH HMONG NUMBER TEN BILLIONS;No;0;L;;;;10000000000;N;;;;;
+16B61;PAHAWH HMONG NUMBER TRILLIONS;No;0;L;;;;1000000000000;N;;;;;
+16B63;PAHAWH HMONG SIGN VOS LUB;Lo;0;L;;;;;N;;;;;
+16B64;PAHAWH HMONG SIGN XYOO;Lo;0;L;;;;;N;;;;;
+16B65;PAHAWH HMONG SIGN HLI;Lo;0;L;;;;;N;;;;;
+16B66;PAHAWH HMONG SIGN THIRD-STAGE HLI;Lo;0;L;;;;;N;;;;;
+16B67;PAHAWH HMONG SIGN ZWJ THAJ;Lo;0;L;;;;;N;;;;;
+16B68;PAHAWH HMONG SIGN HNUB;Lo;0;L;;;;;N;;;;;
+16B69;PAHAWH HMONG SIGN NQIG;Lo;0;L;;;;;N;;;;;
+16B6A;PAHAWH HMONG SIGN XIAB;Lo;0;L;;;;;N;;;;;
+16B6B;PAHAWH HMONG SIGN NTUJ;Lo;0;L;;;;;N;;;;;
+16B6C;PAHAWH HMONG SIGN AV;Lo;0;L;;;;;N;;;;;
+16B6D;PAHAWH HMONG SIGN TXHEEJ CEEV;Lo;0;L;;;;;N;;;;;
+16B6E;PAHAWH HMONG SIGN MEEJ TSEEB;Lo;0;L;;;;;N;;;;;
+16B6F;PAHAWH HMONG SIGN TAU;Lo;0;L;;;;;N;;;;;
+16B70;PAHAWH HMONG SIGN LOS;Lo;0;L;;;;;N;;;;;
+16B71;PAHAWH HMONG SIGN MUS;Lo;0;L;;;;;N;;;;;
+16B72;PAHAWH HMONG SIGN CIM HAIS LUS NTOG NTOG;Lo;0;L;;;;;N;;;;;
+16B73;PAHAWH HMONG SIGN CIM CUAM TSHOOJ;Lo;0;L;;;;;N;;;;;
+16B74;PAHAWH HMONG SIGN CIM TXWV;Lo;0;L;;;;;N;;;;;
+16B75;PAHAWH HMONG SIGN CIM TXWV CHWV;Lo;0;L;;;;;N;;;;;
+16B76;PAHAWH HMONG SIGN CIM PUB DAWB;Lo;0;L;;;;;N;;;;;
+16B77;PAHAWH HMONG SIGN CIM NRES TOS;Lo;0;L;;;;;N;;;;;
+16B7D;PAHAWH HMONG CLAN SIGN TSHEEJ;Lo;0;L;;;;;N;;;;;
+16B7E;PAHAWH HMONG CLAN SIGN YEEG;Lo;0;L;;;;;N;;;;;
+16B7F;PAHAWH HMONG CLAN SIGN LIS;Lo;0;L;;;;;N;;;;;
+16B80;PAHAWH HMONG CLAN SIGN LAUJ;Lo;0;L;;;;;N;;;;;
+16B81;PAHAWH HMONG CLAN SIGN XYOOJ;Lo;0;L;;;;;N;;;;;
+16B82;PAHAWH HMONG CLAN SIGN KOO;Lo;0;L;;;;;N;;;;;
+16B83;PAHAWH HMONG CLAN SIGN HAWJ;Lo;0;L;;;;;N;;;;;
+16B84;PAHAWH HMONG CLAN SIGN MUAS;Lo;0;L;;;;;N;;;;;
+16B85;PAHAWH HMONG CLAN SIGN THOJ;Lo;0;L;;;;;N;;;;;
+16B86;PAHAWH HMONG CLAN SIGN TSAB;Lo;0;L;;;;;N;;;;;
+16B87;PAHAWH HMONG CLAN SIGN PHAB;Lo;0;L;;;;;N;;;;;
+16B88;PAHAWH HMONG CLAN SIGN KHAB;Lo;0;L;;;;;N;;;;;
+16B89;PAHAWH HMONG CLAN SIGN HAM;Lo;0;L;;;;;N;;;;;
+16B8A;PAHAWH HMONG CLAN SIGN VAJ;Lo;0;L;;;;;N;;;;;
+16B8B;PAHAWH HMONG CLAN SIGN FAJ;Lo;0;L;;;;;N;;;;;
+16B8C;PAHAWH HMONG CLAN SIGN YAJ;Lo;0;L;;;;;N;;;;;
+16B8D;PAHAWH HMONG CLAN SIGN TSWB;Lo;0;L;;;;;N;;;;;
+16B8E;PAHAWH HMONG CLAN SIGN KWM;Lo;0;L;;;;;N;;;;;
+16B8F;PAHAWH HMONG CLAN SIGN VWJ;Lo;0;L;;;;;N;;;;;
+16E40;MEDEFAIDRIN CAPITAL LETTER M;Lu;0;L;;;;;N;;;;16E60;
+16E41;MEDEFAIDRIN CAPITAL LETTER S;Lu;0;L;;;;;N;;;;16E61;
+16E42;MEDEFAIDRIN CAPITAL LETTER V;Lu;0;L;;;;;N;;;;16E62;
+16E43;MEDEFAIDRIN CAPITAL LETTER W;Lu;0;L;;;;;N;;;;16E63;
+16E44;MEDEFAIDRIN CAPITAL LETTER ATIU;Lu;0;L;;;;;N;;;;16E64;
+16E45;MEDEFAIDRIN CAPITAL LETTER Z;Lu;0;L;;;;;N;;;;16E65;
+16E46;MEDEFAIDRIN CAPITAL LETTER KP;Lu;0;L;;;;;N;;;;16E66;
+16E47;MEDEFAIDRIN CAPITAL LETTER P;Lu;0;L;;;;;N;;;;16E67;
+16E48;MEDEFAIDRIN CAPITAL LETTER T;Lu;0;L;;;;;N;;;;16E68;
+16E49;MEDEFAIDRIN CAPITAL LETTER G;Lu;0;L;;;;;N;;;;16E69;
+16E4A;MEDEFAIDRIN CAPITAL LETTER F;Lu;0;L;;;;;N;;;;16E6A;
+16E4B;MEDEFAIDRIN CAPITAL LETTER I;Lu;0;L;;;;;N;;;;16E6B;
+16E4C;MEDEFAIDRIN CAPITAL LETTER K;Lu;0;L;;;;;N;;;;16E6C;
+16E4D;MEDEFAIDRIN CAPITAL LETTER A;Lu;0;L;;;;;N;;;;16E6D;
+16E4E;MEDEFAIDRIN CAPITAL LETTER J;Lu;0;L;;;;;N;;;;16E6E;
+16E4F;MEDEFAIDRIN CAPITAL LETTER E;Lu;0;L;;;;;N;;;;16E6F;
+16E50;MEDEFAIDRIN CAPITAL LETTER B;Lu;0;L;;;;;N;;;;16E70;
+16E51;MEDEFAIDRIN CAPITAL LETTER C;Lu;0;L;;;;;N;;;;16E71;
+16E52;MEDEFAIDRIN CAPITAL LETTER U;Lu;0;L;;;;;N;;;;16E72;
+16E53;MEDEFAIDRIN CAPITAL LETTER YU;Lu;0;L;;;;;N;;;;16E73;
+16E54;MEDEFAIDRIN CAPITAL LETTER L;Lu;0;L;;;;;N;;;;16E74;
+16E55;MEDEFAIDRIN CAPITAL LETTER Q;Lu;0;L;;;;;N;;;;16E75;
+16E56;MEDEFAIDRIN CAPITAL LETTER HP;Lu;0;L;;;;;N;;;;16E76;
+16E57;MEDEFAIDRIN CAPITAL LETTER NY;Lu;0;L;;;;;N;;;;16E77;
+16E58;MEDEFAIDRIN CAPITAL LETTER X;Lu;0;L;;;;;N;;;;16E78;
+16E59;MEDEFAIDRIN CAPITAL LETTER D;Lu;0;L;;;;;N;;;;16E79;
+16E5A;MEDEFAIDRIN CAPITAL LETTER OE;Lu;0;L;;;;;N;;;;16E7A;
+16E5B;MEDEFAIDRIN CAPITAL LETTER N;Lu;0;L;;;;;N;;;;16E7B;
+16E5C;MEDEFAIDRIN CAPITAL LETTER R;Lu;0;L;;;;;N;;;;16E7C;
+16E5D;MEDEFAIDRIN CAPITAL LETTER O;Lu;0;L;;;;;N;;;;16E7D;
+16E5E;MEDEFAIDRIN CAPITAL LETTER AI;Lu;0;L;;;;;N;;;;16E7E;
+16E5F;MEDEFAIDRIN CAPITAL LETTER Y;Lu;0;L;;;;;N;;;;16E7F;
+16E60;MEDEFAIDRIN SMALL LETTER M;Ll;0;L;;;;;N;;;16E40;;16E40
+16E61;MEDEFAIDRIN SMALL LETTER S;Ll;0;L;;;;;N;;;16E41;;16E41
+16E62;MEDEFAIDRIN SMALL LETTER V;Ll;0;L;;;;;N;;;16E42;;16E42
+16E63;MEDEFAIDRIN SMALL LETTER W;Ll;0;L;;;;;N;;;16E43;;16E43
+16E64;MEDEFAIDRIN SMALL LETTER ATIU;Ll;0;L;;;;;N;;;16E44;;16E44
+16E65;MEDEFAIDRIN SMALL LETTER Z;Ll;0;L;;;;;N;;;16E45;;16E45
+16E66;MEDEFAIDRIN SMALL LETTER KP;Ll;0;L;;;;;N;;;16E46;;16E46
+16E67;MEDEFAIDRIN SMALL LETTER P;Ll;0;L;;;;;N;;;16E47;;16E47
+16E68;MEDEFAIDRIN SMALL LETTER T;Ll;0;L;;;;;N;;;16E48;;16E48
+16E69;MEDEFAIDRIN SMALL LETTER G;Ll;0;L;;;;;N;;;16E49;;16E49
+16E6A;MEDEFAIDRIN SMALL LETTER F;Ll;0;L;;;;;N;;;16E4A;;16E4A
+16E6B;MEDEFAIDRIN SMALL LETTER I;Ll;0;L;;;;;N;;;16E4B;;16E4B
+16E6C;MEDEFAIDRIN SMALL LETTER K;Ll;0;L;;;;;N;;;16E4C;;16E4C
+16E6D;MEDEFAIDRIN SMALL LETTER A;Ll;0;L;;;;;N;;;16E4D;;16E4D
+16E6E;MEDEFAIDRIN SMALL LETTER J;Ll;0;L;;;;;N;;;16E4E;;16E4E
+16E6F;MEDEFAIDRIN SMALL LETTER E;Ll;0;L;;;;;N;;;16E4F;;16E4F
+16E70;MEDEFAIDRIN SMALL LETTER B;Ll;0;L;;;;;N;;;16E50;;16E50
+16E71;MEDEFAIDRIN SMALL LETTER C;Ll;0;L;;;;;N;;;16E51;;16E51
+16E72;MEDEFAIDRIN SMALL LETTER U;Ll;0;L;;;;;N;;;16E52;;16E52
+16E73;MEDEFAIDRIN SMALL LETTER YU;Ll;0;L;;;;;N;;;16E53;;16E53
+16E74;MEDEFAIDRIN SMALL LETTER L;Ll;0;L;;;;;N;;;16E54;;16E54
+16E75;MEDEFAIDRIN SMALL LETTER Q;Ll;0;L;;;;;N;;;16E55;;16E55
+16E76;MEDEFAIDRIN SMALL LETTER HP;Ll;0;L;;;;;N;;;16E56;;16E56
+16E77;MEDEFAIDRIN SMALL LETTER NY;Ll;0;L;;;;;N;;;16E57;;16E57
+16E78;MEDEFAIDRIN SMALL LETTER X;Ll;0;L;;;;;N;;;16E58;;16E58
+16E79;MEDEFAIDRIN SMALL LETTER D;Ll;0;L;;;;;N;;;16E59;;16E59
+16E7A;MEDEFAIDRIN SMALL LETTER OE;Ll;0;L;;;;;N;;;16E5A;;16E5A
+16E7B;MEDEFAIDRIN SMALL LETTER N;Ll;0;L;;;;;N;;;16E5B;;16E5B
+16E7C;MEDEFAIDRIN SMALL LETTER R;Ll;0;L;;;;;N;;;16E5C;;16E5C
+16E7D;MEDEFAIDRIN SMALL LETTER O;Ll;0;L;;;;;N;;;16E5D;;16E5D
+16E7E;MEDEFAIDRIN SMALL LETTER AI;Ll;0;L;;;;;N;;;16E5E;;16E5E
+16E7F;MEDEFAIDRIN SMALL LETTER Y;Ll;0;L;;;;;N;;;16E5F;;16E5F
+16E80;MEDEFAIDRIN DIGIT ZERO;No;0;L;;;;0;N;;;;;
+16E81;MEDEFAIDRIN DIGIT ONE;No;0;L;;;;1;N;;;;;
+16E82;MEDEFAIDRIN DIGIT TWO;No;0;L;;;;2;N;;;;;
+16E83;MEDEFAIDRIN DIGIT THREE;No;0;L;;;;3;N;;;;;
+16E84;MEDEFAIDRIN DIGIT FOUR;No;0;L;;;;4;N;;;;;
+16E85;MEDEFAIDRIN DIGIT FIVE;No;0;L;;;;5;N;;;;;
+16E86;MEDEFAIDRIN DIGIT SIX;No;0;L;;;;6;N;;;;;
+16E87;MEDEFAIDRIN DIGIT SEVEN;No;0;L;;;;7;N;;;;;
+16E88;MEDEFAIDRIN DIGIT EIGHT;No;0;L;;;;8;N;;;;;
+16E89;MEDEFAIDRIN DIGIT NINE;No;0;L;;;;9;N;;;;;
+16E8A;MEDEFAIDRIN NUMBER TEN;No;0;L;;;;10;N;;;;;
+16E8B;MEDEFAIDRIN NUMBER ELEVEN;No;0;L;;;;11;N;;;;;
+16E8C;MEDEFAIDRIN NUMBER TWELVE;No;0;L;;;;12;N;;;;;
+16E8D;MEDEFAIDRIN NUMBER THIRTEEN;No;0;L;;;;13;N;;;;;
+16E8E;MEDEFAIDRIN NUMBER FOURTEEN;No;0;L;;;;14;N;;;;;
+16E8F;MEDEFAIDRIN NUMBER FIFTEEN;No;0;L;;;;15;N;;;;;
+16E90;MEDEFAIDRIN NUMBER SIXTEEN;No;0;L;;;;16;N;;;;;
+16E91;MEDEFAIDRIN NUMBER SEVENTEEN;No;0;L;;;;17;N;;;;;
+16E92;MEDEFAIDRIN NUMBER EIGHTEEN;No;0;L;;;;18;N;;;;;
+16E93;MEDEFAIDRIN NUMBER NINETEEN;No;0;L;;;;19;N;;;;;
+16E94;MEDEFAIDRIN DIGIT ONE ALTERNATE FORM;No;0;L;;;;1;N;;;;;
+16E95;MEDEFAIDRIN DIGIT TWO ALTERNATE FORM;No;0;L;;;;2;N;;;;;
+16E96;MEDEFAIDRIN DIGIT THREE ALTERNATE FORM;No;0;L;;;;3;N;;;;;
+16E97;MEDEFAIDRIN COMMA;Po;0;L;;;;;N;;;;;
+16E98;MEDEFAIDRIN FULL STOP;Po;0;L;;;;;N;;;;;
+16E99;MEDEFAIDRIN SYMBOL AIVA;Po;0;L;;;;;N;;;;;
+16E9A;MEDEFAIDRIN EXCLAMATION OH;Po;0;L;;;;;N;;;;;
+16F00;MIAO LETTER PA;Lo;0;L;;;;;N;;;;;
+16F01;MIAO LETTER BA;Lo;0;L;;;;;N;;;;;
+16F02;MIAO LETTER YI PA;Lo;0;L;;;;;N;;;;;
+16F03;MIAO LETTER PLA;Lo;0;L;;;;;N;;;;;
+16F04;MIAO LETTER MA;Lo;0;L;;;;;N;;;;;
+16F05;MIAO LETTER MHA;Lo;0;L;;;;;N;;;;;
+16F06;MIAO LETTER ARCHAIC MA;Lo;0;L;;;;;N;;;;;
+16F07;MIAO LETTER FA;Lo;0;L;;;;;N;;;;;
+16F08;MIAO LETTER VA;Lo;0;L;;;;;N;;;;;
+16F09;MIAO LETTER VFA;Lo;0;L;;;;;N;;;;;
+16F0A;MIAO LETTER TA;Lo;0;L;;;;;N;;;;;
+16F0B;MIAO LETTER DA;Lo;0;L;;;;;N;;;;;
+16F0C;MIAO LETTER YI TTA;Lo;0;L;;;;;N;;;;;
+16F0D;MIAO LETTER YI TA;Lo;0;L;;;;;N;;;;;
+16F0E;MIAO LETTER TTA;Lo;0;L;;;;;N;;;;;
+16F0F;MIAO LETTER DDA;Lo;0;L;;;;;N;;;;;
+16F10;MIAO LETTER NA;Lo;0;L;;;;;N;;;;;
+16F11;MIAO LETTER NHA;Lo;0;L;;;;;N;;;;;
+16F12;MIAO LETTER YI NNA;Lo;0;L;;;;;N;;;;;
+16F13;MIAO LETTER ARCHAIC NA;Lo;0;L;;;;;N;;;;;
+16F14;MIAO LETTER NNA;Lo;0;L;;;;;N;;;;;
+16F15;MIAO LETTER NNHA;Lo;0;L;;;;;N;;;;;
+16F16;MIAO LETTER LA;Lo;0;L;;;;;N;;;;;
+16F17;MIAO LETTER LYA;Lo;0;L;;;;;N;;;;;
+16F18;MIAO LETTER LHA;Lo;0;L;;;;;N;;;;;
+16F19;MIAO LETTER LHYA;Lo;0;L;;;;;N;;;;;
+16F1A;MIAO LETTER TLHA;Lo;0;L;;;;;N;;;;;
+16F1B;MIAO LETTER DLHA;Lo;0;L;;;;;N;;;;;
+16F1C;MIAO LETTER TLHYA;Lo;0;L;;;;;N;;;;;
+16F1D;MIAO LETTER DLHYA;Lo;0;L;;;;;N;;;;;
+16F1E;MIAO LETTER KA;Lo;0;L;;;;;N;;;;;
+16F1F;MIAO LETTER GA;Lo;0;L;;;;;N;;;;;
+16F20;MIAO LETTER YI KA;Lo;0;L;;;;;N;;;;;
+16F21;MIAO LETTER QA;Lo;0;L;;;;;N;;;;;
+16F22;MIAO LETTER QGA;Lo;0;L;;;;;N;;;;;
+16F23;MIAO LETTER NGA;Lo;0;L;;;;;N;;;;;
+16F24;MIAO LETTER NGHA;Lo;0;L;;;;;N;;;;;
+16F25;MIAO LETTER ARCHAIC NGA;Lo;0;L;;;;;N;;;;;
+16F26;MIAO LETTER HA;Lo;0;L;;;;;N;;;;;
+16F27;MIAO LETTER XA;Lo;0;L;;;;;N;;;;;
+16F28;MIAO LETTER GHA;Lo;0;L;;;;;N;;;;;
+16F29;MIAO LETTER GHHA;Lo;0;L;;;;;N;;;;;
+16F2A;MIAO LETTER TSSA;Lo;0;L;;;;;N;;;;;
+16F2B;MIAO LETTER DZZA;Lo;0;L;;;;;N;;;;;
+16F2C;MIAO LETTER NYA;Lo;0;L;;;;;N;;;;;
+16F2D;MIAO LETTER NYHA;Lo;0;L;;;;;N;;;;;
+16F2E;MIAO LETTER TSHA;Lo;0;L;;;;;N;;;;;
+16F2F;MIAO LETTER DZHA;Lo;0;L;;;;;N;;;;;
+16F30;MIAO LETTER YI TSHA;Lo;0;L;;;;;N;;;;;
+16F31;MIAO LETTER YI DZHA;Lo;0;L;;;;;N;;;;;
+16F32;MIAO LETTER REFORMED TSHA;Lo;0;L;;;;;N;;;;;
+16F33;MIAO LETTER SHA;Lo;0;L;;;;;N;;;;;
+16F34;MIAO LETTER SSA;Lo;0;L;;;;;N;;;;;
+16F35;MIAO LETTER ZHA;Lo;0;L;;;;;N;;;;;
+16F36;MIAO LETTER ZSHA;Lo;0;L;;;;;N;;;;;
+16F37;MIAO LETTER TSA;Lo;0;L;;;;;N;;;;;
+16F38;MIAO LETTER DZA;Lo;0;L;;;;;N;;;;;
+16F39;MIAO LETTER YI TSA;Lo;0;L;;;;;N;;;;;
+16F3A;MIAO LETTER SA;Lo;0;L;;;;;N;;;;;
+16F3B;MIAO LETTER ZA;Lo;0;L;;;;;N;;;;;
+16F3C;MIAO LETTER ZSA;Lo;0;L;;;;;N;;;;;
+16F3D;MIAO LETTER ZZA;Lo;0;L;;;;;N;;;;;
+16F3E;MIAO LETTER ZZSA;Lo;0;L;;;;;N;;;;;
+16F3F;MIAO LETTER ARCHAIC ZZA;Lo;0;L;;;;;N;;;;;
+16F40;MIAO LETTER ZZYA;Lo;0;L;;;;;N;;;;;
+16F41;MIAO LETTER ZZSYA;Lo;0;L;;;;;N;;;;;
+16F42;MIAO LETTER WA;Lo;0;L;;;;;N;;;;;
+16F43;MIAO LETTER AH;Lo;0;L;;;;;N;;;;;
+16F44;MIAO LETTER HHA;Lo;0;L;;;;;N;;;;;
+16F45;MIAO LETTER BRI;Lo;0;L;;;;;N;;;;;
+16F46;MIAO LETTER SYI;Lo;0;L;;;;;N;;;;;
+16F47;MIAO LETTER DZYI;Lo;0;L;;;;;N;;;;;
+16F48;MIAO LETTER TE;Lo;0;L;;;;;N;;;;;
+16F49;MIAO LETTER TSE;Lo;0;L;;;;;N;;;;;
+16F4A;MIAO LETTER RTE;Lo;0;L;;;;;N;;;;;
+16F4F;MIAO SIGN CONSONANT MODIFIER BAR;Mn;0;NSM;;;;;N;;;;;
+16F50;MIAO LETTER NASALIZATION;Lo;0;L;;;;;N;;;;;
+16F51;MIAO SIGN ASPIRATION;Mc;0;L;;;;;N;;;;;
+16F52;MIAO SIGN REFORMED VOICING;Mc;0;L;;;;;N;;;;;
+16F53;MIAO SIGN REFORMED ASPIRATION;Mc;0;L;;;;;N;;;;;
+16F54;MIAO VOWEL SIGN A;Mc;0;L;;;;;N;;;;;
+16F55;MIAO VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+16F56;MIAO VOWEL SIGN AHH;Mc;0;L;;;;;N;;;;;
+16F57;MIAO VOWEL SIGN AN;Mc;0;L;;;;;N;;;;;
+16F58;MIAO VOWEL SIGN ANG;Mc;0;L;;;;;N;;;;;
+16F59;MIAO VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+16F5A;MIAO VOWEL SIGN OO;Mc;0;L;;;;;N;;;;;
+16F5B;MIAO VOWEL SIGN WO;Mc;0;L;;;;;N;;;;;
+16F5C;MIAO VOWEL SIGN W;Mc;0;L;;;;;N;;;;;
+16F5D;MIAO VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+16F5E;MIAO VOWEL SIGN EN;Mc;0;L;;;;;N;;;;;
+16F5F;MIAO VOWEL SIGN ENG;Mc;0;L;;;;;N;;;;;
+16F60;MIAO VOWEL SIGN OEY;Mc;0;L;;;;;N;;;;;
+16F61;MIAO VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+16F62;MIAO VOWEL SIGN IA;Mc;0;L;;;;;N;;;;;
+16F63;MIAO VOWEL SIGN IAN;Mc;0;L;;;;;N;;;;;
+16F64;MIAO VOWEL SIGN IANG;Mc;0;L;;;;;N;;;;;
+16F65;MIAO VOWEL SIGN IO;Mc;0;L;;;;;N;;;;;
+16F66;MIAO VOWEL SIGN IE;Mc;0;L;;;;;N;;;;;
+16F67;MIAO VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+16F68;MIAO VOWEL SIGN IU;Mc;0;L;;;;;N;;;;;
+16F69;MIAO VOWEL SIGN ING;Mc;0;L;;;;;N;;;;;
+16F6A;MIAO VOWEL SIGN U;Mc;0;L;;;;;N;;;;;
+16F6B;MIAO VOWEL SIGN UA;Mc;0;L;;;;;N;;;;;
+16F6C;MIAO VOWEL SIGN UAN;Mc;0;L;;;;;N;;;;;
+16F6D;MIAO VOWEL SIGN UANG;Mc;0;L;;;;;N;;;;;
+16F6E;MIAO VOWEL SIGN UU;Mc;0;L;;;;;N;;;;;
+16F6F;MIAO VOWEL SIGN UEI;Mc;0;L;;;;;N;;;;;
+16F70;MIAO VOWEL SIGN UNG;Mc;0;L;;;;;N;;;;;
+16F71;MIAO VOWEL SIGN Y;Mc;0;L;;;;;N;;;;;
+16F72;MIAO VOWEL SIGN YI;Mc;0;L;;;;;N;;;;;
+16F73;MIAO VOWEL SIGN AE;Mc;0;L;;;;;N;;;;;
+16F74;MIAO VOWEL SIGN AEE;Mc;0;L;;;;;N;;;;;
+16F75;MIAO VOWEL SIGN ERR;Mc;0;L;;;;;N;;;;;
+16F76;MIAO VOWEL SIGN ROUNDED ERR;Mc;0;L;;;;;N;;;;;
+16F77;MIAO VOWEL SIGN ER;Mc;0;L;;;;;N;;;;;
+16F78;MIAO VOWEL SIGN ROUNDED ER;Mc;0;L;;;;;N;;;;;
+16F79;MIAO VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+16F7A;MIAO VOWEL SIGN EI;Mc;0;L;;;;;N;;;;;
+16F7B;MIAO VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+16F7C;MIAO VOWEL SIGN OU;Mc;0;L;;;;;N;;;;;
+16F7D;MIAO VOWEL SIGN N;Mc;0;L;;;;;N;;;;;
+16F7E;MIAO VOWEL SIGN NG;Mc;0;L;;;;;N;;;;;
+16F7F;MIAO VOWEL SIGN UOG;Mc;0;L;;;;;N;;;;;
+16F80;MIAO VOWEL SIGN YUI;Mc;0;L;;;;;N;;;;;
+16F81;MIAO VOWEL SIGN OG;Mc;0;L;;;;;N;;;;;
+16F82;MIAO VOWEL SIGN OER;Mc;0;L;;;;;N;;;;;
+16F83;MIAO VOWEL SIGN VW;Mc;0;L;;;;;N;;;;;
+16F84;MIAO VOWEL SIGN IG;Mc;0;L;;;;;N;;;;;
+16F85;MIAO VOWEL SIGN EA;Mc;0;L;;;;;N;;;;;
+16F86;MIAO VOWEL SIGN IONG;Mc;0;L;;;;;N;;;;;
+16F87;MIAO VOWEL SIGN UI;Mc;0;L;;;;;N;;;;;
+16F8F;MIAO TONE RIGHT;Mn;0;NSM;;;;;N;;;;;
+16F90;MIAO TONE TOP RIGHT;Mn;0;NSM;;;;;N;;;;;
+16F91;MIAO TONE ABOVE;Mn;0;NSM;;;;;N;;;;;
+16F92;MIAO TONE BELOW;Mn;0;NSM;;;;;N;;;;;
+16F93;MIAO LETTER TONE-2;Lm;0;L;;;;;N;;;;;
+16F94;MIAO LETTER TONE-3;Lm;0;L;;;;;N;;;;;
+16F95;MIAO LETTER TONE-4;Lm;0;L;;;;;N;;;;;
+16F96;MIAO LETTER TONE-5;Lm;0;L;;;;;N;;;;;
+16F97;MIAO LETTER TONE-6;Lm;0;L;;;;;N;;;;;
+16F98;MIAO LETTER TONE-7;Lm;0;L;;;;;N;;;;;
+16F99;MIAO LETTER TONE-8;Lm;0;L;;;;;N;;;;;
+16F9A;MIAO LETTER REFORMED TONE-1;Lm;0;L;;;;;N;;;;;
+16F9B;MIAO LETTER REFORMED TONE-2;Lm;0;L;;;;;N;;;;;
+16F9C;MIAO LETTER REFORMED TONE-4;Lm;0;L;;;;;N;;;;;
+16F9D;MIAO LETTER REFORMED TONE-5;Lm;0;L;;;;;N;;;;;
+16F9E;MIAO LETTER REFORMED TONE-6;Lm;0;L;;;;;N;;;;;
+16F9F;MIAO LETTER REFORMED TONE-8;Lm;0;L;;;;;N;;;;;
+16FE0;TANGUT ITERATION MARK;Lm;0;L;;;;;N;;;;;
+16FE1;NUSHU ITERATION MARK;Lm;0;L;;;;;N;;;;;
+16FE2;OLD CHINESE HOOK MARK;Po;0;ON;;;;;N;;;;;
+16FE3;OLD CHINESE ITERATION MARK;Lm;0;L;;;;;N;;;;;
+17000;<Tangut Ideograph, First>;Lo;0;L;;;;;N;;;;;
+187F7;<Tangut Ideograph, Last>;Lo;0;L;;;;;N;;;;;
+18800;TANGUT COMPONENT-001;Lo;0;L;;;;;N;;;;;
+18801;TANGUT COMPONENT-002;Lo;0;L;;;;;N;;;;;
+18802;TANGUT COMPONENT-003;Lo;0;L;;;;;N;;;;;
+18803;TANGUT COMPONENT-004;Lo;0;L;;;;;N;;;;;
+18804;TANGUT COMPONENT-005;Lo;0;L;;;;;N;;;;;
+18805;TANGUT COMPONENT-006;Lo;0;L;;;;;N;;;;;
+18806;TANGUT COMPONENT-007;Lo;0;L;;;;;N;;;;;
+18807;TANGUT COMPONENT-008;Lo;0;L;;;;;N;;;;;
+18808;TANGUT COMPONENT-009;Lo;0;L;;;;;N;;;;;
+18809;TANGUT COMPONENT-010;Lo;0;L;;;;;N;;;;;
+1880A;TANGUT COMPONENT-011;Lo;0;L;;;;;N;;;;;
+1880B;TANGUT COMPONENT-012;Lo;0;L;;;;;N;;;;;
+1880C;TANGUT COMPONENT-013;Lo;0;L;;;;;N;;;;;
+1880D;TANGUT COMPONENT-014;Lo;0;L;;;;;N;;;;;
+1880E;TANGUT COMPONENT-015;Lo;0;L;;;;;N;;;;;
+1880F;TANGUT COMPONENT-016;Lo;0;L;;;;;N;;;;;
+18810;TANGUT COMPONENT-017;Lo;0;L;;;;;N;;;;;
+18811;TANGUT COMPONENT-018;Lo;0;L;;;;;N;;;;;
+18812;TANGUT COMPONENT-019;Lo;0;L;;;;;N;;;;;
+18813;TANGUT COMPONENT-020;Lo;0;L;;;;;N;;;;;
+18814;TANGUT COMPONENT-021;Lo;0;L;;;;;N;;;;;
+18815;TANGUT COMPONENT-022;Lo;0;L;;;;;N;;;;;
+18816;TANGUT COMPONENT-023;Lo;0;L;;;;;N;;;;;
+18817;TANGUT COMPONENT-024;Lo;0;L;;;;;N;;;;;
+18818;TANGUT COMPONENT-025;Lo;0;L;;;;;N;;;;;
+18819;TANGUT COMPONENT-026;Lo;0;L;;;;;N;;;;;
+1881A;TANGUT COMPONENT-027;Lo;0;L;;;;;N;;;;;
+1881B;TANGUT COMPONENT-028;Lo;0;L;;;;;N;;;;;
+1881C;TANGUT COMPONENT-029;Lo;0;L;;;;;N;;;;;
+1881D;TANGUT COMPONENT-030;Lo;0;L;;;;;N;;;;;
+1881E;TANGUT COMPONENT-031;Lo;0;L;;;;;N;;;;;
+1881F;TANGUT COMPONENT-032;Lo;0;L;;;;;N;;;;;
+18820;TANGUT COMPONENT-033;Lo;0;L;;;;;N;;;;;
+18821;TANGUT COMPONENT-034;Lo;0;L;;;;;N;;;;;
+18822;TANGUT COMPONENT-035;Lo;0;L;;;;;N;;;;;
+18823;TANGUT COMPONENT-036;Lo;0;L;;;;;N;;;;;
+18824;TANGUT COMPONENT-037;Lo;0;L;;;;;N;;;;;
+18825;TANGUT COMPONENT-038;Lo;0;L;;;;;N;;;;;
+18826;TANGUT COMPONENT-039;Lo;0;L;;;;;N;;;;;
+18827;TANGUT COMPONENT-040;Lo;0;L;;;;;N;;;;;
+18828;TANGUT COMPONENT-041;Lo;0;L;;;;;N;;;;;
+18829;TANGUT COMPONENT-042;Lo;0;L;;;;;N;;;;;
+1882A;TANGUT COMPONENT-043;Lo;0;L;;;;;N;;;;;
+1882B;TANGUT COMPONENT-044;Lo;0;L;;;;;N;;;;;
+1882C;TANGUT COMPONENT-045;Lo;0;L;;;;;N;;;;;
+1882D;TANGUT COMPONENT-046;Lo;0;L;;;;;N;;;;;
+1882E;TANGUT COMPONENT-047;Lo;0;L;;;;;N;;;;;
+1882F;TANGUT COMPONENT-048;Lo;0;L;;;;;N;;;;;
+18830;TANGUT COMPONENT-049;Lo;0;L;;;;;N;;;;;
+18831;TANGUT COMPONENT-050;Lo;0;L;;;;;N;;;;;
+18832;TANGUT COMPONENT-051;Lo;0;L;;;;;N;;;;;
+18833;TANGUT COMPONENT-052;Lo;0;L;;;;;N;;;;;
+18834;TANGUT COMPONENT-053;Lo;0;L;;;;;N;;;;;
+18835;TANGUT COMPONENT-054;Lo;0;L;;;;;N;;;;;
+18836;TANGUT COMPONENT-055;Lo;0;L;;;;;N;;;;;
+18837;TANGUT COMPONENT-056;Lo;0;L;;;;;N;;;;;
+18838;TANGUT COMPONENT-057;Lo;0;L;;;;;N;;;;;
+18839;TANGUT COMPONENT-058;Lo;0;L;;;;;N;;;;;
+1883A;TANGUT COMPONENT-059;Lo;0;L;;;;;N;;;;;
+1883B;TANGUT COMPONENT-060;Lo;0;L;;;;;N;;;;;
+1883C;TANGUT COMPONENT-061;Lo;0;L;;;;;N;;;;;
+1883D;TANGUT COMPONENT-062;Lo;0;L;;;;;N;;;;;
+1883E;TANGUT COMPONENT-063;Lo;0;L;;;;;N;;;;;
+1883F;TANGUT COMPONENT-064;Lo;0;L;;;;;N;;;;;
+18840;TANGUT COMPONENT-065;Lo;0;L;;;;;N;;;;;
+18841;TANGUT COMPONENT-066;Lo;0;L;;;;;N;;;;;
+18842;TANGUT COMPONENT-067;Lo;0;L;;;;;N;;;;;
+18843;TANGUT COMPONENT-068;Lo;0;L;;;;;N;;;;;
+18844;TANGUT COMPONENT-069;Lo;0;L;;;;;N;;;;;
+18845;TANGUT COMPONENT-070;Lo;0;L;;;;;N;;;;;
+18846;TANGUT COMPONENT-071;Lo;0;L;;;;;N;;;;;
+18847;TANGUT COMPONENT-072;Lo;0;L;;;;;N;;;;;
+18848;TANGUT COMPONENT-073;Lo;0;L;;;;;N;;;;;
+18849;TANGUT COMPONENT-074;Lo;0;L;;;;;N;;;;;
+1884A;TANGUT COMPONENT-075;Lo;0;L;;;;;N;;;;;
+1884B;TANGUT COMPONENT-076;Lo;0;L;;;;;N;;;;;
+1884C;TANGUT COMPONENT-077;Lo;0;L;;;;;N;;;;;
+1884D;TANGUT COMPONENT-078;Lo;0;L;;;;;N;;;;;
+1884E;TANGUT COMPONENT-079;Lo;0;L;;;;;N;;;;;
+1884F;TANGUT COMPONENT-080;Lo;0;L;;;;;N;;;;;
+18850;TANGUT COMPONENT-081;Lo;0;L;;;;;N;;;;;
+18851;TANGUT COMPONENT-082;Lo;0;L;;;;;N;;;;;
+18852;TANGUT COMPONENT-083;Lo;0;L;;;;;N;;;;;
+18853;TANGUT COMPONENT-084;Lo;0;L;;;;;N;;;;;
+18854;TANGUT COMPONENT-085;Lo;0;L;;;;;N;;;;;
+18855;TANGUT COMPONENT-086;Lo;0;L;;;;;N;;;;;
+18856;TANGUT COMPONENT-087;Lo;0;L;;;;;N;;;;;
+18857;TANGUT COMPONENT-088;Lo;0;L;;;;;N;;;;;
+18858;TANGUT COMPONENT-089;Lo;0;L;;;;;N;;;;;
+18859;TANGUT COMPONENT-090;Lo;0;L;;;;;N;;;;;
+1885A;TANGUT COMPONENT-091;Lo;0;L;;;;;N;;;;;
+1885B;TANGUT COMPONENT-092;Lo;0;L;;;;;N;;;;;
+1885C;TANGUT COMPONENT-093;Lo;0;L;;;;;N;;;;;
+1885D;TANGUT COMPONENT-094;Lo;0;L;;;;;N;;;;;
+1885E;TANGUT COMPONENT-095;Lo;0;L;;;;;N;;;;;
+1885F;TANGUT COMPONENT-096;Lo;0;L;;;;;N;;;;;
+18860;TANGUT COMPONENT-097;Lo;0;L;;;;;N;;;;;
+18861;TANGUT COMPONENT-098;Lo;0;L;;;;;N;;;;;
+18862;TANGUT COMPONENT-099;Lo;0;L;;;;;N;;;;;
+18863;TANGUT COMPONENT-100;Lo;0;L;;;;;N;;;;;
+18864;TANGUT COMPONENT-101;Lo;0;L;;;;;N;;;;;
+18865;TANGUT COMPONENT-102;Lo;0;L;;;;;N;;;;;
+18866;TANGUT COMPONENT-103;Lo;0;L;;;;;N;;;;;
+18867;TANGUT COMPONENT-104;Lo;0;L;;;;;N;;;;;
+18868;TANGUT COMPONENT-105;Lo;0;L;;;;;N;;;;;
+18869;TANGUT COMPONENT-106;Lo;0;L;;;;;N;;;;;
+1886A;TANGUT COMPONENT-107;Lo;0;L;;;;;N;;;;;
+1886B;TANGUT COMPONENT-108;Lo;0;L;;;;;N;;;;;
+1886C;TANGUT COMPONENT-109;Lo;0;L;;;;;N;;;;;
+1886D;TANGUT COMPONENT-110;Lo;0;L;;;;;N;;;;;
+1886E;TANGUT COMPONENT-111;Lo;0;L;;;;;N;;;;;
+1886F;TANGUT COMPONENT-112;Lo;0;L;;;;;N;;;;;
+18870;TANGUT COMPONENT-113;Lo;0;L;;;;;N;;;;;
+18871;TANGUT COMPONENT-114;Lo;0;L;;;;;N;;;;;
+18872;TANGUT COMPONENT-115;Lo;0;L;;;;;N;;;;;
+18873;TANGUT COMPONENT-116;Lo;0;L;;;;;N;;;;;
+18874;TANGUT COMPONENT-117;Lo;0;L;;;;;N;;;;;
+18875;TANGUT COMPONENT-118;Lo;0;L;;;;;N;;;;;
+18876;TANGUT COMPONENT-119;Lo;0;L;;;;;N;;;;;
+18877;TANGUT COMPONENT-120;Lo;0;L;;;;;N;;;;;
+18878;TANGUT COMPONENT-121;Lo;0;L;;;;;N;;;;;
+18879;TANGUT COMPONENT-122;Lo;0;L;;;;;N;;;;;
+1887A;TANGUT COMPONENT-123;Lo;0;L;;;;;N;;;;;
+1887B;TANGUT COMPONENT-124;Lo;0;L;;;;;N;;;;;
+1887C;TANGUT COMPONENT-125;Lo;0;L;;;;;N;;;;;
+1887D;TANGUT COMPONENT-126;Lo;0;L;;;;;N;;;;;
+1887E;TANGUT COMPONENT-127;Lo;0;L;;;;;N;;;;;
+1887F;TANGUT COMPONENT-128;Lo;0;L;;;;;N;;;;;
+18880;TANGUT COMPONENT-129;Lo;0;L;;;;;N;;;;;
+18881;TANGUT COMPONENT-130;Lo;0;L;;;;;N;;;;;
+18882;TANGUT COMPONENT-131;Lo;0;L;;;;;N;;;;;
+18883;TANGUT COMPONENT-132;Lo;0;L;;;;;N;;;;;
+18884;TANGUT COMPONENT-133;Lo;0;L;;;;;N;;;;;
+18885;TANGUT COMPONENT-134;Lo;0;L;;;;;N;;;;;
+18886;TANGUT COMPONENT-135;Lo;0;L;;;;;N;;;;;
+18887;TANGUT COMPONENT-136;Lo;0;L;;;;;N;;;;;
+18888;TANGUT COMPONENT-137;Lo;0;L;;;;;N;;;;;
+18889;TANGUT COMPONENT-138;Lo;0;L;;;;;N;;;;;
+1888A;TANGUT COMPONENT-139;Lo;0;L;;;;;N;;;;;
+1888B;TANGUT COMPONENT-140;Lo;0;L;;;;;N;;;;;
+1888C;TANGUT COMPONENT-141;Lo;0;L;;;;;N;;;;;
+1888D;TANGUT COMPONENT-142;Lo;0;L;;;;;N;;;;;
+1888E;TANGUT COMPONENT-143;Lo;0;L;;;;;N;;;;;
+1888F;TANGUT COMPONENT-144;Lo;0;L;;;;;N;;;;;
+18890;TANGUT COMPONENT-145;Lo;0;L;;;;;N;;;;;
+18891;TANGUT COMPONENT-146;Lo;0;L;;;;;N;;;;;
+18892;TANGUT COMPONENT-147;Lo;0;L;;;;;N;;;;;
+18893;TANGUT COMPONENT-148;Lo;0;L;;;;;N;;;;;
+18894;TANGUT COMPONENT-149;Lo;0;L;;;;;N;;;;;
+18895;TANGUT COMPONENT-150;Lo;0;L;;;;;N;;;;;
+18896;TANGUT COMPONENT-151;Lo;0;L;;;;;N;;;;;
+18897;TANGUT COMPONENT-152;Lo;0;L;;;;;N;;;;;
+18898;TANGUT COMPONENT-153;Lo;0;L;;;;;N;;;;;
+18899;TANGUT COMPONENT-154;Lo;0;L;;;;;N;;;;;
+1889A;TANGUT COMPONENT-155;Lo;0;L;;;;;N;;;;;
+1889B;TANGUT COMPONENT-156;Lo;0;L;;;;;N;;;;;
+1889C;TANGUT COMPONENT-157;Lo;0;L;;;;;N;;;;;
+1889D;TANGUT COMPONENT-158;Lo;0;L;;;;;N;;;;;
+1889E;TANGUT COMPONENT-159;Lo;0;L;;;;;N;;;;;
+1889F;TANGUT COMPONENT-160;Lo;0;L;;;;;N;;;;;
+188A0;TANGUT COMPONENT-161;Lo;0;L;;;;;N;;;;;
+188A1;TANGUT COMPONENT-162;Lo;0;L;;;;;N;;;;;
+188A2;TANGUT COMPONENT-163;Lo;0;L;;;;;N;;;;;
+188A3;TANGUT COMPONENT-164;Lo;0;L;;;;;N;;;;;
+188A4;TANGUT COMPONENT-165;Lo;0;L;;;;;N;;;;;
+188A5;TANGUT COMPONENT-166;Lo;0;L;;;;;N;;;;;
+188A6;TANGUT COMPONENT-167;Lo;0;L;;;;;N;;;;;
+188A7;TANGUT COMPONENT-168;Lo;0;L;;;;;N;;;;;
+188A8;TANGUT COMPONENT-169;Lo;0;L;;;;;N;;;;;
+188A9;TANGUT COMPONENT-170;Lo;0;L;;;;;N;;;;;
+188AA;TANGUT COMPONENT-171;Lo;0;L;;;;;N;;;;;
+188AB;TANGUT COMPONENT-172;Lo;0;L;;;;;N;;;;;
+188AC;TANGUT COMPONENT-173;Lo;0;L;;;;;N;;;;;
+188AD;TANGUT COMPONENT-174;Lo;0;L;;;;;N;;;;;
+188AE;TANGUT COMPONENT-175;Lo;0;L;;;;;N;;;;;
+188AF;TANGUT COMPONENT-176;Lo;0;L;;;;;N;;;;;
+188B0;TANGUT COMPONENT-177;Lo;0;L;;;;;N;;;;;
+188B1;TANGUT COMPONENT-178;Lo;0;L;;;;;N;;;;;
+188B2;TANGUT COMPONENT-179;Lo;0;L;;;;;N;;;;;
+188B3;TANGUT COMPONENT-180;Lo;0;L;;;;;N;;;;;
+188B4;TANGUT COMPONENT-181;Lo;0;L;;;;;N;;;;;
+188B5;TANGUT COMPONENT-182;Lo;0;L;;;;;N;;;;;
+188B6;TANGUT COMPONENT-183;Lo;0;L;;;;;N;;;;;
+188B7;TANGUT COMPONENT-184;Lo;0;L;;;;;N;;;;;
+188B8;TANGUT COMPONENT-185;Lo;0;L;;;;;N;;;;;
+188B9;TANGUT COMPONENT-186;Lo;0;L;;;;;N;;;;;
+188BA;TANGUT COMPONENT-187;Lo;0;L;;;;;N;;;;;
+188BB;TANGUT COMPONENT-188;Lo;0;L;;;;;N;;;;;
+188BC;TANGUT COMPONENT-189;Lo;0;L;;;;;N;;;;;
+188BD;TANGUT COMPONENT-190;Lo;0;L;;;;;N;;;;;
+188BE;TANGUT COMPONENT-191;Lo;0;L;;;;;N;;;;;
+188BF;TANGUT COMPONENT-192;Lo;0;L;;;;;N;;;;;
+188C0;TANGUT COMPONENT-193;Lo;0;L;;;;;N;;;;;
+188C1;TANGUT COMPONENT-194;Lo;0;L;;;;;N;;;;;
+188C2;TANGUT COMPONENT-195;Lo;0;L;;;;;N;;;;;
+188C3;TANGUT COMPONENT-196;Lo;0;L;;;;;N;;;;;
+188C4;TANGUT COMPONENT-197;Lo;0;L;;;;;N;;;;;
+188C5;TANGUT COMPONENT-198;Lo;0;L;;;;;N;;;;;
+188C6;TANGUT COMPONENT-199;Lo;0;L;;;;;N;;;;;
+188C7;TANGUT COMPONENT-200;Lo;0;L;;;;;N;;;;;
+188C8;TANGUT COMPONENT-201;Lo;0;L;;;;;N;;;;;
+188C9;TANGUT COMPONENT-202;Lo;0;L;;;;;N;;;;;
+188CA;TANGUT COMPONENT-203;Lo;0;L;;;;;N;;;;;
+188CB;TANGUT COMPONENT-204;Lo;0;L;;;;;N;;;;;
+188CC;TANGUT COMPONENT-205;Lo;0;L;;;;;N;;;;;
+188CD;TANGUT COMPONENT-206;Lo;0;L;;;;;N;;;;;
+188CE;TANGUT COMPONENT-207;Lo;0;L;;;;;N;;;;;
+188CF;TANGUT COMPONENT-208;Lo;0;L;;;;;N;;;;;
+188D0;TANGUT COMPONENT-209;Lo;0;L;;;;;N;;;;;
+188D1;TANGUT COMPONENT-210;Lo;0;L;;;;;N;;;;;
+188D2;TANGUT COMPONENT-211;Lo;0;L;;;;;N;;;;;
+188D3;TANGUT COMPONENT-212;Lo;0;L;;;;;N;;;;;
+188D4;TANGUT COMPONENT-213;Lo;0;L;;;;;N;;;;;
+188D5;TANGUT COMPONENT-214;Lo;0;L;;;;;N;;;;;
+188D6;TANGUT COMPONENT-215;Lo;0;L;;;;;N;;;;;
+188D7;TANGUT COMPONENT-216;Lo;0;L;;;;;N;;;;;
+188D8;TANGUT COMPONENT-217;Lo;0;L;;;;;N;;;;;
+188D9;TANGUT COMPONENT-218;Lo;0;L;;;;;N;;;;;
+188DA;TANGUT COMPONENT-219;Lo;0;L;;;;;N;;;;;
+188DB;TANGUT COMPONENT-220;Lo;0;L;;;;;N;;;;;
+188DC;TANGUT COMPONENT-221;Lo;0;L;;;;;N;;;;;
+188DD;TANGUT COMPONENT-222;Lo;0;L;;;;;N;;;;;
+188DE;TANGUT COMPONENT-223;Lo;0;L;;;;;N;;;;;
+188DF;TANGUT COMPONENT-224;Lo;0;L;;;;;N;;;;;
+188E0;TANGUT COMPONENT-225;Lo;0;L;;;;;N;;;;;
+188E1;TANGUT COMPONENT-226;Lo;0;L;;;;;N;;;;;
+188E2;TANGUT COMPONENT-227;Lo;0;L;;;;;N;;;;;
+188E3;TANGUT COMPONENT-228;Lo;0;L;;;;;N;;;;;
+188E4;TANGUT COMPONENT-229;Lo;0;L;;;;;N;;;;;
+188E5;TANGUT COMPONENT-230;Lo;0;L;;;;;N;;;;;
+188E6;TANGUT COMPONENT-231;Lo;0;L;;;;;N;;;;;
+188E7;TANGUT COMPONENT-232;Lo;0;L;;;;;N;;;;;
+188E8;TANGUT COMPONENT-233;Lo;0;L;;;;;N;;;;;
+188E9;TANGUT COMPONENT-234;Lo;0;L;;;;;N;;;;;
+188EA;TANGUT COMPONENT-235;Lo;0;L;;;;;N;;;;;
+188EB;TANGUT COMPONENT-236;Lo;0;L;;;;;N;;;;;
+188EC;TANGUT COMPONENT-237;Lo;0;L;;;;;N;;;;;
+188ED;TANGUT COMPONENT-238;Lo;0;L;;;;;N;;;;;
+188EE;TANGUT COMPONENT-239;Lo;0;L;;;;;N;;;;;
+188EF;TANGUT COMPONENT-240;Lo;0;L;;;;;N;;;;;
+188F0;TANGUT COMPONENT-241;Lo;0;L;;;;;N;;;;;
+188F1;TANGUT COMPONENT-242;Lo;0;L;;;;;N;;;;;
+188F2;TANGUT COMPONENT-243;Lo;0;L;;;;;N;;;;;
+188F3;TANGUT COMPONENT-244;Lo;0;L;;;;;N;;;;;
+188F4;TANGUT COMPONENT-245;Lo;0;L;;;;;N;;;;;
+188F5;TANGUT COMPONENT-246;Lo;0;L;;;;;N;;;;;
+188F6;TANGUT COMPONENT-247;Lo;0;L;;;;;N;;;;;
+188F7;TANGUT COMPONENT-248;Lo;0;L;;;;;N;;;;;
+188F8;TANGUT COMPONENT-249;Lo;0;L;;;;;N;;;;;
+188F9;TANGUT COMPONENT-250;Lo;0;L;;;;;N;;;;;
+188FA;TANGUT COMPONENT-251;Lo;0;L;;;;;N;;;;;
+188FB;TANGUT COMPONENT-252;Lo;0;L;;;;;N;;;;;
+188FC;TANGUT COMPONENT-253;Lo;0;L;;;;;N;;;;;
+188FD;TANGUT COMPONENT-254;Lo;0;L;;;;;N;;;;;
+188FE;TANGUT COMPONENT-255;Lo;0;L;;;;;N;;;;;
+188FF;TANGUT COMPONENT-256;Lo;0;L;;;;;N;;;;;
+18900;TANGUT COMPONENT-257;Lo;0;L;;;;;N;;;;;
+18901;TANGUT COMPONENT-258;Lo;0;L;;;;;N;;;;;
+18902;TANGUT COMPONENT-259;Lo;0;L;;;;;N;;;;;
+18903;TANGUT COMPONENT-260;Lo;0;L;;;;;N;;;;;
+18904;TANGUT COMPONENT-261;Lo;0;L;;;;;N;;;;;
+18905;TANGUT COMPONENT-262;Lo;0;L;;;;;N;;;;;
+18906;TANGUT COMPONENT-263;Lo;0;L;;;;;N;;;;;
+18907;TANGUT COMPONENT-264;Lo;0;L;;;;;N;;;;;
+18908;TANGUT COMPONENT-265;Lo;0;L;;;;;N;;;;;
+18909;TANGUT COMPONENT-266;Lo;0;L;;;;;N;;;;;
+1890A;TANGUT COMPONENT-267;Lo;0;L;;;;;N;;;;;
+1890B;TANGUT COMPONENT-268;Lo;0;L;;;;;N;;;;;
+1890C;TANGUT COMPONENT-269;Lo;0;L;;;;;N;;;;;
+1890D;TANGUT COMPONENT-270;Lo;0;L;;;;;N;;;;;
+1890E;TANGUT COMPONENT-271;Lo;0;L;;;;;N;;;;;
+1890F;TANGUT COMPONENT-272;Lo;0;L;;;;;N;;;;;
+18910;TANGUT COMPONENT-273;Lo;0;L;;;;;N;;;;;
+18911;TANGUT COMPONENT-274;Lo;0;L;;;;;N;;;;;
+18912;TANGUT COMPONENT-275;Lo;0;L;;;;;N;;;;;
+18913;TANGUT COMPONENT-276;Lo;0;L;;;;;N;;;;;
+18914;TANGUT COMPONENT-277;Lo;0;L;;;;;N;;;;;
+18915;TANGUT COMPONENT-278;Lo;0;L;;;;;N;;;;;
+18916;TANGUT COMPONENT-279;Lo;0;L;;;;;N;;;;;
+18917;TANGUT COMPONENT-280;Lo;0;L;;;;;N;;;;;
+18918;TANGUT COMPONENT-281;Lo;0;L;;;;;N;;;;;
+18919;TANGUT COMPONENT-282;Lo;0;L;;;;;N;;;;;
+1891A;TANGUT COMPONENT-283;Lo;0;L;;;;;N;;;;;
+1891B;TANGUT COMPONENT-284;Lo;0;L;;;;;N;;;;;
+1891C;TANGUT COMPONENT-285;Lo;0;L;;;;;N;;;;;
+1891D;TANGUT COMPONENT-286;Lo;0;L;;;;;N;;;;;
+1891E;TANGUT COMPONENT-287;Lo;0;L;;;;;N;;;;;
+1891F;TANGUT COMPONENT-288;Lo;0;L;;;;;N;;;;;
+18920;TANGUT COMPONENT-289;Lo;0;L;;;;;N;;;;;
+18921;TANGUT COMPONENT-290;Lo;0;L;;;;;N;;;;;
+18922;TANGUT COMPONENT-291;Lo;0;L;;;;;N;;;;;
+18923;TANGUT COMPONENT-292;Lo;0;L;;;;;N;;;;;
+18924;TANGUT COMPONENT-293;Lo;0;L;;;;;N;;;;;
+18925;TANGUT COMPONENT-294;Lo;0;L;;;;;N;;;;;
+18926;TANGUT COMPONENT-295;Lo;0;L;;;;;N;;;;;
+18927;TANGUT COMPONENT-296;Lo;0;L;;;;;N;;;;;
+18928;TANGUT COMPONENT-297;Lo;0;L;;;;;N;;;;;
+18929;TANGUT COMPONENT-298;Lo;0;L;;;;;N;;;;;
+1892A;TANGUT COMPONENT-299;Lo;0;L;;;;;N;;;;;
+1892B;TANGUT COMPONENT-300;Lo;0;L;;;;;N;;;;;
+1892C;TANGUT COMPONENT-301;Lo;0;L;;;;;N;;;;;
+1892D;TANGUT COMPONENT-302;Lo;0;L;;;;;N;;;;;
+1892E;TANGUT COMPONENT-303;Lo;0;L;;;;;N;;;;;
+1892F;TANGUT COMPONENT-304;Lo;0;L;;;;;N;;;;;
+18930;TANGUT COMPONENT-305;Lo;0;L;;;;;N;;;;;
+18931;TANGUT COMPONENT-306;Lo;0;L;;;;;N;;;;;
+18932;TANGUT COMPONENT-307;Lo;0;L;;;;;N;;;;;
+18933;TANGUT COMPONENT-308;Lo;0;L;;;;;N;;;;;
+18934;TANGUT COMPONENT-309;Lo;0;L;;;;;N;;;;;
+18935;TANGUT COMPONENT-310;Lo;0;L;;;;;N;;;;;
+18936;TANGUT COMPONENT-311;Lo;0;L;;;;;N;;;;;
+18937;TANGUT COMPONENT-312;Lo;0;L;;;;;N;;;;;
+18938;TANGUT COMPONENT-313;Lo;0;L;;;;;N;;;;;
+18939;TANGUT COMPONENT-314;Lo;0;L;;;;;N;;;;;
+1893A;TANGUT COMPONENT-315;Lo;0;L;;;;;N;;;;;
+1893B;TANGUT COMPONENT-316;Lo;0;L;;;;;N;;;;;
+1893C;TANGUT COMPONENT-317;Lo;0;L;;;;;N;;;;;
+1893D;TANGUT COMPONENT-318;Lo;0;L;;;;;N;;;;;
+1893E;TANGUT COMPONENT-319;Lo;0;L;;;;;N;;;;;
+1893F;TANGUT COMPONENT-320;Lo;0;L;;;;;N;;;;;
+18940;TANGUT COMPONENT-321;Lo;0;L;;;;;N;;;;;
+18941;TANGUT COMPONENT-322;Lo;0;L;;;;;N;;;;;
+18942;TANGUT COMPONENT-323;Lo;0;L;;;;;N;;;;;
+18943;TANGUT COMPONENT-324;Lo;0;L;;;;;N;;;;;
+18944;TANGUT COMPONENT-325;Lo;0;L;;;;;N;;;;;
+18945;TANGUT COMPONENT-326;Lo;0;L;;;;;N;;;;;
+18946;TANGUT COMPONENT-327;Lo;0;L;;;;;N;;;;;
+18947;TANGUT COMPONENT-328;Lo;0;L;;;;;N;;;;;
+18948;TANGUT COMPONENT-329;Lo;0;L;;;;;N;;;;;
+18949;TANGUT COMPONENT-330;Lo;0;L;;;;;N;;;;;
+1894A;TANGUT COMPONENT-331;Lo;0;L;;;;;N;;;;;
+1894B;TANGUT COMPONENT-332;Lo;0;L;;;;;N;;;;;
+1894C;TANGUT COMPONENT-333;Lo;0;L;;;;;N;;;;;
+1894D;TANGUT COMPONENT-334;Lo;0;L;;;;;N;;;;;
+1894E;TANGUT COMPONENT-335;Lo;0;L;;;;;N;;;;;
+1894F;TANGUT COMPONENT-336;Lo;0;L;;;;;N;;;;;
+18950;TANGUT COMPONENT-337;Lo;0;L;;;;;N;;;;;
+18951;TANGUT COMPONENT-338;Lo;0;L;;;;;N;;;;;
+18952;TANGUT COMPONENT-339;Lo;0;L;;;;;N;;;;;
+18953;TANGUT COMPONENT-340;Lo;0;L;;;;;N;;;;;
+18954;TANGUT COMPONENT-341;Lo;0;L;;;;;N;;;;;
+18955;TANGUT COMPONENT-342;Lo;0;L;;;;;N;;;;;
+18956;TANGUT COMPONENT-343;Lo;0;L;;;;;N;;;;;
+18957;TANGUT COMPONENT-344;Lo;0;L;;;;;N;;;;;
+18958;TANGUT COMPONENT-345;Lo;0;L;;;;;N;;;;;
+18959;TANGUT COMPONENT-346;Lo;0;L;;;;;N;;;;;
+1895A;TANGUT COMPONENT-347;Lo;0;L;;;;;N;;;;;
+1895B;TANGUT COMPONENT-348;Lo;0;L;;;;;N;;;;;
+1895C;TANGUT COMPONENT-349;Lo;0;L;;;;;N;;;;;
+1895D;TANGUT COMPONENT-350;Lo;0;L;;;;;N;;;;;
+1895E;TANGUT COMPONENT-351;Lo;0;L;;;;;N;;;;;
+1895F;TANGUT COMPONENT-352;Lo;0;L;;;;;N;;;;;
+18960;TANGUT COMPONENT-353;Lo;0;L;;;;;N;;;;;
+18961;TANGUT COMPONENT-354;Lo;0;L;;;;;N;;;;;
+18962;TANGUT COMPONENT-355;Lo;0;L;;;;;N;;;;;
+18963;TANGUT COMPONENT-356;Lo;0;L;;;;;N;;;;;
+18964;TANGUT COMPONENT-357;Lo;0;L;;;;;N;;;;;
+18965;TANGUT COMPONENT-358;Lo;0;L;;;;;N;;;;;
+18966;TANGUT COMPONENT-359;Lo;0;L;;;;;N;;;;;
+18967;TANGUT COMPONENT-360;Lo;0;L;;;;;N;;;;;
+18968;TANGUT COMPONENT-361;Lo;0;L;;;;;N;;;;;
+18969;TANGUT COMPONENT-362;Lo;0;L;;;;;N;;;;;
+1896A;TANGUT COMPONENT-363;Lo;0;L;;;;;N;;;;;
+1896B;TANGUT COMPONENT-364;Lo;0;L;;;;;N;;;;;
+1896C;TANGUT COMPONENT-365;Lo;0;L;;;;;N;;;;;
+1896D;TANGUT COMPONENT-366;Lo;0;L;;;;;N;;;;;
+1896E;TANGUT COMPONENT-367;Lo;0;L;;;;;N;;;;;
+1896F;TANGUT COMPONENT-368;Lo;0;L;;;;;N;;;;;
+18970;TANGUT COMPONENT-369;Lo;0;L;;;;;N;;;;;
+18971;TANGUT COMPONENT-370;Lo;0;L;;;;;N;;;;;
+18972;TANGUT COMPONENT-371;Lo;0;L;;;;;N;;;;;
+18973;TANGUT COMPONENT-372;Lo;0;L;;;;;N;;;;;
+18974;TANGUT COMPONENT-373;Lo;0;L;;;;;N;;;;;
+18975;TANGUT COMPONENT-374;Lo;0;L;;;;;N;;;;;
+18976;TANGUT COMPONENT-375;Lo;0;L;;;;;N;;;;;
+18977;TANGUT COMPONENT-376;Lo;0;L;;;;;N;;;;;
+18978;TANGUT COMPONENT-377;Lo;0;L;;;;;N;;;;;
+18979;TANGUT COMPONENT-378;Lo;0;L;;;;;N;;;;;
+1897A;TANGUT COMPONENT-379;Lo;0;L;;;;;N;;;;;
+1897B;TANGUT COMPONENT-380;Lo;0;L;;;;;N;;;;;
+1897C;TANGUT COMPONENT-381;Lo;0;L;;;;;N;;;;;
+1897D;TANGUT COMPONENT-382;Lo;0;L;;;;;N;;;;;
+1897E;TANGUT COMPONENT-383;Lo;0;L;;;;;N;;;;;
+1897F;TANGUT COMPONENT-384;Lo;0;L;;;;;N;;;;;
+18980;TANGUT COMPONENT-385;Lo;0;L;;;;;N;;;;;
+18981;TANGUT COMPONENT-386;Lo;0;L;;;;;N;;;;;
+18982;TANGUT COMPONENT-387;Lo;0;L;;;;;N;;;;;
+18983;TANGUT COMPONENT-388;Lo;0;L;;;;;N;;;;;
+18984;TANGUT COMPONENT-389;Lo;0;L;;;;;N;;;;;
+18985;TANGUT COMPONENT-390;Lo;0;L;;;;;N;;;;;
+18986;TANGUT COMPONENT-391;Lo;0;L;;;;;N;;;;;
+18987;TANGUT COMPONENT-392;Lo;0;L;;;;;N;;;;;
+18988;TANGUT COMPONENT-393;Lo;0;L;;;;;N;;;;;
+18989;TANGUT COMPONENT-394;Lo;0;L;;;;;N;;;;;
+1898A;TANGUT COMPONENT-395;Lo;0;L;;;;;N;;;;;
+1898B;TANGUT COMPONENT-396;Lo;0;L;;;;;N;;;;;
+1898C;TANGUT COMPONENT-397;Lo;0;L;;;;;N;;;;;
+1898D;TANGUT COMPONENT-398;Lo;0;L;;;;;N;;;;;
+1898E;TANGUT COMPONENT-399;Lo;0;L;;;;;N;;;;;
+1898F;TANGUT COMPONENT-400;Lo;0;L;;;;;N;;;;;
+18990;TANGUT COMPONENT-401;Lo;0;L;;;;;N;;;;;
+18991;TANGUT COMPONENT-402;Lo;0;L;;;;;N;;;;;
+18992;TANGUT COMPONENT-403;Lo;0;L;;;;;N;;;;;
+18993;TANGUT COMPONENT-404;Lo;0;L;;;;;N;;;;;
+18994;TANGUT COMPONENT-405;Lo;0;L;;;;;N;;;;;
+18995;TANGUT COMPONENT-406;Lo;0;L;;;;;N;;;;;
+18996;TANGUT COMPONENT-407;Lo;0;L;;;;;N;;;;;
+18997;TANGUT COMPONENT-408;Lo;0;L;;;;;N;;;;;
+18998;TANGUT COMPONENT-409;Lo;0;L;;;;;N;;;;;
+18999;TANGUT COMPONENT-410;Lo;0;L;;;;;N;;;;;
+1899A;TANGUT COMPONENT-411;Lo;0;L;;;;;N;;;;;
+1899B;TANGUT COMPONENT-412;Lo;0;L;;;;;N;;;;;
+1899C;TANGUT COMPONENT-413;Lo;0;L;;;;;N;;;;;
+1899D;TANGUT COMPONENT-414;Lo;0;L;;;;;N;;;;;
+1899E;TANGUT COMPONENT-415;Lo;0;L;;;;;N;;;;;
+1899F;TANGUT COMPONENT-416;Lo;0;L;;;;;N;;;;;
+189A0;TANGUT COMPONENT-417;Lo;0;L;;;;;N;;;;;
+189A1;TANGUT COMPONENT-418;Lo;0;L;;;;;N;;;;;
+189A2;TANGUT COMPONENT-419;Lo;0;L;;;;;N;;;;;
+189A3;TANGUT COMPONENT-420;Lo;0;L;;;;;N;;;;;
+189A4;TANGUT COMPONENT-421;Lo;0;L;;;;;N;;;;;
+189A5;TANGUT COMPONENT-422;Lo;0;L;;;;;N;;;;;
+189A6;TANGUT COMPONENT-423;Lo;0;L;;;;;N;;;;;
+189A7;TANGUT COMPONENT-424;Lo;0;L;;;;;N;;;;;
+189A8;TANGUT COMPONENT-425;Lo;0;L;;;;;N;;;;;
+189A9;TANGUT COMPONENT-426;Lo;0;L;;;;;N;;;;;
+189AA;TANGUT COMPONENT-427;Lo;0;L;;;;;N;;;;;
+189AB;TANGUT COMPONENT-428;Lo;0;L;;;;;N;;;;;
+189AC;TANGUT COMPONENT-429;Lo;0;L;;;;;N;;;;;
+189AD;TANGUT COMPONENT-430;Lo;0;L;;;;;N;;;;;
+189AE;TANGUT COMPONENT-431;Lo;0;L;;;;;N;;;;;
+189AF;TANGUT COMPONENT-432;Lo;0;L;;;;;N;;;;;
+189B0;TANGUT COMPONENT-433;Lo;0;L;;;;;N;;;;;
+189B1;TANGUT COMPONENT-434;Lo;0;L;;;;;N;;;;;
+189B2;TANGUT COMPONENT-435;Lo;0;L;;;;;N;;;;;
+189B3;TANGUT COMPONENT-436;Lo;0;L;;;;;N;;;;;
+189B4;TANGUT COMPONENT-437;Lo;0;L;;;;;N;;;;;
+189B5;TANGUT COMPONENT-438;Lo;0;L;;;;;N;;;;;
+189B6;TANGUT COMPONENT-439;Lo;0;L;;;;;N;;;;;
+189B7;TANGUT COMPONENT-440;Lo;0;L;;;;;N;;;;;
+189B8;TANGUT COMPONENT-441;Lo;0;L;;;;;N;;;;;
+189B9;TANGUT COMPONENT-442;Lo;0;L;;;;;N;;;;;
+189BA;TANGUT COMPONENT-443;Lo;0;L;;;;;N;;;;;
+189BB;TANGUT COMPONENT-444;Lo;0;L;;;;;N;;;;;
+189BC;TANGUT COMPONENT-445;Lo;0;L;;;;;N;;;;;
+189BD;TANGUT COMPONENT-446;Lo;0;L;;;;;N;;;;;
+189BE;TANGUT COMPONENT-447;Lo;0;L;;;;;N;;;;;
+189BF;TANGUT COMPONENT-448;Lo;0;L;;;;;N;;;;;
+189C0;TANGUT COMPONENT-449;Lo;0;L;;;;;N;;;;;
+189C1;TANGUT COMPONENT-450;Lo;0;L;;;;;N;;;;;
+189C2;TANGUT COMPONENT-451;Lo;0;L;;;;;N;;;;;
+189C3;TANGUT COMPONENT-452;Lo;0;L;;;;;N;;;;;
+189C4;TANGUT COMPONENT-453;Lo;0;L;;;;;N;;;;;
+189C5;TANGUT COMPONENT-454;Lo;0;L;;;;;N;;;;;
+189C6;TANGUT COMPONENT-455;Lo;0;L;;;;;N;;;;;
+189C7;TANGUT COMPONENT-456;Lo;0;L;;;;;N;;;;;
+189C8;TANGUT COMPONENT-457;Lo;0;L;;;;;N;;;;;
+189C9;TANGUT COMPONENT-458;Lo;0;L;;;;;N;;;;;
+189CA;TANGUT COMPONENT-459;Lo;0;L;;;;;N;;;;;
+189CB;TANGUT COMPONENT-460;Lo;0;L;;;;;N;;;;;
+189CC;TANGUT COMPONENT-461;Lo;0;L;;;;;N;;;;;
+189CD;TANGUT COMPONENT-462;Lo;0;L;;;;;N;;;;;
+189CE;TANGUT COMPONENT-463;Lo;0;L;;;;;N;;;;;
+189CF;TANGUT COMPONENT-464;Lo;0;L;;;;;N;;;;;
+189D0;TANGUT COMPONENT-465;Lo;0;L;;;;;N;;;;;
+189D1;TANGUT COMPONENT-466;Lo;0;L;;;;;N;;;;;
+189D2;TANGUT COMPONENT-467;Lo;0;L;;;;;N;;;;;
+189D3;TANGUT COMPONENT-468;Lo;0;L;;;;;N;;;;;
+189D4;TANGUT COMPONENT-469;Lo;0;L;;;;;N;;;;;
+189D5;TANGUT COMPONENT-470;Lo;0;L;;;;;N;;;;;
+189D6;TANGUT COMPONENT-471;Lo;0;L;;;;;N;;;;;
+189D7;TANGUT COMPONENT-472;Lo;0;L;;;;;N;;;;;
+189D8;TANGUT COMPONENT-473;Lo;0;L;;;;;N;;;;;
+189D9;TANGUT COMPONENT-474;Lo;0;L;;;;;N;;;;;
+189DA;TANGUT COMPONENT-475;Lo;0;L;;;;;N;;;;;
+189DB;TANGUT COMPONENT-476;Lo;0;L;;;;;N;;;;;
+189DC;TANGUT COMPONENT-477;Lo;0;L;;;;;N;;;;;
+189DD;TANGUT COMPONENT-478;Lo;0;L;;;;;N;;;;;
+189DE;TANGUT COMPONENT-479;Lo;0;L;;;;;N;;;;;
+189DF;TANGUT COMPONENT-480;Lo;0;L;;;;;N;;;;;
+189E0;TANGUT COMPONENT-481;Lo;0;L;;;;;N;;;;;
+189E1;TANGUT COMPONENT-482;Lo;0;L;;;;;N;;;;;
+189E2;TANGUT COMPONENT-483;Lo;0;L;;;;;N;;;;;
+189E3;TANGUT COMPONENT-484;Lo;0;L;;;;;N;;;;;
+189E4;TANGUT COMPONENT-485;Lo;0;L;;;;;N;;;;;
+189E5;TANGUT COMPONENT-486;Lo;0;L;;;;;N;;;;;
+189E6;TANGUT COMPONENT-487;Lo;0;L;;;;;N;;;;;
+189E7;TANGUT COMPONENT-488;Lo;0;L;;;;;N;;;;;
+189E8;TANGUT COMPONENT-489;Lo;0;L;;;;;N;;;;;
+189E9;TANGUT COMPONENT-490;Lo;0;L;;;;;N;;;;;
+189EA;TANGUT COMPONENT-491;Lo;0;L;;;;;N;;;;;
+189EB;TANGUT COMPONENT-492;Lo;0;L;;;;;N;;;;;
+189EC;TANGUT COMPONENT-493;Lo;0;L;;;;;N;;;;;
+189ED;TANGUT COMPONENT-494;Lo;0;L;;;;;N;;;;;
+189EE;TANGUT COMPONENT-495;Lo;0;L;;;;;N;;;;;
+189EF;TANGUT COMPONENT-496;Lo;0;L;;;;;N;;;;;
+189F0;TANGUT COMPONENT-497;Lo;0;L;;;;;N;;;;;
+189F1;TANGUT COMPONENT-498;Lo;0;L;;;;;N;;;;;
+189F2;TANGUT COMPONENT-499;Lo;0;L;;;;;N;;;;;
+189F3;TANGUT COMPONENT-500;Lo;0;L;;;;;N;;;;;
+189F4;TANGUT COMPONENT-501;Lo;0;L;;;;;N;;;;;
+189F5;TANGUT COMPONENT-502;Lo;0;L;;;;;N;;;;;
+189F6;TANGUT COMPONENT-503;Lo;0;L;;;;;N;;;;;
+189F7;TANGUT COMPONENT-504;Lo;0;L;;;;;N;;;;;
+189F8;TANGUT COMPONENT-505;Lo;0;L;;;;;N;;;;;
+189F9;TANGUT COMPONENT-506;Lo;0;L;;;;;N;;;;;
+189FA;TANGUT COMPONENT-507;Lo;0;L;;;;;N;;;;;
+189FB;TANGUT COMPONENT-508;Lo;0;L;;;;;N;;;;;
+189FC;TANGUT COMPONENT-509;Lo;0;L;;;;;N;;;;;
+189FD;TANGUT COMPONENT-510;Lo;0;L;;;;;N;;;;;
+189FE;TANGUT COMPONENT-511;Lo;0;L;;;;;N;;;;;
+189FF;TANGUT COMPONENT-512;Lo;0;L;;;;;N;;;;;
+18A00;TANGUT COMPONENT-513;Lo;0;L;;;;;N;;;;;
+18A01;TANGUT COMPONENT-514;Lo;0;L;;;;;N;;;;;
+18A02;TANGUT COMPONENT-515;Lo;0;L;;;;;N;;;;;
+18A03;TANGUT COMPONENT-516;Lo;0;L;;;;;N;;;;;
+18A04;TANGUT COMPONENT-517;Lo;0;L;;;;;N;;;;;
+18A05;TANGUT COMPONENT-518;Lo;0;L;;;;;N;;;;;
+18A06;TANGUT COMPONENT-519;Lo;0;L;;;;;N;;;;;
+18A07;TANGUT COMPONENT-520;Lo;0;L;;;;;N;;;;;
+18A08;TANGUT COMPONENT-521;Lo;0;L;;;;;N;;;;;
+18A09;TANGUT COMPONENT-522;Lo;0;L;;;;;N;;;;;
+18A0A;TANGUT COMPONENT-523;Lo;0;L;;;;;N;;;;;
+18A0B;TANGUT COMPONENT-524;Lo;0;L;;;;;N;;;;;
+18A0C;TANGUT COMPONENT-525;Lo;0;L;;;;;N;;;;;
+18A0D;TANGUT COMPONENT-526;Lo;0;L;;;;;N;;;;;
+18A0E;TANGUT COMPONENT-527;Lo;0;L;;;;;N;;;;;
+18A0F;TANGUT COMPONENT-528;Lo;0;L;;;;;N;;;;;
+18A10;TANGUT COMPONENT-529;Lo;0;L;;;;;N;;;;;
+18A11;TANGUT COMPONENT-530;Lo;0;L;;;;;N;;;;;
+18A12;TANGUT COMPONENT-531;Lo;0;L;;;;;N;;;;;
+18A13;TANGUT COMPONENT-532;Lo;0;L;;;;;N;;;;;
+18A14;TANGUT COMPONENT-533;Lo;0;L;;;;;N;;;;;
+18A15;TANGUT COMPONENT-534;Lo;0;L;;;;;N;;;;;
+18A16;TANGUT COMPONENT-535;Lo;0;L;;;;;N;;;;;
+18A17;TANGUT COMPONENT-536;Lo;0;L;;;;;N;;;;;
+18A18;TANGUT COMPONENT-537;Lo;0;L;;;;;N;;;;;
+18A19;TANGUT COMPONENT-538;Lo;0;L;;;;;N;;;;;
+18A1A;TANGUT COMPONENT-539;Lo;0;L;;;;;N;;;;;
+18A1B;TANGUT COMPONENT-540;Lo;0;L;;;;;N;;;;;
+18A1C;TANGUT COMPONENT-541;Lo;0;L;;;;;N;;;;;
+18A1D;TANGUT COMPONENT-542;Lo;0;L;;;;;N;;;;;
+18A1E;TANGUT COMPONENT-543;Lo;0;L;;;;;N;;;;;
+18A1F;TANGUT COMPONENT-544;Lo;0;L;;;;;N;;;;;
+18A20;TANGUT COMPONENT-545;Lo;0;L;;;;;N;;;;;
+18A21;TANGUT COMPONENT-546;Lo;0;L;;;;;N;;;;;
+18A22;TANGUT COMPONENT-547;Lo;0;L;;;;;N;;;;;
+18A23;TANGUT COMPONENT-548;Lo;0;L;;;;;N;;;;;
+18A24;TANGUT COMPONENT-549;Lo;0;L;;;;;N;;;;;
+18A25;TANGUT COMPONENT-550;Lo;0;L;;;;;N;;;;;
+18A26;TANGUT COMPONENT-551;Lo;0;L;;;;;N;;;;;
+18A27;TANGUT COMPONENT-552;Lo;0;L;;;;;N;;;;;
+18A28;TANGUT COMPONENT-553;Lo;0;L;;;;;N;;;;;
+18A29;TANGUT COMPONENT-554;Lo;0;L;;;;;N;;;;;
+18A2A;TANGUT COMPONENT-555;Lo;0;L;;;;;N;;;;;
+18A2B;TANGUT COMPONENT-556;Lo;0;L;;;;;N;;;;;
+18A2C;TANGUT COMPONENT-557;Lo;0;L;;;;;N;;;;;
+18A2D;TANGUT COMPONENT-558;Lo;0;L;;;;;N;;;;;
+18A2E;TANGUT COMPONENT-559;Lo;0;L;;;;;N;;;;;
+18A2F;TANGUT COMPONENT-560;Lo;0;L;;;;;N;;;;;
+18A30;TANGUT COMPONENT-561;Lo;0;L;;;;;N;;;;;
+18A31;TANGUT COMPONENT-562;Lo;0;L;;;;;N;;;;;
+18A32;TANGUT COMPONENT-563;Lo;0;L;;;;;N;;;;;
+18A33;TANGUT COMPONENT-564;Lo;0;L;;;;;N;;;;;
+18A34;TANGUT COMPONENT-565;Lo;0;L;;;;;N;;;;;
+18A35;TANGUT COMPONENT-566;Lo;0;L;;;;;N;;;;;
+18A36;TANGUT COMPONENT-567;Lo;0;L;;;;;N;;;;;
+18A37;TANGUT COMPONENT-568;Lo;0;L;;;;;N;;;;;
+18A38;TANGUT COMPONENT-569;Lo;0;L;;;;;N;;;;;
+18A39;TANGUT COMPONENT-570;Lo;0;L;;;;;N;;;;;
+18A3A;TANGUT COMPONENT-571;Lo;0;L;;;;;N;;;;;
+18A3B;TANGUT COMPONENT-572;Lo;0;L;;;;;N;;;;;
+18A3C;TANGUT COMPONENT-573;Lo;0;L;;;;;N;;;;;
+18A3D;TANGUT COMPONENT-574;Lo;0;L;;;;;N;;;;;
+18A3E;TANGUT COMPONENT-575;Lo;0;L;;;;;N;;;;;
+18A3F;TANGUT COMPONENT-576;Lo;0;L;;;;;N;;;;;
+18A40;TANGUT COMPONENT-577;Lo;0;L;;;;;N;;;;;
+18A41;TANGUT COMPONENT-578;Lo;0;L;;;;;N;;;;;
+18A42;TANGUT COMPONENT-579;Lo;0;L;;;;;N;;;;;
+18A43;TANGUT COMPONENT-580;Lo;0;L;;;;;N;;;;;
+18A44;TANGUT COMPONENT-581;Lo;0;L;;;;;N;;;;;
+18A45;TANGUT COMPONENT-582;Lo;0;L;;;;;N;;;;;
+18A46;TANGUT COMPONENT-583;Lo;0;L;;;;;N;;;;;
+18A47;TANGUT COMPONENT-584;Lo;0;L;;;;;N;;;;;
+18A48;TANGUT COMPONENT-585;Lo;0;L;;;;;N;;;;;
+18A49;TANGUT COMPONENT-586;Lo;0;L;;;;;N;;;;;
+18A4A;TANGUT COMPONENT-587;Lo;0;L;;;;;N;;;;;
+18A4B;TANGUT COMPONENT-588;Lo;0;L;;;;;N;;;;;
+18A4C;TANGUT COMPONENT-589;Lo;0;L;;;;;N;;;;;
+18A4D;TANGUT COMPONENT-590;Lo;0;L;;;;;N;;;;;
+18A4E;TANGUT COMPONENT-591;Lo;0;L;;;;;N;;;;;
+18A4F;TANGUT COMPONENT-592;Lo;0;L;;;;;N;;;;;
+18A50;TANGUT COMPONENT-593;Lo;0;L;;;;;N;;;;;
+18A51;TANGUT COMPONENT-594;Lo;0;L;;;;;N;;;;;
+18A52;TANGUT COMPONENT-595;Lo;0;L;;;;;N;;;;;
+18A53;TANGUT COMPONENT-596;Lo;0;L;;;;;N;;;;;
+18A54;TANGUT COMPONENT-597;Lo;0;L;;;;;N;;;;;
+18A55;TANGUT COMPONENT-598;Lo;0;L;;;;;N;;;;;
+18A56;TANGUT COMPONENT-599;Lo;0;L;;;;;N;;;;;
+18A57;TANGUT COMPONENT-600;Lo;0;L;;;;;N;;;;;
+18A58;TANGUT COMPONENT-601;Lo;0;L;;;;;N;;;;;
+18A59;TANGUT COMPONENT-602;Lo;0;L;;;;;N;;;;;
+18A5A;TANGUT COMPONENT-603;Lo;0;L;;;;;N;;;;;
+18A5B;TANGUT COMPONENT-604;Lo;0;L;;;;;N;;;;;
+18A5C;TANGUT COMPONENT-605;Lo;0;L;;;;;N;;;;;
+18A5D;TANGUT COMPONENT-606;Lo;0;L;;;;;N;;;;;
+18A5E;TANGUT COMPONENT-607;Lo;0;L;;;;;N;;;;;
+18A5F;TANGUT COMPONENT-608;Lo;0;L;;;;;N;;;;;
+18A60;TANGUT COMPONENT-609;Lo;0;L;;;;;N;;;;;
+18A61;TANGUT COMPONENT-610;Lo;0;L;;;;;N;;;;;
+18A62;TANGUT COMPONENT-611;Lo;0;L;;;;;N;;;;;
+18A63;TANGUT COMPONENT-612;Lo;0;L;;;;;N;;;;;
+18A64;TANGUT COMPONENT-613;Lo;0;L;;;;;N;;;;;
+18A65;TANGUT COMPONENT-614;Lo;0;L;;;;;N;;;;;
+18A66;TANGUT COMPONENT-615;Lo;0;L;;;;;N;;;;;
+18A67;TANGUT COMPONENT-616;Lo;0;L;;;;;N;;;;;
+18A68;TANGUT COMPONENT-617;Lo;0;L;;;;;N;;;;;
+18A69;TANGUT COMPONENT-618;Lo;0;L;;;;;N;;;;;
+18A6A;TANGUT COMPONENT-619;Lo;0;L;;;;;N;;;;;
+18A6B;TANGUT COMPONENT-620;Lo;0;L;;;;;N;;;;;
+18A6C;TANGUT COMPONENT-621;Lo;0;L;;;;;N;;;;;
+18A6D;TANGUT COMPONENT-622;Lo;0;L;;;;;N;;;;;
+18A6E;TANGUT COMPONENT-623;Lo;0;L;;;;;N;;;;;
+18A6F;TANGUT COMPONENT-624;Lo;0;L;;;;;N;;;;;
+18A70;TANGUT COMPONENT-625;Lo;0;L;;;;;N;;;;;
+18A71;TANGUT COMPONENT-626;Lo;0;L;;;;;N;;;;;
+18A72;TANGUT COMPONENT-627;Lo;0;L;;;;;N;;;;;
+18A73;TANGUT COMPONENT-628;Lo;0;L;;;;;N;;;;;
+18A74;TANGUT COMPONENT-629;Lo;0;L;;;;;N;;;;;
+18A75;TANGUT COMPONENT-630;Lo;0;L;;;;;N;;;;;
+18A76;TANGUT COMPONENT-631;Lo;0;L;;;;;N;;;;;
+18A77;TANGUT COMPONENT-632;Lo;0;L;;;;;N;;;;;
+18A78;TANGUT COMPONENT-633;Lo;0;L;;;;;N;;;;;
+18A79;TANGUT COMPONENT-634;Lo;0;L;;;;;N;;;;;
+18A7A;TANGUT COMPONENT-635;Lo;0;L;;;;;N;;;;;
+18A7B;TANGUT COMPONENT-636;Lo;0;L;;;;;N;;;;;
+18A7C;TANGUT COMPONENT-637;Lo;0;L;;;;;N;;;;;
+18A7D;TANGUT COMPONENT-638;Lo;0;L;;;;;N;;;;;
+18A7E;TANGUT COMPONENT-639;Lo;0;L;;;;;N;;;;;
+18A7F;TANGUT COMPONENT-640;Lo;0;L;;;;;N;;;;;
+18A80;TANGUT COMPONENT-641;Lo;0;L;;;;;N;;;;;
+18A81;TANGUT COMPONENT-642;Lo;0;L;;;;;N;;;;;
+18A82;TANGUT COMPONENT-643;Lo;0;L;;;;;N;;;;;
+18A83;TANGUT COMPONENT-644;Lo;0;L;;;;;N;;;;;
+18A84;TANGUT COMPONENT-645;Lo;0;L;;;;;N;;;;;
+18A85;TANGUT COMPONENT-646;Lo;0;L;;;;;N;;;;;
+18A86;TANGUT COMPONENT-647;Lo;0;L;;;;;N;;;;;
+18A87;TANGUT COMPONENT-648;Lo;0;L;;;;;N;;;;;
+18A88;TANGUT COMPONENT-649;Lo;0;L;;;;;N;;;;;
+18A89;TANGUT COMPONENT-650;Lo;0;L;;;;;N;;;;;
+18A8A;TANGUT COMPONENT-651;Lo;0;L;;;;;N;;;;;
+18A8B;TANGUT COMPONENT-652;Lo;0;L;;;;;N;;;;;
+18A8C;TANGUT COMPONENT-653;Lo;0;L;;;;;N;;;;;
+18A8D;TANGUT COMPONENT-654;Lo;0;L;;;;;N;;;;;
+18A8E;TANGUT COMPONENT-655;Lo;0;L;;;;;N;;;;;
+18A8F;TANGUT COMPONENT-656;Lo;0;L;;;;;N;;;;;
+18A90;TANGUT COMPONENT-657;Lo;0;L;;;;;N;;;;;
+18A91;TANGUT COMPONENT-658;Lo;0;L;;;;;N;;;;;
+18A92;TANGUT COMPONENT-659;Lo;0;L;;;;;N;;;;;
+18A93;TANGUT COMPONENT-660;Lo;0;L;;;;;N;;;;;
+18A94;TANGUT COMPONENT-661;Lo;0;L;;;;;N;;;;;
+18A95;TANGUT COMPONENT-662;Lo;0;L;;;;;N;;;;;
+18A96;TANGUT COMPONENT-663;Lo;0;L;;;;;N;;;;;
+18A97;TANGUT COMPONENT-664;Lo;0;L;;;;;N;;;;;
+18A98;TANGUT COMPONENT-665;Lo;0;L;;;;;N;;;;;
+18A99;TANGUT COMPONENT-666;Lo;0;L;;;;;N;;;;;
+18A9A;TANGUT COMPONENT-667;Lo;0;L;;;;;N;;;;;
+18A9B;TANGUT COMPONENT-668;Lo;0;L;;;;;N;;;;;
+18A9C;TANGUT COMPONENT-669;Lo;0;L;;;;;N;;;;;
+18A9D;TANGUT COMPONENT-670;Lo;0;L;;;;;N;;;;;
+18A9E;TANGUT COMPONENT-671;Lo;0;L;;;;;N;;;;;
+18A9F;TANGUT COMPONENT-672;Lo;0;L;;;;;N;;;;;
+18AA0;TANGUT COMPONENT-673;Lo;0;L;;;;;N;;;;;
+18AA1;TANGUT COMPONENT-674;Lo;0;L;;;;;N;;;;;
+18AA2;TANGUT COMPONENT-675;Lo;0;L;;;;;N;;;;;
+18AA3;TANGUT COMPONENT-676;Lo;0;L;;;;;N;;;;;
+18AA4;TANGUT COMPONENT-677;Lo;0;L;;;;;N;;;;;
+18AA5;TANGUT COMPONENT-678;Lo;0;L;;;;;N;;;;;
+18AA6;TANGUT COMPONENT-679;Lo;0;L;;;;;N;;;;;
+18AA7;TANGUT COMPONENT-680;Lo;0;L;;;;;N;;;;;
+18AA8;TANGUT COMPONENT-681;Lo;0;L;;;;;N;;;;;
+18AA9;TANGUT COMPONENT-682;Lo;0;L;;;;;N;;;;;
+18AAA;TANGUT COMPONENT-683;Lo;0;L;;;;;N;;;;;
+18AAB;TANGUT COMPONENT-684;Lo;0;L;;;;;N;;;;;
+18AAC;TANGUT COMPONENT-685;Lo;0;L;;;;;N;;;;;
+18AAD;TANGUT COMPONENT-686;Lo;0;L;;;;;N;;;;;
+18AAE;TANGUT COMPONENT-687;Lo;0;L;;;;;N;;;;;
+18AAF;TANGUT COMPONENT-688;Lo;0;L;;;;;N;;;;;
+18AB0;TANGUT COMPONENT-689;Lo;0;L;;;;;N;;;;;
+18AB1;TANGUT COMPONENT-690;Lo;0;L;;;;;N;;;;;
+18AB2;TANGUT COMPONENT-691;Lo;0;L;;;;;N;;;;;
+18AB3;TANGUT COMPONENT-692;Lo;0;L;;;;;N;;;;;
+18AB4;TANGUT COMPONENT-693;Lo;0;L;;;;;N;;;;;
+18AB5;TANGUT COMPONENT-694;Lo;0;L;;;;;N;;;;;
+18AB6;TANGUT COMPONENT-695;Lo;0;L;;;;;N;;;;;
+18AB7;TANGUT COMPONENT-696;Lo;0;L;;;;;N;;;;;
+18AB8;TANGUT COMPONENT-697;Lo;0;L;;;;;N;;;;;
+18AB9;TANGUT COMPONENT-698;Lo;0;L;;;;;N;;;;;
+18ABA;TANGUT COMPONENT-699;Lo;0;L;;;;;N;;;;;
+18ABB;TANGUT COMPONENT-700;Lo;0;L;;;;;N;;;;;
+18ABC;TANGUT COMPONENT-701;Lo;0;L;;;;;N;;;;;
+18ABD;TANGUT COMPONENT-702;Lo;0;L;;;;;N;;;;;
+18ABE;TANGUT COMPONENT-703;Lo;0;L;;;;;N;;;;;
+18ABF;TANGUT COMPONENT-704;Lo;0;L;;;;;N;;;;;
+18AC0;TANGUT COMPONENT-705;Lo;0;L;;;;;N;;;;;
+18AC1;TANGUT COMPONENT-706;Lo;0;L;;;;;N;;;;;
+18AC2;TANGUT COMPONENT-707;Lo;0;L;;;;;N;;;;;
+18AC3;TANGUT COMPONENT-708;Lo;0;L;;;;;N;;;;;
+18AC4;TANGUT COMPONENT-709;Lo;0;L;;;;;N;;;;;
+18AC5;TANGUT COMPONENT-710;Lo;0;L;;;;;N;;;;;
+18AC6;TANGUT COMPONENT-711;Lo;0;L;;;;;N;;;;;
+18AC7;TANGUT COMPONENT-712;Lo;0;L;;;;;N;;;;;
+18AC8;TANGUT COMPONENT-713;Lo;0;L;;;;;N;;;;;
+18AC9;TANGUT COMPONENT-714;Lo;0;L;;;;;N;;;;;
+18ACA;TANGUT COMPONENT-715;Lo;0;L;;;;;N;;;;;
+18ACB;TANGUT COMPONENT-716;Lo;0;L;;;;;N;;;;;
+18ACC;TANGUT COMPONENT-717;Lo;0;L;;;;;N;;;;;
+18ACD;TANGUT COMPONENT-718;Lo;0;L;;;;;N;;;;;
+18ACE;TANGUT COMPONENT-719;Lo;0;L;;;;;N;;;;;
+18ACF;TANGUT COMPONENT-720;Lo;0;L;;;;;N;;;;;
+18AD0;TANGUT COMPONENT-721;Lo;0;L;;;;;N;;;;;
+18AD1;TANGUT COMPONENT-722;Lo;0;L;;;;;N;;;;;
+18AD2;TANGUT COMPONENT-723;Lo;0;L;;;;;N;;;;;
+18AD3;TANGUT COMPONENT-724;Lo;0;L;;;;;N;;;;;
+18AD4;TANGUT COMPONENT-725;Lo;0;L;;;;;N;;;;;
+18AD5;TANGUT COMPONENT-726;Lo;0;L;;;;;N;;;;;
+18AD6;TANGUT COMPONENT-727;Lo;0;L;;;;;N;;;;;
+18AD7;TANGUT COMPONENT-728;Lo;0;L;;;;;N;;;;;
+18AD8;TANGUT COMPONENT-729;Lo;0;L;;;;;N;;;;;
+18AD9;TANGUT COMPONENT-730;Lo;0;L;;;;;N;;;;;
+18ADA;TANGUT COMPONENT-731;Lo;0;L;;;;;N;;;;;
+18ADB;TANGUT COMPONENT-732;Lo;0;L;;;;;N;;;;;
+18ADC;TANGUT COMPONENT-733;Lo;0;L;;;;;N;;;;;
+18ADD;TANGUT COMPONENT-734;Lo;0;L;;;;;N;;;;;
+18ADE;TANGUT COMPONENT-735;Lo;0;L;;;;;N;;;;;
+18ADF;TANGUT COMPONENT-736;Lo;0;L;;;;;N;;;;;
+18AE0;TANGUT COMPONENT-737;Lo;0;L;;;;;N;;;;;
+18AE1;TANGUT COMPONENT-738;Lo;0;L;;;;;N;;;;;
+18AE2;TANGUT COMPONENT-739;Lo;0;L;;;;;N;;;;;
+18AE3;TANGUT COMPONENT-740;Lo;0;L;;;;;N;;;;;
+18AE4;TANGUT COMPONENT-741;Lo;0;L;;;;;N;;;;;
+18AE5;TANGUT COMPONENT-742;Lo;0;L;;;;;N;;;;;
+18AE6;TANGUT COMPONENT-743;Lo;0;L;;;;;N;;;;;
+18AE7;TANGUT COMPONENT-744;Lo;0;L;;;;;N;;;;;
+18AE8;TANGUT COMPONENT-745;Lo;0;L;;;;;N;;;;;
+18AE9;TANGUT COMPONENT-746;Lo;0;L;;;;;N;;;;;
+18AEA;TANGUT COMPONENT-747;Lo;0;L;;;;;N;;;;;
+18AEB;TANGUT COMPONENT-748;Lo;0;L;;;;;N;;;;;
+18AEC;TANGUT COMPONENT-749;Lo;0;L;;;;;N;;;;;
+18AED;TANGUT COMPONENT-750;Lo;0;L;;;;;N;;;;;
+18AEE;TANGUT COMPONENT-751;Lo;0;L;;;;;N;;;;;
+18AEF;TANGUT COMPONENT-752;Lo;0;L;;;;;N;;;;;
+18AF0;TANGUT COMPONENT-753;Lo;0;L;;;;;N;;;;;
+18AF1;TANGUT COMPONENT-754;Lo;0;L;;;;;N;;;;;
+18AF2;TANGUT COMPONENT-755;Lo;0;L;;;;;N;;;;;
+1B000;KATAKANA LETTER ARCHAIC E;Lo;0;L;;;;;N;;;;;
+1B001;HIRAGANA LETTER ARCHAIC YE;Lo;0;L;;;;;N;;;;;
+1B002;HENTAIGANA LETTER A-1;Lo;0;L;;;;;N;;;;;
+1B003;HENTAIGANA LETTER A-2;Lo;0;L;;;;;N;;;;;
+1B004;HENTAIGANA LETTER A-3;Lo;0;L;;;;;N;;;;;
+1B005;HENTAIGANA LETTER A-WO;Lo;0;L;;;;;N;;;;;
+1B006;HENTAIGANA LETTER I-1;Lo;0;L;;;;;N;;;;;
+1B007;HENTAIGANA LETTER I-2;Lo;0;L;;;;;N;;;;;
+1B008;HENTAIGANA LETTER I-3;Lo;0;L;;;;;N;;;;;
+1B009;HENTAIGANA LETTER I-4;Lo;0;L;;;;;N;;;;;
+1B00A;HENTAIGANA LETTER U-1;Lo;0;L;;;;;N;;;;;
+1B00B;HENTAIGANA LETTER U-2;Lo;0;L;;;;;N;;;;;
+1B00C;HENTAIGANA LETTER U-3;Lo;0;L;;;;;N;;;;;
+1B00D;HENTAIGANA LETTER U-4;Lo;0;L;;;;;N;;;;;
+1B00E;HENTAIGANA LETTER U-5;Lo;0;L;;;;;N;;;;;
+1B00F;HENTAIGANA LETTER E-2;Lo;0;L;;;;;N;;;;;
+1B010;HENTAIGANA LETTER E-3;Lo;0;L;;;;;N;;;;;
+1B011;HENTAIGANA LETTER E-4;Lo;0;L;;;;;N;;;;;
+1B012;HENTAIGANA LETTER E-5;Lo;0;L;;;;;N;;;;;
+1B013;HENTAIGANA LETTER E-6;Lo;0;L;;;;;N;;;;;
+1B014;HENTAIGANA LETTER O-1;Lo;0;L;;;;;N;;;;;
+1B015;HENTAIGANA LETTER O-2;Lo;0;L;;;;;N;;;;;
+1B016;HENTAIGANA LETTER O-3;Lo;0;L;;;;;N;;;;;
+1B017;HENTAIGANA LETTER KA-1;Lo;0;L;;;;;N;;;;;
+1B018;HENTAIGANA LETTER KA-2;Lo;0;L;;;;;N;;;;;
+1B019;HENTAIGANA LETTER KA-3;Lo;0;L;;;;;N;;;;;
+1B01A;HENTAIGANA LETTER KA-4;Lo;0;L;;;;;N;;;;;
+1B01B;HENTAIGANA LETTER KA-5;Lo;0;L;;;;;N;;;;;
+1B01C;HENTAIGANA LETTER KA-6;Lo;0;L;;;;;N;;;;;
+1B01D;HENTAIGANA LETTER KA-7;Lo;0;L;;;;;N;;;;;
+1B01E;HENTAIGANA LETTER KA-8;Lo;0;L;;;;;N;;;;;
+1B01F;HENTAIGANA LETTER KA-9;Lo;0;L;;;;;N;;;;;
+1B020;HENTAIGANA LETTER KA-10;Lo;0;L;;;;;N;;;;;
+1B021;HENTAIGANA LETTER KA-11;Lo;0;L;;;;;N;;;;;
+1B022;HENTAIGANA LETTER KA-KE;Lo;0;L;;;;;N;;;;;
+1B023;HENTAIGANA LETTER KI-1;Lo;0;L;;;;;N;;;;;
+1B024;HENTAIGANA LETTER KI-2;Lo;0;L;;;;;N;;;;;
+1B025;HENTAIGANA LETTER KI-3;Lo;0;L;;;;;N;;;;;
+1B026;HENTAIGANA LETTER KI-4;Lo;0;L;;;;;N;;;;;
+1B027;HENTAIGANA LETTER KI-5;Lo;0;L;;;;;N;;;;;
+1B028;HENTAIGANA LETTER KI-6;Lo;0;L;;;;;N;;;;;
+1B029;HENTAIGANA LETTER KI-7;Lo;0;L;;;;;N;;;;;
+1B02A;HENTAIGANA LETTER KI-8;Lo;0;L;;;;;N;;;;;
+1B02B;HENTAIGANA LETTER KU-1;Lo;0;L;;;;;N;;;;;
+1B02C;HENTAIGANA LETTER KU-2;Lo;0;L;;;;;N;;;;;
+1B02D;HENTAIGANA LETTER KU-3;Lo;0;L;;;;;N;;;;;
+1B02E;HENTAIGANA LETTER KU-4;Lo;0;L;;;;;N;;;;;
+1B02F;HENTAIGANA LETTER KU-5;Lo;0;L;;;;;N;;;;;
+1B030;HENTAIGANA LETTER KU-6;Lo;0;L;;;;;N;;;;;
+1B031;HENTAIGANA LETTER KU-7;Lo;0;L;;;;;N;;;;;
+1B032;HENTAIGANA LETTER KE-1;Lo;0;L;;;;;N;;;;;
+1B033;HENTAIGANA LETTER KE-2;Lo;0;L;;;;;N;;;;;
+1B034;HENTAIGANA LETTER KE-3;Lo;0;L;;;;;N;;;;;
+1B035;HENTAIGANA LETTER KE-4;Lo;0;L;;;;;N;;;;;
+1B036;HENTAIGANA LETTER KE-5;Lo;0;L;;;;;N;;;;;
+1B037;HENTAIGANA LETTER KE-6;Lo;0;L;;;;;N;;;;;
+1B038;HENTAIGANA LETTER KO-1;Lo;0;L;;;;;N;;;;;
+1B039;HENTAIGANA LETTER KO-2;Lo;0;L;;;;;N;;;;;
+1B03A;HENTAIGANA LETTER KO-3;Lo;0;L;;;;;N;;;;;
+1B03B;HENTAIGANA LETTER KO-KI;Lo;0;L;;;;;N;;;;;
+1B03C;HENTAIGANA LETTER SA-1;Lo;0;L;;;;;N;;;;;
+1B03D;HENTAIGANA LETTER SA-2;Lo;0;L;;;;;N;;;;;
+1B03E;HENTAIGANA LETTER SA-3;Lo;0;L;;;;;N;;;;;
+1B03F;HENTAIGANA LETTER SA-4;Lo;0;L;;;;;N;;;;;
+1B040;HENTAIGANA LETTER SA-5;Lo;0;L;;;;;N;;;;;
+1B041;HENTAIGANA LETTER SA-6;Lo;0;L;;;;;N;;;;;
+1B042;HENTAIGANA LETTER SA-7;Lo;0;L;;;;;N;;;;;
+1B043;HENTAIGANA LETTER SA-8;Lo;0;L;;;;;N;;;;;
+1B044;HENTAIGANA LETTER SI-1;Lo;0;L;;;;;N;;;;;
+1B045;HENTAIGANA LETTER SI-2;Lo;0;L;;;;;N;;;;;
+1B046;HENTAIGANA LETTER SI-3;Lo;0;L;;;;;N;;;;;
+1B047;HENTAIGANA LETTER SI-4;Lo;0;L;;;;;N;;;;;
+1B048;HENTAIGANA LETTER SI-5;Lo;0;L;;;;;N;;;;;
+1B049;HENTAIGANA LETTER SI-6;Lo;0;L;;;;;N;;;;;
+1B04A;HENTAIGANA LETTER SU-1;Lo;0;L;;;;;N;;;;;
+1B04B;HENTAIGANA LETTER SU-2;Lo;0;L;;;;;N;;;;;
+1B04C;HENTAIGANA LETTER SU-3;Lo;0;L;;;;;N;;;;;
+1B04D;HENTAIGANA LETTER SU-4;Lo;0;L;;;;;N;;;;;
+1B04E;HENTAIGANA LETTER SU-5;Lo;0;L;;;;;N;;;;;
+1B04F;HENTAIGANA LETTER SU-6;Lo;0;L;;;;;N;;;;;
+1B050;HENTAIGANA LETTER SU-7;Lo;0;L;;;;;N;;;;;
+1B051;HENTAIGANA LETTER SU-8;Lo;0;L;;;;;N;;;;;
+1B052;HENTAIGANA LETTER SE-1;Lo;0;L;;;;;N;;;;;
+1B053;HENTAIGANA LETTER SE-2;Lo;0;L;;;;;N;;;;;
+1B054;HENTAIGANA LETTER SE-3;Lo;0;L;;;;;N;;;;;
+1B055;HENTAIGANA LETTER SE-4;Lo;0;L;;;;;N;;;;;
+1B056;HENTAIGANA LETTER SE-5;Lo;0;L;;;;;N;;;;;
+1B057;HENTAIGANA LETTER SO-1;Lo;0;L;;;;;N;;;;;
+1B058;HENTAIGANA LETTER SO-2;Lo;0;L;;;;;N;;;;;
+1B059;HENTAIGANA LETTER SO-3;Lo;0;L;;;;;N;;;;;
+1B05A;HENTAIGANA LETTER SO-4;Lo;0;L;;;;;N;;;;;
+1B05B;HENTAIGANA LETTER SO-5;Lo;0;L;;;;;N;;;;;
+1B05C;HENTAIGANA LETTER SO-6;Lo;0;L;;;;;N;;;;;
+1B05D;HENTAIGANA LETTER SO-7;Lo;0;L;;;;;N;;;;;
+1B05E;HENTAIGANA LETTER TA-1;Lo;0;L;;;;;N;;;;;
+1B05F;HENTAIGANA LETTER TA-2;Lo;0;L;;;;;N;;;;;
+1B060;HENTAIGANA LETTER TA-3;Lo;0;L;;;;;N;;;;;
+1B061;HENTAIGANA LETTER TA-4;Lo;0;L;;;;;N;;;;;
+1B062;HENTAIGANA LETTER TI-1;Lo;0;L;;;;;N;;;;;
+1B063;HENTAIGANA LETTER TI-2;Lo;0;L;;;;;N;;;;;
+1B064;HENTAIGANA LETTER TI-3;Lo;0;L;;;;;N;;;;;
+1B065;HENTAIGANA LETTER TI-4;Lo;0;L;;;;;N;;;;;
+1B066;HENTAIGANA LETTER TI-5;Lo;0;L;;;;;N;;;;;
+1B067;HENTAIGANA LETTER TI-6;Lo;0;L;;;;;N;;;;;
+1B068;HENTAIGANA LETTER TI-7;Lo;0;L;;;;;N;;;;;
+1B069;HENTAIGANA LETTER TU-1;Lo;0;L;;;;;N;;;;;
+1B06A;HENTAIGANA LETTER TU-2;Lo;0;L;;;;;N;;;;;
+1B06B;HENTAIGANA LETTER TU-3;Lo;0;L;;;;;N;;;;;
+1B06C;HENTAIGANA LETTER TU-4;Lo;0;L;;;;;N;;;;;
+1B06D;HENTAIGANA LETTER TU-TO;Lo;0;L;;;;;N;;;;;
+1B06E;HENTAIGANA LETTER TE-1;Lo;0;L;;;;;N;;;;;
+1B06F;HENTAIGANA LETTER TE-2;Lo;0;L;;;;;N;;;;;
+1B070;HENTAIGANA LETTER TE-3;Lo;0;L;;;;;N;;;;;
+1B071;HENTAIGANA LETTER TE-4;Lo;0;L;;;;;N;;;;;
+1B072;HENTAIGANA LETTER TE-5;Lo;0;L;;;;;N;;;;;
+1B073;HENTAIGANA LETTER TE-6;Lo;0;L;;;;;N;;;;;
+1B074;HENTAIGANA LETTER TE-7;Lo;0;L;;;;;N;;;;;
+1B075;HENTAIGANA LETTER TE-8;Lo;0;L;;;;;N;;;;;
+1B076;HENTAIGANA LETTER TE-9;Lo;0;L;;;;;N;;;;;
+1B077;HENTAIGANA LETTER TO-1;Lo;0;L;;;;;N;;;;;
+1B078;HENTAIGANA LETTER TO-2;Lo;0;L;;;;;N;;;;;
+1B079;HENTAIGANA LETTER TO-3;Lo;0;L;;;;;N;;;;;
+1B07A;HENTAIGANA LETTER TO-4;Lo;0;L;;;;;N;;;;;
+1B07B;HENTAIGANA LETTER TO-5;Lo;0;L;;;;;N;;;;;
+1B07C;HENTAIGANA LETTER TO-6;Lo;0;L;;;;;N;;;;;
+1B07D;HENTAIGANA LETTER TO-RA;Lo;0;L;;;;;N;;;;;
+1B07E;HENTAIGANA LETTER NA-1;Lo;0;L;;;;;N;;;;;
+1B07F;HENTAIGANA LETTER NA-2;Lo;0;L;;;;;N;;;;;
+1B080;HENTAIGANA LETTER NA-3;Lo;0;L;;;;;N;;;;;
+1B081;HENTAIGANA LETTER NA-4;Lo;0;L;;;;;N;;;;;
+1B082;HENTAIGANA LETTER NA-5;Lo;0;L;;;;;N;;;;;
+1B083;HENTAIGANA LETTER NA-6;Lo;0;L;;;;;N;;;;;
+1B084;HENTAIGANA LETTER NA-7;Lo;0;L;;;;;N;;;;;
+1B085;HENTAIGANA LETTER NA-8;Lo;0;L;;;;;N;;;;;
+1B086;HENTAIGANA LETTER NA-9;Lo;0;L;;;;;N;;;;;
+1B087;HENTAIGANA LETTER NI-1;Lo;0;L;;;;;N;;;;;
+1B088;HENTAIGANA LETTER NI-2;Lo;0;L;;;;;N;;;;;
+1B089;HENTAIGANA LETTER NI-3;Lo;0;L;;;;;N;;;;;
+1B08A;HENTAIGANA LETTER NI-4;Lo;0;L;;;;;N;;;;;
+1B08B;HENTAIGANA LETTER NI-5;Lo;0;L;;;;;N;;;;;
+1B08C;HENTAIGANA LETTER NI-6;Lo;0;L;;;;;N;;;;;
+1B08D;HENTAIGANA LETTER NI-7;Lo;0;L;;;;;N;;;;;
+1B08E;HENTAIGANA LETTER NI-TE;Lo;0;L;;;;;N;;;;;
+1B08F;HENTAIGANA LETTER NU-1;Lo;0;L;;;;;N;;;;;
+1B090;HENTAIGANA LETTER NU-2;Lo;0;L;;;;;N;;;;;
+1B091;HENTAIGANA LETTER NU-3;Lo;0;L;;;;;N;;;;;
+1B092;HENTAIGANA LETTER NE-1;Lo;0;L;;;;;N;;;;;
+1B093;HENTAIGANA LETTER NE-2;Lo;0;L;;;;;N;;;;;
+1B094;HENTAIGANA LETTER NE-3;Lo;0;L;;;;;N;;;;;
+1B095;HENTAIGANA LETTER NE-4;Lo;0;L;;;;;N;;;;;
+1B096;HENTAIGANA LETTER NE-5;Lo;0;L;;;;;N;;;;;
+1B097;HENTAIGANA LETTER NE-6;Lo;0;L;;;;;N;;;;;
+1B098;HENTAIGANA LETTER NE-KO;Lo;0;L;;;;;N;;;;;
+1B099;HENTAIGANA LETTER NO-1;Lo;0;L;;;;;N;;;;;
+1B09A;HENTAIGANA LETTER NO-2;Lo;0;L;;;;;N;;;;;
+1B09B;HENTAIGANA LETTER NO-3;Lo;0;L;;;;;N;;;;;
+1B09C;HENTAIGANA LETTER NO-4;Lo;0;L;;;;;N;;;;;
+1B09D;HENTAIGANA LETTER NO-5;Lo;0;L;;;;;N;;;;;
+1B09E;HENTAIGANA LETTER HA-1;Lo;0;L;;;;;N;;;;;
+1B09F;HENTAIGANA LETTER HA-2;Lo;0;L;;;;;N;;;;;
+1B0A0;HENTAIGANA LETTER HA-3;Lo;0;L;;;;;N;;;;;
+1B0A1;HENTAIGANA LETTER HA-4;Lo;0;L;;;;;N;;;;;
+1B0A2;HENTAIGANA LETTER HA-5;Lo;0;L;;;;;N;;;;;
+1B0A3;HENTAIGANA LETTER HA-6;Lo;0;L;;;;;N;;;;;
+1B0A4;HENTAIGANA LETTER HA-7;Lo;0;L;;;;;N;;;;;
+1B0A5;HENTAIGANA LETTER HA-8;Lo;0;L;;;;;N;;;;;
+1B0A6;HENTAIGANA LETTER HA-9;Lo;0;L;;;;;N;;;;;
+1B0A7;HENTAIGANA LETTER HA-10;Lo;0;L;;;;;N;;;;;
+1B0A8;HENTAIGANA LETTER HA-11;Lo;0;L;;;;;N;;;;;
+1B0A9;HENTAIGANA LETTER HI-1;Lo;0;L;;;;;N;;;;;
+1B0AA;HENTAIGANA LETTER HI-2;Lo;0;L;;;;;N;;;;;
+1B0AB;HENTAIGANA LETTER HI-3;Lo;0;L;;;;;N;;;;;
+1B0AC;HENTAIGANA LETTER HI-4;Lo;0;L;;;;;N;;;;;
+1B0AD;HENTAIGANA LETTER HI-5;Lo;0;L;;;;;N;;;;;
+1B0AE;HENTAIGANA LETTER HI-6;Lo;0;L;;;;;N;;;;;
+1B0AF;HENTAIGANA LETTER HI-7;Lo;0;L;;;;;N;;;;;
+1B0B0;HENTAIGANA LETTER HU-1;Lo;0;L;;;;;N;;;;;
+1B0B1;HENTAIGANA LETTER HU-2;Lo;0;L;;;;;N;;;;;
+1B0B2;HENTAIGANA LETTER HU-3;Lo;0;L;;;;;N;;;;;
+1B0B3;HENTAIGANA LETTER HE-1;Lo;0;L;;;;;N;;;;;
+1B0B4;HENTAIGANA LETTER HE-2;Lo;0;L;;;;;N;;;;;
+1B0B5;HENTAIGANA LETTER HE-3;Lo;0;L;;;;;N;;;;;
+1B0B6;HENTAIGANA LETTER HE-4;Lo;0;L;;;;;N;;;;;
+1B0B7;HENTAIGANA LETTER HE-5;Lo;0;L;;;;;N;;;;;
+1B0B8;HENTAIGANA LETTER HE-6;Lo;0;L;;;;;N;;;;;
+1B0B9;HENTAIGANA LETTER HE-7;Lo;0;L;;;;;N;;;;;
+1B0BA;HENTAIGANA LETTER HO-1;Lo;0;L;;;;;N;;;;;
+1B0BB;HENTAIGANA LETTER HO-2;Lo;0;L;;;;;N;;;;;
+1B0BC;HENTAIGANA LETTER HO-3;Lo;0;L;;;;;N;;;;;
+1B0BD;HENTAIGANA LETTER HO-4;Lo;0;L;;;;;N;;;;;
+1B0BE;HENTAIGANA LETTER HO-5;Lo;0;L;;;;;N;;;;;
+1B0BF;HENTAIGANA LETTER HO-6;Lo;0;L;;;;;N;;;;;
+1B0C0;HENTAIGANA LETTER HO-7;Lo;0;L;;;;;N;;;;;
+1B0C1;HENTAIGANA LETTER HO-8;Lo;0;L;;;;;N;;;;;
+1B0C2;HENTAIGANA LETTER MA-1;Lo;0;L;;;;;N;;;;;
+1B0C3;HENTAIGANA LETTER MA-2;Lo;0;L;;;;;N;;;;;
+1B0C4;HENTAIGANA LETTER MA-3;Lo;0;L;;;;;N;;;;;
+1B0C5;HENTAIGANA LETTER MA-4;Lo;0;L;;;;;N;;;;;
+1B0C6;HENTAIGANA LETTER MA-5;Lo;0;L;;;;;N;;;;;
+1B0C7;HENTAIGANA LETTER MA-6;Lo;0;L;;;;;N;;;;;
+1B0C8;HENTAIGANA LETTER MA-7;Lo;0;L;;;;;N;;;;;
+1B0C9;HENTAIGANA LETTER MI-1;Lo;0;L;;;;;N;;;;;
+1B0CA;HENTAIGANA LETTER MI-2;Lo;0;L;;;;;N;;;;;
+1B0CB;HENTAIGANA LETTER MI-3;Lo;0;L;;;;;N;;;;;
+1B0CC;HENTAIGANA LETTER MI-4;Lo;0;L;;;;;N;;;;;
+1B0CD;HENTAIGANA LETTER MI-5;Lo;0;L;;;;;N;;;;;
+1B0CE;HENTAIGANA LETTER MI-6;Lo;0;L;;;;;N;;;;;
+1B0CF;HENTAIGANA LETTER MI-7;Lo;0;L;;;;;N;;;;;
+1B0D0;HENTAIGANA LETTER MU-1;Lo;0;L;;;;;N;;;;;
+1B0D1;HENTAIGANA LETTER MU-2;Lo;0;L;;;;;N;;;;;
+1B0D2;HENTAIGANA LETTER MU-3;Lo;0;L;;;;;N;;;;;
+1B0D3;HENTAIGANA LETTER MU-4;Lo;0;L;;;;;N;;;;;
+1B0D4;HENTAIGANA LETTER ME-1;Lo;0;L;;;;;N;;;;;
+1B0D5;HENTAIGANA LETTER ME-2;Lo;0;L;;;;;N;;;;;
+1B0D6;HENTAIGANA LETTER ME-MA;Lo;0;L;;;;;N;;;;;
+1B0D7;HENTAIGANA LETTER MO-1;Lo;0;L;;;;;N;;;;;
+1B0D8;HENTAIGANA LETTER MO-2;Lo;0;L;;;;;N;;;;;
+1B0D9;HENTAIGANA LETTER MO-3;Lo;0;L;;;;;N;;;;;
+1B0DA;HENTAIGANA LETTER MO-4;Lo;0;L;;;;;N;;;;;
+1B0DB;HENTAIGANA LETTER MO-5;Lo;0;L;;;;;N;;;;;
+1B0DC;HENTAIGANA LETTER MO-6;Lo;0;L;;;;;N;;;;;
+1B0DD;HENTAIGANA LETTER YA-1;Lo;0;L;;;;;N;;;;;
+1B0DE;HENTAIGANA LETTER YA-2;Lo;0;L;;;;;N;;;;;
+1B0DF;HENTAIGANA LETTER YA-3;Lo;0;L;;;;;N;;;;;
+1B0E0;HENTAIGANA LETTER YA-4;Lo;0;L;;;;;N;;;;;
+1B0E1;HENTAIGANA LETTER YA-5;Lo;0;L;;;;;N;;;;;
+1B0E2;HENTAIGANA LETTER YA-YO;Lo;0;L;;;;;N;;;;;
+1B0E3;HENTAIGANA LETTER YU-1;Lo;0;L;;;;;N;;;;;
+1B0E4;HENTAIGANA LETTER YU-2;Lo;0;L;;;;;N;;;;;
+1B0E5;HENTAIGANA LETTER YU-3;Lo;0;L;;;;;N;;;;;
+1B0E6;HENTAIGANA LETTER YU-4;Lo;0;L;;;;;N;;;;;
+1B0E7;HENTAIGANA LETTER YO-1;Lo;0;L;;;;;N;;;;;
+1B0E8;HENTAIGANA LETTER YO-2;Lo;0;L;;;;;N;;;;;
+1B0E9;HENTAIGANA LETTER YO-3;Lo;0;L;;;;;N;;;;;
+1B0EA;HENTAIGANA LETTER YO-4;Lo;0;L;;;;;N;;;;;
+1B0EB;HENTAIGANA LETTER YO-5;Lo;0;L;;;;;N;;;;;
+1B0EC;HENTAIGANA LETTER YO-6;Lo;0;L;;;;;N;;;;;
+1B0ED;HENTAIGANA LETTER RA-1;Lo;0;L;;;;;N;;;;;
+1B0EE;HENTAIGANA LETTER RA-2;Lo;0;L;;;;;N;;;;;
+1B0EF;HENTAIGANA LETTER RA-3;Lo;0;L;;;;;N;;;;;
+1B0F0;HENTAIGANA LETTER RA-4;Lo;0;L;;;;;N;;;;;
+1B0F1;HENTAIGANA LETTER RI-1;Lo;0;L;;;;;N;;;;;
+1B0F2;HENTAIGANA LETTER RI-2;Lo;0;L;;;;;N;;;;;
+1B0F3;HENTAIGANA LETTER RI-3;Lo;0;L;;;;;N;;;;;
+1B0F4;HENTAIGANA LETTER RI-4;Lo;0;L;;;;;N;;;;;
+1B0F5;HENTAIGANA LETTER RI-5;Lo;0;L;;;;;N;;;;;
+1B0F6;HENTAIGANA LETTER RI-6;Lo;0;L;;;;;N;;;;;
+1B0F7;HENTAIGANA LETTER RI-7;Lo;0;L;;;;;N;;;;;
+1B0F8;HENTAIGANA LETTER RU-1;Lo;0;L;;;;;N;;;;;
+1B0F9;HENTAIGANA LETTER RU-2;Lo;0;L;;;;;N;;;;;
+1B0FA;HENTAIGANA LETTER RU-3;Lo;0;L;;;;;N;;;;;
+1B0FB;HENTAIGANA LETTER RU-4;Lo;0;L;;;;;N;;;;;
+1B0FC;HENTAIGANA LETTER RU-5;Lo;0;L;;;;;N;;;;;
+1B0FD;HENTAIGANA LETTER RU-6;Lo;0;L;;;;;N;;;;;
+1B0FE;HENTAIGANA LETTER RE-1;Lo;0;L;;;;;N;;;;;
+1B0FF;HENTAIGANA LETTER RE-2;Lo;0;L;;;;;N;;;;;
+1B100;HENTAIGANA LETTER RE-3;Lo;0;L;;;;;N;;;;;
+1B101;HENTAIGANA LETTER RE-4;Lo;0;L;;;;;N;;;;;
+1B102;HENTAIGANA LETTER RO-1;Lo;0;L;;;;;N;;;;;
+1B103;HENTAIGANA LETTER RO-2;Lo;0;L;;;;;N;;;;;
+1B104;HENTAIGANA LETTER RO-3;Lo;0;L;;;;;N;;;;;
+1B105;HENTAIGANA LETTER RO-4;Lo;0;L;;;;;N;;;;;
+1B106;HENTAIGANA LETTER RO-5;Lo;0;L;;;;;N;;;;;
+1B107;HENTAIGANA LETTER RO-6;Lo;0;L;;;;;N;;;;;
+1B108;HENTAIGANA LETTER WA-1;Lo;0;L;;;;;N;;;;;
+1B109;HENTAIGANA LETTER WA-2;Lo;0;L;;;;;N;;;;;
+1B10A;HENTAIGANA LETTER WA-3;Lo;0;L;;;;;N;;;;;
+1B10B;HENTAIGANA LETTER WA-4;Lo;0;L;;;;;N;;;;;
+1B10C;HENTAIGANA LETTER WA-5;Lo;0;L;;;;;N;;;;;
+1B10D;HENTAIGANA LETTER WI-1;Lo;0;L;;;;;N;;;;;
+1B10E;HENTAIGANA LETTER WI-2;Lo;0;L;;;;;N;;;;;
+1B10F;HENTAIGANA LETTER WI-3;Lo;0;L;;;;;N;;;;;
+1B110;HENTAIGANA LETTER WI-4;Lo;0;L;;;;;N;;;;;
+1B111;HENTAIGANA LETTER WI-5;Lo;0;L;;;;;N;;;;;
+1B112;HENTAIGANA LETTER WE-1;Lo;0;L;;;;;N;;;;;
+1B113;HENTAIGANA LETTER WE-2;Lo;0;L;;;;;N;;;;;
+1B114;HENTAIGANA LETTER WE-3;Lo;0;L;;;;;N;;;;;
+1B115;HENTAIGANA LETTER WE-4;Lo;0;L;;;;;N;;;;;
+1B116;HENTAIGANA LETTER WO-1;Lo;0;L;;;;;N;;;;;
+1B117;HENTAIGANA LETTER WO-2;Lo;0;L;;;;;N;;;;;
+1B118;HENTAIGANA LETTER WO-3;Lo;0;L;;;;;N;;;;;
+1B119;HENTAIGANA LETTER WO-4;Lo;0;L;;;;;N;;;;;
+1B11A;HENTAIGANA LETTER WO-5;Lo;0;L;;;;;N;;;;;
+1B11B;HENTAIGANA LETTER WO-6;Lo;0;L;;;;;N;;;;;
+1B11C;HENTAIGANA LETTER WO-7;Lo;0;L;;;;;N;;;;;
+1B11D;HENTAIGANA LETTER N-MU-MO-1;Lo;0;L;;;;;N;;;;;
+1B11E;HENTAIGANA LETTER N-MU-MO-2;Lo;0;L;;;;;N;;;;;
+1B150;HIRAGANA LETTER SMALL WI;Lo;0;L;;;;;N;;;;;
+1B151;HIRAGANA LETTER SMALL WE;Lo;0;L;;;;;N;;;;;
+1B152;HIRAGANA LETTER SMALL WO;Lo;0;L;;;;;N;;;;;
+1B164;KATAKANA LETTER SMALL WI;Lo;0;L;;;;;N;;;;;
+1B165;KATAKANA LETTER SMALL WE;Lo;0;L;;;;;N;;;;;
+1B166;KATAKANA LETTER SMALL WO;Lo;0;L;;;;;N;;;;;
+1B167;KATAKANA LETTER SMALL N;Lo;0;L;;;;;N;;;;;
+1B170;NUSHU CHARACTER-1B170;Lo;0;L;;;;;N;;;;;
+1B171;NUSHU CHARACTER-1B171;Lo;0;L;;;;;N;;;;;
+1B172;NUSHU CHARACTER-1B172;Lo;0;L;;;;;N;;;;;
+1B173;NUSHU CHARACTER-1B173;Lo;0;L;;;;;N;;;;;
+1B174;NUSHU CHARACTER-1B174;Lo;0;L;;;;;N;;;;;
+1B175;NUSHU CHARACTER-1B175;Lo;0;L;;;;;N;;;;;
+1B176;NUSHU CHARACTER-1B176;Lo;0;L;;;;;N;;;;;
+1B177;NUSHU CHARACTER-1B177;Lo;0;L;;;;;N;;;;;
+1B178;NUSHU CHARACTER-1B178;Lo;0;L;;;;;N;;;;;
+1B179;NUSHU CHARACTER-1B179;Lo;0;L;;;;;N;;;;;
+1B17A;NUSHU CHARACTER-1B17A;Lo;0;L;;;;;N;;;;;
+1B17B;NUSHU CHARACTER-1B17B;Lo;0;L;;;;;N;;;;;
+1B17C;NUSHU CHARACTER-1B17C;Lo;0;L;;;;;N;;;;;
+1B17D;NUSHU CHARACTER-1B17D;Lo;0;L;;;;;N;;;;;
+1B17E;NUSHU CHARACTER-1B17E;Lo;0;L;;;;;N;;;;;
+1B17F;NUSHU CHARACTER-1B17F;Lo;0;L;;;;;N;;;;;
+1B180;NUSHU CHARACTER-1B180;Lo;0;L;;;;;N;;;;;
+1B181;NUSHU CHARACTER-1B181;Lo;0;L;;;;;N;;;;;
+1B182;NUSHU CHARACTER-1B182;Lo;0;L;;;;;N;;;;;
+1B183;NUSHU CHARACTER-1B183;Lo;0;L;;;;;N;;;;;
+1B184;NUSHU CHARACTER-1B184;Lo;0;L;;;;;N;;;;;
+1B185;NUSHU CHARACTER-1B185;Lo;0;L;;;;;N;;;;;
+1B186;NUSHU CHARACTER-1B186;Lo;0;L;;;;;N;;;;;
+1B187;NUSHU CHARACTER-1B187;Lo;0;L;;;;;N;;;;;
+1B188;NUSHU CHARACTER-1B188;Lo;0;L;;;;;N;;;;;
+1B189;NUSHU CHARACTER-1B189;Lo;0;L;;;;;N;;;;;
+1B18A;NUSHU CHARACTER-1B18A;Lo;0;L;;;;;N;;;;;
+1B18B;NUSHU CHARACTER-1B18B;Lo;0;L;;;;;N;;;;;
+1B18C;NUSHU CHARACTER-1B18C;Lo;0;L;;;;;N;;;;;
+1B18D;NUSHU CHARACTER-1B18D;Lo;0;L;;;;;N;;;;;
+1B18E;NUSHU CHARACTER-1B18E;Lo;0;L;;;;;N;;;;;
+1B18F;NUSHU CHARACTER-1B18F;Lo;0;L;;;;;N;;;;;
+1B190;NUSHU CHARACTER-1B190;Lo;0;L;;;;;N;;;;;
+1B191;NUSHU CHARACTER-1B191;Lo;0;L;;;;;N;;;;;
+1B192;NUSHU CHARACTER-1B192;Lo;0;L;;;;;N;;;;;
+1B193;NUSHU CHARACTER-1B193;Lo;0;L;;;;;N;;;;;
+1B194;NUSHU CHARACTER-1B194;Lo;0;L;;;;;N;;;;;
+1B195;NUSHU CHARACTER-1B195;Lo;0;L;;;;;N;;;;;
+1B196;NUSHU CHARACTER-1B196;Lo;0;L;;;;;N;;;;;
+1B197;NUSHU CHARACTER-1B197;Lo;0;L;;;;;N;;;;;
+1B198;NUSHU CHARACTER-1B198;Lo;0;L;;;;;N;;;;;
+1B199;NUSHU CHARACTER-1B199;Lo;0;L;;;;;N;;;;;
+1B19A;NUSHU CHARACTER-1B19A;Lo;0;L;;;;;N;;;;;
+1B19B;NUSHU CHARACTER-1B19B;Lo;0;L;;;;;N;;;;;
+1B19C;NUSHU CHARACTER-1B19C;Lo;0;L;;;;;N;;;;;
+1B19D;NUSHU CHARACTER-1B19D;Lo;0;L;;;;;N;;;;;
+1B19E;NUSHU CHARACTER-1B19E;Lo;0;L;;;;;N;;;;;
+1B19F;NUSHU CHARACTER-1B19F;Lo;0;L;;;;;N;;;;;
+1B1A0;NUSHU CHARACTER-1B1A0;Lo;0;L;;;;;N;;;;;
+1B1A1;NUSHU CHARACTER-1B1A1;Lo;0;L;;;;;N;;;;;
+1B1A2;NUSHU CHARACTER-1B1A2;Lo;0;L;;;;;N;;;;;
+1B1A3;NUSHU CHARACTER-1B1A3;Lo;0;L;;;;;N;;;;;
+1B1A4;NUSHU CHARACTER-1B1A4;Lo;0;L;;;;;N;;;;;
+1B1A5;NUSHU CHARACTER-1B1A5;Lo;0;L;;;;;N;;;;;
+1B1A6;NUSHU CHARACTER-1B1A6;Lo;0;L;;;;;N;;;;;
+1B1A7;NUSHU CHARACTER-1B1A7;Lo;0;L;;;;;N;;;;;
+1B1A8;NUSHU CHARACTER-1B1A8;Lo;0;L;;;;;N;;;;;
+1B1A9;NUSHU CHARACTER-1B1A9;Lo;0;L;;;;;N;;;;;
+1B1AA;NUSHU CHARACTER-1B1AA;Lo;0;L;;;;;N;;;;;
+1B1AB;NUSHU CHARACTER-1B1AB;Lo;0;L;;;;;N;;;;;
+1B1AC;NUSHU CHARACTER-1B1AC;Lo;0;L;;;;;N;;;;;
+1B1AD;NUSHU CHARACTER-1B1AD;Lo;0;L;;;;;N;;;;;
+1B1AE;NUSHU CHARACTER-1B1AE;Lo;0;L;;;;;N;;;;;
+1B1AF;NUSHU CHARACTER-1B1AF;Lo;0;L;;;;;N;;;;;
+1B1B0;NUSHU CHARACTER-1B1B0;Lo;0;L;;;;;N;;;;;
+1B1B1;NUSHU CHARACTER-1B1B1;Lo;0;L;;;;;N;;;;;
+1B1B2;NUSHU CHARACTER-1B1B2;Lo;0;L;;;;;N;;;;;
+1B1B3;NUSHU CHARACTER-1B1B3;Lo;0;L;;;;;N;;;;;
+1B1B4;NUSHU CHARACTER-1B1B4;Lo;0;L;;;;;N;;;;;
+1B1B5;NUSHU CHARACTER-1B1B5;Lo;0;L;;;;;N;;;;;
+1B1B6;NUSHU CHARACTER-1B1B6;Lo;0;L;;;;;N;;;;;
+1B1B7;NUSHU CHARACTER-1B1B7;Lo;0;L;;;;;N;;;;;
+1B1B8;NUSHU CHARACTER-1B1B8;Lo;0;L;;;;;N;;;;;
+1B1B9;NUSHU CHARACTER-1B1B9;Lo;0;L;;;;;N;;;;;
+1B1BA;NUSHU CHARACTER-1B1BA;Lo;0;L;;;;;N;;;;;
+1B1BB;NUSHU CHARACTER-1B1BB;Lo;0;L;;;;;N;;;;;
+1B1BC;NUSHU CHARACTER-1B1BC;Lo;0;L;;;;;N;;;;;
+1B1BD;NUSHU CHARACTER-1B1BD;Lo;0;L;;;;;N;;;;;
+1B1BE;NUSHU CHARACTER-1B1BE;Lo;0;L;;;;;N;;;;;
+1B1BF;NUSHU CHARACTER-1B1BF;Lo;0;L;;;;;N;;;;;
+1B1C0;NUSHU CHARACTER-1B1C0;Lo;0;L;;;;;N;;;;;
+1B1C1;NUSHU CHARACTER-1B1C1;Lo;0;L;;;;;N;;;;;
+1B1C2;NUSHU CHARACTER-1B1C2;Lo;0;L;;;;;N;;;;;
+1B1C3;NUSHU CHARACTER-1B1C3;Lo;0;L;;;;;N;;;;;
+1B1C4;NUSHU CHARACTER-1B1C4;Lo;0;L;;;;;N;;;;;
+1B1C5;NUSHU CHARACTER-1B1C5;Lo;0;L;;;;;N;;;;;
+1B1C6;NUSHU CHARACTER-1B1C6;Lo;0;L;;;;;N;;;;;
+1B1C7;NUSHU CHARACTER-1B1C7;Lo;0;L;;;;;N;;;;;
+1B1C8;NUSHU CHARACTER-1B1C8;Lo;0;L;;;;;N;;;;;
+1B1C9;NUSHU CHARACTER-1B1C9;Lo;0;L;;;;;N;;;;;
+1B1CA;NUSHU CHARACTER-1B1CA;Lo;0;L;;;;;N;;;;;
+1B1CB;NUSHU CHARACTER-1B1CB;Lo;0;L;;;;;N;;;;;
+1B1CC;NUSHU CHARACTER-1B1CC;Lo;0;L;;;;;N;;;;;
+1B1CD;NUSHU CHARACTER-1B1CD;Lo;0;L;;;;;N;;;;;
+1B1CE;NUSHU CHARACTER-1B1CE;Lo;0;L;;;;;N;;;;;
+1B1CF;NUSHU CHARACTER-1B1CF;Lo;0;L;;;;;N;;;;;
+1B1D0;NUSHU CHARACTER-1B1D0;Lo;0;L;;;;;N;;;;;
+1B1D1;NUSHU CHARACTER-1B1D1;Lo;0;L;;;;;N;;;;;
+1B1D2;NUSHU CHARACTER-1B1D2;Lo;0;L;;;;;N;;;;;
+1B1D3;NUSHU CHARACTER-1B1D3;Lo;0;L;;;;;N;;;;;
+1B1D4;NUSHU CHARACTER-1B1D4;Lo;0;L;;;;;N;;;;;
+1B1D5;NUSHU CHARACTER-1B1D5;Lo;0;L;;;;;N;;;;;
+1B1D6;NUSHU CHARACTER-1B1D6;Lo;0;L;;;;;N;;;;;
+1B1D7;NUSHU CHARACTER-1B1D7;Lo;0;L;;;;;N;;;;;
+1B1D8;NUSHU CHARACTER-1B1D8;Lo;0;L;;;;;N;;;;;
+1B1D9;NUSHU CHARACTER-1B1D9;Lo;0;L;;;;;N;;;;;
+1B1DA;NUSHU CHARACTER-1B1DA;Lo;0;L;;;;;N;;;;;
+1B1DB;NUSHU CHARACTER-1B1DB;Lo;0;L;;;;;N;;;;;
+1B1DC;NUSHU CHARACTER-1B1DC;Lo;0;L;;;;;N;;;;;
+1B1DD;NUSHU CHARACTER-1B1DD;Lo;0;L;;;;;N;;;;;
+1B1DE;NUSHU CHARACTER-1B1DE;Lo;0;L;;;;;N;;;;;
+1B1DF;NUSHU CHARACTER-1B1DF;Lo;0;L;;;;;N;;;;;
+1B1E0;NUSHU CHARACTER-1B1E0;Lo;0;L;;;;;N;;;;;
+1B1E1;NUSHU CHARACTER-1B1E1;Lo;0;L;;;;;N;;;;;
+1B1E2;NUSHU CHARACTER-1B1E2;Lo;0;L;;;;;N;;;;;
+1B1E3;NUSHU CHARACTER-1B1E3;Lo;0;L;;;;;N;;;;;
+1B1E4;NUSHU CHARACTER-1B1E4;Lo;0;L;;;;;N;;;;;
+1B1E5;NUSHU CHARACTER-1B1E5;Lo;0;L;;;;;N;;;;;
+1B1E6;NUSHU CHARACTER-1B1E6;Lo;0;L;;;;;N;;;;;
+1B1E7;NUSHU CHARACTER-1B1E7;Lo;0;L;;;;;N;;;;;
+1B1E8;NUSHU CHARACTER-1B1E8;Lo;0;L;;;;;N;;;;;
+1B1E9;NUSHU CHARACTER-1B1E9;Lo;0;L;;;;;N;;;;;
+1B1EA;NUSHU CHARACTER-1B1EA;Lo;0;L;;;;;N;;;;;
+1B1EB;NUSHU CHARACTER-1B1EB;Lo;0;L;;;;;N;;;;;
+1B1EC;NUSHU CHARACTER-1B1EC;Lo;0;L;;;;;N;;;;;
+1B1ED;NUSHU CHARACTER-1B1ED;Lo;0;L;;;;;N;;;;;
+1B1EE;NUSHU CHARACTER-1B1EE;Lo;0;L;;;;;N;;;;;
+1B1EF;NUSHU CHARACTER-1B1EF;Lo;0;L;;;;;N;;;;;
+1B1F0;NUSHU CHARACTER-1B1F0;Lo;0;L;;;;;N;;;;;
+1B1F1;NUSHU CHARACTER-1B1F1;Lo;0;L;;;;;N;;;;;
+1B1F2;NUSHU CHARACTER-1B1F2;Lo;0;L;;;;;N;;;;;
+1B1F3;NUSHU CHARACTER-1B1F3;Lo;0;L;;;;;N;;;;;
+1B1F4;NUSHU CHARACTER-1B1F4;Lo;0;L;;;;;N;;;;;
+1B1F5;NUSHU CHARACTER-1B1F5;Lo;0;L;;;;;N;;;;;
+1B1F6;NUSHU CHARACTER-1B1F6;Lo;0;L;;;;;N;;;;;
+1B1F7;NUSHU CHARACTER-1B1F7;Lo;0;L;;;;;N;;;;;
+1B1F8;NUSHU CHARACTER-1B1F8;Lo;0;L;;;;;N;;;;;
+1B1F9;NUSHU CHARACTER-1B1F9;Lo;0;L;;;;;N;;;;;
+1B1FA;NUSHU CHARACTER-1B1FA;Lo;0;L;;;;;N;;;;;
+1B1FB;NUSHU CHARACTER-1B1FB;Lo;0;L;;;;;N;;;;;
+1B1FC;NUSHU CHARACTER-1B1FC;Lo;0;L;;;;;N;;;;;
+1B1FD;NUSHU CHARACTER-1B1FD;Lo;0;L;;;;;N;;;;;
+1B1FE;NUSHU CHARACTER-1B1FE;Lo;0;L;;;;;N;;;;;
+1B1FF;NUSHU CHARACTER-1B1FF;Lo;0;L;;;;;N;;;;;
+1B200;NUSHU CHARACTER-1B200;Lo;0;L;;;;;N;;;;;
+1B201;NUSHU CHARACTER-1B201;Lo;0;L;;;;;N;;;;;
+1B202;NUSHU CHARACTER-1B202;Lo;0;L;;;;;N;;;;;
+1B203;NUSHU CHARACTER-1B203;Lo;0;L;;;;;N;;;;;
+1B204;NUSHU CHARACTER-1B204;Lo;0;L;;;;;N;;;;;
+1B205;NUSHU CHARACTER-1B205;Lo;0;L;;;;;N;;;;;
+1B206;NUSHU CHARACTER-1B206;Lo;0;L;;;;;N;;;;;
+1B207;NUSHU CHARACTER-1B207;Lo;0;L;;;;;N;;;;;
+1B208;NUSHU CHARACTER-1B208;Lo;0;L;;;;;N;;;;;
+1B209;NUSHU CHARACTER-1B209;Lo;0;L;;;;;N;;;;;
+1B20A;NUSHU CHARACTER-1B20A;Lo;0;L;;;;;N;;;;;
+1B20B;NUSHU CHARACTER-1B20B;Lo;0;L;;;;;N;;;;;
+1B20C;NUSHU CHARACTER-1B20C;Lo;0;L;;;;;N;;;;;
+1B20D;NUSHU CHARACTER-1B20D;Lo;0;L;;;;;N;;;;;
+1B20E;NUSHU CHARACTER-1B20E;Lo;0;L;;;;;N;;;;;
+1B20F;NUSHU CHARACTER-1B20F;Lo;0;L;;;;;N;;;;;
+1B210;NUSHU CHARACTER-1B210;Lo;0;L;;;;;N;;;;;
+1B211;NUSHU CHARACTER-1B211;Lo;0;L;;;;;N;;;;;
+1B212;NUSHU CHARACTER-1B212;Lo;0;L;;;;;N;;;;;
+1B213;NUSHU CHARACTER-1B213;Lo;0;L;;;;;N;;;;;
+1B214;NUSHU CHARACTER-1B214;Lo;0;L;;;;;N;;;;;
+1B215;NUSHU CHARACTER-1B215;Lo;0;L;;;;;N;;;;;
+1B216;NUSHU CHARACTER-1B216;Lo;0;L;;;;;N;;;;;
+1B217;NUSHU CHARACTER-1B217;Lo;0;L;;;;;N;;;;;
+1B218;NUSHU CHARACTER-1B218;Lo;0;L;;;;;N;;;;;
+1B219;NUSHU CHARACTER-1B219;Lo;0;L;;;;;N;;;;;
+1B21A;NUSHU CHARACTER-1B21A;Lo;0;L;;;;;N;;;;;
+1B21B;NUSHU CHARACTER-1B21B;Lo;0;L;;;;;N;;;;;
+1B21C;NUSHU CHARACTER-1B21C;Lo;0;L;;;;;N;;;;;
+1B21D;NUSHU CHARACTER-1B21D;Lo;0;L;;;;;N;;;;;
+1B21E;NUSHU CHARACTER-1B21E;Lo;0;L;;;;;N;;;;;
+1B21F;NUSHU CHARACTER-1B21F;Lo;0;L;;;;;N;;;;;
+1B220;NUSHU CHARACTER-1B220;Lo;0;L;;;;;N;;;;;
+1B221;NUSHU CHARACTER-1B221;Lo;0;L;;;;;N;;;;;
+1B222;NUSHU CHARACTER-1B222;Lo;0;L;;;;;N;;;;;
+1B223;NUSHU CHARACTER-1B223;Lo;0;L;;;;;N;;;;;
+1B224;NUSHU CHARACTER-1B224;Lo;0;L;;;;;N;;;;;
+1B225;NUSHU CHARACTER-1B225;Lo;0;L;;;;;N;;;;;
+1B226;NUSHU CHARACTER-1B226;Lo;0;L;;;;;N;;;;;
+1B227;NUSHU CHARACTER-1B227;Lo;0;L;;;;;N;;;;;
+1B228;NUSHU CHARACTER-1B228;Lo;0;L;;;;;N;;;;;
+1B229;NUSHU CHARACTER-1B229;Lo;0;L;;;;;N;;;;;
+1B22A;NUSHU CHARACTER-1B22A;Lo;0;L;;;;;N;;;;;
+1B22B;NUSHU CHARACTER-1B22B;Lo;0;L;;;;;N;;;;;
+1B22C;NUSHU CHARACTER-1B22C;Lo;0;L;;;;;N;;;;;
+1B22D;NUSHU CHARACTER-1B22D;Lo;0;L;;;;;N;;;;;
+1B22E;NUSHU CHARACTER-1B22E;Lo;0;L;;;;;N;;;;;
+1B22F;NUSHU CHARACTER-1B22F;Lo;0;L;;;;;N;;;;;
+1B230;NUSHU CHARACTER-1B230;Lo;0;L;;;;;N;;;;;
+1B231;NUSHU CHARACTER-1B231;Lo;0;L;;;;;N;;;;;
+1B232;NUSHU CHARACTER-1B232;Lo;0;L;;;;;N;;;;;
+1B233;NUSHU CHARACTER-1B233;Lo;0;L;;;;;N;;;;;
+1B234;NUSHU CHARACTER-1B234;Lo;0;L;;;;;N;;;;;
+1B235;NUSHU CHARACTER-1B235;Lo;0;L;;;;;N;;;;;
+1B236;NUSHU CHARACTER-1B236;Lo;0;L;;;;;N;;;;;
+1B237;NUSHU CHARACTER-1B237;Lo;0;L;;;;;N;;;;;
+1B238;NUSHU CHARACTER-1B238;Lo;0;L;;;;;N;;;;;
+1B239;NUSHU CHARACTER-1B239;Lo;0;L;;;;;N;;;;;
+1B23A;NUSHU CHARACTER-1B23A;Lo;0;L;;;;;N;;;;;
+1B23B;NUSHU CHARACTER-1B23B;Lo;0;L;;;;;N;;;;;
+1B23C;NUSHU CHARACTER-1B23C;Lo;0;L;;;;;N;;;;;
+1B23D;NUSHU CHARACTER-1B23D;Lo;0;L;;;;;N;;;;;
+1B23E;NUSHU CHARACTER-1B23E;Lo;0;L;;;;;N;;;;;
+1B23F;NUSHU CHARACTER-1B23F;Lo;0;L;;;;;N;;;;;
+1B240;NUSHU CHARACTER-1B240;Lo;0;L;;;;;N;;;;;
+1B241;NUSHU CHARACTER-1B241;Lo;0;L;;;;;N;;;;;
+1B242;NUSHU CHARACTER-1B242;Lo;0;L;;;;;N;;;;;
+1B243;NUSHU CHARACTER-1B243;Lo;0;L;;;;;N;;;;;
+1B244;NUSHU CHARACTER-1B244;Lo;0;L;;;;;N;;;;;
+1B245;NUSHU CHARACTER-1B245;Lo;0;L;;;;;N;;;;;
+1B246;NUSHU CHARACTER-1B246;Lo;0;L;;;;;N;;;;;
+1B247;NUSHU CHARACTER-1B247;Lo;0;L;;;;;N;;;;;
+1B248;NUSHU CHARACTER-1B248;Lo;0;L;;;;;N;;;;;
+1B249;NUSHU CHARACTER-1B249;Lo;0;L;;;;;N;;;;;
+1B24A;NUSHU CHARACTER-1B24A;Lo;0;L;;;;;N;;;;;
+1B24B;NUSHU CHARACTER-1B24B;Lo;0;L;;;;;N;;;;;
+1B24C;NUSHU CHARACTER-1B24C;Lo;0;L;;;;;N;;;;;
+1B24D;NUSHU CHARACTER-1B24D;Lo;0;L;;;;;N;;;;;
+1B24E;NUSHU CHARACTER-1B24E;Lo;0;L;;;;;N;;;;;
+1B24F;NUSHU CHARACTER-1B24F;Lo;0;L;;;;;N;;;;;
+1B250;NUSHU CHARACTER-1B250;Lo;0;L;;;;;N;;;;;
+1B251;NUSHU CHARACTER-1B251;Lo;0;L;;;;;N;;;;;
+1B252;NUSHU CHARACTER-1B252;Lo;0;L;;;;;N;;;;;
+1B253;NUSHU CHARACTER-1B253;Lo;0;L;;;;;N;;;;;
+1B254;NUSHU CHARACTER-1B254;Lo;0;L;;;;;N;;;;;
+1B255;NUSHU CHARACTER-1B255;Lo;0;L;;;;;N;;;;;
+1B256;NUSHU CHARACTER-1B256;Lo;0;L;;;;;N;;;;;
+1B257;NUSHU CHARACTER-1B257;Lo;0;L;;;;;N;;;;;
+1B258;NUSHU CHARACTER-1B258;Lo;0;L;;;;;N;;;;;
+1B259;NUSHU CHARACTER-1B259;Lo;0;L;;;;;N;;;;;
+1B25A;NUSHU CHARACTER-1B25A;Lo;0;L;;;;;N;;;;;
+1B25B;NUSHU CHARACTER-1B25B;Lo;0;L;;;;;N;;;;;
+1B25C;NUSHU CHARACTER-1B25C;Lo;0;L;;;;;N;;;;;
+1B25D;NUSHU CHARACTER-1B25D;Lo;0;L;;;;;N;;;;;
+1B25E;NUSHU CHARACTER-1B25E;Lo;0;L;;;;;N;;;;;
+1B25F;NUSHU CHARACTER-1B25F;Lo;0;L;;;;;N;;;;;
+1B260;NUSHU CHARACTER-1B260;Lo;0;L;;;;;N;;;;;
+1B261;NUSHU CHARACTER-1B261;Lo;0;L;;;;;N;;;;;
+1B262;NUSHU CHARACTER-1B262;Lo;0;L;;;;;N;;;;;
+1B263;NUSHU CHARACTER-1B263;Lo;0;L;;;;;N;;;;;
+1B264;NUSHU CHARACTER-1B264;Lo;0;L;;;;;N;;;;;
+1B265;NUSHU CHARACTER-1B265;Lo;0;L;;;;;N;;;;;
+1B266;NUSHU CHARACTER-1B266;Lo;0;L;;;;;N;;;;;
+1B267;NUSHU CHARACTER-1B267;Lo;0;L;;;;;N;;;;;
+1B268;NUSHU CHARACTER-1B268;Lo;0;L;;;;;N;;;;;
+1B269;NUSHU CHARACTER-1B269;Lo;0;L;;;;;N;;;;;
+1B26A;NUSHU CHARACTER-1B26A;Lo;0;L;;;;;N;;;;;
+1B26B;NUSHU CHARACTER-1B26B;Lo;0;L;;;;;N;;;;;
+1B26C;NUSHU CHARACTER-1B26C;Lo;0;L;;;;;N;;;;;
+1B26D;NUSHU CHARACTER-1B26D;Lo;0;L;;;;;N;;;;;
+1B26E;NUSHU CHARACTER-1B26E;Lo;0;L;;;;;N;;;;;
+1B26F;NUSHU CHARACTER-1B26F;Lo;0;L;;;;;N;;;;;
+1B270;NUSHU CHARACTER-1B270;Lo;0;L;;;;;N;;;;;
+1B271;NUSHU CHARACTER-1B271;Lo;0;L;;;;;N;;;;;
+1B272;NUSHU CHARACTER-1B272;Lo;0;L;;;;;N;;;;;
+1B273;NUSHU CHARACTER-1B273;Lo;0;L;;;;;N;;;;;
+1B274;NUSHU CHARACTER-1B274;Lo;0;L;;;;;N;;;;;
+1B275;NUSHU CHARACTER-1B275;Lo;0;L;;;;;N;;;;;
+1B276;NUSHU CHARACTER-1B276;Lo;0;L;;;;;N;;;;;
+1B277;NUSHU CHARACTER-1B277;Lo;0;L;;;;;N;;;;;
+1B278;NUSHU CHARACTER-1B278;Lo;0;L;;;;;N;;;;;
+1B279;NUSHU CHARACTER-1B279;Lo;0;L;;;;;N;;;;;
+1B27A;NUSHU CHARACTER-1B27A;Lo;0;L;;;;;N;;;;;
+1B27B;NUSHU CHARACTER-1B27B;Lo;0;L;;;;;N;;;;;
+1B27C;NUSHU CHARACTER-1B27C;Lo;0;L;;;;;N;;;;;
+1B27D;NUSHU CHARACTER-1B27D;Lo;0;L;;;;;N;;;;;
+1B27E;NUSHU CHARACTER-1B27E;Lo;0;L;;;;;N;;;;;
+1B27F;NUSHU CHARACTER-1B27F;Lo;0;L;;;;;N;;;;;
+1B280;NUSHU CHARACTER-1B280;Lo;0;L;;;;;N;;;;;
+1B281;NUSHU CHARACTER-1B281;Lo;0;L;;;;;N;;;;;
+1B282;NUSHU CHARACTER-1B282;Lo;0;L;;;;;N;;;;;
+1B283;NUSHU CHARACTER-1B283;Lo;0;L;;;;;N;;;;;
+1B284;NUSHU CHARACTER-1B284;Lo;0;L;;;;;N;;;;;
+1B285;NUSHU CHARACTER-1B285;Lo;0;L;;;;;N;;;;;
+1B286;NUSHU CHARACTER-1B286;Lo;0;L;;;;;N;;;;;
+1B287;NUSHU CHARACTER-1B287;Lo;0;L;;;;;N;;;;;
+1B288;NUSHU CHARACTER-1B288;Lo;0;L;;;;;N;;;;;
+1B289;NUSHU CHARACTER-1B289;Lo;0;L;;;;;N;;;;;
+1B28A;NUSHU CHARACTER-1B28A;Lo;0;L;;;;;N;;;;;
+1B28B;NUSHU CHARACTER-1B28B;Lo;0;L;;;;;N;;;;;
+1B28C;NUSHU CHARACTER-1B28C;Lo;0;L;;;;;N;;;;;
+1B28D;NUSHU CHARACTER-1B28D;Lo;0;L;;;;;N;;;;;
+1B28E;NUSHU CHARACTER-1B28E;Lo;0;L;;;;;N;;;;;
+1B28F;NUSHU CHARACTER-1B28F;Lo;0;L;;;;;N;;;;;
+1B290;NUSHU CHARACTER-1B290;Lo;0;L;;;;;N;;;;;
+1B291;NUSHU CHARACTER-1B291;Lo;0;L;;;;;N;;;;;
+1B292;NUSHU CHARACTER-1B292;Lo;0;L;;;;;N;;;;;
+1B293;NUSHU CHARACTER-1B293;Lo;0;L;;;;;N;;;;;
+1B294;NUSHU CHARACTER-1B294;Lo;0;L;;;;;N;;;;;
+1B295;NUSHU CHARACTER-1B295;Lo;0;L;;;;;N;;;;;
+1B296;NUSHU CHARACTER-1B296;Lo;0;L;;;;;N;;;;;
+1B297;NUSHU CHARACTER-1B297;Lo;0;L;;;;;N;;;;;
+1B298;NUSHU CHARACTER-1B298;Lo;0;L;;;;;N;;;;;
+1B299;NUSHU CHARACTER-1B299;Lo;0;L;;;;;N;;;;;
+1B29A;NUSHU CHARACTER-1B29A;Lo;0;L;;;;;N;;;;;
+1B29B;NUSHU CHARACTER-1B29B;Lo;0;L;;;;;N;;;;;
+1B29C;NUSHU CHARACTER-1B29C;Lo;0;L;;;;;N;;;;;
+1B29D;NUSHU CHARACTER-1B29D;Lo;0;L;;;;;N;;;;;
+1B29E;NUSHU CHARACTER-1B29E;Lo;0;L;;;;;N;;;;;
+1B29F;NUSHU CHARACTER-1B29F;Lo;0;L;;;;;N;;;;;
+1B2A0;NUSHU CHARACTER-1B2A0;Lo;0;L;;;;;N;;;;;
+1B2A1;NUSHU CHARACTER-1B2A1;Lo;0;L;;;;;N;;;;;
+1B2A2;NUSHU CHARACTER-1B2A2;Lo;0;L;;;;;N;;;;;
+1B2A3;NUSHU CHARACTER-1B2A3;Lo;0;L;;;;;N;;;;;
+1B2A4;NUSHU CHARACTER-1B2A4;Lo;0;L;;;;;N;;;;;
+1B2A5;NUSHU CHARACTER-1B2A5;Lo;0;L;;;;;N;;;;;
+1B2A6;NUSHU CHARACTER-1B2A6;Lo;0;L;;;;;N;;;;;
+1B2A7;NUSHU CHARACTER-1B2A7;Lo;0;L;;;;;N;;;;;
+1B2A8;NUSHU CHARACTER-1B2A8;Lo;0;L;;;;;N;;;;;
+1B2A9;NUSHU CHARACTER-1B2A9;Lo;0;L;;;;;N;;;;;
+1B2AA;NUSHU CHARACTER-1B2AA;Lo;0;L;;;;;N;;;;;
+1B2AB;NUSHU CHARACTER-1B2AB;Lo;0;L;;;;;N;;;;;
+1B2AC;NUSHU CHARACTER-1B2AC;Lo;0;L;;;;;N;;;;;
+1B2AD;NUSHU CHARACTER-1B2AD;Lo;0;L;;;;;N;;;;;
+1B2AE;NUSHU CHARACTER-1B2AE;Lo;0;L;;;;;N;;;;;
+1B2AF;NUSHU CHARACTER-1B2AF;Lo;0;L;;;;;N;;;;;
+1B2B0;NUSHU CHARACTER-1B2B0;Lo;0;L;;;;;N;;;;;
+1B2B1;NUSHU CHARACTER-1B2B1;Lo;0;L;;;;;N;;;;;
+1B2B2;NUSHU CHARACTER-1B2B2;Lo;0;L;;;;;N;;;;;
+1B2B3;NUSHU CHARACTER-1B2B3;Lo;0;L;;;;;N;;;;;
+1B2B4;NUSHU CHARACTER-1B2B4;Lo;0;L;;;;;N;;;;;
+1B2B5;NUSHU CHARACTER-1B2B5;Lo;0;L;;;;;N;;;;;
+1B2B6;NUSHU CHARACTER-1B2B6;Lo;0;L;;;;;N;;;;;
+1B2B7;NUSHU CHARACTER-1B2B7;Lo;0;L;;;;;N;;;;;
+1B2B8;NUSHU CHARACTER-1B2B8;Lo;0;L;;;;;N;;;;;
+1B2B9;NUSHU CHARACTER-1B2B9;Lo;0;L;;;;;N;;;;;
+1B2BA;NUSHU CHARACTER-1B2BA;Lo;0;L;;;;;N;;;;;
+1B2BB;NUSHU CHARACTER-1B2BB;Lo;0;L;;;;;N;;;;;
+1B2BC;NUSHU CHARACTER-1B2BC;Lo;0;L;;;;;N;;;;;
+1B2BD;NUSHU CHARACTER-1B2BD;Lo;0;L;;;;;N;;;;;
+1B2BE;NUSHU CHARACTER-1B2BE;Lo;0;L;;;;;N;;;;;
+1B2BF;NUSHU CHARACTER-1B2BF;Lo;0;L;;;;;N;;;;;
+1B2C0;NUSHU CHARACTER-1B2C0;Lo;0;L;;;;;N;;;;;
+1B2C1;NUSHU CHARACTER-1B2C1;Lo;0;L;;;;;N;;;;;
+1B2C2;NUSHU CHARACTER-1B2C2;Lo;0;L;;;;;N;;;;;
+1B2C3;NUSHU CHARACTER-1B2C3;Lo;0;L;;;;;N;;;;;
+1B2C4;NUSHU CHARACTER-1B2C4;Lo;0;L;;;;;N;;;;;
+1B2C5;NUSHU CHARACTER-1B2C5;Lo;0;L;;;;;N;;;;;
+1B2C6;NUSHU CHARACTER-1B2C6;Lo;0;L;;;;;N;;;;;
+1B2C7;NUSHU CHARACTER-1B2C7;Lo;0;L;;;;;N;;;;;
+1B2C8;NUSHU CHARACTER-1B2C8;Lo;0;L;;;;;N;;;;;
+1B2C9;NUSHU CHARACTER-1B2C9;Lo;0;L;;;;;N;;;;;
+1B2CA;NUSHU CHARACTER-1B2CA;Lo;0;L;;;;;N;;;;;
+1B2CB;NUSHU CHARACTER-1B2CB;Lo;0;L;;;;;N;;;;;
+1B2CC;NUSHU CHARACTER-1B2CC;Lo;0;L;;;;;N;;;;;
+1B2CD;NUSHU CHARACTER-1B2CD;Lo;0;L;;;;;N;;;;;
+1B2CE;NUSHU CHARACTER-1B2CE;Lo;0;L;;;;;N;;;;;
+1B2CF;NUSHU CHARACTER-1B2CF;Lo;0;L;;;;;N;;;;;
+1B2D0;NUSHU CHARACTER-1B2D0;Lo;0;L;;;;;N;;;;;
+1B2D1;NUSHU CHARACTER-1B2D1;Lo;0;L;;;;;N;;;;;
+1B2D2;NUSHU CHARACTER-1B2D2;Lo;0;L;;;;;N;;;;;
+1B2D3;NUSHU CHARACTER-1B2D3;Lo;0;L;;;;;N;;;;;
+1B2D4;NUSHU CHARACTER-1B2D4;Lo;0;L;;;;;N;;;;;
+1B2D5;NUSHU CHARACTER-1B2D5;Lo;0;L;;;;;N;;;;;
+1B2D6;NUSHU CHARACTER-1B2D6;Lo;0;L;;;;;N;;;;;
+1B2D7;NUSHU CHARACTER-1B2D7;Lo;0;L;;;;;N;;;;;
+1B2D8;NUSHU CHARACTER-1B2D8;Lo;0;L;;;;;N;;;;;
+1B2D9;NUSHU CHARACTER-1B2D9;Lo;0;L;;;;;N;;;;;
+1B2DA;NUSHU CHARACTER-1B2DA;Lo;0;L;;;;;N;;;;;
+1B2DB;NUSHU CHARACTER-1B2DB;Lo;0;L;;;;;N;;;;;
+1B2DC;NUSHU CHARACTER-1B2DC;Lo;0;L;;;;;N;;;;;
+1B2DD;NUSHU CHARACTER-1B2DD;Lo;0;L;;;;;N;;;;;
+1B2DE;NUSHU CHARACTER-1B2DE;Lo;0;L;;;;;N;;;;;
+1B2DF;NUSHU CHARACTER-1B2DF;Lo;0;L;;;;;N;;;;;
+1B2E0;NUSHU CHARACTER-1B2E0;Lo;0;L;;;;;N;;;;;
+1B2E1;NUSHU CHARACTER-1B2E1;Lo;0;L;;;;;N;;;;;
+1B2E2;NUSHU CHARACTER-1B2E2;Lo;0;L;;;;;N;;;;;
+1B2E3;NUSHU CHARACTER-1B2E3;Lo;0;L;;;;;N;;;;;
+1B2E4;NUSHU CHARACTER-1B2E4;Lo;0;L;;;;;N;;;;;
+1B2E5;NUSHU CHARACTER-1B2E5;Lo;0;L;;;;;N;;;;;
+1B2E6;NUSHU CHARACTER-1B2E6;Lo;0;L;;;;;N;;;;;
+1B2E7;NUSHU CHARACTER-1B2E7;Lo;0;L;;;;;N;;;;;
+1B2E8;NUSHU CHARACTER-1B2E8;Lo;0;L;;;;;N;;;;;
+1B2E9;NUSHU CHARACTER-1B2E9;Lo;0;L;;;;;N;;;;;
+1B2EA;NUSHU CHARACTER-1B2EA;Lo;0;L;;;;;N;;;;;
+1B2EB;NUSHU CHARACTER-1B2EB;Lo;0;L;;;;;N;;;;;
+1B2EC;NUSHU CHARACTER-1B2EC;Lo;0;L;;;;;N;;;;;
+1B2ED;NUSHU CHARACTER-1B2ED;Lo;0;L;;;;;N;;;;;
+1B2EE;NUSHU CHARACTER-1B2EE;Lo;0;L;;;;;N;;;;;
+1B2EF;NUSHU CHARACTER-1B2EF;Lo;0;L;;;;;N;;;;;
+1B2F0;NUSHU CHARACTER-1B2F0;Lo;0;L;;;;;N;;;;;
+1B2F1;NUSHU CHARACTER-1B2F1;Lo;0;L;;;;;N;;;;;
+1B2F2;NUSHU CHARACTER-1B2F2;Lo;0;L;;;;;N;;;;;
+1B2F3;NUSHU CHARACTER-1B2F3;Lo;0;L;;;;;N;;;;;
+1B2F4;NUSHU CHARACTER-1B2F4;Lo;0;L;;;;;N;;;;;
+1B2F5;NUSHU CHARACTER-1B2F5;Lo;0;L;;;;;N;;;;;
+1B2F6;NUSHU CHARACTER-1B2F6;Lo;0;L;;;;;N;;;;;
+1B2F7;NUSHU CHARACTER-1B2F7;Lo;0;L;;;;;N;;;;;
+1B2F8;NUSHU CHARACTER-1B2F8;Lo;0;L;;;;;N;;;;;
+1B2F9;NUSHU CHARACTER-1B2F9;Lo;0;L;;;;;N;;;;;
+1B2FA;NUSHU CHARACTER-1B2FA;Lo;0;L;;;;;N;;;;;
+1B2FB;NUSHU CHARACTER-1B2FB;Lo;0;L;;;;;N;;;;;
+1BC00;DUPLOYAN LETTER H;Lo;0;L;;;;;N;;;;;
+1BC01;DUPLOYAN LETTER X;Lo;0;L;;;;;N;;;;;
+1BC02;DUPLOYAN LETTER P;Lo;0;L;;;;;N;;;;;
+1BC03;DUPLOYAN LETTER T;Lo;0;L;;;;;N;;;;;
+1BC04;DUPLOYAN LETTER F;Lo;0;L;;;;;N;;;;;
+1BC05;DUPLOYAN LETTER K;Lo;0;L;;;;;N;;;;;
+1BC06;DUPLOYAN LETTER L;Lo;0;L;;;;;N;;;;;
+1BC07;DUPLOYAN LETTER B;Lo;0;L;;;;;N;;;;;
+1BC08;DUPLOYAN LETTER D;Lo;0;L;;;;;N;;;;;
+1BC09;DUPLOYAN LETTER V;Lo;0;L;;;;;N;;;;;
+1BC0A;DUPLOYAN LETTER G;Lo;0;L;;;;;N;;;;;
+1BC0B;DUPLOYAN LETTER R;Lo;0;L;;;;;N;;;;;
+1BC0C;DUPLOYAN LETTER P N;Lo;0;L;;;;;N;;;;;
+1BC0D;DUPLOYAN LETTER D S;Lo;0;L;;;;;N;;;;;
+1BC0E;DUPLOYAN LETTER F N;Lo;0;L;;;;;N;;;;;
+1BC0F;DUPLOYAN LETTER K M;Lo;0;L;;;;;N;;;;;
+1BC10;DUPLOYAN LETTER R S;Lo;0;L;;;;;N;;;;;
+1BC11;DUPLOYAN LETTER TH;Lo;0;L;;;;;N;;;;;
+1BC12;DUPLOYAN LETTER SLOAN DH;Lo;0;L;;;;;N;;;;;
+1BC13;DUPLOYAN LETTER DH;Lo;0;L;;;;;N;;;;;
+1BC14;DUPLOYAN LETTER KK;Lo;0;L;;;;;N;;;;;
+1BC15;DUPLOYAN LETTER SLOAN J;Lo;0;L;;;;;N;;;;;
+1BC16;DUPLOYAN LETTER HL;Lo;0;L;;;;;N;;;;;
+1BC17;DUPLOYAN LETTER LH;Lo;0;L;;;;;N;;;;;
+1BC18;DUPLOYAN LETTER RH;Lo;0;L;;;;;N;;;;;
+1BC19;DUPLOYAN LETTER M;Lo;0;L;;;;;N;;;;;
+1BC1A;DUPLOYAN LETTER N;Lo;0;L;;;;;N;;;;;
+1BC1B;DUPLOYAN LETTER J;Lo;0;L;;;;;N;;;;;
+1BC1C;DUPLOYAN LETTER S;Lo;0;L;;;;;N;;;;;
+1BC1D;DUPLOYAN LETTER M N;Lo;0;L;;;;;N;;;;;
+1BC1E;DUPLOYAN LETTER N M;Lo;0;L;;;;;N;;;;;
+1BC1F;DUPLOYAN LETTER J M;Lo;0;L;;;;;N;;;;;
+1BC20;DUPLOYAN LETTER S J;Lo;0;L;;;;;N;;;;;
+1BC21;DUPLOYAN LETTER M WITH DOT;Lo;0;L;;;;;N;;;;;
+1BC22;DUPLOYAN LETTER N WITH DOT;Lo;0;L;;;;;N;;;;;
+1BC23;DUPLOYAN LETTER J WITH DOT;Lo;0;L;;;;;N;;;;;
+1BC24;DUPLOYAN LETTER J WITH DOTS INSIDE AND ABOVE;Lo;0;L;;;;;N;;;;;
+1BC25;DUPLOYAN LETTER S WITH DOT;Lo;0;L;;;;;N;;;;;
+1BC26;DUPLOYAN LETTER S WITH DOT BELOW;Lo;0;L;;;;;N;;;;;
+1BC27;DUPLOYAN LETTER M S;Lo;0;L;;;;;N;;;;;
+1BC28;DUPLOYAN LETTER N S;Lo;0;L;;;;;N;;;;;
+1BC29;DUPLOYAN LETTER J S;Lo;0;L;;;;;N;;;;;
+1BC2A;DUPLOYAN LETTER S S;Lo;0;L;;;;;N;;;;;
+1BC2B;DUPLOYAN LETTER M N S;Lo;0;L;;;;;N;;;;;
+1BC2C;DUPLOYAN LETTER N M S;Lo;0;L;;;;;N;;;;;
+1BC2D;DUPLOYAN LETTER J M S;Lo;0;L;;;;;N;;;;;
+1BC2E;DUPLOYAN LETTER S J S;Lo;0;L;;;;;N;;;;;
+1BC2F;DUPLOYAN LETTER J S WITH DOT;Lo;0;L;;;;;N;;;;;
+1BC30;DUPLOYAN LETTER J N;Lo;0;L;;;;;N;;;;;
+1BC31;DUPLOYAN LETTER J N S;Lo;0;L;;;;;N;;;;;
+1BC32;DUPLOYAN LETTER S T;Lo;0;L;;;;;N;;;;;
+1BC33;DUPLOYAN LETTER S T R;Lo;0;L;;;;;N;;;;;
+1BC34;DUPLOYAN LETTER S P;Lo;0;L;;;;;N;;;;;
+1BC35;DUPLOYAN LETTER S P R;Lo;0;L;;;;;N;;;;;
+1BC36;DUPLOYAN LETTER T S;Lo;0;L;;;;;N;;;;;
+1BC37;DUPLOYAN LETTER T R S;Lo;0;L;;;;;N;;;;;
+1BC38;DUPLOYAN LETTER W;Lo;0;L;;;;;N;;;;;
+1BC39;DUPLOYAN LETTER WH;Lo;0;L;;;;;N;;;;;
+1BC3A;DUPLOYAN LETTER W R;Lo;0;L;;;;;N;;;;;
+1BC3B;DUPLOYAN LETTER S N;Lo;0;L;;;;;N;;;;;
+1BC3C;DUPLOYAN LETTER S M;Lo;0;L;;;;;N;;;;;
+1BC3D;DUPLOYAN LETTER K R S;Lo;0;L;;;;;N;;;;;
+1BC3E;DUPLOYAN LETTER G R S;Lo;0;L;;;;;N;;;;;
+1BC3F;DUPLOYAN LETTER S K;Lo;0;L;;;;;N;;;;;
+1BC40;DUPLOYAN LETTER S K R;Lo;0;L;;;;;N;;;;;
+1BC41;DUPLOYAN LETTER A;Lo;0;L;;;;;N;;;;;
+1BC42;DUPLOYAN LETTER SLOAN OW;Lo;0;L;;;;;N;;;;;
+1BC43;DUPLOYAN LETTER OA;Lo;0;L;;;;;N;;;;;
+1BC44;DUPLOYAN LETTER O;Lo;0;L;;;;;N;;;;;
+1BC45;DUPLOYAN LETTER AOU;Lo;0;L;;;;;N;;;;;
+1BC46;DUPLOYAN LETTER I;Lo;0;L;;;;;N;;;;;
+1BC47;DUPLOYAN LETTER E;Lo;0;L;;;;;N;;;;;
+1BC48;DUPLOYAN LETTER IE;Lo;0;L;;;;;N;;;;;
+1BC49;DUPLOYAN LETTER SHORT I;Lo;0;L;;;;;N;;;;;
+1BC4A;DUPLOYAN LETTER UI;Lo;0;L;;;;;N;;;;;
+1BC4B;DUPLOYAN LETTER EE;Lo;0;L;;;;;N;;;;;
+1BC4C;DUPLOYAN LETTER SLOAN EH;Lo;0;L;;;;;N;;;;;
+1BC4D;DUPLOYAN LETTER ROMANIAN I;Lo;0;L;;;;;N;;;;;
+1BC4E;DUPLOYAN LETTER SLOAN EE;Lo;0;L;;;;;N;;;;;
+1BC4F;DUPLOYAN LETTER LONG I;Lo;0;L;;;;;N;;;;;
+1BC50;DUPLOYAN LETTER YE;Lo;0;L;;;;;N;;;;;
+1BC51;DUPLOYAN LETTER U;Lo;0;L;;;;;N;;;;;
+1BC52;DUPLOYAN LETTER EU;Lo;0;L;;;;;N;;;;;
+1BC53;DUPLOYAN LETTER XW;Lo;0;L;;;;;N;;;;;
+1BC54;DUPLOYAN LETTER U N;Lo;0;L;;;;;N;;;;;
+1BC55;DUPLOYAN LETTER LONG U;Lo;0;L;;;;;N;;;;;
+1BC56;DUPLOYAN LETTER ROMANIAN U;Lo;0;L;;;;;N;;;;;
+1BC57;DUPLOYAN LETTER UH;Lo;0;L;;;;;N;;;;;
+1BC58;DUPLOYAN LETTER SLOAN U;Lo;0;L;;;;;N;;;;;
+1BC59;DUPLOYAN LETTER OOH;Lo;0;L;;;;;N;;;;;
+1BC5A;DUPLOYAN LETTER OW;Lo;0;L;;;;;N;;;;;
+1BC5B;DUPLOYAN LETTER OU;Lo;0;L;;;;;N;;;;;
+1BC5C;DUPLOYAN LETTER WA;Lo;0;L;;;;;N;;;;;
+1BC5D;DUPLOYAN LETTER WO;Lo;0;L;;;;;N;;;;;
+1BC5E;DUPLOYAN LETTER WI;Lo;0;L;;;;;N;;;;;
+1BC5F;DUPLOYAN LETTER WEI;Lo;0;L;;;;;N;;;;;
+1BC60;DUPLOYAN LETTER WOW;Lo;0;L;;;;;N;;;;;
+1BC61;DUPLOYAN LETTER NASAL U;Lo;0;L;;;;;N;;;;;
+1BC62;DUPLOYAN LETTER NASAL O;Lo;0;L;;;;;N;;;;;
+1BC63;DUPLOYAN LETTER NASAL I;Lo;0;L;;;;;N;;;;;
+1BC64;DUPLOYAN LETTER NASAL A;Lo;0;L;;;;;N;;;;;
+1BC65;DUPLOYAN LETTER PERNIN AN;Lo;0;L;;;;;N;;;;;
+1BC66;DUPLOYAN LETTER PERNIN AM;Lo;0;L;;;;;N;;;;;
+1BC67;DUPLOYAN LETTER SLOAN EN;Lo;0;L;;;;;N;;;;;
+1BC68;DUPLOYAN LETTER SLOAN AN;Lo;0;L;;;;;N;;;;;
+1BC69;DUPLOYAN LETTER SLOAN ON;Lo;0;L;;;;;N;;;;;
+1BC6A;DUPLOYAN LETTER VOCALIC M;Lo;0;L;;;;;N;;;;;
+1BC70;DUPLOYAN AFFIX LEFT HORIZONTAL SECANT;Lo;0;L;;;;;N;;;;;
+1BC71;DUPLOYAN AFFIX MID HORIZONTAL SECANT;Lo;0;L;;;;;N;;;;;
+1BC72;DUPLOYAN AFFIX RIGHT HORIZONTAL SECANT;Lo;0;L;;;;;N;;;;;
+1BC73;DUPLOYAN AFFIX LOW VERTICAL SECANT;Lo;0;L;;;;;N;;;;;
+1BC74;DUPLOYAN AFFIX MID VERTICAL SECANT;Lo;0;L;;;;;N;;;;;
+1BC75;DUPLOYAN AFFIX HIGH VERTICAL SECANT;Lo;0;L;;;;;N;;;;;
+1BC76;DUPLOYAN AFFIX ATTACHED SECANT;Lo;0;L;;;;;N;;;;;
+1BC77;DUPLOYAN AFFIX ATTACHED LEFT-TO-RIGHT SECANT;Lo;0;L;;;;;N;;;;;
+1BC78;DUPLOYAN AFFIX ATTACHED TANGENT;Lo;0;L;;;;;N;;;;;
+1BC79;DUPLOYAN AFFIX ATTACHED TAIL;Lo;0;L;;;;;N;;;;;
+1BC7A;DUPLOYAN AFFIX ATTACHED E HOOK;Lo;0;L;;;;;N;;;;;
+1BC7B;DUPLOYAN AFFIX ATTACHED I HOOK;Lo;0;L;;;;;N;;;;;
+1BC7C;DUPLOYAN AFFIX ATTACHED TANGENT HOOK;Lo;0;L;;;;;N;;;;;
+1BC80;DUPLOYAN AFFIX HIGH ACUTE;Lo;0;L;;;;;N;;;;;
+1BC81;DUPLOYAN AFFIX HIGH TIGHT ACUTE;Lo;0;L;;;;;N;;;;;
+1BC82;DUPLOYAN AFFIX HIGH GRAVE;Lo;0;L;;;;;N;;;;;
+1BC83;DUPLOYAN AFFIX HIGH LONG GRAVE;Lo;0;L;;;;;N;;;;;
+1BC84;DUPLOYAN AFFIX HIGH DOT;Lo;0;L;;;;;N;;;;;
+1BC85;DUPLOYAN AFFIX HIGH CIRCLE;Lo;0;L;;;;;N;;;;;
+1BC86;DUPLOYAN AFFIX HIGH LINE;Lo;0;L;;;;;N;;;;;
+1BC87;DUPLOYAN AFFIX HIGH WAVE;Lo;0;L;;;;;N;;;;;
+1BC88;DUPLOYAN AFFIX HIGH VERTICAL;Lo;0;L;;;;;N;;;;;
+1BC90;DUPLOYAN AFFIX LOW ACUTE;Lo;0;L;;;;;N;;;;;
+1BC91;DUPLOYAN AFFIX LOW TIGHT ACUTE;Lo;0;L;;;;;N;;;;;
+1BC92;DUPLOYAN AFFIX LOW GRAVE;Lo;0;L;;;;;N;;;;;
+1BC93;DUPLOYAN AFFIX LOW LONG GRAVE;Lo;0;L;;;;;N;;;;;
+1BC94;DUPLOYAN AFFIX LOW DOT;Lo;0;L;;;;;N;;;;;
+1BC95;DUPLOYAN AFFIX LOW CIRCLE;Lo;0;L;;;;;N;;;;;
+1BC96;DUPLOYAN AFFIX LOW LINE;Lo;0;L;;;;;N;;;;;
+1BC97;DUPLOYAN AFFIX LOW WAVE;Lo;0;L;;;;;N;;;;;
+1BC98;DUPLOYAN AFFIX LOW VERTICAL;Lo;0;L;;;;;N;;;;;
+1BC99;DUPLOYAN AFFIX LOW ARROW;Lo;0;L;;;;;N;;;;;
+1BC9C;DUPLOYAN SIGN O WITH CROSS;So;0;L;;;;;N;;;;;
+1BC9D;DUPLOYAN THICK LETTER SELECTOR;Mn;0;NSM;;;;;N;;;;;
+1BC9E;DUPLOYAN DOUBLE MARK;Mn;1;NSM;;;;;N;;;;;
+1BC9F;DUPLOYAN PUNCTUATION CHINOOK FULL STOP;Po;0;L;;;;;N;;;;;
+1BCA0;SHORTHAND FORMAT LETTER OVERLAP;Cf;0;BN;;;;;N;;;;;
+1BCA1;SHORTHAND FORMAT CONTINUING OVERLAP;Cf;0;BN;;;;;N;;;;;
+1BCA2;SHORTHAND FORMAT DOWN STEP;Cf;0;BN;;;;;N;;;;;
+1BCA3;SHORTHAND FORMAT UP STEP;Cf;0;BN;;;;;N;;;;;
+1D000;BYZANTINE MUSICAL SYMBOL PSILI;So;0;L;;;;;N;;;;;
+1D001;BYZANTINE MUSICAL SYMBOL DASEIA;So;0;L;;;;;N;;;;;
+1D002;BYZANTINE MUSICAL SYMBOL PERISPOMENI;So;0;L;;;;;N;;;;;
+1D003;BYZANTINE MUSICAL SYMBOL OXEIA EKFONITIKON;So;0;L;;;;;N;;;;;
+1D004;BYZANTINE MUSICAL SYMBOL OXEIA DIPLI;So;0;L;;;;;N;;;;;
+1D005;BYZANTINE MUSICAL SYMBOL VAREIA EKFONITIKON;So;0;L;;;;;N;;;;;
+1D006;BYZANTINE MUSICAL SYMBOL VAREIA DIPLI;So;0;L;;;;;N;;;;;
+1D007;BYZANTINE MUSICAL SYMBOL KATHISTI;So;0;L;;;;;N;;;;;
+1D008;BYZANTINE MUSICAL SYMBOL SYRMATIKI;So;0;L;;;;;N;;;;;
+1D009;BYZANTINE MUSICAL SYMBOL PARAKLITIKI;So;0;L;;;;;N;;;;;
+1D00A;BYZANTINE MUSICAL SYMBOL YPOKRISIS;So;0;L;;;;;N;;;;;
+1D00B;BYZANTINE MUSICAL SYMBOL YPOKRISIS DIPLI;So;0;L;;;;;N;;;;;
+1D00C;BYZANTINE MUSICAL SYMBOL KREMASTI;So;0;L;;;;;N;;;;;
+1D00D;BYZANTINE MUSICAL SYMBOL APESO EKFONITIKON;So;0;L;;;;;N;;;;;
+1D00E;BYZANTINE MUSICAL SYMBOL EXO EKFONITIKON;So;0;L;;;;;N;;;;;
+1D00F;BYZANTINE MUSICAL SYMBOL TELEIA;So;0;L;;;;;N;;;;;
+1D010;BYZANTINE MUSICAL SYMBOL KENTIMATA;So;0;L;;;;;N;;;;;
+1D011;BYZANTINE MUSICAL SYMBOL APOSTROFOS;So;0;L;;;;;N;;;;;
+1D012;BYZANTINE MUSICAL SYMBOL APOSTROFOS DIPLI;So;0;L;;;;;N;;;;;
+1D013;BYZANTINE MUSICAL SYMBOL SYNEVMA;So;0;L;;;;;N;;;;;
+1D014;BYZANTINE MUSICAL SYMBOL THITA;So;0;L;;;;;N;;;;;
+1D015;BYZANTINE MUSICAL SYMBOL OLIGON ARCHAION;So;0;L;;;;;N;;;;;
+1D016;BYZANTINE MUSICAL SYMBOL GORGON ARCHAION;So;0;L;;;;;N;;;;;
+1D017;BYZANTINE MUSICAL SYMBOL PSILON;So;0;L;;;;;N;;;;;
+1D018;BYZANTINE MUSICAL SYMBOL CHAMILON;So;0;L;;;;;N;;;;;
+1D019;BYZANTINE MUSICAL SYMBOL VATHY;So;0;L;;;;;N;;;;;
+1D01A;BYZANTINE MUSICAL SYMBOL ISON ARCHAION;So;0;L;;;;;N;;;;;
+1D01B;BYZANTINE MUSICAL SYMBOL KENTIMA ARCHAION;So;0;L;;;;;N;;;;;
+1D01C;BYZANTINE MUSICAL SYMBOL KENTIMATA ARCHAION;So;0;L;;;;;N;;;;;
+1D01D;BYZANTINE MUSICAL SYMBOL SAXIMATA;So;0;L;;;;;N;;;;;
+1D01E;BYZANTINE MUSICAL SYMBOL PARICHON;So;0;L;;;;;N;;;;;
+1D01F;BYZANTINE MUSICAL SYMBOL STAVROS APODEXIA;So;0;L;;;;;N;;;;;
+1D020;BYZANTINE MUSICAL SYMBOL OXEIAI ARCHAION;So;0;L;;;;;N;;;;;
+1D021;BYZANTINE MUSICAL SYMBOL VAREIAI ARCHAION;So;0;L;;;;;N;;;;;
+1D022;BYZANTINE MUSICAL SYMBOL APODERMA ARCHAION;So;0;L;;;;;N;;;;;
+1D023;BYZANTINE MUSICAL SYMBOL APOTHEMA;So;0;L;;;;;N;;;;;
+1D024;BYZANTINE MUSICAL SYMBOL KLASMA;So;0;L;;;;;N;;;;;
+1D025;BYZANTINE MUSICAL SYMBOL REVMA;So;0;L;;;;;N;;;;;
+1D026;BYZANTINE MUSICAL SYMBOL PIASMA ARCHAION;So;0;L;;;;;N;;;;;
+1D027;BYZANTINE MUSICAL SYMBOL TINAGMA;So;0;L;;;;;N;;;;;
+1D028;BYZANTINE MUSICAL SYMBOL ANATRICHISMA;So;0;L;;;;;N;;;;;
+1D029;BYZANTINE MUSICAL SYMBOL SEISMA;So;0;L;;;;;N;;;;;
+1D02A;BYZANTINE MUSICAL SYMBOL SYNAGMA ARCHAION;So;0;L;;;;;N;;;;;
+1D02B;BYZANTINE MUSICAL SYMBOL SYNAGMA META STAVROU;So;0;L;;;;;N;;;;;
+1D02C;BYZANTINE MUSICAL SYMBOL OYRANISMA ARCHAION;So;0;L;;;;;N;;;;;
+1D02D;BYZANTINE MUSICAL SYMBOL THEMA;So;0;L;;;;;N;;;;;
+1D02E;BYZANTINE MUSICAL SYMBOL LEMOI;So;0;L;;;;;N;;;;;
+1D02F;BYZANTINE MUSICAL SYMBOL DYO;So;0;L;;;;;N;;;;;
+1D030;BYZANTINE MUSICAL SYMBOL TRIA;So;0;L;;;;;N;;;;;
+1D031;BYZANTINE MUSICAL SYMBOL TESSERA;So;0;L;;;;;N;;;;;
+1D032;BYZANTINE MUSICAL SYMBOL KRATIMATA;So;0;L;;;;;N;;;;;
+1D033;BYZANTINE MUSICAL SYMBOL APESO EXO NEO;So;0;L;;;;;N;;;;;
+1D034;BYZANTINE MUSICAL SYMBOL FTHORA ARCHAION;So;0;L;;;;;N;;;;;
+1D035;BYZANTINE MUSICAL SYMBOL IMIFTHORA;So;0;L;;;;;N;;;;;
+1D036;BYZANTINE MUSICAL SYMBOL TROMIKON ARCHAION;So;0;L;;;;;N;;;;;
+1D037;BYZANTINE MUSICAL SYMBOL KATAVA TROMIKON;So;0;L;;;;;N;;;;;
+1D038;BYZANTINE MUSICAL SYMBOL PELASTON;So;0;L;;;;;N;;;;;
+1D039;BYZANTINE MUSICAL SYMBOL PSIFISTON;So;0;L;;;;;N;;;;;
+1D03A;BYZANTINE MUSICAL SYMBOL KONTEVMA;So;0;L;;;;;N;;;;;
+1D03B;BYZANTINE MUSICAL SYMBOL CHOREVMA ARCHAION;So;0;L;;;;;N;;;;;
+1D03C;BYZANTINE MUSICAL SYMBOL RAPISMA;So;0;L;;;;;N;;;;;
+1D03D;BYZANTINE MUSICAL SYMBOL PARAKALESMA ARCHAION;So;0;L;;;;;N;;;;;
+1D03E;BYZANTINE MUSICAL SYMBOL PARAKLITIKI ARCHAION;So;0;L;;;;;N;;;;;
+1D03F;BYZANTINE MUSICAL SYMBOL ICHADIN;So;0;L;;;;;N;;;;;
+1D040;BYZANTINE MUSICAL SYMBOL NANA;So;0;L;;;;;N;;;;;
+1D041;BYZANTINE MUSICAL SYMBOL PETASMA;So;0;L;;;;;N;;;;;
+1D042;BYZANTINE MUSICAL SYMBOL KONTEVMA ALLO;So;0;L;;;;;N;;;;;
+1D043;BYZANTINE MUSICAL SYMBOL TROMIKON ALLO;So;0;L;;;;;N;;;;;
+1D044;BYZANTINE MUSICAL SYMBOL STRAGGISMATA;So;0;L;;;;;N;;;;;
+1D045;BYZANTINE MUSICAL SYMBOL GRONTHISMATA;So;0;L;;;;;N;;;;;
+1D046;BYZANTINE MUSICAL SYMBOL ISON NEO;So;0;L;;;;;N;;;;;
+1D047;BYZANTINE MUSICAL SYMBOL OLIGON NEO;So;0;L;;;;;N;;;;;
+1D048;BYZANTINE MUSICAL SYMBOL OXEIA NEO;So;0;L;;;;;N;;;;;
+1D049;BYZANTINE MUSICAL SYMBOL PETASTI;So;0;L;;;;;N;;;;;
+1D04A;BYZANTINE MUSICAL SYMBOL KOUFISMA;So;0;L;;;;;N;;;;;
+1D04B;BYZANTINE MUSICAL SYMBOL PETASTOKOUFISMA;So;0;L;;;;;N;;;;;
+1D04C;BYZANTINE MUSICAL SYMBOL KRATIMOKOUFISMA;So;0;L;;;;;N;;;;;
+1D04D;BYZANTINE MUSICAL SYMBOL PELASTON NEO;So;0;L;;;;;N;;;;;
+1D04E;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO ANO;So;0;L;;;;;N;;;;;
+1D04F;BYZANTINE MUSICAL SYMBOL KENTIMA NEO ANO;So;0;L;;;;;N;;;;;
+1D050;BYZANTINE MUSICAL SYMBOL YPSILI;So;0;L;;;;;N;;;;;
+1D051;BYZANTINE MUSICAL SYMBOL APOSTROFOS NEO;So;0;L;;;;;N;;;;;
+1D052;BYZANTINE MUSICAL SYMBOL APOSTROFOI SYNDESMOS NEO;So;0;L;;;;;N;;;;;
+1D053;BYZANTINE MUSICAL SYMBOL YPORROI;So;0;L;;;;;N;;;;;
+1D054;BYZANTINE MUSICAL SYMBOL KRATIMOYPORROON;So;0;L;;;;;N;;;;;
+1D055;BYZANTINE MUSICAL SYMBOL ELAFRON;So;0;L;;;;;N;;;;;
+1D056;BYZANTINE MUSICAL SYMBOL CHAMILI;So;0;L;;;;;N;;;;;
+1D057;BYZANTINE MUSICAL SYMBOL MIKRON ISON;So;0;L;;;;;N;;;;;
+1D058;BYZANTINE MUSICAL SYMBOL VAREIA NEO;So;0;L;;;;;N;;;;;
+1D059;BYZANTINE MUSICAL SYMBOL PIASMA NEO;So;0;L;;;;;N;;;;;
+1D05A;BYZANTINE MUSICAL SYMBOL PSIFISTON NEO;So;0;L;;;;;N;;;;;
+1D05B;BYZANTINE MUSICAL SYMBOL OMALON;So;0;L;;;;;N;;;;;
+1D05C;BYZANTINE MUSICAL SYMBOL ANTIKENOMA;So;0;L;;;;;N;;;;;
+1D05D;BYZANTINE MUSICAL SYMBOL LYGISMA;So;0;L;;;;;N;;;;;
+1D05E;BYZANTINE MUSICAL SYMBOL PARAKLITIKI NEO;So;0;L;;;;;N;;;;;
+1D05F;BYZANTINE MUSICAL SYMBOL PARAKALESMA NEO;So;0;L;;;;;N;;;;;
+1D060;BYZANTINE MUSICAL SYMBOL ETERON PARAKALESMA;So;0;L;;;;;N;;;;;
+1D061;BYZANTINE MUSICAL SYMBOL KYLISMA;So;0;L;;;;;N;;;;;
+1D062;BYZANTINE MUSICAL SYMBOL ANTIKENOKYLISMA;So;0;L;;;;;N;;;;;
+1D063;BYZANTINE MUSICAL SYMBOL TROMIKON NEO;So;0;L;;;;;N;;;;;
+1D064;BYZANTINE MUSICAL SYMBOL EKSTREPTON;So;0;L;;;;;N;;;;;
+1D065;BYZANTINE MUSICAL SYMBOL SYNAGMA NEO;So;0;L;;;;;N;;;;;
+1D066;BYZANTINE MUSICAL SYMBOL SYRMA;So;0;L;;;;;N;;;;;
+1D067;BYZANTINE MUSICAL SYMBOL CHOREVMA NEO;So;0;L;;;;;N;;;;;
+1D068;BYZANTINE MUSICAL SYMBOL EPEGERMA;So;0;L;;;;;N;;;;;
+1D069;BYZANTINE MUSICAL SYMBOL SEISMA NEO;So;0;L;;;;;N;;;;;
+1D06A;BYZANTINE MUSICAL SYMBOL XIRON KLASMA;So;0;L;;;;;N;;;;;
+1D06B;BYZANTINE MUSICAL SYMBOL TROMIKOPSIFISTON;So;0;L;;;;;N;;;;;
+1D06C;BYZANTINE MUSICAL SYMBOL PSIFISTOLYGISMA;So;0;L;;;;;N;;;;;
+1D06D;BYZANTINE MUSICAL SYMBOL TROMIKOLYGISMA;So;0;L;;;;;N;;;;;
+1D06E;BYZANTINE MUSICAL SYMBOL TROMIKOPARAKALESMA;So;0;L;;;;;N;;;;;
+1D06F;BYZANTINE MUSICAL SYMBOL PSIFISTOPARAKALESMA;So;0;L;;;;;N;;;;;
+1D070;BYZANTINE MUSICAL SYMBOL TROMIKOSYNAGMA;So;0;L;;;;;N;;;;;
+1D071;BYZANTINE MUSICAL SYMBOL PSIFISTOSYNAGMA;So;0;L;;;;;N;;;;;
+1D072;BYZANTINE MUSICAL SYMBOL GORGOSYNTHETON;So;0;L;;;;;N;;;;;
+1D073;BYZANTINE MUSICAL SYMBOL ARGOSYNTHETON;So;0;L;;;;;N;;;;;
+1D074;BYZANTINE MUSICAL SYMBOL ETERON ARGOSYNTHETON;So;0;L;;;;;N;;;;;
+1D075;BYZANTINE MUSICAL SYMBOL OYRANISMA NEO;So;0;L;;;;;N;;;;;
+1D076;BYZANTINE MUSICAL SYMBOL THEMATISMOS ESO;So;0;L;;;;;N;;;;;
+1D077;BYZANTINE MUSICAL SYMBOL THEMATISMOS EXO;So;0;L;;;;;N;;;;;
+1D078;BYZANTINE MUSICAL SYMBOL THEMA APLOUN;So;0;L;;;;;N;;;;;
+1D079;BYZANTINE MUSICAL SYMBOL THES KAI APOTHES;So;0;L;;;;;N;;;;;
+1D07A;BYZANTINE MUSICAL SYMBOL KATAVASMA;So;0;L;;;;;N;;;;;
+1D07B;BYZANTINE MUSICAL SYMBOL ENDOFONON;So;0;L;;;;;N;;;;;
+1D07C;BYZANTINE MUSICAL SYMBOL YFEN KATO;So;0;L;;;;;N;;;;;
+1D07D;BYZANTINE MUSICAL SYMBOL YFEN ANO;So;0;L;;;;;N;;;;;
+1D07E;BYZANTINE MUSICAL SYMBOL STAVROS;So;0;L;;;;;N;;;;;
+1D07F;BYZANTINE MUSICAL SYMBOL KLASMA ANO;So;0;L;;;;;N;;;;;
+1D080;BYZANTINE MUSICAL SYMBOL DIPLI ARCHAION;So;0;L;;;;;N;;;;;
+1D081;BYZANTINE MUSICAL SYMBOL KRATIMA ARCHAION;So;0;L;;;;;N;;;;;
+1D082;BYZANTINE MUSICAL SYMBOL KRATIMA ALLO;So;0;L;;;;;N;;;;;
+1D083;BYZANTINE MUSICAL SYMBOL KRATIMA NEO;So;0;L;;;;;N;;;;;
+1D084;BYZANTINE MUSICAL SYMBOL APODERMA NEO;So;0;L;;;;;N;;;;;
+1D085;BYZANTINE MUSICAL SYMBOL APLI;So;0;L;;;;;N;;;;;
+1D086;BYZANTINE MUSICAL SYMBOL DIPLI;So;0;L;;;;;N;;;;;
+1D087;BYZANTINE MUSICAL SYMBOL TRIPLI;So;0;L;;;;;N;;;;;
+1D088;BYZANTINE MUSICAL SYMBOL TETRAPLI;So;0;L;;;;;N;;;;;
+1D089;BYZANTINE MUSICAL SYMBOL KORONIS;So;0;L;;;;;N;;;;;
+1D08A;BYZANTINE MUSICAL SYMBOL LEIMMA ENOS CHRONOU;So;0;L;;;;;N;;;;;
+1D08B;BYZANTINE MUSICAL SYMBOL LEIMMA DYO CHRONON;So;0;L;;;;;N;;;;;
+1D08C;BYZANTINE MUSICAL SYMBOL LEIMMA TRION CHRONON;So;0;L;;;;;N;;;;;
+1D08D;BYZANTINE MUSICAL SYMBOL LEIMMA TESSARON CHRONON;So;0;L;;;;;N;;;;;
+1D08E;BYZANTINE MUSICAL SYMBOL LEIMMA IMISEOS CHRONOU;So;0;L;;;;;N;;;;;
+1D08F;BYZANTINE MUSICAL SYMBOL GORGON NEO ANO;So;0;L;;;;;N;;;;;
+1D090;BYZANTINE MUSICAL SYMBOL GORGON PARESTIGMENON ARISTERA;So;0;L;;;;;N;;;;;
+1D091;BYZANTINE MUSICAL SYMBOL GORGON PARESTIGMENON DEXIA;So;0;L;;;;;N;;;;;
+1D092;BYZANTINE MUSICAL SYMBOL DIGORGON;So;0;L;;;;;N;;;;;
+1D093;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON ARISTERA KATO;So;0;L;;;;;N;;;;;
+1D094;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON ARISTERA ANO;So;0;L;;;;;N;;;;;
+1D095;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON DEXIA;So;0;L;;;;;N;;;;;
+1D096;BYZANTINE MUSICAL SYMBOL TRIGORGON;So;0;L;;;;;N;;;;;
+1D097;BYZANTINE MUSICAL SYMBOL ARGON;So;0;L;;;;;N;;;;;
+1D098;BYZANTINE MUSICAL SYMBOL IMIDIARGON;So;0;L;;;;;N;;;;;
+1D099;BYZANTINE MUSICAL SYMBOL DIARGON;So;0;L;;;;;N;;;;;
+1D09A;BYZANTINE MUSICAL SYMBOL AGOGI POLI ARGI;So;0;L;;;;;N;;;;;
+1D09B;BYZANTINE MUSICAL SYMBOL AGOGI ARGOTERI;So;0;L;;;;;N;;;;;
+1D09C;BYZANTINE MUSICAL SYMBOL AGOGI ARGI;So;0;L;;;;;N;;;;;
+1D09D;BYZANTINE MUSICAL SYMBOL AGOGI METRIA;So;0;L;;;;;N;;;;;
+1D09E;BYZANTINE MUSICAL SYMBOL AGOGI MESI;So;0;L;;;;;N;;;;;
+1D09F;BYZANTINE MUSICAL SYMBOL AGOGI GORGI;So;0;L;;;;;N;;;;;
+1D0A0;BYZANTINE MUSICAL SYMBOL AGOGI GORGOTERI;So;0;L;;;;;N;;;;;
+1D0A1;BYZANTINE MUSICAL SYMBOL AGOGI POLI GORGI;So;0;L;;;;;N;;;;;
+1D0A2;BYZANTINE MUSICAL SYMBOL MARTYRIA PROTOS ICHOS;So;0;L;;;;;N;;;;;
+1D0A3;BYZANTINE MUSICAL SYMBOL MARTYRIA ALLI PROTOS ICHOS;So;0;L;;;;;N;;;;;
+1D0A4;BYZANTINE MUSICAL SYMBOL MARTYRIA DEYTEROS ICHOS;So;0;L;;;;;N;;;;;
+1D0A5;BYZANTINE MUSICAL SYMBOL MARTYRIA ALLI DEYTEROS ICHOS;So;0;L;;;;;N;;;;;
+1D0A6;BYZANTINE MUSICAL SYMBOL MARTYRIA TRITOS ICHOS;So;0;L;;;;;N;;;;;
+1D0A7;BYZANTINE MUSICAL SYMBOL MARTYRIA TRIFONIAS;So;0;L;;;;;N;;;;;
+1D0A8;BYZANTINE MUSICAL SYMBOL MARTYRIA TETARTOS ICHOS;So;0;L;;;;;N;;;;;
+1D0A9;BYZANTINE MUSICAL SYMBOL MARTYRIA TETARTOS LEGETOS ICHOS;So;0;L;;;;;N;;;;;
+1D0AA;BYZANTINE MUSICAL SYMBOL MARTYRIA LEGETOS ICHOS;So;0;L;;;;;N;;;;;
+1D0AB;BYZANTINE MUSICAL SYMBOL MARTYRIA PLAGIOS ICHOS;So;0;L;;;;;N;;;;;
+1D0AC;BYZANTINE MUSICAL SYMBOL ISAKIA TELOUS ICHIMATOS;So;0;L;;;;;N;;;;;
+1D0AD;BYZANTINE MUSICAL SYMBOL APOSTROFOI TELOUS ICHIMATOS;So;0;L;;;;;N;;;;;
+1D0AE;BYZANTINE MUSICAL SYMBOL FANEROSIS TETRAFONIAS;So;0;L;;;;;N;;;;;
+1D0AF;BYZANTINE MUSICAL SYMBOL FANEROSIS MONOFONIAS;So;0;L;;;;;N;;;;;
+1D0B0;BYZANTINE MUSICAL SYMBOL FANEROSIS DIFONIAS;So;0;L;;;;;N;;;;;
+1D0B1;BYZANTINE MUSICAL SYMBOL MARTYRIA VARYS ICHOS;So;0;L;;;;;N;;;;;
+1D0B2;BYZANTINE MUSICAL SYMBOL MARTYRIA PROTOVARYS ICHOS;So;0;L;;;;;N;;;;;
+1D0B3;BYZANTINE MUSICAL SYMBOL MARTYRIA PLAGIOS TETARTOS ICHOS;So;0;L;;;;;N;;;;;
+1D0B4;BYZANTINE MUSICAL SYMBOL GORTHMIKON N APLOUN;So;0;L;;;;;N;;;;;
+1D0B5;BYZANTINE MUSICAL SYMBOL GORTHMIKON N DIPLOUN;So;0;L;;;;;N;;;;;
+1D0B6;BYZANTINE MUSICAL SYMBOL ENARXIS KAI FTHORA VOU;So;0;L;;;;;N;;;;;
+1D0B7;BYZANTINE MUSICAL SYMBOL IMIFONON;So;0;L;;;;;N;;;;;
+1D0B8;BYZANTINE MUSICAL SYMBOL IMIFTHORON;So;0;L;;;;;N;;;;;
+1D0B9;BYZANTINE MUSICAL SYMBOL FTHORA ARCHAION DEYTEROU ICHOU;So;0;L;;;;;N;;;;;
+1D0BA;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI PA;So;0;L;;;;;N;;;;;
+1D0BB;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NANA;So;0;L;;;;;N;;;;;
+1D0BC;BYZANTINE MUSICAL SYMBOL FTHORA NAOS ICHOS;So;0;L;;;;;N;;;;;
+1D0BD;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI DI;So;0;L;;;;;N;;;;;
+1D0BE;BYZANTINE MUSICAL SYMBOL FTHORA SKLIRON DIATONON DI;So;0;L;;;;;N;;;;;
+1D0BF;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI KE;So;0;L;;;;;N;;;;;
+1D0C0;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI ZO;So;0;L;;;;;N;;;;;
+1D0C1;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NI KATO;So;0;L;;;;;N;;;;;
+1D0C2;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NI ANO;So;0;L;;;;;N;;;;;
+1D0C3;BYZANTINE MUSICAL SYMBOL FTHORA MALAKON CHROMA DIFONIAS;So;0;L;;;;;N;;;;;
+1D0C4;BYZANTINE MUSICAL SYMBOL FTHORA MALAKON CHROMA MONOFONIAS;So;0;L;;;;;N;;;;;
+1D0C5;BYZANTINE MUSICAL SYMBOL FHTORA SKLIRON CHROMA VASIS;So;0;L;;;;;N;;;;;
+1D0C6;BYZANTINE MUSICAL SYMBOL FTHORA SKLIRON CHROMA SYNAFI;So;0;L;;;;;N;;;;;
+1D0C7;BYZANTINE MUSICAL SYMBOL FTHORA NENANO;So;0;L;;;;;N;;;;;
+1D0C8;BYZANTINE MUSICAL SYMBOL CHROA ZYGOS;So;0;L;;;;;N;;;;;
+1D0C9;BYZANTINE MUSICAL SYMBOL CHROA KLITON;So;0;L;;;;;N;;;;;
+1D0CA;BYZANTINE MUSICAL SYMBOL CHROA SPATHI;So;0;L;;;;;N;;;;;
+1D0CB;BYZANTINE MUSICAL SYMBOL FTHORA I YFESIS TETARTIMORION;So;0;L;;;;;N;;;;;
+1D0CC;BYZANTINE MUSICAL SYMBOL FTHORA ENARMONIOS ANTIFONIA;So;0;L;;;;;N;;;;;
+1D0CD;BYZANTINE MUSICAL SYMBOL YFESIS TRITIMORION;So;0;L;;;;;N;;;;;
+1D0CE;BYZANTINE MUSICAL SYMBOL DIESIS TRITIMORION;So;0;L;;;;;N;;;;;
+1D0CF;BYZANTINE MUSICAL SYMBOL DIESIS TETARTIMORION;So;0;L;;;;;N;;;;;
+1D0D0;BYZANTINE MUSICAL SYMBOL DIESIS APLI DYO DODEKATA;So;0;L;;;;;N;;;;;
+1D0D1;BYZANTINE MUSICAL SYMBOL DIESIS MONOGRAMMOS TESSERA DODEKATA;So;0;L;;;;;N;;;;;
+1D0D2;BYZANTINE MUSICAL SYMBOL DIESIS DIGRAMMOS EX DODEKATA;So;0;L;;;;;N;;;;;
+1D0D3;BYZANTINE MUSICAL SYMBOL DIESIS TRIGRAMMOS OKTO DODEKATA;So;0;L;;;;;N;;;;;
+1D0D4;BYZANTINE MUSICAL SYMBOL YFESIS APLI DYO DODEKATA;So;0;L;;;;;N;;;;;
+1D0D5;BYZANTINE MUSICAL SYMBOL YFESIS MONOGRAMMOS TESSERA DODEKATA;So;0;L;;;;;N;;;;;
+1D0D6;BYZANTINE MUSICAL SYMBOL YFESIS DIGRAMMOS EX DODEKATA;So;0;L;;;;;N;;;;;
+1D0D7;BYZANTINE MUSICAL SYMBOL YFESIS TRIGRAMMOS OKTO DODEKATA;So;0;L;;;;;N;;;;;
+1D0D8;BYZANTINE MUSICAL SYMBOL GENIKI DIESIS;So;0;L;;;;;N;;;;;
+1D0D9;BYZANTINE MUSICAL SYMBOL GENIKI YFESIS;So;0;L;;;;;N;;;;;
+1D0DA;BYZANTINE MUSICAL SYMBOL DIASTOLI APLI MIKRI;So;0;L;;;;;N;;;;;
+1D0DB;BYZANTINE MUSICAL SYMBOL DIASTOLI APLI MEGALI;So;0;L;;;;;N;;;;;
+1D0DC;BYZANTINE MUSICAL SYMBOL DIASTOLI DIPLI;So;0;L;;;;;N;;;;;
+1D0DD;BYZANTINE MUSICAL SYMBOL DIASTOLI THESEOS;So;0;L;;;;;N;;;;;
+1D0DE;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS;So;0;L;;;;;N;;;;;
+1D0DF;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS DISIMOU;So;0;L;;;;;N;;;;;
+1D0E0;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS TRISIMOU;So;0;L;;;;;N;;;;;
+1D0E1;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS TETRASIMOU;So;0;L;;;;;N;;;;;
+1D0E2;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS;So;0;L;;;;;N;;;;;
+1D0E3;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS DISIMOU;So;0;L;;;;;N;;;;;
+1D0E4;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS TRISIMOU;So;0;L;;;;;N;;;;;
+1D0E5;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS TETRASIMOU;So;0;L;;;;;N;;;;;
+1D0E6;BYZANTINE MUSICAL SYMBOL DIGRAMMA GG;So;0;L;;;;;N;;;;;
+1D0E7;BYZANTINE MUSICAL SYMBOL DIFTOGGOS OU;So;0;L;;;;;N;;;;;
+1D0E8;BYZANTINE MUSICAL SYMBOL STIGMA;So;0;L;;;;;N;;;;;
+1D0E9;BYZANTINE MUSICAL SYMBOL ARKTIKO PA;So;0;L;;;;;N;;;;;
+1D0EA;BYZANTINE MUSICAL SYMBOL ARKTIKO VOU;So;0;L;;;;;N;;;;;
+1D0EB;BYZANTINE MUSICAL SYMBOL ARKTIKO GA;So;0;L;;;;;N;;;;;
+1D0EC;BYZANTINE MUSICAL SYMBOL ARKTIKO DI;So;0;L;;;;;N;;;;;
+1D0ED;BYZANTINE MUSICAL SYMBOL ARKTIKO KE;So;0;L;;;;;N;;;;;
+1D0EE;BYZANTINE MUSICAL SYMBOL ARKTIKO ZO;So;0;L;;;;;N;;;;;
+1D0EF;BYZANTINE MUSICAL SYMBOL ARKTIKO NI;So;0;L;;;;;N;;;;;
+1D0F0;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO MESO;So;0;L;;;;;N;;;;;
+1D0F1;BYZANTINE MUSICAL SYMBOL KENTIMA NEO MESO;So;0;L;;;;;N;;;;;
+1D0F2;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO KATO;So;0;L;;;;;N;;;;;
+1D0F3;BYZANTINE MUSICAL SYMBOL KENTIMA NEO KATO;So;0;L;;;;;N;;;;;
+1D0F4;BYZANTINE MUSICAL SYMBOL KLASMA KATO;So;0;L;;;;;N;;;;;
+1D0F5;BYZANTINE MUSICAL SYMBOL GORGON NEO KATO;So;0;L;;;;;N;;;;;
+1D100;MUSICAL SYMBOL SINGLE BARLINE;So;0;L;;;;;N;;;;;
+1D101;MUSICAL SYMBOL DOUBLE BARLINE;So;0;L;;;;;N;;;;;
+1D102;MUSICAL SYMBOL FINAL BARLINE;So;0;L;;;;;N;;;;;
+1D103;MUSICAL SYMBOL REVERSE FINAL BARLINE;So;0;L;;;;;N;;;;;
+1D104;MUSICAL SYMBOL DASHED BARLINE;So;0;L;;;;;N;;;;;
+1D105;MUSICAL SYMBOL SHORT BARLINE;So;0;L;;;;;N;;;;;
+1D106;MUSICAL SYMBOL LEFT REPEAT SIGN;So;0;L;;;;;N;;;;;
+1D107;MUSICAL SYMBOL RIGHT REPEAT SIGN;So;0;L;;;;;N;;;;;
+1D108;MUSICAL SYMBOL REPEAT DOTS;So;0;L;;;;;N;;;;;
+1D109;MUSICAL SYMBOL DAL SEGNO;So;0;L;;;;;N;;;;;
+1D10A;MUSICAL SYMBOL DA CAPO;So;0;L;;;;;N;;;;;
+1D10B;MUSICAL SYMBOL SEGNO;So;0;L;;;;;N;;;;;
+1D10C;MUSICAL SYMBOL CODA;So;0;L;;;;;N;;;;;
+1D10D;MUSICAL SYMBOL REPEATED FIGURE-1;So;0;L;;;;;N;;;;;
+1D10E;MUSICAL SYMBOL REPEATED FIGURE-2;So;0;L;;;;;N;;;;;
+1D10F;MUSICAL SYMBOL REPEATED FIGURE-3;So;0;L;;;;;N;;;;;
+1D110;MUSICAL SYMBOL FERMATA;So;0;L;;;;;N;;;;;
+1D111;MUSICAL SYMBOL FERMATA BELOW;So;0;L;;;;;N;;;;;
+1D112;MUSICAL SYMBOL BREATH MARK;So;0;L;;;;;N;;;;;
+1D113;MUSICAL SYMBOL CAESURA;So;0;L;;;;;N;;;;;
+1D114;MUSICAL SYMBOL BRACE;So;0;L;;;;;N;;;;;
+1D115;MUSICAL SYMBOL BRACKET;So;0;L;;;;;N;;;;;
+1D116;MUSICAL SYMBOL ONE-LINE STAFF;So;0;L;;;;;N;;;;;
+1D117;MUSICAL SYMBOL TWO-LINE STAFF;So;0;L;;;;;N;;;;;
+1D118;MUSICAL SYMBOL THREE-LINE STAFF;So;0;L;;;;;N;;;;;
+1D119;MUSICAL SYMBOL FOUR-LINE STAFF;So;0;L;;;;;N;;;;;
+1D11A;MUSICAL SYMBOL FIVE-LINE STAFF;So;0;L;;;;;N;;;;;
+1D11B;MUSICAL SYMBOL SIX-LINE STAFF;So;0;L;;;;;N;;;;;
+1D11C;MUSICAL SYMBOL SIX-STRING FRETBOARD;So;0;L;;;;;N;;;;;
+1D11D;MUSICAL SYMBOL FOUR-STRING FRETBOARD;So;0;L;;;;;N;;;;;
+1D11E;MUSICAL SYMBOL G CLEF;So;0;L;;;;;N;;;;;
+1D11F;MUSICAL SYMBOL G CLEF OTTAVA ALTA;So;0;L;;;;;N;;;;;
+1D120;MUSICAL SYMBOL G CLEF OTTAVA BASSA;So;0;L;;;;;N;;;;;
+1D121;MUSICAL SYMBOL C CLEF;So;0;L;;;;;N;;;;;
+1D122;MUSICAL SYMBOL F CLEF;So;0;L;;;;;N;;;;;
+1D123;MUSICAL SYMBOL F CLEF OTTAVA ALTA;So;0;L;;;;;N;;;;;
+1D124;MUSICAL SYMBOL F CLEF OTTAVA BASSA;So;0;L;;;;;N;;;;;
+1D125;MUSICAL SYMBOL DRUM CLEF-1;So;0;L;;;;;N;;;;;
+1D126;MUSICAL SYMBOL DRUM CLEF-2;So;0;L;;;;;N;;;;;
+1D129;MUSICAL SYMBOL MULTIPLE MEASURE REST;So;0;L;;;;;N;;;;;
+1D12A;MUSICAL SYMBOL DOUBLE SHARP;So;0;L;;;;;N;;;;;
+1D12B;MUSICAL SYMBOL DOUBLE FLAT;So;0;L;;;;;N;;;;;
+1D12C;MUSICAL SYMBOL FLAT UP;So;0;L;;;;;N;;;;;
+1D12D;MUSICAL SYMBOL FLAT DOWN;So;0;L;;;;;N;;;;;
+1D12E;MUSICAL SYMBOL NATURAL UP;So;0;L;;;;;N;;;;;
+1D12F;MUSICAL SYMBOL NATURAL DOWN;So;0;L;;;;;N;;;;;
+1D130;MUSICAL SYMBOL SHARP UP;So;0;L;;;;;N;;;;;
+1D131;MUSICAL SYMBOL SHARP DOWN;So;0;L;;;;;N;;;;;
+1D132;MUSICAL SYMBOL QUARTER TONE SHARP;So;0;L;;;;;N;;;;;
+1D133;MUSICAL SYMBOL QUARTER TONE FLAT;So;0;L;;;;;N;;;;;
+1D134;MUSICAL SYMBOL COMMON TIME;So;0;L;;;;;N;;;;;
+1D135;MUSICAL SYMBOL CUT TIME;So;0;L;;;;;N;;;;;
+1D136;MUSICAL SYMBOL OTTAVA ALTA;So;0;L;;;;;N;;;;;
+1D137;MUSICAL SYMBOL OTTAVA BASSA;So;0;L;;;;;N;;;;;
+1D138;MUSICAL SYMBOL QUINDICESIMA ALTA;So;0;L;;;;;N;;;;;
+1D139;MUSICAL SYMBOL QUINDICESIMA BASSA;So;0;L;;;;;N;;;;;
+1D13A;MUSICAL SYMBOL MULTI REST;So;0;L;;;;;N;;;;;
+1D13B;MUSICAL SYMBOL WHOLE REST;So;0;L;;;;;N;;;;;
+1D13C;MUSICAL SYMBOL HALF REST;So;0;L;;;;;N;;;;;
+1D13D;MUSICAL SYMBOL QUARTER REST;So;0;L;;;;;N;;;;;
+1D13E;MUSICAL SYMBOL EIGHTH REST;So;0;L;;;;;N;;;;;
+1D13F;MUSICAL SYMBOL SIXTEENTH REST;So;0;L;;;;;N;;;;;
+1D140;MUSICAL SYMBOL THIRTY-SECOND REST;So;0;L;;;;;N;;;;;
+1D141;MUSICAL SYMBOL SIXTY-FOURTH REST;So;0;L;;;;;N;;;;;
+1D142;MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH REST;So;0;L;;;;;N;;;;;
+1D143;MUSICAL SYMBOL X NOTEHEAD;So;0;L;;;;;N;;;;;
+1D144;MUSICAL SYMBOL PLUS NOTEHEAD;So;0;L;;;;;N;;;;;
+1D145;MUSICAL SYMBOL CIRCLE X NOTEHEAD;So;0;L;;;;;N;;;;;
+1D146;MUSICAL SYMBOL SQUARE NOTEHEAD WHITE;So;0;L;;;;;N;;;;;
+1D147;MUSICAL SYMBOL SQUARE NOTEHEAD BLACK;So;0;L;;;;;N;;;;;
+1D148;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP WHITE;So;0;L;;;;;N;;;;;
+1D149;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP BLACK;So;0;L;;;;;N;;;;;
+1D14A;MUSICAL SYMBOL TRIANGLE NOTEHEAD LEFT WHITE;So;0;L;;;;;N;;;;;
+1D14B;MUSICAL SYMBOL TRIANGLE NOTEHEAD LEFT BLACK;So;0;L;;;;;N;;;;;
+1D14C;MUSICAL SYMBOL TRIANGLE NOTEHEAD RIGHT WHITE;So;0;L;;;;;N;;;;;
+1D14D;MUSICAL SYMBOL TRIANGLE NOTEHEAD RIGHT BLACK;So;0;L;;;;;N;;;;;
+1D14E;MUSICAL SYMBOL TRIANGLE NOTEHEAD DOWN WHITE;So;0;L;;;;;N;;;;;
+1D14F;MUSICAL SYMBOL TRIANGLE NOTEHEAD DOWN BLACK;So;0;L;;;;;N;;;;;
+1D150;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP RIGHT WHITE;So;0;L;;;;;N;;;;;
+1D151;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP RIGHT BLACK;So;0;L;;;;;N;;;;;
+1D152;MUSICAL SYMBOL MOON NOTEHEAD WHITE;So;0;L;;;;;N;;;;;
+1D153;MUSICAL SYMBOL MOON NOTEHEAD BLACK;So;0;L;;;;;N;;;;;
+1D154;MUSICAL SYMBOL TRIANGLE-ROUND NOTEHEAD DOWN WHITE;So;0;L;;;;;N;;;;;
+1D155;MUSICAL SYMBOL TRIANGLE-ROUND NOTEHEAD DOWN BLACK;So;0;L;;;;;N;;;;;
+1D156;MUSICAL SYMBOL PARENTHESIS NOTEHEAD;So;0;L;;;;;N;;;;;
+1D157;MUSICAL SYMBOL VOID NOTEHEAD;So;0;L;;;;;N;;;;;
+1D158;MUSICAL SYMBOL NOTEHEAD BLACK;So;0;L;;;;;N;;;;;
+1D159;MUSICAL SYMBOL NULL NOTEHEAD;So;0;L;;;;;N;;;;;
+1D15A;MUSICAL SYMBOL CLUSTER NOTEHEAD WHITE;So;0;L;;;;;N;;;;;
+1D15B;MUSICAL SYMBOL CLUSTER NOTEHEAD BLACK;So;0;L;;;;;N;;;;;
+1D15C;MUSICAL SYMBOL BREVE;So;0;L;;;;;N;;;;;
+1D15D;MUSICAL SYMBOL WHOLE NOTE;So;0;L;;;;;N;;;;;
+1D15E;MUSICAL SYMBOL HALF NOTE;So;0;L;1D157 1D165;;;;N;;;;;
+1D15F;MUSICAL SYMBOL QUARTER NOTE;So;0;L;1D158 1D165;;;;N;;;;;
+1D160;MUSICAL SYMBOL EIGHTH NOTE;So;0;L;1D15F 1D16E;;;;N;;;;;
+1D161;MUSICAL SYMBOL SIXTEENTH NOTE;So;0;L;1D15F 1D16F;;;;N;;;;;
+1D162;MUSICAL SYMBOL THIRTY-SECOND NOTE;So;0;L;1D15F 1D170;;;;N;;;;;
+1D163;MUSICAL SYMBOL SIXTY-FOURTH NOTE;So;0;L;1D15F 1D171;;;;N;;;;;
+1D164;MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE;So;0;L;1D15F 1D172;;;;N;;;;;
+1D165;MUSICAL SYMBOL COMBINING STEM;Mc;216;L;;;;;N;;;;;
+1D166;MUSICAL SYMBOL COMBINING SPRECHGESANG STEM;Mc;216;L;;;;;N;;;;;
+1D167;MUSICAL SYMBOL COMBINING TREMOLO-1;Mn;1;NSM;;;;;N;;;;;
+1D168;MUSICAL SYMBOL COMBINING TREMOLO-2;Mn;1;NSM;;;;;N;;;;;
+1D169;MUSICAL SYMBOL COMBINING TREMOLO-3;Mn;1;NSM;;;;;N;;;;;
+1D16A;MUSICAL SYMBOL FINGERED TREMOLO-1;So;0;L;;;;;N;;;;;
+1D16B;MUSICAL SYMBOL FINGERED TREMOLO-2;So;0;L;;;;;N;;;;;
+1D16C;MUSICAL SYMBOL FINGERED TREMOLO-3;So;0;L;;;;;N;;;;;
+1D16D;MUSICAL SYMBOL COMBINING AUGMENTATION DOT;Mc;226;L;;;;;N;;;;;
+1D16E;MUSICAL SYMBOL COMBINING FLAG-1;Mc;216;L;;;;;N;;;;;
+1D16F;MUSICAL SYMBOL COMBINING FLAG-2;Mc;216;L;;;;;N;;;;;
+1D170;MUSICAL SYMBOL COMBINING FLAG-3;Mc;216;L;;;;;N;;;;;
+1D171;MUSICAL SYMBOL COMBINING FLAG-4;Mc;216;L;;;;;N;;;;;
+1D172;MUSICAL SYMBOL COMBINING FLAG-5;Mc;216;L;;;;;N;;;;;
+1D173;MUSICAL SYMBOL BEGIN BEAM;Cf;0;BN;;;;;N;;;;;
+1D174;MUSICAL SYMBOL END BEAM;Cf;0;BN;;;;;N;;;;;
+1D175;MUSICAL SYMBOL BEGIN TIE;Cf;0;BN;;;;;N;;;;;
+1D176;MUSICAL SYMBOL END TIE;Cf;0;BN;;;;;N;;;;;
+1D177;MUSICAL SYMBOL BEGIN SLUR;Cf;0;BN;;;;;N;;;;;
+1D178;MUSICAL SYMBOL END SLUR;Cf;0;BN;;;;;N;;;;;
+1D179;MUSICAL SYMBOL BEGIN PHRASE;Cf;0;BN;;;;;N;;;;;
+1D17A;MUSICAL SYMBOL END PHRASE;Cf;0;BN;;;;;N;;;;;
+1D17B;MUSICAL SYMBOL COMBINING ACCENT;Mn;220;NSM;;;;;N;;;;;
+1D17C;MUSICAL SYMBOL COMBINING STACCATO;Mn;220;NSM;;;;;N;;;;;
+1D17D;MUSICAL SYMBOL COMBINING TENUTO;Mn;220;NSM;;;;;N;;;;;
+1D17E;MUSICAL SYMBOL COMBINING STACCATISSIMO;Mn;220;NSM;;;;;N;;;;;
+1D17F;MUSICAL SYMBOL COMBINING MARCATO;Mn;220;NSM;;;;;N;;;;;
+1D180;MUSICAL SYMBOL COMBINING MARCATO-STACCATO;Mn;220;NSM;;;;;N;;;;;
+1D181;MUSICAL SYMBOL COMBINING ACCENT-STACCATO;Mn;220;NSM;;;;;N;;;;;
+1D182;MUSICAL SYMBOL COMBINING LOURE;Mn;220;NSM;;;;;N;;;;;
+1D183;MUSICAL SYMBOL ARPEGGIATO UP;So;0;L;;;;;N;;;;;
+1D184;MUSICAL SYMBOL ARPEGGIATO DOWN;So;0;L;;;;;N;;;;;
+1D185;MUSICAL SYMBOL COMBINING DOIT;Mn;230;NSM;;;;;N;;;;;
+1D186;MUSICAL SYMBOL COMBINING RIP;Mn;230;NSM;;;;;N;;;;;
+1D187;MUSICAL SYMBOL COMBINING FLIP;Mn;230;NSM;;;;;N;;;;;
+1D188;MUSICAL SYMBOL COMBINING SMEAR;Mn;230;NSM;;;;;N;;;;;
+1D189;MUSICAL SYMBOL COMBINING BEND;Mn;230;NSM;;;;;N;;;;;
+1D18A;MUSICAL SYMBOL COMBINING DOUBLE TONGUE;Mn;220;NSM;;;;;N;;;;;
+1D18B;MUSICAL SYMBOL COMBINING TRIPLE TONGUE;Mn;220;NSM;;;;;N;;;;;
+1D18C;MUSICAL SYMBOL RINFORZANDO;So;0;L;;;;;N;;;;;
+1D18D;MUSICAL SYMBOL SUBITO;So;0;L;;;;;N;;;;;
+1D18E;MUSICAL SYMBOL Z;So;0;L;;;;;N;;;;;
+1D18F;MUSICAL SYMBOL PIANO;So;0;L;;;;;N;;;;;
+1D190;MUSICAL SYMBOL MEZZO;So;0;L;;;;;N;;;;;
+1D191;MUSICAL SYMBOL FORTE;So;0;L;;;;;N;;;;;
+1D192;MUSICAL SYMBOL CRESCENDO;So;0;L;;;;;N;;;;;
+1D193;MUSICAL SYMBOL DECRESCENDO;So;0;L;;;;;N;;;;;
+1D194;MUSICAL SYMBOL GRACE NOTE SLASH;So;0;L;;;;;N;;;;;
+1D195;MUSICAL SYMBOL GRACE NOTE NO SLASH;So;0;L;;;;;N;;;;;
+1D196;MUSICAL SYMBOL TR;So;0;L;;;;;N;;;;;
+1D197;MUSICAL SYMBOL TURN;So;0;L;;;;;N;;;;;
+1D198;MUSICAL SYMBOL INVERTED TURN;So;0;L;;;;;N;;;;;
+1D199;MUSICAL SYMBOL TURN SLASH;So;0;L;;;;;N;;;;;
+1D19A;MUSICAL SYMBOL TURN UP;So;0;L;;;;;N;;;;;
+1D19B;MUSICAL SYMBOL ORNAMENT STROKE-1;So;0;L;;;;;N;;;;;
+1D19C;MUSICAL SYMBOL ORNAMENT STROKE-2;So;0;L;;;;;N;;;;;
+1D19D;MUSICAL SYMBOL ORNAMENT STROKE-3;So;0;L;;;;;N;;;;;
+1D19E;MUSICAL SYMBOL ORNAMENT STROKE-4;So;0;L;;;;;N;;;;;
+1D19F;MUSICAL SYMBOL ORNAMENT STROKE-5;So;0;L;;;;;N;;;;;
+1D1A0;MUSICAL SYMBOL ORNAMENT STROKE-6;So;0;L;;;;;N;;;;;
+1D1A1;MUSICAL SYMBOL ORNAMENT STROKE-7;So;0;L;;;;;N;;;;;
+1D1A2;MUSICAL SYMBOL ORNAMENT STROKE-8;So;0;L;;;;;N;;;;;
+1D1A3;MUSICAL SYMBOL ORNAMENT STROKE-9;So;0;L;;;;;N;;;;;
+1D1A4;MUSICAL SYMBOL ORNAMENT STROKE-10;So;0;L;;;;;N;;;;;
+1D1A5;MUSICAL SYMBOL ORNAMENT STROKE-11;So;0;L;;;;;N;;;;;
+1D1A6;MUSICAL SYMBOL HAUPTSTIMME;So;0;L;;;;;N;;;;;
+1D1A7;MUSICAL SYMBOL NEBENSTIMME;So;0;L;;;;;N;;;;;
+1D1A8;MUSICAL SYMBOL END OF STIMME;So;0;L;;;;;N;;;;;
+1D1A9;MUSICAL SYMBOL DEGREE SLASH;So;0;L;;;;;N;;;;;
+1D1AA;MUSICAL SYMBOL COMBINING DOWN BOW;Mn;230;NSM;;;;;N;;;;;
+1D1AB;MUSICAL SYMBOL COMBINING UP BOW;Mn;230;NSM;;;;;N;;;;;
+1D1AC;MUSICAL SYMBOL COMBINING HARMONIC;Mn;230;NSM;;;;;N;;;;;
+1D1AD;MUSICAL SYMBOL COMBINING SNAP PIZZICATO;Mn;230;NSM;;;;;N;;;;;
+1D1AE;MUSICAL SYMBOL PEDAL MARK;So;0;L;;;;;N;;;;;
+1D1AF;MUSICAL SYMBOL PEDAL UP MARK;So;0;L;;;;;N;;;;;
+1D1B0;MUSICAL SYMBOL HALF PEDAL MARK;So;0;L;;;;;N;;;;;
+1D1B1;MUSICAL SYMBOL GLISSANDO UP;So;0;L;;;;;N;;;;;
+1D1B2;MUSICAL SYMBOL GLISSANDO DOWN;So;0;L;;;;;N;;;;;
+1D1B3;MUSICAL SYMBOL WITH FINGERNAILS;So;0;L;;;;;N;;;;;
+1D1B4;MUSICAL SYMBOL DAMP;So;0;L;;;;;N;;;;;
+1D1B5;MUSICAL SYMBOL DAMP ALL;So;0;L;;;;;N;;;;;
+1D1B6;MUSICAL SYMBOL MAXIMA;So;0;L;;;;;N;;;;;
+1D1B7;MUSICAL SYMBOL LONGA;So;0;L;;;;;N;;;;;
+1D1B8;MUSICAL SYMBOL BREVIS;So;0;L;;;;;N;;;;;
+1D1B9;MUSICAL SYMBOL SEMIBREVIS WHITE;So;0;L;;;;;N;;;;;
+1D1BA;MUSICAL SYMBOL SEMIBREVIS BLACK;So;0;L;;;;;N;;;;;
+1D1BB;MUSICAL SYMBOL MINIMA;So;0;L;1D1B9 1D165;;;;N;;;;;
+1D1BC;MUSICAL SYMBOL MINIMA BLACK;So;0;L;1D1BA 1D165;;;;N;;;;;
+1D1BD;MUSICAL SYMBOL SEMIMINIMA WHITE;So;0;L;1D1BB 1D16E;;;;N;;;;;
+1D1BE;MUSICAL SYMBOL SEMIMINIMA BLACK;So;0;L;1D1BC 1D16E;;;;N;;;;;
+1D1BF;MUSICAL SYMBOL FUSA WHITE;So;0;L;1D1BB 1D16F;;;;N;;;;;
+1D1C0;MUSICAL SYMBOL FUSA BLACK;So;0;L;1D1BC 1D16F;;;;N;;;;;
+1D1C1;MUSICAL SYMBOL LONGA PERFECTA REST;So;0;L;;;;;N;;;;;
+1D1C2;MUSICAL SYMBOL LONGA IMPERFECTA REST;So;0;L;;;;;N;;;;;
+1D1C3;MUSICAL SYMBOL BREVIS REST;So;0;L;;;;;N;;;;;
+1D1C4;MUSICAL SYMBOL SEMIBREVIS REST;So;0;L;;;;;N;;;;;
+1D1C5;MUSICAL SYMBOL MINIMA REST;So;0;L;;;;;N;;;;;
+1D1C6;MUSICAL SYMBOL SEMIMINIMA REST;So;0;L;;;;;N;;;;;
+1D1C7;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE PERFECTA;So;0;L;;;;;N;;;;;
+1D1C8;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE IMPERFECTA;So;0;L;;;;;N;;;;;
+1D1C9;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE PERFECTA DIMINUTION-1;So;0;L;;;;;N;;;;;
+1D1CA;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE PERFECTA;So;0;L;;;;;N;;;;;
+1D1CB;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA;So;0;L;;;;;N;;;;;
+1D1CC;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-1;So;0;L;;;;;N;;;;;
+1D1CD;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-2;So;0;L;;;;;N;;;;;
+1D1CE;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-3;So;0;L;;;;;N;;;;;
+1D1CF;MUSICAL SYMBOL CROIX;So;0;L;;;;;N;;;;;
+1D1D0;MUSICAL SYMBOL GREGORIAN C CLEF;So;0;L;;;;;N;;;;;
+1D1D1;MUSICAL SYMBOL GREGORIAN F CLEF;So;0;L;;;;;N;;;;;
+1D1D2;MUSICAL SYMBOL SQUARE B;So;0;L;;;;;N;;;;;
+1D1D3;MUSICAL SYMBOL VIRGA;So;0;L;;;;;N;;;;;
+1D1D4;MUSICAL SYMBOL PODATUS;So;0;L;;;;;N;;;;;
+1D1D5;MUSICAL SYMBOL CLIVIS;So;0;L;;;;;N;;;;;
+1D1D6;MUSICAL SYMBOL SCANDICUS;So;0;L;;;;;N;;;;;
+1D1D7;MUSICAL SYMBOL CLIMACUS;So;0;L;;;;;N;;;;;
+1D1D8;MUSICAL SYMBOL TORCULUS;So;0;L;;;;;N;;;;;
+1D1D9;MUSICAL SYMBOL PORRECTUS;So;0;L;;;;;N;;;;;
+1D1DA;MUSICAL SYMBOL PORRECTUS FLEXUS;So;0;L;;;;;N;;;;;
+1D1DB;MUSICAL SYMBOL SCANDICUS FLEXUS;So;0;L;;;;;N;;;;;
+1D1DC;MUSICAL SYMBOL TORCULUS RESUPINUS;So;0;L;;;;;N;;;;;
+1D1DD;MUSICAL SYMBOL PES SUBPUNCTIS;So;0;L;;;;;N;;;;;
+1D1DE;MUSICAL SYMBOL KIEVAN C CLEF;So;0;L;;;;;N;;;;;
+1D1DF;MUSICAL SYMBOL KIEVAN END OF PIECE;So;0;L;;;;;N;;;;;
+1D1E0;MUSICAL SYMBOL KIEVAN FINAL NOTE;So;0;L;;;;;N;;;;;
+1D1E1;MUSICAL SYMBOL KIEVAN RECITATIVE MARK;So;0;L;;;;;N;;;;;
+1D1E2;MUSICAL SYMBOL KIEVAN WHOLE NOTE;So;0;L;;;;;N;;;;;
+1D1E3;MUSICAL SYMBOL KIEVAN HALF NOTE;So;0;L;;;;;N;;;;;
+1D1E4;MUSICAL SYMBOL KIEVAN QUARTER NOTE STEM DOWN;So;0;L;;;;;N;;;;;
+1D1E5;MUSICAL SYMBOL KIEVAN QUARTER NOTE STEM UP;So;0;L;;;;;N;;;;;
+1D1E6;MUSICAL SYMBOL KIEVAN EIGHTH NOTE STEM DOWN;So;0;L;;;;;N;;;;;
+1D1E7;MUSICAL SYMBOL KIEVAN EIGHTH NOTE STEM UP;So;0;L;;;;;N;;;;;
+1D1E8;MUSICAL SYMBOL KIEVAN FLAT SIGN;So;0;L;;;;;N;;;;;
+1D200;GREEK VOCAL NOTATION SYMBOL-1;So;0;ON;;;;;N;;;;;
+1D201;GREEK VOCAL NOTATION SYMBOL-2;So;0;ON;;;;;N;;;;;
+1D202;GREEK VOCAL NOTATION SYMBOL-3;So;0;ON;;;;;N;;;;;
+1D203;GREEK VOCAL NOTATION SYMBOL-4;So;0;ON;;;;;N;;;;;
+1D204;GREEK VOCAL NOTATION SYMBOL-5;So;0;ON;;;;;N;;;;;
+1D205;GREEK VOCAL NOTATION SYMBOL-6;So;0;ON;;;;;N;;;;;
+1D206;GREEK VOCAL NOTATION SYMBOL-7;So;0;ON;;;;;N;;;;;
+1D207;GREEK VOCAL NOTATION SYMBOL-8;So;0;ON;;;;;N;;;;;
+1D208;GREEK VOCAL NOTATION SYMBOL-9;So;0;ON;;;;;N;;;;;
+1D209;GREEK VOCAL NOTATION SYMBOL-10;So;0;ON;;;;;N;;;;;
+1D20A;GREEK VOCAL NOTATION SYMBOL-11;So;0;ON;;;;;N;;;;;
+1D20B;GREEK VOCAL NOTATION SYMBOL-12;So;0;ON;;;;;N;;;;;
+1D20C;GREEK VOCAL NOTATION SYMBOL-13;So;0;ON;;;;;N;;;;;
+1D20D;GREEK VOCAL NOTATION SYMBOL-14;So;0;ON;;;;;N;;;;;
+1D20E;GREEK VOCAL NOTATION SYMBOL-15;So;0;ON;;;;;N;;;;;
+1D20F;GREEK VOCAL NOTATION SYMBOL-16;So;0;ON;;;;;N;;;;;
+1D210;GREEK VOCAL NOTATION SYMBOL-17;So;0;ON;;;;;N;;;;;
+1D211;GREEK VOCAL NOTATION SYMBOL-18;So;0;ON;;;;;N;;;;;
+1D212;GREEK VOCAL NOTATION SYMBOL-19;So;0;ON;;;;;N;;;;;
+1D213;GREEK VOCAL NOTATION SYMBOL-20;So;0;ON;;;;;N;;;;;
+1D214;GREEK VOCAL NOTATION SYMBOL-21;So;0;ON;;;;;N;;;;;
+1D215;GREEK VOCAL NOTATION SYMBOL-22;So;0;ON;;;;;N;;;;;
+1D216;GREEK VOCAL NOTATION SYMBOL-23;So;0;ON;;;;;N;;;;;
+1D217;GREEK VOCAL NOTATION SYMBOL-24;So;0;ON;;;;;N;;;;;
+1D218;GREEK VOCAL NOTATION SYMBOL-50;So;0;ON;;;;;N;;;;;
+1D219;GREEK VOCAL NOTATION SYMBOL-51;So;0;ON;;;;;N;;;;;
+1D21A;GREEK VOCAL NOTATION SYMBOL-52;So;0;ON;;;;;N;;;;;
+1D21B;GREEK VOCAL NOTATION SYMBOL-53;So;0;ON;;;;;N;;;;;
+1D21C;GREEK VOCAL NOTATION SYMBOL-54;So;0;ON;;;;;N;;;;;
+1D21D;GREEK INSTRUMENTAL NOTATION SYMBOL-1;So;0;ON;;;;;N;;;;;
+1D21E;GREEK INSTRUMENTAL NOTATION SYMBOL-2;So;0;ON;;;;;N;;;;;
+1D21F;GREEK INSTRUMENTAL NOTATION SYMBOL-4;So;0;ON;;;;;N;;;;;
+1D220;GREEK INSTRUMENTAL NOTATION SYMBOL-5;So;0;ON;;;;;N;;;;;
+1D221;GREEK INSTRUMENTAL NOTATION SYMBOL-7;So;0;ON;;;;;N;;;;;
+1D222;GREEK INSTRUMENTAL NOTATION SYMBOL-8;So;0;ON;;;;;N;;;;;
+1D223;GREEK INSTRUMENTAL NOTATION SYMBOL-11;So;0;ON;;;;;N;;;;;
+1D224;GREEK INSTRUMENTAL NOTATION SYMBOL-12;So;0;ON;;;;;N;;;;;
+1D225;GREEK INSTRUMENTAL NOTATION SYMBOL-13;So;0;ON;;;;;N;;;;;
+1D226;GREEK INSTRUMENTAL NOTATION SYMBOL-14;So;0;ON;;;;;N;;;;;
+1D227;GREEK INSTRUMENTAL NOTATION SYMBOL-17;So;0;ON;;;;;N;;;;;
+1D228;GREEK INSTRUMENTAL NOTATION SYMBOL-18;So;0;ON;;;;;N;;;;;
+1D229;GREEK INSTRUMENTAL NOTATION SYMBOL-19;So;0;ON;;;;;N;;;;;
+1D22A;GREEK INSTRUMENTAL NOTATION SYMBOL-23;So;0;ON;;;;;N;;;;;
+1D22B;GREEK INSTRUMENTAL NOTATION SYMBOL-24;So;0;ON;;;;;N;;;;;
+1D22C;GREEK INSTRUMENTAL NOTATION SYMBOL-25;So;0;ON;;;;;N;;;;;
+1D22D;GREEK INSTRUMENTAL NOTATION SYMBOL-26;So;0;ON;;;;;N;;;;;
+1D22E;GREEK INSTRUMENTAL NOTATION SYMBOL-27;So;0;ON;;;;;N;;;;;
+1D22F;GREEK INSTRUMENTAL NOTATION SYMBOL-29;So;0;ON;;;;;N;;;;;
+1D230;GREEK INSTRUMENTAL NOTATION SYMBOL-30;So;0;ON;;;;;N;;;;;
+1D231;GREEK INSTRUMENTAL NOTATION SYMBOL-32;So;0;ON;;;;;N;;;;;
+1D232;GREEK INSTRUMENTAL NOTATION SYMBOL-36;So;0;ON;;;;;N;;;;;
+1D233;GREEK INSTRUMENTAL NOTATION SYMBOL-37;So;0;ON;;;;;N;;;;;
+1D234;GREEK INSTRUMENTAL NOTATION SYMBOL-38;So;0;ON;;;;;N;;;;;
+1D235;GREEK INSTRUMENTAL NOTATION SYMBOL-39;So;0;ON;;;;;N;;;;;
+1D236;GREEK INSTRUMENTAL NOTATION SYMBOL-40;So;0;ON;;;;;N;;;;;
+1D237;GREEK INSTRUMENTAL NOTATION SYMBOL-42;So;0;ON;;;;;N;;;;;
+1D238;GREEK INSTRUMENTAL NOTATION SYMBOL-43;So;0;ON;;;;;N;;;;;
+1D239;GREEK INSTRUMENTAL NOTATION SYMBOL-45;So;0;ON;;;;;N;;;;;
+1D23A;GREEK INSTRUMENTAL NOTATION SYMBOL-47;So;0;ON;;;;;N;;;;;
+1D23B;GREEK INSTRUMENTAL NOTATION SYMBOL-48;So;0;ON;;;;;N;;;;;
+1D23C;GREEK INSTRUMENTAL NOTATION SYMBOL-49;So;0;ON;;;;;N;;;;;
+1D23D;GREEK INSTRUMENTAL NOTATION SYMBOL-50;So;0;ON;;;;;N;;;;;
+1D23E;GREEK INSTRUMENTAL NOTATION SYMBOL-51;So;0;ON;;;;;N;;;;;
+1D23F;GREEK INSTRUMENTAL NOTATION SYMBOL-52;So;0;ON;;;;;N;;;;;
+1D240;GREEK INSTRUMENTAL NOTATION SYMBOL-53;So;0;ON;;;;;N;;;;;
+1D241;GREEK INSTRUMENTAL NOTATION SYMBOL-54;So;0;ON;;;;;N;;;;;
+1D242;COMBINING GREEK MUSICAL TRISEME;Mn;230;NSM;;;;;N;;;;;
+1D243;COMBINING GREEK MUSICAL TETRASEME;Mn;230;NSM;;;;;N;;;;;
+1D244;COMBINING GREEK MUSICAL PENTASEME;Mn;230;NSM;;;;;N;;;;;
+1D245;GREEK MUSICAL LEIMMA;So;0;ON;;;;;N;;;;;
+1D2E0;MAYAN NUMERAL ZERO;No;0;L;;;;0;N;;;;;
+1D2E1;MAYAN NUMERAL ONE;No;0;L;;;;1;N;;;;;
+1D2E2;MAYAN NUMERAL TWO;No;0;L;;;;2;N;;;;;
+1D2E3;MAYAN NUMERAL THREE;No;0;L;;;;3;N;;;;;
+1D2E4;MAYAN NUMERAL FOUR;No;0;L;;;;4;N;;;;;
+1D2E5;MAYAN NUMERAL FIVE;No;0;L;;;;5;N;;;;;
+1D2E6;MAYAN NUMERAL SIX;No;0;L;;;;6;N;;;;;
+1D2E7;MAYAN NUMERAL SEVEN;No;0;L;;;;7;N;;;;;
+1D2E8;MAYAN NUMERAL EIGHT;No;0;L;;;;8;N;;;;;
+1D2E9;MAYAN NUMERAL NINE;No;0;L;;;;9;N;;;;;
+1D2EA;MAYAN NUMERAL TEN;No;0;L;;;;10;N;;;;;
+1D2EB;MAYAN NUMERAL ELEVEN;No;0;L;;;;11;N;;;;;
+1D2EC;MAYAN NUMERAL TWELVE;No;0;L;;;;12;N;;;;;
+1D2ED;MAYAN NUMERAL THIRTEEN;No;0;L;;;;13;N;;;;;
+1D2EE;MAYAN NUMERAL FOURTEEN;No;0;L;;;;14;N;;;;;
+1D2EF;MAYAN NUMERAL FIFTEEN;No;0;L;;;;15;N;;;;;
+1D2F0;MAYAN NUMERAL SIXTEEN;No;0;L;;;;16;N;;;;;
+1D2F1;MAYAN NUMERAL SEVENTEEN;No;0;L;;;;17;N;;;;;
+1D2F2;MAYAN NUMERAL EIGHTEEN;No;0;L;;;;18;N;;;;;
+1D2F3;MAYAN NUMERAL NINETEEN;No;0;L;;;;19;N;;;;;
+1D300;MONOGRAM FOR EARTH;So;0;ON;;;;;N;;;;;
+1D301;DIGRAM FOR HEAVENLY EARTH;So;0;ON;;;;;N;;;;;
+1D302;DIGRAM FOR HUMAN EARTH;So;0;ON;;;;;N;;;;;
+1D303;DIGRAM FOR EARTHLY HEAVEN;So;0;ON;;;;;N;;;;;
+1D304;DIGRAM FOR EARTHLY HUMAN;So;0;ON;;;;;N;;;;;
+1D305;DIGRAM FOR EARTH;So;0;ON;;;;;N;;;;;
+1D306;TETRAGRAM FOR CENTRE;So;0;ON;;;;;N;;;;;
+1D307;TETRAGRAM FOR FULL CIRCLE;So;0;ON;;;;;N;;;;;
+1D308;TETRAGRAM FOR MIRED;So;0;ON;;;;;N;;;;;
+1D309;TETRAGRAM FOR BARRIER;So;0;ON;;;;;N;;;;;
+1D30A;TETRAGRAM FOR KEEPING SMALL;So;0;ON;;;;;N;;;;;
+1D30B;TETRAGRAM FOR CONTRARIETY;So;0;ON;;;;;N;;;;;
+1D30C;TETRAGRAM FOR ASCENT;So;0;ON;;;;;N;;;;;
+1D30D;TETRAGRAM FOR OPPOSITION;So;0;ON;;;;;N;;;;;
+1D30E;TETRAGRAM FOR BRANCHING OUT;So;0;ON;;;;;N;;;;;
+1D30F;TETRAGRAM FOR DEFECTIVENESS OR DISTORTION;So;0;ON;;;;;N;;;;;
+1D310;TETRAGRAM FOR DIVERGENCE;So;0;ON;;;;;N;;;;;
+1D311;TETRAGRAM FOR YOUTHFULNESS;So;0;ON;;;;;N;;;;;
+1D312;TETRAGRAM FOR INCREASE;So;0;ON;;;;;N;;;;;
+1D313;TETRAGRAM FOR PENETRATION;So;0;ON;;;;;N;;;;;
+1D314;TETRAGRAM FOR REACH;So;0;ON;;;;;N;;;;;
+1D315;TETRAGRAM FOR CONTACT;So;0;ON;;;;;N;;;;;
+1D316;TETRAGRAM FOR HOLDING BACK;So;0;ON;;;;;N;;;;;
+1D317;TETRAGRAM FOR WAITING;So;0;ON;;;;;N;;;;;
+1D318;TETRAGRAM FOR FOLLOWING;So;0;ON;;;;;N;;;;;
+1D319;TETRAGRAM FOR ADVANCE;So;0;ON;;;;;N;;;;;
+1D31A;TETRAGRAM FOR RELEASE;So;0;ON;;;;;N;;;;;
+1D31B;TETRAGRAM FOR RESISTANCE;So;0;ON;;;;;N;;;;;
+1D31C;TETRAGRAM FOR EASE;So;0;ON;;;;;N;;;;;
+1D31D;TETRAGRAM FOR JOY;So;0;ON;;;;;N;;;;;
+1D31E;TETRAGRAM FOR CONTENTION;So;0;ON;;;;;N;;;;;
+1D31F;TETRAGRAM FOR ENDEAVOUR;So;0;ON;;;;;N;;;;;
+1D320;TETRAGRAM FOR DUTIES;So;0;ON;;;;;N;;;;;
+1D321;TETRAGRAM FOR CHANGE;So;0;ON;;;;;N;;;;;
+1D322;TETRAGRAM FOR DECISIVENESS;So;0;ON;;;;;N;;;;;
+1D323;TETRAGRAM FOR BOLD RESOLUTION;So;0;ON;;;;;N;;;;;
+1D324;TETRAGRAM FOR PACKING;So;0;ON;;;;;N;;;;;
+1D325;TETRAGRAM FOR LEGION;So;0;ON;;;;;N;;;;;
+1D326;TETRAGRAM FOR CLOSENESS;So;0;ON;;;;;N;;;;;
+1D327;TETRAGRAM FOR KINSHIP;So;0;ON;;;;;N;;;;;
+1D328;TETRAGRAM FOR GATHERING;So;0;ON;;;;;N;;;;;
+1D329;TETRAGRAM FOR STRENGTH;So;0;ON;;;;;N;;;;;
+1D32A;TETRAGRAM FOR PURITY;So;0;ON;;;;;N;;;;;
+1D32B;TETRAGRAM FOR FULLNESS;So;0;ON;;;;;N;;;;;
+1D32C;TETRAGRAM FOR RESIDENCE;So;0;ON;;;;;N;;;;;
+1D32D;TETRAGRAM FOR LAW OR MODEL;So;0;ON;;;;;N;;;;;
+1D32E;TETRAGRAM FOR RESPONSE;So;0;ON;;;;;N;;;;;
+1D32F;TETRAGRAM FOR GOING TO MEET;So;0;ON;;;;;N;;;;;
+1D330;TETRAGRAM FOR ENCOUNTERS;So;0;ON;;;;;N;;;;;
+1D331;TETRAGRAM FOR STOVE;So;0;ON;;;;;N;;;;;
+1D332;TETRAGRAM FOR GREATNESS;So;0;ON;;;;;N;;;;;
+1D333;TETRAGRAM FOR ENLARGEMENT;So;0;ON;;;;;N;;;;;
+1D334;TETRAGRAM FOR PATTERN;So;0;ON;;;;;N;;;;;
+1D335;TETRAGRAM FOR RITUAL;So;0;ON;;;;;N;;;;;
+1D336;TETRAGRAM FOR FLIGHT;So;0;ON;;;;;N;;;;;
+1D337;TETRAGRAM FOR VASTNESS OR WASTING;So;0;ON;;;;;N;;;;;
+1D338;TETRAGRAM FOR CONSTANCY;So;0;ON;;;;;N;;;;;
+1D339;TETRAGRAM FOR MEASURE;So;0;ON;;;;;N;;;;;
+1D33A;TETRAGRAM FOR ETERNITY;So;0;ON;;;;;N;;;;;
+1D33B;TETRAGRAM FOR UNITY;So;0;ON;;;;;N;;;;;
+1D33C;TETRAGRAM FOR DIMINISHMENT;So;0;ON;;;;;N;;;;;
+1D33D;TETRAGRAM FOR CLOSED MOUTH;So;0;ON;;;;;N;;;;;
+1D33E;TETRAGRAM FOR GUARDEDNESS;So;0;ON;;;;;N;;;;;
+1D33F;TETRAGRAM FOR GATHERING IN;So;0;ON;;;;;N;;;;;
+1D340;TETRAGRAM FOR MASSING;So;0;ON;;;;;N;;;;;
+1D341;TETRAGRAM FOR ACCUMULATION;So;0;ON;;;;;N;;;;;
+1D342;TETRAGRAM FOR EMBELLISHMENT;So;0;ON;;;;;N;;;;;
+1D343;TETRAGRAM FOR DOUBT;So;0;ON;;;;;N;;;;;
+1D344;TETRAGRAM FOR WATCH;So;0;ON;;;;;N;;;;;
+1D345;TETRAGRAM FOR SINKING;So;0;ON;;;;;N;;;;;
+1D346;TETRAGRAM FOR INNER;So;0;ON;;;;;N;;;;;
+1D347;TETRAGRAM FOR DEPARTURE;So;0;ON;;;;;N;;;;;
+1D348;TETRAGRAM FOR DARKENING;So;0;ON;;;;;N;;;;;
+1D349;TETRAGRAM FOR DIMMING;So;0;ON;;;;;N;;;;;
+1D34A;TETRAGRAM FOR EXHAUSTION;So;0;ON;;;;;N;;;;;
+1D34B;TETRAGRAM FOR SEVERANCE;So;0;ON;;;;;N;;;;;
+1D34C;TETRAGRAM FOR STOPPAGE;So;0;ON;;;;;N;;;;;
+1D34D;TETRAGRAM FOR HARDNESS;So;0;ON;;;;;N;;;;;
+1D34E;TETRAGRAM FOR COMPLETION;So;0;ON;;;;;N;;;;;
+1D34F;TETRAGRAM FOR CLOSURE;So;0;ON;;;;;N;;;;;
+1D350;TETRAGRAM FOR FAILURE;So;0;ON;;;;;N;;;;;
+1D351;TETRAGRAM FOR AGGRAVATION;So;0;ON;;;;;N;;;;;
+1D352;TETRAGRAM FOR COMPLIANCE;So;0;ON;;;;;N;;;;;
+1D353;TETRAGRAM FOR ON THE VERGE;So;0;ON;;;;;N;;;;;
+1D354;TETRAGRAM FOR DIFFICULTIES;So;0;ON;;;;;N;;;;;
+1D355;TETRAGRAM FOR LABOURING;So;0;ON;;;;;N;;;;;
+1D356;TETRAGRAM FOR FOSTERING;So;0;ON;;;;;N;;;;;
+1D360;COUNTING ROD UNIT DIGIT ONE;No;0;L;;;;1;N;;;;;
+1D361;COUNTING ROD UNIT DIGIT TWO;No;0;L;;;;2;N;;;;;
+1D362;COUNTING ROD UNIT DIGIT THREE;No;0;L;;;;3;N;;;;;
+1D363;COUNTING ROD UNIT DIGIT FOUR;No;0;L;;;;4;N;;;;;
+1D364;COUNTING ROD UNIT DIGIT FIVE;No;0;L;;;;5;N;;;;;
+1D365;COUNTING ROD UNIT DIGIT SIX;No;0;L;;;;6;N;;;;;
+1D366;COUNTING ROD UNIT DIGIT SEVEN;No;0;L;;;;7;N;;;;;
+1D367;COUNTING ROD UNIT DIGIT EIGHT;No;0;L;;;;8;N;;;;;
+1D368;COUNTING ROD UNIT DIGIT NINE;No;0;L;;;;9;N;;;;;
+1D369;COUNTING ROD TENS DIGIT ONE;No;0;L;;;;10;N;;;;;
+1D36A;COUNTING ROD TENS DIGIT TWO;No;0;L;;;;20;N;;;;;
+1D36B;COUNTING ROD TENS DIGIT THREE;No;0;L;;;;30;N;;;;;
+1D36C;COUNTING ROD TENS DIGIT FOUR;No;0;L;;;;40;N;;;;;
+1D36D;COUNTING ROD TENS DIGIT FIVE;No;0;L;;;;50;N;;;;;
+1D36E;COUNTING ROD TENS DIGIT SIX;No;0;L;;;;60;N;;;;;
+1D36F;COUNTING ROD TENS DIGIT SEVEN;No;0;L;;;;70;N;;;;;
+1D370;COUNTING ROD TENS DIGIT EIGHT;No;0;L;;;;80;N;;;;;
+1D371;COUNTING ROD TENS DIGIT NINE;No;0;L;;;;90;N;;;;;
+1D372;IDEOGRAPHIC TALLY MARK ONE;No;0;L;;;;1;N;;;;;
+1D373;IDEOGRAPHIC TALLY MARK TWO;No;0;L;;;;2;N;;;;;
+1D374;IDEOGRAPHIC TALLY MARK THREE;No;0;L;;;;3;N;;;;;
+1D375;IDEOGRAPHIC TALLY MARK FOUR;No;0;L;;;;4;N;;;;;
+1D376;IDEOGRAPHIC TALLY MARK FIVE;No;0;L;;;;5;N;;;;;
+1D377;TALLY MARK ONE;No;0;L;;;;1;N;;;;;
+1D378;TALLY MARK FIVE;No;0;L;;;;5;N;;;;;
+1D400;MATHEMATICAL BOLD CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D401;MATHEMATICAL BOLD CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D402;MATHEMATICAL BOLD CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D403;MATHEMATICAL BOLD CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D404;MATHEMATICAL BOLD CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D405;MATHEMATICAL BOLD CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D406;MATHEMATICAL BOLD CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D407;MATHEMATICAL BOLD CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D408;MATHEMATICAL BOLD CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D409;MATHEMATICAL BOLD CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D40A;MATHEMATICAL BOLD CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D40B;MATHEMATICAL BOLD CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D40C;MATHEMATICAL BOLD CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D40D;MATHEMATICAL BOLD CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D40E;MATHEMATICAL BOLD CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D40F;MATHEMATICAL BOLD CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D410;MATHEMATICAL BOLD CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D411;MATHEMATICAL BOLD CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D412;MATHEMATICAL BOLD CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D413;MATHEMATICAL BOLD CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D414;MATHEMATICAL BOLD CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D415;MATHEMATICAL BOLD CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D416;MATHEMATICAL BOLD CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D417;MATHEMATICAL BOLD CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D418;MATHEMATICAL BOLD CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D419;MATHEMATICAL BOLD CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D41A;MATHEMATICAL BOLD SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D41B;MATHEMATICAL BOLD SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D41C;MATHEMATICAL BOLD SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D41D;MATHEMATICAL BOLD SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D41E;MATHEMATICAL BOLD SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D41F;MATHEMATICAL BOLD SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D420;MATHEMATICAL BOLD SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D421;MATHEMATICAL BOLD SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D422;MATHEMATICAL BOLD SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D423;MATHEMATICAL BOLD SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D424;MATHEMATICAL BOLD SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D425;MATHEMATICAL BOLD SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D426;MATHEMATICAL BOLD SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D427;MATHEMATICAL BOLD SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D428;MATHEMATICAL BOLD SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D429;MATHEMATICAL BOLD SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D42A;MATHEMATICAL BOLD SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D42B;MATHEMATICAL BOLD SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D42C;MATHEMATICAL BOLD SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D42D;MATHEMATICAL BOLD SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D42E;MATHEMATICAL BOLD SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D42F;MATHEMATICAL BOLD SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D430;MATHEMATICAL BOLD SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D431;MATHEMATICAL BOLD SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D432;MATHEMATICAL BOLD SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D433;MATHEMATICAL BOLD SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D434;MATHEMATICAL ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D435;MATHEMATICAL ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D436;MATHEMATICAL ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D437;MATHEMATICAL ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D438;MATHEMATICAL ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D439;MATHEMATICAL ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D43A;MATHEMATICAL ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D43B;MATHEMATICAL ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D43C;MATHEMATICAL ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D43D;MATHEMATICAL ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D43E;MATHEMATICAL ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D43F;MATHEMATICAL ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D440;MATHEMATICAL ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D441;MATHEMATICAL ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D442;MATHEMATICAL ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D443;MATHEMATICAL ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D444;MATHEMATICAL ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D445;MATHEMATICAL ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D446;MATHEMATICAL ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D447;MATHEMATICAL ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D448;MATHEMATICAL ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D449;MATHEMATICAL ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D44A;MATHEMATICAL ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D44B;MATHEMATICAL ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D44C;MATHEMATICAL ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D44D;MATHEMATICAL ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D44E;MATHEMATICAL ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D44F;MATHEMATICAL ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D450;MATHEMATICAL ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D451;MATHEMATICAL ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D452;MATHEMATICAL ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D453;MATHEMATICAL ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D454;MATHEMATICAL ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D456;MATHEMATICAL ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D457;MATHEMATICAL ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D458;MATHEMATICAL ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D459;MATHEMATICAL ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D45A;MATHEMATICAL ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D45B;MATHEMATICAL ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D45C;MATHEMATICAL ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D45D;MATHEMATICAL ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D45E;MATHEMATICAL ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D45F;MATHEMATICAL ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D460;MATHEMATICAL ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D461;MATHEMATICAL ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D462;MATHEMATICAL ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D463;MATHEMATICAL ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D464;MATHEMATICAL ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D465;MATHEMATICAL ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D466;MATHEMATICAL ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D467;MATHEMATICAL ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D468;MATHEMATICAL BOLD ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D469;MATHEMATICAL BOLD ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D46A;MATHEMATICAL BOLD ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D46B;MATHEMATICAL BOLD ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D46C;MATHEMATICAL BOLD ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D46D;MATHEMATICAL BOLD ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D46E;MATHEMATICAL BOLD ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D46F;MATHEMATICAL BOLD ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D470;MATHEMATICAL BOLD ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D471;MATHEMATICAL BOLD ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D472;MATHEMATICAL BOLD ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D473;MATHEMATICAL BOLD ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D474;MATHEMATICAL BOLD ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D475;MATHEMATICAL BOLD ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D476;MATHEMATICAL BOLD ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D477;MATHEMATICAL BOLD ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D478;MATHEMATICAL BOLD ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D479;MATHEMATICAL BOLD ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D47A;MATHEMATICAL BOLD ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D47B;MATHEMATICAL BOLD ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D47C;MATHEMATICAL BOLD ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D47D;MATHEMATICAL BOLD ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D47E;MATHEMATICAL BOLD ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D47F;MATHEMATICAL BOLD ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D480;MATHEMATICAL BOLD ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D481;MATHEMATICAL BOLD ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D482;MATHEMATICAL BOLD ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D483;MATHEMATICAL BOLD ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D484;MATHEMATICAL BOLD ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D485;MATHEMATICAL BOLD ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D486;MATHEMATICAL BOLD ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D487;MATHEMATICAL BOLD ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D488;MATHEMATICAL BOLD ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D489;MATHEMATICAL BOLD ITALIC SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D48A;MATHEMATICAL BOLD ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D48B;MATHEMATICAL BOLD ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D48C;MATHEMATICAL BOLD ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D48D;MATHEMATICAL BOLD ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D48E;MATHEMATICAL BOLD ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D48F;MATHEMATICAL BOLD ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D490;MATHEMATICAL BOLD ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D491;MATHEMATICAL BOLD ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D492;MATHEMATICAL BOLD ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D493;MATHEMATICAL BOLD ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D494;MATHEMATICAL BOLD ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D495;MATHEMATICAL BOLD ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D496;MATHEMATICAL BOLD ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D497;MATHEMATICAL BOLD ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D498;MATHEMATICAL BOLD ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D499;MATHEMATICAL BOLD ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D49A;MATHEMATICAL BOLD ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D49B;MATHEMATICAL BOLD ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D49C;MATHEMATICAL SCRIPT CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D49E;MATHEMATICAL SCRIPT CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D49F;MATHEMATICAL SCRIPT CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D4A2;MATHEMATICAL SCRIPT CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D4A5;MATHEMATICAL SCRIPT CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D4A6;MATHEMATICAL SCRIPT CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D4A9;MATHEMATICAL SCRIPT CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D4AA;MATHEMATICAL SCRIPT CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D4AB;MATHEMATICAL SCRIPT CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D4AC;MATHEMATICAL SCRIPT CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D4AE;MATHEMATICAL SCRIPT CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D4AF;MATHEMATICAL SCRIPT CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D4B0;MATHEMATICAL SCRIPT CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D4B1;MATHEMATICAL SCRIPT CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D4B2;MATHEMATICAL SCRIPT CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D4B3;MATHEMATICAL SCRIPT CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D4B4;MATHEMATICAL SCRIPT CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D4B5;MATHEMATICAL SCRIPT CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D4B6;MATHEMATICAL SCRIPT SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D4B7;MATHEMATICAL SCRIPT SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D4B8;MATHEMATICAL SCRIPT SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D4B9;MATHEMATICAL SCRIPT SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D4BB;MATHEMATICAL SCRIPT SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D4BD;MATHEMATICAL SCRIPT SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D4BE;MATHEMATICAL SCRIPT SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D4BF;MATHEMATICAL SCRIPT SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D4C0;MATHEMATICAL SCRIPT SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D4C1;MATHEMATICAL SCRIPT SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D4C2;MATHEMATICAL SCRIPT SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D4C3;MATHEMATICAL SCRIPT SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D4C5;MATHEMATICAL SCRIPT SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D4C6;MATHEMATICAL SCRIPT SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D4C7;MATHEMATICAL SCRIPT SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D4C8;MATHEMATICAL SCRIPT SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D4C9;MATHEMATICAL SCRIPT SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D4CA;MATHEMATICAL SCRIPT SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D4CB;MATHEMATICAL SCRIPT SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D4CC;MATHEMATICAL SCRIPT SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D4CD;MATHEMATICAL SCRIPT SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D4CE;MATHEMATICAL SCRIPT SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D4CF;MATHEMATICAL SCRIPT SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D4D0;MATHEMATICAL BOLD SCRIPT CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D4D1;MATHEMATICAL BOLD SCRIPT CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D4D2;MATHEMATICAL BOLD SCRIPT CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D4D3;MATHEMATICAL BOLD SCRIPT CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D4D4;MATHEMATICAL BOLD SCRIPT CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D4D5;MATHEMATICAL BOLD SCRIPT CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D4D6;MATHEMATICAL BOLD SCRIPT CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D4D7;MATHEMATICAL BOLD SCRIPT CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D4D8;MATHEMATICAL BOLD SCRIPT CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D4D9;MATHEMATICAL BOLD SCRIPT CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D4DA;MATHEMATICAL BOLD SCRIPT CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D4DB;MATHEMATICAL BOLD SCRIPT CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D4DC;MATHEMATICAL BOLD SCRIPT CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D4DD;MATHEMATICAL BOLD SCRIPT CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D4DE;MATHEMATICAL BOLD SCRIPT CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D4DF;MATHEMATICAL BOLD SCRIPT CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D4E0;MATHEMATICAL BOLD SCRIPT CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D4E1;MATHEMATICAL BOLD SCRIPT CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D4E2;MATHEMATICAL BOLD SCRIPT CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D4E3;MATHEMATICAL BOLD SCRIPT CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D4E4;MATHEMATICAL BOLD SCRIPT CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D4E5;MATHEMATICAL BOLD SCRIPT CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D4E6;MATHEMATICAL BOLD SCRIPT CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D4E7;MATHEMATICAL BOLD SCRIPT CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D4E8;MATHEMATICAL BOLD SCRIPT CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D4E9;MATHEMATICAL BOLD SCRIPT CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D4EA;MATHEMATICAL BOLD SCRIPT SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D4EB;MATHEMATICAL BOLD SCRIPT SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D4EC;MATHEMATICAL BOLD SCRIPT SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D4ED;MATHEMATICAL BOLD SCRIPT SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D4EE;MATHEMATICAL BOLD SCRIPT SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D4EF;MATHEMATICAL BOLD SCRIPT SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D4F0;MATHEMATICAL BOLD SCRIPT SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D4F1;MATHEMATICAL BOLD SCRIPT SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D4F2;MATHEMATICAL BOLD SCRIPT SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D4F3;MATHEMATICAL BOLD SCRIPT SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D4F4;MATHEMATICAL BOLD SCRIPT SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D4F5;MATHEMATICAL BOLD SCRIPT SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D4F6;MATHEMATICAL BOLD SCRIPT SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D4F7;MATHEMATICAL BOLD SCRIPT SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D4F8;MATHEMATICAL BOLD SCRIPT SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D4F9;MATHEMATICAL BOLD SCRIPT SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D4FA;MATHEMATICAL BOLD SCRIPT SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D4FB;MATHEMATICAL BOLD SCRIPT SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D4FC;MATHEMATICAL BOLD SCRIPT SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D4FD;MATHEMATICAL BOLD SCRIPT SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D4FE;MATHEMATICAL BOLD SCRIPT SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D4FF;MATHEMATICAL BOLD SCRIPT SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D500;MATHEMATICAL BOLD SCRIPT SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D501;MATHEMATICAL BOLD SCRIPT SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D502;MATHEMATICAL BOLD SCRIPT SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D503;MATHEMATICAL BOLD SCRIPT SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D504;MATHEMATICAL FRAKTUR CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D505;MATHEMATICAL FRAKTUR CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D507;MATHEMATICAL FRAKTUR CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D508;MATHEMATICAL FRAKTUR CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D509;MATHEMATICAL FRAKTUR CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D50A;MATHEMATICAL FRAKTUR CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D50D;MATHEMATICAL FRAKTUR CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D50E;MATHEMATICAL FRAKTUR CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D50F;MATHEMATICAL FRAKTUR CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D510;MATHEMATICAL FRAKTUR CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D511;MATHEMATICAL FRAKTUR CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D512;MATHEMATICAL FRAKTUR CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D513;MATHEMATICAL FRAKTUR CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D514;MATHEMATICAL FRAKTUR CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D516;MATHEMATICAL FRAKTUR CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D517;MATHEMATICAL FRAKTUR CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D518;MATHEMATICAL FRAKTUR CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D519;MATHEMATICAL FRAKTUR CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D51A;MATHEMATICAL FRAKTUR CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D51B;MATHEMATICAL FRAKTUR CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D51C;MATHEMATICAL FRAKTUR CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D51E;MATHEMATICAL FRAKTUR SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D51F;MATHEMATICAL FRAKTUR SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D520;MATHEMATICAL FRAKTUR SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D521;MATHEMATICAL FRAKTUR SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D522;MATHEMATICAL FRAKTUR SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D523;MATHEMATICAL FRAKTUR SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D524;MATHEMATICAL FRAKTUR SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D525;MATHEMATICAL FRAKTUR SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D526;MATHEMATICAL FRAKTUR SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D527;MATHEMATICAL FRAKTUR SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D528;MATHEMATICAL FRAKTUR SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D529;MATHEMATICAL FRAKTUR SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D52A;MATHEMATICAL FRAKTUR SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D52B;MATHEMATICAL FRAKTUR SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D52C;MATHEMATICAL FRAKTUR SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D52D;MATHEMATICAL FRAKTUR SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D52E;MATHEMATICAL FRAKTUR SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D52F;MATHEMATICAL FRAKTUR SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D530;MATHEMATICAL FRAKTUR SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D531;MATHEMATICAL FRAKTUR SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D532;MATHEMATICAL FRAKTUR SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D533;MATHEMATICAL FRAKTUR SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D534;MATHEMATICAL FRAKTUR SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D535;MATHEMATICAL FRAKTUR SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D536;MATHEMATICAL FRAKTUR SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D537;MATHEMATICAL FRAKTUR SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D538;MATHEMATICAL DOUBLE-STRUCK CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D539;MATHEMATICAL DOUBLE-STRUCK CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D53B;MATHEMATICAL DOUBLE-STRUCK CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D53C;MATHEMATICAL DOUBLE-STRUCK CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D53D;MATHEMATICAL DOUBLE-STRUCK CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D53E;MATHEMATICAL DOUBLE-STRUCK CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D540;MATHEMATICAL DOUBLE-STRUCK CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D541;MATHEMATICAL DOUBLE-STRUCK CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D542;MATHEMATICAL DOUBLE-STRUCK CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D543;MATHEMATICAL DOUBLE-STRUCK CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D544;MATHEMATICAL DOUBLE-STRUCK CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D546;MATHEMATICAL DOUBLE-STRUCK CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D54A;MATHEMATICAL DOUBLE-STRUCK CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D54B;MATHEMATICAL DOUBLE-STRUCK CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D54C;MATHEMATICAL DOUBLE-STRUCK CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D54D;MATHEMATICAL DOUBLE-STRUCK CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D54E;MATHEMATICAL DOUBLE-STRUCK CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D54F;MATHEMATICAL DOUBLE-STRUCK CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D550;MATHEMATICAL DOUBLE-STRUCK CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D552;MATHEMATICAL DOUBLE-STRUCK SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D553;MATHEMATICAL DOUBLE-STRUCK SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D554;MATHEMATICAL DOUBLE-STRUCK SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D555;MATHEMATICAL DOUBLE-STRUCK SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D556;MATHEMATICAL DOUBLE-STRUCK SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D557;MATHEMATICAL DOUBLE-STRUCK SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D558;MATHEMATICAL DOUBLE-STRUCK SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D559;MATHEMATICAL DOUBLE-STRUCK SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D55A;MATHEMATICAL DOUBLE-STRUCK SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D55B;MATHEMATICAL DOUBLE-STRUCK SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D55C;MATHEMATICAL DOUBLE-STRUCK SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D55D;MATHEMATICAL DOUBLE-STRUCK SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D55E;MATHEMATICAL DOUBLE-STRUCK SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D55F;MATHEMATICAL DOUBLE-STRUCK SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D560;MATHEMATICAL DOUBLE-STRUCK SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D561;MATHEMATICAL DOUBLE-STRUCK SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D562;MATHEMATICAL DOUBLE-STRUCK SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D563;MATHEMATICAL DOUBLE-STRUCK SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D564;MATHEMATICAL DOUBLE-STRUCK SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D565;MATHEMATICAL DOUBLE-STRUCK SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D566;MATHEMATICAL DOUBLE-STRUCK SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D567;MATHEMATICAL DOUBLE-STRUCK SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D568;MATHEMATICAL DOUBLE-STRUCK SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D569;MATHEMATICAL DOUBLE-STRUCK SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D56A;MATHEMATICAL DOUBLE-STRUCK SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D56B;MATHEMATICAL DOUBLE-STRUCK SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D56C;MATHEMATICAL BOLD FRAKTUR CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D56D;MATHEMATICAL BOLD FRAKTUR CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D56E;MATHEMATICAL BOLD FRAKTUR CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D56F;MATHEMATICAL BOLD FRAKTUR CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D570;MATHEMATICAL BOLD FRAKTUR CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D571;MATHEMATICAL BOLD FRAKTUR CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D572;MATHEMATICAL BOLD FRAKTUR CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D573;MATHEMATICAL BOLD FRAKTUR CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D574;MATHEMATICAL BOLD FRAKTUR CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D575;MATHEMATICAL BOLD FRAKTUR CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D576;MATHEMATICAL BOLD FRAKTUR CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D577;MATHEMATICAL BOLD FRAKTUR CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D578;MATHEMATICAL BOLD FRAKTUR CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D579;MATHEMATICAL BOLD FRAKTUR CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D57A;MATHEMATICAL BOLD FRAKTUR CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D57B;MATHEMATICAL BOLD FRAKTUR CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D57C;MATHEMATICAL BOLD FRAKTUR CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D57D;MATHEMATICAL BOLD FRAKTUR CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D57E;MATHEMATICAL BOLD FRAKTUR CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D57F;MATHEMATICAL BOLD FRAKTUR CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D580;MATHEMATICAL BOLD FRAKTUR CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D581;MATHEMATICAL BOLD FRAKTUR CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D582;MATHEMATICAL BOLD FRAKTUR CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D583;MATHEMATICAL BOLD FRAKTUR CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D584;MATHEMATICAL BOLD FRAKTUR CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D585;MATHEMATICAL BOLD FRAKTUR CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D586;MATHEMATICAL BOLD FRAKTUR SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D587;MATHEMATICAL BOLD FRAKTUR SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D588;MATHEMATICAL BOLD FRAKTUR SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D589;MATHEMATICAL BOLD FRAKTUR SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D58A;MATHEMATICAL BOLD FRAKTUR SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D58B;MATHEMATICAL BOLD FRAKTUR SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D58C;MATHEMATICAL BOLD FRAKTUR SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D58D;MATHEMATICAL BOLD FRAKTUR SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D58E;MATHEMATICAL BOLD FRAKTUR SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D58F;MATHEMATICAL BOLD FRAKTUR SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D590;MATHEMATICAL BOLD FRAKTUR SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D591;MATHEMATICAL BOLD FRAKTUR SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D592;MATHEMATICAL BOLD FRAKTUR SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D593;MATHEMATICAL BOLD FRAKTUR SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D594;MATHEMATICAL BOLD FRAKTUR SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D595;MATHEMATICAL BOLD FRAKTUR SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D596;MATHEMATICAL BOLD FRAKTUR SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D597;MATHEMATICAL BOLD FRAKTUR SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D598;MATHEMATICAL BOLD FRAKTUR SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D599;MATHEMATICAL BOLD FRAKTUR SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D59A;MATHEMATICAL BOLD FRAKTUR SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D59B;MATHEMATICAL BOLD FRAKTUR SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D59C;MATHEMATICAL BOLD FRAKTUR SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D59D;MATHEMATICAL BOLD FRAKTUR SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D59E;MATHEMATICAL BOLD FRAKTUR SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D59F;MATHEMATICAL BOLD FRAKTUR SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D5A0;MATHEMATICAL SANS-SERIF CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D5A1;MATHEMATICAL SANS-SERIF CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D5A2;MATHEMATICAL SANS-SERIF CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D5A3;MATHEMATICAL SANS-SERIF CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D5A4;MATHEMATICAL SANS-SERIF CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D5A5;MATHEMATICAL SANS-SERIF CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D5A6;MATHEMATICAL SANS-SERIF CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D5A7;MATHEMATICAL SANS-SERIF CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D5A8;MATHEMATICAL SANS-SERIF CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D5A9;MATHEMATICAL SANS-SERIF CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D5AA;MATHEMATICAL SANS-SERIF CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D5AB;MATHEMATICAL SANS-SERIF CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D5AC;MATHEMATICAL SANS-SERIF CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D5AD;MATHEMATICAL SANS-SERIF CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D5AE;MATHEMATICAL SANS-SERIF CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D5AF;MATHEMATICAL SANS-SERIF CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D5B0;MATHEMATICAL SANS-SERIF CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D5B1;MATHEMATICAL SANS-SERIF CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D5B2;MATHEMATICAL SANS-SERIF CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D5B3;MATHEMATICAL SANS-SERIF CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D5B4;MATHEMATICAL SANS-SERIF CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D5B5;MATHEMATICAL SANS-SERIF CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D5B6;MATHEMATICAL SANS-SERIF CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D5B7;MATHEMATICAL SANS-SERIF CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D5B8;MATHEMATICAL SANS-SERIF CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D5B9;MATHEMATICAL SANS-SERIF CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D5BA;MATHEMATICAL SANS-SERIF SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D5BB;MATHEMATICAL SANS-SERIF SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D5BC;MATHEMATICAL SANS-SERIF SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D5BD;MATHEMATICAL SANS-SERIF SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D5BE;MATHEMATICAL SANS-SERIF SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D5BF;MATHEMATICAL SANS-SERIF SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D5C0;MATHEMATICAL SANS-SERIF SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D5C1;MATHEMATICAL SANS-SERIF SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D5C2;MATHEMATICAL SANS-SERIF SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D5C3;MATHEMATICAL SANS-SERIF SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D5C4;MATHEMATICAL SANS-SERIF SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D5C5;MATHEMATICAL SANS-SERIF SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D5C6;MATHEMATICAL SANS-SERIF SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D5C7;MATHEMATICAL SANS-SERIF SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D5C8;MATHEMATICAL SANS-SERIF SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D5C9;MATHEMATICAL SANS-SERIF SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D5CA;MATHEMATICAL SANS-SERIF SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D5CB;MATHEMATICAL SANS-SERIF SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D5CC;MATHEMATICAL SANS-SERIF SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D5CD;MATHEMATICAL SANS-SERIF SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D5CE;MATHEMATICAL SANS-SERIF SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D5CF;MATHEMATICAL SANS-SERIF SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D5D0;MATHEMATICAL SANS-SERIF SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D5D1;MATHEMATICAL SANS-SERIF SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D5D2;MATHEMATICAL SANS-SERIF SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D5D3;MATHEMATICAL SANS-SERIF SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D5D4;MATHEMATICAL SANS-SERIF BOLD CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D5D5;MATHEMATICAL SANS-SERIF BOLD CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D5D6;MATHEMATICAL SANS-SERIF BOLD CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D5D7;MATHEMATICAL SANS-SERIF BOLD CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D5D8;MATHEMATICAL SANS-SERIF BOLD CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D5D9;MATHEMATICAL SANS-SERIF BOLD CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D5DA;MATHEMATICAL SANS-SERIF BOLD CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D5DB;MATHEMATICAL SANS-SERIF BOLD CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D5DC;MATHEMATICAL SANS-SERIF BOLD CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D5DD;MATHEMATICAL SANS-SERIF BOLD CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D5DE;MATHEMATICAL SANS-SERIF BOLD CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D5DF;MATHEMATICAL SANS-SERIF BOLD CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D5E0;MATHEMATICAL SANS-SERIF BOLD CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D5E1;MATHEMATICAL SANS-SERIF BOLD CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D5E2;MATHEMATICAL SANS-SERIF BOLD CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D5E3;MATHEMATICAL SANS-SERIF BOLD CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D5E4;MATHEMATICAL SANS-SERIF BOLD CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D5E5;MATHEMATICAL SANS-SERIF BOLD CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D5E6;MATHEMATICAL SANS-SERIF BOLD CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D5E7;MATHEMATICAL SANS-SERIF BOLD CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D5E8;MATHEMATICAL SANS-SERIF BOLD CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D5E9;MATHEMATICAL SANS-SERIF BOLD CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D5EA;MATHEMATICAL SANS-SERIF BOLD CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D5EB;MATHEMATICAL SANS-SERIF BOLD CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D5EC;MATHEMATICAL SANS-SERIF BOLD CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D5ED;MATHEMATICAL SANS-SERIF BOLD CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D5EE;MATHEMATICAL SANS-SERIF BOLD SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D5EF;MATHEMATICAL SANS-SERIF BOLD SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D5F0;MATHEMATICAL SANS-SERIF BOLD SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D5F1;MATHEMATICAL SANS-SERIF BOLD SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D5F2;MATHEMATICAL SANS-SERIF BOLD SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D5F3;MATHEMATICAL SANS-SERIF BOLD SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D5F4;MATHEMATICAL SANS-SERIF BOLD SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D5F5;MATHEMATICAL SANS-SERIF BOLD SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D5F6;MATHEMATICAL SANS-SERIF BOLD SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D5F7;MATHEMATICAL SANS-SERIF BOLD SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D5F8;MATHEMATICAL SANS-SERIF BOLD SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D5F9;MATHEMATICAL SANS-SERIF BOLD SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D5FA;MATHEMATICAL SANS-SERIF BOLD SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D5FB;MATHEMATICAL SANS-SERIF BOLD SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D5FC;MATHEMATICAL SANS-SERIF BOLD SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D5FD;MATHEMATICAL SANS-SERIF BOLD SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D5FE;MATHEMATICAL SANS-SERIF BOLD SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D5FF;MATHEMATICAL SANS-SERIF BOLD SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D600;MATHEMATICAL SANS-SERIF BOLD SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D601;MATHEMATICAL SANS-SERIF BOLD SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D602;MATHEMATICAL SANS-SERIF BOLD SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D603;MATHEMATICAL SANS-SERIF BOLD SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D604;MATHEMATICAL SANS-SERIF BOLD SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D605;MATHEMATICAL SANS-SERIF BOLD SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D606;MATHEMATICAL SANS-SERIF BOLD SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D607;MATHEMATICAL SANS-SERIF BOLD SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D608;MATHEMATICAL SANS-SERIF ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D609;MATHEMATICAL SANS-SERIF ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D60A;MATHEMATICAL SANS-SERIF ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D60B;MATHEMATICAL SANS-SERIF ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D60C;MATHEMATICAL SANS-SERIF ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D60D;MATHEMATICAL SANS-SERIF ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D60E;MATHEMATICAL SANS-SERIF ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D60F;MATHEMATICAL SANS-SERIF ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D610;MATHEMATICAL SANS-SERIF ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D611;MATHEMATICAL SANS-SERIF ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D612;MATHEMATICAL SANS-SERIF ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D613;MATHEMATICAL SANS-SERIF ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D614;MATHEMATICAL SANS-SERIF ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D615;MATHEMATICAL SANS-SERIF ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D616;MATHEMATICAL SANS-SERIF ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D617;MATHEMATICAL SANS-SERIF ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D618;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D619;MATHEMATICAL SANS-SERIF ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D61A;MATHEMATICAL SANS-SERIF ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D61B;MATHEMATICAL SANS-SERIF ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D61C;MATHEMATICAL SANS-SERIF ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D61D;MATHEMATICAL SANS-SERIF ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D61E;MATHEMATICAL SANS-SERIF ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D61F;MATHEMATICAL SANS-SERIF ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D620;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D621;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D622;MATHEMATICAL SANS-SERIF ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D623;MATHEMATICAL SANS-SERIF ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D624;MATHEMATICAL SANS-SERIF ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D625;MATHEMATICAL SANS-SERIF ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D626;MATHEMATICAL SANS-SERIF ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D627;MATHEMATICAL SANS-SERIF ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D628;MATHEMATICAL SANS-SERIF ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D629;MATHEMATICAL SANS-SERIF ITALIC SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D62A;MATHEMATICAL SANS-SERIF ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D62B;MATHEMATICAL SANS-SERIF ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D62C;MATHEMATICAL SANS-SERIF ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D62D;MATHEMATICAL SANS-SERIF ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D62E;MATHEMATICAL SANS-SERIF ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D62F;MATHEMATICAL SANS-SERIF ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D630;MATHEMATICAL SANS-SERIF ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D631;MATHEMATICAL SANS-SERIF ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D632;MATHEMATICAL SANS-SERIF ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D633;MATHEMATICAL SANS-SERIF ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D634;MATHEMATICAL SANS-SERIF ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D635;MATHEMATICAL SANS-SERIF ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D636;MATHEMATICAL SANS-SERIF ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D637;MATHEMATICAL SANS-SERIF ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D638;MATHEMATICAL SANS-SERIF ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D639;MATHEMATICAL SANS-SERIF ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D63A;MATHEMATICAL SANS-SERIF ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D63B;MATHEMATICAL SANS-SERIF ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D63C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D63D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D63E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D63F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D640;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D641;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D642;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D643;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D644;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D645;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D646;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D647;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D648;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D649;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D64A;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D64B;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D64C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D64D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D64E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D64F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D650;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D651;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D652;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D653;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D654;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D655;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D656;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D657;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D658;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D659;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D65A;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D65B;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D65C;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D65D;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D65E;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D65F;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D660;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D661;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D662;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D663;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D664;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D665;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D666;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D667;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D668;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D669;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D66A;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D66B;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D66C;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D66D;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D66E;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D66F;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D670;MATHEMATICAL MONOSPACE CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D671;MATHEMATICAL MONOSPACE CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D672;MATHEMATICAL MONOSPACE CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D673;MATHEMATICAL MONOSPACE CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D674;MATHEMATICAL MONOSPACE CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D675;MATHEMATICAL MONOSPACE CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D676;MATHEMATICAL MONOSPACE CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D677;MATHEMATICAL MONOSPACE CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D678;MATHEMATICAL MONOSPACE CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D679;MATHEMATICAL MONOSPACE CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D67A;MATHEMATICAL MONOSPACE CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D67B;MATHEMATICAL MONOSPACE CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D67C;MATHEMATICAL MONOSPACE CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D67D;MATHEMATICAL MONOSPACE CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D67E;MATHEMATICAL MONOSPACE CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D67F;MATHEMATICAL MONOSPACE CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D680;MATHEMATICAL MONOSPACE CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D681;MATHEMATICAL MONOSPACE CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D682;MATHEMATICAL MONOSPACE CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D683;MATHEMATICAL MONOSPACE CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D684;MATHEMATICAL MONOSPACE CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D685;MATHEMATICAL MONOSPACE CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D686;MATHEMATICAL MONOSPACE CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D687;MATHEMATICAL MONOSPACE CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D688;MATHEMATICAL MONOSPACE CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D689;MATHEMATICAL MONOSPACE CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D68A;MATHEMATICAL MONOSPACE SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D68B;MATHEMATICAL MONOSPACE SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D68C;MATHEMATICAL MONOSPACE SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D68D;MATHEMATICAL MONOSPACE SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D68E;MATHEMATICAL MONOSPACE SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D68F;MATHEMATICAL MONOSPACE SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D690;MATHEMATICAL MONOSPACE SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D691;MATHEMATICAL MONOSPACE SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D692;MATHEMATICAL MONOSPACE SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D693;MATHEMATICAL MONOSPACE SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D694;MATHEMATICAL MONOSPACE SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D695;MATHEMATICAL MONOSPACE SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D696;MATHEMATICAL MONOSPACE SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D697;MATHEMATICAL MONOSPACE SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D698;MATHEMATICAL MONOSPACE SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D699;MATHEMATICAL MONOSPACE SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D69A;MATHEMATICAL MONOSPACE SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D69B;MATHEMATICAL MONOSPACE SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D69C;MATHEMATICAL MONOSPACE SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D69D;MATHEMATICAL MONOSPACE SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D69E;MATHEMATICAL MONOSPACE SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D69F;MATHEMATICAL MONOSPACE SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D6A0;MATHEMATICAL MONOSPACE SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D6A1;MATHEMATICAL MONOSPACE SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D6A2;MATHEMATICAL MONOSPACE SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D6A3;MATHEMATICAL MONOSPACE SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D6A4;MATHEMATICAL ITALIC SMALL DOTLESS I;Ll;0;L;<font> 0131;;;;N;;;;;
+1D6A5;MATHEMATICAL ITALIC SMALL DOTLESS J;Ll;0;L;<font> 0237;;;;N;;;;;
+1D6A8;MATHEMATICAL BOLD CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D6A9;MATHEMATICAL BOLD CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D6AA;MATHEMATICAL BOLD CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D6AB;MATHEMATICAL BOLD CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D6AC;MATHEMATICAL BOLD CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D6AD;MATHEMATICAL BOLD CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D6AE;MATHEMATICAL BOLD CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D6AF;MATHEMATICAL BOLD CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D6B0;MATHEMATICAL BOLD CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D6B1;MATHEMATICAL BOLD CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D6B2;MATHEMATICAL BOLD CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D6B3;MATHEMATICAL BOLD CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D6B4;MATHEMATICAL BOLD CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D6B5;MATHEMATICAL BOLD CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D6B6;MATHEMATICAL BOLD CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D6B7;MATHEMATICAL BOLD CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D6B8;MATHEMATICAL BOLD CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D6B9;MATHEMATICAL BOLD CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D6BA;MATHEMATICAL BOLD CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D6BB;MATHEMATICAL BOLD CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D6BC;MATHEMATICAL BOLD CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D6BD;MATHEMATICAL BOLD CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D6BE;MATHEMATICAL BOLD CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D6BF;MATHEMATICAL BOLD CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D6C0;MATHEMATICAL BOLD CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D6C1;MATHEMATICAL BOLD NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D6C2;MATHEMATICAL BOLD SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D6C3;MATHEMATICAL BOLD SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D6C4;MATHEMATICAL BOLD SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D6C5;MATHEMATICAL BOLD SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D6C6;MATHEMATICAL BOLD SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D6C7;MATHEMATICAL BOLD SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D6C8;MATHEMATICAL BOLD SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D6C9;MATHEMATICAL BOLD SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D6CA;MATHEMATICAL BOLD SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D6CB;MATHEMATICAL BOLD SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D6CC;MATHEMATICAL BOLD SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D6CD;MATHEMATICAL BOLD SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D6CE;MATHEMATICAL BOLD SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D6CF;MATHEMATICAL BOLD SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D6D0;MATHEMATICAL BOLD SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D6D1;MATHEMATICAL BOLD SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D6D2;MATHEMATICAL BOLD SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D6D3;MATHEMATICAL BOLD SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D6D4;MATHEMATICAL BOLD SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D6D5;MATHEMATICAL BOLD SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D6D6;MATHEMATICAL BOLD SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D6D7;MATHEMATICAL BOLD SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D6D8;MATHEMATICAL BOLD SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D6D9;MATHEMATICAL BOLD SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D6DA;MATHEMATICAL BOLD SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D6DB;MATHEMATICAL BOLD PARTIAL DIFFERENTIAL;Sm;0;ON;<font> 2202;;;;Y;;;;;
+1D6DC;MATHEMATICAL BOLD EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D6DD;MATHEMATICAL BOLD THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D6DE;MATHEMATICAL BOLD KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D6DF;MATHEMATICAL BOLD PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D6E0;MATHEMATICAL BOLD RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D6E1;MATHEMATICAL BOLD PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D6E2;MATHEMATICAL ITALIC CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D6E3;MATHEMATICAL ITALIC CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D6E4;MATHEMATICAL ITALIC CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D6E5;MATHEMATICAL ITALIC CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D6E6;MATHEMATICAL ITALIC CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D6E7;MATHEMATICAL ITALIC CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D6E8;MATHEMATICAL ITALIC CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D6E9;MATHEMATICAL ITALIC CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D6EA;MATHEMATICAL ITALIC CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D6EB;MATHEMATICAL ITALIC CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D6EC;MATHEMATICAL ITALIC CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D6ED;MATHEMATICAL ITALIC CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D6EE;MATHEMATICAL ITALIC CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D6EF;MATHEMATICAL ITALIC CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D6F0;MATHEMATICAL ITALIC CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D6F1;MATHEMATICAL ITALIC CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D6F2;MATHEMATICAL ITALIC CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D6F3;MATHEMATICAL ITALIC CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D6F4;MATHEMATICAL ITALIC CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D6F5;MATHEMATICAL ITALIC CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D6F6;MATHEMATICAL ITALIC CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D6F7;MATHEMATICAL ITALIC CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D6F8;MATHEMATICAL ITALIC CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D6F9;MATHEMATICAL ITALIC CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D6FA;MATHEMATICAL ITALIC CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D6FB;MATHEMATICAL ITALIC NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D6FC;MATHEMATICAL ITALIC SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D6FD;MATHEMATICAL ITALIC SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D6FE;MATHEMATICAL ITALIC SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D6FF;MATHEMATICAL ITALIC SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D700;MATHEMATICAL ITALIC SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D701;MATHEMATICAL ITALIC SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D702;MATHEMATICAL ITALIC SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D703;MATHEMATICAL ITALIC SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D704;MATHEMATICAL ITALIC SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D705;MATHEMATICAL ITALIC SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D706;MATHEMATICAL ITALIC SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D707;MATHEMATICAL ITALIC SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D708;MATHEMATICAL ITALIC SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D709;MATHEMATICAL ITALIC SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D70A;MATHEMATICAL ITALIC SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D70B;MATHEMATICAL ITALIC SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D70C;MATHEMATICAL ITALIC SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D70D;MATHEMATICAL ITALIC SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D70E;MATHEMATICAL ITALIC SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D70F;MATHEMATICAL ITALIC SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D710;MATHEMATICAL ITALIC SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D711;MATHEMATICAL ITALIC SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D712;MATHEMATICAL ITALIC SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D713;MATHEMATICAL ITALIC SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D714;MATHEMATICAL ITALIC SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D715;MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL;Sm;0;ON;<font> 2202;;;;Y;;;;;
+1D716;MATHEMATICAL ITALIC EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D717;MATHEMATICAL ITALIC THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D718;MATHEMATICAL ITALIC KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D719;MATHEMATICAL ITALIC PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D71A;MATHEMATICAL ITALIC RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D71B;MATHEMATICAL ITALIC PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D71C;MATHEMATICAL BOLD ITALIC CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D71D;MATHEMATICAL BOLD ITALIC CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D71E;MATHEMATICAL BOLD ITALIC CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D71F;MATHEMATICAL BOLD ITALIC CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D720;MATHEMATICAL BOLD ITALIC CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D721;MATHEMATICAL BOLD ITALIC CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D722;MATHEMATICAL BOLD ITALIC CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D723;MATHEMATICAL BOLD ITALIC CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D724;MATHEMATICAL BOLD ITALIC CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D725;MATHEMATICAL BOLD ITALIC CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D726;MATHEMATICAL BOLD ITALIC CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D727;MATHEMATICAL BOLD ITALIC CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D728;MATHEMATICAL BOLD ITALIC CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D729;MATHEMATICAL BOLD ITALIC CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D72A;MATHEMATICAL BOLD ITALIC CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D72B;MATHEMATICAL BOLD ITALIC CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D72C;MATHEMATICAL BOLD ITALIC CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D72D;MATHEMATICAL BOLD ITALIC CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D72E;MATHEMATICAL BOLD ITALIC CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D72F;MATHEMATICAL BOLD ITALIC CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D730;MATHEMATICAL BOLD ITALIC CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D731;MATHEMATICAL BOLD ITALIC CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D732;MATHEMATICAL BOLD ITALIC CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D733;MATHEMATICAL BOLD ITALIC CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D734;MATHEMATICAL BOLD ITALIC CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D735;MATHEMATICAL BOLD ITALIC NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D736;MATHEMATICAL BOLD ITALIC SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D737;MATHEMATICAL BOLD ITALIC SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D738;MATHEMATICAL BOLD ITALIC SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D739;MATHEMATICAL BOLD ITALIC SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D73A;MATHEMATICAL BOLD ITALIC SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D73B;MATHEMATICAL BOLD ITALIC SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D73C;MATHEMATICAL BOLD ITALIC SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D73D;MATHEMATICAL BOLD ITALIC SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D73E;MATHEMATICAL BOLD ITALIC SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D73F;MATHEMATICAL BOLD ITALIC SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D740;MATHEMATICAL BOLD ITALIC SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D741;MATHEMATICAL BOLD ITALIC SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D742;MATHEMATICAL BOLD ITALIC SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D743;MATHEMATICAL BOLD ITALIC SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D744;MATHEMATICAL BOLD ITALIC SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D745;MATHEMATICAL BOLD ITALIC SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D746;MATHEMATICAL BOLD ITALIC SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D747;MATHEMATICAL BOLD ITALIC SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D748;MATHEMATICAL BOLD ITALIC SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D749;MATHEMATICAL BOLD ITALIC SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D74A;MATHEMATICAL BOLD ITALIC SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D74B;MATHEMATICAL BOLD ITALIC SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D74C;MATHEMATICAL BOLD ITALIC SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D74D;MATHEMATICAL BOLD ITALIC SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D74E;MATHEMATICAL BOLD ITALIC SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D74F;MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL;Sm;0;ON;<font> 2202;;;;Y;;;;;
+1D750;MATHEMATICAL BOLD ITALIC EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D751;MATHEMATICAL BOLD ITALIC THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D752;MATHEMATICAL BOLD ITALIC KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D753;MATHEMATICAL BOLD ITALIC PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D754;MATHEMATICAL BOLD ITALIC RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D755;MATHEMATICAL BOLD ITALIC PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D756;MATHEMATICAL SANS-SERIF BOLD CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D757;MATHEMATICAL SANS-SERIF BOLD CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D758;MATHEMATICAL SANS-SERIF BOLD CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D759;MATHEMATICAL SANS-SERIF BOLD CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D75A;MATHEMATICAL SANS-SERIF BOLD CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D75B;MATHEMATICAL SANS-SERIF BOLD CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D75C;MATHEMATICAL SANS-SERIF BOLD CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D75D;MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D75E;MATHEMATICAL SANS-SERIF BOLD CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D75F;MATHEMATICAL SANS-SERIF BOLD CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D760;MATHEMATICAL SANS-SERIF BOLD CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D761;MATHEMATICAL SANS-SERIF BOLD CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D762;MATHEMATICAL SANS-SERIF BOLD CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D763;MATHEMATICAL SANS-SERIF BOLD CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D764;MATHEMATICAL SANS-SERIF BOLD CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D765;MATHEMATICAL SANS-SERIF BOLD CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D766;MATHEMATICAL SANS-SERIF BOLD CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D767;MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D768;MATHEMATICAL SANS-SERIF BOLD CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D769;MATHEMATICAL SANS-SERIF BOLD CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D76A;MATHEMATICAL SANS-SERIF BOLD CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D76B;MATHEMATICAL SANS-SERIF BOLD CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D76C;MATHEMATICAL SANS-SERIF BOLD CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D76D;MATHEMATICAL SANS-SERIF BOLD CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D76E;MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D76F;MATHEMATICAL SANS-SERIF BOLD NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D770;MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D771;MATHEMATICAL SANS-SERIF BOLD SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D772;MATHEMATICAL SANS-SERIF BOLD SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D773;MATHEMATICAL SANS-SERIF BOLD SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D774;MATHEMATICAL SANS-SERIF BOLD SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D775;MATHEMATICAL SANS-SERIF BOLD SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D776;MATHEMATICAL SANS-SERIF BOLD SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D777;MATHEMATICAL SANS-SERIF BOLD SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D778;MATHEMATICAL SANS-SERIF BOLD SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D779;MATHEMATICAL SANS-SERIF BOLD SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D77A;MATHEMATICAL SANS-SERIF BOLD SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D77B;MATHEMATICAL SANS-SERIF BOLD SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D77C;MATHEMATICAL SANS-SERIF BOLD SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D77D;MATHEMATICAL SANS-SERIF BOLD SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D77E;MATHEMATICAL SANS-SERIF BOLD SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D77F;MATHEMATICAL SANS-SERIF BOLD SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D780;MATHEMATICAL SANS-SERIF BOLD SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D781;MATHEMATICAL SANS-SERIF BOLD SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D782;MATHEMATICAL SANS-SERIF BOLD SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D783;MATHEMATICAL SANS-SERIF BOLD SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D784;MATHEMATICAL SANS-SERIF BOLD SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D785;MATHEMATICAL SANS-SERIF BOLD SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D786;MATHEMATICAL SANS-SERIF BOLD SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D787;MATHEMATICAL SANS-SERIF BOLD SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D788;MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D789;MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL;Sm;0;ON;<font> 2202;;;;Y;;;;;
+1D78A;MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D78B;MATHEMATICAL SANS-SERIF BOLD THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D78C;MATHEMATICAL SANS-SERIF BOLD KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D78D;MATHEMATICAL SANS-SERIF BOLD PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D78E;MATHEMATICAL SANS-SERIF BOLD RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D78F;MATHEMATICAL SANS-SERIF BOLD PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D790;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D791;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D792;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D793;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D794;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D795;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D796;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D797;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D798;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D799;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D79A;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D79B;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D79C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D79D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D79E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D79F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D7A0;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D7A1;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D7A2;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D7A3;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D7A4;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D7A5;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D7A6;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D7A7;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D7A8;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D7A9;MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D7AA;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D7AB;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D7AC;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D7AD;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D7AE;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D7AF;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D7B0;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D7B1;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D7B2;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D7B3;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D7B4;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D7B5;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D7B6;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D7B7;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D7B8;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D7B9;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D7BA;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D7BB;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D7BC;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D7BD;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D7BE;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D7BF;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D7C0;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D7C1;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D7C2;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D7C3;MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL;Sm;0;ON;<font> 2202;;;;Y;;;;;
+1D7C4;MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D7C5;MATHEMATICAL SANS-SERIF BOLD ITALIC THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D7C6;MATHEMATICAL SANS-SERIF BOLD ITALIC KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D7C7;MATHEMATICAL SANS-SERIF BOLD ITALIC PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D7C8;MATHEMATICAL SANS-SERIF BOLD ITALIC RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D7C9;MATHEMATICAL SANS-SERIF BOLD ITALIC PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D7CA;MATHEMATICAL BOLD CAPITAL DIGAMMA;Lu;0;L;<font> 03DC;;;;N;;;;;
+1D7CB;MATHEMATICAL BOLD SMALL DIGAMMA;Ll;0;L;<font> 03DD;;;;N;;;;;
+1D7CE;MATHEMATICAL BOLD DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7CF;MATHEMATICAL BOLD DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7D0;MATHEMATICAL BOLD DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7D1;MATHEMATICAL BOLD DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7D2;MATHEMATICAL BOLD DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7D3;MATHEMATICAL BOLD DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7D4;MATHEMATICAL BOLD DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7D5;MATHEMATICAL BOLD DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7D6;MATHEMATICAL BOLD DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7D7;MATHEMATICAL BOLD DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+1D7D8;MATHEMATICAL DOUBLE-STRUCK DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7D9;MATHEMATICAL DOUBLE-STRUCK DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7DA;MATHEMATICAL DOUBLE-STRUCK DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7DB;MATHEMATICAL DOUBLE-STRUCK DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7DC;MATHEMATICAL DOUBLE-STRUCK DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7DD;MATHEMATICAL DOUBLE-STRUCK DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7DE;MATHEMATICAL DOUBLE-STRUCK DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7DF;MATHEMATICAL DOUBLE-STRUCK DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7E0;MATHEMATICAL DOUBLE-STRUCK DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7E1;MATHEMATICAL DOUBLE-STRUCK DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+1D7E2;MATHEMATICAL SANS-SERIF DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7E3;MATHEMATICAL SANS-SERIF DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7E4;MATHEMATICAL SANS-SERIF DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7E5;MATHEMATICAL SANS-SERIF DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7E6;MATHEMATICAL SANS-SERIF DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7E7;MATHEMATICAL SANS-SERIF DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7E8;MATHEMATICAL SANS-SERIF DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7E9;MATHEMATICAL SANS-SERIF DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7EA;MATHEMATICAL SANS-SERIF DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7EB;MATHEMATICAL SANS-SERIF DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+1D7EC;MATHEMATICAL SANS-SERIF BOLD DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7ED;MATHEMATICAL SANS-SERIF BOLD DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7EE;MATHEMATICAL SANS-SERIF BOLD DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7EF;MATHEMATICAL SANS-SERIF BOLD DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7F0;MATHEMATICAL SANS-SERIF BOLD DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7F1;MATHEMATICAL SANS-SERIF BOLD DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7F2;MATHEMATICAL SANS-SERIF BOLD DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7F3;MATHEMATICAL SANS-SERIF BOLD DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7F4;MATHEMATICAL SANS-SERIF BOLD DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7F5;MATHEMATICAL SANS-SERIF BOLD DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+1D7F6;MATHEMATICAL MONOSPACE DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7F7;MATHEMATICAL MONOSPACE DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7F8;MATHEMATICAL MONOSPACE DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7F9;MATHEMATICAL MONOSPACE DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7FA;MATHEMATICAL MONOSPACE DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7FB;MATHEMATICAL MONOSPACE DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7FC;MATHEMATICAL MONOSPACE DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7FD;MATHEMATICAL MONOSPACE DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7FE;MATHEMATICAL MONOSPACE DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7FF;MATHEMATICAL MONOSPACE DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+1D800;SIGNWRITING HAND-FIST INDEX;So;0;L;;;;;N;;;;;
+1D801;SIGNWRITING HAND-CIRCLE INDEX;So;0;L;;;;;N;;;;;
+1D802;SIGNWRITING HAND-CUP INDEX;So;0;L;;;;;N;;;;;
+1D803;SIGNWRITING HAND-OVAL INDEX;So;0;L;;;;;N;;;;;
+1D804;SIGNWRITING HAND-HINGE INDEX;So;0;L;;;;;N;;;;;
+1D805;SIGNWRITING HAND-ANGLE INDEX;So;0;L;;;;;N;;;;;
+1D806;SIGNWRITING HAND-FIST INDEX BENT;So;0;L;;;;;N;;;;;
+1D807;SIGNWRITING HAND-CIRCLE INDEX BENT;So;0;L;;;;;N;;;;;
+1D808;SIGNWRITING HAND-FIST THUMB UNDER INDEX BENT;So;0;L;;;;;N;;;;;
+1D809;SIGNWRITING HAND-FIST INDEX RAISED KNUCKLE;So;0;L;;;;;N;;;;;
+1D80A;SIGNWRITING HAND-FIST INDEX CUPPED;So;0;L;;;;;N;;;;;
+1D80B;SIGNWRITING HAND-FIST INDEX HINGED;So;0;L;;;;;N;;;;;
+1D80C;SIGNWRITING HAND-FIST INDEX HINGED LOW;So;0;L;;;;;N;;;;;
+1D80D;SIGNWRITING HAND-CIRCLE INDEX HINGE;So;0;L;;;;;N;;;;;
+1D80E;SIGNWRITING HAND-FIST INDEX MIDDLE;So;0;L;;;;;N;;;;;
+1D80F;SIGNWRITING HAND-CIRCLE INDEX MIDDLE;So;0;L;;;;;N;;;;;
+1D810;SIGNWRITING HAND-FIST INDEX MIDDLE BENT;So;0;L;;;;;N;;;;;
+1D811;SIGNWRITING HAND-FIST INDEX MIDDLE RAISED KNUCKLES;So;0;L;;;;;N;;;;;
+1D812;SIGNWRITING HAND-FIST INDEX MIDDLE HINGED;So;0;L;;;;;N;;;;;
+1D813;SIGNWRITING HAND-FIST INDEX UP MIDDLE HINGED;So;0;L;;;;;N;;;;;
+1D814;SIGNWRITING HAND-FIST INDEX HINGED MIDDLE UP;So;0;L;;;;;N;;;;;
+1D815;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED;So;0;L;;;;;N;;;;;
+1D816;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED INDEX BENT;So;0;L;;;;;N;;;;;
+1D817;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED MIDDLE BENT;So;0;L;;;;;N;;;;;
+1D818;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED CUPPED;So;0;L;;;;;N;;;;;
+1D819;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED HINGED;So;0;L;;;;;N;;;;;
+1D81A;SIGNWRITING HAND-FIST INDEX MIDDLE CROSSED;So;0;L;;;;;N;;;;;
+1D81B;SIGNWRITING HAND-CIRCLE INDEX MIDDLE CROSSED;So;0;L;;;;;N;;;;;
+1D81C;SIGNWRITING HAND-FIST MIDDLE BENT OVER INDEX;So;0;L;;;;;N;;;;;
+1D81D;SIGNWRITING HAND-FIST INDEX BENT OVER MIDDLE;So;0;L;;;;;N;;;;;
+1D81E;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB;So;0;L;;;;;N;;;;;
+1D81F;SIGNWRITING HAND-CIRCLE INDEX MIDDLE THUMB;So;0;L;;;;;N;;;;;
+1D820;SIGNWRITING HAND-FIST INDEX MIDDLE STRAIGHT THUMB BENT;So;0;L;;;;;N;;;;;
+1D821;SIGNWRITING HAND-FIST INDEX MIDDLE BENT THUMB STRAIGHT;So;0;L;;;;;N;;;;;
+1D822;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB BENT;So;0;L;;;;;N;;;;;
+1D823;SIGNWRITING HAND-FIST INDEX MIDDLE HINGED SPREAD THUMB SIDE;So;0;L;;;;;N;;;;;
+1D824;SIGNWRITING HAND-FIST INDEX UP MIDDLE HINGED THUMB SIDE;So;0;L;;;;;N;;;;;
+1D825;SIGNWRITING HAND-FIST INDEX UP MIDDLE HINGED THUMB CONJOINED;So;0;L;;;;;N;;;;;
+1D826;SIGNWRITING HAND-FIST INDEX HINGED MIDDLE UP THUMB SIDE;So;0;L;;;;;N;;;;;
+1D827;SIGNWRITING HAND-FIST INDEX MIDDLE UP SPREAD THUMB FORWARD;So;0;L;;;;;N;;;;;
+1D828;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB CUPPED;So;0;L;;;;;N;;;;;
+1D829;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB CIRCLED;So;0;L;;;;;N;;;;;
+1D82A;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB HOOKED;So;0;L;;;;;N;;;;;
+1D82B;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB HINGED;So;0;L;;;;;N;;;;;
+1D82C;SIGNWRITING HAND-FIST THUMB BETWEEN INDEX MIDDLE STRAIGHT;So;0;L;;;;;N;;;;;
+1D82D;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED THUMB SIDE;So;0;L;;;;;N;;;;;
+1D82E;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED THUMB SIDE CONJOINED;So;0;L;;;;;N;;;;;
+1D82F;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED THUMB SIDE BENT;So;0;L;;;;;N;;;;;
+1D830;SIGNWRITING HAND-FIST MIDDLE THUMB HOOKED INDEX UP;So;0;L;;;;;N;;;;;
+1D831;SIGNWRITING HAND-FIST INDEX THUMB HOOKED MIDDLE UP;So;0;L;;;;;N;;;;;
+1D832;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED HINGED THUMB SIDE;So;0;L;;;;;N;;;;;
+1D833;SIGNWRITING HAND-FIST INDEX MIDDLE CROSSED THUMB SIDE;So;0;L;;;;;N;;;;;
+1D834;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED THUMB FORWARD;So;0;L;;;;;N;;;;;
+1D835;SIGNWRITING HAND-FIST INDEX MIDDLE CONJOINED CUPPED THUMB FORWARD;So;0;L;;;;;N;;;;;
+1D836;SIGNWRITING HAND-FIST MIDDLE THUMB CUPPED INDEX UP;So;0;L;;;;;N;;;;;
+1D837;SIGNWRITING HAND-FIST INDEX THUMB CUPPED MIDDLE UP;So;0;L;;;;;N;;;;;
+1D838;SIGNWRITING HAND-FIST MIDDLE THUMB CIRCLED INDEX UP;So;0;L;;;;;N;;;;;
+1D839;SIGNWRITING HAND-FIST MIDDLE THUMB CIRCLED INDEX HINGED;So;0;L;;;;;N;;;;;
+1D83A;SIGNWRITING HAND-FIST INDEX THUMB ANGLED OUT MIDDLE UP;So;0;L;;;;;N;;;;;
+1D83B;SIGNWRITING HAND-FIST INDEX THUMB ANGLED IN MIDDLE UP;So;0;L;;;;;N;;;;;
+1D83C;SIGNWRITING HAND-FIST INDEX THUMB CIRCLED MIDDLE UP;So;0;L;;;;;N;;;;;
+1D83D;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB CONJOINED HINGED;So;0;L;;;;;N;;;;;
+1D83E;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB ANGLED OUT;So;0;L;;;;;N;;;;;
+1D83F;SIGNWRITING HAND-FIST INDEX MIDDLE THUMB ANGLED;So;0;L;;;;;N;;;;;
+1D840;SIGNWRITING HAND-FIST MIDDLE THUMB ANGLED OUT INDEX UP;So;0;L;;;;;N;;;;;
+1D841;SIGNWRITING HAND-FIST MIDDLE THUMB ANGLED OUT INDEX CROSSED;So;0;L;;;;;N;;;;;
+1D842;SIGNWRITING HAND-FIST MIDDLE THUMB ANGLED INDEX UP;So;0;L;;;;;N;;;;;
+1D843;SIGNWRITING HAND-FIST INDEX THUMB HOOKED MIDDLE HINGED;So;0;L;;;;;N;;;;;
+1D844;SIGNWRITING HAND-FLAT FOUR FINGERS;So;0;L;;;;;N;;;;;
+1D845;SIGNWRITING HAND-FLAT FOUR FINGERS BENT;So;0;L;;;;;N;;;;;
+1D846;SIGNWRITING HAND-FLAT FOUR FINGERS HINGED;So;0;L;;;;;N;;;;;
+1D847;SIGNWRITING HAND-FLAT FOUR FINGERS CONJOINED;So;0;L;;;;;N;;;;;
+1D848;SIGNWRITING HAND-FLAT FOUR FINGERS CONJOINED SPLIT;So;0;L;;;;;N;;;;;
+1D849;SIGNWRITING HAND-CLAW FOUR FINGERS CONJOINED;So;0;L;;;;;N;;;;;
+1D84A;SIGNWRITING HAND-FIST FOUR FINGERS CONJOINED BENT;So;0;L;;;;;N;;;;;
+1D84B;SIGNWRITING HAND-HINGE FOUR FINGERS CONJOINED;So;0;L;;;;;N;;;;;
+1D84C;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD;So;0;L;;;;;N;;;;;
+1D84D;SIGNWRITING HAND-FLAT HEEL FIVE FINGERS SPREAD;So;0;L;;;;;N;;;;;
+1D84E;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD FOUR BENT;So;0;L;;;;;N;;;;;
+1D84F;SIGNWRITING HAND-FLAT HEEL FIVE FINGERS SPREAD FOUR BENT;So;0;L;;;;;N;;;;;
+1D850;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD BENT;So;0;L;;;;;N;;;;;
+1D851;SIGNWRITING HAND-FLAT HEEL FIVE FINGERS SPREAD BENT;So;0;L;;;;;N;;;;;
+1D852;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD THUMB FORWARD;So;0;L;;;;;N;;;;;
+1D853;SIGNWRITING HAND-CUP FIVE FINGERS SPREAD;So;0;L;;;;;N;;;;;
+1D854;SIGNWRITING HAND-CUP FIVE FINGERS SPREAD OPEN;So;0;L;;;;;N;;;;;
+1D855;SIGNWRITING HAND-HINGE FIVE FINGERS SPREAD OPEN;So;0;L;;;;;N;;;;;
+1D856;SIGNWRITING HAND-OVAL FIVE FINGERS SPREAD;So;0;L;;;;;N;;;;;
+1D857;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD HINGED;So;0;L;;;;;N;;;;;
+1D858;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD HINGED THUMB SIDE;So;0;L;;;;;N;;;;;
+1D859;SIGNWRITING HAND-FLAT FIVE FINGERS SPREAD HINGED NO THUMB;So;0;L;;;;;N;;;;;
+1D85A;SIGNWRITING HAND-FLAT;So;0;L;;;;;N;;;;;
+1D85B;SIGNWRITING HAND-FLAT BETWEEN PALM FACINGS;So;0;L;;;;;N;;;;;
+1D85C;SIGNWRITING HAND-FLAT HEEL;So;0;L;;;;;N;;;;;
+1D85D;SIGNWRITING HAND-FLAT THUMB SIDE;So;0;L;;;;;N;;;;;
+1D85E;SIGNWRITING HAND-FLAT HEEL THUMB SIDE;So;0;L;;;;;N;;;;;
+1D85F;SIGNWRITING HAND-FLAT THUMB BENT;So;0;L;;;;;N;;;;;
+1D860;SIGNWRITING HAND-FLAT THUMB FORWARD;So;0;L;;;;;N;;;;;
+1D861;SIGNWRITING HAND-FLAT SPLIT INDEX THUMB SIDE;So;0;L;;;;;N;;;;;
+1D862;SIGNWRITING HAND-FLAT SPLIT CENTRE;So;0;L;;;;;N;;;;;
+1D863;SIGNWRITING HAND-FLAT SPLIT CENTRE THUMB SIDE;So;0;L;;;;;N;;;;;
+1D864;SIGNWRITING HAND-FLAT SPLIT CENTRE THUMB SIDE BENT;So;0;L;;;;;N;;;;;
+1D865;SIGNWRITING HAND-FLAT SPLIT LITTLE;So;0;L;;;;;N;;;;;
+1D866;SIGNWRITING HAND-CLAW;So;0;L;;;;;N;;;;;
+1D867;SIGNWRITING HAND-CLAW THUMB SIDE;So;0;L;;;;;N;;;;;
+1D868;SIGNWRITING HAND-CLAW NO THUMB;So;0;L;;;;;N;;;;;
+1D869;SIGNWRITING HAND-CLAW THUMB FORWARD;So;0;L;;;;;N;;;;;
+1D86A;SIGNWRITING HAND-HOOK CURLICUE;So;0;L;;;;;N;;;;;
+1D86B;SIGNWRITING HAND-HOOK;So;0;L;;;;;N;;;;;
+1D86C;SIGNWRITING HAND-CUP OPEN;So;0;L;;;;;N;;;;;
+1D86D;SIGNWRITING HAND-CUP;So;0;L;;;;;N;;;;;
+1D86E;SIGNWRITING HAND-CUP OPEN THUMB SIDE;So;0;L;;;;;N;;;;;
+1D86F;SIGNWRITING HAND-CUP THUMB SIDE;So;0;L;;;;;N;;;;;
+1D870;SIGNWRITING HAND-CUP OPEN NO THUMB;So;0;L;;;;;N;;;;;
+1D871;SIGNWRITING HAND-CUP NO THUMB;So;0;L;;;;;N;;;;;
+1D872;SIGNWRITING HAND-CUP OPEN THUMB FORWARD;So;0;L;;;;;N;;;;;
+1D873;SIGNWRITING HAND-CUP THUMB FORWARD;So;0;L;;;;;N;;;;;
+1D874;SIGNWRITING HAND-CURLICUE OPEN;So;0;L;;;;;N;;;;;
+1D875;SIGNWRITING HAND-CURLICUE;So;0;L;;;;;N;;;;;
+1D876;SIGNWRITING HAND-CIRCLE;So;0;L;;;;;N;;;;;
+1D877;SIGNWRITING HAND-OVAL;So;0;L;;;;;N;;;;;
+1D878;SIGNWRITING HAND-OVAL THUMB SIDE;So;0;L;;;;;N;;;;;
+1D879;SIGNWRITING HAND-OVAL NO THUMB;So;0;L;;;;;N;;;;;
+1D87A;SIGNWRITING HAND-OVAL THUMB FORWARD;So;0;L;;;;;N;;;;;
+1D87B;SIGNWRITING HAND-HINGE OPEN;So;0;L;;;;;N;;;;;
+1D87C;SIGNWRITING HAND-HINGE OPEN THUMB FORWARD;So;0;L;;;;;N;;;;;
+1D87D;SIGNWRITING HAND-HINGE;So;0;L;;;;;N;;;;;
+1D87E;SIGNWRITING HAND-HINGE SMALL;So;0;L;;;;;N;;;;;
+1D87F;SIGNWRITING HAND-HINGE OPEN THUMB SIDE;So;0;L;;;;;N;;;;;
+1D880;SIGNWRITING HAND-HINGE THUMB SIDE;So;0;L;;;;;N;;;;;
+1D881;SIGNWRITING HAND-HINGE OPEN NO THUMB;So;0;L;;;;;N;;;;;
+1D882;SIGNWRITING HAND-HINGE NO THUMB;So;0;L;;;;;N;;;;;
+1D883;SIGNWRITING HAND-HINGE THUMB SIDE TOUCHING INDEX;So;0;L;;;;;N;;;;;
+1D884;SIGNWRITING HAND-HINGE THUMB BETWEEN MIDDLE RING;So;0;L;;;;;N;;;;;
+1D885;SIGNWRITING HAND-ANGLE;So;0;L;;;;;N;;;;;
+1D886;SIGNWRITING HAND-FIST INDEX MIDDLE RING;So;0;L;;;;;N;;;;;
+1D887;SIGNWRITING HAND-CIRCLE INDEX MIDDLE RING;So;0;L;;;;;N;;;;;
+1D888;SIGNWRITING HAND-HINGE INDEX MIDDLE RING;So;0;L;;;;;N;;;;;
+1D889;SIGNWRITING HAND-ANGLE INDEX MIDDLE RING;So;0;L;;;;;N;;;;;
+1D88A;SIGNWRITING HAND-HINGE LITTLE;So;0;L;;;;;N;;;;;
+1D88B;SIGNWRITING HAND-FIST INDEX MIDDLE RING BENT;So;0;L;;;;;N;;;;;
+1D88C;SIGNWRITING HAND-FIST INDEX MIDDLE RING CONJOINED;So;0;L;;;;;N;;;;;
+1D88D;SIGNWRITING HAND-HINGE INDEX MIDDLE RING CONJOINED;So;0;L;;;;;N;;;;;
+1D88E;SIGNWRITING HAND-FIST LITTLE DOWN;So;0;L;;;;;N;;;;;
+1D88F;SIGNWRITING HAND-FIST LITTLE DOWN RIPPLE STRAIGHT;So;0;L;;;;;N;;;;;
+1D890;SIGNWRITING HAND-FIST LITTLE DOWN RIPPLE CURVED;So;0;L;;;;;N;;;;;
+1D891;SIGNWRITING HAND-FIST LITTLE DOWN OTHERS CIRCLED;So;0;L;;;;;N;;;;;
+1D892;SIGNWRITING HAND-FIST LITTLE UP;So;0;L;;;;;N;;;;;
+1D893;SIGNWRITING HAND-FIST THUMB UNDER LITTLE UP;So;0;L;;;;;N;;;;;
+1D894;SIGNWRITING HAND-CIRCLE LITTLE UP;So;0;L;;;;;N;;;;;
+1D895;SIGNWRITING HAND-OVAL LITTLE UP;So;0;L;;;;;N;;;;;
+1D896;SIGNWRITING HAND-ANGLE LITTLE UP;So;0;L;;;;;N;;;;;
+1D897;SIGNWRITING HAND-FIST LITTLE RAISED KNUCKLE;So;0;L;;;;;N;;;;;
+1D898;SIGNWRITING HAND-FIST LITTLE BENT;So;0;L;;;;;N;;;;;
+1D899;SIGNWRITING HAND-FIST LITTLE TOUCHES THUMB;So;0;L;;;;;N;;;;;
+1D89A;SIGNWRITING HAND-FIST LITTLE THUMB;So;0;L;;;;;N;;;;;
+1D89B;SIGNWRITING HAND-HINGE LITTLE THUMB;So;0;L;;;;;N;;;;;
+1D89C;SIGNWRITING HAND-FIST LITTLE INDEX THUMB;So;0;L;;;;;N;;;;;
+1D89D;SIGNWRITING HAND-HINGE LITTLE INDEX THUMB;So;0;L;;;;;N;;;;;
+1D89E;SIGNWRITING HAND-ANGLE LITTLE INDEX THUMB INDEX THUMB OUT;So;0;L;;;;;N;;;;;
+1D89F;SIGNWRITING HAND-ANGLE LITTLE INDEX THUMB INDEX THUMB;So;0;L;;;;;N;;;;;
+1D8A0;SIGNWRITING HAND-FIST LITTLE INDEX;So;0;L;;;;;N;;;;;
+1D8A1;SIGNWRITING HAND-CIRCLE LITTLE INDEX;So;0;L;;;;;N;;;;;
+1D8A2;SIGNWRITING HAND-HINGE LITTLE INDEX;So;0;L;;;;;N;;;;;
+1D8A3;SIGNWRITING HAND-ANGLE LITTLE INDEX;So;0;L;;;;;N;;;;;
+1D8A4;SIGNWRITING HAND-FIST INDEX MIDDLE LITTLE;So;0;L;;;;;N;;;;;
+1D8A5;SIGNWRITING HAND-CIRCLE INDEX MIDDLE LITTLE;So;0;L;;;;;N;;;;;
+1D8A6;SIGNWRITING HAND-HINGE INDEX MIDDLE LITTLE;So;0;L;;;;;N;;;;;
+1D8A7;SIGNWRITING HAND-HINGE RING;So;0;L;;;;;N;;;;;
+1D8A8;SIGNWRITING HAND-ANGLE INDEX MIDDLE LITTLE;So;0;L;;;;;N;;;;;
+1D8A9;SIGNWRITING HAND-FIST INDEX MIDDLE CROSS LITTLE;So;0;L;;;;;N;;;;;
+1D8AA;SIGNWRITING HAND-CIRCLE INDEX MIDDLE CROSS LITTLE;So;0;L;;;;;N;;;;;
+1D8AB;SIGNWRITING HAND-FIST RING DOWN;So;0;L;;;;;N;;;;;
+1D8AC;SIGNWRITING HAND-HINGE RING DOWN INDEX THUMB HOOK MIDDLE;So;0;L;;;;;N;;;;;
+1D8AD;SIGNWRITING HAND-ANGLE RING DOWN MIDDLE THUMB INDEX CROSS;So;0;L;;;;;N;;;;;
+1D8AE;SIGNWRITING HAND-FIST RING UP;So;0;L;;;;;N;;;;;
+1D8AF;SIGNWRITING HAND-FIST RING RAISED KNUCKLE;So;0;L;;;;;N;;;;;
+1D8B0;SIGNWRITING HAND-FIST RING LITTLE;So;0;L;;;;;N;;;;;
+1D8B1;SIGNWRITING HAND-CIRCLE RING LITTLE;So;0;L;;;;;N;;;;;
+1D8B2;SIGNWRITING HAND-OVAL RING LITTLE;So;0;L;;;;;N;;;;;
+1D8B3;SIGNWRITING HAND-ANGLE RING LITTLE;So;0;L;;;;;N;;;;;
+1D8B4;SIGNWRITING HAND-FIST RING MIDDLE;So;0;L;;;;;N;;;;;
+1D8B5;SIGNWRITING HAND-FIST RING MIDDLE CONJOINED;So;0;L;;;;;N;;;;;
+1D8B6;SIGNWRITING HAND-FIST RING MIDDLE RAISED KNUCKLES;So;0;L;;;;;N;;;;;
+1D8B7;SIGNWRITING HAND-FIST RING INDEX;So;0;L;;;;;N;;;;;
+1D8B8;SIGNWRITING HAND-FIST RING THUMB;So;0;L;;;;;N;;;;;
+1D8B9;SIGNWRITING HAND-HOOK RING THUMB;So;0;L;;;;;N;;;;;
+1D8BA;SIGNWRITING HAND-FIST INDEX RING LITTLE;So;0;L;;;;;N;;;;;
+1D8BB;SIGNWRITING HAND-CIRCLE INDEX RING LITTLE;So;0;L;;;;;N;;;;;
+1D8BC;SIGNWRITING HAND-CURLICUE INDEX RING LITTLE ON;So;0;L;;;;;N;;;;;
+1D8BD;SIGNWRITING HAND-HOOK INDEX RING LITTLE OUT;So;0;L;;;;;N;;;;;
+1D8BE;SIGNWRITING HAND-HOOK INDEX RING LITTLE IN;So;0;L;;;;;N;;;;;
+1D8BF;SIGNWRITING HAND-HOOK INDEX RING LITTLE UNDER;So;0;L;;;;;N;;;;;
+1D8C0;SIGNWRITING HAND-CUP INDEX RING LITTLE;So;0;L;;;;;N;;;;;
+1D8C1;SIGNWRITING HAND-HINGE INDEX RING LITTLE;So;0;L;;;;;N;;;;;
+1D8C2;SIGNWRITING HAND-ANGLE INDEX RING LITTLE OUT;So;0;L;;;;;N;;;;;
+1D8C3;SIGNWRITING HAND-ANGLE INDEX RING LITTLE;So;0;L;;;;;N;;;;;
+1D8C4;SIGNWRITING HAND-FIST MIDDLE DOWN;So;0;L;;;;;N;;;;;
+1D8C5;SIGNWRITING HAND-HINGE MIDDLE;So;0;L;;;;;N;;;;;
+1D8C6;SIGNWRITING HAND-FIST MIDDLE UP;So;0;L;;;;;N;;;;;
+1D8C7;SIGNWRITING HAND-CIRCLE MIDDLE UP;So;0;L;;;;;N;;;;;
+1D8C8;SIGNWRITING HAND-FIST MIDDLE RAISED KNUCKLE;So;0;L;;;;;N;;;;;
+1D8C9;SIGNWRITING HAND-FIST MIDDLE UP THUMB SIDE;So;0;L;;;;;N;;;;;
+1D8CA;SIGNWRITING HAND-HOOK MIDDLE THUMB;So;0;L;;;;;N;;;;;
+1D8CB;SIGNWRITING HAND-FIST MIDDLE THUMB LITTLE;So;0;L;;;;;N;;;;;
+1D8CC;SIGNWRITING HAND-FIST MIDDLE LITTLE;So;0;L;;;;;N;;;;;
+1D8CD;SIGNWRITING HAND-FIST MIDDLE RING LITTLE;So;0;L;;;;;N;;;;;
+1D8CE;SIGNWRITING HAND-CIRCLE MIDDLE RING LITTLE;So;0;L;;;;;N;;;;;
+1D8CF;SIGNWRITING HAND-CURLICUE MIDDLE RING LITTLE ON;So;0;L;;;;;N;;;;;
+1D8D0;SIGNWRITING HAND-CUP MIDDLE RING LITTLE;So;0;L;;;;;N;;;;;
+1D8D1;SIGNWRITING HAND-HINGE MIDDLE RING LITTLE;So;0;L;;;;;N;;;;;
+1D8D2;SIGNWRITING HAND-ANGLE MIDDLE RING LITTLE OUT;So;0;L;;;;;N;;;;;
+1D8D3;SIGNWRITING HAND-ANGLE MIDDLE RING LITTLE IN;So;0;L;;;;;N;;;;;
+1D8D4;SIGNWRITING HAND-ANGLE MIDDLE RING LITTLE;So;0;L;;;;;N;;;;;
+1D8D5;SIGNWRITING HAND-CIRCLE MIDDLE RING LITTLE BENT;So;0;L;;;;;N;;;;;
+1D8D6;SIGNWRITING HAND-CLAW MIDDLE RING LITTLE CONJOINED;So;0;L;;;;;N;;;;;
+1D8D7;SIGNWRITING HAND-CLAW MIDDLE RING LITTLE CONJOINED SIDE;So;0;L;;;;;N;;;;;
+1D8D8;SIGNWRITING HAND-HOOK MIDDLE RING LITTLE CONJOINED OUT;So;0;L;;;;;N;;;;;
+1D8D9;SIGNWRITING HAND-HOOK MIDDLE RING LITTLE CONJOINED IN;So;0;L;;;;;N;;;;;
+1D8DA;SIGNWRITING HAND-HOOK MIDDLE RING LITTLE CONJOINED;So;0;L;;;;;N;;;;;
+1D8DB;SIGNWRITING HAND-HINGE INDEX HINGED;So;0;L;;;;;N;;;;;
+1D8DC;SIGNWRITING HAND-FIST INDEX THUMB SIDE;So;0;L;;;;;N;;;;;
+1D8DD;SIGNWRITING HAND-HINGE INDEX THUMB SIDE;So;0;L;;;;;N;;;;;
+1D8DE;SIGNWRITING HAND-FIST INDEX THUMB SIDE THUMB DIAGONAL;So;0;L;;;;;N;;;;;
+1D8DF;SIGNWRITING HAND-FIST INDEX THUMB SIDE THUMB CONJOINED;So;0;L;;;;;N;;;;;
+1D8E0;SIGNWRITING HAND-FIST INDEX THUMB SIDE THUMB BENT;So;0;L;;;;;N;;;;;
+1D8E1;SIGNWRITING HAND-FIST INDEX THUMB SIDE INDEX BENT;So;0;L;;;;;N;;;;;
+1D8E2;SIGNWRITING HAND-FIST INDEX THUMB SIDE BOTH BENT;So;0;L;;;;;N;;;;;
+1D8E3;SIGNWRITING HAND-FIST INDEX THUMB SIDE INDEX HINGE;So;0;L;;;;;N;;;;;
+1D8E4;SIGNWRITING HAND-FIST INDEX THUMB FORWARD INDEX STRAIGHT;So;0;L;;;;;N;;;;;
+1D8E5;SIGNWRITING HAND-FIST INDEX THUMB FORWARD INDEX BENT;So;0;L;;;;;N;;;;;
+1D8E6;SIGNWRITING HAND-FIST INDEX THUMB HOOK;So;0;L;;;;;N;;;;;
+1D8E7;SIGNWRITING HAND-FIST INDEX THUMB CURLICUE;So;0;L;;;;;N;;;;;
+1D8E8;SIGNWRITING HAND-FIST INDEX THUMB CURVE THUMB INSIDE;So;0;L;;;;;N;;;;;
+1D8E9;SIGNWRITING HAND-CLAW INDEX THUMB CURVE THUMB INSIDE;So;0;L;;;;;N;;;;;
+1D8EA;SIGNWRITING HAND-FIST INDEX THUMB CURVE THUMB UNDER;So;0;L;;;;;N;;;;;
+1D8EB;SIGNWRITING HAND-FIST INDEX THUMB CIRCLE;So;0;L;;;;;N;;;;;
+1D8EC;SIGNWRITING HAND-CUP INDEX THUMB;So;0;L;;;;;N;;;;;
+1D8ED;SIGNWRITING HAND-CUP INDEX THUMB OPEN;So;0;L;;;;;N;;;;;
+1D8EE;SIGNWRITING HAND-HINGE INDEX THUMB OPEN;So;0;L;;;;;N;;;;;
+1D8EF;SIGNWRITING HAND-HINGE INDEX THUMB LARGE;So;0;L;;;;;N;;;;;
+1D8F0;SIGNWRITING HAND-HINGE INDEX THUMB;So;0;L;;;;;N;;;;;
+1D8F1;SIGNWRITING HAND-HINGE INDEX THUMB SMALL;So;0;L;;;;;N;;;;;
+1D8F2;SIGNWRITING HAND-ANGLE INDEX THUMB OUT;So;0;L;;;;;N;;;;;
+1D8F3;SIGNWRITING HAND-ANGLE INDEX THUMB IN;So;0;L;;;;;N;;;;;
+1D8F4;SIGNWRITING HAND-ANGLE INDEX THUMB;So;0;L;;;;;N;;;;;
+1D8F5;SIGNWRITING HAND-FIST THUMB;So;0;L;;;;;N;;;;;
+1D8F6;SIGNWRITING HAND-FIST THUMB HEEL;So;0;L;;;;;N;;;;;
+1D8F7;SIGNWRITING HAND-FIST THUMB SIDE DIAGONAL;So;0;L;;;;;N;;;;;
+1D8F8;SIGNWRITING HAND-FIST THUMB SIDE CONJOINED;So;0;L;;;;;N;;;;;
+1D8F9;SIGNWRITING HAND-FIST THUMB SIDE BENT;So;0;L;;;;;N;;;;;
+1D8FA;SIGNWRITING HAND-FIST THUMB FORWARD;So;0;L;;;;;N;;;;;
+1D8FB;SIGNWRITING HAND-FIST THUMB BETWEEN INDEX MIDDLE;So;0;L;;;;;N;;;;;
+1D8FC;SIGNWRITING HAND-FIST THUMB BETWEEN MIDDLE RING;So;0;L;;;;;N;;;;;
+1D8FD;SIGNWRITING HAND-FIST THUMB BETWEEN RING LITTLE;So;0;L;;;;;N;;;;;
+1D8FE;SIGNWRITING HAND-FIST THUMB UNDER TWO FINGERS;So;0;L;;;;;N;;;;;
+1D8FF;SIGNWRITING HAND-FIST THUMB OVER TWO FINGERS;So;0;L;;;;;N;;;;;
+1D900;SIGNWRITING HAND-FIST THUMB UNDER THREE FINGERS;So;0;L;;;;;N;;;;;
+1D901;SIGNWRITING HAND-FIST THUMB UNDER FOUR FINGERS;So;0;L;;;;;N;;;;;
+1D902;SIGNWRITING HAND-FIST THUMB OVER FOUR RAISED KNUCKLES;So;0;L;;;;;N;;;;;
+1D903;SIGNWRITING HAND-FIST;So;0;L;;;;;N;;;;;
+1D904;SIGNWRITING HAND-FIST HEEL;So;0;L;;;;;N;;;;;
+1D905;SIGNWRITING TOUCH SINGLE;So;0;L;;;;;N;;;;;
+1D906;SIGNWRITING TOUCH MULTIPLE;So;0;L;;;;;N;;;;;
+1D907;SIGNWRITING TOUCH BETWEEN;So;0;L;;;;;N;;;;;
+1D908;SIGNWRITING GRASP SINGLE;So;0;L;;;;;N;;;;;
+1D909;SIGNWRITING GRASP MULTIPLE;So;0;L;;;;;N;;;;;
+1D90A;SIGNWRITING GRASP BETWEEN;So;0;L;;;;;N;;;;;
+1D90B;SIGNWRITING STRIKE SINGLE;So;0;L;;;;;N;;;;;
+1D90C;SIGNWRITING STRIKE MULTIPLE;So;0;L;;;;;N;;;;;
+1D90D;SIGNWRITING STRIKE BETWEEN;So;0;L;;;;;N;;;;;
+1D90E;SIGNWRITING BRUSH SINGLE;So;0;L;;;;;N;;;;;
+1D90F;SIGNWRITING BRUSH MULTIPLE;So;0;L;;;;;N;;;;;
+1D910;SIGNWRITING BRUSH BETWEEN;So;0;L;;;;;N;;;;;
+1D911;SIGNWRITING RUB SINGLE;So;0;L;;;;;N;;;;;
+1D912;SIGNWRITING RUB MULTIPLE;So;0;L;;;;;N;;;;;
+1D913;SIGNWRITING RUB BETWEEN;So;0;L;;;;;N;;;;;
+1D914;SIGNWRITING SURFACE SYMBOLS;So;0;L;;;;;N;;;;;
+1D915;SIGNWRITING SURFACE BETWEEN;So;0;L;;;;;N;;;;;
+1D916;SIGNWRITING SQUEEZE LARGE SINGLE;So;0;L;;;;;N;;;;;
+1D917;SIGNWRITING SQUEEZE SMALL SINGLE;So;0;L;;;;;N;;;;;
+1D918;SIGNWRITING SQUEEZE LARGE MULTIPLE;So;0;L;;;;;N;;;;;
+1D919;SIGNWRITING SQUEEZE SMALL MULTIPLE;So;0;L;;;;;N;;;;;
+1D91A;SIGNWRITING SQUEEZE SEQUENTIAL;So;0;L;;;;;N;;;;;
+1D91B;SIGNWRITING FLICK LARGE SINGLE;So;0;L;;;;;N;;;;;
+1D91C;SIGNWRITING FLICK SMALL SINGLE;So;0;L;;;;;N;;;;;
+1D91D;SIGNWRITING FLICK LARGE MULTIPLE;So;0;L;;;;;N;;;;;
+1D91E;SIGNWRITING FLICK SMALL MULTIPLE;So;0;L;;;;;N;;;;;
+1D91F;SIGNWRITING FLICK SEQUENTIAL;So;0;L;;;;;N;;;;;
+1D920;SIGNWRITING SQUEEZE FLICK ALTERNATING;So;0;L;;;;;N;;;;;
+1D921;SIGNWRITING MOVEMENT-HINGE UP DOWN LARGE;So;0;L;;;;;N;;;;;
+1D922;SIGNWRITING MOVEMENT-HINGE UP DOWN SMALL;So;0;L;;;;;N;;;;;
+1D923;SIGNWRITING MOVEMENT-HINGE UP SEQUENTIAL;So;0;L;;;;;N;;;;;
+1D924;SIGNWRITING MOVEMENT-HINGE DOWN SEQUENTIAL;So;0;L;;;;;N;;;;;
+1D925;SIGNWRITING MOVEMENT-HINGE UP DOWN ALTERNATING LARGE;So;0;L;;;;;N;;;;;
+1D926;SIGNWRITING MOVEMENT-HINGE UP DOWN ALTERNATING SMALL;So;0;L;;;;;N;;;;;
+1D927;SIGNWRITING MOVEMENT-HINGE SIDE TO SIDE SCISSORS;So;0;L;;;;;N;;;;;
+1D928;SIGNWRITING MOVEMENT-WALLPLANE FINGER CONTACT;So;0;L;;;;;N;;;;;
+1D929;SIGNWRITING MOVEMENT-FLOORPLANE FINGER CONTACT;So;0;L;;;;;N;;;;;
+1D92A;SIGNWRITING MOVEMENT-WALLPLANE SINGLE STRAIGHT SMALL;So;0;L;;;;;N;;;;;
+1D92B;SIGNWRITING MOVEMENT-WALLPLANE SINGLE STRAIGHT MEDIUM;So;0;L;;;;;N;;;;;
+1D92C;SIGNWRITING MOVEMENT-WALLPLANE SINGLE STRAIGHT LARGE;So;0;L;;;;;N;;;;;
+1D92D;SIGNWRITING MOVEMENT-WALLPLANE SINGLE STRAIGHT LARGEST;So;0;L;;;;;N;;;;;
+1D92E;SIGNWRITING MOVEMENT-WALLPLANE SINGLE WRIST FLEX;So;0;L;;;;;N;;;;;
+1D92F;SIGNWRITING MOVEMENT-WALLPLANE DOUBLE STRAIGHT;So;0;L;;;;;N;;;;;
+1D930;SIGNWRITING MOVEMENT-WALLPLANE DOUBLE WRIST FLEX;So;0;L;;;;;N;;;;;
+1D931;SIGNWRITING MOVEMENT-WALLPLANE DOUBLE ALTERNATING;So;0;L;;;;;N;;;;;
+1D932;SIGNWRITING MOVEMENT-WALLPLANE DOUBLE ALTERNATING WRIST FLEX;So;0;L;;;;;N;;;;;
+1D933;SIGNWRITING MOVEMENT-WALLPLANE CROSS;So;0;L;;;;;N;;;;;
+1D934;SIGNWRITING MOVEMENT-WALLPLANE TRIPLE STRAIGHT MOVEMENT;So;0;L;;;;;N;;;;;
+1D935;SIGNWRITING MOVEMENT-WALLPLANE TRIPLE WRIST FLEX;So;0;L;;;;;N;;;;;
+1D936;SIGNWRITING MOVEMENT-WALLPLANE TRIPLE ALTERNATING;So;0;L;;;;;N;;;;;
+1D937;SIGNWRITING MOVEMENT-WALLPLANE TRIPLE ALTERNATING WRIST FLEX;So;0;L;;;;;N;;;;;
+1D938;SIGNWRITING MOVEMENT-WALLPLANE BEND SMALL;So;0;L;;;;;N;;;;;
+1D939;SIGNWRITING MOVEMENT-WALLPLANE BEND MEDIUM;So;0;L;;;;;N;;;;;
+1D93A;SIGNWRITING MOVEMENT-WALLPLANE BEND LARGE;So;0;L;;;;;N;;;;;
+1D93B;SIGNWRITING MOVEMENT-WALLPLANE CORNER SMALL;So;0;L;;;;;N;;;;;
+1D93C;SIGNWRITING MOVEMENT-WALLPLANE CORNER MEDIUM;So;0;L;;;;;N;;;;;
+1D93D;SIGNWRITING MOVEMENT-WALLPLANE CORNER LARGE;So;0;L;;;;;N;;;;;
+1D93E;SIGNWRITING MOVEMENT-WALLPLANE CORNER ROTATION;So;0;L;;;;;N;;;;;
+1D93F;SIGNWRITING MOVEMENT-WALLPLANE CHECK SMALL;So;0;L;;;;;N;;;;;
+1D940;SIGNWRITING MOVEMENT-WALLPLANE CHECK MEDIUM;So;0;L;;;;;N;;;;;
+1D941;SIGNWRITING MOVEMENT-WALLPLANE CHECK LARGE;So;0;L;;;;;N;;;;;
+1D942;SIGNWRITING MOVEMENT-WALLPLANE BOX SMALL;So;0;L;;;;;N;;;;;
+1D943;SIGNWRITING MOVEMENT-WALLPLANE BOX MEDIUM;So;0;L;;;;;N;;;;;
+1D944;SIGNWRITING MOVEMENT-WALLPLANE BOX LARGE;So;0;L;;;;;N;;;;;
+1D945;SIGNWRITING MOVEMENT-WALLPLANE ZIGZAG SMALL;So;0;L;;;;;N;;;;;
+1D946;SIGNWRITING MOVEMENT-WALLPLANE ZIGZAG MEDIUM;So;0;L;;;;;N;;;;;
+1D947;SIGNWRITING MOVEMENT-WALLPLANE ZIGZAG LARGE;So;0;L;;;;;N;;;;;
+1D948;SIGNWRITING MOVEMENT-WALLPLANE PEAKS SMALL;So;0;L;;;;;N;;;;;
+1D949;SIGNWRITING MOVEMENT-WALLPLANE PEAKS MEDIUM;So;0;L;;;;;N;;;;;
+1D94A;SIGNWRITING MOVEMENT-WALLPLANE PEAKS LARGE;So;0;L;;;;;N;;;;;
+1D94B;SIGNWRITING TRAVEL-WALLPLANE ROTATION-WALLPLANE SINGLE;So;0;L;;;;;N;;;;;
+1D94C;SIGNWRITING TRAVEL-WALLPLANE ROTATION-WALLPLANE DOUBLE;So;0;L;;;;;N;;;;;
+1D94D;SIGNWRITING TRAVEL-WALLPLANE ROTATION-WALLPLANE ALTERNATING;So;0;L;;;;;N;;;;;
+1D94E;SIGNWRITING TRAVEL-WALLPLANE ROTATION-FLOORPLANE SINGLE;So;0;L;;;;;N;;;;;
+1D94F;SIGNWRITING TRAVEL-WALLPLANE ROTATION-FLOORPLANE DOUBLE;So;0;L;;;;;N;;;;;
+1D950;SIGNWRITING TRAVEL-WALLPLANE ROTATION-FLOORPLANE ALTERNATING;So;0;L;;;;;N;;;;;
+1D951;SIGNWRITING TRAVEL-WALLPLANE SHAKING;So;0;L;;;;;N;;;;;
+1D952;SIGNWRITING TRAVEL-WALLPLANE ARM SPIRAL SINGLE;So;0;L;;;;;N;;;;;
+1D953;SIGNWRITING TRAVEL-WALLPLANE ARM SPIRAL DOUBLE;So;0;L;;;;;N;;;;;
+1D954;SIGNWRITING TRAVEL-WALLPLANE ARM SPIRAL TRIPLE;So;0;L;;;;;N;;;;;
+1D955;SIGNWRITING MOVEMENT-DIAGONAL AWAY SMALL;So;0;L;;;;;N;;;;;
+1D956;SIGNWRITING MOVEMENT-DIAGONAL AWAY MEDIUM;So;0;L;;;;;N;;;;;
+1D957;SIGNWRITING MOVEMENT-DIAGONAL AWAY LARGE;So;0;L;;;;;N;;;;;
+1D958;SIGNWRITING MOVEMENT-DIAGONAL AWAY LARGEST;So;0;L;;;;;N;;;;;
+1D959;SIGNWRITING MOVEMENT-DIAGONAL TOWARDS SMALL;So;0;L;;;;;N;;;;;
+1D95A;SIGNWRITING MOVEMENT-DIAGONAL TOWARDS MEDIUM;So;0;L;;;;;N;;;;;
+1D95B;SIGNWRITING MOVEMENT-DIAGONAL TOWARDS LARGE;So;0;L;;;;;N;;;;;
+1D95C;SIGNWRITING MOVEMENT-DIAGONAL TOWARDS LARGEST;So;0;L;;;;;N;;;;;
+1D95D;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN AWAY SMALL;So;0;L;;;;;N;;;;;
+1D95E;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN AWAY MEDIUM;So;0;L;;;;;N;;;;;
+1D95F;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN AWAY LARGE;So;0;L;;;;;N;;;;;
+1D960;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN AWAY LARGEST;So;0;L;;;;;N;;;;;
+1D961;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN TOWARDS SMALL;So;0;L;;;;;N;;;;;
+1D962;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN TOWARDS MEDIUM;So;0;L;;;;;N;;;;;
+1D963;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN TOWARDS LARGE;So;0;L;;;;;N;;;;;
+1D964;SIGNWRITING MOVEMENT-DIAGONAL BETWEEN TOWARDS LARGEST;So;0;L;;;;;N;;;;;
+1D965;SIGNWRITING MOVEMENT-FLOORPLANE SINGLE STRAIGHT SMALL;So;0;L;;;;;N;;;;;
+1D966;SIGNWRITING MOVEMENT-FLOORPLANE SINGLE STRAIGHT MEDIUM;So;0;L;;;;;N;;;;;
+1D967;SIGNWRITING MOVEMENT-FLOORPLANE SINGLE STRAIGHT LARGE;So;0;L;;;;;N;;;;;
+1D968;SIGNWRITING MOVEMENT-FLOORPLANE SINGLE STRAIGHT LARGEST;So;0;L;;;;;N;;;;;
+1D969;SIGNWRITING MOVEMENT-FLOORPLANE SINGLE WRIST FLEX;So;0;L;;;;;N;;;;;
+1D96A;SIGNWRITING MOVEMENT-FLOORPLANE DOUBLE STRAIGHT;So;0;L;;;;;N;;;;;
+1D96B;SIGNWRITING MOVEMENT-FLOORPLANE DOUBLE WRIST FLEX;So;0;L;;;;;N;;;;;
+1D96C;SIGNWRITING MOVEMENT-FLOORPLANE DOUBLE ALTERNATING;So;0;L;;;;;N;;;;;
+1D96D;SIGNWRITING MOVEMENT-FLOORPLANE DOUBLE ALTERNATING WRIST FLEX;So;0;L;;;;;N;;;;;
+1D96E;SIGNWRITING MOVEMENT-FLOORPLANE CROSS;So;0;L;;;;;N;;;;;
+1D96F;SIGNWRITING MOVEMENT-FLOORPLANE TRIPLE STRAIGHT MOVEMENT;So;0;L;;;;;N;;;;;
+1D970;SIGNWRITING MOVEMENT-FLOORPLANE TRIPLE WRIST FLEX;So;0;L;;;;;N;;;;;
+1D971;SIGNWRITING MOVEMENT-FLOORPLANE TRIPLE ALTERNATING MOVEMENT;So;0;L;;;;;N;;;;;
+1D972;SIGNWRITING MOVEMENT-FLOORPLANE TRIPLE ALTERNATING WRIST FLEX;So;0;L;;;;;N;;;;;
+1D973;SIGNWRITING MOVEMENT-FLOORPLANE BEND;So;0;L;;;;;N;;;;;
+1D974;SIGNWRITING MOVEMENT-FLOORPLANE CORNER SMALL;So;0;L;;;;;N;;;;;
+1D975;SIGNWRITING MOVEMENT-FLOORPLANE CORNER MEDIUM;So;0;L;;;;;N;;;;;
+1D976;SIGNWRITING MOVEMENT-FLOORPLANE CORNER LARGE;So;0;L;;;;;N;;;;;
+1D977;SIGNWRITING MOVEMENT-FLOORPLANE CHECK;So;0;L;;;;;N;;;;;
+1D978;SIGNWRITING MOVEMENT-FLOORPLANE BOX SMALL;So;0;L;;;;;N;;;;;
+1D979;SIGNWRITING MOVEMENT-FLOORPLANE BOX MEDIUM;So;0;L;;;;;N;;;;;
+1D97A;SIGNWRITING MOVEMENT-FLOORPLANE BOX LARGE;So;0;L;;;;;N;;;;;
+1D97B;SIGNWRITING MOVEMENT-FLOORPLANE ZIGZAG SMALL;So;0;L;;;;;N;;;;;
+1D97C;SIGNWRITING MOVEMENT-FLOORPLANE ZIGZAG MEDIUM;So;0;L;;;;;N;;;;;
+1D97D;SIGNWRITING MOVEMENT-FLOORPLANE ZIGZAG LARGE;So;0;L;;;;;N;;;;;
+1D97E;SIGNWRITING MOVEMENT-FLOORPLANE PEAKS SMALL;So;0;L;;;;;N;;;;;
+1D97F;SIGNWRITING MOVEMENT-FLOORPLANE PEAKS MEDIUM;So;0;L;;;;;N;;;;;
+1D980;SIGNWRITING MOVEMENT-FLOORPLANE PEAKS LARGE;So;0;L;;;;;N;;;;;
+1D981;SIGNWRITING TRAVEL-FLOORPLANE ROTATION-FLOORPLANE SINGLE;So;0;L;;;;;N;;;;;
+1D982;SIGNWRITING TRAVEL-FLOORPLANE ROTATION-FLOORPLANE DOUBLE;So;0;L;;;;;N;;;;;
+1D983;SIGNWRITING TRAVEL-FLOORPLANE ROTATION-FLOORPLANE ALTERNATING;So;0;L;;;;;N;;;;;
+1D984;SIGNWRITING TRAVEL-FLOORPLANE ROTATION-WALLPLANE SINGLE;So;0;L;;;;;N;;;;;
+1D985;SIGNWRITING TRAVEL-FLOORPLANE ROTATION-WALLPLANE DOUBLE;So;0;L;;;;;N;;;;;
+1D986;SIGNWRITING TRAVEL-FLOORPLANE ROTATION-WALLPLANE ALTERNATING;So;0;L;;;;;N;;;;;
+1D987;SIGNWRITING TRAVEL-FLOORPLANE SHAKING;So;0;L;;;;;N;;;;;
+1D988;SIGNWRITING MOVEMENT-WALLPLANE CURVE QUARTER SMALL;So;0;L;;;;;N;;;;;
+1D989;SIGNWRITING MOVEMENT-WALLPLANE CURVE QUARTER MEDIUM;So;0;L;;;;;N;;;;;
+1D98A;SIGNWRITING MOVEMENT-WALLPLANE CURVE QUARTER LARGE;So;0;L;;;;;N;;;;;
+1D98B;SIGNWRITING MOVEMENT-WALLPLANE CURVE QUARTER LARGEST;So;0;L;;;;;N;;;;;
+1D98C;SIGNWRITING MOVEMENT-WALLPLANE CURVE HALF-CIRCLE SMALL;So;0;L;;;;;N;;;;;
+1D98D;SIGNWRITING MOVEMENT-WALLPLANE CURVE HALF-CIRCLE MEDIUM;So;0;L;;;;;N;;;;;
+1D98E;SIGNWRITING MOVEMENT-WALLPLANE CURVE HALF-CIRCLE LARGE;So;0;L;;;;;N;;;;;
+1D98F;SIGNWRITING MOVEMENT-WALLPLANE CURVE HALF-CIRCLE LARGEST;So;0;L;;;;;N;;;;;
+1D990;SIGNWRITING MOVEMENT-WALLPLANE CURVE THREE-QUARTER CIRCLE SMALL;So;0;L;;;;;N;;;;;
+1D991;SIGNWRITING MOVEMENT-WALLPLANE CURVE THREE-QUARTER CIRCLE MEDIUM;So;0;L;;;;;N;;;;;
+1D992;SIGNWRITING MOVEMENT-WALLPLANE HUMP SMALL;So;0;L;;;;;N;;;;;
+1D993;SIGNWRITING MOVEMENT-WALLPLANE HUMP MEDIUM;So;0;L;;;;;N;;;;;
+1D994;SIGNWRITING MOVEMENT-WALLPLANE HUMP LARGE;So;0;L;;;;;N;;;;;
+1D995;SIGNWRITING MOVEMENT-WALLPLANE LOOP SMALL;So;0;L;;;;;N;;;;;
+1D996;SIGNWRITING MOVEMENT-WALLPLANE LOOP MEDIUM;So;0;L;;;;;N;;;;;
+1D997;SIGNWRITING MOVEMENT-WALLPLANE LOOP LARGE;So;0;L;;;;;N;;;;;
+1D998;SIGNWRITING MOVEMENT-WALLPLANE LOOP SMALL DOUBLE;So;0;L;;;;;N;;;;;
+1D999;SIGNWRITING MOVEMENT-WALLPLANE WAVE CURVE DOUBLE SMALL;So;0;L;;;;;N;;;;;
+1D99A;SIGNWRITING MOVEMENT-WALLPLANE WAVE CURVE DOUBLE MEDIUM;So;0;L;;;;;N;;;;;
+1D99B;SIGNWRITING MOVEMENT-WALLPLANE WAVE CURVE DOUBLE LARGE;So;0;L;;;;;N;;;;;
+1D99C;SIGNWRITING MOVEMENT-WALLPLANE WAVE CURVE TRIPLE SMALL;So;0;L;;;;;N;;;;;
+1D99D;SIGNWRITING MOVEMENT-WALLPLANE WAVE CURVE TRIPLE MEDIUM;So;0;L;;;;;N;;;;;
+1D99E;SIGNWRITING MOVEMENT-WALLPLANE WAVE CURVE TRIPLE LARGE;So;0;L;;;;;N;;;;;
+1D99F;SIGNWRITING MOVEMENT-WALLPLANE CURVE THEN STRAIGHT;So;0;L;;;;;N;;;;;
+1D9A0;SIGNWRITING MOVEMENT-WALLPLANE CURVED CROSS SMALL;So;0;L;;;;;N;;;;;
+1D9A1;SIGNWRITING MOVEMENT-WALLPLANE CURVED CROSS MEDIUM;So;0;L;;;;;N;;;;;
+1D9A2;SIGNWRITING ROTATION-WALLPLANE SINGLE;So;0;L;;;;;N;;;;;
+1D9A3;SIGNWRITING ROTATION-WALLPLANE DOUBLE;So;0;L;;;;;N;;;;;
+1D9A4;SIGNWRITING ROTATION-WALLPLANE ALTERNATE;So;0;L;;;;;N;;;;;
+1D9A5;SIGNWRITING MOVEMENT-WALLPLANE SHAKING;So;0;L;;;;;N;;;;;
+1D9A6;SIGNWRITING MOVEMENT-WALLPLANE CURVE HITTING FRONT WALL;So;0;L;;;;;N;;;;;
+1D9A7;SIGNWRITING MOVEMENT-WALLPLANE HUMP HITTING FRONT WALL;So;0;L;;;;;N;;;;;
+1D9A8;SIGNWRITING MOVEMENT-WALLPLANE LOOP HITTING FRONT WALL;So;0;L;;;;;N;;;;;
+1D9A9;SIGNWRITING MOVEMENT-WALLPLANE WAVE HITTING FRONT WALL;So;0;L;;;;;N;;;;;
+1D9AA;SIGNWRITING ROTATION-WALLPLANE SINGLE HITTING FRONT WALL;So;0;L;;;;;N;;;;;
+1D9AB;SIGNWRITING ROTATION-WALLPLANE DOUBLE HITTING FRONT WALL;So;0;L;;;;;N;;;;;
+1D9AC;SIGNWRITING ROTATION-WALLPLANE ALTERNATING HITTING FRONT WALL;So;0;L;;;;;N;;;;;
+1D9AD;SIGNWRITING MOVEMENT-WALLPLANE CURVE HITTING CHEST;So;0;L;;;;;N;;;;;
+1D9AE;SIGNWRITING MOVEMENT-WALLPLANE HUMP HITTING CHEST;So;0;L;;;;;N;;;;;
+1D9AF;SIGNWRITING MOVEMENT-WALLPLANE LOOP HITTING CHEST;So;0;L;;;;;N;;;;;
+1D9B0;SIGNWRITING MOVEMENT-WALLPLANE WAVE HITTING CHEST;So;0;L;;;;;N;;;;;
+1D9B1;SIGNWRITING ROTATION-WALLPLANE SINGLE HITTING CHEST;So;0;L;;;;;N;;;;;
+1D9B2;SIGNWRITING ROTATION-WALLPLANE DOUBLE HITTING CHEST;So;0;L;;;;;N;;;;;
+1D9B3;SIGNWRITING ROTATION-WALLPLANE ALTERNATING HITTING CHEST;So;0;L;;;;;N;;;;;
+1D9B4;SIGNWRITING MOVEMENT-WALLPLANE WAVE DIAGONAL PATH SMALL;So;0;L;;;;;N;;;;;
+1D9B5;SIGNWRITING MOVEMENT-WALLPLANE WAVE DIAGONAL PATH MEDIUM;So;0;L;;;;;N;;;;;
+1D9B6;SIGNWRITING MOVEMENT-WALLPLANE WAVE DIAGONAL PATH LARGE;So;0;L;;;;;N;;;;;
+1D9B7;SIGNWRITING MOVEMENT-FLOORPLANE CURVE HITTING CEILING SMALL;So;0;L;;;;;N;;;;;
+1D9B8;SIGNWRITING MOVEMENT-FLOORPLANE CURVE HITTING CEILING LARGE;So;0;L;;;;;N;;;;;
+1D9B9;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING CEILING SMALL DOUBLE;So;0;L;;;;;N;;;;;
+1D9BA;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING CEILING LARGE DOUBLE;So;0;L;;;;;N;;;;;
+1D9BB;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING CEILING SMALL TRIPLE;So;0;L;;;;;N;;;;;
+1D9BC;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING CEILING LARGE TRIPLE;So;0;L;;;;;N;;;;;
+1D9BD;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING CEILING SMALL SINGLE;So;0;L;;;;;N;;;;;
+1D9BE;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING CEILING LARGE SINGLE;So;0;L;;;;;N;;;;;
+1D9BF;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING CEILING SMALL DOUBLE;So;0;L;;;;;N;;;;;
+1D9C0;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING CEILING LARGE DOUBLE;So;0;L;;;;;N;;;;;
+1D9C1;SIGNWRITING MOVEMENT-FLOORPLANE WAVE HITTING CEILING SMALL;So;0;L;;;;;N;;;;;
+1D9C2;SIGNWRITING MOVEMENT-FLOORPLANE WAVE HITTING CEILING LARGE;So;0;L;;;;;N;;;;;
+1D9C3;SIGNWRITING ROTATION-FLOORPLANE SINGLE HITTING CEILING;So;0;L;;;;;N;;;;;
+1D9C4;SIGNWRITING ROTATION-FLOORPLANE DOUBLE HITTING CEILING;So;0;L;;;;;N;;;;;
+1D9C5;SIGNWRITING ROTATION-FLOORPLANE ALTERNATING HITTING CEILING;So;0;L;;;;;N;;;;;
+1D9C6;SIGNWRITING MOVEMENT-FLOORPLANE CURVE HITTING FLOOR SMALL;So;0;L;;;;;N;;;;;
+1D9C7;SIGNWRITING MOVEMENT-FLOORPLANE CURVE HITTING FLOOR LARGE;So;0;L;;;;;N;;;;;
+1D9C8;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING FLOOR SMALL DOUBLE;So;0;L;;;;;N;;;;;
+1D9C9;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING FLOOR LARGE DOUBLE;So;0;L;;;;;N;;;;;
+1D9CA;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING FLOOR TRIPLE SMALL TRIPLE;So;0;L;;;;;N;;;;;
+1D9CB;SIGNWRITING MOVEMENT-FLOORPLANE HUMP HITTING FLOOR TRIPLE LARGE TRIPLE;So;0;L;;;;;N;;;;;
+1D9CC;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING FLOOR SMALL SINGLE;So;0;L;;;;;N;;;;;
+1D9CD;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING FLOOR LARGE SINGLE;So;0;L;;;;;N;;;;;
+1D9CE;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING FLOOR SMALL DOUBLE;So;0;L;;;;;N;;;;;
+1D9CF;SIGNWRITING MOVEMENT-FLOORPLANE LOOP HITTING FLOOR LARGE DOUBLE;So;0;L;;;;;N;;;;;
+1D9D0;SIGNWRITING MOVEMENT-FLOORPLANE WAVE HITTING FLOOR SMALL;So;0;L;;;;;N;;;;;
+1D9D1;SIGNWRITING MOVEMENT-FLOORPLANE WAVE HITTING FLOOR LARGE;So;0;L;;;;;N;;;;;
+1D9D2;SIGNWRITING ROTATION-FLOORPLANE SINGLE HITTING FLOOR;So;0;L;;;;;N;;;;;
+1D9D3;SIGNWRITING ROTATION-FLOORPLANE DOUBLE HITTING FLOOR;So;0;L;;;;;N;;;;;
+1D9D4;SIGNWRITING ROTATION-FLOORPLANE ALTERNATING HITTING FLOOR;So;0;L;;;;;N;;;;;
+1D9D5;SIGNWRITING MOVEMENT-FLOORPLANE CURVE SMALL;So;0;L;;;;;N;;;;;
+1D9D6;SIGNWRITING MOVEMENT-FLOORPLANE CURVE MEDIUM;So;0;L;;;;;N;;;;;
+1D9D7;SIGNWRITING MOVEMENT-FLOORPLANE CURVE LARGE;So;0;L;;;;;N;;;;;
+1D9D8;SIGNWRITING MOVEMENT-FLOORPLANE CURVE LARGEST;So;0;L;;;;;N;;;;;
+1D9D9;SIGNWRITING MOVEMENT-FLOORPLANE CURVE COMBINED;So;0;L;;;;;N;;;;;
+1D9DA;SIGNWRITING MOVEMENT-FLOORPLANE HUMP SMALL;So;0;L;;;;;N;;;;;
+1D9DB;SIGNWRITING MOVEMENT-FLOORPLANE LOOP SMALL;So;0;L;;;;;N;;;;;
+1D9DC;SIGNWRITING MOVEMENT-FLOORPLANE WAVE SNAKE;So;0;L;;;;;N;;;;;
+1D9DD;SIGNWRITING MOVEMENT-FLOORPLANE WAVE SMALL;So;0;L;;;;;N;;;;;
+1D9DE;SIGNWRITING MOVEMENT-FLOORPLANE WAVE LARGE;So;0;L;;;;;N;;;;;
+1D9DF;SIGNWRITING ROTATION-FLOORPLANE SINGLE;So;0;L;;;;;N;;;;;
+1D9E0;SIGNWRITING ROTATION-FLOORPLANE DOUBLE;So;0;L;;;;;N;;;;;
+1D9E1;SIGNWRITING ROTATION-FLOORPLANE ALTERNATING;So;0;L;;;;;N;;;;;
+1D9E2;SIGNWRITING MOVEMENT-FLOORPLANE SHAKING PARALLEL;So;0;L;;;;;N;;;;;
+1D9E3;SIGNWRITING MOVEMENT-WALLPLANE ARM CIRCLE SMALL SINGLE;So;0;L;;;;;N;;;;;
+1D9E4;SIGNWRITING MOVEMENT-WALLPLANE ARM CIRCLE MEDIUM SINGLE;So;0;L;;;;;N;;;;;
+1D9E5;SIGNWRITING MOVEMENT-WALLPLANE ARM CIRCLE SMALL DOUBLE;So;0;L;;;;;N;;;;;
+1D9E6;SIGNWRITING MOVEMENT-WALLPLANE ARM CIRCLE MEDIUM DOUBLE;So;0;L;;;;;N;;;;;
+1D9E7;SIGNWRITING MOVEMENT-FLOORPLANE ARM CIRCLE HITTING WALL SMALL SINGLE;So;0;L;;;;;N;;;;;
+1D9E8;SIGNWRITING MOVEMENT-FLOORPLANE ARM CIRCLE HITTING WALL MEDIUM SINGLE;So;0;L;;;;;N;;;;;
+1D9E9;SIGNWRITING MOVEMENT-FLOORPLANE ARM CIRCLE HITTING WALL LARGE SINGLE;So;0;L;;;;;N;;;;;
+1D9EA;SIGNWRITING MOVEMENT-FLOORPLANE ARM CIRCLE HITTING WALL SMALL DOUBLE;So;0;L;;;;;N;;;;;
+1D9EB;SIGNWRITING MOVEMENT-FLOORPLANE ARM CIRCLE HITTING WALL MEDIUM DOUBLE;So;0;L;;;;;N;;;;;
+1D9EC;SIGNWRITING MOVEMENT-FLOORPLANE ARM CIRCLE HITTING WALL LARGE DOUBLE;So;0;L;;;;;N;;;;;
+1D9ED;SIGNWRITING MOVEMENT-WALLPLANE WRIST CIRCLE FRONT SINGLE;So;0;L;;;;;N;;;;;
+1D9EE;SIGNWRITING MOVEMENT-WALLPLANE WRIST CIRCLE FRONT DOUBLE;So;0;L;;;;;N;;;;;
+1D9EF;SIGNWRITING MOVEMENT-FLOORPLANE WRIST CIRCLE HITTING WALL SINGLE;So;0;L;;;;;N;;;;;
+1D9F0;SIGNWRITING MOVEMENT-FLOORPLANE WRIST CIRCLE HITTING WALL DOUBLE;So;0;L;;;;;N;;;;;
+1D9F1;SIGNWRITING MOVEMENT-WALLPLANE FINGER CIRCLES SINGLE;So;0;L;;;;;N;;;;;
+1D9F2;SIGNWRITING MOVEMENT-WALLPLANE FINGER CIRCLES DOUBLE;So;0;L;;;;;N;;;;;
+1D9F3;SIGNWRITING MOVEMENT-FLOORPLANE FINGER CIRCLES HITTING WALL SINGLE;So;0;L;;;;;N;;;;;
+1D9F4;SIGNWRITING MOVEMENT-FLOORPLANE FINGER CIRCLES HITTING WALL DOUBLE;So;0;L;;;;;N;;;;;
+1D9F5;SIGNWRITING DYNAMIC ARROWHEAD SMALL;So;0;L;;;;;N;;;;;
+1D9F6;SIGNWRITING DYNAMIC ARROWHEAD LARGE;So;0;L;;;;;N;;;;;
+1D9F7;SIGNWRITING DYNAMIC FAST;So;0;L;;;;;N;;;;;
+1D9F8;SIGNWRITING DYNAMIC SLOW;So;0;L;;;;;N;;;;;
+1D9F9;SIGNWRITING DYNAMIC TENSE;So;0;L;;;;;N;;;;;
+1D9FA;SIGNWRITING DYNAMIC RELAXED;So;0;L;;;;;N;;;;;
+1D9FB;SIGNWRITING DYNAMIC SIMULTANEOUS;So;0;L;;;;;N;;;;;
+1D9FC;SIGNWRITING DYNAMIC SIMULTANEOUS ALTERNATING;So;0;L;;;;;N;;;;;
+1D9FD;SIGNWRITING DYNAMIC EVERY OTHER TIME;So;0;L;;;;;N;;;;;
+1D9FE;SIGNWRITING DYNAMIC GRADUAL;So;0;L;;;;;N;;;;;
+1D9FF;SIGNWRITING HEAD;So;0;L;;;;;N;;;;;
+1DA00;SIGNWRITING HEAD RIM;Mn;0;NSM;;;;;N;;;;;
+1DA01;SIGNWRITING HEAD MOVEMENT-WALLPLANE STRAIGHT;Mn;0;NSM;;;;;N;;;;;
+1DA02;SIGNWRITING HEAD MOVEMENT-WALLPLANE TILT;Mn;0;NSM;;;;;N;;;;;
+1DA03;SIGNWRITING HEAD MOVEMENT-FLOORPLANE STRAIGHT;Mn;0;NSM;;;;;N;;;;;
+1DA04;SIGNWRITING HEAD MOVEMENT-WALLPLANE CURVE;Mn;0;NSM;;;;;N;;;;;
+1DA05;SIGNWRITING HEAD MOVEMENT-FLOORPLANE CURVE;Mn;0;NSM;;;;;N;;;;;
+1DA06;SIGNWRITING HEAD MOVEMENT CIRCLE;Mn;0;NSM;;;;;N;;;;;
+1DA07;SIGNWRITING FACE DIRECTION POSITION NOSE FORWARD TILTING;Mn;0;NSM;;;;;N;;;;;
+1DA08;SIGNWRITING FACE DIRECTION POSITION NOSE UP OR DOWN;Mn;0;NSM;;;;;N;;;;;
+1DA09;SIGNWRITING FACE DIRECTION POSITION NOSE UP OR DOWN TILTING;Mn;0;NSM;;;;;N;;;;;
+1DA0A;SIGNWRITING EYEBROWS STRAIGHT UP;Mn;0;NSM;;;;;N;;;;;
+1DA0B;SIGNWRITING EYEBROWS STRAIGHT NEUTRAL;Mn;0;NSM;;;;;N;;;;;
+1DA0C;SIGNWRITING EYEBROWS STRAIGHT DOWN;Mn;0;NSM;;;;;N;;;;;
+1DA0D;SIGNWRITING DREAMY EYEBROWS NEUTRAL DOWN;Mn;0;NSM;;;;;N;;;;;
+1DA0E;SIGNWRITING DREAMY EYEBROWS DOWN NEUTRAL;Mn;0;NSM;;;;;N;;;;;
+1DA0F;SIGNWRITING DREAMY EYEBROWS UP NEUTRAL;Mn;0;NSM;;;;;N;;;;;
+1DA10;SIGNWRITING DREAMY EYEBROWS NEUTRAL UP;Mn;0;NSM;;;;;N;;;;;
+1DA11;SIGNWRITING FOREHEAD NEUTRAL;Mn;0;NSM;;;;;N;;;;;
+1DA12;SIGNWRITING FOREHEAD CONTACT;Mn;0;NSM;;;;;N;;;;;
+1DA13;SIGNWRITING FOREHEAD WRINKLED;Mn;0;NSM;;;;;N;;;;;
+1DA14;SIGNWRITING EYES OPEN;Mn;0;NSM;;;;;N;;;;;
+1DA15;SIGNWRITING EYES SQUEEZED;Mn;0;NSM;;;;;N;;;;;
+1DA16;SIGNWRITING EYES CLOSED;Mn;0;NSM;;;;;N;;;;;
+1DA17;SIGNWRITING EYE BLINK SINGLE;Mn;0;NSM;;;;;N;;;;;
+1DA18;SIGNWRITING EYE BLINK MULTIPLE;Mn;0;NSM;;;;;N;;;;;
+1DA19;SIGNWRITING EYES HALF OPEN;Mn;0;NSM;;;;;N;;;;;
+1DA1A;SIGNWRITING EYES WIDE OPEN;Mn;0;NSM;;;;;N;;;;;
+1DA1B;SIGNWRITING EYES HALF CLOSED;Mn;0;NSM;;;;;N;;;;;
+1DA1C;SIGNWRITING EYES WIDENING MOVEMENT;Mn;0;NSM;;;;;N;;;;;
+1DA1D;SIGNWRITING EYE WINK;Mn;0;NSM;;;;;N;;;;;
+1DA1E;SIGNWRITING EYELASHES UP;Mn;0;NSM;;;;;N;;;;;
+1DA1F;SIGNWRITING EYELASHES DOWN;Mn;0;NSM;;;;;N;;;;;
+1DA20;SIGNWRITING EYELASHES FLUTTERING;Mn;0;NSM;;;;;N;;;;;
+1DA21;SIGNWRITING EYEGAZE-WALLPLANE STRAIGHT;Mn;0;NSM;;;;;N;;;;;
+1DA22;SIGNWRITING EYEGAZE-WALLPLANE STRAIGHT DOUBLE;Mn;0;NSM;;;;;N;;;;;
+1DA23;SIGNWRITING EYEGAZE-WALLPLANE STRAIGHT ALTERNATING;Mn;0;NSM;;;;;N;;;;;
+1DA24;SIGNWRITING EYEGAZE-FLOORPLANE STRAIGHT;Mn;0;NSM;;;;;N;;;;;
+1DA25;SIGNWRITING EYEGAZE-FLOORPLANE STRAIGHT DOUBLE;Mn;0;NSM;;;;;N;;;;;
+1DA26;SIGNWRITING EYEGAZE-FLOORPLANE STRAIGHT ALTERNATING;Mn;0;NSM;;;;;N;;;;;
+1DA27;SIGNWRITING EYEGAZE-WALLPLANE CURVED;Mn;0;NSM;;;;;N;;;;;
+1DA28;SIGNWRITING EYEGAZE-FLOORPLANE CURVED;Mn;0;NSM;;;;;N;;;;;
+1DA29;SIGNWRITING EYEGAZE-WALLPLANE CIRCLING;Mn;0;NSM;;;;;N;;;;;
+1DA2A;SIGNWRITING CHEEKS PUFFED;Mn;0;NSM;;;;;N;;;;;
+1DA2B;SIGNWRITING CHEEKS NEUTRAL;Mn;0;NSM;;;;;N;;;;;
+1DA2C;SIGNWRITING CHEEKS SUCKED;Mn;0;NSM;;;;;N;;;;;
+1DA2D;SIGNWRITING TENSE CHEEKS HIGH;Mn;0;NSM;;;;;N;;;;;
+1DA2E;SIGNWRITING TENSE CHEEKS MIDDLE;Mn;0;NSM;;;;;N;;;;;
+1DA2F;SIGNWRITING TENSE CHEEKS LOW;Mn;0;NSM;;;;;N;;;;;
+1DA30;SIGNWRITING EARS;Mn;0;NSM;;;;;N;;;;;
+1DA31;SIGNWRITING NOSE NEUTRAL;Mn;0;NSM;;;;;N;;;;;
+1DA32;SIGNWRITING NOSE CONTACT;Mn;0;NSM;;;;;N;;;;;
+1DA33;SIGNWRITING NOSE WRINKLES;Mn;0;NSM;;;;;N;;;;;
+1DA34;SIGNWRITING NOSE WIGGLES;Mn;0;NSM;;;;;N;;;;;
+1DA35;SIGNWRITING AIR BLOWING OUT;Mn;0;NSM;;;;;N;;;;;
+1DA36;SIGNWRITING AIR SUCKING IN;Mn;0;NSM;;;;;N;;;;;
+1DA37;SIGNWRITING AIR BLOW SMALL ROTATIONS;So;0;L;;;;;N;;;;;
+1DA38;SIGNWRITING AIR SUCK SMALL ROTATIONS;So;0;L;;;;;N;;;;;
+1DA39;SIGNWRITING BREATH INHALE;So;0;L;;;;;N;;;;;
+1DA3A;SIGNWRITING BREATH EXHALE;So;0;L;;;;;N;;;;;
+1DA3B;SIGNWRITING MOUTH CLOSED NEUTRAL;Mn;0;NSM;;;;;N;;;;;
+1DA3C;SIGNWRITING MOUTH CLOSED FORWARD;Mn;0;NSM;;;;;N;;;;;
+1DA3D;SIGNWRITING MOUTH CLOSED CONTACT;Mn;0;NSM;;;;;N;;;;;
+1DA3E;SIGNWRITING MOUTH SMILE;Mn;0;NSM;;;;;N;;;;;
+1DA3F;SIGNWRITING MOUTH SMILE WRINKLED;Mn;0;NSM;;;;;N;;;;;
+1DA40;SIGNWRITING MOUTH SMILE OPEN;Mn;0;NSM;;;;;N;;;;;
+1DA41;SIGNWRITING MOUTH FROWN;Mn;0;NSM;;;;;N;;;;;
+1DA42;SIGNWRITING MOUTH FROWN WRINKLED;Mn;0;NSM;;;;;N;;;;;
+1DA43;SIGNWRITING MOUTH FROWN OPEN;Mn;0;NSM;;;;;N;;;;;
+1DA44;SIGNWRITING MOUTH OPEN CIRCLE;Mn;0;NSM;;;;;N;;;;;
+1DA45;SIGNWRITING MOUTH OPEN FORWARD;Mn;0;NSM;;;;;N;;;;;
+1DA46;SIGNWRITING MOUTH OPEN WRINKLED;Mn;0;NSM;;;;;N;;;;;
+1DA47;SIGNWRITING MOUTH OPEN OVAL;Mn;0;NSM;;;;;N;;;;;
+1DA48;SIGNWRITING MOUTH OPEN OVAL WRINKLED;Mn;0;NSM;;;;;N;;;;;
+1DA49;SIGNWRITING MOUTH OPEN OVAL YAWN;Mn;0;NSM;;;;;N;;;;;
+1DA4A;SIGNWRITING MOUTH OPEN RECTANGLE;Mn;0;NSM;;;;;N;;;;;
+1DA4B;SIGNWRITING MOUTH OPEN RECTANGLE WRINKLED;Mn;0;NSM;;;;;N;;;;;
+1DA4C;SIGNWRITING MOUTH OPEN RECTANGLE YAWN;Mn;0;NSM;;;;;N;;;;;
+1DA4D;SIGNWRITING MOUTH KISS;Mn;0;NSM;;;;;N;;;;;
+1DA4E;SIGNWRITING MOUTH KISS FORWARD;Mn;0;NSM;;;;;N;;;;;
+1DA4F;SIGNWRITING MOUTH KISS WRINKLED;Mn;0;NSM;;;;;N;;;;;
+1DA50;SIGNWRITING MOUTH TENSE;Mn;0;NSM;;;;;N;;;;;
+1DA51;SIGNWRITING MOUTH TENSE FORWARD;Mn;0;NSM;;;;;N;;;;;
+1DA52;SIGNWRITING MOUTH TENSE SUCKED;Mn;0;NSM;;;;;N;;;;;
+1DA53;SIGNWRITING LIPS PRESSED TOGETHER;Mn;0;NSM;;;;;N;;;;;
+1DA54;SIGNWRITING LIP LOWER OVER UPPER;Mn;0;NSM;;;;;N;;;;;
+1DA55;SIGNWRITING LIP UPPER OVER LOWER;Mn;0;NSM;;;;;N;;;;;
+1DA56;SIGNWRITING MOUTH CORNERS;Mn;0;NSM;;;;;N;;;;;
+1DA57;SIGNWRITING MOUTH WRINKLES SINGLE;Mn;0;NSM;;;;;N;;;;;
+1DA58;SIGNWRITING MOUTH WRINKLES DOUBLE;Mn;0;NSM;;;;;N;;;;;
+1DA59;SIGNWRITING TONGUE STICKING OUT FAR;Mn;0;NSM;;;;;N;;;;;
+1DA5A;SIGNWRITING TONGUE LICKING LIPS;Mn;0;NSM;;;;;N;;;;;
+1DA5B;SIGNWRITING TONGUE TIP BETWEEN LIPS;Mn;0;NSM;;;;;N;;;;;
+1DA5C;SIGNWRITING TONGUE TIP TOUCHING INSIDE MOUTH;Mn;0;NSM;;;;;N;;;;;
+1DA5D;SIGNWRITING TONGUE INSIDE MOUTH RELAXED;Mn;0;NSM;;;;;N;;;;;
+1DA5E;SIGNWRITING TONGUE MOVES AGAINST CHEEK;Mn;0;NSM;;;;;N;;;;;
+1DA5F;SIGNWRITING TONGUE CENTRE STICKING OUT;Mn;0;NSM;;;;;N;;;;;
+1DA60;SIGNWRITING TONGUE CENTRE INSIDE MOUTH;Mn;0;NSM;;;;;N;;;;;
+1DA61;SIGNWRITING TEETH;Mn;0;NSM;;;;;N;;;;;
+1DA62;SIGNWRITING TEETH MOVEMENT;Mn;0;NSM;;;;;N;;;;;
+1DA63;SIGNWRITING TEETH ON TONGUE;Mn;0;NSM;;;;;N;;;;;
+1DA64;SIGNWRITING TEETH ON TONGUE MOVEMENT;Mn;0;NSM;;;;;N;;;;;
+1DA65;SIGNWRITING TEETH ON LIPS;Mn;0;NSM;;;;;N;;;;;
+1DA66;SIGNWRITING TEETH ON LIPS MOVEMENT;Mn;0;NSM;;;;;N;;;;;
+1DA67;SIGNWRITING TEETH BITE LIPS;Mn;0;NSM;;;;;N;;;;;
+1DA68;SIGNWRITING MOVEMENT-WALLPLANE JAW;Mn;0;NSM;;;;;N;;;;;
+1DA69;SIGNWRITING MOVEMENT-FLOORPLANE JAW;Mn;0;NSM;;;;;N;;;;;
+1DA6A;SIGNWRITING NECK;Mn;0;NSM;;;;;N;;;;;
+1DA6B;SIGNWRITING HAIR;Mn;0;NSM;;;;;N;;;;;
+1DA6C;SIGNWRITING EXCITEMENT;Mn;0;NSM;;;;;N;;;;;
+1DA6D;SIGNWRITING SHOULDER HIP SPINE;So;0;L;;;;;N;;;;;
+1DA6E;SIGNWRITING SHOULDER HIP POSITIONS;So;0;L;;;;;N;;;;;
+1DA6F;SIGNWRITING WALLPLANE SHOULDER HIP MOVE;So;0;L;;;;;N;;;;;
+1DA70;SIGNWRITING FLOORPLANE SHOULDER HIP MOVE;So;0;L;;;;;N;;;;;
+1DA71;SIGNWRITING SHOULDER TILTING FROM WAIST;So;0;L;;;;;N;;;;;
+1DA72;SIGNWRITING TORSO-WALLPLANE STRAIGHT STRETCH;So;0;L;;;;;N;;;;;
+1DA73;SIGNWRITING TORSO-WALLPLANE CURVED BEND;So;0;L;;;;;N;;;;;
+1DA74;SIGNWRITING TORSO-FLOORPLANE TWISTING;So;0;L;;;;;N;;;;;
+1DA75;SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS;Mn;0;NSM;;;;;N;;;;;
+1DA76;SIGNWRITING LIMB COMBINATION;So;0;L;;;;;N;;;;;
+1DA77;SIGNWRITING LIMB LENGTH-1;So;0;L;;;;;N;;;;;
+1DA78;SIGNWRITING LIMB LENGTH-2;So;0;L;;;;;N;;;;;
+1DA79;SIGNWRITING LIMB LENGTH-3;So;0;L;;;;;N;;;;;
+1DA7A;SIGNWRITING LIMB LENGTH-4;So;0;L;;;;;N;;;;;
+1DA7B;SIGNWRITING LIMB LENGTH-5;So;0;L;;;;;N;;;;;
+1DA7C;SIGNWRITING LIMB LENGTH-6;So;0;L;;;;;N;;;;;
+1DA7D;SIGNWRITING LIMB LENGTH-7;So;0;L;;;;;N;;;;;
+1DA7E;SIGNWRITING FINGER;So;0;L;;;;;N;;;;;
+1DA7F;SIGNWRITING LOCATION-WALLPLANE SPACE;So;0;L;;;;;N;;;;;
+1DA80;SIGNWRITING LOCATION-FLOORPLANE SPACE;So;0;L;;;;;N;;;;;
+1DA81;SIGNWRITING LOCATION HEIGHT;So;0;L;;;;;N;;;;;
+1DA82;SIGNWRITING LOCATION WIDTH;So;0;L;;;;;N;;;;;
+1DA83;SIGNWRITING LOCATION DEPTH;So;0;L;;;;;N;;;;;
+1DA84;SIGNWRITING LOCATION HEAD NECK;Mn;0;NSM;;;;;N;;;;;
+1DA85;SIGNWRITING LOCATION TORSO;So;0;L;;;;;N;;;;;
+1DA86;SIGNWRITING LOCATION LIMBS DIGITS;So;0;L;;;;;N;;;;;
+1DA87;SIGNWRITING COMMA;Po;0;L;;;;;N;;;;;
+1DA88;SIGNWRITING FULL STOP;Po;0;L;;;;;N;;;;;
+1DA89;SIGNWRITING SEMICOLON;Po;0;L;;;;;N;;;;;
+1DA8A;SIGNWRITING COLON;Po;0;L;;;;;N;;;;;
+1DA8B;SIGNWRITING PARENTHESIS;Po;0;L;;;;;N;;;;;
+1DA9B;SIGNWRITING FILL MODIFIER-2;Mn;0;NSM;;;;;N;;;;;
+1DA9C;SIGNWRITING FILL MODIFIER-3;Mn;0;NSM;;;;;N;;;;;
+1DA9D;SIGNWRITING FILL MODIFIER-4;Mn;0;NSM;;;;;N;;;;;
+1DA9E;SIGNWRITING FILL MODIFIER-5;Mn;0;NSM;;;;;N;;;;;
+1DA9F;SIGNWRITING FILL MODIFIER-6;Mn;0;NSM;;;;;N;;;;;
+1DAA1;SIGNWRITING ROTATION MODIFIER-2;Mn;0;NSM;;;;;N;;;;;
+1DAA2;SIGNWRITING ROTATION MODIFIER-3;Mn;0;NSM;;;;;N;;;;;
+1DAA3;SIGNWRITING ROTATION MODIFIER-4;Mn;0;NSM;;;;;N;;;;;
+1DAA4;SIGNWRITING ROTATION MODIFIER-5;Mn;0;NSM;;;;;N;;;;;
+1DAA5;SIGNWRITING ROTATION MODIFIER-6;Mn;0;NSM;;;;;N;;;;;
+1DAA6;SIGNWRITING ROTATION MODIFIER-7;Mn;0;NSM;;;;;N;;;;;
+1DAA7;SIGNWRITING ROTATION MODIFIER-8;Mn;0;NSM;;;;;N;;;;;
+1DAA8;SIGNWRITING ROTATION MODIFIER-9;Mn;0;NSM;;;;;N;;;;;
+1DAA9;SIGNWRITING ROTATION MODIFIER-10;Mn;0;NSM;;;;;N;;;;;
+1DAAA;SIGNWRITING ROTATION MODIFIER-11;Mn;0;NSM;;;;;N;;;;;
+1DAAB;SIGNWRITING ROTATION MODIFIER-12;Mn;0;NSM;;;;;N;;;;;
+1DAAC;SIGNWRITING ROTATION MODIFIER-13;Mn;0;NSM;;;;;N;;;;;
+1DAAD;SIGNWRITING ROTATION MODIFIER-14;Mn;0;NSM;;;;;N;;;;;
+1DAAE;SIGNWRITING ROTATION MODIFIER-15;Mn;0;NSM;;;;;N;;;;;
+1DAAF;SIGNWRITING ROTATION MODIFIER-16;Mn;0;NSM;;;;;N;;;;;
+1E000;COMBINING GLAGOLITIC LETTER AZU;Mn;230;NSM;;;;;N;;;;;
+1E001;COMBINING GLAGOLITIC LETTER BUKY;Mn;230;NSM;;;;;N;;;;;
+1E002;COMBINING GLAGOLITIC LETTER VEDE;Mn;230;NSM;;;;;N;;;;;
+1E003;COMBINING GLAGOLITIC LETTER GLAGOLI;Mn;230;NSM;;;;;N;;;;;
+1E004;COMBINING GLAGOLITIC LETTER DOBRO;Mn;230;NSM;;;;;N;;;;;
+1E005;COMBINING GLAGOLITIC LETTER YESTU;Mn;230;NSM;;;;;N;;;;;
+1E006;COMBINING GLAGOLITIC LETTER ZHIVETE;Mn;230;NSM;;;;;N;;;;;
+1E008;COMBINING GLAGOLITIC LETTER ZEMLJA;Mn;230;NSM;;;;;N;;;;;
+1E009;COMBINING GLAGOLITIC LETTER IZHE;Mn;230;NSM;;;;;N;;;;;
+1E00A;COMBINING GLAGOLITIC LETTER INITIAL IZHE;Mn;230;NSM;;;;;N;;;;;
+1E00B;COMBINING GLAGOLITIC LETTER I;Mn;230;NSM;;;;;N;;;;;
+1E00C;COMBINING GLAGOLITIC LETTER DJERVI;Mn;230;NSM;;;;;N;;;;;
+1E00D;COMBINING GLAGOLITIC LETTER KAKO;Mn;230;NSM;;;;;N;;;;;
+1E00E;COMBINING GLAGOLITIC LETTER LJUDIJE;Mn;230;NSM;;;;;N;;;;;
+1E00F;COMBINING GLAGOLITIC LETTER MYSLITE;Mn;230;NSM;;;;;N;;;;;
+1E010;COMBINING GLAGOLITIC LETTER NASHI;Mn;230;NSM;;;;;N;;;;;
+1E011;COMBINING GLAGOLITIC LETTER ONU;Mn;230;NSM;;;;;N;;;;;
+1E012;COMBINING GLAGOLITIC LETTER POKOJI;Mn;230;NSM;;;;;N;;;;;
+1E013;COMBINING GLAGOLITIC LETTER RITSI;Mn;230;NSM;;;;;N;;;;;
+1E014;COMBINING GLAGOLITIC LETTER SLOVO;Mn;230;NSM;;;;;N;;;;;
+1E015;COMBINING GLAGOLITIC LETTER TVRIDO;Mn;230;NSM;;;;;N;;;;;
+1E016;COMBINING GLAGOLITIC LETTER UKU;Mn;230;NSM;;;;;N;;;;;
+1E017;COMBINING GLAGOLITIC LETTER FRITU;Mn;230;NSM;;;;;N;;;;;
+1E018;COMBINING GLAGOLITIC LETTER HERU;Mn;230;NSM;;;;;N;;;;;
+1E01B;COMBINING GLAGOLITIC LETTER SHTA;Mn;230;NSM;;;;;N;;;;;
+1E01C;COMBINING GLAGOLITIC LETTER TSI;Mn;230;NSM;;;;;N;;;;;
+1E01D;COMBINING GLAGOLITIC LETTER CHRIVI;Mn;230;NSM;;;;;N;;;;;
+1E01E;COMBINING GLAGOLITIC LETTER SHA;Mn;230;NSM;;;;;N;;;;;
+1E01F;COMBINING GLAGOLITIC LETTER YERU;Mn;230;NSM;;;;;N;;;;;
+1E020;COMBINING GLAGOLITIC LETTER YERI;Mn;230;NSM;;;;;N;;;;;
+1E021;COMBINING GLAGOLITIC LETTER YATI;Mn;230;NSM;;;;;N;;;;;
+1E023;COMBINING GLAGOLITIC LETTER YU;Mn;230;NSM;;;;;N;;;;;
+1E024;COMBINING GLAGOLITIC LETTER SMALL YUS;Mn;230;NSM;;;;;N;;;;;
+1E026;COMBINING GLAGOLITIC LETTER YO;Mn;230;NSM;;;;;N;;;;;
+1E027;COMBINING GLAGOLITIC LETTER IOTATED SMALL YUS;Mn;230;NSM;;;;;N;;;;;
+1E028;COMBINING GLAGOLITIC LETTER BIG YUS;Mn;230;NSM;;;;;N;;;;;
+1E029;COMBINING GLAGOLITIC LETTER IOTATED BIG YUS;Mn;230;NSM;;;;;N;;;;;
+1E02A;COMBINING GLAGOLITIC LETTER FITA;Mn;230;NSM;;;;;N;;;;;
+1E100;NYIAKENG PUACHUE HMONG LETTER MA;Lo;0;L;;;;;N;;;;;
+1E101;NYIAKENG PUACHUE HMONG LETTER TSA;Lo;0;L;;;;;N;;;;;
+1E102;NYIAKENG PUACHUE HMONG LETTER NTA;Lo;0;L;;;;;N;;;;;
+1E103;NYIAKENG PUACHUE HMONG LETTER TA;Lo;0;L;;;;;N;;;;;
+1E104;NYIAKENG PUACHUE HMONG LETTER HA;Lo;0;L;;;;;N;;;;;
+1E105;NYIAKENG PUACHUE HMONG LETTER NA;Lo;0;L;;;;;N;;;;;
+1E106;NYIAKENG PUACHUE HMONG LETTER XA;Lo;0;L;;;;;N;;;;;
+1E107;NYIAKENG PUACHUE HMONG LETTER NKA;Lo;0;L;;;;;N;;;;;
+1E108;NYIAKENG PUACHUE HMONG LETTER CA;Lo;0;L;;;;;N;;;;;
+1E109;NYIAKENG PUACHUE HMONG LETTER LA;Lo;0;L;;;;;N;;;;;
+1E10A;NYIAKENG PUACHUE HMONG LETTER SA;Lo;0;L;;;;;N;;;;;
+1E10B;NYIAKENG PUACHUE HMONG LETTER ZA;Lo;0;L;;;;;N;;;;;
+1E10C;NYIAKENG PUACHUE HMONG LETTER NCA;Lo;0;L;;;;;N;;;;;
+1E10D;NYIAKENG PUACHUE HMONG LETTER NTSA;Lo;0;L;;;;;N;;;;;
+1E10E;NYIAKENG PUACHUE HMONG LETTER KA;Lo;0;L;;;;;N;;;;;
+1E10F;NYIAKENG PUACHUE HMONG LETTER DA;Lo;0;L;;;;;N;;;;;
+1E110;NYIAKENG PUACHUE HMONG LETTER NYA;Lo;0;L;;;;;N;;;;;
+1E111;NYIAKENG PUACHUE HMONG LETTER NRA;Lo;0;L;;;;;N;;;;;
+1E112;NYIAKENG PUACHUE HMONG LETTER VA;Lo;0;L;;;;;N;;;;;
+1E113;NYIAKENG PUACHUE HMONG LETTER NTXA;Lo;0;L;;;;;N;;;;;
+1E114;NYIAKENG PUACHUE HMONG LETTER TXA;Lo;0;L;;;;;N;;;;;
+1E115;NYIAKENG PUACHUE HMONG LETTER FA;Lo;0;L;;;;;N;;;;;
+1E116;NYIAKENG PUACHUE HMONG LETTER RA;Lo;0;L;;;;;N;;;;;
+1E117;NYIAKENG PUACHUE HMONG LETTER QA;Lo;0;L;;;;;N;;;;;
+1E118;NYIAKENG PUACHUE HMONG LETTER YA;Lo;0;L;;;;;N;;;;;
+1E119;NYIAKENG PUACHUE HMONG LETTER NQA;Lo;0;L;;;;;N;;;;;
+1E11A;NYIAKENG PUACHUE HMONG LETTER PA;Lo;0;L;;;;;N;;;;;
+1E11B;NYIAKENG PUACHUE HMONG LETTER XYA;Lo;0;L;;;;;N;;;;;
+1E11C;NYIAKENG PUACHUE HMONG LETTER NPA;Lo;0;L;;;;;N;;;;;
+1E11D;NYIAKENG PUACHUE HMONG LETTER DLA;Lo;0;L;;;;;N;;;;;
+1E11E;NYIAKENG PUACHUE HMONG LETTER NPLA;Lo;0;L;;;;;N;;;;;
+1E11F;NYIAKENG PUACHUE HMONG LETTER HAH;Lo;0;L;;;;;N;;;;;
+1E120;NYIAKENG PUACHUE HMONG LETTER MLA;Lo;0;L;;;;;N;;;;;
+1E121;NYIAKENG PUACHUE HMONG LETTER PLA;Lo;0;L;;;;;N;;;;;
+1E122;NYIAKENG PUACHUE HMONG LETTER GA;Lo;0;L;;;;;N;;;;;
+1E123;NYIAKENG PUACHUE HMONG LETTER RRA;Lo;0;L;;;;;N;;;;;
+1E124;NYIAKENG PUACHUE HMONG LETTER A;Lo;0;L;;;;;N;;;;;
+1E125;NYIAKENG PUACHUE HMONG LETTER AA;Lo;0;L;;;;;N;;;;;
+1E126;NYIAKENG PUACHUE HMONG LETTER I;Lo;0;L;;;;;N;;;;;
+1E127;NYIAKENG PUACHUE HMONG LETTER U;Lo;0;L;;;;;N;;;;;
+1E128;NYIAKENG PUACHUE HMONG LETTER O;Lo;0;L;;;;;N;;;;;
+1E129;NYIAKENG PUACHUE HMONG LETTER OO;Lo;0;L;;;;;N;;;;;
+1E12A;NYIAKENG PUACHUE HMONG LETTER E;Lo;0;L;;;;;N;;;;;
+1E12B;NYIAKENG PUACHUE HMONG LETTER EE;Lo;0;L;;;;;N;;;;;
+1E12C;NYIAKENG PUACHUE HMONG LETTER W;Lo;0;L;;;;;N;;;;;
+1E130;NYIAKENG PUACHUE HMONG TONE-B;Mn;230;NSM;;;;;N;;;;;
+1E131;NYIAKENG PUACHUE HMONG TONE-M;Mn;230;NSM;;;;;N;;;;;
+1E132;NYIAKENG PUACHUE HMONG TONE-J;Mn;230;NSM;;;;;N;;;;;
+1E133;NYIAKENG PUACHUE HMONG TONE-V;Mn;230;NSM;;;;;N;;;;;
+1E134;NYIAKENG PUACHUE HMONG TONE-S;Mn;230;NSM;;;;;N;;;;;
+1E135;NYIAKENG PUACHUE HMONG TONE-G;Mn;230;NSM;;;;;N;;;;;
+1E136;NYIAKENG PUACHUE HMONG TONE-D;Mn;230;NSM;;;;;N;;;;;
+1E137;NYIAKENG PUACHUE HMONG SIGN FOR PERSON;Lm;0;L;;;;;N;;;;;
+1E138;NYIAKENG PUACHUE HMONG SIGN FOR THING;Lm;0;L;;;;;N;;;;;
+1E139;NYIAKENG PUACHUE HMONG SIGN FOR LOCATION;Lm;0;L;;;;;N;;;;;
+1E13A;NYIAKENG PUACHUE HMONG SIGN FOR ANIMAL;Lm;0;L;;;;;N;;;;;
+1E13B;NYIAKENG PUACHUE HMONG SIGN FOR INVERTEBRATE;Lm;0;L;;;;;N;;;;;
+1E13C;NYIAKENG PUACHUE HMONG SIGN XW XW;Lm;0;L;;;;;N;;;;;
+1E13D;NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER;Lm;0;L;;;;;N;;;;;
+1E140;NYIAKENG PUACHUE HMONG DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1E141;NYIAKENG PUACHUE HMONG DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1E142;NYIAKENG PUACHUE HMONG DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1E143;NYIAKENG PUACHUE HMONG DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1E144;NYIAKENG PUACHUE HMONG DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1E145;NYIAKENG PUACHUE HMONG DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1E146;NYIAKENG PUACHUE HMONG DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1E147;NYIAKENG PUACHUE HMONG DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1E148;NYIAKENG PUACHUE HMONG DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1E149;NYIAKENG PUACHUE HMONG DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1E14E;NYIAKENG PUACHUE HMONG LOGOGRAM NYAJ;Lo;0;L;;;;;N;;;;;
+1E14F;NYIAKENG PUACHUE HMONG CIRCLED CA;So;0;L;;;;;N;;;;;
+1E2C0;WANCHO LETTER AA;Lo;0;L;;;;;N;;;;;
+1E2C1;WANCHO LETTER A;Lo;0;L;;;;;N;;;;;
+1E2C2;WANCHO LETTER BA;Lo;0;L;;;;;N;;;;;
+1E2C3;WANCHO LETTER CA;Lo;0;L;;;;;N;;;;;
+1E2C4;WANCHO LETTER DA;Lo;0;L;;;;;N;;;;;
+1E2C5;WANCHO LETTER GA;Lo;0;L;;;;;N;;;;;
+1E2C6;WANCHO LETTER YA;Lo;0;L;;;;;N;;;;;
+1E2C7;WANCHO LETTER PHA;Lo;0;L;;;;;N;;;;;
+1E2C8;WANCHO LETTER LA;Lo;0;L;;;;;N;;;;;
+1E2C9;WANCHO LETTER NA;Lo;0;L;;;;;N;;;;;
+1E2CA;WANCHO LETTER PA;Lo;0;L;;;;;N;;;;;
+1E2CB;WANCHO LETTER TA;Lo;0;L;;;;;N;;;;;
+1E2CC;WANCHO LETTER THA;Lo;0;L;;;;;N;;;;;
+1E2CD;WANCHO LETTER FA;Lo;0;L;;;;;N;;;;;
+1E2CE;WANCHO LETTER SA;Lo;0;L;;;;;N;;;;;
+1E2CF;WANCHO LETTER SHA;Lo;0;L;;;;;N;;;;;
+1E2D0;WANCHO LETTER JA;Lo;0;L;;;;;N;;;;;
+1E2D1;WANCHO LETTER ZA;Lo;0;L;;;;;N;;;;;
+1E2D2;WANCHO LETTER WA;Lo;0;L;;;;;N;;;;;
+1E2D3;WANCHO LETTER VA;Lo;0;L;;;;;N;;;;;
+1E2D4;WANCHO LETTER KA;Lo;0;L;;;;;N;;;;;
+1E2D5;WANCHO LETTER O;Lo;0;L;;;;;N;;;;;
+1E2D6;WANCHO LETTER AU;Lo;0;L;;;;;N;;;;;
+1E2D7;WANCHO LETTER RA;Lo;0;L;;;;;N;;;;;
+1E2D8;WANCHO LETTER MA;Lo;0;L;;;;;N;;;;;
+1E2D9;WANCHO LETTER KHA;Lo;0;L;;;;;N;;;;;
+1E2DA;WANCHO LETTER HA;Lo;0;L;;;;;N;;;;;
+1E2DB;WANCHO LETTER E;Lo;0;L;;;;;N;;;;;
+1E2DC;WANCHO LETTER I;Lo;0;L;;;;;N;;;;;
+1E2DD;WANCHO LETTER NGA;Lo;0;L;;;;;N;;;;;
+1E2DE;WANCHO LETTER U;Lo;0;L;;;;;N;;;;;
+1E2DF;WANCHO LETTER LLHA;Lo;0;L;;;;;N;;;;;
+1E2E0;WANCHO LETTER TSA;Lo;0;L;;;;;N;;;;;
+1E2E1;WANCHO LETTER TRA;Lo;0;L;;;;;N;;;;;
+1E2E2;WANCHO LETTER ONG;Lo;0;L;;;;;N;;;;;
+1E2E3;WANCHO LETTER AANG;Lo;0;L;;;;;N;;;;;
+1E2E4;WANCHO LETTER ANG;Lo;0;L;;;;;N;;;;;
+1E2E5;WANCHO LETTER ING;Lo;0;L;;;;;N;;;;;
+1E2E6;WANCHO LETTER ON;Lo;0;L;;;;;N;;;;;
+1E2E7;WANCHO LETTER EN;Lo;0;L;;;;;N;;;;;
+1E2E8;WANCHO LETTER AAN;Lo;0;L;;;;;N;;;;;
+1E2E9;WANCHO LETTER NYA;Lo;0;L;;;;;N;;;;;
+1E2EA;WANCHO LETTER UEN;Lo;0;L;;;;;N;;;;;
+1E2EB;WANCHO LETTER YIH;Lo;0;L;;;;;N;;;;;
+1E2EC;WANCHO TONE TUP;Mn;230;NSM;;;;;N;;;;;
+1E2ED;WANCHO TONE TUPNI;Mn;230;NSM;;;;;N;;;;;
+1E2EE;WANCHO TONE KOI;Mn;230;NSM;;;;;N;;;;;
+1E2EF;WANCHO TONE KOINI;Mn;230;NSM;;;;;N;;;;;
+1E2F0;WANCHO DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1E2F1;WANCHO DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1E2F2;WANCHO DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1E2F3;WANCHO DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1E2F4;WANCHO DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1E2F5;WANCHO DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1E2F6;WANCHO DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1E2F7;WANCHO DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1E2F8;WANCHO DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1E2F9;WANCHO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1E2FF;WANCHO NGUN SIGN;Sc;0;ET;;;;;N;;;;;
+1E800;MENDE KIKAKUI SYLLABLE M001 KI;Lo;0;R;;;;;N;;;;;
+1E801;MENDE KIKAKUI SYLLABLE M002 KA;Lo;0;R;;;;;N;;;;;
+1E802;MENDE KIKAKUI SYLLABLE M003 KU;Lo;0;R;;;;;N;;;;;
+1E803;MENDE KIKAKUI SYLLABLE M065 KEE;Lo;0;R;;;;;N;;;;;
+1E804;MENDE KIKAKUI SYLLABLE M095 KE;Lo;0;R;;;;;N;;;;;
+1E805;MENDE KIKAKUI SYLLABLE M076 KOO;Lo;0;R;;;;;N;;;;;
+1E806;MENDE KIKAKUI SYLLABLE M048 KO;Lo;0;R;;;;;N;;;;;
+1E807;MENDE KIKAKUI SYLLABLE M179 KUA;Lo;0;R;;;;;N;;;;;
+1E808;MENDE KIKAKUI SYLLABLE M004 WI;Lo;0;R;;;;;N;;;;;
+1E809;MENDE KIKAKUI SYLLABLE M005 WA;Lo;0;R;;;;;N;;;;;
+1E80A;MENDE KIKAKUI SYLLABLE M006 WU;Lo;0;R;;;;;N;;;;;
+1E80B;MENDE KIKAKUI SYLLABLE M126 WEE;Lo;0;R;;;;;N;;;;;
+1E80C;MENDE KIKAKUI SYLLABLE M118 WE;Lo;0;R;;;;;N;;;;;
+1E80D;MENDE KIKAKUI SYLLABLE M114 WOO;Lo;0;R;;;;;N;;;;;
+1E80E;MENDE KIKAKUI SYLLABLE M045 WO;Lo;0;R;;;;;N;;;;;
+1E80F;MENDE KIKAKUI SYLLABLE M194 WUI;Lo;0;R;;;;;N;;;;;
+1E810;MENDE KIKAKUI SYLLABLE M143 WEI;Lo;0;R;;;;;N;;;;;
+1E811;MENDE KIKAKUI SYLLABLE M061 WVI;Lo;0;R;;;;;N;;;;;
+1E812;MENDE KIKAKUI SYLLABLE M049 WVA;Lo;0;R;;;;;N;;;;;
+1E813;MENDE KIKAKUI SYLLABLE M139 WVE;Lo;0;R;;;;;N;;;;;
+1E814;MENDE KIKAKUI SYLLABLE M007 MIN;Lo;0;R;;;;;N;;;;;
+1E815;MENDE KIKAKUI SYLLABLE M008 MAN;Lo;0;R;;;;;N;;;;;
+1E816;MENDE KIKAKUI SYLLABLE M009 MUN;Lo;0;R;;;;;N;;;;;
+1E817;MENDE KIKAKUI SYLLABLE M059 MEN;Lo;0;R;;;;;N;;;;;
+1E818;MENDE KIKAKUI SYLLABLE M094 MON;Lo;0;R;;;;;N;;;;;
+1E819;MENDE KIKAKUI SYLLABLE M154 MUAN;Lo;0;R;;;;;N;;;;;
+1E81A;MENDE KIKAKUI SYLLABLE M189 MUEN;Lo;0;R;;;;;N;;;;;
+1E81B;MENDE KIKAKUI SYLLABLE M010 BI;Lo;0;R;;;;;N;;;;;
+1E81C;MENDE KIKAKUI SYLLABLE M011 BA;Lo;0;R;;;;;N;;;;;
+1E81D;MENDE KIKAKUI SYLLABLE M012 BU;Lo;0;R;;;;;N;;;;;
+1E81E;MENDE KIKAKUI SYLLABLE M150 BEE;Lo;0;R;;;;;N;;;;;
+1E81F;MENDE KIKAKUI SYLLABLE M097 BE;Lo;0;R;;;;;N;;;;;
+1E820;MENDE KIKAKUI SYLLABLE M103 BOO;Lo;0;R;;;;;N;;;;;
+1E821;MENDE KIKAKUI SYLLABLE M138 BO;Lo;0;R;;;;;N;;;;;
+1E822;MENDE KIKAKUI SYLLABLE M013 I;Lo;0;R;;;;;N;;;;;
+1E823;MENDE KIKAKUI SYLLABLE M014 A;Lo;0;R;;;;;N;;;;;
+1E824;MENDE KIKAKUI SYLLABLE M015 U;Lo;0;R;;;;;N;;;;;
+1E825;MENDE KIKAKUI SYLLABLE M163 EE;Lo;0;R;;;;;N;;;;;
+1E826;MENDE KIKAKUI SYLLABLE M100 E;Lo;0;R;;;;;N;;;;;
+1E827;MENDE KIKAKUI SYLLABLE M165 OO;Lo;0;R;;;;;N;;;;;
+1E828;MENDE KIKAKUI SYLLABLE M147 O;Lo;0;R;;;;;N;;;;;
+1E829;MENDE KIKAKUI SYLLABLE M137 EI;Lo;0;R;;;;;N;;;;;
+1E82A;MENDE KIKAKUI SYLLABLE M131 IN;Lo;0;R;;;;;N;;;;;
+1E82B;MENDE KIKAKUI SYLLABLE M135 IN;Lo;0;R;;;;;N;;;;;
+1E82C;MENDE KIKAKUI SYLLABLE M195 AN;Lo;0;R;;;;;N;;;;;
+1E82D;MENDE KIKAKUI SYLLABLE M178 EN;Lo;0;R;;;;;N;;;;;
+1E82E;MENDE KIKAKUI SYLLABLE M019 SI;Lo;0;R;;;;;N;;;;;
+1E82F;MENDE KIKAKUI SYLLABLE M020 SA;Lo;0;R;;;;;N;;;;;
+1E830;MENDE KIKAKUI SYLLABLE M021 SU;Lo;0;R;;;;;N;;;;;
+1E831;MENDE KIKAKUI SYLLABLE M162 SEE;Lo;0;R;;;;;N;;;;;
+1E832;MENDE KIKAKUI SYLLABLE M116 SE;Lo;0;R;;;;;N;;;;;
+1E833;MENDE KIKAKUI SYLLABLE M136 SOO;Lo;0;R;;;;;N;;;;;
+1E834;MENDE KIKAKUI SYLLABLE M079 SO;Lo;0;R;;;;;N;;;;;
+1E835;MENDE KIKAKUI SYLLABLE M196 SIA;Lo;0;R;;;;;N;;;;;
+1E836;MENDE KIKAKUI SYLLABLE M025 LI;Lo;0;R;;;;;N;;;;;
+1E837;MENDE KIKAKUI SYLLABLE M026 LA;Lo;0;R;;;;;N;;;;;
+1E838;MENDE KIKAKUI SYLLABLE M027 LU;Lo;0;R;;;;;N;;;;;
+1E839;MENDE KIKAKUI SYLLABLE M084 LEE;Lo;0;R;;;;;N;;;;;
+1E83A;MENDE KIKAKUI SYLLABLE M073 LE;Lo;0;R;;;;;N;;;;;
+1E83B;MENDE KIKAKUI SYLLABLE M054 LOO;Lo;0;R;;;;;N;;;;;
+1E83C;MENDE KIKAKUI SYLLABLE M153 LO;Lo;0;R;;;;;N;;;;;
+1E83D;MENDE KIKAKUI SYLLABLE M110 LONG LE;Lo;0;R;;;;;N;;;;;
+1E83E;MENDE KIKAKUI SYLLABLE M016 DI;Lo;0;R;;;;;N;;;;;
+1E83F;MENDE KIKAKUI SYLLABLE M017 DA;Lo;0;R;;;;;N;;;;;
+1E840;MENDE KIKAKUI SYLLABLE M018 DU;Lo;0;R;;;;;N;;;;;
+1E841;MENDE KIKAKUI SYLLABLE M089 DEE;Lo;0;R;;;;;N;;;;;
+1E842;MENDE KIKAKUI SYLLABLE M180 DOO;Lo;0;R;;;;;N;;;;;
+1E843;MENDE KIKAKUI SYLLABLE M181 DO;Lo;0;R;;;;;N;;;;;
+1E844;MENDE KIKAKUI SYLLABLE M022 TI;Lo;0;R;;;;;N;;;;;
+1E845;MENDE KIKAKUI SYLLABLE M023 TA;Lo;0;R;;;;;N;;;;;
+1E846;MENDE KIKAKUI SYLLABLE M024 TU;Lo;0;R;;;;;N;;;;;
+1E847;MENDE KIKAKUI SYLLABLE M091 TEE;Lo;0;R;;;;;N;;;;;
+1E848;MENDE KIKAKUI SYLLABLE M055 TE;Lo;0;R;;;;;N;;;;;
+1E849;MENDE KIKAKUI SYLLABLE M104 TOO;Lo;0;R;;;;;N;;;;;
+1E84A;MENDE KIKAKUI SYLLABLE M069 TO;Lo;0;R;;;;;N;;;;;
+1E84B;MENDE KIKAKUI SYLLABLE M028 JI;Lo;0;R;;;;;N;;;;;
+1E84C;MENDE KIKAKUI SYLLABLE M029 JA;Lo;0;R;;;;;N;;;;;
+1E84D;MENDE KIKAKUI SYLLABLE M030 JU;Lo;0;R;;;;;N;;;;;
+1E84E;MENDE KIKAKUI SYLLABLE M157 JEE;Lo;0;R;;;;;N;;;;;
+1E84F;MENDE KIKAKUI SYLLABLE M113 JE;Lo;0;R;;;;;N;;;;;
+1E850;MENDE KIKAKUI SYLLABLE M160 JOO;Lo;0;R;;;;;N;;;;;
+1E851;MENDE KIKAKUI SYLLABLE M063 JO;Lo;0;R;;;;;N;;;;;
+1E852;MENDE KIKAKUI SYLLABLE M175 LONG JO;Lo;0;R;;;;;N;;;;;
+1E853;MENDE KIKAKUI SYLLABLE M031 YI;Lo;0;R;;;;;N;;;;;
+1E854;MENDE KIKAKUI SYLLABLE M032 YA;Lo;0;R;;;;;N;;;;;
+1E855;MENDE KIKAKUI SYLLABLE M033 YU;Lo;0;R;;;;;N;;;;;
+1E856;MENDE KIKAKUI SYLLABLE M109 YEE;Lo;0;R;;;;;N;;;;;
+1E857;MENDE KIKAKUI SYLLABLE M080 YE;Lo;0;R;;;;;N;;;;;
+1E858;MENDE KIKAKUI SYLLABLE M141 YOO;Lo;0;R;;;;;N;;;;;
+1E859;MENDE KIKAKUI SYLLABLE M121 YO;Lo;0;R;;;;;N;;;;;
+1E85A;MENDE KIKAKUI SYLLABLE M034 FI;Lo;0;R;;;;;N;;;;;
+1E85B;MENDE KIKAKUI SYLLABLE M035 FA;Lo;0;R;;;;;N;;;;;
+1E85C;MENDE KIKAKUI SYLLABLE M036 FU;Lo;0;R;;;;;N;;;;;
+1E85D;MENDE KIKAKUI SYLLABLE M078 FEE;Lo;0;R;;;;;N;;;;;
+1E85E;MENDE KIKAKUI SYLLABLE M075 FE;Lo;0;R;;;;;N;;;;;
+1E85F;MENDE KIKAKUI SYLLABLE M133 FOO;Lo;0;R;;;;;N;;;;;
+1E860;MENDE KIKAKUI SYLLABLE M088 FO;Lo;0;R;;;;;N;;;;;
+1E861;MENDE KIKAKUI SYLLABLE M197 FUA;Lo;0;R;;;;;N;;;;;
+1E862;MENDE KIKAKUI SYLLABLE M101 FAN;Lo;0;R;;;;;N;;;;;
+1E863;MENDE KIKAKUI SYLLABLE M037 NIN;Lo;0;R;;;;;N;;;;;
+1E864;MENDE KIKAKUI SYLLABLE M038 NAN;Lo;0;R;;;;;N;;;;;
+1E865;MENDE KIKAKUI SYLLABLE M039 NUN;Lo;0;R;;;;;N;;;;;
+1E866;MENDE KIKAKUI SYLLABLE M117 NEN;Lo;0;R;;;;;N;;;;;
+1E867;MENDE KIKAKUI SYLLABLE M169 NON;Lo;0;R;;;;;N;;;;;
+1E868;MENDE KIKAKUI SYLLABLE M176 HI;Lo;0;R;;;;;N;;;;;
+1E869;MENDE KIKAKUI SYLLABLE M041 HA;Lo;0;R;;;;;N;;;;;
+1E86A;MENDE KIKAKUI SYLLABLE M186 HU;Lo;0;R;;;;;N;;;;;
+1E86B;MENDE KIKAKUI SYLLABLE M040 HEE;Lo;0;R;;;;;N;;;;;
+1E86C;MENDE KIKAKUI SYLLABLE M096 HE;Lo;0;R;;;;;N;;;;;
+1E86D;MENDE KIKAKUI SYLLABLE M042 HOO;Lo;0;R;;;;;N;;;;;
+1E86E;MENDE KIKAKUI SYLLABLE M140 HO;Lo;0;R;;;;;N;;;;;
+1E86F;MENDE KIKAKUI SYLLABLE M083 HEEI;Lo;0;R;;;;;N;;;;;
+1E870;MENDE KIKAKUI SYLLABLE M128 HOOU;Lo;0;R;;;;;N;;;;;
+1E871;MENDE KIKAKUI SYLLABLE M053 HIN;Lo;0;R;;;;;N;;;;;
+1E872;MENDE KIKAKUI SYLLABLE M130 HAN;Lo;0;R;;;;;N;;;;;
+1E873;MENDE KIKAKUI SYLLABLE M087 HUN;Lo;0;R;;;;;N;;;;;
+1E874;MENDE KIKAKUI SYLLABLE M052 HEN;Lo;0;R;;;;;N;;;;;
+1E875;MENDE KIKAKUI SYLLABLE M193 HON;Lo;0;R;;;;;N;;;;;
+1E876;MENDE KIKAKUI SYLLABLE M046 HUAN;Lo;0;R;;;;;N;;;;;
+1E877;MENDE KIKAKUI SYLLABLE M090 NGGI;Lo;0;R;;;;;N;;;;;
+1E878;MENDE KIKAKUI SYLLABLE M043 NGGA;Lo;0;R;;;;;N;;;;;
+1E879;MENDE KIKAKUI SYLLABLE M082 NGGU;Lo;0;R;;;;;N;;;;;
+1E87A;MENDE KIKAKUI SYLLABLE M115 NGGEE;Lo;0;R;;;;;N;;;;;
+1E87B;MENDE KIKAKUI SYLLABLE M146 NGGE;Lo;0;R;;;;;N;;;;;
+1E87C;MENDE KIKAKUI SYLLABLE M156 NGGOO;Lo;0;R;;;;;N;;;;;
+1E87D;MENDE KIKAKUI SYLLABLE M120 NGGO;Lo;0;R;;;;;N;;;;;
+1E87E;MENDE KIKAKUI SYLLABLE M159 NGGAA;Lo;0;R;;;;;N;;;;;
+1E87F;MENDE KIKAKUI SYLLABLE M127 NGGUA;Lo;0;R;;;;;N;;;;;
+1E880;MENDE KIKAKUI SYLLABLE M086 LONG NGGE;Lo;0;R;;;;;N;;;;;
+1E881;MENDE KIKAKUI SYLLABLE M106 LONG NGGOO;Lo;0;R;;;;;N;;;;;
+1E882;MENDE KIKAKUI SYLLABLE M183 LONG NGGO;Lo;0;R;;;;;N;;;;;
+1E883;MENDE KIKAKUI SYLLABLE M155 GI;Lo;0;R;;;;;N;;;;;
+1E884;MENDE KIKAKUI SYLLABLE M111 GA;Lo;0;R;;;;;N;;;;;
+1E885;MENDE KIKAKUI SYLLABLE M168 GU;Lo;0;R;;;;;N;;;;;
+1E886;MENDE KIKAKUI SYLLABLE M190 GEE;Lo;0;R;;;;;N;;;;;
+1E887;MENDE KIKAKUI SYLLABLE M166 GUEI;Lo;0;R;;;;;N;;;;;
+1E888;MENDE KIKAKUI SYLLABLE M167 GUAN;Lo;0;R;;;;;N;;;;;
+1E889;MENDE KIKAKUI SYLLABLE M184 NGEN;Lo;0;R;;;;;N;;;;;
+1E88A;MENDE KIKAKUI SYLLABLE M057 NGON;Lo;0;R;;;;;N;;;;;
+1E88B;MENDE KIKAKUI SYLLABLE M177 NGUAN;Lo;0;R;;;;;N;;;;;
+1E88C;MENDE KIKAKUI SYLLABLE M068 PI;Lo;0;R;;;;;N;;;;;
+1E88D;MENDE KIKAKUI SYLLABLE M099 PA;Lo;0;R;;;;;N;;;;;
+1E88E;MENDE KIKAKUI SYLLABLE M050 PU;Lo;0;R;;;;;N;;;;;
+1E88F;MENDE KIKAKUI SYLLABLE M081 PEE;Lo;0;R;;;;;N;;;;;
+1E890;MENDE KIKAKUI SYLLABLE M051 PE;Lo;0;R;;;;;N;;;;;
+1E891;MENDE KIKAKUI SYLLABLE M102 POO;Lo;0;R;;;;;N;;;;;
+1E892;MENDE KIKAKUI SYLLABLE M066 PO;Lo;0;R;;;;;N;;;;;
+1E893;MENDE KIKAKUI SYLLABLE M145 MBI;Lo;0;R;;;;;N;;;;;
+1E894;MENDE KIKAKUI SYLLABLE M062 MBA;Lo;0;R;;;;;N;;;;;
+1E895;MENDE KIKAKUI SYLLABLE M122 MBU;Lo;0;R;;;;;N;;;;;
+1E896;MENDE KIKAKUI SYLLABLE M047 MBEE;Lo;0;R;;;;;N;;;;;
+1E897;MENDE KIKAKUI SYLLABLE M188 MBEE;Lo;0;R;;;;;N;;;;;
+1E898;MENDE KIKAKUI SYLLABLE M072 MBE;Lo;0;R;;;;;N;;;;;
+1E899;MENDE KIKAKUI SYLLABLE M172 MBOO;Lo;0;R;;;;;N;;;;;
+1E89A;MENDE KIKAKUI SYLLABLE M174 MBO;Lo;0;R;;;;;N;;;;;
+1E89B;MENDE KIKAKUI SYLLABLE M187 MBUU;Lo;0;R;;;;;N;;;;;
+1E89C;MENDE KIKAKUI SYLLABLE M161 LONG MBE;Lo;0;R;;;;;N;;;;;
+1E89D;MENDE KIKAKUI SYLLABLE M105 LONG MBOO;Lo;0;R;;;;;N;;;;;
+1E89E;MENDE KIKAKUI SYLLABLE M142 LONG MBO;Lo;0;R;;;;;N;;;;;
+1E89F;MENDE KIKAKUI SYLLABLE M132 KPI;Lo;0;R;;;;;N;;;;;
+1E8A0;MENDE KIKAKUI SYLLABLE M092 KPA;Lo;0;R;;;;;N;;;;;
+1E8A1;MENDE KIKAKUI SYLLABLE M074 KPU;Lo;0;R;;;;;N;;;;;
+1E8A2;MENDE KIKAKUI SYLLABLE M044 KPEE;Lo;0;R;;;;;N;;;;;
+1E8A3;MENDE KIKAKUI SYLLABLE M108 KPE;Lo;0;R;;;;;N;;;;;
+1E8A4;MENDE KIKAKUI SYLLABLE M112 KPOO;Lo;0;R;;;;;N;;;;;
+1E8A5;MENDE KIKAKUI SYLLABLE M158 KPO;Lo;0;R;;;;;N;;;;;
+1E8A6;MENDE KIKAKUI SYLLABLE M124 GBI;Lo;0;R;;;;;N;;;;;
+1E8A7;MENDE KIKAKUI SYLLABLE M056 GBA;Lo;0;R;;;;;N;;;;;
+1E8A8;MENDE KIKAKUI SYLLABLE M148 GBU;Lo;0;R;;;;;N;;;;;
+1E8A9;MENDE KIKAKUI SYLLABLE M093 GBEE;Lo;0;R;;;;;N;;;;;
+1E8AA;MENDE KIKAKUI SYLLABLE M107 GBE;Lo;0;R;;;;;N;;;;;
+1E8AB;MENDE KIKAKUI SYLLABLE M071 GBOO;Lo;0;R;;;;;N;;;;;
+1E8AC;MENDE KIKAKUI SYLLABLE M070 GBO;Lo;0;R;;;;;N;;;;;
+1E8AD;MENDE KIKAKUI SYLLABLE M171 RA;Lo;0;R;;;;;N;;;;;
+1E8AE;MENDE KIKAKUI SYLLABLE M123 NDI;Lo;0;R;;;;;N;;;;;
+1E8AF;MENDE KIKAKUI SYLLABLE M129 NDA;Lo;0;R;;;;;N;;;;;
+1E8B0;MENDE KIKAKUI SYLLABLE M125 NDU;Lo;0;R;;;;;N;;;;;
+1E8B1;MENDE KIKAKUI SYLLABLE M191 NDEE;Lo;0;R;;;;;N;;;;;
+1E8B2;MENDE KIKAKUI SYLLABLE M119 NDE;Lo;0;R;;;;;N;;;;;
+1E8B3;MENDE KIKAKUI SYLLABLE M067 NDOO;Lo;0;R;;;;;N;;;;;
+1E8B4;MENDE KIKAKUI SYLLABLE M064 NDO;Lo;0;R;;;;;N;;;;;
+1E8B5;MENDE KIKAKUI SYLLABLE M152 NJA;Lo;0;R;;;;;N;;;;;
+1E8B6;MENDE KIKAKUI SYLLABLE M192 NJU;Lo;0;R;;;;;N;;;;;
+1E8B7;MENDE KIKAKUI SYLLABLE M149 NJEE;Lo;0;R;;;;;N;;;;;
+1E8B8;MENDE KIKAKUI SYLLABLE M134 NJOO;Lo;0;R;;;;;N;;;;;
+1E8B9;MENDE KIKAKUI SYLLABLE M182 VI;Lo;0;R;;;;;N;;;;;
+1E8BA;MENDE KIKAKUI SYLLABLE M185 VA;Lo;0;R;;;;;N;;;;;
+1E8BB;MENDE KIKAKUI SYLLABLE M151 VU;Lo;0;R;;;;;N;;;;;
+1E8BC;MENDE KIKAKUI SYLLABLE M173 VEE;Lo;0;R;;;;;N;;;;;
+1E8BD;MENDE KIKAKUI SYLLABLE M085 VE;Lo;0;R;;;;;N;;;;;
+1E8BE;MENDE KIKAKUI SYLLABLE M144 VOO;Lo;0;R;;;;;N;;;;;
+1E8BF;MENDE KIKAKUI SYLLABLE M077 VO;Lo;0;R;;;;;N;;;;;
+1E8C0;MENDE KIKAKUI SYLLABLE M164 NYIN;Lo;0;R;;;;;N;;;;;
+1E8C1;MENDE KIKAKUI SYLLABLE M058 NYAN;Lo;0;R;;;;;N;;;;;
+1E8C2;MENDE KIKAKUI SYLLABLE M170 NYUN;Lo;0;R;;;;;N;;;;;
+1E8C3;MENDE KIKAKUI SYLLABLE M098 NYEN;Lo;0;R;;;;;N;;;;;
+1E8C4;MENDE KIKAKUI SYLLABLE M060 NYON;Lo;0;R;;;;;N;;;;;
+1E8C7;MENDE KIKAKUI DIGIT ONE;No;0;R;;;;1;N;;;;;
+1E8C8;MENDE KIKAKUI DIGIT TWO;No;0;R;;;;2;N;;;;;
+1E8C9;MENDE KIKAKUI DIGIT THREE;No;0;R;;;;3;N;;;;;
+1E8CA;MENDE KIKAKUI DIGIT FOUR;No;0;R;;;;4;N;;;;;
+1E8CB;MENDE KIKAKUI DIGIT FIVE;No;0;R;;;;5;N;;;;;
+1E8CC;MENDE KIKAKUI DIGIT SIX;No;0;R;;;;6;N;;;;;
+1E8CD;MENDE KIKAKUI DIGIT SEVEN;No;0;R;;;;7;N;;;;;
+1E8CE;MENDE KIKAKUI DIGIT EIGHT;No;0;R;;;;8;N;;;;;
+1E8CF;MENDE KIKAKUI DIGIT NINE;No;0;R;;;;9;N;;;;;
+1E8D0;MENDE KIKAKUI COMBINING NUMBER TEENS;Mn;220;NSM;;;;;N;;;;;
+1E8D1;MENDE KIKAKUI COMBINING NUMBER TENS;Mn;220;NSM;;;;;N;;;;;
+1E8D2;MENDE KIKAKUI COMBINING NUMBER HUNDREDS;Mn;220;NSM;;;;;N;;;;;
+1E8D3;MENDE KIKAKUI COMBINING NUMBER THOUSANDS;Mn;220;NSM;;;;;N;;;;;
+1E8D4;MENDE KIKAKUI COMBINING NUMBER TEN THOUSANDS;Mn;220;NSM;;;;;N;;;;;
+1E8D5;MENDE KIKAKUI COMBINING NUMBER HUNDRED THOUSANDS;Mn;220;NSM;;;;;N;;;;;
+1E8D6;MENDE KIKAKUI COMBINING NUMBER MILLIONS;Mn;220;NSM;;;;;N;;;;;
+1E900;ADLAM CAPITAL LETTER ALIF;Lu;0;R;;;;;N;;;;1E922;
+1E901;ADLAM CAPITAL LETTER DAALI;Lu;0;R;;;;;N;;;;1E923;
+1E902;ADLAM CAPITAL LETTER LAAM;Lu;0;R;;;;;N;;;;1E924;
+1E903;ADLAM CAPITAL LETTER MIIM;Lu;0;R;;;;;N;;;;1E925;
+1E904;ADLAM CAPITAL LETTER BA;Lu;0;R;;;;;N;;;;1E926;
+1E905;ADLAM CAPITAL LETTER SINNYIIYHE;Lu;0;R;;;;;N;;;;1E927;
+1E906;ADLAM CAPITAL LETTER PE;Lu;0;R;;;;;N;;;;1E928;
+1E907;ADLAM CAPITAL LETTER BHE;Lu;0;R;;;;;N;;;;1E929;
+1E908;ADLAM CAPITAL LETTER RA;Lu;0;R;;;;;N;;;;1E92A;
+1E909;ADLAM CAPITAL LETTER E;Lu;0;R;;;;;N;;;;1E92B;
+1E90A;ADLAM CAPITAL LETTER FA;Lu;0;R;;;;;N;;;;1E92C;
+1E90B;ADLAM CAPITAL LETTER I;Lu;0;R;;;;;N;;;;1E92D;
+1E90C;ADLAM CAPITAL LETTER O;Lu;0;R;;;;;N;;;;1E92E;
+1E90D;ADLAM CAPITAL LETTER DHA;Lu;0;R;;;;;N;;;;1E92F;
+1E90E;ADLAM CAPITAL LETTER YHE;Lu;0;R;;;;;N;;;;1E930;
+1E90F;ADLAM CAPITAL LETTER WAW;Lu;0;R;;;;;N;;;;1E931;
+1E910;ADLAM CAPITAL LETTER NUN;Lu;0;R;;;;;N;;;;1E932;
+1E911;ADLAM CAPITAL LETTER KAF;Lu;0;R;;;;;N;;;;1E933;
+1E912;ADLAM CAPITAL LETTER YA;Lu;0;R;;;;;N;;;;1E934;
+1E913;ADLAM CAPITAL LETTER U;Lu;0;R;;;;;N;;;;1E935;
+1E914;ADLAM CAPITAL LETTER JIIM;Lu;0;R;;;;;N;;;;1E936;
+1E915;ADLAM CAPITAL LETTER CHI;Lu;0;R;;;;;N;;;;1E937;
+1E916;ADLAM CAPITAL LETTER HA;Lu;0;R;;;;;N;;;;1E938;
+1E917;ADLAM CAPITAL LETTER QAAF;Lu;0;R;;;;;N;;;;1E939;
+1E918;ADLAM CAPITAL LETTER GA;Lu;0;R;;;;;N;;;;1E93A;
+1E919;ADLAM CAPITAL LETTER NYA;Lu;0;R;;;;;N;;;;1E93B;
+1E91A;ADLAM CAPITAL LETTER TU;Lu;0;R;;;;;N;;;;1E93C;
+1E91B;ADLAM CAPITAL LETTER NHA;Lu;0;R;;;;;N;;;;1E93D;
+1E91C;ADLAM CAPITAL LETTER VA;Lu;0;R;;;;;N;;;;1E93E;
+1E91D;ADLAM CAPITAL LETTER KHA;Lu;0;R;;;;;N;;;;1E93F;
+1E91E;ADLAM CAPITAL LETTER GBE;Lu;0;R;;;;;N;;;;1E940;
+1E91F;ADLAM CAPITAL LETTER ZAL;Lu;0;R;;;;;N;;;;1E941;
+1E920;ADLAM CAPITAL LETTER KPO;Lu;0;R;;;;;N;;;;1E942;
+1E921;ADLAM CAPITAL LETTER SHA;Lu;0;R;;;;;N;;;;1E943;
+1E922;ADLAM SMALL LETTER ALIF;Ll;0;R;;;;;N;;;1E900;;1E900
+1E923;ADLAM SMALL LETTER DAALI;Ll;0;R;;;;;N;;;1E901;;1E901
+1E924;ADLAM SMALL LETTER LAAM;Ll;0;R;;;;;N;;;1E902;;1E902
+1E925;ADLAM SMALL LETTER MIIM;Ll;0;R;;;;;N;;;1E903;;1E903
+1E926;ADLAM SMALL LETTER BA;Ll;0;R;;;;;N;;;1E904;;1E904
+1E927;ADLAM SMALL LETTER SINNYIIYHE;Ll;0;R;;;;;N;;;1E905;;1E905
+1E928;ADLAM SMALL LETTER PE;Ll;0;R;;;;;N;;;1E906;;1E906
+1E929;ADLAM SMALL LETTER BHE;Ll;0;R;;;;;N;;;1E907;;1E907
+1E92A;ADLAM SMALL LETTER RA;Ll;0;R;;;;;N;;;1E908;;1E908
+1E92B;ADLAM SMALL LETTER E;Ll;0;R;;;;;N;;;1E909;;1E909
+1E92C;ADLAM SMALL LETTER FA;Ll;0;R;;;;;N;;;1E90A;;1E90A
+1E92D;ADLAM SMALL LETTER I;Ll;0;R;;;;;N;;;1E90B;;1E90B
+1E92E;ADLAM SMALL LETTER O;Ll;0;R;;;;;N;;;1E90C;;1E90C
+1E92F;ADLAM SMALL LETTER DHA;Ll;0;R;;;;;N;;;1E90D;;1E90D
+1E930;ADLAM SMALL LETTER YHE;Ll;0;R;;;;;N;;;1E90E;;1E90E
+1E931;ADLAM SMALL LETTER WAW;Ll;0;R;;;;;N;;;1E90F;;1E90F
+1E932;ADLAM SMALL LETTER NUN;Ll;0;R;;;;;N;;;1E910;;1E910
+1E933;ADLAM SMALL LETTER KAF;Ll;0;R;;;;;N;;;1E911;;1E911
+1E934;ADLAM SMALL LETTER YA;Ll;0;R;;;;;N;;;1E912;;1E912
+1E935;ADLAM SMALL LETTER U;Ll;0;R;;;;;N;;;1E913;;1E913
+1E936;ADLAM SMALL LETTER JIIM;Ll;0;R;;;;;N;;;1E914;;1E914
+1E937;ADLAM SMALL LETTER CHI;Ll;0;R;;;;;N;;;1E915;;1E915
+1E938;ADLAM SMALL LETTER HA;Ll;0;R;;;;;N;;;1E916;;1E916
+1E939;ADLAM SMALL LETTER QAAF;Ll;0;R;;;;;N;;;1E917;;1E917
+1E93A;ADLAM SMALL LETTER GA;Ll;0;R;;;;;N;;;1E918;;1E918
+1E93B;ADLAM SMALL LETTER NYA;Ll;0;R;;;;;N;;;1E919;;1E919
+1E93C;ADLAM SMALL LETTER TU;Ll;0;R;;;;;N;;;1E91A;;1E91A
+1E93D;ADLAM SMALL LETTER NHA;Ll;0;R;;;;;N;;;1E91B;;1E91B
+1E93E;ADLAM SMALL LETTER VA;Ll;0;R;;;;;N;;;1E91C;;1E91C
+1E93F;ADLAM SMALL LETTER KHA;Ll;0;R;;;;;N;;;1E91D;;1E91D
+1E940;ADLAM SMALL LETTER GBE;Ll;0;R;;;;;N;;;1E91E;;1E91E
+1E941;ADLAM SMALL LETTER ZAL;Ll;0;R;;;;;N;;;1E91F;;1E91F
+1E942;ADLAM SMALL LETTER KPO;Ll;0;R;;;;;N;;;1E920;;1E920
+1E943;ADLAM SMALL LETTER SHA;Ll;0;R;;;;;N;;;1E921;;1E921
+1E944;ADLAM ALIF LENGTHENER;Mn;230;NSM;;;;;N;;;;;
+1E945;ADLAM VOWEL LENGTHENER;Mn;230;NSM;;;;;N;;;;;
+1E946;ADLAM GEMINATION MARK;Mn;230;NSM;;;;;N;;;;;
+1E947;ADLAM HAMZA;Mn;230;NSM;;;;;N;;;;;
+1E948;ADLAM CONSONANT MODIFIER;Mn;230;NSM;;;;;N;;;;;
+1E949;ADLAM GEMINATE CONSONANT MODIFIER;Mn;230;NSM;;;;;N;;;;;
+1E94A;ADLAM NUKTA;Mn;7;NSM;;;;;N;;;;;
+1E94B;ADLAM NASALIZATION MARK;Lm;0;R;;;;;N;;;;;
+1E950;ADLAM DIGIT ZERO;Nd;0;R;;0;0;0;N;;;;;
+1E951;ADLAM DIGIT ONE;Nd;0;R;;1;1;1;N;;;;;
+1E952;ADLAM DIGIT TWO;Nd;0;R;;2;2;2;N;;;;;
+1E953;ADLAM DIGIT THREE;Nd;0;R;;3;3;3;N;;;;;
+1E954;ADLAM DIGIT FOUR;Nd;0;R;;4;4;4;N;;;;;
+1E955;ADLAM DIGIT FIVE;Nd;0;R;;5;5;5;N;;;;;
+1E956;ADLAM DIGIT SIX;Nd;0;R;;6;6;6;N;;;;;
+1E957;ADLAM DIGIT SEVEN;Nd;0;R;;7;7;7;N;;;;;
+1E958;ADLAM DIGIT EIGHT;Nd;0;R;;8;8;8;N;;;;;
+1E959;ADLAM DIGIT NINE;Nd;0;R;;9;9;9;N;;;;;
+1E95E;ADLAM INITIAL EXCLAMATION MARK;Po;0;R;;;;;N;;;;;
+1E95F;ADLAM INITIAL QUESTION MARK;Po;0;R;;;;;N;;;;;
+1EC71;INDIC SIYAQ NUMBER ONE;No;0;AL;;;;1;N;;;;;
+1EC72;INDIC SIYAQ NUMBER TWO;No;0;AL;;;;2;N;;;;;
+1EC73;INDIC SIYAQ NUMBER THREE;No;0;AL;;;;3;N;;;;;
+1EC74;INDIC SIYAQ NUMBER FOUR;No;0;AL;;;;4;N;;;;;
+1EC75;INDIC SIYAQ NUMBER FIVE;No;0;AL;;;;5;N;;;;;
+1EC76;INDIC SIYAQ NUMBER SIX;No;0;AL;;;;6;N;;;;;
+1EC77;INDIC SIYAQ NUMBER SEVEN;No;0;AL;;;;7;N;;;;;
+1EC78;INDIC SIYAQ NUMBER EIGHT;No;0;AL;;;;8;N;;;;;
+1EC79;INDIC SIYAQ NUMBER NINE;No;0;AL;;;;9;N;;;;;
+1EC7A;INDIC SIYAQ NUMBER TEN;No;0;AL;;;;10;N;;;;;
+1EC7B;INDIC SIYAQ NUMBER TWENTY;No;0;AL;;;;20;N;;;;;
+1EC7C;INDIC SIYAQ NUMBER THIRTY;No;0;AL;;;;30;N;;;;;
+1EC7D;INDIC SIYAQ NUMBER FORTY;No;0;AL;;;;40;N;;;;;
+1EC7E;INDIC SIYAQ NUMBER FIFTY;No;0;AL;;;;50;N;;;;;
+1EC7F;INDIC SIYAQ NUMBER SIXTY;No;0;AL;;;;60;N;;;;;
+1EC80;INDIC SIYAQ NUMBER SEVENTY;No;0;AL;;;;70;N;;;;;
+1EC81;INDIC SIYAQ NUMBER EIGHTY;No;0;AL;;;;80;N;;;;;
+1EC82;INDIC SIYAQ NUMBER NINETY;No;0;AL;;;;90;N;;;;;
+1EC83;INDIC SIYAQ NUMBER ONE HUNDRED;No;0;AL;;;;100;N;;;;;
+1EC84;INDIC SIYAQ NUMBER TWO HUNDRED;No;0;AL;;;;200;N;;;;;
+1EC85;INDIC SIYAQ NUMBER THREE HUNDRED;No;0;AL;;;;300;N;;;;;
+1EC86;INDIC SIYAQ NUMBER FOUR HUNDRED;No;0;AL;;;;400;N;;;;;
+1EC87;INDIC SIYAQ NUMBER FIVE HUNDRED;No;0;AL;;;;500;N;;;;;
+1EC88;INDIC SIYAQ NUMBER SIX HUNDRED;No;0;AL;;;;600;N;;;;;
+1EC89;INDIC SIYAQ NUMBER SEVEN HUNDRED;No;0;AL;;;;700;N;;;;;
+1EC8A;INDIC SIYAQ NUMBER EIGHT HUNDRED;No;0;AL;;;;800;N;;;;;
+1EC8B;INDIC SIYAQ NUMBER NINE HUNDRED;No;0;AL;;;;900;N;;;;;
+1EC8C;INDIC SIYAQ NUMBER ONE THOUSAND;No;0;AL;;;;1000;N;;;;;
+1EC8D;INDIC SIYAQ NUMBER TWO THOUSAND;No;0;AL;;;;2000;N;;;;;
+1EC8E;INDIC SIYAQ NUMBER THREE THOUSAND;No;0;AL;;;;3000;N;;;;;
+1EC8F;INDIC SIYAQ NUMBER FOUR THOUSAND;No;0;AL;;;;4000;N;;;;;
+1EC90;INDIC SIYAQ NUMBER FIVE THOUSAND;No;0;AL;;;;5000;N;;;;;
+1EC91;INDIC SIYAQ NUMBER SIX THOUSAND;No;0;AL;;;;6000;N;;;;;
+1EC92;INDIC SIYAQ NUMBER SEVEN THOUSAND;No;0;AL;;;;7000;N;;;;;
+1EC93;INDIC SIYAQ NUMBER EIGHT THOUSAND;No;0;AL;;;;8000;N;;;;;
+1EC94;INDIC SIYAQ NUMBER NINE THOUSAND;No;0;AL;;;;9000;N;;;;;
+1EC95;INDIC SIYAQ NUMBER TEN THOUSAND;No;0;AL;;;;10000;N;;;;;
+1EC96;INDIC SIYAQ NUMBER TWENTY THOUSAND;No;0;AL;;;;20000;N;;;;;
+1EC97;INDIC SIYAQ NUMBER THIRTY THOUSAND;No;0;AL;;;;30000;N;;;;;
+1EC98;INDIC SIYAQ NUMBER FORTY THOUSAND;No;0;AL;;;;40000;N;;;;;
+1EC99;INDIC SIYAQ NUMBER FIFTY THOUSAND;No;0;AL;;;;50000;N;;;;;
+1EC9A;INDIC SIYAQ NUMBER SIXTY THOUSAND;No;0;AL;;;;60000;N;;;;;
+1EC9B;INDIC SIYAQ NUMBER SEVENTY THOUSAND;No;0;AL;;;;70000;N;;;;;
+1EC9C;INDIC SIYAQ NUMBER EIGHTY THOUSAND;No;0;AL;;;;80000;N;;;;;
+1EC9D;INDIC SIYAQ NUMBER NINETY THOUSAND;No;0;AL;;;;90000;N;;;;;
+1EC9E;INDIC SIYAQ NUMBER LAKH;No;0;AL;;;;100000;N;;;;;
+1EC9F;INDIC SIYAQ NUMBER LAKHAN;No;0;AL;;;;200000;N;;;;;
+1ECA0;INDIC SIYAQ LAKH MARK;No;0;AL;;;;100000;N;;;;;
+1ECA1;INDIC SIYAQ NUMBER KAROR;No;0;AL;;;;10000000;N;;;;;
+1ECA2;INDIC SIYAQ NUMBER KARORAN;No;0;AL;;;;20000000;N;;;;;
+1ECA3;INDIC SIYAQ NUMBER PREFIXED ONE;No;0;AL;;;;1;N;;;;;
+1ECA4;INDIC SIYAQ NUMBER PREFIXED TWO;No;0;AL;;;;2;N;;;;;
+1ECA5;INDIC SIYAQ NUMBER PREFIXED THREE;No;0;AL;;;;3;N;;;;;
+1ECA6;INDIC SIYAQ NUMBER PREFIXED FOUR;No;0;AL;;;;4;N;;;;;
+1ECA7;INDIC SIYAQ NUMBER PREFIXED FIVE;No;0;AL;;;;5;N;;;;;
+1ECA8;INDIC SIYAQ NUMBER PREFIXED SIX;No;0;AL;;;;6;N;;;;;
+1ECA9;INDIC SIYAQ NUMBER PREFIXED SEVEN;No;0;AL;;;;7;N;;;;;
+1ECAA;INDIC SIYAQ NUMBER PREFIXED EIGHT;No;0;AL;;;;8;N;;;;;
+1ECAB;INDIC SIYAQ NUMBER PREFIXED NINE;No;0;AL;;;;9;N;;;;;
+1ECAC;INDIC SIYAQ PLACEHOLDER;So;0;AL;;;;;N;;;;;
+1ECAD;INDIC SIYAQ FRACTION ONE QUARTER;No;0;AL;;;;1/4;N;;;;;
+1ECAE;INDIC SIYAQ FRACTION ONE HALF;No;0;AL;;;;1/2;N;;;;;
+1ECAF;INDIC SIYAQ FRACTION THREE QUARTERS;No;0;AL;;;;3/4;N;;;;;
+1ECB0;INDIC SIYAQ RUPEE MARK;Sc;0;AL;;;;;N;;;;;
+1ECB1;INDIC SIYAQ NUMBER ALTERNATE ONE;No;0;AL;;;;1;N;;;;;
+1ECB2;INDIC SIYAQ NUMBER ALTERNATE TWO;No;0;AL;;;;2;N;;;;;
+1ECB3;INDIC SIYAQ NUMBER ALTERNATE TEN THOUSAND;No;0;AL;;;;10000;N;;;;;
+1ECB4;INDIC SIYAQ ALTERNATE LAKH MARK;No;0;AL;;;;100000;N;;;;;
+1ED01;OTTOMAN SIYAQ NUMBER ONE;No;0;AL;;;;1;N;;;;;
+1ED02;OTTOMAN SIYAQ NUMBER TWO;No;0;AL;;;;2;N;;;;;
+1ED03;OTTOMAN SIYAQ NUMBER THREE;No;0;AL;;;;3;N;;;;;
+1ED04;OTTOMAN SIYAQ NUMBER FOUR;No;0;AL;;;;4;N;;;;;
+1ED05;OTTOMAN SIYAQ NUMBER FIVE;No;0;AL;;;;5;N;;;;;
+1ED06;OTTOMAN SIYAQ NUMBER SIX;No;0;AL;;;;6;N;;;;;
+1ED07;OTTOMAN SIYAQ NUMBER SEVEN;No;0;AL;;;;7;N;;;;;
+1ED08;OTTOMAN SIYAQ NUMBER EIGHT;No;0;AL;;;;8;N;;;;;
+1ED09;OTTOMAN SIYAQ NUMBER NINE;No;0;AL;;;;9;N;;;;;
+1ED0A;OTTOMAN SIYAQ NUMBER TEN;No;0;AL;;;;10;N;;;;;
+1ED0B;OTTOMAN SIYAQ NUMBER TWENTY;No;0;AL;;;;20;N;;;;;
+1ED0C;OTTOMAN SIYAQ NUMBER THIRTY;No;0;AL;;;;30;N;;;;;
+1ED0D;OTTOMAN SIYAQ NUMBER FORTY;No;0;AL;;;;40;N;;;;;
+1ED0E;OTTOMAN SIYAQ NUMBER FIFTY;No;0;AL;;;;50;N;;;;;
+1ED0F;OTTOMAN SIYAQ NUMBER SIXTY;No;0;AL;;;;60;N;;;;;
+1ED10;OTTOMAN SIYAQ NUMBER SEVENTY;No;0;AL;;;;70;N;;;;;
+1ED11;OTTOMAN SIYAQ NUMBER EIGHTY;No;0;AL;;;;80;N;;;;;
+1ED12;OTTOMAN SIYAQ NUMBER NINETY;No;0;AL;;;;90;N;;;;;
+1ED13;OTTOMAN SIYAQ NUMBER ONE HUNDRED;No;0;AL;;;;100;N;;;;;
+1ED14;OTTOMAN SIYAQ NUMBER TWO HUNDRED;No;0;AL;;;;200;N;;;;;
+1ED15;OTTOMAN SIYAQ NUMBER THREE HUNDRED;No;0;AL;;;;300;N;;;;;
+1ED16;OTTOMAN SIYAQ NUMBER FOUR HUNDRED;No;0;AL;;;;400;N;;;;;
+1ED17;OTTOMAN SIYAQ NUMBER FIVE HUNDRED;No;0;AL;;;;500;N;;;;;
+1ED18;OTTOMAN SIYAQ NUMBER SIX HUNDRED;No;0;AL;;;;600;N;;;;;
+1ED19;OTTOMAN SIYAQ NUMBER SEVEN HUNDRED;No;0;AL;;;;700;N;;;;;
+1ED1A;OTTOMAN SIYAQ NUMBER EIGHT HUNDRED;No;0;AL;;;;800;N;;;;;
+1ED1B;OTTOMAN SIYAQ NUMBER NINE HUNDRED;No;0;AL;;;;900;N;;;;;
+1ED1C;OTTOMAN SIYAQ NUMBER ONE THOUSAND;No;0;AL;;;;1000;N;;;;;
+1ED1D;OTTOMAN SIYAQ NUMBER TWO THOUSAND;No;0;AL;;;;2000;N;;;;;
+1ED1E;OTTOMAN SIYAQ NUMBER THREE THOUSAND;No;0;AL;;;;3000;N;;;;;
+1ED1F;OTTOMAN SIYAQ NUMBER FOUR THOUSAND;No;0;AL;;;;4000;N;;;;;
+1ED20;OTTOMAN SIYAQ NUMBER FIVE THOUSAND;No;0;AL;;;;5000;N;;;;;
+1ED21;OTTOMAN SIYAQ NUMBER SIX THOUSAND;No;0;AL;;;;6000;N;;;;;
+1ED22;OTTOMAN SIYAQ NUMBER SEVEN THOUSAND;No;0;AL;;;;7000;N;;;;;
+1ED23;OTTOMAN SIYAQ NUMBER EIGHT THOUSAND;No;0;AL;;;;8000;N;;;;;
+1ED24;OTTOMAN SIYAQ NUMBER NINE THOUSAND;No;0;AL;;;;9000;N;;;;;
+1ED25;OTTOMAN SIYAQ NUMBER TEN THOUSAND;No;0;AL;;;;10000;N;;;;;
+1ED26;OTTOMAN SIYAQ NUMBER TWENTY THOUSAND;No;0;AL;;;;20000;N;;;;;
+1ED27;OTTOMAN SIYAQ NUMBER THIRTY THOUSAND;No;0;AL;;;;30000;N;;;;;
+1ED28;OTTOMAN SIYAQ NUMBER FORTY THOUSAND;No;0;AL;;;;40000;N;;;;;
+1ED29;OTTOMAN SIYAQ NUMBER FIFTY THOUSAND;No;0;AL;;;;50000;N;;;;;
+1ED2A;OTTOMAN SIYAQ NUMBER SIXTY THOUSAND;No;0;AL;;;;60000;N;;;;;
+1ED2B;OTTOMAN SIYAQ NUMBER SEVENTY THOUSAND;No;0;AL;;;;70000;N;;;;;
+1ED2C;OTTOMAN SIYAQ NUMBER EIGHTY THOUSAND;No;0;AL;;;;80000;N;;;;;
+1ED2D;OTTOMAN SIYAQ NUMBER NINETY THOUSAND;No;0;AL;;;;90000;N;;;;;
+1ED2E;OTTOMAN SIYAQ MARRATAN;So;0;AL;;;;;N;;;;;
+1ED2F;OTTOMAN SIYAQ ALTERNATE NUMBER TWO;No;0;AL;;;;2;N;;;;;
+1ED30;OTTOMAN SIYAQ ALTERNATE NUMBER THREE;No;0;AL;;;;3;N;;;;;
+1ED31;OTTOMAN SIYAQ ALTERNATE NUMBER FOUR;No;0;AL;;;;4;N;;;;;
+1ED32;OTTOMAN SIYAQ ALTERNATE NUMBER FIVE;No;0;AL;;;;5;N;;;;;
+1ED33;OTTOMAN SIYAQ ALTERNATE NUMBER SIX;No;0;AL;;;;6;N;;;;;
+1ED34;OTTOMAN SIYAQ ALTERNATE NUMBER SEVEN;No;0;AL;;;;7;N;;;;;
+1ED35;OTTOMAN SIYAQ ALTERNATE NUMBER EIGHT;No;0;AL;;;;8;N;;;;;
+1ED36;OTTOMAN SIYAQ ALTERNATE NUMBER NINE;No;0;AL;;;;9;N;;;;;
+1ED37;OTTOMAN SIYAQ ALTERNATE NUMBER TEN;No;0;AL;;;;10;N;;;;;
+1ED38;OTTOMAN SIYAQ ALTERNATE NUMBER FOUR HUNDRED;No;0;AL;;;;400;N;;;;;
+1ED39;OTTOMAN SIYAQ ALTERNATE NUMBER SIX HUNDRED;No;0;AL;;;;600;N;;;;;
+1ED3A;OTTOMAN SIYAQ ALTERNATE NUMBER TWO THOUSAND;No;0;AL;;;;2000;N;;;;;
+1ED3B;OTTOMAN SIYAQ ALTERNATE NUMBER TEN THOUSAND;No;0;AL;;;;10000;N;;;;;
+1ED3C;OTTOMAN SIYAQ FRACTION ONE HALF;No;0;AL;;;;1/2;N;;;;;
+1ED3D;OTTOMAN SIYAQ FRACTION ONE SIXTH;No;0;AL;;;;1/6;N;;;;;
+1EE00;ARABIC MATHEMATICAL ALEF;Lo;0;AL;<font> 0627;;;;N;;;;;
+1EE01;ARABIC MATHEMATICAL BEH;Lo;0;AL;<font> 0628;;;;N;;;;;
+1EE02;ARABIC MATHEMATICAL JEEM;Lo;0;AL;<font> 062C;;;;N;;;;;
+1EE03;ARABIC MATHEMATICAL DAL;Lo;0;AL;<font> 062F;;;;N;;;;;
+1EE05;ARABIC MATHEMATICAL WAW;Lo;0;AL;<font> 0648;;;;N;;;;;
+1EE06;ARABIC MATHEMATICAL ZAIN;Lo;0;AL;<font> 0632;;;;N;;;;;
+1EE07;ARABIC MATHEMATICAL HAH;Lo;0;AL;<font> 062D;;;;N;;;;;
+1EE08;ARABIC MATHEMATICAL TAH;Lo;0;AL;<font> 0637;;;;N;;;;;
+1EE09;ARABIC MATHEMATICAL YEH;Lo;0;AL;<font> 064A;;;;N;;;;;
+1EE0A;ARABIC MATHEMATICAL KAF;Lo;0;AL;<font> 0643;;;;N;;;;;
+1EE0B;ARABIC MATHEMATICAL LAM;Lo;0;AL;<font> 0644;;;;N;;;;;
+1EE0C;ARABIC MATHEMATICAL MEEM;Lo;0;AL;<font> 0645;;;;N;;;;;
+1EE0D;ARABIC MATHEMATICAL NOON;Lo;0;AL;<font> 0646;;;;N;;;;;
+1EE0E;ARABIC MATHEMATICAL SEEN;Lo;0;AL;<font> 0633;;;;N;;;;;
+1EE0F;ARABIC MATHEMATICAL AIN;Lo;0;AL;<font> 0639;;;;N;;;;;
+1EE10;ARABIC MATHEMATICAL FEH;Lo;0;AL;<font> 0641;;;;N;;;;;
+1EE11;ARABIC MATHEMATICAL SAD;Lo;0;AL;<font> 0635;;;;N;;;;;
+1EE12;ARABIC MATHEMATICAL QAF;Lo;0;AL;<font> 0642;;;;N;;;;;
+1EE13;ARABIC MATHEMATICAL REH;Lo;0;AL;<font> 0631;;;;N;;;;;
+1EE14;ARABIC MATHEMATICAL SHEEN;Lo;0;AL;<font> 0634;;;;N;;;;;
+1EE15;ARABIC MATHEMATICAL TEH;Lo;0;AL;<font> 062A;;;;N;;;;;
+1EE16;ARABIC MATHEMATICAL THEH;Lo;0;AL;<font> 062B;;;;N;;;;;
+1EE17;ARABIC MATHEMATICAL KHAH;Lo;0;AL;<font> 062E;;;;N;;;;;
+1EE18;ARABIC MATHEMATICAL THAL;Lo;0;AL;<font> 0630;;;;N;;;;;
+1EE19;ARABIC MATHEMATICAL DAD;Lo;0;AL;<font> 0636;;;;N;;;;;
+1EE1A;ARABIC MATHEMATICAL ZAH;Lo;0;AL;<font> 0638;;;;N;;;;;
+1EE1B;ARABIC MATHEMATICAL GHAIN;Lo;0;AL;<font> 063A;;;;N;;;;;
+1EE1C;ARABIC MATHEMATICAL DOTLESS BEH;Lo;0;AL;<font> 066E;;;;N;;;;;
+1EE1D;ARABIC MATHEMATICAL DOTLESS NOON;Lo;0;AL;<font> 06BA;;;;N;;;;;
+1EE1E;ARABIC MATHEMATICAL DOTLESS FEH;Lo;0;AL;<font> 06A1;;;;N;;;;;
+1EE1F;ARABIC MATHEMATICAL DOTLESS QAF;Lo;0;AL;<font> 066F;;;;N;;;;;
+1EE21;ARABIC MATHEMATICAL INITIAL BEH;Lo;0;AL;<font> 0628;;;;N;;;;;
+1EE22;ARABIC MATHEMATICAL INITIAL JEEM;Lo;0;AL;<font> 062C;;;;N;;;;;
+1EE24;ARABIC MATHEMATICAL INITIAL HEH;Lo;0;AL;<font> 0647;;;;N;;;;;
+1EE27;ARABIC MATHEMATICAL INITIAL HAH;Lo;0;AL;<font> 062D;;;;N;;;;;
+1EE29;ARABIC MATHEMATICAL INITIAL YEH;Lo;0;AL;<font> 064A;;;;N;;;;;
+1EE2A;ARABIC MATHEMATICAL INITIAL KAF;Lo;0;AL;<font> 0643;;;;N;;;;;
+1EE2B;ARABIC MATHEMATICAL INITIAL LAM;Lo;0;AL;<font> 0644;;;;N;;;;;
+1EE2C;ARABIC MATHEMATICAL INITIAL MEEM;Lo;0;AL;<font> 0645;;;;N;;;;;
+1EE2D;ARABIC MATHEMATICAL INITIAL NOON;Lo;0;AL;<font> 0646;;;;N;;;;;
+1EE2E;ARABIC MATHEMATICAL INITIAL SEEN;Lo;0;AL;<font> 0633;;;;N;;;;;
+1EE2F;ARABIC MATHEMATICAL INITIAL AIN;Lo;0;AL;<font> 0639;;;;N;;;;;
+1EE30;ARABIC MATHEMATICAL INITIAL FEH;Lo;0;AL;<font> 0641;;;;N;;;;;
+1EE31;ARABIC MATHEMATICAL INITIAL SAD;Lo;0;AL;<font> 0635;;;;N;;;;;
+1EE32;ARABIC MATHEMATICAL INITIAL QAF;Lo;0;AL;<font> 0642;;;;N;;;;;
+1EE34;ARABIC MATHEMATICAL INITIAL SHEEN;Lo;0;AL;<font> 0634;;;;N;;;;;
+1EE35;ARABIC MATHEMATICAL INITIAL TEH;Lo;0;AL;<font> 062A;;;;N;;;;;
+1EE36;ARABIC MATHEMATICAL INITIAL THEH;Lo;0;AL;<font> 062B;;;;N;;;;;
+1EE37;ARABIC MATHEMATICAL INITIAL KHAH;Lo;0;AL;<font> 062E;;;;N;;;;;
+1EE39;ARABIC MATHEMATICAL INITIAL DAD;Lo;0;AL;<font> 0636;;;;N;;;;;
+1EE3B;ARABIC MATHEMATICAL INITIAL GHAIN;Lo;0;AL;<font> 063A;;;;N;;;;;
+1EE42;ARABIC MATHEMATICAL TAILED JEEM;Lo;0;AL;<font> 062C;;;;N;;;;;
+1EE47;ARABIC MATHEMATICAL TAILED HAH;Lo;0;AL;<font> 062D;;;;N;;;;;
+1EE49;ARABIC MATHEMATICAL TAILED YEH;Lo;0;AL;<font> 064A;;;;N;;;;;
+1EE4B;ARABIC MATHEMATICAL TAILED LAM;Lo;0;AL;<font> 0644;;;;N;;;;;
+1EE4D;ARABIC MATHEMATICAL TAILED NOON;Lo;0;AL;<font> 0646;;;;N;;;;;
+1EE4E;ARABIC MATHEMATICAL TAILED SEEN;Lo;0;AL;<font> 0633;;;;N;;;;;
+1EE4F;ARABIC MATHEMATICAL TAILED AIN;Lo;0;AL;<font> 0639;;;;N;;;;;
+1EE51;ARABIC MATHEMATICAL TAILED SAD;Lo;0;AL;<font> 0635;;;;N;;;;;
+1EE52;ARABIC MATHEMATICAL TAILED QAF;Lo;0;AL;<font> 0642;;;;N;;;;;
+1EE54;ARABIC MATHEMATICAL TAILED SHEEN;Lo;0;AL;<font> 0634;;;;N;;;;;
+1EE57;ARABIC MATHEMATICAL TAILED KHAH;Lo;0;AL;<font> 062E;;;;N;;;;;
+1EE59;ARABIC MATHEMATICAL TAILED DAD;Lo;0;AL;<font> 0636;;;;N;;;;;
+1EE5B;ARABIC MATHEMATICAL TAILED GHAIN;Lo;0;AL;<font> 063A;;;;N;;;;;
+1EE5D;ARABIC MATHEMATICAL TAILED DOTLESS NOON;Lo;0;AL;<font> 06BA;;;;N;;;;;
+1EE5F;ARABIC MATHEMATICAL TAILED DOTLESS QAF;Lo;0;AL;<font> 066F;;;;N;;;;;
+1EE61;ARABIC MATHEMATICAL STRETCHED BEH;Lo;0;AL;<font> 0628;;;;N;;;;;
+1EE62;ARABIC MATHEMATICAL STRETCHED JEEM;Lo;0;AL;<font> 062C;;;;N;;;;;
+1EE64;ARABIC MATHEMATICAL STRETCHED HEH;Lo;0;AL;<font> 0647;;;;N;;;;;
+1EE67;ARABIC MATHEMATICAL STRETCHED HAH;Lo;0;AL;<font> 062D;;;;N;;;;;
+1EE68;ARABIC MATHEMATICAL STRETCHED TAH;Lo;0;AL;<font> 0637;;;;N;;;;;
+1EE69;ARABIC MATHEMATICAL STRETCHED YEH;Lo;0;AL;<font> 064A;;;;N;;;;;
+1EE6A;ARABIC MATHEMATICAL STRETCHED KAF;Lo;0;AL;<font> 0643;;;;N;;;;;
+1EE6C;ARABIC MATHEMATICAL STRETCHED MEEM;Lo;0;AL;<font> 0645;;;;N;;;;;
+1EE6D;ARABIC MATHEMATICAL STRETCHED NOON;Lo;0;AL;<font> 0646;;;;N;;;;;
+1EE6E;ARABIC MATHEMATICAL STRETCHED SEEN;Lo;0;AL;<font> 0633;;;;N;;;;;
+1EE6F;ARABIC MATHEMATICAL STRETCHED AIN;Lo;0;AL;<font> 0639;;;;N;;;;;
+1EE70;ARABIC MATHEMATICAL STRETCHED FEH;Lo;0;AL;<font> 0641;;;;N;;;;;
+1EE71;ARABIC MATHEMATICAL STRETCHED SAD;Lo;0;AL;<font> 0635;;;;N;;;;;
+1EE72;ARABIC MATHEMATICAL STRETCHED QAF;Lo;0;AL;<font> 0642;;;;N;;;;;
+1EE74;ARABIC MATHEMATICAL STRETCHED SHEEN;Lo;0;AL;<font> 0634;;;;N;;;;;
+1EE75;ARABIC MATHEMATICAL STRETCHED TEH;Lo;0;AL;<font> 062A;;;;N;;;;;
+1EE76;ARABIC MATHEMATICAL STRETCHED THEH;Lo;0;AL;<font> 062B;;;;N;;;;;
+1EE77;ARABIC MATHEMATICAL STRETCHED KHAH;Lo;0;AL;<font> 062E;;;;N;;;;;
+1EE79;ARABIC MATHEMATICAL STRETCHED DAD;Lo;0;AL;<font> 0636;;;;N;;;;;
+1EE7A;ARABIC MATHEMATICAL STRETCHED ZAH;Lo;0;AL;<font> 0638;;;;N;;;;;
+1EE7B;ARABIC MATHEMATICAL STRETCHED GHAIN;Lo;0;AL;<font> 063A;;;;N;;;;;
+1EE7C;ARABIC MATHEMATICAL STRETCHED DOTLESS BEH;Lo;0;AL;<font> 066E;;;;N;;;;;
+1EE7E;ARABIC MATHEMATICAL STRETCHED DOTLESS FEH;Lo;0;AL;<font> 06A1;;;;N;;;;;
+1EE80;ARABIC MATHEMATICAL LOOPED ALEF;Lo;0;AL;<font> 0627;;;;N;;;;;
+1EE81;ARABIC MATHEMATICAL LOOPED BEH;Lo;0;AL;<font> 0628;;;;N;;;;;
+1EE82;ARABIC MATHEMATICAL LOOPED JEEM;Lo;0;AL;<font> 062C;;;;N;;;;;
+1EE83;ARABIC MATHEMATICAL LOOPED DAL;Lo;0;AL;<font> 062F;;;;N;;;;;
+1EE84;ARABIC MATHEMATICAL LOOPED HEH;Lo;0;AL;<font> 0647;;;;N;;;;;
+1EE85;ARABIC MATHEMATICAL LOOPED WAW;Lo;0;AL;<font> 0648;;;;N;;;;;
+1EE86;ARABIC MATHEMATICAL LOOPED ZAIN;Lo;0;AL;<font> 0632;;;;N;;;;;
+1EE87;ARABIC MATHEMATICAL LOOPED HAH;Lo;0;AL;<font> 062D;;;;N;;;;;
+1EE88;ARABIC MATHEMATICAL LOOPED TAH;Lo;0;AL;<font> 0637;;;;N;;;;;
+1EE89;ARABIC MATHEMATICAL LOOPED YEH;Lo;0;AL;<font> 064A;;;;N;;;;;
+1EE8B;ARABIC MATHEMATICAL LOOPED LAM;Lo;0;AL;<font> 0644;;;;N;;;;;
+1EE8C;ARABIC MATHEMATICAL LOOPED MEEM;Lo;0;AL;<font> 0645;;;;N;;;;;
+1EE8D;ARABIC MATHEMATICAL LOOPED NOON;Lo;0;AL;<font> 0646;;;;N;;;;;
+1EE8E;ARABIC MATHEMATICAL LOOPED SEEN;Lo;0;AL;<font> 0633;;;;N;;;;;
+1EE8F;ARABIC MATHEMATICAL LOOPED AIN;Lo;0;AL;<font> 0639;;;;N;;;;;
+1EE90;ARABIC MATHEMATICAL LOOPED FEH;Lo;0;AL;<font> 0641;;;;N;;;;;
+1EE91;ARABIC MATHEMATICAL LOOPED SAD;Lo;0;AL;<font> 0635;;;;N;;;;;
+1EE92;ARABIC MATHEMATICAL LOOPED QAF;Lo;0;AL;<font> 0642;;;;N;;;;;
+1EE93;ARABIC MATHEMATICAL LOOPED REH;Lo;0;AL;<font> 0631;;;;N;;;;;
+1EE94;ARABIC MATHEMATICAL LOOPED SHEEN;Lo;0;AL;<font> 0634;;;;N;;;;;
+1EE95;ARABIC MATHEMATICAL LOOPED TEH;Lo;0;AL;<font> 062A;;;;N;;;;;
+1EE96;ARABIC MATHEMATICAL LOOPED THEH;Lo;0;AL;<font> 062B;;;;N;;;;;
+1EE97;ARABIC MATHEMATICAL LOOPED KHAH;Lo;0;AL;<font> 062E;;;;N;;;;;
+1EE98;ARABIC MATHEMATICAL LOOPED THAL;Lo;0;AL;<font> 0630;;;;N;;;;;
+1EE99;ARABIC MATHEMATICAL LOOPED DAD;Lo;0;AL;<font> 0636;;;;N;;;;;
+1EE9A;ARABIC MATHEMATICAL LOOPED ZAH;Lo;0;AL;<font> 0638;;;;N;;;;;
+1EE9B;ARABIC MATHEMATICAL LOOPED GHAIN;Lo;0;AL;<font> 063A;;;;N;;;;;
+1EEA1;ARABIC MATHEMATICAL DOUBLE-STRUCK BEH;Lo;0;AL;<font> 0628;;;;N;;;;;
+1EEA2;ARABIC MATHEMATICAL DOUBLE-STRUCK JEEM;Lo;0;AL;<font> 062C;;;;N;;;;;
+1EEA3;ARABIC MATHEMATICAL DOUBLE-STRUCK DAL;Lo;0;AL;<font> 062F;;;;N;;;;;
+1EEA5;ARABIC MATHEMATICAL DOUBLE-STRUCK WAW;Lo;0;AL;<font> 0648;;;;N;;;;;
+1EEA6;ARABIC MATHEMATICAL DOUBLE-STRUCK ZAIN;Lo;0;AL;<font> 0632;;;;N;;;;;
+1EEA7;ARABIC MATHEMATICAL DOUBLE-STRUCK HAH;Lo;0;AL;<font> 062D;;;;N;;;;;
+1EEA8;ARABIC MATHEMATICAL DOUBLE-STRUCK TAH;Lo;0;AL;<font> 0637;;;;N;;;;;
+1EEA9;ARABIC MATHEMATICAL DOUBLE-STRUCK YEH;Lo;0;AL;<font> 064A;;;;N;;;;;
+1EEAB;ARABIC MATHEMATICAL DOUBLE-STRUCK LAM;Lo;0;AL;<font> 0644;;;;N;;;;;
+1EEAC;ARABIC MATHEMATICAL DOUBLE-STRUCK MEEM;Lo;0;AL;<font> 0645;;;;N;;;;;
+1EEAD;ARABIC MATHEMATICAL DOUBLE-STRUCK NOON;Lo;0;AL;<font> 0646;;;;N;;;;;
+1EEAE;ARABIC MATHEMATICAL DOUBLE-STRUCK SEEN;Lo;0;AL;<font> 0633;;;;N;;;;;
+1EEAF;ARABIC MATHEMATICAL DOUBLE-STRUCK AIN;Lo;0;AL;<font> 0639;;;;N;;;;;
+1EEB0;ARABIC MATHEMATICAL DOUBLE-STRUCK FEH;Lo;0;AL;<font> 0641;;;;N;;;;;
+1EEB1;ARABIC MATHEMATICAL DOUBLE-STRUCK SAD;Lo;0;AL;<font> 0635;;;;N;;;;;
+1EEB2;ARABIC MATHEMATICAL DOUBLE-STRUCK QAF;Lo;0;AL;<font> 0642;;;;N;;;;;
+1EEB3;ARABIC MATHEMATICAL DOUBLE-STRUCK REH;Lo;0;AL;<font> 0631;;;;N;;;;;
+1EEB4;ARABIC MATHEMATICAL DOUBLE-STRUCK SHEEN;Lo;0;AL;<font> 0634;;;;N;;;;;
+1EEB5;ARABIC MATHEMATICAL DOUBLE-STRUCK TEH;Lo;0;AL;<font> 062A;;;;N;;;;;
+1EEB6;ARABIC MATHEMATICAL DOUBLE-STRUCK THEH;Lo;0;AL;<font> 062B;;;;N;;;;;
+1EEB7;ARABIC MATHEMATICAL DOUBLE-STRUCK KHAH;Lo;0;AL;<font> 062E;;;;N;;;;;
+1EEB8;ARABIC MATHEMATICAL DOUBLE-STRUCK THAL;Lo;0;AL;<font> 0630;;;;N;;;;;
+1EEB9;ARABIC MATHEMATICAL DOUBLE-STRUCK DAD;Lo;0;AL;<font> 0636;;;;N;;;;;
+1EEBA;ARABIC MATHEMATICAL DOUBLE-STRUCK ZAH;Lo;0;AL;<font> 0638;;;;N;;;;;
+1EEBB;ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN;Lo;0;AL;<font> 063A;;;;N;;;;;
+1EEF0;ARABIC MATHEMATICAL OPERATOR MEEM WITH HAH WITH TATWEEL;Sm;0;ON;;;;;N;;;;;
+1EEF1;ARABIC MATHEMATICAL OPERATOR HAH WITH DAL;Sm;0;ON;;;;;N;;;;;
+1F000;MAHJONG TILE EAST WIND;So;0;ON;;;;;N;;;;;
+1F001;MAHJONG TILE SOUTH WIND;So;0;ON;;;;;N;;;;;
+1F002;MAHJONG TILE WEST WIND;So;0;ON;;;;;N;;;;;
+1F003;MAHJONG TILE NORTH WIND;So;0;ON;;;;;N;;;;;
+1F004;MAHJONG TILE RED DRAGON;So;0;ON;;;;;N;;;;;
+1F005;MAHJONG TILE GREEN DRAGON;So;0;ON;;;;;N;;;;;
+1F006;MAHJONG TILE WHITE DRAGON;So;0;ON;;;;;N;;;;;
+1F007;MAHJONG TILE ONE OF CHARACTERS;So;0;ON;;;;;N;;;;;
+1F008;MAHJONG TILE TWO OF CHARACTERS;So;0;ON;;;;;N;;;;;
+1F009;MAHJONG TILE THREE OF CHARACTERS;So;0;ON;;;;;N;;;;;
+1F00A;MAHJONG TILE FOUR OF CHARACTERS;So;0;ON;;;;;N;;;;;
+1F00B;MAHJONG TILE FIVE OF CHARACTERS;So;0;ON;;;;;N;;;;;
+1F00C;MAHJONG TILE SIX OF CHARACTERS;So;0;ON;;;;;N;;;;;
+1F00D;MAHJONG TILE SEVEN OF CHARACTERS;So;0;ON;;;;;N;;;;;
+1F00E;MAHJONG TILE EIGHT OF CHARACTERS;So;0;ON;;;;;N;;;;;
+1F00F;MAHJONG TILE NINE OF CHARACTERS;So;0;ON;;;;;N;;;;;
+1F010;MAHJONG TILE ONE OF BAMBOOS;So;0;ON;;;;;N;;;;;
+1F011;MAHJONG TILE TWO OF BAMBOOS;So;0;ON;;;;;N;;;;;
+1F012;MAHJONG TILE THREE OF BAMBOOS;So;0;ON;;;;;N;;;;;
+1F013;MAHJONG TILE FOUR OF BAMBOOS;So;0;ON;;;;;N;;;;;
+1F014;MAHJONG TILE FIVE OF BAMBOOS;So;0;ON;;;;;N;;;;;
+1F015;MAHJONG TILE SIX OF BAMBOOS;So;0;ON;;;;;N;;;;;
+1F016;MAHJONG TILE SEVEN OF BAMBOOS;So;0;ON;;;;;N;;;;;
+1F017;MAHJONG TILE EIGHT OF BAMBOOS;So;0;ON;;;;;N;;;;;
+1F018;MAHJONG TILE NINE OF BAMBOOS;So;0;ON;;;;;N;;;;;
+1F019;MAHJONG TILE ONE OF CIRCLES;So;0;ON;;;;;N;;;;;
+1F01A;MAHJONG TILE TWO OF CIRCLES;So;0;ON;;;;;N;;;;;
+1F01B;MAHJONG TILE THREE OF CIRCLES;So;0;ON;;;;;N;;;;;
+1F01C;MAHJONG TILE FOUR OF CIRCLES;So;0;ON;;;;;N;;;;;
+1F01D;MAHJONG TILE FIVE OF CIRCLES;So;0;ON;;;;;N;;;;;
+1F01E;MAHJONG TILE SIX OF CIRCLES;So;0;ON;;;;;N;;;;;
+1F01F;MAHJONG TILE SEVEN OF CIRCLES;So;0;ON;;;;;N;;;;;
+1F020;MAHJONG TILE EIGHT OF CIRCLES;So;0;ON;;;;;N;;;;;
+1F021;MAHJONG TILE NINE OF CIRCLES;So;0;ON;;;;;N;;;;;
+1F022;MAHJONG TILE PLUM;So;0;ON;;;;;N;;;;;
+1F023;MAHJONG TILE ORCHID;So;0;ON;;;;;N;;;;;
+1F024;MAHJONG TILE BAMBOO;So;0;ON;;;;;N;;;;;
+1F025;MAHJONG TILE CHRYSANTHEMUM;So;0;ON;;;;;N;;;;;
+1F026;MAHJONG TILE SPRING;So;0;ON;;;;;N;;;;;
+1F027;MAHJONG TILE SUMMER;So;0;ON;;;;;N;;;;;
+1F028;MAHJONG TILE AUTUMN;So;0;ON;;;;;N;;;;;
+1F029;MAHJONG TILE WINTER;So;0;ON;;;;;N;;;;;
+1F02A;MAHJONG TILE JOKER;So;0;ON;;;;;N;;;;;
+1F02B;MAHJONG TILE BACK;So;0;ON;;;;;N;;;;;
+1F030;DOMINO TILE HORIZONTAL BACK;So;0;ON;;;;;N;;;;;
+1F031;DOMINO TILE HORIZONTAL-00-00;So;0;ON;;;;;N;;;;;
+1F032;DOMINO TILE HORIZONTAL-00-01;So;0;ON;;;;;N;;;;;
+1F033;DOMINO TILE HORIZONTAL-00-02;So;0;ON;;;;;N;;;;;
+1F034;DOMINO TILE HORIZONTAL-00-03;So;0;ON;;;;;N;;;;;
+1F035;DOMINO TILE HORIZONTAL-00-04;So;0;ON;;;;;N;;;;;
+1F036;DOMINO TILE HORIZONTAL-00-05;So;0;ON;;;;;N;;;;;
+1F037;DOMINO TILE HORIZONTAL-00-06;So;0;ON;;;;;N;;;;;
+1F038;DOMINO TILE HORIZONTAL-01-00;So;0;ON;;;;;N;;;;;
+1F039;DOMINO TILE HORIZONTAL-01-01;So;0;ON;;;;;N;;;;;
+1F03A;DOMINO TILE HORIZONTAL-01-02;So;0;ON;;;;;N;;;;;
+1F03B;DOMINO TILE HORIZONTAL-01-03;So;0;ON;;;;;N;;;;;
+1F03C;DOMINO TILE HORIZONTAL-01-04;So;0;ON;;;;;N;;;;;
+1F03D;DOMINO TILE HORIZONTAL-01-05;So;0;ON;;;;;N;;;;;
+1F03E;DOMINO TILE HORIZONTAL-01-06;So;0;ON;;;;;N;;;;;
+1F03F;DOMINO TILE HORIZONTAL-02-00;So;0;ON;;;;;N;;;;;
+1F040;DOMINO TILE HORIZONTAL-02-01;So;0;ON;;;;;N;;;;;
+1F041;DOMINO TILE HORIZONTAL-02-02;So;0;ON;;;;;N;;;;;
+1F042;DOMINO TILE HORIZONTAL-02-03;So;0;ON;;;;;N;;;;;
+1F043;DOMINO TILE HORIZONTAL-02-04;So;0;ON;;;;;N;;;;;
+1F044;DOMINO TILE HORIZONTAL-02-05;So;0;ON;;;;;N;;;;;
+1F045;DOMINO TILE HORIZONTAL-02-06;So;0;ON;;;;;N;;;;;
+1F046;DOMINO TILE HORIZONTAL-03-00;So;0;ON;;;;;N;;;;;
+1F047;DOMINO TILE HORIZONTAL-03-01;So;0;ON;;;;;N;;;;;
+1F048;DOMINO TILE HORIZONTAL-03-02;So;0;ON;;;;;N;;;;;
+1F049;DOMINO TILE HORIZONTAL-03-03;So;0;ON;;;;;N;;;;;
+1F04A;DOMINO TILE HORIZONTAL-03-04;So;0;ON;;;;;N;;;;;
+1F04B;DOMINO TILE HORIZONTAL-03-05;So;0;ON;;;;;N;;;;;
+1F04C;DOMINO TILE HORIZONTAL-03-06;So;0;ON;;;;;N;;;;;
+1F04D;DOMINO TILE HORIZONTAL-04-00;So;0;ON;;;;;N;;;;;
+1F04E;DOMINO TILE HORIZONTAL-04-01;So;0;ON;;;;;N;;;;;
+1F04F;DOMINO TILE HORIZONTAL-04-02;So;0;ON;;;;;N;;;;;
+1F050;DOMINO TILE HORIZONTAL-04-03;So;0;ON;;;;;N;;;;;
+1F051;DOMINO TILE HORIZONTAL-04-04;So;0;ON;;;;;N;;;;;
+1F052;DOMINO TILE HORIZONTAL-04-05;So;0;ON;;;;;N;;;;;
+1F053;DOMINO TILE HORIZONTAL-04-06;So;0;ON;;;;;N;;;;;
+1F054;DOMINO TILE HORIZONTAL-05-00;So;0;ON;;;;;N;;;;;
+1F055;DOMINO TILE HORIZONTAL-05-01;So;0;ON;;;;;N;;;;;
+1F056;DOMINO TILE HORIZONTAL-05-02;So;0;ON;;;;;N;;;;;
+1F057;DOMINO TILE HORIZONTAL-05-03;So;0;ON;;;;;N;;;;;
+1F058;DOMINO TILE HORIZONTAL-05-04;So;0;ON;;;;;N;;;;;
+1F059;DOMINO TILE HORIZONTAL-05-05;So;0;ON;;;;;N;;;;;
+1F05A;DOMINO TILE HORIZONTAL-05-06;So;0;ON;;;;;N;;;;;
+1F05B;DOMINO TILE HORIZONTAL-06-00;So;0;ON;;;;;N;;;;;
+1F05C;DOMINO TILE HORIZONTAL-06-01;So;0;ON;;;;;N;;;;;
+1F05D;DOMINO TILE HORIZONTAL-06-02;So;0;ON;;;;;N;;;;;
+1F05E;DOMINO TILE HORIZONTAL-06-03;So;0;ON;;;;;N;;;;;
+1F05F;DOMINO TILE HORIZONTAL-06-04;So;0;ON;;;;;N;;;;;
+1F060;DOMINO TILE HORIZONTAL-06-05;So;0;ON;;;;;N;;;;;
+1F061;DOMINO TILE HORIZONTAL-06-06;So;0;ON;;;;;N;;;;;
+1F062;DOMINO TILE VERTICAL BACK;So;0;ON;;;;;N;;;;;
+1F063;DOMINO TILE VERTICAL-00-00;So;0;ON;;;;;N;;;;;
+1F064;DOMINO TILE VERTICAL-00-01;So;0;ON;;;;;N;;;;;
+1F065;DOMINO TILE VERTICAL-00-02;So;0;ON;;;;;N;;;;;
+1F066;DOMINO TILE VERTICAL-00-03;So;0;ON;;;;;N;;;;;
+1F067;DOMINO TILE VERTICAL-00-04;So;0;ON;;;;;N;;;;;
+1F068;DOMINO TILE VERTICAL-00-05;So;0;ON;;;;;N;;;;;
+1F069;DOMINO TILE VERTICAL-00-06;So;0;ON;;;;;N;;;;;
+1F06A;DOMINO TILE VERTICAL-01-00;So;0;ON;;;;;N;;;;;
+1F06B;DOMINO TILE VERTICAL-01-01;So;0;ON;;;;;N;;;;;
+1F06C;DOMINO TILE VERTICAL-01-02;So;0;ON;;;;;N;;;;;
+1F06D;DOMINO TILE VERTICAL-01-03;So;0;ON;;;;;N;;;;;
+1F06E;DOMINO TILE VERTICAL-01-04;So;0;ON;;;;;N;;;;;
+1F06F;DOMINO TILE VERTICAL-01-05;So;0;ON;;;;;N;;;;;
+1F070;DOMINO TILE VERTICAL-01-06;So;0;ON;;;;;N;;;;;
+1F071;DOMINO TILE VERTICAL-02-00;So;0;ON;;;;;N;;;;;
+1F072;DOMINO TILE VERTICAL-02-01;So;0;ON;;;;;N;;;;;
+1F073;DOMINO TILE VERTICAL-02-02;So;0;ON;;;;;N;;;;;
+1F074;DOMINO TILE VERTICAL-02-03;So;0;ON;;;;;N;;;;;
+1F075;DOMINO TILE VERTICAL-02-04;So;0;ON;;;;;N;;;;;
+1F076;DOMINO TILE VERTICAL-02-05;So;0;ON;;;;;N;;;;;
+1F077;DOMINO TILE VERTICAL-02-06;So;0;ON;;;;;N;;;;;
+1F078;DOMINO TILE VERTICAL-03-00;So;0;ON;;;;;N;;;;;
+1F079;DOMINO TILE VERTICAL-03-01;So;0;ON;;;;;N;;;;;
+1F07A;DOMINO TILE VERTICAL-03-02;So;0;ON;;;;;N;;;;;
+1F07B;DOMINO TILE VERTICAL-03-03;So;0;ON;;;;;N;;;;;
+1F07C;DOMINO TILE VERTICAL-03-04;So;0;ON;;;;;N;;;;;
+1F07D;DOMINO TILE VERTICAL-03-05;So;0;ON;;;;;N;;;;;
+1F07E;DOMINO TILE VERTICAL-03-06;So;0;ON;;;;;N;;;;;
+1F07F;DOMINO TILE VERTICAL-04-00;So;0;ON;;;;;N;;;;;
+1F080;DOMINO TILE VERTICAL-04-01;So;0;ON;;;;;N;;;;;
+1F081;DOMINO TILE VERTICAL-04-02;So;0;ON;;;;;N;;;;;
+1F082;DOMINO TILE VERTICAL-04-03;So;0;ON;;;;;N;;;;;
+1F083;DOMINO TILE VERTICAL-04-04;So;0;ON;;;;;N;;;;;
+1F084;DOMINO TILE VERTICAL-04-05;So;0;ON;;;;;N;;;;;
+1F085;DOMINO TILE VERTICAL-04-06;So;0;ON;;;;;N;;;;;
+1F086;DOMINO TILE VERTICAL-05-00;So;0;ON;;;;;N;;;;;
+1F087;DOMINO TILE VERTICAL-05-01;So;0;ON;;;;;N;;;;;
+1F088;DOMINO TILE VERTICAL-05-02;So;0;ON;;;;;N;;;;;
+1F089;DOMINO TILE VERTICAL-05-03;So;0;ON;;;;;N;;;;;
+1F08A;DOMINO TILE VERTICAL-05-04;So;0;ON;;;;;N;;;;;
+1F08B;DOMINO TILE VERTICAL-05-05;So;0;ON;;;;;N;;;;;
+1F08C;DOMINO TILE VERTICAL-05-06;So;0;ON;;;;;N;;;;;
+1F08D;DOMINO TILE VERTICAL-06-00;So;0;ON;;;;;N;;;;;
+1F08E;DOMINO TILE VERTICAL-06-01;So;0;ON;;;;;N;;;;;
+1F08F;DOMINO TILE VERTICAL-06-02;So;0;ON;;;;;N;;;;;
+1F090;DOMINO TILE VERTICAL-06-03;So;0;ON;;;;;N;;;;;
+1F091;DOMINO TILE VERTICAL-06-04;So;0;ON;;;;;N;;;;;
+1F092;DOMINO TILE VERTICAL-06-05;So;0;ON;;;;;N;;;;;
+1F093;DOMINO TILE VERTICAL-06-06;So;0;ON;;;;;N;;;;;
+1F0A0;PLAYING CARD BACK;So;0;ON;;;;;N;;;;;
+1F0A1;PLAYING CARD ACE OF SPADES;So;0;ON;;;;;N;;;;;
+1F0A2;PLAYING CARD TWO OF SPADES;So;0;ON;;;;;N;;;;;
+1F0A3;PLAYING CARD THREE OF SPADES;So;0;ON;;;;;N;;;;;
+1F0A4;PLAYING CARD FOUR OF SPADES;So;0;ON;;;;;N;;;;;
+1F0A5;PLAYING CARD FIVE OF SPADES;So;0;ON;;;;;N;;;;;
+1F0A6;PLAYING CARD SIX OF SPADES;So;0;ON;;;;;N;;;;;
+1F0A7;PLAYING CARD SEVEN OF SPADES;So;0;ON;;;;;N;;;;;
+1F0A8;PLAYING CARD EIGHT OF SPADES;So;0;ON;;;;;N;;;;;
+1F0A9;PLAYING CARD NINE OF SPADES;So;0;ON;;;;;N;;;;;
+1F0AA;PLAYING CARD TEN OF SPADES;So;0;ON;;;;;N;;;;;
+1F0AB;PLAYING CARD JACK OF SPADES;So;0;ON;;;;;N;;;;;
+1F0AC;PLAYING CARD KNIGHT OF SPADES;So;0;ON;;;;;N;;;;;
+1F0AD;PLAYING CARD QUEEN OF SPADES;So;0;ON;;;;;N;;;;;
+1F0AE;PLAYING CARD KING OF SPADES;So;0;ON;;;;;N;;;;;
+1F0B1;PLAYING CARD ACE OF HEARTS;So;0;ON;;;;;N;;;;;
+1F0B2;PLAYING CARD TWO OF HEARTS;So;0;ON;;;;;N;;;;;
+1F0B3;PLAYING CARD THREE OF HEARTS;So;0;ON;;;;;N;;;;;
+1F0B4;PLAYING CARD FOUR OF HEARTS;So;0;ON;;;;;N;;;;;
+1F0B5;PLAYING CARD FIVE OF HEARTS;So;0;ON;;;;;N;;;;;
+1F0B6;PLAYING CARD SIX OF HEARTS;So;0;ON;;;;;N;;;;;
+1F0B7;PLAYING CARD SEVEN OF HEARTS;So;0;ON;;;;;N;;;;;
+1F0B8;PLAYING CARD EIGHT OF HEARTS;So;0;ON;;;;;N;;;;;
+1F0B9;PLAYING CARD NINE OF HEARTS;So;0;ON;;;;;N;;;;;
+1F0BA;PLAYING CARD TEN OF HEARTS;So;0;ON;;;;;N;;;;;
+1F0BB;PLAYING CARD JACK OF HEARTS;So;0;ON;;;;;N;;;;;
+1F0BC;PLAYING CARD KNIGHT OF HEARTS;So;0;ON;;;;;N;;;;;
+1F0BD;PLAYING CARD QUEEN OF HEARTS;So;0;ON;;;;;N;;;;;
+1F0BE;PLAYING CARD KING OF HEARTS;So;0;ON;;;;;N;;;;;
+1F0BF;PLAYING CARD RED JOKER;So;0;ON;;;;;N;;;;;
+1F0C1;PLAYING CARD ACE OF DIAMONDS;So;0;ON;;;;;N;;;;;
+1F0C2;PLAYING CARD TWO OF DIAMONDS;So;0;ON;;;;;N;;;;;
+1F0C3;PLAYING CARD THREE OF DIAMONDS;So;0;ON;;;;;N;;;;;
+1F0C4;PLAYING CARD FOUR OF DIAMONDS;So;0;ON;;;;;N;;;;;
+1F0C5;PLAYING CARD FIVE OF DIAMONDS;So;0;ON;;;;;N;;;;;
+1F0C6;PLAYING CARD SIX OF DIAMONDS;So;0;ON;;;;;N;;;;;
+1F0C7;PLAYING CARD SEVEN OF DIAMONDS;So;0;ON;;;;;N;;;;;
+1F0C8;PLAYING CARD EIGHT OF DIAMONDS;So;0;ON;;;;;N;;;;;
+1F0C9;PLAYING CARD NINE OF DIAMONDS;So;0;ON;;;;;N;;;;;
+1F0CA;PLAYING CARD TEN OF DIAMONDS;So;0;ON;;;;;N;;;;;
+1F0CB;PLAYING CARD JACK OF DIAMONDS;So;0;ON;;;;;N;;;;;
+1F0CC;PLAYING CARD KNIGHT OF DIAMONDS;So;0;ON;;;;;N;;;;;
+1F0CD;PLAYING CARD QUEEN OF DIAMONDS;So;0;ON;;;;;N;;;;;
+1F0CE;PLAYING CARD KING OF DIAMONDS;So;0;ON;;;;;N;;;;;
+1F0CF;PLAYING CARD BLACK JOKER;So;0;ON;;;;;N;;;;;
+1F0D1;PLAYING CARD ACE OF CLUBS;So;0;ON;;;;;N;;;;;
+1F0D2;PLAYING CARD TWO OF CLUBS;So;0;ON;;;;;N;;;;;
+1F0D3;PLAYING CARD THREE OF CLUBS;So;0;ON;;;;;N;;;;;
+1F0D4;PLAYING CARD FOUR OF CLUBS;So;0;ON;;;;;N;;;;;
+1F0D5;PLAYING CARD FIVE OF CLUBS;So;0;ON;;;;;N;;;;;
+1F0D6;PLAYING CARD SIX OF CLUBS;So;0;ON;;;;;N;;;;;
+1F0D7;PLAYING CARD SEVEN OF CLUBS;So;0;ON;;;;;N;;;;;
+1F0D8;PLAYING CARD EIGHT OF CLUBS;So;0;ON;;;;;N;;;;;
+1F0D9;PLAYING CARD NINE OF CLUBS;So;0;ON;;;;;N;;;;;
+1F0DA;PLAYING CARD TEN OF CLUBS;So;0;ON;;;;;N;;;;;
+1F0DB;PLAYING CARD JACK OF CLUBS;So;0;ON;;;;;N;;;;;
+1F0DC;PLAYING CARD KNIGHT OF CLUBS;So;0;ON;;;;;N;;;;;
+1F0DD;PLAYING CARD QUEEN OF CLUBS;So;0;ON;;;;;N;;;;;
+1F0DE;PLAYING CARD KING OF CLUBS;So;0;ON;;;;;N;;;;;
+1F0DF;PLAYING CARD WHITE JOKER;So;0;ON;;;;;N;;;;;
+1F0E0;PLAYING CARD FOOL;So;0;ON;;;;;N;;;;;
+1F0E1;PLAYING CARD TRUMP-1;So;0;ON;;;;;N;;;;;
+1F0E2;PLAYING CARD TRUMP-2;So;0;ON;;;;;N;;;;;
+1F0E3;PLAYING CARD TRUMP-3;So;0;ON;;;;;N;;;;;
+1F0E4;PLAYING CARD TRUMP-4;So;0;ON;;;;;N;;;;;
+1F0E5;PLAYING CARD TRUMP-5;So;0;ON;;;;;N;;;;;
+1F0E6;PLAYING CARD TRUMP-6;So;0;ON;;;;;N;;;;;
+1F0E7;PLAYING CARD TRUMP-7;So;0;ON;;;;;N;;;;;
+1F0E8;PLAYING CARD TRUMP-8;So;0;ON;;;;;N;;;;;
+1F0E9;PLAYING CARD TRUMP-9;So;0;ON;;;;;N;;;;;
+1F0EA;PLAYING CARD TRUMP-10;So;0;ON;;;;;N;;;;;
+1F0EB;PLAYING CARD TRUMP-11;So;0;ON;;;;;N;;;;;
+1F0EC;PLAYING CARD TRUMP-12;So;0;ON;;;;;N;;;;;
+1F0ED;PLAYING CARD TRUMP-13;So;0;ON;;;;;N;;;;;
+1F0EE;PLAYING CARD TRUMP-14;So;0;ON;;;;;N;;;;;
+1F0EF;PLAYING CARD TRUMP-15;So;0;ON;;;;;N;;;;;
+1F0F0;PLAYING CARD TRUMP-16;So;0;ON;;;;;N;;;;;
+1F0F1;PLAYING CARD TRUMP-17;So;0;ON;;;;;N;;;;;
+1F0F2;PLAYING CARD TRUMP-18;So;0;ON;;;;;N;;;;;
+1F0F3;PLAYING CARD TRUMP-19;So;0;ON;;;;;N;;;;;
+1F0F4;PLAYING CARD TRUMP-20;So;0;ON;;;;;N;;;;;
+1F0F5;PLAYING CARD TRUMP-21;So;0;ON;;;;;N;;;;;
+1F100;DIGIT ZERO FULL STOP;No;0;EN;<compat> 0030 002E;;0;0;N;;;;;
+1F101;DIGIT ZERO COMMA;No;0;EN;<compat> 0030 002C;;0;0;N;;;;;
+1F102;DIGIT ONE COMMA;No;0;EN;<compat> 0031 002C;;1;1;N;;;;;
+1F103;DIGIT TWO COMMA;No;0;EN;<compat> 0032 002C;;2;2;N;;;;;
+1F104;DIGIT THREE COMMA;No;0;EN;<compat> 0033 002C;;3;3;N;;;;;
+1F105;DIGIT FOUR COMMA;No;0;EN;<compat> 0034 002C;;4;4;N;;;;;
+1F106;DIGIT FIVE COMMA;No;0;EN;<compat> 0035 002C;;5;5;N;;;;;
+1F107;DIGIT SIX COMMA;No;0;EN;<compat> 0036 002C;;6;6;N;;;;;
+1F108;DIGIT SEVEN COMMA;No;0;EN;<compat> 0037 002C;;7;7;N;;;;;
+1F109;DIGIT EIGHT COMMA;No;0;EN;<compat> 0038 002C;;8;8;N;;;;;
+1F10A;DIGIT NINE COMMA;No;0;EN;<compat> 0039 002C;;9;9;N;;;;;
+1F10B;DINGBAT CIRCLED SANS-SERIF DIGIT ZERO;No;0;ON;;;;0;N;;;;;
+1F10C;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ZERO;No;0;ON;;;;0;N;;;;;
+1F110;PARENTHESIZED LATIN CAPITAL LETTER A;So;0;L;<compat> 0028 0041 0029;;;;N;;;;;
+1F111;PARENTHESIZED LATIN CAPITAL LETTER B;So;0;L;<compat> 0028 0042 0029;;;;N;;;;;
+1F112;PARENTHESIZED LATIN CAPITAL LETTER C;So;0;L;<compat> 0028 0043 0029;;;;N;;;;;
+1F113;PARENTHESIZED LATIN CAPITAL LETTER D;So;0;L;<compat> 0028 0044 0029;;;;N;;;;;
+1F114;PARENTHESIZED LATIN CAPITAL LETTER E;So;0;L;<compat> 0028 0045 0029;;;;N;;;;;
+1F115;PARENTHESIZED LATIN CAPITAL LETTER F;So;0;L;<compat> 0028 0046 0029;;;;N;;;;;
+1F116;PARENTHESIZED LATIN CAPITAL LETTER G;So;0;L;<compat> 0028 0047 0029;;;;N;;;;;
+1F117;PARENTHESIZED LATIN CAPITAL LETTER H;So;0;L;<compat> 0028 0048 0029;;;;N;;;;;
+1F118;PARENTHESIZED LATIN CAPITAL LETTER I;So;0;L;<compat> 0028 0049 0029;;;;N;;;;;
+1F119;PARENTHESIZED LATIN CAPITAL LETTER J;So;0;L;<compat> 0028 004A 0029;;;;N;;;;;
+1F11A;PARENTHESIZED LATIN CAPITAL LETTER K;So;0;L;<compat> 0028 004B 0029;;;;N;;;;;
+1F11B;PARENTHESIZED LATIN CAPITAL LETTER L;So;0;L;<compat> 0028 004C 0029;;;;N;;;;;
+1F11C;PARENTHESIZED LATIN CAPITAL LETTER M;So;0;L;<compat> 0028 004D 0029;;;;N;;;;;
+1F11D;PARENTHESIZED LATIN CAPITAL LETTER N;So;0;L;<compat> 0028 004E 0029;;;;N;;;;;
+1F11E;PARENTHESIZED LATIN CAPITAL LETTER O;So;0;L;<compat> 0028 004F 0029;;;;N;;;;;
+1F11F;PARENTHESIZED LATIN CAPITAL LETTER P;So;0;L;<compat> 0028 0050 0029;;;;N;;;;;
+1F120;PARENTHESIZED LATIN CAPITAL LETTER Q;So;0;L;<compat> 0028 0051 0029;;;;N;;;;;
+1F121;PARENTHESIZED LATIN CAPITAL LETTER R;So;0;L;<compat> 0028 0052 0029;;;;N;;;;;
+1F122;PARENTHESIZED LATIN CAPITAL LETTER S;So;0;L;<compat> 0028 0053 0029;;;;N;;;;;
+1F123;PARENTHESIZED LATIN CAPITAL LETTER T;So;0;L;<compat> 0028 0054 0029;;;;N;;;;;
+1F124;PARENTHESIZED LATIN CAPITAL LETTER U;So;0;L;<compat> 0028 0055 0029;;;;N;;;;;
+1F125;PARENTHESIZED LATIN CAPITAL LETTER V;So;0;L;<compat> 0028 0056 0029;;;;N;;;;;
+1F126;PARENTHESIZED LATIN CAPITAL LETTER W;So;0;L;<compat> 0028 0057 0029;;;;N;;;;;
+1F127;PARENTHESIZED LATIN CAPITAL LETTER X;So;0;L;<compat> 0028 0058 0029;;;;N;;;;;
+1F128;PARENTHESIZED LATIN CAPITAL LETTER Y;So;0;L;<compat> 0028 0059 0029;;;;N;;;;;
+1F129;PARENTHESIZED LATIN CAPITAL LETTER Z;So;0;L;<compat> 0028 005A 0029;;;;N;;;;;
+1F12A;TORTOISE SHELL BRACKETED LATIN CAPITAL LETTER S;So;0;L;<compat> 3014 0053 3015;;;;N;;;;;
+1F12B;CIRCLED ITALIC LATIN CAPITAL LETTER C;So;0;L;<circle> 0043;;;;N;;;;;
+1F12C;CIRCLED ITALIC LATIN CAPITAL LETTER R;So;0;L;<circle> 0052;;;;N;;;;;
+1F12D;CIRCLED CD;So;0;L;<circle> 0043 0044;;;;N;;;;;
+1F12E;CIRCLED WZ;So;0;L;<circle> 0057 005A;;;;N;;;;;
+1F12F;COPYLEFT SYMBOL;So;0;ON;;;;;N;;;;;
+1F130;SQUARED LATIN CAPITAL LETTER A;So;0;L;<square> 0041;;;;N;;;;;
+1F131;SQUARED LATIN CAPITAL LETTER B;So;0;L;<square> 0042;;;;N;;;;;
+1F132;SQUARED LATIN CAPITAL LETTER C;So;0;L;<square> 0043;;;;N;;;;;
+1F133;SQUARED LATIN CAPITAL LETTER D;So;0;L;<square> 0044;;;;N;;;;;
+1F134;SQUARED LATIN CAPITAL LETTER E;So;0;L;<square> 0045;;;;N;;;;;
+1F135;SQUARED LATIN CAPITAL LETTER F;So;0;L;<square> 0046;;;;N;;;;;
+1F136;SQUARED LATIN CAPITAL LETTER G;So;0;L;<square> 0047;;;;N;;;;;
+1F137;SQUARED LATIN CAPITAL LETTER H;So;0;L;<square> 0048;;;;N;;;;;
+1F138;SQUARED LATIN CAPITAL LETTER I;So;0;L;<square> 0049;;;;N;;;;;
+1F139;SQUARED LATIN CAPITAL LETTER J;So;0;L;<square> 004A;;;;N;;;;;
+1F13A;SQUARED LATIN CAPITAL LETTER K;So;0;L;<square> 004B;;;;N;;;;;
+1F13B;SQUARED LATIN CAPITAL LETTER L;So;0;L;<square> 004C;;;;N;;;;;
+1F13C;SQUARED LATIN CAPITAL LETTER M;So;0;L;<square> 004D;;;;N;;;;;
+1F13D;SQUARED LATIN CAPITAL LETTER N;So;0;L;<square> 004E;;;;N;;;;;
+1F13E;SQUARED LATIN CAPITAL LETTER O;So;0;L;<square> 004F;;;;N;;;;;
+1F13F;SQUARED LATIN CAPITAL LETTER P;So;0;L;<square> 0050;;;;N;;;;;
+1F140;SQUARED LATIN CAPITAL LETTER Q;So;0;L;<square> 0051;;;;N;;;;;
+1F141;SQUARED LATIN CAPITAL LETTER R;So;0;L;<square> 0052;;;;N;;;;;
+1F142;SQUARED LATIN CAPITAL LETTER S;So;0;L;<square> 0053;;;;N;;;;;
+1F143;SQUARED LATIN CAPITAL LETTER T;So;0;L;<square> 0054;;;;N;;;;;
+1F144;SQUARED LATIN CAPITAL LETTER U;So;0;L;<square> 0055;;;;N;;;;;
+1F145;SQUARED LATIN CAPITAL LETTER V;So;0;L;<square> 0056;;;;N;;;;;
+1F146;SQUARED LATIN CAPITAL LETTER W;So;0;L;<square> 0057;;;;N;;;;;
+1F147;SQUARED LATIN CAPITAL LETTER X;So;0;L;<square> 0058;;;;N;;;;;
+1F148;SQUARED LATIN CAPITAL LETTER Y;So;0;L;<square> 0059;;;;N;;;;;
+1F149;SQUARED LATIN CAPITAL LETTER Z;So;0;L;<square> 005A;;;;N;;;;;
+1F14A;SQUARED HV;So;0;L;<square> 0048 0056;;;;N;;;;;
+1F14B;SQUARED MV;So;0;L;<square> 004D 0056;;;;N;;;;;
+1F14C;SQUARED SD;So;0;L;<square> 0053 0044;;;;N;;;;;
+1F14D;SQUARED SS;So;0;L;<square> 0053 0053;;;;N;;;;;
+1F14E;SQUARED PPV;So;0;L;<square> 0050 0050 0056;;;;N;;;;;
+1F14F;SQUARED WC;So;0;L;<square> 0057 0043;;;;N;;;;;
+1F150;NEGATIVE CIRCLED LATIN CAPITAL LETTER A;So;0;L;;;;;N;;;;;
+1F151;NEGATIVE CIRCLED LATIN CAPITAL LETTER B;So;0;L;;;;;N;;;;;
+1F152;NEGATIVE CIRCLED LATIN CAPITAL LETTER C;So;0;L;;;;;N;;;;;
+1F153;NEGATIVE CIRCLED LATIN CAPITAL LETTER D;So;0;L;;;;;N;;;;;
+1F154;NEGATIVE CIRCLED LATIN CAPITAL LETTER E;So;0;L;;;;;N;;;;;
+1F155;NEGATIVE CIRCLED LATIN CAPITAL LETTER F;So;0;L;;;;;N;;;;;
+1F156;NEGATIVE CIRCLED LATIN CAPITAL LETTER G;So;0;L;;;;;N;;;;;
+1F157;NEGATIVE CIRCLED LATIN CAPITAL LETTER H;So;0;L;;;;;N;;;;;
+1F158;NEGATIVE CIRCLED LATIN CAPITAL LETTER I;So;0;L;;;;;N;;;;;
+1F159;NEGATIVE CIRCLED LATIN CAPITAL LETTER J;So;0;L;;;;;N;;;;;
+1F15A;NEGATIVE CIRCLED LATIN CAPITAL LETTER K;So;0;L;;;;;N;;;;;
+1F15B;NEGATIVE CIRCLED LATIN CAPITAL LETTER L;So;0;L;;;;;N;;;;;
+1F15C;NEGATIVE CIRCLED LATIN CAPITAL LETTER M;So;0;L;;;;;N;;;;;
+1F15D;NEGATIVE CIRCLED LATIN CAPITAL LETTER N;So;0;L;;;;;N;;;;;
+1F15E;NEGATIVE CIRCLED LATIN CAPITAL LETTER O;So;0;L;;;;;N;;;;;
+1F15F;NEGATIVE CIRCLED LATIN CAPITAL LETTER P;So;0;L;;;;;N;;;;;
+1F160;NEGATIVE CIRCLED LATIN CAPITAL LETTER Q;So;0;L;;;;;N;;;;;
+1F161;NEGATIVE CIRCLED LATIN CAPITAL LETTER R;So;0;L;;;;;N;;;;;
+1F162;NEGATIVE CIRCLED LATIN CAPITAL LETTER S;So;0;L;;;;;N;;;;;
+1F163;NEGATIVE CIRCLED LATIN CAPITAL LETTER T;So;0;L;;;;;N;;;;;
+1F164;NEGATIVE CIRCLED LATIN CAPITAL LETTER U;So;0;L;;;;;N;;;;;
+1F165;NEGATIVE CIRCLED LATIN CAPITAL LETTER V;So;0;L;;;;;N;;;;;
+1F166;NEGATIVE CIRCLED LATIN CAPITAL LETTER W;So;0;L;;;;;N;;;;;
+1F167;NEGATIVE CIRCLED LATIN CAPITAL LETTER X;So;0;L;;;;;N;;;;;
+1F168;NEGATIVE CIRCLED LATIN CAPITAL LETTER Y;So;0;L;;;;;N;;;;;
+1F169;NEGATIVE CIRCLED LATIN CAPITAL LETTER Z;So;0;L;;;;;N;;;;;
+1F16A;RAISED MC SIGN;So;0;ON;<super> 004D 0043;;;;N;;;;;
+1F16B;RAISED MD SIGN;So;0;ON;<super> 004D 0044;;;;N;;;;;
+1F16C;RAISED MR SIGN;So;0;ON;<super> 004D 0052;;;;N;;;;;
+1F170;NEGATIVE SQUARED LATIN CAPITAL LETTER A;So;0;L;;;;;N;;;;;
+1F171;NEGATIVE SQUARED LATIN CAPITAL LETTER B;So;0;L;;;;;N;;;;;
+1F172;NEGATIVE SQUARED LATIN CAPITAL LETTER C;So;0;L;;;;;N;;;;;
+1F173;NEGATIVE SQUARED LATIN CAPITAL LETTER D;So;0;L;;;;;N;;;;;
+1F174;NEGATIVE SQUARED LATIN CAPITAL LETTER E;So;0;L;;;;;N;;;;;
+1F175;NEGATIVE SQUARED LATIN CAPITAL LETTER F;So;0;L;;;;;N;;;;;
+1F176;NEGATIVE SQUARED LATIN CAPITAL LETTER G;So;0;L;;;;;N;;;;;
+1F177;NEGATIVE SQUARED LATIN CAPITAL LETTER H;So;0;L;;;;;N;;;;;
+1F178;NEGATIVE SQUARED LATIN CAPITAL LETTER I;So;0;L;;;;;N;;;;;
+1F179;NEGATIVE SQUARED LATIN CAPITAL LETTER J;So;0;L;;;;;N;;;;;
+1F17A;NEGATIVE SQUARED LATIN CAPITAL LETTER K;So;0;L;;;;;N;;;;;
+1F17B;NEGATIVE SQUARED LATIN CAPITAL LETTER L;So;0;L;;;;;N;;;;;
+1F17C;NEGATIVE SQUARED LATIN CAPITAL LETTER M;So;0;L;;;;;N;;;;;
+1F17D;NEGATIVE SQUARED LATIN CAPITAL LETTER N;So;0;L;;;;;N;;;;;
+1F17E;NEGATIVE SQUARED LATIN CAPITAL LETTER O;So;0;L;;;;;N;;;;;
+1F17F;NEGATIVE SQUARED LATIN CAPITAL LETTER P;So;0;L;;;;;N;;;;;
+1F180;NEGATIVE SQUARED LATIN CAPITAL LETTER Q;So;0;L;;;;;N;;;;;
+1F181;NEGATIVE SQUARED LATIN CAPITAL LETTER R;So;0;L;;;;;N;;;;;
+1F182;NEGATIVE SQUARED LATIN CAPITAL LETTER S;So;0;L;;;;;N;;;;;
+1F183;NEGATIVE SQUARED LATIN CAPITAL LETTER T;So;0;L;;;;;N;;;;;
+1F184;NEGATIVE SQUARED LATIN CAPITAL LETTER U;So;0;L;;;;;N;;;;;
+1F185;NEGATIVE SQUARED LATIN CAPITAL LETTER V;So;0;L;;;;;N;;;;;
+1F186;NEGATIVE SQUARED LATIN CAPITAL LETTER W;So;0;L;;;;;N;;;;;
+1F187;NEGATIVE SQUARED LATIN CAPITAL LETTER X;So;0;L;;;;;N;;;;;
+1F188;NEGATIVE SQUARED LATIN CAPITAL LETTER Y;So;0;L;;;;;N;;;;;
+1F189;NEGATIVE SQUARED LATIN CAPITAL LETTER Z;So;0;L;;;;;N;;;;;
+1F18A;CROSSED NEGATIVE SQUARED LATIN CAPITAL LETTER P;So;0;L;;;;;N;;;;;
+1F18B;NEGATIVE SQUARED IC;So;0;L;;;;;N;;;;;
+1F18C;NEGATIVE SQUARED PA;So;0;L;;;;;N;;;;;
+1F18D;NEGATIVE SQUARED SA;So;0;L;;;;;N;;;;;
+1F18E;NEGATIVE SQUARED AB;So;0;L;;;;;N;;;;;
+1F18F;NEGATIVE SQUARED WC;So;0;L;;;;;N;;;;;
+1F190;SQUARE DJ;So;0;L;<square> 0044 004A;;;;N;;;;;
+1F191;SQUARED CL;So;0;L;;;;;N;;;;;
+1F192;SQUARED COOL;So;0;L;;;;;N;;;;;
+1F193;SQUARED FREE;So;0;L;;;;;N;;;;;
+1F194;SQUARED ID;So;0;L;;;;;N;;;;;
+1F195;SQUARED NEW;So;0;L;;;;;N;;;;;
+1F196;SQUARED NG;So;0;L;;;;;N;;;;;
+1F197;SQUARED OK;So;0;L;;;;;N;;;;;
+1F198;SQUARED SOS;So;0;L;;;;;N;;;;;
+1F199;SQUARED UP WITH EXCLAMATION MARK;So;0;L;;;;;N;;;;;
+1F19A;SQUARED VS;So;0;L;;;;;N;;;;;
+1F19B;SQUARED THREE D;So;0;L;;;;;N;;;;;
+1F19C;SQUARED SECOND SCREEN;So;0;L;;;;;N;;;;;
+1F19D;SQUARED TWO K;So;0;L;;;;;N;;;;;
+1F19E;SQUARED FOUR K;So;0;L;;;;;N;;;;;
+1F19F;SQUARED EIGHT K;So;0;L;;;;;N;;;;;
+1F1A0;SQUARED FIVE POINT ONE;So;0;L;;;;;N;;;;;
+1F1A1;SQUARED SEVEN POINT ONE;So;0;L;;;;;N;;;;;
+1F1A2;SQUARED TWENTY-TWO POINT TWO;So;0;L;;;;;N;;;;;
+1F1A3;SQUARED SIXTY P;So;0;L;;;;;N;;;;;
+1F1A4;SQUARED ONE HUNDRED TWENTY P;So;0;L;;;;;N;;;;;
+1F1A5;SQUARED LATIN SMALL LETTER D;So;0;L;;;;;N;;;;;
+1F1A6;SQUARED HC;So;0;L;;;;;N;;;;;
+1F1A7;SQUARED HDR;So;0;L;;;;;N;;;;;
+1F1A8;SQUARED HI-RES;So;0;L;;;;;N;;;;;
+1F1A9;SQUARED LOSSLESS;So;0;L;;;;;N;;;;;
+1F1AA;SQUARED SHV;So;0;L;;;;;N;;;;;
+1F1AB;SQUARED UHD;So;0;L;;;;;N;;;;;
+1F1AC;SQUARED VOD;So;0;L;;;;;N;;;;;
+1F1E6;REGIONAL INDICATOR SYMBOL LETTER A;So;0;L;;;;;N;;;;;
+1F1E7;REGIONAL INDICATOR SYMBOL LETTER B;So;0;L;;;;;N;;;;;
+1F1E8;REGIONAL INDICATOR SYMBOL LETTER C;So;0;L;;;;;N;;;;;
+1F1E9;REGIONAL INDICATOR SYMBOL LETTER D;So;0;L;;;;;N;;;;;
+1F1EA;REGIONAL INDICATOR SYMBOL LETTER E;So;0;L;;;;;N;;;;;
+1F1EB;REGIONAL INDICATOR SYMBOL LETTER F;So;0;L;;;;;N;;;;;
+1F1EC;REGIONAL INDICATOR SYMBOL LETTER G;So;0;L;;;;;N;;;;;
+1F1ED;REGIONAL INDICATOR SYMBOL LETTER H;So;0;L;;;;;N;;;;;
+1F1EE;REGIONAL INDICATOR SYMBOL LETTER I;So;0;L;;;;;N;;;;;
+1F1EF;REGIONAL INDICATOR SYMBOL LETTER J;So;0;L;;;;;N;;;;;
+1F1F0;REGIONAL INDICATOR SYMBOL LETTER K;So;0;L;;;;;N;;;;;
+1F1F1;REGIONAL INDICATOR SYMBOL LETTER L;So;0;L;;;;;N;;;;;
+1F1F2;REGIONAL INDICATOR SYMBOL LETTER M;So;0;L;;;;;N;;;;;
+1F1F3;REGIONAL INDICATOR SYMBOL LETTER N;So;0;L;;;;;N;;;;;
+1F1F4;REGIONAL INDICATOR SYMBOL LETTER O;So;0;L;;;;;N;;;;;
+1F1F5;REGIONAL INDICATOR SYMBOL LETTER P;So;0;L;;;;;N;;;;;
+1F1F6;REGIONAL INDICATOR SYMBOL LETTER Q;So;0;L;;;;;N;;;;;
+1F1F7;REGIONAL INDICATOR SYMBOL LETTER R;So;0;L;;;;;N;;;;;
+1F1F8;REGIONAL INDICATOR SYMBOL LETTER S;So;0;L;;;;;N;;;;;
+1F1F9;REGIONAL INDICATOR SYMBOL LETTER T;So;0;L;;;;;N;;;;;
+1F1FA;REGIONAL INDICATOR SYMBOL LETTER U;So;0;L;;;;;N;;;;;
+1F1FB;REGIONAL INDICATOR SYMBOL LETTER V;So;0;L;;;;;N;;;;;
+1F1FC;REGIONAL INDICATOR SYMBOL LETTER W;So;0;L;;;;;N;;;;;
+1F1FD;REGIONAL INDICATOR SYMBOL LETTER X;So;0;L;;;;;N;;;;;
+1F1FE;REGIONAL INDICATOR SYMBOL LETTER Y;So;0;L;;;;;N;;;;;
+1F1FF;REGIONAL INDICATOR SYMBOL LETTER Z;So;0;L;;;;;N;;;;;
+1F200;SQUARE HIRAGANA HOKA;So;0;L;<square> 307B 304B;;;;N;;;;;
+1F201;SQUARED KATAKANA KOKO;So;0;L;<square> 30B3 30B3;;;;N;;;;;
+1F202;SQUARED KATAKANA SA;So;0;L;<square> 30B5;;;;N;;;;;
+1F210;SQUARED CJK UNIFIED IDEOGRAPH-624B;So;0;L;<square> 624B;;;;N;;;;;
+1F211;SQUARED CJK UNIFIED IDEOGRAPH-5B57;So;0;L;<square> 5B57;;;;N;;;;;
+1F212;SQUARED CJK UNIFIED IDEOGRAPH-53CC;So;0;L;<square> 53CC;;;;N;;;;;
+1F213;SQUARED KATAKANA DE;So;0;L;<square> 30C7;;;;N;;;;;
+1F214;SQUARED CJK UNIFIED IDEOGRAPH-4E8C;So;0;L;<square> 4E8C;;;;N;;;;;
+1F215;SQUARED CJK UNIFIED IDEOGRAPH-591A;So;0;L;<square> 591A;;;;N;;;;;
+1F216;SQUARED CJK UNIFIED IDEOGRAPH-89E3;So;0;L;<square> 89E3;;;;N;;;;;
+1F217;SQUARED CJK UNIFIED IDEOGRAPH-5929;So;0;L;<square> 5929;;;;N;;;;;
+1F218;SQUARED CJK UNIFIED IDEOGRAPH-4EA4;So;0;L;<square> 4EA4;;;;N;;;;;
+1F219;SQUARED CJK UNIFIED IDEOGRAPH-6620;So;0;L;<square> 6620;;;;N;;;;;
+1F21A;SQUARED CJK UNIFIED IDEOGRAPH-7121;So;0;L;<square> 7121;;;;N;;;;;
+1F21B;SQUARED CJK UNIFIED IDEOGRAPH-6599;So;0;L;<square> 6599;;;;N;;;;;
+1F21C;SQUARED CJK UNIFIED IDEOGRAPH-524D;So;0;L;<square> 524D;;;;N;;;;;
+1F21D;SQUARED CJK UNIFIED IDEOGRAPH-5F8C;So;0;L;<square> 5F8C;;;;N;;;;;
+1F21E;SQUARED CJK UNIFIED IDEOGRAPH-518D;So;0;L;<square> 518D;;;;N;;;;;
+1F21F;SQUARED CJK UNIFIED IDEOGRAPH-65B0;So;0;L;<square> 65B0;;;;N;;;;;
+1F220;SQUARED CJK UNIFIED IDEOGRAPH-521D;So;0;L;<square> 521D;;;;N;;;;;
+1F221;SQUARED CJK UNIFIED IDEOGRAPH-7D42;So;0;L;<square> 7D42;;;;N;;;;;
+1F222;SQUARED CJK UNIFIED IDEOGRAPH-751F;So;0;L;<square> 751F;;;;N;;;;;
+1F223;SQUARED CJK UNIFIED IDEOGRAPH-8CA9;So;0;L;<square> 8CA9;;;;N;;;;;
+1F224;SQUARED CJK UNIFIED IDEOGRAPH-58F0;So;0;L;<square> 58F0;;;;N;;;;;
+1F225;SQUARED CJK UNIFIED IDEOGRAPH-5439;So;0;L;<square> 5439;;;;N;;;;;
+1F226;SQUARED CJK UNIFIED IDEOGRAPH-6F14;So;0;L;<square> 6F14;;;;N;;;;;
+1F227;SQUARED CJK UNIFIED IDEOGRAPH-6295;So;0;L;<square> 6295;;;;N;;;;;
+1F228;SQUARED CJK UNIFIED IDEOGRAPH-6355;So;0;L;<square> 6355;;;;N;;;;;
+1F229;SQUARED CJK UNIFIED IDEOGRAPH-4E00;So;0;L;<square> 4E00;;;;N;;;;;
+1F22A;SQUARED CJK UNIFIED IDEOGRAPH-4E09;So;0;L;<square> 4E09;;;;N;;;;;
+1F22B;SQUARED CJK UNIFIED IDEOGRAPH-904A;So;0;L;<square> 904A;;;;N;;;;;
+1F22C;SQUARED CJK UNIFIED IDEOGRAPH-5DE6;So;0;L;<square> 5DE6;;;;N;;;;;
+1F22D;SQUARED CJK UNIFIED IDEOGRAPH-4E2D;So;0;L;<square> 4E2D;;;;N;;;;;
+1F22E;SQUARED CJK UNIFIED IDEOGRAPH-53F3;So;0;L;<square> 53F3;;;;N;;;;;
+1F22F;SQUARED CJK UNIFIED IDEOGRAPH-6307;So;0;L;<square> 6307;;;;N;;;;;
+1F230;SQUARED CJK UNIFIED IDEOGRAPH-8D70;So;0;L;<square> 8D70;;;;N;;;;;
+1F231;SQUARED CJK UNIFIED IDEOGRAPH-6253;So;0;L;<square> 6253;;;;N;;;;;
+1F232;SQUARED CJK UNIFIED IDEOGRAPH-7981;So;0;L;<square> 7981;;;;N;;;;;
+1F233;SQUARED CJK UNIFIED IDEOGRAPH-7A7A;So;0;L;<square> 7A7A;;;;N;;;;;
+1F234;SQUARED CJK UNIFIED IDEOGRAPH-5408;So;0;L;<square> 5408;;;;N;;;;;
+1F235;SQUARED CJK UNIFIED IDEOGRAPH-6E80;So;0;L;<square> 6E80;;;;N;;;;;
+1F236;SQUARED CJK UNIFIED IDEOGRAPH-6709;So;0;L;<square> 6709;;;;N;;;;;
+1F237;SQUARED CJK UNIFIED IDEOGRAPH-6708;So;0;L;<square> 6708;;;;N;;;;;
+1F238;SQUARED CJK UNIFIED IDEOGRAPH-7533;So;0;L;<square> 7533;;;;N;;;;;
+1F239;SQUARED CJK UNIFIED IDEOGRAPH-5272;So;0;L;<square> 5272;;;;N;;;;;
+1F23A;SQUARED CJK UNIFIED IDEOGRAPH-55B6;So;0;L;<square> 55B6;;;;N;;;;;
+1F23B;SQUARED CJK UNIFIED IDEOGRAPH-914D;So;0;L;<square> 914D;;;;N;;;;;
+1F240;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-672C;So;0;L;<compat> 3014 672C 3015;;;;N;;;;;
+1F241;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-4E09;So;0;L;<compat> 3014 4E09 3015;;;;N;;;;;
+1F242;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-4E8C;So;0;L;<compat> 3014 4E8C 3015;;;;N;;;;;
+1F243;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-5B89;So;0;L;<compat> 3014 5B89 3015;;;;N;;;;;
+1F244;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-70B9;So;0;L;<compat> 3014 70B9 3015;;;;N;;;;;
+1F245;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-6253;So;0;L;<compat> 3014 6253 3015;;;;N;;;;;
+1F246;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-76D7;So;0;L;<compat> 3014 76D7 3015;;;;N;;;;;
+1F247;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-52DD;So;0;L;<compat> 3014 52DD 3015;;;;N;;;;;
+1F248;TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-6557;So;0;L;<compat> 3014 6557 3015;;;;N;;;;;
+1F250;CIRCLED IDEOGRAPH ADVANTAGE;So;0;L;<circle> 5F97;;;;N;;;;;
+1F251;CIRCLED IDEOGRAPH ACCEPT;So;0;L;<circle> 53EF;;;;N;;;;;
+1F260;ROUNDED SYMBOL FOR FU;So;0;ON;;;;;N;;;;;
+1F261;ROUNDED SYMBOL FOR LU;So;0;ON;;;;;N;;;;;
+1F262;ROUNDED SYMBOL FOR SHOU;So;0;ON;;;;;N;;;;;
+1F263;ROUNDED SYMBOL FOR XI;So;0;ON;;;;;N;;;;;
+1F264;ROUNDED SYMBOL FOR SHUANGXI;So;0;ON;;;;;N;;;;;
+1F265;ROUNDED SYMBOL FOR CAI;So;0;ON;;;;;N;;;;;
+1F300;CYCLONE;So;0;ON;;;;;N;;;;;
+1F301;FOGGY;So;0;ON;;;;;N;;;;;
+1F302;CLOSED UMBRELLA;So;0;ON;;;;;N;;;;;
+1F303;NIGHT WITH STARS;So;0;ON;;;;;N;;;;;
+1F304;SUNRISE OVER MOUNTAINS;So;0;ON;;;;;N;;;;;
+1F305;SUNRISE;So;0;ON;;;;;N;;;;;
+1F306;CITYSCAPE AT DUSK;So;0;ON;;;;;N;;;;;
+1F307;SUNSET OVER BUILDINGS;So;0;ON;;;;;N;;;;;
+1F308;RAINBOW;So;0;ON;;;;;N;;;;;
+1F309;BRIDGE AT NIGHT;So;0;ON;;;;;N;;;;;
+1F30A;WATER WAVE;So;0;ON;;;;;N;;;;;
+1F30B;VOLCANO;So;0;ON;;;;;N;;;;;
+1F30C;MILKY WAY;So;0;ON;;;;;N;;;;;
+1F30D;EARTH GLOBE EUROPE-AFRICA;So;0;ON;;;;;N;;;;;
+1F30E;EARTH GLOBE AMERICAS;So;0;ON;;;;;N;;;;;
+1F30F;EARTH GLOBE ASIA-AUSTRALIA;So;0;ON;;;;;N;;;;;
+1F310;GLOBE WITH MERIDIANS;So;0;ON;;;;;N;;;;;
+1F311;NEW MOON SYMBOL;So;0;ON;;;;;N;;;;;
+1F312;WAXING CRESCENT MOON SYMBOL;So;0;ON;;;;;N;;;;;
+1F313;FIRST QUARTER MOON SYMBOL;So;0;ON;;;;;N;;;;;
+1F314;WAXING GIBBOUS MOON SYMBOL;So;0;ON;;;;;N;;;;;
+1F315;FULL MOON SYMBOL;So;0;ON;;;;;N;;;;;
+1F316;WANING GIBBOUS MOON SYMBOL;So;0;ON;;;;;N;;;;;
+1F317;LAST QUARTER MOON SYMBOL;So;0;ON;;;;;N;;;;;
+1F318;WANING CRESCENT MOON SYMBOL;So;0;ON;;;;;N;;;;;
+1F319;CRESCENT MOON;So;0;ON;;;;;N;;;;;
+1F31A;NEW MOON WITH FACE;So;0;ON;;;;;N;;;;;
+1F31B;FIRST QUARTER MOON WITH FACE;So;0;ON;;;;;N;;;;;
+1F31C;LAST QUARTER MOON WITH FACE;So;0;ON;;;;;N;;;;;
+1F31D;FULL MOON WITH FACE;So;0;ON;;;;;N;;;;;
+1F31E;SUN WITH FACE;So;0;ON;;;;;N;;;;;
+1F31F;GLOWING STAR;So;0;ON;;;;;N;;;;;
+1F320;SHOOTING STAR;So;0;ON;;;;;N;;;;;
+1F321;THERMOMETER;So;0;ON;;;;;N;;;;;
+1F322;BLACK DROPLET;So;0;ON;;;;;N;;;;;
+1F323;WHITE SUN;So;0;ON;;;;;N;;;;;
+1F324;WHITE SUN WITH SMALL CLOUD;So;0;ON;;;;;N;;;;;
+1F325;WHITE SUN BEHIND CLOUD;So;0;ON;;;;;N;;;;;
+1F326;WHITE SUN BEHIND CLOUD WITH RAIN;So;0;ON;;;;;N;;;;;
+1F327;CLOUD WITH RAIN;So;0;ON;;;;;N;;;;;
+1F328;CLOUD WITH SNOW;So;0;ON;;;;;N;;;;;
+1F329;CLOUD WITH LIGHTNING;So;0;ON;;;;;N;;;;;
+1F32A;CLOUD WITH TORNADO;So;0;ON;;;;;N;;;;;
+1F32B;FOG;So;0;ON;;;;;N;;;;;
+1F32C;WIND BLOWING FACE;So;0;ON;;;;;N;;;;;
+1F32D;HOT DOG;So;0;ON;;;;;N;;;;;
+1F32E;TACO;So;0;ON;;;;;N;;;;;
+1F32F;BURRITO;So;0;ON;;;;;N;;;;;
+1F330;CHESTNUT;So;0;ON;;;;;N;;;;;
+1F331;SEEDLING;So;0;ON;;;;;N;;;;;
+1F332;EVERGREEN TREE;So;0;ON;;;;;N;;;;;
+1F333;DECIDUOUS TREE;So;0;ON;;;;;N;;;;;
+1F334;PALM TREE;So;0;ON;;;;;N;;;;;
+1F335;CACTUS;So;0;ON;;;;;N;;;;;
+1F336;HOT PEPPER;So;0;ON;;;;;N;;;;;
+1F337;TULIP;So;0;ON;;;;;N;;;;;
+1F338;CHERRY BLOSSOM;So;0;ON;;;;;N;;;;;
+1F339;ROSE;So;0;ON;;;;;N;;;;;
+1F33A;HIBISCUS;So;0;ON;;;;;N;;;;;
+1F33B;SUNFLOWER;So;0;ON;;;;;N;;;;;
+1F33C;BLOSSOM;So;0;ON;;;;;N;;;;;
+1F33D;EAR OF MAIZE;So;0;ON;;;;;N;;;;;
+1F33E;EAR OF RICE;So;0;ON;;;;;N;;;;;
+1F33F;HERB;So;0;ON;;;;;N;;;;;
+1F340;FOUR LEAF CLOVER;So;0;ON;;;;;N;;;;;
+1F341;MAPLE LEAF;So;0;ON;;;;;N;;;;;
+1F342;FALLEN LEAF;So;0;ON;;;;;N;;;;;
+1F343;LEAF FLUTTERING IN WIND;So;0;ON;;;;;N;;;;;
+1F344;MUSHROOM;So;0;ON;;;;;N;;;;;
+1F345;TOMATO;So;0;ON;;;;;N;;;;;
+1F346;AUBERGINE;So;0;ON;;;;;N;;;;;
+1F347;GRAPES;So;0;ON;;;;;N;;;;;
+1F348;MELON;So;0;ON;;;;;N;;;;;
+1F349;WATERMELON;So;0;ON;;;;;N;;;;;
+1F34A;TANGERINE;So;0;ON;;;;;N;;;;;
+1F34B;LEMON;So;0;ON;;;;;N;;;;;
+1F34C;BANANA;So;0;ON;;;;;N;;;;;
+1F34D;PINEAPPLE;So;0;ON;;;;;N;;;;;
+1F34E;RED APPLE;So;0;ON;;;;;N;;;;;
+1F34F;GREEN APPLE;So;0;ON;;;;;N;;;;;
+1F350;PEAR;So;0;ON;;;;;N;;;;;
+1F351;PEACH;So;0;ON;;;;;N;;;;;
+1F352;CHERRIES;So;0;ON;;;;;N;;;;;
+1F353;STRAWBERRY;So;0;ON;;;;;N;;;;;
+1F354;HAMBURGER;So;0;ON;;;;;N;;;;;
+1F355;SLICE OF PIZZA;So;0;ON;;;;;N;;;;;
+1F356;MEAT ON BONE;So;0;ON;;;;;N;;;;;
+1F357;POULTRY LEG;So;0;ON;;;;;N;;;;;
+1F358;RICE CRACKER;So;0;ON;;;;;N;;;;;
+1F359;RICE BALL;So;0;ON;;;;;N;;;;;
+1F35A;COOKED RICE;So;0;ON;;;;;N;;;;;
+1F35B;CURRY AND RICE;So;0;ON;;;;;N;;;;;
+1F35C;STEAMING BOWL;So;0;ON;;;;;N;;;;;
+1F35D;SPAGHETTI;So;0;ON;;;;;N;;;;;
+1F35E;BREAD;So;0;ON;;;;;N;;;;;
+1F35F;FRENCH FRIES;So;0;ON;;;;;N;;;;;
+1F360;ROASTED SWEET POTATO;So;0;ON;;;;;N;;;;;
+1F361;DANGO;So;0;ON;;;;;N;;;;;
+1F362;ODEN;So;0;ON;;;;;N;;;;;
+1F363;SUSHI;So;0;ON;;;;;N;;;;;
+1F364;FRIED SHRIMP;So;0;ON;;;;;N;;;;;
+1F365;FISH CAKE WITH SWIRL DESIGN;So;0;ON;;;;;N;;;;;
+1F366;SOFT ICE CREAM;So;0;ON;;;;;N;;;;;
+1F367;SHAVED ICE;So;0;ON;;;;;N;;;;;
+1F368;ICE CREAM;So;0;ON;;;;;N;;;;;
+1F369;DOUGHNUT;So;0;ON;;;;;N;;;;;
+1F36A;COOKIE;So;0;ON;;;;;N;;;;;
+1F36B;CHOCOLATE BAR;So;0;ON;;;;;N;;;;;
+1F36C;CANDY;So;0;ON;;;;;N;;;;;
+1F36D;LOLLIPOP;So;0;ON;;;;;N;;;;;
+1F36E;CUSTARD;So;0;ON;;;;;N;;;;;
+1F36F;HONEY POT;So;0;ON;;;;;N;;;;;
+1F370;SHORTCAKE;So;0;ON;;;;;N;;;;;
+1F371;BENTO BOX;So;0;ON;;;;;N;;;;;
+1F372;POT OF FOOD;So;0;ON;;;;;N;;;;;
+1F373;COOKING;So;0;ON;;;;;N;;;;;
+1F374;FORK AND KNIFE;So;0;ON;;;;;N;;;;;
+1F375;TEACUP WITHOUT HANDLE;So;0;ON;;;;;N;;;;;
+1F376;SAKE BOTTLE AND CUP;So;0;ON;;;;;N;;;;;
+1F377;WINE GLASS;So;0;ON;;;;;N;;;;;
+1F378;COCKTAIL GLASS;So;0;ON;;;;;N;;;;;
+1F379;TROPICAL DRINK;So;0;ON;;;;;N;;;;;
+1F37A;BEER MUG;So;0;ON;;;;;N;;;;;
+1F37B;CLINKING BEER MUGS;So;0;ON;;;;;N;;;;;
+1F37C;BABY BOTTLE;So;0;ON;;;;;N;;;;;
+1F37D;FORK AND KNIFE WITH PLATE;So;0;ON;;;;;N;;;;;
+1F37E;BOTTLE WITH POPPING CORK;So;0;ON;;;;;N;;;;;
+1F37F;POPCORN;So;0;ON;;;;;N;;;;;
+1F380;RIBBON;So;0;ON;;;;;N;;;;;
+1F381;WRAPPED PRESENT;So;0;ON;;;;;N;;;;;
+1F382;BIRTHDAY CAKE;So;0;ON;;;;;N;;;;;
+1F383;JACK-O-LANTERN;So;0;ON;;;;;N;;;;;
+1F384;CHRISTMAS TREE;So;0;ON;;;;;N;;;;;
+1F385;FATHER CHRISTMAS;So;0;ON;;;;;N;;;;;
+1F386;FIREWORKS;So;0;ON;;;;;N;;;;;
+1F387;FIREWORK SPARKLER;So;0;ON;;;;;N;;;;;
+1F388;BALLOON;So;0;ON;;;;;N;;;;;
+1F389;PARTY POPPER;So;0;ON;;;;;N;;;;;
+1F38A;CONFETTI BALL;So;0;ON;;;;;N;;;;;
+1F38B;TANABATA TREE;So;0;ON;;;;;N;;;;;
+1F38C;CROSSED FLAGS;So;0;ON;;;;;N;;;;;
+1F38D;PINE DECORATION;So;0;ON;;;;;N;;;;;
+1F38E;JAPANESE DOLLS;So;0;ON;;;;;N;;;;;
+1F38F;CARP STREAMER;So;0;ON;;;;;N;;;;;
+1F390;WIND CHIME;So;0;ON;;;;;N;;;;;
+1F391;MOON VIEWING CEREMONY;So;0;ON;;;;;N;;;;;
+1F392;SCHOOL SATCHEL;So;0;ON;;;;;N;;;;;
+1F393;GRADUATION CAP;So;0;ON;;;;;N;;;;;
+1F394;HEART WITH TIP ON THE LEFT;So;0;ON;;;;;N;;;;;
+1F395;BOUQUET OF FLOWERS;So;0;ON;;;;;N;;;;;
+1F396;MILITARY MEDAL;So;0;ON;;;;;N;;;;;
+1F397;REMINDER RIBBON;So;0;ON;;;;;N;;;;;
+1F398;MUSICAL KEYBOARD WITH JACKS;So;0;ON;;;;;N;;;;;
+1F399;STUDIO MICROPHONE;So;0;ON;;;;;N;;;;;
+1F39A;LEVEL SLIDER;So;0;ON;;;;;N;;;;;
+1F39B;CONTROL KNOBS;So;0;ON;;;;;N;;;;;
+1F39C;BEAMED ASCENDING MUSICAL NOTES;So;0;ON;;;;;N;;;;;
+1F39D;BEAMED DESCENDING MUSICAL NOTES;So;0;ON;;;;;N;;;;;
+1F39E;FILM FRAMES;So;0;ON;;;;;N;;;;;
+1F39F;ADMISSION TICKETS;So;0;ON;;;;;N;;;;;
+1F3A0;CAROUSEL HORSE;So;0;ON;;;;;N;;;;;
+1F3A1;FERRIS WHEEL;So;0;ON;;;;;N;;;;;
+1F3A2;ROLLER COASTER;So;0;ON;;;;;N;;;;;
+1F3A3;FISHING POLE AND FISH;So;0;ON;;;;;N;;;;;
+1F3A4;MICROPHONE;So;0;ON;;;;;N;;;;;
+1F3A5;MOVIE CAMERA;So;0;ON;;;;;N;;;;;
+1F3A6;CINEMA;So;0;ON;;;;;N;;;;;
+1F3A7;HEADPHONE;So;0;ON;;;;;N;;;;;
+1F3A8;ARTIST PALETTE;So;0;ON;;;;;N;;;;;
+1F3A9;TOP HAT;So;0;ON;;;;;N;;;;;
+1F3AA;CIRCUS TENT;So;0;ON;;;;;N;;;;;
+1F3AB;TICKET;So;0;ON;;;;;N;;;;;
+1F3AC;CLAPPER BOARD;So;0;ON;;;;;N;;;;;
+1F3AD;PERFORMING ARTS;So;0;ON;;;;;N;;;;;
+1F3AE;VIDEO GAME;So;0;ON;;;;;N;;;;;
+1F3AF;DIRECT HIT;So;0;ON;;;;;N;;;;;
+1F3B0;SLOT MACHINE;So;0;ON;;;;;N;;;;;
+1F3B1;BILLIARDS;So;0;ON;;;;;N;;;;;
+1F3B2;GAME DIE;So;0;ON;;;;;N;;;;;
+1F3B3;BOWLING;So;0;ON;;;;;N;;;;;
+1F3B4;FLOWER PLAYING CARDS;So;0;ON;;;;;N;;;;;
+1F3B5;MUSICAL NOTE;So;0;ON;;;;;N;;;;;
+1F3B6;MULTIPLE MUSICAL NOTES;So;0;ON;;;;;N;;;;;
+1F3B7;SAXOPHONE;So;0;ON;;;;;N;;;;;
+1F3B8;GUITAR;So;0;ON;;;;;N;;;;;
+1F3B9;MUSICAL KEYBOARD;So;0;ON;;;;;N;;;;;
+1F3BA;TRUMPET;So;0;ON;;;;;N;;;;;
+1F3BB;VIOLIN;So;0;ON;;;;;N;;;;;
+1F3BC;MUSICAL SCORE;So;0;ON;;;;;N;;;;;
+1F3BD;RUNNING SHIRT WITH SASH;So;0;ON;;;;;N;;;;;
+1F3BE;TENNIS RACQUET AND BALL;So;0;ON;;;;;N;;;;;
+1F3BF;SKI AND SKI BOOT;So;0;ON;;;;;N;;;;;
+1F3C0;BASKETBALL AND HOOP;So;0;ON;;;;;N;;;;;
+1F3C1;CHEQUERED FLAG;So;0;ON;;;;;N;;;;;
+1F3C2;SNOWBOARDER;So;0;ON;;;;;N;;;;;
+1F3C3;RUNNER;So;0;ON;;;;;N;;;;;
+1F3C4;SURFER;So;0;ON;;;;;N;;;;;
+1F3C5;SPORTS MEDAL;So;0;ON;;;;;N;;;;;
+1F3C6;TROPHY;So;0;ON;;;;;N;;;;;
+1F3C7;HORSE RACING;So;0;ON;;;;;N;;;;;
+1F3C8;AMERICAN FOOTBALL;So;0;ON;;;;;N;;;;;
+1F3C9;RUGBY FOOTBALL;So;0;ON;;;;;N;;;;;
+1F3CA;SWIMMER;So;0;ON;;;;;N;;;;;
+1F3CB;WEIGHT LIFTER;So;0;ON;;;;;N;;;;;
+1F3CC;GOLFER;So;0;ON;;;;;N;;;;;
+1F3CD;RACING MOTORCYCLE;So;0;ON;;;;;N;;;;;
+1F3CE;RACING CAR;So;0;ON;;;;;N;;;;;
+1F3CF;CRICKET BAT AND BALL;So;0;ON;;;;;N;;;;;
+1F3D0;VOLLEYBALL;So;0;ON;;;;;N;;;;;
+1F3D1;FIELD HOCKEY STICK AND BALL;So;0;ON;;;;;N;;;;;
+1F3D2;ICE HOCKEY STICK AND PUCK;So;0;ON;;;;;N;;;;;
+1F3D3;TABLE TENNIS PADDLE AND BALL;So;0;ON;;;;;N;;;;;
+1F3D4;SNOW CAPPED MOUNTAIN;So;0;ON;;;;;N;;;;;
+1F3D5;CAMPING;So;0;ON;;;;;N;;;;;
+1F3D6;BEACH WITH UMBRELLA;So;0;ON;;;;;N;;;;;
+1F3D7;BUILDING CONSTRUCTION;So;0;ON;;;;;N;;;;;
+1F3D8;HOUSE BUILDINGS;So;0;ON;;;;;N;;;;;
+1F3D9;CITYSCAPE;So;0;ON;;;;;N;;;;;
+1F3DA;DERELICT HOUSE BUILDING;So;0;ON;;;;;N;;;;;
+1F3DB;CLASSICAL BUILDING;So;0;ON;;;;;N;;;;;
+1F3DC;DESERT;So;0;ON;;;;;N;;;;;
+1F3DD;DESERT ISLAND;So;0;ON;;;;;N;;;;;
+1F3DE;NATIONAL PARK;So;0;ON;;;;;N;;;;;
+1F3DF;STADIUM;So;0;ON;;;;;N;;;;;
+1F3E0;HOUSE BUILDING;So;0;ON;;;;;N;;;;;
+1F3E1;HOUSE WITH GARDEN;So;0;ON;;;;;N;;;;;
+1F3E2;OFFICE BUILDING;So;0;ON;;;;;N;;;;;
+1F3E3;JAPANESE POST OFFICE;So;0;ON;;;;;N;;;;;
+1F3E4;EUROPEAN POST OFFICE;So;0;ON;;;;;N;;;;;
+1F3E5;HOSPITAL;So;0;ON;;;;;N;;;;;
+1F3E6;BANK;So;0;ON;;;;;N;;;;;
+1F3E7;AUTOMATED TELLER MACHINE;So;0;ON;;;;;N;;;;;
+1F3E8;HOTEL;So;0;ON;;;;;N;;;;;
+1F3E9;LOVE HOTEL;So;0;ON;;;;;N;;;;;
+1F3EA;CONVENIENCE STORE;So;0;ON;;;;;N;;;;;
+1F3EB;SCHOOL;So;0;ON;;;;;N;;;;;
+1F3EC;DEPARTMENT STORE;So;0;ON;;;;;N;;;;;
+1F3ED;FACTORY;So;0;ON;;;;;N;;;;;
+1F3EE;IZAKAYA LANTERN;So;0;ON;;;;;N;;;;;
+1F3EF;JAPANESE CASTLE;So;0;ON;;;;;N;;;;;
+1F3F0;EUROPEAN CASTLE;So;0;ON;;;;;N;;;;;
+1F3F1;WHITE PENNANT;So;0;ON;;;;;N;;;;;
+1F3F2;BLACK PENNANT;So;0;ON;;;;;N;;;;;
+1F3F3;WAVING WHITE FLAG;So;0;ON;;;;;N;;;;;
+1F3F4;WAVING BLACK FLAG;So;0;ON;;;;;N;;;;;
+1F3F5;ROSETTE;So;0;ON;;;;;N;;;;;
+1F3F6;BLACK ROSETTE;So;0;ON;;;;;N;;;;;
+1F3F7;LABEL;So;0;ON;;;;;N;;;;;
+1F3F8;BADMINTON RACQUET AND SHUTTLECOCK;So;0;ON;;;;;N;;;;;
+1F3F9;BOW AND ARROW;So;0;ON;;;;;N;;;;;
+1F3FA;AMPHORA;So;0;ON;;;;;N;;;;;
+1F3FB;EMOJI MODIFIER FITZPATRICK TYPE-1-2;Sk;0;ON;;;;;N;;;;;
+1F3FC;EMOJI MODIFIER FITZPATRICK TYPE-3;Sk;0;ON;;;;;N;;;;;
+1F3FD;EMOJI MODIFIER FITZPATRICK TYPE-4;Sk;0;ON;;;;;N;;;;;
+1F3FE;EMOJI MODIFIER FITZPATRICK TYPE-5;Sk;0;ON;;;;;N;;;;;
+1F3FF;EMOJI MODIFIER FITZPATRICK TYPE-6;Sk;0;ON;;;;;N;;;;;
+1F400;RAT;So;0;ON;;;;;N;;;;;
+1F401;MOUSE;So;0;ON;;;;;N;;;;;
+1F402;OX;So;0;ON;;;;;N;;;;;
+1F403;WATER BUFFALO;So;0;ON;;;;;N;;;;;
+1F404;COW;So;0;ON;;;;;N;;;;;
+1F405;TIGER;So;0;ON;;;;;N;;;;;
+1F406;LEOPARD;So;0;ON;;;;;N;;;;;
+1F407;RABBIT;So;0;ON;;;;;N;;;;;
+1F408;CAT;So;0;ON;;;;;N;;;;;
+1F409;DRAGON;So;0;ON;;;;;N;;;;;
+1F40A;CROCODILE;So;0;ON;;;;;N;;;;;
+1F40B;WHALE;So;0;ON;;;;;N;;;;;
+1F40C;SNAIL;So;0;ON;;;;;N;;;;;
+1F40D;SNAKE;So;0;ON;;;;;N;;;;;
+1F40E;HORSE;So;0;ON;;;;;N;;;;;
+1F40F;RAM;So;0;ON;;;;;N;;;;;
+1F410;GOAT;So;0;ON;;;;;N;;;;;
+1F411;SHEEP;So;0;ON;;;;;N;;;;;
+1F412;MONKEY;So;0;ON;;;;;N;;;;;
+1F413;ROOSTER;So;0;ON;;;;;N;;;;;
+1F414;CHICKEN;So;0;ON;;;;;N;;;;;
+1F415;DOG;So;0;ON;;;;;N;;;;;
+1F416;PIG;So;0;ON;;;;;N;;;;;
+1F417;BOAR;So;0;ON;;;;;N;;;;;
+1F418;ELEPHANT;So;0;ON;;;;;N;;;;;
+1F419;OCTOPUS;So;0;ON;;;;;N;;;;;
+1F41A;SPIRAL SHELL;So;0;ON;;;;;N;;;;;
+1F41B;BUG;So;0;ON;;;;;N;;;;;
+1F41C;ANT;So;0;ON;;;;;N;;;;;
+1F41D;HONEYBEE;So;0;ON;;;;;N;;;;;
+1F41E;LADY BEETLE;So;0;ON;;;;;N;;;;;
+1F41F;FISH;So;0;ON;;;;;N;;;;;
+1F420;TROPICAL FISH;So;0;ON;;;;;N;;;;;
+1F421;BLOWFISH;So;0;ON;;;;;N;;;;;
+1F422;TURTLE;So;0;ON;;;;;N;;;;;
+1F423;HATCHING CHICK;So;0;ON;;;;;N;;;;;
+1F424;BABY CHICK;So;0;ON;;;;;N;;;;;
+1F425;FRONT-FACING BABY CHICK;So;0;ON;;;;;N;;;;;
+1F426;BIRD;So;0;ON;;;;;N;;;;;
+1F427;PENGUIN;So;0;ON;;;;;N;;;;;
+1F428;KOALA;So;0;ON;;;;;N;;;;;
+1F429;POODLE;So;0;ON;;;;;N;;;;;
+1F42A;DROMEDARY CAMEL;So;0;ON;;;;;N;;;;;
+1F42B;BACTRIAN CAMEL;So;0;ON;;;;;N;;;;;
+1F42C;DOLPHIN;So;0;ON;;;;;N;;;;;
+1F42D;MOUSE FACE;So;0;ON;;;;;N;;;;;
+1F42E;COW FACE;So;0;ON;;;;;N;;;;;
+1F42F;TIGER FACE;So;0;ON;;;;;N;;;;;
+1F430;RABBIT FACE;So;0;ON;;;;;N;;;;;
+1F431;CAT FACE;So;0;ON;;;;;N;;;;;
+1F432;DRAGON FACE;So;0;ON;;;;;N;;;;;
+1F433;SPOUTING WHALE;So;0;ON;;;;;N;;;;;
+1F434;HORSE FACE;So;0;ON;;;;;N;;;;;
+1F435;MONKEY FACE;So;0;ON;;;;;N;;;;;
+1F436;DOG FACE;So;0;ON;;;;;N;;;;;
+1F437;PIG FACE;So;0;ON;;;;;N;;;;;
+1F438;FROG FACE;So;0;ON;;;;;N;;;;;
+1F439;HAMSTER FACE;So;0;ON;;;;;N;;;;;
+1F43A;WOLF FACE;So;0;ON;;;;;N;;;;;
+1F43B;BEAR FACE;So;0;ON;;;;;N;;;;;
+1F43C;PANDA FACE;So;0;ON;;;;;N;;;;;
+1F43D;PIG NOSE;So;0;ON;;;;;N;;;;;
+1F43E;PAW PRINTS;So;0;ON;;;;;N;;;;;
+1F43F;CHIPMUNK;So;0;ON;;;;;N;;;;;
+1F440;EYES;So;0;ON;;;;;N;;;;;
+1F441;EYE;So;0;ON;;;;;N;;;;;
+1F442;EAR;So;0;ON;;;;;N;;;;;
+1F443;NOSE;So;0;ON;;;;;N;;;;;
+1F444;MOUTH;So;0;ON;;;;;N;;;;;
+1F445;TONGUE;So;0;ON;;;;;N;;;;;
+1F446;WHITE UP POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;;
+1F447;WHITE DOWN POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;;
+1F448;WHITE LEFT POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;;
+1F449;WHITE RIGHT POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;;
+1F44A;FISTED HAND SIGN;So;0;ON;;;;;N;;;;;
+1F44B;WAVING HAND SIGN;So;0;ON;;;;;N;;;;;
+1F44C;OK HAND SIGN;So;0;ON;;;;;N;;;;;
+1F44D;THUMBS UP SIGN;So;0;ON;;;;;N;;;;;
+1F44E;THUMBS DOWN SIGN;So;0;ON;;;;;N;;;;;
+1F44F;CLAPPING HANDS SIGN;So;0;ON;;;;;N;;;;;
+1F450;OPEN HANDS SIGN;So;0;ON;;;;;N;;;;;
+1F451;CROWN;So;0;ON;;;;;N;;;;;
+1F452;WOMANS HAT;So;0;ON;;;;;N;;;;;
+1F453;EYEGLASSES;So;0;ON;;;;;N;;;;;
+1F454;NECKTIE;So;0;ON;;;;;N;;;;;
+1F455;T-SHIRT;So;0;ON;;;;;N;;;;;
+1F456;JEANS;So;0;ON;;;;;N;;;;;
+1F457;DRESS;So;0;ON;;;;;N;;;;;
+1F458;KIMONO;So;0;ON;;;;;N;;;;;
+1F459;BIKINI;So;0;ON;;;;;N;;;;;
+1F45A;WOMANS CLOTHES;So;0;ON;;;;;N;;;;;
+1F45B;PURSE;So;0;ON;;;;;N;;;;;
+1F45C;HANDBAG;So;0;ON;;;;;N;;;;;
+1F45D;POUCH;So;0;ON;;;;;N;;;;;
+1F45E;MANS SHOE;So;0;ON;;;;;N;;;;;
+1F45F;ATHLETIC SHOE;So;0;ON;;;;;N;;;;;
+1F460;HIGH-HEELED SHOE;So;0;ON;;;;;N;;;;;
+1F461;WOMANS SANDAL;So;0;ON;;;;;N;;;;;
+1F462;WOMANS BOOTS;So;0;ON;;;;;N;;;;;
+1F463;FOOTPRINTS;So;0;ON;;;;;N;;;;;
+1F464;BUST IN SILHOUETTE;So;0;ON;;;;;N;;;;;
+1F465;BUSTS IN SILHOUETTE;So;0;ON;;;;;N;;;;;
+1F466;BOY;So;0;ON;;;;;N;;;;;
+1F467;GIRL;So;0;ON;;;;;N;;;;;
+1F468;MAN;So;0;ON;;;;;N;;;;;
+1F469;WOMAN;So;0;ON;;;;;N;;;;;
+1F46A;FAMILY;So;0;ON;;;;;N;;;;;
+1F46B;MAN AND WOMAN HOLDING HANDS;So;0;ON;;;;;N;;;;;
+1F46C;TWO MEN HOLDING HANDS;So;0;ON;;;;;N;;;;;
+1F46D;TWO WOMEN HOLDING HANDS;So;0;ON;;;;;N;;;;;
+1F46E;POLICE OFFICER;So;0;ON;;;;;N;;;;;
+1F46F;WOMAN WITH BUNNY EARS;So;0;ON;;;;;N;;;;;
+1F470;BRIDE WITH VEIL;So;0;ON;;;;;N;;;;;
+1F471;PERSON WITH BLOND HAIR;So;0;ON;;;;;N;;;;;
+1F472;MAN WITH GUA PI MAO;So;0;ON;;;;;N;;;;;
+1F473;MAN WITH TURBAN;So;0;ON;;;;;N;;;;;
+1F474;OLDER MAN;So;0;ON;;;;;N;;;;;
+1F475;OLDER WOMAN;So;0;ON;;;;;N;;;;;
+1F476;BABY;So;0;ON;;;;;N;;;;;
+1F477;CONSTRUCTION WORKER;So;0;ON;;;;;N;;;;;
+1F478;PRINCESS;So;0;ON;;;;;N;;;;;
+1F479;JAPANESE OGRE;So;0;ON;;;;;N;;;;;
+1F47A;JAPANESE GOBLIN;So;0;ON;;;;;N;;;;;
+1F47B;GHOST;So;0;ON;;;;;N;;;;;
+1F47C;BABY ANGEL;So;0;ON;;;;;N;;;;;
+1F47D;EXTRATERRESTRIAL ALIEN;So;0;ON;;;;;N;;;;;
+1F47E;ALIEN MONSTER;So;0;ON;;;;;N;;;;;
+1F47F;IMP;So;0;ON;;;;;N;;;;;
+1F480;SKULL;So;0;ON;;;;;N;;;;;
+1F481;INFORMATION DESK PERSON;So;0;ON;;;;;N;;;;;
+1F482;GUARDSMAN;So;0;ON;;;;;N;;;;;
+1F483;DANCER;So;0;ON;;;;;N;;;;;
+1F484;LIPSTICK;So;0;ON;;;;;N;;;;;
+1F485;NAIL POLISH;So;0;ON;;;;;N;;;;;
+1F486;FACE MASSAGE;So;0;ON;;;;;N;;;;;
+1F487;HAIRCUT;So;0;ON;;;;;N;;;;;
+1F488;BARBER POLE;So;0;ON;;;;;N;;;;;
+1F489;SYRINGE;So;0;ON;;;;;N;;;;;
+1F48A;PILL;So;0;ON;;;;;N;;;;;
+1F48B;KISS MARK;So;0;ON;;;;;N;;;;;
+1F48C;LOVE LETTER;So;0;ON;;;;;N;;;;;
+1F48D;RING;So;0;ON;;;;;N;;;;;
+1F48E;GEM STONE;So;0;ON;;;;;N;;;;;
+1F48F;KISS;So;0;ON;;;;;N;;;;;
+1F490;BOUQUET;So;0;ON;;;;;N;;;;;
+1F491;COUPLE WITH HEART;So;0;ON;;;;;N;;;;;
+1F492;WEDDING;So;0;ON;;;;;N;;;;;
+1F493;BEATING HEART;So;0;ON;;;;;N;;;;;
+1F494;BROKEN HEART;So;0;ON;;;;;N;;;;;
+1F495;TWO HEARTS;So;0;ON;;;;;N;;;;;
+1F496;SPARKLING HEART;So;0;ON;;;;;N;;;;;
+1F497;GROWING HEART;So;0;ON;;;;;N;;;;;
+1F498;HEART WITH ARROW;So;0;ON;;;;;N;;;;;
+1F499;BLUE HEART;So;0;ON;;;;;N;;;;;
+1F49A;GREEN HEART;So;0;ON;;;;;N;;;;;
+1F49B;YELLOW HEART;So;0;ON;;;;;N;;;;;
+1F49C;PURPLE HEART;So;0;ON;;;;;N;;;;;
+1F49D;HEART WITH RIBBON;So;0;ON;;;;;N;;;;;
+1F49E;REVOLVING HEARTS;So;0;ON;;;;;N;;;;;
+1F49F;HEART DECORATION;So;0;ON;;;;;N;;;;;
+1F4A0;DIAMOND SHAPE WITH A DOT INSIDE;So;0;ON;;;;;N;;;;;
+1F4A1;ELECTRIC LIGHT BULB;So;0;ON;;;;;N;;;;;
+1F4A2;ANGER SYMBOL;So;0;ON;;;;;N;;;;;
+1F4A3;BOMB;So;0;ON;;;;;N;;;;;
+1F4A4;SLEEPING SYMBOL;So;0;ON;;;;;N;;;;;
+1F4A5;COLLISION SYMBOL;So;0;ON;;;;;N;;;;;
+1F4A6;SPLASHING SWEAT SYMBOL;So;0;ON;;;;;N;;;;;
+1F4A7;DROPLET;So;0;ON;;;;;N;;;;;
+1F4A8;DASH SYMBOL;So;0;ON;;;;;N;;;;;
+1F4A9;PILE OF POO;So;0;ON;;;;;N;;;;;
+1F4AA;FLEXED BICEPS;So;0;ON;;;;;N;;;;;
+1F4AB;DIZZY SYMBOL;So;0;ON;;;;;N;;;;;
+1F4AC;SPEECH BALLOON;So;0;ON;;;;;N;;;;;
+1F4AD;THOUGHT BALLOON;So;0;ON;;;;;N;;;;;
+1F4AE;WHITE FLOWER;So;0;ON;;;;;N;;;;;
+1F4AF;HUNDRED POINTS SYMBOL;So;0;ON;;;;;N;;;;;
+1F4B0;MONEY BAG;So;0;ON;;;;;N;;;;;
+1F4B1;CURRENCY EXCHANGE;So;0;ON;;;;;N;;;;;
+1F4B2;HEAVY DOLLAR SIGN;So;0;ON;;;;;N;;;;;
+1F4B3;CREDIT CARD;So;0;ON;;;;;N;;;;;
+1F4B4;BANKNOTE WITH YEN SIGN;So;0;ON;;;;;N;;;;;
+1F4B5;BANKNOTE WITH DOLLAR SIGN;So;0;ON;;;;;N;;;;;
+1F4B6;BANKNOTE WITH EURO SIGN;So;0;ON;;;;;N;;;;;
+1F4B7;BANKNOTE WITH POUND SIGN;So;0;ON;;;;;N;;;;;
+1F4B8;MONEY WITH WINGS;So;0;ON;;;;;N;;;;;
+1F4B9;CHART WITH UPWARDS TREND AND YEN SIGN;So;0;ON;;;;;N;;;;;
+1F4BA;SEAT;So;0;ON;;;;;N;;;;;
+1F4BB;PERSONAL COMPUTER;So;0;ON;;;;;N;;;;;
+1F4BC;BRIEFCASE;So;0;ON;;;;;N;;;;;
+1F4BD;MINIDISC;So;0;ON;;;;;N;;;;;
+1F4BE;FLOPPY DISK;So;0;ON;;;;;N;;;;;
+1F4BF;OPTICAL DISC;So;0;ON;;;;;N;;;;;
+1F4C0;DVD;So;0;ON;;;;;N;;;;;
+1F4C1;FILE FOLDER;So;0;ON;;;;;N;;;;;
+1F4C2;OPEN FILE FOLDER;So;0;ON;;;;;N;;;;;
+1F4C3;PAGE WITH CURL;So;0;ON;;;;;N;;;;;
+1F4C4;PAGE FACING UP;So;0;ON;;;;;N;;;;;
+1F4C5;CALENDAR;So;0;ON;;;;;N;;;;;
+1F4C6;TEAR-OFF CALENDAR;So;0;ON;;;;;N;;;;;
+1F4C7;CARD INDEX;So;0;ON;;;;;N;;;;;
+1F4C8;CHART WITH UPWARDS TREND;So;0;ON;;;;;N;;;;;
+1F4C9;CHART WITH DOWNWARDS TREND;So;0;ON;;;;;N;;;;;
+1F4CA;BAR CHART;So;0;ON;;;;;N;;;;;
+1F4CB;CLIPBOARD;So;0;ON;;;;;N;;;;;
+1F4CC;PUSHPIN;So;0;ON;;;;;N;;;;;
+1F4CD;ROUND PUSHPIN;So;0;ON;;;;;N;;;;;
+1F4CE;PAPERCLIP;So;0;ON;;;;;N;;;;;
+1F4CF;STRAIGHT RULER;So;0;ON;;;;;N;;;;;
+1F4D0;TRIANGULAR RULER;So;0;ON;;;;;N;;;;;
+1F4D1;BOOKMARK TABS;So;0;ON;;;;;N;;;;;
+1F4D2;LEDGER;So;0;ON;;;;;N;;;;;
+1F4D3;NOTEBOOK;So;0;ON;;;;;N;;;;;
+1F4D4;NOTEBOOK WITH DECORATIVE COVER;So;0;ON;;;;;N;;;;;
+1F4D5;CLOSED BOOK;So;0;ON;;;;;N;;;;;
+1F4D6;OPEN BOOK;So;0;ON;;;;;N;;;;;
+1F4D7;GREEN BOOK;So;0;ON;;;;;N;;;;;
+1F4D8;BLUE BOOK;So;0;ON;;;;;N;;;;;
+1F4D9;ORANGE BOOK;So;0;ON;;;;;N;;;;;
+1F4DA;BOOKS;So;0;ON;;;;;N;;;;;
+1F4DB;NAME BADGE;So;0;ON;;;;;N;;;;;
+1F4DC;SCROLL;So;0;ON;;;;;N;;;;;
+1F4DD;MEMO;So;0;ON;;;;;N;;;;;
+1F4DE;TELEPHONE RECEIVER;So;0;ON;;;;;N;;;;;
+1F4DF;PAGER;So;0;ON;;;;;N;;;;;
+1F4E0;FAX MACHINE;So;0;ON;;;;;N;;;;;
+1F4E1;SATELLITE ANTENNA;So;0;ON;;;;;N;;;;;
+1F4E2;PUBLIC ADDRESS LOUDSPEAKER;So;0;ON;;;;;N;;;;;
+1F4E3;CHEERING MEGAPHONE;So;0;ON;;;;;N;;;;;
+1F4E4;OUTBOX TRAY;So;0;ON;;;;;N;;;;;
+1F4E5;INBOX TRAY;So;0;ON;;;;;N;;;;;
+1F4E6;PACKAGE;So;0;ON;;;;;N;;;;;
+1F4E7;E-MAIL SYMBOL;So;0;ON;;;;;N;;;;;
+1F4E8;INCOMING ENVELOPE;So;0;ON;;;;;N;;;;;
+1F4E9;ENVELOPE WITH DOWNWARDS ARROW ABOVE;So;0;ON;;;;;N;;;;;
+1F4EA;CLOSED MAILBOX WITH LOWERED FLAG;So;0;ON;;;;;N;;;;;
+1F4EB;CLOSED MAILBOX WITH RAISED FLAG;So;0;ON;;;;;N;;;;;
+1F4EC;OPEN MAILBOX WITH RAISED FLAG;So;0;ON;;;;;N;;;;;
+1F4ED;OPEN MAILBOX WITH LOWERED FLAG;So;0;ON;;;;;N;;;;;
+1F4EE;POSTBOX;So;0;ON;;;;;N;;;;;
+1F4EF;POSTAL HORN;So;0;ON;;;;;N;;;;;
+1F4F0;NEWSPAPER;So;0;ON;;;;;N;;;;;
+1F4F1;MOBILE PHONE;So;0;ON;;;;;N;;;;;
+1F4F2;MOBILE PHONE WITH RIGHTWARDS ARROW AT LEFT;So;0;ON;;;;;N;;;;;
+1F4F3;VIBRATION MODE;So;0;ON;;;;;N;;;;;
+1F4F4;MOBILE PHONE OFF;So;0;ON;;;;;N;;;;;
+1F4F5;NO MOBILE PHONES;So;0;ON;;;;;N;;;;;
+1F4F6;ANTENNA WITH BARS;So;0;ON;;;;;N;;;;;
+1F4F7;CAMERA;So;0;ON;;;;;N;;;;;
+1F4F8;CAMERA WITH FLASH;So;0;ON;;;;;N;;;;;
+1F4F9;VIDEO CAMERA;So;0;ON;;;;;N;;;;;
+1F4FA;TELEVISION;So;0;ON;;;;;N;;;;;
+1F4FB;RADIO;So;0;ON;;;;;N;;;;;
+1F4FC;VIDEOCASSETTE;So;0;ON;;;;;N;;;;;
+1F4FD;FILM PROJECTOR;So;0;ON;;;;;N;;;;;
+1F4FE;PORTABLE STEREO;So;0;ON;;;;;N;;;;;
+1F4FF;PRAYER BEADS;So;0;ON;;;;;N;;;;;
+1F500;TWISTED RIGHTWARDS ARROWS;So;0;ON;;;;;N;;;;;
+1F501;CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS;So;0;ON;;;;;N;;;;;
+1F502;CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS WITH CIRCLED ONE OVERLAY;So;0;ON;;;;;N;;;;;
+1F503;CLOCKWISE DOWNWARDS AND UPWARDS OPEN CIRCLE ARROWS;So;0;ON;;;;;N;;;;;
+1F504;ANTICLOCKWISE DOWNWARDS AND UPWARDS OPEN CIRCLE ARROWS;So;0;ON;;;;;N;;;;;
+1F505;LOW BRIGHTNESS SYMBOL;So;0;ON;;;;;N;;;;;
+1F506;HIGH BRIGHTNESS SYMBOL;So;0;ON;;;;;N;;;;;
+1F507;SPEAKER WITH CANCELLATION STROKE;So;0;ON;;;;;N;;;;;
+1F508;SPEAKER;So;0;ON;;;;;N;;;;;
+1F509;SPEAKER WITH ONE SOUND WAVE;So;0;ON;;;;;N;;;;;
+1F50A;SPEAKER WITH THREE SOUND WAVES;So;0;ON;;;;;N;;;;;
+1F50B;BATTERY;So;0;ON;;;;;N;;;;;
+1F50C;ELECTRIC PLUG;So;0;ON;;;;;N;;;;;
+1F50D;LEFT-POINTING MAGNIFYING GLASS;So;0;ON;;;;;N;;;;;
+1F50E;RIGHT-POINTING MAGNIFYING GLASS;So;0;ON;;;;;N;;;;;
+1F50F;LOCK WITH INK PEN;So;0;ON;;;;;N;;;;;
+1F510;CLOSED LOCK WITH KEY;So;0;ON;;;;;N;;;;;
+1F511;KEY;So;0;ON;;;;;N;;;;;
+1F512;LOCK;So;0;ON;;;;;N;;;;;
+1F513;OPEN LOCK;So;0;ON;;;;;N;;;;;
+1F514;BELL;So;0;ON;;;;;N;;;;;
+1F515;BELL WITH CANCELLATION STROKE;So;0;ON;;;;;N;;;;;
+1F516;BOOKMARK;So;0;ON;;;;;N;;;;;
+1F517;LINK SYMBOL;So;0;ON;;;;;N;;;;;
+1F518;RADIO BUTTON;So;0;ON;;;;;N;;;;;
+1F519;BACK WITH LEFTWARDS ARROW ABOVE;So;0;ON;;;;;N;;;;;
+1F51A;END WITH LEFTWARDS ARROW ABOVE;So;0;ON;;;;;N;;;;;
+1F51B;ON WITH EXCLAMATION MARK WITH LEFT RIGHT ARROW ABOVE;So;0;ON;;;;;N;;;;;
+1F51C;SOON WITH RIGHTWARDS ARROW ABOVE;So;0;ON;;;;;N;;;;;
+1F51D;TOP WITH UPWARDS ARROW ABOVE;So;0;ON;;;;;N;;;;;
+1F51E;NO ONE UNDER EIGHTEEN SYMBOL;So;0;ON;;;;;N;;;;;
+1F51F;KEYCAP TEN;So;0;ON;;;;;N;;;;;
+1F520;INPUT SYMBOL FOR LATIN CAPITAL LETTERS;So;0;ON;;;;;N;;;;;
+1F521;INPUT SYMBOL FOR LATIN SMALL LETTERS;So;0;ON;;;;;N;;;;;
+1F522;INPUT SYMBOL FOR NUMBERS;So;0;ON;;;;;N;;;;;
+1F523;INPUT SYMBOL FOR SYMBOLS;So;0;ON;;;;;N;;;;;
+1F524;INPUT SYMBOL FOR LATIN LETTERS;So;0;ON;;;;;N;;;;;
+1F525;FIRE;So;0;ON;;;;;N;;;;;
+1F526;ELECTRIC TORCH;So;0;ON;;;;;N;;;;;
+1F527;WRENCH;So;0;ON;;;;;N;;;;;
+1F528;HAMMER;So;0;ON;;;;;N;;;;;
+1F529;NUT AND BOLT;So;0;ON;;;;;N;;;;;
+1F52A;HOCHO;So;0;ON;;;;;N;;;;;
+1F52B;PISTOL;So;0;ON;;;;;N;;;;;
+1F52C;MICROSCOPE;So;0;ON;;;;;N;;;;;
+1F52D;TELESCOPE;So;0;ON;;;;;N;;;;;
+1F52E;CRYSTAL BALL;So;0;ON;;;;;N;;;;;
+1F52F;SIX POINTED STAR WITH MIDDLE DOT;So;0;ON;;;;;N;;;;;
+1F530;JAPANESE SYMBOL FOR BEGINNER;So;0;ON;;;;;N;;;;;
+1F531;TRIDENT EMBLEM;So;0;ON;;;;;N;;;;;
+1F532;BLACK SQUARE BUTTON;So;0;ON;;;;;N;;;;;
+1F533;WHITE SQUARE BUTTON;So;0;ON;;;;;N;;;;;
+1F534;LARGE RED CIRCLE;So;0;ON;;;;;N;;;;;
+1F535;LARGE BLUE CIRCLE;So;0;ON;;;;;N;;;;;
+1F536;LARGE ORANGE DIAMOND;So;0;ON;;;;;N;;;;;
+1F537;LARGE BLUE DIAMOND;So;0;ON;;;;;N;;;;;
+1F538;SMALL ORANGE DIAMOND;So;0;ON;;;;;N;;;;;
+1F539;SMALL BLUE DIAMOND;So;0;ON;;;;;N;;;;;
+1F53A;UP-POINTING RED TRIANGLE;So;0;ON;;;;;N;;;;;
+1F53B;DOWN-POINTING RED TRIANGLE;So;0;ON;;;;;N;;;;;
+1F53C;UP-POINTING SMALL RED TRIANGLE;So;0;ON;;;;;N;;;;;
+1F53D;DOWN-POINTING SMALL RED TRIANGLE;So;0;ON;;;;;N;;;;;
+1F53E;LOWER RIGHT SHADOWED WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+1F53F;UPPER RIGHT SHADOWED WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+1F540;CIRCLED CROSS POMMEE;So;0;ON;;;;;N;;;;;
+1F541;CROSS POMMEE WITH HALF-CIRCLE BELOW;So;0;ON;;;;;N;;;;;
+1F542;CROSS POMMEE;So;0;ON;;;;;N;;;;;
+1F543;NOTCHED LEFT SEMICIRCLE WITH THREE DOTS;So;0;ON;;;;;N;;;;;
+1F544;NOTCHED RIGHT SEMICIRCLE WITH THREE DOTS;So;0;ON;;;;;N;;;;;
+1F545;SYMBOL FOR MARKS CHAPTER;So;0;ON;;;;;N;;;;;
+1F546;WHITE LATIN CROSS;So;0;ON;;;;;N;;;;;
+1F547;HEAVY LATIN CROSS;So;0;ON;;;;;N;;;;;
+1F548;CELTIC CROSS;So;0;ON;;;;;N;;;;;
+1F549;OM SYMBOL;So;0;ON;;;;;N;;;;;
+1F54A;DOVE OF PEACE;So;0;ON;;;;;N;;;;;
+1F54B;KAABA;So;0;ON;;;;;N;;;;;
+1F54C;MOSQUE;So;0;ON;;;;;N;;;;;
+1F54D;SYNAGOGUE;So;0;ON;;;;;N;;;;;
+1F54E;MENORAH WITH NINE BRANCHES;So;0;ON;;;;;N;;;;;
+1F54F;BOWL OF HYGIEIA;So;0;ON;;;;;N;;;;;
+1F550;CLOCK FACE ONE OCLOCK;So;0;ON;;;;;N;;;;;
+1F551;CLOCK FACE TWO OCLOCK;So;0;ON;;;;;N;;;;;
+1F552;CLOCK FACE THREE OCLOCK;So;0;ON;;;;;N;;;;;
+1F553;CLOCK FACE FOUR OCLOCK;So;0;ON;;;;;N;;;;;
+1F554;CLOCK FACE FIVE OCLOCK;So;0;ON;;;;;N;;;;;
+1F555;CLOCK FACE SIX OCLOCK;So;0;ON;;;;;N;;;;;
+1F556;CLOCK FACE SEVEN OCLOCK;So;0;ON;;;;;N;;;;;
+1F557;CLOCK FACE EIGHT OCLOCK;So;0;ON;;;;;N;;;;;
+1F558;CLOCK FACE NINE OCLOCK;So;0;ON;;;;;N;;;;;
+1F559;CLOCK FACE TEN OCLOCK;So;0;ON;;;;;N;;;;;
+1F55A;CLOCK FACE ELEVEN OCLOCK;So;0;ON;;;;;N;;;;;
+1F55B;CLOCK FACE TWELVE OCLOCK;So;0;ON;;;;;N;;;;;
+1F55C;CLOCK FACE ONE-THIRTY;So;0;ON;;;;;N;;;;;
+1F55D;CLOCK FACE TWO-THIRTY;So;0;ON;;;;;N;;;;;
+1F55E;CLOCK FACE THREE-THIRTY;So;0;ON;;;;;N;;;;;
+1F55F;CLOCK FACE FOUR-THIRTY;So;0;ON;;;;;N;;;;;
+1F560;CLOCK FACE FIVE-THIRTY;So;0;ON;;;;;N;;;;;
+1F561;CLOCK FACE SIX-THIRTY;So;0;ON;;;;;N;;;;;
+1F562;CLOCK FACE SEVEN-THIRTY;So;0;ON;;;;;N;;;;;
+1F563;CLOCK FACE EIGHT-THIRTY;So;0;ON;;;;;N;;;;;
+1F564;CLOCK FACE NINE-THIRTY;So;0;ON;;;;;N;;;;;
+1F565;CLOCK FACE TEN-THIRTY;So;0;ON;;;;;N;;;;;
+1F566;CLOCK FACE ELEVEN-THIRTY;So;0;ON;;;;;N;;;;;
+1F567;CLOCK FACE TWELVE-THIRTY;So;0;ON;;;;;N;;;;;
+1F568;RIGHT SPEAKER;So;0;ON;;;;;N;;;;;
+1F569;RIGHT SPEAKER WITH ONE SOUND WAVE;So;0;ON;;;;;N;;;;;
+1F56A;RIGHT SPEAKER WITH THREE SOUND WAVES;So;0;ON;;;;;N;;;;;
+1F56B;BULLHORN;So;0;ON;;;;;N;;;;;
+1F56C;BULLHORN WITH SOUND WAVES;So;0;ON;;;;;N;;;;;
+1F56D;RINGING BELL;So;0;ON;;;;;N;;;;;
+1F56E;BOOK;So;0;ON;;;;;N;;;;;
+1F56F;CANDLE;So;0;ON;;;;;N;;;;;
+1F570;MANTELPIECE CLOCK;So;0;ON;;;;;N;;;;;
+1F571;BLACK SKULL AND CROSSBONES;So;0;ON;;;;;N;;;;;
+1F572;NO PIRACY;So;0;ON;;;;;N;;;;;
+1F573;HOLE;So;0;ON;;;;;N;;;;;
+1F574;MAN IN BUSINESS SUIT LEVITATING;So;0;ON;;;;;N;;;;;
+1F575;SLEUTH OR SPY;So;0;ON;;;;;N;;;;;
+1F576;DARK SUNGLASSES;So;0;ON;;;;;N;;;;;
+1F577;SPIDER;So;0;ON;;;;;N;;;;;
+1F578;SPIDER WEB;So;0;ON;;;;;N;;;;;
+1F579;JOYSTICK;So;0;ON;;;;;N;;;;;
+1F57A;MAN DANCING;So;0;ON;;;;;N;;;;;
+1F57B;LEFT HAND TELEPHONE RECEIVER;So;0;ON;;;;;N;;;;;
+1F57C;TELEPHONE RECEIVER WITH PAGE;So;0;ON;;;;;N;;;;;
+1F57D;RIGHT HAND TELEPHONE RECEIVER;So;0;ON;;;;;N;;;;;
+1F57E;WHITE TOUCHTONE TELEPHONE;So;0;ON;;;;;N;;;;;
+1F57F;BLACK TOUCHTONE TELEPHONE;So;0;ON;;;;;N;;;;;
+1F580;TELEPHONE ON TOP OF MODEM;So;0;ON;;;;;N;;;;;
+1F581;CLAMSHELL MOBILE PHONE;So;0;ON;;;;;N;;;;;
+1F582;BACK OF ENVELOPE;So;0;ON;;;;;N;;;;;
+1F583;STAMPED ENVELOPE;So;0;ON;;;;;N;;;;;
+1F584;ENVELOPE WITH LIGHTNING;So;0;ON;;;;;N;;;;;
+1F585;FLYING ENVELOPE;So;0;ON;;;;;N;;;;;
+1F586;PEN OVER STAMPED ENVELOPE;So;0;ON;;;;;N;;;;;
+1F587;LINKED PAPERCLIPS;So;0;ON;;;;;N;;;;;
+1F588;BLACK PUSHPIN;So;0;ON;;;;;N;;;;;
+1F589;LOWER LEFT PENCIL;So;0;ON;;;;;N;;;;;
+1F58A;LOWER LEFT BALLPOINT PEN;So;0;ON;;;;;N;;;;;
+1F58B;LOWER LEFT FOUNTAIN PEN;So;0;ON;;;;;N;;;;;
+1F58C;LOWER LEFT PAINTBRUSH;So;0;ON;;;;;N;;;;;
+1F58D;LOWER LEFT CRAYON;So;0;ON;;;;;N;;;;;
+1F58E;LEFT WRITING HAND;So;0;ON;;;;;N;;;;;
+1F58F;TURNED OK HAND SIGN;So;0;ON;;;;;N;;;;;
+1F590;RAISED HAND WITH FINGERS SPLAYED;So;0;ON;;;;;N;;;;;
+1F591;REVERSED RAISED HAND WITH FINGERS SPLAYED;So;0;ON;;;;;N;;;;;
+1F592;REVERSED THUMBS UP SIGN;So;0;ON;;;;;N;;;;;
+1F593;REVERSED THUMBS DOWN SIGN;So;0;ON;;;;;N;;;;;
+1F594;REVERSED VICTORY HAND;So;0;ON;;;;;N;;;;;
+1F595;REVERSED HAND WITH MIDDLE FINGER EXTENDED;So;0;ON;;;;;N;;;;;
+1F596;RAISED HAND WITH PART BETWEEN MIDDLE AND RING FINGERS;So;0;ON;;;;;N;;;;;
+1F597;WHITE DOWN POINTING LEFT HAND INDEX;So;0;ON;;;;;N;;;;;
+1F598;SIDEWAYS WHITE LEFT POINTING INDEX;So;0;ON;;;;;N;;;;;
+1F599;SIDEWAYS WHITE RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;;
+1F59A;SIDEWAYS BLACK LEFT POINTING INDEX;So;0;ON;;;;;N;;;;;
+1F59B;SIDEWAYS BLACK RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;;
+1F59C;BLACK LEFT POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;;
+1F59D;BLACK RIGHT POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;;
+1F59E;SIDEWAYS WHITE UP POINTING INDEX;So;0;ON;;;;;N;;;;;
+1F59F;SIDEWAYS WHITE DOWN POINTING INDEX;So;0;ON;;;;;N;;;;;
+1F5A0;SIDEWAYS BLACK UP POINTING INDEX;So;0;ON;;;;;N;;;;;
+1F5A1;SIDEWAYS BLACK DOWN POINTING INDEX;So;0;ON;;;;;N;;;;;
+1F5A2;BLACK UP POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;;
+1F5A3;BLACK DOWN POINTING BACKHAND INDEX;So;0;ON;;;;;N;;;;;
+1F5A4;BLACK HEART;So;0;ON;;;;;N;;;;;
+1F5A5;DESKTOP COMPUTER;So;0;ON;;;;;N;;;;;
+1F5A6;KEYBOARD AND MOUSE;So;0;ON;;;;;N;;;;;
+1F5A7;THREE NETWORKED COMPUTERS;So;0;ON;;;;;N;;;;;
+1F5A8;PRINTER;So;0;ON;;;;;N;;;;;
+1F5A9;POCKET CALCULATOR;So;0;ON;;;;;N;;;;;
+1F5AA;BLACK HARD SHELL FLOPPY DISK;So;0;ON;;;;;N;;;;;
+1F5AB;WHITE HARD SHELL FLOPPY DISK;So;0;ON;;;;;N;;;;;
+1F5AC;SOFT SHELL FLOPPY DISK;So;0;ON;;;;;N;;;;;
+1F5AD;TAPE CARTRIDGE;So;0;ON;;;;;N;;;;;
+1F5AE;WIRED KEYBOARD;So;0;ON;;;;;N;;;;;
+1F5AF;ONE BUTTON MOUSE;So;0;ON;;;;;N;;;;;
+1F5B0;TWO BUTTON MOUSE;So;0;ON;;;;;N;;;;;
+1F5B1;THREE BUTTON MOUSE;So;0;ON;;;;;N;;;;;
+1F5B2;TRACKBALL;So;0;ON;;;;;N;;;;;
+1F5B3;OLD PERSONAL COMPUTER;So;0;ON;;;;;N;;;;;
+1F5B4;HARD DISK;So;0;ON;;;;;N;;;;;
+1F5B5;SCREEN;So;0;ON;;;;;N;;;;;
+1F5B6;PRINTER ICON;So;0;ON;;;;;N;;;;;
+1F5B7;FAX ICON;So;0;ON;;;;;N;;;;;
+1F5B8;OPTICAL DISC ICON;So;0;ON;;;;;N;;;;;
+1F5B9;DOCUMENT WITH TEXT;So;0;ON;;;;;N;;;;;
+1F5BA;DOCUMENT WITH TEXT AND PICTURE;So;0;ON;;;;;N;;;;;
+1F5BB;DOCUMENT WITH PICTURE;So;0;ON;;;;;N;;;;;
+1F5BC;FRAME WITH PICTURE;So;0;ON;;;;;N;;;;;
+1F5BD;FRAME WITH TILES;So;0;ON;;;;;N;;;;;
+1F5BE;FRAME WITH AN X;So;0;ON;;;;;N;;;;;
+1F5BF;BLACK FOLDER;So;0;ON;;;;;N;;;;;
+1F5C0;FOLDER;So;0;ON;;;;;N;;;;;
+1F5C1;OPEN FOLDER;So;0;ON;;;;;N;;;;;
+1F5C2;CARD INDEX DIVIDERS;So;0;ON;;;;;N;;;;;
+1F5C3;CARD FILE BOX;So;0;ON;;;;;N;;;;;
+1F5C4;FILE CABINET;So;0;ON;;;;;N;;;;;
+1F5C5;EMPTY NOTE;So;0;ON;;;;;N;;;;;
+1F5C6;EMPTY NOTE PAGE;So;0;ON;;;;;N;;;;;
+1F5C7;EMPTY NOTE PAD;So;0;ON;;;;;N;;;;;
+1F5C8;NOTE;So;0;ON;;;;;N;;;;;
+1F5C9;NOTE PAGE;So;0;ON;;;;;N;;;;;
+1F5CA;NOTE PAD;So;0;ON;;;;;N;;;;;
+1F5CB;EMPTY DOCUMENT;So;0;ON;;;;;N;;;;;
+1F5CC;EMPTY PAGE;So;0;ON;;;;;N;;;;;
+1F5CD;EMPTY PAGES;So;0;ON;;;;;N;;;;;
+1F5CE;DOCUMENT;So;0;ON;;;;;N;;;;;
+1F5CF;PAGE;So;0;ON;;;;;N;;;;;
+1F5D0;PAGES;So;0;ON;;;;;N;;;;;
+1F5D1;WASTEBASKET;So;0;ON;;;;;N;;;;;
+1F5D2;SPIRAL NOTE PAD;So;0;ON;;;;;N;;;;;
+1F5D3;SPIRAL CALENDAR PAD;So;0;ON;;;;;N;;;;;
+1F5D4;DESKTOP WINDOW;So;0;ON;;;;;N;;;;;
+1F5D5;MINIMIZE;So;0;ON;;;;;N;;;;;
+1F5D6;MAXIMIZE;So;0;ON;;;;;N;;;;;
+1F5D7;OVERLAP;So;0;ON;;;;;N;;;;;
+1F5D8;CLOCKWISE RIGHT AND LEFT SEMICIRCLE ARROWS;So;0;ON;;;;;N;;;;;
+1F5D9;CANCELLATION X;So;0;ON;;;;;N;;;;;
+1F5DA;INCREASE FONT SIZE SYMBOL;So;0;ON;;;;;N;;;;;
+1F5DB;DECREASE FONT SIZE SYMBOL;So;0;ON;;;;;N;;;;;
+1F5DC;COMPRESSION;So;0;ON;;;;;N;;;;;
+1F5DD;OLD KEY;So;0;ON;;;;;N;;;;;
+1F5DE;ROLLED-UP NEWSPAPER;So;0;ON;;;;;N;;;;;
+1F5DF;PAGE WITH CIRCLED TEXT;So;0;ON;;;;;N;;;;;
+1F5E0;STOCK CHART;So;0;ON;;;;;N;;;;;
+1F5E1;DAGGER KNIFE;So;0;ON;;;;;N;;;;;
+1F5E2;LIPS;So;0;ON;;;;;N;;;;;
+1F5E3;SPEAKING HEAD IN SILHOUETTE;So;0;ON;;;;;N;;;;;
+1F5E4;THREE RAYS ABOVE;So;0;ON;;;;;N;;;;;
+1F5E5;THREE RAYS BELOW;So;0;ON;;;;;N;;;;;
+1F5E6;THREE RAYS LEFT;So;0;ON;;;;;N;;;;;
+1F5E7;THREE RAYS RIGHT;So;0;ON;;;;;N;;;;;
+1F5E8;LEFT SPEECH BUBBLE;So;0;ON;;;;;N;;;;;
+1F5E9;RIGHT SPEECH BUBBLE;So;0;ON;;;;;N;;;;;
+1F5EA;TWO SPEECH BUBBLES;So;0;ON;;;;;N;;;;;
+1F5EB;THREE SPEECH BUBBLES;So;0;ON;;;;;N;;;;;
+1F5EC;LEFT THOUGHT BUBBLE;So;0;ON;;;;;N;;;;;
+1F5ED;RIGHT THOUGHT BUBBLE;So;0;ON;;;;;N;;;;;
+1F5EE;LEFT ANGER BUBBLE;So;0;ON;;;;;N;;;;;
+1F5EF;RIGHT ANGER BUBBLE;So;0;ON;;;;;N;;;;;
+1F5F0;MOOD BUBBLE;So;0;ON;;;;;N;;;;;
+1F5F1;LIGHTNING MOOD BUBBLE;So;0;ON;;;;;N;;;;;
+1F5F2;LIGHTNING MOOD;So;0;ON;;;;;N;;;;;
+1F5F3;BALLOT BOX WITH BALLOT;So;0;ON;;;;;N;;;;;
+1F5F4;BALLOT SCRIPT X;So;0;ON;;;;;N;;;;;
+1F5F5;BALLOT BOX WITH SCRIPT X;So;0;ON;;;;;N;;;;;
+1F5F6;BALLOT BOLD SCRIPT X;So;0;ON;;;;;N;;;;;
+1F5F7;BALLOT BOX WITH BOLD SCRIPT X;So;0;ON;;;;;N;;;;;
+1F5F8;LIGHT CHECK MARK;So;0;ON;;;;;N;;;;;
+1F5F9;BALLOT BOX WITH BOLD CHECK;So;0;ON;;;;;N;;;;;
+1F5FA;WORLD MAP;So;0;ON;;;;;N;;;;;
+1F5FB;MOUNT FUJI;So;0;ON;;;;;N;;;;;
+1F5FC;TOKYO TOWER;So;0;ON;;;;;N;;;;;
+1F5FD;STATUE OF LIBERTY;So;0;ON;;;;;N;;;;;
+1F5FE;SILHOUETTE OF JAPAN;So;0;ON;;;;;N;;;;;
+1F5FF;MOYAI;So;0;ON;;;;;N;;;;;
+1F600;GRINNING FACE;So;0;ON;;;;;N;;;;;
+1F601;GRINNING FACE WITH SMILING EYES;So;0;ON;;;;;N;;;;;
+1F602;FACE WITH TEARS OF JOY;So;0;ON;;;;;N;;;;;
+1F603;SMILING FACE WITH OPEN MOUTH;So;0;ON;;;;;N;;;;;
+1F604;SMILING FACE WITH OPEN MOUTH AND SMILING EYES;So;0;ON;;;;;N;;;;;
+1F605;SMILING FACE WITH OPEN MOUTH AND COLD SWEAT;So;0;ON;;;;;N;;;;;
+1F606;SMILING FACE WITH OPEN MOUTH AND TIGHTLY-CLOSED EYES;So;0;ON;;;;;N;;;;;
+1F607;SMILING FACE WITH HALO;So;0;ON;;;;;N;;;;;
+1F608;SMILING FACE WITH HORNS;So;0;ON;;;;;N;;;;;
+1F609;WINKING FACE;So;0;ON;;;;;N;;;;;
+1F60A;SMILING FACE WITH SMILING EYES;So;0;ON;;;;;N;;;;;
+1F60B;FACE SAVOURING DELICIOUS FOOD;So;0;ON;;;;;N;;;;;
+1F60C;RELIEVED FACE;So;0;ON;;;;;N;;;;;
+1F60D;SMILING FACE WITH HEART-SHAPED EYES;So;0;ON;;;;;N;;;;;
+1F60E;SMILING FACE WITH SUNGLASSES;So;0;ON;;;;;N;;;;;
+1F60F;SMIRKING FACE;So;0;ON;;;;;N;;;;;
+1F610;NEUTRAL FACE;So;0;ON;;;;;N;;;;;
+1F611;EXPRESSIONLESS FACE;So;0;ON;;;;;N;;;;;
+1F612;UNAMUSED FACE;So;0;ON;;;;;N;;;;;
+1F613;FACE WITH COLD SWEAT;So;0;ON;;;;;N;;;;;
+1F614;PENSIVE FACE;So;0;ON;;;;;N;;;;;
+1F615;CONFUSED FACE;So;0;ON;;;;;N;;;;;
+1F616;CONFOUNDED FACE;So;0;ON;;;;;N;;;;;
+1F617;KISSING FACE;So;0;ON;;;;;N;;;;;
+1F618;FACE THROWING A KISS;So;0;ON;;;;;N;;;;;
+1F619;KISSING FACE WITH SMILING EYES;So;0;ON;;;;;N;;;;;
+1F61A;KISSING FACE WITH CLOSED EYES;So;0;ON;;;;;N;;;;;
+1F61B;FACE WITH STUCK-OUT TONGUE;So;0;ON;;;;;N;;;;;
+1F61C;FACE WITH STUCK-OUT TONGUE AND WINKING EYE;So;0;ON;;;;;N;;;;;
+1F61D;FACE WITH STUCK-OUT TONGUE AND TIGHTLY-CLOSED EYES;So;0;ON;;;;;N;;;;;
+1F61E;DISAPPOINTED FACE;So;0;ON;;;;;N;;;;;
+1F61F;WORRIED FACE;So;0;ON;;;;;N;;;;;
+1F620;ANGRY FACE;So;0;ON;;;;;N;;;;;
+1F621;POUTING FACE;So;0;ON;;;;;N;;;;;
+1F622;CRYING FACE;So;0;ON;;;;;N;;;;;
+1F623;PERSEVERING FACE;So;0;ON;;;;;N;;;;;
+1F624;FACE WITH LOOK OF TRIUMPH;So;0;ON;;;;;N;;;;;
+1F625;DISAPPOINTED BUT RELIEVED FACE;So;0;ON;;;;;N;;;;;
+1F626;FROWNING FACE WITH OPEN MOUTH;So;0;ON;;;;;N;;;;;
+1F627;ANGUISHED FACE;So;0;ON;;;;;N;;;;;
+1F628;FEARFUL FACE;So;0;ON;;;;;N;;;;;
+1F629;WEARY FACE;So;0;ON;;;;;N;;;;;
+1F62A;SLEEPY FACE;So;0;ON;;;;;N;;;;;
+1F62B;TIRED FACE;So;0;ON;;;;;N;;;;;
+1F62C;GRIMACING FACE;So;0;ON;;;;;N;;;;;
+1F62D;LOUDLY CRYING FACE;So;0;ON;;;;;N;;;;;
+1F62E;FACE WITH OPEN MOUTH;So;0;ON;;;;;N;;;;;
+1F62F;HUSHED FACE;So;0;ON;;;;;N;;;;;
+1F630;FACE WITH OPEN MOUTH AND COLD SWEAT;So;0;ON;;;;;N;;;;;
+1F631;FACE SCREAMING IN FEAR;So;0;ON;;;;;N;;;;;
+1F632;ASTONISHED FACE;So;0;ON;;;;;N;;;;;
+1F633;FLUSHED FACE;So;0;ON;;;;;N;;;;;
+1F634;SLEEPING FACE;So;0;ON;;;;;N;;;;;
+1F635;DIZZY FACE;So;0;ON;;;;;N;;;;;
+1F636;FACE WITHOUT MOUTH;So;0;ON;;;;;N;;;;;
+1F637;FACE WITH MEDICAL MASK;So;0;ON;;;;;N;;;;;
+1F638;GRINNING CAT FACE WITH SMILING EYES;So;0;ON;;;;;N;;;;;
+1F639;CAT FACE WITH TEARS OF JOY;So;0;ON;;;;;N;;;;;
+1F63A;SMILING CAT FACE WITH OPEN MOUTH;So;0;ON;;;;;N;;;;;
+1F63B;SMILING CAT FACE WITH HEART-SHAPED EYES;So;0;ON;;;;;N;;;;;
+1F63C;CAT FACE WITH WRY SMILE;So;0;ON;;;;;N;;;;;
+1F63D;KISSING CAT FACE WITH CLOSED EYES;So;0;ON;;;;;N;;;;;
+1F63E;POUTING CAT FACE;So;0;ON;;;;;N;;;;;
+1F63F;CRYING CAT FACE;So;0;ON;;;;;N;;;;;
+1F640;WEARY CAT FACE;So;0;ON;;;;;N;;;;;
+1F641;SLIGHTLY FROWNING FACE;So;0;ON;;;;;N;;;;;
+1F642;SLIGHTLY SMILING FACE;So;0;ON;;;;;N;;;;;
+1F643;UPSIDE-DOWN FACE;So;0;ON;;;;;N;;;;;
+1F644;FACE WITH ROLLING EYES;So;0;ON;;;;;N;;;;;
+1F645;FACE WITH NO GOOD GESTURE;So;0;ON;;;;;N;;;;;
+1F646;FACE WITH OK GESTURE;So;0;ON;;;;;N;;;;;
+1F647;PERSON BOWING DEEPLY;So;0;ON;;;;;N;;;;;
+1F648;SEE-NO-EVIL MONKEY;So;0;ON;;;;;N;;;;;
+1F649;HEAR-NO-EVIL MONKEY;So;0;ON;;;;;N;;;;;
+1F64A;SPEAK-NO-EVIL MONKEY;So;0;ON;;;;;N;;;;;
+1F64B;HAPPY PERSON RAISING ONE HAND;So;0;ON;;;;;N;;;;;
+1F64C;PERSON RAISING BOTH HANDS IN CELEBRATION;So;0;ON;;;;;N;;;;;
+1F64D;PERSON FROWNING;So;0;ON;;;;;N;;;;;
+1F64E;PERSON WITH POUTING FACE;So;0;ON;;;;;N;;;;;
+1F64F;PERSON WITH FOLDED HANDS;So;0;ON;;;;;N;;;;;
+1F650;NORTH WEST POINTING LEAF;So;0;ON;;;;;N;;;;;
+1F651;SOUTH WEST POINTING LEAF;So;0;ON;;;;;N;;;;;
+1F652;NORTH EAST POINTING LEAF;So;0;ON;;;;;N;;;;;
+1F653;SOUTH EAST POINTING LEAF;So;0;ON;;;;;N;;;;;
+1F654;TURNED NORTH WEST POINTING LEAF;So;0;ON;;;;;N;;;;;
+1F655;TURNED SOUTH WEST POINTING LEAF;So;0;ON;;;;;N;;;;;
+1F656;TURNED NORTH EAST POINTING LEAF;So;0;ON;;;;;N;;;;;
+1F657;TURNED SOUTH EAST POINTING LEAF;So;0;ON;;;;;N;;;;;
+1F658;NORTH WEST POINTING VINE LEAF;So;0;ON;;;;;N;;;;;
+1F659;SOUTH WEST POINTING VINE LEAF;So;0;ON;;;;;N;;;;;
+1F65A;NORTH EAST POINTING VINE LEAF;So;0;ON;;;;;N;;;;;
+1F65B;SOUTH EAST POINTING VINE LEAF;So;0;ON;;;;;N;;;;;
+1F65C;HEAVY NORTH WEST POINTING VINE LEAF;So;0;ON;;;;;N;;;;;
+1F65D;HEAVY SOUTH WEST POINTING VINE LEAF;So;0;ON;;;;;N;;;;;
+1F65E;HEAVY NORTH EAST POINTING VINE LEAF;So;0;ON;;;;;N;;;;;
+1F65F;HEAVY SOUTH EAST POINTING VINE LEAF;So;0;ON;;;;;N;;;;;
+1F660;NORTH WEST POINTING BUD;So;0;ON;;;;;N;;;;;
+1F661;SOUTH WEST POINTING BUD;So;0;ON;;;;;N;;;;;
+1F662;NORTH EAST POINTING BUD;So;0;ON;;;;;N;;;;;
+1F663;SOUTH EAST POINTING BUD;So;0;ON;;;;;N;;;;;
+1F664;HEAVY NORTH WEST POINTING BUD;So;0;ON;;;;;N;;;;;
+1F665;HEAVY SOUTH WEST POINTING BUD;So;0;ON;;;;;N;;;;;
+1F666;HEAVY NORTH EAST POINTING BUD;So;0;ON;;;;;N;;;;;
+1F667;HEAVY SOUTH EAST POINTING BUD;So;0;ON;;;;;N;;;;;
+1F668;HOLLOW QUILT SQUARE ORNAMENT;So;0;ON;;;;;N;;;;;
+1F669;HOLLOW QUILT SQUARE ORNAMENT IN BLACK SQUARE;So;0;ON;;;;;N;;;;;
+1F66A;SOLID QUILT SQUARE ORNAMENT;So;0;ON;;;;;N;;;;;
+1F66B;SOLID QUILT SQUARE ORNAMENT IN BLACK SQUARE;So;0;ON;;;;;N;;;;;
+1F66C;LEFTWARDS ROCKET;So;0;ON;;;;;N;;;;;
+1F66D;UPWARDS ROCKET;So;0;ON;;;;;N;;;;;
+1F66E;RIGHTWARDS ROCKET;So;0;ON;;;;;N;;;;;
+1F66F;DOWNWARDS ROCKET;So;0;ON;;;;;N;;;;;
+1F670;SCRIPT LIGATURE ET ORNAMENT;So;0;ON;;;;;N;;;;;
+1F671;HEAVY SCRIPT LIGATURE ET ORNAMENT;So;0;ON;;;;;N;;;;;
+1F672;LIGATURE OPEN ET ORNAMENT;So;0;ON;;;;;N;;;;;
+1F673;HEAVY LIGATURE OPEN ET ORNAMENT;So;0;ON;;;;;N;;;;;
+1F674;HEAVY AMPERSAND ORNAMENT;So;0;ON;;;;;N;;;;;
+1F675;SWASH AMPERSAND ORNAMENT;So;0;ON;;;;;N;;;;;
+1F676;SANS-SERIF HEAVY DOUBLE TURNED COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+1F677;SANS-SERIF HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+1F678;SANS-SERIF HEAVY LOW DOUBLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+1F679;HEAVY INTERROBANG ORNAMENT;So;0;ON;;;;;N;;;;;
+1F67A;SANS-SERIF INTERROBANG ORNAMENT;So;0;ON;;;;;N;;;;;
+1F67B;HEAVY SANS-SERIF INTERROBANG ORNAMENT;So;0;ON;;;;;N;;;;;
+1F67C;VERY HEAVY SOLIDUS;So;0;ON;;;;;N;;;;;
+1F67D;VERY HEAVY REVERSE SOLIDUS;So;0;ON;;;;;N;;;;;
+1F67E;CHECKER BOARD;So;0;ON;;;;;N;;;;;
+1F67F;REVERSE CHECKER BOARD;So;0;ON;;;;;N;;;;;
+1F680;ROCKET;So;0;ON;;;;;N;;;;;
+1F681;HELICOPTER;So;0;ON;;;;;N;;;;;
+1F682;STEAM LOCOMOTIVE;So;0;ON;;;;;N;;;;;
+1F683;RAILWAY CAR;So;0;ON;;;;;N;;;;;
+1F684;HIGH-SPEED TRAIN;So;0;ON;;;;;N;;;;;
+1F685;HIGH-SPEED TRAIN WITH BULLET NOSE;So;0;ON;;;;;N;;;;;
+1F686;TRAIN;So;0;ON;;;;;N;;;;;
+1F687;METRO;So;0;ON;;;;;N;;;;;
+1F688;LIGHT RAIL;So;0;ON;;;;;N;;;;;
+1F689;STATION;So;0;ON;;;;;N;;;;;
+1F68A;TRAM;So;0;ON;;;;;N;;;;;
+1F68B;TRAM CAR;So;0;ON;;;;;N;;;;;
+1F68C;BUS;So;0;ON;;;;;N;;;;;
+1F68D;ONCOMING BUS;So;0;ON;;;;;N;;;;;
+1F68E;TROLLEYBUS;So;0;ON;;;;;N;;;;;
+1F68F;BUS STOP;So;0;ON;;;;;N;;;;;
+1F690;MINIBUS;So;0;ON;;;;;N;;;;;
+1F691;AMBULANCE;So;0;ON;;;;;N;;;;;
+1F692;FIRE ENGINE;So;0;ON;;;;;N;;;;;
+1F693;POLICE CAR;So;0;ON;;;;;N;;;;;
+1F694;ONCOMING POLICE CAR;So;0;ON;;;;;N;;;;;
+1F695;TAXI;So;0;ON;;;;;N;;;;;
+1F696;ONCOMING TAXI;So;0;ON;;;;;N;;;;;
+1F697;AUTOMOBILE;So;0;ON;;;;;N;;;;;
+1F698;ONCOMING AUTOMOBILE;So;0;ON;;;;;N;;;;;
+1F699;RECREATIONAL VEHICLE;So;0;ON;;;;;N;;;;;
+1F69A;DELIVERY TRUCK;So;0;ON;;;;;N;;;;;
+1F69B;ARTICULATED LORRY;So;0;ON;;;;;N;;;;;
+1F69C;TRACTOR;So;0;ON;;;;;N;;;;;
+1F69D;MONORAIL;So;0;ON;;;;;N;;;;;
+1F69E;MOUNTAIN RAILWAY;So;0;ON;;;;;N;;;;;
+1F69F;SUSPENSION RAILWAY;So;0;ON;;;;;N;;;;;
+1F6A0;MOUNTAIN CABLEWAY;So;0;ON;;;;;N;;;;;
+1F6A1;AERIAL TRAMWAY;So;0;ON;;;;;N;;;;;
+1F6A2;SHIP;So;0;ON;;;;;N;;;;;
+1F6A3;ROWBOAT;So;0;ON;;;;;N;;;;;
+1F6A4;SPEEDBOAT;So;0;ON;;;;;N;;;;;
+1F6A5;HORIZONTAL TRAFFIC LIGHT;So;0;ON;;;;;N;;;;;
+1F6A6;VERTICAL TRAFFIC LIGHT;So;0;ON;;;;;N;;;;;
+1F6A7;CONSTRUCTION SIGN;So;0;ON;;;;;N;;;;;
+1F6A8;POLICE CARS REVOLVING LIGHT;So;0;ON;;;;;N;;;;;
+1F6A9;TRIANGULAR FLAG ON POST;So;0;ON;;;;;N;;;;;
+1F6AA;DOOR;So;0;ON;;;;;N;;;;;
+1F6AB;NO ENTRY SIGN;So;0;ON;;;;;N;;;;;
+1F6AC;SMOKING SYMBOL;So;0;ON;;;;;N;;;;;
+1F6AD;NO SMOKING SYMBOL;So;0;ON;;;;;N;;;;;
+1F6AE;PUT LITTER IN ITS PLACE SYMBOL;So;0;ON;;;;;N;;;;;
+1F6AF;DO NOT LITTER SYMBOL;So;0;ON;;;;;N;;;;;
+1F6B0;POTABLE WATER SYMBOL;So;0;ON;;;;;N;;;;;
+1F6B1;NON-POTABLE WATER SYMBOL;So;0;ON;;;;;N;;;;;
+1F6B2;BICYCLE;So;0;ON;;;;;N;;;;;
+1F6B3;NO BICYCLES;So;0;ON;;;;;N;;;;;
+1F6B4;BICYCLIST;So;0;ON;;;;;N;;;;;
+1F6B5;MOUNTAIN BICYCLIST;So;0;ON;;;;;N;;;;;
+1F6B6;PEDESTRIAN;So;0;ON;;;;;N;;;;;
+1F6B7;NO PEDESTRIANS;So;0;ON;;;;;N;;;;;
+1F6B8;CHILDREN CROSSING;So;0;ON;;;;;N;;;;;
+1F6B9;MENS SYMBOL;So;0;ON;;;;;N;;;;;
+1F6BA;WOMENS SYMBOL;So;0;ON;;;;;N;;;;;
+1F6BB;RESTROOM;So;0;ON;;;;;N;;;;;
+1F6BC;BABY SYMBOL;So;0;ON;;;;;N;;;;;
+1F6BD;TOILET;So;0;ON;;;;;N;;;;;
+1F6BE;WATER CLOSET;So;0;ON;;;;;N;;;;;
+1F6BF;SHOWER;So;0;ON;;;;;N;;;;;
+1F6C0;BATH;So;0;ON;;;;;N;;;;;
+1F6C1;BATHTUB;So;0;ON;;;;;N;;;;;
+1F6C2;PASSPORT CONTROL;So;0;ON;;;;;N;;;;;
+1F6C3;CUSTOMS;So;0;ON;;;;;N;;;;;
+1F6C4;BAGGAGE CLAIM;So;0;ON;;;;;N;;;;;
+1F6C5;LEFT LUGGAGE;So;0;ON;;;;;N;;;;;
+1F6C6;TRIANGLE WITH ROUNDED CORNERS;So;0;ON;;;;;N;;;;;
+1F6C7;PROHIBITED SIGN;So;0;ON;;;;;N;;;;;
+1F6C8;CIRCLED INFORMATION SOURCE;So;0;ON;;;;;N;;;;;
+1F6C9;BOYS SYMBOL;So;0;ON;;;;;N;;;;;
+1F6CA;GIRLS SYMBOL;So;0;ON;;;;;N;;;;;
+1F6CB;COUCH AND LAMP;So;0;ON;;;;;N;;;;;
+1F6CC;SLEEPING ACCOMMODATION;So;0;ON;;;;;N;;;;;
+1F6CD;SHOPPING BAGS;So;0;ON;;;;;N;;;;;
+1F6CE;BELLHOP BELL;So;0;ON;;;;;N;;;;;
+1F6CF;BED;So;0;ON;;;;;N;;;;;
+1F6D0;PLACE OF WORSHIP;So;0;ON;;;;;N;;;;;
+1F6D1;OCTAGONAL SIGN;So;0;ON;;;;;N;;;;;
+1F6D2;SHOPPING TROLLEY;So;0;ON;;;;;N;;;;;
+1F6D3;STUPA;So;0;ON;;;;;N;;;;;
+1F6D4;PAGODA;So;0;ON;;;;;N;;;;;
+1F6D5;HINDU TEMPLE;So;0;ON;;;;;N;;;;;
+1F6E0;HAMMER AND WRENCH;So;0;ON;;;;;N;;;;;
+1F6E1;SHIELD;So;0;ON;;;;;N;;;;;
+1F6E2;OIL DRUM;So;0;ON;;;;;N;;;;;
+1F6E3;MOTORWAY;So;0;ON;;;;;N;;;;;
+1F6E4;RAILWAY TRACK;So;0;ON;;;;;N;;;;;
+1F6E5;MOTOR BOAT;So;0;ON;;;;;N;;;;;
+1F6E6;UP-POINTING MILITARY AIRPLANE;So;0;ON;;;;;N;;;;;
+1F6E7;UP-POINTING AIRPLANE;So;0;ON;;;;;N;;;;;
+1F6E8;UP-POINTING SMALL AIRPLANE;So;0;ON;;;;;N;;;;;
+1F6E9;SMALL AIRPLANE;So;0;ON;;;;;N;;;;;
+1F6EA;NORTHEAST-POINTING AIRPLANE;So;0;ON;;;;;N;;;;;
+1F6EB;AIRPLANE DEPARTURE;So;0;ON;;;;;N;;;;;
+1F6EC;AIRPLANE ARRIVING;So;0;ON;;;;;N;;;;;
+1F6F0;SATELLITE;So;0;ON;;;;;N;;;;;
+1F6F1;ONCOMING FIRE ENGINE;So;0;ON;;;;;N;;;;;
+1F6F2;DIESEL LOCOMOTIVE;So;0;ON;;;;;N;;;;;
+1F6F3;PASSENGER SHIP;So;0;ON;;;;;N;;;;;
+1F6F4;SCOOTER;So;0;ON;;;;;N;;;;;
+1F6F5;MOTOR SCOOTER;So;0;ON;;;;;N;;;;;
+1F6F6;CANOE;So;0;ON;;;;;N;;;;;
+1F6F7;SLED;So;0;ON;;;;;N;;;;;
+1F6F8;FLYING SAUCER;So;0;ON;;;;;N;;;;;
+1F6F9;SKATEBOARD;So;0;ON;;;;;N;;;;;
+1F6FA;AUTO RICKSHAW;So;0;ON;;;;;N;;;;;
+1F700;ALCHEMICAL SYMBOL FOR QUINTESSENCE;So;0;ON;;;;;N;;;;;
+1F701;ALCHEMICAL SYMBOL FOR AIR;So;0;ON;;;;;N;;;;;
+1F702;ALCHEMICAL SYMBOL FOR FIRE;So;0;ON;;;;;N;;;;;
+1F703;ALCHEMICAL SYMBOL FOR EARTH;So;0;ON;;;;;N;;;;;
+1F704;ALCHEMICAL SYMBOL FOR WATER;So;0;ON;;;;;N;;;;;
+1F705;ALCHEMICAL SYMBOL FOR AQUAFORTIS;So;0;ON;;;;;N;;;;;
+1F706;ALCHEMICAL SYMBOL FOR AQUA REGIA;So;0;ON;;;;;N;;;;;
+1F707;ALCHEMICAL SYMBOL FOR AQUA REGIA-2;So;0;ON;;;;;N;;;;;
+1F708;ALCHEMICAL SYMBOL FOR AQUA VITAE;So;0;ON;;;;;N;;;;;
+1F709;ALCHEMICAL SYMBOL FOR AQUA VITAE-2;So;0;ON;;;;;N;;;;;
+1F70A;ALCHEMICAL SYMBOL FOR VINEGAR;So;0;ON;;;;;N;;;;;
+1F70B;ALCHEMICAL SYMBOL FOR VINEGAR-2;So;0;ON;;;;;N;;;;;
+1F70C;ALCHEMICAL SYMBOL FOR VINEGAR-3;So;0;ON;;;;;N;;;;;
+1F70D;ALCHEMICAL SYMBOL FOR SULFUR;So;0;ON;;;;;N;;;;;
+1F70E;ALCHEMICAL SYMBOL FOR PHILOSOPHERS SULFUR;So;0;ON;;;;;N;;;;;
+1F70F;ALCHEMICAL SYMBOL FOR BLACK SULFUR;So;0;ON;;;;;N;;;;;
+1F710;ALCHEMICAL SYMBOL FOR MERCURY SUBLIMATE;So;0;ON;;;;;N;;;;;
+1F711;ALCHEMICAL SYMBOL FOR MERCURY SUBLIMATE-2;So;0;ON;;;;;N;;;;;
+1F712;ALCHEMICAL SYMBOL FOR MERCURY SUBLIMATE-3;So;0;ON;;;;;N;;;;;
+1F713;ALCHEMICAL SYMBOL FOR CINNABAR;So;0;ON;;;;;N;;;;;
+1F714;ALCHEMICAL SYMBOL FOR SALT;So;0;ON;;;;;N;;;;;
+1F715;ALCHEMICAL SYMBOL FOR NITRE;So;0;ON;;;;;N;;;;;
+1F716;ALCHEMICAL SYMBOL FOR VITRIOL;So;0;ON;;;;;N;;;;;
+1F717;ALCHEMICAL SYMBOL FOR VITRIOL-2;So;0;ON;;;;;N;;;;;
+1F718;ALCHEMICAL SYMBOL FOR ROCK SALT;So;0;ON;;;;;N;;;;;
+1F719;ALCHEMICAL SYMBOL FOR ROCK SALT-2;So;0;ON;;;;;N;;;;;
+1F71A;ALCHEMICAL SYMBOL FOR GOLD;So;0;ON;;;;;N;;;;;
+1F71B;ALCHEMICAL SYMBOL FOR SILVER;So;0;ON;;;;;N;;;;;
+1F71C;ALCHEMICAL SYMBOL FOR IRON ORE;So;0;ON;;;;;N;;;;;
+1F71D;ALCHEMICAL SYMBOL FOR IRON ORE-2;So;0;ON;;;;;N;;;;;
+1F71E;ALCHEMICAL SYMBOL FOR CROCUS OF IRON;So;0;ON;;;;;N;;;;;
+1F71F;ALCHEMICAL SYMBOL FOR REGULUS OF IRON;So;0;ON;;;;;N;;;;;
+1F720;ALCHEMICAL SYMBOL FOR COPPER ORE;So;0;ON;;;;;N;;;;;
+1F721;ALCHEMICAL SYMBOL FOR IRON-COPPER ORE;So;0;ON;;;;;N;;;;;
+1F722;ALCHEMICAL SYMBOL FOR SUBLIMATE OF COPPER;So;0;ON;;;;;N;;;;;
+1F723;ALCHEMICAL SYMBOL FOR CROCUS OF COPPER;So;0;ON;;;;;N;;;;;
+1F724;ALCHEMICAL SYMBOL FOR CROCUS OF COPPER-2;So;0;ON;;;;;N;;;;;
+1F725;ALCHEMICAL SYMBOL FOR COPPER ANTIMONIATE;So;0;ON;;;;;N;;;;;
+1F726;ALCHEMICAL SYMBOL FOR SALT OF COPPER ANTIMONIATE;So;0;ON;;;;;N;;;;;
+1F727;ALCHEMICAL SYMBOL FOR SUBLIMATE OF SALT OF COPPER;So;0;ON;;;;;N;;;;;
+1F728;ALCHEMICAL SYMBOL FOR VERDIGRIS;So;0;ON;;;;;N;;;;;
+1F729;ALCHEMICAL SYMBOL FOR TIN ORE;So;0;ON;;;;;N;;;;;
+1F72A;ALCHEMICAL SYMBOL FOR LEAD ORE;So;0;ON;;;;;N;;;;;
+1F72B;ALCHEMICAL SYMBOL FOR ANTIMONY ORE;So;0;ON;;;;;N;;;;;
+1F72C;ALCHEMICAL SYMBOL FOR SUBLIMATE OF ANTIMONY;So;0;ON;;;;;N;;;;;
+1F72D;ALCHEMICAL SYMBOL FOR SALT OF ANTIMONY;So;0;ON;;;;;N;;;;;
+1F72E;ALCHEMICAL SYMBOL FOR SUBLIMATE OF SALT OF ANTIMONY;So;0;ON;;;;;N;;;;;
+1F72F;ALCHEMICAL SYMBOL FOR VINEGAR OF ANTIMONY;So;0;ON;;;;;N;;;;;
+1F730;ALCHEMICAL SYMBOL FOR REGULUS OF ANTIMONY;So;0;ON;;;;;N;;;;;
+1F731;ALCHEMICAL SYMBOL FOR REGULUS OF ANTIMONY-2;So;0;ON;;;;;N;;;;;
+1F732;ALCHEMICAL SYMBOL FOR REGULUS;So;0;ON;;;;;N;;;;;
+1F733;ALCHEMICAL SYMBOL FOR REGULUS-2;So;0;ON;;;;;N;;;;;
+1F734;ALCHEMICAL SYMBOL FOR REGULUS-3;So;0;ON;;;;;N;;;;;
+1F735;ALCHEMICAL SYMBOL FOR REGULUS-4;So;0;ON;;;;;N;;;;;
+1F736;ALCHEMICAL SYMBOL FOR ALKALI;So;0;ON;;;;;N;;;;;
+1F737;ALCHEMICAL SYMBOL FOR ALKALI-2;So;0;ON;;;;;N;;;;;
+1F738;ALCHEMICAL SYMBOL FOR MARCASITE;So;0;ON;;;;;N;;;;;
+1F739;ALCHEMICAL SYMBOL FOR SAL-AMMONIAC;So;0;ON;;;;;N;;;;;
+1F73A;ALCHEMICAL SYMBOL FOR ARSENIC;So;0;ON;;;;;N;;;;;
+1F73B;ALCHEMICAL SYMBOL FOR REALGAR;So;0;ON;;;;;N;;;;;
+1F73C;ALCHEMICAL SYMBOL FOR REALGAR-2;So;0;ON;;;;;N;;;;;
+1F73D;ALCHEMICAL SYMBOL FOR AURIPIGMENT;So;0;ON;;;;;N;;;;;
+1F73E;ALCHEMICAL SYMBOL FOR BISMUTH ORE;So;0;ON;;;;;N;;;;;
+1F73F;ALCHEMICAL SYMBOL FOR TARTAR;So;0;ON;;;;;N;;;;;
+1F740;ALCHEMICAL SYMBOL FOR TARTAR-2;So;0;ON;;;;;N;;;;;
+1F741;ALCHEMICAL SYMBOL FOR QUICK LIME;So;0;ON;;;;;N;;;;;
+1F742;ALCHEMICAL SYMBOL FOR BORAX;So;0;ON;;;;;N;;;;;
+1F743;ALCHEMICAL SYMBOL FOR BORAX-2;So;0;ON;;;;;N;;;;;
+1F744;ALCHEMICAL SYMBOL FOR BORAX-3;So;0;ON;;;;;N;;;;;
+1F745;ALCHEMICAL SYMBOL FOR ALUM;So;0;ON;;;;;N;;;;;
+1F746;ALCHEMICAL SYMBOL FOR OIL;So;0;ON;;;;;N;;;;;
+1F747;ALCHEMICAL SYMBOL FOR SPIRIT;So;0;ON;;;;;N;;;;;
+1F748;ALCHEMICAL SYMBOL FOR TINCTURE;So;0;ON;;;;;N;;;;;
+1F749;ALCHEMICAL SYMBOL FOR GUM;So;0;ON;;;;;N;;;;;
+1F74A;ALCHEMICAL SYMBOL FOR WAX;So;0;ON;;;;;N;;;;;
+1F74B;ALCHEMICAL SYMBOL FOR POWDER;So;0;ON;;;;;N;;;;;
+1F74C;ALCHEMICAL SYMBOL FOR CALX;So;0;ON;;;;;N;;;;;
+1F74D;ALCHEMICAL SYMBOL FOR TUTTY;So;0;ON;;;;;N;;;;;
+1F74E;ALCHEMICAL SYMBOL FOR CAPUT MORTUUM;So;0;ON;;;;;N;;;;;
+1F74F;ALCHEMICAL SYMBOL FOR SCEPTER OF JOVE;So;0;ON;;;;;N;;;;;
+1F750;ALCHEMICAL SYMBOL FOR CADUCEUS;So;0;ON;;;;;N;;;;;
+1F751;ALCHEMICAL SYMBOL FOR TRIDENT;So;0;ON;;;;;N;;;;;
+1F752;ALCHEMICAL SYMBOL FOR STARRED TRIDENT;So;0;ON;;;;;N;;;;;
+1F753;ALCHEMICAL SYMBOL FOR LODESTONE;So;0;ON;;;;;N;;;;;
+1F754;ALCHEMICAL SYMBOL FOR SOAP;So;0;ON;;;;;N;;;;;
+1F755;ALCHEMICAL SYMBOL FOR URINE;So;0;ON;;;;;N;;;;;
+1F756;ALCHEMICAL SYMBOL FOR HORSE DUNG;So;0;ON;;;;;N;;;;;
+1F757;ALCHEMICAL SYMBOL FOR ASHES;So;0;ON;;;;;N;;;;;
+1F758;ALCHEMICAL SYMBOL FOR POT ASHES;So;0;ON;;;;;N;;;;;
+1F759;ALCHEMICAL SYMBOL FOR BRICK;So;0;ON;;;;;N;;;;;
+1F75A;ALCHEMICAL SYMBOL FOR POWDERED BRICK;So;0;ON;;;;;N;;;;;
+1F75B;ALCHEMICAL SYMBOL FOR AMALGAM;So;0;ON;;;;;N;;;;;
+1F75C;ALCHEMICAL SYMBOL FOR STRATUM SUPER STRATUM;So;0;ON;;;;;N;;;;;
+1F75D;ALCHEMICAL SYMBOL FOR STRATUM SUPER STRATUM-2;So;0;ON;;;;;N;;;;;
+1F75E;ALCHEMICAL SYMBOL FOR SUBLIMATION;So;0;ON;;;;;N;;;;;
+1F75F;ALCHEMICAL SYMBOL FOR PRECIPITATE;So;0;ON;;;;;N;;;;;
+1F760;ALCHEMICAL SYMBOL FOR DISTILL;So;0;ON;;;;;N;;;;;
+1F761;ALCHEMICAL SYMBOL FOR DISSOLVE;So;0;ON;;;;;N;;;;;
+1F762;ALCHEMICAL SYMBOL FOR DISSOLVE-2;So;0;ON;;;;;N;;;;;
+1F763;ALCHEMICAL SYMBOL FOR PURIFY;So;0;ON;;;;;N;;;;;
+1F764;ALCHEMICAL SYMBOL FOR PUTREFACTION;So;0;ON;;;;;N;;;;;
+1F765;ALCHEMICAL SYMBOL FOR CRUCIBLE;So;0;ON;;;;;N;;;;;
+1F766;ALCHEMICAL SYMBOL FOR CRUCIBLE-2;So;0;ON;;;;;N;;;;;
+1F767;ALCHEMICAL SYMBOL FOR CRUCIBLE-3;So;0;ON;;;;;N;;;;;
+1F768;ALCHEMICAL SYMBOL FOR CRUCIBLE-4;So;0;ON;;;;;N;;;;;
+1F769;ALCHEMICAL SYMBOL FOR CRUCIBLE-5;So;0;ON;;;;;N;;;;;
+1F76A;ALCHEMICAL SYMBOL FOR ALEMBIC;So;0;ON;;;;;N;;;;;
+1F76B;ALCHEMICAL SYMBOL FOR BATH OF MARY;So;0;ON;;;;;N;;;;;
+1F76C;ALCHEMICAL SYMBOL FOR BATH OF VAPOURS;So;0;ON;;;;;N;;;;;
+1F76D;ALCHEMICAL SYMBOL FOR RETORT;So;0;ON;;;;;N;;;;;
+1F76E;ALCHEMICAL SYMBOL FOR HOUR;So;0;ON;;;;;N;;;;;
+1F76F;ALCHEMICAL SYMBOL FOR NIGHT;So;0;ON;;;;;N;;;;;
+1F770;ALCHEMICAL SYMBOL FOR DAY-NIGHT;So;0;ON;;;;;N;;;;;
+1F771;ALCHEMICAL SYMBOL FOR MONTH;So;0;ON;;;;;N;;;;;
+1F772;ALCHEMICAL SYMBOL FOR HALF DRAM;So;0;ON;;;;;N;;;;;
+1F773;ALCHEMICAL SYMBOL FOR HALF OUNCE;So;0;ON;;;;;N;;;;;
+1F780;BLACK LEFT-POINTING ISOSCELES RIGHT TRIANGLE;So;0;ON;;;;;N;;;;;
+1F781;BLACK UP-POINTING ISOSCELES RIGHT TRIANGLE;So;0;ON;;;;;N;;;;;
+1F782;BLACK RIGHT-POINTING ISOSCELES RIGHT TRIANGLE;So;0;ON;;;;;N;;;;;
+1F783;BLACK DOWN-POINTING ISOSCELES RIGHT TRIANGLE;So;0;ON;;;;;N;;;;;
+1F784;BLACK SLIGHTLY SMALL CIRCLE;So;0;ON;;;;;N;;;;;
+1F785;MEDIUM BOLD WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+1F786;BOLD WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+1F787;HEAVY WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+1F788;VERY HEAVY WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+1F789;EXTREMELY HEAVY WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+1F78A;WHITE CIRCLE CONTAINING BLACK SMALL CIRCLE;So;0;ON;;;;;N;;;;;
+1F78B;ROUND TARGET;So;0;ON;;;;;N;;;;;
+1F78C;BLACK TINY SQUARE;So;0;ON;;;;;N;;;;;
+1F78D;BLACK SLIGHTLY SMALL SQUARE;So;0;ON;;;;;N;;;;;
+1F78E;LIGHT WHITE SQUARE;So;0;ON;;;;;N;;;;;
+1F78F;MEDIUM WHITE SQUARE;So;0;ON;;;;;N;;;;;
+1F790;BOLD WHITE SQUARE;So;0;ON;;;;;N;;;;;
+1F791;HEAVY WHITE SQUARE;So;0;ON;;;;;N;;;;;
+1F792;VERY HEAVY WHITE SQUARE;So;0;ON;;;;;N;;;;;
+1F793;EXTREMELY HEAVY WHITE SQUARE;So;0;ON;;;;;N;;;;;
+1F794;WHITE SQUARE CONTAINING BLACK VERY SMALL SQUARE;So;0;ON;;;;;N;;;;;
+1F795;WHITE SQUARE CONTAINING BLACK MEDIUM SQUARE;So;0;ON;;;;;N;;;;;
+1F796;SQUARE TARGET;So;0;ON;;;;;N;;;;;
+1F797;BLACK TINY DIAMOND;So;0;ON;;;;;N;;;;;
+1F798;BLACK VERY SMALL DIAMOND;So;0;ON;;;;;N;;;;;
+1F799;BLACK MEDIUM SMALL DIAMOND;So;0;ON;;;;;N;;;;;
+1F79A;WHITE DIAMOND CONTAINING BLACK VERY SMALL DIAMOND;So;0;ON;;;;;N;;;;;
+1F79B;WHITE DIAMOND CONTAINING BLACK MEDIUM DIAMOND;So;0;ON;;;;;N;;;;;
+1F79C;DIAMOND TARGET;So;0;ON;;;;;N;;;;;
+1F79D;BLACK TINY LOZENGE;So;0;ON;;;;;N;;;;;
+1F79E;BLACK VERY SMALL LOZENGE;So;0;ON;;;;;N;;;;;
+1F79F;BLACK MEDIUM SMALL LOZENGE;So;0;ON;;;;;N;;;;;
+1F7A0;WHITE LOZENGE CONTAINING BLACK SMALL LOZENGE;So;0;ON;;;;;N;;;;;
+1F7A1;THIN GREEK CROSS;So;0;ON;;;;;N;;;;;
+1F7A2;LIGHT GREEK CROSS;So;0;ON;;;;;N;;;;;
+1F7A3;MEDIUM GREEK CROSS;So;0;ON;;;;;N;;;;;
+1F7A4;BOLD GREEK CROSS;So;0;ON;;;;;N;;;;;
+1F7A5;VERY BOLD GREEK CROSS;So;0;ON;;;;;N;;;;;
+1F7A6;VERY HEAVY GREEK CROSS;So;0;ON;;;;;N;;;;;
+1F7A7;EXTREMELY HEAVY GREEK CROSS;So;0;ON;;;;;N;;;;;
+1F7A8;THIN SALTIRE;So;0;ON;;;;;N;;;;;
+1F7A9;LIGHT SALTIRE;So;0;ON;;;;;N;;;;;
+1F7AA;MEDIUM SALTIRE;So;0;ON;;;;;N;;;;;
+1F7AB;BOLD SALTIRE;So;0;ON;;;;;N;;;;;
+1F7AC;HEAVY SALTIRE;So;0;ON;;;;;N;;;;;
+1F7AD;VERY HEAVY SALTIRE;So;0;ON;;;;;N;;;;;
+1F7AE;EXTREMELY HEAVY SALTIRE;So;0;ON;;;;;N;;;;;
+1F7AF;LIGHT FIVE SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+1F7B0;MEDIUM FIVE SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+1F7B1;BOLD FIVE SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+1F7B2;HEAVY FIVE SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+1F7B3;VERY HEAVY FIVE SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+1F7B4;EXTREMELY HEAVY FIVE SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+1F7B5;LIGHT SIX SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+1F7B6;MEDIUM SIX SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+1F7B7;BOLD SIX SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+1F7B8;HEAVY SIX SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+1F7B9;VERY HEAVY SIX SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+1F7BA;EXTREMELY HEAVY SIX SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+1F7BB;LIGHT EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+1F7BC;MEDIUM EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+1F7BD;BOLD EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+1F7BE;HEAVY EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+1F7BF;VERY HEAVY EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+1F7C0;LIGHT THREE POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+1F7C1;MEDIUM THREE POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+1F7C2;THREE POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+1F7C3;MEDIUM THREE POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;;
+1F7C4;LIGHT FOUR POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+1F7C5;MEDIUM FOUR POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+1F7C6;FOUR POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+1F7C7;MEDIUM FOUR POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;;
+1F7C8;REVERSE LIGHT FOUR POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;;
+1F7C9;LIGHT FIVE POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+1F7CA;HEAVY FIVE POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+1F7CB;MEDIUM SIX POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+1F7CC;HEAVY SIX POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+1F7CD;SIX POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;;
+1F7CE;MEDIUM EIGHT POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+1F7CF;HEAVY EIGHT POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+1F7D0;VERY HEAVY EIGHT POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+1F7D1;HEAVY EIGHT POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;;
+1F7D2;LIGHT TWELVE POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+1F7D3;HEAVY TWELVE POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+1F7D4;HEAVY TWELVE POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;;
+1F7D5;CIRCLED TRIANGLE;So;0;ON;;;;;N;;;;;
+1F7D6;NEGATIVE CIRCLED TRIANGLE;So;0;ON;;;;;N;;;;;
+1F7D7;CIRCLED SQUARE;So;0;ON;;;;;N;;;;;
+1F7D8;NEGATIVE CIRCLED SQUARE;So;0;ON;;;;;N;;;;;
+1F7E0;LARGE ORANGE CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E1;LARGE YELLOW CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E2;LARGE GREEN CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E3;LARGE PURPLE CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E4;LARGE BROWN CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E5;LARGE RED SQUARE;So;0;ON;;;;;N;;;;;
+1F7E6;LARGE BLUE SQUARE;So;0;ON;;;;;N;;;;;
+1F7E7;LARGE ORANGE SQUARE;So;0;ON;;;;;N;;;;;
+1F7E8;LARGE YELLOW SQUARE;So;0;ON;;;;;N;;;;;
+1F7E9;LARGE GREEN SQUARE;So;0;ON;;;;;N;;;;;
+1F7EA;LARGE PURPLE SQUARE;So;0;ON;;;;;N;;;;;
+1F7EB;LARGE BROWN SQUARE;So;0;ON;;;;;N;;;;;
+1F800;LEFTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F801;UPWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F802;RIGHTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F803;DOWNWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F804;LEFTWARDS ARROW WITH MEDIUM TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F805;UPWARDS ARROW WITH MEDIUM TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F806;RIGHTWARDS ARROW WITH MEDIUM TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F807;DOWNWARDS ARROW WITH MEDIUM TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F808;LEFTWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F809;UPWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F80A;RIGHTWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F80B;DOWNWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F810;LEFTWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F811;UPWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F812;RIGHTWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F813;DOWNWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F814;LEFTWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F815;UPWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F816;RIGHTWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F817;DOWNWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F818;HEAVY LEFTWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F819;HEAVY UPWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F81A;HEAVY RIGHTWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F81B;HEAVY DOWNWARDS ARROW WITH EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F81C;HEAVY LEFTWARDS ARROW WITH LARGE EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F81D;HEAVY UPWARDS ARROW WITH LARGE EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F81E;HEAVY RIGHTWARDS ARROW WITH LARGE EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F81F;HEAVY DOWNWARDS ARROW WITH LARGE EQUILATERAL ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F820;LEFTWARDS TRIANGLE-HEADED ARROW WITH NARROW SHAFT;So;0;ON;;;;;N;;;;;
+1F821;UPWARDS TRIANGLE-HEADED ARROW WITH NARROW SHAFT;So;0;ON;;;;;N;;;;;
+1F822;RIGHTWARDS TRIANGLE-HEADED ARROW WITH NARROW SHAFT;So;0;ON;;;;;N;;;;;
+1F823;DOWNWARDS TRIANGLE-HEADED ARROW WITH NARROW SHAFT;So;0;ON;;;;;N;;;;;
+1F824;LEFTWARDS TRIANGLE-HEADED ARROW WITH MEDIUM SHAFT;So;0;ON;;;;;N;;;;;
+1F825;UPWARDS TRIANGLE-HEADED ARROW WITH MEDIUM SHAFT;So;0;ON;;;;;N;;;;;
+1F826;RIGHTWARDS TRIANGLE-HEADED ARROW WITH MEDIUM SHAFT;So;0;ON;;;;;N;;;;;
+1F827;DOWNWARDS TRIANGLE-HEADED ARROW WITH MEDIUM SHAFT;So;0;ON;;;;;N;;;;;
+1F828;LEFTWARDS TRIANGLE-HEADED ARROW WITH BOLD SHAFT;So;0;ON;;;;;N;;;;;
+1F829;UPWARDS TRIANGLE-HEADED ARROW WITH BOLD SHAFT;So;0;ON;;;;;N;;;;;
+1F82A;RIGHTWARDS TRIANGLE-HEADED ARROW WITH BOLD SHAFT;So;0;ON;;;;;N;;;;;
+1F82B;DOWNWARDS TRIANGLE-HEADED ARROW WITH BOLD SHAFT;So;0;ON;;;;;N;;;;;
+1F82C;LEFTWARDS TRIANGLE-HEADED ARROW WITH HEAVY SHAFT;So;0;ON;;;;;N;;;;;
+1F82D;UPWARDS TRIANGLE-HEADED ARROW WITH HEAVY SHAFT;So;0;ON;;;;;N;;;;;
+1F82E;RIGHTWARDS TRIANGLE-HEADED ARROW WITH HEAVY SHAFT;So;0;ON;;;;;N;;;;;
+1F82F;DOWNWARDS TRIANGLE-HEADED ARROW WITH HEAVY SHAFT;So;0;ON;;;;;N;;;;;
+1F830;LEFTWARDS TRIANGLE-HEADED ARROW WITH VERY HEAVY SHAFT;So;0;ON;;;;;N;;;;;
+1F831;UPWARDS TRIANGLE-HEADED ARROW WITH VERY HEAVY SHAFT;So;0;ON;;;;;N;;;;;
+1F832;RIGHTWARDS TRIANGLE-HEADED ARROW WITH VERY HEAVY SHAFT;So;0;ON;;;;;N;;;;;
+1F833;DOWNWARDS TRIANGLE-HEADED ARROW WITH VERY HEAVY SHAFT;So;0;ON;;;;;N;;;;;
+1F834;LEFTWARDS FINGER-POST ARROW;So;0;ON;;;;;N;;;;;
+1F835;UPWARDS FINGER-POST ARROW;So;0;ON;;;;;N;;;;;
+1F836;RIGHTWARDS FINGER-POST ARROW;So;0;ON;;;;;N;;;;;
+1F837;DOWNWARDS FINGER-POST ARROW;So;0;ON;;;;;N;;;;;
+1F838;LEFTWARDS SQUARED ARROW;So;0;ON;;;;;N;;;;;
+1F839;UPWARDS SQUARED ARROW;So;0;ON;;;;;N;;;;;
+1F83A;RIGHTWARDS SQUARED ARROW;So;0;ON;;;;;N;;;;;
+1F83B;DOWNWARDS SQUARED ARROW;So;0;ON;;;;;N;;;;;
+1F83C;LEFTWARDS COMPRESSED ARROW;So;0;ON;;;;;N;;;;;
+1F83D;UPWARDS COMPRESSED ARROW;So;0;ON;;;;;N;;;;;
+1F83E;RIGHTWARDS COMPRESSED ARROW;So;0;ON;;;;;N;;;;;
+1F83F;DOWNWARDS COMPRESSED ARROW;So;0;ON;;;;;N;;;;;
+1F840;LEFTWARDS HEAVY COMPRESSED ARROW;So;0;ON;;;;;N;;;;;
+1F841;UPWARDS HEAVY COMPRESSED ARROW;So;0;ON;;;;;N;;;;;
+1F842;RIGHTWARDS HEAVY COMPRESSED ARROW;So;0;ON;;;;;N;;;;;
+1F843;DOWNWARDS HEAVY COMPRESSED ARROW;So;0;ON;;;;;N;;;;;
+1F844;LEFTWARDS HEAVY ARROW;So;0;ON;;;;;N;;;;;
+1F845;UPWARDS HEAVY ARROW;So;0;ON;;;;;N;;;;;
+1F846;RIGHTWARDS HEAVY ARROW;So;0;ON;;;;;N;;;;;
+1F847;DOWNWARDS HEAVY ARROW;So;0;ON;;;;;N;;;;;
+1F850;LEFTWARDS SANS-SERIF ARROW;So;0;ON;;;;;N;;;;;
+1F851;UPWARDS SANS-SERIF ARROW;So;0;ON;;;;;N;;;;;
+1F852;RIGHTWARDS SANS-SERIF ARROW;So;0;ON;;;;;N;;;;;
+1F853;DOWNWARDS SANS-SERIF ARROW;So;0;ON;;;;;N;;;;;
+1F854;NORTH WEST SANS-SERIF ARROW;So;0;ON;;;;;N;;;;;
+1F855;NORTH EAST SANS-SERIF ARROW;So;0;ON;;;;;N;;;;;
+1F856;SOUTH EAST SANS-SERIF ARROW;So;0;ON;;;;;N;;;;;
+1F857;SOUTH WEST SANS-SERIF ARROW;So;0;ON;;;;;N;;;;;
+1F858;LEFT RIGHT SANS-SERIF ARROW;So;0;ON;;;;;N;;;;;
+1F859;UP DOWN SANS-SERIF ARROW;So;0;ON;;;;;N;;;;;
+1F860;WIDE-HEADED LEFTWARDS LIGHT BARB ARROW;So;0;ON;;;;;N;;;;;
+1F861;WIDE-HEADED UPWARDS LIGHT BARB ARROW;So;0;ON;;;;;N;;;;;
+1F862;WIDE-HEADED RIGHTWARDS LIGHT BARB ARROW;So;0;ON;;;;;N;;;;;
+1F863;WIDE-HEADED DOWNWARDS LIGHT BARB ARROW;So;0;ON;;;;;N;;;;;
+1F864;WIDE-HEADED NORTH WEST LIGHT BARB ARROW;So;0;ON;;;;;N;;;;;
+1F865;WIDE-HEADED NORTH EAST LIGHT BARB ARROW;So;0;ON;;;;;N;;;;;
+1F866;WIDE-HEADED SOUTH EAST LIGHT BARB ARROW;So;0;ON;;;;;N;;;;;
+1F867;WIDE-HEADED SOUTH WEST LIGHT BARB ARROW;So;0;ON;;;;;N;;;;;
+1F868;WIDE-HEADED LEFTWARDS BARB ARROW;So;0;ON;;;;;N;;;;;
+1F869;WIDE-HEADED UPWARDS BARB ARROW;So;0;ON;;;;;N;;;;;
+1F86A;WIDE-HEADED RIGHTWARDS BARB ARROW;So;0;ON;;;;;N;;;;;
+1F86B;WIDE-HEADED DOWNWARDS BARB ARROW;So;0;ON;;;;;N;;;;;
+1F86C;WIDE-HEADED NORTH WEST BARB ARROW;So;0;ON;;;;;N;;;;;
+1F86D;WIDE-HEADED NORTH EAST BARB ARROW;So;0;ON;;;;;N;;;;;
+1F86E;WIDE-HEADED SOUTH EAST BARB ARROW;So;0;ON;;;;;N;;;;;
+1F86F;WIDE-HEADED SOUTH WEST BARB ARROW;So;0;ON;;;;;N;;;;;
+1F870;WIDE-HEADED LEFTWARDS MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;;
+1F871;WIDE-HEADED UPWARDS MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;;
+1F872;WIDE-HEADED RIGHTWARDS MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;;
+1F873;WIDE-HEADED DOWNWARDS MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;;
+1F874;WIDE-HEADED NORTH WEST MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;;
+1F875;WIDE-HEADED NORTH EAST MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;;
+1F876;WIDE-HEADED SOUTH EAST MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;;
+1F877;WIDE-HEADED SOUTH WEST MEDIUM BARB ARROW;So;0;ON;;;;;N;;;;;
+1F878;WIDE-HEADED LEFTWARDS HEAVY BARB ARROW;So;0;ON;;;;;N;;;;;
+1F879;WIDE-HEADED UPWARDS HEAVY BARB ARROW;So;0;ON;;;;;N;;;;;
+1F87A;WIDE-HEADED RIGHTWARDS HEAVY BARB ARROW;So;0;ON;;;;;N;;;;;
+1F87B;WIDE-HEADED DOWNWARDS HEAVY BARB ARROW;So;0;ON;;;;;N;;;;;
+1F87C;WIDE-HEADED NORTH WEST HEAVY BARB ARROW;So;0;ON;;;;;N;;;;;
+1F87D;WIDE-HEADED NORTH EAST HEAVY BARB ARROW;So;0;ON;;;;;N;;;;;
+1F87E;WIDE-HEADED SOUTH EAST HEAVY BARB ARROW;So;0;ON;;;;;N;;;;;
+1F87F;WIDE-HEADED SOUTH WEST HEAVY BARB ARROW;So;0;ON;;;;;N;;;;;
+1F880;WIDE-HEADED LEFTWARDS VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;;
+1F881;WIDE-HEADED UPWARDS VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;;
+1F882;WIDE-HEADED RIGHTWARDS VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;;
+1F883;WIDE-HEADED DOWNWARDS VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;;
+1F884;WIDE-HEADED NORTH WEST VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;;
+1F885;WIDE-HEADED NORTH EAST VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;;
+1F886;WIDE-HEADED SOUTH EAST VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;;
+1F887;WIDE-HEADED SOUTH WEST VERY HEAVY BARB ARROW;So;0;ON;;;;;N;;;;;
+1F890;LEFTWARDS TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F891;UPWARDS TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F892;RIGHTWARDS TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F893;DOWNWARDS TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F894;LEFTWARDS WHITE ARROW WITHIN TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F895;UPWARDS WHITE ARROW WITHIN TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F896;RIGHTWARDS WHITE ARROW WITHIN TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F897;DOWNWARDS WHITE ARROW WITHIN TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
+1F898;LEFTWARDS ARROW WITH NOTCHED TAIL;So;0;ON;;;;;N;;;;;
+1F899;UPWARDS ARROW WITH NOTCHED TAIL;So;0;ON;;;;;N;;;;;
+1F89A;RIGHTWARDS ARROW WITH NOTCHED TAIL;So;0;ON;;;;;N;;;;;
+1F89B;DOWNWARDS ARROW WITH NOTCHED TAIL;So;0;ON;;;;;N;;;;;
+1F89C;HEAVY ARROW SHAFT WIDTH ONE;So;0;ON;;;;;N;;;;;
+1F89D;HEAVY ARROW SHAFT WIDTH TWO THIRDS;So;0;ON;;;;;N;;;;;
+1F89E;HEAVY ARROW SHAFT WIDTH ONE HALF;So;0;ON;;;;;N;;;;;
+1F89F;HEAVY ARROW SHAFT WIDTH ONE THIRD;So;0;ON;;;;;N;;;;;
+1F8A0;LEFTWARDS BOTTOM-SHADED WHITE ARROW;So;0;ON;;;;;N;;;;;
+1F8A1;RIGHTWARDS BOTTOM SHADED WHITE ARROW;So;0;ON;;;;;N;;;;;
+1F8A2;LEFTWARDS TOP SHADED WHITE ARROW;So;0;ON;;;;;N;;;;;
+1F8A3;RIGHTWARDS TOP SHADED WHITE ARROW;So;0;ON;;;;;N;;;;;
+1F8A4;LEFTWARDS LEFT-SHADED WHITE ARROW;So;0;ON;;;;;N;;;;;
+1F8A5;RIGHTWARDS RIGHT-SHADED WHITE ARROW;So;0;ON;;;;;N;;;;;
+1F8A6;LEFTWARDS RIGHT-SHADED WHITE ARROW;So;0;ON;;;;;N;;;;;
+1F8A7;RIGHTWARDS LEFT-SHADED WHITE ARROW;So;0;ON;;;;;N;;;;;
+1F8A8;LEFTWARDS BACK-TILTED SHADOWED WHITE ARROW;So;0;ON;;;;;N;;;;;
+1F8A9;RIGHTWARDS BACK-TILTED SHADOWED WHITE ARROW;So;0;ON;;;;;N;;;;;
+1F8AA;LEFTWARDS FRONT-TILTED SHADOWED WHITE ARROW;So;0;ON;;;;;N;;;;;
+1F8AB;RIGHTWARDS FRONT-TILTED SHADOWED WHITE ARROW;So;0;ON;;;;;N;;;;;
+1F8AC;WHITE ARROW SHAFT WIDTH ONE;So;0;ON;;;;;N;;;;;
+1F8AD;WHITE ARROW SHAFT WIDTH TWO THIRDS;So;0;ON;;;;;N;;;;;
+1F900;CIRCLED CROSS FORMEE WITH FOUR DOTS;So;0;ON;;;;;N;;;;;
+1F901;CIRCLED CROSS FORMEE WITH TWO DOTS;So;0;ON;;;;;N;;;;;
+1F902;CIRCLED CROSS FORMEE;So;0;ON;;;;;N;;;;;
+1F903;LEFT HALF CIRCLE WITH FOUR DOTS;So;0;ON;;;;;N;;;;;
+1F904;LEFT HALF CIRCLE WITH THREE DOTS;So;0;ON;;;;;N;;;;;
+1F905;LEFT HALF CIRCLE WITH TWO DOTS;So;0;ON;;;;;N;;;;;
+1F906;LEFT HALF CIRCLE WITH DOT;So;0;ON;;;;;N;;;;;
+1F907;LEFT HALF CIRCLE;So;0;ON;;;;;N;;;;;
+1F908;DOWNWARD FACING HOOK;So;0;ON;;;;;N;;;;;
+1F909;DOWNWARD FACING NOTCHED HOOK;So;0;ON;;;;;N;;;;;
+1F90A;DOWNWARD FACING HOOK WITH DOT;So;0;ON;;;;;N;;;;;
+1F90B;DOWNWARD FACING NOTCHED HOOK WITH DOT;So;0;ON;;;;;N;;;;;
+1F90D;WHITE HEART;So;0;ON;;;;;N;;;;;
+1F90E;BROWN HEART;So;0;ON;;;;;N;;;;;
+1F90F;PINCHING HAND;So;0;ON;;;;;N;;;;;
+1F910;ZIPPER-MOUTH FACE;So;0;ON;;;;;N;;;;;
+1F911;MONEY-MOUTH FACE;So;0;ON;;;;;N;;;;;
+1F912;FACE WITH THERMOMETER;So;0;ON;;;;;N;;;;;
+1F913;NERD FACE;So;0;ON;;;;;N;;;;;
+1F914;THINKING FACE;So;0;ON;;;;;N;;;;;
+1F915;FACE WITH HEAD-BANDAGE;So;0;ON;;;;;N;;;;;
+1F916;ROBOT FACE;So;0;ON;;;;;N;;;;;
+1F917;HUGGING FACE;So;0;ON;;;;;N;;;;;
+1F918;SIGN OF THE HORNS;So;0;ON;;;;;N;;;;;
+1F919;CALL ME HAND;So;0;ON;;;;;N;;;;;
+1F91A;RAISED BACK OF HAND;So;0;ON;;;;;N;;;;;
+1F91B;LEFT-FACING FIST;So;0;ON;;;;;N;;;;;
+1F91C;RIGHT-FACING FIST;So;0;ON;;;;;N;;;;;
+1F91D;HANDSHAKE;So;0;ON;;;;;N;;;;;
+1F91E;HAND WITH INDEX AND MIDDLE FINGERS CROSSED;So;0;ON;;;;;N;;;;;
+1F91F;I LOVE YOU HAND SIGN;So;0;ON;;;;;N;;;;;
+1F920;FACE WITH COWBOY HAT;So;0;ON;;;;;N;;;;;
+1F921;CLOWN FACE;So;0;ON;;;;;N;;;;;
+1F922;NAUSEATED FACE;So;0;ON;;;;;N;;;;;
+1F923;ROLLING ON THE FLOOR LAUGHING;So;0;ON;;;;;N;;;;;
+1F924;DROOLING FACE;So;0;ON;;;;;N;;;;;
+1F925;LYING FACE;So;0;ON;;;;;N;;;;;
+1F926;FACE PALM;So;0;ON;;;;;N;;;;;
+1F927;SNEEZING FACE;So;0;ON;;;;;N;;;;;
+1F928;FACE WITH ONE EYEBROW RAISED;So;0;ON;;;;;N;;;;;
+1F929;GRINNING FACE WITH STAR EYES;So;0;ON;;;;;N;;;;;
+1F92A;GRINNING FACE WITH ONE LARGE AND ONE SMALL EYE;So;0;ON;;;;;N;;;;;
+1F92B;FACE WITH FINGER COVERING CLOSED LIPS;So;0;ON;;;;;N;;;;;
+1F92C;SERIOUS FACE WITH SYMBOLS COVERING MOUTH;So;0;ON;;;;;N;;;;;
+1F92D;SMILING FACE WITH SMILING EYES AND HAND COVERING MOUTH;So;0;ON;;;;;N;;;;;
+1F92E;FACE WITH OPEN MOUTH VOMITING;So;0;ON;;;;;N;;;;;
+1F92F;SHOCKED FACE WITH EXPLODING HEAD;So;0;ON;;;;;N;;;;;
+1F930;PREGNANT WOMAN;So;0;ON;;;;;N;;;;;
+1F931;BREAST-FEEDING;So;0;ON;;;;;N;;;;;
+1F932;PALMS UP TOGETHER;So;0;ON;;;;;N;;;;;
+1F933;SELFIE;So;0;ON;;;;;N;;;;;
+1F934;PRINCE;So;0;ON;;;;;N;;;;;
+1F935;MAN IN TUXEDO;So;0;ON;;;;;N;;;;;
+1F936;MOTHER CHRISTMAS;So;0;ON;;;;;N;;;;;
+1F937;SHRUG;So;0;ON;;;;;N;;;;;
+1F938;PERSON DOING CARTWHEEL;So;0;ON;;;;;N;;;;;
+1F939;JUGGLING;So;0;ON;;;;;N;;;;;
+1F93A;FENCER;So;0;ON;;;;;N;;;;;
+1F93B;MODERN PENTATHLON;So;0;ON;;;;;N;;;;;
+1F93C;WRESTLERS;So;0;ON;;;;;N;;;;;
+1F93D;WATER POLO;So;0;ON;;;;;N;;;;;
+1F93E;HANDBALL;So;0;ON;;;;;N;;;;;
+1F93F;DIVING MASK;So;0;ON;;;;;N;;;;;
+1F940;WILTED FLOWER;So;0;ON;;;;;N;;;;;
+1F941;DRUM WITH DRUMSTICKS;So;0;ON;;;;;N;;;;;
+1F942;CLINKING GLASSES;So;0;ON;;;;;N;;;;;
+1F943;TUMBLER GLASS;So;0;ON;;;;;N;;;;;
+1F944;SPOON;So;0;ON;;;;;N;;;;;
+1F945;GOAL NET;So;0;ON;;;;;N;;;;;
+1F946;RIFLE;So;0;ON;;;;;N;;;;;
+1F947;FIRST PLACE MEDAL;So;0;ON;;;;;N;;;;;
+1F948;SECOND PLACE MEDAL;So;0;ON;;;;;N;;;;;
+1F949;THIRD PLACE MEDAL;So;0;ON;;;;;N;;;;;
+1F94A;BOXING GLOVE;So;0;ON;;;;;N;;;;;
+1F94B;MARTIAL ARTS UNIFORM;So;0;ON;;;;;N;;;;;
+1F94C;CURLING STONE;So;0;ON;;;;;N;;;;;
+1F94D;LACROSSE STICK AND BALL;So;0;ON;;;;;N;;;;;
+1F94E;SOFTBALL;So;0;ON;;;;;N;;;;;
+1F94F;FLYING DISC;So;0;ON;;;;;N;;;;;
+1F950;CROISSANT;So;0;ON;;;;;N;;;;;
+1F951;AVOCADO;So;0;ON;;;;;N;;;;;
+1F952;CUCUMBER;So;0;ON;;;;;N;;;;;
+1F953;BACON;So;0;ON;;;;;N;;;;;
+1F954;POTATO;So;0;ON;;;;;N;;;;;
+1F955;CARROT;So;0;ON;;;;;N;;;;;
+1F956;BAGUETTE BREAD;So;0;ON;;;;;N;;;;;
+1F957;GREEN SALAD;So;0;ON;;;;;N;;;;;
+1F958;SHALLOW PAN OF FOOD;So;0;ON;;;;;N;;;;;
+1F959;STUFFED FLATBREAD;So;0;ON;;;;;N;;;;;
+1F95A;EGG;So;0;ON;;;;;N;;;;;
+1F95B;GLASS OF MILK;So;0;ON;;;;;N;;;;;
+1F95C;PEANUTS;So;0;ON;;;;;N;;;;;
+1F95D;KIWIFRUIT;So;0;ON;;;;;N;;;;;
+1F95E;PANCAKES;So;0;ON;;;;;N;;;;;
+1F95F;DUMPLING;So;0;ON;;;;;N;;;;;
+1F960;FORTUNE COOKIE;So;0;ON;;;;;N;;;;;
+1F961;TAKEOUT BOX;So;0;ON;;;;;N;;;;;
+1F962;CHOPSTICKS;So;0;ON;;;;;N;;;;;
+1F963;BOWL WITH SPOON;So;0;ON;;;;;N;;;;;
+1F964;CUP WITH STRAW;So;0;ON;;;;;N;;;;;
+1F965;COCONUT;So;0;ON;;;;;N;;;;;
+1F966;BROCCOLI;So;0;ON;;;;;N;;;;;
+1F967;PIE;So;0;ON;;;;;N;;;;;
+1F968;PRETZEL;So;0;ON;;;;;N;;;;;
+1F969;CUT OF MEAT;So;0;ON;;;;;N;;;;;
+1F96A;SANDWICH;So;0;ON;;;;;N;;;;;
+1F96B;CANNED FOOD;So;0;ON;;;;;N;;;;;
+1F96C;LEAFY GREEN;So;0;ON;;;;;N;;;;;
+1F96D;MANGO;So;0;ON;;;;;N;;;;;
+1F96E;MOON CAKE;So;0;ON;;;;;N;;;;;
+1F96F;BAGEL;So;0;ON;;;;;N;;;;;
+1F970;SMILING FACE WITH SMILING EYES AND THREE HEARTS;So;0;ON;;;;;N;;;;;
+1F971;YAWNING FACE;So;0;ON;;;;;N;;;;;
+1F973;FACE WITH PARTY HORN AND PARTY HAT;So;0;ON;;;;;N;;;;;
+1F974;FACE WITH UNEVEN EYES AND WAVY MOUTH;So;0;ON;;;;;N;;;;;
+1F975;OVERHEATED FACE;So;0;ON;;;;;N;;;;;
+1F976;FREEZING FACE;So;0;ON;;;;;N;;;;;
+1F97A;FACE WITH PLEADING EYES;So;0;ON;;;;;N;;;;;
+1F97B;SARI;So;0;ON;;;;;N;;;;;
+1F97C;LAB COAT;So;0;ON;;;;;N;;;;;
+1F97D;GOGGLES;So;0;ON;;;;;N;;;;;
+1F97E;HIKING BOOT;So;0;ON;;;;;N;;;;;
+1F97F;FLAT SHOE;So;0;ON;;;;;N;;;;;
+1F980;CRAB;So;0;ON;;;;;N;;;;;
+1F981;LION FACE;So;0;ON;;;;;N;;;;;
+1F982;SCORPION;So;0;ON;;;;;N;;;;;
+1F983;TURKEY;So;0;ON;;;;;N;;;;;
+1F984;UNICORN FACE;So;0;ON;;;;;N;;;;;
+1F985;EAGLE;So;0;ON;;;;;N;;;;;
+1F986;DUCK;So;0;ON;;;;;N;;;;;
+1F987;BAT;So;0;ON;;;;;N;;;;;
+1F988;SHARK;So;0;ON;;;;;N;;;;;
+1F989;OWL;So;0;ON;;;;;N;;;;;
+1F98A;FOX FACE;So;0;ON;;;;;N;;;;;
+1F98B;BUTTERFLY;So;0;ON;;;;;N;;;;;
+1F98C;DEER;So;0;ON;;;;;N;;;;;
+1F98D;GORILLA;So;0;ON;;;;;N;;;;;
+1F98E;LIZARD;So;0;ON;;;;;N;;;;;
+1F98F;RHINOCEROS;So;0;ON;;;;;N;;;;;
+1F990;SHRIMP;So;0;ON;;;;;N;;;;;
+1F991;SQUID;So;0;ON;;;;;N;;;;;
+1F992;GIRAFFE FACE;So;0;ON;;;;;N;;;;;
+1F993;ZEBRA FACE;So;0;ON;;;;;N;;;;;
+1F994;HEDGEHOG;So;0;ON;;;;;N;;;;;
+1F995;SAUROPOD;So;0;ON;;;;;N;;;;;
+1F996;T-REX;So;0;ON;;;;;N;;;;;
+1F997;CRICKET;So;0;ON;;;;;N;;;;;
+1F998;KANGAROO;So;0;ON;;;;;N;;;;;
+1F999;LLAMA;So;0;ON;;;;;N;;;;;
+1F99A;PEACOCK;So;0;ON;;;;;N;;;;;
+1F99B;HIPPOPOTAMUS;So;0;ON;;;;;N;;;;;
+1F99C;PARROT;So;0;ON;;;;;N;;;;;
+1F99D;RACCOON;So;0;ON;;;;;N;;;;;
+1F99E;LOBSTER;So;0;ON;;;;;N;;;;;
+1F99F;MOSQUITO;So;0;ON;;;;;N;;;;;
+1F9A0;MICROBE;So;0;ON;;;;;N;;;;;
+1F9A1;BADGER;So;0;ON;;;;;N;;;;;
+1F9A2;SWAN;So;0;ON;;;;;N;;;;;
+1F9A5;SLOTH;So;0;ON;;;;;N;;;;;
+1F9A6;OTTER;So;0;ON;;;;;N;;;;;
+1F9A7;ORANGUTAN;So;0;ON;;;;;N;;;;;
+1F9A8;SKUNK;So;0;ON;;;;;N;;;;;
+1F9A9;FLAMINGO;So;0;ON;;;;;N;;;;;
+1F9AA;OYSTER;So;0;ON;;;;;N;;;;;
+1F9AE;GUIDE DOG;So;0;ON;;;;;N;;;;;
+1F9AF;PROBING CANE;So;0;ON;;;;;N;;;;;
+1F9B0;EMOJI COMPONENT RED HAIR;So;0;ON;;;;;N;;;;;
+1F9B1;EMOJI COMPONENT CURLY HAIR;So;0;ON;;;;;N;;;;;
+1F9B2;EMOJI COMPONENT BALD;So;0;ON;;;;;N;;;;;
+1F9B3;EMOJI COMPONENT WHITE HAIR;So;0;ON;;;;;N;;;;;
+1F9B4;BONE;So;0;ON;;;;;N;;;;;
+1F9B5;LEG;So;0;ON;;;;;N;;;;;
+1F9B6;FOOT;So;0;ON;;;;;N;;;;;
+1F9B7;TOOTH;So;0;ON;;;;;N;;;;;
+1F9B8;SUPERHERO;So;0;ON;;;;;N;;;;;
+1F9B9;SUPERVILLAIN;So;0;ON;;;;;N;;;;;
+1F9BA;SAFETY VEST;So;0;ON;;;;;N;;;;;
+1F9BB;EAR WITH HEARING AID;So;0;ON;;;;;N;;;;;
+1F9BC;MOTORIZED WHEELCHAIR;So;0;ON;;;;;N;;;;;
+1F9BD;MANUAL WHEELCHAIR;So;0;ON;;;;;N;;;;;
+1F9BE;MECHANICAL ARM;So;0;ON;;;;;N;;;;;
+1F9BF;MECHANICAL LEG;So;0;ON;;;;;N;;;;;
+1F9C0;CHEESE WEDGE;So;0;ON;;;;;N;;;;;
+1F9C1;CUPCAKE;So;0;ON;;;;;N;;;;;
+1F9C2;SALT SHAKER;So;0;ON;;;;;N;;;;;
+1F9C3;BEVERAGE BOX;So;0;ON;;;;;N;;;;;
+1F9C4;GARLIC;So;0;ON;;;;;N;;;;;
+1F9C5;ONION;So;0;ON;;;;;N;;;;;
+1F9C6;FALAFEL;So;0;ON;;;;;N;;;;;
+1F9C7;WAFFLE;So;0;ON;;;;;N;;;;;
+1F9C8;BUTTER;So;0;ON;;;;;N;;;;;
+1F9C9;MATE DRINK;So;0;ON;;;;;N;;;;;
+1F9CA;ICE CUBE;So;0;ON;;;;;N;;;;;
+1F9CD;STANDING PERSON;So;0;ON;;;;;N;;;;;
+1F9CE;KNEELING PERSON;So;0;ON;;;;;N;;;;;
+1F9CF;DEAF PERSON;So;0;ON;;;;;N;;;;;
+1F9D0;FACE WITH MONOCLE;So;0;ON;;;;;N;;;;;
+1F9D1;ADULT;So;0;ON;;;;;N;;;;;
+1F9D2;CHILD;So;0;ON;;;;;N;;;;;
+1F9D3;OLDER ADULT;So;0;ON;;;;;N;;;;;
+1F9D4;BEARDED PERSON;So;0;ON;;;;;N;;;;;
+1F9D5;PERSON WITH HEADSCARF;So;0;ON;;;;;N;;;;;
+1F9D6;PERSON IN STEAMY ROOM;So;0;ON;;;;;N;;;;;
+1F9D7;PERSON CLIMBING;So;0;ON;;;;;N;;;;;
+1F9D8;PERSON IN LOTUS POSITION;So;0;ON;;;;;N;;;;;
+1F9D9;MAGE;So;0;ON;;;;;N;;;;;
+1F9DA;FAIRY;So;0;ON;;;;;N;;;;;
+1F9DB;VAMPIRE;So;0;ON;;;;;N;;;;;
+1F9DC;MERPERSON;So;0;ON;;;;;N;;;;;
+1F9DD;ELF;So;0;ON;;;;;N;;;;;
+1F9DE;GENIE;So;0;ON;;;;;N;;;;;
+1F9DF;ZOMBIE;So;0;ON;;;;;N;;;;;
+1F9E0;BRAIN;So;0;ON;;;;;N;;;;;
+1F9E1;ORANGE HEART;So;0;ON;;;;;N;;;;;
+1F9E2;BILLED CAP;So;0;ON;;;;;N;;;;;
+1F9E3;SCARF;So;0;ON;;;;;N;;;;;
+1F9E4;GLOVES;So;0;ON;;;;;N;;;;;
+1F9E5;COAT;So;0;ON;;;;;N;;;;;
+1F9E6;SOCKS;So;0;ON;;;;;N;;;;;
+1F9E7;RED GIFT ENVELOPE;So;0;ON;;;;;N;;;;;
+1F9E8;FIRECRACKER;So;0;ON;;;;;N;;;;;
+1F9E9;JIGSAW PUZZLE PIECE;So;0;ON;;;;;N;;;;;
+1F9EA;TEST TUBE;So;0;ON;;;;;N;;;;;
+1F9EB;PETRI DISH;So;0;ON;;;;;N;;;;;
+1F9EC;DNA DOUBLE HELIX;So;0;ON;;;;;N;;;;;
+1F9ED;COMPASS;So;0;ON;;;;;N;;;;;
+1F9EE;ABACUS;So;0;ON;;;;;N;;;;;
+1F9EF;FIRE EXTINGUISHER;So;0;ON;;;;;N;;;;;
+1F9F0;TOOLBOX;So;0;ON;;;;;N;;;;;
+1F9F1;BRICK;So;0;ON;;;;;N;;;;;
+1F9F2;MAGNET;So;0;ON;;;;;N;;;;;
+1F9F3;LUGGAGE;So;0;ON;;;;;N;;;;;
+1F9F4;LOTION BOTTLE;So;0;ON;;;;;N;;;;;
+1F9F5;SPOOL OF THREAD;So;0;ON;;;;;N;;;;;
+1F9F6;BALL OF YARN;So;0;ON;;;;;N;;;;;
+1F9F7;SAFETY PIN;So;0;ON;;;;;N;;;;;
+1F9F8;TEDDY BEAR;So;0;ON;;;;;N;;;;;
+1F9F9;BROOM;So;0;ON;;;;;N;;;;;
+1F9FA;BASKET;So;0;ON;;;;;N;;;;;
+1F9FB;ROLL OF PAPER;So;0;ON;;;;;N;;;;;
+1F9FC;BAR OF SOAP;So;0;ON;;;;;N;;;;;
+1F9FD;SPONGE;So;0;ON;;;;;N;;;;;
+1F9FE;RECEIPT;So;0;ON;;;;;N;;;;;
+1F9FF;NAZAR AMULET;So;0;ON;;;;;N;;;;;
+1FA00;NEUTRAL CHESS KING;So;0;ON;;;;;N;;;;;
+1FA01;NEUTRAL CHESS QUEEN;So;0;ON;;;;;N;;;;;
+1FA02;NEUTRAL CHESS ROOK;So;0;ON;;;;;N;;;;;
+1FA03;NEUTRAL CHESS BISHOP;So;0;ON;;;;;N;;;;;
+1FA04;NEUTRAL CHESS KNIGHT;So;0;ON;;;;;N;;;;;
+1FA05;NEUTRAL CHESS PAWN;So;0;ON;;;;;N;;;;;
+1FA06;WHITE CHESS KNIGHT ROTATED FORTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA07;BLACK CHESS KNIGHT ROTATED FORTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA08;NEUTRAL CHESS KNIGHT ROTATED FORTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA09;WHITE CHESS KING ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0A;WHITE CHESS QUEEN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0B;WHITE CHESS ROOK ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0C;WHITE CHESS BISHOP ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0D;WHITE CHESS KNIGHT ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0E;WHITE CHESS PAWN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0F;BLACK CHESS KING ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA10;BLACK CHESS QUEEN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA11;BLACK CHESS ROOK ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA12;BLACK CHESS BISHOP ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA13;BLACK CHESS KNIGHT ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA14;BLACK CHESS PAWN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA15;NEUTRAL CHESS KING ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA16;NEUTRAL CHESS QUEEN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA17;NEUTRAL CHESS ROOK ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA18;NEUTRAL CHESS BISHOP ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA19;NEUTRAL CHESS KNIGHT ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA1A;NEUTRAL CHESS PAWN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA1B;WHITE CHESS KNIGHT ROTATED ONE HUNDRED THIRTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA1C;BLACK CHESS KNIGHT ROTATED ONE HUNDRED THIRTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA1D;NEUTRAL CHESS KNIGHT ROTATED ONE HUNDRED THIRTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA1E;WHITE CHESS TURNED KING;So;0;ON;;;;;N;;;;;
+1FA1F;WHITE CHESS TURNED QUEEN;So;0;ON;;;;;N;;;;;
+1FA20;WHITE CHESS TURNED ROOK;So;0;ON;;;;;N;;;;;
+1FA21;WHITE CHESS TURNED BISHOP;So;0;ON;;;;;N;;;;;
+1FA22;WHITE CHESS TURNED KNIGHT;So;0;ON;;;;;N;;;;;
+1FA23;WHITE CHESS TURNED PAWN;So;0;ON;;;;;N;;;;;
+1FA24;BLACK CHESS TURNED KING;So;0;ON;;;;;N;;;;;
+1FA25;BLACK CHESS TURNED QUEEN;So;0;ON;;;;;N;;;;;
+1FA26;BLACK CHESS TURNED ROOK;So;0;ON;;;;;N;;;;;
+1FA27;BLACK CHESS TURNED BISHOP;So;0;ON;;;;;N;;;;;
+1FA28;BLACK CHESS TURNED KNIGHT;So;0;ON;;;;;N;;;;;
+1FA29;BLACK CHESS TURNED PAWN;So;0;ON;;;;;N;;;;;
+1FA2A;NEUTRAL CHESS TURNED KING;So;0;ON;;;;;N;;;;;
+1FA2B;NEUTRAL CHESS TURNED QUEEN;So;0;ON;;;;;N;;;;;
+1FA2C;NEUTRAL CHESS TURNED ROOK;So;0;ON;;;;;N;;;;;
+1FA2D;NEUTRAL CHESS TURNED BISHOP;So;0;ON;;;;;N;;;;;
+1FA2E;NEUTRAL CHESS TURNED KNIGHT;So;0;ON;;;;;N;;;;;
+1FA2F;NEUTRAL CHESS TURNED PAWN;So;0;ON;;;;;N;;;;;
+1FA30;WHITE CHESS KNIGHT ROTATED TWO HUNDRED TWENTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA31;BLACK CHESS KNIGHT ROTATED TWO HUNDRED TWENTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA32;NEUTRAL CHESS KNIGHT ROTATED TWO HUNDRED TWENTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA33;WHITE CHESS KING ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA34;WHITE CHESS QUEEN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA35;WHITE CHESS ROOK ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA36;WHITE CHESS BISHOP ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA37;WHITE CHESS KNIGHT ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA38;WHITE CHESS PAWN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA39;BLACK CHESS KING ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3A;BLACK CHESS QUEEN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3B;BLACK CHESS ROOK ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3C;BLACK CHESS BISHOP ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3D;BLACK CHESS KNIGHT ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3E;BLACK CHESS PAWN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3F;NEUTRAL CHESS KING ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA40;NEUTRAL CHESS QUEEN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA41;NEUTRAL CHESS ROOK ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA42;NEUTRAL CHESS BISHOP ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA43;NEUTRAL CHESS KNIGHT ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA44;NEUTRAL CHESS PAWN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA45;WHITE CHESS KNIGHT ROTATED THREE HUNDRED FIFTEEN DEGREES;So;0;ON;;;;;N;;;;;
+1FA46;BLACK CHESS KNIGHT ROTATED THREE HUNDRED FIFTEEN DEGREES;So;0;ON;;;;;N;;;;;
+1FA47;NEUTRAL CHESS KNIGHT ROTATED THREE HUNDRED FIFTEEN DEGREES;So;0;ON;;;;;N;;;;;
+1FA48;WHITE CHESS EQUIHOPPER;So;0;ON;;;;;N;;;;;
+1FA49;BLACK CHESS EQUIHOPPER;So;0;ON;;;;;N;;;;;
+1FA4A;NEUTRAL CHESS EQUIHOPPER;So;0;ON;;;;;N;;;;;
+1FA4B;WHITE CHESS EQUIHOPPER ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA4C;BLACK CHESS EQUIHOPPER ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA4D;NEUTRAL CHESS EQUIHOPPER ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA4E;WHITE CHESS KNIGHT-QUEEN;So;0;ON;;;;;N;;;;;
+1FA4F;WHITE CHESS KNIGHT-ROOK;So;0;ON;;;;;N;;;;;
+1FA50;WHITE CHESS KNIGHT-BISHOP;So;0;ON;;;;;N;;;;;
+1FA51;BLACK CHESS KNIGHT-QUEEN;So;0;ON;;;;;N;;;;;
+1FA52;BLACK CHESS KNIGHT-ROOK;So;0;ON;;;;;N;;;;;
+1FA53;BLACK CHESS KNIGHT-BISHOP;So;0;ON;;;;;N;;;;;
+1FA60;XIANGQI RED GENERAL;So;0;ON;;;;;N;;;;;
+1FA61;XIANGQI RED MANDARIN;So;0;ON;;;;;N;;;;;
+1FA62;XIANGQI RED ELEPHANT;So;0;ON;;;;;N;;;;;
+1FA63;XIANGQI RED HORSE;So;0;ON;;;;;N;;;;;
+1FA64;XIANGQI RED CHARIOT;So;0;ON;;;;;N;;;;;
+1FA65;XIANGQI RED CANNON;So;0;ON;;;;;N;;;;;
+1FA66;XIANGQI RED SOLDIER;So;0;ON;;;;;N;;;;;
+1FA67;XIANGQI BLACK GENERAL;So;0;ON;;;;;N;;;;;
+1FA68;XIANGQI BLACK MANDARIN;So;0;ON;;;;;N;;;;;
+1FA69;XIANGQI BLACK ELEPHANT;So;0;ON;;;;;N;;;;;
+1FA6A;XIANGQI BLACK HORSE;So;0;ON;;;;;N;;;;;
+1FA6B;XIANGQI BLACK CHARIOT;So;0;ON;;;;;N;;;;;
+1FA6C;XIANGQI BLACK CANNON;So;0;ON;;;;;N;;;;;
+1FA6D;XIANGQI BLACK SOLDIER;So;0;ON;;;;;N;;;;;
+1FA70;BALLET SHOES;So;0;ON;;;;;N;;;;;
+1FA71;ONE-PIECE SWIMSUIT;So;0;ON;;;;;N;;;;;
+1FA72;BRIEFS;So;0;ON;;;;;N;;;;;
+1FA73;SHORTS;So;0;ON;;;;;N;;;;;
+1FA78;DROP OF BLOOD;So;0;ON;;;;;N;;;;;
+1FA79;ADHESIVE BANDAGE;So;0;ON;;;;;N;;;;;
+1FA7A;STETHOSCOPE;So;0;ON;;;;;N;;;;;
+1FA80;YO-YO;So;0;ON;;;;;N;;;;;
+1FA81;KITE;So;0;ON;;;;;N;;;;;
+1FA82;PARACHUTE;So;0;ON;;;;;N;;;;;
+1FA90;RINGED PLANET;So;0;ON;;;;;N;;;;;
+1FA91;CHAIR;So;0;ON;;;;;N;;;;;
+1FA92;RAZOR;So;0;ON;;;;;N;;;;;
+1FA93;AXE;So;0;ON;;;;;N;;;;;
+1FA94;DIYA LAMP;So;0;ON;;;;;N;;;;;
+1FA95;BANJO;So;0;ON;;;;;N;;;;;
+20000;<CJK Ideograph Extension B, First>;Lo;0;L;;;;;N;;;;;
+2A6D6;<CJK Ideograph Extension B, Last>;Lo;0;L;;;;;N;;;;;
+2A700;<CJK Ideograph Extension C, First>;Lo;0;L;;;;;N;;;;;
+2B734;<CJK Ideograph Extension C, Last>;Lo;0;L;;;;;N;;;;;
+2B740;<CJK Ideograph Extension D, First>;Lo;0;L;;;;;N;;;;;
+2B81D;<CJK Ideograph Extension D, Last>;Lo;0;L;;;;;N;;;;;
+2B820;<CJK Ideograph Extension E, First>;Lo;0;L;;;;;N;;;;;
+2CEA1;<CJK Ideograph Extension E, Last>;Lo;0;L;;;;;N;;;;;
+2CEB0;<CJK Ideograph Extension F, First>;Lo;0;L;;;;;N;;;;;
+2EBE0;<CJK Ideograph Extension F, Last>;Lo;0;L;;;;;N;;;;;
+2F800;CJK COMPATIBILITY IDEOGRAPH-2F800;Lo;0;L;4E3D;;;;N;;;;;
+2F801;CJK COMPATIBILITY IDEOGRAPH-2F801;Lo;0;L;4E38;;;;N;;;;;
+2F802;CJK COMPATIBILITY IDEOGRAPH-2F802;Lo;0;L;4E41;;;;N;;;;;
+2F803;CJK COMPATIBILITY IDEOGRAPH-2F803;Lo;0;L;20122;;;;N;;;;;
+2F804;CJK COMPATIBILITY IDEOGRAPH-2F804;Lo;0;L;4F60;;;;N;;;;;
+2F805;CJK COMPATIBILITY IDEOGRAPH-2F805;Lo;0;L;4FAE;;;;N;;;;;
+2F806;CJK COMPATIBILITY IDEOGRAPH-2F806;Lo;0;L;4FBB;;;;N;;;;;
+2F807;CJK COMPATIBILITY IDEOGRAPH-2F807;Lo;0;L;5002;;;;N;;;;;
+2F808;CJK COMPATIBILITY IDEOGRAPH-2F808;Lo;0;L;507A;;;;N;;;;;
+2F809;CJK COMPATIBILITY IDEOGRAPH-2F809;Lo;0;L;5099;;;;N;;;;;
+2F80A;CJK COMPATIBILITY IDEOGRAPH-2F80A;Lo;0;L;50E7;;;;N;;;;;
+2F80B;CJK COMPATIBILITY IDEOGRAPH-2F80B;Lo;0;L;50CF;;;;N;;;;;
+2F80C;CJK COMPATIBILITY IDEOGRAPH-2F80C;Lo;0;L;349E;;;;N;;;;;
+2F80D;CJK COMPATIBILITY IDEOGRAPH-2F80D;Lo;0;L;2063A;;;;N;;;;;
+2F80E;CJK COMPATIBILITY IDEOGRAPH-2F80E;Lo;0;L;514D;;;;N;;;;;
+2F80F;CJK COMPATIBILITY IDEOGRAPH-2F80F;Lo;0;L;5154;;;;N;;;;;
+2F810;CJK COMPATIBILITY IDEOGRAPH-2F810;Lo;0;L;5164;;;;N;;;;;
+2F811;CJK COMPATIBILITY IDEOGRAPH-2F811;Lo;0;L;5177;;;;N;;;;;
+2F812;CJK COMPATIBILITY IDEOGRAPH-2F812;Lo;0;L;2051C;;;;N;;;;;
+2F813;CJK COMPATIBILITY IDEOGRAPH-2F813;Lo;0;L;34B9;;;;N;;;;;
+2F814;CJK COMPATIBILITY IDEOGRAPH-2F814;Lo;0;L;5167;;;;N;;;;;
+2F815;CJK COMPATIBILITY IDEOGRAPH-2F815;Lo;0;L;518D;;;;N;;;;;
+2F816;CJK COMPATIBILITY IDEOGRAPH-2F816;Lo;0;L;2054B;;;;N;;;;;
+2F817;CJK COMPATIBILITY IDEOGRAPH-2F817;Lo;0;L;5197;;;;N;;;;;
+2F818;CJK COMPATIBILITY IDEOGRAPH-2F818;Lo;0;L;51A4;;;;N;;;;;
+2F819;CJK COMPATIBILITY IDEOGRAPH-2F819;Lo;0;L;4ECC;;;;N;;;;;
+2F81A;CJK COMPATIBILITY IDEOGRAPH-2F81A;Lo;0;L;51AC;;;;N;;;;;
+2F81B;CJK COMPATIBILITY IDEOGRAPH-2F81B;Lo;0;L;51B5;;;;N;;;;;
+2F81C;CJK COMPATIBILITY IDEOGRAPH-2F81C;Lo;0;L;291DF;;;;N;;;;;
+2F81D;CJK COMPATIBILITY IDEOGRAPH-2F81D;Lo;0;L;51F5;;;;N;;;;;
+2F81E;CJK COMPATIBILITY IDEOGRAPH-2F81E;Lo;0;L;5203;;;;N;;;;;
+2F81F;CJK COMPATIBILITY IDEOGRAPH-2F81F;Lo;0;L;34DF;;;;N;;;;;
+2F820;CJK COMPATIBILITY IDEOGRAPH-2F820;Lo;0;L;523B;;;;N;;;;;
+2F821;CJK COMPATIBILITY IDEOGRAPH-2F821;Lo;0;L;5246;;;;N;;;;;
+2F822;CJK COMPATIBILITY IDEOGRAPH-2F822;Lo;0;L;5272;;;;N;;;;;
+2F823;CJK COMPATIBILITY IDEOGRAPH-2F823;Lo;0;L;5277;;;;N;;;;;
+2F824;CJK COMPATIBILITY IDEOGRAPH-2F824;Lo;0;L;3515;;;;N;;;;;
+2F825;CJK COMPATIBILITY IDEOGRAPH-2F825;Lo;0;L;52C7;;;;N;;;;;
+2F826;CJK COMPATIBILITY IDEOGRAPH-2F826;Lo;0;L;52C9;;;;N;;;;;
+2F827;CJK COMPATIBILITY IDEOGRAPH-2F827;Lo;0;L;52E4;;;;N;;;;;
+2F828;CJK COMPATIBILITY IDEOGRAPH-2F828;Lo;0;L;52FA;;;;N;;;;;
+2F829;CJK COMPATIBILITY IDEOGRAPH-2F829;Lo;0;L;5305;;;;N;;;;;
+2F82A;CJK COMPATIBILITY IDEOGRAPH-2F82A;Lo;0;L;5306;;;;N;;;;;
+2F82B;CJK COMPATIBILITY IDEOGRAPH-2F82B;Lo;0;L;5317;;;;N;;;;;
+2F82C;CJK COMPATIBILITY IDEOGRAPH-2F82C;Lo;0;L;5349;;;;N;;;;;
+2F82D;CJK COMPATIBILITY IDEOGRAPH-2F82D;Lo;0;L;5351;;;;N;;;;;
+2F82E;CJK COMPATIBILITY IDEOGRAPH-2F82E;Lo;0;L;535A;;;;N;;;;;
+2F82F;CJK COMPATIBILITY IDEOGRAPH-2F82F;Lo;0;L;5373;;;;N;;;;;
+2F830;CJK COMPATIBILITY IDEOGRAPH-2F830;Lo;0;L;537D;;;;N;;;;;
+2F831;CJK COMPATIBILITY IDEOGRAPH-2F831;Lo;0;L;537F;;;;N;;;;;
+2F832;CJK COMPATIBILITY IDEOGRAPH-2F832;Lo;0;L;537F;;;;N;;;;;
+2F833;CJK COMPATIBILITY IDEOGRAPH-2F833;Lo;0;L;537F;;;;N;;;;;
+2F834;CJK COMPATIBILITY IDEOGRAPH-2F834;Lo;0;L;20A2C;;;;N;;;;;
+2F835;CJK COMPATIBILITY IDEOGRAPH-2F835;Lo;0;L;7070;;;;N;;;;;
+2F836;CJK COMPATIBILITY IDEOGRAPH-2F836;Lo;0;L;53CA;;;;N;;;;;
+2F837;CJK COMPATIBILITY IDEOGRAPH-2F837;Lo;0;L;53DF;;;;N;;;;;
+2F838;CJK COMPATIBILITY IDEOGRAPH-2F838;Lo;0;L;20B63;;;;N;;;;;
+2F839;CJK COMPATIBILITY IDEOGRAPH-2F839;Lo;0;L;53EB;;;;N;;;;;
+2F83A;CJK COMPATIBILITY IDEOGRAPH-2F83A;Lo;0;L;53F1;;;;N;;;;;
+2F83B;CJK COMPATIBILITY IDEOGRAPH-2F83B;Lo;0;L;5406;;;;N;;;;;
+2F83C;CJK COMPATIBILITY IDEOGRAPH-2F83C;Lo;0;L;549E;;;;N;;;;;
+2F83D;CJK COMPATIBILITY IDEOGRAPH-2F83D;Lo;0;L;5438;;;;N;;;;;
+2F83E;CJK COMPATIBILITY IDEOGRAPH-2F83E;Lo;0;L;5448;;;;N;;;;;
+2F83F;CJK COMPATIBILITY IDEOGRAPH-2F83F;Lo;0;L;5468;;;;N;;;;;
+2F840;CJK COMPATIBILITY IDEOGRAPH-2F840;Lo;0;L;54A2;;;;N;;;;;
+2F841;CJK COMPATIBILITY IDEOGRAPH-2F841;Lo;0;L;54F6;;;;N;;;;;
+2F842;CJK COMPATIBILITY IDEOGRAPH-2F842;Lo;0;L;5510;;;;N;;;;;
+2F843;CJK COMPATIBILITY IDEOGRAPH-2F843;Lo;0;L;5553;;;;N;;;;;
+2F844;CJK COMPATIBILITY IDEOGRAPH-2F844;Lo;0;L;5563;;;;N;;;;;
+2F845;CJK COMPATIBILITY IDEOGRAPH-2F845;Lo;0;L;5584;;;;N;;;;;
+2F846;CJK COMPATIBILITY IDEOGRAPH-2F846;Lo;0;L;5584;;;;N;;;;;
+2F847;CJK COMPATIBILITY IDEOGRAPH-2F847;Lo;0;L;5599;;;;N;;;;;
+2F848;CJK COMPATIBILITY IDEOGRAPH-2F848;Lo;0;L;55AB;;;;N;;;;;
+2F849;CJK COMPATIBILITY IDEOGRAPH-2F849;Lo;0;L;55B3;;;;N;;;;;
+2F84A;CJK COMPATIBILITY IDEOGRAPH-2F84A;Lo;0;L;55C2;;;;N;;;;;
+2F84B;CJK COMPATIBILITY IDEOGRAPH-2F84B;Lo;0;L;5716;;;;N;;;;;
+2F84C;CJK COMPATIBILITY IDEOGRAPH-2F84C;Lo;0;L;5606;;;;N;;;;;
+2F84D;CJK COMPATIBILITY IDEOGRAPH-2F84D;Lo;0;L;5717;;;;N;;;;;
+2F84E;CJK COMPATIBILITY IDEOGRAPH-2F84E;Lo;0;L;5651;;;;N;;;;;
+2F84F;CJK COMPATIBILITY IDEOGRAPH-2F84F;Lo;0;L;5674;;;;N;;;;;
+2F850;CJK COMPATIBILITY IDEOGRAPH-2F850;Lo;0;L;5207;;;;N;;;;;
+2F851;CJK COMPATIBILITY IDEOGRAPH-2F851;Lo;0;L;58EE;;;;N;;;;;
+2F852;CJK COMPATIBILITY IDEOGRAPH-2F852;Lo;0;L;57CE;;;;N;;;;;
+2F853;CJK COMPATIBILITY IDEOGRAPH-2F853;Lo;0;L;57F4;;;;N;;;;;
+2F854;CJK COMPATIBILITY IDEOGRAPH-2F854;Lo;0;L;580D;;;;N;;;;;
+2F855;CJK COMPATIBILITY IDEOGRAPH-2F855;Lo;0;L;578B;;;;N;;;;;
+2F856;CJK COMPATIBILITY IDEOGRAPH-2F856;Lo;0;L;5832;;;;N;;;;;
+2F857;CJK COMPATIBILITY IDEOGRAPH-2F857;Lo;0;L;5831;;;;N;;;;;
+2F858;CJK COMPATIBILITY IDEOGRAPH-2F858;Lo;0;L;58AC;;;;N;;;;;
+2F859;CJK COMPATIBILITY IDEOGRAPH-2F859;Lo;0;L;214E4;;;;N;;;;;
+2F85A;CJK COMPATIBILITY IDEOGRAPH-2F85A;Lo;0;L;58F2;;;;N;;;;;
+2F85B;CJK COMPATIBILITY IDEOGRAPH-2F85B;Lo;0;L;58F7;;;;N;;;;;
+2F85C;CJK COMPATIBILITY IDEOGRAPH-2F85C;Lo;0;L;5906;;;;N;;;;;
+2F85D;CJK COMPATIBILITY IDEOGRAPH-2F85D;Lo;0;L;591A;;;;N;;;;;
+2F85E;CJK COMPATIBILITY IDEOGRAPH-2F85E;Lo;0;L;5922;;;;N;;;;;
+2F85F;CJK COMPATIBILITY IDEOGRAPH-2F85F;Lo;0;L;5962;;;;N;;;;;
+2F860;CJK COMPATIBILITY IDEOGRAPH-2F860;Lo;0;L;216A8;;;;N;;;;;
+2F861;CJK COMPATIBILITY IDEOGRAPH-2F861;Lo;0;L;216EA;;;;N;;;;;
+2F862;CJK COMPATIBILITY IDEOGRAPH-2F862;Lo;0;L;59EC;;;;N;;;;;
+2F863;CJK COMPATIBILITY IDEOGRAPH-2F863;Lo;0;L;5A1B;;;;N;;;;;
+2F864;CJK COMPATIBILITY IDEOGRAPH-2F864;Lo;0;L;5A27;;;;N;;;;;
+2F865;CJK COMPATIBILITY IDEOGRAPH-2F865;Lo;0;L;59D8;;;;N;;;;;
+2F866;CJK COMPATIBILITY IDEOGRAPH-2F866;Lo;0;L;5A66;;;;N;;;;;
+2F867;CJK COMPATIBILITY IDEOGRAPH-2F867;Lo;0;L;36EE;;;;N;;;;;
+2F868;CJK COMPATIBILITY IDEOGRAPH-2F868;Lo;0;L;36FC;;;;N;;;;;
+2F869;CJK COMPATIBILITY IDEOGRAPH-2F869;Lo;0;L;5B08;;;;N;;;;;
+2F86A;CJK COMPATIBILITY IDEOGRAPH-2F86A;Lo;0;L;5B3E;;;;N;;;;;
+2F86B;CJK COMPATIBILITY IDEOGRAPH-2F86B;Lo;0;L;5B3E;;;;N;;;;;
+2F86C;CJK COMPATIBILITY IDEOGRAPH-2F86C;Lo;0;L;219C8;;;;N;;;;;
+2F86D;CJK COMPATIBILITY IDEOGRAPH-2F86D;Lo;0;L;5BC3;;;;N;;;;;
+2F86E;CJK COMPATIBILITY IDEOGRAPH-2F86E;Lo;0;L;5BD8;;;;N;;;;;
+2F86F;CJK COMPATIBILITY IDEOGRAPH-2F86F;Lo;0;L;5BE7;;;;N;;;;;
+2F870;CJK COMPATIBILITY IDEOGRAPH-2F870;Lo;0;L;5BF3;;;;N;;;;;
+2F871;CJK COMPATIBILITY IDEOGRAPH-2F871;Lo;0;L;21B18;;;;N;;;;;
+2F872;CJK COMPATIBILITY IDEOGRAPH-2F872;Lo;0;L;5BFF;;;;N;;;;;
+2F873;CJK COMPATIBILITY IDEOGRAPH-2F873;Lo;0;L;5C06;;;;N;;;;;
+2F874;CJK COMPATIBILITY IDEOGRAPH-2F874;Lo;0;L;5F53;;;;N;;;;;
+2F875;CJK COMPATIBILITY IDEOGRAPH-2F875;Lo;0;L;5C22;;;;N;;;;;
+2F876;CJK COMPATIBILITY IDEOGRAPH-2F876;Lo;0;L;3781;;;;N;;;;;
+2F877;CJK COMPATIBILITY IDEOGRAPH-2F877;Lo;0;L;5C60;;;;N;;;;;
+2F878;CJK COMPATIBILITY IDEOGRAPH-2F878;Lo;0;L;5C6E;;;;N;;;;;
+2F879;CJK COMPATIBILITY IDEOGRAPH-2F879;Lo;0;L;5CC0;;;;N;;;;;
+2F87A;CJK COMPATIBILITY IDEOGRAPH-2F87A;Lo;0;L;5C8D;;;;N;;;;;
+2F87B;CJK COMPATIBILITY IDEOGRAPH-2F87B;Lo;0;L;21DE4;;;;N;;;;;
+2F87C;CJK COMPATIBILITY IDEOGRAPH-2F87C;Lo;0;L;5D43;;;;N;;;;;
+2F87D;CJK COMPATIBILITY IDEOGRAPH-2F87D;Lo;0;L;21DE6;;;;N;;;;;
+2F87E;CJK COMPATIBILITY IDEOGRAPH-2F87E;Lo;0;L;5D6E;;;;N;;;;;
+2F87F;CJK COMPATIBILITY IDEOGRAPH-2F87F;Lo;0;L;5D6B;;;;N;;;;;
+2F880;CJK COMPATIBILITY IDEOGRAPH-2F880;Lo;0;L;5D7C;;;;N;;;;;
+2F881;CJK COMPATIBILITY IDEOGRAPH-2F881;Lo;0;L;5DE1;;;;N;;;;;
+2F882;CJK COMPATIBILITY IDEOGRAPH-2F882;Lo;0;L;5DE2;;;;N;;;;;
+2F883;CJK COMPATIBILITY IDEOGRAPH-2F883;Lo;0;L;382F;;;;N;;;;;
+2F884;CJK COMPATIBILITY IDEOGRAPH-2F884;Lo;0;L;5DFD;;;;N;;;;;
+2F885;CJK COMPATIBILITY IDEOGRAPH-2F885;Lo;0;L;5E28;;;;N;;;;;
+2F886;CJK COMPATIBILITY IDEOGRAPH-2F886;Lo;0;L;5E3D;;;;N;;;;;
+2F887;CJK COMPATIBILITY IDEOGRAPH-2F887;Lo;0;L;5E69;;;;N;;;;;
+2F888;CJK COMPATIBILITY IDEOGRAPH-2F888;Lo;0;L;3862;;;;N;;;;;
+2F889;CJK COMPATIBILITY IDEOGRAPH-2F889;Lo;0;L;22183;;;;N;;;;;
+2F88A;CJK COMPATIBILITY IDEOGRAPH-2F88A;Lo;0;L;387C;;;;N;;;;;
+2F88B;CJK COMPATIBILITY IDEOGRAPH-2F88B;Lo;0;L;5EB0;;;;N;;;;;
+2F88C;CJK COMPATIBILITY IDEOGRAPH-2F88C;Lo;0;L;5EB3;;;;N;;;;;
+2F88D;CJK COMPATIBILITY IDEOGRAPH-2F88D;Lo;0;L;5EB6;;;;N;;;;;
+2F88E;CJK COMPATIBILITY IDEOGRAPH-2F88E;Lo;0;L;5ECA;;;;N;;;;;
+2F88F;CJK COMPATIBILITY IDEOGRAPH-2F88F;Lo;0;L;2A392;;;;N;;;;;
+2F890;CJK COMPATIBILITY IDEOGRAPH-2F890;Lo;0;L;5EFE;;;9;N;;;;;
+2F891;CJK COMPATIBILITY IDEOGRAPH-2F891;Lo;0;L;22331;;;;N;;;;;
+2F892;CJK COMPATIBILITY IDEOGRAPH-2F892;Lo;0;L;22331;;;;N;;;;;
+2F893;CJK COMPATIBILITY IDEOGRAPH-2F893;Lo;0;L;8201;;;;N;;;;;
+2F894;CJK COMPATIBILITY IDEOGRAPH-2F894;Lo;0;L;5F22;;;;N;;;;;
+2F895;CJK COMPATIBILITY IDEOGRAPH-2F895;Lo;0;L;5F22;;;;N;;;;;
+2F896;CJK COMPATIBILITY IDEOGRAPH-2F896;Lo;0;L;38C7;;;;N;;;;;
+2F897;CJK COMPATIBILITY IDEOGRAPH-2F897;Lo;0;L;232B8;;;;N;;;;;
+2F898;CJK COMPATIBILITY IDEOGRAPH-2F898;Lo;0;L;261DA;;;;N;;;;;
+2F899;CJK COMPATIBILITY IDEOGRAPH-2F899;Lo;0;L;5F62;;;;N;;;;;
+2F89A;CJK COMPATIBILITY IDEOGRAPH-2F89A;Lo;0;L;5F6B;;;;N;;;;;
+2F89B;CJK COMPATIBILITY IDEOGRAPH-2F89B;Lo;0;L;38E3;;;;N;;;;;
+2F89C;CJK COMPATIBILITY IDEOGRAPH-2F89C;Lo;0;L;5F9A;;;;N;;;;;
+2F89D;CJK COMPATIBILITY IDEOGRAPH-2F89D;Lo;0;L;5FCD;;;;N;;;;;
+2F89E;CJK COMPATIBILITY IDEOGRAPH-2F89E;Lo;0;L;5FD7;;;;N;;;;;
+2F89F;CJK COMPATIBILITY IDEOGRAPH-2F89F;Lo;0;L;5FF9;;;;N;;;;;
+2F8A0;CJK COMPATIBILITY IDEOGRAPH-2F8A0;Lo;0;L;6081;;;;N;;;;;
+2F8A1;CJK COMPATIBILITY IDEOGRAPH-2F8A1;Lo;0;L;393A;;;;N;;;;;
+2F8A2;CJK COMPATIBILITY IDEOGRAPH-2F8A2;Lo;0;L;391C;;;;N;;;;;
+2F8A3;CJK COMPATIBILITY IDEOGRAPH-2F8A3;Lo;0;L;6094;;;;N;;;;;
+2F8A4;CJK COMPATIBILITY IDEOGRAPH-2F8A4;Lo;0;L;226D4;;;;N;;;;;
+2F8A5;CJK COMPATIBILITY IDEOGRAPH-2F8A5;Lo;0;L;60C7;;;;N;;;;;
+2F8A6;CJK COMPATIBILITY IDEOGRAPH-2F8A6;Lo;0;L;6148;;;;N;;;;;
+2F8A7;CJK COMPATIBILITY IDEOGRAPH-2F8A7;Lo;0;L;614C;;;;N;;;;;
+2F8A8;CJK COMPATIBILITY IDEOGRAPH-2F8A8;Lo;0;L;614E;;;;N;;;;;
+2F8A9;CJK COMPATIBILITY IDEOGRAPH-2F8A9;Lo;0;L;614C;;;;N;;;;;
+2F8AA;CJK COMPATIBILITY IDEOGRAPH-2F8AA;Lo;0;L;617A;;;;N;;;;;
+2F8AB;CJK COMPATIBILITY IDEOGRAPH-2F8AB;Lo;0;L;618E;;;;N;;;;;
+2F8AC;CJK COMPATIBILITY IDEOGRAPH-2F8AC;Lo;0;L;61B2;;;;N;;;;;
+2F8AD;CJK COMPATIBILITY IDEOGRAPH-2F8AD;Lo;0;L;61A4;;;;N;;;;;
+2F8AE;CJK COMPATIBILITY IDEOGRAPH-2F8AE;Lo;0;L;61AF;;;;N;;;;;
+2F8AF;CJK COMPATIBILITY IDEOGRAPH-2F8AF;Lo;0;L;61DE;;;;N;;;;;
+2F8B0;CJK COMPATIBILITY IDEOGRAPH-2F8B0;Lo;0;L;61F2;;;;N;;;;;
+2F8B1;CJK COMPATIBILITY IDEOGRAPH-2F8B1;Lo;0;L;61F6;;;;N;;;;;
+2F8B2;CJK COMPATIBILITY IDEOGRAPH-2F8B2;Lo;0;L;6210;;;;N;;;;;
+2F8B3;CJK COMPATIBILITY IDEOGRAPH-2F8B3;Lo;0;L;621B;;;;N;;;;;
+2F8B4;CJK COMPATIBILITY IDEOGRAPH-2F8B4;Lo;0;L;625D;;;;N;;;;;
+2F8B5;CJK COMPATIBILITY IDEOGRAPH-2F8B5;Lo;0;L;62B1;;;;N;;;;;
+2F8B6;CJK COMPATIBILITY IDEOGRAPH-2F8B6;Lo;0;L;62D4;;;;N;;;;;
+2F8B7;CJK COMPATIBILITY IDEOGRAPH-2F8B7;Lo;0;L;6350;;;;N;;;;;
+2F8B8;CJK COMPATIBILITY IDEOGRAPH-2F8B8;Lo;0;L;22B0C;;;;N;;;;;
+2F8B9;CJK COMPATIBILITY IDEOGRAPH-2F8B9;Lo;0;L;633D;;;;N;;;;;
+2F8BA;CJK COMPATIBILITY IDEOGRAPH-2F8BA;Lo;0;L;62FC;;;;N;;;;;
+2F8BB;CJK COMPATIBILITY IDEOGRAPH-2F8BB;Lo;0;L;6368;;;;N;;;;;
+2F8BC;CJK COMPATIBILITY IDEOGRAPH-2F8BC;Lo;0;L;6383;;;;N;;;;;
+2F8BD;CJK COMPATIBILITY IDEOGRAPH-2F8BD;Lo;0;L;63E4;;;;N;;;;;
+2F8BE;CJK COMPATIBILITY IDEOGRAPH-2F8BE;Lo;0;L;22BF1;;;;N;;;;;
+2F8BF;CJK COMPATIBILITY IDEOGRAPH-2F8BF;Lo;0;L;6422;;;;N;;;;;
+2F8C0;CJK COMPATIBILITY IDEOGRAPH-2F8C0;Lo;0;L;63C5;;;;N;;;;;
+2F8C1;CJK COMPATIBILITY IDEOGRAPH-2F8C1;Lo;0;L;63A9;;;;N;;;;;
+2F8C2;CJK COMPATIBILITY IDEOGRAPH-2F8C2;Lo;0;L;3A2E;;;;N;;;;;
+2F8C3;CJK COMPATIBILITY IDEOGRAPH-2F8C3;Lo;0;L;6469;;;;N;;;;;
+2F8C4;CJK COMPATIBILITY IDEOGRAPH-2F8C4;Lo;0;L;647E;;;;N;;;;;
+2F8C5;CJK COMPATIBILITY IDEOGRAPH-2F8C5;Lo;0;L;649D;;;;N;;;;;
+2F8C6;CJK COMPATIBILITY IDEOGRAPH-2F8C6;Lo;0;L;6477;;;;N;;;;;
+2F8C7;CJK COMPATIBILITY IDEOGRAPH-2F8C7;Lo;0;L;3A6C;;;;N;;;;;
+2F8C8;CJK COMPATIBILITY IDEOGRAPH-2F8C8;Lo;0;L;654F;;;;N;;;;;
+2F8C9;CJK COMPATIBILITY IDEOGRAPH-2F8C9;Lo;0;L;656C;;;;N;;;;;
+2F8CA;CJK COMPATIBILITY IDEOGRAPH-2F8CA;Lo;0;L;2300A;;;;N;;;;;
+2F8CB;CJK COMPATIBILITY IDEOGRAPH-2F8CB;Lo;0;L;65E3;;;;N;;;;;
+2F8CC;CJK COMPATIBILITY IDEOGRAPH-2F8CC;Lo;0;L;66F8;;;;N;;;;;
+2F8CD;CJK COMPATIBILITY IDEOGRAPH-2F8CD;Lo;0;L;6649;;;;N;;;;;
+2F8CE;CJK COMPATIBILITY IDEOGRAPH-2F8CE;Lo;0;L;3B19;;;;N;;;;;
+2F8CF;CJK COMPATIBILITY IDEOGRAPH-2F8CF;Lo;0;L;6691;;;;N;;;;;
+2F8D0;CJK COMPATIBILITY IDEOGRAPH-2F8D0;Lo;0;L;3B08;;;;N;;;;;
+2F8D1;CJK COMPATIBILITY IDEOGRAPH-2F8D1;Lo;0;L;3AE4;;;;N;;;;;
+2F8D2;CJK COMPATIBILITY IDEOGRAPH-2F8D2;Lo;0;L;5192;;;;N;;;;;
+2F8D3;CJK COMPATIBILITY IDEOGRAPH-2F8D3;Lo;0;L;5195;;;;N;;;;;
+2F8D4;CJK COMPATIBILITY IDEOGRAPH-2F8D4;Lo;0;L;6700;;;;N;;;;;
+2F8D5;CJK COMPATIBILITY IDEOGRAPH-2F8D5;Lo;0;L;669C;;;;N;;;;;
+2F8D6;CJK COMPATIBILITY IDEOGRAPH-2F8D6;Lo;0;L;80AD;;;;N;;;;;
+2F8D7;CJK COMPATIBILITY IDEOGRAPH-2F8D7;Lo;0;L;43D9;;;;N;;;;;
+2F8D8;CJK COMPATIBILITY IDEOGRAPH-2F8D8;Lo;0;L;6717;;;;N;;;;;
+2F8D9;CJK COMPATIBILITY IDEOGRAPH-2F8D9;Lo;0;L;671B;;;;N;;;;;
+2F8DA;CJK COMPATIBILITY IDEOGRAPH-2F8DA;Lo;0;L;6721;;;;N;;;;;
+2F8DB;CJK COMPATIBILITY IDEOGRAPH-2F8DB;Lo;0;L;675E;;;;N;;;;;
+2F8DC;CJK COMPATIBILITY IDEOGRAPH-2F8DC;Lo;0;L;6753;;;;N;;;;;
+2F8DD;CJK COMPATIBILITY IDEOGRAPH-2F8DD;Lo;0;L;233C3;;;;N;;;;;
+2F8DE;CJK COMPATIBILITY IDEOGRAPH-2F8DE;Lo;0;L;3B49;;;;N;;;;;
+2F8DF;CJK COMPATIBILITY IDEOGRAPH-2F8DF;Lo;0;L;67FA;;;;N;;;;;
+2F8E0;CJK COMPATIBILITY IDEOGRAPH-2F8E0;Lo;0;L;6785;;;;N;;;;;
+2F8E1;CJK COMPATIBILITY IDEOGRAPH-2F8E1;Lo;0;L;6852;;;;N;;;;;
+2F8E2;CJK COMPATIBILITY IDEOGRAPH-2F8E2;Lo;0;L;6885;;;;N;;;;;
+2F8E3;CJK COMPATIBILITY IDEOGRAPH-2F8E3;Lo;0;L;2346D;;;;N;;;;;
+2F8E4;CJK COMPATIBILITY IDEOGRAPH-2F8E4;Lo;0;L;688E;;;;N;;;;;
+2F8E5;CJK COMPATIBILITY IDEOGRAPH-2F8E5;Lo;0;L;681F;;;;N;;;;;
+2F8E6;CJK COMPATIBILITY IDEOGRAPH-2F8E6;Lo;0;L;6914;;;;N;;;;;
+2F8E7;CJK COMPATIBILITY IDEOGRAPH-2F8E7;Lo;0;L;3B9D;;;;N;;;;;
+2F8E8;CJK COMPATIBILITY IDEOGRAPH-2F8E8;Lo;0;L;6942;;;;N;;;;;
+2F8E9;CJK COMPATIBILITY IDEOGRAPH-2F8E9;Lo;0;L;69A3;;;;N;;;;;
+2F8EA;CJK COMPATIBILITY IDEOGRAPH-2F8EA;Lo;0;L;69EA;;;;N;;;;;
+2F8EB;CJK COMPATIBILITY IDEOGRAPH-2F8EB;Lo;0;L;6AA8;;;;N;;;;;
+2F8EC;CJK COMPATIBILITY IDEOGRAPH-2F8EC;Lo;0;L;236A3;;;;N;;;;;
+2F8ED;CJK COMPATIBILITY IDEOGRAPH-2F8ED;Lo;0;L;6ADB;;;;N;;;;;
+2F8EE;CJK COMPATIBILITY IDEOGRAPH-2F8EE;Lo;0;L;3C18;;;;N;;;;;
+2F8EF;CJK COMPATIBILITY IDEOGRAPH-2F8EF;Lo;0;L;6B21;;;;N;;;;;
+2F8F0;CJK COMPATIBILITY IDEOGRAPH-2F8F0;Lo;0;L;238A7;;;;N;;;;;
+2F8F1;CJK COMPATIBILITY IDEOGRAPH-2F8F1;Lo;0;L;6B54;;;;N;;;;;
+2F8F2;CJK COMPATIBILITY IDEOGRAPH-2F8F2;Lo;0;L;3C4E;;;;N;;;;;
+2F8F3;CJK COMPATIBILITY IDEOGRAPH-2F8F3;Lo;0;L;6B72;;;;N;;;;;
+2F8F4;CJK COMPATIBILITY IDEOGRAPH-2F8F4;Lo;0;L;6B9F;;;;N;;;;;
+2F8F5;CJK COMPATIBILITY IDEOGRAPH-2F8F5;Lo;0;L;6BBA;;;;N;;;;;
+2F8F6;CJK COMPATIBILITY IDEOGRAPH-2F8F6;Lo;0;L;6BBB;;;;N;;;;;
+2F8F7;CJK COMPATIBILITY IDEOGRAPH-2F8F7;Lo;0;L;23A8D;;;;N;;;;;
+2F8F8;CJK COMPATIBILITY IDEOGRAPH-2F8F8;Lo;0;L;21D0B;;;;N;;;;;
+2F8F9;CJK COMPATIBILITY IDEOGRAPH-2F8F9;Lo;0;L;23AFA;;;;N;;;;;
+2F8FA;CJK COMPATIBILITY IDEOGRAPH-2F8FA;Lo;0;L;6C4E;;;;N;;;;;
+2F8FB;CJK COMPATIBILITY IDEOGRAPH-2F8FB;Lo;0;L;23CBC;;;;N;;;;;
+2F8FC;CJK COMPATIBILITY IDEOGRAPH-2F8FC;Lo;0;L;6CBF;;;;N;;;;;
+2F8FD;CJK COMPATIBILITY IDEOGRAPH-2F8FD;Lo;0;L;6CCD;;;;N;;;;;
+2F8FE;CJK COMPATIBILITY IDEOGRAPH-2F8FE;Lo;0;L;6C67;;;;N;;;;;
+2F8FF;CJK COMPATIBILITY IDEOGRAPH-2F8FF;Lo;0;L;6D16;;;;N;;;;;
+2F900;CJK COMPATIBILITY IDEOGRAPH-2F900;Lo;0;L;6D3E;;;;N;;;;;
+2F901;CJK COMPATIBILITY IDEOGRAPH-2F901;Lo;0;L;6D77;;;;N;;;;;
+2F902;CJK COMPATIBILITY IDEOGRAPH-2F902;Lo;0;L;6D41;;;;N;;;;;
+2F903;CJK COMPATIBILITY IDEOGRAPH-2F903;Lo;0;L;6D69;;;;N;;;;;
+2F904;CJK COMPATIBILITY IDEOGRAPH-2F904;Lo;0;L;6D78;;;;N;;;;;
+2F905;CJK COMPATIBILITY IDEOGRAPH-2F905;Lo;0;L;6D85;;;;N;;;;;
+2F906;CJK COMPATIBILITY IDEOGRAPH-2F906;Lo;0;L;23D1E;;;;N;;;;;
+2F907;CJK COMPATIBILITY IDEOGRAPH-2F907;Lo;0;L;6D34;;;;N;;;;;
+2F908;CJK COMPATIBILITY IDEOGRAPH-2F908;Lo;0;L;6E2F;;;;N;;;;;
+2F909;CJK COMPATIBILITY IDEOGRAPH-2F909;Lo;0;L;6E6E;;;;N;;;;;
+2F90A;CJK COMPATIBILITY IDEOGRAPH-2F90A;Lo;0;L;3D33;;;;N;;;;;
+2F90B;CJK COMPATIBILITY IDEOGRAPH-2F90B;Lo;0;L;6ECB;;;;N;;;;;
+2F90C;CJK COMPATIBILITY IDEOGRAPH-2F90C;Lo;0;L;6EC7;;;;N;;;;;
+2F90D;CJK COMPATIBILITY IDEOGRAPH-2F90D;Lo;0;L;23ED1;;;;N;;;;;
+2F90E;CJK COMPATIBILITY IDEOGRAPH-2F90E;Lo;0;L;6DF9;;;;N;;;;;
+2F90F;CJK COMPATIBILITY IDEOGRAPH-2F90F;Lo;0;L;6F6E;;;;N;;;;;
+2F910;CJK COMPATIBILITY IDEOGRAPH-2F910;Lo;0;L;23F5E;;;;N;;;;;
+2F911;CJK COMPATIBILITY IDEOGRAPH-2F911;Lo;0;L;23F8E;;;;N;;;;;
+2F912;CJK COMPATIBILITY IDEOGRAPH-2F912;Lo;0;L;6FC6;;;;N;;;;;
+2F913;CJK COMPATIBILITY IDEOGRAPH-2F913;Lo;0;L;7039;;;;N;;;;;
+2F914;CJK COMPATIBILITY IDEOGRAPH-2F914;Lo;0;L;701E;;;;N;;;;;
+2F915;CJK COMPATIBILITY IDEOGRAPH-2F915;Lo;0;L;701B;;;;N;;;;;
+2F916;CJK COMPATIBILITY IDEOGRAPH-2F916;Lo;0;L;3D96;;;;N;;;;;
+2F917;CJK COMPATIBILITY IDEOGRAPH-2F917;Lo;0;L;704A;;;;N;;;;;
+2F918;CJK COMPATIBILITY IDEOGRAPH-2F918;Lo;0;L;707D;;;;N;;;;;
+2F919;CJK COMPATIBILITY IDEOGRAPH-2F919;Lo;0;L;7077;;;;N;;;;;
+2F91A;CJK COMPATIBILITY IDEOGRAPH-2F91A;Lo;0;L;70AD;;;;N;;;;;
+2F91B;CJK COMPATIBILITY IDEOGRAPH-2F91B;Lo;0;L;20525;;;;N;;;;;
+2F91C;CJK COMPATIBILITY IDEOGRAPH-2F91C;Lo;0;L;7145;;;;N;;;;;
+2F91D;CJK COMPATIBILITY IDEOGRAPH-2F91D;Lo;0;L;24263;;;;N;;;;;
+2F91E;CJK COMPATIBILITY IDEOGRAPH-2F91E;Lo;0;L;719C;;;;N;;;;;
+2F91F;CJK COMPATIBILITY IDEOGRAPH-2F91F;Lo;0;L;243AB;;;;N;;;;;
+2F920;CJK COMPATIBILITY IDEOGRAPH-2F920;Lo;0;L;7228;;;;N;;;;;
+2F921;CJK COMPATIBILITY IDEOGRAPH-2F921;Lo;0;L;7235;;;;N;;;;;
+2F922;CJK COMPATIBILITY IDEOGRAPH-2F922;Lo;0;L;7250;;;;N;;;;;
+2F923;CJK COMPATIBILITY IDEOGRAPH-2F923;Lo;0;L;24608;;;;N;;;;;
+2F924;CJK COMPATIBILITY IDEOGRAPH-2F924;Lo;0;L;7280;;;;N;;;;;
+2F925;CJK COMPATIBILITY IDEOGRAPH-2F925;Lo;0;L;7295;;;;N;;;;;
+2F926;CJK COMPATIBILITY IDEOGRAPH-2F926;Lo;0;L;24735;;;;N;;;;;
+2F927;CJK COMPATIBILITY IDEOGRAPH-2F927;Lo;0;L;24814;;;;N;;;;;
+2F928;CJK COMPATIBILITY IDEOGRAPH-2F928;Lo;0;L;737A;;;;N;;;;;
+2F929;CJK COMPATIBILITY IDEOGRAPH-2F929;Lo;0;L;738B;;;;N;;;;;
+2F92A;CJK COMPATIBILITY IDEOGRAPH-2F92A;Lo;0;L;3EAC;;;;N;;;;;
+2F92B;CJK COMPATIBILITY IDEOGRAPH-2F92B;Lo;0;L;73A5;;;;N;;;;;
+2F92C;CJK COMPATIBILITY IDEOGRAPH-2F92C;Lo;0;L;3EB8;;;;N;;;;;
+2F92D;CJK COMPATIBILITY IDEOGRAPH-2F92D;Lo;0;L;3EB8;;;;N;;;;;
+2F92E;CJK COMPATIBILITY IDEOGRAPH-2F92E;Lo;0;L;7447;;;;N;;;;;
+2F92F;CJK COMPATIBILITY IDEOGRAPH-2F92F;Lo;0;L;745C;;;;N;;;;;
+2F930;CJK COMPATIBILITY IDEOGRAPH-2F930;Lo;0;L;7471;;;;N;;;;;
+2F931;CJK COMPATIBILITY IDEOGRAPH-2F931;Lo;0;L;7485;;;;N;;;;;
+2F932;CJK COMPATIBILITY IDEOGRAPH-2F932;Lo;0;L;74CA;;;;N;;;;;
+2F933;CJK COMPATIBILITY IDEOGRAPH-2F933;Lo;0;L;3F1B;;;;N;;;;;
+2F934;CJK COMPATIBILITY IDEOGRAPH-2F934;Lo;0;L;7524;;;;N;;;;;
+2F935;CJK COMPATIBILITY IDEOGRAPH-2F935;Lo;0;L;24C36;;;;N;;;;;
+2F936;CJK COMPATIBILITY IDEOGRAPH-2F936;Lo;0;L;753E;;;;N;;;;;
+2F937;CJK COMPATIBILITY IDEOGRAPH-2F937;Lo;0;L;24C92;;;;N;;;;;
+2F938;CJK COMPATIBILITY IDEOGRAPH-2F938;Lo;0;L;7570;;;;N;;;;;
+2F939;CJK COMPATIBILITY IDEOGRAPH-2F939;Lo;0;L;2219F;;;;N;;;;;
+2F93A;CJK COMPATIBILITY IDEOGRAPH-2F93A;Lo;0;L;7610;;;;N;;;;;
+2F93B;CJK COMPATIBILITY IDEOGRAPH-2F93B;Lo;0;L;24FA1;;;;N;;;;;
+2F93C;CJK COMPATIBILITY IDEOGRAPH-2F93C;Lo;0;L;24FB8;;;;N;;;;;
+2F93D;CJK COMPATIBILITY IDEOGRAPH-2F93D;Lo;0;L;25044;;;;N;;;;;
+2F93E;CJK COMPATIBILITY IDEOGRAPH-2F93E;Lo;0;L;3FFC;;;;N;;;;;
+2F93F;CJK COMPATIBILITY IDEOGRAPH-2F93F;Lo;0;L;4008;;;;N;;;;;
+2F940;CJK COMPATIBILITY IDEOGRAPH-2F940;Lo;0;L;76F4;;;;N;;;;;
+2F941;CJK COMPATIBILITY IDEOGRAPH-2F941;Lo;0;L;250F3;;;;N;;;;;
+2F942;CJK COMPATIBILITY IDEOGRAPH-2F942;Lo;0;L;250F2;;;;N;;;;;
+2F943;CJK COMPATIBILITY IDEOGRAPH-2F943;Lo;0;L;25119;;;;N;;;;;
+2F944;CJK COMPATIBILITY IDEOGRAPH-2F944;Lo;0;L;25133;;;;N;;;;;
+2F945;CJK COMPATIBILITY IDEOGRAPH-2F945;Lo;0;L;771E;;;;N;;;;;
+2F946;CJK COMPATIBILITY IDEOGRAPH-2F946;Lo;0;L;771F;;;;N;;;;;
+2F947;CJK COMPATIBILITY IDEOGRAPH-2F947;Lo;0;L;771F;;;;N;;;;;
+2F948;CJK COMPATIBILITY IDEOGRAPH-2F948;Lo;0;L;774A;;;;N;;;;;
+2F949;CJK COMPATIBILITY IDEOGRAPH-2F949;Lo;0;L;4039;;;;N;;;;;
+2F94A;CJK COMPATIBILITY IDEOGRAPH-2F94A;Lo;0;L;778B;;;;N;;;;;
+2F94B;CJK COMPATIBILITY IDEOGRAPH-2F94B;Lo;0;L;4046;;;;N;;;;;
+2F94C;CJK COMPATIBILITY IDEOGRAPH-2F94C;Lo;0;L;4096;;;;N;;;;;
+2F94D;CJK COMPATIBILITY IDEOGRAPH-2F94D;Lo;0;L;2541D;;;;N;;;;;
+2F94E;CJK COMPATIBILITY IDEOGRAPH-2F94E;Lo;0;L;784E;;;;N;;;;;
+2F94F;CJK COMPATIBILITY IDEOGRAPH-2F94F;Lo;0;L;788C;;;;N;;;;;
+2F950;CJK COMPATIBILITY IDEOGRAPH-2F950;Lo;0;L;78CC;;;;N;;;;;
+2F951;CJK COMPATIBILITY IDEOGRAPH-2F951;Lo;0;L;40E3;;;;N;;;;;
+2F952;CJK COMPATIBILITY IDEOGRAPH-2F952;Lo;0;L;25626;;;;N;;;;;
+2F953;CJK COMPATIBILITY IDEOGRAPH-2F953;Lo;0;L;7956;;;;N;;;;;
+2F954;CJK COMPATIBILITY IDEOGRAPH-2F954;Lo;0;L;2569A;;;;N;;;;;
+2F955;CJK COMPATIBILITY IDEOGRAPH-2F955;Lo;0;L;256C5;;;;N;;;;;
+2F956;CJK COMPATIBILITY IDEOGRAPH-2F956;Lo;0;L;798F;;;;N;;;;;
+2F957;CJK COMPATIBILITY IDEOGRAPH-2F957;Lo;0;L;79EB;;;;N;;;;;
+2F958;CJK COMPATIBILITY IDEOGRAPH-2F958;Lo;0;L;412F;;;;N;;;;;
+2F959;CJK COMPATIBILITY IDEOGRAPH-2F959;Lo;0;L;7A40;;;;N;;;;;
+2F95A;CJK COMPATIBILITY IDEOGRAPH-2F95A;Lo;0;L;7A4A;;;;N;;;;;
+2F95B;CJK COMPATIBILITY IDEOGRAPH-2F95B;Lo;0;L;7A4F;;;;N;;;;;
+2F95C;CJK COMPATIBILITY IDEOGRAPH-2F95C;Lo;0;L;2597C;;;;N;;;;;
+2F95D;CJK COMPATIBILITY IDEOGRAPH-2F95D;Lo;0;L;25AA7;;;;N;;;;;
+2F95E;CJK COMPATIBILITY IDEOGRAPH-2F95E;Lo;0;L;25AA7;;;;N;;;;;
+2F95F;CJK COMPATIBILITY IDEOGRAPH-2F95F;Lo;0;L;7AEE;;;;N;;;;;
+2F960;CJK COMPATIBILITY IDEOGRAPH-2F960;Lo;0;L;4202;;;;N;;;;;
+2F961;CJK COMPATIBILITY IDEOGRAPH-2F961;Lo;0;L;25BAB;;;;N;;;;;
+2F962;CJK COMPATIBILITY IDEOGRAPH-2F962;Lo;0;L;7BC6;;;;N;;;;;
+2F963;CJK COMPATIBILITY IDEOGRAPH-2F963;Lo;0;L;7BC9;;;;N;;;;;
+2F964;CJK COMPATIBILITY IDEOGRAPH-2F964;Lo;0;L;4227;;;;N;;;;;
+2F965;CJK COMPATIBILITY IDEOGRAPH-2F965;Lo;0;L;25C80;;;;N;;;;;
+2F966;CJK COMPATIBILITY IDEOGRAPH-2F966;Lo;0;L;7CD2;;;;N;;;;;
+2F967;CJK COMPATIBILITY IDEOGRAPH-2F967;Lo;0;L;42A0;;;;N;;;;;
+2F968;CJK COMPATIBILITY IDEOGRAPH-2F968;Lo;0;L;7CE8;;;;N;;;;;
+2F969;CJK COMPATIBILITY IDEOGRAPH-2F969;Lo;0;L;7CE3;;;;N;;;;;
+2F96A;CJK COMPATIBILITY IDEOGRAPH-2F96A;Lo;0;L;7D00;;;;N;;;;;
+2F96B;CJK COMPATIBILITY IDEOGRAPH-2F96B;Lo;0;L;25F86;;;;N;;;;;
+2F96C;CJK COMPATIBILITY IDEOGRAPH-2F96C;Lo;0;L;7D63;;;;N;;;;;
+2F96D;CJK COMPATIBILITY IDEOGRAPH-2F96D;Lo;0;L;4301;;;;N;;;;;
+2F96E;CJK COMPATIBILITY IDEOGRAPH-2F96E;Lo;0;L;7DC7;;;;N;;;;;
+2F96F;CJK COMPATIBILITY IDEOGRAPH-2F96F;Lo;0;L;7E02;;;;N;;;;;
+2F970;CJK COMPATIBILITY IDEOGRAPH-2F970;Lo;0;L;7E45;;;;N;;;;;
+2F971;CJK COMPATIBILITY IDEOGRAPH-2F971;Lo;0;L;4334;;;;N;;;;;
+2F972;CJK COMPATIBILITY IDEOGRAPH-2F972;Lo;0;L;26228;;;;N;;;;;
+2F973;CJK COMPATIBILITY IDEOGRAPH-2F973;Lo;0;L;26247;;;;N;;;;;
+2F974;CJK COMPATIBILITY IDEOGRAPH-2F974;Lo;0;L;4359;;;;N;;;;;
+2F975;CJK COMPATIBILITY IDEOGRAPH-2F975;Lo;0;L;262D9;;;;N;;;;;
+2F976;CJK COMPATIBILITY IDEOGRAPH-2F976;Lo;0;L;7F7A;;;;N;;;;;
+2F977;CJK COMPATIBILITY IDEOGRAPH-2F977;Lo;0;L;2633E;;;;N;;;;;
+2F978;CJK COMPATIBILITY IDEOGRAPH-2F978;Lo;0;L;7F95;;;;N;;;;;
+2F979;CJK COMPATIBILITY IDEOGRAPH-2F979;Lo;0;L;7FFA;;;;N;;;;;
+2F97A;CJK COMPATIBILITY IDEOGRAPH-2F97A;Lo;0;L;8005;;;;N;;;;;
+2F97B;CJK COMPATIBILITY IDEOGRAPH-2F97B;Lo;0;L;264DA;;;;N;;;;;
+2F97C;CJK COMPATIBILITY IDEOGRAPH-2F97C;Lo;0;L;26523;;;;N;;;;;
+2F97D;CJK COMPATIBILITY IDEOGRAPH-2F97D;Lo;0;L;8060;;;;N;;;;;
+2F97E;CJK COMPATIBILITY IDEOGRAPH-2F97E;Lo;0;L;265A8;;;;N;;;;;
+2F97F;CJK COMPATIBILITY IDEOGRAPH-2F97F;Lo;0;L;8070;;;;N;;;;;
+2F980;CJK COMPATIBILITY IDEOGRAPH-2F980;Lo;0;L;2335F;;;;N;;;;;
+2F981;CJK COMPATIBILITY IDEOGRAPH-2F981;Lo;0;L;43D5;;;;N;;;;;
+2F982;CJK COMPATIBILITY IDEOGRAPH-2F982;Lo;0;L;80B2;;;;N;;;;;
+2F983;CJK COMPATIBILITY IDEOGRAPH-2F983;Lo;0;L;8103;;;;N;;;;;
+2F984;CJK COMPATIBILITY IDEOGRAPH-2F984;Lo;0;L;440B;;;;N;;;;;
+2F985;CJK COMPATIBILITY IDEOGRAPH-2F985;Lo;0;L;813E;;;;N;;;;;
+2F986;CJK COMPATIBILITY IDEOGRAPH-2F986;Lo;0;L;5AB5;;;;N;;;;;
+2F987;CJK COMPATIBILITY IDEOGRAPH-2F987;Lo;0;L;267A7;;;;N;;;;;
+2F988;CJK COMPATIBILITY IDEOGRAPH-2F988;Lo;0;L;267B5;;;;N;;;;;
+2F989;CJK COMPATIBILITY IDEOGRAPH-2F989;Lo;0;L;23393;;;;N;;;;;
+2F98A;CJK COMPATIBILITY IDEOGRAPH-2F98A;Lo;0;L;2339C;;;;N;;;;;
+2F98B;CJK COMPATIBILITY IDEOGRAPH-2F98B;Lo;0;L;8201;;;;N;;;;;
+2F98C;CJK COMPATIBILITY IDEOGRAPH-2F98C;Lo;0;L;8204;;;;N;;;;;
+2F98D;CJK COMPATIBILITY IDEOGRAPH-2F98D;Lo;0;L;8F9E;;;;N;;;;;
+2F98E;CJK COMPATIBILITY IDEOGRAPH-2F98E;Lo;0;L;446B;;;;N;;;;;
+2F98F;CJK COMPATIBILITY IDEOGRAPH-2F98F;Lo;0;L;8291;;;;N;;;;;
+2F990;CJK COMPATIBILITY IDEOGRAPH-2F990;Lo;0;L;828B;;;;N;;;;;
+2F991;CJK COMPATIBILITY IDEOGRAPH-2F991;Lo;0;L;829D;;;;N;;;;;
+2F992;CJK COMPATIBILITY IDEOGRAPH-2F992;Lo;0;L;52B3;;;;N;;;;;
+2F993;CJK COMPATIBILITY IDEOGRAPH-2F993;Lo;0;L;82B1;;;;N;;;;;
+2F994;CJK COMPATIBILITY IDEOGRAPH-2F994;Lo;0;L;82B3;;;;N;;;;;
+2F995;CJK COMPATIBILITY IDEOGRAPH-2F995;Lo;0;L;82BD;;;;N;;;;;
+2F996;CJK COMPATIBILITY IDEOGRAPH-2F996;Lo;0;L;82E6;;;;N;;;;;
+2F997;CJK COMPATIBILITY IDEOGRAPH-2F997;Lo;0;L;26B3C;;;;N;;;;;
+2F998;CJK COMPATIBILITY IDEOGRAPH-2F998;Lo;0;L;82E5;;;;N;;;;;
+2F999;CJK COMPATIBILITY IDEOGRAPH-2F999;Lo;0;L;831D;;;;N;;;;;
+2F99A;CJK COMPATIBILITY IDEOGRAPH-2F99A;Lo;0;L;8363;;;;N;;;;;
+2F99B;CJK COMPATIBILITY IDEOGRAPH-2F99B;Lo;0;L;83AD;;;;N;;;;;
+2F99C;CJK COMPATIBILITY IDEOGRAPH-2F99C;Lo;0;L;8323;;;;N;;;;;
+2F99D;CJK COMPATIBILITY IDEOGRAPH-2F99D;Lo;0;L;83BD;;;;N;;;;;
+2F99E;CJK COMPATIBILITY IDEOGRAPH-2F99E;Lo;0;L;83E7;;;;N;;;;;
+2F99F;CJK COMPATIBILITY IDEOGRAPH-2F99F;Lo;0;L;8457;;;;N;;;;;
+2F9A0;CJK COMPATIBILITY IDEOGRAPH-2F9A0;Lo;0;L;8353;;;;N;;;;;
+2F9A1;CJK COMPATIBILITY IDEOGRAPH-2F9A1;Lo;0;L;83CA;;;;N;;;;;
+2F9A2;CJK COMPATIBILITY IDEOGRAPH-2F9A2;Lo;0;L;83CC;;;;N;;;;;
+2F9A3;CJK COMPATIBILITY IDEOGRAPH-2F9A3;Lo;0;L;83DC;;;;N;;;;;
+2F9A4;CJK COMPATIBILITY IDEOGRAPH-2F9A4;Lo;0;L;26C36;;;;N;;;;;
+2F9A5;CJK COMPATIBILITY IDEOGRAPH-2F9A5;Lo;0;L;26D6B;;;;N;;;;;
+2F9A6;CJK COMPATIBILITY IDEOGRAPH-2F9A6;Lo;0;L;26CD5;;;;N;;;;;
+2F9A7;CJK COMPATIBILITY IDEOGRAPH-2F9A7;Lo;0;L;452B;;;;N;;;;;
+2F9A8;CJK COMPATIBILITY IDEOGRAPH-2F9A8;Lo;0;L;84F1;;;;N;;;;;
+2F9A9;CJK COMPATIBILITY IDEOGRAPH-2F9A9;Lo;0;L;84F3;;;;N;;;;;
+2F9AA;CJK COMPATIBILITY IDEOGRAPH-2F9AA;Lo;0;L;8516;;;;N;;;;;
+2F9AB;CJK COMPATIBILITY IDEOGRAPH-2F9AB;Lo;0;L;273CA;;;;N;;;;;
+2F9AC;CJK COMPATIBILITY IDEOGRAPH-2F9AC;Lo;0;L;8564;;;;N;;;;;
+2F9AD;CJK COMPATIBILITY IDEOGRAPH-2F9AD;Lo;0;L;26F2C;;;;N;;;;;
+2F9AE;CJK COMPATIBILITY IDEOGRAPH-2F9AE;Lo;0;L;455D;;;;N;;;;;
+2F9AF;CJK COMPATIBILITY IDEOGRAPH-2F9AF;Lo;0;L;4561;;;;N;;;;;
+2F9B0;CJK COMPATIBILITY IDEOGRAPH-2F9B0;Lo;0;L;26FB1;;;;N;;;;;
+2F9B1;CJK COMPATIBILITY IDEOGRAPH-2F9B1;Lo;0;L;270D2;;;;N;;;;;
+2F9B2;CJK COMPATIBILITY IDEOGRAPH-2F9B2;Lo;0;L;456B;;;;N;;;;;
+2F9B3;CJK COMPATIBILITY IDEOGRAPH-2F9B3;Lo;0;L;8650;;;;N;;;;;
+2F9B4;CJK COMPATIBILITY IDEOGRAPH-2F9B4;Lo;0;L;865C;;;;N;;;;;
+2F9B5;CJK COMPATIBILITY IDEOGRAPH-2F9B5;Lo;0;L;8667;;;;N;;;;;
+2F9B6;CJK COMPATIBILITY IDEOGRAPH-2F9B6;Lo;0;L;8669;;;;N;;;;;
+2F9B7;CJK COMPATIBILITY IDEOGRAPH-2F9B7;Lo;0;L;86A9;;;;N;;;;;
+2F9B8;CJK COMPATIBILITY IDEOGRAPH-2F9B8;Lo;0;L;8688;;;;N;;;;;
+2F9B9;CJK COMPATIBILITY IDEOGRAPH-2F9B9;Lo;0;L;870E;;;;N;;;;;
+2F9BA;CJK COMPATIBILITY IDEOGRAPH-2F9BA;Lo;0;L;86E2;;;;N;;;;;
+2F9BB;CJK COMPATIBILITY IDEOGRAPH-2F9BB;Lo;0;L;8779;;;;N;;;;;
+2F9BC;CJK COMPATIBILITY IDEOGRAPH-2F9BC;Lo;0;L;8728;;;;N;;;;;
+2F9BD;CJK COMPATIBILITY IDEOGRAPH-2F9BD;Lo;0;L;876B;;;;N;;;;;
+2F9BE;CJK COMPATIBILITY IDEOGRAPH-2F9BE;Lo;0;L;8786;;;;N;;;;;
+2F9BF;CJK COMPATIBILITY IDEOGRAPH-2F9BF;Lo;0;L;45D7;;;;N;;;;;
+2F9C0;CJK COMPATIBILITY IDEOGRAPH-2F9C0;Lo;0;L;87E1;;;;N;;;;;
+2F9C1;CJK COMPATIBILITY IDEOGRAPH-2F9C1;Lo;0;L;8801;;;;N;;;;;
+2F9C2;CJK COMPATIBILITY IDEOGRAPH-2F9C2;Lo;0;L;45F9;;;;N;;;;;
+2F9C3;CJK COMPATIBILITY IDEOGRAPH-2F9C3;Lo;0;L;8860;;;;N;;;;;
+2F9C4;CJK COMPATIBILITY IDEOGRAPH-2F9C4;Lo;0;L;8863;;;;N;;;;;
+2F9C5;CJK COMPATIBILITY IDEOGRAPH-2F9C5;Lo;0;L;27667;;;;N;;;;;
+2F9C6;CJK COMPATIBILITY IDEOGRAPH-2F9C6;Lo;0;L;88D7;;;;N;;;;;
+2F9C7;CJK COMPATIBILITY IDEOGRAPH-2F9C7;Lo;0;L;88DE;;;;N;;;;;
+2F9C8;CJK COMPATIBILITY IDEOGRAPH-2F9C8;Lo;0;L;4635;;;;N;;;;;
+2F9C9;CJK COMPATIBILITY IDEOGRAPH-2F9C9;Lo;0;L;88FA;;;;N;;;;;
+2F9CA;CJK COMPATIBILITY IDEOGRAPH-2F9CA;Lo;0;L;34BB;;;;N;;;;;
+2F9CB;CJK COMPATIBILITY IDEOGRAPH-2F9CB;Lo;0;L;278AE;;;;N;;;;;
+2F9CC;CJK COMPATIBILITY IDEOGRAPH-2F9CC;Lo;0;L;27966;;;;N;;;;;
+2F9CD;CJK COMPATIBILITY IDEOGRAPH-2F9CD;Lo;0;L;46BE;;;;N;;;;;
+2F9CE;CJK COMPATIBILITY IDEOGRAPH-2F9CE;Lo;0;L;46C7;;;;N;;;;;
+2F9CF;CJK COMPATIBILITY IDEOGRAPH-2F9CF;Lo;0;L;8AA0;;;;N;;;;;
+2F9D0;CJK COMPATIBILITY IDEOGRAPH-2F9D0;Lo;0;L;8AED;;;;N;;;;;
+2F9D1;CJK COMPATIBILITY IDEOGRAPH-2F9D1;Lo;0;L;8B8A;;;;N;;;;;
+2F9D2;CJK COMPATIBILITY IDEOGRAPH-2F9D2;Lo;0;L;8C55;;;;N;;;;;
+2F9D3;CJK COMPATIBILITY IDEOGRAPH-2F9D3;Lo;0;L;27CA8;;;;N;;;;;
+2F9D4;CJK COMPATIBILITY IDEOGRAPH-2F9D4;Lo;0;L;8CAB;;;;N;;;;;
+2F9D5;CJK COMPATIBILITY IDEOGRAPH-2F9D5;Lo;0;L;8CC1;;;;N;;;;;
+2F9D6;CJK COMPATIBILITY IDEOGRAPH-2F9D6;Lo;0;L;8D1B;;;;N;;;;;
+2F9D7;CJK COMPATIBILITY IDEOGRAPH-2F9D7;Lo;0;L;8D77;;;;N;;;;;
+2F9D8;CJK COMPATIBILITY IDEOGRAPH-2F9D8;Lo;0;L;27F2F;;;;N;;;;;
+2F9D9;CJK COMPATIBILITY IDEOGRAPH-2F9D9;Lo;0;L;20804;;;;N;;;;;
+2F9DA;CJK COMPATIBILITY IDEOGRAPH-2F9DA;Lo;0;L;8DCB;;;;N;;;;;
+2F9DB;CJK COMPATIBILITY IDEOGRAPH-2F9DB;Lo;0;L;8DBC;;;;N;;;;;
+2F9DC;CJK COMPATIBILITY IDEOGRAPH-2F9DC;Lo;0;L;8DF0;;;;N;;;;;
+2F9DD;CJK COMPATIBILITY IDEOGRAPH-2F9DD;Lo;0;L;208DE;;;;N;;;;;
+2F9DE;CJK COMPATIBILITY IDEOGRAPH-2F9DE;Lo;0;L;8ED4;;;;N;;;;;
+2F9DF;CJK COMPATIBILITY IDEOGRAPH-2F9DF;Lo;0;L;8F38;;;;N;;;;;
+2F9E0;CJK COMPATIBILITY IDEOGRAPH-2F9E0;Lo;0;L;285D2;;;;N;;;;;
+2F9E1;CJK COMPATIBILITY IDEOGRAPH-2F9E1;Lo;0;L;285ED;;;;N;;;;;
+2F9E2;CJK COMPATIBILITY IDEOGRAPH-2F9E2;Lo;0;L;9094;;;;N;;;;;
+2F9E3;CJK COMPATIBILITY IDEOGRAPH-2F9E3;Lo;0;L;90F1;;;;N;;;;;
+2F9E4;CJK COMPATIBILITY IDEOGRAPH-2F9E4;Lo;0;L;9111;;;;N;;;;;
+2F9E5;CJK COMPATIBILITY IDEOGRAPH-2F9E5;Lo;0;L;2872E;;;;N;;;;;
+2F9E6;CJK COMPATIBILITY IDEOGRAPH-2F9E6;Lo;0;L;911B;;;;N;;;;;
+2F9E7;CJK COMPATIBILITY IDEOGRAPH-2F9E7;Lo;0;L;9238;;;;N;;;;;
+2F9E8;CJK COMPATIBILITY IDEOGRAPH-2F9E8;Lo;0;L;92D7;;;;N;;;;;
+2F9E9;CJK COMPATIBILITY IDEOGRAPH-2F9E9;Lo;0;L;92D8;;;;N;;;;;
+2F9EA;CJK COMPATIBILITY IDEOGRAPH-2F9EA;Lo;0;L;927C;;;;N;;;;;
+2F9EB;CJK COMPATIBILITY IDEOGRAPH-2F9EB;Lo;0;L;93F9;;;;N;;;;;
+2F9EC;CJK COMPATIBILITY IDEOGRAPH-2F9EC;Lo;0;L;9415;;;;N;;;;;
+2F9ED;CJK COMPATIBILITY IDEOGRAPH-2F9ED;Lo;0;L;28BFA;;;;N;;;;;
+2F9EE;CJK COMPATIBILITY IDEOGRAPH-2F9EE;Lo;0;L;958B;;;;N;;;;;
+2F9EF;CJK COMPATIBILITY IDEOGRAPH-2F9EF;Lo;0;L;4995;;;;N;;;;;
+2F9F0;CJK COMPATIBILITY IDEOGRAPH-2F9F0;Lo;0;L;95B7;;;;N;;;;;
+2F9F1;CJK COMPATIBILITY IDEOGRAPH-2F9F1;Lo;0;L;28D77;;;;N;;;;;
+2F9F2;CJK COMPATIBILITY IDEOGRAPH-2F9F2;Lo;0;L;49E6;;;;N;;;;;
+2F9F3;CJK COMPATIBILITY IDEOGRAPH-2F9F3;Lo;0;L;96C3;;;;N;;;;;
+2F9F4;CJK COMPATIBILITY IDEOGRAPH-2F9F4;Lo;0;L;5DB2;;;;N;;;;;
+2F9F5;CJK COMPATIBILITY IDEOGRAPH-2F9F5;Lo;0;L;9723;;;;N;;;;;
+2F9F6;CJK COMPATIBILITY IDEOGRAPH-2F9F6;Lo;0;L;29145;;;;N;;;;;
+2F9F7;CJK COMPATIBILITY IDEOGRAPH-2F9F7;Lo;0;L;2921A;;;;N;;;;;
+2F9F8;CJK COMPATIBILITY IDEOGRAPH-2F9F8;Lo;0;L;4A6E;;;;N;;;;;
+2F9F9;CJK COMPATIBILITY IDEOGRAPH-2F9F9;Lo;0;L;4A76;;;;N;;;;;
+2F9FA;CJK COMPATIBILITY IDEOGRAPH-2F9FA;Lo;0;L;97E0;;;;N;;;;;
+2F9FB;CJK COMPATIBILITY IDEOGRAPH-2F9FB;Lo;0;L;2940A;;;;N;;;;;
+2F9FC;CJK COMPATIBILITY IDEOGRAPH-2F9FC;Lo;0;L;4AB2;;;;N;;;;;
+2F9FD;CJK COMPATIBILITY IDEOGRAPH-2F9FD;Lo;0;L;29496;;;;N;;;;;
+2F9FE;CJK COMPATIBILITY IDEOGRAPH-2F9FE;Lo;0;L;980B;;;;N;;;;;
+2F9FF;CJK COMPATIBILITY IDEOGRAPH-2F9FF;Lo;0;L;980B;;;;N;;;;;
+2FA00;CJK COMPATIBILITY IDEOGRAPH-2FA00;Lo;0;L;9829;;;;N;;;;;
+2FA01;CJK COMPATIBILITY IDEOGRAPH-2FA01;Lo;0;L;295B6;;;;N;;;;;
+2FA02;CJK COMPATIBILITY IDEOGRAPH-2FA02;Lo;0;L;98E2;;;;N;;;;;
+2FA03;CJK COMPATIBILITY IDEOGRAPH-2FA03;Lo;0;L;4B33;;;;N;;;;;
+2FA04;CJK COMPATIBILITY IDEOGRAPH-2FA04;Lo;0;L;9929;;;;N;;;;;
+2FA05;CJK COMPATIBILITY IDEOGRAPH-2FA05;Lo;0;L;99A7;;;;N;;;;;
+2FA06;CJK COMPATIBILITY IDEOGRAPH-2FA06;Lo;0;L;99C2;;;;N;;;;;
+2FA07;CJK COMPATIBILITY IDEOGRAPH-2FA07;Lo;0;L;99FE;;;;N;;;;;
+2FA08;CJK COMPATIBILITY IDEOGRAPH-2FA08;Lo;0;L;4BCE;;;;N;;;;;
+2FA09;CJK COMPATIBILITY IDEOGRAPH-2FA09;Lo;0;L;29B30;;;;N;;;;;
+2FA0A;CJK COMPATIBILITY IDEOGRAPH-2FA0A;Lo;0;L;9B12;;;;N;;;;;
+2FA0B;CJK COMPATIBILITY IDEOGRAPH-2FA0B;Lo;0;L;9C40;;;;N;;;;;
+2FA0C;CJK COMPATIBILITY IDEOGRAPH-2FA0C;Lo;0;L;9CFD;;;;N;;;;;
+2FA0D;CJK COMPATIBILITY IDEOGRAPH-2FA0D;Lo;0;L;4CCE;;;;N;;;;;
+2FA0E;CJK COMPATIBILITY IDEOGRAPH-2FA0E;Lo;0;L;4CED;;;;N;;;;;
+2FA0F;CJK COMPATIBILITY IDEOGRAPH-2FA0F;Lo;0;L;9D67;;;;N;;;;;
+2FA10;CJK COMPATIBILITY IDEOGRAPH-2FA10;Lo;0;L;2A0CE;;;;N;;;;;
+2FA11;CJK COMPATIBILITY IDEOGRAPH-2FA11;Lo;0;L;4CF8;;;;N;;;;;
+2FA12;CJK COMPATIBILITY IDEOGRAPH-2FA12;Lo;0;L;2A105;;;;N;;;;;
+2FA13;CJK COMPATIBILITY IDEOGRAPH-2FA13;Lo;0;L;2A20E;;;;N;;;;;
+2FA14;CJK COMPATIBILITY IDEOGRAPH-2FA14;Lo;0;L;2A291;;;;N;;;;;
+2FA15;CJK COMPATIBILITY IDEOGRAPH-2FA15;Lo;0;L;9EBB;;;;N;;;;;
+2FA16;CJK COMPATIBILITY IDEOGRAPH-2FA16;Lo;0;L;4D56;;;;N;;;;;
+2FA17;CJK COMPATIBILITY IDEOGRAPH-2FA17;Lo;0;L;9EF9;;;;N;;;;;
+2FA18;CJK COMPATIBILITY IDEOGRAPH-2FA18;Lo;0;L;9EFE;;;;N;;;;;
+2FA19;CJK COMPATIBILITY IDEOGRAPH-2FA19;Lo;0;L;9F05;;;;N;;;;;
+2FA1A;CJK COMPATIBILITY IDEOGRAPH-2FA1A;Lo;0;L;9F0F;;;;N;;;;;
+2FA1B;CJK COMPATIBILITY IDEOGRAPH-2FA1B;Lo;0;L;9F16;;;;N;;;;;
+2FA1C;CJK COMPATIBILITY IDEOGRAPH-2FA1C;Lo;0;L;9F3B;;;;N;;;;;
+2FA1D;CJK COMPATIBILITY IDEOGRAPH-2FA1D;Lo;0;L;2A600;;;;N;;;;;
+E0001;LANGUAGE TAG;Cf;0;BN;;;;;N;;;;;
+E0020;TAG SPACE;Cf;0;BN;;;;;N;;;;;
+E0021;TAG EXCLAMATION MARK;Cf;0;BN;;;;;N;;;;;
+E0022;TAG QUOTATION MARK;Cf;0;BN;;;;;N;;;;;
+E0023;TAG NUMBER SIGN;Cf;0;BN;;;;;N;;;;;
+E0024;TAG DOLLAR SIGN;Cf;0;BN;;;;;N;;;;;
+E0025;TAG PERCENT SIGN;Cf;0;BN;;;;;N;;;;;
+E0026;TAG AMPERSAND;Cf;0;BN;;;;;N;;;;;
+E0027;TAG APOSTROPHE;Cf;0;BN;;;;;N;;;;;
+E0028;TAG LEFT PARENTHESIS;Cf;0;BN;;;;;N;;;;;
+E0029;TAG RIGHT PARENTHESIS;Cf;0;BN;;;;;N;;;;;
+E002A;TAG ASTERISK;Cf;0;BN;;;;;N;;;;;
+E002B;TAG PLUS SIGN;Cf;0;BN;;;;;N;;;;;
+E002C;TAG COMMA;Cf;0;BN;;;;;N;;;;;
+E002D;TAG HYPHEN-MINUS;Cf;0;BN;;;;;N;;;;;
+E002E;TAG FULL STOP;Cf;0;BN;;;;;N;;;;;
+E002F;TAG SOLIDUS;Cf;0;BN;;;;;N;;;;;
+E0030;TAG DIGIT ZERO;Cf;0;BN;;;;;N;;;;;
+E0031;TAG DIGIT ONE;Cf;0;BN;;;;;N;;;;;
+E0032;TAG DIGIT TWO;Cf;0;BN;;;;;N;;;;;
+E0033;TAG DIGIT THREE;Cf;0;BN;;;;;N;;;;;
+E0034;TAG DIGIT FOUR;Cf;0;BN;;;;;N;;;;;
+E0035;TAG DIGIT FIVE;Cf;0;BN;;;;;N;;;;;
+E0036;TAG DIGIT SIX;Cf;0;BN;;;;;N;;;;;
+E0037;TAG DIGIT SEVEN;Cf;0;BN;;;;;N;;;;;
+E0038;TAG DIGIT EIGHT;Cf;0;BN;;;;;N;;;;;
+E0039;TAG DIGIT NINE;Cf;0;BN;;;;;N;;;;;
+E003A;TAG COLON;Cf;0;BN;;;;;N;;;;;
+E003B;TAG SEMICOLON;Cf;0;BN;;;;;N;;;;;
+E003C;TAG LESS-THAN SIGN;Cf;0;BN;;;;;N;;;;;
+E003D;TAG EQUALS SIGN;Cf;0;BN;;;;;N;;;;;
+E003E;TAG GREATER-THAN SIGN;Cf;0;BN;;;;;N;;;;;
+E003F;TAG QUESTION MARK;Cf;0;BN;;;;;N;;;;;
+E0040;TAG COMMERCIAL AT;Cf;0;BN;;;;;N;;;;;
+E0041;TAG LATIN CAPITAL LETTER A;Cf;0;BN;;;;;N;;;;;
+E0042;TAG LATIN CAPITAL LETTER B;Cf;0;BN;;;;;N;;;;;
+E0043;TAG LATIN CAPITAL LETTER C;Cf;0;BN;;;;;N;;;;;
+E0044;TAG LATIN CAPITAL LETTER D;Cf;0;BN;;;;;N;;;;;
+E0045;TAG LATIN CAPITAL LETTER E;Cf;0;BN;;;;;N;;;;;
+E0046;TAG LATIN CAPITAL LETTER F;Cf;0;BN;;;;;N;;;;;
+E0047;TAG LATIN CAPITAL LETTER G;Cf;0;BN;;;;;N;;;;;
+E0048;TAG LATIN CAPITAL LETTER H;Cf;0;BN;;;;;N;;;;;
+E0049;TAG LATIN CAPITAL LETTER I;Cf;0;BN;;;;;N;;;;;
+E004A;TAG LATIN CAPITAL LETTER J;Cf;0;BN;;;;;N;;;;;
+E004B;TAG LATIN CAPITAL LETTER K;Cf;0;BN;;;;;N;;;;;
+E004C;TAG LATIN CAPITAL LETTER L;Cf;0;BN;;;;;N;;;;;
+E004D;TAG LATIN CAPITAL LETTER M;Cf;0;BN;;;;;N;;;;;
+E004E;TAG LATIN CAPITAL LETTER N;Cf;0;BN;;;;;N;;;;;
+E004F;TAG LATIN CAPITAL LETTER O;Cf;0;BN;;;;;N;;;;;
+E0050;TAG LATIN CAPITAL LETTER P;Cf;0;BN;;;;;N;;;;;
+E0051;TAG LATIN CAPITAL LETTER Q;Cf;0;BN;;;;;N;;;;;
+E0052;TAG LATIN CAPITAL LETTER R;Cf;0;BN;;;;;N;;;;;
+E0053;TAG LATIN CAPITAL LETTER S;Cf;0;BN;;;;;N;;;;;
+E0054;TAG LATIN CAPITAL LETTER T;Cf;0;BN;;;;;N;;;;;
+E0055;TAG LATIN CAPITAL LETTER U;Cf;0;BN;;;;;N;;;;;
+E0056;TAG LATIN CAPITAL LETTER V;Cf;0;BN;;;;;N;;;;;
+E0057;TAG LATIN CAPITAL LETTER W;Cf;0;BN;;;;;N;;;;;
+E0058;TAG LATIN CAPITAL LETTER X;Cf;0;BN;;;;;N;;;;;
+E0059;TAG LATIN CAPITAL LETTER Y;Cf;0;BN;;;;;N;;;;;
+E005A;TAG LATIN CAPITAL LETTER Z;Cf;0;BN;;;;;N;;;;;
+E005B;TAG LEFT SQUARE BRACKET;Cf;0;BN;;;;;N;;;;;
+E005C;TAG REVERSE SOLIDUS;Cf;0;BN;;;;;N;;;;;
+E005D;TAG RIGHT SQUARE BRACKET;Cf;0;BN;;;;;N;;;;;
+E005E;TAG CIRCUMFLEX ACCENT;Cf;0;BN;;;;;N;;;;;
+E005F;TAG LOW LINE;Cf;0;BN;;;;;N;;;;;
+E0060;TAG GRAVE ACCENT;Cf;0;BN;;;;;N;;;;;
+E0061;TAG LATIN SMALL LETTER A;Cf;0;BN;;;;;N;;;;;
+E0062;TAG LATIN SMALL LETTER B;Cf;0;BN;;;;;N;;;;;
+E0063;TAG LATIN SMALL LETTER C;Cf;0;BN;;;;;N;;;;;
+E0064;TAG LATIN SMALL LETTER D;Cf;0;BN;;;;;N;;;;;
+E0065;TAG LATIN SMALL LETTER E;Cf;0;BN;;;;;N;;;;;
+E0066;TAG LATIN SMALL LETTER F;Cf;0;BN;;;;;N;;;;;
+E0067;TAG LATIN SMALL LETTER G;Cf;0;BN;;;;;N;;;;;
+E0068;TAG LATIN SMALL LETTER H;Cf;0;BN;;;;;N;;;;;
+E0069;TAG LATIN SMALL LETTER I;Cf;0;BN;;;;;N;;;;;
+E006A;TAG LATIN SMALL LETTER J;Cf;0;BN;;;;;N;;;;;
+E006B;TAG LATIN SMALL LETTER K;Cf;0;BN;;;;;N;;;;;
+E006C;TAG LATIN SMALL LETTER L;Cf;0;BN;;;;;N;;;;;
+E006D;TAG LATIN SMALL LETTER M;Cf;0;BN;;;;;N;;;;;
+E006E;TAG LATIN SMALL LETTER N;Cf;0;BN;;;;;N;;;;;
+E006F;TAG LATIN SMALL LETTER O;Cf;0;BN;;;;;N;;;;;
+E0070;TAG LATIN SMALL LETTER P;Cf;0;BN;;;;;N;;;;;
+E0071;TAG LATIN SMALL LETTER Q;Cf;0;BN;;;;;N;;;;;
+E0072;TAG LATIN SMALL LETTER R;Cf;0;BN;;;;;N;;;;;
+E0073;TAG LATIN SMALL LETTER S;Cf;0;BN;;;;;N;;;;;
+E0074;TAG LATIN SMALL LETTER T;Cf;0;BN;;;;;N;;;;;
+E0075;TAG LATIN SMALL LETTER U;Cf;0;BN;;;;;N;;;;;
+E0076;TAG LATIN SMALL LETTER V;Cf;0;BN;;;;;N;;;;;
+E0077;TAG LATIN SMALL LETTER W;Cf;0;BN;;;;;N;;;;;
+E0078;TAG LATIN SMALL LETTER X;Cf;0;BN;;;;;N;;;;;
+E0079;TAG LATIN SMALL LETTER Y;Cf;0;BN;;;;;N;;;;;
+E007A;TAG LATIN SMALL LETTER Z;Cf;0;BN;;;;;N;;;;;
+E007B;TAG LEFT CURLY BRACKET;Cf;0;BN;;;;;N;;;;;
+E007C;TAG VERTICAL LINE;Cf;0;BN;;;;;N;;;;;
+E007D;TAG RIGHT CURLY BRACKET;Cf;0;BN;;;;;N;;;;;
+E007E;TAG TILDE;Cf;0;BN;;;;;N;;;;;
+E007F;CANCEL TAG;Cf;0;BN;;;;;N;;;;;
+E0100;VARIATION SELECTOR-17;Mn;0;NSM;;;;;N;;;;;
+E0101;VARIATION SELECTOR-18;Mn;0;NSM;;;;;N;;;;;
+E0102;VARIATION SELECTOR-19;Mn;0;NSM;;;;;N;;;;;
+E0103;VARIATION SELECTOR-20;Mn;0;NSM;;;;;N;;;;;
+E0104;VARIATION SELECTOR-21;Mn;0;NSM;;;;;N;;;;;
+E0105;VARIATION SELECTOR-22;Mn;0;NSM;;;;;N;;;;;
+E0106;VARIATION SELECTOR-23;Mn;0;NSM;;;;;N;;;;;
+E0107;VARIATION SELECTOR-24;Mn;0;NSM;;;;;N;;;;;
+E0108;VARIATION SELECTOR-25;Mn;0;NSM;;;;;N;;;;;
+E0109;VARIATION SELECTOR-26;Mn;0;NSM;;;;;N;;;;;
+E010A;VARIATION SELECTOR-27;Mn;0;NSM;;;;;N;;;;;
+E010B;VARIATION SELECTOR-28;Mn;0;NSM;;;;;N;;;;;
+E010C;VARIATION SELECTOR-29;Mn;0;NSM;;;;;N;;;;;
+E010D;VARIATION SELECTOR-30;Mn;0;NSM;;;;;N;;;;;
+E010E;VARIATION SELECTOR-31;Mn;0;NSM;;;;;N;;;;;
+E010F;VARIATION SELECTOR-32;Mn;0;NSM;;;;;N;;;;;
+E0110;VARIATION SELECTOR-33;Mn;0;NSM;;;;;N;;;;;
+E0111;VARIATION SELECTOR-34;Mn;0;NSM;;;;;N;;;;;
+E0112;VARIATION SELECTOR-35;Mn;0;NSM;;;;;N;;;;;
+E0113;VARIATION SELECTOR-36;Mn;0;NSM;;;;;N;;;;;
+E0114;VARIATION SELECTOR-37;Mn;0;NSM;;;;;N;;;;;
+E0115;VARIATION SELECTOR-38;Mn;0;NSM;;;;;N;;;;;
+E0116;VARIATION SELECTOR-39;Mn;0;NSM;;;;;N;;;;;
+E0117;VARIATION SELECTOR-40;Mn;0;NSM;;;;;N;;;;;
+E0118;VARIATION SELECTOR-41;Mn;0;NSM;;;;;N;;;;;
+E0119;VARIATION SELECTOR-42;Mn;0;NSM;;;;;N;;;;;
+E011A;VARIATION SELECTOR-43;Mn;0;NSM;;;;;N;;;;;
+E011B;VARIATION SELECTOR-44;Mn;0;NSM;;;;;N;;;;;
+E011C;VARIATION SELECTOR-45;Mn;0;NSM;;;;;N;;;;;
+E011D;VARIATION SELECTOR-46;Mn;0;NSM;;;;;N;;;;;
+E011E;VARIATION SELECTOR-47;Mn;0;NSM;;;;;N;;;;;
+E011F;VARIATION SELECTOR-48;Mn;0;NSM;;;;;N;;;;;
+E0120;VARIATION SELECTOR-49;Mn;0;NSM;;;;;N;;;;;
+E0121;VARIATION SELECTOR-50;Mn;0;NSM;;;;;N;;;;;
+E0122;VARIATION SELECTOR-51;Mn;0;NSM;;;;;N;;;;;
+E0123;VARIATION SELECTOR-52;Mn;0;NSM;;;;;N;;;;;
+E0124;VARIATION SELECTOR-53;Mn;0;NSM;;;;;N;;;;;
+E0125;VARIATION SELECTOR-54;Mn;0;NSM;;;;;N;;;;;
+E0126;VARIATION SELECTOR-55;Mn;0;NSM;;;;;N;;;;;
+E0127;VARIATION SELECTOR-56;Mn;0;NSM;;;;;N;;;;;
+E0128;VARIATION SELECTOR-57;Mn;0;NSM;;;;;N;;;;;
+E0129;VARIATION SELECTOR-58;Mn;0;NSM;;;;;N;;;;;
+E012A;VARIATION SELECTOR-59;Mn;0;NSM;;;;;N;;;;;
+E012B;VARIATION SELECTOR-60;Mn;0;NSM;;;;;N;;;;;
+E012C;VARIATION SELECTOR-61;Mn;0;NSM;;;;;N;;;;;
+E012D;VARIATION SELECTOR-62;Mn;0;NSM;;;;;N;;;;;
+E012E;VARIATION SELECTOR-63;Mn;0;NSM;;;;;N;;;;;
+E012F;VARIATION SELECTOR-64;Mn;0;NSM;;;;;N;;;;;
+E0130;VARIATION SELECTOR-65;Mn;0;NSM;;;;;N;;;;;
+E0131;VARIATION SELECTOR-66;Mn;0;NSM;;;;;N;;;;;
+E0132;VARIATION SELECTOR-67;Mn;0;NSM;;;;;N;;;;;
+E0133;VARIATION SELECTOR-68;Mn;0;NSM;;;;;N;;;;;
+E0134;VARIATION SELECTOR-69;Mn;0;NSM;;;;;N;;;;;
+E0135;VARIATION SELECTOR-70;Mn;0;NSM;;;;;N;;;;;
+E0136;VARIATION SELECTOR-71;Mn;0;NSM;;;;;N;;;;;
+E0137;VARIATION SELECTOR-72;Mn;0;NSM;;;;;N;;;;;
+E0138;VARIATION SELECTOR-73;Mn;0;NSM;;;;;N;;;;;
+E0139;VARIATION SELECTOR-74;Mn;0;NSM;;;;;N;;;;;
+E013A;VARIATION SELECTOR-75;Mn;0;NSM;;;;;N;;;;;
+E013B;VARIATION SELECTOR-76;Mn;0;NSM;;;;;N;;;;;
+E013C;VARIATION SELECTOR-77;Mn;0;NSM;;;;;N;;;;;
+E013D;VARIATION SELECTOR-78;Mn;0;NSM;;;;;N;;;;;
+E013E;VARIATION SELECTOR-79;Mn;0;NSM;;;;;N;;;;;
+E013F;VARIATION SELECTOR-80;Mn;0;NSM;;;;;N;;;;;
+E0140;VARIATION SELECTOR-81;Mn;0;NSM;;;;;N;;;;;
+E0141;VARIATION SELECTOR-82;Mn;0;NSM;;;;;N;;;;;
+E0142;VARIATION SELECTOR-83;Mn;0;NSM;;;;;N;;;;;
+E0143;VARIATION SELECTOR-84;Mn;0;NSM;;;;;N;;;;;
+E0144;VARIATION SELECTOR-85;Mn;0;NSM;;;;;N;;;;;
+E0145;VARIATION SELECTOR-86;Mn;0;NSM;;;;;N;;;;;
+E0146;VARIATION SELECTOR-87;Mn;0;NSM;;;;;N;;;;;
+E0147;VARIATION SELECTOR-88;Mn;0;NSM;;;;;N;;;;;
+E0148;VARIATION SELECTOR-89;Mn;0;NSM;;;;;N;;;;;
+E0149;VARIATION SELECTOR-90;Mn;0;NSM;;;;;N;;;;;
+E014A;VARIATION SELECTOR-91;Mn;0;NSM;;;;;N;;;;;
+E014B;VARIATION SELECTOR-92;Mn;0;NSM;;;;;N;;;;;
+E014C;VARIATION SELECTOR-93;Mn;0;NSM;;;;;N;;;;;
+E014D;VARIATION SELECTOR-94;Mn;0;NSM;;;;;N;;;;;
+E014E;VARIATION SELECTOR-95;Mn;0;NSM;;;;;N;;;;;
+E014F;VARIATION SELECTOR-96;Mn;0;NSM;;;;;N;;;;;
+E0150;VARIATION SELECTOR-97;Mn;0;NSM;;;;;N;;;;;
+E0151;VARIATION SELECTOR-98;Mn;0;NSM;;;;;N;;;;;
+E0152;VARIATION SELECTOR-99;Mn;0;NSM;;;;;N;;;;;
+E0153;VARIATION SELECTOR-100;Mn;0;NSM;;;;;N;;;;;
+E0154;VARIATION SELECTOR-101;Mn;0;NSM;;;;;N;;;;;
+E0155;VARIATION SELECTOR-102;Mn;0;NSM;;;;;N;;;;;
+E0156;VARIATION SELECTOR-103;Mn;0;NSM;;;;;N;;;;;
+E0157;VARIATION SELECTOR-104;Mn;0;NSM;;;;;N;;;;;
+E0158;VARIATION SELECTOR-105;Mn;0;NSM;;;;;N;;;;;
+E0159;VARIATION SELECTOR-106;Mn;0;NSM;;;;;N;;;;;
+E015A;VARIATION SELECTOR-107;Mn;0;NSM;;;;;N;;;;;
+E015B;VARIATION SELECTOR-108;Mn;0;NSM;;;;;N;;;;;
+E015C;VARIATION SELECTOR-109;Mn;0;NSM;;;;;N;;;;;
+E015D;VARIATION SELECTOR-110;Mn;0;NSM;;;;;N;;;;;
+E015E;VARIATION SELECTOR-111;Mn;0;NSM;;;;;N;;;;;
+E015F;VARIATION SELECTOR-112;Mn;0;NSM;;;;;N;;;;;
+E0160;VARIATION SELECTOR-113;Mn;0;NSM;;;;;N;;;;;
+E0161;VARIATION SELECTOR-114;Mn;0;NSM;;;;;N;;;;;
+E0162;VARIATION SELECTOR-115;Mn;0;NSM;;;;;N;;;;;
+E0163;VARIATION SELECTOR-116;Mn;0;NSM;;;;;N;;;;;
+E0164;VARIATION SELECTOR-117;Mn;0;NSM;;;;;N;;;;;
+E0165;VARIATION SELECTOR-118;Mn;0;NSM;;;;;N;;;;;
+E0166;VARIATION SELECTOR-119;Mn;0;NSM;;;;;N;;;;;
+E0167;VARIATION SELECTOR-120;Mn;0;NSM;;;;;N;;;;;
+E0168;VARIATION SELECTOR-121;Mn;0;NSM;;;;;N;;;;;
+E0169;VARIATION SELECTOR-122;Mn;0;NSM;;;;;N;;;;;
+E016A;VARIATION SELECTOR-123;Mn;0;NSM;;;;;N;;;;;
+E016B;VARIATION SELECTOR-124;Mn;0;NSM;;;;;N;;;;;
+E016C;VARIATION SELECTOR-125;Mn;0;NSM;;;;;N;;;;;
+E016D;VARIATION SELECTOR-126;Mn;0;NSM;;;;;N;;;;;
+E016E;VARIATION SELECTOR-127;Mn;0;NSM;;;;;N;;;;;
+E016F;VARIATION SELECTOR-128;Mn;0;NSM;;;;;N;;;;;
+E0170;VARIATION SELECTOR-129;Mn;0;NSM;;;;;N;;;;;
+E0171;VARIATION SELECTOR-130;Mn;0;NSM;;;;;N;;;;;
+E0172;VARIATION SELECTOR-131;Mn;0;NSM;;;;;N;;;;;
+E0173;VARIATION SELECTOR-132;Mn;0;NSM;;;;;N;;;;;
+E0174;VARIATION SELECTOR-133;Mn;0;NSM;;;;;N;;;;;
+E0175;VARIATION SELECTOR-134;Mn;0;NSM;;;;;N;;;;;
+E0176;VARIATION SELECTOR-135;Mn;0;NSM;;;;;N;;;;;
+E0177;VARIATION SELECTOR-136;Mn;0;NSM;;;;;N;;;;;
+E0178;VARIATION SELECTOR-137;Mn;0;NSM;;;;;N;;;;;
+E0179;VARIATION SELECTOR-138;Mn;0;NSM;;;;;N;;;;;
+E017A;VARIATION SELECTOR-139;Mn;0;NSM;;;;;N;;;;;
+E017B;VARIATION SELECTOR-140;Mn;0;NSM;;;;;N;;;;;
+E017C;VARIATION SELECTOR-141;Mn;0;NSM;;;;;N;;;;;
+E017D;VARIATION SELECTOR-142;Mn;0;NSM;;;;;N;;;;;
+E017E;VARIATION SELECTOR-143;Mn;0;NSM;;;;;N;;;;;
+E017F;VARIATION SELECTOR-144;Mn;0;NSM;;;;;N;;;;;
+E0180;VARIATION SELECTOR-145;Mn;0;NSM;;;;;N;;;;;
+E0181;VARIATION SELECTOR-146;Mn;0;NSM;;;;;N;;;;;
+E0182;VARIATION SELECTOR-147;Mn;0;NSM;;;;;N;;;;;
+E0183;VARIATION SELECTOR-148;Mn;0;NSM;;;;;N;;;;;
+E0184;VARIATION SELECTOR-149;Mn;0;NSM;;;;;N;;;;;
+E0185;VARIATION SELECTOR-150;Mn;0;NSM;;;;;N;;;;;
+E0186;VARIATION SELECTOR-151;Mn;0;NSM;;;;;N;;;;;
+E0187;VARIATION SELECTOR-152;Mn;0;NSM;;;;;N;;;;;
+E0188;VARIATION SELECTOR-153;Mn;0;NSM;;;;;N;;;;;
+E0189;VARIATION SELECTOR-154;Mn;0;NSM;;;;;N;;;;;
+E018A;VARIATION SELECTOR-155;Mn;0;NSM;;;;;N;;;;;
+E018B;VARIATION SELECTOR-156;Mn;0;NSM;;;;;N;;;;;
+E018C;VARIATION SELECTOR-157;Mn;0;NSM;;;;;N;;;;;
+E018D;VARIATION SELECTOR-158;Mn;0;NSM;;;;;N;;;;;
+E018E;VARIATION SELECTOR-159;Mn;0;NSM;;;;;N;;;;;
+E018F;VARIATION SELECTOR-160;Mn;0;NSM;;;;;N;;;;;
+E0190;VARIATION SELECTOR-161;Mn;0;NSM;;;;;N;;;;;
+E0191;VARIATION SELECTOR-162;Mn;0;NSM;;;;;N;;;;;
+E0192;VARIATION SELECTOR-163;Mn;0;NSM;;;;;N;;;;;
+E0193;VARIATION SELECTOR-164;Mn;0;NSM;;;;;N;;;;;
+E0194;VARIATION SELECTOR-165;Mn;0;NSM;;;;;N;;;;;
+E0195;VARIATION SELECTOR-166;Mn;0;NSM;;;;;N;;;;;
+E0196;VARIATION SELECTOR-167;Mn;0;NSM;;;;;N;;;;;
+E0197;VARIATION SELECTOR-168;Mn;0;NSM;;;;;N;;;;;
+E0198;VARIATION SELECTOR-169;Mn;0;NSM;;;;;N;;;;;
+E0199;VARIATION SELECTOR-170;Mn;0;NSM;;;;;N;;;;;
+E019A;VARIATION SELECTOR-171;Mn;0;NSM;;;;;N;;;;;
+E019B;VARIATION SELECTOR-172;Mn;0;NSM;;;;;N;;;;;
+E019C;VARIATION SELECTOR-173;Mn;0;NSM;;;;;N;;;;;
+E019D;VARIATION SELECTOR-174;Mn;0;NSM;;;;;N;;;;;
+E019E;VARIATION SELECTOR-175;Mn;0;NSM;;;;;N;;;;;
+E019F;VARIATION SELECTOR-176;Mn;0;NSM;;;;;N;;;;;
+E01A0;VARIATION SELECTOR-177;Mn;0;NSM;;;;;N;;;;;
+E01A1;VARIATION SELECTOR-178;Mn;0;NSM;;;;;N;;;;;
+E01A2;VARIATION SELECTOR-179;Mn;0;NSM;;;;;N;;;;;
+E01A3;VARIATION SELECTOR-180;Mn;0;NSM;;;;;N;;;;;
+E01A4;VARIATION SELECTOR-181;Mn;0;NSM;;;;;N;;;;;
+E01A5;VARIATION SELECTOR-182;Mn;0;NSM;;;;;N;;;;;
+E01A6;VARIATION SELECTOR-183;Mn;0;NSM;;;;;N;;;;;
+E01A7;VARIATION SELECTOR-184;Mn;0;NSM;;;;;N;;;;;
+E01A8;VARIATION SELECTOR-185;Mn;0;NSM;;;;;N;;;;;
+E01A9;VARIATION SELECTOR-186;Mn;0;NSM;;;;;N;;;;;
+E01AA;VARIATION SELECTOR-187;Mn;0;NSM;;;;;N;;;;;
+E01AB;VARIATION SELECTOR-188;Mn;0;NSM;;;;;N;;;;;
+E01AC;VARIATION SELECTOR-189;Mn;0;NSM;;;;;N;;;;;
+E01AD;VARIATION SELECTOR-190;Mn;0;NSM;;;;;N;;;;;
+E01AE;VARIATION SELECTOR-191;Mn;0;NSM;;;;;N;;;;;
+E01AF;VARIATION SELECTOR-192;Mn;0;NSM;;;;;N;;;;;
+E01B0;VARIATION SELECTOR-193;Mn;0;NSM;;;;;N;;;;;
+E01B1;VARIATION SELECTOR-194;Mn;0;NSM;;;;;N;;;;;
+E01B2;VARIATION SELECTOR-195;Mn;0;NSM;;;;;N;;;;;
+E01B3;VARIATION SELECTOR-196;Mn;0;NSM;;;;;N;;;;;
+E01B4;VARIATION SELECTOR-197;Mn;0;NSM;;;;;N;;;;;
+E01B5;VARIATION SELECTOR-198;Mn;0;NSM;;;;;N;;;;;
+E01B6;VARIATION SELECTOR-199;Mn;0;NSM;;;;;N;;;;;
+E01B7;VARIATION SELECTOR-200;Mn;0;NSM;;;;;N;;;;;
+E01B8;VARIATION SELECTOR-201;Mn;0;NSM;;;;;N;;;;;
+E01B9;VARIATION SELECTOR-202;Mn;0;NSM;;;;;N;;;;;
+E01BA;VARIATION SELECTOR-203;Mn;0;NSM;;;;;N;;;;;
+E01BB;VARIATION SELECTOR-204;Mn;0;NSM;;;;;N;;;;;
+E01BC;VARIATION SELECTOR-205;Mn;0;NSM;;;;;N;;;;;
+E01BD;VARIATION SELECTOR-206;Mn;0;NSM;;;;;N;;;;;
+E01BE;VARIATION SELECTOR-207;Mn;0;NSM;;;;;N;;;;;
+E01BF;VARIATION SELECTOR-208;Mn;0;NSM;;;;;N;;;;;
+E01C0;VARIATION SELECTOR-209;Mn;0;NSM;;;;;N;;;;;
+E01C1;VARIATION SELECTOR-210;Mn;0;NSM;;;;;N;;;;;
+E01C2;VARIATION SELECTOR-211;Mn;0;NSM;;;;;N;;;;;
+E01C3;VARIATION SELECTOR-212;Mn;0;NSM;;;;;N;;;;;
+E01C4;VARIATION SELECTOR-213;Mn;0;NSM;;;;;N;;;;;
+E01C5;VARIATION SELECTOR-214;Mn;0;NSM;;;;;N;;;;;
+E01C6;VARIATION SELECTOR-215;Mn;0;NSM;;;;;N;;;;;
+E01C7;VARIATION SELECTOR-216;Mn;0;NSM;;;;;N;;;;;
+E01C8;VARIATION SELECTOR-217;Mn;0;NSM;;;;;N;;;;;
+E01C9;VARIATION SELECTOR-218;Mn;0;NSM;;;;;N;;;;;
+E01CA;VARIATION SELECTOR-219;Mn;0;NSM;;;;;N;;;;;
+E01CB;VARIATION SELECTOR-220;Mn;0;NSM;;;;;N;;;;;
+E01CC;VARIATION SELECTOR-221;Mn;0;NSM;;;;;N;;;;;
+E01CD;VARIATION SELECTOR-222;Mn;0;NSM;;;;;N;;;;;
+E01CE;VARIATION SELECTOR-223;Mn;0;NSM;;;;;N;;;;;
+E01CF;VARIATION SELECTOR-224;Mn;0;NSM;;;;;N;;;;;
+E01D0;VARIATION SELECTOR-225;Mn;0;NSM;;;;;N;;;;;
+E01D1;VARIATION SELECTOR-226;Mn;0;NSM;;;;;N;;;;;
+E01D2;VARIATION SELECTOR-227;Mn;0;NSM;;;;;N;;;;;
+E01D3;VARIATION SELECTOR-228;Mn;0;NSM;;;;;N;;;;;
+E01D4;VARIATION SELECTOR-229;Mn;0;NSM;;;;;N;;;;;
+E01D5;VARIATION SELECTOR-230;Mn;0;NSM;;;;;N;;;;;
+E01D6;VARIATION SELECTOR-231;Mn;0;NSM;;;;;N;;;;;
+E01D7;VARIATION SELECTOR-232;Mn;0;NSM;;;;;N;;;;;
+E01D8;VARIATION SELECTOR-233;Mn;0;NSM;;;;;N;;;;;
+E01D9;VARIATION SELECTOR-234;Mn;0;NSM;;;;;N;;;;;
+E01DA;VARIATION SELECTOR-235;Mn;0;NSM;;;;;N;;;;;
+E01DB;VARIATION SELECTOR-236;Mn;0;NSM;;;;;N;;;;;
+E01DC;VARIATION SELECTOR-237;Mn;0;NSM;;;;;N;;;;;
+E01DD;VARIATION SELECTOR-238;Mn;0;NSM;;;;;N;;;;;
+E01DE;VARIATION SELECTOR-239;Mn;0;NSM;;;;;N;;;;;
+E01DF;VARIATION SELECTOR-240;Mn;0;NSM;;;;;N;;;;;
+E01E0;VARIATION SELECTOR-241;Mn;0;NSM;;;;;N;;;;;
+E01E1;VARIATION SELECTOR-242;Mn;0;NSM;;;;;N;;;;;
+E01E2;VARIATION SELECTOR-243;Mn;0;NSM;;;;;N;;;;;
+E01E3;VARIATION SELECTOR-244;Mn;0;NSM;;;;;N;;;;;
+E01E4;VARIATION SELECTOR-245;Mn;0;NSM;;;;;N;;;;;
+E01E5;VARIATION SELECTOR-246;Mn;0;NSM;;;;;N;;;;;
+E01E6;VARIATION SELECTOR-247;Mn;0;NSM;;;;;N;;;;;
+E01E7;VARIATION SELECTOR-248;Mn;0;NSM;;;;;N;;;;;
+E01E8;VARIATION SELECTOR-249;Mn;0;NSM;;;;;N;;;;;
+E01E9;VARIATION SELECTOR-250;Mn;0;NSM;;;;;N;;;;;
+E01EA;VARIATION SELECTOR-251;Mn;0;NSM;;;;;N;;;;;
+E01EB;VARIATION SELECTOR-252;Mn;0;NSM;;;;;N;;;;;
+E01EC;VARIATION SELECTOR-253;Mn;0;NSM;;;;;N;;;;;
+E01ED;VARIATION SELECTOR-254;Mn;0;NSM;;;;;N;;;;;
+E01EE;VARIATION SELECTOR-255;Mn;0;NSM;;;;;N;;;;;
+E01EF;VARIATION SELECTOR-256;Mn;0;NSM;;;;;N;;;;;
+F0000;<Plane 15 Private Use, First>;Co;0;L;;;;;N;;;;;
+FFFFD;<Plane 15 Private Use, Last>;Co;0;L;;;;;N;;;;;
+100000;<Plane 16 Private Use, First>;Co;0;L;;;;;N;;;;;
+10FFFD;<Plane 16 Private Use, Last>;Co;0;L;;;;;N;;;;;
diff --git a/tools/atlas/parse_requires.nim b/tools/atlas/parse_requires.nim
new file mode 100644
index 000000000..66879d04f
--- /dev/null
+++ b/tools/atlas/parse_requires.nim
@@ -0,0 +1,101 @@
+## Utility API for Nim package managers.
+## (c) 2021 Andreas Rumpf
+
+import std / strutils
+import ".." / ".." / compiler / [ast, idents, msgs, syntaxes, options, pathutils]
+
+type
+  NimbleFileInfo* = object
+    requires*: seq[string]
+    srcDir*: string
+    tasks*: seq[(string, string)]
+
+proc extract(n: PNode; conf: ConfigRef; result: var NimbleFileInfo) =
+  case n.kind
+  of nkStmtList, nkStmtListExpr:
+    for child in n:
+      extract(child, conf, result)
+  of nkCallKinds:
+    if n[0].kind == nkIdent:
+      case n[0].ident.s
+      of "requires":
+        for i in 1..<n.len:
+          var ch = n[i]
+          while ch.kind in {nkStmtListExpr, nkStmtList} and ch.len > 0: ch = ch.lastSon
+          if ch.kind in {nkStrLit..nkTripleStrLit}:
+            result.requires.add ch.strVal
+          else:
+            localError(conf, ch.info, "'requires' takes string literals")
+      of "task":
+        if n.len >= 3 and n[1].kind == nkIdent and n[2].kind in {nkStrLit..nkTripleStrLit}:
+          result.tasks.add((n[1].ident.s, n[2].strVal))
+      else: discard
+  of nkAsgn, nkFastAsgn:
+    if n[0].kind == nkIdent and cmpIgnoreCase(n[0].ident.s, "srcDir") == 0:
+      if n[1].kind in {nkStrLit..nkTripleStrLit}:
+        result.srcDir = n[1].strVal
+      else:
+        localError(conf, n[1].info, "assignments to 'srcDir' must be string literals")
+  else:
+    discard
+
+proc extractRequiresInfo*(nimbleFile: string): NimbleFileInfo =
+  ## Extract the `requires` information from a Nimble file. This does **not**
+  ## evaluate the Nimble file. Errors are produced on stderr/stdout and are
+  ## formatted as the Nim compiler does it. The parser uses the Nim compiler
+  ## as an API. The result can be empty, this is not an error, only parsing
+  ## errors are reported.
+  var conf = newConfigRef()
+  conf.foreignPackageNotes = {}
+  conf.notes = {}
+  conf.mainPackageNotes = {}
+
+  let fileIdx = fileInfoIdx(conf, AbsoluteFile nimbleFile)
+  var parser: Parser
+  if setupParser(parser, fileIdx, newIdentCache(), conf):
+    extract(parseAll(parser), conf, result)
+    closeParser(parser)
+
+const Operators* = {'<', '>', '=', '&', '@', '!', '^'}
+
+proc token(s: string; idx: int; lit: var string): int =
+  var i = idx
+  if i >= s.len: return i
+  while s[i] in Whitespace: inc(i)
+  case s[i]
+  of Letters, '#':
+    lit.add s[i]
+    inc i
+    while i < s.len and s[i] notin (Whitespace + {'@', '#'}):
+      lit.add s[i]
+      inc i
+  of '0'..'9':
+    while i < s.len and s[i] in {'0'..'9', '.'}:
+      lit.add s[i]
+      inc i
+  of '"':
+    inc i
+    while i < s.len and s[i] != '"':
+      lit.add s[i]
+      inc i
+    inc i
+  of Operators:
+    while i < s.len and s[i] in Operators:
+      lit.add s[i]
+      inc i
+  else:
+    lit.add s[i]
+    inc i
+  result = i
+
+iterator tokenizeRequires*(s: string): string =
+  var start = 0
+  var tok = ""
+  while start < s.len:
+    tok.setLen 0
+    start = token(s, start, tok)
+    yield tok
+
+when isMainModule:
+  for x in tokenizeRequires("jester@#head >= 1.5 & <= 1.8"):
+    echo x
diff --git a/tools/ci_generate.nim b/tools/ci_generate.nim
new file mode 100644
index 000000000..46bc39470
--- /dev/null
+++ b/tools/ci_generate.nim
@@ -0,0 +1,141 @@
+##[
+avoid code duplication in CI pipelines.
+For now, this is only used for openbsd + freebsd, but there is a lot of other code
+duplication that could be removed.
+
+## usage
+edit this file as needed and then re-generate via:
+```
+nim r tools/ci_generate.nim
+```
+]##
+
+import std/[strformat, os]
+
+const doNotEdit = "DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`"
+proc genCiBsd(header: string, batch: int, num: int): string =
+  result = fmt"""
+## {doNotEdit}
+
+{header}
+
+sources:
+- https://github.com/nim-lang/Nim
+environment:
+  NIM_TESTAMENT_BATCH: "{batch}_{num}"
+  CC: /usr/bin/clang
+tasks:
+- setup: |
+    set -e
+    cd Nim
+    . ci/funs.sh && nimBuildCsourcesIfNeeded
+    echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv
+- test: |
+    set -e
+    cd Nim
+    . ci/funs.sh && nimInternalBuildKochAndRunCI
+triggers:
+- action: email
+  condition: failure
+  to: Andreas Rumpf <rumpf_a@web.de>
+"""
+
+proc genBuildExtras(echoRun, koch, nim: string): string =
+  result = fmt"""
+{echoRun} {nim} c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
+{echoRun} {koch} boot -d:release --skipUserCfg --skipParentCfg --hints:off
+{echoRun} {koch} tools --skipUserCfg --skipParentCfg --hints:off
+"""
+
+proc genWindowsScript(buildAll: bool): string =
+  result = fmt"""
+@echo off
+rem {doNotEdit}
+rem Build development version of the compiler; can be rerun safely
+rem bare bones version of ci/funs.sh adapted for windows.
+
+rem Read in some common shared variables (shared with other tools),
+rem see https://stackoverflow.com/questions/3068929/how-to-read-file-contents-into-a-variable-in-a-batch-file
+for /f "delims== tokens=1,2" %%G in (config/build_config.txt) do set %%G=%%H
+SET nim_csources=bin\nim_csources_%nim_csourcesHash%.exe
+echo "building from csources: %nim_csources%"
+
+if not exist %nim_csourcesDir% (
+  git clone -q --depth 1 -b %nim_csourcesBranch% %nim_csourcesUrl% %nim_csourcesDir%
+)
+
+if not exist %nim_csources% (
+  cd %nim_csourcesDir%
+  git checkout %nim_csourcesHash%
+  echo "%PROCESSOR_ARCHITECTURE%"
+  if "%PROCESSOR_ARCHITECTURE%"=="AMD64" (
+    SET ARCH=64
+  )
+  CALL build.bat
+  cd ..
+  copy /y bin\nim.exe  %nim_csources%
+)
+"""
+
+  if buildAll:
+    result.add genBuildExtras("", "koch", r"bin\nim.exe")
+
+proc genPosixScript(): string =
+  result = fmt"""
+#!/bin/sh
+# {doNotEdit}
+
+# build development version of the compiler; can be rerun safely.
+# arguments can be passed, e.g.:
+# CC=gcc ucpu=amd64 uos=darwin
+
+set -u # error on undefined variables
+set -e # exit on first error
+
+. ci/funs.sh
+nimBuildCsourcesIfNeeded "$@"
+
+{genBuildExtras("echo_run", "./koch", "bin/nim")}
+"""
+
+proc main()=
+  let dir = ".builds"
+  # not too large to be resource friendly, refs bug #17107
+  let num = 2
+    # if you reduce this, make sure to remove files that shouldn't be generated,
+    # or better, do the cleanup logic here e.g.: `rm .builds/openbsd_*`
+  let headerFreebsd = """
+# see https://man.sr.ht/builds.sr.ht/compatibility.md#freebsd
+image: freebsd/latest
+packages:
+- databases/sqlite3
+- devel/boehm-gc-threaded
+- devel/pcre
+- devel/sdl20
+- devel/sfml
+- www/node
+- devel/gmake
+"""
+
+  let headerOpenbsd = """
+image: openbsd/latest
+packages:
+- gmake
+- sqlite3
+- node
+- boehm-gc
+- pcre
+- sfml
+- sdl2
+- libffi
+"""
+
+  for i in 0..<num:
+    writeFile(dir / fmt"openbsd_{i}.yml", genCiBsd(headerOpenbsd, i, num))
+  writeFile(dir / "freebsd.yml", genCiBsd(headerFreebsd, 0, 1))
+  writeFile("build_all.sh", genPosixScript())
+  writeFile("build_all.bat", genWindowsScript(buildAll = true))
+  writeFile("ci/build_autogen.bat", genWindowsScript(buildAll = false))
+
+when isMainModule:
+  main()
diff --git a/tools/ci_testresults.nim b/tools/ci_testresults.nim
new file mode 100644
index 000000000..8c407d6d9
--- /dev/null
+++ b/tools/ci_testresults.nim
@@ -0,0 +1,24 @@
+## Print summary of failed tests for CI
+
+import os, json, sets, strformat
+
+const skip = toHashSet(["reDisabled", "reIgnored", "reSuccess", "reJoined"])
+
+when isMainModule:
+  for fn in walkFiles("testresults/*.json"):
+    let entries = fn.readFile().parseJson()
+    for j in entries:
+      let res = j["result"].getStr()
+      if skip.contains(res):
+        continue
+      echo fmt """
+Category: {j["category"].getStr()}
+Name: {j["name"].getStr()}
+Action: {j["action"].getStr()}
+Result: {res}
+-------- Expected -------
+{j["expected"].getStr()}
+--------- Given  --------
+{j["given"].getStr()}
+-------------------------
+"""
diff --git a/tools/cmerge.nim b/tools/cmerge.nim
index 15fbd8a76..35f2f5555 100644
--- a/tools/cmerge.nim
+++ b/tools/cmerge.nim
@@ -9,7 +9,7 @@ proc process(dir, infile: string, outfile: File,
              processed: var HashSet[string]): ProcessResult =
   if processed.containsOrIncl(infile): return prSkipIncludeDir
   let toProcess = dir / infile
-  if not existsFile(toProcess):
+  if not fileExists(toProcess):
     echo "Warning: could not process: ", toProcess
     return prAddIncludeDir
   echo "adding: ", toProcess
@@ -26,9 +26,9 @@ proc process(dir, infile: string, outfile: File,
 proc main(dir, outfile: string) =
   var o: File
   if open(o, outfile, fmWrite):
-    var processed = initSet[string]()
+    var processed = initHashSet[string]()
     processed.incl(outfile)
-    for infile in walkfiles(dir / "*.c"):
+    for infile in walkFiles(dir / "*.c"):
       discard process(dir, extractFilename(infile), o, processed)
     close(o)
   else:
diff --git a/tools/compiler.gdb b/tools/compiler.gdb
new file mode 100644
index 000000000..c81f47152
--- /dev/null
+++ b/tools/compiler.gdb
@@ -0,0 +1,39 @@
+# create a breakpoint on `debugutils.enteringDebugSection`
+define enable_enteringDebugSection
+	break -function enteringDebugSection
+	# run these commands once breakpoint enteringDebugSection is hit
+	command
+		# enable all breakpoints and watchpoints
+		enable
+		# continue execution
+		cont
+	end
+end
+
+# create a breakpoint on `debugutils.exitingDebugSection` named exitingDebugSection
+define enable_exitingDebugSection
+	break -function exitingDebugSection
+	# run these commands once breakpoint exitingDebugSection is hit
+	command
+		# disable all breakpoints and watchpoints
+		disable
+		# but enable the enteringDebugSection breakpoint
+		enable_enteringDebugSection
+		# continue execution
+		cont
+	end
+end
+
+# some commands can't be set until the process is running, so set an entry breakpoint
+break -function NimMain
+# run these commands once breakpoint NimMain is hit
+command
+	# disable all breakpoints and watchpoints
+	disable
+	# but enable the enteringDebugSection breakpoint
+	enable_enteringDebugSection
+	# no longer need this breakpoint
+	delete -function NimMain
+	# continue execution
+	cont
+end
diff --git a/tools/compiler.lldb b/tools/compiler.lldb
new file mode 100644
index 000000000..e0b375055
--- /dev/null
+++ b/tools/compiler.lldb
@@ -0,0 +1,40 @@
+# create a breakpoint on `debugutils.enteringDebugSection` named enteringDebugSection
+breakpoint set -n 'enteringDebugSection' -N enteringDebugSection
+# run these commands once breakpoint enteringDebugSection is hit
+breakpoint command add enteringDebugSection
+	# enable all breakpoints
+	breakpoint enable
+	# enable all watchpoints
+  # watchpoint enable # FIXME: not currently working for unknown reason
+	# continue execution
+	continue
+DONE
+
+# create a breakpoint on `debugutils.exitingDebugSection` named exitingDebugSection
+breakpoint set -n 'exitingDebugSection' -N exitingDebugSection
+# run these commands once breakpoint exitingDebugSection is hit
+breakpoint command add exitingDebugSection
+	# disable all breakpoints
+	breakpoint disable
+	# disable all watchpoints
+  # watchpoint disable # FIXME: not currently working for unknown reason
+	breakpoint enable enteringDebugSection
+	# continue execution
+	continue
+DONE
+
+# some commands can't be set until the process is running, so set an entry breakpoint
+breakpoint set -n NimMain -N NimMain
+# run these commands once breakpoint NimMain is hit
+breakpoint command add NimMain
+	# disable all breakpoints
+	breakpoint disable
+	# disable all watchpoints
+  # watchpoint disable # FIXME: not currently working for unknown reason
+	# enable the enteringDebugSection breakpoint though
+	breakpoint enable enteringDebugSection
+	# no longer need this breakpoint
+	breakpoint delete NimMain
+	# continue execution
+	continue
+DONE
diff --git a/tools/debug/customdebugtype.nim b/tools/debug/customdebugtype.nim
new file mode 100644
index 000000000..f48979661
--- /dev/null
+++ b/tools/debug/customdebugtype.nim
@@ -0,0 +1,72 @@
+## This is a demo file containing an example of how to
+## create custom LLDB summaries and objects with synthetic
+## children. These are implemented in Nim and called from the Python
+## nimlldb.py module.
+##
+## For summaries, prefix your proc names with "lldbDebugSummary", use
+## the `{.exportc.}` pragma, and return a string. Also, any `$` proc
+## that is available will be used for a given type.
+##
+## For creating a synthetic object (LLDB will display the children), use
+## the prefix "lldbDebugSynthetic", use the `{.exportc.}` pragma, and
+## return any Nim object, array, or sequence. Returning a Nim object
+## will display the fields and values of the object as children.
+## Returning an array or sequence will display children with the index
+## surrounded by square brackets as the key name
+##
+## You may also return a Nim table that contains the string
+## "LLDBDynamicObject" (case insensitive). This allows for dynamic
+## fields to be created at runtime instead of at compile time if you
+## return a Nim object as mentioned above. See the proc
+## `lldbDebugSyntheticDynamicFields` below for an example
+
+import intsets
+import tables
+
+type
+  CustomType* = object of RootObj # RootObj is not necessary, but can be used
+    myField*: int
+
+  DynamicFields* = object
+    customField*: string
+
+  CustomSyntheticReturn* = object
+    differentField*: float
+
+  LLDBDynamicObject = object
+    fields: TableRef[string, int]
+
+  LLDBDynamicObjectDynamicFields = object
+    fields: TableRef[string, string]
+
+proc lldbDebugSummaryCustomType*(ty: CustomType): string {.exportc.} =
+  ## Will display "CustomType(myField: <int_val>)" as a summary
+  result = "CustomType" & $ty
+
+proc lldbDebugSyntheticCustomType*(ty: CustomType): CustomSyntheticReturn {.exportc.} =
+  ## Will display differentField: <float_val> as a child of CustomType instead of
+  ## myField: <int_val>
+  result = CustomSyntheticReturn(differentField: ty.myField.float)
+
+proc lldbDebugSyntheticDynamicFields*(ty: DynamicFields): LLDBDynamicObjectDynamicFields {.exportc.} =
+  ## Returning an object that contains "LLDBDynamicObject" in the type name will expect an
+  ## object with one property that is a Nim Table/TableRef. If the key is a string,
+  ## it will appear in the debugger like an object field name. The value will be whatever you
+  ## set it to here as well.
+  let fields = {"customFieldName": ty.customField & " MORE TEXT"}.newTable()
+  return LLDBDynamicObjectDynamicFields(fields: fields)
+
+proc lldbDebugSummaryIntSet*(intset: IntSet): string {.exportc.} =
+  ## This will print the object in the LLDB summary just as Nim prints it
+  result = $intset
+
+proc lldbDebugSyntheticIntSet*(intset: IntSet): seq[int] {.exportc.} =
+  ## This will create a synthetic object to make it so that IntSet
+  ## will appear as a Nim object in the LLDB debugger window
+  ##
+  ## returning a seq here will display children like:
+  ## [0]: <child_value>
+  ##
+  result = newSeqOfCap[int](intset.len)
+  for val in intset:
+    result.add(val)
diff --git a/tools/debug/nim-gdb.py b/tools/debug/nim-gdb.py
new file mode 100644
index 000000000..8c9854bda
--- /dev/null
+++ b/tools/debug/nim-gdb.py
@@ -0,0 +1,697 @@
+import gdb
+import re
+import sys
+import traceback
+
+# some feedback that the nim runtime support is loading, isn't a bad
+# thing at all.
+gdb.write("Loading Nim Runtime support.\n", gdb.STDERR)
+
+# When error occure they occur regularly. This 'caches' known errors
+# and prevents them from being reprinted over and over again.
+errorSet = set()
+def printErrorOnce(id, message):
+  global errorSet
+  if id not in errorSet:
+    errorSet.add(id)
+    gdb.write("printErrorOnce: " + message, gdb.STDERR)
+
+def debugPrint(x):
+  gdb.write(str(x) + "\n", gdb.STDERR)
+
+NIM_STRING_TYPES = ["NimStringDesc", "NimStringV2"]
+
+################################################################################
+#####  Type pretty printers
+################################################################################
+
+type_hash_regex = re.compile("^([A-Za-z0-9]*)_([A-Za-z0-9]*)_+([A-Za-z0-9]*)$")
+
+def getNimName(typ):
+  if m := type_hash_regex.match(typ):
+    return m.group(2)
+  return f"unknown <{typ}>"
+
+def getNimRti(type_name):
+  """ Return a ``gdb.Value`` object for the Nim Runtime Information of ``type_name``. """
+
+  # Get static const TNimType variable. This should be available for
+  # every non trivial Nim type.
+  m = type_hash_regex.match(type_name)
+  if m:
+    lookups = [
+      "NTI" + m.group(2).lower() + "__" + m.group(3) + "_",
+      "NTI" + "__" + m.group(3) + "_",
+      "NTI" + m.group(2).replace("colon", "58").lower() + "__" + m.group(3) + "_"
+      ]
+    for l in lookups:
+      try:
+        return gdb.parse_and_eval(l)
+      except:
+        pass
+  None
+
+def getNameFromNimRti(rti):
+  """ Return name (or None) given a Nim RTI ``gdb.Value`` """
+  try:
+    # sometimes there isn't a name field -- example enums
+    return rti['name'].string(encoding="utf-8", errors="ignore")
+  except:
+    return None
+
+class NimTypeRecognizer:
+  # this type map maps from types that are generated in the C files to
+  # how they are called in nim. To not mix up the name ``int`` from
+  # system.nim with the name ``int`` that could still appear in
+  # generated code, ``NI`` is mapped to ``system.int`` and not just
+  # ``int``.
+
+  type_map_static = {
+    'NI': 'system.int',  'NI8': 'int8', 'NI16': 'int16',  'NI32': 'int32',
+    'NI64': 'int64',
+    
+    'NU': 'uint', 'NU8': 'uint8','NU16': 'uint16', 'NU32': 'uint32',
+    'NU64': 'uint64',
+    
+    'NF': 'float', 'NF32': 'float32', 'NF64': 'float64',
+    
+    'NIM_BOOL': 'bool',
+
+    'NIM_CHAR': 'char', 'NCSTRING': 'cstring', 'NimStringDesc': 'string', 'NimStringV2': 'string'
+  }
+
+  # object_type_pattern = re.compile("^(\w*):ObjectType$")
+
+  def recognize(self, type_obj):
+    # skip things we can't handle like functions
+    if type_obj.code in [gdb.TYPE_CODE_FUNC, gdb.TYPE_CODE_VOID]:
+      return None
+
+    tname = None
+    if type_obj.tag is not None:
+      tname = type_obj.tag
+    elif type_obj.name is not None:
+      tname = type_obj.name
+
+    # handle pointer types
+    if not tname:
+      target_type = type_obj
+      if type_obj.code in [gdb.TYPE_CODE_PTR]:
+        target_type = type_obj.target()
+
+      if target_type.name:
+        # visualize 'string' as non pointer type (unpack pointer type).
+        if target_type.name == "NimStringDesc":
+          tname = target_type.name # could also just return 'string'
+        else:
+          rti = getNimRti(target_type.name)
+          if rti:
+            return getNameFromNimRti(rti)
+
+    if tname:
+      result = self.type_map_static.get(tname, None)
+      if result:
+        return result
+      elif tname.startswith("tyEnum_"):
+        return getNimName(tname)
+      elif tname.startswith("tyTuple__"):
+        # We make the name be the field types (Just like in Nim)
+        fields = ", ".join([self.recognize(field.type) for field in type_obj.fields()])
+        return f"({fields})"
+
+      rti = getNimRti(tname)
+      if rti:
+        return getNameFromNimRti(rti)
+
+    return None
+
+class NimTypePrinter:
+  """Nim type printer. One printer for all Nim types."""
+
+  # enabling and disabling of type printers can be done with the
+  # following gdb commands:
+  #
+  #   enable  type-printer NimTypePrinter
+  #   disable type-printer NimTypePrinter
+  # relevant docs: https://sourceware.org/gdb/onlinedocs/gdb/Type-Printing-API.html
+
+  name = "NimTypePrinter"
+
+  def __init__(self):
+    self.enabled = True
+
+  def instantiate(self):
+    return NimTypeRecognizer()
+
+################################################################################
+#####  GDB Function, equivalent of Nim's $ operator
+################################################################################
+
+class DollarPrintFunction (gdb.Function):
+  "Nim's equivalent of $ operator as a gdb function, available in expressions `print $dollar(myvalue)"
+
+  dollar_functions = re.findall(
+    r'(?:NimStringDesc \*|NimStringV2)\s?(dollar__[A-z0-9_]+?)\(([^,)]*)\);',
+    gdb.execute("info functions dollar__", True, True)
+  )
+
+  def __init__ (self):
+    super (DollarPrintFunction, self).__init__("dollar")
+
+
+  @staticmethod
+  def invoke_static(arg, ignore_errors = False):
+    if arg.type.code == gdb.TYPE_CODE_PTR and arg.type.target().name in NIM_STRING_TYPES:
+      return arg
+    argTypeName = str(arg.type)
+    for func, arg_typ in DollarPrintFunction.dollar_functions:
+      # this way of overload resolution cannot deal with type aliases,
+      # therefore it won't find all overloads.
+      if arg_typ == argTypeName:
+        func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTION_DOMAIN).value()
+        return func_value(arg)
+
+      elif arg_typ == argTypeName + " *":
+        func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTION_DOMAIN).value()
+        return func_value(arg.address)
+
+    if not ignore_errors:
+      debugPrint(f"No suitable Nim $ operator found for type: {getNimName(argTypeName)}\n")
+    return None
+
+  def invoke(self, arg):
+    return self.invoke_static(arg)
+
+DollarPrintFunction()
+
+
+################################################################################
+#####  GDB Function, Nim string comparison
+################################################################################
+
+class NimStringEqFunction (gdb.Function):
+  """Compare Nim strings for example in conditionals for breakpoints."""
+
+  def __init__ (self):
+    super (NimStringEqFunction, self).__init__("nimstreq")
+
+  @staticmethod
+  def invoke_static(arg1,arg2):
+    if arg1.type.code == gdb.TYPE_CODE_PTR and arg1.type.target().name in NIM_STRING_TYPES:
+      str1 = NimStringPrinter(arg1).to_string()
+    else:
+      str1 = arg1.string()
+    if arg2.type.code == gdb.TYPE_CODE_PTR and arg2.type.target().name in NIM_STRING_TYPES:
+      str2 = NimStringPrinter(arg1).to_string()
+    else:
+      str2 = arg2.string()
+
+    return str1 == str2
+
+  def invoke(self, arg1, arg2):
+    return self.invoke_static(arg1, arg2)
+
+NimStringEqFunction()
+
+
+################################################################################
+#####  GDB Command, equivalent of Nim's $ operator
+################################################################################
+
+class DollarPrintCmd (gdb.Command):
+  """Dollar print command for Nim, `$ expr` will invoke Nim's $ operator and print the result."""
+
+  def __init__ (self):
+    super (DollarPrintCmd, self).__init__ ("$", gdb.COMMAND_DATA, gdb.COMPLETE_EXPRESSION)
+
+  def invoke(self, arg, from_tty):
+    param = gdb.parse_and_eval(arg)
+    strValue = DollarPrintFunction.invoke_static(param)
+    if strValue:
+      gdb.write(
+        str(NimStringPrinter(strValue)) + "\n",
+        gdb.STDOUT
+      )
+
+    # could not find a suitable dollar overload. This here is the
+    # fallback to get sensible output of basic types anyway.
+
+    elif param.type.code == gdb.TYPE_CODE_ARRAY and param.type.target().name == "char":
+      gdb.write(param.string("utf-8", "ignore") + "\n", gdb.STDOUT)
+    elif param.type.code == gdb.TYPE_CODE_INT:
+      gdb.write(str(int(param)) + "\n", gdb.STDOUT)
+    elif param.type.name == "NIM_BOOL":
+      if int(param) != 0:
+        gdb.write("true\n", gdb.STDOUT)
+      else:
+        gdb.write("false\n", gdb.STDOUT)
+
+DollarPrintCmd()
+
+
+################################################################################
+#####  GDB Commands to invoke common nim tools.
+################################################################################
+
+
+import subprocess, os
+
+
+class KochCmd (gdb.Command):
+  """Command that invokes ``koch'', the build tool for the compiler."""
+
+  def __init__ (self):
+    super (KochCmd, self).__init__ ("koch",
+                                    gdb.COMMAND_USER, gdb.COMPLETE_FILENAME)
+    self.binary = os.path.join(
+      os.path.dirname(os.path.dirname(__file__)), "koch")
+
+  def invoke(self, argument, from_tty):
+    subprocess.run([self.binary] + gdb.string_to_argv(argument))
+
+KochCmd()
+
+
+class NimCmd (gdb.Command):
+  """Command that invokes ``nim'', the nim compiler."""
+
+  def __init__ (self):
+    super (NimCmd, self).__init__ ("nim",
+                                   gdb.COMMAND_USER, gdb.COMPLETE_FILENAME)
+    self.binary = os.path.join(
+      os.path.dirname(os.path.dirname(__file__)), "bin/nim")
+
+  def invoke(self, argument, from_tty):
+    subprocess.run([self.binary] + gdb.string_to_argv(argument))
+
+NimCmd()
+
+
+class NimbleCmd (gdb.Command):
+  """Command that invokes ``nimble'', the nim package manager and build tool."""
+
+  def __init__ (self):
+    super (NimbleCmd, self).__init__ ("nimble",
+                                      gdb.COMMAND_USER, gdb.COMPLETE_FILENAME)
+    self.binary = os.path.join(
+      os.path.dirname(os.path.dirname(__file__)), "bin/nimble")
+
+  def invoke(self, argument, from_tty):
+    subprocess.run([self.binary] + gdb.string_to_argv(argument))
+
+NimbleCmd()
+
+################################################################################
+#####  Value pretty printers
+################################################################################
+
+class NimBoolPrinter:
+
+  pattern = re.compile(r'^NIM_BOOL$')
+
+  def __init__(self, val):
+    self.val = val
+
+  def to_string(self):
+    if self.val == 0:
+      return "false"
+    else:
+      return "true"
+
+################################################################################
+
+def strFromLazy(strVal):
+  if isinstance(strVal, str):
+    return strVal
+  else:
+    return strVal.value().string("utf-8")
+
+class NimStringPrinter:
+  pattern = re.compile(r'^(NimStringDesc \*|NimStringV2)$')
+
+  def __init__(self, val):
+    self.val = val
+
+  def display_hint(self):
+    return 'string'
+
+  def to_string(self):
+    if self.val:
+      if self.val.type.name == "NimStringV2":
+        l = int(self.val["len"])
+        data = self.val["p"]["data"]
+      else:
+        l = int(self.val['Sup']['len'])
+        data = self.val["data"]
+      return data.lazy_string(encoding="utf-8", length=l)
+    else:
+      return ""
+
+  def __str__(self):
+    return strFromLazy(self.to_string())
+
+class NimRopePrinter:
+  pattern = re.compile(r'^tyObject_RopeObj__([A-Za-z0-9]*) \*$')
+
+  def __init__(self, val):
+    self.val = val
+
+  def display_hint(self):
+    return 'string'
+
+  def to_string(self):
+    if self.val:
+      left  = NimRopePrinter(self.val["left"]).to_string()
+      data  = NimStringPrinter(self.val["data"]).to_string()
+      right = NimRopePrinter(self.val["right"]).to_string()
+      return left + data + right
+    else:
+      return ""
+
+
+################################################################################
+
+def reprEnum(e, typ):
+  # Casts the value to the enum type and then calls the enum printer
+  e = int(e)
+  val = gdb.Value(e).cast(typ)
+  return strFromLazy(NimEnumPrinter(val).to_string())
+
+def enumNti(typeNimName, idString):
+  typeInfoName = "NTI" + typeNimName.lower() + "__" + idString + "_"
+  nti = gdb.lookup_global_symbol(typeInfoName)
+  if nti is None:
+    typeInfoName = "NTI" + "__" + idString + "_"
+    nti = gdb.lookup_global_symbol(typeInfoName)
+  return (typeInfoName, nti)
+
+class NimEnumPrinter:
+  pattern = re.compile(r'^tyEnum_([A-Za-z0-9]+)__([A-Za-z0-9]*)$')
+  enumReprProc = gdb.lookup_global_symbol("reprEnum", gdb.SYMBOL_FUNCTION_DOMAIN)
+
+  def __init__(self, val):
+    self.val = val
+    typeName = self.val.type.name
+    match = self.pattern.match(typeName)
+    self.typeNimName  = match.group(1)
+    typeInfoName, self.nti = enumNti(self.typeNimName, match.group(2))
+
+  def to_string(self):
+    if NimEnumPrinter.enumReprProc and self.nti:
+      # Use the old runtimes enumRepr function.
+      # We call the Nim proc itself so that the implementation is correct
+      f = gdb.newest_frame()
+      # We need to strip the quotes so it looks like an enum instead of a string
+      reprProc = NimEnumPrinter.enumReprProc.value()
+      return str(reprProc(self.val, self.nti.value(f).address)).strip('"')
+    elif dollarResult := DollarPrintFunction.invoke_static(self.val):
+      # New runtime doesn't use enumRepr so we instead try and call the
+      # dollar function for it
+      return str(NimStringPrinter(dollarResult))
+    else:
+      return self.typeNimName + "(" + str(int(self.val)) + ")"
+
+################################################################################
+
+class NimSetPrinter:
+  ## the set printer is limited to sets that fit in an integer.  Other
+  ## sets are compiled to `NU8 *` (ptr uint8) and are invisible to
+  ## gdb (currently).
+  pattern = re.compile(r'^tySet_tyEnum_([A-Za-z0-9]+)__([A-Za-z0-9]*)$')
+
+  def __init__(self, val):
+    self.val = val
+    typeName = self.val.type.name
+    match = self.pattern.match(typeName)
+    self.typeNimName = match.group(1)
+
+  def to_string(self):
+    # Remove the tySet from the type name
+    typ = gdb.lookup_type(self.val.type.name[6:])
+    enumStrings = []
+    val = int(self.val)
+    i   = 0
+    while val > 0:
+      if (val & 1) == 1:
+        enumStrings.append(reprEnum(i, typ))
+      val = val >> 1
+      i += 1
+
+    return '{' + ', '.join(enumStrings) + '}'
+
+################################################################################
+
+class NimHashSetPrinter:
+  pattern = re.compile(r'^tyObject_(HashSet)__([A-Za-z0-9]*)$')
+
+  def __init__(self, val):
+    self.val = val
+
+  def display_hint(self):
+    return 'array'
+
+  def to_string(self):
+    counter  = 0
+    capacity = 0
+    if self.val:
+      counter  = int(self.val['counter'])
+      if self.val['data']:
+        capacity = int(self.val['data']['Sup']['len'])
+
+    return 'HashSet({0}, {1})'.format(counter, capacity)
+
+  def children(self):
+    if self.val:
+      data = NimSeqPrinter(self.val['data'])
+      for idxStr, entry in data.children():
+        if int(entry['Field0']) > 0:
+          yield ("data." + idxStr + ".Field1", str(entry['Field1']))
+
+################################################################################
+
+class NimSeq:
+  # Wrapper around sequences.
+  # This handles the differences between old and new runtime
+
+  def __init__(self, val):
+    self.val = val
+    # new runtime has sequences on stack, old has them on heap
+    self.new = val.type.code != gdb.TYPE_CODE_PTR
+    if self.new:
+      # Some seqs are just the content and to save repeating ourselves we do
+      # handle them here. Only thing that needs to check this is the len/data getters
+      self.isContent = val.type.name.endswith("Content")
+
+  def __bool__(self):
+    if self.new:
+      return self.val is not None
+    else:
+      return bool(self.val)
+
+  def __len__(self):
+    if not self:
+      return 0
+    if self.new:
+      if self.isContent:
+        return int(self.val["cap"])
+      else:
+        return int(self.val["len"])
+    else:
+      return self.val["Sup"]["len"]
+
+  @property
+  def data(self):
+    if self.new:
+      if self.isContent:
+        return self.val["data"]
+      elif self.val["p"]:
+        return self.val["p"]["data"]
+    else:
+      return self.val["data"]
+
+  @property
+  def cap(self):
+    if not self:
+      return 0
+    if self.new:
+      if self.isContent:
+        return int(self.val["cap"])
+      elif self.val["p"]:
+        return int(self.val["p"]["cap"])
+      else:
+        return 0
+    return int(self.val['Sup']['reserved'])
+
+class NimSeqPrinter:
+  pattern = re.compile(r'^tySequence_\w*\s?\*?$')
+
+  def __init__(self, val):
+    self.val = NimSeq(val)
+
+
+  def display_hint(self):
+    return 'array'
+
+  def to_string(self):
+    return f'seq({len(self.val)}, {self.val.cap})'
+
+  def children(self):
+    if self.val:
+      val = self.val
+      length = len(val)
+
+      if length <= 0:
+        return
+
+      data = val.data
+
+      inaccessible = False
+      for i in range(length):
+        if inaccessible:
+          return
+        try:
+          str(data[i])
+          yield "data[{0}]".format(i), data[i]
+        except RuntimeError:
+          inaccessible = True
+          yield "data[{0}]".format(i), "inaccessible"
+      
+################################################################################
+
+class NimArrayPrinter:
+  pattern = re.compile(r'^tyArray_\w*$')
+
+  def __init__(self, val):
+    self.val = val
+
+  def display_hint(self):
+    return 'array'
+
+  def to_string(self):
+    return 'array'
+
+  def children(self):
+    length = self.val.type.sizeof // self.val[0].type.sizeof
+    align = len(str(length-1))
+    for i in range(length):
+      yield ("[{0:>{1}}]".format(i, align), self.val[i])
+
+################################################################################
+
+class NimStringTablePrinter:
+  pattern = re.compile(r'^tyObject_(StringTableObj)__([A-Za-z0-9]*)(:? \*)?$')
+
+  def __init__(self, val):
+    self.val = val
+
+  def display_hint(self):
+    return 'map'
+
+  def to_string(self):
+    counter  = 0
+    capacity = 0
+    if self.val:
+      counter  = int(self.val['counter'])
+      if self.val['data']:
+        capacity = int(self.val['data']['Sup']['len'])
+
+    return 'StringTableObj({0}, {1})'.format(counter, capacity)
+
+  def children(self):
+    if self.val:
+      data = NimSeqPrinter(self.val['data'].referenced_value())
+      for idxStr, entry in data.children():
+        if int(entry['Field0']) != 0:
+          yield (idxStr + ".Field0", entry['Field0'])
+          yield (idxStr + ".Field1", entry['Field1'])
+
+################################################################
+
+class NimTablePrinter:
+  pattern = re.compile(r'^tyObject_(Table)__([A-Za-z0-9]*)(:? \*)?$')
+
+  def __init__(self, val):
+    self.val = val
+
+  def display_hint(self):
+    return 'map'
+
+  def to_string(self):
+    counter  = 0
+    capacity = 0
+    if self.val:
+      counter  = int(self.val['counter'])
+      if self.val['data']:
+        capacity = NimSeq(self.val["data"]).cap
+
+    return 'Table({0}, {1})'.format(counter, capacity)
+
+  def children(self):
+    if self.val:
+      data = NimSeqPrinter(self.val['data'])
+      for idxStr, entry in data.children():
+        if int(entry['Field0']) != 0:
+          yield (idxStr + '.Field1', entry['Field1'])
+          yield (idxStr + '.Field2', entry['Field2'])
+
+################################################################################
+
+class NimTuplePrinter:
+  pattern = re.compile(r"^tyTuple__([A-Za-z0-9]*)")
+
+  def __init__(self, val):
+    self.val = val
+
+  def to_string(self):
+    # We don't have field names so just print out the tuple as if it was anonymous
+    tupleValues = [str(self.val[field.name]) for field in self.val.type.fields()]
+    return f"({', '.join(tupleValues)})"
+
+################################################################################
+
+class NimFrameFilter:
+  def __init__(self):
+    self.name = "nim-frame-filter"
+    self.enabled = True
+    self.priority = 100
+    self.hidden =  {"NimMainInner","NimMain", "main"}
+
+  def filter(self, iterator):
+    for framedecorator in iterator:
+      if framedecorator.function() not in self.hidden:
+        yield framedecorator
+
+################################################################################
+
+def makematcher(klass):
+  def matcher(val):
+    typeName = str(val.type)
+    try:
+      if hasattr(klass, 'pattern') and hasattr(klass, '__name__'):
+        # print(typeName + " <> " + klass.__name__)
+        if klass.pattern.match(typeName):
+          return klass(val)
+    except Exception as e:
+      print(klass)
+      printErrorOnce(typeName, "No matcher for type '" + typeName + "': " + str(e) + "\n")
+  return matcher
+
+def register_nim_pretty_printers_for_object(objfile):
+  nimMainSym = gdb.lookup_global_symbol("NimMain", gdb.SYMBOL_FUNCTION_DOMAIN)
+  if nimMainSym and nimMainSym.symtab.objfile == objfile:
+    print("set Nim pretty printers for ", objfile.filename)
+
+    gdb.types.register_type_printer(objfile, NimTypePrinter())
+    objfile.pretty_printers = [makematcher(var) for var in list(globals().values()) if hasattr(var, 'pattern')]
+
+# Register pretty printers for all objfiles that are already loaded.
+for old_objfile in gdb.objfiles():
+  register_nim_pretty_printers_for_object(old_objfile)
+
+# Register an event handler to register nim pretty printers for all future objfiles.
+def new_object_handler(event):
+  register_nim_pretty_printers_for_object(event.new_objfile)
+
+gdb.events.new_objfile.connect(new_object_handler)
+
+gdb.frame_filters = {"nim-frame-filter": NimFrameFilter()}
diff --git a/tools/debug/nimlldb.py b/tools/debug/nimlldb.py
new file mode 100644
index 000000000..4bc4e771f
--- /dev/null
+++ b/tools/debug/nimlldb.py
@@ -0,0 +1,1380 @@
+import lldb
+from collections import OrderedDict
+from typing import Union
+
+
+def sbvaluegetitem(self: lldb.SBValue, name: Union[int, str]) -> lldb.SBValue:
+    if isinstance(name, str):
+        return self.GetChildMemberWithName(name)
+    else:
+        return self.GetChildAtIndex(name)
+
+
+# Make this easier to work with
+lldb.SBValue.__getitem__ = sbvaluegetitem
+
+NIM_IS_V2 = True
+
+
+def get_nti(value: lldb.SBValue, nim_name=None):
+    name_split = value.type.name.split("_")
+    type_nim_name = nim_name or name_split[1]
+    id_string = name_split[-1].split(" ")[0]
+
+    type_info_name = "NTI" + type_nim_name.lower() + "__" + id_string + "_"
+    nti = value.target.FindFirstGlobalVariable(type_info_name)
+    if not nti.IsValid():
+        type_info_name = "NTI" + "__" + id_string + "_"
+        nti = value.target.FindFirstGlobalVariable(type_info_name)
+    if not nti.IsValid():
+        print(f"NimEnumPrinter: lookup global symbol: '{type_info_name}' failed for {value.type.name}.\n")
+    return type_nim_name, nti
+
+
+def enum_to_string(value: lldb.SBValue, int_val=None, nim_name=None):
+    tname = nim_name or value.type.name.split("_")[1]
+
+    enum_val = value.signed
+    if int_val is not None:
+        enum_val = int_val
+
+    default_val = f"{tname}.{str(enum_val)}"
+
+    fn_syms = value.target.FindFunctions("reprEnum")
+    if not fn_syms.GetSize() > 0:
+        return default_val
+
+    fn_sym: lldb.SBSymbolContext = fn_syms.GetContextAtIndex(0)
+
+    fn: lldb.SBFunction = fn_sym.function
+
+    fn_type: lldb.SBType = fn.type
+    arg_types: lldb.SBTypeList = fn_type.GetFunctionArgumentTypes()
+    if arg_types.GetSize() < 2:
+        return default_val
+
+    arg1_type: lldb.SBType = arg_types.GetTypeAtIndex(0)
+    arg2_type: lldb.SBType = arg_types.GetTypeAtIndex(1)
+
+    ty_info_name, nti = get_nti(value, nim_name=tname)
+
+    if not nti.IsValid():
+        return default_val
+
+    call = f"{fn.name}(({arg1_type.name}){enum_val}, ({arg2_type.name})" + str(nti.GetLoadAddress()) + ");"
+
+    res = executeCommand(call)
+
+    if res.error.fail:
+        return default_val
+
+    return f"{tname}.{res.summary[1:-1]}"
+
+
+def to_string(value: lldb.SBValue):
+    # For getting NimStringDesc * value
+    value = value.GetNonSyntheticValue()
+
+    # Check if data pointer is Null
+    if value.type.is_pointer and value.unsigned == 0:
+        return None
+
+    size = int(value["Sup"]["len"].unsigned)
+
+    if size == 0:
+        return ""
+
+    if size > 2**14:
+        return "... (too long) ..."
+
+    data = value["data"]
+
+    # Check if first element is NULL
+    base_data_type = value.target.FindFirstType("char")
+    cast = data.Cast(base_data_type)
+
+    if cast.unsigned == 0:
+        return None
+
+    cast = data.Cast(value.target.FindFirstType("char").GetArrayType(size))
+    return bytearray(cast.data.uint8s).decode("utf-8")
+
+
+def to_stringV2(value: lldb.SBValue):
+    # For getting NimStringV2 value
+    value = value.GetNonSyntheticValue()
+
+    data = value["p"]["data"]
+
+    # Check if data pointer is Null
+    if value["p"].unsigned == 0:
+        return None
+
+    size = int(value["len"].signed)
+
+    if size == 0:
+        return ""
+
+    if size > 2**14:
+        return "... (too long) ..."
+
+    # Check if first element is NULL
+    base_data_type = data.type.GetArrayElementType().GetTypedefedType()
+    cast = data.Cast(base_data_type)
+
+    if cast.unsigned == 0:
+        return None
+
+    cast = data.Cast(base_data_type.GetArrayType(size))
+    return bytearray(cast.data.uint8s).decode("utf-8")
+
+
+def NimString(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    if NIM_IS_V2:
+        res = to_stringV2(value)
+    else:
+        res = to_string(value)
+
+    if res is not None:
+        return f'"{res}"'
+    else:
+        return "nil"
+
+
+def rope_helper(value: lldb.SBValue) -> str:
+    value = value.GetNonSyntheticValue()
+    if value.type.is_pointer and value.unsigned == 0:
+        return ""
+
+    if value["length"].unsigned == 0:
+        return ""
+
+    if NIM_IS_V2:
+        str_val = to_stringV2(value["data"])
+    else:
+        str_val = to_string(value["data"])
+
+    if str_val is None:
+        str_val = ""
+
+    return rope_helper(value["left"]) + str_val + rope_helper(value["right"])
+
+
+def Rope(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    rope_str = rope_helper(value)
+
+    if len(rope_str) == 0:
+        rope_str = "nil"
+    else:
+        rope_str = f'"{rope_str}"'
+
+    return f"Rope({rope_str})"
+
+
+def NCSTRING(value: lldb.SBValue, internal_dict=None):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    ty = value.Dereference().type
+    val = value.target.CreateValueFromAddress(
+        value.name or "temp", lldb.SBAddress(value.unsigned, value.target), ty
+    ).AddressOf()
+    return val.summary
+
+
+def ObjectV2(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    orig_value = value.GetNonSyntheticValue()
+    if orig_value.type.is_pointer and orig_value.unsigned == 0:
+        return "nil"
+
+    custom_summary = get_custom_summary(value)
+    if custom_summary is not None:
+        return custom_summary
+
+    while orig_value.type.is_pointer:
+        orig_value = orig_value.Dereference()
+
+    if "_" in orig_value.type.name:
+        obj_name = orig_value.type.name.split("_")[1].replace("colonObjectType", "")
+    else:
+        obj_name = orig_value.type.name
+
+    num_children = value.num_children
+    fields = []
+
+    for i in range(num_children):
+        fields.append(f"{value[i].name}: {value[i].summary}")
+
+    res = f"{obj_name}(" + ", ".join(fields) + ")"
+    return res
+
+
+def Number(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    if value.type.is_pointer and value.signed == 0:
+        return "nil"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    return str(value.signed)
+
+
+def Float(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    return str(value.value)
+
+
+def UnsignedNumber(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    return str(value.unsigned)
+
+
+def Bool(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    return str(value.value)
+
+
+def CharArray(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    return str([f"'{char}'" for char in value.uint8s])
+
+
+def Array(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    value = value.GetNonSyntheticValue()
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    value = value.GetNonSyntheticValue()
+    return "[" + ", ".join([value[i].summary for i in range(value.num_children)]) + "]"
+
+
+def Tuple(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    while value.type.is_pointer:
+        value = value.Dereference()
+
+    num_children = value.num_children
+
+    fields = []
+
+    for i in range(num_children):
+        key = value[i].name
+        val = value[i].summary
+        if key.startswith("Field"):
+            fields.append(f"{val}")
+        else:
+            fields.append(f"{key}: {val}")
+
+    return "(" + ", ".join(fields) + f")"
+
+
+def is_local(value: lldb.SBValue) -> bool:
+    line: lldb.SBLineEntry = value.frame.GetLineEntry()
+    decl: lldb.SBDeclaration = value.GetDeclaration()
+
+    if line.file == decl.file and decl.line != 0:
+        return True
+
+    return False
+
+
+def is_in_scope(value: lldb.SBValue) -> bool:
+    line: lldb.SBLineEntry = value.frame.GetLineEntry()
+    decl: lldb.SBDeclaration = value.GetDeclaration()
+
+    if is_local(value) and decl.line < line.line:
+        return True
+
+    return False
+
+
+def Enum(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_value_summary(value)
+    if custom_summary is not None:
+        return custom_summary
+
+    return enum_to_string(value)
+
+
+def EnumSet(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    vals = []
+    max_vals = 7
+    for child in value.children:
+        vals.append(child.summary)
+        if len(vals) > max_vals:
+            vals.append("...")
+            break
+
+    return "{" + ", ".join(vals) + "}"
+
+
+def Set(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if custom_summary is not None:
+        return custom_summary
+
+    vals = []
+    max_vals = 7
+    for child in value.children:
+        vals.append(child.value)
+        if len(vals) > max_vals:
+            vals.append("...")
+            break
+
+    return "{" + ", ".join(vals) + "}"
+
+
+def Table(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if custom_summary is not None:
+        return custom_summary
+
+    fields = []
+
+    for i in range(value.num_children):
+        key = value[i].name
+        val = value[i].summary
+        fields.append(f"{key}: {val}")
+
+    return "Table({" + ", ".join(fields) + "})"
+
+
+def HashSet(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if custom_summary is not None:
+        return custom_summary
+
+    fields = []
+
+    for i in range(value.num_children):
+        fields.append(f"{value[i].summary}")
+
+    return "HashSet({" + ", ".join(fields) + "})"
+
+
+def StringTable(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    fields = []
+
+    for i in range(value.num_children - 1):
+        key = value[i].name
+        val = value[i].summary
+        fields.append(f"{key}: {val}")
+
+    mode = value[value.num_children - 1].summary
+
+    return "StringTable({" + ", ".join(fields) + f"}}, mode={mode})"
+
+
+def Sequence(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    return "@[" + ", ".join([value[i].summary for i in range(value.num_children)]) + "]"
+
+
+class StringChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.data_type: lldb.SBType
+        if not NIM_IS_V2:
+            self.data_type = self.value.target.FindFirstType("char")
+
+        self.first_element: lldb.SBValue
+        self.update()
+        self.count = 0
+
+    def num_children(self):
+        return self.count
+
+    def get_child_index(self, name):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        offset = index * self.data_size
+        return self.first_element.CreateChildAtOffset("[" + str(index) + "]", offset, self.data_type)
+
+    def get_data(self) -> lldb.SBValue:
+        return self.value["p"]["data"] if NIM_IS_V2 else self.value["data"]
+
+    def get_len(self) -> int:
+        if NIM_IS_V2:
+            if self.value["p"].unsigned == 0:
+                return 0
+
+            size = int(self.value["len"].signed)
+
+            if size == 0:
+                return 0
+
+            data = self.value["p"]["data"]
+
+            # Check if first element is NULL
+            base_data_type = data.type.GetArrayElementType().GetTypedefedType()
+            cast = data.Cast(base_data_type)
+
+            if cast.unsigned == 0:
+                return 0
+        else:
+            if self.value.type.is_pointer and self.value.unsigned == 0:
+                return 0
+
+            size = int(self.value["Sup"]["len"].unsigned)
+
+            if size == 0:
+                return 0
+
+            data = self.value["data"]
+
+            # Check if first element is NULL
+            base_data_type = self.value.target.FindFirstType("char")
+            cast = data.Cast(base_data_type)
+
+            if cast.unsigned == 0:
+                return 0
+
+        return size
+
+    def update(self):
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        data = self.get_data()
+        size = self.get_len()
+
+        self.count = size
+        self.first_element = data
+
+        if NIM_IS_V2:
+            self.data_type = data.type.GetArrayElementType().GetTypedefedType()
+
+        self.data_size = self.data_type.GetByteSize()
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class ArrayChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.data_type: lldb.SBType
+        self.first_element: lldb.SBValue
+        self.update()
+
+    def num_children(self):
+        return self.has_children() and self.value.num_children
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        offset = index * self.value[index].GetByteSize()
+        return self.first_element.CreateChildAtOffset("[" + str(index) + "]", offset, self.data_type)
+
+    def update(self):
+        if not self.has_children():
+            return
+
+        self.first_element = self.value[0]
+        self.data_type = self.value.type.GetArrayElementType()
+
+    def has_children(self):
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return False
+        return bool(self.value.num_children)
+
+
+class SeqChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.data_type: lldb.SBType
+        self.first_element: lldb.SBValue
+        self.data: lldb.SBValue
+        self.count = 0
+        self.update()
+
+    def num_children(self):
+        return self.count
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        offset = index * self.data[index].GetByteSize()
+        return self.first_element.CreateChildAtOffset("[" + str(index) + "]", offset, self.data_type)
+
+    def get_data(self) -> lldb.SBValue:
+        return self.value["p"]["data"] if NIM_IS_V2 else self.value["data"]
+
+    def get_len(self) -> lldb.SBValue:
+        return self.value["len"] if NIM_IS_V2 else self.value["Sup"]["len"]
+
+    def update(self):
+        self.count = 0
+
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        self.count = self.get_len().unsigned
+
+        if not self.has_children():
+            return
+
+        data = self.get_data()
+        self.data_type = data.type.GetArrayElementType()
+
+        self.data = data.Cast(self.data_type.GetArrayType(self.num_children()))
+        self.first_element = self.data
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class ObjectChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.data_type: lldb.SBType
+        self.first_element: lldb.SBValue
+        self.data: lldb.SBValue
+        self.children: OrderedDict[str, int] = OrderedDict()
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.children)
+
+    def get_child_index(self, name: str):
+        return self.children[name]
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def populate_children(self):
+        self.children.clear()
+        self.child_list = []
+
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        stack = [self.value.GetNonSyntheticValue()]
+
+        index = 0
+
+        while stack:
+            cur_val = stack.pop()
+            if cur_val.type.is_pointer and cur_val.unsigned == 0:
+                continue
+
+            while cur_val.type.is_pointer:
+                cur_val = cur_val.Dereference()
+
+            # Add super objects if they exist
+            if cur_val.num_children > 0 and cur_val[0].name == "Sup" and cur_val[0].type.name.startswith("tyObject"):
+                stack.append(cur_val[0])
+
+            for child in cur_val.children:
+                child = child.GetNonSyntheticValue()
+                if child.name == "Sup":
+                    continue
+                self.children[child.name] = index
+                self.child_list.append(child)
+                index += 1
+
+    def update(self):
+        self.populate_children()
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class HashSetChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def get_data(self) -> lldb.SBValue:
+        return self.value["data"]["p"]["data"] if NIM_IS_V2 else self.value["data"]["data"]
+
+    def get_len(self) -> lldb.SBValue:
+        return self.value["data"]["len"] if NIM_IS_V2 else self.value["data"]["Sup"]["len"]
+
+    def update(self):
+        self.child_list = []
+
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        tuple_len = int(self.get_len().unsigned)
+        tuple = self.get_data()
+
+        base_data_type = tuple.type.GetArrayElementType()
+
+        cast = tuple.Cast(base_data_type.GetArrayType(tuple_len))
+
+        index = 0
+        for i in range(tuple_len):
+            el = cast[i]
+            field0 = int(el[0].unsigned)
+            if field0 == 0:
+                continue
+            key = el[1]
+            child = key.CreateValueFromAddress(f"[{str(index)}]", key.GetLoadAddress(), key.GetType())
+            index += 1
+
+            self.child_list.append(child)
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class SetCharChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.ty = self.value.target.FindFirstType("char")
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def update(self):
+        self.child_list = []
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        cur_pos = 0
+        for child in self.value.children:
+            child_val = child.signed
+            if child_val != 0:
+                temp = child_val
+                num_bits = 8
+                while temp != 0:
+                    is_set = temp & 1
+                    if is_set == 1:
+                        data = lldb.SBData.CreateDataFromInt(cur_pos)
+                        child = self.value.synthetic_child_from_data(f"[{len(self.child_list)}]", data, self.ty)
+                        self.child_list.append(child)
+                    temp = temp >> 1
+                    cur_pos += 1
+                    num_bits -= 1
+                cur_pos += num_bits
+            else:
+                cur_pos += 8
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+def create_set_children(value: lldb.SBValue, child_type: lldb.SBType, starting_pos: int) -> list[lldb.SBValue]:
+    child_list: list[lldb.SBValue] = []
+    cur_pos = starting_pos
+
+    if value.num_children > 0:
+        children = value.children
+    else:
+        children = [value]
+
+    for child in children:
+        child_val = child.signed
+        if child_val != 0:
+            temp = child_val
+            num_bits = 8
+            while temp != 0:
+                is_set = temp & 1
+                if is_set == 1:
+                    data = lldb.SBData.CreateDataFromInt(cur_pos)
+                    child = value.synthetic_child_from_data(f"[{len(child_list)}]", data, child_type)
+                    child_list.append(child)
+                temp = temp >> 1
+                cur_pos += 1
+                num_bits -= 1
+            cur_pos += num_bits
+        else:
+            cur_pos += 8
+
+    return child_list
+
+
+class SetIntChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.ty = self.value.target.FindFirstType(f"NI64")
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def update(self):
+        self.child_list = []
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+        bits = self.value.GetByteSize() * 8
+        starting_pos = -(bits // 2)
+        self.child_list = create_set_children(self.value, self.ty, starting_pos)
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class SetUIntChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.ty = self.value.target.FindFirstType(f"NU64")
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def update(self):
+        self.child_list = []
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+        self.child_list = create_set_children(self.value, self.ty, starting_pos=0)
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class SetEnumChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.ty = self.value.target.FindFirstType(self.value.type.name.replace("tySet_", ""))
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def update(self):
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+        self.child_list = create_set_children(self.value, self.ty, starting_pos=0)
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class TableChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.children: OrderedDict[str, int] = OrderedDict()
+        self.child_list: list[lldb.SBValue] = []
+
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return self.children[name]
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def get_data(self) -> lldb.SBValue:
+        return self.value["data"]["p"]["data"] if NIM_IS_V2 else self.value["data"]["data"]
+
+    def get_len(self) -> lldb.SBValue:
+        return self.value["data"]["len"] if NIM_IS_V2 else self.value["data"]["Sup"]["len"]
+
+    def update(self):
+        self.child_list = []
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        tuple_len = int(self.get_len().unsigned)
+        tuple = self.get_data()
+
+        base_data_type = tuple.type.GetArrayElementType()
+
+        cast = tuple.Cast(base_data_type.GetArrayType(tuple_len))
+
+        index = 0
+        for i in range(tuple_len):
+            el = cast[i]
+            field0 = int(el[0].unsigned)
+            if field0 == 0:
+                continue
+            key = el[1]
+            val = el[2]
+            key_summary = key.summary
+            child = self.value.CreateValueFromAddress(key_summary, val.GetLoadAddress(), val.GetType())
+            self.child_list.append(child)
+            self.children[key_summary] = index
+            index += 1
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class StringTableChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.children: OrderedDict[str, int] = OrderedDict()
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return self.children[name]
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def get_data(self) -> lldb.SBValue:
+        return self.value["data"]["p"]["data"] if NIM_IS_V2 else self.value["data"]["data"]
+
+    def get_len(self) -> lldb.SBValue:
+        return self.value["data"]["len"] if NIM_IS_V2 else self.value["data"]["Sup"]["len"]
+
+    def update(self):
+        self.children.clear()
+        self.child_list = []
+
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        tuple_len = int(self.get_len().unsigned)
+        tuple = self.get_data()
+
+        base_data_type = tuple.type.GetArrayElementType()
+
+        cast = tuple.Cast(base_data_type.GetArrayType(tuple_len))
+
+        index = 0
+        for i in range(tuple_len):
+            el = cast[i]
+            field0 = int(el[2].unsigned)
+            if field0 == 0:
+                continue
+            key = el[0]
+            val = el[1]
+            child = val.CreateValueFromAddress(key.summary, val.GetLoadAddress(), val.GetType())
+            self.child_list.append(child)
+            self.children[key.summary] = index
+            index += 1
+
+        self.child_list.append(self.value["mode"])
+        self.children["mode"] = index
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class LLDBDynamicObjectProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        value = value.GetNonSyntheticValue()
+        self.value: lldb.SBValue = value[0]
+        self.children: OrderedDict[str, int] = OrderedDict()
+        self.child_list: list[lldb.SBValue] = []
+
+        while self.value.type.is_pointer:
+            self.value = self.value.Dereference()
+
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return self.children[name]
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def update(self):
+        self.children.clear()
+        self.child_list = []
+
+        for i, child in enumerate(self.value.children):
+            name = child.name.strip('"')
+            new_child = child.CreateValueFromAddress(name, child.GetLoadAddress(), child.GetType())
+
+            self.children[name] = i
+            self.child_list.append(new_child)
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class LLDBBasicObjectProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value: lldb.SBValue = value
+
+    def num_children(self):
+        if self.value is not None:
+            return self.value.num_children
+        return 0
+
+    def get_child_index(self, name: str):
+        return self.value.GetIndexOfChildWithName(name)
+
+    def get_child_at_index(self, index):
+        return self.value.GetChildAtIndex(index)
+
+    def update(self):
+        pass
+
+    def has_children(self):
+        return self.num_children() > 0
+
+
+class CustomObjectChildrenProvider:
+    """
+    This children provider handles values returned from lldbDebugSynthetic*
+    Nim procedures
+    """
+
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value: lldb.SBValue = get_custom_synthetic(value) or value
+        if "lldbdynamicobject" in self.value.type.name.lower():
+            self.provider = LLDBDynamicObjectProvider(self.value, internalDict)
+        else:
+            self.provider = LLDBBasicObjectProvider(self.value, internalDict)
+
+    def num_children(self):
+        return self.provider.num_children()
+
+    def get_child_index(self, name: str):
+        return self.provider.get_child_index(name)
+
+    def get_child_at_index(self, index):
+        return self.provider.get_child_at_index(index)
+
+    def update(self):
+        self.provider.update()
+
+    def has_children(self):
+        return self.provider.has_children()
+
+
+def echo(debugger: lldb.SBDebugger, command: str, result, internal_dict):
+    debugger.HandleCommand("po " + command)
+
+
+SUMMARY_FUNCTIONS: dict[str, lldb.SBFunction] = {}
+SYNTHETIC_FUNCTIONS: dict[str, lldb.SBFunction] = {}
+
+
+def get_custom_summary(value: lldb.SBValue) -> Union[str, None]:
+    """Get a custom summary if a function exists for it"""
+    value = value.GetNonSyntheticValue()
+    if value.GetAddress().GetOffset() == 0:
+        return None
+
+    base_type = get_base_type(value.type)
+
+    fn = SUMMARY_FUNCTIONS.get(base_type.name)
+    if fn is None:
+        return None
+
+    fn_type: lldb.SBType = fn.type
+
+    arg_types: lldb.SBTypeList = fn_type.GetFunctionArgumentTypes()
+    first_type = arg_types.GetTypeAtIndex(0)
+
+    while value.type.is_pointer:
+        value = value.Dereference()
+
+    if first_type.is_pointer:
+        command = f"{fn.name}(({first_type.name})" + str(value.GetLoadAddress()) + ");"
+    else:
+        command = f"{fn.name}(*({first_type.GetPointerType().name})" + str(value.GetLoadAddress()) + ");"
+
+    res = executeCommand(command)
+
+    if res.error.fail:
+        return None
+
+    return res.summary.strip('"')
+
+
+def get_custom_value_summary(value: lldb.SBValue) -> Union[str, None]:
+    """Get a custom summary if a function exists for it"""
+
+    fn: lldb.SBFunction = SUMMARY_FUNCTIONS.get(value.type.name)
+    if fn is None:
+        return None
+
+    command = f"{fn.name}(({value.type.name})" + str(value.signed) + ");"
+    res = executeCommand(command)
+
+    if res.error.fail:
+        return None
+
+    return res.summary.strip('"')
+
+
+def get_custom_synthetic(value: lldb.SBValue) -> Union[lldb.SBValue, None]:
+    """Get a custom synthetic object if a function exists for it"""
+    value = value.GetNonSyntheticValue()
+    if value.GetAddress().GetOffset() == 0:
+        return None
+
+    base_type = get_base_type(value.type)
+
+    fn = SYNTHETIC_FUNCTIONS.get(base_type.name)
+    if fn is None:
+        return None
+
+    fn_type: lldb.SBType = fn.type
+
+    arg_types: lldb.SBTypeList = fn_type.GetFunctionArgumentTypes()
+    first_type = arg_types.GetTypeAtIndex(0)
+
+    while value.type.is_pointer:
+        value = value.Dereference()
+
+    if first_type.is_pointer:
+        first_arg = f"({first_type.name}){value.GetLoadAddress()}"
+    else:
+        first_arg = f"*({first_type.GetPointerType().name}){value.GetLoadAddress()}"
+
+    if arg_types.GetSize() > 1 and fn.GetArgumentName(1) == "Result":
+        ret_type = arg_types.GetTypeAtIndex(1)
+        ret_type = get_base_type(ret_type)
+
+        command = f"""
+            {ret_type.name} lldbT;
+            nimZeroMem((void*)(&lldbT), sizeof({ret_type.name}));
+            {fn.name}(({first_arg}), (&lldbT));
+            lldbT;
+        """
+    else:
+        command = f"{fn.name}({first_arg});"
+
+    res = executeCommand(command)
+
+    if res.error.fail:
+        print(res.error)
+        return None
+
+    return res
+
+
+def get_base_type(ty: lldb.SBType) -> lldb.SBType:
+    """Get the base type of the type"""
+    temp = ty
+    while temp.IsPointerType():
+        temp = temp.GetPointeeType()
+    return temp
+
+
+def use_base_type(ty: lldb.SBType) -> bool:
+    types_to_check = [
+        "NF",
+        "NF32",
+        "NF64",
+        "NI",
+        "NI8",
+        "NI16",
+        "NI32",
+        "NI64",
+        "bool",
+        "NIM_BOOL",
+        "NU",
+        "NU8",
+        "NU16",
+        "NU32",
+        "NU64",
+    ]
+
+    for type_to_check in types_to_check:
+        if ty.name.startswith(type_to_check):
+            return False
+
+    return True
+
+
+def breakpoint_function_wrapper(frame: lldb.SBFrame, bp_loc, internal_dict):
+    """This allows function calls to Nim for custom object summaries and synthetic children"""
+    debugger = lldb.debugger
+
+    global SUMMARY_FUNCTIONS
+    global SYNTHETIC_FUNCTIONS
+
+    global NIM_IS_V2
+
+    for tname, fn in SYNTHETIC_FUNCTIONS.items():
+        debugger.HandleCommand(f"type synthetic delete -w nim {tname}")
+
+    SUMMARY_FUNCTIONS = {}
+    SYNTHETIC_FUNCTIONS = {}
+
+    target: lldb.SBTarget = debugger.GetSelectedTarget()
+
+    NIM_IS_V2 = target.FindFirstType("TNimTypeV2").IsValid()
+
+    module = frame.GetSymbolContext(lldb.eSymbolContextModule).module
+
+    for sym in module:
+        if (
+            not sym.name.startswith("lldbDebugSummary")
+            and not sym.name.startswith("lldbDebugSynthetic")
+            and not sym.name.startswith("dollar___")
+        ):
+            continue
+
+        fn_syms: lldb.SBSymbolContextList = target.FindFunctions(sym.name)
+        if not fn_syms.GetSize() > 0:
+            continue
+
+        fn_sym: lldb.SBSymbolContext = fn_syms.GetContextAtIndex(0)
+
+        fn: lldb.SBFunction = fn_sym.function
+        fn_type: lldb.SBType = fn.type
+        arg_types: lldb.SBTypeList = fn_type.GetFunctionArgumentTypes()
+
+        if arg_types.GetSize() > 1 and fn.GetArgumentName(1) == "Result":
+            pass # don't continue
+        elif arg_types.GetSize() != 1:
+            continue
+
+        arg_type: lldb.SBType = arg_types.GetTypeAtIndex(0)
+        if use_base_type(arg_type):
+            arg_type = get_base_type(arg_type)
+
+        if sym.name.startswith("lldbDebugSummary") or sym.name.startswith("dollar___"):
+            SUMMARY_FUNCTIONS[arg_type.name] = fn
+        elif sym.name.startswith("lldbDebugSynthetic"):
+            SYNTHETIC_FUNCTIONS[arg_type.name] = fn
+            debugger.HandleCommand(
+                f"type synthetic add -w nim -l {__name__}.CustomObjectChildrenProvider {arg_type.name}"
+            )
+
+
+def executeCommand(command, *args):
+    debugger = lldb.debugger
+    process = debugger.GetSelectedTarget().GetProcess()
+    frame: lldb.SBFrame = process.GetSelectedThread().GetSelectedFrame()
+
+    expr_options = lldb.SBExpressionOptions()
+    expr_options.SetIgnoreBreakpoints(False)
+    expr_options.SetFetchDynamicValue(lldb.eDynamicCanRunTarget)
+    expr_options.SetTimeoutInMicroSeconds(30 * 1000 * 1000)  # 30 second timeout
+    expr_options.SetTryAllThreads(True)
+    expr_options.SetUnwindOnError(False)
+    expr_options.SetGenerateDebugInfo(True)
+    expr_options.SetLanguage(lldb.eLanguageTypeC)
+    expr_options.SetCoerceResultToId(True)
+    res = frame.EvaluateExpression(command, expr_options)
+
+    return res
+
+
+def __lldb_init_module(debugger, internal_dict):
+    # fmt: off
+    debugger.HandleCommand(f"breakpoint command add -F {__name__}.breakpoint_function_wrapper --script-type python 1")
+    debugger.HandleCommand(f"type summary add -w nim -n sequence -F  {__name__}.Sequence -x tySequence_+[[:alnum:]]+$")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SeqChildrenProvider -x tySequence_+[[:alnum:]]+$")
+
+    debugger.HandleCommand(f"type summary add -w nim -n chararray -F  {__name__}.CharArray -x char\s+[\d+]")
+    debugger.HandleCommand(f"type summary add -w nim -n array -F  {__name__}.Array -x tyArray_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.ArrayChildrenProvider -x tyArray_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n string -F  {__name__}.NimString NimStringDesc")
+
+    debugger.HandleCommand(f"type summary add -w nim -n stringv2 -F {__name__}.NimString -x NimStringV2$")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.StringChildrenProvider -x NimStringV2$")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.StringChildrenProvider -x NimStringDesc$")
+
+    debugger.HandleCommand(f"type summary add -w nim -n cstring -F  {__name__}.NCSTRING NCSTRING")
+
+    debugger.HandleCommand(f"type summary add -w nim -n object -F  {__name__}.ObjectV2 -x tyObject_+[[:alnum:]]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.ObjectChildrenProvider -x tyObject_+[[:alnum:]]+_+[[:alnum:]]+$")
+
+    debugger.HandleCommand(f"type summary add -w nim -n tframe -F  {__name__}.ObjectV2 -x TFrame$")
+
+    debugger.HandleCommand(f"type summary add -w nim -n rootobj -F  {__name__}.ObjectV2 -x RootObj$")
+
+    debugger.HandleCommand(f"type summary add -w nim -n enum -F  {__name__}.Enum -x tyEnum_+[[:alnum:]]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n hashset -F  {__name__}.HashSet -x tyObject_+HashSet_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.HashSetChildrenProvider -x tyObject_+HashSet_+[[:alnum:]]+")
+
+    debugger.HandleCommand(f"type summary add -w nim -n rope -F  {__name__}.Rope -x tyObject_+Rope[[:alnum:]]+_+[[:alnum:]]+")
+
+    debugger.HandleCommand(f"type summary add -w nim -n setuint -F  {__name__}.Set -x tySet_+tyInt_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetIntChildrenProvider -x tySet_+tyInt[0-9]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n setint -F  {__name__}.Set -x tySet_+tyInt[0-9]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n setuint2 -F  {__name__}.Set -x tySet_+tyUInt[0-9]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetUIntChildrenProvider -x tySet_+tyUInt[0-9]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetUIntChildrenProvider -x tySet_+tyInt_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n setenum -F  {__name__}.EnumSet -x tySet_+tyEnum_+[[:alnum:]]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetEnumChildrenProvider -x tySet_+tyEnum_+[[:alnum:]]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n setchar -F  {__name__}.Set -x tySet_+tyChar_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetCharChildrenProvider -x tySet_+tyChar_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n table -F  {__name__}.Table -x tyObject_+Table_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.TableChildrenProvider -x tyObject_+Table_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n stringtable -F  {__name__}.StringTable -x tyObject_+StringTableObj_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.StringTableChildrenProvider -x tyObject_+StringTableObj_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n tuple2 -F  {__name__}.Tuple -x tyObject_+Tuple_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n tuple -F  {__name__}.Tuple -x tyTuple_+[[:alnum:]]+")
+
+    debugger.HandleCommand(f"type summary add -w nim -n float -F  {__name__}.Float NF")
+    debugger.HandleCommand(f"type summary add -w nim -n float32 -F  {__name__}.Float NF32")
+    debugger.HandleCommand(f"type summary add -w nim -n float64 -F  {__name__}.Float NF64")
+    debugger.HandleCommand(f"type summary add -w nim -n integer -F  {__name__}.Number -x NI")
+    debugger.HandleCommand(f"type summary add -w nim -n integer8 -F  {__name__}.Number -x NI8")
+    debugger.HandleCommand(f"type summary add -w nim -n integer16 -F  {__name__}.Number -x NI16")
+    debugger.HandleCommand(f"type summary add -w nim -n integer32 -F  {__name__}.Number -x NI32")
+    debugger.HandleCommand(f"type summary add -w nim -n integer64 -F  {__name__}.Number -x NI64")
+    debugger.HandleCommand(f"type summary add -w nim -n bool -F  {__name__}.Bool -x bool")
+    debugger.HandleCommand(f"type summary add -w nim -n bool2 -F  {__name__}.Bool -x NIM_BOOL")
+    debugger.HandleCommand(f"type summary add -w nim -n uinteger -F  {__name__}.UnsignedNumber -x NU")
+    debugger.HandleCommand(f"type summary add -w nim -n uinteger8 -F  {__name__}.UnsignedNumber -x NU8")
+    debugger.HandleCommand(f"type summary add -w nim -n uinteger16 -F  {__name__}.UnsignedNumber -x NU16")
+    debugger.HandleCommand(f"type summary add -w nim -n uinteger32 -F  {__name__}.UnsignedNumber -x NU32")
+    debugger.HandleCommand(f"type summary add -w nim -n uinteger64 -F  {__name__}.UnsignedNumber -x NU64")
+    debugger.HandleCommand("type category enable nim")
+    debugger.HandleCommand(f"command script add -f  {__name__}.echo echo")
+    # fmt: on
diff --git a/tools/deps.nim b/tools/deps.nim
new file mode 100644
index 000000000..e43f7a2b4
--- /dev/null
+++ b/tools/deps.nim
@@ -0,0 +1,44 @@
+import std/[os, uri, strformat, strutils]
+import std/private/gitutils
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+proc exec(cmd: string) =
+  echo "deps.cmd: " & cmd
+  let status = execShellCmd(cmd)
+  doAssert status == 0, cmd
+
+proc execRetry(cmd: string) =
+  let ok = retryCall(call = block:
+    let status = execShellCmd(cmd)
+    let result = status == 0
+    if not result:
+      echo fmt"failed command: '{cmd}', status: {status}"
+    result)
+  doAssert ok, cmd
+
+proc cloneDependency*(destDirBase: string, url: string, commit = commitHead,
+                      appendRepoName = true, allowBundled = false) =
+  let destDirBase = destDirBase.absolutePath
+  let p = url.parseUri.path
+  let name = p.splitFile.name
+  var destDir = destDirBase
+  if appendRepoName: destDir = destDir / name
+  let quotedDestDir = destDir.quoteShell
+  if not dirExists(destDir):
+    # note: old code used `destDir / .git` but that wouldn't prevent git clone
+    # from failing
+    execRetry fmt"git clone -q {url} {quotedDestDir}"
+  if isGitRepo(destDir):
+    let oldDir = getCurrentDir()
+    setCurrentDir(destDir)
+    try:
+      execRetry "git fetch -q"
+      exec fmt"git checkout -q {commit}"
+    finally:
+      setCurrentDir(oldDir)
+  elif allowBundled:
+    discard "this dependency was bundled with Nim, don't do anything"
+  else:
+    quit "FAILURE: " & destdir & " already exists but is not a git repo"
diff --git a/tools/detect/detect.nim b/tools/detect/detect.nim
index 5b4fdc99e..ed9438494 100644
--- a/tools/detect/detect.nim
+++ b/tools/detect/detect.nim
@@ -12,14 +12,27 @@
 # The second one is more portable, and less semantically correct. It only works
 # when there's a backing C compiler available as well, preventing standalone
 # compilation.
-import os, strutils
+import std/[os, strutils]
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
+
+when defined(openbsd) or defined(freebsd) or defined(netbsd):
+  const
+    cc = "cc -o $# $#.c"
+    cpp = "cc -E -o $#.i $#.c"
+    ccLinkMath = "cc -lm -o $# $#.c"
+    cppLinkMath = "cc -lm -E -o $#.i $#.c"
+else:
+  const
+    cc = "gcc -o $# $#.c"
+    cpp = "gcc -E -o $#.i $#.c"
 
 const
-  cc = "gcc -o $# $#.c"
-  cpp = "gcc -E -o $#.i $#.c"
-
   cfile = """
 /* Generated by detect.nim */
+#define _GNU_SOURCE
+#define _POSIX_C_SOURCE 200809L
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -87,8 +100,11 @@ proc main =
   if open(f, "other_consts.nim", fmWrite):
     f.write(nimfile % [other])
     close(f)
-  if not myExec(cc % [gen.addFileExt(ExeExt), gen]): quit(1)
-  if not myExec(cpp % [pre.addFileExt(ExeExt), pre]): quit(1)
+
+  let cCompile = when defined(openbsd) or defined(freebsd) or defined(netbsd): ccLinkMath else: cc
+  let cppCompile = when defined(openbsd) or defined(freebsd) or defined(netbsd): cppLinkMath else: cpp
+  if not myExec(cCompile % [gen.addFileExt(ExeExt), gen]): quit(1)
+  if not myExec(cppCompile % [pre.addFileExt(ExeExt), pre]): quit(1)
   when defined(windows):
     if not myExec(gen.addFileExt(ExeExt)): quit(1)
   else:
@@ -119,10 +135,14 @@ proc v(name: string, typ = "cint", no_other = false) =
     addf(tl,
       "#ifdef $3\n  fprintf(f, \"const $1* = $2(%ld)\\n\", $3);\n#endif\n",
       n, t, name)
-  of "cint", "cshort", "InAddrScalar", "TSa_Family":
+  of "cint", "cshort", "TSa_Family":
     addf(tl,
       "#ifdef $3\n  fprintf(f, \"const $1* = $2(%d)\\n\", $3);\n#endif\n",
       n, t, name)
+  of "InAddrScalar":
+    addf(tl,
+      "#ifdef $3\n  fprintf(f, \"const $1* = $2(%u)\\n\", $3);\n#endif\n",
+      n, t, name)
   else:
     addf(tl,
       "#ifdef $3\n  fprintf(f, \"const $1* = cast[$2](%d)\\n\", $3);\n#endif\n",
@@ -228,6 +248,7 @@ v("EXDEV")
 
 header("<fcntl.h>")
 v("F_DUPFD")
+v("F_DUPFD_CLOEXEC")
 v("F_GETFD")
 v("F_SETFD")
 v("F_GETFL")
@@ -254,6 +275,11 @@ v("O_ACCMODE")
 v("O_RDONLY")
 v("O_RDWR")
 v("O_WRONLY")
+v("O_CLOEXEC")
+v("O_DIRECT")
+v("O_PATH")
+v("O_NOATIME")
+v("O_TMPFILE")
 v("POSIX_FADV_NORMAL")
 v("POSIX_FADV_SEQUENTIAL")
 v("POSIX_FADV_RANDOM")
@@ -431,6 +457,7 @@ header("<netinet/in.h>")
 v("IPPROTO_IP")
 v("IPPROTO_IPV6")
 v("IPPROTO_ICMP")
+v("IPPROTO_ICMPV6")
 v("IPPROTO_RAW")
 v("IPPROTO_TCP")
 v("IPPROTO_UDP")
@@ -567,6 +594,9 @@ v("PROT_READ")
 v("PROT_WRITE")
 v("PROT_EXEC")
 v("PROT_NONE")
+v("MAP_ANONYMOUS")
+v("MAP_FIXED_NOREPLACE")
+v("MAP_NORESERVE")
 v("MAP_SHARED")
 v("MAP_PRIVATE")
 v("MAP_FIXED")
@@ -588,6 +618,7 @@ v("MAP_POPULATE", no_other = true)
 
 header("<sys/resource.h>")
 v("RLIMIT_NOFILE")
+v("RLIMIT_STACK")
 
 header("<sys/select.h>")
 v("FD_SETSIZE")
@@ -599,6 +630,7 @@ v("MSG_EOR")
 v("MSG_OOB")
 v("SCM_RIGHTS")
 v("SO_ACCEPTCONN")
+v("SO_BINDTODEVICE")
 v("SO_BROADCAST")
 v("SO_DEBUG")
 v("SO_DONTROUTE")
@@ -618,6 +650,7 @@ v("SOCK_DGRAM")
 v("SOCK_RAW")
 v("SOCK_SEQPACKET")
 v("SOCK_STREAM")
+v("SOCK_CLOEXEC", no_other = true)
 v("SOL_SOCKET")
 v("SOMAXCONN")
 v("SO_REUSEPORT", no_other = true)
@@ -625,10 +658,10 @@ v("MSG_NOSIGNAL", no_other = true)
 v("MSG_PEEK")
 v("MSG_TRUNC")
 v("MSG_WAITALL")
-v("AF_INET", "TSa_Family")
-v("AF_INET6", "TSa_Family")
-v("AF_UNIX", "TSa_Family")
-v("AF_UNSPEC", "TSa_Family")
+v("AF_INET")
+v("AF_INET6")
+v("AF_UNIX")
+v("AF_UNSPEC")
 v("SHUT_RD")
 v("SHUT_RDWR")
 v("SHUT_WR")
@@ -795,7 +828,7 @@ v("_SC_MQ_OPEN_MAX")
 v("_SC_MQ_PRIO_MAX")
 v("_SC_NGROUPS_MAX")
 v("_SC_OPEN_MAX")
-v("_SC_PAGE_SIZE")
+v("_SC_PAGESIZE") # Synonym for _SC_PAGE_SIZE
 v("_SC_PRIORITIZED_IO")
 v("_SC_PRIORITY_SCHEDULING")
 v("_SC_RAW_SOCKETS")
diff --git a/tools/dochack/dochack.nim b/tools/dochack/dochack.nim
index 79a0e7482..946945346 100644
--- a/tools/dochack/dochack.nim
+++ b/tools/dochack/dochack.nim
@@ -1,18 +1,52 @@
+import dom
+import fuzzysearch
+import std/[jsfetch, asyncjs]
+
+
+proc setTheme(theme: cstring) {.exportc.} =
+  document.documentElement.setAttribute("data-theme", theme)
+  window.localStorage.setItem("theme", theme)
+
+# set `data-theme` attribute early to prevent white flash
+setTheme:
+  let t = window.localStorage.getItem("theme")
+  if t.isNil: cstring"auto" else: t
+
+proc onDOMLoaded(e: Event) {.exportc.} =
+  # set theme select value
+  document.getElementById("theme-select").value = window.localStorage.getItem("theme")
+
+  for pragmaDots in document.getElementsByClassName("pragmadots"):
+    pragmaDots.onclick = proc (event: Event) =
+      # Hide tease
+      event.target.parentNode.style.display = "none"
+      # Show actual
+      event.target.parentNode.nextSibling.style.display = "inline"
+
+
+proc tree(tag: cstring; kids: varargs[Element]): Element =
+  result = document.createElement tag
+  for k in kids:
+    result.appendChild k
+
+proc add(parent, kid: Element) =
+  if parent.nodeName == "TR" and (kid.nodeName == "TD" or kid.nodeName == "TH"):
+    let k = document.createElement("TD")
+    appendChild(k, kid)
+    appendChild(parent, k)
+  else:
+    appendChild(parent, kid)
 
+proc setClass(e: Element; value: cstring) =
+  e.setAttribute("class", value)
+proc text(s: cstring): Element = cast[Element](document.createTextNode(s))
 
-import karax
-
-proc findNodeWith(x: Element; tag, content: cstring): Element =
-  if x.nodeName == tag and x.textContent == content:
-    return x
-  for i in 0..<x.len:
-    let it = x[i]
-    let y = findNodeWith(it, tag, content)
-    if y != nil: return y
-  return nil
+proc replaceById(id: cstring; newTree: Node) =
+  let x = document.getElementById(id)
+  x.parentNode.replaceChild(newTree, x)
+  newTree.id = id
 
 proc clone(e: Element): Element {.importcpp: "#.cloneNode(true)", nodecl.}
-proc parent(e: Element): Element {.importcpp: "#.parentNode", nodecl.}
 proc markElement(x: Element) {.importcpp: "#.__karaxMarker__ = true", nodecl.}
 proc isMarked(x: Element): bool {.
   importcpp: "#.hasOwnProperty('__karaxMarker__')", nodecl.}
@@ -21,20 +55,13 @@ proc title(x: Element): cstring {.importcpp: "#.title", nodecl.}
 proc sort[T](x: var openArray[T]; cmp: proc(a, b: T): int) {.importcpp:
   "#.sort(#)", nodecl.}
 
-proc parentWith(x: Element; tag: cstring): Element =
-  result = x.parent
-  while result.nodeName != tag:
-    result = result.parent
-    if result == nil: return nil
-
 proc extractItems(x: Element; items: var seq[Element]) =
   if x == nil: return
   if x.nodeName == "A":
     items.add x
   else:
     for i in 0..<x.len:
-      let it = x[i]
-      extractItems(it, items)
+      extractItems(x[i], items)
 
 # HTML trees are so shitty we transform the TOC into a decent
 # data-structure instead and work on that.
@@ -45,16 +72,14 @@ type
     sortId: int
     doSort: bool
 
-proc extractItems(x: TocEntry; heading: cstring;
-                  items: var seq[Element]) =
+proc extractItems(x: TocEntry; heading: cstring; items: var seq[Element]) =
   if x == nil: return
   if x.heading != nil and x.heading.textContent == heading:
     for i in 0..<x.kids.len:
       items.add x.kids[i].heading
   else:
-    for i in 0..<x.kids.len:
-      let it = x.kids[i]
-      extractItems(it, heading, items)
+    for k in x.kids:
+      extractItems(k, heading, items)
 
 proc toHtml(x: TocEntry; isRoot=false): Element =
   if x == nil: return nil
@@ -88,20 +113,10 @@ proc toHtml(x: TocEntry; isRoot=false): Element =
   if ul.len != 0: result.add ul
   if result.len == 0: result = nil
 
-proc containsWord(a, b: cstring): bool {.asmNoStackFrame.} =
-  {.emit: """
-     var escaped = `b`.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
-     return new RegExp("\\b" + escaped + "\\b").test(`a`);
-  """.}
-
-proc isWhitespace(text: cstring): bool {.asmNoStackFrame.} =
-  {.emit: """
-     return !/[^\s]/.test(`text`);
-  """.}
+proc isWhitespace(text: cstring): bool {.importcpp: r"!/\S/.test(#)".}
 
 proc isWhitespace(x: Element): bool =
-  x.nodeName == "#text" and x.textContent.isWhitespace or
-    x.nodeName == "#comment"
+  x.nodeName == "#text" and x.textContent.isWhitespace or x.nodeName == "#comment"
 
 proc toToc(x: Element; father: TocEntry) =
   if x.nodeName == "UL":
@@ -130,8 +145,7 @@ proc toToc(x: Element; father: TocEntry) =
     for i in 0 ..< x.len:
       if not x[i].isWhitespace: idx.add i
     if idx.len == 2 and x[idx[1]].nodeName == "UL":
-      let e = TocEntry(heading: x[idx[0]], kids: @[],
-                       sortId: father.kids.len)
+      let e = TocEntry(heading: x[idx[0]], kids: @[], sortId: father.kids.len)
       let it = x[idx[1]]
       for j in 0..<it.len:
         toToc(it[j], e)
@@ -140,8 +154,7 @@ proc toToc(x: Element; father: TocEntry) =
       for i in 0..<x.len:
         toToc(x[i], father)
   else:
-    father.kids.add TocEntry(heading: x, kids: @[],
-                             sortId: father.kids.len)
+    father.kids.add TocEntry(heading: x, kids: @[], sortId: father.kids.len)
 
 proc tocul(x: Element): Element =
   # x is a 'ul' element
@@ -153,18 +166,13 @@ proc tocul(x: Element): Element =
     elif it.nodeName == "UL":
       result.add tocul(it)
 
-proc getSection(toc: Element; name: cstring): Element =
-  let sec = findNodeWith(toc, "A", name)
-  if sec != nil:
-    result = sec.parentWith("LI")
-
 proc uncovered(x: TocEntry): TocEntry =
   if x.kids.len == 0 and x.heading != nil:
     return if not isMarked(x.heading): x else: nil
   result = TocEntry(heading: x.heading, kids: @[], sortId: x.sortId,
                     doSort: x.doSort)
-  for i in 0..<x.kids.len:
-    let y = uncovered(x.kids[i])
+  for k in x.kids:
+    let y = uncovered(k)
     if y != nil: result.kids.add y
   if result.kids.len == 0: result = nil
 
@@ -183,9 +191,8 @@ proc buildToc(orig: TocEntry; types, procs: seq[Element]): TocEntry =
     t.markElement()
     for p in procs:
       if not isMarked(p):
-        let xx = karax.getElementsByClass(p.parent, cstring"attachedType")
+        let xx = getElementsByClass(p.parentNode, "attachedType")
         if xx.len == 1 and xx[0].textContent == t.textContent:
-          #kout(cstring"found ", p.nodeName)
           let q = tree("A", text(p.title))
           q.setAttr("href", p.getAttribute("href"))
           c.kids.add TocEntry(heading: q, kids: @[])
@@ -196,15 +203,13 @@ proc buildToc(orig: TocEntry; types, procs: seq[Element]): TocEntry =
 var alternative: Element
 
 proc togglevis(d: Element) =
-  asm """
-    if (`d`.style.display == 'none')
-      `d`.style.display = 'inline';
-    else
-      `d`.style.display = 'none';
-  """
+  if d.style.display == "none":
+    d.style.display = "inline"
+  else:
+    d.style.display = "none"
 
 proc groupBy*(value: cstring) {.exportc.} =
-  let toc = getElementById("toc-list")
+  let toc = document.getElementById("toc-list")
   if alternative.isNil:
     var tt = TocEntry(heading: nil, kids: @[])
     toToc(toc, tt)
@@ -228,69 +233,150 @@ proc groupBy*(value: cstring) {.exportc.} =
     replaceById("tocRoot", alternative)
   else:
     replaceById("tocRoot", tree("DIV"))
-  togglevis(getElementById"toc-list")
+  togglevis(document.getElementById"toc-list")
 
 var
-  db: seq[Element]
+  db: seq[Node]
   contents: seq[cstring]
 
-template normalize(x: cstring): cstring = x.toLower.replace("_", "")
+
+proc escapeCString(x: var cstring) =
+  # Original strings are already escaped except HTML tags, so
+  # we only escape `<` and `>`.
+  var s = ""
+  for c in x:
+    case c
+    of '<': s.add("&lt;")
+    of '>': s.add("&gt;")
+    else: s.add(c)
+  x = s.cstring
 
 proc dosearch(value: cstring): Element =
-  if db.isNil:
-    var stuff: Element
-    {.emit: """
-    var request = new XMLHttpRequest();
-    request.open("GET", "theindex.html", false);
-    request.send(null);
-
-    var doc = document.implementation.createHTMLDocument("theindex");
-    doc.documentElement.innerHTML = request.responseText;
-
-    //parser=new DOMParser();
-    //doc=parser.parseFromString("<html></html>", "text/html");
-
-    `stuff` = doc.documentElement;
-    """.}
-    db = stuff.getElementsByClass"reference external"
-    contents = @[]
-    for ahref in db:
-      contents.add ahref.textContent.normalize
+  if db.len == 0:
+    return
   let ul = tree("UL")
   result = tree("DIV")
   result.setClass"search_results"
-  var matches: seq[(Element, int)] = @[]
-  let key = value.normalize
+  var matches: seq[(Node, int)] = @[]
   for i in 0..<db.len:
     let c = contents[i]
-    if c.containsWord(key):
-      matches.add((db[i], -(30_000 - c.len)))
-    elif c.contains(key):
-      matches.add((db[i], c.len))
-  matches.sort do (a, b: auto) -> int:
-    a[1] - b[1]
-  for i in 0..min(<matches.len, 19):
-    ul.add(tree("LI", matches[i][0]))
+    if c == "Examples" or c == "PEG construction":
+    # Some manual exclusions.
+    # Ideally these should be fixed in the index to be more
+    # descriptive of what they are.
+      continue
+    let (score, matched) = fuzzymatch(value, c)
+    if matched:
+      matches.add((db[i], score))
+
+  matches.sort(proc(a, b: auto): int = b[1] - a[1])
+  for i in 0 ..< min(matches.len, 29):
+    matches[i][0].innerHTML = matches[i][0].getAttribute("data-doc-search-tag")
+    escapeCString(matches[i][0].innerHTML)
+    ul.add(tree("LI", cast[Element](matches[i][0])))
   if ul.len == 0:
     result.add tree("B", text"no search results")
   else:
     result.add tree("B", text"search results")
     result.add ul
 
-var oldtoc: Element
-var timer: Timeout
+proc loadIndex() {.async.} =
+  ## Loads theindex.html to enable searching
+  let
+    indexURL = document.getElementById("indexLink").getAttribute("href")
+    # Get root of project documentation by cutting off theindex.html from index href
+    rootURL = ($indexURL)[0 ..< ^"theindex.html".len]
+  var resp = fetch(indexURL).await().text().await()
+  # Convert into element so we can use DOM functions to parse the html
+  var indexElem = document.createElement("div")
+  indexElem.innerHtml = resp
+  # Add items into the DB/contents
+  for href in indexElem.getElementsByClass("reference"):
+    # Make links be relative to project root instead of current page
+    href.setAttr("href", cstring(rootURL & $href.getAttribute("href")))
+    db &= href
+    contents &= href.getAttribute("data-doc-search-tag")
+
+
+var
+  oldtoc: Element
+  timer: Timeout
+  loadIndexFut: Future[void] = nil
 
 proc search*() {.exportc.} =
   proc wrapper() =
-    let elem = getElementById("searchInput")
+    let elem = document.getElementById("searchInput")
     let value = elem.value
-    if value != "":
+    if value.len != 0:
       if oldtoc.isNil:
-        oldtoc = getElementById("tocRoot")
+        oldtoc = document.getElementById("tocRoot")
       let results = dosearch(value)
       replaceById("tocRoot", results)
     elif not oldtoc.isNil:
       replaceById("tocRoot", oldtoc)
-
+  # Start loading index as soon as user starts typing.
+  # Will only be loaded the once anyways
+  if loadIndexFut == nil:
+    loadIndexFut = loadIndex()
+    # Run wrapper once loaded so we don't miss the users query
+    discard loadIndexFut.then(wrapper)
   if timer != nil: clearTimeout(timer)
   timer = setTimeout(wrapper, 400)
+
+proc copyToClipboard*() {.exportc.} =
+    {.emit: """
+
+    function updatePreTags() {
+
+      const allPreTags = document.querySelectorAll("pre:not(.line-nums)")
+
+      allPreTags.forEach((e) => {
+      
+          const div = document.createElement("div")
+          div.classList.add("copyToClipBoard")
+    
+          const preTag = document.createElement("pre")
+          preTag.innerHTML = e.innerHTML
+    
+          const button = document.createElement("button")
+          button.value = e.textContent.replace('...', '') 
+          button.classList.add("copyToClipBoardBtn")
+          button.style.cursor = "pointer"
+    
+          div.appendChild(preTag)
+          div.appendChild(button)
+    
+          e.outerHTML = div.outerHTML
+      
+      })
+    }
+
+
+    function copyTextToClipboard(e) {
+        const clipBoardContent = e.target.value
+        navigator.clipboard.writeText(clipBoardContent).then(function() {
+            e.target.style.setProperty("--clipboard-image", "var(--clipboard-image-selected)")
+        }, function(err) {
+            console.error("Could not copy text: ", err);
+        });
+    }
+
+    window.addEventListener("click", (e) => {
+        if (e.target.classList.contains("copyToClipBoardBtn")) {
+            copyTextToClipboard(e)
+          }
+    })
+
+    window.addEventListener("mouseover", (e) => {
+        if (e.target.nodeName === "PRE") {
+            e.target.nextElementSibling.style.setProperty("--clipboard-image", "var(--clipboard-image-normal)")
+        }
+    })
+    
+    window.addEventListener("DOMContentLoaded", updatePreTags)
+
+    """
+    .}
+
+copyToClipboard()
+window.addEventListener("DOMContentLoaded", onDOMLoaded)
diff --git a/tools/dochack/fuzzysearch.nim b/tools/dochack/fuzzysearch.nim
new file mode 100644
index 000000000..fecbaf4f0
--- /dev/null
+++ b/tools/dochack/fuzzysearch.nim
@@ -0,0 +1,141 @@
+# A Fuzzy Match implementation inspired by the sublime text fuzzy match algorithm
+# as described here: https://blog.forrestthewoods.com/reverse-engineering-sublime-text-s-fuzzy-match-4cffeed33fdb
+# Heavily modified to provide more subjectively useful results
+# for on the Nim manual.
+#
+import strutils
+import math
+
+
+const
+  MaxUnmatchedLeadingChar = 3
+  ## Maximum number of times the penalty for unmatched leading chars is applied.
+
+  HeadingScaleFactor = 0.5
+  ## The score from before the colon Char is multiplied by this.
+  ## This is to weight function signatures and descriptions over module titles.
+
+
+type
+  ScoreCard = enum
+    StartMatch           = -100 ## Start matching.
+    LeadingCharDiff      = -3   ## An unmatched, leading character was found.
+    CharDiff             = -1   ## An unmatched character was found.
+    CharMatch            = 0    ## A matched character was found.
+    ConsecutiveMatch     = 5    ## A consecutive match was found.
+    LeadingCharMatch     = 10   ## The character matches the beginning of the
+                                ## string or the first character of a word
+                                ## or camel case boundary.
+    WordBoundryMatch     = 20   ## The last ConsecutiveCharMatch that
+                                ## immediately precedes the end of the string,
+                                ## end of the pattern, or a LeadingCharMatch.
+
+
+proc fuzzyMatch*(pattern, str: cstring) : tuple[score: int, matched: bool] =
+  var
+    scoreState = StartMatch
+    headerMatched = false
+    unmatchedLeadingCharCount = 0
+    consecutiveMatchCount = 0
+    strIndex = 0
+    patIndex = 0
+    score = 0
+
+  template transition(nextState) =
+    scoreState = nextState
+    score += ord(scoreState)
+
+  while (strIndex < str.len) and (patIndex < pattern.len):
+    var
+      patternChar = pattern[patIndex].toLowerAscii
+      strChar     = str[strIndex].toLowerAscii
+
+    # Ignore certain characters
+    if patternChar in {'_', ' ', '.'}:
+      patIndex += 1
+      continue
+    if strChar in {'_', ' ', '.'}:
+      strIndex += 1
+      continue
+
+    # Since this algorithm will be used to search against Nim documentation,
+    # the below logic prioritizes headers.
+    if not headerMatched and strChar == ':':
+      headerMatched = true
+      scoreState = StartMatch
+      score = int(floor(HeadingScaleFactor * float(score)))
+      patIndex = 0
+      strIndex += 1
+      continue
+
+    if strChar == patternChar:
+      case scoreState
+      of StartMatch, WordBoundryMatch:
+        scoreState = LeadingCharMatch
+
+      of CharMatch:
+        transition(ConsecutiveMatch)
+
+      of LeadingCharMatch, ConsecutiveMatch:
+        consecutiveMatchCount += 1
+        scoreState = ConsecutiveMatch
+        score += ord(ConsecutiveMatch) * consecutiveMatchCount
+
+        if scoreState == LeadingCharMatch:
+          score += ord(LeadingCharMatch)
+
+        var onBoundary = (patIndex == high(pattern))
+        if not onBoundary and strIndex < high(str):
+          let
+            nextPatternChar = toLowerAscii(pattern[patIndex + 1])
+            nextStrChar     = toLowerAscii(str[strIndex + 1])
+
+          onBoundary = (
+            nextStrChar notin {'a'..'z'} and
+            nextStrChar != nextPatternChar
+          )
+
+        if onBoundary:
+          transition(WordBoundryMatch)
+
+      of CharDiff, LeadingCharDiff:
+        var isLeadingChar = (
+          str[strIndex - 1] notin Letters or
+          str[strIndex - 1] in {'a'..'z'} and
+          str[strIndex] in {'A'..'Z'}
+        )
+
+        if isLeadingChar:
+          scoreState = LeadingCharMatch
+          #a non alpha or a camel case transition counts as a leading char.
+          # Transition the state, but don't give the bonus yet; wait until we verify a consecutive match.
+        else:
+          transition(CharMatch)
+      patIndex += 1
+
+    else:
+      case scoreState
+      of StartMatch:
+        transition(LeadingCharDiff)
+
+      of ConsecutiveMatch:
+        transition(CharDiff)
+        consecutiveMatchCount = 0
+
+      of LeadingCharDiff:
+        if unmatchedLeadingCharCount < MaxUnmatchedLeadingChar:
+          transition(LeadingCharDiff)
+        unmatchedLeadingCharCount += 1
+
+      else:
+        transition(CharDiff)
+
+    strIndex += 1
+
+  if patIndex == pattern.len and (strIndex == str.len or str[strIndex] notin Letters):
+    score += 10
+
+  result = (
+    score:   max(0, score),
+    matched: (score > 0),
+  )
diff --git a/tools/dochack/karax.nim b/tools/dochack/karax.nim
deleted file mode 100644
index d9619992b..000000000
--- a/tools/dochack/karax.nim
+++ /dev/null
@@ -1,343 +0,0 @@
-# Simple lib to write JS UIs
-
-import dom
-
-export dom.Element, dom.Event, dom.cloneNode, dom
-
-proc kout*[T](x: T) {.importc: "console.log", varargs.}
-  ## the preferred way of debugging karax applications.
-
-proc id*(e: Node): cstring {.importcpp: "#.id", nodecl.}
-proc `id=`*(e: Node; x: cstring) {.importcpp: "#.id = #", nodecl.}
-proc className*(e: Node): cstring {.importcpp: "#.className", nodecl.}
-proc `className=`*(e: Node; v: cstring) {.importcpp: "#.className = #", nodecl.}
-
-proc value*(e: Element): cstring {.importcpp: "#.value", nodecl.}
-proc `value=`*(e: Element; v: cstring) {.importcpp: "#.value = #", nodecl.}
-
-proc getElementsByClass*(e: Element; name: cstring): seq[Element] {.importcpp: "#.getElementsByClassName(#)", nodecl.}
-
-proc toLower*(x: cstring): cstring {.
-  importcpp: "#.toLowerCase()", nodecl.}
-proc replace*(x: cstring; search, by: cstring): cstring {.
-  importcpp: "#.replace(#, #)", nodecl.}
-
-type
-  EventHandler* = proc(ev: Event)
-  EventHandlerId* = proc(ev: Event; id: int)
-
-  Timeout* = ref object
-
-var document* {.importc.}: Document
-
-var
-  dorender: proc (): Element {.closure.}
-  drawTimeout: Timeout
-  currentTree: Element
-
-proc setRenderer*(renderer: proc (): Element) =
-  dorender = renderer
-
-proc setTimeout*(action: proc(); ms: int): Timeout {.importc, nodecl.}
-proc clearTimeout*(t: Timeout) {.importc, nodecl.}
-proc targetElem*(e: Event): Element = cast[Element](e.target)
-
-proc getElementById*(id: cstring): Element {.importc: "document.getElementById", nodecl.}
-
-proc getElementsByClassName*(cls: cstring): seq[Element] {.importc:
-  "document.getElementsByClassName", nodecl.}
-
-proc textContent*(e: Element): cstring {.
-  importcpp: "#.textContent", nodecl.}
-
-proc replaceById*(id: cstring; newTree: Node) =
-  let x = getElementById(id)
-  x.parentNode.replaceChild(newTree, x)
-  newTree.id = id
-
-proc equals(a, b: Node): bool =
-  if a.nodeType != b.nodeType: return false
-  if a.id != b.id: return false
-  if a.nodeName != b.nodeName: return false
-  if a.nodeType == TextNode:
-    if a.data != b.data: return false
-  elif a.childNodes.len != b.childNodes.len:
-    return false
-  if a.className != b.className:
-    # style differences are updated in place and we pretend
-    # it's still the same node
-    a.className = b.className
-    #return false
-  return true
-
-proc diffTree(parent, a, b: Node) =
-  if equals(a, b):
-    if a.nodeType != TextNode:
-      # we need to do this correctly in the presence of asyncronous
-      # DOM updates:
-      var i = 0
-      while i < a.childNodes.len and a.childNodes.len == b.childNodes.len:
-        diffTree(a, a.childNodes[i], b.childNodes[i])
-        inc i
-  elif parent == nil:
-    replaceById("ROOT", b)
-  else:
-    parent.replaceChild(b, a)
-
-proc dodraw() =
-  let newtree = dorender()
-  newtree.id = "ROOT"
-  if currentTree == nil:
-    currentTree = newtree
-    replaceById("ROOT", currentTree)
-  else:
-    diffTree(nil, currentTree, newtree)
-
-proc redraw*() =
-  # we buffer redraw requests:
-  if drawTimeout != nil:
-    clearTimeout(drawTimeout)
-  drawTimeout = setTimeout(dodraw, 30)
-
-proc tree*(tag: string; kids: varargs[Element]): Element =
-  result = document.createElement tag
-  for k in kids:
-    result.appendChild k
-
-proc tree*(tag: string; attrs: openarray[(string, string)];
-           kids: varargs[Element]): Element =
-  result = tree(tag, kids)
-  for a in attrs: result.setAttribute(a[0], a[1])
-
-proc text*(s: string): Element = cast[Element](document.createTextNode(s))
-proc text*(s: cstring): Element = cast[Element](document.createTextNode(s))
-proc add*(parent, kid: Element) =
-  if parent.nodeName == "TR" and (kid.nodeName == "TD" or kid.nodeName == "TH"):
-    let k = document.createElement("TD")
-    appendChild(k, kid)
-    appendChild(parent, k)
-  else:
-    appendChild(parent, kid)
-
-proc len*(x: Element): int {.importcpp: "#.childNodes.length".}
-proc `[]`*(x: Element; idx: int): Element {.importcpp: "#.childNodes[#]".}
-
-proc isInt*(s: cstring): bool {.asmNoStackFrame.} =
-  asm """
-    return s.match(/^[0-9]+$/);
-  """
-
-var
-  linkCounter: int
-
-proc link*(id: int): Element =
-  result = document.createElement("a")
-  result.setAttribute("href", "#")
-  inc linkCounter
-  result.setAttribute("id", $linkCounter & ":" & $id)
-
-proc link*(action: EventHandler): Element =
-  result = document.createElement("a")
-  result.setAttribute("href", "#")
-  addEventListener(result, "click", action)
-
-proc parseInt*(s: cstring): int {.importc, nodecl.}
-proc parseFloat*(s: cstring): float {.importc, nodecl.}
-proc split*(s, sep: cstring): seq[cstring] {.importcpp, nodecl.}
-
-proc startsWith*(a, b: cstring): bool {.importcpp: "startsWith", nodecl.}
-proc contains*(a, b: cstring): bool {.importcpp: "(#.indexOf(#)>=0)", nodecl.}
-proc substr*(s: cstring; start: int): cstring {.importcpp: "substr", nodecl.}
-proc substr*(s: cstring; start, length: int): cstring {.importcpp: "substr", nodecl.}
-
-#proc len*(s: cstring): int {.importcpp: "#.length", nodecl.}
-proc `&`*(a, b: cstring): cstring {.importcpp: "(# + #)", nodecl.}
-proc toCstr*(s: int): cstring {.importcpp: "((#)+'')", nodecl.}
-
-proc suffix*(s, prefix: cstring): cstring =
-  if s.startsWith(prefix):
-    result = s.substr(prefix.len)
-  else:
-    kout(cstring"bug! " & s & cstring" does not start with " & prefix)
-
-proc valueAsInt*(e: Element): int = parseInt(e.value)
-proc suffixAsInt*(s, prefix: cstring): int = parseInt(suffix(s, prefix))
-
-proc scrollTop*(e: Element): int {.importcpp: "#.scrollTop", nodecl.}
-proc offsetHeight*(e: Element): int {.importcpp: "#.offsetHeight", nodecl.}
-proc offsetTop*(e: Element): int {.importcpp: "#.offsetTop", nodecl.}
-
-template onImpl(s) {.dirty} =
-  proc wrapper(ev: Event) =
-    action(ev)
-    redraw()
-  addEventListener(e, s, wrapper)
-
-proc setOnclick*(e: Element; action: proc(ev: Event)) =
-  onImpl "click"
-
-proc setOnclick*(e: Element; action: proc(ev: Event; id: int)) =
-  proc wrapper(ev: Event) =
-    let id = ev.target.id
-    let a = id.split(":")
-    if a.len == 2:
-      action(ev, parseInt(a[1]))
-      redraw()
-    else:
-      kout(cstring("cannot deal with id "), id)
-  addEventListener(e, "click", wrapper)
-
-proc setOnfocuslost*(e: Element; action: EventHandler) =
-  onImpl "blur"
-
-proc setOnchanged*(e: Element; action: EventHandler) =
-  onImpl "change"
-
-proc setOnscroll*(e: Element; action: EventHandler) =
-  onImpl "scroll"
-
-proc select*(choices: openarray[string]): Element =
-  result = document.createElement("select")
-  var i = 0
-  for c in choices:
-    result.add tree("option", [("value", $i)], text(c))
-    inc i
-
-proc select*(choices: openarray[(int, string)]): Element =
-  result = document.createElement("select")
-  for c in choices:
-    result.add tree("option", [("value", $c[0])], text(c[1]))
-
-var radioCounter: int
-
-proc radio*(choices: openarray[(int, string)]): Element =
-  result = document.createElement("fieldset")
-  var i = 0
-  inc radioCounter
-  for c in choices:
-    let id = "radio_" & c[1] & $i
-    var kid = tree("input", [("type", "radio"),
-      ("id", id), ("name", "radio" & $radioCounter),
-      ("value", $c[0])])
-    if i == 0:
-      kid.setAttribute("checked", "checked")
-    var lab = tree("label", [("for", id)], text(c[1]))
-    kid.add lab
-    result.add kid
-    inc i
-
-proc tag*(name: string; id="", class=""): Element =
-  result = document.createElement(name)
-  if id.len > 0:
-    result.setAttribute("id", id)
-  if class.len > 0:
-    result.setAttribute("class", class)
-
-proc tdiv*(id="", class=""): Element = tag("div", id, class)
-proc span*(id="", class=""): Element = tag("span", id, class)
-
-proc th*(s: string): Element =
-  result = tag("th")
-  result.add text(s)
-
-proc td*(s: string): Element =
-  result = tag("td")
-  result.add text(s)
-
-proc td*(s: Element): Element =
-  result = tag("td")
-  result.add s
-
-proc td*(class: string; s: Element): Element =
-  result = tag("td")
-  result.add s
-  result.setAttribute("class", class)
-
-proc table*(class="", kids: varargs[Element]): Element =
-  result = tag("table", "", class)
-  for k in kids: result.add k
-
-proc tr*(kids: varargs[Element]): Element =
-  result = tag("tr")
-  for k in kids:
-    if k.nodeName == "TD" or k.nodeName == "TH":
-      result.add k
-    else:
-      result.add td(k)
-
-proc setClass*(e: Element; value: string) =
-  e.setAttribute("class", value)
-
-proc setAttr*(e: Element; key, value: cstring) =
-  e.setAttribute(key, value)
-
-proc getAttr*(e: Element; key: cstring): cstring {.
-  importcpp: "#.getAttribute(#)", nodecl.}
-
-proc realtimeInput*(id, val: string; changed: proc(value: cstring)): Element =
-  let oldElem = getElementById(id)
-  #if oldElem != nil: return oldElem
-  let newVal = if oldElem.isNil: val else: $oldElem.value
-  var timer: Timeout
-  proc wrapper() =
-    changed(getElementById(id).value)
-    redraw()
-  proc onkeyup(ev: Event) =
-    if timer != nil: clearTimeout(timer)
-    timer = setTimeout(wrapper, 400)
-  result = tree("input", [("type", "text"),
-    ("value", newVal),
-    ("id", id)])
-  result.addEventListener("keyup", onkeyup)
-
-proc ajax(meth, url: cstring; headers: openarray[(string, string)];
-          data: cstring;
-          cont: proc (httpStatus: int; response: cstring)) =
-  proc setRequestHeader(a, b: cstring) {.importc: "ajax.setRequestHeader".}
-  {.emit: """
-  var ajax = new XMLHttpRequest();
-  ajax.open(`meth`,`url`,true);""".}
-  for a, b in items(headers):
-    setRequestHeader(a, b)
-  {.emit: """
-  ajax.onreadystatechange = function(){
-    if(this.readyState == 4){
-      if(this.status == 200){
-        `cont`(this.status, this.responseText);
-      } else {
-        `cont`(this.status, this.statusText);
-      }
-    }
-  }
-  ajax.send(`data`);
-  """.}
-
-proc ajaxPut*(url: string; headers: openarray[(string, string)];
-          data: cstring;
-          cont: proc (httpStatus: int, response: cstring)) =
-  ajax("PUT", url, headers, data, cont)
-
-proc ajaxGet*(url: string; headers: openarray[(string, string)];
-          cont: proc (httpStatus: int, response: cstring)) =
-  ajax("GET", url, headers, nil, cont)
-
-{.push stackTrace:off.}
-
-proc setupErrorHandler*(useAlert=false) =
-  ## Installs an error handler that transforms native JS unhandled
-  ## exceptions into Nim based stack traces. If `useAlert` is false,
-  ## the error message it put into the console, otherwise `alert`
-  ## is called.
-  proc stackTraceAsCstring(): cstring = cstring(getStackTrace())
-  {.emit: """
-  window.onerror = function(msg, url, line, col, error) {
-    var x = "Error: " + msg + "\n" + `stackTraceAsCstring`()
-    if (`useAlert`)
-      alert(x);
-    else
-      console.log(x);
-    var suppressErrorAlert = true;
-    return suppressErrorAlert;
-  };""".}
-
-{.pop.}
diff --git a/tools/downloader.nim b/tools/downloader.nim
index 3d866f5d7..37811b273 100644
--- a/tools/downloader.nim
+++ b/tools/downloader.nim
@@ -45,7 +45,7 @@ proc download(pkg: string; c: Controls) {.async.} =
 
 proc apply(a: Actions; c: Controls) {.async.} =
   if a.mingw:
-    await download("mingw" & arch & "-6.3.0", c)
+    await download("mingw" & arch, c)
   if a.aporia:
     await download("aporia-0.4.0", c)
 
diff --git a/tools/finish.nim b/tools/finish.nim
index 4f2c72595..69838829c 100644
--- a/tools/finish.nim
+++ b/tools/finish.nim
@@ -5,15 +5,24 @@ import strutils, os, osproc, streams, browsers
 
 const
   arch = $(sizeof(int)*8)
-  mingw = "mingw$1-6.3.0.7z" % arch
+  mingw = "mingw$1.7z" % arch
   url = r"https://nim-lang.org/download/" & mingw
 
+var
+  interactive = true
+
 type
   DownloadResult = enum
     Failure,
     Manual,
     Success
 
+proc expand(s: string): string =
+  try:
+    result = expandFilename(s)
+  except OSError, IOError:
+    result = s
+
 proc unzip(): bool =
   if not fileExists("dist" / mingw):
     echo "Could not find ", "dist" / mingw
@@ -32,25 +41,34 @@ proc downloadMingw(): DownloadResult =
   let curl = findExe"curl"
   var cmd: string
   if curl.len > 0:
-    cmd = quoteShell(curl) & " --out " & "dist" / mingw & " " & url
+    cmd = quoteShell(curl) & " --output " & "dist" / mingw & " " & url
   elif fileExists"bin/nimgrab.exe":
     cmd = r"bin\nimgrab.exe " & url & " dist" / mingw
   if cmd.len > 0:
     if execShellCmd(cmd) != 0:
       echo "download failed! ", cmd
-      openDefaultBrowser(url)
-      result = Manual
+      if interactive:
+        openDefaultBrowser(url)
+        result = Manual
+      else:
+        result = Failure
     else:
       if unzip(): result = Success
   else:
-    openDefaultBrowser(url)
-    result = Manual
+    if interactive:
+      openDefaultBrowser(url)
+      result = Manual
+    else:
+      result = Failure
 
 when defined(windows):
   import registry
 
   proc askBool(m: string): bool =
     stdout.write m
+    if not interactive:
+      stdout.writeLine "y (non-interactive mode)"
+      return true
     while true:
       try:
         let answer = stdin.readLine().normalize
@@ -67,6 +85,9 @@ when defined(windows):
   proc askNumber(m: string; a, b: int): int =
     stdout.write m
     stdout.write " [" & $a & ".." & $b & "] "
+    if not interactive:
+      stdout.writeLine $a & " (non-interactive mode)"
+      return a
     while true:
       let answer = stdin.readLine()
       try:
@@ -102,12 +123,11 @@ when defined(windows):
 
   proc addToPathEnv*(e: string) =
     var p = tryGetUnicodeValue(r"Environment", "Path", HKEY_CURRENT_USER)
-    let x = if e.contains(Whitespace): "\"" & e & "\"" else: e
     if p.len > 0:
       p.add ";"
-      p.add x
+      p.add e
     else:
-      p = x
+      p = e
     setUnicodeValue(r"Environment", "Path", p, HKEY_CURRENT_USER)
 
   proc createShortcut(src, dest: string; icon = "") =
@@ -177,18 +197,21 @@ when defined(windows):
     let bits = $(sizeof(pointer)*8)
     for d in dirs:
       if dirExists d:
-        let x = expandFilename(d / "bin")
-        if checkGccArch(x): return x
-        else: incompat.add x
+        let x = expand(d / "bin")
+        if x.len != 0:
+          if checkGccArch(x): return x
+          else: incompat.add x
       elif dirExists(d & bits):
-        let x = expandFilename((d & bits) / "bin")
-        if checkGccArch(x): return x
-        else: incompat.add x
+        let x = expand((d & bits) / "bin")
+        if x.len != 0:
+          if checkGccArch(x): return x
+          else: incompat.add x
 
 proc main() =
   when defined(windows):
-    let nimDesiredPath = expandFilename(getCurrentDir() / "bin")
-    let nimbleDesiredPath = expandFilename(getEnv("USERPROFILE") / ".nimble" / "bin")
+    let nimDesiredPath = expand(getCurrentDir() / "bin")
+    let nimbleBin = getEnv("USERPROFILE") / ".nimble" / "bin"
+    let nimbleDesiredPath = expand(nimbleBin)
     let p = tryGetUnicodeValue(r"Environment", "Path",
       HKEY_CURRENT_USER) & ";" & tryGetUnicodeValue(
         r"System\CurrentControlSet\Control\Session Manager\Environment", "Path",
@@ -201,6 +224,8 @@ proc main() =
       if x.len == 0: continue
       let y = try: expandFilename(if x[0] == '"' and x[^1] == '"':
                                     substr(x, 1, x.len-2) else: x)
+              except OSError as e:
+                if e.errorCode == 0: x else: ""
               except: ""
       if y.cmpIgnoreCase(nimDesiredPath) == 0:
         nimAlreadyInPath = true
@@ -291,4 +316,6 @@ when isMainModule:
   when defined(testdownload):
     discard downloadMingw()
   else:
+    if "-y" in commandLineParams():
+      interactive = false
     main()
diff --git a/tools/grammar_nanny.nim b/tools/grammar_nanny.nim
new file mode 100644
index 000000000..cbdc51efc
--- /dev/null
+++ b/tools/grammar_nanny.nim
@@ -0,0 +1,54 @@
+## Simple tool to check for obvious mistakes in Nim's
+## grammar.txt file.
+
+import std / [strutils, sets]
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
+
+import ".." / compiler / [
+  llstream, lexer, options, msgs, idents,
+  lineinfos, pathutils]
+
+proc checkGrammarFileImpl(cache: IdentCache, config: ConfigRef) =
+  var f = AbsoluteFile"doc/grammar.txt"
+  let data = readFile(f.string).multiReplace({"IND{=}": "SAME_IND", "'": "\""})
+  var stream = llStreamOpen(data)
+  var declaredSyms = initHashSet[string]()
+  var usedSyms = initHashSet[string]()
+  usedSyms.incl "module" # 'module' is the start rule.
+  if stream != nil:
+    declaredSyms.incl "section" # special case for 'section(RULE)' in the grammar
+    var
+      L: Lexer
+      tok: Token
+    openLexer(L, f, stream, cache, config)
+    # load the first token:
+    rawGetTok(L, tok)
+    var word = ""
+    while tok.tokType != tkEof:
+      #printTok(config, tok)
+      if isKeyword(tok.tokType) or tok.tokType == tkSymbol:
+        word = tok.ident.s
+        rawGetTok(L, tok)
+        if tok.tokType == tkEquals:
+          declaredSyms.incl word
+          rawGetTok(L, tok)
+        elif not allCharsInSet(word, {'A'..'Z', '0'..'9', '_'}):
+          usedSyms.incl word
+      else:
+        rawGetTok(L, tok)
+    for u in declaredSyms:
+      if u notin usedSyms:
+        echo "Unused non-terminal: ", u
+
+    for u in usedSyms:
+      if u notin declaredSyms:
+        echo "Undeclared non-terminal: ", u
+
+    closeLexer(L)
+  else:
+    rawMessage(config, errGenerated, "cannot open file: " & f.string)
+
+proc checkGrammarFile* =
+  checkGrammarFileImpl(newIdentCache(), newConfigRef())
diff --git a/tools/heapdump2dot.nim b/tools/heapdump2dot.nim
index 4cee6d674..6704cdfb0 100644
--- a/tools/heapdump2dot.nim
+++ b/tools/heapdump2dot.nim
@@ -1,5 +1,5 @@
 
-include prelude
+include std/prelude
 
 proc main(input, output: string) =
   type NodeKind = enum
diff --git a/tools/heapdumprepl.nim b/tools/heapdumprepl.nim
index ffa153f5d..4f06cf111 100644
--- a/tools/heapdumprepl.nim
+++ b/tools/heapdumprepl.nim
@@ -1,5 +1,4 @@
-
-include prelude
+include std/prelude
 import intsets
 
 type
@@ -17,7 +16,6 @@ type
     roots: Table[int, NodeKind]
 
 proc add(father: Node; son: int) =
-  if father.kids.isNil: father.kids = @[]
   father.kids.add(son)
 
 proc renderNode(g: Graph; id: int) =
@@ -49,7 +47,7 @@ const Help = """
 quit          -- quits this REPL
 locals, l     -- output the list of local stack roots
 globals, g    -- output the list of global roots
-alias name addr -- give addr a name. start 'addr' with 'x' for hexidecimal
+alias name addr -- give addr a name. start 'addr' with 'x' for hexadecimal
                    notation
 print name|addr  -- print a node by name or address
 reachable,r  l|g|node  dest   -- outputs TRUE or FALSE depending on whether
@@ -141,8 +139,8 @@ proc importData(input: string): Graph =
     close(i)
   else:
     quit "error: cannot open " & input
-  shallowCopy(result.nodes, nodes)
-  shallowCopy(result.roots, roots)
+  result.nodes = move nodes
+  result.roots = move roots
 
 if paramCount() == 1:
   repl(importData(paramStr(1)))
diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim
new file mode 100644
index 000000000..477fb29fa
--- /dev/null
+++ b/tools/kochdocs.nim
@@ -0,0 +1,388 @@
+## Part of 'koch' responsible for the documentation generation.
+
+import std/[os, strutils, osproc, sets, pathnorm, sequtils, pegs]
+
+import officialpackages
+export exec
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+from std/private/globs import nativeToUnixPath, walkDirRecFilter, PathEntry
+import "../compiler/nimpaths"
+
+const
+  gaCode* = " --doc.googleAnalytics:UA-48159761-1"
+  paCode* = " --doc.plausibleAnalytics:nim-lang.org"
+  # errormax: subsequent errors are probably consequences of 1st one; a simple
+  # bug could cause unlimited number of errors otherwise, hard to debug in CI.
+  docDefines = "-d:nimExperimentalLinenoiseExtra"
+  nimArgs = "--errormax:3 --hint:Conf:off --hint:Path:off --hint:Processing:off --hint:XDeclaredButNotUsed:off --warning:UnusedImport:off -d:boot --putenv:nimversion=$# $#" % [system.NimVersion, docDefines]
+  gitUrl = "https://github.com/nim-lang/Nim"
+  docHtmlOutput = "doc/html"
+  webUploadOutput = "web/upload"
+
+var nimExe*: string
+const allowList = ["jsbigints.nim", "jsheaders.nim", "jsformdata.nim", "jsfetch.nim", "jsutils.nim"]
+
+template isJsOnly(file: string): bool =
+  file.isRelativeTo("lib/js") or
+  file.extractFilename in allowList
+
+proc exe*(f: string): string =
+  result = addFileExt(f, ExeExt)
+  when defined(windows):
+    result = result.replace('/','\\')
+
+proc findNimImpl*(): tuple[path: string, ok: bool] =
+  if nimExe.len > 0: return (nimExe, true)
+  let nim = "nim".exe
+  result.path = "bin" / nim
+  result.ok = true
+  if fileExists(result.path): return
+  for dir in split(getEnv("PATH"), PathSep):
+    result.path = dir / nim
+    if fileExists(result.path): return
+  # assume there is a symlink to the exe or something:
+  return (nim, false)
+
+proc findNim*(): string = findNimImpl().path
+
+template inFold*(desc, body) =
+  if existsEnv("GITHUB_ACTIONS"):
+    echo "::group::" & desc
+  elif existsEnv("TF_BUILD"):
+    echo "##[group]" & desc
+  body
+  if existsEnv("GITHUB_ACTIONS"):
+    echo "::endgroup::"
+  elif existsEnv("TF_BUILD"):
+    echo "##[endgroup]"
+
+proc execFold*(desc, cmd: string, errorcode: int = QuitFailure, additionalPath = "") =
+  ## Execute shell command. Add log folding for various CI services.
+  let desc = if desc.len == 0: cmd else: desc
+  inFold(desc):
+    exec(cmd, errorcode, additionalPath)
+
+proc execCleanPath*(cmd: string,
+                   additionalPath = ""; errorcode: int = QuitFailure) =
+  # simulate a poor man's virtual environment
+  let prevPath = getEnv("PATH")
+  when defined(windows):
+    let cleanPath = r"$1\system32;$1;$1\System32\Wbem" % getEnv"SYSTEMROOT"
+  else:
+    const cleanPath = r"/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin"
+  putEnv("PATH", cleanPath & PathSep & additionalPath)
+  echo(cmd)
+  if execShellCmd(cmd) != 0: quit("FAILURE", errorcode)
+  putEnv("PATH", prevPath)
+
+proc nimexec*(cmd: string) =
+  # Consider using `nimCompile` instead
+  exec findNim().quoteShell() & " " & cmd
+
+proc nimCompile*(input: string, outputDir = "bin", mode = "c", options = "") =
+  let output = outputDir / input.splitFile.name.exe
+  let cmd = findNim().quoteShell() & " " & mode & " -o:" & output & " " & options & " " & input
+  exec cmd
+
+proc nimCompileFold*(desc, input: string, outputDir = "bin", mode = "c", options = "", outputName = "") =
+  let outputName2 = if outputName.len == 0: input.splitFile.name.exe else: outputName.exe
+  let output = outputDir / outputName2
+  let cmd = findNim().quoteShell() & " " & mode & " -o:" & output & " " & options & " " & input
+  execFold(desc, cmd)
+
+const officialPackagesMarkdown = """
+pkgs/atlas/doc/atlas.md
+""".splitWhitespace()
+
+proc getMd2html(): seq[string] =
+  for a in walkDirRecFilter("doc"):
+    let path = a.path
+    if a.kind == pcFile and path.splitFile.ext == ".md" and path.lastPathPart notin
+        ["docs.md",
+         "docstyle.md" # docstyle.md shouldn't be converted to html separately;
+                       # it's included in contributing.md.
+        ]:
+          # `docs` is redundant with `overview`, might as well remove that file?
+      result.add path
+  for md in officialPackagesMarkdown:
+    result.add md
+  doAssert "doc/manual/var_t_return.md".unixToNativePath in result # sanity check
+
+const
+  mdPdfList = """
+manual.md
+lib.md
+tut1.md
+tut2.md
+tut3.md
+nimc.md
+niminst.md
+mm.md
+""".splitWhitespace().mapIt("doc" / it)
+
+  withoutIndex = """
+lib/wrappers/tinyc.nim
+lib/wrappers/pcre.nim
+lib/wrappers/openssl.nim
+lib/posix/posix.nim
+lib/posix/linux.nim
+lib/posix/termios.nim
+""".splitWhitespace()
+
+  # some of these are include files so shouldn't be docgen'd
+  ignoredModules = """
+lib/pure/future.nim
+lib/pure/collections/hashcommon.nim
+lib/pure/collections/tableimpl.nim
+lib/pure/collections/setimpl.nim
+lib/pure/ioselects/ioselectors_kqueue.nim
+lib/pure/ioselects/ioselectors_select.nim
+lib/pure/ioselects/ioselectors_poll.nim
+lib/pure/ioselects/ioselectors_epoll.nim
+lib/posix/posix_macos_amd64.nim
+lib/posix/posix_other.nim
+lib/posix/posix_nintendoswitch.nim
+lib/posix/posix_nintendoswitch_consts.nim
+lib/posix/posix_linux_amd64.nim
+lib/posix/posix_linux_amd64_consts.nim
+lib/posix/posix_other_consts.nim
+lib/posix/posix_freertos_consts.nim
+lib/posix/posix_openbsd_amd64.nim
+lib/posix/posix_haiku.nim
+lib/pure/md5.nim
+lib/std/sha1.nim
+lib/pure/htmlparser.nim
+""".splitWhitespace()
+
+  officialPackagesList = """
+pkgs/asyncftpclient/src/asyncftpclient.nim
+pkgs/smtp/src/smtp.nim
+pkgs/punycode/src/punycode.nim
+pkgs/db_connector/src/db_connector/db_common.nim
+pkgs/db_connector/src/db_connector/db_mysql.nim
+pkgs/db_connector/src/db_connector/db_odbc.nim
+pkgs/db_connector/src/db_connector/db_postgres.nim
+pkgs/db_connector/src/db_connector/db_sqlite.nim
+pkgs/checksums/src/checksums/md5.nim
+pkgs/checksums/src/checksums/sha1.nim
+pkgs/checksums/src/checksums/sha2.nim
+pkgs/checksums/src/checksums/sha3.nim
+pkgs/checksums/src/checksums/bcrypt.nim
+pkgs/htmlparser/src/htmlparser.nim
+""".splitWhitespace()
+
+  officialPackagesListWithoutIndex = """
+pkgs/db_connector/src/db_connector/mysql.nim
+pkgs/db_connector/src/db_connector/sqlite3.nim
+pkgs/db_connector/src/db_connector/postgres.nim
+pkgs/db_connector/src/db_connector/odbcsql.nim
+pkgs/db_connector/src/db_connector/private/dbutils.nim
+""".splitWhitespace()
+
+when (NimMajor, NimMinor) < (1, 1) or not declared(isRelativeTo):
+  proc isRelativeTo(path, base: string): bool =
+    let path = path.normalizedPath
+    let base = base.normalizedPath
+    let ret = relativePath(path, base)
+    result = path.len > 0 and not ret.startsWith ".."
+
+proc getDocList(): seq[string] =
+  ##
+  var docIgnore: HashSet[string]
+  for a in withoutIndex: docIgnore.incl a
+  for a in ignoredModules: docIgnore.incl a
+
+  # don't ignore these even though in lib/system (not include files)
+  const goodSystem = """
+lib/system/nimscript.nim
+lib/system/assertions.nim
+lib/system/iterators.nim
+lib/system/exceptions.nim
+lib/system/dollars.nim
+lib/system/ctypes.nim
+""".splitWhitespace()
+
+  proc follow(a: PathEntry): bool =
+    result = a.path.lastPathPart notin ["nimcache", htmldocsDirname,
+                                        "includes", "deprecated", "genode"] and
+      not a.path.isRelativeTo("lib/fusion") # fusion was un-bundled but we need to keep this in case user has it installed
+  for entry in walkDirRecFilter("lib", follow = follow):
+    let a = entry.path
+    if entry.kind != pcFile or a.splitFile.ext != ".nim" or
+       (a.isRelativeTo("lib/system") and a.nativeToUnixPath notin goodSystem) or
+       a.nativeToUnixPath in docIgnore:
+         continue
+    result.add a
+  result.add normalizePath("nimsuggest/sexp.nim")
+
+let doc = getDocList()
+
+proc sexec(cmds: openArray[string]) =
+  ## Serial queue wrapper around exec.
+  for cmd in cmds:
+    echo(cmd)
+    let (outp, exitCode) = osproc.execCmdEx(cmd)
+    if exitCode != 0: quit outp
+
+proc mexec(cmds: openArray[string]) =
+  ## Multiprocessor version of exec
+  let r = execProcesses(cmds, {poStdErrToStdOut, poParentStreams, poEchoCmd})
+  if r != 0:
+    echo "external program failed, retrying serial work queue for logs!"
+    sexec(cmds)
+
+proc buildDocSamples(nimArgs, destPath: string) =
+  ## Special case documentation sample proc.
+  ##
+  ## TODO: consider integrating into the existing generic documentation builders
+  ## now that we have a single `doc` command.
+  exec(findNim().quoteShell() & " doc $# -o:$# $#" %
+    [nimArgs, destPath / "docgen_sample.html", "doc" / "docgen_sample.nim"])
+
+proc buildDocPackages(nimArgs, destPath: string, indexOnly: bool) =
+  # compiler docs; later, other packages (perhaps tools, testament etc)
+  let nim = findNim().quoteShell()
+    # to avoid broken links to manual from compiler dir, but a multi-package
+    # structure could be supported later
+
+  proc docProject(outdir, options, mainproj: string) =
+    exec("$nim doc --project --outdir:$outdir $nimArgs --git.url:$gitUrl $index $options $mainproj" % [
+      "nim", nim,
+      "outdir", outdir,
+      "nimArgs", nimArgs,
+      "gitUrl", gitUrl,
+      "options", options,
+      "mainproj", mainproj,
+      "index", if indexOnly: "--index:only" else: ""
+      ])
+  let extra = "-u:boot"
+  # xxx keep in sync with what's in $nim_prs_D/config/nimdoc.cfg, or, rather,
+  # start using nims instead of nimdoc.cfg
+  docProject(destPath/"compiler", extra, "compiler/index.nim")
+
+proc buildDoc(nimArgs, destPath: string, indexOnly: bool) =
+  # call nim for the documentation:
+  let rst2html = getMd2html()
+  var
+    commands = newSeq[string](rst2html.len + len(doc) + withoutIndex.len +
+              officialPackagesList.len + officialPackagesListWithoutIndex.len)
+    i = 0
+  let nim = findNim().quoteShell()
+
+  let index = if indexOnly: "--index:only" else: ""
+  for d in items(rst2html):
+    commands[i] = nim & " md2html $# --git.url:$# -o:$# $# $#" %
+      [nimArgs, gitUrl,
+      destPath / changeFileExt(splitFile(d).name, "html"), index, d]
+    i.inc
+  for d in items(doc):
+    let extra = if isJsOnly(d): "--backend:js" else: ""
+    var nimArgs2 = nimArgs
+    if d.isRelativeTo("compiler"): doAssert false
+    commands[i] = nim & " doc $# $# --git.url:$# --outdir:$# $# $#" %
+      [extra, nimArgs2, gitUrl, destPath, index, d]
+    i.inc
+  for d in items(withoutIndex):
+    commands[i] = nim & " doc $# --git.url:$# -o:$# $#" %
+      [nimArgs, gitUrl,
+      destPath / changeFileExt(splitFile(d).name, "html"), d]
+    i.inc
+
+
+  for d in items(officialPackagesList):
+    var nimArgs2 = nimArgs
+    if d.isRelativeTo("compiler"): doAssert false
+    commands[i] = nim & " doc $# --outdir:$# --index:on $#" %
+      [nimArgs2, destPath, d]
+    i.inc
+  for d in items(officialPackagesListWithoutIndex):
+    commands[i] = nim & " doc $# -o:$# $#" %
+      [nimArgs,
+      destPath / changeFileExt(splitFile(d).name, "html"), d]
+    i.inc
+
+  mexec(commands)
+
+proc nim2pdf(src: string, dst: string, nimArgs: string) =
+  # xxx expose as a `nim` command or in some other reusable way.
+  let outDir = "build" / "xelatextmp" # xxx factor pending https://github.com/timotheecour/Nim/issues/616
+  # note: this will generate temporary files in gitignored `outDir`: aux toc log out tex
+  exec("$# md2tex $# --outdir:$# $#" % [findNim().quoteShell(), nimArgs, outDir.quoteShell, src.quoteShell])
+  let texFile = outDir / src.lastPathPart.changeFileExt("tex")
+  for i in 0..<3: # call LaTeX three times to get cross references right:
+    let xelatexLog = outDir / "xelatex.log"
+    # `>` should work on windows, if not, we can use `execCmdEx`
+    let cmd = "xelatex -interaction=nonstopmode -output-directory=$# $# > $#" % [outDir.quoteShell, texFile.quoteShell, xelatexLog.quoteShell]
+    exec(cmd) # on error, user can inspect `xelatexLog`
+    if i == 1:  # build .ind file
+      var texFileBase = texFile
+      texFileBase.removeSuffix(".tex")
+      let cmd = "makeindex $# > $#" % [
+          texFileBase.quoteShell, xelatexLog.quoteShell]
+      exec(cmd)
+  moveFile(texFile.changeFileExt("pdf"), dst)
+
+proc buildPdfDoc*(args: string, destPath: string) =
+  let args = nimArgs & " " & args
+  var pdfList: seq[string]
+  createDir(destPath)
+  if os.execShellCmd("xelatex -version") != 0:
+    doAssert false, "xelatex not found" # or, raise an exception
+  else:
+    for src in items(mdPdfList):
+      let dst = destPath / src.lastPathPart.changeFileExt("pdf")
+      pdfList.add dst
+      nim2pdf(src, dst, args)
+  echo "\nOutput PDF files: \n  ", pdfList.join(" ") # because `nim2pdf` is a bit verbose
+
+proc buildJS(): string =
+  let nim = findNim()
+  exec("$# js -d:release --out:$# tools/nimblepkglist.nim" %
+      [nim.quoteShell(), webUploadOutput / "nimblepkglist.js"])
+      # xxx deadcode? and why is it only for webUploadOutput, not for local docs?
+  result = getDocHacksJs(nimr = getCurrentDir(), nim)
+
+proc buildDocsDir*(args: string, dir: string) =
+  let args = nimArgs & " " & args
+  let docHackJsSource = buildJS()
+  gitClonePackages(@["asyncftpclient", "punycode", "smtp", "db_connector", "checksums", "atlas", "htmlparser"])
+  createDir(dir)
+  buildDocSamples(args, dir)
+
+  # generate `.idx` files and top-level `theindex.html`:
+  buildDoc(args, dir, indexOnly=true) # bottleneck
+  let nim = findNim().quoteShell()
+  exec(nim & " buildIndex -o:$1/theindex.html $1" % [dir])
+    # caveat: this works so long it's called before `buildDocPackages` which
+    # populates `compiler/` with unrelated idx files that shouldn't be in index,
+    # so should work in CI but you may need to remove your generated html files
+    # locally after calling `./koch docs`. The clean fix would be for `idx` files
+    # to be transient with `--project` (eg all in memory).
+  buildDocPackages(args, dir, indexOnly=true)
+
+  # generate HTML and package-level `theindex.html`:
+  buildDoc(args, dir, indexOnly=false) # bottleneck
+  buildDocPackages(args, dir, indexOnly=false)
+
+  copyFile(dir / "overview.html", dir / "index.html")
+  copyFile(docHackJsSource, dir / docHackJsSource.lastPathPart)
+
+proc buildDocs*(args: string, localOnly = false, localOutDir = "") =
+  let localOutDir =
+    if localOutDir.len == 0:
+      docHtmlOutput
+    else:
+      localOutDir
+
+  var args = args
+
+  if not localOnly:
+    buildDocsDir(args, webUploadOutput / NimVersion)
+
+    let gaFilter = peg"@( y'--doc.googleAnalytics:' @(\s / $) )"
+    args = args.replace(gaFilter)
+
+  buildDocsDir(args, localOutDir)
diff --git a/tools/nim.zsh-completion b/tools/nim.zsh-completion
index e9b9002fb..1c3670fd9 100644
--- a/tools/nim.zsh-completion
+++ b/tools/nim.zsh-completion
@@ -1,74 +1,120 @@
 #compdef nim
 
+# Installation note:
+# Please name this file as _nim (with underscore!) and copy it to a
+# completions directory, either:
+# - system wide one, like /usr/share/zsh/functions/Completion/Unix/ on Linux
+# - or to a user directory like ~/scripts. Then you also need to add
+#   that directory in your ~/.zshrc to `fpath` array like so:
+#     fpath=( ~/scripts "${fpath[@]}" )
+
 _nim() {
-  _arguments -C \
-    ':command:((
-      {compile,c}\:compile\ project\ with\ default\ code\ generator\ C
-      doc\:generate\ the\ documentation\ for\ inputfile
-      {compileToC,cc}\:compile\ project\ with\ C\ code\ generator
-      {compileToCpp,cpp}\:compile\ project\ to\ C++\ code
-      {compileToOC,objc}\:compile\ project\ to\ Objective\ C\ code
-      js\:compile\ project\ to\ Javascript
-      e\:run\ a\ Nimscript\ file
-      rst2html\:convert\ a\ reStructuredText\ file\ to\ HTML
-      rst2tex\:convert\ a\ reStructuredText\ file\ to\ TeX
-      jsondoc\:extract\ the\ documentation\ to\ a\ json\ file
-      buildIndex\:build\ an\ index\ for\ the\ whole\ documentation
-      genDepend\:generate\ a\ DOT\ file\ containing\ the\ module\ dependency\ graph
-      dump\:dump\ all\ defined\ conditionals\ and\ search\ paths
-      check\:checks\ the\ project\ for\ syntax\ and\ semantic
-    ))' \
-    '*-r[run the application]' \
-    '*--run[run the application]' \
-    '*-p=[add path to search paths]' \
-    '*--path=[add path to search paths]' \
-    '*-d=[define a conditional symbol]' \
-    '*--define=[define a conditional symbol]' \
-    '*-u=[undefine a conditional symbol]' \
-    '*--undef=[undefine a conditional symbol]' \
-    '*-f[force rebuilding of all modules]' \
-    '*--forceBuild[force rebuilding of all modules]' \
-    '*--stackTrace=on[turn stack tracing on]' \
-    '*--stackTrace=off[turn stack tracing off]' \
-    '*--lineTrace=on[turn line tracing on]' \
-    '*--lineTrace=off[turn line tracing off]' \
-    '*--threads=on[turn support for multi-threading on]' \
-    '*--threads=off[turn support for multi-threading off]' \
-    '*-x=on[turn all runtime checks on]' \
-    '*-x=off[turn all runtime checks off]' \
-    '*--checks=on[turn all runtime checks on]' \
-    '*--checks=off[turn all runtime checks off]' \
-    '*--objChecks=on[turn obj conversion checks on]' \
-    '*--objChecks=off[turn obj conversion checks off]' \
-    '*--fieldChecks=on[turn case variant field checks on]' \
-    '*--fieldChecks=off[turn case variant field checks off]' \
-    '*--rangeChecks=on[turn range checks on]' \
-    '*--rangeChecks=off[turn range checks off]' \
-    '*--boundChecks=on[turn bound checks on]' \
-    '*--boundChecks=off[turn bound checks off]' \
-    '*--overflowChecks=on[turn int over-/underflow checks on]' \
-    '*--overflowChecks=off[turn int over-/underflow checks off]' \
-    '*-a[turn assertions on]' \
-    '*-a[turn assertions off]' \
-    '*--assertions=on[turn assertions on]' \
-    '*--assertions=off[turn assertions off]' \
-    '*--floatChecks=on[turn all floating point (NaN/Inf) checks on]' \
-    '*--floatChecks=off[turn all floating point (NaN/Inf) checks off]' \
-    '*--nanChecks=on[turn NaN checks on]' \
-    '*--nanChecks=off[turn NaN checks off]' \
-    '*--infChecks=on[turn Inf checks on]' \
-    '*--infChecks=off[turn Inf checks off]' \
-    '*--nilChecks=on[turn nil checks on]' \
-    '*--nilChecks=off[turn nil checks off]' \
-    '*--opt=none[do not optimize]' \
-    '*--opt=speed[optimize for speed|size - use -d:release for a release build]' \
-    '*--opt=size[optimize for size]' \
-    '*--debugger:native|endb[use native debugger (gdb) | ENDB (experimental)]' \
-    '*--app=console[generate a console app]' \
-    '*--app=gui[generate a GUI app]' \
-    '*--app=lib[generate a dynamic library]' \
-    '*--app=staticlib[generate a static library]' \
-    ':filename:_files -g"*.nim"'
+  local -a nimCommands=(
+    {compile,c}:'compile project with default code generator C'
+    {compileToC,cc}:'compile project with C code generator'
+    {compileToCpp,cpp}:'compile project to C++ code'
+    {compileToOC,objc}:'compile project to Objective C code'
+    'js:compile project to Javascript'
+    'e:run a Nimscript file'
+    'doc:generate the HTML documentation for inputfile'
+    'rst2html:convert a reStructuredText file to HTML'
+    'doc2tex:generate the documentation for inputfile to LaTeX'
+    'rst2tex:convert a reStructuredText file to TeX'
+    'jsondoc:extract the documentation to a json file'
+    'buildIndex:build an index for the whole documentation'
+    'genDepend:generate a DOT file containing the module dependency graph'
+    'dump:dump all defined conditionals and search paths'
+    'check:checks the project for syntax and semantic'
+    {--help,-h}:'basic help'
+    '--fullhelp:show all switches'
+    {-v,--version}:'show version'
+  )
+
+  _arguments '*:: :->anyState' && return 0
+
+  if (( CURRENT == 1 )); then
+    _describe -t commands "Nim commands" nimCommands -V1
+    return
+  fi
+
+  local -a conditionalSymbols=(
+    release danger mingw androidNDK useNimRtl useMalloc noSignalHandler ssl
+    debug leanCompiler gcDestructors)
+  local -a sharedOpts=(
+    {--define\\:-,-d\\:-}'[define a conditional symbol]:x:($conditionalSymbols)'
+    {--undef\\:-,-u\\:-}'[undefine a conditional symbol]:x:($conditionalSymbols)'
+    {--path\\:-,-p\\:-}'[add path to search paths]:x:_files'
+    '--verbosity\:-[set verbosity level (default\: 1)]:x:(0 1 2 3)'
+    '--hints\:-[print compilation hints? (or `list`)]:x:(on off list)'
+  )
+  local runOpts=(
+    {--run,-r}'[run the application]'
+  )
+  local docOpts=(
+    '--index\:-[enable index .idx files?]:x:(on off)'
+    '--project\:-[output any dependency for doc?]:x:(on off)'
+    '--docInternal\:-[generate module-private documentation?]:x:(on off)'
+  )
+  local -a codeOpts=(
+    {--forceBuild,-f}'[force rebuilding of all modules]'
+    '--stackTrace\:-[enable stack tracing?]:x:(on off)'
+    '--lineTrace\:-[enable line tracing?]:x:(on off)'
+    '--threads\:-[enable support for multi-threading?]:x:(on off)'
+    {--checks\\:-,-x\\:-}'[enable/disable all runtime checks?]:x:(on off)'
+    '--objChecks\:-[enable obj conversion checks]:x:(on off)'
+    '--fieldChecks\:-[enable case variant field checks?]:x:(on off)'
+    '--rangeChecks\:-[enable range checks?]:x:(on off)'
+    '--boundChecks\:-[enable bound checks?]:x:(on off)'
+    '--overflowChecks\:-[enable integer over-/underflow checks?]:x:(on off)'
+    {--assertions\\:-,-a\\:-}'[enable assertions?]:x:(on off)'
+    '--floatChecks\:-[enable floating point (NaN/Inf) checks?]:x:(on off)'
+    '--nanChecks\:-[enable NaN checks?]:x:(on off)'
+    '--infChecks\:-[enable Inf checks?]:x:(on off)'
+    '--nilChecks\:-[enable nil checks?]:x:(on off)'
+    '--expandArc\:-[show how given proc looks before final backend pass]'
+    '--expandMacro\:-[dump every generated AST from given macro]'
+  )
+  local -a nativeOpts=(
+    '--opt\:-[optimization mode]:x:(none speed size)'
+    '--debugger\:native[use native debugger (gdb)]'
+    '--app\:-[generate this type of app (lib=dynamic)]:x:(console gui lib staticlib)'
+    '--cpu\:-[target architecture]:x:(alpha amd64 arm arm64 avr e2k esp hppa i386 ia64 js loongarch64 m68k mips mipsel mips64 mips64el msp430 nimvm powerpc powerpc64 powerpc64el riscv32 riscv64 sparc sparc64 vm wasm32)'
+    '--gc\:-[memory management algorithm to use (default\: refc)]:x:(refc arc orc markAndSweep boehm go regions none)'
+    '--os\:-[operating system to compile for]:x:(AIX Amiga Android Any Atari DOS DragonFly FreeBSD FreeRTOS Genode Haiku iOS Irix JS Linux MacOS MacOSX MorphOS NetBSD Netware NimVM NintendoSwitch OS2 OpenBSD PalmOS Standalone QNX SkyOS Solaris VxWorks Windows)'
+    '--panics\:-[turn panics into process termination (default\: off)]:x:(off on)'
+  )
+
+  case "$words[1]" in
+    compile|c|compileToC|cpp|compileToCpp|compileToOC|objc)
+      _arguments $codeOpts $runOpts $sharedOpts $nativeOpts \
+          '*:filename:_files -g"*.nim"'
+    ;;
+    js)
+      _arguments $codeOpts $runOpts $sharedOpts \
+          '*:filename:_files -g"*.nim"'
+    ;;
+    e)
+      _arguments $codeOpts $runOpts $sharedOpts \
+          '*:filename:_files -g"*.nims"'
+    ;;
+    doc|doc2tex|jsondoc)
+      _arguments $runOpts $sharedOpts '*:filename:_files -g"*.nim"'
+    ;;
+    rst2html|rst2tex)
+      _arguments $runOpts $sharedOpts $docOpts '*:filename:_files -g"*.rst"'
+    ;;
+    buildIndex|genDepend|check)
+      _arguments $sharedOpts '*:filename:_files -g"*.nim"'
+    ;;
+    dump)
+      _arguments $sharedOpts
+    ;;
+    *)
+      _arguments '*:filename:_files -g"*"'
+    ;;
+  esac
+
+  return 1
 }
 
 _nim "$@"
diff --git a/web/nimblepkglist.nim b/tools/nimblepkglist.nim
index 870944ab0..92e1cad20 100644
--- a/web/nimblepkglist.nim
+++ b/tools/nimblepkglist.nim
@@ -1,3 +1,6 @@
+#[
+deadcode?
+]#
 import base64, strutils, json, htmlgen, dom, algorithm
 
 type
@@ -53,16 +56,16 @@ proc processContent(content: string) =
   var officialPkgListDiv = document.getElementById("officialPkgList")
 
   officialPkgListDiv.innerHTML =
-    p("There are currently " & $officialCount &
+    (p("There are currently " & $officialCount &
       " official packages in the Nimble package repository.") &
-    ul(officialList)
+    ul(officialList)).cstring
 
   var unofficialPkgListDiv = document.getElementById("unofficialPkgList")
 
   unofficialPkgListDiv.innerHTML =
-    p("There are currently " & $unofficialCount &
+    (p("There are currently " & $unofficialCount &
       " unofficial packages in the Nimble package repository.") &
-    ul(unofficialList)
+    ul(unofficialList)).cstring
 
 proc gotPackageList(apiReply: TData) {.exportc.} =
   let decoded = decodeContent($apiReply.content)
@@ -73,5 +76,5 @@ proc gotPackageList(apiReply: TData) {.exportc.} =
     var unofficialPkgListDiv = document.getElementById("unofficialPkgList")
     let msg = p("Unable to retrieve package list: ",
       code(getCurrentExceptionMsg()))
-    officialPkgListDiv.innerHTML = msg
-    unofficialPkgListDiv.innerHTML = msg
+    officialPkgListDiv.innerHTML = msg.cstring
+    unofficialPkgListDiv.innerHTML = msg.cstring
diff --git a/tools/nimgrab.nim b/tools/nimgrab.nim
index 937a5dcd4..c86159739 100644
--- a/tools/nimgrab.nim
+++ b/tools/nimgrab.nim
@@ -1,33 +1,21 @@
-
-
-when defined(windows):
-  import os, urldownloader
-
-  proc syncDownload(url, file: string) =
-    proc progress(status: DownloadStatus, progress: uint, total: uint,
-                  message: string) {.procvar, gcsafe.} =
-      echo "Downloading " & url
-      let t = total.BiggestInt
-      if t != 0:
-        echo clamp(int(progress.BiggestInt*100 div t), 0, 100), "%"
-      else:
-        echo "0%"
-
-    downloadToFile(url, file, {optUseCache}, progress)
-    echo "100%"
-
-else:
-  import os, asyncdispatch, httpclient
-
-  proc syncDownload(url, file: string) =
-    var client = newHttpClient()
-    proc onProgressChanged(total, progress, speed: BiggestInt) =
-      echo "Downloading " & url & " " & $(speed div 1000) & "kb/s"
-      echo clamp(int(progress*100 div total), 0, 100), "%"
-
-    client.onProgressChanged = onProgressChanged
-    client.downloadFile(url, file)
-    echo "100%"
+import std/[os, httpclient]
+
+proc syncDownload(url, file: string) =
+  let client = newHttpClient()
+  proc onProgressChanged(total, progress, speed: BiggestInt) =
+    var message = "Downloading "
+    message.add url
+    message.add ' '
+    message.addInt speed div 1000
+    message.add "kb/s\n"
+    message.add $clamp(int(progress * 100 div total), 0, 100)
+    message.add '%'
+    echo message
+
+  client.onProgressChanged = onProgressChanged
+  client.downloadFile(url, file)
+  client.close()
+  echo "100%"
 
 if os.paramCount() != 2:
   quit "Usage: nimgrab <url> <file>"
diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim
index 9cfd7a86f..599c616ba 100644
--- a/tools/nimgrep.nim
+++ b/tools/nimgrep.nim
@@ -8,58 +8,156 @@
 #
 
 import
-  os, strutils, parseopt, pegs, re, terminal
+  os, strutils, parseopt, pegs, re, terminal, osproc, tables, algorithm, times
 
 const
-  Version = "1.2"
-  Usage = "nimgrep - Nim Grep Utility Version " & Version & """
-
-  (c) 2012 Andreas Rumpf
-Usage:
-  nimgrep [options] [pattern] [replacement] (file/directory)*
-Options:
-  --find, -f          find the pattern (default)
-  --replace, -r       replace the pattern
-  --peg               pattern is a peg
-  --re                pattern is a regular expression (default); extended
-                      syntax for the regular expression is always turned on
-  --recursive         process directories recursively
-  --confirm           confirm each occurrence/replacement; there is a chance
-                      to abort any time without touching the file
-  --stdin             read pattern from stdin (to avoid the shell's confusing
-                      quoting rules)
-  --word, -w          the match should have word boundaries (buggy for pegs!)
-  --ignoreCase, -i    be case insensitive
-  --ignoreStyle, -y   be style insensitive
-  --ext:EX1|EX2|...   only search the files with the given extension(s)
-  --nocolor           output will be given without any colours.
-  --oneline           show file on each matched line
-  --verbose           be verbose: list every processed file
-  --filenames         find the pattern in the filenames, not in the contents
-                      of the file
-  --help, -h          shows this help
-  --version, -v       shows the version
-"""
+  Version = "2.0.0"
+  Usage = "nimgrep - Nim Grep Searching and Replacement Utility Version " &
+  Version & """
+
+  (c) 2012-2020 Andreas Rumpf
+""" & slurp "../doc/nimgrep_cmdline.txt"
+
+# Limitations / ideas / TODO:
+# * No unicode support with --cols
+# * Consider making --onlyAscii default, since dumping binary data has
+#   stability and security repercussions
+# * Mode - reads entire buffer by whole from stdin, which is bad for streaming.
+#   To implement line-by-line reading after adding option to turn off
+#   multiline matches
+# * Add some form of file pre-processing, e.g. feed binary files to utility
+#   `strings` and then do the search inside these strings
+# * Add --showCol option to also show column (of match), not just line; it
+#   makes it easier when jump to line+col in an editor or on terminal
+
+
+# Search results for a file are modelled by these levels:
+# FileResult -> Block -> Output/Chunk -> SubLine
+#
+# 1. SubLine is an entire line or its part.
+#
+# 2. Chunk, which is a sequence of SubLine, represents a match and its
+#    surrounding context.
+#    Output is a Chunk or one of auxiliary results like an openError.
+#
+# 3. Block, which is a sequence of Chunks, is not present as a separate type.
+#    It will just be separated from another Block by newline when there is
+#    more than 3 lines in it.
+#    Here is an example of a Block where only 1 match is found and
+#    1 line before and 1 line after of context are required:
+#
+#     ...a_line_before...................................... <<<SubLine(Chunk 1)
+#
+#     .......pre.......  ....new_match....  .......post......
+#     ^^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^
+#     SubLine (Chunk 1)  SubLine (Chunk 1)  SubLine (Chunk 2)
+#
+#     ...a_line_after....................................... <<<SubLine(Chunk 2)
+#
+# 4. FileResult is printed as a sequence of Blocks.
+#    However FileResult is represented as seq[Output] in the program.
 
 type
   TOption = enum
     optFind, optReplace, optPeg, optRegex, optRecursive, optConfirm, optStdin,
-    optWord, optIgnoreCase, optIgnoreStyle, optVerbose, optFilenames
+    optWord, optIgnoreCase, optIgnoreStyle, optVerbose, optFilenames,
+    optRex, optFollow, optCount, optLimitChars, optPipe
   TOptions = set[TOption]
   TConfirmEnum = enum
     ceAbort, ceYes, ceAll, ceNo, ceNone
+  Bin = enum
+    biOn, biOnly, biOff
   Pattern = Regex | Peg
-
-using pattern: Pattern
+  MatchInfo = tuple[first: int, last: int;
+                    lineBeg: int, lineEnd: int, match: string]
+  outputKind = enum
+    openError, rejected, justCount,
+    blockFirstMatch, blockNextMatch, blockEnd, fileContents, outputFileName
+  Output = object
+    case kind: outputKind
+    of openError: msg: string           # file/directory not found
+    of rejected: reason: string         # when the file contents do not pass
+    of justCount: matches: int          # the only output for option --count
+    of blockFirstMatch, blockNextMatch: # the normal case: match itself
+      pre: string
+      match: MatchInfo
+    of blockEnd:                        # block ending right after prev. match
+      blockEnding: string
+      firstLine: int
+        # == last lineN of last match
+    of fileContents:                    # yielded for --replace only
+      buffer: string
+    of outputFileName:                  # yielded for --filenames when no
+      name: string                      #   PATTERN was provided
+  Trequest = (int, string)
+  FileResult = seq[Output]
+  Tresult = tuple[finished: bool, fileNo: int,
+                  filename: string, fileResult: FileResult]
+  WalkOpt = tuple  # used for walking directories/producing paths
+    extensions: seq[string]
+    notExtensions: seq[string]
+    filename: seq[string]
+    notFilename: seq[string]
+    dirPath: seq[string]
+    notDirPath: seq[string]
+    dirname : seq[string]
+    notDirname : seq[string]
+  WalkOptComp[Pat] = tuple  # a compiled version of the previous
+    filename: seq[Pat]
+    notFilename: seq[Pat]
+    dirname : seq[Pat]
+    notDirname : seq[Pat]
+    dirPath: seq[Pat]
+    notDirPath: seq[Pat]
+  SearchOpt = tuple  # used for searching inside a file
+    patternSet: bool           # To distinguish uninitialized/empty 'pattern'
+    pattern: string            # Main PATTERN
+    inFile: seq[string]        # --inFile, --inf
+    notInFile: seq[string]     # --notinFile, --ninf
+    inContext: seq[string]     # --inContext, --inc
+    notInContext: seq[string]  # --notinContext, --ninc
+    checkBin: Bin              # --bin, --text
+  SearchOptComp[Pat] = tuple  # a compiled version of the previous
+    pattern: Pat
+    inFile: seq[Pat]
+    notInFile: seq[Pat]
+    inContext: seq[Pat]
+    notInContext: seq[Pat]
+  SinglePattern[PAT] = tuple  # compile single pattern for replacef
+    pattern: PAT
+  Column = tuple  # current column info for the cropping (--limit) feature
+    terminal: int  # column in terminal emulator
+    file: int      # column in file (for correct Tab processing)
+    overflowMatches: int
 
 var
-  filenames: seq[string] = @[]
-  pattern = ""
+  paths: seq[string] = @[]
   replacement = ""
-  extensions: seq[string] = @[]
+  replacementSet = false
+    # to distinguish between uninitialized 'replacement' and empty one
   options: TOptions = {optRegex}
+  walkOpt {.threadvar.}: WalkOpt
+  searchOpt {.threadvar.}: SearchOpt
+  sortTime = false
+  sortTimeOrder = SortOrder.Ascending
   useWriteStyled = true
-  oneline = false
+  oneline = true  # turned off by --group
+  expandTabs = true  # Tabs are expanded in oneline mode
+  linesBefore = 0
+  linesAfter = 0
+  linesContext = 0
+  newLine = false
+  gVar = (matches: 0, errors: 0, reallyReplace: true)
+    # gVar - variables that can change during search/replace
+  nWorkers = 0  # run in single thread by default
+  searchRequestsChan: Channel[Trequest]
+  resultsChan: Channel[Tresult]
+  colorTheme: string = "simple"
+  limitCharUsr = high(int)  # don't limit line width by default
+  termWidth = 80
+  optOnlyAscii = false
+
+searchOpt.checkBin = biOn
 
 proc ask(msg: string): string =
   stdout.write(msg)
@@ -76,137 +174,577 @@ proc confirm: TConfirmEnum =
     of "e", "none": return ceNone
     else: discard
 
-proc countLines(s: string, first, last: int): int =
+func countLineBreaks(s: string, first, last: int): int =
+  # count line breaks (unlike strutils.countLines starts count from 0)
   var i = first
   while i <= last:
-    if s[i] == '\13':
+    if s[i] == '\c':
       inc result
-      if i < last and s[i+1] == '\10': inc(i)
-    elif s[i] == '\10':
+      if i < last and s[i+1] == '\l': inc(i)
+    elif s[i] == '\l':
       inc result
     inc i
 
-proc beforePattern(s: string, first: int): int =
-  result = first-1
-  while result >= 0:
-    if s[result] in NewLines: break
-    dec(result)
+func beforePattern(s: string, pos: int, nLines = 1): int =
+  var linesLeft = nLines
+  result = min(pos, s.len-1)
+  while true:
+    while result >= 0 and s[result] notin {'\c', '\l'}: dec(result)
+    if result == -1: break
+    if s[result] == '\l':
+      dec(linesLeft)
+      if linesLeft == 0: break
+      dec(result)
+      if result >= 0 and s[result] == '\c': dec(result)
+    else: # '\c'
+      dec(linesLeft)
+      if linesLeft == 0: break
+      dec(result)
   inc(result)
 
-proc afterPattern(s: string, last: int): int =
-  result = last+1
-  while result < s.len:
-    if s[result] in NewLines: break
-    inc(result)
+proc afterPattern(s: string, pos: int, nLines = 1): int =
+  result = max(0, pos)
+  var linesScanned = 0
+  while true:
+    while result < s.len and s[result] notin {'\c', '\l'}: inc(result)
+    inc(linesScanned)
+    if linesScanned == nLines: break
+    if result < s.len:
+      if s[result] == '\l':
+        inc(result)
+      elif s[result] == '\c':
+        inc(result)
+        if result < s.len and s[result] == '\l': inc(result)
+    else: break
   dec(result)
 
-proc writeColored(s: string) =
+template whenColors(body: untyped) =
   if useWriteStyled:
-    terminal.writeStyled(s, {styleUnderscore, styleBright})
+    body
   else:
     stdout.write(s)
 
-proc highlight(s, match, repl: string, t: tuple[first, last: int],
-               filename:string, line: int, showRepl: bool) =
-  const alignment = 6
-  if oneline:
-    stdout.write(filename, ":", line, ": ")
-  else:
-    stdout.write(line.`$`.align(alignment), ": ")
-  var x = beforePattern(s, t.first)
-  var y = afterPattern(s, t.last)
-  for i in x .. t.first-1: stdout.write(s[i])
-  writeColored(match)
-  for i in t.last+1 .. y: stdout.write(s[i])
-  stdout.write("\n")
+proc printFile(s: string) =
+  whenColors:
+    case colorTheme
+    of "simple": stdout.write(s)
+    of "bnw": stdout.styledWrite(styleUnderscore, s)
+    of "ack": stdout.styledWrite(fgGreen, s)
+    of "gnu": stdout.styledWrite(fgMagenta, s)
+
+proc printBlockFile(s: string) =
+  whenColors:
+    case colorTheme
+    of "simple": stdout.styledWrite(styleBright, s)
+    of "bnw": stdout.styledWrite(styleUnderscore, s)
+    of "ack": stdout.styledWrite(styleUnderscore, fgGreen, s)
+    of "gnu": stdout.styledWrite(styleUnderscore, fgMagenta, s)
+
+proc printBold(s: string) =
+  whenColors:
+    stdout.styledWrite(styleBright, s)
+
+proc printSpecial(s: string) =
+  whenColors:
+    case colorTheme
+    of "simple", "bnw":
+      stdout.styledWrite(if s == " ": styleReverse else: styleBright, s)
+    of "ack", "gnu": stdout.styledWrite(styleReverse, fgBlue, bgDefault, s)
+
+proc printError(s: string) =
+  whenColors:
+    case colorTheme
+    of "simple", "bnw": stdout.styledWriteLine(styleBright, s)
+    of "ack", "gnu": stdout.styledWriteLine(styleReverse, fgRed, bgDefault, s)
   stdout.flushFile()
-  if showRepl:
-    stdout.write(spaces(alignment-1), "-> ")
-    for i in x .. t.first-1: stdout.write(s[i])
-    writeColored(repl)
-    for i in t.last+1 .. y: stdout.write(s[i])
+
+proc printLineN(s: string, isMatch: bool) =
+  whenColors:
+    case colorTheme
+    of "simple": stdout.write(s)
+    of "bnw":
+      if isMatch: stdout.styledWrite(styleBright, s)
+      else: stdout.styledWrite(s)
+    of "ack":
+      if isMatch: stdout.styledWrite(fgYellow, s)
+      else: stdout.styledWrite(fgGreen, s)
+    of "gnu":
+      if isMatch: stdout.styledWrite(fgGreen, s)
+      else: stdout.styledWrite(fgCyan, s)
+
+proc printBlockLineN(s: string) =
+  whenColors:
+    case colorTheme
+    of "simple": stdout.styledWrite(styleBright, s)
+    of "bnw": stdout.styledWrite(styleUnderscore, styleBright, s)
+    of "ack": stdout.styledWrite(styleUnderscore, fgYellow, s)
+    of "gnu": stdout.styledWrite(styleUnderscore, fgGreen, s)
+
+proc writeColored(s: string) =
+  whenColors:
+    case colorTheme
+    of "simple": terminal.writeStyled(s, {styleUnderscore, styleBright})
+    of "bnw": stdout.styledWrite(styleReverse, s)
+    # Try styleReverse & bgDefault as a work-around against nasty feature
+    # "Background color erase" (sticky background after line wraps):
+    of "ack": stdout.styledWrite(styleReverse, fgYellow, bgDefault, s)
+    of "gnu": stdout.styledWrite(fgRed, s)
+
+proc printContents(s: string, isMatch: bool) =
+  if isMatch:
+    writeColored(s)
+  else:
+    stdout.write(s)
+
+proc writeArrow(s: string) =
+  whenColors:
+    stdout.styledWrite(styleReverse, s)
+
+const alignment = 6  # selected so that file contents start at 8, i.e.
+                     # Tabs expand correctly without additional care
+
+proc blockHeader(filename: string, line: int|string, replMode=false) =
+  if replMode:
+    writeArrow("     ->\n")
+  elif newLine and optFilenames notin options and optPipe notin options:
+    if oneline:
+      printBlockFile(filename)
+      printBlockLineN(":" & $line & ":")
+    else:
+      printBlockLineN($line.`$`.align(alignment) & ":")
     stdout.write("\n")
-    stdout.flushFile()
 
-proc processFile(pattern; filename: string; counter: var int) =
-  var filenameShown = false
-  template beforeHighlight =
-    if not filenameShown and optVerbose notin options and not oneline:
-      stdout.writeLine(filename)
-      stdout.flushFile()
-      filenameShown = true
+proc newLn(curCol: var Column) =
+  stdout.write("\n")
+  curCol.file = 0
+  curCol.terminal = 0
 
+# We reserve 10+3 chars on the right in --cols mode (optLimitChars).
+# If the current match touches this right margin, subLine before it will
+# be cropped (even if space is enough for subLine after the match — we
+# currently don't have a way to know it since we get it afterwards).
+const matchPaddingFromRight = 10
+const ellipsis = "..."
+
+proc lineHeader(filename: string, line: int|string, isMatch: bool,
+                curCol: var Column) =
+  let lineSym =
+    if isMatch: $line & ":"
+    else: $line & " "
+  if not newLine and optFilenames notin options and optPipe notin options:
+    if oneline:
+      printFile(filename)
+      printLineN(":" & lineSym, isMatch)
+      curcol.terminal += filename.len + 1 + lineSym.len
+    else:
+      printLineN(lineSym.align(alignment+1), isMatch)
+      curcol.terminal += lineSym.align(alignment+1).len
+    stdout.write(" "); curCol.terminal += 1
+    curCol.terminal = curCol.terminal mod termWidth
+    if optLimitChars in options and
+        curCol.terminal > limitCharUsr - matchPaddingFromRight - ellipsis.len:
+      newLn(curCol)
+
+proc reserveChars(mi: MatchInfo): int =
+  if optLimitChars in options:
+    let patternChars = afterPattern(mi.match, 0) + 1
+    result = patternChars + ellipsis.len + matchPaddingFromRight
+  else:
+    result = 0
+
+# Our substitutions of non-printable symbol to ASCII character are similar to
+# those of programm 'less'.
+const lowestAscii  = 0x20  # lowest ASCII Latin printable symbol (@)
+const largestAscii = 0x7e
+const by2ascii = 2  # number of ASCII chars to represent chars < lowestAscii
+const by3ascii = 3  # number of ASCII chars to represent chars > largestAscii
+
+proc printExpanded(s: string, curCol: var Column, isMatch: bool,
+                   limitChar: int) =
+  # Print taking into account tabs and optOnlyAscii (and also optLimitChar:
+  # the proc called from printCropped but we need to check column < limitChar
+  # also here, since exact cut points are known only after tab expansion).
+  # With optOnlyAscii non-ascii chars are highlighted even in matches.
+  #
+  # use buffer because:
+  # 1) we need to print non-ascii character inside matches while keeping the
+  #    amount of color escape sequences minimal.
+  # 2) there is a report that fwrite buffering is slow on MacOS
+  #    https://github.com/nim-lang/Nim/pull/15612#discussion_r510538326
+  const bufSize = 8192  # typical for fwrite too
   var buffer: string
-  if optFilenames in options:
-    buffer = filename
+  const normal = 0
+  const special = 1
+  var lastAdded = normal
+  template dumpBuf() =
+    if lastAdded == normal:
+      printContents(buffer, isMatch)
+    else:
+      printSpecial(buffer)
+  template addBuf(i: int, s: char|string, size: int) =
+    if lastAdded != i or buffer.len + size > bufSize:
+      dumpBuf()
+      buffer.setlen(0)
+    buffer.add s
+    lastAdded = i
+  for c in s:
+    let charsAllowed = limitChar - curCol.terminal
+    if charsAllowed <= 0:
+      break
+    if lowestAscii <= int(c) and int(c) <= largestAscii:  # ASCII latin
+      addBuf(normal, c, 1)
+      curCol.file += 1; curCol.terminal += 1
+    elif (not optOnlyAscii) and c != '\t':  # the same, print raw
+      addBuf(normal, c, 1)
+      curCol.file += 1; curCol.terminal += 1
+    elif c == '\t':
+      let spaces = 8 - (curCol.file mod 8)
+      let spacesAllowed = min(spaces, charsAllowed)
+      curCol.file += spaces
+      curCol.terminal += spacesAllowed
+      if expandTabs:
+        if optOnlyAscii:  # print a nice box for tab
+          addBuf(special, " ", 1)
+          addBuf(normal, " ".repeat(spacesAllowed-1), spacesAllowed-1)
+        else:
+          addBuf(normal, " ".repeat(spacesAllowed), spacesAllowed)
+      else:
+        addBuf(normal, '\t', 1)
+    else:  # substitute characters that are not ACSII Latin
+      if int(c) < lowestAscii:
+        let substitute = char(int(c) + 0x40)  # use common "control codes"
+        addBuf(special, "^" & substitute, by2ascii)
+        curCol.terminal += by2ascii
+      else:  # int(c) > largestAscii
+        curCol.terminal += by3ascii
+        let substitute = '\'' & c.BiggestUInt.toHex(2)
+        addBuf(special, substitute, by3ascii)
+      curCol.file += 1
+  if buffer.len > 0:
+    dumpBuf()
+
+template nextCharacter(c: char, file: var int, term: var int) =
+  if lowestAscii <= int(c) and int(c) <= largestAscii:  # ASCII latin
+    file += 1
+    term += 1
+  elif (not optOnlyAscii) and c != '\t':  # the same, print raw
+    file += 1
+    term += 1
+  elif c == '\t':
+    term += 8 - (file mod 8)
+    file += 8 - (file mod 8)
+  elif int(c) < lowestAscii:
+    file += 1
+    term += by2ascii
+  else:  # int(c) > largestAscii:
+    file += 1
+    term += by3ascii
+
+proc calcTermLen(s: string, firstCol: int, chars: int, fromLeft: bool): int =
+  # calculate additional length added by Tabs expansion and substitutions
+  var col = firstCol
+  var first, last: int
+  if fromLeft:
+    first = max(0, s.len - chars)
+    last = s.len - 1
   else:
-    try:
-      buffer = system.readFile(filename)
-    except IOError:
-      echo "cannot open file: ", filename
+    first = 0
+    last = min(s.len - 1, chars - 1)
+  for c in s[first .. last]:
+    nextCharacter(c, col, result)
+
+proc printCropped(s: string, curCol: var Column, fromLeft: bool,
+                  limitChar: int, isMatch = false) =
+  # print line `s`, may be cropped if option --cols was set
+  const eL = ellipsis.len
+  if optLimitChars notin options:
+    if not expandTabs and not optOnlyAscii:  # for speed mostly
+      printContents(s, isMatch)
+    else:
+      printExpanded(s, curCol, isMatch, limitChar)
+  else:  # limit columns, expand Tabs is also forced
+    var charsAllowed = limitChar - curCol.terminal
+    if fromLeft and charsAllowed < eL:
+      charsAllowed = eL
+    if (not fromLeft) and charsAllowed <= 0:
+      # already overflown and ellipsis shold be in place
       return
-  if optVerbose in options:
-    stdout.writeLine(filename)
+    let fullLenWithin = calcTermLen(s, curCol.file, charsAllowed, fromLeft)
+    # additional length from Tabs and special symbols
+    let addLen = fullLenWithin - min(s.len, charsAllowed)
+    # determine that the string is guaranteed to fit within `charsAllowed`
+    let fits =
+      if s.len > charsAllowed:
+        false
+      else:
+        if isMatch: fullLenWithin <= charsAllowed - eL
+        else: fullLenWithin <= charsAllowed
+    if fits:
+      printExpanded(s, curCol, isMatch, limitChar = high(int))
+    else:
+      if fromLeft:
+        printBold ellipsis
+        curCol.terminal += eL
+        # find position `pos` where the right side of line will fit charsAllowed
+        var col = 0
+        var term = 0
+        var pos = min(s.len, max(0, s.len - (charsAllowed - eL)))
+        while pos <= s.len - 1:
+          let c = s[pos]
+          nextCharacter(c, col, term)
+          if term >= addLen:
+            break
+          inc pos
+        curCol.file = pos
+        # TODO don't expand tabs when cropped from the left - difficult, meaningless
+        printExpanded(s[pos .. s.len - 1], curCol, isMatch,
+                      limitChar = high(int))
+      else:
+        let last = max(-1, min(s.len - 1, charsAllowed - eL - 1))
+        printExpanded(s[0 .. last], curCol, isMatch, limitChar-eL)
+        let numDots = limitChar - curCol.terminal
+        printBold ".".repeat(numDots)
+        curCol.terminal = limitChar
+
+proc printMatch(fileName: string, mi: MatchInfo, curCol: var Column) =
+  let sLines = mi.match.splitLines()
+  for i, l in sLines:
+    if i > 0:
+      lineHeader(filename, mi.lineBeg + i, isMatch = true, curCol)
+    let charsAllowed = limitCharUsr - curCol.terminal
+    if charsAllowed > 0:
+      printCropped(l, curCol, fromLeft = false, limitCharUsr, isMatch = true)
+    else:
+      curCol.overflowMatches += 1
+    if i < sLines.len - 1:
+      newLn(curCol)
+
+proc getSubLinesBefore(buf: string, curMi: MatchInfo): string =
+  let first = beforePattern(buf, curMi.first-1, linesBefore+1)
+  result = substr(buf, first, curMi.first-1)
+
+proc printSubLinesBefore(filename: string, beforeMatch: string, lineBeg: int,
+                         curCol: var Column, reserveChars: int,
+                         replMode=false) =
+  # start block: print 'linesBefore' lines before current match `curMi`
+  let sLines = splitLines(beforeMatch)
+  let startLine = lineBeg - sLines.len + 1
+  blockHeader(filename, lineBeg, replMode=replMode)
+  for i, l in sLines:
+    let isLastLine = i == sLines.len - 1
+    lineHeader(filename, startLine + i, isMatch = isLastLine, curCol)
+    let limit = if isLastLine: limitCharUsr - reserveChars else: limitCharUsr
+    l.printCropped(curCol, fromLeft = isLastLine, limitChar = limit)
+    if not isLastLine:
+      newLn(curCol)
+
+proc getSubLinesAfter(buf: string, mi: MatchInfo): string =
+  let last = afterPattern(buf, mi.last+1, 1+linesAfter)
+  let skipByte =  # workaround posix: suppress extra line at the end of file
+    if (last == buf.len-1 and buf.len >= 2 and
+        buf[^1] == '\l' and buf[^2] != '\c'): 1
+    else: 0
+  result = substr(buf, mi.last+1, last - skipByte)
+
+proc printOverflow(filename: string, line: int, curCol: var Column) =
+  if curCol.overflowMatches > 0:
+    lineHeader(filename, line, isMatch = true, curCol)
+    printBold("(" & $curCol.overflowMatches & " matches skipped)")
+    newLn(curCol)
+    curCol.overflowMatches = 0
+
+proc printSubLinesAfter(filename: string, afterMatch: string, matchLineEnd: int,
+                        curCol: var Column) =
+  # finish block: print 'linesAfter' lines after match `mi`
+  let sLines = splitLines(afterMatch)
+  if sLines.len == 0: # EOF
+    newLn(curCol)
+  else:
+    sLines[0].printCropped(curCol, fromLeft = false, limitCharUsr)
+      # complete the line after the match itself
+    newLn(curCol)
+    printOverflow(filename, matchLineEnd, curCol)
+    for i in 1 ..< sLines.len:
+      lineHeader(filename, matchLineEnd + i, isMatch = false, curCol)
+      sLines[i].printCropped(curCol, fromLeft = false, limitCharUsr)
+      newLn(curCol)
+
+proc getSubLinesBetween(buf: string, prevMi: MatchInfo,
+                        curMi: MatchInfo): string =
+  buf.substr(prevMi.last+1, curMi.first-1)
+
+proc printBetweenMatches(filename: string, betweenMatches: string,
+                         lastLineBeg: int,
+                         curCol: var Column, reserveChars: int) =
+  # continue block: print between `prevMi` and `curMi`
+  let sLines = betweenMatches.splitLines()
+  sLines[0].printCropped(curCol, fromLeft = false, limitCharUsr)
+    # finish the line of previous Match
+  if sLines.len > 1:
+    newLn(curCol)
+    printOverflow(filename, lastLineBeg - sLines.len + 1, curCol)
+    for i in 1 ..< sLines.len:
+      let isLastLine = i == sLines.len - 1
+      lineHeader(filename, lastLineBeg - sLines.len + i + 1,
+                 isMatch = isLastLine, curCol)
+      let limit = if isLastLine: limitCharUsr - reserveChars else: limitCharUsr
+      sLines[i].printCropped(curCol, fromLeft = isLastLine, limitChar = limit)
+      if not isLastLine:
+        newLn(curCol)
+
+proc printReplacement(fileName: string, buf: string, mi: MatchInfo,
+                      repl: string, showRepl: bool, curPos: int,
+                      newBuf: string, curLine: int) =
+  var curCol: Column
+  printSubLinesBefore(fileName, getSubLinesBefore(buf, mi), mi.lineBeg,
+                      curCol, reserveChars(mi))
+  printMatch(fileName, mi, curCol)
+  printSubLinesAfter(fileName, getSubLinesAfter(buf, mi), mi.lineEnd, curCol)
+  stdout.flushFile()
+  if showRepl:
+    let miForNewBuf: MatchInfo =
+      (first: newBuf.len, last: newBuf.len,
+       lineBeg: curLine, lineEnd: curLine, match: "")
+    printSubLinesBefore(fileName, getSubLinesBefore(newBuf, miForNewBuf),
+                        miForNewBuf.lineBeg, curCol, reserveChars(miForNewBuf),
+                        replMode=true)
+
+    let replLines = countLineBreaks(repl, 0, repl.len-1)
+    let miFixLines: MatchInfo =
+      (first: mi.first, last: mi.last,
+       lineBeg: curLine, lineEnd: curLine + replLines, match: repl)
+    printMatch(fileName, miFixLines, curCol)
+    printSubLinesAfter(fileName, getSubLinesAfter(buf, miFixLines),
+                       miFixLines.lineEnd, curCol)
+    if linesAfter + linesBefore >= 2 and not newLine: stdout.write("\n")
     stdout.flushFile()
-  var result: string
 
-  if optReplace in options:
-    result = newStringOfCap(buffer.len)
+proc replace1match(filename: string, buf: string, mi: MatchInfo, i: int,
+                   r: string; newBuf: var string, curLine: var int): bool =
+  newBuf.add(buf.substr(i, mi.first-1))
+  inc(curLine, countLineBreaks(buf, i, mi.first-1))
+  if optConfirm in options:
+    printReplacement(filename, buf, mi, r, showRepl=true, i, newBuf, curLine)
+    case confirm()
+    of ceAbort: quit(0)
+    of ceYes: gVar.reallyReplace = true
+    of ceAll:
+      gVar.reallyReplace = true
+      options.excl(optConfirm)
+    of ceNo:
+      gVar.reallyReplace = false
+    of ceNone:
+      gVar.reallyReplace = false
+      options.excl(optConfirm)
+  elif optPipe notin options:
+    printReplacement(filename, buf, mi, r, showRepl=gVar.reallyReplace, i,
+                     newBuf, curLine)
+  if gVar.reallyReplace:
+    result = true
+    newBuf.add(r)
+    inc(curLine, countLineBreaks(r, 0, r.len-1))
+  else:
+    newBuf.add(mi.match)
+    inc(curLine, countLineBreaks(mi.match, 0, mi.match.len-1))
+
+template updateCounters(output: Output) =
+  case output.kind
+  of blockFirstMatch, blockNextMatch: inc(gVar.matches)
+  of justCount: inc(gVar.matches, output.matches)
+  of openError: inc(gVar.errors)
+  of rejected, blockEnd, fileContents, outputFileName: discard
+
+proc printInfo(filename:string, output: Output) =
+  case output.kind
+  of openError:
+    printError("cannot open path '" & filename & "': " & output.msg)
+  of rejected:
+    if optVerbose in options:
+      echo "(rejected: ", output.reason, ")"
+  of justCount:
+    echo " (" & $output.matches & " matches)"
+  of blockFirstMatch, blockNextMatch, blockEnd, fileContents, outputFileName:
+    discard
+
+proc printOutput(filename: string, output: Output, curCol: var Column) =
+  case output.kind
+  of openError, rejected, justCount: printInfo(filename, output)
+  of fileContents: discard # impossible
+  of outputFileName:
+    printCropped(output.name, curCol, fromLeft=false, limitCharUsr)
+    newLn(curCol)
+  of blockFirstMatch:
+    printSubLinesBefore(filename, output.pre, output.match.lineBeg,
+                        curCol, reserveChars(output.match))
+    printMatch(filename, output.match, curCol)
+  of blockNextMatch:
+    printBetweenMatches(filename, output.pre, output.match.lineBeg,
+                        curCol, reserveChars(output.match))
+    printMatch(filename, output.match, curCol)
+  of blockEnd:
+    printSubLinesAfter(filename, output.blockEnding, output.firstLine, curCol)
+    if linesAfter + linesBefore >= 2 and not newLine and
+       optFilenames notin options: stdout.write("\n")
 
-  var line = 1
+iterator searchFile(pattern: Pattern; buffer: string): Output =
+  var prevMi, curMi: MatchInfo
+  prevMi.lineEnd = 1
   var i = 0
   var matches: array[0..re.MaxSubpatterns-1, string]
   for j in 0..high(matches): matches[j] = ""
-  var reallyReplace = true
-  while i < buffer.len:
+  while true:
     let t = findBounds(buffer, pattern, matches, i)
-    if t.first < 0: break
-    inc(line, countLines(buffer, i, t.first-1))
-
-    var wholeMatch = buffer.substr(t.first, t.last)
+    if t.first < 0 or t.last < t.first:
+      if prevMi.lineBeg != 0: # finalize last match
+        yield Output(kind: blockEnd,
+                     blockEnding: getSubLinesAfter(buffer, prevMi),
+                     firstLine: prevMi.lineEnd)
+      break
 
-    beforeHighlight()
-    inc counter
-    if optReplace notin options:
-      highlight(buffer, wholeMatch, "", t, filename, line, showRepl=false)
+    let lineBeg = prevMi.lineEnd + countLineBreaks(buffer, i, t.first-1)
+    curMi = (first: t.first,
+             last: t.last,
+             lineBeg: lineBeg,
+             lineEnd: lineBeg + countLineBreaks(buffer, t.first, t.last),
+             match: buffer.substr(t.first, t.last))
+    if prevMi.lineBeg == 0: # no prev. match, so no prev. block to finalize
+      let pre = getSubLinesBefore(buffer, curMi)
+      prevMi = curMi
+      yield Output(kind: blockFirstMatch, pre: pre, match: move(curMi))
     else:
-      let r = replace(wholeMatch, pattern, replacement % matches)
-      if optConfirm in options:
-        highlight(buffer, wholeMatch, r, t, filename, line, showRepl=true)
-        case confirm()
-        of ceAbort: quit(0)
-        of ceYes: reallyReplace = true
-        of ceAll:
-          reallyReplace = true
-          options.excl(optConfirm)
-        of ceNo:
-          reallyReplace = false
-        of ceNone:
-          reallyReplace = false
-          options.excl(optConfirm)
-      else:
-        highlight(buffer, wholeMatch, r, t, filename, line, showRepl=reallyReplace)
-      if reallyReplace:
-        result.add(buffer.substr(i, t.first-1))
-        result.add(r)
-      else:
-        result.add(buffer.substr(i, t.last))
-
-    inc(line, countLines(buffer, t.first, t.last))
+      let nLinesBetween = curMi.lineBeg - prevMi.lineEnd
+      if nLinesBetween <= linesAfter + linesBefore + 1: # print as 1 block
+        let pre =  getSubLinesBetween(buffer, prevMi, curMi)
+        prevMi = curMi
+        yield Output(kind: blockNextMatch, pre: pre, match: move(curMi))
+      else: # finalize previous block and then print next block
+        let after = getSubLinesAfter(buffer, prevMi)
+        yield Output(kind: blockEnd, blockEnding: after,
+                     firstLine: prevMi.lineEnd)
+        let pre = getSubLinesBefore(buffer, curMi)
+        prevMi = curMi
+        yield Output(kind: blockFirstMatch,
+                     pre: pre,
+                     match: move(curMi))
     i = t.last+1
-  if optReplace in options:
-    result.add(substr(buffer, i))
-    var f: File
-    if open(f, filename, fmWrite):
-      f.write(result)
-      f.close()
-    else:
-      quit "cannot open file for overwriting: " & filename
+  when typeof(pattern) is Regex:
+    if buffer.len > MaxReBufSize:
+      yield Output(kind: openError, msg: "PCRE size limit is " & $MaxReBufSize)
 
-proc hasRightExt(filename: string, exts: seq[string]): bool =
-  var y = splitFile(filename).ext.substr(1) # skip leading '.'
-  for x in items(exts):
-    if os.cmpPaths(x, y) == 0: return true
+func detectBin(buffer: string): bool =
+  for i in 0 ..< min(1024, buffer.len):
+    if buffer[i] == '\0':
+      return true
+
+proc compilePeg(initPattern: string): Peg =
+  var pattern = initPattern
+  if optWord in options:
+    pattern = r"(^ / !\letter)(" & pattern & r") !\letter"
+  if optIgnoreStyle in options:
+    pattern = "\\y " & pattern
+  elif optIgnoreCase in options:
+    pattern = "\\i " & pattern
+  result = peg(pattern)
 
 proc styleInsensitive(s: string): string =
   template addx =
@@ -242,17 +780,442 @@ proc styleInsensitive(s: string): string =
         addx()
     else: addx()
 
-proc walker(pattern; dir: string; counter: var int) =
-  for kind, path in walkDir(dir):
-    case kind
-    of pcFile:
-      if extensions.len == 0 or path.hasRightExt(extensions):
-        processFile(pattern, path, counter)
-    of pcDir:
-      if optRecursive in options:
-        walker(pattern, path, counter)
-    else: discard
-  if existsFile(dir): processFile(pattern, dir, counter)
+proc compileRegex(initPattern: string): Regex =
+  var pattern = initPattern
+  var reflags = {reStudy}
+  if optIgnoreStyle in options:
+    pattern = styleInsensitive(pattern)
+  if optWord in options:
+    # see https://github.com/nim-lang/Nim/issues/13528#issuecomment-592786443
+    pattern = r"(^|\W)(:?" & pattern & r")($|\W)"
+  if {optIgnoreCase, optIgnoreStyle} * options != {}:
+    reflags.incl reIgnoreCase
+  result = if optRex in options: rex(pattern, reflags)
+           else: re(pattern, reflags)
+
+template declareCompiledPatterns(compiledStruct: untyped,
+                                 StructType: untyped,
+                                 body: untyped) =
+  {.hint[XDeclaredButNotUsed]: off.}
+  if optRegex notin options:
+    var compiledStruct: StructType[Peg]
+    template compile1Pattern(p: string, pat: Peg) =
+      if p!="": pat = p.compilePeg()
+    proc compileArray(initPattern: seq[string]): seq[Peg] =
+      for pat in initPattern:
+        result.add pat.compilePeg()
+    body
+  else:
+    var compiledStruct: StructType[Regex]
+    template compile1Pattern(p: string, pat: Regex) =
+      if p!="": pat = p.compileRegex()
+    proc compileArray(initPattern: seq[string]): seq[Regex] =
+      for pat in initPattern:
+        result.add pat.compileRegex()
+    body
+  {.hint[XDeclaredButNotUsed]: on.}
+
+template ensureIncluded(includePat: seq[Pattern], str: string,
+                                body: untyped) =
+  if includePat.len != 0:
+    var matched = false
+    for pat in includePat:
+      if str.contains(pat):
+        matched = true
+        break
+    if not matched:
+      body
+
+template ensureExcluded(excludePat: seq[Pattern], str: string,
+                                body: untyped) =
+  {.warning[UnreachableCode]: off.}
+  for pat in excludePat:
+    if str.contains(pat, 0):
+      body
+      break
+  {.warning[UnreachableCode]: on.}
+
+func checkContext(context: string, searchOptC: SearchOptComp[Pattern]): bool =
+  ensureIncluded searchOptC.inContext, context:
+    return false
+  ensureExcluded searchOptC.notInContext, context:
+    return false
+  result = true
+
+iterator processFile(searchOptC: SearchOptComp[Pattern], filename: string,
+                     yieldContents=false): Output =
+  var buffer: string
+
+  var error = false
+  if optFilenames in options:
+    buffer = filename
+  elif optPipe in options:
+    buffer = stdin.readAll()
+  else:
+    try:
+      buffer = system.readFile(filename)
+    except IOError as e:
+      yield Output(kind: openError, msg: "readFile failed")
+      error = true
+
+  if not error:
+    var reject = false
+    var reason: string
+    if searchOpt.checkBin in {biOff, biOnly}:
+      let isBin = detectBin(buffer)
+      if isBin and searchOpt.checkBin == biOff:
+        reject = true
+        reason = "binary file"
+      if (not isBin) and searchOpt.checkBin == biOnly:
+        reject = true
+        reason = "text file"
+
+    if not reject:
+      ensureIncluded searchOptC.inFile, buffer:
+        reject = true
+        reason = "doesn't contain a requested match"
+
+    if not reject:
+      ensureExcluded searchOptC.notInFile, buffer:
+        reject = true
+        reason = "contains a forbidden match"
+
+    if reject:
+      yield Output(kind: rejected, reason: move(reason))
+    elif optFilenames in options and searchOpt.pattern == "":
+      yield Output(kind: outputFileName, name: move(buffer))
+    else:
+      var found = false
+      var cnt = 0
+      let skipCheckContext = (searchOpt.notInContext.len == 0 and
+                              searchOpt.inContext.len == 0)
+      if skipCheckContext:
+        for output in searchFile(searchOptC.pattern, buffer):
+          found = true
+          if optCount notin options:
+            yield output
+          else:
+            if output.kind in {blockFirstMatch, blockNextMatch}:
+              inc(cnt)
+      else:
+        var context: string
+        var outputAccumulator: seq[Output]
+        for outp in searchFile(searchOptC.pattern, buffer):
+          if outp.kind in {blockFirstMatch, blockNextMatch}:
+            outputAccumulator.add outp
+            context.add outp.pre
+            context.add outp.match.match
+          elif outp.kind == blockEnd:
+            outputAccumulator.add outp
+            context.add outp.blockEnding
+            # context has been formed, now check it:
+            if checkContext(context, searchOptC):
+              found = true
+              for output in outputAccumulator:
+                if optCount notin options:
+                  yield output
+                else:
+                  if output.kind in {blockFirstMatch, blockNextMatch}:
+                    inc(cnt)
+            context = ""
+            outputAccumulator.setLen 0
+      # end `if skipCheckContext`.
+      if optCount in options and cnt > 0:
+        yield Output(kind: justCount, matches: cnt)
+      if yieldContents and found and optCount notin options:
+        yield Output(kind: fileContents, buffer: move(buffer))
+
+proc hasRightPath(path: string, walkOptC: WalkOptComp[Pattern]): bool =
+  if not (
+      walkOpt.extensions.len > 0 or walkOpt.notExtensions.len > 0 or
+      walkOpt.filename.len > 0 or walkOpt.notFilename.len > 0 or
+      walkOpt.notDirPath.len > 0 or walkOpt.dirPath.len > 0):
+    return true
+  let filename = path.lastPathPart
+  let ex = filename.splitFile.ext.substr(1) # skip leading '.'
+  if walkOpt.extensions.len != 0:
+    var matched = false
+    for x in walkOpt.extensions:
+      if os.cmpPaths(x, ex) == 0:
+        matched = true
+        break
+    if not matched: return false
+  for x in walkOpt.notExtensions:
+    if os.cmpPaths(x, ex) == 0: return false
+  ensureIncluded walkOptC.filename, filename:
+    return false
+  ensureExcluded walkOptC.notFilename, filename:
+    return false
+  let parent = path.parentDir
+  ensureExcluded walkOptC.notDirPath, parent:
+    return false
+  ensureIncluded walkOptC.dirPath, parent:
+    return false
+  result = true
+
+proc isRightDirectory(path: string, walkOptC: WalkOptComp[Pattern]): bool =
+  ## --dirname can be only checked when the final path is known
+  ## so this proc is suitable for files only.
+  if walkOptC.dirname.len > 0:
+    var badDirname = false
+    var (nextParent, dirname) = splitPath(path)
+    # check that --dirname matches for one of directories in parent path:
+    while dirname != "":
+      badDirname = false
+      ensureIncluded walkOptC.dirname, dirname:
+        badDirname = true
+      if not badDirname:
+        break
+      (nextParent, dirname) = splitPath(nextParent)
+    if badDirname:  # badDirname was set to true for all the dirs
+      return false
+  result = true
+
+proc descendToDirectory(path: string, walkOptC: WalkOptComp[Pattern]): bool =
+  ## --notdirname can be checked for directories immediately for optimization to
+  ## prevent descending into undesired directories.
+  if walkOptC.notDirname.len > 0:
+    let dirname = path.lastPathPart
+    ensureExcluded walkOptC.notDirname, dirname:
+      return false
+  result = true
+
+iterator walkDirBasic(dir: string, walkOptC: WalkOptComp[Pattern]): string
+         {.closure.} =
+  var dirStack = @[dir]  # stack of directories
+  var timeFiles = newSeq[(times.Time, string)]()
+  while dirStack.len > 0:
+    let d = dirStack.pop()
+    let rightDirForFiles = d.isRightDirectory(walkOptC)
+    var files = newSeq[string]()
+    var dirs = newSeq[string]()
+    for kind, path in walkDir(d, skipSpecial = true):
+      case kind
+      of pcFile:
+        if path.hasRightPath(walkOptC) and rightDirForFiles:
+          files.add(path)
+      of pcLinkToFile:
+        if optFollow in options and path.hasRightPath(walkOptC) and
+            rightDirForFiles:
+          files.add(path)
+      of pcDir:
+        if optRecursive in options and path.descendToDirectory(walkOptC):
+          dirs.add path
+      of pcLinkToDir:
+        if optFollow in options and optRecursive in options and
+            path.descendToDirectory(walkOptC):
+          dirs.add path
+    if sortTime:  # sort by time - collect files before yielding
+      for file in files:
+        var time: Time
+        try:
+          time = getLastModificationTime(file)  # can fail for broken symlink
+        except:
+          discard
+        timeFiles.add((time, file))
+    else:  # alphanumeric sort, yield immediately after sorting
+      files.sort()
+      for file in files:
+        yield file
+      dirs.sort(order = SortOrder.Descending)
+    for dir in dirs:
+      dirStack.add(dir)
+  if sortTime:
+    timeFiles.sort(sortTimeOrder)
+    for (_, file) in timeFiles:
+      yield file
+
+iterator walkRec(paths: seq[string]): tuple[error: string, filename: string]
+         {.closure.} =
+  declareCompiledPatterns(walkOptC, WalkOptComp):
+    walkOptC.notFilename.add walkOpt.notFilename.compileArray()
+    walkOptC.filename.add    walkOpt.filename.compileArray()
+    walkOptC.dirname.add     walkOpt.dirname.compileArray()
+    walkOptC.notDirname.add  walkOpt.notDirname.compileArray()
+    walkOptC.dirPath.add     walkOpt.dirPath.compileArray()
+    walkOptC.notDirPath.add  walkOpt.notDirPath.compileArray()
+    for path in paths:
+      if dirExists(path):
+        for p in walkDirBasic(path, walkOptC):
+          yield ("", p)
+      else:
+        yield (
+          if fileExists(path): ("", path)
+          else: ("Error: no such file or directory: ", path))
+
+proc replaceMatches(pattern: Pattern; filename: string, buffer: string,
+                    fileResult: FileResult) =
+  var newBuf = newStringOfCap(buffer.len)
+
+  var changed = false
+  var lineRepl = 1
+  var i = 0
+  for output in fileResult:
+    if output.kind in {blockFirstMatch, blockNextMatch}:
+      let curMi = output.match
+      let r = replacef(curMi.match, pattern, replacement)
+      if replace1match(filename, buffer, curMi, i, r, newBuf, lineRepl):
+        changed = true
+      i = curMi.last + 1
+  if changed and optPipe notin options:
+    newBuf.add(substr(buffer, i))  # finalize new buffer after last match
+    var f: File
+    if open(f, filename, fmWrite):
+      f.write(newBuf)
+      f.close()
+    else:
+      printError "cannot open file for overwriting: " & filename
+      inc(gVar.errors)
+  elif optPipe in options:  # always print new buffer to stdout in pipe mode
+    newBuf.add(substr(buffer, i))  # finalize new buffer after last match
+    stdout.write(newBuf)
+
+template processFileResult(pattern: Pattern; filename: string,
+                           fileResult: untyped) =
+  var filenameShown = false
+  template showFilename =
+    if not filenameShown:
+      printBlockFile(filename)
+      stdout.write("\n")
+      stdout.flushFile()
+      filenameShown = true
+  if optVerbose in options:
+    showFilename
+  if optReplace notin options:
+    var curCol: Column
+    var toFlush: bool
+    for output in fileResult:
+      updateCounters(output)
+      toFlush = true
+      if output.kind notin {rejected, openError, justCount} and not oneline:
+        showFilename
+      if output.kind == justCount and oneline:
+        printFile(filename & ":")
+      printOutput(filename, output, curCol)
+      if nWorkers == 0 and output.kind in {blockFirstMatch, blockNextMatch}:
+        stdout.flushFile()  # flush immediately in single thread mode
+    if toFlush: stdout.flushFile()
+  else:
+    var buffer = ""
+    var matches: FileResult
+    for output in fileResult:
+      updateCounters(output)
+      case output.kind
+      of rejected, openError, justCount, outputFileName:
+        printInfo(filename, output)
+      of blockFirstMatch, blockNextMatch, blockEnd:
+        matches.add(output)
+      of fileContents: buffer = output.buffer
+    if matches.len > 0:
+      replaceMatches(pattern, filename, buffer, matches)
+
+proc run1Thread() =
+  declareCompiledPatterns(searchOptC, SearchOptComp):
+    compile1Pattern(searchOpt.pattern, searchOptC.pattern)
+    searchOptC.inFile.add searchOpt.inFile.compileArray()
+    searchOptC.notInFile.add searchOpt.notInFile.compileArray()
+    searchOptC.inContext.add searchOpt.inContext.compileArray()
+    searchOptC.notInContext.add searchOpt.notInContext.compileArray()
+    if optPipe in options:
+      processFileResult(searchOptC.pattern, "-",
+                        processFile(searchOptC, "-",
+                                    yieldContents=optReplace in options))
+    for entry in walkRec(paths):
+      if entry.error != "":
+        inc(gVar.errors)
+        printError (entry.error & entry.filename)
+        continue
+      processFileResult(searchOptC.pattern, entry.filename,
+                        processFile(searchOptC, entry.filename,
+                                    yieldContents=optReplace in options))
+
+# Multi-threaded version: all printing is being done in the Main thread.
+# Totally nWorkers+1 additional threads are created (workers + pathProducer).
+# An example of case nWorkers=2:
+#
+#  ------------------  initial paths   -------------------
+#  |  Main thread   |----------------->|  pathProducer   |
+#  ------------------                  -------------------
+#             ^                          |        | 
+# resultsChan |       walking errors,    |        | searchRequestsChan
+#             |       number of files    |   -----+-----
+#         ----+---------------------------   |         |
+#         |   |   (when walking finished)    |a path   |a path to file
+#         |   |                              |         |
+#         |   |                              V         V 
+#         |   |                      ------------  ------------
+#         |   |                      | worker 1 |  | worker 2 |
+#         |   |                      ------------  ------------
+#         |   |  matches in the file         |         |
+#         |   --------------------------------         |
+#         |      matches in the file                   |
+#         ----------------------------------------------
+#
+# The matches from each file are passed at once as FileResult type.
+
+proc worker(initSearchOpt: SearchOpt) {.thread.} =
+  searchOpt = initSearchOpt  # init thread-local var
+  declareCompiledPatterns(searchOptC, SearchOptComp):
+    compile1Pattern(searchOpt.pattern, searchOptC.pattern)
+    searchOptC.inFile.add searchOpt.inFile.compileArray()
+    searchOptC.notInFile.add searchOpt.notInFile.compileArray()
+    searchOptC.inContext.add searchOpt.inContext.compileArray()
+    searchOptC.notInContext.add searchOpt.notInContext.compileArray()
+    while true:
+      let (fileNo, filename) = searchRequestsChan.recv()
+      var fileResult: FileResult
+      for output in processFile(searchOptC, filename,
+                                yieldContents=(optReplace in options)):
+        fileResult.add(output)
+      resultsChan.send((false, fileNo, filename, move(fileResult)))
+
+proc pathProducer(arg: (seq[string], WalkOpt)) {.thread.} =
+  let paths = arg[0]
+  walkOpt = arg[1]  # init thread-local copy of opt
+  var
+    nextFileN = 0
+  for entry in walkRec(paths):
+    if entry.error == "":
+      searchRequestsChan.send((nextFileN, entry.filename))
+    else:
+      resultsChan.send((false, nextFileN, entry.filename,
+                        @[Output(kind: openError, msg: entry.error)]))
+    nextFileN += 1
+  resultsChan.send((true, nextFileN, "", @[]))  # pass total number of files
+
+proc runMultiThread() =
+  var
+    workers = newSeq[Thread[SearchOpt]](nWorkers)
+    storage = newTable[int, (string, FileResult) ]()
+      # file number -> tuple[filename, fileResult - accumulated data structure]
+    firstUnprocessedFile = 0  # for always processing files in the same order
+  open(searchRequestsChan)
+  open(resultsChan)
+  for n in 0 ..< nWorkers:
+    createThread(workers[n], worker, searchOpt)
+  var producerThread: Thread[(seq[string], WalkOpt)]
+  createThread(producerThread, pathProducer, (paths, walkOpt))
+  declareCompiledPatterns(pat, SinglePattern):
+    compile1Pattern(searchOpt.pattern, pat.pattern)
+    template add1fileResult(fileNo: int, fname: string, fResult: FileResult) =
+      storage[fileNo] = (fname, fResult)
+      while storage.haskey(firstUnprocessedFile):
+        let fileResult = storage[firstUnprocessedFile][1]
+        let filename = storage[firstUnprocessedFile][0]
+        processFileResult(pat.pattern, filename, fileResult)
+        storage.del(firstUnprocessedFile)
+        firstUnprocessedFile += 1
+    var totalFiles = -1  # will be known when pathProducer finishes
+    while totalFiles == -1 or firstUnprocessedFile < totalFiles:
+      let msg = resultsChan.recv()
+      if msg.finished:
+        totalFiles = msg.fileNo
+      else:
+        add1fileResult(msg.fileNo, msg.filename, msg.fileResult)
+
+proc reportError(msg: string) =
+  printError "Error: " & msg
+  quit "Run nimgrep --help for the list of options"
 
 proc writeHelp() =
   stdout.write(Usage)
@@ -268,83 +1231,204 @@ proc checkOptions(subset: TOptions, a, b: string) =
   if subset <= options:
     quit("cannot specify both '$#' and '$#'" % [a, b])
 
+proc parseNonNegative(str: string, key: string): int =
+  try:
+    result = parseInt(str)
+  except ValueError:
+    reportError("Option " & key & " requires an integer but '" &
+                str & "' was given")
+  if result < 0:
+    reportError("A positive integer is expected for option " & key)
+
+when defined(posix):
+  useWriteStyled = terminal.isatty(stdout)
+  # that should be before option processing to allow override of useWriteStyled
+
 for kind, key, val in getopt():
   case kind
   of cmdArgument:
     if options.contains(optStdin):
-      filenames.add(key)
-    elif pattern.len == 0:
-      pattern = key
-    elif options.contains(optReplace) and replacement.len == 0:
+      paths.add(key)
+    elif not searchOpt.patternSet:
+      searchOpt.pattern = key
+      searchOpt.patternSet = true
+    elif options.contains(optReplace) and not replacementSet:
       replacement = key
+      replacementSet = true
     else:
-      filenames.add(key)
-  of cmdLongoption, cmdShortOption:
+      paths.add(key)
+  of cmdLongOption, cmdShortOption:
+    proc addNotEmpty(s: var seq[string], name: string) =
+      if name == "":
+        reportError("empty string given for option --" & key &
+                    " (did you forget `:`?)")
+      s.add name
     case normalize(key)
     of "find", "f": incl(options, optFind)
-    of "replace", "r": incl(options, optReplace)
+    of "replace", "!": incl(options, optReplace)
     of "peg":
       excl(options, optRegex)
       incl(options, optPeg)
     of "re":
       incl(options, optRegex)
       excl(options, optPeg)
-    of "recursive": incl(options, optRecursive)
+    of "rex", "x":
+      incl(options, optRex)
+      incl(options, optRegex)
+      excl(options, optPeg)
+    of "recursive", "r": incl(options, optRecursive)
+    of "follow": incl(options, optFollow)
     of "confirm": incl(options, optConfirm)
     of "stdin": incl(options, optStdin)
     of "word", "w": incl(options, optWord)
-    of "ignorecase", "i": incl(options, optIgnoreCase)
-    of "ignorestyle", "y": incl(options, optIgnoreStyle)
-    of "ext": extensions.add val.split('|')
-    of "nocolor": useWriteStyled = false
-    of "oneline": oneline = true
+    of "ignorecase", "ignore-case", "i": incl(options, optIgnoreCase)
+    of "ignorestyle", "ignore-style", "y": incl(options, optIgnoreStyle)
+    of "threads", "j":
+      if val == "":
+        nWorkers = countProcessors()
+      else:
+        nWorkers = parseNonNegative(val, key)
+    of "extensions", "ex", "ext": walkOpt.extensions.add val.split('|')
+    of "nextensions", "notextensions", "nex", "notex",
+       "noext", "no-ext":  # 2 deprecated options
+      walkOpt.notExtensions.add val.split('|')
+    of "dirname",  "di":
+      walkOpt.dirname.addNotEmpty val
+    of "ndirname", "notdirname", "ndi", "notdi",
+       "excludedir", "exclude-dir", "ed":  # 3 deprecated options
+      walkOpt.notDirname.addNotEmpty val
+    of "dirpath", "dirp",
+       "includedir", "include-dir", "id":  # 3 deprecated options
+      walkOpt.dirPath.addNotEmpty val
+    of "ndirpath", "notdirpath", "ndirp", "notdirp":
+      walkOpt.notDirPath.addNotEmpty val
+    of "filename", "fi",
+       "includefile", "include-file", "if":  # 3 deprecated options
+      walkOpt.filename.addNotEmpty val
+    of "nfilename", "nfi", "notfilename", "notfi",
+       "excludefile", "exclude-file", "ef":  # 3 deprecated options
+      walkOpt.notFilename.addNotEmpty val
+    of "infile", "inf",
+       "matchfile", "match", "mf":  # 3 deprecated options
+      searchOpt.inFile.addNotEmpty val
+    of "ninfile", "notinfile", "ninf", "notinf",
+       "nomatchfile", "nomatch", "nf":  # 3 options are deprecated
+      searchOpt.notInFile.addNotEmpty val
+    of "incontext", "inc":
+      searchOpt.inContext.addNotEmpty val
+    of "nincontext", "notincontext", "ninc", "notinc":
+      searchOpt.notInContext.addNotEmpty val
+    of "bin":
+      case val
+      of "on": searchOpt.checkBin = biOn
+      of "off": searchOpt.checkBin = biOff
+      of "only": searchOpt.checkBin = biOnly
+      else: reportError("unknown value for --bin")
+    of "text", "t": searchOpt.checkBin = biOff
+    of "count": incl(options, optCount)
+    of "sorttime", "sort-time", "s":
+      case normalize(val)
+      of "off": sortTime = false
+      of "", "on", "asc", "ascending":
+        sortTime = true
+        sortTimeOrder = SortOrder.Ascending
+      of "desc", "descending":
+        sortTime = true
+        sortTimeOrder = SortOrder.Descending
+      else: reportError("invalid value '" & val & "' for --sortTime")
+    of "nocolor", "no-color": useWriteStyled = false
+    of "color":
+      case val
+      of "auto": discard
+      of "off", "never", "false": useWriteStyled = false
+      of "", "on", "always", "true": useWriteStyled = true
+      else: reportError("invalid value '" & val & "' for --color")
+    of "colortheme", "color-theme":
+      colortheme = normalize(val)
+      if colortheme notin ["simple", "bnw", "ack", "gnu"]:
+        reportError("unknown colortheme '" & val & "'")
+    of "beforecontext", "before-context", "b":
+      linesBefore = parseNonNegative(val, key)
+    of "aftercontext", "after-context", "a":
+      linesAfter = parseNonNegative(val, key)
+    of "context", "c":
+      linesContext = parseNonNegative(val, key)
+    of "newline", "l":
+      newLine = true
+      # Tabs are aligned automatically for --group, --newLine, --filenames
+      expandTabs = false
+    of "group", "g":
+      oneline = false
+      expandTabs = false
+    of "cols", "%":
+      incl(options, optLimitChars)
+      termWidth = terminalWidth()
+      if val == "auto" or key == "%":
+        limitCharUsr = termWidth
+        when defined(windows):  # Windows cmd & powershell add an empty line
+          limitCharUsr -= 1     # when printing '\n' right after the last column
+      elif val == "":
+        limitCharUsr = 80
+      else:
+        limitCharUsr = parseNonNegative(val, key)
+    of "onlyascii", "only-ascii", "@":
+      if val == "" or val == "on" or key == "@":
+        optOnlyAscii = true
+      elif val == "off":
+        optOnlyAscii = false
+      else:
+        printError("unknown value for --onlyAscii option")
     of "verbose": incl(options, optVerbose)
-    of "filenames": incl(options, optFilenames)
+    of "filenames":
+      incl(options, optFilenames)
+      expandTabs = false
     of "help", "h": writeHelp()
     of "version", "v": writeVersion()
-    else: writeHelp()
+    of "": incl(options, optPipe)
+    else: reportError("unrecognized option '" & key & "'")
   of cmdEnd: assert(false) # cannot happen
 
-when defined(posix):
-  useWriteStyled = terminal.isatty(stdout)
-
 checkOptions({optFind, optReplace}, "find", "replace")
+checkOptions({optCount, optReplace}, "count", "replace")
 checkOptions({optPeg, optRegex}, "peg", "re")
 checkOptions({optIgnoreCase, optIgnoreStyle}, "ignore_case", "ignore_style")
 checkOptions({optFilenames, optReplace}, "filenames", "replace")
+checkOptions({optPipe, optStdin}, "-", "stdin")
+checkOptions({optPipe, optFilenames}, "-", "filenames")
+checkOptions({optPipe, optConfirm}, "-", "confirm")
+checkOptions({optPipe, optRecursive}, "-", "recursive")
+
+linesBefore = max(linesBefore, linesContext)
+linesAfter  = max(linesAfter,  linesContext)
+
+if optPipe in options and paths.len != 0:
+  reportError("both - and paths are specified")
 
 if optStdin in options:
-  pattern = ask("pattern [ENTER to exit]: ")
-  if isNil(pattern) or pattern.len == 0: quit(0)
+  searchOpt.pattern = ask("pattern [ENTER to exit]: ")
+  if searchOpt.pattern.len == 0: quit(0)
   if optReplace in options:
     replacement = ask("replacement [supports $1, $# notations]: ")
 
-if pattern.len == 0:
-  writeHelp()
+if optReplace in options and not replacementSet:
+  reportError("provide REPLACEMENT as second argument (use \"\" for empty one)")
+if optReplace in options and paths.len == 0 and optPipe notin options:
+  reportError("provide paths for replacement explicitly (use . for current directory)")
+
+if searchOpt.pattern == "" and optFilenames notin options:
+  reportError("empty pattern was given")
 else:
-  var counter = 0
-  if filenames.len == 0:
-    filenames.add(os.getCurrentDir())
-  if optRegex notin options:
-    if optWord in options:
-      pattern = r"(^ / !\letter)(" & pattern & r") !\letter"
-    if optIgnoreStyle in options:
-      pattern = "\\y " & pattern
-    elif optIgnoreCase in options:
-      pattern = "\\i " & pattern
-    let pegp = peg(pattern)
-    for f in items(filenames):
-      walker(pegp, f, counter)
+  if paths.len == 0 and optPipe notin options:
+    paths.add(".")
+  if optPipe in options or nWorkers == 0:
+    run1Thread()
   else:
-    var reflags = {reStudy, reExtended}
-    if optIgnoreStyle in options:
-      pattern = styleInsensitive(pattern)
-    if optWord in options:
-      pattern = r"\b (:?" & pattern & r") \b"
-    if {optIgnoreCase, optIgnoreStyle} * options != {}:
-      reflags.incl reIgnoreCase
-    let rep = re(pattern, reflags)
-    for f in items(filenames):
-      walker(rep, f, counter)
-  if not oneline:
-    stdout.write($counter & " matches\n")
+    runMultiThread()
+  if gVar.errors != 0:
+    printError $gVar.errors & " errors"
+  if searchOpt.pattern != "":
+    # PATTERN allowed to be empty if --filenames is given
+    printBold($gVar.matches & " matches")
+    stdout.write("\n")
+  if gVar.errors != 0:
+    quit(1)
diff --git a/tools/nimgrep.nim.cfg b/tools/nimgrep.nim.cfg
deleted file mode 100644
index 6d0ea5aad..000000000
--- a/tools/nimgrep.nim.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-# The GC is stable enough now:
-
-#--gc:none
-
-
diff --git a/tools/niminst/buildbat.tmpl b/tools/niminst/buildbat.nimf
index 6767461e3..6767461e3 100644
--- a/tools/niminst/buildbat.tmpl
+++ b/tools/niminst/buildbat.nimf
diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf
new file mode 100644
index 000000000..063a02779
--- /dev/null
+++ b/tools/niminst/buildsh.nimf
@@ -0,0 +1,287 @@
+#? stdtmpl(subsChar='?') | standard
+#proc generateBuildShellScript(c: ConfigData): string =
+#  result = "#!/bin/sh\n# Generated from niminst\n" &
+#           "# Template is in tools/niminst/buildsh.nimf\n" &
+#           "# To regenerate run ``niminst csource`` or ``koch csource``\n"
+
+set -e
+
+while :
+do
+  case "$1" in
+    --os)
+      optos=$2
+      shift 2
+      ;;
+    --cpu)
+      optcpu=$2
+      shift 2
+      ;;
+    --osname)
+      optosname=$2
+      shift 2
+      ;;
+    --parallel)
+      parallel=$2
+      shift 2
+      ;;
+    --extraBuildArgs)
+      extraBuildArgs=" $2"
+      shift 2
+      ;;
+    -h | --help)
+      echo "Options:"
+      echo "  --os <OS>"
+      echo "  --cpu <CPU architecture>"
+      echo "  --osname <name>           Additional OS specification (used for Android)"
+      echo "  --extraBuildArgs <args>   Additional arguments passed to the compiler"
+      echo "  --parallel <number>       Multiprocess build. Requires GNU parallel"
+      exit 0
+      ;;
+    --) # End of all options
+      shift
+      break;
+      ;;
+    -*)
+      echo 2>&1 "Error: Unknown option: $1" >&2
+      exit 1
+      ;;
+    *)  # No more options
+      break
+      ;;
+  esac
+done
+
+parallel="${parallel:-0}"
+CC="${CC:-gcc}"
+if [ "$parallel" -gt 1 ]; then
+  if ! command -v sem > /dev/null; then
+    echo "Error: GNU parallel is required to use --parallel"
+    exit 1
+  fi
+  CC="sem -j $parallel --id $$ ${CC}"
+fi
+COMP_FLAGS="${CPPFLAGS:-} ${CFLAGS:-} ?{c.ccompiler.flags}$extraBuildArgs"
+LINK_FLAGS="${LDFLAGS:-} ?{c.linker.flags}"
+PS4=""
+#  add(result, "# platform detection\n")
+ucpu=`uname -m`
+uos=`uname`
+uosname=
+#  add(result, "# bin dir detection\n")
+binDir=?{firstBinPath(c).toUnix}
+
+if [ -s ../koch.nim ]; then
+  binDir="../bin"
+fi
+
+if [ ! -d $binDir ]; then
+  mkdir $binDir
+fi
+
+#  add(result, "# override OS, CPU and OS Name with command-line arguments\n")
+if [ -n "$optos" ]; then
+  uos="$optos"
+fi
+if [ -n "$optcpu" ]; then
+  ucpu="$optcpu"
+fi
+if [ -n "$optcpu" ]; then
+  uosname="$optosname"
+fi
+
+#  add(result, "# convert to lower case:\n")
+ucpu=`echo $ucpu | tr "[:upper:]" "[:lower:]"`
+uos=`echo $uos | tr "[:upper:]" "[:lower:]"`
+uosname=`echo $uosname | tr "[:upper:]" "[:lower:]"`
+
+case $uos in
+  *linux* )
+    myos="linux"
+    LINK_FLAGS="$LINK_FLAGS -ldl -lm -lrt"
+    ;;
+  *dragonfly* )
+    myos="dragonfly"
+    LINK_FLAGS="$LINK_FLAGS -lm"
+    ;;
+  *freebsd* )
+    myos="freebsd"
+    CC="clang"
+    LINK_FLAGS="$LINK_FLAGS -lm"
+    ;;
+  *crossos* )
+    myos="crossos"
+    CC="clang"
+    LINK_FLAGS="$LINK_FLAGS -lm"
+    ;;
+  *openbsd* )
+    myos="openbsd"
+    CC="clang"
+    LINK_FLAGS="$LINK_FLAGS -lm"
+    ;;
+  *netbsd* )
+    myos="netbsd"
+    LINK_FLAGS="$LINK_FLAGS -lm"
+    ucpu=`uname -p`
+    ;;
+  *darwin* )
+    myos="macosx"
+    CC="clang"
+    LINK_FLAGS="$LINK_FLAGS -ldl -lm"
+    if [ "$HOSTTYPE" = "x86_64" ] ; then
+      ucpu="amd64"
+    fi
+    ;;
+  *aix* )
+    myos="aix"
+    LINK_FLAGS="$LINK_FLAGS -ldl -lm"
+    ;;
+  *solaris* | *sun* )
+    myos="solaris"
+    LINK_FLAGS="$LINK_FLAGS -ldl -lm -lsocket -lnsl"
+    ;;
+  *SunOS* )
+    myos="solaris"
+    LINK_FLAGS="$LINK_FLAGS -ldl -lm -lsocket -lnsl"
+    isOpenIndiana="yes"
+    ;;
+  *haiku* )
+    myos="haiku"
+    LINK_FLAGS="$LINK_FLAGS -lroot -lnetwork"
+    ;;
+  *mingw* | *msys* )
+    myos="windows"
+    ;;
+  *android* )
+    myos="android"
+    LINK_FLAGS="$LINK_FLAGS -ldl -lm -lrt"
+    LINK_FLAGS="$LINK_FLAGS -landroid-glob"
+    ;;
+  *)
+    echo 2>&1 "Error: unknown operating system: $uos"
+    exit 1
+    ;;
+esac
+
+case $ucpu in
+  *i386* | *i486* | *i586* | *i686* | *bepc* | *i86pc* )
+    if [ "$isOpenIndiana" = "yes" ] || [ `uname -o` == "illumos" ] ; then
+      mycpu="amd64"
+    else
+      mycpu="i386"
+    fi
+    ;;
+  *amd*64* | *x86-64* | *x86_64* )
+    mycpu="amd64" ;;
+  *sparc*|*sun* )
+    mycpu="sparc"
+    if [ "$myos" = "linux" ] ; then
+      if [ "$(getconf LONG_BIT)" = "64" ]; then
+        mycpu="sparc64"
+      elif [ "$(isainfo -b)" = "64" ]; then
+        mycpu="sparc64"
+      fi
+    fi
+    ;;
+  *ppc64le* )
+    mycpu="powerpc64el" ;;
+  *ppc64* )
+    if [ "$myos" = "linux" ] ; then
+      COMP_FLAGS="$COMP_FLAGS -m64"
+      LINK_FLAGS="$LINK_FLAGS -m64"
+    fi
+    mycpu="powerpc64" ;;
+  *power*|*ppc* )
+    if [ "$myos" = "freebsd" ] ; then
+      if [ "$ucpu" != "powerpc" ] ; then
+        COMP_FLAGS="$COMP_FLAGS -m64"
+        LINK_FLAGS="$LINK_FLAGS -m64"
+      fi
+      mycpu=`uname -p`
+      case $mycpu in
+        powerpc64le)
+        mycpu="powerpc64el"
+      esac
+    else
+      mycpu="powerpc"
+    fi
+    ;;
+  *hppa*)
+    mycpu="hppa" ;;
+  *ia64*)
+    mycpu="ia64" ;;
+  *m68k*)
+    mycpu="m68k" ;;
+  *mips* )
+    mycpu="$("$CC" -dumpmachine | sed 's/-.*//')"
+    case $mycpu in
+      mips|mipsel|mips64|mips64el)
+        ;;
+      *)
+        echo 2>&1 "Error: unknown MIPS target: $mycpu"
+        exit 1
+    esac
+    ;;
+  *alpha* )
+    mycpu="alpha" ;;
+  *aarch64*|*arm64* )
+    mycpu="arm64" ;;
+  *arm*|*armv6l*|*armv7l*|*armv8l* )
+    mycpu="arm" ;;
+  *riscv64|riscv* )
+    mycpu="riscv64" ;;
+  *e2k* )
+    mycpu="e2k" ;;
+  *loongarch64* )
+    mycpu="loongarch64" ;;
+  *)
+    echo 2>&1 "Error: unknown processor: $ucpu"
+    exit 1
+    ;;
+esac
+
+case $uosname in
+  *android* )
+    LINK_FLAGS="$LINK_FLAGS -landroid-glob"
+    myosname="android"
+    myos="android"
+    ;;
+esac
+
+#  add(result, "# call the compiler:\n")
+echo \# OS:  $myos
+echo \# CPU: $mycpu
+
+case $myos in
+#  for osA in 1..c.oses.len:
+?{c.oses[osA-1]})
+  case $mycpu in
+#    for cpuA in 1..c.cpus.len:
+  ?{c.cpus[cpuA-1]})
+    set -x
+#      var linkCmd = ""
+#      for ff in items(c.cfiles[osA][cpuA]):
+#        let f = ff.toUnix
+    $CC $COMP_FLAGS -Ic_code -c ?{f} -o ?{changeFileExt(f, "o")}
+#        add(linkCmd, " \\\n" & changeFileExt(f, "o"))
+#      end for
+    if [ "$parallel" -gt 0 ]; then
+      sem --wait --id $$
+    fi
+    $CC -o ?{"$binDir/" & toLowerAscii(c.name)} ?linkCmd $LINK_FLAGS
+    ;;
+#    end for
+  *)
+    echo 2>&1 "Error: no C code generated for: [$myos: $mycpu]"
+    exit 1
+    ;;
+  esac
+  ;;
+#  end for
+*)
+  echo 2>&1 "Error: no C code generated for: [$myos: $mycpu]"
+  exit 1
+  ;;
+esac
+
+: SUCCESS
diff --git a/tools/niminst/buildsh.tmpl b/tools/niminst/buildsh.tmpl
deleted file mode 100644
index be6d43754..000000000
--- a/tools/niminst/buildsh.tmpl
+++ /dev/null
@@ -1,218 +0,0 @@
-#? stdtmpl(subsChar='?') | standard
-#proc generateBuildShellScript(c: ConfigData): string =
-#  result = "#! /bin/sh\n# Generated from niminst\n" &
-#           "# Template is in tools/niminst/buildsh.tmpl\n" &
-#           "# To regenerate run ``niminst csource`` or ``koch csource``\n"
-
-set -e
-
-while :
-do
-  case "$1" in
-    --os)
-      optos=$2
-      shift 2
-      ;;
-    --cpu)
-      optcpu=$2
-      shift 2
-      ;;
-    --osname)
-      optosname=$2
-      shift 2
-      ;;
-    --extraBuildArgs)
-      extraBuildArgs=" $2"
-      shift 2
-      ;;
-    --) # End of all options
-      shift
-      break;
-      ;;
-    -*)
-      echo 2>&1 "Error: Unknown option: $1" >&2
-      exit 1
-      ;;
-    *)  # No more options
-      break
-      ;;
-  esac
-done
-
-CC="${CC:-gcc}"
-LINKER="${LD:-gcc}"
-COMP_FLAGS="${CPPFLAGS:-} ${CFLAGS:-} ?{c.ccompiler.flags}$extraBuildArgs"
-LINK_FLAGS="${LDFLAGS:-} ?{c.linker.flags}"
-PS4=""
-#  add(result, "# platform detection\n")
-ucpu=`uname -m`
-uos=`uname`
-uosname=
-#  add(result, "# bin dir detection\n")
-binDir=?{firstBinPath(c).toUnix}
-
-if [ -s ../koch.nim ]; then
-  binDir="../bin"
-fi
-
-if [ ! -d $binDir ]; then
-  mkdir $binDir
-fi
-
-#  add(result, "# override OS, CPU and OS Name with command-line arguments\n")
-if [ -n "$optos" ]; then 
-  uos="$optos"
-fi
-if [ -n "$optcpu" ]; then
-  ucpu="$optcpu"
-fi
-if [ -n "$optcpu" ]; then
-  uosname="$optosname"
-fi
-
-#  add(result, "# convert to lower case:\n")
-ucpu=`echo $ucpu | tr "[:upper:]" "[:lower:]"`
-uos=`echo $uos | tr "[:upper:]" "[:lower:]"`
-uosname=`echo $uosname | tr "[:upper:]" "[:lower:]"`
-
-case $uos in
-  *linux* )
-    myos="linux"
-    LINK_FLAGS="$LINK_FLAGS -ldl -lm -lrt"
-    ;;
-  *dragonfly* )
-    myos="dragonfly"
-    LINK_FLAGS="$LINK_FLAGS -lm"
-    ;;
-  *freebsd* )
-    myos="freebsd"
-    CC="clang"
-    LINKER="clang"
-    LINK_FLAGS="$LINK_FLAGS -lm"
-    ;;
-  *openbsd* )
-    myos="openbsd"
-    LINK_FLAGS="$LINK_FLAGS -lm"
-    ;;
-  *netbsd* )
-    myos="netbsd"
-    LINK_FLAGS="$LINK_FLAGS -lm"
-    ;;
-  *darwin* )
-    myos="macosx"
-    CC="clang"
-    LINKER="clang"
-    LINK_FLAGS="$LINK_FLAGS -ldl -lm"
-    if [ "$HOSTTYPE" = "x86_64" ] ; then
-      ucpu="amd64"
-    fi
-    ;;
-  *aix* )
-    myos="aix"
-    LINK_FLAGS="$LINK_FLAGS -ldl -lm"
-    ;;
-  *solaris* | *sun* )
-    myos="solaris"
-    LINK_FLAGS="$LINK_FLAGS -ldl -lm -lsocket -lnsl"
-    ;;
-  *haiku* )
-    myos="haiku"
-    LINK_FLAGS="$LINK_FLAGS -lroot -lnetwork"
-    ;;
-  *mingw* )
-    myos="windows"
-    ;;
-  *android* )
-    myos="android"
-    LINK_FLAGS="$LINK_FLAGS -ldl -lm -lrt"
-    LINK_FLAGS="$LINK_FLAGS -landroid-glob"
-    ;;
-  *)
-    echo 2>&1 "Error: unknown operating system: $uos"
-    exit 1
-    ;;
-esac
-
-case $ucpu in
-  *i386* | *i486* | *i586* | *i686* | *bepc* | *i86pc* )
-    mycpu="i386" ;;
-  *amd*64* | *x86-64* | *x86_64* )
-    mycpu="amd64" ;;
-  *sparc*|*sun* )
-    mycpu="sparc"
-    if [ "$(isainfo -b)" = "64" ]; then
-      mycpu="sparc64"
-    fi
-    ;;
-  *ppc64le* )
-    mycpu="powerpc64el" ;;
-  *ppc64* )
-    if [ "$myos" = "linux" ] ; then
-      COMP_FLAGS="$COMP_FLAGS -m64"
-      LINK_FLAGS="$LINK_FLAGS -m64"
-    fi
-    mycpu="powerpc64" ;;
-  *power*|*ppc* )
-    mycpu="powerpc" ;;
-  *mips* )
-    mycpu="$("$CC" -dumpmachine | sed 's/-.*//')"
-    case $mycpu in
-      mips|mipsel|mips64|mips64el)
-        ;;
-      *)
-        echo 2>&1 "Error: unknown MIPS target: $mycpu"
-        exit 1
-    esac
-    ;;
-  *arm*|*armv6l*|*armv71* )
-    mycpu="arm" ;;
-  *aarch64* )
-    mycpu="arm64" ;;
-  *)
-    echo 2>&1 "Error: unknown processor: $ucpu"
-    exit 1
-    ;;
-esac
-
-case $uosname in
-  *android* )
-    LINK_FLAGS="$LINK_FLAGS -landroid-glob"
-    myosname="android"
-    myos="android"
-    ;;
-esac
-
-#  add(result, "# call the compiler:\n")
-echo \# OS:  $myos
-echo \# CPU: $mycpu
-
-case $myos in
-#  for osA in 1..c.oses.len:
-?{c.oses[osA-1]})
-  case $mycpu in
-#    for cpuA in 1..c.cpus.len:
-  ?{c.cpus[cpuA-1]})
-    set -x
-#      var linkCmd = ""
-#      for ff in items(c.cfiles[osA][cpuA]):
-#        let f = ff.toUnix
-    $CC $COMP_FLAGS -Ic_code -c ?{f} -o ?{changeFileExt(f, "o")}
-#        add(linkCmd, " \\\n" & changeFileExt(f, "o"))
-#      end for
-    $LINKER -o ?{"$binDir/" & toLowerAscii(c.name)} ?linkCmd $LINK_FLAGS
-    ;;
-#    end for
-  *)
-    echo 2>&1 "Error: no C code generated for: [$myos: $mycpu]"
-    exit 1
-    ;;
-  esac
-  ;;
-#  end for
-*)
-  echo 2>&1 "Error: no C code generated for: [$myos: $mycpu]"
-  exit 1
-  ;;
-esac
-
-: SUCCESS
diff --git a/tools/niminst/debcreation.nim b/tools/niminst/debcreation.nim
index 0ecea132f..219cb44ce 100644
--- a/tools/niminst/debcreation.nim
+++ b/tools/niminst/debcreation.nim
@@ -9,6 +9,10 @@
 
 import osproc, times, os, strutils
 
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
+
 # http://www.debian.org/doc/manuals/maint-guide/
 
 # Required files for debhelper.
@@ -84,7 +88,7 @@ proc createCopyright(pkgName, mtnName, mtnEmail, version: string,
     addN("Files: " & f)
     addN("License: " & license)
 
-proc formatDateTime(t: TimeInfo, timezone: string): string =
+proc formatDateTime(t: DateTime, timezone: string): string =
   var day = ($t.weekday)[0..2] & ", "
 
   return "$1$2 $3 $4 $5:$6:$7 $8" % [day, intToStr(t.monthday, 2),
@@ -101,7 +105,7 @@ proc createChangelog(pkgName, version, maintainer: string): string =
   addN("  * Initial release.")
   addN("")
   addN(" -- " & maintainer & "  " &
-       formatDateTime(getGMTime(getTime()), "+0000"))
+       formatDateTime(utc(getTime()), "+0000"))
 
 proc createRules(): string =
   ## Creates a nim application-agnostic rules file for building deb packages.
diff --git a/tools/niminst/deinstall.nimf b/tools/niminst/deinstall.nimf
new file mode 100644
index 000000000..0dcfda75e
--- /dev/null
+++ b/tools/niminst/deinstall.nimf
@@ -0,0 +1,81 @@
+#? stdtmpl(subsChar='?') | standard
+#proc generateDeinstallScript(c: ConfigData): string =
+#  result = "#!/bin/sh\n# Generated by niminst\n"
+#  var proj = c.name.toLowerAscii
+
+if [ $# -eq 1 ] ; then
+  case $1 in
+    "--help"|"-h"|"help"|"h")
+      echo "?c.displayName deinstallation script"
+      echo "Usage: [sudo] sh deinstall.sh DIR"
+      echo "Where DIR may be:"
+      echo "  /usr/bin"
+      echo "  /usr/local/bin"
+      echo "  /opt"
+      echo "  <some other dir> (treated similar '/opt')"
+      exit 1
+      ;;
+    "/usr/bin")
+      bindir=/usr/bin
+      configdir=/etc/?proj
+      libdir=/usr/lib/?proj
+      docdir=/usr/share/?proj/doc
+      datadir=/usr/share/?proj/data
+      nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version"
+      ;;
+    "/usr/local/bin")
+      bindir=/usr/local/bin
+      configdir=/etc/?proj
+      libdir=/usr/local/lib/?proj
+      docdir=/usr/local/share/?proj/doc
+      datadir=/usr/local/share/?proj/data
+      nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version"
+      ;;
+    "/opt")
+      bindir="/opt/?proj/bin"
+      configdir="/opt/?proj/config"
+      libdir="/opt/?proj/lib"
+      docdir="/opt/?proj/doc"
+      datadir="/opt/?proj/data"
+      nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version"
+      ;;
+    *)
+      bindir="$1/?proj/bin"
+      configdir="$1/?proj/config"
+      libdir="$1/?proj/lib"
+      docdir="$1/?proj/doc"
+      datadir="$1/?proj/data"
+      nimbleDir="$1/?proj"
+      ;;
+  esac
+  echo "removing files..."
+
+#for ff in items(c.cat[fcUnixBin]):
+  #let f = ff.toUnix
+  rm -f $bindir/?f.skipRoot
+#end for
+#for ff in items(c.cat[fcConfig]):
+  #let f = ff.toUnix
+  rm -f $configdir/?f.skipRoot
+#end for
+  rm -rf $docdir
+  rm -rf $datadir
+  rm -rf $libdir
+
+  ## Nimble pkg stuff
+  #for f in items(c.cat[fcNimble]):
+    rm -f $nimbleDir/?f.toUnix
+  #end for
+  rm -f $nimbleDir/?{c.nimblePkgName}.nimble
+
+  echo "deinstallation successful"
+else
+  echo "?c.displayName deinstallation script"
+  echo "Usage: [sudo] sh deinstall.sh DIR"
+  echo "Where DIR may be:"
+  echo "  /usr/bin"
+  echo "  /usr/local/bin"
+  echo "  /opt"
+  echo "  <some other dir> (treated similar '/opt')"
+  exit 1
+fi
diff --git a/tools/niminst/deinstall.tmpl b/tools/niminst/deinstall.tmpl
deleted file mode 100644
index bba310f76..000000000
--- a/tools/niminst/deinstall.tmpl
+++ /dev/null
@@ -1,81 +0,0 @@
-#? stdtmpl(subsChar='?') | standard
-#proc generateDeinstallScript(c: ConfigData): string =
-#  result = "#! /bin/sh\n# Generated by niminst\n"
-#  var proj = c.name.toLowerAscii
-
-if [ $# -eq 1 ] ; then
-  case $1 in
-    "--help"|"-h"|"help"|"h")
-      echo "?c.displayName deinstallation script"
-      echo "Usage: [sudo] sh deinstall.sh DIR"
-      echo "Where DIR may be:"
-      echo "  /usr/bin"
-      echo "  /usr/local/bin"
-      echo "  /opt"
-      echo "  <some other dir> (treated similar '/opt')"
-      exit 1
-      ;;
-    "/usr/bin")
-      bindir=/usr/bin
-      configdir=/etc
-      libdir=/usr/lib/?proj
-      docdir=/usr/share/?proj/doc
-      datadir=/usr/share/?proj/data
-      nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version"
-      ;;
-    "/usr/local/bin")
-      bindir=/usr/local/bin
-      configdir=/etc
-      libdir=/usr/local/lib/?proj
-      docdir=/usr/local/share/?proj/doc
-      datadir=/usr/local/share/?proj/data
-      nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version"
-      ;;
-    "/opt")
-      bindir="/opt/?proj/bin"
-      configdir="/opt/?proj/config"
-      libdir="/opt/?proj/lib"
-      docdir="/opt/?proj/doc"
-      datadir="/opt/?proj/data"
-      nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version"
-      ;;
-    *)
-      bindir="$1/?proj/bin"
-      configdir="$1/?proj/config"
-      libdir="$1/?proj/lib"
-      docdir="$1/?proj/doc"
-      datadir="$1/?proj/data"
-      nimbleDir="$1/?proj"
-      ;;
-  esac
-  echo "removing files..."
-
-#for ff in items(c.cat[fcUnixBin]):
-  #let f = ff.toUnix
-  rm -f $bindir/?f.skipRoot
-#end for
-#for ff in items(c.cat[fcConfig]):
-  #let f = ff.toUnix
-  rm -f $configdir/?f.skipRoot
-#end for
-  rm -rf $docdir
-  rm -rf $datadir
-  rm -rf $libdir
-
-  ## Nimble pkg stuff
-  #for f in items(c.cat[fcNimble]):
-    rm -f $nimbleDir/?f.toUnix
-  #end for
-  rm -f $nimbleDir/?{c.nimblePkgName}.nimble
-
-  echo "deinstallation successful"
-else
-  echo "?c.displayName deinstallation script"
-  echo "Usage: [sudo] sh deinstall.sh DIR"
-  echo "Where DIR may be:"
-  echo "  /usr/bin"
-  echo "  /usr/local/bin"
-  echo "  /opt"
-  echo "  <some other dir> (treated similar '/opt')"
-  exit 1
-fi
diff --git a/tools/niminst/inno.tmpl b/tools/niminst/inno.nimf
index ef2da8a75..ef2da8a75 100644
--- a/tools/niminst/inno.tmpl
+++ b/tools/niminst/inno.nimf
diff --git a/tools/niminst/install.nimf b/tools/niminst/install.nimf
new file mode 100644
index 000000000..75ff9ce11
--- /dev/null
+++ b/tools/niminst/install.nimf
@@ -0,0 +1,138 @@
+#? stdtmpl(subsChar = '?') | standard
+#proc generateInstallScript(c: ConfigData): string =
+#  result = "#!/bin/sh\n# Generated by niminst\n"
+#  var proj = c.name.toLowerAscii
+
+set -e
+
+if [ $# -eq 1 ] ; then
+#if c.cat[fcUnixBin].len > 0:
+  if [ -f "?{c.cat[fcUnixBin][0].toUnix}" ]
+  then
+    echo "?c.displayName build detected"
+  else
+    echo "Please build ?c.displayName before installing it"
+    exit 1
+  fi
+#end if
+  case $1 in
+    "--help"|"-h"|"help"|"h")
+      echo "?c.displayName installation script"
+      echo "Usage: [sudo] [env DESTDIR=...] sh install.sh DIR"
+      echo "Where DIR may be:"
+      echo "  /usr/bin"
+      echo "  /usr/local/bin"
+      echo "  /opt"
+      echo "  <some other dir> (treated similar to '/opt')"
+      echo "To deinstall, use the command:"
+      echo "sh deinstall.sh DIR"
+      exit 1
+      ;;
+    "/usr/bin")
+      bindir=$1
+      configdir="/etc/?proj"
+      libdir="/usr/lib/?proj"
+      docdir="/usr/share/?proj/doc"
+      datadir="/usr/share/?proj/data"
+      nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version"
+      ;;
+    "/usr/local/bin")
+      bindir=$1
+      configdir="/etc/?proj"
+      libdir="/usr/local/lib/?proj"
+      docdir="/usr/local/share/?proj/doc"
+      datadir="/usr/local/share/?proj/data"
+      nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version"
+      ;;
+    "/opt")
+      bindir="/opt/?proj/bin"
+      configdir="/opt/?proj/config"
+      libdir="/opt/?proj/lib"
+      docdir="/opt/?proj/doc"
+      datadir="/opt/?proj/data"
+      nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version"
+      ;;
+    *)
+      bindir="$1/?proj/bin"
+      configdir="$1/?proj/config"
+      libdir="$1/?proj/lib"
+      docdir="$1/?proj/doc"
+      datadir="$1/?proj/data"
+      nimbleDir="$1/?proj"
+      ;;
+  esac
+
+  bindir="${DESTDIR}${bindir}"
+  configdir="${DESTDIR}${configdir}"
+  libdir="${DESTDIR}${libdir}"
+  docdir="${DESTDIR}${docdir}"
+  datadir="${DESTDIR}${datadir}"
+  nimbleDir="${DESTDIR}${nimbleDir}"
+
+  mkdir -p "$bindir"
+  mkdir -p "$configdir"
+  mkdir -p "$libdir"
+  mkdir -p "$docdir"
+  mkdir -p "$datadir"
+  mkdir -p "$nimbleDir"
+  echo "copying files..."
+#var createdDirs = newStringTable()
+#for cat in {fcConfig..fcLib, fcNimble}:
+#  for f in items(c.cat[cat]):
+#    var mk = splitFile(f.skipRoot).dir
+#    if cat != fcNimble:
+#      mk = unixDirVars[cat] & "/" & mk
+#    else:
+#      mk = "$nimbleDir" / splitFile(f).dir
+#    end if
+#    if mk.len > 0 and not createdDirs.hasKey(mk):
+#      createdDirs[mk] = "true"
+  mkdir -p "?{mk.toUnix}"
+#    end if
+#  end for
+#end for
+
+#for f in items(c.cat[fcUnixBin]):
+  cp "?f.toUnix" "$bindir/?f.skipRoot.toUnix"
+  chmod 755 "$bindir/?f.skipRoot.toUnix"
+#end for
+#for f in items(c.cat[fcConfig]):
+  cp "?f.toUnix" "$configdir/?f.skipRoot.toUnix"
+  chmod 644 "$configdir/?f.skipRoot.toUnix"
+#end for
+#for f in items(c.cat[fcData]):
+  if [ -f "?f.toUnix" ]; then
+    cp "?f.toUnix" "$datadir/?f.skipRoot.toUnix"
+    chmod 644 "$datadir/?f.skipRoot.toUnix"
+  fi
+#end for
+#for f in items(c.cat[fcDoc]):
+  if [ -f "?f.toUnix" ]; then
+    cp "?f.toUnix" "$docdir/?f.skipRoot.toUnix"
+    chmod 644 "$docdir/?f.skipRoot.toUnix"
+  fi
+#end for
+#for f in items(c.cat[fcLib]):
+  cp "?f.toUnix" "$libdir/?f.skipRoot.toUnix"
+  chmod 644 "$libdir/?f.skipRoot.toUnix"
+#end for
+#for f in items(c.cat[fcNimble]):
+  cp "?f.toUnix" "$nimbleDir/?f.toUnix"
+  chmod 644 "$nimbleDir/?f.toUnix"
+#end for
+cp "?{c.nimblePkgName}.nimble" "$nimbleDir/?{c.nimblePkgName}.nimble"
+chmod 644 "$nimbleDir/?{c.nimblePkgName}.nimble"
+
+  echo "installation successful"
+else
+  echo "?c.displayName installation script"
+  echo "Usage: [sudo] [env DESTDIR=...] sh install.sh DIR"
+  echo "Where DIR may be:"
+  echo "  /usr/bin"
+  echo "  /usr/local/bin"
+  echo "  /opt"
+  echo "  <some other dir> (treated similar to '/opt')"
+  echo "To deinstall, use the command:"
+  echo "sh deinstall.sh DIR"
+  exit 1
+fi
diff --git a/tools/niminst/install.tmpl b/tools/niminst/install.tmpl
deleted file mode 100644
index 91504891d..000000000
--- a/tools/niminst/install.tmpl
+++ /dev/null
@@ -1,134 +0,0 @@
-#? stdtmpl(subsChar = '?') | standard
-#proc generateInstallScript(c: ConfigData): string =
-#  result = "#! /bin/sh\n# Generated by niminst\n"
-#  var proj = c.name.toLowerAscii
-
-set -e
-
-if [ $# -eq 1 ] ; then
-# if c.cat[fcUnixBin].len > 0:
-  if test -f ?{c.cat[fcUnixBin][0].toUnix}
-  then
-    echo "?c.displayName build detected"
-  else
-    echo "Please build ?c.displayName before installing it"
-    exit 1
-  fi
-#  end if
-  case $1 in
-    "--help"|"-h"|"help"|"h")
-      echo "?c.displayName installation script"
-      echo "Usage: [sudo] sh install.sh DIR"
-      echo "Where DIR may be:"
-      echo "  /usr/bin"
-      echo "  /usr/local/bin"
-      echo "  /opt"
-      echo "  <some other dir> (treated similar to '/opt')"
-      echo "To deinstall, use the command:"
-      echo "sh deinstall.sh DIR"
-      exit 1
-      ;;
-    "/usr/bin")
-      bindir=/usr/bin
-      configdir=/etc
-      libdir=/usr/lib/?proj
-      docdir=/usr/share/?proj/doc
-      datadir=/usr/share/?proj/data
-      nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version"
-      ;;
-    "/usr/local/bin")
-      bindir=/usr/local/bin
-      configdir=/etc
-      libdir=/usr/local/lib/?proj
-      docdir=/usr/local/share/?proj/doc
-      datadir=/usr/local/share/?proj/data
-      nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version"
-      ;;
-    "/opt")
-      bindir="/opt/?proj/bin"
-      configdir="/opt/?proj/config"
-      libdir="/opt/?proj/lib"
-      docdir="/opt/?proj/doc"
-      datadir="/opt/?proj/data"
-      nimbleDir="/opt/nimble/pkgs/?c.nimblePkgName-?c.version"
-      mkdir -p /opt/?proj
-      mkdir -p $bindir
-      mkdir -p $configdir
-      ;;
-    *)
-      bindir="$1/?proj/bin"
-      configdir="$1/?proj/config"
-      libdir="$1/?proj/lib"
-      docdir="$1/?proj/doc"
-      datadir="$1/?proj/data"
-      nimbleDir="$1/?proj"
-      mkdir -p $1/?proj
-      mkdir -p $bindir
-      mkdir -p $configdir
-      ;;
-  esac
-
-  mkdir -p $libdir
-  mkdir -p $docdir
-  mkdir -p $nimbleDir/
-  echo "copying files..."
-#var createdDirs = newStringTable()
-#for cat in {fcConfig..fcLib, fcNimble}:
-#  for f in items(c.cat[cat]):
-#    var mk = splitFile(f.skipRoot).dir
-#    if cat != fcNimble:
-#      mk = unixDirVars[cat] & "/" & mk
-#    else:
-#      mk = "$nimbleDir" / splitFile(f).dir
-#    end if
-#    if mk.len > 0 and not createdDirs.hasKey(mk):
-#      createdDirs[mk] = "true"
-  mkdir -p ?{mk.toUnix}
-#    end if
-#  end for
-#end for
-
-#for f in items(c.cat[fcUnixBin]):
-  cp ?f.toUnix $bindir/?f.skipRoot.toUnix
-  chmod 755 $bindir/?f.skipRoot.toUnix
-#end for
-#for f in items(c.cat[fcConfig]):
-  cp ?f.toUnix $configdir/?f.skipRoot.toUnix
-  chmod 644 $configdir/?f.skipRoot.toUnix
-#end for
-#for f in items(c.cat[fcData]):
-  if [ -f ?f.toUnix ]; then
-    cp ?f.toUnix $datadir/?f.skipRoot.toUnix
-    chmod 644 $datadir/?f.skipRoot.toUnix
-  fi
-#end for
-#for f in items(c.cat[fcDoc]):
-  if [ -f ?f.toUnix ]; then
-    cp ?f.toUnix $docdir/?f.skipRoot.toUnix
-    chmod 644 $docdir/?f.skipRoot.toUnix
-  fi
-#end for
-#for f in items(c.cat[fcLib]):
-  cp ?f.toUnix $libdir/?f.skipRoot.toUnix
-  chmod 644 $libdir/?f.skipRoot.toUnix
-#end for
-#for f in items(c.cat[fcNimble]):
-  cp ?f.toUnix $nimbleDir/?f.toUnix
-  chmod 644 $nimbleDir/?f.toUnix
-#end for
-cp ?{c.nimblePkgName}.nimble $nimbleDir/?{c.nimblePkgName}.nimble
-chmod 644 $nimbleDir/?{c.nimblePkgName}.nimble
-
-  echo "installation successful"
-else
-  echo "?c.displayName installation script"
-  echo "Usage: [sudo] sh install.sh DIR"
-  echo "Where DIR may be:"
-  echo "  /usr/bin"
-  echo "  /usr/local/bin"
-  echo "  /opt"
-  echo "  <some other dir> (treated similar to '/opt')"
-  echo "To deinstall, use the command:"
-  echo "sh deinstall.sh DIR"
-  exit 1
-fi
diff --git a/tools/niminst/makefile.nimf b/tools/niminst/makefile.nimf
new file mode 100644
index 000000000..002bc0592
--- /dev/null
+++ b/tools/niminst/makefile.nimf
@@ -0,0 +1,219 @@
+#? stdtmpl(subsChar='?') | standard
+#proc generateMakefile(c: ConfigData): string =
+#  result = "# Generated from niminst\n" &
+#           "# Template is in tools/niminst/makefile.nimf\n" &
+#           "# To regenerate run ``niminst csource`` or ``koch csource``\n"
+
+CC ??= gcc
+CFLAGS += -Ic_code ?{c.ccompiler.flags}
+LDFLAGS += ?{c.linker.flags}
+binDir = ?{firstBinPath(c).toUnix}
+
+koch := $(shell sh -c 'test -s ../koch.nim && echo "yes"')
+ifeq ($(koch),yes)
+  binDir = ../bin
+endif
+
+target := ?{"$(binDir)/" & toLowerAscii(c.name)}
+
+
+ucpu := $(shell sh -c 'uname -m | tr "[:upper:]" "[:lower:]"')
+ifeq ($(OS),Windows_NT)
+  uos := windows
+else
+  uos := $(shell sh -c 'uname | tr "[:upper:]" "[:lower:]"')
+endif
+
+ifeq ($(uos),linux)
+  myos = linux
+  ## add -lrt to avoid "undefined reference to `clock_gettime'" with glibc<2.17
+  LDFLAGS += -ldl -lm -lrt
+endif
+ifeq ($(uos),dragonfly)
+  myos = freebsd
+  LDFLAGS += -lm
+endif
+ifeq ($(uos),freebsd)
+  myos= freebsd
+  CC = clang
+  LDFLAGS += -lm
+endif
+ifeq ($(uos),openbsd)
+  myos = openbsd
+  LDFLAGS += -lm
+endif
+ifeq ($(uos),netbsd)
+  myos = netbsd
+  LDFLAGS += -lm
+  ucpu = $(shell sh -c 'uname -p')
+endif
+ifeq ($(uos),darwin)
+  myos = macosx
+  CC = clang
+  LDFLAGS += -ldl -lm
+  ifeq ($(HOSTTYPE),x86_64)
+    ucpu = amd64
+  endif
+endif
+ifeq ($(uos),aix)
+  myos = aix
+  LDFLAGS += -dl -lm
+endif
+ifeq ($(uos),solaris)
+  myos = solaris
+  LDFLAGS += -ldl -lm -lsocket -lnsl
+endif
+ifeq ($(uos),sun)
+  myos = solaris
+  LDFLAGS += -ldl -lm -lsocket -lnsl
+endif
+ifeq ($(uos),haiku)
+  myos = haiku
+endif
+ifeq ($(uos),windows)
+  myos = windows
+  target := $(target).exe
+endif
+ifndef myos
+  $(error unknown operating system: $(uos))
+endif
+
+ifeq ($(ucpu),i386)
+  mycpu = i386
+endif
+ifeq ($(ucpu),i486)
+  mycpu = i386
+endif
+ifeq ($(ucpu),i586)
+  mycpu = i386
+endif
+ifeq ($(ucpu),i686)
+  mycpu = i386
+endif
+ifeq ($(ucpu),bepc)
+  mycpu = i386
+endif
+ifeq ($(ucpu),i86pc)
+  mycpu = i386
+endif
+ifeq ($(ucpu),amd64)
+  mycpu = amd64
+endif
+ifeq ($(ucpu),x86-64)
+  mycpu = amd64
+endif
+ifeq ($(ucpu),x86_64)
+  mycpu = amd64
+endif
+ifeq ($(ucpu),parisc64)
+  mycpu = hppa
+endif
+ifeq ($(ucpu),s390x)
+  mycpu = s390x
+endif
+ifeq ($(ucpu),sparc)
+  mycpu = sparc
+endif
+ifeq ($(ucpu),sparc64)
+  mycpu = sparc64
+endif
+ifeq ($(ucpu),sun)
+  mycpu = sparc
+endif
+ifeq ($(ucpu),ppc64le)
+  mycpu = powerpc64el
+endif
+ifeq ($(ucpu),ppc64)
+  mycpu = powerpc64
+  ifeq ($(myos),linux)
+    CFLAGS += -m64
+    LDFLAGS += -m64
+  endif
+endif
+ifeq ($(ucpu),powerpc)
+  mycpu = powerpc
+  ifeq ($(myos),freebsd)
+    mycpu = $(shell sh -c 'uname -p | tr "[:upper:]" "[:lower:]"')
+    CFLAGS += -m64
+    LDFLAGS += -m64
+    ifeq ($(mycpu),powerpc64le)
+      mycpu = powerpc64el
+    endif
+  endif
+endif
+ifeq ($(ucpu),ppc)
+   mycpu = powerpc
+endif
+ifneq (,$(filter $(ucpu), mips mips64))
+  mycpu = $(shell /bin/sh -c '"$(CC)" -dumpmachine | sed "s/-.*//"')
+  ifeq (,$(filter $(mycpu), mips mipsel mips64 mips64el))
+    $(error unknown MIPS target: $(mycpu))
+  endif
+endif
+ifeq ($(ucpu),arm)
+  mycpu = arm
+endif
+ifeq ($(ucpu),armeb)
+  mycpu = arm
+endif
+ifeq ($(ucpu),armel)
+  mycpu = arm
+endif
+ifeq ($(ucpu),armv6l)
+  mycpu = arm
+endif
+ifeq ($(ucpu),armv7l)
+  mycpu = arm
+endif
+ifeq ($(ucpu),armv7hl)
+  mycpu = arm
+endif
+ifeq ($(ucpu),armv8l)
+  mycpu = arm
+endif
+ifeq ($(ucpu),aarch64)
+  mycpu = arm64
+endif
+ifeq ($(ucpu),arm64)
+  mycpu = arm64
+endif
+ifeq ($(ucpu),riscv64)
+  mycpu = riscv64
+endif
+ifeq ($(ucpu),e2k)
+  mycpu = e2k
+endif
+ifeq ($(ucpu),loongarch64)
+  mycpu = loongarch64
+endif
+ifndef mycpu
+  $(error unknown CPU architecture: $(ucpu) See makefile.nimf)
+endif
+
+# for osA in 1..c.oses.len:
+ifeq ($(myos),?{c.oses[osA-1]})
+#   for cpuA in 1..c.cpus.len:
+  ifeq ($(mycpu),?{c.cpus[cpuA-1]})
+#     var oFiles = ""
+#     for ff in c.cfiles[osA][cpuA].items:
+#       oFiles.add(" " & changeFileExt(ff.toUnix, "o"))
+#     end for
+    oFiles =?oFiles
+  endif
+#   end for
+endif
+# end for
+
+ifeq ($(strip $(oFiles)),)
+  $(error no C code generated for: [$(myos): $(mycpu)])
+endif
+
+$(target): $(oFiles)
+	@mkdir -p $(binDir)
+	$(CC) -o $@ $^ $(LDFLAGS)
+	@echo "SUCCESS"
+
+.PHONY: clean
+
+clean:
+	rm -f $(oFiles) ?{"$(binDir)/" & toLowerAscii(c.name)}
diff --git a/tools/niminst/makefile.tmpl b/tools/niminst/makefile.tmpl
deleted file mode 100644
index 891333251..000000000
--- a/tools/niminst/makefile.tmpl
+++ /dev/null
@@ -1,177 +0,0 @@
-#? stdtmpl(subsChar='?') | standard
-#proc generateMakefile(c: ConfigData): string =
-#  result = "# Generated from niminst\n" &
-#           "# Template is in tools/niminst/makefile.tmpl\n" &
-#           "# To regenerate run ``niminst csource`` or ``koch csource``\n"
-
-CC ??= gcc
-LD ??= gcc
-CFLAGS += -Ic_code ?{c.ccompiler.flags}
-LDFLAGS += ?{c.linker.flags}
-binDir = ?{firstBinPath(c).toUnix}
-
-koch := $(shell sh -c 'test -s ../koch.nim && echo "yes"')
-ifeq ($(koch),yes)
-  binDir = ../bin
-endif
-
-ucpu := $(shell sh -c 'uname -m | tr "[:upper:]" "[:lower:]"')
-uos := $(shell sh -c 'uname | tr "[:upper:]" "[:lower:]"')
-
-ifeq ($(uos),linux)
-  myos = linux
-  LDFLAGS += -ldl -lm
-endif
-ifeq ($(uos),dragonfly)
-  myos = freebsd
-  LDFLAGS += -lm
-endif
-ifeq ($(uos),freebsd)
-  myos= freebsd
-  CC = clang
-  LD = clang
-  LDFLAGS += -lm
-endif
-ifeq ($(uos),openbsd)
-  myos = openbsd
-  LDFLAGS += -lm
-endif
-ifeq ($(uos),netbsd)
-  myos = netbsd
-  LDFLAGS += -lm
-endif
-ifeq ($(uos),darwin)
-  myos = macosx
-  CC = clang
-  LD = clang
-  LDFLAGS += -ldl -lm
-  ifeq ($(HOSTTYPE),x86_64)
-    ucpu = amd64
-  endif
-endif
-ifeq ($(uos),aix)
-  myos = aix
-  LDFLAGS += -dl -lm
-endif
-ifeq ($(uos),solaris)
-  myos = solaris
-  LDFLAGS += -ldl -lm -lsocket -lnsl
-endif
-ifeq ($(uos),sun)
-  myos = solaris
-  LDFLAGS += -ldl -lm -lsocket -lnsl
-endif
-ifeq ($(uos),haiku)
-  myos = haiku
-endif
-ifndef myos
-  $(error unknown operating system: $(uos))
-endif
-
-ifeq ($(ucpu),i386)
-  mycpu = i386
-endif
-ifeq ($(ucpu),i486)
-  mycpu = i386
-endif
-ifeq ($(ucpu),i586)
-  mycpu = i386
-endif
-ifeq ($(ucpu),i686)
-  mycpu = i386
-endif
-ifeq ($(ucpu),bepc)
-  mycpu = i386
-endif
-ifeq ($(ucpu),i86pc)
-  mycpu = i386
-endif
-ifeq ($(ucpu),amd64)
-  mycpu = amd64
-endif
-ifeq ($(ucpu),x86-64)
-  mycpu = amd64
-endif
-ifeq ($(ucpu),x86_64)
-  mycpu = amd64
-endif
-ifeq ($(ucpu),sparc)
-  mycpu = sparc
-endif
-ifeq ($(ucpu),sun)
-  mycpu = sparc
-endif
-ifeq ($(ucpu),ppc64le)
-  mycpu = powerpc64el
-endif
-ifeq ($(ucpu),ppc64)
-  mycpu = powerpc64
-  ifeq ($(myos),linux)
-    CFLAGS += -m64
-    LDFLAGS += -m64
-  endif
-endif
-ifeq ($(ucpu),powerpc)
-  mycpu = powerpc
-endif
-ifeq ($(ucpu),ppc)
-  mycpu = ppc
-endif
-ifneq (,$(filter $(ucpu), mips mips64))
-  mycpu = $(shell /bin/sh -c '"$(CC)" -dumpmachine | sed "s/-.*//"')
-  ifeq (,$(filter $(mycpu), mips mipsel mips64 mips64el))
-    $(error unknown MIPS target: $(mycpu))
-  endif
-endif
-ifeq ($(ucpu),arm)
-  mycpu = arm
-endif
-ifeq ($(ucpu),armeb)
-  mycpu = arm
-endif
-ifeq ($(ucpu),armel)
-  mycpu = arm
-endif
-ifeq ($(ucpu),armv6l)
-  mycpu = arm
-endif
-ifeq ($(ucpu),armv7l)
-  mycpu = arm
-endif
-ifeq ($(ucpu),armv7hl)
-  mycpu = arm
-endif
-ifeq ($(ucpu),aarch64)
-  mycpu = arm64
-endif
-ifndef mycpu
-  $(error unknown processor: $(ucpu))
-endif
-
-# for osA in 1..c.oses.len:
-ifeq ($(myos),?{c.oses[osA-1]})
-#   for cpuA in 1..c.cpus.len:
-  ifeq ($(mycpu),?{c.cpus[cpuA-1]})
-#     var oFiles = ""
-#     for ff in c.cfiles[osA][cpuA].items:
-#       oFiles.add(" " & changeFileExt(ff.toUnix, "o"))
-#     end for
-    oFiles =?oFiles
-  endif
-#   end for
-endif
-# end for
-
-ifeq ($(strip $(oFiles)),)
-  $(error no C code generated for: [$(myos): $(mycpu)])
-endif
-
-?{"$(binDir)/" & toLowerAscii(c.name)}: $(oFiles)
-	@mkdir -p $(binDir)
-	$(LD) -o $@ $^ $(LDFLAGS)
-	@echo "SUCCESS"
-
-.PHONY: clean
-
-clean:
-	rm -f $(oFiles) ?{"$(binDir)/" & toLowerAscii(c.name)}
diff --git a/tools/niminst/nim-file.ico b/tools/niminst/nim-file.ico
new file mode 100644
index 000000000..0685fedcb
--- /dev/null
+++ b/tools/niminst/nim-file.ico
Binary files differdiff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim
index e2cc8cf3a..40ee79814 100644
--- a/tools/niminst/niminst.nim
+++ b/tools/niminst/niminst.nim
@@ -7,15 +7,16 @@
 #    distribution, for details about the copyright.
 #
 
-const
-  haveZipLib = false # zip not in stdlib anymore
+import
+  os, strutils, parseopt, parsecfg, strtabs, streams, debcreation
 
-when haveZipLib:
-  import zipfiles
+import ../../dist/checksums/src/checksums/sha1
 
-import
-  os, osproc, strutils, parseopt, parsecfg, strtabs, streams, debcreation,
-  std / sha1
+when defined(nimPreviewSlimSystem):
+  import std/syncio
+
+when not defined(nimHasEffectsOf):
+  {.pragma: effectsOf.}
 
 const
   maxOS = 20 # max number of OSes
@@ -36,7 +37,7 @@ type
     actionInno,   # action: create Inno Setup installer
     actionNsis,   # action: create NSIS installer
     actionScripts # action: create install and deinstall scripts
-    actionZip,    # action: create zip file
+    actionZip     # action: create zip file
     actionXz,     # action: create xz file
     actionDeb     # action: prepare deb package
 
@@ -75,7 +76,7 @@ const
     "$configdir", "$datadir", "$docdir", "$libdir"
   ]
 
-proc iniConfigData(c: var ConfigData) =
+func iniConfigData(c: var ConfigData) =
   c.actions = {}
   for i in low(FileCategory)..high(FileCategory): c.cat[i] = @[]
   c.binPaths = @[]
@@ -107,17 +108,17 @@ proc iniConfigData(c: var ConfigData) =
   c.debOpts.shortDesc = ""
   c.debOpts.licenses = @[]
 
-proc firstBinPath(c: ConfigData): string =
+func firstBinPath(c: ConfigData): string =
   if c.binPaths.len > 0: result = c.binPaths[0]
   else: result = ""
 
-proc `\`(a, b: string): string =
+func `\`(a, b: string): string =
   result = if a.len == 0: b else: a & '\\' & b
 
 template toUnix(s: string): string = s.replace('\\', '/')
 template toWin(s: string): string = s.replace('/', '\\')
 
-proc skipRoot(f: string): string =
+func skipRoot(f: string): string =
   # "abc/def/xyz" --> "def/xyz"
   var i = 0
   result = ""
@@ -126,13 +127,13 @@ proc skipRoot(f: string): string =
     inc i
   if result.len == 0: result = f
 
-include "inno.tmpl"
-include "nsis.tmpl"
-include "buildsh.tmpl"
-include "makefile.tmpl"
-include "buildbat.tmpl"
-include "install.tmpl"
-include "deinstall.tmpl"
+include "inno.nimf"
+include "nsis.nimf"
+include "buildsh.nimf"
+include "makefile.nimf"
+include "buildbat.nimf"
+include "install.nimf"
+include "deinstall.nimf"
 
 # ------------------------- configuration file -------------------------------
 
@@ -167,11 +168,11 @@ proc parseCmdLine(c: var ConfigData) =
     next(p)
     var kind = p.kind
     var key = p.key
-    var val = p.val.string
+    var val = p.val
     case kind
     of cmdArgument:
       if c.actions == {}:
-        for a in split(normalize(key.string), {';', ','}):
+        for a in split(normalize(key), {';', ','}):
           case a
           of "csource": incl(c.actions, actionCSource)
           of "scripts": incl(c.actions, actionScripts)
@@ -182,11 +183,11 @@ proc parseCmdLine(c: var ConfigData) =
           of "deb": incl(c.actions, actionDeb)
           else: quit(Usage)
       else:
-        c.infile = addFileExt(key.string, "ini")
-        c.nimArgs = cmdLineRest(p).string
+        c.infile = addFileExt(key, "ini")
+        c.nimArgs = cmdLineRest(p)
         break
-    of cmdLongoption, cmdShortOption:
-      case normalize(key.string)
+    of cmdLongOption, cmdShortOption:
+      case normalize(key)
       of "help", "h":
         stdout.write(Usage)
         quit(0)
@@ -204,7 +205,7 @@ proc parseCmdLine(c: var ConfigData) =
   if c.infile.len == 0: quit(Usage)
   if c.mainfile.len == 0: c.mainfile = changeFileExt(c.infile, "nim")
 
-proc eqT(a, b: string; t: proc (a: char): char{.nimcall.}): bool =
+proc eqT(a, b: string; t: proc (a: char): char {.nimcall.}): bool {.effectsOf: t.} =
   ## equality under a transformation ``t``. candidate for the stdlib?
   var i = 0
   var j = 0
@@ -221,11 +222,11 @@ proc eqT(a, b: string; t: proc (a: char): char{.nimcall.}): bool =
       inc j
   result = i >= a.len and j >= b.len
 
-proc tPath(c: char): char =
+func tPath(c: char): char =
   if c == '\\': '/'
   else: c
 
-proc ignoreFile(f, explicit: string, allowHtml: bool): bool =
+func ignoreFile(f, explicit: string, allowHtml: bool): bool =
   let (_, name, ext) = splitFile(f)
   let html = if not allowHtml: ".html" else: ""
   result = (ext in ["", ".exe", ".idx", ".o", ".obj", ".dylib"] or
@@ -251,12 +252,12 @@ proc walkDirRecursively(s: var seq[string], root, explicit: string,
 
 proc addFiles(s: var seq[string], patterns: seq[string]) =
   for p in items(patterns):
-    if existsDir(p):
+    if dirExists(p):
       walkDirRecursively(s, p, p, false)
     else:
       var i = 0
       for f in walkPattern(p):
-        if existsDir(f):
+        if dirExists(f):
           walkDirRecursively(s, f, p, false)
         elif not ignoreFile(f, p, false):
           add(s, unixToNativePath(f))
@@ -283,13 +284,13 @@ proc yesno(p: var CfgParser, v: string): bool =
     result = false
   else: quit(errorStr(p, "unknown value; use: yes|no"))
 
-proc incl(s: var seq[string], x: string): int =
+func incl(s: var seq[string], x: string): int =
   for i in 0 ..< s.len:
     if cmpIgnoreStyle(s[i], x) == 0: return i
   s.add(x)
   result = s.len-1
 
-proc platforms(c: var ConfigData, v: string) =
+func platforms(c: var ConfigData, v: string) =
   for line in splitLines(v):
     let p = line.find(": ")
     if p <= 1: continue
@@ -462,10 +463,10 @@ proc readCFiles(c: var ConfigData, osA, cpuA: int) =
   else:
     quit("Cannot open: " & f)
 
-proc buildDir(os, cpu: int): string =
-  return "c_code" / ($os & "_" & $cpu)
+func buildDir(os, cpu: int): string =
+  "c_code" / ($os & "_" & $cpu)
 
-proc getOutputDir(c: var ConfigData): string =
+func getOutputDir(c: var ConfigData): string =
   if c.outdir.len > 0: c.outdir else: "build"
 
 proc writeFile(filename, content, newline: string) =
@@ -483,7 +484,6 @@ proc deduplicateFiles(c: var ConfigData) =
   let build = getOutputDir(c)
   for osA in countup(1, c.oses.len):
     for cpuA in countup(1, c.cpus.len):
-      if c.cfiles[osA][cpuA].isNil: c.cfiles[osA][cpuA] = @[]
       if c.explicitPlatforms and not c.platforms[osA][cpuA]: continue
       for dup in mitems(c.cfiles[osA][cpuA]):
         let key = $secureHashFile(build / dup)
@@ -504,12 +504,32 @@ proc writeInstallScripts(c: var ConfigData) =
     writeFile(deinstallShFile, generateDeinstallScript(c), "\10")
     inclFilePermissions(deinstallShFile, {fpUserExec, fpGroupExec, fpOthersExec})
 
+template gatherFiles(fun, libpath, outDir) =
+  block:
+    template copySrc(src) =
+      let dst = outDir / extractFilename(src)
+      when false: echo (dst, dst)
+      fun(src, dst)
+
+    for f in walkFiles(libpath / "lib/*.h"): copySrc(f)
+    # commenting out for now, see discussion in https://github.com/nim-lang/Nim/pull/13413
+    # copySrc(libpath / "lib/wrappers/linenoise/linenoise.h")
+
+proc exe(f: string): string =
+  result = addFileExt(f, ExeExt)
+  when defined(windows):
+    result = result.replace('/','\\')
+
+proc findNim(): string =
+  let nim = "nim".exe
+  result = quoteShell("bin" / nim)
+  if not fileExists(result):
+    result = "nim"
+
 proc srcdist(c: var ConfigData) =
-  if not existsDir(getOutputDir(c) / "c_code"):
-    createDir(getOutputDir(c) / "c_code")
-  for x in walkFiles(c.libpath / "lib/*.h"):
-    when false: echo(getOutputDir(c) / "c_code" / extractFilename(x))
-    copyFile(dest=getOutputDir(c) / "c_code" / extractFilename(x), source=x)
+  let cCodeDir = getOutputDir(c) / "c_code"
+  if not dirExists(cCodeDir): createDir(cCodeDir)
+  gatherFiles(copyFile, c.libpath, cCodeDir)
   var winIndex = -1
   var intel32Index = -1
   var intel64Index = -1
@@ -522,12 +542,12 @@ proc srcdist(c: var ConfigData) =
       if cpuname.cmpIgnoreStyle("i386") == 0: intel32Index = cpuA
       elif cpuname.cmpIgnoreStyle("amd64") == 0: intel64Index = cpuA
       var dir = getOutputDir(c) / buildDir(osA, cpuA)
-      if existsDir(dir): removeDir(dir)
+      if dirExists(dir): removeDir(dir)
       createDir(dir)
-      var cmd = ("nim compile -f --symbolfiles:off --compileonly " &
+      var cmd = ("$# compile -f --incremental:off --compileonly " &
                  "--gen_mapping --cc:gcc --skipUserCfg" &
                  " --os:$# --cpu:$# $# $#") %
-                 [osname, cpuname, c.nimArgs, c.mainfile]
+                 [findNim(), osname, cpuname, c.nimArgs, c.mainfile]
       echo(cmd)
       if execShellCmd(cmd) != 0:
         quit("Error: call to nim compiler failed")
@@ -586,41 +606,6 @@ proc setupDist2(c: var ConfigData) =
     else:
       quit("External program failed")
 
-# ------------------ generate ZIP file ---------------------------------------
-when haveZipLib:
-  proc zipDist(c: var ConfigData) =
-    var proj = toLowerAscii(c.name) & "-" & c.version
-    var n = "$#.zip" % proj
-    if c.outdir.len == 0: n = "build" / n
-    else: n = c.outdir / n
-    var z: ZipArchive
-    if open(z, n, fmWrite):
-      addFile(z, proj / buildBatFile, "build" / buildBatFile)
-      addFile(z, proj / buildBatFile32, "build" / buildBatFile32)
-      addFile(z, proj / buildBatFile64, "build" / buildBatFile64)
-      addFile(z, proj / buildShFile, "build" / buildShFile)
-      addFile(z, proj / makeFile, "build" / makeFile)
-      addFile(z, proj / installShFile, installShFile)
-      addFile(z, proj / deinstallShFile, deinstallShFile)
-      for f in walkFiles(c.libpath / "lib/*.h"):
-        addFile(z, proj / "c_code" / extractFilename(f), f)
-      for osA in 1..c.oses.len:
-        for cpuA in 1..c.cpus.len:
-          var dir = buildDir(osA, cpuA)
-          for k, f in walkDir("build" / dir):
-            if k == pcFile: addFile(z, proj / dir / extractFilename(f), f)
-
-      for cat in items({fcConfig..fcOther, fcUnix, fcNimble}):
-        for f in items(c.cat[cat]): addFile(z, proj / f, f)
-
-      # Copy the .nimble file over
-      let nimbleFile = c.nimblePkgName & ".nimble"
-      processFile(z, proj / nimbleFile, nimbleFile)
-
-      close(z)
-    else:
-      quit("Cannot open for writing: " & n)
-
 proc xzDist(c: var ConfigData; windowsZip=false) =
   let proj = toLowerAscii(c.name) & "-" & c.version
   let tmpDir = if c.outdir.len == 0: "build" else: c.outdir
@@ -628,15 +613,15 @@ proc xzDist(c: var ConfigData; windowsZip=false) =
   proc processFile(destFile, src: string) =
     let dest = tmpDir / destFile
     when false: echo "Copying ", src, " to ", dest
-    if not existsFile(src):
+    if not fileExists(src):
       echo "[Warning] Source file doesn't exist: ", src
     let destDir = dest.splitFile.dir
     if not dirExists(destDir): createDir(destDir)
     copyFileWithPermissions(src, dest)
 
-  if not windowsZip and not existsFile("build" / buildBatFile):
+  if not windowsZip and not fileExists("build" / buildBatFile):
     quit("No C sources found in ./build/, please build by running " &
-         "./koch csource -d:release.")
+         "./koch csource -d:danger.")
 
   if not windowsZip:
     processFile(proj / buildBatFile, "build" / buildBatFile)
@@ -646,8 +631,8 @@ proc xzDist(c: var ConfigData; windowsZip=false) =
     processFile(proj / makeFile, "build" / makeFile)
     processFile(proj / installShFile, installShFile)
     processFile(proj / deinstallShFile, deinstallShFile)
-    for f in walkFiles(c.libpath / "lib/*.h"):
-      processFile(proj / "c_code" / extractFilename(f), f)
+    template processFileAux(src, dst) = processFile(dst, src)
+    gatherFiles(processFileAux, c.libpath, proj / "c_code")
     for osA in 1..c.oses.len:
       for cpuA in 1..c.cpus.len:
         var dir = buildDir(osA, cpuA)
@@ -683,7 +668,7 @@ RunProgram="tools\downloader.exe"
           if execShellCmd("7z a -sfx7zS2.sfx -t7z $1.exe $1" % proj) != 0:
             echo("External program failed (7z)")
       else:
-        if execShellCmd("gtar cf $1.tar $1 --exclude=.DS_Store" %
+        if execShellCmd("gtar cf $1.tar --exclude=.DS_Store $1" %
                         proj) != 0:
           # try old 'tar' without --exclude feature:
           if execShellCmd("tar cf $1.tar $1" % proj) != 0:
@@ -697,8 +682,8 @@ RunProgram="tools\downloader.exe"
 # -- prepare build files for .deb creation
 
 proc debDist(c: var ConfigData) =
-  if not existsFile(getOutputDir(c) / "build.sh"): quit("No build.sh found.")
-  if not existsFile(getOutputDir(c) / "install.sh"): quit("No install.sh found.")
+  if not fileExists(getOutputDir(c) / "build.sh"): quit("No build.sh found.")
+  if not fileExists(getOutputDir(c) / "install.sh"): quit("No install.sh found.")
 
   if c.debOpts.shortDesc == "": quit("shortDesc must be set in the .ini file.")
   if c.debOpts.licenses.len == 0:
@@ -721,8 +706,7 @@ proc debDist(c: var ConfigData) =
   copyNimDist(makeFile, makeFile)
   copyNimDist(installShFile, installShFile)
   createDir(workingDir / upstreamSource / "build")
-  for f in walkFiles(c.libpath / "lib/*.h"):
-    copyNimDist(f, "build" / extractFilename(f))
+  gatherFiles(copyNimDist, c.libpath, "build")
   for osA in 1..c.oses.len:
     for cpuA in 1..c.cpus.len:
       var dir = buildDir(osA, cpuA)
@@ -743,21 +727,25 @@ proc debDist(c: var ConfigData) =
 
 # ------------------- main ----------------------------------------------------
 
-var c: ConfigData
-iniConfigData(c)
-parseCmdLine(c)
-parseIniFile(c)
-if actionInno in c.actions:
-  setupDist(c)
-if actionNsis in c.actions:
-  setupDist2(c)
-if actionCSource in c.actions:
-  srcdist(c)
-if actionScripts in c.actions:
-  writeInstallScripts(c)
-if actionZip in c.actions:
-  xzDist(c, true)
-if actionXz in c.actions:
-  xzDist(c)
-if actionDeb in c.actions:
-  debDist(c)
+proc main() =
+  var c: ConfigData
+  iniConfigData(c)
+  parseCmdLine(c)
+  parseIniFile(c)
+  if actionInno in c.actions:
+    setupDist(c)
+  if actionNsis in c.actions:
+    setupDist2(c)
+  if actionCSource in c.actions:
+    srcdist(c)
+  if actionScripts in c.actions:
+    writeInstallScripts(c)
+  if actionZip in c.actions:
+    xzDist(c, true)
+  if actionXz in c.actions:
+    xzDist(c)
+  if actionDeb in c.actions:
+    debDist(c)
+
+when isMainModule:
+  main()
diff --git a/tools/niminst/nsis.tmpl b/tools/niminst/nsis.nimf
index f4eb1d0cd..f4eb1d0cd 100644
--- a/tools/niminst/nsis.tmpl
+++ b/tools/niminst/nsis.nimf
diff --git a/tools/niminst/setup.ico b/tools/niminst/setup.ico
new file mode 100644
index 000000000..867163046
--- /dev/null
+++ b/tools/niminst/setup.ico
Binary files differdiff --git a/tools/niminst/uninstall.ico b/tools/niminst/uninstall.ico
new file mode 100644
index 000000000..aff054644
--- /dev/null
+++ b/tools/niminst/uninstall.ico
Binary files differdiff --git a/tools/nimrepl.nim b/tools/nimrepl.nim
deleted file mode 100644
index ac82d8b75..000000000
--- a/tools/nimrepl.nim
+++ /dev/null
@@ -1,172 +0,0 @@
-#
-#
-#              Nim REPL
-#        (c) Copyright 2012 Dominik Picheta
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-import glib2, gtk2, gdk2, os, osproc, dialogs, strutils
-
-when defined(tinyc):
-  const runCmd = "run"
-else:
-  const runCmd = "c -r"
-
-var nimExe = findExe("nim")
-if nimExe.len == 0: nimExe = "../bin" / addFileExt("nim", os.ExeExt)
-
-proc execCode(code: string): string =
-  var f: File
-  if open(f, "temp.nim", fmWrite):
-    f.write(code)
-    f.close()
-    result = osproc.execProcess(
-      "$# $# --verbosity:0 --hint[Conf]:off temp.nim" % [nimExe, runCmd],
-      options = {poStdErrToStdOut})
-  else:
-    result = "cannot open file 'temp.nim'"
-
-var shiftPressed = false
-var w: gtk2.PWindow
-var inputTextBuffer: PTextBuffer
-var outputTextBuffer: PTextBuffer
-
-proc destroy(widget: PWidget, data: pointer){.cdecl.} =
-  main_quit()
-
-proc fileOpenClicked(menuitem: PMenuItem, userdata: pointer) {.cdecl.} =
-  var path = chooseFileToOpen(w)
-  if path != "":
-    var file = readFile(path)
-    if file != nil:
-      set_text(inputTextBuffer, file, len(file).gint)
-    else:
-      error(w, "Unable to read from file")
-
-proc fileSaveClicked(menuitem: PMenuItem, userdata: pointer) {.cdecl.} =
-  var path = chooseFileToSave(w)
-
-  if path == "": return
-  var startIter: TTextIter
-  var endIter: TTextIter
-  get_start_iter(inputTextBuffer, addr(startIter))
-  get_end_iter(inputTextBuffer, addr(endIter))
-  var inputText = get_text(inputTextBuffer, addr(startIter),
-                           addr(endIter), false)
-  var f: File
-  if open(f, path, fmWrite):
-    f.write(inputText)
-    f.close()
-  else:
-    error(w, "Unable to write to file")
-
-proc inputKeyPressed(widget: PWidget, event: PEventKey,
-                     userdata: pointer): bool {.cdecl.} =
-  if ($keyval_name(event.keyval)).tolower() == "shift_l":
-    # SHIFT is pressed
-    shiftPressed = true
-
-proc setError(msg: string) =
-  outputTextBuffer.setText(msg, msg.len.gint)
-
-proc inputKeyReleased(widget: PWidget, event: PEventKey,
-                      userdata: pointer): bool {.cdecl.} =
-  #echo(keyval_name(event.keyval))
-  if ($keyval_name(event.keyval)).tolower() == "shift_l":
-    # SHIFT is released
-    shiftPressed = false
-
-  if ($keyval_name(event.keyval)).tolower() == "return":
-    #echo($keyval_name(event.keyval), "Shift_L")
-    # Enter pressed
-    if not shiftPressed:
-      var startIter: TTextIter
-      var endIter: TTextIter
-      get_start_iter(inputTextBuffer, addr(startIter))
-      get_end_iter(inputTextBuffer, addr(endIter))
-      var inputText = get_text(inputTextBuffer, addr(startIter),
-                               addr(endIter), false)
-
-      try:
-        var r = execCode($inputText)
-        set_text(outputTextBuffer, r, len(r).gint)
-      except IOError:
-        setError("Error: Could not open file temp.nim")
-
-
-proc initControls() =
-  w = window_new(gtk2.WINDOW_TOPLEVEL)
-  set_default_size(w, 500, 600)
-  set_title(w, "Nim REPL")
-  discard signal_connect(w, "destroy", SIGNAL_FUNC(nimrepl.destroy), nil)
-
-  # MainBox (vbox)
-  var mainBox = vbox_new(false, 0)
-  add(w, mainBox)
-
-  # TopMenu (MenuBar)
-  var topMenu = menu_bar_new()
-  show(topMenu)
-
-  var fileMenu = menu_new()
-  var openMenuItem = menu_item_new("Open")
-  append(fileMenu, openMenuItem)
-  show(openMenuItem)
-  discard signal_connect(openMenuItem, "activate",
-                          SIGNAL_FUNC(fileOpenClicked), nil)
-  var saveMenuItem = menu_item_new("Save...")
-  append(fileMenu, saveMenuItem)
-  show(saveMenuItem)
-  discard signal_connect(saveMenuItem, "activate",
-                          SIGNAL_FUNC(fileSaveClicked), nil)
-  var fileMenuItem = menu_item_new("File")
-
-
-  set_submenu(fileMenuItem, fileMenu)
-  show(fileMenuItem)
-  append(topMenu, fileMenuItem)
-
-  pack_start(mainBox, topMenu, false, false, 0)
-
-  # VPaned - Separates the InputTextView and the OutputTextView
-  var paned = vpaned_new()
-  set_position(paned, 450)
-  pack_start(mainBox, paned, true, true, 0)
-  show(paned)
-
-  # Init the TextBuffers
-  inputTextBuffer = text_buffer_new(nil)
-  outputTextBuffer = text_buffer_new(nil)
-
-  # InputTextView (TextView)
-  var inputScrolledWindow = scrolled_window_new(nil, nil)
-  set_policy(inputScrolledWindow, POLICY_AUTOMATIC, POLICY_AUTOMATIC)
-  var inputTextView = text_view_new(inputTextBuffer)
-  add_with_viewport(inputScrolledWindow, inputTextView)
-  add1(paned, inputScrolledWindow)
-  show(inputScrolledWindow)
-  show(inputTextView)
-
-  discard signal_connect(inputTextView, "key-release-event",
-                          SIGNAL_FUNC(inputKeyReleased), nil)
-  discard signal_connect(inputTextView, "key-press-event",
-                          SIGNAL_FUNC(inputKeyPressed), nil)
-
-  # OutputTextView (TextView)
-  var outputScrolledWindow = scrolled_window_new(nil, nil)
-  set_policy(outputScrolledWindow, POLICY_AUTOMATIC, POLICY_AUTOMATIC)
-  var outputTextView = text_view_new(outputTextBuffer)
-  add_with_viewport(outputScrolledWindow, outputTextView)
-  add2(paned, outputScrolledWindow)
-  show(outputScrolledWindow)
-  show(outputTextView)
-
-  show(w)
-  show(mainBox)
-
-nim_init()
-initControls()
-main()
-
diff --git a/tools/nimweb.nim b/tools/nimweb.nim
deleted file mode 100644
index 6e1d9d359..000000000
--- a/tools/nimweb.nim
+++ /dev/null
@@ -1,549 +0,0 @@
-#
-#
-#           Nim Website Generator
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-import
-  os, strutils, times, parseopt, parsecfg, streams, strtabs, tables,
-  re, htmlgen, macros, md5, osproc, parsecsv, algorithm
-
-from xmltree import escape
-
-const gitRepo = "https://github.com/nim-lang/Nim"
-
-type
-  TKeyValPair = tuple[key, id, val: string]
-  TConfigData = object of RootObj
-    tabs, links: seq[TKeyValPair]
-    doc, srcdoc, srcdoc2, webdoc, pdf: seq[string]
-    authors, projectName, projectTitle, logo, infile, outdir, ticker: string
-    vars: StringTableRef
-    nimArgs: string
-    quotations: Table[string, tuple[quote, author: string]]
-    numProcessors: int # Set by parallelBuild:n, only works for values > 0.
-    gaId: string  # google analytics ID, nil means analytics are disabled
-  TRssItem = object
-    year, month, day, title, url, content: string
-  TAction = enum
-    actAll, actOnlyWebsite, actPdf, actJson2, actOnlyDocs
-
-  Sponsor = object
-    logo: string
-    name: string
-    url: string
-    thisMonth: int
-    allTime: int
-    since: string
-    level: int
-
-var action: TAction
-
-proc initConfigData(c: var TConfigData) =
-  c.tabs = @[]
-  c.links = @[]
-  c.doc = @[]
-  c.srcdoc = @[]
-  c.srcdoc2 = @[]
-  c.webdoc = @[]
-  c.pdf = @[]
-  c.infile = ""
-  c.outdir = ""
-  c.nimArgs = "--hint[Conf]:off --hint[Path]:off --hint[Processing]:off -d:boot "
-  c.authors = ""
-  c.projectTitle = ""
-  c.projectName = ""
-  c.logo = ""
-  c.ticker = ""
-  c.vars = newStringTable(modeStyleInsensitive)
-  c.numProcessors = countProcessors()
-  # Attempts to obtain the git current commit.
-  when false:
-    let (output, code) = execCmdEx("git log -n 1 --format=%H")
-    if code == 0 and output.strip.len == 40:
-      c.gitCommit = output.strip
-  c.quotations = initTable[string, tuple[quote, author: string]]()
-
-include "website.tmpl"
-
-# ------------------------- configuration file -------------------------------
-
-const
-  version = "0.8"
-  usage = "nimweb - Nim Website Generator Version " & version & """
-
-  (c) 2015 Andreas Rumpf
-Usage:
-  nimweb [options] ini-file[.ini] [compile_options]
-Options:
-  -o, --output:dir    set the output directory (default: same as ini-file)
-  --var:name=value    set the value of a variable
-  -h, --help          shows this help
-  -v, --version       shows the version
-  --website           only build the website, not the full documentation
-  --pdf               build the PDF version of the documentation
-  --json2             build JSON of the documentation
-  --onlyDocs          build only the documentation
-Compile_options:
-  will be passed to the Nim compiler
-"""
-
-  rYearMonthDay = r"on\s+(\d{2})\/(\d{2})\/(\d{4})"
-  rssUrl = "http://nim-lang.org/news.xml"
-  rssNewsUrl = "http://nim-lang.org/news.html"
-  activeSponsors = "web/sponsors.csv"
-  inactiveSponsors = "web/inactive_sponsors.csv"
-  validAnchorCharacters = Letters + Digits
-
-
-macro id(e: untyped): untyped =
-  ## generates the rss xml ``id`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "id")
-
-macro updated(e: varargs[untyped]): untyped =
-  ## generates the rss xml ``updated`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "updated")
-
-proc updatedDate(year, month, day: string): string =
-  ## wrapper around the update macro with easy input.
-  result = updated("$1-$2-$3T00:00:00Z" % [year,
-    repeat("0", 2 - len(month)) & month,
-    repeat("0", 2 - len(day)) & day])
-
-macro entry(e: varargs[untyped]): untyped =
-  ## generates the rss xml ``entry`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "entry")
-
-macro content(e: varargs[untyped]): untyped =
-  ## generates the rss xml ``content`` element.
-  let e = callsite()
-  result = xmlCheckedTag(e, "content", reqAttr = "type")
-
-proc parseCmdLine(c: var TConfigData) =
-  var p = initOptParser()
-  while true:
-    next(p)
-    var kind = p.kind
-    var key = p.key
-    var val = p.val
-    case kind
-    of cmdArgument:
-      c.infile = addFileExt(key, "ini")
-      c.nimArgs.add(cmdLineRest(p))
-      break
-    of cmdLongOption, cmdShortOption:
-      case normalize(key)
-      of "help", "h":
-        stdout.write(usage)
-        quit(0)
-      of "version", "v":
-        stdout.write(version & "\n")
-        quit(0)
-      of "o", "output": c.outdir = val
-      of "parallelbuild":
-        try:
-          let num = parseInt(val)
-          if num != 0: c.numProcessors = num
-        except ValueError:
-          quit("invalid numeric value for --parallelBuild")
-      of "var":
-        var idx = val.find('=')
-        if idx < 0: quit("invalid command line")
-        c.vars[substr(val, 0, idx-1)] = substr(val, idx+1)
-      of "website": action = actOnlyWebsite
-      of "pdf": action = actPdf
-      of "json2": action = actJson2
-      of "onlydocs": action = actOnlyDocs
-      of "googleanalytics":
-        c.gaId = val
-        c.nimArgs.add("--doc.googleAnalytics:" & val & " ")
-      else:
-        echo("Invalid argument $1" % [key])
-        quit(usage)
-    of cmdEnd: break
-  if c.infile.len == 0: quit(usage)
-
-proc walkDirRecursively(s: var seq[string], root, ext: string) =
-  for k, f in walkDir(root):
-    case k
-    of pcFile, pcLinkToFile:
-      if cmpIgnoreCase(ext, splitFile(f).ext) == 0:
-        add(s, f)
-    of pcDir: walkDirRecursively(s, f, ext)
-    of pcLinkToDir: discard
-
-proc addFiles(s: var seq[string], dir, ext: string, patterns: seq[string]) =
-  for p in items(patterns):
-    if existsFile(dir / addFileExt(p, ext)):
-      s.add(dir / addFileExt(p, ext))
-    if existsDir(dir / p):
-      walkDirRecursively(s, dir / p, ext)
-
-proc parseIniFile(c: var TConfigData) =
-  var
-    p: CfgParser
-    section: string # current section
-  var input = newFileStream(c.infile, fmRead)
-  if input == nil: quit("cannot open: " & c.infile)
-  open(p, input, c.infile)
-  while true:
-    var k = next(p)
-    case k.kind
-    of cfgEof: break
-    of cfgSectionStart:
-      section = normalize(k.section)
-      case section
-      of "project", "links", "tabs", "ticker", "documentation", "var": discard
-      else: echo("[Warning] Skipping unknown section: " & section)
-
-    of cfgKeyValuePair:
-      var v = k.value % c.vars
-      c.vars[k.key] = v
-
-      case section
-      of "project":
-        case normalize(k.key)
-        of "name": c.projectName = v
-        of "title": c.projectTitle = v
-        of "logo": c.logo = v
-        of "authors": c.authors = v
-        else: quit(errorStr(p, "unknown variable: " & k.key))
-      of "var": discard
-      of "links":
-        let valID = v.split(';')
-        add(c.links, (k.key.replace('_', ' '), valID[1], valID[0]))
-      of "tabs": add(c.tabs, (k.key, "", v))
-      of "ticker": c.ticker = v
-      of "documentation":
-        case normalize(k.key)
-        of "doc": addFiles(c.doc, "doc", ".rst", split(v, {';'}))
-        of "pdf": addFiles(c.pdf, "doc", ".rst", split(v, {';'}))
-        of "srcdoc": addFiles(c.srcdoc, "lib", ".nim", split(v, {';'}))
-        of "srcdoc2": addFiles(c.srcdoc2, "lib", ".nim", split(v, {';'}))
-        of "webdoc": addFiles(c.webdoc, "lib", ".nim", split(v, {';'}))
-        of "parallelbuild":
-          try:
-            let num = parseInt(v)
-            if num != 0: c.numProcessors = num
-          except ValueError:
-            quit("invalid numeric value for --parallelBuild in config")
-        else: quit(errorStr(p, "unknown variable: " & k.key))
-      of "quotations":
-        let vSplit = v.split('-')
-        doAssert vSplit.len == 2
-        c.quotations[k.key.normalize] = (vSplit[0], vSplit[1])
-      else: discard
-    of cfgOption: quit(errorStr(p, "syntax error"))
-    of cfgError: quit(errorStr(p, k.msg))
-  close(p)
-  if c.projectName.len == 0:
-    c.projectName = changeFileExt(extractFilename(c.infile), "")
-  if c.outdir.len == 0:
-    c.outdir = splitFile(c.infile).dir
-
-# ------------------- main ----------------------------------------------------
-
-
-proc exe(f: string): string = return addFileExt(f, ExeExt)
-
-proc findNim(): string =
-  var nim = "nim".exe
-  result = "bin" / nim
-  if existsFile(result): return
-  for dir in split(getEnv("PATH"), PathSep):
-    if existsFile(dir / nim): return dir / nim
-  # assume there is a symlink to the exe or something:
-  return nim
-
-proc exec(cmd: string) =
-  echo(cmd)
-  let (outp, exitCode) = osproc.execCmdEx(cmd)
-  if exitCode != 0: quit outp
-
-proc sexec(cmds: openarray[string]) =
-  ## Serial queue wrapper around exec.
-  for cmd in cmds: exec(cmd)
-
-proc mexec(cmds: openarray[string], processors: int) =
-  ## Multiprocessor version of exec
-  doAssert processors > 0, "nimweb needs at least one processor"
-  if processors == 1:
-    sexec(cmds)
-    return
-  let r = execProcesses(cmds, {poStdErrToStdOut, poParentStreams, poEchoCmd},
-    n = processors)
-  if r != 0:
-    echo "external program failed, retrying serial work queue for logs!"
-    sexec(cmds)
-
-proc buildDocSamples(c: var TConfigData, destPath: string) =
-  ## Special case documentation sample proc.
-  ##
-  ## The docgen sample needs to be generated twice with different commands, so
-  ## it didn't make much sense to integrate into the existing generic
-  ## documentation builders.
-  const src = "doc"/"docgen_sample.nim"
-  exec(findNim() & " doc $# -o:$# $#" %
-    [c.nimArgs, destPath / "docgen_sample.html", src])
-  exec(findNim() & " doc2 $# -o:$# $#" %
-    [c.nimArgs, destPath / "docgen_sample2.html", src])
-
-proc pathPart(d: string): string = splitFile(d).dir.replace('\\', '/')
-
-proc buildDoc(c: var TConfigData, destPath: string) =
-  # call nim for the documentation:
-  var
-    commands = newSeq[string](len(c.doc) + len(c.srcdoc) + len(c.srcdoc2))
-    i = 0
-  for d in items(c.doc):
-    commands[i] = findNim() & " rst2html $# --git.url:$# -o:$# --index:on $#" %
-      [c.nimArgs, gitRepo,
-      destPath / changeFileExt(splitFile(d).name, "html"), d]
-    i.inc
-  for d in items(c.srcdoc):
-    commands[i] = findNim() & " doc0 $# --git.url:$# -o:$# --index:on $#" %
-      [c.nimArgs, gitRepo,
-      destPath / changeFileExt(splitFile(d).name, "html"), d]
-    i.inc
-  for d in items(c.srcdoc2):
-    commands[i] = findNim() & " doc2 $# --git.url:$# -o:$# --index:on $#" %
-      [c.nimArgs, gitRepo,
-      destPath / changeFileExt(splitFile(d).name, "html"), d]
-    i.inc
-
-  mexec(commands, c.numProcessors)
-  exec(findNim() & " buildIndex -o:$1/theindex.html $1" % [destPath])
-
-proc buildPdfDoc(c: var TConfigData, destPath: string) =
-  createDir(destPath)
-  if os.execShellCmd("pdflatex -version") != 0:
-    echo "pdflatex not found; no PDF documentation generated"
-  else:
-    const pdflatexcmd = "pdflatex -interaction=nonstopmode "
-    for d in items(c.pdf):
-      exec(findNim() & " rst2tex $# $#" % [c.nimArgs, d])
-      # call LaTeX twice to get cross references right:
-      exec(pdflatexcmd & changeFileExt(d, "tex"))
-      exec(pdflatexcmd & changeFileExt(d, "tex"))
-      # delete all the crappy temporary files:
-      let pdf = splitFile(d).name & ".pdf"
-      let dest = destPath / pdf
-      removeFile(dest)
-      moveFile(dest=dest, source=pdf)
-      removeFile(changeFileExt(pdf, "aux"))
-      if existsFile(changeFileExt(pdf, "toc")):
-        removeFile(changeFileExt(pdf, "toc"))
-      removeFile(changeFileExt(pdf, "log"))
-      removeFile(changeFileExt(pdf, "out"))
-      removeFile(changeFileExt(d, "tex"))
-
-proc buildAddDoc(c: var TConfigData, destPath: string) =
-  # build additional documentation (without the index):
-  var commands = newSeq[string](c.webdoc.len)
-  for i, doc in pairs(c.webdoc):
-    commands[i] = findNim() & " doc2 $# --git.url:$# -o:$# $#" %
-      [c.nimArgs, gitRepo,
-      destPath / changeFileExt(splitFile(doc).name, "html"), doc]
-  mexec(commands, c.numProcessors)
-
-proc parseNewsTitles(inputFilename: string): seq[TRssItem] =
-  # Goes through each news file, returns its date/title.
-  result = @[]
-  var matches: array[3, string]
-  let reYearMonthDay = re(rYearMonthDay)
-  for kind, path in walkDir(inputFilename):
-    let (dir, name, ext) = path.splitFile
-    if ext == ".rst":
-      let content = readFile(path)
-      let title = content.splitLines()[0]
-      let urlPath = "news/" & name & ".html"
-      if content.find(reYearMonthDay, matches) >= 0:
-        result.add(TRssItem(year: matches[2], month: matches[1], day: matches[0],
-          title: title, url: "http://nim-lang.org/" & urlPath,
-          content: content))
-  result.reverse()
-
-proc genUUID(text: string): string =
-  # Returns a valid RSS uuid, which is basically md5 with dashes and a prefix.
-  result = getMD5(text)
-  result.insert("-", 20)
-  result.insert("-", 16)
-  result.insert("-", 12)
-  result.insert("-", 8)
-  result.insert("urn:uuid:")
-
-proc genNewsLink(title: string): string =
-  # Mangles a title string into an expected news.html anchor.
-  result = title
-  result.insert("Z")
-  for i in 1..len(result)-1:
-    let letter = result[i].toLowerAscii()
-    if letter in validAnchorCharacters:
-      result[i] = letter
-    else:
-      result[i] = '-'
-  result.insert(rssNewsUrl & "#")
-
-proc generateRss(outputFilename: string, news: seq[TRssItem]) =
-  # Given a list of rss items generates an rss overwriting destination.
-  var
-    output: File
-
-  if not open(output, outputFilename, mode = fmWrite):
-    quit("Could not write to $1 for rss generation" % [outputFilename])
-  defer: output.close()
-
-  output.write("""<?xml version="1.0" encoding="utf-8"?>
-<feed xmlns="http://www.w3.org/2005/Atom">
-""")
-  output.write(title("Nim website news"))
-  output.write(link(href = rssUrl, rel = "self"))
-  output.write(link(href = rssNewsUrl))
-  output.write(id(rssNewsUrl))
-
-  let now = getGMTime(getTime())
-  output.write(updatedDate($now.year, $(int(now.month) + 1), $now.monthday))
-
-  for rss in news:
-    output.write(entry(
-        title(xmltree.escape(rss.title)),
-        id(genUUID(rss.title)),
-        link(`type` = "text/html", rel = "alternate",
-          href = rss.url),
-        updatedDate(rss.year, rss.month, rss.day),
-        "<author><name>Nim</name></author>",
-        content(xmltree.escape(rss.content), `type` = "text")
-      ))
-
-  output.write("""</feed>""")
-
-proc buildNewsRss(c: var TConfigData, destPath: string) =
-  # generates an xml feed from the web/news.rst file
-  let
-    srcFilename = "web" / "news"
-    destFilename = destPath / changeFileExt(splitFile(srcFilename).name, "xml")
-
-  generateRss(destFilename, parseNewsTitles(srcFilename))
-
-proc buildJS(destPath: string) =
-  exec(findNim() & " js -d:release --out:$1 web/nimblepkglist.nim" %
-      [destPath / "nimblepkglist.js"])
-
-proc readSponsors(sponsorsFile: string): seq[Sponsor] =
-  result = @[]
-  var fileStream = newFileStream(sponsorsFile, fmRead)
-  if fileStream == nil: quit("Cannot open sponsors.csv file: " & sponsorsFile)
-  var parser: CsvParser
-  open(parser, fileStream, sponsorsFile)
-  discard readRow(parser) # Skip the header row.
-  while readRow(parser):
-    result.add(Sponsor(logo: parser.row[0], name: parser.row[1],
-        url: parser.row[2], thisMonth: parser.row[3].parseInt,
-        allTime: parser.row[4].parseInt,
-        since: parser.row[5], level: parser.row[6].parseInt))
-  parser.close()
-
-proc buildSponsors(c: var TConfigData, outputDir: string) =
-  let sponsors = generateSponsorsPage(readSponsors(activeSponsors),
-                                      readSponsors(inactiveSponsors))
-  let outFile = outputDir / "sponsors.html"
-  var f: File
-  if open(f, outFile, fmWrite):
-    writeLine(f, generateHtmlPage(c, "", "Our Sponsors", sponsors, ""))
-    close(f)
-  else:
-    quit("[Error] Cannot write file: " & outFile)
-
-const
-  cmdRst2Html = " rst2html --compileonly $1 -o:web/$2.temp web/$2.rst"
-
-proc buildPage(c: var TConfigData, file, title, rss: string, assetDir = "") =
-  exec(findNim() & cmdRst2Html % [c.nimArgs, file])
-  var temp = "web" / changeFileExt(file, "temp")
-  var content: string
-  try:
-    content = readFile(temp)
-  except IOError:
-    quit("[Error] cannot open: " & temp)
-  var f: File
-  var outfile = "web/upload/$#.html" % file
-  if not existsDir(outfile.splitFile.dir):
-    createDir(outfile.splitFile.dir)
-  if open(f, outfile, fmWrite):
-    writeLine(f, generateHTMLPage(c, file, title, content, rss, assetDir))
-    close(f)
-  else:
-    quit("[Error] cannot write file: " & outfile)
-  removeFile(temp)
-
-proc buildNews(c: var TConfigData, newsDir: string, outputDir: string) =
-  for kind, path in walkDir(newsDir):
-    let (dir, name, ext) = path.splitFile
-    if ext == ".rst":
-      let title = readFile(path).splitLines()[0]
-      buildPage(c, tailDir(dir) / name, title, "", "../")
-    else:
-      echo("Skipping file in news directory: ", path)
-
-proc buildWebsite(c: var TConfigData) =
-  if c.ticker.len > 0:
-    try:
-      c.ticker = readFile("web" / c.ticker)
-    except IOError:
-      quit("[Error] cannot open: " & c.ticker)
-  for i in 0..c.tabs.len-1:
-    var file = c.tabs[i].val
-    let rss = if file in ["news", "index"]: extractFilename(rssUrl) else: ""
-    if '.' in file: continue
-    buildPage(c, file, if file == "question": "FAQ" else: file, rss)
-  copyDir("web/assets", "web/upload/assets")
-  buildNewsRss(c, "web/upload")
-  buildSponsors(c, "web/upload")
-  buildNews(c, "web/news", "web/upload/news")
-
-proc main(c: var TConfigData) =
-  buildWebsite(c)
-  buildJS("web/upload")
-  const docup = "web/upload/" & NimVersion
-  createDir(docup)
-  buildAddDoc(c, docup)
-  buildDocSamples(c, docup)
-  buildDoc(c, docup)
-  createDir("doc/html")
-  buildDocSamples(c, "doc/html")
-  buildDoc(c, "doc/html")
-
-proc onlyDocs(c: var TConfigData) =
-  createDir("doc/html")
-  buildDocSamples(c, "doc/html")
-  buildDoc(c, "doc/html")
-
-proc json2(c: var TConfigData) =
-  const destPath = "web/json2"
-  var commands = newSeq[string](c.srcdoc2.len)
-  var i = 0
-  for d in items(c.srcdoc2):
-    createDir(destPath / splitFile(d).dir)
-    commands[i] = findNim() & " jsondoc2 $# --git.url:$# -o:$# --index:on $#" %
-      [c.nimArgs, gitRepo,
-      destPath / changeFileExt(d, "json"), d]
-    i.inc
-
-  mexec(commands, c.numProcessors)
-
-var c: TConfigData
-initConfigData(c)
-parseCmdLine(c)
-parseIniFile(c)
-case action
-of actOnlyWebsite: buildWebsite(c)
-of actPdf: buildPdfDoc(c, "doc/pdf")
-of actOnlyDocs: onlyDocs(c)
-of actAll: main(c)
-of actJson2: json2(c)
diff --git a/tools/officialpackages.nim b/tools/officialpackages.nim
new file mode 100644
index 000000000..633944a14
--- /dev/null
+++ b/tools/officialpackages.nim
@@ -0,0 +1,21 @@
+import std/[strformat, paths, dirs, envvars]
+from std/os import execShellCmd
+
+proc exec*(cmd: string, errorcode: int = QuitFailure, additionalPath = "") =
+  let prevPath = getEnv("PATH")
+  if additionalPath.len > 0:
+    var absolute = Path(additionalPath)
+    if not absolute.isAbsolute:
+      absolute = getCurrentDir() / absolute
+    echo("Adding to $PATH: ", string(absolute))
+    putEnv("PATH", (if prevPath.len > 0: prevPath & PathSep else: "") & string(absolute))
+  echo(cmd)
+  if execShellCmd(cmd) != 0: quit("FAILURE", errorcode)
+  putEnv("PATH", prevPath)
+
+proc gitClonePackages*(names: seq[string]) =
+  if not dirExists(Path"pkgs"):
+    createDir(Path"pkgs")
+  for name in names:
+    if not dirExists(Path"pkgs" / Path(name)):
+      exec fmt"git clone https://github.com/nim-lang/{name} pkgs/{name}"
diff --git a/tools/ssl_config_parser.nim b/tools/ssl_config_parser.nim
new file mode 100644
index 000000000..6c0cccf1c
--- /dev/null
+++ b/tools/ssl_config_parser.nim
@@ -0,0 +1,64 @@
+#
+#
+#                 SSL configuration generator
+#     (c) Copyright 2020 Leorize <leorize+oss@disroot.org>
+#
+#        See the file "copying.txt", included in this
+#        distribution, for details about the copyright.
+#
+
+import httpclient, json, net, strformat, strutils, sequtils, times
+
+const
+  ConfigSource = "https://ssl-config.mozilla.org/guidelines/latest.json"
+  OutputFile = "ssl_config.nim"
+
+proc main() =
+  let
+    client = newHttpClient(sslContext = newContext(verifyMode = CVerifyPeer))
+    resp = client.get(ConfigSource)
+  defer: client.close()
+  if not resp.code.is2xx:
+    quit "Couldn't fetch configuration, server returned: " & $resp.code
+
+  let configs = resp.bodyStream.parseJson("ssl-config.json")
+
+  let generationTime = now().utc()
+  let output = open(OutputFile, fmWrite)
+  echo "Generating ", OutputFile
+  output.writeLine(&"""
+# This file was automatically generated by tools/ssl_config_parser on {generationTime}. DO NOT EDIT.
+
+## This module contains SSL configuration parameters obtained from
+## `Mozilla OpSec <https://wiki.mozilla.org/Security/Server_Side_TLS>`_.
+##
+## The configuration file used to generate this module: {configs["href"].getStr}
+""")
+
+  for name, config in configs["configurations"]:
+    let
+      constantName = "Ciphers" & name[0].toUpperAscii & name[1..^1]
+
+    var ciphers: string
+    for c in config["ciphersuites"].getElems & config["ciphers"]["openssl"].getElems:
+      if ciphers.len == 0:
+        ciphers.add c.getStr
+      else:
+        ciphers.add ':'
+        ciphers.add c.getStr
+
+    var constant = &"""
+const {constantName}* = "{ciphers}"
+  ## An OpenSSL-compatible list of secure ciphers for ``{name}`` compatibility
+  ## per Mozilla's recommendations.
+  ##
+  ## Oldest clients supported by this list:
+"""
+
+    for c in config["oldest_clients"]:
+      constant.add "  ## * " & c.getStr
+      constant.add '\n'
+
+    output.writeLine constant
+
+when isMainModule: main()
diff --git a/tools/ssl_config_parser.nims b/tools/ssl_config_parser.nims
new file mode 100644
index 000000000..796d18894
--- /dev/null
+++ b/tools/ssl_config_parser.nims
@@ -0,0 +1 @@
+--d:ssl
diff --git a/tools/trimcc.nim b/tools/trimcc.nim
index 959eaf310..41795e1aa 100644
--- a/tools/trimcc.nim
+++ b/tools/trimcc.nim
@@ -31,7 +31,7 @@ proc processIncludes(dir: string, whitelist: StringTableRef) =
       if ('.' notin name and "include" in path) or ("c++" in path):
         let n = whitelist.getOrDefault(name)
         if n != "processed": whitelist[name] = "found"
-      if name.endswith(".h"):
+      if name.endsWith(".h"):
         let n = whitelist.getOrDefault(name)
         if n == "found": includes(path, name, whitelist)
     of pcDir: processIncludes(path, whitelist)
diff --git a/tools/unicode_parsedata.nim b/tools/unicode_parsedata.nim
new file mode 100644
index 000000000..bd12998d1
--- /dev/null
+++ b/tools/unicode_parsedata.nim
@@ -0,0 +1,224 @@
+import strutils, algorithm
+
+let
+  # this file was obtained from:
+  # https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt
+  filename = "tools/UnicodeData.txt"
+  data = readFile(filename).strip.splitLines()
+
+const
+  # see the table here:
+  # https://www.unicode.org/reports/tr44/#GC_Values_Table
+  letters = ["Lu", "Ll", "Lt", "Lm", "Lo"]
+  spaces = ["Zs", "Zl", "Zp"]
+
+type
+  Ranges = tuple[start, stop, diff: int]
+  Singlets = tuple[code, diff: int]
+  NonLetterRanges = tuple[start, stop: int]
+
+var
+  toUpper = newSeq[Singlets]()
+  toLower = newSeq[Singlets]()
+  toTitle = newSeq[Singlets]()
+  alphas = newSeq[int]()
+  unispaces = newSeq[int]()
+
+
+proc parseData(data: seq[string]) =
+  proc doAdd(firstCode, lastCode: int, category, uc, lc, tc: string) =
+    if category notin spaces and category notin letters:
+      return
+
+    if firstCode != lastCode:
+      doAssert uc == "" and lc == "" and tc == ""
+    if uc.len > 0:
+      let diff = 500 + uc.parseHexInt() - firstCode
+      toUpper.add (firstCode, diff)
+    if lc.len > 0:
+      let diff = 500 + lc.parseHexInt() - firstCode
+      toLower.add (firstCode, diff)
+    if tc.len > 0 and tc != uc:
+      # if titlecase is different than uppercase
+      let diff = 500 + tc.parseHexInt() - firstCode
+      if diff != 500:
+        toTitle.add (firstCode, diff)
+
+    for code in firstCode..lastCode:
+      if category in spaces:
+        unispaces.add code
+      else:
+        alphas.add code
+
+  var idx = 0
+  while idx < data.len:
+    let
+      line = data[idx]
+      fields = line.split(';')
+      code = fields[0].parseHexInt()
+      name = fields[1]
+      category = fields[2]
+      uc = fields[12]
+      lc = fields[13]
+      tc = fields[14]
+    inc(idx)
+    if name.endsWith(", First>"):
+      doAssert idx < data.len
+      let
+        nextLine = data[idx]
+        nextFields = nextLine.split(';')
+        nextCode = nextFields[0].parseHexInt()
+        nextName = nextFields[1]
+      inc(idx)
+      doAssert nextName.endsWith(", Last>")
+      doAdd(code, nextCode, category, uc, lc, tc)
+    else:
+      doAdd(code, code, category, uc, lc, tc)
+
+proc splitRanges(a: seq[Singlets], r: var seq[Ranges], s: var seq[Singlets]) =
+  ## Splits `toLower`, `toUpper` and `toTitle` into separate sequences:
+  ## - `r` contains continuous ranges with the same characteristics
+  ##   (their upper/lower version is the same distance away)
+  ## - `s` contains single code points
+  var i, j: int
+  while i < a.len:
+    j = 1
+    let
+      startCode = a[i].code
+      startDiff = a[i].diff
+    while i + j <= a.len:
+      if i+j >= a.len or a[i+j].code != startCode+j or a[i+j].diff != startDiff:
+        if j == 1:
+          s.add (startCode, startDiff)
+        else:
+          r.add (startCode, a[i+j-1].code, startDiff)
+        i += j-1
+        break
+      else:
+        inc j
+    inc i
+
+proc splitRanges(a: seq[int], r: var seq[NonLetterRanges], s: var seq[int]) =
+  ## Splits `alphas` and `unispaces` into separate sequences:
+  ## - `r` contains continuous ranges
+  ## - `s` contains single code points
+  var i, j: int
+  while i < a.len:
+    j = 1
+    let startCode = a[i]
+    while i + j <= a.len:
+      if i+j >= a.len or a[i+j] != startCode+j:
+        if j == 1:
+          s.add startCode
+        else:
+          r.add (startCode, a[i+j-1])
+        i += j-1
+        break
+      else:
+        inc j
+    inc i
+
+proc splitSpaces(a: seq[int], r: var seq[NonLetterRanges], s: var seq[int]) =
+  ## Spaces are special because of the way how `isWhiteSpace` and `split`
+  ## are implemented.
+  ##
+  ## All spaces are added both to `r` (ranges) and `s` (singlets).
+  var i, j: int
+  while i < a.len:
+    j = 1
+    let startCode = a[i]
+    while i + j <= a.len:
+      if i+j >= a.len or a[i+j] != startCode+j:
+        r.add (startCode, a[i+j-1])
+        i += j-1
+        break
+      else:
+        inc j
+    inc i
+  s = a
+
+
+var
+  toupperRanges = newSeq[Ranges]()
+  toupperSinglets = newSeq[Singlets]()
+  tolowerRanges = newSeq[Ranges]()
+  tolowerSinglets = newSeq[Singlets]()
+  totitleRanges = newSeq[Ranges]()
+  totitleSinglets = newSeq[Singlets]()
+  spaceRanges = newSeq[NonLetterRanges]()
+  unicodeSpaces = newSeq[int]()
+  alphaRanges = newSeq[NonLetterRanges]()
+  alphaSinglets = newSeq[int]()
+
+parseData(data)
+splitRanges(toLower, tolowerRanges, tolowerSinglets)
+splitRanges(toUpper, toUpperRanges, toUpperSinglets)
+splitRanges(toTitle, toTitleRanges, toTitleSinglets)
+splitRanges(alphas, alphaRanges, alphaSinglets)
+
+# manually add "special" spaces
+for i in 9 .. 13:
+  unispaces.add i
+unispaces.add 0x85
+unispaces.sort()
+
+splitSpaces(unispaces, spaceRanges, unicodeSpaces)
+
+
+var output: string
+
+proc createHeader(output: var string) =
+  output.add "# This file was created from a script.\n\n"
+  output.add "const\n"
+
+proc `$`(r: Ranges): string =
+  let
+    start = "0x" & toHex(r.start, 5) & "'i32"
+    stop = "0x" & toHex(r.stop, 5) & "'i32"
+  result = "$#, $#, $#,\n" % [start, stop, $r.diff]
+
+proc `$`(r: Singlets): string =
+  let code = "0x" & toHex(r.code, 5) & "'i32"
+  result = "$#, $#,\n" % [code, $r.diff]
+
+proc `$`(r: NonLetterRanges): string =
+  let
+    start = "0x" & toHex(r.start, 5) & "'i32"
+    stop = "0x" & toHex(r.stop, 5) & "'i32"
+  result = "$#, $#,\n" % [start, stop]
+
+
+proc outputSeq(s: seq[Ranges|Singlets|NonLetterRanges], name: string,
+               output: var string) =
+  output.add "  $# = [\n" % name
+  for r in s:
+    output.add "    " & $r
+  output.add "  ]\n\n"
+
+proc outputSeq(s: seq[int], name: string, output: var string) =
+  output.add "  $# = [\n" % name
+  for i in s:
+    output.add "    0x$#'i32,\n" % toHex(i, 5)
+  output.add "  ]\n\n"
+
+proc outputSpaces(s: seq[int], name: string, output: var string) =
+  output.add "  $# = [\n" % name
+  for i in s:
+    output.add "    Rune 0x$#,\n" % toHex(i, 5)
+  output.add "  ]\n\n"
+
+
+output.createHeader()
+outputSeq(tolowerRanges,   "toLowerRanges",   output)
+outputSeq(tolowerSinglets, "toLowerSinglets", output)
+outputSeq(toupperRanges,   "toUpperRanges",   output)
+outputSeq(toupperSinglets, "toUpperSinglets", output)
+outputSeq(totitleSinglets, "toTitleSinglets", output)
+outputSeq(alphaRanges,     "alphaRanges",     output)
+outputSeq(alphaSinglets,   "alphaSinglets",   output)
+outputSeq(spaceRanges,     "spaceRanges",     output)
+outputSpaces(unispaces,    "unicodeSpaces",   output) # array of runes
+
+
+let outfile = "lib/pure/includes/unicode_ranges.nim"
+outfile.writeFile(output)
diff --git a/tools/urldownloader.nim b/tools/urldownloader.nim
deleted file mode 100644
index 46d67c821..000000000
--- a/tools/urldownloader.nim
+++ /dev/null
@@ -1,431 +0,0 @@
-
-#
-#
-#    Windows native FTP/HTTP/HTTPS file downloader
-#        (c) Copyright 2017 Eugene Kabanov
-#
-#    See the file "LICENSE", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module implements native Windows FTP/HTTP/HTTPS downloading feature,
-## using ``urlmon.UrlDownloadToFile()``.
-##
-##
-
-when not (defined(windows) or defined(nimdoc)):
-  {.error: "Platform is not supported.".}
-
-import os
-
-type
-  DownloadOptions* = enum
-    ## Available download options
-    optUseCache,             ## Use Windows cache.
-    optUseProgressCallback,  ## Report progress via callback.
-    optIgnoreSecurity        ## Ignore HTTPS security problems.
-
-  DownloadStatus* = enum
-    ## Available download status sent to ``progress`` callback.
-    statusProxyDetecting,    ## Automatic Proxy detection.
-    statusCookieSent         ## Cookie will be sent with request.
-    statusResolving,         ## Resolving URL with DNS.
-    statusConnecting,        ## Establish connection to server.
-    statusRedirecting        ## HTTP redirection pending.
-    statusRequesting,        ## Sending request to server.
-    statusMimetypeAvailable, ## Mimetype received from server.
-    statusBeginDownloading,  ## Download process starting.
-    statusDownloading,       ## Download process pending.
-    statusEndDownloading,    ## Download process finished.
-    statusCacheAvailable     ## File found in Windows cache.
-    statusUnsupported        ## Unsupported status.
-    statusError              ## Error happens.
-
-  DownloadProgressCallback* = proc(status: DownloadStatus, progress: uint,
-                                   progressMax: uint,
-                                   message: string)
-    ## Progress callback.
-    ##
-    ## status
-    ##   Indicate current stage of downloading process.
-    ##
-    ## progress
-    ##   Number of bytes currently downloaded. Available only, if ``status`` is
-    ##   ``statusBeginDownloading``, ``statusDownloading`` or
-    ##   ``statusEndDownloading``.
-    ##
-    ## progressMax
-    ##   Number of bytes expected to download. Available only, if ``status`` is
-    ##   ``statusBeginDownloading``, ``statusDownloading`` or
-    ##   ``statusEndDownloading``.
-    ##
-    ## message
-    ##   Status message, which depends on ``status`` code.
-    ##
-    ## Available messages' values:
-    ##
-    ## statusResolving
-    ##   URL hostname to be resolved.
-    ## statusConnecting
-    ##   IP address
-    ## statusMimetypeAvailable
-    ##   Downloading resource MIME type.
-    ## statusCacheAvailable
-    ##   Path to filename stored in Windows cache.
-
-type
-  UUID = array[4, uint32]
-
-  LONG = clong
-  ULONG = culong
-  HRESULT = clong
-  DWORD = uint32
-  OLECHAR = uint16
-  OLESTR = ptr OLECHAR
-  LPWSTR = OLESTR
-  UINT = cuint
-  REFIID = ptr UUID
-
-const
-  E_NOINTERFACE = 0x80004002'i32
-  E_NOTIMPL = 0x80004001'i32
-  S_OK = 0x00000000'i32
-
-  CP_UTF8 = 65001'u32
-
-  IID_IUnknown = UUID([0'u32, 0'u32, 192'u32, 1174405120'u32])
-  IID_IBindStatusCallback = UUID([2045430209'u32, 298760953'u32,
-                                  2852160140'u32, 195644160'u32])
-
-  BINDF_GETNEWESTVERSION = 0x00000010'u32
-  BINDF_IGNORESECURITYPROBLEM = 0x00000100'u32
-  BINDF_RESYNCHRONIZE = 0x00000200'u32
-  BINDF_NO_UI = 0x00000800'u32
-  BINDF_SILENTOPERATION = 0x00001000'u32
-  BINDF_PRAGMA_NO_CACHE = 0x00002000'u32
-
-  ERROR_FILE_NOT_FOUND = 2
-  ERROR_ACCESS_DENIED = 5
-
-  BINDSTATUS_FINDINGRESOURCE = 1
-  BINDSTATUS_CONNECTING = 2
-  BINDSTATUS_REDIRECTING  = 3
-  BINDSTATUS_BEGINDOWNLOADDATA  = 4
-  BINDSTATUS_DOWNLOADINGDATA  = 5
-  BINDSTATUS_ENDDOWNLOADDATA  = 6
-  BINDSTATUS_SENDINGREQUEST = 11
-  BINDSTATUS_MIMETYPEAVAILABLE  = 13
-  BINDSTATUS_CACHEFILENAMEAVAILABLE = 14
-  BINDSTATUS_PROXYDETECTING = 32
-  BINDSTATUS_COOKIE_SENT = 34
-
-type
-  STGMEDIUM = object
-    tymed: DWORD
-    pstg: pointer
-    pUnkForRelease: pointer
-
-  SECURITY_ATTRIBUTES = object
-    nLength*: uint32
-    lpSecurityDescriptor*: pointer
-    bInheritHandle*: int32
-
-  BINDINFO = object
-    cbSize: ULONG
-    stgmedData: STGMEDIUM
-    szExtraInfo: LPWSTR
-    grfBindInfoF: DWORD
-    dwBindVerb: DWORD
-    szCustomVerb: LPWSTR
-    cbstgmedData: DWORD
-    dwOptions: DWORD
-    dwOptionsFlags: DWORD
-    dwCodePage: DWORD
-    securityAttributes: SECURITY_ATTRIBUTES
-    iid: UUID
-    pUnk: pointer
-    dwReserved: DWORD
-
-  IBindStatusCallback = object
-    vtable: ptr IBindStatusCallbackVTable
-    options: set[DownloadOptions]
-    objectRefCount: ULONG
-    binfoFlags: DWORD
-    progressCallback: DownloadProgressCallback
-
-  PIBindStatusCallback = ptr IBindStatusCallback
-  LPBINDSTATUSCALLBACK = PIBindStatusCallback
-
-  IBindStatusCallbackVTable = object
-    QueryInterface: proc (self: PIBindStatusCallback,
-                          riid: ptr UUID,
-                          pvObject: ptr pointer): HRESULT {.gcsafe,stdcall.}
-    AddRef: proc(self: PIBindStatusCallback): ULONG {.gcsafe, stdcall.}
-    Release: proc(self: PIBindStatusCallback): ULONG {.gcsafe, stdcall.}
-    OnStartBinding: proc(self: PIBindStatusCallback,
-                         dwReserved: DWORD, pib: pointer): HRESULT
-                    {.gcsafe, stdcall.}
-    GetPriority: proc(self: PIBindStatusCallback, pnPriority: ptr LONG): HRESULT
-                 {.gcsafe, stdcall.}
-    OnLowResource: proc(self: PIBindStatusCallback, dwReserved: DWORD): HRESULT
-                   {.gcsafe, stdcall.}
-    OnProgress: proc(self: PIBindStatusCallback, ulProgress: ULONG,
-                     ulProgressMax: ULONG, ulStatusCode: ULONG,
-                     szStatusText: LPWSTR): HRESULT
-                {.gcsafe, stdcall.}
-    OnStopBinding: proc(self: PIBindStatusCallback, hresult: HRESULT,
-                        szError: LPWSTR): HRESULT
-                   {.gcsafe, stdcall.}
-    GetBindInfo: proc(self: PIBindStatusCallback, grfBINDF: ptr DWORD,
-                      pbindinfo: ptr BINDINFO): HRESULT
-                 {.gcsafe, stdcall.}
-    OnDataAvailable: proc(self: PIBindStatusCallback, grfBSCF: DWORD,
-                          dwSize: DWORD, pformatetc: pointer,
-                          pstgmed: pointer): HRESULT
-                     {.gcsafe, stdcall.}
-    OnObjectAvailable: proc(self: PIBindStatusCallback, riid: REFIID,
-                            punk: pointer): HRESULT
-                       {.gcsafe, stdcall.}
-
-template FAILED(hr: HRESULT): bool =
-  (hr < 0)
-
-proc URLDownloadToFile(pCaller: pointer, szUrl: LPWSTR, szFileName: LPWSTR,
-                       dwReserved: DWORD,
-                       lpfnCb: LPBINDSTATUSCALLBACK): HRESULT
-     {.stdcall, dynlib: "urlmon.dll", importc: "URLDownloadToFileW".}
-
-proc WideCharToMultiByte(CodePage: UINT, dwFlags: DWORD,
-                         lpWideCharStr: ptr OLECHAR, cchWideChar: cint,
-                         lpMultiByteStr: ptr char, cbMultiByte: cint,
-                         lpDefaultChar: ptr char,
-                         lpUsedDefaultChar: ptr uint32): cint
-     {.stdcall, dynlib: "kernel32.dll", importc: "WideCharToMultiByte".}
-
-proc MultiByteToWideChar(CodePage: UINT, dwFlags: DWORD,
-                         lpMultiByteStr: ptr char, cbMultiByte: cint,
-                         lpWideCharStr: ptr OLECHAR, cchWideChar: cint): cint
-     {.stdcall, dynlib: "kernel32.dll", importc: "MultiByteToWideChar".}
-proc DeleteUrlCacheEntry(lpszUrlName: LPWSTR): int32
-     {.stdcall, dynlib: "wininet.dll", importc: "DeleteUrlCacheEntryW".}
-
-proc `==`(a, b: UUID): bool =
-  result = false
-  if a[0] == b[0] and a[1] == b[1] and
-     a[2] == b[2] and a[3] == b[3]:
-    result = true
-
-proc `$`(bstr: LPWSTR): string =
-  var buffer: char
-  var count = WideCharToMultiByte(CP_UTF8, 0, bstr, -1, addr(buffer), 0,
-                                nil, nil)
-  if count == 0:
-    raiseOsError(osLastError())
-  else:
-    result = newStringOfCap(count + 8)
-    let res = WideCharToMultiByte(CP_UTF8, 0, bstr, -1, addr(result[0]), count,
-                                  nil, nil)
-    if res == 0:
-      raiseOsError(osLastError())
-    result.setLen(res - 1)
-
-proc toBstring(str: string): LPWSTR =
-  var buffer: OLECHAR
-  var count = MultiByteToWideChar(CP_UTF8, 0, unsafeAddr(str[0]), -1,
-                                  addr(buffer), 0)
-  if count == 0:
-    raiseOsError(osLastError())
-  else:
-    result = cast[LPWSTR](alloc0((count + 1) * sizeof(OLECHAR)))
-    let res = MultiByteToWideChar(CP_UTF8, 0, unsafeAddr(str[0]), -1,
-                                  result, count)
-    if res == 0:
-      raiseOsError(osLastError())
-
-proc freeBstring(bstr: LPWSTR) =
-  dealloc(bstr)
-
-proc getStatus(scode: ULONG): DownloadStatus =
-  case scode
-  of 0: result = statusError
-  of BINDSTATUS_PROXYDETECTING: result = statusProxyDetecting
-  of BINDSTATUS_REDIRECTING: result = statusRedirecting
-  of BINDSTATUS_COOKIE_SENT: result = statusCookieSent
-  of BINDSTATUS_FINDINGRESOURCE: result = statusResolving
-  of BINDSTATUS_CONNECTING: result = statusConnecting
-  of BINDSTATUS_SENDINGREQUEST: result = statusRequesting
-  of BINDSTATUS_MIMETYPEAVAILABLE: result = statusMimetypeAvailable
-  of BINDSTATUS_BEGINDOWNLOADDATA: result = statusBeginDownloading
-  of BINDSTATUS_DOWNLOADINGDATA: result = statusDownloading
-  of BINDSTATUS_ENDDOWNLOADDATA: result = statusEndDownloading
-  of BINDSTATUS_CACHEFILENAMEAVAILABLE: result = statusCacheAvailable
-  else: result = statusUnsupported
-
-proc addRef(self: PIBindStatusCallback): ULONG {.gcsafe, stdcall.} =
-  inc(self.objectRefCount)
-  result = self.objectRefCount
-
-proc release(self: PIBindStatusCallback): ULONG {.gcsafe, stdcall.} =
-  dec(self.objectRefCount)
-  result = self.objectRefCount
-
-proc queryInterface(self: PIBindStatusCallback, riid: ptr UUID,
-                    pvObject: ptr pointer): HRESULT {.gcsafe,stdcall.} =
-  pvObject[] = nil
-
-  if riid[] == IID_IUnknown:
-    pvObject[] = cast[pointer](self)
-  elif riid[] == IID_IBindStatusCallback:
-    pvObject[] = cast[pointer](self)
-
-  if not isNil(pvObject[]):
-    discard addRef(self)
-    result = S_OK
-  else:
-    result = E_NOINTERFACE
-
-proc onStartBinding(self: PIBindStatusCallback, dwReserved: DWORD,
-                    pib: pointer): HRESULT {.gcsafe, stdcall.} =
-  result = S_OK
-
-proc getPriority(self: PIBindStatusCallback,
-                 pnPriority: ptr LONG): HRESULT {.gcsafe, stdcall.} =
-  result = E_NOTIMPL
-
-proc onLowResource(self: PIBindStatusCallback,
-                   dwReserved: DWORD): HRESULT {.gcsafe, stdcall.} =
-  result = S_OK
-
-proc onStopBinding(self: PIBindStatusCallback,
-                   hresult: HRESULT, szError: LPWSTR): HRESULT
-     {.gcsafe, stdcall.} =
-  result = S_OK
-
-proc getBindInfo(self: PIBindStatusCallback,
-                 grfBINDF: ptr DWORD, pbindinfo: ptr BINDINFO): HRESULT
-     {.gcsafe, stdcall.} =
-  var cbSize = pbindinfo.cbSize
-  zeroMem(cast[pointer](pbindinfo), cbSize)
-  pbindinfo.cbSize = cbSize
-  grfBINDF[] = self.binfoFlags
-  result = S_OK
-
-proc onDataAvailable(self: PIBindStatusCallback,
-                     grfBSCF: DWORD, dwSize: DWORD, pformatetc: pointer,
-                     pstgmed: pointer): HRESULT {.gcsafe, stdcall.} =
-  result = S_OK
-
-proc onObjectAvailable(self: PIBindStatusCallback,
-                       riid: REFIID, punk: pointer): HRESULT
-     {.gcsafe, stdcall.} =
-  result = S_OK
-
-proc onProgress(self: PIBindStatusCallback,
-                ulProgress: ULONG, ulProgressMax: ULONG, ulStatusCode: ULONG,
-                szStatusText: LPWSTR): HRESULT {.gcsafe, stdcall.} =
-  var message: string
-  if optUseProgressCallback in self.options:
-    if not isNil(szStatusText):
-      message = $szStatusText
-    else:
-      message = ""
-    self.progressCallback(getStatus(ulStatusCode), uint(ulProgress),
-                          uint(ulProgressMax), message)
-  result = S_OK
-
-proc newBindStatusCallback(): IBindStatusCallback =
-  result = IBindStatusCallback()
-  result.vtable = cast[ptr IBindStatusCallbackVTable](
-    alloc0(sizeof(IBindStatusCallbackVTable))
-  )
-  result.vtable.QueryInterface = queryInterface
-  result.vtable.AddRef = addRef
-  result.vtable.Release = release
-  result.vtable.OnStartBinding = onStartBinding
-  result.vtable.GetPriority = getPriority
-  result.vtable.OnLowResource = onLowResource
-  result.vtable.OnStopBinding = onStopBinding
-  result.vtable.GetBindInfo = getBindInfo
-  result.vtable.OnDataAvailable = onDataAvailable
-  result.vtable.OnObjectAvailable = onObjectAvailable
-  result.vtable.OnProgress = onProgress
-  result.objectRefCount = 1
-
-proc freeBindStatusCallback(v: var IBindStatusCallback) =
-  dealloc(v.vtable)
-
-proc downloadToFile*(szUrl: string, szFileName: string,
-                     options: set[DownloadOptions] = {},
-                     progresscb: DownloadProgressCallback = nil) =
-  ## Downloads from URL specified in ``szUrl`` to local filesystem path
-  ## specified in ``szFileName``.
-  ##
-  ## szUrl
-  ##   URL to download, international names are supported.
-  ## szFileName
-  ##   Destination path for downloading resource.
-  ## options
-  ##   Downloading options. Currently only 2 options supported.
-  ## progresscb
-  ##   Callback procedure, which will be called throughout the download
-  ##   process, indicating status and progress.
-  ##
-  ## Available downloading options:
-  ##
-  ## optUseCache
-  ##   Try to use Windows cache when downloading.
-  ## optIgnoreSecurity
-  ##   Ignore HTTPS security problems, e.g. self-signed HTTPS certificate.
-  ##
-  var bszUrl = szUrl.toBstring()
-  var bszFile = szFileName.toBstring()
-  var bstatus = newBindStatusCallback()
-
-  bstatus.options = {}
-
-  if optUseCache notin options:
-    bstatus.options.incl(optUseCache)
-    let res = DeleteUrlCacheEntry(bszUrl)
-    if res == 0:
-      let err = osLastError()
-      if err.int notin {ERROR_ACCESS_DENIED, ERROR_FILE_NOT_FOUND}:
-        freeBindStatusCallback(bstatus)
-        freeBstring(bszUrl)
-        freeBstring(bszFile)
-        raiseOsError(err)
-
-  bstatus.binfoFlags = BINDF_GETNEWESTVERSION or BINDF_RESYNCHRONIZE or
-                       BINDF_PRAGMA_NO_CACHE or BINDF_NO_UI or
-                       BINDF_SILENTOPERATION
-
-  if optIgnoreSecurity in options:
-    bstatus.binfoFlags = bstatus.binfoFlags or BINDF_IGNORESECURITYPROBLEM
-
-  if not isNil(progresscb):
-    bstatus.options.incl(optUseProgressCallback)
-    bstatus.progressCallback = progresscb
-
-  let res = URLDownloadToFile(nil, bszUrl, bszFile, 0, addr bstatus)
-  if FAILED(res):
-    freeBindStatusCallback(bstatus)
-    freeBstring(bszUrl)
-    freeBstring(bszFile)
-    raiseOsError(OSErrorCode(res))
-
-  freeBindStatusCallback(bstatus)
-  freeBstring(bszUrl)
-  freeBstring(bszFile)
-
-when isMainModule:
-  proc progress(status: DownloadStatus, progress: uint, progressMax: uint,
-                message: string) {.procvar,gcsafe.} =
-    const downset: set[DownloadStatus] = {statusBeginDownloading,
-                                        statusDownloading, statusEndDownloading}
-    if status in downset:
-      var message = "Downloaded " & $progress & " of " & $progressMax & "\c"
-      stdout.write(message)
-    else:
-      echo "Status [" & $status & "] message = [" & $message & "]"
-
-  downloadToFile("https://nim-lang.org/download/mingw64-6.3.0.7z",
-                 "test.zip", {optUseCache}, progress)
diff --git a/tools/vccenv/vccenv.nim b/tools/vccenv/vccenv.nim
deleted file mode 100644
index a335efd10..000000000
--- a/tools/vccenv/vccenv.nim
+++ /dev/null
@@ -1,58 +0,0 @@
-import strtabs, os, osproc, streams, strutils
-
-const
-  comSpecEnvKey = "ComSpec"
-  vsComnToolsEnvKeys = [
-    "VS140COMNTOOLS",
-    "VS130COMNTOOLS",
-    "VS120COMNTOOLS",
-    "VS110COMNTOOLS",
-    "VS100COMNTOOLS",
-    "VS90COMNTOOLS"
-  ]
-  vcvarsallRelativePath = joinPath("..", "..", "VC", "vcvarsall")
-
-proc getVsComnToolsPath*(): TaintedString =
-  for vsComnToolsEnvKey in vsComnToolsEnvKeys:
-    let vsComnToolsEnvVal = getEnv vsComnToolsEnvKey
-    if vsComnToolsEnvVal.len > 0:
-      return vsComnToolsEnvVal
-
-proc getVccEnv*(platform: string, windowsStoreSdk: bool = false,
-                sdkVersion: string = nil): StringTableRef =
-  var comSpecCommandString = getEnv comSpecEnvKey
-  if comSpecCommandString.len == 0:
-    comSpecCommandString = "cmd"
-
-  let vsComnToolsPath = getVsComnToolsPath()
-  if vsComnToolsPath.len < 1:
-    return nil
-  let vcvarsallPath = expandFilename joinPath(vsComnToolsPath, vcvarsallRelativePath)
-
-  var vcvarsallArgs: seq[string] = @[]
-  if platform.len > 0:
-    vcvarsallArgs.add(platform)
-  if windowsStoreSdk:
-    vcvarsallArgs.add("store")
-  if sdkVersion.len > 0:
-    vcvarsallArgs.add(sdkVersion)
-  let vcvarsallArgString = vcvarsallArgs.join(" ")
-
-  var vcvarsallCommandString: string
-  if vcvarsallArgString.len > 0:
-    vcvarsallCommandString = "\"$1\" $2" % [vcvarsallPath, vcvarsallArgString]
-  else:
-    vcvarsallCommandString = vcvarsallPath
-
-  let vcvarsallExecCommand = "\"$1\" /C \"$2 && SET\"" %
-                             [comSpecCommandString, vcvarsallCommandString]
-  when defined(release):
-    let vccvarsallOptions = {poEvalCommand, poDemon}
-  else:
-    let vccvarsallOptions = {poEchoCmd, poEvalCommand, poDemon}
-  let vcvarsallStdOut = execProcess(vcvarsallExecCommand, options = vccvarsallOptions)
-  result = newStringTable(modeCaseInsensitive)
-  for line in vcvarsallStdOut.splitLines:
-    let idx = line.find('=')
-    if idx > 0:
-      result[line[0..(idx - 1)]] = line[(idx + 1)..(line.len - 1)]
diff --git a/tools/vccenv/vccexe.nim b/tools/vccenv/vccexe.nim
deleted file mode 100644
index 892246830..000000000
--- a/tools/vccenv/vccexe.nim
+++ /dev/null
@@ -1,66 +0,0 @@
-import strutils, strtabs, os, osproc, vccenv
-
-when defined(release):
-  let vccOptions = {poParentStreams}
-else:
-  let vccOptions = {poEchoCmd, poParentStreams}
-
-const 
-  platformPrefix = "--platform"
-  winstorePrefix = "--winstore"
-  sdkversionPrefix = "--sdkversion"
-
-  platformSepIdx = platformPrefix.len
-  sdkversionSepIdx = sdkversionPrefix.len
-  
-  HelpText = """
-+-----------------------------------------------------------------+
-|         Microsoft C/C++ compiler wrapper for Nim                |
-|             (c) 2016 Fredrik Høisæther Rasch                    |
-+-----------------------------------------------------------------+
-
-Usage:
-  vccexe [options] [compileroptions]
-Options:
-  --platform:<arch>   Specify the Compiler Platform Tools architecture
-                      <arch>: x86 | amd64 | arm | x86_amd64 | x86_arm | amd64_x86 | amd64_arm
-  --winstore          Use Windows Store (rather than desktop) development tools
-  --sdkversion:<v>    Use a specific Windows SDK version:
-                      <v> is either the full Windows 10 SDK version number or 
-                      "8.1" to use the windows 8.1 SDK
-
-Other command line arguments are passed on to the
-Microsoft C/C++ compiler for the specified SDK toolset
-"""
-
-when isMainModule:
-  var platformArg: string = nil
-  var sdkVersionArg: string = nil
-  var storeArg: bool = false
-
-  var clArgs: seq[TaintedString] = @[]
-
-  var wrapperArgs = commandLineParams()
-  for wargv in wrapperArgs:
-    # Check whether the current argument contains -- prefix
-    if wargv.startsWith(platformPrefix): # Check for platform
-      platformArg = wargv.substr(platformSepIdx + 1)
-    elif wargv == winstorePrefix: # Check for winstore
-      storeArg = true
-    elif wargv.startsWith(sdkversionPrefix): # Check for sdkversion
-      sdkVersionArg = wargv.substr(sdkversionSepIdx + 1)
-    else: # Regular cl.exe argument -> store for final cl.exe invocation
-      if (wargv.len == 2) and (wargv[1] == '?'):
-        echo HelpText
-      clArgs.add(wargv)
-
-  var vccEnvStrTab = getVccEnv(platformArg, storeArg, sdkVersionArg)  
-  if vccEnvStrTab != nil:
-    for vccEnvKey, vccEnvVal in vccEnvStrTab:
-      putEnv(vccEnvKey, vccEnvVal)
-  let vccProcess = startProcess(
-      "cl.exe",
-      args = clArgs,
-      options = vccOptions
-    )
-  quit vccProcess.waitForExit()
diff --git a/tools/vccexe/vccenv.nim b/tools/vccexe/vccenv.nim
new file mode 100644
index 000000000..216760e54
--- /dev/null
+++ b/tools/vccexe/vccenv.nim
@@ -0,0 +1,47 @@
+## VCC compiler backend installation discovery using Visual Studio common tools
+## environment variables.
+
+import os
+
+type
+  VccEnvVersion* = enum ## The version of the Visual Studio C/C++ Developer Environment to load
+                        ## Valid versions are Versions of Visual Studio that permanently set a COMNTOOLS
+                        ## environment variable. That includes Visual Studio version up to and including
+                        ## Visual Studio 2015
+    vsUndefined = (0, ""), ## Version not specified, use latest recognized version on the system
+    vs90  = (90,   "VS90COMNTOOLS"), ## Visual Studio 2008
+    vs100 = (100, "VS100COMNTOOLS"), ## Visual Studio 2010
+    vs110 = (110, "VS110COMNTOOLS"), ## Visual Studio 2012
+    vs120 = (120, "VS120COMNTOOLS"), ## Visual Studio 2013
+    vs140 = (140, "VS140COMNTOOLS")  ## Visual Studio 2015
+
+const
+  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.
+  ##
+  ## Returns `nil` if the specified VCC compiler backend installation was not found.
+  ## 
+  ## If the `version` parameter is omitted or set to `vsUndefined`, `vccEnvVcVarsAllPath` searches 
+  ## for the latest recognizable version of the VCC tools it can find.
+  ## 
+  ## `vccEnvVcVarsAllPath` uses the COMNTOOLS environment variables to find the Developer Command Prompt
+  ## executable path. The COMNTOOLS environment variable are permanently set when Visual Studio is installed.
+  ## Each version of Visual Studio has its own COMNTOOLS environment variable. E.g.: Visual Studio 2015 sets
+  ## The VS140COMNTOOLS environment variable.
+  ##
+  ## Note: Beginning with Visual Studio 2017, the installers no longer set environment variables to allow for
+  ## multiple side-by-side installations of Visual Studio. Therefore, `vccEnvVcVarsAllPath` cannot be used
+  ## to detect the VCC Developer Command Prompt executable path for Visual Studio 2017 and later.
+
+  if version == vsUndefined:
+    for tryVersion in [vs140, vs120, vs110, vs100, vs90]:
+      let tryPath = vccEnvVcVarsAllPath(tryVersion)
+      if tryPath.len > 0:
+        return tryPath
+  else: # Specific version requested
+    let key = $version
+    let val = getEnv key
+    if val.len > 0:
+      result = try: expandFilename(joinPath(val, vcvarsallRelativePath)) except OSError: ""
diff --git a/tools/vccexe/vccexe.nim b/tools/vccexe/vccexe.nim
new file mode 100644
index 000000000..2a43f7422
--- /dev/null
+++ b/tools/vccexe/vccexe.nim
@@ -0,0 +1,237 @@
+import strutils, strtabs, os, osproc, vcvarsall, vccenv, vccvswhere
+
+type
+  VccVersion* = enum ## VCC compiler backend versions
+    vccUndefined = 0,   ## VCC version undefined, resolves to the latest recognizable VCC version
+    vcc90  = vs90  ## Visual Studio 2008 (Version 9.0)
+    vcc100 = vs100 ## Visual Studio 2010 (Version 10.0)
+    vcc110 = vs110 ## Visual Studio 2012 (Version 11.0)
+    vcc120 = vs120 ## Visual Studio 2013 (Version 12.0)
+    vcc140 = vs140 ## Visual Studio 2015 (Version 14.0)
+
+proc discoverVccVcVarsAllPath*(version: VccVersion = vccUndefined): string =
+  ## Returns the path to the vcvarsall utility of the specified VCC compiler backend.
+  ##
+  ## version
+  ##   The specific version of the VCC compiler backend to discover.
+  ##   Defaults to the latest recognized VCC compiler backend that is found on the system.
+  ##
+  ## Returns `nil` if the VCC compiler backend discovery failed.
+
+  # Attempt discovery using vswhere utility (VS 2017 and later) if no version specified.
+  if version == vccUndefined:
+    result = vccVswhereVcVarsAllPath()
+    if result.len > 0:
+      return
+
+  # Attempt discovery through VccEnv
+  # (Trying Visual Studio Common Tools Environment Variables)
+  result = vccEnvVcVarsAllPath(cast[VccEnvVersion](version))
+  if result.len > 0:
+    return
+
+  # All attempts to discover vcc failed
+
+const
+  vccversionPrefix = "--vccversion"
+  printPathPrefix = "--printPath"
+  vcvarsallPrefix = "--vcvarsall"
+  commandPrefix = "--command"
+  noCommandPrefix = "--noCommand"
+  platformPrefix = "--platform"
+  sdktypePrefix = "--sdktype"
+  sdkversionPrefix = "--sdkversion"
+  vctoolsetPrefix = "--vctoolset"
+  verbosePrefix = "--verbose"
+
+  vccversionSepIdx = vccversionPrefix.len
+  vcvarsallSepIdx = vcvarsallPrefix.len
+  commandSepIdx = commandPrefix.len
+  platformSepIdx = platformPrefix.len
+  sdktypeSepIdx = sdktypePrefix.len
+  sdkversionSepIdx = sdkversionPrefix.len
+  vctoolsetSepIdx = vctoolsetPrefix.len
+
+  vcvarsallDefaultPath = "vcvarsall.bat"
+
+  helpText = """
++-----------------------------------------------------------------+
+|         Microsoft C/C++ compiler wrapper for Nim                |
+|                                &                                |
+|        Microsoft C/C++ Compiler Discovery Utility               |
+|            (c) 2017 Fredrik Hoeisaether Rasch                   |
++-----------------------------------------------------------------+
+
+Usage:
+  vccexe [options] [compileroptions]
+Options:
+  --vccversion:<v>    Optionally specify the VCC version to discover
+                      <v>: 0, 90, 100, 110, 120, 140
+                      If <v> is omitted, attempts to discover the latest
+                      installed version. <v>: 0, 90, 100, 110, 120, 140
+                      A value of 0 will discover the latest installed SDK
+                      Multiple values can be specified, separated by ,
+  --printPath         Print the discovered path of the vcvarsall utility
+                      of the VCC version specified with the --vccversion argument.
+                      For each specified version the utility prints a line with the
+                      following format: <version>: <path>
+  --noCommand         Flag to suppress VCC secondary command execution
+                      Useful in conjunction with --vccversion and --printPath to
+                      only perform VCC discovery, but without executing VCC tools
+  --vcvarsall:<path>  Path to the Developer Command Prompt utility vcvarsall.bat that selects
+                      the appropriate development settings.
+                      Usual path for Visual Studio 2015 and below:
+                        %VSInstallDir%\VC\vcvarsall
+                      Usual path for Visual Studio 2017 and above:
+                        %VSInstallDir%\VC\Auxiliary\Build\vcvarsall
+  --command:<exec>    Specify the command to run once the development environment is loaded.
+                      <exec> can be any command-line argument. Any arguments not recognized by vccexe
+                      are passed on as arguments to this command.
+                      cl.exe is invoked by default if this argument is omitted.
+  --platform:<arch>   Specify the Compiler Platform Tools architecture
+                      <arch>: x86 | amd64 | arm | x86_amd64 | x86_arm | amd64_x86 | amd64_arm
+                      Values with two architectures (like x86_amd64) specify the architecture
+                      of the cross-platform compiler (e.g. x86) and the target it compiles to (e.g. amd64).
+  --sdktype:<type>    Specify the SDK flavor to use. Defaults to the Desktop SDK.
+                      <type>: {empty} | store | uwp | onecore
+  --sdkversion:<v>    Use a specific Windows SDK version:
+                      <v> is either the full Windows 10 SDK version number or
+                      "8.1" to use the windows 8.1 SDK
+  --verbose           Echoes the command line for loading the Developer Command Prompt
+                      and the command line passed on to the secondary command.
+  --vctoolset         Optionally specifies the Visual Studio compiler toolset to use. 
+                      By default, the environment is set to use the current Visual Studio compiler toolset.
+
+Other command line arguments are passed on to the
+secondary command specified by --command or to the
+Microsoft (R) C/C++ Optimizing Compiler if no secondary
+command was specified
+"""
+
+proc parseVccexeCmdLine(argseq: seq[string],
+    vccversionArg: var seq[string], printPathArg: var bool,
+    vcvarsallArg: var string, commandArg: var string, noCommandArg: var bool,
+    platformArg: var VccArch, sdkTypeArg: var VccPlatformType,
+    sdkVersionArg: var string,  vctoolsetArg: var string, verboseArg: var bool,
+    clArgs: var seq[string]) =
+  ## Cannot use usual command-line argument parser here
+  ## Since vccexe command-line arguments are intermingled
+  ## with the secondary command-line arguments which have
+  ## a syntax that is not supported by the default nim
+  ## argument parser.
+  for wargv in argseq:
+    # Check whether the current argument contains -- prefix
+    if wargv.startsWith("@"): # Check for response file prefix
+      let
+        responsefilename = wargv.substr(1)
+        responsefilehandle = open(responsefilename)
+        responsecontent = responsefilehandle.readAll()
+        responseargs = parseCmdLine(responsecontent)
+      parseVccexeCmdLine(responseargs, vccversionArg, printPathArg,
+        vcvarsallArg, commandArg, noCommandArg, platformArg, sdkTypeArg,
+        sdkVersionArg, vctoolsetArg, verboseArg, clArgs)
+    elif wargv.startsWith(vccversionPrefix): # Check for vccversion
+      vccversionArg.add(wargv.substr(vccversionSepIdx + 1))
+    elif wargv.cmpIgnoreCase(printPathPrefix) == 0: # Check for printPath
+      printPathArg = true
+    elif wargv.startsWith(vcvarsallPrefix): # Check for vcvarsall
+      vcvarsallArg = wargv.substr(vcvarsallSepIdx + 1)
+    elif wargv.startsWith(commandPrefix): # Check for command
+      commandArg = wargv.substr(commandSepIdx + 1)
+    elif wargv.cmpIgnoreCase(noCommandPrefix) == 0: # Check for noCommand
+      noCommandArg = true
+    elif wargv.startsWith(platformPrefix): # Check for platform
+      platformArg = parseEnum[VccArch](wargv.substr(platformSepIdx + 1))
+    elif wargv.startsWith(sdktypePrefix): # Check for sdktype
+      sdkTypeArg = parseEnum[VccPlatformType](wargv.substr(sdktypeSepIdx + 1))
+    elif wargv.startsWith(sdkversionPrefix): # Check for sdkversion
+      sdkVersionArg = wargv.substr(sdkversionSepIdx + 1)
+    elif wargv.startsWith(vctoolsetPrefix): # Check for vctoolset
+      vctoolsetArg = wargv.substr(vctoolsetSepIdx + 1)
+    elif wargv.startsWith(verbosePrefix):
+      verboseArg = true
+    else: # Regular cl.exe argument -> store for final cl.exe invocation
+      if (wargv.len == 2) and (wargv[1] == '?'):
+        echo helpText
+      clArgs.add(wargv)
+
+when isMainModule:
+  var vccversionArg: seq[string] = @[]
+  var printPathArg: bool = false
+  var vcvarsallArg: string
+  var commandArg: string
+  var noCommandArg: bool = false
+  var platformArg: VccArch
+  var sdkTypeArg: VccPlatformType
+  var sdkVersionArg: string
+  var vctoolsetArg: string
+  var verboseArg: bool = false
+
+  var clArgs: seq[string] = @[]
+
+  let wrapperArgs = commandLineParams()
+  parseVccexeCmdLine(wrapperArgs, vccversionArg, printPathArg, vcvarsallArg,
+    commandArg, noCommandArg, platformArg, sdkTypeArg, sdkVersionArg, vctoolsetArg,
+    verboseArg,
+    clArgs)
+
+  # Support for multiple specified versions. Attempt VCC discovery for each version
+  # specified, first successful discovery wins
+  var vccversionValue: VccVersion = vccUndefined
+  for vccversionItem in vccversionArg:
+    try:
+      vccversionValue = cast[VccVersion](parseInt(vccversionItem))
+    except ValueError:
+      continue
+    vcvarsallArg = discoverVccVcVarsAllPath(vccversionValue)
+    if vcvarsallArg.len > 0:
+      break
+  # VCC version not specified, discover latest (call discover without args)
+  if vcvarsallArg.len < 1 and vccversionArg.len < 1:
+    vccversionValue = vccUndefined
+    vcvarsallArg = discoverVccVcVarsAllPath()
+
+  if vcvarsallArg == "":
+    # Assume that default executable is in current directory or in PATH
+    vcvarsallArg = findExe(vcvarsallDefaultPath)
+
+  if printPathArg:
+    var head = $vccversionValue
+    if head.len < 1:
+      head = "latest"
+    echo "$1: $2" % [head, vcvarsallArg]
+
+  # Call vcvarsall to get the appropriate VCC process environment
+  var vcvars = vccVarsAll(vcvarsallArg, platformArg, sdkTypeArg, sdkVersionArg, vctoolsetArg, verboseArg)
+  if vcvars != nil:
+    for vccEnvKey, vccEnvVal in vcvars:
+      putEnv(vccEnvKey, vccEnvVal)
+
+  var vccOptions = {poParentStreams}
+  if verboseArg:
+    vccOptions.incl poEchoCmd
+
+  let currentDir = getCurrentDir()
+  for arg in clArgs.mitems:
+    if fileExists(arg):
+      arg = relativePath(arg, currentDir)
+
+  # Default to the cl.exe command if no secondary command was specified
+  if commandArg.len < 1:
+    commandArg = "cl.exe"
+
+  if not noCommandArg:
+    # Run VCC command with the VCC process environment
+    try:
+      let vccProcess = startProcess(
+          commandArg,
+          args = clArgs,
+          options = vccOptions
+        )
+      quit vccProcess.waitForExit()
+    except:
+      if vcvarsallArg.len != 0:
+        echo "Hint: using " & vcvarsallArg
+      else:
+        echo "Hint: vcvarsall.bat was not found"
+      raise
diff --git a/tools/vccexe/vccvswhere.nim b/tools/vccexe/vccvswhere.nim
new file mode 100644
index 000000000..8f62f06ca
--- /dev/null
+++ b/tools/vccexe/vccvswhere.nim
@@ -0,0 +1,47 @@
+## VCC compiler discovery using vswhere (https://github.com/Microsoft/vswhere)
+
+import os, osproc, strformat, strutils
+
+const
+  vswhereRelativePath = joinPath("Microsoft Visual Studio", "Installer", "vswhere.exe")
+  vswhereArgs = "-latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath"
+  vcvarsRelativePath = joinPath("VC", "Auxiliary", "Build", "vcvarsall.bat")
+
+proc vccVswhereExtractVcVarsAllPath(vswherePath: string): string =
+  ## Returns the path to `vcvarsall.bat` if Visual Studio location was obtained by executing `vswhere.exe`.
+  ##
+  ## `vcvarsall.bat` is supposed to exist under {vsPath}\VC\Auxiliary\Build directory.
+  ##
+  ## Returns "" if no appropriate `vcvarsall.bat` file was found.
+
+  # For more detail on vswhere arguments, refer to https://github.com/microsoft/vswhere/wiki/Find-VC
+  let vsPath = execProcess(&"\"{vswherePath}\" {vswhereArgs}").strip()
+  if vsPath.len > 0:
+    let vcvarsallPath = joinPath(vsPath, vcvarsRelativePath)
+    if fileExists(vcvarsallPath):
+      return vcvarsallPath
+
+proc vccVswhereGeneratePath(envName: string): string =
+  ## Generate a path to vswhere.exe under "Program Files" or "Program Files (x86)" depending on envName.
+  ##
+  ## Returns "" if an environment variable specified by envName was not available.
+
+  let val = getEnv(envName)
+  if val.len > 0:
+    result = try: expandFilename(joinPath(val, vswhereRelativePath)) except OSError: ""
+
+proc vccVswhereVcVarsAllPath*(): string = 
+  ## Returns the path to `vcvarsall.bat` for the latest Visual Studio (2017 and later).
+  ##
+  ## Returns "" if no recognizable Visual Studio installation was found.
+  ## 
+  ## Note: Beginning with Visual Studio 2017, the installers no longer set environment variables to allow for
+  ## multiple side-by-side installations of Visual Studio. Therefore, `vccEnvVcVarsAllPath` cannot be used
+  ## to detect the VCC Developer Command Prompt executable path for Visual Studio 2017 and later.
+
+  for tryEnv in ["ProgramFiles(x86)", "ProgramFiles"]:
+    let vswherePath = vccVswhereGeneratePath(tryEnv)
+    if vswherePath.len > 0 and fileExists(vswherePath):
+      let vcVarsAllPath = vccVswhereExtractVcVarsAllPath(vswherePath)
+      if vcVarsAllPath.len > 0:
+        return vcVarsAllPath
diff --git a/tools/vccexe/vcvarsall.nim b/tools/vccexe/vcvarsall.nim
new file mode 100644
index 000000000..73b103e3c
--- /dev/null
+++ b/tools/vccexe/vcvarsall.nim
@@ -0,0 +1,102 @@
+## VCC Developer Command Prompt Loader
+## 
+## In order for the VCC compiler backend to work properly, it requires numerous
+## environment variables to be set properly for the desired architecture and compile target.
+## For that purpose the VCC compiler ships with the vcvarsall utility which is an executable
+## batch script that can be used to properly set up an Command Prompt environment.
+
+import strtabs, strutils, os, osproc
+
+const
+  comSpecEnvKey = "ComSpec" ## Environment Variable that specifies the command-line application path in Windows
+                            ## Usually set to cmd.exe
+
+type
+  VccArch* = enum ## The VCC compile target architectures
+    vccarchUnspecified = "",
+    vccarchX86 = "x86", ## VCC for compilation against the x86 architecture.
+    vccarchAmd64 = "amd64", ## VCC for compilation against the amd64 architecture.
+    vccarchX86Amd64 = "x86_amd64", ## VCC cross-compilation tools using x86 VCC for compilation against the amd64 architecture.
+    vccarchX86Arm = "x86_arm", ## VCC cross-compilation tools using x86 VCC for compilation against the ARM architecture.
+    vccarchX86Arm64 = "x86_arm64", ## VCC cross-compilation tools using x86 VCC for compilation against the ARM (64-bit) architecture.
+    vccarchAmd64X86 = "amd64_x86", ## VCC cross-compilation tools using amd64 VCC for compilation against the x86 architecture.
+    vccarchAmd64Arm = "amd64_arm", ## VCC cross-compilation tools using amd64 VCC for compilation against the ARM architecture.
+    vccarchAmd64Arm64 = "amd64_arm64", ## VCC cross-compilation tools using amd64 VCC for compilation against the ARM (64-bit) architecture.
+    vccarchX64 = "x64", ## VCC for compilation against the x64 architecture.
+    vccarchX64X86 = "x64_x86", ## VCC cross-compilation tools using x64 VCC for compilation against the x86 architecture.
+    vccarchX64Arm = "x64_arm", ## VCC cross-compilation tools using x64 VCC for compilation against the ARM architecture.
+    vccarchX64Arm64 = "x64_arm64" ## VCC cross-compilation tools using x64 VCC for compilation against the ARM (64-bit) architecture.
+
+  VccPlatformType* = enum ## The VCC platform type of the compile target
+    vccplatEmpty = "", ## Default (i.e. Desktop) Platfor Type
+    vccplatStore = "store", ## Windows Store Application
+    vccplatUWP = "uwp", ## Universal Windows Platform (UWP) Application
+    vccplatOneCore = "onecore" # Undocumented platform type in the Windows SDK, probably XBox One SDK platform type.
+
+proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type: VccPlatformType = vccplatEmpty, sdk_version, vctoolset: string = "", verbose: bool = false): StringTableRef =
+  ## Returns a string table containing the proper process environment to successfully execute VCC compile commands for the specified SDK version, CPU architecture and platform type.
+  ##
+  ## path
+  ##   The path to the vcvarsall utility for VCC compiler backend.
+  ## arch
+  ##   The compile target CPU architecture. Starting with Visual Studio 2017, this value must be specified and must not be set to `vccarchUnspecified`.
+  ## platform_type
+  ##   The compile target Platform Type. Defaults to the Windows Desktop platform, i.e. a regular Windows executable binary.
+  ## sdk_version
+  ##   The Windows SDK version to use.
+  ## vctoolset
+  ##  Visual Studio compiler toolset to use.
+  ## verbose
+  ##   Echo the command-line passed on to the system to load the VCC environment. Defaults to `false`.
+
+  if path == "":
+    return nil
+  
+  let vccvarsallpath = path
+  var args: seq[string] = @[]
+  
+  let archStr: string = $arch
+  if archStr.len > 0:
+    args.add(archStr)
+  
+  let platStr: string = $platform_type
+  if platStr.len > 0:
+    args.add(platStr)
+
+  if sdk_version.len > 0:
+    args.add(sdk_version)
+  
+  if vctoolset.len > 0:
+    args.add("-vcvars_ver="&vctoolset)
+
+  let argStr = args.join " "
+  
+  var vcvarsExec: string
+  if argStr.len > 0:
+    vcvarsExec = "\"$1\" $2" % [vccvarsallpath, argStr]
+  else:
+    vcvarsExec = "\"$1\"" % vccvarsallpath
+
+  var comSpecCmd = getenv comSpecEnvKey
+  if comSpecCmd.len < 1:
+    comSpecCmd = "cmd"
+  
+  # Run the Windows Command Prompt with the /C argument
+  # Execute vcvarsall with its command-line arguments
+  # and then execute the SET command to list all environment variables
+  let comSpecExec = "\"$1\" /C \"$2 && SET\"" % [comSpecCmd, vcvarsExec]
+  var comSpecOpts = {poEvalCommand, poDaemon, poStdErrToStdOut}
+  if verbose:
+    comSpecOpts.incl poEchoCmd
+  let comSpecOut = execProcess(comSpecExec, options = comSpecOpts)
+
+  result = newStringTable(modeCaseInsensitive)
+
+  # Parse the output of the final SET command to construct a String Table
+  # with the appropriate environment variables
+  for line in comSpecOut.splitLines:
+    let idx = line.find('=')
+    if idx > 0:
+      result[line[0..(idx - 1)]] = line[(idx + 1)..(line.len - 1)]
+    elif verbose:
+      echo line
diff --git a/tools/website.tmpl b/tools/website.tmpl
deleted file mode 100644
index f9b1a219a..000000000
--- a/tools/website.tmpl
+++ /dev/null
@@ -1,266 +0,0 @@
-#? stdtmpl | standard
-#proc generateHTMLPage(c: var TConfigData, currentTab, title, content, rss,
-#   rootDir = ""): string =
-#  result = ""
-<!DOCTYPE html>
-<html>
-  <head>
-    <meta http-equiv="content-type" content="text/html; charset=utf-8">
-    <title>${title} - $c.projectTitle</title>
-    <link rel="stylesheet" type="text/css" href="${rootDir}assets/style.css?t=2320" />
-    <link rel="shortcut icon" href="${rootDir}assets/images/favicon.ico">
-  #if len(rss) > 0:
-    <link href="$rss" title="Recent changes" type="application/atom+xml" rel="alternate">
-  #end if
-  </head>
-  <body>
-    <div id="bountysource">
-      <a href="https://salt.bountysource.com/teams/nim">
-      <div class="page-layout" style="padding: 2pt 2pt 2pt 30pt">
-  	<img src="${rootDir}assets/bountysource/bountysource.png" style="width: 20px; float: left;">
-        <span style="margin-left: 10pt; float: left; margin-top: 2pt;">Fund Nim and help us develop it further!</span>
-        <img src="https://api.bountysource.com/badge/team?team_id=19072&style=raised" style="margin-top: 2pt; margin-left: 10pt"/>
-      </div>
-      </a>
-    </div>
-
-    <header id="head">
-      <div class="page-layout tall">
-        <div id="head-logo"></div>
-        <a id="head-logo-link" href="${rootDir}index.html"></a>
-        <nav id="head-links">
-      #for i in 0.. c.tabs.len-1:
-      # let t = c.tabs[i].val
-      # if t != "index" and t != "community" and t != "news":
-      #   let name = c.tabs[i].key
-      #   if currentTab == t:
-            <a class="active"
-      #   else:
-            <a
-      #   end if
-      #   if t.contains('.'):
-            href="${t}" title = "$c.projectName - $name">$name</a>
-      #   else:
-            href="${rootDir}${t}.html" title = "$c.projectName - $name">$name</a>
-      #   end if
-      # end if
-      #end for
-        </nav>
-      </div>
-    </header>
-
-#  if currentTab == "index":
-    <section id="neck" class="home">
-#  else:
-    <section id="neck">
-#  end
-      <div class="page-layout tall">
-        <div id="glow-arrow"></div>
-
-#  if currentTab == "index":
-        <div id="slideshow">
-          <!-- slides -->
-          <div id="slide0" class="active codeslide2">
-            <div>
-              <h2>Nim is simple..</h2>
-<pre>
-<span class="cmt"># compute average line length</span>
-<span class="kwd">var</span>
-<span class="tab">  </span>sum = <span class="val">0</span>
-<span class="tab end">  </span>count = <span class="val">0</span>
-
-<span class="kwd">for</span> line <span class="kwd">in</span> stdin.lines:
-<span class="tab">  </span>sum += line.len
-<span class="tab end">  </span>count += <span class="val">1</span>
-
-echo(<span class="val">"Average line length: "</span>,
-  <span class="kwd">if</span> count &gt; <span class="val">0</span>: sum / count <span class="kwd">else</span>: <span class="val">0</span>)
-</pre>
-            </div>
-            <div>
-               <h2>..and type safe...</h2>
-<pre>
-<span class="cmt"># create and greet someone</span>
-<span class="kwd">type</span> <span class="def">Person</span> = <span class="kwd">object</span>
-<span class="tab">  </span>name: <span class="typ">string</span>
-<span class="tab end">  </span>age: <span class="typ">int</span>
-
-<span class="kwd">proc</span> <span class="def">greet</span>(p: <span class="typ">Person</span>) =
-<span class="tab">  </span>echo <span class="val">"Hi, I'm "</span>, p.name, <span class="val">"."</span>
-<span class="tab end">  </span>echo <span class="val">"I am "</span>, p.age, <span class="val">" years old."</span>
-
-<span class="kwd">let</span> p = <span class="typ">Person</span>(name:<span class="val">"Jon"</span>, age:<span class="val">18</span>)
-p.greet() <span class="cmt"># or greet(p)</span>
-</pre>
-             </div>
-          </div>
-          <div id="slide1" class="codeslide3">
-            <div>
-              <h2>C FFI is easy in Nim..</h2>
-<pre>
-<span class="cmt"># declare a C procedure..</span>
-<span class="kwd">proc</span> <span class="def">unsafeScanf</span>(f: <span class="typ">File</span>, s: <span class="typ">cstring</span>)
-<span class="tab">  </span>{.varargs,
-<span class="tab">    </span>importc: <span class="val">"fscanf"</span>,
-<span class="tab end">    </span>header: <span class="val">"&lt;stdio.h&gt;"</span>.}
-
-<span class="cmt"># ..and use it...</span>
-<span class="kwd">var</span> x: <span class="typ">cint</span>
-stdin.unsafeScanf(<span class="val">"%d"</span>, <span class="kwd">addr</span> x)
-</pre>
-              <p><span class="desc"><b>Compile and run with:</b><br>&nbsp;&nbsp;&nbsp;&nbsp;&#36; nim c -r example.nim</span></p>
-            </div>
-            <div>
-              <h2>..and DSLs are too...</h2>
-<pre>
-<span class="cmt"># a simple html server</span>
-<span class="kwd">import</span>
-  jester, asyncdispatch, htmlgen
-
-<span class="kwd">routes</span>:
-<span class="tab">  </span><span class="kwd">get</span> <span class="val">"/"</span>:
-<span class="tab end">  <span class="tab end">  </span></span><span class="kwd">resp</span> h1(<span class="val">"Hello world"</span>)
-
-runForever()
-</pre>
-              <p><span class="desc"><b>View in browser at:</b><br>&nbsp;&nbsp;&nbsp;&nbsp;localhost:5000</span></p>
-            </div>
-          </div>
-          <div id="slide2" class="niaslide">
-            <a href="news/e030_nim_in_action_in_production.html">
-              <img src="${rootDir}assets/niminaction/banner2.png" alt="A printed copy of Nim in Action should be available in March 2017!"/>
-            </a>
-          </div>
-          <div id="slide3" class="niaslide">
-            <a href="sponsors.html">
-              <img src="${rootDir}assets/bountysource/meet_sponsors.png" alt="Meet our BountySource sponsors!"/>
-            </a>
-          </div>
-        </div>
-        <div id="slideshow-nav">
-          <div id="slideControl0" onclick="slideshow_click(0)" class="active"></div>
-          <div id="slideControl1" onclick="slideshow_click(1)"></div>
-          <div id="slideControl2" onclick="slideshow_click(2)"></div>
-          <div id="slideControl3" onclick="slideshow_click(3)"></div>
-        </div>
-#  end
-        <aside id="sidebar">
-
-#  if len(c.links) > 0:
-          <h3>More Links</h3>
-          <div id="sidebar-links">
-#         for i in 0..c.links.len-1:
-          <a href="${c.links[i].val}" id="${c.links[i].id}">${c.links[i].key}</a>
-#         end for
-          </div>
-#  end if
-#  if len(c.ticker) > 0:
-					<h3 class="blue">Latest News</h3>
-					<div id="sidebar-news">
-          ${c.ticker % rootDir}
-					</div>
-#  end if
-				</aside>
-			</div>
-		</section>
-
-		<section id="body">
-			<div id="body-border"></div>
-			<div id="glow-line"></div>
-			<div class="page-layout">
-				<article id="content" class="page">
-				$content
-				</article>
-			</div>
-		</section>
-
-		<!--- #foot --->
-		<footer id="foot" class="home">
-			<div class="page-layout tall">
-				<div id="foot-links">
-					<div>
-						<h4>Documentation</h4>
-						<a href="${rootDir}documentation.html">Stable Documentation</a>
-						<a href="${rootDir}learn.html">Learning Resources</a>
-					<!--	<a href="">Development Documentation</a> -->
-						<a href="https://github.com/nim-lang/nim">Issues &amp; Requests</a>
-					</div>
-					<div>
-						<h4>Community</h4>
-						<a href="https://forum.nim-lang.org">User Forum</a>
-            <a href="http://webchat.freenode.net/?channels=nim">Online IRC</a>
-            <a href="https://irclogs.nim-lang.org/">IRC Logs</a>
-					</div>
-				</div>
-				<div id="foot-legal">
-					<h4>Written in Nim - Powered by <a href="https://github.com/dom96/jester">Jester</a></h4>
-					Web Design by <a href="http://reign-studios.net/philipwitte/">Philip Witte</a> &amp; <a href="http://picheta.me/">Dominik Picheta</a><br>
-					Copyright © 2017 - <a href="https://nim-lang.org/blog/">Andreas Rumpf</a> &amp; <a href="https://github.com/nim-lang/nim/graphs/contributors">Contributors</a>
-				</div>
-			</div>
-		</footer>
-
-#  if currentTab == "index":
-  <script src="${rootDir}assets/index.js"></script>
-# end if
-#  if c.gaId != nil:
-  <script>
-    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
-    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
-    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
-    })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
-
-    ga('create', '${c.gaId}', 'nim-lang.org');
-    ga('send', 'pageview');
-  </script>
-#  end if
-</body>
-</html>
-#end proc
-#
-#
-#proc generateSponsors(sponsors: seq[Sponsor]): string =
-#result = ""
-#for sponsor in sponsors:
-  <dt class="level-${sponsor.level}">
-    #if sponsor.url.len > 0:
-      <a href="${sponsor.url}" target="_blank">${sponsor.name}</a>
-    #else:
-      ${sponsor.name}
-    #end if
-  </dt>
-  <dd class="logo">
-    #if sponsor.logo.len > 0:
-    <a href="${sponsor.url}" target="_blank">
-      <img alt="${sponsor.name}'s logo" src="${sponsor.logo}"/>
-    </a>
-    #end if
-  </dd>
-  <dd class="this_month">
-    Donated <b>$$${sponsor.thisMonth}</b> this month
-  </dd>
-  <dd class="legend">
-    Donated $$${sponsor.allTime} in total since ${sponsor.since}
-  </dd>
-#end for
-#end proc
-#proc generateSponsorsPage(activeSponsors, inactiveSponsors: seq[Sponsor]): string =
-#result = ""
-<h1 id="our-current-sponsors">Our Current Sponsors</h1>
-<p>This section lists the companies and individuals that are, very kindly, contributing a
-monthly amount to help sustain Nim's development. For more details take a
-look at the <a href="https://salt.bountysource.com/teams/nim">Bountysource campaign</a>.</p>
-<p class="lastUpdate">Last updated: ${getTime().getGMTime().format("dd/MM/yyyy")}</p>
-<dl>
-${generateSponsors(activeSponsors)}
-</dl>
-#
-<h1 id="our-past-sponsors">Our Past Sponsors</h1>
-<p>This section lists the companies and individuals that have contributed
-money in the past to help sustain Nim's development. For more details take a
-look at the <a href="https://salt.bountysource.com/teams/nim">Bountysource campaign</a>.</p>
-<dl>
-${generateSponsors(inactiveSponsors)}
-</dl>
-#
-#end proc
diff --git a/web/assets/bountysource/bountysource.png b/web/assets/bountysource/bountysource.png
deleted file mode 100644
index d92d75df0..000000000
--- a/web/assets/bountysource/bountysource.png
+++ /dev/null
Binary files differdiff --git a/web/assets/bountysource/meet_sponsors.png b/web/assets/bountysource/meet_sponsors.png
deleted file mode 100644
index 67d7a1042..000000000
--- a/web/assets/bountysource/meet_sponsors.png
+++ /dev/null
Binary files differdiff --git a/web/assets/bountysource/meet_sponsors.xcf b/web/assets/bountysource/meet_sponsors.xcf
deleted file mode 100644
index e36560491..000000000
--- a/web/assets/bountysource/meet_sponsors.xcf
+++ /dev/null
Binary files differdiff --git a/web/assets/bountysource/secondspectrum.png b/web/assets/bountysource/secondspectrum.png
deleted file mode 100644
index 4dab35c6f..000000000
--- a/web/assets/bountysource/secondspectrum.png
+++ /dev/null
Binary files differdiff --git a/web/assets/bountysource/xored.png b/web/assets/bountysource/xored.png
deleted file mode 100644
index 7a07cdd31..000000000
--- a/web/assets/bountysource/xored.png
+++ /dev/null
Binary files differdiff --git a/web/assets/bountysource/xored.svg b/web/assets/bountysource/xored.svg
deleted file mode 100644
index 17a4acde6..000000000
--- a/web/assets/bountysource/xored.svg
+++ /dev/null
@@ -1 +0,0 @@
-<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 317 110" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><g id="Layer 1"><path d="M102.691,70.309c-1.469,2.545 -1.59,5.494 -0.593,8.032l-0.026,-0.015c0.988,2.533 0.863,5.473 -0.602,8.011c-1.465,2.537 -3.949,4.115 -6.637,4.526l0.029,0.017c-2.698,0.405 -5.193,1.984 -6.663,4.53c-2.561,4.436 -1.041,10.109 3.395,12.67c4.436,2.561 10.109,1.041 12.67,-3.395c1.47,-2.546 1.59,-5.497 0.592,-8.035l0.029,0.016c-0.988,-2.533 -0.864,-5.473 0.601,-8.011c1.465,-2.537 3.949,-4.115 6.637,-4.526l-0.026,-0.015c2.696,-0.406 5.19,-1.985 6.659,-4.53c2.561,-4.436 1.041,-10.109 -3.395,-12.67c-4.436,-2.561 -10.109,-1.041 -12.67,3.395M15.736,20.106c-1.469,2.545 -1.589,5.494 -0.593,8.032l-0.025,-0.015c0.987,2.533 0.862,5.473 -0.603,8.01c-1.465,2.538 -3.949,4.116 -6.637,4.527l0.029,0.017c-2.697,0.404 -5.193,1.984 -6.663,4.529c-2.561,4.437 -1.041,10.109 3.395,12.67c4.437,2.562 10.109,1.042 12.671,-3.394c1.469,-2.546 1.589,-5.497 0.591,-8.035l0.029,0.016c-0.988,-2.533 -0.864,-5.473 0.602,-8.011c1.465,-2.538 3.949,-4.115 6.636,-4.527l-0.026,-0.015c2.696,-0.405 5.19,-1.984 6.659,-4.529c2.562,-4.436 1.042,-10.109 -3.395,-12.67c-4.436,-2.561 -10.108,-1.042 -12.67,3.395M88.199,45.207c-1.47,2.546 -1.604,5.489 -0.606,8.027c0.987,2.533 0.849,5.465 -0.617,8.003c-1.465,2.537 -3.935,4.123 -6.622,4.535c-2.697,0.405 -5.178,1.992 -6.648,4.537c-1.47,2.546 -1.604,5.488 -0.606,8.027c0.988,2.533 0.849,5.465 -0.616,8.003c-1.465,2.537 -3.935,4.123 -6.623,4.534c-2.697,0.405 -5.178,1.992 -6.647,4.538c-2.562,4.436 -1.042,10.109 3.395,12.67c4.436,2.561 10.108,1.041 12.67,-3.395c1.469,-2.545 1.603,-5.488 0.606,-8.026c-0.988,-2.533 -0.85,-5.465 0.615,-8.003c1.465,-2.537 3.935,-4.124 6.623,-4.535c2.697,-0.405 5.178,-1.992 6.648,-4.538c1.47,-2.545 1.603,-5.487 0.606,-8.025c-0.988,-2.533 -0.849,-5.466 0.616,-8.003c1.465,-2.538 3.935,-4.124 6.622,-4.535c2.698,-0.405 5.179,-1.993 6.649,-4.538c2.561,-4.437 1.041,-10.109 -3.395,-12.671c-4.437,-2.561 -10.109,-1.041 -12.67,3.395M44.721,20.106c-1.47,2.545 -1.604,5.488 -0.606,8.026c0.988,2.533 0.849,5.465 -0.616,8.003c-1.465,2.538 -3.935,4.124 -6.623,4.535c-2.697,0.405 -5.178,1.992 -6.647,4.537c-1.47,2.546 -1.604,5.489 -0.606,8.027c0.988,2.533 0.849,5.465 -0.616,8.003c-1.465,2.537 -3.936,4.123 -6.623,4.535c-2.697,0.405 -5.178,1.992 -6.648,4.537c-2.561,4.436 -1.041,10.109 3.395,12.67c4.437,2.561 10.109,1.041 12.67,-3.395c1.47,-2.545 1.604,-5.487 0.606,-8.025c-0.987,-2.533 -0.849,-5.466 0.616,-8.003c1.465,-2.538 3.935,-4.124 6.623,-4.535c2.697,-0.405 5.178,-1.993 6.648,-4.538c1.469,-2.546 1.603,-5.488 0.606,-8.026c-0.988,-2.533 -0.85,-5.465 0.615,-8.003c1.466,-2.538 3.935,-4.124 6.623,-4.535c2.697,-0.405 5.179,-1.992 6.648,-4.538c2.562,-4.436 1.042,-10.109 -3.395,-12.67c-4.436,-2.561 -10.108,-1.042 -12.67,3.395M86.376,16.711c4.436,2.561 5.956,8.234 3.395,12.67c-1.469,2.546 -3.965,4.125 -6.662,4.53l0.028,0.016c-2.687,0.411 -5.171,1.989 -6.636,4.527c-1.466,2.537 -1.59,5.478 -0.602,8.011l-0.028,-0.016c0.997,2.538 0.877,5.488 -0.592,8.034c-1.469,2.544 -3.963,4.123 -6.659,4.529l0.026,0.015c-2.688,0.411 -5.172,1.989 -6.637,4.527c-1.465,2.537 -1.59,5.478 -0.602,8.011l-0.028,-0.017c0.998,2.539 0.878,5.489 -0.592,8.035c-1.47,2.546 -3.966,4.126 -6.664,4.53l0.029,0.017c-2.687,0.411 -5.171,1.989 -6.636,4.527c-1.465,2.537 -1.59,5.478 -0.602,8.011l-0.027,-0.015c0.997,2.537 0.877,5.487 -0.593,8.032c-2.561,4.436 -8.233,5.956 -12.67,3.395c-4.436,-2.561 -5.956,-8.234 -3.395,-12.67c1.47,-2.545 3.964,-4.124 6.661,-4.53l-0.027,-0.015c2.687,-0.411 5.171,-1.989 6.636,-4.527c1.466,-2.538 1.59,-5.478 0.602,-8.011l0.03,0.017c-0.999,-2.539 -0.879,-5.49 0.591,-8.036c1.47,-2.546 3.966,-4.125 6.663,-4.53l-0.029,-0.017c2.688,-0.411 5.172,-1.989 6.637,-4.526c1.465,-2.538 1.589,-5.478 0.602,-8.011l0.026,0.015c-0.997,-2.538 -0.876,-5.487 0.593,-8.032c1.469,-2.545 3.965,-4.124 6.662,-4.529l-0.028,-0.017c2.687,-0.411 5.171,-1.989 6.636,-4.526c1.465,-2.538 1.59,-5.478 0.602,-8.011l0.028,0.016c-0.997,-2.538 -0.878,-5.489 0.592,-8.034c2.561,-4.437 8.234,-5.956 12.67,-3.395" style="fill:#e02926;"/><path d="M115.407,55.761l18.066,-20.149l-18.066,-20.146l12.924,0l11.701,13.568l11.702,-13.568l12.927,0l-18.136,20.146l18.136,20.149l-12.927,0l-11.702,-13.511l-11.702,13.511l-12.923,0Z" style="fill:#1d1d1b;fill-rule:nonzero;"/><path d="M164.696,27.869l0.047,-1.456l0.159,-1.386l0.255,-1.296l0.36,-1.213l0.467,-1.123l0.578,-1.031l0.685,-0.938l0.79,-0.841l0.889,-0.741l1,-0.637l1.09,-0.538l1.193,-0.437l1.282,-0.335l1.372,-0.241l1.467,-0.141l1.549,-0.048l13.208,0l1.551,0.048l1.461,0.141l1.378,0.241l1.282,0.335l1.187,0.437l1.088,0.538l0.998,0.637l0.897,0.741l0.786,0.841l0.689,0.938l0.572,1.031l0.469,1.123l0.364,1.213l0.257,1.296l0.151,1.386l0.049,1.456l0,15.613l-0.049,1.446l-0.151,1.368l-0.257,1.29l-0.364,1.195l-0.469,1.113l-0.58,1.025l-0.681,0.926l-0.794,0.835l-0.895,0.729l-0.992,0.632l-1.096,0.531l-1.185,0.433l-1.284,0.332l-1.37,0.237l-1.461,0.138l-1.551,0.05l-13.208,0l-1.549,-0.05l-1.467,-0.141l-1.372,-0.242l-1.282,-0.334l-1.193,-0.435l-1.09,-0.537l-1,-0.641l-0.889,-0.737l-0.79,-0.842l-0.685,-0.937l-0.578,-1.03l-0.467,-1.124l-0.36,-1.209l-0.255,-1.299l-0.159,-1.386l-0.047,-1.461l0,-15.488ZM191.058,46.397l0.856,-0.051l0.697,-0.146l0.558,-0.228l0.426,-0.302l0.331,-0.38l0.243,-0.481l0.15,-0.604l0.056,-0.758l0,-15.543l-0.056,-0.789l-0.158,-0.631l-0.241,-0.504l-0.331,-0.395l-0.428,-0.314l-0.558,-0.237l-0.689,-0.148l-0.856,-0.056l-13.15,0l-0.86,0.056l-0.691,0.148l-0.552,0.237l-0.432,0.314l-0.333,0.395l-0.241,0.504l-0.152,0.631l-0.055,0.789l0,15.419l0.055,0.792l0.152,0.631l0.241,0.503l0.333,0.396l0.432,0.314l0.552,0.234l0.691,0.153l0.86,0.051l13.15,0Z" style="fill:#1d1d1b;fill-rule:nonzero;"/><path d="M219.436,55.761l-9.891,0l0,-27.893l0.047,-1.456l0.152,-1.386l0.255,-1.296l0.364,-1.213l0.469,-1.123l0.572,-1.031l0.685,-0.938l0.793,-0.84l0.894,-0.742l0.994,-0.637l1.095,-0.538l1.186,-0.437l1.28,-0.335l1.374,-0.241l1.467,-0.141l1.551,-0.048l6.604,0l0,9.364l-6.571,0l-0.859,0.055l-0.696,0.148l-0.551,0.237l-0.43,0.315l-0.329,0.395l-0.241,0.504l-0.161,0.63l-0.053,0.789l0,27.858Z" style="fill:#1d1d1b;fill-rule:nonzero;"/><path d="M262.96,27.98l-0.053,0.758l-0.153,0.606l-0.24,0.479l-0.332,0.38l-0.427,0.302l-0.558,0.228l-0.695,0.149l-0.856,0.051l-16.467,0l0,-3.029l0.056,-0.789l0.152,-0.631l0.241,-0.504l0.331,-0.395l0.434,-0.314l0.551,-0.237l0.69,-0.148l0.862,-0.056l13.15,0l0.856,0.056l0.687,0.148l0.559,0.237l0.428,0.314l0.332,0.395l0.24,0.504l0.159,0.631l0.053,0.789l0,0.076ZM272.804,26.412l-0.15,-1.385l-0.257,-1.296l-0.364,-1.213l-0.469,-1.123l-0.572,-1.031l-0.689,-0.938l-0.786,-0.841l-0.897,-0.741l-1,-0.637l-1.088,-0.538l-1.185,-0.437l-1.282,-0.335l-1.372,-0.241l-1.469,-0.141l-1.549,-0.048l-13.206,0l-1.551,0.048l-1.469,0.141l-1.371,0.241l-1.283,0.335l-1.191,0.437l-1.091,0.538l-0.991,0.637l-0.897,0.741l-0.792,0.841l-0.683,0.938l-0.578,1.031l-0.469,1.123l-0.358,1.213l-0.255,1.296l-0.159,1.385l-0.047,1.457l0,15.488l0.047,1.461l0.159,1.385l0.255,1.3l0.358,1.209l0.469,1.124l0.578,1.03l0.683,0.937l0.792,0.842l0.897,0.737l0.991,0.641l1.091,0.537l1.191,0.435l1.283,0.333l1.371,0.243l1.469,0.141l1.551,0.05l23.089,0l0,-9.365l-23.062,0l-0.862,-0.052l-0.69,-0.152l-0.551,-0.234l-0.434,-0.314l-0.331,-0.396l-0.241,-0.503l-0.152,-0.631l-0.056,-0.792l0,-3.03l16.496,0l1.549,-0.044l1.463,-0.141l1.378,-0.235l1.275,-0.335l1.185,-0.43l1.095,-0.531l0.994,-0.631l0.894,-0.733l0.795,-0.831l0.68,-0.93l0.581,-1.021l0.469,-1.112l0.364,-1.2l0.257,-1.285l0.15,-1.373l0.056,-1.446l0,-0.146l-0.056,-1.457Z" style="fill:#1d1d1b;fill-rule:nonzero;"/><path d="M307.093,44.115l-0.158,0.63l-0.241,0.504l-0.329,0.395l-0.428,0.314l-0.56,0.235l-0.689,0.152l-0.854,0.051l-13.15,0l-0.862,-0.051l-0.689,-0.152l-0.56,-0.235l-0.428,-0.314l-0.329,-0.395l-0.243,-0.504l-0.156,-0.63l-0.056,-0.792l0,-15.419l0.056,-0.79l0.156,-0.63l0.243,-0.504l0.329,-0.395l0.428,-0.315l0.56,-0.236l0.689,-0.149l0.862,-0.055l16.467,0l0,18.493l-0.058,0.792ZM307.151,0l0,15.466l-16.502,0l-1.551,0.049l-1.461,0.14l-1.38,0.241l-1.274,0.335l-1.193,0.438l-1.088,0.538l-1,0.636l-0.895,0.742l-0.788,0.84l-0.687,0.939l-0.572,1.03l-0.469,1.123l-0.366,1.213l-0.254,1.296l-0.154,1.386l-0.047,1.457l0,15.488l0.047,1.461l0.154,1.385l0.254,1.299l0.366,1.21l0.469,1.123l0.572,1.031l0.687,0.937l0.794,0.841l0.889,0.738l1,0.641l1.088,0.537l1.193,0.435l1.274,0.333l1.38,0.243l1.461,0.141l1.551,0.049l13.212,0l1.545,-0.049l1.467,-0.141l1.372,-0.243l1.282,-0.333l1.193,-0.438l1.088,-0.534l1,-0.641l0.889,-0.738l0.792,-0.841l0.683,-0.937l0.58,-1.031l0.469,-1.123l0.358,-1.213l0.261,-1.296l0.153,-1.385l0.047,-1.461l0,-43.357l-9.889,0Z" style="fill:#1d1d1b;fill-rule:nonzero;"/></g></svg>
\ No newline at end of file
diff --git a/web/assets/images/bg.png b/web/assets/images/bg.png
deleted file mode 100644
index 91f335913..000000000
--- a/web/assets/images/bg.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/docs-articles.png b/web/assets/images/docs-articles.png
deleted file mode 100644
index 7f800ea33..000000000
--- a/web/assets/images/docs-articles.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/docs-examples.png b/web/assets/images/docs-examples.png
deleted file mode 100644
index e6d27e034..000000000
--- a/web/assets/images/docs-examples.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/docs-internals.png b/web/assets/images/docs-internals.png
deleted file mode 100644
index e03a952d5..000000000
--- a/web/assets/images/docs-internals.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/docs-libraries.png b/web/assets/images/docs-libraries.png
deleted file mode 100644
index b14952f7d..000000000
--- a/web/assets/images/docs-libraries.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/docs-tools.png b/web/assets/images/docs-tools.png
deleted file mode 100644
index d83f0faaa..000000000
--- a/web/assets/images/docs-tools.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/docs-tutorials.png b/web/assets/images/docs-tutorials.png
deleted file mode 100644
index 926a4b58b..000000000
--- a/web/assets/images/docs-tutorials.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/favicon.ico b/web/assets/images/favicon.ico
deleted file mode 100644
index aed71adb4..000000000
--- a/web/assets/images/favicon.ico
+++ /dev/null
Binary files differdiff --git a/web/assets/images/foot.png b/web/assets/images/foot.png
deleted file mode 100644
index dc2561cf3..000000000
--- a/web/assets/images/foot.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/glow-arrow.png b/web/assets/images/glow-arrow.png
deleted file mode 100644
index 436d32f03..000000000
--- a/web/assets/images/glow-arrow.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/glow-line.png b/web/assets/images/glow-line.png
deleted file mode 100644
index 6607bdee9..000000000
--- a/web/assets/images/glow-line.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/head-link.png b/web/assets/images/head-link.png
deleted file mode 100644
index d97cba5b8..000000000
--- a/web/assets/images/head-link.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/head-link_hover.png b/web/assets/images/head-link_hover.png
deleted file mode 100644
index 27edf3b05..000000000
--- a/web/assets/images/head-link_hover.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/head.png b/web/assets/images/head.png
deleted file mode 100644
index 009f86728..000000000
--- a/web/assets/images/head.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/link_aporia.png b/web/assets/images/link_aporia.png
deleted file mode 100644
index 145e5ddf2..000000000
--- a/web/assets/images/link_aporia.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/link_forum.png b/web/assets/images/link_forum.png
deleted file mode 100644
index 2973b42bc..000000000
--- a/web/assets/images/link_forum.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/link_nimbuild.png b/web/assets/images/link_nimbuild.png
deleted file mode 100644
index 4b3f943fe..000000000
--- a/web/assets/images/link_nimbuild.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/logo.png b/web/assets/images/logo.png
deleted file mode 100644
index 85d3d2e51..000000000
--- a/web/assets/images/logo.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/mascot.png b/web/assets/images/mascot.png
deleted file mode 100644
index 9beb62c01..000000000
--- a/web/assets/images/mascot.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/more-links_editors.png b/web/assets/images/more-links_editors.png
deleted file mode 100644
index f5970ff1f..000000000
--- a/web/assets/images/more-links_editors.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/more-links_forum.png b/web/assets/images/more-links_forum.png
deleted file mode 100644
index f33277777..000000000
--- a/web/assets/images/more-links_forum.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/more-links_github.png b/web/assets/images/more-links_github.png
deleted file mode 100644
index 4a6a844f4..000000000
--- a/web/assets/images/more-links_github.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/more-links_nimbuild.png b/web/assets/images/more-links_nimbuild.png
deleted file mode 100644
index 473fbe4cd..000000000
--- a/web/assets/images/more-links_nimbuild.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/nim-logo.svg b/web/assets/images/nim-logo.svg
deleted file mode 100644
index feae6c3d7..000000000
--- a/web/assets/images/nim-logo.svg
+++ /dev/null
@@ -1,500 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:xlink="http://www.w3.org/1999/xlink"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="163.84375"
-   height="124.67096"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.48.5 r10040"
-   sodipodi:docname="New document 1">
-  <defs
-     id="defs4">
-    <linearGradient
-       id="linearGradient3833">
-      <stop
-         style="stop-color:#f7c41a;stop-opacity:1;"
-         offset="0"
-         id="stop3835" />
-      <stop
-         style="stop-color:#bd9510;stop-opacity:1;"
-         offset="1"
-         id="stop3837" />
-    </linearGradient>
-    <linearGradient
-       id="linearGradient3825">
-      <stop
-         style="stop-color:#f3d673;stop-opacity:1;"
-         offset="0"
-         id="stop3827" />
-      <stop
-         style="stop-color:#c7a23a;stop-opacity:1;"
-         offset="1"
-         id="stop3829" />
-    </linearGradient>
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3825"
-       id="radialGradient3831"
-       cx="406.47156"
-       cy="414.24271"
-       fx="406.47156"
-       fy="414.24271"
-       r="80.321426"
-       gradientTransform="matrix(-0.01492027,-0.91203843,0.99426233,-0.01626537,1.3722368,813.17816)"
-       gradientUnits="userSpaceOnUse" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3833"
-       id="radialGradient3839"
-       cx="407.17349"
-       cy="414.17612"
-       fx="407.17349"
-       fy="414.17612"
-       r="80.321426"
-       gradientTransform="matrix(-0.0234034,-1.430605,0.9942623,-0.01626526,4.9030359,1024.9638)"
-       gradientUnits="userSpaceOnUse" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3833-7"
-       id="radialGradient3839-8"
-       cx="407.17349"
-       cy="414.17612"
-       fx="407.17349"
-       fy="414.17612"
-       r="80.321426"
-       gradientTransform="matrix(-0.0234034,-1.430605,0.9942623,-0.01626526,4.9030359,1024.9638)"
-       gradientUnits="userSpaceOnUse" />
-    <linearGradient
-       id="linearGradient3833-7">
-      <stop
-         style="stop-color:#f4d779;stop-opacity:1;"
-         offset="0"
-         id="stop3835-5" />
-      <stop
-         style="stop-color:#bd9510;stop-opacity:1;"
-         offset="1"
-         id="stop3837-4" />
-    </linearGradient>
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3825-5"
-       id="radialGradient3831-8"
-       cx="406.47156"
-       cy="414.24271"
-       fx="406.47156"
-       fy="414.24271"
-       r="80.321426"
-       gradientTransform="matrix(-0.01492027,-0.91203843,0.99426233,-0.01626537,1.3722368,813.17816)"
-       gradientUnits="userSpaceOnUse" />
-    <linearGradient
-       id="linearGradient3825-5">
-      <stop
-         style="stop-color:#f3d673;stop-opacity:1;"
-         offset="0"
-         id="stop3827-3" />
-      <stop
-         style="stop-color:#c7a23a;stop-opacity:1;"
-         offset="1"
-         id="stop3829-7" />
-    </linearGradient>
-    <radialGradient
-       r="80.321426"
-       fy="414.17612"
-       fx="407.17349"
-       cy="414.17612"
-       cx="407.17349"
-       gradientTransform="matrix(-0.0234034,-1.430605,0.9942623,-0.01626526,-153.01531,1007.7817)"
-       gradientUnits="userSpaceOnUse"
-       id="radialGradient3870"
-       xlink:href="#linearGradient3833-7"
-       inkscape:collect="always" />
-    <radialGradient
-       r="80.321426"
-       fy="414.24271"
-       fx="406.47156"
-       cy="414.24271"
-       cx="406.47156"
-       gradientTransform="matrix(-0.01492027,-0.91203843,0.99426233,-0.01626537,-156.54611,795.9961)"
-       gradientUnits="userSpaceOnUse"
-       id="radialGradient3872"
-       xlink:href="#linearGradient3825-5"
-       inkscape:collect="always" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3833-7"
-       id="radialGradient3908"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.0234034,-1.430605,0.9942623,-0.01626526,-153.01531,1007.7817)"
-       cx="407.17349"
-       cy="414.17612"
-       fx="407.17349"
-       fy="414.17612"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3825-5"
-       id="radialGradient3910"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.01492027,-0.91203843,0.99426233,-0.01626537,-156.54611,795.9961)"
-       cx="406.47156"
-       cy="414.24271"
-       fx="406.47156"
-       fy="414.24271"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3833-7"
-       id="radialGradient3914"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.0234034,-1.430605,0.9942623,-0.01626526,-153.01531,1007.7817)"
-       cx="407.17349"
-       cy="414.17612"
-       fx="407.17349"
-       fy="414.17612"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3825-5"
-       id="radialGradient3916"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.01492027,-0.91203843,0.99426233,-0.01626537,-156.54611,795.9961)"
-       cx="406.47156"
-       cy="414.24271"
-       fx="406.47156"
-       fy="414.24271"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3833-7"
-       id="radialGradient3918"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.0234034,-1.430605,0.9942623,-0.01626526,-153.01531,1007.7817)"
-       cx="407.17349"
-       cy="414.17612"
-       fx="407.17349"
-       fy="414.17612"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3825-5"
-       id="radialGradient3920"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.01492027,-0.91203843,0.99426233,-0.01626537,-156.54611,795.9961)"
-       cx="406.47156"
-       cy="414.24271"
-       fx="406.47156"
-       fy="414.24271"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3833-7"
-       id="radialGradient3924"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.0234034,-1.430605,0.9942623,-0.01626526,-153.01531,1007.7817)"
-       cx="407.17349"
-       cy="414.17612"
-       fx="407.17349"
-       fy="414.17612"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3825-5"
-       id="radialGradient3926"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.01492027,-0.91203843,0.99426233,-0.01626537,-156.54611,795.9961)"
-       cx="406.47156"
-       cy="414.24271"
-       fx="406.47156"
-       fy="414.24271"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3825-5"
-       id="radialGradient3933"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.01462336,-0.89388887,0.97447651,-0.01594169,19.13339,796.92442)"
-       cx="406.47156"
-       cy="414.24271"
-       fx="406.47156"
-       fy="414.24271"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3833"
-       id="radialGradient3935"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.0234034,-1.430605,0.9942623,-0.01626526,4.9030359,1024.9638)"
-       cx="407.17349"
-       cy="414.17612"
-       fx="407.17349"
-       fy="414.17612"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3825"
-       id="radialGradient3937"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.01492027,-0.91203843,0.99426233,-0.01626537,1.3722368,813.17816)"
-       cx="406.47156"
-       cy="414.24271"
-       fx="406.47156"
-       fy="414.24271"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3833"
-       id="radialGradient3941"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.0234034,-1.430605,0.9942623,-0.01626526,-75.408823,1015.0037)"
-       cx="407.17349"
-       cy="414.17612"
-       fx="407.17349"
-       fy="414.17612"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3825"
-       id="radialGradient3943"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.01492027,-0.91203843,0.99426233,-0.01626537,-78.939622,803.21805)"
-       cx="406.47156"
-       cy="414.24271"
-       fx="406.47156"
-       fy="414.24271"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3825-5"
-       id="radialGradient3949"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.01462336,-0.89388887,0.97447651,-0.01594169,19.13339,796.92442)"
-       cx="406.47156"
-       cy="414.24271"
-       fx="406.47156"
-       fy="414.24271"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3825-5"
-       id="radialGradient3953"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.01462336,-0.89388887,0.97447651,-0.01594169,19.13339,796.92442)"
-       cx="406.47156"
-       cy="414.24271"
-       fx="406.47156"
-       fy="414.24271"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3825-5"
-       id="radialGradient3957"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.01462336,-0.89388887,0.97447651,-0.01594169,-70.86661,796.92442)"
-       cx="406.47156"
-       cy="414.24271"
-       fx="406.47156"
-       fy="414.24271"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3825-5"
-       id="radialGradient3961"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.01462336,-0.89388887,0.97447651,-0.01594169,-70.86661,796.92442)"
-       cx="406.47156"
-       cy="414.24271"
-       fx="406.47156"
-       fy="414.24271"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3833-79"
-       id="radialGradient3941-1"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.0234034,-1.430605,0.9942623,-0.01626526,-75.408823,1015.0037)"
-       cx="407.17349"
-       cy="414.17612"
-       fx="407.17349"
-       fy="414.17612"
-       r="80.321426" />
-    <linearGradient
-       id="linearGradient3833-79">
-      <stop
-         style="stop-color:#f4d779;stop-opacity:1;"
-         offset="0"
-         id="stop3835-55" />
-      <stop
-         style="stop-color:#bd9510;stop-opacity:1;"
-         offset="1"
-         id="stop3837-5" />
-    </linearGradient>
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3825-3"
-       id="radialGradient3943-5"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.01492027,-0.91203843,0.99426233,-0.01626537,-78.939622,803.21805)"
-       cx="406.47156"
-       cy="414.24271"
-       fx="406.47156"
-       fy="414.24271"
-       r="80.321426" />
-    <linearGradient
-       id="linearGradient3825-3">
-      <stop
-         style="stop-color:#f3d673;stop-opacity:1;"
-         offset="0"
-         id="stop3827-0" />
-      <stop
-         style="stop-color:#c7a23a;stop-opacity:1;"
-         offset="1"
-         id="stop3829-2" />
-    </linearGradient>
-    <radialGradient
-       r="80.321426"
-       fy="414.24271"
-       fx="406.47156"
-       cy="414.24271"
-       cx="406.47156"
-       gradientTransform="matrix(-0.01492027,-0.91203843,0.99426233,-0.01626537,91.271992,815.08835)"
-       gradientUnits="userSpaceOnUse"
-       id="radialGradient3985"
-       xlink:href="#linearGradient3825-3"
-       inkscape:collect="always" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3833"
-       id="radialGradient4029"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.0234034,-1.430605,0.9942623,-0.01626526,-75.408823,1015.0037)"
-       cx="407.17349"
-       cy="414.17612"
-       fx="407.17349"
-       fy="414.17612"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3825"
-       id="radialGradient4031"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.01492027,-0.91203843,0.99426233,-0.01626537,-78.939622,803.21805)"
-       cx="406.47156"
-       cy="414.24271"
-       fx="406.47156"
-       fy="414.24271"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3833"
-       id="radialGradient4037"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.0234034,-1.430605,0.9942623,-0.01626526,71.056847,1015.1237)"
-       cx="407.17349"
-       cy="414.17612"
-       fx="407.17349"
-       fy="414.17612"
-       r="80.321426" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3833"
-       id="radialGradient4047"
-       gradientUnits="userSpaceOnUse"
-       gradientTransform="matrix(-0.0234034,-1.430605,0.9942623,-0.01626526,71.056847,1015.1237)"
-       cx="407.17349"
-       cy="414.17612"
-       fx="407.17349"
-       fy="414.17612"
-       r="80.321426" />
-  </defs>
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#595959"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="0.7"
-     inkscape:cx="-296.38002"
-     inkscape:cy="91.425689"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer2"
-     showgrid="false"
-     fit-margin-top="0"
-     fit-margin-left="0"
-     fit-margin-right="0"
-     fit-margin-bottom="0"
-     inkscape:window-width="1920"
-     inkscape:window-height="1027"
-     inkscape:window-x="0"
-     inkscape:window-y="29"
-     inkscape:window-maximized="1" />
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     sodipodi:insensitive="true"
-     style="display:none"
-     transform="translate(-391.875,-406.1875)">
-    <image
-       y="392.14789"
-       x="-90.714287"
-       id="image3047"
-       xlink:href="file:///home/tomasi/Projects/nimrod/logo/new-symbols.png"
-       height="329"
-       width="800" />
-  </g>
-  <g
-     inkscape:groupmode="layer"
-     id="layer2"
-     inkscape:label="Layer"
-     style="display:inline"
-     transform="translate(-391.875,-406.1875)">
-    <g
-       id="g4041">
-      <path
-         id="path4019"
-         d="m 474.37925,408.23965 c 0,0 -6.12468,5.3771 -12.34375,10.6875 -6.39764,-0.22532 -18.88846,1.38269 -25.6875,4.125 -6.26333,-4.40055 -11.8125,-9.28125 -11.8125,-9.28125 0,0 -4.69884,9.01564 -7.65625,14.28125 -4.38598,2.58703 -8.76277,5.43142 -12.6875,9.28125 -4.63902,-2.04302 -10.1875,-4.65625 -10.1875,-4.65625 l 6.25,27.875 0.9375,1.875 -1.09375,-1.875 c 0,0 8.86172,24.01192 14.8125,40 25.2159,36.89492 89.61617,39.46428 117.68751,0.71875 5.37871,-13.44336 11.62618,-31.71161 13.90625,-38.4375 l 1.25,-2.8125 5.90625,-26.78125 c 0,0 -6.87234,2.50886 -11.0625,4.28125 -2.40446,-3.40619 -6.05177,-7.01378 -11.25,-9.46875 -3.05538,-6.20497 -7.5,-14.65625 -7.5,-14.65625 0,0 -5.33268,4.38488 -11.4375,9.125 -8.24767,-1.68845 -18.23488,-3.72666 -26.62501,-3.21875 -5.71156,-5.20637 -11.40625,-11.0625 -11.40625,-11.0625 z"
-         style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
-         inkscape:connector-curvature="0" />
-      <path
-         sodipodi:nodetypes="cccccccccccccccccccc"
-         inkscape:connector-curvature="0"
-         id="path3054"
-         d="m 400.26095,460.55777 -6.25,-27.85715 c 0,0 5.53955,2.59984 10.17857,4.64286 3.92473,-3.84983 8.2926,-6.69868 12.67858,-9.28571 2.95741,-5.26561 7.67857,-14.28572 7.67857,-14.28572 0,0 5.52238,4.88517 11.78571,9.28572 6.79904,-2.74231 19.31665,-4.33247 25.71429,-4.10715 6.21906,-5.3104 12.32143,-10.71428 12.32143,-10.71428 0,0 5.71701,5.86506 11.42857,11.07143 8.39013,-0.50791 18.35947,1.52583 26.60714,3.21428 6.10482,-4.74012 11.42857,-9.10714 11.42857,-9.10714 0,0 4.44462,8.43789 7.5,14.64286 5.19824,2.45497 8.84554,6.05809 11.25,9.46428 4.19017,-1.77239 11.07143,-4.28571 11.07143,-4.28571 l -5.89286,26.78571 -6.60714,14.82143 c 0,0 -4.31067,-2.70091 -7.32143,-4.28571 -16.93933,-45.69195 -106.71744,-37.02003 -119.46428,-0.71429 -5.78255,1.30574 -8.39286,2.32143 -8.39286,2.32143 z"
-         style="fill:url(#radialGradient4047);fill-opacity:1;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
-      <path
-         sodipodi:nodetypes="cccccccccccccc"
-         inkscape:connector-curvature="0"
-         id="path3051"
-         d="m 400.08238,460.55777 c 0,0 8.87064,24.01192 14.82143,40 25.2159,36.89492 89.60723,39.45981 117.67857,0.71428 6.64277,-16.60268 15,-41.60714 15,-41.60714 l -6.07143,11.42857 -7.63525,-0.35714 -11.82903,15 -16.96429,5 -31.07143,-17.85715 -31.25,17.5 -16.96428,-4.82142 -11.54402,-15.59885 -7.56312,2.02742 z"
-         style="fill:#ffffff;stroke:none" />
-      <path
-         sodipodi:nodetypes="scccccccccs"
-         id="path3054-8"
-         d="m 474.88371,439.45394 c 26.96263,-0.0368 50.75931,9.32331 58.9643,31.09821 0.11438,0.0602 0.25716,0.12428 0.375,0.1875 l -0.34375,0 -11.84375,15 -16.93751,5 -31.09375,-17.875 -31.25,17.5 -16.96875,-4.8125 -11.31751,-16.02955 c 6.01205,-17.4758 32.67194,-30.03082 60.41572,-30.06866 z"
-         style="fill:none;stroke:#000000;stroke-width:1.10000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
-         inkscape:connector-curvature="0" />
-    </g>
-  </g>
-</svg>
diff --git a/web/assets/images/quote.png b/web/assets/images/quote.png
deleted file mode 100644
index e9426158c..000000000
--- a/web/assets/images/quote.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/quotes.png b/web/assets/images/quotes.png
deleted file mode 100644
index 0d75b4cc2..000000000
--- a/web/assets/images/quotes.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/sidebar.png b/web/assets/images/sidebar.png
deleted file mode 100644
index 142db93cf..000000000
--- a/web/assets/images/sidebar.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/sidebar_h2.png b/web/assets/images/sidebar_h2.png
deleted file mode 100644
index d1409b57f..000000000
--- a/web/assets/images/sidebar_h2.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/sidebar_head.png b/web/assets/images/sidebar_head.png
deleted file mode 100644
index 05885d9f3..000000000
--- a/web/assets/images/sidebar_head.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/site_foot.png b/web/assets/images/site_foot.png
deleted file mode 100644
index a2efa0460..000000000
--- a/web/assets/images/site_foot.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/site_neck.png b/web/assets/images/site_neck.png
deleted file mode 100644
index d4f42c6b7..000000000
--- a/web/assets/images/site_neck.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/slideshow-nav.png b/web/assets/images/slideshow-nav.png
deleted file mode 100644
index fbfff3e5d..000000000
--- a/web/assets/images/slideshow-nav.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/slideshow-nav_active.png b/web/assets/images/slideshow-nav_active.png
deleted file mode 100644
index 525caf355..000000000
--- a/web/assets/images/slideshow-nav_active.png
+++ /dev/null
Binary files differdiff --git a/web/assets/images/tabEnd.png b/web/assets/images/tabEnd.png
deleted file mode 100644
index a94af90f1..000000000
--- a/web/assets/images/tabEnd.png
+++ /dev/null
Binary files differdiff --git a/web/assets/index.js b/web/assets/index.js
deleted file mode 100644
index 4c8b90f95..000000000
--- a/web/assets/index.js
+++ /dev/null
@@ -1,44 +0,0 @@
-"use strict";
-
-var timer;
-var prevIndex = 0;
-var slideCount = 4;
-
-function modifyActive(el, add) {
-  var element = document.getElementById(el);
-  if (add) {
-    element.className = element.className + " active";
-  }
-  else {
-    element.className = element.className.replace("active", "");
-  }
-}
-
-function setSlideShow(index, short) {
-  if (index >= slideCount) index = 0;
-  modifyActive("slide" + prevIndex, false);
-  modifyActive("slide" + index, true);
-  modifyActive("slideControl" + prevIndex, false);
-  modifyActive("slideControl" + index, true);
-  prevIndex = index;
-  startTimer(short ? 8000 : 32000);
-}
-
-function nextSlide() { setSlideShow(prevIndex + 1, true); }
-function startTimer(t) { timer = setTimeout(nextSlide, t); }
-
-function slideshow_enter() { clearTimeout(timer); }
-function slideshow_exit () { startTimer(16000); }
-
-function slideshow_click(index) {
-  clearTimeout(timer);
-  setSlideShow(index, false);
-}
-
-window.onload = function() {
-  var slideshow = document.getElementById("slideshow");
-  slideshow.onmouseenter = slideshow_enter;
-  slideshow.onmouseleave = slideshow_exit;
-  slideCount = slideshow.children.length;
-  startTimer(8000);
-};
diff --git a/web/assets/news/images/0.15.0/doc_search.gif b/web/assets/news/images/0.15.0/doc_search.gif
deleted file mode 100644
index ac757b404..000000000
--- a/web/assets/news/images/0.15.0/doc_search.gif
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/0.15.0/doc_sort.gif b/web/assets/news/images/0.15.0/doc_sort.gif
deleted file mode 100644
index edd253c4a..000000000
--- a/web/assets/news/images/0.15.0/doc_sort.gif
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/0.16.0/nimble.png b/web/assets/news/images/0.16.0/nimble.png
deleted file mode 100644
index 8faa3f04f..000000000
--- a/web/assets/news/images/0.16.0/nimble.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/10_needs.png b/web/assets/news/images/survey/10_needs.png
deleted file mode 100644
index 67d568552..000000000
--- a/web/assets/news/images/survey/10_needs.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/book.png b/web/assets/news/images/survey/book.png
deleted file mode 100644
index 5bb418e63..000000000
--- a/web/assets/news/images/survey/book.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/book_opinion.png b/web/assets/news/images/survey/book_opinion.png
deleted file mode 100644
index 4e56ab26e..000000000
--- a/web/assets/news/images/survey/book_opinion.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/breakage.png b/web/assets/news/images/survey/breakage.png
deleted file mode 100644
index 5eb4c5289..000000000
--- a/web/assets/news/images/survey/breakage.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/dev_os.png b/web/assets/news/images/survey/dev_os.png
deleted file mode 100644
index 088918dc3..000000000
--- a/web/assets/news/images/survey/dev_os.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/difficulty_fixing_breakage.png b/web/assets/news/images/survey/difficulty_fixing_breakage.png
deleted file mode 100644
index 022aa00ed..000000000
--- a/web/assets/news/images/survey/difficulty_fixing_breakage.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/do_you_use_nim.png b/web/assets/news/images/survey/do_you_use_nim.png
deleted file mode 100644
index 257148325..000000000
--- a/web/assets/news/images/survey/do_you_use_nim.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/domains.png b/web/assets/news/images/survey/domains.png
deleted file mode 100644
index 50b1ed7ff..000000000
--- a/web/assets/news/images/survey/domains.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/editors.png b/web/assets/news/images/survey/editors.png
deleted file mode 100644
index 816ad515f..000000000
--- a/web/assets/news/images/survey/editors.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/ex_nim.png b/web/assets/news/images/survey/ex_nim.png
deleted file mode 100644
index 50082ea8b..000000000
--- a/web/assets/news/images/survey/ex_nim.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/languages.png b/web/assets/news/images/survey/languages.png
deleted file mode 100644
index db35f9bd4..000000000
--- a/web/assets/news/images/survey/languages.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/learning_resources.png b/web/assets/news/images/survey/learning_resources.png
deleted file mode 100644
index 39f533ad0..000000000
--- a/web/assets/news/images/survey/learning_resources.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/nim_appeal.png b/web/assets/news/images/survey/nim_appeal.png
deleted file mode 100644
index 4f53e1447..000000000
--- a/web/assets/news/images/survey/nim_appeal.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/nim_at_work.png b/web/assets/news/images/survey/nim_at_work.png
deleted file mode 100644
index f00ab1a94..000000000
--- a/web/assets/news/images/survey/nim_at_work.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/nim_displeasing.png b/web/assets/news/images/survey/nim_displeasing.png
deleted file mode 100644
index b7232df04..000000000
--- a/web/assets/news/images/survey/nim_displeasing.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/nim_domains.png b/web/assets/news/images/survey/nim_domains.png
deleted file mode 100644
index 2d8fc6652..000000000
--- a/web/assets/news/images/survey/nim_domains.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/nim_found.png b/web/assets/news/images/survey/nim_found.png
deleted file mode 100644
index a0a65b813..000000000
--- a/web/assets/news/images/survey/nim_found.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/nim_time.png b/web/assets/news/images/survey/nim_time.png
deleted file mode 100644
index 23bc4a136..000000000
--- a/web/assets/news/images/survey/nim_time.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/nim_time_rust.png b/web/assets/news/images/survey/nim_time_rust.png
deleted file mode 100644
index 9b861608a..000000000
--- a/web/assets/news/images/survey/nim_time_rust.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/nim_versions.png b/web/assets/news/images/survey/nim_versions.png
deleted file mode 100644
index ba382c93d..000000000
--- a/web/assets/news/images/survey/nim_versions.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/nimble_opinion.png b/web/assets/news/images/survey/nimble_opinion.png
deleted file mode 100644
index 3fe76326e..000000000
--- a/web/assets/news/images/survey/nimble_opinion.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/non_user.png b/web/assets/news/images/survey/non_user.png
deleted file mode 100644
index b5324b69c..000000000
--- a/web/assets/news/images/survey/non_user.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/planning_to_use_at_work.png b/web/assets/news/images/survey/planning_to_use_at_work.png
deleted file mode 100644
index b3e2001c3..000000000
--- a/web/assets/news/images/survey/planning_to_use_at_work.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/project_size.png b/web/assets/news/images/survey/project_size.png
deleted file mode 100644
index ad1359d0c..000000000
--- a/web/assets/news/images/survey/project_size.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/project_size_nim_rust.png b/web/assets/news/images/survey/project_size_nim_rust.png
deleted file mode 100644
index 4bc0e6b47..000000000
--- a/web/assets/news/images/survey/project_size_nim_rust.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/project_size_work.png b/web/assets/news/images/survey/project_size_work.png
deleted file mode 100644
index fab6e52f2..000000000
--- a/web/assets/news/images/survey/project_size_work.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/reliability.png b/web/assets/news/images/survey/reliability.png
deleted file mode 100644
index 1767e9803..000000000
--- a/web/assets/news/images/survey/reliability.png
+++ /dev/null
Binary files differdiff --git a/web/assets/news/images/survey/target_os.png b/web/assets/news/images/survey/target_os.png
deleted file mode 100644
index a36915af1..000000000
--- a/web/assets/news/images/survey/target_os.png
+++ /dev/null
Binary files differdiff --git a/web/assets/niminaction/banner.jpg b/web/assets/niminaction/banner.jpg
deleted file mode 100644
index b2fb3efc9..000000000
--- a/web/assets/niminaction/banner.jpg
+++ /dev/null
Binary files differdiff --git a/web/assets/niminaction/banner2.png b/web/assets/niminaction/banner2.png
deleted file mode 100644
index 3cabd195d..000000000
--- a/web/assets/niminaction/banner2.png
+++ /dev/null
Binary files differdiff --git a/web/assets/style.css b/web/assets/style.css
deleted file mode 100644
index 2e166530d..000000000
--- a/web/assets/style.css
+++ /dev/null
@@ -1,630 +0,0 @@
-
-* { cursor:default; }
-a, a * { cursor:pointer; }
-
-html { margin:0; overflow-x:auto; }
-body {
-	overflow-x:hidden;
-	min-width:1030px;
-	margin:0;
-	font:13pt "arial";
-	background:#152534 url("images/bg.png") no-repeat center top;
-	color:rgba(0,0,0,.8); }
-
-pre {
-  color:#fff;
-  margin:0;
-  padding:15px 10px;
-  font:10pt monospace;
-  line-height:14pt;
-  background:rgba(0,0,0,.4);
-  border-left:8px solid rgba(0,0,0,.3);
-  box-shadow:1px 2px 16px rgba(28,180,236,.4); }
-pre, pre * { cursor:text; }
-pre .cmt { color:rgb(255,229,106); }
-pre .kwd { color:#43A8CF; font-weight:bold; }
-pre .typ { color:#128B7D; font-weight:bold; }
-pre .atr { color:#128B7D; font-weight:bold; font-style:italic; }
-pre .def { color:#CAD6E4; font-weight:bold; }
-pre .prg { color:#854D6A; font-weight:bold; font-style:italic; }
-pre .val { color:#8AB647; }
-pre .tab { border-left:1px dotted rgba(67,168,207,0.4); }
-pre .end { background:url("images/tabEnd.png") no-repeat left bottom; }
-
-.page pre { background:rgba(0,0,0,.8); }
-.page pre > .Comment { color:rgb(255,229,106); }
-.page pre > .Keyword { color:#43A8CF; font-weight:bold; }
-.page pre > .StringLit,
-.page pre > .DecNumber { color:#8AB647; }
-
-.tall { height:100%; }
-.pre { padding:1px 5px; font:11pt monospace; background:#96A9B7; border-radius:3px; }
-
-.page-layout { margin:0 auto; width:1000px; }
-.docs-layout { margin:0 40px; }
-.talk-layout { margin:0 40px; }
-.wide-layout { margin:0 auto; }
-
-#head { height:100px; background:url("images/head.png") repeat-x bottom; }
-#head.docs { margin-left:280px; background:rgba(0,0,0,.25) url("images/head-fade.png") no-repeat right top; }
-#head > div { position:relative }
-
-	#head-logo {
-		position:absolute;
-		left:-390px;
-		top:0;
-		width:917px;
-		height:268px;
-		pointer-events:none;
-		background:url("images/logo.png") no-repeat; }
-	#head.docs #head-logo { left:-381px; position:fixed; }
-	#head.forum #head-logo { left:-370px; }
-
-  #head-logo-link {
-    position:absolute;
-    display:block;
-    top:10px;
-    left:10px;
-    width:236px;
-    height:85px; }
-  #head.docs #head-logo-link { left:-260px; }
-  #head.forum #head-logo-link { left:30px; }
-
-	#head-links { position:absolute; right:0; bottom:13px; }
-	#head.docs #head-links,
-	#head.forum #head-links { right:20px; }
-	#head-links > a {
-		display:block;
-		float:left;
-		padding:10px 25px 25px 25px;
-		color:rgba(255,255,255,.5);
-		font-size:14pt;
-		text-decoration:none;
-		letter-spacing:1px;
-		background:url("images/head-link.png") no-repeat center bottom;
-		transition:
-			color 0.3s ease-in-out,
-			text-shadow 0.4s ease-in-out; }
-	#head-links > a:hover,
-	#head-links > a.active {
-		color:#1cb3ec;
-		text-shadow:0 0 4px rgba(28,179,236,.8);
-		background-image:url("images/head-link_hover.png"); }
-
-	#head-banner { width:200px; height:100px; background:#000; }
-
-#neck { z-index:0; height:40px; }
-#neck.home { height:370px; }
-#neck > div { position:relative }
-
-	#glow-arrow {
-		position:absolute;
-		top:-9px;
-		left:0;
-		right:-16px;
-		height:48px;
-		background:url("images/glow-arrow.png") no-repeat right; }
-  #glow-arrow.docs { left:280px; }
-
-	#glow-line-vert {
-		position:fixed;
-		top:100px;
-		left:280px;
-		width:3px;
-		height:844px;
-		background:url("images/glow-line-vert.png") no-repeat; }
-
-	#slideshow { position:absolute; top:10px; left:10px; width:700px; height: 1000px; }
-	#slideshow > div {
-    position:absolute;
-    margin:30px 0 0 10px;
-    visibility:hidden;
-    opacity:0;
-    transition:
-      visibility 0s linear 1s,
-      opacity 1s ease-in-out; }
-	#slideshow > div.active { visibility:visible; opacity:1; transition-delay:0s; }
-	#slideshow > div.init { transition-delay:0s; }
-	#slideshow-nav { z-index:3; position:absolute; top:341px; left:18px; }
-	#slideshow-nav > div { display:inline-block; margin:5px 0; width:23px; height:23px; background:url("images/slideshow-nav.png") no-repeat; }
-	#slideshow-nav > div:hover { background-image:url("images/slideshow-nav_active.png"); opacity:0.5; }
-	#slideshow-nav > div.active { background-image:url("images/slideshow-nav_active.png"); opacity:1; }
-
-    .niaslide a img {
-      width: 542px;
-      box-shadow: 1px 2px 16px rgba(28, 180, 236, 0.4);
-      margin-left: 4em;
-      margin-top: -1em;
-    }
-
-		.codeslide1 { float:left; width:680px; font:10pt monospace; }
-		.codeslide1 > div:first-child { margin:0 40px 0 0; }
-		.codeslide1 h2 { margin:0 0 5px 0; color:rgba(162,198,223,.78); }
-		.codeslide1 .desc { margin:0 0 5px 0; color:rgba(162,198,223,.78); font:13pt "arial"; }
-
-		.codeslide2 > div { float:left; width:320px; font:10pt monospace; }
-		.codeslide2 > div:first-child { margin:0 40px 0 0; }
-		.codeslide2 h2 { margin:0 0 5px 0; color:rgba(162,198,223,.78); }
-
-		.codeslide3 > div { float:left; width:320px; font:10pt monospace; }
-		.codeslide3 > div:first-child { margin:0 40px 0 0; }
-		.codeslide3 h2 { margin:0 0 5px 0; color:rgba(162,198,223,.78); }
-
-		.codeslide3 .desc { margin:0 0 5px 0; color:rgba(162,198,223,.78); font:13pt "arial"; }
-
-		/* back when slide1 was the quote:
-		#slide1 { margin-top:50px; }
-		#slide1 > p {
-			padding:40px 20px 0 20px;
-			font-style:italic;
-			color:rgba(162,198,223,.78);
-			letter-spacing:1px;
-			line-height:25pt;
-			background:url("images/quotes.png") top left no-repeat; }
-		#slide1 > div {
-			float:right;
-			margin-right:40px;
-			font-style:italic;
-			font-weight:bold;
-			color:rgba(93,155,199,.44); }
-    */
-	#sidebar {
-		z-index:2;
-		position:absolute;
-		top:5px; right:0;
-		width:275px;
-		height:726px;
-		padding:210px 0 0 0;
-		background:url("images/sidebar.png") no-repeat; }
-	#sidebar > h3 { margin:0 30px 0 30px; color:rgba(255,255,255,.5); }
-	#sidebar > h3.blue { color:rgba(28,180,236,.5); }
-	#sidebar-links,
-	#sidebar-news {
-		margin:10px 30px 50px 30px;
-		padding:10px 0;
-		background:rgba(0,0,0,.6); }
-	#sidebar-links { box-shadow:1px 2px 12px rgba(255,255,255,.4); }
-	#sidebar-news { box-shadow:1px 2px 12px rgba(28,180,236,.6); }
-	#sidebar-links > a {
-		display:block;
-		margin-left:15px;
-		padding:12px 20px 12px 45px;
-		font-weight:bold;
-		text-decoration:none;
-		letter-spacing:1px;
-		color:rgba(255,255,255,.4);
-		transition:
-			color 0.1s ease-in-out,
-			text-shadow 0.2s ease-in-out; }
-	#sidebar-news > a { transition: color 0.3s ease-in-out; }
-	#sidebar-news > a > h4 { transition: color 0.1s ease-in-out, text-shadow 0.2s ease-in-out; }
-	#sidebar-links > a:hover { color:#fff; text-shadow:0 0 6px #fff; }
-	#sidebar-news > a { display:block; padding:15px; color:rgba(255,255,255,.4); text-decoration:none; }
-	#sidebar-news > a > h4 { margin:0 0 5px 0; color:rgba(28,180,236,.5); }
-	#sidebar-news > a:hover > h4 { margin:0 0 5px 0; color:rgba(28,180,236,.8); text-shadow:0 0 6px rgba(28,180,236,.6); }
-	#sidebar-news > a:hover { color:rgba(255,255,255,1); }
-	#sidebar-news > a.blue { color:rgba(28,180,236,.5); font-weight:bold; }
-	#sidebar-news > a.blue:hover { color:#fff; }
-
-		#link_forum { background:url("images/more-links_forum.png") no-repeat left center; }
-		#link_github { background:url("images/more-links_github.png") no-repeat left center; }
-		#link_editors { background:url("images/more-links_editors.png") no-repeat left center; }
-		#link_aporia { background:url("images/more-links_editors.png") no-repeat left center; }
-		#link_nimbuild { background:url("images/more-links_nimbuild.png") no-repeat left center; }
-
-	#overview-bg {
-		position:fixed;
-		top:0;
-		bottom:0;
-		left:0;
-		width:280px;
-		background:rgba(0,0,0,0.25); }
-	#overview {
-		z-index:3;
-		position:fixed;
-		overflow:auto;
-		top:115px;
-		bottom:20px;
-		left:20px;
-		width:245px; }
-	#overview::-webkit-scrollbar { width:5px; }
-	#overview::-webkit-scrollbar-track { border-radius:2px; background:rgba(255,255,255,.03); }
-	#overview::-webkit-scrollbar-thumb { border-radius:2px; background:rgba(28,179,236,.5); }
-	#overview > div { overflow:auto; margin-bottom:40px; }
-	#overview a {
-		display:block;
-		padding:0 10px;
-		margin:2px 5px 2px 0;
-		color:rgba(255,255,255,.6);
-		background:rgba(255,255,255,0.03);
-    border-radius:2px;
-		letter-spacing:1px;
-		text-decoration:none; }
-	#overview a:hover { color:#fff; background:rgba(255,255,255,0.05); }
-	#overview > .types a { border-left:2px solid rgba(28,179,236,.4); }
-	#overview > .procs a { border-left:2px solid rgba(255,223,53,.4); }
-	#overview > .iters a { border-left:2px solid rgba(255,134,53,.4); }
-	#overview > div > h4 {
-		margin:0 5px 10px 0;
-		padding:5px 10px;
-		letter-spacing:1px;
-		color:#fff;
-		border-left:2px solid #fff;
-    border-radius:2px;
-		background:rgba(255,255,255,0.1); }
-	#overview > .types h4 { color:#1cb3ec; border-color:#1cb3ec; }
-	#overview > .procs h4 { color:#ffdf35; border-color:#ffdf35; }
-	#overview > .iters h4 { color:#ff8635; border-color:#ff8635; }
-	#overview h5 {
-		color:rgba(28,179,236,.6);
-		margin:10px 0 5px 0;
-		padding:5px 5px;
-		letter-spacing:1px; }
-
-#body { z-index:1; position:relative; background:rgba(220,231,248,0.8); }
-#body.docs { margin:0 40px 20px 320px; }
-#body.forum { margin:0 40px 20px 40px; }
-
-	#body-border {
-		position:absolute;
-		top:-25px;
-		left:0;
-		right:0;
-		height:35px;
-		background:rgba(0,0,0,.25); }
-
-	#body-border-left {
-		position:absolute;
-		left:-25px;
-		top:-25px;
-		bottom:-25px;
-		width:35px;
-		background:rgba(0,0,0,.25); }
-
-	#body-border-right {
-		position:absolute;
-		right:-25px;
-		top:-25px;
-		bottom:-25px;
-		width:35px;
-		background:rgba(0,0,0,.25); }
-
-	#body-border-bottom {
-		position:absolute;
-		left:10px;
-		right:10px;
-		bottom:-25px;
-		height:35px;
-		background:rgba(0,0,0,.25); }
-
-	#body.docs #body-border,
-	#body.forum #body-border { left:10px; right:10px; }
-
-	#glow-line {
-		position:absolute;
-		top:-27px;
-		left:100px;
-		right:-25px;
-		height:3px;
-		background:url("images/glow-line.png") no-repeat left; }
-	#glow-line-bottom {
-		position:absolute;
-		bottom:-27px;
-		left:-25px;
-		right:100px;
-		height:3px;
-		background:url("images/glow-line2.png") no-repeat right; }
-
-	#content { padding:40px 0; line-height:150%; }
-	#content.page { width:680px; min-height:1220px; padding-left:20px; }
-	#content h1 { font-size:20pt; letter-spacing:1px; color:rgba(0,0,0,.75); }
-	#content h2 { font-size:16pt; letter-spacing:1px; color:rgba(0,0,0,.7); margin-top:40px; }
-	#content p { text-align:justify; }
-	#content a { color:#0E65D1; text-decoration:none; }
-	#content a:hover { text-decoration:underline; }
-	#content ul { padding-left:20px; }
-	#content li { margin-bottom:10px; text-align:justify; }
-
-		#body.docs #content > div { margin-top:40px; padding-top:40px; border-top:1px dashed rgba(0,0,0,.25); }
-		#body.docs #content > div:first-child { margin-top:0; padding-top:0; border:none; }
-		#body.docs #content > div > h3 {
-			color:#fff;
-			margin:0 0 10px 0;
-			padding:10px 20px;
-			letter-spacing:1px;
-			border-left:8px solid #fff;
-      border-radius:3px;
-			background:rgba(0,0,0,.7);
-			box-shadow:1px 3px 12px rgba(0,0,0,.4); }
-		#body.docs #content > #types-wrap > h3 { color:#1cb3ec; border-color:#1cb3ec; }
-		#body.docs #content > #procs-wrap > h3 { color:#ffdf35; border-color:#ffdf35; }
-		#body.docs #content > #iters-wrap > h3 { color:#ff8635; border-color:#ff8635; }
-		#body.docs #content > div > div > div {
-			overflow:auto;
-			margin:10px 0;
-			border-left:8px solid #fff;
-      border-radius:3px;
-			background:rgba(0,0,0,.1); }
-		#body.docs #content > #types-wrap > div > div { border-color:rgba(28,179,236,.5); }
-		#body.docs #content > #procs-wrap > div > div { border-color:rgba(255,223,53,.5); }
-		#body.docs #content > #iters-wrap > div > div { border-color:rgba(255,134,53,.5); }
-		#body.docs #content > #procs-wrap > div > div.overload-head { margin-bottom:0; }
-		#body.docs #content > #procs-wrap > div > div.overload-tail { margin-top:0; border-top:1px dashed rgba(255,223,53,.5); }
-		#body.docs #content > #procs-wrap > div > div.overload { margin-top:0; margin-bottom:0; border-top:1px dashed rgba(255,223,53,.5); }
-		#body.docs #content > #iters-wrap > div > div.overload-head { margin-bottom:0; }
-		#body.docs #content > #iters-wrap > div > div.overload-tail { margin-top:0; border-top:1px dashed rgba(255,134,53,.5); }
-		#body.docs #content > #iters-wrap > div > div.overload { margin-top:0; margin-bottom:0; border-top:1px dashed rgba(255,134,53,.5); }
-		#body.docs #content > div > div > p { margin:20px 10px 10px 10px; }
-
-		#body.docs #content > div > div > div > div { float:left; }
-		#body.docs #content > div > div > div > div.head { width:60%; }
-		#body.docs #content > div > div > div > div.data { width:40%; }
-
-    #body.docs #content > h1 > .symbol {
-      padding:0 8px;
-      border-radius:5px;
-      background:rgba(206,218,233,.4); }
-
-		#body.docs #content > div > div > div > div.head > .sign {
-			margin:0 10px 5px 10px;
-			padding:10px 10px 0 10px;
-			font-weight:bold;
-			border-bottom:1px dashed rgba(0,0,0,.25); }
-		#body.docs #content > div > div > div > div.head > .desc {
-			padding:0 20px 10px 20px;
-			color:rgba(0,0,0,.75); }
-		#body.docs #content > div > #types > div > div.head > .sign > .symbol {
-			padding:0 5px;
-			border-radius:3px;
-			background:rgba(28,179,236,.4); }
-		#body.docs #content > div > #procs > div > div.head > .sign > .symbol {
-			padding:0 5px;
-			border-radius:3px;
-			background:rgba(255,223,53,.3); }
-		#body.docs #content > div > #iters > div > div.head > .sign > .symbol {
-			padding:0 5px;
-			border-radius:3px;
-			background:rgba(255,134,53,.3); }
-
-		#body.docs #content > div > div > div > div.data > div {
-			margin:0 20px 5px 10px;
-			padding:10px 0 0 10px;
-			font-style:italic;
-			color:rgba(0,0,0,.6);
-			border-bottom:1px dashed rgba(0,0,0,.25); }
-		#body.docs #content > div > div > div > div.data > ul { margin:0; padding:0 10px; }
-		#body.docs #content > div > div > div > div.data > ul:last-child { margin-bottom:5px; padding-bottom:10px; }
-		#body.docs #content > div > div > div > div.data > ul .symbol { padding:0 5px; border-radius:3px; background:rgba(23,192,23,.25); }
-		#body.docs #content > div > div > div > div.data > ul.pragmas .symbol { background:rgba(106,50,145,.25); }
-		#body.docs #content > div > div > div > div.data > ul > li { margin:0; padding:0 10px; list-style:none; }
-
-		#body.docs #content pre {
-			overflow:auto;
-			margin:10px 0;
-			padding:15px 10px;
-			font-size:10pt;
-			font-style:normal;
-			line-height:14pt;
-			background:rgba(0,0,0,.75);
-			border-left:8px solid rgba(0,0,0,.3); }
-
-    #docs-sort { float:right; font-size:75%; }
-    #docs-sort > a {
-      cursor:default;
-      margin:0 0 0 10px;
-      padding:2px 10px;
-      border-radius:5px;
-      color:rgba(0,0,0,.25);
-      background:rgba(0,0,0,.1);
-      box-shadow:inset 0 1px 8px rgba(0,0,0,.4); }
-    #docs-sort > a:hover,
-    #docs-sort > a.active { color:#000; background:rgba(0,0,0,.2); }
-
-    #talk-heads { overflow:auto; margin:0 8px 0 8px; }
-    #talk-heads > div { float:left; font-size:120%; font-weight:bold; }
-    #talk-heads > .topic { width:55%; }
-    #talk-heads > .detail { width:15%; }
-    #talk-heads > .author { width:15%; }
-    #talk-heads > .reply { width:15%; }
-    #talk-heads > div > div { margin:0 10px 10px 10px; padding:0 10px 10px 10px; border-bottom:1px dashed rgba(0,0,0,0.4); }
-    #talk-heads > .topic > div { margin-left:0; }
-    #talk-heads > .author > div { margin-right:0; }
-
-    #talk-thread  > div,
-    #talk-threads > div {
-      position:relative;
-      margin:5px 0;
-      overflow:auto;
-      border-radius:3px;
-      border:8px solid rgba(0,0,0,.8);
-      border-top:none;
-      border-bottom:none;
-      background:rgba(0,0,0,0.1); }
-    #talk-thread  > div:nth-child(odd) { background:rgba(255,255,255,0.1); }
-    #talk-threads > div:nth-child(odd) { background:rgba(0,0,0,0.2); }
-    #talk-thread  > div > div,
-    #talk-threads > div > div { float:left; }
-    #talk-thread  > div > div > div,
-    #talk-threads > div > div > div { margin:10px 20px; }
-    #talk-threads > div > .topic { width:55%; }
-    #talk-threads > div > .reply { width:15%; overflow:hidden; }
-    #talk-threads > div > .detail { width:15%; overflow:hidden; }
-    #talk-thread  > div > .author,
-    #talk-threads > div > .author {
-      position:absolute;
-      right:0;
-      top:0;
-      bottom:0;
-      width:15%;
-      overflow:hidden;
-      background:rgba(0,0,0,0.8); }
-    #talk-thread  > div > .author a,
-    #talk-threads > div > .author a { color:#1cb3ec !important; }
-    #talk-thread  > div > .author a:hover,
-    #talk-threads > div > .author a:hover { color:#fff !important; }
-    #talk-threads > div > .topic .pages { float:right; }
-    #talk-threads > div > .topic > div > a { font-weight:bold; }
-    #talk-threads > div > .detail > div { float:left; margin:0; }
-    #talk-threads > div > .detail > div > div { margin-left:20px; padding:10px 10px 10px 22px; }
-    #talk-threads > div > .detail > div { width:50%; }
-    #talk-threads > div > .detail > div:first-child > div { background:url("images/forum-views.png") no-repeat left; }
-    #talk-threads > div > .detail > div:last-child > div { background:url("images/forum-posts.png") no-repeat left; }
-
-    #talk-thread > div {  margin:20px 0; min-height:150px; box-shadow:1px 3px 12px rgba(0,0,0,.4) }
-    #talk-thread > div > .author > div > .avatar { margin-top:20px; }
-    #talk-thread > div > .author > div > .avatar > img { box-shadow:0 0 12px #1cb3ec; }
-    #talk-thread > div > .author > div > .name { }
-    #talk-thread > div > .topic { width:85%; padding-bottom:10px; }
-    #talk-thread > div > .topic pre {
-      overflow:auto;
-      margin:0;
-      padding:15px 10px;
-      font-size:10pt;
-      font-style:normal;
-      line-height:14pt;
-      background:rgba(0,0,0,.75);
-      border-left:8px solid rgba(0,0,0,.3); }
-
-    #talk-head,
-    #talk-info {
-      overflow:auto;
-      border-radius:3px;
-      border:8px solid rgba(0,0,0,.2);
-      border-top:none;
-      border-bottom:none;
-      background:rgba(0,0,0,0.1); }
-    #talk-head { margin-bottom:20px; }
-    #talk-info { margin-top:20px; }
-    #talk-head > div,
-    #talk-info > div { float:left; }
-    #talk-head > .info,
-    #talk-info > .info { width:85%; }
-    #talk-head > .user,
-    #talk-info > .user { width:15%; background:rgba(0,0,0,.2); }
-    #talk-info > .user > div > .reply { font-weight:bold; padding-left:22px; background:url("images/forum-reply.png") no-repeat left; }
-    #talk-head > div > div,
-    #talk-info > div > div { padding:5px 20px; }
-    #talk-head > .detail > div { float:left; margin:0; }
-    #talk-head > .detail > div > div { padding-left:22px; }
-    #talk-head > .detail > div:first-child > div { background:url("images/forum-views.png") no-repeat left; }
-    #talk-head > .detail > div:last-child > div { background:url("images/forum-posts.png") no-repeat left; }
-
-    #talk-nav { margin:20px 8px 0 8px; padding-top:10px; border-top:1px dashed rgba(0,0,0,0.4); text-align:center; }
-    #talk-nav > a.active { text-decoration:underline !important; }
-
-    .standout {
-      padding:5px 30px;
-      margin-bottom:20px;
-      border:8px solid rgba(0,0,0,.8);
-      border-right-width:0;
-      border-top-width:0;
-      border-bottom-width:0;
-      border-radius:3px;
-      background:rgba(0,0,0,0.1); }
-    .standout h2 { margin-bottom:10px; padding-bottom:10px; border-bottom:1px dashed rgba(0,0,0,.8); }
-    .standout li { margin:0 !important; padding-top:10px; border-top:1px dashed rgba(0,0,0,.2); }
-    .standout ul { padding-bottom:5px; }
-    .standout .tools     ul { list-style:url("images/docs-tools.png"); }
-    .standout .libraries ul { list-style:url("images/docs-libraries.png"); }
-    .standout .internals ul { list-style:url("images/docs-internals.png"); }
-    .standout .tutorials ul { list-style:url("images/docs-tutorials.png"); }
-    .standout .examples  ul { list-style:url("images/docs-examples.png"); }
-    .standout .articles  ul { list-style:url("images/docs-articles.png"); }
-    .standout li:first-child { padding-top:0; border-top:none; }
-    .standout li p { margin:0 0 10px 0 !important; line-height:130%; }
-    .standout li p > a { font-weight:bold; }
-
-    .forum-user-info,
-    .forum-user-info * { cursor:help }
-
-#foot { height:150px; position:relative; top:-10px; letter-spacing:1px; }
-#foot.home { background:url("images/foot.png") repeat-x top; height:200px; }
-#foot.docs { margin-left:320px; margin-right:40px; }
-#foot.forum { margin-left:40px; margin-right:40px; }
-#foot > div { position:relative; }
-#foot.home > div { width:960px; }
-#foot h4 { font-size:11pt; color:rgba(255,255,255,.4); margin:40px 0 6px 0; }
-#foot a:hover { color:#fff; }
-
-	#foot-links { float:left; }
-	#foot-links > div { float:left; padding:0 40px 0 0; line-height:120%; }
-	#foot-links a { display:block; font-size:10pt; color:rgba(255,255,255,.3); text-decoration:none; }
-	#foot-legal { float:right; font-size:10pt; color:rgba(255,255,255,.3); line-height:150%; text-align:right; }
-	#foot-legal a { color:inherit; text-decoration:none; }
-	#foot-legal > h4 > a { color:inherit; }
-
-
-#body .docutils th {
-    border-bottom: 2px solid #1A1A1A;
-    font-weight: normal;
-    padding: 8px; }
-#body table.docutils {
-    border-collapse: collapse;
-    text-align: left;
-    border-spacing: 0px; }
-
-#bountysource {
-    width: 100%;
-    height: 30px;
-    background-color: #19975d;
-}
-
-#bountysource a, #bountysource a:visited, #bountysource a:hover {
-  color: #1a1a1a;
-}
-
-/* Current sponsors page */
-
-dt {
-  font-size: 20pt;
-  clear: both;
-  margin-bottom: 10pt;
-}
-
-dd.logo {
-  width: 200px;
-  min-height: 50px;
-  margin-bottom: 10pt;
-  margin-right: 20pt;
-  float: left;
-}
-
-dd.logo img {
-  max-height: 200px;
-}
-
-dd.this_month {
-  font-size: 20pt;
-}
-
-dt a, dt a:visited, dt a:hover {
-  color: #1d1d1d !important;
-}
-
-dt.level-1 {
-  color: #2f2f2f !important;
-}
-
-p.lastUpdate {
-  font-size: 12pt;
-  color: #6d6d6d;
-}
-
-/* quotes */
-
-blockquote {
-  padding: 0px 8px;
-  margin: 10px 0px;
-  border-left: 2px solid rgb(61, 61, 61);
-  color: rgb(109, 109, 109);
-}
-
-/* News articles */
-
-.metadata {
-  font-size: 12pt;
-  margin-bottom: 20pt;
-  margin-top: -16pt;
-  color: #4f4f4f;
-}
-
diff --git a/web/assets/zeo/banner.jpg b/web/assets/zeo/banner.jpg
deleted file mode 100644
index f09b000de..000000000
--- a/web/assets/zeo/banner.jpg
+++ /dev/null
Binary files differdiff --git a/web/community.rst b/web/community.rst
deleted file mode 100644
index fefa4c4b6..000000000
--- a/web/community.rst
+++ /dev/null
@@ -1,150 +0,0 @@
-Nim's Community
-===============
-
-.. container:: standout
-
-  Forum
-  -----
-
-  The `Nim forum <https://forum.nim-lang.org/>`_ is the place where most
-  discussions related to the language happen. It not only includes discussions
-  relating to the design of Nim but also allows for beginners to ask questions
-  relating to Nim.
-
-
-.. container:: standout
-
-  Mailing list
-  ------------
-
-  The mailing list can be found here: http://www.freelists.org/list/nim-dev
-  There is no consensus yet about what is discussed via the forum as opposed
-  to the mailing list. Join whatever you like!
-
-
-.. container:: standout
-
-  IRC
-  ----
-
-  Many Nim developers are a part of the
-  `#nim IRC channel <http://webchat.freenode.net/?channels=nim>`_ on
-  Freenode. That is the place where the rest of the discussion relating to Nim
-  occurs. Be sure to join us there if you wish to discuss Nim in real-time.
-  IRC is the perfect place for people just starting to learn Nim and we
-  welcome any questions that you may have!
-
-  You may also be interested in reading the
-  `IRC logs <https://irclogs.nim-lang.org/>`_ which are an archive of all
-  of the previous discussions that took place in the IRC channel.
-
-
-.. container:: standout
-
-  GitHub
-  ------
-
-  Nim's `source code <http://github.com/nim-lang/Nim>`_ is hosted on GitHub.
-  Together with the `wiki <http://github.com/nim-lang/Nim/wiki>`_ and
-  `issue tracker <http://github.com/nim-lang/Nim/issues>`_.
-
-  GitHub also hosts other projects relating to Nim. These projects are a part
-  of the `nim-lang organisation <http://github.com/nim-lang>`_.
-  This includes the `Nimble package manager <https://github.com/nim-lang/nimble>`_
-  and its `package repository <http://github.com/nim-lang/packages>`_.
-
-
-.. container:: standout
-
-  Twitter
-  -------
-
-  Follow us `@nim_lang <http://twitter.com/nim_lang>`_ for latest news about
-  Nim.
-
-.. container:: standout
-
-  Reddit
-  ------
-
-  Subscribe to `/r/nim <http://reddit.com/r/nim>`_ for latest news about
-  Nim.
-
-.. container:: standout
-
-  StackOverflow
-  -------------
-
-  When asking a question relating to Nim, be sure to use the
-  `Nim <http://stackoverflow.com/questions/tagged/nim>`_ tag in your
-  question.
-
-.. container:: standout
-
-  Google+
-  -------
-
-  The `G+ Nim community <https://plus.google.com/u/0/communities/106921341535068810587>`_ is another place where discussions related to the language happen. Read and follow various articles, posts and interesting links about Nim.
-
-.. container:: standout
-
-  Gitter
-  ------
-
-  The `Gitter Chatroom for Nim <https://gitter.im/nim-lang/Nim>`_ is the persistent logged "natural" chatroom for GitHub repositories and very easy to access for GitHub users. This does not need additional software and can send notifications about messages by email.
-
-.. container:: standout
-
-  Meetup
-  ------
-
-  The `Nim BR Meetup <http://www.meetup.com/pt-BR/nim-br>`_ is a brazilian user group about Nim where they are having discussions, talks or workshops about Nim programming language.
-
-.. container:: standout
-
-  How to help
-  -----------
-
-  There are always many things to be done in the main
-  `Nim repository <https://github.com/nim-lang/Nim>`_, check out the
-  `issues <https://github.com/nim-lang/Nim/issues>`_ for
-  things to do; pull requests are always welcome. You can
-  also contribute to the many other projects hosted by the
-  `nim-lang <https://github.com/nim-lang>`_ organisation on github. If you
-  can't find anything you fancy doing, you can always ask for inspiration on IRC
-  (irc.freenode.net #nim) or on the `Nim forums <http://forum.nim-lang.org>`_.
-
-
-.. container:: standout
-
-  Donations
-  ---------
-
-  If you love what we do and are feeling generous then you can always donate.
-  Contributions of any quantity are greatly appreciated and will contribute to
-  making Nim even better!
-
-  Gittip
-    .. raw:: html
-
-      <img src="http://img.shields.io/gratipay/nim.svg">
-
-  BountySource
-
-    .. raw:: html
-
-      <img src="https://img.shields.io/bountysource/team/mozilla-core/activity.svg">
-
-  Paypal
-    .. raw:: html
-
-      <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
-      <input type="hidden" name="cmd" value="_s-xclick">
-      <input type="hidden" name="hosted_button_id" value="C6PBFRF4WDR2E">
-      <input type="image" src="https://www.paypalobjects.com/en_US/GB/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal – The safer, easier way to pay online!">
-      <img alt="" border="0" src="https://www.paypalobjects.com/en_GB/i/scr/pixel.gif" width="1" height="1">
-      </form>
-
-
-  Bitcoin
-    Bitcoin address: 1BXfuKM2uvoD6mbx4g5xM3eQhLzkCK77tJ
diff --git a/web/documentation.rst b/web/documentation.rst
deleted file mode 100644
index ec33d0827..000000000
--- a/web/documentation.rst
+++ /dev/null
@@ -1,70 +0,0 @@
-Nim's Documentation
-===================
-
-.. container:: standout
-
-  Search Options
-  --------------
-
-  .. container:: internals
-
-  `Documentation Index <docs/theindex.html>`_ - The generated
-  index. **Index + (Ctrl+F) == Joy**
-
-.. container:: standout
-
-  Standards & Guides
-  ------------------
-
-  .. container:: libraries
-
-    - | `Standard Library <docs/lib.html>`_
-      | This document describes Nim's standard library.
-
-    - | `Language Manual <docs/manual.html>`_
-      | The Nim manual is a draft that will evolve into a proper specification.
-
-    - | `Nim Style Guide <docs/nep1.html>`_
-      | The stylistic conventions that Nim's official projects adhere to.
-
-    - | `Compiler User Guide <docs/nimc.html>`_
-      | The user guide lists command line arguments, special features of the
-        compiler, etc.
-
-    - | `NimScript <docs/nims.html>`_
-      | NimScript is the upcoming new way to configure Nim.
-
-    - | `Nim Backend Integration <docs/backends.html>`_
-      | The Backend Integeration guide gives further information of how Nim can
-        interact with C, C++, Objective C and JavaScript.
-
-
-.. container:: standout
-
-  Tools & Features
-  ----------------
-
-  .. container:: tools
-
-    - | `Source Code Filters <docs/filters.html>`_
-      | The Nim compiler supports source code filters as a simple yet powerful
-        builtin templating system.
-
-    - | `Tools Documentation <docs/tools.html>`_
-      | Description of some tools that come with the standard distribution.
-
-
-.. container:: standout
-
-  Internal Details
-  ----------------
-
-  .. container:: internals
-
-    - | `Garbage Collector <docs/gc.html>`_
-      | Additional documentation about Nim's GC and how to operate it in a
-        realtime setting.
-
-    - | `Internal Documentation <docs/intern.html>`_
-      | The internal documentation describes how the compiler is implemented.
-        Read this if you want to hack the compiler.
diff --git a/web/download.rst b/web/download.rst
deleted file mode 100644
index dd086cbaa..000000000
--- a/web/download.rst
+++ /dev/null
@@ -1,115 +0,0 @@
-Download the compiler
-=====================
-
-You can download the latest version of the Nim compiler here.
-
-Windows
--------
-
-Zips
-%%%%
-
-Since the website moved to https, only installation via the provided zipfiles
-is supported:
-
-* | 32 bit: `nim-0.16.0_x32.zip <download/nim-0.16.0_x32.zip>`_
-  | SHA-256  69af94a6875a02543c1bf0fa03c665f126f8500a2c0e226c32571e64c6842e57
-* | 64 bit: `nim-0.16.0_x64.zip <download/nim-0.16.0_x64.zip>`_
-  | SHA-256  e667cdad1ae8e9429147aea5031fa8a80c4ccef6d274cec0e9480252d9c3168c
-
-Unzip these where you want and **optionally** run ``finish.exe`` to
-detect your MingW environment. (Though that's not reliable yet.)
-
-You can find the required DLLs here, if you lack them for some reason:
-
-* | 32 and 64 bit: `DLLs.zip <download/dlls.zip>`_
-  | SHA-256  198112d3d6dc74d7964ba452158d44bfa57adef4dc47be8c39903f2a24e4a555
-
-
-These versions of mingw are known to work:
-
-* | 32 bit: `mingw32-6.3.0 <download/mingw32-6.3.0.7z>`_
-  | SHA-256  1239a56d4c42e146b2cb25dc4d0871bd83f569d0a51a9198e84d010e0a75745a
-* | 64 bit: `mingw64-6.3.0 <download/mingw64-6.3.0.7z>`_
-  | SHA-256  9a23d12d96a10e67093c1f2042275c6a7d29da9e2ead573d0f24f4a6d53761a1
-
-
-
-Installation based on generated C code
---------------------------------------
-
-**Note:** The Nim compiler requires a C compiler to compile software. On
-Windows we recommend that you use
-`Mingw-w64 <http://mingw-w64.sourceforge.net/>`_. GCC is recommended on Linux
-and Clang on Mac. The Windows installers above already includes a C compiler.
-
-This installation method is the preferred way for Linux, Mac OS X, and other Unix
-like systems.
-
-Firstly, download this archive:
-
-* | `nim-0.16.0.tar.xz (2.9MB) <download/nim-0.16.0.tar.xz>`_
-  | SHA-256  9e199823be47cba55e62dd6982f02cf0aad732f369799fec42a4d8c2265c5167
-
-Extract the archive. Then copy the extracted files into your chosen installation
-directory, ideally somewhere in your home directory.
-For example: ``~/programs/nim``.
-
-Now open a terminal and follow these instructions:
-
-* ``cd`` into your installation directory, for example by executing
-``cd ~/programs/nim``.
-* run ``sh build.sh``.
-* Add ``$your_install_dir/bin`` to your PATH.
-* To build associated tools like ``nimble`` and ``nimsuggest`` run ``nim c koch && ./koch tools``.
-
-After restarting your terminal, you should be able to run ``nim -v``
-which should show you the version of Nim you just installed.
-
-There are other ways to install Nim (like using the ``install.sh`` script),
-but these tend to cause more problems.
-
-
-Bleeding edge installation from GitHub
---------------------------------------
-
-`GitHub <http://github.com/nim-lang/nim>`_ is where Nim's development takes
-place. You may wish to grab the latest development version of Nim, because
-sometimes bug fixes and new features may not have made it to an official
-release yet. In those circumstances you are better off grabbing the
-current development branch.
-
-You will also need to do this if you would like to contribute to Nim.
-
-Before you download the code, open a new terminal and ``cd`` into the
-directory where you would like the download to take place.
-
-The following commands can be used to download the current development branch
-and then to build it::
-
-  git clone https://github.com/nim-lang/Nim.git
-  cd Nim
-  git clone --depth 1 https://github.com/nim-lang/csources
-  cd csources && sh build.sh
-  cd ..
-  bin/nim c koch
-  ./koch boot -d:release
-
-You should then add the ``./bin`` (make sure to expand this into an
-absolute path) directory to your ``PATH``.
-
-
-Docker Hub
-----------
-
-The `official Docker images <https://hub.docker.com/r/nimlang/nim/>`_
-are published on Docker Hub and include the compiler and Nimble. There are images
-for standalone scripts as well as Nimble packages.
-
-Get the latest stable image::
-
-  docker pull nimlang/nim
-
-The latest development version::
-
-  docker pull nimlang/nim:devel
diff --git a/web/inactive_sponsors.csv b/web/inactive_sponsors.csv
deleted file mode 100644
index 6352bc194..000000000
--- a/web/inactive_sponsors.csv
+++ /dev/null
@@ -1,57 +0,0 @@
-logo, name, url, this_month, all_time, since, level
-,bogen,,0,1010,"Jul 23, 2016",1
-,mikra,,0,400,"Apr 28, 2016",1
-,shkolnick-kun,,0,375,"Jul 6, 2016",1
-,"Chris Heller",,0,350,"May 19, 2016",1
-,linkmonitor,,0,280,"Jan 28, 2016",1
-,avsej,,0,110,"Jun 10, 2016",1
-,WilRubin,,0,100,"Aug 11, 2015",1
-,"Benny Luypaert",,0,100,"Apr 10, 2016",1
-,PhilipWitte,,0,100,"Aug 5, 2016",1
-,skunkiferous,,0,100,"Oct 2, 2016",1
-,"Jonathan Arnett",,0,90,"May 20, 2016",1
-,Boxifier,,0,75,"Apr 12, 2016",1
-,iolloyd,,0,75,"Apr 29, 2016",1
-,btbytes,,0,70,"Apr 6, 2016",1
-,rb01,,0,50,"May 4, 2016",1
-,barcharcraz,,0,50,"Jun 2, 2016",1
-,zachaysan,,0,50,"Jun 7, 2016",1
-,kunev,,0,50,"Dec 26, 2016",1
-,iboB,,0,50,"Jan 28, 2017",1
-,TedSinger,,0,45,"Apr 9, 2016",1
-,johnnovak,,0,45,"Apr 30, 2016",1
-,"Matthew Baulch",,0,40,"Jun 7, 2016",1
-,"Matthew Newton",,0,35,"Apr 20, 2016",1
-,martinbbjerregaard,,0,35,"Jun 9, 2016",1
-,RationalG,,0,30,"Jun 17, 2016",1
-,benbve,,0,30,"Jul 12, 2016",1
-,multikatt,,0,30,"Nov 2, 2016",1
-,"Landon Bass",,0,25,"Jun 7, 2016",1
-,jimrichards,,0,25,"Jun 8, 2016",1
-,jjzazuet,,0,25,"Jul 10, 2016",1
-,kteza1,,0,20,"Jun 10, 2016",1
-,tomkeus,,0,20,"Sep 4, 2016",1
-,csoriano89,,0,20,"Sep 7, 2016",1
-,juanjux,,0,20,"Oct 29, 2016",1
-,zagfai,,0,20,"Nov 3, 2016",1
-,hellcoderz,,0,20,"Jan 24, 2017",1
-,mirek,,0,15,"Apr 9, 2016",1
-,DateinAsia,,0,15,"Jul 30, 2016",1
-,rickc,,0,15,"Jul 31, 2016",1
-,jpkx1984,,0,13,"Jul 11, 2016",1
-,vlkrav,,0,12,"Aug 9, 2015",1
-,tebanep,,0,12,"Aug 7, 2016",1
-,McSpiros,,0,10,"Apr 6, 2016",1
-,"Brandon Hunter",,0,10,"Apr 7, 2016",1
-,funny-falcon,,0,10,"Apr 7, 2016",1
-,teroz,,0,10,"Apr 8, 2016",1
-,iLikeLego,,0,10,"Apr 16, 2016",1
-,Angluca,,0,10,"May 3, 2016",1
-,calind,,0,10,"Jun 7, 2016",1
-,goldenreign,,0,10,"Jun 10, 2016",1
-,Blumenversand,,0,10,"Jul 21, 2016",1
-,cinnabardk,,0,10,"Aug 6, 2016",1
-,reddec,,0,10,"Aug 31, 2016",1
-,cupen,,0,10,"Nov 21, 2016",1
-,yay,,0,10,"Jan 25, 2017",1
-,niv,,0,5,"Apr 6, 2016",1
diff --git a/web/index.rst b/web/index.rst
deleted file mode 100644
index 4b712fa3b..000000000
--- a/web/index.rst
+++ /dev/null
@@ -1,89 +0,0 @@
-====
-Home
-====
-
-Welcome to Nim
---------------
-
-**Nim** is a statically typed, imperative
-programming language that tries to give the programmer ultimate power without
-compromises on runtime efficiency. This means it focuses on compile-time
-mechanisms in all their various forms.
-
-Beneath a nice infix/indentation based syntax with a
-powerful (AST based, hygienic) macro system lies a semantic model that supports
-a soft realtime GC on thread local heaps. Asynchronous message passing is used
-between threads, so no "stop the world" mechanism is necessary. An unsafe
-shared memory heap is also provided for the increased efficiency that results
-from that model.
-
-
-Nim is efficient
-================
-
-* Native code generation (currently via compilation to C), not dependent on a
-  virtual machine: **Nim produces small executables without dependencies
-  for easy redistribution.**
-* A fast **non-tracing** garbage collector that supports soft
-  real-time systems (like games).
-* System programming features: Ability to manage your own memory and access the
-  hardware directly. Pointers to garbage collected memory are distinguished
-  from pointers to manually managed memory.
-* Zero-overhead iterators.
-* Cross-module inlining.
-* Dynamic method binding with inlining and without virtual method table.
-* Compile time evaluation of user-defined functions.
-* Whole program dead code elimination: Only *used functions* are included in
-  the executable.
-* Value-based datatypes: For instance, objects and arrays can be allocated on
-  the stack.
-
-
-Nim is expressive
-=================
-
-* **The Nim compiler and all of the standard libraries are implemented in
-  Nim.**
-* Built-in high level datatypes: strings, sets, sequences, etc.
-* Modern type system with local type inference, tuples, variants,
-  generics, etc.
-* User-defineable operators; code with new operators is often easier to read
-  than code which overloads built-in operators. For example, a
-  ``=~`` operator is defined in the ``re`` module.
-* Macros can modify the abstract syntax tree at compile time.
-
-
-Nim is elegant
-==============
-
-* Macros can use the imperative paradigm to construct parse trees. Nim
-  does not require a different coding style for meta programming.
-* Macros cannot change Nim's syntax because there is no need for it.
-  Nim's syntax is flexible enough.
-* Statements are grouped by indentation but can span multiple lines.
-  Indentation must not contain tabulators so the compiler always sees
-  the code the same way as you do.
-
-
-Nim plays nice with others
-==========================
-
-* The Nim Compiler runs on Windows, Linux, BSD and Mac OS X.
-  Porting to other platforms is easy.
-* **The Nim Compiler can also generate C++ or Objective C for easier
-  interfacing.**
-* There are lots of bindings: for example, bindings to GTK2, the Windows API,
-  the POSIX API, OpenGL, SDL, Cairo, Python, Lua, TCL, X11, libzip, PCRE,
-  libcurl, mySQL and SQLite are included in the standard distribution or
-  can easily be obtained via the
-  `Nimble package manager <https://github.com/nim-lang/nimble>`_.
-* A C to Nim conversion utility: New bindings to C libraries are easily
-  generated by ``c2nim``.
-
-
-Roadmap to 1.0
-==============
-
-Please have a look at
-this `wiki page <https://github.com/nim-lang/Nim/wiki/Roadmap>`_ for
-an up-to-date overview.
diff --git a/web/learn.rst b/web/learn.rst
deleted file mode 100644
index c0b583429..000000000
--- a/web/learn.rst
+++ /dev/null
@@ -1,70 +0,0 @@
-Learning Nim
-============
-
-.. container:: standout
-
-  Tutorials
-  ---------
-
-  .. container:: tutorials
-
-    - | `Tutorial (part I) <docs/tut1.html>`_
-      | Learn the basics of Nim's types, variables, procedures, control flow, etc...
-
-    - | `Tutorial (part II) <docs/tut2.html>`_
-      | Learn Nim's more advanced features such as OOP, generics, macros, etc...
-
-
-.. container:: standout
-
-  Books
-  ---------
-
-  .. container:: books
-
-    - | `Nim in Action <https://manning.com/books/nim-in-action?a_aid=niminaction&a_bid=78a27e81>`_
-      | *Nim in Action* teaches
-        you all you need to know to begin writing powerful, portable
-        and efficient applications in Nim. It includes a handful of practical
-        and fun hands-on examples which teach you Nim.
-
-
-.. container:: standout
-
-  Examples
-  --------
-
-  .. container:: examples
-
-    - | `Nim by Example <http://nim-by-example.github.io/>`_
-      | Nim by Example is an excellent starting place for beginners.
-
-    - | `Nim on Rosetta Code <http://rosettacode.org/wiki/Category:Nimrod>`_
-      | Many different Nim code examples comparable to other languages for reference.
-
-    - | `Nim for C/C++ Programmers <https://github.com/nim-lang/Nim/wiki/Nim-for-C-programmers>`_
-      | A useful cheat-sheet for those most familiar with C/C++ languages.
-
-
-.. container:: standout
-
-  Articles
-  --------
-
-  .. container:: articles
-
-    - `How I Start: Nim <http://howistart.org/posts/nim/1>`_
-    - `Getting Started With Nim <https://akehrer.github.io/posts/getting-started-with-nim>`_
-    - `Getting Started With Nim - Part 2 <https://akehrer.github.io/posts/getting-started-with-nim-2>`_
-    - `What is special about Nim? <http://hookrace.net/blog/what-is-special-about-nim>`_
-    - `What makes Nim practical? <http://hookrace.net/blog/what-makes-nim-practical>`_
-    - `Learn Nim in minutes <http://learnxinyminutes.com/docs/nim>`_
-    - `Dr Dobbs Nimrod Publication <http://www.drdobbs.com/open-source/nimrod-a-new-systems-programming-languag/240165321>`_
-    - `Nim articles by Göran Krampe <http://goran.krampe.se/category/nim>`_
-
-
-Documentation
--------------
-
-More examples of Nim code can be found in the `Nim Language Documentation <docs/manual.html>`_.
-
diff --git a/web/news.rst b/web/news.rst
deleted file mode 100644
index 5086bc682..000000000
--- a/web/news.rst
+++ /dev/null
@@ -1,144 +0,0 @@
-====
-News
-====
-
-`2017-01-08 Nim Version 0.16.2 released <news/e031_version_0_16_2.html>`_
-===================================
-
-`2017-01-08 Nim Version 0.16.0 released <news/e029_version_0_16_0.html>`_
-===================================
-
-`2016-11-20 Nim in Action is going into production! <news/e030_nim_in_action_in_production.html>`_
-===================================
-
-`2016-10-23 Nim Version 0.15.2 released <news/e028_version_0_15_2.html>`_
-===================================
-
-`2016-09-30 Nim Version 0.15.0 released <news/e027_version_0_15_0.html>`_
-===================================
-
-`2016-09-03 Nim Community Survey results <news/e026_survey_results.html>`_
-===================================
-
-`2016-08-06 BountySource Update: The Road to v1.0 <news/e025_bountysource_update.html>`_
-===================================
-
-`2016-06-23 Launching the 2016 Nim community survey <news/e024_survey.html>`_
-===================================
-
-`2016-06-11 Version 0.14.2 released <news/e023_version_0_14_2.html>`_
-===================================
-
-`2016-06-07 Version 0.14.0 released <news/e022_version_0_14_0.html>`_
-===================================
-
-`2016-06-04 Meet our BountySource sponsors <news/e021_meet_sponsors.html>`_
-===================================
-
-`2016-01-27 Nim in Action is now available! <news/e020_nim_in_action.html>`_
-==================================
-
-`2016-01-18 Version 0.13.0 released <news/e019_version_0_13_0.html>`_
-==================================
-
-`2016-01-18 Andreas Rumpf's talk at OSCON Amsterdam <news/e018_oscon_amsterdam.html>`_
-==================================================
-
-`2015-10-27 Version 0.12.0 released <news/e017_version_0_12_0.html>`_
-==================================
-
-`2015-10-16 First Nim conference <news/e016_nim_conf1.html>`_
-===============================
-
-`2015-05-04 Version 0.11.2 released <news/e015_version_0_11_2.html>`_
-==================================
-
-`2015-04-30 Version 0.11.0 released <news/e014_version_0_11_0.html>`_
-==================================
-
-`2014-12-29 Version 0.10.2 released <news/e013_version_0_10_2.html>`_
-==================================
-
-
-`2014-10-19 Version 0.9.6 released <news/e012_version_0_9_6.html>`_
-=================================
-
-
-`2014-04-21 Version 0.9.4 released <news/e011_version_0_9_4.html>`_
-=================================
-
-
-`2014-02-11 Nimrod Featured in Dr. Dobb's Journal <news/e010_dr_dobbs_journal.html>`_
-================================================
-
-
-`2014-01-15 Andreas Rumpf's talk on Nimrod at Strange Loop 2013 is now online <news/e009_andreas_rumpfs_talk.html>`_
-============================================================================
-
-
-`2013-05-20 New website design! <news/e008_new_website.html>`_
-==============================
-
-
-
-`2013-05-20 Version 0.9.2 released <news/e007_version_0_9_2.html>`_
-=================================
-
-
-
-`2012-09-23 Version 0.9.0 released <news/e006_version_0_9_0.html>`_
-=================================
-
-
-
-`2012-02-09 Version 0.8.14 released <news/e005_version_0_8_14.html>`_
-==================================
-
-
-
-`2011-07-10 Version 0.8.12 released <news/e004_version_0_8_12.html>`_
-==================================
-
-
-`2010-10-20 Version 0.8.10 released <news/e003_version_0_8_10.html>`_
-==================================
-
-
-
-`2010-03-14 Version 0.8.8 released <news/e002_version_0_8_8.html>`_
-=================================
-
-
-`2009-12-21 Version 0.8.6 released <news/e001_version_0_8_6.html>`_
-=================================
-
-
-2009-10-21 Version 0.8.2 released
-=================================
-
-
-2009-09-12 Version 0.8.0 released
-=================================
-
-
-2009-06-08 Version 0.7.10 released
-==================================
-
-
-2009-05-08 Version 0.7.8 released
-=================================
-
-
-2009-04-22 Version 0.7.6 released
-=================================
-
-
-2008-11-16 Version 0.7.0 released
-=================================
-
-
-2008-08-22 Version 0.6.0 released
-=================================
-
-Nimrod version 0.6.0 has been released!
-**This is the first version of the compiler that is able to compile itself!**
diff --git a/web/news/e001_version_0_8_6.rst b/web/news/e001_version_0_8_6.rst
deleted file mode 100644
index 019168a44..000000000
--- a/web/news/e001_version_0_8_6.rst
+++ /dev/null
@@ -1,54 +0,0 @@
-2009-12-21 Version 0.8.6 released
-=================================
-
-.. container:: metadata
-
-  Posted by Andreas Rumpf on 21/12/2009
-
-The version jump from 0.8.2 to 0.8.6 acknowledges the fact that all development
-of the compiler is now done in Nimrod.
-
-
-Bugfixes
---------
-- The pragmas ``hint[X]:off`` and ``warning[X]:off`` now work.
-- Method call syntax for iterators works again (``for x in lines.split()``).
-- Fixed a typo in ``removeDir`` for POSIX that lead to an infinite recursion.
-- The compiler now checks that module filenames are valid identifiers.
-- Empty patterns for the ``dynlib`` pragma are now possible.
-- ``os.parseCmdLine`` returned wrong results for trailing whitespace.
-- Inconsequent tuple usage (using the same tuple with and without named fields)
-  does not crash the code generator anymore.
-- A better error message is provided when the loading of a proc within a
-  dynamic lib fails.
-
-
-Additions
----------
-- Added ``system.contains`` for open arrays.
-- The PEG module now supports the *search loop operator* ``@``.
-- Grammar/parser: ``SAD|IND`` is allowed before any kind of closing bracket.
-  This allows for more flexible source code formating.
-- The compiler now uses a *bind* table for symbol lookup within a ``bind``
-  context. (See `<manual.html#templates>`_ for details.)
-- ``discard """my long comment"""`` is now optimized away.
-- New ``--floatChecks: on|off`` switches and pragmas for better debugging
-  of floating point operations. (See
-  `<manual.html#pre-defined-floating-point-types>`_ for details.)
-- The manual has been improved. (Many thanks to Philippe Lhoste!)
-
-
-Changes affecting backwards compatibility
------------------------------------------
-- The compiler does not skip the linking step anymore even if no file
-  has changed.
-- ``os.splitFile(".xyz")`` now returns ``("", ".xyz", "")`` instead of
-  ``("", "", ".xyz")``. So filenames starting with a dot are handled
-  differently.
-- ``strutils.split(s: string, seps: set[char])`` never yields the empty string
-  anymore. This behaviour is probably more appropriate for whitespace splitting.
-- The compiler now stops after the ``--version`` command line switch.
-- Removed support for enum inheritance in the parser; enum inheritance has
-  never been documented anyway.
-- The ``msg`` field of ``system.E_base`` has now the type ``string``, instead
-  of ``cstring``. This improves memory safety.
diff --git a/web/news/e002_version_0_8_8.rst b/web/news/e002_version_0_8_8.rst
deleted file mode 100644
index 2df476814..000000000
--- a/web/news/e002_version_0_8_8.rst
+++ /dev/null
@@ -1,82 +0,0 @@
-Version 0.8.8 released
-======================
-
-.. container:: metadata
-
-  Posted by Andreas Rumpf on 14/03/2010
-
-Bugfixes
---------
-- The Posix version of ``os.copyFile`` has better error handling.
-- Fixed bug #502670 (underscores in identifiers).
-- Fixed a bug in the ``parsexml`` module concerning the parsing of
-  ``<tag attr="value" />``.
-- Fixed a bug in the ``parsexml`` module concerning the parsing of
-  enities like ``&ltXX``.
-- ``system.write(f: TFile, s: string)`` now works even if ``s`` contains binary
-  zeros.
-- Fixed a bug in ``os.setFilePermissions`` for Windows.
-- An overloadable symbol can now have the same name as an imported module.
-- Fixed a serious bug in ``strutils.cmpIgnoreCase``.
-- Fixed ``unicode.toUTF8``.
-- The compiler now rejects ``'\n'`` (use ``"\n"`` instead).
-- ``times.getStartMilsecs()`` now works on Mac OS X.
-- Fixed a bug in ``pegs.match`` concerning start offsets.
-- Lots of other little bugfixes.
-
-
-Additions
----------
-- Added ``system.cstringArrayToSeq``.
-- Added ``system.lines(f: TFile)`` iterator.
-- Added ``system.delete``, ``system.del`` and ``system.insert`` for sequences.
-- Added ``system./`` for int.
-- Exported ``system.newException`` template.
-- Added ``cgi.decodeData(data: string): tuple[key, value: string]``.
-- Added ``strutils.insertSep``.
-- Added ``math.trunc``.
-- Added ``ropes`` module.
-- Added ``sockets`` module.
-- Added ``browsers`` module.
-- Added ``httpserver`` module.
-- Added ``httpclient`` module.
-- Added ``parseutils`` module.
-- Added ``unidecode`` module.
-- Added ``xmldom`` module.
-- Added ``xmldomparser`` module.
-- Added ``xmltree`` module.
-- Added ``xmlparser`` module.
-- Added ``htmlparser`` module.
-- Added ``re`` module.
-- Added ``graphics`` module.
-- Added ``colors`` module.
-- Many wrappers now do not contain redundant name prefixes (like ``GTK_``,
-  ``lua``). The old wrappers are still available in ``lib/oldwrappers``.
-  You can change your configuration file to use these.
-- Triple quoted strings allow for ``"`` in more contexts.
-- ``""`` within raw string literals stands for a single quotation mark.
-- Arguments to ``openArray`` parameters can be left out.
-- More extensive subscript operator overloading. (To be documented.)
-- The documentation generator supports the ``.. raw:: html`` directive.
-- The Pegs module supports back references via the notation ``$capture_index``.
-
-
-Changes affecting backwards compatibility
------------------------------------------
-
-- Overloading of the subscript operator only works if the type does not provide
-  a built-in one.
-- The search order for libraries which is affected by the ``path`` option
-  has been reversed, so that the project's path is searched before
-  the standard library's path.
-- The compiler does not include a Pascal parser for bootstrapping purposes any
-  more. Instead there is a ``pas2nim`` tool that contains the old functionality.
-- The procs ``os.copyFile`` and ``os.moveFile`` have been deprecated
-  temporarily, so that the compiler warns about their usage. Use them with
-  named arguments only, because the parameter order will change the next
-  version!
-- ``atomic`` and ``let`` are now keywords.
-- The ``\w`` character class for pegs now includes the digits ``'0'..'9'``.
-- Many wrappers now do not contain redundant name prefixes (like ``GTK_``,
-  ``lua``) anymore.
-- Arguments to ``openArray`` parameters can be left out.
diff --git a/web/news/e003_version_0_8_10.rst b/web/news/e003_version_0_8_10.rst
deleted file mode 100644
index f72c0076c..000000000
--- a/web/news/e003_version_0_8_10.rst
+++ /dev/null
@@ -1,70 +0,0 @@
-Version 0.8.10 released
-=======================
-
-.. container:: metadata
-
-  Posted by Andreas Rumpf on 20/10/2010
-
-Bugfixes
---------
-
-- Bugfix: Command line parsing on Windows and ``os.parseCmdLine`` now adheres
-  to the same parsing rules as Microsoft's C/C++ startup code.
-- Bugfix: Passing a ``ref`` pointer to the untyped ``pointer`` type is invalid.
-- Bugfix: Updated ``keyval`` example.
-- Bugfix: ``system.splitChunk`` still contained code for debug output.
-- Bugfix: ``dialogs.ChooseFileToSave`` uses ``STOCK_SAVE`` instead of
-  ``STOCK_OPEN`` for the GTK backend.
-- Bugfix: Various bugs concerning exception handling fixed.
-- Bugfix: ``low(somestring)`` crashed the compiler.
-- Bugfix: ``strutils.endsWith`` lacked range checking.
-- Bugfix: Better detection for AMD64 on Mac OS X.
-
-
-Changes affecting backwards compatibility
------------------------------------------
-
-- Reversed parameter order for ``os.copyFile`` and ``os.moveFile``!!!
-- Procs not marked as ``procvar`` cannot only be passed to a procvar anymore,
-  unless they are used in the same module.
-- Deprecated ``times.getStartMilsecs``: Use ``epochTime`` or ``cpuTime``
-  instead.
-- Removed ``system.OpenFile``.
-- Removed ``system.CloseFile``.
-- Removed ``strutils.replaceStr``.
-- Removed ``strutils.deleteStr``.
-- Removed ``strutils.splitLinesSeq``.
-- Removed ``strutils.splitSeq``.
-- Removed ``strutils.toString``.
-- If a DLL cannot be loaded (via the ``dynlib`` pragma) ``EInvalidLibrary``
-  is not raised anymore. Instead ``system.quit()`` is called. This is because
-  raising an exception requires heap allocations. However the memory manager
-  might be contained in the DLL that failed to load.
-- The ``re`` module (and the ``pcre`` wrapper) now depend on the pcre dll.
-
-
-Additions
----------
-
-- The ``{.compile: "file.c".}`` pragma uses a CRC check to see if the file
-  needs to be recompiled.
-- Added ``system.reopen``.
-- Added ``system.getCurrentException``.
-- Added ``system.appType``.
-- Added ``system.compileOption``.
-- Added ``times.epochTime`` and ``times.cpuTime``.
-- Implemented explicit type arguments for generics.
-- Implemented ``{.size: sizeof(cint).}`` pragma for enum types. This is useful
-  for interfacing with C.
-- Implemented ``{.pragma.}`` pragma for user defined pragmas.
-- Implemented ``{.extern.}`` pragma for better control of name mangling.
-- The ``importc`` and ``exportc`` pragmas support format strings:
-  ``proc p{.exportc: "nim_$1".}`` exports ``p`` as ``nim_p``. This is useful
-  for user defined pragmas.
-- The standard library can be built as a DLL. Generating DLLs has been
-  improved.
-- Added ``expat`` module.
-- Added ``json`` module.
-- Added support for a *Tiny C* backend. Currently this only works on Linux.
-  You need to bootstrap with ``-d:tinyc`` to enable Tiny C support. Nimrod
-  can then execute code directly via ``nimrod run myfile``.
diff --git a/web/news/e004_version_0_8_12.rst b/web/news/e004_version_0_8_12.rst
deleted file mode 100644
index 5f154b2db..000000000
--- a/web/news/e004_version_0_8_12.rst
+++ /dev/null
@@ -1,122 +0,0 @@
-Version 0.8.12 released
-==================================
-
-.. container:: metadata
-
-  Posted by Andreas Rumpf on 10/07/2011
-
-Bugfixes
---------
-
-- Bugfix: ``httpclient`` correct passes the path starting with ``/``.
-- Bugfixes for the ``htmlparser`` module.
-- Bugfix: ``pegs.find`` did not respect ``start`` parameter.
-- Bugfix: ``dialogs.ChooseFilesToOpen`` did not work if only one file is
-  selected.
-- Bugfix: niminst: ``nimrod`` is not default dir for *every* project.
-- Bugfix: Multiple yield statements in iterators did not cause local vars to be
-  copied.
-- Bugfix: The compiler does not emit very inaccurate floating point literals
-  anymore.
-- Bugfix: Subclasses are taken into account for ``try except`` matching.
-- Bugfix: Generics and macros are more stable. There are still known bugs left
-  though.
-- Bugfix: Generated type information for tuples was sometimes wrong, causing
-  random crashes.
-- Lots of other bugfixes: Too many to list them all.
-
-
-Changes affecting backwards compatibility
------------------------------------------
-
-- Operators starting with ``^`` are now right-associative and have the highest
-  priority.
-- Deprecated ``os.getApplicationFilename``: Use ``os.getAppFilename`` instead.
-- Deprecated ``os.getApplicationDir``: Use ``os.getAppDir`` instead.
-- Deprecated ``system.copy``: Use ``substr`` or string slicing instead.
-- Changed and documented how generalized string literals work: The syntax
-  ``module.re"abc"`` is now supported.
-- Changed the behaviour of ``strutils.%``, ``ropes.%``
-  if both notations ``$#`` and ``$i`` are involved.
-- The ``pegs`` and ``re`` modules distinguish between ``replace``
-  and ``replacef`` operations.
-- The pointer dereference operation ``p^`` is deprecated and might become
-  ``^p`` in later versions or be dropped entirely since it is rarely used.
-  Use the new notation ``p[]`` in the rare cases where you need to
-  dereference a pointer explicitly.
-- ``system.readFile`` does not return ``nil`` anymore but raises an ``EIO``
-  exception instead.
-- Unsound co-/contravariance for procvars has been removed.
-
-
-Language Additions
-------------------
-
-- Source code filters are now documented.
-- Added the ``linearScanEnd``, ``unroll``, ``shallow`` pragmas.
-- Added ``emit`` pragma for direct code generator control.
-- Case statement branches support constant sets for programming convenience.
-- Tuple unpacking is not enforced in ``for`` loops anymore.
-- The compiler now supports array, sequence and string slicing.
-- A field in an ``enum`` may be given an explicit string representation.
-  This yields more maintainable code than using a constant
-  ``array[TMyEnum, string]`` mapping.
-- Indices in array literals may be explicitly given, enhancing readability:
-  ``[enumValueA: "a", enumValueB: "b"]``.
-- Added thread support via the ``threads`` core module and
-  the ``--threads:on`` command line switch.
-- The built-in iterators ``system.fields`` and ``system.fieldPairs`` can be
-  used to iterate over any field of a tuple. With this mechanism operations
-  like ``==`` and ``hash`` are lifted to tuples.
-- The slice ``..`` is now a first-class operator, allowing code like:
-  ``x in 1000..100_000``.
-
-
-Compiler Additions
-------------------
-
-- The compiler supports IDEs via the new group of ``idetools`` command line
-  options.
-- The *interactive mode* (REPL) has been improved and documented for the
-  first time.
-- The compiler now might use hashing for string case statements depending
-  on the number of string literals in the case statement.
-
-
-Library Additions
------------------
-
-- Added ``lists`` module which contains generic linked lists.
-- Added ``sets`` module which contains generic hash sets.
-- Added ``tables`` module which contains generic hash tables.
-- Added ``queues`` module which contains generic sequence based queues.
-- Added ``intsets`` module which contains a specialized int set data type.
-- Added ``scgi`` module.
-- Added ``smtp`` module.
-- Added ``encodings`` module.
-- Added ``re.findAll``, ``pegs.findAll``.
-- Added ``os.findExe``.
-- Added ``parseutils.parseUntil`` and ``parseutils.parseWhile``.
-- Added ``strutils.align``, ``strutils.tokenize``, ``strutils.wordWrap``.
-- Pegs support a *captured search loop operator* ``{@}``.
-- Pegs support new built-ins: ``\letter``, ``\upper``, ``\lower``,
-  ``\title``, ``\white``.
-- Pegs support the new built-in ``\skip`` operation.
-- Pegs support the ``$`` and ``^`` anchors.
-- Additional operations were added to the ``complex`` module.
-- Added ``strutils.formatFloat``,  ``strutils.formatBiggestFloat``.
-- Added unary ``<`` for nice looking excluding upper bounds in ranges.
-- Added ``math.floor``.
-- Added ``system.reset`` and a version of ``system.open`` that
-  returns a ``TFile`` and raises an exception in case of an error.
-- Added a wrapper for ``redis``.
-- Added a wrapper for ``0mq`` via the ``zmq`` module.
-- Added a wrapper for ``sphinx``.
-- Added ``system.newStringOfCap``.
-- Added ``system.raiseHook`` and ``system.outOfMemHook``.
-- Added ``system.writeFile``.
-- Added ``system.shallowCopy``.
-- ``system.echo`` is guaranteed to be thread-safe.
-- Added ``prelude`` include file for scripting convenience.
-- Added ``typeinfo`` core module for access to runtime type information.
-- Added ``marshal`` module for JSON serialization.
diff --git a/web/news/e005_version_0_8_14.rst b/web/news/e005_version_0_8_14.rst
deleted file mode 100644
index 4050c8b93..000000000
--- a/web/news/e005_version_0_8_14.rst
+++ /dev/null
@@ -1,168 +0,0 @@
-2012-02-09 Version 0.8.14 released
-==================================
-
-.. container:: metadata
-
-  Posted by Andreas Rumpf on 09/02/2012
-
-Version 0.8.14 has been released!
-
-Bugfixes
---------
-
-- Fixed a serious memory corruption concerning message passing.
-- Fixed a serious bug concerning different instantiations of a generic proc.
-- Fixed a newly introduced bug where a wrong ``EIO`` exception was raised for
-  the end of file for text files that do not end with a newline.
-- Bugfix c2nim, c2pas: the ``--out`` option has never worked properly.
-- Bugfix: forwarding of generic procs never worked.
-- Some more bugfixes for macros and compile-time evaluation.
-- The GC now takes into account interior pointers on the stack which may be
-  introduced by aggressive C optimizers.
-- Nimrod's native allocator/GC now works on PowerPC.
-- Lots of other bugfixes: Too many to list them all.
-
-
-Changes affecting backwards compatibility
------------------------------------------
-
-- Removed deprecated ``os.AppendFileExt``, ``os.executeShellCommand``,
-  ``os.iterOverEnvironment``, ``os.pcDirectory``, ``os.pcLinkToDirectory``,
-  ``os.SplitPath``, ``os.extractDir``, ``os.SplitFilename``,
-  ``os.extractFileTrunk``, ``os.extractFileExt``, ``osproc.executeProcess``,
-  ``osproc.executeCommand``.
-- Removed deprecated ``parseopt.init``, ``parseopt.getRestOfCommandLine``.
-- Moved ``strutils.validEmailAddress`` to ``matchers.validEmailAddress``.
-- The pointer dereference operator ``^`` has been removed, so that ``^``
-  can now be a user-defined operator.
-- ``implies`` is no keyword anymore.
-- The ``is`` operator is now the ``of`` operator.
-- The ``is`` operator is now used to check type equivalence in generic code.
-- The ``pure`` pragma for procs has been renamed to ``noStackFrame``.
-- The threading API has been completely redesigned.
-- The ``unidecode`` module is now thread-safe and its interface has changed.
-- The ``bind`` expression is deprecated, use a ``bind`` declaration instead.
-- ``system.raiseHook`` is now split into ``system.localRaiseHook`` and
-  ``system.globalRaiseHook`` to distinguish between thread local and global
-  raise hooks.
-- Changed exception handling/error reporting for ``os.removeFile`` and
-  ``os.removeDir``.
-- The algorithm for searching and loading configuration files has been changed.
-- Operators now have diffent precedence rules: Assignment-like operators
-  (like ``*=``) are now special-cased.
-- The fields in ``TStream`` have been renamed to have an ``Impl`` suffix
-  because they should not be used directly anymore.
-  Wrapper procs have been created that should be used instead.
-- ``export`` is now a keyword.
-- ``assert`` is now implemented in pure Nimrod as a template; it's easy
-  to implement your own assertion templates with ``system.astToStr``.
-
-
-Language Additions
-------------------
-
-- Added new ``is`` and ``of`` operators.
-- The built-in type ``void`` can be used to denote the absence of any type.
-  This is useful in generic code.
-- Return types may be of the type ``var T`` to return an l-value.
-- The error pragma can now be used to mark symbols whose *usage* should trigger
-  a compile-time error.
-- There is a new ``discardable`` pragma that can be used to mark a routine
-  so that its result can be discarded implicitly.
-- Added a new ``noinit`` pragma to prevent automatic initialization to zero
-  of variables.
-- Constants can now have the type ``seq``.
-- There is a new user-definable syntactic construct ``a{i, ...}``
-  that has no semantics yet for built-in types and so can be overloaded to your
-  heart's content.
-- ``bind`` (used for symbol binding in templates and generics) is now a
-  declarative statement.
-- Nimrod now supports single assignment variables via the ``let`` statement.
-- Iterators named ``items`` and ``pairs`` are implicitly invoked when
-  an explicit iterator is missing.
-- The slice assignment ``a[i..j] = b`` where ``a`` is a sequence or string
-  now supports *splicing*.
-
-
-Compiler Additions
-------------------
-
-- The compiler can generate C++ code for easier interfacing with C++.
-- The compiler can generate Objective C code for easier interfacing with
-  Objective C.
-- The new pragmas ``importcpp`` and ``importobjc`` make interfacing with C++
-  and Objective C somewhat easier.
-- Added a new pragma ``incompleteStruct`` to deal with incomplete C struct
-  definitions.
-- Added a ``--nimcache:PATH`` configuration option for control over the output
-  directory for generated code.
-- The ``--genScript`` option now produces different compilation scripts
-  which do not contain absolute paths.
-- Added ``--cincludes:dir``, ``--clibdir:lib`` configuration options for
-  modifying the C compiler's header/library search path in cross-platform way.
-- Added ``--clib:lib`` configuration option for specifying additional
-  C libraries to be linked.
-- Added ``--mainmodule:file`` configuration options for specifying the main
-  project file. This is intended to be used in project configuration files to
-  allow commands like ``nimrod c`` or ``nimrod check`` to be executed anywhere
-  within the project's directory structure.
-- Added a ``--app:staticlib`` option for creating static libraries.
-- Added a ``--tlsEmulation:on|off`` switch for control over thread local
-  storage emulation.
-- The compiler and standard library now support a *taint mode*. Input strings
-  are declared with the ``TaintedString`` string type. If the taint
-  mode is turned on it is a distinct string type which helps to detect input
-  validation errors.
-- The compiler now supports the compilation cache via ``--symbolFiles:on``.
-  This potentially speeds up compilations by an order of magnitude, but is
-  still highly experimental!
-- Added ``--import:file`` and ``--include:file`` configuration options
-  for specifying modules that will be automatically imported/incluced.
-- ``nimrod i`` can now optionally be given a module to execute.
-- The compiler now performs a simple alias analysis to generate better code.
-- The compiler and ENDB now support *watchpoints*.
-- The compiler now supports proper compile time expressions of type ``bool``
-  for ``on|off`` switches in pragmas. In order to not break existing code,
-  ``on`` and ``off`` are now aliases for ``true`` and ``false`` and declared
-  in the system module.
-- The compiler finally supports **closures**. This is a preliminary
-  implementation, which does not yet support nestings deeper than 1 level
-  and still has many known bugs.
-
-
-Library Additions
------------------
-
-- Added ``system.allocShared``, ``system.allocShared0``,
-  ``system.deallocShared``, ``system.reallocShared``.
-- Slicing as implemented by the system module now supports *splicing*.
-- Added explicit channels for thread communication.
-- Added ``matchers`` module for email address etc. matching.
-- Added ``strutils.unindent``, ``strutils.countLines``,
-  ``strutils.replaceWord``.
-- Added ``system.slurp`` for easy resource embedding.
-- Added ``system.running`` for threads.
-- Added ``system.programResult``.
-- Added ``xmltree.innerText``.
-- Added ``os.isAbsolute``, ``os.dynLibFormat``, ``os.isRootDir``,
-  ``os.parentDirs``.
-- Added ``parseutils.interpolatedFragments``.
-- Added ``macros.treeRepr``, ``macros.lispRepr``, ``macros.dumpTree``,
-  ``macros.dumpLisp``, ``macros.parseExpr``, ``macros.parseStmt``,
-  ``macros.getAst``.
-- Added ``locks`` core module for more flexible locking support.
-- Added ``irc`` module.
-- Added ``ftpclient`` module.
-- Added ``memfiles`` module.
-- Added ``subexes`` module.
-- Added ``critbits`` module.
-- Added ``asyncio`` module.
-- Added ``actors`` module.
-- Added ``algorithm`` module for generic ``sort``, ``reverse`` etc. operations.
-- Added ``osproc.startCmd``, ``osproc.execCmdEx``.
-- The ``osproc`` module now uses ``posix_spawn`` instead of ``fork``
-  and ``exec`` on Posix systems. Define the symbol ``useFork`` to revert to
-  the old implementation.
-- Added ``intsets.assign``.
-- Added ``system.astToStr`` and ``system.rand``, ``system.doAssert``.
-- Added ``system.pairs`` for built-in types like arrays and strings.
diff --git a/web/news/e006_version_0_9_0.rst b/web/news/e006_version_0_9_0.rst
deleted file mode 100644
index 5635ca94c..000000000
--- a/web/news/e006_version_0_9_0.rst
+++ /dev/null
@@ -1,182 +0,0 @@
-Version 0.9.0 released
-======================
-
-.. container:: metadata
-
-  Posted by Andreas Rumpf on 23/09/2012
-
-Summary
--------
-
-* Unsigned integers have been added.
-* The integer type promotion rules changed.
-* The template and macro system evolved.
-* Closures have been implemented.
-* Term rewriting macros have been implemented.
-* First steps to unify expressions and statements have been taken.
-* Symbol lookup rules in generics have become stricter to catch more errors.
-
-
-Bugfixes
---------
-
-- Fixed a bug where the compiler would "optimize away" valid constant parts of
-  a string concatenation.
-- Fixed a bug concerning implicit type conversions in ``case`` statements.
-- Fixed a serious code generation bug that caused ``algorithm.sort`` to
-  produce segmentation faults.
-- Fixed ambiguity in recvLine which meant that receiving ``\r\L`` was
-  indistinguishable from disconnections.
-- Many more bugfixes, too many to list them all.
-
-
-Library Additions
------------------
-
-- Added the (already existing) module ``htmlgen`` to the documentation.
-- Added the (already existing) module ``cookies`` to the documentation.
-- Added ``system.shallow`` that can be used to speed up string and sequence
-  assignments.
-- Added ``system.eval`` that can execute an anonymous block of code at
-  compile time as if was a macro.
-- Added ``system.staticExec`` and ``system.gorge`` for compile-time execution
-  of external programs.
-- Added ``system.staticRead`` as a synonym for ``system.slurp``.
-- Added ``macros.emit`` that can emit an arbitrary computed string as nimrod
-  code during compilation.
-- Added ``strutils.parseEnum``.
-- Added ``json.%`` constructor operator.
-- The stdlib can now be avoided to a point where C code generation for 16bit
-  micro controllers is feasible.
-- Added module ``oids``.
-- Added module ``endians``.
-- Added a new OpenGL wrapper that supports OpenGL up to version 4.2.
-- Added a wrapper for ``libsvm``.
-- Added a wrapper for ``mongodb``.
-- Added ``terminal.isatty``.
-- Added an overload for ``system.items`` that can be used to iterate over the
-  values of an enum.
-- Added ``system.TInteger`` and ``system.TNumber`` type classes matching
-  any of the corresponding types available in Nimrod.
-- Added ``system.clamp`` to limit a value within an interval ``[a, b]``.
-- Added ``strutils.continuesWith``.
-- Added ``system.getStackTrace``.
-- Added ``system.||`` for parallel ``for`` loop support.
-- The GC supports (soft) realtime systems via ``GC_setMaxPause``
-  and ``GC_step`` procs.
-- The sockets module now supports ssl through the OpenSSL library, ``recvLine``
-  is now much more efficient thanks to the newly implemented sockets buffering.
-- The httpclient module now supports ssl/tls.
-- Added ``times.format`` as well as many other utility functions
-  for managing time.
-- Added ``system.@`` for converting an ``openarray`` to a ``seq`` (it used to
-  only support fixed length arrays).
-- Added ``system.compiles`` which can be used to check whether a type supports
-  some operation.
-- Added ``strutils.format``, ``subexes.format`` which use the
-  new ``varargs`` type.
-- Added module ``fsmonitor``.
-
-Changes affecting backwards compatibility
------------------------------------------
-
-- On Windows filenames and paths are supposed to be in UTF-8.
-  The ``system``, ``os``, ``osproc`` and ``memfiles`` modules use the wide
-  string versions of the WinAPI. Use the ``-d:useWinAnsi`` switch to revert
-  back to the old behaviour which uses the Ansi string versions.
-- ``static``, ``do``, ``interface`` and ``mixin`` are now keywords.
-- Templates now participate in overloading resolution which can break code that
-  uses templates in subtle ways. Use the new ``immediate`` pragma for templates
-  to get a template of old behaviour.
-- There is now a proper distinction in the type system between ``expr`` and
-  ``PNimrodNode`` which unfortunately breaks the old macro system.
-- ``pegs.@`` has been renamed to ``pegs.!*`` and ``pegs.@@`` has been renamed
-  to ``pegs.!*\`` as ``@`` operators now have different precedence.
-- The type ``proc`` (without any params or return type) is now considered a
-  type class matching all proc types. Use ``proc ()`` to get the old meaning
-  denoting a proc expecing no arguments and returing no value.
-- Deprecated ``system.GC_setStrategy``.
-- ``re.findAll`` and ``pegs.findAll`` don't return *captures* anymore but
-  matching *substrings*.
-- RTTI and thus the ``marshall`` module don't contain the proper field names
-  of tuples anymore. This had to be changed as the old behaviour never
-  produced consistent results.
-- Deprecated the ``ssl`` module.
-- Deprecated ``nimrod pretty`` as it never worked good enough and has some
-  inherent problems.
-- The integer promotion rules changed; the compiler is now less picky in some
-  situations and more picky in other situations: In particular implicit
-  conversions from ``int`` to ``int32`` are now forbidden.
-- ``system.byte`` is now an alias for ``uint8``; it used to be an alias
-  to ``int8``.
-- ``bind`` expressions in templates are not properly supported anymore. Use
-  the declarative ``bind`` statement instead.
-- The default calling convention for a procedural **type** is now ``closure``,
-  for procs it remains ``nimcall`` (which is compatible to ``closure``).
-  Activate the warning ``ImplicitClosure`` to make the compiler list the
-  occurrences of proc types which are affected.
-- The Nimrod type system now distinguishes ``openarray`` from ``varargs``.
-- Templates are now ``hygienic``. Use the ``dirty`` pragma to get the old
-  behaviour.
-- Objects that have no ancestor are now implicitly ``final``. Use
-  the ``inheritable`` pragma to introduce new object roots apart
-  from ``TObject``.
-- Macros now receive parameters like templates do; use the ``callsite`` builtin
-  to gain access to the invocation AST.
-- Symbol lookup rules in generics have become stricter to catch more errors.
-
-
-Compiler Additions
-------------------
-
-- Win64 is now an officially supported target.
-- The Nimrod compiler works on BSD again, but has some issues
-  as ``os.getAppFilename`` and ``os.getAppDir`` cannot work reliably on BSD.
-- The compiler can detect and evaluate calls that can be evaluated at compile
-  time for optimization purposes with the ``--implicitStatic`` command line
-  option or pragma.
-- The compiler now generates marker procs that the GC can use instead of RTTI.
-  This speeds up the GC quite a bit.
-- The compiler now includes a new advanced documentation generator
-  via the ``doc2`` command. This new generator uses all of the semantic passes
-  of the compiler and can thus generate documentation for symbols hiding in
-  macros.
-- The compiler now supports the ``dynlib`` pragma for variables.
-- The compiler now supports ``bycopy`` and ``byref`` pragmas that affect how
-  objects/tuples are passed.
-- The embedded profiler became a stack trace profiler and has been documented.
-
-
-Language Additions
-------------------
-
-- Added explicit ``static`` sections for enforced compile time evaluation.
-- Added an alternative notation for lambdas with ``do``.
-- ``addr`` is now treated like a prefix operator syntactically.
-- Added ``global`` pragma that can be used to introduce new global variables
-  from within procs.
-- ``when`` expressions are now allowed just like ``if`` expressions.
-- The precedence for operators starting with ``@`` is different now
-  allowing for *sigil-like* operators.
-- Stand-alone ``finally`` and ``except`` blocks are now supported.
-- Macros and templates can now be invoked as pragmas.
-- The apostrophe in type suffixes for numerical literals is now optional.
-- Unsigned integer types have been added.
-- The integer promotion rules changed.
-- Nimrod now tracks proper intervals for ``range`` over some built-in operators.
-- In parameter lists a semicolon instead of a comma can be used to improve
-  readability: ``proc divmod(a, b: int; resA, resB: var int)``.
-- A semicolon can now be used to have multiple simple statements on a single
-  line: ``inc i; inc j``.
-- ``bind`` supports overloaded symbols and operators.
-- A ``distinct`` type can now borrow from generic procs.
-- Added the pragmas ``gensym``, ``inject`` and ``dirty`` for hygiene
-  in templates.
-- Comments can be continued with a backslash continuation character so that
-  comment pieces don't have to align on the same column.
-- Enums can be annotated with ``pure`` so that their field names do not pollute
-  the current scope.
-- A proc body can consist of an expression that has a type. This is rewritten
-  to ``result = expression`` then.
-- Term rewriting macros (see `trmacros <http://nimrod-code.org/trmacros.html>`_)
-  have been implemented but are still in alpha.
diff --git a/web/news/e007_version_0_9_2.rst b/web/news/e007_version_0_9_2.rst
deleted file mode 100644
index 89352c06c..000000000
--- a/web/news/e007_version_0_9_2.rst
+++ /dev/null
@@ -1,118 +0,0 @@
-Version 0.9.2 released
-======================
-
-.. container:: metadata
-
-  Posted by Dominik Picheta on 20/05/2013
-
-We are pleased to announce that version 0.9.2 of the Nimrod compiler has been
-released. This release has attracted by far the most contributions in comparison
-to any other release.
-
-This release brings with it many new features and bug fixes, a list of which
-can be seen later. One of the major new features is the effect system together
-with exception tracking which allows for checked exceptions and more,
-for further details check out the `manual <manual.html#effect-system>`_.
-Another major new feature is the introduction of statement list expressions,
-more details on these can be found `here <manual.html#statement-list-expression>`_.
-The ability to exclude symbols from modules has also been
-implemented, this feature can be used like so: ``import module except symbol``.
-
-Thanks to all `contributors <https://github.com/Araq/Nimrod/contributors>`_!
-
-Bugfixes
---------
-
-- The old GC never collected cycles correctly. Fixed but it can cause
-  performance regressions. However you can deactivate the cycle collector
-  with ``GC_disableMarkAndSweep`` and run it explicitly at an appropriate time
-  or not at all. There is also a new GC you can activate
-  with ``--gc:markAndSweep`` which does not have this problem but is slower in
-  general and has no realtime guarantees.
-- ``cast`` for floating point types now does the bitcast as specified in the
-  manual. This breaks code that erroneously uses ``cast`` to convert different
-  floating point values.
-- SCGI module's performance has been improved greatly, it will no longer block
-  on many concurrent requests.
-- In total fixed over 70 github issues and merged over 60 pull requests.
-
-
-Library Additions
------------------
-
-- There is a new experimental mark&sweep GC which can be faster (or much
-  slower) than the default GC. Enable with ``--gc:markAndSweep``.
-- Added ``system.onRaise`` to support a condition system.
-- Added ``system.locals`` that provides access to a proc's locals.
-- Added ``macros.quote`` for AST quasi-quoting.
-- Added ``system.unsafeNew`` to support hacky variable length objects.
-- ``system.fields`` and ``system.fieldPairs`` support ``object`` too; they
-  used to only support tuples.
-- Added ``system.CurrentSourcePath`` returning the full file-system path of
-  the current source file.
-- The ``macros`` module now contains lots of useful helpers for building up
-  abstract syntax trees.
-
-
-Changes affecting backwards compatibility
------------------------------------------
-
-- ``shared`` is a keyword now.
-- Deprecated ``sockets.recvLine`` and ``asyncio.recvLine``, added
-  ``readLine`` instead.
-- The way indentation is handled in the parser changed significantly. However,
-  this affects very little (if any) real world code.
-- The expression/statement unification has been implemented. Again this
-  only affects edge cases and no known real world code.
-- Changed the async interface of the ``scgi`` module.
-- WideStrings are now garbage collected like other string types.
-
-
-Compiler Additions
-------------------
-
-- The ``doc2`` command does not generate output for the whole project anymore.
-  Use the new ``--project`` switch to enable this behaviour.
-- The compiler can now warn about shadowed local variables. However, this needs
-  to be turned on explicitly via ``--warning[ShadowIdent]:on``.
-- The compiler now supports almost every pragma in a ``push`` pragma.
-- Generic converters have been implemented.
-- Added a **highly experimental** ``noforward`` pragma enabling a special
-  compilation mode that largely eliminates the need for forward declarations.
-
-Language Additions
-------------------
-
-- ``case expressions`` are now supported.
-- Table constructors now mimic more closely the syntax of the ``case``
-  statement.
-- Nimrod can now infer the return type of a proc from its body.
-- Added a ``mixin`` declaration to affect symbol binding rules in generics.
-- Exception tracking has been added and the ``doc2`` command annotates possible
-  exceptions for you.
-- User defined effects ("tags") tracking has been added and the ``doc2``
-  command annotates possible tags for you.
-- Types can be annotated with the new syntax ``not nil`` to explicitly state
-  that ``nil`` is not allowed. However currently the compiler performs no
-  advanced static checking for this; for now it's merely for documentation
-  purposes.
-- An ``export`` statement has been added to the language: It can be used for
-  symbol forwarding so client modules don't have to import a module's
-  dependencies explicitly.
-- Overloading based on ASTs has been implemented.
-- Generics are now supported for multi methods.
-- Objects can be initialized via an *object constructor expression*.
-- There is a new syntactic construct ``(;)`` unifying expressions and
-  statements.
-- You can now use ``from module import nil`` if you want to import the module
-  but want to enforce fully qualified access to every symbol in ``module``.
-
-
-Notes for the future
---------------------
-
-- The scope rules of ``if`` statements will change in 0.9.4. This affects the
-  ``=~`` pegs/re templates.
-- The ``sockets`` module will become a low-level wrapper of OS-specific socket
-  functions. All the high-level features of the current ``sockets`` module
-  will be moved to a ``network`` module.
diff --git a/web/news/e008_new_website.rst b/web/news/e008_new_website.rst
deleted file mode 100644
index b36cc99dd..000000000
--- a/web/news/e008_new_website.rst
+++ /dev/null
@@ -1,13 +0,0 @@
-New website design!
-===================
-
-.. container:: metadata
-
-  Posted by Andreas Rumpf on 09/12/2014
-
-A brand new website including an improved forum is now live.
-All thanks go to Philip Witte and
-Dominik Picheta, Philip Witte for the design of the website (together with
-the logo) as well as the HTML and CSS code for his template, and Dominik Picheta
-for integrating Philip's design with Nim's forum. We're sure you will
-agree that Philip's design is beautiful.
diff --git a/web/news/e009_andreas_rumpfs_talk.rst b/web/news/e009_andreas_rumpfs_talk.rst
deleted file mode 100644
index 00cc5e101..000000000
--- a/web/news/e009_andreas_rumpfs_talk.rst
+++ /dev/null
@@ -1,11 +0,0 @@
-2014-01-15 Andreas Rumpf's talk on Nimrod at Strange Loop 2013 is now online
-============================================================================
-
-.. container:: metadata
-
-  Posted by Dominik Picheta on 12/01/2014
-
-Andreas Rumpf presented *Nimrod: A New Approach to Metaprogramming* at
-`Strange Loop 2013<https://thestrangeloop.com/sessions/nimrod-a-new-approach-to-meta-programming>`_.
-The `video and slides<http://www.infoq.com/presentations/nimrod>`_
-of the talk are now available.
diff --git a/web/news/e010_dr_dobbs_journal.rst b/web/news/e010_dr_dobbs_journal.rst
deleted file mode 100644
index b48ccf31f..000000000
--- a/web/news/e010_dr_dobbs_journal.rst
+++ /dev/null
@@ -1,9 +0,0 @@
-Nimrod Featured in Dr. Dobb's Journal
-=====================================
-
-.. container:: metadata
-
-  Posted by Dominik Picheta on 11/02/2014
-
-Nimrod has been `featured<http://www.drdobbs.com/open-source/nimrod-a-new-systems-programming-languag/240165321>`_
-as the cover story in the February 2014 issue of Dr. Dobb's Journal.
diff --git a/web/news/e011_version_0_9_4.rst b/web/news/e011_version_0_9_4.rst
deleted file mode 100644
index 2714c5c78..000000000
--- a/web/news/e011_version_0_9_4.rst
+++ /dev/null
@@ -1,179 +0,0 @@
-2014-04-21 Version 0.9.4 released
-=================================
-
-.. container:: metadata
-
-  Posted by Dominik Picheta on 21/04/2014
-
-The Nimrod development community is proud to announce the release of version
-0.9.4 of the Nimrod compiler and tools. **Note: This release has to be
-considered beta quality! Lots of new features have been implemented but
-unfortunately some do not fulfill our quality standards yet.**
-
-Prebuilt binaries and instructions for building from source are available
-on the `download page <download.html>`_.
-
-This release includes about
-`1400 changes <https://github.com/Araq/Nimrod/compare/v0.9.2...v0.9.4>`_
-in total including various bug
-fixes, new languages features and standard library additions and improvements.
-This release brings with it support for user-defined type classes, a brand
-new VM for executing Nimrod code at compile-time and new symbol binding
-rules for clean templates.
-
-It also introduces support for the brand new
-`Babel package manager <https://github.com/nimrod-code/babel>`_ which
-has itself seen its first release recently. Many of the wrappers that were
-present in the standard library have been moved to separate repositories
-and should now be installed using Babel.
-
-Apart from that a new **experimental** Asynchronous IO API has been added via
-the ``asyncdispatch`` and ``asyncnet`` modules. The ``net`` and ``rawsockets``
-modules have also been added and they will likely replace the sockets
-module in the next release. The Asynchronous IO API has been designed to
-take advantage of Linux's epoll and Windows' IOCP APIs, support for BSD's
-kqueue has not been implemented yet but will be in the future.
-The Asynchronous IO API provides both
-a callback interface and an interface which allows you to write code as you
-would if you were writing synchronous code. The latter is done through
-the use of an ``await`` macro which behaves similar to C#'s await. The
-following is a very simple chat server demonstrating Nimrod's new async
-capabilities.
-
-.. code-block::nim
-  import asyncnet, asyncdispatch
-
-  var clients: seq[PAsyncSocket] = @[]
-
-  proc processClient(client: PAsyncSocket) {.async.} =
-    while true:
-      let line = await client.recvLine()
-      for c in clients:
-        await c.send(line & "\c\L")
-
-  proc serve() {.async.} =
-    var server = newAsyncSocket()
-    server.bindAddr(TPort(12345))
-    server.listen()
-
-    while true:
-      let client = await server.accept()
-      clients.add client
-
-      processClient(client)
-
-  serve()
-  runForever()
-
-
-Note that this feature has been implemented with Nimrod's macro system and so
-``await`` and ``async`` are no keywords.
-
-Syntactic sugar for anonymous procedures has also been introduced. It too has
-been implemented as a macro. The following shows some simple usage of the new
-syntax:
-
-.. code-block::nim
-  import future
-
-  var s = @[1, 2, 3, 4, 5]
-  echo(s.map((x: int) => x * 5))
-
-A list of changes follows, for a comprehensive list of changes take a look
-`here <https://github.com/Araq/Nimrod/compare/v0.9.2...v0.9.4>`_.
-
-Library Additions
------------------
-
-- Added ``macros.genSym`` builtin for AST generation.
-- Added ``macros.newLit`` procs for easier AST generation.
-- Added module ``logging``.
-- Added module ``asyncdispatch``.
-- Added module ``asyncnet``.
-- Added module ``net``.
-- Added module ``rawsockets``.
-- Added module ``selectors``.
-- Added module ``asynchttpserver``.
-- Added support for the new asynchronous IO in the ``httpclient`` module.
-- Added a Python-inspired ``future`` module that features upcoming additions
-  to the ``system`` module.
-
-
-Changes affecting backwards compatibility
------------------------------------------
-
-- The scoping rules for the ``if`` statement changed for better interaction
-  with the new syntactic construct ``(;)``.
-- ``OSError`` family of procedures has been deprecated. Procedures with the same
-  name but which take different parameters have been introduced. These procs now
-  require an error code to be passed to them. This error code can be retrieved
-  using the new ``OSLastError`` proc.
-- ``os.parentDir`` now returns "" if there is no parent dir.
-- In CGI scripts stacktraces are shown to the user only
-  if ``cgi.setStackTraceStdout`` is used.
-- The symbol binding rules for clean templates changed: ``bind`` for any
-  symbol that's not a parameter is now the default. ``mixin`` can be used
-  to require instantiation scope for a symbol.
-- ``quoteIfContainsWhite`` now escapes argument in such way that it can be safely
-  passed to shell, instead of just adding double quotes.
-- ``macros.dumpTree`` and ``macros.dumpLisp`` have been made ``immediate``,
-  ``dumpTreeImm`` and ``dumpLispImm`` are now deprecated.
-- The ``nil`` statement has been deprecated, use an empty ``discard`` instead.
-- ``sockets.select`` now prunes sockets that are **not** ready from the list
-  of sockets given to it.
-- The ``noStackFrame`` pragma has been renamed to ``asmNoStackFrame`` to
-  ensure you only use it when you know what you're doing.
-- Many of the wrappers that were present in the standard library have been
-  moved to separate repositories and should now be installed using Babel.
-
-
-Compiler Additions
-------------------
-
-- The compiler can now warn about "uninitialized" variables. (There are no
-  real uninitialized variables in Nimrod as they are initialized to binary
-  zero). Activate via ``{.warning[Uninit]:on.}``.
-- The compiler now enforces the ``not nil`` constraint.
-- The compiler now supports a ``codegenDecl`` pragma for even more control
-  over the generated code.
-- The compiler now supports a ``computedGoto`` pragma to support very fast
-  dispatching for interpreters and the like.
-- The old evaluation engine has been replaced by a proper register based
-  virtual machine. This fixes numerous bugs for ``nimrod i`` and for macro
-  evaluation.
-- ``--gc:none`` produces warnings when code uses the GC.
-- A ``union`` pragma for better C interoperability is now supported.
-- A ``packed`` pragma to control the memory packing/alignment of fields in
-  an object.
-- Arrays can be annotated to be ``unchecked`` for easier low level
-  manipulations of memory.
-- Support for the new Babel package manager.
-
-
-Language Additions
-------------------
-
-- Arrays can now be declared with a single integer literal ``N`` instead of a
-  range; the range is then ``0..N-1``.
-- Added ``requiresInit`` pragma to enforce explicit initialization.
-- Exported templates are allowed to access hidden fields.
-- The ``using statement`` enables you to more easily author domain-specific
-  languages and libraries providing OOP-like syntactic sugar.
-- Added the possibility to override various dot operators in order to handle
-  calls to missing procs and reads from undeclared fields at compile-time.
-- The overload resolution now supports ``static[T]`` params that must be
-  evaluable at compile-time.
-- Support for user-defined type classes has been added.
-- The *command syntax* is supported in a lot more contexts.
-- Anonymous iterators are now supported and iterators can capture variables
-  of an outer proc.
-- The experimental ``strongSpaces`` parsing mode has been implemented.
-- You can annotate pointer types with regions for increased type safety.
-- Added support for the builtin ``spawn`` for easy thread pool usage.
-
-
-Tools improvements
-------------------
-
-- c2nim can deal with a subset of C++. Use the ``--cpp`` command line option
-  to activate.
diff --git a/web/news/e012_version_0_9_6.rst b/web/news/e012_version_0_9_6.rst
deleted file mode 100644
index 7a148aaa5..000000000
--- a/web/news/e012_version_0_9_6.rst
+++ /dev/null
@@ -1,65 +0,0 @@
-Version 0.9.6 released
-=================================
-
-.. container:: metadata
-
-  Posted by Andreas Rumpf on 19/10/2014
-
-**Note: 0.9.6 is the last release of Nimrod. The language is being renamed to
-Nim. Nim slightly breaks compatibility.**
-
-This is a maintenance release. The upcoming 0.10.0 release has
-the new features and exciting developments.
-
-
-Changes affecting backwards compatibility
------------------------------------------
-
-- ``spawn`` now uses an elaborate self-adapting thread pool and as such
-  has been moved into its own module. So to use it, you now have to import
-  ``threadpool``.
-- The symbol binding rules in generics changed: ``bar`` in ``foo.bar`` is
-  now considered for implicit early binding.
-- ``c2nim`` moved into its own repository and is now a Babel package.
-- ``pas2nim`` moved into its own repository and is now a Babel package.
-- ``system.$`` for floating point types now produces a human friendly string
-  representation.
-- ``uri.TUrl`` as well as the ``parseurl`` module are now deprecated in favour
-  of the new ``TUri`` type in the ``uri`` module.
-- The ``destructor`` pragma has been deprecated. Use the ``override`` pragma
-  instead. The destructor's name has to be ``destroy`` now.
-- ``lambda`` is not a keyword anymore.
-- **system.defined has been split into system.defined and system.declared**.
-  You have to use ``--symbol`` to declare new conditional symbols that can be
-  set via ``--define``.
-- ``--threadanalysis:on`` is now the default. To make your program compile
-  you can disable it but this is only a temporary solution as this option
-  will disappear soon!
-
-
-Compiler improvements
----------------------
-
-- Multi method dispatching performance has been improved by a factor of 10x for
-  pathological cases.
-
-
-Language Additions
-------------------
-
-- This version introduces the ``deprecated`` pragma statement that is used
-  to handle the upcoming massive amount of symbol renames.
-- ``spawn`` can now wrap proc that has a return value. It then returns a data
-  flow variable of the wrapped return type.
-
-
-Library Additions
------------------
-
-- Added module ``cpuinfo``.
-- Added module ``threadpool``.
-- ``sequtils.distnct`` has been renamed to ``sequtils.deduplicate``.
-- Added ``algorithm.reversed``
-- Added ``uri.combine`` and ``uri.parseUri``.
-- Some sockets procedures now support a ``SafeDisconn`` flag which causes
-  them to handle disconnection errors and not raise them.
diff --git a/web/news/e013_version_0_10_2.rst b/web/news/e013_version_0_10_2.rst
deleted file mode 100644
index ad8afa3bf..000000000
--- a/web/news/e013_version_0_10_2.rst
+++ /dev/null
@@ -1,198 +0,0 @@
-Version 0.10.2 released
-=======================
-
-.. container:: metadata
-
-  Posted by Dominik Picheta on 29/12/2014
-
-This release marks the completion of a very important change to the project:
-the official renaming from Nimrod to Nim. Version 0.10.2 contains many language
-changes, some of which may break your existing code. For your convenience, we
-added a new tool called `nimfix <nimfix.html>`_ that will help you convert your
-existing projects so that it works with the latest version of the compiler.
-
-Progress towards version 1.0
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Although Nim is still pre-1.0, we were able to keep the number of breaking
-changes to a minimum so far. Starting with version 1.0, we will not introduce
-any breaking changes between major release versions.
-One of Nim's goals is to ensure that the compiler is as efficient as possible.
-Take a look at the
-`latest benchmarks <https://github.com/logicchains/LPATHBench/blob/master/writeup.md>`_,
-which show that Nim is consistently near
-the top and already nearly as fast as C and C++. Recent developments, such as
-the new ``asyncdispatch`` module will allow you to write efficient web server
-applications using non-blocking code. Nim now also has a built-in thread pool
-for lightweight threading through the use of ``spawn``.
-
-The unpopular "T" and "P" prefixes on types have been deprecated. Nim also
-became more expressive by weakening the distinction between statements and
-expressions. We also added a new and searchable forum, a new website, and our
-documentation generator ``docgen`` has seen major improvements. Many thanks to
-Nick Greenfield for the much more beautiful documentation!
-
-
-
-What's left to be done
-~~~~~~~~~~~~~~~~~~~~~~
-
-The 1.0 release is actually very close. Apart from bug fixes, there are
-two major features missing or incomplete:
-
-* ``static[T]`` needs to be defined precisely and the bugs in the
-  implementation need to be fixed.
-* Overloading of the assignment operator is required for some generic
-  containers and needs to be implemented.
-
-This means that fancy matrix libraries will finally start to work, which used
-to be a major point of pain in the language.
-
-
-Nimble and other Nim tools
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Outside of the language and the compiler itself many Nim tools have seen
-considerable improvements.
-
-Babel the Nim package manager has been renamed to Nimble. Nimble's purpose
-is the installation of packages containing libraries and/or applications
-written in Nim.
-Even though Nimble is still very young it already is very
-functional. It can install packages by name, it does so by accessing a
-packages repository which is hosted on a GitHub repo. Packages can also be
-installed via a Git repo URL or Mercurial repo URL. The package repository
-is searchable through Nimble. Anyone is free to add their own packages to
-the package repository by forking the
-`nim-lang/packages <https://github.com/nim-lang/packages>`_ repo and creating
-a pull request. Nimble is fully cross-platform and should be fully functional
-on all major operating systems.
-It is of course completely written in Nim.
-
-Changelog
-~~~~~~~~~
-
-Changes affecting backwards compatibility
------------------------------------------
-
-- **The language has been renamed from Nimrod to Nim.** The name of the
-  compiler changed from ``nimrod`` to ``nim`` too.
-- ``system.fileHandle`` has been renamed to ``system.getFileHandle`` to
-  prevent name conflicts with the new type ``FileHandle``.
-- Comments are now not part of the AST anymore, as such you cannot use them
-  in place of ``discard``.
-- Large parts of the stdlib got rid of the T/P type prefixes. Instead most
-  types now simply start with an uppercased letter. The
-  so called "partial case sensitivity" rule is now active allowing for code
-  like ``var foo: Foo`` in more contexts.
-- String case (or any non-ordinal case) statements
-  without 'else' are deprecated.
-- Recursive tuple types are not allowed anymore. Use ``object`` instead.
-- The PEGS module returns ``nil`` instead of ``""`` when an optional capture
-  fails to match.
-- The re module returns ``nil`` instead of ``""`` when an optional capture
-  fails to match.
-- The "symmetric set difference" operator (``-+-``) never worked and has been
-  removed.
-- ``defer`` is a keyword now.
-- ``func`` is a keyword now.
-- The ``using`` language feature now needs to be activated via the new
-  ``{.experimental.}`` pragma that enables experimental language features.
-- Destructors are now officially *experimental*.
-- Standalone ``except`` and ``finally`` statements are deprecated now.
-  The standalone ``finally`` can be replaced with ``defer``,
-  standalone ``except`` requires an explicit ``try``.
-- Operators ending in ``>`` are considered as "arrow like" and have their
-  own priority level and are right associative. This means that
-  the ``=>`` and ``->`` operators from the `future <future.html>`_ module
-  work better.
-- Field names in tuples are now ignored for type comparisons. This allows
-  for greater interoperability between different modules.
-- Statement lists are not converted to an implicit ``do`` block anymore. This
-  means the confusing ``nnkDo`` nodes when working with macros are gone for
-  good.
-
-
-Language Additions
-------------------
-
-- The new concurrency model has been implemented including ``locks`` sections,
-  lock levels and object field ``guards``.
-- The ``parallel`` statement has been implemented.
-- ``deepCopy`` has been added to the language.
-- The builtin ``procCall`` can be used to get ``super``-like functionality
-  for multi methods.
-- There is a new pragma ``{.experimental.}`` that enables experimental
-  language features per module, or you can enable these features on a global
-  level with the ``--experimental`` command line option.
-
-
-Compiler Additions
-------------------
-
-- The compiler now supports *mixed* Objective C / C++ / C code generation:
-  The modules that use ``importCpp`` or ``importObjc`` are compiled to C++
-  or Objective C code, any other module is compiled to C code. This
-  improves interoperability.
-- There is a new ``parallel`` statement for safe fork&join parallel computing.
-- ``guard`` and ``lock`` pragmas have been implemented to support safer
-  concurrent programming.
-- The following procs are now available at compile-time::
-
-    math.sqrt, math.ln, math.log10, math.log2, math.exp, math.round,
-    math.arccos, math.arcsin, math.arctan, math.arctan2, math.cos,
-    math.cosh, math.hypot, math.sinh, math.sin, math.tan, math.tanh,
-    math.pow, math.trunc, math.floor, math.ceil, math.fmod,
-    os.getEnv, os.existsEnv, os.dirExists, os.fileExists,
-    system.writeFile
-
-- Two backticks now produce a single backtick within an ``emit`` or ``asm``
-  statement.
-- There is a new tool, `nimfix <nimfix.html>`_ to help you in updating your
-  code from Nimrod to Nim.
-- The compiler's output has been prettified.
-
-Library Additions
------------------
-
-- Added module ``fenv`` to control the handling of floating-point rounding and
-  exceptions (overflow, division by zero, etc.).
-- ``system.setupForeignThreadGc`` can be used for better interaction with
-  foreign libraries that create threads and run a Nim callback from these
-  foreign threads.
-- List comprehensions have been implemented as a macro in the ``future``
-  module.
-- The new Async module (``asyncnet``) now supports SSL.
-- The ``smtp`` module now has an async implementation.
-- Added module ``asyncfile`` which implements asynchronous file reading
-  and writing.
-- ``osproc.kill`` has been added.
-- ``asyncnet`` and ``asynchttpserver`` now support ``SO_REUSEADDR``.
-
-Bugfixes
---------
-
-- ``nil`` and ``NULL`` are now preserved between Nim and databases in the
-  ``db_*`` modules.
-- Fixed issue with OS module in non-unicode mode on Windows.
-- Fixed issue with ``x.low``
-  (`#1366 <https://github.com/Araq/Nim/issues/1366>`_).
-- Fixed tuple unpacking issue inside closure iterators
-  (`#1067 <https://github.com/Araq/Nim/issues/1067>`_).
-- Fixed ENDB compilation issues.
-- Many ``asynchttpserver`` fixes.
-- Macros can now keep global state across macro calls
-  (`#903 <https://github.com/Araq/Nim/issues/903>`_).
-- ``osproc`` fixes on Windows.
-- ``osproc.terminate`` fixed.
-- Improvements to exception handling in async procedures.
-  (`#1487 <https://github.com/Araq/Nim/issues/1487>`_).
-- ``try`` now works at compile-time.
-- Fixes ``T = ref T`` to be an illegal recursive type.
-- Self imports are now disallowed.
-- Improved effect inference.
-- Fixes for the ``math`` module on Windows.
-- User defined pragmas will now work for generics that have
-  been instantiated in different modules.
-- Fixed queue exhaustion bug.
-- Many, many more.
diff --git a/web/news/e014_version_0_11_0.rst b/web/news/e014_version_0_11_0.rst
deleted file mode 100644
index a8a58f2ae..000000000
--- a/web/news/e014_version_0_11_0.rst
+++ /dev/null
@@ -1,396 +0,0 @@
-Version 0.11.0 released
-=======================
-
-.. container:: metadata
-
-  Posted by Dominik Picheta on 30/04/2015
-
-With this release we are one step closer to reaching version 1.0 and by
-extension the persistence of the Nim specification. As mentioned in the
-previous release notes, starting with version 1.0, we will not be introducing
-any more breaking changes to Nim.
-
-The *language* itself is very close to 1.0, the primary area that requires
-more work is the standard library.
-
-Take a look at the `download <download.html>`_ page for binaries (Windows-only)
-and 0.11.0 snapshots of the source code. The Windows installer now also
-includes `Aporia <https://github.com/nim-lang/aporia>`_,
-`Nimble <https://github.com/nim-lang/nimble>`_ and other useful tools to get
-you started with Nim.
-
-What's left to be done
-~~~~~~~~~~~~~~~~~~~~~~
-
-The 1.0 release is expected by the end of this year. Rumors say it will be in
-summer 2015. What's left:
-
-* Bug fixes, bug fixes, bug fixes, in particular:
-  - The remaining bugs of the lambda lifting pass that is responsible to enable
-    closures and closure iterators need to be fixed.
-  - ``concept`` needs to be refined, a nice name for the feature is not enough.
-  - Destructors need to be refined.
-  - ``static[T]`` needs to be fixed.
-  - Finish the implementation of the 'parallel' statement.
-* ``immediate`` templates and macros will be deprecated as these will soon be
-  completely unnecessary, instead the ``typed`` or ``untyped`` metatypes can
-  be used.
-* More of the standard library should be moved to Nimble packages and what's
-  left should use the features we have for concurrency and parallelism.
-
-
-
-Changes affecting backwards compatibility
------------------------------------------
-
-- Parameter names are finally properly ``gensym``'ed. This can break
-  templates though that used to rely on the fact that they are not.
-  (Bug #1915.) This means this doesn't compile anymore:
-
-.. code-block:: nim
-
-  template doIt(body: stmt) {.immediate.} =
-    # this used to inject the 'str' parameter:
-    proc res(str: string) =
-      body
-
-  doIt:
-    echo str # Error: undeclared identifier: 'str'
-..
-
-  This used to inject the ``str`` parameter into the scope of the body.
-  Declare the ``doIt`` template as ``immediate, dirty`` to get the old
-  behaviour.
-- Tuple field names are not ignored anymore, this caused too many problems
-  in practice so now the behaviour is as it was for version 0.9.6: If field
-  names exist for the tuple type, they are checked.
-- ``logging.level`` and ``logging.handlers`` are no longer exported.
-  ``addHandler``, ``getHandlers``, ``setLogFilter`` and ``getLogFilter``
-  should be used instead.
-- ``nim idetools`` has been replaced by a separate
-  tool `nimsuggest <0.11.0/nimsuggest.html>`_.
-- *arrow like* operators are not right associative anymore and are required
-  to end with either ``->``, ``~>`` or
-  ``=>``, not just ``>``. Examples of operators still considered arrow like:
-  ``->``, ``==>``, ``+=>``. On the other hand, the following operators are now
-  considered regular operators again: ``|>``, ``-+>``, etc.
-- Typeless parameters are now only allowed in templates and macros. The old
-  way turned out to be too error-prone.
-- The 'addr' and 'type' operators are now parsed as unary function
-  application. This means ``type(x).name`` is now parsed as ``(type(x)).name``
-  and not as ``type((x).name)``. Note that this also affects the AST
-  structure; for immediate macro parameters ``nkCall('addr', 'x')`` is
-  produced instead of ``nkAddr('x')``.
-- ``concept`` is now a keyword and is used instead of ``generic``.
-- The ``inc``, ``dec``, ``+=``, ``-=`` builtins now produce OverflowError
-  exceptions. This means code like the following:
-
-.. code-block:: nim
-  var x = low(T)
-  while x <= high(T):
-    echo x
-    inc x
-
-Needs to be replaced by something like this:
-
-.. code-block:: nim
-  var x = low(T).int
-  while x <= high(T).int:
-    echo x.T
-    inc x
-
-- **Negative indexing for slicing does not work anymore!** Instead
-  of ``a[0.. -1]`` you can
-  use ``a[0.. ^1]``. This also works with accessing a single
-  element ``a[^1]``. Note that we cannot detect this reliably as it is
-  determined at **runtime** whether negative indexing is used!
-  ``a[0.. -1]`` now produces the empty string/sequence.
-- The compiler now warns about code like ``foo +=1`` which uses inconsistent
-  spacing around binary operators. Later versions of the language will parse
-  these as unary operators instead so that ``echo $foo`` finally can do what
-  people expect it to do.
-- ``system.untyped`` and ``system.typed`` have been introduced as aliases
-  for ``expr`` and ``stmt``. The new names capture the semantics much better
-  and most likely  ``expr`` and ``stmt`` will be deprecated in favor of the
-  new names.
-- The ``split`` method in module ``re`` has changed. It now handles the case
-  of matches having a length of 0, and empty strings being yielded from the
-  iterator. A notable change might be that a pattern being matched at the
-  beginning and end of a string, will result in an empty string being produced
-  at the start and the end of the iterator.
-- The compiler and nimsuggest now count columns starting with 1, not 0 for
-  consistency with the rest of the world.
-
-
-Language Additions
-------------------
-
-- For empty ``case object`` branches ``discard`` can finally be used instead
-  of ``nil``.
-- Automatic dereferencing is now done for the first argument of a routine
-  call if overloading resolution produces no match otherwise. This feature
-  has to be enabled with
-  the `experimental <0.11.0/manual.html#pragmas-experimental-pragma>`_ pragma.
-- Objects that do not use inheritance nor ``case`` can be put into ``const``
-  sections. This means that finally this is possible and produces rather
-  nice code:
-
-.. code-block:: nim
-  import tables
-
-  const
-    foo = {"ah": "finally", "this": "is", "possible.": "nice!"}.toTable()
-
-
-- Ordinary parameters can follow after a varargs parameter. This means the
-  following is finally accepted by the compiler:
-
-.. code-block:: nim
-  template takesBlock(a, b: int, x: varargs[expr]; blck: stmt) =
-    blck
-    echo a, b
-
-  takesBlock 1, 2, "some", 0.90, "random stuff":
-    echo "yay"
-
-- Overloading by 'var T' is now finally possible:
-
-.. code-block:: nim
-  proc varOrConst(x: var int) = echo "var"
-  proc varOrConst(x: int) = echo "const"
-
-  var x: int
-  varOrConst(x) # "var"
-  varOrConst(45) # "const"
-
-- Array and seq indexing can now use the builtin ``^`` operator to access
-  things from backwards: ``a[^1]`` is like Python's ``a[-1]``.
-- A first version of the specification and implementation of the overloading
-  of the assignment operator has arrived!
-- ``system.len`` for strings and sequences now returns 0 for nil.
-
-- A single underscore can now be used to discard values when unpacking tuples:
-
-.. code-block:: nim
-  let (path, _, _) = os.splitFile("path/file.ext")
-
-
-- ``marshal.$$`` and ``marshal.to`` can be executed at compile-time.
-- Interoperability with C++ improved tremendously; C++'s templates and
-  operators can be wrapped directly. See
-  `this <0.11.0/nimc.html#additional-features-importcpp-pragma>`_
-  for more information.
-- ``macros.getType`` can be used to query an AST's type at compile-time. This
-  enables more powerful macros, for instance *currying* can now be done with
-  a macro.
-
-
-Library additions
------------------
-
-- ``reversed`` proc added to the ``unicode`` module.
-- Added multipart param to httpclient's ``post`` and ``postContent`` together
-  with a ``newMultipartData`` proc.
-- Added `%*` operator for JSON.
-- The compiler is now available as Nimble package for c2nim.
-- Added ``..^`` and ``..<`` templates to system so that the rather annoying
-  space between ``.. <`` and ``.. ^`` is not necessary anymore.
-- Added ``system.xlen`` for strings and sequences to get back the old ``len``
-  operation that doesn't check for ``nil`` for efficiency.
-- Added sexp.nim to parse and generate sexp.
-
-
-Bugfixes
---------
-
-- Fixed internal compiler error when using ``char()`` in an echo call
-  (`#1788 <https://github.com/Araq/Nim/issues/1788>`_).
-- Fixed Windows cross-compilation on Linux.
-- Overload resolution now works for types distinguished only by a
-  ``static[int]`` param
-  (`#1056 <https://github.com/Araq/Nim/issues/1056>`_).
-- Other fixes relating to generic types and static params.
-- Fixed some compiler crashes with unnamed tuples
-  (`#1774 <https://github.com/Araq/Nim/issues/1774>`_).
-- Fixed ``channels.tryRecv`` blocking
-  (`#1816 <https://github.com/Araq/Nim/issues/1816>`_).
-- Fixed generic instantiation errors with ``typedesc``
-  (`#419 <https://github.com/Araq/Nim/issues/419>`_).
-- Fixed generic regression where the compiler no longer detected constant
-  expressions properly (`#544 <https://github.com/Araq/Nim/issues/544>`_).
-- Fixed internal error with generic proc using ``static[T]`` in a specific
-  way (`#1049 <https://github.com/Araq/Nim/issues/1049>`_).
-- More fixes relating to generics (`#1820 <https://github.com/Araq/Nim/issues/1820>`_,
-  `#1050 <https://github.com/Araq/Nim/issues/1050>`_,
-  `#1859 <https://github.com/Araq/Nim/issues/1859>`_,
-  `#1858 <https://github.com/Araq/Nim/issues/1858>`_).
-- Fixed httpclient to properly encode queries.
-- Many fixes to the ``uri`` module.
-- Async sockets are now closed on error.
-- Fixes to httpclient's handling of multipart data.
-- Fixed GC segfaults with asynchronous sockets
-  (`#1796 <https://github.com/Araq/Nim/issues/1796>`_).
-- Added more versions to openssl's DLL version list
-  (`076f993 <https://github.com/Araq/Nim/commit/076f993>`_).
-- Fixed shallow copy in iterators being broken
-  (`#1803 <https://github.com/Araq/Nim/issues/1803>`_).
-- ``nil`` can now be inserted into tables with the ``db_sqlite`` module
-  (`#1866 <https://github.com/Araq/Nim/issues/1866>`_).
-- Fixed "Incorrect assembler generated"
-  (`#1907 <https://github.com/Araq/Nim/issues/1907>`_)
-- Fixed "Expression templates that define macros are unusable in some contexts"
-  (`#1903 <https://github.com/Araq/Nim/issues/1903>`_)
-- Fixed "a second level generic subclass causes the compiler to crash"
-  (`#1919 <https://github.com/Araq/Nim/issues/1919>`_)
-- Fixed "nim 0.10.2 generates invalid AsyncHttpClient C code for MSVC "
-  (`#1901 <https://github.com/Araq/Nim/issues/1901>`_)
-- Fixed "1 shl n produces wrong C code"
-  (`#1928 <https://github.com/Araq/Nim/issues/1928>`_)
-- Fixed "Internal error on tuple yield"
-  (`#1838 <https://github.com/Araq/Nim/issues/1838>`_)
-- Fixed "ICE with template"
-  (`#1915 <https://github.com/Araq/Nim/issues/1915>`_)
-- Fixed "include the tool directory in the installer as it is required by koch"
-  (`#1947 <https://github.com/Araq/Nim/issues/1947>`_)
-- Fixed "Can't compile if file location contains spaces on Windows"
-  (`#1955 <https://github.com/Araq/Nim/issues/1955>`_)
-- Fixed "List comprehension macro only supports infix checks as guards"
-  (`#1920 <https://github.com/Araq/Nim/issues/1920>`_)
-- Fixed "wrong field names of compatible tuples in generic types"
-  (`#1910 <https://github.com/Araq/Nim/issues/1910>`_)
-- Fixed "Macros within templates no longer work as expected"
-  (`#1944 <https://github.com/Araq/Nim/issues/1944>`_)
-- Fixed "Compiling for Standalone AVR broken in 0.10.2"
-  (`#1964 <https://github.com/Araq/Nim/issues/1964>`_)
-- Fixed "Compiling for Standalone AVR broken in 0.10.2"
-  (`#1964 <https://github.com/Araq/Nim/issues/1964>`_)
-- Fixed "Code generation for mitems with tuple elements"
-  (`#1833 <https://github.com/Araq/Nim/issues/1833>`_)
-- Fixed "httpclient.HttpMethod should not be an enum"
-  (`#1962 <https://github.com/Araq/Nim/issues/1962>`_)
-- Fixed "terminal / eraseScreen() throws an OverflowError"
-  (`#1906 <https://github.com/Araq/Nim/issues/1906>`_)
-- Fixed "setControlCHook(nil) disables registered quit procs"
-  (`#1546 <https://github.com/Araq/Nim/issues/1546>`_)
-- Fixed "Unexpected idetools behaviour"
-  (`#325 <https://github.com/Araq/Nim/issues/325>`_)
-- Fixed "Unused lifted lambda does not compile"
-  (`#1642 <https://github.com/Araq/Nim/issues/1642>`_)
-- Fixed "'low' and 'high' don't work with cstring asguments"
-  (`#2030 <https://github.com/Araq/Nim/issues/2030>`_)
-- Fixed "Converting to int does not round in JS backend"
-  (`#1959 <https://github.com/Araq/Nim/issues/1959>`_)
-- Fixed "Internal error genRecordField 2 when adding region to pointer."
-  (`#2039 <https://github.com/Araq/Nim/issues/2039>`_)
-- Fixed "Macros fail to compile when compiled with --os:standalone"
-  (`#2041 <https://github.com/Araq/Nim/issues/2041>`_)
-- Fixed "Reading from {.compileTime.} variables can cause code generation to fail"
-  (`#2022 <https://github.com/Araq/Nim/issues/2022>`_)
-- Fixed "Passing overloaded symbols to templates fails inside generic procedures"
-  (`#1988 <https://github.com/Araq/Nim/issues/1988>`_)
-- Fixed "Compiling iterator with object assignment in release mode causes "var not init""
-  (`#2023 <https://github.com/Araq/Nim/issues/2023>`_)
-- Fixed "calling a large number of macros doing some computation fails"
-  (`#1989 <https://github.com/Araq/Nim/issues/1989>`_)
-- Fixed "Can't get Koch to install nim under Windows"
-  (`#2061 <https://github.com/Araq/Nim/issues/2061>`_)
-- Fixed "Template with two stmt parameters segfaults compiler"
-  (`#2057 <https://github.com/Araq/Nim/issues/2057>`_)
-- Fixed "`noSideEffect` not affected by `echo`"
-  (`#2011 <https://github.com/Araq/Nim/issues/2011>`_)
-- Fixed "Compiling with the cpp backend ignores --passc"
-  (`#1601 <https://github.com/Araq/Nim/issues/1601>`_)
-- Fixed "Put untyped procedure parameters behind the experimental pragma"
-  (`#1956 <https://github.com/Araq/Nim/issues/1956>`_)
-- Fixed "generic regression"
-  (`#2073 <https://github.com/Araq/Nim/issues/2073>`_)
-- Fixed "generic regression"
-  (`#2073 <https://github.com/Araq/Nim/issues/2073>`_)
-- Fixed "Regression in template lookup with generics"
-  (`#2004 <https://github.com/Araq/Nim/issues/2004>`_)
-- Fixed "GC's growObj is wrong for edge cases"
-  (`#2070 <https://github.com/Araq/Nim/issues/2070>`_)
-- Fixed "Compiler internal error when creating an array out of a typeclass"
-  (`#1131 <https://github.com/Araq/Nim/issues/1131>`_)
-- Fixed "GC's growObj is wrong for edge cases"
-  (`#2070 <https://github.com/Araq/Nim/issues/2070>`_)
-- Fixed "Invalid Objective-C code generated when calling class method"
-  (`#2068 <https://github.com/Araq/Nim/issues/2068>`_)
-- Fixed "walkDirRec Error"
-  (`#2116 <https://github.com/Araq/Nim/issues/2116>`_)
-- Fixed "Typo in code causes compiler SIGSEGV in evalAtCompileTime"
-  (`#2113 <https://github.com/Araq/Nim/issues/2113>`_)
-- Fixed "Regression on exportc"
-  (`#2118 <https://github.com/Araq/Nim/issues/2118>`_)
-- Fixed "Error message"
-  (`#2102 <https://github.com/Araq/Nim/issues/2102>`_)
-- Fixed "hint[path] = off not working in nim.cfg"
-  (`#2103 <https://github.com/Araq/Nim/issues/2103>`_)
-- Fixed "compiler crashes when getting a tuple from a sequence of generic tuples"
-  (`#2121 <https://github.com/Araq/Nim/issues/2121>`_)
-- Fixed "nim check hangs with when"
-  (`#2123 <https://github.com/Araq/Nim/issues/2123>`_)
-- Fixed "static[T] param in nested type resolve/caching issue"
-  (`#2125 <https://github.com/Araq/Nim/issues/2125>`_)
-- Fixed "repr should display ``\0``"
-  (`#2124 <https://github.com/Araq/Nim/issues/2124>`_)
-- Fixed "'nim check' never ends in case of recursive dependency "
-  (`#2051 <https://github.com/Araq/Nim/issues/2051>`_)
-- Fixed "From macros: Error: unhandled exception: sons is not accessible"
-  (`#2167 <https://github.com/Araq/Nim/issues/2167>`_)
-- Fixed "`fieldPairs` doesn't work inside templates"
-  (`#1902 <https://github.com/Araq/Nim/issues/1902>`_)
-- Fixed "fields iterator misbehavior on break statement"
-  (`#2134 <https://github.com/Araq/Nim/issues/2134>`_)
-- Fixed "Fix for compiler not building anymore since #c3244ef1ff"
-  (`#2193 <https://github.com/Araq/Nim/issues/2193>`_)
-- Fixed "JSON parser fails in cpp output mode"
-  (`#2199 <https://github.com/Araq/Nim/issues/2199>`_)
-- Fixed "macros.getType mishandles void return"
-  (`#2211 <https://github.com/Araq/Nim/issues/2211>`_)
-- Fixed "Regression involving templates instantiated within generics"
-  (`#2215 <https://github.com/Araq/Nim/issues/2215>`_)
-- Fixed ""Error: invalid type" for 'not nil' on generic type."
-  (`#2216 <https://github.com/Araq/Nim/issues/2216>`_)
-- Fixed "--threads:on breaks async"
-  (`#2074 <https://github.com/Araq/Nim/issues/2074>`_)
-- Fixed "Type mismatch not always caught, can generate bad code for C backend."
-  (`#2169 <https://github.com/Araq/Nim/issues/2169>`_)
-- Fixed "Failed C compilation when storing proc to own type in object"
-  (`#2233 <https://github.com/Araq/Nim/issues/2233>`_)
-- Fixed "Unknown line/column number in constant declaration type conversion error"
-  (`#2252 <https://github.com/Araq/Nim/issues/2252>`_)
-- Fixed "Adding {.compile.} fails if nimcache already exists."
-  (`#2247 <https://github.com/Araq/Nim/issues/2247>`_)
-- Fixed "Two different type names generated for a single type (C backend)"
-  (`#2250 <https://github.com/Araq/Nim/issues/2250>`_)
-- Fixed "Ambigous call when it should not be"
-  (`#2229 <https://github.com/Araq/Nim/issues/2229>`_)
-- Fixed "Make sure we can load root urls"
-  (`#2227 <https://github.com/Araq/Nim/issues/2227>`_)
-- Fixed "Failure to slice a string with an int subrange type"
-  (`#794 <https://github.com/Araq/Nim/issues/794>`_)
-- Fixed "documentation error"
-  (`#2205 <https://github.com/Araq/Nim/issues/2205>`_)
-- Fixed "Code growth when using `const`"
-  (`#1940 <https://github.com/Araq/Nim/issues/1940>`_)
-- Fixed "Instances of generic types confuse overload resolution"
-  (`#2220 <https://github.com/Araq/Nim/issues/2220>`_)
-- Fixed "Compiler error when initializing sdl2's EventType"
-  (`#2316 <https://github.com/Araq/Nim/issues/2316>`_)
-- Fixed "Parallel disjoint checking can't handle `<`, `items`, or arrays"
-  (`#2287 <https://github.com/Araq/Nim/issues/2287>`_)
-- Fixed "Strings aren't copied in parallel loop"
-  (`#2286 <https://github.com/Araq/Nim/issues/2286>`_)
-- Fixed "JavaScript compiler crash with tables"
-  (`#2298 <https://github.com/Araq/Nim/issues/2298>`_)
-- Fixed "Range checker too restrictive"
-  (`#1845 <https://github.com/Araq/Nim/issues/1845>`_)
-- Fixed "Failure to slice a string with an int subrange type"
-  (`#794 <https://github.com/Araq/Nim/issues/794>`_)
-- Fixed "Remind user when compiling in debug mode"
-  (`#1868 <https://github.com/Araq/Nim/issues/1868>`_)
-- Fixed "Compiler user guide has jumbled options/commands."
-  (`#1819 <https://github.com/Araq/Nim/issues/1819>`_)
-- Fixed "using `method`: 1 in a objects constructor fails when compiling"
-  (`#1791 <https://github.com/Araq/Nim/issues/1791>`_)
diff --git a/web/news/e015_version_0_11_2.rst b/web/news/e015_version_0_11_2.rst
deleted file mode 100644
index 273182340..000000000
--- a/web/news/e015_version_0_11_2.rst
+++ /dev/null
@@ -1,12 +0,0 @@
-Version 0.11.2 released
-==================================
-
-.. container:: metadata
-
-  Posted by Andreas Rumpf on 04/05/2015
-
-This is just a bugfix release that fixes the most pressing regressions we
-introduced with version 0.11.0. The way types are computed was
-changed significantly causing all sort of problems. Sorry for the
-inconvenience; we grew overconfident our large test suite would prevent these
-things.
diff --git a/web/news/e016_nim_conf1.rst b/web/news/e016_nim_conf1.rst
deleted file mode 100644
index 228bffd28..000000000
--- a/web/news/e016_nim_conf1.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-First Nim conference
-====================
-
-.. container:: metadata
-
-  Posted by Dominik Picheta on 16/10/2015
-
-.. raw::html
-
-  <img src="../assets/zeo/banner.jpg" alt="First Nim conference in Ukraine!" width="682"/>
-
-This Autumn you have the unique opportunity to take part in the first Nim event
-held in Kyiv and to meet the creator of the Nim programming language -
-Andreas Rumpf. The event is hosted by Zeo Alliance and is taking place between
-14-15 November 2015 in Kyiv, Ukraine.
-
-During the workshop you will learn:
-
-- The basics of the language including its safe and unsafe subsets.
-- How to use Nim to develop web applications.
-- How Nim's meta programming capabilities make Nim the ultimate glue language,
-  excellent at interoperability with C++, JavaScript, Java and others.
-- Games in Nim and the ability to rapidly prototype without sacrificing speed.
-
-Registration is free, but the number of places is limited. More details
-can be found `here <https://nimworkshop.splashthat.com/>`_.
diff --git a/web/news/e017_version_0_12_0.rst b/web/news/e017_version_0_12_0.rst
deleted file mode 100644
index 63088f9e2..000000000
--- a/web/news/e017_version_0_12_0.rst
+++ /dev/null
@@ -1,403 +0,0 @@
-2015-10-27 Version 0.12.0 released
-==================================
-
-.. container:: metadata
-
-  Posted by Dominik Picheta on 27/10/2015
-
-The Nim community of developers is proud to announce the new version of the
-Nim compiler. This has been a long time coming as the last release has been
-made over 5 months ago!
-
-This release includes some changes which affect backwards compatibility,
-one major change is that now the hash table ``[]`` operators now raise a
-``KeyError`` exception when the key does not exist.
-
-Some of the more exciting new features include: the ability to unpack tuples
-in any assignment context, the introduction of `NimScript <docs/nims.html>`_,
-and improvements to the type inference of lambdas.
-
-There are of course many many many bug fixes included with this release.
-We are getting closer and closer to a 1.0 release and are hoping that only
-a few 0.x releases will be necessary before we are happy to release version 1.0.
-
-As always you can download the latest version of Nim from the
-`download <download.html>`_ page.
-
-For a more detailed list of changes look below. Some of the upcoming breaking
-changes are also documented in this forum
-`thread <http://forum.nim-lang.org/t/1708>`_.
-
-Changes affecting backwards compatibility
------------------------------------------
-- The regular expression modules, ``re`` and ``nre`` now depend on version
-  8.36 of PCRE. If you have an older version you may see a message similar
-  to ``could not import: pcre_free_study`` output when you start your
-  program. See `this issue <https://github.com/docopt/docopt.nim/issues/13>`_
-  for more information.
-- ``tables.[]``, ``strtabs.[]``, ``critbits.[]`` **now raise**
-  the ``KeyError`` **exception when the key does not exist**! Use the
-  new ``getOrDefault`` instead to get the old behaviour. Compile all your
-  code with ``-d:nimTableGet`` to get a listing of where your code
-  uses ``[]``!
-- The ``rawsockets`` module has been renamed to ``nativesockets`` to avoid
-  confusion with TCP/IP raw sockets, so ``newNativeSocket`` should be used
-  instead of ``newRawSocket``.
-- The ``miliseconds`` property of ``times.TimeInterval`` is now ``milliseconds``.
-  Code accessing that property is deprecated and code using ``miliseconds``
-  during object initialization or as a named parameter of ``initInterval()``
-  will need to be updated.
-- ``std.logging`` functions no longer do formatting and semantically treat
-  their arguments just like ``echo`` does. Affected functions: ``log``,
-  ``debug``, ``info``, ``warn``, ``error``, ``fatal``. Custom subtypes of
-  ``Logger`` also need to be adjusted accordingly.
-- Floating point numbers can now look like ``2d`` (float64)
-  and ``2f`` (float32) which means imports like ``import scene/2d/sprite``
-  do not work anymore. Instead quotes have to be
-  used: ``import "scene/2d/sprite"``. The former code never was valid Nim.
-- The Windows API wrapper (``windows.nim``) is now not part of the official
-  distribution anymore. Instead use the ``oldwinapi`` Nimble package.
-- There is now a clear distinction between ``--os:standalone``
-  and ``--gc:none``. So if you use ``--os:standalone`` ensure you also use
-  ``--gc:none``. ``--os:standalone`` without ``--gc:none`` is now a version
-  that doesn't depend on any OS but includes the GC. However this version
-  is currently untested!
-- All procedures which construct a ``Socket``/``AsyncSocket`` now need to
-  specify the socket domain, type and protocol. The param name
-  ``typ: SockType`` (in ``newSocket``/``newAsyncSocket`` procs) was also
-  renamed to ``sockType``. The param ``af`` in the ``connect`` procs was
-  removed. This affects ``asyncnet``, ``asyncdispatch``, ``net``, and
-  ``rawsockets``.
-- ``varargs[typed]`` and ``varargs[untyped]`` have been refined and now work
-  as expected. However ``varargs[untyped]`` is not an alias anymore for
-  ``varargs[expr]``. So if your code breaks for ``varargs[untyped]``, use
-  ``varargs[expr]`` instead. The same applies to ``varargs[typed]`` vs
-  ``varargs[stmt]``.
-- ``sequtils.delete`` doesn't take confusing default arguments anymore.
-- ``system.free`` was an error-prone alias to ``system.dealloc`` and has
-  been removed.
-- ``macros.high`` never worked and the manual says ``high`` cannot be
-  overloaded, so we removed it with no deprecation cycle.
-- To use the ``parallel`` statement you now have to
-  use the ``--experimental`` mode.
-- Toplevel procs of calling convention ``closure`` never worked reliably
-  and are now deprecated and will be removed from the language. Instead you
-  have to insert type conversions
-  like ``(proc (a, b: int) {.closure.})(myToplevelProc)`` if necessary.
-- The modules ``libffi``, ``sdl``, ``windows``, ``zipfiles``, ``libzip``,
-  ``zlib``, ``zzip``, ``dialogs``, ``expat``, ``graphics``, ``libcurl``,
-  ``sphinx`` have been moved out of the stdlib and are Nimble packages now.
-- The constant fights between 32 and 64 bit DLLs on Windows have been put to
-  an end: The standard distribution now ships with 32 and 64 bit versions
-  of all the DLLs the standard library needs. This means that the following
-  DLLs are now split into 32 and 64 versions:
-
-  * ``pcre.dll``: Split into ``pcre32.dll`` and ``pcre64.dll``.
-  * ``pdcurses.dll``: Split into ``pdcurses32.dll`` and ``pdcurses64.dll``.
-  * ``sqlite3.dll``: Split into ``sqlite3_32.dll`` and ``sqlite3_64.dll``.
-  * ``ssleay32.dll``: Split into ``ssleay32.dll`` and ``ssleay64.dll``.
-  * ``libeay32.dll``: Split into ``libeay32.dll`` and ``libeay64.dll``.
-
-  Compile with ``-d:nimOldDLLs`` to make the stdlib use the old DLL names.
-- Nim VM now treats objects as ``nkObjConstr`` nodes, and not ``nkPar`` nodes
-  as it was previously. Macros that generate ``nkPar`` nodes when object is
-  expected are likely to break. Macros that expect ``nkPar`` nodes to which
-  objects are passed are likely to break as well.
-- Base methods now need to be annotated with the ``base`` pragma. This makes
-  multi methods less error-prone to use with the effect system.
-- Nim's parser directive ``#!`` is now ``#?`` in order to produce no conflicts
-  with Unix's ``#!``.
-- An implicit return type for an iterator is now deprecated. Use ``auto`` if
-  you want more type inference.
-- The type ``auto`` is now a "multi-bind" metatype, so the following compiles:
-
-  .. code-block:: nim
-    proc f(x, y: auto): auto =
-      result = $x & y
-
-    echo f(0, "abc")
-- The ``ftpclient`` module is now deprecated in favour of the
-  ``asyncftpclient`` module.
-- In sequtils.nim renamed ``repeat`` function to ``cycle`` (concatenating
-  a sequence by itself the given times), and also introduced ``repeat``,
-  which repeats an element the given times.
-- The function ``map`` is moved to sequtils.nim. The inplace ``map`` version
-  is renamed to ``apply``.
-- The template ``mapIt`` now doesn't require the result's type parameter.
-  Also the inplace ``mapIt`` is renamed to ``apply``.
-- The compiler is now stricter with what is allowed as a case object
-  discriminator. The following code used to compile but was not supported
-  completely and so now fails:
-
-.. code-block:: nim
-    type
-        DataType* {.pure.} = enum
-            Char = 1,
-            Int8 = 2,
-            Int16 = 3,
-            Int32 = 4,
-            Int64 = 5,
-            Float32 = 6,
-            Float64 = 7
-
-        DataSeq* = object
-            case kind* : DataType
-            of DataType.Char: charSeq* : seq[char]
-            of DataType.Int8: int8Seq* : seq[int8]
-            of DataType.Int16: int16Seq* : seq[int16]
-            of DataType.Int32: int32Seq* : seq[int32]
-            of DataType.Int64: int64Seq* : seq[int64]
-            of DataType.Float32: float32Seq* : seq[float32]
-            of DataType.Float64: float64Seq* : seq[float64]
-
-            length* : int
-
-
-
-Library Additions
------------------
-
-- The nre module has been added, providing a better interface to PCRE than re.
-- The ``expandSymlink`` proc has been added to the ``os`` module.
-- The ``tailDir`` proc has been added to the ``os`` module.
-- Define ``nimPinToCpu`` to make the ``threadpool`` use explicit thread
-  affinities. This can speed up or slow down the thread pool; it's up to you
-  to benchmark it.
-- ``strutils.formatFloat`` and ``formatBiggestFloat`` do not depend on the C
-  locale anymore and now take an optional ``decimalSep = '.'`` parameter.
-- Added ``unicode.lastRune``, ``unicode.graphemeLen``.
-
-
-Compiler Additions
-------------------
-
-- The compiler now supports a new configuration system based on
-  `NimScript <docs/nims.html>`_.
-- The compiler finally considers symbol binding rules in templates and
-  generics for overloaded ``[]``, ``[]=``, ``{}``, ``{}=`` operators
-  (issue `#2599 <https://github.com/nim-lang/Nim/issues/2599>`_).
-- The compiler now supports a `bitsize pragma <docs/manual.html#pragmas-bitsize-pragma>`_
-  for constructing bitfields.
-- Added a new ``--reportConceptFailures`` switch for better debugging of
-  concept related type mismatches. This can also be used to debug
-  ``system.compiles`` failures.
-
-
-Language Additions
-------------------
-
-- ``system.unsafeAddr`` can be used to access the address of a ``let``
-  variable or parameter for C interoperability. Since technically this
-  makes parameters and ``let`` variables mutable, it is considered even more
-  unsafe than the ordinary ``addr`` builtin.
-- Added ``macros.getImpl`` that can be used to access the implementation of
-  a routine or a constant. This allows for example for user-defined inlining
-  of function calls.
-- Tuple unpacking finally works in a non-var/let context: ``(x, y) = f()``
-  is allowed. Note that this doesn't declare ``x`` and ``y`` variables, for
-  this ``let (x, y) = f()`` still needs to be used.
-- ``when nimvm`` can now be used for compiletime versions of some code
-  sections. Click `here <docs/manual.html#when-nimvm-statement>`_ for details.
-- Usage of the type ``NimNode`` in a proc now implicitly annotates the proc
-  with ``.compileTime``. This means generics work much better for ``NimNode``.
-
-
-Bugfixes
---------
-- Fixed "Compiler internal error on iterator it(T: typedesc[Base]) called with it(Child), where Child = object of Base"
-  (`#2662 <https://github.com/Araq/Nim/issues/2662>`_)
-- Fixed "repr() misses base object field in 2nd level derived object"
-  (`#2749 <https://github.com/Araq/Nim/issues/2749>`_)
-- Fixed "nimsuggest doesn't work more than once on the non-main file"
-  (`#2694 <https://github.com/Araq/Nim/issues/2694>`_)
-- Fixed "JS Codegen. Passing arguments by var in certain cases leads to invalid JS."
-  (`#2798 <https://github.com/Araq/Nim/issues/2798>`_)
-- Fixed ""check" proc in unittest.nim prevents the propagation of changes to var parameters."
-  (`#964 <https://github.com/Araq/Nim/issues/964>`_)
-- Fixed "Excessive letters in integer literals are not an error"
-  (`#2523 <https://github.com/Araq/Nim/issues/2523>`_)
-- Fixed "Unicode dashes as "lisp'ish" alternative to hump and snake notation"
-  (`#2811 <https://github.com/Araq/Nim/issues/2811>`_)
-- Fixed "Bad error message when trying to construct an object incorrectly"
-  (`#2584 <https://github.com/Araq/Nim/issues/2584>`_)
-- Fixed "Determination of GC safety of globals is broken "
-  (`#2854 <https://github.com/Araq/Nim/issues/2854>`_)
-- Fixed "v2 gc crashes compiler"
-  (`#2687 <https://github.com/Araq/Nim/issues/2687>`_)
-- Fixed "Compile error using object in const array"
-  (`#2774 <https://github.com/Araq/Nim/issues/2774>`_)
-- Fixed "httpclient async requests with method httpPOST isn't sending Content-Length header"
-  (`#2884 <https://github.com/Araq/Nim/issues/2884>`_)
-- Fixed "Streams module not working with JS backend"
-  (`#2148 <https://github.com/Araq/Nim/issues/2148>`_)
-- Fixed "Sign of certain short constants is wrong"
-  (`#1179 <https://github.com/Araq/Nim/issues/1179>`_)
-- Fixed "Symlinks to directories reported as symlinks to files"
-  (`#1985 <https://github.com/Araq/Nim/issues/1985>`_)
-- Fixed "64-bit literals broken on x86"
-  (`#2909 <https://github.com/Araq/Nim/issues/2909>`_)
-- Fixed "import broken for certain names"
-  (`#2904 <https://github.com/Araq/Nim/issues/2904>`_)
-- Fixed "Invalid UTF-8 strings in JavaScript"
-  (`#2917 <https://github.com/Araq/Nim/issues/2917>`_)
-- Fixed "[JS][Codegen] Initialising object doesn't create unmentioned fields."
-
-  (`#2617 <https://github.com/Araq/Nim/issues/2617>`_)
-- Fixed "Table returned from proc computed at compile time is missing keys:"
-  (`#2297 <https://github.com/Araq/Nim/issues/2297>`_)
-- Fixed "Clarify copyright status for some files"
-  (`#2949 <https://github.com/Araq/Nim/issues/2949>`_)
-- Fixed "math.nim: trigonometry: radians to degrees conversion"
-  (`#2881 <https://github.com/Araq/Nim/issues/2881>`_)
-- Fixed "xoring unsigned integers yields RangeError in certain conditions"
-  (`#2979 <https://github.com/Araq/Nim/issues/2979>`_)
-- Fixed "Directly checking equality between procs"
-  (`#2985 <https://github.com/Araq/Nim/issues/2985>`_)
-- Fixed "Compiler crashed, but there have to be meaningful error message"
-  (`#2974 <https://github.com/Araq/Nim/issues/2974>`_)
-- Fixed "repr is broken"
-  (`#2992 <https://github.com/Araq/Nim/issues/2992>`_)
-- Fixed "Ipv6 devel - add IPv6 support for asyncsockets, make AF_INET6 a default"
-  (`#2976 <https://github.com/Araq/Nim/issues/2976>`_)
-- Fixed "Compilation broken on windows"
-  (`#2996 <https://github.com/Araq/Nim/issues/2996>`_)
-- Fixed "'u64 literal conversion compiler error"
-  (`#2731 <https://github.com/Araq/Nim/issues/2731>`_)
-- Fixed "Importing 'impure' libraries while using threads causes segfaults"
-  (`#2672 <https://github.com/Araq/Nim/issues/2672>`_)
-- Fixed "Uncatched exception in async procedure on raise statement"
-  (`#3014 <https://github.com/Araq/Nim/issues/3014>`_)
-- Fixed "nim doc2 fails in Mac OS X due to system.nim (possibly related to #1898)"
-  (`#3005 <https://github.com/Araq/Nim/issues/3005>`_)
-- Fixed "IndexError when rebuilding Nim on iteration 2"
-  (`#3018 <https://github.com/Araq/Nim/issues/3018>`_)
-- Fixed "Assigning large const set to variable looses some information"
-  (`#2880 <https://github.com/Araq/Nim/issues/2880>`_)
-- Fixed "Inconsistent generics behavior"
-  (`#3022 <https://github.com/Araq/Nim/issues/3022>`_)
-- Fixed "Compiler breaks on float64 division"
-  (`#3028 <https://github.com/Araq/Nim/issues/3028>`_)
-- Fixed "Confusing error message comparing string to nil "
-  (`#2935 <https://github.com/Araq/Nim/issues/2935>`_)
-- Fixed "convert 64bit number to float on 32bit"
-  (`#1463 <https://github.com/Araq/Nim/issues/1463>`_)
-- Fixed "Type redefinition and construction will break nim check"
-  (`#3032 <https://github.com/Araq/Nim/issues/3032>`_)
-- Fixed "XmlParser fails on very large XML files without new lines"
-  (`#2429 <https://github.com/Araq/Nim/issues/2429>`_)
-- Fixed "Error parsing arguments with whitespaces"
-  (`#2874 <https://github.com/Araq/Nim/issues/2874>`_)
-- Fixed "Crash when missing one arg and used a named arg"
-  (`#2993 <https://github.com/Araq/Nim/issues/2993>`_)
-- Fixed "Wrong number of arguments in assert will break nim check"
-  (`#3044 <https://github.com/Araq/Nim/issues/3044>`_)
-- Fixed "Wrong const definition will break nim check"
-  (`#3041 <https://github.com/Araq/Nim/issues/3041>`_)
-- Fixed "Wrong set declaration will break nim check"
-  (`#3040 <https://github.com/Araq/Nim/issues/3040>`_)
-- Fixed "Compiler segfault (type section)"
-  (`#2540 <https://github.com/Araq/Nim/issues/2540>`_)
-- Fixed "Segmentation fault when compiling this code"
-  (`#3038 <https://github.com/Araq/Nim/issues/3038>`_)
-- Fixed "Kill nim i"
-  (`#2633 <https://github.com/Araq/Nim/issues/2633>`_)
-- Fixed "Nim check will break on wrong array declaration"
-  (`#3048 <https://github.com/Araq/Nim/issues/3048>`_)
-- Fixed "boolVal seems to be broken"
-  (`#3046 <https://github.com/Araq/Nim/issues/3046>`_)
-- Fixed "Nim check crashes on wrong set/array declaration inside ref object"
-  (`#3062 <https://github.com/Araq/Nim/issues/3062>`_)
-- Fixed "Nim check crashes on incorrect generic arg definition"
-  (`#3051 <https://github.com/Araq/Nim/issues/3051>`_)
-- Fixed "Nim check crashes on iterating nonexistent var"
-  (`#3053 <https://github.com/Araq/Nim/issues/3053>`_)
-- Fixed "Nim check crashes on wrong param set declaration + iteration"
-  (`#3054 <https://github.com/Araq/Nim/issues/3054>`_)
-- Fixed "Wrong sharing of static_t instantations"
-  (`#3112 <https://github.com/Araq/Nim/issues/3112>`_)
-- Fixed "Automatically generated proc conflicts with user-defined proc when .exportc.'ed"
-  (`#3134 <https://github.com/Araq/Nim/issues/3134>`_)
-- Fixed "getTypeInfo call crashes nim"
-  (`#3099 <https://github.com/Araq/Nim/issues/3099>`_)
-- Fixed "Array ptr dereference"
-  (`#2963 <https://github.com/Araq/Nim/issues/2963>`_)
-- Fixed "Internal error when `repr`-ing a type directly"
-  (`#3079 <https://github.com/Araq/Nim/issues/3079>`_)
-- Fixed "unknown type name 'TNimType' after importing typeinfo module"
-  (`#2841 <https://github.com/Araq/Nim/issues/2841>`_)
-- Fixed "Can export a template twice and from inside a block"
-  (`#1738 <https://github.com/Araq/Nim/issues/1738>`_)
-- Fixed "C Codegen: C Types are defined after their usage in certain cases"
-  (`#2823 <https://github.com/Araq/Nim/issues/2823>`_)
-- Fixed "s.high refers to the current seq instead of the old one"
-  (`#1832 <https://github.com/Araq/Nim/issues/1832>`_)
-- Fixed "Error while unmarshaling null values"
-  (`#3149 <https://github.com/Araq/Nim/issues/3149>`_)
-- Fixed "Inference of `static[T]` in sequences"
-  (`#3144 <https://github.com/Araq/Nim/issues/3144>`_)
-- Fixed "Argument named "closure" to proc inside template interfere with closure pragma"
-  (`#3171 <https://github.com/Araq/Nim/issues/3171>`_)
-- Fixed "Internal error with aliasing inside template"
-  (`#3158 <https://github.com/Araq/Nim/issues/3158>`_)
-- Fixed "Cardinality of sets prints unexpected value"
-  (`#3135 <https://github.com/Araq/Nim/issues/3135>`_)
-- Fixed "Nim crashes on const assignment from function returning var ref object"
-  (`#3103 <https://github.com/Araq/Nim/issues/3103>`_)
-- Fixed "`repr` cstring"
-  (`#3080 <https://github.com/Araq/Nim/issues/3080>`_)
-- Fixed "Nim check crashes on wrong enum declaration"
-  (`#3052 <https://github.com/Araq/Nim/issues/3052>`_)
-- Fixed "Compiler assertion when evaluating template with static[T]"
-  (`#1858 <https://github.com/Araq/Nim/issues/1858>`_)
-- Fixed "Erroneous overflow in iterators when compiler built with overflowChecks enabled"
-  (`#3140 <https://github.com/Araq/Nim/issues/3140>`_)
-- Fixed "Unicode dashes as "lisp'ish" alternative to hump and snake notation"
-  (`#2811 <https://github.com/Araq/Nim/issues/2811>`_)
-- Fixed "Calling discardable proc from a defer is an error."
-  (`#3185 <https://github.com/Araq/Nim/issues/3185>`_)
-- Fixed "Defer statement at the end of a block produces ICE"
-  (`#3186 <https://github.com/Araq/Nim/issues/3186>`_)
-- Fixed "Call to `createU` fails to compile"
-  (`#3193 <https://github.com/Araq/Nim/issues/3193>`_)
-- Fixed "VM crash when accessing array's element"
-  (`#3192 <https://github.com/Araq/Nim/issues/3192>`_)
-- Fixed "Unexpected proc invoked when different modules add procs to a type from a 3rd module"
-  (`#2664 <https://github.com/Araq/Nim/issues/2664>`_)
-- Fixed "Nim crashes on conditional declaration inside a template"
-  (`#2670 <https://github.com/Araq/Nim/issues/2670>`_)
-- Fixed "Iterator names conflict within different scopes"
-  (`#2752 <https://github.com/Araq/Nim/issues/2752>`_)
-- Fixed "VM: Cannot assign int value to ref variable"
-  (`#1329 <https://github.com/Araq/Nim/issues/1329>`_)
-- Fixed "Incorrect code generated for tagged unions with enums not starting at zero"
-  (`#3096 <https://github.com/Araq/Nim/issues/3096>`_)
-- Fixed "Compile time procs using forward declarations are silently ignored"
-  (`#3066 <https://github.com/Araq/Nim/issues/3066>`_)
-- Fixed "re binding error in generic"
-  (`#1965 <https://github.com/Araq/Nim/issues/1965>`_)
-- Fixed "os.getCreationTime is incorrect/impossible on Posix systems"
-  (`#1058 <https://github.com/Araq/Nim/issues/1058>`_)
-- Fixed "Improve error message for osproc.startProcess when command does not exist"
-  (`#2183 <https://github.com/Araq/Nim/issues/2183>`_)
-- Fixed "gctest segfaults with --gc:markandsweep on x86_64"
-  (`#2305 <https://github.com/Araq/Nim/issues/2305>`_)
-- Fixed "Coroutine changes break compilation on unsupported architectures"
-  (`#3245 <https://github.com/Araq/Nim/issues/3245>`_)
-- Fixed "Bugfix: Windows 32bit  TinyCC support issue fixed"
-  (`#3237 <https://github.com/Araq/Nim/issues/3237>`_)
-- Fixed "db_mysql getValue() followed by exec() causing error"
-  (`#3220 <https://github.com/Araq/Nim/issues/3220>`_)
-- Fixed "xmltree.newEntity creates xnCData instead of xnEntity"
-  (`#3282 <https://github.com/Araq/Nim/issues/3282>`_)
-- Fixed "Methods and modules don't work together"
-  (`#2590 <https://github.com/Araq/Nim/issues/2590>`_)
-- Fixed "String slicing not working in the vm"
-  (`#3300 <https://github.com/Araq/Nim/issues/3300>`_)
-- Fixed "internal error: evalOp(mTypeOf)"
-  (`#3230 <https://github.com/Araq/Nim/issues/3230>`_)
-- Fixed "#! source code prefix collides with Unix Shebang"
-  (`#2559 <https://github.com/Araq/Nim/issues/2559>`_)
-- Fixed "wrong codegen for constant object"
-  (`#3195 <https://github.com/Araq/Nim/issues/3195>`_)
-- Fixed "Doc comments inside procs with implicit returns don't work"
-  (`#1528 <https://github.com/Araq/Nim/issues/1528>`_)
diff --git a/web/news/e018_oscon_amsterdam.rst b/web/news/e018_oscon_amsterdam.rst
deleted file mode 100644
index fcb4a8794..000000000
--- a/web/news/e018_oscon_amsterdam.rst
+++ /dev/null
@@ -1,13 +0,0 @@
-Andreas Rumpf's talk at OSCON Amsterdam
-==================================================
-
-.. container:: metadata
-
-  Posted by Dominik Picheta on 18/01/2016
-
-In case you have missed it, here is Andreas' Nim: An Overview talk at
-OSCON Amsterdam.
-
-.. raw:: html
-
-  <iframe width="560" height="315" src="https://www.youtube.com/embed/4rJEBs_Nnaw" frameborder="0" allowfullscreen></iframe>
diff --git a/web/news/e019_version_0_13_0.rst b/web/news/e019_version_0_13_0.rst
deleted file mode 100644
index 2c8e66fa3..000000000
--- a/web/news/e019_version_0_13_0.rst
+++ /dev/null
@@ -1,182 +0,0 @@
-Version 0.13.0 released
-=======================
-
-.. container:: metadata
-
-  Posted by Dominik Picheta on 18/01/2016
-
-Once again we are proud to announce the latest release of the Nim compiler
-and related tools. This release comes just 3 months after the last
-release!
-
-A new version of Nimble which depends on this release, has also been
-released. See `this <http://forum.nim-lang.org/t/1912>`_ forum thread for
-more information about the Nimble release.
-
-This release of Nim includes over 116 bug fixes, many of which are related
-to closures. The lambda lifting algorithm in the compiler has been completely
-rewritten, and some changes have been made to the semantics of closures in
-Nim as a result. These changes may affect backwards compatibility and are all
-described in the section below.
-
-With this release, we are one step closer to Nim version 1.0.
-The 1.0 release will be a big milestone for Nim, because after that version
-is released there will be no more breaking changes made to the language
-or the standard library.
-
-That being said, the next release will likely be Nim 0.14. It will focus on
-improvements to the GC and concurrency. We will in particular be looking at
-ways to add multi-core support to async await. Standard library improvements
-are also on our roadmap but may not make it for Nim 0.14.
-
-As always you can download the latest version of Nim from the
-`download <download.html>`_ page.
-
-Happy coding!
-
-Changes affecting backwards compatibility
------------------------------------------
-
-- ``macros.newLit`` for ``bool`` now produces false/true symbols which
-  actually work with the bool datatype.
-- When compiling to JS: ``Node``, ``NodeType`` and ``Document`` are no longer
-  defined. Use the types defined in ``dom.nim`` instead.
-- The check ``x is iterator`` (used for instance in concepts) was always a
-  weird special case (you could not use ``x is proc``) and was removed from
-  the language.
-- Top level routines cannot have the calling convention ``closure``
-  anymore.
-- The ``redis`` module has been moved out of the standard library. It can
-  now be installed via Nimble and is located here:
-  https://github.com/nim-lang/redis
-- ``math.RunningStat`` and its associated procs have been moved from
-  the ``math`` module to a new ``stats`` module.
-
-
-Syntax changes
-~~~~~~~~~~~~~~
-
-The parser now considers leading whitespace in front of operators
-to determine if an operator is used in prefix or infix position.
-This means that finally ``echo $foo`` is parsed as people expect,
-which is as ``echo($foo)``. It used to be parsed as ``(echo) $ (foo)``.
-
-``echo $ foo`` continues to be parsed as ``(echo) $ (foo)``.
-
-This also means that ``-1`` is always parsed as prefix operator so
-code like ``0..kArraySize div 2 -1`` needs to be changed to
-``0..kArraySize div 2 - 1``.
-
-This release also adds multi-line comments to Nim. The syntax for them is:
-``#[ comment here ]#``. For more details read the section of
-the `manual <docs/manual.html#lexical-analysis-multiline-comments>`_.
-
-Iterator changes
-~~~~~~~~~~~~~~~~
-
-Implicit return type inference for iterators has been removed from the language. The following used to work:
-
-.. code-block:: nim
-  iterator it =
-    yield 7
-
-This was a strange special case and has been removed. Now you need to write it like so which is consistent with procs:
-
-.. code-block:: nim
-  iterator it: auto =
-    yield 7
-
-
-Closure changes
-~~~~~~~~~~~~~~~
-
-The semantics of closures changed: Capturing variables that are in loops do not produce a new environment. Nim closures behave like JavaScript closures now.
-
-The following used to work as the environment creation used to be attached to the loop body:
-
-.. code-block:: nim
-
-  proc outer =
-    var s: seq[proc(): int {.closure.}] = @[]
-    for i in 0 ..< 30:
-      let ii = i
-      s.add(proc(): int = return ii*ii)
-
-This behaviour has changed in 0.13.0 and now needs to be written as:
-
-.. code-block:: nim
-
-  proc outer =
-    var s: seq[proc(): int {.closure.}] = @[]
-    for i in 0 ..< 30:
-      (proc () =
-        let ii = i
-        s.add(proc(): int = return ii*ii))()
-
-The reason is that environment creations are now only performed once
-per proc call. This change is subtle and unfortunate, but:
-
-1. Affects almost no code out there.
-2. Is easier to implement and we are at a point in Nim's development process where simple+stable wins over perfect-in-theory+unstable-in-practice.
-3. Implies programmers are more in control of where memory is allocated which is beneficial for a systems programming language.
-
-Bugfixes
---------
-
-The list below has been generated based on the commits in Nim's git
-repository. As such it lists only the issues which have been closed
-via a commit, for a full list see
-`this link on Github <https://github.com/nim-lang/Nim/issues?utf8=%E2%9C%93&q=is%3Aissue+closed%3A%222015-10-27+..+2016-01-19%22+>`_.
-
-- Fixed "Generic arguments cannot be used in templates (raising undeclared identifier)"
-  (`#3498 <https://github.com/nim-lang/Nim/issues/3498>`_)
-- Fixed "multimethods: Error: internal error: cgmeth.genConv"
-  (`#3550 <https://github.com/nim-lang/Nim/issues/3550>`_)
-- Fixed "nimscript - SIGSEGV in except block"
-  (`#3546 <https://github.com/nim-lang/Nim/issues/3546>`_)
-- Fixed "Bool literals in macros do not work."
-  (`#3541 <https://github.com/nim-lang/Nim/issues/3541>`_)
-- Fixed "Docs: nativesocket.html - 404"
-  (`#3582 <https://github.com/nim-lang/Nim/issues/3582>`_)
-- Fixed ""not nil" return types never trigger an error or warning"
-  (`#2285 <https://github.com/nim-lang/Nim/issues/2285>`_)
-- Fixed "No warning or error is raised even if not nil is specified "
-  (`#3222 <https://github.com/nim-lang/Nim/issues/3222>`_)
-- Fixed "Incorrect fsmonitor add() filter logic"
-  (`#3611 <https://github.com/nim-lang/Nim/issues/3611>`_)
-- Fixed ""nimble install nimsuggest" failed"
-  (`#3622 <https://github.com/nim-lang/Nim/issues/3622>`_)
-- Fixed "compile time `excl ` cause SIGSEGV"
-  (`#3639 <https://github.com/nim-lang/Nim/issues/3639>`_)
-- Fixed "Unable to echo unsigned ints at compile-time"
-  (`#2514 <https://github.com/nim-lang/Nim/issues/2514>`_)
-- Fixed "Nested closure iterator produces internal error"
-  (`#1725 <https://github.com/nim-lang/Nim/issues/1725>`_)
-- Fixed "C Error on walkDirRec closure"
-  (`#3636 <https://github.com/nim-lang/Nim/issues/3636>`_)
-- Fixed "Error in generated c code"
-  (`#3201 <https://github.com/nim-lang/Nim/issues/3201>`_)
-- Fixed "C Compile-time error with generic proc type."
-  (`#2659 <https://github.com/nim-lang/Nim/issues/2659>`_)
-- Fixed "ICE dereferencing array pointer"
-  (`#2240 <https://github.com/nim-lang/Nim/issues/2240>`_)
-- Fixed "Lambda lifting crash"
-  (`#2007 <https://github.com/nim-lang/Nim/issues/2007>`_)
-- Fixed "Can't reference outer variables from a closure in an iterator"
-  (`#2604 <https://github.com/nim-lang/Nim/issues/2604>`_)
-- Fixed "M&S collector breaks with nested for loops."
-  (`#603 <https://github.com/nim-lang/Nim/issues/603>`_)
-- Fixed "Regression: bad C codegen"
-  (`#3723 <https://github.com/nim-lang/Nim/issues/3723>`_)
-- Fixed "JS backend - handle bool type in case statement"
-  (`#3722 <https://github.com/nim-lang/Nim/issues/3722>`_)
-- Fixed "linenoise compilation with cpp"
-  (`#3720 <https://github.com/nim-lang/Nim/issues/3720>`_)
-- Fixed "(???,???) duplicate case label"
-  (`#3665 <https://github.com/nim-lang/Nim/issues/3665>`_)
-- Fixed "linenoise compilation with cpp"
-  (`#3720 <https://github.com/nim-lang/Nim/issues/3720>`_)
-- Fixed "Update list of backward incompatibilities for Nim 0.12.0 in the main site"
-  (`#3689 <https://github.com/nim-lang/Nim/issues/3689>`_)
-- Fixed "Can't compile nimble with latest devel - codegen bug"
-  (`#3730 <https://github.com/nim-lang/Nim/issues/3730>`_)
diff --git a/web/news/e020_nim_in_action.rst b/web/news/e020_nim_in_action.rst
deleted file mode 100644
index 33bcb7947..000000000
--- a/web/news/e020_nim_in_action.rst
+++ /dev/null
@@ -1,32 +0,0 @@
-Nim in Action is now available!
-===============================
-
-.. container:: metadata
-
-  Posted by Dominik Picheta on 27/01/2016
-
-.. raw::html
-
-  <a href="https://manning.com/books/nim-in-action?a_aid=niminaction&a_bid=78a27e81">
-    <img src="../assets/niminaction/banner.jpg" alt="New in Manning Early Access Program: Nim in Action!" width="682"/>
-  </a>
-
-We are proud to announce that *Nim in Action*, a book about the Nim programming
-language, is now available!
-
-The book is available at this URL:
-`https://www.manning.com/books/nim-in-action <https://manning.com/books/nim-in-action?a_aid=niminaction&a_bid=78a27e81>`_
-
-The first three chapters are available for download
-as an eBook through Manning's Early Access program. You can download a free
-sample of the book containing the first chapter as well!
-
-*Nim in Action* is currently being written and is expected to be completed by
-Summer 2016. If you purchase the eBook you will start receiving new chapters
-as they become available. You can also purchase the printed book together with
-the eBook for a slightly higher price.
-
-If you do read the book, even if it's just the first chapter, then please share
-any comments, suggestions and questions on the
-`Nim forum <http://forum.nim-lang.org/t/1978>`_ or in
-Manning's own `Author Online forum! <https://forums.manning.com/forums/nim-in-action>`_
diff --git a/web/news/e021_meet_sponsors.rst b/web/news/e021_meet_sponsors.rst
deleted file mode 100644
index 0bfb472c5..000000000
--- a/web/news/e021_meet_sponsors.rst
+++ /dev/null
@@ -1,30 +0,0 @@
-Meet our BountySource sponsors
-==============================
-
-.. container:: metadata
-
-  Posted by Dominik Picheta on 04/06/2016
-
-
-.. raw::html
-
-  <a href="../sponsors.html">
-    <img src="../assets/bountysource/meet_sponsors.png" alt="Meet our BountySource sponsors!" width="400"/>
-  </a>
-
-It has now been two months since we began our
-`BountySource fundraiser <https://salt.bountysource.com/teams/nim>`_. We
-promised to create a "Current sponsors" page and are happy to announce that
-it is now live `here <http://nim-lang.org/sponsors.html>`_.
-
-We are happy to say that last month we raised more than in the previous month!
-A staggering $862 was raised during that month and we thank each and every one
-of you once again for your generous contributions.
-
-We are gearing up for a brand new release of Nim that includes many bug fixes
-and some new features. In the meantime, as always, feel free to get in
-touch with us via `Twitter <https://twitter.com/nim_lang>`_, the
-`#nim channel on Freenode <http://webchat.freenode.net/?channels=nim>`_
-or via email at contact@nim-lang.org.
-
-Thanks for reading!
diff --git a/web/news/e022_version_0_14_0.rst b/web/news/e022_version_0_14_0.rst
deleted file mode 100644
index 6634d0053..000000000
--- a/web/news/e022_version_0_14_0.rst
+++ /dev/null
@@ -1,476 +0,0 @@
-Version 0.14.0 released
-=======================
-
-.. container:: metadata
-
-  Posted by Dominik Picheta on 07/06/2016
-
-It's been a while since the last release, but we've been very busy in the
-meantime. In
-addition to working on Nim we have started a
-`BountySource campaign <https://salt.bountysource.com/teams/nim>`_ and
-announced the pre-release of a new Nim book titled
-`Nim in Action <https://manning.com/books/nim-in-action?a_aid=niminaction&a_bid=78a27e81>`_.
-Our BountySource campaign has already been very successful, helping us raise
-enough funds to surpass 4 of our monthly goals. The companies and individuals
-that made this possible are listed on our brand new
-`sponsors page <http://nim-lang.org/sponsors.html>`_.
-
-This release includes over 260 bug fixes. As mentioned in the previous release
-announcement, one of the focuses of this release was going to be improvements
-to the GC. Indeed, the most prominent fixes are related to the GC not collecting
-cycles properly. This was a major problem that was triggered typically when
-applications using asynchronous I/O were left running for long periods of time.
-
-There have also been many fixes to the way that the compiler sources are
-installed. Some applications such as Nimble depend on these sources and they
-are now included in the release tarballs. This should fix many of the problems
-that users experienced trying to compile the Nimble package manager.
-
-Finally, you will find multiple changes in the standard library. Some of which
-unfortunately affects backwards compatibility. This includes the ``random``
-procedures being moved to a new ``random`` module, HTTP headers being stored
-in a new ``HttpHeaders`` object and the ``round`` procedure in the ``math`` module
-being changed to return a ``float`` instead of an ``int``. You can find a full
-list of such changes below.
-
-Together with the new release of Nim, we are also releasing a new version of
-Nimble. The release notes for it are available on
-`GitHub <https://github.com/nim-lang/nimble/blob/master/changelog.markdown#074---06062016>`_.
-
-As always you can download the latest version of Nim from the
-`download <http://nim-lang.org/download.html>`_ page.
-
-We hope that you will like this new release. Let us know if you run into
-any trouble, have any questions or want to give some feedback. You can get
-in touch with us on the `Forum <http://forum.nim-lang.org/>`_,
-`IRC <http://webchat.freenode.net/?channels=nim>`_,
-`Twitter <http://twitter.com/nim_lang>`_,
-or via email contact@nim-lang.org.
-
-Happy coding!
-
-Changes affecting backwards compatibility
------------------------------------------
-
-- ``--out`` and ``--nimcache`` command line arguments are now relative to
-  current directory. Previously they were relative to project directory.
-- The json module now stores the name/value pairs in objects internally as a
-  hash table of type ``fields*: Table[string, JsonNode]`` instead of a
-  sequence. This means that order is no longer preserved. When using the
-  ``table.mpairs`` iterator only the returned values can be modified, no
-  longer the keys.
-- The deprecated Nim shebang notation ``#!`` was removed from the language. Use ``#?`` instead.
-- Typeless parameters have been removed from the language since it would
-  clash with ``using``.
-- Procedures in ``mersenne.nim`` (Mersenne Twister implementation) no longer
-  accept and produce ``int`` values which have platform-dependent size -
-  they use ``uint32`` instead.
-- The ``strutils.unindent`` procedure has been rewritten. Its parameters now
-  match the parameters of ``strutils.indent``. See issue `#4037 <https://github.com/nim-lang/Nim/issues/4037>`_
-  for more details.
-- The ``matchers`` module has been deprecated. See issue `#2446 <https://github.com/nim-lang/Nim/issues/2446>`_
-  for more details.
-- The ``json.[]`` no longer returns ``nil`` when a key is not found. Instead it
-  raises a ``KeyError`` exception. You can compile with the ``-d:nimJsonGet``
-  flag to get a list of usages of ``[]``, as well as to restore the operator's
-  previous behaviour.
-- When using ``useMalloc``, an additional header containing the size of the
-  allocation will be allocated, to support zeroing memory on realloc as expected
-  by the language. With this change, ``alloc`` and ``dealloc`` are no longer
-  aliases for ``malloc`` and ``free`` - use ``c_malloc`` and ``c_free`` if
-  you need that.
-- The ``json.%`` operator is now overloaded for ``object``, ``ref object`` and
-  ``openarray[T]``.
-- The procs related to ``random`` number generation in ``math.nim`` have
-  been moved to its own ``random`` module and been reimplemented in pure
-  Nim.
-- The path handling changed. The project directory is not added to the
-  search path automatically anymore. Add this line to your project's
-  config to get back the old behaviour: ``--path:"$projectdir"``. (The compiler
-  replaces ``$projectdir`` with your project's absolute directory when compiling,
-  so you don't need to replace ``$projectdir`` by your project's actual
-  directory!). See issue `#546 <https://github.com/nim-lang/Nim/issues/546>`_
-  and `this forum thread <http://forum.nim-lang.org/t/2277>`_ for more
-  information.
-- The ``round`` function in ``math.nim`` now returns a float and has been
-  corrected such that the C implementation always rounds up from .5 rather
-  than changing the operation for even and odd numbers.
-- The ``round`` function now accepts a ``places`` argument to round to a
-  given number of places (e.g. round 4.35 to 4.4 if ``places`` is 1).
-- In ``strutils.nim``, ``formatSize`` now returns a number representing the
-  size in conventional decimal format (e.g. 2.234GB meaning 2.234 GB rather
-  than meaning 2.285 GB as in the previous implementation).  By default it
-  also uses IEC prefixes (KiB, MiB) etc and optionally uses colloquial names
-  (kB, MB etc) and the (SI-preferred) space.
-- The ``==`` operator for ``cstring`` now implements a value comparison
-  for the C backend (using ``strcmp``), not reference comparisons anymore.
-  Convert the cstrings to pointers if you really want reference equality
-  for speed.
-- HTTP headers are now stored in a ``HttpHeaders`` object instead of a
-  ``StringTableRef``. This object allows multiple values to be associated with
-  a single key. A new ``httpcore`` module implements it and it is used by
-  both ``asynchttpserver`` and ``httpclient``.
-
-The ``using`` statement
-~~~~~~~~~~~~~~~~~~~~~~~
-
-The ``using`` statement now has a different meaning.
-
-In version 0.13.0, it
-was used to provide syntactic convenience for procedures that heavily use
-a single contextual parameter. For example:
-
-.. code-block:: nim
-  var socket = newSocket()
-  using socket
-
-  connect("google.com", Port(80))
-  send("GET / HTTP/1.1\c\l")
-
-
-The ``connect`` and ``send`` calls are both transformed so that they pass
-``socket`` as the first argument:
-
-.. code-block:: nim
-  var socket = newSocket()
-
-  socket.connect("google.com", Port(80))
-  socket.send("GET / HTTP/1.1\c\l")
-
-Take a look at the old version of the
-`manual <http://nim-lang.org/0.13.0/manual.html#statements-and-expressions-using-statement>`_
-to learn more about the old behaviour.
-
-In 0.14.0,
-the ``using`` statement
-instead provides a syntactic convenience for procedure definitions where the
-same parameter names and types are used repeatedly. For example, instead of
-writing:
-
-.. code-block:: nim
-  proc foo(c: Context; n: Node) = ...
-  proc bar(c: Context; n: Node, counter: int) = ...
-  proc baz(c: Context; n: Node) = ...
-
-
-You can simply write:
-
-.. code-block:: nim
-  {.experimental.}
-  using
-    c: Context
-    n: Node
-    counter: int
-
-  proc foo(c, n) = ...
-  proc bar(c, n, counter) = ...
-  proc baz(c, n) = ...
-
-Again, the
-`manual <http://nim-lang.org/docs/manual.html#statements-and-expressions-using-statement>`_
-has more details.
-
-You can still achieve a similar effect to what the old ``using`` statement
-tried to achieve by using the new experimental ``this`` pragma, documented
-`here <http://nim-lang.org/docs/manual.html#overloading-resolution-automatic-self-insertions>`_.
-
-Generic type classes
-~~~~~~~~~~~~~~~~~~~~
-
-Generic type classes are now handled properly in the compiler, but this
-means code like the following does not compile any longer:
-
-.. code-block:: nim
-  type
-    Vec3[T] = distinct array[3, T]
-
-  proc vec3*[T](a, b, c: T): Vec3[T] = Vec3([a, b, c])
-
-While every ``Vec3[T]`` is part of the ``Vec3`` type class, the reverse
-is not true, not every ``Vec3`` is a ``Vec3[T]``. Otherwise there would
-be a subtype relation between ``Vec3[int]`` and ``Vec3[float]`` and there
-is none for Nim. The fix is to write this instead:
-
-.. code-block:: nim
-  type
-    Vec3[T] = distinct array[3, T]
-
-  proc vec3*[T](a, b, c: T): Vec3[T] = Vec3[T]([a, b, c])
-
-Note that in general we don't advise to use ``distinct array``,
-use ``object`` instead.
-
-
-Library Additions
------------------
-
-- The rlocks module has been added providing a reentrant lock synchronization
-  primitive.
-- A generic "sink operator" written as ``&=`` has been added to the
-``system`` and the ``net`` modules. This operator is similar to the C++
-``<<`` operator which writes data to a stream.
-- Added ``strscans`` module that implements a ``scanf`` for easy input extraction.
-- Added a version of ``parseutils.parseUntil`` that can deal with a string
-  ``until`` token. The other versions are for ``char`` and ``set[char]``.
-- Added ``splitDecimal`` to ``math.nim`` to split a floating point value
-  into an integer part and a floating part (in the range -1<x<1).
-- Added ``trimZeros`` to ``strutils.nim`` to trim trailing zeros in a
-  floating point number.
-- Added ``formatEng`` to ``strutils.nim`` to format numbers using engineering
-  notation.
-
-
-Compiler Additions
-------------------
-
-- Added a new ``--noCppExceptions`` switch that allows to use default exception
-  handling (no ``throw`` or ``try``/``catch`` generated) when compiling to C++
-  code.
-
-Language Additions
-------------------
-
-- Nim now supports a ``.this`` pragma for more notational convenience.
-  See `automatic-self-insertions <../docs/manual.html#overloading-resolution-automatic-self-insertions>`_ for more information.
-- Nim now supports a different ``using`` statement for more convenience.
-  Consult `using-statement <../docs/manual.html#statements-and-expressions-using-statement>`_ for more information.
-- ``include`` statements are not restricted to top level statements anymore.
-
-..
-  - Nim now supports ``partial`` object declarations to mitigate the problems
-    that arise when types are mutually dependent and yet should be kept in
-    different modules.
-
-Bugfixes
---------
-
-The list below has been generated based on the commits in Nim's git
-repository. As such it lists only the issues which have been closed
-via a commit, for a full list see
-`this link on Github <https://github.com/nim-lang/Nim/issues?utf8=%E2%9C%93&q=is%3Aissue+closed%3A%222016-01-19+..+2016-06-06%22+>`_.
-
-
-  - Fixed "Calling generic templates with explicit generic arguments crashes compiler"
-    (`#3496 <https://github.com/nim-lang/Nim/issues/3496>`_)
-  - Fixed "JS backend - strange utf-8 handling"
-    (`#3714 <https://github.com/nim-lang/Nim/issues/3714>`_)
-  - Fixed "execvpe is glibc specific"
-    (`#3759 <https://github.com/nim-lang/Nim/issues/3759>`_)
-  - Fixed "GC stack overflow with in data structures with circular references."
-    (`#1895 <https://github.com/nim-lang/Nim/issues/1895>`_)
-  - Fixed "Internal compiler error in genTraverseProc"
-    (`#3794 <https://github.com/nim-lang/Nim/issues/3794>`_)
-  - Fixed "unsafeAddr fails in generic context"
-    (`#3736 <https://github.com/nim-lang/Nim/issues/3736>`_)
-  - Fixed "Generic converters produce internal errors"
-    (`#3799 <https://github.com/nim-lang/Nim/issues/3799>`_)
-  - Fixed "Cannot have two anonymous iterators in one proc"
-    (`#3788 <https://github.com/nim-lang/Nim/issues/3788>`_)
-  - Fixed "pure/net.nim fails to compile with --taintMode:on on HEAD"
-    (`#3789 <https://github.com/nim-lang/Nim/issues/3789>`_)
-  - Fixed "Using break inside iterator may produce memory/resource leak"
-    (`#3802 <https://github.com/nim-lang/Nim/issues/3802>`_)
-
-  - Fixed "--out and --nimcache wrong paths"
-    (`#3871 <https://github.com/nim-lang/Nim/issues/3871>`_)
-  - Fixed "Release 0.13.0: documentation build failure"
-    (`#3823 <https://github.com/nim-lang/Nim/issues/3823>`_)
-  - Fixed "https post request"
-    (`#3895 <https://github.com/nim-lang/Nim/issues/3895>`_)
-  - Fixed "writeFile regression in nimscript"
-    (`#3901 <https://github.com/nim-lang/Nim/issues/3901>`_)
-  - Fixed "Cannot convert variables to int16 at compile time"
-    (`#3916 <https://github.com/nim-lang/Nim/issues/3916>`_)
-  - Fixed "Error in concepts when using functions on typedesc"
-    (`#3686 <https://github.com/nim-lang/Nim/issues/3686>`_)
-  - Fixed "Multiple generic table types with different type signatures lead to compilation errors."
-    (`#3669 <https://github.com/nim-lang/Nim/issues/3669>`_)
-  - Fixed "Explicit arguments with overloaded procedure?"
-    (`#3836 <https://github.com/nim-lang/Nim/issues/3836>`_)
-  - Fixed "doc2 generates strange output for proc generated by template"
-    (`#3868 <https://github.com/nim-lang/Nim/issues/3868>`_)
-  - Fixed "Passing const value as static[] argument to immediate macro leads to infinite memory consumption by compiler"
-    (`#3872 <https://github.com/nim-lang/Nim/issues/3872>`_)
-  - Fixed "`..<` is not happy with `BiggestInt` from `intVal`"
-    (`#3767 <https://github.com/nim-lang/Nim/issues/3767>`_)
-  - Fixed "stdtmpl filter does not support anything apart from '#' metachar"
-    (`#3924 <https://github.com/nim-lang/Nim/issues/3924>`_)
-  - Fixed "lib/pure/net: Can't bind to ports >= 32768"
-    (`#3484 <https://github.com/nim-lang/Nim/issues/3484>`_)
-  - Fixed "int and float assignment compatibility badly broken for generics"
-    (`#3998 <https://github.com/nim-lang/Nim/issues/3998>`_)
-  - Fixed "Adding echo statement causes "type mismatch" error"
-    (`#3975 <https://github.com/nim-lang/Nim/issues/3975>`_)
-  - Fixed "Dynlib error messages should be written to stderr, not stdout"
-    (`#3987 <https://github.com/nim-lang/Nim/issues/3987>`_)
-  - Fixed "Tests regressions while using the devel branch"
-    (`#4005 <https://github.com/nim-lang/Nim/issues/4005>`_)
-
-  - Fixed "Lambda lifting bug: wrong c code generation"
-    (`#3995 <https://github.com/nim-lang/Nim/issues/3995>`_)
-  - Fixed "VM crashes in asgnComplex"
-    (`#3973 <https://github.com/nim-lang/Nim/issues/3973>`_)
-  - Fixed "Unknown opcode opcNGetType"
-    (`#1152 <https://github.com/nim-lang/Nim/issues/1152>`_)
-  - Fixed "`&` operator mutates first operand when used in compileTime proc while assigning result to seq"
-    (`#3804 <https://github.com/nim-lang/Nim/issues/3804>`_)
-  - Fixed "''nil' statement is deprecated' in macro"
-    (`#3561 <https://github.com/nim-lang/Nim/issues/3561>`_)
-  - Fixed "vm crash when accessing seq with mitems iterator"
-    (`#3731 <https://github.com/nim-lang/Nim/issues/3731>`_)
-  - Fixed "`mitems` or `mpairs` does not work for `seq[NimNode]` or `array[T,NimNode]` in a macro"
-    (`#3859 <https://github.com/nim-lang/Nim/issues/3859>`_)
-  - Fixed "passing "proc `,`()" to nim check causes an infinite loop"
-    (`#4036 <https://github.com/nim-lang/Nim/issues/4036>`_)
-  - Fixed "--dynlibOverride does not work with {.push dynlib: name.}"
-    (`#3646 <https://github.com/nim-lang/Nim/issues/3646>`_)
-  - Fixed "system.readChars fails on big len"
-    (`#3752 <https://github.com/nim-lang/Nim/issues/3752>`_)
-  - Fixed "strutils.unindent"
-    (`#4037 <https://github.com/nim-lang/Nim/issues/4037>`_)
-  - Fixed "Compiler's infinite recursion in generic resolution"
-    (`#2006 <https://github.com/nim-lang/Nim/issues/2006>`_)
-  - Fixed "Linux: readLineFromStdin calls quit(0) upon EOF"
-    (`#3159 <https://github.com/nim-lang/Nim/issues/3159>`_)
-  - Fixed "Forum sign up not possible"
-    (`#2446 <https://github.com/nim-lang/Nim/issues/2446>`_)
-  - Fixed "Json module - SIGSEGV if key not exists"
-    (`#3107 <https://github.com/nim-lang/Nim/issues/3107>`_)
-  - Fixed "About asyncdispatch.await and exception"
-    (`#3964 <https://github.com/nim-lang/Nim/issues/3964>`_)
-  - Fixed "Need testcase for JS backend to ensure closure callbacks don't break"
-    (`#3132 <https://github.com/nim-lang/Nim/issues/3132>`_)
-  - Fixed "Unexpected behaviour of C++ templates in conjunction with N_NIMCALL"
-    (`#4093 <https://github.com/nim-lang/Nim/issues/4093>`_)
-  - Fixed "SIGSEGV at compile time when using a compileTime variable as counter"
-    (`#4097 <https://github.com/nim-lang/Nim/issues/4097>`_)
-  - Fixed "Compiler crash issue on 32-bit machines only"
-    (`#4089 <https://github.com/nim-lang/Nim/issues/4089>`_)
-  - Fixed "type mismatch: got (<type>) but expected 'outType' in mapIt"
-    (`#4124 <https://github.com/nim-lang/Nim/issues/4124>`_)
-  - Fixed "Generic type constraints broken?"
-    (`#4084 <https://github.com/nim-lang/Nim/issues/4084>`_)
-  - Fixed "Invalid C code generated"
-    (`#3544 <https://github.com/nim-lang/Nim/issues/3544>`_)
-  - Fixed "An exit variable in proc shadows exit function called by quit()"
-    (`#3471 <https://github.com/nim-lang/Nim/issues/3471>`_)
-  - Fixed "ubuntu 16.04 build error"
-    (`#4144 <https://github.com/nim-lang/Nim/issues/4144>`_)
-  - Fixed "Ambiguous identifier error should list all possible qualifiers"
-    (`#177 <https://github.com/nim-lang/Nim/issues/177>`_)
-  - Fixed "Parameters are not captured inside closures inside closure iterators"
-    (`#4070 <https://github.com/nim-lang/Nim/issues/4070>`_)
-  - Fixed "`$` For array crashes the compiler when assigned to const"
-    (`#4040 <https://github.com/nim-lang/Nim/issues/4040>`_)
-
-  - Fixed "Default value for .importcpp enum is initialized incorrectly"
-    (`#4034 <https://github.com/nim-lang/Nim/issues/4034>`_)
-  - Fixed "Nim doesn't instantiate template parameter in cgen when using procedure return value in for-in loop"
-    (`#4110 <https://github.com/nim-lang/Nim/issues/4110>`_)
-  - Fixed "Compile-time SIGSEGV when invoking procedures that cannot be evaluated at compile time from a macro"
-    (`#3956 <https://github.com/nim-lang/Nim/issues/3956>`_)
-  - Fixed "Backtricks inside .emit pragma output incorrect name for types"
-    (`#3992 <https://github.com/nim-lang/Nim/issues/3992>`_)
-  - Fixed "typedef is generated for .importcpp enums"
-    (`#4145 <https://github.com/nim-lang/Nim/issues/4145>`_)
-  - Fixed "Incorrect C code generated for nnkEmpty node"
-    (`#950 <https://github.com/nim-lang/Nim/issues/950>`_)
-  - Fixed "Syntax error in config file appears as general exception without useful info"
-    (`#3763 <https://github.com/nim-lang/Nim/issues/3763>`_)
-  - Fixed "Converting .importcpp enum to string doesn't work when done inside procs"
-    (`#4147 <https://github.com/nim-lang/Nim/issues/4147>`_)
-  - Fixed "Enum template specifiers do not work for .importcpp enums when they are used as a parameter"
-    (`#4146 <https://github.com/nim-lang/Nim/issues/4146>`_)
-  - Fixed "Providing template specifier recursively for .importcpp type doesn't work"
-    (`#4148 <https://github.com/nim-lang/Nim/issues/4148>`_)
-  - Fixed "sizeof doesn't work for generics in vm"
-    (`#4153 <https://github.com/nim-lang/Nim/issues/4153>`_)
-  - Fixed "Creating list-like structures in a loop leaks memory indefinitely"
-    (`#3793 <https://github.com/nim-lang/Nim/issues/3793>`_)
-  - Fixed "Creating list-like structures in a loop leaks memory indefinitely"
-    (`#3793 <https://github.com/nim-lang/Nim/issues/3793>`_)
-  - Fixed "Enum items generated by a macro have wrong type."
-    (`#4066 <https://github.com/nim-lang/Nim/issues/4066>`_)
-  - Fixed "Memory leak with default GC"
-    (`#3184 <https://github.com/nim-lang/Nim/issues/3184>`_)
-  - Fixed "Rationals Overflow Error on 32-bit machine"
-    (`#4194 <https://github.com/nim-lang/Nim/issues/4194>`_)
-
-  - Fixed "osproc waitForExit() is ignoring the timeout parameter"
-    (`#4200 <https://github.com/nim-lang/Nim/issues/4200>`_)
-  - Fixed "Regression: exception parseFloat("-0.0") "
-    (`#4212 <https://github.com/nim-lang/Nim/issues/4212>`_)
-  - Fixed "JS Codegen: Bad constant initialization order"
-    (`#4222 <https://github.com/nim-lang/Nim/issues/4222>`_)
-  - Fixed "Term-rewriting macros gives Error: wrong number of arguments"
-    (`#4227 <https://github.com/nim-lang/Nim/issues/4227>`_)
-  - Fixed "importcpp allowed in body of proc after push"
-    (`#4225 <https://github.com/nim-lang/Nim/issues/4225>`_)
-  - Fixed "pragma SIGSEGV"
-    (`#4001 <https://github.com/nim-lang/Nim/issues/4001>`_)
-  - Fixed "Restrict hints to the current project"
-    (`#2159 <https://github.com/nim-lang/Nim/issues/2159>`_)
-  - Fixed "`unlikely`/`likely` should be no-ops for the Javascript backend"
-    (`#3882 <https://github.com/nim-lang/Nim/issues/3882>`_)
-  - Fixed ".this pragma doesn't work for fields and procs defined for parent type"
-    (`#4177 <https://github.com/nim-lang/Nim/issues/4177>`_)
-  - Fixed "VM SIGSEV with compile-time Table"
-    (`#3729 <https://github.com/nim-lang/Nim/issues/3729>`_)
-  - Fixed "Error during compilation with cpp option on FreeBSD "
-    (`#3059 <https://github.com/nim-lang/Nim/issues/3059>`_)
-  - Fixed "Compiler doesn't keep type bounds"
-    (`#1713 <https://github.com/nim-lang/Nim/issues/1713>`_)
-  - Fixed "Stdlib: future: Shortcut proc definition doesn't support, varargs, seqs, arrays, or openarrays"
-    (`#4238 <https://github.com/nim-lang/Nim/issues/4238>`_)
-  - Fixed "Why don't ``asynchttpserver`` support request-body when ``put`` ``delete``?"
-    (`#4221 <https://github.com/nim-lang/Nim/issues/4221>`_)
-  - Fixed "Paths for includes in Nim documentation"
-    (`#2640 <https://github.com/nim-lang/Nim/issues/2640>`_)
-  - Fixed "Compile pragma doesn't work with relative import"
-    (`#1262 <https://github.com/nim-lang/Nim/issues/1262>`_)
-  - Fixed "Slurp doesn't work with relative imports"
-    (`#765 <https://github.com/nim-lang/Nim/issues/765>`_)
-  - Fixed "Make tilde expansion consistent"
-    (`#786 <https://github.com/nim-lang/Nim/issues/786>`_)
-  - Fixed "koch expects nim to be in path for tests?"
-    (`#3290 <https://github.com/nim-lang/Nim/issues/3290>`_)
-  - Fixed "Don't use relative imports for non relative modules (aka babel libs)"
-    (`#546 <https://github.com/nim-lang/Nim/issues/546>`_)
-  - Fixed ""echo" on general structs does not work"
-    (`#4236 <https://github.com/nim-lang/Nim/issues/4236>`_)
-  - Fixed "Changing math.round() and adding math.integer()"
-    (`#3473 <https://github.com/nim-lang/Nim/issues/3473>`_)
-  - Fixed "Mathematics module missing modf"
-    (`#4195 <https://github.com/nim-lang/Nim/issues/4195>`_)
-  - Fixed "Passing method to macro causes seg fault"
-    (`#1611 <https://github.com/nim-lang/Nim/issues/1611>`_)
-  - Fixed "Internal error with "discard quit""
-    (`#3532 <https://github.com/nim-lang/Nim/issues/3532>`_)
-  - Fixed "SIGSEGV when using object variant in compile time"
-    (`#4207 <https://github.com/nim-lang/Nim/issues/4207>`_)
-  - Fixed "formatSize has incorrect prefix"
-    (`#4198 <https://github.com/nim-lang/Nim/issues/4198>`_)
-  - Fixed "Add compiler parameter to generate output from source code filters"
-    (`#375 <https://github.com/nim-lang/Nim/issues/375>`_)
-  - Fixed "Add engineering notation to string formatting functions"
-    (`#4197 <https://github.com/nim-lang/Nim/issues/4197>`_)
-  - Fixed "Very minor error in json documentation"
-    (`#4255 <https://github.com/nim-lang/Nim/issues/4255>`_)
-  - Fixed "can't compile when checking if closure == nil"
-    (`#4186 <https://github.com/nim-lang/Nim/issues/4186>`_)
-  - Fixed "Strange code gen for procs returning arrays"
-    (`#2259 <https://github.com/nim-lang/Nim/issues/2259>`_)
-  - Fixed "asynchttpserver may consume unbounded memory reading headers"
-    (`#3847 <https://github.com/nim-lang/Nim/issues/3847>`_)
-
-  - Fixed "download page still implies master is default branch"
-    (`#4022 <https://github.com/nim-lang/Nim/issues/4022>`_)
-  - Fixed "Use standard compiler flags in build script"
-    (`#2128 <https://github.com/nim-lang/Nim/issues/2128>`_)
-  - Fixed "CentOS 6 (gcc-4.4.7) compilation failed (redefinition of typedef)"
-    (`#4272 <https://github.com/nim-lang/Nim/issues/4272>`_)
-  - Fixed "doc2 has issues with httpclient"
-    (`#4278 <https://github.com/nim-lang/Nim/issues/4278>`_)
-  - Fixed "tuples/tuple_with_nil fails without unsigned module"
-    (`#3579 <https://github.com/nim-lang/Nim/issues/3579>`_)
diff --git a/web/news/e023_version_0_14_2.rst b/web/news/e023_version_0_14_2.rst
deleted file mode 100644
index cbfe52713..000000000
--- a/web/news/e023_version_0_14_2.rst
+++ /dev/null
@@ -1,14 +0,0 @@
-Version 0.14.2 released
-=======================
-
-.. container:: metadata
-
-  Posted by Andreas Rumpf on 09/06/2016
-
-Version 0.14.2 is just a bugfix release that fixes the most pressing
-regressions. In particular, the ``tar.xz`` now supports documentation
-generation, and the Windows installers bundle the latest stable nimble
-release.
-
-The news about the 0.14.0 release are still relevant, so check them out
-`here <http://nim-lang.org/news/2016_06_07_version_0_14_0_released.html>`_.
diff --git a/web/news/e024_survey.rst b/web/news/e024_survey.rst
deleted file mode 100644
index 0b87577aa..000000000
--- a/web/news/e024_survey.rst
+++ /dev/null
@@ -1,29 +0,0 @@
-Launching the 2016 Nim Community Survey
-=======================================
-
-.. container:: metadata
-
-  Posted by Dominik Picheta on 23/06/2016
-
-We are proud to announce the official
-`2016 Nim Community Survey <http://goo.gl/forms/XJ3TPsaiIQe5HlTB2>`_! No matter
-whether you use Nim today, have used Nim previously, or never used Nim before;
-we want to know your opinions.
-Your feedback will help the Nim project understand its strengths and
-weaknesses, and to determine development priorities for the future.
-
-It shouldn't take you much longer than 5 to 10 minutes to complete this survey.
-Submissions will be accepted until around the 23rd of July, depending on the
-response rates. If you have any questions or feedback, please don't hesitate
-to get in touch with us via email at survey@nim-lang.org or on the
-`Nim Forum <http://forum.nim-lang.org>`_.
-
-We would appreciate your help in spreading the word about this survey. Share
-the above link on your social network feeds, with your colleagues and in
-other communities.
-
-Thank you to everyone that helped develop and test the survey! Once the
-submission period ends, the results will be shown here and publicised via
-Twitter, the Nim Forum and IRC.
-
-Thanks for your time!
diff --git a/web/news/e025_bountysource_update.rst b/web/news/e025_bountysource_update.rst
deleted file mode 100644
index 00ca7022e..000000000
--- a/web/news/e025_bountysource_update.rst
+++ /dev/null
@@ -1,73 +0,0 @@
-BountySource Update: The Road to v1.0
-=====================================
-
-.. container:: metadata
-
-  Cross posted by Dominik Picheta on 06/08/2016 from
-  `Update #4 on BountySource <https://salt.bountysource.com/teams/nim/updates/4-the-road-to-v1-0>`_
-
-We are now in the fourth month of the
-`Nim BountySource fundraiser <https://salt.bountysource.com/teams/nim>`_ and
-here is
-this month's update[1]. Once again this month, we have beat our previous
-donation record of $1280 having raised over $1600 over the course of July!
-That's now 4 months in a row that your monthly donations have been increasing.
-As always we are absolutely blown away by your contributions,
-`myself <https://github.com/dom96>`_ and the
-rest of the Nim team are extremely thankful for them. It's not only helping us
-pay for the necessary expenses (like for example the server that
-http://nim-lang.org runs on) but it also inspires us to keep going and to make
-Nim the best programming language that it can be.
-
-As mentioned in
-`last month's update <https://salt.bountysource.com/teams/nim/updates/3-engaging-with-our-community>`_, we have begun the process of engaging with
-the Nim community through a survey. This survey has now been open for more than
-a month and will be closing very soon (in about 2 days), so if you haven't
-answered it yet, now would be a perfect time to do so. You can find the survey
-here: http://nim-lang.org/survey.
-
-The survey itself has been designed for three types of people: Nim users,
-ex-Nim users and people who have never used Nim before. This means that you
-have no excuse not to answer it[2]. There are almost 700 submissions and after
-the survey is finalised, a blog post will be written with a thorough analysis.
-
-It is my hope that the survey analysis will give the Nim team a good idea of
-what needs to be implemented before version 1.0 can be released. Personally, I
-hope to make a thorough review of the standard library to ensure that it is
-ready for the "1.0 backwards compatibility lock"[3]. Although I myself have
-been very busy lately[4], `Araq <http://github.com/Araq>`_ has been working very hard to fix
-`High Priority <https://github.com/nim-lang/Nim/issues?q=is%3Aissue+is%3Aopen+label%3A%22High+Priority%22>`_
-issues, ahead of the 1.0 release. And as always, there has also been a
-`lot of pull requests <https://github.com/nim-lang/Nim/pulse>`_
-from a wide range of Nim users.
-
-Lastly, I would like to mention
-`Nim in Action <https://manning.com/books/nim-in-action?a_aid=niminaction&a_bid=78a27e81>`_ once again. I have recently
-finished the final chapter. All that remains now are corrections (based mainly
-on your feedback, huge thanks to everyone for reading!) and a final review.
-The book will then be put into production[5] with an estimated print date of
-around the 20th of January (as usual delays are very much possible). If you
-have not yet picked up the book, now would be a perfect time to do so. There
-is still time for you to give feedback about the book, and for me to improve it
-based on your remarks. It is not often that you can influence a book in this
-way, and it is my hope that you will help me make this book one of the best
-resources for learning Nim!
-
-As always, many thanks for reading and if you have any questions or feedback
-feel free to get in touch via email at contact@nim-lang.org or via
-`Twitter <https://twitter.com/nim_lang>`_.
-
-1 - These updates have so far been fairly regular and it is my hope to write
-at least one a month. Yep, I am patting myself on the back :)
-
-2 - I joke of course, don't worry about if you don't have the time :)
-
-3 - After version 1.0, it is typical for a piece of software (especially
-a programming language) to ensure that backwards compatibility is not broken
-between further minor versions until a new major version such as 2.0 is released.
-
-4 - Writing a book, moving to Switzerland to start a new job, and playing
-`Pokemon Go <https://pokemongostatus.org/>`_ has certainly kept me busy.
-
-5 - Basically the brilliant guys at `Manning <https://manning.com>`_ will
-process the book so that it looks good in a printed format.
diff --git a/web/news/e026_survey_results.rst b/web/news/e026_survey_results.rst
deleted file mode 100644
index 106dce0e4..000000000
--- a/web/news/e026_survey_results.rst
+++ /dev/null
@@ -1,699 +0,0 @@
-Nim Community Survey Results
-============================
-
-.. container:: metadata
-
-  Posted by Dominik Picheta on 3 September 2016
-
-We have recently closed the 2016 Nim Community Survey. I am happy to
-say that we have received exactly 790 responses, huge thanks go to the people
-that took the time to respond. We're incredibly thankful for this very valuable
-feedback.
-
-This survey was inspired in part by the
-`2016 State of Rust <https://blog.rust-lang.org/2016/06/30/State-of-Rust-Survey-2016.html>`_
-survey. You will note that many of the questions were modelled after
-Rust's survey. One of the reasons for doing this was to allow us to easily
-compare our results against the results obtained in the Rust survey. In
-addition, we of course also liked many of their questions.
-
-Our survey ran from the 23rd of June 2016 until the 8th of August 2016. The
-response numbers are impressive considering Nim's community size; at 790 they
-make up just over 25% of the Rust survey's responses.
-
-The goal of this survey was to primarily determine how our community is using
-Nim, in order to better understand how we should be improving it. In particular,
-we wanted to know what people feel is missing from Nim in the lead up to
-version 1.0. We have also asked our respondents about how well the Nim tools
-worked, the challenges of adopting Nim, the resources that they used to learn
-Nim and more.
-
-It is my hope that we will be able to run a similar survey in a years time,
-doing so should give us an idea of whether we are improving.
-With these general facts in mind, let's begin looking at specific questions.
-
-How did you find out about Nim?
--------------------------------
-
-The rationale for the first question was simple, we wanted to know where our
-respondents found out about Nim. This is an interesting question for us, as
-we do occassionally get users asking us why it took so long for them to hear
-about Nim. It allows us to see how effective each website is at spreading the
-word about Nim.
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/nim_found.png">
-    <img src="../assets/news/images/survey/nim_found.png" alt="How did you find out about Nim?" style="width:100%"/>
-  </a>
-
-The majority of our respondents found Nim via Reddit, HackerNews or a search
-engine such as Google. These results are not altogether surprising. There were
-also a lot of "Other" responses, some of which were a bit more
-interesting. These included multiple mentions of habrahabr.ru, Dr. Dobb's,
-and lobste.rs.
-
-Do you use Nim?
----------------
-
-Just like the Rust survey creators, we wanted to ensure that our survey was
-open to both Nim users as well people who never used Nim. In addition to
-those two groups, we have also included a third group of people: ex-Nim
-users. All three are interesting, for many different reasons.
-Nim users can tell us how they are using Nim and also how Nim's
-tooling can improve. Ex-Nim users give us an
-idea of why they stopped using Nim. Finally, respondents who never used Nim
-can tell us the reasons for not adopting it.
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/do_you_use_nim.png">
-    <img src="../assets/news/images/survey/do_you_use_nim.png" alt="Do you use Nim?" style="width:100%"/>
-  </a>
-
-It's nice to see that we have such a good range of respondents. The Rust survey
-had a much larger number of Rust users amongst their respondents, with
-no distinction between users that never used Rust and users that stopped using
-Rust.
-
-Should we consider your answers to be invalid?
-----------------------------------------------
-
-This was something I thought would be interesting to have, after I saw it
-being used in another survey. While it does pinpoint possibly
-invalid respondents, I have opted against filtering those out. Mainly because
-that would require re-creating each of the charts generated by Google Forms
-manually.
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/reliability.png">
-    <img src="../assets/news/images/survey/reliability.png" alt="Should we consider your answers to be invalid?" style="width:100%"/>
-  </a>
-
-According to the responses to this question, around 94% of our responses
-can be considered reliable.
-
-Nim users
----------
-
-The following questions were answered only by the 38.9% of our respondents
-who identified themselves as Nim users.
-
-How long have you been using Nim?
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/nim_time.png">
-    <img src="../assets/news/images/survey/nim_time.png" alt="How long have you been using Nim?" style="width:100%"/>
-  </a>
-
-A large proportion of our Nim users were new. This is good news as it means that
-our community is growing, with a large proportion of new Nim users that could
-become long-term Nimians. In total, more than 35% of Nim users can be considered
-new having used Nim for less than 3 months. With 18% of Nim users that can
-be considered very new having used Nim for less than a month.
-This could suggest that 18% of our users have only just found out about Nim in
-the last week or so and have not yet got the chance to use it extensively.
-
-The high percentages of long term Nim users are encouraging.
-They suggest
-that many users are continuing to use Nim after making it through the first
-few months. The sharp drop at 7-9 months is interesting, but may simply be
-due to the fact that there were fewer newcomers during that period, or it
-could be because our respondents are more likely to estimate that they have
-been using Nim for a year or half a year rather than the awkward 7-9 months.
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/nim_time_rust.png">
-    <img src="../assets/news/images/survey/nim_time_rust.png" alt="Time using Nim and Rust" style="width:100%"/>
-  </a>
-
-The results for Nim and Rust are actually remarkably similar. They both show a
-drop at 7-9 months, although Rust's isn't as dramatic. Nim on the other hand
-has a significantly higher percentage of new Nim users.
-
-Do you use Nim at work?
-~~~~~~~~~~~~~~~~~~~~~~~
-
-An important aspect of a language's adoption is whether it is being used for
-"real" work. We wanted to know how many people are using Nim in their day
-jobs and under what circumstances it is used.
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/nim_at_work.png">
-    <img src="../assets/news/images/survey/nim_at_work.png" alt="Do you use Nim at work?" style="width:100%"/>
-  </a>
-
-While a vast majority of our users are not using Nim at work, more than 25%
-of them are. It's encouraging to see such a high number already, even before
-we have released version 1.0. In fact, this percentage is likely close to 30%,
-because many of the "Other" responses mention using Nim for the likes of
-internal tools or small scripts to help with the respondent's work.
-
-.. raw::html
-
-  <a href="https://blog.rust-lang.org/images/2016-06-Survey/rust_at_work.png">
-    <img src="https://blog.rust-lang.org/images/2016-06-Survey/rust_at_work.png" alt="Do you use Rust at work?" style="width:100%"/>
-  </a>
-
-Interestingly, a larger percentage of Nim users are using Nim at work than
-Rust users. The sample sizes are of course vastly different, but it's still an
-interesting result. Combined, nearly 1/5th of Rust users are using Rust
-commercially whereas more than a quarter of Nim users are using Nim
-commercially.
-
-Approximately how large are all the Nim projects that you work on?
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Finding out how large the Nim projects worked on by Nim users are is also
-very valuable.
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/project_size.png">
-    <img src="../assets/news/images/survey/project_size.png" alt="Nim project size for all users" style="width:100%"/>
-  </a>
-
-This shows us that currently Nim is primarily being used for small scripts and
-applications, with nearly 60% of the projects consisting of less than 1,000
-lines of code. This makes sense as many of our users are not using Nim
-professionally, but are doing so in their spare time.
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/project_size_work.png">
-    <img src="../assets/news/images/survey/project_size_work.png" alt="Nim project size for work users" style="width:100%"/>
-  </a>
-
-The numbers for part-time and full-time work users of Nim tell a different
-story. Over 70% of the projects written by full-time users are between 10,001
-and 100,000 lines of code. Part-time users show a slightly different trend,
-with many more small projects, the majority being between 1,000 and
-10,000 lines of code.
-
-Overall it's good to see that there is a few large projects out there which are
-composed of more than 100,000 lines of code. We expect to see the amount of
-large projects to grow with time, especially with version 1.0 on the way.
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/project_size_nim_rust.png">
-    <img src="../assets/news/images/survey/project_size_nim_rust.png" alt="Nim project size for work users (Nim vs. Rust)" style="width:100%"/>
-  </a>
-
-In comparison to Rust the proportion of project sizes for full-time users is
-vastly different. This is likely due to our small sample size. Project sizes for
-part-time users between Rust and Nim are somewhat similar, with differences of
-around 10% for each project size.
-
-Do you plan to try to use Nim at work?
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/planning_to_use_at_work.png">
-    <img src="../assets/news/images/survey/planning_to_use_at_work.png" alt="Planning to use Nim at work?" style="width:100%"/>
-  </a>
-
-It's also encouraging to see that over 50% of Nim users are planning to use
-Nim at work! This is slightly more than Rust's 40% and should help Nim's
-adoption into even more areas.
-
-Nim and its tools
-~~~~~~~~~~~~~~~~~
-
-In this section of the survey, we wanted to find out the tools that Nim
-users are utilising when developing Nim applications.
-
-What editor(s) do you use when writing Nim?
-___________________________________________
-
-Programmers are very specific when it comes to their editor of choice, because
-of that it's good to know which editor is most popular among our community.
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/editors.png">
-    <img src="../assets/news/images/survey/editors.png" alt="Editors used by Nim users" style="width:100%"/>
-  </a>
-
-Looks like Vim is the winner with almost 30%. Followed by Sublime Text and
-Emacs. Aporia, the Nim IDE, gets a respectable 15.5%. There was
-also more than
-17% of answers which included "Other" editors, such as: Notepad++, Geany, gedit,
-and Kate.
-
-What operating system(s) do you compile for and run your Nim projects on?
-_________________________________________________________________________
-
-This question gave us information about the most popular target operating
-systems, as well as some of the more obscure ones. We have asked this question
-to find out the platforms on which Nim applications run on most frequently.
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/target_os.png">
-    <img src="../assets/news/images/survey/target_os.png" alt="Target operating systems" style="width:100%"/>
-  </a>
-
-This question allowed multiple choices, so each percentage is out of the total
-number of respondents for this question. For example, 80.7% of the
-respondents selected "Linux" but only 26.6% selected OS X.
-
-This makes Linux by far the most popular target for Nim applications.
-Some "Other" targets included: BSD (OpenBSD, FreeBSD), iOS, Android, and
-JavaScript.
-It's great to see Nim being used on such a wide variety of platforms.
-
-What operating system(s) do you develop Nim projects on?
-________________________________________________________
-
-With this question, we wanted to know what operating systems are used for
-development.
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/dev_os.png">
-    <img src="../assets/news/images/survey/dev_os.png" alt="Development operating systems" style="width:100%"/>
-  </a>
-
-This question also allowed multiple choices and ended up with very similar
-results.
-
-You can see that Linux is also the most popular developmental
-platform for Nim. But it's more popular as a target platform.
-
-Which version(s) of Nim do you use for your applications?
-_________________________________________________________
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/nim_versions.png">
-    <img src="../assets/news/images/survey/nim_versions.png" alt="Version use" style="width:100%"/>
-  </a>
-
-At the time of this survey, version 0.14.2 was the latest stable release.
-It's no wonder that it is the most commonly used release of Nim. It's good to
-see that the older versions are not used as often. The high use of ``Git HEAD (devel)``
-(nightly builds) isn't surprising, Nim is still evolving rapidly and our
-release schedule is not regular or frequent.
-
-Once we go past the 1.0 release, we expect to see much less use of the unstable
-``devel`` branch.
-
-Has upgrading to a new version of the Nim compiler broken your code?
-____________________________________________________________________
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/breakage.png">
-    <img src="../assets/news/images/survey/breakage.png" alt="Breakage" style="width:100%"/>
-  </a>
-
-Despite the unstable nature of Nim in the lead up to version 1.0, whenever
-we make breaking changes we do our best to deprecate things and ensure that
-old code continues to work for our users. Of course sometimes this is not
-possible and other times it is simply easier to add a breaking change.
-
-This question was asked to determine how much our user base is affected by
-breaking changes between Nim versions. We decided to have three possible
-answers for this question in order to give us an idea how frequent the
-breakage was.
-
-It's incredible to see that over 50% of our users have not experienced any
-breakage after upgrading. We expect this number to increase significantly
-after version 1.0 is released. Of the users that did experience breakage,
-over 80% of them said that it was a rare occurrence.
-
-In comparison to Rust, our results show that there was a higher percentage of
-users experiencing breakage as a result of an upgrade. This is to be expected,
-because Nim is still in its pre-1.0 period, whereas Rust 1.0 has been released
-over a year ago now.
-
-Unfortunately while we are still in this pre-1.0 period, releases will likely
-introduce breaking changes as we refine certain aspects of Nim such as its
-standard library, so the number of users experiencing breaking changes may
-increase.
-
-If so, how much work did it take to fix it?
-___________________________________________
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/difficulty_fixing_breakage.png">
-    <img src="../assets/news/images/survey/difficulty_fixing_breakage.png" alt="difficulty fixing breakage" style="width:100%"/>
-  </a>
-
-Thankfully most of the breakage experienced by Nim users was very easy to fix.
-
-
-If you used Nimble, do you like it?
-___________________________________
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/nimble_opinion.png">
-    <img src="../assets/news/images/survey/nimble_opinion.png" alt="Do you like Nimble?" style="width:100%"/>
-  </a>
-
-Nimble is the Nim package manager, a tool that is very important in Nim's
-ecosystem as it allows developers to easily install dependencies for their
-software.
-
-The majority of respondents rated it as a 4, showing us that the majority does
-like Nimble. With over 55% rating it a 4 or 5. This percentage isn't as
-overwhelming as the 94.1% of users that rated Cargo a 4 or 5 in the Rust
-survey. Based on these results I think that we definitely need to do a
-better job with Nimble.
-
-In our next survey, it might be a good idea to ask more questions about Nimble
-to determine how exactly it can be improved.
-
-What aspects of Nim do you find most appealing?
-_______________________________________________
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/nim_appeal.png">
-    <img src="../assets/news/images/survey/nim_appeal.png" alt="What aspects of Nim do you find most appealing?" style="width:100%"/>
-  </a>
-
-We were interested to know the features of Nim that appeal most to our users.
-More than 80% of our respondents selected "Execution Speed" as one of the
-features that appeal to them. With "Development Speed" and "Readability"
-tying for second place and "Metaprogramming" at third place.
-
-The options given to our respondents are rather predictable,
-they do show us which of these features have the highest appeal though.
-What's more interesting are the "Other" answers.
-
-By far the most popular "Other" answer was related to Nim's compilation to C.
-Many users mentioned that they like how easy it is to interface with C
-libraries and the great portability that compiling to C offers.
-
-What aspects of Nim do you find most displeasing?
-_________________________________________________
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/nim_displeasing.png">
-    <img src="../assets/news/images/survey/nim_displeasing.png" alt="What aspects of Nim do you find most displeasing?" style="width:100%"/>
-  </a>
-
-It was only natural to ask this question. The results are almost perfectly
-opposite to the previous question's answers, with almost 50% of respondents
-selecting "Debugging Tools"
-as the most displeasing aspect of Nim. With "Documentation" and "Testing Tools"
-in second and third place respectively. There is also a much larger number of
-"Other" answers to this question.
-
-The "Other" answers for this question vary a lot. Here is a selection of
-them, ordered by frequency:
-
-* Small community size.
-* Lack of in-depth tutorials.
-* Quality of error messages.
-* Forward declarations and no cyclic imports.
-* Bugs in the standard library.
-* No good IDE.
-* No REPL.
-* No major version.
-* Bugs in the compiler.
-* Lack of libraries.
-* Difficulty installing on Windows.
-* Non-intuitive semantics of various constructs.
-* Lack of immutable collections.
-* Async/await not being production ready.
-* Lack of shared collections for threads.
-* No Haxe target.
-* Memory safety.
-
-We hope that we can improve these things with time. Many of these issues are
-already being worked on, including the removal of the need for forward
-declarations. Some of these issues like our small community size are difficult
-to fix, but we will nonetheless do our best.
-
-
-Previous Nim users
-~~~~~~~~~~~~~~~~~~
-
-For users that have used Nim before but decided against using it, we asked just
-one specific question. The proportion of our respondents that answered it
-was 24%.
-
-Why did you stop using Nim?
-___________________________
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/ex_nim.png">
-    <img src="../assets/news/images/survey/ex_nim.png" alt="I stopped using Nim because..." style="width:100%"/>
-  </a>
-
-Again, this question got a lot of "Other" answers. Apart from that, the
-most popular reason for leaving Nim is that it is not stable. Followed by the
-a lack of needed libraries and packages and the instability of the
-standard library.
-
-* Lack of IDE support.
-* Style insensitive.
-* Documentation.
-* Dislike the syntax.
-* Community is too small.
-* Missing language features (for example RAII).
-* No opportunities to use it at work.
-* Messy standard library.
-
-The first item, "Lack of IDE support", was mentioned by multiple respondents.
-In the future we should look into ensuring that major IDEs have plugins which
-enable easy Nim development.
-
-Based on some of the "Other" answers, it seems that many of the respondents
-have not used Nim for very long, for example many respondents complained about
-installation issues which they would have run into before getting a chance to
-use Nim. Because of this I would consider them not
-ex-Nim users but developers that have not had a chance to try Nim fully.
-Next time we should also ask how long the respondent has used Nim for to get a
-better idea of whether they had a chance to use Nim for extended periods of
-time.
-
-Non-Nim users
-~~~~~~~~~~~~~
-
-We also wanted to know the reasons why developers decided against using Nim.
-
-Why do you not use Nim?
-_______________________
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/non_user.png">
-    <img src="../assets/news/images/survey/non_user.png" alt="I don't use Nim because..." style="width:100%"/>
-  </a>
-
-The most common reason that people have for not using Nim is that it is
-not yet ready for production. Thankfully this will improve with time.
-IDE support is also a prominent factor just as we've seen in previous results.
-
-There is also a lot of "Other" answers, let's have a look at a selection of
-them. Some of the most prominent ones, in order of frequency, include:
-
-* No time to use/learn it
-* Syntax
-* Documentation is incomplete
-* Garbage Collection
-* Prefer functional paradigm
-* Small community
-* Style insensitivity/Case insensitivity
-
-One respondent made a very good suggestion: they said that the
-"Do you use Nim?" question should have included "No, but I intend to" as
-an answer. Definitely something we will do in the next survey. Indeed, many
-respondents mentioned that they were planning on trying out Nim but that they
-just have no time to do so, this is very encouraging!
-
-Learning Resources
-~~~~~~~~~~~~~~~~~~
-
-We wanted to get an idea of how Nim users are learning Nim. Every respondent
-answered this question, no matter what they answered for the "Do you use Nim?"
-question.
-
-Which learning resources, if any, did you use to learn Nim?
-___________________________________________________________
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/learning_resources.png">
-    <img src="../assets/news/images/survey/learning_resources.png" alt="learning resources" style="width:100%"/>
-  </a>
-
-The idea behind this question was to understand which learning resources
-were most popular among our user base. The
-`Nim tutorial <http://nim-lang.org/docs/tut1.html>`_ is by far the most
-popular. In previous questions, we saw respondents mentioning that the Nim
-tutorial does not go into enough detail about Nim. Thanks to this information
-we can come to the conclusion that the tutorial needs to be improved
-significantly to make sure that it gives our users the necessary information
-to use Nim effectively.
-
-Indeed, many users also use the
-`Nim manual <http://nim-lang.org/docs/manual.html>`_ to learn Nim.
-This manual has been
-written as a specification and so is not ideal for teaching Nim. Many of
-the concepts in the Nim manual need to be explained in a lot more detail in
-the Nim tutorial.
-
-Of course, it's exciting to see our respondents using other materials to learn
-Nim. In particular I am excited to see that over 15% of the respondents have
-used
-`Nim in Action <https://manning.com/books/nim-in-action?a_aid=niminaction&a_bid=78a27e81>`_
-to learn Nim. I expect that more and more users will pick up the book after it
-is fully published.
-
-Nim in Action
-_____________
-
-As the author of
-`Nim in Action <https://manning.com/books/nim-in-action?a_aid=niminaction&a_bid=78a27e81>`_,
-I wanted to get some statistics surrounding
-my book. With this in mind, I have created some questions relating to it.
-
-Have you read Nim in Action?
-____________________________
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/book.png">
-    <img src="../assets/news/images/survey/book.png" alt="Have you read Nim in Action?" style="width:100%"/>
-  </a>
-
-It's good to see that over 50% of respondents have read the book or are at least
-planning to read it. Keep in mind that this question was answered by all
-respondents, not just Nim users.
-
-.. container:: standout
-
-  Are you interested in purchasing a copy of
-  `Nim in Action <https://manning.com/books/nim-in-action?a_aid=niminaction&a_bid=78a27e81>`_?
-  If so, you can use code ``wm090416lt`` to get 50% off the printed book today only!
-  If you purchase it now you will get access to an early access copy of
-  Nim in Action in eBook form and will be able to take part in the development
-  of this book.
-
-Did you enjoy Nim in Action?
-____________________________
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/book_opinion.png">
-    <img src="../assets/news/images/survey/book_opinion.png" alt="Did you enjoy Nim in Action?" style="width:100%"/>
-  </a>
-
-Of the people that read Nim in Action it's nice to see that almost 70% have
-enjoyed it.
-
-Nim's future
-~~~~~~~~~~~~
-
-What improvements are needed before Nim v1.0 can be released?
-_____________________________________________________________
-
-We were interested to know what our users believe is needed before
-Nim version 1.0 can be released.
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/10_needs.png">
-    <img src="../assets/news/images/survey/10_needs.png" alt="What is needed before 1.0 can be released?" style="width:100%"/>
-  </a>
-
-It appears that the standard library is the biggest concern. With more than half
-of all respondents selecting "The standard library needs to reviewed and
-any problems with it fixed". This is in fact something we are already planning
-to address, so it's good to see that the majority agrees with us.
-
-A large proportion of users also believes that the language is great as-is
-and that we should focus on stabilising the compiler. This somewhat contradicts
-the majority. But perhaps most of them thought that "The language" excludes the
-standard library.
-
-For this question, we decided to give our respondents a dedicated place to
-give general feedback about what they feel is needed before v1.0 can be
-released. We received over 200 responses to that. Many of these responses
-reflect what we have already seen: that the documentation needs to improve,
-that we need a good Nim IDE, stability for experimental features such as
-concepts, the standard library needs to be cleaned up.
-
-Unfortunately many respondents used this question to say what needs to be fixed
-in Nim in general, not what is definitely necessary before 1.0 can be released.
-
-Community demographics
-~~~~~~~~~~~~~~~~~~~~~~
-
-What domain do you work in currently?
-_____________________________________
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/domains.png">
-    <img src="../assets/news/images/survey/domains.png" alt="Work domains" style="width:100%"/>
-  </a>
-
-
-Nim users are working in a wide variety of domains. It is encouraging to see
-people from so many different backgrounds taking part in this survey.
-
-What programming languages are you most comfortable with?
-_________________________________________________________
-
-
-.. raw::html
-
-  <a href="../assets/news/images/survey/languages.png">
-    <img src="../assets/news/images/survey/languages.png" alt="Programming languages" style="width:100%"/>
-  </a>
-
-Python and C are the top two programming languages that our respondents are
-most comfortable with. This is not altogether surprising.
-
-Last words
-~~~~~~~~~~
-
-At the end of the survey we gave our respondents a chance to speak their mind
-about anything they wish, with a simple question: "Anything else you'd like
-to tell us?"
-
-There was a lot of great feedback given in this question from people who
-obviously really care deeply about Nim. There is too much to outline here,
-but rest assurred that we will take it all into account and do our best to
-act on it.
-
-In addition to feedback, we were also overwhelmed by the amount of positive
-comments in the answers to this
-question. There was a lot of support from the community thanking us for our
-work and determination.
-
-I'll let some quotes speak for themselves:
-
-.. raw::html
-
-  <blockquote>You rock, seriously.</blockquote>
-  <blockquote>Nim rocks! Keep it up! Thank you very much!</blockquote>
-  <blockquote>You've made great progress on the language without any corporate backing, that is amazing. I wish Nim becomes one of the top used languages in a few years.</blockquote>
-  <blockquote>Nim is elegant and wonderful! Keep at it!</blockquote>
-
-Our community is truly brilliant. We thank each and every one of you for
-filling out this survey and hope that you will help us tackle some of the
-challenges that face Nim.
-
-This survey was a good place to give us feedback, but please don't wait for
-the next one. We are always looking to hear more from you and we hope that you
-will participate in discussions relating to this survey as well the future
-of Nim.
-
-Thanks for reading, and have a good day!
diff --git a/web/news/e027_version_0_15_0.rst b/web/news/e027_version_0_15_0.rst
deleted file mode 100644
index 47c02e9e4..000000000
--- a/web/news/e027_version_0_15_0.rst
+++ /dev/null
@@ -1,517 +0,0 @@
-Version 0.15.0 released
-=======================
-
-.. container:: metadata
-
-  Posted by Dominik Picheta and Andreas Rumpf on 30/09/2016
-
-We're happy to announce that the latest release of Nim, version 0.15.0, is now
-available!
-
-As always, you can grab the latest version from the
-`downloads page <http://nim-lang.org/download.html>`_.
-
-This release includes almost 180 bug fixes and improvements. To see a full list
-of changes, take a look at the detailed changelog
-`below <#changelog>`_.
-
-Some of the most significant changes in this release include: improvements to
-the documentation, addition of a new ``multisync`` macro, and a new
-``HttpClient`` implementation.
-
-Documentation
-~~~~~~~~~~~~~
-
-All pages in the documentation now contain a search box and a drop down to
-select how procedures should be sorted. This allows you to search for
-procedures, types, macros and more from any documentation page.
-
-.. raw::html
-
-  <a href="../assets/news/images/0.15.0/doc_search.gif">
-    <img src="../assets/news/images/0.15.0/doc_search.gif" alt="Doc search" style="width:100%"/>
-  </a>
-
-Sorting the procedures by type shows a more natural table of contents. This
-should also help you to find procedures and other identifiers.
-
-.. raw::html
-
-  <a href="../assets/news/images/0.15.0/doc_sort.gif">
-    <img src="../assets/news/images/0.15.0/doc_sort.gif" alt="Doc sort" style="width:100%"/>
-  </a>
-
-Multisync macro
-~~~~~~~~~~~~~~~
-
-The ``multisync`` macro was implemented to enable you to define both
-synchronous and asynchronous IO procedures without having to duplicate a
-lot of code.
-
-As an example, consider the ``recvTwice`` procedure below:
-
-.. code-block:: nim
-  proc recvTwice(socket: Socket | AsyncSocket): Future[string] {.multisync.} =
-    result = ""
-    result.add(await socket.recv(25))
-    result.add(await socket.recv(20))
-
-The ``multisync`` macro will transform this procedure into the following:
-
-.. code-block:: nim
-  proc recvTwice(socket: Socket): string =
-    result = ""
-    result.add(socket.recv(25))
-    result.add(socket.recv(20))
-
-  proc recvTwice(socket: AsyncSocket): Future[string] {.async.} =
-    result = ""
-    result.add(await socket.recv(25))
-    result.add(await socket.recv(20))
-
-Allowing you to use ``recvTwice`` with both synchronous and asynchronous sockets.
-
-HttpClient
-~~~~~~~~~~
-
-Many of the ``httpclient`` module's procedures have been deprecated in
-favour of a new implementation using the ``multisync`` macro. There are now
-two types: ``HttpClient`` and ``AsyncHttpClient``. Both of these implement the
-same procedures and functionality, the only difference is timeout support and
-whether they are blocking or not.
-
-See the `httpclient <http://nim-lang.org/docs/httpclient.html>`_ module
-documentation for more information.
-
-Changelog
-~~~~~~~~~
-
-Changes affecting backwards compatibility
------------------------------------------
-
-- The ``json`` module now uses an ``OrderedTable`` rather than a ``Table``
-  for JSON objects.
-
-- The ``split`` `(doc) <http://nim-lang.org/docs/strutils.html#split,string,set[char],int>`_
-  procedure in the ``strutils`` module (with a delimiter of type
-  ``set[char]``) no longer strips and splits characters out of the target string
-  by the entire set of characters. Instead, it now behaves in a
-  similar fashion to ``split`` with ``string`` and ``char``
-  delimiters. Use ``splitWhitespace`` to get the old behaviour.
-
-- The command invocation syntax will soon apply to open brackets
-  and curlies too. This means that code like ``a [i]`` will be
-  interpreted as ``a([i])`` and not as ``a[i]`` anymore. Likewise
-  ``f (a, b)`` means that the tuple ``(a, b)`` is passed to ``f``.
-  The compiler produces a warning for ``a [i]``::
-
-    Warning: a [b] will be parsed as command syntax; spacing is deprecated
-
-  See `Issue #3898 <https://github.com/nim-lang/Nim/issues/3898>`_ for the
-  relevant discussion.
-
-- Overloading the special operators ``.``, ``.()``, ``.=``, ``()`` now
-  needs to be enabled via the ``{.experimental.}`` pragma.
-
-- ``immediate`` templates and macros are now deprecated.
-  Use ``untyped`` `(doc) <http://nim-lang.org/docs/manual.html#templates-typed-vs-untyped-parameters>`_
-  parameters instead.
-
-- The metatype ``expr`` is deprecated. Use ``untyped``
-  `(doc) <http://nim-lang.org/docs/manual.html#templates-typed-vs-untyped-parameters>`_ instead.
-
-- The metatype ``stmt`` is deprecated. Use ``typed``
-  `(doc) <http://nim-lang.org/docs/manual.html#templates-typed-vs-untyped-parameters>`_ instead.
-
-- The compiler is now more picky when it comes to ``tuple`` types. The
-  following code used to compile, now it's rejected:
-
-.. code-block:: nim
-
-  import tables
-  var rocketaims = initOrderedTable[string, Table[tuple[k: int8, v: int8], int64]]()
-  rocketaims["hi"] = {(-1.int8, 0.int8): 0.int64}.toTable()
-
-Instead be consistent in your tuple usage and use tuple names for named tuples:
-
-.. code-block:: nim
-
-  import tables
-  var rocketaims = initOrderedTable[string, Table[tuple[k: int8, v: int8], int64]]()
-  rocketaims["hi"] = {(k: -1.int8, v: 0.int8): 0.int64}.toTable()
-
-- Now when you compile console applications for Windows, console output
-  encoding is automatically set to UTF-8.
-
-- Unhandled exceptions in JavaScript are now thrown regardless of whether
-  ``noUnhandledHandler`` is defined. But the stack traces should be much more
-  readable now.
-
-- In JavaScript, the ``system.alert`` procedure has been deprecated.
-  Use ``dom.alert`` instead.
-
-- De-deprecated ``re.nim`` because there is too much code using it
-  and it got the basic API right.
-
-- The type of ``headers`` field in the ``AsyncHttpClient`` type
-  `(doc) <http://nim-lang.org/docs/httpclient.html#AsyncHttpClient>`_
-  has been changed
-  from a string table to the specialised ``HttpHeaders`` type.
-
-- The ``httpclient.request``
-  `(doc) <http://nim-lang.org/docs/httpclient.html#request,AsyncHttpClient,string,string,string>`_
-  procedure which takes the ``httpMethod`` as a string
-  value no longer requires it to be prefixed with ``"http"``
-  (or similar).
-
-- Converting a ``HttpMethod``
-  `(doc) <nim-lang.org/docs/httpcore.html#HttpMethod>`_
-  value to a string using the ``$`` operator will
-  give string values without the ``"Http"`` prefix now.
-
-- The ``Request``
-  `(doc) <http://nim-lang.org/docs/asynchttpserver.html#Request>`_
-  object defined in the ``asynchttpserver`` module now uses
-  the ``HttpMethod`` type for the request method.
-
-Library Additions
------------------
-
-- Added ``readHeaderRow`` and ``rowEntry`` to the ``parsecsv``
-  `(doc) <http://nim-lang.org/docs/parsecsv.html>`_ module
-  to provide
-  a lightweight alternative to python's ``csv.DictReader``.
-
-- Added ``setStdIoUnbuffered`` proc to the ``system`` module to enable
-  unbuffered I/O.
-
-- Added ``center`` and ``rsplit`` to the ``strutils``
-  `(doc) <http://nim-lang.org/docs/strutils.html>`_ module
-  to provide similar Python functionality for Nim's strings.
-
-- Added ``isTitle``, ``title``, ``swapCase``, ``isUpper``, ``toUpper``,
-  ``isLower``, ``toLower``, ``isAlpha``, ``isSpace``, and ``capitalize``
-  to the ``unicode.nim``
-  `(doc) <http://nim-lang.org/docs/unicode.html>`_ module
-  to provide unicode aware case manipulation and case
-  testing.
-
-- Added a new module ``strmisc``
-  `(doc) <http://nim-lang.org/docs/strmisc.html>`_
-  to hold uncommon string
-  operations. Currently contains ``partition``, ``rpartition``
-  and ``expandTabs``.
-
-- Split out ``walkFiles`` in the ``os``
-  `(doc) <http://nim-lang.org/docs/os.html>`_ module to three separate procs
-  in order to make a clear distinction of functionality. ``walkPattern`` iterates
-  over both files and directories, while ``walkFiles`` now only iterates
-  over files and ``walkDirs`` only iterates over directories.
-
-- Added a synchronous ``HttpClient`` in the ``httpclient``
-  `(doc) <http://nim-lang.org/docs/httpclient.html>`_
-  module. The old
-  ``get``, ``post`` and similar procedures are now deprecated in favour of it.
-
-- Added a new macro called ``multisync`` allowing you to write procedures for
-  synchronous and asynchronous sockets with no duplication.
-
-- The ``async`` macro will now complete ``FutureVar[T]`` parameters
-  automatically unless they have been completed already.
-
-Tool Additions
---------------
-
-- The documentation is now searchable and sortable by type.
-- Pragmas are now hidden by default in the documentation to reduce noise.
-- Edit links are now present in the documentation.
-
-
-Compiler Additions
-------------------
-
-- The ``-d/--define`` flag can now optionally take a value to be used
-  by code at compile time.
-  `(doc) <http://nim-lang.org/docs/manual.html#implementation-specific-pragmas-compile-time-define-pragmas>`_
-
-Nimscript Additions
--------------------
-
-- It's possible to enable and disable specific hints and warnings in
-  Nimscript via the ``warning`` and ``hint`` procedures.
-
-- Nimscript exports  a proc named ``patchFile`` which can be used to
-  patch modules or include files for different Nimble packages, including
-  the ``stdlib`` package.
-
-Language Additions
-------------------
-
-- Added ``{.intdefine.}`` and ``{.strdefine.}`` macros to make use of
-  (optional) compile time defines.
-  `(doc) <http://nim-lang.org/docs/manual.html#implementation-specific-pragmas-compile-time-define-pragmas>`_
-
-- If the first statement is an ``import system`` statement then ``system``
-  is not imported implicitly anymore. This allows for code like
-  ``import system except echo`` or ``from system import nil``.
-
-Bugfixes
---------
-
-The list below has been generated based on the commits in Nim's git
-repository. As such it lists only the issues which have been closed
-via a commit, for a full list see
-`this link on Github <https://github.com/nim-lang/Nim/issues?utf8=%E2%9C%93&q=is%3Aissue+closed%3A%222016-06-22+..+2016-09-30%22+>`_.
-
-- Fixed "RFC: should startsWith and endsWith work with characters?"
-  (`#4252 <https://github.com/nim-lang/Nim/issues/4252>`_)
-
-- Fixed "Feature request: unbuffered I/O"
-  (`#2146 <https://github.com/nim-lang/Nim/issues/2146>`_)
-- Fixed "clear() not implemented for CountTableRef"
-  (`#4325 <https://github.com/nim-lang/Nim/issues/4325>`_)
-- Fixed "Cannot close file opened async"
-  (`#4334 <https://github.com/nim-lang/Nim/issues/4334>`_)
-- Fixed "Feature Request: IDNA support"
-  (`#3045 <https://github.com/nim-lang/Nim/issues/3045>`_)
-- Fixed "Async: wrong behavior of boolean operations on futures"
-  (`#4333 <https://github.com/nim-lang/Nim/issues/4333>`_)
-- Fixed "os.walkFiles yields directories"
-  (`#4280 <https://github.com/nim-lang/Nim/issues/4280>`_)
-- Fixed "Fix #4392 and progress on #4170"
-  (`#4393 <https://github.com/nim-lang/Nim/issues/4393>`_)
-- Fixed "Await unable to wait futures from objects fields"
-  (`#4390 <https://github.com/nim-lang/Nim/issues/4390>`_)
-- Fixed "TMP variable name generation should be more stable"
-  (`#4364 <https://github.com/nim-lang/Nim/issues/4364>`_)
-- Fixed "nativesockets doesn't compile for Android 4.x (API v19 or older) because of gethostbyaddr"
-  (`#4376 <https://github.com/nim-lang/Nim/issues/4376>`_)
-- Fixed "no generic parameters allowed for ref"
-  (`#4395 <https://github.com/nim-lang/Nim/issues/4395>`_)
-- Fixed "split proc in strutils inconsistent for set[char]"
-  (`#4305 <https://github.com/nim-lang/Nim/issues/4305>`_)
-- Fixed "Problem with sets in devel"
-  (`#4412 <https://github.com/nim-lang/Nim/issues/4412>`_)
-- Fixed "Compiler crash when using seq[PNimrodNode] in macros"
-  (`#537 <https://github.com/nim-lang/Nim/issues/537>`_)
-- Fixed "ospaths should be marked for nimscript use only"
-  (`#4249 <https://github.com/nim-lang/Nim/issues/4249>`_)
-- Fixed "Repeated deepCopy() on a recursive data structure eventually crashes"
-  (`#4340 <https://github.com/nim-lang/Nim/issues/4340>`_)
-- Fixed "Analyzing destructor"
-  (`#4371 <https://github.com/nim-lang/Nim/issues/4371>`_)
-- Fixed "getType does not work anymore on a typedesc"
-  (`#4462 <https://github.com/nim-lang/Nim/issues/4462>`_)
-- Fixed "Error in rendering empty JSON array"
-  (`#4399 <https://github.com/nim-lang/Nim/issues/4399>`_)
-- Fixed "Segmentation fault when using async pragma on generic procs"
-  (`#2377 <https://github.com/nim-lang/Nim/issues/2377>`_)
-- Fixed "Forwarding does not work for generics,  | produces an implicit generic"
-  (`#3055 <https://github.com/nim-lang/Nim/issues/3055>`_)
-- Fixed "Inside a macro, the length of the `seq` data inside a `queue` does not increase and crashes"
-  (`#4422 <https://github.com/nim-lang/Nim/issues/4422>`_)
-- Fixed "compiler sigsegv while processing varargs"
-  (`#4475 <https://github.com/nim-lang/Nim/issues/4475>`_)
-- Fixed "JS codegen - strings are assigned by reference"
-  (`#4471 <https://github.com/nim-lang/Nim/issues/4471>`_)
-- Fixed "when statement doesn't verify syntax"
-  (`#4301 <https://github.com/nim-lang/Nim/issues/4301>`_)
-- Fixed ".this pragma doesn't work with .async procs"
-  (`#4358 <https://github.com/nim-lang/Nim/issues/4358>`_)
-- Fixed "type foo = range(...) crashes compiler"
-  (`#4429 <https://github.com/nim-lang/Nim/issues/4429>`_)
-- Fixed "Compiler crash"
-  (`#2730 <https://github.com/nim-lang/Nim/issues/2730>`_)
-- Fixed "Crash in compiler with static[int]"
-  (`#3706 <https://github.com/nim-lang/Nim/issues/3706>`_)
-- Fixed "Bad error message "could not resolve""
-  (`#3548 <https://github.com/nim-lang/Nim/issues/3548>`_)
-- Fixed "Roof operator on string in template crashes compiler  (Error: unhandled exception: sons is not accessible [FieldError])"
-  (`#3545 <https://github.com/nim-lang/Nim/issues/3545>`_)
-- Fixed "SIGSEGV during compilation with parallel block"
-  (`#2758 <https://github.com/nim-lang/Nim/issues/2758>`_)
-- Fixed "Codegen error with template and implicit dereference"
-  (`#4478 <https://github.com/nim-lang/Nim/issues/4478>`_)
-- Fixed "@ in importcpp should work with no-argument functions"
-  (`#4496 <https://github.com/nim-lang/Nim/issues/4496>`_)
-- Fixed "Regression: findExe raises"
-  (`#4497 <https://github.com/nim-lang/Nim/issues/4497>`_)
-- Fixed "Linking error - repeated symbols when splitting into modules"
-  (`#4485 <https://github.com/nim-lang/Nim/issues/4485>`_)
-- Fixed "Error: method is not a base"
-  (`#4428 <https://github.com/nim-lang/Nim/issues/4428>`_)
-- Fixed "Casting from function returning a tuple fails"
-  (`#4345 <https://github.com/nim-lang/Nim/issues/4345>`_)
-- Fixed "clang error with default nil parameter"
-  (`#4328 <https://github.com/nim-lang/Nim/issues/4328>`_)
-- Fixed "internal compiler error: openArrayLoc"
-  (`#888 <https://github.com/nim-lang/Nim/issues/888>`_)
-- Fixed "Can't forward declare async procs"
-  (`#1970 <https://github.com/nim-lang/Nim/issues/1970>`_)
-- Fixed "unittest.check and sequtils.allIt do not work together"
-  (`#4494 <https://github.com/nim-lang/Nim/issues/4494>`_)
-- Fixed "httpclient package can't make SSL requests over an HTTP proxy"
-  (`#4520 <https://github.com/nim-lang/Nim/issues/4520>`_)
-- Fixed "False positive warning "declared but not used" for enums."
-  (`#4510 <https://github.com/nim-lang/Nim/issues/4510>`_)
-- Fixed "Explicit conversions not using converters"
-  (`#4432 <https://github.com/nim-lang/Nim/issues/4432>`_)
-
-- Fixed "Unclear error message when importing"
-  (`#4541 <https://github.com/nim-lang/Nim/issues/4541>`_)
-- Fixed "Change console encoding to UTF-8 by default"
-  (`#4417 <https://github.com/nim-lang/Nim/issues/4417>`_)
-
-- Fixed "Typedesc ~= Generic notation does not work anymore!"
-  (`#4534 <https://github.com/nim-lang/Nim/issues/4534>`_)
-- Fixed "unittest broken?"
-  (`#4555 <https://github.com/nim-lang/Nim/issues/4555>`_)
-- Fixed "Operator "or" in converter types seems to crash the compiler."
-  (`#4537 <https://github.com/nim-lang/Nim/issues/4537>`_)
-- Fixed "nimscript failed to compile/run -- Error: cannot 'importc' variable at compile time"
-  (`#4561 <https://github.com/nim-lang/Nim/issues/4561>`_)
-- Fixed "Regression: identifier expected, but found ..."
-  (`#4564 <https://github.com/nim-lang/Nim/issues/4564>`_)
-- Fixed "varargs with transformation that takes var argument creates invalid c code"
-  (`#4545 <https://github.com/nim-lang/Nim/issues/4545>`_)
-- Fixed "Type mismatch when using empty tuple as generic parameter"
-  (`#4550 <https://github.com/nim-lang/Nim/issues/4550>`_)
-- Fixed "strscans"
-  (`#4562 <https://github.com/nim-lang/Nim/issues/4562>`_)
-- Fixed "getTypeImpl crashes (SIGSEGV) on variant types"
-  (`#4526 <https://github.com/nim-lang/Nim/issues/4526>`_)
-- Fixed "Wrong result of sort in VM"
-  (`#4065 <https://github.com/nim-lang/Nim/issues/4065>`_)
-- Fixed "I can't call the random[T](x: Slice[T]): T"
-  (`#4353 <https://github.com/nim-lang/Nim/issues/4353>`_)
-- Fixed "invalid C code generated (function + block + empty tuple)"
-  (`#4505 <https://github.com/nim-lang/Nim/issues/4505>`_)
-
-- Fixed "performance issue: const Table make a copy at runtime lookup."
-  (`#4354 <https://github.com/nim-lang/Nim/issues/4354>`_)
-- Fixed "Compiler issue: libraries without absolute paths cannot be found correctly"
-  (`#4568 <https://github.com/nim-lang/Nim/issues/4568>`_)
-- Fixed "Cannot use math.`^` with non-int types."
-  (`#4574 <https://github.com/nim-lang/Nim/issues/4574>`_)
-- Fixed "C codegen fails when constructing an array using an object constructor."
-  (`#4582 <https://github.com/nim-lang/Nim/issues/4582>`_)
-- Fixed "Visual Studio 10 unresolved external symbol _trunc(should we support VS2010?)"
-  (`#4532 <https://github.com/nim-lang/Nim/issues/4532>`_)
-- Fixed "Cannot pass generic subtypes to proc for generic supertype"
-  (`#4528 <https://github.com/nim-lang/Nim/issues/4528>`_)
-- Fixed "Lamda-lifting bug leading to crash."
-  (`#4551 <https://github.com/nim-lang/Nim/issues/4551>`_)
-- Fixed "First-class iterators declared as inline are compiled at Nim side (no error message) and fail at C"
-  (`#2094 <https://github.com/nim-lang/Nim/issues/2094>`_)
-- Fixed "VS2010-warning C4090 : 'function' : different 'const' qualifiers"
-  (`#4590 <https://github.com/nim-lang/Nim/issues/4590>`_)
-- Fixed "Regression: type mismatch with generics"
-  (`#4589 <https://github.com/nim-lang/Nim/issues/4589>`_)
-- Fixed "„can raise an unlisted exception“ when assigning nil as default value"
-  (`#4593 <https://github.com/nim-lang/Nim/issues/4593>`_)
-- Fixed "upcoming asyncdispatch.closeSocket is not GC-safe"
-  (`#4606 <https://github.com/nim-lang/Nim/issues/4606>`_)
-- Fixed "Visual Studio 10.0 compiler errors, 12.0 warning"
-  (`#4459 <https://github.com/nim-lang/Nim/issues/4459>`_)
-- Fixed "Exception of net.newContext: result.extraInternalIndex == 0  [AssertionError]"
-  (`#4406 <https://github.com/nim-lang/Nim/issues/4406>`_)
-- Fixed "error: redeclaration of 'result_115076' with no linkage"
-  (`#3221 <https://github.com/nim-lang/Nim/issues/3221>`_)
-- Fixed "Compiler crashes on conversion from int to float at compile time"
-  (`#4619 <https://github.com/nim-lang/Nim/issues/4619>`_)
-- Fixed "wrong number of arguments regression in devel"
-  (`#4600 <https://github.com/nim-lang/Nim/issues/4600>`_)
-- Fixed "importc $ has broken error message (and is not documented)"
-  (`#4579 <https://github.com/nim-lang/Nim/issues/4579>`_)
-- Fixed "Compiler segfaults on simple importcpp in js mode [regression]"
-  (`#4632 <https://github.com/nim-lang/Nim/issues/4632>`_)
-- Fixed "Critical reference counting codegen problem"
-  (`#4653 <https://github.com/nim-lang/Nim/issues/4653>`_)
-- Fixed "tables.nim needs lots of {.noSideEffect.}"
-  (`#4254 <https://github.com/nim-lang/Nim/issues/4254>`_)
-- Fixed "Capture variable error when using ``=>`` macro"
-  (`#4658 <https://github.com/nim-lang/Nim/issues/4658>`_)
-- Fixed "Enum from char: internal error getInt"
-  (`#3606 <https://github.com/nim-lang/Nim/issues/3606>`_)
-- Fixed "Compiler crashes in debug mode (no error in release mode) with Natural discriminant in object variants"
-  (`#2865 <https://github.com/nim-lang/Nim/issues/2865>`_)
-- Fixed "SIGSEGV when access field in const object variants"
-  (`#4253 <https://github.com/nim-lang/Nim/issues/4253>`_)
-- Fixed "varargs cannot be used with template converter."
-  (`#4292 <https://github.com/nim-lang/Nim/issues/4292>`_)
-- Fixed "Compiler crashes when borrowing $"
-  (`#3928 <https://github.com/nim-lang/Nim/issues/3928>`_)
-- Fixed "internal error: genMagicExpr: mArrPut"
-  (`#4491 <https://github.com/nim-lang/Nim/issues/4491>`_)
-- Fixed "Unhelpful error message on importc namespace collision"
-  (`#4580 <https://github.com/nim-lang/Nim/issues/4580>`_)
-- Fixed "Problem with openarrays and slices"
-  (`#4179 <https://github.com/nim-lang/Nim/issues/4179>`_)
-- Fixed "Removing lines from end of file then rebuilding does not rebuild [js only?]"
-  (`#4656 <https://github.com/nim-lang/Nim/issues/4656>`_)
-- Fixed "getCurrentException and getCurrentExceptionMsg do not work with JS"
-  (`#4635 <https://github.com/nim-lang/Nim/issues/4635>`_)
-- Fixed "generic proc parameter is not inferred if type parameter has specifier"
-  (`#4672 <https://github.com/nim-lang/Nim/issues/4672>`_)
-- Fixed "Cannot instantiate generic parameter when it is parent type parameter"
-  (`#4673 <https://github.com/nim-lang/Nim/issues/4673>`_)
-- Fixed "deepCopy doesn't work with inheritance after last commit"
-  (`#4693 <https://github.com/nim-lang/Nim/issues/4693>`_)
-- Fixed "Multi-methods don't work when passing ref to a different thread"
-  (`#4689 <https://github.com/nim-lang/Nim/issues/4689>`_)
-- Fixed "Infinite loop in effect analysis on generics"
-  (`#4677 <https://github.com/nim-lang/Nim/issues/4677>`_)
-- Fixed "SIGSEGV when compiling NimYAML tests"
-  (`#4699 <https://github.com/nim-lang/Nim/issues/4699>`_)
-
-- Fixed "Closing AsyncEvent now also unregisters it on non-Windows platforms"
-  (`#4694 <https://github.com/nim-lang/Nim/issues/4694>`_)
-- Fixed "Don't update handle in upcoming/asyncdispatch poll() if it was closed"
-  (`#4697 <https://github.com/nim-lang/Nim/issues/4697>`_)
-- Fixed "generated local variables declared outside block"
-  (`#4721 <https://github.com/nim-lang/Nim/issues/4721>`_)
-- Fixed "Footer Documentation links, & Community link point to the wrong place under news entries"
-  (`#4529 <https://github.com/nim-lang/Nim/issues/4529>`_)
-- Fixed "Jester's macro magic leads to incorrect C generation"
-  (`#4088 <https://github.com/nim-lang/Nim/issues/4088>`_)
-- Fixed "cas bug in atomics.nim"
-  (`#3279 <https://github.com/nim-lang/Nim/issues/3279>`_)
-- Fixed "nimgrep PEG not capturing the pattern 'A'"
-  (`#4751 <https://github.com/nim-lang/Nim/issues/4751>`_)
-- Fixed "GC assert triggers when assigning TableRef threadvar"
-  (`#4640 <https://github.com/nim-lang/Nim/issues/4640>`_)
-- Fixed ".this pragma conflicts with experimental ptr dereferencing when names conflict"
-  (`#4671 <https://github.com/nim-lang/Nim/issues/4671>`_)
-- Fixed "Generic procs accepting var .importcpp type do not work [regression]"
-  (`#4625 <https://github.com/nim-lang/Nim/issues/4625>`_)
-- Fixed "C Error on tuple assignment with array"
-  (`#4626 <https://github.com/nim-lang/Nim/issues/4626>`_)
-- Fixed "module securehash not gcsafe"
-  (`#4760 <https://github.com/nim-lang/Nim/issues/4760>`_)
-
-- Fixed "Nimble installation failed on Windows x86."
-  (`#4764 <https://github.com/nim-lang/Nim/issues/4764>`_)
-- Fixed "Recent changes to marshal module break old marshalled data"
-  (`#4779 <https://github.com/nim-lang/Nim/issues/4779>`_)
-- Fixed "tnewasyncudp.nim test loops forever"
-  (`#4777 <https://github.com/nim-lang/Nim/issues/4777>`_)
-- Fixed "Wrong poll timeout behavior in asyncdispatch"
-  (`#4262 <https://github.com/nim-lang/Nim/issues/4262>`_)
-- Fixed "Standalone await shouldn't read future"
-  (`#4170 <https://github.com/nim-lang/Nim/issues/4170>`_)
-- Fixed "Regression: httpclient fails to compile without -d:ssl"
-  (`#4797 <https://github.com/nim-lang/Nim/issues/4797>`_)
-- Fixed "C Error on declaring array of heritable objects with bitfields"
-  (`#3567 <https://github.com/nim-lang/Nim/issues/3567>`_)
-- Fixed "Corruption when using Channels and Threads"
-  (`#4776 <https://github.com/nim-lang/Nim/issues/4776>`_)
-- Fixed "Sometimes Channel tryRecv() erroneously reports no messages available on the first call on Windows"
-  (`#4746 <https://github.com/nim-lang/Nim/issues/4746>`_)
-- Fixed "Improve error message of functions called without parenthesis"
-  (`#4813 <https://github.com/nim-lang/Nim/issues/4813>`_)
-- Fixed "Docgen doesn't find doc comments in macro generated procs"
-  (`#4803 <https://github.com/nim-lang/Nim/issues/4803>`_)
-- Fixed "asynchttpserver may consume unbounded memory reading headers"
-  (`#3847 <https://github.com/nim-lang/Nim/issues/3847>`_)
-- Fixed "TLS connection to api.clashofclans.com hangs forever."
-  (`#4587 <https://github.com/nim-lang/Nim/issues/4587>`_)
diff --git a/web/news/e028_version_0_15_2.rst b/web/news/e028_version_0_15_2.rst
deleted file mode 100644
index 601a26646..000000000
--- a/web/news/e028_version_0_15_2.rst
+++ /dev/null
@@ -1,77 +0,0 @@
-Version 0.15.2 released
-=======================
-
-.. container:: metadata
-
-  Posted by Andreas Rumpf on 23/10/2016
-
-We're happy to announce that the latest release of Nim, version 0.15.2, is now
-available!
-
-As always, you can grab the latest version from the
-`downloads page <http://nim-lang.org/download.html>`_.
-
-This release is a pure bugfix release fixing the most pressing issues and
-regressions of 0.15.0. For Windows we now provide zipfiles in addition to the
-NSIS based installer which proves to be hard to maintain and after all these
-months still has serious issues. So we encourage you download the .zip
-file instead of the .exe file! Unzip it somewhere, run ``finish.exe`` to
-detect your MingW installation, done. ``finish.exe`` can also set your PATH
-environment variable.
-
-
-Bugfixes
---------
-
-The list below has been generated based on the commits in Nim's git
-repository. As such it lists only the issues which have been closed
-via a commit, for a full list see
-`this link on Github <https://github.com/nim-lang/Nim/issues?utf8=%E2%9C%93&q=is%3Aissue+closed%3A%222016-09-30+..+2016-10-23%22+>`_.
-
-
-- Fixed "`NimMain` not exported in DLL, but `NimMainInner` is"
-  (`#4840 <https://github.com/nim-lang/Nim/issues/4840>`_)
-- Fixed "Tables clear seems to be broken"
-  (`#4844 <https://github.com/nim-lang/Nim/issues/4844>`_)
-- Fixed "compiler: internal error"
-  (`#4845 <https://github.com/nim-lang/Nim/issues/4845>`_)
-- Fixed "trivial macro breaks type checking in the compiler"
-  (`#4608 <https://github.com/nim-lang/Nim/issues/4608>`_)
-- Fixed "derived generic types with static[T] breaks type checking in v0.15.0 (worked in v0.14.2)"
-  (`#4863 <https://github.com/nim-lang/Nim/issues/4863>`_)
-- Fixed "xmlparser.parseXml is not recognised as GC-safe"
-  (`#4899 <https://github.com/nim-lang/Nim/issues/4899>`_)
-- Fixed "async makes generics instantiate only once"
-  (`#4856 <https://github.com/nim-lang/Nim/issues/4856>`_)
-- Fixed "db_common docs aren't generated"
-  (`#4895 <https://github.com/nim-lang/Nim/issues/4895>`_)
-- Fixed "rdstdin  disappeared from documentation index"
-  (`#3755 <https://github.com/nim-lang/Nim/issues/3755>`_)
-- Fixed "ICE on template call resolution"
-  (`#4875 <https://github.com/nim-lang/Nim/issues/4875>`_)
-- Fixed "Invisible code-block"
-  (`#3078 <https://github.com/nim-lang/Nim/issues/3078>`_)
-- Fixed "nim doc does not generate doc comments correctly"
-  (`#4913 <https://github.com/nim-lang/Nim/issues/4913>`_)
-- Fixed "nim doc2 fails on ARM when running against lib/pure/coro.nim"
-  (`#4879 <https://github.com/nim-lang/Nim/issues/4879>`_)
-- Fixed "xmlparser does not unescape correctly"
-  (`#1518 <https://github.com/nim-lang/Nim/issues/1518>`_)
-- Fixed "[docs] mysterious "raise hook""
-  (`#3485 <https://github.com/nim-lang/Nim/issues/3485>`_)
-- Fixed "assertion failure in non-release Nim when compiling NimYAML"
-  (`#4869 <https://github.com/nim-lang/Nim/issues/4869>`_)
-- Fixed "A closure causes nimscript to fail with unhandled exception"
-  (`#4906 <https://github.com/nim-lang/Nim/issues/4906>`_)
-- Fixed "startProcess changes working directory"
-  (`#4867 <https://github.com/nim-lang/Nim/issues/4867>`_)
-- Fixed "bindsym to void template produces ICE"
-  (`#4808 <https://github.com/nim-lang/Nim/issues/4808>`_)
-- Fixed "readline(TFile, var string) segfaults if second argument is nil"
-  (`#564 <https://github.com/nim-lang/Nim/issues/564>`_)
-- Fixed "times.parse gives the wrong day of the week for the first hour of the day."
-  (`#4922 <https://github.com/nim-lang/Nim/issues/4922>`_)
-- Fixed "Internal error when passing parameter proc inside .gcsafe closure"
-  (`#4927 <https://github.com/nim-lang/Nim/issues/4927>`_)
-- Fixed "Upcoming asyncdispatch doesn't compile with C++ backend on OS X"
-  (`#4928 <https://github.com/nim-lang/Nim/issues/4928>`_)
diff --git a/web/news/e029_version_0_16_0.rst b/web/news/e029_version_0_16_0.rst
deleted file mode 100644
index 4b204cfd0..000000000
--- a/web/news/e029_version_0_16_0.rst
+++ /dev/null
@@ -1,222 +0,0 @@
-Version 0.16.0 released
-=======================
-
-.. container:: metadata
-
-  Posted by The Nim Team on 08/01/2017
-
-We're happy to announce that the latest release of Nim, version 0.16.0, is now
-available!
-
-As always, you can grab the latest version from the
-`downloads page <http://nim-lang.org/download.html>`_.
-
-This release includes over 80 bug fixes and improvements. To see a full list
-of changes, take a look at the detailed changelog
-`below <#changelog>`_.
-
-Some of the most significant changes in this release include: a major new
-Nimble release, an improved import syntax, and the stabilisation of
-name mangling rules enabling faster compile times.
-
-The new Nimble release that is included with Nim 0.16.0 includes a variety of
-new features and bug fixes. The most prominent of which is the improved output
-system, as shown in the figure below.
-
-.. raw::html
-
-  <a href="../assets/news/images/0.16.0/nimble.png">
-    <img src="../assets/news/images/0.16.0/nimble.png" alt="Nimble 0.8.0" style="width:100%"/>
-  </a>
-
-For a full list of changes in Nimble, see its
-`changelog <https://github.com/nim-lang/nimble/blob/master/changelog.markdown#080---05012017>`_.
-
-The new import syntax makes it easier to import multiple modules from the same
-package or directory. For example:
-
-.. code-block:: nim
-  import compiler/ast, compiler/parser, compiler/lexer
-  import compiler / [ast, parser, lexer]
-
-The two are equivalent, but the new latter syntax is less redundant.
-
-Finally, the code responsible for name mangling in the generated C and C++ code
-has been improved to reduce compile times. In particular, compile-time for
-the common edit-compile-run cycles have been reduced.
-
-Changelog
-~~~~~~~~~
-
-Changes affecting backwards compatibility
------------------------------------------
-
-- ``staticExec`` now uses the directory of the nim file that contains the
-  ``staticExec`` call as the current working directory.
-- ``TimeInfo.tzname`` has been removed from ``times`` module because it was
-  broken. Because of this, the option ``"ZZZ"`` will no longer work in format
-  strings for formatting and parsing.
-
-Library Additions
------------------
-
-- Added new parameter to ``error`` proc of ``macro`` module to provide better
-  error message
-- Added new ``deques`` module intended to replace ``queues``.
-  ``deques`` provides a superset of ``queues`` API with clear naming.
-  ``queues`` module is now deprecated and will be removed in the future.
-
-- Added ``hideCursor``, ``showCursor``, ``terminalWidth``,
-  ``terminalWidthIoctl`` and ``terminalSize`` to the ``terminal``
-  `(doc) <http://nim-lang.org/docs/terminal.html>`_ module.
-
-- Added new module ``distros``
-  `(doc) <http://nim-lang.org/docs/distros.html>`_  that can be used in Nimble
-  packages to aid in supporting the OS's native package managers.
-
-
-Tool Additions
---------------
-
-
-Compiler Additions
-------------------
-
-- The C/C++ code generator has been rewritten to use stable
-  name mangling rules. This means that compile times for
-  edit-compile-run cycles are much reduced.
-
-
-Language Additions
-------------------
-
-- The ``emit`` pragma now takes a list of Nim expressions instead
-  of a single string literal. This list can easily contain non-strings
-  like template parameters. This means ``emit`` works out of the
-  box with templates and no new quoting rules needed to be introduced.
-  The old way with backtick quoting is still supported but will be
-  deprecated.
-
-.. code-block:: nim
-  type Vector* {.importcpp: "std::vector", header: "<vector>".}[T] = object
-
-  template `[]=`*[T](v: var Vector[T], key: int, val: T) =
-    {.emit: [v, "[", key, "] = ", val, ";"].}
-
-  proc setLen*[T](v: var Vector[T]; size: int) {.importcpp: "resize", nodecl.}
-  proc `[]`*[T](v: var Vector[T], key: int): T {.importcpp: "(#[#])", nodecl.}
-
-  proc main =
-    var v: Vector[float]
-    v.setLen 1
-    v[0] = 6.0
-    echo v[0]
-
-- The ``import`` statement now supports importing multiple modules from
-  the same directory:
-
-.. code-block:: nim
-  import compiler / [ast, parser, lexer]
-
-Is a shortcut for:
-
-.. code-block:: nim
-  import compiler / ast, compiler / parser, compiler / lexer
-
-
-Bugfixes
---------
-
-The list below has been generated based on the commits in Nim's git
-repository. As such it lists only the issues which have been closed
-via a commit, for a full list see
-`this link on Github <https://github.com/nim-lang/Nim/issues?utf8=%E2%9C%93&q=is%3Aissue+closed%3A%222016-10-23+..+2017-01-07%22+>`_.
-
-- Fixed "staticRead and staticExec have different working directories"
-  (`#4871 <https://github.com/nim-lang/Nim/issues/4871>`_)
-- Fixed "CountTable doesn't support the '==' operator"
-  (`#4901 <https://github.com/nim-lang/Nim/issues/4901>`_)
-- Fixed "documentation for module sequtls apply proc"
-  (`#4386 <https://github.com/nim-lang/Nim/issues/4386>`_)
-- Fixed "Operator `==` for CountTable does not work."
-  (`#4946 <https://github.com/nim-lang/Nim/issues/4946>`_)
-- Fixed "sysFatal (IndexError) with parseUri and the / operator"
-  (`#4959 <https://github.com/nim-lang/Nim/issues/4959>`_)
-- Fixed "initialSize parameter does not work in OrderedTableRef"
-  (`#4940 <https://github.com/nim-lang/Nim/issues/4940>`_)
-- Fixed "error proc from macro library could have a node parameter"
-  (`#4915 <https://github.com/nim-lang/Nim/issues/4915>`_)
-- Fixed "Segfault when comparing OrderedTableRef with nil"
-  (`#4974 <https://github.com/nim-lang/Nim/issues/4974>`_)
-- Fixed "Bad codegen when comparing isNil results"
-  (`#4975 <https://github.com/nim-lang/Nim/issues/4975>`_)
-- Fixed "OrderedTable cannot delete entry with empty string or 0 key"
-  (`#5035 <https://github.com/nim-lang/Nim/issues/5035>`_)
-- Fixed "Deleting specific keys from ordered table leaves it in invalid state."
-  (`#5057 <https://github.com/nim-lang/Nim/issues/5057>`_)
-- Fixed "Paths are converted to lowercase on Windows"
-  (`#5076 <https://github.com/nim-lang/Nim/issues/5076>`_)
-- Fixed "toTime(getGMTime(...)) doesn't work correctly when local timezone is not UTC"
-  (`#5065 <https://github.com/nim-lang/Nim/issues/5065>`_)
-- Fixed "out of memory error from `test=` type proc call when parameter is a call to a table's `[]` proc"
-  (`#5079 <https://github.com/nim-lang/Nim/issues/5079>`_)
-- Fixed "Incorrect field order in object construction"
-  (`#5055 <https://github.com/nim-lang/Nim/issues/5055>`_)
-- Fixed "Incorrect codegen when importing nre with C++ backend (commit 8494338)"
-  (`#5081 <https://github.com/nim-lang/Nim/issues/5081>`_)
-- Fixed "Templates, {.emit.}, and backtick interpolation do not work together"
-  (`#4730 <https://github.com/nim-lang/Nim/issues/4730>`_)
-- Fixed "Regression: getType fails in certain cases"
-  (`#5129 <https://github.com/nim-lang/Nim/issues/5129>`_)
-- Fixed "CreateThread doesn't accept functions with generics"
-  (`#43 <https://github.com/nim-lang/Nim/issues/43>`_)
-- Fixed "No instantiation information when template has error"
-  (`#4308 <https://github.com/nim-lang/Nim/issues/4308>`_)
-- Fixed "realloc leaks"
-  (`#4818 <https://github.com/nim-lang/Nim/issues/4818>`_)
-- Fixed "Regression: getType"
-  (`#5131 <https://github.com/nim-lang/Nim/issues/5131>`_)
-- Fixed "Code generation for generics broken by sighashes"
-  (`#5135 <https://github.com/nim-lang/Nim/issues/5135>`_)
-- Fixed "Regression: importc functions are not declared in generated C code"
-  (`#5136 <https://github.com/nim-lang/Nim/issues/5136>`_)
-- Fixed "Calling split("") on string hangs program"
-  (`#5119 <https://github.com/nim-lang/Nim/issues/5119>`_)
-- Fixed "Building dynamic library: undefined references (Linux)"
-  (`#4775 <https://github.com/nim-lang/Nim/issues/4775>`_)
-- Fixed "Bad codegen for distinct + importc - sighashes regression"
-  (`#5137 <https://github.com/nim-lang/Nim/issues/5137>`_)
-- Fixed "C++ codegen regression: memset called on a result variable of `importcpp` type"
-  (`#5140 <https://github.com/nim-lang/Nim/issues/5140>`_)
-- Fixed "C++ codegen regression: using channels leads to broken C++ code"
-  (`#5142 <https://github.com/nim-lang/Nim/issues/5142>`_)
-- Fixed "Ambiguous call when overloading var and non-var with generic type"
-  (`#4519 <https://github.com/nim-lang/Nim/issues/4519>`_)
-- Fixed "[Debian]: build.sh error: unknown processor: aarch64"
-  (`#2147 <https://github.com/nim-lang/Nim/issues/2147>`_)
-- Fixed "RFC: asyncdispatch.poll behaviour"
-  (`#5155 <https://github.com/nim-lang/Nim/issues/5155>`_)
-- Fixed "Can't access enum members through alias (possible sighashes regression)"
-  (`#5148 <https://github.com/nim-lang/Nim/issues/5148>`_)
-- Fixed "Type, declared in generic proc body, leads to incorrect codegen (sighashes regression)"
-  (`#5147 <https://github.com/nim-lang/Nim/issues/5147>`_)
-- Fixed "Compiler SIGSEGV when mixing method and proc"
-  (`#5161 <https://github.com/nim-lang/Nim/issues/5161>`_)
-- Fixed "Compile-time SIGSEGV when declaring .importcpp method with return value "
-  (`#3848 <https://github.com/nim-lang/Nim/issues/3848>`_)
-- Fixed "Variable declaration incorrectly parsed"
-  (`#2050 <https://github.com/nim-lang/Nim/issues/2050>`_)
-- Fixed "Invalid C code when naming a object member "linux""
-  (`#5171 <https://github.com/nim-lang/Nim/issues/5171>`_)
-- Fixed "[Windows] MinGW within Nim install is missing libraries"
-  (`#2723 <https://github.com/nim-lang/Nim/issues/2723>`_)
-- Fixed "async: annoying warning for future.finished"
-  (`#4948 <https://github.com/nim-lang/Nim/issues/4948>`_)
-- Fixed "new import syntax doesn't work?"
-  (`#5185 <https://github.com/nim-lang/Nim/issues/5185>`_)
-- Fixed "Fixes #1994"
-  (`#4874 <https://github.com/nim-lang/Nim/issues/4874>`_)
-- Fixed "Can't tell return value of programs with staticExec"
-  (`#1994 <https://github.com/nim-lang/Nim/issues/1994>`_)
-- Fixed "startProcess() on Windows with poInteractive: Second call fails ("Alle Pipeinstanzen sind ausgelastet")"
-  (`#5179 <https://github.com/nim-lang/Nim/issues/5179>`_)
diff --git a/web/news/e030_nim_in_action_in_production.rst b/web/news/e030_nim_in_action_in_production.rst
deleted file mode 100644
index b68b82801..000000000
--- a/web/news/e030_nim_in_action_in_production.rst
+++ /dev/null
@@ -1,53 +0,0 @@
-Nim in Action is going into production!
-=======================================
-
-.. container:: metadata
-
-  Posted by Dominik Picheta on 20/11/2016
-
-.. raw::html
-
-  <a href="https://manning.com/books/nim-in-action?a_aid=niminaction&a_bid=78a27e81">
-    <img src="../assets/niminaction/banner2.png" alt="A printed copy of Nim in Action should be available in March 2017!" width="682"/>
-  </a>
-
-I am very happy to say that just last week I have put the finishing touches
-on Nim in Action. The final manuscript has been submitted to Manning (the book's
-publisher), and the printed version is expected to start shipping in March
-2017 (give or take 1 month).
-
-The eBook is still available and now contains all of the book's chapters,
-including new ones dealing with the foreign function interface and
-metaprogramming.
-That said, it may still take some time before the eBook is updated with the
-latest corrections.
-
-I am incredibly thankful to everyone that purchased the book already. Many of
-you have also given me a lot of `brilliant <http://forum.nim-lang.org/t/1978>`_
-`feedback <https://forums.manning.com/forums/nim-in-action>`_,
-thank you very much for
-taking the time to do so. I have done my best to act on this
-feedback and I hope you will agree that the book has risen in quality as a
-result.
-
-Writing this book has been both exhausting and incredible at the same time.
-I look forward
-to having a physical copy of it in my hands, and I'm sure many of you do as
-well. I can safely say that without your support this book would not have
-happened, even if you did not purchase a copy your interest in Nim has made it
-possible and I thank you for that.
-
-As always, you can make a purchase on
-`Manning's website <https://manning.com/books/nim-in-action?a_aid=niminaction&a_bid=78a27e81>`_.
-Both eBook's and printed books are available, and purchasing a printed book will
-get you an eBook for free.
-You can now also pre-order Nim in Action on
-`Amazon <https://www.amazon.co.uk/Nim-Action-Dominik-Picheta/dp/1617293431/ref=sr_1_1?ie=UTF8&qid=1479663850&sr=8-1&keywords=nim+in+action>`_!
-
-If you would like updates about the book then please feel free to
-follow either `myself <https://twitter.com/d0m96>`_ or
-`@nim_lang <https://twitter.com/nim_lang>`_ on Twitter. Finally, if you have any
-questions, do get in touch via `Twitter, NimForum,
-IRC or Gitter <http://nim-lang.org/community.html>`_.
-
-Thanks for reading!
diff --git a/web/news/e031_version_0_16_2.rst b/web/news/e031_version_0_16_2.rst
deleted file mode 100644
index 3458ac83e..000000000
--- a/web/news/e031_version_0_16_2.rst
+++ /dev/null
@@ -1,325 +0,0 @@
-Version 0.17.0 released
-=======================
-
-This release fixes the most important regressions introduced in 0.16.0. In
-particular memory manager and channel bugs have been fixed. The NSIS based
-installer is not provided anymore as the Nim website moved to ``https`` and
-this causes NSIS downloads to fail.
-
-
-Changelog
-~~~~~~~~~
-
-Changes affecting backwards compatibility
------------------------------------------
-
-- There are now two different HTTP response types, ``Response`` and
-  ``AsyncResponse``. ``AsyncResponse``'s ``body`` accessor returns a
-  ``Future[string]``!
-- ``httpclient.request`` now respects ``maxRedirects`` option. Previously
-  redirects were handled only by ``get`` and ``post`` procs.
-- The IO routines now raise ``EOFError`` for the "end of file" condition.
-  ``EOFError`` is a subtype of ``IOError`` and so it's easier to distinguish
-  between "error during read" and "error due to EOF".
-- A hash procedure has been added for ``cstring`` type in ``hashes`` module.
-  Previously, hash of a ``cstring`` would be calculated as a hash of the
-  pointer. Now the hash is calculated from the contents of the string, assuming
-  ``cstring`` is a null-terminated string. Equal ``string`` and ``cstring``
-  values produce an equal hash value.
-- Macros accepting `varargs` arguments will now receive a node having the
-  `nkArgList` node kind. Previous code expecting the node kind to be `nkBracket`
-  may have to be updated.
-- ``memfiles.open`` now closes file handleds/fds by default.  Passing
-  ``allowRemap=true`` to ``memfiles.open`` recovers the old behavior.  The old
-  behavior is only needed to call ``mapMem`` on the resulting ``MemFile``.
-- ``posix.nim``: For better C++ interop the field
-  ``sa_sigaction*: proc (x: cint, y: var SigInfo, z: pointer) {.noconv.}`` was
-  changed
-  to ``sa_sigaction*: proc (x: cint, y: ptr SigInfo, z: pointer) {.noconv.}``.
-- The compiler doesn't infer effects for ``.base`` methods anymore. This means
-  you need to annotate them with ``.gcsafe`` or similar to clearly declare
-  upfront every implementation needs to fullfill these contracts.
-- ``system.getAst templateCall(x, y)`` now typechecks the ``templateCall``
-  properly. You need to patch your code accordingly.
-- ``macros.getType`` and ``macros.getTypeImpl`` for an enum will now return an
-  AST that is the same as what is used to define an enum.  Previously the AST
-  returned had a repeated ``EnumTy`` node and was missing the initial pragma
-  node (which is currently empty for an enum).
-- ``macros.getTypeImpl`` now correctly returns the implementation for a symbol
-  of type ``tyGenericBody``.
-- If the dispatcher parameter's value used in multi method is ``nil``,
-  a ``NilError`` exception is raised. The old behavior was that the method
-  would be a ``nop`` then.
-- ``posix.nim``: the family of ``ntohs`` procs now takes unsigned integers
-  instead of signed integers.
-- In Nim identifiers en-dash (Unicode point U+2013) is not an alias for the
-  underscore anymore. Use underscores and fix your programming font instead.
-- When the ``requiresInit`` pragma is applied to a record type, future versions
-  of Nim will also require you to initialize all the fields of the type during
-  object construction. For now, only a warning will be produced.
-- The Object construction syntax now performs a number of additional safety
-  checks. When fields within case objects are initialiazed, the compiler will
-  now demand that the respective discriminator field has a matching known
-  compile-time value.
-- On posix, the results of `waitForExit`, `peekExitCode`, `execCmd` will return
-  128 + signal number if the application terminates via signal.
-- ``ospaths.getConfigDir`` now conforms to the XDG Base Directory specification
-  on non-Windows OSs. It returns the value of the XDG_CONFIG_DIR environment
-  variable if it is set, and returns the default configuration directory,
-  "~/.config/", otherwise.
-
-Library Additions
------------------
-
-- Added ``system.onThreadDestruction``.
-- Added ``dial`` procedure to networking modules: ``net``, ``asyncdispatch``,
-  ``asyncnet``. It merges socket creation, address resolution, and connection
-  into single step. When using ``dial``, you don't have to worry about
-  IPv4 vs IPv6 problem. ``httpclient`` now supports IPv6.
-
-Tool Additions
---------------
-
-- The ``finish`` tool can now download MingW for you should it not find a
-  working MingW installation.
-
-
-Compiler Additions
-------------------
-
-- The name mangling rules used by the C code generator changed. Most of the time
-  local variables and parameters are not mangled at all anymore. This improves
-  debugging experience.
-- The compiler produces explicit name mangling files when ``--debugger:native``
-  is enabled. Debuggers can read these ``.ndi`` files in order to improve
-  debugging Nim code.
-
-
-Language Additions
-------------------
-
-- The ``try`` statement's ``except`` branches now support the binding of a
-caught exception to a variable:
-
-.. code-block:: nim
-  try:
-    raise newException(Exception, "Hello World")
-  except Exception as exc:
-    echo(exc.msg)
-
-This replaces the ``getCurrentException`` and ``getCurrentExceptionMsg()``
-procedures, although these procedures will remain in the stdlib for the
-foreseeable future. This new language feature is actually implemented using
-these procedures.
-
-In the near future we will be converting all exception types to refs to
-remove the need for the ``newException`` template.
-
-- A new pragma ``.used`` can be used for symbols to prevent
-the "declared but not used" warning. More details can be
-found `here <http://nim-lang.org/docs/manual.html#pragmas-used-pragma>`_.
-- The popular "colon block of statements" syntax is now also supported for
-  ``let`` and ``var`` statements and assignments:
-
-.. code-block:: nim
-  template ve(value, effect): untyped =
-    effect
-    val
-
-  let x = ve(4):
-    echo "welcome to Nim!"
-
-This is particularly useful for DSLs that help in tree construction.
-
-
-Language changes
-----------------
-
-- The ``.procvar`` annotation is not required anymore. That doesn't mean you
-  can pass ``system.$`` to ``map`` just yet though.
-
-
-Bugfixes
---------
-
-The list below has been generated based on the commits in Nim's git
-repository. As such it lists only the issues which have been closed
-via a commit, for a full list see
-`this link on Github <https://github.com/nim-lang/Nim/issues?utf8=%E2%9C%93&q=is%3Aissue+closed%3A%222017-01-07+..+2017-02-06%22+>`_.
-
-- Fixed "Weird compilation bug"
-  (`#4884 <https://github.com/nim-lang/Nim/issues/4884>`_)
-- Fixed "Return by arg optimization does not set result to default value"
-  (`#5098 <https://github.com/nim-lang/Nim/issues/5098>`_)
-- Fixed "upcoming asyncdispatch doesn't remove recv callback if remote side closed socket"
-  (`#5128 <https://github.com/nim-lang/Nim/issues/5128>`_)
-- Fixed "compiler bug, executable writes into wrong memory"
-  (`#5218 <https://github.com/nim-lang/Nim/issues/5218>`_)
-- Fixed "Module aliasing fails when multiple modules have the same original name"
-  (`#5112 <https://github.com/nim-lang/Nim/issues/5112>`_)
-- Fixed "JS: var argument + case expr with arg = bad codegen"
-  (`#5244 <https://github.com/nim-lang/Nim/issues/5244>`_)
-- Fixed "compiler reject proc's param shadowing inside template"
-  (`#5225 <https://github.com/nim-lang/Nim/issues/5225>`_)
-- Fixed "const value not accessible in proc"
-  (`#3434 <https://github.com/nim-lang/Nim/issues/3434>`_)
-- Fixed "Compilation regression 0.13.0 vs 0.16.0 in compile-time evaluation"
-  (`#5237 <https://github.com/nim-lang/Nim/issues/5237>`_)
-- Fixed "Regression: JS: wrong field-access codegen"
-  (`#5234 <https://github.com/nim-lang/Nim/issues/5234>`_)
-- Fixed "fixes #5234"
-  (`#5240 <https://github.com/nim-lang/Nim/issues/5240>`_)
-- Fixed "JS Codegen: duplicated fields in object constructor"
-  (`#5271 <https://github.com/nim-lang/Nim/issues/5271>`_)
-- Fixed "RFC: improving JavaScript FFI"
-  (`#4873 <https://github.com/nim-lang/Nim/issues/4873>`_)
-- Fixed "Wrong result type when using bitwise and"
-  (`#5216 <https://github.com/nim-lang/Nim/issues/5216>`_)
-- Fixed "upcoming.asyncdispatch is prone to memory leaks"
-  (`#5290 <https://github.com/nim-lang/Nim/issues/5290>`_)
-- Fixed "Using threadvars leads to crash on Windows when threads are created/destroyed"
-  (`#5301 <https://github.com/nim-lang/Nim/issues/5301>`_)
-- Fixed "Type inferring templates do not work with non-ref types."
-  (`#4973 <https://github.com/nim-lang/Nim/issues/4973>`_)
-- Fixed "Nimble package list no longer works on lib.html"
-  (`#5318 <https://github.com/nim-lang/Nim/issues/5318>`_)
-- Fixed "Missing file name and line number in error message"
-  (`#4992 <https://github.com/nim-lang/Nim/issues/4992>`_)
-- Fixed "ref type can't be converted to var parameter in VM"
-  (`#5327 <https://github.com/nim-lang/Nim/issues/5327>`_)
-- Fixed "nimweb ignores the value of --parallelBuild"
-  (`#5328 <https://github.com/nim-lang/Nim/issues/5328>`_)
-- Fixed "Cannot unregister/close AsyncEvent from within its handler"
-  (`#5331 <https://github.com/nim-lang/Nim/issues/5331>`_)
-- Fixed "name collision with template instanciated generic inline function with inlined iterator specialization used from different modules"
-  (`#5285 <https://github.com/nim-lang/Nim/issues/5285>`_)
-- Fixed "object in VM does not have value semantic"
-  (`#5269 <https://github.com/nim-lang/Nim/issues/5269>`_)
-- Fixed "Unstable tuple destructuring behavior in Nim VM"
-  (`#5221 <https://github.com/nim-lang/Nim/issues/5221>`_)
-- Fixed "nre module breaks os templates"
-  (`#4996 <https://github.com/nim-lang/Nim/issues/4996>`_)
-- Fixed "Cannot implement distinct seq with setLen"
-  (`#5090 <https://github.com/nim-lang/Nim/issues/5090>`_)
-- Fixed "await inside array/dict literal produces invalid code"
-  (`#5314 <https://github.com/nim-lang/Nim/issues/5314>`_)
-
-- Fixed "asyncdispatch.accept() can raise exception inside poll() instead of failing future on Windows"
-  (`#5279 <https://github.com/nim-lang/Nim/issues/5279>`_)
-- Fixed "VM: A crash report should be more informative"
-  (`#5352 <https://github.com/nim-lang/Nim/issues/5352>`_)
-- Fixed "IO routines are poor at handling errors"
-  (`#5349 <https://github.com/nim-lang/Nim/issues/5349>`_)
-- Fixed "new import syntax doesn't work?"
-  (`#5185 <https://github.com/nim-lang/Nim/issues/5185>`_)
-- Fixed "Seq of object literals skips unmentioned fields"
-  (`#5339 <https://github.com/nim-lang/Nim/issues/5339>`_)
-- Fixed "``sym is not accessible`` in compile time"
-  (`#5354 <https://github.com/nim-lang/Nim/issues/5354>`_)
-- Fixed "the matching is broken in re.nim"
-  (`#5382 <https://github.com/nim-lang/Nim/issues/5382>`_)
-- Fixed "development branch breaks in my c wrapper"
-  (`#5392 <https://github.com/nim-lang/Nim/issues/5392>`_)
-- Fixed "Bad codegen: toSeq + tuples + generics"
-  (`#5383 <https://github.com/nim-lang/Nim/issues/5383>`_)
-- Fixed "Bad codegen: toSeq + tuples + generics"
-  (`#5383 <https://github.com/nim-lang/Nim/issues/5383>`_)
-- Fixed "Codegen error when using container of containers"
-  (`#5402 <https://github.com/nim-lang/Nim/issues/5402>`_)
-- Fixed "sizeof(RangeType) is not available in static context"
-  (`#5399 <https://github.com/nim-lang/Nim/issues/5399>`_)
-- Fixed "Regression: ICE: expr: var not init ex_263713"
-  (`#5405 <https://github.com/nim-lang/Nim/issues/5405>`_)
-- Fixed "Stack trace is wrong when assignment operator fails with template"
-  (`#5400 <https://github.com/nim-lang/Nim/issues/5400>`_)
-- Fixed "SIGSEGV in compiler"
-  (`#5391 <https://github.com/nim-lang/Nim/issues/5391>`_)
-- Fixed "Compiler regression with struct member names"
-  (`#5404 <https://github.com/nim-lang/Nim/issues/5404>`_)
-- Fixed "Regression: compiler segfault"
-  (`#5419 <https://github.com/nim-lang/Nim/issues/5419>`_)
-- Fixed "The compilation of jester routes is broken on devel"
-  (`#5417 <https://github.com/nim-lang/Nim/issues/5417>`_)
-- Fixed "Non-generic return type produces "method is not a base""
-  (`#5432 <https://github.com/nim-lang/Nim/issues/5432>`_)
-- Fixed "Confusing error behavior when calling slice[T].random"
-  (`#5430 <https://github.com/nim-lang/Nim/issues/5430>`_)
-- Fixed "Wrong method called"
-  (`#5439 <https://github.com/nim-lang/Nim/issues/5439>`_)
-- Fixed "Attempt to document the strscans.scansp macro"
-  (`#5154 <https://github.com/nim-lang/Nim/issues/5154>`_)
-- Fixed "[Regression] Invalid C code for _ symbol inside jester routes"
-  (`#5452 <https://github.com/nim-lang/Nim/issues/5452>`_)
-- Fixed "StdLib base64 encodeInternal crashes with out of bound exception"
-  (`#5457 <https://github.com/nim-lang/Nim/issues/5457>`_)
-- Fixed "Nim hangs forever in infinite loop in nre library"
-  (`#5444 <https://github.com/nim-lang/Nim/issues/5444>`_)
-
-- Fixed "Tester passes test although individual test in suite fails"
-  (`#5472 <https://github.com/nim-lang/Nim/issues/5472>`_)
-- Fixed "terminal.nim documentation"
-  (`#5483 <https://github.com/nim-lang/Nim/issues/5483>`_)
-- Fixed "Codegen error - expected identifier before ')' token (probably regression)"
-  (`#5481 <https://github.com/nim-lang/Nim/issues/5481>`_)
-- Fixed "mixin not works inside generic proc generated by template"
-  (`#5478 <https://github.com/nim-lang/Nim/issues/5478>`_)
-- Fixed "var not init (converter + template + macro)"
-  (`#5467 <https://github.com/nim-lang/Nim/issues/5467>`_)
-- Fixed "`==` for OrderedTable should consider equal content but different size as equal."
-  (`#5487 <https://github.com/nim-lang/Nim/issues/5487>`_)
-- Fixed "Fixed tests/tester.nim"
-  (`#45 <https://github.com/nim-lang/Nim/issues/45>`_)
-- Fixed "template instanciation crashes compiler"
-  (`#5428 <https://github.com/nim-lang/Nim/issues/5428>`_)
-- Fixed "Internal compiler error in handleGenericInvocation"
-  (`#5167 <https://github.com/nim-lang/Nim/issues/5167>`_)
-- Fixed "compiler crash in forwarding template"
-  (`#5455 <https://github.com/nim-lang/Nim/issues/5455>`_)
-- Fixed "Doc query re public/private + suggestion re deprecated"
-  (`#5529 <https://github.com/nim-lang/Nim/issues/5529>`_)
-- Fixed "inheritance not work for generic object whose parent is parameterized"
-  (`#5264 <https://github.com/nim-lang/Nim/issues/5264>`_)
-- Fixed "weird inheritance rule restriction"
-  (`#5231 <https://github.com/nim-lang/Nim/issues/5231>`_)
-- Fixed "Enum with holes broken in JS"
-  (`#5062 <https://github.com/nim-lang/Nim/issues/5062>`_)
-- Fixed "enum type and aliased enum type inequality when tested with operator `is` involving template"
-  (`#5360 <https://github.com/nim-lang/Nim/issues/5360>`_)
-- Fixed "logging: problem with console logger caused by the latest changes in sysio"
-  (`#5546 <https://github.com/nim-lang/Nim/issues/5546>`_)
-- Fixed "Crash if proc and caller doesn't define seq type - HEAD"
-  (`#4756 <https://github.com/nim-lang/Nim/issues/4756>`_)
-- Fixed "`path` config option doesn't work when compilation is invoked from a different directory"
-  (`#5228 <https://github.com/nim-lang/Nim/issues/5228>`_)
-- Fixed "segfaults module doesn't compile with C++ backend"
-  (`#5550 <https://github.com/nim-lang/Nim/issues/5550>`_)
-- Fixed "Improve `joinThreads` for windows"
-  (`#4972 <https://github.com/nim-lang/Nim/issues/4972>`_)
-- Fixed "Compiling in release mode prevents valid code execution."
-  (`#5296 <https://github.com/nim-lang/Nim/issues/5296>`_)
-- Fixed "Forward declaration of generic procs or iterators doesn't work"
-  (`#4104 <https://github.com/nim-lang/Nim/issues/4104>`_)
-- Fixed "cant create thread after join"
-  (`#4719 <https://github.com/nim-lang/Nim/issues/4719>`_)
-- Fixed "can't compile with var name "near" and --threads:on"
-  (`#5598 <https://github.com/nim-lang/Nim/issues/5598>`_)
-- Fixed "inconsistent behavior when calling parent's proc of generic object"
-  (`#5241 <https://github.com/nim-lang/Nim/issues/5241>`_)
-- Fixed "The problem with import order of asyncdispatch and unittest modules"
-  (`#5597 <https://github.com/nim-lang/Nim/issues/5597>`_)
-- Fixed "Generic code fails to compile in unexpected ways"
-  (`#976 <https://github.com/nim-lang/Nim/issues/976>`_)
-- Fixed "Another 'User defined type class' issue"
-  (`#1128 <https://github.com/nim-lang/Nim/issues/1128>`_)
-- Fixed "compiler fails to compile user defined typeclass"
-  (`#1147 <https://github.com/nim-lang/Nim/issues/1147>`_)
-- Fixed "Type class membership testing doesn't work on instances of generic object types"
-  (`#1570 <https://github.com/nim-lang/Nim/issues/1570>`_)
-- Fixed "Strange overload resolution behavior for procedures with typeclass arguments"
-  (`#1991 <https://github.com/nim-lang/Nim/issues/1991>`_)
-- Fixed "The same UDTC can't constrain two type parameters in the same procedure"
-  (`#2018 <https://github.com/nim-lang/Nim/issues/2018>`_)
-- Fixed "More trait/concept issues"
-  (`#2423 <https://github.com/nim-lang/Nim/issues/2423>`_)
-- Fixed "Bugs with concepts?"
-  (`#2882 <https://github.com/nim-lang/Nim/issues/2882>`_)
\ No newline at end of file
diff --git a/web/question.rst b/web/question.rst
deleted file mode 100644
index 54c392ced..000000000
--- a/web/question.rst
+++ /dev/null
@@ -1,194 +0,0 @@
-===========================================
-         Questions and Answers
-===========================================
-
-
-General FAQ
-===========
-
-
-.. container:: standout
-
-  What is Nim?
-  ------------
-
-  Nim (formerly known as "Nimrod") is a statically typed, imperative programming
-  language that tries to give the programmer ultimate power without compromises
-  on runtime efficiency.
-  This means it focuses on compile-time mechanisms in all their
-  various forms. Beneath a nice infix/indentation based syntax with a
-  powerful (AST based, hygienic) macro system lies a semantic model that supports
-  a soft realtime GC on thread local heaps. Asynchronous message passing is used
-  between threads, so no "stop the world" mechanism is necessary. An unsafe
-  shared memory heap is also provided for the increased efficiency that results
-  from that model.
-
-
-
-..  .. container:: standout
-
-..    Why should I use Nim?
-..    ---------------------
-
-..    It's a conservative language in a sense that we stick to features that have
-..    proven themselves for larger scale programming. But it's revolutionary by
-..    the features which have been laid on top.
-
-..    One of Nim's goals is to increase developer productivity without sacrificing
-..    the produced software's stability. The way that this is done is by providing
-
-..    Depending on your use case.
-
-..    Nim is one of the few programming languages in the world which allows you to
-
-
-..    The language inventor describes it as the ultimate programming language
-..    with features which make it perfect for just about any problem.
-
-.. container:: standout
-
-  Why yet another programming language?
-  -------------------------------------
-
-  Nim is one of the very few *programmable* statically typed languages, and
-  one of the even fewer that produces native binaries that require no
-  runtime or interpreter.
-
-
-.. container:: standout
-
-  What have been the major influences in the language's design?
-  -------------------------------------------------------------
-
-  The language borrows heavily from (in order of impact): Modula 3, Delphi, Ada,
-  C++, Python, Lisp, Oberon.
-
-
-.. container:: standout
-
-  What is Nim's take on concurrency?
-  ----------------------------------
-
-  Nim primarily focusses on thread local (and garbage collected) heaps and
-  message passing between threads. Each thread has its own GC, so no
-  "stop the world" mechanism is necessary. An unsafe shared memory heap is also
-  provided.
-
-  Future versions will additionally include a GC "per thread group"
-  and Nim's type system will be enhanced to accurately model this shared
-  memory heap.
-
-
-.. container:: standout
-
-  How is Nim licensed?
-  --------------------
-
-  The Nim compiler and the library are MIT licensed.
-  This means that you can use any license for your own programs developed with
-  Nim.
-
-
-.. container:: standout
-
-  How stable is Nim?
-  ------------------
-
-  The compiler is in development and some important features are still missing.
-  However, the compiler is quite stable already: It is able to compile itself
-  and a substantial body of other code. Until version 1.0.0 is released,
-  minor incompatibilities with older versions of the compiler will be introduced.
-
-
-.. container:: standout
-
-  How fast is Nim?
-  ----------------
-  Benchmarks show it to be comparable to C. Some language features (methods,
-  closures, message passing) are not yet as optimized as they could and will be.
-  The only overhead Nim has over C is the GC which has been tuned
-  for years but still needs some work.
-
-
-.. container:: standout
-
-  What about JVM/CLR backends?
-  ----------------------------
-
-  JVM/CLR support is not in the nearest plans. However, since these VMs support FFI to C
-  it should be possible to create native Nim bridges, that transparenlty generate all the
-  glue code thanks to powerful metaprogramming capabilities of Nim.
-
-
-.. container:: standout
-
-  What about editor support?
-  --------------------------
-
-  - Native Nim Editor: https://github.com/nim-lang/Aporia
-  - Visual Studio Code: https://marketplace.visualstudio.com/items?itemName=kosz78.nim
-  - Emacs: https://github.com/nim-lang/nim-mode
-  - Vim: https://github.com/zah/nimrod.vim/
-  - Scite: Included
-  - Gedit: The `Aporia .lang file <https://github.com/nim-lang/Aporia/blob/master/share/gtksourceview-2.0/language-specs/nim.lang>`_
-  - jEdit: https://github.com/exhu/nimrod-misc/tree/master/jedit
-  - TextMate: Available in bundle installer (`Repository <https://github.com/textmate/nim.tmbundle>`_)
-  - Sublime Text: Available via Package Control (`Repository <https://github.com/Varriount/NimLime>`_)
-  - LiClipse: http://www.liclipse.com/ (Eclipse based plugin)
-  - Howl: Included
-  - Notepad++: Available via `plugin <https://github.com/jangko/nppnim/releases>`_
-
-
-.. container:: standout
-
-  Why is it named ``proc``?
-  -------------------------
-
-  *Procedure* used to be the common term as opposed to a *function* which is a
-  mathematical entity that has no side effects. It is planned to have ``func``
-  as syntactic sugar for ``proc {.noSideEffect.}`` and ``func`` is already a
-  keyword. Naming it ``def`` would not make sense because Nim also provides a
-  ``iterator`` and ``method`` keywords, whereas ``def`` stands for ``define``.
-
-
-Compilation FAQ
-===============
-
-.. container:: standout
-
-  Which option to use for the fastest executable?
-  -----------------------------------------------
-
-  For the standard configuration file, ``-d:release`` does the trick.
-
-.. container:: standout
-
-  Which option to use for the smallest executable?
-  ------------------------------------------------
-
-  For the standard configuration file, ``-d:quick --opt:size`` does the trick.
-
-.. container:: standout
-
-  How do I use a different C compiler than the default one?
-  ---------------------------------------------------------
-
-  Edit the ``config/nim.cfg`` file.
-  Change the value of the ``cc`` variable to one of the following:
-
-  ================  ============================================
-  **Abbreviation**  **C/C++ Compiler**
-  ================  ============================================
-  ``vcc``           Microsoft's Visual C++
-  ``gcc``           Gnu C
-  ``llvm_gcc``      LLVM-GCC compiler
-  ``icc``           Intel C++ compiler
-  ``clang``         Clang compiler
-  ``ucc``           Generic UNIX C compiler
-  ================  ============================================
-
-  Other C compilers are not officially supported, but might work too.
-
-  If your C compiler is not in the above list, try using the
-  *generic UNIX C compiler* (``ucc``). If the C compiler needs
-  different command line arguments try the ``--passc`` and ``--passl`` switches.
diff --git a/web/snippets/snippet1.nim b/web/snippets/snippet1.nim
deleted file mode 100644
index b0895e0c0..000000000
--- a/web/snippets/snippet1.nim
+++ /dev/null
@@ -1,4 +0,0 @@
-import strutils
-echo "Give a list of integers (separated by spaces): ",
-     stdin.readLine.split.each(parseInt).max,
-     " is the maximum!"
diff --git a/web/sponsors.csv b/web/sponsors.csv
deleted file mode 100644
index 7136808c6..000000000
--- a/web/sponsors.csv
+++ /dev/null
@@ -1,40 +0,0 @@
-logo, name, url, this_month, all_time, since, level
-assets/bountysource/secondspectrum.png,Second Spectrum,http://www.secondspectrum.com/,250,2250,"May 5, 2016",250
-assets/bountysource/xored.svg,"Xored Software, Inc.",http://xored.com/,250,1000,250,2000,"Jun 20, 2016",250
-,Varriount,https://github.com/Varriount,250,750,"Nov 18, 2016",250
-,flyx,http://flyx.org,35,350,"Apr 7, 2016",75
-,"Yuriy Glukhov",,25,250,"Apr 6, 2016",25
-,endragor,https://github.com/endragor,25,250,"Apr 7, 2016",25
-,FedericoCeratto,http://firelet.net,25,250,"Apr 7, 2016",25
-,"Adrian Veith",,25,250,"Apr 20, 2016",25
-,euantorano,http://euantorano.co.uk,25,200,"Jun 7, 2016",25
-,xxlabaza,https://github.com/xxlabaza,25,170,"Jun 17, 2016",25
-,devted,https://github.com/devted,25,100,"Oct 19, 2016",25
-,"pyloor ",https://schwarz-weiss.cc/,10,95,"May 16, 2016",10
-,niebaopeng,https://github.com/niebaopeng,10,90,"Apr 15, 2016",10
-,zolern,https://github.com/zolern,10,80,"Apr 15, 2016",10
-,"Oskari Timperi",,10,80,"Jun 8, 2016",10
-,jcosborn,https://github.com/jcosborn,25,75,"Nov 21, 2016",25
-,swalf,https://github.com/swalf,5,65,"May 9, 2016",5
-,"Handojo Goenadi",,5,55,"Apr 19, 2016",5
-,cpunion,https://github.com/cpunion,10,50,"Sep 9, 2016",10
-,D-L,https://github.com/D-L,5,50,"Apr 7, 2016",5
-,moigagoo,http://sloth-ci.com,10,40,"May 13, 2016",10
-,enthus1ast,http://code0.xyz/,10,40,"Oct 28, 2016",10
-,RyanMarcus,http://rmarcus.info,5,35,"Jul 19, 2016",5
-,lenzenmi,https://github.com/lenzenmi,5,35,"Jul 28, 2016",5
-,"Christian Bagley",,10,30,"Oct 11, 2016",10
-,pandada8,https://github.com/pandada8,5,30,"Aug 12, 2016",5
-,abeaumont,http://alfredobeaumont.org/blog,5,30,"Aug 12, 2016",5
-,opendragon,http://www.opendragon.com,25,25,"Jan 18, 2017",25
-,"Eric Raible",,10,20,"Dec 23, 2016",10
-,zefciu,http://pythonista.net,10,20,"Dec 29, 2016",10
-,"Andrey ",https://github.com/Andrey,5,20,"Oct 10, 2016",5
-,syrol,https://github.com/syrol,5,10,"Dec 12, 2016",5
-,"Svend Knudsen",,1,10,"Apr 11, 2016",1
-,"Michael D. Sklaroff",,1,10,"Apr 27, 2016",1
-,nicck,,1,6,"Aug 9, 2016",1
-,cnygaard,,5,5,"Jan 17, 2017",5
-,Aldrog,,5,5,"Feb 11, 2017",5
-,mpachecofaulk55,,5,5,"Feb 11, 2017",5
-,campbellr,,1,5,"Sep 4, 2016",1
diff --git a/web/support.rst b/web/support.rst
deleted file mode 100644
index 72e6dad71..000000000
--- a/web/support.rst
+++ /dev/null
@@ -1,40 +0,0 @@
-Commercial and Community Support
-================================
-
-We offer a multitude of support networks including those in both a community
-and commercial setting.
-
-Commercial support includes:
-
-.. container:: standout
-
-  Priority Bug Fixes
-  ------------------
-
-  File a bug report and we will address them with the highest priority. Once
-  fixed, you will be able to access either the current Git build or at your
-  request a custom build against the latest release with your bug fixed.
-
-
-.. container:: standout
-
-  Feature Requests
-  ----------------
-
-  Suggest to us any feature that you might need, we will examine your request with
-  care and provide a proper answer about its potential for inclusion.
-
-  Communication happens via email or for a slightly higher fee via Skype.
-  The pricing is based on the amount of hours spent on the bugfix / feature
-  implementation and is open to negotiation.
-
-
-All interested parties should email ``support@nim-lang.org``.
-The bid for contracting work is a commercial offer provided by:
-
-
-| **Andreas Rumpf**
-| St.-Quentin-Ring 47
-| 67663 Kaiserslautern
-| GERMANY
-| EU VAT-IN: DE297783450
diff --git a/web/ticker.html b/web/ticker.html
deleted file mode 100644
index 865dcf4f8..000000000
--- a/web/ticker.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<a class="news" href="$1news/e029_version_0_16_0.html">
-  <h4>January 8, 2017</h4>
-  <p>Nim version 0.16.0 has been released!</p>
-</a>
-
-<a class="news" href="$1news/e030_nim_in_action_in_production.html">
-  <h4>November 20, 2016</h4>
-  <p>Nim in Action is going into production!</p>
-</a>
-
-<a class="news" href="$1news/e028_version_0_15_2.html">
-  <h4>October 23, 2016</h4>
-  <p>Nim version 0.15.2 has been released!</p>
-</a>
-
-<a class="news" href="$1news/e027_version_0_15_0.html">
-  <h4>September 30, 2016</h4>
-  <p>Nim version 0.15.0 has been released!</p>
-</a>
-
-<a class="news" href="$1news/e026_survey_results.html">
-  <h4>September 3, 2016</h4>
-  <p>Nim Community Survey results</p>
-</a>
-
-<a href="$1news.html" class="blue">See All News...</a>
diff --git a/web/website.ini b/web/website.ini
deleted file mode 100644
index 18e7bf2cb..000000000
--- a/web/website.ini
+++ /dev/null
@@ -1,82 +0,0 @@
-[Project]
-Name: "Nim"
-Title: "Nim Programming Language"
-Logo: "efficient, expressive, elegant"
-Authors: "Andreas Rumpf and contributors"
-
-[Links]
-# Underscores are replaced with a space.
-# Everything after ; is the ID
-Community: "community.html;link_forum"
-; Aporia_IDE: "https://github.com/nim-lang/Aporia;link_aporia"
-GitHub_Repo: "http://github.com/nim-lang/Nim;link_github"
-
-
-[Tabs]
-# Menu entry: filename
-home: index
-learn: learn
-docs: documentation
-download: download
-support: support
-forum: "http://forum.nim-lang.org"
-faq: question
-# these two are not in the list of "tabs", but do exist:
-community: community
-news: news
-
-[Ticker]
-file: ticker.html
-
-[Documentation]
-doc: "endb.rst;intern.txt;apis.txt;lib.rst;manual.rst;tut1.rst;tut2.rst;nimc.rst;overview.rst;filters.rst"
-doc: "tools.txt;niminst.rst;nimgrep.rst;gc.rst;estp.rst;idetools.rst;docgen.rst;koch.rst;backends.txt"
-doc: "nimfix.rst;nimsuggest.rst;nep1.rst;nims.rst;contributing.rst;manual/*.rst"
-pdf: "manual.rst;lib.rst;tut1.rst;tut2.rst;nimc.rst;niminst.rst;gc.rst"
-srcdoc2: "system.nim;system/nimscript;pure/ospaths"
-srcdoc2: "core/macros;pure/marshal;core/typeinfo"
-srcdoc2: "impure/re;impure/nre;pure/typetraits;../nimsuggest/sexp.nim"
-srcdoc2: "pure/concurrency/threadpool.nim;pure/concurrency/cpuinfo.nim"
-srcdoc: "system/threads.nim;system/channels.nim;js/dom"
-srcdoc2: "pure/os;pure/strutils;pure/math;pure/matchers;pure/algorithm"
-srcdoc2: "pure/stats;impure/nre;windows/winlean;pure/random;js/jsffi"
-srcdoc2: "pure/complex;pure/times;pure/osproc;pure/pegs;pure/dynlib;pure/strscans"
-srcdoc2: "pure/parseopt;pure/parseopt2;pure/hashes;pure/strtabs;pure/lexbase"
-srcdoc2: "pure/parsecfg;pure/parsexml;pure/parsecsv;pure/parsesql"
-srcdoc2: "pure/streams;pure/terminal;pure/cgi;pure/unicode;pure/strmisc"
-srcdoc2: "pure/htmlgen;pure/parseutils;pure/browsers;js/jsconsole"
-srcdoc2: "impure/db_postgres;impure/db_mysql;impure/db_sqlite;pure/db_common"
-srcdoc2: "pure/httpserver;pure/httpclient;pure/smtp;impure/ssl"
-srcdoc2: "pure/ropes;pure/unidecode/unidecode;pure/xmldom;pure/xmldomparser"
-srcdoc2: "pure/xmlparser;pure/htmlparser;pure/xmltree;pure/colors;pure/mimetypes"
-srcdoc2: "pure/json;pure/base64;pure/scgi"
-srcdoc2: "pure/collections/tables;pure/collections/sets;pure/collections/lists"
-srcdoc2: "pure/collections/sharedlist;pure/collections/sharedtables"
-srcdoc2: "pure/collections/intsets;pure/collections/queues;pure/collections/deques;pure/encodings"
-srcdoc2: "pure/events;pure/collections/sequtils;pure/cookies"
-srcdoc2: "pure/memfiles;pure/subexes;pure/collections/critbits"
-srcdoc2: "deprecated/pure/asyncio;deprecated/pure/actors;core/locks;core/rlocks;pure/oids;pure/endians;pure/uri"
-srcdoc2: "pure/nimprof;pure/unittest;packages/docutils/highlite"
-srcdoc2: "packages/docutils/rst;packages/docutils/rstast"
-srcdoc2: "packages/docutils/rstgen;pure/logging;pure/options;pure/asyncdispatch;pure/asyncnet;pure/asyncstreams;pure/asyncfutures"
-srcdoc2: "pure/nativesockets;pure/asynchttpserver;pure/net;pure/selectors;pure/sugar"
-srcdoc2: "deprecated/pure/ftpclient;pure/collections/chains"
-srcdoc2: "pure/asyncfile;pure/asyncftpclient;pure/lenientops"
-srcdoc2: "pure/md5;pure/rationals"
-srcdoc2: "posix/posix;pure/distros;pure/oswalkdir"
-srcdoc2: "pure/collections/heapqueue"
-srcdoc2: "pure/fenv;std/sha1;impure/rdstdin;pure/strformat"
-srcdoc2: "pure/segfaults"
-srcdoc2: "pure/basic2d;pure/basic3d;pure/mersenne;pure/coro;pure/httpcore"
-srcdoc2: "pure/bitops;pure/nimtracker;pure/punycode;pure/volatile;js/asyncjs"
-
-; Note: everything under 'webdoc' doesn't get listed in the index, so wrappers
-; should live here
-
-webdoc: "wrappers/mysql;wrappers/iup"
-webdoc: "wrappers/sqlite3;wrappers/postgres;wrappers/tinyc;wrappers/odbcsql"
-webdoc: "wrappers/pcre"
-webdoc: "wrappers/openssl"
-webdoc: "js/jscore"
-
-webdoc: "posix/posix;wrappers/odbcsql"